diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml new file mode 100644 index 0000000000000..270ea519191e2 --- /dev/null +++ b/.github/workflows/clean-test.yml @@ -0,0 +1,67 @@ +name: Clean PR checks +on: + workflow_dispatch: + inputs: + pr: + description: PR to be cleaned + required: true + checks: + description: Checks to be cleaned + required: true + default: 'build/O2/o2,build/O2/gpu,build/AliceO2/O2/o2/macOS,build/o2checkcode/o2' + owner: + description: Organization + required: true + default: 'AliceO2Group' + repo: + description: Repository + required: true + default: 'AliceO2' + +jobs: + cleanup_pr_checks: + runs-on: ubuntu-latest + steps: + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install ali-bot + run: | + sudo apt-get update -y + sudo apt-get install -y libsasl2-dev python-dev libldap2-dev libssl-dev + python -m pip install --upgrade pip + pip install git+https://github.com/alisw/ali-bot@master + - uses: octokit/graphql-action@v2.x + id: get_last_commit_for_pr + with: + query: | + { + repository(owner: "${{ github.event.inputs.owner }}", name: "${{ github.event.inputs.repo }}") { + url + pullRequest(number:${{ github.event.inputs.pr }}) { + commits(last: 1) { + nodes { + commit { + oid + } + } + } + } + } + } + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Cleanup tests + run: |- + set -x + cat <<\EOF > results.json + ${{ steps.get_last_commit_for_pr.outputs.data }} + EOF + COMMIT=$(jq -r '.repository.pullRequest.commits.nodes[].commit.oid' results.json) + echo $COMMIT + for check in `echo ${{ github.event.inputs.checks }} | tr , \\\\n`; do + set-github-status -c ${{ github.event.inputs.owner }}/${{ github.event.inputs.repo }}@$COMMIT -s $check/pending + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/code-formatting.yml b/.github/workflows/code-formatting.yml new file mode 100644 index 0000000000000..317b5a46e2d20 --- /dev/null +++ b/.github/workflows/code-formatting.yml @@ -0,0 +1,98 @@ +name: Check PR with clang-format + +on: [pull_request_target] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + 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 clang format + id: clang_format + 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 }} + git fetch origin 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-cleanup-${{ 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 clang-format-8 against branch ${{ github.event.pull_request.base.ref }} , with hash ${{ github.event.pull_request.base.sha }}" + COMMIT_FILES=$(git diff --name-only $BASE_COMMIT | grep -ivE 'LinkDef|Utilities/PCG/') + [ "X$COMMIT_FILES" = X ] && { echo "No files to check" ; exit 0; } + RESULT_OUTPUT="$(git-clang-format-8 --commit $BASE_COMMIT --diff --binary `which clang-format-8` $COMMIT_FILES)" + + for x in $COMMIT_FILES; do + case $x in + # *.h|*.cxx) + # We remove the header from the diff as it contains +++ then + # we only select the added lines to check for the long ones. + # We do not need to check for the lines which have been removed + # and we do not want to check for the lines which were not changed + # to avoid extra work. + # 120 characters are allowed, meaning the error should start with 122, + # to allow for the starting + at the end of the line. + # Disabled for now, since people don't like the convention. + # git diff $BASE_COMMIT $x | tail -n +6 | grep -e '^+' | grep '.\{122,\}' && { echo "Line longer than 120 chars in $x." && exit 1; } || true ;; + *.hxx|*.cc|*.hpp) echo "$x uses non-allowed extension." && exit 1 ;; + *) ;; + esac + done + + if [ "$RESULT_OUTPUT" == "no modified files to format" ] \ + || [ "$RESULT_OUTPUT" == "clang-format did not modify any files" ] ; then + echo "clang-format passed." + git push --set-upstream https://alibuild:$ALIBUILD_GITHUB_TOKEN@github.com/alibuild/AliceO2.git :alibot-cleanup-${{ github.event.pull_request.number }} -f || true + echo ::set-output name=clean::true + else + git-clang-format-8 --commit $BASE_COMMIT \ + --diff --binary `which \ + clang-format-8` $COMMIT_FILES | patch -p1 + echo "clang-format failed." + echo "To reproduce it locally please run" + echo -e "\tgit checkout $TRAVIS_BRANCH" + echo -e "\tgit-clang-format --commit $BASE_COMMIT --diff --binary $(which clang-format)" + echo "Opening a PR to your branch with the fixes" + git commit -m "Please consider the following formatting changes" -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-cleanup-${{ github.event.pull_request.number }} -f + echo ::set-output name=clean::false + fi + + - name: pull-request + uses: alisw/pull-request@master + with: + source_branch: 'alibuild:alibot-cleanup-${{ github.event.pull_request.number }}' + destination_branch: '${{ github.event.pull_request.user.login }}:${{ github.event.pull_request.head.ref }}' + github_token: ${{ secrets.ALIBUILD_GITHUB_TOKEN }} + pr_title: "Please consider the following formatting changes to #${{ github.event.pull_request.number }}" + pr_body: | + This PR cannot be merged as is. You should either run clang-format yourself and update the pull request, or merge this PR in yours. + You can find AliceO2 coding conventions at http://github.com/AliceO2Group/CodingGuidelines. + 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.clang_format.outputs.clean }} in + true) echo "PR clean" ; exit 0 ;; + false) echo "PR not clean" ; exit 1 ;; + esac diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml new file mode 100644 index 0000000000000..5cd4f583d275c --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,31 @@ +name: Build Documentation + +on: + push: + branches: + - 'dev' + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Install doxygen + run: | + sudo apt-get update -y + sudo apt-get install -y doxygen doxygen-doc doxygen-latex doxygen-gui graphviz cmake + - uses: actions/checkout@v2 + with: + ref: "dev" + - name: Install doxygen + run: | + cat > CMakeLists.txt <<\EOF + add_subdirectory(doc) + EOF + cmake . + make doc + git add doc/html + git config --global user.email "github-action-bot@example.com" + git config --global user.name "GitHub Action Bot" + git commit -m "Updated README" -a || echo "No changes to commit" + git push origin `git subtree split --prefix doc/html dev`:refs/heads/gh-pages --force + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000000..bd3afeb09f947 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Prepare O2 tag + +on: + workflow_dispatch: + inputs: + tag: + description: 'Tag to prepare' + required: true + default: 'vX.Y.Z' +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Decide which branch to use + run: | + cat << EOF + ::set-output name=branch::$(echo ${{ github.event.inputs.tag }}-patches | tr . - | sed -e's/-[0-9]*-patches$/-patches/') + EOF + id: decide_release_branch + - uses: actions/checkout@v2 + with: + ref: "dev" + - name: Tag branch (or create one before tagging if does not exists) + run: | + set -x + git fetch origin + git checkout -B ${{ steps.decide_release_branch.outputs.branch }} + git config --global user.email "alibuild@cern.ch" + git config --global user.name "ALICE Action Bot" + git tag ${{ github.event.inputs.tag }} + git push --set-upstream origin ${{ github.event.inputs.tag }} + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.event.inputs.tag }} + release_name: ${{ github.event.inputs.tag }} + draft: false + prerelease: false diff --git a/.github/workflows/reports.yml b/.github/workflows/reports.yml new file mode 100644 index 0000000000000..9fc80962a03d5 --- /dev/null +++ b/.github/workflows/reports.yml @@ -0,0 +1,128 @@ +name: Automatically create CHANGELOG for O2 releases + +on: + push: + workflow_dispatch: + inputs: + LAST_RELEASE_DATE: + description: 'Time of the last release' + required: true + default: '' + schedule: + - cron: '0 0 * * *' + +jobs: + build: + runs-on: macOS-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - uses: actions/cache@v2 + name: Configure pip caching + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - uses: octokit/graphql-action@v2.x + id: get_latest_o2_releases + with: + query: | + { + repository(name: "AliceO2", owner: "AliceO2Group") { + releases(last:14) { + edges { + node { + tagName + publishedAt + } + } + } + } + } + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: octokit/graphql-action@v2.x + id: get_latest_o2_prs + with: + query: | + { + repository(name: "AliceO2", owner: "AliceO2Group") { + pullRequests(last: 100) { + edges { + node { + state + mergedAt + title + number + author { + login + } + files(last: 100) { + edges { + node { + path + } + } + } + } + } + } + } + } + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Update Changelog + run: | + set -x + mkdir -p doc/data + # 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. + CURRENT_MONTH=`date +%Y-%m` + cat <<\EOF > doc/data/${CURRENT_MONTH}-o2_releases.json + ${{ steps.get_latest_o2_releases.outputs.data }} + EOF + cat <<\EOF > doc/data/${CURRENT_MONTH}-o2_prs.json + ${{ steps.get_latest_o2_prs.outputs.data }} + EOF + # FIXME: this should really be one second after the last release + # 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, + # as they will be removed in the next iteration. + # FIXME: it's probably enough to iterate on the last two + # months, at least for bi-weekly releases. + 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 + 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 + done + # Here we do the merging by iterating on the subsystems adding + # an header for each and removing the duplicates. + printf "# Changes since ${MERGED_AFTER}\n\n" > CHANGELOG.md + for x in Algorithm Analysis Common DataFormats Detectors EventVisualisation Examples Framework Generators Steer Testing Utilities; do + [ ! -f /tmp/${x}_prs.md ] && continue + printf "## Changes in $x\n\n" >> CHANGELOG.md + cat /tmp/${x}_prs.md | sort -k3 | uniq >> CHANGELOG.md + done + - name: Commit and push if changed + run: |- + git add CHANGELOG.md doc/data + git diff + git config --global user.email "github-action-bot@example.com" + 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 diff --git a/.gitignore b/.gitignore index 6ba91433a67ae..ff8b7c86c8974 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,6 @@ bazel-* # direnv .envrc + +# LSP support on macOS with vim +.clangd diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 102f63acab08f..0000000000000 --- a/.travis.yml +++ /dev/null @@ -1,80 +0,0 @@ -sudo: false -matrix: - fast_finish: true - # Note: Workaround travis-ci/travis-ci#4681 - # Exclude default job which lacks our included environment variables. - exclude: - - os: linux - - env: - include: - - os: linux - dist: xenial - env: JOB=clang-format - addons: - apt: - sources: - - llvm-toolchain-xenial-8 - packages: - - clang-format-8 - compiler: clang - script: | - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then - BASE_COMMIT=$(git rev-parse $TRAVIS_BRANCH) - echo "Running clang-format-8 against branch $TRAVIS_BRANCH, with hash $BASE_COMMIT" - COMMIT_FILES=$(git diff --name-only $BASE_COMMIT | grep -ivE 'LinkDef|Utilities/PCG/') - RESULT_OUTPUT="$(git-clang-format-8 --commit $BASE_COMMIT --diff --binary `which clang-format-8` $COMMIT_FILES)" - - for x in $COMMIT_FILES; do - case $x in - *.h|*.cxx) - # We remove the header from the diff as it contains +++ then - # we only select the added lines to check for the long ones. - # We do not need to check for the lines which have been removed - # and we do not want to check for the lines which were not changed - # to avoid extra work. - # 120 characters are allowed, meaning the error should start with 122, - # to allow for the starting + at the end of the line. - git diff $x | tail -n +5 | grep -e '^+' | grep '.\{122,\}' && { echo "Line longer than 120 chars in $x." && exit 1; } || true ;; - *.hxx|*.cc|*.hpp) echo "$x uses non-allowed extension." && exit 1 ;; - *) ;; - esac - done - - if [ "$RESULT_OUTPUT" == "no modified files to format" ] \ - || [ "$RESULT_OUTPUT" == "clang-format did not modify any files" ] ; then - echo "clang-format passed." - exit 0 - else - echo "clang-format failed." - echo "To reproduce it locally please run" - echo -e "\tgit checkout $TRAVIS_BRANCH" - echo -e "\tgit-clang-format --commit $BASE_COMMIT --diff --binary $(which clang-format)" - echo "$RESULT_OUTPUT" - exit 1 - fi - fi - - os: linux - dist: bionic - env: JOB=doxygen - addons: - apt: - packages: - - doxygen - - doxygen-doc - - doxygen-latex - - doxygen-gui - - graphviz - - cmake - script: | - cat > CMakeLists.txt <<\EOF - add_subdirectory(doc) - EOF - cmake . - make doc - deploy: - provider: pages - skip_cleanup: true - github_token: $GITHUB_API_TOKEN # Set in travis-ci.org dashboard - local_dir: doc/html - on: - branch: dev diff --git a/Algorithm/include/Algorithm/Parser.h b/Algorithm/include/Algorithm/Parser.h index 960e9e6a18751..43ec4daa90aec 100644 --- a/Algorithm/include/Algorithm/Parser.h +++ b/Algorithm/include/Algorithm/Parser.h @@ -158,25 +158,30 @@ class ForwardParser { static_assert(sizeof(InputType) == 1, "ForwardParser currently only supports byte type buffer"); - if (buffer == nullptr || bufferSize == 0) + 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) + if (sizeof(HeaderType) + position > bufferSize) { break; + } entry.header = reinterpret_cast(buffer + position); - if (!checkHeader(*entry.header)) + 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) + if (frameSize + position > bufferSize) { break; + } // payload starts right after the header entry.payload = reinterpret_cast(entry.header + 1); @@ -188,8 +193,9 @@ class ForwardParser } else { auto trailerStart = buffer + position + frameSize - tailOffset; entry.trailer = reinterpret_cast(trailerStart); - if (!CheckTrailer(entry, checkTrailer)) + if (!CheckTrailer(entry, checkTrailer)) { break; + } } // store the extracted frame info and continue with remaining buffer @@ -201,8 +207,9 @@ class ForwardParser // frames found and format consistent, insert entries to target // Note: the complete block must be consistent for (auto entry : frames) { - if (!insert(entry)) + if (!insert(entry)) { break; + } } return frames.size(); } else if (frames.size() == 0) { @@ -332,30 +339,35 @@ class ReverseParser { static_assert(sizeof(InputType) == 1, "ReverseParser currently only supports byte type buffer"); - if (buffer == nullptr || bufferSize == 0) + 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) + if (sizeof(TrailerType) > position) { break; + } entry.trailer = reinterpret_cast(buffer + position - sizeof(TrailerType)); - if (!checkTrailer(*entry.trailer)) + if (!checkTrailer(*entry.trailer)) { break; + } // get the total frame size auto frameSize = getFrameSize(*entry.trailer); - if (frameSize > position) + if (frameSize > position) { break; + } // extract and check header auto headerStart = buffer + position - frameSize; entry.header = reinterpret_cast(headerStart); - if (!checkHeader(*entry.header)) + if (!checkHeader(*entry.header)) { break; + } // payload immediately after header entry.payload = reinterpret_cast(entry.header + 1); @@ -367,8 +379,9 @@ class ReverseParser if (position == 0) { // frames found and format consistent, the complete block must be consistent for (auto entry : frames) { - if (!insert(entry)) + if (!insert(entry)) { break; + } } return frames.size(); } else if (frames.size() == 0) { diff --git a/Algorithm/include/Algorithm/TableView.h b/Algorithm/include/Algorithm/TableView.h index 3aa2e5b91dd5f..462a5ef74d74c 100644 --- a/Algorithm/include/Algorithm/TableView.h +++ b/Algorithm/include/Algorithm/TableView.h @@ -67,10 +67,12 @@ class TableView bool operator<(const FrameIndex& rh) const { - if (rh.columnIndex < columnIndex) + if (rh.columnIndex < columnIndex) { return false; - if (columnIndex < rh.columnIndex) + } + if (columnIndex < rh.columnIndex) { return true; + } return row < rh.row; } }; @@ -143,8 +145,9 @@ class TableView /// get row data for a data set const RowDescType& getRowData(size_t row) const { - if (row < mRowData.size()) + if (row < mRowData.size()) { return mRowData[row]; + } // TODO: better to throw exception? static RowDescType dummy; return dummy; @@ -180,22 +183,26 @@ class TableView 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()) + while (!isValid() && !isEnd()) { operator++(); + } } self_type& operator++() { mIsCached = false; if (mDirection == kAlongRow) { - if (mColumn < mEnd) + if (mColumn < mEnd) { mColumn++; + } } else { - if (mRow < mEnd) + if (mRow < mEnd) { mRow++; + } } - while (!isEnd() && !isValid()) + while (!isEnd() && !isValid()) { operator++(); + } return *this; } @@ -265,11 +272,13 @@ class TableView self_type& operator++() { if (base::mDirection == iterator::kAlongRow) { - if (base::mColumn < base::mEnd) + if (base::mColumn < base::mEnd) { base::mColumn++; + } } else { - if (base::mRow < base::mEnd) + if (base::mRow < base::mEnd) { base::mRow++; + } } return *this; } @@ -314,8 +323,9 @@ class TableView /// private access function for the iterators bool get(unsigned row, unsigned column, FrameData& data) { - if (this->mColumns.size() == 0) + 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; diff --git a/Algorithm/test/pageparser.cxx b/Algorithm/test/pageparser.cxx index 10a918beb23cf..a7b03ecb36816 100644 --- a/Algorithm/test/pageparser.cxx +++ b/Algorithm/test/pageparser.cxx @@ -78,8 +78,9 @@ std::pair, size_t> MakeBuffer(size_t pagesize, } while (nPages * pagesize < totalSize); } else { auto nRequiredPages = dataset.size() / maxElementsPerPage; - if (dataset.size() % maxElementsPerPage > 0) + if (dataset.size() % maxElementsPerPage > 0) { ++nRequiredPages; + } totalSize = (nRequiredPages > 0 ? nRequiredPages : 1) * pagesize; } diff --git a/Analysis/CMakeLists.txt b/Analysis/CMakeLists.txt index 5f5ec9b76515e..da1c87a32b0b3 100644 --- a/Analysis/CMakeLists.txt +++ b/Analysis/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory(Core) add_subdirectory(DataModel) add_subdirectory(Tasks) add_subdirectory(Tutorials) +add_subdirectory(PWGDQ) diff --git a/Analysis/Core/CMakeLists.txt b/Analysis/Core/CMakeLists.txt index 019a72741a5a9..f8e732ee91b70 100644 --- a/Analysis/Core/CMakeLists.txt +++ b/Analysis/Core/CMakeLists.txt @@ -9,26 +9,27 @@ # submit itself to any jurisdiction. o2_add_library(AnalysisCore - SOURCES src/StepTHn.cxx - src/CorrelationContainer.cxx + SOURCES src/CorrelationContainer.cxx src/TrackSelection.cxx - src/VarManager.cxx - src/HistogramManager.cxx - src/RunToTimestamp.cxx + src/TriggerAliases.cxx + src/HFConfigurables.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel) o2_target_root_dictionary(AnalysisCore - HEADERS include/Analysis/StepTHn.h - include/Analysis/CorrelationContainer.h - include/Analysis/TrackSelection.h - include/Analysis/VarManager.h - include/Analysis/HistogramManager.h - include/Analysis/RunToTimestamp.h - include/Analysis/MC.h + HEADERS include/AnalysisCore/CorrelationContainer.h + include/AnalysisCore/TrackSelection.h + include/AnalysisCore/TrackSelectionDefaults.h + include/AnalysisCore/TriggerAliases.h + include/AnalysisCore/MC.h + include/AnalysisCore/HFConfigurables.h LINKDEF src/AnalysisCoreLinkDef.h) -o2_add_executable(makerun2timestamp - COMPONENT_NAME AnalysisCore - SOURCES src/InsertNewRunToTimestamp.cxx - PUBLIC_LINK_LIBRARIES O2::AnalysisCore O2::CCDB) +if(FastJet_FOUND) +o2_add_library(AnalysisJets + SOURCES src/JetFinder.cxx + PUBLIC_LINK_LIBRARIES O2::AnalysisCore FastJet::FastJet FastJet::Contrib) +o2_target_root_dictionary(AnalysisJets + HEADERS include/AnalysisCore/JetFinder.h + LINKDEF src/AnalysisJetsLinkDef.h) +endif() diff --git a/Analysis/Core/include/Analysis/HistogramManager.h b/Analysis/Core/include/Analysis/HistogramManager.h deleted file mode 100644 index 968abc21d42fd..0000000000000 --- a/Analysis/Core/include/Analysis/HistogramManager.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// - -#ifndef HistogramManager_H -#define HistogramManager_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -using namespace std; - -class HistogramManager : public TNamed -{ - - public: - HistogramManager(); - HistogramManager(const char* name, const char* title, const int maxNVars); - ~HistogramManager() override; - - enum Constants { - kNothing = -1 - }; - - void SetMainHistogramList(THashList* list) - { - if (fMainList) - delete fMainList; - fMainList = list; - } - - void AddHistClass(const char* histClass); - void AddHistogram(const char* histClass, const char* name, const char* title, bool isProfile, - int nXbins, double xmin, double xmax, int varX, - int nYbins = 0, double ymin = 0, double ymax = 0, int varY = -1, - int nZbins = 0, double zmin = 0, double zmax = 0, int varZ = -1, - const char* xLabels = "", const char* yLabels = "", const char* zLabels = "", - int varT = -1, int varW = -1); - void AddHistogram(const char* histClass, const char* name, const char* title, bool isProfile, - int nXbins, double* xbins, int varX, - int nYbins = 0, double* ybins = nullptr, int varY = -1, - int nZbins = 0, double* zbins = nullptr, int varZ = -1, - const char* xLabels = "", const char* yLabels = "", const char* zLabels = "", - int varT = -1, int varW = -1); - void AddHistogram(const char* histClass, const char* name, const char* title, - int nDimensions, int* vars, int* nBins, double* xmin, double* xmax, - TString* axLabels = nullptr, int varW = -1, bool useSparse = kFALSE); - void AddHistogram(const char* histClass, const char* name, const char* title, - int nDimensions, int* vars, TArrayD* binLimits, - TString* axLabels = nullptr, int varW = -1, bool useSparse = kFALSE); - - void FillHistClass(const char* className, float* values); - - void SetUseDefaultVariableNames(bool flag) { fUseDefaultVariableNames = flag; }; - void SetDefaultVarNames(TString* vars, TString* units); - const bool* GetUsedVars() const { return fUsedVars; } - - THashList* GetMainHistogramList() { return fMainList; } // get a histogram list - - unsigned long int GetAllocatedBins() const { return fBinsAllocated; } - void Print(Option_t*) const override; - - private: - THashList* fMainList; // master histogram list - int fNVars; // number of variables handled (tipically fromt he Variable Manager) - bool* fUsedVars; //! flags of used variables - std::map>> fVariablesMap; //! map holding identifiers for all variables needed by histograms - - // various - bool fUseDefaultVariableNames; //! toggle the usage of default variable names and units - unsigned long int fBinsAllocated; //! number of allocated bins - TString* fVariableNames; //! variable names - TString* fVariableUnits; //! variable units - - void MakeAxisLabels(TAxis* ax, const char* labels); - - HistogramManager& operator=(const HistogramManager& c); - HistogramManager(const HistogramManager& c); - - ClassDef(HistogramManager, 1) -}; - -#endif diff --git a/Analysis/Core/include/Analysis/RunToTimestamp.h b/Analysis/Core/include/Analysis/RunToTimestamp.h deleted file mode 100644 index d721b43b7f32d..0000000000000 --- a/Analysis/Core/include/Analysis/RunToTimestamp.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 conversion between run number and timestamp -// -// Author: Nicolo' Jacazio on 2020-06-22 - -#ifndef RunToTimestamp_H -#define RunToTimestamp_H - -#include -#include -#include "TNamed.h" - -class RunToTimestamp : public TNamed -{ - public: - RunToTimestamp() = default; - ~RunToTimestamp() final = default; - - /// Checks if the converter has a particular run - bool Has(uint runNumber) const { return mMap.count(runNumber); } - - /// Inserts a new run with a timestamp in the converter database - bool insert(uint runNumber, long timestamp); - - /// Updates an already present run number with a new timestamp - bool update(uint runNumber, long timestamp); - - /// Gets the timestamp of a run - long getTimestamp(uint runNumber) const; - - /// Prints the content of the converter - void print() const; - - private: - std::map mMap; - ClassDef(RunToTimestamp, 1) // converter class between run number and timestamp -}; - -#endif diff --git a/Analysis/Core/include/Analysis/StepTHn.h b/Analysis/Core/include/Analysis/StepTHn.h deleted file mode 100644 index f57f22f380a81..0000000000000 --- a/Analysis/Core/include/Analysis/StepTHn.h +++ /dev/null @@ -1,105 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 StepTHn_H -#define StepTHn_H - -// optimized data container reusing functionality of THn -// A THnSparse is used to have the axis functionality "for free" - -#include "TNamed.h" -#include "THnSparse.h" - -class TArray; -class TArrayF; -class TArrayD; -class TCollection; - -class StepTHnBase : public TNamed -{ - public: - StepTHnBase() : TNamed() {} - StepTHnBase(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, Int_t* nBins, Double_t** binLimits, const char** axisTitles) : TNamed(name, title) {} - - virtual void Fill(const Double_t* var, Int_t istep, Double_t weight = 1.) = 0; - - virtual THnBase* getTHn(Int_t step, Bool_t sparse = kFALSE) = 0; - virtual Int_t getNSteps() = 0; - virtual Int_t getNVar() = 0; - - virtual TArray* getValues(Int_t step) = 0; - virtual TArray* getSumw2(Int_t step) = 0; - - virtual void deleteContainers() = 0; - - virtual Long64_t Merge(TCollection* list) = 0; - - ClassDef(StepTHnBase, 1) // StepTHn base class -}; - -// TODO equidistant binning for THn - -template -class StepTHn : public StepTHnBase -{ - public: - StepTHn(); - StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, Int_t* nBins, Double_t** binLimits, const char** axisTitles); - ~StepTHn() override; - - void Fill(const Double_t* var, Int_t istep, Double_t weight = 1.) override; - - THnBase* getTHn(Int_t step, Bool_t sparse = kFALSE) override - { - if (!mTarget || !mTarget[step]) - createTarget(step, sparse); - return mTarget[step]; - } - Int_t getNSteps() override { return mNSteps; } - Int_t getNVar() override { return mNVars; } - - TArray* getValues(Int_t step) override { return mValues[step]; } - TArray* getSumw2(Int_t step) override { return mSumw2[step]; } - - StepTHn(const StepTHn& c); - StepTHn& operator=(const StepTHn& corr); - void Copy(TObject& c) const override; - - Long64_t Merge(TCollection* list) override; - - protected: - void init(); - void createTarget(Int_t step, Bool_t sparse); - void deleteContainers() override; - - Long64_t getGlobalBinIndex(const Int_t* binIdx); - - Long64_t mNBins; // number of total bins - Int_t mNVars; // number of variables - Int_t mNSteps; // number of selection steps - TemplateArray** mValues; //[mNSteps] data container - TemplateArray** mSumw2; //[mNSteps] data container - - THnBase** mTarget; //! target histogram - - TAxis** mAxisCache; //! cache axis pointers (about 50% of the time in Fill is spent in GetAxis otherwise) - Int_t* mNbinsCache; //! cache Nbins per axis - Double_t* mLastVars; //! caching of last used bins (in many loops some vars are the same for a while) - Int_t* mLastBins; //! caching of last used bins (in many loops some vars are the same for a while) - - THnSparse* mPrototype; // not filled used as prototype histogram for axis functionality etc. - - ClassDef(StepTHn, 1) // THn like container -}; - -typedef StepTHn StepTHnF; -typedef StepTHn StepTHnD; - -#endif diff --git a/Analysis/Core/include/Analysis/TrackSelection.h b/Analysis/Core/include/Analysis/TrackSelection.h deleted file mode 100644 index 9825c04fe8014..0000000000000 --- a/Analysis/Core/include/Analysis/TrackSelection.h +++ /dev/null @@ -1,138 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 track selection -// - -#ifndef TrackSelection_H -#define TrackSelection_H - -#include "Framework/Logger.h" -#include "Framework/DataTypes.h" -#include "TObject.h" -#include - -class TrackSelection : public TObject -{ - - public: - TrackSelection(); - - // Temporary function to check if track passes selection criteria. To be - // replaced by framework filters - template - bool IsSelected(T const& track) - { - if (track.trackType() == o2::aod::track::TrackTypeEnum::GlobalTrack && - track.pt() >= mMinPt && track.pt() < mMaxPt && track.eta() >= mMinEta && - track.eta() < mMaxEta && track.tpcNClsFound() >= mMinNClustersTPC && - track.tpcNClsCrossedRows() >= mMinNCrossedRowsTPC && - track.tpcCrossedRowsOverFindableCls() >= - mMinNCrossedRowsOverFindableClustersTPC && - (track.itsNCls() >= mMinNClustersITS) && - (track.itsChi2NCl() < mMaxChi2PerClusterITS) && - (track.tpcChi2NCl() < mMaxChi2PerClusterTPC) && - (mRequireITSRefit && (track.flags() & 0x4)) && - (mRequireTPCRefit && (track.flags() & 0x40)) && - FulfillsITSHitRequirements(track.itsClusterMap()) && - abs(track.dcaXY()) < mMaxDcaXY && abs(track.dcaZ()) < mMaxDcaZ) { - return true; - } else { - return false; - } - } - - void SetMinPt(float minPt) { mMinPt = minPt; } - void SetMaxPt(float maxPt) { mMaxPt = maxPt; } - void SetMinEta(float minEta) { mMinEta = minEta; } - void SetMaxEta(float maxEta) { mMaxEta = maxEta; } - void SetRequireITSRefit(bool requireITSRefit = true) - { - mRequireITSRefit = requireITSRefit; - } - void SetRequireTPCRefit(bool requireTPCRefit = true) - { - mRequireTPCRefit = requireTPCRefit; - } - void SetMinNClustersTPC(int minNClustersTPC) - { - mMinNClustersTPC = minNClustersTPC; - } - void SetMinNCrossedRowsTPC(int minNCrossedRowsTPC) - { - mMinNCrossedRowsTPC = minNCrossedRowsTPC; - } - void SetMinNCrossedRowsOverFindableClustersTPC( - float minNCrossedRowsOverFindableClustersTPC) - { - mMinNCrossedRowsOverFindableClustersTPC = - minNCrossedRowsOverFindableClustersTPC; - } - void SetMinNClustersITS(int minNClustersITS) - { - mMinNClustersITS = minNClustersITS; - } - void SetMaxChi2PerClusterTPC(float maxChi2PerClusterTPC) - { - mMaxChi2PerClusterTPC = maxChi2PerClusterTPC; - } - void SetMaxChi2PerClusterITS(float maxChi2PerClusterITS) - { - mMaxChi2PerClusterITS = maxChi2PerClusterITS; - } - void SetMaxDcaXY(float maxDcaXY) { mMaxDcaXY = maxDcaXY; } - void SetMaxDcaZ(float maxDcaZ) { mMaxDcaZ = maxDcaZ; } - void SetRequireHitsInITSLayers(int8_t minNRequiredHits, - std::set requiredLayers) - { - // layer 0 corresponds to the the innermost ITS layer - if (minNRequiredHits > requiredLayers.size()) { - LOGF(FATAL, "More ITS hits required than layers specified."); - } else { - mRequiredITSHits.push_back( - std::make_pair(minNRequiredHits, requiredLayers)); - } - }; - void SetRequireNoHitsInITSLayers(std::set excludedLayers) - { - mRequiredITSHits.push_back(std::make_pair(-1, excludedLayers)); - }; - void ResetITSRequirements() { mRequiredITSHits.clear(); }; - - private: - bool FulfillsITSHitRequirements(uint8_t itsClusterMap); - - // kinematic cuts - float mMinPt, mMaxPt; // range in pT - float mMinEta, mMaxEta; // range in eta - - // track quality cuts - int mMinNClustersTPC; // min number of TPC clusters - int mMinNCrossedRowsTPC; // min number of crossed rows in TPC - int mMinNClustersITS; // min number of ITS clusters - float mMaxChi2PerClusterTPC; // max tpc fit chi2 per TPC cluster - float mMaxChi2PerClusterITS; // max its fit chi2 per ITS cluster - float mMinNCrossedRowsOverFindableClustersTPC; // min ratio crossed rows / - // findable clusters - - float mMaxDcaXY; - float mMaxDcaZ; - - bool mRequireITSRefit; // require refit in ITS - bool mRequireTPCRefit; // require refit in TPC - - std::vector>> - mRequiredITSHits; // vector of ITS requirements (minNRequiredHits in - // specific requiredLayers) - ClassDef(TrackSelection, 1) -}; - -#endif diff --git a/Analysis/Core/include/Analysis/VarManager.h b/Analysis/Core/include/Analysis/VarManager.h deleted file mode 100644 index 24996e60e1ebe..0000000000000 --- a/Analysis/Core/include/Analysis/VarManager.h +++ /dev/null @@ -1,144 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// - -#ifndef VarManager_H -#define VarManager_H - -//#include "Analysis/ReducedInfoTables.h" -#include -#include - -#include -#include -//using namespace o2::aod; -using std::vector; - -//_________________________________________________________________________ -class VarManager : public TObject -{ - - public: - enum Variables { - kNothing = -1, - // Run wise variables - kRunNo = 0, - kRunId, - kRunTimeStart, - kRunTimeStop, - kLHCFillNumber, - kDipolePolarity, - kL3Polarity, - kNRunWiseVariables, - - // Event wise variables - kTimeStamp, - kBC, - kInstLumi, - kEventType, - kIsPhysicsSelection, - kVtxX, - kVtxY, - kVtxZ, - kVtxNcontrib, - kVtxCovXX, - kVtxCovXY, - kVtxCovXZ, - kVtxCovYY, - kVtxCovYZ, - kVtxCovZZ, - kVtxChi2, - kCentVZERO, - kNEventWiseVariables, - - // Basic track(pair) wise variables - kPt, - kEta, - kPhi, - kRap, - kMass, - kCharge, - kNBasicTrackVariables, - - // Barrel track variables - kPin, - kITSncls, - kITSchi2, - kITSlayerHit, - kTPCncls, - kTPCchi2, - kTPCsignal, - kTRDsignal, - kTOFsignal, - kTrackLength, - kNBarrelTrackVariables, - - // Muon track variables - kNMuonTrackVariables, - - // Pair variables - kCandidateId, - kPairType, - kPairLxy, - kNPairVariables, - - // Candidate-track correlation variables - kDeltaEta, - kDeltaPhi, - kNCorrelationVariables, - - kNVars - }; // end of Variables enumeration - - static TString fgVariableNames[kNVars]; // variable names - static TString fgVariableUnits[kNVars]; // variable units - static void SetDefaultVarNames(); - - static void SetUseVariable(Variables var) - { - fgUsedVars[var] = kTRUE; - SetVariableDependencies(); - } - static void SetUseVars(const bool* usedVars) - { - for (int i = 0; i < kNVars; ++i) { - if (usedVars[i]) - fgUsedVars[i] = kTRUE; // overwrite only the variables that are being used since there are more channels to modify the used variables array, independently - } - SetVariableDependencies(); - } - static bool GetUsedVar(Variables var) { return fgUsedVars[var]; } - - static void SetRunNumbers(int n, int* runs); - - static void FillEvent(vector event, float* values = nullptr); - static void FillTrack(vector track, float* values = nullptr); - - public: - VarManager(); - ~VarManager() override; - - static float fgValues[kNVars]; // array holding all variables computed during analysis - static void ResetValues(int startValue = 0, int endValue = kNVars); - - private: - static bool fgUsedVars[kNVars]; // holds flags for when the corresponding variable is needed (e.g., in the histogram manager, in cuts, mixing handler, etc.) - static void SetVariableDependencies(); // toggle those variables on which other used variables might depend - - static std::map fgRunMap; // map of runs to be used in histogram axes - - VarManager& operator=(const VarManager& c); - VarManager(const VarManager& c); - - ClassDef(VarManager, 1) -}; -#endif diff --git a/Analysis/Core/include/Analysis/CorrelationContainer.h b/Analysis/Core/include/AnalysisCore/CorrelationContainer.h similarity index 76% rename from Analysis/Core/include/Analysis/CorrelationContainer.h rename to Analysis/Core/include/AnalysisCore/CorrelationContainer.h index 05ce06125536d..5591193f104df 100644 --- a/Analysis/Core/include/Analysis/CorrelationContainer.h +++ b/Analysis/Core/include/AnalysisCore/CorrelationContainer.h @@ -20,13 +20,14 @@ class TH1; class TH1F; class TH3; class TH3F; +class TH2F; class TH1D; class TH2; class TH2D; class TCollection; class THnSparse; class THnBase; -class StepTHnBase; +class StepTHn; class CorrelationContainer : public TNamed { @@ -50,23 +51,25 @@ class CorrelationContainer : public TNamed const char* getStepTitle(CFStep step); - StepTHnBase* getTrackHist() { return mTrackHist; } - StepTHnBase* getEventHist() { return mEventHist; } - StepTHnBase* getTrackHistEfficiency() { return mTrackHistEfficiency; } + StepTHn* getPairHist() { return mPairHist; } + StepTHn* getTriggerHist() { return mTriggerHist; } + StepTHn* getTrackHistEfficiency() { return mTrackHistEfficiency; } + TH2F* getEventCount() { return mEventCount; } - void setTrackHist(StepTHnBase* hist) { mTrackHist = hist; } - void setEventHist(StepTHnBase* hist) { mEventHist = hist; } - void setTrackHistEfficiency(StepTHnBase* hist) { mTrackHistEfficiency = hist; } + void setPairHist(StepTHn* hist) { mPairHist = hist; } + void setTriggerHist(StepTHn* hist) { mTriggerHist = hist; } + void setTrackHistEfficiency(StepTHn* hist) { mTrackHistEfficiency = hist; } void deepCopy(CorrelationContainer* from); - void getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax, THnBase** trackHist, TH2** eventHist); - TH2* getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax, Int_t multBinBegin, Int_t multBinEnd, Bool_t normalizePerTrigger = kTRUE, Int_t stepForMixed = -1, Int_t* trigger = nullptr); - TH1* getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax); + void getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, THnBase** trackHist, TH2** eventHist); + TH2* getPerTriggerYield(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger = kTRUE); + TH2* getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger = kTRUE, Int_t stepForMixed = -1, Int_t* trigger = nullptr); + TH1* getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax); TH1* getTrackEfficiency(CFStep step1, CFStep step2, Int_t axis1, Int_t axis2 = -1, Int_t source = 1, Int_t axis3 = -1); THnBase* getTrackEfficiencyND(CFStep step1, CFStep step2); - TH1* getEventEfficiency(CFStep step1, CFStep step2, Int_t axis1, Int_t axis2 = -1, Float_t ptLeadMin = -1, Float_t ptLeadMax = -1); + TH1* getEventEfficiency(CFStep step1, CFStep step2, Int_t axis1, Int_t axis2 = -1, Float_t ptTriggerMin = -1, Float_t ptTriggerMax = -1); TH1* getBias(CFStep step1, CFStep step2, const char* axis, Float_t leadPtMin = 0, Float_t leadPtMax = -1, Int_t weighting = 0); TH1D* getTrackingEfficiency(Int_t axis); @@ -87,6 +90,8 @@ class CorrelationContainer : public TNamed TH2D* getTrackingEfficiencyCorrection(); TH2D* getTrackingEfficiencyCorrectionCentrality(); + void fillEvent(Float_t centrality, CFStep step); + void extendTrackingEfficiency(Bool_t verbose = kFALSE); void setEtaRange(Float_t etaMin, Float_t etaMax) @@ -120,7 +125,7 @@ class CorrelationContainer : public TNamed void setHistogramType(const char* histogramType) { mHistogramType = histogramType; } - void countEmptyBins(CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax); + void countEmptyBins(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax); void symmetrizepTBins(); void setBinLimits(THnBase* grid); @@ -140,13 +145,15 @@ class CorrelationContainer : public TNamed static TString combineBinning(TString defaultBinning, TString customBinning); protected: - Double_t* getBinning(const char* configuration, const char* tag, Int_t& nBins); + std::vector getBinning(const char* configuration, const char* tag, Int_t& nBins); void weightHistogram(TH3* hist1, TH1* hist2); void multiplyHistograms(THnBase* grid, THnBase* target, TH1* histogram, Int_t var1, Int_t var2); - StepTHnBase* mTrackHist; // container for track level distributions at all analysis steps - StepTHnBase* mEventHist; // container for event level distribution at all analysis steps - StepTHnBase* mTrackHistEfficiency; // container for tracking efficiency and contamination (all particles filled including leading one): axes: eta, pT, particle species + StepTHn* mPairHist; // container for pair level distributions at all analysis steps + StepTHn* mTriggerHist; // container for "trigger" particle (single-particle) level distribution at all analysis steps + StepTHn* mTrackHistEfficiency; // container for tracking efficiency and contamination (all particles filled including leading one): axes: eta, pT, particle species + + TH2F* mEventCount; // event count as function of step, (for pp: event type (plus additional step -1 for all events without vertex range even in MC)) (for PbPb: centrality) Float_t mEtaMin; // eta min for projections Float_t mEtaMax; // eta max for projections @@ -164,7 +171,7 @@ class CorrelationContainer : public TNamed Bool_t mWeightPerEvent; // weight with the number of trigger particles per event Bool_t mSkipScaleMixedEvent; // scale the mixed event with (0, 0) plus finite bin correction (default: kTRUE) - StepTHnBase* mCache; //! cache variable for getTrackEfficiency + StepTHn* mCache; //! cache variable for getTrackEfficiency Bool_t mGetMultCacheOn; //! cache for getHistsZVtxMult function active THnBase* mGetMultCache; //! cache for getHistsZVtxMult function diff --git a/Analysis/Core/include/AnalysisCore/HFConfigurables.h b/Analysis/Core/include/AnalysisCore/HFConfigurables.h new file mode 100644 index 0000000000000..ead5dde738c3d --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/HFConfigurables.h @@ -0,0 +1,61 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// HF Configurable Classes +// +// Authors: Nima Zardoshti + +#ifndef O2_ANALYSIS_HFCONFIGURABLES_H +#define O2_ANALYSIS_HFCONFIGURABLES_H + +#include + +class HFTrackIndexSkimsCreatorConfigs +{ + public: + HFTrackIndexSkimsCreatorConfigs() = default; + ~HFTrackIndexSkimsCreatorConfigs() = default; + + // 2-prong cuts D0 + double mPtD0Min = 0.; + double mInvMassD0Min = 1.46; + double mInvMassD0Max = 2.26; + double mCPAD0Min = 0.75; + double mImpParProductD0Max = -0.00005; + // 2-prong cuts Jpsi + double mPtJpsiMin = 0.; + double mInvMassJpsiMin = 2.5; + double mInvMassJpsiMax = 4.1; + double mCPAJpsiMin = -2; + double mImpParProductJpsiMax = 1000.; + // 3-prong cuts - D+ + double mPtDPlusMin = 0.; + double mInvMassDPlusMin = 1.7; + double mInvMassDPlusMax = 2.05; + double mCPADPlusMin = 0.5; + double mDecLenDPlusMin = 0.; + // 3-prong cuts - Lc + double mPtLcMin = 0.; + double mInvMassLcMin = 2.1; + double mInvMassLcMax = 2.5; + double mCPALcMin = 0.5; + double mDecLenLcMin = 0.; + // 3-prong cuts - Ds + double mPtDsMin = 0.; + double mInvMassDsMin = 1.7; + double mInvMassDsMax = 2.2; + double mCPADsMin = 0.5; + double mDecLenDsMin = 0.; + + private: + ClassDef(HFTrackIndexSkimsCreatorConfigs, 1); +}; + +#endif diff --git a/Analysis/Core/include/AnalysisCore/HistHelpers.h b/Analysis/Core/include/AnalysisCore/HistHelpers.h new file mode 100644 index 0000000000000..f9481876c3cd7 --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/HistHelpers.h @@ -0,0 +1,343 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Mario Krueger +// +// Some helper templates to simplify working with histograms +// + +#ifndef HistHelpers_H +#define HistHelpers_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Framework/Logger.h" + +namespace o2::experimental::histhelpers +{ + +template +struct is_shared_ptr : std::false_type { +}; +template +struct is_shared_ptr> : std::true_type { +}; + +//************************************************************************************************** +/** + * Container class for storing and saving root histograms of any type. + * RootContainer (TObjArray, TList or TFolder) inheritance is used to interface with O2 file writing functionality. + */ +//************************************************************************************************** +template +class HistContainer : private RootContainer +{ + public: + HistContainer(const std::string& name) : RootContainer() + { + RootContainer::SetOwner(false); // let container handle object deletion + RootContainer::SetName(name.data()); + } + HistContainer(const HistContainer& other) + { + // pseudo copy ctor to move around empty collection on construction (e.g. when put in OutputObj) + // this is needed to make HistContainer also work with TLists since these dont have a copy constructor (as opposed to TObjArrays) + RootContainer::SetOwner(false); + RootContainer::SetName(other.GetName()); + } + + using HistType = std::variant, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr>; + + template + void Add(T&& hist) + { + if (mHistos.find(histID) != mHistos.end()) { + LOGF(WARNING, "HistContainer %s already holds a histogram at histID = %d. Overriding it now...", RootContainer::GetName(), histID); + TObject* oldPtr = nullptr; + std::visit([&](const auto& sharedPtr) { oldPtr = sharedPtr.get(); }, mHistos[histID]); + RootContainer::Remove(oldPtr); + } + // if shared pointers or rvalue raw pointers are provided as argument, the existing object is used + // otherwise the existing object is copied + std::optional histVariant{}; + if constexpr (is_shared_ptr::type>::value) { + histVariant = GetHistVariant(hist); + } else if constexpr (std::is_pointer_v && std::is_rvalue_reference_v(hist))>) { + histVariant = GetHistVariant(std::shared_ptr>(hist)); + } else { + histVariant = GetHistVariant(std::make_shared(hist)); + } + if (histVariant) { + mHistos[histID] = *histVariant; + TObject* rawPtr = nullptr; + std::visit([&](const auto& sharedPtr) { rawPtr = sharedPtr.get(); }, mHistos[histID]); + RootContainer::Add(rawPtr); + } else { + LOGF(FATAL, "Could not create histogram."); + } + } + + // gets the underlying histogram pointer + // we cannot automatically infer type here so it has to be explicitly specified + // -> Get(), Get(), Get(), Get(), Get(), Get(), Get(), Get() + template + std::shared_ptr Get(uint8_t histID) + { + return *std::get_if>(&mHistos[histID]); + } + + // fill histogram or profile with arguments x,y,z,... and weight if requested + template + void Fill(Ts&&... position) + { + std::visit([this, &position...](auto&& hist) { GenericFill(hist, std::forward(position)...); }, mHistos[histID]); + } + template + void FillWeight(Ts&&... positionAndWeight) + { + Fill(std::forward(positionAndWeight)...); + } + + // make accessible only the RootContainer functions needed for writing to file + using RootContainer::Class; + using RootContainer::GetName; + + private: + // FIXME: map is most likely not the fastest -> array? + std::map mHistos; + + template + void GenericFill(std::shared_ptr hist, const Ts&... position) + { + // filling with weights requires one additional argument + constexpr bool isTH3 = (std::is_same_v && sizeof...(Ts) == 3 + fillWeight); + constexpr bool isTH2 = (std::is_same_v && sizeof...(Ts) == 2 + fillWeight); + constexpr bool isTH1 = (std::is_same_v && sizeof...(Ts) == 1 + fillWeight); + constexpr bool isTProfile3D = (std::is_same_v && sizeof...(Ts) == 4 + fillWeight); + constexpr bool isTProfile2D = (std::is_same_v && sizeof...(Ts) == 3 + fillWeight); + constexpr bool isTProfile = (std::is_same_v && sizeof...(Ts) == 2 + fillWeight); + + constexpr bool isValidPrimitive = isTH1 || isTH2 || isTH3 || isTProfile || isTProfile2D || isTProfile3D; + // unfortunately we dont know at compile the dimension of THn(Sparse) + constexpr bool isValidComplex = std::is_base_of_v; + + if constexpr (isValidPrimitive) { + hist->Fill(static_cast(position)...); + } else if constexpr (isValidComplex) { + // savety check for n dimensional histograms (runtime overhead) + // if (hist->GetNdimensions() != sizeof...(position) - fillWeight) return; + double tempArray[] = {static_cast(position)...}; + if constexpr (fillWeight) { + hist->Fill(tempArray, tempArray[sizeof...(Ts) - 1]); + } else { + hist->Fill(tempArray); + } + } + } + + template + std::optional GetHistVariant(std::shared_ptr obj) + { + if (obj->InheritsFrom(T::Class())) { + return std::static_pointer_cast(obj); + } + return std::nullopt; + } + template + std::optional GetHistVariant(std::shared_ptr obj) + { + if (auto hist = GetHistVariant(obj)) { + return hist; + } + return GetHistVariant(obj); + } + std::optional GetHistVariant(std::shared_ptr obj) + { + if (obj) { + // TProfile3D is TH3, TProfile2D is TH2, TH3 is TH1, TH2 is TH1, TProfile is TH1 + return GetHistVariant(obj); + } + return std::nullopt; + } +}; + +//************************************************************************************************** +using HistFolder = HistContainer; +using HistArray = HistContainer; +using HistList = HistContainer; +//************************************************************************************************** + +//************************************************************************************************** +/** + * Helper class to build all kinds of root histograms with a streamlined user interface. + */ +//************************************************************************************************** + +struct Axis { + std::string name{}; + std::string title{}; + std::vector binEdges{}; + std::optional nBins{}; +}; + +class Hist +{ + public: + Hist() : mAxes{} {} + Hist(const std::vector& axes) : mAxes{axes} {} + + void AddAxis(const Axis& axis) + { + mAxes.push_back(axis); + } + + void AddAxis(const std::string& name, const std::string& title, const int nBins, + const double lowerEdge, const double upperEdge) + { + mAxes.push_back({name, title, {lowerEdge, upperEdge}, nBins}); + } + + void AddAxis(const std::string& name, const std::string& title, + const std::vector& binEdges) + { + mAxes.push_back({name, title, binEdges}); + } + + void AddAxes(const std::vector& axes) + { + mAxes.insert(mAxes.end(), axes.begin(), axes.end()); + } + + // add axes defined in other Hist object + void AddAxes(const Hist& other) + { + mAxes.insert(mAxes.end(), other.GetAxes().begin(), other.GetAxes().end()); + } + + void Reset() + { + mAxes.clear(); + } + + // create histogram with the defined axes + template + std::shared_ptr Create(const std::string& name, bool useWeights = false) + { + const std::size_t MAX_DIM{10}; + const std::size_t nAxes{mAxes.size()}; + if (nAxes == 0 || nAxes > MAX_DIM) { + return nullptr; + } + + int nBins[MAX_DIM]{0}; + double lowerBounds[MAX_DIM]{0.}; + double upperBounds[MAX_DIM]{0.}; + + // first figure out number of bins and dimensions + std::string title = "[ "; + for (std::size_t i = 0; i < nAxes; i++) { + nBins[i] = (mAxes[i].nBins) ? *mAxes[i].nBins : mAxes[i].binEdges.size() - 1; + lowerBounds[i] = mAxes[i].binEdges.front(); + upperBounds[i] = mAxes[i].binEdges.back(); + title += mAxes[i].name; + if (i < nAxes - 1) { + title += " : "; + } else { + title += " ]"; + } + } + + // create histogram + std::shared_ptr hist(HistFactory(name, title, nAxes, nBins, lowerBounds, upperBounds)); + + if (!hist) { + LOGF(FATAL, "The number of specified dimensions does not match the type."); + return nullptr; + } + + // set axis properties + for (std::size_t i = 0; i < nAxes; i++) { + TAxis* axis{GetAxis(i, hist)}; + if (axis) { + axis->SetTitle(mAxes[i].title.data()); + if constexpr (std::is_base_of_v) { + axis->SetName((std::to_string(i) + "-" + mAxes[i].name).data()); + } + + // move the bin edges in case a variable binning was requested + if (!mAxes[i].nBins) { + if (!std::is_sorted(std::begin(mAxes[i].binEdges), std::end(mAxes[i].binEdges))) { + LOGF(FATAL, "The bin edges specified for axis %s in histogram %s are not in increasing order!", mAxes[i].name, name); + return nullptr; + } + axis->Set(nBins[i], mAxes[i].binEdges.data()); + } + } + } + if (useWeights) { + hist->Sumw2(); + } + return hist; + } + + const std::vector& GetAxes() const { return mAxes; }; + + private: + std::vector mAxes; + + template + RootHistType* HistFactory(const std::string& name, const std::string& title, const std::size_t nDim, + const int nBins[], const double lowerBounds[], const double upperBounds[]) + { + if constexpr (std::is_base_of_v) { + return new RootHistType(name.data(), title.data(), nDim, nBins, lowerBounds, upperBounds); + } else if constexpr (std::is_base_of_v) { + return (nDim != 3) ? nullptr + : new RootHistType(name.data(), title.data(), nBins[0], lowerBounds[0], + upperBounds[0], nBins[1], lowerBounds[1], upperBounds[1], + nBins[2], lowerBounds[2], upperBounds[2]); + } else if constexpr (std::is_base_of_v) { + return (nDim != 2) ? nullptr + : new RootHistType(name.data(), title.data(), nBins[0], lowerBounds[0], + upperBounds[0], nBins[1], lowerBounds[1], upperBounds[1]); + } else if constexpr (std::is_base_of_v) { + return (nDim != 1) + ? nullptr + : new RootHistType(name.data(), title.data(), nBins[0], lowerBounds[0], upperBounds[0]); + } + return nullptr; + } + + template + TAxis* GetAxis(const int i, std::shared_ptr hist) + { + if constexpr (std::is_base_of_v) { + return hist->GetAxis(i); + } else { + return (i == 0) ? hist->GetXaxis() + : (i == 1) ? hist->GetYaxis() : (i == 2) ? hist->GetZaxis() : nullptr; + } + } +}; + +} // namespace o2::experimental::histhelpers + +#endif diff --git a/Analysis/Core/include/AnalysisCore/JetFinder.h b/Analysis/Core/include/AnalysisCore/JetFinder.h new file mode 100644 index 0000000000000..486d11daedf5e --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/JetFinder.h @@ -0,0 +1,182 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet finder task +// +// Authors: Nima Zardoshti, Jochen Klein + +#ifndef O2_ANALYSIS_JETFINDER_H +#define O2_ANALYSIS_JETFINDER_H + +#include +#include +#include + +#include "fastjet/PseudoJet.hh" +#include "fastjet/ClusterSequenceArea.hh" +#include "fastjet/AreaDefinition.hh" +#include "fastjet/JetDefinition.hh" +#include "fastjet/tools/JetMedianBackgroundEstimator.hh" +#include "fastjet/tools/Subtractor.hh" +#include "fastjet/contrib/ConstituentSubtractor.hh" + +#include + +class JetFinder +{ + + public: + enum class BkgSubMode { none, + rhoAreaSub, + constSub }; + BkgSubMode bkgSubMode; + + void setBkgSubMode(BkgSubMode bSM) { bkgSubMode = bSM; } + + /// Performs jet finding + /// \note the input particle and jet lists are passed by reference + /// \param inputParticles vector of input particles/tracks + /// \param jets veector of jets to be filled + /// \return ClusterSequenceArea object needed to access constituents + // fastjet::ClusterSequenceArea findJets(std::vector &inputParticles, std::vector &jets); + + static constexpr float mPion = 0.139; // TDatabasePDG::Instance()->GetParticle(211)->Mass(); //can be removed when pion mass becomes default for unidentified tracks + + float phiMin; + float phiMax; + float etaMin; + float etaMax; + + float jetR; + float jetPtMin; + float jetPtMax; + float jetPhiMin; + float jetPhiMax; + float jetEtaMin; + float jetEtaMax; + + float ghostEtaMin; + float ghostEtaMax; + float ghostArea; + int ghostRepeatN; + double ghostktMean; + float gridScatter; + float ktScatter; + + float jetBkgR; + float bkgPhiMin; + float bkgPhiMax; + float bkgEtaMin; + float bkgEtaMax; + + float constSubAlpha; + float constSubRMax; + + bool isReclustering; + + fastjet::JetAlgorithm algorithm; + fastjet::RecombinationScheme recombScheme; + fastjet::Strategy strategy; + fastjet::AreaType areaType; + fastjet::GhostedAreaSpec ghostAreaSpec; + fastjet::JetDefinition jetDef; + fastjet::AreaDefinition areaDef; + fastjet::Selector selJets; + fastjet::Selector selGhosts; + + fastjet::JetAlgorithm algorithmBkg; + fastjet::RecombinationScheme recombSchemeBkg; + fastjet::Strategy strategyBkg; + fastjet::AreaType areaTypeBkg; + fastjet::JetDefinition jetDefBkg; + fastjet::AreaDefinition areaDefBkg; + fastjet::Selector selRho; + + /// Default constructor + JetFinder(float eta_Min = -0.9, float eta_Max = 0.9, float phi_Min = 0.0, float phi_Max = 2 * TMath::Pi()) : phiMin(phi_Min), + phiMax(phi_Max), + etaMin(eta_Min), + etaMax(eta_Max), + jetR(0.4), + jetPtMin(0.0), + jetPtMax(1000.0), + jetPhiMin(phi_Min), + jetPhiMax(phi_Max), + jetEtaMin(eta_Min), + jetEtaMax(eta_Max), + ghostEtaMin(eta_Min), + ghostEtaMax(eta_Max), + ghostArea(0.005), + ghostRepeatN(1), + ghostktMean(1e-100), //is float precise enough? + gridScatter(1.0), + ktScatter(0.1), + jetBkgR(0.2), + bkgPhiMin(phi_Min), + bkgPhiMax(phi_Max), + bkgEtaMin(eta_Min), + bkgEtaMax(eta_Max), + algorithm(fastjet::antikt_algorithm), + recombScheme(fastjet::E_scheme), + strategy(fastjet::Best), + areaType(fastjet::active_area), + algorithmBkg(fastjet::JetAlgorithm(fastjet::kt_algorithm)), + recombSchemeBkg(fastjet::RecombinationScheme(fastjet::E_scheme)), + strategyBkg(fastjet::Best), + areaTypeBkg(fastjet::active_area), + bkgSubMode(BkgSubMode::none), + constSubAlpha(1.0), + constSubRMax(0.6), + isReclustering(false) + + { + + //default constructor + } + + /// Default destructor + ~JetFinder() = default; + + /// Sets the jet finding parameters + void setParams(); + + /// Sets the background subtraction estimater pointer + void setBkgE(); + + /// Sets the background subtraction pointer + void setSub(); + + /// Performs jet finding + /// \note the input particle and jet lists are passed by reference + /// \param inputParticles vector of input particles/tracks + /// \param jets veector of jets to be filled + /// \return ClusterSequenceArea object needed to access constituents + fastjet::ClusterSequenceArea findJets(std::vector& inputParticles, std::vector& jets); //ideally find a way of passing the cluster sequence as a reeference + + private: + //void setParams(); + //void setBkgSub(); + std::unique_ptr bkgE; + std::unique_ptr sub; + std::unique_ptr constituentSub; + + ClassDef(JetFinder, 1); +}; + +//does this belong here? +template +void fillConstituents(const T& constituent, std::vector& constituents) +{ + + auto energy = std::sqrt(constituent.p() * constituent.p() + JetFinder::mPion * JetFinder::mPion); + constituents.emplace_back(constituent.px(), constituent.py(), constituent.pz(), energy); +} + +#endif diff --git a/Analysis/Core/include/Analysis/MC.h b/Analysis/Core/include/AnalysisCore/MC.h similarity index 79% rename from Analysis/Core/include/Analysis/MC.h rename to Analysis/Core/include/AnalysisCore/MC.h index 913188baeccd2..aa400966f5d69 100644 --- a/Analysis/Core/include/Analysis/MC.h +++ b/Analysis/Core/include/AnalysisCore/MC.h @@ -75,31 +75,36 @@ bool isPhysicalPrimary(TMCParticles& mcParticles, TMCParticle const& particle) // Initial state particle // Solution for K0L decayed by Pythia6 // -> - if ((ist > 1) && (pdg != 130) && particle.producedByGenerator()) + if ((ist > 1) && (pdg != 130) && particle.producedByGenerator()) { return false; - if ((ist > 1) && !particle.producedByGenerator()) + } + if ((ist > 1) && !particle.producedByGenerator()) { return false; + } // <- - if (!isStable(pdg)) + if (!isStable(pdg)) { return false; + } if (particle.producedByGenerator()) { // Solution for K0L decayed by Pythia6 // -> - if (particle.mother()[0] != -1) { - auto mother = mcParticles.iteratorAt(particle.mother()[0]); - if (std::abs(mother.pdgCode()) == 130) + if (particle.mother0() != -1) { + auto mother = mcParticles.iteratorAt(particle.mother0()); + if (std::abs(mother.pdgCode()) == 130) { return false; + } } // <- // check for direct photon in parton shower // -> - if (pdg == 22 && particle.daughter()[0] != -1) { - LOGF(debug, "D %d", particle.daughter()[0]); - auto daughter = mcParticles.iteratorAt(particle.daughter()[0]); - if (daughter.pdgCode() == 22) + if (pdg == 22 && particle.daughter0() != -1) { + LOGF(debug, "D %d", particle.daughter0()); + auto daughter = mcParticles.iteratorAt(particle.daughter0()); + if (daughter.pdgCode() == 22) { return false; + } } // <- return true; @@ -107,43 +112,48 @@ bool isPhysicalPrimary(TMCParticles& mcParticles, TMCParticle const& particle) // Particle produced during transport - LOGF(debug, "M0 %d %d", particle.producedByGenerator(), particle.mother()[0]); - auto mother = mcParticles.iteratorAt(particle.mother()[0]); + LOGF(debug, "M0 %d %d", particle.producedByGenerator(), particle.mother0()); + auto mother = mcParticles.iteratorAt(particle.mother0()); int mpdg = std::abs(mother.pdgCode()); // Check for Sigma0 - if ((mpdg == 3212) && mother.producedByGenerator()) + if ((mpdg == 3212) && mother.producedByGenerator()) { return true; + } // Check if it comes from a pi0 decay - if ((mpdg == kPi0) && mother.producedByGenerator()) + if ((mpdg == kPi0) && mother.producedByGenerator()) { return true; + } // Check if this is a heavy flavor decay product int mfl = int(mpdg / std::pow(10, int(std::log10(mpdg)))); // Light hadron - if (mfl < 4) + if (mfl < 4) { return false; + } // Heavy flavor hadron produced by generator - if (mother.producedByGenerator()) + if (mother.producedByGenerator()) { return true; + } // To be sure that heavy flavor has not been produced in a secondary interaction // Loop back to the generated mother - LOGF(debug, "M0 %d %d", mother.producedByGenerator(), mother.mother()[0]); - while (mother.mother()[0] != -1 && !mother.producedByGenerator()) { - mother = mcParticles.iteratorAt(mother.mother()[0]); - LOGF(debug, "M+ %d %d", mother.producedByGenerator(), mother.mother()[0]); + LOGF(debug, "M0 %d %d", mother.producedByGenerator(), mother.mother0()); + while (mother.mother0() != -1 && !mother.producedByGenerator()) { + mother = mcParticles.iteratorAt(mother.mother0()); + LOGF(debug, "M+ %d %d", mother.producedByGenerator(), mother.mother0()); mpdg = std::abs(mother.pdgCode()); mfl = int(mpdg / std::pow(10, int(std::log10(mpdg)))); } - if (mfl < 4) + if (mfl < 4) { return false; - else + } else { return true; + } } }; // namespace MC diff --git a/Analysis/Core/include/AnalysisCore/PairCuts.h b/Analysis/Core/include/AnalysisCore/PairCuts.h new file mode 100644 index 0000000000000..a06ee0e23a717 --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/PairCuts.h @@ -0,0 +1,342 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_PAIRCUTS_H +#define O2_ANALYSIS_PAIRCUTS_H + +#include + +#include +#include + +#include "Framework/Logger.h" +#include "Framework/HistogramRegistry.h" + +// Functions which cut on particle pairs (decays, conversions, two-track cuts) +// +// Author: Jan Fiete Grosse-Oetringhaus + +using namespace o2; +using namespace o2::framework; + +class PairCuts +{ + public: + enum Particle { Photon = 0, + K0, + Lambda, + Phi, + Rho, + ParticlesLastEntry }; + + void SetHistogramRegistry(HistogramRegistry* registry) { histogramRegistry = registry; } + + void SetPairCut(Particle particle, float cut) + { + LOGF(info, "Enabled pair cut for %d with value %f", static_cast(particle), cut); + mCuts[particle] = cut; + if (histogramRegistry != nullptr && histogramRegistry->contains("ControlConvResonances") == false) { + histogramRegistry->add("ControlConvResonances", "", {HistType::kTH2F, {{6, -0.5, 5.5, "id"}, {500, -0.5, 0.5, "delta mass"}}}); + } + } + + void SetTwoTrackCuts(float distance = 0.02f, float radius = 0.8f) + { + LOGF(info, "Enabled two-track cut with distance %f and radius %f", distance, radius); + mTwoTrackDistance = distance; + mTwoTrackRadius = radius; + + if (histogramRegistry != nullptr && histogramRegistry->contains("TwoTrackDistancePt_0") == false) { + histogramRegistry->add("TwoTrackDistancePt_0", "", {HistType::kTH3F, {{100, -0.15, 0.15, "#Delta#eta"}, {100, -0.05, 0.05, "#Delta#varphi^{*}_{min}"}, {20, 0, 10, "#Delta p_{T}"}}}); + histogramRegistry->addClone("TwoTrackDistancePt_0", "TwoTrackDistancePt_1"); + } + } + + template + bool conversionCuts(T const& track1, T const& track2); + + template + bool twoTrackCut(T const& track1, T const& track2, int bSign); + + protected: + float mCuts[ParticlesLastEntry] = {-1}; + float mTwoTrackDistance = -1; // distance below which the pair is flagged as to be removed + float mTwoTrackRadius = 0.8f; // radius at which the two track cuts are applied + + HistogramRegistry* histogramRegistry = nullptr; // if set, control histograms are stored here + + template + bool conversionCut(T const& track1, T const& track2, Particle conv, double cut); + + template + double getInvMassSquared(T const& track1, double m0_1, T const& track2, double m0_2); + + template + double getInvMassSquaredFast(T const& track1, double m0_1, T const& track2, double m0_2); + + template + float getDPhiStar(T const& track1, T const& track2, float radius, float bSign); +}; + +template +bool PairCuts::conversionCuts(T const& track1, T const& track2) +{ + // skip if like sign + if (track1.charge() * track2.charge() > 0) { + return false; + } + + for (int i = 0; i < static_cast(ParticlesLastEntry); i++) { + Particle particle = static_cast(i); + if (mCuts[i] > 0) { + if (conversionCut(track1, track2, particle, mCuts[i])) { + return true; + } + if (particle == Lambda) { + if (conversionCut(track2, track1, particle, mCuts[i])) { + return true; + } + } + } + } + + return false; +} + +template +bool PairCuts::twoTrackCut(T const& track1, T const& track2, int bSign) +{ + // the variables & cut have been developed in Run 1 by the CF - HBT group + // + // Parameters: + // bSign: sign of B field + + auto deta = track1.eta() - track2.eta(); + + // optimization + if (TMath::Abs(deta) < mTwoTrackDistance * 2.5 * 3) { + // check first boundaries to see if is worth to loop and find the minimum + float dphistar1 = getDPhiStar(track1, track2, mTwoTrackRadius, bSign); + float dphistar2 = getDPhiStar(track1, track2, 2.5, bSign); + + const float kLimit = mTwoTrackDistance * 3; + + if (TMath::Abs(dphistar1) < kLimit || TMath::Abs(dphistar2) < kLimit || dphistar1 * dphistar2 < 0) { + float dphistarminabs = 1e5; + float dphistarmin = 1e5; + for (Double_t rad = mTwoTrackRadius; rad < 2.51; rad += 0.01) { + float dphistar = getDPhiStar(track1, track2, rad, bSign); + + float dphistarabs = TMath::Abs(dphistar); + + if (dphistarabs < dphistarminabs) { + dphistarmin = dphistar; + dphistarminabs = dphistarabs; + } + } + + if (histogramRegistry != nullptr) { + histogramRegistry->fill(HIST("TwoTrackDistancePt_0"), deta, dphistarmin, TMath::Abs(track1.pt() - track2.pt())); + } + + if (dphistarminabs < mTwoTrackDistance && TMath::Abs(deta) < mTwoTrackDistance) { + //LOGF(debug, "Removed track pair %ld %ld with %f %f %f %f %d %f %f %d %d", track1.index(), track2.index(), deta, dphistarminabs, track1.phi2(), track1.pt(), track1.charge(), track2.phi2(), track2.pt(), track2.charge(), bSign); + return true; + } + + if (histogramRegistry != nullptr) { + histogramRegistry->fill(HIST("TwoTrackDistancePt_1"), deta, dphistarmin, TMath::Abs(track1.pt() - track2.pt())); + } + } + } + + return false; +} + +template +bool PairCuts::conversionCut(T const& track1, T const& track2, Particle conv, double cut) +{ + //LOGF(info, "pt is %f %f", track1.pt(), track2.pt()); + + if (cut < 0) { + return false; + } + + double massD1, massD2, massM; + + switch (conv) { + case Photon: + massD1 = 0.51e-3; + massD2 = 0.51e-3; + massM = 0; + break; + case K0: + massD1 = 0.1396; + massD2 = 0.1396; + massM = 0.4976; + break; + case Lambda: + massD1 = 0.9383; + massD2 = 0.1396; + massM = 1.115; + break; + case Phi: + massD1 = 0.4937; + massD2 = 0.4937; + massM = 1.019; + break; + case Rho: + massD1 = 0.1396; + massD2 = 0.1396; + massM = 0.770; + break; + default: + LOGF(fatal, "Particle now known"); + return false; + break; + } + + auto massC = getInvMassSquaredFast(track1, massD1, track2, massD2); + + if (TMath::Abs(massC - massM * massM) > cut * 5) { + return false; + } + + massC = getInvMassSquared(track1, massD1, track2, massD2); + + if (histogramRegistry != nullptr) { + histogramRegistry->fill(HIST("ControlConvResonances"), static_cast(conv), massC - massM * massM); + } + + if (massC > (massM - cut) * (massM - cut) && massC < (massM + cut) * (massM + cut)) { + return true; + } + + return false; +} + +template +double PairCuts::getInvMassSquared(T const& track1, double m0_1, T const& track2, double m0_2) +{ + // calculate inv mass squared + // same can be achieved, but with more computing time with + /*TLorentzVector photon, p1, p2; + p1.SetPtEtaPhiM(triggerParticle->Pt(), triggerEta, triggerParticle->Phi(), 0.510e-3); + p2.SetPtEtaPhiM(particle->Pt(), eta[j], particle->Phi(), 0.510e-3); + photon = p1+p2; + photon.M()*/ + + float tantheta1 = 1e10; + + if (track1.eta() < -1e-10 || track1.eta() > 1e-10) { + float expTmp = TMath::Exp(-track1.eta()); + tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float tantheta2 = 1e10; + if (track2.eta() < -1e-10 || track2.eta() > 1e-10) { + float expTmp = TMath::Exp(-track2.eta()); + tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float e1squ = m0_1 * m0_1 + track1.pt() * track1.pt() * (1.0 + 1.0 / tantheta1 / tantheta1); + float e2squ = m0_2 * m0_2 + track2.pt() * track2.pt() * (1.0 + 1.0 / tantheta2 / tantheta2); + + float mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (track1.pt() * track2.pt() * (TMath::Cos(track1.phi() - track2.phi()) + 1.0 / tantheta1 / tantheta2))); + + // LOGF(debug, "%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2); + + return mass2; +} + +template +double PairCuts::getInvMassSquaredFast(T const& track1, double m0_1, T const& track2, double m0_2) +{ + // calculate inv mass squared approximately + + const float eta1 = track1.eta(); + const float eta2 = track2.eta(); + const float phi1 = track1.phi(); + const float phi2 = track2.phi(); + const float pt1 = track1.pt(); + const float pt2 = track2.pt(); + + float tantheta1 = 1e10; + + if (eta1 < -1e-10 || eta1 > 1e-10) { + float expTmp = 1.0 - eta1 + eta1 * eta1 / 2 - eta1 * eta1 * eta1 / 6 + eta1 * eta1 * eta1 * eta1 / 24; + tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float tantheta2 = 1e10; + if (eta2 < -1e-10 || eta2 > 1e-10) { + float expTmp = 1.0 - eta2 + eta2 * eta2 / 2 - eta2 * eta2 * eta2 / 6 + eta2 * eta2 * eta2 * eta2 / 24; + tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float e1squ = m0_1 * m0_1 + pt1 * pt1 * (1.0 + 1.0 / tantheta1 / tantheta1); + float e2squ = m0_2 * m0_2 + pt2 * pt2 * (1.0 + 1.0 / tantheta2 / tantheta2); + + // fold onto 0...pi + float deltaPhi = TMath::Abs(phi1 - phi2); + while (deltaPhi > M_PI * 2) { + deltaPhi -= M_PI * 2; + } + if (deltaPhi > M_PI) { + deltaPhi = M_PI * 2 - deltaPhi; + } + + float cosDeltaPhi = 0; + if (deltaPhi < M_PI / 3) { + cosDeltaPhi = 1.0 - deltaPhi * deltaPhi / 2 + deltaPhi * deltaPhi * deltaPhi * deltaPhi / 24; + } else if (deltaPhi < 2 * M_PI / 3) { + cosDeltaPhi = -(deltaPhi - M_PI / 2) + 1.0 / 6 * TMath::Power((deltaPhi - M_PI / 2), 3); + } else { + cosDeltaPhi = -1.0 + 1.0 / 2.0 * (deltaPhi - M_PI) * (deltaPhi - M_PI) - 1.0 / 24.0 * TMath::Power(deltaPhi - M_PI, 4); + } + + double mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (pt1 * pt2 * (cosDeltaPhi + 1.0 / tantheta1 / tantheta2))); + + //LOGF(debug, "%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2); + + return mass2; +} + +template +float PairCuts::getDPhiStar(T const& track1, T const& track2, float radius, float bSign) +{ + // + // calculates dphistar + // + + auto phi1 = track1.phi(); + auto pt1 = track1.pt(); + auto charge1 = track1.charge(); + + auto phi2 = track2.phi(); + auto pt2 = track2.pt(); + auto charge2 = track2.charge(); + + float dphistar = phi1 - phi2 - charge1 * bSign * TMath::ASin(0.075 * radius / pt1) + charge2 * bSign * TMath::ASin(0.075 * radius / pt2); + + if (dphistar > M_PI) { + dphistar = M_PI * 2 - dphistar; + } + if (dphistar < -M_PI) { + dphistar = -M_PI * 2 - dphistar; + } + if (dphistar > M_PI) { // might look funny but is needed + dphistar = M_PI * 2 - dphistar; + } + + return dphistar; +} + +#endif diff --git a/Analysis/Core/include/AnalysisCore/RecoDecay.h b/Analysis/Core/include/AnalysisCore/RecoDecay.h new file mode 100644 index 0000000000000..fb4997040c214 --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/RecoDecay.h @@ -0,0 +1,839 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RecoDecay.h +/// \brief Implementation of the RecoDecay class. +/// +/// \author Vít Kučera , CERN + +#ifndef O2_ANALYSIS_RECODECAY_H_ +#define O2_ANALYSIS_RECODECAY_H_ + +#include +#include +#include +#include + +#include +#include + +#include "CommonConstants/MathConstants.h" +#include "Framework/Logger.h" + +using std::array; +using namespace o2::constants::math; + +/// Base class for calculating properties of reconstructed decays. + +class RecoDecay +{ + public: + /// Default constructor + RecoDecay() = default; + + /// Default destructor + ~RecoDecay() = default; + + // Auxiliary functions + + /// Sums numbers. + /// \param args arbitrary number of numbers of arbitrary types + /// \return sum of numbers + template + static auto sum(const T&... args) + { + return (args + ...); + } + + /// Squares a number. + /// \note Promotes number to double before squaring to avoid precision loss in float multiplication. + /// \param num a number of arbitrary type + /// \return number squared + template + static double sq(T num) + { + return (double)num * (double)num; + } + + /// Sums squares of numbers. + /// \note Promotes numbers to double before squaring to avoid precision loss in float multiplication. + /// \param args arbitrary number of numbers of arbitrary types + /// \return sum of squares of numbers + template + static double sumOfSquares(const T&... args) + { + return (((double)args * (double)args) + ...); + } + + /// Calculates square root of a sum of squares of numbers. + /// \param args arbitrary number of numbers of arbitrary types + /// \return square root of sum of squares of numbers + template + static double sqrtSumOfSquares(const T&... args) + { + return std::sqrt(sumOfSquares(args...)); + } + + /// Sums i-th elements of containers. + /// \param index element index + /// \param args pack of containers of elements accessible by index + /// \return sum of i-th elements + template + static auto getElement(int index, const T&... args) + { + return (args[index] + ...); + } + + /// Sums 3-vectors. + /// \param args pack of 3-vector arrays + /// \return sum of vectors + template + static auto sumOfVec(const array&... args) + { + return array{getElement(0, args...), getElement(1, args...), getElement(2, args...)}; + } + + /// Calculates scalar product of vectors. + /// \note Promotes numbers to double to avoid precision loss in float multiplication. + /// \param N dimension + /// \param vec1,vec2 vectors + /// \return scalar product + template + static double dotProd(const array& vec1, const array& vec2) + { + double res{0}; + for (auto iDim = 0; iDim < N; ++iDim) { + res += (double)vec1[iDim] * (double)vec2[iDim]; + } + return res; + } + + /// FIXME: probably cross and dot products should be in some utility class + /// Calculates cross product of vectors in three dimensions. + /// \note Promotes numbers to double to avoid precision loss in float multiplication. + /// \param vec1,vec2 vectors + /// \return cross-product vector + template + static array crossProd(const array& vec1, const array& vec2) + { + return array{((double)vec1[1] * (double)vec2[2]) - ((double)vec1[2] * (double)vec2[1]), + ((double)vec1[2] * (double)vec2[0]) - ((double)vec1[0] * (double)vec2[2]), + ((double)vec1[0] * (double)vec2[1]) - ((double)vec1[1] * (double)vec2[0])}; + } + + /// Calculates magnitude squared of a vector. + /// \param N dimension + /// \param vec vector + /// \return magnitude squared + template + static double mag2(const array& vec) + { + return dotProd(vec, vec); + } + + /// Calculates 3D distance between two points. + /// \param point1,point2 {x, y, z} coordinates of points + /// \return 3D distance between two points + template + static double distance(const T& point1, const U& point2) + { + return sqrtSumOfSquares(point1[0] - point2[0], point1[1] - point2[1], point1[2] - point2[2]); + } + + /// Calculates 2D {x, y} distance between two points. + /// \param point1,point2 {x, y, z} or {x, y} coordinates of points + /// \return 2D {x, y} distance between two points + template + static double distanceXY(const T& point1, const U& point2) + { + return sqrtSumOfSquares(point1[0] - point2[0], point1[1] - point2[1]); + } + + // Calculation of kinematic quantities + + /// Calculates pseudorapidity. + /// \param mom 3-momentum array + /// \return pseudorapidity + template + static double Eta(const array& mom) + { + // eta = arctanh(pz/p) + if (std::abs(mom[0]) < Almost0 && std::abs(mom[1]) < Almost0) { // very small px and py + return (double)(mom[2] > 0 ? VeryBig : -VeryBig); + } + return (double)(std::atanh(mom[2] / P(mom))); + } + + /// Calculates rapidity. + /// \param mom 3-momentum array + /// \param mass mass + /// \return rapidity + template + static double Y(const array& mom, U mass) + { + // y = arctanh(pz/E) + return std::atanh(mom[2] / E(mom, mass)); + } + + /// Calculates azimuthal angle from x and y momentum components. + /// \param px,py {x, y} momentum components + /// \return azimuthal angle + static double Phi(double px, double py) + { + // phi = pi+TMath::Atan2(-py,-px) + return (double)(o2::constants::math::PI + std::atan2(-py, -px)); + } + + /// Calculates azimuthal angle from 3-(or 2-)momenta. + /// \param args pack of 3-(or 2-)momentum arrays + /// \return azimuthal angle + template + static double Phi(const array& vec) + { + return Phi(vec[0], vec[1]); + } + + /// Calculates cosine of pointing angle. + /// \param posPV {x, y, z} position of the primary vertex + /// \param posSV {x, y, z} position of the secondary vertex + /// \param mom 3-momentum array + /// \return cosine of pointing angle + template + static double CPA(const T& posPV, const U& posSV, const array& mom) + { + // CPA = (l . p)/(|l| |p|) + auto lineDecay = array{posSV[0] - posPV[0], posSV[1] - posPV[1], posSV[2] - posPV[2]}; + auto cos = dotProd(lineDecay, mom) / std::sqrt(mag2(lineDecay) * mag2(mom)); + if (cos < -1.) { + return -1.; + } + if (cos > 1.) { + return 1.; + } + return cos; + } + + /// Calculates cosine of pointing angle in the {x, y} plane. + /// \param posPV {x, y, z} or {x, y} position of the primary vertex + /// \param posSV {x, y, z} or {x, y} position of the secondary vertex + /// \param mom {x, y, z} or {x, y} momentum array + /// \return cosine of pointing angle in {x, y} + template + static double CPAXY(const T& posPV, const U& posSV, const array& mom) + { + // CPAXY = (r . pT)/(|r| |pT|) + auto lineDecay = array{posSV[0] - posPV[0], posSV[1] - posPV[1]}; + auto momXY = array{mom[0], mom[1]}; + auto cos = dotProd(lineDecay, momXY) / std::sqrt(mag2(lineDecay) * mag2(momXY)); + if (cos < -1.) { + return -1.; + } + if (cos > 1.) { + return 1.; + } + return cos; + } + + /// Calculates proper lifetime times c. + /// \note Promotes numbers to double before squaring to avoid precision loss in float multiplication. + /// \param mom 3-momentum array + /// \param mass mass + /// \param length decay length + /// \return proper lifetime times c + template + static double Ct(const array& mom, U length, V mass) + { + // c t = l m c^2/(p c) + return (double)length * (double)mass / P(mom); + } + + /// Calculates cosine of θ* (theta star). + /// \note Implemented for 2 prongs only. + /// \param arrMom array of two 3-momentum arrays + /// \param arrMass array of two masses (in the same order as arrMom) + /// \param mTot assumed mass of mother particle + /// \param iProng index of the prong + /// \return cosine of θ* of the i-th prong under the assumption of the invariant mass + template + static double CosThetaStar(const array, 2>& arrMom, const array& arrMass, V mTot, int iProng) + { + auto pVecTot = PVec(arrMom[0], arrMom[1]); // momentum of the mother particle + auto pTot = P(pVecTot); // magnitude of the momentum of the mother particle + auto eTot = E(pTot, mTot); // energy of the mother particle + auto gamma = eTot / mTot; // γ, Lorentz gamma factor of the mother particle + auto beta = pTot / eTot; // β, velocity of the mother particle + auto pStar = std::sqrt(sq(sq(mTot) - sq(arrMass[0]) - sq(arrMass[1])) - sq(2 * arrMass[0] * arrMass[1])) / (2 * mTot); // p*, prong momentum in the rest frame of the mother particle + // p* = √[(M^2 - m1^2 - m2^2)^2 - 4 m1^2 m2^2]/2M + // Lorentz transformation of the longitudinal momentum of the prong into the detector frame: + // p_L,i = γ (p*_L,i + β E*_i) + // p*_L,i = p_L,i/γ - β E*_i + // cos(θ*_i) = (p_L,i/γ - β E*_i)/p* + return (dotProd(arrMom[iProng], pVecTot) / (pTot * gamma) - beta * E(pStar, arrMass[iProng])) / pStar; + } + + /// Sums 3-momenta. + /// \param args pack of 3-momentum arrays + /// \return total 3-momentum array + template + static auto PVec(const array&... args) + { + return sumOfVec(args...); + } + + /// Calculates momentum squared from momentum components. + /// \param px,py,pz {x, y, z} momentum components + /// \return momentum squared + static double P2(double px, double py, double pz) + { + return sumOfSquares(px, py, pz); + } + + /// Calculates total momentum squared of a sum of 3-momenta. + /// \param args pack of 3-momentum arrays + /// \return total momentum squared + template + static double P2(const array&... args) + { + return sumOfSquares(getElement(0, args...), getElement(1, args...), getElement(2, args...)); + } + + /// Calculates (total) momentum magnitude. + /// \param args {x, y, z} momentum components or pack of 3-momentum arrays + /// \return (total) momentum magnitude + template + static double P(const T&... args) + { + return std::sqrt(P2(args...)); + } + + /// Calculates transverse momentum squared from momentum components. + /// \param px,py {x, y} momentum components + /// \return transverse momentum squared + static double Pt2(double px, double py) + { + return sumOfSquares(px, py); + } + + /// Calculates total transverse momentum squared of a sum of 3-(or 2-)momenta. + /// \param args pack of 3-(or 2-)momentum arrays + /// \return total transverse momentum squared + template + static double Pt2(const array&... args) + { + return sumOfSquares(getElement(0, args...), getElement(1, args...)); + } + + /// Calculates (total) transverse momentum. + /// \param args {x, y} momentum components or pack of 3-momentum arrays + /// \return (total) transverse momentum + template + static double Pt(const T&... args) + { + return std::sqrt(Pt2(args...)); + } + + /// Calculates energy squared from momentum and mass. + /// \param args momentum magnitude, mass + /// \param args {x, y, z} momentum components, mass + /// \return energy squared + template + static double E2(T... args) + { + return sumOfSquares(args...); + } + + /// Calculates energy squared from momentum vector and mass. + /// \param mom 3-momentum array + /// \param mass mass + /// \return energy squared + template + static double E2(const array& mom, U mass) + { + return E2(mom[0], mom[1], mom[2], mass); + } + + /// Calculates energy from momentum and mass. + /// \param args momentum magnitude, mass + /// \param args {x, y, z} momentum components, mass + /// \param args 3-momentum array, mass + /// \return energy + template + static double E(const T&... args) + { + return std::sqrt(E2(args...)); + } + + /// Calculates invariant mass squared from momentum magnitude and energy. + /// \param mom momentum magnitude + /// \param energy energy + /// \return invariant mass squared + static double M2(double mom, double energy) + { + return energy * energy - mom * mom; + } + + /// Calculates invariant mass squared from momentum aray and energy. + /// \param mom 3-momentum array + /// \param energy energy + /// \return invariant mass squared + template + static double M2(const array& mom, double energy) + { + return energy * energy - P2(mom); + } + + /// Calculates invariant mass squared from momenta and masses of several particles (prongs). + /// \param N number of prongs + /// \param arrMom array of N 3-momentum arrays + /// \param arrMass array of N masses (in the same order as arrMom) + /// \return invariant mass squared + template + static double M2(const array, N>& arrMom, const array& arrMass) + { + array momTotal{0., 0., 0.}; // candidate momentum vector + double energyTot{0.}; // candidate energy + for (auto iProng = 0; iProng < N; ++iProng) { + for (auto iMom = 0; iMom < 3; ++iMom) { + momTotal[iMom] += arrMom[iProng][iMom]; + } // loop over momentum components + energyTot += E(arrMom[iProng], arrMass[iProng]); + } // loop over prongs + return energyTot * energyTot - P2(momTotal); + } + + /// Calculates invariant mass. + /// \param args momentum magnitude, energy + /// \param args 3-momentum array, energy + /// \param args array of momenta, array of masses + /// \return invariant mass + template + static double M(const T&... args) + { + return std::sqrt(M2(args...)); + } + + // Calculation of topological quantities + + /// Calculates impact parameter in the bending plane of the particle w.r.t. a point + /// \param point {x, y, z} position of the point + /// \param posSV {x, y, z} position of the secondary vertex + /// \param mom {x, y, z} particle momentum array + /// \return impact parameter in {x, y} + template + static double ImpParXY(const T& point, const U& posSV, const array& mom) + { + // Ported from AliAODRecoDecay::ImpParXY + auto flightLineXY = array{posSV[0] - point[0], posSV[1] - point[1]}; + auto k = dotProd(flightLineXY, array{mom[0], mom[1]}) / Pt2(mom); + auto dx = flightLineXY[0] - k * (double)mom[0]; + auto dy = flightLineXY[1] - k * (double)mom[1]; + auto absImpPar = sqrtSumOfSquares(dx, dy); + auto flightLine = array{posSV[0] - point[0], posSV[1] - point[1], posSV[2] - point[2]}; + auto cross = crossProd(mom, flightLine); + return (cross[2] > 0. ? absImpPar : -1. * absImpPar); + } + + /// Calculates the difference between measured and expected track impact parameter + /// normalized to its uncertainty + /// \param decLenXY decay lenght in {x, y} plane + /// \param errDecLenXY error on decay lenght in {x, y} plane + /// \param momMother {x, y, z} or {x, y} candidate momentum array + /// \param impParProng prong impact parameter + /// \param errImpParProng error on prong impact parameter + /// \param momProng {x, y, z} or {x, y} prong momentum array + /// \return normalized difference between expected and observed impact parameter + template + static double normImpParMeasMinusExpProng(T decLenXY, U errDecLenXY, const array& momMother, W impParProng, + X errImpParProng, const array& momProng) + { + // Ported from AliAODRecoDecayHF::Getd0MeasMinusExpProng adding normalization directly in the function + auto sinThetaP = ((double)momProng[0] * (double)momMother[1] - (double)momProng[1] * (double)momMother[0]) / + (Pt(momProng) * Pt(momMother)); + auto diff = impParProng - (double)decLenXY * sinThetaP; + auto errImpParExpProng = (double)errDecLenXY * sinThetaP; + auto errDiff = sqrtSumOfSquares(errImpParProng, errImpParExpProng); + return (errDiff > 0. ? diff / errDiff : 0.); + } + + /// Calculates maximum normalized difference between measured and expected impact parameter of candidate prongs + /// \param posPV {x, y, z} or {x, y} position of primary vertex + /// \param posSV {x, y, z} or {x, y} position of secondary vertex + /// \param errDecLenXY error on decay lenght in {x, y} plane + /// \param momMother {x, y, z} or {x, y} candidate momentum array + /// \param arrImpPar array of prong impact parameters + /// \param arrErrImpPar array of errors on prong impact parameter (same order as arrImpPar) + /// \param momMom array of {x, y, z} or {x, y} prong momenta (same order as arrImpPar) + /// \return maximum normalized difference between expected and observed impact parameters + template + static double maxNormalisedDeltaIP(const T& posPV, const U& posSV, V errDecLenXY, const array& momMother, + const array& arrImpPar, const array& arrErrImpPar, + const array, N>& arrMom) + { + auto decLenXY = distanceXY(posPV, posSV); + double maxNormDeltaIP{0.}; + for (auto iProng = 0; iProng < N; ++iProng) { + auto prongNormDeltaIP = normImpParMeasMinusExpProng(decLenXY, errDecLenXY, momMother, arrImpPar[iProng], + arrErrImpPar[iProng], arrMom[iProng]); + if (std::abs(prongNormDeltaIP) > std::abs(maxNormDeltaIP)) { + maxNormDeltaIP = prongNormDeltaIP; + } + } + return maxNormDeltaIP; + } + + /// Returns particle mass based on PDG code. + /// \param pdg PDG code + /// \return particle mass + static double getMassPDG(int pdg) + { + // Try to get the particle mass from the list first. + for (const auto& particle : mListMass) { + if (std::get<0>(particle) == pdg) { + return std::get<1>(particle); + } + } + // Get the mass of the new particle and add it in the list. + auto newMass = TDatabasePDG::Instance()->GetParticle(pdg)->Mass(); + mListMass.push_back(std::make_tuple(pdg, newMass)); + return newMass; + } + + /// Finds the mother of an MC particle by looking for the expected PDG code in the mother chain. + /// \param particlesMC table with MC particles + /// \param particle MC particle + /// \param PDGMother expected mother PDG code + /// \param acceptAntiParticles switch to accept the antiparticle of the expected mother + /// \param sign antiparticle indicator of the found mother w.r.t. PDGMother; 1 if particle, -1 if antiparticle, 0 if mother not found + /// \return index of the mother particle if found, -1 otherwise + template + static int getMother(const T& particlesMC, const U& particle, int PDGMother, bool acceptAntiParticles = false, int8_t* sign = nullptr) + { + int8_t sgn = 0; // 1 if the expected mother is particle, -1 if antiparticle (w.r.t. PDGMother) + int indexMother = -1; // index of the final matched mother, if found + auto particleMother = particle; // Initialise loop over mothers. + int stage = 0; // mother tree level (just for debugging) + if (sign) { + *sign = sgn; + } + while (particleMother.mother0() > -1) { + auto indexMotherTmp = particleMother.mother0(); + particleMother = particlesMC.iteratorAt(indexMotherTmp); + // Check mother's PDG code. + auto PDGParticleIMother = particleMother.pdgCode(); // PDG code of the mother + //printf("getMother: "); + //for (int i = stage; i < 0; i++) // Indent to make the tree look nice. + // printf(" "); + //printf("Stage %d: Mother PDG: %d\n", stage, PDGParticleIMother); + if (PDGParticleIMother == PDGMother) { // exact PDG match + sgn = 1; + indexMother = indexMotherTmp; + break; + } else if (acceptAntiParticles && PDGParticleIMother == -PDGMother) { // antiparticle PDG match + sgn = -1; + indexMother = indexMotherTmp; + break; + } + stage--; + } + if (sign) { + *sign = sgn; + } + return indexMother; + } + + /// Gets the complete list of indices of final-state daughters of an MC particle. + /// \param particlesMC table with MC particles + /// \param index index of the MC particle + /// \param list vector where the indices of final-state daughters will be added + /// \param arrPDGFinal array of PDG codes of particles to be considered final + /// \param depthMax maximum decay tree level; Daughters at this level (or beyond) will be considered final. If -1, all levels are considered. + /// \param stage decay tree level; If different from 0, the particle itself will be added in the list in case it has no daughters. + /// \note Final state is defined as particles from arrPDGFinal plus final daughters of any other decay branch. + template + static void getDaughters(const T& particlesMC, + int index, + std::vector* list, + const array& arrPDGFinal, + int depthMax = -1, + int8_t stage = 0) + { + if (index <= -1) { + //Printf("getDaughters: Error: No particle: index %d", index); + return; + } + if (!list) { + //Printf("getDaughters: Error: No list!"); + return; + } + // Get the particle. + auto particle = particlesMC.iteratorAt(index); + bool isFinal = false; // Flag to indicate the end of recursion + if (depthMax > -1 && stage >= depthMax) { // Maximum depth has been reached (or exceeded). + isFinal = true; + } + // Get the range of daughter indices. + int indexDaughterFirst = particle.daughter0(); + int indexDaughterLast = particle.daughter1(); + // Check whether there are any daughters. + if (!isFinal && indexDaughterFirst <= -1 && indexDaughterLast <= -1) { + // If the original particle has no daughters, we do nothing and exit. + if (stage == 0) { + //Printf("getDaughters: No daughters of %d", index); + return; + } + // If this is not the original particle, we are at the end of this branch and this particle is final. + isFinal = true; + } + // If the particle has daughters but is considered to be final, we label it as final. + auto PDGParticle = particle.pdgCode(); + if (!isFinal) { + for (auto PDGi : arrPDGFinal) { + if (std::abs(PDGParticle) == std::abs(PDGi)) { // Accept antiparticles. + isFinal = true; + break; + } + } + } + // If the particle is labelled as final, we add this particle in the list of final daughters and exit. + if (isFinal) { + //printf("getDaughters: "); + //for (int i = 0; i < stage; i++) // Indent to make the tree look nice. + // printf(" "); + //printf("Stage %d: Adding %d (PDG %d) as final daughter.\n", stage, index, PDGParticle); + list->push_back(index); + return; + } + // If we are here, we have to follow the daughter tree. + //printf("getDaughters: "); + //for (int i = 0; i < stage; i++) // Indent to make the tree look nice. + // printf(" "); + //printf("Stage %d: %d (PDG %d) -> %d-%d\n", stage, index, PDGParticle, indexDaughterFirst, indexDaughterLast); + // Call itself to get daughters of daughters recursively. + // Get daughters of the first daughter. + if (indexDaughterFirst > -1) { + getDaughters(particlesMC, indexDaughterFirst, list, arrPDGFinal, depthMax, stage + 1); + } + // Get daughters of the daughters in between if any. + // Daughter indices are supposed to be consecutive and in increasing order. + // Reverse order means two daughters. + if (indexDaughterFirst > -1 && indexDaughterLast > -1) { + for (auto iD = indexDaughterFirst + 1; iD < indexDaughterLast; ++iD) { + getDaughters(particlesMC, iD, list, arrPDGFinal, depthMax, stage + 1); + } + } + // Get daughters of the last daughter if different from the first one. + // Same indices indicate a single daughter. + if (indexDaughterLast > -1 && indexDaughterLast != indexDaughterFirst) { + getDaughters(particlesMC, indexDaughterLast, list, arrPDGFinal, depthMax, stage + 1); + } + } + + /// Checks whether the reconstructed decay candidate is the expected decay. + /// \param particlesMC table with MC particles + /// \param arrDaughters array of candidate daughters + /// \param PDGMother expected mother PDG code + /// \param arrPDGDaughters array of expected daughter PDG codes + /// \param acceptAntiParticles switch to accept the antiparticle version of the expected decay + /// \param sign antiparticle indicator of the found mother w.r.t. PDGMother; 1 if particle, -1 if antiparticle, 0 if mother not found + /// \param depthMax maximum decay tree level to check; Daughters up to this level will be considered. If -1, all levels are considered. + /// \return index of the mother particle if the mother and daughters are correct, -1 otherwise + template + static int getMatchedMCRec(const T& particlesMC, const array& arrDaughters, int PDGMother, array arrPDGDaughters, bool acceptAntiParticles = false, int8_t* sign = nullptr, int depthMax = 1) + { + //Printf("MC Rec: Expected mother PDG: %d", PDGMother); + int8_t sgn = 0; // 1 if the expected mother is particle, -1 if antiparticle (w.r.t. PDGMother) + int indexMother = -1; // index of the mother particle + std::vector arrAllDaughtersIndex; // vector of indices of all daughters of the mother of the first provided daughter + array arrDaughtersIndex; // array of indices of provided daughters + if (sign) { + *sign = sgn; + } + // Loop over decay candidate prongs + for (auto iProng = 0; iProng < N; ++iProng) { + auto particleI = arrDaughters[iProng].label(); // ith daughter particle + arrDaughtersIndex[iProng] = particleI.globalIndex(); + // Get the list of daughter indices from the mother of the first prong. + if (iProng == 0) { + // Get the mother index and its sign. + // PDG code of the first daughter's mother determines whether the expected mother is a particle or antiparticle. + indexMother = getMother(particlesMC, particleI, PDGMother, acceptAntiParticles, &sgn); + // Check whether mother was found. + if (indexMother <= -1) { + //Printf("MC Rec: Rejected: bad mother index or PDG"); + return -1; + } + //Printf("MC Rec: Good mother: %d", indexMother); + auto particleMother = particlesMC.iteratorAt(indexMother); + int indexDaughterFirst = particleMother.daughter0(); // index of the first direct daughter + int indexDaughterLast = particleMother.daughter1(); // index of the last direct daughter + // Check the daughter indices. + if (indexDaughterFirst <= -1 && indexDaughterLast <= -1) { + //Printf("MC Rec: Rejected: bad daughter index range: %d-%d", indexDaughterFirst, indexDaughterLast); + return -1; + } + // Check that the number of direct daughters is not larger than the number of expected final daughters. + if (indexDaughterFirst > -1 && indexDaughterLast > -1 && indexDaughterLast - indexDaughterFirst + 1 > N) { + //Printf("MC Rec: Rejected: too many direct daughters: %d (expected %d final)", indexDaughterLast - indexDaughterFirst + 1, N); + return -1; + } + // Get the list of actual final daughters. + getDaughters(particlesMC, indexMother, &arrAllDaughtersIndex, arrPDGDaughters, depthMax); + //printf("MC Rec: Mother %d has %d final daughters:", indexMother, arrAllDaughtersIndex.size()); + //for (auto i : arrAllDaughtersIndex) { + // printf(" %d", i); + //} + //printf("\n"); + // Check whether the number of actual final daughters is equal to the number of expected final daughters (i.e. the number of provided prongs). + if (arrAllDaughtersIndex.size() != N) { + //Printf("MC Rec: Rejected: incorrect number of final daughters: %d (expected %d)", arrAllDaughtersIndex.size(), N); + return -1; + } + } + // Check that the daughter is in the list of final daughters. + // (Check that the daughter is not a stepdaughter, i.e. particle pointing to the mother while not being its daughter.) + bool isDaughterFound = false; // Is the index of this prong among the remaining expected indices of daughters? + for (auto iD = 0; iD < arrAllDaughtersIndex.size(); ++iD) { + if (arrDaughtersIndex[iProng] == arrAllDaughtersIndex[iD]) { + arrAllDaughtersIndex[iD] = -1; // Remove this index from the array of expected daughters. (Rejects twin daughters, i.e. particle considered twice as a daughter.) + isDaughterFound = true; + break; + } + } + if (!isDaughterFound) { + //Printf("MC Rec: Rejected: bad daughter index: %d not in the list of final daughters", arrDaughtersIndex[iProng]); + return -1; + } + // Check daughter's PDG code. + auto PDGParticleI = particleI.pdgCode(); // PDG code of the ith daughter + //Printf("MC Rec: Daughter %d PDG: %d", iProng, PDGParticleI); + bool isPDGFound = false; // Is the PDG code of this daughter among the remaining expected PDG codes? + for (auto iProngCp = 0; iProngCp < N; ++iProngCp) { + if (PDGParticleI == sgn * arrPDGDaughters[iProngCp]) { + arrPDGDaughters[iProngCp] = 0; // Remove this PDG code from the array of expected ones. + isPDGFound = true; + break; + } + } + if (!isPDGFound) { + //Printf("MC Rec: Rejected: bad daughter PDG: %d", PDGParticleI); + return -1; + } + } + //Printf("MC Rec: Accepted: m: %d", indexMother); + if (sign) { + *sign = sgn; + } + return indexMother; + } + + /// Checks whether the MC particle is the expected one. + /// \param particlesMC table with MC particles + /// \param candidate candidate MC particle + /// \param PDGParticle expected particle PDG code + /// \param acceptAntiParticles switch to accept the antiparticle + /// \return true if PDG code of the particle is correct, false otherwise + template + static int isMatchedMCGen(const T& particlesMC, const U& candidate, int PDGParticle, bool acceptAntiParticles = false) + { + array arrPDGDaughters; + return isMatchedMCGen(particlesMC, candidate, PDGParticle, std::move(arrPDGDaughters), acceptAntiParticles); + } + + /// Check whether the MC particle is the expected one and whether it decayed via the expected decay channel. + /// \param particlesMC table with MC particles + /// \param candidate candidate MC particle + /// \param PDGParticle expected particle PDG code + /// \param arrPDGDaughters array of expected PDG codes of daughters + /// \param acceptAntiParticles switch to accept the antiparticle + /// \param sign antiparticle indicator of the candidate w.r.t. PDGParticle; 1 if particle, -1 if antiparticle, 0 if not matched + /// \param depthMax maximum decay tree level to check; Daughters up to this level will be considered. If -1, all levels are considered. + /// \return true if PDG codes of the particle and its daughters are correct, false otherwise + template + static bool isMatchedMCGen(const T& particlesMC, const U& candidate, int PDGParticle, array arrPDGDaughters, bool acceptAntiParticles = false, int8_t* sign = nullptr, int depthMax = 1) + { + //Printf("MC Gen: Expected particle PDG: %d", PDGParticle); + int8_t sgn = 0; // 1 if the expected mother is particle, -1 if antiparticle (w.r.t. PDGParticle) + if (sign) { + *sign = sgn; + } + // Check the PDG code of the particle. + auto PDGCandidate = candidate.pdgCode(); + //Printf("MC Gen: Candidate PDG: %d", PDGCandidate); + if (PDGCandidate == PDGParticle) { // exact PDG match + sgn = 1; + } else if (acceptAntiParticles && PDGCandidate == -PDGParticle) { // antiparticle PDG match + sgn = -1; + } + if (sgn == 0) { + //Printf("MC Gen: Rejected: bad particle PDG: %s%d != %d", acceptAntiParticles ? "abs " : "", PDGCandidate, std::abs(PDGParticle)); + return false; + } + // Check the PDG codes of the decay products. + if (N > 0) { + //Printf("MC Gen: Checking %d daughters", N); + std::vector arrAllDaughtersIndex; // vector of indices of all daughters + int indexDaughterFirst = candidate.daughter0(); // index of the first direct daughter + int indexDaughterLast = candidate.daughter1(); // index of the last direct daughter + // Check the daughter indices. + if (indexDaughterFirst <= -1 && indexDaughterLast <= -1) { + //Printf("MC Gen: Rejected: bad daughter index range: %d-%d", indexDaughterFirst, indexDaughterLast); + return false; + } + // Check that the number of direct daughters is not larger than the number of expected final daughters. + if (indexDaughterFirst > -1 && indexDaughterLast > -1 && indexDaughterLast - indexDaughterFirst + 1 > N) { + //Printf("MC Gen: Rejected: too many direct daughters: %d (expected %d final)", indexDaughterLast - indexDaughterFirst + 1, N); + return false; + } + // Get the list of actual final daughters. + getDaughters(particlesMC, candidate.globalIndex(), &arrAllDaughtersIndex, arrPDGDaughters, depthMax); + //printf("MC Gen: Mother %d has %d final daughters:", candidate.globalIndex(), arrAllDaughtersIndex.size()); + //for (auto i : arrAllDaughtersIndex) { + // printf(" %d", i); + //} + //printf("\n"); + // Check whether the number of final daughters is equal to the required number. + if (arrAllDaughtersIndex.size() != N) { + //Printf("MC Gen: Rejected: incorrect number of final daughters: %d (expected %d)", arrAllDaughtersIndex.size(), N); + return false; + } + // Check daughters' PDG codes. + for (auto indexDaughterI : arrAllDaughtersIndex) { + auto candidateDaughterI = particlesMC.iteratorAt(indexDaughterI); // ith daughter particle + auto PDGCandidateDaughterI = candidateDaughterI.pdgCode(); // PDG code of the ith daughter + //Printf("MC Gen: Daughter %d PDG: %d", indexDaughterI, PDGCandidateDaughterI); + bool isPDGFound = false; // Is the PDG code of this daughter among the remaining expected PDG codes? + for (auto iProngCp = 0; iProngCp < N; ++iProngCp) { + if (PDGCandidateDaughterI == sgn * arrPDGDaughters[iProngCp]) { + arrPDGDaughters[iProngCp] = 0; // Remove this PDG code from the array of expected ones. + isPDGFound = true; + break; + } + } + if (!isPDGFound) { + //Printf("MC Gen: Rejected: bad daughter PDG: %d", PDGCandidateDaughterI); + return false; + } + } + } + //Printf("MC Gen: Accepted: m: %d", candidate.globalIndex()); + if (sign) { + *sign = sgn; + } + return true; + } + + private: + static std::vector> mListMass; ///< list of particle masses in form (PDG code, mass) +}; + +std::vector> RecoDecay::mListMass; + +#endif // O2_ANALYSIS_RECODECAY_H_ diff --git a/Analysis/Core/include/AnalysisCore/TrackSelection.h b/Analysis/Core/include/AnalysisCore/TrackSelection.h new file mode 100644 index 0000000000000..f4e6edf67c985 --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/TrackSelection.h @@ -0,0 +1,215 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 track selection +// + +#ifndef TrackSelection_H +#define TrackSelection_H + +#include "Framework/Logger.h" +#include "Framework/DataTypes.h" +#include +#include +#include "Rtypes.h" + +class TrackSelection +{ + public: + TrackSelection() = default; + + enum class TrackCuts : int { + kTrackType = 0, + kPtRange, + kEtaRange, + kTPCNCls, + kTPCCrossedRows, + kTPCCrossedRowsOverNCls, + kTPCChi2NDF, + kTPCRefit, + kITSNCls, + kITSChi2NDF, + kITSRefit, + kITSHits, + kGoldenChi2, + kDCAxy, + kDCAz, + kNCuts + }; + + static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + + // Temporary function to check if track passes selection criteria. To be replaced by framework filters. + template + bool IsSelected(T const& track) + { + if (track.trackType() == mTrackType && + track.pt() >= mMinPt && track.pt() <= mMaxPt && + track.eta() >= mMinEta && track.eta() <= mMaxEta && + track.tpcNClsFound() >= mMinNClustersTPC && + track.tpcNClsCrossedRows() >= mMinNCrossedRowsTPC && + track.tpcCrossedRowsOverFindableCls() >= mMinNCrossedRowsOverFindableClustersTPC && + (track.itsNCls() >= mMinNClustersITS) && + (track.itsChi2NCl() <= mMaxChi2PerClusterITS) && + (track.tpcChi2NCl() <= mMaxChi2PerClusterTPC) && + ((mRequireITSRefit) ? (track.flags() & o2::aod::track::ITSrefit) : true) && + ((mRequireTPCRefit) ? (track.flags() & o2::aod::track::TPCrefit) : true) && + ((mRequireGoldenChi2) ? (track.flags() & o2::aod::track::GoldenChi2) : true) && + FulfillsITSHitRequirements(track.itsClusterMap()) && + abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY) && + abs(track.dcaZ()) <= mMaxDcaZ) { + return true; + } else { + return false; + } + } + + // Temporary function to check if track passes a given selection criteria. To be replaced by framework filters. + template + bool IsSelected(T const& track, const TrackCuts& cut) + { + switch (cut) { + case TrackCuts::kTrackType: + return track.trackType() == mTrackType; + case TrackCuts::kPtRange: + return track.pt() >= mMinPt && track.pt() <= mMaxPt; + case TrackCuts::kEtaRange: + return track.eta() >= mMinEta && track.eta() <= mMaxEta; + case TrackCuts::kTPCNCls: + return track.tpcNClsFound() >= mMinNClustersTPC; + case TrackCuts::kTPCCrossedRows: + return track.tpcNClsCrossedRows() >= mMinNCrossedRowsTPC; + case TrackCuts::kTPCCrossedRowsOverNCls: + return track.tpcCrossedRowsOverFindableCls() >= mMinNCrossedRowsOverFindableClustersTPC; + case TrackCuts::kTPCChi2NDF: + return track.itsNCls() >= mMinNClustersITS; + case TrackCuts::kTPCRefit: + return track.itsChi2NCl() <= mMaxChi2PerClusterITS; + case TrackCuts::kITSNCls: + return track.tpcChi2NCl() <= mMaxChi2PerClusterTPC; + case TrackCuts::kITSChi2NDF: + return (mRequireITSRefit) ? (track.flags() & o2::aod::track::ITSrefit) : true; + case TrackCuts::kITSRefit: + return (mRequireTPCRefit) ? (track.flags() & o2::aod::track::TPCrefit) : true; + case TrackCuts::kITSHits: + return (mRequireGoldenChi2) ? (track.flags() & o2::aod::track::GoldenChi2) : true; + case TrackCuts::kGoldenChi2: + return FulfillsITSHitRequirements(track.itsClusterMap()); + case TrackCuts::kDCAxy: + return abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY); + case TrackCuts::kDCAz: + return abs(track.dcaZ()) <= mMaxDcaZ; + default: + return false; + } + } + + void SetTrackType(o2::aod::track::TrackTypeEnum trackType) { mTrackType = trackType; } + void SetPtRange(float minPt = 0.f, float maxPt = 1e10f) + { + mMinPt = minPt; + mMaxPt = maxPt; + } + void SetEtaRange(float minEta = -1e10f, float maxEta = 1e10f) + { + mMinEta = minEta; + mMaxEta = maxEta; + } + void SetRequireITSRefit(bool requireITSRefit = true) + { + mRequireITSRefit = requireITSRefit; + } + void SetRequireTPCRefit(bool requireTPCRefit = true) + { + mRequireTPCRefit = requireTPCRefit; + } + void SetRequireGoldenChi2(bool requireGoldenChi2 = true) + { + mRequireGoldenChi2 = requireGoldenChi2; + } + void SetMinNClustersTPC(int minNClustersTPC) + { + mMinNClustersTPC = minNClustersTPC; + } + void SetMinNCrossedRowsTPC(int minNCrossedRowsTPC) + { + mMinNCrossedRowsTPC = minNCrossedRowsTPC; + } + void SetMinNCrossedRowsOverFindableClustersTPC(float minNCrossedRowsOverFindableClustersTPC) + { + mMinNCrossedRowsOverFindableClustersTPC = minNCrossedRowsOverFindableClustersTPC; + } + void SetMinNClustersITS(int minNClustersITS) + { + mMinNClustersITS = minNClustersITS; + } + void SetMaxChi2PerClusterTPC(float maxChi2PerClusterTPC) + { + mMaxChi2PerClusterTPC = maxChi2PerClusterTPC; + } + void SetMaxChi2PerClusterITS(float maxChi2PerClusterITS) + { + mMaxChi2PerClusterITS = maxChi2PerClusterITS; + } + void SetMaxDcaXY(float maxDcaXY) { mMaxDcaXY = maxDcaXY; } + void SetMaxDcaZ(float maxDcaZ) { mMaxDcaZ = maxDcaZ; } + + void SetMaxDcaXYPtDep(std::function ptDepCut) + { + mMaxDcaXYPtDep = ptDepCut; + } + void SetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + { + // layer 0 corresponds to the the innermost ITS layer + if (minNRequiredHits > requiredLayers.size()) { + LOGF(FATAL, "More ITS hits required than layers specified."); + } else { + mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); + } + } + void SetRequireNoHitsInITSLayers(std::set excludedLayers) + { + mRequiredITSHits.push_back(std::make_pair(-1, excludedLayers)); + } + void ResetITSRequirements() { mRequiredITSHits.clear(); } + + private: + bool FulfillsITSHitRequirements(uint8_t itsClusterMap); + + o2::aod::track::TrackTypeEnum mTrackType{o2::aod::track::TrackTypeEnum::GlobalTrack}; + + // kinematic cuts + float mMinPt{0.f}, mMaxPt{1e10f}; // range in pT + float mMinEta{-1e10f}, mMaxEta{1e10f}; // range in eta + + // track quality cuts + int mMinNClustersTPC{0}; // min number of TPC clusters + int mMinNCrossedRowsTPC{0}; // min number of crossed rows in TPC + int mMinNClustersITS{0}; // min number of ITS clusters + float mMaxChi2PerClusterTPC{1e10f}; // max tpc fit chi2 per TPC cluster + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + float mMinNCrossedRowsOverFindableClustersTPC{0.f}; // min ratio crossed rows / findable clusters + + float mMaxDcaXY{1e10f}; // max dca in xy plane + float mMaxDcaZ{1e10f}; // max dca in z direction + std::function mMaxDcaXYPtDep{}; // max dca in xy plane as function of pT + + bool mRequireITSRefit{false}; // require refit in ITS + bool mRequireTPCRefit{false}; // require refit in TPC + bool mRequireGoldenChi2{false}; // require golden chi2 cut (Run 2 only) + + // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + std::vector>> mRequiredITSHits{}; + + ClassDef(TrackSelection, 1); +}; + +#endif diff --git a/Analysis/Core/include/AnalysisCore/TrackSelectionDefaults.h b/Analysis/Core/include/AnalysisCore/TrackSelectionDefaults.h new file mode 100644 index 0000000000000..bddbf5c560141 --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/TrackSelectionDefaults.h @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackSelectionDefaults.h +/// \brief Class for the definition of standard track selection objects +/// \since 20-10-2020 +/// + +#ifndef TrackSelectionDefaults_H +#define TrackSelectionDefaults_H + +#include "Framework/DataTypes.h" + +// Default track selection requiring one hit in the SPD +TrackSelection getGlobalTrackSelection() +{ + TrackSelection selectedTracks; + selectedTracks.SetTrackType(o2::aod::track::Run2GlobalTrack); + selectedTracks.SetPtRange(0.1f, 1e10f); + selectedTracks.SetEtaRange(-0.8f, 0.8f); + selectedTracks.SetRequireITSRefit(true); + selectedTracks.SetRequireTPCRefit(true); + selectedTracks.SetRequireGoldenChi2(true); + selectedTracks.SetMinNCrossedRowsTPC(70); + selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(0.8f); + selectedTracks.SetMaxChi2PerClusterTPC(4.f); + selectedTracks.SetRequireHitsInITSLayers(1, {0, 1}); // one hit in any SPD layer + selectedTracks.SetMaxChi2PerClusterITS(36.f); + selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / pow(pt, 1.1f); }); + selectedTracks.SetMaxDcaZ(2.f); + return selectedTracks; +} + +// Default track selection requiring no hit in the SPD and one in the innermost +// SDD -> complementary tracks to global selection +TrackSelection getGlobalTrackSelectionSDD() +{ + TrackSelection selectedTracks = getGlobalTrackSelection(); + selectedTracks.ResetITSRequirements(); + selectedTracks.SetRequireNoHitsInITSLayers({0, 1}); // no hit in SPD layers + selectedTracks.SetRequireHitsInITSLayers(1, {2}); // one hit in first SDD layer + return selectedTracks; +} + +#endif diff --git a/Analysis/Core/include/AnalysisCore/TriggerAliases.h b/Analysis/Core/include/AnalysisCore/TriggerAliases.h new file mode 100644 index 0000000000000..9a7e9e9cbd35f --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/TriggerAliases.h @@ -0,0 +1,49 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TriggerAliases_H +#define TriggerAliases_H + +#include +#include +#include +#include + +enum triggerAliases { + kINT7 = 0, + kEMC7, + kINT7inMUON, + kMuonSingleLowPt7, + kMuonUnlikeLowPt7, + kMuonLikeLowPt7, + kCUP8, + kCUP9, + kMUP10, + kMUP11, + kNaliases +}; + +class TriggerAliases +{ + public: + TriggerAliases() = default; + ~TriggerAliases() = default; + void AddAlias(int aliasId, std::string classNames) { mAliases[aliasId] = classNames; } + void AddClassIdToAlias(int aliasId, int classId) { mAliasToClassIds[aliasId].push_back(classId); } + const std::map>& GetAliasToClassIdsMap() const { return mAliasToClassIds; } + void Print(); + + private: + std::map mAliases; + std::map> mAliasToClassIds; + ClassDefNV(TriggerAliases, 1) +}; + +#endif diff --git a/Analysis/Core/include/AnalysisCore/trackUtilities.h b/Analysis/Core/include/AnalysisCore/trackUtilities.h new file mode 100644 index 0000000000000..0017cb1d5cce9 --- /dev/null +++ b/Analysis/Core/include/AnalysisCore/trackUtilities.h @@ -0,0 +1,92 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 trackUtilities.h +/// \brief Utilities for manipulating parameters of tracks and vertices +/// +/// \author Vít Kučera , CERN + +#ifndef O2_ANALYSIS_TRACKUTILITIES_H_ +#define O2_ANALYSIS_TRACKUTILITIES_H_ + +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "AnalysisCore/RecoDecay.h" + +/// Extracts track parameters from a track. +template +o2::track::TrackPar getTrackPar(const T& track) +{ + std::array arraypar = {track.y(), track.z(), track.snp(), + track.tgl(), track.signed1Pt()}; + return o2::track::TrackPar(track.x(), track.alpha(), std::move(arraypar)); +} + +/// Extracts track parameters and covariance matrix from a track. +template +o2::track::TrackParCov getTrackParCov(const T& track) +{ + std::array arraypar = {track.y(), track.z(), track.snp(), + track.tgl(), track.signed1Pt()}; + std::array covpar = {track.cYY(), track.cZY(), track.cZZ(), + track.cSnpY(), track.cSnpZ(), + track.cSnpSnp(), track.cTglY(), track.cTglZ(), + track.cTglSnp(), track.cTglTgl(), + track.c1PtY(), track.c1PtZ(), track.c1PtSnp(), + track.c1PtTgl(), track.c1Pt21Pt2()}; + return o2::track::TrackParCov(track.x(), track.alpha(), std::move(arraypar), std::move(covpar)); +} + +/// Extracts primary vertex position and covariance matrix from a collision. +template +o2::dataformats::VertexBase getPrimaryVertex(const T& collision) +{ + o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); + std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; + return o2::dataformats::VertexBase{std::move(vtxXYZ), std::move(vtxCov)}; +} + +/// Calculates direction of one point w.r.t another point. +/// \param point1,point2 points with {x, y, z} coordinates accessible by index +/// \param phi azimuth angle in the {x, y} plane (taken w.r.t. the x-axis towards the y-axis) +/// \param theta angle w.r.t the {x, y} plane towards the z-axis +/// \return phi,theta of point2 w.r.t. point1 +template +void getPointDirection(const T& point1, const U& point2, V& phi, W& theta) +{ + phi = std::atan2(point2[1] - point1[1], point2[0] - point1[0]); + //auto x1 = point1[0] * std::cos(phi) + point1[1] * std::sin(phi); + //auto x2 = point2[0] * std::cos(phi) + point2[1] * std::sin(phi); + //theta = std::atan2(point2[2] - point1[2], x2 - x1); + theta = std::atan2(point2[2] - point1[2], RecoDecay::distanceXY(point1, point2)); +} + +/// Calculates the XX element of a XYZ covariance matrix after rotation of the coordinate system +/// by phi around the z-axis and by minus theta around the new y-axis. +/// \param matrix matrix +/// \param phi azimuth angle in the {x, y} plane (taken w.r.t. the x-axis towards the y-axis) +/// \param theta angle w.r.t the {x, y} plane towards the z-axis +/// \return XX element of the rotated covariance matrix +template +auto getRotatedCovMatrixXX(const T& matrix, U phi, V theta) +{ + auto cp = std::cos(phi); + auto sp = std::sin(phi); + auto ct = std::cos(theta); + auto st = std::sin(theta); + return matrix[0] * cp * cp * ct * ct // covXX + + matrix[1] * 2. * cp * sp * ct * ct // covXY + + matrix[2] * sp * sp * ct * ct // covYY + + matrix[3] * 2. * cp * ct * st // covXZ + + matrix[4] * 2. * sp * ct * st // covYZ + + matrix[5] * st * st; // covZZ +} + +#endif // O2_ANALYSIS_TRACKUTILITIES_H_ diff --git a/Analysis/Core/src/AnalysisCoreLinkDef.h b/Analysis/Core/src/AnalysisCoreLinkDef.h index 01b17ab1e703d..18e2437504797 100644 --- a/Analysis/Core/src/AnalysisCoreLinkDef.h +++ b/Analysis/Core/src/AnalysisCoreLinkDef.h @@ -12,15 +12,8 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class StepTHnBase+; -#pragma link C++ class StepTHn+; -#pragma link C++ class StepTHn+; -#pragma link C++ typedef StepTHnF; -#pragma link C++ typedef StepTHnD; +#pragma link C++ class CorrelationContainer + ; +#pragma link C++ class TrackSelection + ; +#pragma link C++ class TriggerAliases + ; -#pragma link C++ class RunToTimestamp+; -#pragma link C++ class CorrelationContainer+; -#pragma link C++ class TrackSelection+; - -#pragma link C++ class VarManager + ; -#pragma link C++ class HistogramManager + ; +#pragma link C++ class HFTrackIndexSkimsCreatorConfigs + ; diff --git a/Analysis/Core/src/AnalysisJetsLinkDef.h b/Analysis/Core/src/AnalysisJetsLinkDef.h new file mode 100644 index 0000000000000..8de8bce16da64 --- /dev/null +++ b/Analysis/Core/src/AnalysisJetsLinkDef.h @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class JetFinder + ; diff --git a/Analysis/Core/src/CorrelationContainer.cxx b/Analysis/Core/src/CorrelationContainer.cxx index dcf1413d2294c..36fe789cba3e6 100644 --- a/Analysis/Core/src/CorrelationContainer.cxx +++ b/Analysis/Core/src/CorrelationContainer.cxx @@ -13,8 +13,9 @@ // // Author: Jan Fiete Grosse-Oetringhaus -#include "Analysis/CorrelationContainer.h" -#include "Analysis/StepTHn.h" +#include "AnalysisCore/CorrelationContainer.h" +#include "Framework/StepTHn.h" +#include "Framework/Logger.h" #include "THnSparse.h" #include "TMath.h" #include "TList.h" @@ -26,17 +27,15 @@ #include "TF1.h" #include "THn.h" -// for LOGF -#include "Framework/AnalysisTask.h" - ClassImp(CorrelationContainer) const Int_t CorrelationContainer::fgkCFSteps = 11; CorrelationContainer::CorrelationContainer() : TNamed(), - mTrackHist(nullptr), - mEventHist(nullptr), + mPairHist(nullptr), + mTriggerHist(nullptr), mTrackHistEfficiency(nullptr), + mEventCount(nullptr), mEtaMin(0), mEtaMax(0), mPtMin(0), @@ -60,9 +59,10 @@ CorrelationContainer::CorrelationContainer() : TNamed(), } CorrelationContainer::CorrelationContainer(const char* name, const char* objTitle, const char* reqHist, const char* binning) : TNamed(name, objTitle), - mTrackHist(nullptr), - mEventHist(nullptr), + mPairHist(nullptr), + mTriggerHist(nullptr), mTrackHistEfficiency(nullptr), + mEventCount(nullptr), mEtaMin(0), mEtaMax(0), mPtMin(0), @@ -84,8 +84,11 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl { // Constructor - if (strlen(reqHist) == 0) + using BinList = std::vector; + + if (strlen(reqHist) == 0) { return; + } LOGF(info, "Creating CorrelationContainer with %s (binning: %s)", reqHist, binning); @@ -94,12 +97,12 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl // track level Int_t nTrackVars = 4; // eta vs pT vs pT,lead (vs delta phi) vs multiplicity Int_t iTrackBin[7]; - Double_t* trackBins[7]; + BinList trackBins[7]; const char* trackAxisTitle[7]; // eta Int_t nEtaBins = -1; - Double_t* etaBins = getBinning(binning, "eta", nEtaBins); + BinList etaBins = getBinning(binning, "eta", nEtaBins); const char* etaTitle = "#eta"; iTrackBin[0] = nEtaBins; @@ -108,7 +111,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl // delta eta Int_t nDeltaEtaBins = -1; - Double_t* deltaEtaBins = getBinning(binning, "delta_eta", nDeltaEtaBins); + BinList deltaEtaBins = getBinning(binning, "delta_eta", nDeltaEtaBins); // pT trackBins[1] = getBinning(binning, "p_t_assoc", iTrackBin[1]); @@ -116,41 +119,43 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl // pT, fine Int_t npTBinsFine = -1; - Double_t* pTBinsFine = getBinning(binning, "p_t_eff", npTBinsFine); + BinList pTBinsFine = getBinning(binning, "p_t_eff", npTBinsFine); // pT,lead binning 1 Int_t nLeadingpTBins = -1; - Double_t* leadingpTBins = getBinning(binning, "p_t_leading", nLeadingpTBins); + BinList leadingpTBins = getBinning(binning, "p_t_leading", nLeadingpTBins); // pT,lead binning 2 Int_t nLeadingpTBins2 = -1; - Double_t* leadingpTBins2 = getBinning(binning, "p_t_leading_course", nLeadingpTBins2); + BinList leadingpTBins2 = getBinning(binning, "p_t_leading_course", nLeadingpTBins2); // phi,lead Int_t nLeadingPhiBins = -1; - Double_t* leadingPhiBins = getBinning(binning, "delta_phi", nLeadingPhiBins); + BinList leadingPhiBins = getBinning(binning, "delta_phi", nLeadingPhiBins); trackBins[3] = getBinning(binning, "multiplicity", iTrackBin[3]); trackAxisTitle[3] = "multiplicity"; // particle species const Int_t kNSpeciesBins = 4; // pi, K, p, rest - Double_t speciesBins[] = {-0.5, 0.5, 1.5, 2.5, 3.5}; + BinList speciesBins = {-0.5, 0.5, 1.5, 2.5, 3.5}; // vtx-z axis const char* vertexTitle = "z-vtx (cm)"; Int_t nVertexBins = -1; - Double_t* vertexBins = getBinning(binning, "vertex", nVertexBins); + BinList vertexBins = getBinning(binning, "vertex", nVertexBins); Int_t nVertexBinsEff = -1; - Double_t* vertexBinsEff = getBinning(binning, "vertex_eff", nVertexBinsEff); + BinList vertexBinsEff = getBinning(binning, "vertex_eff", nVertexBinsEff); Int_t useVtxAxis = 0; Int_t useAliTHn = 1; // 0 = don't use | 1 = with float | 2 = with double - if (TString(reqHist).Contains("Sparse")) + if (TString(reqHist).Contains("Sparse")) { useAliTHn = 0; - if (TString(reqHist).Contains("Double")) + } + if (TString(reqHist).Contains("Double")) { useAliTHn = 2; + } // selection depending on requested histogram Int_t axis = -1; // 0 = pT,lead, 1 = phi,lead @@ -161,8 +166,9 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl axis = 1; title = "d^{2}N_{ch}/d#varphid#eta"; } else if (TString(reqHist).BeginsWith("NumberDensityPhiCentrality")) { - if (TString(reqHist).Contains("Vtx")) + if (TString(reqHist).Contains("Vtx")) { useVtxAxis = 1; + } reqHist = "NumberDensityPhiCentrality"; mHistogramType = reqHist; @@ -178,8 +184,9 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl mHistogramType = reqHist; axis = 3; title = "d^{2}N_{ch}/d#varphid#eta"; - } else + } else { LOGF(fatal, "Invalid histogram requested: %s", reqHist); + } UInt_t nSteps = fgkCFSteps; @@ -248,15 +255,16 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl trackAxisTitle[6] = "Trigger 2 p_{T} (GeV/c)"; } - if (axis >= 2 && useAliTHn == 1) - mTrackHist = new StepTHnF("mTrackHist", title, nSteps, nTrackVars, iTrackBin, trackBins, trackAxisTitle); - else if (axis >= 2 && useAliTHn == 2) - mTrackHist = new StepTHnD("mTrackHist", title, nSteps, nTrackVars, iTrackBin, trackBins, trackAxisTitle); + if (axis >= 2 && useAliTHn == 1) { + mPairHist = new StepTHnF("mPairHist", title, nSteps, nTrackVars, iTrackBin, trackBins, trackAxisTitle); + } else if (axis >= 2 && useAliTHn == 2) { + mPairHist = new StepTHnD("mPairHist", title, nSteps, nTrackVars, iTrackBin, trackBins, trackAxisTitle); + } // event level Int_t nEventVars = 2; Int_t iEventBin[4] = {0}; - Double_t* eventBins[4] = {nullptr}; + BinList eventBins[4]; const char* eventAxisTitle[4] = {nullptr}; // track 3rd and 4th axis --> event 1st and 2nd axis @@ -281,7 +289,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl eventBins[3] = trackBins[6]; eventAxisTitle[3] = trackAxisTitle[6]; } - mEventHist = new StepTHnF("mEventHist", title, nSteps, nEventVars, iEventBin, eventBins, eventAxisTitle); + mTriggerHist = new StepTHnF("mTriggerHist", title, nSteps, nEventVars, iEventBin, eventBins, eventAxisTitle); // Efficiency histogram @@ -303,13 +311,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl mTrackHistEfficiency = new StepTHnD("mTrackHistEfficiency", "Tracking efficiency", 6, 5, iTrackBin, trackBins, trackAxisTitle); - delete[] deltaEtaBins; - delete[] pTBinsFine; - delete[] leadingpTBins; - delete[] leadingpTBins2; - delete[] leadingPhiBins; - delete[] vertexBins; - delete[] vertexBinsEff; + mEventCount = new TH2F("mEventCount", ";step;centrality;count", fgkCFSteps + 2, -2.5, -0.5 + fgkCFSteps, iEventBin[1], &(eventBins[1])[0]); } TString CorrelationContainer::combineBinning(TString defaultBinning, TString customBinning) @@ -323,10 +325,11 @@ TString CorrelationContainer::combineBinning(TString defaultBinning, TString cus for (Int_t i = 0; i < lines->GetEntriesFast(); i++) { TString line(lines->At(i)->GetName()); TString tag = line(0, line.Index(":") + 1); - if (!customBinning.BeginsWith(tag) && !customBinning.Contains(TString("\n") + tag)) + if (!customBinning.BeginsWith(tag) && !customBinning.Contains(TString("\n") + tag)) { binningStr += line + "\n"; - else + } else { LOGF(info, "Using custom binning for %s", tag.Data()); + } } delete lines; binningStr += customBinning; @@ -334,15 +337,18 @@ TString CorrelationContainer::combineBinning(TString defaultBinning, TString cus return binningStr; } -Double_t* CorrelationContainer::getBinning(const char* configuration, const char* tag, Int_t& nBins) +std::vector CorrelationContainer::getBinning(const char* configuration, const char* tag, Int_t& nBins) { // takes the binning from identified by // configuration syntax example: - // eta: 2.4, -2.3, -2.2, -2.1, -2.0, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4 - // phi: ..... + // equidistant binning: + // eta: 48 | -2.4, 2.4 + // variable-width binning: + // eta: -2.4, -2.3, -2.2, -2.1, -2.0, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4 // // returns bin edges which have to be deleted by the caller + std::vector bins; TString config(configuration); TObjArray* lines = config.Tokenize("\n"); for (Int_t i = 0; i < lines->GetEntriesFast(); i++) { @@ -350,13 +356,19 @@ Double_t* CorrelationContainer::getBinning(const char* configuration, const char if (line.BeginsWith(TString(tag) + ":")) { line.Remove(0, strlen(tag) + 1); line.ReplaceAll(" ", ""); + if (line.Contains("|")) { + // equidistant binning + nBins = TString(line(0, line.Index("|"))).Atoi(); + line.Remove(0, line.Index("|") + 1); + } else { + // variable-width binning + nBins = line.CountChar(','); + } TObjArray* binning = line.Tokenize(","); - Double_t* bins = new Double_t[binning->GetEntriesFast()]; - for (Int_t j = 0; j < binning->GetEntriesFast(); j++) - bins[j] = TString(binning->At(j)->GetName()).Atof(); - - nBins = binning->GetEntriesFast() - 1; - + //Double_t* bins = new Double_t[binning->GetEntriesFast()]; + for (Int_t j = 0; j < binning->GetEntriesFast(); j++) { + bins.push_back(TString(binning->At(j)->GetName()).Atof()); + } delete binning; delete lines; return bins; @@ -365,13 +377,13 @@ Double_t* CorrelationContainer::getBinning(const char* configuration, const char delete lines; LOGF(fatal, "Tag %s not found in %s", tag, configuration); - return nullptr; + return bins; } //_____________________________________________________________________________ CorrelationContainer::CorrelationContainer(const CorrelationContainer& c) : TNamed(c), - mTrackHist(nullptr), - mEventHist(nullptr), + mPairHist(nullptr), + mTriggerHist(nullptr), mTrackHistEfficiency(nullptr), mEtaMin(0), mEtaMax(0), @@ -404,14 +416,14 @@ CorrelationContainer::~CorrelationContainer() { // Destructor - if (mTrackHist) { - delete mTrackHist; - mTrackHist = nullptr; + if (mPairHist) { + delete mPairHist; + mPairHist = nullptr; } - if (mEventHist) { - delete mEventHist; - mEventHist = nullptr; + if (mTriggerHist) { + delete mTriggerHist; + mTriggerHist = nullptr; } if (mTrackHistEfficiency) { @@ -430,8 +442,9 @@ CorrelationContainer& CorrelationContainer::operator=(const CorrelationContainer { // assigment operator - if (this != &c) + if (this != &c) { ((CorrelationContainer&)c).Copy(*this); + } return *this; } @@ -443,14 +456,17 @@ void CorrelationContainer::Copy(TObject& c) const CorrelationContainer& target = (CorrelationContainer&)c; - if (mTrackHist) - target.mTrackHist = dynamic_cast(mTrackHist->Clone()); + if (mPairHist) { + target.mPairHist = dynamic_cast(mPairHist->Clone()); + } - if (mEventHist) - target.mEventHist = dynamic_cast(mEventHist->Clone()); + if (mTriggerHist) { + target.mTriggerHist = dynamic_cast(mTriggerHist->Clone()); + } - if (mTrackHistEfficiency) - target.mTrackHistEfficiency = dynamic_cast(mTrackHistEfficiency->Clone()); + if (mTrackHistEfficiency) { + target.mTrackHistEfficiency = dynamic_cast(mTrackHistEfficiency->Clone()); + } target.mEtaMin = mEtaMin; target.mEtaMax = mEtaMax; @@ -476,45 +492,54 @@ Long64_t CorrelationContainer::Merge(TCollection* list) // PROOF). // Returns the number of merged objects (including this). - if (!list) + if (!list) { return 0; + } - if (list->IsEmpty()) + if (list->IsEmpty()) { return 1; + } TIterator* iter = list->MakeIterator(); TObject* obj = nullptr; // collections of objects - const UInt_t kMaxLists = 3; + const UInt_t kMaxLists = 4; TList** lists = new TList*[kMaxLists]; - for (UInt_t i = 0; i < kMaxLists; i++) + for (UInt_t i = 0; i < kMaxLists; i++) { lists[i] = new TList; + } Int_t count = 0; while ((obj = iter->Next())) { CorrelationContainer* entry = dynamic_cast(obj); - if (entry == nullptr) + if (entry == nullptr) { continue; + } - if (entry->mTrackHist) - lists[0]->Add(entry->mTrackHist); + if (entry->mPairHist) { + lists[0]->Add(entry->mPairHist); + } - lists[1]->Add(entry->mEventHist); + lists[1]->Add(entry->mTriggerHist); lists[2]->Add(entry->mTrackHistEfficiency); + lists[3]->Add(entry->mEventCount); count++; } - if (mTrackHist) - mTrackHist->Merge(lists[0]); + if (mPairHist) { + mPairHist->Merge(lists[0]); + } - mEventHist->Merge(lists[1]); + mTriggerHist->Merge(lists[1]); mTrackHistEfficiency->Merge(lists[2]); + mEventCount->Merge(lists[3]); - for (UInt_t i = 0; i < kMaxLists; i++) + for (UInt_t i = 0; i < kMaxLists; i++) { delete lists[i]; + } delete[] lists; @@ -526,14 +551,17 @@ void CorrelationContainer::setBinLimits(THnBase* grid) { // sets the bin limits in eta and pT defined by mEtaMin/Max, mPtMin/Max - if (mEtaMax > mEtaMin) + if (mEtaMax > mEtaMin) { grid->GetAxis(0)->SetRangeUser(mEtaMin, mEtaMax); - if (mPtMax > mPtMin) + } + if (mPtMax > mPtMin) { grid->GetAxis(1)->SetRangeUser(mPtMin, mPtMax); - if (mPt2Min > 0 && mPt2Max > 0) + } + if (mPt2Min > 0 && mPt2Max > 0) { grid->GetAxis(6)->SetRangeUser(mPt2Min, mPt2Max); - else if (mPt2Min > 0) + } else if (mPt2Min > 0) { grid->GetAxis(6)->SetRangeUser(mPt2Min, grid->GetAxis(6)->GetXmax() - 0.01); + } } //____________________________________________________________________ @@ -541,13 +569,15 @@ void CorrelationContainer::resetBinLimits(THnBase* grid) { // resets all bin limits - for (Int_t i = 0; i < grid->GetNdimensions(); i++) - if (grid->GetAxis(i)->TestBit(TAxis::kAxisRange)) + for (Int_t i = 0; i < grid->GetNdimensions(); i++) { + if (grid->GetAxis(i)->TestBit(TAxis::kAxisRange)) { grid->GetAxis(i)->SetRangeUser(0, -1); + } + } } //____________________________________________________________________ -void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax) +void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) { // prints the number of empty bins in the track end event histograms in the given step @@ -556,35 +586,36 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo for (Int_t i = 0; i < 4; i++) { binBegin[i] = 1; - binEnd[i] = mTrackHist->getTHn(step)->GetAxis(i)->GetNbins(); + binEnd[i] = mPairHist->getTHn(step)->GetAxis(i)->GetNbins(); } if (mEtaMax > mEtaMin) { - binBegin[0] = mTrackHist->getTHn(step)->GetAxis(0)->FindBin(mEtaMin); - binEnd[0] = mTrackHist->getTHn(step)->GetAxis(0)->FindBin(mEtaMax); + binBegin[0] = mPairHist->getTHn(step)->GetAxis(0)->FindBin(mEtaMin); + binEnd[0] = mPairHist->getTHn(step)->GetAxis(0)->FindBin(mEtaMax); } if (mPtMax > mPtMin) { - binBegin[1] = mTrackHist->getTHn(step)->GetAxis(1)->FindBin(mPtMin); - binEnd[1] = mTrackHist->getTHn(step)->GetAxis(1)->FindBin(mPtMax); + binBegin[1] = mPairHist->getTHn(step)->GetAxis(1)->FindBin(mPtMin); + binEnd[1] = mPairHist->getTHn(step)->GetAxis(1)->FindBin(mPtMax); } - if (ptLeadMax > ptLeadMin) { - binBegin[2] = mTrackHist->getTHn(step)->GetAxis(2)->FindBin(ptLeadMin); - binEnd[2] = mTrackHist->getTHn(step)->GetAxis(2)->FindBin(ptLeadMax); + if (ptTriggerMax > ptTriggerMin) { + binBegin[2] = mPairHist->getTHn(step)->GetAxis(2)->FindBin(ptTriggerMin); + binEnd[2] = mPairHist->getTHn(step)->GetAxis(2)->FindBin(ptTriggerMax); } // start from multiplicity 1 - binBegin[3] = mTrackHist->getTHn(step)->GetAxis(3)->FindBin(1); + binBegin[3] = mPairHist->getTHn(step)->GetAxis(3)->FindBin(1); Int_t total = 0; Int_t count = 0; Int_t vars[4]; - for (Int_t i = 0; i < 4; i++) + for (Int_t i = 0; i < 4; i++) { vars[i] = binBegin[i]; + } - THnBase* grid = mTrackHist->getTHn(step); + THnBase* grid = mPairHist->getTHn(step); while (1) { if (grid->GetBin(vars) == 0) { LOGF(warning, "Empty bin at eta=%.2f pt=%.2f pt_lead=%.2f mult=%.1f", @@ -603,8 +634,9 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } } - if (vars[0] == binEnd[0] + 1) + if (vars[0] == binEnd[0] + 1) { break; + } total++; } @@ -612,13 +644,13 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } //____________________________________________________________________ -void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax, THnBase** trackHist, TH2** eventHist) +void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, THnBase** trackHist, TH2** eventHist) { // Calculates a 4d histogram with deltaphi, deltaeta, zvtx, multiplicity on track level and // a 2d histogram on event level (as fct of zvtx, multiplicity) // Histograms has to be deleted by the caller of the function - THnBase* sparse = mTrackHist->getTHn(step); + THnBase* sparse = mPairHist->getTHn(step); if (mGetMultCacheOn) { if (!mGetMultCache) { mGetMultCache = changeToThn(sparse); @@ -629,34 +661,36 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F // unzoom all axes resetBinLimits(sparse); - resetBinLimits(mEventHist->getTHn(step)); + resetBinLimits(mTriggerHist->getTHn(step)); setBinLimits(sparse); - Int_t firstBin = sparse->GetAxis(2)->FindBin(ptLeadMin); - Int_t lastBin = sparse->GetAxis(2)->FindBin(ptLeadMax); - LOGF(info, "Using pT range %d --> %d", firstBin, lastBin); + Int_t firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); + Int_t lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); + LOGF(info, "Using trigger pT range %d --> %d", firstBin, lastBin); sparse->GetAxis(2)->SetRange(firstBin, lastBin); - mEventHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); + mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); // cut on the second trigger particle if there is a minimum set if (mPt2Min > 0) { Int_t firstBinPt2 = sparse->GetAxis(6)->FindBin(mPt2Min); Int_t lastBinPt2 = sparse->GetAxis(6)->GetNbins(); - if (mPt2Max > 0) + if (mPt2Max > 0) { lastBinPt2 = sparse->GetAxis(6)->FindBin(mPt2Max); + } - mEventHist->getTHn(step)->GetAxis(3)->SetRange(firstBinPt2, lastBinPt2); + mTriggerHist->getTHn(step)->GetAxis(3)->SetRange(firstBinPt2, lastBinPt2); } Bool_t hasVertex = kTRUE; - if (!mTrackHist->getTHn(step)->GetAxis(5)) + if (!mPairHist->getTHn(step)->GetAxis(5)) { hasVertex = kFALSE; + } if (hasVertex) { Int_t dimensions[] = {4, 0, 5, 3}; THnBase* tmpTrackHist = sparse->ProjectionND(4, dimensions, "E"); - *eventHist = (TH2*)mEventHist->getTHn(step)->Projection(2, 1); + *eventHist = (TH2*)mTriggerHist->getTHn(step)->Projection(2, 1); // convert to THn *trackHist = changeToThn(tmpTrackHist); delete tmpTrackHist; @@ -672,8 +706,9 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F for (int i = 0; i < 3; i++) { int j = i; - if (i == 2) + if (i == 2) { j = 3; + } (*trackHist)->SetBinEdges(j, tmpTrackHist->GetAxis(i)->GetXbins()->GetArray()); (*trackHist)->GetAxis(j)->SetTitle(tmpTrackHist->GetAxis(i)->GetTitle()); @@ -698,7 +733,7 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F delete tmpTrackHist; - TH1* projEventHist = (TH1*)mEventHist->getTHn(step)->Projection(1); + TH1* projEventHist = (TH1*)mTriggerHist->getTHn(step)->Projection(1); *eventHist = new TH2F(Form("%s_vtx", projEventHist->GetName()), projEventHist->GetTitle(), 1, vtxAxis, projEventHist->GetNbinsX(), projEventHist->GetXaxis()->GetXbins()->GetArray()); for (Int_t binIdx = 1; binIdx <= projEventHist->GetNbinsX(); binIdx++) { (*eventHist)->SetBinContent(1, binIdx, projEventHist->GetBinContent(binIdx)); @@ -709,13 +744,74 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F } resetBinLimits(sparse); - resetBinLimits(mEventHist->getTHn(step)); + resetBinLimits(mTriggerHist->getTHn(step)); } -//____________________________________________________________________ -TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax, Int_t multBinBegin, Int_t multBinEnd, Bool_t normalizePerTrigger, Int_t stepForMixed, Int_t* trigger) +TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger) { - // Calls GetUEHist(...) for *each* vertex bin and multiplicity bin and performs a sum of ratios: + // Calculate per trigger yield without considering mixed event + // Sums over all vertex bins, and respects the pT and centrality limits set in the class + // + // returns a 2D histogram: deltaphi, deltaeta + // + // Parameters: + // step: Step for which the histogram is received + // ptTriggerMin, ptTriggerMax: trigger pT range + // normalizePerTrigger: divide through number of triggers + + THnBase* trackSameAll = nullptr; + TH2* eventSameAll = nullptr; + + Long64_t totalEvents = 0; + Int_t nCorrelationFunctions = 0; + + getHistsZVtxMult(step, ptTriggerMin, ptTriggerMax, &trackSameAll, &eventSameAll); + + TAxis* multAxis = trackSameAll->GetAxis(3); + int multBinBegin = 1; + int multBinEnd = multAxis->GetNbins(); + if (mCentralityMin < mCentralityMax) { + multBinBegin = multAxis->FindBin(mCentralityMin + 1e-4); + multBinEnd = multAxis->FindBin(mCentralityMax - 1e-4); + LOGF(info, "Using multiplicity range %d --> %d", multBinBegin, multBinEnd); + + trackSameAll->GetAxis(3)->SetRange(multBinBegin, multBinEnd); + } + + TAxis* vertexAxis = trackSameAll->GetAxis(2); + int vertexBinBegin = 1; + int vertexBinEnd = vertexAxis->GetNbins(); + if (mZVtxMax > mZVtxMin) { + vertexBinBegin = vertexAxis->FindBin(mZVtxMin); + vertexBinEnd = vertexAxis->FindBin(mZVtxMax); + LOGF(info, "Using vertex range %d --> %d", vertexBinBegin, vertexBinEnd); + + trackSameAll->GetAxis(2)->SetRange(vertexBinBegin, vertexBinEnd); + } + + TH2* yield = trackSameAll->Projection(1, 0, "E"); + Float_t triggers = eventSameAll->Integral(multBinBegin, multBinEnd, vertexBinBegin, vertexBinEnd); + + if (normalizePerTrigger) { + LOGF(info, "Dividing %f tracks by %f triggers", yield->Integral(), triggers); + if (triggers > 0) { + yield->Scale(1.0 / triggers); + } + } + + // normalizate to dphi width + Float_t normalization = yield->GetXaxis()->GetBinWidth(1); + yield->Scale(1.0 / normalization); + + delete trackSameAll; + delete eventSameAll; + + return yield; +} + +TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger, Int_t stepForMixed, Int_t* trigger) +{ + // Extract 2D per trigger yield with mixed event correction. The quantity is calculated for *each* vertex bin and multiplicity bin and then a sum of ratios is performed: // 1_N [ (same/mixed)_1 + (same/mixed)_2 + (same/mixed)_3 + ... ] // where N is the total number of events/trigger particles and the subscript is the vertex/multiplicity bin // where mixed is normalized such that the information about the number of pairs in same is kept @@ -724,8 +820,9 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // // Parameters: // mixed: CorrelationContainer containing mixed event corresponding to this object (the histograms are taken from step if defined otherwise from step ) - // : check documentation of CorrelationContainer::GetUEHist - // normalizePerTrigger: divide through number of triggers + // step: Step for which the histogram is received + // ptTriggerMin, ptTriggerMax: trigger pT range + // normalizePerTrigger: divide through number of triggers // do not add this hists to the directory Bool_t oldStatus = TH1::AddDirectoryStatus(); @@ -743,16 +840,16 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati Long64_t totalEvents = 0; Int_t nCorrelationFunctions = 0; - getHistsZVtxMult(step, ptLeadMin, ptLeadMax, &trackSameAll, &eventSameAll); - mixed->getHistsZVtxMult((stepForMixed == -1) ? step : (CFStep)stepForMixed, ptLeadMin, ptLeadMax, &trackMixedAll, &eventMixedAll); + getHistsZVtxMult(step, ptTriggerMin, ptTriggerMax, &trackSameAll, &eventSameAll); + mixed->getHistsZVtxMult((stepForMixed == -1) ? step : (CFStep)stepForMixed, ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); // If we ask for histograms from step8 (TTR cut applied) there is a hole at 0,0; so this cannot be used for the // mixed-event normalization. If step6 is available, the normalization factor is read out from that one. // If step6 is not available we fallback to taking the normalization along all delta phi (WARNING requires a // flat delta phi distribution) - if (stepForMixed == -1 && step == kCFStepBiasStudy && mixed->mEventHist->getTHn(kCFStepReconstructed)->GetEntries() > 0 && !mSkipScaleMixedEvent) { + if (stepForMixed == -1 && step == kCFStepBiasStudy && mixed->mTriggerHist->getTHn(kCFStepReconstructed)->GetEntries() > 0 && !mSkipScaleMixedEvent) { LOGF(info, "Using mixed-event normalization factors from step %d", kCFStepReconstructed); - mixed->getHistsZVtxMult(kCFStepReconstructed, ptLeadMin, ptLeadMax, &trackMixedAllStep6, &eventMixedAllStep6); + mixed->getHistsZVtxMult(kCFStepReconstructed, ptTriggerMin, ptTriggerMax, &trackMixedAllStep6, &eventMixedAllStep6); } // Printf("%f %f %f %f", trackSameAll->GetEntries(), eventSameAll->GetEntries(), trackMixedAll->GetEntries(), eventMixedAll->GetEntries()); @@ -763,16 +860,19 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati TAxis* multAxis = trackSameAll->GetAxis(3); - if (multBinEnd < multBinBegin) { - multBinBegin = 1; - multBinEnd = multAxis->GetNbins(); + int multBinBegin = 1; + int multBinEnd = multAxis->GetNbins(); + if (mCentralityMin < mCentralityMax) { + multBinBegin = multAxis->FindBin(mCentralityMin); + multBinEnd = multAxis->FindBin(mCentralityMax); } for (Int_t multBin = TMath::Max(1, multBinBegin); multBin <= TMath::Min(multAxis->GetNbins(), multBinEnd); multBin++) { trackSameAll->GetAxis(3)->SetRange(multBin, multBin); trackMixedAll->GetAxis(3)->SetRange(multBin, multBin); - if (trackMixedAllStep6) + if (trackMixedAllStep6) { trackMixedAllStep6->GetAxis(3)->SetRange(multBin, multBin); + } Double_t mixedNorm = 1; Double_t mixedNormError = 0; @@ -852,8 +952,9 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati mixedNormError /= triggers; delete tracksMixed; - } else + } else { LOGF(warning, "WARNING: Skipping mixed-event scaling! mSkipScaleMixedEvent IS set!"); + } if (mixedNorm <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty at (0,0)", multBin); @@ -894,10 +995,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati if (triggers2 <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d vertex bin %d because mixed event is empty", multBin, vertexBin); } else { - if (!mSkipScaleMixedEvent) + if (!mSkipScaleMixedEvent) { tracksMixed->Scale(1.0 / triggers2 / mixedNorm); - else if (tracksMixed->Integral() > 0) + } else if (tracksMixed->Integral() > 0) { tracksMixed->Scale(1.0 / tracksMixed->Integral()); + } // tracksSame->Scale(tracksMixed->Integral() / tracksSame->Integral()); // new TCanvas; tracksSame->DrawClone("SURF1"); @@ -907,25 +1009,29 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati Double_t sums[] = {0, 0, 0}; Double_t errors[] = {0, 0, 0}; - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) + for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { for (Int_t y = 1; y <= tracksSame->GetNbinsY(); y++) { sums[0] += tracksSame->GetBinContent(x, y); errors[0] += tracksSame->GetBinError(x, y); sums[1] += tracksMixed->GetBinContent(x, y); errors[1] += tracksMixed->GetBinError(x, y); } + } tracksSame->Divide(tracksMixed); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) + for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { for (Int_t y = 1; y <= tracksSame->GetNbinsY(); y++) { sums[2] += tracksSame->GetBinContent(x, y); errors[2] += tracksSame->GetBinError(x, y); } + } - for (Int_t x = 0; x < 3; x++) - if (sums[x] > 0) + for (Int_t x = 0; x < 3; x++) { + if (sums[x] > 0) { errors[x] /= sums[x]; + } + } LOGF(info, "The correlation function %d %d has uncertainties %f %f %f (Ratio S/M %f)", multBin, vertexBin, errors[0], errors[1], errors[2], (errors[1] > 0) ? errors[0] / errors[1] : -1); // code to draw contributions @@ -936,10 +1042,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati proj->DrawCopy((vertexBin > 1) ? "SAME" : ""); */ - if (!totalTracks) + if (!totalTracks) { totalTracks = (TH2*)tracksSame->Clone("totalTracks"); - else + } else { totalTracks->Add(tracksSame); + } totalEvents += eventSameAll->GetBinContent(vertexBin, multBin); @@ -957,21 +1064,25 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati Double_t sums[] = {0, 0, 0}; Double_t errors[] = {0, 0, 0}; - for (Int_t x = 1; x <= totalTracks->GetNbinsX(); x++) + for (Int_t x = 1; x <= totalTracks->GetNbinsX(); x++) { for (Int_t y = 1; y <= totalTracks->GetNbinsY(); y++) { sums[0] += totalTracks->GetBinContent(x, y); errors[0] += totalTracks->GetBinError(x, y); } - if (sums[0] > 0) + } + if (sums[0] > 0) { errors[0] /= sums[0]; + } if (normalizePerTrigger) { LOGF(info, "Dividing %f tracks by %lld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); - if (totalEvents > 0) + if (totalEvents > 0) { totalTracks->Scale(1.0 / totalEvents); + } } - if (trigger != nullptr) + if (trigger != nullptr) { *trigger = (Int_t)totalEvents; + } // normalizate to dphi width Float_t normalization = totalTracks->GetXaxis()->GetBinWidth(1); @@ -992,25 +1103,25 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati return totalTracks; } -TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptLeadMin, Float_t ptLeadMax) +TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) { // returns the distribution of triggers as function of centrality/multiplicity - resetBinLimits(mEventHist->getTHn(step)); + resetBinLimits(mTriggerHist->getTHn(step)); - Int_t firstBin = mEventHist->getTHn(step)->GetAxis(0)->FindBin(ptLeadMin); - Int_t lastBin = mEventHist->getTHn(step)->GetAxis(0)->FindBin(ptLeadMax); + Int_t firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); + Int_t lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); LOGF(info, "Using pT range %d --> %d", firstBin, lastBin); - mEventHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); + mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); if (mZVtxMax > mZVtxMin) { - mEventHist->getTHn(step)->GetAxis(2)->SetRangeUser(mZVtxMin, mZVtxMax); + mTriggerHist->getTHn(step)->GetAxis(2)->SetRangeUser(mZVtxMin, mZVtxMax); LOGF(info, "Restricting z-vtx: %f-%f", mZVtxMin, mZVtxMax); } - TH1* eventHist = mEventHist->getTHn(step)->Projection(1); + TH1* eventHist = mTriggerHist->getTHn(step)->Projection(1); - resetBinLimits(mEventHist->getTHn(step)); + resetBinLimits(mTriggerHist->getTHn(step)); return eventHist; } @@ -1020,7 +1131,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(CFStep step1, CFStep step2) // creates a track-level efficiency by dividing step2 by step1 // in all dimensions but the particle species one - StepTHnBase* sourceContainer = mTrackHistEfficiency; + StepTHn* sourceContainer = mTrackHistEfficiency; // step offset because we start with kCFStepAnaTopology step1 = (CFStep)((Int_t)step1 - (Int_t)kCFStepAnaTopology); step2 = (CFStep)((Int_t)step2 - (Int_t)kCFStepAnaTopology); @@ -1063,7 +1174,7 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t // cache it for efficiency (usually more than one efficiency is requested) - StepTHnBase* sourceContainer = nullptr; + StepTHn* sourceContainer = nullptr; if (source == 0) { return nullptr; @@ -1072,8 +1183,9 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t // step offset because we start with kCFStepAnaTopology step1 = (CFStep)((Int_t)step1 - (Int_t)kCFStepAnaTopology); step2 = (CFStep)((Int_t)step2 - (Int_t)kCFStepAnaTopology); - } else + } else { return nullptr; + } // reset all limits and set the right ones except those in axis1, axis2 and axis3 resetBinLimits(sourceContainer->getTHn(step1)); @@ -1166,8 +1278,9 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t Int_t count = 0; Int_t vars[3]; - for (Int_t i = 0; i < 3; i++) + for (Int_t i = 0; i < 3; i++) { vars[i] = binBegin[i]; + } const Int_t limit = 50; while (1) { @@ -1200,8 +1313,9 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t vars[0]++; } - if (vars[0] == binEnd[0] + 1) + if (vars[0] == binEnd[0] + 1) { break; + } total++; } @@ -1209,7 +1323,7 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t // rebin if required if (source == 2) { - TAxis* axis = mEventHist->getTHn(0)->GetAxis(0); + TAxis* axis = mTriggerHist->getTHn(0)->GetAxis(0); if (axis->GetNbins() < measured->GetNbinsX()) { if (axis2 != -1) { @@ -1217,20 +1331,22 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t TH1* tmp = measured; measured = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) + for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { ((TH2*)measured)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); measured->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } + } delete tmp; tmp = generated; generated = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) + for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { ((TH2*)generated)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); generated->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } + } delete tmp; } else { TH1* tmp = measured; @@ -1297,34 +1413,34 @@ TH1* CorrelationContainer::getTrackEfficiency(CFStep step1, CFStep step2, Int_t } //____________________________________________________________________ -TH1* CorrelationContainer::getEventEfficiency(CFStep step1, CFStep step2, Int_t axis1, Int_t axis2, Float_t ptLeadMin, Float_t ptLeadMax) +TH1* CorrelationContainer::getEventEfficiency(CFStep step1, CFStep step2, Int_t axis1, Int_t axis2, Float_t ptTriggerMin, Float_t ptTriggerMax) { // creates a event-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) - if (ptLeadMax > ptLeadMin) { - mEventHist->getTHn(step1)->GetAxis(0)->SetRangeUser(ptLeadMin, ptLeadMax); - mEventHist->getTHn(step2)->GetAxis(0)->SetRangeUser(ptLeadMin, ptLeadMax); + if (ptTriggerMax > ptTriggerMin) { + mTriggerHist->getTHn(step1)->GetAxis(0)->SetRangeUser(ptTriggerMin, ptTriggerMax); + mTriggerHist->getTHn(step2)->GetAxis(0)->SetRangeUser(ptTriggerMin, ptTriggerMax); } TH1* measured = nullptr; TH1* generated = nullptr; if (axis2 >= 0) { - generated = mEventHist->getTHn(step1)->Projection(axis1, axis2); - measured = mEventHist->getTHn(step2)->Projection(axis1, axis2); + generated = mTriggerHist->getTHn(step1)->Projection(axis1, axis2); + measured = mTriggerHist->getTHn(step2)->Projection(axis1, axis2); } else { - generated = mEventHist->getTHn(step1)->Projection(axis1); - measured = mEventHist->getTHn(step2)->Projection(axis1); + generated = mTriggerHist->getTHn(step1)->Projection(axis1); + measured = mTriggerHist->getTHn(step2)->Projection(axis1); } measured->Divide(measured, generated, 1, 1, "B"); delete generated; - if (ptLeadMax > ptLeadMin) { - mEventHist->getTHn(step1)->GetAxis(0)->SetRangeUser(0, -1); - mEventHist->getTHn(step2)->GetAxis(0)->SetRangeUser(0, -1); + if (ptTriggerMax > ptTriggerMin) { + mTriggerHist->getTHn(step1)->GetAxis(0)->SetRangeUser(0, -1); + mTriggerHist->getTHn(step2)->GetAxis(0)->SetRangeUser(0, -1); } return measured; @@ -1367,25 +1483,27 @@ TH1* CorrelationContainer::getBias(CFStep step1, CFStep step2, const char* axis, // 1 = only track bias is returned // 2 = only event bias is returned - StepTHnBase* tmp = mTrackHist; + StepTHn* tmp = mPairHist; resetBinLimits(tmp->getTHn(step1)); - resetBinLimits(mEventHist->getTHn(step1)); + resetBinLimits(mTriggerHist->getTHn(step1)); setBinLimits(tmp->getTHn(step1)); resetBinLimits(tmp->getTHn(step2)); - resetBinLimits(mEventHist->getTHn(step2)); + resetBinLimits(mTriggerHist->getTHn(step2)); setBinLimits(tmp->getTHn(step2)); - TH1D* events1 = (TH1D*)mEventHist->getTHn(step1)->Projection(0); + TH1D* events1 = (TH1D*)mTriggerHist->getTHn(step1)->Projection(0); TH3D* hist1 = (TH3D*)tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); - if (weighting == 0) + if (weighting == 0) { weightHistogram(hist1, events1); + } - TH1D* events2 = (TH1D*)mEventHist->getTHn(step2)->Projection(0); + TH1D* events2 = (TH1D*)mTriggerHist->getTHn(step2)->Projection(0); TH3D* hist2 = (TH3D*)tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); - if (weighting == 0) + if (weighting == 0) { weightHistogram(hist2, events2); + } TH1* generated = hist1; TH1* measured = hist2; @@ -1590,27 +1708,28 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) // copies the entries of this object's members from the object to this object // fills using the fill function and thus allows that the objects have different binning - for (Int_t step = 0; step < mTrackHist->getNSteps(); step++) { + for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); - THnBase* target = mTrackHist->getTHn(step); - THnBase* source = from->mTrackHist->getTHn(step); + THnBase* target = mPairHist->getTHn(step); + THnBase* source = from->mPairHist->getTHn(step); target->Reset(); target->RebinnedAdd(source); } - for (Int_t step = 0; step < mEventHist->getNSteps(); step++) { + for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { LOGF(info, "Ev: Copying step %d", step); - THnBase* target = mEventHist->getTHn(step); - THnBase* source = from->mEventHist->getTHn(step); + THnBase* target = mTriggerHist->getTHn(step); + THnBase* source = from->mTriggerHist->getTHn(step); target->Reset(); target->RebinnedAdd(source); } for (Int_t step = 0; step < TMath::Min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { - if (!from->mTrackHistEfficiency->getTHn(step)) + if (!from->mTrackHistEfficiency->getTHn(step)) { continue; + } LOGF(info, "Eff: Copying step %d", step); THnBase* target = mTrackHistEfficiency->getTHn(step); @@ -1625,23 +1744,25 @@ void CorrelationContainer::symmetrizepTBins() { // copy pt,a < pt,t bins to pt,a > pt,t (inverting deltaphi and delta eta as it should be) including symmetric bins - for (Int_t step = 0; step < mTrackHist->getNSteps(); step++) { + for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); - THnBase* target = mTrackHist->getTHn(step); - if (target->GetEntries() == 0) + THnBase* target = mPairHist->getTHn(step); + if (target->GetEntries() == 0) { continue; + } // for symmetric bins THnBase* source = (THnBase*)target->Clone(); Int_t zVtxBins = 1; - if (target->GetNdimensions() > 5) + if (target->GetNdimensions() > 5) { zVtxBins = target->GetAxis(5)->GetNbins(); + } // axes: 0 delta eta; 1 pT,a; 2 pT,t; 3 centrality; 4 delta phi; 5 vtx-z - for (Int_t i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) + for (Int_t i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { for (Int_t i5 = 1; i5 <= zVtxBins; i5++) { - for (Int_t i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) + for (Int_t i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { for (Int_t i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { // find source bin Int_t binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); @@ -1649,12 +1770,13 @@ void CorrelationContainer::symmetrizepTBins() LOGF(info, "(%d %d) Copying from %d %d to %d %d", i3, i5, binA, binT, i1, i2); - for (Int_t i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) + for (Int_t i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { for (Int_t i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { Int_t binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); Double_t phi = -target->GetAxis(4)->GetBinCenter(i4); - if (phi < -TMath::Pi() / 2) + if (phi < -TMath::Pi() / 2) { phi += TMath::TwoPi(); + } Int_t binPhi = target->GetAxis(4)->FindBin(phi); Int_t binSource[] = {binEta, binA, binT, i3, binPhi, i5}; @@ -1663,8 +1785,9 @@ void CorrelationContainer::symmetrizepTBins() Double_t value = source->GetBinContent(binSource); Double_t error = source->GetBinError(binSource); - if (error == 0) + if (error == 0) { continue; + } Double_t value2 = target->GetBinContent(binTarget); Double_t error2 = target->GetBinError(binTarget); @@ -1682,8 +1805,11 @@ void CorrelationContainer::symmetrizepTBins() target->SetBinContent(binTarget, sum); target->SetBinError(binTarget, err); } + } } + } } + } delete source; } @@ -1724,8 +1850,8 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitted efficiency between %f and %f and got %f tracking efficiency and %f tracking contamination correction. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, trackingEff, trackingCont, extendRangeBegin, mEtaMin, mEtaMax); // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) + for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { @@ -1738,6 +1864,8 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) mTrackHistEfficiency->getTHn(1)->SetBinContent(bins, 100.0 * trackingEff); mTrackHistEfficiency->getTHn(2)->SetBinContent(bins, 100.0 * trackingEff / trackingCont); } + } + } } else if (mTrackHistEfficiency->getNVar() == 4) { // fit in centrality intervals of 20% for efficiency, one bin for contamination Float_t* trackingEff = nullptr; @@ -1762,26 +1890,28 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } for (Int_t i = 0; i < centralityBinsLocal; i++) { - if (centralityBinsLocal == 1) + if (centralityBinsLocal == 1) { setCentralityRange(centralityBins[0] + 0.1, centralityBins[nCentralityBins] - 0.1); - else + } else { setCentralityRange(centralityBins[i] + 0.1, centralityBins[i + 1] - 0.1); + } TH1* proj = (caseNo == 0) ? getTrackingEfficiency(1) : getTrackingContamination(1); if (verbose) { new TCanvas; proj->DrawCopy(); } - if ((Int_t)proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd) == 0) + if ((Int_t)proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd) == 0) { target[i] = proj->GetFunction("pol0")->GetParameter(0); - else + } else { target[i] = 0; + } LOGF(info, "CorrelationContainer::extendTrackingEfficiency: case %d, centrality %d, eff %f", caseNo, i, target[i]); } } // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) + for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { for (Int_t z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis @@ -1794,19 +1924,23 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) bins[3] = z2; Int_t z2Bin = 0; - while (centralityBins[z2Bin + 1] < mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetBinCenter(z2)) + while (centralityBins[z2Bin + 1] < mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetBinCenter(z2)) { z2Bin++; + } //Printf("%d %d", z2, z2Bin); mTrackHistEfficiency->getTHn(0)->SetBinContent(bins, 100); mTrackHistEfficiency->getTHn(1)->SetBinContent(bins, 100.0 * trackingEff[z2Bin]); - if (trackingCont[0] > 0) + if (trackingCont[0] > 0) { mTrackHistEfficiency->getTHn(2)->SetBinContent(bins, 100.0 * trackingEff[z2Bin] / trackingCont[0]); - else + } else { mTrackHistEfficiency->getTHn(2)->SetBinContent(bins, 0); + } } } + } + } delete[] trackingEff; delete[] trackingCont; @@ -1832,33 +1966,28 @@ void CorrelationContainer::Reset() { // resets all contained histograms - for (Int_t step = 0; step < mTrackHist->getNSteps(); step++) - mTrackHist->getTHn(step)->Reset(); + for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + mPairHist->getTHn(step)->Reset(); + } - for (Int_t step = 0; step < mEventHist->getNSteps(); step++) - mEventHist->getTHn(step)->Reset(); + for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + mTriggerHist->getTHn(step)->Reset(); + } - for (Int_t step = 0; step < mTrackHistEfficiency->getNSteps(); step++) + for (Int_t step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { mTrackHistEfficiency->getTHn(step)->Reset(); + } } THnBase* CorrelationContainer::changeToThn(THnBase* sparse) { // change the object to THn for faster processing - // convert to THn (SEGV's for some strange reason...) - // x = THn::CreateHn("a", "a", sparse); - - // own implementation - Int_t nBins[10]; - for (Int_t i = 0; i < sparse->GetNdimensions(); i++) - nBins[i] = sparse->GetAxis(i)->GetNbins(); - THn* tmpTHn = new THnF(Form("%s_thn", sparse->GetName()), sparse->GetTitle(), sparse->GetNdimensions(), nBins, nullptr, nullptr); - for (Int_t i = 0; i < sparse->GetNdimensions(); i++) { - tmpTHn->SetBinEdges(i, sparse->GetAxis(i)->GetXbins()->GetArray()); - tmpTHn->GetAxis(i)->SetTitle(sparse->GetAxis(i)->GetTitle()); - } - tmpTHn->RebinnedAdd(sparse); + return THn::CreateHn(Form("%s_thn", sparse->GetName()), sparse->GetTitle(), sparse); +} - return tmpTHn; +void CorrelationContainer::fillEvent(Float_t centrality, CFStep step) +{ + // Fill per-event information + mEventCount->Fill(step, centrality); } diff --git a/Analysis/Core/src/HFConfigurables.cxx b/Analysis/Core/src/HFConfigurables.cxx new file mode 100644 index 0000000000000..f1d9351c26563 --- /dev/null +++ b/Analysis/Core/src/HFConfigurables.cxx @@ -0,0 +1,14 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// HF Configurable Classes +// +// Authors: Nima Zardoshti +#include "AnalysisCore/HFConfigurables.h" diff --git a/Analysis/Core/src/InsertNewRunToTimestamp.cxx b/Analysis/Core/src/InsertNewRunToTimestamp.cxx deleted file mode 100644 index 5753f3bd1da31..0000000000000 --- a/Analysis/Core/src/InsertNewRunToTimestamp.cxx +++ /dev/null @@ -1,99 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// -// A simple tool to produce objects for the conversion from run number to timestamp. -// The tool uploads the 'RunNumber -> timestamp' converter to CCDB. -// If no converter object is found in CCDB a new one is created and uploaded. -// -// Author: Nicolo' Jacazio on 2020-06-22 - -#include "Analysis/RunToTimestamp.h" -#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()( - "run,r", bpo::value()->required(), "Run number to use")( - "timestamp,t", bpo::value()->required(), "Timestamp to use equivalent to the run number")( - "path,p", bpo::value()->default_value("Test/RunToTimestamp"), "Path to the object in the CCDB repository")( - "url,u", bpo::value()->default_value("http://ccdb-test.cern.ch:8080"), "URL of the CCDB database")( - "start,s", bpo::value()->default_value(0), "Start timestamp of object validity")( - "stop,S", bpo::value()->default_value(4108971600000), "Stop timestamp of object validity")( - "update,u", bpo::value()->default_value(0), "Flag to update the object instead of inserting the new timestamp")( - "verbose,v", bpo::value()->default_value(0), "Verbose level 0, 1")( - "help,h", "Produce help message."); - - try { - bpo::store(parse_command_line(argc, argv, options), vm); - - // help - if (vm.count("help")) { - LOG(INFO) << options; - return false; - } - - bpo::notify(vm); - } catch (const bpo::error& e) { - LOG(ERROR) << e.what() << "\n"; - LOG(ERROR) << "Error parsing command line arguments; Available options:"; - LOG(ERROR) << options; - return false; - } - return true; -} - -int main(int argc, char* argv[]) -{ - bpo::options_description options("Allowed options"); - bpo::variables_map vm; - if (!initOptionsAndParse(options, argc, argv, vm)) { - return 1; - } - - o2::ccdb::CcdbApi api; - const std::string url = vm["url"].as(); - api.init(url); - if (!api.isHostReachable()) { - LOG(WARNING) << "CCDB host " << url << " is not reacheable, cannot go forward"; - return 1; - } - - std::string path = vm["path"].as(); - std::map metadata; - std::map* headers; - long start = vm["start"].as(); - long stop = vm["stop"].as(); - - RunToTimestamp* converter = api.retrieveFromTFileAny(path, metadata, -1, headers); - - if (!converter) { - LOG(INFO) << "Did not retrieve run number to timestamp converter, creating a new one!"; - converter = new RunToTimestamp(); - } else { - LOG(INFO) << "Retrieved run number to timestamp converter from ccdb url" << url; - } - - if (vm["update"].as()) - converter->update(vm["run"].as(), vm["timestamp"].as()); - else - converter->insert(vm["run"].as(), vm["timestamp"].as()); - - if (vm["verbose"].as()) - converter->print(); - - api.storeAsTFileAny(converter, path, metadata, start, stop); - - return 0; -} diff --git a/Analysis/Core/src/JetFinder.cxx b/Analysis/Core/src/JetFinder.cxx new file mode 100644 index 0000000000000..f232813d1861b --- /dev/null +++ b/Analysis/Core/src/JetFinder.cxx @@ -0,0 +1,92 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet finder task +// +// Author: Jochen Klein, Nima Zardoshti +#include "AnalysisCore/JetFinder.h" +#include "Framework/AnalysisTask.h" + +/// Sets the jet finding parameters +void JetFinder::setParams() +{ + + if (!isReclustering) { + jetEtaMin = etaMin + jetR; //in aliphysics this was (-etaMax + 0.95*jetR) + jetEtaMax = etaMax - jetR; + } + + //selGhosts =fastjet::SelectorRapRange(ghostEtaMin,ghostEtaMax) && fastjet::SelectorPhiRange(phiMin,phiMax); + //ghostAreaSpec=fastjet::GhostedAreaSpec(selGhosts,ghostRepeatN,ghostArea,gridScatter,ktScatter,ghostktMean); + ghostAreaSpec = fastjet::GhostedAreaSpec(ghostEtaMax, ghostRepeatN, ghostArea, gridScatter, ktScatter, ghostktMean); //the first argument is rapidity not pseudorapidity, to be checked + jetDef = fastjet::JetDefinition(algorithm, jetR, recombScheme, strategy); + areaDef = fastjet::AreaDefinition(areaType, ghostAreaSpec); + selJets = fastjet::SelectorPtRange(jetPtMin, jetPtMax) && fastjet::SelectorEtaRange(jetEtaMin, jetEtaMax) && fastjet::SelectorPhiRange(jetPhiMin, jetPhiMax); + jetDefBkg = fastjet::JetDefinition(algorithmBkg, jetBkgR, recombSchemeBkg, strategyBkg); + areaDefBkg = fastjet::AreaDefinition(areaTypeBkg, ghostAreaSpec); + selRho = fastjet::SelectorRapRange(bkgEtaMin, bkgEtaMax) && fastjet::SelectorPhiRange(bkgPhiMin, bkgPhiMax); //&& !fastjet::SelectorNHardest(2) //here we have to put rap range, to be checked! +} + +/// Sets the background subtraction estimater pointer +void JetFinder::setBkgE() +{ + if (bkgSubMode == BkgSubMode::rhoAreaSub || bkgSubMode == BkgSubMode::constSub) { + bkgE = decltype(bkgE)(new fastjet::JetMedianBackgroundEstimator(selRho, jetDefBkg, areaDefBkg)); + } else { + if (bkgSubMode != BkgSubMode::none) { + LOGF(ERROR, "requested subtraction mode not implemented!"); + } + } +} + +/// Sets the background subtraction pointer +void JetFinder::setSub() +{ + //if rho < 1e-6 it is set to 1e-6 in AliPhysics + if (bkgSubMode == BkgSubMode::rhoAreaSub) { + sub = decltype(sub){new fastjet::Subtractor{bkgE.get()}}; + } else if (bkgSubMode == BkgSubMode::constSub) { //event or jetwise + constituentSub = decltype(constituentSub){new fastjet::contrib::ConstituentSubtractor{bkgE.get()}}; + constituentSub->set_distance_type(fastjet::contrib::ConstituentSubtractor::deltaR); + constituentSub->set_max_distance(constSubRMax); + constituentSub->set_alpha(constSubAlpha); + constituentSub->set_ghost_area(ghostArea); + constituentSub->set_max_eta(bkgEtaMax); + constituentSub->set_background_estimator(bkgE.get()); //what about rho_m + } else { + if (bkgSubMode != BkgSubMode::none) { + LOGF(ERROR, "requested subtraction mode not implemented!"); + } + } +} + +/// Performs jet finding +/// \note the input particle and jet lists are passed by reference +/// \param inputParticles vector of input particles/tracks +/// \param jets veector of jets to be filled +/// \return ClusterSequenceArea object needed to access constituents +fastjet::ClusterSequenceArea JetFinder::findJets(std::vector& inputParticles, std::vector& jets) //ideally find a way of passing the cluster sequence as a reeference +{ + setParams(); + setBkgE(); + jets.clear(); + + if (bkgE) { + bkgE->set_particles(inputParticles); + setSub(); + } + if (constituentSub) { + inputParticles = constituentSub->subtract_event(inputParticles); + } + fastjet::ClusterSequenceArea clusterSeq(inputParticles, jetDef, areaDef); + jets = sub ? (*sub)(clusterSeq.inclusive_jets()) : clusterSeq.inclusive_jets(); + jets = selJets(jets); + return clusterSeq; +} diff --git a/Analysis/Core/src/RunToTimestamp.cxx b/Analysis/Core/src/RunToTimestamp.cxx deleted file mode 100644 index ac9af576820ee..0000000000000 --- a/Analysis/Core/src/RunToTimestamp.cxx +++ /dev/null @@ -1,57 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 conversion between run number and timestamp -// -// Author: Nicolo' Jacazio on 2020-06-22 - -#include "Analysis/RunToTimestamp.h" - -ClassImp(RunToTimestamp); - -bool RunToTimestamp::insert(uint runNumber, long timestamp) -{ - std::pair::iterator, bool> check; - check = mMap.insert(std::pair(runNumber, timestamp)); - if (!check.second) { - LOG(FATAL) << "Run number " << runNumber << " already existed with a timestamp of " << check.first->second; - return false; - } - LOG(INFO) << "Add new run " << runNumber << " with timestamp " << timestamp << " to converter"; - return true; -} - -bool RunToTimestamp::update(uint runNumber, long timestamp) -{ - if (!Has(runNumber)) { - LOG(FATAL) << "Run to Timestamp converter does not have run " << runNumber << ", cannot update converter"; - return false; - } - mMap[runNumber] = timestamp; - return true; -} - -long RunToTimestamp::getTimestamp(uint runNumber) const -{ - if (!Has(runNumber)) { - LOG(ERROR) << "Run to Timestamp converter does not have run " << runNumber; - return 0; - } - return mMap.at(runNumber); -} - -void RunToTimestamp::print() const -{ - LOG(INFO) << "Printing run number -> timestamp conversion"; - for (auto e : mMap) { - LOG(INFO) << "Run number: " << e.first << " timestamp: " << e.second << "\n"; - } -} diff --git a/Analysis/Core/src/StepTHn.cxx b/Analysis/Core/src/StepTHn.cxx deleted file mode 100644 index 9bc7a11815537..0000000000000 --- a/Analysis/Core/src/StepTHn.cxx +++ /dev/null @@ -1,502 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// Use StepTHn instead of THn and your memory consumption will be drastically reduced -// Once you have the merged output, use getTHn() to get a standard histogram -// -// this storage container is optimized for small memory usage -// under/over flow bins do not exist -// sumw2 structure is float only and only create when the weight != 1 -// -// Templated version allows also the use of double as storage container - -#include "Analysis/StepTHn.h" -#include "TList.h" -#include "TCollection.h" -#include "TArrayF.h" -#include "TArrayD.h" -#include "THn.h" -#include "TMath.h" - -// for LOGF -#include "Framework/AnalysisTask.h" - -templateClassImp(StepTHn) - - template - StepTHn::StepTHn() : StepTHnBase(), - mNBins(0), - mNVars(0), - mNSteps(0), - mValues(nullptr), - mSumw2(nullptr), - mTarget(nullptr), - mAxisCache(nullptr), - mNbinsCache(nullptr), - mLastVars(nullptr), - mLastBins(nullptr), - mPrototype(nullptr) -{ - // Constructor -} - -template -StepTHn::StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, Int_t* nBins, Double_t** binEdges, const char** axisTitles) : StepTHnBase(name, title, nSteps, nAxis, nBins, binEdges, axisTitles), - mNBins(0), - mNVars(nAxis), - mNSteps(nSteps), - mValues(nullptr), - mSumw2(nullptr), - mTarget(nullptr), - mAxisCache(nullptr), - mNbinsCache(nullptr), - mLastVars(nullptr), - mLastBins(nullptr), - mPrototype(nullptr) -{ - // Constructor - - mNBins = 1; - for (Int_t i = 0; i < mNVars; i++) - mNBins *= nBins[i]; - - // TODO this should be double for StepTHnD - - mPrototype = new THnSparseF(Form("%s_sparse", name), title, nAxis, nBins); - for (Int_t i = 0; i < mNVars; i++) { - mPrototype->SetBinEdges(i, binEdges[i]); - mPrototype->GetAxis(i)->SetTitle(axisTitles[i]); - } - - init(); -} - -template -void StepTHn::StepTHn::init() -{ - // initialize - - mValues = new TemplateArray*[mNSteps]; - mSumw2 = new TemplateArray*[mNSteps]; - - for (Int_t i = 0; i < mNSteps; i++) { - mValues[i] = nullptr; - mSumw2[i] = nullptr; - } -} - -template -StepTHn::StepTHn(const StepTHn& c) : StepTHnBase(c), - mNBins(c.mNBins), - mNVars(c.mNVars), - mNSteps(c.mNSteps), - mValues(new TemplateArray*[c.mNSteps]), - mSumw2(new TemplateArray*[c.mNSteps]), - mTarget(nullptr), - mAxisCache(nullptr), - mNbinsCache(nullptr), - mLastVars(nullptr), - mLastBins(nullptr), - mPrototype(nullptr) -{ - // - // StepTHn copy constructor - // - - ((StepTHn&)c).Copy(*this); -} - -template -StepTHn::~StepTHn() -{ - // Destructor - - deleteContainers(); - - delete[] mValues; - delete[] mSumw2; - delete[] mTarget; - delete[] mAxisCache; - delete[] mNbinsCache; - delete[] mLastVars; - delete[] mLastBins; - delete mPrototype; -} - -template -void StepTHn::deleteContainers() -{ - // delete data containers - - for (Int_t i = 0; i < mNSteps; i++) { - if (mValues && mValues[i]) { - delete mValues[i]; - mValues[i] = nullptr; - } - - if (mSumw2 && mSumw2[i]) { - delete mSumw2[i]; - mSumw2[i] = nullptr; - } - - if (mTarget && mTarget[i]) { - delete mTarget[i]; - mTarget[i] = nullptr; - } - } -} - -//____________________________________________________________________ -template -StepTHn& StepTHn::operator=(const StepTHn& c) -{ - // assigment operator - - if (this != &c) - ((StepTHn&)c).Copy(*this); - - return *this; -} - -//____________________________________________________________________ -template -void StepTHn::Copy(TObject& c) const -{ - // copy function - - StepTHn& target = (StepTHn&)c; - - TNamed::Copy(c); - - target.mNBins = mNBins; - target.mNVars = mNVars; - target.mNSteps = mNSteps; - - target.init(); - - for (Int_t i = 0; i < mNSteps; i++) { - if (mValues[i]) - target.mValues[i] = new TemplateArray(*(mValues[i])); - else - target.mValues[i] = nullptr; - - if (mSumw2[i]) - target.mSumw2[i] = new TemplateArray(*(mSumw2[i])); - else - target.mSumw2[i] = nullptr; - } - - if (mPrototype) - target.mPrototype = dynamic_cast(mPrototype->Clone()); -} - -//____________________________________________________________________ -template -Long64_t StepTHn::Merge(TCollection* list) -{ - // Merge a list of StepTHn objects with this (needed for - // PROOF). - // Returns the number of merged objects (including this). - - if (!list) - return 0; - - if (list->IsEmpty()) - return 1; - - TIterator* iter = list->MakeIterator(); - TObject* obj; - - Int_t count = 0; - while ((obj = iter->Next())) { - - StepTHn* entry = dynamic_cast(obj); - if (entry == nullptr) - continue; - - for (Int_t i = 0; i < mNSteps; i++) { - if (entry->mValues[i]) { - if (!mValues[i]) - mValues[i] = new TemplateArray(mNBins); - - for (Long64_t l = 0; l < mNBins; l++) - mValues[i]->GetArray()[l] += entry->mValues[i]->GetArray()[l]; - } - - if (entry->mSumw2[i]) { - if (!mSumw2[i]) - mSumw2[i] = new TemplateArray(mNBins); - - for (Long64_t l = 0; l < mNBins; l++) - mSumw2[i]->GetArray()[l] += entry->mSumw2[i]->GetArray()[l]; - } - } - - count++; - } - - return count + 1; -} - -template -void StepTHn::Fill(const Double_t* var, Int_t istep, Double_t weight) -{ - // fills an entry - - // fill axis cache - if (!mAxisCache) { - mAxisCache = new TAxis*[mNVars]; - mNbinsCache = new Int_t[mNVars]; - for (Int_t i = 0; i < mNVars; i++) { - mAxisCache[i] = mPrototype->GetAxis(i); - mNbinsCache[i] = mAxisCache[i]->GetNbins(); - } - - mLastVars = new Double_t[mNVars]; - mLastBins = new Int_t[mNVars]; - - // initial values to prevent checking for 0 below - for (Int_t i = 0; i < mNVars; i++) { - mLastBins[i] = mAxisCache[i]->FindBin(var[i]); - mLastVars[i] = var[i]; - } - } - - // calculate global bin index - Long64_t bin = 0; - for (Int_t i = 0; i < mNVars; i++) { - bin *= mNbinsCache[i]; - - Int_t tmpBin = 0; - if (mLastVars[i] == var[i]) - tmpBin = mLastBins[i]; - else { - tmpBin = mAxisCache[i]->FindBin(var[i]); - mLastBins[i] = tmpBin; - mLastVars[i] = var[i]; - } - //Printf("%d", tmpBin); - - // under/overflow not supported - if (tmpBin < 1 || tmpBin > mNbinsCache[i]) - return; - - // bins start from 0 here - bin += tmpBin - 1; - // Printf("%lld", bin); - } - - if (!mValues[istep]) { - mValues[istep] = new TemplateArray(mNBins); - LOGF(info, "Created values container for step %d", istep); - } - - if (weight != 1) { - // initialize with already filled entries (which have been filled with weight == 1), in this case mSumw2 := mValues - if (!mSumw2[istep]) { - mSumw2[istep] = new TemplateArray(*mValues[istep]); - LOGF(info, "Created sumw2 container for step %d", istep); - } - } - - mValues[istep]->GetArray()[bin] += weight; - if (mSumw2[istep]) - mSumw2[istep]->GetArray()[bin] += weight * weight; - - // Printf("%f", mValues[istep][bin]); - - // debug - // AliCFContainer::Fill(var, istep, weight); -} - -template -Long64_t StepTHn::getGlobalBinIndex(const Int_t* binIdx) -{ - // calculates global bin index - // binIdx contains TAxis bin indexes - // here bin count starts at 0 because we do not have over/underflow bins - - Long64_t bin = 0; - for (Int_t i = 0; i < mNVars; i++) { - bin *= mPrototype->GetAxis(i)->GetNbins(); - bin += binIdx[i] - 1; - } - - return bin; -} - -template -void StepTHn::createTarget(Int_t step, Bool_t sparse) -{ - // fills the information stored in the buffer in this class into the target THn - - if (!mValues[step]) { - LOGF(fatal, "Histogram request for step %d which is empty.", step); - return; - } - - if (!mTarget) { - mTarget = new THnBase*[mNSteps]; - for (Int_t i = 0; i < mNSteps; i++) - mTarget[i] = nullptr; - } - - if (mTarget[step]) - return; - - TemplateType* source = mValues[step]->GetArray(); - // if mSumw2 is not stored, the sqrt of the number of bin entries in source is filled below; otherwise we use mSumw2 - TemplateType* sourceSumw2 = source; - if (mSumw2[step]) - sourceSumw2 = mSumw2[step]->GetArray(); - - if (sparse) - mTarget[step] = THnSparse::CreateSparse(Form("%s_%d", GetName(), step), Form("%s_%d", GetTitle(), step), mPrototype); - else - mTarget[step] = THn::CreateHn(Form("%s_%d", GetName(), step), Form("%s_%d", GetTitle(), step), mPrototype); - - THnBase* target = mTarget[step]; - - Int_t* binIdx = new Int_t[mNVars]; - Int_t* nBins = new Int_t[mNVars]; - for (Int_t j = 0; j < mNVars; j++) { - binIdx[j] = 1; - nBins[j] = target->GetAxis(j)->GetNbins(); - } - - Long64_t count = 0; - - while (1) { - // for (Int_t j=0; j %lld", globalBin); - - if (source[globalBin] != 0) { - target->SetBinContent(binIdx, source[globalBin]); - target->SetBinError(binIdx, TMath::Sqrt(sourceSumw2[globalBin])); - - count++; - } - - binIdx[mNVars - 1]++; - - for (Int_t j = mNVars - 1; j > 0; j--) { - if (binIdx[j] > nBins[j]) { - binIdx[j] = 1; - binIdx[j - 1]++; - } - } - - if (binIdx[0] > nBins[0]) - break; - } - - LOGF(info, "Step %d: copied %lld entries out of %lld bins", step, count, getGlobalBinIndex(binIdx)); - - delete[] binIdx; - delete[] nBins; - - delete mValues[step]; - mValues[step] = nullptr; -} - -/* -template -void StepTHn::FillParent() -{ - // fills the information stored in the buffer in this class into the baseclass containers - - FillContainer(this); -} - -template -void StepTHn::ReduceAxis() -{ - // "removes" one axis by summing over the axis and putting the entry to bin 1 - // TODO presently only implemented for the last axis - - Int_t axis = mNVars-1; - - for (Int_t i=0; iGetArray(); - TemplateType* sourceSumw2 = 0; - if (mSumw2[i]) - sourceSumw2 = mSumw2[i]->GetArray(); - - THnSparse* target = GetGrid(i)->GetGrid(); - - Int_t* binIdx = new Int_t[mNVars]; - Int_t* nBins = new Int_t[mNVars]; - for (Int_t j=0; jGetAxis(j)->GetNbins(); - } - - Long64_t count = 0; - - while (1) - { - // sum over axis - TemplateType sumValues = 0; - TemplateType sumSumw2 = 0; - for (Int_t j=1; j<=nBins[axis]; j++) - { - binIdx[axis] = j; - Long64_t globalBin = getGlobalBinIndex(binIdx); - sumValues += source[globalBin]; - source[globalBin] = 0; - - if (sourceSumw2) - { - sumSumw2 += sourceSumw2[globalBin]; - sourceSumw2[globalBin] = 0; - } - } - binIdx[axis] = 1; - - Long64_t globalBin = getGlobalBinIndex(binIdx); - source[globalBin] = sumValues; - if (sourceSumw2) - sourceSumw2[globalBin] = sumSumw2; - - count++; - - // next bin - binIdx[mNVars-2]++; - - for (Int_t j=mNVars-2; j>0; j--) - { - if (binIdx[j] > nBins[j]) - { - binIdx[j] = 1; - binIdx[j-1]++; - } - } - - if (binIdx[0] > nBins[0]) - break; - } - - AliInfo(Form("Step %d: reduced %lld bins to %lld entries", i, getGlobalBinIndex(binIdx), count)); - - delete[] binIdx; - delete[] nBins; - } -}*/ - -template class StepTHn; -template class StepTHn; diff --git a/Analysis/Core/src/TrackSelection.cxx b/Analysis/Core/src/TrackSelection.cxx index dd281f14137bc..70128b43ed68c 100644 --- a/Analysis/Core/src/TrackSelection.cxx +++ b/Analysis/Core/src/TrackSelection.cxx @@ -12,15 +12,7 @@ // Class for track selection // -#include "Analysis/TrackSelection.h" -#include - -ClassImp(TrackSelection) - - TrackSelection::TrackSelection() - : TObject(), mMinPt{0.}, mMaxPt{1e10}, mMinEta{0.}, mMaxEta{1e10}, mMinNClustersTPC{0}, mMinNCrossedRowsTPC{0}, mMinNClustersITS{0}, mMaxChi2PerClusterTPC{1e10}, mMaxChi2PerClusterITS{1e10}, mMinNCrossedRowsOverFindableClustersTPC{1e10}, mMaxDcaXY{1e10}, mMaxDcaZ{1e10}, mRequireITSRefit{false}, mRequireTPCRefit{false} -{ -} +#include "AnalysisCore/TrackSelection.h" bool TrackSelection::FulfillsITSHitRequirements(uint8_t itsClusterMap) { @@ -34,4 +26,6 @@ bool TrackSelection::FulfillsITSHitRequirements(uint8_t itsClusterMap) } } return true; -}; +} + +const std::string TrackSelection::mCutNames[static_cast(TrackSelection::TrackCuts::kNCuts)] = {"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz"}; diff --git a/Analysis/Core/src/TriggerAliases.cxx b/Analysis/Core/src/TriggerAliases.cxx new file mode 100644 index 0000000000000..8039cc3bef6d5 --- /dev/null +++ b/Analysis/Core/src/TriggerAliases.cxx @@ -0,0 +1,22 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "AnalysisCore/TriggerAliases.h" + +void TriggerAliases::Print() +{ + for (auto& al : GetAliasToClassIdsMap()) { + printf("%d", al.first); + for (auto& classIndex : al.second) { + printf(" %d", classIndex); + } + printf("\n"); + } +} diff --git a/Analysis/Core/src/VarManager.cxx b/Analysis/Core/src/VarManager.cxx deleted file mode 100644 index fea8b82695e55..0000000000000 --- a/Analysis/Core/src/VarManager.cxx +++ /dev/null @@ -1,221 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "Analysis/VarManager.h" - -ClassImp(VarManager) - - TString VarManager::fgVariableNames[VarManager::kNVars] = {""}; -TString VarManager::fgVariableUnits[VarManager::kNVars] = {""}; -bool VarManager::fgUsedVars[VarManager::kNVars] = {kFALSE}; -float VarManager::fgValues[VarManager::kNVars] = {0.0}; -std::map VarManager::fgRunMap; - -//__________________________________________________________________ -VarManager::VarManager() : TObject() -{ - // - // constructor - // - SetDefaultVarNames(); -} - -//__________________________________________________________________ -VarManager::~VarManager() = default; - -//__________________________________________________________________ -void VarManager::SetVariableDependencies() -{ - // - // Set as used those variables on which other variables calculation depends - // -} - -//__________________________________________________________________ -void VarManager::ResetValues(int startValue, int endValue) -{ - // - // reset all variables to an "innocent" value - // NOTE: here we use -9999.0 as a neutral value, but depending on situation, this may not be the case - for (Int_t i = startValue; i < endValue; ++i) - fgValues[i] = -9999.; -} - -//__________________________________________________________________ -void VarManager::SetRunNumbers(int n, int* runs) -{ - // - // maps the list of runs such that one can plot the list of runs nicely in a histogram axis - // - for (int i = 0; i < n; ++i) - fgRunMap[runs[i]] = i + 1; -} - -//__________________________________________________________________ -void VarManager::FillEvent(vector event, float* values) -{ - - //TODO: the Fill function should take as argument an aod::ReducedEvent iterator, this is just a temporary fix - if (!values) - values = fgValues; - values[kRunNo] = event[0]; - values[kRunId] = (fgRunMap.size() > 0 ? fgRunMap[int(values[kRunNo])] : 0); - values[kVtxX] = event[1]; - values[kVtxY] = event[2]; - values[kVtxZ] = event[3]; - values[kVtxNcontrib] = event[4]; - /*values[kRunNo] = event.runNumber(); - values[kVtxX] = event.posX(); - values[kVtxY] = event.posY(); - values[kVtxZ] = event.posZ(); - values[kVtxNcontrib] = event.numContrib();*/ - //values[kVtxChi2] = event.chi2(); - //values[kBC] = event.bc(); - //values[kCentVZERO] = event.centVZERO(); - //values[kVtxCovXX] = event.covXX(); - //values[kVtxCovXY] = event.covXY(); - //values[kVtxCovXZ] = event.covXZ(); - //values[kVtxCovYY] = event.covYY(); - //values[kVtxCovYZ] = event.covYZ(); - //values[kVtxCovZZ] = event.covZZ(); -} - -//__________________________________________________________________ -void VarManager::FillTrack(vector track, float* values) -{ - - if (!values) - values = fgValues; - - values[kPt] = track[0]; - values[kEta] = track[1]; - values[kPhi] = track[2]; - values[kCharge] = track[3]; - /*values[kPt] = track.pt(); - values[kEta] = track.eta(); - values[kPhi] = track.phi(); - values[kCharge] = track.charge();*/ - //values[kPin] = track.tpcInnerParam(); - //if(fgUsedVars[kITSncls]) { - // values[kITSncls] = 0.0; - // for(int i=0; i<6; ++i) - // values[kITSncls] += ((track.itsClusterMap() & (1< bool { return bbV0A && bbV0C && bbZNA && bbZNC; }); -} // namespace evsel -DECLARE_SOA_TABLE(EvSels, "AOD", "EVSEL", - evsel::Alias, - evsel::BBV0A, evsel::BBV0C, evsel::BGV0A, evsel::BGV0C, evsel::BBZNA, evsel::BBZNC, - evsel::BBFDA, evsel::BBFDC, evsel::BGFDA, evsel::BGFDC, - evsel::SEL7); -using EvSel = EvSels::iterator; -} // namespace o2::aod - -#endif // O2_ANALYSIS_EVENTSELECTION_H_ diff --git a/Analysis/DataModel/include/Analysis/Jet.h b/Analysis/DataModel/include/Analysis/Jet.h deleted file mode 100644 index 0d08276852a7d..0000000000000 --- a/Analysis/DataModel/include/Analysis/Jet.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// table definitions for jets -// -// Author: Jochen Klein - -#pragma once - -#include "Framework/AnalysisDataModel.h" - -namespace o2::aod -{ -namespace jet -{ -DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_COLUMN(Eta, eta, float); -DECLARE_SOA_COLUMN(Phi, phi, float); -DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_COLUMN(Area, area, float); -DECLARE_SOA_COLUMN(Energy, energy, float); -DECLARE_SOA_COLUMN(Mass, mass, float); -} // namespace jet - -DECLARE_SOA_TABLE(Jets, "AOD", "JET", - o2::soa::Index<>, jet::CollisionId, - jet::Eta, jet::Phi, jet::Pt, jet::Area, jet::Energy, jet::Mass); - -using Jet = Jets::iterator; - -// TODO: absorb in jet table -// when list of references available -namespace constituents -{ -DECLARE_SOA_INDEX_COLUMN(Jet, jet); -DECLARE_SOA_INDEX_COLUMN(Track, track); -} // namespace constituents - -DECLARE_SOA_TABLE(JetConstituents, "AOD", "JETCONSTITUENTS", - constituents::JetId, constituents::TrackId); - -using JetConstituent = JetConstituents::iterator; -} // namespace o2::aod diff --git a/Analysis/DataModel/include/Analysis/Multiplicity.h b/Analysis/DataModel/include/Analysis/Multiplicity.h deleted file mode 100644 index e1895b04a5989..0000000000000 --- a/Analysis/DataModel/include/Analysis/Multiplicity.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_ANALYSIS_MULTIPLICITY_H_ -#define O2_ANALYSIS_MULTIPLICITY_H_ - -#include "Framework/AnalysisDataModel.h" - -namespace o2::aod -{ -namespace mult -{ -DECLARE_SOA_COLUMN(MultV0A, multV0A, float); -DECLARE_SOA_COLUMN(MultV0C, multV0C, float); -DECLARE_SOA_COLUMN(MultZNA, multZNA, float); -DECLARE_SOA_COLUMN(MultZNC, multZNC, float); -DECLARE_SOA_DYNAMIC_COLUMN(MultV0M, multV0M, [](float multV0A, float multV0C) -> float { return multV0A + multV0C; }); -} // namespace mult -DECLARE_SOA_TABLE(Mults, "AOD", "MULT", mult::MultV0A, mult::MultV0C, mult::MultZNA, mult::MultZNC, mult::MultV0M); -using Mult = Mults::iterator; -} // namespace o2::aod - -#endif // O2_ANALYSIS_MULTIPLICITY_H_ diff --git a/Analysis/DataModel/include/Analysis/RecoDecay.h b/Analysis/DataModel/include/Analysis/RecoDecay.h deleted file mode 100644 index d619b515b122f..0000000000000 --- a/Analysis/DataModel/include/Analysis/RecoDecay.h +++ /dev/null @@ -1,84 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_ANALYSIS_RECODECAY_H_ -#define O2_ANALYSIS_RECODECAY_H_ - -float energy(float px, float py, float pz, float mass) -{ - float en_ = sqrtf(mass * mass + px * px + py * py + pz * pz); - return en_; -}; - -float invmass2prongs2(float px0, float py0, float pz0, float mass0, - float px1, float py1, float pz1, float mass1) -{ - - float energy0_ = energy(px0, py0, pz0, mass0); - float energy1_ = energy(px1, py1, pz1, mass1); - float energytot = energy0_ + energy1_; - - float psum2 = (px0 + px1) * (px0 + px1) + - (py0 + py1) * (py0 + py1) + - (pz0 + pz1) * (pz0 + pz1); - return energytot * energytot - psum2; -}; - -float invmass3prongs2(float px0, float py0, float pz0, float mass0, - float px1, float py1, float pz1, float mass1, - float px2, float py2, float pz2, float mass2) -{ - float energy0_ = energy(px0, py0, pz0, mass0); - float energy1_ = energy(px1, py1, pz1, mass1); - float energy2_ = energy(px2, py2, pz2, mass2); - float energytot = energy0_ + energy1_ + energy2_; - - float psum2 = (px0 + px1 + px2) * (px0 + px1 + px2) + - (py0 + py1 + py2) * (py0 + py1 + py2) + - (pz0 + pz1 + pz2) * (pz0 + pz1 + pz2); - return energytot * energytot - psum2; -}; - -float invmass2prongs(float px0, float py0, float pz0, float mass0, - float px1, float py1, float pz1, float mass1) -{ - return sqrt(invmass2prongs2(px0, py0, pz0, mass0, - px1, py1, pz1, mass1)); -}; - -float invmass3prongs(float px0, float py0, float pz0, float mass0, - float px1, float py1, float pz1, float mass1, - float px2, float py2, float pz2, float mass2) -{ - return sqrt(invmass3prongs2(px0, py0, pz0, mass0, - px1, py1, pz1, mass1, - px2, py2, pz2, mass2)); -}; - -float ptcand2prong(float px0, float py0, float px1, float py1) -{ - return sqrt((px0 + px1) * (px0 + px1) + (py0 + py1) * (py0 + py1)); -}; - -float pttrack(float px, float py) -{ - return sqrt(px * px + py * py); -}; - -float declength(float xdecay, float ydecay, float zdecay, float xvtx, float yvtx, float zvtx) -{ - return sqrtf((xdecay - xvtx) * (xdecay - xvtx) + (ydecay - yvtx) * (ydecay - yvtx) + (zdecay - zvtx) * (zdecay - zvtx)); -}; - -float declengthxy(float xdecay, float ydecay, float xvtx, float yvtx) -{ - return sqrtf((xdecay - xvtx) * (xdecay - xvtx) + (ydecay - yvtx) * (ydecay - yvtx)); -}; - -#endif // O2_ANALYSIS_RECODECAY_H_ diff --git a/Analysis/DataModel/include/Analysis/ReducedInfoTables.h b/Analysis/DataModel/include/Analysis/ReducedInfoTables.h deleted file mode 100644 index 685026f8e1c4a..0000000000000 --- a/Analysis/DataModel/include/Analysis/ReducedInfoTables.h +++ /dev/null @@ -1,115 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// - -#ifndef O2_Analysis_ReducedInfoTables_H_ -#define O2_Analysis_ReducedInfoTables_H_ - -#include "Framework/AnalysisDataModel.h" -#include "Analysis/Centrality.h" -#include "MathUtils/Utils.h" -#include - -namespace o2 -{ -namespace aod -{ - -namespace reducedevent -{ - -// basic event information -DECLARE_SOA_COLUMN(Tag, tag, uint64_t); - -// TODO: implement additional separate tables for other types of information, e.g. multiplicity estimators, VZERO-TZERO/FIT, ZDC, etc. - -} // namespace reducedevent - -DECLARE_SOA_TABLE(ReducedEvents, "AOD", "REDUCEDEVENT", o2::soa::Index<>, - reducedevent::Tag, bc::RunNumber, - collision::PosX, collision::PosY, collision::PosZ, collision::NumContrib); - -DECLARE_SOA_TABLE(ReducedEventsExtended, "AOD", "REEXTENDED", - bc::GlobalBC, bc::TriggerMask, collision::CollisionTime, cent::CentV0M); - -DECLARE_SOA_TABLE(ReducedEventsVtxCov, "AOD", "REVTXCOV", - collision::CovXX, collision::CovXY, collision::CovXZ, - collision::CovYY, collision::CovYZ, collision::CovZZ, collision::Chi2); - -using ReducedEvent = ReducedEvents::iterator; -using ReducedEventExtended = ReducedEventsExtended::iterator; -using ReducedEventVtxCov = ReducedEventsVtxCov::iterator; - -namespace reducedtrack -{ -// basic track information -DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); -DECLARE_SOA_COLUMN(Index, index, uint16_t); -// ---- flags reserved for storing various information during filtering -DECLARE_SOA_COLUMN(FilteringFlags, filteringFlags, uint64_t); -// BIT 0: track is from MUON arm (if not toggled then this is a barrel track) -// ----------------------------------------------------- -DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_COLUMN(Eta, eta, float); -DECLARE_SOA_COLUMN(Phi, phi, float); -DECLARE_SOA_COLUMN(Charge, charge, short); -DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return abs(pt) * cos(phi); }); -DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return abs(pt) * sin(phi); }); -DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return abs(pt) * sinh(eta); }); -DECLARE_SOA_DYNAMIC_COLUMN(Pmom, pmom, [](float pt, float eta) -> float { return abs(pt) * cosh(eta); }); - -// MUON tracks extra information -DECLARE_SOA_COLUMN(MuonChi2, muonChi2, float); -DECLARE_SOA_COLUMN(MuonChi2MatchTrigger, muonChi2MatchTrigger, float); - -} //namespace reducedtrack - -// basic track information -DECLARE_SOA_TABLE(ReducedTracks, "AOD", "REDUCEDTRACK", - o2::soa::Index<>, reducedtrack::ReducedEventId, reducedtrack::Index, reducedtrack::FilteringFlags, - reducedtrack::Pt, reducedtrack::Eta, reducedtrack::Phi, reducedtrack::Charge, - reducedtrack::Px, - reducedtrack::Py, - reducedtrack::Pz, - reducedtrack::Pmom); - -// barrel track information -DECLARE_SOA_TABLE(ReducedTracksBarrel, "AOD", "RTBARREL", - track::TPCInnerParam, track::Flags, // tracking status flags - track::ITSClusterMap, track::ITSChi2NCl, - track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, - track::TPCNClsShared, track::TPCChi2NCl, - track::TPCSignal, track::TRDSignal, track::TOFSignal, - track::TRDChi2, track::TOFChi2, track::Length, - track::TPCNClsFound, - track::TPCNClsCrossedRows); - -// barrel covariance matrix -DECLARE_SOA_TABLE(ReducedTracksBarrelCov, "AOD", "RTBARRELCOV", - track::CYY, track::CZZ, track::CSnpSnp, - track::CTglTgl, track::C1Pt21Pt2); - -// TODO: change names of these columns in the AnalysisDataModel to identify the members as muon quantities -DECLARE_SOA_TABLE(ReducedTracksMuon, "AOD", "RTMUON", - reducedtrack::MuonChi2, reducedtrack::MuonChi2MatchTrigger); - -// iterators -using ReducedTrack = ReducedTracks::iterator; -using ReducedTrackBarrel = ReducedTracksBarrel::iterator; -using ReducedTrackBarrelCov = ReducedTracksBarrelCov::iterator; -using ReducedTrackMuon = ReducedTracksMuon::iterator; - -} // namespace aod - -} // namespace o2 - -#endif // O2_Analysis_ReducedInfoTables_H_ diff --git a/Analysis/DataModel/include/Analysis/SecondaryVertex.h b/Analysis/DataModel/include/Analysis/SecondaryVertex.h deleted file mode 100644 index bac1456896728..0000000000000 --- a/Analysis/DataModel/include/Analysis/SecondaryVertex.h +++ /dev/null @@ -1,75 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_ANALYSIS_SECONDARYVERTEX_H_ -#define O2_ANALYSIS_SECONDARYVERTEX_H_ - -#include "Framework/AnalysisDataModel.h" - -namespace o2::aod -{ -namespace secvtx2prong -{ -// FIXME: this is a workaround until we get the index columns to work with joins. -using BigTracks = soa::Join; - -DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_COLUMN(Posdecayx, posdecayx, float); -DECLARE_SOA_COLUMN(Posdecayy, posdecayy, float); -DECLARE_SOA_COLUMN(Posdecayz, posdecayz, float); -DECLARE_SOA_INDEX_COLUMN_FULL(Index0, index0, int, BigTracks, "fIndex0"); -DECLARE_SOA_COLUMN(Px0, px0, float); -DECLARE_SOA_COLUMN(Py0, py0, float); -DECLARE_SOA_COLUMN(Pz0, pz0, float); -DECLARE_SOA_COLUMN(Y0, y0, float); -DECLARE_SOA_INDEX_COLUMN_FULL(Index1, index1, int, BigTracks, "fIndex1"); -DECLARE_SOA_COLUMN(Px1, px1, float); -DECLARE_SOA_COLUMN(Py1, py1, float); -DECLARE_SOA_COLUMN(Pz1, pz1, float); -DECLARE_SOA_COLUMN(Y1, y1, float); -DECLARE_SOA_COLUMN(Mass, mass, float); -DECLARE_SOA_COLUMN(Massbar, massbar, float); -DECLARE_SOA_DYNAMIC_COLUMN(DecaylengthXY, decaylengthXY, - [](float xvtxd, float yvtxd, float xvtxp, float yvtxp) { return sqrtf((yvtxd - yvtxp) * (yvtxd - yvtxp) + (xvtxd - xvtxp) * (xvtxd - xvtxp)); }); -DECLARE_SOA_DYNAMIC_COLUMN(Decaylength, decaylength, - [](float xvtxd, float yvtxd, float zvtxd, float xvtxp, - float yvtxp, float zvtxp) { return sqrtf((yvtxd - yvtxp) * (yvtxd - yvtxp) + (xvtxd - xvtxp) * (xvtxd - xvtxp) + (zvtxd - zvtxp) * (zvtxd - zvtxp)); }); -//old way of doing it -//DECLARE_SOA_COLUMN(Decaylength, decaylength, float); -//DECLARE_SOA_COLUMN(DecaylengthXY, decaylengthXY, float); - -} // namespace secvtx2prong -namespace cand2prong -{ -DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_COLUMN(MassD0, massD0, float); -DECLARE_SOA_COLUMN(MassD0bar, massD0bar, float); -} // namespace cand2prong - -DECLARE_SOA_TABLE(SecVtx2Prong, "AOD", "VTX2PRONG", - secvtx2prong::CollisionId, - collision::PosX, collision::PosY, collision::PosZ, - secvtx2prong::Posdecayx, secvtx2prong::Posdecayy, secvtx2prong::Posdecayz, - secvtx2prong::Index0Id, - secvtx2prong::Px0, secvtx2prong::Py0, secvtx2prong::Pz0, secvtx2prong::Y0, - secvtx2prong::Index1Id, - secvtx2prong::Px1, secvtx2prong::Py1, secvtx2prong::Pz1, secvtx2prong::Y1, - secvtx2prong::Mass, secvtx2prong::Massbar, - secvtx2prong::DecaylengthXY, - secvtx2prong::Decaylength); - -DECLARE_SOA_TABLE(Cand2Prong, "AOD", "CANDDZERO", - //cand2prong::CollisionId, - cand2prong::MassD0, cand2prong::MassD0bar); -} // namespace o2::aod - -#endif // O2_ANALYSIS_SECONDARYVERTEX_H_ diff --git a/Analysis/DataModel/include/Analysis/SecondaryVertexHF.h b/Analysis/DataModel/include/Analysis/SecondaryVertexHF.h deleted file mode 100644 index b4b70649a3bc9..0000000000000 --- a/Analysis/DataModel/include/Analysis/SecondaryVertexHF.h +++ /dev/null @@ -1,98 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_ANALYSIS_SECONDARYVERTEXHF_H_ -#define O2_ANALYSIS_SECONDARYVERTEXHF_H_ - -#include "Framework/AnalysisDataModel.h" -#include "Analysis/RecoDecay.h" - -namespace o2::aod -{ -namespace hftrackindexprong2 -{ -// FIXME: this is a workaround until we get the index columns to work with joins. -using BigTracks = soa::Join; - -DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_INDEX_COLUMN_FULL(Index0, index0, int, BigTracks, "fIndex0"); -DECLARE_SOA_INDEX_COLUMN_FULL(Index1, index1, int, BigTracks, "fIndex1"); -DECLARE_SOA_COLUMN(HFflag, hfflag, int); -} // namespace hftrackindexprong2 - -namespace hftrackindexprong3 -{ -// FIXME: this is a workaround until we get the index columns to work with joins. -using BigTracks = soa::Join; - -DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_INDEX_COLUMN_FULL(Index0, index0, int, BigTracks, "fIndex0"); -DECLARE_SOA_INDEX_COLUMN_FULL(Index1, index1, int, BigTracks, "fIndex1"); -DECLARE_SOA_INDEX_COLUMN_FULL(Index2, index2, int, BigTracks, "fIndex2"); -DECLARE_SOA_COLUMN(HFflag, hfflag, int); -} // namespace hftrackindexprong3 - -namespace hfcandprong2 -{ -DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_COLUMN(PxProng0, pxprong0, float); -DECLARE_SOA_COLUMN(PyProng0, pyprong0, float); -DECLARE_SOA_COLUMN(PzProng0, pzprong0, float); -DECLARE_SOA_COLUMN(PxProng1, pxprong1, float); -DECLARE_SOA_COLUMN(PyProng1, pyprong1, float); -DECLARE_SOA_COLUMN(PzProng1, pzprong1, float); -DECLARE_SOA_COLUMN(DecayVtxX, decayvtxx, float); -DECLARE_SOA_COLUMN(DecayVtxY, decayvtxy, float); -DECLARE_SOA_COLUMN(DecayVtxZ, decayvtxz, float); -DECLARE_SOA_COLUMN(MassD0, massD0, float); -DECLARE_SOA_COLUMN(MassD0bar, massD0bar, float); -DECLARE_SOA_DYNAMIC_COLUMN(PtProng0, ptprong0, - [](float px0, float py0) { return pttrack(px0, py0); }); -DECLARE_SOA_DYNAMIC_COLUMN(PtProng1, ptprong1, - [](float px1, float py1) { return pttrack(px1, py1); }); -DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, - [](float px0, float py0, float px1, float py1) { return ptcand2prong(px0, py0, px1, py1); }); -DECLARE_SOA_DYNAMIC_COLUMN(DecaylengthXY, decaylengthxy, - [](float xvtxd, float yvtxd, float xvtxp, float yvtxp) { return declengthxy(xvtxd, yvtxd, xvtxp, yvtxp); }); -DECLARE_SOA_DYNAMIC_COLUMN(Decaylength, decaylength, - [](float xvtxd, float yvtxd, float zvtxd, float xvtxp, - float yvtxp, float zvtxp) { return declength(xvtxd, yvtxd, zvtxd, xvtxp, yvtxp, zvtxp); }); -} // namespace hfcandprong2 - -DECLARE_SOA_TABLE(HfTrackIndexProng2, "AOD", "HFTRACKIDXP2", - hftrackindexprong2::CollisionId, - hftrackindexprong2::Index0Id, - hftrackindexprong2::Index1Id, - hftrackindexprong2::HFflag); - -DECLARE_SOA_TABLE(HfTrackIndexProng3, "AOD", "HFTRACKIDXP3", - hftrackindexprong3::CollisionId, - hftrackindexprong3::Index0Id, - hftrackindexprong3::Index1Id, - hftrackindexprong3::Index2Id, - hftrackindexprong3::HFflag); - -DECLARE_SOA_TABLE(HfCandProng2, "AOD", "HFCANDPRONG2", - collision::PosX, collision::PosY, collision::PosZ, - hfcandprong2::PxProng0, hfcandprong2::PyProng0, hfcandprong2::PzProng0, - hfcandprong2::PxProng1, hfcandprong2::PyProng1, hfcandprong2::PzProng1, - hfcandprong2::DecayVtxX, hfcandprong2::DecayVtxY, hfcandprong2::DecayVtxZ, - hfcandprong2::MassD0, hfcandprong2::MassD0bar, - hfcandprong2::PtProng0, - hfcandprong2::PtProng1, - hfcandprong2::Pt, - hfcandprong2::DecaylengthXY, - hfcandprong2::Decaylength); -} // namespace o2::aod - -#endif // O2_ANALYSIS_SECONDARYVERTEXHF_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/CFDerived.h b/Analysis/DataModel/include/AnalysisDataModel/CFDerived.h new file mode 100644 index 0000000000000..71c1f4905b7d3 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/CFDerived.h @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_CFDERIVED_H +#define O2_ANALYSIS_CFDERIVED_H + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "AnalysisDataModel/Centrality.h" + +namespace o2::aod +{ +DECLARE_SOA_TABLE(CFCollisions, "AOD", "CFCOLLISION", o2::soa::Index<>, + o2::aod::bc::RunNumber, o2::aod::collision::PosZ, o2::aod::cent::CentV0M); +using CFCollision = CFCollisions::iterator; + +namespace cftrack +{ +DECLARE_SOA_INDEX_COLUMN(CFCollision, cfCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Charge, charge, int8_t); +} // namespace cftrack +DECLARE_SOA_TABLE(CFTracks, "AOD", "CFTRACK", o2::soa::Index<>, + cftrack::CFCollisionId, + cftrack::Pt, cftrack::Eta, cftrack::Phi, + cftrack::Charge, track::TrackType); +using CFTrack = CFTracks::iterator; +} // namespace o2::aod + +#endif // O2_ANALYSIS_CFDERIVED_H diff --git a/Analysis/DataModel/include/Analysis/Centrality.h b/Analysis/DataModel/include/AnalysisDataModel/Centrality.h similarity index 100% rename from Analysis/DataModel/include/Analysis/Centrality.h rename to Analysis/DataModel/include/AnalysisDataModel/Centrality.h diff --git a/Analysis/DataModel/include/AnalysisDataModel/EMCALClusters.h b/Analysis/DataModel/include/AnalysisDataModel/EMCALClusters.h new file mode 100644 index 0000000000000..a75ad1f8ca00b --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/EMCALClusters.h @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Table definitions for EMCAL analysis clusters +// +// Author: Raymond Ehlers + +#pragma once + +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +namespace emcalcluster +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(Energy, energy, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(M02, m02, float); +} // namespace emcalcluster + +DECLARE_SOA_TABLE(EMCALClusters, "AOD", "EMCALCLUSTERS", + o2::soa::Index<>, emcalcluster::CollisionId, emcalcluster::Energy, + emcalcluster::Eta, emcalcluster::Phi, emcalcluster::M02); + +using EMCALCluster = EMCALClusters::iterator; + +} // namespace o2::aod diff --git a/Analysis/DataModel/include/AnalysisDataModel/EventSelection.h b/Analysis/DataModel/include/AnalysisDataModel/EventSelection.h new file mode 100644 index 0000000000000..ffe5af699d1dd --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/EventSelection.h @@ -0,0 +1,49 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_EVENTSELECTION_H_ +#define O2_ANALYSIS_EVENTSELECTION_H_ + +#include "Framework/AnalysisDataModel.h" +#include "AnalysisCore/TriggerAliases.h" + +namespace o2::aod +{ +namespace evsel +{ +// TODO bool arrays are not supported? Storing in int32 for the moment +DECLARE_SOA_COLUMN(Alias, alias, int32_t[kNaliases]); +DECLARE_SOA_COLUMN(BBT0A, bbT0A, bool); // beam-beam time in T0A +DECLARE_SOA_COLUMN(BBT0C, bbT0C, bool); // beam-beam time in T0C +DECLARE_SOA_COLUMN(BBV0A, bbV0A, bool); // beam-beam time in V0A +DECLARE_SOA_COLUMN(BBV0C, bbV0C, bool); // beam-beam time in V0C +DECLARE_SOA_COLUMN(BGV0A, bgV0A, bool); // beam-gas time in V0A +DECLARE_SOA_COLUMN(BGV0C, bgV0C, bool); // beam-gas time in V0C +DECLARE_SOA_COLUMN(BBZNA, bbZNA, bool); // beam-beam time in ZNA +DECLARE_SOA_COLUMN(BBZNC, bbZNC, bool); // beam-beam time in ZNC +DECLARE_SOA_COLUMN(BBFDA, bbFDA, bool); // beam-beam time in FDA +DECLARE_SOA_COLUMN(BBFDC, bbFDC, bool); // beam-beam time in FDC +DECLARE_SOA_COLUMN(BGFDA, bgFDA, bool); // beam-gas time in FDA +DECLARE_SOA_COLUMN(BGFDC, bgFDC, bool); // beam-gas time in FDC +DECLARE_SOA_COLUMN(FoundFT0, foundFT0, int64_t); // the nearest FT0 signal +DECLARE_SOA_DYNAMIC_COLUMN(SEL7, sel7, [](bool bbV0A, bool bbV0C, bool bbZNA, bool bbZNC) -> bool { return bbV0A && bbV0C && bbZNA && bbZNC; }); +DECLARE_SOA_DYNAMIC_COLUMN(SEL8, sel8, [](bool bbT0A, bool bbT0C, bool bbZNA, bool bbZNC) -> bool { return bbT0A && bbT0C && bbZNA && bbZNC; }); +} // namespace evsel +DECLARE_SOA_TABLE(EvSels, "AOD", "EVSEL", + evsel::Alias, + evsel::BBT0A, evsel::BBT0C, + evsel::BBV0A, evsel::BBV0C, evsel::BGV0A, evsel::BGV0C, evsel::BBZNA, evsel::BBZNC, + evsel::BBFDA, evsel::BBFDC, evsel::BGFDA, evsel::BGFDC, + evsel::SEL7, + evsel::SEL8, + evsel::FoundFT0); +using EvSel = EvSels::iterator; +} // namespace o2::aod + +#endif // O2_ANALYSIS_EVENTSELECTION_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/HFCandidateSelectionTables.h b/Analysis/DataModel/include/AnalysisDataModel/HFCandidateSelectionTables.h new file mode 100644 index 0000000000000..429acaf5e1879 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/HFCandidateSelectionTables.h @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_HFCANDIDATESELECTIONTABLES_H_ +#define O2_ANALYSIS_HFCANDIDATESELECTIONTABLES_H_ + +namespace o2::aod +{ +namespace hf_selcandidate_d0 +{ +DECLARE_SOA_COLUMN(IsSelD0, isSelD0, int); +DECLARE_SOA_COLUMN(IsSelD0bar, isSelD0bar, int); +} // namespace hf_selcandidate_d0 +DECLARE_SOA_TABLE(HFSelD0Candidate, "AOD", "HFSELD0CAND", hf_selcandidate_d0::IsSelD0, hf_selcandidate_d0::IsSelD0bar); +} // namespace o2::aod + +namespace o2::aod +{ +namespace hf_selcandidate_lc +{ +DECLARE_SOA_COLUMN(IsSelLcpKpi, isSelLcpKpi, int); +DECLARE_SOA_COLUMN(IsSelLcpiKp, isSelLcpiKp, int); +} // namespace hf_selcandidate_lc +DECLARE_SOA_TABLE(HFSelLcCandidate, "AOD", "HFSELLCCAND", hf_selcandidate_lc::IsSelLcpKpi, hf_selcandidate_lc::IsSelLcpiKp); +} // namespace o2::aod + +#endif // O2_ANALYSIS_HFCANDIDATESELECTIONTABLES_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/HFSecondaryVertex.h b/Analysis/DataModel/include/AnalysisDataModel/HFSecondaryVertex.h new file mode 100644 index 0000000000000..a45c184044966 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/HFSecondaryVertex.h @@ -0,0 +1,410 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 HFSecondaryVertex.h +/// \brief Definitions of tables of heavy-flavour decay candidates. +/// +/// \author Gian Michele Innocenti , CERN +/// \author Vít Kučera , CERN + +#ifndef O2_ANALYSIS_HFSECONDARYVERTEX_H_ +#define O2_ANALYSIS_HFSECONDARYVERTEX_H_ + +#include "Framework/AnalysisDataModel.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisDataModel/PID/PIDResponse.h" + +namespace o2::aod +{ +namespace hf_seltrack +{ +DECLARE_SOA_COLUMN(IsSelProng, isSelProng, int); +DECLARE_SOA_COLUMN(DCAPrim0, dcaPrim0, float); +DECLARE_SOA_COLUMN(DCAPrim1, dcaPrim1, float); +} // namespace hf_seltrack + +DECLARE_SOA_TABLE(HFSelTrack, "AOD", "HFSELTRACK", + hf_seltrack::IsSelProng, + hf_seltrack::DCAPrim0, + hf_seltrack::DCAPrim1); + +using BigTracks = soa::Join; +using BigTracksMC = soa::Join; +using BigTracksPID = soa::Join; + +// FIXME: this is a workaround until we get the index columns to work with joins. + +namespace hf_track_index +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Index0, index0, int, BigTracks, "fIndex0"); +DECLARE_SOA_INDEX_COLUMN_FULL(Index1, index1, int, BigTracks, "fIndex1"); +DECLARE_SOA_INDEX_COLUMN_FULL(Index2, index2, int, BigTracks, "fIndex2"); +DECLARE_SOA_INDEX_COLUMN_FULL(Index3, index3, int, BigTracks, "fIndex3"); +DECLARE_SOA_COLUMN(HFflag, hfflag, uint8_t); + +DECLARE_SOA_COLUMN(D0ToKPiFlag, d0ToKPiFlag, uint8_t); +DECLARE_SOA_COLUMN(JpsiToEEFlag, jpsiToEEFlag, uint8_t); + +DECLARE_SOA_COLUMN(DPlusPiKPiFlag, dPlusPiKPiFlag, uint8_t); +DECLARE_SOA_COLUMN(LcPKPiFlag, lcPKPiFlag, uint8_t); +DECLARE_SOA_COLUMN(DsKKPiFlag, dsKKPiFlag, uint8_t); +} // namespace hf_track_index + +DECLARE_SOA_TABLE(HfTrackIndexProng2, "AOD", "HFTRACKIDXP2", + hf_track_index::Index0Id, + hf_track_index::Index1Id, + hf_track_index::HFflag); + +DECLARE_SOA_TABLE(HfCutStatusProng2, "AOD", "HFCUTSTATUSP2", + hf_track_index::D0ToKPiFlag, + hf_track_index::JpsiToEEFlag); + +DECLARE_SOA_TABLE(HfTrackIndexProng3, "AOD", "HFTRACKIDXP3", + hf_track_index::Index0Id, + hf_track_index::Index1Id, + hf_track_index::Index2Id, + hf_track_index::HFflag); + +DECLARE_SOA_TABLE(HfCutStatusProng3, "AOD", "HFCUTSTATUSP3", + hf_track_index::DPlusPiKPiFlag, + hf_track_index::LcPKPiFlag, + hf_track_index::DsKKPiFlag); + +// general decay properties +namespace hf_cand +{ +// secondary vertex +DECLARE_SOA_COLUMN(XSecondaryVertex, xSecondaryVertex, float); +DECLARE_SOA_COLUMN(YSecondaryVertex, ySecondaryVertex, float); +DECLARE_SOA_COLUMN(ZSecondaryVertex, zSecondaryVertex, float); +DECLARE_SOA_DYNAMIC_COLUMN(RSecondaryVertex, rSecondaryVertex, [](float xVtxS, float yVtxS) { return RecoDecay::sqrtSumOfSquares(xVtxS, yVtxS); }); +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); // sum of (non-weighted) distances of the secondary vertex to its prongs +// prong properties +DECLARE_SOA_COLUMN(PxProng0, pxProng0, float); +DECLARE_SOA_COLUMN(PyProng0, pyProng0, float); +DECLARE_SOA_COLUMN(PzProng0, pzProng0, float); +DECLARE_SOA_DYNAMIC_COLUMN(PtProng0, ptProng0, [](float px, float py) { return RecoDecay::Pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pt2Prong0, pt2Prong0, [](float px, float py) { return RecoDecay::Pt2(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(PVectorProng0, pVectorProng0, [](float px, float py, float pz) { return array{px, py, pz}; }); +DECLARE_SOA_COLUMN(ImpactParameter0, impactParameter0, float); +DECLARE_SOA_COLUMN(ErrorImpactParameter0, errorImpactParameter0, float); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterNormalised0, impactParameterNormalised0, [](float dca, float err) { return dca / err; }); +DECLARE_SOA_COLUMN(PxProng1, pxProng1, float); +DECLARE_SOA_COLUMN(PyProng1, pyProng1, float); +DECLARE_SOA_COLUMN(PzProng1, pzProng1, float); +DECLARE_SOA_DYNAMIC_COLUMN(PtProng1, ptProng1, [](float px, float py) { return RecoDecay::Pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pt2Prong1, pt2Prong1, [](float px, float py) { return RecoDecay::Pt2(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(PVectorProng1, pVectorProng1, [](float px, float py, float pz) { return array{px, py, pz}; }); +DECLARE_SOA_COLUMN(ImpactParameter1, impactParameter1, float); +DECLARE_SOA_COLUMN(ErrorImpactParameter1, errorImpactParameter1, float); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterNormalised1, impactParameterNormalised1, [](float dca, float err) { return dca / err; }); +DECLARE_SOA_COLUMN(PxProng2, pxProng2, float); +DECLARE_SOA_COLUMN(PyProng2, pyProng2, float); +DECLARE_SOA_COLUMN(PzProng2, pzProng2, float); +DECLARE_SOA_DYNAMIC_COLUMN(PtProng2, ptProng2, [](float px, float py) { return RecoDecay::Pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pt2Prong2, pt2Prong2, [](float px, float py) { return RecoDecay::Pt2(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(PVectorProng2, pVectorProng2, [](float px, float py, float pz) { return array{px, py, pz}; }); +DECLARE_SOA_COLUMN(ImpactParameter2, impactParameter2, float); +DECLARE_SOA_COLUMN(ErrorImpactParameter2, errorImpactParameter2, float); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterNormalised2, impactParameterNormalised2, [](float dca, float err) { return dca / err; }); +// candidate properties +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float px, float py) { return RecoDecay::Pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pt2, pt2, [](float px, float py) { return RecoDecay::Pt2(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float px, float py, float pz) { return RecoDecay::P(px, py, pz); }); +DECLARE_SOA_DYNAMIC_COLUMN(P2, p2, [](float px, float py, float pz) { return RecoDecay::P2(px, py, pz); }); +DECLARE_SOA_DYNAMIC_COLUMN(PVector, pVector, [](float px, float py, float pz) { return array{px, py, pz}; }); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float px, float py, float pz) { return RecoDecay::Eta(array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float px, float py) { return RecoDecay::Phi(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(Y, y, [](float px, float py, float pz, double m) { return RecoDecay::Y(array{px, py, pz}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(E, e, [](float px, float py, float pz, double m) { return RecoDecay::E(px, py, pz, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(E2, e2, [](float px, float py, float pz, double m) { return RecoDecay::E2(px, py, pz, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLength, decayLength, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS) { return RecoDecay::distance(array{xVtxP, yVtxP, zVtxP}, array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthXY, decayLengthXY, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS) { return RecoDecay::distanceXY(array{xVtxP, yVtxP}, array{xVtxS, yVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthNormalised, decayLengthNormalised, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float err) { return RecoDecay::distance(array{xVtxP, yVtxP, zVtxP}, array{xVtxS, yVtxS, zVtxS}) / err; }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float err) { return RecoDecay::distanceXY(array{xVtxP, yVtxP}, array{xVtxS, yVtxS}) / err; }); +DECLARE_SOA_COLUMN(ErrorDecayLength, errorDecayLength, float); +DECLARE_SOA_COLUMN(ErrorDecayLengthXY, errorDecayLengthXY, float); +DECLARE_SOA_DYNAMIC_COLUMN(CPA, cpa, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) { return RecoDecay::CPA(array{xVtxP, yVtxP, zVtxP}, array{xVtxS, yVtxS, zVtxS}, array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CPAXY, cpaXY, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px, float py) { return RecoDecay::CPAXY(array{xVtxP, yVtxP}, array{xVtxS, yVtxS}, array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(Ct, ct, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz, double m) { return RecoDecay::Ct(array{px, py, pz}, RecoDecay::distance(array{xVtxP, yVtxP, zVtxP}, array{xVtxS, yVtxS, zVtxS}), m); }); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterXY, impactParameterXY, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) { return RecoDecay::ImpParXY(array{xVtxP, yVtxP, zVtxP}, array{xVtxS, yVtxS, zVtxS}, array{px, py, pz}); }); +} // namespace hf_cand + +// specific 2-prong decay properties +namespace hf_cand_prong2 +{ +DECLARE_SOA_EXPRESSION_COLUMN(Px, px, float, 1.f * aod::hf_cand::pxProng0 + 1.f * aod::hf_cand::pxProng1); +DECLARE_SOA_EXPRESSION_COLUMN(Py, py, float, 1.f * aod::hf_cand::pyProng0 + 1.f * aod::hf_cand::pyProng1); +DECLARE_SOA_EXPRESSION_COLUMN(Pz, pz, float, 1.f * aod::hf_cand::pzProng0 + 1.f * aod::hf_cand::pzProng1); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProduct, impactParameterProduct, [](float dca1, float dca2) { return dca1 * dca2; }); +DECLARE_SOA_DYNAMIC_COLUMN(M, m, [](float px0, float py0, float pz0, float px1, float py1, float pz1, const array& m) { return RecoDecay::M(array{array{px0, py0, pz0}, array{px1, py1, pz1}}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(M2, m2, [](float px0, float py0, float pz0, float px1, float py1, float pz1, const array& m) { return RecoDecay::M2(array{array{px0, py0, pz0}, array{px1, py1, pz1}}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(CosThetaStar, cosThetaStar, [](float px0, float py0, float pz0, float px1, float py1, float pz1, const array& m, double mTot, int iProng) { return RecoDecay::CosThetaStar(array{array{px0, py0, pz0}, array{px1, py1, pz1}}, m, mTot, iProng); }); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProngSqSum, impactParameterProngSqSum, [](float impParProng0, float impParProng1) { return RecoDecay::sumOfSquares(impParProng0, impParProng1); }); +DECLARE_SOA_DYNAMIC_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float errDlxy, float pxM, float pyM, float ip0, float errIp0, float ip1, float errIp1, float px0, float py0, float px1, float py1) { return RecoDecay::maxNormalisedDeltaIP(array{xVtxP, yVtxP}, array{xVtxS, yVtxS}, errDlxy, array{pxM, pyM}, array{ip0, ip1}, array{errIp0, errIp1}, array{array{px0, py0}, array{px1, py1}}); }); +// MC matching result: +// - ±D0ToPiK: D0(bar) → π± K∓ +DECLARE_SOA_COLUMN(FlagMCMatchRec, flagMCMatchRec, int8_t); // reconstruction level +DECLARE_SOA_COLUMN(FlagMCMatchGen, flagMCMatchGen, int8_t); // generator level + +// mapping of decay types +enum DecayType { D0ToPiK = 1 }; + +// functions for specific particles + +// D0(bar) → π± K∓ + +template +auto CtD0(const T& candidate) +{ + return candidate.ct(RecoDecay::getMassPDG(421)); +} + +template +auto YD0(const T& candidate) +{ + return candidate.y(RecoDecay::getMassPDG(421)); +} + +template +auto ED0(const T& candidate) +{ + return candidate.e(RecoDecay::getMassPDG(421)); +} + +template +auto InvMassD0(const T& candidate) +{ + return candidate.m(array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kKPlus)}); +} + +template +auto InvMassD0bar(const T& candidate) +{ + return candidate.m(array{RecoDecay::getMassPDG(kKPlus), RecoDecay::getMassPDG(kPiPlus)}); +} + +template +auto CosThetaStarD0(const T& candidate) +{ + return candidate.cosThetaStar(array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kKPlus)}, RecoDecay::getMassPDG(421), 1); +} + +template +auto CosThetaStarD0bar(const T& candidate) +{ + return candidate.cosThetaStar(array{RecoDecay::getMassPDG(kKPlus), RecoDecay::getMassPDG(kPiPlus)}, RecoDecay::getMassPDG(421), 0); +} +} // namespace hf_cand_prong2 + +// general columns +#define HFCAND_COLUMNS \ + collision::PosX, collision::PosY, collision::PosZ, \ + hf_cand::XSecondaryVertex, hf_cand::YSecondaryVertex, hf_cand::ZSecondaryVertex, \ + hf_cand::ErrorDecayLength, hf_cand::ErrorDecayLengthXY, \ + hf_cand::Chi2PCA, \ + /* dynamic columns */ hf_cand::RSecondaryVertex, \ + hf_cand::DecayLength, \ + hf_cand::DecayLengthXY, \ + hf_cand::DecayLengthNormalised, \ + hf_cand::DecayLengthXYNormalised, \ + /* prong 0 */ hf_cand::ImpactParameterNormalised0, \ + hf_cand::PtProng0, \ + hf_cand::Pt2Prong0, \ + hf_cand::PVectorProng0, \ + /* prong 1 */ hf_cand::ImpactParameterNormalised1, \ + hf_cand::PtProng1, \ + hf_cand::Pt2Prong1, \ + hf_cand::PVectorProng1 + +// 2-prong decay candidate table +DECLARE_SOA_TABLE(HfCandProng2Base, "AOD", "HFCANDP2BASE", + // general columns + HFCAND_COLUMNS, + // 2-prong specific columns + hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, + hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, + hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, + hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + hf_track_index::Index0Id, hf_track_index::Index1Id, + hf_track_index::HFflag, + /* dynamic columns */ + hf_cand_prong2::M, + hf_cand_prong2::M2, + hf_cand_prong2::ImpactParameterProduct, + hf_cand_prong2::CosThetaStar, + hf_cand_prong2::ImpactParameterProngSqSum, + /* dynamic columns that use candidate momentum components */ + hf_cand::Pt, + hf_cand::Pt2, + hf_cand::P, + hf_cand::P2, + hf_cand::PVector, + hf_cand::CPA, + hf_cand::CPAXY, + hf_cand::Ct, + hf_cand::ImpactParameterXY, + hf_cand_prong2::MaxNormalisedDeltaIP, + hf_cand::Eta, + hf_cand::Phi, + hf_cand::Y, + hf_cand::E, + hf_cand::E2); + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(HfCandProng2Ext, HfCandProng2Base, "HFCANDP2EXT", + hf_cand_prong2::Px, hf_cand_prong2::Py, hf_cand_prong2::Pz); + +using HfCandProng2 = HfCandProng2Ext; + +// table with results of reconstruction level MC matching +DECLARE_SOA_TABLE(HfCandProng2MCRec, "AOD", "HFCANDP2MCREC", + hf_cand_prong2::FlagMCMatchRec); + +// table with results of generator level MC matching +DECLARE_SOA_TABLE(HfCandProng2MCGen, "AOD", "HFCANDP2MCGEN", + hf_cand_prong2::FlagMCMatchGen); + +// specific 3-prong decay properties +namespace hf_cand_prong3 +{ +DECLARE_SOA_EXPRESSION_COLUMN(Px, px, float, 1.f * aod::hf_cand::pxProng0 + 1.f * aod::hf_cand::pxProng1 + 1.f * aod::hf_cand::pxProng2); +DECLARE_SOA_EXPRESSION_COLUMN(Py, py, float, 1.f * aod::hf_cand::pyProng0 + 1.f * aod::hf_cand::pyProng1 + 1.f * aod::hf_cand::pyProng2); +DECLARE_SOA_EXPRESSION_COLUMN(Pz, pz, float, 1.f * aod::hf_cand::pzProng0 + 1.f * aod::hf_cand::pzProng1 + 1.f * aod::hf_cand::pzProng2); +DECLARE_SOA_DYNAMIC_COLUMN(M, m, [](float px0, float py0, float pz0, float px1, float py1, float pz1, float px2, float py2, float pz2, const array& m) { return RecoDecay::M(array{array{px0, py0, pz0}, array{px1, py1, pz1}, array{px2, py2, pz2}}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(M2, m2, [](float px0, float py0, float pz0, float px1, float py1, float pz1, float px2, float py2, float pz2, const array& m) { return RecoDecay::M2(array{array{px0, py0, pz0}, array{px1, py1, pz1}, array{px2, py2, pz2}}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProngSqSum, impactParameterProngSqSum, [](float impParProng0, float impParProng1, float impParProng2) { return RecoDecay::sumOfSquares(impParProng0, impParProng1, impParProng2); }); +DECLARE_SOA_DYNAMIC_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float errDlxy, float pxM, float pyM, float ip0, float errIp0, float ip1, float errIp1, float ip2, float errIp2, float px0, float py0, float px1, float py1, float px2, float py2) { return RecoDecay::maxNormalisedDeltaIP(array{xVtxP, yVtxP}, array{xVtxS, yVtxS}, errDlxy, array{pxM, pyM}, array{ip0, ip1, ip2}, array{errIp0, errIp1, errIp2}, array{array{px0, py0}, array{px1, py1}, array{px2, py2}}); }); +// MC matching result: +// - ±DPlusToPiKPi: D± → π± K∓ π± +// - ±LcToPKPi: Λc± → p± K∓ π± +DECLARE_SOA_COLUMN(FlagMCMatchRec, flagMCMatchRec, int8_t); // reconstruction level +DECLARE_SOA_COLUMN(FlagMCMatchGen, flagMCMatchGen, int8_t); // generator level + +// mapping of decay types +enum DecayType { DPlusToPiKPi = 1, + LcToPKPi }; + +// functions for specific particles + +// D± → π± K∓ π± + +template +auto CtDPlus(const T& candidate) +{ + return candidate.ct(RecoDecay::getMassPDG(411)); +} + +template +auto YDPlus(const T& candidate) +{ + return candidate.y(RecoDecay::getMassPDG(411)); +} + +template +auto EDPlus(const T& candidate) +{ + return candidate.e(RecoDecay::getMassPDG(411)); +} + +template +auto InvMassDPlus(const T& candidate) +{ + return candidate.m(array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kKPlus), RecoDecay::getMassPDG(kPiPlus)}); +} + +// Λc± → p± K∓ π± + +template +auto CtLc(const T& candidate) +{ + return candidate.ct(RecoDecay::getMassPDG(4122)); +} + +template +auto YLc(const T& candidate) +{ + return candidate.y(RecoDecay::getMassPDG(4122)); +} + +template +auto ELc(const T& candidate) +{ + return candidate.e(RecoDecay::getMassPDG(4122)); +} + +template +auto InvMassLcpKpi(const T& candidate) +{ + return candidate.m(array{RecoDecay::getMassPDG(kProton), RecoDecay::getMassPDG(kKPlus), RecoDecay::getMassPDG(kPiPlus)}); +} + +template +auto InvMassLcpiKp(const T& candidate) +{ + return candidate.m(array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kKPlus), RecoDecay::getMassPDG(kProton)}); +} +} // namespace hf_cand_prong3 + +// 3-prong decay candidate table +DECLARE_SOA_TABLE(HfCandProng3Base, "AOD", "HFCANDP3BASE", + // general columns + HFCAND_COLUMNS, + // 3-prong specific columns + hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, + hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, + hf_cand::PxProng2, hf_cand::PyProng2, hf_cand::PzProng2, + hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ImpactParameter2, + hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, hf_cand::ErrorImpactParameter2, + hf_track_index::Index0Id, hf_track_index::Index1Id, hf_track_index::Index2Id, + hf_track_index::HFflag, + /* dynamic columns */ + hf_cand_prong3::M, + hf_cand_prong3::M2, + hf_cand_prong3::ImpactParameterProngSqSum, + /* prong 2 */ + hf_cand::ImpactParameterNormalised2, + hf_cand::PtProng2, + hf_cand::Pt2Prong2, + hf_cand::PVectorProng2, + /* dynamic columns that use candidate momentum components */ + hf_cand::Pt, + hf_cand::Pt2, + hf_cand::P, + hf_cand::P2, + hf_cand::PVector, + hf_cand::CPA, + hf_cand::CPAXY, + hf_cand::Ct, + hf_cand::ImpactParameterXY, + hf_cand_prong3::MaxNormalisedDeltaIP, + hf_cand::Eta, + hf_cand::Phi, + hf_cand::Y, + hf_cand::E, + hf_cand::E2); + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(HfCandProng3Ext, HfCandProng3Base, "HFCANDP3EXT", + hf_cand_prong3::Px, hf_cand_prong3::Py, hf_cand_prong3::Pz); + +using HfCandProng3 = HfCandProng3Ext; + +// table with results of reconstruction level MC matching +DECLARE_SOA_TABLE(HfCandProng3MCRec, "AOD", "HFCANDP3MCREC", + hf_cand_prong3::FlagMCMatchRec); + +// table with results of generator level MC matching +DECLARE_SOA_TABLE(HfCandProng3MCGen, "AOD", "HFCANDP3MCGEN", + hf_cand_prong3::FlagMCMatchGen); + +} // namespace o2::aod + +#endif // O2_ANALYSIS_HFSECONDARYVERTEX_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/Jet.h b/Analysis/DataModel/include/AnalysisDataModel/Jet.h new file mode 100644 index 0000000000000..6f36198fb58af --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/Jet.h @@ -0,0 +1,92 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// table definitions for jets +// +// Author: Jochen Klein, Nima Zardoshti + +#pragma once + +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +namespace jet +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Energy, energy, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) { return pt * TMath::Cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) { return pt * TMath::Sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) { return pt * TMath::SinH(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float pt, float eta) { return pt * TMath::CosH(eta); }); //absolute p +} // namespace jet + +DECLARE_SOA_TABLE(Jets, "AOD", "JET", + o2::soa::Index<>, + jet::CollisionId, + jet::Pt, + jet::Eta, + jet::Phi, + jet::Energy, + jet::Mass, + jet::Area, + jet::Px, + jet::Py, + jet::Pz, + jet::P); + +using Jet = Jets::iterator; + +// TODO: absorb in jet table +// when list of references available +namespace constituents +{ +DECLARE_SOA_INDEX_COLUMN(Jet, jet); +DECLARE_SOA_INDEX_COLUMN(Track, track); +} // namespace constituents + +DECLARE_SOA_TABLE(JetConstituents, "AOD", "CONSTITUENTS", + constituents::JetId, + constituents::TrackId); + +using JetConstituent = JetConstituents::iterator; + +namespace constituentssub +{ +DECLARE_SOA_INDEX_COLUMN(Jet, jet); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Energy, energy, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) { return pt * TMath::Cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) { return pt * TMath::Sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) { return pt * TMath::SinH(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float pt, float eta) { return pt * TMath::CosH(eta); }); //absolute p +} //namespace constituentssub +DECLARE_SOA_TABLE(JetConstituentsSub, "AOD", "CONSTITUENTSSUB", + constituentssub::JetId, + constituentssub::Pt, + constituentssub::Eta, + constituentssub::Phi, + constituentssub::Energy, + constituentssub::Mass, + constituentssub::Px, + constituentssub::Py, + constituentssub::Pz, + constituentssub::P); +using JetConstituentSub = JetConstituentsSub::iterator; + +} // namespace o2::aod diff --git a/Analysis/DataModel/include/AnalysisDataModel/Multiplicity.h b/Analysis/DataModel/include/AnalysisDataModel/Multiplicity.h new file mode 100644 index 0000000000000..bb4b4aadccf51 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/Multiplicity.h @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_MULTIPLICITY_H_ +#define O2_ANALYSIS_MULTIPLICITY_H_ + +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +namespace mult +{ +DECLARE_SOA_COLUMN(MultV0A, multV0A, float); +DECLARE_SOA_COLUMN(MultV0C, multV0C, float); +DECLARE_SOA_COLUMN(MultT0A, multT0A, float); +DECLARE_SOA_COLUMN(MultT0C, multT0C, float); +DECLARE_SOA_COLUMN(MultZNA, multZNA, float); +DECLARE_SOA_COLUMN(MultZNC, multZNC, float); +DECLARE_SOA_DYNAMIC_COLUMN(MultV0M, multV0M, [](float multV0A, float multV0C) -> float { return multV0A + multV0C; }); +DECLARE_SOA_DYNAMIC_COLUMN(MultT0M, multT0M, [](float multT0A, float multT0C) -> float { return multT0A + multT0C; }); +DECLARE_SOA_COLUMN(MultTracklets, multTracklets, int); + +} // namespace mult +DECLARE_SOA_TABLE(Mults, "AOD", "MULT", mult::MultV0A, mult::MultV0C, mult::MultT0A, mult::MultT0C, mult::MultZNA, mult::MultZNC, mult::MultV0M, mult::MultT0M, mult::MultTracklets); +using Mult = Mults::iterator; +} // namespace o2::aod + +#endif // O2_ANALYSIS_MULTIPLICITY_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/BetheBloch.h b/Analysis/DataModel/include/AnalysisDataModel/PID/BetheBloch.h new file mode 100644 index 0000000000000..37affbc22e91f --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/BetheBloch.h @@ -0,0 +1,41 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 BetheBloch.h +/// \author Nicolo' Jacazio +/// \since 07/08/2020 +/// \brief Implementation for the TPC PID response of the BB parametrization +/// + +#ifndef O2_ANALYSIS_PID_BETHEBLOCH_H_ +#define O2_ANALYSIS_PID_BETHEBLOCH_H_ + +#include "TPCSimulation/Detector.h" +#include "AnalysisDataModel/PID/ParamBase.h" + +namespace o2::pid::tpc +{ + +class BetheBloch : public Parametrization +{ + public: + BetheBloch() : Parametrization("BetheBloch", 7){}; + ~BetheBloch() override = default; + float operator()(const float* x) const override + { + return mParameters[5] * o2::tpc::Detector::BetheBlochAleph(x[0], mParameters[0], mParameters[1], mParameters[2], mParameters[3], mParameters[4]) * TMath::Power(x[1], mParameters[6]); + } + ClassDef(BetheBloch, 1); +}; + +} // namespace o2::pid::tpc + +#endif diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/DetectorResponse.h b/Analysis/DataModel/include/AnalysisDataModel/PID/DetectorResponse.h new file mode 100644 index 0000000000000..8e4bd405cd164 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/DetectorResponse.h @@ -0,0 +1,105 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DetectorResponse.h +/// \author Nicolo' Jacazio +/// \since 2020-07-30 +/// \brief Handler for any detector (or other entity) response. +/// This provides the basic quantities computed by any response i.e. expected values, resolutions and Nsigmas +/// + +#ifndef O2_ANALYSIS_PID_DETECTORRESPONSE_H_ +#define O2_ANALYSIS_PID_DETECTORRESPONSE_H_ + +#include +#include +#include "Framework/Logger.h" +// ROOT includes +#include "Rtypes.h" +#include "TMath.h" +#include "TFile.h" + +// O2 includes +#include "ReconstructionDataFormats/PID.h" +#include "AnalysisDataModel/PID/ParamBase.h" + +namespace o2::pid +{ +/// \brief Class to handle the general detector response +class DetectorResponse +{ + public: + DetectorResponse() = default; + virtual ~DetectorResponse() = default; + + /// Enumeration of the different types of parametrizations + enum Param_t { kSignal, + kSigma, + kNParams }; + + static constexpr std::array ParamName = {{"Signal", "Sigma"}}; + + /// Setter for the parametrization from input TFile + /// \param fname File name used for input + /// \param pname Name of the parametrization in the file + /// \param ptype Type of the parametrization + void LoadParamFromFile(const TString fname, const TString pname, const Param_t ptype); + + /// Setter for the parametrization + /// \param ptype Type of the parametrization + /// \param param Parametrization + void LoadParam(const Param_t ptype, Parametrization* param) { mParam[ptype] = param; } + + /// Getter for the parametrizations + Parametrization* GetParam(const Param_t ptype) const { return mParam[ptype]; } + + /// Setter for the parametrizations parameters, if the parametrization is not yet initialized a new parametrization is created without any implementation and just parameters + /// \param ptype parametrization type + /// \param p vector with parameters + void SetParameters(const Param_t ptype, std::vector p); + + /// Getter for the value of the parametrization + /// \param ptype parametrization type + /// \param x array with parameters + virtual pidvar_t operator()(const Param_t ptype, const pidvar_t* x) const { return mParam[ptype]->operator()(x); } + + private: + /// Parametrizations for the expected signal and sigma + std::array mParam; +}; + +inline void DetectorResponse::LoadParamFromFile(const TString fname, const TString pname, const Param_t ptype) +{ + TFile f(fname, "READ"); + if (!f.Get(pname)) { + LOG(fatal) << "Did not find parametrization " << pname << " in file " << fname; + } + LOG(info) << "Loading parametrization " << pname << " from TFile " << fname; + f.GetObject(pname, mParam[ptype]); + f.Close(); + mParam[ptype]->Print(); + mParam[ptype]->PrintParametrization(); +} + +inline void DetectorResponse::SetParameters(const DetectorResponse::Param_t ptype, std::vector p) +{ + if (!mParam[ptype]) { + const std::string pname = std::string(ParamName[ptype]) + "_default_param"; + LOG(info) << "Creating new parametrization " << pname << " of size " << p.size(); + mParam[ptype] = new Parametrization(pname, p.size()); + mParam[ptype]->Print(); + } + mParam[ptype]->SetParameters(p); +} + +} // namespace o2::pid + +#endif // O2_ANALYSIS_PID_DETECTORRESPONSE_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/PIDResponse.h b/Analysis/DataModel/include/AnalysisDataModel/PID/PIDResponse.h new file mode 100644 index 0000000000000..9b9395e084a85 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/PIDResponse.h @@ -0,0 +1,161 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Nicolo' Jacazio +/// \brief Set of tables, tasks and utilities to provide the interface between +/// the analysis data model and the PID response +/// + +#ifndef O2_FRAMEWORK_PIDRESPONSE_H_ +#define O2_FRAMEWORK_PIDRESPONSE_H_ + +// O2 includes +#include "Framework/ASoA.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/PID.h" + +namespace o2::aod +{ + +namespace pidtofbeta +{ +DECLARE_SOA_COLUMN(Beta, beta, float); +DECLARE_SOA_COLUMN(BetaError, betaerror, float); +// +DECLARE_SOA_COLUMN(ExpBetaEl, expbetael, float); +DECLARE_SOA_COLUMN(ExpBetaElError, expbetaelerror, float); +// +DECLARE_SOA_COLUMN(SeparationBetaEl, separationbetael, float); +DECLARE_SOA_DYNAMIC_COLUMN(DiffBetaEl, diffbetael, [](float beta, float expbetael) -> float { return beta - expbetael; }); +} // namespace pidtofbeta + +namespace pidtof +{ +// Expected times +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffEl, tofExpSignalDiffEl, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffMu, tofExpSignalDiffMu, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffPi, tofExpSignalDiffPi, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffKa, tofExpSignalDiffKa, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffPr, tofExpSignalDiffPr, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffDe, tofExpSignalDiffDe, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffTr, tofExpSignalDiffTr, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffHe, tofExpSignalDiffHe, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffAl, tofExpSignalDiffAl, [](float nsigma, float sigma) { return nsigma * sigma; }); +// Expected sigma +DECLARE_SOA_COLUMN(TOFExpSigmaEl, tofExpSigmaEl, float); +DECLARE_SOA_COLUMN(TOFExpSigmaMu, tofExpSigmaMu, float); +DECLARE_SOA_COLUMN(TOFExpSigmaPi, tofExpSigmaPi, float); +DECLARE_SOA_COLUMN(TOFExpSigmaKa, tofExpSigmaKa, float); +DECLARE_SOA_COLUMN(TOFExpSigmaPr, tofExpSigmaPr, float); +DECLARE_SOA_COLUMN(TOFExpSigmaDe, tofExpSigmaDe, float); +DECLARE_SOA_COLUMN(TOFExpSigmaTr, tofExpSigmaTr, float); +DECLARE_SOA_COLUMN(TOFExpSigmaHe, tofExpSigmaHe, float); +DECLARE_SOA_COLUMN(TOFExpSigmaAl, tofExpSigmaAl, float); +// NSigma +DECLARE_SOA_COLUMN(TOFNSigmaEl, tofNSigmaEl, float); +DECLARE_SOA_COLUMN(TOFNSigmaMu, tofNSigmaMu, float); +DECLARE_SOA_COLUMN(TOFNSigmaPi, tofNSigmaPi, float); +DECLARE_SOA_COLUMN(TOFNSigmaKa, tofNSigmaKa, float); +DECLARE_SOA_COLUMN(TOFNSigmaPr, tofNSigmaPr, float); +DECLARE_SOA_COLUMN(TOFNSigmaDe, tofNSigmaDe, float); +DECLARE_SOA_COLUMN(TOFNSigmaTr, tofNSigmaTr, float); +DECLARE_SOA_COLUMN(TOFNSigmaHe, tofNSigmaHe, float); +DECLARE_SOA_COLUMN(TOFNSigmaAl, tofNSigmaAl, float); +} // namespace pidtof + +using namespace pidtofbeta; +DECLARE_SOA_TABLE(pidRespTOFbeta, "AOD", "pidRespTOFbeta", + Beta, BetaError, + ExpBetaEl, ExpBetaElError, + SeparationBetaEl, + DiffBetaEl); +using namespace pidtof; +DECLARE_SOA_TABLE(pidRespTOF, "AOD", "pidRespTOF", + // Expected signals + TOFExpSignalDiffEl, + TOFExpSignalDiffMu, + TOFExpSignalDiffPi, + TOFExpSignalDiffKa, + TOFExpSignalDiffPr, + TOFExpSignalDiffDe, + TOFExpSignalDiffTr, + TOFExpSignalDiffHe, + TOFExpSignalDiffAl, + // Expected sigma + TOFExpSigmaEl, TOFExpSigmaMu, TOFExpSigmaPi, + TOFExpSigmaKa, TOFExpSigmaPr, TOFExpSigmaDe, + TOFExpSigmaTr, TOFExpSigmaHe, TOFExpSigmaAl, + // NSigma + TOFNSigmaEl, TOFNSigmaMu, TOFNSigmaPi, + TOFNSigmaKa, TOFNSigmaPr, TOFNSigmaDe, + TOFNSigmaTr, TOFNSigmaHe, TOFNSigmaAl); + +namespace pidtpc +{ +// Expected signals +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffEl, tpcExpSignalDiffEl, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffMu, tpcExpSignalDiffMu, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffPi, tpcExpSignalDiffPi, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffKa, tpcExpSignalDiffKa, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffPr, tpcExpSignalDiffPr, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffDe, tpcExpSignalDiffDe, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffTr, tpcExpSignalDiffTr, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffHe, tpcExpSignalDiffHe, [](float nsigma, float sigma) { return nsigma * sigma; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffAl, tpcExpSignalDiffAl, [](float nsigma, float sigma) { return nsigma * sigma; }); +// Expected sigma +DECLARE_SOA_COLUMN(TPCExpSigmaEl, tpcExpSigmaEl, float); +DECLARE_SOA_COLUMN(TPCExpSigmaMu, tpcExpSigmaMu, float); +DECLARE_SOA_COLUMN(TPCExpSigmaPi, tpcExpSigmaPi, float); +DECLARE_SOA_COLUMN(TPCExpSigmaKa, tpcExpSigmaKa, float); +DECLARE_SOA_COLUMN(TPCExpSigmaPr, tpcExpSigmaPr, float); +DECLARE_SOA_COLUMN(TPCExpSigmaDe, tpcExpSigmaDe, float); +DECLARE_SOA_COLUMN(TPCExpSigmaTr, tpcExpSigmaTr, float); +DECLARE_SOA_COLUMN(TPCExpSigmaHe, tpcExpSigmaHe, float); +DECLARE_SOA_COLUMN(TPCExpSigmaAl, tpcExpSigmaAl, float); +// NSigma +DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, float); +DECLARE_SOA_COLUMN(TPCNSigmaMu, tpcNSigmaMu, float); +DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, float); +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, float); +DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, float); +DECLARE_SOA_COLUMN(TPCNSigmaDe, tpcNSigmaDe, float); +DECLARE_SOA_COLUMN(TPCNSigmaTr, tpcNSigmaTr, float); +DECLARE_SOA_COLUMN(TPCNSigmaHe, tpcNSigmaHe, float); +DECLARE_SOA_COLUMN(TPCNSigmaAl, tpcNSigmaAl, float); +} // namespace pidtpc + +using namespace pidtpc; +DECLARE_SOA_TABLE(pidRespTPC, "AOD", "pidRespTPC", + // Expected signals + TPCExpSignalDiffEl, + TPCExpSignalDiffMu, + TPCExpSignalDiffPi, + TPCExpSignalDiffKa, + TPCExpSignalDiffPr, + TPCExpSignalDiffDe, + TPCExpSignalDiffTr, + TPCExpSignalDiffHe, + TPCExpSignalDiffAl, + // Expected sigma + TPCExpSigmaEl, TPCExpSigmaMu, TPCExpSigmaPi, + TPCExpSigmaKa, TPCExpSigmaPr, TPCExpSigmaDe, + TPCExpSigmaTr, TPCExpSigmaHe, TPCExpSigmaAl, + // NSigma + TPCNSigmaEl, TPCNSigmaMu, TPCNSigmaPi, + TPCNSigmaKa, TPCNSigmaPr, TPCNSigmaDe, + TPCNSigmaTr, TPCNSigmaHe, TPCNSigmaAl); + +} // namespace o2::aod + +#endif // O2_FRAMEWORK_PIDRESPONSE_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/PIDTOF.h b/Analysis/DataModel/include/AnalysisDataModel/PID/PIDTOF.h new file mode 100644 index 0000000000000..5b1d43848c9d3 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/PIDTOF.h @@ -0,0 +1,141 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PIDTOF.h +/// \author Nicolo' Jacazio +/// \since 02/07/2020 +/// \brief Implementation of the TOF detector response for PID +/// + +#ifndef O2_FRAMEWORK_PIDTOF_H_ +#define O2_FRAMEWORK_PIDTOF_H_ + +// ROOT includes +#include "Rtypes.h" +#include "TMath.h" + +// O2 includes +#include "Framework/Logger.h" +#include "ReconstructionDataFormats/PID.h" +#include "AnalysisDataModel/PID/DetectorResponse.h" + +namespace o2::pid::tof +{ + +// Utility values +static constexpr float kCSPEED = TMath::C() * 1.0e2f * 1.0e-12f; /// Speed of light in TOF units (cm/ps) + +/// \brief Class to handle the the TOF detector response for the TOF beta measurement +template +class Beta +{ + public: + Beta() = default; + ~Beta() = default; + + /// Computes the beta of a track given a length, a time measurement and an event time (in ps) + static float GetBeta(const float length, const float tofSignal, const float collisionTime); + + /// Gets the beta for the track of interest + float GetBeta(const Coll& col, const Trck& trk) const { return GetBeta(trk.length(), trk.tofSignal(), col.collisionTime() * 1000.f); } + + /// Computes the expected uncertainty on the beta measurement + static float GetExpectedSigma(const float& length, const float& tofSignal, const float& collisionTime, const float& time_reso); + + /// Gets the expected uncertainty on the beta measurement of the track of interest + float GetExpectedSigma(const Coll& col, const Trck& trk) const { return GetExpectedSigma(trk.length(), trk.tofSignal(), col.collisionTime() * 1000.f, mExpectedResolution); } + + /// Gets the expected beta for a given mass hypothesis (no energy loss taken into account) + static float GetExpectedSignal(const float& mom, const float& mass); + + /// Gets the expected beta given the particle index (no energy loss taken into account) + float GetExpectedSignal(const Coll& col, const Trck& trk) const { return GetExpectedSignal(trk.p(), o2::track::PID::getMass2Z(id)); } + + /// Gets the number of sigmas with respect the approximate beta (no energy loss taken into account) + float GetSeparation(const Coll& col, const Trck& trk) const { return (GetBeta(col, trk) - GetExpectedSignal(col, trk)) / GetExpectedSigma(col, trk); } + + float mExpectedResolution = 80; /// Expected time resolution +}; + +//_________________________________________________________________________ +template +float Beta::GetBeta(const float length, const float tofSignal, const float collisionTime) +{ + if (tofSignal <= 0) { + return -999.f; + } + return length / (tofSignal - collisionTime) / kCSPEED; +} + +//_________________________________________________________________________ +template +float Beta::GetExpectedSigma(const float& length, const float& tofSignal, const float& collisionTime, const float& time_reso) +{ + if (tofSignal <= 0) { + return -999.f; + } + return GetBeta(length, tofSignal, collisionTime) / (tofSignal - collisionTime) * time_reso; +} + +//_________________________________________________________________________ +template +float Beta::GetExpectedSignal(const float& mom, const float& mass) +{ + if (mom > 0) { + return mom / TMath::Sqrt(mom * mom + mass * mass); + } + return 0; +} + +/// \brief Class to handle the the TOF detector response for the expected time +template +class ExpTimes +{ + public: + ExpTimes() = default; + ~ExpTimes() = default; + + /// Computes the expected time of a track, given it TOF expected momentum + static float ComputeExpectedTime(const float& tofExpMom, const float& length, const float& massZ); + + /// Gets the expected signal of the track of interest under the PID assumption + float GetExpectedSignal(const Coll& col, const Trck& trk) const { return ComputeExpectedTime(trk.tofExpMom() / kCSPEED, trk.length(), o2::track::PID::getMass2Z(id)); } + + /// Gets the expected resolution of the measurement + float GetExpectedSigma(const DetectorResponse& response, const Coll& col, const Trck& trk) const; + + /// Gets the number of sigmas with respect the expected time + float GetSeparation(const DetectorResponse& response, const Coll& col, const Trck& trk) const { return (trk.tofSignal() - col.collisionTime() * 1000.f - GetExpectedSignal(col, trk)) / GetExpectedSigma(response, col, trk); } +}; + +//_________________________________________________________________________ +template +float ExpTimes::ComputeExpectedTime(const float& tofExpMom, const float& length, const float& massZ) +{ + const float energy = sqrt((massZ * massZ) + (tofExpMom * tofExpMom)); + return length * energy / (kCSPEED * tofExpMom); +} + +//_________________________________________________________________________ +template +float ExpTimes::GetExpectedSigma(const DetectorResponse& response, const Coll& col, const Trck& trk) const +{ + if (trk.tofSignal() <= 0) { + return -999.f; + } + const float x[4] = {trk.p(), trk.tofSignal(), col.collisionTimeRes() * 1000.f, o2::track::PID::getMass2Z(id)}; + return response(response.kSigma, x); + // return response(response.kSigma, const Coll& col, const Trck& trk, id); +} + +} // namespace o2::pid::tof + +#endif // O2_FRAMEWORK_PIDTOF_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/PIDTPC.h b/Analysis/DataModel/include/AnalysisDataModel/PID/PIDTPC.h new file mode 100644 index 0000000000000..9e0e8ef7fa936 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/PIDTPC.h @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PIDTPC.h +/// \author Nicolo' Jacazio +/// \since 2020-07-24 +/// \brief Implementation of the TPC detector response for PID +/// + +#ifndef O2_FRAMEWORK_PIDTPC_H_ +#define O2_FRAMEWORK_PIDTPC_H_ + +// ROOT includes +#include "Rtypes.h" +#include "TMath.h" + +// O2 includes +#include "Framework/Logger.h" +#include "ReconstructionDataFormats/PID.h" +#include "AnalysisDataModel/PID/DetectorResponse.h" + +namespace o2::pid::tpc +{ + +/// \brief Class to handle the the TPC detector response +template +class ELoss +{ + public: + ELoss() = default; + ~ELoss() = default; + + /// Gets the expected signal of the measurement + float GetExpectedSignal(DetectorResponse& response, const Coll& col, const Trck& trk) const; + + /// Gets the expected resolution of the measurement + float GetExpectedSigma(DetectorResponse& response, const Coll& col, const Trck& trk) const; + + /// Gets the number of sigmas with respect the expected value + float GetSeparation(DetectorResponse& response, const Coll& col, const Trck& trk) const { return (trk.tpcSignal() - GetExpectedSignal(response, col, trk)) / GetExpectedSigma(response, col, trk); } +}; + +template +float ELoss::GetExpectedSignal(DetectorResponse& response, const Coll& col, const Trck& trk) const +{ + const float x[2] = {trk.tpcInnerParam() / o2::track::PID::getMass(id), (float)o2::track::PID::getCharge(id)}; + return response(DetectorResponse::kSignal, x); +} + +template +float ELoss::GetExpectedSigma(DetectorResponse& response, const Coll& col, const Trck& trk) const +{ + const float x[2] = {trk.tpcSignal(), (float)trk.tpcNClsFound()}; + return response(DetectorResponse::kSigma, x); +} + +} // namespace o2::pid::tpc + +#endif // O2_FRAMEWORK_PIDTPC_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/ParamBase.h b/Analysis/DataModel/include/AnalysisDataModel/PID/ParamBase.h new file mode 100644 index 0000000000000..9681800e7dc39 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/ParamBase.h @@ -0,0 +1,132 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ParamBase.h +/// \author Nicolo' Jacazio +/// +/// \brief Set of utilities to handle the parametrization of the PID response for each detector +/// These are the basic storage elements to be kept in the CCDB +/// + +#ifndef O2_FRAMEWORK_PARAMBASE_H_ +#define O2_FRAMEWORK_PARAMBASE_H_ + +// ROOT includes +#include "TNamed.h" + +namespace o2::pid +{ +/// Variable to use for the pid input/output i.e. float, double et cetera +using pidvar_t = float; + +/// \brief Class to handle the parameters of a given detector response +class Parameters : public TObject +{ + public: + /// Default constructor + Parameters() = default; + + /// Parametric constructor + /// \param npar Number of parameters in the container + Parameters(unsigned int npar) : mPar(std::vector(npar)){}; + + /// Parametric constructor + /// \param params Parameters to initialize the container + Parameters(const std::vector params) : mPar{} { SetParameters(params); }; + + /// Default destructor + ~Parameters() override = default; + + /// Setter for the parameter at position iparam + /// \param iparam index in the array of the parameters + /// \param value value of the parameter at position iparam + void SetParameter(const unsigned int iparam, const pidvar_t value) { mPar[iparam] = value; } + + /// Setter for the parameter, using an array + /// \param param array with parameters + void SetParameters(const pidvar_t* params) { std::copy(params, params + mPar.size(), mPar.begin()); } + + /// Setter for the parameter, using an array + /// \param params array with parameters + void SetParameters(const std::vector params); + + /// Printer of the parameter values + void PrintParameters() const; + + /// Getter for the parameters + /// \return returns an array of parameters + const pidvar_t* GetParameters() const { return mPar.data(); } + + /// Getter for the size of the parameter + /// \return returns the size of the parameter array + unsigned int size() const { return mPar.size(); } + + /// Getter of the parameter at position i + /// \param i index of the parameter to get + /// \return returns the parameter value at position i + pidvar_t operator[](unsigned int i) const { return mPar[i]; } + + private: + /// Vector of the parameter + std::vector mPar; + + ClassDef(Parameters, 1); // Container for parameter of parametrizations +}; + +/// \brief Class to handle the parameters and the parametrization of a given detector response +class Parametrization : public TNamed +{ + public: + /// Default constructor + Parametrization() : TNamed("DefaultParametrization", "DefaultParametrization"), mParameters{0} {}; + + /// Parametric constructor + /// \param name Name (and title) of the parametrization + /// \param size Number of parameters of the parametrization + Parametrization(TString name, unsigned int size) : TNamed(name, name), mParameters{size} {}; + + /// Parametric constructor + /// \param name Name (and title) of the parametrization + /// \param params Parameters of the parametrization + Parametrization(TString name, const std::vector params) : TNamed(name, name), mParameters{params} {}; + + /// Default destructor + ~Parametrization() override = default; + + /// Getter for parametrization values, to be reimplemented in the custom parametrization of the user + /// \param x array of variables to use in order to compute the return value + virtual pidvar_t operator()(const pidvar_t* x) const; + + /// Printer for parameters + void PrintParametrization() const; + + /// Setter for the parameter at position iparam + /// \param iparam index in the array of the parameters + /// \param value value of the parameter at position iparam + void SetParameter(const unsigned int iparam, const pidvar_t value) { mParameters.SetParameter(iparam, value); } + + /// Setter for the parameter, using an array + /// \param params array with parameters + void SetParameters(const std::vector params) { mParameters.SetParameters(params); } + + /// Getter for the parameters + Parameters GetParameters() const { return mParameters; } + + protected: + /// Parameters of the parametrization + Parameters mParameters; + + ClassDef(Parametrization, 1); // Container for the parametrization of the response function +}; + +} // namespace o2::pid + +#endif // O2_FRAMEWORK_PARAMBASE_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/TOFReso.h b/Analysis/DataModel/include/AnalysisDataModel/PID/TOFReso.h new file mode 100644 index 0000000000000..45cdb6cd7b657 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/TOFReso.h @@ -0,0 +1,52 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TOFReso.h +/// \author Nicolo' Jacazio +/// \since 07/08/2020 +/// \brief Implementation for the TOF PID response of the expected times resolution +/// + +#ifndef O2_ANALYSIS_PID_TOFRESO_H_ +#define O2_ANALYSIS_PID_TOFRESO_H_ + +// Root includes +#include "TMath.h" +// O2 includes +#include "AnalysisDataModel/PID/ParamBase.h" + +namespace o2::pid::tof +{ + +class TOFReso : public Parametrization +{ + public: + TOFReso() : Parametrization("TOFReso", 5){}; + ~TOFReso() override = default; + float operator()(const float* x) const override + { + const float mom = abs(x[0]); + if (mom <= 0) { + return -999; + } + const float time = x[1]; + const float evtimereso = x[2]; + const float mass = x[3]; + const float dpp = mParameters[0] + mParameters[1] * mom + mParameters[2] * mass / mom; // mean relative pt resolution; + const float sigma = dpp * time / (1. + mom * mom / (mass * mass)); + return TMath::Sqrt(sigma * sigma + mParameters[3] * mParameters[3] / mom / mom + mParameters[4] * mParameters[4] + evtimereso * evtimereso); + } + ClassDef(TOFReso, 1); +}; + +} // namespace o2::pid::tof + +#endif diff --git a/Analysis/DataModel/include/AnalysisDataModel/PID/TPCReso.h b/Analysis/DataModel/include/AnalysisDataModel/PID/TPCReso.h new file mode 100644 index 0000000000000..8f6d2171ee277 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/PID/TPCReso.h @@ -0,0 +1,41 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TPCReso.h +/// \author Nicolo' Jacazio +/// \since 07/08/2020 +/// \brief Implementation for the TPC PID response of the BB resolution +/// + +#ifndef O2_ANALYSIS_PID_TPCRESO_H_ +#define O2_ANALYSIS_PID_TPCRESO_H_ + +#include "AnalysisDataModel/PID/ParamBase.h" + +namespace o2::pid::tpc +{ + +class TPCReso : public Parametrization +{ + public: + TPCReso() : Parametrization("TPCReso", 2){}; + ~TPCReso() override = default; + float operator()(const float* x) const override + { + // relative dEdx resolution rel sigma = fRes0*sqrt(1+fResN2/npoint) + return x[0] * mParameters[0] * (x[1] > 0 ? sqrt(1. + mParameters[1] / x[1]) : 1.f); + } + ClassDef(TPCReso, 1); +}; + +} // namespace o2::pid::tpc + +#endif diff --git a/Analysis/DataModel/include/AnalysisDataModel/ReducedInfoTables.h b/Analysis/DataModel/include/AnalysisDataModel/ReducedInfoTables.h new file mode 100644 index 0000000000000..837cc7232cf3c --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/ReducedInfoTables.h @@ -0,0 +1,158 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// + +#ifndef O2_Analysis_ReducedInfoTables_H_ +#define O2_Analysis_ReducedInfoTables_H_ + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "MathUtils/Utils.h" +#include + +namespace o2::aod +{ + +namespace dqppfilter +{ +DECLARE_SOA_COLUMN(EventFilter, eventFilter, uint64_t); +} +// Table to be used for storing event-level decisions (DQ high level triggers) +DECLARE_SOA_TABLE(DQEventFilter, "AOD", "EVENTFILTER", dqppfilter::EventFilter); + +namespace reducedevent +{ + +// basic event information +DECLARE_SOA_COLUMN(Tag, tag, uint64_t); +DECLARE_SOA_COLUMN(TriggerAlias, triggerAlias, uint32_t); + +} // namespace reducedevent + +DECLARE_SOA_TABLE(ReducedEvents, "AOD", "REDUCEDEVENT", o2::soa::Index<>, + reducedevent::Tag, bc::RunNumber, + collision::PosX, collision::PosY, collision::PosZ, collision::NumContrib); + +DECLARE_SOA_TABLE(ReducedEventsExtended, "AOD", "REEXTENDED", + bc::GlobalBC, bc::TriggerMask, reducedevent::TriggerAlias, cent::CentV0M); + +DECLARE_SOA_TABLE(ReducedEventsVtxCov, "AOD", "REVTXCOV", + collision::CovXX, collision::CovXY, collision::CovXZ, + collision::CovYY, collision::CovYZ, collision::CovZZ, collision::Chi2); + +using ReducedEvent = ReducedEvents::iterator; +using ReducedEventExtended = ReducedEventsExtended::iterator; +using ReducedEventVtxCov = ReducedEventsVtxCov::iterator; + +namespace reducedtrack +{ +// basic track information +DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); +DECLARE_SOA_COLUMN(Index, index, uint16_t); +// ---- flags reserved for storing various information during filtering +DECLARE_SOA_COLUMN(FilteringFlags, filteringFlags, uint64_t); +// BIT 0: track is from MUON arm (if not toggled then this is a barrel track) +// ----------------------------------------------------- +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Charge, charge, int); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return pt * std::cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return pt * std::sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return pt * std::sinh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pmom, pmom, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); + +} //namespace reducedtrack + +// basic track information +DECLARE_SOA_TABLE(ReducedTracks, "AOD", "REDUCEDTRACK", + o2::soa::Index<>, reducedtrack::ReducedEventId, reducedtrack::Index, reducedtrack::FilteringFlags, + reducedtrack::Pt, reducedtrack::Eta, reducedtrack::Phi, reducedtrack::Charge, + reducedtrack::Px, + reducedtrack::Py, + reducedtrack::Pz, + reducedtrack::Pmom); + +// barrel track information +DECLARE_SOA_TABLE(ReducedTracksBarrel, "AOD", "RTBARREL", + track::TPCInnerParam, track::Flags, // tracking status flags + track::ITSClusterMap, track::ITSChi2NCl, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, + track::TPCNClsShared, track::TPCChi2NCl, + track::TRDChi2, track::TOFChi2, track::Length, reducedtrack::DcaXY, reducedtrack::DcaZ, + track::TPCNClsFound, + track::TPCNClsCrossedRows); + +// barrel covariance matrix +DECLARE_SOA_TABLE(ReducedTracksBarrelCov, "AOD", "RTBARRELCOV", + track::CYY, track::CZZ, track::CSnpSnp, + track::CTglTgl, track::C1Pt21Pt2); + +// barrel PID information +DECLARE_SOA_TABLE(ReducedTracksBarrelPID, "AOD", "RTBARRELPID", + track::TPCSignal, + pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaMu, + pidtpc::TPCNSigmaPi, pidtpc::TPCNSigmaKa, pidtpc::TPCNSigmaPr, + pidtpc::TPCNSigmaDe, pidtpc::TPCNSigmaTr, pidtpc::TPCNSigmaHe, pidtpc::TPCNSigmaAl, + track::TOFSignal, pidtofbeta::Beta, + pidtof::TOFNSigmaEl, pidtof::TOFNSigmaMu, + pidtof::TOFNSigmaPi, pidtof::TOFNSigmaKa, pidtof::TOFNSigmaPr, + pidtof::TOFNSigmaDe, pidtof::TOFNSigmaTr, pidtof::TOFNSigmaHe, pidtof::TOFNSigmaAl, + track::TRDSignal); + +// muon quantities +namespace reducedmuon +{ +DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); +DECLARE_SOA_COLUMN(FilteringFlags, filteringFlags, uint64_t); +// the (pt,eta,phi,charge) will be computed in the skimming task +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Charge, charge, int); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return pt * std::cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return pt * std::sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return pt * std::sinh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pmom, pmom, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); +} // namespace reducedmuon + +DECLARE_SOA_TABLE(ReducedMuons, "AOD", "RTMUON", + o2::soa::Index<>, reducedmuon::ReducedEventId, reducedmuon::FilteringFlags, + reducedmuon::Pt, reducedmuon::Eta, reducedmuon::Phi, reducedmuon::Charge, + reducedmuon::Px, + reducedmuon::Py, + reducedmuon::Pz, + reducedmuon::Pmom); + +DECLARE_SOA_TABLE(ReducedMuonsExtended, "AOD", "RTMUONEXTENDED", + muon::InverseBendingMomentum, + muon::ThetaX, muon::ThetaY, muon::ZMu, + muon::BendingCoor, muon::NonBendingCoor, + muon::Chi2, muon::Chi2MatchTrigger, + muon::RAtAbsorberEnd, + muon::PDca); + +// iterators +using ReducedTrack = ReducedTracks::iterator; +using ReducedTrackBarrel = ReducedTracksBarrel::iterator; +using ReducedTrackBarrelCov = ReducedTracksBarrelCov::iterator; +using ReducedTrackBarrelPID = ReducedTracksBarrelPID::iterator; +using ReducedMuon = ReducedMuons::iterator; +using ReducedMuonExtended = ReducedMuonsExtended::iterator; +} // namespace o2::aod + +#endif // O2_Analysis_ReducedInfoTables_H_ diff --git a/Analysis/DataModel/include/AnalysisDataModel/StrangenessTables.h b/Analysis/DataModel/include/AnalysisDataModel/StrangenessTables.h new file mode 100644 index 0000000000000..46701b83488f6 --- /dev/null +++ b/Analysis/DataModel/include/AnalysisDataModel/StrangenessTables.h @@ -0,0 +1,210 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_STRANGENESSTABLES_H_ +#define O2_ANALYSIS_STRANGENESSTABLES_H_ + +#include "Framework/AnalysisDataModel.h" +#include "AnalysisCore/RecoDecay.h" + +namespace o2::aod +{ +namespace v0data +{ +//Needed to have shorter table that does not rely on existing one (filtering!) +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, FullTracks, "fPosTrackID"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, FullTracks, "fNegTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); + +//General V0 properties: position, momentum +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(NegX, negX, float); +DECLARE_SOA_COLUMN(PxPos, pxpos, float); +DECLARE_SOA_COLUMN(PyPos, pypos, float); +DECLARE_SOA_COLUMN(PzPos, pzpos, float); +DECLARE_SOA_COLUMN(PxNeg, pxneg, float); +DECLARE_SOA_COLUMN(PyNeg, pyneg, float); +DECLARE_SOA_COLUMN(PzNeg, pzneg, float); +DECLARE_SOA_COLUMN(X, x, float); +DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(Z, z, float); + +//Saved from finding: DCAs +DECLARE_SOA_COLUMN(DCAV0Daughters, dcaV0daughters, float); +DECLARE_SOA_COLUMN(DCAPosToPV, dcapostopv, float); +DECLARE_SOA_COLUMN(DCANegToPV, dcanegtopv, float); + +//Derived expressions +//Momenta +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float pxpos, float pypos, float pxneg, float pyneg) { return RecoDecay::sqrtSumOfSquares(pxpos + pxneg, pypos + pyneg); }); + +//Length quantities +DECLARE_SOA_DYNAMIC_COLUMN(V0Radius, v0radius, [](float x, float y) { return RecoDecay::sqrtSumOfSquares(x, y); }); + +//CosPA +DECLARE_SOA_DYNAMIC_COLUMN(V0CosPA, v0cosPA, [](float X, float Y, float Z, float Px, float Py, float Pz, float pvX, float pvY, float pvZ) { return RecoDecay::CPA(array{pvX, pvY, pvZ}, array{X, Y, Z}, array{Px, Py, Pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DCAV0ToPV, dcav0topv, [](float X, float Y, float Z, float Px, float Py, float Pz, float pvX, float pvY, float pvZ) { return TMath::Sqrt((TMath::Power((pvY - Y) * Pz - (pvZ - Z) * Py, 2) + TMath::Power((pvX - X) * Pz - (pvZ - Z) * Px, 2) + TMath::Power((pvX - X) * Py - (pvY - Y) * Px, 2)) / (Px * Px + Py * Py + Pz * Pz)); }); + +//Calculated on the fly with mass assumption + dynamic tables +DECLARE_SOA_DYNAMIC_COLUMN(MLambda, mLambda, [](float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { return RecoDecay::M(array{array{pxpos, pypos, pzpos}, array{pxneg, pyneg, pzneg}}, array{RecoDecay::getMassPDG(kProton), RecoDecay::getMassPDG(kPiPlus)}); }); +DECLARE_SOA_DYNAMIC_COLUMN(MAntiLambda, mAntiLambda, [](float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { return RecoDecay::M(array{array{pxpos, pypos, pzpos}, array{pxneg, pyneg, pzneg}}, array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kProton)}); }); +DECLARE_SOA_DYNAMIC_COLUMN(MK0Short, mK0Short, [](float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { return RecoDecay::M(array{array{pxpos, pypos, pzpos}, array{pxneg, pyneg, pzneg}}, array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kPiPlus)}); }); + +DECLARE_SOA_DYNAMIC_COLUMN(YK0Short, yK0Short, [](float Px, float Py, float Pz) { return RecoDecay::Y(array{Px, Py, Pz}, RecoDecay::getMassPDG(kK0)); }); +DECLARE_SOA_DYNAMIC_COLUMN(YLambda, yLambda, [](float Px, float Py, float Pz) { return RecoDecay::Y(array{Px, Py, Pz}, RecoDecay::getMassPDG(kLambda0)); }); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float Px, float Py, float Pz) { return RecoDecay::Eta(array{Px, Py, Pz}); }); +} // namespace v0data + +namespace v0dataext +{ +DECLARE_SOA_EXPRESSION_COLUMN(Px, px, float, 1.f * aod::v0data::pxpos + 1.f * aod::v0data::pxneg); +DECLARE_SOA_EXPRESSION_COLUMN(Py, py, float, 1.f * aod::v0data::pypos + 1.f * aod::v0data::pyneg); +DECLARE_SOA_EXPRESSION_COLUMN(Pz, pz, float, 1.f * aod::v0data::pzpos + 1.f * aod::v0data::pzneg); +} // namespace v0dataext + +DECLARE_SOA_TABLE(V0Data, "AOD", "V0DATA", + o2::soa::Index<>, v0data::PosTrackId, v0data::NegTrackId, v0data::CollisionId, + v0data::PosX, v0data::NegX, + v0data::X, v0data::Y, v0data::Z, + v0data::PxPos, v0data::PyPos, v0data::PzPos, + v0data::PxNeg, v0data::PyNeg, v0data::PzNeg, + v0data::DCAV0Daughters, v0data::DCAPosToPV, v0data::DCANegToPV, + + //Dynamic columns + v0data::Pt, + v0data::V0Radius, + v0data::V0CosPA, + v0data::DCAV0ToPV, + + //Invariant masses + v0data::MLambda, + v0data::MAntiLambda, + v0data::MK0Short, + + //Longitudinal + v0data::YK0Short, + v0data::YLambda, + v0data::Eta); + +using V0DataOrigin = V0Data; + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(V0DataExt, V0DataOrigin, "V0DATAEXT", + v0dataext::Px, v0dataext::Py, v0dataext::Pz); + +using V0DataFull = V0DataExt; + +namespace cascdata +{ +//Necessary for full filtering functionality +DECLARE_SOA_INDEX_COLUMN_FULL(V0, v0, int, V0DataExt, "fV0ID"); +DECLARE_SOA_INDEX_COLUMN_FULL(BachTrack, bachTrack, int, FullTracks, "fBachTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +//General V0 properties: position, momentum +DECLARE_SOA_COLUMN(Charge, charge, int); +DECLARE_SOA_COLUMN(PxPos, pxpos, float); +DECLARE_SOA_COLUMN(PyPos, pypos, float); +DECLARE_SOA_COLUMN(PzPos, pzpos, float); +DECLARE_SOA_COLUMN(PxNeg, pxneg, float); +DECLARE_SOA_COLUMN(PyNeg, pyneg, float); +DECLARE_SOA_COLUMN(PzNeg, pzneg, float); +DECLARE_SOA_COLUMN(PxBach, pxbach, float); +DECLARE_SOA_COLUMN(PyBach, pybach, float); +DECLARE_SOA_COLUMN(PzBach, pzbach, float); +DECLARE_SOA_COLUMN(X, x, float); +DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(Z, z, float); +DECLARE_SOA_COLUMN(Xlambda, xlambda, float); +DECLARE_SOA_COLUMN(Ylambda, ylambda, float); +DECLARE_SOA_COLUMN(Zlambda, zlambda, float); + +//Saved from finding: DCAs +DECLARE_SOA_COLUMN(DCAV0Daughters, dcaV0daughters, float); +DECLARE_SOA_COLUMN(DCACascDaughters, dcacascdaughters, float); +DECLARE_SOA_COLUMN(DCAPosToPV, dcapostopv, float); +DECLARE_SOA_COLUMN(DCANegToPV, dcanegtopv, float); +DECLARE_SOA_COLUMN(DCABachToPV, dcabachtopv, float); + +//Derived expressions +//Momenta +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float Px, float Py) { return RecoDecay::sqrtSumOfSquares(Px, Py); }); + +//Length quantities +DECLARE_SOA_DYNAMIC_COLUMN(V0Radius, v0radius, [](float xlambda, float ylambda) { return RecoDecay::sqrtSumOfSquares(xlambda, ylambda); }); +DECLARE_SOA_DYNAMIC_COLUMN(CascRadius, cascradius, [](float x, float y) { return RecoDecay::sqrtSumOfSquares(x, y); }); + +//CosPAs +DECLARE_SOA_DYNAMIC_COLUMN(V0CosPA, v0cosPA, [](float Xlambda, float Ylambda, float Zlambda, float PxLambda, float PyLambda, float PzLambda, float pvX, float pvY, float pvZ) { return RecoDecay::CPA(array{pvX, pvY, pvZ}, array{Xlambda, Ylambda, Zlambda}, array{PxLambda, PyLambda, PzLambda}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CascCosPA, casccosPA, [](float X, float Y, float Z, float Px, float Py, float Pz, float pvX, float pvY, float pvZ) { return RecoDecay::CPA(array{pvX, pvY, pvZ}, array{X, Y, Z}, array{Px, Py, Pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DCAV0ToPV, dcav0topv, [](float X, float Y, float Z, float Px, float Py, float Pz, float pvX, float pvY, float pvZ) { return TMath::Sqrt((TMath::Power((pvY - Y) * Pz - (pvZ - Z) * Py, 2) + TMath::Power((pvX - X) * Pz - (pvZ - Z) * Px, 2) + TMath::Power((pvX - X) * Py - (pvY - Y) * Px, 2)) / (Px * Px + Py * Py + Pz * Pz)); }); +DECLARE_SOA_DYNAMIC_COLUMN(DCACascToPV, dcacasctopv, [](float X, float Y, float Z, float Px, float Py, float Pz, float pvX, float pvY, float pvZ) { return TMath::Sqrt((TMath::Power((pvY - Y) * Pz - (pvZ - Z) * Py, 2) + TMath::Power((pvX - X) * Pz - (pvZ - Z) * Px, 2) + TMath::Power((pvX - X) * Py - (pvY - Y) * Px, 2)) / (Px * Px + Py * Py + Pz * Pz)); }); + +//Calculated on the fly with mass assumption + dynamic tables +DECLARE_SOA_DYNAMIC_COLUMN(MLambda, mLambda, [](int charge, float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { return RecoDecay::M(array{array{pxpos, pypos, pzpos}, array{pxneg, pyneg, pzneg}}, charge < 0 ? array{RecoDecay::getMassPDG(kProton), RecoDecay::getMassPDG(kPiPlus)} : array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kProton)}); }); +//Calculated on the fly with mass assumption + dynamic tables + +DECLARE_SOA_DYNAMIC_COLUMN(MXi, mXi, [](float pxbach, float pybach, float pzbach, float PxLambda, float PyLambda, float PzLambda) { return RecoDecay::M(array{array{pxbach, pybach, pzbach}, array{PxLambda, PyLambda, PzLambda}}, array{RecoDecay::getMassPDG(kPiPlus), RecoDecay::getMassPDG(kLambda0)}); }); +DECLARE_SOA_DYNAMIC_COLUMN(MOmega, mOmega, [](float pxbach, float pybach, float pzbach, float PxLambda, float PyLambda, float PzLambda) { return RecoDecay::M(array{array{pxbach, pybach, pzbach}, array{PxLambda, PyLambda, PzLambda}}, array{RecoDecay::getMassPDG(kKPlus), RecoDecay::getMassPDG(kLambda0)}); }); + +DECLARE_SOA_DYNAMIC_COLUMN(YXi, yXi, [](float Px, float Py, float Pz) { return RecoDecay::Y(array{Px, Py, Pz}, 1.32171); }); +DECLARE_SOA_DYNAMIC_COLUMN(YOmega, yOmega, [](float Px, float Py, float Pz) { return RecoDecay::Y(array{Px, Py, Pz}, 1.67245); }); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float Px, float Py, float Pz) { return RecoDecay::Eta(array{Px, Py, Pz}); }); +} // namespace cascdata + +namespace cascdataext +{ +DECLARE_SOA_EXPRESSION_COLUMN(PxLambda, pxlambda, float, 1.f * aod::cascdata::pxpos + 1.f * aod::cascdata::pxneg); +DECLARE_SOA_EXPRESSION_COLUMN(PyLambda, pylambda, float, 1.f * aod::cascdata::pypos + 1.f * aod::cascdata::pyneg); +DECLARE_SOA_EXPRESSION_COLUMN(PzLambda, pzlambda, float, 1.f * aod::cascdata::pzpos + 1.f * aod::cascdata::pzneg); +DECLARE_SOA_EXPRESSION_COLUMN(Px, px, float, 1.f * aod::cascdata::pxpos + 1.f * aod::cascdata::pxneg + 1.f * aod::cascdata::pxbach); +DECLARE_SOA_EXPRESSION_COLUMN(Py, py, float, 1.f * aod::cascdata::pypos + 1.f * aod::cascdata::pyneg + 1.f * aod::cascdata::pybach); +DECLARE_SOA_EXPRESSION_COLUMN(Pz, pz, float, 1.f * aod::cascdata::pzpos + 1.f * aod::cascdata::pzneg + 1.f * aod::cascdata::pzbach); +} // namespace cascdataext + +DECLARE_SOA_TABLE(CascData, "AOD", "CASCDATA", + o2::soa::Index<>, cascdata::V0Id, cascdata::BachTrackId, cascdata::CollisionId, + + cascdata::Charge, + cascdata::X, cascdata::Y, cascdata::Z, + cascdata::Xlambda, cascdata::Ylambda, cascdata::Zlambda, + cascdata::PxPos, cascdata::PyPos, cascdata::PzPos, + cascdata::PxNeg, cascdata::PyNeg, cascdata::PzNeg, + cascdata::PxBach, cascdata::PyBach, cascdata::PzBach, + cascdata::DCAV0Daughters, cascdata::DCACascDaughters, + cascdata::DCAPosToPV, cascdata::DCANegToPV, cascdata::DCABachToPV, + + //Dynamic columns + cascdata::Pt, + cascdata::V0Radius, + cascdata::CascRadius, + cascdata::V0CosPA, + cascdata::CascCosPA, + cascdata::DCAV0ToPV, + cascdata::DCACascToPV, + + //Invariant masses + cascdata::MLambda, + cascdata::MXi, + cascdata::MOmega, + //Longitudinal + cascdata::YXi, + cascdata::YOmega, + cascdata::Eta); + +using CascDataOrigin = CascData; + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(CascDataExt, CascDataOrigin, "CascDATAEXT", + cascdataext::PxLambda, cascdataext::PyLambda, cascdataext::PzLambda, + cascdataext::Px, cascdataext::Py, cascdataext::Pz); + +using CascDataFull = CascDataExt; +} // namespace o2::aod + +#endif // O2_ANALYSIS_STRANGENESSTABLES_H_ diff --git a/Analysis/DataModel/include/Analysis/TrackSelectionTables.h b/Analysis/DataModel/include/AnalysisDataModel/TrackSelectionTables.h similarity index 86% rename from Analysis/DataModel/include/Analysis/TrackSelectionTables.h rename to Analysis/DataModel/include/AnalysisDataModel/TrackSelectionTables.h index 7b9e95162fa10..52e114013a87a 100644 --- a/Analysis/DataModel/include/Analysis/TrackSelectionTables.h +++ b/Analysis/DataModel/include/AnalysisDataModel/TrackSelectionTables.h @@ -17,12 +17,13 @@ namespace o2::aod { namespace track { +// Columns to store the DCA to the primary vertex DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); // Columns to store track filter decisions -DECLARE_SOA_COLUMN(IsGlobalTrack, isGlobalTrack, bool); -DECLARE_SOA_COLUMN(IsGlobalTrackSDD, isGlobalTrackSDD, bool); +DECLARE_SOA_COLUMN(IsGlobalTrack, isGlobalTrack, uint8_t); +DECLARE_SOA_COLUMN(IsGlobalTrackSDD, isGlobalTrackSDD, uint8_t); } // namespace track DECLARE_SOA_TABLE(TracksExtended, "AOD", "TRACKEXTENDED", track::DcaXY, diff --git a/Analysis/DataModel/src/AnalysisDataModelLinkDef.h b/Analysis/DataModel/src/AnalysisDataModelLinkDef.h new file mode 100644 index 0000000000000..5db890293367e --- /dev/null +++ b/Analysis/DataModel/src/AnalysisDataModelLinkDef.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::pid::Parameters + ; +#pragma link C++ class o2::pid::Parametrization + ; + +#pragma link C++ class o2::pid::tof::TOFReso + ; + +#pragma link C++ class o2::pid::tpc::BetheBloch + ; +#pragma link C++ class o2::pid::tpc::TPCReso + ; diff --git a/Analysis/DataModel/src/ParamBase.cxx b/Analysis/DataModel/src/ParamBase.cxx new file mode 100644 index 0000000000000..865f566c905e4 --- /dev/null +++ b/Analysis/DataModel/src/ParamBase.cxx @@ -0,0 +1,52 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ParamBase.cxx +/// \author Nicolo' Jacazio +/// \since 07/08/2020 +/// \brief Set of utilities to handle the parametrization of the PID response for each detector +/// These are the basic storage elements to be kept in the CCDB +/// + +#include "AnalysisDataModel/PID/ParamBase.h" +#include "Framework/Logger.h" + +namespace o2::pid +{ + +void Parameters::SetParameters(const std::vector params) +{ + if (mPar.size() != params.size()) { + LOG(fatal) << "Updating parametrization size!"; + } + mPar.assign(params.begin(), params.end()); +} + +void Parameters::PrintParameters() const +{ + for (unsigned int i = 0; i < size(); i++) { + LOG(info) << "Parameter " << i << "/" << size() - 1 << " is " << mPar[i]; + } +}; + +pidvar_t Parametrization::operator()(const pidvar_t* x) const +{ + LOG(fatal) << "Parametrization " << fName << " is not implemented!"; + return -999.999f; +} + +void Parametrization::PrintParametrization() const +{ + LOG(info) << "Parametrization " << fName; + mParameters.PrintParameters(); +}; + +} // namespace o2::pid \ No newline at end of file diff --git a/Analysis/DataModel/src/aodDataModelGraph.cxx b/Analysis/DataModel/src/aodDataModelGraph.cxx new file mode 100644 index 0000000000000..11f70cfb458e9 --- /dev/null +++ b/Analysis/DataModel/src/aodDataModelGraph.cxx @@ -0,0 +1,273 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/AnalysisDataModel.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/Jet.h" +#include "AnalysisDataModel/StrangenessTables.h" +#include +#include + +using namespace o2::framework; +using namespace o2::aod; +using namespace o2::soa; + +static int count = 0; +static int width = 10; +static int height = 10; + +void inline graphSize() +{ + fmt::printf( + R"( +size="%d,%d"; +)", + width, height); +} + +struct Style { + const char* color; + const char* background; + const char* fontcolor; + const char* headerfontcolor; + const char* headerbgcolor; + const char* methodcolor; + const char* methodbgcolor; + const char* indexcolor; + const char* indexbgcolor; +}; + +static Style styles[] = { + {"black", "gray80", "black", "black", "gray70", "black", "gray60", "black", "gray50"}, + {"/reds9/2", "/reds9/4", "white", "white", "/reds9/7", "black", "/reds9/6", "/reds9/1", "/reds9/5"}, + {"/greens9/2", "/greens9/4", "white", "white", "/greens9/7", "black", "/greens9/6", "/greens9/1", "/greens9/5"}, + {"/blues9/2", "/blues9/4", "white", "white", "/blues9/7", "black", "/blues9/6", "/blues9/1", "/blues9/5"}, +}; + +Style const& getDefaultStyle() +{ + return styles[0]; +} + +enum StyleType : int { + DEFAULT = 0, + RED = 1, + GREEN = 2, + BLUE = 3, +}; + +static std::vector> tableStyles = { + {"HfTrackIndexProng", StyleType::GREEN}, + {"pidResp", StyleType::GREEN}, + {"Mults", StyleType::GREEN}, + {"Cents", StyleType::GREEN}, + {"Jet", StyleType::BLUE}, + {"Mc", StyleType::RED}, + {"V0Data", StyleType::GREEN}, + {"CascData", StyleType::GREEN}}; + +template +Style getStyleFor() +{ + auto label = MetadataTrait::metadata::tableLabel(); + auto entry = std::find_if(tableStyles.begin(), tableStyles.end(), [&](auto&& x) { if (std::string(label).find(x.first) != std::string::npos) { return true; + +}return false; }); + if (entry != tableStyles.end()) { + auto value = *entry; + return styles[value.second]; + } + return styles[StyleType::DEFAULT]; +} + +void inline nodeEmpty() +{ + fmt::printf( + R"(node[shape=none,height=0,width=0,label=""])"); +} + +void inline nodeNormal() +{ + fmt::printf( + R"(node[shape=plain,style=filled,fillcolor=gray95])"); +} + +void inline graphHeader(char const* type, char const* name) +{ + fmt::printf(R"(%s %s { +edge[dir=back, arrowtail=empty] +)", + type, name); + nodeNormal(); +} + +void inline graphFooter() +{ + fmt::printf("}\n"); +} + +template +void displayEntity(); + +template +void displayOriginals(pack) +{ + graphHeader("subgraph", fmt::format("cluster_{}", count++).c_str()); + fmt::printf("label = %s;\n", MetadataTrait>>::metadata::tableLabel()); + (..., displayEntity()); + graphFooter(); +} + +template +void printColumn(char const* fg, char const* bg) +{ + if constexpr (!is_index_column_v) { + fmt::printf("%s", fg, bg, C::columnLabel()); + } +} + +template +void printIndexColumn(char const* fg, char const* bg) +{ + if constexpr (is_index_column_v) { + fmt::printf("%s", fg, bg, C::columnLabel()); + } +} + +template +void displayColumns(pack, const char* fg, const char* bg) +{ + (printColumn(fg, bg), ...); + fmt::printf("%s", "\n"); +} + +template +void displayIndexColumns(pack, char const* fg, char const* bg) +{ + (printIndexColumn(fg, bg), ...); + fmt::printf("%s", "\n"); +} + +template +void printIndex() +{ + if constexpr (!is_type_with_originals_v) { + auto a = MetadataTrait::metadata::tableLabel(); + auto b = MetadataTrait::metadata::tableLabel(); + fmt::printf("%s -> %s []\n", a, b); + } else { + using main_original = pack_element_t<1, typename C::binding_t::originals>; + auto a = MetadataTrait::metadata::tableLabel(); + auto b = MetadataTrait::metadata::tableLabel(); + fmt::printf("%s -> %s []\n", a, b); + } +} + +template +void dumpIndex(pack) +{ + (printIndex(), ...); + fmt::printf("%s", "\n"); +} + +template +void displayTable() +{ + auto style = getStyleFor(); + auto label = MetadataTrait::metadata::tableLabel(); + fmt::printf(R"(%s[color="%s" cellpadding="0" fillcolor="%s" fontcolor="%s" label = < +)", + label, style.color, style.background, style.fontcolor, style.headerbgcolor, style.headerfontcolor, label); + if (pack_size(typename T::iterator::persistent_columns_t{}) - + pack_size(typename T::iterator::external_index_columns_t{}) > + 0) { + displayColumns(typename T::iterator::persistent_columns_t{}, style.color, style.background); + fmt::printf("%s", "HR"); + } + if (pack_size(typename T::iterator::dynamic_columns_t{})) { + displayColumns(typename T::iterator::dynamic_columns_t{}, style.methodcolor, style.methodbgcolor); + fmt::printf("%s", "HR"); + } + displayIndexColumns(typename T::iterator::external_index_columns_t{}, style.indexcolor, style.indexbgcolor); + fmt::printf("%s", "
%s
\n>]\n"); + dumpIndex(typename T::iterator::external_index_columns_t{}); +} + +template +void displayEntity() +{ + if constexpr (is_soa_join_t::value) { + displayOriginals(typename T::originals{}); + } else { + displayTable(); + } +} + +template +void displayEntities() +{ + graphHeader("subgraph", fmt::format("cluster_{}", count++).c_str()); + (..., displayEntity()); + graphFooter(); +} + +int main(int, char**) +{ + graphHeader("digraph", "hierarchy"); + graphSize(); + fmt::printf(R"(compound = true; +)"); + + displayEntity(); + /// rank trick to avoid BCs moving + nodeEmpty(); + fmt::printf(R"({rank = same; BCs -> root[style=invis];};)"); + nodeNormal(); + + displayEntity(); + displayEntity(); + displayEntity(); + displayEntity(); + + displayEntities(); + displayEntity(); + displayEntity(); + + displayEntity(); + displayEntity(); + displayEntity(); + + displayEntity(); + + displayEntities(); + displayEntity(); + + displayEntity(); + displayEntity(); + + displayEntity(); + displayEntity(); + + displayEntity(); + displayEntity(); + + displayEntities(); + + displayEntities(); + + displayEntity(); + displayEntity(); + + graphFooter(); + return 0; +} diff --git a/Analysis/DataModel/src/dummy.cxx b/Analysis/DataModel/src/dummy.cxx deleted file mode 100644 index 03082405d8cf5..0000000000000 --- a/Analysis/DataModel/src/dummy.cxx +++ /dev/null @@ -1,10 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "Analysis/SecondaryVertex.h" diff --git a/Analysis/DataModel/src/dumpDataModel.cxx b/Analysis/DataModel/src/dumpDataModel.cxx deleted file mode 100644 index ab9467e910f57..0000000000000 --- a/Analysis/DataModel/src/dumpDataModel.cxx +++ /dev/null @@ -1,174 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/AnalysisDataModel.h" -#include "Analysis/SecondaryVertex.h" -#include "Analysis/Jet.h" -#include -#include - -using namespace o2::framework; -using namespace o2::aod; -using namespace o2::soa; - -static int count = 0; - -template -void printColumn(char const* fg, char const* bg) -{ - if constexpr (!is_index_column_v) { - fmt::printf("%s", fg, bg, C::columnLabel()); - } -} - -template -void printIndexColumn(char const* fg, char const* bg) -{ - if constexpr (is_index_column_v) { - fmt::printf("%s", fg, bg, C::columnLabel()); - } -} - -template -void printIndex() -{ - if constexpr (!is_type_with_originals_v) { - auto a = MetadataTrait::metadata::tableLabel(); - auto b = MetadataTrait::metadata::tableLabel(); - fmt::printf("%s -> %s []\n", a, b); - } else { - using main_original = pack_element_t<0, typename C::binding_t::originals>; - auto a = MetadataTrait::metadata::tableLabel(); - auto b = MetadataTrait::metadata::tableLabel(); - fmt::printf("%s -> %s []\n", a, b); - } -} - -template -void dumpColumns(pack, const char* fg, const char* bg) -{ - (printColumn(fg, bg), ...); - fmt::printf("%s", "\n"); -} - -template -void dumpIndexColumns(pack, char const* fg, char const* bg) -{ - (printIndexColumn(fg, bg), ...); - fmt::printf("%s", "\n"); -} - -template -void dumpIndex(pack) -{ - (printIndex(), ...); - fmt::printf("%s", "\n"); -} - -struct Style { - const char* color; - const char* background; - const char* fontcolor; - const char* headerfontcolor; - const char* headerbgcolor; - const char* methodcolor; - const char* methodbgcolor; - const char* indexcolor; - const char* indexbgcolor; -}; - -static Style styles[] = { - {"black", "gray80", "black", "black", "gray70", "black", "gray60", "black", "gray50"}, - {"/reds9/2", "/reds9/4", "white", "white", "/reds9/7", "black", "/reds9/6", "/reds9/1", "/reds9/5"}, - {"/greens9/2", "/greens9/4", "white", "white", "/greens9/7", "black", "/greens9/6", "/greens9/1", "/greens9/5"}, - {"/blues9/2", "/blues9/4", "white", "white", "/blues9/7", "black", "/blues9/6", "/blues9/1", "/blues9/5"}, -}; - -Style const& getDefaultStyle() -{ - return styles[0]; -} - -enum struct StyleType : int { - DEFAULT = 0, - RED = 1, - GREEN = 2, - BLUE = 3, -}; - -template -void dumpTable(bool index = true, enum StyleType styleId = StyleType::DEFAULT) -{ - auto style = styles[static_cast(styleId)]; - // nodes.push_back({MetadataTrait::metadata::label(), nodeCount}); - auto label = MetadataTrait::metadata::tableLabel(); - fmt::printf(R"(%s[color="%s" cellpadding="0" fillcolor="%s" fontcolor="%s" label = < -)", - label, style.color, style.background, style.fontcolor, style.headerbgcolor, style.headerfontcolor, label); - if (pack_size(typename T::iterator::persistent_columns_t{}) - - pack_size(typename T::iterator::external_index_columns_t{})) { - dumpColumns(typename T::iterator::persistent_columns_t{}, style.color, style.background); - fmt::printf("%s", "HR"); - } - if (pack_size(typename T::iterator::dynamic_columns_t{})) { - dumpColumns(typename T::iterator::dynamic_columns_t{}, style.methodcolor, style.methodbgcolor); - fmt::printf("%s", "HR"); - } - dumpIndexColumns(typename T::iterator::external_index_columns_t{}, style.indexcolor, style.indexbgcolor); - fmt::printf("%s", "
%s
\n>]\n"); - if (index) - dumpIndex(typename T::iterator::external_index_columns_t{}); -} - -template -void dumpCluster() -{ - fmt::printf(R"(subgraph cluster_%d { -node[shape=plain,style=filled,fillcolor=gray95] -edge[dir=back, arrowtail=empty] -)", - count++); - (dumpTable(false), ...); - fmt::printf("%s", "}\n"); - (dumpIndex(typename Ts::iterator::external_index_columns_t{}), ...); -} - -int main(int argc, char** argv) -{ - fmt::printf("%s", R"(digraph hierarchy { -size="5,5" -node[shape=plain,style=filled,fillcolor=gray95] -edge[dir=back, arrowtail=empty] -)"); - dumpCluster(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(); - dumpTable(true, StyleType::GREEN); - dumpTable(true, StyleType::GREEN); - dumpTable(true, StyleType::BLUE); - dumpTable(true, StyleType::BLUE); - dumpTable(); - dumpTable(true, StyleType::RED); - dumpTable(true, StyleType::RED); - dumpTable(true, StyleType::RED); - dumpTable(true, StyleType::RED); - dumpTable(true, StyleType::RED); - fmt::printf("%s\n", R"(})"); -} diff --git a/Analysis/DataModel/src/handleParamTOFReso.cxx b/Analysis/DataModel/src/handleParamTOFReso.cxx new file mode 100644 index 0000000000000..2d6c62f72f3f8 --- /dev/null +++ b/Analysis/DataModel/src/handleParamTOFReso.cxx @@ -0,0 +1,104 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 handleParamTOFBetheBloch.cxx +/// \author Nicolo' Jacazio +/// \since 2020-06-22 +/// \brief A simple tool to produce Bethe Bloch parametrization objects for the TOF PID Response +/// + +#include "CCDB/CcdbApi.h" +#include +#include +#include "TFile.h" +#include "AnalysisDataModel/PID/TOFReso.h" + +using namespace o2::pid::tof; +namespace bpo = boost::program_options; + +bool initOptionsAndParse(bpo::options_description& options, int argc, char* argv[], bpo::variables_map& vm) +{ + options.add_options()( + "url,u", bpo::value()->default_value("http://ccdb-test.cern.ch:8080"), "URL of the CCDB database")( + "start,s", bpo::value()->default_value(0), "Start timestamp of object validity")( + "stop,S", bpo::value()->default_value(4108971600000), "Stop timestamp of object validity")( + "delete_previous,d", bpo::value()->default_value(0), "Flag to delete previous versions of converter objects in the CCDB before uploading the new one so as to avoid proliferation on CCDB")( + "file,f", bpo::value()->default_value(""), "Option to save parametrization to file instead of uploading to ccdb")( + "mode,m", bpo::value()->default_value(0), "Working mode: 0 push 1 pull and test")( + "verbose,v", bpo::value()->default_value(0), "Verbose level 0, 1")( + "help,h", "Produce help message."); + try { + bpo::store(parse_command_line(argc, argv, options), vm); + + // help + if (vm.count("help")) { + LOG(INFO) << options; + return false; + } + + bpo::notify(vm); + } catch (const bpo::error& e) { + LOG(ERROR) << e.what() << "\n"; + LOG(ERROR) << "Error parsing command line arguments; Available options:"; + LOG(ERROR) << options; + return false; + } + return true; +} + +int main(int argc, char* argv[]) +{ + bpo::options_description options("Allowed options"); + bpo::variables_map vm; + if (!initOptionsAndParse(options, argc, argv, vm)) { + return 1; + } + + const unsigned int mode = vm["mode"].as(); + const std::string path = "Analysis/PID/TOF"; + std::map metadata; + std::map* headers; + o2::ccdb::CcdbApi api; + const std::string url = vm["url"].as(); + api.init(url); + if (!api.isHostReachable()) { + LOG(WARNING) << "CCDB host " << url << " is not reacheable, cannot go forward"; + return 1; + } + if (mode == 0) { // Push mode + const std::vector resoparams = {0.008, 0.008, 0.002, 40.0, 60.f}; + TOFReso reso; + reso.SetParameters(resoparams); + const std::string fname = vm["file"].as(); + if (!fname.empty()) { // Saving it to file + TFile f(fname.data(), "RECREATE"); + reso.Write(); + f.ls(); + f.Close(); + } else { // Saving it to CCDB + + long start = vm["start"].as(); + long stop = vm["stop"].as(); + + if (vm["delete_previous"].as()) { + api.truncate(path); + } + api.storeAsTFileAny(&reso, path + "/TOFReso", metadata, start, stop); + } + } else { // Pull and test mode + const float x[2] = {1, 1}; + TOFReso* reso = api.retrieveFromTFileAny(path + "/TOFReso", metadata, -1, headers); + reso->PrintParametrization(); + LOG(INFO) << "TOFReso " << reso->operator()(x); + } + + return 0; +} diff --git a/Analysis/DataModel/src/handleParamTPCBetheBloch.cxx b/Analysis/DataModel/src/handleParamTPCBetheBloch.cxx new file mode 100644 index 0000000000000..cd3d7b33496ba --- /dev/null +++ b/Analysis/DataModel/src/handleParamTPCBetheBloch.cxx @@ -0,0 +1,131 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 handleParamTPCBetheBloch.cxx +/// \author Nicolo' Jacazio +/// \since 2020-06-22 +/// \brief A simple tool to produce Bethe Bloch parametrization objects for the TPC PID Response +/// + +#include "CCDB/CcdbApi.h" +#include +#include +#include "TFile.h" +#include "AnalysisDataModel/PID/BetheBloch.h" +#include "AnalysisDataModel/PID/TPCReso.h" + +using namespace o2::pid::tpc; +namespace bpo = boost::program_options; + +bool initOptionsAndParse(bpo::options_description& options, int argc, char* argv[], bpo::variables_map& vm) +{ + options.add_options()( + "url,u", bpo::value()->default_value("http://ccdb-test.cern.ch:8080"), "URL of the CCDB database")( + "start,s", bpo::value()->default_value(0), "Start timestamp of object validity")( + "stop,S", bpo::value()->default_value(4108971600000), "Stop timestamp of object validity")( + "delete-previous,d", bpo::value()->default_value(0), "Flag to delete previous versions of converter objects in the CCDB before uploading the new one so as to avoid proliferation on CCDB")( + "save-to-file,f", bpo::value()->default_value(""), "Option to save parametrization to file instead of uploading to ccdb")( + "read-from-file", bpo::value()->default_value(""), "Option to get parametrization from a file")( + "mode,m", bpo::value()->default_value(0), "Working mode: 0 push 1 pull and test")( + "verbose,v", bpo::value()->default_value(0), "Verbose level 0, 1")( + "help,h", "Produce help message."); + try { + bpo::store(parse_command_line(argc, argv, options), vm); + + // help + if (vm.count("help")) { + LOG(INFO) << options; + return false; + } + + bpo::notify(vm); + } catch (const bpo::error& e) { + LOG(ERROR) << e.what() << "\n"; + LOG(ERROR) << "Error parsing command line arguments; Available options:"; + LOG(ERROR) << options; + return false; + } + return true; +} + +int main(int argc, char* argv[]) +{ + bpo::options_description options("Allowed options"); + bpo::variables_map vm; + if (!initOptionsAndParse(options, argc, argv, vm)) { + return 1; + } + + const unsigned int mode = vm["mode"].as(); + const std::string path = "Analysis/PID/TPC"; + std::map metadata; + std::map* headers; + o2::ccdb::CcdbApi api; + const std::string url = vm["url"].as(); + api.init(url); + if (!api.isHostReachable()) { + LOG(WARNING) << "CCDB host " << url << " is not reacheable, cannot go forward"; + return 1; + } + if (mode == 0) { // Push mode + BetheBloch* bb = nullptr; + TPCReso* reso = nullptr; + const std::string input_file_name = vm["read-from-file"].as(); + + if (!input_file_name.empty()) { + TFile f(input_file_name.data(), "READ"); + if (!f.IsOpen()) { + LOG(WARNING) << "Input file " << input_file_name << " is not reacheable, cannot get param from file"; + } + f.GetObject("BetheBloch", bb); + f.GetObject("TPCReso", reso); + f.Close(); + } + if (!bb) { + bb = new BetheBloch(); + const std::vector bbparams = {0.0320981, 19.9768, 2.52666e-16, 2.72123, 6.08092, 50.f, 2.3}; + bb->SetParameters(bbparams); + } + if (!reso) { + reso = new TPCReso(); + const std::vector resoparams = {0.07, 0.0}; + reso->SetParameters(resoparams); + } + const std::string fname = vm["save-to-file"].as(); + if (!fname.empty()) { // Saving it to file + TFile f(fname.data(), "RECREATE"); + bb->Write(); + reso->Write(); + f.ls(); + f.Close(); + } else { // Saving it to CCDB + + long start = vm["start"].as(); + long stop = vm["stop"].as(); + + if (vm["delete-previous"].as()) { + api.truncate(path); + } + api.storeAsTFileAny(bb, path + "/BetheBloch", metadata, start, stop); + api.storeAsTFileAny(reso, path + "/TPCReso", metadata, start, stop); + } + } else { // Pull and test mode + const float x[2] = {1, 1}; + BetheBloch* bb = api.retrieveFromTFileAny(path + "/BetheBloch", metadata, -1, headers); + bb->PrintParametrization(); + LOG(INFO) << "BetheBloch " << bb->operator()(x); + TPCReso* reso = api.retrieveFromTFileAny(path + "/TPCReso", metadata, -1, headers); + reso->PrintParametrization(); + LOG(INFO) << "TPCReso " << reso->operator()(x); + } + + return 0; +} diff --git a/Analysis/PWGDQ/CMakeLists.txt b/Analysis/PWGDQ/CMakeLists.txt new file mode 100644 index 0000000000000..06ba6b261cd49 --- /dev/null +++ b/Analysis/PWGDQ/CMakeLists.txt @@ -0,0 +1,25 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(PWGDQCore + SOURCES src/VarManager.cxx + src/HistogramManager.cxx + src/AnalysisCut.cxx + src/AnalysisCompositeCut.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore) + +o2_target_root_dictionary(PWGDQCore + HEADERS include/PWGDQCore/AnalysisCut.h + include/PWGDQCore/AnalysisCompositeCut.h + include/PWGDQCore/VarManager.h + include/PWGDQCore/HistogramManager.h + include/PWGDQCore/CutsLibrary.h + include/PWGDQCore/HistogramsLibrary.h + LINKDEF src/PWGDQCoreLinkDef.h) diff --git a/Analysis/PWGDQ/include/PWGDQCore/AnalysisCompositeCut.h b/Analysis/PWGDQ/include/PWGDQCore/AnalysisCompositeCut.h new file mode 100644 index 0000000000000..5da1b34dd5811 --- /dev/null +++ b/Analysis/PWGDQ/include/PWGDQCore/AnalysisCompositeCut.h @@ -0,0 +1,54 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +// Cut class manipulating groups of cuts +// + +#ifndef AnalysisCompositeCut_H +#define AnalysisCompositeCut_H + +#include "PWGDQCore/AnalysisCut.h" +#include + +//_________________________________________________________________________ +class AnalysisCompositeCut : public AnalysisCut +{ + public: + AnalysisCompositeCut(bool useAND = kTRUE); + AnalysisCompositeCut(const char* name, const char* title, bool useAND = kTRUE); + AnalysisCompositeCut(const AnalysisCompositeCut& c) = default; + AnalysisCompositeCut& operator=(const AnalysisCompositeCut& c) = default; + ~AnalysisCompositeCut() override; + + void AddCut(AnalysisCut* cut) + { + if (cut->IsA() == AnalysisCompositeCut::Class()) { + fCompositeCutList.push_back(*(AnalysisCompositeCut*)cut); + } else { + fCutList.push_back(*cut); + } + }; + + bool GetUseAND() const { return fOptionUseAND; } + int GetNCuts() const { return fCutList.size() + fCompositeCutList.size(); } + + bool IsSelected(float* values) override; + + protected: + bool fOptionUseAND; // true (default): apply AND on all cuts; false: use OR + std::vector fCutList; // list of cuts + std::vector fCompositeCutList; // list of composite cuts + + ClassDef(AnalysisCompositeCut, 2); +}; + +#endif diff --git a/Analysis/PWGDQ/include/PWGDQCore/AnalysisCut.h b/Analysis/PWGDQ/include/PWGDQCore/AnalysisCut.h new file mode 100644 index 0000000000000..d094e0d6cffe8 --- /dev/null +++ b/Analysis/PWGDQ/include/PWGDQCore/AnalysisCut.h @@ -0,0 +1,176 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +// Class for analysis cuts applied on the variables defined in the VarManager +// + +#ifndef AnalysisCut_H +#define AnalysisCut_H + +#include +#include + +//_________________________________________________________________________ +class AnalysisCut : public TNamed +{ + public: + AnalysisCut() = default; + AnalysisCut(const char* name, const char* title); + AnalysisCut(const AnalysisCut& c) = default; + AnalysisCut& operator=(const AnalysisCut& c); + ~AnalysisCut() override; + + // NOTE: Apply a selection on variable "var" to be in the range [cutLow,cutHigh] or outside this range if "exclude" is set to true + // NOTE: If a dependent variable is specified, then the selection is applied only if the dependent variable is in the range [depCutLow,depCutHigh] + // NOTE: or outside if "depCutExclude" is true + template + void AddCut(int var, T1 cutLow, T2 cutHigh, bool exclude = false, + int dependentVar = -1, float depCutLow = 0., float depCutHigh = 0., bool depCutExclude = false, + int dependentVar2 = -1, float depCut2Low = 0., float depCut2High = 0., bool depCut2Exclude = false); + + virtual bool IsSelected(float* values); + + static std::vector fgUsedVars; //! vector of used variables + + struct CutContainer { + short fVar; // variable to be cut upon + float fLow; // lower limit for the var + float fHigh; // upper limit for the var + bool fExclude; // if true, use the selection range for exclusion + + short fDepVar; // first (optional) variable on which the cut depends + float fDepLow; // lower limit for the first dependent var + float fDepHigh; // upper limit for the first dependent var + bool fDepExclude; // if true, then use the dependent variable range as exclusion + + short fDepVar2; // second (optional) variable on which the cut depends + float fDep2Low; // lower limit for the second dependent var + float fDep2High; // upper limit for the second dependent var + bool fDep2Exclude; // if true, then use the dependent variable range as exclusion + + TF1* fFuncLow; // function for the lower limit cut + TF1* fFuncHigh; // function for the upper limit cut + }; + + protected: + std::vector fCuts; + + ClassDef(AnalysisCut, 1); +}; + +//____________________________________________________________________________ +template +void AnalysisCut::AddCut(int var, T1 cutLow, T2 cutHigh, bool exclude, + int dependentVar, float depCutLow, float depCutHigh, bool depCutExclude, + int dependentVar2, float depCut2Low, float depCut2High, bool depCut2Exclude) +{ + // + // Add a cut + // + CutContainer cut = {}; + + if constexpr (std::is_same_v) { + if (dependentVar < 0) { + return; + } + cut.fFuncLow = cutLow; + cut.fLow = -9999.0; + } else { + cut.fFuncLow = nullptr; + cut.fLow = cutLow; + } + if constexpr (std::is_same_v) { + if (dependentVar < 0) { + return; + } + cut.fFuncHigh = cutHigh; + cut.fHigh = -9999.0; + } else { + cut.fFuncHigh = nullptr; + cut.fHigh = cutHigh; + } + cut.fVar = var; + cut.fExclude = exclude; + fgUsedVars.push_back(var); + + cut.fDepVar = dependentVar; + cut.fDepLow = depCutLow; + cut.fDepHigh = depCutHigh; + cut.fDepExclude = depCutExclude; + if (dependentVar != -1) { + fgUsedVars.push_back(dependentVar); + } + + cut.fDepVar2 = dependentVar2; + cut.fDep2Low = depCut2Low; + cut.fDep2High = depCut2High; + cut.fDep2Exclude = depCut2Exclude; + if (dependentVar2 != -1) { + fgUsedVars.push_back(dependentVar2); + } + + fCuts.push_back(cut); +} + +//____________________________________________________________________________ +inline bool AnalysisCut::IsSelected(float* values) +{ + // + // apply the configured cuts + // + // iterate over cuts + for (std::vector::iterator it = fCuts.begin(); it != fCuts.end(); ++it) { + // check whether a dependent variables were enabled and if they are in the requested range + if ((*it).fDepVar != -1) { + bool inRange = (values[(*it).fDepVar] > (*it).fDepLow && values[(*it).fDepVar] <= (*it).fDepHigh); + if (!inRange && !((*it).fDepExclude)) { + continue; + } + if (inRange && (*it).fDepExclude) { + continue; + } + } + if ((*it).fDepVar2 != -1) { + bool inRange = (values[(*it).fDepVar2] > (*it).fDep2Low && values[(*it).fDepVar2] <= (*it).fDep2High); + if (!inRange && !((*it).fDep2Exclude)) { + continue; + } + if (inRange && (*it).fDep2Exclude) { + continue; + } + } + // obtain the low and high cut values (either directly as a value or from a function) + float cutLow, cutHigh; + if ((*it).fFuncLow) { + cutLow = ((*it).fFuncLow)->Eval(values[(*it).fDepVar]); + } else { + cutLow = ((*it).fLow); + } + if ((*it).fFuncHigh) { + cutHigh = ((*it).fFuncHigh)->Eval(values[(*it).fDepVar]); + } else { + cutHigh = ((*it).fHigh); + } + // apply the cut and return the decision + bool inRange = (values[(*it).fVar] >= cutLow && values[(*it).fVar] <= cutHigh); + if (!inRange && !((*it).fExclude)) { + return false; + } + if (inRange && ((*it).fExclude)) { + return false; + } + } + + return true; +} + +#endif diff --git a/Analysis/PWGDQ/include/PWGDQCore/CutsLibrary.h b/Analysis/PWGDQ/include/PWGDQCore/CutsLibrary.h new file mode 100644 index 0000000000000..4b34844464934 --- /dev/null +++ b/Analysis/PWGDQ/include/PWGDQCore/CutsLibrary.h @@ -0,0 +1,194 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include "PWGDQCore/VarManager.h" + +namespace o2::aod +{ +namespace dqcuts +{ +AnalysisCompositeCut* GetCompositeCut(const char* cutName); +AnalysisCut* GetAnalysisCut(const char* cutName); +} // namespace dqcuts +} // namespace o2::aod + +AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) +{ + // + // define composie cuts, typically combinations of all the ingredients needed for a full cut + // + // TODO: Agree on some conventions for the naming + // Possibly think of possible customization of the predefined cuts + + AnalysisCompositeCut* cut = new AnalysisCompositeCut(cutName, cutName); + std::string nameStr = cutName; + + if (!nameStr.compare("jpsiKineAndQuality")) { + cut->AddCut(GetAnalysisCut("jpsiStandardKine")); + cut->AddCut(GetAnalysisCut("electronStandardQuality")); + cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); + return cut; + } + + if (!nameStr.compare("jpsiPID1")) { + cut->AddCut(GetAnalysisCut("jpsiStandardKine")); + cut->AddCut(GetAnalysisCut("electronStandardQuality")); + cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); + cut->AddCut(GetAnalysisCut("electronPID1")); + return cut; + } + + if (!nameStr.compare("jpsiPID2")) { + cut->AddCut(GetAnalysisCut("jpsiStandardKine")); + cut->AddCut(GetAnalysisCut("electronStandardQuality")); + cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); + cut->AddCut(GetAnalysisCut("electronPID2")); + return cut; + } + + if (!nameStr.compare("pairNoCut")) { + cut->AddCut(GetAnalysisCut("pairNoCut")); + return cut; + } + + if (!nameStr.compare("pairMassLow")) { + cut->AddCut(GetAnalysisCut("pairMassLow")); + return cut; + } + + if (!nameStr.compare("pairJpsi")) { + cut->AddCut(GetAnalysisCut("pairJpsi")); + return cut; + } + + if (!nameStr.compare("pairPsi2S")) { + cut->AddCut(GetAnalysisCut("pairPsi2S")); + return cut; + } + + if (!nameStr.compare("pairUpsilon")) { + cut->AddCut(GetAnalysisCut("pairUpsilon")); + return cut; + } + + if (!nameStr.compare("pairJpsiLowPt1")) { + cut->AddCut(GetAnalysisCut("pairJpsi")); + cut->AddCut(GetAnalysisCut("pairPtLow1")); + return cut; + } + + if (!nameStr.compare("pairJpsiLowPt2")) { + cut->AddCut(GetAnalysisCut("pairJpsi")); + cut->AddCut(GetAnalysisCut("pairPtLow2")); + return cut; + } + + delete cut; + return nullptr; +} + +AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) +{ + // + // define here cuts which are likely to be used often + // + AnalysisCut* cut = new AnalysisCut(cutName, cutName); + std::string nameStr = cutName; + + if (!nameStr.compare("eventStandard")) { + cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + cut->AddCut(VarManager::kIsINT7, 0.5, 1.5); + return cut; + } + + if (!nameStr.compare("int7vtxZ5")) { + cut->AddCut(VarManager::kVtxZ, -5.0, 5.0); + cut->AddCut(VarManager::kIsINT7, 0.5, 1.5); + return cut; + } + + if (!nameStr.compare("jpsiStandardKine")) { + cut->AddCut(VarManager::kPt, 1.0, 1000.0); + cut->AddCut(VarManager::kEta, -0.9, 0.9); + return cut; + } + + if (!nameStr.compare("electronStandardQuality")) { + cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); + cut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); + cut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); + cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + cut->AddCut(VarManager::kITSchi2, 0.1, 36.0); + cut->AddCut(VarManager::kTPCncls, 100.0, 161.); + return cut; + } + + if (!nameStr.compare("standardPrimaryTrack")) { + cut->AddCut(VarManager::kTrackDCAxy, -1.0, 1.0); + cut->AddCut(VarManager::kTrackDCAz, -3.0, 3.0); + return cut; + } + + TF1* cutLow1 = new TF1("cutLow1", "pol1", 0., 10.); + cutLow1->SetParameters(130., -40.0); + if (!nameStr.compare("electronPID1")) { + cut->AddCut(VarManager::kTPCsignal, 70., 100.); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + return cut; + } + + if (!nameStr.compare("electronPID2")) { + cut->AddCut(VarManager::kTPCsignal, 73., 100.); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + return cut; + } + + if (!nameStr.compare("pairNoCut")) { + cut->AddCut(VarManager::kMass, 0.0, 1000.0); + return cut; + } + + if (!nameStr.compare("pairMassLow")) { + cut->AddCut(VarManager::kMass, 2.5, 1000.0); + return cut; + } + + if (!nameStr.compare("pairJpsi")) { + cut->AddCut(VarManager::kMass, 2.8, 3.3); + return cut; + } + + if (!nameStr.compare("pairPsi2S")) { + cut->AddCut(VarManager::kMass, 3.4, 3.9); + return cut; + } + + if (!nameStr.compare("pairUpsilon")) { + cut->AddCut(VarManager::kMass, 8.0, 11.0); + return cut; + } + + if (!nameStr.compare("pairPtLow1")) { + cut->AddCut(VarManager::kPt, 2.0, 1000.0); + return cut; + } + + if (!nameStr.compare("pairPtLow2")) { + cut->AddCut(VarManager::kPt, 5.0, 1000.0); + return cut; + } + + delete cut; + return nullptr; +} diff --git a/Analysis/PWGDQ/include/PWGDQCore/HistogramManager.h b/Analysis/PWGDQ/include/PWGDQCore/HistogramManager.h new file mode 100644 index 0000000000000..893b5cc755132 --- /dev/null +++ b/Analysis/PWGDQ/include/PWGDQCore/HistogramManager.h @@ -0,0 +1,118 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +// Class to define and fill histograms +// + +#ifndef HistogramManager_H +#define HistogramManager_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class HistogramManager : public TNamed +{ + + public: + HistogramManager(); + HistogramManager(const char* name, const char* title, const int maxNVars); + ~HistogramManager() override; + + enum Constants { + kNothing = -1 + }; + + void SetMainHistogramList(THashList* list) + { + if (fMainList) { + delete fMainList; + } + fMainList = list; + } + + // Create a new histogram class + void AddHistClass(const char* histClass); + // Create a new histogram in the class with name and title + // The type of histogram is deduced from the parameters specified by the user + // The binning for at least one dimension needs to be specified, namely: nXbins, xmin, xmax, varX which will result in a TH1F histogram + // If the value for a variable is left as -1 then that is considered to not be used + // Up to 3 dimensional histograms can be defined with this function + // If isProfile = true, the last specified variable is the one being averaged + // For the case of TProfile3D, the user must use the varT to specify the averaged variable + // If specified, varW will be used as weight in the TH1::Fill() functions. + // If specified, the xLabels, yLabels, zLabels will be used to set the labels of the x,y,z axes, respectively. + // The axis titles will be set by default, if those were specified (e.g. taken from a Variable Manager) + // Otherwise these can be specified in the title string by separating them with semi-colons ";" + void AddHistogram(const char* histClass, const char* name, const char* title, bool isProfile, + int nXbins, double xmin, double xmax, int varX, + int nYbins = 0, double ymin = 0, double ymax = 0, int varY = -1, + int nZbins = 0, double zmin = 0, double zmax = 0, int varZ = -1, + const char* xLabels = "", const char* yLabels = "", const char* zLabels = "", + int varT = -1, int varW = -1); + // Similar to the above function, with the difference that the user can specify non-equidistant binning + void AddHistogram(const char* histClass, const char* name, const char* title, bool isProfile, + int nXbins, double* xbins, int varX, + int nYbins = 0, double* ybins = nullptr, int varY = -1, + int nZbins = 0, double* zbins = nullptr, int varZ = -1, + const char* xLabels = "", const char* yLabels = "", const char* zLabels = "", + int varT = -1, int varW = -1); + // Create a THn histogram (either THnF or THnSparse) with equidistant binning + void AddHistogram(const char* histClass, const char* name, const char* title, + int nDimensions, int* vars, int* nBins, double* xmin, double* xmax, + TString* axLabels = nullptr, int varW = -1, bool useSparse = kFALSE); + // Create a THn histogram (either THnF or THnSparse) with non-equidistant binning + void AddHistogram(const char* histClass, const char* name, const char* title, + int nDimensions, int* vars, TArrayD* binLimits, + TString* axLabels = nullptr, int varW = -1, bool useSparse = kFALSE); + + void FillHistClass(const char* className, float* values); + + void SetUseDefaultVariableNames(bool flag) { fUseDefaultVariableNames = flag; }; + void SetDefaultVarNames(TString* vars, TString* units); + const bool* GetUsedVars() const { return fUsedVars; } + + THashList* GetMainHistogramList() { return fMainList; } // get a histogram list + + unsigned long int GetAllocatedBins() const { return fBinsAllocated; } + void Print(Option_t*) const override; + + private: + THashList* fMainList; // master histogram list + int fNVars; // number of variables handled (tipically from the Variable Manager) + + bool* fUsedVars; //! flags of used variables + std::map<std::string, std::list<std::vector<int>>> fVariablesMap; //! map holding identifiers for all variables needed by histograms + + // various + bool fUseDefaultVariableNames; //! toggle the usage of default variable names and units + unsigned long int fBinsAllocated; //! number of allocated bins + TString* fVariableNames; //! variable names + TString* fVariableUnits; //! variable units + + void MakeAxisLabels(TAxis* ax, const char* labels); + + HistogramManager& operator=(const HistogramManager& c); + HistogramManager(const HistogramManager& c); + + ClassDef(HistogramManager, 1) +}; + +#endif diff --git a/Analysis/PWGDQ/include/PWGDQCore/HistogramsLibrary.h b/Analysis/PWGDQ/include/PWGDQCore/HistogramsLibrary.h new file mode 100644 index 0000000000000..361bb3fea9edc --- /dev/null +++ b/Analysis/PWGDQ/include/PWGDQCore/HistogramsLibrary.h @@ -0,0 +1,126 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/VarManager.h" + +namespace o2::aod +{ +namespace dqhistograms +{ +void DefineHistograms(HistogramManager* hm, const char* histClass, const char* groupName, const char* subGroupName = ""); +} +} // namespace o2::aod + +void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* histClass, const char* groupName, const char* subGroupName) +{ + // + // Add a predefined group of histograms to the HistogramManager hm and histogram class histClass + // NOTE: The groupName and subGroupName arguments may contain several keywords, but the user should take care of + // ambiguities. TODO: fix it! + // NOTE: All of the histograms which match any of the group or subgroup names will be added to the same histogram class !! + // So one has to make sure not to mix e.g. event-wise with track-wise histograms + // NOTE: The subgroup name can be empty. In this case just a minimal set of histograms corresponding to the group name will be defined + // + TString groupStr = groupName; + groupStr.ToLower(); + TString subGroupStr = subGroupName; + subGroupStr.ToLower(); + if (groupStr.Contains("event")) { + hm->AddHistogram(histClass, "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); + + if (subGroupStr.Contains("trigger")) { + hm->AddHistogram(histClass, "IsINT7", "Is INT7", false, 2, -0.5, 1.5, VarManager::kIsINT7); + hm->AddHistogram(histClass, "IsINT7inMUON", "INT7inMUON", false, 2, -0.5, 1.5, VarManager::kIsINT7inMUON); + hm->AddHistogram(histClass, "IsMuonSingleLowPt7", "Is MuonSingleLowPt7", false, 2, -0.5, 1.5, VarManager::kIsMuonSingleLowPt7); + hm->AddHistogram(histClass, "IsMuonUnlikeLowPt7", "Is MuonUnlikeLowPt7", false, 2, -0.5, 1.5, VarManager::kIsMuonUnlikeLowPt7); + hm->AddHistogram(histClass, "IsMuonLikeLowPt7", "Is MuonLikeLowPt7", false, 2, -0.5, 1.5, VarManager::kIsMuonLikeLowPt7); + } + if (subGroupStr.Contains("vtx")) { + hm->AddHistogram(histClass, "VtxX", "Vtx X", false, 100, -0.5, 0.5, VarManager::kVtxX); + hm->AddHistogram(histClass, "VtxY", "Vtx Y", false, 100, -0.5, 0.5, VarManager::kVtxY); + hm->AddHistogram(histClass, "VtxYVtxX", "Vtx Y vs Vtx X", false, 50, -0.5, 0.5, VarManager::kVtxX, 50, -0.5, 0.5, VarManager::kVtxY); + } + if (subGroupStr.Contains("vtxpp")) { + hm->AddHistogram(histClass, "VtxNContrib", "Vtx n contributors", false, 100, 0.0, 100.0, VarManager::kVtxNcontrib); + } + if (subGroupStr.Contains("vtxPbPb")) { + hm->AddHistogram(histClass, "VtxNContrib", "Vtx n contributors", false, 100, 0.0, 20000.0, VarManager::kVtxNcontrib); + } + if (subGroupStr.Contains("cent")) { + hm->AddHistogram(histClass, "CentV0M", "CentV0M", false, 100, 0., 100., VarManager::kCentVZERO); + hm->AddHistogram(histClass, "CentV0M_vtxZ", "CentV0M vs Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ, 20, 0., 100., VarManager::kCentVZERO); + } + } + + if (groupStr.Contains("track")) { + hm->AddHistogram(histClass, "Pt", "p_{T} distribution", false, 200, 0.0, 20.0, VarManager::kPt); + hm->AddHistogram(histClass, "Eta", "#eta distribution", false, 500, -5.0, 5.0, VarManager::kEta); + hm->AddHistogram(histClass, "Phi", "#varphi distribution", false, 500, -6.3, 6.3, VarManager::kPhi); + + if (subGroupStr.Contains("kine")) { + hm->AddHistogram(histClass, "Phi_Eta", "#phi vs #eta distribution", false, 200, -5.0, 5.0, VarManager::kEta, 200, -6.3, 6.3, VarManager::kPhi); + hm->AddHistogram(histClass, "Eta_Pt", "", false, 20, -1.0, 1.0, VarManager::kEta, 100, 0.0, 20.0, VarManager::kPt); + hm->AddHistogram(histClass, "Px", "p_{x} distribution", false, 200, 0.0, 20.0, VarManager::kPx); + hm->AddHistogram(histClass, "Py", "p_{y} distribution", false, 200, 0.0, 20.0, VarManager::kPy); + hm->AddHistogram(histClass, "Pz", "p_{z} distribution", false, 200, 0.0, 20.0, VarManager::kPz); + } + if (subGroupStr.Contains("its")) { + hm->AddHistogram(histClass, "ITSncls", "Number of cluster in ITS", false, 8, -0.5, 7.5, VarManager::kITSncls); + hm->AddHistogram(histClass, "ITSchi2", "ITS chi2", false, 100, 0.0, 50.0, VarManager::kITSchi2); + hm->AddHistogram(histClass, "IsITSrefit", "", false, 2, -0.5, 1.5, VarManager::kIsITSrefit); + hm->AddHistogram(histClass, "IsSPDany", "", false, 2, -0.5, 1.5, VarManager::kIsSPDany); + } + if (subGroupStr.Contains("tpc")) { + hm->AddHistogram(histClass, "TPCncls", "Number of cluster in TPC", false, 160, -0.5, 159.5, VarManager::kTPCncls); + hm->AddHistogram(histClass, "TPCncls_Run", "Number of cluster in TPC", true, VarManager::GetNRuns(), 0.5, 0.5 + VarManager::GetNRuns(), VarManager::kRunId, + 10, -0.5, 159.5, VarManager::kTPCncls, 10, 0., 1., VarManager::kNothing, VarManager::GetRunStr().Data()); + hm->AddHistogram(histClass, "IsTPCrefit", "", false, 2, -0.5, 1.5, VarManager::kIsTPCrefit); + hm->AddHistogram(histClass, "TPCchi2", "TPC chi2", false, 100, 0.0, 10.0, VarManager::kTPCchi2); + } + if (subGroupStr.Contains("tpcpid")) { + hm->AddHistogram(histClass, "TPCdedx_pIN", "TPC dE/dx vs pIN", false, 200, 0.0, 20.0, VarManager::kPin, 200, 0.0, 200., VarManager::kTPCsignal); + } + if (subGroupStr.Contains("dca")) { + hm->AddHistogram(histClass, "DCAxy", "DCAxy", false, 100, -3.0, 3.0, VarManager::kTrackDCAxy); + hm->AddHistogram(histClass, "DCAz", "DCAz", false, 100, -5.0, 5.0, VarManager::kTrackDCAz); + } + if (subGroupStr.Contains("muon")) { + hm->AddHistogram(histClass, "InvBendingMom", "", false, 100, 0.0, 1.0, VarManager::kMuonInvBendingMomentum); + hm->AddHistogram(histClass, "ThetaX", "", false, 100, -1.0, 1.0, VarManager::kMuonThetaX); + hm->AddHistogram(histClass, "ThetaY", "", false, 100, -2.0, 2.0, VarManager::kMuonThetaY); + hm->AddHistogram(histClass, "ZMu", "", false, 100, -30.0, 30.0, VarManager::kMuonZMu); + hm->AddHistogram(histClass, "BendingCoor", "", false, 100, 0.32, 0.35, VarManager::kMuonBendingCoor); + hm->AddHistogram(histClass, "NonBendingCoor", "", false, 100, 0.065, 0.07, VarManager::kMuonNonBendingCoor); + hm->AddHistogram(histClass, "Chi2", "", false, 100, 0.0, 200.0, VarManager::kMuonChi2); + hm->AddHistogram(histClass, "Chi2MatchTrigger", "", false, 100, 0.0, 20.0, VarManager::kMuonChi2MatchTrigger); + hm->AddHistogram(histClass, "RAtAbsorberEnd", "", false, 140, 10, 150, VarManager::kMuonRAtAbsorberEnd); + hm->AddHistogram(histClass, "p x dca", "", false, 700, 0.0, 700, VarManager::kMuonRAtAbsorberEnd); + } + } + + if (groupStr.Contains("pair")) { + hm->AddHistogram(histClass, "Mass", "", false, 125, 0.0, 5.0, VarManager::kMass); + hm->AddHistogram(histClass, "Mass_Pt", "", false, 125, 0.0, 5.0, VarManager::kMass, 100, 0.0, 20.0, VarManager::kPt); + hm->AddHistogram(histClass, "Eta_Pt", "", false, 125, -2.0, 2.0, VarManager::kEta, 100, 0.0, 20.0, VarManager::kPt); + hm->AddHistogram(histClass, "Mass_VtxZ", "", true, 30, -15.0, 15.0, VarManager::kVtxZ, 100, 0.0, 20.0, VarManager::kMass); + } + + if (groupStr.Contains("pair-hadron-mass")) { + hm->AddHistogram(histClass, "Mass_Pt", "", false, 40, 0.0, 20.0, VarManager::kPairMass, 40, 0.0, 20.0, VarManager::kPairPt); + } + + if (groupStr.Contains("pair-hadron-correlation")) { + hm->AddHistogram(histClass, "DeltaEta_DeltaPhi", "", false, 20, -2.0, 2.0, VarManager::kDeltaEta, 50, -8.0, 8.0, VarManager::kDeltaPhi); + hm->AddHistogram(histClass, "DeltaEta_DeltaPhiSym", "", false, 20, -2.0, 2.0, VarManager::kDeltaEta, 50, -8.0, 8.0, VarManager::kDeltaPhiSym); + } +} diff --git a/Analysis/PWGDQ/include/PWGDQCore/VarManager.h b/Analysis/PWGDQ/include/PWGDQCore/VarManager.h new file mode 100644 index 0000000000000..412d11d79f9f3 --- /dev/null +++ b/Analysis/PWGDQ/include/PWGDQCore/VarManager.h @@ -0,0 +1,533 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +// Class to handle analysis variables +// + +#ifndef VarManager_H +#define VarManager_H + +#include <TObject.h> +#include <TString.h> +#include <Math/Vector4D.h> +#include <TMath.h> + +#include <vector> +#include <map> +#include <cmath> + +#include "Framework/DataTypes.h" +#include "AnalysisCore/TriggerAliases.h" + +// TODO: create an array holding these constants for all needed particles or check for a place where these are already defined +static const float fgkElectronMass = 0.000511; // GeV +static const float fgkMuonMass = 0.105; // GeV + +//_________________________________________________________________________ +class VarManager : public TObject +{ + public: + // map the information contained in the objects passed to the Fill functions + enum ObjTypes { + BC = BIT(0), + Collision = BIT(1), + CollisionCent = BIT(2), + ReducedEvent = BIT(3), + ReducedEventExtended = BIT(4), + ReducedEventVtxCov = BIT(5), + Track = BIT(0), + TrackCov = BIT(1), + TrackExtra = BIT(2), + TrackPID = BIT(3), + TrackDCA = BIT(4), + TrackSelection = BIT(5), + // TODO: Central model MUON variables to be added + ReducedTrack = BIT(6), + ReducedTrackBarrel = BIT(7), + ReducedTrackBarrelCov = BIT(8), + ReducedTrackBarrelPID = BIT(9), + ReducedTrackMuon = BIT(10), + Pair = BIT(11) + }; + + enum PairCandidateType { + kJpsiToEE, // J/psi -> e+ e- + kJpsiToMuMu, // J/psi -> mu+ mu- + kNMaxCandidateTypes + }; + + public: + enum Variables { + kNothing = -1, + // Run wise variables + kRunNo = 0, + kRunId, + kNRunWiseVariables, + + // Event wise variables // Daria: imedjat ai ziua mja + kCollisionTime, + kBC, + kIsPhysicsSelection, + kIsINT7, + kIsEMC7, + kIsINT7inMUON, + kIsMuonSingleLowPt7, + kIsMuonUnlikeLowPt7, + kIsMuonLikeLowPt7, + kVtxX, + kVtxY, + kVtxZ, + kVtxNcontrib, + kVtxCovXX, + kVtxCovXY, + kVtxCovXZ, + kVtxCovYY, + kVtxCovYZ, + kVtxCovZZ, + kVtxChi2, + kCentVZERO, + kNEventWiseVariables, + + // Basic track(pair) wise variables + kPt, + kEta, + kPhi, + kP, + kPx, + kPy, + kPz, + kRap, + kMass, + kCharge, + kNBasicTrackVariables, + + // Barrel track variables + kPin, + kIsGlobalTrack, + kIsGlobalTrackSDD, + kIsITSrefit, + kIsSPDany, + kIsSPDfirst, + kIsSPDboth, + kITSncls, + kITSchi2, + kITSlayerHit, + kIsTPCrefit, + kTPCncls, + kTPCchi2, + kTPCsignal, + kTRDsignal, + kTOFsignal, + kTOFbeta, + kTrackLength, + kTrackDCAxy, + kTrackDCAz, + kTrackCYY, + kTrackCZZ, + kTrackCSnpSnp, + kTrackCTglTgl, + kTrackC1Pt21Pt2, + kTPCnSigmaEl, + kTPCnSigmaMu, + kTPCnSigmaPi, + kTPCnSigmaKa, + kTPCnSigmaPr, + kTOFnSigmaEl, + kTOFnSigmaMu, + kTOFnSigmaPi, + kTOFnSigmaKa, + kTOFnSigmaPr, + kNBarrelTrackVariables, + + // Muon track variables + kMuonInvBendingMomentum, + kMuonThetaX, + kMuonThetaY, + kMuonZMu, + kMuonBendingCoor, + kMuonNonBendingCoor, + kMuonRAtAbsorberEnd, + kMuonPDca, + kMuonChi2, + kMuonChi2MatchTrigger, + kNMuonTrackVariables, + + // Pair variables + kCandidateId, + kPairType, + kPairLxy, + kNPairVariables, + + // Candidate-track correlation variables + kPairMass, + kPairPt, + kPairEta, + kPairPhi, + kDeltaEta, + kDeltaPhi, + kDeltaPhiSym, + kNCorrelationVariables, + + kNVars + }; // end of Variables enumeration + + static TString fgVariableNames[kNVars]; // variable names + static TString fgVariableUnits[kNVars]; // variable units + static void SetDefaultVarNames(); + + static void SetUseVariable(int var) + { + if (var >= 0 && var < kNVars) { + fgUsedVars[var] = kTRUE; + } + SetVariableDependencies(); + } + static void SetUseVars(const bool* usedVars) + { + for (int i = 0; i < kNVars; ++i) { + if (usedVars[i]) { + fgUsedVars[i] = true; // overwrite only the variables that are being used since there are more channels to modify the used variables array, independently + } + } + SetVariableDependencies(); + } + static void SetUseVars(const std::vector<int> usedVars) + { + for (auto& var : usedVars) { + fgUsedVars[var] = true; + } + } + static bool GetUsedVar(int var) + { + if (var >= 0 && var < kNVars) { + return fgUsedVars[var]; + } + return false; + } + + static void SetRunNumbers(int n, int* runs); + static int GetNRuns() + { + return fgRunMap.size(); + } + static TString GetRunStr() + { + return fgRunStr; + } + + template <uint32_t fillMap, typename T> + static void FillEvent(T const& event, float* values = nullptr); + template <uint32_t fillMap, typename T> + static void FillTrack(T const& track, float* values = nullptr); + template <typename T> + static void FillPair(T const& t1, T const& t2, float* values = nullptr, PairCandidateType pairType = kJpsiToEE); + template <typename T1, typename T2> + static void FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* values = nullptr, float hadronMass = 0.0f); + + public: + VarManager(); + ~VarManager() override; + + static float fgValues[kNVars]; // array holding all variables computed during analysis + static void ResetValues(int startValue = 0, int endValue = kNVars, float* values = nullptr); + + private: + static bool fgUsedVars[kNVars]; // holds flags for when the corresponding variable is needed (e.g., in the histogram manager, in cuts, mixing handler, etc.) + static void SetVariableDependencies(); // toggle those variables on which other used variables might depend + + static std::map<int, int> fgRunMap; // map of runs to be used in histogram axes + static TString fgRunStr; // semi-colon separated list of runs, to be used for histogram axis labels + + static void FillEventDerived(float* values = nullptr); + static void FillTrackDerived(float* values = nullptr); + + VarManager& operator=(const VarManager& c); + VarManager(const VarManager& c); + + ClassDef(VarManager, 1) +}; + +template <uint32_t fillMap, typename T> +void VarManager::FillEvent(T const& event, float* values) +{ + if (!values) { + values = fgValues; + } + + if constexpr ((fillMap & BC) > 0) { + values[kRunNo] = event.bc().runNumber(); // accessed via Collisions table + values[kBC] = event.bc().globalBC(); + } + + if constexpr ((fillMap & Collision) > 0) { + if (fgUsedVars[kIsINT7]) { + values[kIsINT7] = (event.alias()[kINT7] > 0); + } + if (fgUsedVars[kIsEMC7]) { + values[kIsEMC7] = (event.alias()[kEMC7] > 0); + } + if (fgUsedVars[kIsINT7inMUON]) { + values[kIsINT7inMUON] = (event.alias()[kINT7inMUON] > 0); + } + if (fgUsedVars[kIsMuonSingleLowPt7]) { + values[kIsMuonSingleLowPt7] = (event.alias()[kMuonSingleLowPt7] > 0); + } + if (fgUsedVars[kIsMuonUnlikeLowPt7]) { + values[kIsMuonUnlikeLowPt7] = (event.alias()[kMuonUnlikeLowPt7] > 0); + } + if (fgUsedVars[kIsMuonLikeLowPt7]) { + values[kIsMuonLikeLowPt7] = (event.alias()[kMuonLikeLowPt7] > 0); + } + values[kVtxX] = event.posX(); + values[kVtxY] = event.posY(); + values[kVtxZ] = event.posZ(); + values[kVtxNcontrib] = event.numContrib(); + values[kCollisionTime] = event.collisionTime(); + values[kVtxCovXX] = event.covXX(); + values[kVtxCovXY] = event.covXY(); + values[kVtxCovXZ] = event.covXZ(); + values[kVtxCovYY] = event.covYY(); + values[kVtxCovYZ] = event.covYZ(); + values[kVtxCovZZ] = event.covZZ(); + values[kVtxChi2] = event.chi2(); + } + + if constexpr ((fillMap & CollisionCent) > 0) { + values[kCentVZERO] = event.centV0M(); + } + + // TODO: need to add EvSels and Cents tables, etc. in case of the central data model + + if constexpr ((fillMap & ReducedEvent) > 0) { + values[kRunNo] = event.runNumber(); + values[kVtxX] = event.posX(); + values[kVtxY] = event.posY(); + values[kVtxZ] = event.posZ(); + values[kVtxNcontrib] = event.numContrib(); + } + + if constexpr ((fillMap & ReducedEventExtended) > 0) { + values[kBC] = event.globalBC(); + values[kCentVZERO] = event.centV0M(); + if (fgUsedVars[kIsINT7]) { + values[kIsINT7] = (event.triggerAlias() & (uint32_t(1) << kINT7)) > 0; + } + if (fgUsedVars[kIsEMC7]) { + values[kIsEMC7] = (event.triggerAlias() & (uint32_t(1) << kEMC7)) > 0; + } + if (fgUsedVars[kIsINT7inMUON]) { + values[kIsINT7inMUON] = (event.triggerAlias() & (uint32_t(1) << kINT7inMUON)) > 0; + } + if (fgUsedVars[kIsMuonSingleLowPt7]) { + values[kIsMuonSingleLowPt7] = (event.triggerAlias() & (uint32_t(1) << kMuonSingleLowPt7)) > 0; + } + if (fgUsedVars[kIsMuonUnlikeLowPt7]) { + values[kIsMuonUnlikeLowPt7] = (event.triggerAlias() & (uint32_t(1) << kMuonUnlikeLowPt7)) > 0; + } + if (fgUsedVars[kIsMuonLikeLowPt7]) { + values[kIsMuonLikeLowPt7] = (event.triggerAlias() & (uint32_t(1) << kMuonLikeLowPt7)) > 0; + } + } + + if constexpr ((fillMap & ReducedEventVtxCov) > 0) { + values[kVtxCovXX] = event.covXX(); + values[kVtxCovXY] = event.covXY(); + values[kVtxCovXZ] = event.covXZ(); + values[kVtxCovYY] = event.covYY(); + values[kVtxCovYZ] = event.covYZ(); + values[kVtxCovZZ] = event.covZZ(); + values[kVtxChi2] = event.chi2(); + } + + FillEventDerived(values); +} + +template <uint32_t fillMap, typename T> +void VarManager::FillTrack(T const& track, float* values) +{ + if (!values) { + values = fgValues; + } + + if constexpr ((fillMap & Track) > 0 || (fillMap & ReducedTrack) > 0) { + values[kPt] = track.pt(); + if (fgUsedVars[kPx]) { + values[kPx] = track.px(); + } + if (fgUsedVars[kPy]) { + values[kPy] = track.py(); + } + if (fgUsedVars[kPz]) { + values[kPz] = track.pz(); + } + values[kEta] = track.eta(); + values[kPhi] = track.phi(); + values[kCharge] = track.charge(); + } + + if constexpr ((fillMap & TrackExtra) > 0 || (fillMap & ReducedTrackBarrel) > 0) { + values[kPin] = track.tpcInnerParam(); + if (fgUsedVars[kIsITSrefit]) { + values[kIsITSrefit] = (track.flags() & (uint32_t(1) << 0)) > 0; + } + if (fgUsedVars[kIsTPCrefit]) { + values[kIsTPCrefit] = (track.flags() & (uint32_t(1) << 1)) > 0; + } + if (fgUsedVars[kIsSPDfirst]) { + values[kIsSPDfirst] = (track.itsClusterMap() & uint8_t(1)) > 0; + } + if (fgUsedVars[kIsSPDboth]) { + values[kIsSPDboth] = (track.itsClusterMap() & uint8_t(3)) > 0; + } + if (fgUsedVars[kIsSPDany]) { + values[kIsSPDany] = (track.itsClusterMap() & uint8_t(1)) || (track.itsClusterMap() & uint8_t(2)); + } + values[kITSchi2] = track.itsChi2NCl(); + values[kTPCncls] = track.tpcNClsFound(); + values[kTPCchi2] = track.tpcChi2NCl(); + values[kTrackLength] = track.length(); + + if constexpr ((fillMap & TrackExtra) > 0) { + if (fgUsedVars[kITSncls]) { + values[kITSncls] = track.itsNCls(); // dynamic column + } + } + if constexpr ((fillMap & ReducedTrackBarrel) > 0) { + if (fgUsedVars[kITSncls]) { + values[kITSncls] = 0.0; + for (int i = 0; i < 6; ++i) { + values[kITSncls] += ((track.itsClusterMap() & (1 << i)) ? 1 : 0); + } + } + values[kTrackDCAxy] = track.dcaXY(); + values[kTrackDCAz] = track.dcaZ(); + } + } + + if constexpr ((fillMap & TrackDCA) > 0) { + values[kTrackDCAxy] = track.dcaXY(); + values[kTrackDCAz] = track.dcaZ(); + } + + if constexpr ((fillMap & TrackSelection) > 0) { + values[kIsGlobalTrack] = track.isGlobalTrack(); + values[kIsGlobalTrackSDD] = track.isGlobalTrackSDD(); + } + + if constexpr ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0) { + values[kTrackCYY] = track.cYY(); + values[kTrackCZZ] = track.cZZ(); + values[kTrackCSnpSnp] = track.cSnpSnp(); + values[kTrackCTglTgl] = track.cTglTgl(); + values[kTrackC1Pt21Pt2] = track.c1Pt21Pt2(); + } + + if constexpr ((fillMap & TrackPID) > 0 || (fillMap & ReducedTrackBarrelPID) > 0) { + values[kTPCnSigmaEl] = track.tpcNSigmaEl(); + values[kTPCnSigmaMu] = track.tpcNSigmaMu(); + values[kTPCnSigmaPi] = track.tpcNSigmaPi(); + values[kTPCnSigmaKa] = track.tpcNSigmaKa(); + values[kTPCnSigmaPr] = track.tpcNSigmaPr(); + values[kTOFnSigmaEl] = track.tofNSigmaEl(); + values[kTOFnSigmaMu] = track.tofNSigmaMu(); + values[kTOFnSigmaPi] = track.tofNSigmaPi(); + values[kTOFnSigmaKa] = track.tofNSigmaKa(); + values[kTOFnSigmaPr] = track.tofNSigmaPr(); + values[kTPCsignal] = track.tpcSignal(); + values[kTRDsignal] = track.trdSignal(); + values[kTOFsignal] = track.tofSignal(); + values[kTOFbeta] = track.beta(); + } + + if constexpr ((fillMap & ReducedTrackMuon) > 0) { + values[kMuonInvBendingMomentum] = track.inverseBendingMomentum(); + values[kMuonThetaX] = track.thetaX(); + values[kMuonThetaY] = track.thetaY(); + values[kMuonZMu] = track.zMu(); + values[kMuonBendingCoor] = track.bendingCoor(); + values[kMuonNonBendingCoor] = track.nonBendingCoor(); + values[kMuonRAtAbsorberEnd] = track.rAtAbsorberEnd(); + values[kMuonPDca] = track.pDca(); + values[kMuonChi2] = track.chi2(); + values[kMuonChi2MatchTrigger] = track.chi2MatchTrigger(); + } + + if constexpr ((fillMap & Pair) > 0) { + values[kMass] = track.mass(); + } + + FillTrackDerived(values); +} + +template <typename T> +void VarManager::FillPair(T const& t1, T const& t2, float* values, PairCandidateType pairType) +{ + if (!values) { + values = fgValues; + } + + float m1 = fgkElectronMass; + float m2 = fgkElectronMass; + if (pairType == kJpsiToMuMu) { + m1 = fgkMuonMass; + m2 = fgkMuonMass; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), m2); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + values[kPhi] = v12.Phi(); + values[kRap] = -v12.Rapidity(); +} + +template <typename T1, typename T2> +void VarManager::FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* values, float hadronMass) +{ + if (!values) { + values = fgValues; + } + + if (fgUsedVars[kPairMass] || fgUsedVars[kPairPt] || fgUsedVars[kPairEta] || fgUsedVars[kPairPhi]) { + ROOT::Math::PtEtaPhiMVector v1(dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.mass()); + ROOT::Math::PtEtaPhiMVector v2(hadron.pt(), hadron.eta(), hadron.phi(), hadronMass); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + values[kPairMass] = v12.M(); + values[kPairPt] = v12.Pt(); + values[kPairEta] = v12.Eta(); + values[kPairPhi] = v12.Phi(); + } + if (fgUsedVars[kDeltaPhi]) { + double delta = dilepton.phi() - hadron.phi(); + if (delta > 3.0 / 2.0 * TMath::Pi()) { + delta -= 2.0 * TMath::Pi(); + } + if (delta < -0.5 * TMath::Pi()) { + delta += 2.0 * TMath::Pi(); + } + values[kDeltaPhi] = delta; + } + if (fgUsedVars[kDeltaPhiSym]) { + double delta = TMath::Abs(dilepton.phi() - hadron.phi()); + if (delta > TMath::Pi()) { + delta = 2 * TMath::Pi() - delta; + } + values[kDeltaPhiSym] = delta; + } + if (fgUsedVars[kDeltaEta]) { + values[kDeltaEta] = dilepton.eta() - hadron.eta(); + } +} + +#endif diff --git a/Analysis/PWGDQ/src/AnalysisCompositeCut.cxx b/Analysis/PWGDQ/src/AnalysisCompositeCut.cxx new file mode 100644 index 0000000000000..160131d9c74f0 --- /dev/null +++ b/Analysis/PWGDQ/src/AnalysisCompositeCut.cxx @@ -0,0 +1,69 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PWGDQCore/AnalysisCompositeCut.h" + +ClassImp(AnalysisCompositeCut) + + //____________________________________________________________________________ + AnalysisCompositeCut::AnalysisCompositeCut(bool useAND) : AnalysisCut(), + fOptionUseAND(useAND), + fCutList(), + fCompositeCutList() +{ + // + // default constructor + // +} + +//____________________________________________________________________________ +AnalysisCompositeCut::AnalysisCompositeCut(const char* name, const char* title, bool useAND) : AnalysisCut(name, title), + fOptionUseAND(useAND), + fCutList(), + fCompositeCutList() +{ + // + // named constructor + // +} + +//____________________________________________________________________________ +AnalysisCompositeCut::~AnalysisCompositeCut() = default; + +//____________________________________________________________________________ +bool AnalysisCompositeCut::IsSelected(float* values) +{ + // + // apply cuts + // + for (std::vector<AnalysisCut>::iterator it = fCutList.begin(); it < fCutList.end(); ++it) { + if (fOptionUseAND && !(*it).IsSelected(values)) { + return false; + } + if (!fOptionUseAND && (*it).IsSelected(values)) { + return true; + } + } + + for (std::vector<AnalysisCompositeCut>::iterator it = fCompositeCutList.begin(); it < fCompositeCutList.end(); ++it) { + if (fOptionUseAND && !(*it).IsSelected(values)) { + return false; + } + if (!fOptionUseAND && (*it).IsSelected(values)) { + return true; + } + } + + if (fOptionUseAND) { + return true; + } else { + return false; + } +} diff --git a/Analysis/PWGDQ/src/AnalysisCut.cxx b/Analysis/PWGDQ/src/AnalysisCut.cxx new file mode 100644 index 0000000000000..e6ba9a44abada --- /dev/null +++ b/Analysis/PWGDQ/src/AnalysisCut.cxx @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PWGDQCore/AnalysisCut.h" + +ClassImp(AnalysisCut); + +std::vector<int> AnalysisCut::fgUsedVars = {}; + +//____________________________________________________________________________ +AnalysisCut::AnalysisCut(const char* name, const char* title) : TNamed(name, title), + fCuts() +{ + // + // named constructor + // +} + +//____________________________________________________________________________ +AnalysisCut& AnalysisCut::operator=(const AnalysisCut& c) +{ + // + // assignment + // + if (this != &c) { + TNamed::operator=(c); + fCuts = c.fCuts; + } + return (*this); +} + +//____________________________________________________________________________ +AnalysisCut::~AnalysisCut() = default; diff --git a/Analysis/Core/src/HistogramManager.cxx b/Analysis/PWGDQ/src/HistogramManager.cxx similarity index 87% rename from Analysis/Core/src/HistogramManager.cxx rename to Analysis/PWGDQ/src/HistogramManager.cxx index 68581d005942a..eaef3413f6f41 100644 --- a/Analysis/Core/src/HistogramManager.cxx +++ b/Analysis/PWGDQ/src/HistogramManager.cxx @@ -8,7 +8,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Analysis/HistogramManager.h" +#include "PWGDQCore/HistogramManager.h" #include <iostream> #include <fstream> @@ -140,25 +140,32 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // deduce the dimension of the histogram from parameters // NOTE: in case of profile histograms, one extra variable is needed int dimension = 1; - if (varY > kNothing) + if (varY > kNothing) { dimension = 2; - if (varZ > kNothing) + } + if (varZ > kNothing) { dimension = 3; + } // tokenize the title string; the user may include in it axis titles which will overwrite the defaults TString titleStr(title); - TObjArray* arr = titleStr.Tokenize(";"); + std::unique_ptr<TObjArray> arr(titleStr.Tokenize(";")); // mark required variables as being used - if (varX > kNothing) + if (varX > kNothing) { fUsedVars[varX] = kTRUE; - if (varY > kNothing) + } + if (varY > kNothing) { fUsedVars[varY] = kTRUE; - if (varZ > kNothing) + } + if (varZ > kNothing) { fUsedVars[varZ] = kTRUE; - if (varT > kNothing) + } + if (varT > kNothing) { fUsedVars[varT] = kTRUE; - if (varW > kNothing) + } + if (varW > kNothing) { fUsedVars[varW] = kTRUE; + } // encode needed variable identifiers in a vector and push it to the std::list corresponding to the current histogram list std::vector<int> varVector; @@ -183,13 +190,16 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += nXbins + 2; // TODO: possibly make the call of Sumw2() optional for all histograms h->Sumw2(); - if (fVariableNames[varX][0]) + if (fVariableNames[varX][0]) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); - if (arr->At(1)) + } + if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); - if (xLabels[0] != '\0') + } + if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); + } hList->Add(h); h->SetDirectory(nullptr); break; @@ -201,31 +211,39 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co h->Sumw2(); // if requested, build the profile using the profile widths instead of stat errors // TODO: make this option more transparent to the user ? - if (titleStr.Contains("--s--")) + if (titleStr.Contains("--s--")) { ((TProfile*)h)->BuildOptions(0., 0., "s"); + } } else { h = new TH2F(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xmin, xmax, nYbins, ymin, ymax); fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) + if (fVariableNames[varX][0]) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); - if (arr->At(1)) + } + if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); - if (xLabels[0] != '\0') + } + if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); + } - if (fVariableNames[varY][0]) + if (fVariableNames[varY][0]) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); - if (fVariableNames[varY][0] && isProfile) + } + if (fVariableNames[varY][0] && isProfile) { h->GetYaxis()->SetTitle(Form("<%s> %s", fVariableNames[varY].Data(), (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); - if (arr->At(2)) + } + if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); - if (yLabels[0] != '\0') + } + if (yLabels[0] != '\0') { MakeAxisLabels(h->GetYaxis(), yLabels); + } hList->Add(h); h->SetDirectory(nullptr); break; @@ -236,44 +254,56 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co h = new TProfile3D(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xmin, xmax, nYbins, ymin, ymax, nZbins, zmin, zmax); fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); - if (titleStr.Contains("--s--")) + if (titleStr.Contains("--s--")) { ((TProfile3D*)h)->BuildOptions(0., 0., "s"); + } } else { // TProfile2D h = new TProfile2D(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xmin, xmax, nYbins, ymin, ymax); fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); - if (titleStr.Contains("--s--")) + if (titleStr.Contains("--s--")) { ((TProfile2D*)h)->BuildOptions(0., 0., "s"); + } } } else { // TH3F h = new TH3F(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xmin, xmax, nYbins, ymin, ymax, nZbins, zmin, zmax); fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) + if (fVariableNames[varX][0]) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); - if (arr->At(1)) + } + if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); - if (xLabels[0] != '\0') + } + if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); - if (fVariableNames[varY][0]) + } + if (fVariableNames[varY][0]) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); - if (arr->At(2)) + } + if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); - if (yLabels[0] != '\0') + } + if (yLabels[0] != '\0') { MakeAxisLabels(h->GetYaxis(), yLabels); - if (fVariableNames[varZ][0]) + } + if (fVariableNames[varZ][0]) { h->GetZaxis()->SetTitle(Form("%s %s", fVariableNames[varZ].Data(), (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); - if (fVariableNames[varZ][0] && isProfile && varT < 0) // for TProfile2D + } + if (fVariableNames[varZ][0] && isProfile && varT < 0) { // for TProfile2D h->GetZaxis()->SetTitle(Form("<%s> %s", fVariableNames[varZ].Data(), (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); - if (arr->At(3)) + } + if (arr->At(3)) { h->GetZaxis()->SetTitle(arr->At(3)->GetName()); - if (zLabels[0] != '\0') + } + if (zLabels[0] != '\0') { MakeAxisLabels(h->GetZaxis(), zLabels); + } h->SetDirectory(nullptr); hList->Add(h); break; @@ -308,26 +338,33 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // deduce the dimension of the histogram from parameters // NOTE: in case of profile histograms, one extra variable is needed int dimension = 1; - if (varY > kNothing) + if (varY > kNothing) { dimension = 2; - if (varZ > kNothing) + } + if (varZ > kNothing) { dimension = 3; + } // mark required variables as being used - if (varX > kNothing) + if (varX > kNothing) { fUsedVars[varX] = kTRUE; - if (varY > kNothing) + } + if (varY > kNothing) { fUsedVars[varY] = kTRUE; - if (varZ > kNothing) + } + if (varZ > kNothing) { fUsedVars[varZ] = kTRUE; - if (varT > kNothing) + } + if (varT > kNothing) { fUsedVars[varT] = kTRUE; - if (varW > kNothing) + } + if (varW > kNothing) { fUsedVars[varW] = kTRUE; + } // tokenize the title string; the user may include in it axis titles which will overwrite the defaults TString titleStr(title); - TObjArray* arr = titleStr.Tokenize(";"); + std::unique_ptr<TObjArray> arr(titleStr.Tokenize(";")); // encode needed variable identifiers in a vector and push it to the std::list corresponding to the current histogram list std::vector<int> varVector; @@ -350,13 +387,16 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co h = new TH1F(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins); fBinsAllocated += nXbins + 2; h->Sumw2(); - if (fVariableNames[varX][0]) + if (fVariableNames[varX][0]) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); - if (arr->At(1)) + } + if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); - if (xLabels[0] != '\0') + } + if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); + } h->SetDirectory(nullptr); hList->Add(h); break; @@ -366,31 +406,39 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co h = new TProfile(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins); fBinsAllocated += nXbins + 2; h->Sumw2(); - if (titleStr.Contains("--s--")) + if (titleStr.Contains("--s--")) { ((TProfile*)h)->BuildOptions(0., 0., "s"); + } } else { h = new TH2F(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins, nYbins, ybins); fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) + if (fVariableNames[varX][0]) { h->GetXaxis()->SetTitle(Form("%s (%s)", fVariableNames[varX].Data(), (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); - if (arr->At(1)) + } + if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); - if (xLabels[0] != '\0') + } + if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); - if (fVariableNames[varY][0]) + } + if (fVariableNames[varY][0]) { h->GetYaxis()->SetTitle(Form("%s (%s)", fVariableNames[varY].Data(), (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); - if (fVariableNames[varY][0] && isProfile) + } + if (fVariableNames[varY][0] && isProfile) { h->GetYaxis()->SetTitle(Form("<%s> (%s)", fVariableNames[varY].Data(), (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + } - if (arr->At(2)) + if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); - if (yLabels[0] != '\0') + } + if (yLabels[0] != '\0') { MakeAxisLabels(h->GetYaxis(), yLabels); + } h->SetDirectory(nullptr); hList->Add(h); break; @@ -401,45 +449,57 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co h = new TProfile3D(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins, nYbins, ybins, nZbins, zbins); fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); - if (titleStr.Contains("--s--")) + if (titleStr.Contains("--s--")) { ((TProfile3D*)h)->BuildOptions(0., 0., "s"); + } } else { h = new TProfile2D(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins, nYbins, ybins); fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); - if (titleStr.Contains("--s--")) + if (titleStr.Contains("--s--")) { ((TProfile2D*)h)->BuildOptions(0., 0., "s"); + } } } else { h = new TH3F(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins, nYbins, ybins, nZbins, zbins); fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) + if (fVariableNames[varX][0]) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); - if (arr->At(1)) + } + if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); - if (xLabels[0] != '\0') + } + if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); - if (fVariableNames[varY][0]) + } + if (fVariableNames[varY][0]) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); - if (arr->At(2)) + } + if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); - if (yLabels[0] != '\0') + } + if (yLabels[0] != '\0') { MakeAxisLabels(h->GetYaxis(), yLabels); - if (fVariableNames[varZ][0]) + } + if (fVariableNames[varZ][0]) { h->GetZaxis()->SetTitle(Form("%s %s", fVariableNames[varZ].Data(), (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); - if (fVariableNames[varZ][0] && isProfile && varT < 0) // TProfile2D + } + if (fVariableNames[varZ][0] && isProfile && varT < 0) { // TProfile2D h->GetZaxis()->SetTitle(Form("<%s> %s", fVariableNames[varZ].Data(), (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); + } - if (arr->At(3)) + if (arr->At(3)) { h->GetZaxis()->SetTitle(arr->At(3)->GetName()); - if (zLabels[0] != '\0') + } + if (zLabels[0] != '\0') { MakeAxisLabels(h->GetZaxis(), zLabels); + } hList->Add(h); break; } // end switch(dimension) @@ -469,51 +529,59 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // tokenize the title string; the user may include in it axis titles which will overwrite the defaults TString titleStr(title); - TObjArray* arr = titleStr.Tokenize(";"); + std::unique_ptr<TObjArray> arr(titleStr.Tokenize(";")); - if (varW > kNothing) + if (varW > kNothing) { fUsedVars[varW] = kTRUE; + } // encode needed variable identifiers in a vector and push it to the std::list corresponding to the current histogram list std::vector<int> varVector; varVector.push_back(0); // whether the histogram is a profile varVector.push_back(nDimensions); // number of dimensions varVector.push_back(varW); // variable used for weighting - for (int idim = 0; idim < nDimensions; ++idim) + for (int idim = 0; idim < nDimensions; ++idim) { varVector.push_back(vars[idim]); // axes variables + } std::list varList = fVariablesMap[histClass]; varList.push_back(varVector); cout << "Adding histogram " << hname << endl; cout << "size of array :: " << varList.size() << endl; fVariablesMap[histClass] = varList; + unsigned long int nbins = 1; THnBase* h = nullptr; - if (useSparse) + if (useSparse) { h = new THnSparseF(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nDimensions, nBins, xmin, xmax); - else + } else { h = new THnF(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nDimensions, nBins, xmin, xmax); + } h->Sumw2(); // configure the THn histogram and count the allocated bins - unsigned long int bins = 1; for (int idim = 0; idim < nDimensions; ++idim) { - bins *= (nBins[idim] + 2); + nbins *= (nBins[idim] + 2); TAxis* axis = h->GetAxis(idim); - - if (fVariableNames[vars[idim]][0]) + if (fVariableNames[vars[idim]][0]) { axis->SetTitle(Form("%s %s", fVariableNames[vars[idim]].Data(), (fVariableUnits[vars[idim]][0] ? Form("(%s)", fVariableUnits[vars[idim]].Data()) : ""))); - if (arr->At(1 + idim)) + } + if (arr->At(1 + idim)) { axis->SetTitle(arr->At(1 + idim)->GetName()); - if (axLabels && !axLabels[idim].IsNull()) + } + if (axLabels && !axLabels[idim].IsNull()) { MakeAxisLabels(axis, axLabels[idim].Data()); + } + fUsedVars[vars[idim]] = kTRUE; } - if (useSparse) + if (useSparse) { hList->Add((THnSparseF*)h); - else + } else { hList->Add((THnF*)h); - fBinsAllocated += bins; + } + + fBinsAllocated += nbins; } //_________________________________________________________________ @@ -540,18 +608,20 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // tokenize the title string; the user may include in it axis titles which will overwrite the defaults TString titleStr(title); - TObjArray* arr = titleStr.Tokenize(";"); + std::unique_ptr<TObjArray> arr(titleStr.Tokenize(";")); - if (varW > kNothing) + if (varW > kNothing) { fUsedVars[varW] = kTRUE; + } // encode needed variable identifiers in a vector and push it to the std::list corresponding to the current histogram list std::vector<int> varVector; varVector.push_back(0); // whether the histogram is a profile varVector.push_back(nDimensions); // number of dimensions varVector.push_back(varW); // variable used for weighting - for (int idim = 0; idim < nDimensions; ++idim) + for (int idim = 0; idim < nDimensions; ++idim) { varVector.push_back(vars[idim]); // axes variables + } std::list varList = fVariablesMap[histClass]; varList.push_back(varVector); cout << "Adding histogram " << hname << endl; @@ -570,10 +640,11 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // initialize the THn with equal spaced bins THnBase* h = nullptr; - if (useSparse) + if (useSparse) { h = new THnSparseF(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nDimensions, nBins, xmin, xmax); - else + } else { h = new THnF(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nDimensions, nBins, xmin, xmax); + } // rebin the axes according to the user requested binning for (int idim = 0; idim < nDimensions; ++idim) { TAxis* axis = h->GetAxis(idim); @@ -585,19 +656,23 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co for (int idim = 0; idim < nDimensions; ++idim) { bins *= (nBins[idim] + 2); TAxis* axis = h->GetAxis(idim); - if (fVariableNames[vars[idim]][0]) + if (fVariableNames[vars[idim]][0]) { axis->SetTitle(Form("%s %s", fVariableNames[vars[idim]].Data(), (fVariableUnits[vars[idim]][0] ? Form("(%s)", fVariableUnits[vars[idim]].Data()) : ""))); - if (arr->At(1 + idim)) + } + if (arr->At(1 + idim)) { axis->SetTitle(arr->At(1 + idim)->GetName()); - if (axLabels && !axLabels[idim].IsNull()) + } + if (axLabels && !axLabels[idim].IsNull()) { MakeAxisLabels(axis, axLabels[idim].Data()); + } fUsedVars[vars[idim]] = kTRUE; } - if (useSparse) + if (useSparse) { hList->Add((THnSparseF*)h); - else + } else { hList->Add((THnF*)h); + } fBinsAllocated += bins; } @@ -640,16 +715,18 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) // decode information from the vector of indices isProfile = (varIter->at(0) == 1 ? true : false); isTHn = (varIter->at(1) > 0 ? true : false); - if (isTHn) + if (isTHn) { dimension = varIter->at(1); - else + } else { dimension = ((TH1*)h)->GetDimension(); + } // get the various variable indices varW = varIter->at(2); if (isTHn) { - for (int i = 0; i < dimension; i++) + for (int i = 0; i < dimension; i++) { fillValues[i] = values[varIter->at(3 + i)]; + } } else { varX = varIter->at(3); varY = varIter->at(4); @@ -657,47 +734,51 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) varT = varIter->at(6); } - // TODO: check that all needed variables are marked for usage in fUsedVars, otherwise throw and error - if (!isTHn) { switch (dimension) { case 1: if (isProfile) { - if (varW > kNothing) + if (varW > kNothing) { ((TProfile*)h)->Fill(values[varX], values[varY], values[varW]); - else + } else { ((TProfile*)h)->Fill(values[varX], values[varY]); + } } else { - if (varW > kNothing) + if (varW > kNothing) { ((TH1F*)h)->Fill(values[varX], values[varW]); - else + } else { ((TH1F*)h)->Fill(values[varX]); + } } break; case 2: if (isProfile) { - if (varW > kNothing) + if (varW > kNothing) { ((TProfile2D*)h)->Fill(values[varX], values[varY], values[varZ], values[varW]); - else + } else { ((TProfile2D*)h)->Fill(values[varX], values[varY], values[varZ]); + } } else { - if (varW > kNothing) + if (varW > kNothing) { ((TH2F*)h)->Fill(values[varX], values[varY], values[varW]); - else + } else { ((TH2F*)h)->Fill(values[varX], values[varY]); + } } break; case 3: if (isProfile) { - if (varW > kNothing) + if (varW > kNothing) { ((TProfile3D*)h)->Fill(values[varX], values[varY], values[varZ], values[varT], values[varW]); - else + } else { ((TProfile3D*)h)->Fill(values[varX], values[varY], values[varZ], values[varT]); + } } else { - if (varW > kNothing) + if (varW > kNothing) { ((TH3F*)h)->Fill(values[varX], values[varY], values[varZ], values[varW]); - else + } else { ((TH3F*)h)->Fill(values[varX], values[varY], values[varZ]); + } } break; @@ -707,15 +788,17 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) } // end if(!isTHn) else { if (varW > kNothing) { - if (isSparse) + if (isSparse) { ((THnSparseF*)h)->Fill(fillValues, values[varW]); - else + } else { ((THnF*)h)->Fill(fillValues, values[varW]); + } } else { - if (isSparse) + if (isSparse) { ((THnSparseF*)h)->Fill(fillValues); - else + } else { ((THnF*)h)->Fill(fillValues); + } } } // end else } // end loop over histograms @@ -728,10 +811,11 @@ void HistogramManager::MakeAxisLabels(TAxis* ax, const char* labels) // add bin labels to an axis // TString labelsStr(labels); - TObjArray* arr = labelsStr.Tokenize(";"); + std::unique_ptr<TObjArray> arr(labelsStr.Tokenize(";")); for (int ib = 1; ib <= ax->GetNbins(); ++ib) { - if (ib >= arr->GetEntries() + 1) + if (ib >= arr->GetEntries() + 1) { break; + } ax->SetBinLabel(ib, arr->At(ib - 1)->GetName()); } } diff --git a/Analysis/PWGDQ/src/PWGDQCoreLinkDef.h b/Analysis/PWGDQ/src/PWGDQCoreLinkDef.h new file mode 100644 index 0000000000000..170a8b7a38bab --- /dev/null +++ b/Analysis/PWGDQ/src/PWGDQCoreLinkDef.h @@ -0,0 +1,18 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class VarManager + ; +#pragma link C++ class HistogramManager + ; +#pragma link C++ class AnalysisCut + ; +#pragma link C++ class AnalysisCompositeCut + ; diff --git a/Analysis/PWGDQ/src/VarManager.cxx b/Analysis/PWGDQ/src/VarManager.cxx new file mode 100644 index 0000000000000..adfb2face737c --- /dev/null +++ b/Analysis/PWGDQ/src/VarManager.cxx @@ -0,0 +1,247 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PWGDQCore/VarManager.h" + +#include <TMath.h> + +ClassImp(VarManager); + +TString VarManager::fgVariableNames[VarManager::kNVars] = {""}; +TString VarManager::fgVariableUnits[VarManager::kNVars] = {""}; +bool VarManager::fgUsedVars[VarManager::kNVars] = {kFALSE}; +float VarManager::fgValues[VarManager::kNVars] = {0.0}; +std::map<int, int> VarManager::fgRunMap; +TString VarManager::fgRunStr = ""; + +//__________________________________________________________________ +VarManager::VarManager() : TObject() +{ + // + // constructor + // + SetDefaultVarNames(); +} + +//__________________________________________________________________ +VarManager::~VarManager() = default; + +//__________________________________________________________________ +void VarManager::SetVariableDependencies() +{ + // + // Set as used variables on which other variables calculation depends + // + if (fgUsedVars[kP]) { + fgUsedVars[kPt] = kTRUE; + fgUsedVars[kEta] = kTRUE; + } +} + +//__________________________________________________________________ +void VarManager::ResetValues(int startValue, int endValue, float* values) +{ + // + // reset all variables to an "innocent" value + // NOTE: here we use -9999.0 as a neutral value, but depending on situation, this may not be the case + if (!values) { + values = fgValues; + } + for (Int_t i = startValue; i < endValue; ++i) { + values[i] = -9999.; + } +} + +//__________________________________________________________________ +void VarManager::SetRunNumbers(int n, int* runs) +{ + // + // maps the list of runs such that one can plot the list of runs nicely in a histogram axis + // + for (int i = 0; i < n; ++i) { + fgRunMap[runs[i]] = i + 1; + fgRunStr += Form("%d;", runs[i]); + } +} + +//__________________________________________________________________ +void VarManager::FillEventDerived(float* values) +{ + // + // Fill event-wise derived quantities (these are all quantities which can be computed just based on the values already filled in the FillEvent() function) + // + if (fgUsedVars[kRunId]) { + values[kRunId] = (fgRunMap.size() > 0 ? fgRunMap[int(values[kRunNo])] : 0); + } +} + +//__________________________________________________________________ +void VarManager::FillTrackDerived(float* values) +{ + // + // Fill track-wise derived quantities (these are all quantities which can be computed just based on the values already filled in the FillTrack() function) + // + if (fgUsedVars[kP]) { + values[kP] = values[kPt] * TMath::CosH(values[kEta]); + } +} + +//__________________________________________________________________ +void VarManager::SetDefaultVarNames() +{ + // + // Set default variable names + // + for (Int_t ivar = 0; ivar < kNVars; ++ivar) { + fgVariableNames[ivar] = "DEFAULT NOT DEFINED"; + fgVariableUnits[ivar] = "n/a"; + } + + fgVariableNames[kRunNo] = "Run number"; + fgVariableUnits[kRunNo] = ""; + fgVariableNames[kRunId] = "Run number"; + fgVariableUnits[kRunId] = ""; + fgVariableNames[kCollisionTime] = "collision time"; + fgVariableUnits[kCollisionTime] = ""; + fgVariableNames[kBC] = "Bunch crossing"; + fgVariableUnits[kBC] = ""; + fgVariableNames[kIsPhysicsSelection] = "Physics selection"; + fgVariableUnits[kIsPhysicsSelection] = ""; + fgVariableNames[kVtxX] = "Vtx X "; + fgVariableUnits[kVtxX] = "cm"; + fgVariableNames[kVtxY] = "Vtx Y "; + fgVariableUnits[kVtxY] = "cm"; + fgVariableNames[kVtxZ] = "Vtx Z "; + fgVariableUnits[kVtxZ] = "cm"; + fgVariableNames[kVtxNcontrib] = "Vtx contrib."; + fgVariableUnits[kVtxNcontrib] = ""; + fgVariableNames[kVtxCovXX] = "Vtx covXX"; + fgVariableUnits[kVtxCovXX] = "cm"; + fgVariableNames[kVtxCovXY] = "Vtx covXY"; + fgVariableUnits[kVtxCovXY] = "cm"; + fgVariableNames[kVtxCovXZ] = "Vtx covXZ"; + fgVariableUnits[kVtxCovXZ] = "cm"; + fgVariableNames[kVtxCovYY] = "Vtx covYY"; + fgVariableUnits[kVtxCovYY] = "cm"; + fgVariableNames[kVtxCovYZ] = "Vtx covYZ"; + fgVariableUnits[kVtxCovYZ] = "cm"; + fgVariableNames[kVtxCovZZ] = "Vtx covZZ"; + fgVariableUnits[kVtxCovZZ] = "cm"; + fgVariableNames[kVtxChi2] = "Vtx chi2"; + fgVariableUnits[kVtxChi2] = ""; + fgVariableNames[kCentVZERO] = "Centrality VZERO"; + fgVariableUnits[kCentVZERO] = "%"; + fgVariableNames[kPt] = "p_{T}"; + fgVariableUnits[kPt] = "GeV/c"; + fgVariableNames[kP] = "p"; + fgVariableUnits[kP] = "GeV/c"; + fgVariableNames[kPx] = "p_{x}"; + fgVariableUnits[kPy] = "GeV/c"; + fgVariableNames[kPy] = "p_{y}"; + fgVariableUnits[kPz] = "GeV/c"; + fgVariableNames[kPz] = "p_{z}"; + fgVariableUnits[kPx] = "GeV/c"; + fgVariableNames[kEta] = "#eta"; + fgVariableUnits[kEta] = ""; + fgVariableNames[kPhi] = "#varphi"; + fgVariableUnits[kPhi] = "rad."; + fgVariableNames[kRap] = "y"; + fgVariableUnits[kRap] = ""; + fgVariableNames[kMass] = "mass"; + fgVariableUnits[kMass] = "GeV/c2"; + fgVariableNames[kCharge] = "charge"; + fgVariableUnits[kCharge] = ""; + fgVariableNames[kPin] = "p_{IN}"; + fgVariableUnits[kPin] = "GeV/c"; + fgVariableNames[kITSncls] = "ITS #cls"; + fgVariableUnits[kITSncls] = ""; + fgVariableNames[kITSchi2] = "ITS chi2"; + fgVariableUnits[kITSchi2] = ""; + fgVariableNames[kITSlayerHit] = "ITS layer"; + fgVariableUnits[kITSlayerHit] = ""; + fgVariableNames[kTPCncls] = "TPC #cls"; + fgVariableUnits[kTPCncls] = ""; + fgVariableNames[kTPCchi2] = "TPC chi2"; + fgVariableUnits[kTPCchi2] = ""; + fgVariableNames[kTPCsignal] = "TPC dE/dx"; + fgVariableUnits[kTPCsignal] = ""; + fgVariableNames[kTRDsignal] = "TRD dE/dx"; + fgVariableUnits[kTRDsignal] = ""; + fgVariableNames[kTOFsignal] = "TOF signal"; + fgVariableUnits[kTOFsignal] = ""; + fgVariableNames[kTOFbeta] = "TOF #beta"; + fgVariableUnits[kTOFbeta] = ""; + fgVariableNames[kTrackLength] = "track length"; + fgVariableUnits[kTrackLength] = "cm"; + fgVariableNames[kTrackDCAxy] = "DCA_{xy}"; + fgVariableUnits[kTrackDCAxy] = "cm"; + fgVariableNames[kTrackDCAz] = "DCA_{z}"; + fgVariableUnits[kTrackDCAz] = "cm"; + fgVariableNames[kTPCnSigmaEl] = "n #sigma_{e}^{TPC}"; + fgVariableUnits[kTPCnSigmaEl] = ""; + fgVariableNames[kTPCnSigmaMu] = "n #sigma_{#mu}^{TPC}"; + fgVariableUnits[kTPCnSigmaMu] = ""; + fgVariableNames[kTPCnSigmaPi] = "n #sigma_{#pi}^{TPC}"; + fgVariableUnits[kTPCnSigmaPi] = ""; + fgVariableNames[kTPCnSigmaKa] = "n #sigma_{K}^{TPC}"; + fgVariableUnits[kTPCnSigmaKa] = ""; + fgVariableNames[kTPCnSigmaPr] = "n #sigma_{p}^{TPC}"; + fgVariableUnits[kTPCnSigmaPr] = ""; + fgVariableNames[kTOFnSigmaEl] = "n #sigma_{e}^{TOF}"; + fgVariableUnits[kTOFnSigmaEl] = ""; + fgVariableNames[kTOFnSigmaMu] = "n #sigma_{#mu}^{TOF}"; + fgVariableUnits[kTOFnSigmaMu] = ""; + fgVariableNames[kTOFnSigmaPi] = "n #sigma_{#pi}^{TOF}"; + fgVariableUnits[kTOFnSigmaPi] = ""; + fgVariableNames[kTOFnSigmaKa] = "n #sigma_{K}^{TOF}"; + fgVariableUnits[kTOFnSigmaKa] = ""; + fgVariableNames[kTOFnSigmaPr] = "n #sigma_{p}^{TOF}"; + fgVariableUnits[kTOFnSigmaPr] = ""; + fgVariableNames[kMuonInvBendingMomentum] = "Inverse bending mom."; + fgVariableUnits[kMuonInvBendingMomentum] = "1/(GeV/c)"; + fgVariableNames[kMuonThetaX] = "theta X"; + fgVariableUnits[kMuonThetaX] = ""; + fgVariableNames[kMuonThetaY] = "theta Y"; + fgVariableUnits[kMuonThetaY] = ""; + fgVariableNames[kMuonZMu] = "ZMu"; + fgVariableUnits[kMuonZMu] = ""; + fgVariableNames[kMuonBendingCoor] = "bending coor."; + fgVariableUnits[kMuonBendingCoor] = ""; + fgVariableNames[kMuonNonBendingCoor] = "non-bending coor."; + fgVariableUnits[kMuonNonBendingCoor] = ""; + fgVariableNames[kMuonRAtAbsorberEnd] = "R at the end of the absorber"; + fgVariableUnits[kMuonRAtAbsorberEnd] = ""; + fgVariableNames[kMuonPDca] = "p x dca"; + fgVariableUnits[kMuonPDca] = "cm x GeV/c"; + fgVariableNames[kMuonChi2] = "#chi 2"; + fgVariableUnits[kMuonChi2] = ""; + fgVariableNames[kMuonChi2MatchTrigger] = "#chi 2 trigger match"; + fgVariableUnits[kMuonChi2MatchTrigger] = ""; + fgVariableNames[kCandidateId] = ""; + fgVariableUnits[kCandidateId] = ""; + fgVariableNames[kPairType] = "Pair type"; + fgVariableUnits[kPairType] = ""; + fgVariableNames[kPairLxy] = "Pair Lxy"; + fgVariableUnits[kPairLxy] = "cm"; + fgVariableNames[kPairMass] = "mass"; + fgVariableUnits[kPairMass] = "GeV/c2"; + fgVariableNames[kPairPt] = "p_{T}"; + fgVariableUnits[kPairPt] = "GeV/c"; + fgVariableNames[kPairEta] = "#eta"; + fgVariableUnits[kPairEta] = ""; + fgVariableNames[kPairPhi] = "#varphi"; + fgVariableUnits[kPairPhi] = "rad."; + fgVariableNames[kDeltaEta] = "#Delta#eta"; + fgVariableUnits[kDeltaEta] = ""; + fgVariableNames[kDeltaPhi] = "#Delta#phi"; + fgVariableUnits[kDeltaPhi] = "rad."; + fgVariableNames[kDeltaPhiSym] = "#Delta#phi"; + fgVariableUnits[kDeltaPhiSym] = "rad."; +} diff --git a/Analysis/Scripts/update_ccdb.py b/Analysis/Scripts/update_ccdb.py index 00b32ef5bc477..a3cd525175908 100755 --- a/Analysis/Scripts/update_ccdb.py +++ b/Analysis/Scripts/update_ccdb.py @@ -19,87 +19,126 @@ 3) old object with validity [requested_upper_bound, old_upper_validity] Author: Nicolo' Jacazio on 2020-06-22 TODO add support for 3 files update -TODO add support for user input """ import subprocess from datetime import datetime import matplotlib.pyplot as plt +import argparse def convert_timestamp(ts): - return datetime.utcfromtimestamp(ts/1000).strftime('%Y-%m-%d %H:%M:%S') + """ + Converts the timestamp in milliseconds in human readable format + """ + return datetime.utcfromtimestamp(ts/1000).strftime('%Y-%m-%d %H:%M:%S') -def get_ccdb_obj(path, timestamp, dest = "/tmp/"): - print("Getting obj", path, "with timestamp", - timestamp, convert_timestamp(timestamp)) - cmd=f"o2-ccdb-downloadccdbfile --path {path} --dest {dest} --timestamp {timestamp}" +def get_ccdb_obj(path, timestamp, dest="/tmp/", verbose=0): + """ + Gets the ccdb object from 'path' and 'timestamp' and downloads it into 'dest' + """ + if verbose: + print("Getting obj", path, "with timestamp", + timestamp, convert_timestamp(timestamp)) + cmd = f"o2-ccdb-downloadccdbfile --path {path} --dest {dest} --timestamp {timestamp}" subprocess.run(cmd.split()) -def get_ccdb_obj_validity(path, dest = "/tmp/", verbose = True): - cmd=f"o2-ccdb-inspectccdbfile {dest}{path}/snapshot.root" - process=subprocess.Popen(cmd.split(), stdout = subprocess.PIPE) - output, error=process.communicate() - output=output.decode("utf-8").split("\n") - error=error.decode("utf-8").split("\n") if error is not None else error +def get_ccdb_obj_validity(path, dest="/tmp/", verbose=0): + """ + Gets the timestamp validity for an object downloaded from CCDB. + Returns a list with the initial and end timestamps. + """ + cmd = f"o2-ccdb-inspectccdbfile {dest}{path}/snapshot.root" + process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) + output, error = process.communicate() + output = output.decode("utf-8").split("\n") + error = error.decode("utf-8").split("\n") if error is not None else error if verbose: print("out:") print(*output, "\n") print("err:") print(error) - result=list(filter(lambda x: x.startswith('Valid-'), output)) + result = list(filter(lambda x: x.startswith('Valid-'), output)) ValidFrom = result[0].split() ValidUntil = result[1].split() - print(*output, sep="\n") return [int(ValidFrom[-1]), int(ValidUntil[-1])] - return {ValidFrom[0]: ValidFrom[-1], ValidUntil[0]: ValidUntil[-1]} def upload_ccdb_obj(path, timestamp_from, timestamp_until, dest="/tmp/", meta=""): + """ + Uploads a new object to CCDB in the 'path' using the validity timestamp specified + """ print("Uploading obj", path, "with timestamp", [timestamp_from, timestamp_until], convert_timestamp(timestamp_from), convert_timestamp(timestamp_until)) - key=path.split("/")[-1] - cmd=f"o2-ccdb-upload -f {dest}{path}/snapshot.root " + key = path.split("/")[-1] + cmd = f"o2-ccdb-upload -f {dest}{path}/snapshot.root " cmd += f"--key {key} --path {path} " cmd += f"--starttimestamp {timestamp_from} --endtimestamp {timestamp_until} --meta \"{meta}\"" subprocess.run(cmd.split()) -def main(path, timestamp_from, timestamp_until): +def main(path, timestamp_from, timestamp_until, verbose=0, show=False): + """ + Used to upload a new object to CCDB in 'path' valid from 'timestamp_from' to 'timestamp_until' + Gets the object from CCDB specified in 'path' and for 'timestamp_from-1' + Gets the object from CCDB specified in 'path' and for 'timestamp_until+1' + If required plots the situation before and after the update + """ get_ccdb_obj(path, timestamp_from-1) - val_before=get_ccdb_obj_validity(path) + val_before = get_ccdb_obj_validity(path, verbose=verbose) get_ccdb_obj(path, timestamp_until+1) - val_after=get_ccdb_obj_validity(path) - overlap_before=val_before[1] > timestamp_from - overlap_after=val_after[0] < timestamp_until - print(overlap_before, overlap_after) - trimmed_before=val_before if not overlap_before else [ + val_after = get_ccdb_obj_validity(path, verbose=verbose) + overlap_before = val_before[1] > timestamp_from + overlap_after = val_after[0] < timestamp_until + if verbose: + if overlap_before: + print("Previous objects overalps") + if overlap_after: + print("Next objects overalps") + trimmed_before = val_before if not overlap_before else [ val_before[0], timestamp_from - 1] trimmed_after = val_after if not overlap_after else [ timestamp_until+1, val_after[1]] - fig, ax = plt.subplots() - - def bef_af(v, y): - return [v[0] - 1] + v + [v[1] + 1], [0, y, y, 0] - if True: - ax.plot(*bef_af(val_before, 0.95), label='before') - ax.plot(*bef_af(val_after, 1.05), label='after') - if False: - ax.plot(*bef_af(trimmed_before, 0.9), label='trimmed before') - ax.plot(*bef_af(trimmed_after, 1.1), label='trimmed after') - ax.plot(*bef_af([timestamp_from, timestamp_until], 1), label='object') - xlim = 10000000 - plt.xlim([timestamp_from-xlim, timestamp_until+xlim]) - plt.ylim(0, 2) - plt.xlabel('Timestamp') - plt.ylabel('Validity') - plt.legend() - plt.show() + if show: + fig, ax = plt.subplots() + fig + + def bef_af(v, y): + return [v[0] - 1] + v + [v[1] + 1], [0, y, y, 0] + if True: + ax.plot(*bef_af(val_before, 0.95), label='before') + ax.plot(*bef_af(val_after, 1.05), label='after') + if False: + ax.plot(*bef_af(trimmed_before, 0.9), label='trimmed before') + ax.plot(*bef_af(trimmed_after, 1.1), label='trimmed after') + ax.plot(*bef_af([timestamp_from, timestamp_until], 1), label='object') + xlim = 10000000 + plt.xlim([timestamp_from-xlim, timestamp_until+xlim]) + plt.ylim(0, 2) + plt.xlabel('Timestamp') + plt.ylabel('Validity') + plt.legend() + plt.show() if __name__ == "__main__": - main("qc/TOF/TOFTaskCompressed/hDiagnostic", - 1588946517161 + 10000000, - 1588946517161 + 40000000) + parser = argparse.ArgumentParser( + description="Uploads timestamp non overlapping objects to CCDB." + "Basic example: `./update_ccdb.py qc/TOF/TOFTaskCompressed/hDiagnostic 1588956517161 1588986517161 --show --verbose`") + parser.add_argument('path', metavar='path_to_object', type=str, + help='Path of the object in the CCDB repository') + parser.add_argument('timestamp_from', metavar='from_timestamp', type=int, + help='Timestamp of start for the new object to use') + parser.add_argument('timestamp_until', metavar='until_timestamp', type=int, + help='Timestamp of stop for the new object to use') + parser.add_argument('--verbose', '-v', action='count', default=0) + parser.add_argument('--show', '-s', action='count', default=0) + + args = parser.parse_args() + main(path=args.path, + timestamp_from=args.timestamp_from, + timestamp_until=args.timestamp_until, + verbose=args.verbose, + show=args.show) diff --git a/Analysis/Tasks/ALICE3/CMakeLists.txt b/Analysis/Tasks/ALICE3/CMakeLists.txt new file mode 100644 index 0000000000000..7734d35e2289b --- /dev/null +++ b/Analysis/Tasks/ALICE3/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(alice3-trackselection + SOURCES alice3-trackselection.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + + diff --git a/Analysis/Tasks/ALICE3/alice3-trackselection.cxx b/Analysis/Tasks/ALICE3/alice3-trackselection.cxx new file mode 100644 index 0000000000000..2f35eea40c23b --- /dev/null +++ b/Analysis/Tasks/ALICE3/alice3-trackselection.cxx @@ -0,0 +1,56 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +// Task performing basic track selection. +// + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/runDataProcessing.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisCore/trackUtilities.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +//**************************************************************************************** +/** + * Produce track filter table. + */ +//**************************************************************************************** +struct TrackSelectionTask { + Produces<aod::TrackSelection> filterTable; + + void init(InitContext&) + { + } + + void process(soa::Join<aod::FullTracks, aod::TracksExtended> const& tracks) + { + for (auto& track : tracks) { + filterTable(kTRUE, + kTRUE); + } + } +}; + +//**************************************************************************************** +/** + * Workflow definition. + */ +//**************************************************************************************** +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask<TrackSelectionTask>("track-selection")}; + return workflow; +} diff --git a/Analysis/Tasks/CMakeLists.txt b/Analysis/Tasks/CMakeLists.txt index 6126bc02e3d76..eb42e55c516ff 100644 --- a/Analysis/Tasks/CMakeLists.txt +++ b/Analysis/Tasks/CMakeLists.txt @@ -8,110 +8,88 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. +add_subdirectory(PWGCF) +add_subdirectory(PWGDQ) +add_subdirectory(PWGHF) +add_subdirectory(PWGJE) +add_subdirectory(PWGLF) +add_subdirectory(PWGUD) +add_subdirectory(ALICE3) +add_subdirectory(SkimmingTutorials) + + +o2_add_dpl_workflow(trackextension + SOURCES trackextension.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + o2_add_dpl_workflow(trackselection - SOURCES trackselection.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(correlations - SOURCES correlations.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(correlations-collection - SOURCES correlationsCollection.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(correlations-mixed - SOURCES correlationsMixed.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(vertexing-hf - SOURCES vertexerhf.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsVertexing - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(hftrackindexskimscreator - SOURCES hftrackindexskimscreator.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsVertexing - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(hfcandidatecreator2prong - SOURCES hfcandidatecreator2prong.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsVertexing - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(taskdzero - SOURCES taskdzero.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsVertexing - COMPONENT_NAME Analysis) + SOURCES trackselection.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(trackqa + SOURCES trackqa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + + +o2_add_dpl_workflow(pid-tof + SOURCES pidTOF.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(pid-tpc + SOURCES pidTPC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) o2_add_dpl_workflow(validation - SOURCES validation.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase - COMPONENT_NAME Analysis) - -if(FastJet_FOUND) -o2_add_dpl_workflow(jetfinder - SOURCES jetfinder.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel - FastJet::FastJet - COMPONENT_NAME Analysis) -endif() - -o2_add_dpl_workflow(upc-an - SOURCES upcAnalysis.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase - COMPONENT_NAME Analysis) + SOURCES validation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase + COMPONENT_NAME Analysis) o2_add_dpl_workflow(event-selection - SOURCES eventSelection.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::CCDB - COMPONENT_NAME Analysis) + SOURCES eventSelection.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase O2::CCDB O2::CommonConstants + COMPONENT_NAME Analysis) o2_add_dpl_workflow(event-selection-qa - SOURCES eventSelectionQa.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase - COMPONENT_NAME Analysis) + SOURCES eventSelectionQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) o2_add_dpl_workflow(multiplicity-table - SOURCES multiplicityTable.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase - COMPONENT_NAME Analysis) + SOURCES multiplicityTable.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) o2_add_dpl_workflow(multiplicity-qa - SOURCES multiplicityQa.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase - COMPONENT_NAME Analysis) - + SOURCES multiplicityQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) + o2_add_dpl_workflow(centrality-table - SOURCES centralityTable.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::CCDB - COMPONENT_NAME Analysis) + SOURCES centralityTable.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase O2::CCDB + COMPONENT_NAME Analysis) o2_add_dpl_workflow(centrality-qa - SOURCES centralityQa.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(invmass-an - SOURCES invMassAnalysis.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(table-maker - SOURCES tableMaker.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore - COMPONENT_NAME Analysis) - -o2_add_dpl_workflow(table-reader - SOURCES tableReader.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore - COMPONENT_NAME Analysis) + SOURCES centralityQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) o2_add_dpl_workflow(timestamp SOURCES timestamp.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::DetectorsRaw O2::AnalysisCore O2::CommonDataFormat O2::CCDB COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(weak-decay-indices + SOURCES weakDecayIndices.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(emcal-correction-task + SOURCES emcalCorrectionTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase O2::EMCALBase O2::EMCALReconstruction + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/PWGCF/AnalysisConfigurableCuts.cxx b/Analysis/Tasks/PWGCF/AnalysisConfigurableCuts.cxx new file mode 100644 index 0000000000000..e0f9ff79899eb --- /dev/null +++ b/Analysis/Tasks/PWGCF/AnalysisConfigurableCuts.cxx @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "AnalysisConfigurableCuts.h" + +using namespace o2::analysis; + +ClassImp(SimpleInclusiveCut); + +SimpleInclusiveCut::SimpleInclusiveCut() : TNamed(), + mX(0), + mY(0.0) +{ + // + // default constructor + // +} + +SimpleInclusiveCut::SimpleInclusiveCut(const char* name, int _x, float _y) : TNamed(name, name), + mX(_x), + mY(_y) +{ + // + // explicit constructor + // +} + +SimpleInclusiveCut& SimpleInclusiveCut::operator=(const SimpleInclusiveCut& sic) +{ + // + // assignment operator + // + if (this != &sic) { + TNamed::operator=(sic); + mX = sic.mX; + mY = sic.mY; + } + return (*this); +} diff --git a/Analysis/Tasks/PWGCF/AnalysisConfigurableCuts.h b/Analysis/Tasks/PWGCF/AnalysisConfigurableCuts.h new file mode 100644 index 0000000000000..9c916039f9542 --- /dev/null +++ b/Analysis/Tasks/PWGCF/AnalysisConfigurableCuts.h @@ -0,0 +1,77 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ANALYSIS_CONFIGURABLE_CUTS_CLASSES_H +#define ANALYSIS_CONFIGURABLE_CUTS_CLASSES_H + +#include <Rtypes.h> +#include <TObject.h> +#include <TNamed.h> +#include <TMath.h> + +namespace o2 +{ +namespace analysis +{ +/// \class EventSelectionCuts +/// \brief Class which implements configurable event selection cuts +/// +class EventSelectionCuts +{ + public: + int mOfflinetrigger = 1; ///< offline trigger, default MB = 1 + std::string mCentmultestmator = "V0M"; ///< centrality / multiplicity estimation, default V0M + int mRemovepileupcode = 1; ///< Procedure for pile-up removal, default V0M vs TPCout tracks = 1 + std::string mRemovepileupfn = "-2500+5.0*x"; ///< function for pile-up removal, procedure dependent, defaul V0M vs TPCout tracks for LHC15o HIR + + private: + ClassDefNV(EventSelectionCuts, 1); +}; + +/// \class DptDptBinningCuts +/// \brief Class which implements configurable acceptance cuts +/// +class DptDptBinningCuts +{ + public: + int mZVtxbins = 28; ///< the number of z_vtx bins default 28 + float mZVtxmin = -7.0; ///< the minimum z_vtx value, default -7.0 cm + float mZVtxmax = 7.0; ///< the maximum z_vtx value, default 7.0 cm + int mPTbins = 18; ///< the number of pT bins, default 18 + float mPTmin = 0.2; ///< the minimum pT value, default 0.2 GeV + float mPTmax = 2.0; ///< the maximum pT value, default 2.0 GeV + int mEtabins = 16; ///< the number of eta bins default 16 + float mEtamin = -0.8; ///< the minimum eta value, default -0.8 + float mEtamax = 0.8; ///< the maximum eta value, default 0.8 + int mPhibins = 72; ///< the number of phi bins, default 72 + float mPhibinshift = 0.5; ///< the shift in the azimuthal origen, defoult 0.5, i.e half a bin + + private: + ClassDefNV(DptDptBinningCuts, 1); +}; + +class SimpleInclusiveCut : public TNamed +{ + public: + int mX = 1; + float mY = 2.f; + SimpleInclusiveCut(); + SimpleInclusiveCut(const char*, int, float); + SimpleInclusiveCut(const SimpleInclusiveCut&) = default; + ~SimpleInclusiveCut() override = default; + + SimpleInclusiveCut& operator=(const SimpleInclusiveCut&); + + private: + ClassDef(SimpleInclusiveCut, 1); +}; + +} // namespace analysis +} // namespace o2 +#endif // ANALYSIS_CONFIGURABLE_CUTS_CLASSES_H diff --git a/Analysis/Tasks/PWGCF/CMakeLists.txt b/Analysis/Tasks/PWGCF/CMakeLists.txt new file mode 100644 index 0000000000000..e7eb33cb131ab --- /dev/null +++ b/Analysis/Tasks/PWGCF/CMakeLists.txt @@ -0,0 +1,43 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(PWGCFCore + SOURCES AnalysisConfigurableCuts.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel) + +o2_target_root_dictionary(PWGCFCore + HEADERS AnalysisConfigurableCuts.h + LINKDEF PWGCFCoreLinkDef.h) + +o2_add_dpl_workflow(dptdptcorrelations + SOURCES dptdptcorrelations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel O2::PWGCFCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(correlations + SOURCES correlations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(correlations-mixed + SOURCES correlationsMixed.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(filter-cf + SOURCES filterCF.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(correlations-filtered + SOURCES correlationsFiltered.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore + COMPONENT_NAME Analysis) + diff --git a/Analysis/Tasks/PWGCF/PWGCFCoreLinkDef.h b/Analysis/Tasks/PWGCF/PWGCFCoreLinkDef.h new file mode 100644 index 0000000000000..9a46f9dc8e5da --- /dev/null +++ b/Analysis/Tasks/PWGCF/PWGCFCoreLinkDef.h @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::analysis::EventSelectionCuts + ; +#pragma link C++ class o2::analysis::DptDptBinningCuts + ; +#pragma link C++ class o2::analysis::SimpleInclusiveCut + ; diff --git a/Analysis/Tasks/PWGCF/correlations.cxx b/Analysis/Tasks/PWGCF/correlations.cxx new file mode 100644 index 0000000000000..93c47963c9187 --- /dev/null +++ b/Analysis/Tasks/PWGCF/correlations.cxx @@ -0,0 +1,317 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include <CCDB/BasicCCDBManager.h> +#include "Framework/StepTHn.h" +#include "Framework/HistogramRegistry.h" + +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/CorrelationContainer.h" +#include "AnalysisCore/PairCuts.h" + +#include <TH1F.h> +#include <cmath> +#include <TDirectory.h> +#include <THn.h> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable<TYPE> NAME{#NAME, DEFAULT, HELP}; + +struct CorrelationTask { + + // Configuration + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 7.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + + O2_DEFINE_CONFIGURABLE(cfgPtOrder, int, 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)"); + O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative"); + O2_DEFINE_CONFIGURABLE(cfgAssociatedCharge, int, 0, "Select on charge of associated particle: 0 = all; 1 = positive; -1 = negative"); + O2_DEFINE_CONFIGURABLE(cfgPairCharge, int, 0, "Select on charge of particle pair: 0 = all; 1 = like sign; -1 = unlike sign"); + + O2_DEFINE_CONFIGURABLE(cfgTwoTrackCut, float, -1, "Two track cut: -1 = off; >0 otherwise distance value (suggested: 0.02)"); + O2_DEFINE_CONFIGURABLE(cfgTwoTrackCutMinRadius, float, 0.8f, "Two track cut: radius in m from which two track cuts are applied"); + + O2_DEFINE_CONFIGURABLE(cfgPairCutPhoton, float, -1, "Pair cut on photons: -1 = off; >0 otherwise distance value (suggested: 0.004)") + O2_DEFINE_CONFIGURABLE(cfgPairCutK0, float, -1, "Pair cut on K0s: -1 = off; >0 otherwise distance value (suggested: 0.005)") + O2_DEFINE_CONFIGURABLE(cfgPairCutLambda, float, -1, "Pair cut on Lambda: -1 = off; >0 otherwise distance value (suggested: 0.005)") + O2_DEFINE_CONFIGURABLE(cfgPairCutPhi, float, -1, "Pair cut on Phi: -1 = off; >0 otherwise distance value") + O2_DEFINE_CONFIGURABLE(cfgPairCutRho, float, -1, "Pair cut on Rho: -1 = off; >0 otherwise distance value") + + O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path to efficiency object for trigger particles") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path to efficiency object for associated particles") + + // Filters and input definitions + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPt) && ((aod::track::isGlobalTrack == (uint8_t) true) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); + using myTracks = soa::Filtered<soa::Join<aod::Tracks, aod::TrackSelection>>; + + // Output definitions + OutputObj<CorrelationContainer> same{"sameEvent"}; + OutputObj<CorrelationContainer> mixed{"mixedEvent"}; + + struct Config { + bool mPairCuts = false; + THn* mEfficiencyTrigger = nullptr; + THn* mEfficiencyAssociated = nullptr; + } cfg; + + HistogramRegistry registry{"registry", { + {"yields", "centrality vs pT vs eta", {HistType::kTH3F, {{100, 0, 100, "centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}}, // + {"etaphi", "centrality vs eta vs phi", {HistType::kTH3F, {{100, 0, 100, "centrality"}, {100, -2, 2, "#eta"}, {200, 0, 2 * M_PI, "#varphi"}}}} // + }}; + + PairCuts mPairCuts; + + Service<o2::ccdb::BasicCCDBManager> ccdb; + + void init(o2::framework::InitContext&) + { + // --- CONFIGURATION --- + const char* binning = + "vertex: 7 | -7, 7\n" + "delta_phi: 72 | -1.570796, 4.712389\n" + "delta_eta: 40 | -2.0, 2.0\n" + "p_t_assoc: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0\n" + "p_t_trigger: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0\n" + "multiplicity: 0, 5, 10, 20, 30, 40, 50, 100.1\n" + "eta: 20 | -1.0, 1.0\n" + "p_t_leading: 100 | 0.0, 50.0\n" + "p_t_leading_course: 0.5, 1.0, 2.0, 3.0, 4.0, 6.0, 8.0\n" + "p_t_eff: 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0\n" + "vertex_eff: 10 | -10, 10\n"; + + mPairCuts.SetHistogramRegistry(®istry); + + if (cfgPairCutPhoton > 0 || cfgPairCutK0 > 0 || cfgPairCutLambda > 0 || cfgPairCutPhi > 0 || cfgPairCutRho > 0) { + mPairCuts.SetPairCut(PairCuts::Photon, cfgPairCutPhoton); + mPairCuts.SetPairCut(PairCuts::K0, cfgPairCutK0); + mPairCuts.SetPairCut(PairCuts::Lambda, cfgPairCutLambda); + mPairCuts.SetPairCut(PairCuts::Phi, cfgPairCutPhi); + mPairCuts.SetPairCut(PairCuts::Rho, cfgPairCutRho); + cfg.mPairCuts = true; + } + + if (cfgTwoTrackCut > 0) { + mPairCuts.SetTwoTrackCuts(cfgTwoTrackCut, cfgTwoTrackCutMinRadius); + } + + // --- OBJECT INIT --- + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", "NumberDensityPhiCentralityVtx", binning)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", "NumberDensityPhiCentralityVtx", binning)); + + // o2-ccdb-upload -p Users/jgrosseo/correlations/LHC15o -f /tmp/correction_2011_global.root -k correction + + ccdb->setURL("http://ccdb-test.cern.ch:8080"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + long now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time + + if (cfgEfficiencyTrigger.value.empty() == false) { + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp<THnT<float>>(cfgEfficiencyTrigger, now); + LOGF(info, "Loaded efficiency histogram for trigger particles from %s (%p)", cfgEfficiencyTrigger.value.c_str(), (void*)cfg.mEfficiencyTrigger); + } + if (cfgEfficiencyAssociated.value.empty() == false) { + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp<THnT<float>>(cfgEfficiencyAssociated, now); + LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), (void*)cfg.mEfficiencyAssociated); + } + } + + // Version with explicit nested loop + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels, aod::Cents>>::iterator const& collision, aod::BCsWithTimestamps const&, myTracks const& tracks) + { + auto bc = collision.bc_as<aod::BCsWithTimestamps>(); + if (cfgEfficiencyTrigger.value.empty() == false) { + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp<THnT<float>>(cfgEfficiencyTrigger, bc.timestamp()); + LOGF(info, "Loaded efficiency histogram for trigger particles from %s (%p)", cfgEfficiencyTrigger.value.c_str(), (void*)cfg.mEfficiencyTrigger); + } + if (cfgEfficiencyAssociated.value.empty() == false) { + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp<THnT<float>>(cfgEfficiencyAssociated, bc.timestamp()); + LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), (void*)cfg.mEfficiencyAssociated); + } + + LOGF(info, "Tracks for collision: %d | Vertex: %.1f | INT7: %d | V0M: %.1f", tracks.size(), collision.posZ(), collision.sel7(), collision.centV0M()); + + const auto centrality = collision.centV0M(); + + same->fillEvent(centrality, CorrelationContainer::kCFStepAll); + + if (!collision.sel7()) { + return; + } + + same->fillEvent(centrality, CorrelationContainer::kCFStepTriggered); + + // vertex already checked as filter + same->fillEvent(centrality, CorrelationContainer::kCFStepVertex); + + same->fillEvent(centrality, CorrelationContainer::kCFStepReconstructed); + + int bSign = 1; // TODO magnetic field from CCDB + + // Cache efficiency for particles (too many FindBin lookups) + float* efficiencyAssociated = nullptr; + if (cfg.mEfficiencyAssociated) { + efficiencyAssociated = new float[tracks.size()]; + int i = 0; + for (auto& track1 : tracks) { + efficiencyAssociated[i++] = getEfficiency(cfg.mEfficiencyAssociated, track1.eta(), track1.pt(), centrality, collision.posZ()); + } + } + + for (auto& track1 : tracks) { + // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD()); + + registry.fill(HIST("yields"), centrality, track1.pt(), track1.eta()); + registry.fill(HIST("etaphi"), centrality, track1.eta(), track1.phi()); + + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) { + continue; + } + + float triggerWeight = 1.0; + if (cfg.mEfficiencyTrigger) { + triggerWeight = getEfficiency(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), centrality, collision.posZ()); + } + + same->getTriggerHist()->Fill(CorrelationContainer::kCFStepReconstructed, track1.pt(), centrality, collision.posZ(), triggerWeight); + //mixed->getTriggerHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); + + int i = -1; + for (auto& track2 : tracks) { + i++; // HACK + if (track1 == track2) { + continue; + } + + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) { + continue; + } + if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) { + continue; + } + + if (cfg.mPairCuts && mPairCuts.conversionCuts(track1, track2)) { + continue; + } + + if (cfgTwoTrackCut > 0 && mPairCuts.twoTrackCut(track1, track2, bSign)) { + continue; + } + + float associatedWeight = 1.0; + if (cfg.mEfficiencyAssociated) { + associatedWeight = efficiencyAssociated[i]; + } + + float deltaPhi = track1.phi() - track2.phi(); + if (deltaPhi > 1.5 * TMath::Pi()) { + deltaPhi -= TMath::TwoPi(); + } + if (deltaPhi < -0.5 * TMath::Pi()) { + deltaPhi += TMath::TwoPi(); + } + + same->getPairHist()->Fill(CorrelationContainer::kCFStepReconstructed, + track1.eta() - track2.eta(), track2.pt(), track1.pt(), centrality, deltaPhi, collision.posZ(), + triggerWeight * associatedWeight); + //mixed->getPairHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); + } + } + + delete[] efficiencyAssociated; + } + + // Version with combinations + void process2(soa::Join<aod::Collisions, aod::Cents>::iterator const& collision, soa::Filtered<aod::Tracks> const& tracks) + { + LOGF(info, "Tracks for collision (Combination run): %d", tracks.size()); + + const auto centrality = collision.centV0M(); + + int bSign = 1; // TODO magnetic field from CCDB + + for (auto track1 = tracks.begin(); track1 != tracks.end(); ++track1) { + + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) { + continue; + } + + // LOGF(info, "TRACK %f %f | %f %f | %f %f", track1.eta(), track1.eta(), track1.phi(), track1.phi2(), track1.pt(), track1.pt()); + + same->getTriggerHist()->Fill(CorrelationContainer::kCFStepReconstructed, track1.pt(), centrality, collision.posZ()); + //mixed->getTriggerHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); + } + + for (auto& [track1, track2] : combinations(tracks, tracks)) { + //LOGF(info, "Combination %d %d", track1.index(), track2.index()); + + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) { + continue; + } + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) { + continue; + } + if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) { + continue; + } + + if (cfg.mPairCuts && mPairCuts.conversionCuts(track1, track2)) { + continue; + } + + if (cfgTwoTrackCut > 0 && mPairCuts.twoTrackCut(track1, track2, bSign)) { + continue; + } + + float deltaPhi = track1.phi() - track2.phi(); + if (deltaPhi > 1.5 * TMath::Pi()) { + deltaPhi -= TMath::TwoPi(); + } + if (deltaPhi < -0.5 * TMath::Pi()) { + deltaPhi += TMath::TwoPi(); + } + + same->getPairHist()->Fill(CorrelationContainer::kCFStepReconstructed, + track1.eta() - track2.eta(), track2.pt(), track1.pt(), centrality, deltaPhi, collision.posZ()); + //mixed->getPairHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); + } + } + + double getEfficiency(THn* eff, float eta, float pt, float centrality, float posZ) + { + int effVars[4]; + effVars[0] = eff->GetAxis(0)->FindBin(eta); + effVars[1] = eff->GetAxis(1)->FindBin(pt); + effVars[2] = eff->GetAxis(2)->FindBin(centrality); + effVars[3] = eff->GetAxis(3)->FindBin(posZ); + return eff->GetBinContent(effVars); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<CorrelationTask>("correlation-task")}; +} diff --git a/Analysis/Tasks/PWGCF/correlationsFiltered.cxx b/Analysis/Tasks/PWGCF/correlationsFiltered.cxx new file mode 100644 index 0000000000000..6a8549d1ad95d --- /dev/null +++ b/Analysis/Tasks/PWGCF/correlationsFiltered.cxx @@ -0,0 +1,445 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "Framework/StepTHn.h" + +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/CorrelationContainer.h" +#include "AnalysisDataModel/CFDerived.h" + +#include <TH1F.h> +#include <cmath> +#include <TDirectory.h> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable<TYPE> NAME{#NAME, DEFAULT, HELP}; + +struct CorrelationTask { + + // Configuration + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 7.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + + O2_DEFINE_CONFIGURABLE(cfgPtOrder, int, 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)"); + O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative"); + O2_DEFINE_CONFIGURABLE(cfgAssociatedCharge, int, 0, "Select on charge of associated particle: 0 = all; 1 = positive; -1 = negative"); + O2_DEFINE_CONFIGURABLE(cfgPairCharge, int, 0, "Select on charge of particle pair: 0 = all; 1 = like sign; -1 = unlike sign"); + + O2_DEFINE_CONFIGURABLE(cfgTwoTrackCut, float, -1, "Two track cut: -1 = off; >0 otherwise distance value"); + O2_DEFINE_CONFIGURABLE(cfgTwoTrackCutMinRadius, float, 0.8f, "Two track cut: radius in m from which two track cuts are applied"); + + O2_DEFINE_CONFIGURABLE(cfgPairCutPhoton, float, -1, "Pair cut on photons: -1 = off; >0 otherwise distance value (suggested: 0.004)") + O2_DEFINE_CONFIGURABLE(cfgPairCutK0, float, -1, "Pair cut on K0s: -1 = off; >0 otherwise distance value (suggested: 0.005)") + O2_DEFINE_CONFIGURABLE(cfgPairCutLambda, float, -1, "Pair cut on Lambda: -1 = off; >0 otherwise distance value (suggested: 0.005)") + O2_DEFINE_CONFIGURABLE(cfgPairCutPhi, float, -1, "Pair cut on Phi: -1 = off; >0 otherwise distance value") + O2_DEFINE_CONFIGURABLE(cfgPairCutRho, float, -1, "Pair cut on Rho: -1 = off; >0 otherwise distance value") + + // Filters and input definitions + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (nabs(aod::cftrack::eta) < cfgCutEta) && (aod::cftrack::pt > cfgCutPt); + + // Output definitions + OutputObj<CorrelationContainer> same{"sameEvent"}; + OutputObj<CorrelationContainer> mixed{"mixedEvent"}; + //OutputObj<TDirectory> qaOutput{"qa"}; + + enum PairCuts { Photon = 0, + K0, + Lambda, + Phi, + Rho }; + struct Config { + bool mPairCuts = false; + //THn* mEfficiencyTrigger = nullptr; + //THn* mEfficiencyAssociated = nullptr; + } cfg; + + struct QA { + TH3F* mTwoTrackDistancePt[2] = {nullptr}; // control histograms for two-track efficiency study: dphi*_min vs deta (0 = before cut, 1 = after cut) + TH2F* mControlConvResoncances = nullptr; // control histograms for cuts on conversions and resonances + } qa; + + // HistogramRegistry registry{"qa", true, { + // {"yields", "centrality vs pT vs eta", {HistogramType::kTH3F, { {100, 0, 100, "centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"} }}}, + // {"etaphi", "centrality vs eta vs phi", {HistogramType::kTH3F, { {100, 0, 100, "centrality"}, {100, -2, 2, "#eta"}, {200, 0, 2 * M_PI, "#varphi"} }}} + // }}; + + OutputObj<TH3F> yields{TH3F("yields", "centrality vs pT vs eta", 100, 0, 100, 40, 0, 20, 100, -2, 2)}; + OutputObj<TH3F> etaphi{TH3F("etaphi", "centrality vs eta vs phi", 100, 0, 100, 100, -2, 2, 200, 0, 2 * M_PI)}; + + void init(o2::framework::InitContext&) + { + // --- CONFIGURATION --- + const char* binning = + "vertex: 7 | -7, 7\n" + "delta_phi: 72 | -1.570796, 4.712389\n" + "delta_eta: 40 | -2.0, 2.0\n" + "p_t_assoc: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0\n" + "p_t_trigger: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0\n" + "multiplicity: 0, 5, 10, 20, 30, 40, 50, 100.1\n" + "eta: 20 | -1.0, 1.0\n" + "p_t_leading: 100 | 0.0, 50.0\n" + "p_t_leading_course: 0.5, 1.0, 2.0, 3.0, 4.0, 6.0, 8.0\n" + "p_t_eff: 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0\n" + "vertex_eff: 10 | -10, 10\n"; + + if (cfgPairCutPhoton > 0 || cfgPairCutK0 > 0 || cfgPairCutLambda > 0 || cfgPairCutPhi > 0 || cfgPairCutRho > 0) { + cfg.mPairCuts = true; + } + + // --- OBJECT INIT --- + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", "NumberDensityPhiCentralityVtx", binning)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", "NumberDensityPhiCentralityVtx", binning)); + //qaOutput.setObject(new TDirectory("qa", "qa")); + + if (cfgTwoTrackCut > 0) { + qa.mTwoTrackDistancePt[0] = new TH3F("TwoTrackDistancePt[0]", ";#Delta#eta;#Delta#varphi^{*}_{min};#Delta p_{T}", 100, -0.15, 0.15, 100, -0.05, 0.05, 20, 0, 10); + qa.mTwoTrackDistancePt[1] = (TH3F*)qa.mTwoTrackDistancePt[0]->Clone("TwoTrackDistancePt[1]"); + //qaOutput->Add(qa.mTwoTrackDistancePt[0]); + //qaOutput->Add(qa.mTwoTrackDistancePt[1]); + } + + if (cfg.mPairCuts) { + qa.mControlConvResoncances = new TH2F("ControlConvResoncances", ";id;delta mass", 6, -0.5, 5.5, 500, -0.5, 0.5); + //qaOutput->Add(qa.mControlConvResoncances); + } + } + + // Version with explicit nested loop + void process(soa::Filtered<aod::CFCollisions>::iterator const& collision, soa::Filtered<aod::CFTracks> const& tracks) + { + LOGF(info, "Tracks for collision: %d | Vertex: %.1f | V0M: %.1f", tracks.size(), collision.posZ(), collision.centV0M()); + + const auto centrality = collision.centV0M(); + + same->fillEvent(centrality, CorrelationContainer::kCFStepReconstructed); + + int bSign = 1; // TODO magnetic field from CCDB + + for (auto& track1 : tracks) { + + // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD()); + + // control histograms + // ((TH3*) (registry.get("yields").get()))->Fill(centrality, track1.pt(), track1.eta()); + // ((TH3*) (registry.get("etaphi").get()))->Fill(centrality, track1.eta(), track1.phi()); + yields->Fill(centrality, track1.pt(), track1.eta()); + etaphi->Fill(centrality, track1.eta(), track1.phi()); + + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) { + continue; + } + + same->getTriggerHist()->Fill(CorrelationContainer::kCFStepReconstructed, track1.pt(), centrality, collision.posZ()); + //mixed->getTriggerHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); + + for (auto& track2 : tracks) { + if (track1 == track2) { + continue; + } + + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) { + continue; + } + if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) { + continue; + } + + if (cfg.mPairCuts && conversionCuts(track1, track2)) { + continue; + } + + if (cfgTwoTrackCut > 0 && twoTrackCut(track1, track2, bSign)) { + continue; + } + + float deltaPhi = track1.phi() - track2.phi(); + if (deltaPhi > 1.5 * TMath::Pi()) { + deltaPhi -= TMath::TwoPi(); + } + if (deltaPhi < -0.5 * TMath::Pi()) { + deltaPhi += TMath::TwoPi(); + } + + same->getPairHist()->Fill(CorrelationContainer::kCFStepReconstructed, + track1.eta() - track2.eta(), track2.pt(), track1.pt(), centrality, deltaPhi, collision.posZ()); + //mixed->getPairHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); + } + } + } + + template <typename T> + bool conversionCuts(T const& track1, T const& track2) + { + // skip if like sign + if (track1.charge() * track2.charge() > 0) { + return false; + } + + bool decision = false; + + if (conversionCut(track1, track2, Photon, cfgPairCutPhoton)) { + decision = true; + } + if (conversionCut(track1, track2, K0, cfgPairCutK0)) { + decision = true; + } + if (conversionCut(track1, track2, Lambda, cfgPairCutLambda)) { + decision = true; + } + if (conversionCut(track2, track1, Lambda, cfgPairCutLambda)) { + decision = true; + } + if (conversionCut(track1, track2, Phi, cfgPairCutPhi)) { + decision = true; + } + if (conversionCut(track1, track2, Rho, cfgPairCutRho)) { + decision = true; + } + + return decision; + } + + template <typename T> + bool conversionCut(T const& track1, T const& track2, PairCuts conv, double cut) + { + //LOGF(info, "pt is %f %f", track1.pt(), track2.pt()); + + if (cut < 0) { + return false; + } + + double massD1, massD2, massM; + + switch (conv) { + case Photon: + massD1 = 0.51e-3; + massD2 = 0.51e-3; + massM = 0; + break; + case K0: + massD1 = 0.1396; + massD2 = 0.1396; + massM = 0.4976; + break; + case Lambda: + massD1 = 0.9383; + massD2 = 0.1396; + massM = 1.115; + break; + case Phi: + massD1 = 0.4937; + massD2 = 0.4937; + massM = 1.019; + break; + case Rho: + massD1 = 0.1396; + massD2 = 0.1396; + massM = 0.770; + break; + } + + auto massC = getInvMassSquaredFast(track1, massD1, track2, massD2); + + if (TMath::Abs(massC - massM * massM) > cut * 5) { + return false; + } + + massC = getInvMassSquared(track1, massD1, track2, massD2); + qa.mControlConvResoncances->Fill(static_cast<int>(conv), massC - massM * massM); + if (massC > (massM - cut) * (massM - cut) && massC < (massM + cut) * (massM + cut)) { + return true; + } + + return false; + } + + template <typename T> + double getInvMassSquared(T const& track1, double m0_1, T const& track2, double m0_2) + { + // calculate inv mass squared + // same can be achieved, but with more computing time with + /*TLorentzVector photon, p1, p2; + p1.SetPtEtaPhiM(triggerParticle->Pt(), triggerEta, triggerParticle->Phi(), 0.510e-3); + p2.SetPtEtaPhiM(particle->Pt(), eta[j], particle->Phi(), 0.510e-3); + photon = p1+p2; + photon.M()*/ + + float tantheta1 = 1e10; + + if (track1.eta() < -1e-10 || track1.eta() > 1e-10) { + float expTmp = TMath::Exp(-track1.eta()); + tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float tantheta2 = 1e10; + if (track2.eta() < -1e-10 || track2.eta() > 1e-10) { + float expTmp = TMath::Exp(-track2.eta()); + tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float e1squ = m0_1 * m0_1 + track1.pt() * track1.pt() * (1.0 + 1.0 / tantheta1 / tantheta1); + float e2squ = m0_2 * m0_2 + track2.pt() * track2.pt() * (1.0 + 1.0 / tantheta2 / tantheta2); + + float mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (track1.pt() * track2.pt() * (TMath::Cos(track1.phi() - track2.phi()) + 1.0 / tantheta1 / tantheta2))); + + // Printf(Form("%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2)); + + return mass2; + } + + template <typename T> + double getInvMassSquaredFast(T const& track1, double m0_1, T const& track2, double m0_2) + { + // calculate inv mass squared approximately + + const float eta1 = track1.eta(); + const float eta2 = track2.eta(); + const float phi1 = track1.phi(); + const float phi2 = track2.phi(); + const float pt1 = track1.pt(); + const float pt2 = track2.pt(); + + float tantheta1 = 1e10; + + if (eta1 < -1e-10 || eta1 > 1e-10) { + float expTmp = 1.0 - eta1 + eta1 * eta1 / 2 - eta1 * eta1 * eta1 / 6 + eta1 * eta1 * eta1 * eta1 / 24; + tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float tantheta2 = 1e10; + if (eta2 < -1e-10 || eta2 > 1e-10) { + float expTmp = 1.0 - eta2 + eta2 * eta2 / 2 - eta2 * eta2 * eta2 / 6 + eta2 * eta2 * eta2 * eta2 / 24; + tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); + } + + float e1squ = m0_1 * m0_1 + pt1 * pt1 * (1.0 + 1.0 / tantheta1 / tantheta1); + float e2squ = m0_2 * m0_2 + pt2 * pt2 * (1.0 + 1.0 / tantheta2 / tantheta2); + + // fold onto 0...pi + float deltaPhi = TMath::Abs(phi1 - phi2); + while (deltaPhi > TMath::TwoPi()) { + deltaPhi -= TMath::TwoPi(); + } + if (deltaPhi > TMath::Pi()) { + deltaPhi = TMath::TwoPi() - deltaPhi; + } + + float cosDeltaPhi = 0; + if (deltaPhi < TMath::Pi() / 3) { + cosDeltaPhi = 1.0 - deltaPhi * deltaPhi / 2 + deltaPhi * deltaPhi * deltaPhi * deltaPhi / 24; + } else if (deltaPhi < 2 * TMath::Pi() / 3) { + cosDeltaPhi = -(deltaPhi - TMath::Pi() / 2) + 1.0 / 6 * TMath::Power((deltaPhi - TMath::Pi() / 2), 3); + } else { + cosDeltaPhi = -1.0 + 1.0 / 2.0 * (deltaPhi - TMath::Pi()) * (deltaPhi - TMath::Pi()) - 1.0 / 24.0 * TMath::Power(deltaPhi - TMath::Pi(), 4); + } + + double mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (pt1 * pt2 * (cosDeltaPhi + 1.0 / tantheta1 / tantheta2))); + + // Printf(Form("%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2)); + + return mass2; + } + + template <typename T> + bool twoTrackCut(T const& track1, T const& track2, int bSign) + { + // the variables & cuthave been developed by the HBT group + // see e.g. https://indico.cern.ch/materialDisplay.py?contribId=36&sessionId=6&materialId=slides&confId=142700 + + auto deta = track1.eta() - track2.eta(); + + // optimization + if (TMath::Abs(deta) < cfgTwoTrackCut * 2.5 * 3) { + // check first boundaries to see if is worth to loop and find the minimum + float dphistar1 = getDPhiStar(track1, track2, cfgTwoTrackCutMinRadius, bSign); + float dphistar2 = getDPhiStar(track1, track2, 2.5, bSign); + + const float kLimit = cfgTwoTrackCut * 3; + + if (TMath::Abs(dphistar1) < kLimit || TMath::Abs(dphistar2) < kLimit || dphistar1 * dphistar2 < 0) { + float dphistarminabs = 1e5; + float dphistarmin = 1e5; + for (Double_t rad = cfgTwoTrackCutMinRadius; rad < 2.51; rad += 0.01) { + float dphistar = getDPhiStar(track1, track2, rad, bSign); + + float dphistarabs = TMath::Abs(dphistar); + + if (dphistarabs < dphistarminabs) { + dphistarmin = dphistar; + dphistarminabs = dphistarabs; + } + } + + qa.mTwoTrackDistancePt[0]->Fill(deta, dphistarmin, TMath::Abs(track1.pt() - track2.pt())); + + if (dphistarminabs < cfgTwoTrackCut && TMath::Abs(deta) < cfgTwoTrackCut) { + //Printf("Removed track pair %ld %ld with %f %f %f %f %d %f %f %d %d", track1.index(), track2.index(), deta, dphistarminabs, track1.phi2(), track1.pt(), track1.charge(), track2.phi2(), track2.pt(), track2.charge(), bSign); + return true; + } + + qa.mTwoTrackDistancePt[1]->Fill(deta, dphistarmin, TMath::Abs(track1.pt() - track2.pt())); + } + } + + return false; + } + + template <typename T> + float getDPhiStar(T const& track1, T const& track2, float radius, float bSign) + { + // + // calculates dphistar + // + + auto phi1 = track1.phi(); + auto pt1 = track1.pt(); + auto charge1 = track1.charge(); + + auto phi2 = track2.phi(); + auto pt2 = track2.pt(); + auto charge2 = track2.charge(); + + float dphistar = phi1 - phi2 - charge1 * bSign * TMath::ASin(0.075 * radius / pt1) + charge2 * bSign * TMath::ASin(0.075 * radius / pt2); + + static const Double_t kPi = TMath::Pi(); + + if (dphistar > kPi) { + dphistar = kPi * 2 - dphistar; + } + if (dphistar < -kPi) { + dphistar = -kPi * 2 - dphistar; + } + if (dphistar > kPi) { // might look funny but is needed + dphistar = kPi * 2 - dphistar; + } + + return dphistar; + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<CorrelationTask>("correlation-task")}; +} diff --git a/Analysis/Tasks/correlationsMixed.cxx b/Analysis/Tasks/PWGCF/correlationsMixed.cxx similarity index 80% rename from Analysis/Tasks/correlationsMixed.cxx rename to Analysis/Tasks/PWGCF/correlationsMixed.cxx index 8d36c33d85987..d2197ac895df3 100644 --- a/Analysis/Tasks/correlationsMixed.cxx +++ b/Analysis/Tasks/PWGCF/correlationsMixed.cxx @@ -11,29 +11,17 @@ #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" #include "Framework/ASoAHelpers.h" +#include "Framework/StepTHn.h" -#include "Analysis/EventSelection.h" -#include "Analysis/Centrality.h" -#include "Analysis/StepTHn.h" -#include "Analysis/CorrelationContainer.h" -#include "Analysis/TrackSelectionTables.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/CorrelationContainer.h" +#include "AnalysisDataModel/TrackSelectionTables.h" #include <TH1F.h> #include <cmath> #include <TDirectory.h> -namespace o2::aod -{ -namespace etaphi -{ -DECLARE_SOA_COLUMN(Etam, etam, float); -DECLARE_SOA_COLUMN(Phim, phim, float); -DECLARE_SOA_COLUMN(Ptm, ptm, float); -} // namespace etaphi -DECLARE_SOA_TABLE(EtaPhi, "AOD", "ETAPHI", - etaphi::Etam, etaphi::Phim, etaphi::Ptm); -} // namespace o2::aod - namespace o2::aod { namespace hash @@ -49,21 +37,6 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -struct ATask { - Produces<aod::EtaPhi> etaphi; - - void process(aod::Tracks const& tracks) - { - for (auto& track : tracks) { - float eta = log(tan(0.25f * static_cast<float>(M_PI) - 0.5f * atan(track.tgl()))); - float phi = asin(track.snp()) + track.alpha() + static_cast<float>(M_PI); - float pt = fabs(1.0f / track.signed1Pt()); - - etaphi(eta, phi, pt); - } - } -}; - struct HashTask { std::vector<float> vtxBins{-7.0f, -5.0f, -3.0f, -1.0f, 1.0f, 3.0f, 5.0f, 7.0f}; std::vector<float> multBins{0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f}; @@ -73,10 +46,12 @@ struct HashTask { int getHash(std::vector<float> const& vtxBins, std::vector<float> const& multBins, float vtx, float mult) { // underflow - if (vtx < vtxBins[0]) + if (vtx < vtxBins[0]) { return -1; - if (mult < multBins[0]) + } + if (mult < multBins[0]) { return -1; + } for (int i = 1; i < vtxBins.size(); i++) { if (vtx < vtxBins[i]) { @@ -106,10 +81,10 @@ struct HashTask { struct CorrelationTask { // Input definitions - using myTracks = soa::Filtered<soa::Join<aod::Tracks, aod::EtaPhi>>; + using myTracks = soa::Filtered<aod::Tracks>; // Filters - Filter trackFilter = (aod::etaphi::etam > -0.8f) && (aod::etaphi::etam < 0.8f) && (aod::etaphi::ptm > 1.0f); + Filter trackFilter = (aod::track::eta > -0.8f) && (aod::track::eta < 0.8f) && (aod::track::pt > 1.0f); // Output definitions OutputObj<CorrelationContainer> same{"sameEvent"}; @@ -162,8 +137,9 @@ struct CorrelationTask { "p_t_eff: 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0\n" "vertex_eff: -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10\n"; - if (cfgPairCutPhoton > 0 || cfgPairCutK0 > 0 || cfgPairCutLambda > 0 || cfgPairCutPhi > 0 || cfgPairCutRho > 0) + if (cfgPairCutPhoton > 0 || cfgPairCutK0 > 0 || cfgPairCutLambda > 0 || cfgPairCutPhi > 0 || cfgPairCutRho > 0) { cfg.mPairCuts = true; + } // --- OBJECT INIT --- same.setObject(new CorrelationContainer("sameEvent", "sameEvent", "NumberDensityPhiCentrality", binning)); @@ -221,47 +197,41 @@ struct CorrelationTask { for (auto& track1 : tracks1) { - if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) { continue; + } - //LOGF(info, "TRACK %f %f | %f %f | %f %f", track1.eta(), track1.etam(), track1.phi(), track1.phim(), track1.pt(), track1.ptm()); - - double eventValues[3]; - eventValues[0] = track1.ptm(); - eventValues[1] = collision1.centV0M(); - eventValues[2] = collision1.posZ(); + //LOGF(info, "TRACK %f %f | %f %f | %f %f", track1.eta(), track1.eta(), track1.phi(), track1.phi(), track1.pt(), track1.pt()); - mixed->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); + mixed->getTriggerHist()->Fill(CorrelationContainer::kCFStepReconstructed, track1.pt(), collision1.centV0M(), collision1.posZ()); for (auto& track2 : tracks2) { - if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) { continue; - if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) + } + if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) { continue; + } - if (cfg.mPairCuts && conversionCuts(track1, track2)) + if (cfg.mPairCuts && conversionCuts(track1, track2)) { continue; + } - if (cfgTwoTrackCut > 0 && twoTrackCut(track1, track2, bSign)) + if (cfgTwoTrackCut > 0 && twoTrackCut(track1, track2, bSign)) { continue; + } - double values[6] = {0}; - - values[0] = track1.etam() - track2.etam(); - values[1] = track1.ptm(); - values[2] = track2.ptm(); - values[3] = collision1.centV0M(); - - values[4] = track1.phim() - track2.phim(); - if (values[4] > 1.5 * TMath::Pi()) - values[4] -= TMath::TwoPi(); - if (values[4] < -0.5 * TMath::Pi()) - values[4] += TMath::TwoPi(); - - values[5] = collision1.posZ(); + float deltaPhi = track1.phi() - track2.phi(); + if (deltaPhi > 1.5 * TMath::Pi()) { + deltaPhi -= TMath::TwoPi(); + } + if (deltaPhi < -0.5 * TMath::Pi()) { + deltaPhi += TMath::TwoPi(); + } - mixed->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); + mixed->getPairHist()->Fill(CorrelationContainer::kCFStepReconstructed, + track1.eta() - track2.eta(), track2.pt(), track1.pt(), collision1.centV0M(), deltaPhi, collision1.posZ()); } } } @@ -271,23 +241,30 @@ struct CorrelationTask { bool conversionCuts(T const& track1, T const& track2) { // skip if like sign - if (track1.charge() * track2.charge() > 0) + if (track1.charge() * track2.charge() > 0) { return false; + } bool decision = false; - if (conversionCut(track1, track2, Photon, cfgPairCutPhoton)) + if (conversionCut(track1, track2, Photon, cfgPairCutPhoton)) { decision = true; - if (conversionCut(track1, track2, K0, cfgPairCutK0)) + } + if (conversionCut(track1, track2, K0, cfgPairCutK0)) { decision = true; - if (conversionCut(track1, track2, Lambda, cfgPairCutLambda)) + } + if (conversionCut(track1, track2, Lambda, cfgPairCutLambda)) { decision = true; - if (conversionCut(track2, track1, Lambda, cfgPairCutLambda)) + } + if (conversionCut(track2, track1, Lambda, cfgPairCutLambda)) { decision = true; - if (conversionCut(track1, track2, Phi, cfgPairCutPhi)) + } + if (conversionCut(track1, track2, Phi, cfgPairCutPhi)) { decision = true; - if (conversionCut(track1, track2, Rho, cfgPairCutRho)) + } + if (conversionCut(track1, track2, Rho, cfgPairCutRho)) { decision = true; + } return decision; } @@ -295,10 +272,11 @@ struct CorrelationTask { template <typename T> bool conversionCut(T const& track1, T const& track2, PairCuts conv, double cut) { - //LOGF(info, "pt is %f %f", track1.ptm(), track2.ptm()); + //LOGF(info, "pt is %f %f", track1.pt(), track2.pt()); - if (cut < 0) + if (cut < 0) { return false; + } double massD1, massD2, massM; @@ -332,13 +310,15 @@ struct CorrelationTask { auto massC = getInvMassSquaredFast(track1, massD1, track2, massD2); - if (TMath::Abs(massC - massM * massM) > cut * 5) + if (TMath::Abs(massC - massM * massM) > cut * 5) { return false; + } massC = getInvMassSquared(track1, massD1, track2, massD2); qa.mControlConvResoncances->Fill(static_cast<int>(conv), massC - massM * massM); - if (massC > (massM - cut) * (massM - cut) && massC < (massM + cut) * (massM + cut)) + if (massC > (massM - cut) * (massM - cut) && massC < (massM + cut) * (massM + cut)) { return true; + } return false; } @@ -356,21 +336,21 @@ struct CorrelationTask { float tantheta1 = 1e10; - if (track1.etam() < -1e-10 || track1.etam() > 1e-10) { - float expTmp = TMath::Exp(-track1.etam()); + if (track1.eta() < -1e-10 || track1.eta() > 1e-10) { + float expTmp = TMath::Exp(-track1.eta()); tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); } float tantheta2 = 1e10; - if (track2.etam() < -1e-10 || track2.etam() > 1e-10) { - float expTmp = TMath::Exp(-track2.etam()); + if (track2.eta() < -1e-10 || track2.eta() > 1e-10) { + float expTmp = TMath::Exp(-track2.eta()); tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); } - float e1squ = m0_1 * m0_1 + track1.ptm() * track1.ptm() * (1.0 + 1.0 / tantheta1 / tantheta1); - float e2squ = m0_2 * m0_2 + track2.ptm() * track2.ptm() * (1.0 + 1.0 / tantheta2 / tantheta2); + float e1squ = m0_1 * m0_1 + track1.pt() * track1.pt() * (1.0 + 1.0 / tantheta1 / tantheta1); + float e2squ = m0_2 * m0_2 + track2.pt() * track2.pt() * (1.0 + 1.0 / tantheta2 / tantheta2); - float mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (track1.ptm() * track2.ptm() * (TMath::Cos(track1.phim() - track2.phim()) + 1.0 / tantheta1 / tantheta2))); + float mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (track1.pt() * track2.pt() * (TMath::Cos(track1.phi() - track2.phi()) + 1.0 / tantheta1 / tantheta2))); // Printf(Form("%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2)); @@ -382,12 +362,12 @@ struct CorrelationTask { { // calculate inv mass squared approximately - const float eta1 = track1.etam(); - const float eta2 = track2.etam(); - const float phi1 = track1.phim(); - const float phi2 = track2.phim(); - const float pt1 = track1.ptm(); - const float pt2 = track2.ptm(); + const float eta1 = track1.eta(); + const float eta2 = track2.eta(); + const float phi1 = track1.phi(); + const float phi2 = track2.phi(); + const float pt1 = track1.pt(); + const float pt2 = track2.pt(); float tantheta1 = 1e10; @@ -407,18 +387,21 @@ struct CorrelationTask { // fold onto 0...pi float deltaPhi = TMath::Abs(phi1 - phi2); - while (deltaPhi > TMath::TwoPi()) + while (deltaPhi > TMath::TwoPi()) { deltaPhi -= TMath::TwoPi(); - if (deltaPhi > TMath::Pi()) + } + if (deltaPhi > TMath::Pi()) { deltaPhi = TMath::TwoPi() - deltaPhi; + } float cosDeltaPhi = 0; - if (deltaPhi < TMath::Pi() / 3) + if (deltaPhi < TMath::Pi() / 3) { cosDeltaPhi = 1.0 - deltaPhi * deltaPhi / 2 + deltaPhi * deltaPhi * deltaPhi * deltaPhi / 24; - else if (deltaPhi < 2 * TMath::Pi() / 3) + } else if (deltaPhi < 2 * TMath::Pi() / 3) { cosDeltaPhi = -(deltaPhi - TMath::Pi() / 2) + 1.0 / 6 * TMath::Power((deltaPhi - TMath::Pi() / 2), 3); - else + } else { cosDeltaPhi = -1.0 + 1.0 / 2.0 * (deltaPhi - TMath::Pi()) * (deltaPhi - TMath::Pi()) - 1.0 / 24.0 * TMath::Power(deltaPhi - TMath::Pi(), 4); + } double mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (pt1 * pt2 * (cosDeltaPhi + 1.0 / tantheta1 / tantheta2))); @@ -433,7 +416,7 @@ struct CorrelationTask { // the variables & cuthave been developed by the HBT group // see e.g. https://indico.cern.ch/materialDisplay.py?contribId=36&sessionId=6&materialId=slides&confId=142700 - auto deta = track1.etam() - track2.etam(); + auto deta = track1.eta() - track2.eta(); // optimization if (TMath::Abs(deta) < cfgTwoTrackCut * 2.5 * 3) { @@ -457,14 +440,14 @@ struct CorrelationTask { } } - qa.mTwoTrackDistancePt[0]->Fill(deta, dphistarmin, TMath::Abs(track1.ptm() - track2.ptm())); + qa.mTwoTrackDistancePt[0]->Fill(deta, dphistarmin, TMath::Abs(track1.pt() - track2.pt())); if (dphistarminabs < cfgTwoTrackCut && TMath::Abs(deta) < cfgTwoTrackCut) { - //Printf("Removed track pair %ld %ld with %f %f %f %f %d %f %f %d %d", track1.index(), track2.index(), deta, dphistarminabs, track1.phim(), track1.ptm(), track1.charge(), track2.phim(), track2.ptm(), track2.charge(), bSign); + //Printf("Removed track pair %ld %ld with %f %f %f %f %d %f %f %d %d", track1.index(), track2.index(), deta, dphistarminabs, track1.phi(), track1.pt(), track1.charge(), track2.phi(), track2.pt(), track2.charge(), bSign); return true; } - qa.mTwoTrackDistancePt[1]->Fill(deta, dphistarmin, TMath::Abs(track1.ptm() - track2.ptm())); + qa.mTwoTrackDistancePt[1]->Fill(deta, dphistarmin, TMath::Abs(track1.pt() - track2.pt())); } } @@ -478,24 +461,27 @@ struct CorrelationTask { // calculates dphistar // - auto phi1 = track1.phim(); - auto pt1 = track1.ptm(); + auto phi1 = track1.phi(); + auto pt1 = track1.pt(); auto charge1 = track1.charge(); - auto phi2 = track2.phim(); - auto pt2 = track2.ptm(); + auto phi2 = track2.phi(); + auto pt2 = track2.pt(); auto charge2 = track2.charge(); float dphistar = phi1 - phi2 - charge1 * bSign * TMath::ASin(0.075 * radius / pt1) + charge2 * bSign * TMath::ASin(0.075 * radius / pt2); static const Double_t kPi = TMath::Pi(); - if (dphistar > kPi) + if (dphistar > kPi) { dphistar = kPi * 2 - dphistar; - if (dphistar < -kPi) + } + if (dphistar < -kPi) { dphistar = -kPi * 2 - dphistar; - if (dphistar > kPi) // might look funny but is needed + } + if (dphistar > kPi) { // might look funny but is needed dphistar = kPi * 2 - dphistar; + } return dphistar; } @@ -507,7 +493,7 @@ struct CorrelationTask { // static_assert("Need to pass aod::track"); // // - // // LOGF(info, "pt %f", track1.ptm()); + // // LOGF(info, "pt %f", track1.pt()); // return false; // } @@ -517,7 +503,6 @@ struct CorrelationTask { WorkflowSpec defineDataProcessing(ConfigContext const&) { return WorkflowSpec{ - adaptAnalysisTask<ATask>("produce-etaphi"), adaptAnalysisTask<HashTask>("produce-hashes"), adaptAnalysisTask<CorrelationTask>("correlation-task")}; } diff --git a/Analysis/Tasks/PWGCF/dptdptcorrelations.cxx b/Analysis/Tasks/PWGCF/dptdptcorrelations.cxx new file mode 100644 index 0000000000000..2f11edb496b7f --- /dev/null +++ b/Analysis/Tasks/PWGCF/dptdptcorrelations.cxx @@ -0,0 +1,854 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisConfigurableCuts.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include <TROOT.h> +#include <TParameter.h> +#include <TList.h> +#include <TDirectory.h> +#include <TFolder.h> +#include <TH1.h> +#include <TH2.h> +#include <TH3.h> +#include <TProfile3D.h> + +#include <cmath> + +using namespace o2; +using namespace o2::framework; +using namespace o2::soa; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + ConfigParamSpec centspec = {"centralities", + VariantType::String, + "00-05,05-10,10-20,20-30,30-40,40-50,50-60,60-70,70-80", + {"Centrality/multiplicity ranges in min-max separated by commas"}}; + workflowOptions.push_back(centspec); +} + +#include "Framework/runDataProcessing.h" + +namespace o2 +{ +namespace aod +{ +/* we have to change from int to bool when bool columns work properly */ +namespace dptdptcorrelations +{ +DECLARE_SOA_COLUMN(EventAccepted, eventaccepted, uint8_t); +DECLARE_SOA_COLUMN(EventCentMult, centmult, float); +DECLARE_SOA_COLUMN(TrackacceptedAsOne, trackacceptedasone, uint8_t); +DECLARE_SOA_COLUMN(TrackacceptedAsTwo, trackacceptedastwo, uint8_t); +} // namespace dptdptcorrelations +DECLARE_SOA_TABLE(AcceptedEvents, "AOD", "ACCEPTEDEVENTS", dptdptcorrelations::EventAccepted, dptdptcorrelations::EventCentMult); +DECLARE_SOA_TABLE(ScannedTracks, "AOD", "SCANNEDTRACKS", dptdptcorrelations::TrackacceptedAsOne, dptdptcorrelations::TrackacceptedAsTwo); + +using CollisionEvSelCent = soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator; +using TrackData = soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra, aod::TracksExtended, aod::TrackSelection>::iterator; +using FilteredTracks = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksExtended, aod::ScannedTracks>>; +using FilteredTrackData = Partition<aod::FilteredTracks>::filtered_iterator; +} // namespace aod +} // namespace o2 + +namespace dptdptcorrelations +{ +/* all this is made configurable */ +int ptbins = 18; +float ptlow = 0.2, ptup = 2.0; +int etabins = 16; +float etalow = -0.8, etaup = 0.8; +int zvtxbins = 40; +float zvtxlow = -10.0, zvtxup = 10.0; +int phibins = 72; +float philow = 0.0; +float phiup = TMath::TwoPi(); +float phibinshift = 0.5; +float etabinwidth = (etaup - etalow) / float(etabins); +float phibinwidth = (phiup - philow) / float(phibins); +int tracktype = 1; +int trackonecharge = 1; +int tracktwocharge = -1; +bool processpairs = false; +std::string fTaskConfigurationString = "PendingToConfigure"; + +/// \enum SystemType +/// \brief The type of the system under analysis +enum SystemType { + kNoSystem = 0, ///< no system defined + kpp, ///< **p-p** system + kpPb, ///< **p-Pb** system + kPbp, ///< **Pb-p** system + kPbPb, ///< **Pb-Pb** system + kXeXe, ///< **Xe-Xe** system + knSystems ///< number of handled systems +}; + +/// \enum CentMultEstimatorType +/// \brief The detector used to estimate centrality/multiplicity +enum CentMultEstimatorType { + kV0M = 0, ///< V0M centrality/multiplicity estimator + kV0A, ///< V0A centrality/multiplicity estimator + kV0C, ///< V0C centrality/multiplicity estimator + kCL0, ///< CL0 centrality/multiplicity estimator + kCL1, ///< CL1 centrality/multiplicity estimator + knCentMultEstimators ///< number of centrality/mutiplicity estimator +}; + +namespace filteranalyistask +{ +//============================================================================================ +// The DptDptCorrelationsFilterAnalysisTask output objects +//============================================================================================ +SystemType fSystem = kNoSystem; +CentMultEstimatorType fCentMultEstimator = kV0M; +TH1F* fhCentMultB = nullptr; +TH1F* fhCentMultA = nullptr; +TH1F* fhVertexZB = nullptr; +TH1F* fhVertexZA = nullptr; +TH1F* fhPtB = nullptr; +TH1F* fhPtA = nullptr; +TH1F* fhPtPosB = nullptr; +TH1F* fhPtPosA = nullptr; +TH1F* fhPtNegB = nullptr; +TH1F* fhPtNegA = nullptr; + +TH1F* fhEtaB = nullptr; +TH1F* fhEtaA = nullptr; + +TH1F* fhPhiB = nullptr; +TH1F* fhPhiA = nullptr; + +TH2F* fhEtaVsPhiB = nullptr; +TH2F* fhEtaVsPhiA = nullptr; + +TH2F* fhPtVsEtaB = nullptr; +TH2F* fhPtVsEtaA = nullptr; +} // namespace filteranalyistask + +namespace correlationstask +{ +//============================================================================================ +// The DptDptCorrelationsAnalysisTask output objects +//============================================================================================ +/* histograms */ +TH1F* fhN1_1_vsPt; //!<! track 1 weighted single particle distribution vs \f$p_T\f$ +TH2F* fhN1_1_vsEtaPhi; //!<! track 1 weighted single particle distribution vs \f$\eta,\;\phi\f$ +TH2F* fhSum1Pt_1_vsEtaPhi; //!<! track 1 accumulated sum of weighted \f$p_T\f$ vs \f$\eta,\;\phi\f$ +TH3F* fhN1_1_vsZEtaPhiPt; //!<! track 1 single particle distribution vs \f$\mbox{vtx}_z,\; \eta,\;\phi,\;p_T\f$ +TH1F* fhN1_2_vsPt; //!<! track 2 weighted single particle distribution vs \f$p_T\f$ +TH2F* fhN1_2_vsEtaPhi; //!<! track 2 weighted single particle distribution vs \f$\eta,\;\phi\f$ +TH2F* fhSum1Pt_2_vsEtaPhi; //!<! track 2 accumulated sum of weighted \f$p_T\f$ vs \f$\eta,\;\phi\f$ +TH3F* fhN1_2_vsZEtaPhiPt; //!<! track 2 single particle distribution vs \f$\mbox{vtx}_z,\;\eta,\;\phi,\;p_T\f$ +TH2F* fhN2_12_vsPtPt; //!<! track 1 and 2 weighted two particle distribution vs \f${p_T}_1, {p_T}_2\f$ +TH1F* fhN2_12_vsEtaPhi; //!<! track 1 and 2 weighted two particle distribution vs \f$\eta,\;\phi\f$ +TH1F* fhSum2PtPt_12_vsEtaPhi; //!<! track 1 and 2 weighted accumulated \f${p_T}_1 {p_T}_2\f$ distribution vs \f$\eta,\;\phi\f$ +TH1F* fhSum2PtN_12_vsEtaPhi; //!<! track 1 and 2 weighted accumulated \f${p_T}_1 n_2\f$ distribution vs \f$\eta,\;\phi\f$ +TH1F* fhSum2NPt_12_vsEtaPhi; //!<! track 1 and 2 weighted accumulated \f$n_1 {p_T}_2\f$ distribution vs \f$\eta,\;\phi\f$ +/* versus centrality/multiplicity profiles */ +TProfile* fhN1_1_vsC; //!<! track 1 weighted single particle distribution vs event centrality +TProfile* fhSum1Pt_1_vsC; //!<! track 1 accumulated sum of weighted \f$p_T\f$ vs event centrality +TProfile* fhN1nw_1_vsC; //!<! track 1 un-weighted single particle distribution vs event centrality +TProfile* fhSum1Ptnw_1_vsC; //!<! track 1 accumulated sum of un-weighted \f$p_T\f$ vs event centrality +TProfile* fhN1_2_vsC; //!<! track 2 weighted single particle distribution vs event centrality +TProfile* fhSum1Pt_2_vsC; //!<! track 2 accumulated sum of weighted \f$p_T\f$ vs event centrality +TProfile* fhN1nw_2_vsC; //!<! track 2 un-weighted single particle distribution vs event centrality +TProfile* fhSum1Ptnw_2_vsC; //!<! track 2 accumulated sum of un-weighted \f$p_T\f$ vs event centrality +TProfile* fhN2_12_vsC; //!<! track 1 and 2 weighted two particle distribution vs event centrality +TProfile* fhSum2PtPt_12_vsC; //!<! track 1 and 2 weighted accumulated \f${p_T}_1 {p_T}_2\f$ distribution vs event centrality +TProfile* fhSum2PtN_12_vsC; //!<! track 1 and 2 weighted accumulated \f${p_T}_1 n_2\f$ distribution vs event centrality +TProfile* fhSum2NPt_12_vsC; //!<! track 1 and 2 weighted accumulated \f$n_1 {p_T}_2\f$ distribution vs event centrality +TProfile* fhN2nw_12_vsC; //!<! track 1 and 2 un-weighted two particle distribution vs event centrality +TProfile* fhSum2PtPtnw_12_vsC; //!<! track 1 and 2 un-weighted accumulated \f${p_T}_1 {p_T}_2\f$ distribution vs event centrality +TProfile* fhSum2PtNnw_12_vsC; //!<! track 1 and 2 un-weighted accumulated \f${p_T}_1 n_2\f$ distribution vs event centrality +TProfile* fhSum2NPtnw_12_vsC; //!<! track 1 and 2 un-weighted accumulated \f$n_1 {p_T}_2\f$ distribution vs event centrality +} // namespace correlationstask + +SystemType getSystemType() +{ + /* we have to figure out how extract the system type */ + return kPbPb; +} + +bool IsEvtSelected(aod::CollisionEvSelCent const& collision, float& centormult) +{ + using namespace filteranalyistask; + + if (collision.alias()[kINT7]) { + if (collision.sel7()) { + /* TODO: vertex quality checks */ + if (zvtxlow < collision.posZ() and collision.posZ() < zvtxup) { + switch (fCentMultEstimator) { + case kV0M: + if (collision.centV0M() < 100 and 0 < collision.centV0M()) { + centormult = collision.centV0M(); + return true; + } + break; + default: + break; + } + return false; + } + return false; + } + } + return false; +} + +bool matchTrackType(aod::TrackData const& track) +{ + switch (tracktype) { + case 1: + if (track.isGlobalTrack() != 0 || track.isGlobalTrackSDD() != 0) { + return true; + } else { + return false; + } + break; + default: + return false; + } +} + +inline void AcceptTrack(aod::TrackData const& track, bool& asone, bool& astwo) +{ + + asone = false; + astwo = false; + + /* TODO: incorporate a mask in the scanned tracks table for the rejecting track reason */ + if (matchTrackType(track)) { + if (ptlow < track.pt() and track.pt() < ptup and etalow < track.eta() and track.eta() < etaup) { + if (((track.charge() > 0) and (trackonecharge > 0)) or ((track.charge() < 0) and (trackonecharge < 0))) { + asone = true; + } + if (((track.charge() > 0) and (tracktwocharge > 0)) or ((track.charge() < 0) and (tracktwocharge < 0))) { + astwo = true; + } + } + } +} + +/// \brief Returns the potentially phi origin shifted phi +/// \param phi the track azimuthal angle +/// \return the track phi origin shifted azimuthal angle +inline float GetShiftedPhi(float phi) +{ + if (not(phi < phiup)) { + return phi - TMath::TwoPi(); + } else { + return phi; + } +} + +/// \brief Returns the zero based bin index of the eta phi passed track +/// \param t the intended track +/// \return the zero based bin index +/// +/// According to the bining structure, to the track eta will correspond +/// a zero based bin index and similarlly for the track phi +/// The returned index is the composition of both considering eta as +/// the first index component +/// WARNING: for performance reasons no checks are done about the consistency +/// of track's eta and phin with the corresponding ranges so, it is suppossed +/// the track has been accepted and it is within that ranges +/// IF THAT IS NOT THE CASE THE ROUTINE WILL PRODUCE NONSENSE RESULTS +inline int GetEtaPhiIndex(aod::FilteredTrackData const& t) +{ + int etaix = int((t.eta() - etalow) / etabinwidth); + /* consider a potential phi origin shift */ + float phi = GetShiftedPhi(t.phi()); + int phiix = int((phi - philow) / phibinwidth); + return etaix * phibins + phiix; +} +} /* end namespace dptdptcorrelations */ + +// Task for <dpt,dpt> correlations analysis +// FIXME: this should really inherit from AnalysisTask but +// we need GCC 7.4+ for that + +using namespace dptdptcorrelations; + +struct DptDptCorrelationsFilterAnalysisTask { + + Configurable<int> cfgTrackType{"trktype", 1, "Type of selected tracks: 0 = no selection, 1 = global tracks FB96"}; + Configurable<int> cfgTrackOneCharge{"trk1charge", -1, "Trakc one charge: 1 = positive, -1 = negative"}; + Configurable<int> cfgTrackTwoCharge{"trk2charge", -1, "Trakc two charge: 1 = positive, -1 = negative"}; + Configurable<bool> cfgProcessPairs{"processpairs", true, "Process pairs: false = no, just singles, true = yes, process pairs"}; + Configurable<std::string> cfgCentMultEstimator{"centmultestimator", "V0M", "Centrality/multiplicity estimator detector: default V0M"}; + + Configurable<o2::analysis::DptDptBinningCuts> cfgBinning{"binning", + {28, -7.0, 7.0, 18, 0.2, 2.0, 16, -0.8, 0.8, 72, 0.5}, + "triplets - nbins, min, max - for z_vtx, pT, eta and phi, binning plus bin fraction of phi origin shift"}; + + OutputObj<TList> fOutput{"DptDptCorrelationsGlobalInfo", OutputObjHandlingPolicy::AnalysisObject}; + + Produces<aod::AcceptedEvents> acceptedevents; + Produces<aod::ScannedTracks> scannedtracks; + + void init(InitContext const&) + { + using namespace filteranalyistask; + + /* update with the configurable values */ + /* the binning */ + ptbins = cfgBinning->mPTbins; + ptlow = cfgBinning->mPTmin; + ptup = cfgBinning->mPTmax; + etabins = cfgBinning->mEtabins; + etalow = cfgBinning->mEtamin; + etaup = cfgBinning->mEtamax; + zvtxbins = cfgBinning->mZVtxbins; + zvtxlow = cfgBinning->mZVtxmin; + zvtxup = cfgBinning->mZVtxmax; + /* the track types and combinations */ + tracktype = cfgTrackType.value; + trackonecharge = cfgTrackOneCharge.value; + tracktwocharge = cfgTrackTwoCharge.value; + /* the centrality/multiplicity estimation */ + if (cfgCentMultEstimator->compare("V0M") == 0) { + fCentMultEstimator = kV0M; + } else { + LOGF(FATAL, "Centrality/Multiplicity estimator %s not supported yet", cfgCentMultEstimator->c_str()); + } + + /* if the system type is not known at this time, we have to put the initalization somewhere else */ + fSystem = getSystemType(); + + /* create the output list which will own the task histograms */ + TList* fOutputList = new TList(); + fOutputList->SetOwner(true); + fOutput.setObject(fOutputList); + + /* incorporate configuration parameters to the output */ + fOutputList->Add(new TParameter<Int_t>("TrackType", cfgTrackType, 'f')); + fOutputList->Add(new TParameter<Int_t>("TrackOneCharge", cfgTrackOneCharge, 'f')); + fOutputList->Add(new TParameter<Int_t>("TrackTwoCharge", cfgTrackTwoCharge, 'f')); + + /* create the histograms */ + if (fSystem > kPbp) { + fhCentMultB = new TH1F("CentralityB", "Centrality before cut; centrality (%)", 100, 0, 100); + fhCentMultA = new TH1F("CentralityA", "Centrality; centrality (%)", 100, 0, 100); + } else { + /* for pp, pPb and Pbp systems use multiplicity instead */ + fhCentMultB = new TH1F("MultiplicityB", "Multiplicity before cut; multiplicity (%)", 100, 0, 100); + fhCentMultA = new TH1F("MultiplicityA", "Multiplicity; multiplicity (%)", 100, 0, 100); + } + + fhVertexZB = new TH1F("VertexZB", "Vertex Z; z_{vtx}", 60, -15, 15); + fhVertexZA = new TH1F("VertexZA", "Vertex Z; z_{vtx}", zvtxbins, zvtxlow, zvtxup); + + fhPtB = new TH1F("fHistPtB", "p_{T} distribution for reconstructed before;p_{T} (GeV/c);dN/dP_{T} (c/GeV)", 100, 0.0, 15.0); + fhPtA = new TH1F("fHistPtA", "p_{T} distribution for reconstructed;p_{T} (GeV/c);dN/dP_{T} (c/GeV)", ptbins, ptlow, ptup); + fhPtPosB = new TH1F("fHistPtPosB", "P_{T} distribution for reconstructed (#{+}) before;P_{T} (GeV/c);dN/dP_{T} (c/GeV)", 100, 0.0, 15.0); + fhPtPosA = new TH1F("fHistPtPosA", "P_{T} distribution for reconstructed (#{+});P_{T} (GeV/c);dN/dP_{T} (c/GeV)", ptbins, ptlow, ptup); + fhPtNegB = new TH1F("fHistPtNegB", "P_{T} distribution for reconstructed (#{-}) before;P_{T} (GeV/c);dN/dP_{T} (c/GeV)", 100, 0.0, 15.0); + fhPtNegA = new TH1F("fHistPtNegA", "P_{T} distribution for reconstructed (#{-});P_{T} (GeV/c);dN/dP_{T} (c/GeV)", ptbins, ptlow, ptup); + fhEtaB = new TH1F("fHistEtaB", "#eta distribution for reconstructed before;#eta;counts", 40, -2.0, 2.0); + fhEtaA = new TH1F("fHistEtaA", "#eta distribution for reconstructed;#eta;counts", etabins, etalow, etaup); + fhPhiB = new TH1F("fHistPhiB", "#phi distribution for reconstructed before;#phi;counts", 360, 0.0, 2 * M_PI); + fhPhiA = new TH1F("fHistPhiA", "#phi distribution for reconstructed;#phi;counts", 360, 0.0, 2 * M_PI); + fhEtaVsPhiB = new TH2F(TString::Format("CSTaskEtaVsPhiB_%s", fTaskConfigurationString.c_str()), "#eta vs #phi before;#phi;#eta", 360, 0.0, 2 * M_PI, 100, -2.0, 2.0); + fhEtaVsPhiA = new TH2F(TString::Format("CSTaskEtaVsPhiA_%s", fTaskConfigurationString.c_str()), "#eta vs #phi;#phi;#eta", 360, 0.0, 2 * M_PI, etabins, etalow, etaup); + fhPtVsEtaB = new TH2F(TString::Format("fhPtVsEtaB_%s", fTaskConfigurationString.c_str()), "p_{T} vs #eta before;#eta;p_{T} (GeV/c)", etabins, etalow, etaup, 100, 0.0, 15.0); + fhPtVsEtaA = new TH2F(TString::Format("fhPtVsEtaA_%s", fTaskConfigurationString.c_str()), "p_{T} vs #eta;#eta;p_{T} (GeV/c)", etabins, etalow, etaup, ptbins, ptlow, ptup); + + /* add the hstograms to the output list */ + fOutputList->Add(fhCentMultB); + fOutputList->Add(fhCentMultA); + fOutputList->Add(fhVertexZB); + fOutputList->Add(fhVertexZA); + fOutputList->Add(fhPtB); + fOutputList->Add(fhPtA); + fOutputList->Add(fhPtPosB); + fOutputList->Add(fhPtPosA); + fOutputList->Add(fhPtNegB); + fOutputList->Add(fhPtNegA); + fOutputList->Add(fhEtaB); + fOutputList->Add(fhEtaA); + fOutputList->Add(fhPhiB); + fOutputList->Add(fhPhiA); + fOutputList->Add(fhEtaVsPhiB); + fOutputList->Add(fhEtaVsPhiA); + fOutputList->Add(fhPtVsEtaB); + fOutputList->Add(fhPtVsEtaA); + } + + void process(aod::CollisionEvSelCent const& collision, soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra, aod::TracksExtended, aod::TrackSelection> const& ftracks) + { + using namespace filteranalyistask; + + // LOGF(INFO,"New collision with %d filtered tracks", ftracks.size()); + fhCentMultB->Fill(collision.centV0M()); + fhVertexZB->Fill(collision.posZ()); + bool acceptedevent = false; + float centormult = -100.0; + if (IsEvtSelected(collision, centormult)) { + acceptedevent = true; + fhCentMultA->Fill(collision.centV0M()); + fhVertexZA->Fill(collision.posZ()); + // LOGF(INFO,"New accepted collision with %d filtered tracks", ftracks.size()); + + for (auto& track : ftracks) { + /* before track selection */ + fhPtB->Fill(track.pt()); + fhEtaB->Fill(track.eta()); + fhPhiB->Fill(track.phi()); + fhEtaVsPhiB->Fill(track.phi(), track.eta()); + fhPtVsEtaB->Fill(track.eta(), track.pt()); + if (track.charge() > 0) { + fhPtPosB->Fill(track.pt()); + } else { + fhPtNegB->Fill(track.pt()); + } + + /* track selection */ + /* tricky because the boolean columns issue */ + bool asone, astwo; + AcceptTrack(track, asone, astwo); + if (asone or astwo) { + /* the track has been accepted */ + fhPtA->Fill(track.pt()); + fhEtaA->Fill(track.eta()); + fhPhiA->Fill(track.phi()); + fhEtaVsPhiA->Fill(track.phi(), track.eta()); + fhPtVsEtaA->Fill(track.eta(), track.pt()); + if (track.charge() > 0) { + fhPtPosA->Fill(track.pt()); + } else { + fhPtNegA->Fill(track.pt()); + } + } + scannedtracks((uint8_t)asone, (uint8_t)astwo); + } + } else { + for (auto& track : ftracks) { + scannedtracks((uint8_t) false, (uint8_t) false); + } + } + acceptedevents((uint8_t)acceptedevent, centormult); + } +}; + +// Task for building <dpt,dpt> correlations +struct DptDptCorrelationsTask { + /* the data memebers for this task */ + /* the centrality / multiplicity limits for collecting data in this task instance */ + float fCentMultMin; + float fCentMultMax; + + Configurable<bool> cfgProcessPairs{"processpairs", false, "Process pairs: false = no, just singles, true = yes, process pairs"}; + + Configurable<o2::analysis::DptDptBinningCuts> cfgBinning{"binning", + {28, -7.0, 7.0, 18, 0.2, 2.0, 16, -0.8, 0.8, 72, 0.5}, + "triplets - nbins, min, max - for z_vtx, pT, eta and phi, binning plus bin fraction of phi origin shift"}; + + OutputObj<TList> fOutput{"DptDptCorrelationsData", OutputObjHandlingPolicy::AnalysisObject}; + + Filter onlyacceptedevents = (aod::dptdptcorrelations::eventaccepted == (uint8_t) true); + Filter onlyacceptedtracks = ((aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true) or (aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true)); + + Partition<aod::FilteredTracks> Tracks1 = aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true; + Partition<aod::FilteredTracks> Tracks2 = aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true; + + DptDptCorrelationsTask(float cmmin, + float cmmax, + Configurable<bool> _cfgProcessPairs = {"processpairs", false, "Process pairs: false = no, just singles, true = yes, process pairs"}, + Configurable<o2::analysis::DptDptBinningCuts> _cfgBinning = {"binning", + {28, -7.0, 7.0, 18, 0.2, 2.0, 16, -0.8, 0.8, 72, 0.5}, + "triplets - nbins, min, max - for z_vtx, pT, eta and phi, binning plus bin fraction of phi origin shift"}, + OutputObj<TList> _fOutput = {"DptDptCorrelationsData", OutputObjHandlingPolicy::AnalysisObject}, + Filter _onlyacceptedevents = (aod::dptdptcorrelations::eventaccepted == (uint8_t) true), + Filter _onlyacceptedtracks = ((aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true) or (aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true)), + Partition<aod::FilteredTracks> _Tracks1 = aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true, + Partition<aod::FilteredTracks> _Tracks2 = aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true) + : fCentMultMin(cmmin), + fCentMultMax(cmmax), + cfgProcessPairs(_cfgProcessPairs), + cfgBinning(_cfgBinning), + fOutput(_fOutput), + onlyacceptedevents((aod::dptdptcorrelations::eventaccepted == (uint8_t) true) and (aod::dptdptcorrelations::centmult > fCentMultMin) and (aod::dptdptcorrelations::centmult < fCentMultMax)), + onlyacceptedtracks((aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true) or (aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true)), + Tracks1(aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true), + Tracks2(aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true) + { + } + + void init(InitContext const&) + { + using namespace correlationstask; + + /* update with the configurable values */ + ptbins = cfgBinning->mPTbins; + ptlow = cfgBinning->mPTmin; + ptup = cfgBinning->mPTmax; + etabins = cfgBinning->mEtabins; + etalow = cfgBinning->mEtamin; + etaup = cfgBinning->mEtamax; + zvtxbins = cfgBinning->mZVtxbins; + zvtxlow = cfgBinning->mZVtxmin; + zvtxup = cfgBinning->mZVtxmax; + phibins = cfgBinning->mPhibins; + philow = 0.0f; + phiup = TMath::TwoPi(); + phibinshift = cfgBinning->mPhibinshift; + processpairs = cfgProcessPairs.value; + /* update the potential binning change */ + etabinwidth = (etaup - etalow) / float(etabins); + phibinwidth = (phiup - philow) / float(phibins); + + /* create the output list which will own the task output */ + TList* fOutputList = new TList(); + fOutputList->SetOwner(true); + fOutput.setObject(fOutputList); + + /* incorporate configuration parameters to the output */ + fOutputList->Add(new TParameter<Int_t>("NoBinsVertexZ", zvtxbins, 'f')); + fOutputList->Add(new TParameter<Int_t>("NoBinsPt", ptbins, 'f')); + fOutputList->Add(new TParameter<Int_t>("NoBinsEta", etabins, 'f')); + fOutputList->Add(new TParameter<Int_t>("NoBinsPhi", phibins, 'f')); + fOutputList->Add(new TParameter<Double_t>("MinVertexZ", zvtxlow, 'f')); + fOutputList->Add(new TParameter<Double_t>("MaxVertexZ", zvtxup, 'f')); + fOutputList->Add(new TParameter<Double_t>("MinPt", ptlow, 'f')); + fOutputList->Add(new TParameter<Double_t>("MaxPt", ptup, 'f')); + fOutputList->Add(new TParameter<Double_t>("MinEta", etalow, 'f')); + fOutputList->Add(new TParameter<Double_t>("MaxEta", etaup, 'f')); + fOutputList->Add(new TParameter<Double_t>("MinPhi", philow, 'f')); + fOutputList->Add(new TParameter<Double_t>("MaxPhi", phiup, 'f')); + fOutputList->Add(new TParameter<Double_t>("PhiBinShift", phibinshift, 'f')); + + /* after the parameters dump the proper phi limits are set according to the phi shift */ + phiup = phiup - phibinwidth * phibinshift; + philow = philow - phibinwidth * phibinshift; + + /* create the histograms */ + Bool_t oldstatus = TH1::AddDirectoryStatus(); + TH1::AddDirectory(kFALSE); + + if (!processpairs) { + fhN1_1_vsPt = new TH1F("n1_1_vsPt", "#LT n_{1} #GT;p_{t,1} (GeV/c);#LT n_{1} #GT", ptbins, ptlow, ptup); + fhN1_2_vsPt = new TH1F("n1_2_vsPt", "#LT n_{1} #GT;p_{t,2} (GeV/c);#LT n_{1} #GT", ptbins, ptlow, ptup); + /* we don't want the Sumw2 structure being created here */ + bool defSumw2 = TH1::GetDefaultSumw2(); + TH1::SetDefaultSumw2(false); + fhN1_1_vsZEtaPhiPt = new TH3F("n1_1_vsZ_vsEtaPhi_vsPt", "#LT n_{1} #GT;vtx_{z};#eta_{1}#times#varphi_{1};p_{t,1} (GeV/c)", + zvtxbins, zvtxlow, zvtxup, etabins * phibins, 0.0, double(etabins * phibins), ptbins, ptlow, ptup); + + fhN1_2_vsZEtaPhiPt = new TH3F("n1_2_vsZ_vsEtaPhi_vsPt", "#LT n_{2} #GT;vtx_{z};#eta_{2}#times#varphi_{2};p_{t,2} (GeV/c)", + zvtxbins, zvtxlow, zvtxup, etabins * phibins, 0.0, double(etabins * phibins), ptbins, ptlow, ptup); + /* we return it back to previuos state */ + TH1::SetDefaultSumw2(defSumw2); + + /* the statistical uncertainties will be estimated by the subsamples method so let's get rid of the error tracking */ + fhN1_1_vsZEtaPhiPt->SetBit(TH1::kIsNotW); + fhN1_1_vsZEtaPhiPt->Sumw2(false); + fhN1_2_vsZEtaPhiPt->SetBit(TH1::kIsNotW); + fhN1_2_vsZEtaPhiPt->Sumw2(false); + + fOutputList->Add(fhN1_1_vsPt); + fOutputList->Add(fhN1_1_vsZEtaPhiPt); + fOutputList->Add(fhN1_2_vsPt); + fOutputList->Add(fhN1_2_vsZEtaPhiPt); + } else { + fhN1_1_vsEtaPhi = new TH2F("n1_1_vsEtaPhi", "#LT n_{1} #GT;#eta_{1};#varphi_{1} (radian);#LT n_{1} #GT", + etabins, etalow, etaup, phibins, philow, phiup); + fhSum1Pt_1_vsEtaPhi = new TH2F("sumPt_1_vsEtaPhi", "#LT #Sigma p_{t,1} #GT;#eta_{1};#varphi_{1} (radian);#LT #Sigma p_{t,1} #GT (GeV/c)", + etabins, etalow, etaup, phibins, philow, phiup); + fhN1_1_vsC = new TProfile("n1_1_vsM", "#LT n_{1} #GT (weighted);Centrality (%);#LT n_{1} #GT", 100, 0.0, 100.0); + fhSum1Pt_1_vsC = new TProfile("sumPt_1_vsM", "#LT #Sigma p_{t,1} #GT (weighted);Centrality (%);#LT #Sigma p_{t,1} #GT (GeV/c)", 100, 0.0, 100.0); + fhN1nw_1_vsC = new TProfile("n1Nw_1_vsM", "#LT n_{1} #GT;Centrality (%);#LT n_{1} #GT", 100, 0.0, 100.0); + fhSum1Ptnw_1_vsC = new TProfile("sumPtNw_1_vsM", "#LT #Sigma p_{t,1} #GT;Centrality (%);#LT #Sigma p_{t,1} #GT (GeV/c)", 100, 0.0, 100.0); + + fhN1_2_vsEtaPhi = new TH2F("n1_2_vsEtaPhi", "#LT n_{1} #GT;#eta_{2};#varphi_{2} (radian);#LT n_{1} #GT", + etabins, etalow, etaup, phibins, philow, phiup); + fhSum1Pt_2_vsEtaPhi = new TH2F("sumPt_2_vsEtaPhi", "#LT #Sigma p_{t,2} #GT;#eta_{2};#varphi_{2} (radian);#LT #Sigma p_{t,2} #GT (GeV/c)", + etabins, etalow, etaup, phibins, philow, phiup); + fhN1_2_vsC = new TProfile("n1_2_vsM", "#LT n_{1} #GT (weighted);Centrality (%);#LT n_{1} #GT", 100, 0.0, 100.0); + fhSum1Pt_2_vsC = new TProfile("sumPt_2_vsM", "#LT #Sigma p_{t,1} #GT (weighted);Centrality (%);#LT #Sigma p_{t,1} #GT (GeV/c)", 100, 0.0, 100.0); + fhN1nw_2_vsC = new TProfile("n1Nw_2_vsM", "#LT n_{1} #GT;Centrality (%);#LT n_{1} #GT", 100, 0.0, 100.0); + fhSum1Ptnw_2_vsC = new TProfile("sumPtNw_2_vsM", "#LT #Sigma p_{t,1} #GT;Centrality (%);#LT #Sigma p_{t,1} #GT (GeV/c)", 100, 0.0, 100.0); + + /* we don't want the Sumw2 structure being created here */ + bool defSumw2 = TH1::GetDefaultSumw2(); + TH1::SetDefaultSumw2(false); + fhN2_12_vsEtaPhi = new TH1F("n2_12_vsEtaPhi", "#LT n_{2} #GT;#eta_{1}#times#varphi_{1}#times#eta_{2}#times#varphi_{2};#LT n_{2} #GT", + etabins * phibins * etabins * phibins, 0., double(etabins * phibins * etabins * phibins)); + fhSum2PtPt_12_vsEtaPhi = new TH1F("sumPtPt_12_vsEtaPhi", "#LT #Sigma p_{t,1}p_{t,2} #GT;#eta_{1}#times#varphi_{1}#times#eta_{2}#times#varphi_{2};#LT #Sigma p_{t,1}p_{t,2} #GT (GeV)^{2}", + etabins * phibins * etabins * phibins, 0., double(etabins * phibins * etabins * phibins)); + fhSum2PtN_12_vsEtaPhi = new TH1F("sumPtN_12_vsEtaPhi", "#LT #Sigma p_{t,1}N #GT;#eta_{1}#times#varphi_{1}#times#eta_{2}#times#varphi_{2};#LT #Sigma p_{t,1}N #GT (GeV)", + etabins * phibins * etabins * phibins, 0., double(etabins * phibins * etabins * phibins)); + fhSum2NPt_12_vsEtaPhi = new TH1F("sumNPt_12_vsEtaPhi", "#LT N#Sigma p_{t,2} #GT;#eta_{1}#times#varphi_{1}#times#eta_{2}#times#varphi_{2};#LT N#Sigma p_{t,2} #GT (GeV)", + etabins * phibins * etabins * phibins, 0., double(etabins * phibins * etabins * phibins)); + /* we return it back to previuos state */ + TH1::SetDefaultSumw2(defSumw2); + + fhN2_12_vsPtPt = new TH2F("n2_12_vsPtVsPt", "#LT n_{2} #GT;p_{t,1} (GeV/c);p_{t,2} (GeV/c);#LT n_{2} #GT", + ptbins, ptlow, ptup, ptbins, ptlow, ptup); + fhN2_12_vsC = new TProfile("n2_12_vsM", "#LT n_{2} #GT (weighted);Centrality (%);#LT n_{2} #GT", 100, 0.0, 100.0); + fhSum2PtPt_12_vsC = new TProfile("sumPtPt_12_vsM", "#LT #Sigma p_{t,1}p_{t,2} #GT (weighted);Centrality (%);#LT #Sigma p_{t,1}p_{t,2} #GT (GeV)^{2}", 100, 0.0, 100.0); + fhSum2PtN_12_vsC = new TProfile("sumPtN_12_vsM", "#LT #Sigma p_{t,1}N #GT (weighted);Centrality (%);#LT #Sigma p_{t,1}N #GT (GeV)", 100, 0.0, 100.0); + fhSum2NPt_12_vsC = new TProfile("sumNPt_12_vsM", "#LT N#Sigma p_{t,2} #GT (weighted);Centrality (%);#LT N#Sigma p_{t,2} #GT (GeV)", 100, 0.0, 100.0); + fhN2nw_12_vsC = new TProfile("n2Nw_12_vsM", "#LT n_{2} #GT;Centrality (%);#LT n_{2} #GT", 100, 0.0, 100.0); + fhSum2PtPtnw_12_vsC = new TProfile("sumPtPtNw_12_vsM", "#LT #Sigma p_{t,1}p_{t,2} #GT;Centrality (%);#LT #Sigma p_{t,1}p_{t,2} #GT (GeV)^{2}", 100, 0.0, 100.0); + fhSum2PtNnw_12_vsC = new TProfile("sumPtNNw_12_vsM", "#LT #Sigma p_{t,1}N #GT;Centrality (%);#LT #Sigma p_{t,1}N #GT (GeV)", 100, 0.0, 100.0); + fhSum2NPtnw_12_vsC = new TProfile("sumNPtNw_12_vsM", "#LT N#Sigma p_{t,2} #GT;Centrality (%);#LT N#Sigma p_{t,2} #GT (GeV)", 100, 0.0, 100.0); + + /* the statistical uncertainties will be estimated by the subsamples method so let's get rid of the error tracking */ + fhN2_12_vsEtaPhi->SetBit(TH1::kIsNotW); + fhN2_12_vsEtaPhi->Sumw2(false); + fhSum2PtPt_12_vsEtaPhi->SetBit(TH1::kIsNotW); + fhSum2PtPt_12_vsEtaPhi->Sumw2(false); + fhSum2PtN_12_vsEtaPhi->SetBit(TH1::kIsNotW); + fhSum2PtN_12_vsEtaPhi->Sumw2(false); + fhSum2NPt_12_vsEtaPhi->SetBit(TH1::kIsNotW); + fhSum2NPt_12_vsEtaPhi->Sumw2(false); + + fOutputList->Add(fhN1_1_vsEtaPhi); + fOutputList->Add(fhSum1Pt_1_vsEtaPhi); + fOutputList->Add(fhN1_1_vsC); + fOutputList->Add(fhSum1Pt_1_vsC); + fOutputList->Add(fhN1nw_1_vsC); + fOutputList->Add(fhSum1Ptnw_1_vsC); + fOutputList->Add(fhN1_2_vsEtaPhi); + fOutputList->Add(fhSum1Pt_2_vsEtaPhi); + fOutputList->Add(fhN1_2_vsC); + fOutputList->Add(fhSum1Pt_2_vsC); + fOutputList->Add(fhN1nw_2_vsC); + fOutputList->Add(fhSum1Ptnw_2_vsC); + + fOutputList->Add(fhN2_12_vsEtaPhi); + fOutputList->Add(fhSum2PtPt_12_vsEtaPhi); + fOutputList->Add(fhSum2PtN_12_vsEtaPhi); + fOutputList->Add(fhSum2NPt_12_vsEtaPhi); + fOutputList->Add(fhN2_12_vsPtPt); + fOutputList->Add(fhN2_12_vsC); + fOutputList->Add(fhSum2PtPt_12_vsC); + fOutputList->Add(fhSum2PtN_12_vsC); + fOutputList->Add(fhSum2NPt_12_vsC); + fOutputList->Add(fhN2nw_12_vsC); + fOutputList->Add(fhSum2PtPtnw_12_vsC); + fOutputList->Add(fhSum2PtNnw_12_vsC); + fOutputList->Add(fhSum2NPtnw_12_vsC); + } + + TH1::AddDirectory(oldstatus); + } + + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels, aod::Cents, aod::AcceptedEvents>>::iterator const& collision, + aod::FilteredTracks const& tracks) + { + using namespace correlationstask; + + if (not processpairs) { + /* process single tracks */ + for (auto& track1 : Tracks1) { + double corr = 1.0; /* TODO: track correction weights */ + fhN1_1_vsPt->Fill(track1.pt(), corr); + fhN1_1_vsZEtaPhiPt->Fill(collision.posZ(), GetEtaPhiIndex(track1) + 0.5, track1.pt(), corr); + } + for (auto& track2 : Tracks2) { + double corr = 1.0; /* TODO: track correction weights */ + fhN1_2_vsPt->Fill(track2.pt(), corr); + fhN1_2_vsZEtaPhiPt->Fill(collision.posZ(), GetEtaPhiIndex(track2) + 0.5, track2.pt(), corr); + } + } else { + { + /* process track one magnitudes */ + double n1_1 = 0; ///< weighted number of track 1 tracks for current collision + double sum1Pt_1 = 0; ///< accumulated sum of weighted track 1 \f$p_T\f$ for current collision + double n1nw_1 = 0; ///< not weighted number of track 1 tracks for current collision + double sum1Ptnw_1 = 0; ///< accumulated sum of not weighted track 1 \f$p_T\f$ for current collision + for (auto& track1 : Tracks1) { + double corr = 1.0; /* TODO: track correction weights */ + n1_1 += corr; + sum1Pt_1 += track1.pt() * corr; + n1nw_1 += 1; + sum1Ptnw_1 += track1.pt(); + + fhN1_1_vsEtaPhi->Fill(track1.eta(), GetShiftedPhi(track1.phi()), corr); + fhSum1Pt_1_vsEtaPhi->Fill(track1.eta(), GetShiftedPhi(track1.phi()), track1.pt() * corr); + } + /* TODO: the centrality should be chosen non detector dependent */ + fhN1_1_vsC->Fill(collision.centmult(), n1_1); + fhSum1Pt_1_vsC->Fill(collision.centmult(), sum1Pt_1); + fhN1nw_1_vsC->Fill(collision.centmult(), n1nw_1); + fhSum1Ptnw_1_vsC->Fill(collision.centmult(), sum1Ptnw_1); + } + { + /* process track two magnitudes */ + double n1_2 = 0; ///< weighted number of track 2 tracks for current collisiont + double sum1Pt_2 = 0; ///< accumulated sum of weighted track 2 \f$p_T\f$ for current collision + double n1nw_2 = 0; ///< not weighted number of track 2 tracks for current collision + double sum1Ptnw_2 = 0; ///< accumulated sum of not weighted track 2 \f$p_T\f$ for current collision + for (auto& track2 : Tracks2) { + double corr = 1.0; /* TODO: track correction weights */ + n1_2 += corr; + sum1Pt_2 += track2.pt() * corr; + n1nw_2 += 1; + sum1Ptnw_2 += track2.pt(); + + fhN1_2_vsEtaPhi->Fill(track2.eta(), GetShiftedPhi(track2.phi()), corr); + fhSum1Pt_2_vsEtaPhi->Fill(track2.eta(), GetShiftedPhi(track2.phi()), track2.pt() * corr); + } + /* TODO: the centrality should be chosen non detector dependent */ + fhN1_2_vsC->Fill(collision.centmult(), n1_2); + fhSum1Pt_2_vsC->Fill(collision.centmult(), sum1Pt_2); + fhN1nw_2_vsC->Fill(collision.centmult(), n1nw_2); + fhSum1Ptnw_2_vsC->Fill(collision.centmult(), sum1Ptnw_2); + } + /* process pairs of tracks */ + // this will be desireable + // for (auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(Tracks1, Tracks2))) { + /* process pair magnitudes */ + double n2_12 = 0; ///< weighted number of track 1 track 2 pairs for current collision + double sum2PtPt_12 = 0; ///< accumulated sum of weighted track 1 track 2 \f${p_T}_1 {p_T}_2\f$ for current collision + double sum2NPt_12 = 0; ///< accumulated sum of weighted number of track 1 tracks times weighted track 2 \f$p_T\f$ for current collision + double sum2PtN_12 = 0; ///< accumulated sum of weighted track 1 \f$p_T\f$ times weighted number of track 2 tracks for current collision + double n2nw_12 = 0; ///< not weighted number of track1 track 2 pairs for current collision + double sum2PtPtnw_12 = 0; ///< accumulated sum of not weighted track 1 track 2 \f${p_T}_1 {p_T}_2\f$ for current collision + double sum2NPtnw_12 = 0; ///< accumulated sum of not weighted number of track 1 tracks times not weighted track 2 \f$p_T\f$ for current collision + double sum2PtNnw_12 = 0; ///< accumulated sum of not weighted track 1 \f$p_T\f$ times not weighted number of track tracks for current collision + for (auto& track1 : Tracks1) { + for (auto& track2 : Tracks2) { + /* checkiing the same track id condition */ + if (track1.index() == track2.index()) { + /* exclude autocorrelations */ + } else { + /* process pair magnitudes */ + double corr1 = 1.0; /* TODO: track correction weights */ + double corr2 = 1.0; /* TODO: track correction weights */ + double corr = corr1 * corr2; + n2_12 += corr; + sum2PtPt_12 += track1.pt() * track2.pt() * corr; + sum2NPt_12 += corr * track2.pt(); + sum2PtN_12 += track1.pt() * corr; + n2nw_12 += 1; + sum2PtPtnw_12 += track1.pt() * track2.pt(); + sum2NPtnw_12 += track2.pt(); + sum2PtNnw_12 += track1.pt(); + /* we already know the bin in the flattened histograms, let's use it to update them */ + fhN2_12_vsEtaPhi->AddBinContent(GetEtaPhiIndex(track1) * etabins * phibins + GetEtaPhiIndex(track2) + 1, corr); + fhSum2NPt_12_vsEtaPhi->AddBinContent(GetEtaPhiIndex(track1) * etabins * phibins + GetEtaPhiIndex(track2) + 1, corr * track2.pt()); + fhSum2PtN_12_vsEtaPhi->AddBinContent(GetEtaPhiIndex(track1) * etabins * phibins + GetEtaPhiIndex(track2) + 1, track1.pt() * corr); + fhSum2PtPt_12_vsEtaPhi->AddBinContent(GetEtaPhiIndex(track1) * etabins * phibins + GetEtaPhiIndex(track2) + 1, track1.pt() * track2.pt() * corr); + fhN2_12_vsPtPt->Fill(track1.pt(), track2.pt(), corr); + } + } + } + fhN2_12_vsC->Fill(collision.centmult(), n2_12); + fhSum2PtPt_12_vsC->Fill(collision.centmult(), sum2PtPt_12); + fhSum2PtN_12_vsC->Fill(collision.centmult(), sum2PtN_12); + fhSum2NPt_12_vsC->Fill(collision.centmult(), sum2NPt_12); + fhN2nw_12_vsC->Fill(collision.centmult(), n2nw_12); + fhSum2PtPtnw_12_vsC->Fill(collision.centmult(), sum2PtPtnw_12); + fhSum2PtNnw_12_vsC->Fill(collision.centmult(), sum2PtNnw_12); + fhSum2NPtnw_12_vsC->Fill(collision.centmult(), sum2NPtnw_12); + /* let's also update the number of entries in the flattened histograms */ + fhN2_12_vsEtaPhi->SetEntries(fhN2_12_vsEtaPhi->GetEntries() + n2nw_12); + fhSum2NPt_12_vsEtaPhi->SetEntries(fhSum2NPt_12_vsEtaPhi->GetEntries() + n2nw_12); + fhSum2PtN_12_vsEtaPhi->SetEntries(fhSum2PtN_12_vsEtaPhi->GetEntries() + n2nw_12); + fhSum2PtPt_12_vsEtaPhi->SetEntries(fhSum2PtPt_12_vsEtaPhi->GetEntries() + n2nw_12); + } + } +}; + +// Task for building <dpt,dpt> correlations +struct TracksAndEventClassificationQA { + + Configurable<o2::analysis::SimpleInclusiveCut> cfg{"mycfg", {"mycfg", 3, 2.0f}, "A Configurable Object, default mycfg.x=3, mycfg.y=2.0"}; + + OutputObj<TH1F> fTracksOne{TH1F("TracksOne", "Tracks as track one;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksTwo{TH1F("TracksTwo", "Tracks as track two;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksOneAndTwo{TH1F("TracksOneAndTwo", "Tracks as track one and as track two;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksNone{TH1F("TracksNone", "Not selected tracks;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksOneUnsel{TH1F("TracksOneUnsel", "Tracks as track one;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksTwoUnsel{TH1F("TracksTwoUnsel", "Tracks as track two;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksOneAndTwoUnsel{TH1F("TracksOneAndTwoUnsel", "Tracks as track one and as track two;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fTracksNoneUnsel{TH1F("TracksNoneUnsel", "Not selected tracks;number of tracks;events", 1500, 0.0, 1500.0)}; + OutputObj<TH1F> fSelectedEvents{TH1F("SelectedEvents", "Selected events;;events", 2, 0.0, 2.0)}; + + void init(InitContext const&) + { + fSelectedEvents->GetXaxis()->SetBinLabel(1, "Not selected events"); + fSelectedEvents->GetXaxis()->SetBinLabel(2, "Selected events"); + } + + Filter onlyacceptedevents = (aod::dptdptcorrelations::eventaccepted == (uint8_t) true); + Filter onlyacceptedtracks = ((aod::dptdptcorrelations::trackacceptedasone == (uint8_t) true) or (aod::dptdptcorrelations::trackacceptedastwo == (uint8_t) true)); + + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels, aod::Cents, aod::AcceptedEvents>>::iterator const& collision, + soa::Filtered<soa::Join<aod::Tracks, aod::ScannedTracks>> const& tracks) + { + if (collision.eventaccepted() != (uint8_t) true) { + fSelectedEvents->Fill(0.5); + } else { + fSelectedEvents->Fill(1.5); + } + + int ntracks_one = 0; + int ntracks_two = 0; + int ntracks_one_and_two = 0; + int ntracks_none = 0; + for (auto& track : tracks) { + if ((track.trackacceptedasone() != (uint8_t) true) and (track.trackacceptedastwo() != (uint8_t) true)) { + ntracks_none++; + } + if ((track.trackacceptedasone() == (uint8_t) true) and (track.trackacceptedastwo() == (uint8_t) true)) { + ntracks_one_and_two++; + } + if (track.trackacceptedasone() == (uint8_t) true) { + ntracks_one++; + } + if (track.trackacceptedastwo() == (uint8_t) true) { + ntracks_two++; + } + } + if (collision.eventaccepted() != (uint8_t) true) { + /* control for non selected events */ + fTracksOneUnsel->Fill(ntracks_one); + fTracksTwoUnsel->Fill(ntracks_two); + fTracksNoneUnsel->Fill(ntracks_none); + fTracksOneAndTwoUnsel->Fill(ntracks_one_and_two); + } else { + fTracksOne->Fill(ntracks_one); + fTracksTwo->Fill(ntracks_two); + fTracksNone->Fill(ntracks_none); + fTracksOneAndTwo->Fill(ntracks_one_and_two); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + std::string myoptcentralities = cfgc.options().get<std::string>("centralities"); + TObjArray* tokens = TString(myoptcentralities.c_str()).Tokenize(","); + int nranges = tokens->GetEntries(); + + WorkflowSpec workflow{ + adaptAnalysisTask<DptDptCorrelationsFilterAnalysisTask>("DptDptCorrelationsFilterAnalysisTask"), + adaptAnalysisTask<TracksAndEventClassificationQA>("TracksAndEventClassificationQA")}; + for (int i = 0; i < nranges; ++i) { + float cmmin = 0.0f; + float cmmax = 0.0f; + sscanf(tokens->At(i)->GetName(), "%f-%f", &cmmin, &cmmax); + workflow.push_back(adaptAnalysisTask<DptDptCorrelationsTask>(Form("DptDptCorrelationsTask-%s", tokens->At(i)->GetName()), cmmin, cmmax)); + } + delete tokens; + return workflow; +} diff --git a/Analysis/Tasks/PWGCF/filterCF.cxx b/Analysis/Tasks/PWGCF/filterCF.cxx new file mode 100644 index 0000000000000..7c689f2aca3cc --- /dev/null +++ b/Analysis/Tasks/PWGCF/filterCF.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" + +#include "AnalysisDataModel/CFDerived.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/Centrality.h" + +#include <TH3F.h> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable<TYPE> NAME{#NAME, DEFAULT, HELP}; + +struct FilterCF { + + // Configuration + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 7.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + + // Filters and input definitions + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex && aod::cent::centV0M <= 80.0f; + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPt) && ((aod::track::isGlobalTrack == (uint8_t) true) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); + + OutputObj<TH3F> yields{TH3F("yields", "centrality vs pT vs eta", 100, 0, 100, 40, 0, 20, 100, -2, 2)}; + OutputObj<TH3F> etaphi{TH3F("etaphi", "centrality vs eta vs phi", 100, 0, 100, 100, -2, 2, 200, 0, 2 * M_PI)}; + + Produces<aod::CFCollisions> outputCollisions; + Produces<aod::CFTracks> outputTracks; + + void init(o2::framework::InitContext&) + { + } + + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels, aod::Cents>>::iterator const& collision, aod::BCs const&, soa::Filtered<soa::Join<aod::Tracks, aod::TrackSelection>> const& tracks) + { + LOGF(info, "Tracks for collision: %d | Vertex: %.1f | INT7: %d | V0M: %.1f", tracks.size(), collision.posZ(), collision.sel7(), collision.centV0M()); + + if (!collision.sel7()) { + return; + } + + outputCollisions(collision.bc().runNumber(), collision.posZ(), collision.centV0M()); + + for (auto& track : tracks) { + uint8_t trackType = 0; + if (track.isGlobalTrack()) { + trackType = 1; + } else if (track.isGlobalTrackSDD()) { + trackType = 2; + } + + outputTracks(outputCollisions.lastIndex(), track.pt(), track.eta(), track.phi(), track.charge(), trackType); + + yields->Fill(collision.centV0M(), track.pt(), track.eta()); + etaphi->Fill(collision.centV0M(), track.eta(), track.phi()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<FilterCF>("filter-cf")}; +} diff --git a/Analysis/Tasks/PWGDQ/CMakeLists.txt b/Analysis/Tasks/PWGDQ/CMakeLists.txt new file mode 100644 index 0000000000000..e5de4e409c570 --- /dev/null +++ b/Analysis/Tasks/PWGDQ/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(table-maker + SOURCES tableMaker.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(table-reader + SOURCES tableReader.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(dilepton-ee + SOURCES dileptonEE.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(dilepton-mumu + SOURCES dileptonMuMu.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(table-maker-pp + SOURCES tableMaker_pp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(dq-filter-pp + SOURCES filterPP.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(table-maker-muon-pp + SOURCES tableMakerMuon_pp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::DetectorsBase O2::AnalysisCore O2::PWGDQCore + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/PWGDQ/dileptonEE.cxx b/Analysis/Tasks/PWGDQ/dileptonEE.cxx new file mode 100644 index 0000000000000..d0784a184af11 --- /dev/null +++ b/Analysis/Tasks/PWGDQ/dileptonEE.cxx @@ -0,0 +1,369 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include <TH1F.h> +#include <TMath.h> +#include <THashList.h> +#include <TString.h> +#include <iostream> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +//using namespace o2::framework::expressions; +using namespace o2::aod; + +// Some definitions +namespace o2::aod +{ +namespace reducedtrack +{ +DECLARE_SOA_COLUMN(IsBarrelSelected, isBarrelSelected, int); +} // namespace reducedtrack +namespace reducedpair +{ +DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Charge, charge, int); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return pt * std::cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return pt * std::sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return pt * std::sinh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pmom, pmom, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); +} // namespace reducedpair + +DECLARE_SOA_TABLE(BarrelTrackCuts, "AOD", "BARRELTRACKCUTS", reducedtrack::IsBarrelSelected); +} // namespace o2::aod + +using MyEvent = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended>::iterator; +using MyEventVtxCov = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::ReducedEventsVtxCov>::iterator; +using MyBarrelTracks = soa::Join<aod::ReducedTracks, aod::ReducedTracksBarrel, aod::ReducedTracksBarrelCov, aod::ReducedTracksBarrelPID>; +using MyBarrelTracksSelected = soa::Join<aod::ReducedTracks, aod::ReducedTracksBarrel, aod::ReducedTracksBarrelCov, aod::ReducedTracksBarrelPID, aod::BarrelTrackCuts>; + +void DefineHistograms(o2::framework::OutputObj<HistogramManager> histMan, TString histClasses); + +// HACK: In order to be able to deduce which kind of aod object is transmitted to the templated VarManager::Fill functions +// a constexpr static bit map must be defined and sent as template argument +// The user has to include in this bit map all the tables needed in analysis, as defined in VarManager::ObjTypes +// Additionally, one should make sure that the requested tables are actually provided in the process() function, +// otherwise a compile time error will be thrown. +// This is a temporary fix until the arrow/ROOT issues are solved, at which point it will be possible +// to automatically detect the object types transmitted to the VarManager +constexpr static uint32_t fgEventFillMap = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended; +constexpr static uint32_t fgTrackFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; + +struct BarrelTrackSelection { + Produces<aod::BarrelTrackCuts> trackSel; + OutputObj<HistogramManager> fHistMan{"output"}; + AnalysisCompositeCut* fTrackCut; + + float* fValues; // array to be used by the VarManager + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan.setObject(new HistogramManager("analysisHistos", "aa", VarManager::kNVars)); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "TrackBarrel_BeforeCuts;TrackBarrel_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + + DefineCuts(); + } + + void DefineCuts() + { + fTrackCut = new AnalysisCompositeCut(true); // true: use AND + AnalysisCut* cut1 = new AnalysisCut(); + cut1->AddCut(VarManager::kPt, 0.2, 10.0); + cut1->AddCut(VarManager::kEta, -0.8, 0.8); + cut1->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + cut1->AddCut(VarManager::kITSchi2, 0.0, 5.0); + cut1->AddCut(VarManager::kITSncls, 3.5, 7.5); + cut1->AddCut(VarManager::kTPCncls, 69.5, 159.5); + cut1->AddCut(VarManager::kTrackDCAxy, -1.0, +1.0); + cut1->AddCut(VarManager::kTrackDCAz, -3.0, +3.0); + + cut1->AddCut(VarManager::kTPCsignal, 70, 90, false); //exclude = false + cut1->AddCut(VarManager::kTPCsignal, 75, 90, false, VarManager::kPin, 2.0, 1e+10); //exclude = false + //cut1->AddCut(VarManager::kTOFnSigmaEl, -3, +3, false); //exclude = false + //fTrackCut->AddCut(cut1); + + AnalysisCompositeCut* pidcut = new AnalysisCompositeCut(false); // false : use OR + AnalysisCut* pidcut_high = new AnalysisCut("pidcut_high", "e high"); + pidcut_high->AddCut(VarManager::kTPCsignal, 75, 90, false, VarManager::kPin, 2.0, 1e+10, false); //exclude = false + pidcut_high->AddCut(VarManager::kTPCsignal, 70, 90, false); //exclude = false + + AnalysisCut* pidcut_rejPr = new AnalysisCut("pidcut_rejPr", "proton rejection"); + TF1* f1minPr = new TF1("f1minPr", "[0]+[1]*x", 0, 10); + f1minPr->SetParameters(170, -100); + TF1* f1maxPr = new TF1("f1maxPr", "[0]+[1]*x", 0, 10); + f1maxPr->SetParameters(175, -75); + pidcut_rejPr->AddCut(VarManager::kTPCsignal, f1minPr, f1maxPr, true, VarManager::kPin, 0.8, 1.4, false); //exclude = false + + AnalysisCut* pidcut_rejKa = new AnalysisCut("pidcut_rejKa", "kaon rejection"); + TF1* f1minKa = new TF1("f1minKa", "[0]+[1]*x", 0, 10); + f1minKa->SetParameters(220, -300); + TF1* f1maxKa = new TF1("f1maxKa", "[0]+[1]*x", 0, 10); + f1maxKa->SetParameters(182.5, -150); + pidcut_rejKa->AddCut(VarManager::kTPCsignal, f1minKa, f1maxKa, true, VarManager::kPin, 0.4, 0.8, false); //exclude = false + + AnalysisCut* pidcut_rejPi = new AnalysisCut("pidcut_rejPi", "pion rejection"); + TF1* f1maxPi = new TF1("f1maxPi", "[0]+[1]*x", 0, 10); + f1maxPi->SetParameters(85, -50); + pidcut_rejPi->AddCut(VarManager::kTPCsignal, 70, f1maxPi, true, VarManager::kPin, 0.0, 0.4, false); //exclude = false + + cut1->AddCut(VarManager::kTPCsignal, f1minPr, f1maxPr, true, VarManager::kPin, 0.8, 1.4, false); //exclude = false + cut1->AddCut(VarManager::kTPCsignal, f1minKa, f1maxKa, true, VarManager::kPin, 0.4, 0.8, false); //exclude = false + cut1->AddCut(VarManager::kTPCsignal, 70, f1maxPi, true, VarManager::kPin, 0.0, 0.4, false); //exclude = false + + //pidcut->AddCut(pidcut_high); + //pidcut->AddCut(pidcut_rejPr); + //pidcut->AddCut(pidcut_rejKa); + //pidcut->AddCut(pidcut_rejPi); + //fTrackCut->AddCut(pidcut); + + fTrackCut->AddCut(cut1); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvent event, MyBarrelTracks const& tracks) + { + for (int i = 0; i < VarManager::kNVars; ++i) { + fValues[i] = -9999.0f; + } + // fill event information which might be needed in histograms that combine track and event properties + VarManager::FillEvent<fgEventFillMap>(event, fValues); + + for (auto& track : tracks) { + for (int i = VarManager::kNEventWiseVariables; i < VarManager::kNMuonTrackVariables; ++i) { + fValues[i] = -9999.0f; + } + VarManager::FillTrack<fgTrackFillMap>(track, fValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", fValues); + + if (fTrackCut->IsSelected(fValues) && + (track.flags() & (uint64_t(1) << 2)) && // kITSrefit + (track.flags() & (uint64_t(1) << 6)) && // kTPCrefit + ((track.itsClusterMap() & (uint8_t(1) << 0))) // SPD first + ) { + trackSel(1); + fHistMan->FillHistClass("TrackBarrel_AfterCuts", fValues); + } else { + trackSel(0); + } + } + } +}; + +struct DileptonEE { + OutputObj<HistogramManager> fHistMan{"output"}; + AnalysisCompositeCut* fEventCut; + //NOTE: one could define also a dilepton cut, but for now basic selections can be supported using Partition + + Partition<MyBarrelTracksSelected> posTracks = aod::reducedtrack::charge > 0 && aod::reducedtrack::isBarrelSelected == 1; + Partition<MyBarrelTracksSelected> negTracks = aod::reducedtrack::charge < 0 && aod::reducedtrack::isBarrelSelected == 1; + + void init(o2::framework::InitContext&) + { + VarManager::SetDefaultVarNames(); + fHistMan.setObject(new HistogramManager("analysisHistos", "aa", VarManager::kNVars)); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;PairsBarrelULS;PairsBarrelLSpp;PairsBarrelLSnn;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + + DefineCuts(); + } + + void DefineCuts() + { + fEventCut = new AnalysisCompositeCut(true); + + AnalysisCut* varCut = new AnalysisCut(); + varCut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + varCut->AddCut(VarManager::kVtxNcontrib, 0.5, 1e+10); + fEventCut->AddCut(varCut); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEventVtxCov event, MyBarrelTracksSelected const& tracks) + { + // Reset the VarManager::fgValues array + // The reset can be done selectively, using arguments in the ResetValues() function + VarManager::ResetValues(); + + VarManager::FillEvent<fgEventFillMap>(event); + fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); // automatically fill all the histograms in the class Event + if (!fEventCut->IsSelected(VarManager::fgValues)) { + return; + } + fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + + // Run the same event pairing for barrel tracks + // TODO: Use combinations() when this will work for Partitions + /* e.g. + * for (auto& [tpos, tneg] : combinations(posTracks, negTracks)) { + VarManager::FillPair(tpos, tneg); + fHistMan->FillHistClass("PairsBarrelULS", VarManager::fgValues); + } + */ + + for (auto tpos : posTracks) { + for (auto tneg : negTracks) { // +- pairs + VarManager::FillPair(tpos, tneg); + fHistMan->FillHistClass("PairsBarrelULS", VarManager::fgValues); + } + for (auto tpos2 = tpos + 1; tpos2 != posTracks.end(); ++tpos2) { // ++ pairs + VarManager::FillPair(tpos, tpos2); + fHistMan->FillHistClass("PairsBarrelLSpp", VarManager::fgValues); + } + } + for (auto tneg : negTracks) { // -- pairs + for (auto tneg2 = tneg + 1; tneg2 != negTracks.end(); ++tneg2) { + VarManager::FillPair(tneg, tneg2); + fHistMan->FillHistClass("PairsBarrelLSnn", VarManager::fgValues); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<BarrelTrackSelection>("barrel-track-selection"), + adaptAnalysisTask<DileptonEE>("dilepton-ee")}; +} + +void DefineHistograms(o2::framework::OutputObj<HistogramManager> histMan, TString histClasses) +{ + // + // Define here the histograms for all the classes required in analysis. + // The histogram classes are provided in the histClasses string, separated by semicolon ";" + // The histogram classes and their components histograms are defined below depending on the name of the histogram class + // + const int kNRuns = 2; + int runs[kNRuns] = {244918, 244919}; + TString runsStr; + for (int i = 0; i < kNRuns; i++) { + runsStr += Form("%d;", runs[i]); + } + VarManager::SetRunNumbers(kNRuns, runs); + + std::unique_ptr<TObjArray> arr(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < arr->GetEntries(); ++iclass) { + TString classStr = arr->At(iclass)->GetName(); + + if (classStr.Contains("Event")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "VtxZ_Run", "Vtx Z", true, + kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, runsStr.Data()); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "VtxX_VtxY", "Vtx X vs Vtx Y", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "VtxX_VtxY_VtxZ", "vtx x - y - z", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 60, -15.0, 15.0, VarManager::kVtxZ); // TH3F histogram + histMan->AddHistogram(classStr.Data(), "NContrib_vs_VtxZ_prof", "Vtx Z vs ncontrib", true, 30, -15.0, 15.0, VarManager::kVtxZ, 10, -1., 1., VarManager::kVtxNcontrib); // TProfile histogram + histMan->AddHistogram(classStr.Data(), "VtxZ_vs_VtxX_VtxY_prof", "Vtx Z vs (x,y)", true, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 10, -1., 1., VarManager::kVtxZ); // TProfile2D histogram + histMan->AddHistogram(classStr.Data(), "Ncontrib_vs_VtxZ_VtxX_VtxY_prof", "n-contrib vs (x,y,z)", true, + 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 30, -15., 15., VarManager::kVtxZ, + "", "", "", VarManager::kVtxNcontrib); // TProfile3D + + double vtxXbinLims[10] = {0.055, 0.06, 0.062, 0.064, 0.066, 0.068, 0.070, 0.072, 0.074, 0.08}; + double vtxYbinLims[7] = {0.31, 0.32, 0.325, 0.33, 0.335, 0.34, 0.35}; + double vtxZbinLims[13] = {-15.0, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 15.0}; + double nContribbinLims[9] = {0.0, 100.0, 200.0, 400.0, 600.0, 1000.0, 1500.0, 2000.0, 4000.0}; + + histMan->AddHistogram(classStr.Data(), "VtxX_VtxY_nonEqualBinning", "Vtx X vs Vtx Y", false, 9, vtxXbinLims, VarManager::kVtxX, 6, vtxYbinLims, VarManager::kVtxY); // THnF histogram with custom non-equal binning + + histMan->AddHistogram(classStr.Data(), "VtxZ_weights", "Vtx Z", false, + 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, 10, 0., 0., VarManager::kNothing, + "", "", "", VarManager::kNothing, VarManager::kVtxNcontrib); // TH1F histogram, filled with weights using the vtx n-contributors + + Int_t vars[4] = {VarManager::kVtxX, VarManager::kVtxY, VarManager::kVtxZ, VarManager::kVtxNcontrib}; + TArrayD binLimits[4]; + binLimits[0] = TArrayD(10, vtxXbinLims); + binLimits[1] = TArrayD(7, vtxYbinLims); + binLimits[2] = TArrayD(13, vtxZbinLims); + binLimits[3] = TArrayD(9, nContribbinLims); + histMan->AddHistogram(classStr.Data(), "vtxHisto", "n contrib vs (x,y,z)", 4, vars, binLimits); + + histMan->AddHistogram(classStr.Data(), "CentV0M_vtxZ", "CentV0M vs Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ, 20, 0., 100., VarManager::kCentVZERO); // TH2F histogram + + histMan->AddHistogram(classStr.Data(), "VtxChi2", "Vtx chi2", false, 100, 0.0, 100.0, VarManager::kVtxChi2); // TH1F histogram + + continue; + } // end if(Event) + + if (classStr.Contains("Track")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Pt", "p_{T} distribution", false, 100, 0.0, 10.0, VarManager::kPt); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Eta", "#eta distribution", false, 200, -1.0, 1.0, VarManager::kEta); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Phi_Eta", "#phi vs #eta distribution", false, 200, -1.0, 1.0, VarManager::kEta, 72, 0, TMath::TwoPi(), VarManager::kPhi); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "P", "p distribution", false, 200, 0.0, 20.0, VarManager::kP); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Px", "p_{x} distribution", false, 200, 0.0, 20.0, VarManager::kPx); + histMan->AddHistogram(classStr.Data(), "Py", "p_{y} distribution", false, 200, 0.0, 20.0, VarManager::kPy); + histMan->AddHistogram(classStr.Data(), "Pz", "p_{z} distribution", false, 400, -20.0, 20.0, VarManager::kPz); + histMan->AddHistogram(classStr.Data(), "DCAxy_DCAz", "DCA_{xy} vs DCA_{z}", false, 100, -5.0, 5.0, VarManager::kTrackDCAxy, 100, -5.0, 5.0, VarManager::kTrackDCAz); // TH2F histogram + + if (classStr.Contains("Barrel")) { + histMan->AddHistogram(classStr.Data(), "TPCncls", "Number of cluster in TPC", false, 160, -0.5, 159.5, VarManager::kTPCncls); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "ITSncls", "Number of cluster in ITS", false, 8, -0.5, 7.5, VarManager::kITSncls); // TH1F histogram + //for TPC PID + histMan->AddHistogram(classStr.Data(), "TPCdedx_pIN", "TPC dE/dx vs pIN", false, 1000, 0.0, 10.0, VarManager::kPin, 200, 0.0, 200., VarManager::kTPCsignal); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaEl_pIN", "TPC dE/dx n#sigma_{e} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTPCnSigmaEl); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaEl_Eta", "TPC dE/dx n#sigma_{e} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTPCnSigmaEl); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaPi_pIN", "TPC dE/dx n#sigma_{#pi} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTPCnSigmaPi); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaPi_Eta", "TPC dE/dx n#sigma_{#pi} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTPCnSigmaPi); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaKa_pIN", "TPC dE/dx n#sigma_{K} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTPCnSigmaKa); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaKa_Eta", "TPC dE/dx n#sigma_{K} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTPCnSigmaKa); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaPr_pIN", "TPC dE/dx n#sigma_{p} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTPCnSigmaPr); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCnSigmaPr_Eta", "TPC dE/dx n#sigma_{p} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTPCnSigmaPr); // TH2F histogram + + //for TOF PID + histMan->AddHistogram(classStr.Data(), "TOFbeta_pIN", "TOF #beta vs pIN", false, 1000, 0.0, 10.0, VarManager::kPin, 120, 0.0, 1.2, VarManager::kTOFbeta); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaEl_pIN", "TOF #beta n#sigma_{e} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTOFnSigmaEl); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaEl_Eta", "TOF #beta n#sigma_{e} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTOFnSigmaEl); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaPi_pIN", "TOF #beta n#sigma_{#pi} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTOFnSigmaPi); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaPi_Eta", "TOF #beta n#sigma_{#pi} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTOFnSigmaPi); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaKa_pIN", "TOF #beta n#sigma_{K} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTOFnSigmaKa); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaKa_Eta", "TOF #beta n#sigma_{K} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTOFnSigmaKa); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaPr_pIN", "TOF #beta n#sigma_{p} vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 200, -10, +10, VarManager::kTOFnSigmaPr); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TOFnSigmaPr_Eta", "TOF #beta n#sigma_{p} vs #eta", false, 20, -1, +1, VarManager::kEta, 200, -10, +10, VarManager::kTOFnSigmaPr); // TH2F histogram + + histMan->AddHistogram(classStr.Data(), "Cov1Pt_Pt", "cov(1/pt,1/pt) vs p_{T} distribution", false, 100, 0.0, 10.0, VarManager::kPt, 100, 0.0, 1.0, VarManager::kTrackC1Pt21Pt2); // TH2F histogram + } + } + + if (classStr.Contains("Pairs")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Mass_Pt", "", false, 500, 0.0, 5.0, VarManager::kMass, 200, 0.0, 20.0, VarManager::kPt); + } + + } // end loop over histogram classes +} diff --git a/Analysis/Tasks/PWGDQ/dileptonMuMu.cxx b/Analysis/Tasks/PWGDQ/dileptonMuMu.cxx new file mode 100644 index 0000000000000..b63321b201daf --- /dev/null +++ b/Analysis/Tasks/PWGDQ/dileptonMuMu.cxx @@ -0,0 +1,393 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include <TH1F.h> +#include <TMath.h> +#include <THashList.h> +#include <TString.h> +#include <iostream> +#include <vector> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +//using namespace o2::framework::expressions; +using namespace o2::aod; + +// Some definitions +namespace o2::aod +{ + +namespace reducedevent +{ +DECLARE_SOA_COLUMN(Category, category, int); +DECLARE_SOA_COLUMN(IsEventSelected, isEventSelected, int); +} // namespace reducedevent + +namespace reducedtrack +{ +DECLARE_SOA_COLUMN(IsMuonSelected, isMuonSelected, int); +} // namespace reducedtrack +namespace reducedpair +{ +DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Charge, charge, int); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return pt * std::cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return pt * std::sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return pt * std::sinh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pmom, pmom, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); +} // namespace reducedpair + +DECLARE_SOA_TABLE(EventCuts, "AOD", "EVENTCUTS", reducedevent::IsEventSelected); +DECLARE_SOA_TABLE(EventCategories, "AOD", "EVENTCATEGORIES", reducedevent::Category); +DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "MUONTRACKCUTS", reducedtrack::IsMuonSelected); +} // namespace o2::aod + +using MyEvents = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended>; +using MyEventsSelected = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::EventCuts>; +using MyEventsVtxCov = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::ReducedEventsVtxCov>; +using MyEventsVtxCovSelected = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::ReducedEventsVtxCov, aod::EventCuts>; +using MyMuonTracks = soa::Join<aod::ReducedMuons, aod::ReducedMuonsExtended>; +using MyMuonTracksSelected = soa::Join<aod::ReducedMuons, aod::ReducedMuonsExtended, aod::MuonTrackCuts>; + +void DefineHistograms(HistogramManager* histMan, TString histClasses); + +// HACK: In order to be able to deduce which kind of aod object is transmitted to the templated VarManager::Fill functions +// a constexpr static bit map must be defined and sent as template argument +// The user has to include in this bit map all the tables needed in analysis, as defined in VarManager::ObjTypes +// Additionally, one should make sure that the requested tables are actually provided in the process() function, +// otherwise a compile time error will be thrown. +// This is a temporary fix until the arrow/ROOT issues are solved, at which point it will be possible +// to automatically detect the object types transmitted to the VarManager +constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended; +constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackMuon; + +struct EventSelection { + Produces<aod::EventCuts> eventSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut* fEventCut; + + float* fValues; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + DefineCuts(); + } + + void DefineCuts() + { + fEventCut = new AnalysisCompositeCut(true); + + AnalysisCut* varCut = new AnalysisCut(); + varCut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + varCut->AddCut(VarManager::kIsMuonUnlikeLowPt7, 0.5, 1.5); + + fEventCut->AddCut(varCut); + // TODO: Add more cuts, also enable cuts which are not easily possible via the VarManager (e.g. trigger selections) + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& event) + { + // Reset the fValues array + VarManager::ResetValues(0, VarManager::kNEventWiseVariables, fValues); + + VarManager::FillEvent<gkEventFillMap>(event, fValues); + fHistMan->FillHistClass("Event_BeforeCuts", fValues); // automatically fill all the histograms in the class Event + if (fEventCut->IsSelected(fValues)) { + fHistMan->FillHistClass("Event_AfterCuts", fValues); + eventSel(1); + } else { + eventSel(0); + } + } +}; + +struct MuonTrackSelection { + Produces<aod::MuonTrackCuts> trackSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut* fMuonCut23; + AnalysisCompositeCut* fMuonCut310; + + float* fValues; // array to be used by the VarManager + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "TrackMuon_BeforeCuts;TrackMuon_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); // provide the list of required variables so that VarManager knows what to fill + + DefineCuts(); + } + + void DefineCuts() + { + fMuonCut23 = new AnalysisCompositeCut(true); + AnalysisCut kineMuonCut23; + kineMuonCut23.AddCut(VarManager::kPt, 1.0, 20); + kineMuonCut23.AddCut(VarManager::kEta, -4.0, -2.5); + kineMuonCut23.AddCut(VarManager::kMuonChi2, 0.0, 1e6); + // The value of sigma_PDCA depends on the postion w.r.t the absorber. For 17.6 < RAbs < 26.5cm, sigma_PDCA = 99 cmxGeV/c. For 26.5 < RAbs < 89.5 cm, sigma_PDCA = 54 + // temporarily not applied + // kineMuonCut23.AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); + // kineMuonCut23.AddCut(VarManager::kMuonPDca, 0.0, 594); // Cut is pDCA < 6*sigma_PDCA + fMuonCut23->AddCut(&kineMuonCut23); + + fMuonCut310 = new AnalysisCompositeCut(true); + AnalysisCut kineMuonCut310; + kineMuonCut310.AddCut(VarManager::kPt, 1.0, 20); + kineMuonCut310.AddCut(VarManager::kEta, -4.0, -2.5); + kineMuonCut310.AddCut(VarManager::kMuonChi2, 0.0, 1e6); + // The value of sigma_PDCA depends on the postion w.r.t the absorber. For 17.6 < RAbs < 26.5cm, sigma_PDCA = 99 cmxGeV/c. For 26.5 < RAbs < 89.5 cm, sigma_PDCA = 54 + // temporarily not applied + // kineMuonCut310.AddCut(VarManager::kMuonRAtAbsorberEnd, 26.5, 89.5); + // kineMuonCut310.AddCut(VarManager::kMuonPDca, 0.0, 324); // Cut is pDCA < 6*sigma_PDCA + fMuonCut310->AddCut(&kineMuonCut310); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEventsSelected::iterator const& event, MyMuonTracks const& muons) + { + VarManager::ResetValues(0, VarManager::kNMuonTrackVariables, fValues); + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + for (auto& muon : muons) { + //VarManager::ResetValues(VarManager::kNBarrelTrackVariables, VarManager::kNMuonTrackVariables, fValues); + VarManager::FillTrack<gkMuonFillMap>(muon, fValues); + fHistMan->FillHistClass("TrackMuon_BeforeCuts", fValues); + + if (fMuonCut23->IsSelected(fValues)) { + trackSel(1); + fHistMan->FillHistClass("TrackMuon_AfterCuts", fValues); + } else if (fMuonCut310->IsSelected(fValues)) { + trackSel(1); + fHistMan->FillHistClass("TrackMuon_AfterCuts", fValues); + } else { + trackSel(0); + } + } + } +}; + +struct DileptonMuMu { + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut* fDiMuonCut; + //NOTE: one could define also a dilepton cut, but for now basic selections can be supported using Partition + // NOTE TO THE NOTE: a dimuon cut is needed on the rapidity of the pair. So I added one. Hopefully this works + + float* fValues; + + Partition<MyMuonTracksSelected> posMuons = aod::reducedtrack::charge > 0 && aod::reducedtrack::isMuonSelected == 1; + Partition<MyMuonTracksSelected> negMuons = aod::reducedtrack::charge < 0 && aod::reducedtrack::isMuonSelected == 1; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "PairsMuonULS;PairsMuonLSpp;PairsMuonLSnn;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + DefineCuts(); + } + + void DefineCuts() + { + fDiMuonCut = new AnalysisCompositeCut(true); + AnalysisCut* diMuonCut = new AnalysisCut(); + diMuonCut->AddCut(VarManager::kRap, 2.5, 4.0); + fDiMuonCut->AddCut(diMuonCut); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEventsVtxCovSelected::iterator const& event, MyMuonTracksSelected const& tracks) + { + if (!event.isEventSelected()) { + return; + } + // Reset the fValues array + VarManager::ResetValues(0, VarManager::kNVars, fValues); + + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + // same event pairing for muons + for (auto& tpos : posMuons) { + for (auto& tneg : negMuons) { + //dileptonList(event, VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], 1); + VarManager::FillPair(tpos, tneg, nullptr, VarManager::kJpsiToMuMu); + if (!fDiMuonCut->IsSelected(VarManager::fgValues)) { + return; + } + fHistMan->FillHistClass("PairsMuonULS", VarManager::fgValues); + } + for (auto tpos2 = tpos + 1; tpos2 != posMuons.end(); ++tpos2) { // ++ pairs + VarManager::FillPair(tpos, tpos2, nullptr, VarManager::kJpsiToMuMu); + if (!fDiMuonCut->IsSelected(VarManager::fgValues)) { + return; + } + fHistMan->FillHistClass("PairsMuonLSpp", VarManager::fgValues); + } + } + for (auto tneg : negMuons) { // -- pairs + for (auto tneg2 = tneg + 1; tneg2 != negMuons.end(); ++tneg2) { + VarManager::FillPair(tneg, tneg2, nullptr, VarManager::kJpsiToMuMu); + if (!fDiMuonCut->IsSelected(VarManager::fgValues)) { + return; + } + fHistMan->FillHistClass("PairsMuonLSnn", VarManager::fgValues); + } + } + } +}; + +void DefineHistograms(HistogramManager* histMan, TString histClasses) +{ + // + // Define here the histograms for all the classes required in analysis. + // The histogram classes are provided in the histClasses string, separated by semicolon ";" + // The histogram classes and their components histograms are defined below depending on the name of the histogram class + // + const int kNRuns = 2; + int runs[kNRuns] = {244918, 244919}; + TString runsStr; + for (int i = 0; i < kNRuns; i++) { + runsStr += Form("%d;", runs[i]); + } + VarManager::SetRunNumbers(kNRuns, runs); + + std::unique_ptr<TObjArray> arr(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < arr->GetEntries(); ++iclass) { + TString classStr = arr->At(iclass)->GetName(); + + if (classStr.Contains("Event")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "VtxZ_Run", "Vtx Z", true, + kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, runsStr.Data()); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "VtxX_VtxY", "Vtx X vs Vtx Y", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "VtxX_VtxY_VtxZ", "vtx x - y - z", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 60, -15.0, 15.0, VarManager::kVtxZ); // TH3F histogram + histMan->AddHistogram(classStr.Data(), "NContrib_vs_VtxZ_prof", "Vtx Z vs ncontrib", true, 30, -15.0, 15.0, VarManager::kVtxZ, 10, -1., 1., VarManager::kVtxNcontrib); // TProfile histogram + histMan->AddHistogram(classStr.Data(), "VtxZ_vs_VtxX_VtxY_prof", "Vtx Z vs (x,y)", true, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 10, -1., 1., VarManager::kVtxZ); // TProfile2D histogram + histMan->AddHistogram(classStr.Data(), "Ncontrib_vs_VtxZ_VtxX_VtxY_prof", "n-contrib vs (x,y,z)", true, + 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 30, -15., 15., VarManager::kVtxZ, + "", "", "", VarManager::kVtxNcontrib); // TProfile3D + + double vtxXbinLims[10] = {0.055, 0.06, 0.062, 0.064, 0.066, 0.068, 0.070, 0.072, 0.074, 0.08}; + double vtxYbinLims[7] = {0.31, 0.32, 0.325, 0.33, 0.335, 0.34, 0.35}; + double vtxZbinLims[13] = {-15.0, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 15.0}; + double nContribbinLims[9] = {0.0, 100.0, 200.0, 400.0, 600.0, 1000.0, 1500.0, 2000.0, 4000.0}; + + histMan->AddHistogram(classStr.Data(), "VtxX_VtxY_nonEqualBinning", "Vtx X vs Vtx Y", false, 9, vtxXbinLims, VarManager::kVtxX, 6, vtxYbinLims, VarManager::kVtxY); // THnF histogram with custom non-equal binning + + histMan->AddHistogram(classStr.Data(), "VtxZ_weights", "Vtx Z", false, + 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, 10, 0., 0., VarManager::kNothing, + "", "", "", VarManager::kNothing, VarManager::kVtxNcontrib); // TH1F histogram, filled with weights using the vtx n-contributors + + Int_t vars[4] = {VarManager::kVtxX, VarManager::kVtxY, VarManager::kVtxZ, VarManager::kVtxNcontrib}; + TArrayD binLimits[4]; + binLimits[0] = TArrayD(10, vtxXbinLims); + binLimits[1] = TArrayD(7, vtxYbinLims); + binLimits[2] = TArrayD(13, vtxZbinLims); + binLimits[3] = TArrayD(9, nContribbinLims); + histMan->AddHistogram(classStr.Data(), "vtxHisto", "n contrib vs (x,y,z)", 4, vars, binLimits); + + histMan->AddHistogram(classStr.Data(), "CentV0M_vtxZ", "CentV0M vs Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ, 20, 0., 100., VarManager::kCentVZERO); // TH2F histogram + + histMan->AddHistogram(classStr.Data(), "VtxChi2", "Vtx chi2", false, 100, 0.0, 100.0, VarManager::kVtxChi2); // TH1F histogram + + continue; + } // end if(Event) + + if (classStr.Contains("Track")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Pt", "p_{T} distribution", false, 200, 0.0, 20.0, VarManager::kPt); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Eta", "#eta distribution", false, 19, -4.2, -2.3, VarManager::kEta); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Phi_Eta", "#phi vs #eta distribution", false, 200, -5.0, 5.0, VarManager::kEta, 200, -6.3, 6.3, VarManager::kPhi); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "P", "p distribution", false, 200, 0.0, 20.0, VarManager::kP); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Px", "p_{x} distribution", false, 200, 0.0, 20.0, VarManager::kPx); + histMan->AddHistogram(classStr.Data(), "Py", "p_{y} distribution", false, 200, 0.0, 20.0, VarManager::kPy); + histMan->AddHistogram(classStr.Data(), "Pz", "p_{z} distribution", false, 400, -20.0, 20.0, VarManager::kPz); + + if (classStr.Contains("Muon")) { + histMan->AddHistogram(classStr.Data(), "InvBendingMom", "", false, 100, 0.0, 1.0, VarManager::kMuonInvBendingMomentum); + histMan->AddHistogram(classStr.Data(), "ThetaX", "", false, 100, -1.0, 1.0, VarManager::kMuonThetaX); + histMan->AddHistogram(classStr.Data(), "ThetaY", "", false, 100, -2.0, 2.0, VarManager::kMuonThetaY); + histMan->AddHistogram(classStr.Data(), "ZMu", "", false, 100, -30.0, 30.0, VarManager::kMuonZMu); + histMan->AddHistogram(classStr.Data(), "BendingCoor", "", false, 100, 0.32, 0.35, VarManager::kMuonBendingCoor); + histMan->AddHistogram(classStr.Data(), "NonBendingCoor", "", false, 100, 0.065, 0.07, VarManager::kMuonNonBendingCoor); + histMan->AddHistogram(classStr.Data(), "Chi2", "", false, 100, 0.0, 200.0, VarManager::kMuonChi2); + histMan->AddHistogram(classStr.Data(), "Chi2MatchTrigger", "", false, 100, 0.0, 20.0, VarManager::kMuonChi2MatchTrigger); + histMan->AddHistogram(classStr.Data(), "RAtAbsorberEnd", "", false, 140, 10, 150, VarManager::kMuonRAtAbsorberEnd); + histMan->AddHistogram(classStr.Data(), "p x dca", "", false, 700, 0.0, 700, VarManager::kMuonRAtAbsorberEnd); + } + } + + if (classStr.Contains("Pairs")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Mass", "", false, 100, 2.0, 12, VarManager::kMass); + histMan->AddHistogram(classStr.Data(), "Pt", "", false, 200, 0.0, 20.0, VarManager::kPt); + histMan->AddHistogram(classStr.Data(), "Rapidity", "", false, 19, 2.0, 4.3, VarManager::kRap); + histMan->AddHistogram(classStr.Data(), "Mass_Pt", "mass vs p_{T} distribution", false, 100, 0.0, 20.0, VarManager::kMass, 200, 0.0, 20.0, VarManager::kPt); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "Mass_Y", "mass vs y distribution", false, 100, 0.0, 20.0, VarManager::kMass, 19, 2.0, 4.3, VarManager::kRap); // TH2F histogram + } + } // end loop over histogram classes +} + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<EventSelection>("my-event-selection"), + adaptAnalysisTask<MuonTrackSelection>("muon-track-selection"), + adaptAnalysisTask<DileptonMuMu>("dilepton-mumu")}; +} diff --git a/Analysis/Tasks/PWGDQ/filterPP.cxx b/Analysis/Tasks/PWGDQ/filterPP.cxx new file mode 100644 index 0000000000000..5b5c3573f8838 --- /dev/null +++ b/Analysis/Tasks/PWGDQ/filterPP.cxx @@ -0,0 +1,359 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/TriggerAliases.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include "PWGDQCore/CutsLibrary.h" +#include "PWGDQCore/HistogramsLibrary.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include <TH1F.h> +#include <TH2I.h> +#include <TMath.h> +#include <THashList.h> +#include <TString.h> +#include <iostream> +#include <vector> +#include <memory> +#include <cstring> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod; + +// Some definitions +namespace o2::aod +{ +namespace dqppfilter +{ +DECLARE_SOA_COLUMN(IsDQEventSelected, isDQEventSelected, int); +DECLARE_SOA_COLUMN(IsBarrelSelected, isBarrelSelected, uint8_t); +} // namespace dqppfilter + +DECLARE_SOA_TABLE(DQEventCuts, "AOD", "DQEVENTCUTS", dqppfilter::IsDQEventSelected); +DECLARE_SOA_TABLE(DQBarrelTrackCuts, "AOD", "DQBARRELCUTS", dqppfilter::IsBarrelSelected); +} // namespace o2::aod + +using MyEvents = soa::Join<aod::Collisions, aod::EvSels>; +using MyEventsSelected = soa::Join<aod::Collisions, aod::EvSels, aod::DQEventCuts>; +using MyBarrelTracks = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov, aod::TracksExtended, aod::TrackSelection, aod::pidRespTPC, aod::pidRespTOF, aod::pidRespTOFbeta>; +using MyBarrelTracksSelected = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov, aod::TracksExtended, aod::TrackSelection, aod::pidRespTPC, aod::pidRespTOF, aod::pidRespTOFbeta, aod::DQBarrelTrackCuts>; + +constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision; +constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID; + +void DefineHistograms(HistogramManager* histMan, TString histClasses); + +struct EventSelectionTask { + Produces<aod::DQEventCuts> eventSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut fEventCut{true}; + + float* fValues; + + Configurable<std::string> fConfigCuts{"cfgEventCuts", "eventStandard", "Comma separated list of event cuts; multiple cuts are applied with a logical AND"}; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + DefineCuts(); + } + + void DefineCuts() + { + // default cut is "eventStandard" (kINT7 and vtxZ selection) + TString cutNamesStr = fConfigCuts.value; + if (!cutNamesStr.IsNull()) { + std::unique_ptr<TObjArray> objArray(cutNamesStr.Tokenize(",")); + for (int icut = 0; icut < objArray->GetEntries(); ++icut) { + fEventCut.AddCut(dqcuts::GetAnalysisCut(objArray->At(icut)->GetName())); + } + } + + // NOTE: Additional cuts to those specified via the Configurable may still be added + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& event, aod::BCs const& bcs) + { + // Reset the fValues array + VarManager::ResetValues(0, VarManager::kNEventWiseVariables, fValues); + + VarManager::FillEvent<gkEventFillMap>(event, fValues); + fHistMan->FillHistClass("Event_BeforeCuts", fValues); // automatically fill all the histograms in the class Event + if (fEventCut.IsSelected(fValues)) { + fHistMan->FillHistClass("Event_AfterCuts", fValues); + eventSel(1); + } else { + eventSel(0); + } + } +}; + +struct BarrelTrackSelectionTask { + Produces<aod::DQBarrelTrackCuts> trackSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + std::vector<AnalysisCompositeCut> fTrackCuts; + + float* fValues; // array to be used by the VarManager + + Configurable<std::string> fConfigCuts{"cfgBarrelTrackCuts", "jpsiPID1", "Comma separated list of barrel track cuts"}; + + void init(o2::framework::InitContext&) + { + DefineCuts(); + + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + TString cutNames = "TrackBarrel_BeforeCuts;"; + for (int i = 0; i < fTrackCuts.size(); i++) { + cutNames += Form("TrackBarrel_%s;", fTrackCuts[i].GetName()); + } + + DefineHistograms(fHistMan, cutNames.Data()); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + } + + void DefineCuts() + { + // available cuts: jpsiKineAndQuality, jpsiPID1, jpsiPID2 + TString cutNamesStr = fConfigCuts.value; + if (!cutNamesStr.IsNull()) { + std::unique_ptr<TObjArray> objArray(cutNamesStr.Tokenize(",")); + for (int icut = 0; icut < objArray->GetEntries(); ++icut) { + fTrackCuts.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + } + } + + // NOTE: Additional cuts to those specified via the Configurable may still be added + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& event, MyBarrelTracks const& tracks, aod::BCs const& bcs) + { + uint8_t filterMap = uint8_t(0); + trackSel.reserve(tracks.size()); + + VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables, fValues); + // fill event information which might be needed in histograms that combine track and event properties + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + for (auto& track : tracks) { + filterMap = uint8_t(0); + // TODO: if a condition on the event selection is applied, the histogram output is missing + VarManager::FillTrack<gkTrackFillMap>(track, fValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", fValues); + int i = 0; + for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); ++cut, ++i) { + if ((*cut).IsSelected(fValues)) { + filterMap |= (uint8_t(1) << i); + fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), fValues); + } + } + trackSel(filterMap); + } + } +}; + +struct FilterPPTask { + Produces<aod::DQEventFilter> eventFilter; + OutputObj<THashList> fOutputList{"output"}; + OutputObj<TH2I> fStats{"stats"}; + HistogramManager* fHistMan; + std::vector<AnalysisCompositeCut> fPairCuts; + + float* fValues; + + Partition<MyBarrelTracksSelected> posTracks = aod::track::signed1Pt > 0.0f && aod::dqppfilter::isBarrelSelected > uint8_t(0); + Partition<MyBarrelTracksSelected> negTracks = aod::track::signed1Pt < 0.0f && aod::dqppfilter::isBarrelSelected > uint8_t(0); + + Configurable<std::string> fConfigTrackCuts{"cfgBarrelTrackCuts", "jpsiPID1", "Comma separated list of barrel track cuts"}; + Configurable<std::string> fConfigPairCuts{"cfgPairCuts", "pairMassLow", "Comma separated list of pair cuts"}; + + int fNTrackCuts; + int fNPairCuts; + TObjArray* fTrkCutsNameArray; + + void DefineCuts() + { + // available pair cuts in CutsLibrary: pairNoCut,pairMassLow,pairJpsi,pairPsi2S,pairUpsilon,pairJpsiPtLow1, pairJpsiPtLow2 + TString pairCutNamesStr = fConfigPairCuts.value; + std::unique_ptr<TObjArray> objArray(pairCutNamesStr.Tokenize(",")); + fNPairCuts = objArray->GetEntries(); + if (fNPairCuts) { + for (int icut = 0; icut < fNPairCuts; ++icut) { + fPairCuts.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + } + } + + // NOTE: Additional cuts to those specified via the Configurable may still be added + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void init(o2::framework::InitContext&) + { + DefineCuts(); + + fValues = new float[VarManager::kNVars]; + + // initialize the variable manager + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + // configure histograms + TString trackCutNamesStr = fConfigTrackCuts.value; + fTrkCutsNameArray = trackCutNamesStr.Tokenize(","); + fNTrackCuts = fTrkCutsNameArray->GetEntries(); + TString histNames = ""; + for (int i = 0; i < fNTrackCuts; i++) { + histNames += Form("PairsBarrelPM_%s;", fTrkCutsNameArray->At(i)->GetName()); + } + DefineHistograms(fHistMan, histNames.Data()); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + // configure the stats histogram + fStats.setObject(new TH2I("stats", "stats", fNPairCuts, -0.5, -0.5 + fNPairCuts, fNTrackCuts, -0.5, -0.5 + fNTrackCuts)); + for (int i = 0; i < fNPairCuts; ++i) { + fStats->GetXaxis()->SetBinLabel(i + 1, fPairCuts[i].GetName()); + } + for (int i = 0; i < fNTrackCuts; ++i) { + fStats->GetYaxis()->SetBinLabel(i + 1, fTrkCutsNameArray->At(i)->GetName()); + } + } + + void process(MyEventsSelected::iterator const& event, MyBarrelTracksSelected const& tracks, aod::BCs const& bcs) + { + uint64_t filter = 0; + + if (event.isDQEventSelected() == 1) { + // Reset the fValues array + VarManager::ResetValues(0, VarManager::kNVars, fValues); + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + uint8_t* pairsCount = new uint8_t[fNPairCuts * fNTrackCuts]; + for (int i = 0; i < fNPairCuts * fNTrackCuts; i++) { + pairsCount[i] = 0; + } + + uint8_t cutFilter = 0; + for (auto tpos : posTracks) { + for (auto tneg : negTracks) { // +- pairs + cutFilter = tpos.isBarrelSelected() & tneg.isBarrelSelected(); + if (!cutFilter) { // the tracks must have at least one filter bit in common to continue + continue; + } + VarManager::FillPair(tpos, tneg, fValues); // compute pair quantities + for (int i = 0; i < fNTrackCuts; ++i) { + if (!(cutFilter & (uint8_t(1) << i))) { + continue; + } + fHistMan->FillHistClass(Form("PairsBarrelPM_%s", fTrkCutsNameArray->At(i)->GetName()), fValues); + int j = 0; + for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, j++) { + if ((*cut).IsSelected(fValues)) { + pairsCount[j + i * fNPairCuts] += 1; + } + } + } + } + } + + for (int i = 0; i < fNTrackCuts; i++) { + for (int j = 0; j < fNPairCuts; j++) { + if (pairsCount[j + i * fNPairCuts] > 0) { + filter |= (uint64_t(1) << (j + i * fNPairCuts)); + fStats->Fill(j, i); + } + } + } + delete[] pairsCount; + } + eventFilter(filter); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<EventSelectionTask>("dq-event-selection"), + adaptAnalysisTask<BarrelTrackSelectionTask>("dq-barrel-track-selection"), + adaptAnalysisTask<FilterPPTask>("dq-ppFilter")}; +} + +void DefineHistograms(HistogramManager* histMan, TString histClasses) +{ + // + // Define here the histograms for all the classes required in analysis. + // The histogram classes are provided in the histClasses string, separated by semicolon ";" + // NOTE: Additional histograms to those predefined in the library can be added here !! + const int kNRuns = 25; + int runs[kNRuns] = { + 285009, 285011, 285012, 285013, 285014, 285015, 285064, 285065, 285066, 285106, + 285108, 285125, 285127, 285165, 285200, 285202, 285203, 285222, 285224, 285327, + 285328, 285347, 285364, 285365, 285396}; + VarManager::SetRunNumbers(kNRuns, runs); + + std::unique_ptr<TObjArray> objArray(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { + TString classStr = objArray->At(iclass)->GetName(); + histMan->AddHistClass(classStr.Data()); + + if (classStr.Contains("Event")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "event", "trigger,vtxPbPb"); + } + + if (classStr.Contains("Track")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "its,tpcpid,dca"); + } + + if (classStr.Contains("Pairs")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair"); + } + } +} diff --git a/Analysis/Tasks/PWGDQ/tableMaker.cxx b/Analysis/Tasks/PWGDQ/tableMaker.cxx new file mode 100644 index 0000000000000..03f6a2a6eaffb --- /dev/null +++ b/Analysis/Tasks/PWGDQ/tableMaker.cxx @@ -0,0 +1,252 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/TriggerAliases.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include <iostream> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +//using namespace o2::framework::expressions; +using namespace o2::aod; + +using MyEvents = soa::Join<aod::Collisions, aod::EvSels, aod::Cents>; +using MyBarrelTracks = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov, aod::TracksExtended, aod::TrackSelection, aod::pidRespTPC, aod::pidRespTOF, aod::pidRespTOFbeta>; + +// HACK: In order to be able to deduce which kind of aod object is transmitted to the templated VarManager::Fill functions +// a constexpr static bit map must be defined and sent as template argument +// The user has to include in this bit map all the tables needed in analysis, as defined in VarManager::ObjTypes +// Additionally, one should make sure that the requested tables are actually provided in the process() function, +// otherwise a compile time error will be thrown. +// This is a temporary fix until the arrow/ROOT issues are solved, at which point it will be possible +// to automatically detect the object types transmitted to the VarManager +constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent; +constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID; + +struct TableMaker { + + Produces<ReducedEvents> event; + Produces<ReducedEventsExtended> eventExtended; + Produces<ReducedEventsVtxCov> eventVtxCov; + Produces<ReducedTracks> trackBasic; + Produces<ReducedTracksBarrel> trackBarrel; + Produces<ReducedTracksBarrelCov> trackBarrelCov; + Produces<ReducedTracksBarrelPID> trackBarrelPID; + Produces<ReducedMuons> muonBasic; + Produces<ReducedMuonsExtended> muonExtended; + + float* fValues; + + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + + // TODO: Filters should be used to make lowest level selection. The additional more restrictive cuts should be defined via the AnalysisCuts + // TODO: Multiple event selections can be applied and decisions stored in the reducedevent::tag + AnalysisCompositeCut* fEventCut; + // TODO: Multiple track selections can be applied and decisions stored in the reducedtrack::filteringFlags + // Cuts should be defined using Configurables (prepare cut libraries, as discussed in O2 DQ meetings) + AnalysisCompositeCut* fTrackCut; + + // Partition will select fast a group of tracks with basic requirements + // If some of the cuts cannot be included in the Partition expression, add them via AnalysisCut(s) + Partition<MyBarrelTracks> barrelSelectedTracks = o2::aod::track::pt >= 1.0f && nabs(o2::aod::track::eta) <= 0.9f && o2::aod::track::tpcSignal >= 70.0f && o2::aod::track::tpcSignal <= 100.0f && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; + + // TODO a few of the important muon variables in the central data model are dynamic columns so not usable in expressions (e.g. eta, phi) + // Update the data model to have them as expression columns + Partition<aod::Muons> muonSelectedTracks = o2::aod::muon::pt >= 1.0f; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms("Event_BeforeCuts;Event_AfterCuts;TrackBarrel_BeforeCuts;TrackBarrel_AfterCuts"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + DefineCuts(); + } + + void DefineCuts() + { + fEventCut = new AnalysisCompositeCut(true); + AnalysisCut* eventVarCut = new AnalysisCut(); + eventVarCut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + eventVarCut->AddCut(VarManager::kIsINT7, 0.5, 1.5); // require kINT7 + fEventCut->AddCut(eventVarCut); + + fTrackCut = new AnalysisCompositeCut(true); + AnalysisCut* trackVarCut = new AnalysisCut(); + //trackVarCut->AddCut(VarManager::kPt, 1.0, 1000.0); + //trackVarCut->AddCut(VarManager::kEta, -0.9, 0.9); + //trackVarCut->AddCut(VarManager::kTPCsignal, 70.0, 100.0); + trackVarCut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); + trackVarCut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); + //trackVarCut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + //trackVarCut->AddCut(VarManager::kITSchi2, 0.1, 36.0); + trackVarCut->AddCut(VarManager::kTPCncls, 100.0, 161.); + + AnalysisCut* pidCut1 = new AnalysisCut(); + TF1* cutLow1 = new TF1("cutLow1", "pol1", 0., 10.); + cutLow1->SetParameters(130., -40.0); + pidCut1->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + + fTrackCut->AddCut(trackVarCut); + fTrackCut->AddCut(pidCut1); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& collision, aod::MuonClusters const& clustersMuon, aod::Muons const& tracksMuon, aod::BCs const& bcs, MyBarrelTracks const& tracksBarrel) + { + uint64_t tag = 0; + uint32_t triggerAliases = 0; + for (int i = 0; i < kNaliases; i++) { + if (collision.alias()[i] > 0) { + triggerAliases |= (uint32_t(1) << i); + } + } + + VarManager::ResetValues(0, VarManager::kNEventWiseVariables, fValues); + VarManager::FillEvent<gkEventFillMap>(collision, fValues); // extract event information and place it in the fgValues array + fHistMan->FillHistClass("Event_BeforeCuts", fValues); // automatically fill all the histograms in the class Event + + if (!fEventCut->IsSelected(fValues)) { + return; + } + + fHistMan->FillHistClass("Event_AfterCuts", fValues); + + event(tag, collision.bc().runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib()); + eventExtended(collision.bc().globalBC(), collision.bc().triggerMask(), triggerAliases, collision.centV0M()); + eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + + uint64_t trackFilteringTag = 0; + trackBasic.reserve(barrelSelectedTracks.size()); + trackBarrel.reserve(barrelSelectedTracks.size()); + trackBarrelCov.reserve(barrelSelectedTracks.size()); + trackBarrelPID.reserve(barrelSelectedTracks.size()); + + for (auto& track : barrelSelectedTracks) { + VarManager::FillTrack<gkTrackFillMap>(track, fValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", fValues); + if (!fTrackCut->IsSelected(fValues)) { + continue; + } + fHistMan->FillHistClass("TrackBarrel_AfterCuts", fValues); + + if (track.isGlobalTrack()) { + trackFilteringTag |= (uint64_t(1) << 0); + } + if (track.isGlobalTrackSDD()) { + trackFilteringTag |= (uint64_t(1) << 1); + } + trackBasic(event.lastIndex(), track.globalIndex(), trackFilteringTag, track.pt(), track.eta(), track.phi(), track.charge()); + trackBarrel(track.tpcInnerParam(), track.flags(), track.itsClusterMap(), track.itsChi2NCl(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), + track.tpcNClsShared(), track.tpcChi2NCl(), + track.trdChi2(), track.tofChi2(), + track.length(), track.dcaXY(), track.dcaZ()); + trackBarrelCov(track.cYY(), track.cZZ(), track.cSnpSnp(), track.cTglTgl(), track.c1Pt21Pt2()); + trackBarrelPID(track.tpcSignal(), + track.tpcNSigmaEl(), track.tpcNSigmaMu(), + track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), + track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl(), + track.tofSignal(), track.beta(), + track.tofNSigmaEl(), track.tofNSigmaMu(), + track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), + track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe(), track.tofNSigmaAl(), + track.trdSignal()); + } + + muonBasic.reserve(muonSelectedTracks.size()); + muonExtended.reserve(muonSelectedTracks.size()); + for (auto& muon : muonSelectedTracks) { + // TODO: add proper information for muon tracks + if (muon.bcId() != collision.bcId()) { + continue; + } + // TODO: the trackFilteringTag will not be needed to encode whether the track is a muon since there is a dedicated table for muons + trackFilteringTag |= (uint64_t(1) << 0); // this is a MUON arm track + muonBasic(event.lastIndex(), trackFilteringTag, muon.pt(), muon.eta(), muon.phi(), muon.charge()); + muonExtended(muon.inverseBendingMomentum(), muon.thetaX(), muon.thetaY(), muon.zMu(), muon.bendingCoor(), muon.nonBendingCoor(), muon.chi2(), muon.chi2MatchTrigger()); + } + } + + void DefineHistograms(TString histClasses) + { + const int kNRuns = 2; + int runs[kNRuns] = {244918, 244919}; + TString runsStr; + for (int i = 0; i < kNRuns; i++) { + runsStr += Form("%d;", runs[i]); + } + VarManager::SetRunNumbers(kNRuns, runs); + + std::unique_ptr<TObjArray> arr(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < arr->GetEntries(); ++iclass) { + TString classStr = arr->At(iclass)->GetName(); + + if (classStr.Contains("Event")) { + fHistMan->AddHistClass(classStr.Data()); + fHistMan->AddHistogram(classStr.Data(), "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxZ_Run", "Vtx Z", true, + kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, runsStr.Data()); + fHistMan->AddHistogram(classStr.Data(), "CentVZERO", "Centrality VZERO", false, 100, 0.0, 100.0, VarManager::kCentVZERO); // TH1F histogram + } + + if (classStr.Contains("Track")) { + fHistMan->AddHistClass(classStr.Data()); + fHistMan->AddHistogram(classStr.Data(), "Pt", "p_{T} distribution", false, 200, 0.0, 20.0, VarManager::kPt); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "Eta", "#eta distribution", false, 500, -5.0, 5.0, VarManager::kEta); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "Phi_Eta", "#phi vs #eta distribution", false, 200, -5.0, 5.0, VarManager::kEta, 200, -6.3, 6.3, VarManager::kPhi); // TH2F histogram + + if (classStr.Contains("Barrel")) { + fHistMan->AddHistogram(classStr.Data(), "TPCncls", "Number of cluster in TPC", false, 160, -0.5, 159.5, VarManager::kTPCncls); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "TPCncls_Run", "Number of cluster in TPC", true, kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, + 10, -0.5, 159.5, VarManager::kTPCncls, 10, 0., 1., VarManager::kNothing, runsStr.Data()); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "ITSncls", "Number of cluster in ITS", false, 8, -0.5, 7.5, VarManager::kITSncls); // TH1F histogram + //for TPC PID + fHistMan->AddHistogram(classStr.Data(), "TPCdedx_pIN", "TPC dE/dx vs pIN", false, 200, 0.0, 20.0, VarManager::kPin, 200, 0.0, 200., VarManager::kTPCsignal); // TH2F histogram + fHistMan->AddHistogram(classStr.Data(), "DCAxy", "DCAxy", false, 100, -3.0, 3.0, VarManager::kTrackDCAxy); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "DCAz", "DCAz", false, 100, -5.0, 5.0, VarManager::kTrackDCAz); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "IsGlobalTrack", "IsGlobalTrack", false, 2, -0.5, 1.5, VarManager::kIsGlobalTrack); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "IsGlobalTrackSDD", "IsGlobalTrackSDD", false, 2, -0.5, 1.5, VarManager::kIsGlobalTrackSDD); // TH1F histogram + } + } + } // end loop over histogram classes + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<TableMaker>("table-maker")}; +} diff --git a/Analysis/Tasks/PWGDQ/tableMakerMuon_pp.cxx b/Analysis/Tasks/PWGDQ/tableMakerMuon_pp.cxx new file mode 100644 index 0000000000000..ea4b779414549 --- /dev/null +++ b/Analysis/Tasks/PWGDQ/tableMakerMuon_pp.cxx @@ -0,0 +1,172 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/TriggerAliases.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include <iostream> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +//using namespace o2::framework::expressions; +using namespace o2::aod; + +using MyEvents = soa::Join<aod::Collisions, aod::EvSels>; + +// HACK: In order to be able to deduce which kind of aod object is transmitted to the templated VarManager::Fill functions +// a constexpr static bit map must be defined and sent as template argument +// The user has to include in this bit map all the tables needed in analysis, as defined in VarManager::ObjTypes +// Additionally, one should make sure that the requested tables are actually provided in the process() function, +// otherwise a compile time error will be thrown. +// This is a temporary fix until the arrow/ROOT issues are solved, at which point it will be possible +// to automatically detect the object types transmitted to the VarManager +constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision; + +struct TableMakerMuon_pp { + + Produces<ReducedEvents> event; + Produces<ReducedEventsExtended> eventExtended; + Produces<ReducedEventsVtxCov> eventVtxCov; + Produces<ReducedTracks> trackBasic; + Produces<ReducedMuons> muonBasic; + Produces<ReducedMuonsExtended> muonExtended; + + float* fValues; + + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + + // TODO: Filters should be used to make lowest level selection. The additional more restrictive cuts should be defined via the AnalysisCuts + // TODO: Multiple event selections can be applied and decisions stored in the reducedevent::tag + AnalysisCompositeCut* fEventCut; + // TODO: Multiple track selections can be applied and decisions stored in the reducedtrack::filteringFlags + + // TODO a few of the important muon variables in the central data model are dynamic columns so not usable in expressions (e.g. eta, phi) + // Update the data model to have them as expression columns + Partition<aod::Muons> muonSelectedTracks = o2::aod::muon::pt >= 0.5f; // For pp collisions a 0.5 GeV/c pp cuts is defined + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms("Event_BeforeCuts;Event_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + DefineCuts(); + } + + void DefineCuts() + { + fEventCut = new AnalysisCompositeCut(true); + AnalysisCut* eventVarCut = new AnalysisCut(); + eventVarCut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + eventVarCut->AddCut(VarManager::kIsINT7, 0.5, 1.5); // require kINT7 + fEventCut->AddCut(eventVarCut); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& collision, aod::MuonClusters const& clustersMuon, aod::Muons const& tracksMuon, aod::BCs const& bcs) + { + uint64_t tag = 0; + uint32_t triggerAliases = 0; + for (int i = 0; i < kNaliases; i++) { + if (collision.alias()[i] > 0) { + triggerAliases |= (uint32_t(1) << i); + } + } + + VarManager::ResetValues(0, VarManager::kNEventWiseVariables, fValues); + VarManager::FillEvent<gkEventFillMap>(collision, fValues); // extract event information and place it in the fgValues array + fHistMan->FillHistClass("Event_BeforeCuts", fValues); // automatically fill all the histograms in the class Event + + if (!fEventCut->IsSelected(fValues)) { + return; + } + + fHistMan->FillHistClass("Event_AfterCuts", fValues); + + event(tag, collision.bc().runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib()); + eventExtended(collision.bc().globalBC(), collision.bc().triggerMask(), triggerAliases, 0.0f); + eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + + uint64_t trackFilteringTag = 0; + + muonBasic.reserve(muonSelectedTracks.size()); + muonExtended.reserve(muonSelectedTracks.size()); + for (auto& muon : muonSelectedTracks) { + // TODO: add proper information for muon tracks + if (muon.bcId() != collision.bcId()) { + continue; + } + // TODO: the trackFilteringTag will not be needed to encode whether the track is a muon since there is a dedicated table for muons + trackFilteringTag |= (uint64_t(1) << 0); // this is a MUON arm track + muonBasic(event.lastIndex(), trackFilteringTag, muon.pt(), muon.eta(), muon.phi(), muon.charge()); + muonExtended(muon.inverseBendingMomentum(), muon.thetaX(), muon.thetaY(), muon.zMu(), muon.bendingCoor(), muon.nonBendingCoor(), muon.chi2(), muon.chi2MatchTrigger()); + } + } + + void DefineHistograms(TString histClasses) + { + const int kNRuns = 2; + int runs[kNRuns] = {244918, 244919}; + TString runsStr; + for (int i = 0; i < kNRuns; i++) { + runsStr += Form("%d;", runs[i]); + } + VarManager::SetRunNumbers(kNRuns, runs); + + TObjArray* arr = histClasses.Tokenize(";"); + for (Int_t iclass = 0; iclass < arr->GetEntries(); ++iclass) { + TString classStr = arr->At(iclass)->GetName(); + + if (classStr.Contains("Event")) { + fHistMan->AddHistClass(classStr.Data()); + fHistMan->AddHistogram(classStr.Data(), "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxZ_Run", "Vtx Z", true, + kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, runsStr.Data()); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxX_VtxY", "Vtx X vs Vtx Y", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY); // TH2F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxX_VtxY_VtxZ", "vtx x - y - z", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 60, -15.0, 15.0, VarManager::kVtxZ); // TH3F histogram + fHistMan->AddHistogram(classStr.Data(), "NContrib_vs_VtxZ_prof", "Vtx Z vs ncontrib", true, 30, -15.0, 15.0, VarManager::kVtxZ, 10, -1., 1., VarManager::kVtxNcontrib); // TProfile histogram + fHistMan->AddHistogram(classStr.Data(), "VtxZ_vs_VtxX_VtxY_prof", "Vtx Z vs (x,y)", true, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 10, -1., 1., VarManager::kVtxZ); // TProfile2D histogram + fHistMan->AddHistogram(classStr.Data(), "Ncontrib_vs_VtxZ_VtxX_VtxY_prof", "n-contrib vs (x,y,z)", true, + 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 30, -15., 15., VarManager::kVtxZ, + "", "", "", VarManager::kVtxNcontrib); // TProfile3D + } + } // end loop over histogram classes + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<TableMakerMuon_pp>("table-maker-muon-pp")}; +} diff --git a/Analysis/Tasks/PWGDQ/tableMaker_pp.cxx b/Analysis/Tasks/PWGDQ/tableMaker_pp.cxx new file mode 100644 index 0000000000000..6f4603193b33a --- /dev/null +++ b/Analysis/Tasks/PWGDQ/tableMaker_pp.cxx @@ -0,0 +1,258 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/TriggerAliases.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include <iostream> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +//using namespace o2::framework::expressions; +using namespace o2::aod; + +using MyEvents = soa::Join<aod::Collisions, aod::EvSels>; +using MyBarrelTracks = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov, aod::TracksExtended, aod::TrackSelection, aod::pidRespTPC, aod::pidRespTOF, aod::pidRespTOFbeta>; + +// HACK: In order to be able to deduce which kind of aod object is transmitted to the templated VarManager::Fill functions +// a constexpr static bit map must be defined and sent as template argument +// The user has to include in this bit map all the tables needed in analysis, as defined in VarManager::ObjTypes +// Additionally, one should make sure that the requested tables are actually provided in the process() function, +// otherwise a compile time error will be thrown. +// This is a temporary fix until the arrow/ROOT issues are solved, at which point it will be possible +// to automatically detect the object types transmitted to the VarManager +constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision; +constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID; + +struct TableMaker_pp { + + Produces<ReducedEvents> event; + Produces<ReducedEventsExtended> eventExtended; + Produces<ReducedEventsVtxCov> eventVtxCov; + Produces<ReducedTracks> trackBasic; + Produces<ReducedTracksBarrel> trackBarrel; + Produces<ReducedTracksBarrelCov> trackBarrelCov; + Produces<ReducedTracksBarrelPID> trackBarrelPID; + Produces<ReducedMuons> muonBasic; + Produces<ReducedMuonsExtended> muonExtended; + + float* fValues; + + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + + // TODO: Filters should be used to make lowest level selection. The additional more restrictive cuts should be defined via the AnalysisCuts + // TODO: Multiple event selections can be applied and decisions stored in the reducedevent::tag + AnalysisCompositeCut* fEventCut; + // TODO: Multiple track selections can be applied and decisions stored in the reducedtrack::filteringFlags + // Cuts should be defined using Configurables (prepare cut libraries, as discussed in O2 DQ meetings) + AnalysisCompositeCut* fTrackCut; + + // Partition will select fast a group of tracks with basic requirements + // If some of the cuts cannot be included in the Partition expression, add them via AnalysisCut(s) + Partition<MyBarrelTracks> barrelSelectedTracks = o2::aod::track::pt >= 1.0f && nabs(o2::aod::track::eta) <= 0.9f && o2::aod::track::tpcSignal >= 70.0f && o2::aod::track::tpcSignal <= 100.0f && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; + + // TODO a few of the important muon variables in the central data model are dynamic columns so not usable in expressions (e.g. eta, phi) + // Update the data model to have them as expression columns + Partition<aod::Muons> muonSelectedTracks = o2::aod::muon::pt >= 0.5f; // For pp collisions a 0.5 GeV/c pp cuts is defined + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms("Event_BeforeCuts;Event_AfterCuts;TrackBarrel_BeforeCuts;TrackBarrel_AfterCuts"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + DefineCuts(); + } + + void DefineCuts() + { + fEventCut = new AnalysisCompositeCut(true); + AnalysisCut* eventVarCut = new AnalysisCut(); + eventVarCut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + eventVarCut->AddCut(VarManager::kIsINT7, 0.5, 1.5); // require kINT7 + fEventCut->AddCut(eventVarCut); + + fTrackCut = new AnalysisCompositeCut(true); + AnalysisCut* trackVarCut = new AnalysisCut(); + //trackVarCut->AddCut(VarManager::kPt, 1.0, 1000.0); + //trackVarCut->AddCut(VarManager::kEta, -0.9, 0.9); + //trackVarCut->AddCut(VarManager::kTPCsignal, 70.0, 100.0); + trackVarCut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); + trackVarCut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); + //trackVarCut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + //trackVarCut->AddCut(VarManager::kITSchi2, 0.1, 36.0); + trackVarCut->AddCut(VarManager::kTPCncls, 100.0, 161.); + + AnalysisCut* pidCut1 = new AnalysisCut(); + TF1* cutLow1 = new TF1("cutLow1", "pol1", 0., 10.); + cutLow1->SetParameters(130., -40.0); + pidCut1->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + + fTrackCut->AddCut(trackVarCut); + fTrackCut->AddCut(pidCut1); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& collision, aod::MuonClusters const& clustersMuon, aod::Muons const& tracksMuon, aod::BCs const& bcs, MyBarrelTracks const& tracksBarrel) + { + uint64_t tag = 0; + uint32_t triggerAliases = 0; + for (int i = 0; i < kNaliases; i++) { + if (collision.alias()[i] > 0) { + triggerAliases |= (uint32_t(1) << i); + } + } + + VarManager::ResetValues(0, VarManager::kNEventWiseVariables, fValues); + VarManager::FillEvent<gkEventFillMap>(collision, fValues); // extract event information and place it in the fgValues array + fHistMan->FillHistClass("Event_BeforeCuts", fValues); // automatically fill all the histograms in the class Event + + if (!fEventCut->IsSelected(fValues)) { + return; + } + + fHistMan->FillHistClass("Event_AfterCuts", fValues); + + event(tag, collision.bc().runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib()); + eventExtended(collision.bc().globalBC(), collision.bc().triggerMask(), triggerAliases, 0.0f); + eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + + uint64_t trackFilteringTag = 0; + trackBasic.reserve(barrelSelectedTracks.size()); + trackBarrel.reserve(barrelSelectedTracks.size()); + trackBarrelCov.reserve(barrelSelectedTracks.size()); + trackBarrelPID.reserve(barrelSelectedTracks.size()); + + for (auto& track : barrelSelectedTracks) { + VarManager::FillTrack<gkTrackFillMap>(track, fValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", fValues); + if (!fTrackCut->IsSelected(fValues)) { + continue; + } + fHistMan->FillHistClass("TrackBarrel_AfterCuts", fValues); + + if (track.isGlobalTrack()) { + trackFilteringTag |= (uint64_t(1) << 0); + } + if (track.isGlobalTrackSDD()) { + trackFilteringTag |= (uint64_t(1) << 1); + } + trackBasic(event.lastIndex(), track.globalIndex(), trackFilteringTag, track.pt(), track.eta(), track.phi(), track.charge()); + trackBarrel(track.tpcInnerParam(), track.flags(), track.itsClusterMap(), track.itsChi2NCl(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), + track.tpcNClsShared(), track.tpcChi2NCl(), + track.trdChi2(), track.tofChi2(), + track.length(), track.dcaXY(), track.dcaZ()); + trackBarrelCov(track.cYY(), track.cZZ(), track.cSnpSnp(), track.cTglTgl(), track.c1Pt21Pt2()); + trackBarrelPID(track.tpcSignal(), + track.tpcNSigmaEl(), track.tpcNSigmaMu(), + track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), + track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl(), + track.tofSignal(), track.beta(), + track.tofNSigmaEl(), track.tofNSigmaMu(), + track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), + track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe(), track.tofNSigmaAl(), + track.trdSignal()); + } + + muonBasic.reserve(muonSelectedTracks.size()); + muonExtended.reserve(muonSelectedTracks.size()); + for (auto& muon : muonSelectedTracks) { + // TODO: add proper information for muon tracks + if (muon.bcId() != collision.bcId()) { + continue; + } + // TODO: the trackFilteringTag will not be needed to encode whether the track is a muon since there is a dedicated table for muons + trackFilteringTag |= (uint64_t(1) << 0); // this is a MUON arm track + muonBasic(event.lastIndex(), trackFilteringTag, muon.pt(), muon.eta(), muon.phi(), muon.charge()); + muonExtended(muon.inverseBendingMomentum(), muon.thetaX(), muon.thetaY(), muon.zMu(), muon.bendingCoor(), muon.nonBendingCoor(), muon.chi2(), muon.chi2MatchTrigger()); + } + } + + void DefineHistograms(TString histClasses) + { + const int kNRuns = 2; + int runs[kNRuns] = {244918, 244919}; + TString runsStr; + for (int i = 0; i < kNRuns; i++) { + runsStr += Form("%d;", runs[i]); + } + VarManager::SetRunNumbers(kNRuns, runs); + + std::unique_ptr<TObjArray> arr(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < arr->GetEntries(); ++iclass) { + TString classStr = arr->At(iclass)->GetName(); + + if (classStr.Contains("Event")) { + fHistMan->AddHistClass(classStr.Data()); + fHistMan->AddHistogram(classStr.Data(), "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxZ_Run", "Vtx Z", true, + kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, runsStr.Data()); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxX_VtxY", "Vtx X vs Vtx Y", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY); // TH2F histogram + fHistMan->AddHistogram(classStr.Data(), "VtxX_VtxY_VtxZ", "vtx x - y - z", false, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 60, -15.0, 15.0, VarManager::kVtxZ); // TH3F histogram + fHistMan->AddHistogram(classStr.Data(), "NContrib_vs_VtxZ_prof", "Vtx Z vs ncontrib", true, 30, -15.0, 15.0, VarManager::kVtxZ, 10, -1., 1., VarManager::kVtxNcontrib); // TProfile histogram + fHistMan->AddHistogram(classStr.Data(), "VtxZ_vs_VtxX_VtxY_prof", "Vtx Z vs (x,y)", true, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 10, -1., 1., VarManager::kVtxZ); // TProfile2D histogram + fHistMan->AddHistogram(classStr.Data(), "Ncontrib_vs_VtxZ_VtxX_VtxY_prof", "n-contrib vs (x,y,z)", true, + 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 30, -15., 15., VarManager::kVtxZ, + "", "", "", VarManager::kVtxNcontrib); // TProfile3D + } + + if (classStr.Contains("Track")) { + fHistMan->AddHistClass(classStr.Data()); + fHistMan->AddHistogram(classStr.Data(), "Pt", "p_{T} distribution", false, 200, 0.0, 20.0, VarManager::kPt); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "Eta", "#eta distribution", false, 500, -5.0, 5.0, VarManager::kEta); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "Phi_Eta", "#phi vs #eta distribution", false, 200, -5.0, 5.0, VarManager::kEta, 200, -6.3, 6.3, VarManager::kPhi); // TH2F histogram + + if (classStr.Contains("Barrel")) { + fHistMan->AddHistogram(classStr.Data(), "TPCncls", "Number of cluster in TPC", false, 160, -0.5, 159.5, VarManager::kTPCncls); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "TPCncls_Run", "Number of cluster in TPC", true, kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, + 10, -0.5, 159.5, VarManager::kTPCncls, 10, 0., 1., VarManager::kNothing, runsStr.Data()); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "ITSncls", "Number of cluster in ITS", false, 8, -0.5, 7.5, VarManager::kITSncls); // TH1F histogram + //for TPC PID + fHistMan->AddHistogram(classStr.Data(), "TPCdedx_pIN", "TPC dE/dx vs pIN", false, 200, 0.0, 20.0, VarManager::kPin, 200, 0.0, 200., VarManager::kTPCsignal); // TH2F histogram + fHistMan->AddHistogram(classStr.Data(), "DCAxy", "DCAxy", false, 100, -3.0, 3.0, VarManager::kTrackDCAxy); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "DCAz", "DCAz", false, 100, -5.0, 5.0, VarManager::kTrackDCAz); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "IsGlobalTrack", "IsGlobalTrack", false, 2, -0.5, 1.5, VarManager::kIsGlobalTrack); // TH1F histogram + fHistMan->AddHistogram(classStr.Data(), "IsGlobalTrackSDD", "IsGlobalTrackSDD", false, 2, -0.5, 1.5, VarManager::kIsGlobalTrackSDD); // TH1F histogram + } + } + } // end loop over histogram classes + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<TableMaker_pp>("table-maker-pp")}; +} diff --git a/Analysis/Tasks/PWGDQ/tableReader.cxx b/Analysis/Tasks/PWGDQ/tableReader.cxx new file mode 100644 index 0000000000000..e1123d058ed2e --- /dev/null +++ b/Analysis/Tasks/PWGDQ/tableReader.cxx @@ -0,0 +1,616 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/ReducedInfoTables.h" +#include "PWGDQCore/VarManager.h" +#include "PWGDQCore/HistogramManager.h" +#include "PWGDQCore/AnalysisCut.h" +#include "PWGDQCore/AnalysisCompositeCut.h" +#include <TH1F.h> +#include <TMath.h> +#include <THashList.h> +#include <TString.h> +#include <iostream> +#include <vector> + +using std::cout; +using std::endl; + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod; + +// Some definitions +namespace o2::aod +{ + +namespace reducedevent +{ +DECLARE_SOA_COLUMN(Category, category, int); +DECLARE_SOA_COLUMN(IsEventSelected, isEventSelected, int); +} // namespace reducedevent + +namespace reducedtrack +{ +DECLARE_SOA_COLUMN(IsBarrelSelected, isBarrelSelected, uint8_t); +DECLARE_SOA_COLUMN(IsMuonSelected, isMuonSelected, int); +} // namespace reducedtrack + +namespace reducedpair +{ +DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Charge, charge, int); +DECLARE_SOA_COLUMN(FilterMap, filterMap, uint8_t); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return pt * std::cos(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return pt * std::sin(phi); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return pt * std::sinh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(Pmom, pmom, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); +} // namespace reducedpair + +DECLARE_SOA_TABLE(EventCuts, "AOD", "EVENTCUTS", reducedevent::IsEventSelected); +DECLARE_SOA_TABLE(EventCategories, "AOD", "EVENTCATEGORIES", reducedevent::Category); +DECLARE_SOA_TABLE(BarrelTrackCuts, "AOD", "BARRELTRACKCUTS", reducedtrack::IsBarrelSelected); +DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "MUONTRACKCUTS", reducedtrack::IsMuonSelected); +DECLARE_SOA_TABLE(Dileptons, "AOD", "DILEPTON", reducedpair::ReducedEventId, reducedpair::Mass, reducedpair::Pt, reducedpair::Eta, reducedpair::Phi, reducedpair::Charge, reducedpair::FilterMap, + reducedpair::Px<reducedpair::Pt, reducedpair::Phi>, reducedpair::Py<reducedpair::Pt, reducedpair::Phi>, + reducedpair::Pz<reducedpair::Pt, reducedpair::Eta>, reducedpair::Pmom<reducedpair::Pt, reducedpair::Eta>); +using Dilepton = Dileptons::iterator; +} // namespace o2::aod + +using MyEvents = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended>; +using MyEventsSelected = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::EventCuts>; +using MyEventsVtxCov = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::ReducedEventsVtxCov>; +using MyEventsVtxCovSelected = soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::ReducedEventsVtxCov, aod::EventCuts>; +using MyBarrelTracks = soa::Join<aod::ReducedTracks, aod::ReducedTracksBarrel, aod::ReducedTracksBarrelCov, aod::ReducedTracksBarrelPID>; +using MyBarrelTracksSelected = soa::Join<aod::ReducedTracks, aod::ReducedTracksBarrel, aod::ReducedTracksBarrelCov, aod::ReducedTracksBarrelPID, aod::BarrelTrackCuts>; +using MyMuonTracks = soa::Join<aod::ReducedMuons, aod::ReducedMuonsExtended>; +using MyMuonTracksSelected = soa::Join<aod::ReducedMuons, aod::ReducedMuonsExtended, aod::MuonTrackCuts>; + +void DefineHistograms(HistogramManager* histMan, TString histClasses); + +// HACK: In order to be able to deduce which kind of aod object is transmitted to the templated VarManager::Fill functions +// a constexpr static bit map must be defined and sent as template argument +// The user has to include in this bit map all the tables needed in analysis, as defined in VarManager::ObjTypes +// Additionally, one should make sure that the requested tables are actually provided in the process() function, +// otherwise a compile time error will be thrown. +// This is a temporary fix until the arrow/ROOT issues are solved, at which point it will be possible +// to automatically detect the object types transmitted to the VarManager +constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended; +constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; +constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackMuon; + +int gNTrackCuts = 2; + +struct EventSelection { + Produces<aod::EventCuts> eventSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut* fEventCut; + + float* fValues; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + DefineCuts(); + } + + void DefineCuts() + { + fEventCut = new AnalysisCompositeCut(true); + + AnalysisCut* varCut = new AnalysisCut(); + varCut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + varCut->AddCut(VarManager::kIsINT7, 0.5, 1.5); + varCut->AddCut(VarManager::kCentVZERO, 0.0, 10.0); + + fEventCut->AddCut(varCut); + // TODO: Add more cuts, also enable cuts which are not easily possible via the VarManager (e.g. trigger selections) + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEvents::iterator const& event) + { + // Reset the fValues array + VarManager::ResetValues(0, VarManager::kNEventWiseVariables, fValues); + + VarManager::FillEvent<gkEventFillMap>(event, fValues); + fHistMan->FillHistClass("Event_BeforeCuts", fValues); // automatically fill all the histograms in the class Event + if (fEventCut->IsSelected(fValues)) { + fHistMan->FillHistClass("Event_AfterCuts", fValues); + eventSel(1); + } else { + eventSel(0); + } + } +}; + +struct BarrelTrackSelection { + Produces<aod::BarrelTrackCuts> trackSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + std::vector<AnalysisCompositeCut> fTrackCuts; + + float* fValues; // array to be used by the VarManager + + void init(o2::framework::InitContext&) + { + DefineCuts(); + + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + TString cutNames = "TrackBarrel_BeforeCuts;"; + for (int i = 0; i < gNTrackCuts; i++) { + cutNames += Form("TrackBarrel_%s;", fTrackCuts[i].GetName()); + } + + DefineHistograms(fHistMan, cutNames.Data()); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + } + + void DefineCuts() + { + AnalysisCut* commonCuts = new AnalysisCut(); + commonCuts->AddCut(VarManager::kPt, 1.0, 20.0); + commonCuts->AddCut(VarManager::kTPCsignal, 70.0, 100.0); + commonCuts->AddCut(VarManager::kEta, -0.9, 0.9); + commonCuts->AddCut(VarManager::kIsSPDany, 0.5, 1.5); + commonCuts->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); + commonCuts->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); + commonCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + commonCuts->AddCut(VarManager::kITSchi2, 0.1, 36.0); + commonCuts->AddCut(VarManager::kTPCncls, 100.0, 161.); + commonCuts->AddCut(VarManager::kTrackDCAxy, -1.0, 1.0); + commonCuts->AddCut(VarManager::kTrackDCAz, -3.0, 3.0); + + AnalysisCut* pidCut1 = new AnalysisCut(); + TF1* cutLow1 = new TF1("cutLow1", "pol1", 0., 10.); + cutLow1->SetParameters(130., -40.0); + pidCut1->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + + AnalysisCut* pidCut2 = new AnalysisCut(); + pidCut2->AddCut(VarManager::kTPCsignal, 73.0, 100.0); + + AnalysisCompositeCut trackCut1("cut1", "cut1", true); // true: use AND + trackCut1.AddCut(commonCuts); + trackCut1.AddCut(pidCut1); + + AnalysisCompositeCut trackCut2("cut2", "cut2", true); // true: use AND + trackCut2.AddCut(commonCuts); + trackCut2.AddCut(pidCut1); + trackCut2.AddCut(pidCut2); + + fTrackCuts.push_back(trackCut1); + fTrackCuts.push_back(trackCut2); + + //gNTrackCuts = fTrackCuts.size(); + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEventsSelected::iterator const& event, MyBarrelTracks const& tracks) + { + VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables, fValues); + // fill event information which might be needed in histograms that combine track and event properties + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + uint8_t filterMap = uint8_t(0); + + trackSel.reserve(tracks.size()); + + for (auto& track : tracks) { + filterMap = uint8_t(0); + VarManager::FillTrack<gkTrackFillMap>(track, fValues); + if (event.isEventSelected()) { + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", fValues); + } + + int i = 0; + for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); ++cut, ++i) { + if ((*cut).IsSelected(fValues)) { + filterMap |= (uint8_t(1) << i); + fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), fValues); + } + } + trackSel(filterMap); + } + } +}; + +struct MuonTrackSelection { + Produces<aod::MuonTrackCuts> trackSel; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut* fTrackCut; + + float* fValues; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "TrackMuon_BeforeCuts;TrackMuon_AfterCuts;"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + DefineCuts(); + } + + void DefineCuts() + { + fTrackCut = new AnalysisCompositeCut(true); + AnalysisCut kineMuonCut; + kineMuonCut.AddCut(VarManager::kPt, 1.5, 10.0); + fTrackCut->AddCut(&kineMuonCut); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(MyEventsSelected::iterator const& event, MyMuonTracks const& muons) + { + VarManager::ResetValues(0, VarManager::kNMuonTrackVariables, fValues); + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + for (auto& muon : muons) { + //VarManager::ResetValues(VarManager::kNBarrelTrackVariables, VarManager::kNMuonTrackVariables, fValues); + VarManager::FillTrack<gkMuonFillMap>(muon, fValues); + //if(event.isEventSelected()) + fHistMan->FillHistClass("TrackMuon_BeforeCuts", fValues); + + if (fTrackCut->IsSelected(fValues)) { + trackSel(1); + //if(event.isEventSelected()) + fHistMan->FillHistClass("TrackMuon_AfterCuts", fValues); + } else { + trackSel(0); + } + } + } +}; + +struct TableReader { + Produces<aod::Dileptons> dileptonList; + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + //NOTE: one could define also a dilepton cut, but for now basic selections can be supported using Partition + + float* fValues; + + Partition<MyBarrelTracksSelected> posTracks = aod::reducedtrack::charge > 0 && aod::reducedtrack::isBarrelSelected > uint8_t(0); + Partition<MyBarrelTracksSelected> negTracks = aod::reducedtrack::charge < 0 && aod::reducedtrack::isBarrelSelected > uint8_t(0); + Partition<MyMuonTracksSelected> posMuons = aod::reducedtrack::charge > 0 && aod::reducedtrack::isMuonSelected == 1; + Partition<MyMuonTracksSelected> negMuons = aod::reducedtrack::charge < 0 && aod::reducedtrack::isMuonSelected == 1; + + void init(o2::framework::InitContext&) + { + fValues = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + TString histNames = ""; + for (int i = 0; i < gNTrackCuts; i++) { + histNames += Form("PairsBarrelPM_cut%d;PairsBarrelPP_cut%d;PairsBarrelMM_cut%d;", i + 1, i + 1, i + 1); + } + + DefineHistograms(fHistMan, histNames.Data()); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + } + + //void process(soa::Filtered<MyEventsVtxCovSelected>::iterator const& event, MyBarrelTracksSelected const& tracks, MyMuonTracksSelected const& muons) + void process(MyEventsVtxCovSelected::iterator const& event, MyBarrelTracksSelected const& tracks, MyMuonTracksSelected const& muons) + { + if (!event.isEventSelected()) { + return; + } + // Reset the fValues array + VarManager::ResetValues(0, VarManager::kNVars, fValues); + + VarManager::FillEvent<gkEventFillMap>(event, fValues); + + // Run the same event pairing for barrel tracks + // TODO: Use combinations() when this will work for Partitions + /* e.g. + * for (auto& [tpos, tneg] : combinations(posTracks, negTracks)) { + VarManager::FillPair(tpos, tneg); + fHistMan->FillHistClass("PairsBarrelPM", VarManager::fgValues); + } + */ + uint8_t filter = 0; + for (auto tpos : posTracks) { + for (auto tneg : negTracks) { // +- pairs + filter = tpos.isBarrelSelected() & tneg.isBarrelSelected(); + if (!filter) { // the tracks must have at least one filter bit in common to continue + continue; + } + VarManager::FillPair(tpos, tneg, fValues); + dileptonList(event, fValues[VarManager::kMass], fValues[VarManager::kPt], fValues[VarManager::kEta], fValues[VarManager::kPhi], 0, filter); + for (int i = 0; i < gNTrackCuts; ++i) { + if (filter & (uint8_t(1) << i)) { + fHistMan->FillHistClass(Form("PairsBarrelPM_cut%d", i + 1), fValues); + } + } + } + for (auto tpos2 = tpos + 1; tpos2 != posTracks.end(); ++tpos2) { // ++ pairs + filter = tpos.isBarrelSelected() & tpos2.isBarrelSelected(); + if (!filter) { // the tracks must have at least one filter bit in common to continue + continue; + } + VarManager::FillPair(tpos, tpos2, fValues); + dileptonList(event, fValues[VarManager::kMass], fValues[VarManager::kPt], fValues[VarManager::kEta], fValues[VarManager::kPhi], 2, filter); + for (int i = 0; i < gNTrackCuts; ++i) { + if (filter & (uint8_t(1) << i)) { + fHistMan->FillHistClass(Form("PairsBarrelPP_cut%d", i + 1), fValues); + } + } + } + } + for (auto tneg : negTracks) { // -- pairs + for (auto tneg2 = tneg + 1; tneg2 != negTracks.end(); ++tneg2) { + filter = tneg.isBarrelSelected() & tneg2.isBarrelSelected(); + if (!filter) { // the tracks must have at least one filter bit in common to continue + continue; + } + VarManager::FillPair(tneg, tneg2, fValues); + dileptonList(event, fValues[VarManager::kMass], fValues[VarManager::kPt], fValues[VarManager::kEta], fValues[VarManager::kPhi], -2, filter); + for (int i = 0; i < gNTrackCuts; ++i) { + if (filter & (uint8_t(1) << i)) { + fHistMan->FillHistClass(Form("PairsBarrelMM_cut%d", i + 1), fValues); + } + } + } + } + + // same event pairing for muons + for (auto& tpos : posMuons) { + for (auto& tneg : negMuons) { + //dileptonList(event, VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], 1); + VarManager::FillPair(tpos, tneg, fValues); + fHistMan->FillHistClass("PairsMuon", fValues); + } + } + } +}; + +struct DileptonHadronAnalysis { + // + // This task combines dilepton candidates with a track and could be used for example + // in analyses with the dilepton as one of the decay products of a higher mass resonance (e.g. B0 -> Jpsi + K) + // or in dilepton + hadron correlations, etc. + // It requires the TableReader task to be in the workflow and produce the dilepton table + // + OutputObj<THashList> fOutputList{"output"}; + HistogramManager* fHistMan; + AnalysisCompositeCut* fHadronCut; // TODO: this cut will be moved into the barrel/muon track selection task + //NOTE: no cut has been included for dileptons because that can be controlled via the TableReader task and the partition below + + // use two values array to avoid mixing up the quantities + float* fValuesDilepton; + float* fValuesHadron; + + Filter eventFilter = aod::reducedevent::isEventSelected == 1; + + Partition<aod::Dileptons> selDileptons = aod::reducedpair::charge == 0 && aod::reducedpair::mass > 2.92f && aod::reducedpair::mass<3.16f && aod::reducedpair::pt> 5.0f; + + constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; + + void init(o2::framework::InitContext&) + { + fValuesDilepton = new float[VarManager::kNVars]; + fValuesHadron = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + DefineHistograms(fHistMan, "DileptonsSelected;HadronsSelected;DileptonHadronInvMass;DileptonHadronCorrelation"); // define all histograms + VarManager::SetUseVars(fHistMan->GetUsedVars()); + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + DefineCuts(); + } + + void DefineCuts() + { + fHadronCut = new AnalysisCompositeCut(true); // true: use AND + AnalysisCut* cut1 = new AnalysisCut(); + cut1->AddCut(VarManager::kPt, 4.0, 20.0); + cut1->AddCut(VarManager::kEta, -0.9, 0.9); + cut1->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + cut1->AddCut(VarManager::kITSchi2, 0.1, 36.0); + cut1->AddCut(VarManager::kITSncls, 2.5, 7.5); + cut1->AddCut(VarManager::kTPCncls, 69.5, 159.5); + fHadronCut->AddCut(cut1); + + VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill + } + + void process(soa::Filtered<MyEventsVtxCovSelected>::iterator const& event, MyBarrelTracks const& hadrons, aod::Dileptons const& dileptons) + { + VarManager::ResetValues(0, VarManager::kNVars, fValuesHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDilepton); + // fill event information which might be needed in histograms that combine track/pair and event properties + VarManager::FillEvent<gkEventFillMap>(event, fValuesHadron); + VarManager::FillEvent<gkEventFillMap>(event, fValuesDilepton); // TODO: check if needed (just for dilepton QA which might be depending on event wise variables) + + // loop once over dileptons for QA purposes + for (auto dilepton : selDileptons) { + VarManager::FillTrack<fgDileptonFillMap>(dilepton, fValuesDilepton); + fHistMan->FillHistClass("DileptonsSelected", fValuesDilepton); + } + + // loop over hadrons + for (auto& hadron : hadrons) { + VarManager::FillTrack<gkTrackFillMap>(hadron, fValuesHadron); + if (!fHadronCut->IsSelected(fValuesHadron)) { // TODO: this will be moved to a partition when the selection will be done in the barrel/muon track selection + continue; + } + + fHistMan->FillHistClass("HadronsSelected", fValuesHadron); + + for (auto dilepton : selDileptons) { + // TODO: At the moment there is no check on whether this hadron is one of the dilepton daughters! + VarManager::FillDileptonHadron(dilepton, hadron, fValuesHadron); + fHistMan->FillHistClass("DileptonHadronInvMass", fValuesHadron); + fHistMan->FillHistClass("DileptonHadronCorrelation", fValuesHadron); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<EventSelection>("my-event-selection"), + adaptAnalysisTask<BarrelTrackSelection>("barrel-track-selection"), + adaptAnalysisTask<MuonTrackSelection>("muon-track-selection"), + adaptAnalysisTask<TableReader>("table-reader"), + adaptAnalysisTask<DileptonHadronAnalysis>("dilepton-hadron") + + }; +} + +void DefineHistograms(HistogramManager* histMan, TString histClasses) +{ + // + // Define here the histograms for all the classes required in analysis. + // The histogram classes are provided in the histClasses string, separated by semicolon ";" + // The histogram classes and their components histograms are defined below depending on the name of the histogram class + // + const int kNRuns = 135; + int runs[kNRuns] = { + 244917, 244918, 244975, 244980, 244982, 244983, 245064, 245066, 245068, 245145, + 245146, 245151, 245152, 245231, 245232, 245259, 245343, 245345, 245346, 245347, + 245349, 245353, 245396, 245397, 245401, 245407, 245409, 245411, 245439, 245441, + 245446, 245450, 245452, 245454, 245496, 245497, 245501, 245504, 245505, 245507, + 245535, 245540, 245542, 245543, 245544, 245545, 245554, 245683, 245692, 245700, + 245702, 245705, 245829, 245831, 245833, 245923, 245949, 245952, 245954, 245963, + 246001, 246003, 246012, 246036, 246037, 246042, 246048, 246049, 246052, 246053, + 246087, 246089, 246113, 246115, 246148, 246151, 246152, 246153, 246178, 246180, + 246181, 246182, 246185, 246217, 246222, 246225, 246271, 246272, 246275, 246276, + 246391, 246392, 246424, 246428, 246431, 246434, 246487, 246488, 246493, 246495, + 246675, 246676, 246750, 246751, 246757, 246758, 246759, 246760, 246763, 246765, + 246766, 246804, 246805, 246807, 246808, 246809, 246810, 246844, 246845, 246846, + 246847, 246851, 246865, 246867, 246870, 246871, 246928, 246945, 246948, 246980, + 246982, 246984, 246989, 246991, 246994}; + TString runsStr; + for (int i = 0; i < kNRuns; i++) { + runsStr += Form("%d;", runs[i]); + } + VarManager::SetRunNumbers(kNRuns, runs); + + std::unique_ptr<TObjArray> arr(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < arr->GetEntries(); ++iclass) { + TString classStr = arr->At(iclass)->GetName(); + + if (classStr.Contains("Event")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "CentV0M_vtxZ", "CentV0M vs Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ, 20, 0., 100., VarManager::kCentVZERO); // TH2F histogram + continue; + } // end if(Event) + + if (classStr.Contains("Track")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Pt", "p_{T} distribution", false, 200, 0.0, 20.0, VarManager::kPt); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Eta", "#eta distribution", false, 500, -5.0, 5.0, VarManager::kEta); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "Phi_Eta", "#phi vs #eta distribution", false, 200, -5.0, 5.0, VarManager::kEta, 200, -6.3, 6.3, VarManager::kPhi); // TH2F histogram + + if (classStr.Contains("Barrel")) { + histMan->AddHistogram(classStr.Data(), "TPCncls", "Number of cluster in TPC", false, 160, -0.5, 159.5, VarManager::kTPCncls); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "TPCncls_Run", "Number of cluster in TPC", true, kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, + 10, -0.5, 159.5, VarManager::kTPCncls, 10, 0., 1., VarManager::kNothing, runsStr.Data()); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "ITSncls", "Number of cluster in ITS", false, 8, -0.5, 7.5, VarManager::kITSncls); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "ITSchi2", "ITS chi2", false, 100, 0.0, 50.0, VarManager::kITSchi2); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "IsITSrefit", "", false, 2, -0.5, 1.5, VarManager::kIsITSrefit); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "IsTPCrefit", "", false, 2, -0.5, 1.5, VarManager::kIsTPCrefit); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "IsSPDany", "", false, 2, -0.5, 1.5, VarManager::kIsSPDany); // TH1F histogram + //for TPC PID + histMan->AddHistogram(classStr.Data(), "TPCdedx_pIN", "TPC dE/dx vs pIN", false, 200, 0.0, 20.0, VarManager::kPin, 200, 0.0, 200., VarManager::kTPCsignal); // TH2F histogram + histMan->AddHistogram(classStr.Data(), "TPCchi2", "TPC chi2", false, 100, 0.0, 10.0, VarManager::kTPCchi2); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "DCAxy", "DCAxy", false, 100, -3.0, 3.0, VarManager::kTrackDCAxy); // TH1F histogram + histMan->AddHistogram(classStr.Data(), "DCAz", "DCAz", false, 100, -5.0, 5.0, VarManager::kTrackDCAz); // TH1F histogram + } + + if (classStr.Contains("Muon")) { + histMan->AddHistogram(classStr.Data(), "InvBendingMom", "", false, 100, 0.0, 1.0, VarManager::kMuonInvBendingMomentum); + histMan->AddHistogram(classStr.Data(), "ThetaX", "", false, 100, -1.0, 1.0, VarManager::kMuonThetaX); + histMan->AddHistogram(classStr.Data(), "ThetaY", "", false, 100, -2.0, 2.0, VarManager::kMuonThetaY); + histMan->AddHistogram(classStr.Data(), "ZMu", "", false, 100, -30.0, 30.0, VarManager::kMuonZMu); + histMan->AddHistogram(classStr.Data(), "BendingCoor", "", false, 100, 0.32, 0.35, VarManager::kMuonBendingCoor); + histMan->AddHistogram(classStr.Data(), "NonBendingCoor", "", false, 100, 0.065, 0.07, VarManager::kMuonNonBendingCoor); + histMan->AddHistogram(classStr.Data(), "Chi2", "", false, 100, 0.0, 200.0, VarManager::kMuonChi2); + histMan->AddHistogram(classStr.Data(), "Chi2MatchTrigger", "", false, 100, 0.0, 20.0, VarManager::kMuonChi2MatchTrigger); + } + } + + if (classStr.Contains("DileptonsSelected")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Mass_Pt", "", false, 100, 0.0, 5.0, VarManager::kMass, 100, 0.0, 20.0, VarManager::kPt); + } + + if (classStr.Contains("HadronsSelected")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Eta_Pt", "", false, 20, -1.0, 1.0, VarManager::kEta, 100, 0.0, 20.0, VarManager::kPt); + histMan->AddHistogram(classStr.Data(), "Eta_Phi", "", false, 20, -1.0, 1.0, VarManager::kEta, 100, -8.0, 8.0, VarManager::kPhi); + } + + if (classStr.Contains("Pairs")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Mass_Pt_Cent", "", false, 125, 0.0, 5.0, VarManager::kMass, 20, 0.0, 20.0, VarManager::kPt, 10, 0.0, 100.0, VarManager::kCentVZERO); + histMan->AddHistogram(classStr.Data(), "Mass_Pt", "", false, 125, 0.0, 5.0, VarManager::kMass, 100, 0.0, 20.0, VarManager::kPt); + histMan->AddHistogram(classStr.Data(), "Mass", "", false, 125, 0.0, 5.0, VarManager::kMass); + histMan->AddHistogram(classStr.Data(), "Mass_Run", "", true, kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 10, 0.0, 5.0, VarManager::kMass, + 10, 0., 1., VarManager::kNothing, runsStr.Data()); + } + + if (classStr.Contains("DileptonHadronInvMass")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "Mass_Pt", "", false, 40, 0.0, 20.0, VarManager::kPairMass, 40, 0.0, 20.0, VarManager::kPairPt); + } + + if (classStr.Contains("DileptonHadronCorrelation")) { + histMan->AddHistClass(classStr.Data()); + histMan->AddHistogram(classStr.Data(), "DeltaEta_DeltaPhi", "", false, 20, -2.0, 2.0, VarManager::kDeltaEta, 50, -8.0, 8.0, VarManager::kDeltaPhi); + histMan->AddHistogram(classStr.Data(), "DeltaEta_DeltaPhiSym", "", false, 20, -2.0, 2.0, VarManager::kDeltaEta, 50, -8.0, 8.0, VarManager::kDeltaPhiSym); + } + } // end loop over histogram classes +} diff --git a/Analysis/Tasks/PWGHF/CMakeLists.txt b/Analysis/Tasks/PWGHF/CMakeLists.txt new file mode 100644 index 0000000000000..16e4180937401 --- /dev/null +++ b/Analysis/Tasks/PWGHF/CMakeLists.txt @@ -0,0 +1,54 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(qatask + SOURCES qaTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-track-index-skims-creator + SOURCES HFTrackIndexSkimsCreator.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing ROOT::EG + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-candidate-creator-2prong + SOURCES HFCandidateCreator2Prong.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing ROOT::EG + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-candidate-creator-3prong + SOURCES HFCandidateCreator3Prong.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing ROOT::EG + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-d0-candidate-selector + SOURCES HFD0CandidateSelector.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-lc-candidate-selector + SOURCES HFLcCandidateSelector.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-task-d0 + SOURCES taskD0.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-task-dplus + SOURCES taskDPlus.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(hf-task-lc + SOURCES taskLc.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/PWGHF/HFCandidateCreator2Prong.cxx b/Analysis/Tasks/PWGHF/HFCandidateCreator2Prong.cxx new file mode 100644 index 0000000000000..6f6863ac2e77e --- /dev/null +++ b/Analysis/Tasks/PWGHF/HFCandidateCreator2Prong.cxx @@ -0,0 +1,192 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 HFCandidateCreator2Prong.cxx +/// \brief Reconstruction of heavy-flavour 2-prong decay candidates +/// +/// \author Gian Michele Innocenti <gian.michele.innocenti@cern.ch>, CERN +/// \author Vít Kučera <vit.kucera@cern.ch>, CERN + +#include "Framework/AnalysisTask.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisCore/trackUtilities.h" +#include "ReconstructionDataFormats/DCA.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong2; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + ConfigParamSpec optionDoMC{"doMC", VariantType::Bool, false, {"Perform MC matching."}}; + workflowOptions.push_back(optionDoMC); +} + +#include "Framework/runDataProcessing.h" + +/// Reconstruction of heavy-flavour 2-prong decay candidates +struct HFCandidateCreator2Prong { + Produces<aod::HfCandProng2Base> rowCandidateBase; + + Configurable<double> magneticField{"d_bz", 5., "magnetic field"}; + Configurable<bool> b_propdca{"b_propdca", true, "create tracks version propagated to PCA"}; + Configurable<double> d_maxr{"d_maxr", 200., "reject PCA's above this radius"}; + Configurable<double> d_maxdzini{"d_maxdzini", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; + Configurable<double> d_minparamchange{"d_minparamchange", 1.e-3, "stop iterations if largest change of any X is smaller than this"}; + Configurable<double> d_minrelchi2change{"d_minrelchi2change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable<bool> b_dovalplots{"b_dovalplots", true, "do validation plots"}; + + OutputObj<TH1F> hmass2{TH1F("hmass2", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", 500, 0., 5.)}; + OutputObj<TH1F> hCovPVXX{TH1F("hCovPVXX", "2-prong candidates;XX element of cov. matrix of prim. vtx. position (cm^{2});entries", 100, 0., 1.e-4)}; + OutputObj<TH1F> hCovSVXX{TH1F("hCovSVXX", "2-prong candidates;XX element of cov. matrix of sec. vtx. position (cm^{2});entries", 100, 0., 0.2)}; + + double massPi = RecoDecay::getMassPDG(kPiPlus); + double massK = RecoDecay::getMassPDG(kKPlus); + double massPiK{0.}; + double massKPi{0.}; + + void process(aod::Collisions const& collisions, + aod::HfTrackIndexProng2 const& rowsTrackIndexProng2, + aod::BigTracks const& tracks) + { + // 2-prong vertex fitter + o2::vertexing::DCAFitterN<2> df; + df.setBz(magneticField); + df.setPropagateToPCA(b_propdca); + df.setMaxR(d_maxr); + df.setMaxDZIni(d_maxdzini); + df.setMinParamChange(d_minparamchange); + df.setMinRelChi2Change(d_minrelchi2change); + df.setUseAbsDCA(true); + + // loop over pairs of track indeces + for (const auto& rowTrackIndexProng2 : rowsTrackIndexProng2) { + auto trackParVarPos1 = getTrackParCov(rowTrackIndexProng2.index0()); + auto trackParVarNeg1 = getTrackParCov(rowTrackIndexProng2.index1()); + auto collision = rowTrackIndexProng2.index0().collision(); + + // reconstruct the 2-prong secondary vertex + if (df.process(trackParVarPos1, trackParVarNeg1) == 0) { + continue; + } + const auto& secondaryVertex = df.getPCACandidate(); + auto chi2PCA = df.getChi2AtPCACandidate(); + auto covMatrixPCA = df.calcPCACovMatrix().Array(); + hCovSVXX->Fill(covMatrixPCA[0]); // FIXME: Calculation of errorDecayLength(XY) gives wrong values without this line. + auto trackParVar0 = df.getTrack(0); + auto trackParVar1 = df.getTrack(1); + + // get track momenta + array<float, 3> pvec0; + array<float, 3> pvec1; + trackParVar0.getPxPyPzGlo(pvec0); + trackParVar1.getPxPyPzGlo(pvec1); + + // get track impact parameters + // This modifies track momenta! + auto primaryVertex = getPrimaryVertex(collision); + auto covMatrixPV = primaryVertex.getCov(); + hCovPVXX->Fill(covMatrixPV[0]); + o2::dataformats::DCA impactParameter0; + o2::dataformats::DCA impactParameter1; + trackParVar0.propagateToDCA(primaryVertex, magneticField, &impactParameter0); + trackParVar1.propagateToDCA(primaryVertex, magneticField, &impactParameter1); + + // get uncertainty of the decay length + double phi, theta; + getPointDirection(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex, phi, theta); + auto errorDecayLength = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); + auto errorDecayLengthXY = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); + + // fill candidate table rows + rowCandidateBase(collision.posX(), collision.posY(), collision.posZ(), + secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], + errorDecayLength, errorDecayLengthXY, + chi2PCA, + pvec0[0], pvec0[1], pvec0[2], + pvec1[0], pvec1[1], pvec1[2], + impactParameter0.getY(), impactParameter1.getY(), + std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()), + rowTrackIndexProng2.index0Id(), rowTrackIndexProng2.index1Id(), + rowTrackIndexProng2.hfflag()); + + // fill histograms + if (b_dovalplots) { + // calculate invariant masses + auto arrayMomenta = array{pvec0, pvec1}; + massPiK = RecoDecay::M(arrayMomenta, array{massPi, massK}); + massKPi = RecoDecay::M(arrayMomenta, array{massK, massPi}); + hmass2->Fill(massPiK); + hmass2->Fill(massKPi); + } + } + } +}; + +/// Extends the base table with expression columns. +struct HFCandidateCreator2ProngExpressions { + Spawns<aod::HfCandProng2Ext> rowCandidateProng2; + void init(InitContext const&) {} +}; + +/// Performs MC matching. +struct HFCandidateCreator2ProngMC { + Produces<aod::HfCandProng2MCRec> rowMCMatchRec; + Produces<aod::HfCandProng2MCGen> rowMCMatchGen; + + void process(aod::HfCandProng2 const& candidates, + aod::BigTracksMC const& tracks, + aod::McParticles const& particlesMC) + { + int8_t sign = 0; + int8_t result = 0; + + // Match reconstructed candidates. + for (auto& candidate : candidates) { + //Printf("New rec. candidate"); + result = 0; + + // D0(bar) → π± K∓ + //Printf("Checking D0(bar) → π± K∓"); + auto indexRecD0 = RecoDecay::getMatchedMCRec( + particlesMC, array{candidate.index0_as<aod::BigTracksMC>(), candidate.index1_as<aod::BigTracksMC>()}, + 421, array{+kPiPlus, -kKPlus}, true, &sign); + result += sign * D0ToPiK * int8_t(indexRecD0 > -1); + + rowMCMatchRec(result); + } + + // Match generated particles. + for (auto& particle : particlesMC) { + //Printf("New gen. candidate"); + result = 0; + + // D0(bar) → π± K∓ + //Printf("Checking D0(bar) → π± K∓"); + auto isMatchedGenD0 = RecoDecay::isMatchedMCGen(particlesMC, particle, 421, array{+kPiPlus, -kKPlus}, true, &sign); + result += sign * D0ToPiK * int8_t(isMatchedGenD0); + + rowMCMatchGen(result); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask<HFCandidateCreator2Prong>("hf-cand-creator-2prong"), + adaptAnalysisTask<HFCandidateCreator2ProngExpressions>("hf-cand-creator-2prong-expressions")}; + const bool doMC = cfgc.options().get<bool>("doMC"); + if (doMC) { + workflow.push_back(adaptAnalysisTask<HFCandidateCreator2ProngMC>("hf-cand-creator-2prong-mc")); + } + return workflow; +} diff --git a/Analysis/Tasks/PWGHF/HFCandidateCreator3Prong.cxx b/Analysis/Tasks/PWGHF/HFCandidateCreator3Prong.cxx new file mode 100644 index 0000000000000..3053da869fb9f --- /dev/null +++ b/Analysis/Tasks/PWGHF/HFCandidateCreator3Prong.cxx @@ -0,0 +1,205 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 HFCandidateCreator3Prong.cxx +/// \brief Reconstruction of heavy-flavour 3-prong decay candidates +/// \note Extended from HFCandidateCreator2Prong +/// +/// \author Vít Kučera <vit.kucera@cern.ch>, CERN + +#include "Framework/AnalysisTask.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisCore/trackUtilities.h" +#include "ReconstructionDataFormats/DCA.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong3; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + ConfigParamSpec optionDoMC{"doMC", VariantType::Bool, false, {"Perform MC matching."}}; + workflowOptions.push_back(optionDoMC); +} + +#include "Framework/runDataProcessing.h" + +/// Reconstruction of heavy-flavour 3-prong decay candidates +struct HFCandidateCreator3Prong { + Produces<aod::HfCandProng3Base> rowCandidateBase; + + Configurable<double> magneticField{"d_bz", 5., "magnetic field"}; + Configurable<bool> b_propdca{"b_propdca", true, "create tracks version propagated to PCA"}; + Configurable<double> d_maxr{"d_maxr", 200., "reject PCA's above this radius"}; + Configurable<double> d_maxdzini{"d_maxdzini", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; + Configurable<double> d_minparamchange{"d_minparamchange", 1.e-3, "stop iterations if largest change of any X is smaller than this"}; + Configurable<double> d_minrelchi2change{"d_minrelchi2change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable<bool> b_dovalplots{"b_dovalplots", true, "do validation plots"}; + + OutputObj<TH1F> hmass3{TH1F("hmass3", "3-prong candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", 500, 1.6, 2.1)}; + OutputObj<TH1F> hCovPVXX{TH1F("hCovPVXX", "3-prong candidates;XX element of cov. matrix of prim. vtx position (cm^{2});entries", 100, 0., 1.e-4)}; + OutputObj<TH1F> hCovSVXX{TH1F("hCovSVXX", "3-prong candidates;XX element of cov. matrix of sec. vtx position (cm^{2});entries", 100, 0., 0.2)}; + + double massPi = RecoDecay::getMassPDG(kPiPlus); + double massK = RecoDecay::getMassPDG(kKPlus); + double massPiKPi{0.}; + + void process(aod::Collisions const& collisions, + aod::HfTrackIndexProng3 const& rowsTrackIndexProng3, + aod::BigTracks const& tracks) + { + // 3-prong vertex fitter + o2::vertexing::DCAFitterN<3> df; + df.setBz(magneticField); + df.setPropagateToPCA(b_propdca); + df.setMaxR(d_maxr); + df.setMaxDZIni(d_maxdzini); + df.setMinParamChange(d_minparamchange); + df.setMinRelChi2Change(d_minrelchi2change); + df.setUseAbsDCA(true); + + // loop over pairs of track indeces + for (const auto& rowTrackIndexProng3 : rowsTrackIndexProng3) { + auto trackParVar0 = getTrackParCov(rowTrackIndexProng3.index0()); + auto trackParVar1 = getTrackParCov(rowTrackIndexProng3.index1()); + auto trackParVar2 = getTrackParCov(rowTrackIndexProng3.index2()); + auto collision = rowTrackIndexProng3.index0().collision(); + + // reconstruct the 3-prong secondary vertex + if (df.process(trackParVar0, trackParVar1, trackParVar2) == 0) { + continue; + } + const auto& secondaryVertex = df.getPCACandidate(); + auto chi2PCA = df.getChi2AtPCACandidate(); + auto covMatrixPCA = df.calcPCACovMatrix().Array(); + hCovSVXX->Fill(covMatrixPCA[0]); // FIXME: Calculation of errorDecayLength(XY) gives wrong values without this line. + trackParVar0 = df.getTrack(0); + trackParVar1 = df.getTrack(1); + trackParVar2 = df.getTrack(2); + + // get track momenta + array<float, 3> pvec0; + array<float, 3> pvec1; + array<float, 3> pvec2; + trackParVar0.getPxPyPzGlo(pvec0); + trackParVar1.getPxPyPzGlo(pvec1); + trackParVar2.getPxPyPzGlo(pvec2); + + // get track impact parameters + // This modifies track momenta! + auto primaryVertex = getPrimaryVertex(collision); + auto covMatrixPV = primaryVertex.getCov(); + hCovPVXX->Fill(covMatrixPV[0]); + o2::dataformats::DCA impactParameter0; + o2::dataformats::DCA impactParameter1; + o2::dataformats::DCA impactParameter2; + trackParVar0.propagateToDCA(primaryVertex, magneticField, &impactParameter0); + trackParVar1.propagateToDCA(primaryVertex, magneticField, &impactParameter1); + trackParVar2.propagateToDCA(primaryVertex, magneticField, &impactParameter2); + + // get uncertainty of the decay length + double phi, theta; + getPointDirection(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex, phi, theta); + auto errorDecayLength = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); + auto errorDecayLengthXY = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); + + // fill candidate table rows + rowCandidateBase(collision.posX(), collision.posY(), collision.posZ(), + secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], + errorDecayLength, errorDecayLengthXY, + chi2PCA, + pvec0[0], pvec0[1], pvec0[2], + pvec1[0], pvec1[1], pvec1[2], + pvec2[0], pvec2[1], pvec2[2], + impactParameter0.getY(), impactParameter1.getY(), impactParameter2.getY(), + std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()), std::sqrt(impactParameter2.getSigmaY2()), + rowTrackIndexProng3.index0Id(), rowTrackIndexProng3.index1Id(), rowTrackIndexProng3.index2Id(), + rowTrackIndexProng3.hfflag()); + + // fill histograms + if (b_dovalplots) { + // calculate invariant mass + auto arrayMomenta = array{pvec0, pvec1, pvec2}; + massPiKPi = RecoDecay::M(std::move(arrayMomenta), array{massPi, massK, massPi}); + hmass3->Fill(massPiKPi); + } + } + } +}; + +/// Extends the base table with expression columns. +struct HFCandidateCreator3ProngExpressions { + Spawns<aod::HfCandProng3Ext> rowCandidateProng3; + void init(InitContext const&) {} +}; + +/// Performs MC matching. +struct HFCandidateCreator3ProngMC { + Produces<aod::HfCandProng3MCRec> rowMCMatchRec; + Produces<aod::HfCandProng3MCGen> rowMCMatchGen; + + void process(aod::HfCandProng3 const& candidates, + aod::BigTracksMC const& tracks, + aod::McParticles const& particlesMC) + { + int8_t sign = 0; + int8_t result = 0; + + // Match reconstructed candidates. + for (auto& candidate : candidates) { + //Printf("New rec. candidate"); + result = 0; + auto arrayDaughters = array{candidate.index0_as<aod::BigTracksMC>(), candidate.index1_as<aod::BigTracksMC>(), candidate.index2_as<aod::BigTracksMC>()}; + + // D± → π± K∓ π± + //Printf("Checking D± → π± K∓ π±"); + auto indexRecDPlus = RecoDecay::getMatchedMCRec(particlesMC, arrayDaughters, 411, array{+kPiPlus, -kKPlus, +kPiPlus}, true, &sign); + result += sign * DPlusToPiKPi * int8_t(indexRecDPlus > -1); + + // Λc± → p± K∓ π± + //Printf("Checking Λc± → p± K∓ π±"); + auto indexRecLc = RecoDecay::getMatchedMCRec(particlesMC, std::move(arrayDaughters), 4122, array{+kProton, -kKPlus, +kPiPlus}, true, &sign); + result += sign * LcToPKPi * int8_t(indexRecLc > -1); + + rowMCMatchRec(result); + } + + // Match generated particles. + for (auto& particle : particlesMC) { + //Printf("New gen. candidate"); + result = 0; + + // D± → π± K∓ π± + //Printf("Checking D± → π± K∓ π±"); + auto isMatchedGenDPlus = RecoDecay::isMatchedMCGen(particlesMC, particle, 411, array{+kPiPlus, -kKPlus, +kPiPlus}, true, &sign); + result += sign * DPlusToPiKPi * int8_t(isMatchedGenDPlus); + + // Λc± → p± K∓ π± + //Printf("Checking Λc± → p± K∓ π±"); + auto isMatchedGenLc = RecoDecay::isMatchedMCGen(particlesMC, particle, 4122, array{+kProton, -kKPlus, +kPiPlus}, true, &sign); + result += sign * LcToPKPi * int8_t(isMatchedGenLc); + + rowMCMatchGen(result); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask<HFCandidateCreator3Prong>("hf-cand-creator-3prong"), + adaptAnalysisTask<HFCandidateCreator3ProngExpressions>("hf-cand-creator-3prong-expressions")}; + const bool doMC = cfgc.options().get<bool>("doMC"); + if (doMC) { + workflow.push_back(adaptAnalysisTask<HFCandidateCreator3ProngMC>("hf-cand-creator-3prong-mc")); + } + return workflow; +} diff --git a/Analysis/Tasks/PWGHF/HFD0CandidateSelector.cxx b/Analysis/Tasks/PWGHF/HFD0CandidateSelector.cxx new file mode 100644 index 0000000000000..59d11b4291eac --- /dev/null +++ b/Analysis/Tasks/PWGHF/HFD0CandidateSelector.cxx @@ -0,0 +1,405 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 HFD0CandidateSelector.cxx +/// \brief D0 selection task. +/// +/// \author Nima Zardoshti <nima.zardoshti@cern.ch>, CERN + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong2; + +static const int npTBins = 25; +static const int nCutVars = 11; +//temporary until 2D array in configurable is solved - then move to json +//m dca cost* ptk ptpi d0k d0pi d0d0 cosp cosxy normdxy +constexpr double cuts[npTBins][nCutVars] = {{0.400, 350. * 1E-4, 0.8, 0.5, 0.5, 1000. * 1E-4, 1000. * 1E-4, -5000. * 1E-8, 0.80, 0., 0.}, /* pt<0.5*/ + {0.400, 350. * 1E-4, 0.8, 0.5, 0.5, 1000. * 1E-4, 1000. * 1E-4, -5000. * 1E-8, 0.80, 0., 0.}, /* 0.5<pt<1*/ + {0.400, 300. * 1E-4, 0.8, 0.4, 0.4, 1000. * 1E-4, 1000. * 1E-4, -25000. * 1E-8, 0.80, 0., 0.}, /* 1<pt<1.5 */ + {0.400, 300. * 1E-4, 0.8, 0.4, 0.4, 1000. * 1E-4, 1000. * 1E-4, -25000. * 1E-8, 0.80, 0., 0.}, /* 1.5<pt<2 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -20000. * 1E-8, 0.90, 0., 0.}, /* 2<pt<2.5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -20000. * 1E-8, 0.90, 0., 0.}, /* 2.5<pt<3 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -12000. * 1E-8, 0.85, 0., 0.}, /* 3<pt<3.5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -12000. * 1E-8, 0.85, 0., 0.}, /* 3.5<pt<4 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -8000. * 1E-8, 0.85, 0., 0.}, /* 4<pt<4.5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -8000. * 1E-8, 0.85, 0., 0.}, /* 4.5<pt<5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -8000. * 1E-8, 0.85, 0., 0.}, /* 5<pt<5.5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -8000. * 1E-8, 0.85, 0., 0.}, /* 5.5<pt<6 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -8000. * 1E-8, 0.85, 0., 0.}, /* 6<pt<6.5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -8000. * 1E-8, 0.85, 0., 0.}, /* 6.5<pt<7 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -7000. * 1E-8, 0.85, 0., 0.}, /* 7<pt<7.5 */ + {0.400, 300. * 1E-4, 0.8, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -7000. * 1E-8, 0.85, 0., 0.}, /* 7.5<pt<8 */ + {0.400, 300. * 1E-4, 0.9, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -5000. * 1E-8, 0.85, 0., 0.}, /* 8<pt<9 */ + {0.400, 300. * 1E-4, 0.9, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -5000. * 1E-8, 0.85, 0., 0.}, /* 9<pt<10 */ + {0.400, 300. * 1E-4, 0.9, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, -5000. * 1E-8, 0.85, 0., 0.}, /* 10<pt<12 */ + {0.400, 300. * 1E-4, 1.0, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, 10000. * 1E-8, 0.85, 0., 0.}, /* 12<pt<16 */ + {0.400, 300. * 1E-4, 1.0, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, 999999. * 1E-8, 0.85, 0., 0.}, /* 16<pt<20 */ + {0.400, 300. * 1E-4, 1.0, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, 999999. * 1E-8, 0.85, 0., 0.}, /* 20<pt<24 */ + {0.400, 300. * 1E-4, 1.0, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, 999999. * 1E-8, 0.85, 0., 0.}, /* 24<pt<36 */ + {0.400, 300. * 1E-4, 1.0, 0.7, 0.7, 1000. * 1E-4, 1000. * 1E-4, 999999. * 1E-8, 0.85, 0., 0.}, /* 36<pt<50 */ + {0.400, 300. * 1E-4, 1.0, 0.6, 0.6, 1000. * 1E-4, 1000. * 1E-4, 999999. * 1E-8, 0.80, 0., 0.}}; /* pt>50 */ + +/// Struct for applying D0 selection cuts + +struct HFD0CandidateSelector { + + Produces<aod::HFSelD0Candidate> hfSelD0Candidate; + + Configurable<double> d_pTCandMin{"d_pTCandMin", 0., "Lower bound of candidate pT"}; + Configurable<double> d_pTCandMax{"d_pTCandMax", 50., "Upper bound of candidate pT"}; + + Configurable<double> d_pidTPCMinpT{"d_pidTPCMinpT", 0.15, "Lower bound of track pT for TPC PID"}; + Configurable<double> d_pidTPCMaxpT{"d_pidTPCMaxpT", 5., "Upper bound of track pT for TPC PID"}; + Configurable<double> d_pidTOFMinpT{"d_pidTOFMinpT", 0.15, "Lower bound of track pT for TOF PID"}; + Configurable<double> d_pidTOFMaxpT{"d_pidTOFMaxpT", 5., "Upper bound of track pT for TOF PID"}; + + Configurable<double> d_TPCNClsFindablePIDCut{"d_TPCNClsFindablePIDCut", 50., "Lower bound of TPC findable clusters for good PID"}; + Configurable<double> d_nSigmaTPC{"d_nSigmaTPC", 3., "Nsigma cut on TPC only"}; + Configurable<double> d_nSigmaTPCCombined{"d_nSigmaTPCCombined", 5., "Nsigma cut on TPC combined with TOF"}; + Configurable<double> d_nSigmaTOF{"d_nSigmaTOF", 3., "Nsigma cut on TOF only"}; + Configurable<double> d_nSigmaTOFCombined{"d_nSigmaTOFCombined", 5., "Nsigma cut on TOF combined with TPC"}; + + /// Gets corresponding pT bin from cut file array + /// \param candpT is the pT of the candidate + /// \return corresponding bin number of array + template <typename T> + int getpTBin(T candpT) + { + double pTBins[npTBins + 1] = {0, 0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5, 5., 5.5, 6., 6.5, 7., 7.5, 8., 9., 10., 12., 16., 20., 24., 36., 50., 100.}; + if (candpT < pTBins[0] || candpT >= pTBins[npTBins]) { + return -1; + } + for (int i = 0; i < npTBins; i++) { + if (candpT < pTBins[i + 1]) { + return i; + } + } + return -1; + } + + /// Selection on goodness of daughter tracks + /// \note should be applied at candidate selection + /// \param track is daughter track + /// \return true if track is good + template <typename T> + bool daughterSelection(const T& track) + { + if (track.charge() == 0) { + return false; + } + if (track.tpcNClsFound() == 0) { + return false; //is it clusters findable or found - need to check + } + return true; + } + + /// Conjugate independent toplogical cuts + /// \param hfCandProng2 is candidate + /// \return true if candidate passes all cuts + template <typename T> + bool selectionTopol(const T& hfCandProng2) + { + auto candpT = hfCandProng2.pt(); + int pTBin = getpTBin(candpT); + if (pTBin == -1) { + return false; + } + + if (candpT < d_pTCandMin || candpT >= d_pTCandMax) { + return false; //check that the candidate pT is within the analysis range + } + if (hfCandProng2.impactParameterProduct() > cuts[pTBin][7]) { + return false; //product of daughter impact parameters + } + if (hfCandProng2.cpa() < cuts[pTBin][8]) { + return false; //cosine of pointing angle + } + if (hfCandProng2.cpaXY() < cuts[pTBin][9]) { + return false; //cosine of pointing angle XY + } + if (hfCandProng2.decayLengthXYNormalised() < cuts[pTBin][10]) { + return false; //normalised decay length in XY plane + } + // if (hfCandProng2.dca() > cuts[pTBin][1]) return false; //candidate DCA + //if (hfCandProng2.chi2PCA() > cuts[pTBin][1]) return false; //candidate DCA + + //decay exponentail law, with tau = beta*gamma*ctau + //decay length > ctau retains (1-1/e) + double decayLengthCut = TMath::Min((hfCandProng2.p() * 0.0066) + 0.01, 0.06); + if (TMath::Abs(hfCandProng2.impactParameterNormalised0()) < 0.5 || TMath::Abs(hfCandProng2.impactParameterNormalised1()) < 0.5) { + return false; + } + if (hfCandProng2.decayLength() * hfCandProng2.decayLength() < decayLengthCut * decayLengthCut) { + return false; + } + if (hfCandProng2.decayLengthNormalised() * hfCandProng2.decayLengthNormalised() < 1.0) { + //return false; //add back when getter fixed + } + return true; + } + + /// Conjugate dependent toplogical cuts + /// \param hfCandProng2 is candidate + /// \param trackPion is the track with the pion hypothesis + /// \param trackKaon is the track with the kaon hypothesis + /// \note trackPion = positive and trackKaon = negative for D0 selection and inverse for D0bar + /// \return true if candidate passes all cuts for the given Conjugate + template <typename T1, typename T2> + bool selectionTopolConjugate(const T1& hfCandProng2, const T2& trackPion, const T2& trackKaon) + { + + auto candpT = hfCandProng2.pt(); + int pTBin = getpTBin(candpT); + if (pTBin == -1) { + return false; + } + + if (trackPion.charge() > 0) { //invariant mass cut + if (TMath::Abs(InvMassD0(hfCandProng2) - RecoDecay::getMassPDG(421)) > cuts[pTBin][0]) { + return false; + } + } else { + if (TMath::Abs(InvMassD0bar(hfCandProng2) - RecoDecay::getMassPDG(421)) > cuts[pTBin][0]) { + return false; + } + } + + if (trackPion.pt() < cuts[pTBin][4] || trackKaon.pt() < cuts[pTBin][3]) { + return false; //cut on daughter pT + } + if (TMath::Abs(trackPion.dcaPrim0()) > cuts[pTBin][6] || TMath::Abs(trackKaon.dcaPrim0()) > cuts[pTBin][5]) { + return false; //cut on daughter dca - need to add secondary vertex constraint here + } + + if (trackPion.charge() > 0) { //cut on cos(theta *) + if (TMath::Abs(CosThetaStarD0(hfCandProng2)) > cuts[pTBin][2]) { + return false; + } + } else { + if (TMath::Abs(CosThetaStarD0bar(hfCandProng2)) > cuts[pTBin][2]) { + return false; + } + } + + return true; + } + + /// Check if track is ok for TPC PID + /// \param track is the track + /// \note function to be expanded + /// \return true if track is ok for TPC PID + template <typename T> + bool validTPCPID(const T& track) + { + if (TMath::Abs(track.pt()) < d_pidTPCMinpT || TMath::Abs(track.pt()) >= d_pidTPCMaxpT) { + return false; + } + //if (track.TPCNClsFindable() < d_TPCNClsFindablePIDCut) return false; + return true; + } + + /// Check if track is ok for TOF PID + /// \param track is the track + /// \note function to be expanded + /// \return true if track is ok for TOF PID + template <typename T> + bool validTOFPID(const T& track) + { + if (TMath::Abs(track.pt()) < d_pidTOFMinpT || TMath::Abs(track.pt()) >= d_pidTOFMaxpT) { + return false; + } + return true; + } + + /// Check if track is compatible with given TPC Nsigma cut for a given flavour hypothesis + /// \param track is the track + /// \param nPDG is the flavour hypothesis PDG number + /// \param nSigmaCut is the nsigma threshold to test against + /// \note nPDG=211 pion nPDG=321 kaon + /// \return true if track satisfies TPC PID hypothesis for given Nsigma cut + template <typename T> + bool selectionPIDTPC(const T& track, int nPDG, int nSigmaCut) + { + double nSigma = 100.0; //arbitarily large value + nPDG = TMath::Abs(nPDG); + if (nPDG == 111) { + nSigma = track.tpcNSigmaPi(); + } else if (nPDG == 321) { + nSigma = track.tpcNSigmaKa(); + } else { + return false; + } + return nSigma < nSigmaCut; + } + + /// Check if track is compatible with given TOF NSigma cut for a given flavour hypothesis + /// \param track is the track + /// \param nPDG is the flavour hypothesis PDG number + /// \param nSigmaCut is the nSigma threshold to test against + /// \note nPDG=211 pion nPDG=321 kaon + /// \return true if track satisfies TOF PID hypothesis for given NSigma cut + template <typename T> + bool selectionPIDTOF(const T& track, int nPDG, int nSigmaCut) + { + double nSigma = 100.0; //arbitarily large value + nPDG = TMath::Abs(nPDG); + if (nPDG == 111) { + nSigma = track.tofNSigmaPi(); + } else if (nPDG == 321) { + nSigma = track.tofNSigmaKa(); + } else { + return false; + } + return nSigma < nSigmaCut; + } + + /// PID selection on daughter track + /// \param track is the daughter track + /// \param nPDG is the PDG code of the flavour hypothesis + /// \note nPDG=211 pion nPDG=321 kaon + /// \return 1 if successful PID match, 0 if successful PID rejection, -1 if no PID info + template <typename T> + int selectionPID(const T& track, int nPDG) + { + int statusTPC = -1; + int statusTOF = -1; + + if (validTPCPID(track)) { + if (!selectionPIDTPC(track, nPDG, d_nSigmaTPC)) { + if (!selectionPIDTPC(track, nPDG, d_nSigmaTPCCombined)) { + statusTPC = 0; //rejected by PID + } else { + statusTPC = 1; //potential to be acceepted if combined with TOF + } + } else { + statusTPC = 2; //positive PID + } + } else { + statusTPC = -1; //no PID info + } + + if (validTOFPID(track)) { + if (!selectionPIDTOF(track, nPDG, d_nSigmaTOF)) { + if (!selectionPIDTOF(track, nPDG, d_nSigmaTOFCombined)) { + statusTOF = 0; //rejected by PID + } else { + statusTOF = 1; //potential to be acceepted if combined with TOF + } + } else { + statusTOF = 2; //positive PID + } + } else { + statusTOF = -1; //no PID info + } + + if (statusTPC == 2 || statusTOF == 2) { + return 1; //what if we have 2 && 0 ? + } else if (statusTPC == 1 && statusTOF == 1) { + return 1; + } else if (statusTPC == 0 || statusTOF == 0) { + return 0; + } else { + return -1; + } + } + + void process(aod::HfCandProng2 const& hfCandProng2s, aod::BigTracksPID const& tracks) + { + int statusD0, statusD0bar; // final selection flag : 0-rejected 1-accepted + bool topolD0, topolD0bar; + int pidD0, pidD0bar, pionPlus, pionMinus, kaonPlus, kaonMinus; + + for (auto& hfCandProng2 : hfCandProng2s) { //looping over 2 prong candidates + + auto trackPos = hfCandProng2.index0_as<aod::BigTracksPID>(); //positive daughter + auto trackNeg = hfCandProng2.index1_as<aod::BigTracksPID>(); //negative daughter + + statusD0 = 0; + statusD0bar = 0; + topolD0 = true; + topolD0bar = true; + pidD0 = -1; + pidD0bar = -1; + pionPlus = -1; + pionMinus = -1; + kaonPlus = -1; + kaonMinus = -1; + + // daughter track validity selection + if (!daughterSelection(trackPos) || !daughterSelection(trackNeg)) { + hfSelD0Candidate(statusD0, statusD0bar); + continue; + } + + //implement filter bit 4 cut - should be done before this task at the track selection level + //need to add special cuts (additional cuts on decay length and d0 norm) + + //conjugate independent topological selection + if (!selectionTopol(hfCandProng2)) { + hfSelD0Candidate(statusD0, statusD0bar); + continue; + } + + //conjugate dependent toplogical selection for D0 + topolD0 = selectionTopolConjugate(hfCandProng2, trackPos, trackNeg); + //conjugate dependent toplogical selection for D0bar + topolD0bar = selectionTopolConjugate(hfCandProng2, trackNeg, trackPos); + + if (!topolD0 && !topolD0bar) { + hfSelD0Candidate(statusD0, statusD0bar); + continue; + } + + pionPlus = selectionPID(trackPos, 211); + kaonMinus = selectionPID(trackNeg, 321); + pionMinus = selectionPID(trackNeg, 211); + kaonPlus = selectionPID(trackPos, 321); + + if (pionPlus == 0 || kaonMinus == 0 || pionMinus == 1 || kaonPlus == 1) { + pidD0 = 0; //exclude D0 + } + if (pionPlus == 1 || kaonMinus == 1 || pionMinus == 0 || kaonPlus == 0) { + pidD0bar = 0; //exclude D0bar + } + if (pionPlus == 1 && kaonMinus == 1) { + pidD0 = 1; //accept D0 + } + if (pionMinus == 1 && kaonPlus == 1) { + pidD0bar = 1; //accept D0bar + } + + if (pidD0 == 0 && pidD0bar == 0) { + hfSelD0Candidate(statusD0, statusD0bar); + continue; + } + + if ((pidD0 == -1 || pidD0 == 1) && topolD0) { + statusD0 = 1; //identified as D0 + } + if ((pidD0bar == -1 || pidD0bar == 1) && topolD0bar) { + statusD0bar = 1; //identified as D0bar + } + + hfSelD0Candidate(statusD0, statusD0bar); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<HFD0CandidateSelector>("hf-d0-candidate-selector")}; +} diff --git a/Analysis/Tasks/PWGHF/HFLcCandidateSelector.cxx b/Analysis/Tasks/PWGHF/HFLcCandidateSelector.cxx new file mode 100644 index 0000000000000..d4fe99f1cb21f --- /dev/null +++ b/Analysis/Tasks/PWGHF/HFLcCandidateSelector.cxx @@ -0,0 +1,364 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 HFD0CandidateSelector.cxx +/// \brief Lc->pKpi selection task. +/// +/// \author Luigi Dello Stritto <luigi.dello.stritto@cern.ch>, University and INFN SALERNO +/// \author Nima Zardoshti <nima.zardoshti@cern.ch>, CERN + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong3; + +static const int npTBins = 10; +static const int nCutVars = 8; +//temporary until 2D array in configurable is solved - then move to json +//m ptp ptk ptpi DCA sigmavtx dlenght cosp +constexpr double cuts[npTBins][nCutVars] = {{0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* pt<1 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 1<pt<2 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 2<pt<3 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 3<pt<4 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 4<pt<5 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 5<pt<6 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 6<pt<8 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 8<pt<12 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}, /* 12<pt<24 */ + {0.400, 0.4, 0.4, 0.4, 0.05, 0.09, 0.005, 0.}}; /* 24<pt<36 */ + +/// Struct for applying Lc selection cuts +struct HFLcCandidateSelector { + + Produces<aod::HFSelLcCandidate> hfSelLcCandidate; + + Configurable<double> d_pTCandMin{"d_pTCandMin", 0., "Lower bound of candidate pT"}; + Configurable<double> d_pTCandMax{"d_pTCandMax", 36., "Upper bound of candidate pT"}; + + Configurable<double> d_pidTPCMinpT{"d_pidTPCMinpT", 0.15, "Lower bound of track pT for TPC PID"}; + Configurable<double> d_pidTPCMaxpT{"d_pidTPCMaxpT", 1., "Upper bound of track pT for TPC PID"}; + Configurable<double> d_pidTOFMinpT{"d_pidTOFMinpT", 0.5, "Lower bound of track pT for TOF PID"}; + Configurable<double> d_pidTOFMaxpT{"d_pidTOFMaxpT", 4., "Upper bound of track pT for TOF PID"}; + + Configurable<double> d_TPCNClsFindablePIDCut{"d_TPCNClsFindablePIDCut", 70., "Lower bound of TPC findable clusters for good PID"}; + Configurable<double> d_nSigmaTPC{"d_nSigmaTPC", 3., "Nsigma cut on TPC only"}; + Configurable<double> d_nSigmaTPCCombined{"d_nSigmaTPCCombined", 5., "Nsigma cut on TPC combined with TOF"}; + Configurable<double> d_nSigmaTOF{"d_nSigmaTOF", 3., "Nsigma cut on TOF only"}; + Configurable<double> d_nSigmaTOFCombined{"d_nSigmaTOFCombined", 5., "Nsigma cut on TOF combined with TPC"}; + + /// Gets corresponding pT bin from cut file array + /// \param candpT is the pT of the candidate + /// \return corresponding bin number of array + template <typename T> + int getpTBin(T candpT) + { + double pTBins[npTBins + 1] = {0, 1., 2., 3., 4., 5., 6., 8., 12., 24., 36.}; + if (candpT < pTBins[0] || candpT >= pTBins[npTBins]) { + return -1; + } + for (int i = 0; i < npTBins; i++) { + if (candpT < pTBins[i + 1]) { + return i; + } + } + return -1; + } + + /// Selection on goodness of daughter tracks + /// \note should be applied at candidate selection + /// \param track is daughter track + /// \return true if track is good + template <typename T> + bool daughterSelection(const T& track) + { + if (track.charge() == 0) { + return false; + } + if (track.tpcNClsFound() == 0) { + return false; //is it clusters findable or found - need to check + } + return true; + } + + /// Conjugate independent toplogical cuts + /// \param hfCandProng3 is candidate + /// \return true if candidate passes all cuts + template <typename T> + bool selectionTopol(const T& hfCandProng3) + { + auto candpT = hfCandProng3.pt(); + int pTBin = getpTBin(candpT); + if (pTBin == -1) { + return false; + } + + if (candpT < d_pTCandMin || candpT >= d_pTCandMax) { + return false; //check that the candidate pT is within the analysis range + } + + if (hfCandProng3.cpa() <= cuts[pTBin][7]) { + return false; //cosine of pointing angle + } + + /* if (hfCandProng3.chi2PCA() > cuts[pTBin][5]) { //candidate DCA + return false; + }*/ + + if (hfCandProng3.decayLength() <= cuts[pTBin][6]) { + return false; + } + return true; + } + + /// Conjugate dependent toplogical cuts + /// \param hfCandProng3 is candidate + /// \param trackProton is the track with the proton hypothesis + /// \param trackPion is the track with the pion hypothesis + /// \param trackKaon is the track with the kaon hypothesis + /// \return true if candidate passes all cuts for the given Conjugate + template <typename T1, typename T2> + bool selectionTopolConjugate(const T1& hfCandProng3, const T2& trackProton, const T2& trackKaon, const T2& trackPion) + { + + auto candpT = hfCandProng3.pt(); + int pTBin = getpTBin(candpT); + if (pTBin == -1) { + return false; + } + + if (trackProton.pt() < cuts[pTBin][1] || trackKaon.pt() < cuts[pTBin][2] || trackPion.pt() < cuts[pTBin][3]) { + return false; //cut on daughter pT + } + + if (trackProton.globalIndex() == hfCandProng3.index0Id()) { + if (TMath::Abs(InvMassLcpKpi(hfCandProng3) - RecoDecay::getMassPDG(4122)) > cuts[pTBin][0]) { + return false; + } + } else { + if (TMath::Abs(InvMassLcpiKp(hfCandProng3) - RecoDecay::getMassPDG(4122)) > cuts[pTBin][0]) { + return false; + } + } + + return true; + } + + /// Check if track is ok for TPC PID + /// \param track is the track + /// \note function to be expanded + /// \return true if track is ok for TPC PID + template <typename T> + bool validTPCPID(const T& track) + { + if (TMath::Abs(track.pt()) < d_pidTPCMinpT || TMath::Abs(track.pt()) >= d_pidTPCMaxpT) { + return false; + } + //if (track.TPCNClsFindable() < d_TPCNClsFindablePIDCut) return false; + return true; + } + + /// Check if track is ok for TOF PID + /// \param track is the track + /// \note function to be expanded + /// \return true if track is ok for TOF PID + template <typename T> + bool validTOFPID(const T& track) + { + if (TMath::Abs(track.pt()) < d_pidTOFMinpT || TMath::Abs(track.pt()) >= d_pidTOFMaxpT) { + return false; + } + return true; + } + + /// Check if track is compatible with given TPC Nsigma cut for a given flavour hypothesis + /// \param track is the track + /// \param nPDG is the flavour hypothesis PDG number + /// \param nSigmaCut is the nsigma threshold to test against + /// \note nPDG=2212 proton nPDG=211 pion nPDG=321 kaon + /// \return true if track satisfies TPC PID hypothesis for given Nsigma cut + template <typename T> + bool selectionPIDTPC(const T& track, int nPDG, int nSigmaCut) + { + double nSigma = 1.0; //arbitarily large value + nPDG = TMath::Abs(nPDG); + /* if (nPDG == 2212) { + nSigma = track.tpcNSigmaPr(); + } else if (nPDG == 321) { + nSigma = track.tpcNSigmaKa(); + } else if (nPDG == 111) { + nSigma = track.tpcNSigmaPi(); + } else { + return false; + }*/ + return nSigma < nSigmaCut; + } + + /// Check if track is compatible with given TOF NSigma cut for a given flavour hypothesis + /// \param track is the track + /// \param nPDG is the flavour hypothesis PDG number + /// \param nSigmaCut is the nSigma threshold to test against + /// \note nPDG=2212 proton nPDG=211 pion nPDG=321 kaon + /// \return true if track satisfies TOF PID hypothesis for given NSigma cut + template <typename T> + bool selectionPIDTOF(const T& track, int nPDG, int nSigmaCut) + { + double nSigma = 1.; //arbitarily large value + nPDG = TMath::Abs(nPDG); + /* if (nPDG == 2212) { + nSigma = track.tofNSigmaPr(); + } else if (nPDG == 321) { + nSigma = track.tofNSigmaKa(); + } else if (nPDG == 321) { + nSigma = track.tofNSigmaPi(); + } else { + return false; + }*/ + return nSigma < nSigmaCut; + } + + /// PID selection on daughter track + /// \param track is the daughter track + /// \param nPDG is the PDG code of the flavour hypothesis + /// \note nPDG=2212 nPDG=211 pion nPDG=321 kaon + /// \return 1 if successful PID match, 0 if successful PID rejection, -1 if no PID info + template <typename T> + int selectionPID(const T& track, int nPDG) + { + int statusTPC = -1; + int statusTOF = -1; + + if (validTPCPID(track)) { + if (!selectionPIDTPC(track, nPDG, d_nSigmaTPC)) { + if (!selectionPIDTPC(track, nPDG, d_nSigmaTPCCombined)) { + statusTPC = 0; //rejected by PID + } else { + statusTPC = 1; //potential to be acceepted if combined with TOF + } + } else { + statusTPC = 2; //positive PID + } + } else { + statusTPC = -1; //no PID info + } + + if (validTOFPID(track)) { + if (!selectionPIDTOF(track, nPDG, d_nSigmaTOF)) { + if (!selectionPIDTOF(track, nPDG, d_nSigmaTOFCombined)) { + statusTOF = 0; //rejected by PID + } else { + statusTOF = 1; //potential to be acceepted if combined with TOF + } + } else { + statusTOF = 2; //positive PID + } + } else { + statusTOF = -1; //no PID info + } + + if (statusTPC == 2 || statusTOF == 2) { + return 1; //what if we have 2 && 0 ? + } else if (statusTPC == 1 && statusTOF == 1) { + return 1; + } else if (statusTPC == 0 || statusTOF == 0) { + return 0; + } else { + return -1; + } + } + + void process(aod::HfCandProng3 const& hfCandProng3s, aod::BigTracksPID const& tracks) + { + int statusLcpKpi, statusLcpiKp; // final selection flag : 0-rejected 1-accepted + bool topolLcpKpi, topolLcpiKp; + int pidLc, proton, kaonMinus, pionPlus; + + for (auto& hfCandProng3 : hfCandProng3s) { //looping over 3 prong candidates + + statusLcpKpi = 0; + statusLcpiKp = 0; + int LcFlag = hfCandProng3.hfflag(); + + if (!(LcFlag & 1 << 1)) { + hfSelLcCandidate(statusLcpKpi, statusLcpiKp); + continue; + } + + auto trackPos1 = hfCandProng3.index0_as<aod::BigTracksPID>(); //positive daughter (negative for the antiparticles) + auto trackNeg1 = hfCandProng3.index1_as<aod::BigTracksPID>(); //negative daughter (positive for the antiparticles) + auto trackPos2 = hfCandProng3.index2_as<aod::BigTracksPID>(); //positive daughter (negative for the antiparticles) + + topolLcpKpi = true; + topolLcpiKp = true; + pidLc = -1; + proton = -1; + kaonMinus = -1; + pionPlus = -1; + + // daughter track validity selection + if (!daughterSelection(trackPos1) || !daughterSelection(trackNeg1) || !daughterSelection(trackPos2)) { + hfSelLcCandidate(statusLcpKpi, statusLcpiKp); + continue; + } + + //implement filter bit 4 cut - should be done before this task at the track selection level + + //conjugate independent topological selection + if (!selectionTopol(hfCandProng3)) { + hfSelLcCandidate(statusLcpKpi, statusLcpiKp); + continue; + } + + //conjugate dependent toplogical selection for Lc + + topolLcpKpi = selectionTopolConjugate(hfCandProng3, trackPos1, trackNeg1, trackPos2); + topolLcpiKp = selectionTopolConjugate(hfCandProng3, trackPos2, trackNeg1, trackPos1); + + if (!topolLcpKpi && !topolLcpiKp) { + hfSelLcCandidate(statusLcpKpi, statusLcpiKp); + continue; + } + + proton = selectionPID(trackPos1, 2212); + kaonMinus = selectionPID(trackNeg1, 321); + pionPlus = selectionPID(trackPos2, 211); + + if (proton == 0 || kaonMinus == 0 || pionPlus == 0) { + pidLc = 0; //exclude Lc + } + if (proton == 1 && kaonMinus == 1 && pionPlus == 1) { + pidLc = 1; //accept Lc + } + + if (pidLc == 0) { + hfSelLcCandidate(statusLcpKpi, statusLcpiKp); + continue; + } + + if ((pidLc == -1 || pidLc == 1) && topolLcpKpi) { + statusLcpKpi = 1; //identified as Lc + } + if ((pidLc == -1 || pidLc == 1) && topolLcpiKp) { + statusLcpiKp = 1; //identified as Lc + } + + hfSelLcCandidate(statusLcpKpi, statusLcpiKp); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<HFLcCandidateSelector>("hf-lc-candidate-selector")}; +} diff --git a/Analysis/Tasks/PWGHF/HFTrackIndexSkimsCreator.cxx b/Analysis/Tasks/PWGHF/HFTrackIndexSkimsCreator.cxx new file mode 100644 index 0000000000000..eb15aae86c9cd --- /dev/null +++ b/Analysis/Tasks/PWGHF/HFTrackIndexSkimsCreator.cxx @@ -0,0 +1,831 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 HFTrackIndexSkimsCreator.cxx +/// \brief Pre-selection of 2-prong and 3-prong secondary vertices of heavy-flavour decay candidates +/// +/// \author Gian Michele Innocenti <gian.michele.innocenti@cern.ch>, CERN +/// \author Vít Kučera <vit.kucera@cern.ch>, CERN +/// \author Nima Zardoshti <nima.zardoshti@cern.ch>, CERN + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisCore/HFConfigurables.h" +//#include "AnalysisDataModel/Centrality.h" +#include "Framework/HistogramRegistry.h" +#include <algorithm> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// Track selection +struct SelectTracks { + Produces<aod::HFSelTrack> rowSelectedTrack; + + Configurable<bool> b_dovalplots{"b_dovalplots", true, "fill histograms"}; + Configurable<double> d_bz{"d_bz", 5., "bz field"}; + // quality cut + Configurable<bool> doCutQuality{"doCutQuality", true, "apply quality cuts"}; + Configurable<int> d_tpcnclsfound{"d_tpcnclsfound", 70, ">= min. number of TPC clusters needed"}; + // 2-prong cuts + Configurable<double> ptmintrack_2prong{"ptmintrack_2prong", -1., "min. track pT for 2 prong candidate"}; + Configurable<double> dcatoprimxy_2prong_maxpt{"dcatoprimxymin_2prong_minpt", 2., "max pt cut for min. DCAXY to prim. vtx. for 2 prong candidate"}; + Configurable<double> dcatoprimxymin_2prong{"dcatoprimxymin_2prong", 0., "min. DCAXY to prim. vtx. for 2 prong candidate"}; + Configurable<double> dcatoprimxymax_2prong{"dcatoprimxymax_2prong", 1.0, "max. DCAXY to prim. vtx. for 2 prong candidate"}; + Configurable<double> etamax_2prong{"etamax_2prong", 4., "max. pseudorapidity for 2 prong candidate"}; + // 3-prong cuts + Configurable<double> ptmintrack_3prong{"ptmintrack_3prong", -1., "min. track pT for 3 prong candidate"}; + Configurable<double> dcatoprimxy_3prong_maxpt{"dcatoprimxy_3prong_minpt", 2., "max pt cut for min. DCAXY to prim. vtx. for 3 prong candidate"}; + Configurable<double> dcatoprimxymin_3prong{"dcatoprimxymin_3prong", 0., "min. DCAXY to prim. vtx. for 3 prong candidate"}; + Configurable<double> dcatoprimxymax_3prong{"dcatoprimxymax_3prong", 1.0, "max. DCAXY to prim. vtx. for 3 prong candidate"}; + Configurable<double> etamax_3prong{"etamax_3prong", 4., "max. pseudorapidity for 3 prong candidate"}; + + HistogramRegistry registry{ + "registry", + {{"hpt_nocuts", "all tracks;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}}, + // 2-prong histograms + {"hpt_cuts_2prong", "tracks selected for 2-prong vertexing;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}}, + {"hdcatoprimxy_cuts_2prong", "tracks selected for 2-prong vertexing;DCAxy to prim. vtx. (cm);entries", {HistType::kTH1F, {{static_cast<int>(1.2 * dcatoprimxymax_2prong * 100), -1.2 * dcatoprimxymax_2prong, 1.2 * dcatoprimxymax_2prong}}}}, + {"heta_cuts_2prong", "tracks selected for 2-prong vertexing;#it{#eta};entries", {HistType::kTH1F, {{static_cast<int>(1.2 * etamax_2prong * 100), -1.2 * etamax_2prong, 1.2 * etamax_2prong}}}}, + // 3-prong histograms + {"hpt_cuts_3prong", "tracks selected for 3-prong vertexing;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}}, + {"hdcatoprimxy_cuts_3prong", "tracks selected for 3-prong vertexing;DCAxy to prim. vtx. (cm);entries", {HistType::kTH1F, {{static_cast<int>(1.2 * dcatoprimxymax_3prong) * 100, -1.2 * dcatoprimxymax_3prong, 1.2 * dcatoprimxymax_3prong}}}}, + {"heta_cuts_3prong", "tracks selected for 3-prong vertexing;#it{#eta};entries", {HistType::kTH1F, {{static_cast<int>(1.2 * etamax_3prong * 100), -1.2 * etamax_3prong, 1.2 * etamax_3prong}}}}}}; + + void process(aod::Collision const& collision, + soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra> const& tracks) + { + math_utils::Point3D<float> vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); + for (auto& track : tracks) { + + int status_prong = 3; // selection flag , 2 bits on + + auto trackPt = track.pt(); + if (b_dovalplots) { + registry.get<TH1>(HIST("hpt_nocuts"))->Fill(trackPt); + } + + // pT cut + if (trackPt < ptmintrack_2prong) { + status_prong = status_prong & ~(1 << 0); // the bitwise operation & ~(1 << n) will set the nth bit to 0 + } + if (trackPt < ptmintrack_3prong) { + status_prong = status_prong & ~(1 << 1); + } + + auto trackEta = track.eta(); + // eta cut + if ((status_prong & (1 << 0)) && abs(trackEta) > etamax_2prong) { + status_prong = status_prong & ~(1 << 0); + } + if ((status_prong & (1 << 1)) && abs(trackEta) > etamax_3prong) { + status_prong = status_prong & ~(1 << 1); + } + + // quality cut + if (doCutQuality && status_prong > 0) { // FIXME to make a more complete selection e.g track.flags() & o2::aod::track::TPCrefit && track.flags() & o2::aod::track::GoldenChi2 && + UChar_t clustermap = track.itsClusterMap(); + if (!(track.tpcNClsFound() >= d_tpcnclsfound && + track.flags() & o2::aod::track::ITSrefit && + (TESTBIT(clustermap, 0) || TESTBIT(clustermap, 1)))) { + status_prong = 0; + } + } + + // DCA cut + array<float, 2> dca; + if (status_prong > 0) { + double dcatoprimxymin_2prong_ptdep = dcatoprimxymin_2prong * TMath::Max(0., (1 - TMath::Floor(trackPt / dcatoprimxy_2prong_maxpt))); + double dcatoprimxymin_3prong_ptdep = dcatoprimxymin_3prong * TMath::Max(0., (1 - TMath::Floor(trackPt / dcatoprimxy_3prong_maxpt))); + auto trackparvar0 = getTrackParCov(track); + if (!trackparvar0.propagateParamToDCA(vtxXYZ, d_bz, &dca, 100.)) { // get impact parameters + status_prong = 0; + } + if ((status_prong & (1 << 0)) && (abs(dca[0]) < dcatoprimxymin_2prong_ptdep || abs(dca[0]) > dcatoprimxymax_2prong)) { + status_prong = status_prong & ~(1 << 0); + } + if ((status_prong & (1 << 1)) && (abs(dca[0]) < dcatoprimxymin_3prong_ptdep || abs(dca[0]) > dcatoprimxymax_3prong)) { + status_prong = status_prong & ~(1 << 1); + } + } + + // fill histograms + if (b_dovalplots) { + if (status_prong & (1 << 0)) { + registry.get<TH1>(HIST("hpt_cuts_2prong"))->Fill(trackPt); + registry.get<TH1>(HIST("hdcatoprimxy_cuts_2prong"))->Fill(dca[0]); + registry.get<TH1>(HIST("heta_cuts_2prong"))->Fill(trackEta); + } + if (status_prong & (1 << 1)) { + registry.get<TH1>(HIST("hpt_cuts_3prong"))->Fill(trackPt); + registry.get<TH1>(HIST("hdcatoprimxy_cuts_3prong"))->Fill(dca[0]); + registry.get<TH1>(HIST("heta_cuts_3prong"))->Fill(trackEta); + } + } + + // fill table row + rowSelectedTrack(status_prong, dca[0], dca[1]); + } + } +}; + +/// Pre-selection of 2-prong and 3-prong secondary vertices +struct HFTrackIndexSkimsCreator { + Produces<aod::HfTrackIndexProng2> rowTrackIndexProng2; + Produces<aod::HfCutStatusProng2> rowProng2CutStatus; + Produces<aod::HfTrackIndexProng3> rowTrackIndexProng3; + Produces<aod::HfCutStatusProng3> rowProng3CutStatus; + + //Configurable<int> nCollsMax{"nCollsMax", -1, "Max collisions per file"}; //can be added to run over limited collisions per file - for tesing purposes + Configurable<bool> b_dovalplots{"b_dovalplots", true, "fill histograms"}; + Configurable<int> do3prong{"do3prong", 0, "do 3 prong"}; + // event selection + Configurable<int> triggerindex{"triggerindex", -1, "trigger index"}; + // vertexing parameters + Configurable<double> d_bz{"d_bz", 5., "magnetic field kG"}; + Configurable<bool> b_propdca{"b_propdca", true, "create tracks version propagated to PCA"}; + Configurable<double> d_maxr{"d_maxr", 200., "reject PCA's above this radius"}; + Configurable<double> d_maxdzini{"d_maxdzini", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; + Configurable<double> d_minparamchange{"d_minparamchange", 1.e-3, "stop iterations if largest change of any X is smaller than this"}; + Configurable<double> d_minrelchi2change{"d_minrelchi2change", 0.9, "stop iterations if chi2/chi2old > this"}; + Configurable<HFTrackIndexSkimsCreatorConfigs> configs{"configs", {}, "configurables"}; + Configurable<bool> b_debug{"b_debug", false, "debug mode"}; + + HistogramRegistry registry{ + "registry", + {{"hNTracks", ";# of tracks;entries", {HistType::kTH1F, {{2500, 0., 25000.}}}}, + //2prong histograms + {"hvtx2_x", "2-prong candidates;#it{x}_{sec. vtx.} (cm);entries", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"hvtx2_y", "2-prong candidates;#it{y}_{sec. vtx.} (cm);entries", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"hvtx2_z", "2-prong candidates;#it{z}_{sec. vtx.} (cm);entries", {HistType::kTH1F, {{1000, -20., 20.}}}}, + {"hNCand2Prong", "2-prong candidates preselected;# of candidates;entries", {HistType::kTH1F, {{2000, 0., 200000.}}}}, + {"hNCand2ProngVsNTracks", "2-prong candidates preselected;# of selected tracks;# of candidates;entries", {HistType::kTH2F, {{2500, 0., 25000.}, {2000, 0., 200000.}}}}, + {"hmassD0", "D0 candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}, + {"hmassJpsi", "Jpsi candidates;inv. mass (e+ e-) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}, + //3prong histograms + {"hvtx3_x", "3-prong candidates;#it{x}_{sec. vtx.} (cm);entries", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"hvtx3_y", "3-prong candidates;#it{y}_{sec. vtx.} (cm);entries", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"hvtx3_z", "3-prong candidates;#it{z}_{sec. vtx.} (cm);entries", {HistType::kTH1F, {{1000, -20., 20.}}}}, + {"hNCand3Prong", "3-prong candidates preselected;# of candidates;entries", {HistType::kTH1F, {{5000, 0., 500000.}}}}, + {"hNCand3ProngVsNTracks", "3-prong candidates preselected;# of selected tracks;# of candidates;entries", {HistType::kTH2F, {{2500, 0., 25000.}, {5000, 0., 500000.}}}}, + {"hmassDPlus", "D+ candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}, + {"hmassLc", "Lc candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}, + {"hmassDs", "Ds candidates;inv. mass (K K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}}}; + + Filter filterSelectTracks = (aod::hf_seltrack::isSelProng > 0); + using SelectedTracks = soa::Filtered<soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra, aod::HFSelTrack>>; + + // FIXME + //Partition<SelectedTracks> tracksPos = aod::track::signed1Pt > 0.f; + //Partition<SelectedTracks> tracksNeg = aod::track::signed1Pt < 0.f; + + double massPi = RecoDecay::getMassPDG(kPiPlus); + double massK = RecoDecay::getMassPDG(kKPlus); + double massProton = RecoDecay::getMassPDG(kProton); + double massElectron = RecoDecay::getMassPDG(kElectron); + + // int nColls{0}; //can be added to run over limited collisions per file - for tesing purposes + + void process( //soa::Join<aod::Collisions, aod::Cents>::iterator const& collision, //FIXME add centrality when option for variations to the process function appears + aod::Collision const& collision, + aod::BCs const& bcs, + SelectedTracks const& tracks) + { + + //can be added to run over limited collisions per file - for tesing purposes + /* + if (nCollsMax > -1){ + if (nColls == nCollMax){ + return; + //can be added to run over limited collisions per file - for tesing purposes + } + nColls++; + } + */ + + //auto centrality = collision.centV0M(); //FIXME add centrality when option for variations to the process function appears + + int trigindex = int{triggerindex}; + if (trigindex != -1) { + uint64_t triggerMask = collision.bc().triggerMask(); + bool isTriggerClassFired = triggerMask & 1ul << (trigindex - 1); + if (!isTriggerClassFired) { + return; + } + } + + //FIXME move above process function + const int n2ProngDecays = 2; //number of two prong hadron types + const int n3ProngDecays = 3; //number of three prong hadron types + int n2ProngBit = TMath::Power(2, n2ProngDecays) - 1; //bit value for 2 prongs candidates where each candidiate is one bit and they are all set it 1 + int n3ProngBit = TMath::Power(2, n3ProngDecays) - 1; //bit value for 3 prongs candidates where each candidiate is one bit and they are all set it 1 + + //retrieve cuts from json - to be made pT dependent when option appears in json + const int nCuts2Prong = 4; //how mant different selections are made on 2 prongs + double cut2ProngPtCandMin[n2ProngDecays] = {configs->mPtD0Min, configs->mPtJpsiMin}; + double cut2ProngInvMassCandMin[n2ProngDecays] = {configs->mInvMassD0Min, configs->mInvMassJpsiMin}; + double cut2ProngInvMassCandMax[n2ProngDecays] = {configs->mInvMassD0Max, configs->mInvMassJpsiMax}; + double cut2ProngCPACandMin[n2ProngDecays] = {configs->mCPAD0Min, configs->mCPAJpsiMin}; + double cut2ProngImpParProductCandMax[n2ProngDecays] = {configs->mImpParProductD0Max, configs->mImpParProductJpsiMax}; + + const int nCuts3Prong = 4; //how mant different selections are made on 3 prongs + double cut3ProngPtCandMin[n3ProngDecays] = {configs->mPtDPlusMin, configs->mPtLcMin, configs->mPtDsMin}; + double cut3ProngInvMassCandMin[n3ProngDecays] = {configs->mInvMassDPlusMin, configs->mInvMassLcMin, configs->mInvMassDsMin}; + double cut3ProngInvMassCandMax[n3ProngDecays] = {configs->mInvMassDPlusMax, configs->mInvMassLcMax, configs->mInvMassDsMax}; + double cut3ProngCPACandMin[n3ProngDecays] = {configs->mCPADPlusMin, configs->mCPALcMin, configs->mCPADsMin}; + double cut3ProngDecLenCandMin[n3ProngDecays] = {configs->mDecLenDPlusMin, configs->mDecLenLcMin, configs->mDecLenDsMin}; + + bool cutStatus2Prong[n2ProngDecays][nCuts2Prong]; + bool cutStatus3Prong[n3ProngDecays][nCuts3Prong]; + int nCutStatus2ProngBit = TMath::Power(2, nCuts2Prong) - 1; //bit value for selection status for each 2 prongs candidate where each selection is one bit and they are all set it 1 + int nCutStatus3ProngBit = TMath::Power(2, nCuts3Prong) - 1; //bit value for selection status for each 2 prongs candidate where each selection is one bit and they are all set it 1 + + //set mass hypothesis 1 for 2 prong + auto arr2Mass1 = array{ + array{massPi, massK}, + array{massElectron, massElectron}}; + + //set mass hypothesis 2 for 2 prong + auto arr2Mass2 = array{ + array{massK, massPi}, + array{massElectron, massElectron}}; + + //set mass hypothesis 1 for 3 prong + auto arr3Mass1 = array{ + array{massPi, massK, massPi}, + array{massProton, massK, massPi}, + array{massK, massK, massPi}}; + + //set mass hypothesis 2 for 3 prong + auto arr3Mass2 = array{ + array{massPi, massK, massPi}, + array{massPi, massK, massProton}, + array{massPi, massK, massK}}; + + double mass2ProngHypo1[n2ProngDecays]; + double mass2ProngHypo2[n2ProngDecays]; + + double mass3ProngHypo1[n3ProngDecays]; + double mass3ProngHypo2[n3ProngDecays]; + + // 2-prong vertex fitter + o2::vertexing::DCAFitterN<2> df2; + df2.setBz(d_bz); + df2.setPropagateToPCA(b_propdca); + df2.setMaxR(d_maxr); + df2.setMaxDZIni(d_maxdzini); + df2.setMinParamChange(d_minparamchange); + df2.setMinRelChi2Change(d_minrelchi2change); + df2.setUseAbsDCA(true); + + // 3-prong vertex fitter + o2::vertexing::DCAFitterN<3> df3; + df3.setBz(d_bz); + df3.setPropagateToPCA(b_propdca); + df3.setMaxR(d_maxr); + df3.setMaxDZIni(d_maxdzini); + df3.setMinParamChange(d_minparamchange); + df3.setMinRelChi2Change(d_minrelchi2change); + df3.setUseAbsDCA(true); + + //used to calculate number of candidiates per event + auto nCand2 = rowTrackIndexProng2.lastIndex(); + auto nCand3 = rowTrackIndexProng3.lastIndex(); + + // first loop over positive tracks + //for (auto trackPos1 = tracksPos.begin(); trackPos1 != tracksPos.end(); ++trackPos1) { + for (auto trackPos1 = tracks.begin(); trackPos1 != tracks.end(); ++trackPos1) { + if (trackPos1.signed1Pt() < 0) { + continue; + } + bool sel2ProngStatus = true; + bool sel3ProngStatusPos1 = trackPos1.isSelProng() & (1 << 1); + if (!(trackPos1.isSelProng() & (1 << 0))) { + sel2ProngStatus = false; + } + if (!sel2ProngStatus && !sel3ProngStatusPos1) { + continue; + } + + auto trackParVarPos1 = getTrackParCov(trackPos1); + + // first loop over negative tracks + //for (auto trackNeg1 = tracksNeg.begin(); trackNeg1 != tracksNeg.end(); ++trackNeg1) { + for (auto trackNeg1 = tracks.begin(); trackNeg1 != tracks.end(); ++trackNeg1) { + if (trackNeg1.signed1Pt() > 0) { + continue; + } + bool sel3ProngStatusNeg1 = trackNeg1.isSelProng() & (1 << 1); + if (sel2ProngStatus && !(trackNeg1.isSelProng() & (1 << 0))) { + sel2ProngStatus = false; + } + if (!sel2ProngStatus && !sel3ProngStatusNeg1) { + continue; + } + + auto trackParVarNeg1 = getTrackParCov(trackNeg1); + + int isSelected2ProngCand = n2ProngBit; //bitmap for checking status of two-prong candidates (1 is true, 0 is rejected) + + if (b_debug) { + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + for (int n2cut = 0; n2cut < nCuts2Prong; n2cut++) { + cutStatus2Prong[n2][n2cut] = true; + } + } + } + int iDebugCut = 0; + + // 2prong invariant-mass cut + if (sel2ProngStatus > 0) { + auto arrMom = array{ + array{trackPos1.px(), trackPos1.py(), trackPos1.pz()}, + array{trackNeg1.px(), trackNeg1.py(), trackNeg1.pz()}}; + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + mass2ProngHypo1[n2] = RecoDecay::M(arrMom, arr2Mass1[n2]); + mass2ProngHypo2[n2] = RecoDecay::M(arrMom, arr2Mass2[n2]); + if ((b_debug || (isSelected2ProngCand & 1 << n2)) && cut2ProngInvMassCandMin[n2] >= 0. && cut2ProngInvMassCandMax[n2] > 0.) { //no need to check isSelected2Prong but to avoid mistakes + if ((mass2ProngHypo1[n2] < cut2ProngInvMassCandMin[n2] || mass2ProngHypo1[n2] >= cut2ProngInvMassCandMax[n2]) && + (mass2ProngHypo2[n2] < cut2ProngInvMassCandMin[n2] || mass2ProngHypo2[n2] >= cut2ProngInvMassCandMax[n2])) { + isSelected2ProngCand = isSelected2ProngCand & ~(1 << n2); + if (b_debug) { + cutStatus2Prong[n2][iDebugCut] = false; + } + } + } + } + iDebugCut++; + + //secondary vertex reconstruction and further 2 prong selections + if (isSelected2ProngCand > 0 && df2.process(trackParVarPos1, trackParVarNeg1) > 0) { //should it be this or > 0 or are they equivalent + // get secondary vertex + const auto& secondaryVertex2 = df2.getPCACandidate(); + // get track momenta + array<float, 3> pvec0; + array<float, 3> pvec1; + df2.getTrack(0).getPxPyPzGlo(pvec0); + df2.getTrack(1).getPxPyPzGlo(pvec1); + + auto pVecCandProng2 = RecoDecay::PVec(pvec0, pvec1); + + // candidate pT cut + if ((b_debug || isSelected2ProngCand > 0) && (std::count_if(std::begin(cut2ProngPtCandMin), std::end(cut2ProngPtCandMin), [](double d) { return d >= 0.; }) > 0)) { + double cand2ProngPt = RecoDecay::Pt(pVecCandProng2); + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + if ((b_debug || (isSelected2ProngCand & 1 << n2)) && cand2ProngPt < cut2ProngPtCandMin[n2]) { + isSelected2ProngCand = isSelected2ProngCand & ~(1 << n2); + if (b_debug) { + cutStatus2Prong[n2][iDebugCut] = false; + } + } + } + } + iDebugCut++; + + // imp. par. product cut + if ((b_debug || isSelected2ProngCand > 0) && (std::count_if(std::begin(cut2ProngImpParProductCandMax), std::end(cut2ProngImpParProductCandMax), [](double d) { return d < 100.; }) > 0)) { + auto impParProduct = trackPos1.dcaPrim0() * trackNeg1.dcaPrim0(); + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + if ((b_debug || (isSelected2ProngCand & 1 << n2)) && impParProduct > cut2ProngImpParProductCandMax[n2]) { + isSelected2ProngCand = isSelected2ProngCand & ~(1 << n2); + if (b_debug) { + cutStatus2Prong[n2][iDebugCut] = false; + } + } + } + } + iDebugCut++; + + // CPA cut + if ((b_debug || isSelected2ProngCand > 0) && (std::count_if(std::begin(cut2ProngCPACandMin), std::end(cut2ProngCPACandMin), [](double d) { return d > -2.; }) > 0)) { + auto cpa = RecoDecay::CPA(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex2, pVecCandProng2); + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + if ((b_debug || (isSelected2ProngCand & 1 << n2)) && cpa < cut2ProngCPACandMin[n2]) { + isSelected2ProngCand = isSelected2ProngCand & ~(1 << n2); + if (b_debug) { + cutStatus2Prong[n2][iDebugCut] = false; + } + } + } + } + iDebugCut++; + + if (isSelected2ProngCand > 0) { + // fill table row + rowTrackIndexProng2(trackPos1.globalIndex(), + trackNeg1.globalIndex(), isSelected2ProngCand); + if (b_debug) { + int Prong2CutStatus[n2ProngDecays]; + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + Prong2CutStatus[n2] = nCutStatus2ProngBit; + for (int n2cut = 0; n2cut < nCuts2Prong; n2cut++) { + if (!cutStatus2Prong[n2][n2cut]) { + Prong2CutStatus[n2] = Prong2CutStatus[n2] & ~(1 << n2cut); + } + } + } + rowProng2CutStatus(Prong2CutStatus[0], Prong2CutStatus[1]); //FIXME when we can do this by looping over n2ProngDecays + } + + // fill histograms + if (b_dovalplots) { + + registry.get<TH1>(HIST("hvtx2_x"))->Fill(secondaryVertex2[0]); + registry.get<TH1>(HIST("hvtx2_y"))->Fill(secondaryVertex2[1]); + registry.get<TH1>(HIST("hvtx2_z"))->Fill(secondaryVertex2[2]); + arrMom = array{pvec0, pvec1}; + for (int n2 = 0; n2 < n2ProngDecays; n2++) { + if (isSelected2ProngCand & 1 << n2) { + if ((cut2ProngInvMassCandMin[n2] < 0. && cut2ProngInvMassCandMax[n2] <= 0.) || (mass2ProngHypo1[n2] >= cut2ProngInvMassCandMin[n2] && mass2ProngHypo1[n2] < cut2ProngInvMassCandMax[n2])) { + mass2ProngHypo1[n2] = RecoDecay::M(arrMom, arr2Mass1[n2]); + if (n2 == 0) { + registry.get<TH1>(HIST("hmassD0"))->Fill(mass2ProngHypo1[n2]); + } + if (n2 == 1) { + registry.get<TH1>(HIST("hmassJpsi"))->Fill(mass2ProngHypo1[n2]); + } + } + if ((cut2ProngInvMassCandMin[n2] < 0. && cut2ProngInvMassCandMax[n2] <= 0.) || (mass2ProngHypo2[n2] >= cut2ProngInvMassCandMin[n2] && mass2ProngHypo2[n2] < cut2ProngInvMassCandMax[n2])) { + mass2ProngHypo2[n2] = RecoDecay::M(arrMom, arr2Mass2[n2]); + if (n2 == 0) { + registry.get<TH1>(HIST("hmassD0"))->Fill(mass2ProngHypo1[n2]); + } + } + } + } + } + } + } + } + + // 3-prong vertex reconstruction + if (do3prong == 1) { + if (!sel3ProngStatusPos1 || !sel3ProngStatusNeg1) { + continue; + } + + // second loop over positive tracks + //for (auto trackPos2 = trackPos1 + 1; trackPos2 != tracksPos.end(); ++trackPos2) { + for (auto trackPos2 = trackPos1 + 1; trackPos2 != tracks.end(); ++trackPos2) { + if (trackPos2.signed1Pt() < 0) { + continue; + } + if (!(trackPos2.isSelProng() & (1 << 1))) { + continue; + } + + int isSelected3ProngCand = n3ProngBit; + + if (b_debug) { + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + for (int n3cut = 0; n3cut < nCuts3Prong; n3cut++) { + cutStatus3Prong[n3][n3cut] = true; + } + } + } + int iDebugCut = 0; + + // 3prong invariant-mass cut + auto arr3Mom = array{ + array{trackPos1.px(), trackPos1.py(), trackPos1.pz()}, + array{trackNeg1.px(), trackNeg1.py(), trackNeg1.pz()}, + array{trackPos2.px(), trackPos2.py(), trackPos2.pz()}}; + + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + mass3ProngHypo1[n3] = RecoDecay::M(arr3Mom, arr3Mass1[n3]); + mass3ProngHypo2[n3] = RecoDecay::M(arr3Mom, arr3Mass2[n3]); + if ((isSelected3ProngCand & 1 << n3) && cut3ProngInvMassCandMin[n3] >= 0. && cut3ProngInvMassCandMax[n3] > 0.) { + if ((mass3ProngHypo1[n3] < cut3ProngInvMassCandMin[n3] || mass3ProngHypo1[n3] >= cut3ProngInvMassCandMax[n3]) && + (mass3ProngHypo2[n3] < cut3ProngInvMassCandMin[n3] || mass3ProngHypo2[n3] >= cut3ProngInvMassCandMax[n3])) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + iDebugCut++; + + // reconstruct the 3-prong secondary vertex + auto trackParVarPos2 = getTrackParCov(trackPos2); + if (df3.process(trackParVarPos1, trackParVarNeg1, trackParVarPos2) == 0) { + continue; + } + // get secondary vertex + const auto& secondaryVertex3 = df3.getPCACandidate(); + // get track momenta + array<float, 3> pvec0; + array<float, 3> pvec1; + array<float, 3> pvec2; + df3.getTrack(0).getPxPyPzGlo(pvec0); + df3.getTrack(1).getPxPyPzGlo(pvec1); + df3.getTrack(2).getPxPyPzGlo(pvec2); + + auto pVecCandProng3Pos = RecoDecay::PVec(pvec0, pvec1, pvec2); + + // candidate pT cut + if (std::count_if(std::begin(cut3ProngPtCandMin), std::end(cut3ProngPtCandMin), [](double d) { return d >= 0.; }) > 0) { + double cand3ProngPt = RecoDecay::Pt(pVecCandProng3Pos); + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if (cand3ProngPt < cut3ProngPtCandMin[n3]) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + } + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; //this and all further instances should be changed if 4 track loop is added + } + } + iDebugCut++; + + // CPA cut + if (std::count_if(std::begin(cut3ProngCPACandMin), std::end(cut3ProngCPACandMin), [](double d) { return d > -2.; }) > 0) { + auto cpa = RecoDecay::CPA(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex3, pVecCandProng3Pos); + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if ((isSelected3ProngCand & 1 << n3) && cpa < cut3ProngCPACandMin[n3]) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + } + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + } + iDebugCut++; + + // decay length cut + if (std::count_if(std::begin(cut3ProngDecLenCandMin), std::end(cut3ProngDecLenCandMin), [](double d) { return d > 0.; }) > 0) { + auto decayLength = RecoDecay::distance(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex3); + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if ((isSelected3ProngCand & 1 << n3) && decayLength < cut3ProngDecLenCandMin[n3]) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + } + iDebugCut++; + + // fill table row + rowTrackIndexProng3(trackPos1.globalIndex(), + trackNeg1.globalIndex(), + trackPos2.globalIndex(), isSelected3ProngCand); + + if (b_debug) { + int Prong3CutStatus[n3ProngDecays]; + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + Prong3CutStatus[n3] = nCutStatus3ProngBit; + for (int n3cut = 0; n3cut < nCuts3Prong; n3cut++) { + if (!cutStatus3Prong[n3][n3cut]) { + Prong3CutStatus[n3] = Prong3CutStatus[n3] & ~(1 << n3cut); + } + } + } + rowProng3CutStatus(Prong3CutStatus[0], Prong3CutStatus[1], Prong3CutStatus[2]); //FIXME when we can do this by looping over n3ProngDecays + } + + // fill histograms + if (b_dovalplots) { + + registry.get<TH1>(HIST("hvtx3_x"))->Fill(secondaryVertex3[0]); + registry.get<TH1>(HIST("hvtx3_y"))->Fill(secondaryVertex3[1]); + registry.get<TH1>(HIST("hvtx3_z"))->Fill(secondaryVertex3[2]); + arr3Mom = array{pvec0, pvec1, pvec2}; + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if (isSelected3ProngCand & 1 << n3) { + if ((cut3ProngInvMassCandMin[n3] < 0. && cut3ProngInvMassCandMax[n3] <= 0.) || (mass3ProngHypo1[n3] >= cut3ProngInvMassCandMin[n3] && mass3ProngHypo1[n3] < cut3ProngInvMassCandMax[n3])) { + mass3ProngHypo1[n3] = RecoDecay::M(arr3Mom, arr3Mass1[n3]); + if (n3 == 0) { + registry.get<TH1>(HIST("hmassDPlus"))->Fill(mass3ProngHypo1[n3]); + } + if (n3 == 1) { + registry.get<TH1>(HIST("hmassLc"))->Fill(mass3ProngHypo1[n3]); + } + if (n3 == 2) { + registry.get<TH1>(HIST("hmassDs"))->Fill(mass3ProngHypo1[n3]); + } + } + if ((cut3ProngInvMassCandMin[n3] < 0. && cut3ProngInvMassCandMax[n3] <= 0.) || (mass3ProngHypo2[n3] >= cut3ProngInvMassCandMin[n3] && mass3ProngHypo2[n3] < cut3ProngInvMassCandMax[n3])) { + mass3ProngHypo2[n3] = RecoDecay::M(arr3Mom, arr3Mass2[n3]); + if (n3 == 1) { + registry.get<TH1>(HIST("hmassLc"))->Fill(mass3ProngHypo2[n3]); + } + if (n3 == 2) { + registry.get<TH1>(HIST("hmassDs"))->Fill(mass3ProngHypo2[n3]); + } + } + } + } + } + } + + // second loop over negative tracks + //for (auto trackNeg2 = trackNeg1 + 1; trackNeg2 != tracksNeg.end(); ++trackNeg2) { + for (auto trackNeg2 = trackNeg1 + 1; trackNeg2 != tracks.end(); ++trackNeg2) { + if (trackNeg2.signed1Pt() > 0) { + continue; + } + if (!(trackNeg2.isSelProng() & (1 << 1))) { + continue; + } + + int isSelected3ProngCand = n3ProngBit; + + if (b_debug) { + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + for (int n3cut = 0; n3cut < nCuts3Prong; n3cut++) { + cutStatus3Prong[n3][n3cut] = true; + } + } + } + int iDebugCut = 0; + + // 3prong invariant-mass cut + auto arr3Mom = array{ + array{trackNeg1.px(), trackNeg1.py(), trackNeg1.pz()}, + array{trackPos1.px(), trackPos1.py(), trackPos1.pz()}, + array{trackNeg2.px(), trackNeg2.py(), trackNeg2.pz()}}; + + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + mass3ProngHypo1[n3] = RecoDecay::M(arr3Mom, arr3Mass1[n3]); + mass3ProngHypo2[n3] = RecoDecay::M(arr3Mom, arr3Mass2[n3]); + if ((isSelected3ProngCand & 1 << n3) && cut3ProngInvMassCandMin[n3] >= 0. && cut3ProngInvMassCandMax[n3] > 0.) { + if ((mass3ProngHypo1[n3] < cut3ProngInvMassCandMin[n3] || mass3ProngHypo1[n3] >= cut3ProngInvMassCandMax[n3]) && + (mass3ProngHypo2[n3] < cut3ProngInvMassCandMin[n3] || mass3ProngHypo2[n3] >= cut3ProngInvMassCandMax[n3])) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + iDebugCut++; + + // reconstruct the 3-prong secondary vertex + auto trackParVarNeg2 = getTrackParCov(trackNeg2); + if (df3.process(trackParVarNeg1, trackParVarPos1, trackParVarNeg2) == 0) { + continue; + } + + // get secondary vertex + const auto& secondaryVertex3 = df3.getPCACandidate(); + // get track momenta + array<float, 3> pvec0; + array<float, 3> pvec1; + array<float, 3> pvec2; + df3.getTrack(0).getPxPyPzGlo(pvec0); + df3.getTrack(1).getPxPyPzGlo(pvec1); + df3.getTrack(2).getPxPyPzGlo(pvec2); + + auto pVecCandProng3Neg = RecoDecay::PVec(pvec0, pvec1, pvec2); + + // candidate pT cut + if (std::count_if(std::begin(cut3ProngPtCandMin), std::end(cut3ProngPtCandMin), [](double d) { return d >= 0.; }) > 0) { + double cand3ProngPt = RecoDecay::Pt(pVecCandProng3Neg); + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if (cand3ProngPt < cut3ProngPtCandMin[n3]) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + } + iDebugCut++; + + // CPA cut + if (std::count_if(std::begin(cut3ProngCPACandMin), std::end(cut3ProngCPACandMin), [](double d) { return d > -2.; }) > 0) { + auto cpa = RecoDecay::CPA(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex3, pVecCandProng3Neg); + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if ((isSelected3ProngCand & 1 << n3) && cpa < cut3ProngCPACandMin[n3]) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + } + iDebugCut++; + + // decay length cut + if (std::count_if(std::begin(cut3ProngDecLenCandMin), std::end(cut3ProngDecLenCandMin), [](double d) { return d > 0.; }) > 0) { + auto decayLength = RecoDecay::distance(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex3); + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if ((isSelected3ProngCand & 1 << n3) && decayLength < cut3ProngDecLenCandMin[n3]) { + isSelected3ProngCand = isSelected3ProngCand & ~(1 << n3); + if (b_debug) { + cutStatus3Prong[n3][iDebugCut] = false; + } + } + } + if (!b_debug && isSelected3ProngCand == 0) { + continue; + } + } + iDebugCut++; + + // fill table row + rowTrackIndexProng3(trackNeg1.globalIndex(), + trackPos1.globalIndex(), + trackNeg2.globalIndex(), isSelected3ProngCand); + + if (b_debug) { + int Prong3CutStatus[n3ProngDecays]; + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + Prong3CutStatus[n3] = nCutStatus3ProngBit; + for (int n3cut = 0; n3cut < nCuts3Prong; n3cut++) { + if (!cutStatus3Prong[n3][n3cut]) { + Prong3CutStatus[n3] = Prong3CutStatus[n3] & ~(1 << n3cut); + } + } + } + rowProng3CutStatus(Prong3CutStatus[0], Prong3CutStatus[1], Prong3CutStatus[2]); //FIXME when we can do this by looping over n3ProngDecays + } + + // fill histograms + if (b_dovalplots) { + + registry.get<TH1>(HIST("hvtx3_x"))->Fill(secondaryVertex3[0]); + registry.get<TH1>(HIST("hvtx3_y"))->Fill(secondaryVertex3[1]); + registry.get<TH1>(HIST("hvtx3_z"))->Fill(secondaryVertex3[2]); + arr3Mom = array{pvec0, pvec1, pvec2}; + for (int n3 = 0; n3 < n3ProngDecays; n3++) { + if (isSelected3ProngCand & 1 << n3) { + if ((cut3ProngInvMassCandMin[n3] < 0. && cut3ProngInvMassCandMax[n3] <= 0.) || (mass3ProngHypo1[n3] >= cut3ProngInvMassCandMin[n3] && mass3ProngHypo1[n3] < cut3ProngInvMassCandMax[n3])) { + mass3ProngHypo1[n3] = RecoDecay::M(arr3Mom, arr3Mass1[n3]); + if (n3 == 0) { + registry.get<TH1>(HIST("hmassDPlus"))->Fill(mass3ProngHypo1[n3]); + } + if (n3 == 1) { + registry.get<TH1>(HIST("hmassLc"))->Fill(mass3ProngHypo1[n3]); + } + if (n3 == 2) { + registry.get<TH1>(HIST("hmassDs"))->Fill(mass3ProngHypo1[n3]); + } + } + if ((cut3ProngInvMassCandMin[n3] < 0. && cut3ProngInvMassCandMax[n3] <= 0.) || (mass3ProngHypo2[n3] >= cut3ProngInvMassCandMin[n3] && mass3ProngHypo2[n3] < cut3ProngInvMassCandMax[n3])) { + mass3ProngHypo2[n3] = RecoDecay::M(arr3Mom, arr3Mass2[n3]); + if (n3 == 1) { + registry.get<TH1>(HIST("hmassLc"))->Fill(mass3ProngHypo2[n3]); + } + if (n3 == 2) { + registry.get<TH1>(HIST("hmassDs"))->Fill(mass3ProngHypo2[n3]); + } + } + } + } + } + } + } + } + } + + auto nTracks = tracks.size(); // number of tracks passing 2 and 3 prong selection in this collision + nCand2 = rowTrackIndexProng2.lastIndex() - nCand2; // number of 2-prong candidates in this collision + nCand3 = rowTrackIndexProng3.lastIndex() - nCand3; // number of 3-prong candidates in this collision + + registry.get<TH1>(HIST("hNTracks"))->Fill(nTracks); + registry.get<TH1>(HIST("hNCand2Prong"))->Fill(nCand2); + registry.get<TH1>(HIST("hNCand3Prong"))->Fill(nCand3); + registry.get<TH2>(HIST("hNCand2ProngVsNTracks"))->Fill(nTracks, nCand2); + registry.get<TH2>(HIST("hNCand3ProngVsNTracks"))->Fill(nTracks, nCand3); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<SelectTracks>("hf-produce-sel-track"), + adaptAnalysisTask<HFTrackIndexSkimsCreator>("hf-track-index-skims-creator")}; +} diff --git a/Analysis/Tasks/PWGHF/qaTask.cxx b/Analysis/Tasks/PWGHF/qaTask.cxx new file mode 100644 index 0000000000000..e6fa820857843 --- /dev/null +++ b/Analysis/Tasks/PWGHF/qaTask.cxx @@ -0,0 +1,445 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Peter Hristov <Peter.Hristov@cern.ch>, CERN +/// \author Gian Michele Innocenti <gian.michele.innocenti@cern.ch>, CERN +/// \author Henrique J C Zanoli <henrique.zanoli@cern.ch>, Utrecht University +/// \author Nicolo' Jacazio <nicolo.jacazio@cern.ch>, CERN + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/HistogramRegistry.h" +#include "AnalysisCore/trackUtilities.h" +#include "ReconstructionDataFormats/DCA.h" + +#include "TH1D.h" + +#include <cmath> +#include <string> +#include <sstream> +#include "boost/algorithm/string.hpp" + +using namespace o2::framework; +namespace o2fw = o2::framework; +namespace o2exp = o2::framework::expressions; +namespace o2df = o2::dataformats; + +namespace track_utils +{ +/// Converts a angle phi to the -pi to pi range. +double ConvertPhiRange(double phi) +{ + if (phi > M_PI) { + phi -= 2 * M_PI; + } else if (phi < -M_PI) { + phi += 2 * M_PI; + } + + return phi; +} + +/// Determines the impact parameter and its error for a given track. +/// \param track the track to get the impact parameter from. +/// \param primaryVertex the primary vertex of th collision. +/// \param impactParameterRPhi variable to save the impact parameter (in r phi) in micrometers. +/// \param impactParameterRPhiError variable to save the impact parameter (in r phi) error in micrometers. +/// \param impactParameterZ variable to save the impact parameter (in Z) in micrometers. +/// \param impactParameterZError variable to save the impact parameter (in Z) error in micrometers. +template <typename Track> +bool GetImpactParameterAndError(const Track& track, const o2df::VertexBase& primaryVertex, double& impactParameterRPhi, + double& impactParameterRPhiError, double& impactParameterZ, + double& impactParameterErrorZ) +{ + impactParameterRPhi = -999.; + impactParameterRPhiError = -999.; + impactParameterZ = -999; + impactParameterErrorZ = -999; + + o2df::DCA dca; + // FIXME: get this from CCDB + constexpr float magneticField{5.0}; // in kG + auto trackParameter = getTrackParCov(track); + bool propagate = trackParameter.propagateToDCA(primaryVertex, magneticField, &dca); + + constexpr float conversion_to_micrometer = 1000; + if (propagate) { + impactParameterRPhi = conversion_to_micrometer * dca.getY(); + impactParameterRPhiError = conversion_to_micrometer * std::sqrt(dca.getSigmaY2()); + impactParameterZ = conversion_to_micrometer * dca.getZ(); + impactParameterErrorZ = conversion_to_micrometer * std::sqrt(dca.getSigmaZ2()); + } + return propagate; +} +} // namespace track_utils + +namespace o2::qa::features +{ + +/// Class to abstract the naming of a particular feature. It can help you to build the histogram +/// labels in a consistent way and generate the titles. +class Feature +{ + public: + Feature() = default; + Feature(std::string name, std::string unit = "") : mName(std::move(name)), mUnit(std::move(unit)){}; + + /// Returns the feature tagged as MC with no unit. Example: p_{T}^{MC}. + std::string MCRaw() const { return mName + "^{MC}"; }; + + /// Returns the feature tagged as Reconstructed with no unit. Example: p_{T}^{Rec}. + std::string RecRaw() const { return mName + "^{Rec}"; }; + + /// Returns the unit with no brackets. Example: GeV/c. + std::string UnitRaw() const { return mUnit; }; + + /// Returns the name with no units. Example: p_{T}. + std::string NameRaw() const { return mName; }; + + /// Returns the name with the unit. Example: p_{T} [Gev/c]. + std::string Name() const { return mName + Unit(); }; + + /// Returns the name tagged as MC with the unit. Example: p_{T}^{MC} [Gev/c]. + std::string MC() const { return MCRaw() + Unit(); }; + + /// Returns the name tagged as Reconstructed with the unit. Example: p_{T}^{Rec} [Gev/c] + std::string Rec() const { return RecRaw() + Unit(); }; + + /// Returns the name difference between the MC and reconstructed with the unit. + /// Example: p_{T}^{MC} - p_{T}^{Rec} [Gev/c] + std::string MCRecDiff() const { return MCRaw() + " - " + RecRaw() + Unit(); }; + + /// Returns the name difference between the MC and reconstructed divided by the reconstructed. + /// For example: (p_{T}^{MC} - p_{T}^{Rec})/p_{T}^{Rec} + std::string RelativeMCRecDiff() const { return "(" + MCRaw() + " - " + RecRaw() + ")/" + RecRaw(); }; + + /// Returns the unit formatted to be used in the axis. If no unit is present, returns an empty value. + /// Example: [GeV/c] + std::string Unit() const + { + if (UnitRaw().empty()) { + return ""; + } + return " [" + UnitRaw() + "]"; + }; + + operator std::string() { return Name(); } + + private: + const std::string mName; + const std::string mUnit; +}; + +/// Makes a title for an histogram +std::string MakeTitle(std::vector<std::string> axisTitles, const std::string& counts = "Counts") +{ + axisTitles.push_back(counts); + return ";" + boost::algorithm::join(axisTitles, ";"); +} + +Feature Eta("#eta"); +Feature TrackMultiplicity("Track Multiplicity"); +Feature Phi{"#varphi", "rad"}; +Feature Pt("p_{T}", "GeV/c"); +Feature VertexX("X", "cm"); +Feature VertexY("Y", "cm"); +Feature VertexZ("Z", "cm"); +Feature ImpactParameterRPhi("Impact Parameter r#varphi", "#mum"); +Feature ImpactParameterRPhiError("Impact Parameter Error r#varphi", "#mum"); +Feature ImpactParameterZ("Impact Parameter Z", "#mum"); +Feature ImpactParameterZError("Impact Parameter Z Error", "#mum"); +} // namespace o2::qa::features + +namespace qafeat = o2::qa::features; + +/// Task to QA global observables of the event +struct QAGlobalObservables { + o2fw::Configurable<int> nBinsNumberOfTracks{"nBinsNumberOfTracks", 2000, "Number of bins for the Number of Tracks"}; + o2fw::Configurable<int> nBinsVertexPosition{"nBinsPt", 100, "Number of bins for the Vertex Position"}; + + std::array<float, 2> collisionZRange = {-20., 20.}; + std::array<float, 2> collisionXYRange = {-0.01, 0.01}; + + std::array<float, 2> numberOfTracksRange = {0, 400}; + + o2fw::OutputObj<TH1D> eventCount{TH1D("eventCount", qafeat::MakeTitle({"Selected Events"}).c_str(), 2, 0, 2)}; + o2fw::HistogramRegistry histograms{"HistogramsGlobalQA"}; + + void init(o2fw::InitContext&) + { + histograms.add("collision/collisionX", qafeat::MakeTitle({qafeat::VertexX}).c_str(), o2fw::kTH1D, + {{nBinsVertexPosition, collisionXYRange[0], collisionXYRange[1]}}); + + histograms.add("collision/collisionY", qafeat::MakeTitle({qafeat::VertexY}).c_str(), o2fw::kTH1D, + {{nBinsVertexPosition, collisionXYRange[0], collisionXYRange[1]}}); + + histograms.add("collision/collisionZ", qafeat::MakeTitle({qafeat::VertexZ}).c_str(), o2fw::kTH1D, + {{nBinsVertexPosition, collisionZRange[0], collisionZRange[1]}}); + + histograms.add("multiplicity/numberOfTracks", qafeat::MakeTitle({qafeat::TrackMultiplicity}).c_str(), o2fw::kTH1D, + {{nBinsNumberOfTracks, numberOfTracksRange[0], numberOfTracksRange[1]}}); + + // covariance histograms + histograms.add("Covariance/xx", "xx;Cov_{xx} [cm^{2}]", kTH1D, {{200, -0.1, 0.1}}); + histograms.add("Covariance/xy", "xy;Cov_{xy} [cm^{2}]", kTH1D, {{200, -0.1, 0.1}}); + histograms.add("Covariance/xz", "xz;Cov_{xz} [cm^{2}]", kTH1D, {{200, -0.1, 0.1}}); + histograms.add("Covariance/yy", "yy;Cov_{yy} [cm^{2}]", kTH1D, {{200, -0.1, 0.1}}); + histograms.add("Covariance/yz", "yz;Cov_{yz} [cm^{2}]", kTH1D, {{200, -0.1, 0.1}}); + histograms.add("Covariance/zz", "zz;Cov_{zz} [cm^{2}]", kTH1D, {{200, -0.1, 0.1}}); + + // quality histograms + histograms.add("Quality/Chi2", "#Chi^{2};#Chi^{2}", kTH1D, {{100, 0, 10}}); + histograms.add("Quality/Contributors", "Contributors;Contributors", kTH1D, {{100, 0, 100}}); + } + + void process(const o2::aod::Collision& collision, const o2::aod::Tracks& tracks) + { + eventCount->Fill(0); + histograms.fill(HIST("collision/collisionX"), collision.posX()); + histograms.fill(HIST("collision/collisionY"), collision.posY()); + histograms.fill(HIST("collision/collisionZ"), collision.posZ()); + + int nTracks(0); + for (const auto& track : tracks) { + nTracks++; + } + + histograms.fill(HIST("multiplicity/numberOfTracks"), nTracks); + + // fill covariance variables + histograms.fill(HIST("Covariance/xx"), collision.covXX()); + histograms.fill(HIST("Covariance/xy"), collision.covXY()); + histograms.fill(HIST("Covariance/xz"), collision.covXZ()); + histograms.fill(HIST("Covariance/yy"), collision.covYY()); + histograms.fill(HIST("Covariance/yz"), collision.covYZ()); + histograms.fill(HIST("Covariance/zz"), collision.covZZ()); + + // fill quality variables + histograms.fill(HIST("Quality/Chi2"), collision.chi2()); + histograms.fill(HIST("Quality/Contributors"), collision.numContrib()); + } +}; + +/// Task to QA the kinematic properties of the tracks +struct QATrackingKine { + o2fw::Configurable<int> nBinsPt{"nBinsPt", 100, "Number of bins for Pt"}; + std::array<double, 2> ptRange = {0, 10.}; + + o2fw::Configurable<int> nBinsPhi{"nBinsPhi", 100, "Number of bins for Phi"}; + + o2fw::Configurable<int> nBinsEta{"nBinsEta", 100, "Number of bins for the eta histogram."}; + std::array<double, 2> etaRange = {-6, 6}; + + o2fw::HistogramRegistry histos{"HistogramsKineQA"}; + + void init(o2fw::InitContext&) + { + histos.add("tracking/pt", qafeat::MakeTitle({qafeat::Pt}).c_str(), o2fw::kTH1D, + {{nBinsPt, ptRange[0], ptRange[1]}}); + histos.add("tracking/eta", qafeat::MakeTitle({qafeat::Eta.NameRaw()}).c_str(), o2fw::kTH1D, + {{nBinsEta, etaRange[0], etaRange[1]}}); + histos.add("tracking/phi", qafeat::MakeTitle({qafeat::Phi}).c_str(), o2fw::kTH1D, {{nBinsPhi, 0, 2 * M_PI}}); + } + + void process(const o2::aod::Track& track) + { + histos.fill(HIST("tracking/eta"), track.eta()); + histos.fill(HIST("tracking/pt"), track.pt()); + histos.fill(HIST("tracking/phi"), track.phi()); + } +}; + +/// Task to evaluate the tracking resolution (Pt, Eta, Phi and impact parameter) +struct QATrackingResolution { + std::vector<double> ptBins = {0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.12, + 0.14, 0.16, 0.18, 0.2, 0.225, 0.25, 0.275, 0.3, 0.35, 0.4, 0.45, 0.5, + 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0, + 3.5, 4., 5., 6., 8., 10., 15., 20., 30., 50., 100.}; + + o2fw::Configurable<int> nBinsEta{"nBinsEta", 60, "Number of bins for the pseudorapidity"}; + std::array<double, 2> etaRange = {-3, 3}; + + o2fw::Configurable<int> nBinsPhi{"nBinsPhi", 50, "Number of bins for Phi"}; + std::array<double, 2> phiRange = {0, 2 * M_PI}; + + o2fw::Configurable<int> nBinsDeltaPt{"nBinsDeltaPt", 100, "Number of bins for the transverse momentum differences"}; + std::array<double, 2> deltaPtRange = {-0.5, 0.5}; + + o2fw::Configurable<int> nBinsDeltaPhi{"nBinsDeltaPhi", 100, "Number of bins for the azimuthal angle differences"}; + std::array<double, 2> deltaPhiRange = {-0.1, 0.1}; + + o2fw::Configurable<int> nBinsDeltaEta{"nBinsDeltaEta", 100, "Number of bins for the pseudorapidity differences"}; + std::array<double, 2> deltaEtaRange = {-0.1, 0.1}; + + o2fw::Configurable<int> nBinsImpactParameter{"nBinsImpactParameter", 1000, "Number of bins for the Impact parameter"}; + + std::array<double, 2> impactParameterRange = {-1500, 1500}; // micrometer + std::array<double, 2> impactParameterResolutionRange = {0, 1000}; // micrometer + + // Registry of histograms + o2fw::HistogramRegistry histos{"HistogramsTrackingResolutionQA"}; + + void init(o2fw::InitContext&) + { + // Histogram axis definitions + + o2fw::AxisSpec ptAxis{ptBins}; + o2fw::AxisSpec deltaPtAxis{nBinsDeltaPt, deltaPtRange[0], deltaPtRange[1]}; + o2fw::AxisSpec deltaPtRelativeAxis{nBinsDeltaPt, deltaPtRange[0], deltaPtRange[1]}; + o2fw::AxisSpec deltaPtAbsoluteRelativeAxis{nBinsDeltaPt, 0., deltaPtRange[1]}; + + o2fw::AxisSpec etaAxis{nBinsEta, etaRange[0], etaRange[1]}; + o2fw::AxisSpec deltaEtaAxis{nBinsDeltaEta, deltaEtaRange[0], deltaEtaRange[1]}; + + o2fw::AxisSpec phiAxis{nBinsPhi, phiRange[0], phiRange[1]}; + o2fw::AxisSpec deltaPhiAxis{nBinsDeltaPhi, deltaPhiRange[0], deltaPhiRange[1]}; + + o2fw::AxisSpec impactParRPhiAxis{nBinsImpactParameter, impactParameterRange[0], impactParameterRange[1]}; + o2fw::AxisSpec impactParRPhiErrorAxis{nBinsImpactParameter, impactParameterResolutionRange[0], + impactParameterResolutionRange[1]}; + + o2fw::AxisSpec impactParZAxis{nBinsImpactParameter, impactParameterRange[0], impactParameterRange[1]}; + o2fw::AxisSpec impactParZErrorAxis{nBinsImpactParameter, impactParameterResolutionRange[0], + impactParameterResolutionRange[1]}; + + // Eta + histos.add("eta/etaDiffMCReco", qafeat::MakeTitle({qafeat::Eta.MCRecDiff()}).c_str(), o2fw::kTH1D, {deltaEtaAxis}); + + histos.add("eta/etaDiffMCRecoVsEtaMC", qafeat::MakeTitle({qafeat::Eta.MCRecDiff(), qafeat::Eta.MC()}).c_str(), + o2fw::kTH2D, {deltaEtaAxis, etaAxis}); + + histos.add("eta/etaDiffMCRecoVsEtaReco", qafeat::MakeTitle({qafeat::Eta.MCRecDiff(), qafeat::Eta.Rec()}).c_str(), + o2fw::kTH2D, {deltaEtaAxis, etaAxis}); + + // Phi + histos.add("phi/phiDiffMCRec", qafeat::MakeTitle({qafeat::Phi.MCRecDiff()}).c_str(), o2fw::kTH1D, {deltaPhiAxis}); + + // Pt + histos.add("pt/ptDiffMCRec", qafeat::MakeTitle({qafeat::Pt.MCRecDiff()}).c_str(), o2fw::kTH1D, {deltaPtAxis}); + + histos.add("pt/ptResolution", qafeat::MakeTitle({qafeat::Pt.RelativeMCRecDiff()}).c_str(), o2fw::kTH1D, + {deltaPtRelativeAxis}); + + histos.add("pt/ptResolutionVsPt", qafeat::MakeTitle({qafeat::Pt.Rec(), qafeat::Pt.RelativeMCRecDiff()}).c_str(), + o2fw::kTH2D, {ptAxis, deltaPtAbsoluteRelativeAxis}); + + histos.add("pt/ptResolutionVsEta", qafeat::MakeTitle({qafeat::Eta.Rec(), qafeat::Pt.RelativeMCRecDiff()}).c_str(), + o2fw::kTH2D, {etaAxis, deltaPtAbsoluteRelativeAxis}); + + histos.add("pt/ptResolutionVsPhi", qafeat::MakeTitle({qafeat::Phi.Rec(), qafeat::Pt.RelativeMCRecDiff()}).c_str(), + o2fw::kTH2D, {phiAxis, deltaPtAbsoluteRelativeAxis}); + + // Impact parameters + histos.add("impactParameter/impactParameterRPhiVsPt", + qafeat::MakeTitle({qafeat::Pt.Rec(), qafeat::ImpactParameterRPhi}).c_str(), o2fw::kTH2D, + {ptAxis, impactParRPhiAxis}); + + histos.add("impactParameter/impactParameterRPhiVsEta", + qafeat::MakeTitle({qafeat::Eta.Rec(), qafeat::ImpactParameterRPhi}).c_str(), o2fw::kTH2D, + {etaAxis, impactParRPhiAxis}); + + histos.add("impactParameter/impactParameterRPhiVsPhi", + qafeat::MakeTitle({qafeat::Phi.Rec(), qafeat::ImpactParameterRPhi}).c_str(), o2fw::kTH2D, + {phiAxis, impactParRPhiAxis}); + + histos.add("impactParameter/impactParameterErrorRPhiVsPt", + qafeat::MakeTitle({qafeat::Pt.Rec(), qafeat::ImpactParameterRPhiError}).c_str(), o2fw::kTH2D, + {ptAxis, impactParRPhiErrorAxis}); + + histos.add("impactParameter/impactParameterErrorRPhiVsEta", + qafeat::MakeTitle({qafeat::Eta.Rec(), qafeat::ImpactParameterRPhiError}).c_str(), o2fw::kTH2D, + {etaAxis, impactParRPhiErrorAxis}); + + histos.add("impactParameter/impactParameterErrorRPhiVsPhi", + qafeat::MakeTitle({qafeat::Phi.Rec(), qafeat::ImpactParameterRPhiError}).c_str(), o2fw::kTH2D, + {phiAxis, impactParRPhiErrorAxis}); + + histos.add("impactParameter/impactParameterZVsPt", + qafeat::MakeTitle({qafeat::Pt.Rec(), qafeat::ImpactParameterZ}).c_str(), o2fw::kTH2D, + {ptAxis, impactParZAxis}); + + histos.add("impactParameter/impactParameterZVsEta", + qafeat::MakeTitle({qafeat::Eta.Rec(), qafeat::ImpactParameterZ}).c_str(), o2fw::kTH2D, + {etaAxis, impactParZAxis}); + + histos.add("impactParameter/impactParameterZVsPhi", + qafeat::MakeTitle({qafeat::Phi.Rec(), qafeat::ImpactParameterZ}).c_str(), o2fw::kTH2D, + {phiAxis, impactParZAxis}); + + histos.add("impactParameter/impactParameterErrorZVsPt", + qafeat::MakeTitle({qafeat::Pt.Rec(), qafeat::ImpactParameterZError}).c_str(), o2fw::kTH2D, + {ptAxis, impactParZErrorAxis}); + + histos.add("impactParameter/impactParameterErrorZVsEta", + qafeat::MakeTitle({qafeat::Eta.Rec(), qafeat::ImpactParameterZError}).c_str(), o2fw::kTH2D, + {etaAxis, impactParZErrorAxis}); + + histos.add("impactParameter/impactParameterErrorZVsPhi", + qafeat::MakeTitle({qafeat::Phi.Rec(), qafeat::ImpactParameterZError}).c_str(), o2fw::kTH2D, + {phiAxis, impactParZErrorAxis}); + } + + void process(const o2::soa::Join<o2::aod::Collisions, o2::aod::McCollisionLabels>::iterator& collision, + const o2::soa::Join<o2::aod::Tracks, o2::aod::TracksCov, o2::aod::McTrackLabels>& tracks, + const o2::aod::McParticles& mcParticles, const o2::aod::McCollisions& mcCollisions) + { + const o2df::VertexBase primaryVertex = getPrimaryVertex(collision); + + for (const auto& track : tracks) { + const double deltaPt = track.label().pt() - track.pt(); + histos.fill(HIST("pt/ptDiffMCRec"), deltaPt); + + const double deltaPtOverPt = deltaPt / track.pt(); + + histos.fill(HIST("pt/ptResolution"), deltaPtOverPt); + histos.fill(HIST("pt/ptResolutionVsPt"), track.pt(), std::abs(deltaPtOverPt)); + histos.fill(HIST("pt/ptResolutionVsEta"), track.eta(), std::abs(deltaPtOverPt)); + histos.fill(HIST("pt/ptResolutionVsPhi"), track.phi(), std::abs(deltaPtOverPt)); + + const double deltaEta = track.label().eta() - track.eta(); + histos.fill(HIST("eta/etaDiffMCReco"), deltaEta); + histos.fill(HIST("eta/etaDiffMCRecoVsEtaMC"), deltaEta, track.label().eta()); + histos.fill(HIST("eta/etaDiffMCRecoVsEtaReco"), deltaEta, track.eta()); + + const double deltaPhi = track_utils::ConvertPhiRange(track.label().phi() - track.phi()); + histos.fill(HIST("phi/phiDiffMCRec"), deltaPhi); + + double impactParameterRPhi{-999.}, impactParameterRPhiError{-999.}; + double impactParameterZ{-999.}, impactParameterErrorZ{-999.}; + + const bool propagate = track_utils::GetImpactParameterAndError( + track, primaryVertex, impactParameterRPhi, impactParameterRPhiError, impactParameterZ, impactParameterErrorZ); + + if (propagate) { + histos.fill(HIST("impactParameter/impactParameterRPhiVsPt"), track.pt(), impactParameterRPhi); + histos.fill(HIST("impactParameter/impactParameterRPhiVsEta"), track.eta(), impactParameterRPhi); + histos.fill(HIST("impactParameter/impactParameterRPhiVsPhi"), track.phi(), impactParameterRPhi); + + histos.fill(HIST("impactParameter/impactParameterZVsPt"), track.pt(), impactParameterZ); + histos.fill(HIST("impactParameter/impactParameterZVsEta"), track.eta(), impactParameterZ); + histos.fill(HIST("impactParameter/impactParameterZVsPhi"), track.phi(), impactParameterZ); + + histos.fill(HIST("impactParameter/impactParameterErrorRPhiVsPt"), track.pt(), impactParameterRPhiError); + histos.fill(HIST("impactParameter/impactParameterErrorRPhiVsEta"), track.eta(), impactParameterRPhiError); + histos.fill(HIST("impactParameter/impactParameterErrorRPhiVsPhi"), track.phi(), impactParameterRPhiError); + + histos.fill(HIST("impactParameter/impactParameterErrorZVsPt"), track.pt(), impactParameterErrorZ); + histos.fill(HIST("impactParameter/impactParameterErrorZVsEta"), track.eta(), impactParameterErrorZ); + histos.fill(HIST("impactParameter/impactParameterErrorZVsPhi"), track.phi(), impactParameterErrorZ); + } + } + } +}; + +o2fw::WorkflowSpec defineDataProcessing(o2fw::ConfigContext const&) +{ + return o2fw::WorkflowSpec{o2fw::adaptAnalysisTask<QAGlobalObservables>("qa-global-observables"), + o2fw::adaptAnalysisTask<QATrackingKine>("qa-tracking-kine"), + o2fw::adaptAnalysisTask<QATrackingResolution>("qa-tracking-resolution")}; +} diff --git a/Analysis/Tasks/PWGHF/taskD0.cxx b/Analysis/Tasks/PWGHF/taskD0.cxx new file mode 100644 index 0000000000000..6089c4bf2d6aa --- /dev/null +++ b/Analysis/Tasks/PWGHF/taskD0.cxx @@ -0,0 +1,161 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 taskD0.cxx +/// \brief D0 analysis task +/// +/// \author Gian Michele Innocenti <gian.michele.innocenti@cern.ch>, CERN +/// \author Vít Kučera <vit.kucera@cern.ch>, CERN + +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong2; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + ConfigParamSpec optionDoMC{"doMC", VariantType::Bool, false, {"Fill MC histograms."}}; + workflowOptions.push_back(optionDoMC); +} + +#include "Framework/runDataProcessing.h" + +/// D0 analysis task +struct TaskD0 { + HistogramRegistry registry{ + "registry", + {{"hmass", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}, + {"hptcand", "2-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hptprong0", "2-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hptprong1", "2-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hdeclength", "2-prong candidates;decay length (cm);entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + {"hdeclengthxy", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + {"hd0Prong0", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hd0Prong1", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hd0d0", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2});entries", {HistType::kTH1F, {{500, -1., 1.}}}}, + {"hCTS", "2-prong candidates;cos #it{#theta}* (D^{0});entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hCt", "2-prong candidates;proper lifetime (D^{0}) * #it{c} (cm);entries", {HistType::kTH1F, {{120, -20., 100.}}}}, + {"hCPA", "2-prong candidates;cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hEta", "2-prong candidates;candidate #it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hselectionstatus", "2-prong candidates;selection status;entries", {HistType::kTH1F, {{5, -0.5, 4.5}}}}, + {"hImpParErr", "2-prong candidates;impact parameter error (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hDecLenErr", "2-prong candidates;decay length error (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"hDecLenXYErr", "2-prong candidates;decay length xy error (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}}}; + + Configurable<int> d_selectionFlagD0{"d_selectionFlagD0", 1, "Selection Flag for D0"}; + Configurable<int> d_selectionFlagD0bar{"d_selectionFlagD0bar", 1, "Selection Flag for D0bar"}; + Configurable<double> cutEtaCandMax{"cutEtaCandMax", -1., "max. cand. pseudorapidity"}; + + Filter filterSelectCandidates = (aod::hf_selcandidate_d0::isSelD0 >= d_selectionFlagD0 || aod::hf_selcandidate_d0::isSelD0bar >= d_selectionFlagD0bar); + + void process(soa::Filtered<soa::Join<aod::HfCandProng2, aod::HFSelD0Candidate>> const& candidates) + { + //Printf("Candidates: %d", candidates.size()); + for (auto& candidate : candidates) { + if (cutEtaCandMax >= 0. && std::abs(candidate.eta()) > cutEtaCandMax) { + //Printf("Candidate: eta rejection: %g", candidate.eta()); + continue; + } + if (candidate.isSelD0() >= d_selectionFlagD0) { + registry.get<TH1>(HIST("hmass"))->Fill(InvMassD0(candidate)); + } + if (candidate.isSelD0bar() >= d_selectionFlagD0bar) { + registry.get<TH1>(HIST("hmass"))->Fill(InvMassD0bar(candidate)); + } + registry.get<TH1>(HIST("hptcand"))->Fill(candidate.pt()); + registry.get<TH1>(HIST("hptprong0"))->Fill(candidate.ptProng0()); + registry.get<TH1>(HIST("hptprong1"))->Fill(candidate.ptProng1()); + registry.get<TH1>(HIST("hdeclength"))->Fill(candidate.decayLength()); + registry.get<TH1>(HIST("hdeclengthxy"))->Fill(candidate.decayLengthXY()); + registry.get<TH1>(HIST("hd0Prong0"))->Fill(candidate.impactParameter0()); + registry.get<TH1>(HIST("hd0Prong1"))->Fill(candidate.impactParameter1()); + registry.get<TH1>(HIST("hd0d0"))->Fill(candidate.impactParameterProduct()); + registry.get<TH1>(HIST("hCTS"))->Fill(CosThetaStarD0(candidate)); + registry.get<TH1>(HIST("hCt"))->Fill(CtD0(candidate)); + registry.get<TH1>(HIST("hCPA"))->Fill(candidate.cpa()); + registry.get<TH1>(HIST("hEta"))->Fill(candidate.eta()); + registry.get<TH1>(HIST("hselectionstatus"))->Fill(candidate.isSelD0() + (candidate.isSelD0bar() * 2)); + registry.get<TH1>(HIST("hImpParErr"))->Fill(candidate.errorImpactParameter0()); + registry.get<TH1>(HIST("hImpParErr"))->Fill(candidate.errorImpactParameter1()); + registry.get<TH1>(HIST("hDecLenErr"))->Fill(candidate.errorDecayLength()); + registry.get<TH1>(HIST("hDecLenXYErr"))->Fill(candidate.errorDecayLengthXY()); + } + } +}; + +/// Fills MC histograms. +struct TaskD0MC { + HistogramRegistry registry{ + "registry", + {{"hPtRecSig", "2-prong candidates (rec. matched);#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hPtRecBg", "2-prong candidates (rec. unmatched);#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hPtGen", "2-prong candidates (gen. matched);#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hCPARecSig", "2-prong candidates (rec. matched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hCPARecBg", "2-prong candidates (rec. unmatched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hEtaRecSig", "2-prong candidates (rec. matched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hEtaRecBg", "2-prong candidates (rec. unmatched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hEtaGen", "2-prong candidates (gen. matched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}}}; + + Configurable<int> d_selectionFlagD0{"d_selectionFlagD0", 1, "Selection Flag for D0"}; + Configurable<int> d_selectionFlagD0bar{"d_selectionFlagD0bar", 1, "Selection Flag for D0bar"}; + Configurable<double> cutEtaCandMax{"cutEtaCandMax", -1., "max. cand. pseudorapidity"}; + + Filter filterSelectCandidates = (aod::hf_selcandidate_d0::isSelD0 >= d_selectionFlagD0 || aod::hf_selcandidate_d0::isSelD0bar >= d_selectionFlagD0bar); + + void process(soa::Filtered<soa::Join<aod::HfCandProng2, aod::HFSelD0Candidate, aod::HfCandProng2MCRec>> const& candidates, + soa::Join<aod::McParticles, aod::HfCandProng2MCGen> const& particlesMC) + { + // MC rec. + //Printf("MC Candidates: %d", candidates.size()); + for (auto& candidate : candidates) { + if (cutEtaCandMax >= 0. && std::abs(candidate.eta()) > cutEtaCandMax) { + //Printf("MC Rec.: eta rejection: %g", candidate.eta()); + continue; + } + if (std::abs(candidate.flagMCMatchRec()) == D0ToPiK) { + registry.get<TH1>(HIST("hPtRecSig"))->Fill(candidate.pt()); + registry.get<TH1>(HIST("hCPARecSig"))->Fill(candidate.cpa()); + registry.get<TH1>(HIST("hEtaRecSig"))->Fill(candidate.eta()); + } else { + registry.get<TH1>(HIST("hPtRecBg"))->Fill(candidate.pt()); + registry.get<TH1>(HIST("hCPARecBg"))->Fill(candidate.cpa()); + registry.get<TH1>(HIST("hEtaRecBg"))->Fill(candidate.eta()); + } + } + // MC gen. + //Printf("MC Particles: %d", particlesMC.size()); + for (auto& particle : particlesMC) { + if (cutEtaCandMax >= 0. && std::abs(particle.eta()) > cutEtaCandMax) { + //Printf("MC Gen.: eta rejection: %g", particle.eta()); + continue; + } + if (std::abs(particle.flagMCMatchGen()) == D0ToPiK) { + registry.get<TH1>(HIST("hPtGen"))->Fill(particle.pt()); + registry.get<TH1>(HIST("hEtaGen"))->Fill(particle.eta()); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask<TaskD0>("hf-task-d0")}; + const bool doMC = cfgc.options().get<bool>("doMC"); + if (doMC) { + workflow.push_back(adaptAnalysisTask<TaskD0MC>("hf-task-d0-mc")); + } + return workflow; +} diff --git a/Analysis/Tasks/PWGHF/taskDPlus.cxx b/Analysis/Tasks/PWGHF/taskDPlus.cxx new file mode 100644 index 0000000000000..324ad85c00cfb --- /dev/null +++ b/Analysis/Tasks/PWGHF/taskDPlus.cxx @@ -0,0 +1,97 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 taskDPlus.cxx +/// \brief D± analysis task +/// \note Extended from taskD0 +/// +/// \author Vít Kučera <vit.kucera@cern.ch>, CERN + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong3; +//using namespace o2::framework::expressions; + +/// D± analysis task +struct TaskDPlus { + HistogramRegistry registry{ + "registry", + { + {"hMass", "3-prong candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{350, 1.7, 2.05}}}}, + {"hPt", "3-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hEta", "3-prong candidates;candidate #it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hCt", "3-prong candidates;proper lifetime (D^{#pm}) * #it{c} (cm);entries", {HistType::kTH1F, {{120, -20., 100.}}}}, + {"hDecayLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + {"hDecayLengthXY", "3-prong candidates;decay length xy (cm);entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + {"hNormalisedDecayLengthXY", "3-prong candidates;norm. decay length xy;entries", {HistType::kTH1F, {{80, 0., 80.}}}}, + {"hCPA", "3-prong candidates;cos. pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hCPAxy", "3-prong candidates;cos. pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hImpactParameterXY", "3-prong candidates;impact parameter xy (cm);entries", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"hMaxNormalisedDeltaIP", "3-prong candidates;norm. IP;entries", {HistType::kTH1F, {{200, -20., 20.}}}}, + {"hImpactParameterProngSqSum", "3-prong candidates;squared sum of prong imp. par. (cm^{2});entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"hDecayLengthError", "3-prong candidates;decay length error (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"hDecayLengthXYError", "3-prong candidates;decay length xy error (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"hImpactParameterError", "3-prong candidates;impact parameter error (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"hPtProng0", "3-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hPtProng1", "3-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hPtProng2", "3-prong candidates;prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hd0Prong1", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hd0Prong2", "3-prong candidates;prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}} + //{"hSelectionStatus", "3-prong candidates;selection status;entries", {HistType::kTH1F, {{5, -0.5, 4.5}}}} + }}; + + Configurable<int> d_selectionFlagDPlus{"d_selectionFlagDPlus", 1, "Selection Flag for DPlus"}; + + //Filter filterSelectCandidates = (aod::hf_selcandidate::isSelDPlus >= d_selectionFlagDPlus); + + //void process(soa::Filtered<soa::Join<aod::HfCandProng3, aod::HFSelDPlusCandidate>> const& candidates) + void process(aod::HfCandProng3 const& candidates) + { + for (auto& candidate : candidates) { + registry.get<TH1>(HIST("hMass"))->Fill(InvMassDPlus(candidate)); + registry.get<TH1>(HIST("hPt"))->Fill(candidate.pt()); + registry.get<TH1>(HIST("hEta"))->Fill(candidate.eta()); + registry.get<TH1>(HIST("hCt"))->Fill(CtDPlus(candidate)); + registry.get<TH1>(HIST("hDecayLength"))->Fill(candidate.decayLength()); + registry.get<TH1>(HIST("hDecayLengthXY"))->Fill(candidate.decayLengthXY()); + registry.get<TH1>(HIST("hNormalisedDecayLengthXY"))->Fill(candidate.decayLengthXYNormalised()); + registry.get<TH1>(HIST("hCPA"))->Fill(candidate.cpa()); + registry.get<TH1>(HIST("hCPAxy"))->Fill(candidate.cpaXY()); + registry.get<TH1>(HIST("hImpactParameterXY"))->Fill(candidate.impactParameterXY()); + registry.get<TH1>(HIST("hMaxNormalisedDeltaIP"))->Fill(candidate.maxNormalisedDeltaIP()); + registry.get<TH1>(HIST("hImpactParameterProngSqSum"))->Fill(candidate.impactParameterProngSqSum()); + registry.get<TH1>(HIST("hDecayLengthError"))->Fill(candidate.errorDecayLength()); + registry.get<TH1>(HIST("hDecayLengthXYError"))->Fill(candidate.errorDecayLengthXY()); + registry.get<TH1>(HIST("hImpactParameterError"))->Fill(candidate.errorImpactParameter0()); + registry.get<TH1>(HIST("hImpactParameterError"))->Fill(candidate.errorImpactParameter1()); + registry.get<TH1>(HIST("hImpactParameterError"))->Fill(candidate.errorImpactParameter2()); + registry.get<TH1>(HIST("hPtProng0"))->Fill(candidate.ptProng0()); + registry.get<TH1>(HIST("hPtProng1"))->Fill(candidate.ptProng1()); + registry.get<TH1>(HIST("hPtProng2"))->Fill(candidate.ptProng2()); + registry.get<TH1>(HIST("hd0Prong0"))->Fill(candidate.impactParameter0()); + registry.get<TH1>(HIST("hd0Prong1"))->Fill(candidate.impactParameter1()); + registry.get<TH1>(HIST("hd0Prong2"))->Fill(candidate.impactParameter2()); + //registry.get<TH1>(HIST("hSelectionStatus"))->Fill(candidate.isSelDPlus()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<TaskDPlus>("hf-task-dplus")}; +} diff --git a/Analysis/Tasks/PWGHF/taskLc.cxx b/Analysis/Tasks/PWGHF/taskLc.cxx new file mode 100644 index 0000000000000..ebf91b37889d0 --- /dev/null +++ b/Analysis/Tasks/PWGHF/taskLc.cxx @@ -0,0 +1,163 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 taskLc.cxx +/// \brief Lc± analysis task +/// \note Extended from taskD0 +/// +/// \author Luigi Dello Stritto <luigi.dello.stritto@cern.ch>, University and INFN SALERNO +/// \author Vít Kučera <vit.kucera@cern.ch>, CERN + +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::hf_cand_prong3; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + ConfigParamSpec optionDoMC{"doMC", VariantType::Bool, false, {"Fill MC histograms."}}; + workflowOptions.push_back(optionDoMC); +} + +#include "Framework/runDataProcessing.h" + +/// Lc± analysis task +struct TaskLc { + HistogramRegistry registry{ + "registry", + {{"hmass", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 0., 5.}}}}, + {"hptcand", "3-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hptprong0", "3-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hptprong1", "3-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hptprong2", "3-prong candidates;prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hdeclength", "3-prong candidates;decay length (cm);entries", {HistType::kTH1F, {{400, -2., 2.}}}}, + {"hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hd0Prong1", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hd0Prong2", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hCt", "3-prong candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{120, -20., 100.}}}}, + {"hCPA", "3-prong candidates;cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hEta", "3-prong candidates;candidate #it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hselectionstatus", "3-prong candidates;selection status;entries", {HistType::kTH1F, {{5, -0.5, 4.5}}}}, + {"hImpParErr", "3-prong candidates;impact parameter error (cm);entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"hDecLenErr", "3-prong candidates;decay length error (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"hdca2", "3-prong candidates;prong DCA to sec. vertex (cm);entries", {HistType::kTH1F, {{100, 0., 1.}}}}}}; + + Configurable<int> d_selectionFlagLc{"d_selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable<double> cutEtaCandMax{"cutEtaCandMax", -1., "max. cand. pseudorapidity"}; + + Filter filterSelectCandidates = (aod::hf_selcandidate_lc::isSelLcpKpi >= d_selectionFlagLc || aod::hf_selcandidate_lc::isSelLcpiKp >= d_selectionFlagLc); + + //void process(aod::HfCandProng3 const& candidates) + void process(soa::Filtered<soa::Join<aod::HfCandProng3, aod::HFSelLcCandidate>> const& candidates) + { + for (auto& candidate : candidates) { + /* if (candidate.pt()>5){ + continue;}*/ + if (cutEtaCandMax >= 0. && std::abs(candidate.eta()) > cutEtaCandMax) { + //Printf("Candidate: eta rejection: %g", candidate.eta()); + continue; + } + if (candidate.isSelLcpKpi() >= d_selectionFlagLc) { + registry.get<TH1>("hmass")->Fill(InvMassLcpKpi(candidate)); + } + if (candidate.isSelLcpiKp() >= d_selectionFlagLc) { + registry.get<TH1>("hmass")->Fill(InvMassLcpiKp(candidate)); + } + registry.get<TH1>("hptcand")->Fill(candidate.pt()); + registry.get<TH1>("hptprong0")->Fill(candidate.ptProng0()); + registry.get<TH1>("hptprong1")->Fill(candidate.ptProng1()); + registry.get<TH1>("hptprong2")->Fill(candidate.ptProng2()); + registry.get<TH1>("hdeclength")->Fill(candidate.decayLength()); + registry.get<TH1>("hd0Prong0")->Fill(candidate.impactParameter0()); + registry.get<TH1>("hd0Prong1")->Fill(candidate.impactParameter1()); + registry.get<TH1>("hd0Prong2")->Fill(candidate.impactParameter2()); + registry.get<TH1>("hCt")->Fill(CtLc(candidate)); + registry.get<TH1>("hCPA")->Fill(candidate.cpa()); + registry.get<TH1>("hEta")->Fill(candidate.eta()); + registry.get<TH1>("hselectionstatus")->Fill(candidate.isSelLcpKpi()); + registry.get<TH1>("hselectionstatus")->Fill(candidate.isSelLcpiKp()); + registry.get<TH1>("hImpParErr")->Fill(candidate.errorImpactParameter0()); + registry.get<TH1>("hImpParErr")->Fill(candidate.errorImpactParameter1()); + registry.get<TH1>("hImpParErr")->Fill(candidate.errorImpactParameter2()); + registry.get<TH1>("hDecLenErr")->Fill(candidate.errorDecayLength()); + registry.get<TH1>("hDecLenErr")->Fill(candidate.chi2PCA()); + } + } +}; + +/// Fills MC histograms. +struct TaskLcMC { + HistogramRegistry registry{ + "registry", + {{"hPtRecSig", "3-prong candidates (rec. matched);#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hPtRecBg", "3-prong candidates (rec. unmatched);#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hPtGen", "3-prong candidates (gen. matched);#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 10.}}}}, + {"hCPARecSig", "3-prong candidates (rec. matched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hCPARecBg", "3-prong candidates (rec. unmatched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"hEtaRecSig", "3-prong candidates (rec. matched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hEtaRecBg", "3-prong candidates (rec. unmatched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"hEtaGen", "3-prong candidates (gen. matched);#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}}}; + + Configurable<int> d_selectionFlagLc{"d_selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable<int> d_selectionFlagLcbar{"d_selectionFlagLcbar", 1, "Selection Flag for Lcbar"}; + Configurable<double> cutEtaCandMax{"cutEtaCandMax", -1., "max. cand. pseudorapidity"}; + + Filter filterSelectCandidates = (aod::hf_selcandidate_lc::isSelLcpKpi >= d_selectionFlagLc || aod::hf_selcandidate_lc::isSelLcpiKp >= d_selectionFlagLc); + + void process(soa::Filtered<soa::Join<aod::HfCandProng3, aod::HFSelLcCandidate, aod::HfCandProng3MCRec>> const& candidates, + soa::Join<aod::McParticles, aod::HfCandProng3MCGen> const& particlesMC) + { + // MC rec. + //Printf("MC Candidates: %d", candidates.size()); + for (auto& candidate : candidates) { + if (cutEtaCandMax >= 0. && std::abs(candidate.eta()) > cutEtaCandMax) { + //Printf("MC Rec.: eta rejection: %g", candidate.eta()); + continue; + } + if (std::abs(candidate.flagMCMatchRec()) == LcToPKPi) { + registry.get<TH1>("hPtRecSig")->Fill(candidate.pt()); + registry.get<TH1>("hCPARecSig")->Fill(candidate.cpa()); + registry.get<TH1>("hEtaRecSig")->Fill(candidate.eta()); + } else { + registry.get<TH1>("hPtRecBg")->Fill(candidate.pt()); + registry.get<TH1>("hCPARecBg")->Fill(candidate.cpa()); + registry.get<TH1>("hEtaRecBg")->Fill(candidate.eta()); + } + } + // MC gen. + //Printf("MC Particles: %d", particlesMC.size()); + for (auto& particle : particlesMC) { + if (cutEtaCandMax >= 0. && std::abs(particle.eta()) > cutEtaCandMax) { + //Printf("MC Gen.: eta rejection: %g", particle.eta()); + continue; + } + if (std::abs(particle.flagMCMatchGen()) == LcToPKPi) { + registry.get<TH1>("hPtGen")->Fill(particle.pt()); + registry.get<TH1>("hEtaGen")->Fill(particle.eta()); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask<TaskLc>("hf-task-lc")}; + const bool doMC = cfgc.options().get<bool>("doMC"); + if (doMC) { + workflow.push_back(adaptAnalysisTask<TaskLcMC>("hf-task-lc-mc")); + } + return workflow; +} diff --git a/Analysis/Tasks/PWGJE/CMakeLists.txt b/Analysis/Tasks/PWGJE/CMakeLists.txt new file mode 100644 index 0000000000000..d0afc844d3375 --- /dev/null +++ b/Analysis/Tasks/PWGJE/CMakeLists.txt @@ -0,0 +1,38 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(FastJet_FOUND) +o2_add_dpl_workflow(jet-finder + SOURCES jetfinder.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisJets O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(jet-finder-hf + SOURCES jetfinderhf.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisJets O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(jet-finder-hadron-recoil + SOURCES jetfinderhadronrecoil.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisJets O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(jet-substructure + SOURCES jetsubstructure.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisJets O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(jet-skimmer + SOURCES jetskimming.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisJets O2::AnalysisDataModel + COMPONENT_NAME Analysis) +endif() + diff --git a/Analysis/Tasks/PWGJE/jetfinder.cxx b/Analysis/Tasks/PWGJE/jetfinder.cxx new file mode 100644 index 0000000000000..7237557289a4d --- /dev/null +++ b/Analysis/Tasks/PWGJE/jetfinder.cxx @@ -0,0 +1,105 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet finder task +// +// Author: Jochen Klein, Nima Zardoshti + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +#include "fastjet/PseudoJet.hh" +#include "fastjet/ClusterSequenceArea.hh" + +#include "AnalysisDataModel/Jet.h" +#include "AnalysisCore/JetFinder.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct JetFinderTask { + Produces<o2::aod::Jets> jetsTable; + Produces<o2::aod::JetConstituents> constituentsTable; + Produces<o2::aod::JetConstituentsSub> constituentsSubTable; + OutputObj<TH1F> hJetPt{"h_jet_pt"}; + OutputObj<TH1F> hJetPhi{"h_jet_phi"}; + OutputObj<TH1F> hJetEta{"h_jet_eta"}; + OutputObj<TH1F> hJetN{"h_jet_n"}; + + Configurable<bool> b_DoRhoAreaSub{"b_DoRhoAreaSub", false, "do rho area subtraction"}; + Configurable<bool> b_DoConstSub{"b_DoConstSub", false, "do constituent subtraction"}; + + Filter trackCuts = aod::track::pt >= 0.15f && aod::track::eta >= -0.9f && aod::track::eta <= 0.9f; + + std::vector<fastjet::PseudoJet> jets; + std::vector<fastjet::PseudoJet> inputParticles; + JetFinder jetFinder; + + void init(InitContext const&) + { + hJetPt.setObject(new TH1F("h_jet_pt", "jet p_{T};p_{T} (GeV/#it{c})", + 100, 0., 100.)); + hJetPhi.setObject(new TH1F("h_jet_phi", "jet #phi;#phi", + 80, -1., 7.)); + hJetEta.setObject(new TH1F("h_jet_eta", "jet #eta;#eta", + 70, -0.7, 0.7)); + hJetN.setObject(new TH1F("h_jet_n", "jet n;n constituents", + 30, 0., 30.)); + if (b_DoRhoAreaSub) { + jetFinder.setBkgSubMode(JetFinder::BkgSubMode::rhoAreaSub); + } + if (b_DoConstSub) { + jetFinder.setBkgSubMode(JetFinder::BkgSubMode::constSub); + } + } + + void process(aod::Collision const& collision, + soa::Filtered<aod::Tracks> const& tracks) + { + + jets.clear(); + inputParticles.clear(); + + for (auto& track : tracks) { + /* auto energy = std::sqrt(track.p() * track.p() + mPion*mPion); + inputParticles.emplace_back(track.px(), track.py(), track.pz(), energy); + inputParticles.back().set_user_index(track.globalIndex());*/ + fillConstituents(track, inputParticles); + inputParticles.back().set_user_index(track.globalIndex()); + } + + fastjet::ClusterSequenceArea clusterSeq(jetFinder.findJets(inputParticles, jets)); + + for (const auto& jet : jets) { + jetsTable(collision, jet.pt(), jet.eta(), jet.phi(), + jet.E(), jet.m(), jet.area()); + hJetPt->Fill(jet.pt()); + hJetPhi->Fill(jet.phi()); + hJetEta->Fill(jet.eta()); + hJetN->Fill(jet.constituents().size()); + for (const auto& constituent : jet.constituents()) { //event or jetwise + if (b_DoConstSub) { + constituentsSubTable(jetsTable.lastIndex(), constituent.pt(), constituent.eta(), constituent.phi(), + constituent.E(), constituent.m()); + } + constituentsTable(jetsTable.lastIndex(), constituent.user_index()); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<JetFinderTask>("jet-finder")}; +} diff --git a/Analysis/Tasks/PWGJE/jetfinderhadronrecoil.cxx b/Analysis/Tasks/PWGJE/jetfinderhadronrecoil.cxx new file mode 100644 index 0000000000000..49a7fdff15bc6 --- /dev/null +++ b/Analysis/Tasks/PWGJE/jetfinderhadronrecoil.cxx @@ -0,0 +1,127 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet finder task +// +// Author: Nima Zardoshti + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +#include "fastjet/PseudoJet.hh" +#include "fastjet/ClusterSequenceArea.hh" + +#include "AnalysisDataModel/Jet.h" +#include "AnalysisCore/JetFinder.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct JetFinderHadronRecoilTask { + OutputObj<TH1F> hJetPt{"h_jet_pt"}; + OutputObj<TH1F> hHadronPt{"h_hadron_pt"}; + OutputObj<TH1F> hJetHadronDeltaPhi{"h_jet_hadron_deltaphi"}; + + Configurable<float> f_trackTTMin{"f_trackTTMin", 8.0, "TT hadron min pT"}; + Configurable<float> f_trackTTMax{"f_trackTTMax", 9.0, "TT hadron max pT"}; + Configurable<float> f_recoilWindow{"f_recoilWindow", 0.6, "jet finding phi window reecoilling from hadron"}; + + Filter trackCuts = aod::track::pt >= 0.15f && aod::track::eta > -0.9f && aod::track::eta < 0.9f; + int collisionSplit = 0; //can we partition the collisions? + //can we also directly filter the collision based on the max track::pt? + + std::vector<float> trackTTPt; + std::vector<fastjet::PseudoJet> jets; + std::vector<fastjet::PseudoJet> inputParticles; + JetFinder jetFinder; + + template <typename T> + T relativePhi(T phi1, T phi2) + { + if (phi1 < -TMath::Pi()) { + phi1 += (2 * TMath::Pi()); + } else if (phi1 > TMath::Pi()) { + phi1 -= (2 * TMath::Pi()); + } + if (phi2 < -TMath::Pi()) { + phi2 += (2 * TMath::Pi()); + } else if (phi2 > TMath::Pi()) { + phi2 -= (2 * TMath::Pi()); + } + T deltaPhi = phi2 - phi1; + if (deltaPhi < -TMath::Pi()) { + deltaPhi += (2 * TMath::Pi()); + } else if (deltaPhi > TMath::Pi()) { + deltaPhi -= (2 * TMath::Pi()); + } + return deltaPhi; + } + + void init(InitContext const&) + { + hJetPt.setObject(new TH1F("h_jet_pt", "jet p_{T};jet p_{T} (GeV/#it{c})", + 100, 0., 100.)); + hHadronPt.setObject(new TH1F("h_hadron_pt", "hadron p_{T};hadron p_{T} (GeV/#it{c})", + 120, 0., 60.)); + hJetHadronDeltaPhi.setObject(new TH1F("h_jet_hadron_deltaphi", "jet #eta;#eta", + 40, 0.0, 4.)); + } + + void process(aod::Collision const& collision, + soa::Filtered<aod::Tracks> const& tracks) + { + + jets.clear(); + inputParticles.clear(); + auto trackTTPhi = 0.0; + auto trackTTPt = 0.0; + auto comparisonPt = 0.0; + bool isTT = false; + for (auto& track : tracks) { + if (track.pt() >= f_trackTTMin && track.pt() < f_trackTTMax) { //can this also go into a partition? + isTT = true; + if (track.pt() >= comparisonPt) { //currently take highest pT but later to randomise + comparisonPt = track.pt(); + trackTTPt = track.pt(); + trackTTPhi = track.phi(); + } + } + auto energy = std::sqrt(track.p() * track.p() + JetFinder::mPion * JetFinder::mPion); + inputParticles.emplace_back(track.px(), track.py(), track.pz(), energy); + inputParticles.back().set_user_index(track.globalIndex()); + } + if (!isTT) { + return; + } + hHadronPt->Fill(trackTTPt); + + // you can set phi selector here for jets + fastjet::ClusterSequenceArea clusterSeq(jetFinder.findJets(inputParticles, jets)); + + for (const auto& jet : jets) { + auto deltaPhi = TMath::Abs(relativePhi(jet.phi(), trackTTPhi)); + if (deltaPhi >= (TMath::Pi() - f_recoilWindow)) { + hJetPt->Fill(jet.pt()); + } + if (deltaPhi >= TMath::Pi() / 2.0 && deltaPhi <= TMath::Pi()) { + hJetHadronDeltaPhi->Fill(deltaPhi); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<JetFinderHadronRecoilTask>("jet-finder-hadron-recoil")}; +} diff --git a/Analysis/Tasks/PWGJE/jetfinderhf.cxx b/Analysis/Tasks/PWGJE/jetfinderhf.cxx new file mode 100644 index 0000000000000..222113c38efc2 --- /dev/null +++ b/Analysis/Tasks/PWGJE/jetfinderhf.cxx @@ -0,0 +1,113 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet finder task +// +// Author: Nima Zardoshti + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "AnalysisDataModel/HFCandidateSelectionTables.h" + +#include "fastjet/PseudoJet.hh" +#include "fastjet/ClusterSequenceArea.hh" + +#include "AnalysisDataModel/Jet.h" +#include "AnalysisCore/JetFinder.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct JetFinderHFTask { + Produces<o2::aod::Jets> jetsTable; + Produces<o2::aod::JetConstituents> constituents; + OutputObj<TH1F> hJetPt{"h_jet_pt"}; + OutputObj<TH1F> hD0Pt{"h_D0_pt"}; + + std::vector<fastjet::PseudoJet> jets; + std::vector<fastjet::PseudoJet> inputParticles; + JetFinder jetFinder; + + void init(InitContext const&) + { + hJetPt.setObject(new TH1F("h_jet_pt", "jet p_{T};p_{T} (GeV/#it{c})", + 100, 0., 100.)); + hD0Pt.setObject(new TH1F("h_D0_pt", "jet p_{T,D};p_{T,D} (GeV/#it{c})", + 60, 0., 60.)); + } + + Configurable<int> d_selectionFlagD0{"d_selectionFlagD0", 1, "Selection Flag for D0"}; + Configurable<int> d_selectionFlagD0bar{"d_selectionFlagD0bar", 1, "Selection Flag for D0bar"}; + + //need enum as configurable + enum pdgCode { pdgD0 = 421 }; + + Filter trackCuts = (aod::track::pt > 0.15f && aod::track::eta > -0.9f && aod::track::eta < 0.9f); + Filter seltrack = (aod::hf_selcandidate_d0::isSelD0 >= d_selectionFlagD0 || aod::hf_selcandidate_d0::isSelD0bar >= d_selectionFlagD0bar); + + void process(aod::Collision const& collision, + soa::Filtered<aod::Tracks> const& tracks, + soa::Filtered<soa::Join<aod::HfCandProng2, aod::HFSelD0Candidate>> const& candidates) + { + std::cout << "Per Event" << std::endl; + // TODO: retrieve pion mass from somewhere + bool isHFJet; + + //this loop should be made more efficient + for (auto& candidate : candidates) { + jets.clear(); + inputParticles.clear(); + for (auto& track : tracks) { + auto energy = std::sqrt(track.p() * track.p() + JetFinder::mPion * JetFinder::mPion); + if (candidate.index0().globalIndex() == track.globalIndex() || candidate.index1().globalIndex() == track.globalIndex()) { //is it global index? + continue; + } + inputParticles.emplace_back(track.px(), track.py(), track.pz(), energy); + inputParticles.back().set_user_index(track.globalIndex()); + } + inputParticles.emplace_back(candidate.px(), candidate.py(), candidate.pz(), candidate.e(RecoDecay::getMassPDG(pdgD0))); + inputParticles.back().set_user_index(1); + + fastjet::ClusterSequenceArea clusterSeq(jetFinder.findJets(inputParticles, jets)); + + for (const auto& jet : jets) { + isHFJet = false; + for (const auto& constituent : jet.constituents()) { + if (constituent.user_index() == 1 && (candidate.isSelD0() == 1 || candidate.isSelD0bar() == 1)) { + isHFJet = true; + break; + } + } + if (isHFJet) { + jetsTable(collision, jet.eta(), jet.phi(), jet.pt(), + jet.area(), jet.E(), jet.m()); + for (const auto& constituent : jet.constituents()) { + constituents(jetsTable.lastIndex(), constituent.user_index()); + } + hJetPt->Fill(jet.pt()); + std::cout << "Filling" << std::endl; + hD0Pt->Fill(candidate.pt()); + break; + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<JetFinderHFTask>("jet-finder-hf")}; +} diff --git a/Analysis/Tasks/PWGJE/jetskimming.cxx b/Analysis/Tasks/PWGJE/jetskimming.cxx new file mode 100644 index 0000000000000..490ce1e284e3b --- /dev/null +++ b/Analysis/Tasks/PWGJE/jetskimming.cxx @@ -0,0 +1,69 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet skimming task +// +// Author: Nima Zardoshti + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +namespace jetskim +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Energy, energy, float); +} // namespace jetskim +DECLARE_SOA_TABLE(JetSkim, "AOD", "JETSKIM1", + jetskim::CollisionId, + jetskim::Pt, jetskim::Eta, jetskim::Phi, jetskim::Energy); +} // namespace o2::aod + +struct JetSkimmingTask1 { + Produces<o2::aod::JetSkim> skim; + + Filter trackCuts = aod::track::pt > 0.15f; + float mPionSquared = 0.139 * 0.139; + + void process(aod::Collision const& collision, + soa::Filtered<aod::Tracks> const& tracks) + { + for (auto& track : tracks) { + float energy = std::sqrt(track.p() * track.p() + mPionSquared); + skim(collision, track.pt(), track.eta(), track.phi(), energy); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<JetSkimmingTask1>("jet-skimmer")}; +} diff --git a/Analysis/Tasks/PWGJE/jetsubstructure.cxx b/Analysis/Tasks/PWGJE/jetsubstructure.cxx new file mode 100644 index 0000000000000..62ea4d2c6a944 --- /dev/null +++ b/Analysis/Tasks/PWGJE/jetsubstructure.cxx @@ -0,0 +1,125 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet analysis tasks (subscribing to jet finder task) +// +// Author: Nima Zardoshti +// + +#include "TH1F.h" +#include "TTree.h" + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +#include "AnalysisDataModel/Jet.h" +#include "AnalysisCore/JetFinder.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +namespace jetsubstructure +{ +DECLARE_SOA_COLUMN(Zg, zg, float); +DECLARE_SOA_COLUMN(Rg, rg, float); +DECLARE_SOA_COLUMN(Nsd, nsd, float); +} // namespace jetsubstructure +DECLARE_SOA_TABLE(JetSubtructure, "AOD", "JETSUBSTRUCTURE", jetsubstructure::Zg, jetsubstructure::Rg, jetsubstructure::Nsd); +} // namespace o2::aod + +struct JetSubstructure { + Produces<aod::JetSubtructure> jetSubstructure; + OutputObj<TH1F> hZg{"h_jet_zg"}; + OutputObj<TH1F> hRg{"h_jet_rg"}; + OutputObj<TH1F> hNsd{"h_jet_nsd"}; + + Configurable<float> f_jetPtMin{"f_jetPtMin", 0.0, "minimum jet pT cut"}; + Configurable<float> f_zCut{"f_zCut", 0.1, "soft drop z cut"}; + Configurable<float> f_beta{"f_beta", 0.0, "soft drop beta"}; + Configurable<float> f_jetR{"f_jetR", 0.4, "jer resolution parameter"}; //possible to get configurable from another task? jetR + Configurable<bool> b_DoConstSub{"b_DoConstSub", false, "do constituent subtraction"}; + + std::vector<fastjet::PseudoJet> jetConstituents; + std::vector<fastjet::PseudoJet> jetReclustered; + JetFinder jetReclusterer; + + void init(InitContext const&) + { + hZg.setObject(new TH1F("h_jet_zg", "zg ;zg", + 10, 0.0, 0.5)); + hRg.setObject(new TH1F("h_jet_rg", "rg ;rg", + 10, 0.0, 0.5)); + hNsd.setObject(new TH1F("h_jet_nsd", "nsd ;nsd", + 7, -0.5, 6.5)); + jetReclusterer.isReclustering = true; + jetReclusterer.algorithm = fastjet::JetAlgorithm::cambridge_algorithm; + jetReclusterer.jetR = f_jetR * 2.5; + } + + //Filter jetCuts = aod::jet::pt > f_jetPtMin; //how does this work? + + void process(aod::Jet const& jet, + aod::Tracks const& tracks, + aod::JetConstituents const& constituents, + aod::JetConstituentsSub const& constituentsSub) + { + jetConstituents.clear(); + jetReclustered.clear(); + if (b_DoConstSub) { + for (const auto constituent : constituentsSub) { + fillConstituents(constituent, jetConstituents); + } + } else { + for (const auto constituentIndex : constituents) { + auto constituent = constituentIndex.track(); + fillConstituents(constituent, jetConstituents); + } + } + fastjet::ClusterSequenceArea clusterSeq(jetReclusterer.findJets(jetConstituents, jetReclustered)); + jetReclustered = sorted_by_pt(jetReclustered); + fastjet::PseudoJet daughterSubJet = jetReclustered[0]; + fastjet::PseudoJet parentSubJet1; + fastjet::PseudoJet parentSubJet2; + bool softDropped = false; + int nsd = 0.0; + auto zg = -1.0; + auto rg = -1.0; + while (daughterSubJet.has_parents(parentSubJet1, parentSubJet2)) { + if (parentSubJet1.perp() < parentSubJet2.perp()) { + std::swap(parentSubJet1, parentSubJet2); + } + auto z = parentSubJet2.perp() / (parentSubJet1.perp() + parentSubJet2.perp()); + auto r = parentSubJet1.delta_R(parentSubJet2); + if (z >= f_zCut * TMath::Power(r / f_jetR, f_beta)) { + if (!softDropped) { + zg = z; + rg = r; + hZg->Fill(zg); + hRg->Fill(rg); + softDropped = true; + } + nsd++; + } + daughterSubJet = parentSubJet1; + } + hNsd->Fill(nsd); + jetSubstructure(zg, rg, nsd); + } +}; +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<JetSubstructure>("jet-substructure")}; +} diff --git a/Analysis/Tasks/PWGLF/CMakeLists.txt b/Analysis/Tasks/PWGLF/CMakeLists.txt new file mode 100644 index 0000000000000..6b1d703be7636 --- /dev/null +++ b/Analysis/Tasks/PWGLF/CMakeLists.txt @@ -0,0 +1,71 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(mc-spectra-efficiency + SOURCES mcspectraefficiency.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(spectra-tof + SOURCES spectraTOF.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(spectra-tpc + SOURCES spectraTPC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(nuclei-spectra + SOURCES NucleiSpectraTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(lambdakzeroproducer + SOURCES lambdakzeroproducer + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(lambdakzeroconsumer + SOURCES lambdakzeroconsumer + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(lambdakzerofinder + SOURCES lambdakzerofinder + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(cascadeproducer + SOURCES cascadeproducer + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(cascadeconsumer + SOURCES cascadeconsumer + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(cascadefinder + SOURCES cascadefinder + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(raacharged + SOURCES raacharged.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(track-checks + SOURCES trackchecks.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) + + diff --git a/Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx b/Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx new file mode 100644 index 0000000000000..6d2a76941dfd4 --- /dev/null +++ b/Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx @@ -0,0 +1,66 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +#include "AnalysisDataModel/PID/PIDResponse.h" + +#include <TH1F.h> +#include <TH2F.h> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct NucleiSpecraTask { + + OutputObj<TH2F> hTPCsignal{TH2F("hTPCsignal", ";#it{p} (GeV/#it{c}); d#it{E} / d#it{X} (a. u.)", 600, 0., 3, 1400, 0, 1400)}; + OutputObj<TH1F> hMomentum{TH1F("hMomentum", ";#it{p} (GeV/#it{c});", 600, 0., 3.)}; + + Configurable<float> absEtaMax{"absEtaMax", 0.8, "pseudo-rapidity edges"}; + Configurable<float> absYmax{"absYmax", 0.5, "rapidity edges"}; + Configurable<float> beamRapidity{"yBeam", 0., "beam rapidity"}; + Configurable<float> chi2TPCperNDF{"chi2TPCperNDF", 4., "chi2 per NDF in TPC"}; + Configurable<float> foundFractionTPC{"foundFractionTPC", 0., "TPC clusters / TPC crossed rows"}; + Configurable<int> recPointsTPC{"recPointsTPC", 0, "clusters in TPC"}; + Configurable<int> signalClustersTPC{"signalClustersTPC", 70, "clusters with PID in TPC"}; + Configurable<float> minEnergyLoss{"minEnergyLoss", 0., "energy loss in TPC"}; + Configurable<int> recPointsITS{"recPointsITS", 2, "number of ITS points"}; + Configurable<int> recPointsITSInnerBarrel{"recPointsITSInnerBarrel", 1, "number of points in ITS Inner Barrel"}; + + Filter etaFilter = aod::track::eta > -1 * absEtaMax&& aod::track::eta < absEtaMax; + Filter chi2Filter = aod::track::tpcChi2NCl < chi2TPCperNDF; + + void process(soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra>> const& tracks) + { + for (auto& track : tracks) { + // Part not covered by filters + if (track.tpcNClsFound() < recPointsTPC) { + continue; + } + if (track.itsNCls() < recPointsITS) { + continue; + } + if (track.itsNClsInnerBarrel() < recPointsITSInnerBarrel) { + continue; + } + + hTPCsignal->Fill(track.tpcInnerParam(), track.tpcSignal()); + hMomentum->Fill(track.p()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<NucleiSpecraTask>("nuclei-spectra")}; +} diff --git a/Analysis/Tasks/PWGLF/cascadeconsumer.cxx b/Analysis/Tasks/PWGLF/cascadeconsumer.cxx new file mode 100644 index 0000000000000..a6ae55727684c --- /dev/null +++ b/Analysis/Tasks/PWGLF/cascadeconsumer.cxx @@ -0,0 +1,170 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 cascade analysis task +// ============================= +// +// This code loops over a CascData table and produces some +// standard analysis output. It requires either +// the cascadefinder or the cascadeproducer tasks +// to have been executed in the workflow (before). +// +// Comments, questions, complaints, suggestions? +// Please write to: +// david.dobrigkeit.chinellato@cern.ch +// + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "ReconstructionDataFormats/Track.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisDataModel/StrangenessTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" + +#include <TFile.h> +#include <TH2F.h> +#include <TProfile.h> +#include <TLorentzVector.h> +#include <Math/Vector4D.h> +#include <TPDGCode.h> +#include <TDatabasePDG.h> +#include <cmath> +#include <array> +#include <cstdlib> +#include "Framework/ASoAHelpers.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; + +struct cascadeQA { + //Basic checks + OutputObj<TH1F> hMassXiMinus{TH1F("hMassXiMinus", "", 3000, 0.0, 3.0)}; + OutputObj<TH1F> hMassXiPlus{TH1F("hMassXiPlus", "", 3000, 0.0, 3.0)}; + OutputObj<TH1F> hMassOmegaMinus{TH1F("hMassOmegaMinus", "", 3000, 0.0, 3.0)}; + OutputObj<TH1F> hMassOmegaPlus{TH1F("hMassOmegaPlus", "", 3000, 0.0, 3.0)}; + + OutputObj<TH1F> hV0Radius{TH1F("hV0Radius", "", 1000, 0.0, 100)}; + OutputObj<TH1F> hCascRadius{TH1F("hCascRadius", "", 1000, 0.0, 100)}; + OutputObj<TH1F> hV0CosPA{TH1F("hV0CosPA", "", 1000, 0.95, 1.0)}; + OutputObj<TH1F> hCascCosPA{TH1F("hCascCosPA", "", 1000, 0.95, 1.0)}; + OutputObj<TH1F> hDCAPosToPV{TH1F("hDCAPosToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCANegToPV{TH1F("hDCANegToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCABachToPV{TH1F("hDCABachToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCAV0ToPV{TH1F("hDCAV0ToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCAV0Dau{TH1F("hDCAV0Dau", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCACascDau{TH1F("hDCACascDau", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hLambdaMass{TH1F("hLambdaMass", "", 1000, 0.0, 10.0)}; + + void process(aod::Collision const& collision, aod::CascDataExt const& Cascades) + { + for (auto& casc : Cascades) { + if (casc.charge() < 0) { //FIXME: could be done better... + hMassXiMinus->Fill(casc.mXi()); + hMassOmegaMinus->Fill(casc.mOmega()); + } else { + hMassXiPlus->Fill(casc.mXi()); + hMassOmegaPlus->Fill(casc.mOmega()); + } + //The basic eleven! + hV0Radius->Fill(casc.v0radius()); + hCascRadius->Fill(casc.cascradius()); + hV0CosPA->Fill(casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + hCascCosPA->Fill(casc.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + hDCAPosToPV->Fill(casc.dcapostopv()); + hDCANegToPV->Fill(casc.dcanegtopv()); + hDCABachToPV->Fill(casc.dcabachtopv()); + hDCAV0ToPV->Fill(casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ())); + hDCAV0Dau->Fill(casc.dcaV0daughters()); + hDCACascDau->Fill(casc.dcacascdaughters()); + hLambdaMass->Fill(casc.mLambda()); + } + } +}; + +struct cascadeconsumer { + OutputObj<TH3F> h3dMassXiMinus{TH3F("h3dMassXiMinus", "", 20, 0, 100, 200, 0, 10, 200, 1.322 - 0.100, 1.322 + 0.100)}; + OutputObj<TH3F> h3dMassXiPlus{TH3F("h3dMassXiPlus", "", 20, 0, 100, 200, 0, 10, 200, 1.322 - 0.100, 1.322 + 0.100)}; + OutputObj<TH3F> h3dMassOmegaMinus{TH3F("h3dMassOmegaMinus", "", 20, 0, 100, 200, 0, 10, 200, 1.672 - 0.100, 1.672 + 0.100)}; + OutputObj<TH3F> h3dMassOmegaPlus{TH3F("h3dMassOmegaPlus", "", 20, 0, 100, 200, 0, 10, 200, 1.672 - 0.100, 1.672 + 0.100)}; + + //Selection criteria + Configurable<double> v0cospa{"v0cospa", 0.999, "V0 CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<double> casccospa{"casccospa", 0.999, "Casc CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcav0dau", 1.0, "DCA V0 Daughters"}; + Configurable<float> dcacascdau{"dcacascdau", .3, "DCA Casc Daughters"}; + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<float> dcabachtopv{"dcabachtopv", .1, "DCA Bach To PV"}; + Configurable<float> dcav0topv{"dcav0topv", .1, "DCA V0 To PV"}; + Configurable<float> v0radius{"v0radius", 2.0, "v0radius"}; + Configurable<float> cascradius{"cascradius", 1.0, "cascradius"}; + Configurable<float> v0masswindow{"v0masswindow", 0.008, "v0masswindow"}; + + Filter preFilterV0 = + aod::cascdata::dcapostopv > dcapostopv&& aod::cascdata::dcanegtopv > dcanegtopv&& + aod::cascdata::dcabachtopv > dcabachtopv&& + aod::cascdata::dcaV0daughters < dcav0dau&& aod::cascdata::dcacascdaughters < dcacascdau; + + void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator const& collision, soa::Filtered<aod::CascDataExt> const& Cascades) + { + if (!collision.alias()[kINT7]) { + return; + } + if (!collision.sel7()) { + return; + } + for (auto& casc : Cascades) { + //FIXME: dynamic columns cannot be filtered on? + if (casc.v0radius() > v0radius && + casc.cascradius() > cascradius && + casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) > v0cospa && + casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()) > casccospa && + casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ()) > dcav0topv) { + if (casc.charge() < 0) { //FIXME: could be done better... + if (TMath::Abs(casc.yXi()) < 0.5) { + h3dMassXiMinus->Fill(collision.centV0M(), casc.pt(), casc.mXi()); + } + if (TMath::Abs(casc.yOmega()) < 0.5) { + h3dMassOmegaMinus->Fill(collision.centV0M(), casc.pt(), casc.mOmega()); + } + } else { + if (TMath::Abs(casc.yXi()) < 0.5) { + h3dMassXiPlus->Fill(collision.centV0M(), casc.pt(), casc.mXi()); + } + if (TMath::Abs(casc.yOmega()) < 0.5) { + h3dMassOmegaPlus->Fill(collision.centV0M(), casc.pt(), casc.mOmega()); + } + } + } + } + } +}; + +/// Extends the v0data table with expression columns +struct cascadeinitializer { + Spawns<aod::CascDataExt> cascdataext; + void init(InitContext const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<cascadeconsumer>("lf-cascadeconsumer"), + adaptAnalysisTask<cascadeQA>("lf-cascadeQA"), + adaptAnalysisTask<cascadeinitializer>("lf-cascadeinitializer")}; +} diff --git a/Analysis/Tasks/PWGLF/cascadefinder.cxx b/Analysis/Tasks/PWGLF/cascadefinder.cxx new file mode 100644 index 0000000000000..150aa0780ac0d --- /dev/null +++ b/Analysis/Tasks/PWGLF/cascadefinder.cxx @@ -0,0 +1,438 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Cascade Finder task +// =================== +// +// This code loops over existing V0s and bachelor tracks and finds +// valid cascade candidates from scratch using a certain set of +// minimum (configurable) selection criteria. +// +// It is different than the producer: the producer merely +// loops over an *existing* list of cascades (V0+bachelor track +// indices) and calculates the corresponding full cascade information +// +// In both cases, any analysis should loop over the "CascData" +// table as that table contains all information. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// david.dobrigkeit.chinellato@cern.ch +// + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/HFSecondaryVertex.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "ReconstructionDataFormats/Track.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/StrangenessTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" + +#include <TFile.h> +#include <TLorentzVector.h> +#include <TH1F.h> +#include <TH2F.h> +#include <TProfile.h> +#include <Math/Vector4D.h> +#include <TPDGCode.h> +#include <TDatabasePDG.h> +#include <cmath> +#include <array> +#include <cstdlib> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; +using namespace ROOT::Math; + +namespace o2::aod +{ + +namespace cascgoodpostracks +{ +DECLARE_SOA_INDEX_COLUMN_FULL(GoodPosTrack, goodPosTrack, int, FullTracks, "fGoodPosTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(DCAXY, dcaXY, float); +} // namespace cascgoodpostracks +DECLARE_SOA_TABLE(CascGoodPosTracks, "AOD", "CASCGOODPTRACKS", o2::soa::Index<>, cascgoodpostracks::GoodPosTrackId, cascgoodpostracks::CollisionId, cascgoodpostracks::DCAXY); +namespace cascgoodnegtracks +{ +DECLARE_SOA_INDEX_COLUMN_FULL(GoodNegTrack, goodNegTrack, int, FullTracks, "fGoodNegTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(DCAXY, dcaXY, float); +} // namespace cascgoodnegtracks +DECLARE_SOA_TABLE(CascGoodNegTracks, "AOD", "CASCGOODNTRACKS", o2::soa::Index<>, cascgoodnegtracks::GoodNegTrackId, cascgoodnegtracks::CollisionId, cascgoodnegtracks::DCAXY); +namespace cascgoodlambdas +{ +DECLARE_SOA_INDEX_COLUMN_FULL(GoodLambda, goodLambda, int, V0DataExt, "fGoodLambdaId"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +} // namespace cascgoodlambdas +DECLARE_SOA_TABLE(CascGoodLambdas, "AOD", "CASCGOODLAM", o2::soa::Index<>, cascgoodlambdas::GoodLambdaId, cascgoodlambdas::CollisionId); +namespace cascgoodantilambdas +{ +DECLARE_SOA_INDEX_COLUMN_FULL(GoodAntiLambda, goodAntiLambda, int, V0DataExt, "fGoodAntiLambdaId"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +} // namespace cascgoodantilambdas +DECLARE_SOA_TABLE(CascGoodAntiLambdas, "AOD", "CASCGOODALAM", o2::soa::Index<>, cascgoodantilambdas::GoodAntiLambdaId, cascgoodantilambdas::CollisionId); +} // namespace o2::aod + +struct cascadeprefilter { + Configurable<float> dcabachtopv{"dcabachtopv", .1, "DCA Bach To PV"}; + Configurable<int> mincrossedrows{"mincrossedrows", 70, "min crossed rows"}; + Configurable<float> dcav0topv{"dcav0topv", .1, "DCA V0 To PV"}; + Configurable<double> cospaV0{"cospaV0", .98, "CosPA V0"}; + Configurable<double> v0radius{"v0radius", 0.9, "v0radius"}; + Configurable<float> lambdamasswindow{"lambdamasswindow", .006, "Distance from Lambda mass"}; + Configurable<float> dcav0dau{"dcav0dau", .6, "DCA V0 Daughters"}; + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + + Produces<aod::CascGoodLambdas> cascGoodLambdas; + Produces<aod::CascGoodAntiLambdas> cascGoodAntiLambdas; + Produces<aod::CascGoodPosTracks> cascGoodPosTracks; + Produces<aod::CascGoodNegTracks> cascGoodNegTracks; + + Partition<soa::Join<aod::FullTracks, aod::TracksExtended>> goodPosTracks = aod::track::signed1Pt > 0.0f && aod::track::dcaXY > dcabachtopv; + Partition<soa::Join<aod::FullTracks, aod::TracksExtended>> goodNegTracks = aod::track::signed1Pt < 0.0f && aod::track::dcaXY < -dcabachtopv; + + Partition<aod::V0DataExt> goodV0s = aod::v0data::dcapostopv > dcapostopv&& aod::v0data::dcanegtopv > dcanegtopv&& aod::v0data::dcaV0daughters < dcav0dau; + + using FullTracksExt = soa::Join<aod::FullTracks, aod::TracksExtended>; + + void process(aod::Collision const& collision, + FullTracksExt const& tracks, + aod::V0DataExt const& V0s) + { + for (auto& t0 : goodPosTracks) { + if (!(t0.trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + if (t0.tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + cascGoodPosTracks(t0.globalIndex(), t0.collisionId(), t0.dcaXY()); + } + for (auto& t0 : goodNegTracks) { + if (!(t0.trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + if (t0.tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + cascGoodNegTracks(t0.globalIndex(), t0.collisionId(), -t0.dcaXY()); + } + for (auto& v0 : goodV0s) { + if (v0.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < cospaV0) { + continue; + } + if (v0.dcav0topv(collision.posX(), collision.posY(), collision.posZ()) < dcav0topv) { + continue; + } + if (v0.dcaV0daughters() > dcav0dau) { + continue; + } + if (v0.v0radius() < v0radius) { + continue; + } + + if (fabs(v0.mLambda() - 1.116) < lambdamasswindow) { + cascGoodLambdas(v0.globalIndex(), v0.collisionId()); + } + if (fabs(v0.mAntiLambda() - 1.116) < lambdamasswindow) { + cascGoodAntiLambdas(v0.globalIndex(), v0.collisionId()); + } + } + } +}; + +struct cascadefinder { + Produces<aod::CascData> cascdata; + + OutputObj<TH1F> hCandPerEvent{TH1F("hCandPerEvent", "", 100, 0, 100)}; + + //Configurables + Configurable<double> d_bz{"d_bz", +5.0, "bz field"}; + Configurable<double> d_UseAbsDCA{"d_UseAbsDCA", kTRUE, "Use Abs DCAs"}; + + //Selection criteria + Configurable<double> v0cospa{"casccospa", 0.998, "Casc CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcacascdau", 1.0, "DCA Casc Daughters"}; + Configurable<float> v0radius{"cascradius", 1.0, "cascradius"}; + + //Process: subscribes to a lot of things! + void process(aod::Collision const& collision, + aod::FullTracks const& tracks, + aod::V0DataExt const& V0s, + aod::CascGoodLambdas const& lambdas, + aod::CascGoodAntiLambdas const& antiLambdas, + aod::CascGoodPosTracks const& pBachtracks, + aod::CascGoodNegTracks const& nBachtracks) + { + //Define o2 fitter, 2-prong + o2::vertexing::DCAFitterN<2> fitterV0, fitterCasc; + fitterV0.setBz(d_bz); + fitterV0.setPropagateToPCA(true); + fitterV0.setMaxR(200.); + fitterV0.setMinParamChange(1e-3); + fitterV0.setMinRelChi2Change(0.9); + fitterV0.setMaxDZIni(1e9); + fitterV0.setMaxChi2(1e9); + fitterV0.setUseAbsDCA(d_UseAbsDCA); + + fitterCasc.setBz(d_bz); + fitterCasc.setPropagateToPCA(true); + fitterCasc.setMaxR(200.); + fitterCasc.setMinParamChange(1e-3); + fitterCasc.setMinRelChi2Change(0.9); + fitterCasc.setMaxDZIni(1e9); + fitterCasc.setMaxChi2(1e9); + fitterCasc.setUseAbsDCA(d_UseAbsDCA); + + Long_t lNCand = 0; + + std::array<float, 3> pVtx = {collision.posX(), collision.posY(), collision.posZ()}; + std::array<float, 3> pos = {0.}; + std::array<float, 3> posXi = {0.}; + std::array<float, 3> pvecpos = {0.}; + std::array<float, 3> pvecneg = {0.}; + std::array<float, 3> pvecbach = {0.}; + + //Cascades first + for (auto& v0id : lambdas) { + //required: de-reference the tracks for cascade building + auto v0 = v0id.goodLambda(); + auto pTrack = getTrackParCov(v0.posTrack()); + auto nTrack = getTrackParCov(v0.negTrack()); + //Let's do the slow part first: the V0 recalculation from scratch + int nCand = fitterV0.process(pTrack, nTrack); + if (nCand != 0) { + fitterV0.propagateTracksToVertex(); + const auto& v0vtx = fitterV0.getPCACandidate(); + for (int i = 0; i < 3; i++) { + pos[i] = v0vtx[i]; + } + + std::array<float, 21> cov0 = {0}; + std::array<float, 21> cov1 = {0}; + std::array<float, 21> covV0 = {0}; + + //Covariance matrix calculation + const int momInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + fitterV0.getTrack(0).getPxPyPzGlo(pvecpos); + fitterV0.getTrack(1).getPxPyPzGlo(pvecneg); + fitterV0.getTrack(0).getCovXYZPxPyPzGlo(cov0); + fitterV0.getTrack(1).getCovXYZPxPyPzGlo(cov1); + for (int i = 0; i < 6; i++) { + int j = momInd[i]; + covV0[j] = cov0[j] + cov1[j]; + } + auto covVtxV0 = fitterV0.calcPCACovMatrix(); + covV0[0] = covVtxV0(0, 0); + covV0[1] = covVtxV0(1, 0); + covV0[2] = covVtxV0(1, 1); + covV0[3] = covVtxV0(2, 0); + covV0[4] = covVtxV0(2, 1); + covV0[5] = covVtxV0(2, 2); + + const std::array<float, 3> vertex = {(float)v0vtx[0], (float)v0vtx[1], (float)v0vtx[2]}; + const std::array<float, 3> momentum = {pvecpos[0] + pvecneg[0], pvecpos[1] + pvecneg[1], pvecpos[2] + pvecneg[2]}; + + auto tV0 = o2::track::TrackParCov(vertex, momentum, covV0, 0); + tV0.setQ2Pt(0); //No bending, please + + for (auto& t0id : nBachtracks) { + auto t0 = t0id.goodNegTrack(); + auto bTrack = getTrackParCov(t0); + + int nCand2 = fitterCasc.process(tV0, bTrack); + if (nCand2 != 0) { + fitterCasc.propagateTracksToVertex(); + const auto& cascvtx = fitterCasc.getPCACandidate(); + for (int i = 0; i < 3; i++) { + posXi[i] = cascvtx[i]; + } + fitterCasc.getTrack(1).getPxPyPzGlo(pvecbach); + + lNCand++; + //If we got here, it means this is a good candidate! + cascdata(v0.globalIndex(), v0.posTrack().globalIndex(), v0.negTrack().collisionId(), + -1, posXi[0], posXi[1], posXi[2], pos[0], pos[1], pos[2], + pvecpos[0], pvecpos[1], pvecpos[2], + pvecneg[0], pvecneg[1], pvecneg[2], + pvecbach[0], pvecbach[1], pvecbach[2], + fitterV0.getChi2AtPCACandidate(), fitterCasc.getChi2AtPCACandidate(), + v0.dcapostopv(), + v0.dcanegtopv(), + t0id.dcaXY()); + } //end if cascade recoed + } //end loop over bachelor + } //end if v0 recoed + } //end loop over cascades + + //Anticascades + for (auto& v0id : antiLambdas) { + //required: de-reference the tracks for cascade building + auto v0 = v0id.goodAntiLambda(); + auto pTrack = getTrackParCov(v0.posTrack()); + auto nTrack = getTrackParCov(v0.negTrack()); + //Let's do the slow part first: the V0 recalculation from scratch + int nCand = fitterV0.process(pTrack, nTrack); + if (nCand != 0) { + fitterV0.propagateTracksToVertex(); + const auto& v0vtx = fitterV0.getPCACandidate(); + for (int i = 0; i < 3; i++) { + pos[i] = v0vtx[i]; + } + + std::array<float, 21> cov0 = {0}; + std::array<float, 21> cov1 = {0}; + std::array<float, 21> covV0 = {0}; + + //Covariance matrix calculation + const int momInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + fitterV0.getTrack(0).getPxPyPzGlo(pvecpos); + fitterV0.getTrack(1).getPxPyPzGlo(pvecneg); + fitterV0.getTrack(0).getCovXYZPxPyPzGlo(cov0); + fitterV0.getTrack(1).getCovXYZPxPyPzGlo(cov1); + for (int i = 0; i < 6; i++) { + int j = momInd[i]; + covV0[j] = cov0[j] + cov1[j]; + } + auto covVtxV0 = fitterV0.calcPCACovMatrix(); + covV0[0] = covVtxV0(0, 0); + covV0[1] = covVtxV0(1, 0); + covV0[2] = covVtxV0(1, 1); + covV0[3] = covVtxV0(2, 0); + covV0[4] = covVtxV0(2, 1); + covV0[5] = covVtxV0(2, 2); + + const std::array<float, 3> vertex = {(float)v0vtx[0], (float)v0vtx[1], (float)v0vtx[2]}; + const std::array<float, 3> momentum = {pvecpos[0] + pvecneg[0], pvecpos[1] + pvecneg[1], pvecpos[2] + pvecneg[2]}; + + auto tV0 = o2::track::TrackParCov(vertex, momentum, covV0, 0); + tV0.setQ2Pt(0); //No bending, please + + for (auto& t0id : pBachtracks) { + auto t0 = t0id.goodPosTrack(); + auto bTrack = getTrackParCov(t0); + + int nCand2 = fitterCasc.process(tV0, bTrack); + if (nCand2 != 0) { + fitterCasc.propagateTracksToVertex(); + const auto& cascvtx = fitterCasc.getPCACandidate(); + for (int i = 0; i < 3; i++) { + posXi[i] = cascvtx[i]; + } + fitterCasc.getTrack(1).getPxPyPzGlo(pvecbach); + + lNCand++; + //If we got here, it means this is a good candidate! + cascdata(v0.globalIndex(), v0.posTrack().globalIndex(), v0.negTrack().collisionId(), + +1, posXi[0], posXi[1], posXi[2], pos[0], pos[1], pos[2], + pvecpos[0], pvecpos[1], pvecpos[2], + pvecneg[0], pvecneg[1], pvecneg[2], + pvecbach[0], pvecbach[1], pvecbach[2], + fitterV0.getChi2AtPCACandidate(), fitterCasc.getChi2AtPCACandidate(), + v0.dcapostopv(), + v0.dcanegtopv(), + t0id.dcaXY()); + } //end if cascade recoed + } //end loop over bachelor + } //end if v0 recoed + } //end loop over anticascades + + hCandPerEvent->Fill(lNCand); + } +}; + +struct cascadefinderQA { + //Basic checks + OutputObj<TH3F> h3dMassXiMinus{TH3F("h3dMassXiMinus", "", 20, 0, 100, 200, 0, 10, 200, 1.322 - 0.100, 1.322 + 0.100)}; + OutputObj<TH3F> h3dMassXiPlus{TH3F("h3dMassXiPlus", "", 20, 0, 100, 200, 0, 10, 200, 1.322 - 0.100, 1.322 + 0.100)}; + OutputObj<TH3F> h3dMassOmegaMinus{TH3F("h3dMassOmegaMinus", "", 20, 0, 100, 200, 0, 10, 200, 1.672 - 0.100, 1.672 + 0.100)}; + OutputObj<TH3F> h3dMassOmegaPlus{TH3F("h3dMassOmegaPlus", "", 20, 0, 100, 200, 0, 10, 200, 1.672 - 0.100, 1.672 + 0.100)}; + + //Selection criteria + Configurable<double> v0cospa{"v0cospa", 0.999, "V0 CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<double> casccospa{"casccospa", 0.999, "Casc CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcav0dau", 1.0, "DCA V0 Daughters"}; + Configurable<float> dcacascdau{"dcacascdau", .3, "DCA Casc Daughters"}; + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<float> dcabachtopv{"dcabachtopv", .1, "DCA Bach To PV"}; + Configurable<float> dcav0topv{"dcav0topv", .1, "DCA V0 To PV"}; + Configurable<float> v0radius{"v0radius", 2.0, "v0radius"}; + Configurable<float> cascradius{"cascradius", 1.0, "cascradius"}; + Configurable<float> v0masswindow{"v0masswindow", 0.008, "v0masswindow"}; + + Filter preFilterV0 = + aod::cascdata::dcapostopv > dcapostopv&& aod::cascdata::dcanegtopv > dcanegtopv&& + aod::cascdata::dcabachtopv > dcabachtopv&& + aod::cascdata::dcaV0daughters < dcav0dau&& aod::cascdata::dcacascdaughters < dcacascdau; + + ///Connect to CascFinderData: newly indexed, note: CascDataExt table incompatible with standard V0 table! + void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator const& collision, soa::Filtered<aod::CascDataExt> const& Cascades) + { + if (!collision.alias()[kINT7]) { + return; + } + if (!collision.sel7()) { + return; + } + for (auto& casc : Cascades) { + //FIXME: dynamic columns cannot be filtered on? + if (casc.v0radius() > v0radius && + casc.cascradius() > cascradius && + casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) > v0cospa && + casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()) > casccospa && + casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ()) > dcav0topv) { + if (casc.charge() < 0) { //FIXME: could be done better... + if (TMath::Abs(casc.yXi()) < 0.5) { + h3dMassXiMinus->Fill(collision.centV0M(), casc.pt(), casc.mXi()); + } + if (TMath::Abs(casc.yOmega()) < 0.5) { + h3dMassOmegaMinus->Fill(collision.centV0M(), casc.pt(), casc.mOmega()); + } + } else { + if (TMath::Abs(casc.yXi()) < 0.5) { + h3dMassXiPlus->Fill(collision.centV0M(), casc.pt(), casc.mXi()); + } + if (TMath::Abs(casc.yOmega()) < 0.5) { + h3dMassOmegaPlus->Fill(collision.centV0M(), casc.pt(), casc.mOmega()); + } + } + } + } + } +}; + +/// Extends the v0data table with expression columns +struct cascadeinitializer { + Spawns<aod::CascDataExt> cascdataext; + void init(InitContext const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<cascadeprefilter>("lf-cascadeprefilter"), + adaptAnalysisTask<cascadefinder>("lf-cascadefinder"), + adaptAnalysisTask<cascadeinitializer>("lf-cascadeinitializer"), + adaptAnalysisTask<cascadefinderQA>("lf-cascadefinderQA")}; +} diff --git a/Analysis/Tasks/PWGLF/cascadeproducer.cxx b/Analysis/Tasks/PWGLF/cascadeproducer.cxx new file mode 100644 index 0000000000000..eee9c39beb25c --- /dev/null +++ b/Analysis/Tasks/PWGLF/cascadeproducer.cxx @@ -0,0 +1,283 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Cascade Producer task +// ===================== +// +// This task loops over an *existing* list of cascades (V0+bachelor track +// indices) and calculates the corresponding full cascade information +// +// Any analysis should loop over the "CascData" +// table as that table contains all information +// +// WARNING: adding filters to the producer IS NOT +// equivalent to re-running the finders. This will only +// ever produce *tighter* selection sections. It is your +// responsibility to check if, by setting a loose filter +// setting, you are going into a region in which no +// candidates exist because the original indices were generated +// using tigher selections. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// david.dobrigkeit.chinellato@cern.ch +// + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "ReconstructionDataFormats/Track.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/StrangenessTables.h" + +#include <TFile.h> +#include <TH2F.h> +#include <TProfile.h> +#include <TLorentzVector.h> +#include <Math/Vector4D.h> +#include <TPDGCode.h> +#include <TDatabasePDG.h> +#include <cmath> +#include <array> +#include <cstdlib> +#include "Framework/ASoAHelpers.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; + +namespace o2::aod +{ + +namespace cascgood +{ +DECLARE_SOA_INDEX_COLUMN_FULL(V0, v0, int, V0DataExt, "fV0Id"); +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, FullTracks, "fTracksID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +} // namespace cascgood +DECLARE_SOA_TABLE(CascGood, "AOD", "CASCGOOD", o2::soa::Index<>, + cascgood::V0Id, cascgood::BachelorId, + cascgood::CollisionId); +} // namespace o2::aod + +using FullTracksExt = soa::Join<aod::FullTracks, aod::TracksExtended>; + +//This prefilter creates a skimmed list of good V0s to re-reconstruct so that +//CPU is saved in case there are specific selections that are to be done +//Note: more configurables, more options to be added as needed +struct cascadeprefilterpairs { + Configurable<int> mincrossedrows{"mincrossedrows", 70, "min crossed rows"}; + Configurable<float> dcav0topv{"dcav0topv", .1, "DCA V0 To PV"}; + Configurable<double> cospaV0{"cospaV0", .98, "CosPA V0"}; + Configurable<float> lambdamasswindow{"lambdamasswindow", .006, "Distance from Lambda mass"}; + Configurable<float> dcav0dau{"dcav0dau", .6, "DCA V0 Daughters"}; + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<float> dcabachtopv{"dcabachtopv", .1, "DCA Bach To PV"}; + Configurable<bool> tpcrefit{"tpcrefit", 1, "demand TPC refit"}; + Configurable<double> v0radius{"v0radius", 0.9, "v0radius"}; + + Produces<aod::CascGood> cascgood; + void process(aod::Collision const& collision, aod::V0DataExt const& V0s, aod::Cascades const& Cascades, + soa::Join<aod::FullTracks, aod::TracksExtended> const& tracks) + { + for (auto& casc : Cascades) { + //Single-track properties filter + if (tpcrefit) { + if (!(casc.v0_as<o2::aod::V0DataExt>().posTrack_as<FullTracksExt>().trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + if (!(casc.v0_as<o2::aod::V0DataExt>().negTrack_as<FullTracksExt>().trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + if (!(casc.bachelor_as<FullTracksExt>().trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + } + if (casc.v0_as<o2::aod::V0DataExt>().posTrack_as<FullTracksExt>().tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().negTrack_as<FullTracksExt>().tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + if (casc.bachelor_as<FullTracksExt>().tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().posTrack_as<FullTracksExt>().dcaXY() < dcapostopv) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().negTrack_as<FullTracksExt>().dcaXY() < dcanegtopv) { + continue; + } + if (casc.bachelor_as<FullTracksExt>().dcaXY() < dcabachtopv) { + continue; + } + + //V0 selections + if (fabs(casc.v0_as<o2::aod::V0DataExt>().mLambda() - 1.116) > lambdamasswindow && fabs(casc.v0_as<o2::aod::V0DataExt>().mAntiLambda() - 1.116) > lambdamasswindow) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().dcaV0daughters() > dcav0dau) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().v0radius() < v0radius) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < cospaV0) { + continue; + } + if (casc.v0_as<o2::aod::V0DataExt>().dcav0topv(collision.posX(), collision.posY(), collision.posZ()) < dcav0topv) { + continue; + } + cascgood( + casc.v0_as<o2::aod::V0DataExt>().globalIndex(), + casc.bachelor_as<FullTracksExt>().globalIndex(), + casc.bachelor_as<FullTracksExt>().collisionId()); + } + } +}; + +/// Cascade builder task: rebuilds cascades +struct cascadeproducer { + Produces<aod::CascData> cascdata; + + OutputObj<TH1F> hEventCounter{TH1F("hEventCounter", "", 1, 0, 1)}; + OutputObj<TH1F> hCascCandidate{TH1F("hCascCandidate", "", 10, 0, 10)}; + + //Configurables + Configurable<double> d_bz{"d_bz", -5.0, "bz field"}; + Configurable<double> d_UseAbsDCA{"d_UseAbsDCA", kTRUE, "Use Abs DCAs"}; + + void process(aod::Collision const& collision, aod::V0DataExt const& V0s, aod::CascGood const& Cascades, aod::FullTracks const& tracks) + { + //Define o2 fitter, 2-prong + o2::vertexing::DCAFitterN<2> fitterV0, fitterCasc; + fitterV0.setBz(d_bz); + fitterV0.setPropagateToPCA(true); + fitterV0.setMaxR(200.); + fitterV0.setMinParamChange(1e-3); + fitterV0.setMinRelChi2Change(0.9); + fitterV0.setMaxDZIni(1e9); + fitterV0.setMaxChi2(1e9); + fitterV0.setUseAbsDCA(d_UseAbsDCA); + + fitterCasc.setBz(d_bz); + fitterCasc.setPropagateToPCA(true); + fitterCasc.setMaxR(200.); + fitterCasc.setMinParamChange(1e-3); + fitterCasc.setMinRelChi2Change(0.9); + fitterCasc.setMaxDZIni(1e9); + fitterCasc.setMaxChi2(1e9); + fitterCasc.setUseAbsDCA(d_UseAbsDCA); + + hEventCounter->Fill(0.5); + std::array<float, 3> pVtx = {collision.posX(), collision.posY(), collision.posZ()}; + + for (auto& casc : Cascades) { + auto charge = -1; + std::array<float, 3> pos = {0.}; + std::array<float, 3> posXi = {0.}; + std::array<float, 3> pvecpos = {0.}; + std::array<float, 3> pvecneg = {0.}; + std::array<float, 3> pvecbach = {0.}; + + hCascCandidate->Fill(0.5); + + //Acquire basic tracks + auto pTrack = getTrackParCov(casc.v0_as<o2::aod::V0DataExt>().posTrack_as<FullTracksExt>()); + auto nTrack = getTrackParCov(casc.v0_as<o2::aod::V0DataExt>().negTrack_as<FullTracksExt>()); + auto bTrack = getTrackParCov(casc.bachelor_as<FullTracksExt>()); + if (casc.bachelor().signed1Pt() > 0) { + charge = +1; + } + + int nCand = fitterV0.process(pTrack, nTrack); + if (nCand != 0) { + fitterV0.propagateTracksToVertex(); + hCascCandidate->Fill(1.5); + const auto& v0vtx = fitterV0.getPCACandidate(); + for (int i = 0; i < 3; i++) { + pos[i] = v0vtx[i]; + } + + std::array<float, 21> cov0 = {0}; + std::array<float, 21> cov1 = {0}; + std::array<float, 21> covV0 = {0}; + + //Covariance matrix calculation + const int momInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + fitterV0.getTrack(0).getPxPyPzGlo(pvecpos); + fitterV0.getTrack(1).getPxPyPzGlo(pvecneg); + fitterV0.getTrack(0).getCovXYZPxPyPzGlo(cov0); + fitterV0.getTrack(1).getCovXYZPxPyPzGlo(cov1); + for (int i = 0; i < 6; i++) { + int j = momInd[i]; + covV0[j] = cov0[j] + cov1[j]; + } + auto covVtxV0 = fitterV0.calcPCACovMatrix(); + covV0[0] = covVtxV0(0, 0); + covV0[1] = covVtxV0(1, 0); + covV0[2] = covVtxV0(1, 1); + covV0[3] = covVtxV0(2, 0); + covV0[4] = covVtxV0(2, 1); + covV0[5] = covVtxV0(2, 2); + + const std::array<float, 3> vertex = {(float)v0vtx[0], (float)v0vtx[1], (float)v0vtx[2]}; + const std::array<float, 3> momentum = {pvecpos[0] + pvecneg[0], pvecpos[1] + pvecneg[1], pvecpos[2] + pvecneg[2]}; + + auto tV0 = o2::track::TrackParCov(vertex, momentum, covV0, 0); + tV0.setQ2Pt(0); //No bending, please + int nCand2 = fitterCasc.process(tV0, bTrack); + if (nCand2 != 0) { + fitterCasc.propagateTracksToVertex(); + hCascCandidate->Fill(2.5); + const auto& cascvtx = fitterCasc.getPCACandidate(); + for (int i = 0; i < 3; i++) { + posXi[i] = cascvtx[i]; + } + fitterCasc.getTrack(1).getPxPyPzGlo(pvecbach); + } //end if cascade recoed + } //end if v0 recoed + //Fill table, please + cascdata( + casc.v0_as<o2::aod::V0DataExt>().globalIndex(), + casc.bachelor_as<FullTracksExt>().globalIndex(), + casc.bachelor_as<FullTracksExt>().collisionId(), + charge, posXi[0], posXi[1], posXi[2], pos[0], pos[1], pos[2], + pvecpos[0], pvecpos[1], pvecpos[2], + pvecneg[0], pvecneg[1], pvecneg[2], + pvecbach[0], pvecbach[1], pvecbach[2], + fitterV0.getChi2AtPCACandidate(), fitterCasc.getChi2AtPCACandidate(), + casc.v0().posTrack_as<FullTracksExt>().dcaXY(), + casc.v0().negTrack_as<FullTracksExt>().dcaXY(), + casc.bachelor_as<FullTracksExt>().dcaXY()); + } + } +}; + +/// Extends the v0data table with expression columns +struct cascprodinitializer { + Spawns<aod::V0DataExt> v0dataext; + void init(InitContext const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<cascprodinitializer>("lf-cascprodinitializer"), + adaptAnalysisTask<cascadeprefilterpairs>("lf-cascadeprefilterpairs"), + adaptAnalysisTask<cascadeproducer>("lf-cascadeproducer")}; +} diff --git a/Analysis/Tasks/PWGLF/lambdakzeroconsumer.cxx b/Analysis/Tasks/PWGLF/lambdakzeroconsumer.cxx new file mode 100644 index 0000000000000..35a5242b1e292 --- /dev/null +++ b/Analysis/Tasks/PWGLF/lambdakzeroconsumer.cxx @@ -0,0 +1,133 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V0 analysis task +// ======================== +// +// This code loops over a V0Data table and produces some +// standard analysis output. It requires either +// the lambdakzerofinder or the lambdakzeroproducer tasks +// to have been executed in the workflow (before). +// +// Comments, questions, complaints, suggestions? +// Please write to: +// david.dobrigkeit.chinellato@cern.ch +// +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "ReconstructionDataFormats/Track.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisDataModel/StrangenessTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" + +#include <TFile.h> +#include <TH2F.h> +#include <TProfile.h> +#include <TLorentzVector.h> +#include <Math/Vector4D.h> +#include <TPDGCode.h> +#include <TDatabasePDG.h> +#include <cmath> +#include <array> +#include <cstdlib> +#include "Framework/ASoAHelpers.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; + +struct lambdakzeroQA { + //Basic checks + OutputObj<TH1F> hMassK0Short{TH1F("hMassK0Short", "", 3000, 0.0, 3.0)}; + OutputObj<TH1F> hMassLambda{TH1F("hMassLambda", "", 3000, 0.0, 3.0)}; + OutputObj<TH1F> hMassAntiLambda{TH1F("hMassAntiLambda", "", 3000, 0.0, 3.0)}; + + OutputObj<TH1F> hV0Radius{TH1F("hV0Radius", "", 1000, 0.0, 100)}; + OutputObj<TH1F> hV0CosPA{TH1F("hV0CosPA", "", 1000, 0.95, 1.0)}; + OutputObj<TH1F> hDCAPosToPV{TH1F("hDCAPosToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCANegToPV{TH1F("hDCANegToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCAV0Dau{TH1F("hDCAV0Dau", "", 1000, 0.0, 10.0)}; + + void process(aod::Collision const& collision, aod::V0DataExt const& fullV0s) + { + for (auto& v0 : fullV0s) { + hMassLambda->Fill(v0.mLambda()); + hMassAntiLambda->Fill(v0.mAntiLambda()); + hMassK0Short->Fill(v0.mK0Short()); + + hV0Radius->Fill(v0.v0radius()); + hV0CosPA->Fill(v0.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + + hDCAPosToPV->Fill(v0.dcapostopv()); + hDCANegToPV->Fill(v0.dcanegtopv()); + hDCAV0Dau->Fill(v0.dcaV0daughters()); + } + } +}; + +struct lambdakzeroconsumer { + OutputObj<TH3F> h3dMassK0Short{TH3F("h3dMassK0Short", "", 20, 0, 100, 200, 0, 10, 200, 0.450, 0.550)}; + OutputObj<TH3F> h3dMassLambda{TH3F("h3dMassLambda", "", 20, 0, 100, 200, 0, 10, 200, 1.115 - 0.100, 1.115 + 0.100)}; + OutputObj<TH3F> h3dMassAntiLambda{TH3F("h3dMassAntiLambda", "", 20, 0, 100, 200, 0, 10, 200, 1.115 - 0.100, 1.115 + 0.100)}; + + //Selection criteria + Configurable<double> v0cospa{"v0cospa", 0.995, "V0 CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcav0dau", 1.0, "DCA V0 Daughters"}; + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<float> v0radius{"v0radius", 5.0, "v0radius"}; + + Filter preFilterV0 = aod::v0data::dcapostopv > dcapostopv&& + aod::v0data::dcanegtopv > dcanegtopv&& aod::v0data::dcaV0daughters < dcav0dau; + + void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator const& collision, soa::Filtered<aod::V0DataExt> const& fullV0s) + { + if (!collision.alias()[kINT7]) { + return; + } + if (!collision.sel7()) { + return; + } + + for (auto& v0 : fullV0s) { + //FIXME: could not find out how to filter cosPA and radius variables (dynamic columns) + if (v0.v0radius() > v0radius && v0.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) > v0cospa) { + if (TMath::Abs(v0.yLambda()) < 0.5) { + h3dMassLambda->Fill(collision.centV0M(), v0.pt(), v0.mLambda()); + h3dMassAntiLambda->Fill(collision.centV0M(), v0.pt(), v0.mAntiLambda()); + } + if (TMath::Abs(v0.yK0Short()) < 0.5) { + h3dMassK0Short->Fill(collision.centV0M(), v0.pt(), v0.mK0Short()); + } + } + } + } +}; + +/// Extends the v0data table with expression columns +struct lambdakzeroinitializer { + Spawns<aod::V0DataExt> v0dataext; + void init(InitContext const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<lambdakzeroconsumer>("lf-lambdakzeroconsumer"), + adaptAnalysisTask<lambdakzeroQA>("lf-lambdakzeroQA"), + adaptAnalysisTask<lambdakzeroinitializer>("lf-lambdakzeroinitializer")}; +} diff --git a/Analysis/Tasks/PWGLF/lambdakzerofinder.cxx b/Analysis/Tasks/PWGLF/lambdakzerofinder.cxx new file mode 100644 index 0000000000000..f71b9347bc3b4 --- /dev/null +++ b/Analysis/Tasks/PWGLF/lambdakzerofinder.cxx @@ -0,0 +1,282 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// V0 Finder task +// ============== +// +// This code loops over positive and negative tracks and finds +// valid V0 candidates from scratch using a certain set of +// minimum (configurable) selection criteria. +// +// It is different than the producer: the producer merely +// loops over an *existing* list of V0s (pos+neg track +// indices) and calculates the corresponding full V0 information +// +// In both cases, any analysis should loop over the "V0Data" +// table as that table contains all information. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// david.dobrigkeit.chinellato@cern.ch +// + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "ReconstructionDataFormats/Track.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/StrangenessTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" + +#include <TFile.h> +#include <TLorentzVector.h> +#include <TH1F.h> +#include <TH2F.h> +#include <TProfile.h> +#include <Math/Vector4D.h> +#include <TPDGCode.h> +#include <TDatabasePDG.h> +#include <cmath> +#include <array> +#include <cstdlib> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; +using namespace ROOT::Math; + +namespace o2::aod +{ +namespace v0goodpostracks +{ +DECLARE_SOA_INDEX_COLUMN_FULL(GoodTrack, goodTrack, int, FullTracks, "fGoodTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(DCAXY, dcaXY, float); +} // namespace v0goodpostracks +DECLARE_SOA_TABLE(V0GoodPosTracks, "AOD", "V0GOODPOSTRACKS", o2::soa::Index<>, v0goodpostracks::GoodTrackId, v0goodpostracks::CollisionId, v0goodpostracks::DCAXY); +namespace v0goodnegtracks +{ +DECLARE_SOA_INDEX_COLUMN_FULL(GoodTrack, goodTrack, int, FullTracks, "fGoodTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(DCAXY, dcaXY, float); +} // namespace v0goodnegtracks +DECLARE_SOA_TABLE(V0GoodNegTracks, "AOD", "V0GOODNEGTRACKS", o2::soa::Index<>, v0goodnegtracks::GoodTrackId, v0goodnegtracks::CollisionId, v0goodnegtracks::DCAXY); +} // namespace o2::aod + +struct lambdakzeroprefilter { + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<int> mincrossedrows{"mincrossedrows", 70, "min crossed rows"}; + Configurable<int> tpcrefit{"tpcrefit", 1, "demand TPC refit"}; + + Produces<aod::V0GoodPosTracks> v0GoodPosTracks; + Produces<aod::V0GoodNegTracks> v0GoodNegTracks; + + //still exhibiting issues? To be checked + //Partition<soa::Join<aod::FullTracks, aod::TracksExtended>> goodPosTracks = aod::track::signed1Pt > 0.0f && aod::track::dcaXY > dcapostopv; + //Partition<soa::Join<aod::FullTracks, aod::TracksExtended>> goodNegTracks = aod::track::signed1Pt < 0.0f && aod::track::dcaXY < -dcanegtopv; + + void process(aod::Collision const& collision, + soa::Join<aod::FullTracks, aod::TracksExtended> const& tracks) + { + for (auto& t0 : tracks) { + if (tpcrefit) { + if (!(t0.trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + } + if (t0.tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + if (t0.signed1Pt() > 0.0f) { + if (fabs(t0.dcaXY()) < dcapostopv) { + continue; + } + v0GoodPosTracks(t0.globalIndex(), t0.collisionId(), t0.dcaXY()); + } + if (t0.signed1Pt() < 0.0f) { + if (fabs(t0.dcaXY()) < dcanegtopv) { + continue; + } + v0GoodNegTracks(t0.globalIndex(), t0.collisionId(), -t0.dcaXY()); + } + } + } +}; + +struct lambdakzerofinder { + Produces<aod::V0Data> v0data; + + OutputObj<TH1F> hCandPerEvent{TH1F("hCandPerEvent", "", 1000, 0, 1000)}; + + //Configurables + Configurable<double> d_bz{"d_bz", +5.0, "bz field"}; + Configurable<double> d_UseAbsDCA{"d_UseAbsDCA", kTRUE, "Use Abs DCAs"}; + + //Selection criteria + Configurable<double> v0cospa{"v0cospa", 0.995, "V0 CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcav0dau", 1.0, "DCA V0 Daughters"}; + Configurable<float> v0radius{"v0radius", 5.0, "v0radius"}; + + void process(aod::Collision const& collision, aod::FullTracks const& tracks, + aod::V0GoodPosTracks const& ptracks, aod::V0GoodNegTracks const& ntracks) + { + //Define o2 fitter, 2-prong + o2::vertexing::DCAFitterN<2> fitter; + fitter.setBz(d_bz); + fitter.setPropagateToPCA(true); + fitter.setMaxR(200.); + fitter.setMinParamChange(1e-3); + fitter.setMinRelChi2Change(0.9); + fitter.setMaxDZIni(1e9); + fitter.setMaxChi2(1e9); + fitter.setUseAbsDCA(d_UseAbsDCA); + + Long_t lNCand = 0; + + std::array<float, 3> pVtx = {collision.posX(), collision.posY(), collision.posZ()}; + + for (auto& t0id : ptracks) { //FIXME: turn into combination(...) + auto t0 = t0id.goodTrack(); + auto Track1 = getTrackParCov(t0); + for (auto& t1id : ntracks) { + auto t1 = t1id.goodTrack(); + auto Track2 = getTrackParCov(t1); + + //Try to progate to dca + int nCand = fitter.process(Track1, Track2); + if (nCand == 0) { + continue; + } + const auto& vtx = fitter.getPCACandidate(); + + //Fiducial: min radius + auto thisv0radius = TMath::Sqrt(TMath::Power(vtx[0], 2) + TMath::Power(vtx[1], 2)); + if (thisv0radius < v0radius) { + continue; + } + + //DCA V0 daughters + auto thisdcav0dau = fitter.getChi2AtPCACandidate(); + if (thisdcav0dau > dcav0dau) { + continue; + } + + std::array<float, 3> pos = {0.}; + std::array<float, 3> pvec0; + std::array<float, 3> pvec1; + for (int i = 0; i < 3; i++) { + pos[i] = vtx[i]; + } + fitter.getTrack(0).getPxPyPzGlo(pvec0); + fitter.getTrack(1).getPxPyPzGlo(pvec1); + + auto thisv0cospa = RecoDecay::CPA(array{collision.posX(), collision.posY(), collision.posZ()}, + array{vtx[0], vtx[1], vtx[2]}, array{pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}); + if (thisv0cospa < v0cospa) { + continue; + } + + lNCand++; + v0data(t0.globalIndex(), t1.globalIndex(), t0.collisionId(), + fitter.getTrack(0).getX(), fitter.getTrack(1).getX(), + pos[0], pos[1], pos[2], + pvec0[0], pvec0[1], pvec0[2], + pvec1[0], pvec1[1], pvec1[2], + fitter.getChi2AtPCACandidate(), + t0id.dcaXY(), t1id.dcaXY()); + } + } + hCandPerEvent->Fill(lNCand); + } +}; + +struct lambdakzerofinderQA { + //Basic checks + //Selection criteria + Configurable<double> v0cospa{"v0cospa", 0.998, "V0 CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcav0dau", .6, "DCA V0 Daughters"}; + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<float> v0radius{"v0radius", 5.0, "v0radius"}; + + OutputObj<TH1F> hCandPerEvent{TH1F("hCandPerEvent", "", 1000, 0, 1000)}; + + OutputObj<TH1F> hV0Radius{TH1F("hV0Radius", "", 1000, 0.0, 100)}; + OutputObj<TH1F> hV0CosPA{TH1F("hV0CosPA", "", 1000, 0.95, 1.0)}; + OutputObj<TH1F> hDCAPosToPV{TH1F("hDCAPosToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCANegToPV{TH1F("hDCANegToPV", "", 1000, 0.0, 10.0)}; + OutputObj<TH1F> hDCAV0Dau{TH1F("hDCAV0Dau", "", 1000, 0.0, 10.0)}; + + OutputObj<TH3F> h3dMassK0Short{TH3F("h3dMassK0Short", "", 20, 0, 100, 200, 0, 10, 200, 0.450, 0.550)}; + OutputObj<TH3F> h3dMassLambda{TH3F("h3dMassLambda", "", 20, 0, 100, 200, 0, 10, 200, 1.115 - 0.100, 1.115 + 0.100)}; + OutputObj<TH3F> h3dMassAntiLambda{TH3F("h3dMassAntiLambda", "", 20, 0, 100, 200, 0, 10, 200, 1.115 - 0.100, 1.115 + 0.100)}; + + //FIXME: figure out why this does not work? + //Filter preFilter1 = aod::v0data::dcapostopv > dcapostopv; + //Filter preFilter2 = aod::v0data::dcanegtopv > dcanegtopv; + //Filter preFilter3 = aod::v0data::dcaV0daughters < dcav0dau; + + ///Connect to V0Data: newly indexed, note: V0DataExt table incompatible with standard V0 table! + void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator const& collision, + aod::V0DataExt const& fullV0s) + { + if (!collision.alias()[kINT7]) { + return; + } + if (!collision.sel7()) { + return; + } + + Long_t lNCand = 0; + for (auto& v0 : fullV0s) { + if (v0.v0radius() > v0radius && v0.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) > v0cospa && v0.dcapostopv() > dcapostopv && v0.dcanegtopv() > dcanegtopv && v0.dcaV0daughters() > dcav0dau) { + hV0Radius->Fill(v0.v0radius()); + hV0CosPA->Fill(v0.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + hDCAPosToPV->Fill(v0.dcapostopv()); + hDCANegToPV->Fill(v0.dcanegtopv()); + hDCAV0Dau->Fill(v0.dcaV0daughters()); + + if (TMath::Abs(v0.yLambda()) < 0.5) { + h3dMassLambda->Fill(collision.centV0M(), v0.pt(), v0.mLambda()); + h3dMassAntiLambda->Fill(collision.centV0M(), v0.pt(), v0.mAntiLambda()); + } + if (TMath::Abs(v0.yK0Short()) < 0.5) { + h3dMassK0Short->Fill(collision.centV0M(), v0.pt(), v0.mK0Short()); + } + lNCand++; + } + } + hCandPerEvent->Fill(lNCand); + } +}; + +/// Extends the v0data table with expression columns +struct lambdakzeroinitializer { + Spawns<aod::V0DataExt> v0dataext; + void init(InitContext const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<lambdakzeroprefilter>("lf-lambdakzeroprefilter"), + adaptAnalysisTask<lambdakzerofinder>("lf-lambdakzerofinder"), + adaptAnalysisTask<lambdakzeroinitializer>("lf-lambdakzeroinitializer"), + adaptAnalysisTask<lambdakzerofinderQA>("lf-lambdakzerofinderQA")}; +} diff --git a/Analysis/Tasks/PWGLF/lambdakzeroproducer.cxx b/Analysis/Tasks/PWGLF/lambdakzeroproducer.cxx new file mode 100644 index 0000000000000..5ccbc145a4d0d --- /dev/null +++ b/Analysis/Tasks/PWGLF/lambdakzeroproducer.cxx @@ -0,0 +1,218 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// V0 Producer task +// ================ +// +// This task loops over an *existing* list of V0s (neg/pos track +// indices) and calculates the corresponding full V0 information +// +// Any analysis should loop over the "V0Data" +// table as that table contains all information +// +// WARNING: adding filters to the producer IS NOT +// equivalent to re-running the finders. This will only +// ever produce *tighter* selection sections. It is your +// responsibility to check if, by setting a loose filter +// setting, you are going into a region in which no +// candidates exist because the original indices were generated +// using tigher selections. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// david.dobrigkeit.chinellato@cern.ch +// + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "ReconstructionDataFormats/Track.h" +#include "AnalysisCore/RecoDecay.h" +#include "AnalysisCore/trackUtilities.h" +#include "AnalysisDataModel/StrangenessTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +#include <TFile.h> +#include <TH2F.h> +#include <TProfile.h> +#include <TLorentzVector.h> +#include <Math/Vector4D.h> +#include <TPDGCode.h> +#include <TDatabasePDG.h> +#include <cmath> +#include <array> +#include <cstdlib> +#include "Framework/ASoAHelpers.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; + +//This table stores a filtered list of valid V0 indices +namespace o2::aod +{ +namespace v0goodindices +{ +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, FullTracks, "fPositiveTrackID"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, FullTracks, "fNegativeTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +} // namespace v0goodindices +DECLARE_SOA_TABLE(V0GoodIndices, "AOD", "V0GOODINDICES", o2::soa::Index<>, + v0goodindices::PosTrackId, v0goodindices::NegTrackId, v0goodindices::CollisionId); +} // namespace o2::aod + +using FullTracksExt = soa::Join<aod::FullTracks, aod::TracksExtended>; + +//This prefilter creates a skimmed list of good V0s to re-reconstruct so that +//CPU is saved in case there are specific selections that are to be done +//Note: more configurables, more options to be added as needed +struct lambdakzeroprefilterpairs { + Configurable<float> dcanegtopv{"dcanegtopv", .1, "DCA Neg To PV"}; + Configurable<float> dcapostopv{"dcapostopv", .1, "DCA Pos To PV"}; + Configurable<int> mincrossedrows{"mincrossedrows", 70, "min crossed rows"}; + Configurable<bool> tpcrefit{"tpcrefit", 1, "demand TPC refit"}; + + OutputObj<TH1F> hGoodIndices{TH1F("hGoodIndices", "", 4, 0, 4)}; + + Produces<aod::V0GoodIndices> v0goodindices; + + void process(aod::Collision const& collision, aod::V0s const& V0s, + soa::Join<aod::FullTracks, aod::TracksExtended> const& tracks) + { + for (auto& V0 : V0s) { + hGoodIndices->Fill(0.5); + if (tpcrefit) { + if (!(V0.posTrack_as<FullTracksExt>().trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + if (!(V0.negTrack_as<FullTracksExt>().trackType() & o2::aod::track::TPCrefit)) { + continue; //TPC refit + } + } + hGoodIndices->Fill(1.5); + if (V0.posTrack_as<FullTracksExt>().tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + if (V0.negTrack_as<FullTracksExt>().tpcNClsCrossedRows() < mincrossedrows) { + continue; + } + hGoodIndices->Fill(2.5); + if (fabs(V0.posTrack_as<FullTracksExt>().dcaXY()) < dcapostopv) { + continue; + } + if (fabs(V0.negTrack_as<FullTracksExt>().dcaXY()) < dcanegtopv) { + continue; + } + hGoodIndices->Fill(3.5); + v0goodindices(V0.posTrack_as<FullTracksExt>().globalIndex(), + V0.negTrack_as<FullTracksExt>().globalIndex(), + V0.posTrack_as<FullTracksExt>().collisionId()); + } + } +}; + +/// Cascade builder task: rebuilds cascades +struct lambdakzeroproducer { + + Produces<aod::V0Data> v0data; + + OutputObj<TH1F> hEventCounter{TH1F("hEventCounter", "", 1, 0, 1)}; + OutputObj<TH1F> hV0Candidate{TH1F("hV0Candidate", "", 2, 0, 2)}; + + //Configurables + Configurable<double> d_bz{"d_bz", -5.0, "bz field"}; + Configurable<double> d_UseAbsDCA{"d_UseAbsDCA", kTRUE, "Use Abs DCAs"}; + + //Selection criteria + Configurable<double> v0cospa{"v0cospa", 0.995, "V0 CosPA"}; //double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable<float> dcav0dau{"dcav0dau", 1.0, "DCA V0 Daughters"}; + Configurable<float> v0radius{"v0radius", 5.0, "v0radius"}; + + double massPi = TDatabasePDG::Instance()->GetParticle(kPiPlus)->Mass(); + double massKa = TDatabasePDG::Instance()->GetParticle(kKPlus)->Mass(); + double massPr = TDatabasePDG::Instance()->GetParticle(kProton)->Mass(); + + using FullTracksExt = soa::Join<aod::FullTracks, aod::TracksExtended>; + + void process(aod::Collision const& collision, aod::V0GoodIndices const& V0s, soa::Join<aod::FullTracks, aod::TracksExtended> const& tracks) + { + //Define o2 fitter, 2-prong + o2::vertexing::DCAFitterN<2> fitter; + fitter.setBz(d_bz); + fitter.setPropagateToPCA(true); + fitter.setMaxR(200.); + fitter.setMinParamChange(1e-3); + fitter.setMinRelChi2Change(0.9); + fitter.setMaxDZIni(1e9); + fitter.setMaxChi2(1e9); + fitter.setUseAbsDCA(d_UseAbsDCA); + + hEventCounter->Fill(0.5); + std::array<float, 3> pVtx = {collision.posX(), collision.posY(), collision.posZ()}; + + for (auto& V0 : V0s) { + std::array<float, 3> pos = {0.}; + std::array<float, 3> pvec0 = {0.}; + std::array<float, 3> pvec1 = {0.}; + + hV0Candidate->Fill(0.5); + + auto pTrack = getTrackParCov(V0.posTrack_as<FullTracksExt>()); + auto nTrack = getTrackParCov(V0.negTrack_as<FullTracksExt>()); + int nCand = fitter.process(pTrack, nTrack); + if (nCand != 0) { + fitter.propagateTracksToVertex(); + const auto& vtx = fitter.getPCACandidate(); + for (int i = 0; i < 3; i++) { + pos[i] = vtx[i]; + } + fitter.getTrack(0).getPxPyPzGlo(pvec0); + fitter.getTrack(1).getPxPyPzGlo(pvec1); + } else { + continue; + } + + //Apply selections so a skimmed table is created only + if (fitter.getChi2AtPCACandidate() > dcav0dau) { + continue; + } + + auto V0CosinePA = RecoDecay::CPA(array{collision.posX(), collision.posY(), collision.posZ()}, array{pos[0], pos[1], pos[2]}, array{pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}); + + if (V0CosinePA < v0cospa) { + continue; + } + + hV0Candidate->Fill(1.5); + v0data( + V0.posTrack_as<FullTracksExt>().globalIndex(), + V0.negTrack_as<FullTracksExt>().globalIndex(), + V0.negTrack_as<FullTracksExt>().collisionId(), + fitter.getTrack(0).getX(), fitter.getTrack(1).getX(), + pos[0], pos[1], pos[2], + pvec0[0], pvec0[1], pvec0[2], + pvec1[0], pvec1[1], pvec1[2], + fitter.getChi2AtPCACandidate(), + V0.posTrack_as<FullTracksExt>().dcaXY(), + V0.negTrack_as<FullTracksExt>().dcaXY()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<lambdakzeroprefilterpairs>("lf-lambdakzeroprefilterpairs"), + adaptAnalysisTask<lambdakzeroproducer>("lf-lambdakzeroproducer")}; +} diff --git a/Analysis/Tasks/PWGLF/mcspectraefficiency.cxx b/Analysis/Tasks/PWGLF/mcspectraefficiency.cxx new file mode 100644 index 0000000000000..6033718cf159f --- /dev/null +++ b/Analysis/Tasks/PWGLF/mcspectraefficiency.cxx @@ -0,0 +1,163 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "AnalysisCore/MC.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +// ROOT includes +#include <TH1F.h> +#include "TPDGCode.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace MC; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"add-vertex", VariantType::Int, 1, {"Vertex plots"}}, + {"add-gen", VariantType::Int, 1, {"Generated plots"}}, + {"add-reco", VariantType::Int, 1, {"Reconstructed plots"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +#define PDGBINNING 100, 0, 100 + +// Simple access to collision +struct VertexTask { + OutputObj<TH1F> vertex{TH1F("vertex", "vertex", 100, -10, 10)}; + + void process(aod::McCollision const& mcCollision) + { + LOGF(info, "MC. vtx-z = %f", mcCollision.posZ()); + vertex->Fill(mcCollision.posZ()); + } +}; + +// Grouping between MC particles and collisions +struct GeneratedTask { + OutputObj<TH2F> phiH{TH2F("phi", "phi;#phi;PDG code", 100, 0., 2. * M_PI, PDGBINNING)}; + OutputObj<TH2F> etaH{TH2F("eta", "eta;#eta;PDG code", 102, -2.01, 2.01, PDGBINNING)}; + OutputObj<TH2F> ptH{TH2F("pt", "pt;#it{p}_{T} (GeV/#it{c});PDG code", 500, 0, 20, PDGBINNING)}; + OutputObj<TH2F> pH{TH2F("p", "p;#it{p} (GeV/#it{c});PDG code", 500, 0, 20, PDGBINNING)}; + OutputObj<TH1F> pdgH{TH1F("pdg", "pdg;PDG code", PDGBINNING)}; + + int events = 0; + int particles = 0; + int primaryparticles = 0; + + void process(aod::McCollision const& mcCollision, aod::McParticles& mcParticles) + { + LOGF(info, "MC. vtx-z = %f", mcCollision.posZ()); + for (auto& mcParticle : mcParticles) { + if (abs(mcParticle.eta()) > 0.8) { + continue; + } + if (isPhysicalPrimary(mcParticles, mcParticle)) { + const auto pdg = Form("%i", mcParticle.pdgCode()); + pdgH->Fill(pdg, 1); + const float pdgbin = pdgH->GetXaxis()->GetBinCenter(pdgH->GetXaxis()->FindBin(pdg)); + phiH->Fill(mcParticle.phi(), pdgbin); + etaH->Fill(mcParticle.eta(), pdgbin); + pH->Fill(sqrt(mcParticle.px() * mcParticle.px() + mcParticle.py() * mcParticle.py() + mcParticle.pz() * mcParticle.pz()), pdgbin); + ptH->Fill(mcParticle.pt(), pdgbin); + primaryparticles++; + } + particles++; + } + LOGF(info, "Events %i", events++ + 1); + LOGF(info, "Particles %i", particles); + LOGF(info, "Primaries %i", primaryparticles); + } +}; + +// Access from tracks to MC particle +struct ReconstructedTask { + OutputObj<TH2F> phiH{TH2F("phi", "phi;#phi;PDG code", 100, 0., 2. * M_PI, PDGBINNING)}; + OutputObj<TH2F> etaH{TH2F("eta", "eta;#eta;PDG code", 102, -2.01, 2.01, PDGBINNING)}; + OutputObj<TH2F> ptH{TH2F("pt", "pt;#it{p}_{T} (GeV/#it{c})", 500, 0, 20, PDGBINNING)}; + OutputObj<TH2F> pH{TH2F("p", "p;#it{p} (GeV/#it{c})", 500, 0, 20, PDGBINNING)}; + OutputObj<TH2F> dcaxyH{TH2F("dcaxy", "dcaxy;DCA_{xy} (cm)", 500, -10, 10, PDGBINNING)}; + OutputObj<TH2F> dcazH{TH2F("dcaz", "dcaz;DCA_{z} (cm)", 500, -10, 10, PDGBINNING)}; + OutputObj<TH1F> pdgH{TH1F("pdg", "pdg;PDG code", PDGBINNING)}; + OutputObj<TH2F> dcaxysecH{TH2F("dcaxysec", "dcaxysec;DCA_{xy} (cm)", 500, -10, 10, PDGBINNING)}; + OutputObj<TH2F> dcazsecH{TH2F("dcazsec", "dcazsec;DCA_{z} (cm)", 500, -10, 10, PDGBINNING)}; + OutputObj<TH1F> pdgsecH{TH1F("pdgsec", "pdgsec;PDG code", PDGBINNING)}; + + OutputObj<TH2F> phiDiff{TH2F("phiDiff", ";phi_{MC} - phi_{Rec}", 100, -M_PI, M_PI, PDGBINNING)}; + OutputObj<TH2F> etaDiff{TH2F("etaDiff", ";eta_{MC} - eta_{Rec}", 100, -2, 2, PDGBINNING)}; + OutputObj<TH2F> ptDiff{TH2F("ptDiff", "ptDiff;#it{p}_{T}_{MC} #it{p}_{T}_{Rec} (GeV/#it{c})", 500, -2, 2, PDGBINNING)}; + OutputObj<TH2F> pDiff{TH2F("pDiff", "pDiff;#it{p}_{MC} #it{p}_{Rec} (GeV/#it{c})", 500, -2, 2, PDGBINNING)}; + + Filter trackAcceptance = (nabs(aod::track::eta) < 0.8f); + Filter trackCuts = ((aod::track::isGlobalTrack == (uint8_t) true) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); + + void process(soa::Join<aod::Collisions, aod::McCollisionLabels>::iterator const& collision, + soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksExtended, aod::McTrackLabels, aod::TrackSelection>> const& tracks, + aod::McParticles& mcParticles, aod::McCollisions const& mcCollisions) + { + LOGF(info, "vtx-z (data) = %f | vtx-z (MC) = %f", collision.posZ(), collision.label().posZ()); + for (auto& track : tracks) { + const auto pdg = Form("%i", track.label().pdgCode()); + if (!isPhysicalPrimary(mcParticles, track.label())) { + pdgsecH->Fill(pdg, 1); + const float pdgbinsec = pdgH->GetXaxis()->GetBinCenter(pdgsecH->GetXaxis()->FindBin(pdg)); + dcaxysecH->Fill(track.dcaXY(), pdgbinsec); + dcazsecH->Fill(track.dcaZ(), pdgbinsec); + continue; + } + pdgH->Fill(pdg, 1); // Filling the first bin and check its bin + const float pdgbin = pdgH->GetXaxis()->GetBinCenter(pdgH->GetXaxis()->FindBin(pdg)); + phiH->Fill(track.phi(), pdgbin); + etaH->Fill(track.eta(), pdgbin); + pH->Fill(track.p(), pdgbin); + ptH->Fill(track.pt(), pdgbin); + dcaxyH->Fill(track.dcaXY(), pdgbin); + dcazH->Fill(track.dcaZ(), pdgbin); + + etaDiff->Fill(track.label().eta() - track.eta(), pdgbin); + auto delta = track.label().phi() - track.phi(); + if (delta > M_PI) { + delta -= 2 * M_PI; + } + if (delta < -M_PI) { + delta += 2 * M_PI; + } + phiDiff->Fill(delta, pdgbin); + pDiff->Fill(sqrt(track.label().px() * track.label().px() + track.label().py() * track.label().py() + track.label().pz() * track.label().pz()) - track.p(), pdgbin); + ptDiff->Fill(track.label().pt() - track.pt(), pdgbin); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + const bool vertex = cfgc.options().get<int>("add-vertex"); + const bool gen = cfgc.options().get<int>("add-gen"); + const bool reco = cfgc.options().get<int>("add-reco"); + WorkflowSpec workflow{}; + if (vertex) { + workflow.push_back(adaptAnalysisTask<VertexTask>("vertex-histogram")); + } + if (gen) { + workflow.push_back(adaptAnalysisTask<GeneratedTask>("generator-histogram")); + } + if (reco) { + workflow.push_back(adaptAnalysisTask<ReconstructedTask>("reconstructed-histogram")); + } + return workflow; +} diff --git a/Analysis/Tasks/PWGLF/raacharged.cxx b/Analysis/Tasks/PWGLF/raacharged.cxx new file mode 100644 index 0000000000000..f24a5b64e1bb5 --- /dev/null +++ b/Analysis/Tasks/PWGLF/raacharged.cxx @@ -0,0 +1,225 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisCore/MC.h" +#include <TH1F.h> +#include <TParticlePDG.h> +#include <TDatabasePDG.h> +#include <cmath> + +using namespace o2; +using namespace o2::framework; + +struct raacharged { + // data members + OutputObj<THnF> fHistTrack{"fHistTrack"}; + OutputObj<THnF> fHistEvent{"fHistEvent"}; + OutputObj<THnF> fHistMC{"fHistMC"}; + Configurable<int> selectedTracks{"select", 1, "Choice of track selection. 0 = no selection, 1 = globalTracks, 2 = globalTracksSDD"}; + // member functions + void init(InitContext const&) + { + constexpr Int_t nbinsMultCent = 11; + constexpr Int_t nnAcc = 54; + constexpr Int_t nbinspT = 82; + constexpr Int_t nChargeBins = 7; + + Double_t MultCent[] = {0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100}; + Double_t nAccbins[55]; //mult6kcoarse option in AlidNdPtTools + nAccbins[0] = -0.5; + int i = 0; + for (; i <= 10; i++) { + nAccbins[i + 1] = nAccbins[i] + 1; + } + for (; i <= 10 + 9; i++) { + nAccbins[i + 1] = nAccbins[i] + 10; + } + for (; i <= 10 + 9 + 9; i++) { + nAccbins[i + 1] = nAccbins[i] + 100; + } + for (; i <= 10 + 9 + 9 + 25; i++) { + nAccbins[i + 1] = nAccbins[i] + 200; + } + Double_t pTBins[] = {0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0, 26.0, 28.0, 30.0, 32.0, 34.0, 36.0, 40.0, 45.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 180.0, 200.0}; + Double_t ChargeBins[] = {-2.5, -1.5, -0.5, 0., 0.5, 1.5, 2.5}; + + Int_t nBinsTrack[4] = {nbinsMultCent - 1, nnAcc - 1, nbinspT - 1, nChargeBins - 1}; + Double_t minTrack[4] = {MultCent[0], nAccbins[0], pTBins[0], ChargeBins[0]}; + Double_t maxTrack[4] = {MultCent[nbinsMultCent - 1], nAccbins[nnAcc - 1], pTBins[nbinspT - 1], ChargeBins[nChargeBins - 1]}; + + fHistTrack.setObject(new THnF("fHistTrack", "Hist. for tracks", 4, nBinsTrack, minTrack, maxTrack)); + fHistTrack->SetBinEdges(0, MultCent); + fHistTrack->SetBinEdges(1, nAccbins); + fHistTrack->SetBinEdges(2, pTBins); + fHistTrack->SetBinEdges(3, ChargeBins); + fHistTrack->GetAxis(0)->SetTitle("cent"); + fHistTrack->GetAxis(1)->SetTitle("nAcc"); + fHistTrack->GetAxis(2)->SetTitle("p_{T} (GeV/c)"); + fHistTrack->GetAxis(3)->SetTitle("Q"); + + constexpr Int_t nbinszV = 13; + Double_t ZvBins[] = {-30., -25., -20., -15., -10., -5., 0., 5., 10., 15., 20., 25., 30.}; + + Int_t nBinsEvent[3] = {nbinsMultCent - 1, nnAcc - 1, nbinszV - 1}; + Double_t minEvent[3] = {MultCent[0], nAccbins[0], ZvBins[0]}; + Double_t maxEvent[3] = {MultCent[nbinsMultCent - 1], nAccbins[nnAcc - 1], ZvBins[nbinszV - 1]}; + + fHistEvent.setObject(new THnF("fHistEvent", "Histogram for Events", 3, nBinsEvent, minEvent, maxEvent)); + fHistEvent->SetBinEdges(0, MultCent); + fHistEvent->SetBinEdges(1, nAccbins); + fHistEvent->SetBinEdges(2, ZvBins); + fHistEvent->GetAxis(0)->SetTitle("cent"); + fHistEvent->GetAxis(1)->SetTitle("nAcc"); + fHistEvent->GetAxis(2)->SetTitle("Zv (cm)"); + + constexpr Int_t nParTypeBins = 11; + constexpr Int_t nMCinfo = 4; + + Double_t ParTypeBins[11] = {-0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5}; // 0=e, 1=mu, 2=pi, 3=K, 4=p, 6=sigmaP, 7=sigmaM, 8=xi, 9=omega, 5=other + Double_t MCinfoBins[4] = {-0.5, 0.5, 1.5, 2.5}; // 0=prim, 1=sec.(decays) 2=genprim , sec. from material not considered + + Int_t nBinsMC[5] = {nbinspT - 1, nParTypeBins - 1, nMCinfo - 1, nChargeBins - 1}; + Double_t minMC[5] = {pTBins[0], ParTypeBins[0], MCinfoBins[0], ChargeBins[0]}; + Double_t maxMC[5] = {pTBins[nbinspT - 1], ParTypeBins[nParTypeBins - 1], MCinfoBins[nMCinfo - 1], ChargeBins[nChargeBins - 1]}; + + fHistMC.setObject(new THnF("fHistMC", "Hist. for MC Info", 4, nBinsMC, minMC, maxMC)); + fHistMC->SetBinEdges(0, pTBins); + fHistMC->SetBinEdges(1, ParTypeBins); + fHistMC->SetBinEdges(2, MCinfoBins); + fHistMC->SetBinEdges(3, ChargeBins); + fHistMC->GetAxis(0)->SetTitle("MCp_{T} (GeV/c)"); + fHistMC->GetAxis(1)->SetTitle("Particle Type"); + fHistMC->GetAxis(2)->SetTitle("MCinfo"); + fHistMC->GetAxis(3)->SetTitle("Q"); + } + + Int_t WhichParticle(Int_t pdgCode) + { //see in AlidNdPtTools + if (pdgCode == 0) { + return -1; + } + if (pdgCode == std::abs(211)) { + return 0; //pi+, pi- + } + if (pdgCode == std::abs(321)) { + return 1; //K+, K- + } + if (pdgCode == std::abs(2212)) { + return 2; //p, pbar + } + if (pdgCode == 3222 || pdgCode == -3112) { + return 3; //sigmaPlus, SigmaBarMinus + } + if (pdgCode == 3112 || pdgCode == -3222) { + return 4; //sigmaMinus, SigmaBarPlus + } + if (pdgCode == std::abs(11)) { + return 5; //e-, e+ + } + if (pdgCode == std::abs(3312)) { + return 6; //XiP, XiM + } + if (pdgCode == std::abs(13)) { + return 7; //mu,antimu + } + if (pdgCode == std::abs(3334)) { + return 8; //OmegaP, OmegaM + } + + return 9; //other + } + Double_t MCCharge(Int_t pdgCode) + { + TParticlePDG* par = TDatabasePDG::Instance()->GetParticle(pdgCode); + Double_t charge = par->Charge() / 3.0; + return charge; + } + + Configurable<bool> isMC{"isMC", 1, "0 - data, 1 - MC"}; + + void process(soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels, aod::Cents>::iterator const& collision, soa::Join<aod::Tracks, aod::McTrackLabels, aod::TrackSelection> const& tracks, aod::McParticles& mcParticles) + { + if (!collision.alias()[kINT7]) { + return; + } + if (!collision.sel7()) { + return; + } + + Double_t eventValues[3] = {0.0, 0.0, collision.posZ()}; + fHistEvent->Fill(eventValues); + + for (auto& track : tracks) { + if (selectedTracks == 1 && !track.isGlobalTrack()) { + continue; + } else if (selectedTracks == 2 && !track.isGlobalTrackSDD()) { + continue; + } + + Double_t trackValues[4] = {0.0, 0.0, track.pt(), (Double_t)track.charge()}; + fHistTrack->Fill(trackValues); + + Double_t mcInfoVal; + if (!isMC) { + continue; + } + if (MC::isPhysicalPrimary(mcParticles, track.label())) { + mcInfoVal = 0.0; + } else { + mcInfoVal = 1.0; + } + + Double_t MCpt = track.label().pt(); + Double_t parType = (Double_t)WhichParticle(track.label().pdgCode()); + Double_t MCcharge = (Double_t)track.charge(); + Double_t MCvalues[4] = {MCpt, parType, mcInfoVal, MCcharge}; + + fHistMC->Fill(MCvalues); + } + if (isMC) { + for (auto& mcParticle : mcParticles) { + + if (abs(mcParticle.eta()) > 0.8) { + continue; + } + if (!MC::isPhysicalPrimary(mcParticles, mcParticle)) { + continue; + } + + Double_t MCpt = mcParticle.pt(); + Double_t parType = (Double_t)WhichParticle(mcParticle.pdgCode()); + Int_t pdg = (Int_t)mcParticle.pdgCode(); + Double_t MCcharge = MCCharge(pdg); + + if (MCcharge == 0.0) { + continue; + } + Double_t MCvalues[4] = {MCpt, parType, 2.0, MCcharge}; + fHistMC->Fill(MCvalues); + } + } + } +}; + +//-------------------------------------------------------------------- +// Workflow definition +//-------------------------------------------------------------------- +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<raacharged>("raa-charged")}; +} diff --git a/Analysis/Tasks/PWGLF/spectraTOF.cxx b/Analysis/Tasks/PWGLF/spectraTOF.cxx new file mode 100644 index 0000000000000..0f7052c4adb7a --- /dev/null +++ b/Analysis/Tasks/PWGLF/spectraTOF.cxx @@ -0,0 +1,98 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "ReconstructionDataFormats/Track.h" +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct TOFSpectraTask { + static constexpr int Np = 9; + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + static constexpr std::string_view hp[Np] = {"p/El", "p/Mu", "p/Pi", "p/Ka", "p/Pr", "p/De", "p/Tr", "p/He", "p/Al"}; + static constexpr std::string_view hpt[Np] = {"pt/El", "pt/Mu", "pt/Pi", "pt/Ka", "pt/Pr", "pt/De", "pt/Tr", "pt/He", "pt/Al"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + histos.add("p/Unselected", "Unselected;#it{p} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + histos.add("pt/Unselected", "Unselected;#it{p}_{T} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + for (int i = 0; i < Np; i++) { + histos.add(hp[i].data(), Form("%s;#it{p} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + histos.add(hpt[i].data(), Form("%s;#it{p}_{T} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + } + histos.add("electronbeta/hp_El", ";#it{p} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + histos.add("electronbeta/hpt_El", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + histos.add("electronbeta/hlength_El", ";Track Length (cm);Tracks", kTH1D, {{100, 0, 1000}}); + histos.add("electronbeta/htime_El", ";TOF Time (ns);Tracks", kTH1D, {{1000, 0, 600}}); + histos.add("electronbeta/hp_beta_El", ";#it{p} (GeV/#it{c});#beta - #beta_{e};Tracks", kTH2D, {{100, 0, 20}, {100, -0.01, 0.01}}); + histos.add("electronbeta/hp_betasigma_El", ";#it{p} (GeV/#it{c});(#beta - #beta_{e})/#sigma;Tracks", kTH2D, {{100, 0, 20}, {100, -5, 5}}); + } + + template <std::size_t i, typename T> + void fillParticleHistos(const T& track, const float nsigma[]) + { + if (abs(nsigma[i]) > nsigmacut.value) { + return; + } + histos.fill(HIST(hp[i]), track.p()); + histos.fill(HIST(hpt[i]), track.pt()); + } + + Configurable<float> cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable<float> cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable<float> nsigmacut{"nsigmacut", 3, "Value of the Nsigma cut"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == (uint8_t) true) && (aod::track::tofSignal > 0.f); + using TrackCandidates = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTOF, aod::pidRespTOFbeta, aod::TrackSelection>>; + void process(TrackCandidates::iterator const& track) + { + const float nsigma[Np] = {track.tofNSigmaEl(), track.tofNSigmaMu(), track.tofNSigmaPi(), + track.tofNSigmaKa(), track.tofNSigmaPr(), track.tofNSigmaDe(), + track.tofNSigmaTr(), track.tofNSigmaHe(), track.tofNSigmaAl()}; + histos.fill(HIST("p/Unselected"), track.p()); + histos.fill(HIST("pt/Unselected"), track.pt()); + + fillParticleHistos<0>(track, nsigma); + fillParticleHistos<1>(track, nsigma); + fillParticleHistos<2>(track, nsigma); + fillParticleHistos<3>(track, nsigma); + fillParticleHistos<4>(track, nsigma); + fillParticleHistos<5>(track, nsigma); + fillParticleHistos<6>(track, nsigma); + fillParticleHistos<7>(track, nsigma); + fillParticleHistos<8>(track, nsigma); + + // + if (TMath::Abs(track.separationbetael() < 1.f)) { + histos.fill(HIST("electronbeta/hp_El"), track.p()); + histos.fill(HIST("electronbeta/hpt_El"), track.pt()); + histos.fill(HIST("electronbeta/hlength_El"), track.length()); + histos.fill(HIST("electronbeta/htime_El"), track.tofSignal() / 1000); + histos.fill(HIST("electronbeta/hp_beta_El"), track.p(), track.diffbetael()); + histos.fill(HIST("electronbeta/hp_betasigma_El"), track.p(), track.separationbetael()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + WorkflowSpec workflow{adaptAnalysisTask<TOFSpectraTask>("tofspectra-task")}; + return workflow; +} diff --git a/Analysis/Tasks/PWGLF/spectraTPC.cxx b/Analysis/Tasks/PWGLF/spectraTPC.cxx new file mode 100644 index 0000000000000..84918ed15f349 --- /dev/null +++ b/Analysis/Tasks/PWGLF/spectraTPC.cxx @@ -0,0 +1,167 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "ReconstructionDataFormats/Track.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"add-tof-histos", VariantType::Int, 0, {"Generate TPC with TOF histograms"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +#define CANDIDATE_SELECTION \ + Configurable<float> cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; \ + Configurable<float> cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; \ + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; \ + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == (uint8_t) true); + +// FIXME: we should put this function in some common header so it has to be defined only once +template <typename T> +void makelogaxis(T h) +{ + const int nbins = h->GetNbinsX(); + double binp[nbins + 1]; + double max = h->GetXaxis()->GetBinUpEdge(nbins); + double min = h->GetXaxis()->GetBinLowEdge(1); + if (min <= 0) { + min = 0.00001; + } + double lmin = TMath::Log10(min); + double ldelta = (TMath::Log10(max) - lmin) / ((double)nbins); + for (int i = 0; i < nbins; i++) { + binp[i] = TMath::Exp(TMath::Log(10) * (lmin + i * ldelta)); + } + binp[nbins] = max + 1; + h->GetXaxis()->Set(nbins, binp); +} + +constexpr int Np = 9; +struct TPCSpectraTask { + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + static constexpr std::string_view hp[Np] = {"p/El", "p/Mu", "p/Pi", "p/Ka", "p/Pr", "p/De", "p/Tr", "p/He", "p/Al"}; + static constexpr std::string_view hpt[Np] = {"pt/El", "pt/Mu", "pt/Pi", "pt/Ka", "pt/Pr", "pt/De", "pt/Tr", "pt/He", "pt/Al"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + histos.add("p/Unselected", "Unselected;#it{p} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + histos.add("pt/Unselected", "Unselected;#it{p}_{T} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + for (int i = 0; i < Np; i++) { + histos.add(hp[i].data(), Form("%s;#it{p} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + histos.add(hpt[i].data(), Form("%s;#it{p}_{T} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + } + } + + //Defining filters and input + CANDIDATE_SELECTION + + Configurable<float> nsigmacut{"nsigmacut", 3, "Value of the Nsigma cut"}; + + template <std::size_t i, typename T> + void fillParticleHistos(const T& track, const float nsigma[]) + { + if (abs(nsigma[i]) > nsigmacut.value) { + return; + } + histos.fill(HIST(hp[i]), track.p()); + histos.fill(HIST(hpt[i]), track.pt()); + } + + using TrackCandidates = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTPC, aod::TrackSelection>>; + void process(TrackCandidates::iterator const& track) + { + const float nsigma[Np] = {track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), + track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe(), + track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl()}; + histos.fill(HIST("p/Unselected"), track.p()); + histos.fill(HIST("pt/Unselected"), track.pt()); + + fillParticleHistos<0>(track, nsigma); + fillParticleHistos<1>(track, nsigma); + fillParticleHistos<2>(track, nsigma); + fillParticleHistos<3>(track, nsigma); + fillParticleHistos<4>(track, nsigma); + fillParticleHistos<5>(track, nsigma); + fillParticleHistos<6>(track, nsigma); + fillParticleHistos<7>(track, nsigma); + fillParticleHistos<8>(track, nsigma); + } +}; + +struct TPCPIDQASignalwTOFTask { + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + static constexpr std::string_view htpcsignal[Np] = {"tpcsignal/El", "tpcsignal/Mu", "tpcsignal/Pi", + "tpcsignal/Ka", "tpcsignal/Pr", "tpcsignal/De", + "tpcsignal/Tr", "tpcsignal/He", "tpcsignal/Al"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + template <uint8_t i> + void addParticleHistos() + { + histos.add(htpcsignal[i].data(), Form(";#it{p} (GeV/#it{c});TPC Signal;N_{#sigma}^{TPC}(%s)", pT[i]), kTH3D, {{1000, 0.001, 20}, {1000, 0, 1000}, {20, -10, 10}}); + makelogaxis(histos.get<TH3>(HIST(htpcsignal[i]))); + } + + void init(o2::framework::InitContext&) + { + addParticleHistos<0>(); + addParticleHistos<1>(); + addParticleHistos<2>(); + addParticleHistos<3>(); + addParticleHistos<4>(); + addParticleHistos<5>(); + addParticleHistos<6>(); + addParticleHistos<7>(); + addParticleHistos<8>(); + } + + // Filters + CANDIDATE_SELECTION + + Filter trackFilterTOF = (aod::track::tofSignal > 0.f); // Skip tracks without TOF + using TrackCandidates = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTPC, aod::pidRespTOF, aod::TrackSelection>>; + void process(TrackCandidates::iterator const& track) + { + // const float mom = track.p(); + const float mom = track.tpcInnerParam(); + histos.fill(HIST(htpcsignal[0]), mom, track.tpcSignal(), track.tofNSigmaEl()); + histos.fill(HIST(htpcsignal[1]), mom, track.tpcSignal(), track.tofNSigmaMu()); + histos.fill(HIST(htpcsignal[2]), mom, track.tpcSignal(), track.tofNSigmaPi()); + histos.fill(HIST(htpcsignal[3]), mom, track.tpcSignal(), track.tofNSigmaKa()); + histos.fill(HIST(htpcsignal[4]), mom, track.tpcSignal(), track.tofNSigmaPr()); + histos.fill(HIST(htpcsignal[5]), mom, track.tpcSignal(), track.tofNSigmaDe()); + histos.fill(HIST(htpcsignal[6]), mom, track.tpcSignal(), track.tofNSigmaTr()); + histos.fill(HIST(htpcsignal[7]), mom, track.tpcSignal(), track.tofNSigmaHe()); + histos.fill(HIST(htpcsignal[8]), mom, track.tpcSignal(), track.tofNSigmaAl()); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + int TPCwTOF = cfgc.options().get<int>("add-tof-histos"); + WorkflowSpec workflow{adaptAnalysisTask<TPCSpectraTask>("tpcspectra-task")}; + if (TPCwTOF) { + workflow.push_back(adaptAnalysisTask<TPCPIDQASignalwTOFTask>("TPCpidqa-signalwTOF-task")); + } + return workflow; +} diff --git a/Analysis/Tasks/PWGLF/trackchecks.cxx b/Analysis/Tasks/PWGLF/trackchecks.cxx new file mode 100644 index 0000000000000..cd2cbeb0ec857 --- /dev/null +++ b/Analysis/Tasks/PWGLF/trackchecks.cxx @@ -0,0 +1,337 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisCore/MC.h" +#include "AnalysisCore/HistHelpers.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +#include <cmath> +#include <array> +#include <utility> + +#include <TH1.h> +#include <TH2.h> +#include <TF1.h> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::experimental::histhelpers; +using namespace std; + +#define NPTBINS 53 +#define NPARTICLES 5 +const double pt_bins[NPTBINS + 1] = {0.0, 0.05, 0.08, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, + 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, + 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, + 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 10.0, 20.0}; +const double mass[NPARTICLES] = {0.000510999, 0.139570, 0.493677, 0.938272, 1.87561}; +enum PType : uint8_t { + kEl, + kPi, + kKa, + kPr, + kDe, + kNull +}; + +//function to convert eta to y +double eta2y(double pt, double m, double eta) +{ + double mt = sqrt(m * m + pt * pt); + return asinh(pt / mt * sinh(eta)); +} + +int getparticleint(int pdgcode) +{ + if (pdgcode == 11) { + return kEl; + } else if (pdgcode == 211) { + return kPi; + } else if (pdgcode == 321) { + return kKa; + } else if (pdgcode == 2212) { + return kPr; + } else if (pdgcode == 1000010020) { + return kDe; + } else { + return kNull; + } +} + +//No track selection --> only event selection here +struct TrackCheckTaskEvSel { + + Configurable<bool> isMC{"isMC", false, "option to flag mc"}; + Configurable<double> cfgCutY{"cfgCutY", 0.5, "option to configure rapidity cut"}; + Configurable<float> cfgCutVZ{"cfgCutVZ", 10.f, "option to configure z-vertex cut"}; + OutputObj<TH1F> hTrkPrimAftEvSel{TH1F("hTrkPrimAftEvSel", + "Reco Prim tracks AftEvSel (charged); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)}; + OutputObj<HistArray> hTrkPrimAftEvSel_truepid{HistArray("hTrkPrimAftEvSel_truepid"), + OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + hTrkPrimAftEvSel_truepid->Add<kEl>(TH1F("hTrkPrimAftEvSel_truepid_el", + "Gen tracks aft. ev. sel. (true El); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimAftEvSel_truepid->Add<kPi>(TH1F("hTrkPrimAftEvSel_truepid_pi", + "Gen tracks aft. ev. sel. (true Pi); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimAftEvSel_truepid->Add<kKa>(TH1F("hTrkPrimAftEvSel_truepid_ka", + "Gen. tracks aft. ev. sel. (true Ka); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimAftEvSel_truepid->Add<kPr>(TH1F("hTrkPrimAftEvSel_truepid_pr", + "Gen tracks aft. ev. sel. (true Pr); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimAftEvSel_truepid->Add<kDe>(TH1F("hTrkPrimAftEvSel_truepid_de", + "Gen tracks aft. ev. sel. (true De); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + } //init + + //Filters + Filter collfilter = nabs(aod::collision::posZ) < cfgCutVZ; + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels>>::iterator const& col, + soa::Join<aod::Tracks, aod::TracksExtra, aod::McTrackLabels>& tracks, aod::McParticles& mcParticles) + { + + //event selection + if (!isMC && !col.alias()[kINT7]) { // trigger (should be skipped in MC) + return; + } + if (!col.sel7()) { //additional cuts + return; + } + + //Loop on tracks + for (auto& track : tracks) { + double y = -999.; + bool isPrimary = false; + + if (isMC) { //determine particle species base on MC truth and if it is primary or not + int pdgcode = track.label().pdgCode(); + + if (MC::isPhysicalPrimary(mcParticles, track.label())) { //is primary? + isPrimary = true; + } + + //calculate rapidity + int pint = getparticleint(pdgcode); + if (pint == kNull) { + y = (double)kNull; + } else { + y = eta2y(track.pt(), mass[pint], track.eta()); + } + + if (isPrimary && abs(y) < cfgCutY) { + //histograms with generated distribution (after event selection) + if (pdgcode == 11) { + hTrkPrimAftEvSel_truepid->Fill<kEl>(track.pt()); + } else if (pdgcode == 211) { + hTrkPrimAftEvSel_truepid->Fill<kPi>(track.pt()); + } else if (pdgcode == 321) { + hTrkPrimAftEvSel_truepid->Fill<kKa>(track.pt()); + } else if (pdgcode == 2212) { + hTrkPrimAftEvSel_truepid->Fill<kPr>(track.pt()); + } else if (pdgcode == 1000010020) { + hTrkPrimAftEvSel_truepid->Fill<kDe>(track.pt()); + } + + hTrkPrimAftEvSel->Fill(track.pt()); //charged particles + } + } + } // end loop on tracks + } //end of process +}; //end struct TrackCheckTaskEvSel + +//event selection + track selection here +struct TrackCheckTaskEvSelTrackSel { + + Configurable<bool> isMC{"isMC", false, "option to flag mc"}; + Configurable<double> cfgCutY{"cfgCutY", 0.5, "option to configure rapidity cut"}; + Configurable<float> cfgCutVZ{"cfgCutVZ", 10.f, "option to configure z-vertex cut"}; + OutputObj<TH1F> hTrkPrimReco{TH1F("hTrkPrimReco", "Reco Prim tracks (charged); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)}; + OutputObj<HistArray> hTrkPrimReco_truepid{HistArray("hTrkPrimReco_truepid"), + OutputObjHandlingPolicy::AnalysisObject}; + OutputObj<HistArray> hDCAxyReco_truepid{HistArray("hDCAxyReco_truepid"), OutputObjHandlingPolicy::AnalysisObject}; + OutputObj<HistArray> hDCAxyPrim_truepid{HistArray("hDCAxyPrim_truepid"), OutputObjHandlingPolicy::AnalysisObject}; + OutputObj<HistArray> hDCAxySeco_truepid{HistArray("hDCAxySeco_truepid"), OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + hTrkPrimReco_truepid->Add<kEl>(TH1F("hTrkPrimReco_truepid_el", + "Primary Reco tracks (true El); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimReco_truepid->Add<kPi>(TH1F("hTrkPrimReco_truepid_pi", + "Primary Reco tracks (true Pi); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimReco_truepid->Add<kKa>(TH1F("hTrkPrimReco_truepid_ka", + "Primary Reco tracks (true Ka); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimReco_truepid->Add<kPr>(TH1F("hTrkPrimReco_truepid_pr", + "Primary Reco tracks (true Pr); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + hTrkPrimReco_truepid->Add<kDe>(TH1F("hTrkPrimReco_truepid_de", + "Primary Reco tracks (true De); #it{p}_{T} (GeV/#it{c}); Counts", + NPTBINS, pt_bins)); + + hDCAxyReco_truepid->Add<kEl>(TH2F("hDCAxyReco_truepid_el", + "DCAxy reco (true El); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyReco_truepid->Add<kPi>(TH2F("hDCAxyReco_truepid_pi", + "DCAxy reco (true Pi); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyReco_truepid->Add<kKa>(TH2F("hDCAxyReco_truepid_ka", + "DCAxy reco (true Ka); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyReco_truepid->Add<kPr>(TH2F("hDCAxyReco_truepid_pr", + "DCAxy reco (true Pr); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyReco_truepid->Add<kDe>(TH2F("hDCAxyReco_truepid_de", + "DCAxy reco (true De); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + + hDCAxyPrim_truepid->Add<kEl>(TH2F("hDCAxyPrim_truepid_el", + "DCAxy primaries (true El); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyPrim_truepid->Add<kPi>(TH2F("hDCAxyPrim_truepid_pi", + "DCAxy primaries (true Pi); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyPrim_truepid->Add<kKa>(TH2F("hDCAxyPrim_truepid_ka", + "DCAxy primaries (true Ka); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyPrim_truepid->Add<kPr>(TH2F("hDCAxyPrim_truepid_pr", + "DCAxy primaries (true Pr); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxyPrim_truepid->Add<kDe>(TH2F("hDCAxyPrim_truepid_de", + "DCAxy primaries (true De); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + + hDCAxySeco_truepid->Add<kEl>(TH2F("hDCAxySeco_truepid_el", + "DCAxy secondaries (true El); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxySeco_truepid->Add<kPi>(TH2F("hDCAxySeco_truepid_pi", + "DCAxy secondaries (true Pi); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxySeco_truepid->Add<kKa>(TH2F("hDCAxySeco_truepid_ka", + "DCAxy secondaries (true Ka); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxySeco_truepid->Add<kPr>(TH2F("hDCAxySeco_truepid_pr", + "DCAxy secondaries (true Pr); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + hDCAxySeco_truepid->Add<kDe>(TH2F("hDCAxySeco_truepid_de", + "DCAxy secondaries (true De); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", + NPTBINS, pt_bins, 800, -4., 4.)); + + } //init + + //Filters + Filter collfilter = nabs(aod::collision::posZ) < cfgCutVZ; + Filter trackfilter = aod::track::isGlobalTrack == (uint8_t) true; + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels>>::iterator const& col, + soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksExtended, + aod::TrackSelection, aod::McTrackLabels>>& tracks, + aod::McParticles& mcParticles) + { + + //event selection + if (!isMC && !col.alias()[kINT7]) { // trigger (should be skipped in MC) + return; + } + if (!col.sel7()) { //additional cuts + return; + } + + //Loop on tracks + for (auto& track : tracks) { + double y = -999.; + bool isPrimary = false; + + if (isMC) { //determine particle species base on MC truth and if it is primary or not + int pdgcode = track.label().pdgCode(); + + if (MC::isPhysicalPrimary(mcParticles, track.label())) { //is primary? + isPrimary = true; + } + //Calculate y + int pint = getparticleint(pdgcode); + if (pint == kNull) { + y = (double)kNull; + } else { + y = eta2y(track.pt(), mass[pint], track.eta()); + } + + //DCAxy distributions for reco, primary, secondaries (= not primary) + // + distributions for reco primaries (MC truth) + if (abs(y) < cfgCutY) { + if (pdgcode == 11) { + hDCAxyReco_truepid->Fill<kEl>(track.pt(), track.dcaXY()); + if (isPrimary) { + hDCAxyPrim_truepid->Fill<kEl>(track.pt(), track.dcaXY()); + hTrkPrimReco_truepid->Fill<kEl>(track.pt()); + } else { + hDCAxySeco_truepid->Fill<kEl>(track.pt(), track.dcaXY()); + } + } else if (pdgcode == 211) { + hDCAxyReco_truepid->Fill<kPi>(track.pt(), track.dcaXY()); + if (isPrimary) { + hDCAxyPrim_truepid->Fill<kPi>(track.pt(), track.dcaXY()); + hTrkPrimReco_truepid->Fill<kPi>(track.pt()); + } else { + hDCAxySeco_truepid->Fill<kPi>(track.pt(), track.dcaXY()); + } + } else if (pdgcode == 321) { + hDCAxyReco_truepid->Fill<kKa>(track.pt(), track.dcaXY()); + if (isPrimary) { + hDCAxyPrim_truepid->Fill<kKa>(track.pt(), track.dcaXY()); + hTrkPrimReco_truepid->Fill<kKa>(track.pt()); + } else { + hDCAxySeco_truepid->Fill<kKa>(track.pt(), track.dcaXY()); + } + } else if (pdgcode == 2212) { + hDCAxyReco_truepid->Fill<kPr>(track.pt(), track.dcaXY()); + if (isPrimary) { + hDCAxyPrim_truepid->Fill<kPr>(track.pt(), track.dcaXY()); + hTrkPrimReco_truepid->Fill<kPr>(track.pt()); + } else { + hDCAxySeco_truepid->Fill<kPr>(track.pt(), track.dcaXY()); + } + } else if (pdgcode == 1000010020) { + hDCAxyReco_truepid->Fill<kDe>(track.pt(), track.dcaXY()); + if (isPrimary) { + hDCAxyPrim_truepid->Fill<kDe>(track.pt(), track.dcaXY()); + hTrkPrimReco_truepid->Fill<kDe>(track.pt()); + } else { + hDCAxySeco_truepid->Fill<kDe>(track.pt(), track.dcaXY()); + } + } + } + + //reco histos (charged particles) + if (isPrimary && abs(y) < cfgCutY) { + hTrkPrimReco->Fill(track.pt()); //charged particles + } + } + } // end loop on tracks + } +}; // struct TrackCheckTask1 + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<TrackCheckTaskEvSel>("track-histos-evsel"), + adaptAnalysisTask<TrackCheckTaskEvSelTrackSel>("track-histos-evsel-trksel")}; +} diff --git a/Analysis/Tasks/PWGMM/CMakeLists.txt b/Analysis/Tasks/PWGMM/CMakeLists.txt new file mode 100644 index 0000000000000..b0a6b6fadcc0f --- /dev/null +++ b/Analysis/Tasks/PWGMM/CMakeLists.txt @@ -0,0 +1,14 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(dNdetaRun2Tracklets-analysis + SOURCES dNdetaRun2Tracklets.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/PWGMM/dNdetaRun2Tracklets.cxx b/Analysis/Tasks/PWGMM/dNdetaRun2Tracklets.cxx new file mode 100644 index 0000000000000..a3eaf73d4e9d4 --- /dev/null +++ b/Analysis/Tasks/PWGMM/dNdetaRun2Tracklets.cxx @@ -0,0 +1,64 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" +#include <TH1F.h> +#include <TH2F.h> +#include <cmath> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct PseudorapidityDensity { + + Configurable<double> etaBinWidth{"etaBinWidth", 0.1, "eta bin width"}; + Configurable<float> etaMax{"etaMax", 1.5, "max eta value"}; + Configurable<float> etaMin{"etaMin", -1.5, "min eta value"}; + Configurable<float> vtxZMax{"vtxZMax", 10, "max z vertex"}; + Configurable<float> vtxZMin{"vtxZMin", -10, "min z vertex"}; + int etaBins = TMath::Nint((etaMax - etaMin) / etaBinWidth); + int vtxZBins = TMath::Nint(vtxZMax - vtxZMin); + + OutputObj<TH1F> hStat{TH1F("hStat", "TotalEvents", 1, 0.5, 1.5)}; + OutputObj<TH1F> hdNdeta{TH1F("hdNdeta", "dNdeta", 50, -2.5, 2.5)}; + OutputObj<TH2F> vtxZEta{TH2F("vtxZEta", ";#eta;vtxZ", 50, -2.5, 2.5, 60, -30, 30)}; + OutputObj<TH2F> phiEta{TH2F("phiEta", ";#eta;#varphi", 50, -2.5, 2.5, 200, 0., 2 * TMath::Pi())}; + + // TODO remove static casts for configurables when fixed + Filter etaFilter = (aod::track::eta < (float)etaMax) && (aod::track::eta > (float)etaMin); + Filter trackTypeFilter = (aod::track::trackType == static_cast<uint8_t>(aod::track::TrackTypeEnum::Run2Tracklet)); + Filter posZFilter = (aod::collision::posZ < (float)vtxZMax) && (aod::collision::posZ > (float)vtxZMin); + + void process(soa::Filtered<soa::Join<aod::Collisions, aod::EvSels>>::iterator const& collisions, soa::Filtered<aod::Tracks> const& tracklets) + { + // TODO change to sel7 filter expression when implemented + if (!collisions.sel7()) + return; + hStat->Fill(collisions.size()); + auto vtxZ = collisions.posZ(); + for (auto& track : tracklets) { + vtxZEta->Fill(track.eta(), vtxZ); + phiEta->Fill(track.eta(), track.phi()); + hdNdeta->Fill(track.eta()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<PseudorapidityDensity>("dNdetaRun2Tracklets-analysis")}; +} diff --git a/Analysis/Tasks/PWGUD/CMakeLists.txt b/Analysis/Tasks/PWGUD/CMakeLists.txt new file mode 100644 index 0000000000000..ab3c773a4ce99 --- /dev/null +++ b/Analysis/Tasks/PWGUD/CMakeLists.txt @@ -0,0 +1,14 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(upc-an + SOURCES upcAnalysis.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisDataModel O2::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/upcAnalysis.cxx b/Analysis/Tasks/PWGUD/upcAnalysis.cxx similarity index 84% rename from Analysis/Tasks/upcAnalysis.cxx rename to Analysis/Tasks/PWGUD/upcAnalysis.cxx index 54b728d5784c2..e307feee0594e 100644 --- a/Analysis/Tasks/upcAnalysis.cxx +++ b/Analysis/Tasks/PWGUD/upcAnalysis.cxx @@ -10,9 +10,9 @@ #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/TrackSelection.h" -#include "Analysis/TrackSelectionTables.h" -#include "Analysis/EventSelection.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/EventSelection.h" #include <TH1D.h> #include <TH2D.h> #include <cmath> @@ -33,32 +33,38 @@ struct UPCAnalysis { void process(soa::Join<aod::Collisions, aod::EvSels>::iterator const& col, soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection> const& tracks) { bool checkV0 = col.bbV0A() || col.bbV0C() || col.bgV0A() || col.bgV0C(); - if (checkV0) + if (checkV0) { return; + } bool checkFDD = col.bbFDA() || col.bbFDC() || col.bgFDA() || col.bgFDC(); - if (checkFDD) + if (checkFDD) { + return; + } + if (!col.alias()[kCUP9]) { return; - // TODO: - // Add trigger class check - // if (!col.alias()[kCUP9]) - // return; + } std::vector<soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection>::iterator> selTracks; for (auto track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; + } selTracks.push_back(track); - if (selTracks.size() == 2) + if (selTracks.size() > 2) { break; + } } - if (selTracks.size() != 2) + if (selTracks.size() != 2) { return; - if (selTracks[0].charge() * selTracks[1].charge() >= 0) + } + if (selTracks[0].charge() * selTracks[1].charge() >= 0) { return; + } UChar_t clustermap1 = selTracks[0].itsClusterMap(); UChar_t clustermap2 = selTracks[1].itsClusterMap(); bool checkClusMap = TESTBIT(clustermap1, 0) && TESTBIT(clustermap1, 1) && TESTBIT(clustermap2, 0) && TESTBIT(clustermap2, 1); - if (!checkClusMap) + if (!checkClusMap) { return; + } TLorentzVector p1, p2, p; p1.SetXYZM(selTracks[0].px(), selTracks[0].py(), selTracks[0].pz(), mpion); p2.SetXYZM(selTracks[1].px(), selTracks[1].py(), selTracks[1].pz(), mpion); diff --git a/Analysis/Tasks/SkimmingTutorials/CMakeLists.txt b/Analysis/Tasks/SkimmingTutorials/CMakeLists.txt new file mode 100644 index 0000000000000..fc409b4efb12c --- /dev/null +++ b/Analysis/Tasks/SkimmingTutorials/CMakeLists.txt @@ -0,0 +1,25 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_dpl_workflow(tpcspectra-task-skim-reference + SOURCES spectraTPCReference.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(tpcspectra-task-skim-provider + SOURCES spectraTPCProvider.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) + +o2_add_dpl_workflow(tpcspectra-task-skim-analyser + SOURCES spectraTPCAnalyser.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/SkimmingTutorials/DataModel/LFDerived.h b/Analysis/Tasks/SkimmingTutorials/DataModel/LFDerived.h new file mode 100644 index 0000000000000..27c19e854a3e0 --- /dev/null +++ b/Analysis/Tasks/SkimmingTutorials/DataModel/LFDerived.h @@ -0,0 +1,37 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSIS_LFDERIVED_H +#define O2_ANALYSIS_LFDERIVED_H + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +//DECLARE_SOA_TABLE(LFCollisions, "AOD", "LFCOLLISION", o2::soa::Index<>, +// o2::aod::bc::RunNumber, o2::aod::collision::PosZ); +//using LFCollision = LFCollisions::iterator; + +namespace lftrack +{ +//DECLARE_SOA_INDEX_COLUMN(LFCollision, lfCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(P, p, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(TPCNSigma, tpcNSigma, float[9]); +} // namespace lftrack +DECLARE_SOA_TABLE(LFTracks, "AOD", "LFTRACK", o2::soa::Index<>, + //lftrack::LFCollisionId, + lftrack::Pt, lftrack::P, lftrack::Eta, + lftrack::TPCNSigma); +using LFTrack = LFTracks::iterator; +} // namespace o2::aod + +#endif // O2_ANALYSIS_LFDERIVED_H diff --git a/Analysis/Tasks/SkimmingTutorials/LFDerived.h b/Analysis/Tasks/SkimmingTutorials/LFDerived.h new file mode 100644 index 0000000000000..b122888acd74b --- /dev/null +++ b/Analysis/Tasks/SkimmingTutorials/LFDerived.h @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Nima Zardoshti <nima.zardoshti@cern.ch>, CERN +#ifndef O2_ANALYSIS_LFDERIVED_H +#define O2_ANALYSIS_LFDERIVED_H + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +//DECLARE_SOA_TABLE(LFCollisions, "AOD", "LFCOLLISION", o2::soa::Index<>, +// o2::aod::bc::RunNumber, o2::aod::collision::PosZ); +//using LFCollision = LFCollisions::iterator; + +namespace lftrack +{ +//DECLARE_SOA_INDEX_COLUMN(LFCollision, lfCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(P, p, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(TPCNSigma, tpcNSigma, float[9]); +} // namespace lftrack +DECLARE_SOA_TABLE(LFTracks, "AOD", "LFTRACK", o2::soa::Index<>, + //lftrack::LFCollisionId, + lftrack::Pt, lftrack::P, lftrack::Eta, + lftrack::TPCNSigma); +using LFTrack = LFTracks::iterator; +} // namespace o2::aod + +#endif // O2_ANALYSIS_LFDERIVED_H diff --git a/Analysis/Tasks/SkimmingTutorials/spectraTPCAnalyser.cxx b/Analysis/Tasks/SkimmingTutorials/spectraTPCAnalyser.cxx new file mode 100644 index 0000000000000..58ea028b15150 --- /dev/null +++ b/Analysis/Tasks/SkimmingTutorials/spectraTPCAnalyser.cxx @@ -0,0 +1,110 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "ReconstructionDataFormats/Track.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "DataModel/LFDerived.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"add-tof-histos", VariantType::Int, 0, {"Generate TPC with TOF histograms"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +// FIXME: we should put this function in some common header so it has to be defined only once +template <typename T> +void makelogaxis(T h) +{ + const int nbins = h->GetNbinsX(); + double binp[nbins + 1]; + double max = h->GetXaxis()->GetBinUpEdge(nbins); + double min = h->GetXaxis()->GetBinLowEdge(1); + if (min <= 0) { + min = 0.00001; + } + double lmin = TMath::Log10(min); + double ldelta = (TMath::Log10(max) - lmin) / ((double)nbins); + for (int i = 0; i < nbins; i++) { + binp[i] = TMath::Exp(TMath::Log(10) * (lmin + i * ldelta)); + } + binp[nbins] = max + 1; + h->GetXaxis()->Set(nbins, binp); +} + +constexpr int Np = 9; +struct TPCSpectraAnalyserTask { + + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + static constexpr std::string_view hp[Np] = {"p/El", "p/Mu", "p/Pi", "p/Ka", "p/Pr", "p/De", "p/Tr", "p/He", "p/Al"}; + static constexpr std::string_view hpt[Np] = {"pt/El", "pt/Mu", "pt/Pi", "pt/Ka", "pt/Pr", "pt/De", "pt/Tr", "pt/He", "pt/Al"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + histos.add("p/Unselected", "Unselected;#it{p} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + histos.add("pt/Unselected", "Unselected;#it{p}_{T} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + for (int i = 0; i < Np; i++) { + histos.add(hp[i].data(), Form("%s;#it{p} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + histos.add(hpt[i].data(), Form("%s;#it{p}_{T} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + } + } + + Configurable<float> nsigmacut{"nsigmacut", 3, "Value of the Nsigma cut"}; + Configurable<float> cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable<float> cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + + template <std::size_t i, typename T> + void fillParticleHistos(const T& track, const float nsigma[]) + { + if (abs(nsigma[i]) > nsigmacut.value) { + return; + } + histos.fill(HIST(hp[i]), track.p()); + histos.fill(HIST(hpt[i]), track.pt()); + } + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; //collision filters not doing anything now? + Filter trackFilter = nabs(aod::lftrack::eta) < cfgCutEta; + + void process(soa::Filtered<aod::LFTracks>::iterator const& track) + { + auto nsigma = track.tpcNSigma(); + histos.fill(HIST("p/Unselected"), track.p()); + histos.fill(HIST("pt/Unselected"), track.pt()); + + fillParticleHistos<0>(track, nsigma); + fillParticleHistos<1>(track, nsigma); + fillParticleHistos<2>(track, nsigma); + fillParticleHistos<3>(track, nsigma); + fillParticleHistos<4>(track, nsigma); + fillParticleHistos<5>(track, nsigma); + fillParticleHistos<6>(track, nsigma); + fillParticleHistos<7>(track, nsigma); + fillParticleHistos<8>(track, nsigma); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask<TPCSpectraAnalyserTask>("tpcspectra-task-skim-analyser")}; + return workflow; +} diff --git a/Analysis/Tasks/SkimmingTutorials/spectraTPCProvider.cxx b/Analysis/Tasks/SkimmingTutorials/spectraTPCProvider.cxx new file mode 100644 index 0000000000000..4c4b8ca7914de --- /dev/null +++ b/Analysis/Tasks/SkimmingTutorials/spectraTPCProvider.cxx @@ -0,0 +1,61 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Nima Zardoshti <nima.zardoshti@cern.ch>, CERN + +// O2 includes +#include "ReconstructionDataFormats/Track.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "MathUtils/Utils.h" +#include "DataModel/LFDerived.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::math_utils::detail; + +#include "Framework/runDataProcessing.h" + +struct TPCSpectraProviderTask { + + //Produces<aod::LFCollisions> outputCollisions; //currently it seems in the spectraTPC task no loop over the collision is made. Leave this here in case it will be added + Produces<aod::LFTracks> outputTracks; + + //Configurable<float> cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable<float> trackEtaCut{"trackEtaCut", 0.9f, "Eta range for tracks"}; + + //Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (nabs(aod::track::eta) < trackEtaCut) && (aod::track::isGlobalTrack == (uint8_t) true); + + Configurable<float> nsigmacut{"nsigmacut", 3, "Value of the Nsigma cut"}; //can we add an upper limit? + + using TrackCandidates = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTPC, aod::TrackSelection>>; + void process(TrackCandidates::iterator const& track) + { + uint32_t pNsigma = 0xFFFFFF00; //15 bit precision for Nsigma + float nsigma[9] = {truncateFloatFraction(track.tpcNSigmaEl(), pNsigma), truncateFloatFraction(track.tpcNSigmaMu(), pNsigma), + truncateFloatFraction(track.tpcNSigmaPi(), pNsigma), truncateFloatFraction(track.tpcNSigmaKa(), pNsigma), + truncateFloatFraction(track.tpcNSigmaPr(), pNsigma), truncateFloatFraction(track.tpcNSigmaDe(), pNsigma), + truncateFloatFraction(track.tpcNSigmaTr(), pNsigma), truncateFloatFraction(track.tpcNSigmaHe(), pNsigma), + truncateFloatFraction(track.tpcNSigmaAl(), pNsigma)}; //the significance needs to be discussed + + //outputTracks(outputCollisions.lastIndex(), track.pt(), track.p(), track.eta(), nsigma); + outputTracks(track.pt(), track.p(), track.eta(), nsigma); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + WorkflowSpec workflow{adaptAnalysisTask<TPCSpectraProviderTask>("tpcspectra-task-skim-provider")}; + return workflow; +} diff --git a/Analysis/Tasks/SkimmingTutorials/spectraTPCReference.cxx b/Analysis/Tasks/SkimmingTutorials/spectraTPCReference.cxx new file mode 100644 index 0000000000000..f9d6305756734 --- /dev/null +++ b/Analysis/Tasks/SkimmingTutorials/spectraTPCReference.cxx @@ -0,0 +1,112 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "ReconstructionDataFormats/Track.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"add-tof-histos", VariantType::Int, 0, {"Generate TPC with TOF histograms"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +// FIXME: we should put this function in some common header so it has to be defined only once +template <typename T> +void makelogaxis(T h) +{ + const int nbins = h->GetNbinsX(); + double binp[nbins + 1]; + double max = h->GetXaxis()->GetBinUpEdge(nbins); + double min = h->GetXaxis()->GetBinLowEdge(1); + if (min <= 0) { + min = 0.00001; + } + double lmin = TMath::Log10(min); + double ldelta = (TMath::Log10(max) - lmin) / ((double)nbins); + for (int i = 0; i < nbins; i++) { + binp[i] = TMath::Exp(TMath::Log(10) * (lmin + i * ldelta)); + } + binp[nbins] = max + 1; + h->GetXaxis()->Set(nbins, binp); +} + +constexpr int Np = 9; +struct TPCSpectraReferenceTask { + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + static constexpr std::string_view hp[Np] = {"p/El", "p/Mu", "p/Pi", "p/Ka", "p/Pr", "p/De", "p/Tr", "p/He", "p/Al"}; + static constexpr std::string_view hpt[Np] = {"pt/El", "pt/Mu", "pt/Pi", "pt/Ka", "pt/Pr", "pt/De", "pt/Tr", "pt/He", "pt/Al"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + histos.add("p/Unselected", "Unselected;#it{p} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + histos.add("pt/Unselected", "Unselected;#it{p}_{T} (GeV/#it{c})", kTH1F, {{100, 0, 20}}); + for (int i = 0; i < Np; i++) { + histos.add(hp[i].data(), Form("%s;#it{p} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + histos.add(hpt[i].data(), Form("%s;#it{p}_{T} (GeV/#it{c})", pT[i]), kTH1F, {{100, 0, 20}}); + } + } + + //Defining filters and input + Configurable<float> cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable<float> cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == (uint8_t) true); + + Configurable<float> nsigmacut{"nsigmacut", 3, "Value of the Nsigma cut"}; + + template <std::size_t i, typename T> + void fillParticleHistos(const T& track, const float nsigma[]) + { + if (abs(nsigma[i]) > nsigmacut.value) { + return; + } + histos.fill(HIST(hp[i]), track.p()); + histos.fill(HIST(hpt[i]), track.pt()); + } + + using TrackCandidates = soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTPC, aod::TrackSelection>>; + void process(TrackCandidates::iterator const& track) + { + const float nsigma[Np] = {track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), + track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe(), + track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl()}; + histos.fill(HIST("p/Unselected"), track.p()); + histos.fill(HIST("pt/Unselected"), track.pt()); + + fillParticleHistos<0>(track, nsigma); + fillParticleHistos<1>(track, nsigma); + fillParticleHistos<2>(track, nsigma); + fillParticleHistos<3>(track, nsigma); + fillParticleHistos<4>(track, nsigma); + fillParticleHistos<5>(track, nsigma); + fillParticleHistos<6>(track, nsigma); + fillParticleHistos<7>(track, nsigma); + fillParticleHistos<8>(track, nsigma); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask<TPCSpectraReferenceTask>("tpcspectra-task-skim-reference")}; + return workflow; +} diff --git a/Analysis/Tasks/centralityQa.cxx b/Analysis/Tasks/centralityQa.cxx index 7d6717f8c67d3..f92e4e1792191 100644 --- a/Analysis/Tasks/centralityQa.cxx +++ b/Analysis/Tasks/centralityQa.cxx @@ -10,8 +10,8 @@ #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/EventSelection.h" -#include "Analysis/Centrality.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Centrality.h" #include "TH1F.h" using namespace o2; @@ -21,10 +21,12 @@ struct CentralityQaTask { OutputObj<TH1F> hCentV0M{TH1F("hCentV0M", "", 21, 0, 105.)}; void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator const& col) { - if (!col.alias()[kINT7]) + if (!col.alias()[kINT7]) { return; - if (!col.sel7()) + } + if (!col.sel7()) { return; + } LOGF(debug, "centV0M=%.0f", col.centV0M()); // fill centrality histos diff --git a/Analysis/Tasks/centralityTable.cxx b/Analysis/Tasks/centralityTable.cxx index dfb0435cd03cd..65f2cfaf2368d 100644 --- a/Analysis/Tasks/centralityTable.cxx +++ b/Analysis/Tasks/centralityTable.cxx @@ -10,41 +10,35 @@ #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/Multiplicity.h" -#include "Analysis/Centrality.h" -#include "CCDB/CcdbApi.h" -#include "TFile.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/Centrality.h" +#include <CCDB/BasicCCDBManager.h> #include "TH1F.h" -#include <map> -using std::map; -using std::string; using namespace o2; using namespace o2::framework; struct CentralityTableTask { Produces<aod::Cents> cent; - TH1F* hCumMultV0M; + Service<o2::ccdb::BasicCCDBManager> ccdb; void init(InitContext&) { - // TODO read run number from configurables - int run = 244918; - - // read hMultV0M from ccdb - o2::ccdb::CcdbApi ccdb; - map<string, string> metadata; - ccdb.init("http://ccdb-test.cern.ch:8080"); - TH1F* hMultV0M = ccdb.retrieveFromTFileAny<TH1F>("Multiplicity/MultV0M", metadata, run); - - // TODO produce cumulative histos in the post processing macro - hCumMultV0M = (TH1F*)hMultV0M->GetCumulative(false); - hCumMultV0M->Scale(100. / hCumMultV0M->GetMaximum()); + ccdb->setURL("http://ccdb-test.cern.ch:8080"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); } - void process(aod::Mult const& mult) + void process(soa::Join<aod::Collisions, aod::Mults>::iterator const& collision, aod::BCsWithTimestamps const&) { - float centV0M = hCumMultV0M->GetBinContent(hCumMultV0M->FindFixBin(mult.multV0M())); + auto bc = collision.bc_as<aod::BCsWithTimestamps>(); + LOGF(debug, "timestamp=%llu", bc.timestamp()); + TH1F* hCumMultV0M = ccdb->getForTimeStamp<TH1F>("Multiplicity/CumMultV0M", bc.timestamp()); + if (!hCumMultV0M) { + LOGF(fatal, "V0M centrality calibration is not available in CCDB for run=%d at timestamp=%llu", bc.runNumber(), bc.timestamp()); + } + float centV0M = hCumMultV0M->GetBinContent(hCumMultV0M->FindFixBin(collision.multV0M())); + LOGF(debug, "centV0M=%.0f", centV0M); // fill centrality columns cent(centV0M); diff --git a/Analysis/Tasks/correlations.cxx b/Analysis/Tasks/correlations.cxx deleted file mode 100644 index 1ac440d299248..0000000000000 --- a/Analysis/Tasks/correlations.cxx +++ /dev/null @@ -1,96 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Analysis/StepTHn.h" -#include "Analysis/CorrelationContainer.h" -#include <TH1F.h> -#include <cmath> - -using namespace o2; -using namespace o2::framework; - -struct CorrelationTask { - OutputObj<CorrelationContainer> same{"sameEvent"}; - //OutputObj<CorrelationContainer> mixed{"mixedEvent"}; - - void init(o2::framework::InitContext&) - { - const char* binning = - "vertex: -7, -5, -3, -1, 1, 3, 5, 7\n" - "delta_phi: -1.570796, -1.483530, -1.396263, -1.308997, -1.221730, -1.134464, -1.047198, -0.959931, -0.872665, -0.785398, -0.698132, -0.610865, -0.523599, -0.436332, -0.349066, -0.261799, -0.174533, -0.087266, 0.0, 0.087266, 0.174533, 0.261799, 0.349066, 0.436332, 0.523599, 0.610865, 0.698132, 0.785398, 0.872665, 0.959931, 1.047198, 1.134464, 1.221730, 1.308997, 1.396263, 1.483530, 1.570796, 1.658063, 1.745329, 1.832596, 1.919862, 2.007129, 2.094395, 2.181662, 2.268928, 2.356194, 2.443461, 2.530727, 2.617994, 2.705260, 2.792527, 2.879793, 2.967060, 3.054326, 3.141593, 3.228859, 3.316126, 3.403392, 3.490659, 3.577925, 3.665191, 3.752458, 3.839724, 3.926991, 4.014257, 4.101524, 4.188790, 4.276057, 4.363323, 4.450590, 4.537856, 4.625123, 4.712389\n" - "delta_eta: -2.0, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0\n" - "p_t_assoc: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0\n" - "p_t_trigger: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0\n" - "multiplicity: 0, 5, 10, 20, 30, 40, 50, 100.1\n" - "eta: -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0\n" - "p_t_leading: 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0, 16.5, 17.0, 17.5, 18.0, 18.5, 19.0, 19.5, 20.0, 20.5, 21.0, 21.5, 22.0, 22.5, 23.0, 23.5, 24.0, 24.5, 25.0, 25.5, 26.0, 26.5, 27.0, 27.5, 28.0, 28.5, 29.0, 29.5, 30.0, 30.5, 31.0, 31.5, 32.0, 32.5, 33.0, 33.5, 34.0, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0, 41.5, 42.0, 42.5, 43.0, 43.5, 44.0, 44.5, 45.0, 45.5, 46.0, 46.5, 47.0, 47.5, 48.0, 48.5, 49.0, 49.5, 50.0\n" - "p_t_leading_course: 0.5, 1.0, 2.0, 3.0, 4.0, 6.0, 8.0\n" - "p_t_eff: 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0\n" - "vertex_eff: -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10\n"; - - same.setObject(new CorrelationContainer("sameEvent", "sameEvent", "NumberDensityPhiCentrality", binning)); - //mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", "NumberDensityPhiCentrality", binning)); - } - - void process(aod::Collision const& collision, aod::Tracks const& tracks) - { - LOGF(info, "Tracks for collision: %d (z: %f V0: %f)", tracks.size(), -1.0, collision.posZ()); - - for (auto it1 = tracks.begin(); it1 != tracks.end(); ++it1) { - auto& track1 = *it1; - if (track1.pt() < 0.5) - continue; - - double eventValues[3]; - eventValues[0] = track1.pt(); - eventValues[1] = 0; //collision.v0mult(); - eventValues[2] = collision.posZ(); - - same->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); - //mixed->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); - - for (auto it2 = it1 + 1; it2 != tracks.end(); ++it2) { - auto& track2 = *it2; - //if (track1 == track2) - // continue; - if (track2.pt() < 0.5) - continue; - - double values[6]; - - values[0] = track1.eta() - track2.eta(); - values[1] = track1.pt(); - values[2] = track2.pt(); - values[3] = 0; //collision.v0mult(); - - values[4] = track1.phi() - track2.phi(); - if (values[4] > 1.5 * TMath::Pi()) - values[4] -= TMath::TwoPi(); - if (values[4] < -0.5 * TMath::Pi()) - values[4] += TMath::TwoPi(); - - values[5] = collision.posZ(); - - same->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); - //mixed->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<CorrelationTask>("correlation-task"), - }; -} diff --git a/Analysis/Tasks/correlationsCollection.cxx b/Analysis/Tasks/correlationsCollection.cxx deleted file mode 100644 index f53fa25b68d3e..0000000000000 --- a/Analysis/Tasks/correlationsCollection.cxx +++ /dev/null @@ -1,527 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" - -#include "Analysis/EventSelection.h" -#include "Analysis/Centrality.h" -#include "Analysis/StepTHn.h" -#include "Analysis/CorrelationContainer.h" - -#include <TH1F.h> -#include <cmath> -#include <TDirectory.h> - -namespace o2::aod -{ -namespace etaphi -{ -DECLARE_SOA_COLUMN(Etam, etam, float); -DECLARE_SOA_COLUMN(Phim, phim, float); -DECLARE_SOA_COLUMN(Ptm, ptm, float); -} // namespace etaphi -DECLARE_SOA_TABLE(EtaPhi, "AOD", "ETAPHI", - etaphi::Etam, etaphi::Phim, etaphi::Ptm); -} // namespace o2::aod - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -struct ATask { - Produces<aod::EtaPhi> etaphi; - - void process(aod::Tracks const& tracks) - { - for (auto& track : tracks) { - float eta = log(tan(0.25f * static_cast<float>(M_PI) - 0.5f * atan(track.tgl()))); - float phi = asin(track.snp()) + track.alpha() + static_cast<float>(M_PI); - float pt = fabs(1.0f / track.signed1Pt()); - - etaphi(eta, phi, pt); - } - } -}; - -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable<TYPE> NAME{#NAME, DEFAULT, HELP}; - -struct CorrelationTask { - - // Filters and input definitions -#define MYFILTER -#ifdef MYFILTER - Filter trackFilter = (aod::etaphi::etam > -0.8f) && (aod::etaphi::etam < 0.8f) && (aod::etaphi::ptm > 1.0f); - using myTracks = soa::Filtered<soa::Join<aod::Tracks, aod::EtaPhi>>; -#else - using myTracks = soa::Join<aod::Tracks, aod::EtaPhi>; -#endif - - // Output definitions - OutputObj<CorrelationContainer> same{"sameEvent"}; - OutputObj<CorrelationContainer> mixed{"mixedEvent"}; - //OutputObj<TDirectory> qaOutput{"qa"}; - - // Configuration - O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative"); - O2_DEFINE_CONFIGURABLE(cfgAssociatedCharge, int, 0, "Select on charge of associated particle: 0 = all; 1 = positive; -1 = negative"); - O2_DEFINE_CONFIGURABLE(cfgPairCharge, int, 0, "Select on charge of particle pair: 0 = all; 1 = like sign; -1 = unlike sign"); - - O2_DEFINE_CONFIGURABLE(cfgTwoTrackCut, float, -1, "Two track cut: -1 = off; >0 otherwise distance value"); - O2_DEFINE_CONFIGURABLE(cfgTwoTrackCutMinRadius, float, 0.8f, "Two track cut: radius in m from which two track cuts are applied"); - - O2_DEFINE_CONFIGURABLE(cfgPairCutPhoton, float, -1, "Pair cut on photons: -1 = off; >0 otherwise distance value (suggested: 0.004)") - O2_DEFINE_CONFIGURABLE(cfgPairCutK0, float, -1, "Pair cut on K0s: -1 = off; >0 otherwise distance value (suggested: 0.005)") - O2_DEFINE_CONFIGURABLE(cfgPairCutLambda, float, -1, "Pair cut on Lambda: -1 = off; >0 otherwise distance value (suggested: 0.005)") - O2_DEFINE_CONFIGURABLE(cfgPairCutPhi, float, -1, "Pair cut on Phi: -1 = off; >0 otherwise distance value") - O2_DEFINE_CONFIGURABLE(cfgPairCutRho, float, -1, "Pair cut on Rho: -1 = off; >0 otherwise distance value") - - enum PairCuts { Photon = 0, - K0, - Lambda, - Phi, - Rho }; - struct Config { - bool mPairCuts = false; - //THn* mEfficiencyTrigger = nullptr; - //THn* mEfficiencyAssociated = nullptr; - } cfg; - - struct QA { - TH3F* mTwoTrackDistancePt[2] = {nullptr}; // control histograms for two-track efficiency study: dphi*_min vs deta (0 = before cut, 1 = after cut) - TH2F* mControlConvResoncances = nullptr; // control histograms for cuts on conversions and resonances - } qa; - - void init(o2::framework::InitContext&) - { - // --- CONFIGURATION --- - const char* binning = - "vertex: -7, -5, -3, -1, 1, 3, 5, 7\n" - "delta_phi: -1.570796, -1.483530, -1.396263, -1.308997, -1.221730, -1.134464, -1.047198, -0.959931, -0.872665, -0.785398, -0.698132, -0.610865, -0.523599, -0.436332, -0.349066, -0.261799, -0.174533, -0.087266, 0.0, 0.087266, 0.174533, 0.261799, 0.349066, 0.436332, 0.523599, 0.610865, 0.698132, 0.785398, 0.872665, 0.959931, 1.047198, 1.134464, 1.221730, 1.308997, 1.396263, 1.483530, 1.570796, 1.658063, 1.745329, 1.832596, 1.919862, 2.007129, 2.094395, 2.181662, 2.268928, 2.356194, 2.443461, 2.530727, 2.617994, 2.705260, 2.792527, 2.879793, 2.967060, 3.054326, 3.141593, 3.228859, 3.316126, 3.403392, 3.490659, 3.577925, 3.665191, 3.752458, 3.839724, 3.926991, 4.014257, 4.101524, 4.188790, 4.276057, 4.363323, 4.450590, 4.537856, 4.625123, 4.712389\n" - "delta_eta: -2.0, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0\n" - "p_t_assoc: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0\n" - "p_t_trigger: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0\n" - "multiplicity: 0, 5, 10, 20, 30, 40, 50, 100.1\n" - "eta: -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0\n" - "p_t_leading: 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0, 16.5, 17.0, 17.5, 18.0, 18.5, 19.0, 19.5, 20.0, 20.5, 21.0, 21.5, 22.0, 22.5, 23.0, 23.5, 24.0, 24.5, 25.0, 25.5, 26.0, 26.5, 27.0, 27.5, 28.0, 28.5, 29.0, 29.5, 30.0, 30.5, 31.0, 31.5, 32.0, 32.5, 33.0, 33.5, 34.0, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0, 41.5, 42.0, 42.5, 43.0, 43.5, 44.0, 44.5, 45.0, 45.5, 46.0, 46.5, 47.0, 47.5, 48.0, 48.5, 49.0, 49.5, 50.0\n" - "p_t_leading_course: 0.5, 1.0, 2.0, 3.0, 4.0, 6.0, 8.0\n" - "p_t_eff: 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0\n" - "vertex_eff: -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10\n"; - - if (cfgPairCutPhoton > 0 || cfgPairCutK0 > 0 || cfgPairCutLambda > 0 || cfgPairCutPhi > 0 || cfgPairCutRho > 0) - cfg.mPairCuts = true; - - // --- OBJECT INIT --- - same.setObject(new CorrelationContainer("sameEvent", "sameEvent", "NumberDensityPhiCentrality", binning)); - mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", "NumberDensityPhiCentrality", binning)); - //qaOutput.setObject(new TDirectory("qa", "qa")); - - if (cfgTwoTrackCut > 0) { - qa.mTwoTrackDistancePt[0] = new TH3F("TwoTrackDistancePt[0]", ";#Delta#eta;#Delta#varphi^{*}_{min};#Delta p_{T}", 100, -0.15, 0.15, 100, -0.05, 0.05, 20, 0, 10); - qa.mTwoTrackDistancePt[1] = (TH3F*)qa.mTwoTrackDistancePt[0]->Clone("TwoTrackDistancePt[1]"); - //qaOutput->Add(qa.mTwoTrackDistancePt[0]); - //qaOutput->Add(qa.mTwoTrackDistancePt[1]); - } - - if (cfg.mPairCuts) { - qa.mControlConvResoncances = new TH2F("ControlConvResoncances", ";id;delta mass", 6, -0.5, 5.5, 500, -0.5, 0.5); - //qaOutput->Add(qa.mControlConvResoncances); - } - } - - // Version with explicit nested loop - void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator const& collision, aod::BCs const& bcs, aod::Run2V0s const& vzeros, myTracks const& tracks) - { - LOGF(info, "Tracks for collision: %d | Trigger mask: %lld | INT7: %d | V0M: %.1f", tracks.size(), collision.bc().triggerMask(), collision.sel7(), collision.centV0M()); - // for (auto& vzero : vzeros) - // if (vzero.bc() == collision.bc()) - // LOGF(info, "V0: %f %f", vzero.adc()[0], vzero.adc()[1]); - - if (!collision.sel7()) - return; - - int bSign = 1; // TODO magnetic field from CCDB - const float pTCut = 1.0; - - for (auto track1 = tracks.begin(); track1 != tracks.end(); ++track1) { - -#ifndef MYFILTER - if (track1.pt2() < pTCut) - continue; - if (track1.eta2() < -0.8 || track1.eta2() > 0.8) - continue; -#endif - - if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) - continue; - - //LOGF(info, "TRACK %f %f | %f %f | %f %f", track1.eta(), track1.eta2(), track1.phi(), track1.phi2(), track1.pt(), track1.pt2()); - - double eventValues[3]; - eventValues[0] = track1.pt2(); - eventValues[1] = collision.centV0M(); - eventValues[2] = collision.posZ(); - - same->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); - //mixed->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); - - for (auto track2 = track1 + 1; track2 != tracks.end(); ++track2) { -#ifndef MYFILTER - if (track2.pt2() < pTCut) - continue; - if (track2.eta2() < -0.8 || track2.eta2() > 0.8) - continue; -#endif - - if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) - continue; - if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) - continue; - - if (cfg.mPairCuts && conversionCuts(track1, track2)) - continue; - - if (cfgTwoTrackCut > 0 && twoTrackCut(track1, track2, bSign)) - continue; - - double values[6] = {0}; - - values[0] = track1.etam() - track2.etam(); - values[1] = track1.pt2(); - values[2] = track2.pt2(); - values[3] = collision.centV0M(); - - values[4] = track1.phim() - track2.phim(); - if (values[4] > 1.5 * TMath::Pi()) - values[4] -= TMath::TwoPi(); - if (values[4] < -0.5 * TMath::Pi()) - values[4] += TMath::TwoPi(); - - values[5] = collision.posZ(); - - same->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); - //mixed->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); - } - } - } - - // Version with combinations - void process2(aod::Collision const& collision, soa::Filtered<soa::Join<aod::Tracks, aod::EtaPhi>> const& tracks) - { - LOGF(info, "Tracks for collision (Combination run): %d", tracks.size()); - - int bSign = 1; // TODO magnetic field from CCDB - - for (auto track1 = tracks.begin(); track1 != tracks.end(); ++track1) { - - if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) - continue; - - // LOGF(info, "TRACK %f %f | %f %f | %f %f", track1.eta(), track1.eta2(), track1.phi(), track1.phi2(), track1.pt(), track1.pt2()); - - double eventValues[3]; - eventValues[0] = track1.pt2(); - eventValues[1] = 0; // collision.v0mult(); - eventValues[2] = collision.posZ(); - - same->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); - //mixed->getEventHist()->Fill(eventValues, CorrelationContainer::kCFStepReconstructed); - } - - for (auto& [track1, track2] : combinations(tracks, tracks)) { - //LOGF(info, "Combination %d %d", track1.index(), track2.index()); - - if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.charge() < 0) - continue; - if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.charge() < 0) - continue; - if (cfgPairCharge != 0 && cfgPairCharge * track1.charge() * track2.charge() < 0) - continue; - - if (cfg.mPairCuts && conversionCuts(track1, track2)) - continue; - - if (cfgTwoTrackCut > 0 && twoTrackCut(track1, track2, bSign)) - continue; - - double values[6] = {0}; - - values[0] = track1.etam() - track2.etam(); - values[1] = track1.pt2(); - values[2] = track2.pt2(); - values[3] = 0; // collision.v0mult(); - - values[4] = track1.phim() - track2.phim(); - if (values[4] > 1.5 * TMath::Pi()) - values[4] -= TMath::TwoPi(); - if (values[4] < -0.5 * TMath::Pi()) - values[4] += TMath::TwoPi(); - - values[5] = collision.posZ(); - - same->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); - //mixed->getTrackHist()->Fill(values, CorrelationContainer::kCFStepReconstructed); - } - } - - template <typename T> - bool conversionCuts(T const& track1, T const& track2) - { - // skip if like sign - if (track1.charge() * track2.charge() > 0) - return false; - - bool decision = false; - - if (conversionCut(track1, track2, Photon, cfgPairCutPhoton)) - decision = true; - if (conversionCut(track1, track2, K0, cfgPairCutK0)) - decision = true; - if (conversionCut(track1, track2, Lambda, cfgPairCutLambda)) - decision = true; - if (conversionCut(track2, track1, Lambda, cfgPairCutLambda)) - decision = true; - if (conversionCut(track1, track2, Phi, cfgPairCutPhi)) - decision = true; - if (conversionCut(track1, track2, Rho, cfgPairCutRho)) - decision = true; - - return decision; - } - - template <typename T> - bool conversionCut(T const& track1, T const& track2, PairCuts conv, double cut) - { - //LOGF(info, "pt is %f %f", track1.pt2(), track2.pt2()); - - if (cut < 0) - return false; - - double massD1, massD2, massM; - - switch (conv) { - case Photon: - massD1 = 0.51e-3; - massD2 = 0.51e-3; - massM = 0; - break; - case K0: - massD1 = 0.1396; - massD2 = 0.1396; - massM = 0.4976; - break; - case Lambda: - massD1 = 0.9383; - massD2 = 0.1396; - massM = 1.115; - break; - case Phi: - massD1 = 0.4937; - massD2 = 0.4937; - massM = 1.019; - break; - case Rho: - massD1 = 0.1396; - massD2 = 0.1396; - massM = 0.770; - break; - } - - auto massC = getInvMassSquaredFast(track1, massD1, track2, massD2); - - if (TMath::Abs(massC - massM * massM) > cut * 5) - return false; - - massC = getInvMassSquared(track1, massD1, track2, massD2); - qa.mControlConvResoncances->Fill(static_cast<int>(conv), massC - massM * massM); - if (massC > (massM - cut) * (massM - cut) && massC < (massM + cut) * (massM + cut)) - return true; - - return false; - } - - template <typename T> - double getInvMassSquared(T const& track1, double m0_1, T const& track2, double m0_2) - { - // calculate inv mass squared - // same can be achieved, but with more computing time with - /*TLorentzVector photon, p1, p2; - p1.SetPtEtaPhiM(triggerParticle->Pt(), triggerEta, triggerParticle->Phi(), 0.510e-3); - p2.SetPtEtaPhiM(particle->Pt(), eta[j], particle->Phi(), 0.510e-3); - photon = p1+p2; - photon.M()*/ - - float tantheta1 = 1e10; - - if (track1.etam() < -1e-10 || track1.etam() > 1e-10) { - float expTmp = TMath::Exp(-track1.etam()); - tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); - } - - float tantheta2 = 1e10; - if (track2.etam() < -1e-10 || track2.etam() > 1e-10) { - float expTmp = TMath::Exp(-track2.etam()); - tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); - } - - float e1squ = m0_1 * m0_1 + track1.pt2() * track1.pt2() * (1.0 + 1.0 / tantheta1 / tantheta1); - float e2squ = m0_2 * m0_2 + track2.pt2() * track2.pt2() * (1.0 + 1.0 / tantheta2 / tantheta2); - - float mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (track1.ptm() * track2.ptm() * (TMath::Cos(track1.phim() - track2.phim()) + 1.0 / tantheta1 / tantheta2))); - - // Printf(Form("%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2)); - - return mass2; - } - - template <typename T> - double getInvMassSquaredFast(T const& track1, double m0_1, T const& track2, double m0_2) - { - // calculate inv mass squared approximately - - const float eta1 = track1.etam(); - const float eta2 = track2.etam(); - const float phi1 = track1.phim(); - const float phi2 = track2.phim(); - const float pt1 = track1.ptm(); - const float pt2 = track2.ptm(); - - float tantheta1 = 1e10; - - if (eta1 < -1e-10 || eta1 > 1e-10) { - float expTmp = 1.0 - eta1 + eta1 * eta1 / 2 - eta1 * eta1 * eta1 / 6 + eta1 * eta1 * eta1 * eta1 / 24; - tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); - } - - float tantheta2 = 1e10; - if (eta2 < -1e-10 || eta2 > 1e-10) { - float expTmp = 1.0 - eta2 + eta2 * eta2 / 2 - eta2 * eta2 * eta2 / 6 + eta2 * eta2 * eta2 * eta2 / 24; - tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); - } - - float e1squ = m0_1 * m0_1 + pt1 * pt1 * (1.0 + 1.0 / tantheta1 / tantheta1); - float e2squ = m0_2 * m0_2 + pt2 * pt2 * (1.0 + 1.0 / tantheta2 / tantheta2); - - // fold onto 0...pi - float deltaPhi = TMath::Abs(phi1 - phi2); - while (deltaPhi > TMath::TwoPi()) - deltaPhi -= TMath::TwoPi(); - if (deltaPhi > TMath::Pi()) - deltaPhi = TMath::TwoPi() - deltaPhi; - - float cosDeltaPhi = 0; - if (deltaPhi < TMath::Pi() / 3) - cosDeltaPhi = 1.0 - deltaPhi * deltaPhi / 2 + deltaPhi * deltaPhi * deltaPhi * deltaPhi / 24; - else if (deltaPhi < 2 * TMath::Pi() / 3) - cosDeltaPhi = -(deltaPhi - TMath::Pi() / 2) + 1.0 / 6 * TMath::Power((deltaPhi - TMath::Pi() / 2), 3); - else - cosDeltaPhi = -1.0 + 1.0 / 2.0 * (deltaPhi - TMath::Pi()) * (deltaPhi - TMath::Pi()) - 1.0 / 24.0 * TMath::Power(deltaPhi - TMath::Pi(), 4); - - double mass2 = m0_1 * m0_1 + m0_2 * m0_2 + 2 * (TMath::Sqrt(e1squ * e2squ) - (pt1 * pt2 * (cosDeltaPhi + 1.0 / tantheta1 / tantheta2))); - - // Printf(Form("%f %f %f %f %f %f %f %f %f", pt1, eta1, phi1, pt2, eta2, phi2, m0_1, m0_2, mass2)); - - return mass2; - } - - template <typename T> - bool twoTrackCut(T const& track1, T const& track2, int bSign) - { - // the variables & cuthave been developed by the HBT group - // see e.g. https://indico.cern.ch/materialDisplay.py?contribId=36&sessionId=6&materialId=slides&confId=142700 - - auto deta = track1.etam() - track2.etam(); - - // optimization - if (TMath::Abs(deta) < cfgTwoTrackCut * 2.5 * 3) { - // check first boundaries to see if is worth to loop and find the minimum - float dphistar1 = getDPhiStar(track1, track2, cfgTwoTrackCutMinRadius, bSign); - float dphistar2 = getDPhiStar(track1, track2, 2.5, bSign); - - const float kLimit = cfgTwoTrackCut * 3; - - if (TMath::Abs(dphistar1) < kLimit || TMath::Abs(dphistar2) < kLimit || dphistar1 * dphistar2 < 0) { - float dphistarminabs = 1e5; - float dphistarmin = 1e5; - for (Double_t rad = cfgTwoTrackCutMinRadius; rad < 2.51; rad += 0.01) { - float dphistar = getDPhiStar(track1, track2, rad, bSign); - - float dphistarabs = TMath::Abs(dphistar); - - if (dphistarabs < dphistarminabs) { - dphistarmin = dphistar; - dphistarminabs = dphistarabs; - } - } - - qa.mTwoTrackDistancePt[0]->Fill(deta, dphistarmin, TMath::Abs(track1.pt2() - track2.pt2())); - - if (dphistarminabs < cfgTwoTrackCut && TMath::Abs(deta) < cfgTwoTrackCut) { - //Printf("Removed track pair %ld %ld with %f %f %f %f %d %f %f %d %d", track1.index(), track2.index(), deta, dphistarminabs, track1.phi2(), track1.pt2(), track1.charge(), track2.phi2(), track2.pt2(), track2.charge(), bSign); - return true; - } - - qa.mTwoTrackDistancePt[1]->Fill(deta, dphistarmin, TMath::Abs(track1.pt2() - track2.pt2())); - } - } - - return false; - } - - template <typename T> - float getDPhiStar(T const& track1, T const& track2, float radius, float bSign) - { - // - // calculates dphistar - // - - auto phi1 = track1.phim(); - auto pt1 = track1.ptm(); - auto charge1 = track1.charge(); - - auto phi2 = track2.phim(); - auto pt2 = track2.ptm(); - auto charge2 = track2.charge(); - - float dphistar = phi1 - phi2 - charge1 * bSign * TMath::ASin(0.075 * radius / pt1) + charge2 * bSign * TMath::ASin(0.075 * radius / pt2); - - static const Double_t kPi = TMath::Pi(); - - if (dphistar > kPi) - dphistar = kPi * 2 - dphistar; - if (dphistar < -kPi) - dphistar = -kPi * 2 - dphistar; - if (dphistar > kPi) // might look funny but is needed - dphistar = kPi * 2 - dphistar; - - return dphistar; - } - - // template<typename... Ts> - // unsigned int getFilterBit(soa::Table<Ts...>::iterator const& track) - // { - // if constexpr(!has_type_v<aod::track::X, pack<Ts...>>) - // static_assert("Need to pass aod::track"); - // - // - // // LOGF(info, "pt %f", track1.pt2()); - // return false; - // } - - // float getInvMassSquared(float pt1, float eta1, float phi1, float pt2, float eta2, float phi2, float m0_1, float m0_2) -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<ATask>("produce-etaphi"), - adaptAnalysisTask<CorrelationTask>("correlation-task")}; -} diff --git a/Analysis/Tasks/emcalCorrectionTask.cxx b/Analysis/Tasks/emcalCorrectionTask.cxx new file mode 100644 index 0000000000000..dfba6072ae069 --- /dev/null +++ b/Analysis/Tasks/emcalCorrectionTask.cxx @@ -0,0 +1,198 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// EMCAL Correction Task +// +// Author: Raymond Ehlers + +#include <cmath> + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +#include "DetectorsBase/GeometryManager.h" + +#include "AnalysisDataModel/EMCALClusters.h" + +#include "DataFormatsEMCAL/Cell.h" +#include "DataFormatsEMCAL/Constants.h" +#include "DataFormatsEMCAL/AnalysisCluster.h" +#include "EMCALBase/Geometry.h" +#include "EMCALBase/ClusterFactory.h" +#include "EMCALReconstruction/Clusterizer.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct EmcalCorrectionTask { + Produces<o2::aod::EMCALClusters> clusters; + + // Options for the clusterization + // 1 corresponds to EMCAL cells based on the Run2 definition. + Configurable<int> selectedCellType{"selectedCellType", 1, "EMCAL Cell type"}; + Configurable<double> seedEnergy{"seedEnergy", 0.1, "Clusterizer seed energy."}; + Configurable<double> minCellEnergy{"minCellEnergy", 0.05, "Clusterizer minimum cell energy."}; + // TODO: Check this range, especially after change to the conversion... + Configurable<double> timeCut{"timeCut", 10000, "Cell time cut"}; + Configurable<double> timeMin{"timeMin", 0, "Min cell time"}; + Configurable<double> timeMax{"timeMax", 10000, "Max cell time"}; + Configurable<bool> enableEnergyGradientCut{"enableEnergyGradientCut", true, "Enable energy gradient cut."}; + Configurable<double> gradientCut{"gradientCut", 0.03, "Clusterizer energy gradient cut."}; + + // Clusterizer and related + // Apparently streaming these objects really doesn't work, and causes problems for setting up the workflow. + // So we use unique_ptr and define them below. + std::unique_ptr<o2::emcal::Clusterizer<o2::emcal::Cell>> mClusterizer; + std::unique_ptr<o2::emcal::ClusterFactory<o2::emcal::Cell>> mClusterFactory; + // Cells and clusters + std::vector<o2::emcal::Cell> mEmcalCells; + std::vector<o2::emcal::AnalysisCluster> mAnalysisClusters; + + // QA + // NOTE: This is not comprehensive. + OutputObj<TH1F> hCellE{"hCellE"}; + OutputObj<TH1I> hCellTowerID{"hCellTowerID"}; + OutputObj<TH2F> hCellEtaPhi{"hCellEtaPhi"}; + OutputObj<TH2I> hCellRowCol{"hCellRowCol"}; + OutputObj<TH1F> hClusterE{"hClusterE"}; + OutputObj<TH2F> hClusterEtaPhi{"hClusterEtaPhi"}; + + void init(InitContext const&) + { + LOG(DEBUG) << "Start init!"; + // NOTE: The geometry manager isn't necessary just to load the EMCAL geometry. + // However, it _is_ necessary for loading the misalignment matrices as of September 2020 + // Eventually, those matrices will be moved to the CCDB, but it's not yet ready. + // FIXME: Hardcoded for run 2 + o2::base::GeometryManager::loadGeometry(); // for generating full clusters + LOG(DEBUG) << "After load geometry!"; + o2::emcal::Geometry* geometry = o2::emcal::Geometry::GetInstanceFromRunNumber(223409); + if (!geometry) { + LOG(ERROR) << "Failure accessing geometry"; + } + + // Setup clusterizer + LOG(DEBUG) << "Init clusterizer!"; + mClusterizer = decltype(mClusterizer)(new o2::emcal::Clusterizer<o2::emcal::Cell>()); + mClusterizer->initialize(timeCut, timeMin, timeMax, gradientCut, enableEnergyGradientCut, seedEnergy, minCellEnergy); + mClusterizer->setGeometry(geometry); + LOG(DEBUG) << "Done with clusterizer. Setup cluster factory."; + // Setup cluster factory. + mClusterFactory = decltype(mClusterFactory)(new o2::emcal::ClusterFactory<o2::emcal::Cell>()); + LOG(DEBUG) << "Completed init!"; + + // Setup QA hists. + hCellE.setObject(new TH1F("hCellE", "hCellE", 200, 0.0, 100)); + hCellTowerID.setObject(new TH1I("hCellTowerID", "hCellTowerID", 20000, 0, 20000)); + hCellEtaPhi.setObject(new TH2F("hCellEtaPhi", "hCellEtaPhi", 160, -0.8, 0.8, 72, 0, 2 * 3.14159)); + // NOTE: Reversed column and row because it's more natural for presentatin. + hCellRowCol.setObject(new TH2I("hCellRowCol", "hCellRowCol;Column;Row", 97, 0, 97, 600, 0, 600)); + hClusterE.setObject(new TH1F("hClusterE", "hClusterE", 200, 0.0, 100)); + hClusterEtaPhi.setObject(new TH2F("hClusterEtaPhi", "hClusterEtaPhi", 160, -0.8, 0.8, 72, 0, 2 * 3.14159)); + } + + //void process(aod::Collision const& collision, soa::Filtered<aod::Tracks> const& fullTracks, aod::Calos const& cells) + //void process(aod::Collision const& collision, aod::Tracks const& tracks, aod::Calos const& cells) + //void process(aod::BCs const& bcs, aod::Collision const& collision, aod::Calos const& cells) + // Appears to need the BC to be accessed to be available in the collision table... + void process(aod::Collision const& collision, aod::Calos const& cells, aod::BCs const& bcs) + { + LOG(DEBUG) << "Starting process."; + // Convert aod::Calo to o2::emcal::Cell which can be used with the clusterizer. + // In particular, we need to filter only EMCAL cells. + mEmcalCells.clear(); + for (auto& cell : cells) { + if (cell.caloType() != selectedCellType || cell.bc() != collision.bc()) { + //LOG(DEBUG) << "Rejected"; + continue; + } + //LOG(DEBUG) << "Cell E: " << cell.getEnergy(); + //LOG(DEBUG) << "Cell E: " << cell; + + mEmcalCells.emplace_back(o2::emcal::Cell( + cell.cellNumber(), + cell.amplitude(), + cell.time(), + o2::emcal::intToChannelType(cell.cellType()))); + } + + // Cell QA + // For convenience, use the clusterizer stored geometry to get the eta-phi + for (auto& cell : mEmcalCells) { + hCellE->Fill(cell.getEnergy()); + hCellTowerID->Fill(cell.getTower()); + auto res = mClusterizer->getGeometry()->EtaPhiFromIndex(cell.getTower()); + hCellEtaPhi->Fill(std::get<0>(res), std::get<1>(res)); + res = mClusterizer->getGeometry()->GlobalRowColFromIndex(cell.getTower()); + // NOTE: Reversed column and row because it's more natural for presentatin. + hCellRowCol->Fill(std::get<1>(res), std::get<0>(res)); + } + + // TODO: Helpful for now, but should be removed. + LOG(DEBUG) << "Converted EMCAL cells"; + for (auto& cell : mEmcalCells) { + LOG(DEBUG) << cell.getTower() << ": E: " << cell.getEnergy() << ", time: " << cell.getTimeStamp() << ", type: " << cell.getType(); + } + + LOG(INFO) << "Converted cells. Contains: " << mEmcalCells.size() << ". Originally " << cells.size() << ". About to run clusterizer."; + + // Run the clusterizer + mClusterizer->findClusters(mEmcalCells); + LOG(DEBUG) << "Found clusters."; + auto emcalClusters = mClusterizer->getFoundClusters(); + auto emcalClustersInputIndices = mClusterizer->getFoundClustersInputIndices(); + LOG(DEBUG) << "Retrieved results. About to setup cluster factory."; + + // Convert to analysis clusters. + // First, the cluster factory requires cluster and cell information in order to build the clusters. + mAnalysisClusters.clear(); + mClusterFactory->reset(); + mClusterFactory->setClustersContainer(*emcalClusters); + mClusterFactory->setCellsContainer(mEmcalCells); + mClusterFactory->setCellsIndicesContainer(*emcalClustersInputIndices); + LOG(DEBUG) << "Cluster factory set up."; + + // Convert to analysis clusters. + for (int icl = 0; icl < mClusterFactory->getNumberOfClusters(); icl++) { + auto analysisCluster = mClusterFactory->buildCluster(icl); + mAnalysisClusters.emplace_back(analysisCluster); + } + LOG(DEBUG) << "Converted to analysis clusters."; + + // Store the clusters in the table + clusters.reserve(mAnalysisClusters.size()); + for (const auto& cluster : mAnalysisClusters) { + // Determine the cluster eta, phi, correcting for the vertex position. + auto pos = cluster.getGlobalPosition(); + pos = pos - math_utils::Point3D<float>{collision.posX(), collision.posY(), collision.posZ()}; + // Normalize the vector and rescale by energy. + pos /= (cluster.E() / std::sqrt(pos.Mag2())); + + // We have our necessary properties. Now we store outputs + //LOG(DEBUG) << "Cluster E: " << cluster.E(); + clusters(collision, cluster.E(), pos.Eta(), pos.Phi(), cluster.getM02()); + //if (cluster.E() < 0.300) { + // continue; + //} + hClusterE->Fill(cluster.E()); + hClusterEtaPhi->Fill(pos.Eta(), pos.Phi()); + } + LOG(DEBUG) << "Done with process."; + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<EmcalCorrectionTask>("emcal-correction-task")}; +} diff --git a/Analysis/Tasks/eventSelection.cxx b/Analysis/Tasks/eventSelection.cxx index 89fabe9b625ae..60b11a5729277 100644 --- a/Analysis/Tasks/eventSelection.cxx +++ b/Analysis/Tasks/eventSelection.cxx @@ -7,21 +7,26 @@ // In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Analysis/EventSelection.h" -#include "CCDB/CcdbApi.h" -#include "TFile.h" -#include "TTree.h" -#include <map> -using std::map; -using std::string; +#include "Framework/ConfigParamSpec.h" using namespace o2; using namespace o2::framework; +// custom configurable for switching between run2 and run3 selection types +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{"selection-run", VariantType::Int, 2, {"selection type: 2 - run 2, 3 - run 3"}}); +} + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisCore/TriggerAliases.h" +#include <CCDB/BasicCCDBManager.h> +#include "CommonConstants/LHCConstants.h" + struct EvSelParameters { // time-of-flight offset float fV0ADist = 329.00 / TMath::Ccgs() * 1e9; // ns @@ -54,126 +59,60 @@ struct EvSelParameters { float fZNABBupper = 2.0; // ns float fZNCBBlower = -2.0; // ns float fZNCBBupper = 2.0; // ns + + // TODO rough cuts to be adjusted + float fT0ABBlower = -2.0; // ns + float fT0ABBupper = 2.0; // ns + float fT0CBBlower = -2.0; // ns + float fT0CBBupper = 2.0; // ns }; struct EventSelectionTask { Produces<aod::EvSels> evsel; - map<string, int>* pClassNameToIndexMap; - map<int, string> mAliases; - map<int, vector<int>> mAliasToClassIds; - EvSelParameters par; + Service<o2::ccdb::BasicCCDBManager> ccdb; + Configurable<bool> isMC{"isMC", 0, "0 - data, 1 - MC"}; - aod::Run2V0 getVZero(aod::BC const& bc, aod::Run2V0s const& vzeros) - { - for (auto& vzero : vzeros) - if (vzero.bc() == bc) - return vzero; - aod::Run2V0 dummy; - return dummy; - } - - aod::Zdc getZdc(aod::BC const& bc, aod::Zdcs const& zdcs) - { - for (auto& zdc : zdcs) - if (zdc.bc() == bc) - return zdc; - aod::Zdc dummy; - return dummy; - } - - aod::FDD getFDD(aod::BC const& bc, aod::FDDs const& fdds) - { - for (auto& fdd : fdds) - if (fdd.bc() == bc) - return fdd; - aod::FDD dummy; - return dummy; - } - - // TODO create aliases elsewhere (like BrowseAndFill macro) - void createAliases() - { - mAliases[kINT7] = "CINT7-B-NOPF-CENT,CINT7-B-NOPF-CENTNOTRD"; - mAliases[kEMC7] = "CEMC7-B-NOPF-CENTNOPMD,CDMC7-B-NOPF-CENTNOPMD"; - mAliases[kINT7inMUON] = "CINT7-B-NOPF-MUFAST"; - mAliases[kMuonSingleLowPt7] = "CMSL7-B-NOPF-MUFAST"; - mAliases[kMuonUnlikeLowPt7] = "CMUL7-B-NOPF-MUFAST"; - mAliases[kMuonLikeLowPt7] = "CMLL7-B-NOPF-MUFAST"; - mAliases[kCUP8] = "CCUP8-B-NOPF-CENTNOTRD"; - mAliases[kCUP9] = "CCUP9-B-NOPF-CENTNOTRD"; - mAliases[kMUP10] = "CMUP10-B-NOPF-MUFAST"; - mAliases[kMUP11] = "CMUP11-B-NOPF-MUFAST"; - } + EvSelParameters par; void init(InitContext&) { - // TODO read run number from configurables - int run = 244918; - - // read ClassNameToIndexMap from ccdb - o2::ccdb::CcdbApi ccdb; - map<string, string> metadata; - ccdb.init("http://ccdb-test.cern.ch:8080"); - pClassNameToIndexMap = ccdb.retrieveFromTFileAny<map<string, int>>("Trigger/ClassNameToIndexMap", metadata, run); - - LOGF(debug, "List of trigger classes"); - for (auto& cl : *pClassNameToIndexMap) { - LOGF(debug, "class %02d %s", cl.second, cl.first); - } - - // TODO read aliases from CCDB - createAliases(); - - LOGF(debug, "Fill map of alias-to-class-indices"); - for (auto& al : mAliases) { - LOGF(debug, "alias classes: %s", al.second.data()); - TObjArray* tokens = TString(al.second).Tokenize(","); - for (int iClasses = 0; iClasses < tokens->GetEntriesFast(); iClasses++) { - string className = tokens->At(iClasses)->GetName(); - int index = (*pClassNameToIndexMap)[className] - 1; - if (index < 0 || index > 63) - continue; - mAliasToClassIds[al.first].push_back(index); - } - delete tokens; - } - - // TODO Fill EvSelParameters with configurable cuts from CCDB + ccdb->setURL("http://ccdb-test.cern.ch:8080"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); } - void process(aod::Collision const& collision, aod::BCs const& bcs, aod::Zdcs const& zdcs, aod::Run2V0s const& vzeros, aod::FDDs const& fdds) + void process(aod::Run2MatchedSparse::iterator const& collision, aod::BCsWithTimestamps const&, aod::Zdcs const& zdcs, aod::FV0As const& fv0as, aod::FV0Cs const& fv0cs, aod::FT0s const& ft0s, aod::FDDs const& fdds) { - LOGF(debug, "Starting new event"); - // CTP info - uint64_t triggerMask = collision.bc().triggerMask(); + auto bc = collision.bc_as<aod::BCsWithTimestamps>(); + LOGF(debug, "timestamp=%llu", bc.timestamp()); + TriggerAliases* aliases = ccdb->getForTimeStamp<TriggerAliases>("Trigger/TriggerAliases", bc.timestamp()); + if (!aliases) { + LOGF(fatal, "Trigger aliases are not available in CCDB for run=%d at timestamp=%llu", bc.runNumber(), bc.timestamp()); + } + uint64_t triggerMask = bc.triggerMask(); LOGF(debug, "triggerMask=%llu", triggerMask); + // fill fired aliases - int32_t alias[nAliases] = {0}; - for (auto& al : mAliasToClassIds) { + int32_t alias[kNaliases] = {0}; + for (auto& al : aliases->GetAliasToClassIdsMap()) { for (auto& classIndex : al.second) { alias[al.first] |= (triggerMask & (1ull << classIndex)) > 0; } } - // for (int i=0;i<64;i++) printf("%i",(triggerMask & (1ull << i)) > 0); printf("\n"); - // for (int i=0;i<nAliases;i++) printf("%i ",alias[i]); printf("\n"); - - // ZDC info - auto zdc = getZdc(collision.bc(), zdcs); - float timeZNA = zdc.timeZNA(); - float timeZNC = zdc.timeZNC(); - // VZERO info - auto vzero = getVZero(collision.bc(), vzeros); - float timeV0A = vzero.timeA(); - float timeV0C = vzero.timeC(); - // FDD info - auto fdd = getFDD(collision.bc(), fdds); - float timeFDA = fdd.timeA(); - float timeFDC = fdd.timeC(); + float timeZNA = collision.has_zdc() ? collision.zdc().timeZNA() : -999.f; + float timeZNC = collision.has_zdc() ? collision.zdc().timeZNC() : -999.f; + float timeV0A = collision.has_fv0a() ? collision.fv0a().time() : -999.f; + float timeV0C = collision.has_fv0c() ? collision.fv0c().time() : -999.f; + float timeT0A = collision.has_ft0() ? collision.ft0().timeA() : -999.f; + float timeT0C = collision.has_ft0() ? collision.ft0().timeC() : -999.f; + float timeFDA = collision.has_fdd() ? collision.fdd().timeA() : -999.f; + float timeFDC = collision.has_fdd() ? collision.fdd().timeC() : -999.f; LOGF(debug, "timeZNA=%f timeZNC=%f", timeZNA, timeZNC); LOGF(debug, "timeV0A=%f timeV0C=%f", timeV0A, timeV0C); LOGF(debug, "timeFDA=%f timeFDC=%f", timeFDA, timeFDC); + LOGF(debug, "timeT0A=%f timeT0C=%f", timeT0A, timeT0C); bool bbZNA = timeZNA > par.fZNABBlower && timeZNA < par.fZNABBupper; bool bbZNC = timeZNC > par.fZNCBBlower && timeZNC < par.fZNCBBupper; @@ -185,14 +124,93 @@ struct EventSelectionTask { bool bbFDC = timeFDC > par.fFDCBBlower && timeFDC < par.fFDCBBupper; bool bgFDA = timeFDA > par.fFDABGlower && timeFDA < par.fFDABGupper; bool bgFDC = timeFDC > par.fFDCBGlower && timeFDC < par.fFDCBGupper; + bool bbT0A = timeT0A > par.fT0ABBlower && timeT0A < par.fT0ABBupper; + bool bbT0C = timeT0C > par.fT0CBBlower && timeT0C < par.fT0CBBupper; + + if (isMC) { + bbZNA = 1; + bbZNC = 1; + } + + // Fill event selection columns + int64_t foundFT0 = -1; // this column is not used in run2 analysis + evsel(alias, bbT0A, bbT0C, bbV0A, bbV0C, bgV0A, bgV0C, bbZNA, bbZNC, bbFDA, bbFDC, bgFDA, bgFDC, foundFT0); + } +}; + +struct EventSelectionTaskRun3 { + Produces<aod::EvSels> evsel; + + EvSelParameters par; + void process(aod::Collision const& collision, soa::Join<aod::BCs, aod::Run3MatchedToBCSparse> const& bct0s, aod::Zdcs const& zdcs, aod::FV0As const& fv0as, aod::FT0s const& ft0s, aod::FDDs const& fdds) + { + int64_t ft0Dist; + int64_t foundFT0 = -1; + float timeA = -999.f; + float timeC = -999.f; + + auto bcIter = collision.bc_as<soa::Join<aod::BCs, aod::Run3MatchedToBCSparse>>(); + + uint64_t apprBC = bcIter.globalBC(); + uint64_t meanBC = apprBC - std::lround(collision.collisionTime() / o2::constants::lhc::LHCBunchSpacingNS); + int deltaBC = std::ceil(collision.collisionTimeRes() / o2::constants::lhc::LHCBunchSpacingNS * 4); + + int moveCount = 0; + while (bcIter != bct0s.end() && bcIter.globalBC() <= meanBC + deltaBC && bcIter.globalBC() >= meanBC - deltaBC) { + if (bcIter.has_ft0()) { + ft0Dist = bcIter.globalBC() - meanBC; + foundFT0 = bcIter.ft0().globalIndex(); + break; + } + ++bcIter; + ++moveCount; + } + + bcIter.moveByIndex(-moveCount); + while (bcIter != bct0s.begin() && bcIter.globalBC() <= meanBC + deltaBC && bcIter.globalBC() >= meanBC - deltaBC) { + --bcIter; + if (bcIter.has_ft0() && (meanBC - bcIter.globalBC()) < ft0Dist) { + foundFT0 = bcIter.ft0().globalIndex(); + break; + } + if ((meanBC - bcIter.globalBC()) >= ft0Dist) { + break; + } + } + + if (foundFT0 != -1) { + auto ft0 = ft0s.iteratorAt(foundFT0); + timeA = ft0.timeA(); + timeC = ft0.timeC(); + } + + bool bbZNA = 1; + bool bbZNC = 1; + bool bbV0A = 0; + bool bbV0C = 0; + bool bgV0A = 0; + bool bgV0C = 0; + bool bbFDA = 0; + bool bbFDC = 0; + bool bgFDA = 0; + bool bgFDC = 0; + bool bbT0A = timeA > par.fT0ABBlower && timeA < par.fT0ABBupper; + bool bbT0C = timeC > par.fT0CBBlower && timeC < par.fT0CBBupper; + + int32_t alias[kNaliases] = {0}; // Fill event selection columns - evsel(alias, bbV0A, bbV0C, bgV0A, bgV0C, bbZNA, bbZNC, bbFDA, bbFDC, bgFDA, bgFDC); + // saving FT0 row index (foundFT0) for further analysis + evsel(alias, bbT0A, bbT0C, bbV0A, bbV0C, bgV0A, bgV0C, bbZNA, bbZNC, bbFDA, bbFDC, bgFDA, bgFDC, foundFT0); } }; -WorkflowSpec defineDataProcessing(ConfigContext const&) +WorkflowSpec defineDataProcessing(ConfigContext const& ctx) { - return WorkflowSpec{ - adaptAnalysisTask<EventSelectionTask>("event-selection")}; -} + if (ctx.options().get<int>("selection-run") == 2) { + return WorkflowSpec{adaptAnalysisTask<EventSelectionTask>("event-selection")}; + } else { + return WorkflowSpec{ + adaptAnalysisTask<EventSelectionTaskRun3>("event-selection")}; + } +} \ No newline at end of file diff --git a/Analysis/Tasks/eventSelectionQa.cxx b/Analysis/Tasks/eventSelectionQa.cxx index 599a235c6b90f..76eaeedf9fea6 100644 --- a/Analysis/Tasks/eventSelectionQa.cxx +++ b/Analysis/Tasks/eventSelectionQa.cxx @@ -10,35 +10,63 @@ #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/EventSelection.h" +#include "AnalysisDataModel/EventSelection.h" using namespace o2; using namespace o2::framework; struct EventSelectionTask { - aod::Run2V0 getVZero(aod::BC const& bc, aod::Run2V0s const& vzeros) + aod::FV0A getVZeroA(aod::BC const& bc, aod::FV0As const& vzeros) { - for (auto& vzero : vzeros) - if (vzero.bc() == bc) + for (auto& vzero : vzeros) { + if (vzero.bc() == bc) { return vzero; - aod::Run2V0 dummy; + } + } + aod::FV0A dummy; + return dummy; + } + + aod::FV0C getVZeroC(aod::BC const& bc, aod::FV0Cs const& vzeros) + { + for (auto& vzero : vzeros) { + if (vzero.bc() == bc) { + return vzero; + } + } + aod::FV0C dummy; return dummy; } aod::Zdc getZdc(aod::BC const& bc, aod::Zdcs const& zdcs) { - for (auto& zdc : zdcs) - if (zdc.bc() == bc) + for (auto& zdc : zdcs) { + if (zdc.bc() == bc) { return zdc; + } + } aod::Zdc dummy; return dummy; } + aod::FT0 getFT0(aod::BC const& bc, aod::FT0s const& ft0s) + { + for (auto& ft0 : ft0s) { + if (ft0.bc() == bc) { + return ft0; + } + } + aod::FT0 dummy; + return dummy; + } + aod::FDD getFDD(aod::BC const& bc, aod::FDDs const& fdds) { - for (auto& fdd : fdds) - if (fdd.bc() == bc) + for (auto& fdd : fdds) { + if (fdd.bc() == bc) { return fdd; + } + } aod::FDD dummy; return dummy; } @@ -47,39 +75,63 @@ struct EventSelectionTask { OutputObj<TH1F> hTimeV0Call{TH1F("hTimeV0Call", "", 200, -50., 50.)}; OutputObj<TH1F> hTimeZNAall{TH1F("hTimeZNAall", "", 250, -25., 25.)}; OutputObj<TH1F> hTimeZNCall{TH1F("hTimeZNCall", "", 250, -25., 25.)}; + OutputObj<TH1F> hTimeT0Aall{TH1F("hTimeT0Aall", "", 200, -10., 10.)}; + OutputObj<TH1F> hTimeT0Call{TH1F("hTimeT0Call", "", 200, -10., 10.)}; OutputObj<TH1F> hTimeFDAall{TH1F("hTimeFDAall", "", 1000, -100., 100.)}; OutputObj<TH1F> hTimeFDCall{TH1F("hTimeFDCall", "", 1000, -100., 100.)}; OutputObj<TH1F> hTimeV0Aacc{TH1F("hTimeV0Aacc", "", 200, -50., 50.)}; OutputObj<TH1F> hTimeV0Cacc{TH1F("hTimeV0Cacc", "", 200, -50., 50.)}; OutputObj<TH1F> hTimeZNAacc{TH1F("hTimeZNAacc", "", 250, -25., 25.)}; OutputObj<TH1F> hTimeZNCacc{TH1F("hTimeZNCacc", "", 250, -25., 25.)}; + OutputObj<TH1F> hTimeT0Aacc{TH1F("hTimeT0Aacc", "", 200, -10., 10.)}; + OutputObj<TH1F> hTimeT0Cacc{TH1F("hTimeT0Cacc", "", 200, -10., 10.)}; OutputObj<TH1F> hTimeFDAacc{TH1F("hTimeFDAacc", "", 1000, -100., 100.)}; OutputObj<TH1F> hTimeFDCacc{TH1F("hTimeFDCacc", "", 1000, -100., 100.)}; - void process(soa::Join<aod::Collisions, aod::EvSels>::iterator const& col, aod::BCs const& bcs, aod::Zdcs const& zdcs, aod::Run2V0s const& vzeros, aod::FDDs fdds) + Configurable<bool> isMC{"isMC", 0, "0 - data, 1 - MC"}; + Configurable<int> selection{"sel", 7, "trigger: 7 - sel7, 8 - sel8"}; + + void process(soa::Join<aod::Collisions, aod::EvSels>::iterator const& col, aod::BCs const& bcs, aod::Zdcs const& zdcs, aod::FV0As const& fv0as, aod::FV0Cs const& fv0cs, aod::FT0s ft0s, aod::FDDs fdds) { - if (!col.alias()[kINT7]) + if (!isMC && !col.alias()[kINT7]) { return; + } - auto vzero = getVZero(col.bc(), vzeros); - hTimeV0Aall->Fill(vzero.timeA()); - hTimeV0Call->Fill(vzero.timeC()); + auto fv0a = getVZeroA(col.bc(), fv0as); + auto fv0c = getVZeroC(col.bc(), fv0cs); + hTimeV0Aall->Fill(fv0a.time()); + hTimeV0Call->Fill(fv0c.time()); auto zdc = getZdc(col.bc(), zdcs); hTimeZNAall->Fill(zdc.timeZNA()); hTimeZNCall->Fill(zdc.timeZNC()); + auto ft0 = getFT0(col.bc(), ft0s); + hTimeT0Aall->Fill(ft0.timeA()); + hTimeT0Call->Fill(ft0.timeC()); + auto fdd = getFDD(col.bc(), fdds); hTimeFDAall->Fill(fdd.timeA()); hTimeFDCall->Fill(fdd.timeC()); - if (!col.sel7()) + if (selection == 7 && !col.sel7()) { return; + } + + if (selection == 8 && !col.sel8()) { + return; + } + + if (selection != 7 && selection != 8) { + LOGF(fatal, "Unknown selection type! Use `--sel 7` or `--sel 8`"); + } - hTimeV0Aacc->Fill(vzero.timeA()); - hTimeV0Cacc->Fill(vzero.timeC()); + hTimeV0Aacc->Fill(fv0a.time()); + hTimeV0Cacc->Fill(fv0c.time()); hTimeZNAacc->Fill(zdc.timeZNA()); hTimeZNCacc->Fill(zdc.timeZNC()); + hTimeT0Aacc->Fill(ft0.timeA()); + hTimeT0Cacc->Fill(ft0.timeC()); hTimeFDAacc->Fill(fdd.timeA()); hTimeFDCacc->Fill(fdd.timeC()); } diff --git a/Analysis/Tasks/hfcandidatecreator2prong.cxx b/Analysis/Tasks/hfcandidatecreator2prong.cxx deleted file mode 100644 index 1ec0575169b9a..0000000000000 --- a/Analysis/Tasks/hfcandidatecreator2prong.cxx +++ /dev/null @@ -1,120 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/SecondaryVertexHF.h" -#include "DetectorsVertexing/DCAFitterN.h" -#include "ReconstructionDataFormats/Track.h" -#include "Analysis/RecoDecay.h" - -#include <TFile.h> -#include <TH1F.h> -#include <cmath> -#include <array> -#include <cstdlib> - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using std::array; - -struct HFCandidateCreator2Prong { - Produces<aod::HfCandProng2> hfcandprong2; - Configurable<bool> b_dovalplots{"b_dovalplots", true, "do validation plots"}; - OutputObj<TH1F> hvtx_x_out{TH1F("hvtx_x", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hvtx_y_out{TH1F("hvtx_y", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hvtx_z_out{TH1F("hvtx_z", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hmass2{TH1F("hmass2", "2-track inv mass", 500, 0, 5.0)}; - Configurable<double> d_bz{"d_bz", 5.0, "bz field"}; - Configurable<bool> b_propdca{"b_propdca", true, - "create tracks version propagated to PCA"}; - Configurable<double> d_maxr{"d_maxr", 200, "reject PCA's above this radius"}; - Configurable<double> d_maxdzini{"d_maxdzini", 4, - "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; - Configurable<double> d_minparamchange{"d_minparamchange", 1e-3, - "stop iterations if largest change of any X is smaller than this"}; - Configurable<double> d_minrelchi2change{"d_minrelchi2change", 0.9, - "stop iterations is chi2/chi2old > this"}; - void process(aod::Collision const& collision, - aod::HfTrackIndexProng2 const& hftrackindexprong2s, - soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra> const& tracks) - { - o2::vertexing::DCAFitterN<2> df; - df.setBz(d_bz); - df.setPropagateToPCA(b_propdca); - df.setMaxR(d_maxr); - df.setMaxDZIni(d_maxdzini); - df.setMinParamChange(d_minparamchange); - df.setMinRelChi2Change(d_minrelchi2change); - - for (auto& hfpr2 : hftrackindexprong2s) { - float x_p1 = hfpr2.index0().x(); - float alpha_p1 = hfpr2.index0().alpha(); - std::array<float, 5> arraypar_p1 = {hfpr2.index0().y(), hfpr2.index0().z(), hfpr2.index0().snp(), - hfpr2.index0().tgl(), hfpr2.index0().signed1Pt()}; - std::array<float, 15> covpar_p1 = {hfpr2.index0().cYY(), hfpr2.index0().cZY(), hfpr2.index0().cZZ(), - hfpr2.index0().cSnpY(), hfpr2.index0().cSnpZ(), - hfpr2.index0().cSnpSnp(), hfpr2.index0().cTglY(), hfpr2.index0().cTglZ(), - hfpr2.index0().cTglSnp(), hfpr2.index0().cTglTgl(), - hfpr2.index0().c1PtY(), hfpr2.index0().c1PtZ(), hfpr2.index0().c1PtSnp(), - hfpr2.index0().c1PtTgl(), hfpr2.index0().c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar_p1(x_p1, alpha_p1, arraypar_p1, covpar_p1); - - float x_n1 = hfpr2.index1().x(); - float alpha_n1 = hfpr2.index1().alpha(); - std::array<float, 5> arraypar_n1 = {hfpr2.index1().y(), hfpr2.index1().z(), hfpr2.index1().snp(), - hfpr2.index1().tgl(), hfpr2.index1().signed1Pt()}; - std::array<float, 15> covpar_n1 = {hfpr2.index1().cYY(), hfpr2.index1().cZY(), hfpr2.index1().cZZ(), - hfpr2.index1().cSnpY(), hfpr2.index1().cSnpZ(), - hfpr2.index1().cSnpSnp(), hfpr2.index1().cTglY(), hfpr2.index1().cTglZ(), - hfpr2.index1().cTglSnp(), hfpr2.index1().cTglTgl(), - hfpr2.index1().c1PtY(), hfpr2.index1().c1PtZ(), hfpr2.index1().c1PtSnp(), - hfpr2.index1().c1PtTgl(), hfpr2.index1().c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar_n1(x_n1, alpha_n1, arraypar_n1, covpar_n1); - df.setUseAbsDCA(true); - int nCand = df.process(trackparvar_p1, trackparvar_n1); - if (nCand == 0) - continue; - const auto& vtx = df.getPCACandidate(); - std::array<float, 3> pvec0; - std::array<float, 3> pvec1; - df.getTrack(0).getPxPyPzGlo(pvec0); - df.getTrack(1).getPxPyPzGlo(pvec1); - float masspion = 0.140; - float masskaon = 0.494; - float mass_ = sqrt(invmass2prongs2(pvec0[0], pvec0[1], - pvec0[2], masspion, - pvec1[0], pvec1[1], - pvec1[2], masskaon)); - float masssw_ = sqrt(invmass2prongs2(pvec0[0], pvec0[1], - pvec0[2], masskaon, - pvec1[0], pvec1[1], - pvec1[2], masspion)); - hfcandprong2(collision.posX(), collision.posY(), collision.posZ(), - pvec0[0], pvec0[1], pvec0[2], pvec1[0], pvec1[1], pvec1[2], - vtx[0], vtx[1], vtx[2], mass_, masssw_); - if (b_dovalplots == true) { - hvtx_x_out->Fill(vtx[0]); - hvtx_y_out->Fill(vtx[1]); - hvtx_z_out->Fill(vtx[2]); - hmass2->Fill(mass_); - hmass2->Fill(masssw_); - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<HFCandidateCreator2Prong>("vertexerhf-hfcandcreator2prong")}; -} diff --git a/Analysis/Tasks/hftrackindexskimscreator.cxx b/Analysis/Tasks/hftrackindexskimscreator.cxx deleted file mode 100644 index c7208ee46ef86..0000000000000 --- a/Analysis/Tasks/hftrackindexskimscreator.cxx +++ /dev/null @@ -1,310 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/SecondaryVertexHF.h" -#include "DetectorsVertexing/DCAFitterN.h" -#include "ReconstructionDataFormats/Track.h" -#include "Analysis/RecoDecay.h" - -#include <TFile.h> -#include <TH1F.h> -#include <cmath> -#include <array> -#include <cstdlib> - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using std::array; - -namespace o2::aod -{ -namespace seltrack -{ -DECLARE_SOA_COLUMN(IsSel, issel, int); -DECLARE_SOA_COLUMN(DCAPrim0, dcaprim0, float); -DECLARE_SOA_COLUMN(DCAPrim1, dcaprim1, float); -} // namespace seltrack -DECLARE_SOA_TABLE(SelTrack, "AOD", "SELTRACK", seltrack::IsSel, seltrack::DCAPrim0, - seltrack::DCAPrim1); -} // namespace o2::aod - -struct SelectTracks { - Produces<aod::SelTrack> seltrack; - Configurable<double> ptmintrack{"ptmintrack", -1, "ptmin single track"}; - Configurable<double> dcatoprimxymin{"dcatoprimxymin", 0, "dca xy to prim vtx min"}; - Configurable<int> d_tpcnclsfound{"d_tpcnclsfound", 70, "min number of tpc cls >="}; - Configurable<double> d_bz{"d_bz", 5.0, "bz field"}; - Configurable<bool> b_dovalplots{"b_dovalplots", true, "do validation plots"}; - OutputObj<TH1F> hpt_nocuts{TH1F("hpt_nocuts", "pt tracks (#GeV)", 100, 0., 10.)}; - OutputObj<TH1F> hpt_cuts{TH1F("hpt_cuts", "pt tracks (#GeV)", 100, 0., 10.)}; - OutputObj<TH1F> hdcatoprimxy_cuts{TH1F("hdcatoprimxy_cuts", "dca xy to prim. vertex (cm)", 100, -1.0, 1.0)}; - - void process(aod::Collision const& collision, - soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra> const& tracks) - { - Point3D<float> vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); - for (auto it0 = tracks.begin(); it0 != tracks.end(); ++it0) { - auto& track_0 = *it0; - int status = 1; - if (b_dovalplots == true) - hpt_nocuts->Fill(track_0.pt()); - if (track_0.pt() < ptmintrack) - status = 0; - UChar_t clustermap_0 = track_0.itsClusterMap(); - bool isselected_0 = track_0.tpcNClsFound() >= d_tpcnclsfound && track_0.flags() & 0x4; - isselected_0 = isselected_0 && (TESTBIT(clustermap_0, 0) || TESTBIT(clustermap_0, 1)); - if (!isselected_0) - status = 0; - array<float, 2> dca; - float x0_ = track_0.x(); - float alpha0_ = track_0.alpha(); - std::array<float, 5> arraypar0 = {track_0.y(), track_0.z(), track_0.snp(), - track_0.tgl(), track_0.signed1Pt()}; - std::array<float, 15> covpar0 = {track_0.cYY(), track_0.cZY(), track_0.cZZ(), - track_0.cSnpY(), track_0.cSnpZ(), - track_0.cSnpSnp(), track_0.cTglY(), track_0.cTglZ(), - track_0.cTglSnp(), track_0.cTglTgl(), - track_0.c1PtY(), track_0.c1PtZ(), track_0.c1PtSnp(), - track_0.c1PtTgl(), track_0.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar0(x0_, alpha0_, arraypar0, covpar0); - trackparvar0.propagateParamToDCA(vtxXYZ, d_bz, &dca); - if (abs(dca[0]) < dcatoprimxymin) - status = 0; - if (b_dovalplots == true) { - if (status == 1) { - hpt_cuts->Fill(track_0.pt()); - hdcatoprimxy_cuts->Fill(dca[0]); - } - } - seltrack(status, dca[0], dca[1]); - } - } -}; - -struct HFTrackIndexSkimsCreator { - float masspion = 0.140; - float masskaon = 0.494; - OutputObj<TH1F> hmass2{TH1F("hmass2", "; Inv Mass (GeV/c^{2})", 500, 0, 5.0)}; - OutputObj<TH1F> hmass3{TH1F("hmass3", "; Inv Mass (GeV/c^{2})", 500, 0, 5.0)}; - Produces<aod::HfTrackIndexProng2> hftrackindexprong2; - Produces<aod::HfTrackIndexProng3> hftrackindexprong3; - Configurable<int> triggerindex{"triggerindex", -1, "trigger index"}; - Configurable<int> do3prong{"do3prong", 0, "do 3 prong"}; - Configurable<double> d_bz{"d_bz", 5.0, "bz field"}; - Configurable<bool> b_propdca{"b_propdca", true, - "create tracks version propagated to PCA"}; - Configurable<double> d_maxr{"d_maxr", 200, "reject PCA's above this radius"}; - Configurable<double> d_maxdzini{"d_maxdzini", 4, - "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; - Configurable<double> d_minparamchange{"d_minparamchange", 1e-3, - "stop iterations if largest change of any X is smaller than this"}; - Configurable<double> d_minrelchi2change{"d_minrelchi2change", 0.9, - "stop iterations is chi2/chi2old > this"}; - Configurable<double> d_minmassDp{"d_minmassDp", 1.5, "min mass dplus presel"}; - Configurable<double> d_maxmassDp{"d_maxmassDp", 2.1, "max mass dplus presel"}; - Configurable<bool> b_dovalplots{"b_dovalplots", true, "do validation plots"}; - Filter seltrack = (aod::seltrack::issel == 1); - - void process(aod::Collision const& collision, - aod::BCs const& bcs, - soa::Filtered<soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra, aod::SelTrack>> const& tracks) - { - int trigindex = int{triggerindex}; - if (trigindex != -1) { - //LOGF(info, "Selecting on trigger bit %d", trigindex); - uint64_t triggerMask = collision.bc().triggerMask(); - bool isTriggerClassFired = triggerMask & 1ul << (trigindex - 1); - if (!isTriggerClassFired) - return; - } - - LOGF(info, "N. of Tracks for collision: %d", tracks.size()); - o2::vertexing::DCAFitterN<2> df; - df.setBz(d_bz); - df.setPropagateToPCA(b_propdca); - df.setMaxR(d_maxr); - df.setMaxDZIni(d_maxdzini); - df.setMinParamChange(d_minparamchange); - df.setMinRelChi2Change(d_minrelchi2change); - - o2::vertexing::DCAFitterN<3> df3; - df3.setBz(d_bz); - df3.setPropagateToPCA(b_propdca); - df3.setMaxR(d_maxr); - df3.setMaxDZIni(d_maxdzini); - df3.setMinParamChange(d_minparamchange); - df3.setMinRelChi2Change(d_minrelchi2change); - - for (auto i_p1 = tracks.begin(); i_p1 != tracks.end(); ++i_p1) { - auto& track_p1 = *i_p1; - if (track_p1.signed1Pt() < 0) - continue; - float x_p1 = track_p1.x(); - float alpha_p1 = track_p1.alpha(); - std::array<float, 5> arraypar_p1 = {track_p1.y(), track_p1.z(), track_p1.snp(), - track_p1.tgl(), track_p1.signed1Pt()}; - std::array<float, 15> covpar_p1 = {track_p1.cYY(), track_p1.cZY(), track_p1.cZZ(), - track_p1.cSnpY(), track_p1.cSnpZ(), - track_p1.cSnpSnp(), track_p1.cTglY(), track_p1.cTglZ(), - track_p1.cTglSnp(), track_p1.cTglTgl(), - track_p1.c1PtY(), track_p1.c1PtZ(), track_p1.c1PtSnp(), - track_p1.c1PtTgl(), track_p1.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar_p1(x_p1, alpha_p1, arraypar_p1, covpar_p1); - for (auto i_n1 = tracks.begin(); i_n1 != tracks.end(); ++i_n1) { - auto& track_n1 = *i_n1; - if (track_n1.signed1Pt() > 0) - continue; - float x_n1 = track_n1.x(); - float alpha_n1 = track_n1.alpha(); - std::array<float, 5> arraypar_n1 = {track_n1.y(), track_n1.z(), track_n1.snp(), - track_n1.tgl(), track_n1.signed1Pt()}; - std::array<float, 15> covpar_n1 = {track_n1.cYY(), track_n1.cZY(), track_n1.cZZ(), - track_n1.cSnpY(), track_n1.cSnpZ(), - track_n1.cSnpSnp(), track_n1.cTglY(), track_n1.cTglZ(), - track_n1.cTglSnp(), track_n1.cTglTgl(), - track_n1.c1PtY(), track_n1.c1PtZ(), track_n1.c1PtSnp(), - track_n1.c1PtTgl(), track_n1.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar_n1(x_n1, alpha_n1, arraypar_n1, covpar_n1); - df.setUseAbsDCA(true); - int nCand = df.process(trackparvar_p1, trackparvar_n1); - if (nCand == 0) - continue; - const auto& vtx = df.getPCACandidate(); - std::array<float, 3> pvec0; - std::array<float, 3> pvec1; - df.getTrack(0).getPxPyPzGlo(pvec0); - df.getTrack(1).getPxPyPzGlo(pvec1); - float mass_ = sqrt(invmass2prongs2(pvec0[0], pvec0[1], - pvec0[2], masspion, - pvec1[0], pvec1[1], - pvec1[2], masskaon)); - float masssw_ = sqrt(invmass2prongs2(pvec0[0], pvec0[1], - pvec0[2], masskaon, - pvec1[0], pvec1[1], - pvec1[2], masspion)); - if (b_dovalplots == true) { - hmass2->Fill(mass_); - hmass2->Fill(masssw_); - } - hftrackindexprong2(track_p1.collisionId(), - track_p1.globalIndex(), - track_n1.globalIndex(), 1); - if (do3prong == 1) { - //second loop on positive tracks - for (auto i_p2 = i_p1 + 1; i_p2 != tracks.end(); ++i_p2) { - auto& track_p2 = *i_p2; - if (track_p2.signed1Pt() < 0) - continue; - float x_p2 = track_p2.x(); - float alpha_p2 = track_p2.alpha(); - double mass3prong2 = invmass3prongs2(track_p1.px(), track_p1.py(), track_p1.pz(), masspion, - track_n1.px(), track_n1.py(), track_n1.pz(), masskaon, - track_p2.px(), track_p2.py(), track_p2.pz(), masspion); - if (mass3prong2 < d_minmassDp * d_minmassDp || mass3prong2 > d_maxmassDp * d_maxmassDp) - continue; - if (b_dovalplots == true) - hmass3->Fill(sqrt(mass3prong2)); - std::array<float, 5> arraypar_p2 = {track_p2.y(), track_p2.z(), track_p2.snp(), - track_p2.tgl(), track_p2.signed1Pt()}; - std::array<float, 15> covpar_p2 = {track_p2.cYY(), track_p2.cZY(), track_p2.cZZ(), - track_p2.cSnpY(), track_p2.cSnpZ(), - track_p2.cSnpSnp(), track_p2.cTglY(), track_p2.cTglZ(), - track_p2.cTglSnp(), track_p2.cTglTgl(), - track_p2.c1PtY(), track_p2.c1PtZ(), track_p2.c1PtSnp(), - track_p2.c1PtTgl(), track_p2.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar_p2(x_p2, alpha_p2, arraypar_p2, covpar_p2); - df3.setUseAbsDCA(true); - int nCand3 = df3.process(trackparvar_p1, trackparvar_n1, trackparvar_p2); - if (nCand3 == 0) - continue; - const auto& vtx3 = df3.getPCACandidate(); - std::array<float, 3> pvec0; - std::array<float, 3> pvec1; - std::array<float, 3> pvec2; - df.getTrack(0).getPxPyPzGlo(pvec0); - df.getTrack(1).getPxPyPzGlo(pvec1); - df.getTrack(2).getPxPyPzGlo(pvec2); - float mass_ = sqrt(invmass3prongs2(pvec0[0], pvec0[1], - pvec0[2], masspion, - pvec1[0], pvec1[1], - pvec1[2], masskaon, - pvec2[0], pvec2[1], - pvec2[2], masspion)); - if (b_dovalplots == true) { - hmass3->Fill(mass_); - } - hftrackindexprong3(track_p1.collisionId(), - track_p1.globalIndex(), - track_n1.globalIndex(), - track_p1.globalIndex(), 2); - } - //second loop on negative tracks - for (auto i_n2 = i_n1 + 1; i_n2 != tracks.end(); ++i_n2) { - auto& track_n2 = *i_n2; - if (track_n2.signed1Pt() > 0) - continue; - float x_n2 = track_n2.x(); - float alpha_n2 = track_n2.alpha(); - double mass3prong2 = invmass3prongs2(track_n1.px(), track_n1.py(), track_n1.pz(), masspion, - track_p1.px(), track_p1.py(), track_p1.pz(), masskaon, - track_n2.px(), track_n2.py(), track_n2.pz(), masspion); - if (mass3prong2 < d_minmassDp * d_minmassDp || mass3prong2 > d_maxmassDp * d_maxmassDp) - continue; - hmass3->Fill(sqrt(mass3prong2)); - std::array<float, 5> arraypar_n2 = {track_n2.y(), track_n2.z(), track_n2.snp(), - track_n2.tgl(), track_n2.signed1Pt()}; - std::array<float, 15> covpar_n2 = {track_n2.cYY(), track_n2.cZY(), track_n2.cZZ(), - track_n2.cSnpY(), track_n2.cSnpZ(), - track_n2.cSnpSnp(), track_n2.cTglY(), track_n2.cTglZ(), - track_n2.cTglSnp(), track_n2.cTglTgl(), - track_n2.c1PtY(), track_n2.c1PtZ(), track_n2.c1PtSnp(), - track_n2.c1PtTgl(), track_n2.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar_n2(x_n2, alpha_n2, arraypar_n2, covpar_n2); - df3.setUseAbsDCA(true); - int nCand3 = df3.process(trackparvar_n1, trackparvar_p1, trackparvar_n2); - if (nCand3 == 0) - continue; - const auto& vtx3 = df3.getPCACandidate(); - std::array<float, 3> pvec0; - std::array<float, 3> pvec1; - std::array<float, 3> pvec2; - df.getTrack(0).getPxPyPzGlo(pvec0); - df.getTrack(1).getPxPyPzGlo(pvec1); - df.getTrack(2).getPxPyPzGlo(pvec2); - float mass_ = sqrt(invmass3prongs2(pvec0[0], pvec0[1], - pvec0[2], masspion, - pvec1[0], pvec1[1], - pvec1[2], masskaon, - pvec2[0], pvec2[1], - pvec2[2], masspion)); - if (b_dovalplots == true) { - hmass3->Fill(mass_); - } - hftrackindexprong3(track_n1.collisionId(), - track_n1.globalIndex(), - track_p1.globalIndex(), - track_n1.globalIndex(), 2.); - } - } - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<SelectTracks>("produce-sel-track"), - adaptAnalysisTask<HFTrackIndexSkimsCreator>("vertexerhf-hftrackindexskimscreator")}; -} diff --git a/Analysis/Tasks/invMassAnalysis.cxx b/Analysis/Tasks/invMassAnalysis.cxx deleted file mode 100644 index 7403df356a15f..0000000000000 --- a/Analysis/Tasks/invMassAnalysis.cxx +++ /dev/null @@ -1,135 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/Multiplicity.h" -#include "Analysis/EventSelection.h" -#include "Analysis/Centrality.h" -#include <TH1F.h> -#include <TH2F.h> -#include <TMath.h> -#include "TVector3.h" -#include "TLorentzVector.h" -#include <cmath> -#include <vector> - -const float gkMass = 0.0005; - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -// This is a very simple example showing how to create an histogram -// FIXME: this should really inherit from AnalysisTask but -// we need GCC 7.4+ for that - -struct InvMassAnalysis { - // needs to be initialized with a label or an obj - // when adding an object to OutputObj later, the object name will be - // *reset* to OutputObj label - needed for correct placement in the output file - OutputObj<TH1F> centV0M{TH1F("centV0M", "centrality V0", 100, 0.0, 100.0)}; - OutputObj<TH1F> vtxZ{TH1F("vtxZ", "vtx Z", 200, -20.0, 20.0)}; - - OutputObj<TH1F> ptH{TH1F("pt", "pt", 100, -0.01, 10.01)}; - OutputObj<TH2F> ptCorr{TH2F("ptToPt", "ptToPt", 100, -0.01, 10.01, 100, -0.01, 10.01)}; - OutputObj<TH2F> tpcDedx{TH2F("tpcDedx", "TPC de/dx", 100, 0.0, 10.0, 100, 0.0, 200.0)}; - OutputObj<TH1I> itsHits{TH1I("itsHits", "ITS hits per layer", 6, -0.5, 5.5)}; - OutputObj<TH2I> itsHitsVsPt{TH2I("itsHitsVsPt", "ITS hits per layer", 6, -0.5, 5.5, 100, 0.0, 10.0)}; - OutputObj<TH1I> itsChi2{TH1I("itsChi2", "ITS chi2", 100, 0.0, 20.0)}; - OutputObj<TH1I> tpcChi2{TH1I("tpcChi2", "TPC chi2", 100, 0.0, 10.0)}; - OutputObj<TH1I> tpcCls{TH1I("tpcCls", "TPC clusters", 160, 0.0, 160.0)}; - OutputObj<TH1I> flagsHist{TH1I("flagsHist", "Flags", 64, -0.5, 63.5)}; - OutputObj<TH1F> invMassPM{TH1F("invMassPM", "Invariant mass, SEPM", 125, 0.0, 5.0)}; - OutputObj<TH1F> invMassPP{TH1F("invMassPP", "Invariant mass, SEPP", 125, 0.0, 5.0)}; - OutputObj<TH1F> invMassMM{TH1F("invMassMM", "Invariant mass, SEMM", 125, 0.0, 5.0)}; - OutputObj<TH2F> invMassVsPt{TH2F("invMassVsPt", "Invariant mass", 125, 0.0, 5.0, 10, 0.0, 10.0)}; - OutputObj<TH2F> invMassVsCentrality{TH2F("invMassVsCentrality", "Invariant mass", 125, 0.0, 5.0, 10, 0.0, 100.0)}; - OutputObj<TH1F> trZ{"trZ", OutputObjHandlingPolicy::QAObject}; - //Configurable<float> ptlow{"ptlow", 1.0f, "Lower pT limit"}; - //Configurable<float> pthigh{"pthigh", 1.0f, "Higher pT limit"}; - - float ptlow = 1.0; - float pthigh = 5.0; - Filter ptFilter = ((1.0f / aod::track::signed1Pt > ptlow) && (1.0f / aod::track::signed1Pt < pthigh)) || ((1.0f / aod::track::signed1Pt > -1.0f * pthigh) && (1.0f / aod::track::signed1Pt < -1.0f * ptlow)); - //Filter spdAnyFilter = (aod::track::itsClusterMap & (uint8_t(1)<<0)) || (aod::track::itsClusterMap & (uint8_t(1)<<1)); - float dedxLow = 75.0; - float dedxHigh = 90.0; - Filter dedxFilter = (aod::track::tpcSignal > dedxLow) && (aod::track::tpcSignal < dedxHigh); - float tpcChi2Max = 4.0; - float itsChi2Max = 36; - Filter qualityFilter = (aod::track::tpcChi2NCl < tpcChi2Max) && (aod::track::itsChi2NCl < itsChi2Max); - - void init(InitContext const&) - { - trZ.setObject(new TH1F("Z", "Z", 100, -10., 10.)); - } - - void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator collision, soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra>> const& tracks) - { - - if (!collision.sel7()) - return; - - centV0M->Fill(collision.centV0M()); - vtxZ->Fill(collision.posZ()); - - for (auto& track : tracks) { - //if (track.pt() < ptlow) - // continue; - ptH->Fill(track.pt()); - trZ->Fill(track.z()); - itsChi2->Fill(track.itsChi2NCl()); - for (int i = 0; i < 6; i++) { - if (track.itsClusterMap() & (uint8_t(1) << i)) - itsHits->Fill(i); - if (track.itsClusterMap() & (uint8_t(1) << i)) - itsHitsVsPt->Fill(i, track.pt()); - } - tpcDedx->Fill(track.tpcInnerParam(), track.tpcSignal()); - tpcChi2->Fill(track.tpcChi2NCl()); - tpcCls->Fill(track.tpcNClsFound()); - for (int i = 0; i < 64; i++) { - if (track.flags() & (uint64_t(1) << i)) - flagsHist->Fill(i); - } - } - for (auto& [t0, t1] : combinations(tracks, tracks)) { - ptCorr->Fill(t0.pt(), t1.pt()); - if (!((t0.itsClusterMap() & (uint8_t(1) << 0)) || (t0.itsClusterMap() & (uint8_t(1) << 1)))) - continue; - if (!((t1.itsClusterMap() & (uint8_t(1) << 0)) || (t1.itsClusterMap() & (uint8_t(1) << 1)))) - continue; - - TLorentzVector p1, p2, p; - p1.SetXYZM(t0.px(), t0.py(), t0.pz(), gkMass); - p2.SetXYZM(t1.px(), t1.py(), t1.pz(), gkMass); - p = p1 + p2; - - if (t0.charge() * t1.charge() < 0) { - invMassPM->Fill(p.M()); - invMassVsPt->Fill(p.M(), p.Pt()); - invMassVsCentrality->Fill(p.M(), collision.centV0M()); - } else { - if (t0.charge() > 0) - invMassPP->Fill(p.M()); - if (t0.charge() < 0) - invMassMM->Fill(p.M()); - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<InvMassAnalysis>("InvMassAnalysis")}; -} diff --git a/Analysis/Tasks/jetfinder.cxx b/Analysis/Tasks/jetfinder.cxx deleted file mode 100644 index 9959d0cd407c5..0000000000000 --- a/Analysis/Tasks/jetfinder.cxx +++ /dev/null @@ -1,127 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// jet finder task -// -// Author: Jochen Klein - -#include "Framework/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoA.h" - -#include "fastjet/PseudoJet.hh" -#include "fastjet/ClusterSequenceArea.hh" -#include "fastjet/AreaDefinition.hh" -#include "fastjet/JetDefinition.hh" -#include "fastjet/tools/JetMedianBackgroundEstimator.hh" -#include "fastjet/tools/Subtractor.hh" - -#include "Analysis/Jet.h" - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -struct JetFinderTask { - Produces<o2::aod::Jets> jets; - Produces<o2::aod::JetConstituents> constituents; - - // options for jet finding - Configurable<float> rParam{"rParam", 0.4, "jet radius"}; - Configurable<float> ghostEtamax{"ghostEtamax", 1.0, "eta max for ghosts"}; - Configurable<float> minJetPt{"minJetPt", 0., "minimum jet pt"}; - // TODO: initialize with 0.9 - rParam (requires lazy evaluation) - Configurable<float> maxJetEta{"maxJetEta", 0.5, "eta range for jets"}; - // TODO: use configurables also for enums - fastjet::JetAlgorithm algorithm{fastjet::antikt_algorithm}; - fastjet::RecombinationScheme recombScheme{fastjet::E_scheme}; - fastjet::Strategy strategy{fastjet::Best}; - fastjet::AreaType areaType{fastjet::passive_area}; - fastjet::GhostedAreaSpec ghostSpec{ghostEtamax}; - fastjet::AreaDefinition areaDef{areaType, ghostSpec}; - fastjet::JetDefinition jetDef{algorithm, rParam, recombScheme, strategy}; - fastjet::Selector selJet = fastjet::SelectorPtMin(minJetPt) && - fastjet::SelectorAbsRapMax(maxJetEta); - - // options for background subtraction - enum class BkgMode { none, - rhoArea }; - BkgMode bkgMode = BkgMode::none; - Configurable<double> rParamBkg{"rParamBkg", 0.2, "jet radius for background"}; - Configurable<double> rapBkg{"rapBkg", .9, "rapidity range for background"}; - // TODO: use configurables also for enums - fastjet::JetAlgorithm algorithmBkg{fastjet::kt_algorithm}; - fastjet::RecombinationScheme recombSchemeBkg{fastjet::E_scheme}; - fastjet::JetDefinition jetDefBkg{algorithmBkg, rParamBkg, recombSchemeBkg, strategy}; - fastjet::AreaDefinition areaDefBkg{areaType, ghostSpec}; - fastjet::Selector selBkg = fastjet::SelectorAbsRapMax(rapBkg); - - // TODO: use abs, eventually use pt - // TODO: use values from configurables - // TODO: add eta cuts - Filter trackCuts = (aod::track::signed1Pt < 10.f) && - (aod::track::signed1Pt > -10.f); - - std::unique_ptr<fastjet::BackgroundEstimatorBase> bge; - std::unique_ptr<fastjet::Subtractor> sub; - - std::vector<fastjet::PseudoJet> pJets; - - void init(InitContext const&) - { - if (bkgMode == BkgMode::none) { - } else if (bkgMode == BkgMode::rhoArea) { - bge = decltype(bge)(new fastjet::JetMedianBackgroundEstimator(selBkg, jetDefBkg, areaDefBkg)); - sub = decltype(sub){new fastjet::Subtractor{bge.get()}}; - } else { - LOGF(ERROR, "requested subtraction mode not implemented!"); - } - } - - void process(aod::Collision const& collision, - soa::Filtered<aod::Tracks> const& fullTracks) - { - // TODO: retrieve pion mass from somewhere - const float mPionSquared = 0.139 * 0.139; - - pJets.clear(); - - std::vector<fastjet::PseudoJet> inputParticles; - for (auto& track : fullTracks) { - auto energy = std::sqrt(track.p2() + mPionSquared); - inputParticles.emplace_back(track.px(), track.py(), track.pz(), energy); - inputParticles.back().set_user_index(track.globalIndex()); - } - - fastjet::ClusterSequenceArea clust_seq(inputParticles, jetDef, areaDef); - if (bge) - bge->set_particles(inputParticles); - pJets = sub ? (*sub)(clust_seq.inclusive_jets()) : clust_seq.inclusive_jets(); - - pJets = selJet(pJets); - - for (const auto& pjet : pJets) { - jets(collision, pjet.eta(), pjet.phi(), pjet.pt(), - pjet.area(), pjet.Et(), pjet.m()); - for (const auto& track : pjet.constituents()) { - LOGF(DEBUG, "jet %d constituent %d: %f %f %f", jets.lastIndex(), - track.user_index(), track.eta(), track.phi(), track.pt()); - constituents(jets.lastIndex(), track.user_index()); - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<JetFinderTask>("jet-finder")}; -} diff --git a/Analysis/Tasks/multiplicityQa.cxx b/Analysis/Tasks/multiplicityQa.cxx index 3160e14397f96..9a305a7443906 100644 --- a/Analysis/Tasks/multiplicityQa.cxx +++ b/Analysis/Tasks/multiplicityQa.cxx @@ -7,33 +7,76 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// +// This code calculates output histograms for centrality calibration +// as well as vertex-Z dependencies of raw variables (either for calibration +// of vtx-Z dependencies or for the calibration of those). +// +// This task is not strictly necessary in a typical analysis workflow, +// except for centrality calibration! The necessary task is the multiplicity +// tables. + #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/Multiplicity.h" -#include "Analysis/EventSelection.h" +#include "AnalysisDataModel/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" #include "TH1F.h" +#include "TH2F.h" using namespace o2; using namespace o2::framework; struct MultiplicityQaTask { - OutputObj<TH1F> hMultV0M{TH1F("hMultV0M", "", 40000, 0., 40000.)}; - OutputObj<TH1F> hMultZNA{TH1F("hMultZNA", "", 500, 0., 200000.)}; - OutputObj<TH1F> hMultZNC{TH1F("hMultZNC", "", 500, 0., 200000.)}; + OutputObj<TH1F> hMultV0M{TH1F("hMultV0M", "", 50000, 0., 50000.)}; + OutputObj<TH1F> hMultT0M{TH1F("hMultT0M", "", 10000, 0., 200000.)}; + OutputObj<TH1F> hMultZNA{TH1F("hMultZNA", "", 600, 0., 240000.)}; + OutputObj<TH1F> hMultZNC{TH1F("hMultZNC", "", 600, 0., 240000.)}; + OutputObj<TH2F> hMultV0MvsT0M{TH2F("hMultV0MvsT0M", ";V0M;T0M", 200, 0., 50000., 200, 0., 200000.)}; + + //For vertex-Z corrections + OutputObj<TProfile> hVtxProfV0M{TProfile("hVtxProfV0M", "", 150, -15, 15)}; + OutputObj<TProfile> hVtxProfT0M{TProfile("hVtxProfT0M", "", 150, -15, 15)}; + OutputObj<TProfile> hVtxProfZNA{TProfile("hVtxProfZNA", "", 150, -15, 15)}; + OutputObj<TProfile> hVtxProfZNC{TProfile("hVtxProfZNC", "", 150, -15, 15)}; + + OutputObj<TProfile> hMultNtrackletsVsV0M{TProfile("hMultNtrackletsVsV0M", "", 50000, 0., 50000.)}; + + Configurable<bool> isMC{"isMC", 0, "0 - data, 1 - MC"}; + Configurable<int> selection{"sel", 7, "trigger: 7 - sel7, 8 - sel8"}; void process(soa::Join<aod::Collisions, aod::EvSels, aod::Mults>::iterator const& col) { - if (!col.alias()[kINT7]) + if (!isMC && !col.alias()[kINT7]) { return; - if (!col.sel7()) + } + + if (selection == 7 && !col.sel7()) { + return; + } + + if (selection == 8 && !col.sel8()) { return; + } - LOGF(debug, "multV0A=%5.0f multV0C=%5.0f multV0M=%5.0f", col.multV0A(), col.multV0C(), col.multV0M()); + if (selection != 7 && selection != 8) { + LOGF(fatal, "Unknown selection type! Use `--sel 7` or `--sel 8`"); + } + + LOGF(debug, "multV0A=%5.0f multV0C=%5.0f multV0M=%5.0f multT0A=%5.0f multT0C=%5.0f multT0M=%5.0f", col.multV0A(), col.multV0C(), col.multV0M(), col.multT0A(), col.multT0C(), col.multT0M()); // fill calibration histos hMultV0M->Fill(col.multV0M()); + hMultT0M->Fill(col.multT0M()); hMultZNA->Fill(col.multZNA()); hMultZNC->Fill(col.multZNC()); + hMultV0MvsT0M->Fill(col.multV0M(), col.multT0M()); + hMultNtrackletsVsV0M->Fill(col.multV0M(), col.multTracklets()); + + //Vertex-Z dependencies + hVtxProfV0M->Fill(col.posZ(), col.multV0M()); + hVtxProfT0M->Fill(col.posZ(), col.multT0M()); + hVtxProfZNA->Fill(col.posZ(), col.multZNA()); + hVtxProfZNC->Fill(col.posZ(), col.multZNC()); } }; diff --git a/Analysis/Tasks/multiplicityTable.cxx b/Analysis/Tasks/multiplicityTable.cxx index 8cf85dc398e68..724bb4791b088 100644 --- a/Analysis/Tasks/multiplicityTable.cxx +++ b/Analysis/Tasks/multiplicityTable.cxx @@ -7,53 +7,113 @@ // In applying this license CERN does 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" + +using namespace o2; +using namespace o2::framework; + +// custom configurable for switching between run2 and run3 selection types +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{"selection-run", VariantType::Int, 2, {"selection type: 2 - run 2, 3 - run 3"}}); +} + #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/EventSelection.h" -#include "Analysis/Multiplicity.h" +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/Multiplicity.h" #include "iostream" -using namespace o2; -using namespace o2::framework; -struct MultiplicityTableTask { +struct MultiplicityTableTaskIndexed { Produces<aod::Mults> mult; + Partition<aod::Tracks> tracklets = (aod::track::trackType == static_cast<uint8_t>(o2::aod::track::TrackTypeEnum::Run2Tracklet)); - aod::Run2V0 getVZero(aod::BC const& bc, aod::Run2V0s const& vzeros) + void process(aod::Run2MatchedSparse::iterator const& collision, aod::Tracks const& tracks, aod::BCs const&, aod::Zdcs const&, aod::FV0As const& fv0as, aod::FV0Cs const& fv0cs, aod::FT0s const& ft0s) { - for (auto& vzero : vzeros) - if (vzero.bc() == bc) - return vzero; - aod::Run2V0 dummy; - return dummy; - } + float multV0A = -1.f; + float multV0C = -1.f; + float multT0A = -1.f; + float multT0C = -1.f; + float multZNA = -1.f; + float multZNC = -1.f; + int multTracklets = tracklets.size(); - aod::Zdc getZdc(aod::BC const& bc, aod::Zdcs const& zdcs) - { - for (auto& zdc : zdcs) - if (zdc.bc() == bc) - return zdc; - aod::Zdc dummy; - return dummy; + if (collision.has_fv0a()) { + auto v0a = collision.fv0a(); + for (int i = 0; i < 48; i++) { + multV0A += v0a.amplitude()[i]; + } + } + if (collision.has_fv0c()) { + auto v0c = collision.fv0c(); + for (int i = 0; i < 32; i++) { + multV0C += v0c.amplitude()[i]; + } + } + if (collision.has_ft0()) { + auto ft0 = collision.ft0(); + for (int i = 0; i < 96; i++) { + multT0A += ft0.amplitudeA()[i]; + } + for (int i = 0; i < 112; i++) { + multT0C += ft0.amplitudeC()[i]; + } + } + if (collision.has_zdc()) { + auto zdc = collision.zdc(); + multZNA = zdc.energyCommonZNA(); + multZNC = zdc.energyCommonZNC(); + } + LOGF(debug, "multV0A=%5.0f multV0C=%5.0f multT0A=%5.0f multT0C=%5.0f multZNA=%6.0f multZNC=%6.0f multTracklets=%i", multV0A, multV0C, multT0A, multT0C, multZNA, multZNC, multTracklets); + mult(multV0A, multV0C, multT0A, multT0C, multZNA, multZNC, multTracklets); } +}; - void process(aod::Collision const& collision, aod::BCs const& bcs, aod::Zdcs const& zdcs, aod::Run2V0s const& vzeros) +struct MultiplicityTableTaskRun3 { + Produces<aod::Mults> mult; + Partition<aod::Tracks> tracklets = (aod::track::trackType == static_cast<uint8_t>(o2::aod::track::TrackTypeEnum::Run2Tracklet)); + + void process(soa::Join<aod::Collisions, aod::EvSels> const& collisions, aod::Tracks const& tracks, aod::BCs const& bcs, aod::Zdcs const& zdcs, aod::FV0As const& fv0as, aod::FT0s const& ft0s) { - auto zdc = getZdc(collision.bc(), zdcs); - auto vzero = getVZero(collision.bc(), vzeros); - float multV0A = vzero.multA(); - float multV0C = vzero.multC(); - float multZNA = zdc.energyCommonZNA(); - float multZNC = zdc.energyCommonZNC(); - - LOGF(debug, "multV0A=%5.0f multV0C=%5.0f multZNA=%6.0f multZNC=%6.0f", multV0A, multV0C, multZNA, multZNC); - // fill multiplicity columns - mult(multV0A, multV0C, multZNA, multZNC); + for (auto& collision : collisions) { + float multV0A = -1.f; + float multV0C = -1.f; + float multT0A = -1.f; + float multT0C = -1.f; + float multZNA = -1.f; + float multZNC = -1.f; + int multTracklets = tracklets.size(); + + const float* aAmplitudesA; + const float* aAmplitudesC; + + // using FT0 row index from event selection task + int64_t foundFT0 = collision.foundFT0(); + + if (foundFT0 != -1) { + auto ft0 = ft0s.iteratorAt(foundFT0); + aAmplitudesA = ft0.amplitudeA(); + aAmplitudesC = ft0.amplitudeC(); + for (int i = 0; i < 96; i++) { + multT0A += aAmplitudesA[i]; + } + for (int i = 0; i < 112; i++) { + multT0C += aAmplitudesC[i]; + } + } + + LOGF(debug, "multV0A=%5.0f multV0C=%5.0f multT0A=%5.0f multT0C=%5.0f multZNA=%6.0f multZNC=%6.0f multTracklets=%i", multV0A, multV0C, multT0A, multT0C, multZNA, multZNC, multTracklets); + mult(multV0A, multV0C, multT0A, multT0C, multZNA, multZNC, multTracklets); + } } }; -WorkflowSpec defineDataProcessing(ConfigContext const&) +WorkflowSpec defineDataProcessing(ConfigContext const& ctx) { - return WorkflowSpec{ - adaptAnalysisTask<MultiplicityTableTask>("multiplicity-table")}; + if (ctx.options().get<int>("selection-run") == 2) { + return WorkflowSpec{adaptAnalysisTask<MultiplicityTableTaskIndexed>("multiplicity-table")}; + } else { + return WorkflowSpec{adaptAnalysisTask<MultiplicityTableTaskRun3>("multiplicity-table")}; + } } diff --git a/Analysis/Tasks/pidTOF.cxx b/Analysis/Tasks/pidTOF.cxx new file mode 100644 index 0000000000000..9966c9011d2e2 --- /dev/null +++ b/Analysis/Tasks/pidTOF.cxx @@ -0,0 +1,255 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "Framework/HistogramRegistry.h" +#include "ReconstructionDataFormats/Track.h" +#include <CCDB/BasicCCDBManager.h> +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/PID/PIDTOF.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::pid; +using namespace o2::framework::expressions; +using namespace o2::track; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"add-qa", VariantType::Int, 0, {"Produce TOF PID QA histograms"}}, + {"add-beta", VariantType::Int, 1, {"Produce TOF Beta table"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +struct pidTOFTask { + using Trks = soa::Join<aod::Tracks, aod::TracksExtra>; + using Coll = aod::Collisions; + Produces<aod::pidRespTOF> tofpid; + DetectorResponse resp; + Service<o2::ccdb::BasicCCDBManager> ccdb; + Configurable<std::string> paramfile{"param-file", "", "Path to the parametrization object, if emtpy the parametrization is not taken from file"}; + Configurable<std::string> sigmaname{"param-sigma", "TOFReso", "Name of the parametrization for the expected sigma, used in both file and CCDB mode"}; + Configurable<std::string> url{"ccdb-url", "http://ccdb-test.cern.ch:8080", "url of the ccdb repository"}; + Configurable<long> timestamp{"ccdb-timestamp", -1, "timestamp of the object"}; + + void init(o2::framework::InitContext&) + { + ccdb->setURL(url.value); + ccdb->setTimestamp(timestamp.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + // Not later than now objects + ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()); + // + const std::vector<float> p = {0.008, 0.008, 0.002, 40.0}; + resp.SetParameters(DetectorResponse::kSigma, p); + const std::string fname = paramfile.value; + if (!fname.empty()) { // Loading the parametrization from file + resp.LoadParamFromFile(fname.data(), sigmaname.value, DetectorResponse::kSigma); + } else { // Loading it from CCDB + const std::string path = "Analysis/PID/TOF"; + resp.LoadParam(DetectorResponse::kSigma, ccdb->getForTimeStamp<Parametrization>(path + "/" + sigmaname.value, timestamp.value)); + } + } + + void process(Coll const& collisions, Trks const& tracks) + { + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Electron> resp_Electron = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Electron>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Muon> resp_Muon = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Muon>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Pion> resp_Pion = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Pion>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Kaon> resp_Kaon = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Kaon>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Proton> resp_Proton = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Proton>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Deuteron> resp_Deuteron = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Deuteron>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Triton> resp_Triton = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Triton>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Helium3> resp_Helium3 = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Helium3>(); + constexpr tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Alpha> resp_Alpha = tof::ExpTimes<Coll::iterator, Trks::iterator, PID::Alpha>(); + + tofpid.reserve(tracks.size()); + for (auto const& trk : tracks) { + tofpid(resp_Electron.GetExpectedSigma(resp, trk.collision(), trk), + resp_Muon.GetExpectedSigma(resp, trk.collision(), trk), + resp_Pion.GetExpectedSigma(resp, trk.collision(), trk), + resp_Kaon.GetExpectedSigma(resp, trk.collision(), trk), + resp_Proton.GetExpectedSigma(resp, trk.collision(), trk), + resp_Deuteron.GetExpectedSigma(resp, trk.collision(), trk), + resp_Triton.GetExpectedSigma(resp, trk.collision(), trk), + resp_Helium3.GetExpectedSigma(resp, trk.collision(), trk), + resp_Alpha.GetExpectedSigma(resp, trk.collision(), trk), + resp_Electron.GetSeparation(resp, trk.collision(), trk), + resp_Muon.GetSeparation(resp, trk.collision(), trk), + resp_Pion.GetSeparation(resp, trk.collision(), trk), + resp_Kaon.GetSeparation(resp, trk.collision(), trk), + resp_Proton.GetSeparation(resp, trk.collision(), trk), + resp_Deuteron.GetSeparation(resp, trk.collision(), trk), + resp_Triton.GetSeparation(resp, trk.collision(), trk), + resp_Helium3.GetSeparation(resp, trk.collision(), trk), + resp_Alpha.GetSeparation(resp, trk.collision(), trk)); + } + } +}; + +struct pidTOFTaskBeta { + using Trks = soa::Join<aod::Tracks, aod::TracksExtra>; + using Coll = aod::Collision; + Produces<aod::pidRespTOFbeta> tofpidbeta; + tof::Beta<Coll, Trks::iterator, PID::Electron> resp_Electron; + Configurable<float> expreso{"tof-expreso", 80, "Expected resolution for the computation of the expected beta"}; + + void init(o2::framework::InitContext&) + { + resp_Electron.mExpectedResolution = expreso.value; + } + + void process(Coll const& collision, Trks const& tracks) + { + tofpidbeta.reserve(tracks.size()); + for (auto const& trk : tracks) { + tofpidbeta(resp_Electron.GetBeta(collision, trk), + resp_Electron.GetExpectedSigma(collision, trk), + resp_Electron.GetExpectedSignal(collision, trk), + resp_Electron.GetExpectedSigma(collision, trk), + resp_Electron.GetSeparation(collision, trk)); + } + } +}; + +struct pidTOFTaskQA { + + static constexpr int Np = 9; + static constexpr std::string_view hexpected[Np] = {"expected/El", "expected/Mu", "expected/Pi", + "expected/Ka", "expected/Pr", "expected/De", + "expected/Tr", "expected/He", "expected/Al"}; + static constexpr std::string_view hexpected_diff[Np] = {"expected_diff/El", "expected_diff/Mu", "expected_diff/Pi", + "expected_diff/Ka", "expected_diff/Pr", "expected_diff/De", + "expected_diff/Tr", "expected_diff/He", "expected_diff/Al"}; + static constexpr std::string_view hnsigma[Np] = {"nsigma/El", "nsigma/Mu", "nsigma/Pi", + "nsigma/Ka", "nsigma/Pr", "nsigma/De", + "nsigma/Tr", "nsigma/He", "nsigma/Al"}; + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; + + Configurable<int> nBinsP{"nBinsP", 400, "Number of bins for the momentum"}; + Configurable<float> MinP{"MinP", 0.1, "Minimum momentum in range"}; + Configurable<float> MaxP{"MaxP", 5, "Maximum momentum in range"}; + + template <typename T> + void makelogaxis(T h) + { + const int nbins = h->GetNbinsX(); + double binp[nbins + 1]; + double max = h->GetXaxis()->GetBinUpEdge(nbins); + double min = h->GetXaxis()->GetBinLowEdge(1); + if (min <= 0) { + min = 0.00001; + } + double lmin = TMath::Log10(min); + double ldelta = (TMath::Log10(max) - lmin) / ((double)nbins); + for (int i = 0; i < nbins; i++) { + binp[i] = TMath::Exp(TMath::Log(10) * (lmin + i * ldelta)); + } + binp[nbins] = max + 1; + h->GetXaxis()->Set(nbins, binp); + } + + template <uint8_t i> + void addParticleHistos() + { + // Exp signal + histos.add(hexpected[i].data(), Form(";#it{p} (GeV/#it{c});t_{exp}(%s)", pT[i]), HistType::kTH2F, {{nBinsP, MinP, MaxP}, {1000, 0, 2e6}}); + makelogaxis(histos.get<TH2>(HIST(hexpected[i]))); + + // T-Texp + histos.add(hexpected_diff[i].data(), Form(";#it{p} (GeV/#it{c});(t-t_{evt}-t_{exp}(%s))", pT[i]), HistType::kTH2F, {{nBinsP, MinP, MaxP}, {100, -1000, 1000}}); + makelogaxis(histos.get<TH2>(HIST(hexpected_diff[i]))); + + // NSigma + histos.add(hnsigma[i].data(), Form(";#it{p} (GeV/#it{c});N_{#sigma}^{TOF}(%s)", pT[i]), HistType::kTH2F, {{nBinsP, MinP, MaxP}, {200, -10, 10}}); + makelogaxis(histos.get<TH2>(HIST(hnsigma[i]))); + } + + void init(o2::framework::InitContext&) + { + // Event properties + histos.add("event/vertexz", ";Vtx_{z} (cm);Entries", HistType::kTH1F, {{100, -20, 20}}); + histos.add("event/colltime", ";Collision time (ps);Entries", HistType::kTH1F, {{100, -2000, 2000}}); + histos.add("event/tofsignal", ";#it{p} (GeV/#it{c});TOF Signal", HistType::kTH2F, {{nBinsP, MinP, MaxP}, {10000, 0, 2e6}}); + makelogaxis(histos.get<TH2>(HIST("event/tofsignal"))); + histos.add("event/tofbeta", ";#it{p} (GeV/#it{c});TOF #beta", HistType::kTH2F, {{nBinsP, MinP, MaxP}, {1000, 0, 2}}); + makelogaxis(histos.get<TH2>(HIST("event/tofbeta"))); + + addParticleHistos<0>(); + addParticleHistos<1>(); + addParticleHistos<2>(); + addParticleHistos<3>(); + addParticleHistos<4>(); + addParticleHistos<5>(); + addParticleHistos<6>(); + addParticleHistos<7>(); + addParticleHistos<8>(); + } + + template <uint8_t i, typename T> + void fillParticleHistos(const T& t, const float tof, const float exp_diff, const float nsigma) + { + histos.fill(HIST(hexpected[i]), t.p(), tof - exp_diff); + histos.fill(HIST(hexpected_diff[i]), t.p(), exp_diff); + histos.fill(HIST(hnsigma[i]), t.p(), nsigma); + } + + void process(aod::Collision const& collision, soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTOF, aod::pidRespTOFbeta> const& tracks) + { + const float collisionTime_ps = collision.collisionTime() * 1000.f; + histos.fill(HIST("event/vertexz"), collision.posZ()); + histos.fill(HIST("event/colltime"), collisionTime_ps); + + for (auto t : tracks) { + // + if (t.tofSignal() < 0) { // Skipping tracks without TOF + continue; + } + + const float tof = t.tofSignal() - collisionTime_ps; + + // + histos.fill(HIST("event/tofsignal"), t.p(), t.tofSignal()); + histos.fill(HIST("event/tofbeta"), t.p(), t.beta()); + // + fillParticleHistos<0>(t, tof, t.tofExpSignalDiffEl(), t.tofNSigmaEl()); + fillParticleHistos<1>(t, tof, t.tofExpSignalDiffMu(), t.tofNSigmaMu()); + fillParticleHistos<2>(t, tof, t.tofExpSignalDiffPi(), t.tofNSigmaPi()); + fillParticleHistos<3>(t, tof, t.tofExpSignalDiffKa(), t.tofNSigmaKa()); + fillParticleHistos<4>(t, tof, t.tofExpSignalDiffPr(), t.tofNSigmaPr()); + fillParticleHistos<5>(t, tof, t.tofExpSignalDiffDe(), t.tofNSigmaDe()); + fillParticleHistos<6>(t, tof, t.tofExpSignalDiffTr(), t.tofNSigmaTr()); + fillParticleHistos<7>(t, tof, t.tofExpSignalDiffHe(), t.tofNSigmaHe()); + fillParticleHistos<8>(t, tof, t.tofExpSignalDiffAl(), t.tofNSigmaAl()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + auto workflow = WorkflowSpec{adaptAnalysisTask<pidTOFTask>("pidTOF-task")}; + const int add_beta = cfgc.options().get<int>("add-beta"); + const int add_qa = cfgc.options().get<int>("add-qa"); + if (add_beta || add_qa) { + workflow.push_back(adaptAnalysisTask<pidTOFTaskBeta>("pidTOFBeta-task")); + } + if (add_qa) { + workflow.push_back(adaptAnalysisTask<pidTOFTaskQA>("pidTOFQA-task")); + } + return workflow; +} diff --git a/Analysis/Tasks/pidTPC.cxx b/Analysis/Tasks/pidTPC.cxx new file mode 100644 index 0000000000000..d94e45954b03d --- /dev/null +++ b/Analysis/Tasks/pidTPC.cxx @@ -0,0 +1,212 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 includes +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "Framework/HistogramRegistry.h" +#include "ReconstructionDataFormats/Track.h" +#include <CCDB/BasicCCDBManager.h> +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/PID/PIDTPC.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::pid; +using namespace o2::framework::expressions; +using namespace o2::track; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"add-qa", VariantType::Int, 0, {"Produce TOF PID QA histograms"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +struct pidTPCTask { + using Trks = soa::Join<aod::Tracks, aod::TracksExtra>; + using Coll = aod::Collisions; + Produces<aod::pidRespTPC> tpcpid; + DetectorResponse resp; + Service<o2::ccdb::BasicCCDBManager> ccdb; + Configurable<std::string> paramfile{"param-file", "", "Path to the parametrization object, if emtpy the parametrization is not taken from file"}; + Configurable<std::string> signalname{"param-signal", "BetheBloch", "Name of the parametrization for the expected signal, used in both file and CCDB mode"}; + Configurable<std::string> sigmaname{"param-sigma", "TPCReso", "Name of the parametrization for the expected sigma, used in both file and CCDB mode"}; + Configurable<std::string> url{"ccdb-url", "http://ccdb-test.cern.ch:8080", "url of the ccdb repository"}; + Configurable<long> timestamp{"ccdb-timestamp", -1, "timestamp of the object"}; + + void init(o2::framework::InitContext&) + { + ccdb->setURL(url.value); + ccdb->setTimestamp(timestamp.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + // Not later than now objects + ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()); + // + const std::string fname = paramfile.value; + if (!fname.empty()) { // Loading the parametrization from file + resp.LoadParamFromFile(fname.data(), signalname.value, DetectorResponse::kSignal); + resp.LoadParamFromFile(fname.data(), sigmaname.value, DetectorResponse::kSigma); + } else { // Loading it from CCDB + const std::string path = "Analysis/PID/TPC"; + resp.LoadParam(DetectorResponse::kSignal, ccdb->getForTimeStamp<Parametrization>(path + "/" + signalname.value, timestamp.value)); + resp.LoadParam(DetectorResponse::kSigma, ccdb->getForTimeStamp<Parametrization>(path + "/" + sigmaname.value, timestamp.value)); + } + } + + void process(Coll const& collisions, Trks const& tracks) + { + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Electron> resp_Electron = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Electron>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Muon> resp_Muon = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Muon>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Pion> resp_Pion = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Pion>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Kaon> resp_Kaon = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Kaon>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Proton> resp_Proton = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Proton>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Deuteron> resp_Deuteron = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Deuteron>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Triton> resp_Triton = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Triton>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Helium3> resp_Helium3 = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Helium3>(); + constexpr tpc::ELoss<Coll::iterator, Trks::iterator, PID::Alpha> resp_Alpha = tpc::ELoss<Coll::iterator, Trks::iterator, PID::Alpha>(); + + tpcpid.reserve(tracks.size()); + for (auto const& trk : tracks) { + tpcpid(resp_Electron.GetExpectedSigma(resp, trk.collision(), trk), + resp_Muon.GetExpectedSigma(resp, trk.collision(), trk), + resp_Pion.GetExpectedSigma(resp, trk.collision(), trk), + resp_Kaon.GetExpectedSigma(resp, trk.collision(), trk), + resp_Proton.GetExpectedSigma(resp, trk.collision(), trk), + resp_Deuteron.GetExpectedSigma(resp, trk.collision(), trk), + resp_Triton.GetExpectedSigma(resp, trk.collision(), trk), + resp_Helium3.GetExpectedSigma(resp, trk.collision(), trk), + resp_Alpha.GetExpectedSigma(resp, trk.collision(), trk), + resp_Electron.GetSeparation(resp, trk.collision(), trk), + resp_Muon.GetSeparation(resp, trk.collision(), trk), + resp_Pion.GetSeparation(resp, trk.collision(), trk), + resp_Kaon.GetSeparation(resp, trk.collision(), trk), + resp_Proton.GetSeparation(resp, trk.collision(), trk), + resp_Deuteron.GetSeparation(resp, trk.collision(), trk), + resp_Triton.GetSeparation(resp, trk.collision(), trk), + resp_Helium3.GetSeparation(resp, trk.collision(), trk), + resp_Alpha.GetSeparation(resp, trk.collision(), trk)); + } + } +}; + +struct pidTPCTaskQA { + static constexpr int Np = 9; + static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p", "d", "t", "^{3}He", "#alpha"}; + static constexpr std::string_view hexpected[Np] = {"expected/El", "expected/Mu", "expected/Pi", + "expected/Ka", "expected/Pr", "expected/De", + "expected/Tr", "expected/He", "expected/Al"}; + static constexpr std::string_view hexpected_diff[Np] = {"expected_diff/El", "expected_diff/Mu", "expected_diff/Pi", + "expected_diff/Ka", "expected_diff/Pr", "expected_diff/De", + "expected_diff/Tr", "expected_diff/He", "expected_diff/Al"}; + static constexpr std::string_view hnsigma[Np] = {"nsigma/El", "nsigma/Mu", "nsigma/Pi", + "nsigma/Ka", "nsigma/Pr", "nsigma/De", + "nsigma/Tr", "nsigma/He", "nsigma/Al"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; + + Configurable<int> nBinsP{"nBinsP", 400, "Number of bins for the momentum"}; + Configurable<float> MinP{"MinP", 0, "Minimum momentum in range"}; + Configurable<float> MaxP{"MaxP", 20, "Maximum momentum in range"}; + + template <typename T> + void makelogaxis(T h) + { + const int nbins = h->GetNbinsX(); + double binp[nbins + 1]; + double max = h->GetXaxis()->GetBinUpEdge(nbins); + double min = h->GetXaxis()->GetBinLowEdge(1); + if (min <= 0) { + min = 0.00001; + } + double lmin = TMath::Log10(min); + double ldelta = (TMath::Log10(max) - lmin) / ((double)nbins); + for (int i = 0; i < nbins; i++) { + binp[i] = TMath::Exp(TMath::Log(10) * (lmin + i * ldelta)); + } + binp[nbins] = max + 1; + h->GetXaxis()->Set(nbins, binp); + } + + template <uint8_t i> + void addParticleHistos() + { + // Exp signal + histos.add(hexpected[i].data(), Form(";#it{p} (GeV/#it{c});d#it{E}/d#it{x}_(%s)", pT[i]), kTH2F, {{nBinsP, MinP, MaxP}, {1000, 0, 1000}}); + makelogaxis(histos.get<TH2>(HIST(hexpected[i]))); + + // Signal - Expected signal + histos.add(hexpected_diff[i].data(), Form(";#it{p} (GeV/#it{c});;d#it{E}/d#it{x} - d#it{E}/d#it{x}(%s)", pT[i]), kTH2F, {{nBinsP, MinP, MaxP}, {1000, -500, 500}}); + makelogaxis(histos.get<TH2>(HIST(hexpected_diff[i]))); + + // NSigma + histos.add(hnsigma[i].data(), Form(";#it{p} (GeV/#it{c});N_{#sigma}^{TPC}(%s)", pT[i]), kTH2F, {{nBinsP, MinP, MaxP}, {200, -10, 10}}); + makelogaxis(histos.get<TH2>(HIST(hnsigma[i]))); + } + + void init(o2::framework::InitContext&) + { + // Event properties + histos.add("event/vertexz", ";Vtx_{z} (cm);Entries", kTH1F, {{100, -20, 20}}); + histos.add("event/tpcsignal", ";#it{p} (GeV/#it{c});TPC Signal", kTH2F, {{nBinsP, MinP, MaxP}, {1000, 0, 1000}}); + makelogaxis(histos.get<TH2>(HIST("event/tpcsignal"))); + + addParticleHistos<0>(); + addParticleHistos<1>(); + addParticleHistos<2>(); + addParticleHistos<3>(); + addParticleHistos<4>(); + addParticleHistos<5>(); + addParticleHistos<6>(); + addParticleHistos<7>(); + addParticleHistos<8>(); + } + + template <uint8_t i, typename T> + void fillParticleHistos(const T& t, const float mom, const float exp_diff, const float nsigma) + { + histos.fill(HIST(hexpected[i]), mom, t.tpcSignal() - exp_diff); + histos.fill(HIST(hexpected_diff[i]), mom, exp_diff); + histos.fill(HIST(hnsigma[i]), t.p(), nsigma); + } + + void process(aod::Collision const& collision, soa::Join<aod::Tracks, aod::TracksExtra, aod::pidRespTPC> const& tracks) + { + histos.fill(HIST("event/vertexz"), collision.posZ()); + + for (auto t : tracks) { + // const float mom = t.p(); + const float mom = t.tpcInnerParam(); + histos.fill(HIST("event/tpcsignal"), mom, t.tpcSignal()); + // + fillParticleHistos<0>(t, mom, t.tpcExpSignalDiffEl(), t.tpcNSigmaEl()); + fillParticleHistos<1>(t, mom, t.tpcExpSignalDiffMu(), t.tpcNSigmaMu()); + fillParticleHistos<2>(t, mom, t.tpcExpSignalDiffPi(), t.tpcNSigmaPi()); + fillParticleHistos<3>(t, mom, t.tpcExpSignalDiffKa(), t.tpcNSigmaKa()); + fillParticleHistos<4>(t, mom, t.tpcExpSignalDiffPr(), t.tpcNSigmaPr()); + fillParticleHistos<5>(t, mom, t.tpcExpSignalDiffDe(), t.tpcNSigmaDe()); + fillParticleHistos<6>(t, mom, t.tpcExpSignalDiffTr(), t.tpcNSigmaTr()); + fillParticleHistos<7>(t, mom, t.tpcExpSignalDiffHe(), t.tpcNSigmaHe()); + fillParticleHistos<8>(t, mom, t.tpcExpSignalDiffAl(), t.tpcNSigmaAl()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + auto workflow = WorkflowSpec{adaptAnalysisTask<pidTPCTask>("pidTPC-task")}; + if (cfgc.options().get<int>("add-qa")) { + workflow.push_back(adaptAnalysisTask<pidTPCTaskQA>("pidTPCQA-task")); + } + return workflow; +} diff --git a/Analysis/Tasks/reducedEventAnalysis.cxx b/Analysis/Tasks/reducedEventAnalysis.cxx deleted file mode 100644 index 7403df356a15f..0000000000000 --- a/Analysis/Tasks/reducedEventAnalysis.cxx +++ /dev/null @@ -1,135 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/Multiplicity.h" -#include "Analysis/EventSelection.h" -#include "Analysis/Centrality.h" -#include <TH1F.h> -#include <TH2F.h> -#include <TMath.h> -#include "TVector3.h" -#include "TLorentzVector.h" -#include <cmath> -#include <vector> - -const float gkMass = 0.0005; - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -// This is a very simple example showing how to create an histogram -// FIXME: this should really inherit from AnalysisTask but -// we need GCC 7.4+ for that - -struct InvMassAnalysis { - // needs to be initialized with a label or an obj - // when adding an object to OutputObj later, the object name will be - // *reset* to OutputObj label - needed for correct placement in the output file - OutputObj<TH1F> centV0M{TH1F("centV0M", "centrality V0", 100, 0.0, 100.0)}; - OutputObj<TH1F> vtxZ{TH1F("vtxZ", "vtx Z", 200, -20.0, 20.0)}; - - OutputObj<TH1F> ptH{TH1F("pt", "pt", 100, -0.01, 10.01)}; - OutputObj<TH2F> ptCorr{TH2F("ptToPt", "ptToPt", 100, -0.01, 10.01, 100, -0.01, 10.01)}; - OutputObj<TH2F> tpcDedx{TH2F("tpcDedx", "TPC de/dx", 100, 0.0, 10.0, 100, 0.0, 200.0)}; - OutputObj<TH1I> itsHits{TH1I("itsHits", "ITS hits per layer", 6, -0.5, 5.5)}; - OutputObj<TH2I> itsHitsVsPt{TH2I("itsHitsVsPt", "ITS hits per layer", 6, -0.5, 5.5, 100, 0.0, 10.0)}; - OutputObj<TH1I> itsChi2{TH1I("itsChi2", "ITS chi2", 100, 0.0, 20.0)}; - OutputObj<TH1I> tpcChi2{TH1I("tpcChi2", "TPC chi2", 100, 0.0, 10.0)}; - OutputObj<TH1I> tpcCls{TH1I("tpcCls", "TPC clusters", 160, 0.0, 160.0)}; - OutputObj<TH1I> flagsHist{TH1I("flagsHist", "Flags", 64, -0.5, 63.5)}; - OutputObj<TH1F> invMassPM{TH1F("invMassPM", "Invariant mass, SEPM", 125, 0.0, 5.0)}; - OutputObj<TH1F> invMassPP{TH1F("invMassPP", "Invariant mass, SEPP", 125, 0.0, 5.0)}; - OutputObj<TH1F> invMassMM{TH1F("invMassMM", "Invariant mass, SEMM", 125, 0.0, 5.0)}; - OutputObj<TH2F> invMassVsPt{TH2F("invMassVsPt", "Invariant mass", 125, 0.0, 5.0, 10, 0.0, 10.0)}; - OutputObj<TH2F> invMassVsCentrality{TH2F("invMassVsCentrality", "Invariant mass", 125, 0.0, 5.0, 10, 0.0, 100.0)}; - OutputObj<TH1F> trZ{"trZ", OutputObjHandlingPolicy::QAObject}; - //Configurable<float> ptlow{"ptlow", 1.0f, "Lower pT limit"}; - //Configurable<float> pthigh{"pthigh", 1.0f, "Higher pT limit"}; - - float ptlow = 1.0; - float pthigh = 5.0; - Filter ptFilter = ((1.0f / aod::track::signed1Pt > ptlow) && (1.0f / aod::track::signed1Pt < pthigh)) || ((1.0f / aod::track::signed1Pt > -1.0f * pthigh) && (1.0f / aod::track::signed1Pt < -1.0f * ptlow)); - //Filter spdAnyFilter = (aod::track::itsClusterMap & (uint8_t(1)<<0)) || (aod::track::itsClusterMap & (uint8_t(1)<<1)); - float dedxLow = 75.0; - float dedxHigh = 90.0; - Filter dedxFilter = (aod::track::tpcSignal > dedxLow) && (aod::track::tpcSignal < dedxHigh); - float tpcChi2Max = 4.0; - float itsChi2Max = 36; - Filter qualityFilter = (aod::track::tpcChi2NCl < tpcChi2Max) && (aod::track::itsChi2NCl < itsChi2Max); - - void init(InitContext const&) - { - trZ.setObject(new TH1F("Z", "Z", 100, -10., 10.)); - } - - void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator collision, soa::Filtered<soa::Join<aod::Tracks, aod::TracksExtra>> const& tracks) - { - - if (!collision.sel7()) - return; - - centV0M->Fill(collision.centV0M()); - vtxZ->Fill(collision.posZ()); - - for (auto& track : tracks) { - //if (track.pt() < ptlow) - // continue; - ptH->Fill(track.pt()); - trZ->Fill(track.z()); - itsChi2->Fill(track.itsChi2NCl()); - for (int i = 0; i < 6; i++) { - if (track.itsClusterMap() & (uint8_t(1) << i)) - itsHits->Fill(i); - if (track.itsClusterMap() & (uint8_t(1) << i)) - itsHitsVsPt->Fill(i, track.pt()); - } - tpcDedx->Fill(track.tpcInnerParam(), track.tpcSignal()); - tpcChi2->Fill(track.tpcChi2NCl()); - tpcCls->Fill(track.tpcNClsFound()); - for (int i = 0; i < 64; i++) { - if (track.flags() & (uint64_t(1) << i)) - flagsHist->Fill(i); - } - } - for (auto& [t0, t1] : combinations(tracks, tracks)) { - ptCorr->Fill(t0.pt(), t1.pt()); - if (!((t0.itsClusterMap() & (uint8_t(1) << 0)) || (t0.itsClusterMap() & (uint8_t(1) << 1)))) - continue; - if (!((t1.itsClusterMap() & (uint8_t(1) << 0)) || (t1.itsClusterMap() & (uint8_t(1) << 1)))) - continue; - - TLorentzVector p1, p2, p; - p1.SetXYZM(t0.px(), t0.py(), t0.pz(), gkMass); - p2.SetXYZM(t1.px(), t1.py(), t1.pz(), gkMass); - p = p1 + p2; - - if (t0.charge() * t1.charge() < 0) { - invMassPM->Fill(p.M()); - invMassVsPt->Fill(p.M(), p.Pt()); - invMassVsCentrality->Fill(p.M(), collision.centV0M()); - } else { - if (t0.charge() > 0) - invMassPP->Fill(p.M()); - if (t0.charge() < 0) - invMassMM->Fill(p.M()); - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<InvMassAnalysis>("InvMassAnalysis")}; -} diff --git a/Analysis/Tasks/tableMaker.cxx b/Analysis/Tasks/tableMaker.cxx deleted file mode 100644 index 6cd008e70ca89..0000000000000 --- a/Analysis/Tasks/tableMaker.cxx +++ /dev/null @@ -1,91 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// -#include "Framework/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/Multiplicity.h" -#include "Analysis/EventSelection.h" -#include "Analysis/Centrality.h" -#include "Analysis/ReducedInfoTables.h" -#include <TH1F.h> -#include <TMath.h> -#include <iostream> - -using std::cout; -using std::endl; - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -struct TableMaker { - - Produces<aod::ReducedEvents> event; - Produces<aod::ReducedEventsExtended> eventExtended; - Produces<aod::ReducedEventsVtxCov> eventVtxCov; - Produces<aod::ReducedTracks> trackBasic; - Produces<aod::ReducedTracksBarrel> trackBarrel; - Produces<aod::ReducedTracksBarrelCov> trackBarrelCov; - //Produces<aod::ReducedTracksMuon> trackMuon; - - OutputObj<TH1F> vtxZ{TH1F("vtxZ", "vtx Z", 200, -20.0, 20.0)}; - OutputObj<TH1F> vtxX{TH1F("vtxX", "vtx X", 2000, -1.0, 1.0)}; - OutputObj<TH1F> vtxY{TH1F("vtxY", "vtx Y", 2000, -1.0, 1.0)}; - - void init(o2::framework::InitContext&) - { - } - - void process(soa::Join<aod::Collisions, aod::EvSels, aod::Cents>::iterator collision, /*aod::Muons const& tracksMuon,*/ aod::BCs const& bcs, soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov> const& tracksBarrel) - { - uint64_t tag = 0; - if (collision.sel7()) - tag |= (uint64_t(1) << 0); - - vtxZ->Fill(collision.posZ()); - vtxX->Fill(collision.posX()); - vtxY->Fill(collision.posY()); - - event(tag, collision.bc().runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib()); - eventExtended(collision.bc().globalBC(), collision.bc().triggerMask(), collision.collisionTime(), collision.centV0M()); - eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); - - for (auto& track : tracksBarrel) { - - if (track.pt() < 1.0) - continue; - - trackBasic(collision, track.globalIndex(), uint64_t(0), track.pt(), track.eta(), track.phi(), track.charge()); - trackBarrel(track.tpcInnerParam(), track.flags(), track.itsClusterMap(), track.itsChi2NCl(), - track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), - track.tpcNClsShared(), track.tpcChi2NCl(), - track.tpcSignal(), track.trdSignal(), track.tofSignal(), - track.trdChi2(), track.tofChi2(), - track.length()); - trackBarrelCov(track.cYY(), track.cZZ(), track.cSnpSnp(), track.cTglTgl(), track.c1Pt21Pt2()); - } - - /*for (auto& muon : tracksMuon) { - // TODO: add proper information for muon tracks - trackBasic(collision, 0, muon.inverseBendingMomentum(), muon.thetaX(), muon.thetaY(), short(0), short(0), uint64_t(1)); - trackMuon(muon.chi2(), muon.chi2MatchTrigger()); - }*/ - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<TableMaker>("table-maker")}; -} diff --git a/Analysis/Tasks/tableReader.cxx b/Analysis/Tasks/tableReader.cxx deleted file mode 100644 index 24d4f8fe2453e..0000000000000 --- a/Analysis/Tasks/tableReader.cxx +++ /dev/null @@ -1,124 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// -#include "Framework/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Analysis/ReducedInfoTables.h" -#include "Analysis/VarManager.h" -#include "Analysis/HistogramManager.h" -#include <TH1F.h> -#include <TMath.h> -#include <THashList.h> -#include <iostream> -#include <vector> - -using std::cout; -using std::endl; -using std::vector; - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -struct TableReader { - - OutputObj<HistogramManager> fHistMan{"output"}; - - void DefineHistograms() - { - const int kNRuns = 2; - int runs[kNRuns] = {244918, 244919}; - TString runsStr; - for (int i = 0; i < kNRuns; i++) - runsStr += Form("%d;", runs[i]); - VarManager::SetRunNumbers(kNRuns, runs); - - fHistMan->AddHistClass("Event"); - fHistMan->AddHistogram("Event", "VtxZ", "Vtx Z", kFALSE, 60, -15.0, 15.0, VarManager::kVtxZ); // TH1F histogram - fHistMan->AddHistogram("Event", "VtxZ_Run", "Vtx Z", kTRUE, - kNRuns, 0.5, 0.5 + kNRuns, VarManager::kRunId, 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, runsStr.Data()); // TH1F histogram - fHistMan->AddHistogram("Event", "VtxX_VtxY", "Vtx X vs Vtx Y", kFALSE, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY); // TH2F histogram - fHistMan->AddHistogram("Event", "VtxX_VtxY_VtxZ", "vtx x - y - z", kFALSE, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 60, -15.0, 15.0, VarManager::kVtxZ); // TH3F histogram - fHistMan->AddHistogram("Event", "NContrib_vs_VtxZ_prof", "Vtx Z vs ncontrib", kTRUE, 30, -15.0, 15.0, VarManager::kVtxZ, 10, -1., 1., VarManager::kVtxNcontrib); // TProfile histogram - fHistMan->AddHistogram("Event", "VtxZ_vs_VtxX_VtxY_prof", "Vtx Z vs (x,y)", kTRUE, 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 10, -1., 1., VarManager::kVtxZ); // TProfile2D histogram - fHistMan->AddHistogram("Event", "Ncontrib_vs_VtxZ_VtxX_VtxY_prof", "n-contrib vs (x,y,z)", kTRUE, - 100, 0.055, 0.08, VarManager::kVtxX, 100, 0.31, 0.35, VarManager::kVtxY, 30, -15., 15., VarManager::kVtxZ, - "", "", "", VarManager::kVtxNcontrib); // TProfile3D - - double vtxXbinLims[10] = {0.055, 0.06, 0.062, 0.064, 0.066, 0.068, 0.070, 0.072, 0.074, 0.08}; - double vtxYbinLims[7] = {0.31, 0.32, 0.325, 0.33, 0.335, 0.34, 0.35}; - double vtxZbinLims[13] = {-15.0, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 15.0}; - double nContribbinLims[9] = {0.0, 100.0, 200.0, 400.0, 600.0, 1000.0, 1500.0, 2000.0, 4000.0}; - - fHistMan->AddHistogram("Event", "VtxX_VtxY_nonEqualBinning", "Vtx X vs Vtx Y", kFALSE, 9, vtxXbinLims, VarManager::kVtxX, 6, vtxYbinLims, VarManager::kVtxY); // TH2F histogram with custom non-equal binning - - fHistMan->AddHistogram("Event", "VtxZ_weights", "Vtx Z", kFALSE, - 60, -15.0, 15.0, VarManager::kVtxZ, 10, 0., 0., VarManager::kNothing, 10, 0., 0., VarManager::kNothing, - "", "", "", VarManager::kNothing, VarManager::kVtxNcontrib); // TH1F histogram, filled with weights using the vtx n-contributors - - Int_t vars[4] = {VarManager::kVtxX, VarManager::kVtxY, VarManager::kVtxZ, VarManager::kVtxNcontrib}; - TArrayD binLimits[4]; - binLimits[0] = TArrayD(10, vtxXbinLims); - binLimits[1] = TArrayD(7, vtxYbinLims); - binLimits[2] = TArrayD(13, vtxZbinLims); - binLimits[3] = TArrayD(9, nContribbinLims); - fHistMan->AddHistogram("Event", "vtxHisto", "n contrib vs (x,y,z)", 4, vars, binLimits); - - //fHistMan.AddHistogram("Event", "CentVZERO", "CentVZERO", kFALSE, 100, 0.0, 100.0, VarManager::kCentVZERO); // TH1F histogram - //fHistMan.AddHistogram("Event", "CentVZERO_VtxZ_prof", "CentVZERO vs vtxZ", kTRUE, 60, -15.0, 15.0, VarManager::kVtxZ, - // 10, 0.0, 0.0, VarManager::kCentVZERO); // TProfile with <CentVZERO> vs vtxZ - - fHistMan->AddHistClass("Track"); - fHistMan->AddHistogram("Track", "Pt", "p_{T} distribution", kFALSE, 200, 0.0, 20.0, VarManager::kPt); // TH1F histogram - //fHistMan.AddHistogram("Track", "TPCdedx_pIN", "TPC dE/dx vs pIN", kFALSE, 100, 0.0, 20.0, VarManager::kPin, - // 200, 0.0, 200., VarManager::kTPCsignal); // TH2F histogram - } - - void init(o2::framework::InitContext&) - { - VarManager::SetDefaultVarNames(); - fHistMan.setObject(new HistogramManager("analysisHistos", "aa", VarManager::kNVars)); - - fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); - - DefineHistograms(); // define all histograms - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill - } - - //void process(soa::Join<aod::ReducedEvents, aod::ReducedEventsExtended, aod::ReducedEventsVtxCov>::iterator event, - // soa::Join<aod::ReducedTracks, aod::ReducedTracksBarrel> tracks) - void process(aod::ReducedEvents::iterator event, aod::ReducedTracks tracks) - { - // Reset the fgValues array - // TODO: reseting will have to be done selectively, for example run-wise variables don't need to be reset every event, but just updated if the run changes - // The reset can be done selectively, using arguments in the ResetValues() function - VarManager::ResetValues(); - - std::vector<float> eventInfo = {(float)event.runNumber(), event.posX(), event.posY(), event.posZ(), (float)event.numContrib()}; - VarManager::FillEvent(eventInfo); // extract event information and place it in the fgValues array - fHistMan->FillHistClass("Event", VarManager::fgValues); // automatically fill all the histograms in the class Event - - for (auto& track : tracks) { - std::vector<float> trackInfo = {track.pt(), track.eta(), track.phi(), (float)track.charge()}; - VarManager::FillTrack(trackInfo); - fHistMan->FillHistClass("Track", VarManager::fgValues); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<TableReader>("table-reader")}; -} diff --git a/Analysis/Tasks/taskdzero.cxx b/Analysis/Tasks/taskdzero.cxx deleted file mode 100644 index a968774413d05..0000000000000 --- a/Analysis/Tasks/taskdzero.cxx +++ /dev/null @@ -1,55 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/SecondaryVertexHF.h" -#include "DetectorsVertexing/DCAFitterN.h" -#include "ReconstructionDataFormats/Track.h" - -#include <TFile.h> -#include <TH1F.h> -#include <cmath> -#include <array> -#include <cstdlib> - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using std::array; - -struct TaskDzero { - OutputObj<TH1F> hmass{TH1F("hmass", "2-track inv mass", 500, 0, 5.0)}; - OutputObj<TH1F> hptcand{TH1F("hptcand", "pt candidate", 100, 0, 10.0)}; - OutputObj<TH1F> hptprong0{TH1F("hptprong0", "pt prong0", 100, 0, 10.0)}; - OutputObj<TH1F> hptprong1{TH1F("hptprong1", "pt prong1", 100, 0, 10.0)}; - OutputObj<TH1F> hdeclength{TH1F("declength", "decay length", 100, 0., 1.0)}; - OutputObj<TH1F> hdeclengthxy{TH1F("declengthxy", "decay length xy", 100, 0., 1.0)}; - - void process(aod::HfCandProng2 const& hfcandprong2s) - { - for (auto& hfcandprong2 : hfcandprong2s) { - hmass->Fill(hfcandprong2.massD0()); - hmass->Fill(hfcandprong2.massD0bar()); - hptprong0->Fill(hfcandprong2.ptprong0()); - hptprong1->Fill(hfcandprong2.ptprong1()); - hptcand->Fill(hfcandprong2.pt()); - hdeclength->Fill(hfcandprong2.decaylength()); - hdeclengthxy->Fill(hfcandprong2.decaylengthxy()); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<TaskDzero>("hf-taskdzero")}; -} diff --git a/Analysis/Tasks/timestamp.cxx b/Analysis/Tasks/timestamp.cxx index d105fdedd46cf..f13501cbdca7b 100644 --- a/Analysis/Tasks/timestamp.cxx +++ b/Analysis/Tasks/timestamp.cxx @@ -10,46 +10,96 @@ // // A task to fill the timestamp table from run number. -// Uses RunToTimestamp object from CCDB, fails if not available. +// Uses headers from CCDB // // Author: Nicolo' Jacazio on 2020-06-22 #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" -#include "Analysis/RunToTimestamp.h" #include <CCDB/BasicCCDBManager.h> #include "CommonDataFormat/InteractionRecord.h" #include "DetectorsRaw/HBFUtils.h" +#include <map> using namespace o2::framework; using namespace o2::header; using namespace o2; struct TimestampTask { - Produces<aod::Timestamps> ts_table; - RunToTimestamp* converter = nullptr; - Service<o2::ccdb::BasicCCDBManager> ccdb; - Configurable<std::string> path{"ccdb-path", "Test/RunToTimestamp", "path to the ccdb object"}; - Configurable<long> timestamp{"ccdb-timestamp", -1, "timestamp of the object"}; + Produces<aod::Timestamps> ts_table; /// Table with SOR timestamps produced by the task + Service<o2::ccdb::BasicCCDBManager> ccdb; /// Object manager in CCDB + o2::ccdb::CcdbApi ccdb_api; /// API to access CCDB + std::map<int, int>* mapStartOrbit = nullptr; /// Map of the starting orbit for the run + std::pair<int, long> lastCall; /// Last run number processed and its timestamp, needed for caching + std::map<int, long> mapRunToTimestamp; /// Cache of processed run numbers + + // Configurables + Configurable<bool> verbose{"verbose", false, "verbose mode"}; + Configurable<std::string> rct_path{"rct-path", "RCT/RunInformation/", "path to the ccdb RCT objects for the SOR timestamps"}; + Configurable<std::string> start_orbit_path{"start-orbit-path", "Trigger/StartOrbit", "path to the ccdb SOR orbit objects"}; + Configurable<std::string> url{"ccdb-url", "http://ccdb-test.cern.ch:8080", "URL of the CCDB database"}; + Configurable<bool> isMC{"isMC", 0, "0 - data, 1 - MC"}; void init(o2::framework::InitContext&) { LOGF(info, "Initializing TimestampTask"); - converter = ccdb->get<RunToTimestamp>(path.value); - if (converter) { - LOGF(info, "Run-number to timestamp converter found!"); - } else { - LOGF(fatal, "Cannot find run-number to timestamp converter in path '%s'.", path.value.data()); + ccdb->setURL(url.value); // Setting URL of CCDB manager from configuration + LOGF(debug, "Getting SOR orbit map from CCDB url '%s' path '%s'", url.value, start_orbit_path.value); + mapStartOrbit = ccdb->get<std::map<int, int>>(start_orbit_path.value); + if (!mapStartOrbit) { + LOGF(fatal, "Cannot find map of SOR orbits in CCDB in path %s", start_orbit_path.value.data()); + } + ccdb_api.init(url.value); + if (!ccdb_api.isHostReachable()) { + LOGF(fatal, "CCDB host %s is not reacheable, cannot go forward", url.value.data()); } } void process(aod::BC const& bc) { - long timestamp = converter->getTimestamp(bc.runNumber()); - InteractionRecord current(bc.globalBC(), 0); - InteractionRecord initial = o2::raw::HBFUtils::Instance().getFirstIR(); - timestamp += 1000 * (current - initial).bc2ns(); - ts_table(timestamp); + long ts = 0; + if (bc.runNumber() == lastCall.first) { // The run number coincides to the last run processed + LOGF(debug, "Getting timestamp from last call"); + ts = lastCall.second; + } else if (mapRunToTimestamp.count(bc.runNumber())) { // The run number was already requested before: getting it from cache! + LOGF(debug, "Getting timestamp from cache"); + ts = mapRunToTimestamp[bc.runNumber()]; + } else { // The run was not requested before: need to acccess CCDB! + LOGF(debug, "Getting timestamp from CCDB"); + std::map<std::string, std::string> metadata, headers; + const std::string run_path = Form("%s/%i", rct_path.value.data(), bc.runNumber()); + headers = ccdb_api.retrieveHeaders(run_path, metadata, -1); + if (headers.count("SOR") == 0) { + LOGF(fatal, "Cannot find run-number to timestamp in path '%s'.", run_path.data()); + } + ts = atol(headers["SOR"].c_str()); // timestamp of the SOR in ms + + // Adding the timestamp to the cache map + std::pair<std::map<int, long>::iterator, bool> check; + check = mapRunToTimestamp.insert(std::pair<int, long>(bc.runNumber(), ts)); + if (!check.second) { + LOGF(fatal, "Run number %i already existed with a timestamp of %llu", bc.runNumber(), check.first->second); + } + LOGF(info, "Add new run %i with timestamp %llu to cache", bc.runNumber(), ts); + } + + // Setting latest run information + lastCall = std::make_pair(bc.runNumber(), ts); + + if (verbose.value) { + LOGF(info, "Run-number to timestamp found! %i %llu ms", bc.runNumber(), ts); + } + const uint16_t initialBC = 0; // exact bc number not relevant due to ms precision of timestamps + if (!mapStartOrbit->count(bc.runNumber())) { + LOGF(fatal, "Cannot find run %i in mapStartOrbit map", bc.runNumber()); + } + const uint32_t initialOrbit = mapStartOrbit->at(bc.runNumber()); + const uint16_t currentBC = isMC ? initialBC : bc.globalBC() % o2::constants::lhc::LHCMaxBunches; + const uint32_t currentOrbit = isMC ? initialOrbit : bc.globalBC() / o2::constants::lhc::LHCMaxBunches; + const InteractionRecord current(currentBC, currentOrbit); + const InteractionRecord initial(initialBC, initialOrbit); + ts += (current - initial).bc2ns() / 1000000; + ts_table(ts); } }; diff --git a/Analysis/Tasks/trackextension.cxx b/Analysis/Tasks/trackextension.cxx new file mode 100644 index 0000000000000..646a83dbce891 --- /dev/null +++ b/Analysis/Tasks/trackextension.cxx @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +// Task performing basic track selection. +// + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/runDataProcessing.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisCore/trackUtilities.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +//**************************************************************************************** +/** + * Produce the more complicated derived track quantities needed for track selection. + * FIXME: we shall run this only if all other selections are passed to avoid + * FIXME: computing overhead and errors in calculations + */ +//**************************************************************************************** +struct TrackExtensionTask { + + Produces<aod::TracksExtended> extendedTrackQuantities; + + void process(aod::Collision const& collision, aod::FullTracks const& tracks) + { + for (auto& track : tracks) { + + std::array<float, 2> dca{1e10f, 1e10f}; + // FIXME: temporary solution to remove tracks that should not be there after conversion + if (track.trackType() == o2::aod::track::TrackTypeEnum::Run2GlobalTrack && track.itsChi2NCl() != 0.f && track.tpcChi2NCl() != 0.f && std::abs(track.x()) < 10.f) { + float magField = 5.0; // in kG (FIXME: get this from CCDB) + auto trackPar = getTrackPar(track); + trackPar.propagateParamToDCA({collision.posX(), collision.posY(), collision.posZ()}, magField, &dca); + } + extendedTrackQuantities(dca[0], dca[1]); + + // TODO: add realtive pt resolution sigma(pt)/pt \approx pt * sigma(1/pt) + // TODO: add geometrical length / fiducial volume + } + } +}; + +//**************************************************************************************** +/** + * Workflow definition. + */ +//**************************************************************************************** +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask<TrackExtensionTask>("track-extension")}; + return workflow; +} diff --git a/Analysis/Tasks/trackqa.cxx b/Analysis/Tasks/trackqa.cxx new file mode 100644 index 0000000000000..ea92825718c0b --- /dev/null +++ b/Analysis/Tasks/trackqa.cxx @@ -0,0 +1,192 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +// Task producing basic tracking qa histograms +// + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "AnalysisCore/MC.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisCore/TrackSelectionDefaults.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"mc", VariantType::Bool, false, {"Add MC QA histograms."}}, + {"add-cut-qa", VariantType::Int, 0, {"Add track cut QA histograms."}}}; + std::swap(workflowOptions, options); +} +#include "Framework/runDataProcessing.h" + +//**************************************************************************************** +/** + * QA histograms for track quantities. + */ +//**************************************************************************************** +struct TrackQATask { + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; + + Configurable<int> selectedTracks{"select", 1, "Choice of track selection. 0 = no selection, 1 = globalTracks, 2 = globalTracksSDD"}; + + Filter trackFilter = aod::track::isGlobalTrack == (uint8_t) true; + + void init(o2::framework::InitContext&) + { + std::vector<double> ptBinning = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 2.0, 5.0, 10.0, 20.0, 50.0}; + + // kine histograms + histos.add("Kine/pt", "#it{p}_{T};#it{p}_{T} [GeV/c]", kTH1D, {{ptBinning}}); + histos.add("Kine/eta", "#eta;#eta", kTH1D, {{180, -0.9, 0.9}}); + histos.add("Kine/phi", "#phi;#phi [rad]", kTH1D, {{180, 0., 2 * M_PI}}); + + // track histograms + histos.add("TrackPar/x", "track #it{x} position at dca in local coordinate system;#it{x} [cm]", kTH1D, {{200, -0.36, 0.36}}); + histos.add("TrackPar/y", "track #it{y} position at dca in local coordinate system;#it{y} [cm]", kTH1D, {{200, -0.5, 0.5}}); + histos.add("TrackPar/z", "track #it{z} position at dca in local coordinate system;#it{z} [cm]", kTH1D, {{200, -11., 11.}}); + histos.add("TrackPar/alpha", "rotation angle of local wrt. global coordinate system;#alpha [rad]", kTH1D, {{36, -M_PI, M_PI}}); + histos.add("TrackPar/signed1Pt", "track signed 1/#it{p}_{T};#it{q}/#it{p}_{T}", kTH1D, {{200, -8, 8}}); + histos.add("TrackPar/snp", "sinus of track momentum azimuthal angle;snp", kTH1D, {{11, -0.1, 0.1}}); + histos.add("TrackPar/tgl", "tangent of the track momentum dip angle;tgl;", kTH1D, {{200, -1., 1.}}); + histos.add("TrackPar/flags", "track flag;flag bit", kTH1D, {{64, -0.5, 63.5}}); + histos.add("TrackPar/dcaXY", "distance of closest approach in #it{xy} plane;#it{dcaXY} [cm];", kTH1D, {{200, -0.15, 0.15}}); + histos.add("TrackPar/dcaZ", "distance of closest approach in #it{z};#it{dcaZ} [cm];", kTH1D, {{200, -0.15, 0.15}}); + histos.add("TrackPar/length", "track length in cm;#it{Length} [cm];", kTH1D, {{400, 0, 1000}}); + + // its histograms + histos.add("ITS/itsNCls", "number of found ITS clusters;# clusters ITS", kTH1D, {{8, -0.5, 7.5}}); + histos.add("ITS/itsChi2NCl", "chi2 per ITS cluster;chi2 / cluster ITS", kTH1D, {{100, 0, 40}}); + histos.add("ITS/itsHits", "hitmap ITS;layer ITS", kTH1D, {{7, -0.5, 6.5}}); + + // tpc histograms + histos.add("TPC/tpcNClsFindable", "number of findable TPC clusters;# findable clusters TPC", kTH1D, {{165, -0.5, 164.5}}); + histos.add("TPC/tpcNClsFound", "number of found TPC clusters;# clusters TPC", kTH1D, {{165, -0.5, 164.5}}); + histos.add("TPC/tpcNClsShared", "number of shared TPC clusters;# shared clusters TPC", kTH1D, {{165, -0.5, 164.5}}); + histos.add("TPC/tpcNClsCrossedRows", "number of crossed TPC rows;# crossed rows TPC", kTH1D, {{165, -0.5, 164.5}}); + histos.add("TPC/tpcFractionSharedCls", "fraction of shared TPC clusters;fraction shared clusters TPC", kTH1D, {{100, 0., 1.}}); + histos.add("TPC/tpcCrossedRowsOverFindableCls", "crossed TPC rows over findable clusters;crossed rows / findable clusters TPC", kTH1D, {{120, 0.0, 1.2}}); + histos.add("TPC/tpcChi2NCl", "chi2 per cluster in TPC;chi2 / cluster TPC", kTH1D, {{100, 0, 10}}); + + histos.print(); + } + + void process(soa::Filtered<soa::Join<aod::FullTracks, aod::TracksExtended, aod::TrackSelection>>::iterator const& track) + { + // fill kinematic variables + histos.fill(HIST("Kine/pt"), track.pt()); + histos.fill(HIST("Kine/eta"), track.eta()); + histos.fill(HIST("Kine/phi"), track.phi()); + + // fill track parameters + histos.fill(HIST("TrackPar/alpha"), track.alpha()); + histos.fill(HIST("TrackPar/x"), track.x()); + histos.fill(HIST("TrackPar/y"), track.y()); + histos.fill(HIST("TrackPar/z"), track.z()); + histos.fill(HIST("TrackPar/signed1Pt"), track.signed1Pt()); + histos.fill(HIST("TrackPar/snp"), track.snp()); + histos.fill(HIST("TrackPar/tgl"), track.tgl()); + for (unsigned int i = 0; i < 64; i++) { + if (track.flags() & (1 << i)) { + histos.fill(HIST("TrackPar/flags"), i); + } + } + histos.fill(HIST("TrackPar/dcaXY"), track.dcaXY()); + histos.fill(HIST("TrackPar/dcaZ"), track.dcaZ()); + histos.fill(HIST("TrackPar/length"), track.length()); + + // fill ITS variables + histos.fill(HIST("ITS/itsNCls"), track.itsNCls()); + histos.fill(HIST("ITS/itsChi2NCl"), track.itsChi2NCl()); + for (unsigned int i = 0; i < 7; i++) { + if (track.itsClusterMap() & (1 << i)) { + histos.fill(HIST("ITS/itsHits"), i); + } + } + + // fill TPC variables + histos.fill(HIST("TPC/tpcNClsFindable"), track.tpcNClsFindable()); + histos.fill(HIST("TPC/tpcNClsFound"), track.tpcNClsFound()); + histos.fill(HIST("TPC/tpcNClsShared"), track.tpcNClsShared()); + histos.fill(HIST("TPC/tpcNClsCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("TPC/tpcCrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("TPC/tpcFractionSharedCls"), track.tpcFractionSharedCls()); + histos.fill(HIST("TPC/tpcChi2NCl"), track.tpcChi2NCl()); + } +}; + +struct TrackCutQATask { + HistogramRegistry cuts{"Cuts", {}, OutputObjHandlingPolicy::QAObject}; + TrackSelection selectedTracks = getGlobalTrackSelection(); + static constexpr int ncuts = static_cast<int>(TrackSelection::TrackCuts::kNCuts); + void init(InitContext&) + { + cuts.add("single_cut", ";Cut;Tracks", kTH1D, {{ncuts, 0, ncuts}}); + for (int i = 0; i < ncuts; i++) { + cuts.get<TH1>(HIST("single_cut"))->GetXaxis()->SetBinLabel(1 + i, TrackSelection::mCutNames[i].data()); + } + } + + void process(soa::Join<aod::FullTracks, aod::TracksExtended>::iterator const& track) + { + for (int i = 0; i < ncuts; i++) { + if (selectedTracks.IsSelected(track, static_cast<TrackSelection::TrackCuts>(i))) { + cuts.fill(HIST("single_cut"), i); + } + } + } +}; + +//**************************************************************************************** +/** + * QA task including MC truth info. + */ +//**************************************************************************************** +struct TrackQATaskMC { + + HistogramRegistry resolution{"Resolution", {}, OutputObjHandlingPolicy::QAObject}; + + void init(o2::framework::InitContext&){ + + }; + + void process(soa::Join<aod::FullTracks, aod::McTrackLabels>::iterator const& track){ + + }; +}; + +//**************************************************************************************** +/** + * Workflow definition. + */ +//**************************************************************************************** +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + const bool isMC = cfgc.options().get<bool>("mc"); + const int add_cut_qa = cfgc.options().get<int>("add-cut-qa"); + + WorkflowSpec workflow; + workflow.push_back(adaptAnalysisTask<TrackQATask>("track-qa-histograms")); + if (add_cut_qa) { + workflow.push_back(adaptAnalysisTask<TrackCutQATask>("track-cut-qa-histograms")); + } + if (isMC) { + workflow.push_back(adaptAnalysisTask<TrackQATaskMC>("track-qa-histograms-mc")); + } + return workflow; +} diff --git a/Analysis/Tasks/trackselection.cxx b/Analysis/Tasks/trackselection.cxx index efa4eca4c7448..263a00d6de117 100644 --- a/Analysis/Tasks/trackselection.cxx +++ b/Analysis/Tasks/trackselection.cxx @@ -9,99 +9,21 @@ // or submit itself to any jurisdiction. // -// Task performing basic track selection +// Task performing basic track selection. // #include "Framework/AnalysisDataModel.h" #include "Framework/AnalysisTask.h" -#include <TH1F.h> - -#include <cmath> - -#include "Analysis/TrackSelection.h" -#include "Analysis/TrackSelectionTables.h" +#include "Framework/runDataProcessing.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisCore/TrackSelectionDefaults.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisCore/trackUtilities.h" using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) -{ - std::vector<ConfigParamSpec> options{ - {"qa-histos", VariantType::Bool, false, {"Generate QA histograms"}}}; - std::swap(workflowOptions, options); -} - -#include "Framework/runDataProcessing.h" - -// Default track selection requiring one hit in the SPD -TrackSelection getGlobalTrackSelection() -{ - TrackSelection selectedTracks; - selectedTracks.SetMinPt(0.1f); - selectedTracks.SetMaxPt(1e10f); - selectedTracks.SetMinEta(-0.8f); - selectedTracks.SetMaxEta(0.8f); - selectedTracks.SetRequireITSRefit(true); - selectedTracks.SetRequireTPCRefit(true); - selectedTracks.SetMinNCrossedRowsTPC(70); - selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(0.8f); - selectedTracks.SetMaxChi2PerClusterTPC(4.f); - selectedTracks.SetMaxChi2PerClusterITS(36.f); - selectedTracks.SetRequireHitsInITSLayers(1, - {0, 1}); // one hit in any SPD layer - selectedTracks.SetMaxDcaXY(2.4f); - selectedTracks.SetMaxDcaZ(2.f); - return selectedTracks; -} - -// Default track selection requiring no hit in the SPD and one in the innermost -// SDD -TrackSelection getGlobalTrackSelectionSDD() -{ - TrackSelection selectedTracks = getGlobalTrackSelection(); - selectedTracks.ResetITSRequirements(); - selectedTracks.SetRequireNoHitsInITSLayers({0, 1}); // no hit in SPD layers - selectedTracks.SetRequireHitsInITSLayers(1, - {2}); // one hit in first SDD layer - return selectedTracks; -} - -//**************************************************************************************** -/** - * Produce the derived track quantities needed for track selection. - */ -//**************************************************************************************** -struct TrackExtensionTask { - - Produces<aod::TracksExtended> extendedTrackQuantities; - - void process(aod::Collision const& collision, - soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov> const& tracks) - { - float sinAlpha = 0.f; - float cosAlpha = 0.f; - float globalX = 0.f; - float globalY = 0.f; - float dcaXY = 0.f; - float dcaZ = 0.f; - - for (auto& track : tracks) { - - sinAlpha = sin(track.alpha()); - cosAlpha = cos(track.alpha()); - globalX = track.x() * cosAlpha - track.y() * sinAlpha; - globalY = track.x() * sinAlpha + track.y() * cosAlpha; - - dcaXY = track.charge() * sqrt(pow((globalX - collision.posX()), 2) + - pow((globalY - collision.posY()), 2)); - dcaZ = track.charge() * sqrt(pow(track.z() - collision.posZ(), 2)); - - extendedTrackQuantities(dcaXY, dcaZ); - } - } -}; - //**************************************************************************************** /** * Produce track filter table. @@ -119,184 +41,11 @@ struct TrackSelectionTask { globalTracksSDD = getGlobalTrackSelectionSDD(); } - void process(soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra, aod::TracksExtended> const& tracks) - { - for (auto& track : tracks) { - filterTable(globalTracks.IsSelected(track), - globalTracksSDD.IsSelected(track)); - } - } -}; - -//**************************************************************************************** -/** - * Generate QA histograms for track selection. - */ -//**************************************************************************************** -struct TrackQATask { - - Configurable<int> selectedTracks{"select", 1, "Choice of track selection. 0 = no selection, 1 = globalTracks, 2 = globalTracksSDD"}; - //Filter trackFilter = ((selectedTracks == 1) && (aod::track::isGlobalTrack == true)) || ((selectedTracks == 2) && (aod::track::isGlobalTrackSDD == true)); - - // track parameters - OutputObj<TH1F> x{TH1F( - "trackpar-x", "track x position at dca in local coordinate system;x [cm]", - 200, -0.4, 0.4), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> y{TH1F( - "trackpar-y", "track y position at dca in local coordinate system;y [cm]", - 100, -4., 4.), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> z{TH1F( - "trackpar-z", "track z position at dca in local coordinate system;z [cm]", - 100, -20., 20.), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> alpha{ - TH1F("trackpar-alpha", - "rotation angle of local wrt. global coordinate system;#alpha [rad]", - 100, -(M_PI + 0.01), (M_PI + 0.01)), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> signed1Pt{TH1F("trackpar-signed1Pt", - "track signed 1/p_{T};q/p_{T}", 200, - -8, 8), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> snp{TH1F( - "trackpar-snp", "sinus of track momentum azimuthal angle;snp", - 100, -1., 1.), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tgl{TH1F( - "trackpar-tgl", "tangent of the track momentum dip angle;tgl;", - 1000, -2, 2), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> dcaxy{ - TH1F("track-dcaXY", - "distance of closest approach in xy plane;dca-xy [cm];", 200, - -3., 3.), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> dcaz{TH1F( - "track-dcaZ", "distance of closest approach in z;dca-z [cm];", - 200, -3., 3.), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> flag{TH1F("track-flags", "track flag;flag bit", 64, -0.5, 63.5), OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> pt{TH1F("track-pt", "p_{T};p_{T} [GeV/c]", 100, 0., 50.), OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> eta{TH1F("track-eta", "#eta;#eta", 101, -1.0, 1.0), OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> phi{TH1F("track-phi", "#phi;#phi [rad]", 100, 0., 2 * M_PI), OutputObjHandlingPolicy::QAObject}; - - // ITS related quantities - OutputObj<TH1F> itsFoundClusters{ - TH1F("its-foundClusters", "number of found ITS clusters;# clusters ITS", 8, -0.5, 7.5), OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> itsChi2PerCluster{TH1F( - "its-chi2PerCluster", "chi2 per ITS cluster;chi2 / cluster ITS", 100, 0, 40), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> itsHits{TH1F("its-hits", "hitmap ITS;layer ITS", 7, -0.5, 6.5), OutputObjHandlingPolicy::QAObject}; - - // TPC related quantities - OutputObj<TH1F> tpcFindableClusters{TH1F( - "tpc-findableClusters", "number of findable TPC clusters;# clusters TPC", 165, -0.5, 164.5), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tpcFoundClusters{ - TH1F("tpc-foundClusters", "number of found TPC clusters;# clusters TPC", 165, -0.5, 164.5), OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tpcSharedClusters{TH1F( - "tpc-sharedClusters", "number of shared TPC clusters;;# shared clusters TPC", 165, -0.5, 164.5), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tpcFractionSharedClusters{ - TH1F("tpc-fractionSharedClusters", - "fraction of shared TPC clusters;fraction shared clusters TPC", 100, 0., 1.), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tpcCrossedRows{ - TH1F("tpc-crossedRows", "number of crossed TPC rows;# crossed rows TPC", 165, -0.5, 164.5), OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tpcCrossedRowsOverFindableClusters{ - TH1F("tpc-crossedRowsOverFindableClusters", - "crossed TPC rows over findable clusters;crossed rows / findable clusters TPC", 110, 0.0, 1.1), - OutputObjHandlingPolicy::QAObject}; - - OutputObj<TH1F> tpcChi2PerCluster{ - TH1F("tpc-chi2PerCluster", "chi2 per cluster in TPC;chi2 / cluster TPC", - 100, 0, 10), - OutputObjHandlingPolicy::QAObject}; - - // collision related quantities - OutputObj<TH2F> collisionPos{ - TH2F("collision-xy", "Position of collision;x [cm];y [cm]", 100, - -0.5, 0.5, 100, -0.5, 0.5), - OutputObjHandlingPolicy::QAObject}; - - void init(o2::framework::InitContext&) {} - - void process(aod::Collision const& collision, - soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksCov, aod::TracksExtended, - aod::TrackSelection> const& tracks) + void process(soa::Join<aod::FullTracks, aod::TracksExtended> const& tracks) { - - collisionPos->Fill(collision.posX(), collision.posY()); - for (auto& track : tracks) { - - if (selectedTracks == 1 && !track.isGlobalTrack()) - continue; // FIXME: this should be replaced by framework filter - else if (selectedTracks == 2 && !track.isGlobalTrackSDD()) - continue; - - // TPC - tpcFindableClusters->Fill(track.tpcNClsFindable()); - tpcFoundClusters->Fill(track.tpcNClsFound()); - tpcSharedClusters->Fill(track.tpcNClsShared()); - tpcCrossedRows->Fill(track.tpcNClsCrossedRows()); - - tpcCrossedRowsOverFindableClusters->Fill( - track.tpcCrossedRowsOverFindableCls()); - tpcFractionSharedClusters->Fill(track.tpcFractionSharedCls()); - tpcChi2PerCluster->Fill( - track.tpcChi2NCl()); - - // ITS - itsFoundClusters->Fill(track.itsNCls()); - itsChi2PerCluster->Fill(track.itsChi2NCl()); - for (unsigned int i = 0; i < 7; i++) { - if (track.itsClusterMap() & (1 << i)) - itsHits->Fill(i); - } - - // Tracks - alpha->Fill(track.alpha()); - x->Fill(track.x()); - y->Fill(track.y()); - z->Fill(track.z()); - - dcaxy->Fill(track.dcaXY()); - dcaz->Fill(track.dcaZ()); - - signed1Pt->Fill(track.signed1Pt()); - snp->Fill(track.snp()); - tgl->Fill(track.tgl()); - - for (unsigned int i = 0; i < 64; i++) { - if (track.flags() & (1 << i)) - flag->Fill(i); - } - - pt->Fill(track.pt()); - eta->Fill(track.eta()); - phi->Fill(track.phi()); + filterTable((uint8_t)globalTracks.IsSelected(track), + (uint8_t)globalTracksSDD.IsSelected(track)); } } }; @@ -308,11 +57,6 @@ struct TrackQATask { //**************************************************************************************** WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - bool createQAplots = cfgc.options().get<bool>("qa-histos"); - WorkflowSpec workflow{ - adaptAnalysisTask<TrackExtensionTask>("track-extension"), - adaptAnalysisTask<TrackSelectionTask>("track-selection")}; - if (createQAplots) - workflow.push_back(adaptAnalysisTask<TrackQATask>("track-qa-histograms")); + WorkflowSpec workflow{adaptAnalysisTask<TrackSelectionTask>("track-selection")}; return workflow; } diff --git a/Analysis/Tasks/vertexerhf.cxx b/Analysis/Tasks/vertexerhf.cxx deleted file mode 100644 index 3ab8b2b19a52e..0000000000000 --- a/Analysis/Tasks/vertexerhf.cxx +++ /dev/null @@ -1,289 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/ASoAHelpers.h" -#include "Analysis/SecondaryVertex.h" -#include "Analysis/RecoDecay.h" -#include "DetectorsVertexing/DCAFitterN.h" -#include "ReconstructionDataFormats/Track.h" - -#include <TFile.h> -#include <TH1F.h> -#include <cmath> -#include <array> - -using namespace o2; -using namespace o2::framework; - -struct DecayVertexBuilder2Prong { - // primary vertex position - OutputObj<TH1F> hvtxp_x_out{TH1F("hvertexx", "x primary vtx", 100, -10., 10.)}; - OutputObj<TH1F> hvtxp_y_out{TH1F("hvertexy", "y primary vtx", 100, -10., 10.)}; - OutputObj<TH1F> hvtxp_z_out{TH1F("hvertexz", "z primary vtx", 100, -10., 10.)}; - // track distributions before cuts - OutputObj<TH1F> hpt_nocuts{TH1F("hpt_nocuts", "pt tracks (#GeV)", 100, 0., 10.)}; - OutputObj<TH1F> htgl_nocuts{TH1F("htgl_nocuts", "tgl tracks (#GeV)", 100, 0., 10.)}; - OutputObj<TH1F> hitsmap_nocuts{TH1F("hitsmap_nocuts", "hitsmap", 100, 0., 100.)}; - // track distributions after cuts - OutputObj<TH1F> hpt_cuts{TH1F("hpt_cuts", "pt tracks (#GeV)", 100, 0., 10.)}; - OutputObj<TH1F> htgl_cuts{TH1F("htgl_cuts", "tgl tracks (#GeV)", 100, 0., 10.)}; - OutputObj<TH1F> hitsmap_cuts{TH1F("hitsmap_cuts", "hitsmap", 100, 0., 100.)}; - // secondary vertex position - OutputObj<TH1F> hvtx_x_out{TH1F("hvtx_x", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hvtx_y_out{TH1F("hvtx_y", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hvtx_z_out{TH1F("hvtx_z", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hchi2dca{TH1F("hchi2dca", "chi2 DCA decay", 1000, 0., 0.0002)}; - - Produces<aod::SecVtx2Prong> secvtx2prong; - //Configurable<std::string> triggersel{"triggersel", "test", "A string configurable"}; - Configurable<int> triggerindex{"triggerindex", -1, "trigger index"}; - - void process(aod::Collision const& collision, - aod::BCs const& bcs, - soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra> const& tracks) - { - //LOGP(error, "Trigger selection {}", std::string{triggersel}); - int trigindex = int{triggerindex}; - if (trigindex != -1) { - LOGF(info, "Selecting on trigger bit %d", trigindex); - uint64_t triggerMask = collision.bc().triggerMask(); - bool isTriggerClassFired = triggerMask & 1ul << (trigindex - 1); - if (!isTriggerClassFired) - return; - } - - LOGF(info, "N. of Tracks for collision: %d", tracks.size()); - o2::vertexing::DCAFitterN<2> df; - df.setBz(5.0); - // After finding the vertex, propagate tracks to the DCA. This is default anyway - df.setPropagateToPCA(true); - // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway - df.setMaxR(200); - // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway - df.setMaxDZIni(4); - // stop iterations if max correction is below this value. This is default anyway - df.setMinParamChange(1e-3); - // stop iterations if chi2 improves by less that this factor - df.setMinRelChi2Change(0.9); - - hvtxp_x_out->Fill(collision.posX()); - hvtxp_y_out->Fill(collision.posY()); - hvtxp_z_out->Fill(collision.posZ()); - for (auto it0 = tracks.begin(); it0 != tracks.end(); ++it0) { - auto& track_0 = *it0; - - UChar_t clustermap_0 = track_0.itsClusterMap(); - //fill track distribution before selection - hitsmap_nocuts->Fill(clustermap_0); - hpt_nocuts->Fill(track_0.pt()); - htgl_nocuts->Fill(track_0.tgl()); - bool isselected_0 = track_0.tpcNClsFound() > 70 && track_0.flags() & 0x4; - isselected_0 = isselected_0 && (TESTBIT(clustermap_0, 0) || TESTBIT(clustermap_0, 1)); - if (!isselected_0) - continue; - //fill track distribution after selection - hitsmap_cuts->Fill(clustermap_0); - hpt_cuts->Fill(track_0.pt()); - htgl_cuts->Fill(track_0.tgl()); - - float x0_ = track_0.x(); - float alpha0_ = track_0.alpha(); - std::array<float, 5> arraypar0 = {track_0.y(), track_0.z(), track_0.snp(), - track_0.tgl(), track_0.signed1Pt()}; - std::array<float, 15> covpar0 = {track_0.cYY(), track_0.cZY(), track_0.cZZ(), - track_0.cSnpY(), track_0.cSnpZ(), - track_0.cSnpSnp(), track_0.cTglY(), track_0.cTglZ(), - track_0.cTglSnp(), track_0.cTglTgl(), - track_0.c1PtY(), track_0.c1PtZ(), track_0.c1PtSnp(), - track_0.c1PtTgl(), track_0.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar0(x0_, alpha0_, arraypar0, covpar0); - for (auto it1 = it0 + 1; it1 != tracks.end(); ++it1) { - auto& track_1 = *it1; - UChar_t clustermap_1 = track_1.itsClusterMap(); - bool isselected_1 = track_1.tpcNClsFound() > 70 && track_1.flags() & 0x4; - isselected_1 = isselected_1 && (TESTBIT(clustermap_1, 0) || TESTBIT(clustermap_1, 1)); - if (!isselected_1) - continue; - if (track_0.signed1Pt() * track_1.signed1Pt() > 0) - continue; - float x1_ = track_1.x(); - float alpha1_ = track_1.alpha(); - std::array<float, 5> arraypar1 = {track_1.y(), track_1.z(), track_1.snp(), - track_1.tgl(), track_1.signed1Pt()}; - std::array<float, 15> covpar1 = {track_1.cYY(), track_1.cZY(), track_1.cZZ(), - track_1.cSnpY(), track_1.cSnpZ(), - track_1.cSnpSnp(), track_1.cTglY(), track_1.cTglZ(), - track_1.cTglSnp(), track_1.cTglTgl(), - track_1.c1PtY(), track_1.c1PtZ(), track_1.c1PtSnp(), - track_1.c1PtTgl(), track_1.c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar1(x1_, alpha1_, arraypar1, covpar1); - df.setUseAbsDCA(true); - int nCand = df.process(trackparvar0, trackparvar1); - if (nCand == 0) - continue; - const auto& vtx = df.getPCACandidate(); - LOGF(info, "vertex x %f", vtx[0]); - hvtx_x_out->Fill(vtx[0]); - hvtx_y_out->Fill(vtx[1]); - hvtx_z_out->Fill(vtx[2]); - o2::track::TrackParCov trackdec0 = df.getTrack(0); - o2::track::TrackParCov trackdec1 = df.getTrack(1); - std::array<float, 3> pvec0; - std::array<float, 3> pvec1; - trackdec0.getPxPyPzGlo(pvec0); - trackdec1.getPxPyPzGlo(pvec1); - float masspion = 0.140; - float masskaon = 0.494; - float mass_ = invmass2prongs(pvec0[0], pvec0[1], pvec0[2], masspion, - pvec1[0], pvec1[1], pvec1[2], masskaon); - float masssw_ = invmass2prongs(pvec0[0], pvec0[1], pvec0[2], masskaon, - pvec1[0], pvec1[1], pvec1[2], masspion); - secvtx2prong(track_0.collisionId(), - collision.posX(), collision.posY(), collision.posZ(), - vtx[0], vtx[1], vtx[2], track_0.globalIndex(), - pvec0[0], pvec0[1], pvec0[2], track_0.y(), - track_1.globalIndex(), pvec1[0], pvec1[1], pvec1[2], track_1.y(), - mass_, masssw_); - hchi2dca->Fill(df.getChi2AtPCACandidate()); - } - } - } -}; - -struct CandidateBuildingDzero { - Produces<aod::Cand2Prong> cand2prong; - void process(aod::SecVtx2Prong const& secVtx2Prongs, - soa::Join<aod::Tracks, aod::TracksCov, aod::TracksExtra> const& tracks) - { - LOGF(info, "NEW EVENT CANDIDATE"); - - o2::vertexing::DCAFitterN<2> df; - df.setBz(5.0); - // After finding the vertex, propagate tracks to the DCA. This is default anyway - df.setPropagateToPCA(true); - // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway - df.setMaxR(200); - // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway - df.setMaxDZIni(4); - // stop iterations if max correction is below this value. This is default anyway - df.setMinParamChange(1e-3); - // stop iterations if chi2 improves by less that this factor - df.setMinRelChi2Change(0.9); - - for (auto& secVtx2prong : secVtx2Prongs) { - LOGF(INFO, " ------- new event ---------"); - LOGF(INFO, " track0 y from secvtx tab. %f", secVtx2prong.y0()); - LOGF(INFO, " track0 y from track %f", secVtx2prong.index0().y()); - LOGF(INFO, " track1 y from secvtx table %f", secVtx2prong.y1()); - LOGF(INFO, " track1 y from track %f", secVtx2prong.index1().y()); - - float x0_ = secVtx2prong.index0().x(); - float alpha0_ = secVtx2prong.index0().alpha(); - std::array<float, 5> arraypar0 = {secVtx2prong.index0().y(), secVtx2prong.index0().z(), - secVtx2prong.index0().snp(), secVtx2prong.index0().tgl(), - secVtx2prong.index0().signed1Pt()}; - std::array<float, 15> covpar0 = {secVtx2prong.index0().cYY(), secVtx2prong.index0().cZY(), - secVtx2prong.index0().cZZ(), secVtx2prong.index0().cSnpY(), - secVtx2prong.index0().cSnpZ(), secVtx2prong.index0().cSnpSnp(), - secVtx2prong.index0().cTglY(), secVtx2prong.index0().cTglZ(), - secVtx2prong.index0().cTglSnp(), secVtx2prong.index0().cTglTgl(), - secVtx2prong.index0().c1PtY(), secVtx2prong.index0().c1PtZ(), - secVtx2prong.index0().c1PtSnp(), secVtx2prong.index0().c1PtTgl(), - secVtx2prong.index0().c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar0(x0_, alpha0_, arraypar0, covpar0); - - float x1_ = secVtx2prong.index1().x(); - float alpha1_ = secVtx2prong.index1().alpha(); - std::array<float, 5> arraypar1 = {secVtx2prong.index1().y(), secVtx2prong.index1().z(), - secVtx2prong.index1().snp(), secVtx2prong.index1().tgl(), - secVtx2prong.index1().signed1Pt()}; - std::array<float, 15> covpar1 = {secVtx2prong.index1().cYY(), secVtx2prong.index1().cZY(), - secVtx2prong.index1().cZZ(), secVtx2prong.index1().cSnpY(), - secVtx2prong.index1().cSnpZ(), secVtx2prong.index1().cSnpSnp(), - secVtx2prong.index1().cTglY(), secVtx2prong.index1().cTglZ(), - secVtx2prong.index1().cTglSnp(), secVtx2prong.index1().cTglTgl(), - secVtx2prong.index1().c1PtY(), secVtx2prong.index1().c1PtZ(), - secVtx2prong.index1().c1PtSnp(), secVtx2prong.index1().c1PtTgl(), - secVtx2prong.index1().c1Pt21Pt2()}; - o2::track::TrackParCov trackparvar1(x1_, alpha1_, arraypar1, covpar1); - - df.setUseAbsDCA(true); - //FIXME: currently I rebuild the vertex for each track-track pair and - //select the candidate via its index. It is redundant cause the secondary - //vertex recostruction is performed more than once for each dca candidate - int nCand = df.process(trackparvar0, trackparvar1); - if (nCand == 0) { - LOGF(error, " DCAFitter failing in the candidate building: it should not happen"); - } - const auto& secvtx = df.getPCACandidate(); - float masspion = 0.140; - float masskaon = 0.494; - float mass_ = invmass2prongs(secVtx2prong.px0(), secVtx2prong.py0(), - secVtx2prong.pz0(), masspion, - secVtx2prong.px1(), secVtx2prong.py1(), - secVtx2prong.pz1(), masskaon); - float masssw_ = invmass2prongs(secVtx2prong.px0(), secVtx2prong.py0(), - secVtx2prong.pz0(), masskaon, - secVtx2prong.px1(), secVtx2prong.py1(), - secVtx2prong.pz1(), masspion); - cand2prong(mass_, masssw_); - o2::track::TrackParCov trackdec0 = df.getTrack(0); - o2::track::TrackParCov trackdec1 = df.getTrack(1); - std::array<float, 3> pvec0; - std::array<float, 3> pvec1; - trackdec0.getPxPyPzGlo(pvec0); - trackdec1.getPxPyPzGlo(pvec1); - LOGF(info, "Pt track 0 from table %f and from calc %f", secVtx2prong.px0(), pvec0[0]); - if (abs(secVtx2prong.px0() - pvec0[0]) > 0.000000001) { - LOGF(info, "BIG ERRROR"); - } - } - } -}; - -struct DzeroHistoTask { - // secondary vertex position - OutputObj<TH1F> hvtx_x_outt{TH1F("hvtx_xt", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hvtx_y_outt{TH1F("hvtx_yt", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hvtx_z_outt{TH1F("hvtx_zt", "2-track vtx", 100, -0.1, 0.1)}; - OutputObj<TH1F> hmass_nocuts_out{TH1F("hmass_nocuts", "2-track inv mass", 500, 0, 5.0)}; - OutputObj<TH1F> hdecayxy{TH1F("hdecayxy", "decay length xy", 100, 0., 1.0)}; - OutputObj<TH1F> hdecayxyz{TH1F("hdecayxyz", "decay length", 100, 0., 1.0)}; - - void process(soa::Join<aod::Cand2Prong, aod::SecVtx2Prong> const& secVtx2Prongs) - { - LOGF(info, "NEW EVENT"); - - for (auto& secVtx2prong : secVtx2Prongs) { - hvtx_y_outt->Fill(secVtx2prong.posdecayy()); - hvtx_z_outt->Fill(secVtx2prong.posdecayz()); - hvtx_x_outt->Fill(secVtx2prong.posdecayx()); - hvtx_y_outt->Fill(secVtx2prong.posdecayy()); - hvtx_z_outt->Fill(secVtx2prong.posdecayz()); - hdecayxy->Fill(secVtx2prong.decaylengthXY()); - hdecayxyz->Fill(secVtx2prong.decaylength()); - hmass_nocuts_out->Fill(secVtx2prong.mass()); - hmass_nocuts_out->Fill(secVtx2prong.massbar()); - LOGF(info, "new event"); - LOGF(info, "mass %f", secVtx2prong.mass()); - LOGF(info, "mass from cand %f", secVtx2prong.massD0()); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - adaptAnalysisTask<DecayVertexBuilder2Prong>("vertexerhf-decayvertexbuilder2prong"), - adaptAnalysisTask<CandidateBuildingDzero>("vertexerhf-candidatebuildingDzero"), - adaptAnalysisTask<DzeroHistoTask>("vertexerhf-Dzerotask")}; -} diff --git a/Analysis/Tasks/weakDecayIndices.cxx b/Analysis/Tasks/weakDecayIndices.cxx new file mode 100644 index 0000000000000..10890bea7bf09 --- /dev/null +++ b/Analysis/Tasks/weakDecayIndices.cxx @@ -0,0 +1,54 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +using namespace o2; +using namespace o2::framework; + +// Tasks to build transient indices to group V0s and cascades to collisions + +struct IndexV0s { + Produces<aod::TransientV0s> transientV0s; + + void process(aod::StoredV0s const& v0s, aod::FullTracks const& tracks) + { + for (auto& v0 : v0s) { + if (v0.posTrack().collisionId() != v0.negTrack().collisionId()) { + LOGF(WARNING, "V0 %d has inconsistent collision information (%d, %d)", v0.globalIndex(), v0.posTrack().collisionId(), v0.negTrack().collisionId()); + } + transientV0s(v0.posTrack().collisionId()); + } + } +}; + +// NOTE These tasks have to be split because for the cascades, V0s and not StoredV0s are needed +struct IndexCascades { + Produces<aod::TransientCascades> transientCascades; + + void process(aod::V0s const& v0s, aod::StoredCascades const& cascades, aod::FullTracks const& tracks) + { + for (auto& cascade : cascades) { + if (cascade.bachelor().collisionId() != cascade.v0().posTrack().collisionId() || cascade.v0().posTrack().collisionId() != cascade.v0().negTrack().collisionId()) { + LOGF(WARNING, "Cascade %d has inconsistent collision information (%d, %d, %d)", cascade.globalIndex(), cascade.bachelor().collisionId(), cascade.v0().posTrack().collisionId(), cascade.v0().negTrack().collisionId()); + } + transientCascades(cascade.bachelor().collisionId()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<IndexV0s>("weak-decay-indices-v0"), + adaptAnalysisTask<IndexCascades>("weak-decay-indices-cascades"), + }; +} diff --git a/Analysis/Tutorials/CMakeLists.txt b/Analysis/Tutorials/CMakeLists.txt index b3717f9ccfe94..07edbd72dd749 100644 --- a/Analysis/Tutorials/CMakeLists.txt +++ b/Analysis/Tutorials/CMakeLists.txt @@ -57,11 +57,21 @@ o2_add_dpl_workflow(histograms PUBLIC_LINK_LIBRARIES O2::Framework COMPONENT_NAME AnalysisTutorial) +o2_add_dpl_workflow(histograms-full-tracks + SOURCES src/histogramsFullTracks.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + COMPONENT_NAME AnalysisTutorial) + o2_add_dpl_workflow(filters SOURCES src/filters.cxx PUBLIC_LINK_LIBRARIES O2::Framework COMPONENT_NAME AnalysisTutorial) +o2_add_dpl_workflow(partitions + SOURCES src/partitions.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + COMPONENT_NAME AnalysisTutorial) + o2_add_dpl_workflow(aodwriter SOURCES src/aodwriter.cxx PUBLIC_LINK_LIBRARIES O2::Framework @@ -102,3 +112,63 @@ o2_add_dpl_workflow(ccdbaccess PUBLIC_LINK_LIBRARIES O2::AnalysisCore O2::CCDB O2::Framework O2::AnalysisDataModel COMPONENT_NAME AnalysisTutorial) +o2_add_dpl_workflow(weak-decay-iteration + SOURCES src/weakDecayIteration.cxx + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(extended-columns + SOURCES src/extendedColumns.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(zdc-vzero-iteration + SOURCES src/ZDCVZeroIteration.cxx + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(hist-helpers-test + SOURCES src/histHelpersTest.cxx + PUBLIC_LINK_LIBRARIES O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(histogram-registry + SOURCES src/histogramRegistry.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME AnalysisTutorial) + +o2_add_library(ConfigurableCut + SOURCES src/configurableCut.cxx + PUBLIC_LINK_LIBRARIES O2::AnalysisCore) + +o2_target_root_dictionary(ConfigurableCut + HEADERS include/Analysis/configurableCut.h + LINKDEF src/ConfigurableCutLinkDef.h) + +o2_add_dpl_workflow(configurable-objects + SOURCES src/configurableObjects.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::ConfigurableCut + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(muon-iteration + SOURCES src/muonIteration.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(schema-evolution + SOURCES src/schemaEvolution.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(efficiency-per-run + SOURCES src/efficiencyPerRun.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(efficiency-global + SOURCES src/efficiencyGlobal.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME AnalysisTutorial) + +o2_add_dpl_workflow(compatible-bcs + SOURCES src/compatibleBCs.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::AnalysisCore O2::AnalysisDataModel + COMPONENT_NAME AnalysisTutorial) diff --git a/Analysis/Tutorials/include/Analysis/configurableCut.h b/Analysis/Tutorials/include/Analysis/configurableCut.h new file mode 100644 index 0000000000000..55be646f6a8a6 --- /dev/null +++ b/Analysis/Tutorials/include/Analysis/configurableCut.h @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CONFIGURABLECUT_H +#define CONFIGURABLECUT_H + +#include "Framework/Array2D.h" +#include <iosfwd> +#include <Rtypes.h> +#include <TMath.h> + +static constexpr double default_matrix[3][3] = {{1.1, 1.2, 1.3}, {2.1, 2.2, 2.3}, {3.1, 3.2, 3.3}}; + +class configurableCut +{ + public: + configurableCut(float cut_ = 2., int state_ = 1, bool option_ = true, + std::vector<float> bins_ = {0.5, 1.5, 2.5}, + std::vector<std::string> labels_ = {"l1", "l2", "l3"}, + o2::framework::Array2D<double> cuts_ = {&default_matrix[0][0], 3, 3}) + : cut{cut_}, state{state_}, option{option_}, bins{bins_}, labels{labels_}, cuts{cuts_} + { + } + + bool method(float arg) const; + + void setCut(float cut_); + float getCut() const; + + void setState(int state_); + int getState() const; + + void setOption(bool option_); + bool getOption() const; + + void setBins(std::vector<float> bins_); + std::vector<float> getBins() const; + + void setLabels(std::vector<std::string> labels_); + std::vector<std::string> getLabels() const; + + void setCuts(o2::framework::Array2D<double> cuts_); + o2::framework::Array2D<double> getCuts() const; + + private: + float cut; + int state; + bool option; + std::vector<float> bins; + std::vector<std::string> labels; + o2::framework::Array2D<double> cuts; + + ClassDef(configurableCut, 5); +}; + +std::ostream& operator<<(std::ostream& os, configurableCut const& c); + +#endif // CONFIGURABLECUT_H diff --git a/Analysis/Tutorials/src/ConfigurableCutLinkDef.h b/Analysis/Tutorials/src/ConfigurableCutLinkDef.h new file mode 100644 index 0000000000000..4ad2560c106b7 --- /dev/null +++ b/Analysis/Tutorials/src/ConfigurableCutLinkDef.h @@ -0,0 +1,19 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; +#pragma link C++ nestedclasses; +#pragma link C++ class configurableCut + ; +#pragma link C++ class std::vector < int> + ; +#pragma link C++ class std::vector < float> + ; +#pragma link C++ class std::vector < double> + ; +#pragma link C++ class std::vector < bool> + ; diff --git a/Analysis/Tutorials/src/ZDCVZeroIteration.cxx b/Analysis/Tutorials/src/ZDCVZeroIteration.cxx new file mode 100644 index 0000000000000..a4fbc3eda0dcd --- /dev/null +++ b/Analysis/Tutorials/src/ZDCVZeroIteration.cxx @@ -0,0 +1,97 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// These example tasks show how to access the ZDC and FV0A information which belongs to a collision +// The association is made through the BC column (and in Run 3 may not be unique!) + +// This example accesses the collisions and the related FV0A information. +// Note that one has to subscribe to aod::FV0As const& to load +// the relevant data even if you access the data itself through collisionMatched.fv0a() +// Here the "sparse" matcher is used which means, there can be collisions without FV0A information +// To find out, collisionMatched.has_fv0a() has to be called. Otherwise collisionMatched.fv0a() will fail. +// NOTE: subscribing to Collisions separately will lead to a circular dependency due to forwarding + +struct IterateV0 { + void process(aod::CollisionMatchedRun2Sparse const& collisionMatched, aod::FV0As const&) + { + LOGF(INFO, "Vertex = %f", collisionMatched.posZ()); + if (collisionMatched.has_fv0a()) { + auto v0a = collisionMatched.fv0a(); + LOGF(info, "V0A: %f %f", v0a.amplitude()[0], v0a.amplitude()[1]); + } else { + LOGF(INFO, "No V0A info"); + } + } +}; + +// This example is identical to IterateV0, but uses the exclusive match. This means that collisions where any +// of the tables asked for in Run2MatchedExclusive (see AnalysisDataModel.h) are missing are not there. +// Therefore, the syntax is more complicated because we cannot join against Collision +// (the tables have different number of entries) +// Only to be used if one is sure that all your events have the desired information +struct IterateV0Exclusive { + void process(aod::Run2MatchedExclusive::iterator const& matcher, aod::Collisions const&, aod::FV0As const&) + { + LOGF(INFO, "Vertex = %f", matcher.collision().posZ()); + auto fv0a = matcher.fv0a(); + LOGF(info, "V0: %f %f", fv0a.amplitude()[0], fv0a.amplitude()[1]); + } +}; + +// This example builds on IterateV0 and in addition accesses also the tracks grouped to the specific collision. +// The tracks are directly accessed through its pointer as usual +struct IterateV0Tracks { + void process(aod::CollisionMatchedRun2Sparse const& collisionMatched, aod::FV0As const&, aod::Tracks const& tracks) + { + LOGF(INFO, "Vertex = %f. %d tracks", collisionMatched.posZ(), tracks.size()); + if (collisionMatched.has_fv0a()) { + auto v0a = collisionMatched.fv0a(); + LOGF(info, "V0A: %f %f", v0a.amplitude()[0], v0a.amplitude()[1]); + } else { + LOGF(INFO, "No V0A info"); + } + } +}; + +// This example accesses V0 and ZDC information +struct IterateV0ZDC { + void process(aod::CollisionMatchedRun2Sparse const& collisionMatched, aod::FV0As const&, aod::Zdcs const&) + { + LOGF(INFO, "Vertex = %f", collisionMatched.posZ()); + if (collisionMatched.has_fv0a()) { + auto v0a = collisionMatched.fv0a(); + LOGF(info, "V0A: %f %f", v0a.amplitude()[0], v0a.amplitude()[1]); + } else { + LOGF(INFO, "No V0A info"); + } + if (collisionMatched.has_zdc()) { + LOGF(INFO, "ZDC: E1 = %.3f; E2 = %.3f", collisionMatched.zdc().energyZEM1(), collisionMatched.zdc().energyZEM2()); + } else { + LOGF(INFO, "No ZDC info"); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<IterateV0>("iterate-v0"), + adaptAnalysisTask<IterateV0Exclusive>("iterate-v0-exclusive"), + adaptAnalysisTask<IterateV0Tracks>("iterate-v0-tracks"), + adaptAnalysisTask<IterateV0ZDC>("iterate-v0-zdc"), + }; +} diff --git a/Analysis/Tutorials/src/aodwriter.cxx b/Analysis/Tutorials/src/aodwriter.cxx index 7b5ecbde6d6f2..72df95cc46cf5 100644 --- a/Analysis/Tutorials/src/aodwriter.cxx +++ b/Analysis/Tutorials/src/aodwriter.cxx @@ -139,7 +139,7 @@ struct ATask { table_uno(phi, eta, mom); table_due(ok, phi, eta, mom, pt); table_tre(phi, eta, mom, id); - LOGF(INFO, "Values (%i): (%i %f, %f, %f, %f, %i)", cnt, ok, eta, phi, mom, pt, id); + //LOGF(INFO, "Values (%i): (%i %f, %f, %f, %f, %i)", cnt, ok, eta, phi, mom, pt, id); cnt++; } diff --git a/Analysis/Tutorials/src/associatedExample.cxx b/Analysis/Tutorials/src/associatedExample.cxx index 424c9298b87bc..30f6adbeb1f95 100644 --- a/Analysis/Tutorials/src/associatedExample.cxx +++ b/Analysis/Tutorials/src/associatedExample.cxx @@ -15,15 +15,13 @@ namespace o2::aod { namespace etaphi { -DECLARE_SOA_COLUMN(Eta, etas, float); -DECLARE_SOA_COLUMN(Phi, phis, float); -DECLARE_SOA_COLUMN(Pt, pts, float); +DECLARE_SOA_COLUMN(AEta, etas, float); +DECLARE_SOA_COLUMN(APhi, phis, float); +DECLARE_SOA_COLUMN(APt, pts, float); } // namespace etaphi DECLARE_SOA_TABLE(EtaPhi, "AOD", "ETAPHI", - etaphi::Eta, etaphi::Phi); -DECLARE_SOA_TABLE(EtaPhiPt, "AOD", "ETAPHIPT", - etaphi::Eta, etaphi::Phi, etaphi::Pt); + etaphi::AEta, etaphi::APhi); namespace collision { @@ -43,16 +41,13 @@ using namespace o2::framework; // we need GCC 7.4+ for that struct ATask { Produces<aod::EtaPhi> etaphi; - Produces<aod::EtaPhiPt> etaphipt; void process(aod::Tracks const& tracks) { for (auto& track : tracks) { float phi = asin(track.snp()) + track.alpha() + static_cast<float>(M_PI); float eta = log(tan(0.25f * static_cast<float>(M_PI) - 0.5f * atan(track.tgl()))); - float pt = 1.f / track.signed1Pt(); etaphi(eta, phi); - etaphipt(eta, phi, pt); } } }; @@ -72,23 +67,54 @@ struct BTask { LOGF(INFO, "ID: %d", collision.globalIndex()); LOGF(INFO, "Tracks: %d", extTracks.size()); for (auto& track : extTracks) { - LOGF(INFO, "(%f, %f) - (%f, %f)", track.eta(), track.phi(), track.etas(), track.phis()); + LOGF(INFO, "(%f, %f) - (%f, %f)", track.eta(), track.phiraw(), track.etas(), track.phis()); } } }; -struct CTask { - void process(aod::Collision const& collision, soa::Concat<aod::EtaPhi, aod::EtaPhiPt> const& concatenated) +struct TTask { + using myCol = soa::Join<aod::Collisions, aod::CollisionsExtra>; + expressions::Filter multfilter = aod::collision::mult > 10; + void process(soa::Filtered<myCol>::iterator const& col, aod::Tracks const& tracks) { - LOGF(INFO, "ID: %d", collision.globalIndex()); - LOGF(INFO, "Tracks: %d", concatenated.size()); + LOGF(INFO, "[direct] ID: %d; %d == %d", col.globalIndex(), col.mult(), tracks.size()); + if (tracks.size() > 0) { + auto track0 = tracks.begin(); + LOGF(INFO, "[index ] ID: %d; %d == %d", track0.collision_as<myCol>().globalIndex(), track0.collision_as<myCol>().mult(), tracks.size()); + } } }; -struct TTask { - void process(soa::Join<aod::Collisions, aod::CollisionsExtra>::iterator const& col, aod::Tracks const& tracks) +struct ZTask { + using myCol = soa::Join<aod::Collisions, aod::CollisionsExtra>; + + void process(myCol const& collisions, aod::Tracks const& tracks) { - LOGF(INFO, "ID: %d; %d == %d", col.globalIndex(), col.mult(), tracks.size()); + auto multbin0_10 = collisions.select(aod::collision::mult >= 0 && aod::collision::mult < 10); + auto multbin10_30 = collisions.select(aod::collision::mult >= 10 && aod::collision::mult < 30); + auto multbin30_100 = collisions.select(aod::collision::mult >= 30 && aod::collision::mult < 100); + + LOGF(INFO, "Bin 0-10"); + for (auto& col : multbin0_10) { + auto groupedTracks = tracks.sliceBy(aod::track::collisionId, col.globalIndex()); + LOGF(INFO, "Collision %d; Ntrk = %d vs %d", col.globalIndex(), col.mult(), groupedTracks.size()); + if (groupedTracks.size() > 0) { + auto track = groupedTracks.begin(); + LOGF(INFO, "Track 0 belongs to collision %d at Z = %f", track.collisionId(), track.collision_as<myCol>().posZ()); + } + } + + LOGF(INFO, "Bin 10-30"); + for (auto& col : multbin10_30) { + auto groupedTracks = tracks.sliceBy(aod::track::collisionId, col.globalIndex()); + LOGF(INFO, "Collision %d; Ntrk = %d vs %d", col.globalIndex(), col.mult(), groupedTracks.size()); + } + + LOGF(INFO, "Bin 30-100"); + for (auto& col : multbin30_100) { + auto groupedTracks = tracks.sliceBy(aod::track::collisionId, col.globalIndex()); + LOGF(INFO, "Collision %d; Ntrk = %d vs %d", col.globalIndex(), col.mult(), groupedTracks.size()); + } } }; @@ -97,7 +123,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) return WorkflowSpec{ adaptAnalysisTask<ATask>("produce-etaphi"), adaptAnalysisTask<BTask>("consume-etaphi"), - adaptAnalysisTask<CTask>("consume-etaphi-twice"), adaptAnalysisTask<MTask>("produce-mult"), - adaptAnalysisTask<TTask>("consume-mult")}; + adaptAnalysisTask<TTask>("consume-mult"), + adaptAnalysisTask<ZTask>("partition-mult")}; } diff --git a/Analysis/Tutorials/src/ccdbaccess.cxx b/Analysis/Tutorials/src/ccdbaccess.cxx index c697236bf2ec2..0d2718e50eac6 100644 --- a/Analysis/Tutorials/src/ccdbaccess.cxx +++ b/Analysis/Tutorials/src/ccdbaccess.cxx @@ -8,14 +8,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// A tutorial task to retrieve objects from CCDB given a run number -// -// Author: Nicolo' Jacazio on 2020-06-22 +/// +/// \brief A tutorial task to retrieve objects from CCDB given a run number. +/// The tutorial shows also how to use timestamps in your analysis. +/// This task requires to access the timestamp table in order to be working. +/// Currently this is done by adding `o2-analysis-timestamp` to the workflow +/// \author Nicolo' Jacazio +/// \since 2020-06-22 +/// #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" -#include "Analysis/RunToTimestamp.h" +#include "Framework/AnalysisDataModel.h" #include <CCDB/BasicCCDBManager.h> #include "CommonDataFormat/InteractionRecord.h" @@ -36,21 +40,21 @@ struct TimestampUserTask { // Set CCDB url ccdb->setURL(url.value); // Enabling object caching - ccdb->setCachingEnabled(true); + ccdb->setCaching(true); // Not later than now objects ccdb->setCreatedNotAfter(nolaterthan.value); } - void process(soa::Join<aod::BCs, aod::Timestamps> const& iter) + void process(aod::Collision const& collision, aod::BCsWithTimestamps const& /*bc*/) { - for (auto i : iter) { - auto obj = ccdb->getForTimeStamp<TH2F>(path.value, i.timestamp()); - if (obj) { - LOGF(info, "Found object!"); - obj->Print("all"); - } else { - LOGF(warning, "Object not found!"); - } + auto bc = collision.bc_as<aod::BCsWithTimestamps>(); + LOGF(info, "Getting object %s for run number %i from timestamp=%llu", path.value.data(), bc.runNumber(), bc.timestamp()); + auto obj = ccdb->getForTimeStamp<TH2F>(path.value, bc.timestamp()); + if (obj) { + LOGF(info, "Found object!"); + obj->Print("all"); + } else { + LOGF(warning, "Object not found!"); } } }; diff --git a/Analysis/Tutorials/src/collisionTracksIteration.cxx b/Analysis/Tutorials/src/collisionTracksIteration.cxx index 8bf3b940309e8..e2d1f8a07efe3 100644 --- a/Analysis/Tutorials/src/collisionTracksIteration.cxx +++ b/Analysis/Tutorials/src/collisionTracksIteration.cxx @@ -23,10 +23,10 @@ using namespace o2::framework; struct ATask { void process(aod::Collision const&, aod::Tracks const& tracks) { - // FIXME: to see some output, we create the histogram - // for every timeframe. In general this is not the way it - // should be done. LOGF(info, "Tracks for collision: %d", tracks.size()); + for (auto& track : tracks) { + LOGF(info, "This track has pT = %f GeV/c", track.pt()); + } } }; diff --git a/Analysis/Tutorials/src/compatibleBCs.cxx b/Analysis/Tutorials/src/compatibleBCs.cxx new file mode 100644 index 0000000000000..658e154dd4ed8 --- /dev/null +++ b/Analysis/Tutorials/src/compatibleBCs.cxx @@ -0,0 +1,102 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "CommonConstants/LHCConstants.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// In Run 3 the association between collisions and bunch crossings is not unique as the time of a collision vertex +// is derived from the track information themselves. + +template <typename T> +T getCompatibleBCs(aod::Collision const& collision, T const& bcs) +{ + auto bcIter = collision.bc_as<T>(); + + // due to the filling scheme the most probably BC may not be the one estimated from the collision time + uint64_t mostProbableBC = bcIter.globalBC(); + uint64_t meanBC = mostProbableBC - std::lround(collision.collisionTime() / (o2::constants::lhc::LHCBunchSpacingNS / 1000)); + int deltaBC = std::ceil(collision.collisionTimeRes() / (o2::constants::lhc::LHCBunchSpacingNS / 1000) * 4); + + LOGF(INFO, "BC range: %llu - %llu", meanBC - deltaBC, meanBC + deltaBC); + + // find slice of BCs table with BC in [meanBC - deltaBC, meanBC + deltaBC] + int64_t maxBCId = bcIter.globalIndex(); + int moveCount = 0; // optimize to avoid to re-create the iterator + while (bcIter != bcs.end() && bcIter.globalBC() <= meanBC + deltaBC && bcIter.globalBC() >= meanBC - deltaBC) { + LOGF(DEBUG, "Table id %d BC %llu", bcIter.globalIndex(), bcIter.globalBC()); + maxBCId = bcIter.globalIndex(); + ++bcIter; + ++moveCount; + } + + bcIter.moveByIndex(-moveCount); // Move back to original position + int64_t minBCId = collision.bcId(); + while (bcIter != bcs.begin() && bcIter.globalBC() <= meanBC + deltaBC && bcIter.globalBC() >= meanBC - deltaBC) { + LOGF(DEBUG, "Table id %d BC %llu", bcIter.globalIndex(), bcIter.globalBC()); + minBCId = bcIter.globalIndex(); + --bcIter; + } + + LOGF(INFO, "Will consider BC entries from %d to %d", minBCId, maxBCId); + + T slice{{bcs.asArrowTable()->Slice(minBCId, maxBCId - minBCId + 1)}, (uint64_t)minBCId}; + bcs.copyIndexBindings(slice); + return slice; +} + +// Example 1 (academic, because it is not enough to just access the BC table): +// This task shows how to loop over the bunch crossings which are compatible with the estimated collision time. +struct CompatibleBCs { + void process(aod::Collision const& collision, aod::BCs const& bcs) + { + LOGF(INFO, "Vertex with most probably BC %llu and collision time %f +- %f ps", collision.bc().globalBC(), collision.collisionTime(), collision.collisionTimeRes()); + + auto bcSlice = getCompatibleBCs(collision, bcs); + + for (auto& bc : bcSlice) { + LOGF(info, "This collision may belong to BC %lld", bc.globalBC()); + } + } +}; + +// Example 2: +// Using getCompatibleBCs to retrieve the entries of several tables linked through the BC table (here FT0 and FV0A) +// and making sure that one has the direct association between them +// Note that one has to subscribe to aod::FT0s and aod::FV0As to load +// the relevant data even if you access the data itself through m.ft0() and m.fv0a() +struct CompatibleT0V0A { + void process(aod::Collision const& collision, soa::Join<aod::BCs, aod::Run3MatchedToBCSparse> const& bct0s, aod::FT0s& ft0s, aod::FV0As& fv0as) + { + // NOTE collision.bc() causes SEGV here because we have only subscribed to BCs joined, therefore: + auto bc = collision.bc_as<soa::Join<aod::BCs, aod::Run3MatchedToBCSparse>>(); + LOGF(INFO, "Vertex with most probable BC %llu and collision time %f +- %f ps", bc.globalBC(), collision.collisionTime(), collision.collisionTimeRes()); + + auto bcSlice = getCompatibleBCs(collision, bct0s); + + for (auto& bc : bcSlice) { + if (bc.has_ft0() && bc.has_fv0a()) { + LOGF(info, "This collision may belong to BC %lld and has T0 timeA: %f and V0A time: %f", bc.globalBC(), bc.ft0().timeA(), bc.fv0a().time()); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<CompatibleBCs>("compatible-bcs"), + adaptAnalysisTask<CompatibleT0V0A>("compatible-t0-v0a"), + }; +} diff --git a/Analysis/Tutorials/src/configurableCut.cxx b/Analysis/Tutorials/src/configurableCut.cxx new file mode 100644 index 0000000000000..dc3b4b4fe928c --- /dev/null +++ b/Analysis/Tutorials/src/configurableCut.cxx @@ -0,0 +1,83 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "Analysis/configurableCut.h" +#include <iostream> + +std::ostream& operator<<(std::ostream& os, configurableCut const& c) +{ + os << "Cut value: " << c.getCut() << "; state: " << c.getState(); + return os; +} + +bool configurableCut::method(float arg) const +{ + return arg > cut; +} + +void configurableCut::setCut(float cut_) +{ + cut = cut_; +} + +float configurableCut::getCut() const +{ + return cut; +} + +void configurableCut::setState(int state_) +{ + state = state_; +} + +int configurableCut::getState() const +{ + return state; +} + +void configurableCut::setOption(bool option_) +{ + option = option_; +} + +bool configurableCut::getOption() const +{ + return option; +} + +void configurableCut::setBins(std::vector<float> bins_) +{ + bins = std::move(bins_); +} + +std::vector<float> configurableCut::getBins() const +{ + return bins; +} + +void configurableCut::setLabels(std::vector<std::string> labels_) +{ + labels = std::move(labels_); +} + +std::vector<std::string> configurableCut::getLabels() const +{ + return labels; +} + +void configurableCut::setCuts(o2::framework::Array2D<double> cuts_) +{ + cuts = std::move(cuts_); +} + +o2::framework::Array2D<double> configurableCut::getCuts() const +{ + return cuts; +} diff --git a/Analysis/Tutorials/src/configurableCut.json b/Analysis/Tutorials/src/configurableCut.json new file mode 100644 index 0000000000000..33d4294b13286 --- /dev/null +++ b/Analysis/Tutorials/src/configurableCut.json @@ -0,0 +1,21 @@ +{ + "internal-dpl-clock": "", + "internal-dpl-aod-reader": { + "aod-file": "aod.root", + "time-limit": "0", + "start-value-enumeration": "0", + "end-value-enumeration": "-1", + "step-value-enumeration": "1" + }, + "internal-dpl-aod-spawner": "", + "configurable-demo": { + "cut": { + "cut": "0.1", + "state": "1" + }, + "mutable_cut": { + "cut": "0.2", + "state": "-1" + } + } +} diff --git a/Analysis/Tutorials/src/configurableObjects.cxx b/Analysis/Tutorials/src/configurableObjects.cxx new file mode 100644 index 0000000000000..e28f71bb0713b --- /dev/null +++ b/Analysis/Tutorials/src/configurableObjects.cxx @@ -0,0 +1,102 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Analysis/configurableCut.h" + +#include <sstream> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// This task demonstrates how to use configurable to wrap classes +/// use it with supplied configuration: "configurableObject.json" + +template <typename T> +auto printArray(std::vector<T> const& vec) +{ + std::stringstream ss; + ss << "[" << vec[0]; + for (auto i = 1u; i < vec.size(); ++i) { + ss << ", " << vec[i]; + } + ss << "]"; + return ss.str(); +} + +template <typename T> +auto printMatrix(Array2D<T> const& m) +{ + std::stringstream ss; + ss << "[[" << m(0, 0); + for (auto i = 1u; i < m.cols; ++i) { + ss << "," << m(0, i); + } + for (auto i = 1u; i < m.rows; ++i) { + ss << "], [" << m(i, 0); + for (auto j = 1u; j < m.cols; ++j) { + ss << "," << m(i, j); + } + } + ss << "]]"; + return ss.str(); +} + +static constexpr float defaultm[3][4] = {{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}; + +struct ConfigurableObjectDemo { + Configurable<configurableCut> cut{"cut", {0.5, 1, true}, "generic cut"}; + MutableConfigurable<configurableCut> mutable_cut{"mutable_cut", {1., 2, false}, "generic cut"}; + + // note that size is fixed by this declaration - externally supplied vector needs to be the same size! + Configurable<std::vector<int>> array{"array", {0, 0, 0, 0, 0, 0, 0}, "generic array"}; + Configurable<Array2D<float>> vmatrix{"matrix", {&defaultm[0][0], 3, 4}, "generic matrix"}; + + void init(InitContext const&){}; + void process(aod::Collision const&, aod::Tracks const& tracks) + { + LOGF(INFO, "Cut1: %.3f; Cut2: %.3f", cut, mutable_cut); + LOGF(INFO, "Cut1 bins: %s; Cut2 bins: %s", printArray(cut->getBins()), printArray(mutable_cut->getBins())); + LOGF(INFO, "Cut1 labels: %s; Cut2 labels: %s", printArray(cut->getLabels()), printArray(mutable_cut->getLabels())); + auto vec = (std::vector<int>)array; + LOGF(INFO, "Array: %s", printArray(vec).c_str()); + LOGF(INFO, "Matrix: %s", printMatrix((Array2D<float>)vmatrix)); + for (auto const& track : tracks) { + if (track.globalIndex() % 500 == 0) { + std::string decision1; + std::string decision2; + if (cut->method(std::abs(track.eta()))) { + decision1 = "true"; + } else { + decision1 = "false"; + } + if (mutable_cut->method(std::abs(track.eta()))) { + decision2 = "true"; + } else { + decision2 = "false"; + } + LOGF(INFO, "Cut1: %s; Cut2: %s", decision1, decision2); + if (decision2 == "false") { + mutable_cut->setState(-1); + } else { + mutable_cut->setState(1); + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<ConfigurableObjectDemo>("configurable-demo")}; +} diff --git a/Analysis/Tutorials/src/dynamicColumns.cxx b/Analysis/Tutorials/src/dynamicColumns.cxx index ae851d6e519bd..a4c2492d7f443 100644 --- a/Analysis/Tutorials/src/dynamicColumns.cxx +++ b/Analysis/Tutorials/src/dynamicColumns.cxx @@ -13,46 +13,25 @@ namespace o2::aod { -namespace etaphi +namespace extension { -DECLARE_SOA_COLUMN(Tgl, tgl, float); -DECLARE_SOA_COLUMN(Snp, snp, float); -DECLARE_SOA_COLUMN(Alpha, alpha, float); -DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float tgl) { return log(tan(0.25 * M_PI - 0.5 * atan(tgl))); }); -DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float snp, float alpha) { return asin(snp) + alpha + M_PI; }); +DECLARE_SOA_DYNAMIC_COLUMN(P2, p2, [](float p) { return p * p; }); } // namespace etaphi -DECLARE_SOA_TABLE(EtaPhis, "AOD", "ETAPHI", - etaphi::Tgl, etaphi::Snp, etaphi::Alpha, - etaphi::Eta<etaphi::Tgl>, - etaphi::Phi<etaphi::Snp, etaphi::Alpha>); } // namespace o2::aod using namespace o2; using namespace o2::framework; -// This is a very simple example showing how to iterate over tracks -// and create a new collection for them. -// FIXME: this should really inherit from AnalysisTask but -// we need GCC 7.4+ for that struct ATask { - Produces<aod::EtaPhis> etaphi; - - void process(aod::Tracks const& tracks) + void process(aod::Collision const&, aod::Tracks const& tracks) { - for (auto& track : tracks) { - etaphi(track.tgl(), track.snp(), track.alpha()); - } - } -}; - -struct BTask { - void process(aod::EtaPhis const& etaPhis) - { - for (auto& etaPhi : etaPhis) { - auto phi = asin(etaPhi.snp()) + etaPhi.alpha() + M_PI; - auto eta = log(tan(0.25 * M_PI - 0.5 * atan(etaPhi.tgl()))); - - //LOGF(INFO, "(%f, %f, %f, %f)", etaPhi.eta(), etaPhi.phi(), eta - etaPhi.eta(), phi - etaPhi.phi()); + auto table_with_extra_dynamic_columns = soa::Attach<aod::Tracks, aod::extension::P2<aod::track::P>>(tracks); + for (auto& row : table_with_extra_dynamic_columns) { + if (row.trackType() != 3) { + if (row.index() % 100 == 0) { + LOGF(info, "P^2 = %.3f", row.p2()); + } + } } } }; @@ -61,14 +40,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) { // create and use table return WorkflowSpec{ - adaptAnalysisTask<ATask>("produce-track-copy"), - adaptAnalysisTask<BTask>("check-eta-phi")}; - - // only create table -> tabwle is written to file - //return WorkflowSpec{ - // adaptAnalysisTask<ATask>("produce-track-copy")}; - - // only use table -> table is read from file - //return WorkflowSpec{ - // adaptAnalysisTask<BTask>("check-eta-phi")}; + adaptAnalysisTask<ATask>("attach-showcase")}; } diff --git a/Analysis/Tutorials/src/efficiencyGlobal.cxx b/Analysis/Tutorials/src/efficiencyGlobal.cxx new file mode 100644 index 0000000000000..7f742ca493c2d --- /dev/null +++ b/Analysis/Tutorials/src/efficiencyGlobal.cxx @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 This example demonstrates how the CCDB can be used to store an efficiency object which is valid for a full train run +/// The objects are uploaded with +/// The objects are uploaded with https://alimonitor.cern.ch/ccdb/upload.jsp +/// A sufficiently large time stamp interval should be given to span all runs under consideration +/// NOTE If an efficiency object per run is needed, please check the example efficiencyPerRun.cxx +/// + +#include <chrono> + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include <CCDB/BasicCCDBManager.h> + +using namespace o2::framework; +using namespace o2; + +struct EfficiencyGlobal { + Service<ccdb::BasicCCDBManager> ccdb; + Configurable<std::string> path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; + Configurable<std::string> url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable<long> nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + + OutputObj<TH1F> pt{TH1F("pt", "pt", 20, 0., 10.)}; + + TH1F* efficiency = nullptr; + + void init(InitContext&) + { + ccdb->setURL(url.value); + // Enabling object caching, otherwise each call goes to the CCDB server + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + // Not later than now, will be replaced by the value of the train creation + // This avoids that users can replace objects **while** a train is running + ccdb->setCreatedNotAfter(nolaterthan.value); + LOGF(info, "Getting object %s", path.value.data()); + efficiency = ccdb->getForTimeStamp<TH1F>(path.value, nolaterthan.value); + if (!efficiency) { + LOGF(fatal, "Efficiency object not found!"); + } + } + + void process(aod::Collision const& collision, aod::Tracks const& tracks) + { + for (auto& track : tracks) { + pt->Fill(track.pt(), efficiency->GetBinContent(efficiency->FindBin(track.pt()))); + //LOGF(info, "Efficiency %f for pt %f", efficiency->GetBinContent(efficiency->FindBin(track.pt())), track.pt()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{adaptAnalysisTask<EfficiencyGlobal>("EfficiencyGlobal")}; +} diff --git a/Analysis/Tutorials/src/efficiencyPerRun.cxx b/Analysis/Tutorials/src/efficiencyPerRun.cxx new file mode 100644 index 0000000000000..72636d35c6f77 --- /dev/null +++ b/Analysis/Tutorials/src/efficiencyPerRun.cxx @@ -0,0 +1,70 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tutorial task to retrieve objects from CCDB given a run number. +/// This example demonstrates how the CCDB can be used to store an efficiency object which is valid only for a specific time +/// interval (e.g. for a run) +/// The objects are uploaded with https://alimonitor.cern.ch/ccdb/upload.jsp +/// Different timestamps intervals can be given. +/// You need to run this with the o2-analysis-timestamp task +/// NOTE If only one efficiency object for all runs is needed, this code is not optimal. In this case please check the example: +/// efficiencyGlobal.cxx +/// + +#include <chrono> + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include <CCDB/BasicCCDBManager.h> + +using namespace o2::framework; +using namespace o2; + +struct EfficiencyPerRun { + Service<ccdb::BasicCCDBManager> ccdb; + Configurable<std::string> path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; + Configurable<std::string> url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable<long> nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + + OutputObj<TH1F> pt{TH1F("pt", "pt", 20, 0., 10.)}; + + void init(InitContext&) + { + ccdb->setURL(url.value); + // Enabling object caching, otherwise each call goes to the CCDB server + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + // Not later than now, will be replaced by the value of the train creation + // This avoids that users can replace objects **while** a train is running + ccdb->setCreatedNotAfter(nolaterthan.value); + } + + void process(aod::Collision const& collision, aod::BCsWithTimestamps const&, aod::Tracks const& tracks) + { + auto bc = collision.bc_as<aod::BCsWithTimestamps>(); + LOGF(info, "Getting object %s for run number %i from timestamp=%llu", path.value.data(), bc.runNumber(), bc.timestamp()); + auto efficiency = ccdb->getForTimeStamp<TH1F>(path.value, bc.timestamp()); + if (!efficiency) { + LOGF(fatal, "Efficiency object not found!"); + } + + for (auto& track : tracks) { + pt->Fill(track.pt(), efficiency->GetBinContent(efficiency->FindBin(track.pt()))); + //LOGF(info, "Efficiency %f for pt %f", efficiency->GetBinContent(efficiency->FindBin(track.pt())), track.pt()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{adaptAnalysisTask<EfficiencyPerRun>("EfficiencyPerRun")}; +} diff --git a/Analysis/Tutorials/src/extendedColumns.cxx b/Analysis/Tutorials/src/extendedColumns.cxx new file mode 100644 index 0000000000000..7d1ec1185ac41 --- /dev/null +++ b/Analysis/Tutorials/src/extendedColumns.cxx @@ -0,0 +1,44 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +namespace extension +{ +DECLARE_SOA_EXPRESSION_COLUMN(P2, p2, float, track::p* track::p); +} // namespace extension +} // namespace o2::aod + +using namespace o2; +using namespace o2::framework; + +struct ATask { + void process(aod::Collision const&, aod::Tracks const& tracks) + { + auto table_extension = soa::Extend<aod::Tracks, aod::extension::P2>(tracks); + for (auto& row : table_extension) { + if (row.trackType() != 3) { + if (row.index() % 100 == 0) { + LOGF(info, "P^2 = %.3f", row.p2()); + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + // create and use table + return WorkflowSpec{ + adaptAnalysisTask<ATask>("extend-showcase")}; +} diff --git a/Analysis/Tutorials/src/filters.cxx b/Analysis/Tutorials/src/filters.cxx index 1b5ea6bdae3f2..3df02db0427ae 100644 --- a/Analysis/Tutorials/src/filters.cxx +++ b/Analysis/Tutorials/src/filters.cxx @@ -15,11 +15,18 @@ namespace o2::aod { namespace etaphi { -DECLARE_SOA_COLUMN(Eta, eta2, float); -DECLARE_SOA_COLUMN(Phi, phi2, float); +DECLARE_SOA_COLUMN(NPhi, nphi, float); +DECLARE_SOA_EXPRESSION_COLUMN(CosPhi, cosphi, float, ncos(aod::etaphi::nphi)); } // namespace etaphi -DECLARE_SOA_TABLE(EtaPhi, "AOD", "ETAPHI", - etaphi::Eta, etaphi::Phi); +namespace track +{ +DECLARE_SOA_EXPRESSION_COLUMN(SPt, spt, float, nabs(aod::track::sigma1Pt / aod::track::signed1Pt)); +} +DECLARE_SOA_TABLE(TPhi, "AOD", "TPHI", + etaphi::NPhi); +DECLARE_SOA_EXTENDED_TABLE_USER(EPhi, TPhi, "EPHI", aod::etaphi::CosPhi); +using etracks = soa::Join<aod::Tracks, aod::TracksCov>; +DECLARE_SOA_EXTENDED_TABLE_USER(MTracks, etracks, "MTRACK", aod::track::SPt); } // namespace o2::aod using namespace o2; @@ -31,38 +38,61 @@ using namespace o2::framework::expressions; // FIXME: this should really inherit from AnalysisTask but // we need GCC 7.4+ for that struct ATask { - Produces<aod::EtaPhi> etaphi; - + Produces<aod::TPhi> tphi; void process(aod::Tracks const& tracks) { for (auto& track : tracks) { - etaphi(track.eta(), track.phi()); + tphi(track.phi()); } } }; struct BTask { + Spawns<aod::EPhi> ephi; + Spawns<aod::MTracks> mtrk; + float fPI = static_cast<float>(M_PI); float ptlow = 0.5f; float ptup = 2.0f; - Filter ptFilter_a = aod::track::pt2 > (ptlow * ptlow); - Filter ptFilter_b = aod::track::pt2 < (ptup * ptup); + Filter ptFilter_a = aod::track::pt > ptlow; + Filter ptFilter_b = aod::track::pt < ptup; - // Filter ptFilter = ((aod::track::signed1Pt < 1.0f / ptlow) && (aod::track::signed1Pt > 1.0f / ptup)) || ((aod::track::signed1Pt < -1.0f / ptup) && (aod::track::signed1Pt < -1.0f / ptlow)); float etalow = -1.0f; float etaup = 1.0f; - Filter etafilter = (aod::etaphi::eta2 < etaup) && (aod::etaphi::eta2 > etalow); + Filter etafilter = (aod::track::eta < etaup) && (aod::track::eta > etalow); - // Filter etaFilter = (aod::track::tgl < tan(0.5f * fPI - 2.0f * atan(exp(etalow)))) && (aod::track::tgl > tan(0.5f * fPI - 2.0f * atan(exp(etaup)))); float philow = 1.0f; float phiup = 2.0f; - Filter phifilter = (aod::etaphi::phi2 < phiup) && (aod::etaphi::phi2 > philow); + Filter phifilter = (aod::etaphi::nphi < phiup) && (aod::etaphi::nphi > philow); + + Filter posZfilter = nabs(aod::collision::posZ) < 10.0f; + + void process(soa::Filtered<aod::Collisions>::iterator const& collision, soa::Filtered<soa::Join<aod::Tracks, aod::TPhi>> const& tracks) + { + LOGF(INFO, "Collision: %d [N = %d out of %d], -10 < %.3f < 10", + collision.globalIndex(), tracks.size(), tracks.tableSize(), collision.posZ()); + for (auto& track : tracks) { + LOGF(INFO, "id = %d; eta: %.3f < %.3f < %.3f; phi: %.3f < %.3f < %.3f; pt: %.3f < %.3f < %.3f", + track.collisionId(), etalow, track.eta(), etaup, philow, track.nphi(), phiup, ptlow, track.pt(), ptup); + } + } +}; + +struct CTask { + void process(aod::Collision const&, soa::Join<aod::Tracks, aod::EPhi> const& tracks) + { + for (auto& track : tracks) { + LOGF(INFO, "%.3f == %.3f", track.cosphi(), std::cos(track.phi())); + } + } +}; - void process(aod::Collision const& collision, soa::Filtered<soa::Join<aod::Tracks, aod::EtaPhi>> const& tracks) +struct DTask { + Filter notTracklet = aod::track::trackType != static_cast<uint8_t>(aod::track::TrackTypeEnum::Run2Tracklet); + void process(aod::Collision const&, soa::Filtered<aod::MTracks> const& tracks) { - LOGF(INFO, "Collision: %d [N = %d]", collision.globalIndex(), tracks.size()); for (auto& track : tracks) { - LOGF(INFO, "id = %d; eta: %.3f < %.3f < %.3f; phi: %.3f < %.3f < %.3f; pt: %.3f < %.3f < %.3f", track.collisionId(), etalow, track.eta2(), etaup, philow, track.phi2(), phiup, ptlow, track.pt(), ptup); + LOGF(INFO, "%.3f == %.3f", track.spt(), std::abs(track.sigma1Pt() / track.signed1Pt())); } } }; @@ -70,6 +100,8 @@ struct BTask { WorkflowSpec defineDataProcessing(ConfigContext const&) { return WorkflowSpec{ - adaptAnalysisTask<ATask>("produce-etaphi"), - adaptAnalysisTask<BTask>("consume-etaphi")}; + adaptAnalysisTask<ATask>("produce-normalizedphi"), + adaptAnalysisTask<BTask>("consume-normalizedphi"), + adaptAnalysisTask<CTask>("consume-spawned-ephi"), + adaptAnalysisTask<DTask>("consume-spawned-mtracks")}; } diff --git a/Analysis/Tutorials/src/histHelpersTest.cxx b/Analysis/Tutorials/src/histHelpersTest.cxx new file mode 100644 index 0000000000000..34e39bcece9c4 --- /dev/null +++ b/Analysis/Tutorials/src/histHelpersTest.cxx @@ -0,0 +1,210 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/AnalysisTask.h" +#include "AnalysisCore/HistHelpers.h" + +#include "Framework/AnalysisDataModel.h" +#include "Framework/runDataProcessing.h" +#include <cmath> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::experimental::histhelpers; + +struct HistHelpersTest { + + // some unique names for the histograms + enum HistNamesTest : uint8_t { + test_1d_TH1D, + test_2d_TH2F, + test_3d_TH3I, + test_1d_TH1D_Weight, + test_2d_TH2F_VarBinningY, + + test_3d_THnI, + test_5d_THnSparseI, + + test_1d_TProfile, + test_1d_TProfile_Weight, + test_2d_TProfile2D, + test_3d_TProfile3D, + + test_7d_THnF_first, + test_7d_THnF_second, + test_8d_THnC_third, + }; + enum HistNamesKine : uint8_t { + pt, + eta, + phi, + }; + enum HistNamesTPC : uint8_t { + tpcNClsFound, + tpcNClsCrossedRows, + tpcChi2NCl, + }; + enum HistNamesITS : uint8_t { + itsChi2NCl, + }; + + OutputObj<HistArray> test{HistArray("Test"), OutputObjHandlingPolicy::QAObject}; + OutputObj<HistArray> kine{HistArray("Kine"), OutputObjHandlingPolicy::QAObject}; + OutputObj<HistFolder> tpc{HistFolder("TPC"), OutputObjHandlingPolicy::QAObject}; + OutputObj<HistList> its{HistList("ITS"), OutputObjHandlingPolicy::QAObject}; + + OutputObj<THnF> standaloneHist{"standaloneHist", OutputObjHandlingPolicy::QAObject}; + + void init(o2::framework::InitContext&) + { + // add some plain and simple histograms + test->Add<test_1d_TH1D>(new TH1D("test_1d_TH1D", ";x", 100, 0., 50.)); + test->Add<test_2d_TH2F>(new TH2F("test_2d_TH2F", ";x;y", 100, -0.5, 0.5, 100, -0.5, 0.5)); + test->Add<test_3d_TH3I>(new TH3I("test_3d_TH3I", ";x;y;z", 100, 0., 20., 100, 0., 20., 100, 0., 20.)); + + // alternatively use Hist to generate the histogram and add it to container afterwards + Hist sameAsBefore; + sameAsBefore.AddAxis("x", "x", 100, 0., 50.); + // via Hist::Create() we can generate the actual root histogram with the requested axes + // Parameters: the name and optionally the decision wether to call SumW2 in case we want to fill this histogram with weights + test->Add<test_1d_TH1D_Weight>(sameAsBefore.Create<TH1D>("test_1d_TH1D_Weight", true)); + + // this helper enables us to have combinations of flexible + fixed binning in 2d or 3d histograms + // (which are not available via default root constructors) + Hist sameButDifferent; + sameButDifferent.AddAxis("x", "x", 100, -0.5, 0.5); + sameButDifferent.AddAxis("y", "y", {-0.5, -0.48, -0.3, 0.4, 0.5}); // use variable binning for y axsis this time + test->Add<test_2d_TH2F_VarBinningY>(sameButDifferent.Create<TH2F>("test_2d_TH2F_VarBinningY")); + + // also for n dimensional histograms things become much simpler: + std::vector<double> ptBins = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 2.0, 5.0, 10.0, 20.0, 50.0}; + std::vector<double> centBins = {0., 30., 60., 90.}; + + // varaiable binning + Axis ptAxis = {"pt", "#it{p}_{T} (GeV/c)", ptBins}; + Axis centAxis = {"cent", "centrality", centBins}; + // equidistant binning + Axis etaAxis = {"eta", "#eta", {-0.8, 0.8}, 5}; + Axis phiAxis = {"phi", "#phi", {0., 2. * M_PI}, 4}; // 36 to see tpc sectors + const int nCuts = 5; + Axis cutAxis = {"cut", "cut setting", {-0.5, nCuts - 0.5}, nCuts}; + + Hist myHistogram({ptAxis, etaAxis, {"signed1Pt", "q/p_{T}", {-8, 8}, 200}}); + test->Add<test_3d_THnI>(myHistogram.Create<THnI>("test_3d_THnI")); + + Hist testSparseHist({ptAxis, etaAxis, phiAxis, centAxis, cutAxis}); + test->Add<test_5d_THnSparseI>(testSparseHist.Create<THnSparseI>("test_5d_THnSparseI")); + + Hist testProfile({ptAxis}); + test->Add<test_1d_TProfile>(testProfile.Create<TProfile>("test_1d_TProfile")); + test->Get<TProfile>(test_1d_TProfile)->GetYaxis()->SetTitle("eta profile"); + + // now add same histogram but intended for weighted filling + test->Add<test_1d_TProfile_Weight>(testProfile.Create<TProfile>("test_1d_TProfile_Weight", true)); + + Hist testProfile2d; + testProfile2d.AddAxis(ptAxis); + testProfile2d.AddAxis(etaAxis); + test->Add<test_2d_TProfile2D>(testProfile2d.Create<TProfile2D>("test_2d_TProfile2D")); + + Hist testProfile3d; + testProfile3d.AddAxis(ptAxis); + testProfile3d.AddAxis(etaAxis); + testProfile3d.AddAxis(phiAxis); + test->Add<test_3d_TProfile3D>(testProfile3d.Create<TProfile3D>("test_3d_TProfile3D")); + + // we can also re-use axis definitions in case they are similar in many histograms: + Hist baseDimensions({ptAxis, etaAxis, phiAxis, centAxis, cutAxis, centAxis}); + + Hist firstHist{baseDimensions}; + firstHist.AddAxis("something", "v (m/s)", 10, -1, 1); + test->Add<test_7d_THnF_first>(firstHist.Create<THnF>("test_7d_THnF_first")); + + Hist secondHist{baseDimensions}; + secondHist.AddAxis("somethingElse", "a (m/(s*s))", 10, -1, 1); + test->Add<test_7d_THnF_second>(secondHist.Create<THnF>("test_7d_THnF_second")); + + // or if we want to have the baseDimensions somewhere in between: + Hist thirdHist; + thirdHist.AddAxis("myFirstDimension", "a (m/(s*s))", 10, -1, 1); + thirdHist.AddAxes(baseDimensions); + thirdHist.AddAxis("myLastDimension", "a (m/(s*s))", 10, -1, 1); + test->Add<test_8d_THnC_third>(thirdHist.Create<THnC>("test_8d_THnC_third")); + + // we can also use the Hist helper tool independent of the HistCollections: + Hist myHist; + myHist.AddAxis(ptAxis); + myHist.AddAxis(etaAxis); + myHist.AddAxis(phiAxis); + standaloneHist.setObject(myHist.Create<THnF>("standaloneHist")); + + // now add some more useful histograms + kine->Add<pt>(new TH1F("pt", "p_{T};p_{T} [GeV/c]", 100, 0., 5.)); + kine->Add<eta>(new TH1F("eta", "#eta;#eta", 101, -1.0, 1.0)); + kine->Add<phi>(new TH1F("phi", "#phi;#phi [rad]", 100, 0., 2 * M_PI)); + + tpc->Add<tpcNClsFound>(new TH1F("tpcNClsFound", "number of found TPC clusters;# clusters TPC", 165, -0.5, 164.5)); + tpc->Add<tpcNClsCrossedRows>(new TH1F("tpcNClsCrossedRows", "number of crossed TPC rows;# crossed rows TPC", 165, -0.5, 164.5)); + tpc->Add<tpcChi2NCl>(new TH1F("tpcChi2NCl", "chi2 per cluster in TPC;chi2 / cluster TPC", 100, 0, 10)); + + its->Add<itsChi2NCl>(new TH1F("itsChi2NCl", "chi2 per ITS cluster;chi2 / cluster ITS", 100, 0, 40)); + } + + void process(soa::Join<aod::Tracks, aod::TracksExtra>::iterator const& track) + { + test->Fill<test_1d_TH1D>(20.); + test->Fill<test_2d_TH2F>(0.1, 0.3); + test->Fill<test_3d_TH3I>(10, 10, 15.5); + + // this time fill the 1d histogram with weight of 10: + test->FillWeight<test_1d_TH1D_Weight>(20., 10.); + + test->Fill<test_2d_TH2F_VarBinningY>(0.1, 0.3); + + test->Fill<test_3d_THnI>(1., 0., 1.5); + test->Fill<test_5d_THnSparseI>(1., 0., 1.5, 30, 1); + + // we can also directly access to the underlying histogram + // for this the correct type has to be known (TH1, TH2, TH3, THn, THnSparse, TProfile, TProfile2D or TProfile3D) + test->Get<TH2>(test_2d_TH2F)->Fill(0.2, 0.2); + + test->Fill<test_1d_TProfile>(track.pt(), track.eta()); + // now fill same histogram, but with random weight + test->FillWeight<test_1d_TProfile_Weight>(track.pt(), track.eta(), std::rand()); + test->Fill<test_2d_TProfile2D>(track.pt(), track.eta(), track.phi()); + test->Fill<test_3d_TProfile3D>(track.pt(), track.eta(), track.phi(), track.tpcNClsFound()); + + kine->Fill<pt>(track.pt()); + kine->Fill<eta>(track.eta()); + kine->Fill<phi>(track.phi()); + + tpc->Fill<tpcNClsFound>(track.tpcNClsFound()); + tpc->Fill<tpcNClsCrossedRows>(track.tpcNClsCrossedRows()); + tpc->Fill<tpcChi2NCl>(track.tpcChi2NCl()); + + its->Fill<itsChi2NCl>(track.itsChi2NCl()); + + double dummyArray[] = {track.pt(), track.eta(), track.phi()}; + standaloneHist->Fill(dummyArray); + } +}; + +//**************************************************************************************** +/** + * Workflow definition. + */ +//**************************************************************************************** +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<HistHelpersTest>("hist-helpers-test")}; +} diff --git a/Analysis/Tutorials/src/histogramRegistry.cxx b/Analysis/Tutorials/src/histogramRegistry.cxx new file mode 100644 index 0000000000000..fa79c947733d6 --- /dev/null +++ b/Analysis/Tutorials/src/histogramRegistry.cxx @@ -0,0 +1,262 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/HistogramRegistry.h" +#include <TH1F.h> +#include <TParameter.h> + +#include <cmath> + +using namespace o2; +using namespace o2::framework; + +// This is a very simple example showing how to create an histogram +// FIXME: this should really inherit from AnalysisTask but +// we need GCC 7.4+ for that +struct ATask { + /// Construct a registry object with direct declaration + HistogramRegistry registry{ + "registry", + { + {"eta", "#eta", {HistType::kTH1F, {{102, -2.01, 2.01}}}}, // + {"phi", "#varphi", {HistType::kTH1F, {{100, 0., 2. * M_PI}}}} // + } // + }; + + void process(aod::Tracks const& tracks) + { + for (auto& track : tracks) { + registry.get<TH1>(HIST("eta"))->Fill(track.eta()); + registry.get<TH1>(HIST("phi"))->Fill(track.phi()); + } + } +}; + +struct BTask { + /// Construct a registry object with direct declaration + HistogramRegistry registry{ + "registry", + { + {"eta", "#eta", {HistType::kTH1F, {{102, -2.01, 2.01}}}}, // + {"ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + } // + }; + + void process(aod::Tracks const& tracks) + { + registry.fill<aod::track::Eta>(HIST("eta"), tracks, aod::track::eta > 0.0f); + registry.fill<aod::track::Pt, aod::track::Pt>(HIST("ptToPt"), tracks, aod::track::pt < 5.0f); + } +}; + +struct CTask { + + HistogramRegistry registry{ + "registry", + { + {"1d", "test 1d", {HistType::kTH1I, {{100, -10.0f, 10.0f}}}}, // + {"2d", "test 2d", {HistType::kTH2F, {{100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}}}}, // + {"3d", "test 3d", {HistType::kTH3D, {{100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}}}}, // + {"4d", "test 4d", {HistType::kTHnC, {{100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}}}}, // + {"5d", "test 5d", {HistType::kTHnSparseL, {{10, -10.0f, 10.01f}, {10, -10.0f, 10.01f}, {10, -10.0f, 10.01f}, {10, -10.0f, 10.01f}, {10, -10.0f, 10.01f}}}}, // + }, + OutputObjHandlingPolicy::AnalysisObject, + true // + }; + + void init(o2::framework::InitContext&) + { + registry.add("7d", "test 7d", {HistType::kTHnC, {{3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}}}); + + registry.add("6d", "test 6d", {HistType::kTHnC, {{3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}, {3, -10.0f, 10.01f}}}); + + registry.add("1d-profile", "test 1d profile", {HistType::kTProfile, {{20, 0.0f, 10.01f}}}); + registry.add("2d-profile", "test 2d profile", {HistType::kTProfile2D, {{20, 0.0f, 10.01f}, {20, 0.0f, 10.01f}}}); + registry.add("3d-profile", "test 3d profile", {HistType::kTProfile3D, {{20, 0.0f, 10.01f}, {20, 0.0f, 10.01f}, {20, 0.0f, 10.01f}}}); + + registry.add("2d-weight", "test 2d weight", {HistType::kTH2C, {{2, -10.0f, 10.01f}, {2, -10.0f, 10.01f}}}, true); + + registry.add("3d-weight", "test 3d weight", {HistType::kTH3C, {{2, -10.0f, 10.01f}, {2, -10.0f, 10.01f}, {2, -10.0f, 10.01f}}}, true); + + registry.add("4d-weight", "test 4d weight", {HistType::kTHnC, {{2, -10.0f, 10.01f}, {2, -10.0f, 10.01f}, {2, -10.0f, 10.01f}, {100, -10.0f, 10.01f}}}, true); + + registry.add("1d-profile-weight", "test 1d profile weight", {HistType::kTProfile, {{2, -10.0f, 10.01f}}}, true); + registry.add("2d-profile-weight", "test 2d profile weight", {HistType::kTProfile2D, {{2, -10.0f, 10.01f}, {2, -10.0f, 10.01f}}}, true); + } + + void process(aod::Tracks const& tracks) + { + using namespace aod::track; + // does not work with dynamic columns (e.g. Charge, NormalizedPhi) + registry.fill<Eta>(HIST("1d"), tracks, eta > -0.7f); + registry.fill<Pt, Eta, RawPhi>(HIST("3d"), tracks, eta > 0.f); + registry.fill<Pt, Eta, RawPhi, P, X>(HIST("5d"), tracks, pt > 0.15f); + registry.fill<Pt, Eta, RawPhi, P, X, Y, Z>(HIST("7d"), tracks, pt > 0.15f); + registry.fill<Pt, Eta, RawPhi>(HIST("2d-profile"), tracks, eta > -0.5f); + + // fill 4d histogram with weight (column X) + registry.fill<Pt, Eta, RawPhi, Z, X>(HIST("4d-weight"), tracks, eta > 0.f); + + registry.fill<Pt, Eta, RawPhi>(HIST("2d-weight"), tracks, eta > 0.f); + + registry.fill<Pt, Eta, RawPhi>(HIST("1d-profile-weight"), tracks, eta > 0.f); + + for (auto& track : tracks) { + registry.fill(HIST("2d"), track.eta(), track.pt()); + registry.fill(HIST("4d"), track.pt(), track.eta(), track.phi(), track.signed1Pt()); + registry.fill(HIST("6d"), track.pt(), track.eta(), track.phi(), track.snp(), track.tgl(), track.alpha()); + registry.fill(HIST("1d-profile"), track.pt(), track.eta()); + registry.fill(HIST("3d-profile"), track.pt(), track.eta(), track.phi(), track.snp()); + + // fill 3d histogram with weight (2.) + registry.fill(HIST("3d-weight"), track.pt(), track.eta(), track.phi(), 2.); + + registry.fill(HIST("2d-profile-weight"), track.pt(), track.eta(), track.phi(), 5.); + } + } +}; + +struct DTask { + HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry etaStudy{"etaStudy", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + void init(o2::framework::InitContext&) + { + std::vector<double> ptBinning = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 2.0, 5.0, 10.0, 20.0, 50.0}; + std::vector<double> centBinning = {0., 30., 60., 90.}; + + AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/c)"}; + AxisSpec centAxis = {centBinning, "centrality"}; + AxisSpec etaAxis = {5, -0.8, 0.8, "#eta"}; + AxisSpec phiAxis = {4, 0., 2. * M_PI, "#phi"}; + const int nCuts = 5; + AxisSpec cutAxis = {nCuts, -0.5, nCuts - 0.5, "cut setting"}; + + HistogramConfigSpec defaultParticleHist({HistType::kTHnF, {ptAxis, etaAxis, centAxis, cutAxis}}); + + spectra.add("myControlHist", "a", kTH2F, {ptAxis, etaAxis}); + spectra.get<TH2>(HIST("myControlHist"))->GetYaxis()->SetTitle("my-y-axis"); + spectra.get<TH2>(HIST("myControlHist"))->SetTitle("something meaningful"); + + spectra.add("charged/pions", "Pions", defaultParticleHist); + spectra.add("neutral/pions", "Pions", defaultParticleHist); + spectra.add("one/two/three/four/kaons", "Kaons", defaultParticleHist); + spectra.add("sigmas", "Sigmas", defaultParticleHist); + spectra.add("lambdas", "Lambd", defaultParticleHist); + + spectra.get<THn>(HIST("lambdas"))->SetTitle("Lambdas"); + + etaStudy.add("positive", "A side spectra", kTH1I, {ptAxis}); + etaStudy.add("negative", "C side spectra", kTH1I, {ptAxis}); + + spectra.add("before_cuts/hist1", "asdf", defaultParticleHist); + spectra.add("before_cuts/hist2", "asdf", defaultParticleHist); + spectra.add("before_cuts/hist3", "asdf", defaultParticleHist); + + // clone whole category / group + spectra.addClone("before_cuts/", "after_cuts/"); + + // clone single histograms + spectra.addClone("sigmas", "cascades"); + spectra.addClone("neutral/pions", "strange/funny/particles"); + } + + template <bool mode, typename T> + void fillHistos(const T& track) + { + static constexpr std::string_view subDir[] = {"before_cuts/", "after_cuts/"}; + + spectra.fill(HIST(subDir[mode]) + HIST("hist1"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST(subDir[mode]) + HIST("hist2"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST(subDir[mode]) + HIST("hist3"), track.pt(), track.eta(), 50., 0.); + } + + void process(aod::Tracks const& tracks) + { + using namespace aod::track; + + etaStudy.fill<Pt>(HIST("positive"), tracks, eta > 0.f); + etaStudy.fill<Pt>(HIST("negative"), tracks, eta < 0.f); + + for (auto& track : tracks) { + spectra.fill(HIST("myControlHist"), track.pt(), track.eta()); + spectra.fill(HIST("charged/pions"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST("charged/pions"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST("neutral/pions"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST("one/two/three/four/kaons"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST("sigmas"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST("lambdas"), track.pt(), track.eta(), 50., 0.); + + // fill histograms before and after cuts + fillHistos<false>(track); + if (std::rand() > (RAND_MAX / 2)) { + fillHistos<true>(track); + } + + spectra.fill(HIST("cascades"), track.pt(), track.eta(), 50., 0.); + spectra.fill(HIST("strange/funny/particles"), track.pt(), track.eta(), 50., 0.); + } + } +}; + +struct ETask { + OutputObj<TH1F> phiH{TH1F("phi", "phi", 100, 0., 2. * M_PI)}; + OutputObj<TH1F> etaH{TH1F("eta", "eta", 102, -2.01, 2.01)}; + + void process(aod::Tracks const& tracks) + { + for (auto& track : tracks) { + phiH->Fill(track.phi()); + etaH->Fill(track.eta()); + } + } +}; + +struct FTask { + Configurable<int> cfgTrackType{"trktype", 1, "Type of selected tracks: 0 = no selection, 1 = global tracks FB96"}; + OutputObj<TList> fOutput{"TListForTests", OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType::OutputObjSource}; + HistogramRegistry registry{ + "registry", + { + {"eta", "#eta", {HistType::kTH1F, {{102, -2.01, 2.01}}}}, // + {"phi", "#varphi", {HistType::kTH1F, {{100, 0., 2. * M_PI}}}} // + } // + }; + + void init(InitContext const&) + { + TList* fOutputList = new TList(); + fOutputList->SetOwner(true); + fOutput.setObject(fOutputList); + fOutputList->Add(new TParameter<Int_t>("TrackType", cfgTrackType, 'f')); + } + + void process(aod::Tracks const& tracks) + { + for (auto& track : tracks) { + registry.get<TH1>(HIST("eta"))->Fill(track.eta()); + registry.get<TH1>(HIST("phi"))->Fill(track.phi()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<ETask>("output-obj-test"), + adaptAnalysisTask<ATask>("eta-and-phi-histograms"), + adaptAnalysisTask<BTask>("filtered-histograms"), + adaptAnalysisTask<CTask>("dimension-test"), + adaptAnalysisTask<DTask>("realistic-example"), + adaptAnalysisTask<FTask>("tlist-test")}; +} diff --git a/Analysis/Tutorials/src/histogramTrackSelection.cxx b/Analysis/Tutorials/src/histogramTrackSelection.cxx index cf1b13e1bf857..a7e344ffdba03 100644 --- a/Analysis/Tutorials/src/histogramTrackSelection.cxx +++ b/Analysis/Tutorials/src/histogramTrackSelection.cxx @@ -15,8 +15,8 @@ #include <cmath> -#include "Analysis/TrackSelection.h" -#include "Analysis/TrackSelectionTables.h" +#include "AnalysisCore/TrackSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" using namespace o2; using namespace o2::framework; @@ -38,10 +38,11 @@ struct HistogramTrackSelection { { for (auto& track : tracks) { - if (selectedTracks == 1 && !track.isGlobalTrack()) + if (selectedTracks == 1 && !track.isGlobalTrack()) { continue; - else if (selectedTracks == 2 && !track.isGlobalTrackSDD()) + } else if (selectedTracks == 2 && !track.isGlobalTrackSDD()) { continue; + } pt->Fill(track.pt()); } diff --git a/Analysis/Tutorials/src/histograms.cxx b/Analysis/Tutorials/src/histograms.cxx index 17a4efa2e9151..4b8bf0bac23a5 100644 --- a/Analysis/Tutorials/src/histograms.cxx +++ b/Analysis/Tutorials/src/histograms.cxx @@ -16,6 +16,7 @@ using namespace o2; using namespace o2::framework; +using namespace o2::framework::expressions; // This is a very simple example showing how to create an histogram // FIXME: this should really inherit from AnalysisTask but @@ -58,6 +59,8 @@ struct CTask { OutputObj<TH1F> trZ{"trZ", OutputObjHandlingPolicy::QAObject}; Configurable<float> pTCut{"pTCut", 0.5f, "Lower pT limit"}; + Filter ptfilter = aod::track::pt > pTCut; + void init(InitContext const&) { trZ.setObject(new TH1F("Z", "Z", 100, -10., 10.)); @@ -67,12 +70,10 @@ struct CTask { // trZ.setObject({"Z","Z",100,-10.,10.}); <- creates new } - void process(aod::Tracks const& tracks) + void process(soa::Filtered<aod::Tracks> const& tracks) { for (auto& track : tracks) { - if (track.pt2() < pTCut * pTCut) - continue; - ptH->Fill(std::sqrt(track.pt2())); + ptH->Fill(track.pt()); trZ->Fill(track.z()); } } @@ -93,7 +94,7 @@ struct DTask { auto pHist = dynamic_cast<TH1F*>(list->At(0)); auto etaHist = dynamic_cast<TH1F*>(list->At(1)); - pHist->Fill(std::sqrt(track.p2())); + pHist->Fill(track.p()); etaHist->Fill(track.eta()); } }; diff --git a/Analysis/Tutorials/src/histogramsFullTracks.cxx b/Analysis/Tutorials/src/histogramsFullTracks.cxx new file mode 100644 index 0000000000000..f264ce8cf7361 --- /dev/null +++ b/Analysis/Tutorials/src/histogramsFullTracks.cxx @@ -0,0 +1,37 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include <TH2F.h> + +#include <cmath> + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct ATask { + OutputObj<TH2F> etaClsH{TH2F("eta_vs_cls", "#eta vs N_{cls}", 102, -2.01, 2.01, 160, -0.5, 159.5)}; + + void process(aod::FullTracks const& tracks) + { + for (auto& track : tracks) { + etaClsH->Fill(track.eta(), track.tpcNClsFindable()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<ATask>("eta-and-cls-histograms"), + }; +} diff --git a/Analysis/Tutorials/src/jetAnalysis.cxx b/Analysis/Tutorials/src/jetAnalysis.cxx index 92f6c15ff5616..250636e304cb0 100644 --- a/Analysis/Tutorials/src/jetAnalysis.cxx +++ b/Analysis/Tutorials/src/jetAnalysis.cxx @@ -21,27 +21,30 @@ #include "Framework/AnalysisDataModel.h" #include "Framework/ASoA.h" -#include "Analysis/Jet.h" +#include "AnalysisDataModel/Jet.h" using namespace o2; using namespace o2::framework; struct JetAnalysis { - OutputObj<TH1F> hJetPt{"pt"}; + OutputObj<TH1F> hJetPt{"jetPt"}; + OutputObj<TH1F> hConstPt{"constPt"}; void init(InitContext const&) { - hJetPt.setObject(new TH1F("pt", "jet p_{T};p_{T} (GeV/#it{c})", + hJetPt.setObject(new TH1F("jetPt", "jet p_{T};p_{T} (GeV/#it{c})", 100, 0., 100.)); + hConstPt.setObject(new TH1F("constPt", "constituent p_{T};p_{T} (GeV/#it{c})", + 100, 0., 100.)); } - // TODO: add aod::Tracks (when available) void process(aod::Jet const& jet, - aod::JetConstituents const& constituents) + aod::JetConstituents const& constituents, aod::Tracks const& tracks) { hJetPt->Fill(jet.pt()); for (const auto c : constituents) { - LOGF(INFO, "jet %d: track id %d", jet.index(), c.trackId()); + LOGF(DEBUG, "jet %d: track id %d, track pt %g", jet.index(), c.trackId(), c.track().pt()); + hConstPt->Fill(c.track().pt()); } } }; diff --git a/Analysis/Tutorials/src/mcHistograms.cxx b/Analysis/Tutorials/src/mcHistograms.cxx index 49debb6ff7054..1e0d5e4dcc8d6 100644 --- a/Analysis/Tutorials/src/mcHistograms.cxx +++ b/Analysis/Tutorials/src/mcHistograms.cxx @@ -10,7 +10,7 @@ #include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" #include "Framework/AnalysisDataModel.h" -#include "Analysis/MC.h" +#include "AnalysisCore/MC.h" #include <TH1F.h> #include <cmath> @@ -39,7 +39,7 @@ struct BTask { { //LOGF(info, "MC. vtx-z = %f", mcCollision.posZ()); LOGF(info, "First: %d | Length: %d", mcParticles.begin().index(), mcParticles.size()); - LOGF(info, "Particles mother: %d", (mcParticles.begin() + 1000).mother()[0]); + LOGF(info, "Particles mother: %d", (mcParticles.begin() + 1000).mother0()); for (auto& mcParticle : mcParticles) { if (MC::isPhysicalPrimary(mcParticles, mcParticle)) { phiH->Fill(mcParticle.phi()); @@ -64,10 +64,12 @@ struct CTask { // continue; etaDiff->Fill(track.label().eta() - track.eta()); auto delta = track.label().phi() - track.phi(); - if (delta > M_PI) + if (delta > M_PI) { delta -= 2 * M_PI; - if (delta < -M_PI) + } + if (delta < -M_PI) { delta += 2 * M_PI; + } phiDiff->Fill(delta); //LOGF(info, "eta: %.2f %.2f \t phi: %.2f %.2f | %d", track.label().eta(), track.eta(), track.label().phi(), track.phi(), track.label().index()); } diff --git a/Analysis/Tutorials/src/muonIteration.cxx b/Analysis/Tutorials/src/muonIteration.cxx new file mode 100644 index 0000000000000..e32237a006229 --- /dev/null +++ b/Analysis/Tutorials/src/muonIteration.cxx @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// This task shows how to access the Muons belong to a collision +// The association is made through the BC column (and in Run 3 may not be unique!) +// +// Note that one has to subscribe to aod::Collisions const& to load +// the relevant data even if you access the data itself through m.collision() +// This uses the exclusive matcher, so you only get BCs which have a collision +// If you want also BCs without collision, see the example IterateMuonsSparse below +struct IterateMuons { + void process(aod::MatchedBCCollisionsExclusive::iterator const& m, aod::Collisions const&, aod::Muons const& muons) + { + LOGF(INFO, "Vertex = %f has %d muons", m.collision().posZ(), muons.size()); + for (auto& muon : muons) { + LOGF(info, " pT = %.2f", muon.pt()); + } + } +}; + +// This uses the sparase matcher, so you also get BCs without a collision. +// You need to check with m.has_collision() +struct IterateMuonsSparse { + void process(aod::MatchedBCCollisionsSparse::iterator const& m, aod::Collisions const&, aod::Muons const& muons) + { + if (m.has_collision()) { + LOGF(INFO, "Vertex = %f has %d muons", m.collision().posZ(), muons.size()); + } else { + LOGF(INFO, "BC without collision has %d muons", muons.size()); + } + for (auto& muon : muons) { + LOGF(info, " pT = %.2f", muon.pt()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<IterateMuons>("iterate-muons"), + adaptAnalysisTask<IterateMuonsSparse>("iterate-muons-sparse"), + }; +} diff --git a/Analysis/Tutorials/src/partitions.cxx b/Analysis/Tutorials/src/partitions.cxx new file mode 100644 index 0000000000000..e80fd07da5b58 --- /dev/null +++ b/Analysis/Tutorials/src/partitions.cxx @@ -0,0 +1,82 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// This is a very simple example showing how to iterate over tracks +// and create a new collection for them. +// FIXME: this should really inherit from AnalysisTask but +// we need GCC 7.4+ for that +struct ATask { + float fPI = static_cast<float>(M_PI); + Configurable<float> ptlow{"pTlow", 0.5f, "Lowest pT"}; + Configurable<float> ptup{"pTup", 2.0f, "highest pT"}; + Filter ptFilter_a = aod::track::pt > ptlow; + Filter ptFilter_b = aod::track::pt < ptup; + + Configurable<float> etalow{"etaLow", -1.0f, "lowest eta"}; + Configurable<float> etaup{"etaUp", 1.0f, "highest eta"}; + Filter etafilter = (aod::track::eta < etaup) && (aod::track::eta > etalow); + + Configurable<float> philow{"phiLow", 1.0f, "lowest phi"}; + Configurable<float> phiup{"phiUp", 2.0f, "highest phi"}; + + using myTracks = soa::Filtered<aod::Tracks>; + + Partition<myTracks> leftPhi = aod::track::phiraw < philow; + Partition<myTracks> midPhi = aod::track::phiraw >= philow && aod::track::phiraw < phiup; + Partition<myTracks> rightPhi = aod::track::phiraw >= phiup; + + void process(aod::Collision const& collision, myTracks const& tracks) + { + LOGF(INFO, "Collision: %d [N = %d] [left phis = %d] [mid phis = %d] [right phis = %d]", + collision.globalIndex(), tracks.size(), leftPhi.size(), midPhi.size(), rightPhi.size()); + + for (auto& track : leftPhi) { + LOGF(INFO, "id = %d, from collision: %d, collision: %d; eta: %.3f < %.3f < %.3f; phi: %.3f < %.3f; pt: %.3f < %.3f < %.3f", + track.collisionId(), track.collision().globalIndex(), collision.globalIndex(), (float)etalow, track.eta(), (float)etaup, track.phiraw(), (float)philow, (float)ptlow, track.pt(), (float)ptup); + } + for (auto& track : midPhi) { + LOGF(INFO, "id = %d, from collision: %d, collision: %d; eta: %.3f < %.3f < %.3f; phi: %.3f <= %.3f < %.3f; pt: %.3f < %.3f < %.3f", + track.collisionId(), track.collision().globalIndex(), collision.globalIndex(), (float)etalow, track.eta(), (float)etaup, (float)philow, track.phiraw(), (float)phiup, (float)ptlow, track.pt(), (float)ptup); + } + for (auto& track : rightPhi) { + LOGF(INFO, "id = %d, from collision: %d, collision: %d; eta: %.3f < %.3f < %.3f; phi: %.3f < %.3f; pt: %.3f < %.3f < %.3f", + track.collisionId(), track.collision().globalIndex(), collision.globalIndex(), (float)etalow, track.eta(), (float)etaup, (float)phiup, track.phiraw(), (float)ptlow, track.pt(), (float)ptup); + } + } +}; + +// Partition inside process +// Caveat: partitioned table cannot be passed as const& to process() +struct BTask { + void process(aod::Collisions const& collisions, aod::Tracks& tracks) + { + for (auto& c : collisions) { + Partition<aod::Tracks> groupedTracks = aod::track::collisionId == c.globalIndex(); + groupedTracks.bindTable(tracks); + for (auto& t : groupedTracks) { + LOGF(INFO, "collision global index: %d grouped track collision id: %d", c.globalIndex(), t.collisionId()); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<ATask>("consume-tracks"), + adaptAnalysisTask<BTask>("partition-in-process")}; +} diff --git a/Analysis/Tutorials/src/schemaEvolution.cxx b/Analysis/Tutorials/src/schemaEvolution.cxx new file mode 100644 index 0000000000000..8d126b86f34cb --- /dev/null +++ b/Analysis/Tutorials/src/schemaEvolution.cxx @@ -0,0 +1,89 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +// This example shows how schema evolution of tables can be implemented +// Here two tables are defined, EtaPhiV2 has an additional member compared to EtaPhiV1 +// It is shown how an example task can use a template, and can be instantiated to work +// on both. + +namespace o2::aod +{ +namespace etaphi +{ +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(AbsEta, absEta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace etaphi +DECLARE_SOA_TABLE(EtaPhiV1, "AOD", "ETAPHI", etaphi::Eta, etaphi::Phi); +DECLARE_SOA_TABLE(EtaPhiV2, "AOD", "ETAPHIV2", etaphi::Eta, etaphi::AbsEta, etaphi::Phi); +} // namespace o2::aod + +using namespace o2; +using namespace o2::framework; + +// Producer of EtaPhiV1 +struct ATask { + Produces<aod::EtaPhiV1> etaphi; + + void process(aod::Tracks const& tracks) + { + for (auto& track : tracks) { + float phi = asin(track.snp()) + track.alpha() + static_cast<float>(M_PI); + float eta = log(tan(0.25f * static_cast<float>(M_PI) - 0.5f * atan(track.tgl()))); + + etaphi(eta, phi); + } + } +}; + +// Producer of EtaPhiV2 +struct BTask { + Produces<aod::EtaPhiV2> etaphi; + + void process(aod::Tracks const& tracks) + { + for (auto& track : tracks) { + float phi = asin(track.snp()) + track.alpha() + static_cast<float>(M_PI); + float eta = log(tan(0.25f * static_cast<float>(M_PI) - 0.5f * atan(track.tgl()))); + + etaphi(eta, std::abs(eta), phi); + } + } +}; + +// Consumper of both EtaPhiV1 and EtaPhiV2 +// InputTable is a template which is then specified below when the workflow is defined +template <typename InputTable> +struct CTask { + void process(InputTable const& etaPhis) + { + constexpr bool isV2 = std::is_same<InputTable, aod::EtaPhiV2>::value; + for (auto& etaPhi : etaPhis) { + LOGF(info, "(%f, %f)", etaPhi.eta(), etaPhi.phi()); + if constexpr (isV2) { + // This line is only compiled if this is templated with EtaPhiV2 + LOGF(info, "We have the new data model (%f)", etaPhi.absEta()); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<ATask>("produce-etaphi-v1"), + adaptAnalysisTask<BTask>("produce-etaphi-v2"), + adaptAnalysisTask<CTask<aod::EtaPhiV1>>("consume-etaphi-v1"), // here CTask is added with EtaPhiV1 input + adaptAnalysisTask<CTask<aod::EtaPhiV2>>("consume-etaphi-v2"), // here CTask is added with EtaPhiV2 input + }; +} diff --git a/Analysis/Tutorials/src/weakDecayIteration.cxx b/Analysis/Tutorials/src/weakDecayIteration.cxx new file mode 100644 index 0000000000000..ad350d7407c80 --- /dev/null +++ b/Analysis/Tutorials/src/weakDecayIteration.cxx @@ -0,0 +1,73 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" + +// Example how to enumerate V0s and cascades +// Needs weak-decay-indices in the workflow +// Example usage: o2-analysis-weak-decay-indices --aod-file AO2D.root | o2-analysistutorial-weak-decay-iteration + +using namespace o2; +using namespace o2::framework; + +struct BTask { + void process(aod::V0s const& v0s, aod::FullTracks const& tracks) + { + for (auto& v0 : v0s) { + LOGF(DEBUG, "V0 (%d, %d, %d)", v0.posTrack().collisionId(), v0.negTrack().collisionId(), v0.collisionId()); + } + } +}; + +struct CTask { + void process(aod::Cascades const& cascades, aod::V0s const& v0s, aod::FullTracks const& tracks) + { + for (auto& cascade : cascades) { + LOGF(DEBUG, "Cascade %d (%d, %d, %d, %d)", cascade.globalIndex(), cascade.bachelor().collisionId(), cascade.v0().posTrack().collisionId(), cascade.v0().negTrack().collisionId(), cascade.collisionId()); + } + } +}; + +// Grouping V0s +struct DTask { + void process(aod::Collision const& collision, aod::V0s const& v0s, aod::FullTracks const& tracks) + { + LOGF(INFO, "Collision %d has %d V0s", collision.globalIndex(), v0s.size()); + + for (auto& v0 : v0s) { + LOGF(DEBUG, "Collision %d V0 %d (%d, %d)", collision.globalIndex(), v0.globalIndex(), v0.posTrackId(), v0.negTrackId()); + } + } +}; + +// Grouping V0s and cascades +// NOTE that you need to subscribe to V0s even if you only process cascades +struct ETask { + void process(aod::Collision const& collision, aod::V0s const& v0s, aod::Cascades const& cascades, aod::FullTracks const& tracks) + { + LOGF(INFO, "Collision %d has %d cascades (%d tracks)", collision.globalIndex(), cascades.size(), tracks.size()); + + for (auto& cascade : cascades) { + LOGF(INFO, "Collision %d Cascade %d (%d, %d, %d)", collision.globalIndex(), cascade.globalIndex(), cascade.v0().posTrackId(), cascade.v0().negTrackId(), cascade.bachelorId()); + LOGF(INFO, " IDs: %d %d %d", cascade.v0().posTrack().collisionId(), cascade.v0().negTrack().collisionId(), cascade.bachelor().collisionId()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask<BTask>("consume-v0s"), + adaptAnalysisTask<CTask>("consume-cascades"), + adaptAnalysisTask<DTask>("consume-grouped-v0s"), + adaptAnalysisTask<ETask>("consume-grouped-cascades"), + }; +} diff --git a/CCDB/CMakeLists.txt b/CCDB/CMakeLists.txt index b592cb27d8213..a91a3aa31347e 100644 --- a/CCDB/CMakeLists.txt +++ b/CCDB/CMakeLists.txt @@ -51,6 +51,12 @@ o2_add_test(CcdbApi PUBLIC_LINK_LIBRARIES O2::CCDB LABELS ccdb) +o2_add_test(CcdbApi-Alien + SOURCES test/testCcdbApi_alien.cxx + COMPONENT_NAME ccdb + PUBLIC_LINK_LIBRARIES O2::CCDB + LABELS ccdb) + o2_add_test(BasicCCDBManager SOURCES test/testBasicCCDBManager.cxx COMPONENT_NAME ccdb diff --git a/CCDB/README.md b/CCDB/README.md index 97796f6bd8429..adcc993c950f1 100644 --- a/CCDB/README.md +++ b/CCDB/README.md @@ -92,9 +92,14 @@ unless the validity range of the cached object does not match to requested times In case user wants to enforce a fresh copy loading, the cache for particular CCDB path can be cleaned by invoking `mgr.clear(<path>)`. One can also reset whole cache using `mgr.clear()`. -Uncached mode can be imposed by invoking `mgr.setCachingEnabled(false)`, in which case every query will retrieve a new copy of object from the server and +Uncached mode can be imposed by invoking `mgr.setCaching(false)`, in which case every query will retrieve a new copy of object from the server and the user should take care himself of deleting retrieved objects to avoid memory leaks. +Upper and lower limits on the object creation time can be set by `mgr.setCreatedNotAfter(upper_limit_timestamp)` and `mgr.setCreatedNotBefore(lower_limit_timestamp)`, it specifies the fields "If-Not-After" and "If-Not-Before" when retrieving an object from CCDB. +This feature is useful to avoid using newer objects if the CCDB is updated in parallel to the task execution. + +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. + ## Future ideas / todo: - [ ] offer improved error handling / exceptions diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index 215fb4558283b..2a6a85b840981 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -87,7 +87,7 @@ class BasicCCDBManager bool isCachingEnabled() const { return mCachingEnabled; } /// disable or enable caching - void setCachingEnabled(bool v) + void setCaching(bool v) { mCachingEnabled = v; if (!v) { @@ -96,10 +96,10 @@ class BasicCCDBManager } /// check if checks of object validity before CCDB query is enabled - bool isValidityCheckingEnabled() const { return mCheckObjValidityEnabled; } + bool isLocalObjectValidityCheckingEnabled() const { return mCheckObjValidityEnabled; } /// set the flag to check object validity before CCDB query - void setValidityCheckingEnabled(bool v = true) { mCheckObjValidityEnabled = v; } + void setLocalObjectValidityChecking(bool v = true) { mCheckObjValidityEnabled = v; } /// set the object upper validity limit void setCreatedNotAfter(long v) { mCreatedNotAfter = v; } @@ -147,8 +147,9 @@ T* BasicCCDBManager::getForTimeStamp(std::string const& path, long timestamp) mCreatedNotBefore ? std::to_string(mCreatedNotBefore) : ""); } auto& cached = mCache[path]; - if (mCheckObjValidityEnabled && cached.isValid(timestamp)) + if (mCheckObjValidityEnabled && cached.isValid(timestamp)) { return reinterpret_cast<T*>(cached.objPtr.get()); + } T* ptr = mCCDBAccessor.retrieveFromTFileAny<T>(path, mMetaData, timestamp, &mHeaders, cached.uuid, mCreatedNotAfter ? std::to_string(mCreatedNotAfter) : "", diff --git a/CCDB/include/CCDB/CcdbApi.h b/CCDB/include/CCDB/CcdbApi.h index 7fbf711845636..f4f196daf2702 100644 --- a/CCDB/include/CCDB/CcdbApi.h +++ b/CCDB/include/CCDB/CcdbApi.h @@ -25,6 +25,7 @@ #include "CCDB/CcdbObjectInfo.h" class TFile; +class TGrid; namespace o2 { @@ -377,6 +378,26 @@ class CcdbApi //: public DatabaseInterface */ void* extractFromLocalFile(std::string const& filename, TClass const* cl) const; + /** + * Helper function to download binary content from alien:// 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, TClass* tcl) const; + + // initialize the TGrid (Alien connection) + bool initTGrid() const; + // checks if an alien token is available, required to make a TGrid connection + bool checkAlienToken() const; + + /// Queries the CCDB server and navigates through possible redirects until binary content is found; Retrieves content as instance + /// given by TClass if that is possible. Returns nullptr if something fails... + void* navigateURLsAndRetrieveContent(CURL*, std::string const& url, TClass* cl, std::map<std::string, std::string>* headers) const; + + // helper that interprets a content chunk as TMemFile and extracts the object therefrom + void* interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, TClass* cl) const; + /** * Initialization of CURL */ @@ -386,6 +407,8 @@ class CcdbApi //: public DatabaseInterface std::string mUrl{}; std::string mSnapshotTopPath{}; bool mInSnapshotMode = false; + mutable TGrid* mAlienInstance = nullptr; // a cached connection to TGrid (needed for Alien locations) + bool mHaveAlienToken = false; // stores if an alien token is available ClassDefNV(CcdbApi, 1); }; diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index 243e431d0380b..5bbe6bf085a04 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -22,6 +22,7 @@ #include <TMessage.h> #include <sstream> #include <TFile.h> +#include <TGrid.h> #include <TSystem.h> #include <TStreamerInfo.h> #include <TMemFile.h> @@ -70,6 +71,12 @@ void CcdbApi::init(std::string const& host) } else { curlInit(); } + + // find out if we can can in principle connect to Alien + mHaveAlienToken = checkAlienToken(); + if (!mHaveAlienToken) { + LOG(WARN) << "CCDB: Did not find an alien token; Cannot serve objects located on alien://"; + } } /** @@ -391,15 +398,16 @@ std::string CcdbApi::generateFileName(const std::string& inp) namespace { +template <typename MapType = std::map<std::string, std::string>> size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userdata) { - auto* headers = static_cast<std::map<std::string, std::string>*>(userdata); + auto* headers = static_cast<MapType*>(userdata); auto header = std::string(buffer, size * nitems); std::string::size_type index = header.find(':', 0); if (index != std::string::npos) { - headers->insert(std::make_pair( - boost::algorithm::trim_copy(header.substr(0, index)), - boost::algorithm::trim_copy(header.substr(index + 1)))); + 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; } @@ -458,7 +466,7 @@ TObject* CcdbApi::retrieveFromTFile(std::string const& path, std::map<std::strin // setup curl for headers handling if (headers != nullptr) { list = curl_slist_append(list, ("If-None-Match: " + to_string(timestamp)).c_str()); - curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback<>); curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, headers); } @@ -643,6 +651,173 @@ void* CcdbApi::extractFromLocalFile(std::string const& filename, TClass const* t return extractFromTFile(f, tcl); } +bool CcdbApi::checkAlienToken() const +{ + // a somewhat weird construction to programmatically find out if we + // have a GRID token; Can be replaced with something more elegant once + // alien-token-info does not ask for passwords interactively + if (getenv("JALIEN_TOKEN_CERT")) { + return true; + } + auto returncode = system("alien-token-info > /dev/null 2> /dev/null"); + return returncode == 0; +} + +bool CcdbApi::initTGrid() const +{ + if (!mAlienInstance) { + if (mHaveAlienToken) { + mAlienInstance = TGrid::Connect("alien"); + } + } + return mAlienInstance != nullptr; +} + +void* CcdbApi::downloadAlienContent(std::string const& url, TClass* cl) const +{ + if (!initTGrid()) { + return nullptr; + } + auto memfile = TMemFile::Open(url.c_str(), "OPEN"); + if (memfile) { + auto content = extractFromTFile(*memfile, cl); + delete memfile; + return content; + } + return nullptr; +} + +void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, TClass* tcl) const +{ + void* result = nullptr; + Int_t previousErrorLevel = gErrorIgnoreLevel; + gErrorIgnoreLevel = kFatal; + TMemFile memFile("name", contentptr, contentsize, "READ"); + gErrorIgnoreLevel = previousErrorLevel; + if (!memFile.IsZombie()) { + result = extractFromTFile(memFile, tcl); + if (!result) { + LOG(ERROR) << o2::utils::concat_string("Couldn't retrieve object corresponding to ", tcl->GetName(), " from TFile"); + } + memFile.Close(); + } + return result; +} + +// navigate sequence of URLs until TFile content is found; object is extracted and returned +void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, TClass* cl, std::map<string, string>* headers) 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<std::string, std::string> 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, cl); + } + // add other final cases here + // example root:// + + // otherwise make an HTTP/CURL request + // specify URL to get + curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); + // some servers don't like requests that are made without a user-agent + // field, so we provide one + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + // if redirected , we tell libcurl NOT to follow redirection + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 0L); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback<decltype(headerData)>); + headerData.clear(); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&headerData); + + MemoryStruct chunk{(char*)malloc(1), 0}; + + // send all data to this function + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&chunk); + + auto res = curl_easy_perform(curl_handle); + long response_code = -1; + void* content = nullptr; + bool errorflag = false; + bool cachingflag = false; + 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; + } + } + 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, cl); + } else if (response_code == 304) { + // this means the object exist but I am not serving + // it since it's already in your possession + + // there is nothing to be done here + cachingflag = true; + } + // 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<std::string> 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; + content = navigateURLsAndRetrieveContent(curl_handle, l, cl, nullptr); + if (content /* or other success marker in future */) { + break; + } + } + } + } else if (response_code == 404) { + LOG(ERROR) << "Requested resource does not exist"; + errorflag = true; + } else { + errorflag = true; + } + // cleanup + if (chunk.memory != nullptr) { + free(chunk.memory); + } + } else { + LOG(ERROR) << "Curl request to " << url << " failed "; + errorflag = true; + } + // indicate that an error occurred ---> used by caching layers (such as CCDBManager) + if (errorflag && headers) { + (*headers)["Error"] = "An error occurred during retrieval"; + } + return content; +} + void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp, std::map<std::string, std::string>* headers, std::string const& etag, @@ -655,103 +830,32 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& return nullptr; } - // Note : based on https://curl.haxx.se/libcurl/c/getinmemory.html - // Thus it does not comply to our coding guidelines as it is a copy paste. - - // Prepare CURL - CURL* curl_handle; - CURLcode res; - struct MemoryStruct chunk { - (char*)malloc(1) /*memory*/, 0 /*size*/ - }; - - /* init the curl session */ - curl_handle = curl_easy_init(); - + CURL* curl_handle = curl_easy_init(); string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // if we are in snapshot mode we can simply open the file; extract the object and return if (mInSnapshotMode) { return extractFromLocalFile(fullUrl, tcl); } - /* specify URL to get */ - curl_easy_setopt(curl_handle, CURLOPT_URL, fullUrl.c_str()); - - /* send all data to this function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - - /* we pass our 'chunk' struct to the callback function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&chunk); - - /* some servers don't like requests that are made without a user-agent - field, so we provide one */ - curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - - /* if redirected , we tell libcurl to follow redirection */ - curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); - + // add some global options to the curl query struct curl_slist* list = nullptr; if (!etag.empty()) { list = curl_slist_append(list, ("If-None-Match: " + etag).c_str()); } - if (!createdNotAfter.empty()) { list = curl_slist_append(list, ("If-Not-After: " + createdNotAfter).c_str()); } - if (!createdNotBefore.empty()) { list = curl_slist_append(list, ("If-Not-Before: " + createdNotBefore).c_str()); } - - // setup curl for headers handling - if (headers != nullptr) { + if (headers) { list = curl_slist_append(list, ("If-None-Match: " + to_string(timestamp)).c_str()); - curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback); - curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, headers); - } - - if (list) { - curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list); - } - - /* get it! */ - res = curl_easy_perform(curl_handle); - std::string errStr; - void* result = nullptr; - if (res == CURLE_OK) { - long response_code; - res = curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code); - if ((res == CURLE_OK) && (response_code != 404)) { - Int_t previousErrorLevel = gErrorIgnoreLevel; - gErrorIgnoreLevel = kFatal; - TMemFile memFile("name", chunk.memory, chunk.size, "READ"); - gErrorIgnoreLevel = previousErrorLevel; - if (!memFile.IsZombie()) { - result = extractFromTFile(memFile, tcl); - if (!result) { - errStr = o2::utils::concat_string("Couldn't retrieve the object ", path); - LOG(ERROR) << errStr; - } - memFile.Close(); - } else { - LOG(DEBUG) << "Object " << path << " is stored in a TMemFile"; - } - } else { - errStr = o2::utils::concat_string("Invalid URL : ", fullUrl); - LOG(ERROR) << errStr; - } - } else { - errStr = o2::utils::concat_string("curl_easy_perform() failed: ", curl_easy_strerror(res)); - fprintf(stderr, "%s", errStr.c_str()); - } - - if (!errStr.empty() && headers) { - (*headers)["Error"] = errStr; } + curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list); + auto content = navigateURLsAndRetrieveContent(curl_handle, fullUrl, tcl, headers); curl_easy_cleanup(curl_handle); - free(chunk.memory); - return result; + return content; } size_t CurlWrite_CallbackFunc_StdString2(void* contents, size_t size, size_t nmemb, std::string* s) @@ -930,7 +1034,7 @@ std::map<std::string, std::string> CcdbApi::retrieveHeaders(std::string const& p /* 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_HEADERFUNCTION, header_map_callback<>); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); // Perform the request, res will get the return code diff --git a/CCDB/test/testBasicCCDBManager.cxx b/CCDB/test/testBasicCCDBManager.cxx index 956a3da911643..3a49d56172c7f 100644 --- a/CCDB/test/testBasicCCDBManager.cxx +++ b/CCDB/test/testBasicCCDBManager.cxx @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(TestBasicCCDBManager) auto& cdb = o2::ccdb::BasicCCDBManager::instance(); cdb.setURL(uri); cdb.setTimestamp((start + stop) / 2); - cdb.setCachingEnabled(true); + cdb.setCaching(true); auto* objA = cdb.get<std::string>(pathA); // will be loaded from scratch and fill the cache LOG(INFO) << "1st reading of A: " << *objA; @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(TestBasicCCDBManager) cdb.resetCreatedNotBefore(); // resetting upper validity limit // disable cache at all (will also clean it) - cdb.setCachingEnabled(false); + cdb.setCaching(false); objA = cdb.get<std::string>(pathA); // will be loaded from scratch, w/o filling the cache LOG(INFO) << "Reading A after disabling the cache: " << *objA; BOOST_CHECK(objA && (*objA) == ccdbObjO); // make sure correct object is loaded diff --git a/CCDB/test/testCcdbApi.cxx b/CCDB/test/testCcdbApi.cxx index f5a619eb09de3..40b51774c9f49 100644 --- a/CCDB/test/testCcdbApi.cxx +++ b/CCDB/test/testCcdbApi.cxx @@ -421,16 +421,24 @@ BOOST_AUTO_TEST_CASE(TestHeaderParsing) BOOST_AUTO_TEST_CASE(TestFetchingHeaders, *utf::precondition(if_reachable())) { + // first store the object + string objectPath = basePath + "objectETag"; + test_fixture f; + TH1F h1("objectETag", "objectETag", 100, 0, 99); + f.api.storeAsTFile(&h1, objectPath, f.metadata); + + // then get the headers std::string etag; std::vector<std::string> headers; std::vector<std::string> pfns; - auto updated = CcdbApi::getCCDBEntryHeaders("http://ccdb-test.cern.ch:8080/TOF/LHCphase/1567080816927", etag, headers); + 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); CcdbApi::parseCCDBHeaders(headers, pfns, etag); BOOST_REQUIRE(etag != ""); BOOST_REQUIRE(pfns.size()); - updated = CcdbApi::getCCDBEntryHeaders("http://ccdb-test.cern.ch:8080/TOF/LHCphase/1567080816927", etag, headers); + updated = CcdbApi::getCCDBEntryHeaders("http://ccdb-test.cern.ch:8080/" + path, etag, headers); BOOST_CHECK_EQUAL(updated, false); } diff --git a/CCDB/test/testCcdbApi_alien.cxx b/CCDB/test/testCcdbApi_alien.cxx new file mode 100644 index 0000000000000..b97b477aa9248 --- /dev/null +++ b/CCDB/test/testCcdbApi_alien.cxx @@ -0,0 +1,102 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// Ccdb unit tests focusing on alien replicas +/// +/// \file testCcdbApi_alien.cxx +/// \author Sandro Wenzel +/// + +#define BOOST_TEST_MODULE CCDB +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include "CCDB/CcdbApi.h" +#include <TH1.h> +#include <boost/test/unit_test.hpp> +#include <iostream> + +using namespace std; +using namespace o2::ccdb; +namespace utf = boost::unit_test; +namespace tt = boost::test_tools; + +static string ccdbUrl; +bool hostReachable = false; + +/** + * Global fixture, ie general setup and teardown + */ +struct Fixture { + Fixture() + { + CcdbApi api; + ccdbUrl = "http://alice-ccdb.cern.ch"; + api.init(ccdbUrl); + cout << "ccdb url: " << ccdbUrl << endl; + hostReachable = api.isHostReachable(); + cout << "Is host reachable ? --> " << hostReachable << endl; + } +}; +BOOST_GLOBAL_FIXTURE(Fixture); + +/** + * Just an accessor to the hostReachable variable to be used to determine whether tests can be ran or not. + */ +struct if_reachable { + tt::assertion_result operator()(utf::test_unit_id) + { + return hostReachable; + } +}; + +/** + * Fixture for the tests, i.e. code is ran in every test that uses it, i.e. it is like a setup and teardown for tests. + */ +struct test_fixture { + test_fixture() + { + api.init(ccdbUrl); + metadata["Hello"] = "World"; + std::cout << "*** " << boost::unit_test::framework::current_test_case().p_name << " ***" << std::endl; + } + ~test_fixture() = default; + + CcdbApi api; + map<string, string> metadata; +}; + +// handle the case where the object comes from alien and redirect does not work with curl +BOOST_AUTO_TEST_CASE(retrieveTemplated_ALIEN, *utf::precondition(if_reachable())) +{ + test_fixture f; + + // try to retrieve an object from the production instance, including the headers + std::map<std::string, std::string> headers; + std::map<std::string, std::string> meta; + + std::string path("/Users/j/jgrosseo/tutorial/efficiency/simple/1"); + { + auto* object = f.api.retrieveFromTFileAny<TH1>(path, meta, -1, &headers); + BOOST_CHECK(object != nullptr); + LOG(INFO) << headers["Content-Location"]; + if (object) { + BOOST_CHECK(headers.size() > 0); + LOG(INFO) << "Histo name " << object->GetName(); + LOG(INFO) << "Number of bins " << object->GetNbinsX() << " Mean " << object->GetMean(); + } + } + + // it should also work without headers of course + { + auto* object = f.api.retrieveFromTFileAny<TH1>(path, meta); + BOOST_CHECK(object != nullptr); + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000..2e2670cb1e803 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,110 @@ +# Changes since 2021-01-05 + +## Changes in Analysis + +- [#5138](https://github.com/AliceO2Group/AliceO2/pull/5138) 2021-01-12: PWGDQ utility classes moved from AnalysisCore to Analysis/Tasks/PWGDQ by [@iarsene](https://github.com/iarsene) +- [#5160](https://github.com/AliceO2Group/AliceO2/pull/5160) 2021-01-12: add alien SE by [@jgrosseo](https://github.com/jgrosseo) +- [#5155](https://github.com/AliceO2Group/AliceO2/pull/5155) 2021-01-13: Add multiplicity distribution task by [@jgrosseo](https://github.com/jgrosseo) +- [#5177](https://github.com/AliceO2Group/AliceO2/pull/5177) 2021-01-13: AliEn metrics by [@jgrosseo](https://github.com/jgrosseo) +- [#5158](https://github.com/AliceO2Group/AliceO2/pull/5158) 2021-01-13: use filters by [@jgrosseo](https://github.com/jgrosseo) +- [#5174](https://github.com/AliceO2Group/AliceO2/pull/5174) 2021-01-14: Add histrogram register and track selection by [@lbariogl](https://github.com/lbariogl) +- [#5183](https://github.com/AliceO2Group/AliceO2/pull/5183) 2021-01-14: Adding opening and run time metrics by [@jgrosseo](https://github.com/jgrosseo) +- [#5195](https://github.com/AliceO2Group/AliceO2/pull/5195) 2021-01-15: fix for 2 readers and 1 input file by [@jgrosseo](https://github.com/jgrosseo) +- [#5200](https://github.com/AliceO2Group/AliceO2/pull/5200) 2021-01-16: PWGHF changing default preselection values for hyperloop by [@nzardosh](https://github.com/nzardosh) +- [#5150](https://github.com/AliceO2Group/AliceO2/pull/5150) 2021-01-16: PWGHF: fix of the histogram registry in the Lc task by [@DelloStritto](https://github.com/DelloStritto) +- [#5205](https://github.com/AliceO2Group/AliceO2/pull/5205) 2021-01-18: Change task names, move initializers by [@ddobrigk](https://github.com/ddobrigk) +- [#5204](https://github.com/AliceO2Group/AliceO2/pull/5204) 2021-01-18: PWGHF: Use pre-selections for Dplus by [@fcatalan92](https://github.com/fcatalan92) +- [#5210](https://github.com/AliceO2Group/AliceO2/pull/5210) 2021-01-18: small bug (re)fix, tpc momentum for dedx instead pvtx by [@akalweit](https://github.com/akalweit) +- [#5182](https://github.com/AliceO2Group/AliceO2/pull/5182) 2021-01-19: Add split tables per particle species in PID response by [@njacazio](https://github.com/njacazio) +- [#5223](https://github.com/AliceO2Group/AliceO2/pull/5223) 2021-01-19: PWGHF : syncing decays in the preselection with enums used for MC by [@nzardosh](https://github.com/nzardosh) +- [#5206](https://github.com/AliceO2Group/AliceO2/pull/5206) 2021-01-19: TableMaker task updated to use configurables for system type, event and track selections by [@iarsene](https://github.com/iarsene) +## Changes in Common + +- [#5134](https://github.com/AliceO2Group/AliceO2/pull/5134) 2021-01-08: RootSerializableKeyValueStore: Add print function by [@sawenzel](https://github.com/sawenzel) +- [#5133](https://github.com/AliceO2Group/AliceO2/pull/5133) 2021-01-11: Populate MC event header with information from current Pythia8 event by [@preghenella](https://github.com/preghenella) +- [#5178](https://github.com/AliceO2Group/AliceO2/pull/5178) 2021-01-13: Fixes in entropy compression memory management by [@shahor02](https://github.com/shahor02) +- [#5184](https://github.com/AliceO2Group/AliceO2/pull/5184) 2021-01-14: Update CommonUtilsLinkDef.h by [@sawenzel](https://github.com/sawenzel) +- [#5199](https://github.com/AliceO2Group/AliceO2/pull/5199) 2021-01-15: (O2-1203) [CTF] use merging/splitting iterators during CTF ecoding/decoding for TPC by [@MichaelLettrich](https://github.com/MichaelLettrich) +- [#5203](https://github.com/AliceO2Group/AliceO2/pull/5203) 2021-01-17: DPL: move driver communication to a separate Service by [@ktf](https://github.com/ktf) +- [#5228](https://github.com/AliceO2Group/AliceO2/pull/5228) 2021-01-19: o2-sim: Make configfile prefixed with correct name by [@sawenzel](https://github.com/sawenzel) +## Changes in DataFormats + +- [#5135](https://github.com/AliceO2Group/AliceO2/pull/5135) 2021-01-08: Add trigger inputs branch by [@AllaMaevskaya](https://github.com/AllaMaevskaya) +- [#5139](https://github.com/AliceO2Group/AliceO2/pull/5139) 2021-01-08: [EMCAL-677] Propagate trigger bits from RDH to TriggerRecord by [@mfasDa](https://github.com/mfasDa) +- [#5142](https://github.com/AliceO2Group/AliceO2/pull/5142) 2021-01-09: Several unrelated fixes in GPU code by [@davidrohr](https://github.com/davidrohr) +- [#5141](https://github.com/AliceO2Group/AliceO2/pull/5141) 2021-01-09: Use GPUTPCO2InterfaceRefit for TPC-ITS matches refit + misc fixes. by [@shahor02](https://github.com/shahor02) +- [#5144](https://github.com/AliceO2Group/AliceO2/pull/5144) 2021-01-10: Fix: increment EMCAL TriggerRecord class version by [@shahor02](https://github.com/shahor02) +- [#5133](https://github.com/AliceO2Group/AliceO2/pull/5133) 2021-01-11: Populate MC event header with information from current Pythia8 event by [@preghenella](https://github.com/preghenella) +- [#5163](https://github.com/AliceO2Group/AliceO2/pull/5163) 2021-01-12: Bring back info treatment in MCEventHeader by [@sawenzel](https://github.com/sawenzel) +- [#5147](https://github.com/AliceO2Group/AliceO2/pull/5147) 2021-01-12: [FV0][O2-1849] Trigger inputs for CTP simulation by [@mslupeck](https://github.com/mslupeck) +- [#5178](https://github.com/AliceO2Group/AliceO2/pull/5178) 2021-01-13: Fixes in entropy compression memory management by [@shahor02](https://github.com/shahor02) +- [#5199](https://github.com/AliceO2Group/AliceO2/pull/5199) 2021-01-15: (O2-1203) [CTF] use merging/splitting iterators during CTF ecoding/decoding for TPC by [@MichaelLettrich](https://github.com/MichaelLettrich) +- [#5192](https://github.com/AliceO2Group/AliceO2/pull/5192) 2021-01-15: FT0 LUT prepared as Singleton for DigitBlockFT0 usage by [@afurs](https://github.com/afurs) +- [#5226](https://github.com/AliceO2Group/AliceO2/pull/5226) 2021-01-19: Bugfix: WorkflowHelper.h was not installled by [@davidrohr](https://github.com/davidrohr) +- [#5227](https://github.com/AliceO2Group/AliceO2/pull/5227) 2021-01-19: Check for selective primary transport also in case of parallel sim by [@preghenella](https://github.com/preghenella) +- [#5216](https://github.com/AliceO2Group/AliceO2/pull/5216) 2021-01-19: TPC Workflow: Move cluster / digit reading from tpc tracking spec into a helper by [@davidrohr](https://github.com/davidrohr) +- [#5228](https://github.com/AliceO2Group/AliceO2/pull/5228) 2021-01-19: o2-sim: Make configfile prefixed with correct name by [@sawenzel](https://github.com/sawenzel) +## Changes in Detectors + +- [#5135](https://github.com/AliceO2Group/AliceO2/pull/5135) 2021-01-08: Add trigger inputs branch by [@AllaMaevskaya](https://github.com/AllaMaevskaya) +- [#5140](https://github.com/AliceO2Group/AliceO2/pull/5140) 2021-01-08: Fix: do not invoke FIT recpoints reader with --use-fit in raw data input mode by [@shahor02](https://github.com/shahor02) +- [#5139](https://github.com/AliceO2Group/AliceO2/pull/5139) 2021-01-08: [EMCAL-677] Propagate trigger bits from RDH to TriggerRecord by [@mfasDa](https://github.com/mfasDa) +- [#5141](https://github.com/AliceO2Group/AliceO2/pull/5141) 2021-01-09: Use GPUTPCO2InterfaceRefit for TPC-ITS matches refit + misc fixes. by [@shahor02](https://github.com/shahor02) +- [#5144](https://github.com/AliceO2Group/AliceO2/pull/5144) 2021-01-10: Fix: increment EMCAL TriggerRecord class version by [@shahor02](https://github.com/shahor02) +- [#5143](https://github.com/AliceO2Group/AliceO2/pull/5143) 2021-01-11: Fixes for OMP and for dumping events for the standalone benchmark by [@davidrohr](https://github.com/davidrohr) +- [#5153](https://github.com/AliceO2Group/AliceO2/pull/5153) 2021-01-12: Avoid 2D params in SVertexer configurable params by [@shahor02](https://github.com/shahor02) +- [#5147](https://github.com/AliceO2Group/AliceO2/pull/5147) 2021-01-12: [FV0][O2-1849] Trigger inputs for CTP simulation by [@mslupeck](https://github.com/mslupeck) +- [#5164](https://github.com/AliceO2Group/AliceO2/pull/5164) 2021-01-13: Allow multiple test workflows with non-overlapping TF-ids by [@shahor02](https://github.com/shahor02) +- [#5178](https://github.com/AliceO2Group/AliceO2/pull/5178) 2021-01-13: Fixes in entropy compression memory management by [@shahor02](https://github.com/shahor02) +- [#5175](https://github.com/AliceO2Group/AliceO2/pull/5175) 2021-01-13: GPU: remove leftover debug messages by [@davidrohr](https://github.com/davidrohr) +- [#5179](https://github.com/AliceO2Group/AliceO2/pull/5179) 2021-01-13: Work towards getting the TPC Tracking QA run standalone from a tracks ROOT file by [@davidrohr](https://github.com/davidrohr) +- [#5185](https://github.com/AliceO2Group/AliceO2/pull/5185) 2021-01-14: Add TPC QC histograms for (limited) monitoring of cluster rejection on the fly while processing without MC information by [@davidrohr](https://github.com/davidrohr) +- [#5180](https://github.com/AliceO2Group/AliceO2/pull/5180) 2021-01-14: Do not encode TDC errors to compressed output stream by [@preghenella](https://github.com/preghenella) +- [#5181](https://github.com/AliceO2Group/AliceO2/pull/5181) 2021-01-14: Standalone TPC Tracking QA (independent from o2-tpc-reco-workflow) by [@davidrohr](https://github.com/davidrohr) +- [#5199](https://github.com/AliceO2Group/AliceO2/pull/5199) 2021-01-15: (O2-1203) [CTF] use merging/splitting iterators during CTF ecoding/decoding for TPC by [@MichaelLettrich](https://github.com/MichaelLettrich) +- [#5192](https://github.com/AliceO2Group/AliceO2/pull/5192) 2021-01-15: FT0 LUT prepared as Singleton for DigitBlockFT0 usage by [@afurs](https://github.com/afurs) +- [#5187](https://github.com/AliceO2Group/AliceO2/pull/5187) 2021-01-15: Fix: remap RootTreeWriter branches only once by [@shahor02](https://github.com/shahor02) +- [#5202](https://github.com/AliceO2Group/AliceO2/pull/5202) 2021-01-16: [EMCAL-630] Store result of the raw fit as energy by [@mfasDa](https://github.com/mfasDa) +- [#5186](https://github.com/AliceO2Group/AliceO2/pull/5186) 2021-01-17: GPU: Add option to tpc-reco-workflow to ship shared cluster map created during tracking by [@davidrohr](https://github.com/davidrohr) +- [#5208](https://github.com/AliceO2Group/AliceO2/pull/5208) 2021-01-18: TPC-ITS matching will use tpc-reco-workflow or on-the-fly created shared cl.map by [@shahor02](https://github.com/shahor02) +- [#5198](https://github.com/AliceO2Group/AliceO2/pull/5198) 2021-01-18: small improvement in fake track rejection by [@pillot](https://github.com/pillot) +- [#5226](https://github.com/AliceO2Group/AliceO2/pull/5226) 2021-01-19: Bugfix: WorkflowHelper.h was not installled by [@davidrohr](https://github.com/davidrohr) +- [#5216](https://github.com/AliceO2Group/AliceO2/pull/5216) 2021-01-19: TPC Workflow: Move cluster / digit reading from tpc tracking spec into a helper by [@davidrohr](https://github.com/davidrohr) +- [#5225](https://github.com/AliceO2Group/AliceO2/pull/5225) 2021-01-19: [EMCAL-630] Fix assignment of energy and time by [@mfasDa](https://github.com/mfasDa) +- [#5228](https://github.com/AliceO2Group/AliceO2/pull/5228) 2021-01-19: o2-sim: Make configfile prefixed with correct name by [@sawenzel](https://github.com/sawenzel) +- [#5221](https://github.com/AliceO2Group/AliceO2/pull/5221) 2021-01-19: refactor TRD digit class by [@bazinski](https://github.com/bazinski) +## Changes in Examples + +- [#5133](https://github.com/AliceO2Group/AliceO2/pull/5133) 2021-01-11: Populate MC event header with information from current Pythia8 event by [@preghenella](https://github.com/preghenella) +## Changes in Framework + +- [#5160](https://github.com/AliceO2Group/AliceO2/pull/5160) 2021-01-12: add alien SE by [@jgrosseo](https://github.com/jgrosseo) +- [#5177](https://github.com/AliceO2Group/AliceO2/pull/5177) 2021-01-13: AliEn metrics by [@jgrosseo](https://github.com/jgrosseo) +- [#5165](https://github.com/AliceO2Group/AliceO2/pull/5165) 2021-01-13: DPL utils: allow customising output-proxy by [@ktf](https://github.com/ktf) +- [#5168](https://github.com/AliceO2Group/AliceO2/pull/5168) 2021-01-13: DPL: increase max size of string metrics to 256 bytes by [@ktf](https://github.com/ktf) +- [#5183](https://github.com/AliceO2Group/AliceO2/pull/5183) 2021-01-14: Adding opening and run time metrics by [@jgrosseo](https://github.com/jgrosseo) +- [#5159](https://github.com/AliceO2Group/AliceO2/pull/5159) 2021-01-14: fix to allow iteratorAt on filtered tables by [@jgrosseo](https://github.com/jgrosseo) +- [#5189](https://github.com/AliceO2Group/AliceO2/pull/5189) 2021-01-15: DPL: add `--forwarding-policy none` to help message by [@ktf](https://github.com/ktf) +- [#5187](https://github.com/AliceO2Group/AliceO2/pull/5187) 2021-01-15: Fix: remap RootTreeWriter branches only once by [@shahor02](https://github.com/shahor02) +- [#5195](https://github.com/AliceO2Group/AliceO2/pull/5195) 2021-01-15: fix for 2 readers and 1 input file by [@jgrosseo](https://github.com/jgrosseo) +- [#5203](https://github.com/AliceO2Group/AliceO2/pull/5203) 2021-01-17: DPL: move driver communication to a separate Service by [@ktf](https://github.com/ktf) +- [#5229](https://github.com/AliceO2Group/AliceO2/pull/5229) 2021-01-19: raw-parser logs DataHeader and DataProcessingHeader info by [@shahor02](https://github.com/shahor02) +## Changes in Generators + +- [#5133](https://github.com/AliceO2Group/AliceO2/pull/5133) 2021-01-11: Populate MC event header with information from current Pythia8 event by [@preghenella](https://github.com/preghenella) +- [#5163](https://github.com/AliceO2Group/AliceO2/pull/5163) 2021-01-12: Bring back info treatment in MCEventHeader by [@sawenzel](https://github.com/sawenzel) +- [#5152](https://github.com/AliceO2Group/AliceO2/pull/5152) 2021-01-12: Revert "Populate event header with information of the current Pythia8… by [@sawenzel](https://github.com/sawenzel) +## Changes in Steer + +- [#5135](https://github.com/AliceO2Group/AliceO2/pull/5135) 2021-01-08: Add trigger inputs branch by [@AllaMaevskaya](https://github.com/AllaMaevskaya) +- [#5139](https://github.com/AliceO2Group/AliceO2/pull/5139) 2021-01-08: [EMCAL-677] Propagate trigger bits from RDH to TriggerRecord by [@mfasDa](https://github.com/mfasDa) +- [#5147](https://github.com/AliceO2Group/AliceO2/pull/5147) 2021-01-12: [FV0][O2-1849] Trigger inputs for CTP simulation by [@mslupeck](https://github.com/mslupeck) +- [#5187](https://github.com/AliceO2Group/AliceO2/pull/5187) 2021-01-15: Fix: remap RootTreeWriter branches only once by [@shahor02](https://github.com/shahor02) +## Changes in Utilities + +- [#5151](https://github.com/AliceO2Group/AliceO2/pull/5151) 2021-01-12: Small fixes by [@sawenzel](https://github.com/sawenzel) +- [#5156](https://github.com/AliceO2Group/AliceO2/pull/5156) 2021-01-12: o2-sim: Better signal propagation; small fix in jobutils by [@sawenzel](https://github.com/sawenzel) +- [#5169](https://github.com/AliceO2Group/AliceO2/pull/5169) 2021-01-13: jobutils: exit workflows on first task error by [@sawenzel](https://github.com/sawenzel) +- [#5172](https://github.com/AliceO2Group/AliceO2/pull/5172) 2021-01-14: Fix o2_add_dpl_workflow on Ubuntu and some other systems by [@davidrohr](https://github.com/davidrohr) +- [#5199](https://github.com/AliceO2Group/AliceO2/pull/5199) 2021-01-15: (O2-1203) [CTF] use merging/splitting iterators during CTF ecoding/decoding for TPC by [@MichaelLettrich](https://github.com/MichaelLettrich) +- [#5196](https://github.com/AliceO2Group/AliceO2/pull/5196) 2021-01-15: small fix in jobutils by [@sawenzel](https://github.com/sawenzel) diff --git a/CMakeLists.txt b/CMakeLists.txt index 616f9bb7b7872..f989fdf44cb9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,10 @@ if (DPL_TESTS_BATCH_MODE) set(DPL_WORKFLOW_TESTS_EXTRA_OPTIONS -b) endif() +if (DPL_ENABLE_TRACING) + add_definitions(-DDPL_ENABLE_TRACING) +endif() + # include macros and functions that are used in the following subdirectories' # CMakeLists.txt include(O2AddExecutable) @@ -60,10 +64,12 @@ include(O2AddHeaderOnlyLibrary) include(O2AddLibrary) include(O2AddTest) include(O2AddTestRootMacro) +include(O2ReportNonTestedMacros) include(O2TargetRootDictionary) include(O2DataFile) include(O2TargetManPage) include(O2AddWorkflow) +include(O2SetROOTPCMDependencies) # Main targets of the project in various subdirectories. Order matters. add_subdirectory(Common) @@ -104,9 +110,9 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() if(BUILD_TEST_ROOT_MACROS) add_subdirectory(macro) - include(O2ReportNonTestedMacros) o2_report_non_tested_macros() endif() add_subdirectory(packaging) - endif() + +set_root_pcm_dependencies() diff --git a/CODEOWNERS b/CODEOWNERS index 5d6caaf8fd90e..33ab2dfb99c6d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -12,7 +12,9 @@ /Algorithm @matthiasrichter -/Analysis @jgrosseo +/Analysis @jgrosseo @iarsene +/Analysis/Tasks/ALICE3 @ginnocen +/Analysis/Tasks/PWGHF @ginnocen /CCDB @costing @Barthelemy @sawenzel @AliceO2Group/framework-admins @@ -113,10 +115,10 @@ /Utilities @AliceO2Group/framework-admins /Utilities/Mergers @Barthelemy @knopers8 +/Utilities/DataSampling @Barthelemy @knopers8 #/Utilities/DataCompression #/Utilities/DataFlow #/Utilities/MCStepLogger -#/Utilities/Mergers #/Utilities/O2Device #/Utilities/O2MessageMonitor #/Utilities/PCG @@ -124,3 +126,4 @@ #/Utilities/Tools #/Utilities/aliceHLTwrapper #/Utilities/hough +/Utilities/rANS @shahor02 @MichaelLettrich diff --git a/Common/Constants/include/CommonConstants/LHCConstants.h b/Common/Constants/include/CommonConstants/LHCConstants.h index 083c1a79d45dc..ba3cc93331a5a 100644 --- a/Common/Constants/include/CommonConstants/LHCConstants.h +++ b/Common/Constants/include/CommonConstants/LHCConstants.h @@ -30,6 +30,8 @@ static constexpr double LHCRFFreq = 400.789e6; // LHC R static constexpr double LHCBunchSpacingNS = 10 * 1.e9 / LHCRFFreq; // bunch spacing in ns (10 RFbuckets) static constexpr double LHCOrbitNS = LHCMaxBunches * LHCBunchSpacingNS; // orbit duration in ns static constexpr double LHCRevFreq = 1.e9 / LHCOrbitNS; // revolution frequency +static constexpr double LHCBunchSpacingMS = LHCBunchSpacingNS * 1e-3; // bunch spacing in ms (10 RFbuckets) +static constexpr double LHCOrbitMS = LHCOrbitNS * 1e-3; // orbit duration in ms static constexpr unsigned int MaxNOrbits = 0xffffffff; } // namespace lhc diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index 9f62599916a3f..b303ec049f90e 100644 --- a/Common/Constants/include/CommonConstants/PhysicsConstants.h +++ b/Common/Constants/include/CommonConstants/PhysicsConstants.h @@ -22,6 +22,7 @@ namespace constants namespace physics { // particles masses +constexpr float MassPhoton = 0.0; constexpr float MassElectron = 0.000511; constexpr float MassMuon = 0.105658; constexpr float MassPionCharged = 0.139570; @@ -29,10 +30,12 @@ constexpr float MassPionNeutral = 0.134976; constexpr float MassKaonCharged = 0.493677; constexpr float MassKaonNeutral = 0.497648; constexpr float MassProton = 0.938272; -constexpr float MassDeuteron = 1.875613; -constexpr float MassTriton = 2.809250; -constexpr float MassHelium3 = 2.809230; -constexpr float MassAlpha = 3.727379; +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 LightSpeedCm2S = 299792458.e2; // C in cm/s constexpr float LightSpeedCm2NS = LightSpeedCm2S * 1e-9; // C in cm/ns diff --git a/Common/Field/include/Field/MagFieldFast.h b/Common/Field/include/Field/MagFieldFast.h index 79d9d3c16e0c9..4d904e7adc464 100644 --- a/Common/Field/include/Field/MagFieldFast.h +++ b/Common/Field/include/Field/MagFieldFast.h @@ -14,9 +14,12 @@ #ifndef ALICEO2_FIELD_MAGFIELDFAST_H_ #define ALICEO2_FIELD_MAGFIELDFAST_H_ -#include <Rtypes.h> +#include <GPUCommonRtypes.h> +#include "MathUtils/Cartesian.h" + +#ifndef GPUCA_GPUCODE_DEVICE #include <string> -#include "MathUtils/Cartesian3D.h" +#endif namespace o2 { @@ -49,11 +52,11 @@ class MagFieldFast bool Field(const double xyz[3], double bxyz[3]) const; bool Field(const float xyz[3], float bxyz[3]) const; - bool Field(const Point3D<float> xyz, float bxyz[3]) const; + bool Field(const math_utils::Point3D<float> xyz, float 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 Point3D<float> xyz, double& b) const; - bool GetBcomp(EDim comp, const Point3D<float> xyz, float& b) const; + bool GetBcomp(EDim comp, const math_utils::Point3D<float> xyz, double& b) const; + bool GetBcomp(EDim comp, const math_utils::Point3D<float> xyz, float& b) const; bool GetBx(const double xyz[3], double& bx) const { return GetBcomp(kX, xyz, bx); } bool GetBx(const float xyz[3], float& bx) const { return GetBcomp(kX, xyz, bx); } diff --git a/Common/Field/include/Field/MagneticField.h b/Common/Field/include/Field/MagneticField.h index 4d19c6800b99b..d4241fe49c498 100644 --- a/Common/Field/include/Field/MagneticField.h +++ b/Common/Field/include/Field/MagneticField.h @@ -68,6 +68,9 @@ class MagneticField : public FairField /// Default destructor ~MagneticField() override = default; + /// create field from rounded value, i.e. +-5 or +-2 kGauss + static MagneticField* createNominalField(int fld); + /// real field creation is here void CreateField(); diff --git a/Common/Field/src/MagFieldFast.cxx b/Common/Field/src/MagFieldFast.cxx index 2016012e02b24..34eeadfc3b59c 100644 --- a/Common/Field/src/MagFieldFast.cxx +++ b/Common/Field/src/MagFieldFast.cxx @@ -13,15 +13,16 @@ /// \author ruben.shahoyan@cern.ch // #include "Field/MagFieldFast.h" -#include <FairLogger.h> -#include <TString.h> -#include <TSystem.h> +#include <GPUCommonLogger.h> + +#ifndef GPUCA_GPUCODE_DEVICE #include <cmath> #include <fstream> #include <sstream> +using namespace std; +#endif using namespace o2::field; -using namespace std; ClassImp(o2::field::MagFieldFast); @@ -30,6 +31,10 @@ const float MagFieldFast::kSolR2Max[MagFieldFast::kNSolRRanges] = {80.f * 80.f, const float MagFieldFast::kSolZMax = 550.0f; +#ifndef GPUCA_STANDALONE +#include <TString.h> +#include <TSystem.h> + //_______________________________________________________________________ MagFieldFast::MagFieldFast(const string inpFName) : mFactorSol(1.f) { @@ -68,22 +73,25 @@ bool MagFieldFast::LoadData(const string inpFName) SolParam* curParam = nullptr; while (std::getline(in, line)) { - if (line.empty() || line[0] == '#') + if (line.empty() || line[0] == '#') { continue; // empy or comment + } std::stringstream ss(line); int cnt = 0; if (component < 0) { - while (cnt < 4 && (ss >> header[cnt++])) + while (cnt < 4 && (ss >> header[cnt++])) { ; + } if (cnt != 4) { LOG(FATAL) << "Wrong header " << line; return false; } curParam = &mSolPar[header[0]][header[1]][header[2]]; } else { - while (cnt < header[3] && (ss >> curParam->parBxyz[component][cnt++])) + while (cnt < header[3] && (ss >> curParam->parBxyz[component][cnt++])) { ; + } if (cnt != header[3]) { LOG(FATAL) << "Wrong data (npar=" << cnt << ") for param " << header[0] << " " << header[1] << " " << header[2] << " " << header[3] << " " << line; @@ -103,6 +111,7 @@ bool MagFieldFast::LoadData(const string inpFName) } return true; } +#endif // GPUCA_STANDALONE //_______________________________________________________________________ bool MagFieldFast::Field(const double xyz[3], double bxyz[3]) const @@ -135,7 +144,7 @@ bool MagFieldFast::GetBcomp(EDim comp, const double xyz[3], double& b) const } //_______________________________________________________________________ -bool MagFieldFast::GetBcomp(EDim comp, const Point3D<float> xyz, double& b) const +bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D<float> xyz, double& b) const { // get field int zSeg, rSeg, quadrant; @@ -149,7 +158,7 @@ bool MagFieldFast::GetBcomp(EDim comp, const Point3D<float> xyz, double& b) cons } //_______________________________________________________________________ -bool MagFieldFast::GetBcomp(EDim comp, const Point3D<float> xyz, float& b) const +bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D<float> xyz, float& b) const { // get field int zSeg, rSeg, quadrant; @@ -193,7 +202,7 @@ bool MagFieldFast::Field(const float xyz[3], float bxyz[3]) const } //_______________________________________________________________________ -bool MagFieldFast::Field(const Point3D<float> xyz, float bxyz[3]) const +bool MagFieldFast::Field(const math_utils::Point3D<float> xyz, float bxyz[3]) const { // get field int zSeg, rSeg, quadrant; @@ -215,20 +224,24 @@ bool MagFieldFast::GetSegment(float x, float y, float z, int& zSeg, int& rSeg, i const float zGridSpaceInv = 1.f / (kSolZMax * 2 / kNSolZRanges); zSeg = -1; if (z < kSolZMax) { - if (z > -kSolZMax) + if (z > -kSolZMax) { zSeg = (z + kSolZMax) * zGridSpaceInv; // solenoid params - else { // need to check dipole params + } else { // need to check dipole params return false; } - } else + } else { return false; + } // R segment float xx = x * x, yy = y * y, rr = xx + yy; - for (rSeg = 0; rSeg < kNSolRRanges; rSeg++) - if (rr < kSolR2Max[rSeg]) + for (rSeg = 0; rSeg < kNSolRRanges; rSeg++) { + if (rr < kSolR2Max[rSeg]) { break; - if (rSeg == kNSolRRanges) - return kFALSE; + } + } + if (rSeg == kNSolRRanges) { + return false; + } quadrant = GetQuadrant(x, y); return true; } diff --git a/Common/Field/src/MagFieldParam.cxx b/Common/Field/src/MagFieldParam.cxx index 66310ea7b4117..dd7fb68978c7f 100644 --- a/Common/Field/src/MagFieldParam.cxx +++ b/Common/Field/src/MagFieldParam.cxx @@ -48,8 +48,9 @@ void MagFieldParam::SetParam(const MagneticField* field) void MagFieldParam::putParams(FairParamList* list) { /// store parameters in the list - if (!list) + if (!list) { return; + } list->add("Map Type ID", int(mMapType)); list->add("Beam Type ID", int(mBeamType)); list->add("Integral Type", mDefaultIntegration); @@ -65,33 +66,42 @@ Bool_t MagFieldParam::getParams(FairParamList* list) { /// retried parameters int int2enum = 0; - if (!list->fill("Map Type ID", &int2enum)) + if (!list->fill("Map Type ID", &int2enum)) { return kFALSE; + } mMapType = static_cast<BMap_t>(int2enum); - if (!list->fill("Beam Type ID", &int2enum)) + if (!list->fill("Beam Type ID", &int2enum)) { return kFALSE; + } mBeamType = static_cast<BeamType_t>(int2enum); // - if (!list->fill("Integral Type", &mDefaultIntegration)) + if (!list->fill("Integral Type", &mDefaultIntegration)) { return kFALSE; - if (!list->fill("Fact.Solenoid", &mFactorSol)) + } + if (!list->fill("Fact.Solenoid", &mFactorSol)) { return kFALSE; - if (!list->fill("Fact.Dipole ", &mFactorDip)) + } + if (!list->fill("Fact.Dipole ", &mFactorDip)) { return kFALSE; - if (!list->fill("Beam Energy ", &mBeamEnergy)) + } + if (!list->fill("Beam Energy ", &mBeamEnergy)) { return kFALSE; - if (!list->fill("Max. Field ", &mMaxField)) + } + if (!list->fill("Max. Field ", &mMaxField)) { return kFALSE; + } FairParamObj* parpath = list->find("Path to map "); - if (!parpath) + if (!parpath) { return kFALSE; + } int lgt = parpath->getLength(); // RS: is there a bug in FairParamList::fill(const Text_t* name,Text_t* value,const Int_t length)? // I think the "if (l<length-1)" should be "if (l<length)" char cbuff[lgt + 2]; memset(cbuff, 0, sizeof(char) * (lgt + 2)); - if (!list->fill("Path to map ", cbuff, lgt + 2)) + if (!list->fill("Path to map ", cbuff, lgt + 2)) { return kFALSE; + } mMapPath = cbuff; return kTRUE; } diff --git a/Common/Field/src/MagneticField.cxx b/Common/Field/src/MagneticField.cxx index 5fb485ee1c3f1..3afabde8a0bbb 100644 --- a/Common/Field/src/MagneticField.cxx +++ b/Common/Field/src/MagneticField.cxx @@ -148,6 +148,29 @@ MagneticField::MagneticField(const MagFieldParam& param) CreateField(); } +MagneticField* MagneticField::createNominalField(int fld) +{ + float fldCoeff; + o2::field::MagFieldParam::BMap_t fldType; + switch (std::abs(fld)) { + case 5: + fldType = o2::field::MagFieldParam::k5kG; + fldCoeff = fld > 0 ? 1. : -1; + break; + case 0: + fldType = o2::field::MagFieldParam::k5kG; + fldCoeff = 0; + break; + case 2: + fldType = o2::field::MagFieldParam::k2kG; + fldCoeff = fld > 0 ? 1. : -1; + break; + default: + LOG(FATAL) << "Field option " << fld << " is not supported, use +-2, +-5 or 0"; + }; + return new o2::field::MagneticField("Maps", "Maps", fldCoeff, fldCoeff, fldType); +} + void MagneticField::CreateField() { /* @@ -162,16 +185,18 @@ void MagneticField::CreateField() << "; Helix tracking chosen instead"; mDefaultIntegration = 2; } - if (mDefaultIntegration == 0) + if (mDefaultIntegration == 0) { mPrecisionInteg = 0; + } if (mBeamEnergy <= 0 && mBeamType != MagFieldParam::kNoBeamField) { - if (mBeamType == MagFieldParam::kBeamTypepp) + if (mBeamType == MagFieldParam::kBeamTypepp) { mBeamEnergy = 7000.; // max proton energy - else if (mBeamType == MagFieldParam::kBeamTypeAA) + } else if (mBeamType == MagFieldParam::kBeamTypeAA) { mBeamEnergy = 2760; // max PbPb energy - else if (mBeamType == MagFieldParam::kBeamTypepA || mBeamType == MagFieldParam::kBeamTypeAp) + } else if (mBeamType == MagFieldParam::kBeamTypepA || mBeamType == MagFieldParam::kBeamTypeAp) { mBeamEnergy = 2760; // same rigitiy max PbPb energy + } // LOG(INFO) << "MagneticField::CreateField: Maximim possible beam energy for requested beam is assumed"; } @@ -234,8 +259,9 @@ void MagneticField::Field(const Double_t* __restrict__ xyz, Double_t* __restrict */ // b[0]=b[1]=b[2]=0.0; - if (mFastField && mFastField->Field(xyz, b)) + if (mFastField && mFastField->Field(xyz, b)) { return; + } if (mMeasuredMap && xyz[2] > mMeasuredMap->getMinZ() && xyz[2] < mMeasuredMap->getMaxZ()) { mMeasuredMap->Field(xyz, b); @@ -261,8 +287,9 @@ Double_t MagneticField::getBz(const Double_t* xyz) const if (mFastField) { double bz = 0; - if (mFastField->GetBz(xyz, bz)) + if (mFastField->GetBz(xyz, bz)) { return bz; + } } if (mMeasuredMap && xyz[2] > mMeasuredMap->getMinZ() && xyz[2] < mMeasuredMap->getMaxZ()) { double bz = mMeasuredMap->getBz(xyz); @@ -455,8 +482,9 @@ void MagneticField::setFactorSolenoid(Float_t fc) mMultipicativeFactorSolenoid = fc; break; // case kConvMap2005: mMultipicativeFactorSolenoid = fc; break; } - if (mFastField) + if (mFastField) { mFastField->setFactorSol(getFactorSolenoid()); + } } void MagneticField::setFactorDipole(Float_t fc) @@ -642,8 +670,9 @@ void MagneticField::FillParContainer() void MagneticField::AllowFastField(bool v) { if (v) { - if (!mFastField) + if (!mFastField) { mFastField = std::make_unique<MagFieldFast>(getFactorSolenoid(), mMapType == MagFieldParam::k2kG ? 2 : 5); + } } else { mFastField.reset(nullptr); } diff --git a/Common/Field/src/MagneticWrapperChebyshev.cxx b/Common/Field/src/MagneticWrapperChebyshev.cxx index 10e8d7d8924d1..db73f2bf7ce45 100644 --- a/Common/Field/src/MagneticWrapperChebyshev.cxx +++ b/Common/Field/src/MagneticWrapperChebyshev.cxx @@ -1403,8 +1403,9 @@ void MagneticWrapperChebyshev::saveData(const char* outfile) const // TPCIntegral part fprintf(stream, "START TPCINT\n#Number of pieces\n%d\n", mNumberOfParameterizationTPC); - for (int ip = 0; ip < mNumberOfParameterizationTPC; ip++) + for (int ip = 0; ip < mNumberOfParameterizationTPC; ip++) { getParameterTPCIntegral(ip)->saveData(stream); + } fprintf(stream, "#\nEND TPCINT\n"); // TPCRatIntegral part diff --git a/Common/MathUtils/CMakeLists.txt b/Common/MathUtils/CMakeLists.txt index e1ee3ab4233b2..0fa418fb1c385 100644 --- a/Common/MathUtils/CMakeLists.txt +++ b/Common/MathUtils/CMakeLists.txt @@ -11,13 +11,9 @@ o2_add_library( MathUtils SOURCES src/CachingTF1.cxx - src/Cartesian2D.cxx - src/Cartesian3D.cxx + src/Cartesian.cxx src/Chebyshev3D.cxx src/Chebyshev3DCalc.cxx - src/MathBase.cxx - src/RandomRing.cxx - src/Primitive2D.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist FairRoot::Base @@ -32,13 +28,12 @@ o2_target_root_dictionary( HEADERS include/MathUtils/Utils.h include/MathUtils/Chebyshev3D.h include/MathUtils/Chebyshev3DCalc.h - include/MathUtils/MathBase.h - include/MathUtils/Cartesian2D.h - include/MathUtils/Cartesian3D.h + include/MathUtils/fit.h + include/MathUtils/Cartesian.h + include/MathUtils/CartesianGPU.h include/MathUtils/CachingTF1.h include/MathUtils/RandomRing.h - include/MathUtils/Primitive2D.h - include/MathUtils/Bracket.h) + include/MathUtils/Primitive2D.h) o2_add_test( CachingTF1 @@ -49,7 +44,7 @@ o2_add_test( o2_add_test( Cartesian3D - SOURCES test/testCartesian3D.cxx + SOURCES test/testCartesian.cxx COMPONENT_NAME MathUtils PUBLIC_LINK_LIBRARIES O2::MathUtils LABELS utils) diff --git a/Common/MathUtils/include/MathUtils/Bracket.h b/Common/MathUtils/include/MathUtils/Bracket.h deleted file mode 100644 index f93c6265da546..0000000000000 --- a/Common/MathUtils/include/MathUtils/Bracket.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Bracket.h -/// \brief Class to represent an interval and some operations over it -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_BRACKET_H -#define ALICEO2_BRACKET_H - -#include <Rtypes.h> - -namespace o2 -{ -namespace utils -{ - -template <typename T = float> -class Bracket -{ - public: - enum Relation : int { Below = -1, - Inside = 0, - Above = 1 }; - - Bracket(T minv, T maxv) : mMin(minv), mMax(maxv) {} - Bracket(const Bracket<T>& src) = default; - Bracket() = default; - ~Bracket() = default; - void set(T minv, T maxv) - { - mMin = minv; - mMax = maxv; - } - bool operator<(const T rhs) - { - return mMax < rhs; - } - bool operator>(const T rhs) - { - return mMin > rhs; - } - - bool operator<(const Bracket<T>& rhs) - { - return mMax < rhs.mMin; - } - bool operator>(const Bracket<T>& rhs) - { - return mMin > rhs.mMax; - } - bool operator==(const Bracket<T>& rhs) - { - return mMin == rhs.mMin && mMax == rhs.mMax; - } - - void setMax(T v) { mMax = v; } - void setMin(T v) { mMin = v; } - T& max() { return mMax; } - T& min() { return mMin; } - T max() const { return mMax; } - T min() const { return mMin; } - T mean() const { return (mMin + mMax) / 2; } - T delta() const { return mMax - mMin; } - void update(T v) - { - // update limits - if (v > mMax) { - mMax = v; - } - if (v < mMin) { - mMin = v; - } - } - Relation isOutside(const Bracket<T>& t) const - { - ///< check if provided bracket is outside of this bracket - return t.mMax < mMin ? Below : (t.mMin > mMax ? Above : Inside); - } - int isOutside(T t, T tErr) const - { - ///< check if the provided value t with error tErr is outside of the bracket - return t + tErr < mMin ? Below : (t - tErr > mMax ? Above : Inside); - } - - private: - T mMin = 0, mMax = 0; - - ClassDefNV(Bracket, 1); -}; -} // namespace utils -} // namespace o2 - -#endif diff --git a/Common/MathUtils/include/MathUtils/CachingTF1.h b/Common/MathUtils/include/MathUtils/CachingTF1.h index ccea20753c102..4c3b64915b250 100644 --- a/Common/MathUtils/include/MathUtils/CachingTF1.h +++ b/Common/MathUtils/include/MathUtils/CachingTF1.h @@ -18,7 +18,7 @@ namespace o2 { -namespace base +namespace math_utils { class CachingTF1 : public TF1 { @@ -45,7 +45,7 @@ class CachingTF1 : public TF1 std::vector<double>* mGammaCache = &fGamma; ClassDefOverride(CachingTF1, 1); }; -} // namespace base +} // namespace math_utils } // namespace o2 #endif diff --git a/Common/MathUtils/include/MathUtils/Cartesian.h b/Common/MathUtils/include/MathUtils/Cartesian.h new file mode 100644 index 0000000000000..82f9acfdd1aaf --- /dev/null +++ b/Common/MathUtils/include/MathUtils/Cartesian.h @@ -0,0 +1,271 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 O2 interface class to ROOT::Math::Transform3D for Cartesian transformations +/// @author Sandro Wenzel, sandro.wenzel@cern.ch +/// @author Ruben Shahoyan, ruben.shahoyan@cern.ch + +#ifndef ALICEO2_CARTESIAN3D_H +#define ALICEO2_CARTESIAN3D_H + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) +#include <Math/GenVector/DisplacementVector3D.h> +#include <Math/GenVector/PositionVector3D.h> +#include <Math/GenVector/Rotation3D.h> +#include <Math/GenVector/Transform3D.h> +#include <Math/GenVector/Translation3D.h> +#include <Math/GenVector/DisplacementVector2D.h> +#include <Math/GenVector/PositionVector2D.h> +#include <TGeoMatrix.h> +#include <iosfwd> +#else +#include "GPUCommonMath.h" +#include "CartesianGPU.h" +#endif +#include "GPUROOTCartesianFwd.h" + +namespace o2 +{ + +namespace math_utils +{ + +// more typedefs can follow + +/// predefined transformations: Tracking->Local, Tracking->Global, Local->Global etc +/// The IDs must be < 32 + +struct TransformType { + static constexpr int L2G = 0; + static constexpr int T2L = 1; + static constexpr int T2G = 2; + static constexpr int T2GRot = 3; +}; /// transformation types + +template <typename value_T> +class Rotation2D +{ + // + // class to perform rotation of 3D (around Z) and 2D points + + public: + using value_t = value_T; + + Rotation2D() = default; + Rotation2D(value_t cs, value_t sn) : mCos(cs), mSin(sn) {} + Rotation2D(value_t phiZ) : mCos(cos(phiZ)), mSin(sin(phiZ)) {} + ~Rotation2D() = default; + Rotation2D(const Rotation2D& src) = default; + Rotation2D(Rotation2D&& src) = default; + Rotation2D& operator=(const Rotation2D& src) = default; + Rotation2D& operator=(Rotation2D&& src) = default; + + void set(value_t phiZ) + { + mCos = cos(phiZ); + mSin = sin(phiZ); + } + + void set(value_t cs, value_t sn) + { + mCos = cs; + mSin = sn; + } + + void getComponents(value_t& cs, value_t& sn) const + { + cs = mCos; + sn = mSin; + } + + template <typename T> + Point3D<T> operator()(const Point3D<T>& v) const + { // local->master + return Point3D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos, v.Z()); + } + + template <typename T> + Point3D<T> operator^(const Point3D<T>& v) const + { // master->local + return Point3D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos, v.Z()); + } + + template <typename T> + Vector3D<T> operator()(const Vector3D<T>& v) const + { // local->master + return Vector3D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos, v.Z()); + } + + template <typename T> + Vector3D<T> operator^(const Vector3D<T>& v) const + { // master->local + return Vector3D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos, v.Z()); + } + + template <typename T> + Point2D<T> operator()(const Point2D<T>& v) const + { // local->master + return Point2D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos); + } + + template <typename T> + Point2D<T> operator^(const Point2D<T>& v) const + { // master->local + return Point2D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos); + } + + template <typename T> + Vector2D<T> operator()(const Vector2D<T>& v) const + { // local->master + return Vector2D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos); + } + + template <typename T> + Vector2D<T> operator^(const Vector2D<T>& v) const + { // master->local + return Vector2D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos); + } + + private: + value_t mCos = 1; ///< cos of rotation angle + value_t mSin = 0; ///< sin of rotation angle + + ClassDefNV(Rotation2D, 2); +}; + +using Rotation2Df_t = Rotation2D<float>; +using Rotation2Dd_t = Rotation2D<double>; + +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIGPUCODE) + +class Transform3D : public ROOT::Math::Transform3D +{ + // + // Class to perform geom.transformations (rotation and displacements only) in + // double precision over the cartesian points and vectors (float or double). + // Adds to the base ROOT::Math::Transform3D<double> class a convertor from + // TGeoMatrix. + // To be used instead of TGeoHMatrix for all transformations of hits, + // clusters etc. + // + + public: + Transform3D() = default; + Transform3D(const TGeoMatrix& m); + ~Transform3D() = default; + + // inherit assignment operators of the base class + using ROOT::Math::Transform3D::operator=; + + // to avoid conflict between the base Transform3D(const ForeignMatrix & m) and + // Transform3D(const TGeoMatrix &m) constructors we cannot inherit base c-tors, + // therefore we redefine them here + Transform3D(const ROOT::Math::Rotation3D& r, const Vector& v) : ROOT::Math::Transform3D(r, v) {} + Transform3D(const ROOT::Math::Rotation3D& r, const ROOT::Math::Translation3D& t) : ROOT::Math::Transform3D(r, t) {} + template <class IT> + Transform3D(IT begin, IT end) : ROOT::Math::Transform3D(begin, end) + { + } + + // conversion operator to TGeoHMatrix + operator TGeoHMatrix&() const + { + static TGeoHMatrix tmp; + double rot[9], tra[3]; + GetComponents(rot[0], rot[1], rot[2], tra[0], rot[3], rot[4], rot[5], tra[1], rot[6], rot[7], rot[8], tra[2]); + tmp.SetRotation(rot); + tmp.SetTranslation(tra); + return tmp; + } + + void set(const TGeoMatrix& m); // set parameters from TGeoMatrix + + using ROOT::Math::Transform3D::operator(); + // the local->master transformation for points and vectors can be + // done in operator form (inherited from base Transform3D) as + // Point3D pnt; + // Transform3D trans; + // auto pntTr0 = trans(pnt); // 1st version + // auto pntTr1 = trans*pnt; // 2nd version + // + // For the inverse transform we define our own operator^ + + template <typename T> + Point3D<T> operator^(const Point3D<T>& p) const + { // master->local + return ApplyInverse(p); + } + + template <typename T> + Vector3D<T> operator^(const Vector3D<T>& v) const + { // local->master + return ApplyInverse(v); + } + + // TGeoHMatrix-like aliases + template <typename T> + void LocalToMaster(const Point3D<T>& loc, Point3D<T>& mst) const + { + mst = operator()(loc); + } + + template <typename T> + void MasterToLocal(const Point3D<T>& mst, Point3D<T>& loc) const + { + loc = operator^(mst); + } + + template <typename T> + void LocalToMasterVect(const Point3D<T>& loc, Point3D<T>& mst) const + { + mst = operator()(loc); + } + + template <typename T> + void MasterToLocalVect(const Point3D<T>& mst, Point3D<T>& loc) const + { + loc = operator^(mst); + } + + void print() const; + + ClassDefNV(Transform3D, 1); +}; +#endif // Disable for GPU +} // namespace math_utils +} // namespace o2 + +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIGPUCODE) +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 +{ + +/// Defining Point3D explicitly as trivially copyable +/// +/// std::is_trivially_copyable<ROOT::Math::Cartesian3D<T>> 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 <typename T> +struct is_trivially_copyable<o2::math_utils::Point3D<T>> : std::true_type { +}; +} // namespace std +#endif // Disable for GPU + +#endif diff --git a/Common/MathUtils/include/MathUtils/Cartesian2D.h b/Common/MathUtils/include/MathUtils/Cartesian2D.h deleted file mode 100644 index 72703ec78404a..0000000000000 --- a/Common/MathUtils/include/MathUtils/Cartesian2D.h +++ /dev/null @@ -1,27 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Defs.h -/// @brief Common definitions for 2D coordinates -/// @author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de - -#ifndef ALICEO2_CARTESIAN2D_H -#define ALICEO2_CARTESIAN2D_H - -#include "Math/GenVector/DisplacementVector2D.h" -#include "Math/GenVector/PositionVector2D.h" - -template <typename T> -using Point2D = ROOT::Math::PositionVector2D<ROOT::Math::Cartesian2D<T>, ROOT::Math::DefaultCoordinateSystemTag>; - -template <typename T> -using Vector2D = ROOT::Math::DisplacementVector2D<ROOT::Math::Cartesian2D<T>, ROOT::Math::DefaultCoordinateSystemTag>; - -#endif diff --git a/Common/MathUtils/include/MathUtils/Cartesian3D.h b/Common/MathUtils/include/MathUtils/Cartesian3D.h deleted file mode 100644 index 3305da7ee9edb..0000000000000 --- a/Common/MathUtils/include/MathUtils/Cartesian3D.h +++ /dev/null @@ -1,247 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 O2 interface class to ROOT::Math::Transform3D for Cartesian transformations -/// @author Sandro Wenzel, sandro.wenzel@cern.ch -/// @author Ruben Shahoyan, ruben.shahoyan@cern.ch - -#ifndef ALICEO2_CARTESIAN3D_H -#define ALICEO2_CARTESIAN3D_H - -#include <Math/GenVector/DisplacementVector3D.h> -#include <Math/GenVector/PositionVector3D.h> -#include <Math/GenVector/Rotation3D.h> -#include <Math/GenVector/Transform3D.h> -#include <Math/GenVector/Translation3D.h> -#include <Rtypes.h> -#include <TGeoMatrix.h> -#include <iosfwd> -#include "MathUtils/Cartesian2D.h" - -template <typename T> -using Point3D = ROOT::Math::PositionVector3D<ROOT::Math::Cartesian3D<T>, ROOT::Math::DefaultCoordinateSystemTag>; -template <typename T> -using Vector3D = ROOT::Math::DisplacementVector3D<ROOT::Math::Cartesian3D<T>, ROOT::Math::DefaultCoordinateSystemTag>; -// more typedefs can follow - -namespace o2 -{ -/// predefined transformations: Tracking->Local, Tracking->Global, Local->Global etc -/// The IDs must be < 32 - -struct TransformType { - static constexpr int L2G = 0; - static constexpr int T2L = 1; - static constexpr int T2G = 2; - static constexpr int T2GRot = 3; -}; /// transformation types - -class Rotation2D -{ - // - // class to perform rotation of 3D (around Z) and 2D points - - public: - Rotation2D() = default; - Rotation2D(float cs, float sn) : mCos(cs), mSin(sn) {} - Rotation2D(float phiZ) : mCos(cos(phiZ)), mSin(sin(phiZ)) {} - ~Rotation2D() = default; - Rotation2D(const Rotation2D& src) = default; - Rotation2D& operator=(const Rotation2D& src) = default; - - void set(float phiZ) - { - mCos = cos(phiZ); - mSin = sin(phiZ); - } - - void set(float cs, float sn) - { - mCos = cs; - mSin = sn; - } - - void getComponents(float& cs, float& sn) const - { - cs = mCos; - sn = mSin; - } - - template <typename T> - Point3D<T> operator()(const Point3D<T>& v) const - { // local->master - return Point3D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos, v.Z()); - } - - template <typename T> - Point3D<T> operator^(const Point3D<T>& v) const - { // master->local - return Point3D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos, v.Z()); - } - - template <typename T> - Vector3D<T> operator()(const Vector3D<T>& v) const - { // local->master - return Vector3D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos, v.Z()); - } - - template <typename T> - Vector3D<T> operator^(const Vector3D<T>& v) const - { // master->local - return Vector3D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos, v.Z()); - } - - template <typename T> - Point2D<T> operator()(const Point2D<T>& v) const - { // local->master - return Point2D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos); - } - - template <typename T> - Point2D<T> operator^(const Point2D<T>& v) const - { // master->local - return Point2D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos); - } - - template <typename T> - Vector2D<T> operator()(const Vector2D<T>& v) const - { // local->master - return Vector2D<T>(v.X() * mCos - v.Y() * mSin, v.X() * mSin + v.Y() * mCos); - } - - template <typename T> - Vector2D<T> operator^(const Vector2D<T>& v) const - { // master->local - return Vector2D<T>(v.X() * mCos + v.Y() * mSin, -v.X() * mSin + v.Y() * mCos); - } - - private: - float mCos = 1.f; ///< cos of rotation angle - float mSin = 0.f; ///< sin of rotation angle - - ClassDefNV(Rotation2D, 1); -}; - -class Transform3D : public ROOT::Math::Transform3D -{ - // - // Class to perform geom.transformations (rotation and displacements only) in - // double precision over the cartesian points and vectors (float or double). - // Adds to the base ROOT::Math::Transform3D<double> class a convertor from - // TGeoMatrix. - // To be used instead of TGeoHMatrix for all transformations of hits, - // clusters etc. - // - - public: - Transform3D() = default; - Transform3D(const TGeoMatrix& m); - ~Transform3D() = default; - - // inherit assignment operators of the base class - using ROOT::Math::Transform3D::operator=; - - // to avoid conflict between the base Transform3D(const ForeignMatrix & m) and - // Transform3D(const TGeoMatrix &m) constructors we cannot inherit base c-tors, - // therefore we redefine them here - Transform3D(const ROOT::Math::Rotation3D& r, const Vector& v) : ROOT::Math::Transform3D(r, v) {} - Transform3D(const ROOT::Math::Rotation3D& r, const ROOT::Math::Translation3D& t) : ROOT::Math::Transform3D(r, t) {} - template <class IT> - Transform3D(IT begin, IT end) : ROOT::Math::Transform3D(begin, end) - { - } - - // conversion operator to TGeoHMatrix - operator TGeoHMatrix&() const - { - static TGeoHMatrix tmp; - double rot[9], tra[3]; - GetComponents(rot[0], rot[1], rot[2], tra[0], rot[3], rot[4], rot[5], tra[1], rot[6], rot[7], rot[8], tra[2]); - tmp.SetRotation(rot); - tmp.SetTranslation(tra); - return tmp; - } - - void set(const TGeoMatrix& m); // set parameters from TGeoMatrix - - using ROOT::Math::Transform3D::operator(); - // the local->master transformation for points and vectors can be - // done in operator form (inherited from base Transform3D) as - // Point3D pnt; - // Transform3D trans; - // auto pntTr0 = trans(pnt); // 1st version - // auto pntTr1 = trans*pnt; // 2nd version - // - // For the inverse transform we define our own operator^ - - template <typename T> - Point3D<T> operator^(const Point3D<T>& p) const - { // master->local - return ApplyInverse(p); - } - - template <typename T> - Vector3D<T> operator^(const Vector3D<T>& v) const - { // local->master - return ApplyInverse(v); - } - - // TGeoHMatrix-like aliases - template <typename T> - void LocalToMaster(const Point3D<T>& loc, Point3D<T>& mst) const - { - mst = operator()(loc); - } - - template <typename T> - void MasterToLocal(const Point3D<T>& mst, Point3D<T>& loc) const - { - loc = operator^(mst); - } - - template <typename T> - void LocalToMasterVect(const Point3D<T>& loc, Point3D<T>& mst) const - { - mst = operator()(loc); - } - - template <typename T> - void MasterToLocalVect(const Point3D<T>& mst, Point3D<T>& loc) const - { - loc = operator^(mst); - } - - void print() const; - - ClassDefNV(Transform3D, 1); -}; -} // namespace o2 - -std::ostream& operator<<(std::ostream& os, const o2::Rotation2D& t); - -namespace std -{ - -/// Defining Point3D explicitly as trivially copyable -/// -/// std::is_trivially_copyable<ROOT::Math::Cartesian3D<T>> 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 <typename T> -struct is_trivially_copyable<Point3D<T>> : std::true_type { -}; -} // namespace std -#endif diff --git a/Common/MathUtils/include/MathUtils/CartesianGPU.h b/Common/MathUtils/include/MathUtils/CartesianGPU.h new file mode 100644 index 0000000000000..4ec08e51bc6c2 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/CartesianGPU.h @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CartesianGPU.h +/// @author David Rohr + +#ifndef ALICEO2_CARTESIANGPU_H +#define ALICEO2_CARTESIANGPU_H + +#include "GPUCommonDef.h" + +namespace o2::math_utils +{ + +namespace detail +{ +template <typename T, int I> +struct GPUPoint2D { + GPUdDefault() GPUPoint2D() = default; + GPUd() GPUPoint2D(T a, T b) : xx(a), yy(b) {} + GPUd() float X() const { return xx; } + GPUd() float Y() const { return yy; } + GPUd() float R() const { return o2::gpu::CAMath::Sqrt(xx * xx + yy * yy); } + GPUd() void SetX(float v) { xx = v; } + GPUd() void SetY(float v) { yy = v; } + T xx; + T yy; +}; + +template <typename T, int I> +struct GPUPoint3D : public GPUPoint2D<T, I> { + GPUdDefault() GPUPoint3D() = default; + GPUd() GPUPoint3D(T a, T b, T c) : GPUPoint2D<T, I>(a, b), zz(c) {} + GPUd() float Z() const { return zz; } + GPUd() float R() const { return o2::gpu::CAMath::Sqrt(GPUPoint2D<T, I>::xx * GPUPoint2D<T, I>::xx + GPUPoint2D<T, I>::yy * GPUPoint2D<T, I>::yy + zz * zz); } + GPUd() void SetZ(float v) { zz = v; } + T zz; +}; +} // namespace detail + +} // end namespace o2::math_utils + +#endif diff --git a/Common/MathUtils/include/MathUtils/MathBase.h b/Common/MathUtils/include/MathUtils/MathBase.h deleted file mode 100644 index f4c26c4a0adfc..0000000000000 --- a/Common/MathUtils/include/MathUtils/MathBase.h +++ /dev/null @@ -1,526 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_MATHUTILS_MATHBASE_H_ -#define ALICEO2_MATHUTILS_MATHBASE_H_ - -/// \file MathBase.h -/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de - -#include <cmath> -#include <numeric> -#include <algorithm> -#include <vector> -#include <array> - -#include "Rtypes.h" -#include "TLinearFitter.h" -#include "TVectorD.h" -#include "TMath.h" -#include "TF1.h" -#include "Foption.h" -#include "HFitInterface.h" -#include "TFitResultPtr.h" -#include "TFitResult.h" -#include "Fit/Fitter.h" -#include "Fit/BinData.h" -#include "Math/WrappedMultiTF1.h" - -#include "Framework/Logger.h" - -namespace o2 -{ -namespace math_utils -{ -namespace math_base -{ -/// fit 1D array of histogrammed data with generic root function -/// -/// The code was extracted out of ROOT to be able to do fitting on an array with histogrammed data -/// instead of root histograms. -/// It is a stripped down version, so does not provide the same functionality. -/// To be used with care. -/// -/// \param[in] nbins size of the array and number of histogram bins -/// \param[in] arr array with elements -/// \param[in] xMin minimum range of the array -/// \param[in] xMax maximum range of the array -/// \param[in] func fit function -/// -/// -template <typename T> -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::DataRange range(xMin, xMax); - ROOT::Fit::DataOptions opt; - ROOT::Fit::BinData fitdata(opt, range); - fitdata.Initialize(nBins, 1); - - // create an empty TFitResult - std::shared_ptr<TFitResult> tfr(new TFitResult()); - // create the fitter from an empty fit result - //std::shared_ptr<ROOT::Fit::Fitter> fitter(new ROOT::Fit::Fitter(std::static_pointer_cast<ROOT::Fit::FitResult>(tfr) ) ); - ROOT::Fit::Fitter fitter(tfr); - //ROOT::Fit::FitConfig & fitConfig = fitter->Config(); - - const double binWidth = double(xMax - xMin) / double(nBins); - - for (Int_t ibin = 0; ibin < nBins; ibin++) { - const double x = double(xMin) + double(ibin + 0.5) * binWidth; - const double y = double(arr[ibin]); - const double ey = std::sqrt(y); - fitdata.Add(x, y, ey); - } - - const int special = func.GetNumber(); - const int npar = func.GetNpar(); - bool linear = func.IsLinear(); - if (special == 299 + npar) { - linear = kTRUE; // for polynomial functions - } - // do not use linear fitter in these case - if (fitOption.Bound || fitOption.Like || fitOption.Errors || fitOption.Gradient || fitOption.More || fitOption.User || fitOption.Integral || fitOption.Minuit) { - linear = kFALSE; - } - - if (special != 0 && !fitOption.Bound && !linear) { - if (special == 100) { - ROOT::Fit::InitGaus(fitdata, &func); // gaussian - } else if (special == 400) { - ROOT::Fit::InitGaus(fitdata, &func); // landau (use the same) - } else if (special == 200) { - ROOT::Fit::InitExpo(fitdata, &func); // exponential - } - } - - if ((linear || fitOption.Gradient)) { - fitter.SetFunction(ROOT::Math::WrappedMultiTF1(func)); - } else { - fitter.SetFunction(static_cast<const ROOT::Math::IParamMultiFunction&>(ROOT::Math::WrappedMultiTF1(func))); - } - - // standard least square fit - const bool fitok = fitter.Fit(fitdata, fitOption.ExecPolicy); - if (!fitok) { - LOGP(warning, "bad fit"); - } - - return TFitResultPtr(tfr); -} - -/// fast fit of an array with ranges (histogram) with gaussian function -/// -/// Fitting procedure: -/// 1. Step - make logarithm -/// 2. Linear fit (parabola) - more robust, always converges, fast -/// -/// \param[in] nbins size of the array and number of histogram bins -/// \param[in] arr array with elements -/// \param[in] xMin minimum range of the array -/// \param[in] xMax maximum range of the array -/// \param[out] param return paramters of the fit (0-Constant, 1-Mean, 2-Sigma, 3-Sum) -/// -/// \return chi2 or exit code -/// >0: the chi2 returned by TLinearFitter -/// -3: only three points have been used for the calculation - no fitter was used -/// -2: only two points have been used for the calculation - center of gravity was uesed for calculation -/// -1: only one point has been used for the calculation - center of gravity was uesed for calculation -/// -4: invalid result!! -/// -//template <typename T> -//Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector<T>& param); -template <typename T> -Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, std::vector<T>& param) -{ - static TLinearFitter fitter(3, "pol2"); - static TMatrixD mat(3, 3); - static Double_t kTol = mat.GetTol(); - fitter.StoreData(kFALSE); - fitter.ClearPoints(); - TVectorD par(3); - TVectorD sigma(3); - TMatrixD A(3, 3); - TMatrixD b(3, 1); - T rms = TMath::RMS(nBins, arr); - T max = TMath::MaxElement(nBins, arr); - T binWidth = (xMax - xMin) / T(nBins); - - Float_t meanCOG = 0; - Float_t rms2COG = 0; - Float_t sumCOG = 0; - - Float_t entries = 0; - Int_t nfilled = 0; - - param.resize(4); - param[0] = 0.; - param[1] = 0.; - param[2] = 0.; - param[3] = 0.; - - for (Int_t i = 0; i < nBins; i++) { - entries += arr[i]; - if (arr[i] > 0) - nfilled++; - } - - // TODO: Check why this is needed - if (max < 4) - return -4; - if (entries < 12) - return -4; - - if (rms < kTol) - return -4; - - param[3] = entries; - - Int_t npoints = 0; - for (Int_t ibin = 0; ibin < nBins; ibin++) { - Float_t entriesI = arr[ibin]; - if (entriesI > 1) { - Double_t xcenter = xMin + (ibin + 0.5) * binWidth; - Double_t error = 1. / TMath::Sqrt(entriesI); - Double_t val = TMath::Log(Float_t(entriesI)); - fitter.AddPoint(&xcenter, val, error); - if (npoints < 3) { - A(npoints, 0) = 1; - A(npoints, 1) = xcenter; - A(npoints, 2) = xcenter * xcenter; - b(npoints, 0) = val; - meanCOG += xcenter * entriesI; - rms2COG += xcenter * entriesI * xcenter; - sumCOG += entriesI; - } - npoints++; - } - } - - Double_t chi2 = 0; - if (npoints >= 3) { - if (npoints == 3) { - //analytic calculation of the parameters for three points - A.Invert(); - TMatrixD res(1, 3); - res.Mult(A, b); - par[0] = res(0, 0); - par[1] = res(0, 1); - par[2] = res(0, 2); - chi2 = -3.; - } else { - // use fitter for more than three points - fitter.Eval(); - fitter.GetParameters(par); - fitter.GetCovarianceMatrix(mat); - chi2 = fitter.GetChisquare() / Double_t(npoints); - } - if (TMath::Abs(par[1]) < kTol) - return -4; - if (TMath::Abs(par[2]) < kTol) - return -4; - - param[1] = T(par[1] / (-2. * par[2])); - param[2] = T(1. / TMath::Sqrt(TMath::Abs(-2. * par[2]))); - Double_t lnparam0 = par[0] + par[1] * param[1] + par[2] * param[1] * param[1]; - if (lnparam0 > 307) - return -4; - param[0] = TMath::Exp(lnparam0); - - return chi2; - } - - if (npoints == 2) { - //use center of gravity for 2 points - meanCOG /= sumCOG; - rms2COG /= sumCOG; - param[0] = max; - param[1] = meanCOG; - param[2] = TMath::Sqrt(TMath::Abs(meanCOG * meanCOG - rms2COG)); - chi2 = -2.; - } - if (npoints == 1) { - meanCOG /= sumCOG; - param[0] = max; - param[1] = meanCOG; - param[2] = binWidth / TMath::Sqrt(12); - chi2 = -1.; - } - return chi2; -} - -/// struct for returning statistical parameters -/// -/// \todo make type templated? -/// \todo use Vc -struct StatisticsData { - double mCOG{0}; ///< calculated centre of gravity - double mStdDev{0}; ///< standard deviation - double mSum{0}; ///< sum of values -}; - -/// calculate statistical parameters on a binned array -/// -/// The function assumes a binned array of -/// \param nBins size of the array -/// \param xMin lower histogram bound -/// \param xMax upper histogram bound -/// \todo make return type templated? -template <typename T> -StatisticsData getStatisticsData(const T* arr, const size_t nBins, const double xMin, const double xMax) -{ - double mean = 0; - double rms2 = 0; - double sum = 0; - size_t npoints = 0; - - double binWidth = (xMax - xMin) / (double)nBins; - - StatisticsData data; - // in case something went wrong the COG is the histogram lower limit - data.mCOG = xMin; - - for (size_t ibin = 0; ibin < nBins; ++ibin) { - double entriesI = (double)arr[ibin]; - double xcenter = xMin + (ibin + 0.5) * binWidth; // +0.5 to shift to bin centre - if (entriesI > 0) { - mean += xcenter * entriesI; - rms2 += xcenter * entriesI * xcenter; - sum += entriesI; - ++npoints; - } - } - if (sum == 0) - return data; - mean /= sum; - - data.mCOG = mean; - // exception in case of only one bin is filled - // set the standard deviation to bin width over sqrt(12) - rms2 /= sum; - if (npoints == 1) { - data.mStdDev = binWidth / std::sqrt(12.); - } else { - data.mStdDev = std::sqrt(std::abs(rms2 - mean * mean)); - } - - data.mSum = sum; - - return data; -} - -/// median of values in a std::vector -/// -/// we need to make a copy of the vector since we need to sort it -/// based on this discussion: https://stackoverflow.com/questions/1719070/what-is-the-right-approach-when-using-stl-container-for-median-calculation/1719155#1719155 -/// \todo Is there a better way to do this? -template <typename T, typename R = double> -R median(std::vector<T> v) -{ - if (v.empty()) { - return R{}; - } - 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 - auto max_it = max_element(v.begin(), v.begin() + n); - med = R{(*max_it + med) / 2.0}; - } - return med; -} - -/// Fills the index vector with sorted indices of the input vector. -/// The input vector is not modified (similar to TMath::Sort()). -/// \param values Vector to be indexed -/// \param index Vector to hold the sorted indices (must have the same size as values) -template <typename T> -void SortData(std::vector<T> const& values, std::vector<size_t>& index) -{ - if (values.size() != index.size()) { - LOG(error) << "Vector with values must have same size as vector for indices"; - return; - } - std::iota(index.begin(), index.end(), static_cast<size_t>(0)); - std::sort(index.begin(), index.end(), [&](size_t a, size_t b) { return values[a] < values[b]; }); -} - -/// LTM : Trimmed mean of unbinned array -/// -/// Robust statistic to estimate properties of the distribution -/// To handle binning error special treatment -/// for definition of unbinned data see: -/// http://en.wikipedia.org/w/index.php?title=Trimmed_estimator&oldid=582847999 -/// \param data Input vector (unsorted) -/// \param index Vector with indices of sorted input data -/// \param params Array with characteristics of distribution -/// \param fracKeep Fraction of data to be kept -/// \return Flag if successfull -/// Characteristics: -/// -# area -/// -# mean -/// -# rms -/// -# error estimate of mean -/// -# error estimate of RMS -/// -# first accepted element (of sorted array) -/// -# last accepted element (of sorted array) -template <typename T> -bool LTMUnbinned(const std::vector<T>& data, std::vector<size_t>& index, std::array<float, 7>& params, float fracKeep) -{ - int nPoints = data.size(); - std::vector<float> w(2 * nPoints); - int nKeep = nPoints * fracKeep; - if (nKeep > nPoints) { - nKeep = nPoints; - } - if (nKeep < 2) { - return false; - } - // sort in increasing order - SortData(data, index); - // build cumulants - double sum1 = 0.0; - double sum2 = 0.0; - for (int i = 0; i < nPoints; i++) { - double x = data[index[i]]; - sum1 += x; - sum2 += x * x; - w[i] = sum1; - w[i + nPoints] = sum2; - } - double maxRMS = sum2 + 1e6; - params[0] = nKeep; - int limI = nPoints - nKeep + 1; // lowest possible bin to accept - for (int i = 0; i < limI; i++) { - const int limJ = i + nKeep - 1; // highest accepted bin - sum1 = static_cast<double>(w[limJ]) - static_cast<double>(i ? w[i - 1] : 0.); - sum2 = static_cast<double>(w[nPoints + limJ]) - static_cast<double>(i ? w[nPoints + i - 1] : 0.); - const double mean = sum1 / nKeep; - const double rms2 = sum2 / nKeep - mean * mean; - if (rms2 > maxRMS) { - continue; - } - maxRMS = rms2; - params[1] = mean; - params[2] = rms2; - params[5] = i; - params[6] = limJ; - } - // - if (params[2] < 0) { - LOG(error) << "Rounding error: RMS = " << params[2] << " < 0"; - return false; - } - params[2] = std::sqrt(params[2]); - params[3] = params[2] / std::sqrt(params[0]); // error on mean - params[4] = params[3] / std::sqrt(2.0); // error on RMS - return true; -} - -/// Rearranges the input vector in the order given by the index vector -/// \param data Input vector -/// \param index Index vector -template <typename T> -void Reorder(std::vector<T>& data, const std::vector<size_t>& index) -{ - // rearange data in order given by index - if (data.size() != index.size()) { - LOG(error) << "Reordering not possible if number of elements in index container different from the data container"; - return; - } - std::vector<T> tmp(data); - for (size_t i = 0; i < data.size(); ++i) { - data[i] = tmp[index[i]]; - } -} - -/// Compare this function to LTMUnbinned. -/// A target sigma of the distribution can be specified and it will be trimmed to match that target. -/// \param data Input vector (unsorted) -/// \param index Vector with indices of sorted input data -/// \param params Array with characteristics of distribution -/// \param fracKeepMin Minimum fraction to keep of the input data -/// \param sigTgt Target distribution sigma -/// \param sorted Flag if the data is already sorted -/// \return Flag if successfull -template <typename T> -bool LTMUnbinnedSig(const std::vector<T>& data, std::vector<size_t>& index, std::array<float, 7>& params, float fracKeepMin, float sigTgt, bool sorted = false) -{ - int nPoints = data.size(); - std::vector<double> wx(nPoints); - std::vector<double> wx2(nPoints); - - if (!sorted) { - // sort in increasing order - SortData(data, index); - } else { - // array is already sorted - std::iota(index.begin(), index.end(), 0); - } - // build cumulants - double sum1 = 0.0; - double sum2 = 0.0; - for (int i = 0; i < nPoints; i++) { - double x = data[index[i]]; - sum1 += x; - sum2 += x * x; - wx[i] = sum1; - wx2[i] = sum2; - } - int keepMax = nPoints; - int keepMin = fracKeepMin * nPoints; - if (keepMin > keepMax) { - keepMin = keepMax; - } - float sigTgt2 = sigTgt * sigTgt; - // - while (true) { - double maxRMS = wx2.back() + 1e6; - int keepN = (keepMax + keepMin) / 2; - if (keepN < 2) { - return false; - } - params[0] = keepN; - int limI = nPoints - keepN + 1; - for (int i = 0; i < limI; ++i) { - const int limJ = i + keepN - 1; - sum1 = wx[limJ] - (i ? wx[i - 1] : 0.); - sum2 = wx2[limJ] - (i ? wx2[i - 1] : 0.); - const double mean = sum1 / keepN; - const double rms2 = sum2 / keepN - mean * mean; - if (rms2 > maxRMS) { - continue; - } - maxRMS = rms2; - params[1] = mean; - params[2] = rms2; - params[5] = i; - params[6] = limJ; - } - if (maxRMS < sigTgt2) { - keepMin = keepN; - } else { - keepMax = keepN; - } - if (keepMin >= keepMax - 1) { - break; - } - } - params[2] = std::sqrt(params[2]); - params[3] = params[2] / std::sqrt(params[0]); // error on mean - params[4] = params[3] / std::sqrt(2.0); // error on RMS - return true; -} -} // namespace math_base -} // namespace math_utils -} // namespace o2 -#endif diff --git a/Common/MathUtils/include/MathUtils/Primitive2D.h b/Common/MathUtils/include/MathUtils/Primitive2D.h index b2b1b8ca25b4d..1d69fda1f8710 100644 --- a/Common/MathUtils/include/MathUtils/Primitive2D.h +++ b/Common/MathUtils/include/MathUtils/Primitive2D.h @@ -9,61 +9,37 @@ // or submit itself to any jurisdiction. /// \file Primitive2D.h -/// \brief Declarations of 2D primitives: straight line (XY interval) and circle -/// \author ruben.shahoyan@cern.ch +/// \brief Declarations of 2D primitives +/// \author ruben.shahoyan@cern.ch michael.lettrich@cern.ch #ifndef ALICEO2_COMMON_MATH_PRIMITIVE2D_H #define ALICEO2_COMMON_MATH_PRIMITIVE2D_H #include "GPUCommonRtypes.h" +#include "MathUtils/detail/CircleXY.h" +#include "MathUtils/detail/IntervalXY.h" +#include "MathUtils/detail/Bracket.h" namespace o2 { -namespace utils +namespace math_utils { - -struct CircleXY { - float rC, xC, yC; // circle radius, x-center, y-center - CircleXY(float r = 0., float x = 0., float y = 0.) : rC(r), xC(x), yC(y) {} - float getCenterD2() const { return xC * xC + yC * yC; } - ClassDefNV(CircleXY, 1); -}; - -struct IntervalXY { - ///< 2D interval in lab frame defined by its one edge and signed projection lengths on X,Y axes - float xP, yP; ///< one of edges - float dxP, dyP; ///< other edge minus provided - IntervalXY(float x = 0, float y = 0, float dx = 0, float dy = 0) : xP(x), yP(y), dxP(dx), dyP(dy) {} - float getX0() const { return xP; } - float getY0() const { return yP; } - float getX1() const { return xP + dxP; } - float getY1() const { return yP + dyP; } - float getDX() const { return dxP; } - float getDY() const { return dyP; } - void eval(float t, float& x, float& y) const - { - x = xP + t * dxP; - y = yP + t * dyP; - } - void getLineCoefs(float& a, float& b, float& c) const; - - void setEdges(float x0, float y0, float x1, float y1) - { - xP = x0; - yP = y0; - dxP = x1 - x0; - dyP = y1 - y0; - } - bool seenByCircle(const CircleXY& circle, float eps) const; - bool circleCrossParam(const CircleXY& cicle, float& t) const; - - bool seenByLine(const IntervalXY& other, float eps) const; - bool lineCrossParam(const IntervalXY& other, float& t) const; - - ClassDefNV(IntervalXY, 1); -}; - -} // namespace utils +template <typename T> +using CircleXY = detail::CircleXY<T>; +using CircleXYf_t = detail::CircleXY<float>; +using CircleXYd_t = detail::CircleXY<double>; + +template <typename T> +using IntervalXY = detail::IntervalXY<T>; +using IntervalXYf_t = detail::IntervalXY<float>; +using IntervalXYd_t = detail::IntervalXY<double>; + +template <typename T> +using Bracket = detail::Bracket<T>; +using Bracketf_t = detail::Bracket<float>; +using Bracketd_t = detail::Bracket<double>; + +} // namespace math_utils } // namespace o2 #endif diff --git a/Common/MathUtils/include/MathUtils/Utils.h b/Common/MathUtils/include/MathUtils/Utils.h index adf4e4bdb5e81..d23933cc5ba0f 100644 --- a/Common/MathUtils/include/MathUtils/Utils.h +++ b/Common/MathUtils/include/MathUtils/Utils.h @@ -10,208 +10,221 @@ /// \file Utils.h /// \brief General auxilliary methods -/// \author ruben.shahoyan@cern.ch +/// \author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch #ifndef ALICEO2_COMMON_MATH_UTILS_ #define ALICEO2_COMMON_MATH_UTILS_ -#ifndef __OPENCL__ -#include <array> -#include <cmath> -#endif -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "CommonConstants/MathConstants.h" +#include "MathUtils/detail/bitOps.h" +#include "MathUtils/detail/StatAccumulator.h" +#include "MathUtils/detail/trigonometric.h" +#include "MathUtils/detail/TypeTruncation.h" namespace o2 { -// namespace common -//{ -namespace utils +namespace math_utils { -GPUdi() void BringTo02Pi(float& phi) + +GPUdi() float to02Pi(float phi) { - // ensure angle in [0:2pi] for the input in [-pi:pi] or [0:pi] - if (phi < 0.f) { - phi += o2::constants::math::TwoPI; - } + return detail::to02Pi<float>(phi); } -inline void BringTo02PiGen(float& phi) +GPUdi() double to02Pid(double phi) { - // ensure angle in [0:2pi] for the any input angle - while (phi < 0.f) { - phi += o2::constants::math::TwoPI; - } - while (phi > o2::constants::math::TwoPI) { - phi -= o2::constants::math::TwoPI; - } + return detail::to02Pi<double>(phi); } -inline void BringToPMPi(float& phi) +GPUdi() void bringTo02Pi(float& phi) { - // ensure angle in [-pi:pi] for the input in [-pi:pi] or [0:pi] - if (phi > o2::constants::math::PI) { - phi -= o2::constants::math::TwoPI; - } else if (phi < -o2::constants::math::PI) { - phi += o2::constants::math::TwoPI; - } + detail::bringTo02Pi<float>(phi); } -inline void BringToPMPiGen(float& phi) +GPUdi() void bringTo02Pid(double& phi) { - // ensure angle in [-pi:pi] for any input angle - while (phi < -o2::constants::math::PI) { - phi += o2::constants::math::TwoPI; - } - while (phi > o2::constants::math::PI) { - phi -= o2::constants::math::TwoPI; - } + detail::bringTo02Pi<double>(phi); } -inline void sincosf(float ang, float& s, float& c) +inline float toPMPiGen(float phi) { - o2::gpu::GPUCommonMath::SinCos(ang, s, c); + return detail::toPMPiGen<float>(phi); } -inline void sincos(float ang, float& s, float& c) +inline double toPMPiGend(double phi) { - o2::gpu::GPUCommonMath::SinCos(ang, s, c); + return detail::toPMPiGen<double>(phi); } -inline void sincos(double ang, double& s, double& c) +inline void bringToPMPiGen(float& phi) { - o2::gpu::GPUCommonMath::SinCos(ang, s, c); + detail::bringToPMPiGen<float>(phi); } -inline void rotateZ(float xL, float yL, float& xG, float& yG, float snAlp, float csAlp) +inline void bringToPMPiGend(double& phi) { - // 2D rotation of the point by angle alpha (local to global) - xG = xL * csAlp - yL * snAlp; - yG = xL * snAlp + yL * csAlp; + detail::bringToPMPiGen<double>(phi); } -inline void rotateZInv(float xG, float yG, float& xL, float& yL, float snAlp, float csAlp) +inline float to02PiGen(float phi) +{ + return detail::to02PiGen<float>(phi); +} + +inline double to02PiGend(double phi) +{ + return detail::to02PiGen<double>(phi); +} + +inline void bringTo02PiGen(float& phi) +{ + detail::bringTo02PiGen<float>(phi); +} + +inline void bringTo02PiGend(double& phi) +{ + detail::bringTo02PiGen<double>(phi); +} + +inline float toPMPi(float phi) +{ + return detail::toPMPi<float>(phi); +} + +inline double toPMPid(double phi) { - // inverse 2D rotation of the point by angle alpha (global to local) - rotateZ(xG, yG, xL, yL, -snAlp, csAlp); + return detail::toPMPi<double>(phi); } -inline void rotateZ(double xL, double yL, double& xG, double& yG, double snAlp, double csAlp) +inline void bringToPMPi(float& phi) { - // 2D rotation of the point by angle alpha (local to global) - xG = xL * csAlp - yL * snAlp; - yG = xL * snAlp + yL * csAlp; + return detail::bringToPMPi<float>(phi); } -inline void rotateZInv(double xG, double yG, double& xL, double& yL, double snAlp, double csAlp) +inline void bringToPMPid(double& phi) { - // inverse 2D rotation of the point by angle alpha (global to local) - rotateZ(xG, yG, xL, yL, -snAlp, csAlp); + return detail::bringToPMPi<double>(phi); } +GPUdi() void sincos(float ang, float& s, float& c) +{ + detail::sincos<float>(ang, s, c); +} #ifndef __OPENCL__ -inline void RotateZ(std::array<float, 3>& xy, float alpha) +GPUdi() void sincosd(double ang, double& s, double& c) { - // transforms vector in tracking frame alpha to global frame - float sn, cs, x = xy[0]; - sincosf(alpha, sn, cs); - xy[0] = x * cs - xy[1] * sn; - xy[1] = x * sn + xy[1] * cs; + detail::sincos<double>(ang, s, c); } #endif -inline int Angle2Sector(float phi) +GPUdi() void rotateZ(float xL, float yL, float& xG, float& yG, float snAlp, float csAlp) +{ + return detail::rotateZ<float>(xL, yL, xG, yG, snAlp, csAlp); +} + +GPUdi() void rotateZd(double xL, double yL, double& xG, double& yG, double snAlp, double csAlp) +{ + return detail::rotateZ<double>(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) +{ + detail::rotateZInv<float>(xG, yG, xL, yL, snAlp, csAlp); +} + +inline void rotateZInvd(double xG, double yG, double& xL, double& yL, double snAlp, double csAlp) +{ + detail::rotateZInv<double>(xG, yG, xL, yL, snAlp, csAlp); +} + +inline std::tuple<float, float> rotateZInv(float xG, float yG, float snAlp, float csAlp) +{ + return detail::rotateZInv<float>(xG, yG, snAlp, csAlp); +} + +inline std::tuple<double, double> rotateZInvd(double xG, double yG, double snAlp, double csAlp) +{ + return detail::rotateZInv<double>(xG, yG, snAlp, csAlp); +} + +GPUdi() std::tuple<float, float> sincos(float ang) +{ + return detail::sincos<float>(ang); +} + +GPUdi() std::tuple<double, double> sincosd(double ang) +{ + return detail::sincos<double>(ang); +} + +inline std::tuple<float, float> rotateZ(float xL, float yL, float snAlp, float csAlp) { - // convert angle to sector ID, phi can be either in 0:2pi or -pi:pi convention - int sect = phi * o2::constants::math::Rad2Deg / o2::constants::math::SectorSpanDeg; - if (phi < 0.f) { - sect += o2::constants::math::NSectors - 1; - } - return sect; + return detail::rotateZ<float>(xL, yL, snAlp, csAlp); } -inline float Sector2Angle(int sect) +inline std::tuple<double, double> rotateZd(double xL, double yL, double snAlp, double csAlp) { - // convert sector to its angle center, in -pi:pi convention - float ang = o2::constants::math::SectorSpanRad * (0.5f + sect); - BringToPMPi(ang); - return ang; + return detail::rotateZ<double>(xL, yL, snAlp, csAlp); } -inline float Angle2Alpha(float phi) +inline void rotateZ(std::array<float, 3>& xy, float alpha) { - // convert angle to its sector alpha - return Sector2Angle(Angle2Sector(phi)); + detail::rotateZ<float>(xy, alpha); } -//-------------------------------------->>> -// fast bit count -inline int numberOfBitsSet(uint32_t x) +inline void rotateZd(std::array<double, 3>& xy, double alpha) { - // count number of non-0 bits in 32bit word - x = x - ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + detail::rotateZ<double>(xy, alpha); } +#endif -// recursive creation of bitmask -template <typename T> -constexpr int bit2Mask(T v) +inline int angle2Sector(float phi) { - return 0x1 << v; + return detail::angle2Sector<float>(phi); } -template <typename T, typename... Args> -constexpr int bit2Mask(T first, Args... args) +inline int angle2Sectord(double phi) { - return (0x1 << first) | bit2Mask(args...); + return detail::angle2Sector<double>(phi); } -//--------------------------------------<<< -GPUhdi() float FastATan2(float y, float x) +inline float sector2Angle(int sect) { - // Fast atan2(y,x) for any angle [-Pi,Pi] - // Average inaccuracy: 0.00048 - // Max inaccuracy: 0.00084 - // Speed: 6.2 times faster than atan2f() + return detail::sector2Angle<float>(sect); +} - constexpr float kPi = 3.1415926535897f; +inline double sector2Angled(int sect) +{ + return detail::sector2Angle<double>(sect); +} - auto atan = [](float a) -> float { - // returns the arctan for the angular range [-Pi/4, Pi/4] - // the polynomial coefficients are taken from: - // https://stackoverflow.com/questions/42537957/fast-accurate-atan-arctan-approximation-algorithm - constexpr float kA = 0.0776509570923569f; - constexpr float kB = -0.287434475393028f; - constexpr float kC = (kPi / 4 - kA - kB); - float a2 = a * a; - return ((kA * a2 + kB) * a2 + kC) * a; - }; +inline float angle2Alpha(float phi) +{ + return detail::angle2Alpha<float>(phi); +} - auto atan2P = [atan](float yy, float xx) -> float { - // fast atan2(yy,xx) for the angular range [0,+Pi] - constexpr float kPi025 = 1 * kPi / 4; - constexpr float kPi075 = 3 * kPi / 4; - float x1 = xx + yy; // point p1 (x1,y1) = (xx,yy) - Pi/4 - float y1 = yy - xx; - float phi0, tan; - if (xx < 0) { // p1 is in the range [Pi/4, 3*Pi/4] - phi0 = kPi075; - tan = -x1 / y1; - } else { // p1 is in the range [-Pi/4, Pi/4] - phi0 = kPi025; - tan = y1 / x1; - } - return phi0 + atan(tan); - }; +inline double angle2Alphad(double phi) +{ + return detail::angle2Alpha<double>(phi); +} - // fast atan2(y,x) for any angle [-Pi,Pi] - return o2::gpu::GPUCommonMath::Copysign(atan2P(o2::gpu::CAMath::Abs(y), x), y); +GPUhdi() float fastATan2(float y, float x) +{ + return detail::fastATan2<float>(y, x); } -} // namespace utils -//} +GPUhdi() double fastATan2d(double y, double x) +{ + return detail::fastATan2<double>(y, x); +} + +using detail::StatAccumulator; + +using detail::bit2Mask; +using detail::numberOfBitsSet; +using detail::truncateFloatFraction; + +} // namespace math_utils } // namespace o2 #endif diff --git a/Common/MathUtils/include/MathUtils/detail/Bracket.h b/Common/MathUtils/include/MathUtils/detail/Bracket.h new file mode 100644 index 0000000000000..28c0f505ae13d --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/Bracket.h @@ -0,0 +1,208 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Bracket.h +/// \brief Class to represent an interval and some operations over it +/// \author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch + +#ifndef ALICEO2_BRACKET_H +#define ALICEO2_BRACKET_H + +#include <GPUCommonRtypes.h> + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ + +template <typename T = float> +class Bracket +{ + public: + enum Relation : int { Below = -1, + Inside = 0, + Above = 1 }; + + Bracket(T minv, T maxv); + Bracket() = default; + Bracket(const Bracket<T>& src) = default; + Bracket(Bracket<T>&& src) = default; + Bracket& operator=(const Bracket<T>& src) = default; + Bracket& operator=(Bracket<T>&& src) = default; + ~Bracket() = default; + + bool operator<(T other) const; + bool operator>(T other) const; + + bool operator<(const Bracket<T>& other) const; + bool operator>(const Bracket<T>& other) const; + bool operator==(const Bracket<T>& other) const; + bool operator!=(const Bracket<T>& other) const; + + void setMax(T v) noexcept; + void setMin(T v) noexcept; + void set(T minv, T maxv) noexcept; + + T& getMax(); + T& getMin(); + + T getMax() const; + T getMin() const; + T mean() const; + T delta() const; + bool isValid() const; + bool isInvalid() const; + void update(T v); + Relation isOutside(const Bracket<T>& t) const; + Relation isOutside(T t, T tErr) const; + + private: + T mMin{}; + T mMax{}; + + ClassDefNV(Bracket, 2); +}; + +template <typename T> +Bracket<T>::Bracket(T minv, T maxv) : mMin(minv), mMax(maxv) +{ +} + +template <typename T> +inline bool Bracket<T>::operator<(T other) const +{ + return mMax < other; +} + +template <typename T> +inline bool Bracket<T>::operator>(T other) const +{ + return mMin > other; +} + +template <typename T> +inline bool Bracket<T>::operator<(const Bracket<T>& other) const +{ + return *this < other.mMin; +} + +template <typename T> +inline bool Bracket<T>::operator>(const Bracket<T>& other) const +{ + return *this > other.mMax; +} + +template <typename T> +inline bool Bracket<T>::operator==(const Bracket<T>& rhs) const +{ + return mMin == rhs.mMin && mMax == rhs.mMax; +} + +template <typename T> +inline bool Bracket<T>::operator!=(const Bracket<T>& rhs) const +{ + return !(*this == rhs); +} + +template <typename T> +inline void Bracket<T>::setMax(T v) noexcept +{ + mMax = v; +} + +template <typename T> +inline void Bracket<T>::setMin(T v) noexcept +{ + mMin = v; +} + +template <typename T> +inline void Bracket<T>::set(T minv, T maxv) noexcept +{ + this->setMin(minv); + this->setMax(maxv); +} + +template <typename T> +inline T& Bracket<T>::getMax() +{ + return mMax; +} + +template <typename T> +inline T& Bracket<T>::getMin() +{ + return mMin; +} + +template <typename T> +inline T Bracket<T>::getMax() const +{ + return mMax; +} + +template <typename T> +inline T Bracket<T>::getMin() const +{ + return mMin; +} + +template <typename T> +inline T Bracket<T>::mean() const +{ + return (mMin + mMax) / 2; +} +template <typename T> +inline T Bracket<T>::delta() const +{ + return mMax - mMin; +} +template <typename T> +inline bool Bracket<T>::isValid() const +{ + return mMax >= mMin; +} +template <typename T> +inline bool Bracket<T>::isInvalid() const +{ + return mMin > mMax; +} +template <typename T> +inline void Bracket<T>::update(T v) +{ + // update limits + if (v > mMax) { + mMax = v; + } + if (v < mMin) { + mMin = v; + } +} + +template <typename T> +inline typename Bracket<T>::Relation Bracket<T>::isOutside(const Bracket<T>& t) const +{ + ///< check if provided bracket is outside of this bracket + return t.mMax < mMin ? Below : (t.mMin > mMax ? Above : Inside); +} +template <typename T> +inline typename Bracket<T>::Relation Bracket<T>::isOutside(T t, T tErr) const +{ + ///< check if the provided value t with error tErr is outside of the bracket + return t + tErr < mMin ? Below : (t - tErr > mMax ? Above : Inside); +} + +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif diff --git a/Common/MathUtils/include/MathUtils/detail/CircleXY.h b/Common/MathUtils/include/MathUtils/detail/CircleXY.h new file mode 100644 index 0000000000000..cf740b455441c --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/CircleXY.h @@ -0,0 +1,55 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CircleXY.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 10, 2020 +/// @brief + +#ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_CIRCLEXY_H_ +#define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_CIRCLEXY_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ + +template <typename T> +struct CircleXY { + using value_t = T; + + T rC; // circle radius + T xC; // x-center + T yC; // y-center + GPUd() CircleXY(T r = T(), T x = T(), T y = T()); + GPUd() T getCenterD2() const; + ClassDefNV(CircleXY, 2); +}; + +template <typename T> +GPUdi() CircleXY<T>::CircleXY(T r, T x, T y) : rC(r), xC(x), yC(y) +{ +} + +template <typename T> +GPUdi() T CircleXY<T>::getCenterD2() const +{ + return xC * xC + yC * yC; +} +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_CIRCLEXY_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/IntervalXY.h b/Common/MathUtils/include/MathUtils/detail/IntervalXY.h new file mode 100644 index 0000000000000..2a65634ff0778 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/IntervalXY.h @@ -0,0 +1,313 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 IntervalXY.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 10, 2020 +/// @brief + +#ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_INTERVALXY_H_ +#define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_INTERVALXY_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include <cmath> +#include <tuple> +#endif + +#include "MathUtils/detail/CircleXY.h" + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ + +template <typename T> +class IntervalXY +{ + public: + using value_t = T; + + ///< 2D interval in lab frame defined by its one edge and signed projection lengths on X,Y axes + GPUd() IntervalXY(T x = T(), T y = T(), T dx = T(), T dy = T()); + GPUd() T getX0() const; + GPUd() T getY0() const; + GPUd() T& getX0(); + GPUd() T& getY0(); + GPUd() T getX1() const; + GPUd() T getY1() const; + GPUd() T getDX() const; + GPUd() T getDY() const; + GPUd() T& getDX(); + GPUd() T& getDY(); + + GPUd() void setX0(T x0); + GPUd() void setY0(T y0); + GPUd() void setX1(T x1); + GPUd() void setY1(T y1); + GPUd() void setDX(T dx); + GPUd() void setDY(T dy); + GPUd() void setEdges(T x0, T y0, T x1, T y1); + + GPUd() void eval(T t, T& x, T& y) const; +#ifndef GPUCA_GPUCODE_DEVICE + std::tuple<T, T> eval(T t) const; +#endif + + GPUd() void getLineCoefs(T& a, T& b, T& c) const; + + /** check if XY interval is seen by the circle. + * The tolerance parameter eps is interpreted as a fraction of the interval + * lenght to be added to the edges along the interval (i.e. while the points + * within the interval are spanning + * x = xc + dx*t + * y = yc + dy*t + * with 0<t<1., we acctually check the interval for -eps<t<1+eps + */ + GPUd() bool seenByCircle(const CircleXY<T>& circle, T eps) const; + + GPUd() bool circleCrossParam(const CircleXY<T>& circle, T& t) const; + + /** + * check if XY interval is seen by the line defined by other interval + * The tolerance parameter eps is interpreted as a fraction of the interval + * lenght to be added to the edges along the interval (i.e. while the points + * within the interval are spanning + * x = xc + dx*t + * y = yc + dy*t + * with 0<t<1., we acctually check the interval for -eps<t<1+eps + */ + GPUd() bool seenByLine(const IntervalXY<T>& other, T eps) const; + + /** + * get crossing parameter of 2 intervals + */ + GPUd() bool lineCrossParam(const IntervalXY<T>& other, T& t) const; + + private: + T mX, mY; ///< one of edges + T mDx, mDY; ///< other edge minus provided + + ClassDefNV(IntervalXY, 2); +}; + +template <typename T> +GPUdi() IntervalXY<T>::IntervalXY(T x, T y, T dx, T dy) : mX(x), mY(y), mDx(dx), mDY(dy) +{ +} + +template <typename T> +GPUdi() T IntervalXY<T>::getX0() const +{ + return mX; +} + +template <typename T> +GPUdi() T IntervalXY<T>::getY0() const +{ + return mY; +} + +template <typename T> +GPUdi() T& IntervalXY<T>::getX0() +{ + return mX; +} + +template <typename T> +GPUdi() T& IntervalXY<T>::getY0() +{ + return mY; +} + +template <typename T> +GPUdi() T IntervalXY<T>::getX1() const +{ + return mX + mDx; +} +template <typename T> +GPUdi() T IntervalXY<T>::getY1() const +{ + return mY + mDY; +} +template <typename T> +GPUdi() T IntervalXY<T>::getDX() const +{ + return mDx; +} +template <typename T> +GPUdi() T IntervalXY<T>::getDY() const +{ + return mDY; +} + +template <typename T> +GPUdi() T& IntervalXY<T>::getDX() +{ + return mDx; +} +template <typename T> +GPUdi() T& IntervalXY<T>::getDY() +{ + return mDY; +} + +template <typename T> +GPUdi() void IntervalXY<T>::setX0(T x0) +{ + mX = x0; +} + +template <typename T> +GPUdi() void IntervalXY<T>::setY0(T y0) +{ + mY = y0; +} +template <typename T> +GPUdi() void IntervalXY<T>::setX1(T x1) +{ + mDx = x1 - mX; +} +template <typename T> +GPUdi() void IntervalXY<T>::setY1(T y1) +{ + mDY = y1 - mY; +} +template <typename T> +GPUdi() void IntervalXY<T>::setDX(T dx) +{ + mDx = dx; +} +template <typename T> +GPUdi() void IntervalXY<T>::setDY(T dy) +{ + mDY = dy; +} + +template <typename T> +GPUdi() void IntervalXY<T>::setEdges(T x0, T y0, T x1, T y1) +{ + mX = x0; + mY = y0; + mDx = x1 - x0; + mDY = y1 - y0; +} + +#ifndef GPUCA_GPUCODE_DEVICE +template <typename T> +std::tuple<T, T> IntervalXY<T>::eval(T t) const +{ + return {mX + t * mDx, mY + t * mDY}; +} +#endif + +template <typename T> +GPUdi() void IntervalXY<T>::eval(T t, T& x, T& y) const +{ + x = mX + t * mDx; + y = mY + t * mDY; +} + +template <typename T> +GPUdi() void IntervalXY<T>::getLineCoefs(T& a, T& b, T& c) const +{ + // convert to line parameters in canonical form: a*x+b*y+c = 0 + c = mX * mDY - mY * mDx; + if (c) { + a = -mDY; + b = mDx; + } else if (fabs(mDx) > fabs(mDY)) { + a = 0.; + b = -1.; + c = mY; + } else { + a = -1.; + b = 0.; + c = mX; + } +} + +template <typename T> +GPUdi() bool IntervalXY<T>::seenByCircle(const CircleXY<T>& circle, T eps) const +{ + T dx0 = mX - circle.xC; + T dy0 = mY - circle.yC; + T dx1 = dx0 + mDx; + T dy1 = dy0 + mDY; + if (eps > 0.) { + const T dxeps = eps * mDx, dyeps = eps * mDY; + dx0 -= dxeps; + dx1 += dxeps; + dy0 -= dyeps; + dy1 += dyeps; + } + // distance^2 from circle center to edges + const T d02 = dx0 * dx0 + dy0 * dy0; + const T d12 = dx1 * dx1 + dy1 * dy1; + const T rC2 = circle.rC * circle.rC; + + return (d02 - rC2) * (d12 - rC2) < 0; +} +template <typename T> +GPUdi() bool IntervalXY<T>::circleCrossParam(const CircleXY<T>& circle, T& t) const +{ + const T dx = mX - circle.xC; + const T dy = mY - circle.yC; + const T del2I = 1.f / (mDx * mDx + mDY * mDY); + const T b = (dx * mDx + dy * mDY) * del2I; + const T c = del2I * (dx * dx + dy * dy - circle.rC * circle.rC); + T det = b * b - c; + if (det < 0.f) { + return false; + } + det = gpu::CAMath::Sqrt(det); + const T t0 = -b - det; + const T t1 = -b + det; + // select the one closer to [0:1] interval + t = (gpu::CAMath::Abs(t0 - 0.5f) < gpu::CAMath::Abs(t1 - 0.5f)) ? t0 : t1; + return true; +} + +template <typename T> +GPUdi() bool IntervalXY<T>::seenByLine(const IntervalXY<T>& other, T eps) const +{ + T a, b, c; // find equation of the line a*x+b*y+c = 0 + other.getLineCoefs(a, b, c); + T x0, y0, x1, y1; + eval(-eps, x0, y0); + eval(1.f + eps, x0, y0); + + return (a * x0 + b * y0 + c) * (a * x1 + b * y1 + c) < 0; +} + +template <typename T> +GPUdi() bool IntervalXY<T>::lineCrossParam(const IntervalXY<T>& other, T& t) const +{ + // tolerance + constexpr float eps = 1.e-9f; + const T dx = other.getX0() - mX; + const T dy = other.getY0() - mY; + const T det = -mDx * other.getY0() + mDY * other.getX0(); + if (gpu::CAMath::Abs(det) < eps) { + return false; // parallel + } + t = (-dx * other.getY0() + dy * other.getX0()) / det; + return true; +} + +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_INTERVALXY_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h b/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h new file mode 100644 index 0000000000000..5ec3781de0d6d --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h @@ -0,0 +1,89 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 StatAccumulator.h +/// \brief +/// \author ruben.shahoyan@cern.ch michael.lettrich@cern.ch + +#ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_STATACCUMULATOR_H_ +#define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_STATACCUMULATOR_H_ + +#ifndef GPUCA_GPUCODE_DEVICE +#include <tuple> +#endif + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ + +struct StatAccumulator { + // mean / RMS accumulator + double sum = 0.; + double sum2 = 0.; + double wsum = 0.; + int n = 0; + + void add(float v, float w = 1.) + { + const auto c = v * w; + sum += c; + sum2 += c * v; + wsum += w; + n++; + } + double getMean() const { return wsum > 0. ? sum / wsum : 0.; } + +#ifndef GPUCA_GPUCODE_DEVICE + template <typename T = float> + std::tuple<T, T> getMeanRMS2() const + { + T mean = 0; + T rms2 = 0; + + if (wsum) { + const T wi = 1. / wsum; + mean = sum * wi; + rms2 = sum2 * wi - mean * mean; + } + + return {mean, rms2}; + } +#endif + + StatAccumulator& operator+=(const StatAccumulator& other) + { + sum += other.sum; + sum2 += other.sum2; + wsum += other.wsum; + return *this; + } + + StatAccumulator operator+(const StatAccumulator& other) const + { + StatAccumulator res = *this; + res += other; + return res; + } + + void clear() + { + sum = sum2 = wsum = 0.; + n = 0; + } +}; + +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_STATACCUMULATOR_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/TypeTruncation.h b/Common/MathUtils/include/MathUtils/detail/TypeTruncation.h new file mode 100644 index 0000000000000..96cfb94bde876 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/TypeTruncation.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TypeTruncation.h +/// \brief +/// \author Nima Zardoshti <nima.zardoshti@cern.ch>, CERN (copied from AliPhysics implementation by Peter Hristov) + +#ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TYPETRUNCATION_H_ +#define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TYPETRUNCATION_H_ + +#ifndef GPUCA_GPUCODE_DEVICE +#include <cstdint> +#endif + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ + +static float truncateFloatFraction(float x, uint32_t mask = 0xFFFFFF00) +{ + // Mask the less significant bits in the float fraction (1 bit sign, 8 bits exponent, 23 bits fraction), see + // https://en.wikipedia.org/wiki/Single-precision_floating-point_format + // mask 0xFFFFFF00 means 23 - 8 = 15 bits in the fraction + union { + float y; + uint32_t iy; + } myu; + myu.y = x; + myu.iy &= mask; + return myu.y; +} + +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TYPETRUNCATION_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/bitOps.h b/Common/MathUtils/include/MathUtils/detail/bitOps.h new file mode 100644 index 0000000000000..d272069baa660 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/bitOps.h @@ -0,0 +1,54 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 bitOps.h +/// \brief +/// \author ruben.shahoyan@cern.ch michael.lettrich@cern.ch + +#ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BITOPS_H_ +#define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BITOPS_H_ + +#ifndef GPUCA_GPUCODE_DEVICE +#include <cstdint> +#endif + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ +// fast bit count +inline int numberOfBitsSet(uint32_t x) +{ + // count number of non-0 bits in 32bit word + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; +} + +// recursive creation of bitmask +template <typename T> +constexpr int bit2Mask(T v) +{ + return 0x1 << v; +} + +template <typename T, typename... Args> +constexpr int bit2Mask(T first, Args... args) +{ + return (0x1 << first) | bit2Mask(args...); +} + +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BITOPS_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/trigonometric.h b/Common/MathUtils/include/MathUtils/detail/trigonometric.h new file mode 100644 index 0000000000000..af6eb69274783 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/detail/trigonometric.h @@ -0,0 +1,268 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 trigonometric.h +/// \brief +/// \author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch + +#ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ +#define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ + +#ifndef GPUCA_GPUCODE_DEVICE +#include <cmath> +#include <tuple> +#endif +#include "GPUCommonArray.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "CommonConstants/MathConstants.h" + +namespace o2 +{ +namespace math_utils +{ +namespace detail +{ + +template <typename T> +GPUhdi() T to02Pi(T phi) +{ + T res = phi; + // ensure angle in [0:2pi] for the input in [-pi:pi] or [0:pi] + if (res < 0.f) { + res += o2::constants::math::TwoPI; + } + return res; +} + +template <typename T> +GPUhdi() void bringTo02Pi(T& phi) +{ + phi = to02Pi<T>(phi); +} + +template <typename T> +inline T to02PiGen(T phi) +{ + T res = phi; + // ensure angle in [0:2pi] for the any input angle + while (res < 0.f) { + res += o2::constants::math::TwoPI; + } + while (res > o2::constants::math::TwoPI) { + res -= o2::constants::math::TwoPI; + } + return res; +} + +template <typename T> +inline void bringTo02PiGen(T& phi) +{ + phi = to02PiGen<T>(phi); +} + +template <typename T> +GPUhdi() T toPMPi(T phi) +{ + T res = phi; + // ensure angle in [-pi:pi] for the input in [-pi:pi] or [0:pi] + if (res > o2::constants::math::PI) { + res -= o2::constants::math::TwoPI; + } else if (res < -o2::constants::math::PI) { + res += o2::constants::math::TwoPI; + } + return res; +} + +template <typename T> +GPUhdi() void bringToPMPi(T& phi) +{ + phi = toPMPi<T>(phi); +} + +template <typename T> +inline T toPMPiGen(T phi) +{ + // ensure angle in [-pi:pi] for any input angle + T res = phi; + while (res < -o2::constants::math::PI) { + res += o2::constants::math::TwoPI; + } + while (res > o2::constants::math::PI) { + res -= o2::constants::math::TwoPI; + } + return res; +} + +template <typename T> +inline void bringToPMPiGen(T& phi) +{ + phi = toPMPiGen<T>(phi); +} + +#ifdef __OPENCL__ // TODO: get rid of that stupid workaround for OpenCL template address spaces +template <typename T> +GPUhdi() void sincos(T ang, float& s, float& c) +{ + return o2::gpu::GPUCommonMath::SinCos(ang, s, c); +} +#else +template <typename T> +GPUhdi() void sincos(T ang, T& s, T& c) +{ + return o2::gpu::GPUCommonMath::SinCos(ang, s, c); +} +template <> +GPUhdi() void sincos(double ang, double& s, double& c) +{ + return o2::gpu::GPUCommonMath::SinCosd(ang, s, c); +} +#endif + +#ifndef GPUCA_GPUCODE_DEVICE + +template <typename T> +GPUhdi() std::tuple<T, T> sincos(T ang) +{ + T sin = 0; + T cos = 0; + sincos<T>(ang, sin, cos); + return std::make_tuple(sin, cos); +} + +template <typename T> +inline std::tuple<T, T> rotateZ(T xL, T yL, T snAlp, T csAlp) +{ + // 2D rotation of the point by angle alpha (local to global) + return std::make_tuple(xL * csAlp - yL * snAlp, xL * snAlp + yL * csAlp); +} + +template <typename T> +GPUhdi() std::tuple<T, T> rotateZInv(T xG, T yG, T snAlp, T csAlp) +{ + // inverse 2D rotation of the point by angle alpha (global to local) + return rotateZ<T>(xG, yG, -snAlp, csAlp); +} + +#endif + +template <typename T> +GPUhdi() void rotateZ(gpu::gpustd::array<T, 3>& xy, T alpha) +{ + // transforms vector in tracking frame alpha to global frame + T sin, cos; + sincos<T>(alpha, sin, cos); + const T x = xy[0]; + xy[0] = x * cos - xy[1] * sin; + xy[1] = x * sin + xy[1] * cos; +} + +template <typename T> +GPUhdi() void rotateZ(T xL, T yL, T& xG, T& yG, T snAlp, T csAlp) +{ + xG = xL * csAlp - yL * snAlp; + yG = xL * snAlp + yL * csAlp; + ; + // std::tie(xG, yG) = rotateZ<T>(xL, yL, snAlp, csAlp); // must not use std:: - versions on GPU +} + +template <typename T> +GPUhdi() void rotateZInv(T xG, T yG, T& xL, T& yL, T snAlp, T csAlp) +{ + // inverse 2D rotation of the point by angle alpha (global to local) + rotateZ<T>(xG, yG, xL, yL, -snAlp, csAlp); +} + +template <typename T> +GPUhdi() int angle2Sector(T phi) +{ + // convert angle to sector ID, phi can be either in 0:2pi or -pi:pi convention + int sect = phi * o2::constants::math::Rad2Deg / o2::constants::math::SectorSpanDeg; + if (phi < 0.f) { + sect += o2::constants::math::NSectors - 1; + } + return sect; +} + +template <typename T> +GPUhdi() T sector2Angle(int sect) +{ + // convert sector to its angle center, in -pi:pi convention + T ang = o2::constants::math::SectorSpanRad * (0.5f + sect); + bringToPMPi<T>(ang); + return ang; +} + +template <typename T> +GPUhdi() T angle2Alpha(T phi) +{ + // convert angle to its sector alpha + return sector2Angle<T>(angle2Sector<T>(phi)); +} + +template <typename T> +GPUhdi() T copysign(T x, T y) +{ + return o2::gpu::GPUCommonMath::Copysign(x, y); +} + +template <> +GPUhdi() double copysign(double x, double y) +{ + return o2::gpu::GPUCommonMath::Copysignd(x, y); +} + +template <typename T> +GPUhdi() T fastATan2(T y, T x) +{ + // Fast atan2(y,x) for any angle [-Pi,Pi] + // Average inaccuracy: 0.00048 + // Max inaccuracy: 0.00084 + // Speed: 6.2 times faster than atan2f() + constexpr T Pi = 3.1415926535897932384626433832795; + + auto atan = [](T a) -> T { + // returns the arctan for the angular range [-Pi/4, Pi/4] + // the polynomial coefficients are taken from: + // 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); + const T a2 = a * a; + return ((A * a2 + B) * a2 + C) * a; + }; + + auto atan2P = [atan](T yy, T xx) -> T { + // fast atan2(yy,xx) for the angular range [0,+Pi] + constexpr T Pi025 = 1 * Pi / 4; + constexpr T Pi075 = 3 * Pi / 4; + const T x1 = xx + yy; // point p1 (x1,y1) = (xx,yy) - Pi/4 + const T y1 = yy - xx; + T phi0 = 0; + T tan = 0; + if (xx < 0) { // p1 is in the range [Pi/4, 3*Pi/4] + phi0 = Pi075; + tan = -x1 / y1; + } else { // p1 is in the range [-Pi/4, Pi/4] + phi0 = Pi025; + tan = y1 / x1; + } + return phi0 + atan(tan); + }; + + // fast atan2(y,x) for any angle [-Pi,Pi] + return copysign<T>(atan2P(o2::gpu::GPUCommonMath::Abs(y), x), y); +} + +} // namespace detail +} // namespace math_utils +} // namespace o2 + +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ */ diff --git a/Common/MathUtils/include/MathUtils/fit.h b/Common/MathUtils/include/MathUtils/fit.h new file mode 100644 index 0000000000000..ea7cc626ba0af --- /dev/null +++ b/Common/MathUtils/include/MathUtils/fit.h @@ -0,0 +1,531 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MATHUTILS_MATHBASE_H_ +#define ALICEO2_MATHUTILS_MATHBASE_H_ + +/// \file MathBase.h +/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de + +#include <cmath> +#include <numeric> +#include <algorithm> +#include <vector> +#include <array> + +#include "Rtypes.h" +#include "TLinearFitter.h" +#include "TVectorD.h" +#include "TMath.h" +#include "TF1.h" +#include "Foption.h" +#include "HFitInterface.h" +#include "TFitResultPtr.h" +#include "TFitResult.h" +#include "Fit/Fitter.h" +#include "Fit/BinData.h" +#include "Math/WrappedMultiTF1.h" + +#include "Framework/Logger.h" + +namespace o2 +{ +namespace math_utils +{ +/// fit 1D array of histogrammed data with generic root function +/// +/// The code was extracted out of ROOT to be able to do fitting on an array with histogrammed data +/// instead of root histograms. +/// It is a stripped down version, so does not provide the same functionality. +/// To be used with care. +/// +/// \param[in] nbins size of the array and number of histogram bins +/// \param[in] arr array with elements +/// \param[in] xMin minimum range of the array +/// \param[in] xMax maximum range of the array +/// \param[in] func fit function +/// +/// +template <typename T> +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::DataRange range(xMin, xMax); + ROOT::Fit::DataOptions opt; + ROOT::Fit::BinData fitdata(opt, range); + fitdata.Initialize(nBins, 1); + + // create an empty TFitResult + std::shared_ptr<TFitResult> tfr(new TFitResult()); + // create the fitter from an empty fit result + //std::shared_ptr<ROOT::Fit::Fitter> fitter(new ROOT::Fit::Fitter(std::static_pointer_cast<ROOT::Fit::FitResult>(tfr) ) ); + ROOT::Fit::Fitter fitter(tfr); + //ROOT::Fit::FitConfig & fitConfig = fitter->Config(); + + const double binWidth = double(xMax - xMin) / double(nBins); + + for (Int_t ibin = 0; ibin < nBins; ibin++) { + const double x = double(xMin) + double(ibin + 0.5) * binWidth; + const double y = double(arr[ibin]); + const double ey = std::sqrt(y); + fitdata.Add(x, y, ey); + } + + const int special = func.GetNumber(); + const int npar = func.GetNpar(); + bool linear = func.IsLinear(); + if (special == 299 + npar) { + linear = kTRUE; // for polynomial functions + } + // do not use linear fitter in these case + if (fitOption.Bound || fitOption.Like || fitOption.Errors || fitOption.Gradient || fitOption.More || fitOption.User || fitOption.Integral || fitOption.Minuit) { + linear = kFALSE; + } + + if (special != 0 && !fitOption.Bound && !linear) { + if (special == 100) { + ROOT::Fit::InitGaus(fitdata, &func); // gaussian + } else if (special == 400) { + ROOT::Fit::InitGaus(fitdata, &func); // landau (use the same) + } else if (special == 200) { + ROOT::Fit::InitExpo(fitdata, &func); // exponential + } + } + + if ((linear || fitOption.Gradient)) { + fitter.SetFunction(ROOT::Math::WrappedMultiTF1(func)); + } else { + fitter.SetFunction(static_cast<const ROOT::Math::IParamMultiFunction&>(ROOT::Math::WrappedMultiTF1(func))); + } + + // standard least square fit + const bool fitok = fitter.Fit(fitdata, fitOption.ExecPolicy); + if (!fitok) { + LOGP(warning, "bad fit"); + } + + return TFitResultPtr(tfr); +} + +/// fast fit of an array with ranges (histogram) with gaussian function +/// +/// Fitting procedure: +/// 1. Step - make logarithm +/// 2. Linear fit (parabola) - more robust, always converges, fast +/// +/// \param[in] nbins size of the array and number of histogram bins +/// \param[in] arr array with elements +/// \param[in] xMin minimum range of the array +/// \param[in] xMax maximum range of the array +/// \param[out] param return paramters of the fit (0-Constant, 1-Mean, 2-Sigma, 3-Sum) +/// +/// \return chi2 or exit code +/// >0: the chi2 returned by TLinearFitter +/// -3: only three points have been used for the calculation - no fitter was used +/// -2: only two points have been used for the calculation - center of gravity was uesed for calculation +/// -1: only one point has been used for the calculation - center of gravity was uesed for calculation +/// -4: invalid result!! +/// +//template <typename T> +//Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector<T>& param); +template <typename T> +Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, std::vector<T>& param) +{ + static TLinearFitter fitter(3, "pol2"); + static TMatrixD mat(3, 3); + static Double_t kTol = mat.GetTol(); + fitter.StoreData(kFALSE); + fitter.ClearPoints(); + TVectorD par(3); + TVectorD sigma(3); + TMatrixD A(3, 3); + TMatrixD b(3, 1); + T rms = TMath::RMS(nBins, arr); + T max = TMath::MaxElement(nBins, arr); + T binWidth = (xMax - xMin) / T(nBins); + + Float_t meanCOG = 0; + Float_t rms2COG = 0; + Float_t sumCOG = 0; + + Float_t entries = 0; + Int_t nfilled = 0; + + param.resize(4); + param[0] = 0.; + param[1] = 0.; + param[2] = 0.; + param[3] = 0.; + + for (Int_t i = 0; i < nBins; i++) { + entries += arr[i]; + if (arr[i] > 0) { + nfilled++; + } + } + + // TODO: Check why this is needed + if (max < 4) { + return -4; + } + if (entries < 12) { + return -4; + } + + if (rms < kTol) { + return -4; + } + + param[3] = entries; + + Int_t npoints = 0; + for (Int_t ibin = 0; ibin < nBins; ibin++) { + Float_t entriesI = arr[ibin]; + if (entriesI > 1) { + Double_t xcenter = xMin + (ibin + 0.5) * binWidth; + Double_t error = 1. / TMath::Sqrt(entriesI); + Double_t val = TMath::Log(Float_t(entriesI)); + fitter.AddPoint(&xcenter, val, error); + if (npoints < 3) { + A(npoints, 0) = 1; + A(npoints, 1) = xcenter; + A(npoints, 2) = xcenter * xcenter; + b(npoints, 0) = val; + meanCOG += xcenter * entriesI; + rms2COG += xcenter * entriesI * xcenter; + sumCOG += entriesI; + } + npoints++; + } + } + + Double_t chi2 = 0; + if (npoints >= 3) { + if (npoints == 3) { + //analytic calculation of the parameters for three points + A.Invert(); + TMatrixD res(1, 3); + res.Mult(A, b); + par[0] = res(0, 0); + par[1] = res(0, 1); + par[2] = res(0, 2); + chi2 = -3.; + } else { + // use fitter for more than three points + fitter.Eval(); + fitter.GetParameters(par); + fitter.GetCovarianceMatrix(mat); + chi2 = fitter.GetChisquare() / Double_t(npoints); + } + if (TMath::Abs(par[1]) < kTol) { + return -4; + } + if (TMath::Abs(par[2]) < kTol) { + return -4; + } + + param[1] = T(par[1] / (-2. * par[2])); + param[2] = T(1. / TMath::Sqrt(TMath::Abs(-2. * par[2]))); + Double_t lnparam0 = par[0] + par[1] * param[1] + par[2] * param[1] * param[1]; + if (lnparam0 > 307) { + return -4; + } + param[0] = TMath::Exp(lnparam0); + + return chi2; + } + + if (npoints == 2) { + //use center of gravity for 2 points + meanCOG /= sumCOG; + rms2COG /= sumCOG; + param[0] = max; + param[1] = meanCOG; + param[2] = TMath::Sqrt(TMath::Abs(meanCOG * meanCOG - rms2COG)); + chi2 = -2.; + } + if (npoints == 1) { + meanCOG /= sumCOG; + param[0] = max; + param[1] = meanCOG; + param[2] = binWidth / TMath::Sqrt(12); + chi2 = -1.; + } + return chi2; +} + +/// struct for returning statistical parameters +/// +/// \todo make type templated? +/// \todo use Vc +struct StatisticsData { + double mCOG{0}; ///< calculated centre of gravity + double mStdDev{0}; ///< standard deviation + double mSum{0}; ///< sum of values +}; + +/// calculate statistical parameters on a binned array +/// +/// The function assumes a binned array of +/// \param nBins size of the array +/// \param xMin lower histogram bound +/// \param xMax upper histogram bound +/// \todo make return type templated? +template <typename T> +StatisticsData getStatisticsData(const T* arr, const size_t nBins, const double xMin, const double xMax) +{ + double mean = 0; + double rms2 = 0; + double sum = 0; + size_t npoints = 0; + + double binWidth = (xMax - xMin) / (double)nBins; + + StatisticsData data; + // in case something went wrong the COG is the histogram lower limit + data.mCOG = xMin; + + for (size_t ibin = 0; ibin < nBins; ++ibin) { + double entriesI = (double)arr[ibin]; + double xcenter = xMin + (ibin + 0.5) * binWidth; // +0.5 to shift to bin centre + if (entriesI > 0) { + mean += xcenter * entriesI; + rms2 += xcenter * entriesI * xcenter; + sum += entriesI; + ++npoints; + } + } + if (sum == 0) { + return data; + } + mean /= sum; + + data.mCOG = mean; + // exception in case of only one bin is filled + // set the standard deviation to bin width over sqrt(12) + rms2 /= sum; + if (npoints == 1) { + data.mStdDev = binWidth / std::sqrt(12.); + } else { + data.mStdDev = std::sqrt(std::abs(rms2 - mean * mean)); + } + + data.mSum = sum; + + return data; +} + +/// median of values in a std::vector +/// +/// we need to make a copy of the vector since we need to sort it +/// based on this discussion: https://stackoverflow.com/questions/1719070/what-is-the-right-approach-when-using-stl-container-for-median-calculation/1719155#1719155 +/// \todo Is there a better way to do this? +template <typename T, typename R = double> +R median(std::vector<T> v) +{ + if (v.empty()) { + return R{}; + } + 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 + auto max_it = max_element(v.begin(), v.begin() + n); + med = R{(*max_it + med) / 2.0}; + } + return med; +} + +/// Fills the index vector with sorted indices of the input vector. +/// The input vector is not modified (similar to TMath::Sort()). +/// \param values Vector to be indexed +/// \param index Vector to hold the sorted indices (must have the same size as values) +template <typename T> +void SortData(std::vector<T> const& values, std::vector<size_t>& index) +{ + if (values.size() != index.size()) { + LOG(error) << "Vector with values must have same size as vector for indices"; + return; + } + std::iota(index.begin(), index.end(), static_cast<size_t>(0)); + std::sort(index.begin(), index.end(), [&](size_t a, size_t b) { return values[a] < values[b]; }); +} + +/// LTM : Trimmed mean of unbinned array +/// +/// Robust statistic to estimate properties of the distribution +/// To handle binning error special treatment +/// for definition of unbinned data see: +/// http://en.wikipedia.org/w/index.php?title=Trimmed_estimator&oldid=582847999 +/// \param data Input vector (unsorted) +/// \param index Vector with indices of sorted input data +/// \param params Array with characteristics of distribution +/// \param fracKeep Fraction of data to be kept +/// \return Flag if successfull +/// Characteristics: +/// -# area +/// -# mean +/// -# rms +/// -# error estimate of mean +/// -# error estimate of RMS +/// -# first accepted element (of sorted array) +/// -# last accepted element (of sorted array) +template <typename T> +bool LTMUnbinned(const std::vector<T>& data, std::vector<size_t>& index, std::array<float, 7>& params, float fracKeep) +{ + int nPoints = data.size(); + std::vector<float> w(2 * nPoints); + int nKeep = nPoints * fracKeep; + if (nKeep > nPoints) { + nKeep = nPoints; + } + if (nKeep < 2) { + return false; + } + // sort in increasing order + SortData(data, index); + // build cumulants + double sum1 = 0.0; + double sum2 = 0.0; + for (int i = 0; i < nPoints; i++) { + double x = data[index[i]]; + sum1 += x; + sum2 += x * x; + w[i] = sum1; + w[i + nPoints] = sum2; + } + double maxRMS = sum2 + 1e6; + params[0] = nKeep; + int limI = nPoints - nKeep + 1; // lowest possible bin to accept + for (int i = 0; i < limI; i++) { + const int limJ = i + nKeep - 1; // highest accepted bin + sum1 = static_cast<double>(w[limJ]) - static_cast<double>(i ? w[i - 1] : 0.); + sum2 = static_cast<double>(w[nPoints + limJ]) - static_cast<double>(i ? w[nPoints + i - 1] : 0.); + const double mean = sum1 / nKeep; + const double rms2 = sum2 / nKeep - mean * mean; + if (rms2 > maxRMS) { + continue; + } + maxRMS = rms2; + params[1] = mean; + params[2] = rms2; + params[5] = i; + params[6] = limJ; + } + // + if (params[2] < 0) { + LOG(error) << "Rounding error: RMS = " << params[2] << " < 0"; + return false; + } + params[2] = std::sqrt(params[2]); + params[3] = params[2] / std::sqrt(params[0]); // error on mean + params[4] = params[3] / std::sqrt(2.0); // error on RMS + return true; +} + +/// Rearranges the input vector in the order given by the index vector +/// \param data Input vector +/// \param index Index vector +template <typename T> +void Reorder(std::vector<T>& data, const std::vector<size_t>& index) +{ + // rearange data in order given by index + if (data.size() != index.size()) { + LOG(error) << "Reordering not possible if number of elements in index container different from the data container"; + return; + } + std::vector<T> tmp(data); + for (size_t i = 0; i < data.size(); ++i) { + data[i] = tmp[index[i]]; + } +} + +/// Compare this function to LTMUnbinned. +/// A target sigma of the distribution can be specified and it will be trimmed to match that target. +/// \param data Input vector (unsorted) +/// \param index Vector with indices of sorted input data +/// \param params Array with characteristics of distribution +/// \param fracKeepMin Minimum fraction to keep of the input data +/// \param sigTgt Target distribution sigma +/// \param sorted Flag if the data is already sorted +/// \return Flag if successfull +template <typename T> +bool LTMUnbinnedSig(const std::vector<T>& data, std::vector<size_t>& index, std::array<float, 7>& params, float fracKeepMin, float sigTgt, bool sorted = false) +{ + int nPoints = data.size(); + std::vector<double> wx(nPoints); + std::vector<double> wx2(nPoints); + + if (!sorted) { + // sort in increasing order + SortData(data, index); + } else { + // array is already sorted + std::iota(index.begin(), index.end(), 0); + } + // build cumulants + double sum1 = 0.0; + double sum2 = 0.0; + for (int i = 0; i < nPoints; i++) { + double x = data[index[i]]; + sum1 += x; + sum2 += x * x; + wx[i] = sum1; + wx2[i] = sum2; + } + int keepMax = nPoints; + int keepMin = fracKeepMin * nPoints; + if (keepMin > keepMax) { + keepMin = keepMax; + } + float sigTgt2 = sigTgt * sigTgt; + // + while (true) { + double maxRMS = wx2.back() + 1e6; + int keepN = (keepMax + keepMin) / 2; + if (keepN < 2) { + return false; + } + params[0] = keepN; + int limI = nPoints - keepN + 1; + for (int i = 0; i < limI; ++i) { + const int limJ = i + keepN - 1; + sum1 = wx[limJ] - (i ? wx[i - 1] : 0.); + sum2 = wx2[limJ] - (i ? wx2[i - 1] : 0.); + const double mean = sum1 / keepN; + const double rms2 = sum2 / keepN - mean * mean; + if (rms2 > maxRMS) { + continue; + } + maxRMS = rms2; + params[1] = mean; + params[2] = rms2; + params[5] = i; + params[6] = limJ; + } + if (maxRMS < sigTgt2) { + keepMin = keepN; + } else { + keepMax = keepN; + } + if (keepMin >= keepMax - 1) { + break; + } + } + params[2] = std::sqrt(params[2]); + params[3] = params[2] / std::sqrt(params[0]); // error on mean + params[4] = params[3] / std::sqrt(2.0); // error on RMS + return true; +} +} // namespace math_utils +} // namespace o2 +#endif diff --git a/Common/MathUtils/src/CachingTF1.cxx b/Common/MathUtils/src/CachingTF1.cxx index ce457408984a6..1d3957a476bf2 100644 --- a/Common/MathUtils/src/CachingTF1.cxx +++ b/Common/MathUtils/src/CachingTF1.cxx @@ -10,6 +10,6 @@ #include "MathUtils/CachingTF1.h" -using namespace o2::base; +using namespace o2::math_utils; -ClassImp(o2::base::CachingTF1); +ClassImp(o2::math_utils::CachingTF1); diff --git a/Common/MathUtils/src/Cartesian.cxx b/Common/MathUtils/src/Cartesian.cxx new file mode 100644 index 0000000000000..fc6e950f115bb --- /dev/null +++ b/Common/MathUtils/src/Cartesian.cxx @@ -0,0 +1,71 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <iostream> +#include "MathUtils/Cartesian.h" + +ClassImp(o2::math_utils::Transform3D); +ClassImp(o2::math_utils::Rotation2Df_t); +ClassImp(o2::math_utils::Rotation2Dd_t); + +namespace o2 +{ +namespace math_utils +{ + +//_________________________________________________ +Transform3D::Transform3D(const TGeoMatrix& m) +{ + /* + * Construct from TGeoMatrix or its derived classes + */ + set(m); +} + +//_________________________________________________ +void Transform3D::set(const TGeoMatrix& m) +{ + /* + * Set parameters from TGeoMatrix + */ + const double* t = m.GetTranslation(); + const double* r = m.GetRotationMatrix(); + SetComponents(r[0], r[1], r[2], t[0], r[3], r[4], r[5], t[1], r[6], r[7], r[8], t[2]); +} + +//_________________________________________________ +void Transform3D::print() const +{ + /* + * print itself + */ + std::cout << *this << std::endl; +} + +} // namespace math_utils +} // namespace o2 + +//_________________________________________________ +std::ostream& operator<<(std::ostream& os, const o2::math_utils::Rotation2Df_t& t) +{ + float cs, sn; + t.getComponents(cs, sn); + os << "cos: " << cs << " sin: " << sn; + return os; +} + +//_________________________________________________ +std::ostream& operator<<(std::ostream& os, const o2::math_utils::Rotation2Dd_t& t) +{ + double cs, sn; + t.getComponents(cs, sn); + os << "cos: " << cs << " sin: " << sn; + return os; +} diff --git a/Common/MathUtils/src/Cartesian2D.cxx b/Common/MathUtils/src/Cartesian2D.cxx deleted file mode 100644 index ebdb63375afaa..0000000000000 --- a/Common/MathUtils/src/Cartesian2D.cxx +++ /dev/null @@ -1,11 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/Cartesian2D.h" diff --git a/Common/MathUtils/src/Cartesian3D.cxx b/Common/MathUtils/src/Cartesian3D.cxx deleted file mode 100644 index cf9e17da9070b..0000000000000 --- a/Common/MathUtils/src/Cartesian3D.cxx +++ /dev/null @@ -1,55 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/Cartesian3D.h" -#include <iostream> - -using namespace o2; - -ClassImp(o2::Transform3D); -ClassImp(o2::Rotation2D); - -//_________________________________________________ -Transform3D::Transform3D(const TGeoMatrix& m) -{ - /* - * Construct from TGeoMatrix or its derived classes - */ - set(m); -} - -//_________________________________________________ -void Transform3D::set(const TGeoMatrix& m) -{ - /* - * Set parameters from TGeoMatrix - */ - const double* t = m.GetTranslation(); - const double* r = m.GetRotationMatrix(); - SetComponents(r[0], r[1], r[2], t[0], r[3], r[4], r[5], t[1], r[6], r[7], r[8], t[2]); -} - -//_________________________________________________ -void Transform3D::print() const -{ - /* - * print itself - */ - std::cout << *this << std::endl; -} - -//_________________________________________________ -std::ostream& operator<<(std::ostream& os, const o2::Rotation2D& t) -{ - float cs, sn; - t.getComponents(cs, sn); - os << "cos: " << cs << " sin: " << sn; - return os; -} diff --git a/Common/MathUtils/src/Chebyshev3D.cxx b/Common/MathUtils/src/Chebyshev3D.cxx index 963df5ae1ba60..2fd984640b6af 100644 --- a/Common/MathUtils/src/Chebyshev3D.cxx +++ b/Common/MathUtils/src/Chebyshev3D.cxx @@ -524,8 +524,9 @@ Int_t Chebyshev3D::chebyshevFit(int dmOut) Chebyshev3DCalc* cheb = getChebyshevCalc(dmOut); Float_t prec = cheb->getPrecision(); - if (prec < sMinimumPrecision) + if (prec < sMinimumPrecision) { prec = mPrecision; // no specific precision for this dim. + } Float_t rTiny = 0.1 * prec / Float_t(maxDim); // neglect coefficient below this threshold float ncals2count = mNumberOfPoints[2] * mNumberOfPoints[1] * mNumberOfPoints[0]; @@ -864,8 +865,9 @@ TH1* Chebyshev3D::TestRMS(int idim, int npoints, TH1* histo) } float prc = getChebyshevCalc(idim)->getPrecision(); - if (prc < sMinimumPrecision) + if (prc < sMinimumPrecision) { prc = mPrecision; // no dimension specific precision + } for (int ip = npoints; ip--;) { gRandom->RndmArray(3, (Float_t*)mTemporaryCoefficient); diff --git a/Common/MathUtils/src/MathBase.cxx b/Common/MathUtils/src/MathBase.cxx deleted file mode 100644 index 29818ee3f0380..0000000000000 --- a/Common/MathUtils/src/MathBase.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MathBase.cxx -/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de - -#include "MathUtils/MathBase.h" - -using namespace o2::math_utils::math_base; diff --git a/Common/MathUtils/src/MathUtilsLinkDef.h b/Common/MathUtils/src/MathUtilsLinkDef.h index 7be4951a0bee5..59f6deb11b8e0 100644 --- a/Common/MathUtils/src/MathUtilsLinkDef.h +++ b/Common/MathUtils/src/MathUtilsLinkDef.h @@ -12,29 +12,31 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; -#pragma link C++ enum o2::TransformType; +#pragma link C++ enum o2::math_utils::TransformType; #pragma link C++ class o2::math_utils::Chebyshev3D + ; #pragma link C++ class o2::math_utils::Chebyshev3DCalc + ; -#pragma link C++ namespace o2::math_utils::math_base; +#pragma link C++ function o2::math_utils::fit < float>; +#pragma link C++ function o2::math_utils::fit < double>; -#pragma link C++ function o2::math_utils::math_base::fit < float>; -#pragma link C++ function o2::math_utils::math_base::fit < double>; +#pragma link C++ function o2::math_utils::fitGaus < float>; +#pragma link C++ function o2::math_utils::fitGaus < double>; -#pragma link C++ function o2::math_utils::math_base::fitGaus < float>; -#pragma link C++ function o2::math_utils::math_base::fitGaus < double>; +#pragma link C++ function o2::math_utils::getStatisticsData < float>; +#pragma link C++ function o2::math_utils::getStatisticsData < double>; +#pragma link C++ function o2::math_utils::getStatisticsData < short>; -#pragma link C++ function o2::math_utils::math_base::getStatisticsData < float>; -#pragma link C++ function o2::math_utils::math_base::getStatisticsData < double>; -#pragma link C++ function o2::math_utils::math_base::getStatisticsData < short>; +#pragma link C++ class o2::math_utils::Transform3D + ; +#pragma link C++ class o2::math_utils::Rotation2Df_t + ; +#pragma link C++ class o2::math_utils::Rotation2Dd_t + ; +#pragma link C++ class o2::math_utils::CachingTF1 + ; -#pragma link C++ class o2::Transform3D + ; -#pragma link C++ class o2::Rotation2D + ; -#pragma link C++ class o2::base::CachingTF1 + ; - -#pragma link C++ class o2::utils::CircleXY + ; -#pragma link C++ class o2::utils::IntervalXY + ; -#pragma link C++ class o2::utils::Bracket < float> + ; +#pragma link C++ class o2::math_utils::CircleXYf_t + ; +#pragma link C++ class o2::math_utils::CircleXYd_t + ; +#pragma link C++ class o2::math_utils::IntervalXYf_t + ; +#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 + ; #endif diff --git a/Common/MathUtils/src/Primitive2D.cxx b/Common/MathUtils/src/Primitive2D.cxx deleted file mode 100644 index a380d03efe44b..0000000000000 --- a/Common/MathUtils/src/Primitive2D.cxx +++ /dev/null @@ -1,110 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Primitive.cxx -/// \brief Implementation of 2D primitives: straight line (XY interval) and circle -/// \author ruben.shahoyan@cern.ch - -#include "MathUtils/Primitive2D.h" -#include <cmath> - -using namespace o2::utils; - -//_____________________________________________________________________ -bool IntervalXY::seenByCircle(const CircleXY& circle, float eps) const -{ - // check if XY interval is seen by the circle. - // The tolerance parameter eps is interpreted as a fraction of the interval - // lenght to be added to the edges along the interval (i.e. while the points - // within the interval are spanning - // x = xc + dx*t - // y = yc + dy*t - // with 0<t<1., we acctually check the interval for -eps<t<1+eps - auto dx0 = xP - circle.xC, dy0 = yP - circle.yC, dx1 = dx0 + dxP, dy1 = dy0 + dyP; - if (eps > 0.) { - auto dxeps = eps * dxP, dyeps = eps * dyP; - dx0 -= dxeps; - dx1 += dxeps; - dy0 -= dyeps; - dy1 += dyeps; - } - auto d02 = dx0 * dx0 + dy0 * dy0, d12 = dx1 * dx1 + dy1 * dy1, rC2 = circle.rC * circle.rC; // distance^2 from circle center to edges - return (d02 - rC2) * (d12 - rC2) < 0; -} - -//_____________________________________________________________________ -bool IntervalXY::seenByLine(const IntervalXY& other, float eps) const -{ - // check if XY interval is seen by the line defined by other interval - // The tolerance parameter eps is interpreted as a fraction of the interval - // lenght to be added to the edges along the interval (i.e. while the points - // within the interval are spanning - // x = xc + dx*t - // y = yc + dy*t - // with 0<t<1., we acctually check the interval for -eps<t<1+eps - float a, b, c, x0, y0, x1, y1; // find equation of the line a*x+b*y+c = 0 - other.getLineCoefs(a, b, c); - eval(-eps, x0, y0); - eval(1.f + eps, x1, y1); - return (a * x0 + b * y0 + c) * (a * x1 + b * y1 + c) < 0; -} - -//_____________________________________________________________________ -bool IntervalXY::lineCrossParam(const IntervalXY& other, float& t) const -{ - // get crossing parameter of 2 intervals - float dx = other.xP - xP, dy = other.yP - yP; - float det = -dxP * other.dyP + dyP * other.dxP; - if (fabs(det) < 1.e-9) { - return false; // parallel - } - t = (-dx * other.dyP + dy * other.dxP) / det; - return true; -} - -//_____________________________________________________________________ -bool IntervalXY::circleCrossParam(const CircleXY& circle, float& t) const -{ - // get crossing point of circle with the interval - // solution of the system (wrt t) - // (x-xC)^2+(y-yC)^2 = rC2 - // x = xEdge + xProj * t; - // y = yEdge + yProj * t; - float dx = xP - circle.xC, dy = yP - circle.yC, del2I = 1. / (dxP * dxP + dyP * dyP), - b = (dx * dxP + dy * dyP) * del2I, c = del2I * (dx * dx + dy * dy - circle.rC * circle.rC); - float det = b * b - c; - if (det < 0.) { - return false; - } - det = sqrtf(det); - float t0 = -b - det, t1 = -b + det; - // select the one closer to [0:1] interval - t = (fabs(t0 - 0.5) < fabs(t1 - 0.5)) ? t0 : t1; - return true; -} - -//_____________________________________________________________________ -void IntervalXY::getLineCoefs(float& a, float& b, float& c) const -{ - // convert to line parameters in canonical form: a*x+b*y+c = 0 - c = xP * dyP - yP * dxP; - if (c) { - a = -dyP; - b = dxP; - } else if (fabs(dxP) > fabs(dyP)) { - a = 0.; - b = -1.; - c = yP; - } else { - a = -1.; - b = 0.; - c = xP; - } -} diff --git a/Common/MathUtils/src/RandomRing.cxx b/Common/MathUtils/src/RandomRing.cxx deleted file mode 100644 index d30476e781bdc..0000000000000 --- a/Common/MathUtils/src/RandomRing.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 RandomRing.cxx -/// @author Jens Wiechula, Jens.Wiechula@cern.ch -/// - -#include "MathUtils/RandomRing.h" diff --git a/Common/MathUtils/test/testCachingTF1.cxx b/Common/MathUtils/test/testCachingTF1.cxx index f20a6f0c249cf..adab3ef589ca5 100644 --- a/Common/MathUtils/test/testCachingTF1.cxx +++ b/Common/MathUtils/test/testCachingTF1.cxx @@ -15,12 +15,10 @@ #include "MathUtils/CachingTF1.h" #include <TFile.h> -using namespace o2::base; - BOOST_AUTO_TEST_CASE(CachingTF1_test) { std::string s("std::pow(x, 1.2)*std::exp(-x/3.)"); - CachingTF1 func("testfunction", s.c_str(), 0, 100.); + o2::math_utils::CachingTF1 func("testfunction", s.c_str(), 0, 100.); const int kNPoints = 500; func.SetNpx(kNPoints); BOOST_CHECK(func.getIntegralVector().size() == 0); @@ -33,7 +31,7 @@ BOOST_AUTO_TEST_CASE(CachingTF1_test) // open for reading and verify that integral was cached f = TFile::Open("tmpTF1Cache.root"); BOOST_CHECK(f); - volatile auto func2 = (CachingTF1*)f->Get("func"); + volatile auto func2 = (o2::math_utils::CachingTF1*)f->Get("func"); BOOST_CHECK(func2); BOOST_CHECK(func2->getIntegralVector().size() == kNPoints + 1); diff --git a/Common/MathUtils/test/testCartesian.cxx b/Common/MathUtils/test/testCartesian.cxx new file mode 100644 index 0000000000000..da17623d5fdc3 --- /dev/null +++ b/Common/MathUtils/test/testCartesian.cxx @@ -0,0 +1,95 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Cartesian3D +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <TGeoMatrix.h> +#include <boost/test/unit_test.hpp> +#include <iostream> +#include <algorithm> +#include <memory> +#include <cstring> +#include "MathUtils/Cartesian.h" + +using namespace o2; + +BOOST_AUTO_TEST_CASE(Cartesian_test) +{ + // we create Transform3D by conversion from TGeoHMatrix + TGeoRotation rotg("r", 10., 20., 30.); + TGeoTranslation trag("g", 100., 200., 300.); + TGeoHMatrix hmat = trag; + hmat *= rotg; + + math_utils::Transform3D tr(hmat); + math_utils::Point3D<double> pd(10., 20., 30.); + math_utils::Point3D<float> pf(10.f, 20.f, 30.f); + // + // local to master + auto pdt = tr(pd); // operator form + math_utils::Point3D<float> pft; + tr.LocalToMaster(pf, pft); // TGeoHMatrix form + + std::cout << "Create Transform3D " << std::endl + << tr << std::endl + << "from" << std::endl; + hmat.Print(); + + std::cout << " Transforming " << pd << " to master" << std::endl; + // compare difference between float and double vector transform + std::cout << "Float: " << pft << std::endl; + std::cout << "Double: " << pdt << std::endl; + std::cout << "Diff: " << pdt.X() - pft.X() << "," << pdt.Y() - pft.Y() << "," << pdt.Z() - pft.Z() << std::endl; + + const double toler = 1e-4; + BOOST_CHECK(std::abs(pdt.X() - pft.X()) < toler); + BOOST_CHECK(std::abs(pdt.Y() - pft.Y()) < toler); + BOOST_CHECK(std::abs(pdt.Z() - pft.Z()) < toler); + + // inverse transform + auto pfti = tr ^ (pft); // operator form + math_utils::Point3D<double> pdti; + tr.MasterToLocal(pdt, pdti); // TGeoHMatrix form + + std::cout << " Transforming back to local" << std::endl; + std::cout << "Float: " << pfti << std::endl; + std::cout << "Double: " << pdti << std::endl; + std::cout << "Diff: " << pd.X() - pfti.X() << ", " << pd.Y() - pfti.Y() << ", " << pd.Z() - pfti.Z() << std::endl; + + BOOST_CHECK(std::abs(pd.X() - pfti.X()) < toler); + BOOST_CHECK(std::abs(pd.Y() - pfti.Y()) < toler); + BOOST_CHECK(std::abs(pd.Z() - pfti.Z()) < toler); + + BOOST_CHECK(std::abs(pdti.X() - pfti.X()) < toler); + BOOST_CHECK(std::abs(pdti.Y() - pfti.Y()) < toler); + BOOST_CHECK(std::abs(pdti.Z() - pfti.Z()) < toler); +} + +BOOST_AUTO_TEST_CASE(Point3D_messageable) +{ + using ElementType = math_utils::Point3D<int>; + static_assert(std::is_trivially_copyable<ElementType>::value == true); + std::vector<ElementType> pts(10); + auto makeElement = [](int idx) { + return ElementType{idx, idx + 10, idx + 20}; + }; + std::generate(pts.begin(), pts.end(), [makeElement, idx = std::make_shared<int>(0)]() { return makeElement(++(*idx)); }); + + size_t memsize = sizeof(ElementType) * pts.size(); + auto buffer = std::make_unique<char[]>(memsize); + memcpy(buffer.get(), (char*)pts.data(), memsize); + auto* pp = reinterpret_cast<ElementType*>(buffer.get()); + + for (auto const& point : pts) { + BOOST_REQUIRE(point == *pp); + ++pp; + } +} diff --git a/Common/MathUtils/test/testCartesian3D.cxx b/Common/MathUtils/test/testCartesian3D.cxx deleted file mode 100644 index 4a4b2303b5ab8..0000000000000 --- a/Common/MathUtils/test/testCartesian3D.cxx +++ /dev/null @@ -1,95 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Cartesian3D -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include <TGeoMatrix.h> -#include <boost/test/unit_test.hpp> -#include <iostream> -#include <algorithm> -#include <memory> -#include <cstring> -#include "MathUtils/Cartesian3D.h" - -using namespace o2; - -BOOST_AUTO_TEST_CASE(Cartesian3D_test) -{ - // we create Transform3D by conversion from TGeoHMatrix - TGeoRotation rotg("r", 10., 20., 30.); - TGeoTranslation trag("g", 100., 200., 300.); - TGeoHMatrix hmat = trag; - hmat *= rotg; - - Transform3D tr(hmat); - Point3D<double> pd(10., 20., 30.); - Point3D<float> pf(10.f, 20.f, 30.f); - // - // local to master - auto pdt = tr(pd); // operator form - Point3D<float> pft; - tr.LocalToMaster(pf, pft); // TGeoHMatrix form - - std::cout << "Create Transform3D " << std::endl - << tr << std::endl - << "from" << std::endl; - hmat.Print(); - - std::cout << " Transforming " << pd << " to master" << std::endl; - // compare difference between float and double vector transform - std::cout << "Float: " << pft << std::endl; - std::cout << "Double: " << pdt << std::endl; - std::cout << "Diff: " << pdt.X() - pft.X() << "," << pdt.Y() - pft.Y() << "," << pdt.Z() - pft.Z() << std::endl; - - const double toler = 1e-4; - BOOST_CHECK(std::abs(pdt.X() - pft.X()) < toler); - BOOST_CHECK(std::abs(pdt.Y() - pft.Y()) < toler); - BOOST_CHECK(std::abs(pdt.Z() - pft.Z()) < toler); - - // inverse transform - auto pfti = tr ^ (pft); // operator form - Point3D<double> pdti; - tr.MasterToLocal(pdt, pdti); // TGeoHMatrix form - - std::cout << " Transforming back to local" << std::endl; - std::cout << "Float: " << pfti << std::endl; - std::cout << "Double: " << pdti << std::endl; - std::cout << "Diff: " << pd.X() - pfti.X() << ", " << pd.Y() - pfti.Y() << ", " << pd.Z() - pfti.Z() << std::endl; - - BOOST_CHECK(std::abs(pd.X() - pfti.X()) < toler); - BOOST_CHECK(std::abs(pd.Y() - pfti.Y()) < toler); - BOOST_CHECK(std::abs(pd.Z() - pfti.Z()) < toler); - - BOOST_CHECK(std::abs(pdti.X() - pfti.X()) < toler); - BOOST_CHECK(std::abs(pdti.Y() - pfti.Y()) < toler); - BOOST_CHECK(std::abs(pdti.Z() - pfti.Z()) < toler); -} - -BOOST_AUTO_TEST_CASE(Point3D_messageable) -{ - using ElementType = Point3D<int>; - static_assert(std::is_trivially_copyable<ElementType>::value == true); - std::vector<ElementType> pts(10); - auto makeElement = [](int idx) { - return ElementType{idx, idx + 10, idx + 20}; - }; - std::generate(pts.begin(), pts.end(), [makeElement, idx = std::make_shared<int>(0)]() { return makeElement(++(*idx)); }); - - size_t memsize = sizeof(ElementType) * pts.size(); - auto buffer = std::make_unique<char[]>(memsize); - memcpy(buffer.get(), (char*)pts.data(), memsize); - auto* pp = reinterpret_cast<ElementType*>(buffer.get()); - - for (auto const& point : pts) { - BOOST_REQUIRE(point == *pp); - ++pp; - } -} diff --git a/Common/MathUtils/test/testUtils.cxx b/Common/MathUtils/test/testUtils.cxx index 804a246765c8b..4bf03b94f8322 100644 --- a/Common/MathUtils/test/testUtils.cxx +++ b/Common/MathUtils/test/testUtils.cxx @@ -20,30 +20,30 @@ #include "MathUtils/Utils.h" using namespace o2; -using namespace utils; BOOST_AUTO_TEST_CASE(Utils_test) { // test MathUtils/Utils.h - { // test FastATan2() + { // test fastATan2() int n = 1000; - TProfile* p = new TProfile("pFastATan2", "FastATan2(y,x)", n, -TMath::Pi(), TMath::Pi()); + TProfile* p = new TProfile("pFastATan2", "fastATan2(y,x)", n, -TMath::Pi(), TMath::Pi()); double maxDiff = 0; for (int i = 0; i < n; i++) { double phi0 = -TMath::Pi() + i * TMath::TwoPi() / n; float x = TMath::Cos(phi0); float y = TMath::Sin(phi0); - float phi = utils::FastATan2(y, x); + float phi = math_utils::fastATan2(y, x); double diff = phi - phi0; p->Fill(phi0, diff); - diff = fabs(diff); - if (diff > maxDiff) + diff = std::fabs(diff); + if (diff > maxDiff) { maxDiff = diff; + } } //p->Draw(); - std::cout << "test FastATan2:" << std::endl; + std::cout << "test fastATan2:" << std::endl; std::cout << " Max inaccuracy " << maxDiff << std::endl; // test the speed @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(Utils_test) begin = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { for (int j = 0; j < M; ++j) { - dsum += atan2(vyd[j], vxd[j]); + dsum += std::atan2(vyd[j], vxd[j]); } } end = std::chrono::high_resolution_clock::now(); @@ -108,15 +108,15 @@ BOOST_AUTO_TEST_CASE(Utils_test) begin = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { for (int j = 0; j < M; ++j) { - sum += utils::FastATan2(vy[j], vx[j]); + sum += math_utils::fastATan2(vy[j], vx[j]); } } end = std::chrono::high_resolution_clock::now(); auto time4 = scale * std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count(); - std::cout << " FastATan2: time " << time4 << " ns. checksum " << sum << std::endl; + std::cout << " fastATan2: time " << time4 << " ns. checksum " << sum << std::endl; std::cout << " speed up to atan2f(): " << (time3 - time1) / (time4 - time1) << " times " << std::endl; BOOST_CHECK(maxDiff < 1.e-3); - } // test FastATan2() + } // test fastATan2() } diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index 435e9a1312786..f510a91abcad1 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -27,11 +27,6 @@ struct SimConfigData { 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 mHepMCFileName; // file name of HepMC file - std::string mExtGenFileName; // file name containing the external generator configuration - std::string mExtGenFuncName; // function call to retrieve the external generator configuration - std::string mExtTrgFileName; // file name containing the external trigger configuration - std::string mExtTrgFuncName; // function call to retrieve the external trigger configuration 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 @@ -101,11 +96,6 @@ class SimConfig unsigned int getNEvents() const { return mConfigData.mNEvents; } std::string getExtKinematicsFileName() const { return mConfigData.mExtKinFileName; } - std::string getHepMCFileName() const { return mConfigData.mHepMCFileName; } - std::string getExtGeneratorFileName() const { return mConfigData.mExtGenFileName; } - std::string getExtGeneratorFuncName() const { return mConfigData.mExtGenFuncName; } - std::string getExtTriggerFileName() const { return mConfigData.mExtTrgFileName; } - std::string getExtTriggerFuncName() const { return mConfigData.mExtTrgFuncName; } std::string getEmbedIntoFileName() const { return mConfigData.mEmbedIntoFileName; } unsigned int getStartEvent() const { return mConfigData.mStartEvent; } float getBMax() const { return mConfigData.mBMax; } diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 54089d9a484f3..a6f4192949ec1 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -31,16 +31,6 @@ void SimConfig::initOptions(boost::program_options::options_description& options "startEvent", bpo::value<unsigned int>()->default_value(0), "index of first event to be used (when applicable)")( "extKinFile", bpo::value<std::string>()->default_value("Kinematics.root"), "name of kinematics file for event generator from file (when applicable)")( - "HepMCFile", bpo::value<std::string>()->default_value("kinematics.hepmc"), - "name of HepMC file for event generator from file (when applicable)")( - "extGenFile", bpo::value<std::string>()->default_value("extgen.C"), - "name of .C file with definition of external event generator")( - "extGenFunc", bpo::value<std::string>()->default_value(""), - "function call to load the definition of external event generator")( - "extTrgFile", bpo::value<std::string>()->default_value("exttrg.C"), - "name of .C file with definition of external event generator trigger")( - "extTrgFunc", bpo::value<std::string>()->default_value(""), - "function call to load the definition of external event generator trigger")( "embedIntoFile", bpo::value<std::string>()->default_value(""), "filename containing the reference events to be used for the embedding")( "bMax,b", bpo::value<float>()->default_value(0.), "maximum value for impact parameter sampling (when applicable)")( @@ -69,7 +59,13 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& if (active.size() == 1 && active[0] == "all") { active.clear(); for (int d = DetID::First; d <= DetID::Last; ++d) { +#ifdef ENABLE_UPGRADES + if (d != DetID::IT3 && d != DetID::TRK) { + active.emplace_back(DetID::getName(d)); + } +#else active.emplace_back(DetID::getName(d)); +#endif } // add passive components manually (make a PassiveDetID for them!) active.emplace_back("HALL"); @@ -94,11 +90,6 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& mConfigData.mTrigger = vm["trigger"].as<std::string>(); mConfigData.mNEvents = vm["nEvents"].as<unsigned int>(); mConfigData.mExtKinFileName = vm["extKinFile"].as<std::string>(); - mConfigData.mHepMCFileName = vm["HepMCFile"].as<std::string>(); - mConfigData.mExtGenFileName = vm["extGenFile"].as<std::string>(); - mConfigData.mExtGenFuncName = vm["extGenFunc"].as<std::string>(); - mConfigData.mExtTrgFileName = vm["extTrgFile"].as<std::string>(); - mConfigData.mExtTrgFuncName = vm["extTrgFunc"].as<std::string>(); mConfigData.mEmbedIntoFileName = vm["embedIntoFile"].as<std::string>(); mConfigData.mStartEvent = vm["startEvent"].as<unsigned int>(); mConfigData.mBMax = vm["bMax"].as<float>(); diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 2a1d6a4d1416a..8e12f538f3c99 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -12,7 +12,7 @@ o2_add_library(CommonUtils SOURCES src/TreeStream.cxx src/TreeStreamRedirector.cxx src/RootChain.cxx src/CompStream.cxx src/ShmManager.cxx src/ValueMonitor.cxx - src/ConfigurableParamHelper.cxx src/ConfigurableParam.cxx + src/ConfigurableParamHelper.cxx src/ConfigurableParam.cxx src/RootSerializableKeyValueStore.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::Tree Boost::iostreams O2::CommonDataFormat O2::Headers FairLogger::FairLogger) @@ -27,7 +27,9 @@ o2_target_root_dictionary(CommonUtils include/CommonUtils/ValueMonitor.h include/CommonUtils/MemFileHelper.h include/CommonUtils/ConfigurableParam.h - include/CommonUtils/ConfigurableParamHelper.h) + include/CommonUtils/ConfigurableParamHelper.h + include/CommonUtils/ConfigurationMacroHelper.h + include/CommonUtils/RootSerializableKeyValueStore.h) o2_add_test(TreeStream COMPONENT_NAME CommonUtils @@ -53,6 +55,12 @@ o2_add_test(ValueMonitor SOURCES test/testValueMonitor.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils) +o2_add_test(PropertyMapIO + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testRootSerializableKeyValueStore.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + o2_add_test(MemFileHelper COMPONENT_NAME CommonUtils LABELS utils diff --git a/Generators/include/Generators/ConfigurationMacroHelper.h b/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h similarity index 92% rename from Generators/include/Generators/ConfigurationMacroHelper.h rename to Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h index cc34961d33445..58bc912a9990f 100644 --- a/Generators/include/Generators/ConfigurationMacroHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h @@ -10,8 +10,8 @@ /// \author R+Preghenella - February 2020 -#ifndef ALICEO2_EVENTGEN_CONFIGURATIONMACRO_H_ -#define ALICEO2_EVENTGEN_CONFIGURATIONMACRO_H_ +#ifndef ALICEO2_CONF_CONFIGURATIONMACRO_H_ +#define ALICEO2_CONF_CONFIGURATIONMACRO_H_ #include "FairLogger.h" #include "TROOT.h" @@ -22,7 +22,7 @@ namespace o2 { -namespace eventgen +namespace conf { template <typename T> @@ -67,7 +67,7 @@ T GetFromMacro(const std::string& file, const std::string& funcname, const std:: return *ptr; } -} // namespace eventgen +} // namespace conf } // namespace o2 -#endif /* ALICEO2_EVENTGEN_CONFIGURATIONMACRO_H_ */ +#endif /* ALICEO2_CONF_CONFIGURATIONMACRO_H_ */ diff --git a/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h b/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h new file mode 100644 index 0000000000000..a4dda395d593b --- /dev/null +++ b/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h @@ -0,0 +1,241 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ROOTSERKEYVALUESTORE_H +#define ALICEO2_ROOTSERKEYVALUESTORE_H + +#include <map> +#include <string> +#include <TClass.h> +#include <Rtypes.h> +#include <typeinfo> +#include <typeindex> +#include <TBufferFile.h> +#include <type_traits> + +namespace o2 +{ +namespace utils +{ + +/// A ROOT serializable container mapping a key (string) to an object of arbitrary type. +/// +/// The container allows to group objects in heterogeneous type in a key-value container. +/// The container can be ROOT serialized which adds on-top of existing solutions such as boost::property_tree. +/// This may be useful in various circumenstances, for +/// instance to assemble various CCDB objects into a single aggregate to reduce the number of CCDB files/entries. +class RootSerializableKeyValueStore +{ + public: + /// Structure encapsulating the stored information: raw buffers and attached type information (combination of type_index_hash and TClass information) + struct SerializedInfo { + void* objptr = nullptr; //! pointer to existing object in memory + 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; + std::string typeinfo_name; // typeinfo name that can be used to store type if TClass not available (for PODs!) + ClassDefNV(SerializedInfo, 1); + }; + + enum class GetState { + kOK = 0, + kNOSUCHKEY = 1, + kWRONGTYPE = 2, + kNOTCLASS = 3 + }; + + private: + static constexpr const char* GetStateString[4] = { + "ok", + "no such key", + "wrong type", + "no TClass"}; + + public: + static const char* getStateString(GetState state) + { + return (int)state < 4 ? GetStateString[(int)state] : nullptr; + }; + + RootSerializableKeyValueStore() = default; + + /// Putting a value (and overrides previous entries) + /// T needs to be trivial non-pointer type or a type having a ROOT TClass instance + template <typename T> + void put(std::string const& key, T const& value) + { + remove_entry(key); + GetState s; + return put_impl(key, value, s, int{}); + } + + /// returns object pointer for this key or nullptr if error or does not exist. + template <typename T> + const T* get(std::string const& key) const + { + GetState s; + return get_impl<T>(key, s, int{}); + } + + /// returns object pointer for this key or nullptr if error or does not exist; state is set with meaningful error code + template <typename T> + const T* get(std::string const& key, GetState& state) const + { + return get_impl<T>(key, state, int{}); + } + + /// get interface returning a const reference instead of pointer; Error handling/detection is done via the state argument; + /// Beware: In case of errors, a default object is returned. + template <typename T> + const T& getRef(std::string const& key, GetState& state) const + { + auto ptr = get_impl<T>(key, state, int{}); + if (ptr) { + return *ptr; + } else { + static T t = T(); + // in case of error we return a default object + return t; + } + } + + /// checks if a key exists + bool has(std::string const& key) const + { + return mStore.find(key) != mStore.end(); + } + + /// clear the store + void clear() + { + mStore.clear(); + } + + /// print list of keys and type information + void print() const; + + private: + std::map<std::string, SerializedInfo*> mStore; + + // generic implementation for put relying on TClass + template <typename T> + void put_impl(std::string const& key, T const& value, GetState& state, ...) + { + // 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 cl = TClass::GetClass(typeid(value)); + if (!cl) { + state = GetState::kNOTCLASS; + return; + } + char* bufferptr = nullptr; + TBufferFile buff(TBuffer::kWrite); + buff.WriteObjectAny(ptr, cl); + int N = buff.Length(); + bufferptr = new char[N]; + memcpy(bufferptr, buff.Buffer(), sizeof(char) * N); + + auto name = std::type_index(typeid(value)).name(); + mStore.insert(std::pair<std::string, SerializedInfo*>(key, new SerializedInfo{(void*)ptr, N, (char*)bufferptr, cl, name})); + } + + // implementation for put for trivial types + template <typename T, typename std::enable_if<std::is_trivial<T>::value, T>::type* = nullptr> + void put_impl(std::string const& key, T const& value, GetState& state, int) + { + // we forbid pointers + static_assert(!std::is_pointer<T>::value); + // serialization of trivial types is easy (not based on ROOT) + auto ptr = new T(value); + int N = sizeof(T); + auto bufferptr = new char[N]; + memcpy(bufferptr, (char*)ptr, sizeof(char) * N); + + auto name = std::type_index(typeid(value)).name(); + mStore.insert(std::pair<std::string, SerializedInfo*>(key, new SerializedInfo{(void*)ptr, N, (char*)bufferptr, nullptr, name})); + } + + // generic implementation for get relying on TClass + template <typename T> + const T* get_impl(std::string const& key, GetState& state, ...) const + { + state = GetState::kOK; + auto iter = mStore.find(key); + if (iter != mStore.end()) { + auto value = iter->second; + auto cl = TClass::GetClass(typeid(T)); + if (!cl) { + state = GetState::kNOTCLASS; + return nullptr; + } + if (value->cl && strcmp(cl->GetName(), value->cl->GetName()) == 0) { + // if there is a (cached) object pointer ... we return it + if (value->objptr) { + return (T*)value->objptr; + } + // do this only once and cache instance into value.objptr + TBufferFile buff(TBuffer::kRead, value->N, value->bufferptr, false, nullptr); + buff.Reset(); + auto instance = (T*)buff.ReadObjectAny(cl); + value->objptr = instance; + return (T*)value->objptr; + } else { + state = GetState::kWRONGTYPE; + return nullptr; + } + } + state = GetState::kNOSUCHKEY; + return nullptr; + } + + // implementation for standard POD types + template <typename T, typename std::enable_if<std::is_trivial<T>::value, T>::type* = nullptr> + const T* get_impl(std::string const& key, GetState& state, int) const + { + state = GetState::kOK; + auto iter = mStore.find(key); + if (iter != mStore.end()) { + auto value = iter->second; + if (strcmp(std::type_index(typeid(T)).name(), value->typeinfo_name.c_str()) == 0) { + // if there is a (cached) object pointer ... we return it + if (value->objptr) { + return (T*)value->objptr; + } + value->objptr = (T*)value->bufferptr; + return (T*)value->objptr; + } else { + state = GetState::kWRONGTYPE; + return nullptr; + } + } + state = GetState::kNOSUCHKEY; + return nullptr; + } + + // removes a previous entry + void remove_entry(std::string const& key) + { + auto iter = mStore.find(key); + if (iter != mStore.end()) { + delete iter->second; + mStore.erase(iter); + } + } + + ClassDefNV(RootSerializableKeyValueStore, 1); +}; + +} // namespace utils +} // namespace o2 + +#endif diff --git a/Common/Utils/include/CommonUtils/ShmManager.h b/Common/Utils/include/CommonUtils/ShmManager.h index b59998c36289f..72308780bff16 100644 --- a/Common/Utils/include/CommonUtils/ShmManager.h +++ b/Common/Utils/include/CommonUtils/ShmManager.h @@ -20,6 +20,7 @@ #include <list> #include <cstddef> +#include <atomic> #include <boost/interprocess/managed_external_buffer.hpp> #include <boost/interprocess/allocators/allocator.hpp> diff --git a/Common/Utils/include/CommonUtils/TreeStream.h b/Common/Utils/include/CommonUtils/TreeStream.h index 35ed0616302ef..6882176632d57 100644 --- a/Common/Utils/include/CommonUtils/TreeStream.h +++ b/Common/Utils/include/CommonUtils/TreeStream.h @@ -177,8 +177,9 @@ Int_t TreeStream::CheckIn(T* obj) { // check in arbitrary class having dictionary TClass* pClass = nullptr; - if (obj) - pClass = obj->IsA(); + if (obj) { + pClass = TClass::GetClass(typeid(*obj)); + } if (mCurrentIndex >= static_cast<int>(mElements.size())) { mElements.emplace_back(); diff --git a/Common/Utils/src/CommonUtilsLinkDef.h b/Common/Utils/src/CommonUtilsLinkDef.h index 2dc1ac8ab97ec..921b848ca72e8 100644 --- a/Common/Utils/src/CommonUtilsLinkDef.h +++ b/Common/Utils/src/CommonUtilsLinkDef.h @@ -19,5 +19,8 @@ #pragma link C++ class o2::utils::RootChain + ; #pragma link C++ class o2::utils::RngHelper; #pragma link C++ class o2::utils::MemFileHelper + ; +#pragma link C++ class o2::utils::RootSerializableKeyValueStore::SerializedInfo + ; +#pragma link C++ class map < string, o2::utils::RootSerializableKeyValueStore::SerializedInfo*> + ; +#pragma link C++ class o2::utils::RootSerializableKeyValueStore + ; #endif diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index 0f8580360f688..73d32880ac5f0 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -20,6 +20,7 @@ #include <boost/lexical_cast.hpp> #include <algorithm> #include <array> +#include <limits> #ifdef NDEBUG #undef NDEBUG #endif @@ -747,6 +748,38 @@ bool ConvertAndCopy<std::string>(std::string const& valuestring, void* targetadd } return false; } +// special version for char and unsigned char since we are interested in the numeric +// meaning of char as an 8-bit integer (boost lexical cast is assigning the string as a character i// nterpretation +template <> +bool ConvertAndCopy<char>(std::string const& valuestring, void* targetaddr) +{ + int intvalue = boost::lexical_cast<int>(valuestring); + if (intvalue > std::numeric_limits<char>::max() || intvalue < std::numeric_limits<char>::min()) { + LOG(ERROR) << "Cannot assign " << valuestring << " to a char variable"; + return false; + } + char addr = intvalue; + if (isMemblockDifferent<char>(targetaddr, (void*)&addr)) { + std::memcpy(targetaddr, (void*)&addr, sizeof(char)); + return true; + } + return false; +} +template <> +bool ConvertAndCopy<unsigned char>(std::string const& valuestring, void* targetaddr) +{ + unsigned int intvalue = boost::lexical_cast<int>(valuestring); + if (intvalue > std::numeric_limits<unsigned char>::max() || intvalue < std::numeric_limits<unsigned char>::min()) { + LOG(ERROR) << "Cannot assign " << valuestring << " to an unsigned char variable"; + return false; + } + unsigned char addr = intvalue; + if (isMemblockDifferent<unsigned char>(targetaddr, (void*)&addr)) { + std::memcpy(targetaddr, (void*)&addr, sizeof(unsigned char)); + return true; + } + return false; +} bool ConfigurableParam::updateThroughStorageMapWithConversion(std::string const& key, std::string const& valuestring) { diff --git a/Common/Utils/src/ConfigurableParamHelper.cxx b/Common/Utils/src/ConfigurableParamHelper.cxx index 5fcba03b305e5..f8fe1d16f570a 100644 --- a/Common/Utils/src/ConfigurableParamHelper.cxx +++ b/Common/Utils/src/ConfigurableParamHelper.cxx @@ -83,18 +83,18 @@ void loopOverMembers(TClass* cl, void* obj, } if (dm->IsaPointer()) { - LOG(WARNING) << "Pointer types not supported in ConfigurableParams"; + LOG(WARNING) << "Pointer types not supported in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName(); continue; } if (!dm->IsBasic() && !isValidComplex()) { - LOG(WARNING) << "Generic complex types not supported in ConfigurableParams"; + LOG(WARNING) << "Generic complex types not supported in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName(); continue; } const auto dim = dm->GetArrayDim(); // we support very simple vectored data in 1D for now if (dim > 1) { - LOG(WARNING) << "We support at most 1 dimensional arrays in ConfigurableParams"; + LOG(WARNING) << "We support at most 1 dimensional arrays in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName(); continue; } @@ -118,6 +118,24 @@ std::string getName(const TDataMember* dm, int index, int size) return namestream.str(); } +// ---------------------------------------------------------------------- +size_t getSizeOfUnderlyingType(const TDataMember& dm) +{ + auto dt = dm.GetDataType(); + if (dt) { + // if basic built-in type supported by ROOT + return dt->Size(); + } else { + // for now only catch std::string as other supported type + auto tname = dm.GetFullTypeName(); + if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string")) { + return sizeof(std::string); + } + LOG(ERROR) << "ENCOUNTERED AN UNSUPPORTED TYPE " << tname << "IN A CONFIGURABLE PARAMETER"; + } + return 0; +} + // ---------------------------------------------------------------------- const char* asString(TDataMember const& dm, char* pointer) @@ -165,8 +183,7 @@ std::vector<ParamDataMember>* _ParamHelper::getDataMembersImpl(std::string const std::vector<ParamDataMember>* members = new std::vector<ParamDataMember>; auto toDataMember = [&members, obj, mainkey, provmap](const TDataMember* dm, int index, int size) { - auto dt = dm->GetDataType(); - auto TS = dt ? dt->Size() : 0; + auto TS = getSizeOfUnderlyingType(*dm); char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; const std::string name = getName(dm, index, size); const char* value = asString(*dm, pointer); @@ -259,7 +276,7 @@ void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, voi auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); - auto TS = dt ? dt->Size() : 0; + auto TS = getSizeOfUnderlyingType(*dm); char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; localtree.put(name, asString(*dm, pointer)); @@ -318,7 +335,7 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* auto assignifchanged = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); - auto TS = dt ? dt->Size() : 0; + auto TS = getSizeOfUnderlyingType(*dm); char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; @@ -351,7 +368,7 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* if (!isMemblockDifferent(pointerto, pointerfrom, TS)) { updateProv(); // actually copy - std::memcpy(pointerto, pointerfrom, dt->Size()); + std::memcpy(pointerto, pointerfrom, getSizeOfUnderlyingType(*dm)); } }; loopOverMembers(cl, to, assignifchanged); diff --git a/Common/Utils/src/RootSerializableKeyValueStore.cxx b/Common/Utils/src/RootSerializableKeyValueStore.cxx new file mode 100644 index 0000000000000..6a5979bcd052d --- /dev/null +++ b/Common/Utils/src/RootSerializableKeyValueStore.cxx @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/RootSerializableKeyValueStore.h" +#include <iostream> + +using namespace o2::utils; + +void RootSerializableKeyValueStore::print() 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"; + } +} \ No newline at end of file diff --git a/Common/Utils/src/TreeStream.cxx b/Common/Utils/src/TreeStream.cxx index 664f002423b61..24171ff647ad3 100644 --- a/Common/Utils/src/TreeStream.cxx +++ b/Common/Utils/src/TreeStream.cxx @@ -59,16 +59,18 @@ void TreeStream::BuildTree() // Build the Tree int entriesFilled = mTree.GetEntries(); - if (mBranches.size() < mElements.size()) + if (mBranches.size() < mElements.size()) { mBranches.resize(mElements.size()); + } TString name; TBranch* br = nullptr; for (int i = 0; i < static_cast<int>(mElements.size()); i++) { // auto& element = mElements[i]; - if (mBranches[i]) + if (mBranches[i]) { continue; + } name = element.name.data(); if (name.IsNull()) { name = TString::Format("B%d", i); @@ -111,16 +113,19 @@ void TreeStream::Fill() } for (int i = 0; i < entries; i++) { auto& element = mElements[i]; - if (!element.type) + if (!element.type) { continue; + } auto br = mBranches[i]; if (br) { - if (element.type) + if (element.type) { br->SetAddress(element.ptr); + } } } - if (!mStatus) + if (!mStatus) { mTree.Fill(); // fill only in case of non conflicts + } mStatus = 0; } @@ -129,8 +134,9 @@ TreeStream& TreeStream::Endl() { // Perform pseudo endl operation - if (mTree.GetNbranches() == 0) + if (mTree.GetNbranches() == 0) { BuildTree(); + } Fill(); mStatus = 0; mCurrentIndex = 0; @@ -147,14 +153,16 @@ TreeStream& TreeStream::operator<<(const Char_t* name) } // // if tree was already defined ignore - if (mTree.GetEntries() > 0) + if (mTree.GetEntries() > 0) { return *this; + } // check branch name if tree was not // Int_t last = 0; for (last = 0;; last++) { - if (name[last] == 0) + if (name[last] == 0) { break; + } } if (last > 0 && name[last - 1] == '=') { diff --git a/Common/Utils/src/TreeStreamRedirector.cxx b/Common/Utils/src/TreeStreamRedirector.cxx index 0fa91c7f8d2ad..6218c0998ce3d 100644 --- a/Common/Utils/src/TreeStreamRedirector.cxx +++ b/Common/Utils/src/TreeStreamRedirector.cxx @@ -76,8 +76,9 @@ TreeStream& TreeStreamRedirector::operator<<(Int_t id) mDataLayouts.emplace_back(std::unique_ptr<TreeStream>(new TreeStream(Form("Tree%d", id)))); auto layout = mDataLayouts.back().get(); layout->setID(id); - if (backup) + if (backup) { backup->cd(); + } return *layout; } @@ -88,8 +89,9 @@ TreeStream& TreeStreamRedirector::operator<<(const char* name) // if not existing - creates new for (auto& layout : mDataLayouts) { - if (!std::strcmp(layout->getName(), name)) + if (!std::strcmp(layout->getName(), name)) { return *layout.get(); + } } // create new @@ -98,8 +100,9 @@ TreeStream& TreeStreamRedirector::operator<<(const char* name) mDataLayouts.emplace_back(std::unique_ptr<TreeStream>(new TreeStream(name))); auto layout = mDataLayouts.back().get(); layout->setID(-1); - if (backup) + if (backup) { backup->cd(); + } return *layout; } @@ -114,8 +117,9 @@ void TreeStreamRedirector::Close() layout->getTree().Write(layout->getName()); } mDataLayouts.clear(); - if (backup) + if (backup) { backup->cd(); + } if (mOwnDirectory) { mDirectory->Close(); @@ -143,8 +147,9 @@ void TreeStreamRedirector::FixLeafNameBug(TTree* tree) // After the fix unit test code with pairs of sprse friend trees worked properly // Side effects of fix: // - if (!tree) + if (!tree) { return; + } TObjArray* brArray = tree->GetListOfBranches(); TObjArray* lArray = tree->GetListOfLeaves(); for (int i = 0; i < brArray->GetLast(); i++) { diff --git a/Common/Utils/test/testRootSerializableKeyValueStore.cxx b/Common/Utils/test/testRootSerializableKeyValueStore.cxx new file mode 100644 index 0000000000000..3629a9e360db0 --- /dev/null +++ b/Common/Utils/test/testRootSerializableKeyValueStore.cxx @@ -0,0 +1,118 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RootSerKeyValueStore +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "CommonUtils/RootSerializableKeyValueStore.h" +#include <TMemFile.h> +#include <TH1F.h> +#include <vector> +#include <iostream> +#include <TClass.h> + +using namespace o2; +using namespace o2::utils; + +BOOST_AUTO_TEST_CASE(write_read_test) +{ + RootSerializableKeyValueStore s; + + // put POD some stuff + double x = 1.1; + s.put("x", x); + s.put("i", 110); + + // put some complex classes (need dictionary) + std::string str = "hello"; + s.put("str", str); + + // this should fail compiling: + // const char* text = "foo"; + // s.put("cstr", text); + + TH1F h1("th1name", "th1name", 100, 0, 99); + h1.FillRandom("gaus", 10000); + s.put("h1", h1); + + // check basic assumption that typeinfo name is unique for basic types + BOOST_CHECK(strcmp(std::type_index(typeid(double*)).name(), "Pd") == 0); + BOOST_CHECK(strcmp(std::type_index(typeid(int)).name(), "i") == 0); + BOOST_CHECK(strcmp(std::type_index(typeid(unsigned int)).name(), "j") == 0); + BOOST_CHECK(strcmp(std::type_index(typeid(char)).name(), "c") == 0); + BOOST_CHECK(strcmp(std::type_index(typeid(char*)).name(), "Pc") == 0); + BOOST_CHECK(strcmp(std::type_index(typeid(unsigned char)).name(), "h") == 0); + + // check assumption that for more complicated types the TClass name is unique + // (the std::type_index is not standardized) + BOOST_CHECK(strcmp(TClass::GetClass(typeid(std::vector<double>))->GetName(), "vector<double>") == 0); + + // retrieve + BOOST_CHECK(s.get<std::string>("str")->compare(str) == 0); + BOOST_CHECK(*(s.get<double>("x")) == x); + BOOST_CHECK(s.has("x")); + BOOST_CHECK(!s.has("x_does_not_exist")); + + // retrieve with state/error information + using ErrorState = RootSerializableKeyValueStore::GetState; + ErrorState state; + { + auto r1 = s.get<std::string>("str", state); + BOOST_CHECK(state == ErrorState::kOK); + auto returnedstring = s.getRef<std::string>("str", state); + BOOST_CHECK(state == ErrorState::kOK); + BOOST_CHECK(returnedstring.compare(str) == 0); + + auto r2 = s.get<int>("str", state); + BOOST_CHECK(r2 == nullptr); + BOOST_CHECK(state == ErrorState::kWRONGTYPE); + auto r3 = s.get<int>("str2", state); + BOOST_CHECK(state == ErrorState::kNOSUCHKEY); + BOOST_CHECK(r3 == nullptr); + + auto r4 = s.get<TH1F>("non-existend-histogram", state); + BOOST_CHECK(state == ErrorState::kNOSUCHKEY); + } + + // put something twice + s.put<int>("twice", 10); + s.put<int>("twice", 7); + BOOST_CHECK(*(s.get<int>("twice")) == 7); + + std::cerr << "TESTING FILE IO\n"; + TMemFile f("tmp.root", "RECREATE"); + f.WriteObject(&s, "map"); + + RootSerializableKeyValueStore* s2 = nullptr; + f.GetObject("map", s2); + BOOST_CHECK(s2 != nullptr); + if (s2) { + auto t1 = s2->get<double>("x"); + BOOST_CHECK(t1 != nullptr); + BOOST_CHECK(t1 && *(t1) == x); + + auto i1 = s2->get<int>("i"); + BOOST_CHECK(i1 != nullptr); + BOOST_CHECK(i1 && *(i1) == 110); + + auto t2 = s2->get<std::string>("str"); + BOOST_CHECK(t2 && t2->compare(str) == 0); + + auto t3 = s2->get<std::string>("str"); + BOOST_CHECK(t3 && t3->compare(str) == 0); + + auto histo = s2->get<TH1F>("h1"); + BOOST_CHECK(histo); + + s2->print(); + } + f.Close(); +} diff --git a/Common/Utils/test/testTreeStream.cxx b/Common/Utils/test/testTreeStream.cxx index a401e2530baa4..d9a857ff091f6 100644 --- a/Common/Utils/test/testTreeStream.cxx +++ b/Common/Utils/test/testTreeStream.cxx @@ -124,16 +124,19 @@ bool UnitTestSparse(Double_t scale, Int_t testEntries) // Input parameter scale => downscaling of sprse element std::string outFName("testTreeStreamSparse.root"); - if (scale <= 0) + if (scale <= 0) { scale = 1; - if (scale > 1) + } + if (scale > 1) { scale = 1; + } TreeStreamRedirector* pcstream = new TreeStreamRedirector(outFName.data(), "recreate"); for (Int_t ientry = 0; ientry < testEntries; ientry++) { TVectorD vecRandom(200); TVectorD vecZerro(200); // zerro vector - for (Int_t j = 0; j < 200; j++) + for (Int_t j = 0; j < 200; j++) { vecRandom[j] = j + ientry + 0.1 * gRandom->Rndm(); + } Bool_t isSelected = (gRandom->Rndm() < scale); TVectorD* pvecFull = &vecRandom; TVectorD* pvecSparse = isSelected ? &vecRandom : nullptr; @@ -192,12 +195,15 @@ bool UnitTestSparse(Double_t scale, Int_t testEntries) treeSparseSkip0->SetBranchAddress("vec.", &pvecRead); Bool_t readOK = kTRUE; for (Int_t ientry = 0; ientry < testEntries; ientry++) { - if (!pvecRead) + if (!pvecRead) { continue; - if (pvecRead->GetNrows() == 0) + } + if (pvecRead->GetNrows() == 0) { continue; - if (TMath::Abs((*pvecRead)[0] - ientry) > 0.5) + } + if (TMath::Abs((*pvecRead)[0] - ientry) > 0.5) { readOK = kFALSE; + } } printf("#UnitTest:\tTestSparse(%f)\tReadOK\t%d\n", scale, readOK); // diff --git a/DataFormats/CMakeLists.txt b/DataFormats/CMakeLists.txt index 06f4597cdb328..b6b2899c4e56f 100644 --- a/DataFormats/CMakeLists.txt +++ b/DataFormats/CMakeLists.txt @@ -6,4 +6,5 @@ add_subdirectory(MemoryResources) add_subdirectory(simulation) add_subdirectory(TimeFrame) add_subdirectory(Parameters) +add_subdirectory(Calibration) diff --git a/DataFormats/Calibration/CMakeLists.txt b/DataFormats/Calibration/CMakeLists.txt new file mode 100644 index 0000000000000..c63199a6e02b8 --- /dev/null +++ b/DataFormats/Calibration/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(DataFormatsCalibration + SOURCES src/MeanVertexObject.cxx) + +o2_target_root_dictionary(DataFormatsCalibration + HEADERS include/DataFormatsCalibration/MeanVertexObject.h) diff --git a/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h b/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h new file mode 100644 index 0000000000000..245cdcb2fed24 --- /dev/null +++ b/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h @@ -0,0 +1,87 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MEAN_VERTEX_OBJECT_H_ +#define MEAN_VERTEX_OBJECT_H_ + +#include <array> +#include "Rtypes.h" + +namespace o2 +{ +namespace dataformats +{ +class MeanVertexObject +{ + + public: + MeanVertexObject(float x, float y, float z, float sigmax, float sigmay, float sigmaz) + { + mPos[0] = x; + mPos[1] = y; + mPos[2] = z; + mSigma[0] = sigmax; + mSigma[1] = sigmay; + mSigma[2] = sigmaz; + } + MeanVertexObject(std::array<float, 3> pos, std::array<float, 3> sigma) + { + for (int i = 0; i < 3; i++) { + mPos[i] = pos[i]; + mSigma[i] = sigma[i]; + } + } + MeanVertexObject() = default; + ~MeanVertexObject() = default; + MeanVertexObject(const MeanVertexObject& other) = default; + MeanVertexObject(MeanVertexObject&& other) = default; + MeanVertexObject& operator=(MeanVertexObject& other) = default; + MeanVertexObject& operator=(MeanVertexObject&& other) = default; + + void setX(float val) { mPos[0] = val; } + void setY(float val) { mPos[1] = val; } + void setZ(float val) { mPos[2] = val; } + void setPos(std::array<float, 3> val) + { + for (int i = 0; i < 3; i++) { + mPos[i] = val[i]; + } + } + + float getX() const { return mPos[0]; } + float getY() const { return mPos[1]; } + float getZ() const { return mPos[2]; } + const std::array<float, 3>& getPos() const { return mPos; } + + void setSigmaX(float val) { mSigma[0] = val; } + void setSigmaY(float val) { mSigma[1] = val; } + void setSigmaZ(float val) { mSigma[2] = val; } + void setSigma(std::array<float, 3> val) + { + for (int i = 0; i < 3; i++) { + mSigma[i] = val[i]; + } + } + + float getSigmaX() const { return mSigma[0]; } + float getSigmaY() const { return mSigma[1]; } + float getSigmaZ() const { return mSigma[2]; } + const std::array<float, 3>& getSigma() const { return mSigma; } + + private: + std::array<float, 3> mPos; // position of mean vertex + std::array<float, 3> mSigma; // sigma of mean vertex + + ClassDefNV(MeanVertexObject, 1); +}; +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h b/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h new file mode 100644 index 0000000000000..f364ef185a8dc --- /dev/null +++ b/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h @@ -0,0 +1,19 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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++ struct o2::dataformats::MeanVertexObject + ; + +#endif diff --git a/DataFormats/Calibration/src/MeanVertexObject.cxx b/DataFormats/Calibration/src/MeanVertexObject.cxx new file mode 100644 index 0000000000000..c76593ac7908a --- /dev/null +++ b/DataFormats/Calibration/src/MeanVertexObject.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DataFormatsCalibration/MeanVertexObject.h" diff --git a/DataFormats/Detectors/CPV/CMakeLists.txt b/DataFormats/Detectors/CPV/CMakeLists.txt index acd427dca151e..408c28eeea50c 100644 --- a/DataFormats/Detectors/CPV/CMakeLists.txt +++ b/DataFormats/Detectors/CPV/CMakeLists.txt @@ -13,6 +13,7 @@ o2_add_library(DataFormatsCPV src/Digit.cxx src/Cluster.cxx src/TriggerRecord.cxx + src/CTF.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::MathUtils @@ -25,4 +26,5 @@ o2_target_root_dictionary(DataFormatsCPV HEADERS include/DataFormatsCPV/CPVBlockHeader.h include/DataFormatsCPV/Digit.h include/DataFormatsCPV/Cluster.h - include/DataFormatsCPV/TriggerRecord.h) + include/DataFormatsCPV/TriggerRecord.h + include/DataFormatsCPV/CTF.h) diff --git a/DataFormats/Detectors/CPV/include/DataFormatsCPV/CTF.h b/DataFormats/Detectors/CPV/include/DataFormatsCPV/CTF.h new file mode 100644 index 0000000000000..55ed73bd7895a --- /dev/null +++ b/DataFormats/Detectors/CPV/include/DataFormatsCPV/CTF.h @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for CPV CTF data + +#ifndef O2_CPV_CTF_H +#define O2_CPV_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "DataFormatsCPV/Cluster.h" + +namespace o2 +{ +namespace cpv +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nTriggers = 0; /// number of triggers + uint32_t nClusters = 0; /// number of referred cells + uint32_t firstOrbit = 0; /// orbit of 1st trigger + uint16_t firstBC = 0; /// bc of 1st trigger + + ClassDefNV(CTFHeader, 1); +}; + +/// wrapper for the Entropy-encoded triggers and cells of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 7, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { BLC_bcIncTrig, + BLC_orbitIncTrig, + BLC_entriesTrig, + BLC_posX, + BLC_posZ, + BLC_energy, + BLC_status + }; + ClassDefNV(CTF, 1); +}; + +} // namespace cpv +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/CPV/include/DataFormatsCPV/Cluster.h b/DataFormats/Detectors/CPV/include/DataFormatsCPV/Cluster.h index 1aa9908db17dc..433d64353970c 100644 --- a/DataFormats/Detectors/CPV/include/DataFormatsCPV/Cluster.h +++ b/DataFormats/Detectors/CPV/include/DataFormatsCPV/Cluster.h @@ -21,11 +21,27 @@ class Geometry; /// \class Cluster /// \brief Contains CPV cluster parameters +constexpr float kMinX = -72.32; // Minimal coordinate in X direction +constexpr float kStepX = 0.0025; // digitization step in X direction +constexpr float kMinZ = -63.3; // Minimal coordinate in Z direction +constexpr float kStepZ = 0.002; // digitization step in Z direction +constexpr float kStepE = 1.; // Amplitude digitization step + class Cluster { + union CluStatus { + uint8_t mBits; + struct { + uint8_t multiplicity : 5; // Pad multiplicty, bits 0-4 + uint8_t module : 2; // module number, bits 5-6 + uint8_t unfolded : 1; // unfolded bit, bit 7 + }; + }; + public: Cluster() = default; + Cluster(char mult, char mod, char exMax, float x, float z, float e) : mMulDigit(mult), mModule(mod), mNExMax(exMax), mLocalPosX(x), mLocalPosZ(z), mEnergy(e) {} Cluster(const Cluster& clu) = default; ~Cluster() = default; @@ -42,13 +58,6 @@ class Cluster void setEnergy(float e) { mEnergy = e; } float getEnergy() const { return mEnergy; } - void getPosition(float& posX, float& posY, float& posZ) const - { - posX = mPosX; - posX = mPosY; - posZ = mPosZ; - } - void getLocalPosition(float& posX, float& posZ) const { posX = mLocalPosX; @@ -58,24 +67,51 @@ class Cluster // 0: was no unfolging, -1: unfolding failed char getModule() const { return mModule; } // CPV module of a current cluster - int getLabel() const { return mLabel; } //Index in MCContainer entry - void setLabel(int l) { mLabel = l; } - // 0: was no unfolging, -1: unfolding failed void setNExMax(char nmax = 1) { mNExMax = nmax; } char getNExMax() const { return mNExMax; } // Number of maxima found in cluster in unfolding: // 0: was no unfolging, -1: unfolding failed + // raw access for CTF encoding + uint16_t getPackedPosX() const { return uint16_t((mLocalPosX - kMinX) / kStepX); } + void setPackedPosX(uint16_t v) { mLocalPosX = kMinX + kStepX * v; } + + uint16_t getPackedPosZ() const { return uint16_t((mLocalPosZ - kMinZ) / kStepZ); } + void setPackedPosZ(uint16_t v) { mLocalPosZ = kMinZ + kStepZ * v; } + + uint8_t getPackedEnergy() const { return uint8_t(std::min(255, int(mEnergy / kStepE))); } + void setPackedEnergy(uint16_t v) { mEnergy = v * kStepE; } + + uint8_t getPackedClusterStatus() const + { + CluStatus s = {0}; + s.multiplicity = mMulDigit; + s.module = mModule; + s.unfolded = mNExMax > 1; + return s.mBits; + } + void setPackedClusterStatus(uint8_t v) + { + CluStatus s = {v}; + mMulDigit = s.multiplicity; + mModule = s.module; + mNExMax = s.unfolded ? 1 : 2; + } + + void setPacked(uint16_t posX, uint16_t posZ, uint8_t en, uint8_t status) + { + setPackedPosX(posX); + setPackedPosZ(posZ); + setPackedEnergy(en); + setPackedClusterStatus(status); + } + protected: char mMulDigit = 0; ///< Digit nultiplicity char mModule = 0; ///< Module number char mNExMax = -1; ///< number of (Ex-)maxima before unfolding - int mLabel = -1; ///< Ref to entry in MCTruthContainer with list of labels float mLocalPosX = 0.; ///< Center of gravity position in local module coordunates (phi direction) float mLocalPosZ = 0.; ///< Center of gravity position in local module coordunates (z direction) - float mPosX = 0.; ///< Center of gravity position in global coordinates - float mPosY = 0.; ///< Center of gravity position in global coordinates - float mPosZ = 0.; ///< Center of gravity position in global coordinates float mEnergy = 0.; ///< full energy of a cluster ClassDefNV(Cluster, 1); diff --git a/DataFormats/Detectors/CPV/include/DataFormatsCPV/Digit.h b/DataFormats/Detectors/CPV/include/DataFormatsCPV/Digit.h index 7b059e65f1094..8c5f83ea6c964 100644 --- a/DataFormats/Detectors/CPV/include/DataFormatsCPV/Digit.h +++ b/DataFormats/Detectors/CPV/include/DataFormatsCPV/Digit.h @@ -36,7 +36,7 @@ class Digit : public DigitBase /// \brief Main Digit constructor /// \param cell absId of a cell, amplitude energy deposited in a cell, time time measured in cell, label label of a /// particle in case of MC \return constructed Digit - Digit(short cell, float amplitude, int label); + Digit(unsigned short cell, float amplitude, int label); /// \brief Digit constructor from Hit /// \param CPV Hit @@ -57,8 +57,9 @@ class Digit : public DigitBase { if (fabs(getTimeStamp() - other.getTimeStamp()) < kTimeGate) { return getAbsId() < other.getAbsId(); - } else + } else { return getTimeStamp() < other.getTimeStamp(); + } } /// \brief Comparison oparator, based on time and absId @@ -68,8 +69,9 @@ class Digit : public DigitBase { if (fabs(getTimeStamp() - other.getTimeStamp()) <= kTimeGate) { return getAbsId() > other.getAbsId(); - } else + } else { return getTimeStamp() > other.getTimeStamp(); + } } /// \brief Comparison oparator, based on time and absId @@ -91,8 +93,8 @@ class Digit : public DigitBase Digit& operator+=(const Digit& other); // /// \brief Absolute sell id - short getAbsId() const { return mAbsId; } - void setAbsId(short cellId) { mAbsId = cellId; } + unsigned short getAbsId() const { return mAbsId; } + void setAbsId(unsigned short cellId) { mAbsId = cellId; } /// \brief Energy deposited in a cell float getAmplitude() const { return mAmplitude; } @@ -107,9 +109,9 @@ class Digit : public DigitBase private: // friend class boost::serialization::access; - short mAbsId = 0; ///< pad index (absolute pad ID) - int mLabel = -1; ///< Index of the corresponding entry/entries in the MC label array - float mAmplitude = 0; ///< Amplitude + unsigned short mAbsId = 0; ///< pad index (absolute pad ID) + int mLabel = -1; ///< Index of the corresponding entry/entries in the MC label array + float mAmplitude = 0; ///< Amplitude ClassDefNV(Digit, 2); }; diff --git a/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h b/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h new file mode 100644 index 0000000000000..b1d3b4677d30d --- /dev/null +++ b/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h @@ -0,0 +1,63 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CPV_RAWFORMATS_H +#define ALICEO2_CPV_RAWFORMATS_H + +namespace o2 +{ + +namespace cpv +{ + +union PadWord { + uint32_t mDataWord; + struct { + uint32_t charge : 11; ///< Bits 0 - 10 : charge + uint32_t address : 6; ///< Bits 12 - 17 : address (0..47) + uint32_t dilogic : 4; ///< Bits 18 - 21 : dilogic (1..10) + uint32_t row : 6; ///< Bits 22 - 26 : raw (1..24) + uint32_t zero : 1; ///< Bits 27 - 27 : zeroed so we can distinguish it from the EoE + }; +}; + +union EoEWord { + uint32_t mDataWord; + struct { + uint32_t nword : 7; ///< Bits 0 - 6 : word counter (0...47) + uint32_t en : 11; ///< Bits 7 - 17 : event number -- not used + uint32_t dilogic : 4; ///< Bits 18 - 21 : dilogic (1..10) + uint32_t row : 6; ///< Bits 22 - 26 : raw (1..24) + uint32_t checkbit : 1; ///< Bits 27 - 27 : bit 27 is always 1 by definition of EoE + }; +}; + +union SegMarkerWord { + uint32_t mDataWord; + struct { + uint32_t row : 8; ///< Bits 0 - 7 : segment 0,1,2 charge + uint32_t nwords : 12; ///< Bits 8 - 19 : number of words in the segment + uint32_t marker : 12; ///< Bits 20 - 31: ab0 the segment marker word + }; +}; + +union RowMarkerWord { + uint32_t mDataWord; + struct { + uint32_t marker : 16; ///< Bits 0,15); //the marker word + uint32_t nwords : 16; ///< Bits 16 - 31 : number of words written after row marker (digits and EoE) + }; +}; + +} // namespace cpv + +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/CPV/src/CTF.cxx b/DataFormats/Detectors/CPV/src/CTF.cxx new file mode 100644 index 0000000000000..cd082fe17df8e --- /dev/null +++ b/DataFormats/Detectors/CPV/src/CTF.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "DataFormatsCPV/CTF.h" + +using namespace o2::cpv; diff --git a/DataFormats/Detectors/CPV/src/DataFormatsCPVLinkDef.h b/DataFormats/Detectors/CPV/src/DataFormatsCPVLinkDef.h index 7b08dd67ba51f..6313759502591 100644 --- a/DataFormats/Detectors/CPV/src/DataFormatsCPVLinkDef.h +++ b/DataFormats/Detectors/CPV/src/DataFormatsCPVLinkDef.h @@ -22,4 +22,8 @@ #pragma link C++ class std::vector < o2::cpv::Cluster> + ; #pragma link C++ class std::vector < o2::cpv::TriggerRecord> + ; +#pragma link C++ struct o2::cpv::CTFHeader + ; +#pragma link C++ struct o2::cpv::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::cpv::CTFHeader, 7, uint32_t> + ; + #endif diff --git a/DataFormats/Detectors/CPV/src/Digit.cxx b/DataFormats/Detectors/CPV/src/Digit.cxx index a2e211b167fda..b0968d45d6bb9 100644 --- a/DataFormats/Detectors/CPV/src/Digit.cxx +++ b/DataFormats/Detectors/CPV/src/Digit.cxx @@ -17,7 +17,7 @@ using namespace o2::cpv; ClassImp(Digit); -Digit::Digit(short absId, float amplitude, int label) +Digit::Digit(unsigned short absId, float amplitude, int label) : DigitBase(0), mAmplitude(amplitude), mAbsId(absId), mLabel(label) { } diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h index 0d14edec5ac04..fd963354f030d 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h @@ -70,7 +70,8 @@ class DetID static constexpr ID ACO = 15; #ifdef ENABLE_UPGRADES static constexpr ID IT3 = 16; - static constexpr ID Last = IT3; + static constexpr ID TRK = 17; + static constexpr ID Last = TRK; #else static constexpr ID Last = ACO; ///< if extra detectors added, update this !!! #endif @@ -132,19 +133,19 @@ class DetID 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", "ACO", "IT3", nullptr}; + {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "ACO", "IT3", "TRK", nullptr}; #else {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "ACO", nullptr}; #endif // detector names, will be defined in DataSources static constexpr std::array<mask_t, nDetectors> sMasks = ///< detectot masks - {utils::bit2Mask(ITS), utils::bit2Mask(TPC), utils::bit2Mask(TRD), utils::bit2Mask(TOF), utils::bit2Mask(PHS), - utils::bit2Mask(CPV), utils::bit2Mask(EMC), utils::bit2Mask(HMP), utils::bit2Mask(MFT), utils::bit2Mask(MCH), - utils::bit2Mask(MID), utils::bit2Mask(ZDC), utils::bit2Mask(FT0), utils::bit2Mask(FV0), utils::bit2Mask(FDD), - utils::bit2Mask(ACO) + {math_utils::bit2Mask(ITS), math_utils::bit2Mask(TPC), math_utils::bit2Mask(TRD), math_utils::bit2Mask(TOF), math_utils::bit2Mask(PHS), + math_utils::bit2Mask(CPV), math_utils::bit2Mask(EMC), math_utils::bit2Mask(HMP), math_utils::bit2Mask(MFT), math_utils::bit2Mask(MCH), + math_utils::bit2Mask(MID), math_utils::bit2Mask(ZDC), math_utils::bit2Mask(FT0), math_utils::bit2Mask(FV0), math_utils::bit2Mask(FDD), + math_utils::bit2Mask(ACO) #ifdef ENABLE_UPGRADES , - utils::bit2Mask(IT3) + math_utils::bit2Mask(IT3), math_utils::bit2Mask(TRK) #endif }; @@ -155,7 +156,7 @@ class DetID o2h::gDataOriginMID, o2h::gDataOriginZDC, o2h::gDataOriginFT0, o2h::gDataOriginFV0, o2h::gDataOriginFDD, o2h::gDataOriginACO #ifdef ENABLE_UPGRADES , - o2h::gDataOriginIT3 + o2h::gDataOriginIT3, o2h::gDataOriginTRK #endif }; diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetMatrixCache.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetMatrixCache.h index 55246681554e2..38b10f1febcf3 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetMatrixCache.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetMatrixCache.h @@ -16,8 +16,9 @@ #include <array> #include <vector> + +#include "MathUtils/Cartesian.h" #include "DetectorsCommonDataFormats/DetID.h" -#include "MathUtils/Cartesian3D.h" class TGeoHMatrix; @@ -27,7 +28,7 @@ namespace detectors { /// MatrixCache is a vector of cached transform matrices (per sensor) for specific Transformation type -template <typename T = o2::Transform3D> +template <typename T = o2::math_utils::Transform3D> class MatrixCache { // matrices (per sensor) for specific transformation type @@ -41,8 +42,9 @@ class MatrixCache /// set the size of the cache void setSize(int s) { - if (!mCache.size()) + if (!mCache.size()) { mCache.resize(s); + } } /// get the size of the cache @@ -71,8 +73,8 @@ class MatrixCache class DetMatrixCache { public: - typedef o2::Transform3D Mat3D; - typedef o2::Rotation2D Rot2D; + typedef o2::math_utils::Transform3D Mat3D; + typedef o2::math_utils::Rotation2Df_t Rot2D; DetMatrixCache() = default; DetMatrixCache(const o2::detectors::DetID& id) : mDetID(id) {} @@ -99,7 +101,7 @@ class DetMatrixCache // detector derived class must define its implementation for the method to populate the matrix cache, as an // example, see ITS implementation GeometryTGeo. // The method can be called multiple times to init caches for different transformations, i.e. - // with differen mask as o2::utils::bit2Mask(T2L), or bit2Mask(L2G,T2L), but for the consistency + // with differen mask as o2::math_utils::bit2Mask(T2L), or bit2Mask(L2G,T2L), but for the consistency // check the nsens must be always the same. virtual void fillMatrixCache(int mask) = 0; @@ -127,8 +129,8 @@ class DetMatrixCache class DetMatrixCacheIndirect : private DetMatrixCache { public: - typedef o2::Transform3D Mat3D; - typedef o2::Rotation2D Rot2D; + typedef o2::math_utils::Transform3D Mat3D; + typedef o2::math_utils::Rotation2Df_t Rot2D; DetMatrixCacheIndirect() = default; DetMatrixCacheIndirect(const o2::detectors::DetID& id) : DetMatrixCache(id) {} diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h index 3d4d2ae2f392c..1a5c97a25b7e6 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h @@ -68,6 +68,7 @@ struct Metadata { NODATA // no data was provided }; size_t messageLength = 0; + size_t nLiterals = 0; uint8_t coderType = 0; uint8_t streamSize = 0; uint8_t probabilityBits = 0; @@ -76,15 +77,19 @@ struct Metadata { int32_t max = 0; int nDictWords = 0; int nDataWords = 0; + int nLiteralWords = 0; void clear() { min = max = 0; messageLength = 0; + nLiterals = 0; coderType = 0; streamSize = 0; probabilityBits = 0; - nDataWords = nDictWords = 0; + nDictWords = 0; + nDataWords = 0; + nLiteralWords = 0; } ClassDefNV(Metadata, 1); }; @@ -103,13 +108,6 @@ struct Registry { return head + offsFreeStart; } - /// advance the head of the writeable space - void shrinkFreeBlock(size_t sz) - { - offsFreeStart = alignSize(offsFreeStart + sz); - assert(offsFreeStart <= size); - } - /// size in bytes available to fill data size_t getFreeSize() const { @@ -125,15 +123,42 @@ struct Block { Registry* registry = nullptr; //! non-persistent info for in-memory ops int nDict = 0; // dictionary length (if any) - int nStored = 0; // total payload: data + dictionary length + int nData = 0; // length of data + int nLiterals = 0; // length of literals vector (if any) + int nStored = 0; // total length W* payload = nullptr; //[nStored]; - W* getData() { return payload + nDict; } - W* getDict() { return nDict ? payload : nullptr; } - const W* getData() const { return payload + nDict; } - const W* getDict() const { return nDict ? payload : nullptr; } - int getNData() const { return nStored - nDict; } - int getNDict() const { return nDict; } + inline const W* getDict() const { return nDict ? payload : nullptr; } + inline const W* getData() const { return payload ? (payload + nDict) : nullptr; } + inline const W* getLiterals() const { return nLiterals ? (payload + nDict + nData) : nullptr; } + + inline W* getCreatePayload() { return payload ? payload : (registry ? (payload = reinterpret_cast<W*>(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 void setNDict(int _ndict) + { + nDict = _ndict; + nStored += nDict; + } + + inline void setNData(int _ndata) + { + nData = _ndata; + nStored += nData; + } + + inline void setNLiterals(int _nliterals) + { + nLiterals = _nliterals; + nStored += nLiterals; + } + + inline int getNDict() const { return nDict; } + inline int getNData() const { return nData; } + inline int getNLiterals() const { return nLiterals; } + inline int getNStored() const { return nStored; } ~Block() { @@ -145,81 +170,105 @@ struct Block { /// clear itself void clear() { - nDict = nStored = 0; + nDict = 0; + nData = 0; + nLiterals = 0; + nStored = 0; payload = nullptr; } /// estimate free size needed to add new block - static size_t estimateSize(int _ndict, int _ndata) + static size_t estimateSize(int n) { - return alignSize((_ndict + _ndata) * sizeof(W)); + return alignSize(n * sizeof(W)); } // store a dictionary in an empty block void storeDict(int _ndict, const W* _dict) { - if (nDict || nStored) { + if (getNStored() > 0) { throw std::runtime_error("trying to write in occupied block"); } - size_t sz = estimateSize(_ndict, 0); + size_t sz = estimateSize(_ndict); assert(registry); // this method is valid only for flat version, which has a registry assert(sz <= registry->getFreeSize()); assert((_ndict > 0) == (_dict != nullptr)); - nDict = nStored = _ndict; + setNDict(_ndict); if (nDict) { - auto ptr = payload = reinterpret_cast<W*>(registry->getFreeBlockStart()); - memcpy(ptr, _dict, _ndict * sizeof(W)); - ptr += _ndict; + memcpy(getCreateDict(), _dict, _ndict * sizeof(W)); + realignBlock(); } }; // store a dictionary to a block which can either be empty or contain a dict. void storeData(int _ndata, const W* _data) { - if (nStored > nDict) { + if (getNStored() > getNDict()) { throw std::runtime_error("trying to write in occupied block"); } - size_t sz = estimateSize(0, _ndata); + size_t sz = estimateSize(_ndata); assert(registry); // this method is valid only for flat version, which has a registry assert(sz <= registry->getFreeSize()); assert((_ndata > 0) == (_data != nullptr)); - nStored = nDict + _ndata; - if (_ndata) { - auto ptr = payload = reinterpret_cast<W*>(registry->getFreeBlockStart()); - ptr += nDict; - memcpy(ptr, _data, _ndata * sizeof(W)); + setNData(_ndata); + if (nData) { + memcpy(getCreateData(), _data, _ndata * sizeof(W)); + realignBlock(); + } + } + + // store a dictionary to a block which can either be empty or contain a dict. + void storeLiterals(int _nliterals, const W* _literals) + { + if (getNStored() > getNDict() + getNData()) { + throw std::runtime_error("trying to write in occupied block"); + } + + size_t sz = estimateSize(_nliterals); + assert(registry); // this method is valid only for flat version, which has a registry + assert(sz <= registry->getFreeSize()); + // assert((_nliterals > 0) == (_literals != nullptr)); + setNLiterals(_nliterals); + if (nLiterals) { + memcpy(getCreateLiterals(), _literals, _nliterals * sizeof(W)); + realignBlock(); } } // resize block and free up unused buffer space. - void endBlock() + void realignBlock() { - size_t sz = estimateSize(nStored, 0); - registry->shrinkFreeBlock(sz); + size_t sz = estimateSize(getNStored()); + registry->offsFreeStart = (reinterpret_cast<char*>(payload) - registry->head) + sz; } /// store binary blob data (buffer filled from head to tail) - void store(int _ndict, int _ndata, const W* _dict, const W* _data) + void store(int _ndict, int _ndata, int _nliterals, const W* _dict, const W* _data, const W* _literals) { - size_t sz = estimateSize(_ndict, _ndata); + size_t sz = estimateSize(_ndict + _ndata + _nliterals); assert(registry); // this method is valid only for flat version, which has a registry assert(sz <= registry->getFreeSize()); assert((_ndict > 0) == (_dict != nullptr)); assert((_ndata > 0) == (_data != nullptr)); - nStored = _ndict + _ndata; - nDict = _ndict; - if (nStored) { - auto ptr = payload = reinterpret_cast<W*>(registry->getFreeBlockStart()); - if (_dict) { - memcpy(ptr, _dict, _ndict * sizeof(W)); - ptr += _ndict; + // assert(_literals == _data + _nliterals); + setNDict(_ndict); + setNData(_ndata); + setNLiterals(_nliterals); + getCreatePayload(); // do this even for empty block!!! + if (getNStored()) { + payload = reinterpret_cast<W*>(registry->getFreeBlockStart()); + if (getNDict()) { + memcpy(getCreateDict(), _dict, _ndict * sizeof(W)); + } + if (getNData()) { + memcpy(getCreateData(), _data, _ndata * sizeof(W)); } - if (_data) { - memcpy(ptr, _data, _ndata * sizeof(W)); + if (getNLiterals()) { + memcpy(getCreateLiterals(), _literals, _nliterals * sizeof(W)); } } - registry->shrinkFreeBlock(sz); + realignBlock(); } /// relocate to different head position @@ -243,9 +292,22 @@ class EncodedBlocks void setHeader(const H& h) { mHeader = h; } const H& getHeader() const { return mHeader; } H& getHeader() { return mHeader; } + std::shared_ptr<H> cloneHeader() const { return std::shared_ptr<H>(new H(mHeader)); } // for dictionary creation const auto& getMetadata() const { return mMetadata; } + auto& getMetadata(int i) const + { + assert(i < N); + return mMetadata[i]; + } + + auto& getBlock(int i) const + { + assert(i < N); + return mBlocks[i]; + } + void setANSHeader(const ANSHeader& h) { mANSHeader = h; } const ANSHeader& getANSHeader() const { return mANSHeader; } ANSHeader& getANSHeader() { return mANSHeader; } @@ -269,7 +331,7 @@ class EncodedBlocks static auto create(VD& v); /// estimate free size needed to add new block - static size_t estimateBlockSize(int _ndict, int _ndata) { return Block<W>::estimateSize(_ndict, _ndata); } + static size_t estimateBlockSize(int n) { return Block<W>::estimateSize(n); } /// check if empty and valid bool empty() const { return (mRegistry.offsFreeStart == alignSize(sizeof(*this))) && (mRegistry.size >= mRegistry.offsFreeStart); } @@ -312,25 +374,28 @@ class EncodedBlocks /// encode vector src to bloc at provided slot template <typename VE, typename VB> - inline void encode(const VE& src, int slot, uint8_t probabilityBits, Metadata::OptStore opt, VB* buffer = nullptr) + inline void encode(const VE& src, int slot, uint8_t probabilityBits, Metadata::OptStore opt, VB* buffer = nullptr, const void* encoderExt = nullptr) { - encode(&(*src.begin()), &(*src.end()), slot, probabilityBits, opt, buffer); + encode(&(*src.begin()), &(*src.end()), slot, probabilityBits, opt, buffer, encoderExt); } /// encode vector src to bloc at provided slot - template <typename S, typename VB> - void encode(const S* const srcBegin, const S* const srcEnd, int slot, uint8_t probabilityBits, Metadata::OptStore opt, VB* buffer = nullptr); + template <typename S_IT, typename VB> + void encode(const S_IT srcBegin, const S_IT srcEnd, int slot, uint8_t probabilityBits, Metadata::OptStore opt, VB* buffer = nullptr, const void* encoderExt = nullptr); /// decode block at provided slot to destination vector (will be resized as needed) template <typename VD> - void decode(VD& dest, int slot) const; + void decode(VD& dest, int slot, const void* decoderExt = nullptr) const; /// decode block at provided slot to destination pointer, the needed space assumed to be available template <typename D> - void decode(D* dest, int slot) const; + void decode(D* dest, int slot, const void* decoderExt = nullptr) const; + + /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables + static std::vector<char> createDictionaryBlocks(const std::vector<o2::rans::FrequencyTable>& vfreq, const std::vector<Metadata>& prbits); /// print itself - void print() const; + void print(const std::string& prefix = "") const; protected: static_assert(N > 0, "number of encoded blocks < 1"); @@ -394,7 +459,7 @@ void EncodedBlocks<H, N, W>::readFromTree(VD& vec, TTree& tree, const std::strin for (int i = 0; i < N; i++) { Block<W> bl; readTreeBranch(tree, o2::utils::concat_string(name, "_block.", std::to_string(i), "."), bl); - tmp->mBlocks[i].store(bl.getNDict(), bl.getNData(), bl.getDict(), bl.getData()); + tmp->mBlocks[i].store(bl.getNDict(), bl.getNData(), bl.getNLiterals(), bl.getDict(), bl.getData(), bl.getLiterals()); } } @@ -408,6 +473,7 @@ void EncodedBlocks<H, N, W>::appendToTree(TTree& tree, const std::string& name) int compression = mMetadata[i].opt == Metadata::OptStore::ROOTCompression ? 1 : 0; fillTreeBranch(tree, o2::utils::concat_string(name, "_block.", std::to_string(i), "."), const_cast<Block<W>&>(mBlocks[i]), compression); } + tree.SetEntries(tree.GetEntries() + 1); } ///_____________________________________________________________________________ @@ -482,7 +548,7 @@ size_t EncodedBlocks<H, N, W>::estimateSizeFromMetadata() const { size_t sz = alignSize(sizeof(*this)); for (int i = 0; i < N; i++) { - sz += alignSize((mMetadata[i].nDictWords + mMetadata[i].nDataWords) * sizeof(W)); + sz += alignSize((mMetadata[i].nDictWords + mMetadata[i].nDataWords + mMetadata[i].nLiteralWords) * sizeof(W)); } return sz; } @@ -557,7 +623,6 @@ const auto EncodedBlocks<H, N, W>::getImage(const void* newHead) // now fix its pointers // we don't modify newHead, but still need to remove constness for relocation interface relocate(image.mRegistry.head, const_cast<char*>(reinterpret_cast<const char*>(newHead)), reinterpret_cast<char*>(&image)); - image.print(); return std::move(image); } @@ -589,41 +654,68 @@ inline auto EncodedBlocks<H, N, W>::create(VD& v) ///_____________________________________________________________________________ /// print itself template <typename H, int N, typename W> -void EncodedBlocks<H, N, W>::print() const +void EncodedBlocks<H, N, W>::print(const std::string& prefix) const { - LOG(INFO) << "Container " << N << " blocks, size: " << size() << " bytes, unused: " << getFreeSize(); + LOG(INFO) << prefix << "Container of " << N << " blocks, size: " << size() << " bytes, unused: " << getFreeSize(); for (int i = 0; i < N; i++) { - LOG(INFO) << "Block " << i << " NDictWords: " << mBlocks[i].getNDict() << " NDataWords: " << mBlocks[i].getNData(); + LOG(INFO) << "Block " << i << " for " << mMetadata[i].messageLength << " message words |" + << " NDictWords: " << mBlocks[i].getNDict() << " NDataWords: " << mBlocks[i].getNData() + << " NLiteralWords: " << mBlocks[i].getNLiterals(); } } ///_____________________________________________________________________________ template <typename H, int N, typename W> template <typename VD> -inline void EncodedBlocks<H, N, W>::decode(VD& dest, // destination container - int slot) const // slot of the block to decode +inline void EncodedBlocks<H, N, W>::decode(VD& dest, // destination container + int slot, // slot of the block to decode + const void* decoderExt) const // optional externally provided decoder { dest.resize(mMetadata[slot].messageLength); // allocate output buffer - decode(dest.data(), slot); + decode(dest.data(), slot, decoderExt); } ///_____________________________________________________________________________ template <typename H, int N, typename W> template <typename D> -void EncodedBlocks<H, N, W>::decode(D* dest, // destination pointer - int slot) const // slot of the block to decode +void EncodedBlocks<H, N, W>::decode(D* dest, // destination pointer + int slot, // slot of the block to decode + const void* decoderExt) const // optional externally provided decoder { // get references to the right data const auto& block = mBlocks[slot]; const auto& md = mMetadata[slot]; // decode - if (block.getNData()) { + if (block.getNStored()) { if (md.opt == Metadata::OptStore::EENCODE) { - assert(block.getNDict()); // at the moment we expect to have dictionary - o2::rans::SymbolStatistics stats(block.getDict(), block.getDict() + block.getNDict(), md.min, md.max, md.messageLength); - o2::rans::Decoder64<D> decoder(stats, md.probabilityBits); - decoder.process(dest, block.getData() + block.getNData(), md.messageLength); + 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<D>* decoder = reinterpret_cast<const o2::rans::LiteralDecoder64<D>*>(decoderExt); + std::unique_ptr<o2::rans::LiteralDecoder64<D>> decoderLoc; + if (block.getNDict()) { // if dictionaty is saved, prefer it + o2::rans::FrequencyTable frequencies; + frequencies.addFrequencies(block.getDict(), block.getDict() + block.getNDict(), md.min, md.max); + decoderLoc = std::make_unique<o2::rans::LiteralDecoder64<D>>(frequencies, md.probabilityBits); + decoder = decoderLoc.get(); + } else { // verify that decoded corresponds to stored metadata + if (md.min != decoder->getMinSymbol() || md.max != decoder->getMaxSymbol()) { + LOG(ERROR) << "Mismatch between min=" << md.min << "/" << md.max << " symbols in metadata and those in external decoder " + << decoder->getMinSymbol() << "/" << decoder->getMaxSymbol() << " for slot " << slot; + throw std::runtime_error("Mismatch between min/max symbols in metadata and those in external decoder"); + } + } + // load incompressible symbols if they existed + std::vector<D> 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<D>{reinterpret_cast<const D*>(block.getLiterals()), reinterpret_cast<const D*>(block.getLiterals()) + md.nLiterals}; + } + decoder->process(dest, block.getData() + block.getNData(), md.messageLength, literals); } else { // data was stored as is std::memcpy(dest, block.payload, md.messageLength * sizeof(D)); } @@ -632,43 +724,46 @@ void EncodedBlocks<H, N, W>::decode(D* dest, // destination pointer ///_____________________________________________________________________________ template <typename H, int N, typename W> -template <typename S, typename VB> -void EncodedBlocks<H, N, W>::encode(const S* const srcBegin, // begin of source message - const S* const srcEnd, // end of source message +template <typename S_IT, typename VB> +void EncodedBlocks<H, N, W>::encode(const S_IT srcBegin, // iterator begin of source message + const S_IT srcEnd, // iterator end of source message int slot, // slot in encoded data to fill uint8_t probabilityBits, // encoding into Metadata::OptStore opt, // option for data compression - VB* buffer) // optional buffer (vector) providing memory for encoded blocks + VB* buffer, // optional buffer (vector) providing memory for encoded blocks + const void* encoderExt) // optional external encoder { // fill a new block assert(slot == mRegistry.nFilledBlocks); mRegistry.nFilledBlocks++; - using stream_t = typename o2::rans::Encoder64<S>::stream_t; - + using STYP = typename std::iterator_traits<S_IT>::value_type; + using stream_t = typename o2::rans::Encoder64<STYP>::stream_t; + ; + const size_t messageLength = std::distance(srcBegin, srcEnd); // cover three cases: // * empty source message: no entropy coding // * source message to pass through without any entropy coding // * source message where entropy coding should be applied // case 1: empty source message - if (srcBegin == srcEnd) { - mMetadata[slot] = Metadata{0, sizeof(uint64_t), sizeof(stream_t), probabilityBits, Metadata::OptStore::NODATA, 0, 0, 0, 0}; + if (messageLength == 0) { + mMetadata[slot] = Metadata{0, 0, sizeof(uint64_t), sizeof(stream_t), probabilityBits, Metadata::OptStore::NODATA, 0, 0, 0, 0, 0}; return; } static_assert(std::is_same<W, stream_t>()); - std::unique_ptr<rans::SymbolStatistics> stats = nullptr; Metadata md; auto* bl = &mBlocks[slot]; auto* meta = &mMetadata[slot]; // resize underlying buffer of block if necessary and update all pointers. - auto expandStorage = [&](int dictElems, int encodeBufferElems) { - auto szNeed = estimateBlockSize(dictElems, encodeBufferElems); // size in bytes!!! - if (szNeed >= getFreeSize()) { - LOG(INFO) << "Slot " << slot << ": free size: " << getFreeSize() << ", need " << szNeed; + auto expandStorage = [&](int nElems) { + auto eeb = get(bl->registry->head); // extract pointer from the block, as "this" might be invalid + auto szNeed = eeb->estimateBlockSize(nElems); // size in bytes!!! + if (szNeed >= bl->registry->getFreeSize()) { + LOG(INFO) << "Slot " << slot << ": free size: " << bl->registry->getFreeSize() << ", need " << szNeed << " for " << nElems << " words"; if (buffer) { - expand(*buffer, size() + (szNeed - getFreeSize())); + eeb->expand(*buffer, size() + (szNeed - getFreeSize())); meta = &(get(buffer->data())->mMetadata[slot]); bl = &(get(buffer->data())->mBlocks[slot]); // in case of resizing this and any this.xxx becomes invalid } else { @@ -680,39 +775,87 @@ void EncodedBlocks<H, N, W>::encode(const S* const srcBegin, // begin of source // case 3: message where entropy coding should be applied if (opt == Metadata::OptStore::EENCODE) { // build symbol statistics - stats = std::make_unique<rans::SymbolStatistics>(srcBegin, srcEnd); - stats->rescaleToNBits(probabilityBits); - const o2::rans::Encoder64<S> encoder{*stats, probabilityBits}; - const int dictSize = stats->getFrequencyTable().size(); + constexpr size_t SizeEstMargin = 10 * 1024; + const o2::rans::LiteralEncoder64<STYP>* encoder = reinterpret_cast<const o2::rans::LiteralEncoder64<STYP>*>(encoderExt); + std::unique_ptr<o2::rans::LiteralEncoder64<STYP>> encoderLoc; + std::unique_ptr<o2::rans::FrequencyTable> frequencies = nullptr; + int dictSize = 0; + if (!encoder) { // no external encoder provide, create one on spot + frequencies = std::make_unique<o2::rans::FrequencyTable>(); + frequencies->addSamples(srcBegin, srcEnd); + encoderLoc = std::make_unique<o2::rans::LiteralEncoder64<STYP>>(*frequencies, probabilityBits); + encoder = encoderLoc.get(); + dictSize = frequencies->size(); + } // estimate size of encode buffer - int dataSize = rans::calculateMaxBufferSize(stats->getMessageLength(), - stats->getAlphabetRangeBits(), - sizeof(S)); + int dataSize = rans::calculateMaxBufferSize(messageLength, encoder->getAlphabetRangeBits(), sizeof(STYP)); // size in bytes // preliminary expansion of storage based on dict size + estimated size of encode buffer - expandStorage(dictSize, dataSize); + dataSize = SizeEstMargin + dataSize / sizeof(W) + (sizeof(STYP) < sizeof(W)); // size in words of output stream + expandStorage(dictSize + dataSize); //store dictionary first - bl->storeDict(dictSize, stats->getFrequencyTable().data()); + if (dictSize) { + bl->storeDict(dictSize, frequencies->data()); + } + // vector of incompressible literal symbols + std::vector<STYP> literals; // directly encode source message into block buffer. - const auto encodedMessageEnd = encoder.process(bl->getData(), bl->getData() + dataSize, srcBegin, srcEnd); + const auto encodedMessageEnd = encoder->process(bl->getCreateData(), bl->getCreateData() + dataSize, srcBegin, srcEnd, literals); dataSize = encodedMessageEnd - bl->getData(); + bl->setNData(dataSize); + bl->realignBlock(); // update the size claimed by encode message directly inside the block - bl->nStored = bl->nDict + dataSize; - *meta = Metadata{stats->getMessageLength(), sizeof(uint64_t), sizeof(stream_t), probabilityBits, opt, - stats->getMinSymbol(), stats->getMaxSymbol(), dictSize, dataSize}; + // store incompressible symbols if any + + int literalSize = 0; + if (literals.size()) { + literalSize = (literals.size() * sizeof(STYP)) / sizeof(stream_t) + (sizeof(STYP) < sizeof(stream_t)); + expandStorage(literalSize); + bl->storeLiterals(literalSize, reinterpret_cast<const stream_t*>(literals.data())); + } + *meta = Metadata{messageLength, literals.size(), sizeof(uint64_t), sizeof(stream_t), static_cast<uint8_t>(encoder->getProbabilityBits()), opt, + encoder->getMinSymbol(), encoder->getMaxSymbol(), dictSize, dataSize, literalSize}; } else { // store original data w/o EEncoding - const size_t messageLength = (srcEnd - srcBegin); - const size_t szb = messageLength * sizeof(S); - const int dataSize = szb / sizeof(stream_t) + (sizeof(S) < sizeof(stream_t)); + const size_t szb = messageLength * sizeof(STYP); + const int dataSize = szb / sizeof(stream_t) + (sizeof(STYP) < sizeof(stream_t)); // no dictionary needed - expandStorage(0, dataSize); - *meta = Metadata{messageLength, sizeof(uint64_t), sizeof(stream_t), probabilityBits, opt, - 0, 0, 0, dataSize}; - bl->storeData(meta->nDataWords, reinterpret_cast<const W*>(srcBegin)); + expandStorage(dataSize); + *meta = Metadata{messageLength, 0, sizeof(uint64_t), sizeof(stream_t), probabilityBits, opt, 0, 0, 0, dataSize, 0}; + // provided iterator is not necessarily pointer, need to use intermediate vector!!! + std::vector<STYP> vtmp(srcBegin, srcEnd); + bl->storeData(meta->nDataWords, reinterpret_cast<const W*>(vtmp.data())); } // resize block if necessary - bl->endBlock(); +} + +/// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables +template <typename H, int N, typename W> +std::vector<char> EncodedBlocks<H, N, W>::createDictionaryBlocks(const std::vector<o2::rans::FrequencyTable>& vfreq, const std::vector<Metadata>& vmd) +{ + if (vfreq.size() != N) { + throw std::runtime_error("mismatch between the size of frequencies vector and number of blocks"); + } + size_t sz = alignSize(sizeof(EncodedBlocks<H, N, W>)); + for (int ib = 0; ib < N; ib++) { + sz += Block<W>::estimateSize(vfreq[ib].size()); + } + std::vector<char> 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()); + 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! + dictBlocks->mBlocks[ib].realignBlock(); + } else { + dictBlocks->mMetadata[ib].opt = Metadata::OptStore::NONE; + } + dictBlocks->mRegistry.nFilledBlocks++; + } + return std::move(vdict); } } // namespace ctf diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/NameConf.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/NameConf.h index 1431d7d228479..e2eece219ebff 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/NameConf.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/NameConf.h @@ -82,6 +82,9 @@ class NameConf // standard name for digitization configuration output static constexpr std::string_view DIGITIZATIONCONFIGFILE = "o2simdigitizerworkflow_configuration.ini"; + // public standard CTF dictionary + static constexpr std::string_view CTFDICT = "ctf_dictionary"; // hardcoded + // Block for ITS/TPC matching static constexpr std::string_view TPCITS_TracksBranchName = "TPCITS"; ///< name of branch containing output matched tracks static constexpr std::string_view TPCITS_TPCMCTruthBranchName = "MatchTPCMCTruth"; ///< name of branch for output matched tracks TPC MC diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h index 8ae6399a7a065..018e6c4a54054 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h @@ -89,14 +89,15 @@ class SimTraits /*ACO*/ VS{ "ACOHit" } #ifdef ENABLE_UPGRADES , - /*IT3*/ VS{ "ITS3Hit" } + /*IT3*/ VS{ "IT3Hit" }, + /*TRK*/ VS{ "TRKHit" } #endif }; // clang-format on // branches that are related to kinematics and general event information static inline const std::vector<std::string> KINEMATICSBRANCHES = - {"MCTrack", "MCEventHeader", "TrackRefs", "IndexedTrackRefs"}; + {"MCTrack", "MCEventHeader", "TrackRefs"}; ClassDefNV(SimTraits, 1); }; @@ -226,6 +227,11 @@ template <> struct DetIDToHitTypes<o2::detectors::DetID::IT3> { using HitType = o2::itsmft::Hit; }; + +template <> +struct DetIDToHitTypes<o2::detectors::DetID::TRK> { + using HitType = o2::itsmft::Hit; +}; #endif } // namespace detectors diff --git a/DataFormats/Detectors/Common/src/AlignParam.cxx b/DataFormats/Detectors/Common/src/AlignParam.cxx index 633873238941a..6b351549a812e 100644 --- a/DataFormats/Detectors/Common/src/AlignParam.cxx +++ b/DataFormats/Detectors/Common/src/AlignParam.cxx @@ -103,8 +103,8 @@ bool AlignParam::setRotation(const TGeoMatrix& m) double psi, theta, phi; if (!matrixToAngles(rot, psi, theta, phi)) { return false; - setRotation(psi, theta, phi); } + setRotation(psi, theta, phi); } else { mPsi = mTheta = mPhi = 0.; } @@ -190,7 +190,8 @@ bool AlignParam::setLocalParams(const TGeoMatrix& m) m1.Multiply(&gprimeinv); m1.MultiplyLeft(&gprime); - return setLocalParams(m1); + setParams(m1); + return true; } //_____________________________________________________________________________ @@ -317,10 +318,11 @@ int AlignParam::Compare(const TObject* obj) const int level = getLevel(); int level2 = ((AlignParam*)obj)->getLevel(); - if (level == level2) + if (level == level2) { return 0; - else + } else { return ((level > level2) ? 1 : -1); + } } //_____________________________________________________________________________ diff --git a/DataFormats/Detectors/Common/src/DetID.cxx b/DataFormats/Detectors/Common/src/DetID.cxx index 6214caf5c2c0f..1706990322a36 100644 --- a/DataFormats/Detectors/Common/src/DetID.cxx +++ b/DataFormats/Detectors/Common/src/DetID.cxx @@ -30,6 +30,7 @@ constexpr DetID::ID DetID::ITS, DetID::TPC, DetID::TRD, DetID::TOF, DetID::PHS, #ifdef ENABLE_UPGRADES constexpr DetID::ID DetID::IT3; +constexpr DetID::ID DetID::TRK; #endif constexpr int DetID::nDetectors; diff --git a/DataFormats/Detectors/Common/src/DetMatrixCache.cxx b/DataFormats/Detectors/Common/src/DetMatrixCache.cxx index ed17514b2e480..387603bc74160 100644 --- a/DataFormats/Detectors/Common/src/DetMatrixCache.cxx +++ b/DataFormats/Detectors/Common/src/DetMatrixCache.cxx @@ -13,10 +13,9 @@ #include "MathUtils/Utils.h" using namespace o2::detectors; -using namespace o2::utils; -ClassImp(o2::detectors::MatrixCache<o2::Transform3D>); -ClassImp(o2::detectors::MatrixCache<o2::Rotation2D>); +ClassImp(o2::detectors::MatrixCache<o2::math_utils::Transform3D>); +ClassImp(o2::detectors::MatrixCache<o2::math_utils::Rotation2Df_t>); ClassImp(o2::detectors::DetMatrixCache); //_______________________________________________________ diff --git a/DataFormats/Detectors/Common/src/DetectorsCommonDataFormatsLinkDef.h b/DataFormats/Detectors/Common/src/DetectorsCommonDataFormatsLinkDef.h index 1e5ab5fe057ef..019a2bca9499f 100644 --- a/DataFormats/Detectors/Common/src/DetectorsCommonDataFormatsLinkDef.h +++ b/DataFormats/Detectors/Common/src/DetectorsCommonDataFormatsLinkDef.h @@ -16,8 +16,8 @@ #pragma link C++ class o2::detectors::DetID + ; #pragma link C++ class o2::detectors::AlignParam + ; -#pragma link C++ class o2::detectors::MatrixCache < o2::Transform3D> + ; -#pragma link C++ class o2::detectors::MatrixCache < o2::Rotation2D> + ; +#pragma link C++ class o2::detectors::MatrixCache < o2::math_utils::Transform3D> + ; +#pragma link C++ class o2::detectors::MatrixCache < o2::math_utils::Rotation2Df_t> + ; #pragma link C++ class o2::detectors::DetMatrixCache + ; #pragma link C++ class o2::detectors::DetMatrixCacheIndirect + ; diff --git a/DataFormats/Detectors/EMCAL/CMakeLists.txt b/DataFormats/Detectors/EMCAL/CMakeLists.txt index f8b83e4808fac..df6fd86c0205f 100644 --- a/DataFormats/Detectors/EMCAL/CMakeLists.txt +++ b/DataFormats/Detectors/EMCAL/CMakeLists.txt @@ -17,6 +17,7 @@ o2_add_library(DataFormatsEMCAL src/Cell.cxx src/Digit.cxx src/EventHandler.cxx + src/CTF.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::MathUtils @@ -33,7 +34,8 @@ o2_target_root_dictionary(DataFormatsEMCAL include/DataFormatsEMCAL/Cluster.h include/DataFormatsEMCAL/AnalysisCluster.h include/DataFormatsEMCAL/EventHandler.h - include/DataFormatsEMCAL/MCLabel.h) + include/DataFormatsEMCAL/MCLabel.h + include/DataFormatsEMCAL/CTF.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 5cc0936c4721a..eb65775919cba 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h @@ -15,7 +15,7 @@ #include <gsl/span> #include <array> #include "Rtypes.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "TLorentzVector.h" namespace o2 @@ -39,7 +39,7 @@ class AnalysisCluster /// \class CellOutOfRangeException /// \brief Exception handling non-existing cell indices /// \ingroup EMCALbase - class CellOutOfRangeException : public std::exception + class CellOutOfRangeException final : public std::exception { public: /// \brief Constructor, setting cell wrong cell index raising the exception @@ -85,14 +85,14 @@ class AnalysisCluster /// /// Set the cluster global position. - void setGlobalPosition(Point3D<float> x); - Point3D<float> getGlobalPosition() const + void setGlobalPosition(math_utils::Point3D<float> x); + math_utils::Point3D<float> getGlobalPosition() const { return mGlobalPos; } - void setLocalPosition(Point3D<float> x); - Point3D<float> getLocalPosition() const + void setLocalPosition(math_utils::Point3D<float> x); + math_utils::Point3D<float> getLocalPosition() const { return mLocalPos; } @@ -146,18 +146,20 @@ class AnalysisCluster int getCellIndex(int i) const { - if (i >= 0 && i < mNCells) + if (i >= 0 && i < mNCells) { return mCellsIndices[i]; - else + } else { throw CellOutOfRangeException(i); + } } float getCellAmplitudeFraction(int i) const { - if (i >= 0 && i < mNCells) + if (i >= 0 && i < mNCells) { return mCellsAmpFraction[i]; - else + } else { throw CellOutOfRangeException(i); + } } bool getIsExotic() const { return mIsExotic; } @@ -198,14 +200,14 @@ class AnalysisCluster /// here we store what fraction of the cell energy is assigned to a given cluster. std::vector<float> mCellsAmpFraction; //[mNCells][0.,1.,16] - Point3D<float> mGlobalPos; ///< Position in global coordinate system (cm). - Point3D<float> mLocalPos; ///< Local position in the sub-detector coordinate - float mEnergy = 0; ///< Energy measured by calorimeter in GeV. - float mCoreEnergy = 0.; ///< Energy in a shower core - float mDispersion = 0; ///< Cluster shape dispersion. - float mChi2 = 0; ///< Chi2 of cluster fit (unfolded clusters) - float mM20 = 0; ///< 2-nd moment along the second eigen axis. - float mM02 = 0; ///< 2-nd moment along the main eigen axis. + math_utils::Point3D<float> mGlobalPos; ///< Position in global coordinate system (cm). + math_utils::Point3D<float> mLocalPos; ///< Local position in the sub-detector coordinate + float mEnergy = 0; ///< Energy measured by calorimeter in GeV. + float mCoreEnergy = 0.; ///< Energy in a shower core + float mDispersion = 0; ///< Cluster shape dispersion. + float mChi2 = 0; ///< Chi2 of cluster fit (unfolded clusters) + float mM20 = 0; ///< 2-nd moment along the second eigen axis. + float mM02 = 0; ///< 2-nd moment along the main eigen axis. float mEmcCpvDistance = 1024; ///< the distance from PHOS EMC rec.point to the closest CPV rec.point. @@ -228,4 +230,4 @@ class AnalysisCluster } // namespace emcal } // namespace o2 -#endif //ANALYSISCLUSTER_H \ No newline at end of file +#endif //ANALYSISCLUSTER_H diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CTF.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CTF.h new file mode 100644 index 0000000000000..283c98d52bed2 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CTF.h @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for EMC CTF data + +#ifndef O2_EMC_CTF_H +#define O2_EMC_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "DataFormatsEMCAL/TriggerRecord.h" +#include "DataFormatsEMCAL/Cell.h" + +namespace o2 +{ +namespace emcal +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nTriggers = 0; /// number of triggers + uint32_t nCells = 0; /// number of referred cells + uint32_t firstOrbit = 0; /// orbit of 1st trigger + uint16_t firstBC = 0; /// bc of 1st trigger + + ClassDefNV(CTFHeader, 1); +}; + +/// wrapper for the Entropy-encoded triggers and cells of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 7, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { BLC_bcIncTrig, + BLC_orbitIncTrig, + BLC_entriesTrig, + BLC_towerID, + BLC_time, + BLC_energy, + BLC_status + }; + ClassDefNV(CTF, 1); +}; + +} // namespace emcal +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h index 568a069543c2d..a9872a9039bd2 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h @@ -12,7 +12,6 @@ #define ALICEO2_EMCAL_CELL_H_ #include <bitset> -#include "Rtypes.h" #include "DataFormatsEMCAL/Constants.h" namespace o2 @@ -22,62 +21,177 @@ namespace emcal /// \class Cell /// \brief EMCAL compressed cell information +/// \author Anders Knospe, University of Houston +/// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory +/// \since March 6, 2019 /// \ingroup EMCALDataFormat /// -/// Structure: -/// Bits 38-39: Cell type: 00=Low Gain, 01=High Gain, 10=LED mon, 11=TRU -/// Bits 24-37: Energy (input/output in GeV/c^2, resolution 1/16 ADC count) -/// Bits 15-23: Time (ns) -/// Bits 0-14: Tower ID +/// # Base format for EMCAL cell information in the Compressed Timeframe +/// +/// 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 +/// 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: +/// +/// | 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 | +/// +/// The remaining bits are 0 class Cell { public: - Cell() = default; - Cell(Short_t tower, Double_t energy, Double_t time, ChannelType_t ctype = ChannelType_t::LOW_GAIN); + Cell(); + Cell(short tower, float energy, float time, ChannelType_t ctype = ChannelType_t::LOW_GAIN); ~Cell() = default; // override - void setTower(Short_t tower); - Short_t getTower() const; - - void setTimeStamp(Double_t time); - Short_t getTimeStamp() const; - - void setEnergyBits(Short_t ebits); - Short_t getEnergyBits() const; - - void setEnergy(Double_t energy); - Double_t getEnergy() const; + void setTower(short tower) { getDataRepresentation()->mTowerID = tower; } + short getTower() const { return getDataRepresentation()->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); + + /// \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; + + /// \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); + + /// \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; + + /// \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 + 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<uint16_t>(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<ChannelType_t>(getDataRepresentation()->mCellStatus); } + + /// \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; } + + /// \brief Mark cell as low gain cell + void setLowGain() { setType(ChannelType_t::LOW_GAIN); } + + /// \brief Check whether the cell is a low gain cell + /// \return True if the cell type is low gain, false otherwise + Bool_t getLowGain() const { return isChannelType(ChannelType_t::LOW_GAIN); } + + /// \brief Mark cell as high gain cell + void setHighGain() { setType(ChannelType_t::HIGH_GAIN); } + + /// \brief Check whether the cell is a high gain cell + /// \return True if the cell type is high gain, false otherwise + Bool_t getHighGain() const { return isChannelType(ChannelType_t::HIGH_GAIN); }; + + /// \brief Mark cell as LED monitor cell + void setLEDMon() { setType(ChannelType_t::LEDMON); } + + /// \brief Check whether the cell is a LED monitor cell + /// \return True if the cell type is LED monitor, false otherwise + Bool_t getLEDMon() const { return isChannelType(ChannelType_t::LEDMON); } + + /// \brief Mark cell as TRU cell + void setTRU() { setType(ChannelType_t::TRU); } + + /// \brief Check whether the cell is a TRU cell + /// \return True if the cell type is TRU, false otherwise + Bool_t getTRU() const { return isChannelType(ChannelType_t::TRU); } - void setAmplitude(Double_t energy) { setEnergy(energy); } - Double_t getAmplitude() const { return getEnergy(); } - - void setType(ChannelType_t ctype); - ChannelType_t getType() const; - - void setLowGain(); - Bool_t getLowGain() const; - - void setHighGain(); - Bool_t getHighGain() const; - - void setLEDMon(); - Bool_t getLEDMon() const; + void PrintStream(std::ostream& stream) const; - void setTRU(); - Bool_t getTRU() const; + /// used for CTF encoding/decoding: access to packed data + void setPacked(uint16_t tower, uint16_t t, uint16_t en, uint16_t status) + { + auto dt = getDataRepresentation(); + dt->mTowerID = tower; + dt->mTime = t; + dt->mEnergy = en; + dt->mCellStatus = status; + } + + auto getPackedTowerID() const { return getDataRepresentation()->mTowerID; } + auto getPackedTime() const { return getDataRepresentation()->mTime; } + auto getPackedEnergy() const { return getDataRepresentation()->mEnergy; } + auto getPackedCellStatus() const { return getDataRepresentation()->mCellStatus; } - void setLong(ULong_t l); - ULong_t getLong() const { return mBits.to_ulong(); } + 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 + }; - void PrintStream(std::ostream& stream) const; + CellData* getDataRepresentation() { return reinterpret_cast<CellData*>(mCellWords); } + const CellData* getDataRepresentation() const { return reinterpret_cast<const CellData*>(mCellWords); } - private: - std::bitset<40> mBits; + uint16_t mCellWords[3]; ///< data word ClassDefNV(Cell, 1); }; -std::ostream& operator<<(std::ostream& stream, const Cell& c); +/// \brief Stream operator for EMCAL cell +/// \param stream Stream where to print the EMCAL cell +/// \param cell Cell to be printed +/// \return Stream after printing +std::ostream& operator<<(std::ostream& stream, const Cell& cell); } // namespace emcal } // namespace o2 diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h index 1666568bd2dc0..b538c3a34fd58 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h @@ -38,7 +38,7 @@ enum ChannelType_t { /// \class InvalidChanneltypeException /// \brief Error handling invalid channel types -class InvalidChanneltypeException : public std::exception +class InvalidChanneltypeException final : public std::exception { public: /// \brief Constructor initializing the exception @@ -101,8 +101,8 @@ constexpr Int_t EMCAL_MAXTIMEBINS = 15; ///< Maximum number of time bins fo enum FitAlgorithm { Standard = 0, ///< Standard raw fitter - NeuralNet = 1, ///< Neural net raw fitter - FastFit = 2, ///< Fast raw fitter (Martin) + Gamma2 = 1, ///< Gamma2 raw fitter + NeuralNet = 2, ///< Neural net raw fitter NONE = 3 }; diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h index c2c55b1e3f764..f510f48915705 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h @@ -88,7 +88,7 @@ class EventHandler /// \class RangeException /// \brief Exception handling errors due to exceeding the range of triggers handled by the handler - class RangeException : public std::exception + class RangeException final : public std::exception { public: /// \brief Constructor defining the error @@ -125,7 +125,7 @@ class EventHandler /// \class NotInitializedException /// \brief Exception handling unitialized event handler - class NotInitializedException : public std::exception + class NotInitializedException final : public std::exception { public: /// \brief Constructor initializing the exception @@ -141,7 +141,7 @@ class EventHandler /// \class InteractionRecordInvalidException /// \brief Error handling in case the interaction records from various sources do not match - class InteractionRecordInvalidException : public std::exception + class InteractionRecordInvalidException final : public std::exception { public: /// \brief Constructor initializing the exception diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h index e2656f6bcca71..6f3c59dcf5f85 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h @@ -11,6 +11,7 @@ #ifndef ALICEO2_EMCAL_TRIGGERRECORD_H #define ALICEO2_EMCAL_TRIGGERRECORD_H +#include <cstdint> #include <iosfwd> #include "Rtypes.h" #include "CommonDataFormat/InteractionRecord.h" @@ -35,26 +36,30 @@ class TriggerRecord public: TriggerRecord() = default; - TriggerRecord(const BCData& bunchcrossing, int firstentry, int nentries) : mBCData(bunchcrossing), mDataRange(firstentry, nentries) {} + TriggerRecord(const BCData& bunchcrossing, int firstentry, int nentries) : mBCData(bunchcrossing), mDataRange(firstentry, nentries), mTriggerBits(0) {} + TriggerRecord(const BCData& bunchcrossing, uint32_t triggerbits, int firstentry, int nentries) : TriggerRecord(bunchcrossing, firstentry, nentries) { mTriggerBits = triggerbits; } ~TriggerRecord() = default; void setBCData(const BCData& data) { mBCData = data; } + void setTriggerBits(uint32_t triggerbits) { mTriggerBits = triggerbits; } void setDataRange(int firstentry, int nentries) { mDataRange.set(firstentry, nentries); } void setIndexFirstObject(int firstentry) { mDataRange.setFirstEntry(firstentry); } void setNumberOfObjects(int nentries) { mDataRange.setEntries(nentries); } const BCData& getBCData() const { return mBCData; } BCData& getBCData() { return mBCData; } + uint32_t getTriggerBits() const { return mTriggerBits; } int getNumberOfObjects() const { return mDataRange.getEntries(); } int getFirstEntry() const { return mDataRange.getFirstEntry(); } void printStream(std::ostream& stream) const; private: - BCData mBCData; /// Bunch crossing data of the trigger - DataRange mDataRange; /// Index of the triggering event (event index and first entry in the container) + BCData mBCData; /// Bunch crossing data of the trigger + DataRange mDataRange; /// Index of the triggering event (event index and first entry in the container) + uint32_t mTriggerBits; /// Trigger bits as from the Raw Data Header - ClassDefNV(TriggerRecord, 1); + ClassDefNV(TriggerRecord, 2); }; std::ostream& operator<<(std::ostream& stream, const TriggerRecord& trg); @@ -63,4 +68,4 @@ std::ostream& operator<<(std::ostream& stream, const TriggerRecord& trg); } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/DataFormats/Detectors/EMCAL/src/AnalysisCluster.cxx b/DataFormats/Detectors/EMCAL/src/AnalysisCluster.cxx index f905e8495ffdd..c106ad0ab646d 100644 --- a/DataFormats/Detectors/EMCAL/src/AnalysisCluster.cxx +++ b/DataFormats/Detectors/EMCAL/src/AnalysisCluster.cxx @@ -40,16 +40,17 @@ TLorentzVector AnalysisCluster::getMomentum(std::array<const float, 3> vertex) c float r = TMath::Sqrt(pos[0] * pos[0] + pos[1] * pos[1] + pos[2] * pos[2]); - if (r > 0) + if (r > 0) { p.SetPxPyPzE(mEnergy * pos[0] / r, mEnergy * pos[1] / r, mEnergy * pos[2] / r, mEnergy); - else + } else { LOG(INFO) << "Null cluster radius, momentum calculation not possible"; + } return p; } //______________________________________________________________________________ -void AnalysisCluster::setGlobalPosition(Point3D<float> x) +void AnalysisCluster::setGlobalPosition(math_utils::Point3D<float> x) { mGlobalPos.SetX(x.X()); mGlobalPos.SetY(x.Y()); @@ -57,9 +58,9 @@ void AnalysisCluster::setGlobalPosition(Point3D<float> x) } //______________________________________________________________________________ -void AnalysisCluster::setLocalPosition(Point3D<float> x) +void AnalysisCluster::setLocalPosition(math_utils::Point3D<float> x) { mLocalPos.SetX(x.X()); mLocalPos.SetY(x.Y()); mLocalPos.SetZ(x.Z()); -} \ No newline at end of file +} diff --git a/DataFormats/Detectors/EMCAL/src/CTF.cxx b/DataFormats/Detectors/EMCAL/src/CTF.cxx new file mode 100644 index 0000000000000..1f9b440e65ec2 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/CTF.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "DataFormatsEMCAL/CTF.h" + +using namespace o2::emcal; diff --git a/DataFormats/Detectors/EMCAL/src/Cell.cxx b/DataFormats/Detectors/EMCAL/src/Cell.cxx index 069c23e0e70d0..b02533111c925 100644 --- a/DataFormats/Detectors/EMCAL/src/Cell.cxx +++ b/DataFormats/Detectors/EMCAL/src/Cell.cxx @@ -15,184 +15,63 @@ using namespace o2::emcal; -Cell::Cell(Short_t tower, Double_t energy, Double_t time, ChannelType_t ctype) -{ - setTower(tower); - setTimeStamp(time); - setEnergy(energy); - setType(ctype); -} - -void Cell::setTower(Short_t tower) -{ - if (tower > 0x7fff || tower < 0) - tower = 0x7fff; - ULong_t t = (ULong_t)tower; - - ULong_t b = getLong() & 0xffffff8000; // 1111111111111111111111111000000000000000 - mBits = b + t; -} +const float TIME_SHIFT = 600., + TIME_RANGE = 1500., + TIME_RESOLUTION = TIME_RANGE / 2047., + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION = ENERGY_TRUNCATION / 16383.; -Short_t Cell::getTower() const +Cell::Cell() { - ULong_t t = getLong(); - t &= 0x7fff; - return ((Short_t)t); -} - -void Cell::setTimeStamp(Double_t time) -{ - ULong_t t = 0; - if (time > 0x1ff) - t = 0x1ff; - else if (time < 0) - t = 0; - else - t = (ULong_t)time; - - t <<= 15; - ULong_t b = getLong() & 0xffff007fff; // 1111111111111111000000000111111111111111 - mBits = b + t; + memset(mCellWords, 0, sizeof(uint16_t) * 3); } -Short_t Cell::getTimeStamp() const +Cell::Cell(short tower, float energy, float time, ChannelType_t ctype) { - ULong_t t = getLong(); - t >>= 15; - t &= 0x1ff; - return ((Short_t)t); -} - -void Cell::setEnergyBits(Short_t ebits) -{ - if (ebits > 0x3fff) - ebits = 0x3fff; - else if (ebits < 0) - ebits = 0; - ULong_t a = (ULong_t)ebits; - - a <<= 24; - ULong_t b = getLong() & 0xc00ffffff; // 1100000000000000111111111111111111111111 - mBits = b + a; -} - -Short_t Cell::getEnergyBits() const -{ - ULong_t a = getLong(); - a >>= 24; - a &= 0x3fff; - return ((Short_t)a); -} - -void Cell::setEnergy(Double_t energy) -{ - ULong_t a = static_cast<ULong_t>(energy / 0.0153); - a = a & 0x3FFF; - - a <<= 24; - ULong_t b = getLong() & 0xc000ffffff; // 1100000000000000111111111111111111111111 - mBits = b + a; -} - -Double_t Cell::getEnergy() const -{ - return double(getEnergyBits() * 0.0153); -} - -void Cell::setType(ChannelType_t ctype) -{ - switch (ctype) { - case ChannelType_t::HIGH_GAIN: - setHighGain(); - break; - case ChannelType_t::LOW_GAIN: - setLowGain(); - break; - case ChannelType_t::TRU: - setTRU(); - break; - case ChannelType_t::LEDMON: - setHighGain(); - break; - }; -} - -ChannelType_t Cell::getType() const -{ - if (getHighGain()) - return ChannelType_t::HIGH_GAIN; - else if (getLEDMon()) - return ChannelType_t::LEDMON; - else if (getTRU()) - return ChannelType_t::TRU; - return ChannelType_t::LOW_GAIN; -} - -void Cell::setLowGain() -{ - std::bitset<40> b(0x3fffffffff); // 0011111111111111111111111111111111111111 - mBits = (mBits & b); -} - -Bool_t Cell::getLowGain() const -{ - ULong_t t = (getLong() >> 38); - if (t) - return false; - return true; -} - -void Cell::setHighGain() -{ - ULong_t b = getLong() & 0x3fffffffff; // 0011111111111111111111111111111111111111 - mBits = b + 0x4000000000; // 0100000000000000000000000000000000000000 -} - -Bool_t Cell::getHighGain() const -{ - ULong_t t = (getLong() >> 38); - if (t == 1) - return true; - return false; -} - -void Cell::setLEDMon() -{ - ULong_t b = getLong() & 0x3fffffffff; // 0011111111111111111111111111111111111111 - mBits = b + 0x8000000000; // 1000000000000000000000000000000000000000 + memset(mCellWords, 0, sizeof(uint16_t) * 3); + setTower(tower); + setTimeStamp(time); + setEnergy(energy); + setType(ctype); } -Bool_t Cell::getLEDMon() const +void Cell::setTimeStamp(float timestamp) { - ULong_t t = (getLong() >> 38); - if (t == 2) - return true; - return false; + // 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; + } + getDataRepresentation()->mTime = static_cast<uint16_t>((timestamp + TIME_SHIFT) / TIME_RESOLUTION); } -void Cell::setTRU() +float Cell::getTimeStamp() const { - ULong_t b = getLong() & 0x3fffffffff; // 0011111111111111111111111111111111111111 - mBits = b + 0xc000000000; // 1100000000000000000000000000000000000000 + return (static_cast<float>(getDataRepresentation()->mTime) * TIME_RESOLUTION) - TIME_SHIFT; } -Bool_t Cell::getTRU() const +void Cell::setEnergy(float energy) { - ULong_t t = (getLong() >> 38); - if (t == 3) - return true; - return false; + double truncatedEnergy = energy; + if (truncatedEnergy < 0.) { + truncatedEnergy = 0.; + } else if (truncatedEnergy > ENERGY_TRUNCATION) { + truncatedEnergy = ENERGY_TRUNCATION; + } + getDataRepresentation()->mEnergy = static_cast<int16_t>(truncatedEnergy / ENERGY_RESOLUTION); } -void Cell::setLong(ULong_t l) +float Cell::getEnergy() const { - std::bitset<40> b(l); - mBits = b; + return static_cast<float>(getDataRepresentation()->mEnergy) * ENERGY_RESOLUTION; } void Cell::PrintStream(std::ostream& stream) const { - stream << "EMCAL Cell: Type " << getType() << ", Energy " << getEnergy() << ", Time " << getTimeStamp() << ", Tower " << getTower() << ", Bits " << mBits; + stream << "EMCAL Cell: Type " << getType() << ", Energy " << getEnergy() << ", Time " << getTimeStamp() << ", Tower " << getTower(); } std::ostream& operator<<(std::ostream& stream, const Cell& c) diff --git a/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h b/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h index 1f1161c95a798..5f9bdd0c321cd 100644 --- a/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h +++ b/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h @@ -41,4 +41,8 @@ #pragma link C++ class o2::emcal::EventHandler < o2::emcal::Cell> + ; #pragma link C++ class o2::emcal::EventHandler < o2::emcal::Digit> + ; +#pragma link C++ struct o2::emcal::CTFHeader + ; +#pragma link C++ struct o2::emcal::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::emcal::CTFHeader, 7, uint32_t> + ; + #endif diff --git a/DataFormats/Detectors/EMCAL/src/Digit.cxx b/DataFormats/Detectors/EMCAL/src/Digit.cxx index 7036d03cd6da7..e25d5082b1d8f 100644 --- a/DataFormats/Detectors/EMCAL/src/Digit.cxx +++ b/DataFormats/Detectors/EMCAL/src/Digit.cxx @@ -25,14 +25,15 @@ Digit& Digit::operator+=(const Digit& other) Int_t a = getAmplitudeADC() + other.getAmplitudeADC(); Short_t s = (Short_t)(a); - if (a < -0x8000) + if (a < -0x8000) { setAmplitudeADC(-0x8000); - else if (a <= constants::EMCAL_HGLGTRANSITION) + } else if (a <= constants::EMCAL_HGLGTRANSITION) { setAmplitudeADC(s); - else if (a <= 0x7fff * constants::EMCAL_HGLGFACTOR) + } else if (a <= 0x7fff * constants::EMCAL_HGLGFACTOR) { setAmplitudeADC(s / constants::EMCAL_HGLGFACTOR, ChannelType_t::LOW_GAIN); - else + } else { setAmplitudeADC(0x7fff, ChannelType_t::LOW_GAIN); + } } // Does nothing if the digits are in different towers or have incompatible times. return *this; @@ -66,15 +67,17 @@ Int_t Digit::getAmplitudeADC(ChannelType_t ctype) const { if (ctype == ChannelType_t::LOW_GAIN) { // return in units of Low-Gain ADC counts - if (mChannelType == ChannelType_t::LOW_GAIN) + if (mChannelType == ChannelType_t::LOW_GAIN) { return (Int_t)(mAmplitude); - else + } else { return mAmplitude / constants::EMCAL_HGLGFACTOR; + } } // return in units of High-Gain ADC counts (default) - if (mChannelType == ChannelType_t::LOW_GAIN) + if (mChannelType == ChannelType_t::LOW_GAIN) { return mAmplitude * constants::EMCAL_HGLGFACTOR; + } return (Int_t)(mAmplitude); } diff --git a/DataFormats/Detectors/EMCAL/src/EventHandler.cxx b/DataFormats/Detectors/EMCAL/src/EventHandler.cxx index 69c9a7ff53f6f..ab3f809f5ff73 100644 --- a/DataFormats/Detectors/EMCAL/src/EventHandler.cxx +++ b/DataFormats/Detectors/EMCAL/src/EventHandler.cxx @@ -41,12 +41,13 @@ int EventHandler<CellInputType>::getNumberOfEvents() const { int neventsClusters = mTriggerRecordsClusters.size(), neventsCells = mTriggerRecordsCells.size(); - if (neventsClusters) + if (neventsClusters) { return neventsClusters; - else if (neventsCells) + } else if (neventsCells) { return neventsCells; - else + } else { return 0; + } } template <class CellInputType> @@ -54,20 +55,23 @@ const o2::InteractionRecord& EventHandler<CellInputType>::getInteractionRecordFo { const InteractionRecord *irClusters(nullptr), *irCells(nullptr); if (mTriggerRecordsClusters.size()) { - if (eventID >= mTriggerRecordsClusters.size()) + if (eventID >= mTriggerRecordsClusters.size()) { throw RangeException(eventID, mTriggerRecordsClusters.size()); + } irClusters = &(mTriggerRecordsClusters[eventID].getBCData()); } if (mTriggerRecordsCells.size()) { - if (eventID >= mTriggerRecordsCells.size()) + if (eventID >= mTriggerRecordsCells.size()) { throw RangeException(eventID, mTriggerRecordsCells.size()); + } irCells = &(mTriggerRecordsCellIndices[eventID].getBCData()); } if (irClusters && irCells) { - if (compareInteractionRecords(*irClusters, *irCells)) + if (compareInteractionRecords(*irClusters, *irCells)) { return *irClusters; - else + } else { throw InteractionRecordInvalidException(*irClusters, *irCells); + } } else if (irClusters) { return *irClusters; } else if (irCells) { @@ -80,8 +84,9 @@ template <class CellInputType> const typename EventHandler<CellInputType>::ClusterRange EventHandler<CellInputType>::getClustersForEvent(int eventID) const { if (mTriggerRecordsClusters.size()) { - if (eventID >= mTriggerRecordsClusters.size()) + if (eventID >= mTriggerRecordsClusters.size()) { throw RangeException(eventID, mTriggerRecordsClusters.size()); + } auto& trgrecord = mTriggerRecordsClusters[eventID]; return ClusterRange(mClusters.data() + trgrecord.getFirstEntry(), trgrecord.getNumberOfObjects()); } @@ -92,8 +97,9 @@ template <class CellInputType> const typename EventHandler<CellInputType>::CellRange EventHandler<CellInputType>::getCellsForEvent(int eventID) const { if (mTriggerRecordsCells.size()) { - if (eventID >= mTriggerRecordsCells.size()) + if (eventID >= mTriggerRecordsCells.size()) { throw RangeException(eventID, mTriggerRecordsCells.size()); + } auto& trgrecord = mTriggerRecordsCells[eventID]; return CellRange(mCells.data() + trgrecord.getFirstEntry(), trgrecord.getNumberOfObjects()); } @@ -104,8 +110,9 @@ template <class CellInputType> const typename EventHandler<CellInputType>::CellIndexRange EventHandler<CellInputType>::getClusterCellIndicesForEvent(int eventID) const { if (mTriggerRecordsCellIndices.size()) { - if (eventID >= mTriggerRecordsCellIndices.size()) + if (eventID >= mTriggerRecordsCellIndices.size()) { throw RangeException(eventID, mTriggerRecordsCellIndices.size()); + } auto& trgrecord = mTriggerRecordsCellIndices[eventID]; return CellIndexRange(mClusterCellIndices.data() + trgrecord.getFirstEntry(), trgrecord.getNumberOfObjects()); } @@ -128,12 +135,15 @@ EventData<CellInputType> EventHandler<CellInputType>::buildEvent(int eventID) co { EventData<CellInputType> outputEvent; outputEvent.mInteractionRecord = getInteractionRecordForEvent(eventID); - if (hasClusters()) + if (hasClusters()) { outputEvent.mClusters = getClustersForEvent(eventID); - if (hasClusterIndices()) + } + if (hasClusterIndices()) { outputEvent.mCellIndices = getClusterCellIndicesForEvent(eventID); - if (hasCells()) + } + if (hasCells()) { outputEvent.mCells = getCellsForEvent(eventID); + } return outputEvent; } @@ -162,10 +172,11 @@ bool EventHandler<CellInputType>::EventIterator::operator==(const EventHandler<C template <class CellInputType> typename EventHandler<CellInputType>::EventIterator& EventHandler<CellInputType>::EventIterator::operator++() { - if (mForward) + if (mForward) { mEventID++; - else + } else { mEventID--; + } mCurrentEvent = mEventHandler.buildEvent(mEventID); return *this; } @@ -181,10 +192,11 @@ typename EventHandler<CellInputType>::EventIterator EventHandler<CellInputType>: template <class CellInputType> typename EventHandler<CellInputType>::EventIterator& EventHandler<CellInputType>::EventIterator::operator--() { - if (mForward) + if (mForward) { mEventID--; - else + } else { mEventID++; + } mCurrentEvent = mEventHandler.buildEvent(mEventID); return *this; } diff --git a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx index 2890cfb5ce185..50cfdafe69694 100644 --- a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx +++ b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx @@ -8,6 +8,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include <bitset> #include <iostream> #include "DataFormatsEMCAL/TriggerRecord.h" @@ -19,7 +20,7 @@ namespace emcal void TriggerRecord::printStream(std::ostream& stream) const { - stream << "Data for bc " << getBCData().bc << ", orbit " << getBCData().orbit << ", starting from entry " << getFirstEntry() << " with " << getNumberOfObjects() << " objects"; + stream << "Data for bc " << getBCData().bc << ", orbit " << getBCData().orbit << ", Triggers " << std::bitset<sizeof(mTriggerBits) * 8>(mTriggerBits) << ", starting from entry " << getFirstEntry() << " with " << getNumberOfObjects() << " objects"; } std::ostream& operator<<(std::ostream& stream, const TriggerRecord& trg) diff --git a/DataFormats/Detectors/EMCAL/test/testCell.cxx b/DataFormats/Detectors/EMCAL/test/testCell.cxx index b24703e2bc890..f98f0166066be 100644 --- a/DataFormats/Detectors/EMCAL/test/testCell.cxx +++ b/DataFormats/Detectors/EMCAL/test/testCell.cxx @@ -27,25 +27,26 @@ namespace emcal /// - verify that set and get functions return consistent values BOOST_AUTO_TEST_CASE(Cell_test) { - Cell c; + Cell c(0, 0, 0, o2::emcal::ChannelType_t::HIGH_GAIN); for (short j = 0; j < 17664; j++) { c.setTower(j); BOOST_CHECK_EQUAL(c.getTower(), j); - BOOST_CHECK_EQUAL(c.getTimeStamp(), 0); + BOOST_CHECK_SMALL(double(c.getTimeStamp()), 0.73); BOOST_CHECK_EQUAL(c.getEnergy(), 0); - BOOST_CHECK_EQUAL(c.getLowGain(), true); + BOOST_CHECK_EQUAL(c.getHighGain(), true); } c.setTower(0); - std::vector<int> times = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, 200}; + // Test time over the full range + std::vector<int> times = {-500, -200, -100, -50, -20, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, 200, 500, 900}; for (auto t : times) { c.setTimeStamp(t); BOOST_CHECK_EQUAL(c.getTower(), 0); - BOOST_CHECK_EQUAL(c.getTimeStamp(), t); + BOOST_CHECK_SMALL(double(c.getTimeStamp() - t), 0.73); BOOST_CHECK_EQUAL(c.getEnergy(), 0); - BOOST_CHECK_EQUAL(c.getLowGain(), true); + BOOST_CHECK_EQUAL(c.getHighGain(), true); } c.setTimeStamp(0); @@ -53,35 +54,42 @@ BOOST_AUTO_TEST_CASE(Cell_test) for (auto e : energies) { c.setEnergy(e); + if (e > 16) { + c.setLowGain(); + } BOOST_CHECK_EQUAL(c.getTower(), 0); - BOOST_CHECK_EQUAL(c.getTimeStamp(), 0); + BOOST_CHECK_SMALL(double(c.getTimeStamp()), 0.73); BOOST_CHECK_SMALL(e - c.getEnergy(), 0.02); // Require 20 MeV resolution - BOOST_CHECK_EQUAL(c.getLowGain(), true); + if (e > 16) { + BOOST_CHECK_EQUAL(c.getLowGain(), true); + } else { + BOOST_CHECK_EQUAL(c.getHighGain(), true); + } } c.setEnergy(0); c.setLowGain(); BOOST_CHECK_EQUAL(c.getTower(), 0); - BOOST_CHECK_EQUAL(c.getTimeStamp(), 0); + BOOST_CHECK_SMALL(double(c.getTimeStamp()), 0.73); BOOST_CHECK_EQUAL(c.getEnergy(), 0); - BOOST_CHECK_EQUAL(c.getLowGain(), true); BOOST_CHECK_EQUAL(c.getHighGain(), false); + BOOST_CHECK_EQUAL(c.getLowGain(), true); BOOST_CHECK_EQUAL(c.getLEDMon(), false); BOOST_CHECK_EQUAL(c.getTRU(), false); c.setHighGain(); BOOST_CHECK_EQUAL(c.getTower(), 0); - BOOST_CHECK_EQUAL(c.getTimeStamp(), 0); + BOOST_CHECK_SMALL(double(c.getTimeStamp()), 0.73); BOOST_CHECK_EQUAL(c.getEnergy(), 0); - BOOST_CHECK_EQUAL(c.getLowGain(), false); BOOST_CHECK_EQUAL(c.getHighGain(), true); + BOOST_CHECK_EQUAL(c.getLowGain(), false); BOOST_CHECK_EQUAL(c.getLEDMon(), false); BOOST_CHECK_EQUAL(c.getTRU(), false); c.setLEDMon(); BOOST_CHECK_EQUAL(c.getTower(), 0); - BOOST_CHECK_EQUAL(c.getTimeStamp(), 0); + BOOST_CHECK_SMALL(double(c.getTimeStamp()), 0.73); BOOST_CHECK_EQUAL(c.getEnergy(), 0); BOOST_CHECK_EQUAL(c.getLowGain(), false); BOOST_CHECK_EQUAL(c.getHighGain(), false); @@ -90,7 +98,7 @@ BOOST_AUTO_TEST_CASE(Cell_test) c.setTRU(); BOOST_CHECK_EQUAL(c.getTower(), 0); - BOOST_CHECK_EQUAL(c.getTimeStamp(), 0); + BOOST_CHECK_SMALL(double(c.getTimeStamp()), 0.73); BOOST_CHECK_EQUAL(c.getEnergy(), 0); BOOST_CHECK_EQUAL(c.getLowGain(), false); BOOST_CHECK_EQUAL(c.getHighGain(), false); diff --git a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt index f67ea3bdd2773..86c1eb28335e3 100644 --- a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt @@ -9,7 +9,10 @@ # submit itself to any jurisdiction. o2_add_library(DataFormatsFDD - PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + SOURCES src/RawEventData.cxx + src/CTF.cxx + PUBLIC_LINK_LIBRARIES O2::FDDBase + O2::CommonDataFormat O2::SimulationDataFormat) o2_target_root_dictionary(DataFormatsFDD @@ -17,7 +20,10 @@ o2_target_root_dictionary(DataFormatsFDD include/DataFormatsFDD/ChannelData.h include/DataFormatsFDD/RecPoint.h include/DataFormatsFDD/MCLabel.h - include/DataFormatsFDD/Hit.h) + include/DataFormatsFDD/Hit.h + 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 new file mode 100644 index 0000000000000..a199319242fa0 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h @@ -0,0 +1,82 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for FDD CTF data + +#ifndef O2_FDD_CTF_H +#define O2_FDD_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" + +namespace o2 +{ +namespace fdd +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nTriggers = 0; /// number of triggers in TF + uint32_t firstOrbit = 0; /// 1st orbit of TF + uint16_t firstBC = 0; /// 1st BC of TF + + ClassDefNV(CTFHeader, 1); +}; + +/// Intermediate, compressed but not yet entropy-encoded digits +struct CompressedDigits { + + CTFHeader header; + + // BC data + std::vector<uint8_t> trigger; // trigger bits + std::vector<uint16_t> bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector<uint32_t> orbitInc; // increment in orbit + std::vector<uint8_t> nChan; // number of fired channels + + // channel data + std::vector<uint8_t> idChan; // channels ID: 1st on absolute, then increment + std::vector<int16_t> time; // time + std::vector<int16_t> charge; // Amplitude + std::vector<uint8_t> feeBits; // QTC chain + + CompressedDigits() = default; + + void clear(); + + ClassDefNV(CompressedDigits, 2); +}; + +/// wrapper for the Entropy-encoded clusters of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 8, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { + BLC_trigger, // trigger bits + BLC_bcInc, // increment in BC + BLC_orbitInc, // increment in orbit + BLC_nChan, // number of fired channels + + BLC_idChan, // channels ID: 1st on absolute, then increment + BLC_time, // time + BLC_charge, // amplitude + BLC_feeBits // bits + }; + + ClassDefNV(CTF, 2); +}; + +} // namespace fdd +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h new file mode 100644 index 0000000000000..6b5e6707e0e53 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h @@ -0,0 +1,115 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 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_ +//////////////////////////////////////////////// +// Look Up Table FDD +////////////////////////////////////////////// + +#include <Rtypes.h> +#include <cassert> +#include <iostream> +#include <iomanip> // std::setfill, std::setw - for stream formating +#include <Framework/Logger.h> +#include "FDDBase/Constants.h" + +namespace o2 +{ +namespace fdd +{ + +struct Topo { + int modLink = 0; // Number of Processing Module, associated with GBT link ID + int modCh = 0; // Channel within the Processing Module in range from 0-11 + ClassDefNV(Topo, 1); +}; + +inline bool operator<(Topo const& a, Topo const& b) +{ + return (a.modLink < b.modLink || (a.modLink == b.modLink && a.modCh < b.modCh)); +} + +class LookUpTable +{ + public: + /// + /// Default constructor. + /// It must be kept public for root persistency purposes, + /// but should never be called by the outside world + LookUpTable() = default; + ~LookUpTable() = default; + + explicit LookUpTable(bool fillLinearly) + : mTopoVector(Nmodules * NChPerMod, {0, 0}), + mInvTopo(mTopoVector.size(), 0) + { + if (fillLinearly) { + LOG(INFO) << "Mapping of global channel and (PM, PM channel) pair"; + for (int link = 0; link < Nmodules; ++link) { + for (int ch = 0; ch < NChPerMod; ++ch) { + mTopoVector[link * NChPerMod + ch] = o2::fdd::Topo{link, ch}; + } + } + } else { + // TODO: If needed: implement more realistic splitting: 1 ring -> 1 PM instead of linear + LOG(WARNING) << "Don't use it - not implemented yet."; + } + + // Fill inverted LUT - matters only if LUT is not linear + for (size_t channel = 0; channel < mTopoVector.size(); ++channel) { + mInvTopo[getIdx(mTopoVector[channel].modLink, mTopoVector[channel].modCh)] = channel; + } + } + + int getChannel(int link, int mcp) const { return mInvTopo[getIdx(link, mcp)]; } + int getLink(int channel) const { return mTopoVector[channel].modLink; } + int getModChannel(int channel) const { return mTopoVector[channel].modCh; } + int getTcmLink() const { return Nmodules; } + void printFullMap() const + { + std::cout << "o2::fdd::LookUpTable::printFullMap(): mTopoVector: [globalCh link modCh]" << std::endl; + for (size_t channel = 0; channel < mTopoVector.size(); ++channel) { + std::cout << " " << std::right << std::setw(2) << channel << " "; + std::cout << std::right << std::setw(2) << mTopoVector[channel].modLink << " "; + std::cout << std::right << std::setw(3) << mTopoVector[channel].modCh << std::endl; + } + std::cout << "o2::fdd::LookUpTable::printFullMap(): mInvTopo: [idx globalCh link modCh]" << std::endl; + for (size_t idx = 0; idx < mInvTopo.size(); ++idx) { + std::cout << " " << std::right << std::setw(3) << idx << " "; + std::cout << std::right << std::setw(3) << mInvTopo[idx] << " "; + std::cout << std::right << std::setw(2) << getLinkFromIdx(mInvTopo[idx]) << " "; + std::cout << std::right << std::setw(2) << getModChannelFromIdx(mInvTopo[idx]) << std::endl; + } + } + + private: + std::vector<Topo> mTopoVector; // iterator of each vector element gives the global channel number + std::vector<int> mInvTopo; // each element is an iterator of mTopoVector + + static int getIdx(int link, int modCh) + { + assert(modCh < NChPerMod); + return link * NChPerMod + modCh; + } + static int getLinkFromIdx(int idx) { return idx / NChPerMod; } + static int getModChannelFromIdx(int idx) { return idx % NChPerMod; } + + ClassDefNV(LookUpTable, 1); +}; + +} // namespace fdd +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RawEventData.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RawEventData.h new file mode 100644 index 0000000000000..3f8220e4a8f06 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RawEventData.h @@ -0,0 +1,187 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Based on the FT0 file RawEventData.h (almost identical except additional printing) by +// Alla.Maevskaya@cern.ch +// with Artur.Furs +// +#ifndef ALICEO2_FDD_RAWEVENTDATA_H_ +#define ALICEO2_FDD_RAWEVENTDATA_H_ + +#include "FDDBase/Constants.h" +#include "DataFormatsFDD/ChannelData.h" +#include "Headers/RAWDataHeader.h" +#include "DataFormatsFDD/LookUpTable.h" +#include <CommonDataFormat/InteractionRecord.h> +#include <Framework/Logger.h> +#include <iostream> +#include <iomanip> +#include <cstring> +#include "Rtypes.h" + +namespace o2 +{ +namespace fdd +{ +struct EventHeader { + static constexpr int PayloadSize = 16; + union { + uint64_t word[2] = {}; + struct { + uint64_t bc : 12; + uint64_t orbit : 32; + uint64_t reservedField1 : 20; + uint64_t reservedField2 : 8; + uint64_t nGBTWords : 4; + uint64_t startDescriptor : 4; + uint64_t reservedField3 : 48; + }; + }; +}; +struct EventData { + union { + uint64_t word = {0}; + struct { + int64_t time : 12; + int64_t charge : 13; + uint64_t numberADC : 1, + isDoubleEvent : 1, + is1TimeLostEvent : 1, + is2TimeLostEvent : 1, + isADCinGate : 1, + isTimeInfoLate : 1, + isAmpHigh : 1, + isEventInTVDC : 1, + isTimeInfoLost : 1, + reservedField : 2, + channelID : 4; + }; + }; + uint64_t word_zeros = 0x0; + static const size_t PayloadSizeSecondWord = 11; + static const size_t PayloadSizeFirstWord = 5; + void generateFlags() + { + numberADC = std::rand() % 2; + isDoubleEvent = 0; + is1TimeLostEvent = 0; + is2TimeLostEvent = 1; + isADCinGate = 0; + isTimeInfoLate = 0; + isAmpHigh = 0; + isEventInTVDC = 1; + isTimeInfoLost = 0; + } +}; + +struct TCMdata { + static constexpr int PayloadSize = 16; + union { + uint64_t word[2] = {0}; + struct { + uint64_t orC : 1, + orA : 1, + sCen : 1, + cen : 1, + vertex : 1, + nChanA : 7, + nChanC : 7; + int64_t amplA : 18, + amplC : 18, + reservedField1 : 1, //56B, PayloadSize1stWord 6 + timeA : 9, + timeC : 9, + reservedField2 : 46; + }; + }; +}; + +class RawEventData +{ + public: + RawEventData() = default; + EventHeader* getEventHeaderPtr() { return &mEventHeader; } + EventData* getEventDataPtr() { return mEventData; } + void print(); + void printHexEventHeader(); + void printHexEventData(uint64_t i); + enum EEventDataBit { kNumberADC, + kIsDoubleEvent, + kIs1TimeLostEvent, + kIs2TimeLostEvent, + kIsADCinGate, + kIsTimeInfoLate, + kIsAmpHigh, + kIsEventInTVDC, + kIsTimeInfoLost }; + const static int gStartDescriptor = 0x0000000f; + + int size() const + { + return 1 + mEventHeader.nGBTWords; // EventHeader + EventData size + } + + std::vector<char> to_vector(bool tcm) + { + constexpr int CRUWordSize = 16; + + std::vector<char> result(size() * CRUWordSize); + char* out = result.data(); + if (!tcm) { + std::memcpy(out, &mEventHeader, EventHeader::PayloadSize); + out += EventHeader::PayloadSize; + LOG(DEBUG) << " Write PM header: nWords: " << (int)mEventHeader.nGBTWords + << " orbit: " << int(mEventHeader.orbit) + << " BC: " << int(mEventHeader.bc) + << " size: " << result.size(); + printHexEventHeader(); + out += CRUWordSize - EventHeader::PayloadSize; // Padding enabled + + for (uint64_t i = 0; i < mEventHeader.nGBTWords; ++i) { + std::memcpy(out, &mEventData[2 * i], EventData::PayloadSizeFirstWord); + out += EventData::PayloadSizeFirstWord; + LOG(DEBUG) << " 1st word: Ch: " << std::setw(2) << mEventData[2 * i].channelID + << " charge: " << std::setw(4) << mEventData[2 * i].charge + << " time: " << std::setw(4) << mEventData[2 * i].time; + std::memcpy(out, &mEventData[2 * i + 1], EventData::PayloadSizeSecondWord); + out += EventData::PayloadSizeSecondWord; + LOG(DEBUG) << " 2nd word: Ch: " << std::setw(2) << mEventData[2 * i + 1].channelID + << " charge: " << std::setw(4) << mEventData[2 * i + 1].charge + << " time: " << std::setw(4) << mEventData[2 * i + 1].time; + + out += CRUWordSize - EventData::PayloadSizeSecondWord - EventData::PayloadSizeFirstWord; + printHexEventData(i); + } + } else { + // TCM data + std::memcpy(out, &mEventHeader, EventHeader::PayloadSize); + out += EventHeader::PayloadSize; + LOG(DEBUG) << " Write TCM header: nWords: " << (int)mEventHeader.nGBTWords + << " orbit: " << int(mEventHeader.orbit) + << " BC: " << int(mEventHeader.bc) + << " size: " << result.size(); + std::memcpy(out, &mTCMdata, sizeof(TCMdata)); + out += sizeof(TCMdata); + // TODO: No TCM payload printing until the meaning of trigger bits and other flags is clarified + } + return result; + } + + public: + EventHeader mEventHeader; + EventData mEventData[NChPerMod]; + TCMdata mTCMdata; + + ClassDefNV(RawEventData, 1); +}; + +} // namespace fdd +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/FDD/src/CTF.cxx b/DataFormats/Detectors/FIT/FDD/src/CTF.cxx new file mode 100644 index 0000000000000..24724cce55852 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/src/CTF.cxx @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "Framework/Logger.h" +#include "DataFormatsFDD/CTF.h" + +using namespace o2::fdd; + +///________________________________ +void CompressedDigits::clear() +{ + trigger.clear(); + bcInc.clear(); + orbitInc.clear(); + nChan.clear(); + + idChan.clear(); + time.clear(); + charge.clear(); + feeBits.clear(); + + header.nTriggers = 0; + header.firstOrbit = 0; + header.firstBC = 0; +} diff --git a/DataFormats/Detectors/FIT/FDD/src/DataFormatsFDDLinkDef.h b/DataFormats/Detectors/FIT/FDD/src/DataFormatsFDDLinkDef.h index 054c4a6a7eac2..6f4f3e9fe4554 100644 --- a/DataFormats/Detectors/FIT/FDD/src/DataFormatsFDDLinkDef.h +++ b/DataFormats/Detectors/FIT/FDD/src/DataFormatsFDDLinkDef.h @@ -30,4 +30,14 @@ #pragma link C++ class o2::fdd::RecPoint + ; #pragma link C++ class vector < o2::fdd::RecPoint> + ; +#pragma link C++ class o2::fdd::RawEventData + ; +#pragma link C++ class o2::fdd::EventHeader + ; +#pragma link C++ class o2::fdd::EventData + ; +#pragma link C++ class o2::fdd::TCMdata + ; +#pragma link C++ class o2::fdd::Topo + ; + +#pragma link C++ class o2::fdd::CTFHeader + ; +#pragma link C++ class o2::fdd::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::fdd::CTFHeader, 8, uint32_t> + ; + #endif diff --git a/DataFormats/Detectors/FIT/FDD/src/RawEventData.cxx b/DataFormats/Detectors/FIT/FDD/src/RawEventData.cxx new file mode 100644 index 0000000000000..64c5872e9a0e1 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/src/RawEventData.cxx @@ -0,0 +1,71 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/RawEventData.h" +#include <sstream> + +using namespace o2::fdd; + +ClassImp(RawEventData); + +void RawEventData::print() +{ + std::cout << "==================Raw event data==================" << std::endl; + std::cout << "##################Header##################" << std::endl; + std::cout << "startDescriptor: " << mEventHeader.startDescriptor << std::endl; + std::cout << "Nchannels: " << mEventHeader.nGBTWords * 2 << std::endl; + std::cout << "BC: " << mEventHeader.bc << std::endl; + std::cout << "Orbit: " << mEventHeader.orbit << std::endl; + std::cout << "##########################################" << std::endl; + std::cout << "###################DATA###################" << std::endl; + for (int iCh = 0; iCh < mEventHeader.nGBTWords * 2; iCh++) { + std::cout << "------------Channel " << mEventData[iCh].channelID << "------------" << std::endl; + std::cout << "Charge: " << mEventData[iCh].charge << std::endl; + std::cout << "Time: " << mEventData[iCh].time << std::endl; + std::cout << "1TimeLostEvent: " << mEventData[iCh].is1TimeLostEvent << std::endl; + std::cout << "2TimeLostEvent: " << mEventData[iCh].is2TimeLostEvent << std::endl; + std::cout << "ADCinGate: " << mEventData[iCh].isADCinGate << std::endl; + std::cout << "AmpHigh: " << mEventData[iCh].isAmpHigh << std::endl; + std::cout << "DoubleEvent: " << mEventData[iCh].isDoubleEvent << std::endl; + std::cout << "EventInTVDC: " << mEventData[iCh].isEventInTVDC << std::endl; + std::cout << "TimeInfoLate: " << mEventData[iCh].isTimeInfoLate << std::endl; + std::cout << "TimeInfoLost: " << mEventData[iCh].isTimeInfoLost << std::endl; + std::cout << "numberADC: " << mEventData[iCh].numberADC << std::endl; + } + std::cout << "##########################################" << std::endl; +} + +void RawEventData::printHexEventHeader() +{ + std::stringstream ssheader; + ssheader << std::setfill('0') << std::setw(16) << std::hex << mEventHeader.word[0] << " " << std::setw(16) << mEventHeader.word[1] << "\n "; + ssheader << std::setw(3) << (0x00000fff & mEventHeader.bc) << " " + << std::setw(8) << (0xffffffff & mEventHeader.orbit) << " " + << std::setw(5) << (0x000fffff & mEventHeader.reservedField1) << " " + << std::setw(2) << (0x000000ff & mEventHeader.reservedField2) << " " + << std::setw(1) << (0x0000000f & mEventHeader.nGBTWords) << " " + << std::setw(1) << (0x0000000f & mEventHeader.startDescriptor) << " " + << std::setw(12) << (0xffffffffffff & mEventHeader.reservedField3); + LOG(DEBUG) << ssheader.str(); +} + +void RawEventData::printHexEventData(uint64_t i) +{ + std::stringstream ssdata; + ssdata << "D0:0x "; + ssdata << std::setfill('0') << std::hex << std::setw(16) << mEventData[2 * i].word << "\n "; + ssdata << std::setw(3) << (0x0fff & mEventData[2 * i].time) << " " + << std::setw(8) << (0x1fff & mEventData[2 * i].charge) << "\n "; + ssdata << "D1:0x "; + ssdata << std::setfill('0') << std::hex << std::setw(16) << mEventData[2 * i + 1].word << "\n "; + ssdata << std::setw(3) << (0x0fff & mEventData[2 * i + 1].time) << " " + << std::setw(8) << (0x1fff & mEventData[2 * i + 1].charge); + LOG(DEBUG) << " | " << ssdata.str(); +} diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index 05db031ba9ae6..ed0399a72d9cb 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -14,9 +14,13 @@ o2_add_library(DataFormatsFT0 SOURCES src/ChannelData.cxx SOURCES src/RecPoints.cxx SOURCES src/RawEventData.cxx - PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers - O2::SimulationDataFormat O2::FT0Base - ) + src/CTF.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::Headers + O2::SimulationDataFormat + O2::CCDB + O2::DetectorsCalibration + O2::FT0Base) o2_target_root_dictionary(DataFormatsFT0 HEADERS include/DataFormatsFT0/Digit.h @@ -26,4 +30,5 @@ o2_target_root_dictionary(DataFormatsFT0 include/DataFormatsFT0/MCLabel.h include/DataFormatsFT0/HitType.h include/DataFormatsFT0/RawEventData.h - include/DataFormatsFT0/LookUpTable.h ) + include/DataFormatsFT0/LookUpTable.h + include/DataFormatsFT0/CTF.h) diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h new file mode 100644 index 0000000000000..fdf54804c0d1a --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h @@ -0,0 +1,83 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for FT0 CTF data + +#ifndef O2_FT0_CTF_H +#define O2_FT0_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" + +namespace o2 +{ +namespace ft0 +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nTriggers = 0; /// number of triggers in TF + uint32_t firstOrbit = 0; /// 1st orbit of TF + uint16_t firstBC = 0; /// 1st BC of TF + + ClassDefNV(CTFHeader, 1); +}; + +/// Intermediate, compressed but not yet entropy-encoded digits +struct CompressedDigits { + + CTFHeader header; + + // trigger data + std::vector<uint8_t> trigger; // trigger bits + std::vector<uint16_t> bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector<uint32_t> orbitInc; // increment in orbit + std::vector<uint8_t> nChan; // number of fired channels + std::vector<uint8_t> eventFlags; // special flags about event conditions: pile-up, not use for collision time, not use for event plane, etc. + + // channel data + std::vector<uint8_t> idChan; // channels ID: 1st on absolute, then increment + std::vector<int16_t> cfdTime; // CFD time + std::vector<int32_t> qtcAmpl; // Amplitude + std::vector<uint8_t> qtcChain; // QTC chain + + CompressedDigits() = default; + + void clear(); + + ClassDefNV(CompressedDigits, 1); +}; + +/// wrapper for the Entropy-encoded clusters of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 9, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { + BLC_trigger, // trigger bits + BLC_bcInc, // increment in BC + BLC_orbitInc, // increment in orbit + BLC_nChan, // number of fired channels + BLC_flags, // flags special flags about event conditions: pile-up, not use for collision time, not use for event plane, etc. + BLC_idChan, // channels ID: 1st on absolute, then increment + BLC_qtcChain, // ADC chain + BLC_cfdTime, // CFD time + BLC_qtcAmpl // amplitude + }; + + ClassDefNV(CTF, 1); +}; + +} // namespace ft0 +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h index 6c6422871febd..d42bad4cfef8e 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h @@ -27,6 +27,15 @@ struct ChannelData { uint8_t ChainQTC = 0xff; //QTC chain int16_t CFDTime = -1000; //time in #CFD channels, 0 at the LHC clk center int16_t QTCAmpl = -1000; // Amplitude #channels + enum EEventDataBit { kNumberADC, + kIsDoubleEvent, + kIsTimeInfoNOTvalid, + kIsCFDinADCgate, + kIsTimeInfoLate, + kIsAmpHigh, + kIsEventInTVDC, + kIsTimeInfoLost + }; ChannelData() = default; ChannelData(uint8_t iPmt, int time, int charge, uint8_t chainQTC) @@ -36,10 +45,18 @@ struct ChannelData { QTCAmpl = charge; ChainQTC = chainQTC; } - + void setFlag(uint8_t flag) + { + ChainQTC = flag; + } + void setFlag(EEventDataBit bitFlag, bool value) + { + ChainQTC |= (value << bitFlag); + } + bool getFlag(EEventDataBit bitFlag) const { return bool(ChainQTC << bitFlag); } void print() const; - ClassDefNV(ChannelData, 1); + ClassDefNV(ChannelData, 2); }; } // namespace ft0 } // namespace o2 diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h index 2ca2098a4ad5e..d1877271b1bed 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h @@ -36,13 +36,14 @@ struct Triggers { bitVertex, bitCen, bitSCen }; - uint8_t triggersignals; // T0 trigger signals - int8_t nChanA; // number of faired channels A side - int8_t nChanC; // number of faired channels A side - int32_t amplA; // sum amplitude A side - int32_t amplC; // sum amplitude C side - int16_t timeA; // average time A side - int16_t timeC; // average time C side + uint8_t triggersignals = 0; // T0 trigger signals + int8_t nChanA = 0; // number of fired channels A side + int8_t nChanC = 0; // number of fired channels A side + int32_t amplA = -1000; // sum amplitude A side + int32_t amplC = -1000; // sum amplitude C side + int16_t timeA = -1000; // average time A side + int16_t timeC = -1000; // average time C side + uint8_t eventFlags = 0; // event conditions Triggers() = default; Triggers(uint8_t signals, int8_t chanA, int8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC) { @@ -54,11 +55,11 @@ struct Triggers { timeA = atimeA; timeC = atimeC; } - bool getOrA() { return (triggersignals & (1 << bitA)) != 0; } - bool getOrC() { return (triggersignals & (1 << bitC)) != 0; } - bool getVertex() { return (triggersignals & (1 << bitVertex)) != 0; } - bool getCen() { return (triggersignals & (1 << bitCen)) != 0; } - bool getSCen() { return (triggersignals & (1 << bitSCen)) != 0; } + bool getOrA() const { return (triggersignals & (1 << bitA)) != 0; } + bool getOrC() const { return (triggersignals & (1 << bitC)) != 0; } + bool getVertex() const { return (triggersignals & (1 << bitVertex)) != 0; } + bool getCen() const { return (triggersignals & (1 << bitCen)) != 0; } + bool getSCen() const { return (triggersignals & (1 << bitSCen)) != 0; } void setTriggers(Bool_t isA, Bool_t isC, Bool_t isVrtx, Bool_t isCnt, Bool_t isSCnt, int8_t chanA, int8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC) @@ -74,23 +75,41 @@ struct Triggers { void cleanTriggers() { triggersignals = 0; - nChanA = nChanC = -1; + nChanA = nChanC = 0; amplA = amplC = -1000; timeA = timeC = -1000; } - Triggers getTriggers(); - ClassDefNV(Triggers, 1); }; +struct DetTrigInput { + o2::InteractionRecord mIntRecord; // bc/orbit of the intpur + std::bitset<5> mInputs; // pattern of inputs. + DetTrigInput() = default; + DetTrigInput(const o2::InteractionRecord& iRec, Bool_t isA, Bool_t isC, Bool_t isVrtx, Bool_t isCnt, Bool_t isSCnt) + : mIntRecord(iRec), + mInputs((isA << Triggers::bitA) | + (isC << Triggers::bitC) | + (isVrtx << Triggers::bitVertex) | + (isCnt << Triggers::bitCen) | + (isSCnt << Triggers::bitSCen)) + { + } + ClassDefNV(DetTrigInput, 1); +}; + struct Digit { o2::dataformats::RangeReference<int, int> ref; Triggers mTriggers; // pattern of triggers in this BC + uint8_t mEventStatus; //Status of event from FT0, such as Pileup , etc o2::InteractionRecord mIntRecord; // Interaction record (orbit, bc) int mEventID; + enum EEventStatus { + kPileup + }; Digit() = default; - Digit(int first, int ne, o2::InteractionRecord iRec, Triggers chTrig, int event) + Digit(int first, int ne, const o2::InteractionRecord& iRec, const Triggers& chTrig, int event) { ref.setFirstEntry(first); ref.setEntries(ne); @@ -100,28 +119,42 @@ struct Digit { } uint32_t getOrbit() const { return mIntRecord.orbit; } uint16_t getBC() const { return mIntRecord.bc; } - Triggers getTriggers() { return mTriggers; } + Triggers getTriggers() const { return mTriggers; } int getEventID() const { return mEventID; } o2::InteractionRecord getIntRecord() { return mIntRecord; }; gsl::span<const ChannelData> getBunchChannelData(const gsl::span<const ChannelData> tfdata) const; - enum { bit1TimeLostEvent, - bit2TimeLostEvent, - bitADCinGate, - bitTimeInfoLate, - bitAmpHigh, - bitEventInTVDC, - bitTimeInfoLost }; - // T0 channel flags - uint8_t flags; - void setFlags(bool is1TimeLostEvent, bool is2TimeLostEvent, bool isADCinGate, bool isTimeInfoLate, bool isAmpHigh, bool isEventInTVDC, bool isTimeInfoLost) - { - flags = (is1TimeLostEvent << bit1TimeLostEvent) | (is2TimeLostEvent << bit2TimeLostEvent) | (isADCinGate << bitADCinGate) | (isTimeInfoLate << bitTimeInfoLate) | (isAmpHigh << bitAmpHigh) | (isEventInTVDC << bitEventInTVDC) | (isTimeInfoLost << bitTimeInfoLost); - }; void printStream(std::ostream& stream) const; void setTriggers(Triggers trig) { mTriggers = trig; }; + void setEventStatus(uint8_t stat) { mEventStatus = stat; }; + void setStatusFlag(EEventStatus bit, bool value) { mEventStatus |= (value << bit); }; + bool getStatusFlag(EEventStatus bit) const { return bool(mEventStatus << bit); } + uint8_t getEventStatusWord() const { return mEventStatus; } - ClassDefNV(Digit, 3); + ClassDefNV(Digit, 5); +}; + +//For TCM extended mode (calibration mode), TCMdataExtended digit +struct TriggersExt { + TriggersExt(uint32_t triggerWord) + { + mTriggerWord = triggerWord; + } + TriggersExt() = default; + uint32_t mTriggerWord; + ClassDefNV(TriggersExt, 1); +}; + +//For TCM extended mode (calibration mode) +struct DigitExt : Digit { + DigitExt(int first, int ne, int firstExt, int neExt, const o2::InteractionRecord& iRec, const Triggers& chTrig, int event) : Digit(first, ne, iRec, chTrig, event) + { + refExt.setFirstEntry(firstExt); + refExt.setEntries(neExt); + } + DigitExt() = default; + o2::dataformats::RangeReference<int, int> refExt; //range reference to container with TriggerExt objects + ClassDefNV(DigitExt, 1); }; } // namespace ft0 } // namespace o2 diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h index b139213474be5..bc8c7684474f9 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h @@ -8,10 +8,10 @@ // 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 +// 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 +// Will be really set after module/electronics connections // #ifndef ALICEO2_FT0_LOOKUPTABLE_H_ #define ALICEO2_FT0_LOOKUPTABLE_H_ @@ -19,10 +19,21 @@ // Look Up Table FT0 ////////////////////////////////////////////// +#include "CCDB/BasicCCDBManager.h" + #include <Rtypes.h> #include <cassert> +#include <exception> #include <iostream> +#include <fstream> +#include <stdexcept> #include <tuple> +#include <TSystem.h> +#include <cstdlib> +#include <map> +#include <string_view> +#include <vector> + namespace o2 { namespace ft0 @@ -33,35 +44,85 @@ struct Topo { ClassDefNV(Topo, 1); }; +// enum class Side : char { A, C }; +// struct PM { +// Side side; +// uint8_t PM_num, PM_channel; +// }; + +struct HVchannel { + enum class HVBoard : uint8_t { NA, + A_out, + A_in, + C_up, + C_down, + C_mid }; + uint8_t channel; + Topo pm; + HVBoard HV_board; + uint8_t HV_channel; + std::string MCP_SN; + std::string HV_cabel; + std::string signal_cable; + + ClassDefNV(HVchannel, 1); +}; + inline bool operator<(Topo const& a, Topo const& b) { return (a.mPM < b.mPM || (a.mPM == b.mPM && a.mMCP < b.mMCP)); } +inline o2::ft0::Topo read_Topo(std::string_view str) +{ + assert(str.substr(0, 2) == "PM" && str[4] == '/' && str[5] == 'C' && str[6] == 'h'); + char side = str[2]; + uint8_t pm_num = str[3] - '0'; + uint8_t pm_ch = (str[7] - '0') * 10 + (str[8] - '0') - 1; + assert(side == 'A' || side == 'C'); + if (str.substr(0, 4) == "PMA9") { + pm_num = 18; + } + return Topo{(side == 'C' ? 8 : 0) + pm_num, pm_ch}; +} + class LookUpTable { + using CcdbManager = o2::ccdb::BasicCCDBManager; + using CcdbApi = o2::ccdb::CcdbApi; static constexpr int NUMBER_OF_MCPs = 12; - static constexpr int NUMBER_OF_PMs = 18; + static constexpr int NUMBER_OF_PMs = 19; public: /// /// Default constructor. /// It must be kept public for root persistency purposes, /// but should never be called by the outside world - LookUpTable() = default; + // LookUpTable() = default; + explicit LookUpTable(std::vector<Topo> const& topoVector) : mTopoVector(topoVector), mInvTopo(topoVector.size()) { - for (size_t channel = 0; channel < mTopoVector.size(); ++channel) - mInvTopo.at(getIdx(mTopoVector[channel].mPM, mTopoVector[channel].mMCP)) = channel; + for (size_t channel = 0; channel < mTopoVector.size(); ++channel) { + mInvTopo.at(getIdx(mTopoVector[channel].mPM, mTopoVector[channel].mMCP)) = + channel; + } } + LookUpTable() = default; ~LookUpTable() = default; + + HVchannel mHVchannel; + void printFullMap() const { - for (size_t channel = 0; channel < mTopoVector.size(); ++channel) - std::cout << channel << "\t : PM \t" << mTopoVector[channel].mPM << " MCP \t" << mTopoVector[channel].mMCP << std::endl; - for (size_t idx = 0; idx < mInvTopo.size(); ++idx) - std::cout << "PM \t" << getLinkFromIdx(mInvTopo[idx]) << " MCP \t" << getMCPFromIdx(mInvTopo[idx]) << std::endl; + for (size_t channel = 0; channel < mTopoVector.size(); ++channel) { + std::cout << channel << "\t : PM \t" << mTopoVector[channel].mPM + << " MCP \t" << mTopoVector[channel].mMCP << std::endl; + } + for (size_t idx = 0; idx < mInvTopo.size(); ++idx) { + std::cout << "PM \t" << getLinkFromIdx(mInvTopo[idx]) << " MCP \t" + << getMCPFromIdx(mInvTopo[idx]) << std::endl; + } } int getChannel(int link, int mcp) const @@ -69,8 +130,75 @@ class LookUpTable return mInvTopo[getIdx(link, mcp)]; } - int getLink(int channel) const { return mTopoVector[channel].mPM; } - int getMCP(int channel) const { return mTopoVector[channel].mMCP; } + int getLink(int channel) const + { + return mTopoVector[channel].mPM; + } + int getMCP(int channel) const + { + return mTopoVector[channel].mMCP; + } + + static o2::ft0::LookUpTable linear() + { + std::vector<o2::ft0::Topo> lut_data(NUMBER_OF_MCPs * NUMBER_OF_PMs); + for (int link = 0; link < NUMBER_OF_PMs; ++link) { + for (int mcp = 0; mcp < NUMBER_OF_MCPs; ++mcp) { + lut_data[link * NUMBER_OF_MCPs + mcp] = o2::ft0::Topo{link, mcp}; + } + } + return o2::ft0::LookUpTable{lut_data}; + } + + static o2::ft0::LookUpTable readTableFile() + { + std::string inputDir; + const char* aliceO2env = std::getenv("O2_ROOT"); + if (aliceO2env) { + inputDir = aliceO2env; + } + inputDir += "/share/Detectors/FT0/files/"; + + std::string lutPath = inputDir + "FT0ChannelsTable.txt"; + lutPath = gSystem->ExpandPathName(lutPath.data()); // Expand $(ALICE_ROOT) into real system path + + std::ifstream infile; + infile.open(lutPath.c_str()); + + std::vector<o2::ft0::Topo> lut_data(NUMBER_OF_MCPs * NUMBER_OF_PMs - 8); + std::string comment; // dummy, used just to read 4 first lines and move the cursor to the 5th, otherwise unused + if (!getline(infile, comment)) { // first comment line + /* std::cout << "Error opening ascii file (it is probably a folder!): " << filename.c_str() << std::endl; */ + throw std::runtime_error("Error reading lookup table"); + } + int channel; + std::string pm, pm_channel, hv_board, hv_channel, mcp_sn, hv_cable, signal_cable; + std::getline(infile, pm); // skip one line + while (infile >> channel >> pm >> pm_channel >> hv_board >> hv_channel >> mcp_sn >> hv_cable >> signal_cable) { + lut_data[channel] = read_Topo(pm_channel); + } + return o2::ft0::LookUpTable{lut_data}; + } + static o2::ft0::LookUpTable readTable() + { + + std::vector<o2::ft0::Topo> lut_data; + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL("http://ccdb-test.cern.ch:8080"); + auto hvch = mgr.get<std::vector<o2::ft0::HVchannel>>("FT0/LookUpTable"); + size_t max = 0; + for (auto const& chan : *hvch) { + if (max < chan.channel) { + max = chan.channel; + } + } + lut_data.resize(max + 1); + for (auto const& chan : *hvch) { + o2::ft0::Topo topo = chan.pm; + lut_data[chan.channel] = topo; + } + return o2::ft0::LookUpTable{lut_data}; + } private: std::vector<Topo> mTopoVector; @@ -81,16 +209,10 @@ class LookUpTable assert(mcp < NUMBER_OF_MCPs); return link * NUMBER_OF_MCPs + mcp; } - static int getLinkFromIdx(int idx) - { - return idx / NUMBER_OF_MCPs; - } - static int getMCPFromIdx(int idx) - { - return idx % NUMBER_OF_MCPs; - } + static int getLinkFromIdx(int idx) { return idx / NUMBER_OF_MCPs; } + static int getMCPFromIdx(int idx) { return idx % NUMBER_OF_MCPs; } - ClassDefNV(LookUpTable, 1); + ClassDefNV(LookUpTable, 2); }; } // namespace ft0 diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RawEventData.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RawEventData.h index 4ebe0341e5779..ccb322a35dab6 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RawEventData.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RawEventData.h @@ -16,11 +16,14 @@ #define ALICEO2_FIT_RAWEVENTDATA_H_ #include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/ChannelData.h" + #include "Headers/RAWDataHeader.h" #include "DataFormatsFT0/LookUpTable.h" #include <CommonDataFormat/InteractionRecord.h> #include <Framework/Logger.h> #include <iostream> +#include <utility> #include <cstring> #include "Rtypes.h" namespace o2 @@ -29,90 +32,203 @@ namespace ft0 { constexpr int Nchannels_FT0 = 208; constexpr int Nchannels_PM = 12; -constexpr int NPMs = 19; +constexpr int NPMs = 20; +constexpr size_t sizeWord = 16; struct EventHeader { - static constexpr int PayloadSize = 16; + static constexpr size_t PayloadSize = 16; //should be equal to 10 + static constexpr size_t PayloadPerGBTword = 16; //should be equal to 10 + static constexpr int MinNelements = 1; + static constexpr int MaxNelements = 1; union { uint64_t word[2] = {}; struct { uint64_t bc : 12; uint64_t orbit : 32; - uint64_t reservedField1 : 20; + uint64_t phase : 3; + uint64_t errorPhase : 1; + uint64_t reservedField1 : 16; uint64_t reservedField2 : 8; uint64_t nGBTWords : 4; uint64_t startDescriptor : 4; uint64_t reservedField3 : 48; }; }; + InteractionRecord getIntRec() const { return InteractionRecord{(uint16_t)bc, (uint32_t)orbit}; } + + void print() const + { + std::cout << std::hex; + std::cout << "################EventHeader###############" << std::endl; + std::cout << "startDescriptor: " << startDescriptor << std::endl; + std::cout << "nGBTWords: " << nGBTWords << std::endl; + std::cout << "BC: " << bc << std::endl; + std::cout << "Orbit: " << orbit << std::endl; + std::cout << "##########################################" << std::endl; + std::cout << std::dec; + } }; struct EventData { + static constexpr size_t PayloadSize = 5; + static constexpr size_t PayloadPerGBTword = 10; + static constexpr int MinNelements = 1; //additional static field + static constexpr int MaxNelements = 12; + // + static constexpr int BitFlagPos = 25; // position of first bit flag(numberADC) + union { uint64_t word = {0}; struct { int64_t time : 12; int64_t charge : 13; - uint64_t numberADC : 1, + uint64_t numberADC : 1, //25 bit isDoubleEvent : 1, - is1TimeLostEvent : 1, - is2TimeLostEvent : 1, - isADCinGate : 1, + isTimeInfoNOTvalid : 1, + isCFDinADCgate : 1, isTimeInfoLate : 1, isAmpHigh : 1, isEventInTVDC : 1, isTimeInfoLost : 1, - reservedField : 2, + reservedField : 3, channelID : 4; }; }; - uint64_t word_zeros = 0x0; - static const size_t PayloadSizeSecondWord = 11; - static const size_t PayloadSizeFirstWord = 5; + void generateFlags() + { + numberADC = std::rand() % 2; + isDoubleEvent = 0; + isTimeInfoNOTvalid = 0; + isCFDinADCgate = 1; + isTimeInfoLate = 0; + isAmpHigh = 0; + isEventInTVDC = 1; + isTimeInfoLost = 0; + } + uint8_t getFlagWord() const + { + return uint8_t(word >> BitFlagPos); + } + void print() const + { + std::cout << std::hex; + std::cout << "###############EventData(PM)##############" << std::endl; + std::cout << "------------Channel " << channelID << "------------" << std::endl; + std::cout << "Charge: " << charge << std::endl; + std::cout << "Time: " << time << std::endl; + std::cout << "numberADC: " << numberADC << std::endl; + std::cout << "isDoubleEvent: " << isDoubleEvent << std::endl; + std::cout << "isTimeInfoNOTvalid: " << isTimeInfoNOTvalid << std::endl; + std::cout << "isCFDinADCgate: " << isCFDinADCgate << std::endl; + std::cout << "isTimeInfoLate: " << isTimeInfoLate << std::endl; + std::cout << "isAmpHigh: " << isAmpHigh << std::endl; + std::cout << "isEventInTVDC: " << isEventInTVDC << std::endl; + std::cout << "isTimeInfoLost: " << isTimeInfoLost << std::endl; + std::cout << "##########################################" << std::endl; + std::cout << std::dec; + } + + //temporary, this method should be in ChannelData struct, TODO + void fillChannelData(ChannelData& channelData) const + { + channelData.ChainQTC = getFlagWord(); + } + + uint64_t word_zeros = 0x0; //to remove + static const size_t PayloadSizeSecondWord = 11; //to remove + static const size_t PayloadSizeFirstWord = 5; //to remove }; struct TCMdata { - static constexpr int PayloadSize = 16; + static constexpr size_t PayloadSize = 16; //should be equal to 10 + static constexpr size_t PayloadPerGBTword = 16; //should be equal to 10 + static constexpr int MinNelements = 1; + static constexpr int MaxNelements = 1; + uint64_t orC : 1, // 0 bit (0 byte) + orA : 1, //1 bit + sCen : 1, //2 bit + cen : 1, //3 bit + vertex : 1, //4 bit + reservedField1 : 3, //5 bit + nChanA : 7, //8 bit(1 byte) + reservedField2 : 1, //15 bit + nChanC : 7, //16 bit(2 byte) + reservedField3 : 1; // 23 bit + int64_t amplA : 17, //24 bit (3 byte) + reservedField4 : 1, //41 bit + amplC : 17, //42 bit. + reservedField5 : 1, //59 bit. + //in standard case(without __atribute__((packed)) macros, or packing by using union) + //here will be empty 4 bits, end next field("timeA") will start from 64 bit. + timeA : 9, //60 bit + reservedField6 : 1, //69 bit + timeC : 9, //70 bit + reservedField7 : 1, //79 bit + reservedField8 : 48; //80 bit + + void print() const + { + std::cout << std::hex; + std::cout << "################TCMdata###################" << std::endl; + std::cout << "orC: " << orC << std::endl; + std::cout << "orA: " << orA << std::endl; + std::cout << "sCen: " << sCen << std::endl; + std::cout << "cen: " << cen << std::endl; + std::cout << "vertex: " << vertex << std::endl; + std::cout << "nChanA: " << nChanA << std::endl; + std::cout << "nChanC: " << nChanC << std::endl; + std::cout << "amplA: " << amplA << std::endl; + std::cout << "amplC: " << amplC << std::endl; + std::cout << "timeA: " << timeA << std::endl; + std::cout << "timeC: " << timeC << std::endl; + std::cout << "##########################################" << std::endl; + + std::cout << std::dec; + } + + //temporary, this method should be in Triggers struct, TODO + void fillTrigger(Triggers& trg) + { + trg.triggersignals = ((bool)orA << Triggers::bitA) | + ((bool)orC << Triggers::bitC) | + ((bool)vertex << Triggers::bitVertex) | + ((bool)cen << Triggers::bitCen) | + ((bool)sCen << Triggers::bitSCen); + trg.nChanA = (int8_t)nChanA; + trg.nChanC = (int8_t)nChanC; + trg.amplA = (int32_t)amplA; + trg.amplC = (int32_t)amplC; + trg.timeA = (int16_t)timeA; + trg.timeC = (int16_t)timeC; + } +} __attribute__((__packed__)); + +struct TCMdataExtended { + static constexpr size_t PayloadSize = 4; + static constexpr size_t PayloadPerGBTword = 10; + static constexpr int MinNelements = 0; + static constexpr int MaxNelements = 20; union { - uint64_t word[2] = {0}; - struct { - uint64_t orC : 1, - orA : 1, - sCen : 1, - cen : 1, - vertex : 1, - nChanA : 7, - nChanC : 7; - int64_t amplA : 18, - amplC : 18, - reservedField1 : 1, //56B, PayloadSize1stWord 6 - timeA : 9, - timeC : 9, - reservedField2 : 46; - }; + uint32_t word[1] = {}; + uint32_t triggerWord; }; + + void print() const + { + + std::cout << std::hex; + std::cout << "############TCMdataExtended###############" << std::endl; + std::cout << "triggerWord: " << triggerWord << std::endl; + std::cout << "##########################################" << std::endl; + + std::cout << std::dec; + } }; class RawEventData { public: RawEventData() = default; - void generateHeader(int nChannels); - void generateData(); - void generateRandomHeader(int nChannels); - void generateRandomData(); - void generateRandomEvent(int nChannels); - EventHeader* getEventHeaderPtr() { return &mEventHeader; } - EventData* getEventDataPtr() { return mEventData; } void print(); - enum EEventDataBit { kNumberADC, - kIsDoubleEvent, - kIs1TimeLostEvent, - kIs2TimeLostEvent, - kIsADCinGate, - kIsTimeInfoLate, - kIsAmpHigh, - kIsEventInTVDC, - kIsTimeInfoLost }; const static int gStartDescriptor = 0x0000000f; int size() const @@ -169,7 +285,7 @@ class RawEventData TCMdata mTCMdata; bool mIsPadded = true; ///////////////////////////////////////////////// - ClassDefNV(RawEventData, 1); + ClassDefNV(RawEventData, 2); }; std::ostream& operator<<(std::ostream& stream, const RawEventData& data); @@ -208,8 +324,9 @@ class DataPageWriter void writePage() { - if (mBuffer.size() == 0) + if (mBuffer.size() == 0) { return; + } mPages.emplace_back(std::move(mBuffer)); LOG(DEBUG) << " writePage " << mBuffer.size(); mNpackets.push_back(mNpacketsInBuffer); diff --git a/DataFormats/Detectors/FIT/FT0/src/CTF.cxx b/DataFormats/Detectors/FIT/FT0/src/CTF.cxx new file mode 100644 index 0000000000000..7a8b5818d1d9f --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/src/CTF.cxx @@ -0,0 +1,35 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "Framework/Logger.h" +#include "DataFormatsFT0/CTF.h" + +using namespace o2::ft0; + +///________________________________ +void CompressedDigits::clear() +{ + trigger.clear(); + bcInc.clear(); + orbitInc.clear(); + nChan.clear(); + eventFlags.clear(); + + idChan.clear(); + qtcChain.clear(); + cfdTime.clear(); + qtcAmpl.clear(); + + header.nTriggers = 0; + header.firstOrbit = 0; + header.firstBC = 0; +} diff --git a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 682cc73aa12ee..3d480d5d96976 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -15,13 +15,19 @@ #pragma link off all functions; #pragma link C++ class o2::ft0::Digit + ; +#pragma link C++ class o2::ft0::DigitExt + ; #pragma link C++ class o2::ft0::DigitsTemp + ; #pragma link C++ class o2::ft0::ChannelData + ; #pragma link C++ class o2::ft0::Triggers + ; +#pragma link C++ class o2::ft0::DetTrigInput + ; +#pragma link C++ class o2::ft0::TriggersExt + ; #pragma link C++ class vector < o2::ft0::ChannelData> + ; #pragma link C++ class vector < o2::ft0::Digit> + ; +#pragma link C++ class vector < o2::ft0::DigitExt> + ; #pragma link C++ class vector < o2::ft0::DigitsTemp> + ; #pragma link C++ class vector < o2::ft0::Triggers> + ; +#pragma link C++ class vector < o2::ft0::DetTrigInput> + ; +#pragma link C++ class vector < o2::ft0::TriggersExt> + ; #pragma link C++ class o2::ft0::RecPoints + ; #pragma link C++ class vector < o2::ft0::RecPoints> + ; @@ -29,6 +35,7 @@ #pragma link C++ class vector < o2::ft0::ChannelDataFloat> + ; #pragma link C++ class o2::ft0::MCLabel + ; +#include "SimulationDataFormat/MCTruthContainer.h" #pragma link C++ class o2::dataformats::MCTruthContainer < o2::ft0::MCLabel> + ; #pragma link C++ class o2::ft0::HitType + ; @@ -38,5 +45,12 @@ #pragma link C++ class o2::ft0::EventHeader + ; #pragma link C++ class o2::ft0::EventData + ; #pragma link C++ class o2::ft0::Topo + ; +#pragma link C++ class o2::ft0::HVchannel + ; +#pragma link C++ class vector < o2::ft0::HVchannel> + ; + +#pragma link C++ class o2::ft0::CTFHeader + ; +#pragma link C++ class o2::ft0::CompressedDigits + ; +#pragma link C++ class o2::ft0::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::ft0::CTFHeader, 9, uint32_t> + ; #endif diff --git a/DataFormats/Detectors/FIT/FT0/src/DigitsTemp.cxx b/DataFormats/Detectors/FIT/FT0/src/DigitsTemp.cxx index 202b429ec31cb..10baf8fe41dc1 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DigitsTemp.cxx +++ b/DataFormats/Detectors/FIT/FT0/src/DigitsTemp.cxx @@ -17,9 +17,10 @@ void DigitsTemp::printStream(std::ostream& stream) const stream << "FT0 Digit: event time " << mTime << " BC " << mIntRecord.bc << " orbit " << mIntRecord.orbit << std::endl; stream << " A amp " << mTrigger.amplA << " C amp " << mTrigger.amplC << " time A " << mTrigger.timeA << " time C " << mTrigger.timeC << " signals " << int(mTrigger.triggersignals) << std::endl; - for (auto& chdata : mChDgDataArr) + for (auto& chdata : mChDgDataArr) { stream << "CH " << int(chdata.ChId) << " TIME " << chdata.CFDTime << " ns " << chdata.QTCAmpl << " mV " << " ADC chain " << int(chdata.ChainQTC) << std::endl; + } } std::ostream& operator<<(std::ostream& stream, const DigitsTemp& digi) diff --git a/DataFormats/Detectors/FIT/FT0/src/RawEventData.cxx b/DataFormats/Detectors/FIT/FT0/src/RawEventData.cxx index 835a51e3993cc..1ee3d9f348a03 100644 --- a/DataFormats/Detectors/FIT/FT0/src/RawEventData.cxx +++ b/DataFormats/Detectors/FIT/FT0/src/RawEventData.cxx @@ -15,96 +15,4 @@ using namespace o2::ft0; -using namespace std; - -ClassImp(RawEventData); - -void RawEventData::generateData() -{ - for (int iCh = 0; iCh < mEventHeader.nGBTWords * 2; iCh++) { - mEventData[iCh].channelID = iCh; - mEventData[iCh].charge = 1000; - mEventData[iCh].time = 500; - mEventData[iCh].is1TimeLostEvent = 1; - mEventData[iCh].is2TimeLostEvent = 1; - mEventData[iCh].isADCinGate = 1; - mEventData[iCh].isAmpHigh = 1; - mEventData[iCh].isDoubleEvent = 1; - mEventData[iCh].isEventInTVDC = 1; - mEventData[iCh].isTimeInfoLate = 1; - mEventData[iCh].isTimeInfoLost = 1; - mEventData[iCh].numberADC = 1; - } -} - -void RawEventData::generateHeader(int nChannels) -{ - mEventHeader.startDescriptor = 15; - mEventHeader.nGBTWords = (nChannels + 1) / 2; - mEventHeader.reservedField1 = 0; - mEventHeader.reservedField2 = 0; - mEventHeader.bc = 200; - mEventHeader.orbit = 100; -} - -void RawEventData::generateRandomHeader(int nChannels) -{ - mEventHeader.startDescriptor = 0x0000000f; - if (nChannels > 0 && nChannels < 13) - mEventHeader.nGBTWords = (nChannels + 1) / 2; - else - mEventHeader.nGBTWords = 1; - mEventHeader.bc = std::rand() % 2000; // 1999-max bc - mEventHeader.orbit = std::rand() % 100; -} - -void RawEventData::generateRandomData() -{ - for (int iCh = 0; iCh < mEventHeader.nGBTWords * 2; iCh++) { - mEventData[iCh].channelID = std::rand() % 208 + 1; - mEventData[iCh].charge = std::rand() % 1000; - mEventData[iCh].time = std::rand() % 500; - mEventData[iCh].is1TimeLostEvent = std::rand() % 2; - mEventData[iCh].is2TimeLostEvent = std::rand() % 2; - mEventData[iCh].isADCinGate = std::rand() % 2; - mEventData[iCh].isAmpHigh = std::rand() % 2; - mEventData[iCh].isDoubleEvent = std::rand() % 2; - mEventData[iCh].isEventInTVDC = std::rand() % 2; - mEventData[iCh].isTimeInfoLate = std::rand() % 2; - mEventData[iCh].isTimeInfoLost = std::rand() % 2; - mEventData[iCh].numberADC = std::rand() % 2; - } -} - -void RawEventData::generateRandomEvent(int nChannels) -{ - generateRandomHeader(nChannels); - generateRandomData(); -} - -void RawEventData::print() -{ - std::cout << "==================Raw event data==================" << endl; - std::cout << "##################Header##################" << endl; - std::cout << "startDescriptor: " << mEventHeader.startDescriptor << endl; - std::cout << "Nchannels: " << mEventHeader.nGBTWords * 2 << endl; - std::cout << "BC: " << mEventHeader.bc << endl; - std::cout << "Orbit: " << mEventHeader.orbit << endl; - std::cout << "##########################################" << endl; - std::cout << "###################DATA###################" << endl; - for (int iCh = 0; iCh < mEventHeader.nGBTWords * 2; iCh++) { - std::cout << "------------Channel " << mEventData[iCh].channelID << "------------" << endl; - std::cout << "Charge: " << mEventData[iCh].charge << endl; - std::cout << "Time: " << mEventData[iCh].time << endl; - std::cout << "1TimeLostEvent: " << mEventData[iCh].is1TimeLostEvent << endl; - std::cout << "2TimeLostEvent: " << mEventData[iCh].is2TimeLostEvent << endl; - std::cout << "ADCinGate: " << mEventData[iCh].isADCinGate << endl; - std::cout << "AmpHigh: " << mEventData[iCh].isAmpHigh << endl; - std::cout << "DoubleEvent: " << mEventData[iCh].isDoubleEvent << endl; - std::cout << "EventInTVDC: " << mEventData[iCh].isEventInTVDC << endl; - std::cout << "TimeInfoLate: " << mEventData[iCh].isTimeInfoLate << endl; - std::cout << "TimeInfoLost: " << mEventData[iCh].isTimeInfoLost << endl; - std::cout << "numberADC: " << mEventData[iCh].numberADC << endl; - } - std::cout << "##########################################" << endl; -} +ClassImp(RawEventData); \ No newline at end of file diff --git a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt index 23d4c88ae6b5d..faa74c2eaccff 100644 --- a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt @@ -12,11 +12,18 @@ o2_add_library(DataFormatsFV0 SOURCES src/Hit.cxx src/ChannelData.cxx src/BCData.cxx - PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat + src/RawEventData.cxx + src/CTF.cxx + PUBLIC_LINK_LIBRARIES O2::FV0Base + O2::SimulationDataFormat O2::CommonDataFormat ms_gsl::ms_gsl) o2_target_root_dictionary(DataFormatsFV0 HEADERS include/DataFormatsFV0/Hit.h include/DataFormatsFV0/BCData.h - include/DataFormatsFV0/ChannelData.h) + include/DataFormatsFV0/MCLabel.h + include/DataFormatsFV0/ChannelData.h + include/DataFormatsFV0/RawEventData.h + include/DataFormatsFV0/LookUpTable.h + include/DataFormatsFV0/CTF.h) diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/BCData.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/BCData.h index a1ef3a810340a..3025273b09f99 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/BCData.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/BCData.h @@ -15,6 +15,7 @@ #include "CommonDataFormat/RangeReference.h" #include <Rtypes.h> #include <gsl/span> +#include <bitset> /// \file BCData.h /// \brief Class to describe fired triggered and/or stored channels for the BC and to refer to channel data @@ -26,20 +27,76 @@ namespace fv0 { class ChannelData; +struct Triggers { + enum { + bitMinBias, + bitMinBiasInner, // experimental + bitMinBiasOuter, // experimental + bitHighMult, + bitDummy // non-defined yet, placeholder + }; + uint8_t triggerSignals = 0; // V0 trigger signals + int8_t nChanA = 0; // number of fired channels [A side] + int32_t amplA = -1000; // sum amplitude [A side] + Triggers() = default; + + Triggers(uint8_t signals, int8_t chanA, int32_t amplASum) + { + triggerSignals = signals; + nChanA = chanA; + amplA = amplASum; + } + + bool getIsMinBias() const { return (triggerSignals & (1 << bitMinBias)) != 0; } + bool getIsMinBiasInner() const { return (triggerSignals & (1 << bitMinBiasInner)) != 0; } + bool getIsMinBiasOuter() const { return (triggerSignals & (1 << bitMinBiasOuter)) != 0; } + bool getIsHighMult() const { return (triggerSignals & (1 << bitHighMult)) != 0; } + + void setTriggers(Bool_t isMinBias, Bool_t isMinBiasInner, Bool_t isMinBiasOuter, Bool_t isHighMult, Bool_t isDummy, int8_t chanA, int32_t amplASum) + { + triggerSignals = (isMinBias << bitMinBias) | (isMinBiasInner << bitMinBiasInner) | (isMinBiasOuter << bitMinBiasOuter) | (isHighMult << bitHighMult) | (isDummy << bitDummy); + nChanA = chanA; + amplA = amplASum; + } + + ClassDefNV(Triggers, 1); +}; + +struct DetTrigInput { + o2::InteractionRecord mIntRecord; // bc/orbit of the intpur + std::bitset<5> mInputs; // pattern of inputs. + DetTrigInput() = default; + DetTrigInput(const o2::InteractionRecord& iRec, Bool_t isMb, Bool_t isMbIn, Bool_t isMbOut, Bool_t isHm, Bool_t isDummy) + : mIntRecord(iRec), + mInputs((isMb << Triggers::bitMinBias) | + (isMbIn << Triggers::bitMinBiasInner) | + (isMbOut << Triggers::bitMinBiasOuter) | + (isHm << Triggers::bitHighMult) | + (isDummy << Triggers::bitDummy)) + { + } + ClassDefNV(DetTrigInput, 1); +}; + struct BCData { /// we are going to refer to at most 48 channels, so 6 bits for the number of channels and 26 for the reference o2::dataformats::RangeRefComp<6> ref; o2::InteractionRecord ir; + Triggers mTriggers; BCData() = default; - BCData(int first, int ne, o2::InteractionRecord iRec) + BCData(int first, int ne, o2::InteractionRecord iRec, const Triggers& chTrig) { ref.setFirstEntry(first); ref.setEntries(ne); ir = iRec; + mTriggers = chTrig; } gsl::span<const ChannelData> getBunchChannelData(const gsl::span<const ChannelData> tfdata) const; + const o2::InteractionRecord& getIntRecord() const { return ir; }; + Triggers getTriggers() const { return mTriggers; } + void setTriggers(Triggers triggers) { mTriggers = triggers; }; void print() const; ClassDefNV(BCData, 1); diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h new file mode 100644 index 0000000000000..d2c7be2fb6d4d --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h @@ -0,0 +1,78 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for FV0 CTF data + +#ifndef O2_FV0_CTF_H +#define O2_FV0_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" + +namespace o2 +{ +namespace fv0 +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nTriggers = 0; /// number of triggers in TF + uint32_t firstOrbit = 0; /// 1st orbit of TF + uint16_t firstBC = 0; /// 1st BC of TF + + ClassDefNV(CTFHeader, 1); +}; + +/// Intermediate, compressed but not yet entropy-encoded digits +struct CompressedDigits { + + CTFHeader header; + + // BC data + std::vector<uint16_t> bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector<uint32_t> orbitInc; // increment in orbit + std::vector<uint8_t> nChan; // number of fired channels + + // channel data + std::vector<uint8_t> idChan; // channels ID: 1st on absolute, then increment + std::vector<int16_t> time; // time + std::vector<int16_t> charge; // Amplitude + + CompressedDigits() = default; + + void clear(); + + ClassDefNV(CompressedDigits, 1); +}; + +/// wrapper for the Entropy-encoded clusters of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 6, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { + BLC_bcInc, // increment in BC + BLC_orbitInc, // increment in orbit + BLC_nChan, // number of fired channels + + BLC_idChan, // channels ID: 1st on absolute, then increment + BLC_time, // time + BLC_charge // amplitude + }; + + ClassDefNV(CTF, 1); +}; + +} // namespace fv0 +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h index 5bc80ee55ae83..5cda22e706ab8 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h @@ -24,7 +24,7 @@ namespace fv0 struct ChannelData { Short_t pmtNumber = -1; // PhotoMultiplier number (0 to 47) - Float_t time = -1; // [ns] Time associated with rising edge of the singal in a given channel + Short_t time = -1; // [ns] Time associated with rising edge of the singal in a given channel Short_t chargeAdc = -1; // ADC sample as present in raw data ChannelData() = default; diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/Hit.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/Hit.h index b31dc2b0813d4..d15d8af19f7cc 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/Hit.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/Hit.h @@ -43,16 +43,16 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> /// \param particlePdg PDG code of the partcile associated with the track inline Hit(int trackID, int cellID, - const Point3D<float>& startPos, - const Point3D<float>& endPos, - const Vector3D<float>& startMom, + const math_utils::Point3D<float>& startPos, + const math_utils::Point3D<float>& endPos, + const math_utils::Vector3D<float>& startMom, double startE, double endTime, double eLoss, Int_t particlePdg); // Entrance position getters - Point3D<Float_t> const& GetPosStart() const { return mPositionStart; } + math_utils::Point3D<Float_t> const& GetPosStart() const { return mPositionStart; } Float_t GetStartX() const { return mPositionStart.X(); } Float_t GetStartY() const { return mPositionStart.Y(); } Float_t GetStartZ() const { return mPositionStart.Z(); } @@ -65,8 +65,8 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> } // Momentum getters - Vector3D<Float_t> const& GetMomentum() const { return mMomentumStart; } - Vector3D<Float_t>& GetMomentum() { return mMomentumStart; } + math_utils::Vector3D<Float_t> const& GetMomentum() const { return mMomentumStart; } + math_utils::Vector3D<Float_t>& GetMomentum() { return mMomentumStart; } Float_t GetPx() const { return mMomentumStart.X(); } Float_t GetPy() const { return mMomentumStart.Y(); } Float_t GetPz() const { return mMomentumStart.Z(); } @@ -77,19 +77,19 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> void Print(const Option_t* opt) const; private: - Vector3D<float> mMomentumStart; ///< momentum at entrance - Point3D<float> 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 + math_utils::Vector3D<float> mMomentumStart; ///< momentum at entrance + math_utils::Point3D<float> 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, 2); }; Hit::Hit(int trackID, int detID, - const Point3D<float>& startPos, - const Point3D<float>& endPos, - const Vector3D<float>& startMom, + const math_utils::Point3D<float>& startPos, + const math_utils::Point3D<float>& endPos, + const math_utils::Vector3D<float>& startMom, double startE, double endTime, double eLoss, diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h new file mode 100644 index 0000000000000..d87375bacde84 --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h @@ -0,0 +1,116 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 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 <Rtypes.h> +#include <cassert> +#include <iostream> +#include <iomanip> // std::setfill, std::setw - for stream formating +#include <Framework/Logger.h> +#include "FV0Base/Constants.h" + +namespace o2 +{ +namespace fv0 +{ + +struct Topo { + int pmLink = 0; // Number of Processing Module, associated with GBT link ID + int pmCh = 0; // Channel within the Processing Module in range from 0-11 + ClassDefNV(Topo, 1); +}; + +inline bool operator<(Topo const& a, Topo const& b) +{ + return (a.pmLink < b.pmLink || (a.pmLink == b.pmLink && a.pmCh < b.pmCh)); +} + +class LookUpTable +{ + public: + /// + /// Default constructor. + /// It must be kept public for root persistency purposes, + /// but should never be called by the outside world + LookUpTable() = default; + ~LookUpTable() = default; + + explicit LookUpTable(bool fillLinearly) + : mTopoVector(Constants::nPms * Constants::nChannelsPerPm, {0, 0}), + mInvTopo(mTopoVector.size(), 0) + { + if (fillLinearly) { + LOG(INFO) << "Mapping of global channel and (PM, PM channel) pair"; + for (int link = 0; link < Constants::nPms; ++link) { + for (int ch = 0; ch < Constants::nChannelsPerPm; ++ch) { + mTopoVector[link * Constants::nChannelsPerPm + ch] = o2::fv0::Topo{link, ch}; + } + } + } else { + // TODO: If needed: implement more realistic splitting: 1 ring -> 1 PM instead of linear + LOG(WARNING) << "Don't use it - not implemented yet."; + } + + // Fill inverted LUT - matters only if LUT is not linear + for (size_t channel = 0; channel < mTopoVector.size(); ++channel) { + mInvTopo[getIdx(mTopoVector[channel].pmLink, mTopoVector[channel].pmCh)] = channel; + } + } + + int getChannel(int link, int mcp) const { return mInvTopo[getIdx(link, mcp)]; } + int getLink(int channel) const { return mTopoVector[channel].pmLink; } + int getPmChannel(int channel) const { return mTopoVector[channel].pmCh; } + int getTcmLink() const { return mLinkTCM; } + void printFullMap() const + { + std::cout << "o2::fv0::LookUpTable::printFullMap(): mTopoVector: [globalCh link pmCh]" << std::endl; + for (size_t channel = 0; channel < mTopoVector.size(); ++channel) { + std::cout << " " << std::right << std::setw(2) << channel << " "; + std::cout << std::right << std::setw(2) << mTopoVector[channel].pmLink << " "; + std::cout << std::right << std::setw(3) << mTopoVector[channel].pmCh << std::endl; + } + std::cout << "o2::fv0::LookUpTable::printFullMap(): mInvTopo: [idx globalCh link pmCh]" << std::endl; + for (size_t idx = 0; idx < mInvTopo.size(); ++idx) { + std::cout << " " << std::right << std::setw(3) << idx << " "; + std::cout << std::right << std::setw(3) << mInvTopo[idx] << " "; + std::cout << std::right << std::setw(2) << getLinkFromIdx(mInvTopo[idx]) << " "; + std::cout << std::right << std::setw(2) << getPmChannelFromIdx(mInvTopo[idx]) << std::endl; + } + } + + private: + std::vector<Topo> mTopoVector; // iterator of each vector element gives the global channel number + std::vector<int> mInvTopo; // each element is an iterator of mTopoVector + static constexpr int mLinkTCM = Constants::nPms; + + static int getIdx(int link, int pmCh) + { + assert(pmCh < Constants::nChannelsPerPm); + return link * Constants::nChannelsPerPm + pmCh; + } + static int getLinkFromIdx(int idx) { return idx / Constants::nChannelsPerPm; } + static int getPmChannelFromIdx(int idx) { return idx % Constants::nChannelsPerPm; } + + ClassDefNV(LookUpTable, 1); +}; + +} // namespace fv0 +} // namespace o2 +#endif diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/MCLabel.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/MCLabel.h similarity index 100% rename from Detectors/FIT/FV0/simulation/include/FV0Simulation/MCLabel.h rename to DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/MCLabel.h diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RawEventData.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RawEventData.h new file mode 100644 index 0000000000000..383f46e9fba07 --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RawEventData.h @@ -0,0 +1,187 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Based on the FT0 file RawEventData.h (almost identical except additional printing) by +// Alla.Maevskaya@cern.ch +// with Artur.Furs +// +#ifndef ALICEO2_FV0_RAWEVENTDATA_H_ +#define ALICEO2_FV0_RAWEVENTDATA_H_ + +#include "FV0Base/Constants.h" +#include "DataFormatsFV0/ChannelData.h" +#include "Headers/RAWDataHeader.h" +#include "DataFormatsFV0/LookUpTable.h" +#include <CommonDataFormat/InteractionRecord.h> +#include <Framework/Logger.h> +#include <iostream> +#include <iomanip> +#include <cstring> +#include "Rtypes.h" + +namespace o2 +{ +namespace fv0 +{ +struct EventHeader { + static constexpr int PayloadSize = 16; + union { + uint64_t word[2] = {}; + struct { + uint64_t bc : 12; + uint64_t orbit : 32; + uint64_t reservedField1 : 20; + uint64_t reservedField2 : 8; + uint64_t nGBTWords : 4; + uint64_t startDescriptor : 4; + uint64_t reservedField3 : 48; + }; + }; +}; +struct EventData { + union { + uint64_t word = {0}; + struct { + int64_t time : 12; + int64_t charge : 13; + uint64_t numberADC : 1, + isDoubleEvent : 1, + is1TimeLostEvent : 1, + is2TimeLostEvent : 1, + isADCinGate : 1, + isTimeInfoLate : 1, + isAmpHigh : 1, + isEventInTVDC : 1, + isTimeInfoLost : 1, + reservedField : 2, + channelID : 4; + }; + }; + uint64_t word_zeros = 0x0; + static const size_t PayloadSizeSecondWord = 11; + static const size_t PayloadSizeFirstWord = 5; + void generateFlags() + { + numberADC = std::rand() % 2; + isDoubleEvent = 0; + is1TimeLostEvent = 0; + is2TimeLostEvent = 1; + isADCinGate = 0; + isTimeInfoLate = 0; + isAmpHigh = 0; + isEventInTVDC = 1; + isTimeInfoLost = 0; + } +}; + +struct TCMdata { + static constexpr int PayloadSize = 16; + union { + uint64_t word[2] = {0}; + struct { + uint64_t orC : 1, + orA : 1, + sCen : 1, + cen : 1, + vertex : 1, + nChanA : 7, + nChanC : 7; + int64_t amplA : 18, + amplC : 18, + reservedField1 : 1, //56B, PayloadSize1stWord 6 + timeA : 9, + timeC : 9, + reservedField2 : 46; + }; + }; +}; + +class RawEventData +{ + public: + RawEventData() = default; + EventHeader* getEventHeaderPtr() { return &mEventHeader; } + EventData* getEventDataPtr() { return mEventData; } + void print(); + void printHexEventHeader(); + void printHexEventData(uint64_t i); + enum EEventDataBit { kNumberADC, + kIsDoubleEvent, + kIs1TimeLostEvent, + kIs2TimeLostEvent, + kIsADCinGate, + kIsTimeInfoLate, + kIsAmpHigh, + kIsEventInTVDC, + kIsTimeInfoLost }; + const static int gStartDescriptor = 0x0000000f; + + int size() const + { + return 1 + mEventHeader.nGBTWords; // EventHeader + EventData size + } + + std::vector<char> to_vector(bool tcm) + { + constexpr int CRUWordSize = 16; + + std::vector<char> result(size() * CRUWordSize); + char* out = result.data(); + if (!tcm) { + std::memcpy(out, &mEventHeader, EventHeader::PayloadSize); + out += EventHeader::PayloadSize; + LOG(DEBUG) << " Write PM header: nWords: " << (int)mEventHeader.nGBTWords + << " orbit: " << int(mEventHeader.orbit) + << " BC: " << int(mEventHeader.bc) + << " size: " << result.size(); + printHexEventHeader(); + out += CRUWordSize - EventHeader::PayloadSize; // Padding enabled + + for (uint64_t i = 0; i < mEventHeader.nGBTWords; ++i) { + std::memcpy(out, &mEventData[2 * i], EventData::PayloadSizeFirstWord); + out += EventData::PayloadSizeFirstWord; + LOG(DEBUG) << " 1st word: Ch: " << std::setw(2) << mEventData[2 * i].channelID + << " charge: " << std::setw(4) << mEventData[2 * i].charge + << " time: " << std::setw(4) << mEventData[2 * i].time; + std::memcpy(out, &mEventData[2 * i + 1], EventData::PayloadSizeSecondWord); + out += EventData::PayloadSizeSecondWord; + LOG(DEBUG) << " 2nd word: Ch: " << std::setw(2) << mEventData[2 * i + 1].channelID + << " charge: " << std::setw(4) << mEventData[2 * i + 1].charge + << " time: " << std::setw(4) << mEventData[2 * i + 1].time; + + out += CRUWordSize - EventData::PayloadSizeSecondWord - EventData::PayloadSizeFirstWord; + printHexEventData(i); + } + } else { + // TCM data + std::memcpy(out, &mEventHeader, EventHeader::PayloadSize); + out += EventHeader::PayloadSize; + LOG(DEBUG) << " Write TCM header: nWords: " << (int)mEventHeader.nGBTWords + << " orbit: " << int(mEventHeader.orbit) + << " BC: " << int(mEventHeader.bc) + << " size: " << result.size(); + std::memcpy(out, &mTCMdata, sizeof(TCMdata)); + out += sizeof(TCMdata); + // TODO: No TCM payload printing until the meaning of trigger bits and other flags is clarified + } + return result; + } + + public: + EventHeader mEventHeader; + EventData mEventData[Constants::nChannelsPerPm]; + TCMdata mTCMdata; + + ClassDefNV(RawEventData, 1); +}; + +} // namespace fv0 +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/FV0/src/CTF.cxx b/DataFormats/Detectors/FIT/FV0/src/CTF.cxx new file mode 100644 index 0000000000000..8b85884c7e091 --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/src/CTF.cxx @@ -0,0 +1,32 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "Framework/Logger.h" +#include "DataFormatsFV0/CTF.h" + +using namespace o2::fv0; + +///________________________________ +void CompressedDigits::clear() +{ + bcInc.clear(); + orbitInc.clear(); + nChan.clear(); + + idChan.clear(); + time.clear(); + charge.clear(); + + header.nTriggers = 0; + header.firstOrbit = 0; + header.firstBC = 0; +} diff --git a/DataFormats/Detectors/FIT/FV0/src/ChannelData.cxx b/DataFormats/Detectors/FIT/FV0/src/ChannelData.cxx index 890ca7fbc1edb..7507b29c0f033 100644 --- a/DataFormats/Detectors/FIT/FV0/src/ChannelData.cxx +++ b/DataFormats/Detectors/FIT/FV0/src/ChannelData.cxx @@ -14,5 +14,5 @@ using namespace o2::fv0; void ChannelData::print() const { - printf(" Pmt=%2d | time =%8.3f | charge =%6d\n", pmtNumber, time, chargeAdc); + printf(" Pmt=%2d | time =%4d | charge =%6d\n", pmtNumber, time, chargeAdc); } diff --git a/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h b/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h index 7682d1dcdc91c..ae77b276b8399 100644 --- a/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h +++ b/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h @@ -16,10 +16,25 @@ #pragma link C++ class o2::fv0::Hit + ; #pragma link C++ class vector < o2::fv0::Hit> + ; +#pragma link C++ class o2::fv0::MCLabel + ; #pragma link C++ class o2::fv0::ChannelData + ; #pragma link C++ class o2::fv0::BCData + ; +#pragma link C++ class o2::fv0::Triggers + ; +#pragma link C++ class o2::fv0::DetTrigInput + ; #pragma link C++ class std::vector < o2::fv0::ChannelData> + ; +#pragma link C++ class std::vector < o2::fv0::Triggers> + ; +#pragma link C++ class std::vector < o2::fv0::DetTrigInput> + ; #pragma link C++ class std::vector < o2::fv0::BCData> + ; +#pragma link C++ class o2::fv0::RawEventData + ; +#pragma link C++ class o2::fv0::EventHeader + ; +#pragma link C++ class o2::fv0::EventData + ; +#pragma link C++ class o2::fv0::TCMdata + ; +#pragma link C++ class o2::fv0::Topo + ; + +#pragma link C++ class o2::fv0::CTFHeader + ; +#pragma link C++ class o2::fv0::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::fv0::CTFHeader, 6, uint32_t> + ; + #endif diff --git a/DataFormats/Detectors/FIT/FV0/src/RawEventData.cxx b/DataFormats/Detectors/FIT/FV0/src/RawEventData.cxx new file mode 100644 index 0000000000000..022b003d0ab43 --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/src/RawEventData.cxx @@ -0,0 +1,71 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/RawEventData.h" +#include <sstream> + +using namespace o2::fv0; + +ClassImp(RawEventData); + +void RawEventData::print() +{ + std::cout << "==================Raw event data==================" << std::endl; + std::cout << "##################Header##################" << std::endl; + std::cout << "startDescriptor: " << mEventHeader.startDescriptor << std::endl; + std::cout << "Nchannels: " << mEventHeader.nGBTWords * 2 << std::endl; + std::cout << "BC: " << mEventHeader.bc << std::endl; + std::cout << "Orbit: " << mEventHeader.orbit << std::endl; + std::cout << "##########################################" << std::endl; + std::cout << "###################DATA###################" << std::endl; + for (int iCh = 0; iCh < mEventHeader.nGBTWords * 2; iCh++) { + std::cout << "------------Channel " << mEventData[iCh].channelID << "------------" << std::endl; + std::cout << "Charge: " << mEventData[iCh].charge << std::endl; + std::cout << "Time: " << mEventData[iCh].time << std::endl; + std::cout << "1TimeLostEvent: " << mEventData[iCh].is1TimeLostEvent << std::endl; + std::cout << "2TimeLostEvent: " << mEventData[iCh].is2TimeLostEvent << std::endl; + std::cout << "ADCinGate: " << mEventData[iCh].isADCinGate << std::endl; + std::cout << "AmpHigh: " << mEventData[iCh].isAmpHigh << std::endl; + std::cout << "DoubleEvent: " << mEventData[iCh].isDoubleEvent << std::endl; + std::cout << "EventInTVDC: " << mEventData[iCh].isEventInTVDC << std::endl; + std::cout << "TimeInfoLate: " << mEventData[iCh].isTimeInfoLate << std::endl; + std::cout << "TimeInfoLost: " << mEventData[iCh].isTimeInfoLost << std::endl; + std::cout << "numberADC: " << mEventData[iCh].numberADC << std::endl; + } + std::cout << "##########################################" << std::endl; +} + +void RawEventData::printHexEventHeader() +{ + std::stringstream ssheader; + ssheader << std::setfill('0') << std::setw(16) << std::hex << mEventHeader.word[0] << " " << std::setw(16) << mEventHeader.word[1] << "\n "; + ssheader << std::setw(3) << (0x00000fff & mEventHeader.bc) << " " + << std::setw(8) << (0xffffffff & mEventHeader.orbit) << " " + << std::setw(5) << (0x000fffff & mEventHeader.reservedField1) << " " + << std::setw(2) << (0x000000ff & mEventHeader.reservedField2) << " " + << std::setw(1) << (0x0000000f & mEventHeader.nGBTWords) << " " + << std::setw(1) << (0x0000000f & mEventHeader.startDescriptor) << " " + << std::setw(12) << (0xffffffffffff & mEventHeader.reservedField3); + LOG(DEBUG) << ssheader.str(); +} + +void RawEventData::printHexEventData(uint64_t i) +{ + std::stringstream ssdata; + ssdata << "D0:0x "; + ssdata << std::setfill('0') << std::hex << std::setw(16) << mEventData[2 * i].word << "\n "; + ssdata << std::setw(3) << (0x0fff & mEventData[2 * i].time) << " " + << std::setw(8) << (0x1fff & mEventData[2 * i].charge) << "\n "; + ssdata << "D1:0x "; + ssdata << std::setfill('0') << std::hex << std::setw(16) << mEventData[2 * i + 1].word << "\n "; + ssdata << std::setw(3) << (0x0fff & mEventData[2 * i + 1].time) << " " + << std::setw(8) << (0x1fff & mEventData[2 * i + 1].charge); + LOG(DEBUG) << " | " << ssdata.str(); +} diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 03f2d16a1ff81..2bed2f9be28be 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -37,13 +37,13 @@ class TrackITS : public o2::track::TrackParCov public: using o2::track::TrackParCov::TrackParCov; // inherit base constructors - static constexpr int MaxClusters = 7; + static constexpr int MaxClusters = 16; TrackITS() = default; TrackITS(const TrackITS& t) = default; - TrackITS(const o2::track::TrackParCov& parcov) : TrackParCov{parcov} {} - TrackITS(const o2::track::TrackParCov& parCov, float chi2, std::uint32_t rof, const o2::track::TrackParCov& outer) - : o2::track::TrackParCov{parCov}, mChi2{chi2}, mROFrame{rof}, mParamOut{outer} {} + TrackITS(const o2::track::TrackParCov& parcov) : o2::track::TrackParCov{parcov} {} + TrackITS(const o2::track::TrackParCov& parCov, float chi2, const o2::track::TrackParCov& outer) + : o2::track::TrackParCov{parCov}, mParamOut{outer}, mChi2{chi2} {} TrackITS& operator=(const TrackITS& tr) = default; ~TrackITS() = default; @@ -82,33 +82,34 @@ class TrackITS : public o2::track::TrackParCov void setChi2(float chi2) { mChi2 = chi2; } - std::uint32_t getROFrame() const { return mROFrame; } - void setROFrame(std::uint32_t f) { mROFrame = f; } bool isBetter(const TrackITS& best, float maxChi2) const; o2::track::TrackParCov& getParamOut() { return mParamOut; } const o2::track::TrackParCov& getParamOut() const { return mParamOut; } + void setPattern(uint16_t p) { mPattern = p; } + int getPattern() const { return mPattern; } + bool hasHitOnLayer(int i) { return mPattern & (0x1 << i); } + private: - float mMass = 0.139; ///< Assumed mass for this track - float mChi2 = 0.; ///< Chi2 for this track - std::uint32_t mROFrame = 0; ///< RO Frame o2::track::TrackParCov mParamOut; ///< parameter at largest radius ClusRefs mClusRef; ///< references on clusters + float mChi2 = 0.; ///< Chi2 for this track + uint16_t mPattern = 0; ///< layers pattern - ClassDefNV(TrackITS, 3); + ClassDefNV(TrackITS, 4); }; class TrackITSExt : public TrackITS { ///< heavy version of TrackITS, with clusters embedded public: - static constexpr int MaxClusters = 7; + static constexpr int MaxClusters = 16; /// Prepare for overlaps and new detector configurations using TrackITS::TrackITS; // inherit base constructors - TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, std::uint32_t rof, + TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, o2::track::TrackParCov&& outer, std::array<int, MaxClusters> cls) - : TrackITS(parCov, chi2, rof, outer), mIndex{cls} + : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } @@ -131,8 +132,8 @@ class TrackITSExt : public TrackITS } private: - std::array<int, MaxClusters> mIndex = {-1}; ///< Indices of associated clusters - ClassDefNV(TrackITSExt, 1); + std::array<int, MaxClusters> mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters + ClassDefNV(TrackITSExt, 2); }; } // namespace its } // namespace o2 diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx index 9deda82f98906..9ab6eba05f784 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx @@ -70,10 +70,11 @@ void TrackITS::getImpactParams(Float_t x, Float_t y, Float_t z, Float_t bz, Floa Bool_t TrackITS::propagate(Float_t alpha, Float_t x, Float_t bz) { - if (rotate(alpha)) - if (propagateTo(x, bz)) + if (rotate(alpha)) { + if (propagateTo(x, bz)) { return kTRUE; - + } + } return kFALSE; } diff --git a/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h b/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h index 9ce67d44855d8..bcd6cd04b77f5 100644 --- a/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h +++ b/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h @@ -22,20 +22,15 @@ #include "CommonDataFormat/RangeReference.h" #include "SimulationDataFormat/MCCompLabel.h" +#include "ReconstructionDataFormats/TrackFwd.h" namespace o2 { -namespace itsmft -{ -class Cluster; -} - namespace mft { -class TrackMFT +class TrackMFT : public o2::track::TrackParCovFwd { - using Cluster = o2::itsmft::Cluster; using ClusRefs = o2::dataformats::RangeRefComp<4>; using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>; using SMatrix5 = ROOT::Math::SVector<Double_t, 5>; @@ -43,153 +38,49 @@ class TrackMFT public: TrackMFT() = default; TrackMFT(const TrackMFT& t) = default; - TrackMFT(const Double_t Z, const SMatrix5 parameters, const SMatrix55 covariances, const Double_t chi2); - ~TrackMFT() = default; - /// return Z coordinate (cm) - Double_t getZ() const { return mZ; } - /// set Z coordinate (cm) - void setZ(Double_t z) { mZ = z; } - Double_t getX() const { return mParameters(0); } - void setX(Double_t x) { mParameters(0) = x; } - Double_t getSigmaX() const { return mCovariances(0, 0); } - - Double_t getY() const { return mParameters(1); } - void setY(Double_t y) { mParameters(1) = y; } - Double_t getSigmaY() const { return mCovariances(1, 1); } - - void setPhi(Double_t phi) { mParameters(2) = phi; } - Double_t getPhi() const { return mParameters(2); } - Double_t getSigmaPhi() const { return mCovariances(2, 2); } - - void setTanl(Double_t tanl) { mParameters(3) = tanl; } - Double_t getTanl() const { return mParameters(3); } - Double_t getSigmaTanl() const { return mCovariances(3, 3); } - - void setInvQPt(Double_t invqpt) { mParameters(4) = invqpt; } - Double_t getInvQPt() const { return mParameters(4); } // return Inverse charged pt - Double_t getPt() const { return TMath::Abs(1.f / mParameters(4)); } - Double_t getInvPt() const { return TMath::Abs(mParameters(4)); } - Double_t getSigmaInvQPt() const { return mCovariances(4, 4); } - - // Charge and momentum from quadratic regression of clusters X,Y positions - void setInvQPtQuadtratic(Double_t invqpt) { mInvQPtQuadtratic = invqpt; } - const Double_t getInvQPtQuadtratic() const { return mInvQPtQuadtratic; } // Inverse charged pt - const Double_t getPtQuadtratic() const { return TMath::Abs(1.f / getInvQPtQuadtratic()); } - const Double_t getChargeQuadratic() const { return TMath::Sign(1., getInvQPtQuadtratic()); } - void setChi2QPtQuadtratic(Double_t chi2) { mQuadraticFitChi2 = chi2; } - const Double_t getChi2QPtQuadtratic() const { return mQuadraticFitChi2; } - - Double_t getPx() const { return TMath::Cos(getPhi()) * getPt(); } // return px - Double_t getInvPx() const { return 1. / getPx(); } // return invpx - - Double_t getPy() const { return TMath::Sin(getPhi()) * getPt(); } // return py - Double_t getInvPy() const { return 1. / getPx(); } // return invpy - - Double_t getPz() const { return getTanl() * getPt(); } // return pz - Double_t getInvPz() const { return 1. / getPz(); } // return invpz - - Double_t getP() const { return getPt() * TMath::Sqrt(1. + getTanl() * getTanl()); } // return total momentum - Double_t getInverseMomentum() const { return 1.f / getP(); } - - Double_t getEta() const { return -TMath::Log(TMath::Tan((TMath::PiOver2() - TMath::ATan(getTanl())) / 2)); } // return total momentum - - /// return the charge (assumed forward motion) - Double_t getCharge() const { return TMath::Sign(1., mParameters(4)); } - /// set the charge (assumed forward motion) - void setCharge(Double_t charge) - { - if (charge * mParameters(4) < 0.) - mParameters(4) *= -1.; - } - - /// return track parameters - const SMatrix5& getParameters() const { return mParameters; } - /// set track parameters - void setParameters(const SMatrix5& parameters) { mParameters = parameters; } - - const SMatrix55& getCovariances() const; - void setCovariances(const SMatrix55& covariances); + // Track finding method + void setCA(Bool_t method = true) { mIsCA = method; } + const Bool_t isCA() const { return mIsCA; } + const Bool_t isLTF() const { return !mIsCA; } - /// return the chi2 of the track when the associated cluster was attached - Double_t getTrackChi2() const { return mTrackChi2; } - /// set the chi2 of the track when the associated cluster was attached - void setTrackChi2(Double_t chi2) { mTrackChi2 = chi2; } + // Tracking seed charge and momentum from Fast Circle Fit of clusters X,Y positions + void setInvQPtSeed(Double_t invqpt) { mInvQPtSeed = invqpt; } + const Double_t getInvQPtSeed() const { return mInvQPtSeed; } // Inverse charged pt + const Double_t getPtSeed() const { return TMath::Abs(1.f / getInvQPtSeed()); } + const Double_t getChargeSeed() const { return TMath::Sign(1., getInvQPtSeed()); } + void setChi2QPtSeed(Double_t chi2) { mSeedinvQPtFitChi2 = chi2; } + const Double_t getChi2QPtSeed() const { return mSeedinvQPtFitChi2; } - // Other functions - int getNumberOfClusters() const { return mClusRef.getEntries(); } - int getFirstClusterEntry() const { return mClusRef.getFirstEntry(); } - int getClusterEntry(int i) const { return getFirstClusterEntry() + i; } - void shiftFirstClusterEntry(int bias) - { - mClusRef.setFirstEntry(mClusRef.getFirstEntry() + bias); - } - void setFirstClusterEntry(int offs) - { - mClusRef.setFirstEntry(offs); - } - void setNumberOfClusters(int n) - { - mClusRef.setEntries(n); - } - void setClusterRefs(int firstEntry, int n) - { - mClusRef.set(firstEntry, n); - } + std::uint32_t getROFrame() const { return mROFrame; } + void setROFrame(std::uint32_t f) { mROFrame = f; } - const std::array<MCCompLabel, 10>& getMCCompLabels() const { return mMCCompLabels; } // constants::mft::LayersNumber = 10 - void setMCCompLabels(const std::array<MCCompLabel, 10>& labels, int nPoints) - { - mMCCompLabels = labels; - mNPoints = nPoints; - } + const int getNumberOfPoints() const { return mClusRef.getEntries(); } - const ClusRefs& getClusterRefs() const { return mClusRef; } - ClusRefs& getClusterRefs() { return mClusRef; } + const int getExternalClusterIndexOffset() const { return mClusRef.getFirstEntry(); } - std::uint32_t getROFrame() const { return mROFrame; } - void setROFrame(std::uint32_t f) { mROFrame = f; } + void setExternalClusterIndexOffset(int offset = 0) { mClusRef.setFirstEntry(offset); } - const Int_t getNPoints() const { return mNPoints; } + void setNumberOfPoints(int n) { mClusRef.setEntries(n); } void print() const; - void printMCCompLabels() const; - // Extrapolate this track to - void extrapHelixToZ(double zEnd, double Field); + const o2::track::TrackParCovFwd& getOutParam() const { return mOutParameters; } + void setOutParam(const o2::track::TrackParCovFwd parcov) { mOutParameters = parcov; } private: - std::uint32_t mROFrame = 0; ///< RO Frame - Int_t mNPoints{0}; // Number of clusters - std::array<MCCompLabel, 10> mMCCompLabels; // constants::mft::LayersNumber = 10 + std::uint32_t mROFrame = 0; ///< RO Frame + Bool_t mIsCA = false; // Track finding method CA vs. LTF ClusRefs mClusRef; ///< references on clusters - Double_t mZ = 0.; ///< Z coordinate (cm) - - /// Track parameters ordered as follow: <pre> - /// X = X coordinate (cm) - /// Y = Y coordinate (cm) - /// PHI = azimutal angle - /// TANL = tangent of \lambda (dip angle) - /// INVQPT = Inverse transverse momentum (GeV/c ** -1) times charge (assumed forward motion) </pre> - SMatrix5 mParameters; ///< \brief Track parameters - - /// Covariance matrix of track parameters, ordered as follows: <pre> - /// <X,X> <Y,X> <PHI,X> <TANL,X> <INVQPT,X> - /// <X,Y> <Y,Y> <PHI,Y> <TANL,Y> <INVQPT,Y> - /// <X,PHI> <Y,PHI> <PHI,PHI> <TANL,PHI> <INVQPT,PHI> - /// <X,TANL> <Y,TANL> <PHI,TANL> <TANL,TANL> <INVQPT,TANL> - /// <X,INVQPT> <Y,INVQPT> <PHI,INVQPT> <TANL,INVQPT> <INVQPT,INVQPT> </pre> - SMatrix55 mCovariances; ///< \brief Covariance matrix of track parameters - Double_t mTrackChi2 = 0.; ///< Chi2 of the track when the associated cluster was attached - - // Results from quadratic regression of clusters X,Y positions - // Chi2 of the quadratic regression used to estimate track pT and charge - Double_t mQuadraticFitChi2 = 0.; - // inversed charged momentum from quadratic regression - Double_t mInvQPtQuadtratic; + // Outward parameters for MCH matching + o2::track::TrackParCovFwd mOutParameters; + + // Seed InveQPt and Chi2 from fitting clusters X,Y positions + Double_t mSeedinvQPtFitChi2 = 0.; + Double_t mInvQPtSeed; ClassDefNV(TrackMFT, 1); }; @@ -198,28 +89,22 @@ class TrackMFTExt : public TrackMFT { ///< heavy version of TrackMFT, with clusters embedded public: + TrackMFTExt() = default; + TrackMFTExt(const TrackMFTExt& t) = default; + ~TrackMFTExt() = default; static constexpr int MaxClusters = 10; using TrackMFT::TrackMFT; // inherit base constructors - void setClusterIndex(int l, int i) - { - int ncl = getNumberOfClusters(); - mIndex[ncl++] = (l << 28) + i; - getClusterRefs().setEntries(ncl); - } - - int getClusterIndex(int lr) const { return mIndex[lr]; } + int getExternalClusterIndex(int i) const { return mExtClsIndex[i]; } - void setExternalClusterIndex(int layer, int idx, bool newCluster = false) + void setExternalClusterIndex(int np, int idx) { - if (newCluster) { - getClusterRefs().setEntries(getNumberOfClusters() + 1); - } - mIndex[layer] = idx; + mExtClsIndex[np] = idx; } - private: - std::array<int, MaxClusters> mIndex = {-1}; ///< Indices of associated clusters + protected: + std::array<int, MaxClusters> mExtClsIndex = {-1}; ///< External indices of associated clusters + ClassDefNV(TrackMFTExt, 1); }; } // namespace mft diff --git a/DataFormats/Detectors/ITSMFT/MFT/src/TrackMFT.cxx b/DataFormats/Detectors/ITSMFT/MFT/src/TrackMFT.cxx index 9661919be665f..814329b02e3f3 100644 --- a/DataFormats/Detectors/ITSMFT/MFT/src/TrackMFT.cxx +++ b/DataFormats/Detectors/ITSMFT/MFT/src/TrackMFT.cxx @@ -17,12 +17,6 @@ #include "CommonConstants/MathConstants.h" #include "Framework/Logger.h" #include "MathUtils/Utils.h" -#include <TMath.h> -#include "DataFormatsITSMFT/Cluster.h" - -using namespace o2::mft; -using namespace o2::itsmft; -using namespace o2::constants::math; namespace o2 { @@ -32,64 +26,6 @@ namespace mft using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>; using SMatrix5 = ROOT::Math::SVector<Double_t, 5>; -//__________________________________________________________________________ -TrackMFT::TrackMFT(const Double_t z, const SMatrix5 parameters, const SMatrix55 covariances, const Double_t chi2) -{ - mZ = z; - mParameters = parameters; - mCovariances = covariances; - mTrackChi2 = chi2; -} - -//__________________________________________________________________________ -const SMatrix55& TrackMFT::getCovariances() const -{ - /// Return the covariance matrix - return mCovariances; -} - -//__________________________________________________________________________ -void TrackMFT::setCovariances(const SMatrix55& covariances) -{ - mCovariances = covariances; -} - -//_________________________________________________________________________________________________ -void TrackMFT::extrapHelixToZ(double zEnd, double Field) -{ - /// Track extrapolated to the plane at "zEnd" considering a helix - - if (getZ() == zEnd) { - return; // nothing to be done if same z - } - - // Extrapolate track parameters - double dZ = (zEnd - getZ()); // Propagate in meters - double tanl0 = getTanl(); - double invtanl0 = 1.0 / tanl0; - double px0 = getPx(); - double py0 = getPy(); - double invqpt0 = getInvQPt(); - auto q = getCharge(); - auto Hz = std::copysign(1.0, Field); - double k = TMath::Abs(o2::constants::math::B2C * Field); - auto invk = 1.0 / k; - double theta = -invqpt0 * dZ * k * invtanl0; - double costheta, sintheta; - o2::utils::sincos(theta, sintheta, costheta); - - double deltax = Hz * py0 * invk * (1.0 - costheta) - px0 * q * invk * sintheta; - double deltay = -Hz * px0 * invk * (1.0 - costheta) - py0 * q * invk * sintheta; - double x = getX() + deltax; - double y = getY() + deltay; - double deltaphi = +dZ * k * invqpt0 * invtanl0; - double phi = getPhi() + theta; - setX(x); - setY(y); - setZ(zEnd); - setPhi(phi); -} - //__________________________________________________________________________ void TrackMFT::print() const { @@ -103,12 +39,6 @@ void TrackMFT::print() const << " chi2 = " << std::setw(5) << std::setprecision(3) << getTrackChi2() << std::endl; } -//__________________________________________________________________________ -void TrackMFT::printMCCompLabels() const -{ - /// Printing TrackMFT MCLabel information - LOG(INFO) << "TrackMFT with " << mNPoints << " clusters. MCLabels: " << mMCCompLabels[0] << mMCCompLabels[1] << "..."; //<< mMCCompLabels[2] << mMCCompLabels[3] << mMCCompLabels[4] << mMCCompLabels[5] << mMCCompLabels[6] << mMCCompLabels[7] << mMCCompLabels[8] << mMCCompLabels[9]; -} } // namespace mft } // namespace o2 diff --git a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt index 712dedda2b3ae..01cd0301c980d 100644 --- a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt @@ -11,6 +11,7 @@ o2_add_library(DataFormatsITSMFT SOURCES src/ROFRecord.cxx src/Digit.cxx + src/NoiseMap.cxx src/Cluster.cxx src/CompCluster.cxx src/ClusterPattern.cxx @@ -24,6 +25,7 @@ o2_add_library(DataFormatsITSMFT o2_target_root_dictionary(DataFormatsITSMFT HEADERS include/DataFormatsITSMFT/ROFRecord.h include/DataFormatsITSMFT/Digit.h + include/DataFormatsITSMFT/NoiseMap.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 12804de148c57..6002ca330189a 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h @@ -79,7 +79,6 @@ struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 10, uint32_t> { BLCcolInc, BLCpattID, BLCpattMap }; - ClassDefNV(CTF, 1); }; diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/Cluster.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/Cluster.h index 407ab4b3d8fd0..ad8e844236533 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/Cluster.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/Cluster.h @@ -165,8 +165,9 @@ inline void Cluster::decreaseClusterUsage() { // decrease cluster usage counter int n = getClusterUsage(); - if (n) + if (n) { setClusterUsage(--n); + } // } @@ -176,10 +177,12 @@ inline void Cluster::setClusterUsage(Int_t n) // set cluster usage counter mNxNzN &= ~(kMaskClUse << kOffsClUse); mNxNzN |= (n & kMaskClUse) << kOffsClUse; - if (n < 2) + if (n < 2) { resetBit(kShared); - if (!n) + } + if (!n) { resetBit(kUsed); + } } } // namespace itsmft @@ -190,7 +193,7 @@ inline void Cluster::setClusterUsage(Int_t n) /// std::is_trivially_copyable<ROOT::Math::Cartesian3D<float>> fails because the class /// implements a copy constructor, although it does not much more than the default copy /// constructor. Have been trying to specialize std::is_trivially_copyable for Point3D -/// alias in MathUtils/Cartesian3D.h, but structures with a member of Point3D are +/// alias in MathUtils/Cartesian.h, but structures with a member of Point3D are /// still not fulfilling the condition. Need to understand how the type trait checks /// the condition for members. /// We believe that o2::itsmft::Cluster is messageable and explicitly specialize the diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterPattern.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterPattern.h index 7e0a5f1446a00..3769e28ddf762 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterPattern.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterPattern.h @@ -38,7 +38,9 @@ class BuildTopologyDictionary; class ClusterPattern { public: - static constexpr int MaxPatternBits = 32 * 16; + static constexpr uint8_t MaxRowSpan = 128; + static constexpr uint8_t MaxColSpan = 128; + static constexpr int MaxPatternBits = MaxRowSpan * MaxColSpan; static constexpr int MaxPatternBytes = MaxPatternBits / 8; static constexpr int SpanMask = 0x7fff; static constexpr int TruncateMask = 0x8000; @@ -55,8 +57,9 @@ class ClusterPattern mBitmap[1] = *pattIt++; int nbits = mBitmap[0] * mBitmap[1]; int nBytes = nbits / 8; - if (((nbits) % 8) != 0) + if (((nbits) % 8) != 0) { nBytes++; + } memcpy(&mBitmap[2], &(*pattIt), nBytes); pattIt += nBytes; } @@ -79,7 +82,7 @@ class ClusterPattern /// Sets the whole bitmask: the number of rows, the number of columns and the pattern void setPattern(const unsigned char bitmask[kExtendedPatternBytes]); /// Static: Compute pattern's COG position. Returns the number of fired pixels - static int getCOG(int nRow, int nCol, const unsigned char patt[MaxPatternBytes], float& xCOG, float& zCOG); + static int getCOG(int rowSpan, int colSpan, const unsigned char patt[MaxPatternBytes], float& xCOG, float& zCOG); /// Compute pattern's COG position. Returns the number of fired pixels int getCOG(float& xCOG, float& zCOG) const; diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterTopology.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterTopology.h index c19eb8114b36a..f25dab5768854 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterTopology.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ClusterTopology.h @@ -82,7 +82,7 @@ class ClusterTopology /// Hashcode computed from the pattern /// /// The first four bytes are computed with MurMur2 hash-function. The remaining - /// four bytes are the first 32 pixels of the pattern. If the number of pixles + /// four bytes are the first 32 pixels of the pattern. If the number of pixels /// is less than 32, the remaining bits are set to 0. unsigned long mHash; diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h new file mode 100644 index 0000000000000..bc3f826145e91 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h @@ -0,0 +1,137 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 NoiseMap.h +/// \brief Definition of the ITSMFT NoiseMap +#ifndef ALICEO2_ITSMFT_NOISEMAP_H +#define ALICEO2_ITSMFT_NOISEMAP_H + +#include "Rtypes.h" // for Double_t, ULong_t, etc +#include <iostream> +#include <climits> +#include <vector> +#include <map> + +namespace o2 +{ + +namespace itsmft +{ +/// \class NoiseMap +/// \brief NoiseMap class for the ITS and MFT +/// + +class NoiseMap +{ + + public: + /// Constructor, initializing values for position, charge and readout frame + NoiseMap(std::vector<std::map<int, int>>& noise) { mNoisyPixels.swap(noise); } + + /// Constructor + NoiseMap() = default; + /// Constructor + NoiseMap(int nchips) + { + mNoisyPixels.assign(nchips, std::map<int, int>()); + } + /// Destructor + ~NoiseMap() = default; + + /// Get the noise level for this pixels + float getNoiseLevel(int chip, int row, int col) const + { + if (chip > mNoisyPixels.size()) { + return 0; + } + auto key = row * 1024 + col; + const auto keyIt = mNoisyPixels[chip].find(key); + if (keyIt != mNoisyPixels[chip].end()) { + return keyIt->second; + } + return 0; + } + + void increaseNoiseCount(int chip, int row, int col) + { + if (chip > mNoisyPixels.size()) { + return; + } + auto key = row * 1024 + col; + mNoisyPixels[chip][key]++; + } + + int dumpAboveThreshold(int t = 3) const + { + int n = 0; + auto chipID = mNoisyPixels.size(); + while (chipID--) { + const auto& map = mNoisyPixels[chipID]; + for (const auto& pair : map) { + if (pair.second <= t) { + continue; + } + n++; + auto key = pair.first; + auto row = key / 1024; + auto col = key % 1024; + std::cout << "chip, row, col, noise: " << chipID << ' ' << row << ' ' << col << ' ' << pair.second << '\n'; + } + } + return n; + } + int dumpAboveProbThreshold(float p = 1e-7) const + { + return dumpAboveThreshold(p * mNumOfStrobes); + } + + void applyProbThreshold(float t, long int n) + { + // Remove from the maps all pixels with the firing probability below the threshold + mProbThreshold = t; + mNumOfStrobes = n; + for (auto& map : mNoisyPixels) { + for (auto it = map.begin(); it != map.end();) { + float prob = float(it->second) / mNumOfStrobes; + if (prob < mProbThreshold) { + it = map.erase(it); + } else { + ++it; + } + } + } + } + float getProbThreshold() const { return mProbThreshold; } + long int getNumOfStrobes() const { return mNumOfStrobes; } + + bool isNoisy(int chip, int row, int col) const + { + if (chip > mNoisyPixels.size()) { + return false; + } + auto key = row * 1024 + col; + const auto keyIt = mNoisyPixels[chip].find(key); + if (keyIt != mNoisyPixels[chip].end()) { + return true; + } + return false; + } + + private: + std::vector<std::map<int, int>> mNoisyPixels; ///< Internal noise map representation + long int mNumOfStrobes = 0; ///< Accumulated number of ALPIDE strobes + float mProbThreshold = 0; ///< Probability threshold for noisy pixels + + ClassDefNV(NoiseMap, 2); +}; +} // namespace itsmft +} // namespace o2 + +#endif /* ALICEO2_ITSMFT_NOISEMAP_H */ diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h index d099f529117a6..9568b2abca15f 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h @@ -103,7 +103,7 @@ 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 eventRecordID < 0 ? 0 : (maxROF - minROF); } + int getNROFs() const { return (rofRecordID < 0 || minROF > maxROF) ? 0 : (maxROF - minROF); } void print() const; ClassDefNV(MC2ROFRecord, 1); }; diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h index f46c244b0852e..8668075dc4302 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h @@ -34,7 +34,7 @@ #include <string> #include <unordered_map> #include <vector> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "DataFormatsITSMFT/CompCluster.h" #include "TH1F.h" @@ -73,11 +73,11 @@ class TopologyDictionary /// constexpr for the definition of the groups of rare topologies. /// The attritbution of the group ID is stringly dependent on the following parameters: it must be a power of 2. - static constexpr int RowClassSpan = 4; ///< Row span of the classes of rare topologies - static constexpr int ColClassSpan = 4; ///< Column span of the classes of rare topologies - static constexpr int MinimumClassArea = RowClassSpan * ColClassSpan; ///< Area of the smallest class of rare topologies (used as reference) - static constexpr int MaxNumberOfClasses = ClusterPattern::MaxPatternBits / MinimumClassArea; ///< Maximum number of row/column classes for the groups of rare topologies - static constexpr int NumberOfRareGroups = MaxNumberOfClasses * MaxNumberOfClasses; ///< Number of entries corresponding to groups of rare topologies (those whos matrix exceed the max number of bytes are empty). + static constexpr int RowClassSpan = 4; ///< Row span of the classes of rare topologies + static constexpr int ColClassSpan = 4; ///< Column span of the classes of rare topologies + static constexpr int MaxNumberOfRowClasses = 1 + (ClusterPattern::MaxRowSpan - 1) / RowClassSpan; ///< Maximum number of row classes for the groups of rare topologies + static constexpr int MaxNumberOfColClasses = 1 + (ClusterPattern::MaxColSpan - 1) / ColClassSpan; ///< Maximum number of col classes for the groups of rare topologies + static constexpr int NumberOfRareGroups = MaxNumberOfRowClasses * MaxNumberOfColClasses; ///< Number of entries corresponding to groups of rare topologies (those whos matrix exceed the max number of bytes are empty). /// Prints the dictionary friend std::ostream& operator<<(std::ostream& os, const TopologyDictionary& dictionary); /// Prints the dictionary in a binary file @@ -155,9 +155,9 @@ class TopologyDictionary /// Returns the number of elements in the dicionary; int getSize() const { return (int)mVectorOfIDs.size(); } ///Returns the local position of a compact cluster - Point3D<float> getClusterCoordinates(const CompCluster& cl) const; + math_utils::Point3D<float> getClusterCoordinates(const CompCluster& cl) const; ///Returns the local position of a compact cluster - static Point3D<float> getClusterCoordinates(const CompCluster& cl, const ClusterPattern& patt); + static math_utils::Point3D<float> getClusterCoordinates(const CompCluster& cl, const ClusterPattern& patt, bool isGroup = true); friend BuildTopologyDictionary; friend LookUp; @@ -169,7 +169,7 @@ class TopologyDictionary int mSmallTopologiesLUT[8 * 255 + 1]; ///< Look-Up Table for the topologies with 1-byte linearised matrix std::vector<GroupStruct> mVectorOfIDs; ///< Vector of topologies and groups - ClassDefNV(TopologyDictionary, 3); + ClassDefNV(TopologyDictionary, 4); }; // namespace itsmft } // namespace itsmft } // namespace o2 diff --git a/DataFormats/Detectors/ITSMFT/common/src/ClusterPattern.cxx b/DataFormats/Detectors/ITSMFT/common/src/ClusterPattern.cxx index f8d1e58cfc5e9..b7a531f8d2665 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ClusterPattern.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/ClusterPattern.cxx @@ -43,8 +43,9 @@ int ClusterPattern::getUsedBytes() const { int nBits = (int)mBitmap[0] * (int)mBitmap[1]; int nBytes = nBits / 8; - if (nBits % 8 != 0) + if (nBits % 8 != 0) { nBytes++; + } return nBytes; } @@ -53,8 +54,9 @@ void ClusterPattern::setPattern(int nRow, int nCol, const unsigned char patt[Max mBitmap[0] = (unsigned char)nRow; mBitmap[1] = (unsigned char)nCol; int nBytes = nRow * nCol / 8; - if (((nRow * nCol) % 8) != 0) + if (((nRow * nCol) % 8) != 0) { nBytes++; + } memcpy(&mBitmap[2], patt, nBytes); } diff --git a/DataFormats/Detectors/ITSMFT/common/src/ClusterTopology.cxx b/DataFormats/Detectors/ITSMFT/common/src/ClusterTopology.cxx index 3819612aa7df8..8e7ece08643a5 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ClusterTopology.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/ClusterTopology.cxx @@ -97,8 +97,9 @@ unsigned long ClusterTopology::getCompleteHash(int nRow, int nCol, extended_pattern[1] = (unsigned char)nCol; int nBits = nRow * nCol; int nBytes = nBits / 8; - if (nBits % 8 != 0) + if (nBits % 8 != 0) { nBytes++; + } memcpy(&extended_pattern[2], patt, nBytes); unsigned long partialHash = (unsigned long)hashFunction(extended_pattern, nBytes); diff --git a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h index 38853604b2d43..93d32c016b985 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h @@ -15,6 +15,7 @@ #pragma link off all functions; #pragma link C++ class o2::itsmft::Digit + ; +#pragma link C++ class o2::itsmft::NoiseMap + ; #pragma link C++ class std::vector < o2::itsmft::Digit> + ; #pragma link C++ class o2::itsmft::ROFRecord + ; diff --git a/DataFormats/Detectors/ITSMFT/common/src/NoiseMap.cxx b/DataFormats/Detectors/ITSMFT/common/src/NoiseMap.cxx new file mode 100644 index 0000000000000..a1c95abb6e37a --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/src/NoiseMap.cxx @@ -0,0 +1,16 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 NoiseMap.cxx +/// \brief Implementation of the ITSMFT NoiseMap + +#include "DataFormatsITSMFT/NoiseMap.h" + +ClassImp(o2::itsmft::NoiseMap); diff --git a/DataFormats/Detectors/ITSMFT/common/src/TopologyDictionary.cxx b/DataFormats/Detectors/ITSMFT/common/src/TopologyDictionary.cxx index 374a9df6de8ea..7a6cb413d671c 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/TopologyDictionary.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/TopologyDictionary.cxx @@ -40,8 +40,9 @@ TopologyDictionary::TopologyDictionary(std::string fileName) std::ostream& operator<<(std::ostream& os, const TopologyDictionary& dict) { + int ID = 0; for (auto& p : dict.mVectorOfIDs) { - os << "Hash: " << p.mHash << " ErrX: " << p.mErrX << " ErrZ : " << p.mErrZ << " xCOG: " << p.mXCOG << " zCOG: " << p.mZCOG << " Npixles: " << p.mNpixels << " Frequency: " << p.mFrequency << " isGroup : " << std::boolalpha << p.mIsGroup << std::endl + os << "ID: " << ID++ << " Hash: " << p.mHash << " ErrX: " << p.mErrX << " ErrZ : " << p.mErrZ << " xCOG: " << p.mXCOG << " zCOG: " << p.mZCOG << " Npixles: " << p.mNpixels << " Frequency: " << p.mFrequency << " isGroup : " << std::boolalpha << p.mIsGroup << std::endl << p.mPattern << std::endl << "*********************************************************" << std::endl << std::endl; @@ -73,8 +74,9 @@ int TopologyDictionary::readBinaryFile(string fname) { mVectorOfIDs.clear(); mCommonMap.clear(); - for (auto& p : mSmallTopologiesLUT) + for (auto& p : mSmallTopologiesLUT) { p = -1; + } std::ifstream in(fname.data(), std::ios::in | std::ios::binary); GroupStruct gr; int groupID = 0; @@ -96,8 +98,9 @@ int TopologyDictionary::readBinaryFile(string fname) mVectorOfIDs.push_back(gr); if (!gr.mIsGroup) { mCommonMap.insert(std::make_pair(gr.mHash, groupID)); - if (gr.mPattern.getUsedBytes() == 1) + if (gr.mPattern.getUsedBytes() == 1) { mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.mBitmap[2]] = groupID; + } } else { mGroupMap.insert(std::make_pair((int)(gr.mHash >> 32) & 0x00000000ffffffff, groupID)); } @@ -111,8 +114,9 @@ int TopologyDictionary::readBinaryFile(string fname) void TopologyDictionary::getTopologyDistribution(const TopologyDictionary& dict, TH1F*& histo, const char* histName) { int dictSize = (int)dict.getSize(); - if (histo) + if (histo) { delete histo; + } histo = new TH1F(histName, ";Topology ID;Frequency", dictSize, -0.5, dictSize - 0.5); histo->SetFillColor(kRed); histo->SetFillStyle(3005); @@ -122,21 +126,27 @@ void TopologyDictionary::getTopologyDistribution(const TopologyDictionary& dict, } } -Point3D<float> TopologyDictionary::getClusterCoordinates(const CompCluster& cl) const +math_utils::Point3D<float> TopologyDictionary::getClusterCoordinates(const CompCluster& cl) const { - Point3D<float> locCl; + math_utils::Point3D<float> locCl; o2::itsmft::SegmentationAlpide::detectorToLocalUnchecked(cl.getRow(), cl.getCol(), locCl); locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID())); locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID())); return locCl; } -Point3D<float> TopologyDictionary::getClusterCoordinates(const CompCluster& cl, const ClusterPattern& patt) +math_utils::Point3D<float> TopologyDictionary::getClusterCoordinates(const CompCluster& cl, const ClusterPattern& patt, bool isGroup) { + auto refRow = cl.getRow(); + auto refCol = cl.getCol(); float xCOG = 0, zCOG = 0; patt.getCOG(xCOG, zCOG); - Point3D<float> locCl; - o2::itsmft::SegmentationAlpide::detectorToLocalUnchecked(cl.getRow() - round(xCOG) + xCOG, cl.getCol() - round(zCOG) + zCOG, locCl); + if (isGroup) { + refRow -= round(xCOG); + refCol -= round(zCOG); + } + math_utils::Point3D<float> locCl; + o2::itsmft::SegmentationAlpide::detectorToLocalUnchecked(refRow + xCOG, refCol + zCOG, locCl); return locCl; } diff --git a/DataFormats/Detectors/MUON/CMakeLists.txt b/DataFormats/Detectors/MUON/CMakeLists.txt index 25f5ebce089ac..497e1d502f36f 100644 --- a/DataFormats/Detectors/MUON/CMakeLists.txt +++ b/DataFormats/Detectors/MUON/CMakeLists.txt @@ -9,3 +9,4 @@ # submit itself to any jurisdiction. add_subdirectory(MID) +add_subdirectory(MCH) diff --git a/DataFormats/Detectors/MUON/MCH/CMakeLists.txt b/DataFormats/Detectors/MUON/MCH/CMakeLists.txt new file mode 100644 index 0000000000000..d7f5190bed5b2 --- /dev/null +++ b/DataFormats/Detectors/MUON/MCH/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(DataFormatsMCH + SOURCES src/TrackMCH.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat) + +o2_target_root_dictionary(DataFormatsMCH + HEADERS include/DataFormatsMCH/TrackMCH.h) diff --git a/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h b/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h new file mode 100644 index 0000000000000..becd8805e93f7 --- /dev/null +++ b/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h @@ -0,0 +1,118 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackMCH.h +/// \brief Definition of the MCH track +/// +/// \author Philippe Pillot, Subatech + +#ifndef ALICEO2_MCH_TRACKMCH_H_ +#define ALICEO2_MCH_TRACKMCH_H_ + +#include <TMatrixD.h> + +#include "CommonDataFormat/RangeReference.h" + +namespace o2 +{ +namespace mch +{ + +/// MCH track external format +class TrackMCH +{ + using ClusRef = o2::dataformats::RangeRefComp<5>; + + public: + TrackMCH() = default; + TrackMCH(double z, const TMatrixD& param, const TMatrixD& cov, double chi2, int firstClIdx, int nClusters); + ~TrackMCH() = default; + + TrackMCH(const TrackMCH& track) = default; + TrackMCH& operator=(const TrackMCH& track) = default; + TrackMCH(TrackMCH&&) = default; + TrackMCH& operator=(TrackMCH&&) = default; + + /// get the track x position + double getX() const { return mParam[0]; } + /// get the track y position + double getY() const { return mParam[2]; } + /// get the track z position where the parameters are evaluated + double getZ() const { return mZ; } + /// set the track z position where the parameters are evaluated + void setZ(double z) { mZ = z; } + + double getPx() const; + double getPy() const; + double getPz() const; + double getP() const; + + /// get the muon sign + short getSign() const { return (mParam[4] < 0) ? -1 : 1; } + + /// get the track parameters + const double* getParameters() const { return mParam; } + /// set the track parameters + void setParameters(const TMatrixD& param) { param.GetMatrix2Array(mParam); } + + /// get the track parameter covariances + const double* getCovariances() const { return mCov; } + /// get the covariance between track parameters i and j + double getCovariance(int i, int j) const { return mCov[SCovIdx[i][j]]; } + // set the track parameter covariances + void setCovariances(const TMatrixD& cov); + + /// get the track chi2 + double getChi2() const { return mChi2; } + /// set the track chi2 + void setChi2(double chi2) { mChi2 = chi2; } + /// get the number of degrees of freedom of the track + int getNDF() const { return 2 * mClusRef.getEntries() - 5; } + /// get the track normalized chi2 + double getChi2OverNDF() const { return mChi2 / getNDF(); } + + /// get the number of clusters attached to the track + int getNClusters() const { return mClusRef.getEntries(); } + /// get the index of the first cluster attached to the track + int getFirstClusterIdx() const { return mClusRef.getFirstEntry(); } + /// get the index of the last cluster attached to the track + int getLastClusterIdx() const { return mClusRef.getFirstEntry() + mClusRef.getEntries() - 1; } + /// set the number of the clusters attached to the track and the index of the first one + void setClusterRef(int firstClusterIdx, int nClusters) { mClusRef.set(firstClusterIdx, nClusters); } + + private: + static constexpr int SNParams = 5; ///< number of track parameters + static constexpr int SCovSize = 15; ///< number of different elements in the symmetric covariance matrix + /// corresponding indices to access the covariance matrix elements by row and column + static constexpr int SCovIdx[SNParams][SNParams] = {{0, 1, 3, 6, 10}, + {1, 2, 4, 7, 11}, + {3, 4, 5, 8, 12}, + {6, 7, 8, 9, 13}, + {10, 11, 12, 13, 14}}; + + double mZ = 0.; ///< z position where the parameters are evaluated + double mParam[SNParams] = {0.}; ///< 5 parameters: X (cm), SlopeX, Y (cm), SlopeY, q/pYZ ((GeV/c)^-1) + /// reduced covariance matrix of track parameters, formated as follow: <pre> + /// [0] = <X,X> + /// [1] = <SlopeX,X> [2] = <SlopeX,SlopeX> + /// [3] = <Y,X> [4] = <Y,SlopeX> [5] = <Y,Y> + /// [6] = <SlopeY,X> [7] = <SlopeY,SlopeX> [8] = <SlopeY,Y> [9] = <SlopeY,SlopeY> + /// [10]= <q/pYZ,X> [11]= <q/pYZ,SlopeX> [12]= <q/pYZ,Y> [13]= <q/pYZ,SlopeY> [14]= <q/pYZ,q/pYZ> </pre> + double mCov[SCovSize] = {0.}; + double mChi2 = 0.; ///< chi2 of track + ClusRef mClusRef{}; ///< reference to external cluster indices + + ClassDefNV(TrackMCH, 1); +}; + +} // namespace mch +} // namespace o2 + +#endif // ALICEO2_MCH_TRACKMCH_H_ diff --git a/DataFormats/Detectors/MUON/MCH/src/DataFormatsMCHLinkDef.h b/DataFormats/Detectors/MUON/MCH/src/DataFormatsMCHLinkDef.h new file mode 100644 index 0000000000000..7784e087e6eb5 --- /dev/null +++ b/DataFormats/Detectors/MUON/MCH/src/DataFormatsMCHLinkDef.h @@ -0,0 +1,18 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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::mch::TrackMCH + ; +#endif diff --git a/DataFormats/Detectors/MUON/MCH/src/TrackMCH.cxx b/DataFormats/Detectors/MUON/MCH/src/TrackMCH.cxx new file mode 100644 index 0000000000000..80ef07470b556 --- /dev/null +++ b/DataFormats/Detectors/MUON/MCH/src/TrackMCH.cxx @@ -0,0 +1,84 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackMCH.cxx +/// \brief Implementation of the MCH track +/// +/// \author Philippe Pillot, Subatech + +#include "DataFormatsMCH/TrackMCH.h" + +#include <cmath> +#include <limits> + +namespace o2 +{ +namespace mch +{ + +//__________________________________________________________________________ +TrackMCH::TrackMCH(double z, const TMatrixD& param, const TMatrixD& cov, double chi2, int firstClIdx, int nClusters) + : mZ(z), mChi2(chi2), mClusRef(firstClIdx, nClusters) +{ + /// constructor + setParameters(param); + setCovariances(cov); +} + +//__________________________________________________________________________ +double TrackMCH::getPx() const +{ + /// return track momentum along x + return getPz() * mParam[1]; +} + +//__________________________________________________________________________ +double TrackMCH::getPy() const +{ + /// return track momentum along y + return getPz() * mParam[3]; +} + +//__________________________________________________________________________ +double TrackMCH::getPz() const +{ + /// return track momentum along z + if (mParam[4] != 0.) { + return -std::abs(1. / mParam[4]) / std::sqrt(1. + mParam[3] * mParam[3]); // spectro. (z<0) + } else { + return -std::numeric_limits<float>::max() / std::sqrt(1. + mParam[3] * mParam[3] + mParam[1] * mParam[1]); + } +} + +//__________________________________________________________________________ +double TrackMCH::getP() const +{ + /// return track momentum + if (mParam[4] != 0.) { + return std::abs(1. / mParam[4]) / std::sqrt(1. + mParam[3] * mParam[3]) * + std::sqrt(1. + mParam[3] * mParam[3] + mParam[1] * mParam[1]); + } else { + return std::numeric_limits<float>::max(); + } +} + +//__________________________________________________________________________ +void TrackMCH::setCovariances(const TMatrixD& cov) +{ + /// set the track parameter covariances + for (int i = 0; i < SNParams; i++) { + for (int j = 0; j <= i; j++) { + mCov[SCovIdx[i][j]] = cov(i, j); + } + } +} + +} // namespace mch +} // namespace o2 diff --git a/DataFormats/Detectors/MUON/MID/CMakeLists.txt b/DataFormats/Detectors/MUON/MID/CMakeLists.txt index e94061f33c1b0..e4379336070f1 100644 --- a/DataFormats/Detectors/MUON/MID/CMakeLists.txt +++ b/DataFormats/Detectors/MUON/MID/CMakeLists.txt @@ -9,8 +9,12 @@ # submit itself to any jurisdiction. o2_add_library(DataFormatsMID - SOURCES src/ColumnData.cxx src/Track.cxx - PUBLIC_LINK_LIBRARIES Boost::serialization O2::MathUtils + SOURCES src/ColumnData.cxx + src/Track.cxx + src/CTF.cxx + PUBLIC_LINK_LIBRARIES Boost::serialization O2::MathUtils + O2::CommonDataFormat + O2::DetectorsCommonDataFormats O2::ReconstructionDataFormats) o2_target_root_dictionary(DataFormatsMID @@ -18,4 +22,5 @@ o2_target_root_dictionary(DataFormatsMID include/DataFormatsMID/Cluster3D.h include/DataFormatsMID/ColumnData.h include/DataFormatsMID/ROFRecord.h - include/DataFormatsMID/Track.h) + include/DataFormatsMID/Track.h + include/DataFormatsMID/CTF.h) diff --git a/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/CTF.h b/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/CTF.h new file mode 100644 index 0000000000000..ed7d4b78ed539 --- /dev/null +++ b/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/CTF.h @@ -0,0 +1,56 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for MID CTF data + +#ifndef O2_MID_CTF_H +#define O2_MID_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "DataFormatsMID/ROFRecord.h" +#include "DataFormatsMID/ColumnData.h" + +namespace o2 +{ +namespace mid +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nROFs = 0; /// number of ROFrames in TF + uint32_t nColumns = 0; /// number of referred columns in TF + uint32_t firstOrbit = 0; /// 1st orbit of TF + uint16_t firstBC = 0; /// 1st BC of TF + + ClassDefNV(CTFHeader, 1); +}; + +/// wrapper for the Entropy-encoded clusters of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 7, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { BLC_bcIncROF, + BLC_orbitIncROF, + BLC_entriesROF, + BLC_evtypeROF, + BLC_pattern, + BLC_deId, + BLC_colId }; + ClassDefNV(CTF, 1); +}; + +} // namespace mid +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/MUON/MID/src/CTF.cxx b/DataFormats/Detectors/MUON/MID/src/CTF.cxx new file mode 100644 index 0000000000000..9e3bdccc1f992 --- /dev/null +++ b/DataFormats/Detectors/MUON/MID/src/CTF.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "DataFormatsMID/CTF.h" + +using namespace o2::mid; diff --git a/DataFormats/Detectors/MUON/MID/src/ColumnData.cxx b/DataFormats/Detectors/MUON/MID/src/ColumnData.cxx index 479ba823153e5..d0240c259b1d7 100644 --- a/DataFormats/Detectors/MUON/MID/src/ColumnData.cxx +++ b/DataFormats/Detectors/MUON/MID/src/ColumnData.cxx @@ -25,10 +25,11 @@ namespace mid void ColumnData::setPattern(uint16_t pattern, int cathode, int line) { /// Sets the pattern - if (cathode == 0) + if (cathode == 0) { setBendPattern(pattern, line); - else + } else { setNonBendPattern(pattern); + } } uint16_t ColumnData::getPattern(int cathode, int line) const diff --git a/DataFormats/Detectors/MUON/MID/src/DataFormatsMIDLinkDef.h b/DataFormats/Detectors/MUON/MID/src/DataFormatsMIDLinkDef.h index 14add9a4d87b2..a4986e7abc4c8 100644 --- a/DataFormats/Detectors/MUON/MID/src/DataFormatsMIDLinkDef.h +++ b/DataFormats/Detectors/MUON/MID/src/DataFormatsMIDLinkDef.h @@ -22,4 +22,7 @@ #pragma link C++ class std::vector < o2::mid::ROFRecord > +; #pragma link C++ struct o2::mid::Track + ; +#pragma link C++ struct o2::mid::CTFHeader + ; +#pragma link C++ struct o2::mid::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::mid::CTFHeader, 7, uint32_t> + ; #endif diff --git a/DataFormats/Detectors/PHOS/CMakeLists.txt b/DataFormats/Detectors/PHOS/CMakeLists.txt index e38d682a52a4f..3284e9df89663 100644 --- a/DataFormats/Detectors/PHOS/CMakeLists.txt +++ b/DataFormats/Detectors/PHOS/CMakeLists.txt @@ -15,6 +15,7 @@ o2_add_library(DataFormatsPHOS src/Cluster.cxx src/MCLabel.cxx src/TriggerRecord.cxx + src/CTF.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::MathUtils @@ -29,7 +30,9 @@ o2_target_root_dictionary(DataFormatsPHOS include/DataFormatsPHOS/Digit.h include/DataFormatsPHOS/Cluster.h include/DataFormatsPHOS/MCLabel.h - include/DataFormatsPHOS/TriggerRecord.h) + include/DataFormatsPHOS/TriggerRecord.h + include/DataFormatsPHOS/CTF.h + ) o2_add_test(Cell SOURCES test/testCell.cxx COMPONENT_NAME DataFormats-PHOS diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/CTF.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/CTF.h new file mode 100644 index 0000000000000..ee9300a01972c --- /dev/null +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/CTF.h @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author ruben.shahoyan@cern.ch +/// \brief Definitions for PHOS CTF data + +#ifndef O2_PHS_CTF_H +#define O2_PHS_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "DataFormatsPHOS/Cell.h" + +namespace o2 +{ +namespace phos +{ + +/// Header for a single CTF +struct CTFHeader { + uint32_t nTriggers = 0; /// number of triggers + uint32_t nCells = 0; /// number of referred cells + uint32_t firstOrbit = 0; /// orbit of 1st trigger + uint16_t firstBC = 0; /// bc of 1st trigger + + ClassDefNV(CTFHeader, 1); +}; + +/// wrapper for the Entropy-encoded triggers and cells of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 7, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { BLC_bcIncTrig, + BLC_orbitIncTrig, + BLC_entriesTrig, + BLC_packedID, + BLC_time, + BLC_energy, + BLC_status + }; + ClassDefNV(CTF, 1); +}; + +} // namespace phos +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Cell.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Cell.h index 6069a6e1b2fc1..7c36191dbe91f 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Cell.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Cell.h @@ -71,6 +71,24 @@ class Cell void PrintStream(std::ostream& stream) const; + // raw access for CTF encoding + uint16_t getPackedID() const { return getLong() & 0x3fff; } + void setPackedID(uint16_t v) { mBits = (getLong() & 0xffffffc000) + (v & 0x3fff); } + + uint16_t getPackedTime() const { return (getLong() >> 14) & 0x3ff; } + void setPackedTime(uint16_t v) { mBits = (getLong() & 0xffff003fff) + (uint64_t(v & 0x3ff) << 14); } + + uint16_t getPackedEnergy() const { return (getLong() >> 24) & 0x7fff; } + void setPackedEnergy(uint16_t v) { mBits = (getLong() & 0x8000ffffff) + (uint64_t(v & 0x7fff) << 24); } + + uint8_t getPackedCellStatus() const { return mBits[39]; } + void setPackedCellStatus(uint8_t v) { mBits[39] = v ? true : false; } + + void setPacked(uint16_t id, uint16_t t, uint16_t en, uint16_t status) + { + mBits = uint64_t(id & 0x3fff) + (uint64_t(t & 0x3ff) << 14) + (uint64_t(en & 0x7fff) << 24) + (uint64_t(status & 0x1) << 39); + } + private: std::bitset<40> mBits; diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Digit.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Digit.h index f534c496fc339..3eb61d9e5b837 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Digit.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Digit.h @@ -57,8 +57,9 @@ class Digit : public DigitBase { if (fabs(getTimeStamp() - other.getTimeStamp()) < kTimeGate) { return getAbsId() < other.getAbsId(); - } else + } else { return getTimeStamp() < other.getTimeStamp(); + } } /// \brief Comparison oparator, based on time and absId @@ -68,8 +69,9 @@ class Digit : public DigitBase { if (fabs(getTimeStamp() - other.getTimeStamp()) <= kTimeGate) { return getAbsId() > other.getAbsId(); - } else + } else { return getTimeStamp() > other.getTimeStamp(); + } } /// \brief Comparison oparator, based on time and absId diff --git a/DataFormats/Detectors/PHOS/src/CTF.cxx b/DataFormats/Detectors/PHOS/src/CTF.cxx new file mode 100644 index 0000000000000..d7cda37769955 --- /dev/null +++ b/DataFormats/Detectors/PHOS/src/CTF.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "DataFormatsPHOS/CTF.h" + +using namespace o2::phos; diff --git a/DataFormats/Detectors/PHOS/src/Cell.cxx b/DataFormats/Detectors/PHOS/src/Cell.cxx index d7cdd2fe43eef..cebcb4f001a62 100644 --- a/DataFormats/Detectors/PHOS/src/Cell.cxx +++ b/DataFormats/Detectors/PHOS/src/Cell.cxx @@ -35,8 +35,9 @@ Cell::Cell(short absId, float energy, float time, ChannelType_t ctype) void Cell::setAbsId(short absId) { //14 bits available - if (absId > kNmaxCell || absId < 0) + if (absId > kNmaxCell || absId < 0) { absId = kNmaxCell; + } ULong_t t = (ULong_t)absId; ULong_t b = getLong() & 0xffffffc000; // 1111 1111 1111 1111 1111 1111 1100 0000 0000 0000 @@ -58,20 +59,22 @@ short Cell::getAbsId() const { ULong_t t = getLong() & 0x3fff; //14 bits short a = (short)t; - if (a <= kNmaxCell) + if (a <= kNmaxCell) { return a; - else + } else { return 0; + } } short Cell::getTRUId() const { ULong_t t = getLong() & 0x3fff; //14 bits short a = (short)t; - if (a > kNmaxCell) + if (a > kNmaxCell) { return a - kNmaxCell - 1; - else + } else { return 0; + } } void Cell::setTime(float time) @@ -137,10 +140,11 @@ void Cell::setType(ChannelType_t ctype) ChannelType_t Cell::getType() const { - if (getHighGain()) + if (getHighGain()) { return ChannelType_t::HIGH_GAIN; - else if (getTRU()) + } else if (getTRU()) { return ChannelType_t::TRU; + } return ChannelType_t::LOW_GAIN; } @@ -153,8 +157,9 @@ void Cell::setLowGain() Bool_t Cell::getLowGain() const { ULong_t t = (getLong() >> 39); - if (t) + if (t) { return false; + } return true; } @@ -167,8 +172,9 @@ void Cell::setHighGain() Bool_t Cell::getHighGain() const { ULong_t t = (getLong() >> 39); - if (t == 1) + if (t == 1) { return true; + } return false; } diff --git a/DataFormats/Detectors/PHOS/src/DataFormatsPHOSLinkDef.h b/DataFormats/Detectors/PHOS/src/DataFormatsPHOSLinkDef.h index d0cc6c80fb182..5e5046fed4b74 100644 --- a/DataFormats/Detectors/PHOS/src/DataFormatsPHOSLinkDef.h +++ b/DataFormats/Detectors/PHOS/src/DataFormatsPHOSLinkDef.h @@ -29,4 +29,8 @@ // For channel type in digits and cells #pragma link C++ enum o2::phos::ChannelType_t + ; +#pragma link C++ struct o2::phos::CTFHeader + ; +#pragma link C++ struct o2::phos::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::phos::CTFHeader, 7, uint32_t> + ; + #endif diff --git a/DataFormats/Detectors/TOF/CMakeLists.txt b/DataFormats/Detectors/TOF/CMakeLists.txt index d991fecf5ba91..d92bfabaaed6a 100644 --- a/DataFormats/Detectors/TOF/CMakeLists.txt +++ b/DataFormats/Detectors/TOF/CMakeLists.txt @@ -14,6 +14,7 @@ o2_add_library(DataFormatsTOF src/CalibInfoTOF.cxx src/CalibLHCphaseTOF.cxx src/CalibTimeSlewingParamTOF.cxx + src/CTF.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats Boost::serialization) @@ -25,4 +26,5 @@ o2_target_root_dictionary(DataFormatsTOF include/DataFormatsTOF/CalibTimeSlewingParamTOF.h include/DataFormatsTOF/RawDataFormat.h include/DataFormatsTOF/CompressedDataFormat.h + include/DataFormatsTOF/CTF.h ) diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h new file mode 100644 index 0000000000000..75341a074681b --- /dev/null +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h @@ -0,0 +1,101 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTF.h +/// \author fnoferin@cern.ch +/// \brief Definitions for TOF CTF data + +#ifndef O2_TOF_CTF_H +#define O2_TOF_CTF_H + +#include <vector> +#include <Rtypes.h> +#include "DetectorsCommonDataFormats/EncodedBlocks.h" + +namespace o2 +{ +namespace tof +{ + +class ROFRecord; +class CompClusterExt; + +/// Header for a single CTF +struct CTFHeader { + uint32_t nROFs = 0; /// number of ROFrame in TF + uint32_t nDigits = 0; /// number of digits in TF + 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, 1); +}; + +/// Compressed but not yet entropy-encoded infos +struct CompressedInfos { + + CTFHeader header; + + /* + ROF = 1/3 orbit = 1188 BC + 1 TF = 128 * 3 = 364 ROF + TIMEFRAME = 2^8 TDC + 1 BC = 2^10 TDC = 4 TIMEFRAME + ROF = 4752 TIMEFRAME < 2^13 TIMEFRAME + + timeFrame = deltaBC/64; + timeTDC = (deltaBC%64)*1024+ dig.getTDC(); + */ + + // ROF header data + std::vector<uint16_t> bcIncROF; /// increment of ROF BC wrt BC of previous ROF + std::vector<uint32_t> orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF + std::vector<uint32_t> ndigROF; /// number of digits in ROF + std::vector<uint32_t> ndiaROF; /// number of diagnostic/pattern words in ROF + std::vector<uint32_t> ndiaCrate; /// number of diagnostic/pattern words per crate in ROF + + // Hit data + std::vector<uint16_t> timeFrameInc; /// time increment with respect of previous digit in TimeFrame units + std::vector<uint16_t> timeTDCInc; /// time increment with respect of previous digit in TDC channel (about 24.4 ps) within timeframe + std::vector<uint16_t> stripID; /// increment of stripID wrt that of prev. strip + std::vector<uint8_t> chanInStrip; /// channel in strip 0-95 (ordered in time) + std::vector<uint16_t> tot; /// Time-Over-Threshold in TOF channel (about 48.8 ps) + std::vector<uint32_t> pattMap; /// explict patterns container + + CompressedInfos() = default; + + void clear(); + + ClassDefNV(CompressedInfos, 2); +}; + +/// wrapper for the Entropy-encoded clusters of the TF +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 11, uint32_t> { + + static constexpr size_t N = getNBlocks(); + enum Slots { BLCbcIncROF, + BLCorbitIncROF, + BLCndigROF, + BLCndiaROF, + BLCndiaCrate, + BLCtimeFrameInc, + BLCtimeTDCInc, + BLCstripID, + BLCchanInStrip, + BLCtot, + BLCpattMap }; + + ClassDefNV(CTF, 1); +}; + +} // namespace tof +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOF.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOF.h index eeb2853e1a47b..9fa941afc33ba 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOF.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOF.h @@ -36,7 +36,7 @@ class CalibInfoTOF void setDeltaTimePi(float time) { mDeltaTimePi = time; } float getDeltaTimePi() const { return mDeltaTimePi; } - void setTot(int tot) { mTot = tot; } + void setTot(float tot) { mTot = tot; } float getTot() const { return mTot; } void setFlags(int flags) { mFlags = flags; } diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOFshort.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOFshort.h index 20d0ca199d2fd..3f5579c0bc33a 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOFshort.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibInfoTOFshort.h @@ -14,6 +14,8 @@ #ifndef ALICEO2_CALIBINFOTOFSHORT_H #define ALICEO2_CALIBINFOTOFSHORT_H +#include "Rtypes.h" + namespace o2 { namespace dataformats @@ -43,7 +45,8 @@ class CalibInfoTOFshort float mDeltaTimePi; // raw tof time - expected time for pi hypotesis float mTot; // time-over-threshold unsigned char mFlags; // bit mask with quality flags (to be defined) - // ClassDefNV(CalibInfoTOFshort, 1); + + ClassDefNV(CalibInfoTOFshort, 1); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibTimeSlewingParamTOF.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibTimeSlewingParamTOF.h index 04112a1eaaaad..ba15203e7781f 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibTimeSlewingParamTOF.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CalibTimeSlewingParamTOF.h @@ -48,8 +48,9 @@ class CalibTimeSlewingParamTOF int size() const { int n = 0; - for (int i = 0; i < NSECTORS; i++) + for (int i = 0; i < NSECTORS; i++) { n += (mTimeSlewing[i]).size(); + } return n; } @@ -81,11 +82,12 @@ class CalibTimeSlewingParamTOF private: // TOF channel calibrations std::array<std::array<int, NCHANNELXSECTOR>, NSECTORS> mChannelStart; ///< array with the index of the first element of a channel in the time slewing vector (per sector) + std::array<std::array<float, NCHANNELXSECTOR>, NSECTORS> mGlobalOffset; ///< array with the sigma of the peak std::array<std::vector<std::pair<unsigned short, short>>, NSECTORS> mTimeSlewing; ///< array of sector vectors; first element of the pair is TOT (in ps), second is t-texp_pi (in ps) std::array<std::array<float, NCHANNELXSECTOR>, NSECTORS> mFractionUnderPeak; ///< array with the fraction of entries below the peak std::array<std::array<float, NCHANNELXSECTOR>, NSECTORS> mSigmaPeak; ///< array with the sigma of the peak - ClassDefNV(CalibTimeSlewingParamTOF, 1); // class for TOF time slewing params + ClassDefNV(CalibTimeSlewingParamTOF, 2); // class for TOF time slewing params }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h index 0920bbcc9e846..0658241c7841e 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -19,6 +19,8 @@ #include <TMath.h> #include <cstdlib> #include "CommonConstants/LHCConstants.h" +#include <vector> +#include "Rtypes.h" namespace o2 { @@ -63,7 +65,7 @@ class Cluster : public o2::BaseCluster<float> double getTime() const { return mTime; } // Cluster ToF getter void setTime(double time) { mTime = time; } // Cluster ToF setter float getTot() const { return mTot; } // Cluster Charge getter - void setTot(int tot) { mTot = tot; } // Cluster ToT setter + void setTot(float tot) { mTot = tot; } // Cluster ToT setter int getL0L1Latency() const { return mL0L1Latency; }; // L0L1 latency void setL0L1Latency(int value) { mL0L1Latency = value; }; // L0-L1 latency int getDeltaBC() const { return mDeltaBC; }; // deltaBC @@ -110,6 +112,11 @@ class Cluster : public o2::BaseCluster<float> int getBC() const { return int(mTimeRaw * BC_TIME_INPS_INV); } + void setDigitInfo(int idig, int ch, double t, float tot); + int getDigitInfoCH(int idig) const { return mDigitInfoCh[idig]; } + double getDigitInfoT(int idig) const { return mDigitInfoT[idig]; } + float getDigitInfoTOT(int idig) const { return mDigitInfoTOT[idig]; } + private: friend class boost::serialization::access; @@ -123,7 +130,12 @@ class Cluster : public o2::BaseCluster<float> float mPhi = PhiOutOfRange; //! phi coordinate int mEntryInTree; //! index of the entry in the tree from which we read the cluster - ClassDefNV(Cluster, 3); + // add extra info to trace all digit infos (for commissioning phase) + 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.}; + + ClassDefNV(Cluster, 4); }; std::ostream& operator<<(std::ostream& os, Cluster& c); @@ -136,7 +148,7 @@ std::ostream& operator<<(std::ostream& os, Cluster& c); /// std::is_trivially_copyable<ROOT::Math::Cartesian3D<float>> fails because the class /// implements a copy constructor, although it does not much more than the default copy /// constructor. Have been trying to specialize std::is_trivially_copyable for Point3D -/// alias in MathUtils/Cartesian3D.h, but structures with a member of Point3D are +/// alias in MathUtils/Cartesian.h, but structures with a member of Point3D are /// still not fulfilling the condition. Need to understand how the type trait checks /// the condition for members. /// We believe that o2::tof::Cluster is messageable and explicitly specialize the diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CompressedDataFormat.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CompressedDataFormat.h index d95372a05d318..ad00322500e81 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CompressedDataFormat.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CompressedDataFormat.h @@ -110,17 +110,48 @@ enum EDRMDiagnostic_t { DRM_FAULTSLOTMASK_NOTZERO = 1 << 11, DRM_READOUTTIMEOUT_NOTZERO = 1 << 12, DRM_EVENTWORDS_MISMATCH = 1 << 13, + DRM_DIAGNOSTIC_SPARE1 = 1 << 14, + DRM_DECODE_ERROR = 1 << 15, DRM_MAXDIAGNOSTIC_BIT = 1 << 16 // end before BIT(16) }; +static const char* DRMDiagnosticName[32] = { + "DRM_HAS_DATA", + "", + "", + "", + "DRM_HEADER_MISSING", + "DRM_TRAILER_MISSING", + "DRM_FEEID_MISMATCH", + "DRM_ORBIT_MISMATCH", + "DRM_CRC_MISMATCH", + "DRM_ENAPARTMASK_DIFFER", + "DRM_CLOCKSTATUS_WRONG", + "DRM_FAULTSLOTMASK_NOTZERO", + "DRM_READOUTTIMEOUT_NOTZERO", + "DRM_EVENTWORDS_MISMATCH", + "DRM_DIAGNOSTIC_SPARE1", + "DRM_DECODE_ERROR"}; + /** LTM diagnostic bits **/ enum ELTMDiagnostic_t { LTM_HEADER_MISSING = 1 << 4, // start from BIT(4) LTM_TRAILER_MISSING = 1 << 5, + LTM_DIAGNOSTIC_SPARE1 = 1 << 6, LTM_HEADER_UNEXPECTED = 1 << 7, LTM_MAXDIAGNOSTIC_BIT = 1 << 16 // end before BIT(16) }; +static const char* LTMDiagnosticName[32] = { + "LTM_HAS_DATA", + "", + "", + "", + "LTM_HEADER_MISSING", + "LTM_TRAILER_MISSING", + "LTM_DIAGNOSTIC_SPARE1", + "LTM_HEADER_UNEXPECTED"}; + /** TRM diagnostic bits, 12 bits [4-15] **/ enum ETRMDiagnostic_t { TRM_HEADER_MISSING = 1 << 4, // start from BIT(4) @@ -134,8 +165,9 @@ enum ETRMDiagnostic_t { TRM_EVENTWORDS_MISMATCH = 1 << 12, TRM_DIAGNOSTIC_SPARE1 = 1 << 13, TRM_DIAGNOSTIC_SPARE2 = 1 << 14, - TRM_DIAGNOSTIC_SPARE3 = 1 << 15, - TRM_MAXDIAGNOSTIC_BIT = 1 << 16 // end before BIT(16) + TRM_DECODE_ERROR = 1 << 15, + TRM_DIAGNOSTIC_SPARE3 = TRM_DECODE_ERROR, // backward compatibility + TRM_MAXDIAGNOSTIC_BIT = 1 << 16 // end before BIT(16) }; /** TRM Chain diagnostic bits, 8 bits [16-23] chainA [24-31] chainB **/ @@ -151,6 +183,40 @@ enum ETRMChainDiagnostic_t { TRMCHAIN_MAXDIAGNOSTIC_BIT = 1 << 24 // end before BIT(23), BIT(32) }; +static const char* TRMDiagnosticName[32] = { + "TRM_HAS_DATA", + "", + "", + "", + "TRM_HEADER_MISSING", + "TRM_TRAILER_MISSING", + "TRM_CRC_MISMATCH", + "TRM_HEADER_UNEXPECTED", + "TRM_EVENTCNT_MISMATCH", + "TRM_EMPTYBIT_NOTZERO", + "TRM_LBIT_NOTZERO", + "TRM_FAULTSLOTBIT_NOTZERO", + "TRM_EVENTWORDS_MISMATCH", + "TRM_DIAGNOSTIC_SPARE1", + "TRM_DIAGNOSTIC_SPARE2", + "TRM_DECODE_ERROR", + "TRM_CHAIN_A_HEADER_MISSING", + "TRM_CHAIN_A_TRAILER_MISSING", + "TRM_CHAIN_A_STATUS_NOTZERO", + "TRM_CHAIN_A_EVENTCNT_MISMATCH", + "TRM_CHAIN_A_TDCERROR_DETECTED", + "TRM_CHAIN_A_BUNCHCNT_MISMATCH", + "TRM_CHAIN_A_DIAGNOSTIC_SPARE1", + "TRM_CHAIN_A_DIAGNOSTIC_SPARE2", + "TRM_CHAIN_B_HEADER_MISSING", + "TRM_CHAIN_B_TRAILER_MISSING", + "TRM_CHAIN_B_STATUS_NOTZERO", + "TRM_CHAIN_B_EVENTCNT_MISMATCH", + "TRM_CHAIN_B_TDCERROR_DETECTED", + "TRM_CHAIN_B_BUNCHCNT_MISMATCH", + "TRM_CHAIN_B_DIAGNOSTIC_SPARE1", + "TRM_CHAIN_B_DIAGNOSTIC_SPARE2"}; + } // namespace diagnostic } // namespace tof diff --git a/DataFormats/Detectors/TOF/src/CTF.cxx b/DataFormats/Detectors/TOF/src/CTF.cxx new file mode 100644 index 0000000000000..a1097f94efbae --- /dev/null +++ b/DataFormats/Detectors/TOF/src/CTF.cxx @@ -0,0 +1,32 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <stdexcept> +#include <cstring> +#include "Framework/Logger.h" +#include "DataFormatsTOF/CTF.h" + +using namespace o2::tof; + +///________________________________ +void CompressedInfos::clear() +{ + bcIncROF.clear(); + orbitIncROF.clear(); + ndigROF.clear(); + ndiaROF.clear(); + ndiaCrate.clear(); + timeFrameInc.clear(); + timeTDCInc.clear(); + stripID.clear(); + chanInStrip.clear(); + tot.clear(); + pattMap.clear(); +} diff --git a/DataFormats/Detectors/TOF/src/CalibLHCphaseTOF.cxx b/DataFormats/Detectors/TOF/src/CalibLHCphaseTOF.cxx index 06f023683991b..8cab385255223 100644 --- a/DataFormats/Detectors/TOF/src/CalibLHCphaseTOF.cxx +++ b/DataFormats/Detectors/TOF/src/CalibLHCphaseTOF.cxx @@ -22,8 +22,9 @@ using namespace o2::dataformats; float CalibLHCphaseTOF::getLHCphase(int timestamp) const { int n = 0; - while (n < mLHCphase.size() && mLHCphase[n].first <= timestamp) + while (n < mLHCphase.size() && mLHCphase[n].first <= timestamp) { n++; + } n--; if (n < 0) { // timestamp is before of the first available value @@ -51,8 +52,9 @@ CalibLHCphaseTOF& CalibLHCphaseTOF::operator+=(const CalibLHCphaseTOF& other) { if (other.mLHCphase.size() > mLHCphase.size()) { mLHCphase.clear(); - for (auto obj = other.mLHCphase.begin(); obj != other.mLHCphase.end(); obj++) + for (auto obj = other.mLHCphase.begin(); obj != other.mLHCphase.end(); obj++) { mLHCphase.push_back(*obj); + } } return *this; } diff --git a/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx b/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx index e53a8b5f32ec9..f58e815f5d171 100644 --- a/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx +++ b/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx @@ -45,22 +45,26 @@ float CalibTimeSlewingParamTOF::evalTimeSlewing(int channel, float totIn) const int sector = channel / NCHANNELXSECTOR; channel = channel % NCHANNELXSECTOR; - if (sector >= NSECTORS) + if (sector >= NSECTORS) { return 0.; // something went wrong! + } int n = mChannelStart[sector][channel]; - if (n < 0) + if (n < 0) { return 0.; + } int nstop = (mTimeSlewing[sector]).size(); - if (channel < NCHANNELXSECTOR - 1) + if (channel < NCHANNELXSECTOR - 1) { nstop = mChannelStart[sector][channel + 1]; + } - if (n >= nstop) + if (n >= nstop) { return 0.; // something went wrong! + } if (totIn == 0) { - return (float)((mTimeSlewing[sector])[n].second); + return (float)((mTimeSlewing[sector])[n].second + mGlobalOffset[sector][channel]); } // we convert tot from ns to ps and to unsigned short @@ -75,13 +79,14 @@ float CalibTimeSlewingParamTOF::evalTimeSlewing(int channel, float totIn) const return 0; } - if (n == nstop - 1) - return (float)((mTimeSlewing[sector])[n].second); // use the last value stored for that channel + if (n == nstop - 1) { + return (float)((mTimeSlewing[sector])[n].second + mGlobalOffset[sector][channel]); // use the last value stored for that channel + } float w1 = (float)(tot - (mTimeSlewing[sector])[n].first); float w2 = (float)((mTimeSlewing[sector])[n + 1].first - tot); - return (float)(((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) / ((mTimeSlewing[sector])[n + 1].first - (mTimeSlewing[sector])[n].first))); } //______________________________________________ @@ -100,18 +105,20 @@ void CalibTimeSlewingParamTOF::addTimeSlewingInfo(int channel, float tot, float // printf("DBG: addTimeSlewinginfo sec=%i\n",sector); - if (sector >= NSECTORS) + if (sector >= NSECTORS) { return; // something went wrong! + } int currentch = channel; while (mChannelStart[sector][currentch] == -1 && currentch > -1) { // printf("DBG: fill channel %i\n",currentch); // set also all the previous ones which were not filled mChannelStart[sector][currentch] = (mTimeSlewing[sector]).size(); + mGlobalOffset[sector][currentch] = time; currentch--; } // printf("DBG: emplace back (%f,%f)\n",tot,time); - (mTimeSlewing[sector]).emplace_back((unsigned short)(tot * 1000), (short)time); + (mTimeSlewing[sector]).emplace_back((unsigned short)(tot * 1000), (short)(time - mGlobalOffset[sector][currentch])); } //______________________________________________ diff --git a/DataFormats/Detectors/TOF/src/Cluster.cxx b/DataFormats/Detectors/TOF/src/Cluster.cxx index c00ede9fcc947..1bf4096bef7fc 100644 --- a/DataFormats/Detectors/TOF/src/Cluster.cxx +++ b/DataFormats/Detectors/TOF/src/Cluster.cxx @@ -37,22 +37,30 @@ int Cluster::getNumOfContributingChannels() const // int nContributingChannels = 1; - if (isAdditionalChannelSet(kUpLeft)) + if (isAdditionalChannelSet(kUpLeft)) { nContributingChannels++; - if (isAdditionalChannelSet(kUp)) + } + if (isAdditionalChannelSet(kUp)) { nContributingChannels++; - if (isAdditionalChannelSet(kUpRight)) + } + if (isAdditionalChannelSet(kUpRight)) { nContributingChannels++; - if (isAdditionalChannelSet(kRight)) + } + if (isAdditionalChannelSet(kRight)) { nContributingChannels++; - if (isAdditionalChannelSet(kDownRight)) + } + if (isAdditionalChannelSet(kDownRight)) { nContributingChannels++; - if (isAdditionalChannelSet(kDown)) + } + if (isAdditionalChannelSet(kDown)) { nContributingChannels++; - if (isAdditionalChannelSet(kDownLeft)) + } + if (isAdditionalChannelSet(kDownLeft)) { nContributingChannels++; - if (isAdditionalChannelSet(kLeft)) + } + if (isAdditionalChannelSet(kLeft)) { nContributingChannels++; + } return nContributingChannels; } @@ -64,3 +72,11 @@ std::ostream& operator<<(std::ostream& os, Cluster& c) os << " TOF cluster: raw time = " << std::scientific << c.getTimeRaw() << ", time = " << std::scientific << c.getTime() << ", Tot = " << std::scientific << c.getTot() << ", L0L1Latency = " << c.getL0L1Latency() << ", deltaBC = " << c.getDeltaBC() << ", R = " << c.getR() << ", mPhi = " << c.getPhi() << ", Number of contributingChannels = " << c.getNumOfContributingChannels() << "\n"; return os; } + +//______________________________________________________________________ +void Cluster::setDigitInfo(int idig, int ch, double t, float tot) +{ + mDigitInfoCh[idig] = ch; + mDigitInfoT[idig] = t; + mDigitInfoTOT[idig] = tot; +} diff --git a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h index 001dcd3dce855..c6142bb3e8ec1 100644 --- a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h +++ b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h @@ -25,4 +25,9 @@ #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOFshort> + ; #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOF> + ; +#pragma link C++ class o2::tof::CTFHeader + ; +#pragma link C++ class o2::tof::CompressedInfos + ; +#pragma link C++ class o2::tof::CTF + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::tof::CTFHeader, 11, uint32_t> + ; + #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h index fded32542461e..c501776e34e06 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h @@ -23,35 +23,49 @@ namespace o2 namespace tpc { +struct CTFHeader : public CompressedClustersCounters { + enum : uint32_t { CombinedColumns = 0x1 }; + uint32_t flags = 0; + + ClassDefNV(CTFHeader, 1); +}; + /// wrapper for the Entropy-encoded clusters of the TF -struct CTF : public o2::ctf::EncodedBlocks<CompressedClustersCounters, 23, uint32_t> { +struct CTF : public o2::ctf::EncodedBlocks<CTFHeader, 23, uint32_t> { static constexpr size_t N = getNBlocks(); + static constexpr int NBitsQTot = 16; + static constexpr int NBitsQMax = 10; + static constexpr int NBitsSigmaPad = 8; + static constexpr int NBitsSigmaTime = 8; + static constexpr int NBitsRowDiff = 8; + static constexpr int NBitsSliceLegDiff = 7; + enum Slots { BLCqTotA, - BLCqMaxA, + BLCqMaxA, // can be combined with BLCqTotA BLCflagsA, BLCrowDiffA, - BLCsliceLegDiffA, + BLCsliceLegDiffA, // can be combined with BLCrowDiffA BLCpadResA, BLCtimeResA, BLCsigmaPadA, - BLCsigmaTimeA, + BLCsigmaTimeA, // can be combined with BLCsigmaPadA BLCqPtA, BLCrowA, BLCsliceA, BLCtimeA, BLCpadA, BLCqTotU, - BLCqMaxU, + BLCqMaxU, // can be combined with BLCqTotU BLCflagsU, BLCpadDiffU, BLCtimeDiffU, BLCsigmaPadU, - BLCsigmaTimeU, + BLCsigmaTimeU, // can be combined with BLCsigmaPadU BLCnTrackClusters, BLCnSliceRowClusters }; - ClassDefNV(CTF, 1); + ClassDefNV(CTF, 2); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h index 79581f7494233..70124c72d62c9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h @@ -13,9 +13,10 @@ /// \author David Rohr #ifndef ALICEO2_DATAFORMATSTPC_CLUSTERNATIVE_H #define ALICEO2_DATAFORMATSTPC_CLUSTERNATIVE_H -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cstdint> #include <cstddef> // for size_t +#include <utility> #endif #include "DataFormatsTPC/Constants.h" #include "GPUCommonDef.h" @@ -26,7 +27,9 @@ class MCCompLabel; namespace dataformats { template <class T> -class MCTruthContainer; +class ConstMCTruthContainer; +template <class T> +class ConstMCTruthContainerView; } } // namespace o2 @@ -156,22 +159,28 @@ struct ClusterNative { // the data inside a buffer. struct ClusterNativeAccess { const ClusterNative* clustersLinear; - const ClusterNative* clusters[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW]; - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* clustersMCTruth; - unsigned int nClusters[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW]; - unsigned int nClustersSector[Constants::MAXSECTOR]; - unsigned int clusterOffset[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW]; + const ClusterNative* clusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]; + const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* clustersMCTruth; + unsigned int nClusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]; + unsigned int nClustersSector[constants::MAXSECTOR]; + unsigned int clusterOffset[constants::MAXSECTOR][constants::MAXGLOBALPADROW]; unsigned int nClustersTotal; void setOffsetPtrs(); + +#ifndef GPUCA_GPUCODE + using ConstMCLabelContainer = o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>; + using ConstMCLabelContainerView = o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>; + using ConstMCLabelContainerViewWithBuffer = std::pair<ConstMCLabelContainer, ConstMCLabelContainerView>; +#endif }; inline void ClusterNativeAccess::setOffsetPtrs() { int offset = 0; - for (unsigned int i = 0; i < Constants::MAXSECTOR; i++) { + for (unsigned int i = 0; i < constants::MAXSECTOR; i++) { nClustersSector[i] = 0; - for (unsigned int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (unsigned int j = 0; j < constants::MAXGLOBALPADROW; j++) { clusterOffset[i][j] = offset; clusters[i][j] = &clustersLinear[offset]; nClustersSector[i] += nClusters[i][j]; diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h index 8ccc6b10a1b63..7c316f3deba9c 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h @@ -18,7 +18,8 @@ #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/ClusterGroupAttribute.h" #include "DataFormatsTPC/Constants.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include <gsl/gsl> #include <TFile.h> @@ -33,7 +34,6 @@ namespace o2 { namespace tpc { -using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; /// @struct ClusterNativeContainer /// A container class for a collection of ClusterNative object @@ -82,7 +82,7 @@ struct ClusterNativeBuffer : public ClusterGroupHeader { // This is the header for the transport format of TPC ClusterNative data, // followed by a linear buffer of clusters. struct alignas(64) ClusterCountIndex { - unsigned int nClusters[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW]; + unsigned int nClusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]; }; // @struct ClusterCountIndex @@ -91,13 +91,13 @@ struct alignas(64) ClusterCountIndex { struct alignas(64) ClusterIndexBuffer { using value_type = ClusterNative; - unsigned int nClusters[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW]; + unsigned int nClusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]; size_t getNClusters() const { size_t count = 0; - for (auto sector = 0; sector < Constants::MAXSECTOR; sector++) { - for (auto row = 0; row < Constants::MAXGLOBALPADROW; row++) { + for (auto sector = 0; sector < constants::MAXSECTOR; sector++) { + for (auto row = 0; row < constants::MAXGLOBALPADROW; row++) { count += nClusters[sector][row]; } } @@ -132,11 +132,16 @@ struct alignas(64) ClusterIndexBuffer { class ClusterNativeHelper { public: + using MCLabelContainer = o2::dataformats::MCLabelContainer; + using ConstMCLabelContainer = o2::dataformats::ConstMCLabelContainer; + using ConstMCLabelContainerView = o2::dataformats::ConstMCLabelContainerView; + using ConstMCLabelContainerViewWithBuffer = ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer; + ClusterNativeHelper() = default; ~ClusterNativeHelper() = default; - constexpr static unsigned int NSectors = Constants::MAXSECTOR; - constexpr static unsigned int NPadRows = Constants::MAXGLOBALPADROW; + constexpr static unsigned int NSectors = constants::MAXSECTOR; + constexpr static unsigned int NPadRows = constants::MAXGLOBALPADROW; /// convert clusters stored in binary cluster native format to a tree and write to root file /// the cluster parameters are stored in the tree together with sector and padrow numbers. @@ -187,7 +192,7 @@ class ClusterNativeHelper // Fill the ClusterNative access structure from data and corresponding mc label arrays // from the internal data structures of the reader. int fillIndex(ClusterNativeAccess& clusterIndex, std::unique_ptr<ClusterNative[]>& clusterBuffer, - MCLabelContainer& mcBuffer); + ConstMCLabelContainerViewWithBuffer& mcBuffer); // Fill the ClusterNative access structure from data and corresponding mc label arrays. // Both cluster data input and mc containers are provided as a collection with one entry per @@ -211,10 +216,10 @@ class ClusterNativeHelper // @param inputs data arrays, fixed array, one per sector // @param mcinputs vectors mc truth container, fixed array, one per sector // @param checkFct check whether a sector index is valid - template <typename DataArrayType, typename MCArrayType, typename CheckFct> + template <typename DataArrayType, typename MCArrayType, typename CheckFct = std::function<bool(size_t&)>> static int fillIndex( ClusterNativeAccess& clusterIndex, std::unique_ptr<ClusterNative[]>& clusterBuffer, - MCLabelContainer& mcBuffer, DataArrayType& inputs, MCArrayType const& mcinputs, + ConstMCLabelContainerViewWithBuffer& mcBuffer, DataArrayType& inputs, MCArrayType const& mcinputs, CheckFct checkFct = [](auto const&) { return true; }); template <typename DataArrayType, typename CheckFct = std::function<bool(size_t&)>> @@ -226,7 +231,7 @@ class ClusterNativeHelper // TODO: maybe do in one function with conditional template parameter std::vector<std::unique_ptr<MCLabelContainer>> dummy; // another default, nothing will be added to the container - MCLabelContainer mcBuffer; + ConstMCLabelContainerViewWithBuffer mcBuffer; return fillIndex(clusterIndex, clusterBuffer, mcBuffer, inputs, dummy, checkFct); } @@ -234,17 +239,17 @@ class ClusterNativeHelper // This function does not copy any data but sets the corresponding poiters in the index. // Cluster data are provided as a raw buffer of consecutive ClusterNative arrays preceded by ClusterGroupHeader // MC labels are provided as a span of MCLabelContainers, one per sector. - static int parseSector(const char* buffer, size_t size, gsl::span<MCLabelContainer const> const& mcinput, // - ClusterNativeAccess& clusterIndex, // - const MCLabelContainer* (&clustersMCTruth)[NSectors]); // + static int parseSector(const char* buffer, size_t size, gsl::span<ConstMCLabelContainerView const> const& mcinput, // + ClusterNativeAccess& clusterIndex, // + const ConstMCLabelContainerView* (&clustersMCTruth)[NSectors]); // // Process data for one sector // Helper method receiving raw buffer provided as container // This function does not copy any data but sets the corresponding poiters in the index. template <typename ContainerT> - static int parseSector(ContainerT const cont, gsl::span<MCLabelContainer const> const& mcinput, // - ClusterNativeAccess& clusterIndex, // - const MCLabelContainer* (&clustersMCTruth)[NSectors]) // + static int parseSector(ContainerT const cont, gsl::span<ConstMCLabelContainerView const> const& mcinput, // + ClusterNativeAccess& clusterIndex, // + const ConstMCLabelContainerView* (&clustersMCTruth)[NSectors]) // { using T = typename std::remove_pointer<ContainerT>::type; static_assert(sizeof(typename T::value_type) == 1, "raw container must be byte-type"); @@ -276,10 +281,8 @@ class ClusterNativeHelper std::array<std::vector<char>*, NSectors> mSectorRaw = {nullptr}; /// the array of raw buffers std::array<size_t, NSectors> mSectorRawSize = {0}; - /// the array of MC label containers - std::array<std::vector<MCLabelContainer>, NSectors> mSectorMC; /// pointers on the elements of array of MC label containers - std::array<std::vector<MCLabelContainer>*, NSectors> mSectorMCPtr = {nullptr}; + std::array<dataformats::IOMCTruthContainerView*, NSectors> mSectorMCPtr{}; }; /// @class TreeWriter @@ -346,7 +349,7 @@ class ClusterNativeHelper template <typename DataArrayType, typename MCArrayType, typename CheckFct> int ClusterNativeHelper::Reader::fillIndex(ClusterNativeAccess& clusterIndex, - std::unique_ptr<ClusterNative[]>& clusterBuffer, MCLabelContainer& mcBuffer, + std::unique_ptr<ClusterNative[]>& clusterBuffer, ConstMCLabelContainerViewWithBuffer& mcBuffer, DataArrayType& inputs, MCArrayType const& mcinputs, CheckFct checkFct) { if (mcinputs.size() > 0 && mcinputs.size() != inputs.size()) { @@ -361,7 +364,7 @@ int ClusterNativeHelper::Reader::fillIndex(ClusterNativeAccess& clusterIndex, clusterIndex.clustersLinear = reinterpret_cast<const ClusterNative*>(inputs[0].data() + sizeof(*hdr)); clusterIndex.setOffsetPtrs(); if (mcinputs.size() > 0) { - clusterIndex.clustersMCTruth = mcinputs[0].get(); + clusterIndex.clustersMCTruth = &mcinputs[0]; } } if (sizeof(ClusterCountIndex) + clusterIndex.nClustersTotal * sizeof(ClusterNative) > inputs[0].size()) { @@ -371,16 +374,16 @@ int ClusterNativeHelper::Reader::fillIndex(ClusterNativeAccess& clusterIndex, } // multiple data blocks need to be merged into the single block - const MCLabelContainer* clustersMCTruth[NSectors] = {nullptr}; + const ConstMCLabelContainerView* clustersMCTruth[NSectors] = {nullptr}; int result = 0; for (size_t index = 0, end = inputs.size(); index < end; index++) { if (!checkFct(index)) { continue; } - MCLabelContainer const* labelsptr = nullptr; + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> const* labelsptr = nullptr; int extent = 0; if (index < mcinputs.size()) { - labelsptr = mcinputs[index].get(); + labelsptr = &mcinputs[index]; extent = 1; } int locres = parseSector(inputs[index], {labelsptr, extent}, clusterIndex, clustersMCTruth); @@ -393,7 +396,8 @@ int ClusterNativeHelper::Reader::fillIndex(ClusterNativeAccess& clusterIndex, // Now move all data to a new consecutive buffer ClusterNativeAccess old = clusterIndex; clusterBuffer.reset(new ClusterNative[result]); - mcBuffer.clear(); + MCLabelContainer tmpMCBuffer; + tmpMCBuffer.clear(); bool mcPresent = false; clusterIndex.clustersLinear = clusterBuffer.get(); clusterIndex.setOffsetPtrs(); @@ -405,14 +409,16 @@ int ClusterNativeHelper::Reader::fillIndex(ClusterNativeAccess& clusterIndex, mcPresent = true; for (unsigned int k = 0; k < old.nClusters[i][j]; k++, sectorLabelId++) { for (auto const& label : clustersMCTruth[i]->getLabels(sectorLabelId)) { - mcBuffer.addElement(clusterIndex.clusterOffset[i][j] + k, label); + tmpMCBuffer.addElement(clusterIndex.clusterOffset[i][j] + k, label); } } } } } if (mcPresent) { - clusterIndex.clustersMCTruth = &mcBuffer; + tmpMCBuffer.flatten_to(mcBuffer.first); + mcBuffer.second = mcBuffer.first; + clusterIndex.clustersMCTruth = &mcBuffer.second; } return result; diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Constants.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Constants.h index 69b627e3b9e9d..a7d4c6c410975 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Constants.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Constants.h @@ -20,23 +20,22 @@ namespace o2 { namespace tpc { - -class Constants +namespace constants { - public: - // the number of sectors - static constexpr int MAXSECTOR = 36; - // the number of global pad rows +// the number of sectors +constexpr int MAXSECTOR = 36; + +// the number of global pad rows #if defined(GPUCA_STANDALONE) && !defined(GPUCA_O2_LIB) && !defined(GPUCA_TPC_GEOMETRY_O2) - static constexpr int MAXGLOBALPADROW = 159; // Number of pad rows in Run 2, used for GPU TPC tests with Run 2 data +constexpr int MAXGLOBALPADROW = 159; // Number of pad rows in Run 2, used for GPU TPC tests with Run 2 data #else - static constexpr int MAXGLOBALPADROW = 152; // Correct number of pad rows in Run 3 +constexpr int MAXGLOBALPADROW = 152; // Correct number of pad rows in Run 3 #endif - // number of LHC bunch crossings per TPC time bin (40 MHz / 5 MHz) - static constexpr int LHCBCPERTIMEBIN = 8; -}; +// number of LHC bunch crossings per TPC time bin (40 MHz / 5 MHz) +constexpr int LHCBCPERTIMEBIN = 8; +} // namespace constants } // namespace tpc } // namespace o2 diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h index 5e75d2b708bb0..0499dcd7dc6f5 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h @@ -18,10 +18,14 @@ #ifndef AliceO2_TPC_Defs_H #define AliceO2_TPC_Defs_H +#ifndef GPUCA_GPUCODE_DEVICE #include <cmath> +#endif -#include "MathUtils/Cartesian2D.h" -#include "MathUtils/Cartesian3D.h" +#include "GPUROOTCartesianFwd.h" +#ifndef GPUCA_ALIGPUCODE +#include "MathUtils/Cartesian.h" +#endif namespace o2 { @@ -72,11 +76,11 @@ enum class StatisticsType { /// Pad centres as 2D float // For some reason cling does not like the nested using statement, typedef works ... -typedef Point2D<float> PadCentre; -typedef Point2D<float> GlobalPosition2D; -typedef Point2D<float> LocalPosition2D; -typedef Point3D<float> GlobalPosition3D; -typedef Point3D<float> LocalPosition3D; +typedef math_utils::Point2D<float> PadCentre; +typedef math_utils::Point2D<float> GlobalPosition2D; +typedef math_utils::Point2D<float> LocalPosition2D; +typedef math_utils::Point3D<float> GlobalPosition3D; +typedef math_utils::Point3D<float> LocalPosition3D; /// global pad number typedef unsigned short GlobalPadNumber; diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h index 728bd405f6deb..4c446024324c5 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h @@ -18,7 +18,7 @@ #include "GPUCommonRtypes.h" #include "GPUCommonDef.h" #include "CommonDataFormat/TimeStamp.h" -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <climits> #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h index 7f9bbf388dc5a..6397086e10e48 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h @@ -32,7 +32,7 @@ class LaserTrack : public o2::track::TrackPar static constexpr int TracksPerBundle = 7; ///< number of micro-mirrors per bundle LaserTrack() = default; - LaserTrack(int id, float x, float alpha, const std::array<float, o2::track::kNParams>& par) : mID(id), TrackPar(x, alpha, par) { ; } + LaserTrack(int id, float x, float alpha, const std::array<float, o2::track::kNParams>& par) : mID(id), o2::track::TrackPar(x, alpha, par) { ; } LaserTrack(LaserTrack const&) = default; /// set laser track ID diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TPCSectorHeader.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TPCSectorHeader.h index 14daef1ce8c3a..c9221ff5fb37a 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TPCSectorHeader.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TPCSectorHeader.h @@ -25,7 +25,7 @@ struct TPCSectorHeader : public o2::header::BaseHeader { // Required to do the lookup constexpr static const o2::header::HeaderType sHeaderType = "TPCSectH"; static const uint32_t sVersion = 2; - static constexpr int NSectors = o2::tpc::Constants::MAXSECTOR; + static constexpr int NSectors = o2::tpc::constants::MAXSECTOR; TPCSectorHeader(int s) : BaseHeader(sizeof(TPCSectorHeader), sHeaderType, o2::header::gSerializationMethodNone, sVersion), sectorBits(((uint64_t)0x1) << s) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h index 6cb7b62a36e47..0b0ddb49f774c 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h @@ -11,12 +11,11 @@ #ifndef ALICEO2_TPC_TRACKTPC #define ALICEO2_TPC_TRACKTPC +#include "GPUCommonDef.h" #include "ReconstructionDataFormats/Track.h" #include "CommonDataFormat/RangeReference.h" #include "DataFormatsTPC/ClusterNative.h" -#include "DataFormatsTPC/Defs.h" #include "DataFormatsTPC/dEdxInfo.h" -#include <gsl/span> namespace o2 { @@ -42,85 +41,102 @@ class TrackTPC : public o2::track::TrackParCov using o2::track::TrackParCov::TrackParCov; // inherit /// Default constructor - TrackTPC() = default; + GPUdDefault() TrackTPC() = default; /// Destructor - ~TrackTPC() = default; - - unsigned short getFlags() const { return mFlags; } - unsigned short getClustersSideInfo() const { return mFlags & HasBothSidesClusters; } - bool hasASideClusters() const { return mFlags & HasASideClusters; } - bool hasCSideClusters() const { return mFlags & HasCSideClusters; } - bool hasBothSidesClusters() const { return (mFlags & (HasASideClusters | HasCSideClusters)) == (HasASideClusters | HasCSideClusters); } - bool hasASideClustersOnly() const { return (mFlags & HasBothSidesClusters) == HasASideClusters; } - bool hasCSideClustersOnly() const { return (mFlags & HasBothSidesClusters) == HasCSideClusters; } - - void setHasASideClusters() { mFlags |= HasASideClusters; } - void setHasCSideClusters() { mFlags |= HasCSideClusters; } - - float getTime0() const { return mTime0; } ///< Reference time of the track, i.e. t-bins of a primary track with eta=0. - short getDeltaTBwd() const { return mDeltaTBwd; } ///< max possible decrement to getTimeVertex - short getDeltaTFwd() const { return mDeltaTFwd; } ///< max possible increment to getTimeVertex - void setDeltaTBwd(short t) { mDeltaTBwd = t; } ///< set max possible decrement to getTimeVertex - void setDeltaTFwd(short t) { mDeltaTFwd = t; } ///< set max possible increment to getTimeVertex - - float getChi2() const { return mChi2; } - const o2::track::TrackParCov& getOuterParam() const { return mOuterParam; } - void setTime0(float v) { mTime0 = v; } - void setChi2(float v) { mChi2 = v; } - void setOuterParam(o2::track::TrackParCov&& v) { mOuterParam = v; } - const ClusRef& getClusterRef() const { return mClustersReference; } - void shiftFirstClusterRef(int dif) { mClustersReference.setFirstEntry(dif + mClustersReference.getFirstEntry()); } - int getNClusters() const { return mClustersReference.getEntries(); } - int getNClusterReferences() const { return getNClusters(); } - void setClusterRef(uint32_t entry, uint16_t ncl) { mClustersReference.set(entry, ncl); } - - void getClusterReference(gsl::span<const o2::tpc::TPCClRefElem> clinfo, int nCluster, - uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex) const + GPUdDefault() ~TrackTPC() = default; + + GPUd() unsigned short getFlags() const { return mFlags; } + GPUd() unsigned short getClustersSideInfo() const { return mFlags & HasBothSidesClusters; } + GPUd() bool hasASideClusters() const { return mFlags & HasASideClusters; } + GPUd() bool hasCSideClusters() const { return mFlags & HasCSideClusters; } + GPUd() bool hasBothSidesClusters() const { return (mFlags & (HasASideClusters | HasCSideClusters)) == (HasASideClusters | HasCSideClusters); } + GPUd() bool hasASideClustersOnly() const { return (mFlags & HasBothSidesClusters) == HasASideClusters; } + GPUd() bool hasCSideClustersOnly() const { return (mFlags & HasBothSidesClusters) == HasCSideClusters; } + + GPUd() void setHasASideClusters() { mFlags |= HasASideClusters; } + GPUd() void setHasCSideClusters() { mFlags |= HasCSideClusters; } + + GPUd() float getTime0() const { return mTime0; } ///< Reference time of the track, i.e. t-bins of a primary track with eta=0. + GPUd() float getDeltaTBwd() const { return mDeltaTBwd; } ///< max possible decrement to getTimeVertex + GPUd() float getDeltaTFwd() const { return mDeltaTFwd; } ///< max possible increment to getTimeVertex + GPUd() void setDeltaTBwd(float t) { mDeltaTBwd = t; } ///< set max possible decrement to getTimeVertex + GPUd() void setDeltaTFwd(float t) { mDeltaTFwd = t; } ///< set max possible increment to getTimeVertex + + GPUd() float getChi2() const { return mChi2; } + GPUd() const o2::track::TrackParCov& getOuterParam() const { return mOuterParam; } + GPUd() void setTime0(float v) { mTime0 = v; } + GPUd() void setChi2(float v) { mChi2 = v; } + GPUd() void setOuterParam(o2::track::TrackParCov&& v) { mOuterParam = v; } + GPUd() const ClusRef& getClusterRef() const { return mClustersReference; } + GPUd() void shiftFirstClusterRef(int dif) { mClustersReference.setFirstEntry(dif + mClustersReference.getFirstEntry()); } + GPUd() int getNClusters() const { return mClustersReference.getEntries(); } + GPUd() int getNClusterReferences() const { return getNClusters(); } + GPUd() void setClusterRef(uint32_t entry, uint16_t ncl) { mClustersReference.set(entry, ncl); } + + template <class T> + GPUd() static inline 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[ mClustersReference.getFirstEntry() ], - // 1st mClustersReference.getEntries() cluster indices are stored as uint32_t + // data for given tracks starts at clinfo[ ref.getFirstEntry() ], + // 1st ref.getEntries() cluster indices are stored as uint32_t // then sector indices as uint8_t, then row indices ar uin8_t - // const uint32_t* clIndArr = &clinfo[ mClustersReference.getFirstEntry() ]; - const uint32_t* clIndArr = reinterpret_cast<const uint32_t*>(&clinfo[mClustersReference.getFirstEntry()]); // TODO remove this trick + // const uint32_t* clIndArr = &clinfo[ ref.getFirstEntry() ]; + const uint32_t* clIndArr = reinterpret_cast<const uint32_t*>(&clinfo[ref.getFirstEntry()]); // TODO remove this trick clusterIndex = clIndArr[nCluster]; - const uint8_t* srIndexArr = reinterpret_cast<const uint8_t*>(clIndArr + mClustersReference.getEntries()); + const uint8_t* srIndexArr = reinterpret_cast<const uint8_t*>(clIndArr + ref.getEntries()); sectorIndex = srIndexArr[nCluster]; - rowIndex = srIndexArr[nCluster + mClustersReference.getEntries()]; + rowIndex = srIndexArr[nCluster + ref.getEntries()]; } - const o2::tpc::ClusterNative& getCluster(gsl::span<const o2::tpc::TPCClRefElem> clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex) const + template <class T> + GPUd() inline void getClusterReference(T& clinfo, int nCluster, + uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex) const + { + getClusterReference<T>(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex, mClustersReference); + } + + template <class T> + 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) { uint32_t clusterIndex; - getClusterReference(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex); + getClusterReference<T>(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex, ref); return (clusters.clusters[sectorIndex][rowIndex][clusterIndex]); } - const o2::tpc::ClusterNative& getCluster(gsl::span<const o2::tpc::TPCClRefElem> clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters) const + template <class T> + GPUd() inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex) const + { + return getCluster<T>(clinfo, nCluster, clusters, sectorIndex, rowIndex, mClustersReference); + } + + template <class T> + GPUd() inline 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)); + return (getCluster<T>(clinfo, nCluster, clusters, sectorIndex, rowIndex)); } - const dEdxInfo& getdEdx() const { return mdEdx; } - void setdEdx(const dEdxInfo& v) { mdEdx = v; } + GPUd() const dEdxInfo& getdEdx() const { return mdEdx; } + GPUd() void setdEdx(const dEdxInfo& v) { mdEdx = v; } private: float mTime0 = 0.f; ///< Reference Z of the track assumed for the vertex, scaled with pseudo ///< VDrift and reference timeframe length, unless it was moved to be on the ///< side of TPC compatible with edge clusters sides. - short mDeltaTFwd = 0; ///< max possible increment to track time - short mDeltaTBwd = 0; ///< max possible decrement to track time + float mDeltaTFwd = 0; ///< max possible increment to track time + float mDeltaTBwd = 0; ///< max possible decrement to track time short mFlags = 0; ///< various flags, see Flags enum float mChi2 = 0.f; // Chi2 of the track o2::track::TrackParCov mOuterParam; // Track parameters at outer end of TPC. dEdxInfo mdEdx; // dEdx Information ClusRef mClustersReference; // reference to externale cluster indices - ClassDefNV(TrackTPC, 3); + ClassDefNV(TrackTPC, 4); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h index 3a14b0fd34dad..2fd6c82b920af 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h @@ -13,7 +13,7 @@ /// \author David Rohr #ifndef ALICEO2_DATAFORMATSTPC_ZEROSUPPRESSION_H #define ALICEO2_DATAFORMATSTPC_ZEROSUPPRESSION_H -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cstdint> #include <cstddef> // for size_t #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h index c6275f0a4da49..0f14e9a6b74af 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h @@ -29,6 +29,8 @@ static constexpr uint32_t DataWordSizeBytes = DataWordSizeBits / 8; ///< size of /// header definition of the zero suppressed link based data format struct Header { + static constexpr uint32_t MagicWord = 0xFC000000; + union { uint64_t word0 = 0; ///< lower 64 bits struct { /// @@ -41,15 +43,17 @@ struct Header { struct { /// uint64_t bitMaskHigh : 16; ///< higher bits of the 80 bit bitmask uint32_t bunchCrossing : 12; ///< bunch crossing number - uint32_t numWordsPayload : 8; ///< number of 128bit words with 12bit ADC values - uint64_t zero : 28; ///< not used + uint32_t numWordsPayload : 4; ///< number of 128bit words with 12bit ADC values + uint32_t magicWord : 32; ///< not used }; }; - std::bitset<80> getChannelBits() + std::bitset<80> getChannelBits() const { return std::bitset<80>((std::bitset<80>(bitMaskHigh) << 64) | std::bitset<80>(bitMaskLow)); } + + bool hasCorrectMagicWord() const { return magicWord == MagicWord; } }; /// empty header for @@ -66,11 +70,12 @@ struct Header { /// 2 x ((6 x 10 bit) + 4 bit padding) template <uint32_t DataBitSizeT = 12, uint32_t SignificantBitsT = 2> struct Data { - static constexpr uint32_t ChannelsPerHalfWord = DataWordSizeBits / DataBitSizeT / 2; ///< number of ADC values in one 128b word - static constexpr uint32_t DataBitSize = DataBitSizeT; ///< number of bits of the data representation - static constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision - static constexpr uint64_t BitMask = ((uint64_t(1) << DataBitSize) - 1); ///< mask for bits - static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + static constexpr uint32_t ChannelsPerWord = DataWordSizeBits / DataBitSizeT; ///< number of ADC values in one 128b word + static constexpr uint32_t ChannelsPerHalfWord = ChannelsPerWord / 2; ///< number of ADC values in one 64b word + static constexpr uint32_t DataBitSize = DataBitSizeT; ///< number of bits of the data representation + static constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision + static constexpr uint64_t BitMask = ((uint64_t(1) << DataBitSize) - 1); ///< mask for bits + static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float uint64_t adcValues[2]{}; ///< 128bit ADC values (max. 10x12bit) @@ -94,7 +99,7 @@ struct Data { } /// get ADC value of channel at position 'pos' (0-9) - uint32_t getADCValue(uint32_t pos) + uint32_t getADCValue(uint32_t pos) const { const uint32_t word = pos / ChannelsPerHalfWord; const uint32_t posInWord = pos % ChannelsPerHalfWord; @@ -103,7 +108,7 @@ struct Data { } /// get ADC value in float - float getADCValueFloat(uint32_t pos) + float getADCValueFloat(uint32_t pos) const { return float(getADCValue(pos)) * FloatConversion; } @@ -124,14 +129,34 @@ struct ContainerT<DataBitSizeT, SignificantBitsT, true> { Header header; ///< header data Data<DataBitSizeT, SignificantBitsT> data[0]; ///< 128 bit words with 12bit ADC values - uint32_t dataWords() { return header.numWordsPayload + 1; } + /// bunch crossing number + uint32_t getBunchCrossing() const { return header.bunchCrossing; } + + /// number of data words without the header + uint32_t getDataWords() const { return header.numWordsPayload; } + + /// number of data words including the header + uint32_t getTotalWords() const { return header.numWordsPayload + 1; } + + /// channel bitmask + std::bitset<80> getChannelBits() const { return header.getChannelBits(); } }; template <uint32_t DataBitSizeT, uint32_t SignificantBitsT> struct ContainerT<DataBitSizeT, SignificantBitsT, false> { Data<DataBitSizeT, SignificantBitsT> data[0]; ///< 128 bit words with 12bit ADC values - uint32_t dataWords() { return 7; } + /// bunch crossing number + uint32_t getBunchCrossing() const { return 0; } + + /// number of data words without the header + uint32_t getDataWords() const { return 7; } + + /// number of data words including the header, which is not present in case of 10bit decoded data format + uint32_t getTotalWords() const { return 7; } + + /// channel bitmask + std::bitset<80> getChannelBits() const { return std::bitset<80>().set(); } }; /// Container for decoded data, either zero suppressed or pure raw data @@ -158,16 +183,25 @@ struct Container { /// reset all ADC values void reset() { - for (int i = 0; i < cont.dataWords(); ++i) { + for (int i = 0; i < cont.getDataWords(); ++i) { cont.data[i].reset(); } } + uint32_t getBunchCrossing() const { return cont.getBunchCrossing(); } + uint32_t getDataWords() const { return cont.getDataWords(); } + uint32_t getTotalWords() const { return cont.getTotalWords(); } + std::bitset<80> getChannelBits() const { return cont.getChannelBits(); } + + /// total size in bytes + size_t getTotalSizeBytes() const { return getTotalWords() * DataWordSizeBytes; } + /// get position of next container. Validity check to be done outside! - Container* next() + Container* next() const { - return (Container*)(this + cont.dataWords() * DataWordSizeBytes); + return (Container*)((const char*)this + getTotalWords() * DataWordSizeBytes); } + }; // namespace zerosupp_link_based using ContainerZS = Container<>; diff --git a/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx b/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx index ac047133db6ac..10c38de78b7de 100644 --- a/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx +++ b/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx @@ -20,6 +20,7 @@ #include <iostream> using namespace o2::tpc; +using namespace o2::tpc::constants; void ClusterNativeHelper::convert(const char* fromFile, const char* toFile, const char* toTreeName) { @@ -30,7 +31,7 @@ void ClusterNativeHelper::convert(const char* fromFile, const char* toFile, cons size_t nEntries = reader.getTreeSize(); ClusterNativeAccess clusterIndex; std::unique_ptr<ClusterNative[]> clusterBuffer; - MCLabelContainer mcBuffer; + ConstMCLabelContainerViewWithBuffer mcBuffer; int result = 0; int nClusters = 0; @@ -113,10 +114,13 @@ void ClusterNativeHelper::Reader::init(const char* filename, const char* treenam LOG(ERROR) << "can not find tree " << mTreeName << " in file " << filename; return; } + + const bool singleBranch = mTree->GetBranch(mDataBranchName.data()); + size_t nofDataBranches = 0; size_t nofMCBranches = 0; for (size_t sector = 0; sector < NSectors; ++sector) { - auto branchname = mDataBranchName + "_" + std::to_string(sector); + auto branchname = singleBranch ? mDataBranchName : mDataBranchName + "_" + std::to_string(sector); TBranch* branch = mTree->GetBranch(branchname.c_str()); if (branch) { TBranch* sizebranch = mTree->GetBranch((branchname + "Size").c_str()); @@ -128,13 +132,16 @@ void ClusterNativeHelper::Reader::init(const char* filename, const char* treenam LOG(ERROR) << "can not find corresponding 'Size' branch for data branch " << branchname << ", skipping it"; } } - branchname = mMCBranchName + "_" + std::to_string(sector); + branchname = singleBranch ? mMCBranchName : mMCBranchName + "_" + std::to_string(sector); branch = mTree->GetBranch(branchname.c_str()); if (branch) { - mSectorMCPtr[sector] = &mSectorMC[sector]; branch->SetAddress(&mSectorMCPtr[sector]); ++nofMCBranches; } + + if (singleBranch) { + break; + } } LOG(INFO) << "reading " << nofDataBranches << " data branch(es) and " << nofMCBranches << " mc branch(es)"; } @@ -160,24 +167,34 @@ void ClusterNativeHelper::Reader::clear() } int ClusterNativeHelper::Reader::fillIndex(ClusterNativeAccess& clusterIndex, std::unique_ptr<ClusterNative[]>& clusterBuffer, - MCLabelContainer& mcBuffer) + ConstMCLabelContainerViewWithBuffer& mcBuffer) { + std::vector<gsl::span<const char>> clustersTPC; + std::vector<ConstMCLabelContainer> constMCLabelContainers; + std::vector<ConstMCLabelContainerView> constMCLabelContainerViews; + for (size_t index = 0; index < mSectorRaw.size(); ++index) { - if (mSectorRaw[index] && mSectorRaw[index]->size() != mSectorRawSize[index]) { - LOG(ERROR) << "inconsistent raw size for sector " << index << ": " << mSectorRaw[index]->size() << " v.s. " << mSectorRawSize[index]; - mSectorRaw[index]->clear(); + if (mSectorRaw[index]) { + if (mSectorRaw[index]->size() != mSectorRawSize[index]) { + LOG(ERROR) << "inconsistent raw size for sector " << index << ": " << mSectorRaw[index]->size() << " v.s. " << mSectorRawSize[index]; + mSectorRaw[index]->clear(); + } else { + clustersTPC.emplace_back(mSectorRaw[index]->data(), mSectorRawSize[index]); + } + } + if (mSectorMCPtr[index]) { + auto& view = constMCLabelContainers.emplace_back(); + mSectorMCPtr[index]->copyandflatten(view); + constMCLabelContainerViews.emplace_back(view); } } - // after changing this ClusterNative transport format, this functionality needs - // to be adapted - throw std::runtime_error("code path currently not supported"); - //int result = fillIndex(clusterIndex, clusterBuffer, mcBuffer, mSectorRaw, mSectorMC, [](auto&) { return true; }); - //return result; - return 0; + + int result = fillIndex(clusterIndex, clusterBuffer, mcBuffer, clustersTPC, constMCLabelContainerViews, [](auto&) { return true; }); + return result; } -int ClusterNativeHelper::Reader::parseSector(const char* buffer, size_t size, gsl::span<MCLabelContainer const> const& mcinput, ClusterNativeAccess& clusterIndex, - const MCLabelContainer* (&clustersMCTruth)[Constants::MAXSECTOR]) +int ClusterNativeHelper::Reader::parseSector(const char* buffer, size_t size, gsl::span<ConstMCLabelContainerView const> const& mcinput, ClusterNativeAccess& clusterIndex, + const ConstMCLabelContainerView* (&clustersMCTruth)[MAXSECTOR]) { if (!buffer || size < sizeof(ClusterCountIndex)) { return 0; @@ -187,9 +204,9 @@ int ClusterNativeHelper::Reader::parseSector(const char* buffer, size_t size, gs ClusterCountIndex const& counts = *reinterpret_cast<const ClusterCountIndex*>(buffer); ClusterNative const* clusters = reinterpret_cast<ClusterNative const*>(buffer + sizeof(ClusterCountIndex)); size_t numberOfClusters = 0; - for (int i = 0; i < Constants::MAXSECTOR; i++) { + for (int i = 0; i < MAXSECTOR; i++) { int nSectorClusters = 0; - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int j = 0; j < MAXGLOBALPADROW; j++) { if (counts.nClusters[i][j] == 0) { continue; } @@ -246,8 +263,8 @@ int ClusterNativeHelper::TreeWriter::fillFrom(ClusterNativeAccess const& cluster return -1; } int result = 0; - for (size_t sector = 0; sector < Constants::MAXSECTOR; ++sector) { - for (size_t padrow = 0; padrow < Constants::MAXGLOBALPADROW; ++padrow) { + for (size_t sector = 0; sector < MAXSECTOR; ++sector) { + for (size_t padrow = 0; padrow < MAXGLOBALPADROW; ++padrow) { int locres = fillFrom(sector, padrow, clusterIndex.clusters[sector][padrow], clusterIndex.nClusters[sector][padrow]); if (result >= 0 && locres >= 0) { result += locres; diff --git a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index fac8b3e0c86b3..a8eb060a3d400 100644 --- a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h +++ b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h @@ -46,7 +46,8 @@ #pragma link C++ class o2::tpc::CompressedClusters + ; #pragma link C++ class o2::tpc::CompressedClustersROOT - ; #pragma link C++ class o2::tpc::CTF + ; -#pragma link C++ class o2::ctf::EncodedBlocks < o2::tpc::CompressedClustersCounters, 23, uint32_t> + ; +#pragma link C++ class o2::tpc::CTFHeader + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::tpc::CTFHeader, 23, uint32_t> + ; #pragma link C++ enum o2::tpc::StatisticsType; #endif diff --git a/DataFormats/Detectors/TRD/CMakeLists.txt b/DataFormats/Detectors/TRD/CMakeLists.txt index ea053219e7630..c8e9a8ab4ffdd 100644 --- a/DataFormats/Detectors/TRD/CMakeLists.txt +++ b/DataFormats/Detectors/TRD/CMakeLists.txt @@ -10,10 +10,15 @@ o2_add_library(DataFormatsTRD SOURCES src/TriggerRecord.cxx - PUBLIC_LINK_LIBRARIES O2::CommonDataFormat - O2::SimulationDataFormat - ) - -o2_target_root_dictionary(DataFormatsTRD - HEADERS include/DataFormatsTRD/TriggerRecord.h) + src/LinkRecord.cxx + src/Tracklet64.cxx + src/RawData.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::SimulationDataFormat) + o2_target_root_dictionary(DataFormatsTRD + HEADERS include/DataFormatsTRD/TriggerRecord.h + include/DataFormatsTRD/LinkRecord.h + include/DataFormatsTRD/Tracklet64.h + include/DataFormatsTRD/RawData.h + include/DataFormatsTRD/Constants.h + include/DataFormatsTRD/CalibratedTracklet.h) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalibratedTracklet.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalibratedTracklet.h new file mode 100644 index 0000000000000..8944d4ed610b9 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalibratedTracklet.h @@ -0,0 +1,52 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_CALIBRATEDTRACKLET_H +#define O2_TRD_CALIBRATEDTRACKLET_H + +#include "DataFormatsTRD/Tracklet64.h" + +namespace o2 +{ +namespace trd +{ + +// The CalibratedTracklet has been calibrated in x and dy according to a calculated Lorentz Angle and Drift Velocity. +// Tracklet positions in local z direction are reported at the center of the pad-row. +// Pad-tilting correction is performed after tracking. +class CalibratedTracklet : public Tracklet64 +{ + public: + CalibratedTracklet() = default; + CalibratedTracklet(uint64_t trackletWord, float x, float y, float z, float dy) + : Tracklet64(trackletWord), mx(x), my(y), mz(z), mdy(dy){}; + ~CalibratedTracklet() = default; + + float getX() { return mx; } + float getY() { return my; } + float getZ() { return mz; } + float getDy() { return mdy; } + + void setX(float x) { mx = x; } + void setY(float y) { my = y; } + void setZ(float z) { mz = z; } + void setDy(float dy) { mdy = dy; } + + private: + float mx; + float my; + float mz; + float mdy; +}; + +} // namespace trd +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h new file mode 100644 index 0000000000000..6233327914f9e --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h @@ -0,0 +1,55 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Constants.h +/// \brief Global TRD definitions and constants +/// \author ole.schmidt@cern.ch + +#ifndef AliceO2_TRD_Constants_H +#define AliceO2_TRD_Constants_H + +namespace o2 +{ +namespace trd +{ +namespace constants +{ +constexpr int NSECTOR = 18; // the number of sectors +constexpr int NSTACK = 5; // the number of stacks per sector +constexpr int NLAYER = 6; // the number of layers +constexpr int NCHAMBERPERSEC = 30; // the number of chambers per sector +constexpr int MAXCHAMBER = 540; // the maximum number of installed chambers +constexpr int NCHAMBER = 521; // the number of chambers actually installed + +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 stack 0,1,3 and 4) +constexpr int NROWC1 = 16; // the number of pad rows for chambers of type C1 (installed in stack 2) + +constexpr int NMCMROB = 16; // the number of MCMs per ROB +constexpr int NMCMROBINROW = 4; // the number of MCMs per ROB in row direction +constexpr int NMCMROBINCOL = 4; // the number of MCMs per ROB in column direction +constexpr int NROBC0 = 6; // the number of ROBs per C0 chamber +constexpr int NROBC1 = 8; // the number of ROBs per C1 chamber +constexpr int NADCMCM = 21; // the number of ADC channels per MCM +constexpr int NCOLMCM = 18; // the number of pads per MCM + +constexpr int NBITSTRKLPOS = 11; // number of bits for position in tracklet64 word +constexpr int NBITSTRKLSLOPE = 8; // number of bits for slope in tracklet64 word +constexpr float GRANULARITYTRKLPOS = 1.f / 75; // granularity of position in tracklet64 word in pad-widths +constexpr float GRANULARITYTRKLSLOPE = 1.f / 1000; // granularity of slope in tracklet64 word in pads/timebin + +// OS: Should this not be flexible for example in case of Kr calib? +constexpr int TIMEBINS = 30; // the number of time bins + +} //namespace constants +} // namespace trd +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/LinkRecord.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/LinkRecord.h new file mode 100644 index 0000000000000..0c5db1563b11f --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/LinkRecord.h @@ -0,0 +1,91 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_LINKRECORD_H +#define ALICEO2_TRD_LINKRECORD_H + +#include <iosfwd> +#include "Rtypes.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include "DataFormatsTRD/RawData.h" + +namespace o2 +{ + +namespace trd +{ + +/// \class LinkRecord +/// \brief Header for data corresponding to the indexing of the links in the raw data output +/// adapted from DataFormatsTRD/TriggerRecord +class LinkRecord +{ + using DataRange = o2::dataformats::RangeReference<int>; + + public: + LinkRecord() = default; + LinkRecord(const uint32_t linkid, int firstentry, int nentries) : mDataRange(firstentry, nentries) { mLinkId = linkid; } + // LinkRecord(const LinkRecord::LinkId linkid, int firstentry, int nentries) : mDataRange(firstentry, nentries) {mLinkId.word=linkid.word;} + LinkRecord(uint32_t sector, int stack, int layer, int side, int firstentry, int nentries) : mDataRange(firstentry, nentries) { setLinkId(sector, stack, layer, side); } + + ~LinkRecord() = default; + + void setLinkId(const uint32_t linkid) { mLinkId = linkid; } + void setLinkId(const uint32_t sector, const uint32_t stack, const uint32_t layer, const uint32_t side); + void setDataRange(int firstentry, int nentries) { mDataRange.set(firstentry, nentries); } + void setIndexFirstObject(int firstentry) { mDataRange.setFirstEntry(firstentry); } + void setNumberOfObjects(int nentries) { mDataRange.setEntries(nentries); } + void setSector(const int sector) { mLinkId |= ((sector << supermodulebs) & supermodulemask); } + void setStack(const int stack) { mLinkId |= ((stack << stackbs) & stackmask); } + void setLayer(const int layer) { mLinkId |= ((layer << layerbs) & layermask); } + void setSide(const int side) { mLinkId |= ((side << sidebs) & sidemask); } + void setSpare(const int spare = 0) { mLinkId |= ((spare << sparebs) & sparemask); } + + const uint32_t getLinkId() { return mLinkId; } + //TODO come backwith a ccdb lookup. const uint32_t getLinkHCID() { return mLinkId & 0x7ff; } // the last 11 bits. + const uint32_t getSector() { return (mLinkId & supermodulemask) >> supermodulebs; } + const uint32_t getStack() { return (mLinkId & stackmask) >> stackbs; } + const uint32_t getLayer() { return (mLinkId & layermask) >> layerbs; } + const uint32_t getSide() { return (mLinkId & sidemask) >> sidebs; } + int getNumberOfObjects() const { return mDataRange.getEntries(); } + int getFirstEntry() const { return mDataRange.getFirstEntry(); } + static uint32_t getHalfChamberLinkId(uint32_t detector, uint32_t rob); + static uint32_t getHalfChamberLinkId(uint32_t sector, uint32_t stack, uint32_t layer, uint32_t side); + + void printStream(std::ostream& stream); + // bit masks for the above raw data; + static constexpr uint64_t sparemask = 0x000f; + static constexpr uint64_t sidemask = 0x0010; + static constexpr uint64_t layermask = 0x00e0; + static constexpr uint64_t stackmask = 0x0700; + static constexpr uint64_t supermodulemask = 0xf800; + //bit shifts for the above raw data + static constexpr uint64_t sparebs = 0; + static constexpr uint64_t sidebs = 4; + static constexpr uint64_t layerbs = 5; + static constexpr uint64_t stackbs = 8; + static constexpr uint64_t supermodulebs = 11; + + private: + uint16_t mLinkId; + DataRange mDataRange; /// Index of the triggering event (event index and first entry in the container) + ClassDefNV(LinkRecord, 3); +}; + +std::ostream& operator<<(std::ostream& stream, LinkRecord& trg); + +extern void buildTrackletHCHeader(TrackletHCHeader& header, int sector, int stack, int layer, int side, int chipclock, int format); +extern void buildTrakcletHCHeader(TrackletHCHeader& header, int detector, int rob, int chipclock, int format); + +} // namespace trd +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h new file mode 100644 index 0000000000000..b14c8d220c8ad --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h @@ -0,0 +1,244 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_RAWDATA_H +#define ALICEO2_TRD_RAWDATA_H + +#include "fairlogger/Logger.h" + +/// \class TRDRDH +/// \brief Header for TRD raw data header +// this is the header added by the CRU + +namespace o2 +{ +namespace trd +{ + +/// \structure HalfCRUHeader +/// \brief Header for half a cru, each cru has 2 output, 1 for each pciid. +// This comes at the top of the data stream for each event and 1/2cru + +struct HalfCRUHeader { + + /* Half cru header +64 bits is too wide, hence reduce to 32 to make it readable. + |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| + ------------------------------------------------------------------------------------------------- +Word 0 | eventtype | end point | stopbit| bunchcrossing | headerversion | + ------------------------------------------------------------------------------------------------- +Word 0 | reserved 1 | + ------------------------------------------------------------------------------------------------- +Word 1 | link 3 errorflags | link 2 errorflags | link 1 error flags | link 0 error flags | + ------------------------------------------------------------------------------------------------- +Word 1 | link 7 error flags | link 6 error flags | link 5 error flags | link 4 error flags | + ------------------------------------------------------------------------------------------------- +Word 2 | link 11 error flags | link 10 error flags | link 9 error flags | link 8 error flags | + ------------------------------------------------------------------------------------------------- +Word 2 | link 12 error flags | link 13 error flags | link 14 error flags | reserved 2 | + ------------------------------------------------------------------------------------------------- +Word 3 | reserved 3 | + ------------------------------------------------------------------------------------------------- +Word 3 | reserved 4 | + ------------------------------------------------------------------------------------------------- +Word 4 | link 1 datasize | link 0 datasize | + ------------------------------------------------------------------------------------------------- +Word 4 | link 3 datasize | link 2 datasize | + ------------------------------------------------------------------------------------------------- +Word 5 | link 5 datasize | link 4 datasize | + ------------------------------------------------------------------------------------------------- +Word 5 | link 7 datasize | link 6 datasize | + ------------------------------------------------------------------------------------------------- +Word 6 | link 9 datasize | link 8 datasize | + ------------------------------------------------------------------------------------------------- +Word 6 | link 11 datasize | link 10 datasize | + ------------------------------------------------------------------------------------------------- +Word 7 | link 13 datasize | link 12 datasize | + ------------------------------------------------------------------------------------------------- +Word 7 | reserved 5 | link 14 datasize | + ------------------------------------------------------------------------------------------------- +*/ + + union { + uint64_t word0 = 0x0; + //first word * + // 6 5 4 3 2 1 + // 3210987654321098765432109876543210987654321098765432109876543210 + // uint64_t: 0000000000000000000000000000000000000000000000000000000000000000 + // | | | | | |-------- 0..7 TRD Header version + // | | | | |-------------------- 8..19 bunch crossing + // | | | |------------------------ 20..23 stop bit + // | | |---------------------------- 24..27 end point + // | |-------------------------------- 28..31 event type + // |---------------------------------------------------------------- 32..63 reserved1 + struct { + uint64_t HeaderVersion : 8; // TRD Header Version + uint64_t BunchCrossing : 12; // bunch crossing + uint64_t StopBit : 4; // 8 .. 11 stop bit 0x1 if TRD packet is last data packet of trigger, else 0x0 TODO why 4 bits if only using 1? + uint64_t EndPoint : 4; // bit 0..7 event type of the data. Trigger bits from TTC-PON message, distinguish physics from calibration events. + uint64_t EventType : 4; // bit 0..7 event type of the data. Trigger bits from TTC-PON message, distinguish physics from calibration events. + uint64_t reserveda : 32; // + } __attribute__((__packed__)); + }; + union { + uint64_t word12[2]; + // 15 8 bit error flags and 1 8 bit reserved. + struct { + struct { + uint8_t errorflag : 8; + } __attribute__((__packed__)) errorflags[15]; + uint8_t reserved2 : 8; + } __attribute__((__packed__)); + }; + union { + uint64_t word3 = 0x0; + struct { + uint64_t reserved34; + } __attribute__((__packed__)); + }; + union { + uint64_t word47[4]; + //15 16 bit data sizes and 1 16 bit reserved word. + struct { + struct { + uint64_t size : 16; + } __attribute__((__packed__)) datasizes[15]; // although this is 8 dont use index 0 as its part of reserved. + uint16_t reserved5; + } __attribute__((__packed__)); + }; +}; + +/// \structure TrackletHCHeader +/// \brief Header for each half chamber +// Coming before all other tracklet data, a single tracklet word as a header for its half chamber + +struct TrackletHCHeader { + union { + // 10987654321098765432109876543210 + // uint32_t: 00000000000000000000000000000000 + // cccccccccccccccX LLL SSSSS + // ffff| |y| sss| + // | | ||| | |----- 0-4 supermodule + // | | ||| |-------- 5-7 stack + // | | ||------------ 8-10 layer + // | | |------------- 11 always 0x1 + // | | |------------- 12 side of chamber + // | ----------------------------- 13-72 MCM Clock counter + // --------------------------------- 28-31 tracklet data format number + uint32_t word; + struct { + uint32_t supermodule : 5; + uint32_t stack : 3; + uint32_t layer : 3; + uint32_t one : 1; //always 1 + uint32_t side : 1; // side of chamber + uint32_t MCLK : 15; // MCM clock counter 120MHz ... for simulation -- incrementing, and same number in all for each event. + uint32_t format : 4; + // 0 baseline PID 3 time slices, 7 bit each + // 1 DO NOT USE ! reserved for tracklet end marker disambiguation + // 14 Tracklet test-pattern mode + // 15 Reserved for testing + } __attribute__((__packed__)); + }; +}; + +/// \structure TrackletMCMHeader +/// \brief Header for MCM tracklet data outuput +// This constitutes the "4x32" bits of information from a single MCM, TrackletMCMHeader and 1-3 TrackletMCMData. +struct TrackletMCMHeader { + //first word * + // 10987654321098765432109876543210 + // uint32_t: 00000000000000000000000000000000 + // 1zzzz pppppppp pppppppp1 + // || yy| pppppppp | |--- 0 1 check bits + // || | | | ----------- 1-8 pid for tracklet 3 second part + // || | | -------------------- 9-16 pid for tracklet 2 second part + // || | ---------------------------- 17-24 pid for tracklet 1 second part + // || ------------------------------ 25-26 col + // |---------------------------------- 27-30 padrow + // ----------------------------------- 31 1 + //TODO need to check endianness, I have a vague memory the trap chip has different endianness to x86. + union { + uint32_t word; + struct { + uint32_t oneb : 1; // + uint32_t pid0 : 8; // part of pid for tracklet 0 + uint32_t pid1 : 8; // part of pid for tracklet 1 + uint32_t pid2 : 8; // part of pid for tracklet 2 + uint32_t col : 2; // 2 bits for position in pad direction. + uint32_t padrow : 4; // padrow,z coordinate for chip. + uint32_t onea : 1; // + } __attribute__((__packed__)); + }; +}; + +// \structure TrackletMCMData. +// \brief Raw Data of a tracklet, part is how ever in the MCM Header hence both are grouped together in the same file + +struct TrackletMCMData { + union { + uint32_t word; + struct { + uint8_t checkbit : 1; // + uint16_t slope : 6; // Deflection angle of tracklet + uint16_t pid : 15; // Particle Identity + uint16_t pos : 10; // Position of tracklet, signed 10 bits, granularity 0.02 pad widths, -10.22 to +10.22, relative to centre of pad 10 + } __attribute__((__packed__)); + }; +}; + +/// \structure TRDFeeID +/// \brief Frontend Electronics ID, is made up of supermodule, a/c side and the end point encoded as below. + +struct TRDFeeID { + // + // 5432109876543210 + // uint16_t: 0000000000000000 + // mmmmmmmm --- supermodule 0 - 17 + // xxx --- unused1 + // s --- side 0=A C=1 + // xxx --- unused 2; + // e --- endpoint 0=lower, 1=upper + union { + uint16_t word; + struct { + uint8_t endpoint : 1; // the pci end point of the cru in question + uint8_t unused2 : 3; // seperate so easier to read in hex dumps + uint8_t side : 1; // the A=0 or C=1 side of the supermodule being readout + uint8_t unused1 : 3; // seperate so easier to read in hex dumps + uint8_t supermodule : 8; // the supermodule being read out 0-17 + } __attribute__((__packed__)); + }; +}; + +void buildTrackletHCHeader(TrackletHCHeader& header, int sector, int stack, int layer, int side, int chipclock, int format); +void buildTrackletHCHeaderd(TrackletHCHeader& header, int detector, int rob, int chipclock, int format); +uint16_t buildTRDFeeID(int supermodule, int side, int endpoint); +uint32_t setHalfCRUHeader(HalfCRUHeader& cruhead, int crurdhversion, int bunchcrossing, int stopbits, int endpoint, int eventtype, int feeid, int cruid); +uint32_t setHalfCRUHeaderLinkData(HalfCRUHeader& cruhead, int link, int size, int errors); +void buildTrackletMCMData(TrackletMCMData& trackletword, const uint slope, const uint pos, const uint q0, const uint q1, const uint q2); +uint32_t unpacklinkinfo(const HalfCRUHeader& cruhead, const uint32_t link, const bool data); +uint32_t getlinkerrorflag(const HalfCRUHeader& cruhead, const uint32_t link); +uint32_t getlinkdatasize(const HalfCRUHeader& cruhead, const uint32_t link); +uint32_t getlinkerrorflags(const HalfCRUHeader& cruheader, std::array<uint32_t, 15>& linkerrorflags); +uint32_t getlinkdatasizes(const HalfCRUHeader& cruheader, std::array<uint32_t, 15>& linksizes); +std::ostream& operator<<(std::ostream& stream, const TrackletHCHeader halfchamberheader); +std::ostream& operator<<(std::ostream& stream, const TrackletMCMData& tracklet); +void printTrackletMCMData(o2::trd::TrackletMCMData& tracklet); +void printTrackletMCMHeader(o2::trd::TrackletMCMHeader& mcmhead); +std::ostream& operator<<(std::ostream& stream, const TrackletMCMHeader& mcmhead); +void printHalfChamber(o2::trd::TrackletHCHeader& halfchamber); +void dumpHalfChamber(o2::trd::TrackletHCHeader& halfchamber); +void printHalfCRUHeader(o2::trd::HalfCRUHeader& halfcru); +void dumpHalfCRUHeader(o2::trd::HalfCRUHeader& halfcru); +std::ostream& operator<<(std::ostream& stream, const HalfCRUHeader& halfcru); +} +} +#endif diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h new file mode 100644 index 0000000000000..fe3ed80fdfba1 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h @@ -0,0 +1,160 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDBase/GeometryBase.h" +//#include "DetectorsCommonDataFormats/DetMatrixCache.h" +//#include "DetectorsCommonDataFormats/DetID.h" + +#ifndef O2_TRD_TRACKLET64_H +#define O2_TRD_TRACKLET64_H + +//////////////////////////////////////////////////////////////////////////// +// // +// TRD Raw tracklet // +// class for TRD tracklets // +// loosely based on the old TrackletMCM // +// It still returns the old TrackletWord of run2, rebuilt on calling. // +// Authors // +// Sean Murray (murrays@cern.ch) // +// +//////////////////////////////////////////////////////////////////////////// +#include <vector> +#include <array> +#include <memory> // for std::unique_ptr +#include "Rtypes.h" // for ClassDef +#include "fairlogger/Logger.h" + +namespace o2 +{ +namespace trd +{ +/* |63|62|61|60|59|58|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|40|39|38|37|36|35|34|33|32| + ------------------------------------------------------------------------------------------------- +Word 0 | Format | HCID | padrow | col | position | + ------------------------------------------------------------------------------------------------- + |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| + ------------------------------------------------------------------------------------------------- +Word 0 | slope | Q2 | Q1 | Q0 | + ------------------------------------------------------------------------------------------------- +*/ +class Tracklet64 +{ + + public: + Tracklet64() = default; + Tracklet64(uint64_t trackletword) { mtrackletWord = trackletword; } + Tracklet64(const Tracklet64&) = default; + Tracklet64(uint64_t format, uint64_t hcid, uint64_t padrow, uint64_t col, uint64_t position, + uint64_t slope, uint64_t Q0, uint64_t Q1, uint64_t Q2) + { + buildTrackletWord(format, hcid, padrow, col, position, slope, Q0, Q1, Q2); + } + + ~Tracklet64() = default; + Tracklet64& operator=(const Tracklet64& rhs) = default; + + //TODO convert to the actual number regarding compliments. + // ----- Getters for contents of tracklet word ----- + uint64_t getHCID() const { return ((mtrackletWord & hcidmask) >> hcidbs); }; // no units 0..1077 + uint64_t getPadRow() const { return ((mtrackletWord & padrowmask) >> padrowbs); }; // in units of + uint64_t getColumn() const { return ((mtrackletWord & colmask) >> colbs); }; // in units of + uint64_t getPosition() const { return ((mtrackletWord & posmask) >> posbs); }; // in units of 0.02 pads [10bits] .. -10.22 to 10.22 + uint64_t getSlope() const { return ((mtrackletWord & slopemask) >> slopebs); }; // in units of -127 .. 127 + uint64_t getPID() const { return ((mtrackletWord & PIDmask)); }; // in units of counts all 3 together + uint64_t getQ0() const { return ((mtrackletWord & Q0mask) >> Q0bs); }; // in units of counts all 3 together + uint64_t getQ1() const { return ((mtrackletWord & Q1mask) >> Q1bs); }; // in units of counts all 3 together + uint64_t getQ2() const { return ((mtrackletWord & Q2mask) >> Q2bs); }; // in units of counts all 3 together + + uint64_t buildTrackletWord(uint64_t format, uint64_t hcid, uint64_t padrow, uint64_t col, uint64_t position, uint64_t slope, uint64_t Q2, uint64_t Q1, uint64_t Q0) + { + LOG(debug) << "Build tracklet with format:" << format << " hcid:" << hcid << " padrow: " << padrow << " col:" << col << " position:" << position << " slope:" << slope << " Q0:1:2:: " << Q0 << ":" << Q1 << ":" << Q2; + mtrackletWord = ((format << formatbs) & formatmask) + ((hcid << hcidbs) & hcidmask) + ((padrow << padrowbs) & padrowmask) + ((col << colbs) & colmask) + ((position << posbs) & posmask) + ((slope << slopebs) & slopemask) + ((Q2 << Q2bs) & Q2mask) + ((Q1 << Q1bs) & Q1mask) + ((Q0 << Q0bs) & Q0mask); + return 0; + } + uint64_t setTrackletWord(uint64_t trackletword) + { + mtrackletWord = trackletword; + return 0; + } + + // ----- Getters for tracklet information ----- + int getMCM() const + { + return (getColumn() % (72)) / 18 + 4 * (getPadRow() % 4); + } + int getROB() const + { + int side = getColumn() / 72; + return (int)((int)getPadRow() / 8 + side); + } + + // ----- Getters for offline corresponding values ----- + int getDetector() const { return getHCID() / 2; } + + uint64_t getTrackletWord() const { return mtrackletWord; }; + uint32_t getTrackletWord32() const; + + // void setDetector(int id) { uint64_t hcid= 2* id; uint64_t side=1;mtrackletWord = hcid << hcidbs + ; } + // void setHCId(int id) { mHCId = id; } + // TODO row and mcm to col and padrow mapping. + uint64_t setQ0(int charge) + { + mtrackletWord |= ((charge << Q0bs) & Q0mask); + return mtrackletWord; + } + uint64_t setQ1(int charge) + { + mtrackletWord |= ((charge << Q1bs) & Q1mask); + return mtrackletWord; + } + uint64_t setQ2(int charge) + { + mtrackletWord |= ((charge << Q2bs) & Q2mask); + return mtrackletWord; + } + void setPID(uint64_t pid) { mtrackletWord |= ((((uint64_t)pid) << PIDbs) & PIDmask); } // set the entire pid area of the trackletword, all the 3 Q's + void setPosition(uint64_t position) { mtrackletWord |= ((((uint64_t)position) << posbs) & posmask); } + void setSlope(uint64_t slope) { mtrackletWord |= ((((uint64_t)slope) << slopebs) & slopemask); } + void printStream(std::ostream& stream) const; + + // bit masks for the above raw data; + static constexpr uint64_t formatmask = 0xf000000000000000; + static constexpr uint64_t hcidmask = 0x0ffe000000000000; + static constexpr uint64_t padrowmask = 0x0001e00000000000; + static constexpr uint64_t colmask = 0x0000180000000000; + static constexpr uint64_t posmask = 0x000007ff00000000; + static constexpr uint64_t slopemask = 0x00000000ff000000; + static constexpr uint64_t Q2mask = 0x0000000000ff0000; + 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 + static constexpr uint64_t formatbs = 60; + static constexpr uint64_t hcidbs = 49; + static constexpr uint64_t padrowbs = 45; + static constexpr uint64_t colbs = 43; + static constexpr uint64_t posbs = 32; + static constexpr uint64_t slopebs = 24; + static constexpr uint64_t PIDbs = 0; + static constexpr uint64_t Q2bs = 16; + static constexpr uint64_t Q1bs = 8; + static constexpr uint64_t Q0bs = 0; + + protected: + uint64_t mtrackletWord; // the 64 bit word holding all the tracklet information for run3. + private: + ClassDefNV(Tracklet64, 1); +}; + +std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg); + +} //namespace trd +} //namespace o2 +#endif diff --git a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h index 30b2926d3845b..2fc0cbc2ee316 100644 --- a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h +++ b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h @@ -15,6 +15,16 @@ #pragma link off all functions; #pragma link C++ class o2::trd::TriggerRecord + ; +#pragma link C++ class o2::trd::LinkRecord + ; +#pragma link C++ struct o2::trd::HalfCRUHeader + ; +#pragma link C++ struct o2::trd::TrackletHCHeader + ; +#pragma link C++ struct o2::trd::TrackletMCMHeader + ; +#pragma link C++ struct o2::trd::TrackletMCMData + ; +#pragma link C++ class o2::trd::Tracklet64 + ; +#pragma link C++ class o2::trd::CalibratedTracklet + ; +#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::TriggerRecord > +; +#pragma link C++ class std::vector < o2::trd::LinkRecord > +; #endif diff --git a/DataFormats/Detectors/TRD/src/LinkRecord.cxx b/DataFormats/Detectors/TRD/src/LinkRecord.cxx new file mode 100644 index 0000000000000..ba2fb389a903e --- /dev/null +++ b/DataFormats/Detectors/TRD/src/LinkRecord.cxx @@ -0,0 +1,58 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <iostream> +#include "DataFormatsTRD/LinkRecord.h" +#include "DataFormatsTRD/Constants.h" + +namespace o2 +{ + +namespace trd +{ + +uint32_t LinkRecord::getHalfChamberLinkId(uint32_t detector, uint32_t rob) +{ + int sector = (detector % (constants::NLAYER * constants::NSTACK)); + int stack = (detector % constants::NLAYER); + int layer = ((detector % (constants::NLAYER * constants::NSTACK)) / constants::NLAYER); + int side = rob % 2; + return LinkRecord::getHalfChamberLinkId(sector, stack, layer, side); +} + +uint32_t LinkRecord::getHalfChamberLinkId(uint32_t sector, uint32_t stack, uint32_t layer, uint32_t side) +{ + LinkRecord a; + a.setLinkId(sector, stack, layer, side); + return a.mLinkId; +} + +void LinkRecord::setLinkId(const uint32_t sector, const uint32_t stack, const uint32_t layer, const uint32_t side) +{ + setSector(sector); + setStack(stack); + setLayer(layer); + setSide(side); + setSpare(); +} + +void LinkRecord::printStream(std::ostream& stream) +{ + stream << "Data for link from supermodule:" << this->getSector() << " stack:" << this->getStack() << " layer:" << this->getLayer() << "side :" << this->getSide() << ", starting from entry " << this->getFirstEntry() << " with " << this->getNumberOfObjects() << " objects"; +} + +std::ostream& operator<<(std::ostream& stream, LinkRecord& trg) +{ + trg.printStream(stream); + return stream; +} + +} // namespace trd +} // namespace o2 diff --git a/DataFormats/Detectors/TRD/src/RawData.cxx b/DataFormats/Detectors/TRD/src/RawData.cxx new file mode 100644 index 0000000000000..7668b3a47462a --- /dev/null +++ b/DataFormats/Detectors/TRD/src/RawData.cxx @@ -0,0 +1,219 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <iomanip> +#include <iostream> +#include <iomanip> +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/LinkRecord.h" +#include "DataFormatsTRD/Constants.h" + +namespace o2 +{ + +namespace trd +{ + +void buildTrackletHCHeader(TrackletHCHeader& header, int sector, int stack, int layer, int side, int chipclock, int format) +{ + header.MCLK = chipclock; + header.format = format; + header.one = 1; + header.supermodule = sector; + header.stack = stack; + header.layer = layer; + header.side = side; +} + +void buildTrackletHCHeaderd(TrackletHCHeader& header, int detector, int rob, int chipclock, int format) +{ + int sector = (detector % (constants::NLAYER * constants::NSTACK)); + int stack = (detector % constants::NLAYER); + int layer = ((detector % (constants::NLAYER * constants::NSTACK)) / constants::NLAYER); + int side = rob % 2; + buildTrackletHCHeader(header, sector, stack, layer, side, chipclock, format); +} + +uint16_t buildTRDFeeID(int supermodule, int side, int endpoint) +{ + TRDFeeID feeid; + feeid.supermodule = supermodule; + feeid.side = side; + feeid.endpoint = endpoint; + return feeid.word; +} + +void buildTrackletMCMData(TrackletMCMData& trackletword, const uint slope, const uint pos, const uint q0, const uint q1, const uint q2) +{ + trackletword.slope = slope; + trackletword.pos = pos; + trackletword.pid = q0 | ((q1 & 0xff) << 8); //q2 sits with a bit of q1 in the header pid word. + trackletword.checkbit = 1; +} + +uint32_t getlinkerrorflag(const HalfCRUHeader& cruhead, const uint32_t link) +{ + // link is the link you are requesting information on, 0-14 + uint32_t errorflag = 0; + //dealing with word0-2 + errorflag = cruhead.errorflags[link].errorflag; + return errorflag; +} + +uint32_t getlinkdatasize(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. + uint32_t size = 0; + size = cruhead.datasizes[link].size; + return size; +} + +uint32_t getlinkerrorflags(const HalfCRUHeader& cruheader, std::array<uint32_t, 15>& linkerrorflags) +{ + // retrieve all the link error flags for this half cru + for (uint32_t link = 0; link < 15; link++) { + linkerrorflags[link] = getlinkerrorflag(cruheader, link); + } + return 0; +} +uint32_t getlinkdatasizes(const HalfCRUHeader& cruheader, std::array<uint32_t, 15>& linksizes) +{ + // retrieve all the link sizes for this half cru + for (uint32_t link = 0; link < 15; link++) { + linksizes[link] = getlinkdatasize(cruheader, link); + } + return 0; +} + +uint32_t setHalfCRUHeader(HalfCRUHeader& cruhead, int crurdhversion, int bunchcrossing, int stopbits, int endpoint, int eventtype, int feeid, int cruid) +{ + cruhead.BunchCrossing = bunchcrossing; + cruhead.StopBit = stopbits; + cruhead.EndPoint = endpoint; + cruhead.EventType = eventtype; + //later versions + // cruhead.rdhversion = crurdhversion; + // cruhead.FeeID = feeid; + // cruhead.CRUID = cruid; + return 0; +} + +uint32_t setHalfCRUHeaderLinkData(HalfCRUHeader& cruhead, int link, int size, int errors) +{ + cruhead.datasizes[link].size = size; + cruhead.errorflags[link].errorflag = errors; + return 0; +} +// +// Printing methods to dump and display the various structures above in pretty format or hexdump +// printNameOfStruct(const NameOfStruct& nameofstruct); +// dumpNameOfStruct(const NameOfStruct& nameofstruct); +// std::ostrea& operator<<(std::ostream& stream, const NameOfStruct& nameofstruct); +// + +std::ostream& operator<<(std::ostream& stream, const TrackletHCHeader halfchamberheader) +{ + stream << "TrackletHCHeader : Raw:0x" << std::hex << halfchamberheader.word << " " + << halfchamberheader.format << " ;; " << halfchamberheader.MCLK << " :: " + << halfchamberheader.one << " :: (" << halfchamberheader.supermodule << "," + << halfchamberheader.stack << "," << halfchamberheader.layer << ") on side :" + << halfchamberheader.side << std::endl; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const TrackletMCMData& tracklet) +{ + // make a pretty output of the tracklet. + stream << "TrackletMCMData: Raw:0x" << std::hex << tracklet.word << " pos=" << tracklet.pos + << "::slope=" << tracklet.slope << "::pid=" << tracklet.pid << "::checkbit=" + << tracklet.checkbit << std::endl; + return stream; +} +void printTrackletMCMData(o2::trd::TrackletMCMData const& tracklet) +{ + LOGF(INFO, "TrackletMCMData: Raw:0x%08x pos:%d slope:%d pid:0x%08x checkbit:0x%02x", + tracklet.word, tracklet.pos, tracklet.slope, tracklet.pid, tracklet.checkbit); +} + +void printTrackletMCMHeader(o2::trd::TrackletMCMHeader const& mcmhead) +{ + LOGF(INFO, "MCMRawHeader: Raw:0x%08x 1:%d padrow: 0x%02x col: 0x%01x pid2 0x%02x pid1: 0x%02x pid0: 0x%02x 1:%d", + mcmhead.word, mcmhead.onea, mcmhead.padrow, mcmhead.col, + mcmhead.pid2, mcmhead.pid1, mcmhead.pid0, mcmhead.oneb); +} + +std::ostream& operator<<(std::ostream& stream, const TrackletMCMHeader& mcmhead) +{ + // make a pretty output of the mcm header. + stream << "MCMRawHeader: Raw:0x" << std::hex << mcmhead.word << " " << mcmhead.onea << "::" + << mcmhead.pid2 << ":" << mcmhead.pid1 << ":" << mcmhead.pid0 << "::" + << mcmhead.oneb << std::endl; + return stream; +} + +void printHalfChamber(o2::trd::TrackletHCHeader const& halfchamber) +{ + LOGF(INFO, "TrackletHCHeader: Raw:0x%08x SM : %d stack %d layer %d side : %d MCLK: 0x%0x Format: 0x%0x Always1:0x%0x", + halfchamber.supermodule, halfchamber.stack, halfchamber.layer, halfchamber.side, halfchamber.MCLK, halfchamber.format, halfchamber.one); +} + +void dumpHalfChamber(o2::trd::TrackletHCHeader const& halfchamber) +{ + LOGF(INFO, "HalfChamber : 0x%08x", halfchamber.word); +} + +void printHalfCRUHeader(o2::trd::HalfCRUHeader const& halfcru) +{ + std::array<uint32_t, 15> sizes; + std::array<uint32_t, 15> errorflags; + getlinkdatasizes(halfcru, sizes); + getlinkerrorflags(halfcru, errorflags); + LOGF(INFO, "V:%d BC:%d SB:%d EType:%d", halfcru.HeaderVersion, halfcru.BunchCrossing, + halfcru.StopBit, halfcru.EventType); + for (int i = 0; i < 15; i++) { + LOGF(INFO, "Link %d size: %ul eflag: 0x%02x", i, sizes[i], errorflags[i]); + } +} + +void dumpHalfCRUHeader(o2::trd::HalfCRUHeader& halfcru) +{ + std::array<uint64_t, 8> raw{}; + memcpy(&raw[0], &halfcru, sizeof(halfcru)); + for (int i = 0; i < 2; i++) { + int index = 4 * i; + LOGF(INFO, "[1/2CRUHeader %d] 0x%08x 0x%08x 0x%08x 0x%08x", i, raw[index + 3], raw[index + 2], + raw[index + 1], raw[index + 0]); + } +} + +std::ostream& operator<<(std::ostream& stream, const HalfCRUHeader& halfcru) +{ // make a pretty output of the header. + stream << std::hex; + stream << "EventType : " << halfcru.EventType << std::endl; + stream << "StopBit : " << halfcru.StopBit << std::endl; + stream << "BunchCrossing : " << halfcru.BunchCrossing << std::endl; + stream << "HeaderVersion : " << halfcru.HeaderVersion << std::endl; + stream << "link sizes : "; + for (int link = 0; link < 15; link++) { + stream << link << ":" << std::hex << std::setw(4) << getlinkdatasize(halfcru, link) << ","; + } + stream << std::endl; + stream << "link errorflags : "; + for (int link = 0; link < 15; link++) { + stream << link << ":" << std::hex << std::setw(2) << getlinkerrorflag(halfcru, link) << ","; + } + stream << std::endl; + stream << "0x" << std::hex << halfcru.word0 << " 0x" << halfcru.word12[0] << " 0x" << halfcru.word12[1] << " 0x" << halfcru.word3 << " 0x" << halfcru.word47[0] << " 0x" << halfcru.word47[1] << " 0x" << halfcru.word47[2] << " 0x" << halfcru.word47[3] << std::endl; + return stream; +} + +} // namespace trd +} // namespace o2 diff --git a/DataFormats/Detectors/TRD/src/Tracklet64.cxx b/DataFormats/Detectors/TRD/src/Tracklet64.cxx new file mode 100644 index 0000000000000..63151ddc56546 --- /dev/null +++ b/DataFormats/Detectors/TRD/src/Tracklet64.cxx @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <iostream> +#include "DataFormatsTRD/Tracklet64.h" + +namespace o2 +{ + +namespace trd +{ + +void Tracklet64::printStream(std::ostream& stream) const +{ + stream << "Tracklet64 : 0x" << std::hex << getTrackletWord(); + stream << "\t hcid : " << getHCID() << " row:" << getPadRow() << " col:" << getColumn() + << " Position:" << getPosition() << " slope:" << getSlope() + << " PID:0x" << getPID() + << " Q0:" << getQ0() << " Q1:" << getQ1() << " Q2:" << getQ2(); +} + +std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg) +{ + trg.printStream(stream); + return stream; +} + +} // namespace trd +} // namespace o2 diff --git a/DataFormats/Detectors/ZDC/CMakeLists.txt b/DataFormats/Detectors/ZDC/CMakeLists.txt index a5c1fec2c14c4..b36feed39b580 100644 --- a/DataFormats/Detectors/ZDC/CMakeLists.txt +++ b/DataFormats/Detectors/ZDC/CMakeLists.txt @@ -9,7 +9,7 @@ # submit itself to any jurisdiction. o2_add_library(DataFormatsZDC - SOURCES src/ChannelData.cxx src/BCData.cxx src/RecEvent.cxx + SOURCES src/ChannelData.cxx src/BCData.cxx src/RecEvent.cxx src/RawEventData.cxx src/OrbitRawData.cxx src/OrbitRecData.cxx PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::CommonDataFormat O2::ZDCBase ROOT::MathCore FairRoot::Base @@ -18,4 +18,4 @@ o2_add_library(DataFormatsZDC o2_target_root_dictionary(DataFormatsZDC HEADERS include/DataFormatsZDC/BCData.h include/DataFormatsZDC/ChannelData.h include/DataFormatsZDC/RecEvent.h include/DataFormatsZDC/OrbitRawData.h - include/DataFormatsZDC/OrbitRecData.h) + include/DataFormatsZDC/OrbitRecData.h include/DataFormatsZDC/RawEventData.h) diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h index db805b9b2ed52..32213f8ec4d58 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h @@ -31,17 +31,19 @@ struct BCData { /// we are going to refer to at most 26 channels, so 5 bits for the NChannels and 27 for the reference o2::dataformats::RangeRefComp<5> ref; o2::InteractionRecord ir; - 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 channels = 0; // pattern of channels it refers to + uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC + uint8_t ext_triggers = 0; // pattern of ALICE triggers BCData() = default; - BCData(int first, int ne, o2::InteractionRecord iRec, uint32_t chSto, uint32_t chTrig) + BCData(int first, int ne, o2::InteractionRecord iRec, uint32_t chSto, uint32_t chTrig, uint8_t extTrig) { ref.setFirstEntry(first); ref.setEntries(ne); ir = iRec; channels = chSto; triggers = chTrig; + ext_triggers = extTrig; } gsl::span<const ChannelData> getBunchChannelData(const gsl::span<const ChannelData> tfdata) const; diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h new file mode 100644 index 0000000000000..e96a0e95f59f6 --- /dev/null +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h @@ -0,0 +1,98 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 + +#ifndef ALICEO2_ZDC_RAWEVENTDATA_H_ +#define ALICEO2_ZDC_RAWEVENTDATA_H_ +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include "ZDCBase/Constants.h" +#include <Rtypes.h> +#include <iostream> +#include <gsl/span> + +/// \file RawEventData.h +/// \brief Container of ZDC raw data +/// \author pietro.cortese@cern.ch + +namespace o2 +{ +namespace zdc +{ + +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; + +struct __attribute__((__packed__)) ChannelDataV0 { + // First GBT word + unsigned fixed_0 : 2; + unsigned board : 4; + unsigned ch : 2; + unsigned offset : 16; + unsigned hits : 12; + unsigned bc : 12; + unsigned orbit : 32; + unsigned empty_0 : 16; + unsigned empty_1 : 32; + + // Second GBT word + unsigned fixed_1 : 2; + unsigned error : 2; + unsigned Alice_0 : 1; + unsigned Alice_1 : 1; + unsigned Alice_2 : 1; + unsigned Alice_3 : 1; + unsigned s00 : 12; + unsigned s01 : 12; + unsigned s02 : 12; + unsigned s03 : 12; + unsigned s04 : 12; + unsigned s05 : 12; + unsigned empty_2 : 16; + unsigned empty_3 : 32; + + // Third GBT word + unsigned fixed_2 : 2; + unsigned Hit : 1; + unsigned Auto_m : 1; + unsigned Auto_0 : 1; + unsigned Auto_1 : 1; + unsigned Auto_2 : 1; + unsigned Auto_3 : 1; + unsigned s06 : 12; + unsigned s07 : 12; + unsigned s08 : 12; + unsigned s09 : 12; + unsigned s10 : 12; + unsigned s11 : 12; + unsigned empty_4 : 16; + unsigned empty_5 : 32; +}; + +union EventChData { + UInt_t w[NWPerBc][NWPerGBTW]; + struct ChannelDataV0 f; + void reset(); +}; + +struct EventData { + EventChData data[NModules][NChPerModule] = {0}; + void print() const; + void reset(); + ClassDefNV(EventData, 1); +}; + +} // namespace zdc +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEvent.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEvent.h index 041e27702d432..1b70cb64e731b 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEvent.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEvent.h @@ -12,7 +12,7 @@ #define _ZDC_RECEVENT_H_ #include "CommonDataFormat/InteractionRecord.h" -#include "MathUtils/Cartesian2D.h" +#include "MathUtils/Cartesian.h" #include "ZDCBase/Constants.h" #include <Rtypes.h> #include <array> @@ -31,13 +31,13 @@ struct RecEvent { o2::InteractionRecord ir; uint32_t flags; /// reconstruction flags - std::array<float, NChannelsZEM> eneregyZEM; /// signal in the electromagnetic ZDCs + std::array<float, NChannelsZEM> energyZEM; /// signal in the electromagnetic ZDCs std::array<float, NChannelsZN> energyZNA; /// reco E in 5 ZNA sectors + sum std::array<float, NChannelsZN> energyZNC; /// reco E in 5 ZNC sectors + sum std::array<float, NChannelsZP> energyZPA; /// reco E in 5 ZPA sectors + sum std::array<float, NChannelsZP> energyZPC; /// reco E in 5 ZPC sectors + sum - Point2D<float> centroidZNA; /// centroid coordinates for ZNA - Point2D<float> centroidZNC; /// centroid coordinates for ZNC + math_utils::Point2D<float> centroidZNA; /// centroid coordinates for ZNA + math_utils::Point2D<float> centroidZNC; /// centroid coordinates for ZNC std::array<TDCChannel, NTDCChannels> tdcChannels; /// At most MaxTDCValues Values in ns per TDC channel void print() const; diff --git a/DataFormats/Detectors/ZDC/src/BCData.cxx b/DataFormats/Detectors/ZDC/src/BCData.cxx index bed2a3576d373..b090a7eacb389 100644 --- a/DataFormats/Detectors/ZDC/src/BCData.cxx +++ b/DataFormats/Detectors/ZDC/src/BCData.cxx @@ -16,18 +16,35 @@ using namespace o2::zdc; void BCData::print() const { - ir.print(); - printf("%d channels starting from %d\n", ref.getEntries(), ref.getFirstEntry()); - printf("Read : ["); - for (int ic = 0; ic < NChannels; ic++) { + printf("Orbit %9u bc %4u nch %2d pos %d\n", ir.orbit, ir.bc, ref.getEntries(), ref.getFirstEntry()); + printf("Read:"); + for (int ic = 0; ic < NDigiChannels; ic++) { + if (ic % NChPerModule == 0) { + if (ic == 0) { + printf(" %d[", ic / NChPerModule); + } else { + printf("] %d[", ic / NChPerModule); + } + } if (channels & (0x1 << ic)) { - printf("%s ", channelName(ic)); + printf("R"); + } else { + printf(" "); } } - printf("] Triggered: ["); - for (int ic = 0; ic < NChannels; ic++) { + printf("]\nHits:"); + for (int ic = 0; ic < NDigiChannels; ic++) { + if (ic % NChPerModule == 0) { + if (ic == 0) { + printf(" %d[", ic / NChPerModule); + } else { + printf("] %d[", ic / NChPerModule); + } + } if (triggers & (0x1 << ic)) { - printf("%s ", channelName(ic)); + printf("H"); + } else { + printf(" "); } } printf("]\n"); diff --git a/DataFormats/Detectors/ZDC/src/ChannelData.cxx b/DataFormats/Detectors/ZDC/src/ChannelData.cxx index c1391cc6f17f6..a29f977762e02 100644 --- a/DataFormats/Detectors/ZDC/src/ChannelData.cxx +++ b/DataFormats/Detectors/ZDC/src/ChannelData.cxx @@ -14,7 +14,7 @@ using namespace o2::zdc; void ChannelData::print() const { - printf("Ch%2d | ", id); + printf("Sig %2d | ", id); for (int i = 0; i < NTimeBinsPerBC; i++) { printf("%+5d ", data[i]); } diff --git a/DataFormats/Detectors/ZDC/src/RawEventData.cxx b/DataFormats/Detectors/ZDC/src/RawEventData.cxx new file mode 100644 index 0000000000000..06155e310e2e8 --- /dev/null +++ b/DataFormats/Detectors/ZDC/src/RawEventData.cxx @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/src/RawEventData.cxx +#include "DataFormatsZDC/RawEventData.h" + +using namespace o2::zdc; + +//ClassImp(EventData); + +//______________________________________________________________________________ +void EventChData::reset() +{ + static constexpr int payloadSize = NWPerGBTW * sizeof(UInt_t); + memset((void*)&w[0][0], 0, payloadSize); +} + +//______________________________________________________________________________ +void EventData::print() const +{ + for (Int_t im = 0; im < o2::zdc::NModules; im++) { + for (Int_t ic = 0; ic < o2::zdc::NChPerModule; ic++) { + if (data[im][ic].f.fixed_0 == Id_w0 && data[im][ic].f.fixed_1 == Id_w1 && data[im][ic].f.fixed_2 == Id_w2) { + // Not empty event + auto f = data[im][ic].f; + // Word 0 + printf("%04x %08x %08x ", data[im][ic].w[0][2], data[im][ic].w[0][1], data[im][ic].w[0][0]); + printf("orbit %-9u bc %-4u hits %-4u offset %+6i Board %2u Ch %1u\n", f.orbit, f.bc, f.hits, f.offset, f.ch, f.board); + // Word 1 + printf("%04x %08x %08x ", data[im][ic].w[1][2], data[im][ic].w[1][1], data[im][ic].w[1][0]); + printf(" %s %s %s %s 0-5 ", f.Alice_0 ? "A0" : " ", f.Alice_1 ? "A1" : " ", f.Alice_2 ? "A2" : " ", f.Alice_3 ? "A3" : " "); + printf(" %5d %5d %5d %5d %5d %5d EC=%u\n", f.s00, f.s01, f.s02, f.s03, f.s04, f.s05, f.error); + // Word 2 + printf("%04x %08x %08x ", data[im][ic].w[2][2], data[im][ic].w[2][1], data[im][ic].w[2][0]); + printf("%s %s %s %s %s %s 6-b ", f.Hit ? "H" : " ", f.Auto_m ? "TM" : " ", f.Auto_0 ? "T0" : " ", f.Auto_1 ? "T1" : " ", f.Auto_2 ? "T2" : " ", f.Auto_3 ? "T3" : " "); + printf(" %5d %5d %5d %5d %5d %5d\n", f.s06, f.s07, f.s08, f.s09, f.s10, f.s11); + } else if (data[im][ic].f.fixed_0 == 0 && data[im][ic].f.fixed_1 == 0 && data[im][ic].f.fixed_2 == 0) { + // Empty channel + } else { + // Wrong data format. Insert warning. + } + } + } +} + +//______________________________________________________________________________ +void EventData::reset() +{ + static constexpr int payloadSize = NModules * NChPerModule * NWPerGBTW * sizeof(UInt_t); + memset((void*)&data[0][0], 0, payloadSize); +} diff --git a/DataFormats/Headers/include/Headers/DAQID.h b/DataFormats/Headers/include/Headers/DAQID.h index 96cfb10b98997..4918750ae1818 100644 --- a/DataFormats/Headers/include/Headers/DAQID.h +++ b/DataFormats/Headers/include/Headers/DAQID.h @@ -22,6 +22,9 @@ namespace header { /// Source IDs used by DAQ +constexpr o2::header::DataOrigin gDataOriginUnloaded{"UNL"}; +constexpr o2::header::DataOrigin gDataOriginTST{"TST"}; + class DAQID { @@ -48,6 +51,7 @@ class DAQID static constexpr ID MID = 37; static constexpr ID DCS = 38; static constexpr ID FOC = 39; + static constexpr ID UNLOADED = 255; static constexpr ID MINDAQ = TPC; static constexpr ID MAXDAQ = FOC; @@ -60,7 +64,7 @@ class DAQID static constexpr o2::header::DataOrigin DAQtoO2(ID daq) { - return (daq < MAXDAQ + 1) ? MAP_DAQtoO2[daq] : MAP_DAQtoO2[INVALID]; + return (daq < MAXDAQ + 1) ? MAP_DAQtoO2[daq] : (daq == UNLOADED ? gDataOriginUnloaded : MAP_DAQtoO2[INVALID]); } static constexpr ID O2toDAQ(o2::header::DataOrigin o2orig) @@ -71,7 +75,7 @@ class DAQID private: ID mID = INVALID; - static constexpr o2::header::DataOrigin MAP_DAQtoO2[] = { + static constexpr o2::header::DataOrigin MAP_DAQtoO2[0xff + 1] = { "NIL", "NIL", "NIL", "TPC", "TRD", "TOF", "HMP", "PHS", "CPV", "NIL", @@ -81,11 +85,44 @@ class DAQID "NIL", "TRG", "EMC", "TST", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", - "ITS", "FDD", "FT0", "FV0", "MFT", "MID", "DCS", "FOC"}; + "ITS", "FDD", "FT0", "FV0", "MFT", "MID", "DCS", "FOC", // 39 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 49 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 59 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 69 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 79 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 89 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 99 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 109 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 119 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 129 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 139 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 149 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 159 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 169 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 179 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 189 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 199 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 209 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 219 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 229 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 239 + "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", "NIL", // 249 + "NIL", "NIL", "NIL", "NIL", "NIL", "UNL" // 255 + }; + + static constexpr bool isSameOrigin(o2::header::DataOrigin origin, ID id) + { + for (auto i = sizeof(o2::header::DataOrigin); i--;) { + if (MAP_DAQtoO2[id].str[i] != origin.str[i]) { + return false; + } + } + return true; + } static constexpr ID or2daq(o2::header::DataOrigin origin, ID id) { - return id > MAXDAQ ? INVALID : (origin == MAP_DAQtoO2[id] ? id : or2daq(origin, id + 1)); + return (id > MAXDAQ) ? (isSameOrigin(origin, UNLOADED) ? UNLOADED : INVALID) : (isSameOrigin(origin, id) ? id : or2daq(origin, id + 1)); } }; diff --git a/DataFormats/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index 525b1f377fb91..ea674174997b5 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -305,16 +305,19 @@ struct Descriptor { { char* target = str; char* targetEnd = target; - if (length >= 0 && length < (int)N) + if (length >= 0 && length < (int)N) { targetEnd += length; - else + } else { targetEnd += N; + } const char* source = string; - for (; source != nullptr && target < targetEnd && *source != 0; ++target, ++source) + for (; source != nullptr && target < targetEnd && *source != 0; ++target, ++source) { *target = *source; + } targetEnd = str + N; - for (; target < targetEnd; ++target) + for (; target < targetEnd; ++target) { *target = 0; + } // require the string to be not longer than the descriptor size if (source != nullptr && (*source == 0 || (length >= 0 && length <= (int)N))) { } else { @@ -634,6 +637,7 @@ constexpr o2::header::DataOrigin gDataOriginTRD{"TRD"}; constexpr o2::header::DataOrigin gDataOriginZDC{"ZDC"}; #ifdef ENABLE_UPGRADES constexpr o2::header::DataOrigin gDataOriginIT3{"IT3"}; +constexpr o2::header::DataOrigin gDataOriginTRK{"TRK"}; #endif //possible data types @@ -666,9 +670,11 @@ struct DataHeader : public BaseHeader { using SplitPayloadPartsType = uint32_t; using PayloadSizeType = uint64_t; using TForbitType = uint32_t; + using TFCounterType = uint32_t; + using RunNumberType = uint32_t; //static data for this header type/version - static constexpr uint32_t sVersion{2}; + static constexpr uint32_t sVersion{3}; static constexpr o2::header::HeaderType sHeaderType{String2<uint64_t>("DataHead")}; static constexpr o2::header::SerializationMethod sSerializationMethod{gSerializationMethodNone}; @@ -711,10 +717,20 @@ struct DataHeader : public BaseHeader { //___NEW STUFF GOES BELOW /// - /// first orbit of time frame as unique identifier within the run + /// first orbit of time frame, since v2 /// TForbitType firstTForbit; + /// + /// ever incrementing TF counter, allows to disentangle even TFs with same firstTForbit in case of replay, since v3 + /// + TFCounterType tfCounter; + + /// + /// run number TF belongs to, since v3 + /// + RunNumberType runNumber; + //___the functions: //__________________________________________________________________________________________________ constexpr DataHeader() @@ -726,7 +742,9 @@ struct DataHeader : public BaseHeader { subSpecification(0), splitPayloadIndex(0), payloadSize(0), - firstTForbit{0} + firstTForbit{0}, + tfCounter(0), + runNumber(0) { } @@ -740,7 +758,9 @@ struct DataHeader : public BaseHeader { subSpecification(subspec), splitPayloadIndex(0), payloadSize(size), - firstTForbit{0} + firstTForbit{0}, + tfCounter(0), + runNumber(0) { } @@ -754,7 +774,9 @@ struct DataHeader : public BaseHeader { subSpecification(subspec), splitPayloadIndex(partIndex), payloadSize(size), - firstTForbit{0} + firstTForbit{0}, + tfCounter(0), + runNumber(0) { } @@ -808,8 +830,8 @@ static_assert(sizeof(BaseHeader) == 32, "BaseHeader struct must be of size 32"); static_assert(sizeof(DataOrigin) == 4, "DataOrigin struct must be of size 4"); -static_assert(sizeof(DataHeader) == 88, - "DataHeader struct must be of size 88"); +static_assert(sizeof(DataHeader) == 96, + "DataHeader struct must be of size 96"); static_assert(gSizeMagicString == sizeof(BaseHeader::magicStringInt), "Size mismatch in magic string union"); static_assert(sizeof(BaseHeader::sMagicString) == sizeof(BaseHeader::magicStringInt), diff --git a/DataFormats/Headers/include/Headers/RAWDataHeader.h b/DataFormats/Headers/include/Headers/RAWDataHeader.h index 588080abd2746..8f68a0356c9c3 100644 --- a/DataFormats/Headers/include/Headers/RAWDataHeader.h +++ b/DataFormats/Headers/include/Headers/RAWDataHeader.h @@ -15,7 +15,7 @@ /// @since 2017-11-22 /// @brief Definition of the RAW Data Header -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cstdint> #endif diff --git a/DataFormats/Headers/include/Headers/RDHAny.h b/DataFormats/Headers/include/Headers/RDHAny.h index 6a93835fe7d49..b4a57d362abf4 100644 --- a/DataFormats/Headers/include/Headers/RDHAny.h +++ b/DataFormats/Headers/include/Headers/RDHAny.h @@ -15,8 +15,10 @@ #define ALICEO2_HEADER_RDHANY_H #include "GPUCommonDef.h" #include "Headers/RAWDataHeader.h" +#ifndef GPUCA_GPUCODE_DEVICE #include <type_traits> #include <stdexcept> +#endif namespace o2 { @@ -50,7 +52,7 @@ struct RDHAny { template <typename RDH> GPUhdi() static constexpr void sanityCheckStrict() { -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE static_assert(std::is_same<RDH, RDHv4>::value || std::is_same<RDH, RDHv5>::value || std::is_same<RDH, RDHv6>::value, "not an RDH"); @@ -61,7 +63,7 @@ struct RDHAny { template <typename RDH> GPUhdi() static constexpr void sanityCheckLoose() { -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE static_assert(std::is_same<RDH, RDHv4>::value || std::is_same<RDH, RDHv5>::value || std::is_same<RDH, RDHv6>::value || std::is_same<RDHAny, RDH>::value, "not an RDH or RDHAny"); diff --git a/DataFormats/Headers/include/Headers/Stack.h b/DataFormats/Headers/include/Headers/Stack.h index 55dae8821265a..27924f3b53036 100644 --- a/DataFormats/Headers/include/Headers/Stack.h +++ b/DataFormats/Headers/include/Headers/Stack.h @@ -155,8 +155,9 @@ struct Stack { } std::copy(h.data(), h.data() + h.size(), here); BaseHeader* last = const_cast<BaseHeader*>(lastHeader(here)); - if (!last) + if (!last) { return here; + } last->flagsNextHeader = more; return here + h.size(); } else if constexpr (std::is_same_v<BaseHeader, headerType>) { @@ -176,8 +177,9 @@ struct Stack { here += from->size(); from = from->next(); }; - if (last) + if (last) { last->flagsNextHeader = more; + } return here; } else { static_assert(true, "Stack can only be constructed from other stacks and BaseHeader derived classes"); diff --git a/DataFormats/Headers/src/DataHeader.cxx b/DataFormats/Headers/src/DataHeader.cxx index 3401697725e9b..0fe5c446cc5a2 100644 --- a/DataFormats/Headers/src/DataHeader.cxx +++ b/DataFormats/Headers/src/DataHeader.cxx @@ -54,13 +54,16 @@ void o2::header::BaseHeader::throwInconsistentStackError() const //__________________________________________________________________________________________________ void o2::header::DataHeader::print() const { - printf("Data header version %i, flags: %i\n", headerVersion, flags); + printf("Data header version %u, flags: %u\n", headerVersion, flags); printf(" origin : %s\n", dataOrigin.str); printf(" serialization: %s\n", payloadSerializationMethod.str); printf(" description : %s\n", dataDescription.str); printf(" sub spec. : %llu\n", (long long unsigned int)subSpecification); - printf(" header size : %i\n", headerSize); + printf(" header size : %d\n", headerSize); printf(" payloadSize : %llu\n", (long long unsigned int)payloadSize); + printf(" firstTFOrbit : %u\n", firstTForbit); + printf(" tfCounter : %u\n", tfCounter); + printf(" runNumber : %u\n", runNumber); } //__________________________________________________________________________________________________ @@ -116,11 +119,13 @@ o2::header::DataIdentifier::DataIdentifier() //__________________________________________________________________________________________________ bool o2::header::DataIdentifier::operator==(const DataIdentifier& other) const { - if (other.dataOrigin != gDataOriginAny && dataOrigin != other.dataOrigin) + if (other.dataOrigin != gDataOriginAny && dataOrigin != other.dataOrigin) { return false; + } if (other.dataDescription != gDataDescriptionAny && - dataDescription != other.dataDescription) + dataDescription != other.dataDescription) { return false; + } return true; } @@ -140,8 +145,9 @@ void o2::header::hexDump(const char* desc, const void* voidaddr, size_t len, siz const byte* addr = reinterpret_cast<const byte*>(voidaddr); // Output description if given. - if (desc != nullptr) + if (desc != nullptr) { printf("%s, ", desc); + } printf("%zu bytes:", len); if (max > 0 && len > max) { len = max; //limit the output if requested @@ -161,8 +167,9 @@ void o2::header::hexDump(const char* desc, const void* voidaddr, size_t len, siz // Multiple of 16 means new line (with line offset). if ((i % 16) == 0) { // Just don't print ASCII for the zeroth line. - if (i != 0) + if (i != 0) { printf(" %s\n", buff); + } // Output the offset. //printf (" %04x ", i); @@ -173,10 +180,11 @@ void o2::header::hexDump(const char* desc, const void* voidaddr, size_t len, siz printf(" %02x", addr[i]); // And store a printable ASCII character for later. - if ((addr[i] < 0x20) || (addr[i] > 0x7e)) + if ((addr[i] < 0x20) || (addr[i] > 0x7e)) { buff[i % 16] = '.'; - else + } else { buff[i % 16] = addr[i]; + } buff[(i % 16) + 1] = '\0'; fflush(stdout); } diff --git a/DataFormats/Headers/test/testDAQID.cxx b/DataFormats/Headers/test/testDAQID.cxx index 49c741011aa50..404eca33d7b19 100644 --- a/DataFormats/Headers/test/testDAQID.cxx +++ b/DataFormats/Headers/test/testDAQID.cxx @@ -23,6 +23,7 @@ using namespace o2::header; BOOST_AUTO_TEST_CASE(DAQIDTEST) { + for (int i = 0; i < DAQID::MAXDAQ + 5; i++) { auto vo2 = DAQID::DAQtoO2(i); auto daq = DAQID::O2toDAQ(vo2); @@ -32,4 +33,7 @@ BOOST_AUTO_TEST_CASE(DAQIDTEST) BOOST_CHECK(i == daq || vo2 == DAQID::DAQtoO2(DAQID::INVALID)); } std::cout << "DAQ INVALID " << int(DAQID::INVALID) << " <-> " << DAQID::DAQtoO2(DAQID::INVALID).str << std::endl; + std::cout << "DAQ UNLOADED " << int(DAQID::UNLOADED) << " <-> " << DAQID::DAQtoO2(DAQID::UNLOADED).str << std::endl; + BOOST_CHECK(DAQID::DAQtoO2(DAQID::UNLOADED) == o2::header::gDataOriginUnloaded); + BOOST_CHECK(DAQID::O2toDAQ(o2::header::gDataOriginUnloaded) == DAQID::UNLOADED); } diff --git a/DataFormats/Headers/test/testDataHeader.cxx b/DataFormats/Headers/test/testDataHeader.cxx index 6fc03a054d831..6c41a77ea7779 100644 --- a/DataFormats/Headers/test/testDataHeader.cxx +++ b/DataFormats/Headers/test/testDataHeader.cxx @@ -236,9 +236,9 @@ BOOST_AUTO_TEST_CASE(DataHeader_test) << "size " << std::setw(2) << sizeof(dh.payloadSize) << " at " << (char*)(&dh.payloadSize) - (char*)(&dh) << std::endl; } - // DataHeader must have size 88 - static_assert(sizeof(DataHeader) == 88, - "DataHeader struct must be of size 88"); + // DataHeader must have size 96 + static_assert(sizeof(DataHeader) == 96, + "DataHeader struct must be of size 96"); DataHeader dh2; BOOST_CHECK(dh == dh2); DataHeader dh3{gDataDescriptionInvalid, gDataOriginInvalid, DataHeader::SubSpecificationType{0}, 0}; diff --git a/DataFormats/Reconstruction/CMakeLists.txt b/DataFormats/Reconstruction/CMakeLists.txt index 0cd8cad59eb9a..c24054a29dcde 100644 --- a/DataFormats/Reconstruction/CMakeLists.txt +++ b/DataFormats/Reconstruction/CMakeLists.txt @@ -9,28 +9,40 @@ # submit itself to any jurisdiction. o2_add_library(ReconstructionDataFormats - SOURCES src/Track.cxx + SOURCES src/TrackParametrization.cxx + src/TrackParametrizationWithError.cxx + src/TrackFwd.cxx src/BaseCluster.cxx src/TrackTPCITS.cxx src/Vertex.cxx + src/PrimaryVertex.cxx src/MatchInfoTOF.cxx src/TrackLTIntegral.cxx src/PID.cxx src/DCA.cxx + src/V0.cxx + src/VtxTrackIndex.cxx + src/VtxTrackRef.cxx PUBLIC_LINK_LIBRARIES O2::GPUCommon + O2::FrameworkLogger O2::DetectorsCommonDataFormats O2::CommonDataFormat) o2_target_root_dictionary( ReconstructionDataFormats HEADERS include/ReconstructionDataFormats/Track.h + include/ReconstructionDataFormats/TrackFwd.h include/ReconstructionDataFormats/BaseCluster.h include/ReconstructionDataFormats/TrackTPCITS.h include/ReconstructionDataFormats/Vertex.h + include/ReconstructionDataFormats/PrimaryVertex.h include/ReconstructionDataFormats/MatchInfoTOF.h include/ReconstructionDataFormats/TrackLTIntegral.h include/ReconstructionDataFormats/PID.h - include/ReconstructionDataFormats/DCA.h) + include/ReconstructionDataFormats/DCA.h + include/ReconstructionDataFormats/V0.h + include/ReconstructionDataFormats/VtxTrackIndex.h + include/ReconstructionDataFormats/VtxTrackRef.h) o2_add_test(Vertex SOURCES test/testVertex.cxx diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/BaseCluster.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/BaseCluster.h index d00ac377ebcc7..a202249016824 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/BaseCluster.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/BaseCluster.h @@ -11,13 +11,13 @@ #ifndef ALICEO2_BASE_BASECLUSTER_H #define ALICEO2_BASE_BASECLUSTER_H +#include <MathUtils/Cartesian.h> #include <TObject.h> #include <bitset> #include <iomanip> #include <ios> #include <iosfwd> #include "DetectorsCommonDataFormats/DetMatrixCache.h" -#include "MathUtils/Cartesian3D.h" namespace o2 { @@ -30,7 +30,7 @@ template <typename T> class BaseCluster { private: - Point3D<T> mPos; // cartesian position + math_utils::Point3D<T> mPos; // cartesian position T mSigmaY2; // error in Y direction (usually rphi) T mSigmaZ2; // error in Z direction (usually Z) T mSigmaYZ; // non-diagonal term of error matrix @@ -44,9 +44,9 @@ class BaseCluster ~BaseCluster() = default; // constructor - BaseCluster(std::uint16_t sensid, const Point3D<T>& xyz) : mPos(xyz), mSensorID(sensid) {} + BaseCluster(std::uint16_t sensid, const math_utils::Point3D<T>& xyz) : mPos(xyz), mSensorID(sensid) {} BaseCluster(std::uint16_t sensid, T x, T y, T z) : mPos(x, y, z), mSensorID(sensid) {} - BaseCluster(std::uint16_t sensid, const Point3D<T>& xyz, T sy2, T sz2, T syz) + BaseCluster(std::uint16_t sensid, const math_utils::Point3D<T>& xyz, T sy2, T sz2, T syz) : mPos(xyz), mSigmaY2(sy2), mSigmaZ2(sz2), mSigmaYZ(syz), mSensorID(sensid) { } @@ -62,16 +62,16 @@ class BaseCluster T getSigmaY2() const { return mSigmaY2; } T getSigmaZ2() const { return mSigmaZ2; } T getSigmaYZ() const { return mSigmaYZ; } - Point3D<T> getXYZ() const { return mPos; } - Point3D<T>& getXYZ() { return mPos; } + math_utils::Point3D<T> getXYZ() const { return mPos; } + math_utils::Point3D<T>& getXYZ() { return mPos; } // position in local frame, no check for matrices cache validity - Point3D<T> getXYZLoc(const o2::detectors::DetMatrixCache& dm) const { return dm.getMatrixT2L(mSensorID)(mPos); } + math_utils::Point3D<T> getXYZLoc(const o2::detectors::DetMatrixCache& dm) const { return dm.getMatrixT2L(mSensorID)(mPos); } // position in global frame, no check for matrices cache validity - Point3D<T> getXYZGlo(const o2::detectors::DetMatrixCache& dm) const { return dm.getMatrixT2G(mSensorID)(mPos); } + math_utils::Point3D<T> getXYZGlo(const o2::detectors::DetMatrixCache& dm) const { return dm.getMatrixT2G(mSensorID)(mPos); } // position in global frame obtained as simple rotation from tracking one: // much faster for barrel detectors than using full 3D matrix. // no check for matrices cache validity - Point3D<T> getXYZGloRot(const o2::detectors::DetMatrixCache& dm) const { return dm.getMatrixT2GRot(mSensorID)(mPos); } + math_utils::Point3D<T> getXYZGloRot(const o2::detectors::DetMatrixCache& dm) const { return dm.getMatrixT2GRot(mSensorID)(mPos); } // get sensor id std::int16_t getSensorID() const { return mSensorID; } // get count field @@ -80,7 +80,7 @@ class BaseCluster std::uint8_t getBits() const { return mBits; } bool isBitSet(int bit) const { return mBits & (0xff & (0x1 << bit)); } // cast to Point3D - operator Point3D<T>&() { return mPos; } + operator math_utils::Point3D<T>&() { return mPos; } // modifiers // set sensor id @@ -101,7 +101,7 @@ class BaseCluster setY(y); setZ(z); } - void setPos(const Point3D<T>& p) { mPos = p; } + void setPos(const math_utils::Point3D<T>& p) { mPos = p; } void setSigmaY2(T v) { mSigmaY2 = v; } void setSigmaZ2(T v) { mSigmaZ2 = v; } void setSigmaYZ(T v) { mSigmaYZ = v; } @@ -113,7 +113,6 @@ class BaseCluster } protected: - ClassDefNV(BaseCluster, 2); }; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h index 2b827d5bb740d..ff4167b59fdbb 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h @@ -11,12 +11,11 @@ #ifndef ALICEO2_DCA_H #define ALICEO2_DCA_H +#include "GPUCommonDef.h" #include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" -#ifndef __OPENCL__ -#include <array> -#endif -#ifndef ALIGPU_GPUCODE +#ifndef GPUCA_GPUCODE_DEVICE #include <iosfwd> #endif @@ -32,14 +31,14 @@ class DCA { public: - DCA() = default; + GPUdDefault() DCA() = default; - DCA(float y, float z, float syy = 0.f, float syz = 0.f, float szz = 0.f) + GPUd() DCA(float y, float z, float syy = 0.f, float syz = 0.f, float szz = 0.f) { set(y, z, syy, syz, szz); } - void set(float y, float z, float syy, float syz, float szz) + GPUd() void set(float y, float z, float syy, float syz, float szz) { mY = y; mZ = z; @@ -48,30 +47,32 @@ class DCA mCov[2] = szz; } - void set(float y, float z) + GPUd() void set(float y, float z) { mY = y; mZ = z; } - auto getY() const { return mY; } - auto getZ() const { return mZ; } - auto getSigmaY2() const { return mCov[0]; } - auto getSigmaYZ() const { return mCov[1]; } - auto getSigmaZ2() const { return mCov[2]; } - const auto& getCovariance() const { return mCov; } + GPUd() auto getY() const { return mY; } + GPUd() auto getZ() const { return mZ; } + GPUd() auto getSigmaY2() const { return mCov[0]; } + GPUd() auto getSigmaYZ() const { return mCov[1]; } + GPUd() auto getSigmaZ2() const { return mCov[2]; } + GPUd() const auto& getCovariance() const { return mCov; } void print() const; private: float mY = 0.f; float mZ = 0.f; - std::array<float, 3> mCov; ///< s2y, syz, s2z + gpu::gpustd::array<float, 3> mCov; ///< s2y, syz, s2z ClassDefNV(DCA, 1); }; +#ifndef GPUCA_GPUCODE_DEVICE std::ostream& operator<<(std::ostream& os, const DCA& d); +#endif } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h index 6e3df2f92cd7a..22644a7fec457 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h @@ -15,18 +15,65 @@ #ifndef ALICEO2_track_PID_H_ #define ALICEO2_track_PID_H_ -#include <Rtypes.h> +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" #include "CommonConstants/PhysicsConstants.h" namespace o2 { namespace track { +namespace o2cp = o2::constants::physics; + +namespace pid_constants // GPUs currently cannot have static constexpr array members +{ +typedef uint8_t ID; +static constexpr ID NIDsTot = 14; +GPUconstexpr() const char* sNames[NIDsTot + 1] = ///< defined particle names + {"Electron", "Muon", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "He3", "Alpha", + "Pion0", "Photon", "K0", "Lambda", "HyperTriton", + nullptr}; + +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}; + +GPUconstexpr() const float sMasses2[NIDsTot] = ///< defined particle masses^2 + {o2cp::MassElectron * o2cp::MassElectron, + o2cp::MassMuon* o2cp::MassMuon, + o2cp::MassPionCharged* o2cp::MassPionCharged, + o2cp::MassKaonCharged* o2cp::MassKaonCharged, + o2cp::MassProton* o2cp::MassProton, + o2cp::MassDeuteron* o2cp::MassDeuteron, + o2cp::MassTriton* o2cp::MassTriton, + o2cp::MassHelium3* o2cp::MassHelium3, + o2cp::MassAlpha* o2cp::MassAlpha, + o2cp::MassPionNeutral* o2cp::MassPionNeutral, + o2cp::MassPhoton* o2cp::MassPhoton, + o2cp::MassKaonNeutral* o2cp::MassKaonNeutral, + o2cp::MassLambda* o2cp::MassLambda, + o2cp::MassHyperTriton* o2cp::MassHyperTriton}; + +GPUconstexpr() const float sMasses2Z[NIDsTot] = ///< defined particle masses / Z + {o2cp::MassElectron, o2cp::MassMuon, + o2cp::MassPionCharged, o2cp::MassKaonCharged, + o2cp::MassProton, o2cp::MassDeuteron, + o2cp::MassTriton, o2cp::MassHelium3 / 2., + o2cp::MassAlpha / 2., + 0, 0, 0, 0, o2cp::MassHyperTriton}; + +GPUconstexpr() const int sCharges[NIDsTot] = ///< defined particle charges + {1, 1, 1, 1, 1, 1, 1, 2, 2, + 0, 0, 0, 0, 1}; +} // namespace pid_constants + class PID { public: // particle identifiers, continuos starting from 0 - typedef std::int32_t ID; + typedef pid_constants::ID ID; static constexpr ID Electron = 0; static constexpr ID Muon = 1; @@ -40,61 +87,62 @@ class PID static constexpr ID First = Electron; static constexpr ID Last = Alpha; ///< if extra IDs added, update this !!! - static constexpr int NIDs = Last + 1; ///< number of defined IDs - - PID() = default; - PID(ID id) : mID(id) {} - PID(const char* name); - PID(const PID& src) = default; - PID& operator=(const PID& src) = default; - - ID getID() const { return mID; } - - float getMass() const { return getMass(mID); } - float getMass2Z() const { return getMass2Z(mID); } - int getCharge() const { return getCharge(mID); } - const char* getName() const { return getName(mID); } - - static constexpr const char* getName(ID id) { return sNames[id]; } - static constexpr float getMass(ID id) { return sMasses[id]; } - static constexpr float getMass2Z(ID id) { return sMasses2Z[id]; } - static constexpr int getCharge(ID id) { return sCharges[id]; } + static constexpr ID NIDs = Last + 1; ///< number of defined IDs + + // PID for derived particles + static constexpr ID PI0 = 9; + static constexpr ID Photon = 10; + static constexpr ID K0 = 11; + static constexpr ID Lambda = 12; + static constexpr ID HyperTriton = 13; + static constexpr ID FirstExt = PI0; + static constexpr ID LastExt = HyperTriton; + static constexpr ID NIDsTot = pid_constants::NIDsTot; ///< total number of defined IDs + static_assert(NIDsTot == LastExt + 1, "Incorrect NIDsTot, please update!"); + + GPUdDefault() PID() = default; + GPUd() PID(ID id) : mID(id) {} + GPUd() PID(const char* name); + GPUdDefault() PID(const PID& src) = default; + GPUdDefault() PID& operator=(const PID& src) = default; + + GPUd() ID getID() const { return mID; } + GPUd() operator ID() const { return getID(); } + + GPUd() float getMass() const { return getMass(mID); } + GPUd() float getMass2() const { return getMass2(mID); } + GPUd() float getMass2Z() const { return getMass2Z(mID); } + GPUd() int getCharge() const { return getCharge(mID); } + + GPUd() static float getMass(ID id) { return pid_constants::sMasses[id]; } + 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 + GPUd() const char* getName() const + { + return getName(mID); + } + GPUd() static const char* getName(ID id) { return pid_constants::sNames[id]; } +#endif private: ID mID = Pion; // are 2 strings equal ? (trick from Giulio) - inline static constexpr bool sameStr(char const* x, char const* y) + GPUdi() static constexpr bool sameStr(char const* x, char const* y) { return !*x && !*y ? true : /* default */ (*x == *y && sameStr(x + 1, y + 1)); } - inline static constexpr int nameToID(char const* name, int id) +#ifndef GPUCA_GPUCODE_DEVICE + GPUdi() static constexpr ID nameToID(char const* name, ID id) { - return id > Last ? id : sameStr(name, sNames[id]) ? id : nameToID(name, id + 1); + return id > LastExt ? id : sameStr(name, pid_constants::sNames[id]) ? id : nameToID(name, id + 1); } +#endif - static constexpr const char* sNames[NIDs + 1] = ///< defined particle names - {"Electron", "Muon", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "He3", "Alpha", nullptr}; - - static constexpr const float sMasses[NIDs] = ///< defined particle masses - {o2::constants::physics::MassElectron, o2::constants::physics::MassMuon, - o2::constants::physics::MassPionCharged, o2::constants::physics::MassKaonCharged, - o2::constants::physics::MassProton, o2::constants::physics::MassDeuteron, - o2::constants::physics::MassTriton, o2::constants::physics::MassHelium3, - o2::constants::physics::MassAlpha}; - - static constexpr const float sMasses2Z[NIDs] = ///< defined particle masses / Z - {o2::constants::physics::MassElectron, o2::constants::physics::MassMuon, - o2::constants::physics::MassPionCharged, o2::constants::physics::MassKaonCharged, - o2::constants::physics::MassProton, o2::constants::physics::MassDeuteron, - o2::constants::physics::MassTriton, o2::constants::physics::MassHelium3 / 2., - o2::constants::physics::MassAlpha / 2.}; - - static constexpr const int sCharges[NIDs] = ///< defined particle charges - {1, 1, 1, 1, 1, 1, 1, 2, 2}; - - ClassDefNV(PID, 1); + ClassDefNV(PID, 2); }; } // namespace track } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h new file mode 100644 index 0000000000000..872e7517e8ff6 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h @@ -0,0 +1,68 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_H +#define ALICEO2_PRIMARYVERTEX_H + +#include "CommonDataFormat/InteractionRecord.h" +#include "ReconstructionDataFormats/Vertex.h" + +namespace o2 +{ +namespace dataformats +{ + +// primary vertex class: position, time with error + IR (by default: not assigned) + +class PrimaryVertex : public Vertex<TimeStampWithError<float, float>> +{ + public: + using Vertex<TimeStampWithError<float, float>>::Vertex; + PrimaryVertex() = default; + PrimaryVertex(const PrimaryVertex&) = default; + ~PrimaryVertex() = default; + + const InteractionRecord& getIRMax() const { return mIRMax; } + void setIRMax(const InteractionRecord& ir) { mIRMax = ir; } + const InteractionRecord& getIRMin() const { return mIRMin; } + void setIRMin(const InteractionRecord& ir) { mIRMin = ir; } + void setIR(const InteractionRecord& ir) { mIRMin = mIRMax = ir; } + bool hasUniqueIR() const { return !mIRMin.isDummy() && (mIRMin == mIRMax); } + +#ifndef GPUCA_ALIGPUCODE + void print() const; + std::string asString() const; +#endif + + protected: + InteractionRecord mIRMin{}; ///< by default not assigned! + InteractionRecord mIRMax{}; ///< by default not assigned! + + ClassDefNV(PrimaryVertex, 1); +}; + +#ifndef GPUCA_ALIGPUCODE +std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertex& v); +#endif + +} // namespace dataformats + +/// Defining PrimaryVertex explicitly as messageable +namespace framework +{ +template <typename T> +struct is_messageable; +template <> +struct is_messageable<o2::dataformats::PrimaryVertex> : std::true_type { +}; +} // namespace framework + +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Track.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Track.h index 68d6505f8ede2..2a4a57753a580 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Track.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Track.h @@ -10,452 +10,21 @@ /// \file Track.h /// \brief Base track model for the Barrel, params only, w/o covariance -/// \author ruben.shahoyan@cern.ch +/// \author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch #ifndef ALICEO2_BASE_TRACK #define ALICEO2_BASE_TRACK -#include "GPUCommonRtypes.h" - -#ifndef __OPENCL__ -#include <algorithm> -#include <array> -#include <cfloat> -#include <cmath> -#include <cstring> -#include <iosfwd> -#endif - -#ifndef GPUCA_ALIGPUCODE //Used only by functions that are hidden on the GPU -#include "ReconstructionDataFormats/BaseCluster.h" -#include <string> -#endif - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include "MathUtils/Primitive2D.h" - -//Forward declarations, since we cannot include the headers if we eventually want to use track.h on GPU -namespace ROOT -{ -namespace Math -{ -template <class T, unsigned int D1, unsigned int D2, class R> -class SMatrix; -template <class T, unsigned int D> -class MatRepSym; -template <class T, unsigned int D1, unsigned int D2> -class MatRepStd; -template <class CoordSystem, class Tag> -class PositionVector3D; -template <class T> -class Cartesian3D; -class DefaultCoordinateSystemTag; -} // namespace Math -} // namespace ROOT -template <typename T> -using Point3D = ROOT::Math::PositionVector3D<ROOT::Math::Cartesian3D<T>, ROOT::Math::DefaultCoordinateSystemTag>; +#include "ReconstructionDataFormats/TrackParametrization.h" +#include "ReconstructionDataFormats/TrackParametrizationWithError.h" namespace o2 { -template <typename T> -class BaseCluster; - -namespace dataformats -{ -class VertexBase; -class DCA; -} // namespace dataformats - namespace track { -// aliases for track elements -enum ParLabels : int { kY, - kZ, - kSnp, - kTgl, - kQ2Pt }; -enum CovLabels : int { - kSigY2, - kSigZY, - kSigZ2, - kSigSnpY, - kSigSnpZ, - kSigSnp2, - kSigTglY, - kSigTglZ, - kSigTglSnp, - kSigTgl2, - kSigQ2PtY, - kSigQ2PtZ, - kSigQ2PtSnp, - kSigQ2PtTgl, - kSigQ2Pt2 -}; - -enum DirType : int { DirInward = -1, - DirAuto = 0, - DirOutward = 1 }; - -constexpr int kNParams = 5, kCovMatSize = 15, kLabCovMatSize = 21; - -constexpr float kCY2max = 100 * 100, // SigmaY<=100cm - kCZ2max = 100 * 100, // SigmaZ<=100cm - kCSnp2max = 1 * 1, // SigmaSin<=1 - kCTgl2max = 1 * 1, // SigmaTan<=1 - kC1Pt2max = 100 * 100, // Sigma1/Pt<=100 1/GeV - kMostProbablePt = 0.6, // Most Probable Pt (GeV), for running with Bz=0 - kCalcdEdxAuto = -999.f; // value indicating request for dedx calculation - -// access to covariance matrix by row and column -constexpr int CovarMap[kNParams][kNParams] = {{0, 1, 3, 6, 10}, - {1, 2, 4, 7, 11}, - {3, 4, 5, 8, 12}, - {6, 7, 8, 9, 13}, - {10, 11, 12, 13, 14}}; - -// access to covariance matrix diagonal elements -constexpr int DiagMap[kNParams] = {0, 2, 5, 9, 14}; - -constexpr float HugeF = o2::constants::math::VeryBig; - -// helper function -float BetheBlochSolid(float bg, float rho = 2.33f, float kp1 = 0.20f, float kp2 = 3.00f, float meanI = 173e-9f, - float meanZA = 0.49848f); -void g3helx3(float qfield, float step, std::array<float, 7>& vect); - -class TrackPar -{ // track parameterization, kinematics only. - public: - TrackPar() = default; - TrackPar(float x, float alpha, const std::array<float, kNParams>& par); - TrackPar(const std::array<float, 3>& xyz, const std::array<float, 3>& pxpypz, int sign, bool sectorAlpha = true); - TrackPar(const TrackPar&) = default; - TrackPar& operator=(const TrackPar& src) = default; - ~TrackPar() = default; - - const float* getParams() const { return mP; } - float getParam(int i) const { return mP[i]; } - float getX() const { return mX; } - float getAlpha() const { return mAlpha; } - float getY() const { return mP[kY]; } - float getZ() const { return mP[kZ]; } - float getSnp() const { return mP[kSnp]; } - float getTgl() const { return mP[kTgl]; } - float getQ2Pt() const { return mP[kQ2Pt]; } - - /// calculate cos^2 and cos of track direction in rphi-tracking - float getCsp2() const - { - float csp2 = (1. - mP[kSnp]) * (1. + mP[kSnp]); - return csp2 > o2::constants::math::Almost0 ? csp2 : o2::constants::math::Almost0; - } - float getCsp() const { return sqrtf(getCsp2()); } - - void setX(float v) { mX = v; } - void setParam(float v, int i) { mP[i] = v; } - void setAlpha(float v) { mAlpha = v; } - void setY(float v) { mP[kY] = v; } - void setZ(float v) { mP[kZ] = v; } - void setSnp(float v) { mP[kSnp] = v; } - void setTgl(float v) { mP[kTgl] = v; } - void setQ2Pt(float v) { mP[kQ2Pt] = v; } - - // derived getters - bool getXatLabR(float r, float& x, float bz, DirType dir = DirAuto) const; - void getCircleParamsLoc(float bz, o2::utils::CircleXY& circle) const; - void getCircleParams(float bz, o2::utils::CircleXY& circle, float& sna, float& csa) const; - void getLineParams(o2::utils::IntervalXY& line, float& sna, float& csa) const; - float getCurvature(float b) const { return mP[kQ2Pt] * b * o2::constants::math::B2C; } - float getSign() const { return mP[kQ2Pt] > 0 ? 1.f : -1.f; } - float getPhi() const; - float getPhiPos() const; - - float getP2Inv() const; - float getP2() const; - float getPInv() const; - float getP() const; - float getPt() const; - - float getTheta() const { return constants::math::PIHalf - std::atan(mP[3]); } - float getEta() const { return -std::log(std::tan(0.5 * getTheta())); } - Point3D<float> getXYZGlo() const; - void getXYZGlo(std::array<float, 3>& xyz) const; - bool getPxPyPzGlo(std::array<float, 3>& pxyz) const; - bool getPosDirGlo(std::array<float, 9>& posdirp) const; - - // methods for track params estimate at other point - bool getYZAt(float xk, float b, float& y, float& z) const; - float getZAt(float xk, float b) const; - float getYAt(float xk, float b) const; - Point3D<float> getXYZGloAt(float xk, float b, bool& ok) const; - - // parameters manipulation - bool correctForELoss(float xrho, float mass, bool anglecorr = false, float dedx = kCalcdEdxAuto); - bool rotateParam(float alpha); - bool propagateParamTo(float xk, float b); - bool propagateParamTo(float xk, const std::array<float, 3>& b); - - bool propagateParamToDCA(const Point3D<float>& vtx, float b, std::array<float, 2>* dca = nullptr, float maxD = 999.f); - - void invertParam(); - -#ifndef GPUCA_ALIGPUCODE - void printParam() const; - std::string asString() const; -#endif - - protected: - void updateParam(float delta, int i) { mP[i] += delta; } - void updateParams(const float delta[kNParams]) - { - for (int i = kNParams; i--;) { - mP[i] += delta[i]; - } - } - // derived getters - - private: - // - float mX = 0.f; /// X of track evaluation - float mAlpha = 0.f; /// track frame angle - float mP[kNParams] = {0.f}; /// 5 parameters: Y,Z,sin(phi),tg(lambda),q/pT - - ClassDefNV(TrackPar, 1); -}; - -class TrackParCov : public TrackPar -{ // track+error parameterization - using MatrixDSym5 = ROOT::Math::SMatrix<double, kNParams, kNParams, ROOT::Math::MatRepSym<double, kNParams>>; - using MatrixD5 = ROOT::Math::SMatrix<double, kNParams, kNParams, ROOT::Math::MatRepStd<double, kNParams, kNParams>>; - - public: - TrackParCov() : TrackPar{} {} - TrackParCov(float x, float alpha, const std::array<float, kNParams>& par, const std::array<float, kCovMatSize>& cov); - TrackParCov(const std::array<float, 3>& xyz, const std::array<float, 3>& pxpypz, - const std::array<float, kLabCovMatSize>& cv, int sign, bool sectorAlpha = true); - TrackParCov(const TrackParCov& src) = default; - TrackParCov& operator=(const TrackParCov& src) = default; - ~TrackParCov() = default; - - const float* getCov() const { return mC; } - float getSigmaY2() const { return mC[kSigY2]; } - float getSigmaZY() const { return mC[kSigZY]; } - float getSigmaZ2() const { return mC[kSigZ2]; } - float getSigmaSnpY() const { return mC[kSigSnpY]; } - float getSigmaSnpZ() const { return mC[kSigSnpZ]; } - float getSigmaSnp2() const { return mC[kSigSnp2]; } - float getSigmaTglY() const { return mC[kSigTglY]; } - float getSigmaTglZ() const { return mC[kSigTglZ]; } - float getSigmaTglSnp() const { return mC[kSigTglSnp]; } - float getSigmaTgl2() const { return mC[kSigTgl2]; } - float getSigma1PtY() const { return mC[kSigQ2PtY]; } - float getSigma1PtZ() const { return mC[kSigQ2PtZ]; } - float getSigma1PtSnp() const { return mC[kSigQ2PtSnp]; } - float getSigma1PtTgl() const { return mC[kSigQ2PtTgl]; } - float getSigma1Pt2() const { return mC[kSigQ2Pt2]; } - float getCovarElem(int i, int j) const { return mC[CovarMap[i][j]]; } - float getDiagError2(int i) const { return mC[DiagMap[i]]; } - -#ifndef GPUCA_ALIGPUCODE - void print() const; - std::string asString() const; -#endif - - // parameters + covmat manipulation - bool rotate(float alpha); - bool propagateTo(float xk, float b); - bool propagateTo(float xk, const std::array<float, 3>& b); - bool propagateToDCA(const o2::dataformats::VertexBase& vtx, float b, o2::dataformats::DCA* dca = nullptr, float maxD = 999.f); - void invert(); - - float getPredictedChi2(const std::array<float, 2>& p, const std::array<float, 3>& cov) const; - - template <typename T> - float getPredictedChi2(const BaseCluster<T>& p) const - { - const std::array<float, 2> pyz = {p.getY(), p.getZ()}; - const std::array<float, 3> cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()}; - return getPredictedChi2(pyz, cov); - } - float getPredictedChi2(const TrackParCov& rhs) const; - - void buildCombinedCovMatrix(const TrackParCov& rhs, MatrixDSym5& cov) const; - float getPredictedChi2(const TrackParCov& rhs, MatrixDSym5& covToSet) const; - bool update(const TrackParCov& rhs, const MatrixDSym5& covInv); - - bool update(const std::array<float, 2>& p, const std::array<float, 3>& cov); - - template <typename T> - bool update(const BaseCluster<T>& p) - { - const std::array<float, 2> pyz = {p.getY(), p.getZ()}; - const std::array<float, 3> cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()}; - return update(pyz, cov); - } - - bool update(const TrackParCov& rhs); - - bool correctForMaterial(float x2x0, float xrho, float mass, bool anglecorr = false, float dedx = kCalcdEdxAuto); - - void resetCovariance(float s2 = 0); - void checkCovariance(); - void setCov(float v, int i) { mC[i] = v; } - - protected: - void updateCov(const float delta[kCovMatSize]) - { - for (int i = kCovMatSize; i--;) - mC[i] += delta[i]; - } - - protected: - float mC[kCovMatSize] = {0.f}; // 15 covariance matrix elements - - ClassDefNV(TrackParCov, 1); -}; - -//____________________________________________________________ -inline TrackPar::TrackPar(float x, float alpha, const std::array<float, kNParams>& par) : mX{x}, mAlpha{alpha} -{ - // explicit constructor - std::copy(par.begin(), par.end(), mP); -} - -//_______________________________________________________ -inline void TrackPar::getLineParams(o2::utils::IntervalXY& ln, float& sna, float& csa) const -{ - // get line parameterization as { x = x0 + xSlp*t, y = y0 + ySlp*t } - o2::utils::sincos(getAlpha(), sna, csa); - o2::utils::rotateZ(getX(), getY(), ln.xP, ln.yP, sna, csa); // reference point in global frame - float snp = getSnp(), csp = sqrtf((1.f - snp) * (1.f + snp)); - ln.dxP = csp * csa - snp * sna; - ln.dyP = snp * csa + csp * sna; -} - -//_______________________________________________________ -inline void TrackPar::getCircleParams(float bz, o2::utils::CircleXY& c, float& sna, float& csa) const -{ - // get circle params in loc and lab frame, for straight line just set to global coordinates - getCircleParamsLoc(bz, c); - o2::utils::sincos(getAlpha(), sna, csa); - o2::utils::rotateZ(c.xC, c.yC, c.xC, c.yC, sna, csa); // center in global frame -} - -//_______________________________________________________ -inline void TrackPar::getCircleParamsLoc(float bz, o2::utils::CircleXY& c) const -{ - // get circle params in track local frame, for straight line just set to local coordinates - c.rC = getCurvature(bz); - if (std::abs(c.rC) > o2::constants::math::Almost0) { - c.rC = 1.f / getCurvature(bz); - float sn = getSnp(), cs = sqrtf((1. - sn) * (1. + sn)); - c.xC = getX() - sn * c.rC; // center in tracking - c.yC = getY() + cs * c.rC; // frame. Note: r is signed!!! - c.rC = fabs(c.rC); - } else { - c.rC = 0.f; // signal straight line - c.xC = getX(); - c.yC = getY(); - } -} - -//_______________________________________________________ -inline void TrackPar::getXYZGlo(std::array<float, 3>& xyz) const -{ - // track coordinates in lab frame - xyz[0] = getX(); - xyz[1] = getY(); - xyz[2] = getZ(); - utils::RotateZ(xyz, getAlpha()); -} - -//_______________________________________________________ -inline float TrackPar::getPhi() const -{ - // track pt direction phi (in 0:2pi range) - float phi = asinf(getSnp()) + getAlpha(); - utils::BringTo02Pi(phi); - return phi; -} - -#ifndef GPUCA_ALIGPUCODE //These functions clash with GPU code and are thus hidden -//_______________________________________________________ -inline Point3D<float> TrackPar::getXYZGlo() const -{ - return Rotation2D(getAlpha())(Point3D<float>(getX(), getY(), getZ())); -} - -//_______________________________________________________ -inline Point3D<float> TrackPar::getXYZGloAt(float xk, float b, bool& ok) const -{ - //---------------------------------------------------------------- - // estimate global X,Y,Z in global frame at given X - //---------------------------------------------------------------- - float y = 0.f, z = 0.f; - ok = getYZAt(xk, b, y, z); - return ok ? Rotation2D(getAlpha())(Point3D<float>(xk, y, z)) : Point3D<float>(); -} -#endif - -//_______________________________________________________ -inline float TrackPar::getPhiPos() const -{ - // angle of track position (in -pi:pi range) - float phi = atan2f(getY(), getX()) + getAlpha(); - utils::BringTo02Pi(phi); - return phi; -} - -//____________________________________________________________ -inline float TrackPar::getP2Inv() const -{ - // return the inverted track momentum^2 - return getQ2Pt() * getQ2Pt() / (1.f + getTgl() * getTgl()); -} - -//____________________________________________________________ -inline float TrackPar::getPInv() const -{ - // return the inverted track momentum^2 - return fabs(getQ2Pt()) / sqrtf(1.f + getTgl() * getTgl()); -} - -//____________________________________________________________ -inline float TrackPar::getP2() const -{ - // return the track momentum^2 - auto p2inv = getP2Inv(); - return (p2inv > o2::constants::math::Almost0) ? 1. / p2inv : o2::constants::math::VeryBig; -} - -//____________________________________________________________ -inline float TrackPar::getP() const -{ - // return the track momentum - float pInv = getPInv(); - return (pInv > o2::constants::math::Almost0) ? 1. / pInv : o2::constants::math::VeryBig; -} - -//____________________________________________________________ -inline float TrackPar::getPt() const -{ - // return the track transverse momentum - float ptI = fabs(getQ2Pt()); - return (ptI > o2::constants::math::Almost0) ? 1.f / ptI : o2::constants::math::VeryBig; -} - -//============================================================ - -//____________________________________________________________ -inline TrackParCov::TrackParCov(float x, float alpha, const std::array<float, kNParams>& par, - const std::array<float, kCovMatSize>& cov) - : TrackPar{x, alpha, par} -{ - // explicit constructor - std::copy(cov.begin(), cov.end(), mC); -} +using TrackPar = TrackParametrization<float>; +using TrackParCov = TrackParametrizationWithError<float>; } // namespace track } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h new file mode 100644 index 0000000000000..bf9749a55d1ca --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h @@ -0,0 +1,165 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackFwd.h +/// \brief Base forward track model, params only, w/o covariance +/// +/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS + +#ifndef ALICEO2_BASE_TRACKFWD +#define ALICEO2_BASE_TRACKFWD + +#include <Rtypes.h> +#include <TMath.h> +#include "Math/SMatrix.h" +#include "MathUtils/Utils.h" + +namespace o2 +{ +namespace track +{ + +using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>; +using SMatrix5 = ROOT::Math::SVector<Double_t, 5>; + +class TrackParFwd +{ // Forward track parameterization, kinematics only. + public: + TrackParFwd() = default; + ~TrackParFwd() = default; + + TrackParFwd(const TrackParFwd& tp) = default; + TrackParFwd& operator=(const TrackParFwd& tp) = default; + TrackParFwd(TrackParFwd&&) = delete; + TrackParFwd& operator=(TrackParFwd&&) = delete; + + /// return Z coordinate (cm) + Double_t getZ() const { return mZ; } + /// set Z coordinate (cm) + void setZ(Double_t z) { mZ = z; } + Double_t getX() const { return mParameters(0); } + void setX(Double_t x) { mParameters(0) = x; } + + Double_t getY() const { return mParameters(1); } + void setY(Double_t y) { mParameters(1) = y; } + + void setPhi(Double_t phi) { mParameters(2) = phi; } + Double_t getPhi() const { return mParameters(2); } + + void setTanl(Double_t tanl) { mParameters(3) = tanl; } + Double_t getTanl() const { return mParameters(3); } + + void setInvQPt(Double_t invqpt) { mParameters(4) = invqpt; } + Double_t getInvQPt() const { return mParameters(4); } // return Inverse charged pt + Double_t getPt() const { return TMath::Abs(1.f / mParameters(4)); } + Double_t getInvPt() const { return TMath::Abs(mParameters(4)); } + + Double_t getPx() const { return TMath::Cos(getPhi()) * getPt(); } // return px + Double_t getInvPx() const { return 1. / getPx(); } // return invpx + + Double_t getPy() const { return TMath::Sin(getPhi()) * getPt(); } // return py + Double_t getInvPy() const { return 1. / getPx(); } // return invpy + + Double_t getPz() const { return getTanl() * getPt(); } // return pz + Double_t getInvPz() const { return 1. / getPz(); } // return invpz + + Double_t getP() const { return getPt() * TMath::Sqrt(1. + getTanl() * getTanl()); } // return total momentum + Double_t getInverseMomentum() const { return 1.f / getP(); } + + Double_t getEta() const { return -TMath::Log(TMath::Tan((TMath::PiOver2() - TMath::ATan(getTanl())) / 2)); } // return total momentum + + /// return the charge (assumed forward motion) + Double_t getCharge() const { return TMath::Sign(1., mParameters(4)); } + /// set the charge (assumed forward motion) + void setCharge(Double_t charge) + { + if (charge * mParameters(4) < 0.) { + mParameters(4) *= -1.; + } + } + + /// return track parameters + const SMatrix5& getParameters() const { return mParameters; } + /// set track parameters + void setParameters(const SMatrix5& parameters) { mParameters = parameters; } + /// add track parameters + void addParameters(const SMatrix5& parameters) { mParameters += parameters; } + + /// return the chi2 of the track when the associated cluster was attached + Double_t getTrackChi2() const { return mTrackChi2; } + /// set the chi2 of the track when the associated cluster was attached + void setTrackChi2(Double_t chi2) { mTrackChi2 = chi2; } + + // Track parameter propagation + void propagateParamToZlinear(double zEnd); + void propagateParamToZquadratic(double zEnd, double zField); + void propagateParamToZhelix(double zEnd, double zField); + + protected: + Double_t mZ = 0.; ///< Z coordinate (cm) + + /// Track parameters ordered as follow: <pre> + /// X = X coordinate (cm) + /// Y = Y coordinate (cm) + /// PHI = azimutal angle + /// TANL = tangent of \lambda (dip angle) + /// INVQPT = Inverse transverse momentum (GeV/c ** -1) times charge (assumed forward motion) </pre> + SMatrix5 mParameters{}; ///< \brief Track parameters + Double_t mTrackChi2 = 0.; ///< Chi2 of the track when the associated cluster was attached + + ClassDefNV(TrackParFwd, 1); +}; + +class TrackParCovFwd : public TrackParFwd +{ // Forward track+error parameterization + public: + using TrackParFwd::TrackParFwd; // inherit base constructors + + TrackParCovFwd() = default; + ~TrackParCovFwd() = default; + TrackParCovFwd& operator=(const TrackParCovFwd& tpf) = default; + TrackParCovFwd(const Double_t z, const SMatrix5 parameters, const SMatrix55 covariances, const Double_t chi2); + + const SMatrix55& getCovariances() const { return mCovariances; } + void setCovariances(const SMatrix55& covariances) { mCovariances = covariances; } + void deleteCovariances() { mCovariances = SMatrix55(); } + + Double_t getSigma2X() const { return mCovariances(0, 0); } + Double_t getSigma2Y() const { return mCovariances(1, 1); } + Double_t getSigma2Phi() const { return mCovariances(2, 2); } + Double_t getSigma2Tanl() const { return mCovariances(3, 3); } + Double_t getSigma2InvQPt() const { return mCovariances(4, 4); } + + // Propagate parameters and covariances matrix + void propagateToZlinear(double zEnd); + void propagateToZquadratic(double zEnd, double zField); + void propagateToZhelix(double zEnd, double zField); + + // Add Multiple Coulomb Scattering effects + void addMCSEffect(double dZ, double x2X0); + + // Kalman filter/fitting + bool update(const std::array<float, 2>& p, const std::array<float, 2>& cov); + + private: + /// Covariance matrix of track parameters, ordered as follows: <pre> + /// <X,X> <Y,X> <PHI,X> <TANL,X> <INVQPT,X> + /// <X,Y> <Y,Y> <PHI,Y> <TANL,Y> <INVQPT,Y> + /// <X,PHI> <Y,PHI> <PHI,PHI> <TANL,PHI> <INVQPT,PHI> + /// <X,TANL> <Y,TANL> <PHI,TANL> <TANL,TANL> <INVQPT,TANL> + /// <X,INVQPT> <Y,INVQPT> <PHI,INVQPT> <TANL,INVQPT> <INVQPT,INVQPT> </pre> + SMatrix55 mCovariances{}; ///< \brief Covariance matrix of track parameters + ClassDefNV(TrackParCovFwd, 1); +}; + +} // namespace track +} // namespace o2 + +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h index 4122c38be2c19..a022717d553df 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h @@ -15,7 +15,8 @@ #ifndef ALICEO2_TRACK_LTINTEGRAL_H_ #define ALICEO2_TRACK_LTINTEGRAL_H_ -#include <Rtypes.h> +#include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" #include "ReconstructionDataFormats/PID.h" #include "ReconstructionDataFormats/Track.h" @@ -24,22 +25,20 @@ namespace o2 namespace track { -class TrackPar; - class TrackLTIntegral { public: - TrackLTIntegral() = default; - TrackLTIntegral(const TrackLTIntegral& stc) = default; - ~TrackLTIntegral() = default; + GPUdDefault() TrackLTIntegral() = default; + GPUdDefault() TrackLTIntegral(const TrackLTIntegral& stc) = default; + GPUdDefault() ~TrackLTIntegral() = default; - static constexpr int getNTOFs() { return o2::track::PID::NIDs; } + GPUd() static constexpr int getNTOFs() { return o2::track::PID::NIDs; } - float getL() const { return mL; } - float getX2X0() const { return mX2X0; } - float getTOF(int id) const { return mT[id]; } + GPUd() float getL() const { return mL; } + GPUd() float getX2X0() const { return mX2X0; } + GPUd() float getTOF(int id) const { return mT[id]; } - void clear() + GPUd() void clear() { mL = 0.f; mX2X0 = 0.f; @@ -48,14 +47,14 @@ class TrackLTIntegral } } - void addStep(float dL, const TrackPar& track); - void addX2X0(float d) { mX2X0 += d; } + GPUd() void addStep(float dL, const TrackPar& track); + GPUd() void addX2X0(float d) { mX2X0 += d; } - void setL(float l) { mL = l; } - void setX2X0(float x) { mX2X0 = x; } - void setTOF(float t, int id) { mT[id] = t; } + GPUd() void setL(float l) { mL = l; } + GPUd() void setX2X0(float x) { mX2X0 = x; } + GPUd() void setTOF(float t, int id) { mT[id] = t; } - void print() const; + GPUd() void print() const; private: float mL = 0.; // length in cm diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h new file mode 100644 index 0000000000000..64eb2d3847d31 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -0,0 +1,677 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackParametrization.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 1, 2020 +/// @brief + +/* + 24/09/2020: Added new data member for abs. charge. This is needed for uniform treatment of tracks with non-standard + charge: 0 (for V0s) and e.g. 2 for hypernuclei. + In the aliroot AliExternalTrackParam this was treated by derived classes using virtual methods, which we don't use in O2. + The meaning of mP[kQ2Pt] remains exactly the same, except for q=0 case: in this case the mP[kQ2Pt] is just an alias to + 1/pT, regardless of its sign, and the getCurvature() will 0 (because mAbsCharge is 0). + The methods returning lab momentum or its combination account for eventual q>1. + */ + +#ifndef INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATION_H_ +#define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATION_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" +#include "GPUCommonArray.h" +#include "GPUROOTCartesianFwd.h" + +#ifndef GPUCA_GPUCODE_DEVICE +#include <algorithm> +#include <cfloat> +#include <cmath> +#include <cstring> +#include <iosfwd> +#endif + +#ifndef GPUCA_ALIGPUCODE //Used only by functions that are hidden on the GPU +#include "ReconstructionDataFormats/BaseCluster.h" +#include <string> +#endif + +#include "CommonConstants/MathConstants.h" +#include "MathUtils/Utils.h" +#include "MathUtils/Primitive2D.h" +#include "ReconstructionDataFormats/PID.h" + +#include "ReconstructionDataFormats/TrackUtils.h" + +namespace o2 +{ +template <typename T> +class BaseCluster; + +namespace dataformats +{ +class VertexBase; +class DCA; +} // namespace dataformats + +namespace track +{ +// aliases for track elements +enum ParLabels : int { kY, + kZ, + kSnp, + kTgl, + kQ2Pt }; +enum CovLabels : int { + kSigY2, + kSigZY, + kSigZ2, + kSigSnpY, + kSigSnpZ, + kSigSnp2, + kSigTglY, + kSigTglZ, + kSigTglSnp, + kSigTgl2, + kSigQ2PtY, + kSigQ2PtZ, + kSigQ2PtSnp, + kSigQ2PtTgl, + kSigQ2Pt2 +}; + +enum DirType : int { DirInward = -1, + DirAuto = 0, + DirOutward = 1 }; + +constexpr int kNParams = 5, kCovMatSize = 15, kLabCovMatSize = 21; + +constexpr float kCY2max = 100 * 100, // SigmaY<=100cm + kCZ2max = 100 * 100, // SigmaZ<=100cm + kCSnp2max = 1 * 1, // SigmaSin<=1 + kCTgl2max = 1 * 1, // SigmaTan<=1 + kC1Pt2max = 100 * 100, // Sigma1/Pt<=100 1/GeV + kMostProbablePt = 0.6f, // Most Probable Pt (GeV), for running with Bz=0 + kCalcdEdxAuto = -999.f; // value indicating request for dedx calculation + +// access to covariance matrix by row and column +GPUconstexpr() int CovarMap[kNParams][kNParams] = {{0, 1, 3, 6, 10}, + {1, 2, 4, 7, 11}, + {3, 4, 5, 8, 12}, + {6, 7, 8, 9, 13}, + {10, 11, 12, 13, 14}}; + +// access to covariance matrix diagonal elements +GPUconstexpr() int DiagMap[kNParams] = {0, 2, 5, 9, 14}; + +constexpr float HugeF = o2::constants::math::VeryBig; + +template <typename value_T = float> +class TrackParametrization +{ // track parameterization, kinematics only. + + public: + using value_t = value_T; + using dim2_t = gpu::gpustd::array<value_t, 2>; + using dim3_t = gpu::gpustd::array<value_t, 3>; + using params_t = gpu::gpustd::array<value_t, kNParams>; + +#ifndef GPUCA_GPUCODE_DEVICE + static_assert(std::is_floating_point_v<value_t>); +#endif + + GPUdDefault() TrackParametrization() = default; + GPUd() TrackParametrization(value_t x, value_t alpha, const params_t& par, int charge = 1); + GPUd() TrackParametrization(const dim3_t& xyz, const dim3_t& pxpypz, int charge, bool sectorAlpha = true); + GPUdDefault() TrackParametrization(const TrackParametrization&) = default; + GPUdDefault() TrackParametrization(TrackParametrization&&) = default; + GPUdDefault() TrackParametrization& operator=(const TrackParametrization& src) = default; + GPUdDefault() TrackParametrization& operator=(TrackParametrization&& src) = default; + GPUdDefault() ~TrackParametrization() = default; + + GPUd() void set(value_t x, value_t alpha, const params_t& par, int charge = 1); + GPUd() const value_t* getParams() const; + GPUd() value_t getParam(int i) const; + GPUd() value_t getX() const; + GPUd() value_t getAlpha() const; + GPUd() value_t getY() const; + GPUd() value_t getZ() const; + GPUd() value_t getSnp() const; + GPUd() value_t getTgl() const; + GPUd() value_t getQ2Pt() const; + GPUd() value_t getCharge2Pt() const; + GPUd() int getAbsCharge() const; + GPUd() PID getPID() const; + GPUd() void setPID(const PID pid); + + /// calculate cos^2 and cos of track direction in rphi-tracking + GPUd() value_t getCsp2() const; + GPUd() value_t getCsp() const; + + GPUd() void setX(value_t v); + GPUd() void setParam(value_t v, int i); + GPUd() void setAlpha(value_t v); + GPUd() void setY(value_t v); + GPUd() void setZ(value_t v); + GPUd() void setSnp(value_t v); + GPUd() void setTgl(value_t v); + GPUd() void setQ2Pt(value_t v); + GPUd() void setAbsCharge(int q); + + // derived getters + GPUd() bool getXatLabR(value_t r, value_t& x, value_t bz, DirType dir = DirAuto) const; + GPUd() void getCircleParamsLoc(value_t bz, o2::math_utils::CircleXY<value_t>& circle) const; + GPUd() void getCircleParams(value_t bz, o2::math_utils::CircleXY<value_t>& circle, value_t& sna, value_t& csa) const; + GPUd() void getLineParams(o2::math_utils::IntervalXY<value_t>& line, value_t& sna, value_t& csa) const; + GPUd() value_t getCurvature(value_t b) const; + GPUd() int getCharge() const; + GPUd() int getSign() const; + GPUd() value_t getPhi() const; + GPUd() value_t getPhiPos() 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 getTheta() const; + GPUd() value_t getEta() const; + GPUd() math_utils::Point3D<value_t> getXYZGlo() const; + GPUd() void getXYZGlo(dim3_t& xyz) const; + GPUd() bool getPxPyPzGlo(dim3_t& pxyz) const; + GPUd() bool getPosDirGlo(gpu::gpustd::array<value_t, 9>& 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; + GPUd() value_t getZAt(value_t xk, value_t b) const; + GPUd() value_t getYAt(value_t xk, value_t b) const; + GPUd() math_utils::Point3D<value_t> 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 rotateParam(value_t alpha); + GPUd() bool propagateParamTo(value_t xk, value_t b); + GPUd() bool propagateParamTo(value_t xk, const dim3_t& b); + + GPUd() bool propagateParamToDCA(const math_utils::Point3D<value_t>& vtx, value_t b, dim2_t* dca = nullptr, value_t maxD = 999.f); + + GPUd() void invertParam(); + + GPUd() bool isValid() const; + GPUd() void invalidate(); + + GPUd() uint16_t getUserField() const; + GPUd() void setUserField(uint16_t v); + + GPUd() void printParam() const; +#ifndef GPUCA_ALIGPUCODE + std::string asString() const; +#endif + + protected: + GPUd() void updateParam(value_t delta, int i); + GPUd() void updateParams(const value_t delta[kNParams]); + + private: + // + static constexpr value_t InvalidX = -99999.f; + value_t mX = 0.f; /// X of track evaluation + value_t mAlpha = 0.f; /// track frame angle + value_t mP[kNParams] = {0.f}; /// 5 parameters: Y,Z,sin(phi),tg(lambda),q/pT + char mAbsCharge = 1; /// Extra info about the abs charge, to be taken into account only if not 1 + PID mPID{}; /// 8 bit PID + uint16_t mUserField = 0; /// field provided to user + + ClassDefNV(TrackParametrization, 3); +}; + +//____________________________________________________________ +template <typename value_T> +GPUdi() TrackParametrization<value_T>::TrackParametrization(value_t x, value_t alpha, const params_t& par, int charge) + : mX{x}, mAlpha{alpha}, mAbsCharge{char(gpu::CAMath::Abs(charge))} +{ + // explicit constructor + for (int i = 0; i < kNParams; i++) { + mP[i] = par[i]; + } +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::set(value_t x, value_t alpha, const params_t& par, int charge) +{ + mX = x; + mAlpha = alpha; + mAbsCharge = char(gpu::CAMath::Abs(charge)); + for (int i = 0; i < kNParams; i++) { + mP[i] = par[i]; + } +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() const typename TrackParametrization<value_T>::value_t* TrackParametrization<value_T>::getParams() const +{ + return mP; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getParam(int i) const +{ + return mP[i]; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getX() const +{ + return mX; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getAlpha() const +{ + return mAlpha; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getY() const +{ + return mP[kY]; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getZ() const +{ + return mP[kZ]; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getSnp() const +{ + return mP[kSnp]; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getTgl() const +{ + return mP[kTgl]; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getQ2Pt() const +{ + return mP[kQ2Pt]; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getCharge2Pt() const +{ + return mAbsCharge ? mP[kQ2Pt] : 0.f; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() int TrackParametrization<value_T>::getAbsCharge() const +{ + return mAbsCharge; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() PID TrackParametrization<value_T>::getPID() const +{ + return mPID; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setPID(const PID pid) +{ + mPID = pid; + setAbsCharge(pid.getCharge()); +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getCsp2() const +{ + const value_t csp2 = (1.f - mP[kSnp]) * (1.f + mP[kSnp]); + return csp2 > o2::constants::math::Almost0 ? csp2 : o2::constants::math::Almost0; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getCsp() const +{ + return gpu::CAMath::Sqrt(getCsp2()); +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setX(value_t v) +{ + mX = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setParam(value_t v, int i) +{ + mP[i] = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setAlpha(value_t v) +{ + mAlpha = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setY(value_t v) +{ + mP[kY] = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setZ(value_t v) +{ + mP[kZ] = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setSnp(value_t v) +{ + mP[kSnp] = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setTgl(value_t v) +{ + mP[kTgl] = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setQ2Pt(value_t v) +{ + mP[kQ2Pt] = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setAbsCharge(int q) +{ + mAbsCharge = gpu::CAMath::Abs(q); +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::getCircleParamsLoc(value_t bz, o2::math_utils::CircleXY<value_t>& c) const +{ + // get circle params in track local frame, for straight line just set to local coordinates + c.rC = getCurvature(bz); + // treat as straight track if sagitta between the vertex and middle of TPC is below 0.01 cm + constexpr value_t MinSagitta = 0.01f, TPCMidR = 160.f, MinCurv = 8 * MinSagitta / (TPCMidR * TPCMidR); + if (gpu::CAMath::Abs(c.rC) > MinCurv) { + c.rC = 1.f / getCurvature(bz); + value_t sn = getSnp(), cs = gpu::CAMath::Sqrt((1.f - sn) * (1.f + sn)); + c.xC = getX() - sn * c.rC; // center in tracking + c.yC = getY() + cs * c.rC; // frame. Note: r is signed!!! + c.rC = gpu::CAMath::Abs(c.rC); + } else { + c.rC = 0.f; // signal straight line + c.xC = getX(); + c.yC = getY(); + } +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::getCircleParams(value_t bz, o2::math_utils::CircleXY<value_t>& c, value_t& sna, value_t& csa) const +{ + // get circle params in loc and lab frame, for straight line just set to global coordinates + getCircleParamsLoc(bz, c); + o2::math_utils::detail::sincos(getAlpha(), sna, csa); + o2::math_utils::detail::rotateZ<value_t>(c.xC, c.yC, c.xC, c.yC, sna, csa); // center in global frame +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::getLineParams(o2::math_utils::IntervalXY<value_t>& ln, value_t& sna, value_t& csa) const +{ + // get line parameterization as { x = x0 + xSlp*t, y = y0 + ySlp*t } + o2::math_utils::detail::sincos(getAlpha(), sna, csa); + o2::math_utils::detail::rotateZ<value_t>(getX(), getY(), ln.getX0(), ln.getY0(), sna, csa); // reference point in global frame + value_t snp = getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)); + ln.setDX(csp * csa - snp * sna); + ln.setDY(snp * csa + csp * sna); +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getCurvature(value_t b) const +{ + return mAbsCharge ? mP[kQ2Pt] * b * o2::constants::math::B2C : 0.; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() int TrackParametrization<value_T>::getCharge() const +{ + return getSign() > 0 ? mAbsCharge : -mAbsCharge; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() int TrackParametrization<value_T>::getSign() const +{ + return mAbsCharge ? (mP[kQ2Pt] > 0.f ? 1 : -1) : 0; +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getPhi() const +{ + // track pt direction phi (in 0:2pi range) + value_t phi = gpu::CAMath::ASin(getSnp()) + getAlpha(); + math_utils::detail::bringTo02Pi<value_t>(phi); + return phi; +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getPhiPos() const +{ + // angle of track position (in -pi:pi range) + value_t phi = gpu::CAMath::ATan2(getY(), getX()) + getAlpha(); + math_utils::detail::bringTo02Pi<value_t>(phi); + return phi; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getPtInv() const +{ + // return the inverted track pT + const value_t ptInv = gpu::CAMath::Abs(mP[kQ2Pt]); + return (mAbsCharge > 1) ? ptInv / mAbsCharge : ptInv; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getP2Inv() const +{ + // 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; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getP2() const +{ + // return the track momentum^2 + const value_t p2inv = getP2Inv(); + return (p2inv > o2::constants::math::Almost0) ? 1.f / p2inv : o2::constants::math::VeryBig; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getPInv() const +{ + // 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; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getP() const +{ + // return the track momentum + const value_t pInv = getPInv(); + return (pInv > o2::constants::math::Almost0) ? 1.f / pInv : o2::constants::math::VeryBig; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getPt() const +{ + // 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; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getTheta() const +{ + return constants::math::PIHalf - gpu::CAMath::ATan(mP[3]); +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getEta() const +{ + return -gpu::CAMath::Log(gpu::CAMath::Tan(0.5f * getTheta())); +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() math_utils::Point3D<typename TrackParametrization<value_T>::value_t> TrackParametrization<value_T>::getXYZGlo() const +{ +#ifndef GPUCA_ALIGPUCODE + return math_utils::Rotation2D<value_t>(getAlpha())(math_utils::Point3D<value_t>(getX(), getY(), getZ())); +#else // mockup on GPU without ROOT + float sina, cosa; + gpu::CAMath::SinCos(getAlpha(), sina, cosa); + return math_utils::Point3D<value_t>(cosa * getX() + sina * getY(), cosa * getY() - sina * getX(), getZ()); +#endif +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::getXYZGlo(dim3_t& xyz) const +{ + // track coordinates in lab frame + xyz[0] = getX(); + xyz[1] = getY(); + xyz[2] = getZ(); + math_utils::detail::rotateZ<value_t>(xyz, getAlpha()); +} + +//_______________________________________________________ +template <typename value_T> +GPUdi() math_utils::Point3D<typename TrackParametrization<value_T>::value_t> TrackParametrization<value_T>::getXYZGloAt(value_t xk, value_t b, bool& ok) const +{ + //---------------------------------------------------------------- + // estimate global X,Y,Z in global frame at given X + //---------------------------------------------------------------- + value_t y = 0.f, z = 0.f; + ok = getYZAt(xk, b, y, z); + if (ok) { +#ifndef GPUCA_ALIGPUCODE + return math_utils::Rotation2D<value_t>(getAlpha())(math_utils::Point3D<value_t>(xk, y, z)); +#else // mockup on GPU without ROOT + float sina, cosa; + gpu::CAMath::SinCos(getAlpha(), sina, cosa); + return math_utils::Point3D<value_t>(cosa * xk + sina * y, cosa * y - sina * xk, z); +#endif + } else { + return math_utils::Point3D<value_t>(); + } +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() bool TrackParametrization<value_T>::isValid() const +{ + return mX != InvalidX; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::invalidate() +{ + mX = InvalidX; +} + +template <typename value_T> +GPUdi() uint16_t TrackParametrization<value_T>::getUserField() const +{ + return mUserField; +} + +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::setUserField(uint16_t v) +{ + mUserField = v; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::updateParam(value_t delta, int i) +{ + mP[i] += delta; +} + +//____________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrization<value_T>::updateParams(const value_t delta[kNParams]) +{ + for (int i = kNParams; i--;) { + mP[i] += delta[i]; + } +} + +} // namespace track +} // namespace o2 + +#endif /* INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATION_H_ */ diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h new file mode 100644 index 0000000000000..9f92ddd731ef9 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h @@ -0,0 +1,320 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackParametrizationWithError.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 1, 2020 +/// @brief + +#ifndef INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATIONWITHERROR_H_ +#define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATIONWITHERROR_H_ + +#include "ReconstructionDataFormats/TrackParametrization.h" + +namespace o2 +{ +namespace track +{ + +template <typename value_T = float> +class TrackParametrizationWithError : public TrackParametrization<value_T> +{ // track+error parameterization + using MatrixDSym5 = ROOT::Math::SMatrix<double, kNParams, kNParams, ROOT::Math::MatRepSym<double, kNParams>>; + using MatrixD5 = ROOT::Math::SMatrix<double, kNParams, kNParams, ROOT::Math::MatRepStd<double, kNParams, kNParams>>; + + using typename TrackParametrization<value_T>::value_t; + using typename TrackParametrization<value_T>::dim3_t; + using typename TrackParametrization<value_T>::dim2_t; + using typename TrackParametrization<value_T>::params_t; + +#ifndef GPUCA_GPUCODE_DEVICE + static_assert(std::is_floating_point_v<value_t>); +#endif + + public: + using covMat_t = gpu::gpustd::array<value_t, kCovMatSize>; + + GPUd() TrackParametrizationWithError(); + GPUd() TrackParametrizationWithError(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge = 1); + GPUd() TrackParametrizationWithError(const dim3_t& xyz, const dim3_t& pxpypz, + const gpu::gpustd::array<value_t, kLabCovMatSize>& cv, int sign, bool sectorAlpha = true); + + GPUdDefault() TrackParametrizationWithError(const TrackParametrizationWithError& src) = default; + GPUdDefault() TrackParametrizationWithError(TrackParametrizationWithError&& src) = default; + GPUdDefault() TrackParametrizationWithError& operator=(const TrackParametrizationWithError& src) = default; + GPUdDefault() TrackParametrizationWithError& operator=(TrackParametrizationWithError&& src) = default; + GPUdDefault() ~TrackParametrizationWithError() = default; + using TrackParametrization<value_T>::TrackParametrization; + + GPUd() void set(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge = 1); + GPUd() const value_t* getCov() const; + GPUd() value_t getSigmaY2() const; + GPUd() value_t getSigmaZY() const; + GPUd() value_t getSigmaZ2() const; + GPUd() value_t getSigmaSnpY() const; + GPUd() value_t getSigmaSnpZ() const; + GPUd() value_t getSigmaSnp2() const; + GPUd() value_t getSigmaTglY() const; + GPUd() value_t getSigmaTglZ() const; + GPUd() value_t getSigmaTglSnp() const; + GPUd() value_t getSigmaTgl2() const; + GPUd() value_t getSigma1PtY() const; + GPUd() value_t getSigma1PtZ() const; + GPUd() value_t getSigma1PtSnp() const; + GPUd() value_t getSigma1PtTgl() const; + GPUd() value_t getSigma1Pt2() const; + GPUd() value_t getCovarElem(int i, int j) const; + GPUd() value_t getDiagError2(int i) const; + + GPUd() bool getCovXYZPxPyPzGlo(gpu::gpustd::array<value_t, kLabCovMatSize>& c) const; + + GPUd() void print() const; +#ifndef GPUCA_ALIGPUCODE + std::string asString() const; +#endif + + // parameters + covmat manipulation + GPUd() bool rotate(value_t alpha); + GPUd() bool propagateTo(value_t xk, value_t b); + 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() void invert(); + + GPUd() value_t getPredictedChi2(const dim2_t& p, const dim3_t& cov) const; + + template <typename T> + GPUd() value_t getPredictedChi2(const BaseCluster<T>& 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() bool update(const dim2_t& p, const dim3_t& cov); + + template <typename T> + GPUd() bool update(const BaseCluster<T>& p); + + GPUd() bool correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr = false, value_t dedx = kCalcdEdxAuto); + + GPUd() void resetCovariance(value_t s2 = 0); + GPUd() void checkCovariance(); + GPUd() void setCov(value_t v, int i); + + GPUd() void updateCov(const value_t delta[kCovMatSize]); + GPUd() void updateCov(value_t delta, int i); + + protected: + value_t mC[kCovMatSize] = {0.f}; // 15 covariance matrix elements + + ClassDefNV(TrackParametrizationWithError, 2); +}; + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() TrackParametrizationWithError<value_T>::TrackParametrizationWithError() : TrackParametrization<value_T>{} +{ +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() TrackParametrizationWithError<value_T>::TrackParametrizationWithError(value_t x, value_t alpha, const params_t& par, + const covMat_t& cov, int charge) + : TrackParametrization<value_T>{x, alpha, par, charge} +{ + // explicit constructor + for (int i = 0; i < kCovMatSize; i++) { + mC[i] = cov[i]; + } +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrizationWithError<value_T>::set(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge) +{ + TrackParametrization<value_T>::set(x, alpha, par, charge); + for (int i = 0; i < kCovMatSize; i++) { + mC[i] = cov[i]; + } +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() const typename TrackParametrizationWithError<value_T>::value_t* TrackParametrizationWithError<value_T>::getCov() const +{ + return mC; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaY2() const +{ + return mC[kSigY2]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaZY() const +{ + return mC[kSigZY]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaZ2() const +{ + return mC[kSigZ2]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaSnpY() const +{ + return mC[kSigSnpY]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaSnpZ() const +{ + return mC[kSigSnpZ]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaSnp2() const +{ + return mC[kSigSnp2]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaTglY() const +{ + return mC[kSigTglY]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaTglZ() const +{ + return mC[kSigTglZ]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaTglSnp() const +{ + return mC[kSigTglSnp]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigmaTgl2() const +{ + return mC[kSigTgl2]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigma1PtY() const +{ + return mC[kSigQ2PtY]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigma1PtZ() const +{ + return mC[kSigQ2PtZ]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigma1PtSnp() const +{ + return mC[kSigQ2PtSnp]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigma1PtTgl() const +{ + return mC[kSigQ2PtTgl]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getSigma1Pt2() const +{ + return mC[kSigQ2Pt2]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getCovarElem(int i, int j) const +{ + return mC[CovarMap[i][j]]; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getDiagError2(int i) const +{ + return mC[DiagMap[i]]; +} + +//__________________________________________________________________________ +template <typename value_T> +template <typename T> +GPUdi() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getPredictedChi2(const BaseCluster<T>& p) const +{ + const dim2_t pyz = {p.getY(), p.getZ()}; + const dim3_t cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()}; + return getPredictedChi2(pyz, cov); +} + +//__________________________________________________________________________ +template <typename value_T> +template <typename T> +GPUdi() bool TrackParametrizationWithError<value_T>::update(const BaseCluster<T>& p) +{ + const dim2_t pyz = {p.getY(), p.getZ()}; + const dim3_t cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()}; + return update(pyz, cov); +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrizationWithError<value_T>::setCov(value_t v, int i) +{ + mC[i] = v; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrizationWithError<value_T>::updateCov(value_t delta, int i) +{ + mC[i] += delta; +} + +//__________________________________________________________________________ +template <typename value_T> +GPUdi() void TrackParametrizationWithError<value_T>::updateCov(const value_t delta[kCovMatSize]) +{ + for (int i = kCovMatSize; i--;) { + mC[i] += delta[i]; + } +} + +} // namespace track +} // namespace o2 +#endif /* INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATIONWITHERROR_H_ */ diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h new file mode 100644 index 0000000000000..30e4495b140db --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h @@ -0,0 +1,146 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackParametrizationHelpers.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 1, 2020 +/// @brief Helper functions for track manipulation + +#ifndef INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKUTILS_H_ +#define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKUTILS_H_ + +#include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" + +#ifndef GPUCA_GPUCODE_DEVICE +#include <cmath> +#endif + +#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" + +namespace o2 +{ +namespace track +{ +// helper function +template <typename value_T = float> +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 <typename value_T = float> +GPUd() void g3helx3(value_T qfield, value_T step, gpu::gpustd::array<value_T, 7>& vect); + +//____________________________________________________ +template <typename value_T> +GPUd() void g3helx3(value_T qfield, value_T step, gpu::gpustd::array<value_T, 7>& vect) +{ + /****************************************************************** + * * + * GEANT3 tracking routine in a constant field oriented * + * along axis 3 * + * Tracking is performed with a conventional * + * helix step method * + * * + * Authors R.Brun, M.Hansroul ********* * + * Rewritten V.Perevoztchikov * + * * + * Rewritten in C++ by I.Belikov * + * * + * qfield (kG) - particle charge times magnetic field * + * step (cm) - step length along the helix * + * vect[7](cm,GeV/c) - input/output x, y, z, px/p, py/p ,pz/p, p * + * * + ******************************************************************/ +#ifndef GPUCA_GPUCODE_DEVICE + static_assert(std::is_floating_point_v<value_T>); +#endif + + const int ix = 0, iy = 1, iz = 2, ipx = 3, ipy = 4, ipz = 5, ipp = 6; + constexpr value_T kOvSqSix = 0.408248f; // std::sqrt(1./6.); + + value_T cosx = vect[ipx], cosy = vect[ipy], cosz = vect[ipz]; + + value_T rho = qfield * constants::math::B2C / vect[ipp]; + value_T tet = rho * step; + + value_T tsint, sintt, sint, cos1t; + if (gpu::CAMath::Abs(tet) > 0.03f) { + sint = gpu::CAMath::Sin(tet); + sintt = sint / tet; + tsint = (tet - sint) / tet; + value_T t = gpu::CAMath::Sin(0.5f * tet); + cos1t = 2 * t * t / tet; + } else { + tsint = tet * tet / 6.f; + sintt = (1.f - tet * kOvSqSix) * (1.f + tet * kOvSqSix); // 1.- tsint; + sint = tet * sintt; + cos1t = 0.5f * tet; + } + + value_T f1 = step * sintt; + value_T f2 = step * cos1t; + value_T f3 = step * tsint * cosz; + value_T f4 = -tet * cos1t; + value_T f5 = sint; + + vect[ix] += f1 * cosx - f2 * cosy; + vect[iy] += f1 * cosy + f2 * cosx; + vect[iz] += f1 * cosz + f3; + + vect[ipx] += f4 * cosx - f5 * cosy; + vect[ipy] += f4 * cosy + f5 * cosx; +} + +//____________________________________________________ +template <typename value_T> +GPUd() value_T BetheBlochSolid(value_T bg, value_T rho, value_T kp1, value_T kp2, value_T meanI, + value_T meanZA) +{ + // + // This is the parameterization of the Bethe-Bloch formula inspired by Geant. + // + // 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)]. + // +#ifndef GPUCA_GPUCODE_DEVICE + static_assert(std::is_floating_point_v<value_T>); +#endif + + constexpr value_T mK = 0.307075e-3f; // [GeV*cm^2/g] + constexpr value_T me = 0.511e-3f; // [GeV/c^2] + kp1 *= 2.303f; + kp2 *= 2.303f; + value_T bg2 = bg * bg; + value_T maxT = 2.f * me * bg2; // neglecting the electron mass + + //*** Density effect + value_T d2 = 0.; + const value_T x = gpu::CAMath::Log(bg); + const value_T lhwI = gpu::CAMath::Log(28.816f * 1e-9f * gpu::CAMath::Sqrt(rho * meanZA) / meanI); + if (x > kp2) { + d2 = lhwI + x - 0.5f; + } else if (x > kp1) { + 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); +} + +} // namespace track +} // namespace o2 + +#endif /* INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKUTILS_H_ */ diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h new file mode 100644 index 0000000000000..3c8055dc33219 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h @@ -0,0 +1,77 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_V0_H +#define ALICEO2_V0_H + +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/PID.h" +#include <array> +#include <Math/SVector.h> + +namespace o2 +{ +namespace dataformats +{ + +class V0 : public o2::track::TrackParCov +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + using Track = o2::track::TrackParCov; + using PID = o2::track::PID; + + V0() = default; + V0(const std::array<float, 3>& xyz, const std::array<float, 3>& pxyz, + const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg, + GIndex trPosID, GIndex trNegID); + + 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 getMass2() const + { + return calcMass2(mProngs[0].getPID(), mProngs[1].getPID()); + } + + float calcMass2(PID pidPos, PID pidNeg) const + { + return calcMass2(PID::getMass2(pidPos), PID::getMass2(pidNeg)); + } + + float calcMass2(float massPos2, float massNeg2) const; + + protected: + std::array<GIndex, 2> mProngIDs; // global IDs of prongs + std::array<Track, 2> 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); +}; + +} // namespace dataformats +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h index c723e26778f32..eb8bf67d5d474 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h @@ -11,14 +11,13 @@ #ifndef ALICEO2_VERTEX_H #define ALICEO2_VERTEX_H -#include "MathUtils/Cartesian3D.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "GPUCommonArray.h" +#include <MathUtils/Cartesian.h> + #include "CommonDataFormat/TimeStamp.h" -#ifndef __OPENCL__ -#include <array> -#endif -#ifndef ALIGPU_GPUCODE -#include <iomanip> -#include <ios> +#ifndef GPUCA_GPUCODE_DEVICE #include <iosfwd> #endif @@ -38,50 +37,51 @@ class VertexBase kCovYZ, kCovZZ }; static constexpr int kNCov = 6; - VertexBase() = default; - ~VertexBase() = default; - VertexBase(const Point3D<float>& pos, const std::array<float, kNCov>& cov) : mPos(pos), mCov(cov) + GPUdDefault() VertexBase() = default; + GPUdDefault() ~VertexBase() = default; + GPUd() VertexBase(const math_utils::Point3D<float>& pos, const gpu::gpustd::array<float, kNCov>& cov) : mPos(pos), mCov(cov) { } -#ifndef ALIGPU_GPUCODE +#ifndef GPUCA_GPUCODE_DEVICE void print() const; + std::string asString() const; #endif // getting the cartesian coordinates and errors - float getX() const { return mPos.X(); } - float getY() const { return mPos.Y(); } - float getZ() const { return mPos.Z(); } - float getSigmaX2() const { return mCov[kCovXX]; } - float getSigmaY2() const { return mCov[kCovYY]; } - float getSigmaZ2() const { return mCov[kCovZZ]; } - float getSigmaXY() const { return mCov[kCovXY]; } - float getSigmaXZ() const { return mCov[kCovXZ]; } - float getSigmaYZ() const { return mCov[kCovYZ]; } - const std::array<float, kNCov>& getCov() const { return mCov; } - - Point3D<float> getXYZ() const { return mPos; } - Point3D<float>& getXYZ() { return mPos; } - - void setX(float x) { mPos.SetX(x); } - void setY(float y) { mPos.SetY(y); } - void setZ(float z) { mPos.SetZ(z); } - - void setXYZ(float x, float y, float z) + GPUd() float getX() const { return mPos.X(); } + GPUd() float getY() const { return mPos.Y(); } + GPUd() float getZ() const { return mPos.Z(); } + GPUd() float getSigmaX2() const { return mCov[kCovXX]; } + GPUd() float getSigmaY2() const { return mCov[kCovYY]; } + GPUd() float getSigmaZ2() const { return mCov[kCovZZ]; } + GPUd() float getSigmaXY() const { return mCov[kCovXY]; } + GPUd() float getSigmaXZ() const { return mCov[kCovXZ]; } + GPUd() float getSigmaYZ() const { return mCov[kCovYZ]; } + GPUd() const gpu::gpustd::array<float, kNCov>& getCov() const { return mCov; } + + GPUd() math_utils::Point3D<float> getXYZ() const { return mPos; } + GPUd() math_utils::Point3D<float>& getXYZ() { return mPos; } + + GPUd() void setX(float x) { mPos.SetX(x); } + GPUd() void setY(float y) { mPos.SetY(y); } + GPUd() void setZ(float z) { mPos.SetZ(z); } + + GPUd() void setXYZ(float x, float y, float z) { setX(x); setY(y); setZ(z); } - void setPos(const Point3D<float>& p) { mPos = p; } - - void setSigmaX2(float v) { mCov[kCovXX] = v; } - void setSigmaY2(float v) { mCov[kCovYY] = v; } - void setSigmaZ2(float v) { mCov[kCovZZ] = v; } - void setSigmaXY(float v) { mCov[kCovXY] = v; } - void setSigmaXZ(float v) { mCov[kCovXZ] = v; } - void setSigmaYZ(float v) { mCov[kCovYZ] = v; } - void setCov(float sxx, float sxy, float syy, float sxz, float syz, float szz) + GPUd() void setPos(const math_utils::Point3D<float>& p) { mPos = p; } + + GPUd() void setSigmaX2(float v) { mCov[kCovXX] = v; } + GPUd() void setSigmaY2(float v) { mCov[kCovYY] = v; } + GPUd() void setSigmaZ2(float v) { mCov[kCovZZ] = v; } + GPUd() void setSigmaXY(float v) { mCov[kCovXY] = v; } + GPUd() void setSigmaXZ(float v) { mCov[kCovXZ] = v; } + GPUd() void setSigmaYZ(float v) { mCov[kCovYZ] = v; } + GPUd() void setCov(float sxx, float sxy, float syy, float sxz, float syz, float szz) { setSigmaX2(sxx); setSigmaY2(syy); @@ -90,11 +90,11 @@ class VertexBase setSigmaXZ(sxz); setSigmaYZ(syz); } - void setCov(const std::array<float, kNCov>& cov) { mCov = cov; } + GPUd() void setCov(const gpu::gpustd::array<float, kNCov>& cov) { mCov = cov; } protected: - Point3D<float> mPos{0., 0., 0.}; ///< cartesian position - std::array<float, kNCov> mCov{}; ///< errors, see CovElems enum + math_utils::Point3D<float> mPos{0., 0., 0.}; ///< cartesian position + gpu::gpustd::array<float, kNCov> mCov{}; ///< errors, see CovElems enum ClassDefNV(VertexBase, 1); }; @@ -103,65 +103,50 @@ class VertexBase // The Stamp template parameter allows to define vertex (time)stamp in different // formats (ITS ROFrame ID, real time + error etc) -template <typename Stamp = o2::dataformats::TimeStamp<int>> +template <typename Stamp> class Vertex : public VertexBase { public: using ushort = unsigned short; - static ushort constexpr FlagsMask = 0xffff; - - Vertex() = default; - ~Vertex() = default; - Vertex(const Point3D<float>& pos, const std::array<float, kNCov>& cov, ushort nCont, float chi2) + enum Flags : ushort { + TimeValidated = 0x1 << 0, // Flag that the vertex was validated by external time measurement (e.g. FIT) + FlagsMask = 0xffff + }; + + GPUdDefault() Vertex() = default; + GPUdDefault() ~Vertex() = default; + GPUd() Vertex(const math_utils::Point3D<float>& pos, const gpu::gpustd::array<float, kNCov>& cov, ushort nCont, float chi2) : VertexBase(pos, cov), mNContributors(nCont), mChi2(chi2) { } -#ifndef ALIGPU_GPUCODE - void print() const; -#endif - - ushort getNContributors() const { return mNContributors; } - void setNContributors(ushort v) { mNContributors = v; } + GPUd() ushort getNContributors() const { return mNContributors; } + GPUd() void setNContributors(ushort v) { mNContributors = v; } + GPUd() void addContributor() { mNContributors++; } - ushort getBits() const { return mBits; } - bool isBitSet(int bit) const { return mBits & (FlagsMask & (0x1 << bit)); } - void setBits(ushort b) { mBits = b; } - void setBit(int bit) { mBits |= FlagsMask & (0x1 << bit); } - void resetBit(int bit) { mBits &= ~(FlagsMask & (0x1 << bit)); } + 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); } - void setChi2(float v) { mChi2 = v; } - float getChi2() const { return mChi2; } + GPUd() void setChi2(float v) { mChi2 = v; } + GPUd() float getChi2() const { return mChi2; } - const Stamp& getTimeStamp() const { return mTimeStamp; } - Stamp& getTimeStamp() { return mTimeStamp; } - void setTimeStamp(const Stamp& v) { mTimeStamp = v; } + GPUd() const Stamp& getTimeStamp() const { return mTimeStamp; } + GPUd() Stamp& getTimeStamp() { return mTimeStamp; } + GPUd() void setTimeStamp(const Stamp& v) { mTimeStamp = v; } - private: - float mChi2 = 0; ///< chi2 or quality of tracks to vertex attachment - ushort mNContributors = 0; ///< N contributors - ushort mBits = 0; ///< bit field for flags - Stamp mTimeStamp; ///< vertex time-stamp + protected: + float mChi2 = 0; ///< chi2 or quality of tracks to vertex attachment + ushort mNContributors = 0; ///< N contributors + ushort mBits = 0; ///< bit field for flags + Stamp mTimeStamp; ///< vertex time-stamp - ClassDefNV(Vertex, 2); + ClassDefNV(Vertex, 3); }; -#ifndef ALIGPU_GPUCODE +#ifndef GPUCA_GPUCODE_DEVICE std::ostream& operator<<(std::ostream& os, const o2::dataformats::VertexBase& v); - -template <typename Stamp> -inline std::ostream& operator<<(std::ostream& os, const o2::dataformats::Vertex<Stamp>& v) -{ - // stream itself - os << (const VertexBase&)v << "\n NCont: " << v.getNContributors() << " Chi2: " << v.getChi2() << " TimeStamp: " << v.getTimeStamp(); - return os; -} - -template <typename Stamp> -void Vertex<Stamp>::print() const -{ - std::cout << *this << std::endl; -} #endif } // namespace dataformats diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackIndex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackIndex.h new file mode 100644 index 0000000000000..44b6ff228ba33 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackIndex.h @@ -0,0 +1,72 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VtxTrackIndex.h +/// \brief Index of track attached to vertx: index in its proper container, container source and flags +/// \author ruben.shahoyan@cern.ch + +#ifndef O2_VERTEX_TRACK_INDEX +#define O2_VERTEX_TRACK_INDEX + +#include "CommonDataFormat/AbstractRef.h" +#include <iosfwd> +#include <string> +#include <array> +#include <string_view> + +namespace o2 +{ +namespace dataformats +{ + +class VtxTrackIndex : public AbstractRef<26, 3, 3> +{ + public: + enum Source : uint8_t { // provenance of the track + TPCITS, + ITS, + TPC, + NSources + }; + static constexpr std::array<std::string_view, NSources> SourceNames = { + "TPCITS", + "ITS", + "TPC"}; + + enum Flags : uint8_t { + Contributor, // flag that it contributes to vertex fit + Reserved, // + Ambiguous, // flag that attachment is ambiguous + NFlags + }; + + using AbstractRef<26, 3, 3>::AbstractRef; + + static constexpr std::string_view getSourceName(int i) { return SourceNames[i]; } + void print() const; + std::string asString() const; + + operator auto() const { return AbstractRef<26, 3, 3>(); } + + bool isPVContributor() const { return testBit(Contributor); } + void setPVContributor() { setBit(Contributor); } + + bool isAmbiguous() const { return testBit(Ambiguous); } + void setAmbiguous() { setBit(Ambiguous); } + + ClassDefNV(VtxTrackIndex, 1); +}; + +std::ostream& operator<<(std::ostream& os, const o2::dataformats::VtxTrackIndex& v); + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h new file mode 100644 index 0000000000000..fda790cd72108 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h @@ -0,0 +1,78 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VtxTrackRef.h +/// \brief Referenc on track indices contributing to the vertex, with possibility chose tracks from specific source (global, ITS, TPC...) +/// \author ruben.shahoyan@cern.ch + +#ifndef O2_VERTEX_TRACK_REF +#define O2_VERTEX_TRACK_REF + +#include "CommonDataFormat/RangeReference.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include <cassert> +#include <array> +#include <iosfwd> +#include <string> + +namespace o2 +{ +namespace dataformats +{ + +class VtxTrackRef : public RangeReference<int, int> +{ + public: + VtxTrackRef(int ent, int n) : RangeReference(ent, n) + { + auto end = ent + n; + for (int i = VtxTrackIndex::Source::NSources - 1; i--;) { + mFirstEntrySource[i] = end; // only 1st source (base reference) is filled at constructor level + } + } + using RangeReference<int, int>::RangeReference; + void print() const; + std::string asString() const; + + // get 1st of entry of indices for given source + int getFirstEntryOfSource(int s) const + { + assert(s >= 0 && s < VtxTrackIndex::NSources); + return s ? mFirstEntrySource[s - 1] : getFirstEntry(); + } + + // get number of entries for given source + int getEntriesOfSource(int s) const + { + return (s == VtxTrackIndex::NSources - 1 ? (getFirstEntry() + getEntries()) : getFirstEntryOfSource(s + 1)) - getFirstEntryOfSource(s); + } + + void setFirstEntryOfSource(int s, int i) + { + assert(s >= 0 && s < VtxTrackIndex::NSources); + if (s) { + mFirstEntrySource[s - 1] = i; + } else { + setFirstEntry(i); + } + } + + private: + std::array<int, VtxTrackIndex::Source::NSources - 1> mFirstEntrySource{0}; + + ClassDefNV(VtxTrackRef, 1); +}; + +std::ostream& operator<<(std::ostream& os, const o2::dataformats::VtxTrackRef& v); + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/Reconstruction/src/DCA.cxx b/DataFormats/Reconstruction/src/DCA.cxx index 925948b2bc127..34d6f714d0feb 100644 --- a/DataFormats/Reconstruction/src/DCA.cxx +++ b/DataFormats/Reconstruction/src/DCA.cxx @@ -19,7 +19,7 @@ namespace o2 namespace dataformats { -#ifndef ALIGPU_GPUCODE +#ifndef GPUCA_GPUCODE_DEVICE std::ostream& operator<<(std::ostream& os, const o2::dataformats::DCA& d) { // stream itself @@ -30,7 +30,7 @@ std::ostream& operator<<(std::ostream& os, const o2::dataformats::DCA& d) void DCA::print() const { -#ifndef ALIGPU_GPUCODE +#ifndef GPUCA_GPUCODE_DEVICE std::cout << *this << '\n'; #endif } diff --git a/DataFormats/Reconstruction/src/PID.cxx b/DataFormats/Reconstruction/src/PID.cxx index 1af3ae35cb695..ffe6dab6c778d 100644 --- a/DataFormats/Reconstruction/src/PID.cxx +++ b/DataFormats/Reconstruction/src/PID.cxx @@ -18,14 +18,9 @@ using namespace o2::track; -constexpr const char* PID::sNames[NIDs + 1]; -constexpr const float PID::sMasses[NIDs]; -constexpr const float PID::sMasses2Z[NIDs]; -constexpr const int PID::sCharges[NIDs]; - //_______________________________ PID::PID(const char* name) : mID(nameToID(name, First)) { // construct from the name - assert(mID < NIDs); + assert(mID < NIDsTot); } diff --git a/DataFormats/Reconstruction/src/PrimaryVertex.cxx b/DataFormats/Reconstruction/src/PrimaryVertex.cxx new file mode 100644 index 0000000000000..fb4f7d3b652e5 --- /dev/null +++ b/DataFormats/Reconstruction/src/PrimaryVertex.cxx @@ -0,0 +1,49 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/PrimaryVertex.h" +#include <fmt/printf.h> +#include <iostream> +#include "CommonUtils/StringUtils.h" + +namespace o2 +{ +namespace dataformats +{ + +#ifndef GPUCA_ALIGPUCODE + +std::string PrimaryVertex::asString() const +{ + auto str = o2::utils::concat_string(VertexBase::asString(), + fmt::format("Chi2={:.2f} NCont={:d}: T={:.3f}+-{:.3f} IR=", mChi2, mNContributors, mTimeStamp.getTimeStamp(), mTimeStamp.getTimeStampError()), + mIRMin.asString()); + if (!hasUniqueIR()) { + str = o2::utils::concat_string(str, " : ", mIRMax.asString()); + } + return str; +} + +std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertex& v) +{ + // stream itself + os << v.asString(); + return os; +} + +void PrimaryVertex::print() const +{ + std::cout << *this << std::endl; +} + +#endif + +} // namespace dataformats +} // namespace o2 diff --git a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h index e8c481cddf741..6cf9150f89429 100644 --- a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h +++ b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h @@ -15,7 +15,13 @@ #pragma link off all functions; #pragma link C++ class o2::track::TrackPar + ; +#pragma link C++ class o2::track::TrackParametrization < float> + ; +#pragma link C++ class o2::track::TrackParametrization < double> + ; #pragma link C++ class o2::track::TrackParCov + ; +#pragma link C++ class o2::track::TrackParametrizationWithError < float> + ; +#pragma link C++ class o2::track::TrackParametrizationWithError < double> + ; +#pragma link C++ class o2::track::TrackParFwd + ; +#pragma link C++ class o2::track::TrackParCovFwd + ; #pragma link C++ class o2::track::PID + ; #pragma link C++ class o2::track::TrackLTIntegral + ; @@ -32,9 +38,22 @@ #pragma link C++ class o2::dataformats::VertexBase + ; #pragma link C++ class o2::dataformats::Vertex < int> + ; #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>> + ; -#pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < double, double>> + ; +#pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>> + ; +#pragma link C++ class o2::dataformats::PrimaryVertex + ; + #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 o2::dataformats::VtxTrackIndex + ; +#pragma link C++ class std::vector < o2::dataformats::VtxTrackIndex> + ; + +#pragma link C++ class o2::dataformats::VtxTrackRef + ; +#pragma link C++ class std::vector < o2::dataformats::VtxTrackRef> + ; #pragma link C++ class o2::dataformats::DCA + ; +#pragma link C++ class o2::dataformats::V0 + ; +#pragma link C++ class std::vector < o2::dataformats::V0> + ; + #endif diff --git a/DataFormats/Reconstruction/src/Track.cxx b/DataFormats/Reconstruction/src/Track.cxx deleted file mode 100644 index aff40fe2b46ac..0000000000000 --- a/DataFormats/Reconstruction/src/Track.cxx +++ /dev/null @@ -1,1771 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/Track.h" -#include "ReconstructionDataFormats/Vertex.h" -#include "ReconstructionDataFormats/DCA.h" -#include <FairLogger.h> -#include <iostream> -#include "Math/SMatrix.h" -#include <fmt/printf.h> -#include "Framework/Logger.h" - -using o2::track::TrackPar; -using o2::track::TrackParCov; -using std::array; -using namespace o2::constants::math; - -//______________________________________________________________ -TrackPar::TrackPar(const array<float, 3>& xyz, const array<float, 3>& pxpypz, int charge, bool sectorAlpha) - : mX{0.f}, mAlpha{0.f}, mP{0.f} -{ - // construct track param from kinematics - - // Alpha of the frame is defined as: - // sectorAlpha == false : -> angle of pt direction - // sectorAlpha == true : -> angle of the sector from X,Y coordinate for r>1 - // angle of pt direction for r==0 - // - // - constexpr float kSafe = 1e-5f; - float radPos2 = xyz[0] * xyz[0] + xyz[1] * xyz[1]; - float alp = 0; - if (sectorAlpha || radPos2 < 1) { - alp = atan2f(pxpypz[1], pxpypz[0]); - } else { - alp = atan2f(xyz[1], xyz[0]); - } - if (sectorAlpha) { - alp = utils::Angle2Alpha(alp); - } - // - float sn, cs; - utils::sincos(alp, sn, cs); - // protection: avoid alpha being too close to 0 or +-pi/2 - if (fabs(sn) < 2 * kSafe) { - if (alp > 0) { - alp += alp < PIHalf ? 2 * kSafe : -2 * kSafe; - } else { - alp += alp > -PIHalf ? -2 * kSafe : 2 * kSafe; - } - utils::sincos(alp, sn, cs); - } else if (fabs(cs) < 2 * kSafe) { - if (alp > 0) { - alp += alp > PIHalf ? 2 * kSafe : -2 * kSafe; - } else { - alp += alp > -PIHalf ? 2 * kSafe : -2 * kSafe; - } - utils::sincos(alp, sn, cs); - } - // get the vertex of origin and the momentum - array<float, 3> ver{xyz[0], xyz[1], xyz[2]}; - array<float, 3> mom{pxpypz[0], pxpypz[1], pxpypz[2]}; - // - // Rotate to the local coordinate system - utils::RotateZ(ver, -alp); - utils::RotateZ(mom, -alp); - // - float ptI = 1.f / sqrt(mom[0] * mom[0] + mom[1] * mom[1]); - mX = ver[0]; - mAlpha = alp; - mP[kY] = ver[1]; - mP[kZ] = ver[2]; - mP[kSnp] = mom[1] * ptI; - mP[kTgl] = mom[2] * ptI; - mP[kQ2Pt] = ptI * charge; - // - if (fabs(1 - getSnp()) < kSafe) { - mP[kSnp] = 1. - kSafe; // Protection - } else if (fabs(-1 - getSnp()) < kSafe) { - mP[kSnp] = -1. + kSafe; // Protection - } - // -} - -//_______________________________________________________ -bool TrackPar::getPxPyPzGlo(array<float, 3>& pxyz) const -{ - // track momentum - if (fabs(getQ2Pt()) < Almost0 || fabs(getSnp()) > Almost1) { - return false; - } - float cs, sn, pt = fabs(1.f / getQ2Pt()); - float r = std::sqrt((1.f - getSnp()) * (1.f + getSnp())); - utils::sincos(getAlpha(), sn, cs); - pxyz[0] = pt * (r * cs - getSnp() * sn); - pxyz[1] = pt * (getSnp() * cs + r * sn); - pxyz[2] = pt * getTgl(); - return true; -} - -//____________________________________________________ -bool TrackPar::getPosDirGlo(array<float, 9>& posdirp) const -{ - // fill vector with lab x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha - float ptI = fabs(getQ2Pt()); - float snp = getSnp(); - if (ptI < Almost0 || fabs(snp) > Almost1) { - return false; - } - float &sn = posdirp[7], &cs = posdirp[8]; - float csp = std::sqrt((1.f - snp) * (1.f + snp)); - float cstht = std::sqrt(1.f + getTgl() * getTgl()); - float csthti = 1.f / cstht; - utils::sincos(getAlpha(), sn, cs); - posdirp[0] = getX() * cs - getY() * sn; - posdirp[1] = getX() * sn + getY() * cs; - posdirp[2] = getZ(); - posdirp[3] = (csp * cs - snp * sn) * csthti; // px/p - posdirp[4] = (snp * cs + csp * sn) * csthti; // py/p - posdirp[5] = getTgl() * csthti; // pz/p - posdirp[6] = cstht / ptI; // p - return true; -} - -//______________________________________________________________ -bool TrackPar::rotateParam(float alpha) -{ - // rotate to alpha frame - if (fabs(getSnp()) > Almost1) { - LOGF(WARNING, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", getSnp()); - return false; - } - // - utils::BringToPMPi(alpha); - // - float ca = 0, sa = 0; - utils::sincos(alpha - getAlpha(), sa, ca); - float snp = getSnp(), csp = std::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; - } - // - float tmp = snp * ca - csp * sa; - if (fabs(tmp) > Almost1) { - LOGF(WARNING, "Rotation failed: new snp {:.2f}", tmp); - return false; - } - float xold = getX(), yold = getY(); - mAlpha = alpha; - mX = xold * ca + yold * sa; - mP[kY] = -xold * sa + yold * ca; - mP[kSnp] = tmp; - return true; -} - -//____________________________________________________________ -bool TrackPar::propagateParamTo(float xk, const array<float, 3>& b) -{ - //---------------------------------------------------------------- - // Extrapolate this track params (w/o cov matrix) 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. - //---------------------------------------------------------------- - - float dx = xk - getX(); - if (fabs(dx) < Almost0) { - return true; - } - // Do not propagate tracks outside the ALICE detector - if (fabs(dx) > 1e5 || fabs(getY()) > 1e5 || fabs(getZ()) > 1e5) { - LOGF(WARNING, "Anomalous track, target X:{:f}", xk); - // print(); - return false; - } - float crv = (fabs(b[2]) < Almost0) ? 0.f : getCurvature(b[2]); - float x2r = crv * dx; - float f1 = getSnp(), f2 = f1 + x2r; - if (fabs(f1) > Almost1 || fabs(f2) > Almost1) { - return false; - } - float r1 = std::sqrt((1.f - f1) * (1.f + f1)); - if (fabs(r1) < Almost0) { - return false; - } - float r2 = std::sqrt((1.f - f2) * (1.f + f2)); - if (fabs(r2) < Almost0) { - return false; - } - float dy2dx = (f1 + f2) / (r1 + r2); - float step = (fabs(x2r) < 0.05f) ? dx * fabs(r2 + f2 * dy2dx) // chord - : 2.f * asinf(0.5f * dx * std::sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc - step *= std::sqrt(1.f + getTgl() * getTgl()); - // - // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System - array<float, 9> vecLab{0.f}; - if (!getPosDirGlo(vecLab)) { - return false; - } - - // rotate to the system where Bx=By=0. - float bxy2 = b[0] * b[0] + b[1] * b[1]; - float bt = std::sqrt(bxy2); - float cosphi = 1.f, sinphi = 0.f; - if (bt > Almost0) { - cosphi = b[0] / bt; - sinphi = b[1] / bt; - } - float bb = std::sqrt(bxy2 + b[2] * b[2]); - float costet = 1., sintet = 0.; - if (bb > Almost0) { - costet = b[2] / bb; - sintet = bt / bb; - } - array<float, 7> 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 - float sgn = getSign(); - g3helx3(sgn * 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 - float sinalp = -vecLab[7], cosalp = vecLab[8]; - float 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) - float x = vecLab[0], y = vecLab[1], z = vecLab[2]; - if (fabs(dx) > Almost0) { - if (fabs(vecLab[3]) < Almost0) { - return false; - } - dx = xk - vecLab[0]; - x += dx; - y += vecLab[4] / vecLab[3] * dx; - z += vecLab[5] / vecLab[3] * dx; - } - - // Calculate the track parameters - t = 1.f / std::sqrt(vecLab[3] * vecLab[3] + vecLab[4] * vecLab[4]); - mX = x; - mP[kY] = y; - mP[kZ] = z; - mP[kSnp] = vecLab[4] * t; - mP[kTgl] = vecLab[5] * t; - mP[kQ2Pt] = sgn * t / vecLab[6]; - - return true; -} - -//____________________________________________________________ -bool TrackPar::propagateParamTo(float xk, float b) -{ - //---------------------------------------------------------------- - // propagate this track to the plane X=xk (cm) in the field "b" (kG) - // Only parameters are propagated, not the matrix. To be used for small - // distances only (<mm, i.e. misalignment) - //---------------------------------------------------------------- - float dx = xk - getX(); - if (fabs(dx) < Almost0) { - return true; - } - float crv = (fabs(b) < Almost0) ? 0.f : getCurvature(b); - float x2r = crv * dx; - float f1 = getSnp(), f2 = f1 + x2r; - if ((fabs(f1) > Almost1) || (fabs(f2) > Almost1)) { - return false; - } - float r1 = std::sqrt((1.f - f1) * (1.f + f1)); - if (fabs(r1) < Almost0) { - return false; - } - float r2 = std::sqrt((1.f - f2) * (1.f + f2)); - if (fabs(r2) < Almost0) { - return false; - } - mX = xk; - double dy2dx = (f1 + f2) / (r1 + r2); - mP[kY] += dx * dy2dx; - mP[kSnp] += x2r; - if (fabs(x2r) < 0.05f) { - mP[kZ] += dx * (r2 + f2 * dy2dx) * getTgl(); - } else { - // for small dx/R the linear apporximation of the arc by the segment is OK, - // but at large dx/R the error is very large and leads to incorrect Z propagation - // angle traversed delta = 2*asin(dist_start_end / R / 2), hence the arc is: R*deltaPhi - // The dist_start_end is obtained from sqrt(dx^2+dy^2) = x/(r1+r2)*sqrt(2+f1*f2+r1*r2) - // double chord = dx*TMath::Sqrt(1+dy2dx*dy2dx); // distance from old position to new one - // double rot = 2*TMath::ASin(0.5*chord*crv); // angular difference seen from the circle center - // track1 += rot/crv*track3; - // - float rot = asinf(r1 * f2 - r2 * f1); // more economic version from Yura. - if (f1 * f1 + f2 * f2 > 1.f && f1 * f2 < 0.f) { // special cases of large rotations or large abs angles - if (f2 > 0.f) { - rot = PI - rot; // - } else { - rot = -PI - rot; - } - } - mP[kZ] += getTgl() / crv * rot; - } - return true; -} - -//_______________________________________________________________________ -bool TrackPar::propagateParamToDCA(const Point3D<float>& vtx, float b, std::array<float, 2>* dca, float maxD) -{ - // propagate track to DCA to the vertex - float sn, cs, alp = getAlpha(); - o2::utils::sincos(alp, sn, cs); - float x = getX(), y = getY(), snp = getSnp(), csp = std::sqrt((1.f - snp) * (1.f + snp)); - float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z(); - x -= xv; - y -= yv; - //Estimate the impact parameter neglecting the track curvature - Double_t d = std::abs(x * snp - y * csp); - if (d > maxD) { - return false; - } - float crv = getCurvature(b); - float tgfv = -(crv * x - snp) / (crv * y + csp); - sn = tgfv / std::sqrt(1.f + tgfv * tgfv); - cs = std::sqrt((1. - sn) * (1. + sn)); - cs = (std::abs(tgfv) > Almost0) ? sn / tgfv : Almost1; - - x = xv * cs + yv * sn; - yv = -xv * sn + yv * cs; - xv = x; - - auto tmpT(*this); // operate on the copy to recover after the failure - alp += std::asin(sn); - if (!tmpT.rotateParam(alp) || !tmpT.propagateParamTo(xv, b)) { - LOG(ERROR) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " - << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z() << " | Track is: "; - tmpT.printParam(); - return false; - } - *this = tmpT; - if (dca) { - (*dca)[0] = getY() - yv; - (*dca)[1] = getZ() - zv; - } - return true; -} - -//____________________________________________________________ -bool TrackPar::getYZAt(float xk, float b, float& y, float& z) const -{ - //---------------------------------------------------------------- - // estimate Y,Z in tracking frame at given X - //---------------------------------------------------------------- - float dx = xk - getX(); - if (fabs(dx) < Almost0) { - return true; - } - float crv = (fabs(b) < Almost0) ? 0.f : getCurvature(b); - float x2r = crv * dx; - float f1 = getSnp(), f2 = f1 + x2r; - if ((fabs(f1) > Almost1) || (fabs(f2) > Almost1)) { - return false; - } - if (fabs(getQ2Pt()) < Almost0) { - return false; - } - float r1 = std::sqrt((1.f - f1) * (1.f + f1)); - if (fabs(r1) < Almost0) { - return false; - } - float r2 = std::sqrt((1.f - f2) * (1.f + f2)); - if (fabs(r2) < Almost0) { - return false; - } - double dy2dx = (f1 + f2) / (r1 + r2); - y = mP[kY] + dx * dy2dx; - z = mP[kZ]; - if (fabs(x2r) < 0.05f) { - z += dx * (r2 + f2 * dy2dx) * getTgl(); - } else { - // for small dx/R the linear apporximation of the arc by the segment is OK, - // but at large dx/R the error is very large and leads to incorrect Z propagation - // angle traversed delta = 2*asin(dist_start_end / R / 2), hence the arc is: R*deltaPhi - // The dist_start_end is obtained from sqrt(dx^2+dy^2) = x/(r1+r2)*sqrt(2+f1*f2+r1*r2) - // double chord = dx*TMath::Sqrt(1+dy2dx*dy2dx); // distance from old position to new one - // double rot = 2*TMath::ASin(0.5*chord*crv); // angular difference seen from the circle center - // track1 += rot/crv*track3; - // - float rot = asinf(r1 * f2 - r2 * f1); // more economic version from Yura. - if (f1 * f1 + f2 * f2 > 1.f && f1 * f2 < 0.f) { // special cases of large rotations or large abs angles - if (f2 > 0.f) { - rot = PI - rot; // - } else { - rot = -PI - rot; - } - } - z += getTgl() / crv * rot; - } - return true; -} - -//______________________________________________________________ -void TrackPar::invertParam() -{ - // Transform this track to the local coord. system rotated by 180 deg. - mX = -mX; - mAlpha += PI; - utils::BringToPMPi(mAlpha); - // - mP[0] = -mP[0]; - mP[3] = -mP[3]; - mP[4] = -mP[4]; - // -} - -//______________________________________________________________ -float TrackPar::getZAt(float xk, float b) const -{ - ///< this method is just an alias for obtaining Z @ X in the tree->Draw() - float y, z; - return getYZAt(xk, b, y, z) ? z : -9999.; -} - -//______________________________________________________________ -float TrackPar::getYAt(float xk, float b) const -{ - ///< this method is just an alias for obtaining Z @ X in the tree->Draw() - float y, z; - return getYZAt(xk, b, y, z) ? y : -9999.; -} - -#ifndef GPUCA_ALIGPUCODE -//______________________________________________________________ -std::string TrackPar::asString() const -{ - // print parameters as string - return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e}", getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt()); -} - -//______________________________________________________________ -void TrackPar::printParam() const -{ - // print parameters - printf("%s\n", asString().c_str()); -} -#endif - -//______________________________________________________________ -bool TrackPar::getXatLabR(float r, float& x, float bz, o2::track::DirType dir) const -{ - // Get local X of the track position estimated at the radius lab radius r. - // The track curvature is accounted exactly - // - // The flag "dir" can be used to remove the ambiguity of which intersection to take (out of 2 possible) - // DirAuto (==0) - take the intersection closest to the current track position - // DirOutward (==1) - go along the track (increasing mX) - // DirInward (==-1) - go backward (decreasing mX) - // - const auto fy = mP[0], sn = mP[2]; - const float kEps = 1.e-6; - // - auto crv = getCurvature(bz); - if (fabs(crv) > o2::constants::math::Almost0) { // helix - // get center of the track circle - o2::utils::CircleXY circle; - getCircleParamsLoc(bz, circle); - auto r0 = std::sqrt(circle.getCenterD2()); - if (r0 <= o2::constants::math::Almost0) { - return false; // the track is concentric to circle - } - float tR2r0 = 1., g = 0., tmp = 0.; - if (fabs(circle.rC - r0) > kEps) { - tR2r0 = circle.rC / r0; - g = 0.5 * (r * r / (r0 * circle.rC) - tR2r0 - 1. / tR2r0); - tmp = 1. + g * tR2r0; - } else { - tR2r0 = 1.0; - g = 0.5 * r * r / (r0 * circle.rC) - 1.; - tmp = 0.5 * r * r / (r0 * r0); - } - auto det = (1. - g) * (1. + g); - if (det < 0.) { - return false; // does not reach raduis r - } - det = std::sqrt(det); - // - // the intersection happens in 2 points: {circle.xC+tR*C,circle.yC+tR*S} - // with C=f*c0+-|s0|*det and S=f*s0-+c0 sign(s0)*det - // where s0 and c0 make direction for the circle center (=circle.xC/r0 and circle.yC/r0) - // - x = circle.xC * tmp; - auto y = circle.yC * tmp; - if (fabs(circle.yC) > o2::constants::math::Almost0) { // when circle.yC==0 the x,y is unique - auto dfx = tR2r0 * fabs(circle.yC) * det; - auto dfy = tR2r0 * circle.xC * (circle.yC > 0. ? det : -det); - if (dir == DirAuto) { // chose the one which corresponds to smallest step - 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) - auto dfeps = mX - x; // handle special case of very small step - if (dfeps < -kEps) { - return true; - } - if (fabs(dfeps) < kEps && fabs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r? - return mX; - } - x += dfx + dfx; - auto dxm = x - mX; - if (dxm > 0.) { - return true; - } else if (dxm < -kEps) { - return false; - } - x = mX; // don't move - } else { // backward: x must be < mX - x += dfx; // try the smallest step (dfx is positive) - auto dfeps = x - mX; // handle special case of very small step - if (dfeps < -kEps) { - return true; - } - if (fabs(dfeps) < kEps && fabs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r? - return mX; - } - x -= dfx + dfx; - auto dxm = x - mX; - if (dxm < 0.) { - return true; - } - if (dxm > kEps) { - return false; - } - x = mX; // don't move - } - } else { // special case: track touching the circle just in 1 point - if ((dir == DirOutward && x < mX) || (dir == DirInward && x > mX)) { - return false; - } - } - } else { // this is a straight track - if (fabs(sn) >= o2::constants::math::Almost1) { // || to Y axis - auto det = (r - mX) * (r + mX); - if (det < 0.) { - return false; // does not reach raduis r - } - x = mX; - if (dir == DirAuto) { - return true; - } - det = std::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 (fy < -det) { - return false; // track is against Y axis amd belo the circle - } - } - } 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 if (fabs(sn) <= o2::constants::math::Almost0) { // || to X axis - auto det = (r - fy) * (r + fy); - if (det < 0.) { - return false; // does not reach raduis r - } - det = std::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 - } - } else { // against the track direction - if (mX < -det) { - return false; - } else { - x = mX > det ? det : -det; - } - } - } else { // general case of straight line - auto cs = std::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 = std::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 increasing 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; -} - -//______________________________________________ -bool TrackPar::correctForELoss(float xrho, float mass, bool anglecorr, float dedx) -{ - //------------------------------------------------------------------ - // This function corrects the track parameters for the energy loss in crossed material. - // "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. - // "mass" - the mass of this particle (GeV/c^2). Negative mass means charge=2 particle - // "dedx" - mean enery loss (GeV/(g/cm^2), if <=kCalcdEdxAuto : calculate on the fly - // "anglecorr" - switch for the angular correction - //------------------------------------------------------------------ - constexpr float kMaxELossFrac = 0.3f; // max allowed fractional eloss - constexpr float kMinP = 0.01f; // kill below this momentum - - // Apply angle correction, if requested - if (anglecorr) { - float csp2 = (1.f - getSnp()) * (1.f + getSnp()); // cos(phi)^2 - float cst2I = (1.f + getTgl() * getTgl()); // 1/cos(lambda)^2 - float angle = std::sqrt(cst2I / (csp2)); - xrho *= angle; - } - - float p = getP(); - if (mass < 0) { - p += p; // q=2 particle - } - float p2 = p * p, mass2 = mass * mass; - float e2 = p2 + mass2; - float beta2 = p2 / e2; - - // Calculating the energy loss corrections************************ - if ((xrho != 0.f) && (beta2 < 1.f)) { - if (dedx < kCalcdEdxAuto + Almost1) { // request to calculate dedx on the fly - dedx = BetheBlochSolid(p / fabs(mass)); - if (mass < 0) { - dedx *= 4.f; // z=2 particle - } - } - - float dE = dedx * xrho; - float e = std::sqrt(e2); - if (fabs(dE) > kMaxELossFrac * e) { - return false; // 30% energy loss is too much! - } - float eupd = e + dE; - float pupd2 = eupd * eupd - mass2; - if (pupd2 < kMinP * kMinP) { - return false; - } - setQ2Pt(getQ2Pt() * p / std::sqrt(pupd2)); - } - - return true; -} - -//______________________________________________________________ -void TrackParCov::invert() -{ - // Transform this track to the local coord. system rotated by 180 deg. - invertParam(); - // since the fP1 and fP2 are not inverted, their covariances with others change sign - mC[kSigZY] = -mC[kSigZY]; - mC[kSigSnpY] = -mC[kSigSnpY]; - mC[kSigTglZ] = -mC[kSigTglZ]; - mC[kSigTglSnp] = -mC[kSigTglSnp]; - mC[kSigQ2PtZ] = -mC[kSigQ2PtZ]; - mC[kSigQ2PtSnp] = -mC[kSigQ2PtSnp]; -} - -//______________________________________________________________ -bool TrackParCov::propagateTo(float xk, float b) -{ - //---------------------------------------------------------------- - // propagate this track to the plane X=xk (cm) in the field "b" (kG) - //---------------------------------------------------------------- - float dx = xk - getX(); - if (fabs(dx) < Almost0) { - return true; - } - float crv = (fabs(b) < Almost0) ? 0.f : getCurvature(b); - float x2r = crv * dx; - float f1 = getSnp(), f2 = f1 + x2r; - if ((fabs(f1) > Almost1) || (fabs(f2) > Almost1)) { - return false; - } - float r1 = std::sqrt((1.f - f1) * (1.f + f1)); - if (fabs(r1) < Almost0) { - return false; - } - float r2 = std::sqrt((1.f - f2) * (1.f + f2)); - if (fabs(r2) < Almost0) { - return false; - } - setX(xk); - double dy2dx = (f1 + f2) / (r1 + r2); - float dP[kNParams] = {0.f}; - dP[kY] = dx * dy2dx; - dP[kSnp] = x2r; - if (fabs(x2r) < 0.05f) { - dP[kZ] = dx * (r2 + f2 * dy2dx) * getTgl(); - } else { - // for small dx/R the linear apporximation of the arc by the segment is OK, - // but at large dx/R the error is very large and leads to incorrect Z propagation - // angle traversed delta = 2*asin(dist_start_end / R / 2), hence the arc is: R*deltaPhi - // The dist_start_end is obtained from sqrt(dx^2+dy^2) = x/(r1+r2)*sqrt(2+f1*f2+r1*r2) - // double chord = dx*TMath::Sqrt(1+dy2dx*dy2dx); // distance from old position to new one - // double rot = 2*TMath::ASin(0.5*chord*crv); // angular difference seen from the circle center - // mP1 += rot/crv*mP3; - // - float rot = asinf(r1 * f2 - r2 * f1); // more economic version from Yura. - if (f1 * f1 + f2 * f2 > 1.f && f1 * f2 < 0.f) { // special cases of large rotations or large abs angles - if (f2 > 0.f) { - rot = PI - rot; // - } else { - rot = -PI - rot; - } - } - dP[kZ] = getTgl() / crv * rot; - } - - updateParams(dP); // apply corrections - - float &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]; - - // evaluate matrix in double prec. - double rinv = 1. / r1; - double r3inv = rinv * rinv * rinv; - double f24 = dx * b * B2C; // x2r/mP[kQ2Pt]; - double f02 = dx * r3inv; - double f04 = 0.5 * f24 * f02; - double f12 = f02 * getTgl() * f1; - double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1; - double f13 = dx * rinv; - - // 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; -} - -//______________________________________________________________ -bool TrackParCov::rotate(float alpha) -{ - // rotate to alpha frame - if (fabs(getSnp()) > Almost1) { - LOGF(ERROR, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", getSnp()); - return false; - } - // - utils::BringToPMPi(alpha); - // - float ca = 0, sa = 0; - utils::sincos(alpha - getAlpha(), sa, ca); - float snp = getSnp(), csp = std::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; - } - // - - float updSnp = snp * ca - csp * sa; - if (fabs(updSnp) > Almost1) { - LOGF(WARNING, "Rotation failed: new snp {:.2f}", updSnp); - return false; - } - float xold = getX(), yold = getY(); - setAlpha(alpha); - setX(xold * ca + yold * sa); - setY(-xold * sa + yold * ca); - setSnp(updSnp); - - if (fabs(csp) < Almost0) { - LOGF(WARNING, "Too small cosine value {:f}", csp); - csp = Almost0; - } - - float rr = (ca + snp / csp * sa); - - 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; - - checkCovariance(); - return true; -} - -//_______________________________________________________________________ -bool TrackParCov::propagateToDCA(const o2::dataformats::VertexBase& vtx, float b, o2::dataformats::DCA* dca, float maxD) -{ - // propagate track to DCA to the vertex - float sn, cs, alp = getAlpha(); - o2::utils::sincos(alp, sn, cs); - float x = getX(), y = getY(), snp = getSnp(), csp = std::sqrt((1.f - snp) * (1.f + snp)); - float xv = vtx.getX() * cs + vtx.getY() * sn, yv = -vtx.getX() * sn + vtx.getY() * cs, zv = vtx.getZ(); - x -= xv; - y -= yv; - //Estimate the impact parameter neglecting the track curvature - Double_t d = std::abs(x * snp - y * csp); - if (d > maxD) { - return false; - } - float crv = getCurvature(b); - float tgfv = -(crv * x - snp) / (crv * y + csp); - sn = tgfv / std::sqrt(1.f + tgfv * tgfv); - cs = std::sqrt((1. - sn) * (1. + sn)); - cs = (std::abs(tgfv) > Almost0) ? sn / tgfv : Almost1; - - x = xv * cs + yv * sn; - yv = -xv * sn + yv * cs; - xv = x; - - auto tmpT(*this); // operate on the copy to recover after the failure - alp += std::asin(sn); - if (!tmpT.rotate(alp) || !tmpT.propagateTo(xv, b)) { - LOG(ERROR) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: "; - tmpT.print(); - return false; - } - *this = tmpT; - if (dca) { - o2::utils::sincos(alp, sn, cs); - auto s2ylocvtx = vtx.getSigmaX2() * sn * sn + vtx.getSigmaY2() * cs * cs - 2. * vtx.getSigmaXY() * cs * sn; - dca->set(getY() - yv, getZ() - zv, getSigmaY2() + s2ylocvtx, getSigmaZY(), getSigmaZ2() + vtx.getSigmaZ2()); - } - return true; -} - -//______________________________________________________________ -TrackParCov::TrackParCov(const array<float, 3>& xyz, const array<float, 3>& pxpypz, - const array<float, kLabCovMatSize>& cv, int charge, bool sectorAlpha) -{ - // construct track param and covariance from kinematics and lab errors - - // Alpha of the frame is defined as: - // sectorAlpha == false : -> angle of pt direction - // sectorAlpha == true : -> angle of the sector from X,Y coordinate for r>1 - // angle of pt direction for r==0 - // - // - constexpr float kSafe = 1e-5f; - float radPos2 = xyz[0] * xyz[0] + xyz[1] * xyz[1]; - float alp = 0; - if (sectorAlpha || radPos2 < 1) { - alp = atan2f(pxpypz[1], pxpypz[0]); - } else { - alp = atan2f(xyz[1], xyz[0]); - } - if (sectorAlpha) { - alp = utils::Angle2Alpha(alp); - } - // - float sn, cs; - utils::sincos(alp, sn, cs); - // protection: avoid alpha being too close to 0 or +-pi/2 - if (fabs(sn) < 2.f * kSafe) { - if (alp > 0) { - alp += alp < PIHalf ? 2.f * kSafe : -2.f * kSafe; - } else { - alp += alp > -PIHalf ? -2.f * kSafe : 2.f * kSafe; - } - utils::sincos(alp, sn, cs); - } else if (fabs(cs) < 2.f * kSafe) { - if (alp > 0) { - alp += alp > PIHalf ? 2.f * kSafe : -2.f * kSafe; - } else { - alp += alp > -PIHalf ? 2.f * kSafe : -2.f * kSafe; - } - utils::sincos(alp, sn, cs); - } - // get the vertex of origin and the momentum - array<float, 3> ver{xyz[0], xyz[1], xyz[2]}; - array<float, 3> mom{pxpypz[0], pxpypz[1], pxpypz[2]}; - // - // Rotate to the local coordinate system - utils::RotateZ(ver, -alp); - utils::RotateZ(mom, -alp); - // - float pt = sqrt(mom[0] * mom[0] + mom[1] * mom[1]); - float ptI = 1.f / pt; - setX(ver[0]); - setAlpha(alp); - setY(ver[1]); - setZ(ver[2]); - setSnp(mom[1] * ptI); // cos(phi) - setTgl(mom[2] * ptI); // tg(lambda) - setQ2Pt(ptI * charge); - // - if (fabs(1.f - getSnp()) < kSafe) { - setSnp(1.f - kSafe); // Protection - } else if (fabs(-1.f - getSnp()) < kSafe) { - setSnp(-1.f + kSafe); // Protection - } - // - // Covariance matrix (formulas to be simplified) - float r = mom[0] * ptI; // cos(phi) - float cv34 = std::sqrt(cv[3] * cv[3] + cv[4] * cv[4]); - // - int special = 0; - float sgcheck = r * sn + getSnp() * cs; - if (fabs(sgcheck) > 1 - kSafe) { // special case: lab phi is +-pi/2 - special = 1; - sgcheck = sgcheck < 0 ? -1.f : 1.f; - } else if (fabs(sgcheck) < kSafe) { - sgcheck = cs < 0 ? -1.0f : 1.0f; - special = 2; // special case: lab phi is 0 - } - // - mC[kSigY2] = cv[0] + cv[2]; - mC[kSigZY] = (-cv[3] * sn) < 0 ? -cv34 : cv34; - mC[kSigZ2] = cv[5]; - // - float ptI2 = ptI * ptI; - float tgl2 = getTgl() * getTgl(); - if (special == 1) { - mC[kSigSnpY] = cv[6] * ptI; - mC[kSigSnpZ] = -sgcheck * cv[8] * r * ptI; - mC[kSigSnp2] = fabs(cv[9] * r * r * ptI2); - mC[kSigTglY] = (cv[10] * getTgl() - sgcheck * cv[15]) * ptI / r; - mC[kSigTglZ] = (cv[17] - sgcheck * cv[12] * getTgl()) * ptI; - mC[kSigTglSnp] = (-sgcheck * cv[18] + cv[13] * getTgl()) * r * ptI2; - mC[kSigTgl2] = fabs(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] * getTgl()) * r * ptI2 * ptI; - mC[kSigQ2Pt2] = fabs(cv[14] * ptI2 * ptI2); - } else if (special == 2) { - mC[kSigSnpY] = -cv[10] * ptI * cs / sn; - mC[kSigSnpZ] = cv[12] * cs * ptI; - mC[kSigSnp2] = fabs(cv[14] * cs * cs * ptI2); - mC[kSigTglY] = (sgcheck * cv[6] * getTgl() - cv[15]) * ptI / sn; - mC[kSigTglZ] = (cv[17] - sgcheck * cv[8] * getTgl()) * ptI; - mC[kSigTglSnp] = (cv[19] - sgcheck * cv[13] * getTgl()) * cs * ptI2; - mC[kSigTgl2] = fabs(cv[20] - 2 * sgcheck * cv[18] * 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] * getTgl()) * ptI2 * ptI * charge; - mC[kSigQ2Pt2] = fabs(cv[9] * ptI2 * ptI2); - } else { - double m00 = -sn; // m10=cs; - double m23 = -pt * (sn + getSnp() * cs / r), m43 = -pt * pt * (r * cs - getSnp() * sn); - double m24 = pt * (cs - getSnp() * sn / r), m44 = -pt * pt * (r * sn + getSnp() * cs); - double m35 = pt, m45 = -pt * pt * getTgl(); - // - m43 *= charge; - m44 *= charge; - m45 *= charge; - // - 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] = fabs((a4 * a3 - a6 * a1) / (a5 * a3 - a6 * a2)); - mC[kSigQ2Pt2] = fabs((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] = fabs((cv[20] - mC[kSigQ2Pt2] * (m45 * m45) - mC[kSigQ2PtTgl] * 2. * m35 * m45) / (m35 * m35)); - } - checkCovariance(); -} - -//____________________________________________________________ -bool TrackParCov::propagateTo(float xk, const array<float, 3>& 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. - //---------------------------------------------------------------- - - float dx = xk - getX(); - if (fabs(dx) < Almost0) { - return true; - } - // Do not propagate tracks outside the ALICE detector - if (fabs(dx) > 1e5 || fabs(getY()) > 1e5 || fabs(getZ()) > 1e5) { - LOGF(WARNING, "Anomalous track, target X:{:f}", xk); - // print(); - return false; - } - float crv = (fabs(b[2]) < Almost0) ? 0.f : getCurvature(b[2]); - float x2r = crv * dx; - float f1 = getSnp(), f2 = f1 + x2r; - if ((fabs(f1) > Almost1) || (fabs(f2) > Almost1)) { - return false; - } - float r1 = std::sqrt((1.f - f1) * (1.f + f1)); - if (fabs(r1) < Almost0) { - return false; - } - float r2 = std::sqrt((1.f - f2) * (1.f + f2)); - if (fabs(r2) < Almost0) { - return false; - } - - float dy2dx = (f1 + f2) / (r1 + r2); - float step = (fabs(x2r) < 0.05f) ? dx * fabs(r2 + f2 * dy2dx) // chord - : 2.f * asinf(0.5f * dx * std::sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc - step *= std::sqrt(1.f + getTgl() * getTgl()); - // - // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System - array<float, 9> vecLab{0.f}; - if (!getPosDirGlo(vecLab)) { - return false; - } - // - // matrix transformed with Bz component only - float &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]; - // evaluate matrix in double prec. - double rinv = 1. / r1; - double r3inv = rinv * rinv * rinv; - double f24 = dx * b[2] * B2C; // x2r/track[kQ2Pt]; - double f02 = dx * r3inv; - double f04 = 0.5 * f24 * f02; - double f12 = f02 * getTgl() * f1; - double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1; - double f13 = dx * rinv; - - // 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(); - - // Rotate to the system where Bx=By=0. - float bxy2 = b[0] * b[0] + b[1] * b[1]; - float bt = std::sqrt(bxy2); - float cosphi = 1.f, sinphi = 0.f; - if (bt > Almost0) { - cosphi = b[0] / bt; - sinphi = b[1] / bt; - } - float bb = std::sqrt(bxy2 + b[2] * b[2]); - float costet = 1., sintet = 0.; - if (bb > Almost0) { - costet = b[2] / bb; - sintet = bt / bb; - } - array<float, 7> 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 - float sgn = getSign(); - g3helx3(sgn * 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 - float sinalp = -vecLab[7], cosalp = vecLab[8]; - float 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) - float x = vecLab[0], y = vecLab[1], z = vecLab[2]; - if (fabs(dx) > Almost0) { - if (fabs(vecLab[3]) < Almost0) { - return false; - } - dx = xk - vecLab[0]; - x += dx; - y += vecLab[4] / vecLab[3] * dx; - z += vecLab[5] / vecLab[3] * dx; - } - - // Calculate the track parameters - t = 1.f / std::sqrt(vecLab[3] * vecLab[3] + vecLab[4] * vecLab[4]); - setX(x); - setY(y); - setZ(z); - setSnp(vecLab[4] * t); - setTgl(vecLab[5] * t); - setQ2Pt(sgn * t / vecLab[6]); - - return true; -} - -//______________________________________________ -void TrackParCov::checkCovariance() -{ - // This function forces the diagonal elements of the covariance matrix to be positive. - // In case the diagonal element is bigger than the maximal allowed value, it is set to - // the limit and the off-diagonal elements that correspond to it are set to zero. - - mC[kSigY2] = fabs(mC[kSigY2]); - if (mC[kSigY2] > kCY2max) { - float scl = std::sqrt(kCY2max / mC[kSigY2]); - mC[kSigY2] = kCY2max; - mC[kSigZY] *= scl; - mC[kSigSnpY] *= scl; - mC[kSigTglY] *= scl; - mC[kSigQ2PtY] *= scl; - } - mC[kSigZ2] = fabs(mC[kSigZ2]); - if (mC[kSigZ2] > kCZ2max) { - float scl = std::sqrt(kCZ2max / mC[kSigZ2]); - mC[kSigZ2] = kCZ2max; - mC[kSigZY] *= scl; - mC[kSigSnpZ] *= scl; - mC[kSigTglZ] *= scl; - mC[kSigQ2PtZ] *= scl; - } - mC[kSigSnp2] = fabs(mC[kSigSnp2]); - if (mC[kSigSnp2] > kCSnp2max) { - float scl = std::sqrt(kCSnp2max / mC[kSigSnp2]); - mC[kSigSnp2] = kCSnp2max; - mC[kSigSnpY] *= scl; - mC[kSigSnpZ] *= scl; - mC[kSigTglSnp] *= scl; - mC[kSigQ2PtSnp] *= scl; - } - mC[kSigTgl2] = fabs(mC[kSigTgl2]); - if (mC[kSigTgl2] > kCTgl2max) { - float scl = std::sqrt(kCTgl2max / mC[kSigTgl2]); - mC[kSigTgl2] = kCTgl2max; - mC[kSigTglY] *= scl; - mC[kSigTglZ] *= scl; - mC[kSigTglSnp] *= scl; - mC[kSigQ2PtTgl] *= scl; - } - mC[kSigQ2Pt2] = fabs(mC[kSigQ2Pt2]); - if (mC[kSigQ2Pt2] > kC1Pt2max) { - float scl = std::sqrt(kC1Pt2max / mC[kSigQ2Pt2]); - mC[kSigQ2Pt2] = kC1Pt2max; - mC[kSigQ2PtY] *= scl; - mC[kSigQ2PtZ] *= scl; - mC[kSigQ2PtSnp] *= scl; - mC[kSigQ2PtTgl] *= scl; - } -} - -//______________________________________________ -void TrackParCov::resetCovariance(float s2) -{ - // Reset the covarince matrix to "something big" - double d0(kCY2max), d1(kCZ2max), d2(kCSnp2max), d3(kCTgl2max), d4(kC1Pt2max); - if (s2 > Almost0) { - d0 = getSigmaY2() * s2; - d1 = getSigmaZ2() * s2; - d2 = getSigmaSnp2() * s2; - d3 = getSigmaTgl2() * s2; - d4 = getSigma1Pt2() * s2; - if (d0 > kCY2max) { - d0 = kCY2max; - } - if (d1 > kCZ2max) { - d1 = kCZ2max; - } - if (d2 > kCSnp2max) { - d2 = kCSnp2max; - } - if (d3 > kCTgl2max) { - d3 = kCTgl2max; - } - if (d4 > kC1Pt2max) { - d4 = kC1Pt2max; - } - } - memset(mC, 0, kCovMatSize * sizeof(float)); - mC[kSigY2] = d0; - mC[kSigZ2] = d1; - mC[kSigSnp2] = d2; - mC[kSigTgl2] = d3; - mC[kSigQ2Pt2] = d4; -} - -//______________________________________________ -float TrackParCov::getPredictedChi2(const array<float, 2>& p, const array<float, 3>& cov) const -{ - // Estimate the chi2 of the space point "p" with the cov. matrix "cov" - auto sdd = static_cast<double>(getSigmaY2()) + static_cast<double>(cov[0]); - auto sdz = static_cast<double>(getSigmaZY()) + static_cast<double>(cov[1]); - auto szz = static_cast<double>(getSigmaZ2()) + static_cast<double>(cov[2]); - auto det = sdd * szz - sdz * sdz; - - if (fabs(det) < Almost0) { - return VeryBig; - } - - float d = getY() - p[0]; - float z = getZ() - p[1]; - - return (d * (szz * d - sdz * z) + z * (sdd * z - d * sdz)) / det; -} - -//______________________________________________ -void TrackParCov::buildCombinedCovMatrix(const TrackParCov& rhs, MatrixDSym5& cov) const -{ - // fill combined cov.matrix (NOT inverted) - cov(kY, kY) = static_cast<double>(getSigmaY2()) + static_cast<double>(rhs.getSigmaY2()); - cov(kZ, kY) = static_cast<double>(getSigmaZY()) + static_cast<double>(rhs.getSigmaZY()); - cov(kZ, kZ) = static_cast<double>(getSigmaZ2()) + static_cast<double>(rhs.getSigmaZ2()); - cov(kSnp, kY) = static_cast<double>(getSigmaSnpY()) + static_cast<double>(rhs.getSigmaSnpY()); - cov(kSnp, kZ) = static_cast<double>(getSigmaSnpZ()) + static_cast<double>(rhs.getSigmaSnpZ()); - cov(kSnp, kSnp) = static_cast<double>(getSigmaSnp2()) + static_cast<double>(rhs.getSigmaSnp2()); - cov(kTgl, kY) = static_cast<double>(getSigmaTglY()) + static_cast<double>(rhs.getSigmaTglY()); - cov(kTgl, kZ) = static_cast<double>(getSigmaTglZ()) + static_cast<double>(rhs.getSigmaTglZ()); - cov(kTgl, kSnp) = static_cast<double>(getSigmaTglSnp()) + static_cast<double>(rhs.getSigmaTglSnp()); - cov(kTgl, kTgl) = static_cast<double>(getSigmaTgl2()) + static_cast<double>(rhs.getSigmaTgl2()); - cov(kQ2Pt, kY) = static_cast<double>(getSigma1PtY()) + static_cast<double>(rhs.getSigma1PtY()); - cov(kQ2Pt, kZ) = static_cast<double>(getSigma1PtZ()) + static_cast<double>(rhs.getSigma1PtZ()); - cov(kQ2Pt, kSnp) = static_cast<double>(getSigma1PtSnp()) + static_cast<double>(rhs.getSigma1PtSnp()); - cov(kQ2Pt, kTgl) = static_cast<double>(getSigma1PtTgl()) + static_cast<double>(rhs.getSigma1PtTgl()); - cov(kQ2Pt, kQ2Pt) = static_cast<double>(getSigma1Pt2()) + static_cast<double>(rhs.getSigma1Pt2()); -} - -//______________________________________________ -float TrackParCov::getPredictedChi2(const TrackParCov& rhs) const -{ - MatrixDSym5 cov; // perform matrix operations in double! - return getPredictedChi2(rhs, cov); -} - -//______________________________________________ -float TrackParCov::getPredictedChi2(const TrackParCov& rhs, MatrixDSym5& covToSet) const -{ - // 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 (std::abs(getAlpha() - rhs.getAlpha()) > FLT_EPSILON) { - LOG(ERROR) << "The reference Alpha of the tracks differ: " << getAlpha() << " : " << rhs.getAlpha(); - return 2. * HugeF; - } - if (std::abs(getX() - rhs.getX()) > FLT_EPSILON) { - LOG(ERROR) << "The reference X of the tracks differ: " << getX() << " : " << rhs.getX(); - return 2. * HugeF; - } - buildCombinedCovMatrix(rhs, covToSet); - if (!covToSet.Invert()) { - LOG(ERROR) << "Cov.matrix inversion failed: " << covToSet; - return 2. * HugeF; - } - double chi2diag = 0., chi2ndiag = 0., diff[kNParams]; - for (int i = kNParams; i--;) { - diff[i] = getParam(i) - rhs.getParam(i); - chi2diag += diff[i] * diff[i] * covToSet(i, i); - } - for (int i = kNParams; i--;) { - for (int j = i; j--;) { - chi2ndiag += diff[i] * diff[j] * covToSet(i, j); - } - } - return chi2diag + 2. * chi2ndiag; -} - -//______________________________________________ -bool TrackParCov::update(const TrackParCov& 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 (std::abs(getAlpha() - rhs.getAlpha()) > FLT_EPSILON) { - LOG(ERROR) << "The reference Alpha of the tracks differ: " << getAlpha() << " : " << rhs.getAlpha(); - return false; - } - if (std::abs(getX() - rhs.getX()) > FLT_EPSILON) { - LOG(ERROR) << "The reference X of the tracks differ: " << getX() << " : " << rhs.getX(); - return false; - } - - // gain matrix K = Cov0*H*(Cov0+Cov0)^-1 (for measurement matrix H=I) - MatrixDSym5 matC0; - matC0(kY, kY) = getSigmaY2(); - matC0(kZ, kY) = getSigmaZY(); - matC0(kZ, kZ) = getSigmaZ2(); - matC0(kSnp, kY) = getSigmaSnpY(); - matC0(kSnp, kZ) = getSigmaSnpZ(); - matC0(kSnp, kSnp) = getSigmaSnp2(); - matC0(kTgl, kY) = getSigmaTglY(); - matC0(kTgl, kZ) = getSigmaTglZ(); - matC0(kTgl, kSnp) = getSigmaTglSnp(); - matC0(kTgl, kTgl) = getSigmaTgl2(); - matC0(kQ2Pt, kY) = getSigma1PtY(); - matC0(kQ2Pt, kZ) = getSigma1PtZ(); - matC0(kQ2Pt, kSnp) = getSigma1PtSnp(); - matC0(kQ2Pt, kTgl) = getSigma1PtTgl(); - matC0(kQ2Pt, kQ2Pt) = getSigma1Pt2(); - MatrixD5 matK = matC0 * covInv; - - // updated state vector: x = K*(x1-x0) - // RS: why SMatix, SVector does not provide multiplication operators ??? - double diff[kNParams]; - for (int i = kNParams; i--;) { - diff[i] = rhs.getParam(i) - getParam(i); - } - for (int i = kNParams; i--;) { - for (int j = kNParams; j--;) { - updateParam(matK(i, j) * diff[j], i); - } - } - - // updated covariance: Cov0 = Cov0 - K*Cov0 - matK *= ROOT::Math::SMatrix<double, kNParams, kNParams, ROOT::Math::MatRepStd<double, kNParams>>(matC0); - mC[kSigY2] -= matK(kY, kY); - mC[kSigZY] -= matK(kZ, kY); - mC[kSigZ2] -= matK(kZ, kZ); - mC[kSigSnpY] -= matK(kSnp, kY); - mC[kSigSnpZ] -= matK(kSnp, kZ); - mC[kSigSnp2] -= matK(kSnp, kSnp); - mC[kSigTglY] -= matK(kTgl, kY); - mC[kSigTglZ] -= matK(kTgl, kZ); - mC[kSigTglSnp] -= matK(kTgl, kSnp); - mC[kSigTgl2] -= matK(kTgl, kTgl); - mC[kSigQ2PtY] -= matK(kQ2Pt, kY); - mC[kSigQ2PtZ] -= matK(kQ2Pt, kZ); - mC[kSigQ2PtSnp] -= matK(kQ2Pt, kSnp); - mC[kSigQ2PtTgl] -= matK(kQ2Pt, kTgl); - mC[kSigQ2Pt2] -= matK(kQ2Pt, kQ2Pt); - - return true; -} - -//______________________________________________ -bool TrackParCov::update(const TrackParCov& rhs) -{ - // update track with other track - MatrixDSym5 covI; // perform matrix operations in double! - buildCombinedCovMatrix(rhs, covI); - if (!covI.Invert()) { - LOG(ERROR) << "Cov.matrix inversion failed: " << covI; - return false; - } - return update(rhs, covI); -} - -//______________________________________________ -bool TrackParCov::update(const array<float, 2>& p, const array<float, 3>& cov) -{ - // Update the track parameters with the space point "p" having - // the covariance matrix "cov" - - float &cm00 = mC[kSigY2], &cm10 = mC[kSigZY], &cm11 = mC[kSigZ2], &cm20 = mC[kSigSnpY], &cm21 = mC[kSigSnpZ], - &cm22 = mC[kSigSnp2], &cm30 = mC[kSigTglY], &cm31 = mC[kSigTglZ], &cm32 = mC[kSigTglSnp], &cm33 = mC[kSigTgl2], - &cm40 = mC[kSigQ2PtY], &cm41 = mC[kSigQ2PtZ], &cm42 = mC[kSigQ2PtSnp], &cm43 = mC[kSigQ2PtTgl], - &cm44 = mC[kSigQ2Pt2]; - - // use double precision? - double r00 = static_cast<double>(cov[0]) + static_cast<double>(cm00); - double r01 = static_cast<double>(cov[1]) + static_cast<double>(cm10); - double r11 = static_cast<double>(cov[2]) + static_cast<double>(cm11); - double det = r00 * r11 - r01 * r01; - - if (fabs(det) < Almost0) { - return false; - } - double detI = 1. / det; - double tmp = r00; - r00 = r11 * detI; - r11 = tmp * detI; - r01 = -r01 * detI; - - double k00 = cm00 * r00 + cm10 * r01, k01 = cm00 * r01 + cm10 * r11; - double k10 = cm10 * r00 + cm11 * r01, k11 = cm10 * r01 + cm11 * r11; - double k20 = cm20 * r00 + cm21 * r01, k21 = cm20 * r01 + cm21 * r11; - double k30 = cm30 * r00 + cm31 * r01, k31 = cm30 * r01 + cm31 * r11; - double k40 = cm40 * r00 + cm41 * r01, k41 = cm40 * r01 + cm41 * r11; - - float dy = p[kY] - getY(), dz = p[kZ] - getZ(); - float dsnp = k20 * dy + k21 * dz; - if (fabs(getSnp() + dsnp) > Almost1) { - return false; - } - - float dP[kNParams] = {float(k00 * dy + k01 * dz), float(k10 * dy + k11 * dz), dsnp, float(k30 * dy + k31 * dz), - float(k40 * dy + k41 * dz)}; - updateParams(dP); - - double c01 = cm10, c02 = cm20, c03 = cm30, c04 = cm40; - double c12 = cm21, c13 = cm31, c14 = cm41; - - cm00 -= k00 * cm00 + k01 * cm10; - cm10 -= k00 * c01 + k01 * cm11; - cm20 -= k00 * c02 + k01 * c12; - cm30 -= k00 * c03 + k01 * c13; - cm40 -= k00 * c04 + k01 * c14; - - cm11 -= k10 * c01 + k11 * cm11; - cm21 -= k10 * c02 + k11 * c12; - cm31 -= k10 * c03 + k11 * c13; - cm41 -= k10 * c04 + k11 * c14; - - cm22 -= k20 * c02 + k21 * c12; - cm32 -= k20 * c03 + k21 * c13; - cm42 -= k20 * c04 + k21 * c14; - - cm33 -= k30 * c03 + k31 * c13; - cm43 -= k30 * c04 + k31 * c14; - - cm44 -= k40 * c04 + k41 * c14; - - checkCovariance(); - - return true; -} - -//______________________________________________ -bool TrackParCov::correctForMaterial(float x2x0, float xrho, float mass, bool anglecorr, float dedx) -{ - //------------------------------------------------------------------ - // This function corrects the 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. - // "mass" - the mass of this particle (GeV/c^2). Negative mass means charge=2 particle - // "dedx" - mean enery loss (GeV/(g/cm^2), if <=kCalcdEdxAuto : calculate on the fly - // "anglecorr" - switch for the angular correction - //------------------------------------------------------------------ - constexpr float kMSConst2 = 0.0136f * 0.0136f; - constexpr float kMaxELossFrac = 0.3f; // max allowed fractional eloss - constexpr float kMinP = 0.01f; // kill below this momentum - - float& fC22 = mC[kSigSnp2]; - float& fC33 = mC[kSigTgl2]; - float& fC43 = mC[kSigQ2PtTgl]; - float& fC44 = mC[kSigQ2Pt2]; - // - float csp2 = (1.f - getSnp()) * (1.f + getSnp()); // cos(phi)^2 - float cst2I = (1.f + getTgl() * getTgl()); // 1/cos(lambda)^2 - // Apply angle correction, if requested - if (anglecorr) { - float angle = std::sqrt(cst2I / (csp2)); - x2x0 *= angle; - xrho *= angle; - } - - float p = getP(); - if (mass < 0) { - p += p; // q=2 particle - } - float p2 = p * p, mass2 = mass * mass; - float e2 = p2 + mass2; - float beta2 = p2 / e2; - - // Calculating the multiple scattering corrections****************** - float cC22(0.f), cC33(0.f), cC43(0.f), cC44(0.f); - if (x2x0 != 0.f) { - float theta2 = kMSConst2 / (beta2 * p2) * fabs(x2x0); - if (mass < 0) { - theta2 *= 4.f; // q=2 particle - } - if (theta2 > PI * PI) { - return false; - } - float fp34 = getTgl() * getQ2Pt(); - float t2c2I = theta2 * cst2I; - cC22 = t2c2I * csp2; - cC33 = t2c2I * cst2I; - cC43 = t2c2I * fp34; - cC44 = theta2 * fp34 * fp34; - // optimes this - // cC22 = theta2*((1.-getSnp())*(1.+getSnp()))*(1. + getTgl()*getTgl()); - // cC33 = theta2*(1. + getTgl()*getTgl())*(1. + getTgl()*getTgl()); - // cC43 = theta2*getTgl()*getQ2Pt()*(1. + getTgl()*getTgl()); - // cC44 = theta2*getTgl()*getQ2Pt()*getTgl()*getQ2Pt(); - } - - // Calculating the energy loss corrections************************ - float cP4 = 1.f; - if ((xrho != 0.f) && (beta2 < 1.f)) { - if (dedx < kCalcdEdxAuto + Almost1) { // request to calculate dedx on the fly - dedx = BetheBlochSolid(p / fabs(mass)); - if (mass < 0) { - dedx *= 4.f; // z=2 particle - } - } - - float dE = dedx * xrho; - float e = std::sqrt(e2); - if (fabs(dE) > kMaxELossFrac * e) { - return false; // 30% energy loss is too much! - } - float eupd = e + dE; - float pupd2 = eupd * eupd - mass2; - if (pupd2 < kMinP * kMinP) { - return false; - } - cP4 = p / std::sqrt(pupd2); - // - // Approximate energy loss fluctuation (M.Ivanov) - constexpr float knst = 0.07f; // To be tuned. - float sigmadE = knst * std::sqrt(fabs(dE)) * e / p2 * getQ2Pt(); - cC44 += sigmadE * sigmadE; - } - - // Applying the corrections***************************** - fC22 += cC22; - fC33 += cC33; - fC43 += cC43; - fC44 += cC44; - setQ2Pt(getQ2Pt() * cP4); - - checkCovariance(); - - return true; -} - -#ifndef GPUCA_ALIGPUCODE -//______________________________________________________________ -std::string TrackParCov::asString() const -{ - return TrackPar::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]); -} - -//______________________________________________________________ -void TrackParCov::print() const -{ - // print parameters - printf("%s\n", asString().c_str()); -} -#endif - -//================================================= -// -// Aux. methods for tracks manipulation -// -//================================================= - -void o2::track::g3helx3(float qfield, float step, array<float, 7>& vect) -{ - /****************************************************************** - * * - * GEANT3 tracking routine in a constant field oriented * - * along axis 3 * - * Tracking is performed with a conventional * - * helix step method * - * * - * Authors R.Brun, M.Hansroul ********* * - * Rewritten V.Perevoztchikov * - * * - * Rewritten in C++ by I.Belikov * - * * - * qfield (kG) - particle charge times magnetic field * - * step (cm) - step length along the helix * - * vect[7](cm,GeV/c) - input/output x, y, z, px/p, py/p ,pz/p, p * - * * - ******************************************************************/ - const int ix = 0, iy = 1, iz = 2, ipx = 3, ipy = 4, ipz = 5, ipp = 6; - constexpr float kOvSqSix = 0.408248f; // std::sqrt(1./6.); - - float cosx = vect[ipx], cosy = vect[ipy], cosz = vect[ipz]; - - float rho = qfield * B2C / vect[ipp]; - float tet = rho * step; - - float tsint, sintt, sint, cos1t; - if (fabs(tet) > 0.03f) { - sint = sinf(tet); - sintt = sint / tet; - tsint = (tet - sint) / tet; - float t = sinf(0.5f * tet); - cos1t = 2 * t * t / tet; - } else { - tsint = tet * tet / 6.f; - sintt = (1.f - tet * kOvSqSix) * (1.f + tet * kOvSqSix); // 1.- tsint; - sint = tet * sintt; - cos1t = 0.5f * tet; - } - - float f1 = step * sintt; - float f2 = step * cos1t; - float f3 = step * tsint * cosz; - float f4 = -tet * cos1t; - float f5 = sint; - - vect[ix] += f1 * cosx - f2 * cosy; - vect[iy] += f1 * cosy + f2 * cosx; - vect[iz] += f1 * cosz + f3; - - vect[ipx] += f4 * cosx - f5 * cosy; - vect[ipy] += f4 * cosy + f5 * cosx; -} - -//____________________________________________________ -float o2::track::BetheBlochSolid(float bg, float rho, float kp1, float kp2, float meanI, float meanZA) -{ - // - // This is the parameterization of the Bethe-Bloch formula inspired by Geant. - // - // 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 float mK = 0.307075e-3f; // [GeV*cm^2/g] - constexpr float me = 0.511e-3f; // [GeV/c^2] - kp1 *= 2.303f; - kp2 *= 2.303f; - float bg2 = bg * bg; - float maxT = 2.f * me * bg2; // neglecting the electron mass - - //*** Density effect - float d2 = 0.; - const float x = log(bg); - const float lhwI = log(28.816 * 1e-9 * std::sqrt(rho * meanZA) / meanI); - if (x > kp2) { - d2 = lhwI + x - 0.5; - } else if (x > kp1) { - double r = (kp2 - x) / (kp2 - kp1); - d2 = lhwI + x - 0.5 + (0.5 - lhwI - kp1) * r * r * r; - } - return mK * meanZA * (1 + bg2) / bg2 * (0.5 * log(2 * me * bg2 * maxT / (meanI * meanI)) - bg2 / (1 + bg2) - d2); -} diff --git a/DataFormats/Reconstruction/src/TrackFwd.cxx b/DataFormats/Reconstruction/src/TrackFwd.cxx new file mode 100644 index 0000000000000..c826f73126df1 --- /dev/null +++ b/DataFormats/Reconstruction/src/TrackFwd.cxx @@ -0,0 +1,388 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/TrackFwd.h" +#include "Math/MatrixFunctions.h" + +namespace o2 +{ +namespace track +{ +using namespace std; + +//_________________________________________________________________________ +TrackParCovFwd::TrackParCovFwd(const Double_t z, const SMatrix5 parameters, const SMatrix55 covariances, const Double_t chi2) +{ + setZ(z); + setParameters(parameters); + setCovariances(covariances); + setTrackChi2(chi2); +} + +//__________________________________________________________________________ +void TrackParFwd::propagateParamToZlinear(double zEnd) +{ + // Track parameters linearly extrapolated to the plane at "zEnd". + + if (getZ() == zEnd) { + return; // nothing to be done if same z + } + + // Compute track parameters + auto dZ = (zEnd - getZ()); + auto phi0 = getPhi(); + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + auto invtanl0 = 1.0 / getTanl(); + auto n = dZ * invtanl0; + mParameters(0) += n * cosphi0; + mParameters(1) += n * sinphi0; + mZ = zEnd; +} + +//__________________________________________________________________________ +void TrackParCovFwd::propagateToZlinear(double zEnd) +{ + // Track parameters and their covariances linearly extrapolated to the plane at "zEnd". + + // Calculate the jacobian related to the track parameters extrapolated to "zEnd" + auto dZ = (zEnd - getZ()); + auto phi0 = getPhi(); + auto tanl0 = getTanl(); + auto invtanl0 = 1.0 / tanl0; + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + auto n = dZ * invtanl0; + auto m = n * invtanl0; + + // Extrapolate track parameters to "zEnd" + mParameters(0) += n * cosphi0; + mParameters(1) += n * sinphi0; + setZ(zEnd); + + // Calculate Jacobian + SMatrix55 jacob = ROOT::Math::SMatrixIdentity(); + jacob(0, 2) = -n * sinphi0; + jacob(0, 3) = -m * cosphi0; + jacob(1, 2) = n * cosphi0; + jacob(1, 3) = -m * sinphi0; + + // Extrapolate track parameter covariances to "zEnd" + setCovariances(ROOT::Math::Similarity(jacob, mCovariances)); +} + +//__________________________________________________________________________ +void TrackParFwd::propagateParamToZquadratic(double zEnd, double zField) +{ + // Track parameters extrapolated to the plane at "zEnd" considering a helix + + if (getZ() == zEnd) { + return; // nothing to be done if same z + } + + // Compute track parameters + auto dZ = (zEnd - getZ()); + auto phi0 = getPhi(); + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + auto invtanl0 = 1.0 / getTanl(); + auto invqpt0 = getInvQPt(); + auto Hz = std::copysign(1, zField); + auto k = TMath::Abs(o2::constants::math::B2C * zField); + auto n = dZ * invtanl0; + auto theta = -invqpt0 * dZ * k * invtanl0; + + mParameters(0) += n * cosphi0 - 0.5 * n * theta * Hz * sinphi0; + mParameters(1) += n * sinphi0 + 0.5 * n * theta * Hz * cosphi0; + mParameters(2) += Hz * theta; + setZ(zEnd); +} + +//__________________________________________________________________________ +void TrackParCovFwd::propagateToZquadratic(double zEnd, double zField) +{ + // Extrapolate track parameters and covariances matrix to "zEnd" + // using quadratic track model + + if (getZ() == zEnd) { + return; // nothing to be done if same z + } + + // Compute track parameters + auto dZ = (zEnd - getZ()); + auto phi0 = getPhi(); + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + auto invtanl0 = 1.0 / getTanl(); + auto invqpt0 = getInvQPt(); + auto Hz = std::copysign(1, zField); + auto k = TMath::Abs(o2::constants::math::B2C * zField); + auto n = dZ * invtanl0; + auto m = n * invtanl0; + auto theta = -invqpt0 * dZ * k * invtanl0; + + // Extrapolate track parameters to "zEnd" + mParameters(0) += n * cosphi0 - 0.5 * n * theta * Hz * sinphi0; + mParameters(1) += n * sinphi0 + 0.5 * n * theta * Hz * cosphi0; + mParameters(2) += Hz * theta; + mZ = zEnd; + + // Calculate Jacobian + SMatrix55 jacob = ROOT::Math::SMatrixIdentity(); + jacob(0, 2) = -n * theta * 0.5 * Hz * cosphi0 - n * sinphi0; + jacob(0, 3) = Hz * m * theta * sinphi0 - m * cosphi0; + jacob(0, 4) = k * m * 0.5 * Hz * dZ * sinphi0; + jacob(1, 2) = -n * theta * 0.5 * Hz * sinphi0 + n * cosphi0; + jacob(1, 3) = -Hz * m * theta * cosphi0 - m * sinphi0; + jacob(1, 4) = -k * m * 0.5 * Hz * dZ * cosphi0; + jacob(2, 3) = -Hz * theta * invtanl0; + jacob(2, 4) = -Hz * k * n; + + // Extrapolate track parameter covariances to "zEnd" + setCovariances(ROOT::Math::Similarity(jacob, mCovariances)); +} + +//__________________________________________________________________________ +void TrackParFwd::propagateParamToZhelix(double zEnd, double zField) +{ + // Track parameters extrapolated to the plane at "zEnd" + // using helix track model + + if (getZ() == zEnd) { + return; // nothing to be done if same z + } + + // Compute track parameters + auto dZ = (zEnd - getZ()); + auto phi0 = getPhi(); + auto tanl0 = getTanl(); + auto invtanl0 = 1.0 / tanl0; + auto invqpt0 = getInvQPt(); + auto qpt0 = 1.0 / invqpt0; + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + + auto k = TMath::Abs(o2::constants::math::B2C * zField); + auto invk = 1.0 / k; + auto theta = -invqpt0 * dZ * k * invtanl0; + auto [sintheta, costheta] = o2::math_utils::sincosd(theta); + auto Hz = std::copysign(1, zField); + auto Y = sinphi0 * qpt0 * invk; + auto X = cosphi0 * qpt0 * invk; + auto YC = Y * costheta; + auto YS = Y * sintheta; + auto XC = X * costheta; + auto XS = X * sintheta; + + // Extrapolate track parameters to "zEnd" + mParameters(0) += Hz * (Y - YC) - XS; + mParameters(1) += Hz * (-X + XC) - YS; + mParameters(2) += Hz * theta; + mZ = zEnd; +} + +//__________________________________________________________________________ +void TrackParCovFwd::propagateToZhelix(double zEnd, double zField) +{ + // Extrapolate track parameters and covariances matrix to "zEnd" + // using helix track model + + auto dZ = (zEnd - getZ()); + auto phi0 = getPhi(); + auto tanl0 = getTanl(); + auto invtanl0 = 1.0 / tanl0; + auto invqpt0 = getInvQPt(); + auto qpt0 = 1.0 / invqpt0; + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + auto k = TMath::Abs(o2::constants::math::B2C * zField); + auto invk = 1.0 / k; + auto theta = -invqpt0 * dZ * k * invtanl0; + auto [sintheta, costheta] = o2::math_utils::sincosd(theta); + auto Hz = std::copysign(1, zField); + auto L = qpt0 * qpt0 * invk; + auto N = dZ * invtanl0 * qpt0; + auto O = sintheta * cosphi0; + auto P = sinphi0 * costheta; + auto R = sinphi0 * sintheta; + auto S = cosphi0 * costheta; + auto Y = sinphi0 * qpt0 * invk; + auto X = cosphi0 * qpt0 * invk; + auto YC = Y * costheta; + auto YS = Y * sintheta; + auto XC = X * costheta; + auto XS = X * sintheta; + auto T = qpt0 * costheta; + auto U = qpt0 * sintheta; + auto V = qpt0; + auto n = dZ * invtanl0; + auto m = n * invtanl0; + + // Extrapolate track parameters to "zEnd" + mParameters(0) += Hz * (Y - YC) - XS; + mParameters(1) += Hz * (-X + XC) - YS; + mParameters(2) += Hz * theta; + mZ = zEnd; + + // Calculate Jacobian + SMatrix55 jacob = ROOT::Math::SMatrixIdentity(); + jacob(0, 2) = Hz * X - Hz * XC + YS; + jacob(0, 3) = Hz * R * m - S * m; + jacob(0, 4) = -Hz * N * R + Hz * T * Y - Hz * V * Y + N * S + U * X; + jacob(1, 2) = Hz * Y - Hz * YC - XS; + jacob(1, 3) = -Hz * O * m - P * m; + jacob(1, 4) = Hz * N * O - Hz * T * X + Hz * V * X + N * P + U * Y; + jacob(2, 3) = -Hz * theta * invtanl0; + jacob(2, 4) = -Hz * k * n; + + // Extrapolate track parameter covariances to "zEnd" + setCovariances(ROOT::Math::Similarity(jacob, mCovariances)); +} + +//__________________________________________________________________________ +bool TrackParCovFwd::update(const std::array<float, 2>& p, const std::array<float, 2>& cov) +{ + /// Kalman update step: computes new track parameters with a new cluster position and uncertainties + /// The current track is expected to have been propagated to the cluster z position + + using SVector2 = ROOT::Math::SVector<double, 2>; + using SMatrix22 = ROOT::Math::SMatrix<double, 2>; + using SMatrix25 = ROOT::Math::SMatrix<double, 2, 5>; + using SMatrix52 = ROOT::Math::SMatrix<double, 5, 2>; + using SMatrix55Std = ROOT::Math::SMatrix<double, 5>; + + SMatrix55 I = ROOT::Math::SMatrixIdentity(); + SMatrix25 H_k; + SMatrix22 V_k; + SVector2 m_k(p[0], p[1]), r_k_kminus1; + V_k(0, 0) = cov[0]; + V_k(1, 1) = cov[1]; + H_k(0, 0) = 1.0; + H_k(1, 1) = 1.0; + + // Covariance of residuals + SMatrix22 invResCov = (V_k + ROOT::Math::Similarity(H_k, mCovariances)); + invResCov.Invert(); + + // Kalman Gain Matrix + SMatrix52 K_k = mCovariances * ROOT::Math::Transpose(H_k) * invResCov; + + // Update Parameters + r_k_kminus1 = m_k - H_k * mParameters; // Residuals of prediction + mParameters = mParameters + K_k * r_k_kminus1; + + // Update covariances Matrix + SMatrix55Std updatedCov = (I - K_k * H_k) * mCovariances; + mCovariances(0, 0) = updatedCov(0, 0); + mCovariances(0, 1) = updatedCov(0, 1); + mCovariances(0, 2) = updatedCov(0, 2); + mCovariances(0, 3) = updatedCov(0, 3); + mCovariances(0, 4) = updatedCov(0, 4); + mCovariances(1, 1) = updatedCov(1, 1); + mCovariances(1, 2) = updatedCov(1, 2); + mCovariances(1, 3) = updatedCov(1, 3); + mCovariances(1, 4) = updatedCov(1, 4); + mCovariances(2, 2) = updatedCov(2, 2); + mCovariances(2, 3) = updatedCov(2, 3); + mCovariances(2, 4) = updatedCov(2, 4); + mCovariances(3, 3) = updatedCov(3, 3); + mCovariances(3, 4) = updatedCov(3, 4); + mCovariances(4, 4) = updatedCov(4, 4); + + auto addChi2Track = ROOT::Math::Similarity(r_k_kminus1, invResCov); + mTrackChi2 += addChi2Track; + + return true; +} + +//__________________________________________________________________________ +void TrackParCovFwd::addMCSEffect(double dZ, double x_over_X0) +{ + /// Add multiple Coulomb scattering effects to the track parameter covariances. + /// * if (dZ > 0): MCS effects are evaluated with a linear propagation model. + /// * if (dZ <= 0): only angular MCS effects are evaluated as if dZ = 0. + /// * x_over_X0 is the fraction of the radiation lenght (x/X0). + /// * No energy loss correction. + /// * All scattering evaluated at the position of the first cluster. + + auto phi0 = getPhi(); + auto tanl0 = getTanl(); + auto invtanl0 = 1.0 / tanl0; + auto invqpt0 = getInvQPt(); + + auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); + + auto csclambda = TMath::Abs(TMath::Sqrt(1 + tanl0 * tanl0) * invtanl0); + auto pathLengthOverX0 = x_over_X0 * csclambda; // + + // Angular dispersion square of the track (variance) in a plane perpendicular to the trajectory + auto sigmathetasq = 0.0136 * getInverseMomentum(); + sigmathetasq *= sigmathetasq * pathLengthOverX0; + + // Get covariance matrix + SMatrix55 newParamCov(getCovariances()); + + if (dZ > 0) { + auto A = tanl0 * tanl0 + 1; + auto B = dZ * cosphi0 * invtanl0; + auto C = dZ * sinphi0 * invtanl0; + auto D = A * B * invtanl0; + auto E = -A * C * invtanl0; + auto F = -C - D; + auto G = B + E; + auto H = -invqpt0 * tanl0; + + newParamCov(0, 0) += sigmathetasq * F * F; + + newParamCov(0, 1) += sigmathetasq * F * G; + + newParamCov(1, 1) += sigmathetasq * G * G; + + newParamCov(2, 0) += sigmathetasq * F; + + newParamCov(2, 1) += sigmathetasq * G; + + newParamCov(2, 2) += sigmathetasq; + + newParamCov(3, 0) += sigmathetasq * A * F; + + newParamCov(3, 1) += sigmathetasq * A * G; + + newParamCov(3, 2) += sigmathetasq * A; + + newParamCov(3, 3) += sigmathetasq * A * A; + + newParamCov(4, 0) += sigmathetasq * F * H; + + newParamCov(4, 1) += sigmathetasq * G * H; + + newParamCov(4, 2) += sigmathetasq * H; + + newParamCov(4, 3) += sigmathetasq * A * H; + + newParamCov(4, 4) += sigmathetasq * tanl0 * tanl0 * invqpt0 * invqpt0; + } else { + + auto A = tanl0 * tanl0 + 1; + auto H = -invqpt0 * tanl0; + + newParamCov(2, 2) += sigmathetasq; + + newParamCov(3, 2) += sigmathetasq * A; + + newParamCov(3, 3) += sigmathetasq * A * A; + + newParamCov(4, 2) += sigmathetasq * H; + + newParamCov(4, 3) += sigmathetasq * A * H; + + newParamCov(4, 4) += sigmathetasq * tanl0 * tanl0 * invqpt0 * invqpt0; + } + + // Set new covariances + setCovariances(newParamCov); +} + +} // namespace track +} // namespace o2 diff --git a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx index bbb4461c47a26..3f7b39624ab8b 100644 --- a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx +++ b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx @@ -10,22 +10,25 @@ #include "ReconstructionDataFormats/TrackLTIntegral.h" #include "CommonConstants/PhysicsConstants.h" -#include <cmath> +#include "GPUCommonMath.h" using namespace o2::track; +using namespace o2::gpu; //_____________________________________________________ -void TrackLTIntegral::print() const +GPUd() void TrackLTIntegral::print() const { +#ifndef GPUCA_GPUCODE_DEVICE printf("L(cm): %6.2f, X2X0: %5.3f TOF(ps): ", getL(), getX2X0()); for (int i = 0; i < getNTOFs(); i++) { printf(" %7.1f |", getTOF(i)); } printf("\n"); +#endif } //_____________________________________________________ -void TrackLTIntegral::addStep(float dL, const TrackPar& track) +GPUd() void TrackLTIntegral::addStep(float dL, const TrackPar& track) { ///< add step in cm to integrals mL += dL; @@ -33,7 +36,7 @@ void TrackLTIntegral::addStep(float dL, const TrackPar& track) float dTns = dL * 1000.f / o2::constants::physics::LightSpeedCm2NS; // time change in ps for beta = 1 particle for (int id = 0; id < getNTOFs(); id++) { float m2z = o2::track::PID::getMass2Z(id); - float betaInv = std::sqrt(1.f + m2z * m2z * p2); + float betaInv = CAMath::Sqrt(1.f + m2z * m2z * p2); mT[id] += dTns * betaInv; } } diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx new file mode 100644 index 0000000000000..041491b74c12a --- /dev/null +++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx @@ -0,0 +1,741 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackParametrization.cxx +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 1, 2020 +/// @brief + +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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" +#include <MathUtils/Cartesian.h> +#include <GPUCommonLogger.h> + +#ifndef GPUCA_GPUCODE_DEVICE +#include <iostream> +#endif + +#ifndef GPUCA_ALIGPUCODE +#include <fmt/printf.h> +#endif + +using namespace o2::gpu; +using namespace o2::track; + +//______________________________________________________________ +template <typename value_T> +GPUd() TrackParametrization<value_T>::TrackParametrization(const dim3_t& xyz, const dim3_t& pxpypz, int charge, bool sectorAlpha) + : mX{0.f}, mAlpha{0.f}, mP{0.f} +{ + // construct track param from kinematics + + // Alpha of the frame is defined as: + // sectorAlpha == false : -> angle of pt direction + // sectorAlpha == true : -> angle of the sector from X,Y coordinate for r>1 + // angle of pt direction for r==0 + // + // + constexpr value_t kSafe = 1e-5; + value_t radPos2 = xyz[0] * xyz[0] + xyz[1] * xyz[1]; + value_t alp = 0; + if (sectorAlpha || radPos2 < 1) { + alp = gpu::CAMath::ATan2(pxpypz[1], pxpypz[0]); + } else { + alp = gpu::CAMath::ATan2(xyz[1], xyz[0]); + } + if (sectorAlpha) { + alp = math_utils::detail::angle2Alpha<value_t>(alp); + } + // + value_t sn, cs; + math_utils::detail::sincos(alp, sn, cs); + // protection: avoid alpha being too close to 0 or +-pi/2 + if (gpu::CAMath::Abs(sn) < 2 * kSafe) { + if (alp > 0) { + alp += alp < constants::math::PIHalf ? 2 * kSafe : -2 * kSafe; + } else { + alp += alp > -constants::math::PIHalf ? -2 * kSafe : 2 * kSafe; + } + math_utils::detail::sincos(alp, sn, cs); + } else if (gpu::CAMath::Abs(cs) < 2 * kSafe) { + if (alp > 0) { + alp += alp > constants::math::PIHalf ? 2 * kSafe : -2 * kSafe; + } else { + alp += alp > -constants::math::PIHalf ? 2 * kSafe : -2 * kSafe; + } + math_utils::detail::sincos(alp, sn, cs); + } + // get the vertex of origin and the momentum + dim3_t ver{xyz[0], xyz[1], xyz[2]}; + dim3_t mom{pxpypz[0], pxpypz[1], pxpypz[2]}; + // + // Rotate to the local coordinate system + math_utils::detail::rotateZ<value_t>(ver, -alp); + math_utils::detail::rotateZ<value_t>(mom, -alp); + // + value_t ptI = 1.f / sqrt(mom[0] * mom[0] + mom[1] * mom[1]); + mX = ver[0]; + mAlpha = alp; + mP[kY] = ver[1]; + mP[kZ] = ver[2]; + mP[kSnp] = mom[1] * ptI; + mP[kTgl] = mom[2] * ptI; + mAbsCharge = gpu::CAMath::Abs(charge); + mP[kQ2Pt] = charge ? ptI * charge : ptI; + // + if (gpu::CAMath::Abs(1 - getSnp()) < kSafe) { + mP[kSnp] = 1.f - kSafe; // Protection + } else if (gpu::CAMath::Abs(-1 - getSnp()) < kSafe) { + mP[kSnp] = -1.f + kSafe; // Protection + } + // +} + +//_______________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::getPxPyPzGlo(dim3_t& pxyz) const +{ + // track momentum + if (gpu::CAMath::Abs(getQ2Pt()) < constants::math::Almost0 || gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) { + return false; + } + value_t cs, sn, pt = getPt(); + value_t r = gpu::CAMath::Sqrt((1.f - getSnp()) * (1.f + getSnp())); + math_utils::detail::sincos(getAlpha(), sn, cs); + pxyz[0] = pt * (r * cs - getSnp() * sn); + pxyz[1] = pt * (getSnp() * cs + r * sn); + pxyz[2] = pt * getTgl(); + return true; +} + +//____________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::getPosDirGlo(gpu::gpustd::array<value_t, 9>& 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 snp = getSnp(); + if (ptI < constants::math::Almost0 || gpu::CAMath::Abs(snp) > constants::math::Almost1) { + return false; + } + value_t &sn = posdirp[7], &cs = posdirp[8]; + value_t csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)); + value_t cstht = gpu::CAMath::Sqrt(1.f + getTgl() * getTgl()); + value_t csthti = 1.f / cstht; + math_utils::detail::sincos(getAlpha(), sn, cs); + posdirp[0] = getX() * cs - getY() * sn; + posdirp[1] = getX() * sn + getY() * cs; + posdirp[2] = getZ(); + posdirp[3] = (csp * cs - snp * sn) * csthti; // px/p + posdirp[4] = (snp * cs + csp * sn) * csthti; // py/p + posdirp[5] = getTgl() * csthti; // pz/p + posdirp[6] = cstht / ptI; // p + return true; +} + +//______________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::rotateParam(value_t alpha) +{ + // rotate to alpha frame + if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) { + LOGP(WARNING, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", getSnp()); + return false; + } + // + math_utils::detail::bringToPMPi<value_t>(alpha); + // + value_t ca = 0, sa = 0; + 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(WARNING, "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 <typename value_T> +GPUd() bool TrackParametrization<value_T>::propagateParamTo(value_t xk, const dim3_t& b) +{ + //---------------------------------------------------------------- + // Extrapolate this track params (w/o cov matrix) 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 - 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(getY()) > 1e5 || gpu::CAMath::Abs(getZ()) > 1e5) { + LOGP(WARNING, "Anomalous track, target X:{:f}", xk); + // print(); + return false; + } + value_t crv = getCurvature(b[2]); + value_t x2r = crv * dx; + value_t f1 = getSnp(), f2 = f1 + x2r; + if (gpu::CAMath::Abs(f1) > constants::math::Almost1 || gpu::CAMath::Abs(f2) > constants::math::Almost1) { + return false; + } + value_t r1 = gpu::CAMath::Sqrt((1.f - f1) * (1.f + f1)); + if (gpu::CAMath::Abs(r1) < constants::math::Almost0) { + return false; + } + value_t r2 = gpu::CAMath::Sqrt((1.f - f2) * (1.f + f2)); + if (gpu::CAMath::Abs(r2) < constants::math::Almost0) { + return false; + } + value_t dy2dx = (f1 + f2) / (r1 + r2); + value_t step = (gpu::CAMath::Abs(x2r) < 0.05f) ? dx * gpu::CAMath::Abs(r2 + f2 * dy2dx) // chord + : 2.f * CAMath::ASin(0.5f * dx * gpu::CAMath::Sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc + 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<value_t, 9> vecLab{0.f}; + if (!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.f, sintet = 0.f; + if (bb > constants::math::Almost0) { + costet = b[2] / bb; + sintet = bt / bb; + } + gpu::gpustd::array<value_t, 7> 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 = 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(dx) > 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; + } + + // Calculate the track parameters + t = 1.f / gpu::CAMath::Sqrt(vecLab[3] * vecLab[3] + vecLab[4] * vecLab[4]); + mX = x; + mP[kY] = y; + mP[kZ] = z; + mP[kSnp] = vecLab[4] * t; + mP[kTgl] = vecLab[5] * t; + mP[kQ2Pt] = q * t / vecLab[6]; + + return true; +} + +//____________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::propagateParamTo(value_t xk, value_t b) +{ + //---------------------------------------------------------------- + // propagate this track to the plane X=xk (cm) in the field "b" (kG) + // Only parameters are propagated, not the matrix. To be used for small + // distances only (<mm, i.e. misalignment) + //---------------------------------------------------------------- + value_t dx = xk - getX(); + if (gpu::CAMath::Abs(dx) < constants::math::Almost0) { + return true; + } + value_t crv = (gpu::CAMath::Abs(b) < constants::math::Almost0) ? 0.f : getCurvature(b); + value_t x2r = crv * dx; + value_t f1 = getSnp(), f2 = f1 + x2r; + if ((gpu::CAMath::Abs(f1) > constants::math::Almost1) || (gpu::CAMath::Abs(f2) > constants::math::Almost1)) { + return false; + } + value_t r1 = gpu::CAMath::Sqrt((1.f - f1) * (1.f + f1)); + if (gpu::CAMath::Abs(r1) < constants::math::Almost0) { + return false; + } + value_t r2 = gpu::CAMath::Sqrt((1.f - f2) * (1.f + f2)); + if (gpu::CAMath::Abs(r2) < constants::math::Almost0) { + return false; + } + mX = xk; + double dy2dx = (f1 + f2) / (r1 + r2); + mP[kY] += dx * dy2dx; + mP[kSnp] += x2r; + if (gpu::CAMath::Abs(x2r) < 0.05f) { + mP[kZ] += dx * (r2 + f2 * dy2dx) * getTgl(); + } else { + // for small dx/R the linear apporximation of the arc by the segment is OK, + // but at large dx/R the error is very large and leads to incorrect Z propagation + // angle traversed delta = 2*asin(dist_start_end / R / 2), hence the arc is: R*deltaPhi + // The dist_start_end is obtained from sqrt(dx^2+dy^2) = x/(r1+r2)*sqrt(2+f1*f2+r1*r2) + // double chord = dx*TMath::Sqrt(1+dy2dx*dy2dx); // distance from old position to new one + // double rot = 2*TMath::ASin(0.5*chord*crv); // angular difference seen from the circle center + // track1 += rot/crv*track3; + // + value_t rot = CAMath::ASin(r1 * f2 - r2 * f1); // more economic version from Yura. + if (f1 * f1 + f2 * f2 > 1.f && f1 * f2 < 0.f) { // special cases of large rotations or large abs angles + if (f2 > 0.f) { + rot = constants::math::PI - rot; // + } else { + rot = -constants::math::PI - rot; + } + } + mP[kZ] += getTgl() / crv * rot; + } + return true; +} + +//_______________________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::propagateParamToDCA(const math_utils::Point3D<value_t>& vtx, value_t b, dim2_t* dca, value_t maxD) +{ + // propagate track to DCA to the vertex + value_t sn, cs, alp = getAlpha(); + math_utils::detail::sincos(alp, sn, cs); + value_t x = getX(), y = getY(), snp = getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)); + value_t xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z(); + x -= xv; + y -= yv; + //Estimate the impact parameter neglecting the track curvature + value_t d = gpu::CAMath::Abs(x * snp - y * csp); + if (d > maxD) { + return false; + } + value_t crv = getCurvature(b); + value_t tgfv = -(crv * x - snp) / (crv * y + csp); + sn = tgfv / gpu::CAMath::Sqrt(1.f + tgfv * tgfv); + cs = gpu::CAMath::Sqrt((1.f - sn) * (1.f + sn)); + cs = (gpu::CAMath::Abs(tgfv) > constants::math::Almost0) ? sn / tgfv : constants::math::Almost1; + + x = xv * cs + yv * sn; + yv = -xv * sn + yv * cs; + xv = x; + + auto tmpT(*this); // operate on the copy to recover after the failure + alp += gpu::CAMath::ASin(sn); + if (!tmpT.rotateParam(alp) || !tmpT.propagateParamTo(xv, b)) { + LOG(WARNING) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " + << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z() << " | Track is: "; + tmpT.printParam(); + return false; + } + *this = tmpT; + if (dca) { + (*dca)[0] = getY() - yv; + (*dca)[1] = getZ() - zv; + } + return true; +} + +//____________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::getYZAt(value_t xk, value_t b, value_t& y, value_t& z) const +{ + //---------------------------------------------------------------- + // estimate Y,Z in tracking frame at given X + //---------------------------------------------------------------- + value_t dx = xk - getX(); + if (gpu::CAMath::Abs(dx) < constants::math::Almost0) { + return true; + } + value_t crv = getCurvature(b); + value_t x2r = crv * dx; + value_t f1 = getSnp(), f2 = f1 + x2r; + if ((gpu::CAMath::Abs(f1) > constants::math::Almost1) || (gpu::CAMath::Abs(f2) > constants::math::Almost1)) { + return false; + } + value_t r1 = gpu::CAMath::Sqrt((1.f - f1) * (1.f + f1)); + if (gpu::CAMath::Abs(r1) < constants::math::Almost0) { + return false; + } + value_t r2 = gpu::CAMath::Sqrt((1.f - f2) * (1.f + f2)); + if (gpu::CAMath::Abs(r2) < constants::math::Almost0) { + return false; + } + double dy2dx = (f1 + f2) / (r1 + r2); + y = mP[kY] + dx * dy2dx; + z = mP[kZ]; + if (gpu::CAMath::Abs(x2r) < 0.05f) { + z += dx * (r2 + f2 * dy2dx) * getTgl(); + } else { + // for small dx/R the linear apporximation of the arc by the segment is OK, + // but at large dx/R the error is very large and leads to incorrect Z propagation + // angle traversed delta = 2*asin(dist_start_end / R / 2), hence the arc is: R*deltaPhi + // The dist_start_end is obtained from sqrt(dx^2+dy^2) = x/(r1+r2)*sqrt(2+f1*f2+r1*r2) + // double chord = dx*TMath::Sqrt(1+dy2dx*dy2dx); // distance from old position to new one + // double rot = 2*TMath::ASin(0.5*chord*crv); // angular difference seen from the circle center + // track1 += rot/crv*track3; + // + value_t rot = CAMath::ASin(r1 * f2 - r2 * f1); // more economic version from Yura. + if (f1 * f1 + f2 * f2 > 1.f && f1 * f2 < 0.f) { // special cases of large rotations or large abs angles + if (f2 > 0.f) { + rot = constants::math::PI - rot; // + } else { + rot = -constants::math::PI - rot; + } + } + z += getTgl() / crv * rot; + } + return true; +} + +//______________________________________________________________ +template <typename value_T> +GPUd() void TrackParametrization<value_T>::invertParam() +{ + // Transform this track to the local coord. system rotated by 180 deg. + mX = -mX; + mAlpha += constants::math::PI; + math_utils::detail::bringToPMPi<value_t>(mAlpha); + // + mP[0] = -mP[0]; + mP[3] = -mP[3]; + mP[4] = -mP[4]; + // +} + +//______________________________________________________________ +template <typename value_T> +GPUd() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getZAt(value_t xk, value_t b) const +{ + ///< this method is just an alias for obtaining Z @ X in the tree->Draw() + value_t y, z; + return getYZAt(xk, b, y, z) ? z : -9999.f; +} + +//______________________________________________________________ +template <typename value_T> +GPUd() typename TrackParametrization<value_T>::value_t TrackParametrization<value_T>::getYAt(value_t xk, value_t b) const +{ + ///< this method is just an alias for obtaining Z @ X in the tree->Draw() + value_t y, z; + return getYZAt(xk, b, y, z) ? y : -9999.f; +} + +#ifndef GPUCA_ALIGPUCODE +//_____________________________________________________________ +template <typename value_T> +std::string TrackParametrization<value_T>::asString() const +{ + // print parameters as string + 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()); +} +#endif + +//______________________________________________________________ +template <typename value_T> +GPUd() void TrackParametrization<value_T>::printParam() const +{ + // print parameters +#ifndef GPUCA_ALIGPUCODE + printf("%s\n", asString().c_str()); +#endif +} + +//______________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::getXatLabR(value_t r, value_t& x, value_t bz, track::DirType dir) const +{ + // Get local X of the track position estimated at the radius lab radius r. + // The track curvature is accounted exactly + // + // The flag "dir" can be used to remove the ambiguity of which intersection to take (out of 2 possible) + // DirAuto (==0) - take the intersection closest to the current track position + // DirOutward (==1) - go along the track (increasing mX) + // DirInward (==-1) - go backward (decreasing mX) + // + const auto fy = mP[0], sn = mP[2]; + const value_t kEps = 1.e-6; + // + auto crv = getCurvature(bz); + if (gpu::CAMath::Abs(crv) > constants::math::Almost0) { // helix + // get center of the track circle + math_utils::CircleXY<value_t> circle; + getCircleParamsLoc(bz, circle); + 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; + 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); + } + value_t det = (1.f - g) * (1.f + g); + if (det < 0.f) { + return false; // does not reach raduis r + } + det = gpu::CAMath::Sqrt(det); + // + // the intersection happens in 2 points: {circle.xC+tR*C,circle.yC+tR*S} + // with C=f*c0+-|s0|*det and S=f*s0-+c0 sign(s0)*det + // 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; + 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); + 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; + } 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 + 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 += dfx + dfx; + value_t dxm = x - mX; + if (dxm > 0.f) { + return true; + } else if (dxm < -kEps) { + return false; + } + 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 + 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 -= dfx + dfx; + value_t dxm = x - mX; + if (dxm < 0.f) { + return true; + } + if (dxm > kEps) { + return false; + } + x = mX; // don't move + } + } else { // special case: track touching the circle just in 1 point + if ((dir == DirOutward && x < mX) || (dir == DirInward && x > mX)) { + 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 + } + } + } 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 (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 { // 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 + } + 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 increasing 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; +} + +//______________________________________________ +template <typename value_T> +GPUd() bool TrackParametrization<value_T>::correctForELoss(value_t xrho, bool anglecorr, value_t dedx) +{ + //------------------------------------------------------------------ + // This function corrects the track parameters for the energy loss in crossed material. + // "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 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; + } + } + + 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! + } + value_t eupd = e + dE; + value_t pupd2 = eupd * eupd - getPID().getMass2(); + if (pupd2 < kMinP * kMinP) { + return false; + } + setQ2Pt(getQ2Pt() * p / gpu::CAMath::Sqrt(pupd2)); + } + + return true; +} + +namespace o2::track +{ +template class TrackParametrization<float>; +#ifndef GPUCA_GPUCODE_DEVICE +template class TrackParametrization<double>; +#endif +} // namespace o2::track diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx new file mode 100644 index 0000000000000..3afb44d38edf6 --- /dev/null +++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx @@ -0,0 +1,1106 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackparametrizationWithError.cxx +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since Oct 1, 2020 +/// @brief + +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/Vertex.h" +#include "ReconstructionDataFormats/DCA.h" +#include <GPUCommonLogger.h> + +#ifndef GPUCA_GPUCODE_DEVICE +#include <iostream> +#include "Math/SMatrix.h" +#endif + +#ifndef GPUCA_ALIGPUCODE +#include <fmt/printf.h> +#endif + +using namespace o2::track; +using namespace o2::gpu; + +//______________________________________________________________ +template <typename value_T> +GPUd() void TrackParametrizationWithError<value_T>::invert() +{ + // Transform this track to the local coord. system rotated by 180 deg. + this->invertParam(); + // since the fP1 and fP2 are not inverted, their covariances with others change sign + mC[kSigZY] = -mC[kSigZY]; + mC[kSigSnpY] = -mC[kSigSnpY]; + mC[kSigTglZ] = -mC[kSigTglZ]; + mC[kSigTglSnp] = -mC[kSigTglSnp]; + mC[kSigQ2PtZ] = -mC[kSigQ2PtZ]; + mC[kSigQ2PtSnp] = -mC[kSigQ2PtSnp]; +} + +//______________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::propagateTo(value_t xk, value_t b) +{ + //---------------------------------------------------------------- + // propagate this track to the plane X=xk (cm) in the field "b" (kG) + //---------------------------------------------------------------- + value_t dx = xk - this->getX(); + if (gpu::CAMath::Abs(dx) < constants::math::Almost0) { + return true; + } + value_t crv = this->getCurvature(b); + 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)) { + return false; + } + value_t r1 = gpu::CAMath::Sqrt((1.f - f1) * (1.f + f1)); + if (gpu::CAMath::Abs(r1) < constants::math::Almost0) { + return false; + } + value_t r2 = gpu::CAMath::Sqrt((1.f - f2) * (1.f + f2)); + if (gpu::CAMath::Abs(r2) < constants::math::Almost0) { + return false; + } + this->setX(xk); + double dy2dx = (f1 + f2) / (r1 + r2); + value_t dP[kNParams] = {0.f}; + dP[kY] = dx * dy2dx; + dP[kSnp] = x2r; + if (gpu::CAMath::Abs(x2r) < 0.05f) { + dP[kZ] = dx * (r2 + f2 * dy2dx) * this->getTgl(); + } else { + // for small dx/R the linear apporximation of the arc by the segment is OK, + // but at large dx/R the error is very large and leads to incorrect Z propagation + // angle traversed delta = 2*asin(dist_start_end / R / 2), hence the arc is: R*deltaPhi + // The dist_start_end is obtained from sqrt(dx^2+dy^2) = x/(r1+r2)*sqrt(2+f1*f2+r1*r2) + // double chord = dx*TMath::Sqrt(1+dy2dx*dy2dx); // distance from old position to new one + // double rot = 2*TMath::ASin(0.5*chord*crv); // angular difference seen from the circle center + // mP1 += rot/crv*mP3; + // + value_t rot = gpu::CAMath::ASin(r1 * f2 - r2 * f1); // more economic version from Yura. + if (f1 * f1 + f2 * f2 > 1.f && f1 * f2 < 0.f) { // special cases of large rotations or large abs angles + if (f2 > 0.f) { + rot = constants::math::PI - rot; // + } else { + rot = -constants::math::PI - rot; + } + } + dP[kZ] = this->getTgl() / crv * rot; + } + + this->updateParams(dP); // apply corrections + + 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]; + + // 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; + + // 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 <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::rotate(value_t alpha) +{ + // rotate to alpha frame + if (gpu::CAMath::Abs(this->getSnp()) > constants::math::Almost1) { + LOGP(WARNING, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", this->getSnp()); + return false; + } + // + math_utils::detail::bringToPMPi<value_t>(alpha); + // + value_t ca = 0, sa = 0; + math_utils::detail::sincos(alpha - this->getAlpha(), sa, ca); + value_t snp = this->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) { + //LOGP(WARNING,"Rotation failed: local cos(phi) would become {:.2f}", csp * ca + snp * sa); + return false; + } + // + + value_t updSnp = snp * ca - csp * sa; + if (gpu::CAMath::Abs(updSnp) > constants::math::Almost1) { + LOGP(WARNING, "Rotation failed: new snp {:.2f}", updSnp); + return false; + } + value_t xold = this->getX(), yold = this->getY(); + this->setAlpha(alpha); + this->setX(xold * ca + yold * sa); + this->setY(-xold * sa + yold * ca); + this->setSnp(updSnp); + + if (gpu::CAMath::Abs(csp) < constants::math::Almost0) { + LOGP(WARNING, "Too small cosine value {:f}", csp); + csp = constants::math::Almost0; + } + + value_t rr = (ca + snp / csp * sa); + + 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; + + checkCovariance(); + return true; +} + +//_______________________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t b, o2::dataformats::DCA* dca, value_t maxD) +{ + // propagate track to DCA to the vertex + value_t sn, cs, alp = this->getAlpha(); + o2::math_utils::detail::sincos(alp, sn, cs); + value_t x = this->getX(), y = this->getY(), snp = this->getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)); + value_t xv = vtx.getX() * cs + vtx.getY() * sn, yv = -vtx.getX() * sn + vtx.getY() * cs, zv = vtx.getZ(); + x -= xv; + y -= yv; + //Estimate the impact parameter neglecting the track curvature + value_t d = gpu::CAMath::Abs(x * snp - y * csp); + if (d > maxD) { + return false; + } + value_t crv = this->getCurvature(b); + value_t tgfv = -(crv * x - snp) / (crv * y + csp); + sn = tgfv / gpu::CAMath::Sqrt(1.f + tgfv * tgfv); + cs = gpu::CAMath::Sqrt((1.f - sn) * (1.f + sn)); + cs = (gpu::CAMath::Abs(tgfv) > constants::math::Almost0) ? sn / tgfv : constants::math::Almost1; + + x = xv * cs + yv * sn; + yv = -xv * sn + yv * cs; + xv = x; + + auto tmpT(*this); // operate on the copy to recover after the failure + alp += gpu::CAMath::ASin(sn); + if (!tmpT.rotate(alp) || !tmpT.propagateTo(xv, b)) { + LOG(WARNING) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: "; + tmpT.print(); + return false; + } + *this = tmpT; + if (dca) { + o2::math_utils::detail::sincos(alp, sn, cs); + auto s2ylocvtx = vtx.getSigmaX2() * sn * sn + vtx.getSigmaY2() * cs * cs - 2. * vtx.getSigmaXY() * cs * sn; + dca->set(this->getY() - yv, this->getZ() - zv, getSigmaY2() + s2ylocvtx, getSigmaZY(), getSigmaZ2() + vtx.getSigmaZ2()); + } + return true; +} + +//______________________________________________________________ +template <typename value_T> +GPUd() TrackParametrizationWithError<value_T>::TrackParametrizationWithError(const dim3_t& xyz, const dim3_t& pxpypz, + const gpu::gpustd::array<value_t, kLabCovMatSize>& cv, int charge, bool sectorAlpha) +{ + // construct track param and covariance from kinematics and lab errors + + // Alpha of the frame is defined as: + // sectorAlpha == false : -> angle of pt direction + // sectorAlpha == true : -> angle of the sector from X,Y coordinate for r>1 + // angle of pt direction for r==0 + // + // + constexpr value_t kSafe = 1e-5f; + value_t radPos2 = xyz[0] * xyz[0] + xyz[1] * xyz[1]; + value_t alp = 0; + if (sectorAlpha || radPos2 < 1) { + alp = gpu::CAMath::ATan2(pxpypz[1], pxpypz[0]); + } else { + alp = gpu::CAMath::ATan2(xyz[1], xyz[0]); + } + if (sectorAlpha) { + alp = math_utils::detail::angle2Alpha<value_t>(alp); + } + // + value_t sn, cs; + math_utils::detail::sincos(alp, sn, cs); + // protection: avoid alpha being too close to 0 or +-pi/2 + if (gpu::CAMath::Abs(sn) < 2.f * kSafe) { + if (alp > 0) { + alp += alp < constants::math::PIHalf ? 2.f * kSafe : -2.f * kSafe; + } else { + alp += alp > -constants::math::PIHalf ? -2.f * kSafe : 2.f * kSafe; + } + math_utils::detail::sincos(alp, sn, cs); + } else if (gpu::CAMath::Abs(cs) < 2.f * kSafe) { + if (alp > 0) { + alp += alp > constants::math::PIHalf ? 2.f * kSafe : -2.f * kSafe; + } else { + alp += alp > -constants::math::PIHalf ? 2.f * kSafe : -2.f * kSafe; + } + math_utils::detail::sincos(alp, sn, cs); + } + // get the vertex of origin and the momentum + dim3_t ver{xyz[0], xyz[1], xyz[2]}; + dim3_t mom{pxpypz[0], pxpypz[1], pxpypz[2]}; + // + // Rotate to the local coordinate system + math_utils::detail::rotateZ<value_t>(ver, -alp); + math_utils::detail::rotateZ<value_t>(mom, -alp); + // + value_t pt = gpu::CAMath::Sqrt(mom[0] * mom[0] + mom[1] * mom[1]); + value_t ptI = 1.f / pt; + this->setX(ver[0]); + this->setAlpha(alp); + this->setY(ver[1]); + this->setZ(ver[2]); + this->setSnp(mom[1] * ptI); // cos(phi) + this->setTgl(mom[2] * ptI); // tg(lambda) + this->setAbsCharge(gpu::CAMath::Abs(charge)); + this->setQ2Pt(charge ? ptI * charge : ptI); + // + if (gpu::CAMath::Abs(1.f - this->getSnp()) < kSafe) { + this->setSnp(1.f - kSafe); // Protection + } else if (gpu::CAMath::Abs(-1.f - this->getSnp()) < kSafe) { + 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 + } + // + 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; + } + // + 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(); +} + +//____________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::propagateTo(value_t xk, 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) { + LOGP(WARNING, "Anomalous track, target X:{:f}", xk); + // print(); + return false; + } + value_t crv = (gpu::CAMath::Abs(b[2]) < constants::math::Almost0) ? 0.f : this->getCurvature(b[2]); + 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)) { + return false; + } + value_t r1 = gpu::CAMath::Sqrt((1.f - f1) * (1.f + f1)); + if (gpu::CAMath::Abs(r1) < constants::math::Almost0) { + return false; + } + value_t r2 = gpu::CAMath::Sqrt((1.f - f2) * (1.f + f2)); + if (gpu::CAMath::Abs(r2) < constants::math::Almost0) { + return false; + } + + value_t dy2dx = (f1 + f2) / (r1 + r2); + 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<value_t, 9> vecLab{0.f}; + if (!this->getPosDirGlo(vecLab)) { + return false; + } + // + // 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]; + // 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; + + // 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(); + + // 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; + } + gpu::gpustd::array<value_t, 7> 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 sgn = this->getSign(); + g3helx3(sgn * 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(dx) > 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; + } + + // Calculate the track parameters + t = 1.f / gpu::CAMath::Sqrt(vecLab[3] * vecLab[3] + vecLab[4] * vecLab[4]); + this->setX(x); + this->setY(y); + this->setZ(z); + this->setSnp(vecLab[4] * t); + this->setTgl(vecLab[5] * t); + this->setQ2Pt(sgn * t / vecLab[6]); + + return true; +} + +//______________________________________________ +template <typename value_T> +GPUd() void TrackParametrizationWithError<value_T>::checkCovariance() +{ + // This function forces the diagonal elements of the covariance matrix to be positive. + // In case the diagonal element is bigger than the maximal allowed value, it is set to + // the limit and the off-diagonal elements that correspond to it are set to zero. + + mC[kSigY2] = gpu::CAMath::Abs(mC[kSigY2]); + if (mC[kSigY2] > kCY2max) { + value_t scl = gpu::CAMath::Sqrt(kCY2max / mC[kSigY2]); + mC[kSigY2] = kCY2max; + mC[kSigZY] *= scl; + mC[kSigSnpY] *= scl; + mC[kSigTglY] *= scl; + mC[kSigQ2PtY] *= scl; + } + mC[kSigZ2] = gpu::CAMath::Abs(mC[kSigZ2]); + if (mC[kSigZ2] > kCZ2max) { + value_t scl = gpu::CAMath::Sqrt(kCZ2max / mC[kSigZ2]); + mC[kSigZ2] = kCZ2max; + mC[kSigZY] *= scl; + mC[kSigSnpZ] *= scl; + mC[kSigTglZ] *= scl; + mC[kSigQ2PtZ] *= scl; + } + mC[kSigSnp2] = gpu::CAMath::Abs(mC[kSigSnp2]); + if (mC[kSigSnp2] > kCSnp2max) { + value_t scl = gpu::CAMath::Sqrt(kCSnp2max / mC[kSigSnp2]); + mC[kSigSnp2] = kCSnp2max; + mC[kSigSnpY] *= scl; + mC[kSigSnpZ] *= scl; + mC[kSigTglSnp] *= scl; + mC[kSigQ2PtSnp] *= scl; + } + mC[kSigTgl2] = gpu::CAMath::Abs(mC[kSigTgl2]); + if (mC[kSigTgl2] > kCTgl2max) { + value_t scl = gpu::CAMath::Sqrt(kCTgl2max / mC[kSigTgl2]); + mC[kSigTgl2] = kCTgl2max; + mC[kSigTglY] *= scl; + mC[kSigTglZ] *= scl; + mC[kSigTglSnp] *= scl; + mC[kSigQ2PtTgl] *= scl; + } + mC[kSigQ2Pt2] = gpu::CAMath::Abs(mC[kSigQ2Pt2]); + if (mC[kSigQ2Pt2] > kC1Pt2max) { + value_t scl = gpu::CAMath::Sqrt(kC1Pt2max / mC[kSigQ2Pt2]); + mC[kSigQ2Pt2] = kC1Pt2max; + mC[kSigQ2PtY] *= scl; + mC[kSigQ2PtZ] *= scl; + mC[kSigQ2PtSnp] *= scl; + mC[kSigQ2PtTgl] *= scl; + } +} + +//______________________________________________ +template <typename value_T> +GPUd() void TrackParametrizationWithError<value_T>::resetCovariance(value_t s2) +{ + // Reset the covarince matrix to "something big" + double d0(kCY2max), d1(kCZ2max), d2(kCSnp2max), d3(kCTgl2max), d4(kC1Pt2max); + if (s2 > constants::math::Almost0) { + d0 = getSigmaY2() * s2; + d1 = getSigmaZ2() * s2; + d2 = getSigmaSnp2() * s2; + d3 = getSigmaTgl2() * s2; + d4 = getSigma1Pt2() * s2; + if (d0 > kCY2max) { + d0 = kCY2max; + } + if (d1 > kCZ2max) { + d1 = kCZ2max; + } + if (d2 > kCSnp2max) { + d2 = kCSnp2max; + } + if (d3 > kCTgl2max) { + d3 = kCTgl2max; + } + if (d4 > kC1Pt2max) { + d4 = kC1Pt2max; + } + } + for (int i = 0; i < kCovMatSize; i++) { + mC[i] = 0; + } + mC[kSigY2] = d0; + mC[kSigZ2] = d1; + mC[kSigSnp2] = d2; + mC[kSigTgl2] = d3; + mC[kSigQ2Pt2] = d4; +} + +//______________________________________________ +template <typename value_T> +GPUd() typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getPredictedChi2(const dim2_t& p, const dim3_t& cov) const +{ + // Estimate the chi2 of the space point "p" with the cov. matrix "cov" + auto sdd = static_cast<double>(getSigmaY2()) + static_cast<double>(cov[0]); + auto sdz = static_cast<double>(getSigmaZY()) + static_cast<double>(cov[1]); + auto szz = static_cast<double>(getSigmaZ2()) + static_cast<double>(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; +} + +#ifndef GPUCA_GPUCODE // Disable function relying on ROOT SMatrix on GPU + +//______________________________________________ +template <typename value_T> +void TrackParametrizationWithError<value_T>::buildCombinedCovMatrix(const TrackParametrizationWithError<value_T>& rhs, MatrixDSym5& cov) const +{ + // fill combined cov.matrix (NOT inverted) + cov(kY, kY) = static_cast<double>(getSigmaY2()) + static_cast<double>(rhs.getSigmaY2()); + cov(kZ, kY) = static_cast<double>(getSigmaZY()) + static_cast<double>(rhs.getSigmaZY()); + cov(kZ, kZ) = static_cast<double>(getSigmaZ2()) + static_cast<double>(rhs.getSigmaZ2()); + cov(kSnp, kY) = static_cast<double>(getSigmaSnpY()) + static_cast<double>(rhs.getSigmaSnpY()); + cov(kSnp, kZ) = static_cast<double>(getSigmaSnpZ()) + static_cast<double>(rhs.getSigmaSnpZ()); + cov(kSnp, kSnp) = static_cast<double>(getSigmaSnp2()) + static_cast<double>(rhs.getSigmaSnp2()); + cov(kTgl, kY) = static_cast<double>(getSigmaTglY()) + static_cast<double>(rhs.getSigmaTglY()); + cov(kTgl, kZ) = static_cast<double>(getSigmaTglZ()) + static_cast<double>(rhs.getSigmaTglZ()); + cov(kTgl, kSnp) = static_cast<double>(getSigmaTglSnp()) + static_cast<double>(rhs.getSigmaTglSnp()); + cov(kTgl, kTgl) = static_cast<double>(getSigmaTgl2()) + static_cast<double>(rhs.getSigmaTgl2()); + cov(kQ2Pt, kY) = static_cast<double>(getSigma1PtY()) + static_cast<double>(rhs.getSigma1PtY()); + cov(kQ2Pt, kZ) = static_cast<double>(getSigma1PtZ()) + static_cast<double>(rhs.getSigma1PtZ()); + cov(kQ2Pt, kSnp) = static_cast<double>(getSigma1PtSnp()) + static_cast<double>(rhs.getSigma1PtSnp()); + cov(kQ2Pt, kTgl) = static_cast<double>(getSigma1PtTgl()) + static_cast<double>(rhs.getSigma1PtTgl()); + cov(kQ2Pt, kQ2Pt) = static_cast<double>(getSigma1Pt2()) + static_cast<double>(rhs.getSigma1Pt2()); +} + +//______________________________________________ +template <typename value_T> +typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getPredictedChi2(const TrackParametrizationWithError<value_T>& rhs) const +{ + MatrixDSym5 cov; // perform matrix operations in double! + return getPredictedChi2(rhs, cov); +} + +//______________________________________________ +template <typename value_T> +typename TrackParametrizationWithError<value_T>::value_t TrackParametrizationWithError<value_T>::getPredictedChi2(const TrackParametrizationWithError<value_T>& rhs, MatrixDSym5& covToSet) const +{ + // 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) { + 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) { + LOG(ERROR) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX(); + return 2.f * HugeF; + } + buildCombinedCovMatrix(rhs, covToSet); + if (!covToSet.Invert()) { + LOG(WARNING) << "Cov.matrix inversion failed: " << covToSet; + return 2.f * HugeF; + } + double chi2diag = 0., chi2ndiag = 0., diff[kNParams]; + for (int i = kNParams; i--;) { + diff[i] = this->getParam(i) - rhs.getParam(i); + chi2diag += diff[i] * diff[i] * covToSet(i, i); + } + for (int i = kNParams; i--;) { + for (int j = i; j--;) { + chi2ndiag += diff[i] * diff[j] * covToSet(i, j); + } + } + return chi2diag + 2. * chi2ndiag; +} + +//______________________________________________ +template <typename value_T> +bool TrackParametrizationWithError<value_T>::update(const TrackParametrizationWithError<value_T>& 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) { + 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) { + LOG(ERROR) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX(); + return false; + } + + // gain matrix K = Cov0*H*(Cov0+Cov0)^-1 (for measurement matrix H=I) + MatrixDSym5 matC0; + matC0(kY, kY) = getSigmaY2(); + matC0(kZ, kY) = getSigmaZY(); + matC0(kZ, kZ) = getSigmaZ2(); + matC0(kSnp, kY) = getSigmaSnpY(); + matC0(kSnp, kZ) = getSigmaSnpZ(); + matC0(kSnp, kSnp) = getSigmaSnp2(); + matC0(kTgl, kY) = getSigmaTglY(); + matC0(kTgl, kZ) = getSigmaTglZ(); + matC0(kTgl, kSnp) = getSigmaTglSnp(); + matC0(kTgl, kTgl) = getSigmaTgl2(); + matC0(kQ2Pt, kY) = getSigma1PtY(); + matC0(kQ2Pt, kZ) = getSigma1PtZ(); + matC0(kQ2Pt, kSnp) = getSigma1PtSnp(); + matC0(kQ2Pt, kTgl) = getSigma1PtTgl(); + matC0(kQ2Pt, kQ2Pt) = getSigma1Pt2(); + MatrixD5 matK = matC0 * covInv; + + // updated state vector: x = K*(x1-x0) + // RS: why SMatix, SVector does not provide multiplication operators ??? + double diff[kNParams]; + for (int i = kNParams; i--;) { + diff[i] = rhs.getParam(i) - this->getParam(i); + } + for (int i = kNParams; i--;) { + for (int j = kNParams; j--;) { + this->updateParam(matK(i, j) * diff[j], i); + } + } + + // updated covariance: Cov0 = Cov0 - K*Cov0 + matK *= ROOT::Math::SMatrix<double, kNParams, kNParams, ROOT::Math::MatRepStd<double, kNParams>>(matC0); + mC[kSigY2] -= matK(kY, kY); + mC[kSigZY] -= matK(kZ, kY); + mC[kSigZ2] -= matK(kZ, kZ); + mC[kSigSnpY] -= matK(kSnp, kY); + mC[kSigSnpZ] -= matK(kSnp, kZ); + mC[kSigSnp2] -= matK(kSnp, kSnp); + mC[kSigTglY] -= matK(kTgl, kY); + mC[kSigTglZ] -= matK(kTgl, kZ); + mC[kSigTglSnp] -= matK(kTgl, kSnp); + mC[kSigTgl2] -= matK(kTgl, kTgl); + mC[kSigQ2PtY] -= matK(kQ2Pt, kY); + mC[kSigQ2PtZ] -= matK(kQ2Pt, kZ); + mC[kSigQ2PtSnp] -= matK(kQ2Pt, kSnp); + mC[kSigQ2PtTgl] -= matK(kQ2Pt, kTgl); + mC[kSigQ2Pt2] -= matK(kQ2Pt, kQ2Pt); + + return true; +} + +//______________________________________________ +template <typename value_T> +bool TrackParametrizationWithError<value_T>::update(const TrackParametrizationWithError<value_T>& rhs) +{ + // update track with other track + MatrixDSym5 covI; // perform matrix operations in double! + buildCombinedCovMatrix(rhs, covI); + if (!covI.Invert()) { + LOG(WARNING) << "Cov.matrix inversion failed: " << covI; + return false; + } + return update(rhs, covI); +} + +#endif + +//______________________________________________ +template <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::update(const dim2_t& p, const dim3_t& cov) +{ + // Update the track parameters with the space point "p" having + // the covariance matrix "cov" + + value_t &cm00 = mC[kSigY2], &cm10 = mC[kSigZY], &cm11 = mC[kSigZ2], &cm20 = mC[kSigSnpY], &cm21 = mC[kSigSnpZ], + &cm22 = mC[kSigSnp2], &cm30 = mC[kSigTglY], &cm31 = mC[kSigTglZ], &cm32 = mC[kSigTglSnp], &cm33 = mC[kSigTgl2], + &cm40 = mC[kSigQ2PtY], &cm41 = mC[kSigQ2PtZ], &cm42 = mC[kSigQ2PtSnp], &cm43 = mC[kSigQ2PtTgl], + &cm44 = mC[kSigQ2Pt2]; + + // use double precision? + double r00 = static_cast<double>(cov[0]) + static_cast<double>(cm00); + double r01 = static_cast<double>(cov[1]) + static_cast<double>(cm10); + double r11 = static_cast<double>(cov[2]) + static_cast<double>(cm11); + double det = r00 * r11 - r01 * r01; + + if (gpu::CAMath::Abs(det) < constants::math::Almost0) { + return false; + } + double detI = 1. / det; + double tmp = r00; + r00 = r11 * detI; + r11 = tmp * detI; + r01 = -r01 * detI; + + double k00 = cm00 * r00 + cm10 * r01, k01 = cm00 * r01 + cm10 * r11; + double k10 = cm10 * r00 + cm11 * r01, k11 = cm10 * r01 + cm11 * r11; + double k20 = cm20 * r00 + cm21 * r01, k21 = cm20 * r01 + cm21 * r11; + double k30 = cm30 * r00 + cm31 * r01, k31 = cm30 * r01 + cm31 * r11; + double k40 = cm40 * r00 + cm41 * r01, k41 = cm40 * r01 + cm41 * r11; + + value_t dy = p[kY] - this->getY(), dz = p[kZ] - this->getZ(); + value_t dsnp = k20 * dy + k21 * dz; + if (gpu::CAMath::Abs(this->getSnp() + dsnp) > constants::math::Almost1) { + return false; + } + + value_t dP[kNParams] = {value_t(k00 * dy + k01 * dz), value_t(k10 * dy + k11 * dz), dsnp, value_t(k30 * dy + k31 * dz), + value_t(k40 * dy + k41 * dz)}; + this->updateParams(dP); + + double c01 = cm10, c02 = cm20, c03 = cm30, c04 = cm40; + double c12 = cm21, c13 = cm31, c14 = cm41; + + cm00 -= k00 * cm00 + k01 * cm10; + cm10 -= k00 * c01 + k01 * cm11; + cm20 -= k00 * c02 + k01 * c12; + cm30 -= k00 * c03 + k01 * c13; + cm40 -= k00 * c04 + k01 * c14; + + cm11 -= k10 * c01 + k11 * cm11; + cm21 -= k10 * c02 + k11 * c12; + cm31 -= k10 * c03 + k11 * c13; + cm41 -= k10 * c04 + k11 * c14; + + cm22 -= k20 * c02 + k21 * c12; + cm32 -= k20 * c03 + k21 * c13; + cm42 -= k20 * c04 + k21 * c14; + + cm33 -= k30 * c03 + k31 * c13; + cm43 -= k30 * c04 + k31 * c14; + + cm44 -= k40 * c04 + k41 * c14; + + checkCovariance(); + + return true; +} + +//______________________________________________ +template <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr, value_t dedx) +{ + //------------------------------------------------------------------ + // This function corrects the 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 kMaxELossFrac = 0.3f; // max allowed fractional eloss + 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) { + 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; + + // Calculating the multiple scattering corrections****************** + 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(); + } + 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 + // 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(); + } + } + + 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! + } + value_t eupd = e + dE; + value_t pupd2 = eupd * eupd - this->getPID().getMass2(); + if (pupd2 < kMinP * kMinP) { + 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; + } + + // Applying the corrections***************************** + fC22 += cC22; + fC33 += cC33; + fC43 += cC43; + fC44 += cC44; + this->setQ2Pt(this->getQ2Pt() * cP4); + + checkCovariance(); + + return true; +} + +//______________________________________________________________ +template <typename value_T> +GPUd() bool TrackParametrizationWithError<value_T>::getCovXYZPxPyPzGlo(gpu::gpustd::array<value_t, kLabCovMatSize>& cv) const +{ + //--------------------------------------------------------------------- + // This function returns the global covariance matrix of the track 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] + // + // Results for (nearly) straight tracks are meaningless ! + //--------------------------------------------------------------------- + if (gpu::CAMath::Abs(this->getQ2Pt()) <= constants::math::Almost0 || gpu::CAMath::Abs(this->getSnp()) > constants::math::Almost1) { + for (int i = 0; i < 21; i++) { + cv[i] = 0.; + } + return false; + } + + auto pt = this->getPt(); + value_t sn, cs; + 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); + + return true; +} + +#ifndef GPUCA_ALIGPUCODE +//______________________________________________________________ +template <typename value_T> +std::string TrackParametrizationWithError<value_T>::asString() const +{ + return TrackParametrization<value_t>::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]); +} +#endif + +//______________________________________________________________ +template <typename value_T> +GPUd() void TrackParametrizationWithError<value_T>::print() const +{ + // print parameters +#ifndef GPUCA_ALIGPUCODE + printf("%s\n", asString().c_str()); +#endif +} + +namespace o2::track +{ +template class TrackParametrizationWithError<float>; +#ifndef GPUCA_GPUCODE_DEVICE +template class TrackParametrizationWithError<double>; +#endif +} // namespace o2::track diff --git a/DataFormats/Reconstruction/src/V0.cxx b/DataFormats/Reconstruction/src/V0.cxx new file mode 100644 index 0000000000000..0fee7300a103c --- /dev/null +++ b/DataFormats/Reconstruction/src/V0.cxx @@ -0,0 +1,28 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/V0.h" + +using namespace o2::dataformats; + +V0::V0(const std::array<float, 3>& xyz, const std::array<float, 3>& pxyz, + const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg, + GIndex trPosID, GIndex trNegID) + : o2::track::TrackParCov{xyz, pxyz, 0, false}, mProngIDs{trPosID, trNegID}, mProngs{trPos, trNeg} +{ +} + +float V0::calcMass2(float massPos2, float massNeg2) const +{ + auto p2 = getP2(); + auto p2pos = mProngs[0].getP2(), p2neg = mProngs[1].getP2(); + auto energy = std::sqrt(massPos2 + p2pos) + std::sqrt(massNeg2 + p2neg); + return energy * energy - p2; +} diff --git a/DataFormats/Reconstruction/src/Vertex.cxx b/DataFormats/Reconstruction/src/Vertex.cxx index c35c2e395c322..4c85cbdae24ca 100644 --- a/DataFormats/Reconstruction/src/Vertex.cxx +++ b/DataFormats/Reconstruction/src/Vertex.cxx @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #include "ReconstructionDataFormats/Vertex.h" +#include <fmt/printf.h> #include <iostream> namespace o2 @@ -16,15 +17,18 @@ namespace o2 namespace dataformats { -#ifndef ALIGPU_GPUCODE +#ifndef GPUCA_GPUCODE_DEVICE + +std::string VertexBase::asString() const +{ + return fmt::format("Vtx {{{:+.4e},{:+.4e},{:+.4e}}} Cov.:{{{{{:.3e}..}},{{{:.3e},{:.3e}..}},{{{:.3e},{:.3e},{:.3e}}}}}", + mPos.X(), mPos.Y(), mPos.Z(), mCov[0], mCov[1], mCov[2], mCov[3], mCov[4], mCov[5]); +} + std::ostream& operator<<(std::ostream& os, const o2::dataformats::VertexBase& v) { // stream itself - os << std::scientific << "Vertex X: " << v.getX() << " Y: " << v.getY() << " Z: " << v.getZ() - << " Cov.mat:\n" - << v.getSigmaX2() << '\n' - << v.getSigmaXY() << ' ' << v.getSigmaY2() << '\n' - << v.getSigmaXZ() << ' ' << v.getSigmaYZ() << ' ' << v.getSigmaZ2() << '\n'; + os << v.asString(); return os; } @@ -32,6 +36,7 @@ void VertexBase::print() const { std::cout << *this << std::endl; } + #endif } // namespace dataformats diff --git a/DataFormats/Reconstruction/src/VtxTrackIndex.cxx b/DataFormats/Reconstruction/src/VtxTrackIndex.cxx new file mode 100644 index 0000000000000..4c75f81d4cb1f --- /dev/null +++ b/DataFormats/Reconstruction/src/VtxTrackIndex.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VtxTrackIndex.h +/// \brief Index of track attached to vertx: index in its proper container, container source and flags +/// \author ruben.shahoyan@cern.ch + +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "Framework/Logger.h" +#include <fmt/printf.h> +#include <iostream> +#include <bitset> + +using namespace o2::dataformats; + +std::string VtxTrackIndex::asString() const +{ + std::bitset<NBitsFlags()> bits{getFlags()}; + return fmt::format("[{:d}/{:d}/{:s}]", getIndex(), getSource(), bits.to_string()); +} + +std::ostream& o2::dataformats::operator<<(std::ostream& os, const o2::dataformats::VtxTrackIndex& v) +{ + // stream itself + os << v.asString(); + return os; +} + +void VtxTrackIndex::print() const +{ + LOG(INFO) << asString(); +} diff --git a/DataFormats/Reconstruction/src/VtxTrackRef.cxx b/DataFormats/Reconstruction/src/VtxTrackRef.cxx new file mode 100644 index 0000000000000..d1b0d59dec0b9 --- /dev/null +++ b/DataFormats/Reconstruction/src/VtxTrackRef.cxx @@ -0,0 +1,42 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VtxTrackIndex.h +/// \brief Index of track attached to vertx: index in its proper container, container source and flags +/// \author ruben.shahoyan@cern.ch + +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "Framework/Logger.h" +#include <fmt/printf.h> +#include <iostream> +#include <bitset> + +using namespace o2::dataformats; + +std::string VtxTrackRef::asString() const +{ + std::string str = fmt::format("1st entry: {:d} ", getFirstEntry()); + for (int i = 0; i < VtxTrackIndex::NSources; i++) { + str += fmt::format(", N{:s} : {:d}", VtxTrackIndex::getSourceName(i), getEntriesOfSource(i)); + } + return str; +} + +std::ostream& o2::dataformats::operator<<(std::ostream& os, const o2::dataformats::VtxTrackRef& v) +{ + // stream itself + os << v.asString(); + return os; +} + +void VtxTrackRef::print() const +{ + LOG(INFO) << asString(); +} diff --git a/DataFormats/Reconstruction/test/testVertex.cxx b/DataFormats/Reconstruction/test/testVertex.cxx index d65b6adeb509e..60300c6c292e6 100644 --- a/DataFormats/Reconstruction/test/testVertex.cxx +++ b/DataFormats/Reconstruction/test/testVertex.cxx @@ -19,13 +19,13 @@ namespace o2 { -using myTS = o2::dataformats::TimeStampWithError<double, double>; +using myTS = o2::dataformats::TimeStampWithError<float, float>; using myVtx = o2::dataformats::Vertex<myTS>; // basic Vertex tests BOOST_AUTO_TEST_CASE(Vertex) { - const Point3D<float> pos(0.1, 0.2, 3.0); + const math_utils::Point3D<float> pos(0.1, 0.2, 3.0); const std::array<float, myVtx::kNCov> cov = {1e-4, -1e-9, 2e-4, -1e-9, 1e-9, 1e-4}; int nc = 10; float chi2 = 5.5f; diff --git a/DataFormats/common/CMakeLists.txt b/DataFormats/common/CMakeLists.txt index df5fd75cac3fb..f1c4e4eda918e 100644 --- a/DataFormats/common/CMakeLists.txt +++ b/DataFormats/common/CMakeLists.txt @@ -9,16 +9,22 @@ # submit itself to any jurisdiction. o2_add_library(CommonDataFormat - SOURCES src/InteractionRecord.cxx src/BunchFilling.cxx + SOURCES src/InteractionRecord.cxx + src/BunchFilling.cxx + src/FlatHisto1D.cxx + src/FlatHisto2D.cxx PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::GPUCommon - ROOT::Core FairRoot::Base) + ROOT::Core FairRoot::Base ms_gsl::ms_gsl) o2_target_root_dictionary(CommonDataFormat HEADERS include/CommonDataFormat/TimeStamp.h include/CommonDataFormat/EvIndex.h include/CommonDataFormat/RangeReference.h include/CommonDataFormat/InteractionRecord.h - include/CommonDataFormat/BunchFilling.h) + include/CommonDataFormat/BunchFilling.h + include/CommonDataFormat/AbstractRef.h + include/CommonDataFormat/FlatHisto1D.h + include/CommonDataFormat/FlatHisto2D.h) o2_add_test(TimeStamp SOURCES test/testTimeStamp.cxx @@ -29,3 +35,8 @@ o2_add_test(RangeRef SOURCES test/testRangeRef.cxx COMPONENT_NAME CommonDataFormat PUBLIC_LINK_LIBRARIES O2::CommonDataFormat) + +o2_add_test(FlatHisto + SOURCES test/testFlatHisto.cxx + COMPONENT_NAME CommonDataFormat + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat) diff --git a/DataFormats/common/include/CommonDataFormat/AbstractRef.h b/DataFormats/common/include/CommonDataFormat/AbstractRef.h new file mode 100644 index 0000000000000..70fdc16735902 --- /dev/null +++ b/DataFormats/common/include/CommonDataFormat/AbstractRef.h @@ -0,0 +1,84 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 AbstractRef.h +/// \brief Class to refer to object indicating its Indec, Source and status flags +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ABSTRACT_REF_H +#define ALICEO2_ABSTRACT_REF_H + +#include <Rtypes.h> + +namespace o2 +{ +namespace dataformats +{ + +template <int NBIdx, int NBSrc, int NBFlg> +class AbstractRef +{ + template <int NBIT> + static constexpr auto MVAR() + { + 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; + } + + public: + using Base_t = decltype(AbstractRef::MVAR<NBIdx + NBSrc + NBFlg>()); + using Idx_t = decltype(AbstractRef::MVAR<NBIdx>()); + using Src_t = decltype(AbstractRef::MVAR<NBSrc>()); + using Flg_t = decltype(AbstractRef::MVAR<NBFlg>()); + + static constexpr Base_t BaseMask = Base_t((((0x1U << (NBIdx + NBSrc + NBFlg - 1)) - 1) << 1) + 1); + static constexpr Idx_t IdxMask = Idx_t((((0x1U << (NBIdx - 1)) - 1) << 1) + 1); + static constexpr Src_t SrcMask = Src_t((((0x1U << (NBSrc - 1)) - 1) << 1) + 1); + static constexpr Flg_t FlgMask = Flg_t((((0x1U << (NBFlg - 1)) - 1) << 1) + 1); + static constexpr int NBitsIndex() { return NBIdx; } + static constexpr int NBitsSource() { return NBSrc; } + static constexpr int NBitsFlags() { return NBFlg; } + + AbstractRef() = default; + + AbstractRef(Idx_t idx) { setIndex(idx); } + + AbstractRef(Idx_t idx, Src_t src) { set(idx, src); } + + // + Idx_t getIndex() const { return static_cast<Idx_t>(mRef & IdxMask); } + void setIndex(Idx_t idx) { mRef = (mRef & (BaseMask & ~IdxMask)) | (IdxMask & idx); } + + // + Src_t getSource() const { return static_cast<Idx_t>((mRef >> NBIdx) & SrcMask); } + void setSource(Src_t src) { mRef = (mRef & (BaseMask & ~(SrcMask << NBIdx))) | ((SrcMask & src) << NBIdx); } + + // + Flg_t getFlags() const { return static_cast<Flg_t>((mRef >> (NBIdx + NBSrc)) & FlgMask); } + void setFlags(Flg_t f) { mRef = (mRef & (BaseMask & ~(FlgMask << (NBIdx + NBSrc)))) | ((FlgMask & f) << NBIdx); } + bool testBit(int i) const { return (mRef >> (NBIdx + NBSrc)) & ((0x1U << i) & FlgMask); } + void setBit(int i) { mRef = (mRef & (BaseMask & ~(0x1U << (i + NBIdx + NBSrc)))) | (((0x1U << i) & FlgMask) << (NBIdx + NBSrc)); } + void resetBit(int i) { mRef = (mRef & (BaseMask & ~(0x1U << (i + NBIdx + NBSrc)))); } + void set(Idx_t idx, Src_t src) { mRef = (mRef & (BaseMask & ~((SrcMask << NBIdx) | (BaseMask & ~IdxMask)))) | ((SrcMask & Src_t(src)) << NBIdx) | (IdxMask & Idx_t(idx)); } + + Base_t getRaw() const { return mRef; } + + operator Base_t() const { return mRef; } + + protected: + Base_t mRef = 0; // packed reference + + ClassDefNV(AbstractRef, 1); +}; + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/common/include/CommonDataFormat/BunchFilling.h b/DataFormats/common/include/CommonDataFormat/BunchFilling.h index 14775e468f9bf..30d50109a6c5c 100644 --- a/DataFormats/common/include/CommonDataFormat/BunchFilling.h +++ b/DataFormats/common/include/CommonDataFormat/BunchFilling.h @@ -29,6 +29,8 @@ class BunchFilling void setBCTrains(int nTrains, int trainSpacingInBC, int nBC, int bcSpacing, int firstBC); void print(int bcPerLine = 100) const; const auto& getPattern() const { return mPattern; } + int getFirstFilledBC() const; + int getLastFilledBC() const; // set BC filling a la TPC TDR, 12 50ns trains of 48 BCs // but instead of uniform train spacing we add 96empty BCs after each train void setDefault() diff --git a/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h b/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h new file mode 100644 index 0000000000000..18407f295cbf4 --- /dev/null +++ b/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h @@ -0,0 +1,215 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FlatHisto1D.h +/// \brief 1D messeageable histo class +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_FLATHISTO1D_H +#define ALICEO2_FLATHISTO1D_H + +#include <Rtypes.h> +#include <vector> +#include <gsl/span> +#include <TH1F.h> +#include <type_traits> + +namespace o2 +{ +namespace dataformats +{ + +/* + Fast 1D histo class which can be messages as + FlatHisto1D<float> histo(nbins, xmin, xmax); + histo.fill(...); + pc.outputs().snapshot(Output{"Origin", "Desc", 0, Lifetime::Timeframe}, histo.getBase()); + + and received (read only!) as + const auto hdata = pc.inputs().get<gsl::span<float>>("histodata"); + FlatHisto1D<float> histoView; + histoView.adoptExternal(hdata); + or directly + FlatHisto1D<float> histoView(pc.inputs().get<gsl::span<float>>("histodata")); +*/ + +template <typename T = float> +class FlatHisto1D +{ + static_assert(std::is_same<T, float>::value || std::is_same<T, double>::value, "T must be float or double"); + + public: + enum { NBins, + XMin, + XMax, + BinSize, + BinSizeInv, + NServiceSlots }; + + FlatHisto1D() = default; + + FlatHisto1D(int nb, T xmin, T xmax) + { + assert(nb > 0 && xmin < xmax); + mData.resize(nb + NServiceSlots, 0.); + mData[NBins] = nb; + mData[XMin] = xmin; + mData[XMax] = xmax; + mData[BinSize] = (xmax - xmin) / nb; + mData[BinSizeInv] = nb / (xmax - xmin); + init(); + } + + FlatHisto1D(const gsl::span<const T> ext) + { + adoptExternal(ext); + } + + int getNBins() const { return mNBins; } + T getXMin() const { return mDataView[XMin]; } + T getXMax() const { return mDataView[XMax]; } + T getBinSize() const { return mDataView[BinSize]; } + T getBinContent(uint32_t ib) const { return ib < mNBins ? mDataView[ib + NServiceSlots] : 0.; } + T getBinContentForX(T x) const { getBinContent(getBin(x)); } + + T getBinStart(int i) const + { + assert(i < getNBins()); + return getXMin() + i * getBinSize(); + } + + T getBinCenter(int i) const + { + assert(i < getNBins()); + return getXMin() + (i + 0.5) * getBinSize(); + } + + T getBinEnd(int i) const + { + assert(i < getNBins()); + return getXMin() + (i + 1) * getBinSize(); + } + + void add(const FlatHisto1D& other) + { + assert(getNBins() == other.getNBins() && getXMin() == other.getXMin() && getXMax() == other.getXMax() && canFill()); + int last = NServiceSlots + getNBins(); + const auto& otherView = other.getView(); + for (int i = NServiceSlots; i < last; i++) { + mData[i] += otherView[i]; + } + } + + void subtract(const FlatHisto1D& other) + { + assert(getNBins() == other.getNBins() && getXMin() == other.getXMin() && getXMax() == other.getXMax() && canFill()); + int last = NServiceSlots + getNBins(); + const auto& otherView = other.getView(); + for (int i = NServiceSlots; i < last; i++) { + mData[i] -= otherView[i]; + } + } + + void setBinContent(uint32_t bin, T w) + { + assert(canFill() && bin < mNBins); + mData[bin + NServiceSlots] = w; + } + + void clear() + { + assert(canFill()); + memset(mData.data() + NServiceSlots, 0, sizeof(T) * getNBins()); + } + + T getSum() const + { + T sum = 0; + for (int i = getNBins(); i--;) { + sum += getBinContent(i); + } + return sum; + } + + void adoptExternal(const gsl::span<const T> ext) + { + assert(ext.size() > NServiceSlots); + mData.clear(); + mDataView = ext; + mNBins = (int)mDataView[NBins]; + } + + void init() + { // when reading from file, need to call this method to make it operational + assert(mData.size() > NServiceSlots); + mDataView = gsl::span<const T>(mData.data(), mData.size()); + mNBins = (int)mData[NBins]; + } + + void fill(T x) + { + uint32_t bin = getBin(x); + if (bin < mNBins) { + mData[NServiceSlots + bin]++; + } + } + + void fill(T x, T w) + { + uint32_t bin = getBin(x); + if (bin < mNBins) { + mData[NServiceSlots + bin] += w; + } + } + + uint32_t getBin(T x) const + { + auto dx = x - getXMin(); + return dx < 0 ? 0xffffffff : uint32_t(dx * getBinSizeInv()); + } + + bool canFill() const + { + // histo can be filled only if hase its own data, otherwise only query can be done on the view + return mData.size() > NServiceSlots; + } + + TH1F createTH1F(const std::string& name = "histo1d") + { + TH1F h(name.c_str(), name.c_str(), getNBins(), getXMin(), getXMax()); + for (int i = getNBins(); i--;) { + auto w = getBinContent(i); + if (w) { + h.SetBinContent(i + 1, w); + } + } + return h; + } + + const std::vector<T>& getBase() const { return mData; } + gsl::span<const T> getView() const { return mDataView; } + + protected: + T getBinSizeInv() const { return mDataView[BinSizeInv]; } + + std::vector<T> mData; // data to fill + gsl::span<const T> mDataView; //! + int mNBins = 0; //! + + ClassDefNV(FlatHisto1D, 1); +}; + +using FlatHisto1D_f = FlatHisto1D<float>; +using FlatHisto1D_d = FlatHisto1D<double>; + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h b/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h new file mode 100644 index 0000000000000..8da1daf658162 --- /dev/null +++ b/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h @@ -0,0 +1,305 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FlatHisto2D.h +/// \brief 2D messeageable histo class +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_FLATHISTO2D_H +#define ALICEO2_FLATHISTO2D_H + +#include <Rtypes.h> +#include <vector> +#include <gsl/span> +#include <TH2F.h> +#include <type_traits> + +namespace o2 +{ +namespace dataformats +{ + +/* + Fast 2D histo class which can be messages as + FlatHisto2D<float> histo(nbinsX, xmin, xmax, nbinsY, ymin, ymax); + histo.fill(...); + pc.outputs().snapshot(Output{"Origin", "Desc", 0, Lifetime::Timeframe}, histo.getBase()); + + and received (read only!) as + const auto hdata = pc.inputs().get<gsl::span<float>>("histodata"); + FlatHisto2D<float> histoView; + histoView.adoptExternal(hdata); + or directly + FlatHisto2D<float> histoView(pc.inputs().get<gsl::span<float>>("histodata")); +*/ + +template <typename T = float> +class FlatHisto2D +{ + static_assert(std::is_same<T, float>::value || std::is_same<T, double>::value, "T must be float or double"); + + public: + enum { NBinsX, + NBinsY, + XMin, + XMax, + YMin, + YMax, + BinSizeX, + BinSizeY, + BinSizeXInv, + BinSizeYInv, + NServiceSlots }; + + FlatHisto2D() = default; + + FlatHisto2D(int nbx, T xmin, T xmax, int nby, T ymin, T ymax) + { + assert(nbx > 0 && xmin < xmax); + assert(nby > 0 && ymin < ymax); + mData.resize(nbx * nby + NServiceSlots, 0.); + mData[NBinsX] = nbx; + mData[NBinsY] = nby; + mData[XMin] = xmin; + mData[XMax] = xmax; + mData[YMin] = ymin; + mData[YMax] = ymax; + mData[BinSizeX] = (xmax - xmin) / nbx; + mData[BinSizeXInv] = nbx / (xmax - xmin); + mData[BinSizeY] = (ymax - ymin) / nby; + mData[BinSizeYInv] = nby / (ymax - ymin); + init(); + } + + FlatHisto2D(const gsl::span<const T> ext) + { + adoptExternal(ext); + } + + int getNBinsX() const { return mNBinsX; } + int getNBinsY() const { return mNBinsY; } + int getNBins() const { return mNBins; } + T getXMin() const { return mDataView[XMin]; } + T getXMax() const { return mDataView[XMax]; } + T getYMin() const { return mDataView[YMin]; } + T getYMax() const { return mDataView[YMax]; } + T getBinSizeX() const { return mDataView[BinSizeX]; } + T getBinSizeY() const { return mDataView[BinSizeY]; } + T getBinContent(uint32_t ib) const { return ib < mNBins ? mDataView[ib + NServiceSlots] : 0.; } + T getBinContent(uint32_t ibx, uint32_t iby) const { return getBinContent(getGlobalBin(ibx, iby)); } + T getBinContentForXY(T x, T y) const { return getBinContent(getBinX(x), getBinY(y)); } + + T getBinStartX(int i) const + { + assert(i < getNBinsX()); + return getXMin() + i * getBinSizeX(); + } + + T getBinCenterX(int i) const + { + assert(i < getNBinsX()); + return getXMin() + (i + 0.5) * getBinSizeX(); + } + + T getBinEndX(int i) const + { + assert(i < getNBinsX()); + return getXMin() + (i + 1) * getBinSizeX(); + } + + T getBinStartY(int i) const + { + assert(i < getNBinsY()); + return getYMin() + i * getBinSizeY(); + } + + T getBinCenterY(int i) const + { + assert(i < getNBinsY()); + return getYMin() + (i + 0.5) * getBinSizeY(); + } + + T getBinEndY(int i) const + { + assert(i < getNBinsY()); + return getYMin() + (i + 1) * getBinSizeY(); + } + + void add(const FlatHisto2D& other) + { + assert(getNBinsX() == other.getNBinsX() && getXMin() == other.getXMin() && getXMax() == other.getXMax() && + getNBinsY() == other.getNBinsY() && getYMin() == other.getYMin() && getYMax() == other.getYMax() && + canFill()); + int last = NServiceSlots + getNBins(); + const auto& otherView = other.getView(); + for (int i = NServiceSlots; i < last; i++) { + mData[i] += otherView[i]; + } + } + + void subtract(const FlatHisto2D& other) + { + assert(getNBinsX() == other.getNBinsX() && getXMin() == other.getXMin() && getXMax() == other.getXMax() && + getNBinsY() == other.getNBinsY() && getYMin() == other.getYMin() && getYMax() == other.getYMax() && + canFill()); + int last = NServiceSlots + getNBins(); + const auto& otherView = other.getView(); + for (int i = NServiceSlots; i < last; i++) { + mData[i] -= otherView[i]; + } + } + + void setBinContent(uint32_t bin, T w) + { + assert(canFill()); + if (bin < getNBins()) { + mData[bin + NServiceSlots] = w; + } + } + + void setBinContent(uint32_t binX, uint32_t binY, T w) + { + assert(canFill()); + auto bin = getGlobalBin(binX, binY); + if (bin < getNBins()) { + mData[+NServiceSlots] = w; + } + } + + void clear() + { + assert(canFill()); + memset(mData.data() + NServiceSlots, 0, sizeof(T) * getNBins()); + } + + T getSum() const + { + T sum = 0; + for (int i = getNBins(); i--;) { + sum += getBinContent(i); + } + return sum; + } + + void adoptExternal(const gsl::span<const T> ext) + { + assert(ext.size() > NServiceSlots); + mData.clear(); + mDataView = ext; + mNBinsX = (int)mDataView[NBinsX]; + mNBinsY = (int)mDataView[NBinsY]; + mNBins = mNBinsX * mNBinsY; + } + + void init() + { // when reading from file, need to call this method to make it operational + assert(mData.size() > NServiceSlots); + mDataView = gsl::span<const T>(mData.data(), mData.size()); + mNBinsX = (int)mData[NBinsX]; + mNBinsY = (int)mData[NBinsY]; + mNBins = mNBinsX * mNBinsY; + } + + void fill(T x, T y) + { + uint32_t bin = getBin(x, y); + if (bin < mNBins) { + mData[NServiceSlots + bin]++; + } + } + + void fill(T x, T y, T w) + { + uint32_t bin = getBin(x, y); + if (bin < mNBins) { + mData[NServiceSlots + bin] += w; + } + } + + uint32_t getBinX(T x) const + { + auto dx = x - getXMin(); + return dx < 0 ? 0xffffffff : uint32_t(dx * getBinSizeXInv()); + } + + uint32_t getBinY(T y) const + { + auto dy = y - getYMin(); + return dy < 0 ? 0xffffffff : uint32_t(dy * getBinSizeYInv()); + } + + uint32_t getBin(T x, T y) const + { + auto bx = getBinX(x), by = getBinY(y); + return bx < getNBinsX() && by < getNBinsY() ? getGlobalBin(bx, by) : 0xffffffff; + } + + bool canFill() const + { + // histo can be filled only if hase its own data, otherwise only query can be done on the view + return mData.size() > NServiceSlots; + } + + gsl::span<const T> getSliceY(uint32_t binX) const + { + int offs = NServiceSlots + binX * getNBinsY(); + return binX < getNBinsX() ? gsl::span<const T>(&mDataView[offs], getNBinsY()) : gsl::span<const T>(); + } + + TH1F createSliceYTH1F(uint32_t binX, const std::string& name = "histo2dslice") const + { + TH1F h(name.c_str(), name.c_str(), getNBinsY(), getYMin(), getYMax()); + if (binX < getNBinsX()) { + for (int i = getNBinsY(); i--;) { + h.SetBinContent(i + 1, getBinContent(binX, i)); + } + } + return h; + } + + TH2F createTH2F(const std::string& name = "histo2d") + { + TH2F h(name.c_str(), name.c_str(), getNBinsX(), getXMin(), getXMax(), getNBinsY(), getYMin(), getYMax()); + for (int i = getNBinsX(); i--;) { + for (int j = getNBinsY(); j--;) { + auto w = getBinContent(i, j); + if (w) { + h.SetBinContent(i + 1, j + 1, w); + } + } + } + return h; + } + + const std::vector<T>& getBase() const { return mData; } + gsl::span<const T> getView() const { return mDataView; } + + int getGlobalBin(uint32_t binX, uint32_t binY) const { return binX * getNBinsY() + binY; } + + protected: + T getBinSizeXInv() const { return mDataView[BinSizeXInv]; } + T getBinSizeYInv() const { return mDataView[BinSizeYInv]; } + + std::vector<T> mData; // data to fill + gsl::span<const T> mDataView; //! + int mNBinsX = 0; //! + int mNBinsY = 0; //! + int mNBins = 0; //! + + ClassDefNV(FlatHisto2D, 1); +}; + +using FlatHisto2D_f = FlatHisto2D<float>; +using FlatHisto2D_d = FlatHisto2D<double>; + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h index bbcee574f32ff..7ed2b72b0ccb2 100644 --- a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h +++ b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h @@ -14,9 +14,11 @@ #define ALICEO2_INTERACTIONRECORD_H #include "GPUCommonRtypes.h" +#ifndef GPUCA_ALIGPUCODE #include <iosfwd> -#include <cmath> #include <cstdint> +#endif +#include <cmath> #include "CommonConstants/LHCConstants.h" namespace o2 @@ -88,7 +90,7 @@ struct InteractionRecord { int64_t differenceInBC(const InteractionRecord& other) const { - // return differenc in bunch-crossings + // return difference in bunch-crossings int64_t diffBC = int(bc) - other.bc; if (orbit != other.orbit) { diffBC += (int64_t(orbit) - other.orbit) * o2::constants::lhc::LHCMaxBunches; @@ -96,6 +98,18 @@ struct InteractionRecord { return diffBC; } + float differenceInBCNS(const InteractionRecord& other) const + { + // return difference in bunch-crossings in ns + return differenceInBC(other) * o2::constants::lhc::LHCBunchSpacingNS; + } + + float differenceInBCMS(const InteractionRecord& other) const + { + // return difference in bunch-crossings in ms + return differenceInBC(other) * o2::constants::lhc::LHCBunchSpacingMS; + } + int64_t toLong() const { // return as single long number @@ -232,10 +246,11 @@ struct InteractionRecord { return InteractionRecord(l % o2::constants::lhc::LHCMaxBunches, l / o2::constants::lhc::LHCMaxBunches); } +#ifndef GPUCA_ALIGPUCODE void print() const; - + std::string asString() const; friend std::ostream& operator<<(std::ostream& stream, InteractionRecord const& ir); - +#endif ClassDefNV(InteractionRecord, 3); }; @@ -309,9 +324,11 @@ struct InteractionTimeRecord : public InteractionRecord { return !((*this) > other); } +#ifndef GPUCA_ALIGPUCODE void print() const; - + std::string asString() const; friend std::ostream& operator<<(std::ostream& stream, InteractionTimeRecord const& ir); +#endif ClassDefNV(InteractionTimeRecord, 1); }; diff --git a/DataFormats/common/include/CommonDataFormat/RangeReference.h b/DataFormats/common/include/CommonDataFormat/RangeReference.h index 1b2e77b229867..ae230bf324b4f 100644 --- a/DataFormats/common/include/CommonDataFormat/RangeReference.h +++ b/DataFormats/common/include/CommonDataFormat/RangeReference.h @@ -16,6 +16,7 @@ #define ALICEO2_RANGEREFERENCE_H #include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" namespace o2 { @@ -27,22 +28,22 @@ template <typename FirstEntry = int, typename NElem = int> class RangeReference { public: - RangeReference(FirstEntry ent, NElem n) { set(ent, n); } - RangeReference(const RangeReference<FirstEntry, NElem>& src) = default; - RangeReference() = default; - ~RangeReference() = default; - void set(FirstEntry ent, NElem n) + GPUd() RangeReference(FirstEntry ent, NElem n) { set(ent, n); } + GPUdDefault() RangeReference(const RangeReference<FirstEntry, NElem>& src) = default; + GPUdDefault() RangeReference() = default; + GPUdDefault() ~RangeReference() = default; + GPUd() void set(FirstEntry ent, NElem n) { mFirstEntry = ent; mEntries = n; } - void clear() { set(0, 0); } - FirstEntry getFirstEntry() const { return mFirstEntry; } - NElem getEntries() const { return mEntries; } - void setFirstEntry(FirstEntry ent) { mFirstEntry = ent; } - void setEntries(NElem n) { mEntries = n; } - void changeEntriesBy(NElem inc) { mEntries += inc; } - bool operator==(const RangeReference& other) const + GPUd() void clear() { set(0, 0); } + GPUd() FirstEntry getFirstEntry() const { return mFirstEntry; } + 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 { return mFirstEntry == other.mFirstEntry && mEntries == other.mEntries; } @@ -58,34 +59,34 @@ class RangeReference template <int NBitsN> class RangeRefComp { - using Base = std::uint32_t; + using Base = unsigned int; private: static constexpr int NBitsTotal = sizeof(Base) * 8; static constexpr Base MaskN = ((0x1 << NBitsN) - 1); static constexpr Base MaskR = (~Base(0)) & (~MaskN); Base mData = 0; ///< packed 1st entry reference + N entries - void sanityCheck() + GPUd() void sanityCheck() { static_assert(NBitsN < NBitsTotal, "NBitsN too large"); } public: - RangeRefComp(int ent, int n) { set(ent, n); } - RangeRefComp() = default; - RangeRefComp(const RangeRefComp& src) = default; - void set(int ent, int n) + GPUd() RangeRefComp(int ent, int n) { set(ent, n); } + GPUdDefault() RangeRefComp() = default; + GPUdDefault() RangeRefComp(const RangeRefComp& src) = default; + GPUd() void set(int ent, int n) { mData = (Base(ent) << NBitsN) + (Base(n) & MaskN); } - static constexpr Base getMaxFirstEntry() { return MaskR >> NBitsN; } - static constexpr Base getMaxEntries() { return MaskN; } - int getFirstEntry() const { return mData >> NBitsN; } - int getEntries() const { return mData & ((0x1 << NBitsN) - 1); } - void setFirstEntry(int ent) { mData = (Base(ent) << NBitsN) | (mData & MaskN); } - void setEntries(int n) { mData = (mData & MaskR) | (Base(n) & MaskN); } - void changeEntriesBy(int inc) { setEntries(getEntries() + inc); } - bool operator==(const RangeRefComp& other) const + GPUd() static constexpr Base getMaxFirstEntry() { return MaskR >> NBitsN; } + GPUd() static constexpr Base getMaxEntries() { return MaskN; } + GPUd() int getFirstEntry() const { return mData >> NBitsN; } + GPUd() int getEntries() const { return mData & ((0x1 << NBitsN) - 1); } + GPUd() void setFirstEntry(int ent) { mData = (Base(ent) << NBitsN) | (mData & MaskN); } + GPUd() void setEntries(int n) { mData = (mData & MaskR) | (Base(n) & MaskN); } + GPUd() void changeEntriesBy(int inc) { setEntries(getEntries() + inc); } + GPUd() bool operator==(const RangeRefComp& other) const { return mData == other.mData; } diff --git a/DataFormats/common/src/BunchFilling.cxx b/DataFormats/common/src/BunchFilling.cxx index 328c63ff50707..818be2c9361b0 100644 --- a/DataFormats/common/src/BunchFilling.cxx +++ b/DataFormats/common/src/BunchFilling.cxx @@ -15,6 +15,28 @@ using namespace o2; +//_________________________________________________ +int BunchFilling::getFirstFilledBC() const +{ + for (int bc = 0; bc < o2::constants::lhc::LHCMaxBunches; bc++) { + if (testBC(bc)) { + return bc; + } + } + return -1; +} + +//_________________________________________________ +int BunchFilling::getLastFilledBC() const +{ + for (int bc = o2::constants::lhc::LHCMaxBunches; bc--;) { + if (testBC(bc)) { + return bc; + } + } + return -1; +} + //_________________________________________________ void BunchFilling::setBC(int bcID, bool active) { diff --git a/DataFormats/common/src/CommonDataFormatLinkDef.h b/DataFormats/common/src/CommonDataFormatLinkDef.h index 6119655bfc723..d4503c6722057 100644 --- a/DataFormats/common/src/CommonDataFormatLinkDef.h +++ b/DataFormats/common/src/CommonDataFormatLinkDef.h @@ -38,10 +38,24 @@ #pragma link C++ class o2::dataformats::RangeRefComp < 4> + ; // reference to a set with 15 entries max (ITS clusters) #pragma link C++ class o2::dataformats::RangeRefComp < 5> + ; // reference to a set with 15 entries max (ZDC BCData) #pragma link C++ class o2::dataformats::RangeRefComp < 6> + ; // reference to a set with 63 entries max (FV0 BCData) -#pragma link C++ class o2::dataformats::RangeRefComp < 8> + ; // +#pragma link C++ class o2::dataformats::RangeRefComp < 8> + ; // + +#pragma link C++ class std::vector < o2::dataformats::RangeReference < int, int>> + ; #pragma link C++ class o2::InteractionRecord + ; #pragma link C++ class o2::InteractionTimeRecord + ; #pragma link C++ class o2::BunchFilling + ; +#pragma link C++ class o2::dataformats::AbstractRef < 26, 3, 3> + ; + +#pragma link C++ class o2::dataformats::FlatHisto1D < float> + ; +#pragma link C++ class o2::dataformats::FlatHisto1D < double> + ; +#pragma link C++ class o2::dataformats::FlatHisto1D_f + ; +#pragma link C++ class o2::dataformats::FlatHisto1D_d + ; + +#pragma link C++ class o2::dataformats::FlatHisto2D < float> + ; +#pragma link C++ class o2::dataformats::FlatHisto2D < double> + ; +#pragma link C++ class o2::dataformats::FlatHisto2D_f + ; +#pragma link C++ class o2::dataformats::FlatHisto2D_d + ; + #endif diff --git a/DataFormats/common/src/FlatHisto1D.cxx b/DataFormats/common/src/FlatHisto1D.cxx new file mode 100644 index 0000000000000..0b59cd2b8259d --- /dev/null +++ b/DataFormats/common/src/FlatHisto1D.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FlatHisto1D.cxx +/// \brief 1D messeageable histo class +/// \author ruben.shahoyan@cern.ch + +#include "CommonDataFormat/FlatHisto1D.h" + +using namespace o2::dataformats; diff --git a/DataFormats/common/src/FlatHisto2D.cxx b/DataFormats/common/src/FlatHisto2D.cxx new file mode 100644 index 0000000000000..3266a23b81fd3 --- /dev/null +++ b/DataFormats/common/src/FlatHisto2D.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FlatHisto1D.cxx +/// \brief 2D messeageable histo class +/// \author ruben.shahoyan@cern.ch + +#include "CommonDataFormat/FlatHisto2D.h" + +using namespace o2::dataformats; diff --git a/DataFormats/common/src/InteractionRecord.cxx b/DataFormats/common/src/InteractionRecord.cxx index fb4c3459a0435..59962961548b1 100644 --- a/DataFormats/common/src/InteractionRecord.cxx +++ b/DataFormats/common/src/InteractionRecord.cxx @@ -9,21 +9,32 @@ // or submit itself to any jurisdiction. #include "CommonDataFormat/InteractionRecord.h" #include <iostream> +#include <fmt/printf.h> namespace o2 { -//const double InteractionRecord::DummyTime = InteractionRecord::bc2ns(0xffff,0xffffffff); +#ifndef GPUCA_ALIGPUCODE + +std::string InteractionRecord::asString() const +{ + return isDummy() ? std::string{"NotSet"} : fmt::format("BCid: {:4d} Orbit: {:6d}", bc, orbit); +} std::ostream& operator<<(std::ostream& stream, o2::InteractionRecord const& ir) { - stream << "BCid: " << ir.bc << " Orbit: " << ir.orbit; + stream << ir.asString(); return stream; } +std::string InteractionTimeRecord::asString() const +{ + return isDummy() ? InteractionRecord::asString() : InteractionRecord::asString() + fmt::format(" |T in BC(ns): {:.3f}", timeInBCNS); +} + std::ostream& operator<<(std::ostream& stream, o2::InteractionTimeRecord const& ir) { - stream << "BCid: " << ir.bc << " Orbit: " << ir.orbit << " T in BC(ns): " << ir.timeInBCNS; + stream << ir.asString(); return stream; } @@ -37,4 +48,6 @@ void InteractionTimeRecord::print() const std::cout << (*this) << std::endl; } +#endif + } // namespace o2 diff --git a/DataFormats/common/test/testFlatHisto.cxx b/DataFormats/common/test/testFlatHisto.cxx new file mode 100644 index 0000000000000..d1f9a56cdfd05 --- /dev/null +++ b/DataFormats/common/test/testFlatHisto.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FlatHisto class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "CommonDataFormat/FlatHisto1D.h" +#include "CommonDataFormat/FlatHisto2D.h" +#include <TFile.h> +#include <TRandom.h> +#include <TFitResult.h> + +namespace o2 +{ + +// basic Vertex tests +BOOST_AUTO_TEST_CASE(FlatHisto) +{ + o2::dataformats::FlatHisto1D_f h1(100, -100., 100.); + o2::dataformats::FlatHisto2D_f h2(100, -100., 100., 50, -5., 45.); + for (int i = 0; i < 1000000; i++) { + h1.fill(gRandom->Gaus(10, 30)); + } + auto h2ref = h2.createTH2F("h2ref"); + for (int i = 0; i < 10000000; i++) { + auto x = gRandom->Gaus(10, 40), y = gRandom->Gaus(10, 10); + h2.fill(x, y); + h2ref.Fill(x, y); + } + auto th1f = h1.createTH1F(); + auto res = th1f.Fit("gaus", "S"); + BOOST_CHECK_CLOSE(res->GetParams()[1], 10, 0.2); + + printf("%e %e\n", h2.getSum(), h2ref.Integral()); + BOOST_CHECK(h2.getSum() == h2ref.Integral()); + + o2::dataformats::FlatHisto1D_f h1v(h1); + + BOOST_CHECK_CLOSE(h1.getBinStart(0), -100, 1e-5); + BOOST_CHECK_CLOSE(h1.getBinEnd(h1.getNBins() - 1), 100, 1e-5); + + BOOST_CHECK_CLOSE(h2.getBinStartX(0), -100, 1e-5); + BOOST_CHECK_CLOSE(h2.getBinEndY(h2.getNBinsY() - 1), 45, 1e-5); + BOOST_CHECK_CLOSE(h2.getBinStartY(h2.getNBinsY() - 1), 45 - h2.getBinSizeY(), 1e-5); + + BOOST_CHECK(h1.canFill() && h1v.canFill()); + BOOST_CHECK(h1.getSum() == h1v.getSum()); + { + TFile flout("flathisto.root", "recreate"); + flout.WriteObjectAny(&h1, "o2::dataformats::FlatHisto1D_f", "h1"); + flout.WriteObjectAny(&h2, "o2::dataformats::FlatHisto2D_f", "h2"); + flout.Close(); + } + + TFile flin("flathisto.root"); + o2::dataformats::FlatHisto1D_f* h1r = (o2::dataformats::FlatHisto1D_f*)flin.GetObjectUnchecked("h1"); + o2::dataformats::FlatHisto2D_f* h2r = (o2::dataformats::FlatHisto2D_f*)flin.GetObjectUnchecked("h2"); + flin.Close(); + h1r->init(); + h2r->init(); + + o2::dataformats::FlatHisto1D_f h1vv; + h1vv.adoptExternal(h1r->getView()); + BOOST_CHECK(h1.getSum() == h1vv.getSum()); + h1.add(h1vv); + h2.subtract(*h2r); + BOOST_CHECK(h1.getSum() == 2 * h1vv.getSum()); + BOOST_CHECK(h2.getSum() == 0.); +} + +} // namespace o2 diff --git a/DataFormats/simulation/CMakeLists.txt b/DataFormats/simulation/CMakeLists.txt index fec3ebbd23e70..9ed5155633744 100644 --- a/DataFormats/simulation/CMakeLists.txt +++ b/DataFormats/simulation/CMakeLists.txt @@ -12,6 +12,7 @@ o2_add_library(SimulationDataFormat SOURCES src/Stack.cxx src/MCTrack.cxx src/MCCompLabel.cxx + src/MCEventLabel.cxx src/DigitizationContext.cxx src/StackParam.cxx src/MCEventHeader.cxx @@ -29,12 +30,14 @@ o2_target_root_dictionary( include/SimulationDataFormat/BaseHits.h include/SimulationDataFormat/MCTruthContainer.h include/SimulationDataFormat/MCCompLabel.h + include/SimulationDataFormat/MCEventLabel.h include/SimulationDataFormat/TrackReference.h include/SimulationDataFormat/PrimaryChunk.h include/SimulationDataFormat/DigitizationContext.h include/SimulationDataFormat/LabelContainer.h include/SimulationDataFormat/MCEventHeader.h include/SimulationDataFormat/MCEventStats.h + include/SimulationDataFormat/IOMCTruthContainerView.h LINKDEF src/SimulationDataLinkDef.h) # note the explicit LINKDEF as the linkdef in src is # @@ -56,6 +59,11 @@ o2_add_test(MCCompLabel COMPONENT_NAME SimulationDataFormat PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat) +o2_add_test(MCEventLabel + SOURCES test/testMCEventLabel.cxx + COMPONENT_NAME SimulationDataFormat + PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat) + o2_add_test(MCTrack SOURCES test/MCTrack.cxx COMPONENT_NAME SimulationDataFormat diff --git a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h index ce757ced13d63..31e506cbe65e0 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h +++ b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h @@ -10,7 +10,7 @@ #ifndef ALICEO2_BASE_HIT_H #define ALICEO2_BASE_HIT_H -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" namespace o2 { @@ -45,10 +45,10 @@ class BaseHit template <typename T, typename E, typename V = float> class BasicXYZVHit : public BaseHit { - Point3D<T> mPos; // cartesian position of Hit - E mTime; // time of flight - V mHitValue; // hit value - short mDetectorID; // the detector/sensor id + math_utils::Point3D<T> mPos; // cartesian position of Hit + E mTime; // time of flight + V mHitValue; // hit value + short mDetectorID; // the detector/sensor id public: BasicXYZVHit() = default; // for ROOT IO @@ -63,7 +63,7 @@ class BasicXYZVHit : public BaseHit T GetX() const { return mPos.X(); } T GetY() const { return mPos.Y(); } T GetZ() const { return mPos.Z(); } - Point3D<T> GetPos() const { return mPos; } + math_utils::Point3D<T> GetPos() const { return mPos; } // getting hit value V GetHitValue() const { return mHitValue; } // getting the time @@ -84,7 +84,7 @@ class BasicXYZVHit : public BaseHit SetY(y); SetZ(z); } - void SetPos(Point3D<T> const& p) { mPos = p; } + void SetPos(math_utils::Point3D<T> const& p) { mPos = p; } ClassDefNV(BasicXYZVHit, 1); }; diff --git a/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h b/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h new file mode 100644 index 0000000000000..f287dd56803b7 --- /dev/null +++ b/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h @@ -0,0 +1,212 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ConstMCTruthContainer.h +/// \brief A const (ready only) version of MCTruthContainer +/// \author Sandro Wenzel - August 2020 + +#ifndef O2_CONSTMCTRUTHCONTAINER_H +#define O2_CONSTMCTRUTHCONTAINER_H + +#include <SimulationDataFormat/MCTruthContainer.h> +#ifndef GPUCA_STANDALONE +#include <Framework/Traits.h> +#endif + +namespace o2 +{ +namespace dataformats +{ + +/// @class ConstMCTruthContainer +/// @brief A read-only version of MCTruthContainer allowing for storage optimisation +/// +/// This provides access functionality to MCTruthContainer with optimized linear storage +/// so that the data can easily be shared in memory or sent over network. +/// This container needs to be initialized by calling "flatten_to" from an existing +/// MCTruthContainer +template <typename TruthElement> +class ConstMCTruthContainer : public std::vector<char> +{ + public: + // (unfortunately we need these constructors for DPL) + using std::vector<char>::vector; + ConstMCTruthContainer() = default; + + // const data access + // get individual const "view" container for a given data index + // the caller can't do modifications on this view + MCTruthHeaderElement const& getMCTruthHeader(uint32_t dataindex) const + { + return getHeaderStart()[dataindex]; + } + + gsl::span<const TruthElement> getLabels(uint32_t dataindex) const + { + if (dataindex >= getIndexedSize()) { + return gsl::span<const TruthElement>(); + } + const auto start = getMCTruthHeader(dataindex).index; + const auto labelsptr = getLabelStart(); + return gsl::span<const TruthElement>(&labelsptr[start], getSize(dataindex)); + } + + // return the number of original data indexed here + size_t getIndexedSize() const { return size() >= sizeof(FlatHeader) ? getHeader().nofHeaderElements : 0; } + + // return the number of labels managed in this container + size_t getNElements() const { return size() >= sizeof(FlatHeader) ? getHeader().nofTruthElements : 0; } + + private: + using FlatHeader = typename MCTruthContainer<TruthElement>::FlatHeader; + + size_t getSize(uint32_t dataindex) const + { + // calculate size / number of labels from a difference in pointed indices + const auto size = (dataindex < getIndexedSize() - 1) + ? getMCTruthHeader(dataindex + 1).index - getMCTruthHeader(dataindex).index + : getNElements() - getMCTruthHeader(dataindex).index; + return size; + } + + /// Restore internal vectors from a raw buffer + /// The two vectors are resized according to the information in the \a FlatHeader + /// struct at the beginning of the buffer. Data is copied to the vectors. + TruthElement const* getLabelStart() const + { + auto* source = &(*this)[0]; + auto flatheader = getHeader(); + source += sizeof(FlatHeader); + const size_t headerSize = flatheader.sizeofHeaderElement * flatheader.nofHeaderElements; + source += headerSize; + return (TruthElement const*)source; + } + + FlatHeader const& getHeader() const + { + const auto* source = &(*this)[0]; + const auto& flatheader = *reinterpret_cast<FlatHeader const*>(source); + return flatheader; + } + + MCTruthHeaderElement const* getHeaderStart() const + { + auto* source = &(*this)[0]; + source += sizeof(FlatHeader); + return (MCTruthHeaderElement const*)source; + } +}; +} // namespace dataformats +} // namespace o2 + +// This is done so that DPL treats this container as a vector. +// In particular in enables +// a) --> snapshot without ROOT dictionary (as a flat buffer) +// b) --> requesting the resource in shared mem using make<T> +#ifndef GPUCA_STANDALONE +namespace o2::framework +{ +template <typename T> +struct is_specialization<o2::dataformats::ConstMCTruthContainer<T>, std::vector> : std::true_type { +}; +} // namespace o2::framework +#endif + +namespace o2 +{ +namespace dataformats +{ + +// A "view" label container without owning the storage (similar to gsl::span) +template <typename TruthElement> +class ConstMCTruthContainerView +{ + public: + ConstMCTruthContainerView(gsl::span<const char> const bufferview) : mStorage(bufferview){}; + ConstMCTruthContainerView(ConstMCTruthContainer<TruthElement> const& cont) : mStorage(gsl::span<const char>(cont)){}; + ConstMCTruthContainerView() : mStorage(nullptr, (gsl::span<const char>::index_type)0) { (void)0; } // be explicit that we want nullptr / 0 for an uninitialized container + ConstMCTruthContainerView(const ConstMCTruthContainerView&) = default; + + // const data access + // get individual const "view" container for a given data index + // the caller can't do modifications on this view + MCTruthHeaderElement const& getMCTruthHeader(uint32_t dataindex) const + { + return getHeaderStart()[dataindex]; + } + + gsl::span<const TruthElement> getLabels(uint32_t dataindex) const + { + if (dataindex >= getIndexedSize()) { + return gsl::span<const TruthElement>(); + } + const auto start = getMCTruthHeader(dataindex).index; + const auto labelsptr = getLabelStart(); + return gsl::span<const TruthElement>(&labelsptr[start], getSize(dataindex)); + } + + // return the number of original data indexed here + size_t getIndexedSize() const { return (size_t)mStorage.size() >= sizeof(FlatHeader) ? getHeader().nofHeaderElements : 0; } + + // return the number of labels managed in this container + size_t getNElements() const { return (size_t)mStorage.size() >= sizeof(FlatHeader) ? getHeader().nofTruthElements : 0; } + + // return underlying buffer + const gsl::span<const char>& getBuffer() const { return mStorage; } + + private: + gsl::span<const char> mStorage; + + using FlatHeader = typename MCTruthContainer<TruthElement>::FlatHeader; + + size_t getSize(uint32_t dataindex) const + { + // calculate size / number of labels from a difference in pointed indices + const auto size = (dataindex < getIndexedSize() - 1) + ? getMCTruthHeader(dataindex + 1).index - getMCTruthHeader(dataindex).index + : getNElements() - getMCTruthHeader(dataindex).index; + return size; + } + + /// Restore internal vectors from a raw buffer + /// The two vectors are resized according to the information in the \a FlatHeader + /// struct at the beginning of the buffer. Data is copied to the vectors. + TruthElement const* getLabelStart() const + { + auto* source = &(mStorage)[0]; + auto flatheader = getHeader(); + source += sizeof(FlatHeader); + const size_t headerSize = flatheader.sizeofHeaderElement * flatheader.nofHeaderElements; + source += headerSize; + return (TruthElement const*)source; + } + + FlatHeader const& getHeader() const + { + const auto* source = &(mStorage)[0]; + const auto& flatheader = *reinterpret_cast<FlatHeader const*>(source); + return flatheader; + } + + MCTruthHeaderElement const* getHeaderStart() const + { + auto* source = &(mStorage)[0]; + source += sizeof(FlatHeader); + return (MCTruthHeaderElement const*)source; + } +}; + +using ConstMCLabelContainer = o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>; +using ConstMCLabelContainerView = o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>; + +} // namespace dataformats +} // namespace o2 + +#endif //O2_CONSTMCTRUTHCONTAINER_H diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h index 8fb7aa7ed9756..2371389056c39 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -18,7 +18,7 @@ #include "CommonDataFormat/BunchFilling.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsParameters/GRPObject.h" -#include <FairLogger.h> +#include <GPUCommonLogger.h> namespace o2 { diff --git a/DataFormats/simulation/include/SimulationDataFormat/IOMCTruthContainerView.h b/DataFormats/simulation/include/SimulationDataFormat/IOMCTruthContainerView.h new file mode 100644 index 0000000000000..590524cd61e8f --- /dev/null +++ b/DataFormats/simulation/include/SimulationDataFormat/IOMCTruthContainerView.h @@ -0,0 +1,139 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 IOMCTruthContainerView.h +/// \brief A special IO container - splitting a given vector to enable ROOT IO +/// \author Sandro Wenzel - August 2020 + +#ifndef ALICEO2_DATAFORMATS_IOMCTRUTHVIEW_H_ +#define ALICEO2_DATAFORMATS_IOMCTRUTHVIEW_H_ + +#include "GPUCommonRtypes.h" // to have the ClassDef macros +#include <vector> +#include <gsl/span> + +namespace o2 +{ +namespace dataformats +{ + +/// +/// A specially constructed class allowing to stream a very large +/// vector buffer to a ROOT file. This is needed since ROOT currently has a size +/// limitation of ~1GB for data that it can stream per entry in a branch. +/// The solution is based on the ability of ROOT to split entries per data member, so +/// some input buffer gets divided into multiple parts. +/// +/// TODO: We could template this class to encode original type information (for the input buffer). +class IOMCTruthContainerView +{ + public: + IOMCTruthContainerView() = default; + + /// Constructor taking an existing flat vector as input; No copy is done - the + /// container is just a split view on the original buffer. + IOMCTruthContainerView(std::vector<char> const& input) + { + adopt(gsl::span<const char>(&(input[0]), input.size())); + } + + IOMCTruthContainerView(gsl::span<const char> const input) + { + adopt(input); + } + + ~IOMCTruthContainerView() + { + if (!mIsView) { + delete[] part1; + delete[] part2; + delete[] part3; + delete[] part4; + delete[] part5; + delete[] part6; + delete[] part7; + delete[] part8; + delete[] part9; + delete[] part10; + } + } + + /// "adopt" (without taking ownership) from an existing buffer + void adopt(gsl::span<const char> const input) + { + mIsView = true; + const auto delta = input.size() / N; + N2 = input.size() - (N - 1) * delta; + N1 = delta; + // TODO: this could benefit from a loop expansion + part1 = &input[0]; + part2 = &input[delta]; + part3 = &input[2 * delta]; + part4 = &input[3 * delta]; + part5 = &input[4 * delta]; + part6 = &input[5 * delta]; + part7 = &input[6 * delta]; + part8 = &input[7 * delta]; + part9 = &input[8 * delta]; + part10 = &input[9 * delta]; + } + + /// A function to recreate a flat output vector from this buffer. This + /// function is copying the data. + template <typename Alloc> + void copyandflatten(std::vector<char, Alloc>& output) const + { + // TODO: this could benefit from a loop expansion + copyhelper(part1, N1, output); + copyhelper(part2, N1, output); + copyhelper(part3, N1, output); + copyhelper(part4, N1, output); + copyhelper(part5, N1, output); + copyhelper(part6, N1, output); + copyhelper(part7, N1, output); + copyhelper(part8, N1, output); + copyhelper(part9, N1, output); + copyhelper(part10, N2, output); + } + + /// return total size in bytes + size_t getSize() const { return N1 * (N - 1) + N2; } + + private: + static constexpr int N = 10; + int N1 = 0; + int N2 = 0; + const char* part1 = nullptr; //[N1] + const char* part2 = nullptr; //[N1] + const char* part3 = nullptr; //[N1] + const char* part4 = nullptr; //[N1] + const char* part5 = nullptr; //[N1] + const char* part6 = nullptr; //[N1] + const char* part7 = nullptr; //[N1] + const char* part8 = nullptr; //[N1] + const char* part9 = nullptr; //[N1] + const char* part10 = nullptr; //[N2] + + bool mIsView = false; //! if this was merely adopted from an existing container or actually owns the memory + // we need to know this for the destructor + + template <typename Alloc> + void copyhelper(const char* input, int size, std::vector<char, Alloc>& output) const + { + gsl::span<const char> tmp(input, size); + std::copy(tmp.begin(), tmp.end(), std::back_inserter(output)); + } + + ClassDefNV(IOMCTruthContainerView, 1); +}; +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/DataFormats/simulation/include/SimulationDataFormat/LabelContainer.h b/DataFormats/simulation/include/SimulationDataFormat/LabelContainer.h index e13b1e5dafa23..656f4c2ed18d4 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/LabelContainer.h +++ b/DataFormats/simulation/include/SimulationDataFormat/LabelContainer.h @@ -38,18 +38,18 @@ class LabelContainer public: struct HeaderElementContinuous { HeaderElementContinuous() = default; // for ROOT IO - HeaderElementContinuous(ushort s, uint i) : size(s), index(i) {} - uint index = 0; // index of first label in the actual label storage - ushort size = 0; // total number of labels + HeaderElementContinuous(unsigned short s, unsigned int i) : size(s), index(i) {} + unsigned int index = 0; // index of first label in the actual label storage + unsigned short size = 0; // total number of labels ClassDefNV(HeaderElementContinuous, 1); }; struct HeaderElementLinked { HeaderElementLinked() = default; // for ROOT IO - HeaderElementLinked(uint i, int l, ushort s) : index(i), lastindex(l), size(s) {} - uint index = 0; // index of first label in the actual label storage - uint lastindex = 0; // index of last label in the actual label storage - ushort size = 0; // total number of labels + HeaderElementLinked(unsigned int i, int l, unsigned short s) : index(i), lastindex(l), size(s) {} + unsigned int index = 0; // index of first label in the actual label storage + unsigned int lastindex = 0; // index of last label in the actual label storage + unsigned short size = 0; // total number of labels ClassDefNV(HeaderElementLinked, 1); }; @@ -57,11 +57,11 @@ class LabelContainer using StoredLabelType = typename std::conditional<isContiguousStorage, LabelType, std::pair<LabelType, int>>::type; // internal functions allowing the iterator implementation to be completely generic - static uint getNextIndex(uint index, std::vector<LabelType> const& /*labels*/) + static unsigned int getNextIndex(unsigned int index, std::vector<LabelType> const& /*labels*/) { return index + 1; } - static uint getNextIndex(uint index, std::vector<std::pair<LabelType, int>> const& labels) + static unsigned int getNextIndex(unsigned int index, std::vector<std::pair<LabelType, int>> const& labels) { return labels[index].second; } @@ -202,7 +202,7 @@ class LabelContainer } /// add a label for a dataindex - void addLabel(uint dataindex, LabelType const& label) + void addLabel(unsigned int dataindex, LabelType const& label) { // refer to concrete specialized implementation addLabelImpl(dataindex, mHeaderArray, mLabelArray, label); diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h b/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h index 90d5c1a7ba5a2..ca536e767e4cc 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h @@ -120,6 +120,7 @@ class MCCompLabel int getTrackIDSigned() const { return isFake() ? -getTrackID() : getTrackID(); } int getEventID() const { return (mLabel >> nbitsTrackID) & maskEvID; } int getSourceID() const { return (mLabel >> (nbitsTrackID + nbitsEvID)) & maskSrcID; } + ULong64_t getTrackEventSourceID() const { return static_cast<ULong64_t>(mLabel & maskFull); } void get(int& trackID, int& evID, int& srcID, bool& fake) { /// parse label diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h index 42844cb8c9894..2a0338828ac17 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h @@ -15,7 +15,9 @@ #include "FairMCEventHeader.h" #include "SimulationDataFormat/MCEventStats.h" +#include "CommonUtils/RootSerializableKeyValueStore.h" #include <string> +#include <Framework/Logger.h> namespace o2 { @@ -42,6 +44,42 @@ class MCEventHeader : public FairMCEventHeader void setEmbeddingEventIndex(Int_t value) { mEmbeddingEventIndex = value; }; int getEmbeddedIndex() const { return mEmbeddingEventIndex; } + /** methods to handle stored information **/ + + void clearInfo() + { + mEventInfo.clear(); + }; + + template <typename T> + void putInfo(std::string const& key, T const& value) + { + mEventInfo.put<T>(key, value); + }; + + bool hasInfo(std::string const& key) const + { + return mEventInfo.has(key); + } + + template <typename T> + const T& getInfo(std::string const& key, bool& isvalid) const + { + o2::utils::RootSerializableKeyValueStore::GetState state; + auto& ref = mEventInfo.getRef<T>(key, state); + isvalid = (state == o2::utils::RootSerializableKeyValueStore::GetState::kOK); + if (!isvalid) { + LOG(WARNING) << "problem retrieving info '" << key << "': " << o2::utils::RootSerializableKeyValueStore::getStateString(state); + } + return ref; + }; + + /// prints a summary of info keys/types attached to this header + void printInfo() const + { + mEventInfo.print(); + } + /** methods **/ virtual void Reset(); @@ -54,6 +92,7 @@ class MCEventHeader : public FairMCEventHeader // store a view global properties that this event // had in the current simulation (which can be used quick filtering/searching) MCEventStats mEventStats{}; + o2::utils::RootSerializableKeyValueStore mEventInfo; ClassDefOverride(MCEventHeader, 2); diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCEventLabel.h b/DataFormats/simulation/include/SimulationDataFormat/MCEventLabel.h new file mode 100644 index 0000000000000..170aa89470979 --- /dev/null +++ b/DataFormats/simulation/include/SimulationDataFormat/MCEventLabel.h @@ -0,0 +1,122 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MCVEVENTLABEL_H +#define ALICEO2_MCVEVENTLABEL_H + +#include "GPUCommonRtypes.h" +#include <cmath> +#include <cassert> + +namespace o2 +{ +// Composed Label to encode MC event and the source (file) + weight + +class MCEventLabel +{ + private: + static constexpr uint32_t NotSet = 0xffffffff; + uint32_t mLabel = NotSet; ///< MC label encoding MCevent origin and fraction of correct contributors + + public: + static constexpr int nbitsEvID = 16; // number of bits reserved for MC event ID + static constexpr int nbitsSrcID = 7; // number of bits reserved for MC source ID + static constexpr int nbitsCorrW = sizeof(mLabel) * 8 - nbitsEvID - nbitsSrcID - 1; + + // Mask to extract MC event ID + static constexpr uint32_t MaskEvID = (0x1 << nbitsEvID) - 1; + // Mask to extract MC source ID + static constexpr uint32_t MaskSrcID = (0x1 << nbitsSrcID) - 1; + // Mask to extract MC source and event ID only + static constexpr uint32_t MaskSrcEvID = MaskSrcID | MaskEvID; + // Mask to extract MC correct contribution weight + static constexpr uint32_t MaskCorrW = (0x1 << nbitsCorrW) - 1; + static constexpr float WeightNorm = 1. / float(MaskCorrW); + + MCEventLabel(int evID, int srcID, float corrw = 1.0) { set(evID, srcID, corrw); } + MCEventLabel() = default; + ~MCEventLabel() = default; + + // check if label was assigned + bool isSet() const { return mLabel != NotSet; } + // check if label was not assigned + bool isEmpty() const { return mLabel == NotSet; } + + // conversion operator + operator uint32_t() const { return mLabel; } + // allow to retrieve bare label + uint32_t getRawValue() const { return mLabel; } + + // get only combined identifier, discarding weight info + uint32_t getIDOnly() const { return mLabel & MaskSrcEvID; } + + // compare + bool compare(const MCEventLabel& other, bool strict = false) const + { + return strict ? (getRawValue() == other.getRawValue()) : (getIDOnly() == other.getIDOnly()); + } + + // comparison operator, compares only label, not eventual weight or correctness info + bool operator==(const MCEventLabel& other) const { return compare(other); } + + // invalidate + void unset() { mLabel = NotSet; } + + /// compose label + void set(int evID, int srcID, float corrW) + { + uint32_t iw = static_cast<uint32_t>(std::round(corrW * MaskCorrW)); + assert(iw <= MaskCorrW); + mLabel = (iw << (nbitsEvID + nbitsSrcID)) | ((MaskSrcID & static_cast<uint32_t>(srcID)) << nbitsEvID) | (MaskEvID & static_cast<uint32_t>(evID)); + } + void setCorrWeight(float corrW) + { + uint32_t iw = static_cast<uint32_t>(std::round(corrW * MaskCorrW)); + assert(iw <= MaskCorrW); + mLabel = (mLabel & ((MaskSrcID << nbitsEvID) | MaskEvID)) | (iw << (nbitsEvID + nbitsSrcID)); + } + + int getEventID() const { return mLabel & MaskEvID; } + int getSourceID() const { return (mLabel >> nbitsEvID) & MaskSrcID; } + float getCorrWeight() const { return ((mLabel >> (nbitsEvID + nbitsSrcID)) & MaskCorrW) * WeightNorm; } + + void get(int& evID, int& srcID, float& corrW) + { + /// parse label + evID = getEventID(); + srcID = getSourceID(); + corrW = getCorrWeight(); + } + + void print() const; + + static constexpr uint32_t MaxSourceID() { return MaskSrcID; } + static constexpr uint32_t MaxEventID() { return MaskEvID; } + static constexpr float WeightPrecision() { return WeightNorm; } + ClassDefNV(MCEventLabel, 1); +}; +} // namespace o2 + +std::ostream& operator<<(std::ostream& os, const o2::MCEventLabel& c); + +namespace std +{ +// defining std::hash for MCEventLabel in order to be used with unordered_maps +template <> +struct hash<o2::MCEventLabel> { + public: + size_t operator()(o2::MCEventLabel const& label) const + { + return static_cast<uint32_t>(label); + } +}; +} // namespace std + +#endif diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h index 2f35edcbaa71e..307e398453884 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h @@ -15,6 +15,7 @@ #ifndef ALICEO2_DATA_MCTRACK_H_ #define ALICEO2_DATA_MCTRACK_H_ +#include "SimulationDataFormat/ParticleStatus.h" #include "DetectorsCommonDataFormats/DetID.h" #include "Rtypes.h" #include "TDatabasePDG.h" @@ -40,7 +41,7 @@ class MCTrackT MCTrackT(); /// Standard constructor - MCTrackT(Int_t pdgCode, Int_t motherID, Int_t firstDaighterID, Int_t lastDaughterID, + MCTrackT(Int_t pdgCode, Int_t motherID, Int_t secondMotherID, Int_t firstDaughterID, Int_t lastDaughterID, Double_t px, Double_t py, Double_t pz, Double_t x, Double_t y, Double_t z, Double_t t, Int_t nPoints); @@ -59,7 +60,9 @@ class MCTrackT /// Accessors Int_t GetPdgCode() const { return mPdgCode; } Int_t getMotherTrackId() const { return mMotherTrackId; } - bool isSecondary() const { return mMotherTrackId != -1; } + Int_t getSecondMotherTrackId() const { return mSecondMotherTrackId; } + bool isPrimary() const { return getProcess() == TMCProcess::kPPrimary; } + bool isSecondary() const { return !isPrimary(); } Int_t getFirstDaughterTrackId() const { return mFirstDaughterTrackId; } Int_t getLastDaughterTrackId() const { return mLastDaughterTrackId; } Double_t GetStartVertexMomentumX() const { return mStartVertexMomentumX; } @@ -108,10 +111,11 @@ class MCTrackT { double_t pmom = GetP(); double mz(mStartVertexMomentumZ); - if (pmom != TMath::Abs(mz)) + if (pmom != TMath::Abs(mz)) { return 0.5 * std::log((pmom + mz) / (pmom - mz)); - else + } else { return 1.e30; + } } Double_t GetTheta() const @@ -133,6 +137,7 @@ class MCTrackT void setHitMask(Int_t m) { ((PropEncoding)mProp).hitmask = m; } /// Modifiers void SetMotherTrackId(Int_t id) { mMotherTrackId = id; } + 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 @@ -152,8 +157,9 @@ class MCTrackT { int count = 0; for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) { - if (leftTrace(i)) + if (leftTrace(i)) { count++; + } } return count; } @@ -180,6 +186,14 @@ class MCTrackT /// get the production process (id) of this track int getProcess() const { return ((PropEncoding)mProp).process; } + void setToBeDone(bool f) + { + auto prop = ((PropEncoding)mProp); + prop.toBeDone = f; + mProp = prop.i; + } + bool getToBeDone() const { return ((PropEncoding)mProp).toBeDone; } + /// get the string representation of the production process const char* getProdProcessAsString() const; @@ -193,11 +207,12 @@ class MCTrackT /// PDG particle code Int_t mPdgCode; - /// Index of mother track. - Int_t mMotherTrackId; + /// Index of mother tracks + Int_t mMotherTrackId = -1; + Int_t mSecondMotherTrackId = -1; - Int_t mFirstDaughterTrackId; - Int_t mLastDaughterTrackId; + Int_t mFirstDaughterTrackId = -1; + Int_t mLastDaughterTrackId = -1; // hitmask stored as an int // if bit i is set it means that this track left a trace in detector i // we should have sizeof(int) < o2::base::DetId::nDetectors @@ -211,11 +226,12 @@ class MCTrackT struct { int storage : 1; // encoding whether to store this track to the output int process : 6; // encoding process that created this track (enough to store TMCProcess from ROOT) - int hitmask : 25; // encoding hits per detector + int hitmask : 24; // encoding hits per detector + int toBeDone : 1; // whether this (still) needs tracking --> we might more complete information to cover full ParticleStatus space }; }; - ClassDefNV(MCTrackT, 2); + ClassDefNV(MCTrackT, 3); }; template <typename T> @@ -248,6 +264,7 @@ template <typename T> inline MCTrackT<T>::MCTrackT() : mPdgCode(0), mMotherTrackId(-1), + mSecondMotherTrackId(-1), mFirstDaughterTrackId(-1), mLastDaughterTrackId(-1), mStartVertexMomentumX(0.), @@ -262,11 +279,12 @@ inline MCTrackT<T>::MCTrackT() } template <typename T> -inline MCTrackT<T>::MCTrackT(Int_t pdgCode, Int_t motherId, Int_t firstDaughterId, Int_t lastDaughterId, +inline MCTrackT<T>::MCTrackT(Int_t pdgCode, Int_t motherId, Int_t secondMotherId, Int_t firstDaughterId, Int_t lastDaughterId, Double_t px, Double_t py, Double_t pz, Double_t x, Double_t y, Double_t z, Double_t t, Int_t mask) : mPdgCode(pdgCode), mMotherTrackId(motherId), + mSecondMotherTrackId(secondMotherId), mFirstDaughterTrackId(firstDaughterId), mLastDaughterTrackId(lastDaughterId), mStartVertexMomentumX(px), @@ -284,6 +302,7 @@ template <typename T> inline MCTrackT<T>::MCTrackT(const TParticle& part) : mPdgCode(part.GetPdgCode()), mMotherTrackId(part.GetMother(0)), + mSecondMotherTrackId(part.GetMother(1)), mFirstDaughterTrackId(part.GetFirstDaughter()), mLastDaughterTrackId(part.GetLastDaughter()), mStartVertexMomentumX(part.Px()), @@ -297,6 +316,8 @@ inline MCTrackT<T>::MCTrackT(const TParticle& part) { // our convention is to communicate the process as (part) of the unique ID setProcess(part.GetUniqueID()); + // extract toBeDone flag + setToBeDone(part.TestBit(ParticleStatus::kToBeDone)); } template <typename T> diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCTruthContainer.h b/DataFormats/simulation/include/SimulationDataFormat/MCTruthContainer.h index 3fbac958bbbc1..506f24b56897a 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCTruthContainer.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCTruthContainer.h @@ -30,8 +30,10 @@ namespace o2 { +class MCCompLabel; namespace dataformats { + /// @struct MCTruthHeaderElement /// @brief Simple struct having information about truth elements for particular indices: /// how many associations we have and where they start in the storage @@ -138,6 +140,11 @@ class MCTruthContainer size_t getIndexedSize() const { return mHeaderArray.size(); } // return the number of elements managed in this container size_t getNElements() const { return mTruthArray.size(); } + // return unterlaying vector of elements + const std::vector<TruthElement>& getTruthArray() const + { + return mTruthArray; + } // get individual "view" container for a given data index // the caller can do modifications on this view (such as sorting) @@ -165,6 +172,16 @@ class MCTruthContainer mTruthArray.clear(); } + // clear and force freeing the memory + void clear_andfreememory() + { + clear(); + // this forces the desctructor being called on existing buffers + mHeaderArray = std::vector<MCTruthHeaderElement>(); + mTruthArray = std::vector<TruthElement>(); + mStreamerData = std::vector<char>(); + } + // add element for a particular dataindex // at the moment only strictly consecutive modes are supported void addElement(uint32_t dataindex, TruthElement const& element) @@ -255,7 +272,7 @@ class MCTruthContainer // fix headers for (uint32_t i = dataindex + 1; i < mHeaderArray.size(); ++i) { auto oldindex = mHeaderArray[i].index; - mHeaderArray[i].index = (oldindex != -1) ? oldindex + 1 : oldindex; + mHeaderArray[i].index = (oldindex != (uint32_t)-1) ? oldindex + 1 : oldindex; } } } @@ -310,7 +327,7 @@ class MCTruthContainer /// The flattened data starts with a specific header @ref FlatHeader describing /// size and content of the two vectors within the raw buffer. template <typename ContainerType> - size_t flatten_to(ContainerType& container) + size_t flatten_to(ContainerType& container) const { size_t bufferSize = sizeof(FlatHeader) + sizeof(MCTruthHeaderElement) * mHeaderArray.size() + sizeof(TruthElement) * mTruthArray.size(); container.resize((bufferSize / sizeof(typename ContainerType::value_type)) + ((bufferSize % sizeof(typename ContainerType::value_type)) > 0 ? 1 : 0)); @@ -405,6 +422,8 @@ class MCTruthContainer ClassDefNV(MCTruthContainer, 2); }; // end class +using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + } // namespace dataformats } // namespace o2 diff --git a/DataFormats/simulation/include/SimulationDataFormat/ParticleStatus.h b/DataFormats/simulation/include/SimulationDataFormat/ParticleStatus.h new file mode 100644 index 0000000000000..798ff293178f4 --- /dev/null +++ b/DataFormats/simulation/include/SimulationDataFormat/ParticleStatus.h @@ -0,0 +1,24 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PARTICLESTATUS_H_ +#define ALICEO2_SIMDATA_PARTICLESTATUS_H_ + +#include "TParticle.h" + +/// enumeration to define status bits for particles in simulation +enum ParticleStatus { kKeep = BIT(14), + kDaughters = BIT(15), + kToBeDone = BIT(16), + kPrimary = BIT(17), + kTransport = BIT(18), + kInhibited = BIT(19) }; + +#endif diff --git a/DataFormats/simulation/include/SimulationDataFormat/Stack.h b/DataFormats/simulation/include/SimulationDataFormat/Stack.h index 87e78a8411017..ff55904d9342c 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/Stack.h +++ b/DataFormats/simulation/include/SimulationDataFormat/Stack.h @@ -18,9 +18,9 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "FairGenericStack.h" #include "SimulationDataFormat/MCTrack.h" -#include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/TrackReference.h" #include "SimulationDataFormat/MCEventStats.h" +#include "SimulationDataFormat/ParticleStatus.h" #include "Rtypes.h" #include "TParticle.h" @@ -28,6 +28,7 @@ #include <memory> #include <stack> #include <utility> +#include <functional> class TClonesArray; class TRefArray; @@ -55,11 +56,6 @@ namespace data /// The storage of secondaries can be switched off. /// The storage of all mothers can be switched off. /// By default, the minimal number of hits is 1 and the energy cut is 0. -enum ParticleStatus { kKeep = BIT(14), - kDaughters = BIT(15), - kToBeDone = BIT(16), - kPrimary = BIT(17), - kTransport = BIT(18) }; class Stack : public FairGenericStack { public: @@ -92,6 +88,10 @@ class Stack : public FairGenericStack Double_t vx, Double_t vy, Double_t vz, Double_t time, Double_t polx, Double_t poly, Double_t polz, TMCProcess proc, Int_t& ntr, Double_t weight, Int_t is, Int_t secondParentId) override; + void PushTrack(Int_t toBeDone, Int_t parentID, Int_t pdgCode, Double_t px, Double_t py, Double_t pz, Double_t e, + Double_t vx, Double_t vy, Double_t vz, Double_t time, Double_t polx, Double_t poly, Double_t polz, + TMCProcess proc, Int_t& ntr, Double_t weight, Int_t is, Int_t secondParentId, Int_t daughter1Id, Int_t daughter2Id); + // similar function taking a particle void PushTrack(Int_t toBeDone, TParticle&); @@ -177,7 +177,6 @@ class Stack : public FairGenericStack /// Clone for worker (used in MT mode only) FairGenericStack* CloneStack() const override; - // methods concerning track references void addTrackReference(const o2::TrackReference& p); @@ -191,8 +190,9 @@ class Stack : public FairGenericStack Reset(); for (auto p : primaries) { Int_t doTrack = 0; - if (p.TestBit(ParticleStatus::kToBeDone)) + if (p.TestBit(ParticleStatus::kToBeDone)) { doTrack = 1; + } PushTrack(doTrack, p); } mNumberOfPrimaryParticles = primaries.size(); @@ -225,6 +225,8 @@ class Stack : public FairGenericStack /// update values in the current event header void updateEventStats(); + typedef std::function<bool(const TParticle& p, const std::vector<TParticle>& particles)> TransportFcn; + private: /// STL stack (FILO) used to handle the TParticles for tracking /// stack entries refer to @@ -232,9 +234,9 @@ class Stack : public FairGenericStack /// Array of TParticles (contains all TParticles put into or created /// by the transport) - std::vector<o2::MCTrack> mParticles; //! - std::vector<int> mTransportedIDs; //! prim + sec trackIDs transported for "current" primary - std::vector<int> mIndexOfPrimaries; //! index of primaries in mParticles + std::vector<o2::MCTrack> mParticles; //! + std::vector<int> mTransportedIDs; //! prim + sec trackIDs transported for "current" primary + std::vector<int> mIndexOfPrimaries; //! index of primaries in mParticles std::vector<int> mTrackIDtoParticlesEntry; //! an O(1) mapping of trackID to the entry of mParticles // the current TParticle object TParticle mCurrentParticle; @@ -281,11 +283,11 @@ class Stack : public FairGenericStack bool mIsExternalMode = false; // is stack an external factory or directly used inside simulation? + TransportFcn mTransportPrimary = [](const TParticle& p, const std::vector<TParticle>& particles) { return false; }; //! a function to inhibit the tracking of a particle + // storage for track references std::vector<o2::TrackReference>* mTrackRefs = nullptr; //! - o2::dataformats::MCTruthContainer<o2::TrackReference>* mIndexedTrackRefs = nullptr; //! - /// a pointer to the current MCEventStats object o2::dataformats::MCEventStats* mMCEventStats = nullptr; //! diff --git a/DataFormats/simulation/include/SimulationDataFormat/StackParam.h b/DataFormats/simulation/include/SimulationDataFormat/StackParam.h index 7c58b5327cf24..d5e940381039b 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/StackParam.h +++ b/DataFormats/simulation/include/SimulationDataFormat/StackParam.h @@ -23,6 +23,10 @@ namespace sim struct StackParam : public o2::conf::ConfigurableParamHelper<StackParam> { bool storeSecondaries = true; bool pruneKine = true; + std::string transportPrimary = "all"; + std::string transportPrimaryFileName = ""; + std::string transportPrimaryFuncName = ""; + bool transportPrimaryInvert = false; // boilerplate stuff + make principal key "Stack" O2ParamDef(StackParam, "Stack"); diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index abb7f9ed519f3..f6263a1accd26 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -15,7 +15,7 @@ #include <TFile.h> #include <iostream> #include <numeric> // for iota -#include <MathUtils/Cartesian3D.h> +#include <MathUtils/Cartesian.h> using namespace o2::steer; @@ -119,14 +119,14 @@ bool DigitizationContext::checkVertexCompatibility(bool verbose) const return true; } - auto checkVertexPair = [](Point3D<double> const& p1, Point3D<double> const& p2) -> bool { + auto checkVertexPair = [](math_utils::Point3D<double> const& p1, math_utils::Point3D<double> const& p2) -> bool { return (p2 - p1).Mag2() < 1E-6; }; std::vector<TChain*> kinematicschain; std::vector<TBranch*> headerbranches; std::vector<o2::dataformats::MCEventHeader*> headers; - std::vector<Point3D<double>> vertices; + std::vector<math_utils::Point3D<double>> vertices; initSimKinematicsChains(kinematicschain); bool consistent = true; if (kinematicschain.size() > 0) { @@ -212,7 +212,7 @@ void DigitizationContext::fillQED(std::string_view QEDprefix, std::vector<o2::In auto t = (TTree*)f.Get("o2sim"); if (!t) { LOG(ERROR) << "No QED kinematics found"; - return; + throw std::runtime_error("No QED kinematics found"); } auto numberQEDevents = t->GetEntries(); int eventID = 0; diff --git a/DataFormats/simulation/src/MCEventHeader.cxx b/DataFormats/simulation/src/MCEventHeader.cxx index 7991e3078c9ea..0d32dc18d019c 100644 --- a/DataFormats/simulation/src/MCEventHeader.cxx +++ b/DataFormats/simulation/src/MCEventHeader.cxx @@ -25,9 +25,11 @@ void MCEventHeader::Reset() { /** reset **/ + FairMCEventHeader::Reset(); + + clearInfo(); mEmbeddingFileName.clear(); mEmbeddingEventIndex = 0; - FairMCEventHeader::Reset(); } /*****************************************************************/ diff --git a/DataFormats/simulation/src/MCEventLabel.cxx b/DataFormats/simulation/src/MCEventLabel.cxx new file mode 100644 index 0000000000000..f12b6bf5d1dc1 --- /dev/null +++ b/DataFormats/simulation/src/MCEventLabel.cxx @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "SimulationDataFormat/MCEventLabel.h" +#include <iomanip> +#include <ios> +#include <iostream> +#include <cassert> + +using namespace o2; + +//_____________________________________________ +void MCEventLabel::print() const +{ + // print itself + std::cout << (MCEventLabel) * this << std::endl; +} + +//_____________________________________________ +std::ostream& operator<<(std::ostream& os, const o2::MCEventLabel& c) +{ + // stream itself + if (c.isSet()) { + os << '[' << c.getSourceID() << '/' << c.getEventID() << '/' << c.getCorrWeight() << ']'; + } else { + os << "[unset]"; + } + return os; +} diff --git a/DataFormats/simulation/src/SimulationDataLinkDef.h b/DataFormats/simulation/src/SimulationDataLinkDef.h index b48037d6cc9d4..8c18aee9724ee 100644 --- a/DataFormats/simulation/src/SimulationDataLinkDef.h +++ b/DataFormats/simulation/src/SimulationDataLinkDef.h @@ -32,6 +32,7 @@ #pragma link C++ class std::vector < o2::MCTrackT < double>> + ; #pragma link C++ class std::vector < o2::MCTrackT < float>> + ; #pragma link C++ class o2::MCCompLabel + ; +#pragma link C++ class o2::MCEventLabel + ; #pragma link C++ class o2::BaseHit + ; #pragma link C++ class o2::BasicXYZVHit < float, float, float> + ; @@ -47,6 +48,7 @@ #pragma link C++ class o2::dataformats::MCTruthContainer < o2::MCCompLabel > -; #pragma link C++ class std::vector < o2::dataformats::MCTruthContainer < o2::MCCompLabel>> + ; #pragma link C++ class std::vector < o2::MCCompLabel> + ; +#pragma link C++ class std::vector < o2::MCEventLabel> + ; #pragma link C++ class std::vector < o2::dataformats::MCTruthHeaderElement> + ; #pragma link C++ class o2::SimTrackStatus + ; @@ -66,4 +68,6 @@ #pragma link C++ class o2::dataformats::MCEventStats + ; #pragma link C++ class o2::dataformats::MCEventHeader + ; +#pragma link C++ class o2::dataformats::IOMCTruthContainerView + ; + #endif diff --git a/DataFormats/simulation/src/Stack.cxx b/DataFormats/simulation/src/Stack.cxx index b4c9db1970e45..41f5f8fd1acd5 100644 --- a/DataFormats/simulation/src/Stack.cxx +++ b/DataFormats/simulation/src/Stack.cxx @@ -22,6 +22,8 @@ #include "FairLogger.h" // for FairLogger #include "FairRootManager.h" #include "SimulationDataFormat/BaseHits.h" +#include "SimulationDataFormat/StackParam.h" +#include "CommonUtils/ConfigurationMacroHelper.h" #include "TLorentzVector.h" // for TLorentzVector #include "TParticle.h" // for TParticle @@ -72,12 +74,43 @@ Stack::Stack(Int_t size) mMinHits(1), mEnergyCut(0.), mTrackRefs(new std::vector<o2::TrackReference>), - mIndexedTrackRefs(new typename std::remove_pointer<decltype(mIndexedTrackRefs)>::type), mIsG4Like(false) { auto vmc = TVirtualMC::GetMC(); - if (vmc && strcmp(vmc->GetName(), "TGeant4") == 0) { - mIsG4Like = true; + if (vmc) { + mIsG4Like = !(vmc->SecondariesAreOrdered()); + } + + auto& param = o2::sim::StackParam::Instance(); + LOG(INFO) << param; + TransportFcn transportPrimary; + if (param.transportPrimary.compare("none") == 0) { + transportPrimary = [](const TParticle& p, const std::vector<TParticle>& particles) { + return false; + }; + } else if (param.transportPrimary.compare("all") == 0) { + transportPrimary = [](const TParticle& p, const std::vector<TParticle>& particles) { + return true; + }; + } else if (param.transportPrimary.compare("barrel") == 0) { + transportPrimary = [](const TParticle& p, const std::vector<TParticle>& particles) { + return (std::fabs(p.Eta()) < 2.0); + }; + } else if (param.transportPrimary.compare("external") == 0) { + transportPrimary = o2::conf::GetFromMacro<o2::data::Stack::TransportFcn>(param.transportPrimaryFileName, + param.transportPrimaryFuncName, + "o2::data::Stack::TransportFcn", "stack_transport_primary"); + if (!mTransportPrimary) { + LOG(FATAL) << "Failed to retrieve external \'transportPrimary\' function: problem with configuration "; + } + } else { + LOG(FATAL) << "unsupported \'trasportPrimary\' mode: " << param.transportPrimary; + } + + if (param.transportPrimaryInvert) { + mTransportPrimary = [transportPrimary](const TParticle& p, const std::vector<TParticle>& particles) { return !transportPrimary; }; + } else { + mTransportPrimary = transportPrimary; } } @@ -148,7 +181,14 @@ void Stack::PushTrack(Int_t toBeDone, Int_t parentId, Int_t pdgCode, Double_t px void Stack::PushTrack(Int_t toBeDone, Int_t parentId, Int_t pdgCode, Double_t px, Double_t py, Double_t pz, Double_t e, Double_t vx, Double_t vy, Double_t vz, Double_t time, Double_t polx, Double_t poly, Double_t polz, - TMCProcess proc, Int_t& ntr, Double_t weight, Int_t is, Int_t secondparentID) + TMCProcess proc, Int_t& ntr, Double_t weight, Int_t is, Int_t secondparentId) +{ + PushTrack(toBeDone, parentId, pdgCode, px, py, pz, e, vx, vy, vz, time, polx, poly, polz, proc, ntr, weight, is, secondparentId, -1, -1); +} + +void Stack::PushTrack(Int_t toBeDone, Int_t parentId, Int_t pdgCode, Double_t px, Double_t py, Double_t pz, Double_t e, + Double_t vx, Double_t vy, Double_t vz, Double_t time, Double_t polx, Double_t poly, Double_t polz, + TMCProcess proc, Int_t& ntr, Double_t weight, Int_t is, Int_t secondparentId, Int_t daughter1Id, Int_t daughter2Id) { // printf("Pushing %s toBeDone %5d parentId %5d pdgCode %5d is %5d entries %5d \n", // proc == kPPrimary ? "Primary: " : "Secondary: ", @@ -167,11 +207,10 @@ void Stack::PushTrack(Int_t toBeDone, Int_t parentId, Int_t pdgCode, Double_t px Int_t trackId = mNumberOfEntriesInParticles; // Set track variable ntr = trackId; - Int_t nPoints = 0; - Int_t daughter1Id = -1; - Int_t daughter2Id = -1; + // Int_t daughter1Id = -1; + // Int_t daughter2Id = -1; Int_t iStatus = (proc == kPPrimary) ? is : trackId; - TParticle p(pdgCode, iStatus, parentId, nPoints, daughter1Id, daughter2Id, px, py, pz, e, vx, vy, vz, time); + TParticle p(pdgCode, iStatus, parentId, secondparentId, daughter1Id, daughter2Id, px, py, pz, e, vx, vy, vz, time); p.SetPolarisation(polx, poly, polz); p.SetWeight(weight); p.SetUniqueID(proc); // using the unique ID to transfer process ID @@ -191,8 +230,13 @@ void Stack::PushTrack(Int_t toBeDone, Int_t parentId, Int_t pdgCode, Double_t px p.SetBit(ParticleStatus::kKeep); p.SetBit(ParticleStatus::kPrimary); if (toBeDone == 1) { - p.SetBit(ParticleStatus::kToBeDone, 1); - mNumberOfPrimariesforTracking++; + if (mTransportPrimary(p, mPrimaryParticles)) { + p.SetBit(ParticleStatus::kToBeDone, 1); + mNumberOfPrimariesforTracking++; + } else { + p.SetBit(ParticleStatus::kToBeDone, 0); + p.SetBit(ParticleStatus::kInhibited, 1); + } } else { p.SetBit(ParticleStatus::kToBeDone, 0); } @@ -225,8 +269,9 @@ void Stack::PushTrack(int toBeDone, TParticle& p) mNumberOfPrimaryParticles++; mPrimaryParticles.push_back(p); // Push particle on the stack - if (p.TestBit(ParticleStatus::kPrimary) && p.TestBit(ParticleStatus::kToBeDone)) + if (p.TestBit(ParticleStatus::kPrimary) && p.TestBit(ParticleStatus::kToBeDone)) { mNumberOfPrimariesforTracking++; + } mStack.push(p); mTracks->emplace_back(p); } @@ -449,8 +494,9 @@ void Stack::FinishPrimary() for (Int_t idTrack = imin; idTrack < imax; idTrack++) { Int_t index1 = mTrackIDtoParticlesEntry[idTrack]; Int_t index2 = indicesKept[index1]; - if (index2 == -1) + if (index2 == -1) { continue; + } Int_t index3 = (mIsG4Like) ? invreOrderedIndices[index2] : index2; mIndexMap[idTrack] = index3 + indexoffset; } @@ -517,14 +563,6 @@ void Stack::UpdateTrackIndex(TRefArray* detList) return a.getTrackID() < b.getTrackID(); }); - // make final indexed container for track references - // fill empty - for (auto& ref : *mTrackRefs) { - if (ref.getTrackID() >= 0) { - mIndexedTrackRefs->addElement(ref.getTrackID(), ref); - } - } - for (auto det : mActiveDetectors) { // update the track indices by delegating to specialized detector functions det->updateHitTrackIndices(mIndexMap); @@ -535,6 +573,7 @@ void Stack::UpdateTrackIndex(TRefArray* detList) void Stack::Reset() { + mIndexOfCurrentTrack = -1; mNumberOfPrimaryParticles = mNumberOfEntriesInParticles = mNumberOfEntriesInTracks = 0; while (!mStack.empty()) { @@ -551,7 +590,6 @@ void Stack::Reset() mNumberOfPrimariesPopped = 0; mPrimaryParticles.clear(); mTrackRefs->clear(); - mIndexedTrackRefs->clear(); mTrackIDtoParticlesEntry.clear(); mHitCounter = 0; } @@ -560,7 +598,6 @@ void Stack::Register() { FairRootManager::Instance()->RegisterAny("MCTrack", mTracks, kTRUE); FairRootManager::Instance()->RegisterAny("TrackRefs", mTrackRefs, kTRUE); - FairRootManager::Instance()->RegisterAny("IndexedTrackRefs", mIndexedTrackRefs, kTRUE); } void Stack::Print(Int_t iVerbose) const @@ -578,8 +615,9 @@ void Stack::Print(Int_t iVerbose) const void Stack::Print(Option_t* option) const { Int_t verbose = 0; - if (option) + if (option) { verbose = 1; + } Print(verbose); } @@ -697,8 +735,9 @@ bool Stack::selectTracks() bool Stack::isPrimary(const MCTrack& part) { /** check if primary **/ - if (part.getProcess() == kPPrimary || part.getMotherTrackId() < 0) + if (part.getProcess() == kPPrimary || part.getMotherTrackId() < 0) { return true; + } /** not primary **/ return false; } @@ -709,13 +748,15 @@ bool Stack::isFromPrimaryDecayChain(const MCTrack& part) decay chain of a primary particle **/ /** check if from decay **/ - if (part.getProcess() != kPDecay) + if (part.getProcess() != kPDecay) { return false; + } /** check if mother is primary **/ auto imother = part.getMotherTrackId(); auto mother = mParticles[imother]; - if (isPrimary(mother)) + if (isPrimary(mother)) { return true; + } /** else check if mother is from primary decay **/ return isFromPrimaryDecayChain(mother); } @@ -727,13 +768,15 @@ bool Stack::isFromPrimaryPairProduction(const MCTrack& part) belonging to the primary decay chain **/ /** check if from pair production **/ - if (part.getProcess() != kPPair) + if (part.getProcess() != kPPair) { return false; + } /** check if mother is primary **/ auto imother = part.getMotherTrackId(); auto mother = mParticles[imother]; - if (isPrimary(mother)) + if (isPrimary(mother)) { return true; + } /** else check if mother is from primary decay **/ return isFromPrimaryDecayChain(mother); } @@ -745,12 +788,15 @@ bool Stack::keepPhysics(const MCTrack& part) // by physics analysis. Decision is put here. // - if (isPrimary(part)) + if (isPrimary(part)) { return true; - if (isFromPrimaryDecayChain(part)) + } + if (isFromPrimaryDecayChain(part)) { return true; - if (isFromPrimaryPairProduction(part)) + } + if (isFromPrimaryPairProduction(part)) { return true; + } return false; } @@ -809,8 +855,9 @@ void Stack::ReorderKine(std::vector<MCTrack>& particles, std::vector<int>& reOrd int indexoffset = mTracks->size(); Int_t index = 0; Int_t imoOld = 0; - for (Int_t i = 0; i < ntr; i++) + for (Int_t i = 0; i < ntr; i++) { reOrderedIndices[i] = i; + } for (Int_t i = -1; i < ntr; i++) { if (i != -1) { diff --git a/DataFormats/simulation/test/testMCEventLabel.cxx b/DataFormats/simulation/test/testMCEventLabel.cxx new file mode 100644 index 0000000000000..0ea2c8af288a9 --- /dev/null +++ b/DataFormats/simulation/test/testMCEventLabel.cxx @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MCEventLabel class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "Framework/Logger.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include <TRandom.h> + +using namespace o2; + +BOOST_AUTO_TEST_CASE(MCEventLabel_test) +{ + MCEventLabel lbUndef; + BOOST_CHECK(!lbUndef.isSet()); // test invalid label status + + for (int itr = 0; itr < 100; itr++) { + int ev = gRandom->Integer(MCEventLabel::MaxEventID()), src = gRandom->Integer(MCEventLabel::MaxSourceID()); + float w = gRandom->Rndm(); + MCEventLabel lb(ev, src, w); + LOG(INFO) << "Input: [" << src << '/' << ev << '/' << w << ']'; + LOG(INFO) << "Encoded: " << lb << " (packed: " << uint32_t(lb) << ")"; + int evE, srcE; + float wE; + lb.get(evE, srcE, wE); + LOG(INFO) << "Decoded: [" << srcE << '/' << evE << '/' << wE << ']'; + BOOST_CHECK(ev == evE && src == srcE && std::abs(w - wE) < MCEventLabel::WeightPrecision()); + } +} diff --git a/DataFormats/simulation/test/testMCTruthContainer.cxx b/DataFormats/simulation/test/testMCTruthContainer.cxx index 7045309196d2b..8e5b66ba826d6 100644 --- a/DataFormats/simulation/test/testMCTruthContainer.cxx +++ b/DataFormats/simulation/test/testMCTruthContainer.cxx @@ -12,10 +12,14 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/LabelContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" #include <algorithm> #include <iostream> +#include <TFile.h> +#include <TTree.h> namespace o2 { @@ -180,6 +184,19 @@ BOOST_AUTO_TEST_CASE(MCTruthContainer_flatten) BOOST_CHECK(restoredContainer.getElement(1) == 2); BOOST_CHECK(restoredContainer.getElement(2) == 1); BOOST_CHECK(restoredContainer.getElement(3) == 10); + + // check the special version ConstMCTruthContainer + using ConstMCTruthContainer = dataformats::ConstMCTruthContainer<TruthElement>; + ConstMCTruthContainer cc; + container.flatten_to(cc); + + BOOST_CHECK(cc.getIndexedSize() == container.getIndexedSize()); + BOOST_CHECK(cc.getNElements() == container.getNElements()); + BOOST_CHECK(cc.getLabels(0).size() == container.getLabels(0).size()); + BOOST_CHECK(cc.getLabels(1).size() == container.getLabels(1).size()); + BOOST_CHECK(cc.getLabels(2).size() == container.getLabels(2).size()); + BOOST_CHECK(cc.getLabels(2)[0] == container.getLabels(2)[0]); + BOOST_CHECK(cc.getLabels(2)[0] == 10); } BOOST_AUTO_TEST_CASE(LabelContainer_noncont) @@ -305,4 +322,52 @@ BOOST_AUTO_TEST_CASE(MCTruthContainer_move) BOOST_CHECK(container.getNElements() == 4); } +BOOST_AUTO_TEST_CASE(MCTruthContainer_ROOTIO) +{ + using TruthElement = o2::MCCompLabel; + using Container = dataformats::MCTruthContainer<TruthElement>; + Container container; + const size_t BIGSIZE{1000000}; + for (int i = 0; i < BIGSIZE; ++i) { + container.addElement(i, TruthElement(i, i, i)); + container.addElement(i, TruthElement(i + 1, i, i)); + } + std::vector<char> buffer; + container.flatten_to(buffer); + + // We use the special IO split container to stream to a file and back + dataformats::IOMCTruthContainerView io(buffer); + { + TFile f("tmp2.root", "RECREATE"); + TTree tree("o2sim", "o2sim"); + auto br = tree.Branch("Labels", &io, 32000, 2); + tree.Fill(); + tree.Write(); + f.Close(); + } + + // read back + TFile f2("tmp2.root", "OPEN"); + auto tree2 = (TTree*)f2.Get("o2sim"); + dataformats::IOMCTruthContainerView* io2 = nullptr; + auto br2 = tree2->GetBranch("Labels"); + BOOST_CHECK(br2 != nullptr); + br2->SetAddress(&io2); + br2->GetEntry(0); + + // make a const MC label container out of it + using ConstMCTruthContainer = dataformats::ConstMCTruthContainer<TruthElement>; + ConstMCTruthContainer cc; + io2->copyandflatten(cc); + + BOOST_CHECK(cc.getNElements() == BIGSIZE * 2); + BOOST_CHECK(cc.getIndexedSize() == BIGSIZE); + BOOST_CHECK(cc.getLabels(0).size() == 2); + BOOST_CHECK(cc.getLabels(0)[0] == TruthElement(0, 0, 0)); + BOOST_CHECK(cc.getLabels(0)[1] == TruthElement(1, 0, 0)); + BOOST_CHECK(cc.getLabels(BIGSIZE - 1).size() == 2); + BOOST_CHECK(cc.getLabels(BIGSIZE - 1)[0] == TruthElement(BIGSIZE - 1, BIGSIZE - 1, BIGSIZE - 1)); + BOOST_CHECK(cc.getLabels(BIGSIZE - 1)[1] == TruthElement(BIGSIZE, BIGSIZE - 1, BIGSIZE - 1)); +} + } // namespace o2 diff --git a/Detectors/AOD/CMakeLists.txt b/Detectors/AOD/CMakeLists.txt new file mode 100644 index 0000000000000..238591fed5a55 --- /dev/null +++ b/Detectors/AOD/CMakeLists.txt @@ -0,0 +1,64 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + AODProducerWorkflow + SOURCES src/AODProducerWorkflow.cxx + src/AODProducerWorkflowSpec.cxx + PUBLIC_LINK_LIBRARIES + O2::AnalysisDataModel + O2::DetectorsVertexing + O2::Framework + O2::FT0Workflow + O2::GlobalTracking + O2::GlobalTrackingWorkflow + O2::ITSMFTWorkflow + O2::ITStracking + O2::ITSWorkflow + O2::SimulationDataFormat + O2::Steer + O2::TOFWorkflow + O2::TPCWorkflow + O2::CCDB +) + +o2_add_executable( + workflow + COMPONENT_NAME aod-producer + SOURCES src/aod-producer-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::AODProducerWorkflow +) + +o2_add_executable( + standalone-aod-producer + COMPONENT_NAME reco + SOURCES src/StandaloneAODProducer.cxx + PUBLIC_LINK_LIBRARIES + O2::DataFormatsTPC + O2::DataFormatsITSMFT + O2::DataFormatsITS + O2::DataFormatsFT0 + O2::DataFormatsTOF + O2::ITSReconstruction + O2::FT0Reconstruction + O2::TPCFastTransformation + O2::GPUTracking + O2::TPCBase + O2::TPCReconstruction + O2::TOFBase + O2::TOFCalibration + O2::SimConfig + O2::DataFormatsFT0 + O2::AnalysisDataModel + O2::Steer +) + + + diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflow.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflow.h new file mode 100644 index 0000000000000..c9ce6f23400b3 --- /dev/null +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflow.h @@ -0,0 +1,24 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_AODPRODUCER_WORKFLOW_H +#define O2_AODPRODUCER_WORKFLOW_H + +/// @file AODProducerWorkflow.h + +#include "Framework/WorkflowSpec.h" + +namespace o2::aodproducer +{ + +framework::WorkflowSpec getAODProducerWorkflow(); + +} // namespace o2::aodproducer +#endif diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h new file mode 100644 index 0000000000000..1560217d9ae17 --- /dev/null +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -0,0 +1,162 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 AODProducerWorkflowSpec.h + +#ifndef O2_AODPRODUCER_WORKFLOW_SPEC +#define O2_AODPRODUCER_WORKFLOW_SPEC + +#include "DataFormatsFT0/RecPoints.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "GlobalTrackingWorkflow/PrimaryVertexingSpec.h" +#include "TStopwatch.h" +#include <CCDB/BasicCCDBManager.h> +#include <string> +#include <vector> + +using namespace o2::framework; + +namespace o2::aodproducer +{ + +using TracksTable = o2::soa::Table<o2::aod::track::CollisionId, + o2::aod::track::TrackType, + o2::aod::track::X, + o2::aod::track::Alpha, + o2::aod::track::Y, + o2::aod::track::Z, + o2::aod::track::Snp, + o2::aod::track::Tgl, + o2::aod::track::Signed1Pt, + o2::aod::track::SigmaY, + o2::aod::track::SigmaZ, + o2::aod::track::SigmaSnp, + o2::aod::track::SigmaTgl, + o2::aod::track::Sigma1Pt, + o2::aod::track::RhoZY, + o2::aod::track::RhoSnpY, + o2::aod::track::RhoSnpZ, + o2::aod::track::RhoTglY, + o2::aod::track::RhoTglZ, + o2::aod::track::RhoTglSnp, + o2::aod::track::Rho1PtY, + o2::aod::track::Rho1PtZ, + o2::aod::track::Rho1PtSnp, + o2::aod::track::Rho1PtTgl, + o2::aod::track::TPCInnerParam, + o2::aod::track::Flags, + o2::aod::track::ITSClusterMap, + o2::aod::track::TPCNClsFindable, + o2::aod::track::TPCNClsFindableMinusFound, + o2::aod::track::TPCNClsFindableMinusCrossedRows, + o2::aod::track::TPCNClsShared, + o2::aod::track::TRDPattern, + o2::aod::track::ITSChi2NCl, + o2::aod::track::TPCChi2NCl, + o2::aod::track::TRDChi2, + o2::aod::track::TOFChi2, + o2::aod::track::TPCSignal, + o2::aod::track::TRDSignal, + o2::aod::track::TOFSignal, + o2::aod::track::Length, + o2::aod::track::TOFExpMom, + o2::aod::track::TrackEtaEMCAL, + o2::aod::track::TrackPhiEMCAL>; + +using MCParticlesTable = o2::soa::Table<o2::aod::mcparticle::McCollisionId, + o2::aod::mcparticle::PdgCode, + o2::aod::mcparticle::StatusCode, + o2::aod::mcparticle::Flags, + o2::aod::mcparticle::Mother0, + o2::aod::mcparticle::Mother1, + o2::aod::mcparticle::Daughter0, + o2::aod::mcparticle::Daughter1, + o2::aod::mcparticle::Weight, + o2::aod::mcparticle::Px, + o2::aod::mcparticle::Py, + o2::aod::mcparticle::Pz, + o2::aod::mcparticle::E, + o2::aod::mcparticle::Vx, + o2::aod::mcparticle::Vy, + o2::aod::mcparticle::Vz, + o2::aod::mcparticle::Vt>; + +class AODProducerWorkflowDPL : public Task +{ + public: + AODProducerWorkflowDPL() = default; + ~AODProducerWorkflowDPL() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; + + private: + int mFillTracksITS = 1; + int mFillTracksTPC = 1; + int mFillTracksITSTPC = 1; + int mTFNumber = -1; + int mTruncate = 1; + TStopwatch mTimer; + + // truncation is enabled by default + uint32_t mCollisionPosition = 0xFFFFFFF0; // 19 bits mantissa + uint32_t mCollisionPositionCov = 0xFFFFE000; // 10 bits mantissa + uint32_t mTrackX = 0xFFFFFFF0; // 19 bits + uint32_t mTrackAlpha = 0xFFFFFFF0; // 19 bits + uint32_t mTrackSnp = 0xFFFFFF00; // 15 bits + uint32_t mTrackTgl = 0xFFFFFF00; // 15 bits + uint32_t mTrack1Pt = 0xFFFFFC00; // 13 bits + uint32_t mTrackCovDiag = 0xFFFFFF00; // 15 bits + uint32_t mTrackCovOffDiag = 0xFFFF0000; // 7 bits + uint32_t mTrackSignal = 0xFFFFFF00; // 15 bits + uint32_t mTrackPosEMCAL = 0xFFFFFF00; // 15 bits + uint32_t mTracklets = 0xFFFFFF00; // 15 bits + uint32_t mMcParticleW = 0xFFFFFFF0; // 19 bits + uint32_t mMcParticlePos = 0xFFFFFFF0; // 19 bits + uint32_t mMcParticleMom = 0xFFFFFFF0; // 19 bits + uint32_t mCaloAmp = 0xFFFFFF00; // 15 bits + uint32_t mCaloTime = 0xFFFFFF00; // 15 bits + uint32_t mMuonTr1P = 0xFFFFFC00; // 13 bits + uint32_t mMuonTrThetaX = 0xFFFFFF00; // 15 bits + uint32_t mMuonTrThetaY = 0xFFFFFF00; // 15 bits + uint32_t mMuonTrZmu = 0xFFFFFFF0; // 19 bits + uint32_t mMuonTrBend = 0xFFFFFFF0; // 19 bits + uint32_t mMuonTrNonBend = 0xFFFFFFF0; // 19 bits + uint32_t mMuonTrCov = 0xFFFF0000; // 7 bits + uint32_t mMuonCl = 0xFFFFFF00; // 15 bits + uint32_t mMuonClErr = 0xFFFF0000; // 7 bits + uint32_t mV0Time = 0xFFFFF000; // 11 bits + uint32_t mFDDTime = 0xFFFFF000; // 11 bits + uint32_t mT0Time = 0xFFFFFF00; // 15 bits + uint32_t mV0Amplitude = 0xFFFFF000; // 11 bits + uint32_t mFDDAmplitude = 0xFFFFF000; // 11 bits + uint32_t mT0Amplitude = 0xFFFFF000; // 11 bits + + uint64_t maxGlBC = 0; + uint64_t minGlBC = INT64_MAX; + + void findMinMaxBc(gsl::span<const o2::ft0::RecPoints>& ft0RecPoints, gsl::span<const o2::vertexing::PVertex>& primVertices, const std::vector<o2::InteractionTimeRecord>& mcRecords); + int64_t getTFNumber(uint64_t firstVtxGlBC, int runNumber); + + template <typename TracksType, typename TracksCursorType> + void fillTracksTable(const TracksType& tracks, std::vector<int>& vCollRefs, const TracksCursorType& tracksCursor, int trackType); + + float TruncateFloatFraction(float x, uint32_t mask); +}; + +/// create a processor spec +framework::DataProcessorSpec getAODProducerWorkflowSpec(); + +} // namespace o2::aodproducer + +#endif /* O2_AODPRODUCER_WORKFLOW_SPEC */ diff --git a/Detectors/AOD/src/AODProducerWorkflow.cxx b/Detectors/AOD/src/AODProducerWorkflow.cxx new file mode 100644 index 0000000000000..5925e6362234c --- /dev/null +++ b/Detectors/AOD/src/AODProducerWorkflow.cxx @@ -0,0 +1,55 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 AODProducerWorkflow.cxx + +#include "Algorithm/RangeTokenizer.h" +#include "AODProducerWorkflow/AODProducerWorkflow.h" +#include "AODProducerWorkflow/AODProducerWorkflowSpec.h" +#include "DataFormatsTPC/Constants.h" +#include "FT0Workflow/DigitReaderSpec.h" +#include "FT0Workflow/ReconstructionSpec.h" +#include "GlobalTracking/MatchTPCITSParams.h" +#include "GlobalTrackingWorkflow/MatchTPCITSWorkflow.h" +#include "GlobalTrackingWorkflow/PrimaryVertexingSpec.h" +#include "GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h" +#include "GlobalTrackingWorkflow/TPCITSMatchingSpec.h" +#include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" +#include "GlobalTrackingWorkflow/TrackWriterTPCITSSpec.h" +#include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "ITSWorkflow/TrackReaderSpec.h" +#include "TPCWorkflow/PublisherSpec.h" +#include "TPCWorkflow/TrackReaderSpec.h" + +namespace o2::aodproducer +{ + +framework::WorkflowSpec getAODProducerWorkflow() +{ + // TODO: + // switch to configurable parameters (?) + bool useMC = true; + + // FIXME: + // switch (?) from o2::ft0::getReconstructionSpec to RecPointReader + // (which does not return RECCHDATA at the moment) + framework::WorkflowSpec specs{ + o2::vertexing::getPrimaryVertexReaderSpec(useMC), + o2::globaltracking::getTrackTPCITSReaderSpec(useMC), + o2::its::getITSTrackReaderSpec(useMC), + o2::tpc::getTPCTrackReaderSpec(useMC), + o2::ft0::getDigitReaderSpec(useMC), + o2::ft0::getReconstructionSpec(useMC), + o2::aodproducer::getAODProducerWorkflowSpec()}; + + return specs; +} + +} // namespace o2::aodproducer diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx new file mode 100644 index 0000000000000..1db42b87b195d --- /dev/null +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -0,0 +1,745 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 AODProducerWorkflowSpec.cxx + +#include "AODProducerWorkflow/AODProducerWorkflowSpec.h" +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include <CCDB/BasicCCDBManager.h> +#include "CommonDataFormat/InteractionRecord.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisHelpers.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 "GlobalTracking/MatchTOF.h" +#include "GlobalTrackingWorkflow/PrimaryVertexingSpec.h" +#include "Headers/DataHeader.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "Steer/MCKinematicsReader.h" +#include "TMath.h" +#include <map> +#include <vector> + +using namespace o2::framework; + +namespace o2::aodproducer +{ + +float AODProducerWorkflowDPL::TruncateFloatFraction(float x, uint32_t mask) +{ + union { + float y; + uint32_t iy; + } myu; + myu.y = x; + myu.iy &= mask; + return myu.y; +} + +void AODProducerWorkflowDPL::findMinMaxBc(gsl::span<const o2::ft0::RecPoints>& ft0RecPoints, gsl::span<const o2::vertexing::PVertex>& primVertices, const std::vector<o2::InteractionTimeRecord>& mcRecords) +{ + for (auto& ft0RecPoint : ft0RecPoints) { + uint64_t bc = ft0RecPoint.getInteractionRecord().orbit * o2::constants::lhc::LHCMaxBunches + ft0RecPoint.getInteractionRecord().bc; + if (minGlBC > bc) { + minGlBC = bc; + } + if (maxGlBC < bc) { + maxGlBC = bc; + } + } + + for (auto& vertex : primVertices) { + const InteractionRecord& colIRMin = vertex.getIRMin(); + const InteractionRecord& colIRMax = vertex.getIRMax(); + uint64_t colBCMin = colIRMin.orbit * o2::constants::lhc::LHCMaxBunches + colIRMin.bc; + uint64_t colBCMax = colIRMax.orbit * o2::constants::lhc::LHCMaxBunches + colIRMax.bc; + if (minGlBC > colBCMin) { + minGlBC = colBCMin; + } + if (maxGlBC < colBCMax) { + maxGlBC = colBCMax; + } + } + + for (auto& rec : mcRecords) { + uint64_t bc = rec.bc + rec.orbit * o2::constants::lhc::LHCMaxBunches; + if (minGlBC > bc) { + minGlBC = bc; + } + if (maxGlBC < bc) { + maxGlBC = bc; + } + } +} + +int64_t AODProducerWorkflowDPL::getTFNumber(uint64_t firstVtxGlBC, int runNumber) +{ + // FIXME: + // check if this code is correct + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + o2::ccdb::CcdbApi ccdb_api; + const std::string rct_path = "RCT/RunInformation/"; + const std::string start_orbit_path = "Trigger/StartOrbit"; + const std::string url = "http://ccdb-test.cern.ch:8080"; + + mgr.setURL(url); + ccdb_api.init(url); + + std::map<int, int>* mapStartOrbit = mgr.get<std::map<int, int>>(start_orbit_path); + int64_t ts = 0; + std::map<std::string, std::string> metadata; + std::map<std::string, std::string> 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; + + // firstRec --> calculated using `minimal` global BC in the simulation (see AODProducerWorkflowDPL::findMinMaxBc) + // firstVtxGlBC --> calculated using global BC correspinding to the first prim. vertex + + uint32_t initialOrbit = mapStartOrbit->at(runNumber); + uint16_t firstRecBC = minGlBC % o2::constants::lhc::LHCMaxBunches; + uint32_t firstRecOrbit = minGlBC / o2::constants::lhc::LHCMaxBunches; + uint16_t firstVtxBC = firstVtxGlBC % o2::constants::lhc::LHCMaxBunches; + uint32_t firstVtxOrbit = firstVtxGlBC / o2::constants::lhc::LHCMaxBunches; + const o2::InteractionRecord firstRec(firstRecBC, firstRecOrbit); + const o2::InteractionRecord firstVtx(firstVtxBC, firstVtxOrbit); + ts += (firstVtx - firstRec).bc2ns() / 1000000; + + return ts; +}; + +template <typename TracksType, typename TracksCursorType> +void AODProducerWorkflowDPL::fillTracksTable(const TracksType& tracks, std::vector<int>& vCollRefs, const TracksCursorType& tracksCursor, int trackType) +{ + for (int i = 0; i < tracks.size(); i++) { + auto& track = tracks[i]; + int collisionID = vCollRefs[i]; + + float tpcInnerParam = 0.f; + uint32_t flags = 0; + uint8_t itsClusterMap = 0; + uint8_t tpcNClsFindable = 0; + int8_t tpcNClsFindableMinusFound = 0; + int8_t tpcNClsFindableMinusCrossedRows = 0; + uint8_t tpcNClsShared = 0; + uint8_t trdPattern = 0; + float itsChi2NCl = -999.f; + float tpcChi2NCl = -999.f; + float trdChi2 = -999.f; + float tofChi2 = -999.f; + float tpcSignal = -999.f; + float trdSignal = -999.f; + float tofSignal = -999.f; + float length = -999.f; + float tofExpMom = -999.f; + float trackEtaEMCAL = -999.f; + float trackPhiEMCAL = -999.f; + + // filling available columns for different track types + // FIXME: + // is there a more nice/simple way to do this? + std::variant<o2::its::TrackITS, o2::tpc::TrackTPC, o2::dataformats::TrackTPCITS> tmp = track; + std::visit( + overloaded{ + [&](o2::its::TrackITS itsTrack) { + itsClusterMap = itsTrack.getPattern(); + }, + [&](o2::tpc::TrackTPC tpcTrack) { + tpcChi2NCl = tpcTrack.getNClusters() ? tpcTrack.getChi2() / tpcTrack.getNClusters() : 0; + tpcSignal = tpcTrack.getdEdx().dEdxTotTPC; + tpcNClsFindable = tpcTrack.getNClusters(); + }, + [&](o2::dataformats::TrackTPCITS itsTpcTrack) { + LOG(DEBUG) << "TrackTPCITS: check"; + }}, + tmp); + + // TODO: + // fill trackextra table + tracksCursor(0, + collisionID, + trackType, + TruncateFloatFraction(track.getX(), mTrackX), + TruncateFloatFraction(track.getAlpha(), mTrackAlpha), + track.getY(), + track.getZ(), + TruncateFloatFraction(track.getSnp(), mTrackSnp), + TruncateFloatFraction(track.getTgl(), mTrackTgl), + TruncateFloatFraction(track.getQ2Pt(), mTrack1Pt), + TruncateFloatFraction(TMath::Sqrt(track.getSigmaY2()), mTrackCovDiag), + TruncateFloatFraction(TMath::Sqrt(track.getSigmaZ2()), mTrackCovDiag), + TruncateFloatFraction(TMath::Sqrt(track.getSigmaSnp2()), mTrackCovDiag), + TruncateFloatFraction(TMath::Sqrt(track.getSigmaTgl2()), mTrackCovDiag), + TruncateFloatFraction(TMath::Sqrt(track.getSigma1Pt2()), mTrackCovDiag), + (Char_t)(128. * track.getSigmaZY() / track.getSigmaZ2() / track.getSigmaY2()), + (Char_t)(128. * track.getSigmaSnpY() / track.getSigmaSnp2() / track.getSigmaY2()), + (Char_t)(128. * track.getSigmaSnpZ() / track.getSigmaSnp2() / track.getSigmaZ2()), + (Char_t)(128. * track.getSigmaTglY() / track.getSigmaTgl2() / track.getSigmaY2()), + (Char_t)(128. * track.getSigmaTglZ() / track.getSigmaTgl2() / track.getSigmaZ2()), + (Char_t)(128. * track.getSigmaTglSnp() / track.getSigmaTgl2() / track.getSigmaSnp2()), + (Char_t)(128. * track.getSigma1PtY() / track.getSigma1Pt2() / track.getSigmaY2()), + (Char_t)(128. * track.getSigma1PtZ() / track.getSigma1Pt2() / track.getSigmaZ2()), + (Char_t)(128. * track.getSigma1PtSnp() / track.getSigma1Pt2() / track.getSigmaSnp2()), + (Char_t)(128. * track.getSigma1PtTgl() / track.getSigma1Pt2() / track.getSigmaTgl2()), + TruncateFloatFraction(tpcInnerParam, mTrack1Pt), + flags, + itsClusterMap, + tpcNClsFindable, + tpcNClsFindableMinusFound, + tpcNClsFindableMinusCrossedRows, + tpcNClsShared, + trdPattern, + TruncateFloatFraction(itsChi2NCl, mTrackCovOffDiag), + TruncateFloatFraction(tpcChi2NCl, mTrackCovOffDiag), + TruncateFloatFraction(trdChi2, mTrackCovOffDiag), + TruncateFloatFraction(tofChi2, mTrackCovOffDiag), + TruncateFloatFraction(tpcSignal, mTrackSignal), + TruncateFloatFraction(trdSignal, mTrackSignal), + TruncateFloatFraction(tofSignal, mTrackSignal), + TruncateFloatFraction(length, mTrackSignal), + TruncateFloatFraction(tofExpMom, mTrack1Pt), + TruncateFloatFraction(trackEtaEMCAL, mTrackPosEMCAL), + TruncateFloatFraction(trackPhiEMCAL, mTrackPosEMCAL)); + } +} + +void AODProducerWorkflowDPL::init(InitContext& ic) +{ + mTimer.Stop(); + + mFillTracksITS = ic.options().get<int>("fill-tracks-its"); + mFillTracksTPC = ic.options().get<int>("fill-tracks-tpc"); + mFillTracksITSTPC = ic.options().get<int>("fill-tracks-its-tpc"); + mTFNumber = ic.options().get<int>("aod-timeframe-id"); + if (mTFNumber == -1) { + LOG(INFO) << "TFNumber will be obtained from CCDB"; + } + LOG(INFO) << "Track filling flags are set to: " + << "\n ITS = " << mFillTracksITS << "\n TPC = " << mFillTracksTPC << "\n ITSTPC = " << mFillTracksITSTPC; + + mTruncate = ic.options().get<int>("enable-truncation"); + if (mTruncate != 1) { + LOG(INFO) << "Truncation is not used!"; + + mCollisionPosition = 0xFFFFFFFF; + mCollisionPositionCov = 0xFFFFFFFF; + mTrackX = 0xFFFFFFFF; + mTrackAlpha = 0xFFFFFFFF; + mTrackSnp = 0xFFFFFFFF; + mTrackTgl = 0xFFFFFFFF; + mTrack1Pt = 0xFFFFFFFF; + mTrackCovDiag = 0xFFFFFFFF; + mTrackCovOffDiag = 0xFFFFFFFF; + mTrackSignal = 0xFFFFFFFF; + mTrackPosEMCAL = 0xFFFFFFFF; + mTracklets = 0xFFFFFFFF; + mMcParticleW = 0xFFFFFFFF; + mMcParticlePos = 0xFFFFFFFF; + mMcParticleMom = 0xFFFFFFFF; + mCaloAmp = 0xFFFFFFFF; + mCaloTime = 0xFFFFFFFF; + mMuonTr1P = 0xFFFFFFFF; + mMuonTrThetaX = 0xFFFFFFFF; + mMuonTrThetaY = 0xFFFFFFFF; + mMuonTrZmu = 0xFFFFFFFF; + mMuonTrBend = 0xFFFFFFFF; + mMuonTrNonBend = 0xFFFFFFFF; + mMuonTrCov = 0xFFFFFFFF; + mMuonCl = 0xFFFFFFFF; + mMuonClErr = 0xFFFFFFFF; + mV0Time = 0xFFFFFFFF; + mFDDTime = 0xFFFFFFFF; + mT0Time = 0xFFFFFFFF; + mV0Amplitude = 0xFFFFFFFF; + mFDDAmplitude = 0xFFFFFFFF; + mT0Amplitude = 0xFFFFFFFF; + } + + mTimer.Reset(); +} + +void AODProducerWorkflowDPL::run(ProcessingContext& pc) +{ + mTimer.Start(false); + + auto ft0ChData = pc.inputs().get<gsl::span<o2::ft0::ChannelDataFloat>>("ft0ChData"); + auto ft0RecPoints = pc.inputs().get<gsl::span<o2::ft0::RecPoints>>("ft0RecPoints"); + auto primVer2TRefs = pc.inputs().get<gsl::span<o2::vertexing::V2TRef>>("primVer2TRefs"); + auto primVerGIs = pc.inputs().get<gsl::span<o2::vertexing::GIndex>>("primVerGIs"); + auto primVertices = pc.inputs().get<gsl::span<o2::vertexing::PVertex>>("primVertices"); + auto tracksITS = pc.inputs().get<gsl::span<o2::its::TrackITS>>("trackITS"); + auto tracksITSTPC = pc.inputs().get<gsl::span<o2::dataformats::TrackTPCITS>>("tracksITSTPC"); + auto tracksTPC = pc.inputs().get<gsl::span<o2::tpc::TrackTPC>>("trackTPC"); + auto tracksTPCMCTruth = pc.inputs().get<gsl::span<o2::MCCompLabel>>("trackTPCMCTruth"); + auto tracksITSMCTruth = pc.inputs().get<gsl::span<o2::MCCompLabel>>("trackITSMCTruth"); + auto tracksITSTPC_ITSMC = pc.inputs().get<gsl::span<o2::MCCompLabel>>("tracksITSTPC_ITSMC"); + auto tracksITSTPC_TPCMC = pc.inputs().get<gsl::span<o2::MCCompLabel>>("tracksITSTPC_TPCMC"); + + LOG(DEBUG) << "FOUND " << tracksTPC.size() << " TPC tracks"; + LOG(DEBUG) << "FOUND " << tracksITS.size() << " ITS tracks"; + LOG(DEBUG) << "FOUND " << tracksITSTPC.size() << " ITCTPC tracks"; + + auto& bcBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "BC"}); + auto& collisionsBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "COLLISION"}); + auto& ft0Builder = pc.outputs().make<TableBuilder>(Output{"AOD", "FT0"}); + auto& mcCollisionsBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "MCCOLLISION"}); + auto& tracksBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "TRACK"}); + auto& mcParticlesBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "MCPARTICLE"}); + auto& mcTrackLabelBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "MCTRACKLABEL"}); + auto& timeFrameNumberBuilder = pc.outputs().make<uint64_t>(Output{"TFN", "TFNumber"}); + + auto& fv0aBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "FV0A"}); + auto& fddBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "FDD"}); + auto& fv0cBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "FV0C"}); + auto& zdcBuilder = pc.outputs().make<TableBuilder>(Output{"AOD", "ZDC"}); + + auto bcCursor = bcBuilder.cursor<o2::aod::BCs>(); + auto collisionsCursor = collisionsBuilder.cursor<o2::aod::Collisions>(); + auto ft0Cursor = ft0Builder.cursor<o2::aod::FT0s>(); + auto mcCollisionsCursor = mcCollisionsBuilder.cursor<o2::aod::McCollisions>(); + auto tracksCursor = tracksBuilder.cursor<o2::aodproducer::TracksTable>(); + auto mcParticlesCursor = mcParticlesBuilder.cursor<o2::aodproducer::MCParticlesTable>(); + auto mcTrackLabelCursor = mcTrackLabelBuilder.cursor<o2::aod::McTrackLabels>(); + + auto fv0aCursor = fv0aBuilder.cursor<o2::aod::FV0As>(); + auto fv0cCursor = fv0cBuilder.cursor<o2::aod::FV0Cs>(); + auto fddCursor = fddBuilder.cursor<o2::aod::FDDs>(); + auto zdcCursor = zdcBuilder.cursor<o2::aod::Zdcs>(); + + o2::steer::MCKinematicsReader mcReader("collisioncontext.root"); + const auto mcContext = mcReader.getDigitizationContext(); + const auto& mcRecords = mcContext->getEventRecords(); + const auto& mcParts = mcContext->getEventParts(); + + LOG(DEBUG) << "FOUND " << mcRecords.size() << " records"; + LOG(DEBUG) << "FOUND " << mcParts.size() << " parts"; + + findMinMaxBc(ft0RecPoints, primVertices, mcRecords); + + std::map<uint64_t, uint64_t> mGlobBC2BCID; + + // TODO: + // get real run number and triggerMask + int runNumber = 244918; + uint64_t triggerMask = 1; + + // filling BC table and map<globalBC, BCId> + for (uint64_t i = 0; i <= maxGlBC - minGlBC; i++) { + bcCursor(0, + runNumber, + minGlBC + i, + triggerMask); + mGlobBC2BCID[minGlBC + i] = i; + } + + // TODO: + // add real FV0A, FV0C, FDD, ZDC tables instead of dummies + uint64_t dummyBC = 0; + float dummyTime = 0.f; + float dummyFV0AmplA[48] = {0.}; + uint8_t dummyTriggerMask = 0; + fv0aCursor(0, + dummyBC, + dummyFV0AmplA, + dummyTime, + dummyTriggerMask); + + float dummyFV0AmplC[32] = {0.}; + fv0cCursor(0, + dummyBC, + dummyFV0AmplC, + dummyTime); + + float dummyFDDAmplA[4] = {0.}; + float dummyFDDAmplC[4] = {0.}; + fddCursor(0, + dummyBC, + dummyFDDAmplA, + dummyFDDAmplC, + dummyTime, + dummyTime, + dummyTriggerMask); + + float dummyEnergyZEM1 = 0; + float dummyEnergyZEM2 = 0; + float dummyEnergyCommonZNA = 0; + float dummyEnergyCommonZNC = 0; + float dummyEnergyCommonZPA = 0; + float dummyEnergyCommonZPC = 0; + float dummyEnergySectorZNA[4] = {0.}; + float dummyEnergySectorZNC[4] = {0.}; + float dummyEnergySectorZPA[4] = {0.}; + float dummyEnergySectorZPC[4] = {0.}; + zdcCursor(0, + dummyBC, + dummyEnergyZEM1, + dummyEnergyZEM2, + dummyEnergyCommonZNA, + dummyEnergyCommonZNC, + dummyEnergyCommonZPA, + dummyEnergyCommonZPC, + dummyEnergySectorZNA, + dummyEnergySectorZNC, + dummyEnergySectorZPA, + dummyEnergySectorZPC, + dummyTime, + dummyTime, + dummyTime, + dummyTime, + dummyTime, + dummyTime); + + // TODO: + // figure out collision weight + float mcColWeight = 1.; + + // filling mcCollison table + int index = 0; + for (auto& rec : mcRecords) { + auto time = rec.getTimeNS(); + uint64_t globalBC = rec.bc + rec.orbit * o2::constants::lhc::LHCMaxBunches; + auto& colParts = mcParts[index]; + for (int i = 0; i < colParts.size(); i++) { + auto eventID = colParts[i].entryID; + auto sourceID = colParts[i].sourceID; + // FIXME: + // use generators' names for generatorIDs (?) + short generatorID = sourceID; + auto& header = mcReader.getMCEventHeader(sourceID, eventID); + mcCollisionsCursor(0, + mGlobBC2BCID.at(globalBC), + generatorID, + TruncateFloatFraction(header.GetX(), mCollisionPosition), + TruncateFloatFraction(header.GetY(), mCollisionPosition), + TruncateFloatFraction(header.GetZ(), mCollisionPosition), + TruncateFloatFraction(time, mCollisionPosition), + TruncateFloatFraction(mcColWeight, mCollisionPosition), + header.GetB()); + } + index++; + } + + // tracks --> mc particles + // std::map<<sourceID, eventID, trackID>, McParticles::Index> + std::map<std::tuple<uint32_t, uint32_t, uint32_t>, uint32_t> mIDsToIndex; + + // filling mcparticle table + uint32_t mcParticlesIndex = 0; + for (int sourceID = 0; sourceID < mcReader.getNSources(); sourceID++) { + for (int mcEventID = 0; mcEventID < mcReader.getNEvents(sourceID); mcEventID++) { + std::vector<MCTrack> const& mcParticles = mcReader.getTracks(sourceID, mcEventID); + + // TODO + // *fill dummy columns + // *mother/daughter IDs need to be recalculated before storing into table + int statusCode = 0; + uint8_t flags = 0; + float weight = 0.f; + int mother0 = 0; + int mother1 = 0; + int daughter0 = 0; + int daughter1 = 0; + + int mcTrackID = 0; + for (auto& mcParticle : mcParticles) { + mcParticlesCursor(0, + mcEventID, + mcParticle.GetPdgCode(), + statusCode, + flags, + mother0, + mother1, + daughter0, + daughter1, + TruncateFloatFraction(weight, mMcParticleW), + TruncateFloatFraction((float)mcParticle.Px(), mMcParticleMom), + TruncateFloatFraction((float)mcParticle.Py(), mMcParticleMom), + TruncateFloatFraction((float)mcParticle.Pz(), mMcParticleMom), + TruncateFloatFraction((float)mcParticle.GetEnergy(), mMcParticleMom), + TruncateFloatFraction((float)mcParticle.Vx(), mMcParticlePos), + TruncateFloatFraction((float)mcParticle.Vy(), mMcParticlePos), + TruncateFloatFraction((float)mcParticle.Vz(), mMcParticlePos), + TruncateFloatFraction((float)mcParticle.T(), mMcParticlePos)); + mIDsToIndex[std::make_tuple(sourceID, mcEventID, mcTrackID)] = mcParticlesIndex; + mcTrackID++; + mcParticlesIndex++; + } + } + } + + // vector of FT0 amplitudes + std::vector<float> vAmplitudes(208, 0.); + // filling FT0 table + for (auto& ft0RecPoint : ft0RecPoints) { + const auto channelData = ft0RecPoint.getBunchChannelData(ft0ChData); + // TODO: + // switch to calibrated amplitude + for (auto& channel : channelData) { + vAmplitudes[channel.ChId] = channel.QTCAmpl; // amplitude, mV + } + float aAmplitudesA[96]; + float aAmplitudesC[112]; + for (int i = 0; i < 96; i++) { + aAmplitudesA[i] = TruncateFloatFraction(vAmplitudes[i], mT0Amplitude); + } + for (int i = 0; i < 112; i++) { + aAmplitudesC[i] = TruncateFloatFraction(vAmplitudes[i + 96], mT0Amplitude); + } + uint64_t globalBC = ft0RecPoint.getInteractionRecord().orbit * o2::constants::lhc::LHCMaxBunches + ft0RecPoint.getInteractionRecord().bc; + ft0Cursor(0, + mGlobBC2BCID.at(globalBC), + aAmplitudesA, + aAmplitudesC, + TruncateFloatFraction(ft0RecPoint.getCollisionTimeA() / 1E3, mT0Time), // ps to ns + TruncateFloatFraction(ft0RecPoint.getCollisionTimeC() / 1E3, mT0Time), // ps to ns + ft0RecPoint.getTrigger().triggersignals); + } + + // initializing vectors for trackID --> collisionID connection + std::vector<int> vCollRefsITS(tracksITS.size(), -1); + std::vector<int> vCollRefsTPC(tracksTPC.size(), -1); + std::vector<int> vCollRefsTPCITS(tracksITSTPC.size(), -1); + + // global bc of the 1st vertex for TF number + uint64_t firstVtxGlBC; + + // filling collisions table + int collisionID = 0; + for (auto& vertex : primVertices) { + auto& cov = vertex.getCov(); + auto& timeStamp = vertex.getTimeStamp(); + Double_t tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns + // FIXME: + // should use IRMin and IRMax for globalBC calculation + uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); + + LOG(DEBUG) << globalBC << " " << tsTimeStamp; + + if (collisionID == 0) { + firstVtxGlBC = globalBC; + } + + // collision timestamp in ns wrt the beginning of collision BC + tsTimeStamp = globalBC * o2::constants::lhc::LHCBunchSpacingNS - tsTimeStamp; + int BCid = mGlobBC2BCID.at(globalBC); + // TODO: + // get real collision time mask + int collisionTimeMask = 0; + collisionsCursor(0, + BCid, + TruncateFloatFraction(vertex.getX(), mCollisionPosition), + TruncateFloatFraction(vertex.getY(), mCollisionPosition), + TruncateFloatFraction(vertex.getZ(), mCollisionPosition), + TruncateFloatFraction(cov[0], mCollisionPositionCov), + TruncateFloatFraction(cov[1], mCollisionPositionCov), + TruncateFloatFraction(cov[2], mCollisionPositionCov), + TruncateFloatFraction(cov[3], mCollisionPositionCov), + TruncateFloatFraction(cov[4], mCollisionPositionCov), + TruncateFloatFraction(cov[5], mCollisionPositionCov), + TruncateFloatFraction(vertex.getChi2(), mCollisionPositionCov), + vertex.getNContributors(), + TruncateFloatFraction(tsTimeStamp, mCollisionPosition), + TruncateFloatFraction(timeStamp.getTimeStampError() * 1E3, mCollisionPositionCov), + collisionTimeMask); + + auto trackRef = primVer2TRefs[collisionID]; + int start = trackRef.getFirstEntryOfSource(0); + int ntracks = trackRef.getEntriesOfSource(0); + + // FIXME: + // `track<-->vertex` ambiguity is not accounted for in this code + for (int ti = 0; ti < ntracks; ti++) { + auto trackIndex = primVerGIs[start + ti]; + const auto source = trackIndex.getSource(); + // setting collisionID for tracks attached to vertices + if (source == o2::vertexing::GIndex::Source::TPC) { + vCollRefsTPC[trackIndex.getIndex()] = collisionID; + } else if (source == o2::vertexing::GIndex::Source::ITS) { + vCollRefsITS[trackIndex.getIndex()] = collisionID; + } else if (source == o2::vertexing::GIndex::Source::TPCITS) { + vCollRefsTPCITS[trackIndex.getIndex()] = collisionID; + } else { + LOG(WARNING) << "Unsupported track type!"; + } + } + collisionID++; + } + + // filling tracks tables and track label table + + // labelMask (temporary) usage: + // bit 13 -- ITS and TPC labels are not equal + // bit 14 -- isNoise() == true + // bit 15 -- isFake() == true + // labelID = std::numeric_limits<uint32_t>::max() -- label is not set + + uint32_t labelID; + uint32_t labelITS; + uint32_t labelTPC; + uint16_t labelMask; + + if (mFillTracksITS) { + fillTracksTable(tracksITS, vCollRefsITS, tracksCursor, o2::vertexing::GIndex::Source::ITS); // fTrackType = 1 + for (auto& mcTruthITS : tracksITSMCTruth) { + labelID = std::numeric_limits<uint32_t>::max(); + // TODO: + // fill label mask + labelMask = 0; + if (mcTruthITS.isValid()) { + labelID = mIDsToIndex.at(std::make_tuple(mcTruthITS.getSourceID(), mcTruthITS.getEventID(), mcTruthITS.getTrackID())); + } + if (mcTruthITS.isFake()) { + labelMask |= (0x1 << 15); + } + if (mcTruthITS.isNoise()) { + labelMask |= (0x1 << 14); + } + mcTrackLabelCursor(0, + labelID, + labelMask); + } + } + + if (mFillTracksTPC) { + fillTracksTable(tracksTPC, vCollRefsTPC, tracksCursor, o2::vertexing::GIndex::Source::TPC); // fTrackType = 2 + for (auto& mcTruthTPC : tracksTPCMCTruth) { + labelID = std::numeric_limits<uint32_t>::max(); + // TODO: + // fill label mask + labelMask = 0; + if (mcTruthTPC.isValid()) { + labelID = mIDsToIndex.at(std::make_tuple(mcTruthTPC.getSourceID(), mcTruthTPC.getEventID(), mcTruthTPC.getTrackID())); + } + if (mcTruthTPC.isFake()) { + labelMask |= (0x1 << 15); + } + if (mcTruthTPC.isNoise()) { + labelMask |= (0x1 << 14); + } + mcTrackLabelCursor(0, + labelID, + labelMask); + } + } + + if (mFillTracksITSTPC) { + fillTracksTable(tracksITSTPC, vCollRefsTPCITS, tracksCursor, o2::vertexing::GIndex::Source::TPCITS); // fTrackType = 0 + for (int i = 0; i < tracksITSTPC.size(); i++) { + auto& mcTruthITS = tracksITSTPC_ITSMC[i]; + auto& mcTruthTPC = tracksITSTPC_TPCMC[i]; + labelID = std::numeric_limits<uint32_t>::max(); + labelITS = std::numeric_limits<uint32_t>::max(); + labelTPC = std::numeric_limits<uint32_t>::max(); + // TODO: + // fill label mask + // currently using label mask to indicate labelITS != labelTPC + labelMask = 0; + if (mcTruthITS.isValid() && mcTruthTPC.isValid()) { + labelITS = mIDsToIndex.at(std::make_tuple(mcTruthITS.getSourceID(), mcTruthITS.getEventID(), mcTruthITS.getTrackID())); + labelTPC = mIDsToIndex.at(std::make_tuple(mcTruthTPC.getSourceID(), mcTruthTPC.getEventID(), mcTruthTPC.getTrackID())); + labelID = labelITS; + } + if (mcTruthITS.isFake() || mcTruthTPC.isFake()) { + labelMask |= (0x1 << 15); + } + if (mcTruthITS.isNoise() || mcTruthTPC.isNoise()) { + labelMask |= (0x1 << 14); + } + if (labelITS != labelTPC) { + LOG(DEBUG) << "ITS-TPC MCTruth: labelIDs do not match at " << i; + labelMask |= (0x1 << 13); + } + mcTrackLabelCursor(0, + labelID, + labelMask); + } + } + + if (mTFNumber == -1) { + timeFrameNumberBuilder = getTFNumber(firstVtxGlBC, runNumber); + } else { + timeFrameNumberBuilder = mTFNumber; + } + + mTimer.Stop(); +} + +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); +} + +DataProcessorSpec getAODProducerWorkflowSpec() +{ + std::vector<InputSpec> inputs; + std::vector<OutputSpec> outputs; + + inputs.emplace_back("ft0ChData", "FT0", "RECCHDATA", 0, Lifetime::Timeframe); + inputs.emplace_back("ft0RecPoints", "FT0", "RECPOINTS", 0, Lifetime::Timeframe); + inputs.emplace_back("primVer2TRefs", "GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe); + inputs.emplace_back("primVerGIs", "GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe); + inputs.emplace_back("primVertices", "GLO", "PVTX", 0, Lifetime::Timeframe); + inputs.emplace_back("trackITS", "ITS", "TRACKS", 0, Lifetime::Timeframe); + inputs.emplace_back("tracksITSTPC", "GLO", "TPCITS", 0, Lifetime::Timeframe); + inputs.emplace_back("trackTPC", "TPC", "TRACKS", 0, Lifetime::Timeframe); + inputs.emplace_back("trackTPCMCTruth", "TPC", "TRACKSMCLBL", 0, Lifetime::Timeframe); + inputs.emplace_back("trackITSMCTruth", "ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); + inputs.emplace_back("tracksITSTPC_ITSMC", "GLO", "TPCITS_ITSMC", 0, Lifetime::Timeframe); + inputs.emplace_back("tracksITSTPC_TPCMC", "GLO", "TPCITS_TPCMC", 0, Lifetime::Timeframe); + + outputs.emplace_back(OutputLabel{"O2bc"}, "AOD", "BC", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2collision"}, "AOD", "COLLISION", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2ft0"}, "AOD", "FT0", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2mccollision"}, "AOD", "MCCOLLISION", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2track"}, "AOD", "TRACK", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2mcparticle"}, "AOD", "MCPARTICLE", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2mctracklabel"}, "AOD", "MCTRACKLABEL", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); + + // TODO: + // add FV0A, FV0C, FDD tables + outputs.emplace_back(OutputLabel{"O2fv0a"}, "AOD", "FV0A", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2fv0c"}, "AOD", "FV0C", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2fdd"}, "AOD", "FDD", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2zdc"}, "AOD", "ZDC", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "aod-producer-workflow", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<AODProducerWorkflowDPL>()}, + Options{ + ConfigParamSpec{"fill-tracks-its", VariantType::Int, 1, {"Fill ITS tracks into tracks table"}}, + ConfigParamSpec{"fill-tracks-tpc", VariantType::Int, 1, {"Fill TPC tracks into tracks table"}}, + ConfigParamSpec{"fill-tracks-its-tpc", VariantType::Int, 1, {"Fill ITS-TPC tracks into tracks table"}}, + ConfigParamSpec{"aod-timeframe-id", VariantType::Int, -1, {"Set timeframe number"}}, + ConfigParamSpec{"enable-truncation", VariantType::Int, 1, {"Truncation parameter: 1 -- on (default), != 1 -- off"}}}}; +} + +} // namespace o2::aodproducer diff --git a/Detectors/AOD/src/StandaloneAODProducer.cxx b/Detectors/AOD/src/StandaloneAODProducer.cxx new file mode 100644 index 0000000000000..7c5aceb68bc36 --- /dev/null +++ b/Detectors/AOD/src/StandaloneAODProducer.cxx @@ -0,0 +1,225 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Produce AOD from reconstructed objects + +/// This is a standalone demonstrator producer of the AOD format +/// from MC-RECO. For the moment not a DPL device but in future this +/// promotion is targeted. The executable produces the aod.root file which +/// can be fed into analysis workflows. + +#include "Framework/AnalysisDataModel.h" +#include "Framework/TableBuilder.h" +#include "Framework/TableTreeHelpers.h" +#include "Framework/Logger.h" +#include "TFile.h" + +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsITS/TrackITS.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "Steer/MCKinematicsReader.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "SimulationDataFormat/DigitizationContext.h" + +#include <vector> + +using namespace o2; +using namespace o2::framework; + +using GIndex = o2::dataformats::VtxTrackIndex; + +template <typename TrackT> +std::vector<TrackT>* fetchTracks(const char* filename, const char* treename, const char* branchname) +{ + TFile file(filename, "OPEN"); + auto tree = (TTree*)file.Get(treename); + auto br = tree->GetBranch(branchname); + std::vector<TrackT>* tracks = nullptr; + br->SetAddress(&tracks); + br->GetEntry(0); + return tracks; +} + +// fills the table of Monte Carlo collisions +// these are the ones references in the digitizer context +void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) +{ + + auto digitizationContext = mcreader.getDigitizationContext(); + const auto& records = digitizationContext->getEventRecords(); + const auto& parts = digitizationContext->getEventParts(); + + TableBuilder mcCollBuilder; + auto mcCollCursor = mcCollBuilder.cursor<o2::aod::McCollisions>(); + + // QUESTIONS: + // Well defined enumeration for Generator type? + // What to do with pile-up/embedded collision - which generator type? + // In the data model: What are "null / invalid" values or default values + // In the data model: What are the units? + + // iterating over records gives us the time information + int index = 0; + for (auto& rec : records) { + // concrete header information is available via the parts and MCKineReader + auto time = rec.getTimeNS(); + auto& colparts = parts[index]; + // get event ID(s) and source ID(s) + // if there are more than one sources --> the generator might differ but vertex etc should be same + auto eventID = colparts[0].entryID; + auto sourceID = colparts[0].sourceID; + + auto& header = mcreader.getMCEventHeader(eventID, sourceID); + //DECLARE_SOA_TABLE(McCollisions, "AOD", "MCCOLLISION", o2::soa::Index<>, mccollision::BCId, + // mccollision::GeneratorsID, + // 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()); + + index++; + } + auto mccoltable = mcCollBuilder.finalize(); + + TFile outfile("aod.root", "UPDATE"); + { + TableToTree t2t(mccoltable, &outfile, aod::MetadataTrait<o2::aod::McCollisions>::metadata::tableLabel()); + t2t.addAllBranches(); + t2t.process(); + } +} + +// add vertices/collisions +void fillCollisionAndTrackTable() +{ + // open the file for vertices + TFile f("o2_primary_vertex.root", "OPEN"); + auto t = (TTree*)f.Get("o2sim"); + + // fetch the tracks (these names are not following any convention!!) + auto tpctracks = fetchTracks<o2::tpc::TrackTPC>("tpctracks.root", "events", "Tracks"); + auto itstracks = fetchTracks<o2::its::TrackITS>("o2trac_its.root", "o2sim", "ITSTrack"); + auto itstpctracks = fetchTracks<o2::dataformats::TrackTPCITS>("o2match_itstpc.root", "matchTPCITS", "TPCITS"); + LOG(INFO) << "FOUND " << tpctracks->size() << " TPC tracks"; + LOG(INFO) << "FOUND " << itstracks->size() << " ITS tracks"; + LOG(INFO) << "FOUND " << itstpctracks->size() << " ITCTPC tracks"; + + if (t) { + auto br = t->GetBranch("PrimaryVertex"); + std::vector<o2::dataformats::PrimaryVertex>* vertices = nullptr; + br->SetAddress(&vertices); + br->GetEntry(0); + + // this referes to actual tracks + auto indexbr = t->GetBranch("PVTrackIndices"); + std::vector<GIndex>* vertexTrackIDs = nullptr; + indexbr->SetAddress(&vertexTrackIDs); + indexbr->GetEntry(0); + + // this makes the connection of vertex to track indices + auto v2totrackrefbr = t->GetBranch("PV2TrackRefs"); + std::vector<o2::dataformats::VtxTrackRef>* v2trackref = nullptr; + v2totrackrefbr->SetAddress(&v2trackref); + v2totrackrefbr->GetEntry(0); + + if (vertices && vertexTrackIDs) { + TableBuilder collBuilder; + auto collCursor = collBuilder.cursor<o2::aod::Collisions>(); + + TableBuilder trackBuilder; + auto trackCursor = trackBuilder.cursor<o2::aod::Tracks>(); + + int index = 0; + for (auto& v : *vertices) { + //DECLARE_SOA_TABLE(Collisions, "AOD", "COLLISION", o2::soa::Index<>, + // collision::BCId, collision::PosX, collision::PosY, collision::PosZ, + // collision::CovXX, collision::CovXY, collision::CovXZ, collision::CovYY, collision::CovYZ, collision::CovZZ, + // collision::Chi2, collision::NumContrib, collision::CollisionTime, collision::CollisionTimeRes, collision::CollisionTimeMask); + int BCid = 0; + auto& cov = v.getCov(); + auto& ts = v.getTimeStamp(); + + // TODO: figure out BC + CollisionTimeMask + collCursor(0, BCid, v.getX(), v.getY(), v.getZ(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[6], + v.getChi2(), v.getNContributors(), ts.getTimeStamp(), ts.getTimeStampError(), 1); + + // get the track for each vertex and fill the tracks table + // now go over tracks via the indices + auto& trackref = (*v2trackref)[index]; + int start = trackref.getFirstEntryOfSource(0); + int ntracks = trackref.getEntriesOfSource(0); + for (int ti = 0; ti < ntracks; ++ti) { + auto trackindex = (*vertexTrackIDs)[start + ti]; + + // now we need to fetch the actual track and fill the table + const auto source = trackindex.getSource(); + o2::track::TrackParCov* track = nullptr; + if (source == o2::dataformats::VtxTrackIndex::Source::TPC) { + track = &((*tpctracks)[trackindex.getIndex()]); + } else if (source == o2::dataformats::VtxTrackIndex::Source::ITS) { + track = &((*itstracks)[trackindex.getIndex()]); + } else if (source == o2::dataformats::VtxTrackIndex::Source::TPCITS) { + track = &((*itstpctracks)[trackindex.getIndex()]); + } else { + LOG(WARNING) << "Unsupported track source"; + } + + //DECLARE_SOA_TABLE_FULL(StoredTracks, "Tracks", "AOD", "TRACK:PAR", + // o2::soa::Index<>, track::CollisionId, track::TrackType, + // track::X, track::Alpha, + // track::Y, track::Z, track::Snp, track::Tgl, + // track::Signed1Pt, + // track::NormalizedPhi<track::RawPhi>, + // track::Px<track::Signed1Pt, track::Snp, track::Alpha>, + // track::Py<track::Signed1Pt, track::Snp, track::Alpha>, + // track::Pz<track::Signed1Pt, track::Tgl>, + // track::Charge<track::Signed1Pt>); + + std::array<float, 3> pxpypz; + track->getPxPyPzGlo(pxpypz); + trackCursor(0, index, 0 /* CORRECT THIS */, track->getX(), track->getAlpha(), track->getY(), track->getZ(), track->getSnp(), track->getTgl(), + track->getPt() /*CHECK!!*/, track->getPhi(), pxpypz[0], pxpypz[1], pxpypz[2]); + } + index++; + } + auto colltable = collBuilder.finalize(); + auto tracktable = trackBuilder.finalize(); + + f.Close(); + TFile outfile("aod.root", "RECREATE"); + { + TableToTree t2t(colltable, &outfile, aod::MetadataTrait<o2::aod::Collisions>::metadata::tableLabel()); + t2t.addAllBranches(); + t2t.process(); + } + { + TableToTree t2t(tracktable, &outfile, "Tracks" /* aod::MetadataTrait<o2::aod::Tracks>::metadata::tableLabel() */); + t2t.addAllBranches(); + t2t.process(); + } + } + } +} + +// TODO: add MCparticles + +int main() +{ + fillCollisionAndTrackTable(); + + o2::steer::MCKinematicsReader mcreader("collisioncontext.root"); + fillMCollisionTable(mcreader); + + return 0; +} diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx new file mode 100644 index 0000000000000..62226f01db3bf --- /dev/null +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "AODProducerWorkflow/AODProducerWorkflow.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" + +using namespace o2::framework; + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + // Update the (declared) parameters if changed from the command line + return std::move(o2::aodproducer::getAODProducerWorkflow()); +} diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 5fcd16c97e1f2..dda6c34f843a1 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -16,8 +16,9 @@ o2_add_library(DetectorsBase src/MatLayerCyl.cxx src/MatLayerCylSet.cxx src/Ray.cxx - src/DCAFitter.cxx + src/DCAFitter.cxx src/BaseDPLDigitizer.cxx + src/CTFCoderBase.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonUtils O2::DetectorsCommonDataFormats @@ -28,8 +29,10 @@ o2_add_library(DetectorsBase O2::Framework FairMQ::FairMQ O2::DataFormatsParameters - O2::SimConfig - ROOT::VMC) + O2::SimConfig + ROOT::VMC + PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Merger # Must not link to avoid cyclic dependency + ) o2_target_root_dictionary(DetectorsBase HEADERS include/DetectorsBase/Detector.h @@ -40,14 +43,15 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/Ray.h include/DetectorsBase/MatCell.h include/DetectorsBase/MatLayerCyl.h - include/DetectorsBase/MatLayerCylSet.h) + include/DetectorsBase/MatLayerCylSet.h + include/DetectorsBase/CTFCoderBase.h) if(BUILD_SIMULATION) o2_add_test( MatBudLUT SOURCES test/testMatBudLUT.cxx COMPONENT_NAME DetectorsBase - PUBLIC_LINK_LIBRARIES O2::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}) diff --git a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h new file mode 100644 index 0000000000000..8cf0e02639e04 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -0,0 +1,88 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoderBase.h +/// \brief Declarations for CTFCoderBase class (support of external dictionaries) +/// \author ruben.shahoyan@cern.ch + +#ifndef _ALICEO2_CTFCODER_BASE_H_ +#define _ALICEO2_CTFCODER_BASE_H_ + +#include <memory> +#include <TFile.h> +#include <TTree.h> +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "rANS/rans.h" + +namespace o2 +{ +namespace ctf +{ + +/// this is a base class for particular detector CTF coder/decoder, provides common +/// interface to create external entropy encoders/decoders + +using DetID = o2::detectors::DetID; + +class CTFCoderBase +{ + + public: + enum class OpType : int { Encoder, + Decoder }; + + CTFCoderBase() = delete; + CTFCoderBase(int n, DetID det) : mCoders(n), mDet(det) {} + + std::unique_ptr<TFile> loadDictionaryTreeFile(const std::string& dictPath, bool mayFail = false); + + template <typename CTF> + std::vector<char> readDictionaryFromFile(const std::string& dictPath, bool mayFail = false) + { + std::vector<char> bufVec; + auto fileDict = loadDictionaryTreeFile(dictPath, mayFail); + if (fileDict) { + std::unique_ptr<TTree> tree((TTree*)fileDict->Get(std::string(o2::base::NameConf::CTFDICT).c_str())); + CTF::readFromTree(bufVec, *tree.get(), mDet.getName()); + } + return bufVec; + } + + template <typename S> + void createCoder(OpType op, const o2::rans::FrequencyTable& freq, uint8_t probabilityBits, int slot) + { + if (op == OpType::Encoder) { + mCoders[slot].reset(new o2::rans::LiteralEncoder64<S>(freq, probabilityBits)); + } else { + mCoders[slot].reset(new o2::rans::LiteralDecoder64<S>(freq, probabilityBits)); + } + } + + void clear() + { + for (auto c : mCoders) { + c.reset(); + } + } + + protected: + std::string getPrefix() const { return o2::utils::concat_string(mDet.getName(), "_CTF: "); } + + std::vector<std::shared_ptr<void>> mCoders; // encoders/decoders + DetID mDet; + + ClassDefNV(CTFCoderBase, 1); +}; + +} // namespace ctf +} // namespace o2 + +#endif diff --git a/Detectors/Base/include/DetectorsBase/Detector.h b/Detectors/Base/include/DetectorsBase/Detector.h index 7a8877f00bab5..f37d2bc5b2ca2 100644 --- a/Detectors/Base/include/DetectorsBase/Detector.h +++ b/Detectors/Base/include/DetectorsBase/Detector.h @@ -363,8 +363,9 @@ class DetImpl : public o2::base::Detector } else { Int_t entries = origin.GetEntries(); Int_t nprimTot = 0; - for (auto entry = 0; entry < entries; entry++) + for (auto entry = 0; entry < entries; entry++) { nprimTot += nprimaries[entry]; + } // offset for pimary track index Int_t idelta0 = 0; // offset for secondary track index diff --git a/Detectors/Base/include/DetectorsBase/GeometryManager.h b/Detectors/Base/include/DetectorsBase/GeometryManager.h index 5b340f7487380..dadd378f07968 100644 --- a/Detectors/Base/include/DetectorsBase/GeometryManager.h +++ b/Detectors/Base/include/DetectorsBase/GeometryManager.h @@ -23,9 +23,9 @@ #include <string_view> #include "DetectorsCommonDataFormats/DetID.h" #include "FairLogger.h" // for LOG -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "DetectorsBase/MatCell.h" - +#include <mutex> class TGeoHMatrix; // lines 11-11 class TGeoManager; // lines 9-9 @@ -64,7 +64,7 @@ class GeometryManager : public TObject static int getSensID(o2::detectors::DetID detid, int sensid) { /// compose combined detector+sensor ID for sensitive volumes - return (detid.getMask().to_ulong() << sDetOffset) | (sensid & sSensorMask); + return (detid << sDetOffset) | (sensid & sSensorMask); } /// Default destructor @@ -92,28 +92,28 @@ class GeometryManager : public TObject }; static o2::base::MatBudget meanMaterialBudget(float x0, float y0, float z0, float x1, float y1, float z1); - static o2::base::MatBudget meanMaterialBudget(const Point3D<float>& start, const Point3D<float>& end) + static o2::base::MatBudget meanMaterialBudget(const math_utils::Point3D<float>& start, const math_utils::Point3D<float>& end) { return meanMaterialBudget(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); } - static o2::base::MatBudget meanMaterialBudget(const Point3D<double>& start, const Point3D<double>& end) + static o2::base::MatBudget meanMaterialBudget(const math_utils::Point3D<double>& start, const math_utils::Point3D<double>& end) { return meanMaterialBudget(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); } static MatBudgetExt meanMaterialBudgetExt(float x0, float y0, float z0, float x1, float y1, float z1); - static MatBudgetExt meanMaterialBudgetExt(const Point3D<float>& start, const Point3D<float>& end) + static MatBudgetExt meanMaterialBudgetExt(const math_utils::Point3D<float>& start, const math_utils::Point3D<float>& end) { return meanMaterialBudgetExt(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); } - static MatBudgetExt meanMaterialBudgetExt(const Point3D<double>& start, const Point3D<double>& end) + static MatBudgetExt meanMaterialBudgetExt(const math_utils::Point3D<double>& start, const math_utils::Point3D<double>& end) { return meanMaterialBudgetExt(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); } private: /// Default constructor - GeometryManager(); + GeometryManager() = default; static void accountMaterial(const TGeoMaterial* material, MatBudgetExt& bd); static void accountMaterial(const TGeoMaterial* material, o2::base::MatBudget& bd) @@ -126,16 +126,12 @@ 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_mask<<sDetOffset)|(sensid&sSensorMask) -#ifdef ENABLE_UPGRADES - static constexpr UInt_t sDetOffset = 14; /// detector identifier will start from this bit -#else + /// sensitive volume identifier composed from (det_ID<<sDetOffset)|(sensid&sSensorMask) static constexpr UInt_t sDetOffset = 15; /// detector identifier will start from this bit -#endif static constexpr UInt_t sSensorMask = (0x1 << sDetOffset) - 1; /// mask=max sensitive volumes allowed per detector (0xffff) + static std::mutex sTGMutex; ClassDefOverride(GeometryManager, 0); // Manager of geometry information for alignment }; diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h index ccb7df1c23198..cf0acc750b53b 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h @@ -14,7 +14,7 @@ #ifndef ALICEO2_MATLAYERCYL_H #define ALICEO2_MATLAYERCYL_H -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cmath> #include <cstring> #endif @@ -171,8 +171,9 @@ class MatLayerCyl : public o2::gpu::FlatObject // Get edge bin (in direction dir) of the slice, to which phiBin belongs // No check for phiBin validity is done auto slice = phiBin2Slice(phiBin); - while (slice == phiBin2Slice((phiBin += dir))) + while (slice == phiBin2Slice((phiBin += dir))) { ; + } return phiBin - dir; } diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index 5c8e741a7a1c0..e0912de30c691 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -20,7 +20,7 @@ #include "FlatObject.h" #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #endif // !GPUCA_ALIGPUCODE /********************************************************************** @@ -82,7 +82,7 @@ class MatLayerCylSet : public o2::gpu::FlatObject #endif // !GPUCA_ALIGPUCODE #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version - MatBudget getMatBudget(const Point3D<float>& point0, const Point3D<float>& point1) const + MatBudget getMatBudget(const math_utils::Point3D<float>& point0, const math_utils::Point3D<float>& point1) const { // 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()); diff --git a/Detectors/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index 9185d70cefd1e..174fc69f59240 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -15,13 +15,18 @@ #ifndef ALICEO2_BASE_PROPAGATOR_ #define ALICEO2_BASE_PROPAGATOR_ -#include <string> +#include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" #include "CommonConstants/PhysicsConstants.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/DCA.h" #include "ReconstructionDataFormats/TrackLTIntegral.h" #include "DetectorsBase/MatLayerCylSet.h" +#ifndef GPUCA_GPUCODE +#include <string> +#endif + namespace o2 { namespace parameters @@ -39,6 +44,11 @@ namespace field class MagFieldFast; } +namespace gpu +{ +class GPUTPCGMPolynomialField; +} + namespace base { class Propagator @@ -50,47 +60,44 @@ class Propagator USEMatCorrLUT }; // flag to use LUT for material queries (user must provide a pointer - static Propagator* Instance() - { - static Propagator instance; - return &instance; - } + static constexpr float MAX_SIN_PHI = 0.85f; + static constexpr float MAX_STEP = 2.0f; - bool PropagateToXBxByBz(o2::track::TrackParCov& track, float x, float mass = o2::constants::physics::MassPionCharged, - float maxSnp = 0.85, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool PropagateToXBxByBz(o2::track::TrackParCov& track, float x, + float maxSnp = MAX_SIN_PHI, float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; - bool PropagateToXBxByBz(o2::track::TrackPar& track, float x, float mass = o2::constants::physics::MassPionCharged, - float maxSnp = 0.85, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool PropagateToXBxByBz(o2::track::TrackPar& track, float x, + float maxSnp = MAX_SIN_PHI, float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; - bool propagateToX(o2::track::TrackParCov& track, float x, float bZ, float mass = o2::constants::physics::MassPionCharged, - float maxSnp = 0.85, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToX(o2::track::TrackParCov& track, float x, float bZ, + float maxSnp = MAX_SIN_PHI, float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; - bool propagateToX(o2::track::TrackPar& track, float x, float bZ, float mass = o2::constants::physics::MassPionCharged, - float maxSnp = 0.85, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToX(o2::track::TrackPar& track, float x, float bZ, + float maxSnp = MAX_SIN_PHI, float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + o2::track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; - bool propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, float bZ, - float mass = o2::constants::physics::MassPionCharged, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - o2::dataformats::DCA* dcaInfo = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, - int signCorr = 0, float maxD = 999.f) const; + GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, float bZ, + float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + o2::dataformats::DCA* dcaInfo = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, + int signCorr = 0, float maxD = 999.f) const; - bool propagateToDCABxByBz(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, - float mass = o2::constants::physics::MassPionCharged, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - o2::dataformats::DCA* dcaInfo = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, - int signCorr = 0, float maxD = 999.f) const; + GPUd() bool propagateToDCABxByBz(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, + float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + o2::dataformats::DCA* dcaInfo = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, + int signCorr = 0, float maxD = 999.f) const; - bool propagateToDCA(const Point3D<float>& vtx, o2::track::TrackPar& track, float bZ, - float mass = o2::constants::physics::MassPionCharged, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - std::array<float, 2>* dca = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, - int signCorr = 0, float maxD = 999.f) const; + GPUd() bool propagateToDCA(const o2::math_utils::Point3D<float>& vtx, o2::track::TrackPar& track, float bZ, + float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + gpu::gpustd::array<float, 2>* dca = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, + int signCorr = 0, float maxD = 999.f) const; - bool propagateToDCABxByBz(const Point3D<float>& vtx, o2::track::TrackPar& track, - float mass = o2::constants::physics::MassPionCharged, float maxStep = 2.0, MatCorrType matCorr = MatCorrType::USEMatCorrTGeo, - std::array<float, 2>* dca = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, - int signCorr = 0, float maxD = 999.f) const; + GPUd() bool propagateToDCABxByBz(const o2::math_utils::Point3D<float>& vtx, o2::track::TrackPar& track, + float maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + gpu::gpustd::array<float, 2>* dca = nullptr, o2::track::TrackLTIntegral* tofInfo = nullptr, + int signCorr = 0, float maxD = 999.f) const; Propagator(Propagator const&) = delete; Propagator(Propagator&&) = delete; @@ -98,24 +105,39 @@ class Propagator Propagator& operator=(Propagator&&) = delete; // Bz at the origin - float getNominalBz() const { return mBz; } + GPUd() float getNominalBz() const { return mBz; } - void setMatLUT(const o2::base::MatLayerCylSet* lut) { mMatLUT = lut; } - const o2::base::MatLayerCylSet* getMatLUT() const { return mMatLUT; } + 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(float bz) { mBz = bz; } + +#ifndef GPUCA_GPUCODE + static Propagator* Instance() + { + static Propagator instance; + return &instance; + } static int initFieldFromGRP(const o2::parameters::GRPObject* grp, bool verbose = false); static int initFieldFromGRP(const std::string grpFileName, std::string grpName = "GRP", bool verbose = false); +#endif + + GPUd() MatBudget getMatBudget(MatCorrType corrType, const o2::math_utils::Point3D<float>& p0, const o2::math_utils::Point3D<float>& p1) const; + GPUd() void getFiedXYZ(const math_utils::Point3D<float> xyz, float* bxyz) const; private: +#ifndef GPUCA_GPUCODE Propagator(); ~Propagator() = default; - - MatBudget getMatBudget(MatCorrType corrType, const Point3D<float>& p0, const Point3D<float>& p1) const; +#endif const o2::field::MagFieldFast* mField = nullptr; ///< External fast field (barrel only for the moment) float mBz = 0; // nominal field - const o2::base::MatLayerCylSet* mMatLUT = nullptr; // externally set LUT + const o2::base::MatLayerCylSet* mMatLUT = nullptr; // externally set LUT + const o2::gpu::GPUTPCGMPolynomialField* mGPUField = nullptr; // externally set GPU Field ClassDef(Propagator, 0); }; diff --git a/Detectors/Base/include/DetectorsBase/Ray.h b/Detectors/Base/include/DetectorsBase/Ray.h index 3e4752a82b720..4b489b599fb5f 100644 --- a/Detectors/Base/include/DetectorsBase/Ray.h +++ b/Detectors/Base/include/DetectorsBase/Ray.h @@ -21,7 +21,7 @@ #include "MathUtils/Utils.h" #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #endif // !GPUCA_ALIGPUCODE /********************************************************************** @@ -51,7 +51,7 @@ class Ray GPUdDefault() ~Ray() CON_DEFAULT; #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version - Ray(const Point3D<float> point0, const Point3D<float> point1); + Ray(const math_utils::Point3D<float> point0, const math_utils::Point3D<float> point1); #endif // !GPUCA_ALIGPUCODE GPUd() Ray(float x0, float y0, float z0, float x1, float y1, float z1); GPUd() int crossLayer(const MatLayerCyl& lr); @@ -79,7 +79,7 @@ class Ray GPUd() float getPhi(float t) const { float p = o2::gpu::CAMath::ATan2(mP[1] + t * mD[1], mP[0] + t * mD[0]); - o2::utils::BringTo02Pi(p); + o2::math_utils::bringTo02Pi(p); return p; } @@ -107,7 +107,7 @@ class Ray //______________________________________________________ #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version -inline Ray::Ray(const Point3D<float> point0, const Point3D<float> point1) +inline Ray::Ray(const math_utils::Point3D<float> point0, const math_utils::Point3D<float> point1) : mP{point0.X(), point0.Y(), point0.Z()}, mD{point1.X() - point0.X(), point1.Y() - point0.Y(), point1.Z() - point0.Z()} { mDistXY2 = mD[0] * mD[0] + mD[1] * mD[1]; diff --git a/Detectors/Base/src/CTFCoderBase.cxx b/Detectors/Base/src/CTFCoderBase.cxx new file mode 100644 index 0000000000000..433cafb8f0883 --- /dev/null +++ b/Detectors/Base/src/CTFCoderBase.cxx @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoderBase.cxx +/// \brief Defintions for CTFCoderBase class (support of external dictionaries) +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsCommonDataFormats/CTFHeader.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "TSystem.h" + +using namespace o2::ctf; + +template <typename T> +bool readFromTree(TTree& tree, const std::string brname, T& dest, int ev = 0) +{ + auto* br = tree.GetBranch(brname.c_str()); + if (br && br->GetEntries() > ev) { + auto* ptr = &dest; + br->SetAddress(&ptr); + br->GetEntry(ev); + br->ResetAddress(); + return true; + } + return false; +} + +std::unique_ptr<TFile> CTFCoderBase::loadDictionaryTreeFile(const std::string& dictPath, bool mayFail) +{ + TDirectory* curd = gDirectory; + std::unique_ptr<TFile> fileDict(gSystem->AccessPathName(dictPath.c_str()) ? nullptr : TFile::Open(dictPath.c_str())); + if (!fileDict || fileDict->IsZombie()) { + if (mayFail) { + LOG(INFO) << "CTF dictionary file " << dictPath << " for detector " << mDet.getName() << " is absent, will use dictionaries stored in CTF"; + fileDict.reset(); + return std::move(fileDict); + } + LOG(ERROR) << "Failed to open CTF dictionary file " << dictPath << " for detector " << mDet.getName(); + throw std::runtime_error("Failed to open dictionary file"); + } + auto tnm = std::string(o2::base::NameConf::CTFDICT); + std::unique_ptr<TTree> tree((TTree*)fileDict->Get(tnm.c_str())); + if (!tree) { + fileDict.reset(); + LOG(ERROR) << "Did not find CTF dictionary tree " << tnm << " in " << dictPath; + throw std::runtime_error("Did not fine CTF dictionary tree in the file"); + } + CTFHeader ctfHeader; + if (!readFromTree(*tree.get(), "CTFHeader", ctfHeader) || !ctfHeader.detectors[mDet]) { + tree.reset(); + fileDict.reset(); + LOG(ERROR) << "Did not find CTF dictionary header or Detector " << mDet.getName() << " in it"; + if (!mayFail) { + throw std::runtime_error("did not find CTFHeader with needed detector"); + } + } else { + LOG(INFO) << "Found CTF dictionary for " << mDet.getName() << " in " << dictPath; + } + return fileDict; +} diff --git a/Detectors/Base/src/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index d927d33a8c945..5e7edcfdc77f3 100644 --- a/Detectors/Base/src/DetectorsBaseLinkDef.h +++ b/Detectors/Base/src/DetectorsBaseLinkDef.h @@ -29,4 +29,6 @@ #pragma link C++ class o2::base::DCAFitter + ; +#pragma link C++ class o2::ctf::CTFCoderBase + ; + #endif diff --git a/Detectors/Base/src/GeometryManager.cxx b/Detectors/Base/src/GeometryManager.cxx index cb3b6537c4498..b97c6c65c2f11 100644 --- a/Detectors/Base/src/GeometryManager.cxx +++ b/Detectors/Base/src/GeometryManager.cxx @@ -33,28 +33,18 @@ using namespace o2::base; /// Implementation of GeometryManager, the geometry manager class which interfaces to TGeo and /// the look-up table mapping unique volume indices to symbolic volume names. For that, it /// collects several static methods - -//______________________________________________________________________ -GeometryManager::GeometryManager() -{ - /// default constructor - - /// make sure detectors masks can be encoded in the combined det+sensor id - static_assert(sizeof(Int_t) * 8 - sDetOffset > DetID::getNDetectors(), - "N detectors exceeds available N bits for their encoding"); -} +std::mutex GeometryManager::sTGMutex; //______________________________________________________________________ Bool_t GeometryManager::getOriginalMatrix(const char* symname, TGeoHMatrix& m) { m.Clear(); - if (!gGeoManager || !gGeoManager->IsClosed()) { LOG(ERROR) << "No active geometry or geometry not yet closed!"; ; return kFALSE; } - + std::lock_guard<std::mutex> guard(sTGMutex); if (!gGeoManager->GetListOfPhysicalNodes()) { LOG(WARNING) << "gGeoManager doesn't contain any aligned nodes!"; @@ -91,7 +81,7 @@ Bool_t GeometryManager::getOriginalMatrixFromPath(const char* path, TGeoHMatrix& LOG(ERROR) << "Can't get the original global matrix! gGeoManager doesn't exist or it is still opened!"; return kFALSE; } - + std::lock_guard<std::mutex> guard(sTGMutex); if (!gGeoManager->CheckPath(path)) { LOG(ERROR) << "Volume path " << path << " not valid!"; return kFALSE; @@ -288,7 +278,7 @@ GeometryManager::MatBudgetExt GeometryManager::meanMaterialBudgetExt(float x0, f for (int i = 3; i--;) { dir[i] *= invlen; } - + std::lock_guard<std::mutex> guard(sTGMutex); // Initialize start point and direction TGeoNode* currentnode = gGeoManager->InitTrack(startD, dir); if (!currentnode) { @@ -322,7 +312,7 @@ GeometryManager::MatBudgetExt GeometryManager::meanMaterialBudgetExt(float x0, f // This means navigation has problems on one boundary // Try to cross by making a small step const double* curPos = gGeoManager->GetCurrentPoint(); - LOG(ERROR) << "Cannot cross boundary at (" << curPos[0] << ',' << curPos[1] << ',' << curPos[2] << ')'; + LOG(warning) << "Cannot cross boundary at (" << curPos[0] << ',' << curPos[1] << ',' << curPos[2] << ')'; budTotal.normalize(stepTot); budTotal.nCross = -1; // flag failed navigation return MatBudgetExt(budTotal); @@ -380,7 +370,7 @@ o2::base::MatBudget GeometryManager::meanMaterialBudget(float x0, float y0, floa for (int i = 3; i--;) { dir[i] *= invlen; } - + std::lock_guard<std::mutex> guard(sTGMutex); // Initialize start point and direction TGeoNode* currentnode = gGeoManager->InitTrack(startD, dir); if (!currentnode) { @@ -414,7 +404,7 @@ o2::base::MatBudget GeometryManager::meanMaterialBudget(float x0, float y0, floa // This means navigation has problems on one boundary // Try to cross by making a small step const double* curPos = gGeoManager->GetCurrentPoint(); - LOG(ERROR) << "Cannot cross boundary at (" << curPos[0] << ',' << curPos[1] << ',' << curPos[2] << ')'; + LOG(warning) << "Cannot cross boundary at (" << curPos[0] << ',' << curPos[1] << ',' << curPos[2] << ')'; budTotal.meanRho /= stepTot; budTotal.length = stepTot; return o2::base::MatBudget(budTotal); diff --git a/Detectors/Base/src/MatLayerCyl.cxx b/Detectors/Base/src/MatLayerCyl.cxx index 906c2f5936536..f8705a505b334 100644 --- a/Detectors/Base/src/MatLayerCyl.cxx +++ b/Detectors/Base/src/MatLayerCyl.cxx @@ -132,7 +132,7 @@ void MatLayerCyl::populateFromTGeo(int ip, int iz, int ntrPerCell) float zs = zmn + (isz + 0.5) * dz; float dzt = zs > 0.f ? 0.25 * dz : -0.25 * dz; // to avoid 90 degree polar angle for (int isp = ntrPerCell; isp--;) { - o2::utils::sincosf(phmn + (isp + 0.5) * getDPhi() / ntrPerCell, sn, cs); + 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); if (bud.length > 0.) { meanRho += bud.length * bud.meanRho; @@ -174,10 +174,12 @@ bool MatLayerCyl::cellsDiffer(const MatCell& cellA, const MatCell& cellB, float /// check if the cells content is different within the tolerance float rav = 0.5 * (cellA.meanRho + cellB.meanRho), xav = 0.5 * (cellA.meanX2X0 + cellB.meanX2X0); float rdf = 0.5 * (cellA.meanRho - cellB.meanRho), xdf = 0.5 * (cellA.meanX2X0 - cellB.meanX2X0); - if (rav > 0 && std::abs(rdf / rav) > maxRelDiff) + if (rav > 0 && std::abs(rdf / rav) > maxRelDiff) { return true; - if (xav > 0 && std::abs(xdf / xav) > maxRelDiff) + } + if (xav > 0 && std::abs(xdf / xav) > maxRelDiff) { return true; + } return false; } diff --git a/Detectors/Base/src/MatLayerCylSet.cxx b/Detectors/Base/src/MatLayerCylSet.cxx index 13915a23fab6e..3d110766c6436 100644 --- a/Detectors/Base/src/MatLayerCylSet.cxx +++ b/Detectors/Base/src/MatLayerCylSet.cxx @@ -133,9 +133,10 @@ void MatLayerCylSet::dumpToTree(const std::string outName) const if (ip + 1 < nphib) { int ips1 = lr.phiBin2Slice(ip + 1); merge = ips == ips1 ? -1 : lr.canMergePhiSlices(ips, ips1); // -1 for already merged - } else + } else { merge = -2; // last one - o2::utils::sincosf(phi, sn, cs); + } + o2::math_utils::sincos(phi, sn, cs); float x = r * cs, y = r * sn; for (int iz = 0; iz < lr.getNZBins(); iz++) { float z = 0.5 * (lr.getZBinMin(iz) + lr.getZBinMax(iz)); @@ -305,7 +306,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa } } // account materials of this step - float step = tEndZ - tStartZ; // the real step is ray.getDist(tEnd-tStart), will rescale all later + float step = tEndZ > tStartZ ? tEndZ - tStartZ : tStartZ - tEndZ; // the real step is ray.getDist(tEnd-tStart), will rescale all later const auto& cell = lr.getCell(phiID, zID); rval.meanRho += cell.meanRho * step; rval.meanX2X0 += cell.meanX2X0 * step; @@ -325,7 +326,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa zID += stepZID; } while (checkMoreZ); } else { - float step = tEndPhi - tStartPhi; // the real step is |ray.getDist(tEnd-tStart)|, will rescale all later + float step = tEndPhi > tStartPhi ? tEndPhi - tStartPhi : tStartPhi - tEndPhi; // the real step is |ray.getDist(tEnd-tStart)|, will rescale all later const auto& cell = lr.getCell(phiID, zID); rval.meanRho += cell.meanRho * step; rval.meanX2X0 += cell.meanX2X0 * step; @@ -352,8 +353,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa if (rval.length != 0.f) { rval.meanRho /= rval.length; // average - float norm = (rval.length < 0.f) ? -ray.getDist() : ray.getDist(); // normalize - rval.meanX2X0 *= norm; + rval.meanX2X0 *= ray.getDist(); // normalize } rval.length = ray.getDist(); diff --git a/Detectors/Base/src/MaterialManager.cxx b/Detectors/Base/src/MaterialManager.cxx index 76ad1ddb0229a..2b184635f0574 100644 --- a/Detectors/Base/src/MaterialManager.cxx +++ b/Detectors/Base/src/MaterialManager.cxx @@ -405,14 +405,14 @@ void MaterialManager::loadCutsAndProcessesFromFile(const char* modname, const ch // apply cuts via material manager interface for (int i = 0; i < NCUTS; ++i) { if (cut[i] >= 0.) { - SpecialCut(modname, itmed, cutnames[i], cut[i]); + SpecialCut(detName, itmed, cutnames[i], cut[i]); } } // apply process flags for (int i = 0; i < NFLAGS - 1; ++i) { if (flag[i] >= 0) { - SpecialProcess(modname, itmed, procnames[i], flag[i]); + SpecialProcess(detName, itmed, procnames[i], flag[i]); } } } // end loop over lines diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index db8147e68085d..098d3bc440376 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -9,17 +9,25 @@ // or submit itself to any jurisdiction. #include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GeometryManager.h" -#include <FairLogger.h> -#include <FairRunAna.h> // eventually will get rid of it -#include <TGeoGlobalMagField.h> -#include "DataFormatsParameters/GRPObject.h" -#include "Field/MagFieldFast.h" -#include "Field/MagneticField.h" +#include "GPUCommonLogger.h" +#include "GPUCommonMath.h" +#include "GPUTPCGMPolynomialField.h" #include "MathUtils/Utils.h" #include "ReconstructionDataFormats/Vertex.h" using namespace o2::base; +using namespace o2::gpu; + +#if !defined(GPUCA_GPUCODE) +#include "Field/MagFieldFast.h" // Don't use this on the GPU +#endif + +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) +#include "Field/MagneticField.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DetectorsBase/GeometryManager.h" +#include <FairRunAna.h> // eventually will get rid of it +#include <TGeoGlobalMagField.h> Propagator::Propagator() { @@ -47,9 +55,58 @@ Propagator::Propagator() mField->GetBz(xyz, mBz); } +//____________________________________________________________ +int Propagator::initFieldFromGRP(const std::string grpFileName, std::string grpName, bool verbose) +{ + /// load grp and init magnetic field + if (verbose) { + LOG(INFO) << "Loading field from GRP of " << grpFileName; + } + const auto grp = o2::parameters::GRPObject::loadFrom(grpFileName, grpName); + if (!grp) { + return -1; + } + if (verbose) { + grp->print(); + } + + return initFieldFromGRP(grp); +} + +//____________________________________________________________ +int Propagator::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()); + 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<o2::field::MagneticField*>( TGeoGlobalMagField::Instance()->GetField() )"; + } + return 0; +} +#elif !defined(GPUCA_GPUCODE) +Propagator::Propagator() +{ +} // empty dummy constructor for standalone benchmark +#endif + //_______________________________________________________________________ -bool Propagator::PropagateToXBxByBz(o2::track::TrackParCov& track, float xToGo, float mass, float maxSnp, float maxStep, - Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const +GPUd() bool Propagator::PropagateToXBxByBz(o2::track::TrackParCov& track, float xToGo, float maxSnp, float maxStep, + Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const { //---------------------------------------------------------------- // @@ -57,7 +114,6 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackParCov& track, float xToGo, // taking into account all the three components of the magnetic field // and correcting for the crossed material. // - // mass - mass used in propagation - used for energy loss correction (if <0 then q=2) // maxStep - maximal step for propagation // tofInfo - optional container for track length and PID-dependent TOF integration // @@ -70,26 +126,26 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackParCov& track, float xToGo, signCorr = -dir; // sign of eloss correction is not imposed } - std::array<float, 3> b; - while (std::abs(dx) > Epsilon) { - auto step = std::min(std::abs(dx), maxStep); + gpu::gpustd::array<float, 3> b; + while (CAMath::Abs(dx) > Epsilon) { + auto step = CAMath::Min(CAMath::Abs(dx), maxStep); if (dir < 0) { step = -step; } auto x = track.getX() + step; auto xyz0 = track.getXYZGlo(); - mField->Field(xyz0, b.data()); + getFiedXYZ(xyz0, &b[0]); if (!track.propagateTo(x, b)) { return false; } - if (maxSnp > 0 && std::abs(track.getSnp()) >= maxSnp) { + if (maxSnp > 0 && CAMath::Abs(track.getSnp()) >= maxSnp) { return false; } if (matCorr != MatCorrType::USEMatCorrNONE) { auto xyz1 = track.getXYZGlo(); auto mb = getMatBudget(matCorr, xyz0, xyz1); - if (!track.correctForMaterial(mb.meanX2X0, ((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho, mass)) { + if (!track.correctForMaterial(mb.meanX2X0, ((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { return false; } @@ -99,7 +155,7 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackParCov& track, float xToGo, } } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght auto xyz1 = track.getXYZGlo(); - Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + math_utils::Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); tofInfo->addStep(stepV.R(), track); } dx = xToGo - track.getX(); @@ -108,8 +164,8 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackParCov& track, float xToGo, } //_______________________________________________________________________ -bool Propagator::PropagateToXBxByBz(o2::track::TrackPar& track, float xToGo, float mass, float maxSnp, float maxStep, - Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const +GPUd() bool Propagator::PropagateToXBxByBz(o2::track::TrackPar& track, float xToGo, float maxSnp, float maxStep, + Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const { //---------------------------------------------------------------- // @@ -117,7 +173,6 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackPar& track, float xToGo, flo // taking into account all the three components of the magnetic field // and optionally correcting for the e.loss crossed material. // - // mass - mass used in propagation - used for energy loss correction (if <0 then q=2) // maxStep - maximal step for propagation // tofInfo - optional container for track length and PID-dependent TOF integration // @@ -130,26 +185,26 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackPar& track, float xToGo, flo signCorr = -dir; // sign of eloss correction is not imposed } - std::array<float, 3> b; - while (std::abs(dx) > Epsilon) { - auto step = std::min(std::abs(dx), maxStep); + gpu::gpustd::array<float, 3> b; + while (CAMath::Abs(dx) > Epsilon) { + auto step = CAMath::Min(CAMath::Abs(dx), maxStep); if (dir < 0) { step = -step; } auto x = track.getX() + step; auto xyz0 = track.getXYZGlo(); - mField->Field(xyz0, b.data()); + getFiedXYZ(xyz0, &b[0]); if (!track.propagateParamTo(x, b)) { return false; } - if (maxSnp > 0 && std::abs(track.getSnp()) >= maxSnp) { + if (maxSnp > 0 && CAMath::Abs(track.getSnp()) >= maxSnp) { 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, mass)) { + if (!track.correctForELoss(((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { return false; } if (tofInfo) { @@ -158,7 +213,7 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackPar& track, float xToGo, flo } } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght auto xyz1 = track.getXYZGlo(); - Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + math_utils::Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); tofInfo->addStep(stepV.R(), track); } dx = xToGo - track.getX(); @@ -167,8 +222,8 @@ bool Propagator::PropagateToXBxByBz(o2::track::TrackPar& track, float xToGo, flo } //_______________________________________________________________________ -bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float bZ, float mass, float maxSnp, float maxStep, - Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const +GPUd() bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float bZ, float maxSnp, float maxStep, + Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const { //---------------------------------------------------------------- // @@ -176,7 +231,6 @@ bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float // taking into account all the three components of the magnetic field // and correcting for the crossed material. // - // mass - mass used in propagation - used for energy loss correction (if <0 then q=2) // maxStep - maximal step for propagation // tofInfo - optional container for track length and PID-dependent TOF integration // @@ -189,8 +243,8 @@ bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float signCorr = -dir; // sign of eloss correction is not imposed } - while (std::abs(dx) > Epsilon) { - auto step = std::min(std::abs(dx), maxStep); + while (CAMath::Abs(dx) > Epsilon) { + auto step = CAMath::Min(CAMath::Abs(dx), maxStep); if (dir < 0) { step = -step; } @@ -200,14 +254,14 @@ bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float if (!track.propagateTo(x, bZ)) { return false; } - if (maxSnp > 0 && std::abs(track.getSnp()) >= maxSnp) { + if (maxSnp > 0 && CAMath::Abs(track.getSnp()) >= maxSnp) { return false; } if (matCorr != MatCorrType::USEMatCorrNONE) { auto xyz1 = track.getXYZGlo(); auto mb = getMatBudget(matCorr, xyz0, xyz1); // - if (!track.correctForMaterial(mb.meanX2X0, ((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho, mass)) { + if (!track.correctForMaterial(mb.meanX2X0, ((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { return false; } @@ -217,7 +271,7 @@ bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float } } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght auto xyz1 = track.getXYZGlo(); - Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + math_utils::Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); tofInfo->addStep(stepV.R(), track); } dx = xToGo - track.getX(); @@ -226,8 +280,8 @@ bool Propagator::propagateToX(o2::track::TrackParCov& track, float xToGo, float } //_______________________________________________________________________ -bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, float mass, float maxSnp, float maxStep, - Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const +GPUd() bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, float maxSnp, float maxStep, + Propagator::MatCorrType matCorr, o2::track::TrackLTIntegral* tofInfo, int signCorr) const { //---------------------------------------------------------------- // @@ -235,7 +289,6 @@ bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, // taking into account all the three components of the magnetic field // and correcting for the crossed material. // - // mass - mass used in propagation - used for energy loss correction (if <0 then q=2) // maxStep - maximal step for propagation // tofInfo - optional container for track length and PID-dependent TOF integration // @@ -248,8 +301,8 @@ bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, signCorr = -dir; // sign of eloss correction is not imposed } - while (std::abs(dx) > Epsilon) { - auto step = std::min(std::abs(dx), maxStep); + while (CAMath::Abs(dx) > Epsilon) { + auto step = CAMath::Min(CAMath::Abs(dx), maxStep); if (dir < 0) { step = -step; } @@ -259,14 +312,14 @@ bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, if (!track.propagateParamTo(x, bZ)) { return false; } - if (maxSnp > 0 && std::abs(track.getSnp()) >= maxSnp) { + if (maxSnp > 0 && CAMath::Abs(track.getSnp()) >= maxSnp) { 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, mass)) { + if (!track.correctForELoss(((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { return false; } @@ -276,7 +329,7 @@ bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, } } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght auto xyz1 = track.getXYZGlo(); - Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + math_utils::Vector3D<float> stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); tofInfo->addStep(stepV.R(), track); } dx = xToGo - track.getX(); @@ -285,43 +338,43 @@ bool Propagator::propagateToX(o2::track::TrackPar& track, float xToGo, float bZ, } //_______________________________________________________________________ -bool Propagator::propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, float bZ, - float mass, float maxStep, Propagator::MatCorrType matCorr, - o2::dataformats::DCA* dca, o2::track::TrackLTIntegral* tofInfo, - int signCorr, float maxD) const +GPUd() bool Propagator::propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, float bZ, + float maxStep, Propagator::MatCorrType matCorr, + o2::dataformats::DCA* dca, o2::track::TrackLTIntegral* tofInfo, + int signCorr, float maxD) const { // propagate track to DCA to the vertex float sn, cs, alp = track.getAlpha(); - o2::utils::sincosf(alp, sn, cs); - float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = std::sqrt((1.f - snp) * (1.f + snp)); + o2::math_utils::sincos(alp, sn, cs); + float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = CAMath::Sqrt((1.f - snp) * (1.f + snp)); float xv = vtx.getX() * cs + vtx.getY() * sn, yv = -vtx.getX() * sn + vtx.getY() * cs, zv = vtx.getZ(); x -= xv; y -= yv; //Estimate the impact parameter neglecting the track curvature - Double_t d = std::abs(x * snp - y * csp); + float d = CAMath::Abs(x * snp - y * csp); if (d > maxD) { return false; } float crv = track.getCurvature(bZ); float tgfv = -(crv * x - snp) / (crv * y + csp); - sn = tgfv / std::sqrt(1.f + tgfv * tgfv); - cs = std::sqrt((1. - sn) * (1. + sn)); - cs = (std::abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; + sn = tgfv / CAMath::Sqrt(1.f + tgfv * tgfv); + cs = CAMath::Sqrt((1. - sn) * (1. + sn)); + cs = (CAMath::Abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; x = xv * cs + yv * sn; yv = -xv * sn + yv * cs; xv = x; auto tmpT(track); // operate on the copy to recover after the failure - alp += std::asin(sn); - if (!tmpT.rotate(alp) || !propagateToX(tmpT, xv, bZ, mass, 0.85, maxStep, matCorr, tofInfo, signCorr)) { - LOG(ERROR) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: "; + alp += CAMath::ASin(sn); + if (!tmpT.rotate(alp) || !propagateToX(tmpT, xv, bZ, 0.85, maxStep, matCorr, tofInfo, signCorr)) { + LOG(WARNING) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: "; tmpT.print(); return false; } track = tmpT; if (dca) { - o2::utils::sincosf(alp, sn, cs); + o2::math_utils::sincos(alp, sn, cs); auto s2ylocvtx = vtx.getSigmaX2() * sn * sn + vtx.getSigmaY2() * cs * cs - 2. * vtx.getSigmaXY() * cs * sn; dca->set(track.getY() - yv, track.getZ() - zv, track.getSigmaY2() + s2ylocvtx, track.getSigmaZY(), track.getSigmaZ2() + vtx.getSigmaZ2()); @@ -330,43 +383,43 @@ bool Propagator::propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::trac } //_______________________________________________________________________ -bool Propagator::propagateToDCABxByBz(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, - float mass, float maxStep, Propagator::MatCorrType matCorr, - o2::dataformats::DCA* dca, o2::track::TrackLTIntegral* tofInfo, - int signCorr, float maxD) const +GPUd() bool Propagator::propagateToDCABxByBz(const o2::dataformats::VertexBase& vtx, o2::track::TrackParCov& track, + float maxStep, Propagator::MatCorrType matCorr, + o2::dataformats::DCA* dca, o2::track::TrackLTIntegral* tofInfo, + int signCorr, float maxD) const { // propagate track to DCA to the vertex float sn, cs, alp = track.getAlpha(); - o2::utils::sincosf(alp, sn, cs); - float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = std::sqrt((1.f - snp) * (1.f + snp)); + o2::math_utils::sincos(alp, sn, cs); + float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = CAMath::Sqrt((1.f - snp) * (1.f + snp)); float xv = vtx.getX() * cs + vtx.getY() * sn, yv = -vtx.getX() * sn + vtx.getY() * cs, zv = vtx.getZ(); x -= xv; y -= yv; //Estimate the impact parameter neglecting the track curvature - Double_t d = std::abs(x * snp - y * csp); + float d = CAMath::Abs(x * snp - y * csp); if (d > maxD) { return false; } float crv = track.getCurvature(mBz); float tgfv = -(crv * x - snp) / (crv * y + csp); - sn = tgfv / std::sqrt(1.f + tgfv * tgfv); - cs = std::sqrt((1. - sn) * (1. + sn)); - cs = (std::abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; + sn = tgfv / CAMath::Sqrt(1.f + tgfv * tgfv); + cs = CAMath::Sqrt((1. - sn) * (1. + sn)); + cs = (CAMath::Abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; x = xv * cs + yv * sn; yv = -xv * sn + yv * cs; xv = x; auto tmpT(track); // operate on the copy to recover after the failure - alp += std::asin(sn); - if (!tmpT.rotate(alp) || !PropagateToXBxByBz(tmpT, xv, mass, 0.85, maxStep, matCorr, tofInfo, signCorr)) { - LOG(ERROR) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: "; + alp += CAMath::ASin(sn); + if (!tmpT.rotate(alp) || !PropagateToXBxByBz(tmpT, xv, 0.85, maxStep, matCorr, tofInfo, signCorr)) { + LOG(WARNING) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: "; tmpT.print(); return false; } track = tmpT; if (dca) { - o2::utils::sincosf(alp, sn, cs); + o2::math_utils::sincos(alp, sn, cs); auto s2ylocvtx = vtx.getSigmaX2() * sn * sn + vtx.getSigmaY2() * cs * cs - 2. * vtx.getSigmaXY() * cs * sn; dca->set(track.getY() - yv, track.getZ() - zv, track.getSigmaY2() + s2ylocvtx, track.getSigmaZY(), track.getSigmaZ2() + vtx.getSigmaZ2()); @@ -375,38 +428,38 @@ bool Propagator::propagateToDCABxByBz(const o2::dataformats::VertexBase& vtx, o2 } //_______________________________________________________________________ -bool Propagator::propagateToDCA(const Point3D<float>& vtx, o2::track::TrackPar& track, float bZ, - float mass, float maxStep, Propagator::MatCorrType matCorr, - std::array<float, 2>* dca, o2::track::TrackLTIntegral* tofInfo, - int signCorr, float maxD) const +GPUd() bool Propagator::propagateToDCA(const math_utils::Point3D<float>& vtx, o2::track::TrackPar& track, float bZ, + float maxStep, Propagator::MatCorrType matCorr, + gpu::gpustd::array<float, 2>* dca, o2::track::TrackLTIntegral* tofInfo, + int signCorr, float maxD) const { // propagate track to DCA to the vertex float sn, cs, alp = track.getAlpha(); - o2::utils::sincosf(alp, sn, cs); - float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = std::sqrt((1.f - snp) * (1.f + snp)); + o2::math_utils::sincos(alp, sn, cs); + float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = CAMath::Sqrt((1.f - snp) * (1.f + snp)); float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z(); x -= xv; y -= yv; //Estimate the impact parameter neglecting the track curvature - Double_t d = std::abs(x * snp - y * csp); + float d = CAMath::Abs(x * snp - y * csp); if (d > maxD) { return false; } float crv = track.getCurvature(bZ); float tgfv = -(crv * x - snp) / (crv * y + csp); - sn = tgfv / std::sqrt(1.f + tgfv * tgfv); - cs = std::sqrt((1. - sn) * (1. + sn)); - cs = (std::abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; + sn = tgfv / CAMath::Sqrt(1.f + tgfv * tgfv); + cs = CAMath::Sqrt((1. - sn) * (1. + sn)); + cs = (CAMath::Abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; x = xv * cs + yv * sn; yv = -xv * sn + yv * cs; xv = x; auto tmpT(track); // operate on the copy to recover after the failure - alp += std::asin(sn); - if (!tmpT.rotateParam(alp) || !propagateToX(tmpT, xv, bZ, mass, 0.85, maxStep, matCorr, tofInfo, signCorr)) { - LOG(ERROR) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " - << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z() << " | Track is: "; + alp += CAMath::ASin(sn); + if (!tmpT.rotateParam(alp) || !propagateToX(tmpT, xv, bZ, 0.85, maxStep, matCorr, tofInfo, signCorr)) { + LOG(WARNING) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " + << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z() << " | Track is: "; tmpT.printParam(); return false; } @@ -419,38 +472,38 @@ bool Propagator::propagateToDCA(const Point3D<float>& vtx, o2::track::TrackPar& } //_______________________________________________________________________ -bool Propagator::propagateToDCABxByBz(const Point3D<float>& vtx, o2::track::TrackPar& track, - float mass, float maxStep, Propagator::MatCorrType matCorr, - std::array<float, 2>* dca, o2::track::TrackLTIntegral* tofInfo, - int signCorr, float maxD) const +GPUd() bool Propagator::propagateToDCABxByBz(const math_utils::Point3D<float>& vtx, o2::track::TrackPar& track, + float maxStep, Propagator::MatCorrType matCorr, + gpu::gpustd::array<float, 2>* dca, o2::track::TrackLTIntegral* tofInfo, + int signCorr, float maxD) const { // propagate track to DCA to the vertex float sn, cs, alp = track.getAlpha(); - o2::utils::sincosf(alp, sn, cs); - float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = std::sqrt((1.f - snp) * (1.f + snp)); + o2::math_utils::sincos(alp, sn, cs); + float x = track.getX(), y = track.getY(), snp = track.getSnp(), csp = CAMath::Sqrt((1.f - snp) * (1.f + snp)); float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z(); x -= xv; y -= yv; //Estimate the impact parameter neglecting the track curvature - Double_t d = std::abs(x * snp - y * csp); + float d = CAMath::Abs(x * snp - y * csp); if (d > maxD) { return false; } float crv = track.getCurvature(mBz); float tgfv = -(crv * x - snp) / (crv * y + csp); - sn = tgfv / std::sqrt(1.f + tgfv * tgfv); - cs = std::sqrt((1. - sn) * (1. + sn)); - cs = (std::abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; + sn = tgfv / CAMath::Sqrt(1.f + tgfv * tgfv); + cs = CAMath::Sqrt((1. - sn) * (1. + sn)); + cs = (CAMath::Abs(tgfv) > o2::constants::math::Almost0) ? sn / tgfv : o2::constants::math::Almost1; x = xv * cs + yv * sn; yv = -xv * sn + yv * cs; xv = x; auto tmpT(track); // operate on the copy to recover after the failure - alp += std::asin(sn); - if (!tmpT.rotateParam(alp) || !PropagateToXBxByBz(tmpT, xv, mass, 0.85, maxStep, matCorr, tofInfo, signCorr)) { - LOG(ERROR) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " - << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z() << " | Track is: "; + alp += CAMath::ASin(sn); + if (!tmpT.rotateParam(alp) || !PropagateToXBxByBz(tmpT, xv, 0.85, maxStep, matCorr, tofInfo, signCorr)) { + LOG(WARNING) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " + << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z() << " | Track is: "; tmpT.printParam(); return false; } @@ -463,51 +516,28 @@ bool Propagator::propagateToDCABxByBz(const Point3D<float>& vtx, o2::track::Trac } //____________________________________________________________ -int Propagator::initFieldFromGRP(const std::string grpFileName, std::string grpName, bool verbose) +GPUd() MatBudget Propagator::getMatBudget(Propagator::MatCorrType corrType, const math_utils::Point3D<float>& p0, const math_utils::Point3D<float>& p1) const { - /// load grp and init magnetic field - if (verbose) { - LOG(INFO) << "Loading field from GRP of " << grpFileName; - } - const auto grp = o2::parameters::GRPObject::loadFrom(grpFileName, grpName); - if (!grp) { - return -1; - } - if (verbose) { - grp->print(); +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) + if (corrType == MatCorrType::USEMatCorrTGeo || !mMatLUT) { + return GeometryManager::meanMaterialBudget(p0, p1); } - - return initFieldFromGRP(grp); +#endif + return mMatLUT->getMatBudget(p0.X(), p0.Y(), p0.Z(), p1.X(), p1.Y(), p1.Z()); } -//____________________________________________________________ -int Propagator::initFieldFromGRP(const o2::parameters::GRPObject* grp, bool verbose) +GPUd() void Propagator::getFiedXYZ(const math_utils::Point3D<float> xyz, float* bxyz) const { - /// 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()); - 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<o2::field::MagneticField*>( TGeoGlobalMagField::Instance()->GetField() )"; + 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 + f->GetField(xyz.X(), xyz.Y(), xyz.Z(), bxyz); + } else { +#ifndef GPUCA_GPUCODE + mField->Field(xyz, bxyz); // Must not call the host-only function in GPU compilation +#endif } - return 0; -} - -//____________________________________________________________ -MatBudget Propagator::getMatBudget(Propagator::MatCorrType corrType, const Point3D<float>& p0, const Point3D<float>& p1) const -{ - return (corrType == MatCorrType::USEMatCorrTGeo) ? GeometryManager::meanMaterialBudget(p0, p1) : mMatLUT->getMatBudget(p0.X(), p0.Y(), p0.Z(), p1.X(), p1.Y(), p1.Z()); } diff --git a/Detectors/Base/src/Ray.cxx b/Detectors/Base/src/Ray.cxx index abad6ee80fd9b..1fede46d3be26 100644 --- a/Detectors/Base/src/Ray.cxx +++ b/Detectors/Base/src/Ray.cxx @@ -26,8 +26,9 @@ GPUd() int Ray::crossLayer(const MatLayerCyl& lr) // Region of valid t is 0:1. // Straigh line may have 2 crossings with cyl. layer float detMax = mXDxPlusYDy2 - mDistXY2 * (mR02 - lr.getRMax2()); - if (detMax < 0) + if (detMax < 0) { return 0; // does not reach outer R, hence inner also + } float detMaxRed = CAMath::Sqrt(detMax) * mDistXY2i; float tCross0Max = mXDxPlusYDyRed + detMaxRed; // largest possible t diff --git a/Detectors/Base/test/README.md b/Detectors/Base/test/README.md index cfbd2390998d2..9b6e798268be6 100644 --- a/Detectors/Base/test/README.md +++ b/Detectors/Base/test/README.md @@ -26,7 +26,7 @@ float xyz0[3] = {0.,0.,0.}; float xyz1[3] = {70.,80.,20.}; auto mb = mbl.getMatBudget(xyz0[0],xyz0[1],xyz0[2], xyz1[0],xyz1[1],xyz1[2]); -// alternatively, use MatCell getMatBudget(const Point3D<float> &point0,const Point3D<float> &point1) method +// alternatively, use MatCell getMatBudget(const math_utils::Point3D<float> &point0,const math_utils::Point3D<float> &point1) method std::cout << "<rho>= " << mb.meanRho << " <x/X0>= " << mb.meanX2X0 << "\n"; ``` diff --git a/Detectors/Base/test/buildMatBudLUT.C b/Detectors/Base/test/buildMatBudLUT.C index 6244b6cec0018..b0f0d0d2ac2b3 100644 --- a/Detectors/Base/test/buildMatBudLUT.C +++ b/Detectors/Base/test/buildMatBudLUT.C @@ -15,9 +15,11 @@ #include "DetectorsBase/MatLayerCylSet.h" #include "DetectorsBase/MatLayerCyl.h" #include "DetectorsBase/GeometryManager.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" #include "DetectorsCommonDataFormats/NameConf.h" #include <TFile.h> #include <TSystem.h> +#include <TStopwatch.h> #endif #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version @@ -60,16 +62,22 @@ bool buildMatBudLUT(int nTst, int maxLr, std::string outName, std::string outFil } for (int i = 0; i < maxLr; i++) { auto& l = lrData[i]; + printf("L:%3d %6.2f<R<%6.2f ZH=%5.1f | dz = %6.2f drph = %6.2f\n", i, l.rMin, l.rMax, l.zHalf, l.dZMin, l.dRPhiMin); mbLUT.addLayer(l.rMin, l.rMax, l.zHalf, l.dZMin, l.dRPhiMin); } + TStopwatch sw; mbLUT.populateFromTGeo(nTst); mbLUT.optimizePhiSlices(); // move to populateFromTGeo mbLUT.flatten(); // move to populateFromTGeo mbLUT.writeToFile(outFile, outName); - + sw.Stop(); + sw.Print(); + sw.Start(false); mbLUT.dumpToTree("matbudTree.root"); + sw.Stop(); + sw.Print(); return true; } @@ -172,18 +180,23 @@ bool testMBLUT(std::string lutName, std::string lutFile) //_______________________________________________________________________ void configLayers() { + const int NSect = 18; + const float kToler = 1e-3; float drStep = 0.f, zSpanH = 0.f, zBin = 0.f, rphiBin = 0.f, phiBin = 0.f; + o2::itsmft::ChipMappingITS mp; + int nStave = 0; + // rMin rMax zHalf lrData.emplace_back(LrData(0.0f, 1.8f, 30.f)); // beam pipe - lrData.emplace_back(LrData(lrData.back().rMax, 1.9f, 30.f)); + lrData.emplace_back(LrData(lrData.back().rMax, 2.0f, 30.f)); // ITS Inner Barrel drStep = 0.1; - zSpanH = 17.; + zSpanH = 20.; rphiBin = 0.2; // 0.1; zBin = 0.5; do { @@ -191,64 +204,140 @@ void configLayers() } while (lrData.back().rMax < 5.2 + kToler); // air space between Inner and Middle Barrels - lrData.emplace_back(LrData(lrData.back().rMax, 18.0, zSpanH)); + zSpanH = 40.; + zBin = 5.; + rphiBin = 2.; + lrData.emplace_back(LrData(lrData.back().rMax, 19.0, zSpanH, zBin, rphiBin)); + //=================================================================================== // ITS Middle Barrel - drStep = 0.2; - zSpanH = 50.; - rphiBin = 0.5; + nStave = mp.getNStavesOnLr(3); // Lr 3 + zSpanH = 55.; zBin = 0.5; + drStep = 0.2; + 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); + + 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); + + nStave = mp.getNStavesOnLr(3); // Lr 4 + drStep = 0.2; + 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 < 25.6 + 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); + //=================================================================================== + // air space between Middle and Outer Barrels zSpanH = 80.f; lrData.emplace_back(LrData(lrData.back().rMax, 33.5, zSpanH)); + //=================================================================================== // ITS Outer barrel - drStep = 0.5; + nStave = mp.getNStavesOnLr(5); // Lr 5 + drStep = 0.25; zSpanH = 80.; - rphiBin = 1.; zBin = 1.; 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 < 45.5 + kToler); + } while (lrData.back().rMax < 36. + kToler); + + drStep = 1.; + 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 < 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); + lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + } while (lrData.back().rMax < 41. + kToler); + + drStep = 1.; + 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 < 44. + kToler); + + //=================================================================================== - // air space between Outer Barrel and shell zSpanH = 100.f; - lrData.emplace_back(LrData(lrData.back().rMax, 59.5, zSpanH)); + zBin = 5.; + lrData.emplace_back(LrData(lrData.back().rMax, 47., zSpanH, zBin)); - // Shell - drStep = 0.5; - zSpanH = 100.; - rphiBin = 1.; - zBin = 1.; + 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 < 63. + kToler); + } while (lrData.back().rMax < 53. + kToler); - // air space between Shell and TPC - zSpanH = 250.f; - lrData.emplace_back(LrData(lrData.back().rMax, 76, zSpanH)); + zSpanH = 120.f; + lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH)); + + zSpanH = 150.f; + drStep = 4.; + zBin = 15.; + rphiBin = 10; + do { + lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + } while (lrData.back().rMax < 68.5 + kToler); + zSpanH = 250.f; + zBin = 25.; + rphiBin = 5; + { + auto rmean = (lrData.back().rMax + 76) / 2.; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 2); + lrData.emplace_back(LrData(lrData.back().rMax, 76, zSpanH, zBin, rphiBin)); + } // TPC inner vessel // up to r = 78.5 zSpanH = 250.f; - rphiBin = 1.; zBin = 25.; - lrData.emplace_back(LrData(lrData.back().rMax, 78.5, zSpanH, zBin, rphiBin)); - + { + 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)); + } // zSpanH = 250.f; - rphiBin = 2.; zBin = 2; - lrData.emplace_back(LrData(lrData.back().rMax, 84.5, zSpanH, zBin, rphiBin)); + { + auto rmean = (lrData.back().rMax + 78.5) / 2; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); + lrData.emplace_back(LrData(lrData.back().rMax, 84.5, zSpanH, zBin, rphiBin)); + } // TPC drum zSpanH = 250.f; lrData.emplace_back(LrData(lrData.back().rMax, 250.0, zSpanH)); + //=============================== + // TPC outer vessel zSpanH = 247.f; // ignore large lumps of material at |z|>247 rphiBin = 2.; @@ -260,13 +349,38 @@ void configLayers() zBin = 999.; // no segmentation in Z lrData.emplace_back(LrData(lrData.back().rMax, 280., zSpanH, zBin, rphiBin)); + // TRD + + zSpanH = 360.; + drStep = 1; + zBin = 10; + do { + auto rmean = lrData.back().rMax + drStep / 2; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); + lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + } while (lrData.back().rMax < 370); + + // TOF + zSpanH = 380.; drStep = 1; - zBin = 5; + zBin = 10; rphiBin = 5.; do { - zSpanH = lrData.back().rMax; + auto rmean = lrData.back().rMax + drStep / 2; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); } while (lrData.back().rMax < 400); + + // rest + drStep = 1; + zBin = 10; + rphiBin = 5.; + do { + zSpanH = lrData.back().rMax; + auto rmean = lrData.back().rMax + drStep / 2; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); + lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + } while (lrData.back().rMax < 500); } #endif //!_COMPILED_ON_GPU_ diff --git a/Detectors/CMakeLists.txt b/Detectors/CMakeLists.txt index 9b41c803e8ba1..e15b6fefa0c04 100644 --- a/Detectors/CMakeLists.txt +++ b/Detectors/CMakeLists.txt @@ -32,8 +32,10 @@ add_subdirectory(TPC) add_subdirectory(GlobalTracking) add_subdirectory(GlobalTrackingWorkflow) add_subdirectory(Vertexing) +add_subdirectory(AOD) add_subdirectory(Calibration) +add_subdirectory(DCS) if(BUILD_SIMULATION) add_subdirectory(gconfig) diff --git a/Detectors/CPV/base/CMakeLists.txt b/Detectors/CPV/base/CMakeLists.txt index e3ad172022a44..40286d5f9f733 100644 --- a/Detectors/CPV/base/CMakeLists.txt +++ b/Detectors/CPV/base/CMakeLists.txt @@ -9,9 +9,14 @@ # submit itself to any jurisdiction. o2_add_library(CPVBase - SOURCES src/Geometry.cxx src/Hit.cxx src/CPVSimParams.cxx + SOURCES src/Geometry.cxx + src/Hit.cxx + src/CPVSimParams.cxx + src/RCUTrailer.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat) o2_target_root_dictionary(CPVBase HEADERS include/CPVBase/Geometry.h - include/CPVBase/Hit.h include/CPVBase/CPVSimParams.h) + include/CPVBase/Hit.h + include/CPVBase/CPVSimParams.h + include/CPVBase/RCUTrailer.h) diff --git a/Detectors/CPV/base/include/CPVBase/CPVSimParams.h b/Detectors/CPV/base/include/CPVBase/CPVSimParams.h index 501902c0ff518..bc827cb64a5f0 100644 --- a/Detectors/CPV/base/include/CPVBase/CPVSimParams.h +++ b/Detectors/CPV/base/include/CPVBase/CPVSimParams.h @@ -39,14 +39,14 @@ struct CPVSimParams : public o2::conf::ConfigurableParamHelper<CPVSimParams> { //Parameters used in electronic noise calculation and thresholds (Digitizer) bool mApplyDigitization = true; ///< if energy digitization should be applied - float mZSthreshold = 0.005; ///< Zero Suppression threshold + float mZSthreshold = 0.01; ///< Zero Suppression threshold float mADCWidth = 0.005; ///< Widht of ADC channel used for energy digitization - float mNoise = 0.03; ///< charge noise in one pad + float mNoise = 0.01; ///< charge noise in one pad float mCoeffToNanoSecond = 1.e+9; ///< Conversion for time units float mSortingDelta = 0.1; ///< used in sorting clusters inverse sorting band in cm //Parameters used in clusterization - float mDigitMinEnergy = 0.005; ///< Minimal amplitude of a digit to be used in cluster + float mDigitMinEnergy = 0.01; ///< Minimal amplitude of a digit to be used in cluster float mClusteringThreshold = 0.050; ///< Seed digit minimal amplitude float mUnfogingEAccuracy = 1.e-3; ///< Accuracy of energy calculation in unfoding prosedure (GeV) float mUnfogingXZAccuracy = 1.e-1; ///< Accuracy of position calculation in unfolding procedure (cm) diff --git a/Detectors/CPV/base/include/CPVBase/Geometry.h b/Detectors/CPV/base/include/CPVBase/Geometry.h index 21e535639ce74..99df0860473ef 100644 --- a/Detectors/CPV/base/include/CPVBase/Geometry.h +++ b/Detectors/CPV/base/include/CPVBase/Geometry.h @@ -13,6 +13,7 @@ #include <string> +#include <Rtypes.h> #include <RStringView.h> #include <TMath.h> @@ -27,6 +28,22 @@ class Geometry static constexpr short kNumberOfCPVPadsZ = 60; static constexpr float kCPVPadSizePhi = 1.13; static constexpr float kCPVPadSizeZ = 2.1093; + //for hwaddress + static constexpr short kNPAD = 48; + static constexpr short kNDilogic = 10; + static constexpr short kNRow = 16; + static constexpr short kNDDL = 4; + + /// Available numbering schems: + /// relative pad coordinates + /// relId[3]={Module, phi col, z row} where Module=1..3, phi col=0..127, z row=0..59 + /// Absolute pad coordunate + /// absId=0..128*60*3=23040 + /// Raw addresses: + /// DDL corresponds to one module: ddl=Module-1 + /// each module consist of 16 columns of width 8 pads: row=0..15 + /// Each column consists of 10 dilogics (in z direction) dilogic=0...9 + /// Ecah dilogic contains 8*6 pads: hwaddress=0...48 /// /// Default constructor. @@ -54,7 +71,7 @@ class Geometry // = 1 are neighbour // = 2 are not neighbour but do not continue searching // =-1 are not neighbour, continue searching, but do not look before d2 next time - static int areNeighbours(short absId1, short absId2); + static short areNeighbours(unsigned short absId1, unsigned short absId2); /// /// \return AbsId index of the CPV cell @@ -63,17 +80,22 @@ class Geometry /// \param strip: strip number // \param cell: cell in strip number /// - static short relToAbsId(char moduleNumber, int iphi, int iz); - static bool absToRelNumbering(short absId, short* relid); - static char absIdToModule(short absId); - static void absIdToRelPosInModule(short absId, float& x, float& z); - static bool relToAbsNumbering(const short* relId, short& absId); + static unsigned short relToAbsId(short moduleNumber, short iphi, short iz); + static bool absToRelNumbering(unsigned short absId, short* relid); + static short absIdToModule(unsigned short absId); + static void absIdToRelPosInModule(unsigned short absId, float& x, float& z); + static bool relToAbsNumbering(const short* relId, unsigned short& absId); + + static void hwaddressToAbsId(short ddl, short row, short dilogic, short hw, unsigned short& absId); + static void absIdToHWaddress(unsigned short absId, short& ddl, short& row, short& dilogic, short& hw); - static int getTotalNPads() { return kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ * 3; } - static bool IsPadExists(short absId) + static unsigned short getTotalNPads() { return kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ * 4; } + static bool IsPadExists(unsigned short absId) { return absId > 0 && absId <= getTotalNPads(); } // TODO: evaluate from real geometry + + ClassDefNV(Geometry, 1); }; } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/base/include/CPVBase/Hit.h b/Detectors/CPV/base/include/CPVBase/Hit.h index 5171a545ea617..4e7e7febba883 100644 --- a/Detectors/CPV/base/include/CPVBase/Hit.h +++ b/Detectors/CPV/base/include/CPVBase/Hit.h @@ -38,7 +38,7 @@ class Hit : public o2::BasicXYZEHit<float> /// \param initialEnergy Energy of the primary particle enering the EMCAL /// \param tof Time of the hit /// \param length Length of the segment - Hit(int trackID, int detID, const Point3D<float>& pos, double tof, double qLoss) + Hit(int trackID, int detID, const math_utils::Point3D<float>& pos, double tof, double qLoss) : o2::BasicXYZEHit<float>(pos.X(), pos.Y(), pos.Z(), tof, qLoss, trackID, detID) { } diff --git a/Detectors/CPV/base/include/CPVBase/RCUTrailer.h b/Detectors/CPV/base/include/CPVBase/RCUTrailer.h new file mode 100644 index 0000000000000..d6f8967c108e9 --- /dev/null +++ b/Detectors/CPV/base/include/CPVBase/RCUTrailer.h @@ -0,0 +1,186 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CPV_RCUTRAILER_H +#define ALICEO2_CPV_RCUTRAILER_H + +#include <exception> +#include <iosfwd> +#include <string> +#include <cstdint> +#include <gsl/span> +#include "Rtypes.h" + +namespace o2 +{ + +namespace cpv +{ + +/// \class RCUTrailer +/// \brief Information stored in the RCU trailer +/// \ingroup CPVbase +/// +/// The RCU trailer can be found at the end of +/// the payload and contains general information +/// sent by the SRU. +class RCUTrailer +{ + public: + /// \class Error + /// \brief Error handling of the + class Error : public std::exception + { + public: + /// \enum ErrorType_t + /// \brief Error codes for different error types + enum class ErrorType_t { + DECODING_INVALID, ///< Invalid words during decoding + SIZE_INVALID, ///< Invalid trailer size + SAMPLINGFREQ_INVALID, ///< Invalid sampling frequency + L1PHASE_INVALID ///< Invalid L1 phase + }; + + /// \brief Constructor + /// \param errtype Code of the error type + /// \param message corresponding error message + /// + /// Initializing the error with error code and message. + /// To be called when the exception is raised. + Error(ErrorType_t errtype, const char* message) : mErrorType(errtype), mErrorMessage(message) {} + + /// \brief Destructor + ~Error() noexcept override = default; + + /// \brief Access to the error message + /// \return Error message related to the exception type + const char* what() const noexcept override { return mErrorMessage.data(); } + + /// \brief Access to error code + /// \return Error code of the exception type + ErrorType_t getErrorType() const noexcept { return mErrorType; } + + private: + ErrorType_t mErrorType; ///< Type of the error + std::string mErrorMessage; ///< Error Message + }; + + /// \brief Constructor + RCUTrailer() = default; + + /// \brief destructor + ~RCUTrailer() = default; + + /// \brief Reset the RCU trailer + /// + /// Setting all values to 0 + void reset(); + + /// \brief Prints the contents of the RCU trailer data + /// \param stream stream the trailer has to be put on + void printStream(std::ostream& stream) const; + + /// \brief Decode RCU trailer from the 32-bit words in the raw buffer + /// \param buffer Raw buffer from which to read the trailer + /// + /// Read the RCU trailer according to the RCU formware version + /// specified in CDH. + void constructFromRawPayload(const gsl::span<const char> payload); + + unsigned int getFECErrorsA() const { return mFECERRA; } + unsigned int getFECErrorsB() const { return mFECERRB; } + unsigned short getErrorsG2() const { return mERRREG2; } + unsigned int getErrorsG3() const { return mERRREG3; } + unsigned short getActiveFECsA() const { return mActiveFECsA; } + unsigned short getActiveFECsB() const { return mActiveFECsB; } + unsigned int getAltroCFGReg1() const { return mAltroCFG1; } + unsigned int getAltroCFGReg2() const { return mAltroCFG2; } + int getRCUID() const { return mRCUId; } + unsigned int getTrailerSize() const { return mTrailerSize; } + unsigned int getPayloadSize() const { return mPayloadSize; } + unsigned char getFirmwareVersion() const { return mFirmwareVersion; } + + unsigned short getNumberOfChannelAddressMismatch() const { return (mERRREG3 & 0xFFF); } + unsigned short getNumberOfChannelLengthMismatch() const { return ((mERRREG3 >> 12) & 0x1FFF); } + unsigned char getBaselineCorrection() const { return mAltroCFG1 & 0xF; } + bool getPolarity() const { return (mAltroCFG1 >> 4) & 0x1; } + unsigned char getNumberOfPresamples() const { return (mAltroCFG1 >> 5) & 0x3; } + unsigned char getNumberOfPostsamples() const { return (mAltroCFG1 >> 7) & 0xF; } + bool hasSecondBaselineCorr() const { return (mAltroCFG1 >> 11) & 0x1; } + unsigned char getGlitchFilter() const { return (mAltroCFG1 >> 12) & 0x3; } + unsigned char getNumberOfNonZeroSuppressedPostsamples() const { return (mAltroCFG1 >> 14) & 0x7; } + unsigned char getNumberOfNonZeroSuppressedPresamples() const { return (mAltroCFG1 >> 17) & 0x3; } + bool hasZeroSuppression() const { return (mAltroCFG1 >> 19) & 0x1; } + bool getNumberOfAltroBuffers() const { return (mAltroCFG2 >> 24) & 0x1; } + unsigned char getNumberOfPretriggerSamples() const { return (mAltroCFG2 >> 20) & 0xF; } + unsigned short getNumberOfSamplesPerChannel() const { return (mAltroCFG2 >> 10) & 0x3FF; } + bool isSparseReadout() const { return (mAltroCFG2 >> 9) & 0x1; } + + /// \brief Access to the sampling time + /// \return Sampling time in seconds. + /// \throw Error if the RCU trailer was not properly initializied + double getTimeSample() const; + + /// \brief set time sample + /// \param timesample Time sample (in ns) + void setTimeSample(double timesample); + + /// \brief Access to the L1 phase + /// \return L1 phase w.r.t to the LHC clock + double getL1Phase() const; + + /// \brief Set the L1 phase + /// \param l1phase L1 phase (in ns) + void setL1Phase(double l1phase); + + void setFECErrorsA(unsigned int value) { mFECERRA = value; } + void setFECErrorsB(unsigned int value) { mFECERRB = value; } + void setErrorsG2(unsigned short value) { mERRREG2 = value; } + void setErrorsG3(unsigned int value) { mERRREG3 = value; } + void setActiveFECsA(unsigned short value) { mActiveFECsA = value; } + void setActiveFECsB(unsigned short value) { mActiveFECsB = value; } + void setAltroCFGReg1(unsigned int value) { mAltroCFG1 = value; } + void setAltroCFGReg2(unsigned int value) { mAltroCFG2 = value; } + void setFirmwareVersion(unsigned char version) { mFirmwareVersion = version; } + void setPayloadSize(unsigned int size) { mPayloadSize = size; } + + /// \brief checlks whether the RCU trailer is initialzied + /// \return True if the trailer is initialized, false otherwise + bool isInitialized() const { return mIsInitialized; } + + std::vector<uint32_t> encode() const; + + static RCUTrailer constructFromPayloadWords(const gsl::span<const uint32_t> payloadwords); + static RCUTrailer constructFromPayload(const gsl::span<const char> payload); + + private: + int mRCUId = -1; ///< current RCU identifier + unsigned char mFirmwareVersion = 0; ///< RCU firmware version + unsigned int mTrailerSize = 0; ///< Size of the trailer (in number of 32 bit words) + unsigned int mPayloadSize = 0; ///< Size of the payload (in nunber of 32 bit words) + unsigned int mFECERRA = 0; ///< contains errors related to ALTROBUS transactions + unsigned int mFECERRB = 0; ///< contains errors related to ALTROBUS transactions + unsigned short mERRREG2 = 0; ///< contains errors related to ALTROBUS transactions or trailer of ALTRO channel block + unsigned int mERRREG3 = 0; ///< contains number of altro channels skipped due to an address mismatch + unsigned short mActiveFECsA = 0; ///< bit pattern of active FECs in branch A + unsigned short mActiveFECsB = 0; ///< bit pattern of active FECs in branch B + unsigned int mAltroCFG1 = 0; ///< ALTROCFG1 register + unsigned int mAltroCFG2 = 0; ///< ALTROCFG2 and ALTROIF register + bool mIsInitialized = false; ///< Flag whether RCU trailer is initialized for the given raw event + + ClassDefNV(RCUTrailer, 1); +}; + +std::ostream& operator<<(std::ostream& stream, const RCUTrailer& trailer); + +} // namespace cpv + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/CPV/base/src/Geometry.cxx b/Detectors/CPV/base/src/Geometry.cxx index f80c0387183a8..f9f27f57ffdb9 100644 --- a/Detectors/CPV/base/src/Geometry.cxx +++ b/Detectors/CPV/base/src/Geometry.cxx @@ -9,39 +9,40 @@ // or submit itself to any jurisdiction. #include "CPVBase/Geometry.h" +#include "FairLogger.h" using namespace o2::cpv; ClassImp(Geometry); -short Geometry::relToAbsId(char moduleNumber, int iphi, int iz) +unsigned short Geometry::relToAbsId(short moduleNumber, short iphi, short iz) { //converts module number, phi and z coordunates to absId - return kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ * (moduleNumber - 1) + kNumberOfCPVPadsZ * (iz - 1) + iphi; + return kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ * (moduleNumber - 1) + kNumberOfCPVPadsZ * iz + iphi; } -bool Geometry::absToRelNumbering(short absId, short* relid) +bool Geometry::absToRelNumbering(unsigned short absId, short* relid) { // Converts the absolute numbering into the following array // relid[0] = CPV Module number 1:fNModules // relid[1] = Column number inside a CPV module (Phi coordinate) // relid[2] = Row number inside a CPV module (Z coordinate) - short nCPV = kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ; - relid[0] = (absId - 1) / nCPV + 1; + const short nCPV = kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ; + relid[0] = absId / nCPV + 1; absId -= (relid[0] - 1) * nCPV; - relid[2] = absId / kNumberOfCPVPadsZ + 1; - relid[1] = absId - (relid[2] - 1) * kNumberOfCPVPadsZ; + relid[1] = absId / kNumberOfCPVPadsZ; + relid[2] = absId % kNumberOfCPVPadsZ; return true; } -char Geometry::absIdToModule(short absId) +short Geometry::absIdToModule(unsigned short absId) { - return 1 + (absId - 1) / (kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ); + return 1 + absId / (kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ); } -int Geometry::areNeighbours(short absId1, short absId2) +short Geometry::areNeighbours(unsigned short absId1, unsigned short absId2) { // Gives the neighbourness of two digits = 0 are not neighbour but continue searching @@ -80,23 +81,69 @@ int Geometry::areNeighbours(short absId1, short absId2) } return 0; } -void Geometry::absIdToRelPosInModule(short absId, float& x, float& z) +void Geometry::absIdToRelPosInModule(unsigned short absId, float& x, float& z) { //Calculate from absId of a cell its position in module short relid[3]; absToRelNumbering(absId, relid); - x = (relid[1] - kNumberOfCPVPadsPhi / 2 - 0.5) * kCPVPadSizePhi; - z = (relid[2] - kNumberOfCPVPadsZ / 2 - 0.5) * kCPVPadSizeZ; + x = (relid[1] - kNumberOfCPVPadsPhi / 2 + 0.5) * kCPVPadSizePhi; + z = (relid[2] - kNumberOfCPVPadsZ / 2 + 0.5) * kCPVPadSizeZ; } -bool Geometry::relToAbsNumbering(const short* relId, short& absId) +bool Geometry::relToAbsNumbering(const short* relId, unsigned short& absId) { absId = (relId[0] - 1) * kNumberOfCPVPadsPhi * kNumberOfCPVPadsZ + // the offset of PHOS modules - (relId[2] - 1) * kNumberOfCPVPadsZ + // the offset along phi - relId[1]; // the offset along z + relId[1] * kNumberOfCPVPadsZ + // the offset along phi + relId[2]; // the offset along z return true; } +void Geometry::hwaddressToAbsId(short ddl, short row, short dilog, short hw, unsigned short& absId) +{ + + short relid[3] = {short(ddl + 1), short(8 * row + hw % 8), short(6 * dilog + hw / 8)}; + + relToAbsNumbering(relid, absId); +} + +void Geometry::absIdToHWaddress(unsigned short absId, short& ddl, short& row, short& dilogic, short& hw) +{ + // Convert absId to hw address + // Arguments: w32,ddl,row,dilogic,address where to write the results + + short relid[3]; + absToRelNumbering(absId, relid); + + ddl = relid[0] - 1; // DDL# 0..2 + row = relid[1] / 8; // row# 0..16 + dilogic = relid[2] / 6; // Dilogic# 0..10 + hw = relid[1] % 8 + 8 * (relid[2] % 6); // Address 0..47 + + if (hw < 0 || hw > kNPAD) { + LOG(ERROR) << "Wrong hw address: hw=" << hw << " > kNPAD=" << kNPAD; + hw = 0; + dilogic = 0; + row = 0; + ddl = 0; + return; + } + if (dilogic < 0 || dilogic > kNDilogic) { + LOG(ERROR) << "Wrong dilogic address: dilogic=" << dilogic << " > kNDilogic=" << kNDilogic; + hw = 0; + dilogic = 0; + row = 0; + ddl = 0; + return; + } + if (row < 0 || row > kNRow) { + LOG(ERROR) << "Wrong row address: row=" << row << " > kNRow=" << kNRow; + hw = 0; + dilogic = 0; + row = 0; + ddl = 0; + return; + } +} diff --git a/Detectors/CPV/base/src/RCUTrailer.cxx b/Detectors/CPV/base/src/RCUTrailer.cxx new file mode 100644 index 0000000000000..a7c8e075a2065 --- /dev/null +++ b/Detectors/CPV/base/src/RCUTrailer.cxx @@ -0,0 +1,249 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <cfloat> +#include <cmath> +#include <iostream> +#include <fmt/format.h> +#include "CommonConstants/LHCConstants.h" +#include "CPVBase/RCUTrailer.h" + +using namespace o2::cpv; + +void RCUTrailer::reset() +{ + mRCUId = -1; + mFirmwareVersion = 0; + mTrailerSize = 0; + mPayloadSize = 0; + mFECERRA = 0; + mFECERRB = 0; + mERRREG2 = 0; + mERRREG3 = 0; + mActiveFECsA = 0; + mActiveFECsB = 0; + mAltroCFG1 = 0; + mAltroCFG2 = 0; + mIsInitialized = false; +} + +void RCUTrailer::constructFromRawPayload(const gsl::span<const char> payloadwords) +{ + + // Code below assumes uint32_t span instead of current char!!!!!! + // reset(); + // int index = payloadwords.size(); + // auto word = payloadwords[--index]; + // if ((word >> 30) != 3) { + // throw Error(Error::ErrorType_t::DECODING_INVALID, "Last RCU trailer word not found!"); + // } + // mFirmwareVersion = (word >> 16) & 0xFF; + + // mRCUId = (int)((word >> 7) & 0x1FF); + // int trailerSize = (word & 0x7F); + + // if (trailerSize < 2) { + // throw Error(Error::ErrorType_t::SIZE_INVALID, fmt::format("Invalid trailer size found (%d bytes) !", trailerSize * 4).data()); + // } + // mTrailerSize = trailerSize; + + // trailerSize -= 2; // Cut first and last trailer words as they are handled separately + // for (; trailerSize > 0; trailerSize--) { + // word = payloadwords[--index]; + // if ((word >> 30) != 2) { + // std::cerr << "Missing RCU trailer identifier pattern!\n"; + // continue; + // } + // int parCode = (word >> 26) & 0xF; + // int parData = word & 0x3FFFFFF; + // switch (parCode) { + // case 1: + // // ERR_REG1 + // mFECERRA = ((parData >> 13) & 0x1FFF) << 7; + // mFECERRB = ((parData & 0x1FFF)) << 7; + // break; + // case 2: + // // ERR_REG2 + // mERRREG2 = parData & 0x1FF; + // break; + // case 3: + // // ERR_REG3 + // mERRREG3 = parData & 0x1FFFFFF; + // break; + // case 4: + // // FEC_RO_A + // mActiveFECsA = parData & 0xFFFF; + // break; + // case 5: + // // FEC_RO_B + // mActiveFECsB = parData & 0xFFFF; + // break; + // case 6: + // // RDO_CFG1 + // mAltroCFG1 = parData & 0xFFFFF; + // break; + // case 7: + // // RDO_CFG2 + // mAltroCFG2 = parData & 0x1FFFFFF; + // break; + // default: + // std::cerr << "Undefined parameter code " << parCode << ", ignore it !\n"; + // break; + // } + // } + // mPayloadSize = payloadwords[--index] & 0x3FFFFFF; + mIsInitialized = true; +} + +double RCUTrailer::getTimeSample() const +{ + unsigned char fq = (mAltroCFG2 >> 5) & 0xF; + double tSample; + switch (fq) { + case 0: + // 20 MHz + tSample = 2.0; + break; + case 1: + // 10 Mhz + tSample = 4.0; + break; + case 2: + // 5 MHz + tSample = 8.; + break; + default: + throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format("Invalid sampling frequency value %d !", int(fq)).data()); + } + + return tSample * o2::constants::lhc::LHCBunchSpacingNS * 1.e-9; +} + +void RCUTrailer::setTimeSample(double timesample) +{ + int fq = 0; + if (std::abs(timesample - 50) < DBL_EPSILON) { + fq = 0; + } else if (std::abs(timesample - 100) < DBL_EPSILON) { + fq = 1; + } else if (std::abs(timesample - 200) < DBL_EPSILON) { + fq = 2; + } else { + throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format("invalid time sample: %f", timesample).data()); + } + mAltroCFG2 = (mAltroCFG2 & 0x1F) | fq << 5; +} + +double RCUTrailer::getL1Phase() const +{ + double tSample = getTimeSample(), + phase = ((double)(mAltroCFG2 & 0x1F)) * o2::constants::lhc::LHCBunchSpacingNS * 1.e-9; + if (phase >= tSample) { + throw Error(Error::ErrorType_t::L1PHASE_INVALID, fmt::format("Invalid L1 trigger phase (%e s (phase) >= %e s (sampling time)) !", phase, tSample).data()); + } + return phase; +} + +void RCUTrailer::setL1Phase(double l1phase) +{ + int phase = l1phase / 25.; + mAltroCFG2 = (mAltroCFG2 & 0x1E0) | phase; +} + +std::vector<uint32_t> RCUTrailer::encode() const +{ + std::vector<uint32_t> encoded; + encoded.emplace_back(mPayloadSize | 2 << 30); + encoded.emplace_back(mAltroCFG2 | 7 << 26 | 2 << 30); + encoded.emplace_back(mAltroCFG1 | 6 << 26 | 2 << 30); + encoded.emplace_back(mActiveFECsB | 5 << 26 | 2 << 30); + encoded.emplace_back(mActiveFECsA | 4 << 26 | 2 << 30); + encoded.emplace_back(mERRREG3 | 3 << 26 | 2 << 30); + encoded.emplace_back(mERRREG2 | 2 << 26 | 2 << 30); + encoded.emplace_back(mFECERRB >> 7 | (mFECERRA >> 7) << 13 | 1 << 26 | 2 << 30); + + uint32_t lasttrailerword = 3 << 30 | mFirmwareVersion << 16 | mRCUId << 7 | (encoded.size() + 1); + encoded.emplace_back(lasttrailerword); + + return encoded; +} + +void RCUTrailer::printStream(std::ostream& stream) const +{ + std::vector<std::string> errors; + double timesample = -1., l1phase = -1.; + try { + timesample = getTimeSample(); + } catch (Error& e) { + errors.push_back(e.what()); + } + try { + l1phase = getL1Phase(); + } catch (Error& e) { + errors.push_back(e.what()); + } + + stream << "RCU trailer (Format version 2):\n" + << "==================================================\n" + << "RCU ID: " << mRCUId << "\n" + << "Firmware version: " << int(mFirmwareVersion) << "\n" + << "Trailer size: " << mTrailerSize << "\n" + << "Payload size: " << mPayloadSize << "\n" + << "FECERRA: 0x" << std::hex << mFECERRA << "\n" + << "FECERRB: 0x" << std::hex << mFECERRB << "\n" + << "ERRREG2: 0x" << std::hex << mERRREG2 << "\n" + << "#channels skipped due to address mismatch: " << std::dec << getNumberOfChannelAddressMismatch() << "\n" + << "#channels skipped due to bad block length: " << std::dec << getNumberOfChannelLengthMismatch() << "\n" + << "Active FECs (branch A): 0x" << std::hex << mActiveFECsA << "\n" + << "Active FECs (branch B): 0x" << std::hex << mActiveFECsB << "\n" + << "Baseline corr: 0x" << std::hex << int(getBaselineCorrection()) << "\n" + << "Number of presamples: " << std::dec << int(getNumberOfPresamples()) << "\n" + << "Number of postsamples: " << std::dec << int(getNumberOfPostsamples()) << "\n" + << "Second baseline corr: " << (hasSecondBaselineCorr() ? "yes" : "no") << "\n" + << "GlitchFilter: " << std::dec << int(getGlitchFilter()) << "\n" + << "Number of non-ZS postsamples: " << std::dec << int(getNumberOfNonZeroSuppressedPostsamples()) << "\n" + << "Number of non-ZS presamples: " << std::dec << int(getNumberOfNonZeroSuppressedPresamples()) << "\n" + << "Number of ALTRO buffers: " << std::dec << getNumberOfAltroBuffers() << "\n" + << "Number of pretrigger samples: " << std::dec << int(getNumberOfPretriggerSamples()) << "\n" + << "Number of samples per channel: " << std::dec << getNumberOfSamplesPerChannel() << "\n" + << "Sparse readout: " << (isSparseReadout() ? "yes" : "no") << "\n" + << "AltroCFG1: 0x" << std::hex << mAltroCFG1 << "\n" + << "AltroCFG2: 0x" << std::hex << mAltroCFG2 << "\n" + << "Sampling time: " << std::scientific << timesample << " s\n" + << "L1 Phase: " << std::scientific << l1phase << " s\n" + << std::dec << std::fixed; + if (errors.size()) { + stream << "Errors: \n" + << "-------------------------------------------------\n"; + for (const auto& e : errors) { + stream << e << "\n"; + } + } + stream << "==================================================\n"; +} + +RCUTrailer RCUTrailer::constructFromPayloadWords(const gsl::span<const uint32_t> payloadwords) +{ + RCUTrailer result; + auto tmp = gsl::span<const char>(reinterpret_cast<const char*>(payloadwords.data()), payloadwords.size() * sizeof(uint32_t)); + result.constructFromRawPayload(tmp); + return result; +} +RCUTrailer RCUTrailer::constructFromPayload(const gsl::span<const char> payloadwords) +{ + RCUTrailer result; + result.constructFromRawPayload(payloadwords); + return result; +} + +std::ostream& o2::cpv::operator<<(std::ostream& stream, const o2::cpv::RCUTrailer& trailer) +{ + trailer.printStream(stream); + return stream; +} diff --git a/Detectors/CPV/calib/include/CPVCalib/BadChannelMap.h b/Detectors/CPV/calib/include/CPVCalib/BadChannelMap.h index 54f69a0d0e596..d2ba63f23346f 100644 --- a/Detectors/CPV/calib/include/CPVCalib/BadChannelMap.h +++ b/Detectors/CPV/calib/include/CPVCalib/BadChannelMap.h @@ -59,7 +59,7 @@ class BadChannelMap BadChannelMap() = default; /// \brief Constructur used to build test bad map - BadChannelMap(int test); + BadChannelMap(short test); /// \brief Destructor ~BadChannelMap() = default; @@ -93,20 +93,20 @@ class BadChannelMap /// Only bad or warm cells are added to the container. In case /// the mask type is GOOD_CELL, the entry is removed from the /// container if present before, otherwise the cell is ignored. - void addBadChannel(short channelID) { mBadCells.set(channelID); } //set bit to true + void addBadChannel(unsigned short channelID) { mBadCells.set(channelID); } //set bit to true /// \brief Mark channel as good /// \param channelID Absolute ID of the channel /// /// Setting channel as good. - void setChannelGood(short channelID) { mBadCells.set(channelID, false); } + void setChannelGood(unsigned short channelID) { mBadCells.set(channelID, false); } /// \brief Get the status of a certain cell /// \param channelID channel for which to obtain the channel status /// \return true if good channel /// /// Provide the mask status of a cell. - bool isChannelGood(short channelID) const { return !mBadCells.test(channelID); } + bool isChannelGood(unsigned short channelID) const { return !mBadCells.test(channelID); } /// \brief Convert map into 2D histogram representation /// \param mod Module number @@ -117,7 +117,7 @@ class BadChannelMap /// - 0: GOOD_CELL /// - 1: BAD_CELL /// Attention: It is responsibility of user to create/delete histogram - void getHistogramRepresentation(char mod, TH2* h) const; + void getHistogramRepresentation(short mod, TH2* h) const; /// \brief Print bad channels on a given stream /// \param stream Stream on which the bad channel map is printed on @@ -131,8 +131,8 @@ class BadChannelMap void PrintStream(std::ostream& stream) const; private: - static constexpr short NCHANNELS = 28673; ///< Number of channels starting from 1 (4*128*56+1 - std::bitset<NCHANNELS> mBadCells; ///< Container for bad cells, 1 means bad sell + static constexpr unsigned short NCHANNELS = 30720; ///< Number of channels in modules 1-4 starting from 0 (4*128*60) + std::bitset<NCHANNELS> mBadCells; ///< Container for bad cells, 1 means bad sell ClassDefNV(BadChannelMap, 1); }; diff --git a/Detectors/CPV/calib/include/CPVCalib/CalibParams.h b/Detectors/CPV/calib/include/CPVCalib/CalibParams.h index aa9f6ff27e898..dc598c18c65ab 100644 --- a/Detectors/CPV/calib/include/CPVCalib/CalibParams.h +++ b/Detectors/CPV/calib/include/CPVCalib/CalibParams.h @@ -36,7 +36,7 @@ class CalibParams CalibParams() = default; /// \brief Constructor for tests - CalibParams(int test); + CalibParams(short test); /// \brief Destructor ~CalibParams() = default; @@ -44,22 +44,22 @@ class CalibParams /// \brief Get High Gain energy calibration coefficients /// \param cellID Absolute ID of cell /// \return high gain energy calibration coefficient of the cell - float getGain(short cellID) const { return mGainCalib[cellID]; } + float getGain(unsigned short cellID) const { return mGainCalib[cellID]; } /// \brief Set High Gain energy calibration coefficient /// \param cellID Absolute ID of cell /// \param c is the calibration coefficient - void setGain(short cellID, float c) { mGainCalib[cellID] = c; } + void setGain(unsigned short cellID, float c) { mGainCalib[cellID] = c; } /// \brief Set High Gain energy calibration coefficients for one module in the form of 2D histogram /// \param 2D(64,56) histogram with calibration coefficients /// \param module number /// \return Is successful - bool setGain(TH2* h, char module); + bool setGain(TH2* h, short module); private: - static constexpr short NCHANNELS = 28673; ///< Number of channels starting from 1 - std::array<float, NCHANNELS> mGainCalib; ///< Container for the gain calibration coefficients + static constexpr unsigned short NCHANNELS = 30720; ///< Number of channels starting from 1 + std::array<float, NCHANNELS> mGainCalib; ///< Container for the gain calibration coefficients ClassDefNV(CalibParams, 1); }; diff --git a/Detectors/CPV/calib/src/BadChannelMap.cxx b/Detectors/CPV/calib/src/BadChannelMap.cxx index b453b8e030903..0e545574b16bc 100644 --- a/Detectors/CPV/calib/src/BadChannelMap.cxx +++ b/Detectors/CPV/calib/src/BadChannelMap.cxx @@ -19,13 +19,13 @@ using namespace o2::cpv; -BadChannelMap::BadChannelMap(int /*dummy*/) +BadChannelMap::BadChannelMap(short /*dummy*/) { //Mark few channels as bad for test peurposes for (short i = 0; i < 60; i++) { //module 2 - short channelID = 3584 + i * 57; + unsigned short channelID = 3584 + i * 57; mBadCells.set(channelID); channelID = 3640 + i * 55; mBadCells.set(channelID); @@ -33,7 +33,7 @@ BadChannelMap::BadChannelMap(int /*dummy*/) for (short i = 0; i < 16; i++) { //module 3 - int channelID = 8972 + i * 57; + unsigned short channelID = 8972 + i * 57; mBadCells.set(channelID); channelID = 8092 + i * 57; mBadCells.set(channelID); @@ -44,7 +44,7 @@ BadChannelMap::BadChannelMap(int /*dummy*/) } } -void BadChannelMap::getHistogramRepresentation(char module, TH2* h) const +void BadChannelMap::getHistogramRepresentation(short module, TH2* h) const { if (!h) { LOG(ERROR) << "provide histogram to be filled"; @@ -60,7 +60,7 @@ void BadChannelMap::getHistogramRepresentation(char module, TH2* h) const h->Reset(); short relid[3] = {module, 1, 1}; - short absId; + unsigned short absId; for (short ix = 1; ix <= MAXX; ix++) { relid[1] = ix; for (short iz = 1; iz <= MAXZ; iz++) { @@ -79,8 +79,9 @@ void BadChannelMap::PrintStream(std::ostream& stream) const // first sort bad channel IDs stream << "Number of bad cells: " << mBadCells.count() << "\n"; for (int cellID = 0; cellID < mBadCells.size(); cellID++) { - if (mBadCells.test(cellID)) + if (mBadCells.test(cellID)) { stream << cellID << "\n"; + } } } diff --git a/Detectors/CPV/calib/src/CalibParams.cxx b/Detectors/CPV/calib/src/CalibParams.cxx index f7de1a259f88a..c2ee2487e5fca 100644 --- a/Detectors/CPV/calib/src/CalibParams.cxx +++ b/Detectors/CPV/calib/src/CalibParams.cxx @@ -19,13 +19,13 @@ using namespace o2::cpv; -CalibParams::CalibParams(int /*dummy*/) +CalibParams::CalibParams(short /*dummy*/) { //produce reasonable objest for test purposes - mGainCalib.fill(0.005); + mGainCalib.fill(0.01); } -bool CalibParams::setGain(TH2* h, char module) +bool CalibParams::setGain(TH2* h, short module) { const short MAXX = 128, MAXZ = 56; @@ -40,7 +40,7 @@ bool CalibParams::setGain(TH2* h, char module) } short relid[3] = {module, 1, 1}; - short absId; + unsigned short absId; for (short ix = 1; ix <= MAXX; ix++) { relid[1] = ix; for (short iz = 1; iz <= MAXZ; iz++) { diff --git a/Detectors/CPV/reconstruction/CMakeLists.txt b/Detectors/CPV/reconstruction/CMakeLists.txt index a7c66754ad5f4..ba63c292f58d8 100644 --- a/Detectors/CPV/reconstruction/CMakeLists.txt +++ b/Detectors/CPV/reconstruction/CMakeLists.txt @@ -11,11 +11,20 @@ o2_add_library(CPVReconstruction SOURCES src/Clusterer.cxx src/FullCluster.cxx + src/RawDecoder.cxx + src/RawReaderMemory.cxx + src/CTFCoder.cxx + src/CTFHelper.cxx PUBLIC_LINK_LIBRARIES O2::CPVBase O2::CPVCalib O2::DataFormatsCPV - AliceO2::InfoLogger) + O2::DetectorsRaw + AliceO2::InfoLogger + O2::rANS + ms_gsl::ms_gsl) o2_target_root_dictionary(CPVReconstruction HEADERS include/CPVReconstruction/Clusterer.h - include/CPVReconstruction/FullCluster.h) + include/CPVReconstruction/FullCluster.h + include/CPVReconstruction/RawReaderMemory.h + include/CPVReconstruction/RawDecoder.h) diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h new file mode 100644 index 0000000000000..79b9a0f228289 --- /dev/null +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -0,0 +1,152 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of CPV data + +#ifndef O2_CPV_CTFCODER_H +#define O2_CPV_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include <array> +#include "DataFormatsCPV/CTF.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" +#include "CPVReconstruction/CTFHelper.h" + +class TTree; + +namespace o2 +{ +namespace cpv +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::CPV) {} + ~CTFCoder() = default; + + /// entropy-encode data to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const TriggerRecord>& trigData, const gsl::span<const Cluster>& cluData); + + /// entropy decode data from buffer with CTF + template <typename VTRG, typename VCLUSTER> + void decode(const CTF::base& ec, VTRG& trigVec, VCLUSTER& cluVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<TriggerRecord>& trigVec, std::vector<Cluster>& cluVec); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const TriggerRecord>& trigData, const gsl::span<const Cluster>& cluData) +{ + 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_bcIncTrig + MD::EENCODE, // BLC_orbitIncTrig + MD::EENCODE, // BLC_entriesTrig + MD::EENCODE, // BLC_posX + MD::EENCODE, // BLC_posZ + MD::EENCODE, // BLC_energy + MD::EENCODE // BLC_status + }; + + CTFHelper helper(trigData, cluData); + + // book output size with some margin + auto szIni = sizeof(CTFHeader) + helper.getSize() / 4; // will be autoexpanded if needed + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(helper.createHeader()); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODECPV(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODECPV(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); + ENCODECPV(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); + ENCODECPV(helper.begin_entriesTrig(), helper.end_entriesTrig(), CTF::BLC_entriesTrig, 0); + + ENCODECPV(helper.begin_posX(), helper.end_posX(), CTF::BLC_posX, 0); + ENCODECPV(helper.begin_posZ(), helper.end_posZ(), CTF::BLC_posZ, 0); + ENCODECPV(helper.begin_energy(), helper.end_energy(), CTF::BLC_energy, 0); + ENCODECPV(helper.begin_status(), helper.end_status(), CTF::BLC_status, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VTRG, typename VCLUSTER> +void CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCLUSTER& cluVec) +{ + auto header = ec.getHeader(); + ec.print(getPrefix()); + std::vector<uint16_t> bcInc, entries, posX, posZ; + std::vector<uint32_t> orbitInc; + std::vector<uint8_t> energy, status; + +#define DECODECPV(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODECPV(bcInc, CTF::BLC_bcIncTrig); + DECODECPV(orbitInc, CTF::BLC_orbitIncTrig); + DECODECPV(entries, CTF::BLC_entriesTrig); + DECODECPV(posX, CTF::BLC_posX); + DECODECPV(posZ, CTF::BLC_posZ); + DECODECPV(energy, CTF::BLC_energy); + DECODECPV(status, CTF::BLC_status); + // clang-format on + // + trigVec.clear(); + cluVec.clear(); + trigVec.reserve(header.nTriggers); + status.reserve(header.nClusters); + + uint32_t firstEntry = 0, cluCount = 0; + o2::InteractionRecord ir(header.firstBC, header.firstOrbit); + + Cluster clu; + for (uint32_t itrig = 0; itrig < header.nTriggers; itrig++) { + // restore TrigRecord + if (orbitInc[itrig]) { // non-0 increment => new orbit + ir.bc = bcInc[itrig]; // bcInc has absolute meaning + ir.orbit += orbitInc[itrig]; + } else { + ir.bc += bcInc[itrig]; + } + + firstEntry = cluVec.size(); + for (uint16_t ic = 0; ic < entries[itrig]; ic++) { + clu.setPacked(posX[cluCount], posZ[cluCount], energy[cluCount], status[cluCount]); + cluVec.emplace_back(clu); + cluCount++; + } + trigVec.emplace_back(ir, firstEntry, entries[itrig]); + } + assert(cluCount == header.nClusters); +} + +} // namespace cpv +} // namespace o2 + +#endif // O2_CPV_CTFCODER_H diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h new file mode 100644 index 0000000000000..32652e5610e31 --- /dev/null +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h @@ -0,0 +1,193 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.h +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for CPV CTF creation + +#ifndef O2_CPV_CTF_HELPER_H +#define O2_CPV_CTF_HELPER_H + +#include "DataFormatsCPV/CTF.h" +#include <gsl/span> + +namespace o2 +{ +namespace cpv +{ + +class CTFHelper +{ + + public: + CTFHelper(const gsl::span<const TriggerRecord>& trgData, const gsl::span<const Cluster>& cluData) + : mTrigData(trgData), mCluData(cluData) {} + + CTFHeader createHeader() + { + CTFHeader h{uint32_t(mTrigData.size()), uint32_t(mCluData.size()), 0, 0}; + if (mTrigData.size()) { + h.firstOrbit = mTrigData[0].getBCData().orbit; + h.firstBC = mTrigData[0].getBCData().bc; + } + return h; + } + + size_t getSize() const { return mTrigData.size() * sizeof(TriggerRecord) + mCluData.size() * sizeof(Cluster); } + + //>>> =========================== ITERATORS ======================================== + + template <typename I, typename D, typename T> + class _Iter + { + public: + using difference_type = int64_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; + using iterator_category = std::random_access_iterator_tag; + + _Iter(const gsl::span<const D>& data, bool end = false) : mData(data), mIndex(end ? data.size() : 0){}; + _Iter() = default; + + const I& operator++() + { + ++mIndex; + return (I&)(*this); + } + + const I& operator--() + { + mIndex--; + return (I&)(*this); + } + + 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); + } + + 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; } + + protected: + gsl::span<const D> mData{}; + size_t 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<Iter_bcIncTrig, TriggerRecord, uint16_t> + { + public: + using _Iter<Iter_bcIncTrig, TriggerRecord, uint16_t>::_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; + } else { + return mData[mIndex].getBCData().bc; + } + } + return 0; + } + }; + + //_______________________________________________ + // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) + class Iter_orbitIncTrig : public _Iter<Iter_orbitIncTrig, TriggerRecord, uint32_t> + { + public: + using _Iter<Iter_orbitIncTrig, TriggerRecord, uint32_t>::_Iter; + value_type operator*() const { return mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0; } + }; + + //_______________________________________________ + // Number of cells for trigger + class Iter_entriesTrig : public _Iter<Iter_entriesTrig, TriggerRecord, uint16_t> + { + public: + using _Iter<Iter_entriesTrig, TriggerRecord, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getNumberOfObjects(); } + }; + + //_______________________________________________ + class Iter_posX : public _Iter<Iter_posX, Cluster, uint16_t> + { + public: + using _Iter<Iter_posX, Cluster, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedPosX(); } + }; + + //_______________________________________________ + class Iter_posZ : public _Iter<Iter_posZ, Cluster, uint16_t> + { + public: + using _Iter<Iter_posZ, Cluster, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedPosZ(); } + }; + + //_______________________________________________ + class Iter_energy : public _Iter<Iter_energy, Cluster, uint16_t> + { + public: + using _Iter<Iter_energy, Cluster, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedEnergy(); } + }; + + //_______________________________________________ + class Iter_status : public _Iter<Iter_status, Cluster, uint8_t> + { + public: + using _Iter<Iter_status, Cluster, uint8_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedClusterStatus(); } + }; + + //<<< =========================== ITERATORS ======================================== + + Iter_bcIncTrig begin_bcIncTrig() const { return Iter_bcIncTrig(mTrigData, false); } + Iter_bcIncTrig end_bcIncTrig() const { return Iter_bcIncTrig(mTrigData, true); } + + Iter_orbitIncTrig begin_orbitIncTrig() const { return Iter_orbitIncTrig(mTrigData, false); } + Iter_orbitIncTrig end_orbitIncTrig() const { return Iter_orbitIncTrig(mTrigData, true); } + + Iter_entriesTrig begin_entriesTrig() const { return Iter_entriesTrig(mTrigData, false); } + Iter_entriesTrig end_entriesTrig() const { return Iter_entriesTrig(mTrigData, true); } + + Iter_posX begin_posX() const { return Iter_posX(mCluData, false); } + Iter_posX end_posX() const { return Iter_posX(mCluData, true); } + + Iter_posZ begin_posZ() const { return Iter_posZ(mCluData, false); } + Iter_posZ end_posZ() const { return Iter_posZ(mCluData, true); } + + Iter_energy begin_energy() const { return Iter_energy(mCluData, false); } + Iter_energy end_energy() const { return Iter_energy(mCluData, true); } + + Iter_status begin_status() const { return Iter_status(mCluData, false); } + Iter_status end_status() const { return Iter_status(mCluData, true); } + + private: + const gsl::span<const o2::cpv::TriggerRecord> mTrigData; + const gsl::span<const o2::cpv::Cluster> mCluData; +}; + +} // namespace cpv +} // namespace o2 + +#endif diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/Clusterer.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/Clusterer.h index cb0269312ef69..591d5f75d13c4 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/Clusterer.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/Clusterer.h @@ -33,36 +33,29 @@ class Clusterer void initialize(); void process(gsl::span<const Digit> digits, gsl::span<const TriggerRecord> dtr, - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* dmc, - std::vector<Cluster>* clusters, std::vector<TriggerRecord>* rigRec, + const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& dmc, + std::vector<Cluster>* clusters, std::vector<TriggerRecord>* trigRec, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* cluMC); void makeClusters(gsl::span<const Digit> digits); void evalCluProperties(gsl::span<const Digit> digits, std::vector<Cluster>* clusters, - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* dmc, + const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& dmc, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* cluMC); float responseShape(float dx, float dz); // Parameterization of EM shower + void propagateMC(bool toRun = true) { mRunMC = toRun; } void makeUnfoldings(gsl::span<const Digit> digits); // Find and unfold clusters with few local maxima void unfoldOneCluster(FullCluster& iniClu, char nMax, gsl::span<int> digitId, gsl::span<const Digit> digits); - protected: - //Calibrate Amplitude - inline float calibrate(float amp, short absId) { return amp * mCalibParams->getGain(absId); } - //Test Bad map - inline bool isBadChannel(short absId) { return (!mBadMap->isChannelGood(absId)); } - protected: static constexpr short NLMMax = 10; ///< maximal number of local maxima in cluster - const CalibParams* mCalibParams = nullptr; //! Calibration coefficients - const BadChannelMap* mBadMap = nullptr; //! Calibration coefficients - - std::vector<FullCluster> mClusters; ///< internal vector of clusters + bool mRunMC = false; ///< Process MC info int mFirstDigitInEvent; ///< Range of digits from one event int mLastDigitInEvent; ///< Range of digits from one event - std::vector<Digit> mDigits; ///< vector of trancient digits for cell processing + std::vector<FullCluster> mClusters; ///< internal vector of clusters + std::vector<Digit> mDigits; ///< vector of transient digits for cell processing std::vector<std::vector<float>> meInClusters = std::vector<std::vector<float>>(10, std::vector<float>(NLMMax)); std::vector<std::vector<float>> mfij = std::vector<std::vector<float>>(10, std::vector<float>(NLMMax)); diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/RawDecoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/RawDecoder.h new file mode 100644 index 0000000000000..9adfd45848a4d --- /dev/null +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/RawDecoder.h @@ -0,0 +1,121 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CPV_RAWDECODER_H +#define ALICEO2_CPV_RAWDECODER_H + +#include <iosfwd> +#include <gsl/span> +#include <string> +#include "CPVBase/RCUTrailer.h" +#include "DataFormatsCPV/Digit.h" +#include "CPVReconstruction/RawReaderMemory.h" +class Digits; + +namespace o2 +{ +namespace cpv +{ + +class RawDecoderError +{ + public: + RawDecoderError() = default; //Constructors for vector::emplace_back methods + RawDecoderError(short l, short r, short d, short p, RawErrorType_t e) : Ddl(l), Row(r), Dilogic(d), Pad(p), errortype(e) {} + RawDecoderError(const RawDecoderError& e) = default; + ~RawDecoderError() = default; + + short Ddl; + short Row; + short Dilogic; + short Pad; + RawErrorType_t errortype; + ClassDefNV(RawDecoderError, 1); +}; + +union AddressCharge { + uint32_t mDataWord; + struct { + uint32_t Address : 18; ///< Bits 0 - 17 : Address + uint32_t Charge : 14; ///< Bits 18 - 32 : charge + }; +}; + +/// \class RawDecoder +/// \brief Decoder of the ALTRO data in the raw page +/// \ingroup CPVreconstruction +/// \author Dmitri Peresunko +/// \since Dec, 2020 +/// +/// This is a base class for reading raw data digits. + +class RawDecoder +{ + public: + /// \brief Constructor + /// \param reader Raw reader instance to be decoded + RawDecoder(RawReaderMemory& reader); + + /// \brief Destructor + ~RawDecoder() = default; + + /// \brief Decode the ALTRO stream + /// \throw RawDecoderError if the RCUTrailer or ALTRO payload cannot be decoded + /// + /// Decoding and checking the RCUTtrailer and + /// all channels and bunches in the ALTRO stream. + /// After successfull decoding the Decoder can provide + /// a reference to the RCU trailer and a vector + /// with the decoded chanenels, each containing + /// its bunches. + RawErrorType_t decode(); + + /// \brief Get reference to the RCU trailer object + /// \return const reference to the RCU trailer + const RCUTrailer& getRCUTrailer() const; + + /// \brief Get the reference to the digits container + /// \return Reference to the digits container + const std::vector<uint32_t>& getDigits() const; + + /// \brief Get the reference to the list of decoding errors + /// \return Reference to the list of decoding errors + const std::vector<o2::cpv::RawDecoderError>& getErrors() const { return mErrors; } + + protected: + /// \brief Read RCU trailer for the current event in the raw buffer + RawErrorType_t readRCUTrailer(); + + /// \brief Read channels for the current event in the raw buffer + RawErrorType_t readChannels(); + + private: + /// \brief run checks on the RCU trailer + /// \throw Error if the RCU trailer has inconsistencies + /// + /// Performing various consistency checks on the RCU trailer + /// In case of failure an exception is thrown. + void checkRCUTrailer(); + + void addDigit(uint32_t padWord, short ddl); + + RawReaderMemory& mRawReader; ///< underlying raw reader + RCUTrailer mRCUTrailer; ///< RCU trailer + std::vector<uint32_t> mDigits; ///< vector of channels in the raw stream + std::vector<RawDecoderError> mErrors; ///< vector of decoding errors + bool mChannelsInitialized = false; ///< check whether the channels are initialized + + ClassDefNV(RawDecoder, 1); +}; + +} // namespace cpv + +} // namespace o2 + +#endif diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/RawReaderMemory.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/RawReaderMemory.h new file mode 100644 index 0000000000000..5492fc12086a3 --- /dev/null +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/RawReaderMemory.h @@ -0,0 +1,123 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CPV_RAWREADERMEMORY_H +#define ALICEO2_CPV_RAWREADERMEMORY_H + +#include <gsl/span> +#include <Rtypes.h> + +#include "CPVBase/RCUTrailer.h" +#include "Headers/RAWDataHeader.h" +#include "Headers/RDHAny.h" + +namespace o2 +{ + +namespace cpv +{ + +enum RawErrorType_t { + kOK, ///< NoError + kNO_PAYLOAD, ///< No payload per ddl + kHEADER_DECODING, + kPAGE_NOTFOUND, + kPAYLOAD_DECODING, + kHEADER_INVALID, + kRCU_TRAILER_ERROR, ///< RCU trailer cannot be decoded or invalid + kRCU_VERSION_ERROR, ///< RCU trailer version not matching with the version in the raw header + kRCU_TRAILER_SIZE_ERROR, ///< RCU trailer size length + kSEGMENT_HEADER_ERROR, + kROW_HEADER_ERROR, + kEOE_HEADER_ERROR, + kPADERROR, + kPadAddress +}; + +/// \class RawReaderMemory +/// \brief Reader for raw data produced by the Readout application in in-memory format +/// \ingroup CPVreconstruction +/// \author Dmitri Peresunko after Markus Fasel +/// \since Sept. 25, 2020 +/// +/// +class RawReaderMemory +{ + public: + /// \brief Constructor + RawReaderMemory(const gsl::span<const char> rawmemory); + + /// \brief Destructor + ~RawReaderMemory() = default; + + /// \brief set new raw memory chunk + /// \param rawmemory New raw memory chunk + void setRawMemory(const gsl::span<const char> rawmemory); + + /// \brief Read next payload from the stream + /// + /// Read the next pages until the stop bit is found. + RawErrorType_t next(); + + /// \brief Read the next page from the stream (single DMA page) + /// \param resetPayload If true the raw payload is reset + /// \throw Error if the page cannot be read or header or payload cannot be deocded + /// + /// Function reading a single DMA page from the stream. It is called + /// inside the next() function for reading payload from multiple DMA + /// pages. As the function cannot handle payload from multiple pages + /// it should not be called directly by the user. + RawErrorType_t nextPage(); + + /// \brief access to the raw header of the current page + /// \return Raw header of the current page + const o2::header::RDHAny& getRawHeader() const { return mRawHeader; } + + /// \brief access to the full raw payload (single or multiple DMA pages) + /// \return Raw Payload of the data until the stop bit is received. + const std::vector<uint32_t>& getPayload() const { return mRawPayload; } + + /// \brief Return size of the payload + /// \return size of the payload + int getPayloadSize() const { return mRawPayload.size(); } + + /// \brief get the size of the file in bytes + /// \return size of the file in byte + int getFileSize() const noexcept { return mRawMemoryBuffer.size(); } + + /// \brief check if more pages are available in the raw file + /// \return true if there is a next page + bool hasNext() const { return mCurrentPosition < mRawMemoryBuffer.size(); } + + protected: + /// \brief Initialize the raw stream + /// + /// Rewind stream to the first entry + void init(); + + o2::header::RDHAny decodeRawHeader(const void* headerwords); + + private: + gsl::span<const char> mRawMemoryBuffer; ///< Memory block with multiple DMA pages + o2::header::RDHAny mRawHeader; ///< Raw header + std::vector<uint32_t> mRawPayload; ///< Raw payload (can consist of multiple pages) + RCUTrailer mCurrentTrailer; ///< RCU trailer + uint64_t mTrailerPayloadWords = 0; ///< Payload words in common trailer + int mCurrentPosition = 0; ///< Current page in file + bool mRawHeaderInitialized = false; ///< RDH for current page initialized + bool mPayloadInitialized = false; ///< Payload for current page initialized + + ClassDefNV(RawReaderMemory, 1); +}; + +} // namespace cpv + +} // namespace o2 + +#endif diff --git a/Detectors/CPV/reconstruction/src/CPVReconstructionLinkDef.h b/Detectors/CPV/reconstruction/src/CPVReconstructionLinkDef.h index 98a55d8446614..76d1bc00c7ae6 100644 --- a/Detectors/CPV/reconstruction/src/CPVReconstructionLinkDef.h +++ b/Detectors/CPV/reconstruction/src/CPVReconstructionLinkDef.h @@ -16,5 +16,7 @@ #pragma link C++ class o2::cpv::Clusterer + ; #pragma link C++ class o2::cpv::FullCluster + ; +#pragma link C++ class o2::cpv::RawReaderMemory + ; +#pragma link C++ class o2::cpv::RawDecoder + ; #endif diff --git a/Detectors/CPV/reconstruction/src/CTFCoder.cxx b/Detectors/CPV/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..df9d4f6292ee3 --- /dev/null +++ b/Detectors/CPV/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of CPV data + +#include "CPVReconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::cpv; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, std::vector<TriggerRecord>& trigVec, std::vector<Cluster>& cluVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, trigVec, cluVec); +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + // just to get types + uint16_t bcInc = 0, entries = 0, cluPosX = 0, cluPosZ = 0; + uint32_t orbitInc = 0; + uint8_t energy = 0, status = 0; +#define MAKECODER(part, slot) createCoder<decltype(part)>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(bcInc, CTF::BLC_bcIncTrig); + MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); + MAKECODER(entries, CTF::BLC_entriesTrig); + MAKECODER(cluPosX, CTF::BLC_posX); + MAKECODER(cluPosZ, CTF::BLC_posZ); + MAKECODER(energy, CTF::BLC_energy); + MAKECODER(status, CTF::BLC_status); + // clang-format on +} diff --git a/Detectors/CPV/reconstruction/src/CTFHelper.cxx b/Detectors/CPV/reconstruction/src/CTFHelper.cxx new file mode 100644 index 0000000000000..9954f5a8d0a1b --- /dev/null +++ b/Detectors/CPV/reconstruction/src/CTFHelper.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for CPV CTF creation + +#include "CPVReconstruction/CTFHelper.h" diff --git a/Detectors/CPV/reconstruction/src/Clusterer.cxx b/Detectors/CPV/reconstruction/src/Clusterer.cxx index 74105acc4f378..edd427f29d4da 100644 --- a/Detectors/CPV/reconstruction/src/Clusterer.cxx +++ b/Detectors/CPV/reconstruction/src/Clusterer.cxx @@ -32,18 +32,21 @@ void Clusterer::initialize() mFirstDigitInEvent = 0; mLastDigitInEvent = -1; } + //____________________________________________________________________________ void Clusterer::process(gsl::span<const Digit> digits, gsl::span<const TriggerRecord> dtr, - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* dmc, + const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& dmc, std::vector<Cluster>* clusters, std::vector<TriggerRecord>* trigRec, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* cluMC) { clusters->clear(); //final out list of clusters trigRec->clear(); - if (cluMC) + if (mRunMC) { cluMC->clear(); + } for (const auto& tr : dtr) { + mFirstDigitInEvent = tr.getFirstEntry(); mLastDigitInEvent = mFirstDigitInEvent + tr.getNumberOfObjects(); int indexStart = clusters->size(); @@ -51,36 +54,14 @@ void Clusterer::process(gsl::span<const Digit> digits, gsl::span<const TriggerRe LOG(DEBUG) << "Starting clusteriztion digits from " << mFirstDigitInEvent << " to " << mLastDigitInEvent; - if (!mBadMap) { - if (o2::cpv::CPVSimParams::Instance().mCCDBPath.compare("localtest") == 0) { - mBadMap = new BadChannelMap(1); // test default map - mCalibParams = new CalibParams(1); //test calibration map - LOG(INFO) << "No reading BadMap/Calibration from ccdb requested, set default"; - } else { - LOG(INFO) << "Getting BadMap object from ccdb"; - o2::ccdb::CcdbApi ccdb; - std::map<std::string, std::string> metadata; // do we want to store any meta data? - ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation - long bcTime = 1; //TODO!!! Convert BC time to time o2::InteractionRecord bcTime = digitsTR.front().getBCData() ; - mBadMap = ccdb.retrieveFromTFileAny<o2::cpv::BadChannelMap>("CPV/BadMap", metadata, bcTime); - mCalibParams = ccdb.retrieveFromTFileAny<o2::cpv::CalibParams>("CPV/Calib", metadata, bcTime); - if (!mBadMap) { - LOG(FATAL) << "[CPVClusterer - run] can not get Bad Map"; - } - if (!mCalibParams) { - LOG(FATAL) << "[CPVClusterer - run] can not get CalibParams"; - } - } - } - // Collect digits to clusters makeClusters(digits); - // Unfold overlapped clusters - // Split clusters with several local maxima if necessary - if (o2::cpv::CPVSimParams::Instance().mUnfoldClusters) { - makeUnfoldings(digits); - } + // // Unfold overlapped clusters + // // Split clusters with several local maxima if necessary + // if (o2::cpv::CPVSimParams::Instance().mUnfoldClusters) { + // makeUnfoldings(digits); + // } // Calculate properties of collected clusters (Local position, energy, disp etc.) evalCluProperties(digits, clusters, dmc, cluMC); @@ -96,57 +77,51 @@ void Clusterer::makeClusters(gsl::span<const Digit> digits) // A cluster is defined as a list of neighbour digits // Mark all digits as unused yet - const int maxNDigits = 12546; // There is no digits more than in CPV modules ;) - bool digitsUsed[maxNDigits]; - memset(digitsUsed, 0, sizeof(bool) * maxNDigits); + const int maxNDigits = 23040; // There is no digits more than in CPV modules ;) + std::bitset<maxNDigits> digitsUsed; ///< Container for bad cells, 1 means bad sell + digitsUsed.reset(); int iFirst = mFirstDigitInEvent; // first index of digit which potentially can be a part of cluster for (int i = iFirst; i < mLastDigitInEvent; i++) { - if (digitsUsed[i - mFirstDigitInEvent]) + if (digitsUsed.test(i - mFirstDigitInEvent)) { continue; + } const Digit& digitSeed = digits[i]; - float digitSeedEnergy = calibrate(digitSeed.getAmplitude(), digitSeed.getAbsId()); - if (isBadChannel(digitSeed.getAbsId())) { - digitSeedEnergy = 0.; - } + float digitSeedEnergy = digitSeed.getAmplitude(); //already calibrated digits if (digitSeedEnergy < o2::cpv::CPVSimParams::Instance().mDigitMinEnergy) { continue; } // is this digit so energetic that start cluster? - FullCluster* clu = nullptr; - int iDigitInCluster = 0; - if (digitSeedEnergy > o2::cpv::CPVSimParams::Instance().mClusteringThreshold) { - // start new cluster - mClusters.emplace_back(digitSeed.getAbsId(), digitSeedEnergy, digitSeed.getLabel()); - clu = &(mClusters.back()); - - digitsUsed[i - mFirstDigitInEvent] = true; - iDigitInCluster = 1; - } else { + if (digitSeedEnergy < o2::cpv::CPVSimParams::Instance().mClusteringThreshold) { continue; } + // start new cluster + mClusters.emplace_back(digitSeed.getAbsId(), digitSeedEnergy, digitSeed.getLabel()); + FullCluster& clu = mClusters.back(); + digitsUsed.set(i - mFirstDigitInEvent, true); + int iDigitInCluster = 1; + // Now scan remaining digits in list to find neigbours of our seed int index = 0; while (index < iDigitInCluster) { // scan over digits already in cluster - short digitSeedAbsId = clu->getDigitAbsId(index); + short digitSeedAbsId = clu.getDigitAbsId(index); index++; - for (Int_t j = iFirst; j < mLastDigitInEvent; j++) { - if (digitsUsed[j - mFirstDigitInEvent]) + bool runLoop = true; + for (Int_t j = iFirst; runLoop && (j < mLastDigitInEvent); j++) { + if (digitsUsed.test(j - mFirstDigitInEvent)) { continue; // look through remaining digits - const Digit* digitN = &(digits[j]); - float digitNEnergy = calibrate(digitN->getAmplitude(), digitN->getAbsId()); - if (isBadChannel(digitN->getAbsId())) { //remove digit - digitNEnergy = 0.; } + const Digit& digitN = digits[j]; + float digitNEnergy = digitN.getAmplitude(); //Already calibrated digits! if (digitNEnergy < o2::cpv::CPVSimParams::Instance().mDigitMinEnergy) { continue; } // call (digit,digitN) in THAT oder !!!!! - Int_t ineb = Geometry::areNeighbours(digitSeedAbsId, digitN->getAbsId()); + Int_t ineb = Geometry::areNeighbours(digitSeedAbsId, digitN.getAbsId()); switch (ineb) { case -1: // too early (e.g. previous module), do not look before j at subsequent passes iFirst = j; @@ -154,12 +129,13 @@ void Clusterer::makeClusters(gsl::span<const Digit> digits) case 0: // not a neighbour break; case 1: // are neighbours - clu->addDigit(digitN->getAbsId(), digitNEnergy, digitN->getLabel()); + clu.addDigit(digitN.getAbsId(), digitNEnergy, digitN.getLabel()); iDigitInCluster++; - digitsUsed[j - mFirstDigitInEvent] = true; + digitsUsed.set(j - mFirstDigitInEvent, true); break; case 2: // too far from each other default: + runLoop = false; break; } // switch } @@ -251,8 +227,9 @@ void Clusterer::unfoldOneCluster(FullCluster& iniClu, char nMax, gsl::span<int> a[iclu] += mfij[idig][iclu] * mfij[idig][iclu]; b[iclu] += it.energy * mfij[idig][iclu]; for (int kclu = 0; kclu < nMax; kclu++) { - if (iclu == kclu) + if (iclu == kclu) { continue; + } c[iclu] += eMax[kclu] * mfij[idig][iclu] * mfij[idig][kclu]; } } @@ -336,7 +313,7 @@ void Clusterer::unfoldOneCluster(FullCluster& iniClu, char nMax, gsl::span<int> //____________________________________________________________________________ void Clusterer::evalCluProperties(gsl::span<const Digit> digits, std::vector<Cluster>* clusters, - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* dmc, + const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& dmc, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* cluMC) { @@ -345,9 +322,10 @@ void Clusterer::evalCluProperties(gsl::span<const Digit> digits, std::vector<Clu } int labelIndex = 0; - if (cluMC) { + if (mRunMC) { labelIndex = cluMC->getIndexedSize(); } + auto clu = mClusters.begin(); while (clu != mClusters.end()) { @@ -366,7 +344,7 @@ void Clusterer::evalCluProperties(gsl::span<const Digit> digits, std::vector<Clu if (clu->getEnergy() > 1.e-4) { //Non-empty cluster clusters->emplace_back(*clu); - if (cluMC) { //Handle labels + if (mRunMC) { //Handle labels //Calculate list of primaries //loop over entries in digit MCTruthContainer const std::vector<FullCluster::CluElement>* vl = clu->getElementList(); @@ -377,7 +355,7 @@ void Clusterer::evalCluProperties(gsl::span<const Digit> digits, std::vector<Clu ++ll; continue; } - gsl::span<const o2::MCCompLabel> spDigList = dmc->getLabels(i); + gsl::span<const o2::MCCompLabel> spDigList = dmc.getLabels(i); gsl::span<o2::MCCompLabel> spCluList = cluMC->getLabels(labelIndex); //get updated list auto digL = spDigList.begin(); while (digL != spDigList.end()) { @@ -397,7 +375,6 @@ void Clusterer::evalCluProperties(gsl::span<const Digit> digits, std::vector<Clu } ++ll; } - clusters->back().setLabel(labelIndex); labelIndex++; } // Work with MC } diff --git a/Detectors/CPV/reconstruction/src/RawDecoder.cxx b/Detectors/CPV/reconstruction/src/RawDecoder.cxx new file mode 100644 index 0000000000000..18937c0e8ea31 --- /dev/null +++ b/Detectors/CPV/reconstruction/src/RawDecoder.cxx @@ -0,0 +1,171 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <FairLogger.h> +#include "CPVReconstruction/RawReaderMemory.h" +#include "CPVReconstruction/RawDecoder.h" +#include "DataFormatsCPV/RawFormats.h" +#include "InfoLogger/InfoLogger.hxx" +#include "DetectorsRaw/RDHUtils.h" +#include "CPVBase/Geometry.h" + +using namespace o2::cpv; + +RawDecoder::RawDecoder(RawReaderMemory& reader) : mRawReader(reader), + mRCUTrailer(), + mChannelsInitialized(false) +{ +} + +RawErrorType_t RawDecoder::decode() +{ + + auto& header = mRawReader.getRawHeader(); + short ddl = o2::raw::RDHUtils::getFEEID(header); + mDigits.clear(); + + auto payloadwords = mRawReader.getPayload(); + if (payloadwords.size() == 0) { + mErrors.emplace_back(ddl, 0, 0, 0, kNO_PAYLOAD); //add error + LOG(ERROR) << "Empty payload for DDL=" << ddl; + return kNO_PAYLOAD; + } + + // if(readRCUTrailer()!=kOK){ + // LOG(ERROR) << "can not read RCU trailer for DDL " << ddl ; + // return kRCU_TRAILER_ERROR; + // } + + return readChannels(); +} + +RawErrorType_t RawDecoder::readRCUTrailer() +{ + gsl::span<const char> payload(reinterpret_cast<const char*>(mRawReader.getPayload().data()), mRawReader.getPayload().size() * sizeof(uint32_t)); + mRCUTrailer.constructFromRawPayload(payload); + return kOK; +} + +RawErrorType_t RawDecoder::readChannels() +{ + mChannelsInitialized = false; + auto& header = mRawReader.getRawHeader(); + short ddl = o2::raw::RDHUtils::getFEEID(header); //Current fee/ddl + + auto& payloadwords = mRawReader.getPayload(); + //start reading from the end + auto currentWord = payloadwords.rbegin(); + while (currentWord != payloadwords.rend()) { + SegMarkerWord sw = {*currentWord++}; //first get value, then increment + if (sw.marker != 2736) { //error + mErrors.emplace_back(ddl, 17, 2, 0, kSEGMENT_HEADER_ERROR); //add error for non-existing row + //try adding this as padWord + addDigit(sw.mDataWord, ddl); + continue; + } + short nSegWords = sw.nwords; + short currentRow = sw.row; + short nEoE = 0; + while (nSegWords > 0 && currentWord != payloadwords.rend()) { + EoEWord ew = {*currentWord++}; + nSegWords--; + if (ew.checkbit != 1) { //error + LOG(ERROR) << " error EoE, ddl" << ddl << " row " << currentRow; + mErrors.emplace_back(ddl, currentRow, 11, 0, kEOE_HEADER_ERROR); //add error + //try adding this as padWord + addDigit(ew.mDataWord, ddl); + continue; + } + nEoE++; + short nEoEwords = ew.nword; + short currentDilogic = ew.dilogic; + if (ew.row != currentRow) { + LOG(ERROR) << "Row in EoE=" << ew.row << " != expected row " << currentRow; + mErrors.emplace_back(ddl, currentRow, currentDilogic, 0, kEOE_HEADER_ERROR); //add error + //try adding this as padWord + addDigit(ew.mDataWord, ddl); + continue; + } + if (currentDilogic < 0 || currentDilogic > 10) { + LOG(ERROR) << "wrong Dilogic in EoE=" << currentDilogic; + mErrors.emplace_back(ddl, currentRow, currentDilogic, 0, kEOE_HEADER_ERROR); //add error + //try adding this as padWord + addDigit(ew.mDataWord, ddl); + continue; + } + while (nEoEwords > 0 && currentWord != payloadwords.rend()) { + PadWord pad = {*currentWord++}; + nEoEwords--; + nSegWords--; + if (pad.zero != 0) { + LOG(ERROR) << "bad pad, word=" << pad.mDataWord; + mErrors.emplace_back(ddl, currentRow, currentDilogic, 49, kPADERROR); //add error and skip word + continue; + } + //check paw/pad indexes + if (pad.row != currentRow || pad.dilogic != currentDilogic) { + LOG(ERROR) << "RawPad " << pad.row << " != currentRow=" << currentRow << "dilogicPad=" << pad.dilogic << "!= currentDilogic=" << currentDilogic; + mErrors.emplace_back(ddl, short(pad.row), short(pad.dilogic), short(pad.address), kPadAddress); //add error and skip word + //do not skip, try adding using info from pad + } + addDigit(pad.mDataWord, ddl); + } //pads in EoE + if (nEoE % 10 == 0) { // kNDilogic = 10; ///< Number of dilogic per row + if (currentWord != payloadwords.rend()) { //Read row HEader + RowMarkerWord rw = {*currentWord++}; + nSegWords--; + currentRow--; + if (rw.marker != 13992) { + LOG(ERROR) << "Error in row marker:" << rw.marker << "row header word=" << rw.mDataWord; + mErrors.emplace_back(ddl, currentRow, 11, 0, kPadAddress); //add error and skip word + //try adding digit assuming this is pad word + addDigit(rw.mDataWord, ddl); + } + } + } + } // in Segment + } + mChannelsInitialized = true; + return kOK; +} + +const RCUTrailer& RawDecoder::getRCUTrailer() const +{ + if (!mRCUTrailer.isInitialized()) { + LOG(ERROR) << "RCU trailer not initialized"; + } + return mRCUTrailer; +} + +const std::vector<uint32_t>& RawDecoder::getDigits() const +{ + if (!mChannelsInitialized) { + LOG(ERROR) << "Channels not initialized"; + } + return mDigits; +} + +void RawDecoder::addDigit(uint32_t w, short ddl) +{ + + PadWord pad = {w}; + if (pad.zero != 0) { + return; + } + short rowPad = pad.row; + short dilogicPad = pad.dilogic; + short hw = pad.address; + unsigned short absId; + o2::cpv::Geometry::hwaddressToAbsId(ddl, rowPad, dilogicPad, hw, absId); + + AddressCharge ac = {0}; + ac.Address = absId; + ac.Charge = pad.charge; + mDigits.push_back(ac.mDataWord); +} \ No newline at end of file diff --git a/Detectors/CPV/reconstruction/src/RawReaderMemory.cxx b/Detectors/CPV/reconstruction/src/RawReaderMemory.cxx new file mode 100644 index 0000000000000..701130be7ff27 --- /dev/null +++ b/Detectors/CPV/reconstruction/src/RawReaderMemory.cxx @@ -0,0 +1,142 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <sstream> +#include <string> +#include "FairLogger.h" +#include "CPVReconstruction/RawReaderMemory.h" +#include "DetectorsRaw/RDHUtils.h" + +using namespace o2::cpv; + +using RDHDecoder = o2::raw::RDHUtils; + +RawReaderMemory::RawReaderMemory(gsl::span<const char> rawmemory) : mRawMemoryBuffer(rawmemory) +{ + init(); +} + +void RawReaderMemory::setRawMemory(const gsl::span<const char> rawmemory) +{ + mRawMemoryBuffer = rawmemory; + init(); +} + +o2::header::RDHAny RawReaderMemory::decodeRawHeader(const void* payloadwords) +{ + auto headerversion = RDHDecoder::getVersion(payloadwords); + if (headerversion == 4) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV4*>(payloadwords)); + } else if (headerversion == 5) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV5*>(payloadwords)); + } else if (headerversion == 6) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV6*>(payloadwords)); + } + LOG(ERROR) << "Unknown RDH version"; + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV6*>(payloadwords)); + ; +} + +void RawReaderMemory::init() +{ + mCurrentPosition = 0; + mRawHeaderInitialized = false; + mPayloadInitialized = false; +} + +RawErrorType_t RawReaderMemory::next() +{ + mRawPayload.clear(); + mCurrentTrailer.reset(); + bool isDataTerminated = false; + do { + RawErrorType_t e = nextPage(); + if (e != RawErrorType_t::kOK) { + return e; + } + if (hasNext()) { + auto nextheader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + // check continuing payload based on the bc/orbit ID + auto currentbc = RDHDecoder::getTriggerBC(mRawHeader), + nextbc = RDHDecoder::getTriggerBC(nextheader); + auto currentorbit = RDHDecoder::getTriggerOrbit(mRawHeader), + nextorbit = RDHDecoder::getTriggerOrbit(nextheader); + if (currentbc != nextbc || currentorbit != nextorbit) { + isDataTerminated = true; + } else { + auto nextpagecounter = RDHDecoder::getPageCounter(nextheader); + if (nextpagecounter == 0) { + isDataTerminated = true; + } else { + isDataTerminated = false; + } + } + } else { + isDataTerminated = true; + } + // Check if the data continues + } while (!isDataTerminated); + try { + mCurrentTrailer.constructFromPayloadWords(mRawPayload); + } catch (...) { + return RawErrorType_t::kHEADER_DECODING; + } + return RawErrorType_t::kOK; +} + +RawErrorType_t RawReaderMemory::nextPage() +{ + if (!hasNext()) { + return RawErrorType_t::kPAGE_NOTFOUND; + } + mRawHeaderInitialized = false; + mPayloadInitialized = false; + + // Read RDH header + try { + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + while (RDHDecoder::getOffsetToNext(mRawHeader) == RDHDecoder::getHeaderSize(mRawHeader) && + mCurrentPosition < mRawMemoryBuffer.size()) { + // No Payload - jump to next rawheader + // This will eventually move, depending on whether for events without payload in the SRU we send the RCU trailer + mCurrentPosition += RDHDecoder::getHeaderSize(mRawHeader); + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + } + mRawHeaderInitialized = true; + } catch (...) { + return RawErrorType_t::kHEADER_DECODING; + } + if (mCurrentPosition + RDHDecoder::getMemorySize(mRawHeader) > mRawMemoryBuffer.size()) { + // Payload incomplete + return RawErrorType_t::kPAYLOAD_DECODING; + } + + auto tmp = reinterpret_cast<const uint32_t*>(mRawMemoryBuffer.data()); + int start = (mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader)) / sizeof(uint32_t); + int end = start + (RDHDecoder::getMemorySize(mRawHeader) - RDHDecoder::getHeaderSize(mRawHeader)) / sizeof(uint32_t); + for (auto iword = start; iword < end; iword++) { + mRawPayload.push_back(tmp[iword]); + } + + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); /// Assume fixed 8 kB page size + /* + mCurrentTrailer.setPayloadSize(mCurrentTrailer.getPayloadSize() + trailer.getPayloadSize()); + tralersize = trailer.getTrailerSize(); + } + + gsl::span<const uint32_t> payloadWithoutTrailer(mRawBuffer.getDataWords().data(), mRawBuffer.getDataWords().size() - tralersize); + + mRawPayload.appendPayloadWords(payloadWithoutTrailer); + mRawPayload.increasePageCount(); + } + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); /// Assume fixed 8 kB page size +*/ + return RawErrorType_t::kOK; +} diff --git a/Detectors/CPV/simulation/CMakeLists.txt b/Detectors/CPV/simulation/CMakeLists.txt index 5d96c06ae79e0..dd2b803aa3f4d 100644 --- a/Detectors/CPV/simulation/CMakeLists.txt +++ b/Detectors/CPV/simulation/CMakeLists.txt @@ -9,11 +9,27 @@ # submit itself to any jurisdiction. o2_add_library(CPVSimulation - SOURCES src/Detector.cxx src/GeometryParams.cxx - src/Digitizer.cxx - PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::CPVBase O2::DataFormatsCPV O2::CPVCalib O2::CCDB) + SOURCES src/Detector.cxx + src/GeometryParams.cxx + src/Digitizer.cxx + src/RawWriter.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + O2::DataFormatsCPV + O2::CPVBase + O2::CPVCalib + O2::CCDB + O2::SimConfig + O2::SimulationDataFormat + O2::Headers + O2::DetectorsRaw) o2_target_root_dictionary(CPVSimulation HEADERS include/CPVSimulation/Detector.h include/CPVSimulation/GeometryParams.h - include/CPVSimulation/Digitizer.h) + include/CPVSimulation/Digitizer.h + include/CPVSimulation/RawWriter.h) +o2_add_executable(digi2raw + COMPONENT_NAME cpv + PUBLIC_LINK_LIBRARIES O2::CPVSimulation + SOURCES src/RawCreator.cxx) + diff --git a/Detectors/CPV/simulation/include/CPVSimulation/Detector.h b/Detectors/CPV/simulation/include/CPVSimulation/Detector.h index 6f174e0d729bb..9c95ec79c0017 100644 --- a/Detectors/CPV/simulation/include/CPVSimulation/Detector.h +++ b/Detectors/CPV/simulation/include/CPVSimulation/Detector.h @@ -12,7 +12,7 @@ #define ALICEO2_CPV_DETECTOR_H_ #include "DetectorsBase/Detector.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "CPVBase/Hit.h" #include "RStringView.h" #include "Rtypes.h" @@ -86,7 +86,7 @@ class Detector : public o2::base::DetImpl<Detector> /// \param[in] time Time of the hit /// \param[in] qdep charge deposited in this pad /// - void addHit(int trackID, short detID, const Point3D<float>& pos, double time, float qdep); + void addHit(int trackID, short detID, const math_utils::Point3D<float>& pos, double time, float qdep); /// /// Register vector with hits diff --git a/Detectors/CPV/simulation/include/CPVSimulation/GeometryParams.h b/Detectors/CPV/simulation/include/CPVSimulation/GeometryParams.h index 4fef031b0bf8f..7c40586b518f7 100644 --- a/Detectors/CPV/simulation/include/CPVSimulation/GeometryParams.h +++ b/Detectors/CPV/simulation/include/CPVSimulation/GeometryParams.h @@ -21,7 +21,7 @@ namespace o2 { namespace cpv { -class GeometryParams : public TNamed +class GeometryParams final : public TNamed { public: /// Default constructor @@ -41,17 +41,20 @@ class GeometryParams : public TNamed void GetModuleAngle(int module, float angle[3][2]) const { - for (int i = 0; i < 3; i++) - for (int ian = 0; ian < 2; ian++) + for (int i = 0; i < 3; i++) { + for (int ian = 0; ian < 2; ian++) { angle[i][ian] = mModuleAngle[module][i][ian]; + } + } } float GetCPVAngle(Int_t index) const { return mCPVAngle[index - 1]; } void GetModuleCenter(int module, float* pos) const { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { pos[i] = mModuleCenter[module][i]; + } } int GetNModules() const { return mNModules; } diff --git a/Detectors/CPV/simulation/include/CPVSimulation/RawWriter.h b/Detectors/CPV/simulation/include/CPVSimulation/RawWriter.h new file mode 100644 index 0000000000000..6d990fb4c375c --- /dev/null +++ b/Detectors/CPV/simulation/include/CPVSimulation/RawWriter.h @@ -0,0 +1,91 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CPV_RAWWRITER_H +#define ALICEO2_CPV_RAWWRITER_H + +#include <gsl/span> + +#include <array> +#include <fstream> +#include <memory> +#include <string> +#include <map> +#include <vector> + +#include "Rtypes.h" + +#include "DetectorsRaw/RawFileWriter.h" +#include "DataFormatsCPV/Digit.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "CPVCalib/CalibParams.h" + +namespace o2 +{ + +namespace cpv +{ + +static constexpr short kNDDL = 4; ///< Total number of DDLs +static constexpr short kNPAD = 48; ///< Nuber of pads per dilogic +static constexpr short kNDilogic = 10; ///< Number of dilogic per row +static constexpr short kNRow = 16; ///< number of rows per dddl + +struct padCharge { + short charge; + short pad; + padCharge() : charge(0), pad(0) {} + padCharge(short a, short b) : charge(a), + pad(b) + { + } //for std::vector::emplace_back functionality +}; + +class RawWriter +{ + public: + enum class FileFor_t { + kFullDet, + kLink + }; + RawWriter() = default; + RawWriter(const char* outputdir) { setOutputLocation(outputdir); } + ~RawWriter() = default; + + o2::raw::RawFileWriter& getWriter() const { return *mRawWriter; } + + void setOutputLocation(const char* outputdir) { mOutputLocation = outputdir; } + void setFileFor(FileFor_t filefor) { mFileFor = filefor; } + + void init(); + void digitsToRaw(gsl::span<o2::cpv::Digit> digits, gsl::span<o2::cpv::TriggerRecord> triggers); + bool processTrigger(const gsl::span<o2::cpv::Digit> digitsbranch, const o2::cpv::TriggerRecord& trg); + + int carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data, + const char* ptr, int maxSize, int splitID, + std::vector<char>& trailer, std::vector<char>& header) const; + + private: + std::vector<padCharge> mPadCharge[kNDDL][kNRow][kNDilogic]; ///< list of signals per event + FileFor_t mFileFor = FileFor_t::kFullDet; ///< Granularity of the output files + std::string mOutputLocation = "./"; ///< Rawfile name + std::unique_ptr<CalibParams> mCalibParams; ///< CPV calibration + std::vector<uint32_t> mPayload; ///< Payload to be written + gsl::span<o2::cpv::Digit> mDigits; ///< Digits input vector - must be in digitized format including the time response + std::unique_ptr<o2::raw::RawFileWriter> mRawWriter; ///< Raw writer + + ClassDefNV(RawWriter, 1); +}; + +} // namespace cpv + +} // namespace o2 + +#endif diff --git a/Detectors/CPV/simulation/src/CPVSimulationLinkDef.h b/Detectors/CPV/simulation/src/CPVSimulationLinkDef.h index 1c9ff59953133..a2e865e038991 100644 --- a/Detectors/CPV/simulation/src/CPVSimulationLinkDef.h +++ b/Detectors/CPV/simulation/src/CPVSimulationLinkDef.h @@ -18,5 +18,6 @@ #pragma link C++ class o2::cpv::GeometryParams + ; #pragma link C++ class o2::base::DetImpl < o2::cpv::Detector> + ; #pragma link C++ class o2::cpv::Digitizer + ; +#pragma link C++ class o2::cpv::RawWriter + ; #endif diff --git a/Detectors/CPV/simulation/src/Detector.cxx b/Detectors/CPV/simulation/src/Detector.cxx index b68d112205a52..3a857759abd98 100644 --- a/Detectors/CPV/simulation/src/Detector.cxx +++ b/Detectors/CPV/simulation/src/Detector.cxx @@ -1,4 +1,3 @@ - // 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". @@ -250,15 +249,15 @@ Bool_t Detector::ProcessHits(FairVolume* v) int ixcell = (int)xcell; float zc = zcell - izcell - 0.5; float xc = xcell - ixcell - 0.5; - for (int iz = 1; iz <= cpvparam.mNgamz; iz++) { + for (int iz = 0; iz < cpvparam.mNgamz; iz++) { int kzg = izcell + iz - nz3; - if (kzg <= 0 || kzg > cpvparam.mnCellZ) { + if (kzg < 0 || kzg >= cpvparam.mnCellZ) { continue; } float zg = (float)(iz - nz3) - zc; - for (int ix = 1; ix <= cpvparam.mNgamx; ix++) { + for (int ix = 0; ix < cpvparam.mNgamx; ix++) { int kxg = ixcell + ix - nx3; - if (kxg <= 0 || kxg > cpvparam.mnCellX) { + if (kxg < 0 || kxg >= cpvparam.mnCellX) { continue; } float xg = (float)(ix - nx3) - xc; @@ -272,7 +271,7 @@ Bool_t Detector::ProcessHits(FairVolume* v) // Fill hit with pad response ID and amplitude // hist will be sorted and merged later if necessary short detID = Geometry::relToAbsId(moduleNumber, kxg, kzg); - addHit(partID, detID, Point3D<float>(xyzm[0], xyzm[1], xyzm[2]), time, qpad); + addHit(partID, detID, math_utils::Point3D<float>(xyzm[0], xyzm[1], xyzm[2]), time, qpad); } } } @@ -324,7 +323,7 @@ float Detector::CPVCumulPadResponse(float x, float y) return cumulPRF; } -void Detector::addHit(int trackID, short detID, const Point3D<float>& pos, double time, float qdep) +void Detector::addHit(int trackID, short detID, const math_utils::Point3D<float>& pos, double time, float qdep) { LOG(DEBUG) << "Adding hit for track " << trackID << " in a pad " << detID << " with position (" << pos.X() << ", " << pos.Y() << ", " << pos.Z() << "), time" << time << ", qdep =" << qdep << std::endl; diff --git a/Detectors/CPV/simulation/src/RawCreator.cxx b/Detectors/CPV/simulation/src/RawCreator.cxx new file mode 100644 index 0000000000000..89a8352f3193b --- /dev/null +++ b/Detectors/CPV/simulation/src/RawCreator.cxx @@ -0,0 +1,108 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <memory> +#include <string> +#include <vector> +#include "Framework/Logger.h" + +#include <boost/program_options.hpp> + +#include <TFile.h> +#include <TTree.h> +#include <TTreeReader.h> +#include <TSystem.h> + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/StringUtils.h" +#include "DataFormatsCPV/Digit.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "CPVBase/Geometry.h" +#include "CPVSimulation/RawWriter.h" + +namespace bpo = boost::program_options; + +int main(int argc, const char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " <cmds/options>\n" + " Tool will encode cpv raw data from input file\n" + "Commands / Options"); + 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("verbose,v", bpo::value<uint32_t>()->default_value(0), "Select verbosity level [0 = no output]"); + add_option("input-file,i", bpo::value<std::string>()->default_value("cpvdigits.root"), "Specifies digit input file."); + add_option("file-for,f", bpo::value<std::string>()->default_value("all"), "single file per: all,link"); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + add_option("debug,d", bpo::value<uint32_t>()->default_value(0), "Select debug output level [0 = no debug output]"); + add_option("configKeyValues", bpo::value<std::string>()->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") || argc == 1) { + std::cout << opt_general << std::endl; + exit(0); + } + + } 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<std::string>()); + + auto digitfilename = vm["input-file"].as<std::string>(), + outputdir = vm["output-dir"].as<std::string>(), + filefor = vm["file-for"].as<std::string>(); + + // if needed, create output directory + if (gSystem->AccessPathName(outputdir.c_str())) { + if (gSystem->mkdir(outputdir.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outputdir; + } else { + LOG(INFO) << "created output directory " << outputdir; + } + } + + std::unique_ptr<TFile> digitfile(TFile::Open(digitfilename.data(), "READ")); + auto treereader = std::make_unique<TTreeReader>(static_cast<TTree*>(digitfile->Get("o2sim"))); + TTreeReaderValue<std::vector<o2::cpv::Digit>> digitbranch(*treereader, "CPVDigit"); + TTreeReaderValue<std::vector<o2::cpv::TriggerRecord>> triggerbranch(*treereader, "CPVDigitTrigRecords"); + + o2::cpv::RawWriter::FileFor_t granularity = o2::cpv::RawWriter::FileFor_t::kFullDet; + if (filefor == "all") { + granularity = o2::cpv::RawWriter::FileFor_t::kFullDet; + } else if (filefor == "link") { + granularity = o2::cpv::RawWriter::FileFor_t::kLink; + } + + o2::cpv::RawWriter rawwriter; + rawwriter.setOutputLocation(outputdir.data()); + rawwriter.setFileFor(granularity); + rawwriter.init(); + + // Loop over all entries in the tree, where each tree entry corresponds to a time frame + for (auto en : *treereader) { + rawwriter.digitsToRaw(*digitbranch, *triggerbranch); + } + rawwriter.getWriter().writeConfFile("CPV", "RAWDATA", o2::utils::concat_string(outputdir, "/CPVraw.cfg")); +} diff --git a/Detectors/CPV/simulation/src/RawWriter.cxx b/Detectors/CPV/simulation/src/RawWriter.cxx new file mode 100644 index 0000000000000..1bcc660004ff3 --- /dev/null +++ b/Detectors/CPV/simulation/src/RawWriter.cxx @@ -0,0 +1,169 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FairLogger.h" + +#include <fmt/core.h> +#include <gsl/span> +#include <TSystem.h> +#include "DataFormatsCPV/RawFormats.h" +#include "CPVSimulation/RawWriter.h" +#include "CPVBase/CPVSimParams.h" +#include "CPVBase/RCUTrailer.h" +#include "CPVBase/Geometry.h" +#include "CCDB/CcdbApi.h" + +using namespace o2::cpv; + +void RawWriter::init() +{ + mRawWriter = std::make_unique<o2::raw::RawFileWriter>(o2::header::gDataOriginCPV, false); + mRawWriter->setCarryOverCallBack(this); + mRawWriter->setApplyCarryOverToLastPage(true); + + // Set output file and register link + std::string rawfilename = mOutputLocation; + rawfilename += "/cpv.raw"; + + for (int ddl = 0; ddl < kNDDL; ddl++) { + short crorc = 0, link = ddl; + mRawWriter->registerLink(ddl, crorc, link, 0, rawfilename.data()); + } +} + +void RawWriter::digitsToRaw(gsl::span<o2::cpv::Digit> digitsbranch, gsl::span<o2::cpv::TriggerRecord> triggerbranch) +{ + if (!mCalibParams) { + if (o2::cpv::CPVSimParams::Instance().mCCDBPath.compare("localtest") == 0) { + mCalibParams = std::make_unique<o2::cpv::CalibParams>(1); // test default calibration + LOG(INFO) << "[RawWriter] No reading calibration from ccdb requested, set default"; + } else { + LOG(INFO) << "[RawWriter] getting calibration object from ccdb"; + o2::ccdb::CcdbApi ccdb; + std::map<std::string, std::string> metadata; + ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation + auto tr = triggerbranch.begin(); + double eventTime = -1; + // if(tr!=triggerbranch.end()){ + // eventTime = (*tr).getBCData().getTimeNS() ; + // } + //add copy constructor if necessary + // mCalibParams = std::make_unique<o2::cpv::CalibParams>(ccdb.retrieveFromTFileAny<o2::cpv::CalibParams>("CPV/Calib", metadata, eventTime)); + if (!mCalibParams) { + LOG(FATAL) << "[RawWriter] can not get calibration object from ccdb"; + } + } + } + + for (auto trg : triggerbranch) { + processTrigger(digitsbranch, trg); + } +} + +bool RawWriter::processTrigger(const gsl::span<o2::cpv::Digit> digitsbranch, const o2::cpv::TriggerRecord& trg) +{ + + //Array used to sort properly digits + for (int i = kNDDL; i--;) { + for (int j = kNRow; j--;) { + for (int k = kNDilogic; k--;) { + mPadCharge[i][j][k].clear(); + } + } + } + + for (auto& dig : gsl::span(digitsbranch.data() + trg.getFirstEntry(), trg.getNumberOfObjects())) { + short absId = dig.getAbsId(); + short ddl, dilogic, row, hwAddr; + o2::cpv::Geometry::absIdToHWaddress(absId, ddl, row, dilogic, hwAddr); + + //Convert Amp to ADC counts + short charge = dig.getAmplitude() / mCalibParams->getGain(absId); + if (charge > 2047) { + charge = 2047; + } + mPadCharge[ddl][row][dilogic].emplace_back(charge, hwAddr); + } + + //Do through DLLs and fill raw words in proper order + for (int ddl = 0; ddl < kNDDL; ddl++) { + mPayload.clear(); + //write empty header, later will be updated ? + + int nwInSegment = 0; + int posRowMarker = 0; + for (int row = 0; row < kNRow; row++) { + //reserve place for row header + mPayload.emplace_back(uint32_t(0)); + posRowMarker = mPayload.size() - 1; + nwInSegment++; + int nwRow = 0; + for (Int_t dilogic = 0; dilogic < kNDilogic; dilogic++) { + int nPad = 0; + for (padCharge& pc : mPadCharge[ddl][row][dilogic]) { + PadWord currentword = {0}; + currentword.charge = pc.charge; + currentword.address = pc.pad; + currentword.dilogic = dilogic; + currentword.row = row; + mPayload.push_back(currentword.mDataWord); + nwInSegment++; + nPad++; + nwRow++; + } + EoEWord we = {0}; + we.nword = nPad; + we.dilogic = dilogic; + we.row = row; + we.checkbit = 1; + mPayload.push_back(we.mDataWord); + nwInSegment++; + nwRow++; + } + if (row % 8 == 7) { + SegMarkerWord w = {0}; + w.row = row; + w.nwords = nwInSegment; + w.marker = 2736; + mPayload.push_back(w.mDataWord); + nwInSegment = 0; + nwRow++; + } + //Now we know number of words, update rawMarker + RowMarkerWord wr = {0}; + wr.marker = 13992; + wr.nwords = nwRow - 1; + mPayload[posRowMarker] = wr.mDataWord; + } + + // register output data + LOG(DEBUG1) << "Adding payload with size " << mPayload.size() << " (" << mPayload.size() / 4 << " ALTRO words)"; + + short crorc = 0, link = ddl; + mRawWriter->addData(ddl, crorc, link, 0, trg.getBCData(), gsl::span<char>(reinterpret_cast<char*>(mPayload.data()), mPayload.size() * sizeof(uint32_t))); + } + return true; +} +int RawWriter::carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data, + const char* ptr, int maxSize, int splitID, + std::vector<char>& trailer, std::vector<char>& header) const +{ + + constexpr int phosTrailerSize = 36; + int offs = ptr - &data[0]; // offset wrt the head of the payload + assert(offs >= 0 && size_t(offs + maxSize) <= data.size()); // make sure ptr and end of the suggested block are within the payload + int leftBefore = data.size() - offs; // payload left before this splitting + int leftAfter = leftBefore - maxSize; // what would be left after the suggested splitting + int actualSize = maxSize; + if (leftAfter && leftAfter <= phosTrailerSize) { // avoid splitting the trailer or writing only it. + actualSize -= (phosTrailerSize - leftAfter) + 4; // (as we work with int, not char in decoding) + } + return actualSize; +} diff --git a/Detectors/CPV/workflow/CMakeLists.txt b/Detectors/CPV/workflow/CMakeLists.txt index ec8736c8cc89b..8b6a4e6657690 100644 --- a/Detectors/CPV/workflow/CMakeLists.txt +++ b/Detectors/CPV/workflow/CMakeLists.txt @@ -13,10 +13,24 @@ o2_add_library(CPVWorkflow src/PublisherSpec.cxx src/ClusterizerSpec.cxx src/DigitsPrinterSpec.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsCPV - O2::DPLUtils O2::CPVBase O2::CPVCalib O2::CPVSimulation O2::CPVReconstruction O2::Algorithm) + src/RawToDigitConverterSpec.cxx + src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsCPV + O2::DPLUtils + O2::CPVBase + O2::CPVCalib + O2::CPVSimulation + O2::CPVReconstruction + O2::Algorithm) o2_add_executable(reco-workflow COMPONENT_NAME cpv SOURCES src/cpv-reco-workflow.cxx PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(entropy-encoder-workflow + COMPONENT_NAME cpv + SOURCES src/entropy-encoder-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..7f23f832667d0 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to CPV digit/channels strean + +#ifndef O2_CPV_ENTROPYDECODER_SPEC +#define O2_CPV_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "CPVReconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace cpv +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::cpv::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace cpv +} // namespace o2 + +#endif diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..f7543b856e77f --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert CPV data to CTF (EncodedBlocks) + +#ifndef O2_CPV_ENTROPYENCODER_SPEC +#define O2_CPV_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "CPVReconstruction/CTFCoder.h" + +namespace o2 +{ +namespace cpv +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::cpv::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace cpv +} // namespace o2 + +#endif diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/RawToDigitConverterSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/RawToDigitConverterSpec.h new file mode 100644 index 0000000000000..78764982c9ebe --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/RawToDigitConverterSpec.h @@ -0,0 +1,81 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DataFormatsCPV/Digit.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "CPVCalib/CalibParams.h" +#include "CPVCalib/BadChannelMap.h" +#include "CPVReconstruction/RawDecoder.h" + +namespace o2 +{ + +namespace cpv +{ + +namespace reco_workflow +{ + +/// \class RawToDigitConverterSpec +/// \brief Coverter task for Raw data to CPV cells +/// \author Dmitri Peresunko NRC KI +/// \since Sept., 2020 +/// +class RawToDigitConverterSpec : public framework::Task +{ + public: + /// \brief Constructor + /// \param propagateMC If true the MCTruthContainer is propagated to the output + RawToDigitConverterSpec() : framework::Task(){}; + + /// \brief Destructor + ~RawToDigitConverterSpec() override = default; + + /// \brief Initializing the RawToDigitConverterSpec + /// \param ctx Init context + void init(framework::InitContext& ctx) final; + + /// \brief Run conversion of raw data to cells + /// \param ctx Processing context + /// + /// The following branches are linked: + /// Input RawData: {"ROUT", "RAWDATA", 0, Lifetime::Timeframe} + /// Output cells: {"CPV", "CELLS", 0, Lifetime::Timeframe} + /// Output cells trigger record: {"CPV", "CELLSTR", 0, Lifetime::Timeframe} + /// Output HW errors: {"CPV", "RAWHWERRORS", 0, Lifetime::Timeframe} + void run(framework::ProcessingContext& ctx) final; + + protected: + /// \brief simple check of HW address + char CheckHWAddress(short ddl, short hwAddress, short& fee); + + private: + int mDDL = 15; + std::unique_ptr<CalibParams> mCalibParams; ///< CPV calibration + std::unique_ptr<BadChannelMap> mBadMap; ///< BadMap + std::vector<Digit> mOutputDigits; ///< Container with output cells + std::vector<TriggerRecord> mOutputTriggerRecords; ///< Container with output cells + std::vector<RawDecoderError> mOutputHWErrors; ///< Errors occured in reading data +}; + +/// \brief Creating DataProcessorSpec for the CPV Cell Converter Spec +/// +/// Refer to RawToDigitConverterSpec::run for input and output specs +o2::framework::DataProcessorSpec getRawToDigitConverterSpec(); + +} // namespace reco_workflow + +} // namespace cpv + +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/ClusterizerSpec.cxx b/Detectors/CPV/workflow/src/ClusterizerSpec.cxx index 88d79e6bbeca9..7a6ab4cde247f 100644 --- a/Detectors/CPV/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/CPV/workflow/src/ClusterizerSpec.cxx @@ -23,41 +23,37 @@ void ClusterizerSpec::init(framework::InitContext& ctx) // Initialize clusterizer and link geometry mClusterizer.initialize(); + mClusterizer.propagateMC(mPropagateMC); } void ClusterizerSpec::run(framework::ProcessingContext& ctx) { - if (ctx.inputs().isValid("digits")) { - LOG(DEBUG) << "CPVClusterizer - run on digits called"; + LOG(INFO) << "Start run "; + LOG(DEBUG) << "CPVClusterizer - run on digits called"; + auto digits = ctx.inputs().get<gsl::span<Digit>>("digits"); + // auto digitsTR = ctx.inputs().get<std::span<TriggerRecord>>("digitTriggerRecords"); //TODO:: Why span does not work??? + // auto digits = ctx.inputs().get<std::vector<o2::cpv::Digit>>("digits"); + auto digitsTR = ctx.inputs().get<std::vector<o2::cpv::TriggerRecord>>("digitTriggerRecords"); - auto dataref = ctx.inputs().get("digits"); - auto const* cpvheader = o2::framework::DataRefUtils::getHeader<o2::cpv::CPVBlockHeader*>(dataref); - if (!cpvheader->mHasPayload) { - LOG(DEBUG) << "[CPVClusterizer - run] No more digits" << std::endl; - ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); - return; - } + // printf("CluSpec: digits=%d, TR=%d \n",digits.size(),digitsTR.size()) ; - // auto digits = ctx.inputs().get<gsl::span<o2::cpv::Digit>>("digits"); - auto digits = ctx.inputs().get<std::vector<o2::cpv::Digit>>("digits"); - auto digitsTR = ctx.inputs().get<std::vector<o2::cpv::TriggerRecord>>("digitTriggerRecords"); - - LOG(DEBUG) << "[CPVClusterizer - run] Received " << digitsTR.size() << " TR, running clusterizer ..."; - auto truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("digitsmctr"); - mClusterizer.process(digits, digitsTR, truthcont.get(), &mOutputClusters, &mOutputClusterTrigRecs, &mOutputTruthCont); // Find clusters on digits (pass by ref) + LOG(DEBUG) << "[CPVClusterizer - run] Received " << digitsTR.size() << " TR, running clusterizer ..."; + std::unique_ptr<const o2::dataformats::MCTruthContainer<MCCompLabel>> truthcont; + if (mPropagateMC) { + truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("digitsmctr"); } - LOG(DEBUG) << "[CPVClusterizer - run] Writing " << mOutputClusters.size() << " clusters, " << mOutputClusterTrigRecs.size() << "TR and " << mOutputTruthCont.getIndexedSize() << " Labels"; + mClusterizer.process(digits, digitsTR, *truthcont, &mOutputClusters, &mOutputClusterTrigRecs, &mOutputTruthCont); // Find clusters on digits (pass by ref) + 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); if (mPropagateMC) { ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthCont); } - LOG(INFO) << "Finished "; + LOG(INFO) << "Finished, wrote " << mOutputClusters.size() << " clusters, " << mOutputClusterTrigRecs.size() << "TR and " << mOutputTruthCont.getIndexedSize() << " Labels"; ctx.services().get<o2::framework::ControlService>().endOfStream(); ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); } - o2::framework::DataProcessorSpec o2::cpv::reco_workflow::getClusterizerSpec(bool propagateMC) { std::vector<o2::framework::InputSpec> inputs; diff --git a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..1b696185f7dbd --- /dev/null +++ b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace cpv +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("cpv-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& triggers = pc.outputs().make<std::vector<TriggerRecord>>(OutputRef{"triggers"}); + auto& clusters = pc.outputs().make<std::vector<Cluster>>(OutputRef{"clusters"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::cpv::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, triggers, clusters); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << clusters.size() << " CPV clusters in " << triggers.size() << " triggers in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "CPV Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"triggers"}, "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe}, + OutputSpec{{"clusters"}, "CPV", "CLUSTERS", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "cpv-entropy-decoder", + Inputs{InputSpec{"ctf", "CPV", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"cpv-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..95588b1f45e77 --- /dev/null +++ b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace cpv +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("cpv-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto triggers = pc.inputs().get<gsl::span<TriggerRecord>>("triggers"); + auto clusters = pc.inputs().get<gsl::span<Cluster>>("clusters"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"CPV", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, triggers, clusters); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + // eeb->print(); + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for CPV in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "CPV Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("triggers", "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); + inputs.emplace_back("clusters", "CPV", "CLUSTERS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "cpv-entropy-encoder", + inputs, + Outputs{{"CPV", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"cpv-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx b/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx new file mode 100644 index 0000000000000..219e19ccb6248 --- /dev/null +++ b/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx @@ -0,0 +1,212 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <string> +#include "FairLogger.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "DataFormatsCPV/CPVBlockHeader.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "DetectorsRaw/RDHUtils.h" +#include "CPVReconstruction/RawDecoder.h" +#include "CPVWorkflow/RawToDigitConverterSpec.h" +#include "CCDB/CcdbApi.h" +#include "CPVBase/CPVSimParams.h" +#include "CPVBase/Geometry.h" + +using namespace o2::cpv::reco_workflow; + +void RawToDigitConverterSpec::init(framework::InitContext& ctx) +{ + mDDL = ctx.options().get<int>("DDL"); + LOG(DEBUG) << "Initialize converter "; +} + +void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) +{ + // Cache digits from bunch crossings as the component reads timeframes from many links consecutively + std::map<o2::InteractionRecord, std::shared_ptr<std::vector<o2::cpv::Digit>>> digitBuffer; // Internal digit buffer + int firstEntry = 0; + mOutputHWErrors.clear(); + + if (!mCalibParams) { + if (o2::cpv::CPVSimParams::Instance().mCCDBPath.compare("localtest") == 0) { + mCalibParams = std::make_unique<o2::cpv::CalibParams>(1); // test default calibration + LOG(INFO) << "No reading calibration from ccdb requested, set default"; + } else { + LOG(INFO) << "Getting calibration object from ccdb"; + //TODO: configuring ccdb address from config file, readign proper calibration/BadMap and updateing if necessary + o2::ccdb::CcdbApi ccdb; + std::map<std::string, std::string> metadata; + ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation + // auto tr = triggerbranch.begin(); + // double eventTime = -1; + // if(tr!=triggerbranch.end()){ + // eventTime = (*tr).getBCData().getTimeNS() ; + // } + // mCalibParams = ccdb.retrieveFromTFileAny<o2::cpv::CalibParams>("CPV/Calib", metadata, eventTime); + // if (!mCalibParams) { + // LOG(FATAL) << "Can not get calibration object from ccdb"; + // } + } + } + + if (!mBadMap) { + if (o2::cpv::CPVSimParams::Instance().mCCDBPath.compare("localtest") == 0) { + mBadMap = std::make_unique<o2::cpv::BadChannelMap>(1); // test default calibration + LOG(INFO) << "No reading bad map from ccdb requested, set default"; + } else { + LOG(INFO) << "Getting bad map object from ccdb"; + o2::ccdb::CcdbApi ccdb; + std::map<std::string, std::string> metadata; + ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation + // auto tr = triggerbranch.begin(); + // double eventTime = -1; + // if(tr!=triggerbranch.end()){ + // eventTime = (*tr).getBCData().getTimeNS() ; + // } + // mBadMap = ccdb.retrieveFromTFileAny<o2::cpv::BadChannelMap>("CPV/BadMap", metadata, eventTime); + // if (!mBadMap) { + // LOG(FATAL) << "Can not get bad map object from ccdb"; + // } + } + } + + for (const auto& rawData : framework::InputRecordWalker(ctx.inputs())) { + + // enum RawErrorType_t { + // kOK, ///< NoError + // kNO_PAYLOAD, ///< No payload per ddl + // kHEADER_DECODING, + // kPAGE_NOTFOUND, + // kPAYLOAD_DECODING, + // kHEADER_INVALID, + // kRCU_TRAILER_ERROR, ///< RCU trailer cannot be decoded or invalid + // kRCU_VERSION_ERROR, ///< RCU trailer version not matching with the version in the raw header + // kRCU_TRAILER_SIZE_ERROR, ///< RCU trailer size length + // kSEGMENT_HEADER_ERROR, + // kROW_HEADER_ERROR, + // kEOE_HEADER_ERROR, + // kPADERROR, + // kPadAddress + // }; + + o2::cpv::RawReaderMemory rawreader(o2::framework::DataRefUtils::as<const char>(rawData)); + // loop over all the DMA pages + while (rawreader.hasNext()) { + try { + rawreader.next(); + } catch (RawErrorType_t e) { + LOG(ERROR) << "Raw decoding error " << (int)e; + //add error list + mOutputHWErrors.emplace_back(5, 0, 0, 0, e); //Put general errors to non-existing DDL5 + //if problem in header, abandon this page + if (e == RawErrorType_t::kPAGE_NOTFOUND || + e == RawErrorType_t::kHEADER_DECODING || + e == RawErrorType_t::kHEADER_INVALID) { + break; + } + //if problem in payload, try to continue + continue; + } + auto& header = rawreader.getRawHeader(); + auto triggerBC = o2::raw::RDHUtils::getTriggerBC(header); + auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(header); + auto ddl = o2::raw::RDHUtils::getFEEID(header); + ddl -= mDDL; + + o2::InteractionRecord currentIR(triggerBC, triggerOrbit); + std::shared_ptr<std::vector<o2::cpv::Digit>> currentDigitContainer; + auto found = digitBuffer.find(currentIR); + if (found == digitBuffer.end()) { + currentDigitContainer = std::make_shared<std::vector<o2::cpv::Digit>>(); + digitBuffer[currentIR] = currentDigitContainer; + } else { + currentDigitContainer = found->second; + } + // + if (ddl > o2::cpv::Geometry::kNDDL) { //only 4 correct DDLs + LOG(ERROR) << "DDL=" << ddl; + mOutputHWErrors.emplace_back(6, ddl, 0, 0, kHEADER_INVALID); //Add non-existing DDL as DDL 5 + continue; //skip STU ddl + } + // use the altro decoder to decode the raw data, and extract the RCU trailer + o2::cpv::RawDecoder decoder(rawreader); + RawErrorType_t err = decoder.decode(); + + if (err != kOK) { + //TODO handle severe errors + //TODO: probably careful conversion of decoder errors to Fitter errors? + mOutputHWErrors.emplace_back(ddl, 1, 0, 0, err); //assign general header errors to non-existing FEE 16 + } + // Loop over all the channels + for (uint32_t adch : decoder.getDigits()) { + AddressCharge ac = {adch}; + unsigned short absId = ac.Address; + //test bad map + if (mBadMap->isChannelGood(absId)) { + if (ac.Charge > o2::cpv::CPVSimParams::Instance().mZSthreshold) { + float amp = mCalibParams->getGain(absId) * ac.Charge; + currentDigitContainer->emplace_back(absId, amp, -1); + } + } + } + //Check and send list of hwErrors + for (auto& er : decoder.getErrors()) { + mOutputHWErrors.push_back(er); + } + } //RawReader::hasNext + } + + // Loop over BCs, sort digits with increasing digit ID and write to output containers + mOutputDigits.clear(); + mOutputTriggerRecords.clear(); + for (auto [bc, digits] : digitBuffer) { + int prevDigitSize = mOutputDigits.size(); + if (digits->size()) { + // Sort digits according to digit ID + std::sort(digits->begin(), digits->end(), [](o2::cpv::Digit& lhs, o2::cpv::Digit& rhs) { return lhs.getAbsId() < rhs.getAbsId(); }); + + for (auto digit : *digits) { + mOutputDigits.push_back(digit); + } + } + + mOutputTriggerRecords.emplace_back(bc, prevDigitSize, mOutputDigits.size() - prevDigitSize); + } + digitBuffer.clear(); + + LOG(INFO) << "[CPVRawToDigitConverter - run] Writing " << mOutputDigits.size() << " digits ..."; + 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); +} + +o2::framework::DataProcessorSpec o2::cpv::reco_workflow::getRawToDigitConverterSpec() +{ + std::vector<o2::framework::InputSpec> inputs; + inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"CPV", "RAWDATA"}, o2::framework::Lifetime::Timeframe); + + std::vector<o2::framework::OutputSpec> outputs; + outputs.emplace_back("CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe); + + return o2::framework::DataProcessorSpec{"CPVRawToDigitConverterSpec", + inputs, // o2::framework::select("A:CPV/RAWDATA"), + outputs, + o2::framework::adaptFromTask<o2::cpv::reco_workflow::RawToDigitConverterSpec>(), + o2::framework::Options{ + {"pedestal", o2::framework::VariantType::String, "off", {"Analyze as pedestal run on/off"}}, + {"DDL", o2::framework::VariantType::String, "0", {"DDL id to read"}}, + }}; +} diff --git a/Detectors/CPV/workflow/src/RecoWorkflow.cxx b/Detectors/CPV/workflow/src/RecoWorkflow.cxx index 0f73562af8901..f46a9614b5e37 100644 --- a/Detectors/CPV/workflow/src/RecoWorkflow.cxx +++ b/Detectors/CPV/workflow/src/RecoWorkflow.cxx @@ -24,12 +24,14 @@ #include "DataFormatsCPV/TriggerRecord.h" #include "CPVWorkflow/RecoWorkflow.h" #include "CPVWorkflow/ClusterizerSpec.h" -#include "CPVWorkflow/DigitsPrinterSpec.h" #include "CPVWorkflow/PublisherSpec.h" +#include "CPVWorkflow/RawToDigitConverterSpec.h" //#include "CPVWorkflow/RawWriterSpec.h" #include "Framework/DataSpecUtils.h" #include "SimulationDataFormat/MCTruthContainer.h" +using namespace o2::dataformats; + namespace o2 { @@ -81,20 +83,29 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, // specs.emplace_back(o2::cpv::reco_workflow::getRawWriterSpec()); // } + if (inputType == InputType::Raw) { + //no explicit raw reader + + if (isEnabled(OutputType::Digits)) { + specs.emplace_back(o2::cpv::reco_workflow::getRawToDigitConverterSpec()); + } + } + if (inputType == InputType::Digits) { - specs.emplace_back(o2::cpv::getPublisherSpec(PublisherConf{ - "cpv-digit-reader", - "o2sim", - {"digitbranch", "CPVDigit", "Digit branch"}, - {"digittrigger", "CPVDigitTrigRecords", "TrigRecords branch"}, - {"mcbranch", "CPVDigitMCTruth", "MC label branch"}, - o2::framework::OutputSpec{"CPV", "DIGITS"}, - o2::framework::OutputSpec{"CPV", "DIGITTRIGREC"}, - o2::framework::OutputSpec{"CPV", "DIGITSMCTR"}}, - propagateMC)); - - if (enableDigitsPrinter) - specs.emplace_back(o2::cpv::reco_workflow::getPhosDigitsPrinterSpec()); + // specs.emplace_back(o2::cpv::getPublisherSpec(PublisherConf{ + // "cpv-digit-reader", + // "o2sim", + // {"digitbranch", "CPVDigit", "Digit branch"}, + // {"digittrigger", "CPVDigitTrigRecords", "TrigRecords branch"}, + // {"mcbranch", "CPVDigitMCTruth", "MC label branch"}, + // o2::framework::OutputSpec{"CPV", "DIGITS"}, + // o2::framework::OutputSpec{"CPV", "DIGITTRIGREC"}, + // o2::framework::OutputSpec{"CPV", "DIGITSMCTR"}}, + // propagateMC)); + + // if (enableDigitsPrinter) { + // specs.emplace_back(o2::cpv::reco_workflow::getDigitsPrinterSpec()); + // } if (isEnabled(OutputType::Clusters)) { // add clusterizer @@ -107,106 +118,56 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, // } } - // check if the process is ready to quit - // this is decided upon the meta information in the CPV block header, the operation is set - // value kNoPayload in case of no data or no operation - // see also PublisherSpec.cxx - // in this workflow, the EOD is sent after the last real data, and all inputs will receive EOD, - // so it is enough to check on the first occurence - // FIXME: this will be changed once DPL can propagate control events like EOD - auto checkReady = [](o2::framework::DataRef const& ref) { - auto const* cpvheader = o2::framework::DataRefUtils::getHeader<o2::cpv::CPVBlockHeader*>(ref); - // sector number -1 indicates end-of-data - if (cpvheader != nullptr) { - // indicate normal processing if not ready and skip if ready - if (!cpvheader->mHasPayload) { - return std::make_tuple(o2::framework::MakeRootTreeWriterSpec::TerminationCondition::Action::SkipProcessing, true); - } - } - return std::make_tuple(o2::framework::MakeRootTreeWriterSpec::TerminationCondition::Action::DoProcessing, false); - }; - - // auto makeWriterSpec = [propagateMC, checkReady](const char* processName, - // const char* defaultFileName, - // const char* defaultTreeName, - // bool createMCMap, - // auto&& databranch, - // auto&& datatrbranch, - // auto&& mcbranch=nullptr, - // auto&& mcmapbranch=nullptr) { - // // depending on the MC propagation flag, the RootTreeWriter spec is created with two - // // or one branch definition - // if (propagateMC) { - // if(createMCMap){ - // return std::move(o2::framework::MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, - // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - // std::move(databranch), - // std::move(datatrbranch), - // std::move(mcbranch), - // std::move(mcmapbranch))); + // // check if the process is ready to quit + // // this is decided upon the meta information in the CPV block header, the operation is set + // // value kNoPayload in case of no data or no operation + // // see also PublisherSpec.cxx + // // in this workflow, the EOD is sent after the last real data, and all inputs will receive EOD, + // // so it is enough to check on the first occurence + // // FIXME: this will be changed once DPL can propagate control events like EOD + // auto checkReady = [](o2::framework::DataRef const& ref) { + // auto const* cpvheader = o2::framework::DataRefUtils::getHeader<o2::cpv::CPVBlockHeader*>(ref); + // // sector number -1 indicates end-of-data + // if (cpvheader != nullptr) { + // // indicate normal processing if not ready and skip if ready + // if (!cpvheader->mHasPayload) { + // return std::make_tuple(o2::framework::MakeRootTreeWriterSpec::TerminationCondition::Action::SkipProcessing, true); // } - // else{ - // return std::move(o2::framework::MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, - // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - // std::move(databranch), - // std::move(datatrbranch), - // std::move(mcbranch))); - // } - // } - // else{ - // return std::move(o2::framework::MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, - // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - // std::move(databranch), - // std::move(datatrbranch))); // } + // return std::make_tuple(o2::framework::MakeRootTreeWriterSpec::TerminationCondition::Action::DoProcessing, false); // }; - // if (isEnabled(OutputType::Raw)) { - // using RawOutputType = std::vector<o2::cpv::Raw>; - // using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - // specs.push_back(makeWriterSpec("cpv-raw-writer", - // inputType == InputType::Digits ? "cpv-raw.root" : "cpvrawcells.root", - // "o2sim", - // BranchDefinition<DigitOutputType>{o2::framework::InputSpec{"data", "CPV", "RAW", 0}, - // "CPVRaw", - // "raw-branch-name"}, - // BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "CPV", "RAWMCTR", 0}, - // "CPVRawMCTruth", - // "rawmc-branch-name"})()); - // } + // if (isEnabled(OutputType::Digits)) { + // using DigitOutputType = std::vector<o2::cpv::Digit>; + // using DTROutputType = std::vector<o2::cpv::TriggerRecord>; - if (isEnabled(OutputType::Digits)) { - using DigitOutputType = std::vector<o2::cpv::Digit>; - using DTROutputType = std::vector<o2::cpv::TriggerRecord>; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - - specs.emplace_back(o2::framework::MakeRootTreeWriterSpec("cpv-digits-writer", "cpvdigits.root", "o2sim", - -1, - o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - BranchDefinition<DigitOutputType>{o2::framework::InputSpec{"data", "CPV", "DIGITS", 0}, - "CPVDigit", - "digit-branch-name"}, - BranchDefinition<DTROutputType>{o2::framework::InputSpec{"data", "CPV", "DIGITTRIGREC", 0}, - "CPVDigTR", - "digittr-branch-name"}, - BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "CPV", "DIGITSMCTR", 0}, - "CPVDigitMCTruth", - "digitmc-branch-name"})()); - } + // specs.emplace_back(o2::framework::MakeRootTreeWriterSpec("cpv-digits-writer", "cpvdigits.root", "o2sim", + // -1, + // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, + // BranchDefinition<DigitOutputType>{o2::framework::InputSpec{"data", "CPV", "DIGITS", 0}, + // "CPVDigit", + // "digit-branch-name"}, + // BranchDefinition<DTROutputType>{o2::framework::InputSpec{"data", "CPV", "DIGITTRIGREC", 0}, + // "CPVDigTR", + // "digittr-branch-name"}, + // BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "CPV", "DIGITSMCTR", 0}, + // "CPVDigitMCTruth", + // "digitmc-branch-name"})()); + // } - if (isEnabled(OutputType::Clusters)) { - specs.emplace_back(o2::framework::MakeRootTreeWriterSpec("cpv-clusters-writer", "cpvclusters.root", "o2sim", -1, - o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - BranchDefinition<std::vector<o2::cpv::Cluster>>{o2::framework::InputSpec{"data", "CPV", "CLUSTERS", 0}, - "CPVCluster", - "cluster-branch-name"}, - BranchDefinition<std::vector<o2::cpv::TriggerRecord>>{o2::framework::InputSpec{"datatr", "CPV", "CLUSTERTRIGRECS", 0}, - "CPVClusTR", - "clustertr-branch-name"}, - BranchDefinition<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>{o2::framework::InputSpec{"mc", "CPV", "CLUSTERTRUEMC", 0}, - "CPVClusMC", - "clustermc-branch-name"})()); - } + // if (isEnabled(OutputType::Clusters)) { + // specs.emplace_back(o2::framework::MakeRootTreeWriterSpec("cpv-clusters-writer", "cpvclusters.root", "o2sim", -1, + // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, + // BranchDefinition<std::vector<o2::cpv::Cluster>>{o2::framework::InputSpec{"data", "CPV", "CLUSTERS", 0}, + // "CPVCluster", + // "cluster-branch-name"}, + // BranchDefinition<std::vector<o2::cpv::TriggerRecord>>{o2::framework::InputSpec{"datatr", "CPV", "CLUSTERTRIGRECS", 0}, + // "CPVClusTR", + // "clustertr-branch-name"}, + // BranchDefinition<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>{o2::framework::InputSpec{"mc", "CPV", "CLUSTERTRUEMC", 0}, + // "CPVClusMC", + // "clustermc-branch-name"})()); + // } return std::move(specs); } diff --git a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..5c43f8af68991 --- /dev/null +++ b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "CPVWorkflow/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::cpv::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/CTF/CMakeLists.txt b/Detectors/CTF/CMakeLists.txt index 4e1bd6ee7b22c..5565515a769fe 100644 --- a/Detectors/CTF/CMakeLists.txt +++ b/Detectors/CTF/CMakeLists.txt @@ -7,7 +7,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. - add_subdirectory(workflow) o2_add_test(itsmft @@ -25,3 +24,68 @@ o2_add_test(tpc SOURCES test/test_ctf_io_tpc.cxx COMPONENT_NAME ctf LABELS ctf) + +o2_add_test(ft0 + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::FT0Reconstruction + O2::DataFormatsFT0 + SOURCES test/test_ctf_io_ft0.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(fv0 + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::FV0Reconstruction + O2::DataFormatsFV0 + SOURCES test/test_ctf_io_fv0.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(fdd + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::FDDReconstruction + O2::DataFormatsFDD + SOURCES test/test_ctf_io_fdd.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(tof + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::TOFBase + O2::TOFReconstruction + O2::DataFormatsTOF + SOURCES test/test_ctf_io_tof.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(mid + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::DataFormatsMID + O2::MIDCTF + SOURCES test/test_ctf_io_mid.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(emcal + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::DataFormatsEMCAL + O2::EMCALReconstruction + SOURCES test/test_ctf_io_emcal.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(phos + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::DataFormatsPHOS + O2::PHOSReconstruction + SOURCES test/test_ctf_io_phos.cxx + COMPONENT_NAME ctf + LABELS ctf) + +o2_add_test(cpv + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow + O2::DataFormatsCPV + O2::CPVReconstruction + SOURCES test/test_ctf_io_cpv.cxx + COMPONENT_NAME ctf + LABELS ctf) diff --git a/Detectors/CTF/README.md b/Detectors/CTF/README.md index 18d26a91a072c..f064019f33f45 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -27,3 +27,25 @@ Example of usage: ```bash o2-ctf-reader-workflow --onlyDet ITS --ctf-input o2_ctf_0000000000.root | o2-its-reco-workflow --trackerCA --clusters-from-upstream --disable-mc ``` + +## Support for externally provided encoding dictionaries + +By default encoding with generate for every TF and store in the CTF the dictionary information necessary to decode the CTF. +Since the time needed for the creation of dictionary and encoder/decoder may exceed encoding/decoding time, there is a possibility +to create in a separate pass a dictionary stored in the CTF-like object and use it for further encoding/decoding. + +To create a dictionary run usual CTF creation chain but with extra option, e.g.: +```bash +o2-its-reco-workflow --entropy-encoding | o2-ctf-writer-workflow --output-type dict --onlyDet ITS +``` +This will create a file `ctf_dictionary.root` containing dictionary data for all detectors processed by the `o2-ctf-writer-workflow`. +By default the dictionary file is written on the exit from the workflow, in `CTFWriterSpec::endOfStream()` which is currently not called if the workflow is stopped +by `ctrl-C`. Periodic incremental saving of so-far accumulated dictionary data during processing can be triggered by providing an option +``--save-dict-after <N>``. + +Following encoding / decoding will use external dictionaries automatically if this file is found in the working directory (eventually it will be provided via CCDB). +Note that if the file is found but dictionary data for some detector participating in the workflow are not found, an error will be printed and for given detector +the workflows will use in-ctf dictionaries. +The dictionaries must be provided for decoding of CTF data encoded using external dictionaries (otherwise an exception will be thrown). + +When decoding CTF containing dictionary data (i.e. encoded w/o external dictionaries), the CTF-specific dictionary will be created/used on the fly, ignoring eventually provided external dictionary data. diff --git a/Detectors/CTF/test/test_ctf_io_cpv.cxx b/Detectors/CTF/test/test_ctf_io_cpv.cxx new file mode 100644 index 0000000000000..b60131e60c851 --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_cpv.cxx @@ -0,0 +1,127 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CPVCTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "CPVReconstruction/CTFCoder.h" +#include "DataFormatsCPV/CTF.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <TSystem.h> +#include <cstring> + +using namespace o2::cpv; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<TriggerRecord> triggers; + std::vector<Cluster> clusters; + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + Cluster clu; + for (int irof = 0; irof < 1000; irof++) { + ir += 1 + gRandom->Integer(200); + + auto start = clusters.size(); + int n = 1 + gRandom->Poisson(100); + for (int i = n; i--;) { + char mult = gRandom->Integer(30); + char mod = 1 + gRandom->Integer(3); + char exMax = gRandom->Integer(3); + float x = 72.3 * 2. * (gRandom->Rndm() - 0.5); + float z = 63.3 * 2. * (gRandom->Rndm() - 0.5); + float e = 254. * gRandom->Rndm(); + clusters.emplace_back(mult, mod, exMax, x, z, e); + } + triggers.emplace_back(ir, start, clusters.size() - start); + } + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, triggers, clusters); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + auto* ctfImage = o2::cpv::CTF::get(vec.data()); + TFile flOut("test_ctf_cpv.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "CPV"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + LOG(INFO) << "Start reading from tree "; + { + sw.Start(); + TFile flIn("test_ctf_cpv.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::cpv::CTF::readFromTree(vec, *(tree.get()), "CPV"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<TriggerRecord> triggersD; + std::vector<Cluster> clustersD; + + sw.Start(); + const auto ctfImage = o2::cpv::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, triggersD, clustersD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(triggersD.size() == triggers.size()); + BOOST_CHECK(clustersD.size() == clusters.size()); + LOG(INFO) << " BOOST_CHECK triggersD.size() " << triggersD.size() << " triggers.size() " << triggers.size() + << " BOOST_CHECK(clustersD.size() " << clustersD.size() << " clusters.size()) " << clusters.size(); + + for (size_t i = 0; i < triggers.size(); i++) { + const auto& dor = triggers[i]; + const auto& ddc = triggersD[i]; + LOG(DEBUG) << " Orig.TriggerRecord " << i << " " << dor.getBCData() << " " << dor.getFirstEntry() << " " << dor.getNumberOfObjects(); + LOG(DEBUG) << " Deco.TriggerRecord " << i << " " << ddc.getBCData() << " " << ddc.getFirstEntry() << " " << ddc.getNumberOfObjects(); + + BOOST_CHECK(dor.getBCData() == ddc.getBCData()); + BOOST_CHECK(dor.getNumberOfObjects() == ddc.getNumberOfObjects()); + BOOST_CHECK(dor.getFirstEntry() == dor.getFirstEntry()); + } + + for (size_t i = 0; i < clusters.size(); i++) { + const auto& cor = clusters[i]; + const auto& cdc = clustersD[i]; + BOOST_CHECK(cor.getMultiplicity() == cdc.getMultiplicity()); + BOOST_CHECK(cor.getModule() == cdc.getModule()); + BOOST_CHECK(TMath::Abs(cor.getEnergy() - cdc.getEnergy()) < 1.); + float xCor, zCor, xCdc, zCdc; + cor.getLocalPosition(xCor, zCor); + cdc.getLocalPosition(xCdc, zCdc); + BOOST_CHECK(TMath::Abs(xCor - xCdc) < 0.004); + BOOST_CHECK(TMath::Abs(zCor - zCdc) < 0.004); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_emcal.cxx b/Detectors/CTF/test/test_ctf_io_emcal.cxx new file mode 100644 index 0000000000000..974ff2f3e1174 --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_emcal.cxx @@ -0,0 +1,120 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EMCCTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "EMCALReconstruction/CTFCoder.h" +#include "DataFormatsEMCAL/CTF.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <TSystem.h> +#include <cstring> + +using namespace o2::emcal; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<TriggerRecord> triggers; + std::vector<Cell> cells; + // gSystem->Load("libO2DetectorsCommonDataFormats"); + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + for (int irof = 0; irof < 1000; irof++) { + ir += 1 + gRandom->Integer(200); + + auto start = cells.size(); + short tower = gRandom->Poisson(10); + 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); + tower += 1 + gRandom->Integer(100); + } + triggers.emplace_back(ir, start, cells.size() - start); + } + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, triggers, cells); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + auto* ctfImage = o2::emcal::CTF::get(vec.data()); + TFile flOut("test_ctf_emcal.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "EMC"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_emcal.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::emcal::CTF::readFromTree(vec, *(tree.get()), "EMC"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<TriggerRecord> triggersD; + std::vector<Cell> cellsD; + + sw.Start(); + const auto ctfImage = o2::emcal::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, triggersD, cellsD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(triggersD.size() == triggers.size()); + BOOST_CHECK(cellsD.size() == cells.size()); + LOG(INFO) << " BOOST_CHECK triggersD.size() " << triggersD.size() << " triggers.size() " << triggers.size() + << " BOOST_CHECK(cellsD.size() " << cellsD.size() << " cells.size()) " << cells.size(); + + for (size_t i = 0; i < triggers.size(); i++) { + const auto& dor = triggers[i]; + const auto& ddc = triggersD[i]; + LOG(DEBUG) << " Orig.TriggerRecord " << i << " " << dor.getBCData() << " " << dor.getFirstEntry() << " " << dor.getNumberOfObjects(); + LOG(DEBUG) << " Deco.TriggerRecord " << i << " " << ddc.getBCData() << " " << ddc.getFirstEntry() << " " << ddc.getNumberOfObjects(); + + BOOST_CHECK(dor.getBCData() == ddc.getBCData()); + BOOST_CHECK(dor.getNumberOfObjects() == ddc.getNumberOfObjects()); + BOOST_CHECK(dor.getFirstEntry() == dor.getFirstEntry()); + } + + for (size_t i = 0; i < cells.size(); i++) { + const auto& cor = cells[i]; + const auto& cdc = cellsD[i]; + BOOST_CHECK(cor.getPackedTowerID() == cdc.getPackedTowerID()); + BOOST_CHECK(cor.getPackedTime() == cdc.getPackedTime()); + BOOST_CHECK(cor.getPackedEnergy() == cdc.getPackedEnergy()); + BOOST_CHECK(cor.getPackedCellStatus() == cdc.getPackedCellStatus()); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_fdd.cxx b/Detectors/CTF/test/test_ctf_io_fdd.cxx new file mode 100644 index 0000000000000..96b8543e59e6b --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_fdd.cxx @@ -0,0 +1,146 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FDDCTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "FDDReconstruction/CTFCoder.h" +#include "FDDBase/Constants.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <cstring> + +using namespace o2::fdd; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<Digit> digits; + std::vector<ChannelData> channels; + + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + + for (int idig = 0; idig < 1000; idig++) { + ir += 1 + gRandom->Integer(200); + uint8_t ich = gRandom->Poisson(4); + auto start = channels.size(); + int32_t tMeanA = 0, tMeanC = 0; + int32_t ampTotA = 0, ampTotC = 0; + Triggers trig; + trig.triggersignals = gRandom->Integer(128); + while (ich < Nchannels) { + int16_t t = -2048 + gRandom->Integer(2048 * 2); + uint16_t q = gRandom->Integer(4096); + uint16_t feb = gRandom->Rndm() > 0.5 ? 0 : 1; + channels.emplace_back(ich, t, q, feb); + + if (ich > 7) { + trig.nChanA++; + ampTotA += q; + tMeanA += t; + } else { + trig.nChanC++; + ampTotC += q; + tMeanC += t; + } + ich += 1 + gRandom->Poisson(4); + } + if (trig.nChanA) { + trig.timeA = tMeanA / trig.nChanA; + trig.amplA = ampTotA * 0.125; + } + if (trig.nChanC) { + trig.timeC = tMeanC / trig.nChanC; + trig.amplC = ampTotC * 0.125; // sum/8 + } + + auto end = channels.size(); + digits.emplace_back(start, end - start, ir, trig); + } + + LOG(INFO) << "Generated " << channels.size() << " channels in " << digits.size() << " digits " << sw.CpuTime() << " s"; + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, digits, channels); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + TFile flOut("test_ctf_fdd.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + auto* ctfImage = o2::fdd::CTF::get(vec.data()); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "FDD"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_fdd.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::fdd::CTF::readFromTree(vec, *(tree.get()), "FDD"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<Digit> digitsD; + std::vector<ChannelData> channelsD; + + sw.Start(); + const auto ctfImage = o2::fdd::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, digitsD, channelsD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(digitsD.size() == digits.size()); + BOOST_CHECK(channelsD.size() == channels.size()); + LOG(INFO) << " BOOST_CHECK digitsD.size() " << digitsD.size() << " digits.size() " << digits.size() << " BOOST_CHECK(channelsD.size() " << channelsD.size() << " channels.size()) " << channels.size(); + + for (int i = digits.size(); i--;) { + const auto& dor = digits[i]; + const auto& ddc = digitsD[i]; + BOOST_CHECK(dor.mIntRecord == ddc.mIntRecord); + BOOST_CHECK(dor.ref == ddc.ref); + BOOST_CHECK(dor.mTriggers.nChanA == ddc.mTriggers.nChanA); + BOOST_CHECK(dor.mTriggers.nChanC == ddc.mTriggers.nChanC); + BOOST_CHECK(dor.mTriggers.amplA == ddc.mTriggers.amplA); + BOOST_CHECK(dor.mTriggers.amplC == ddc.mTriggers.amplC); + BOOST_CHECK(dor.mTriggers.timeA == ddc.mTriggers.timeA); + BOOST_CHECK(dor.mTriggers.timeC == ddc.mTriggers.timeC); + BOOST_CHECK(dor.mTriggers.triggersignals == ddc.mTriggers.triggersignals); + } + for (int i = channels.size(); i--;) { + const auto& cor = channels[i]; + const auto& cdc = channelsD[i]; + BOOST_CHECK(cor.mPMNumber == cdc.mPMNumber); + BOOST_CHECK(cor.mTime == cdc.mTime); + BOOST_CHECK(cor.mChargeADC == cdc.mChargeADC); + BOOST_CHECK(cor.mFEEBits == cdc.mFEEBits); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_ft0.cxx b/Detectors/CTF/test/test_ctf_io_ft0.cxx new file mode 100644 index 0000000000000..a870b6549b16f --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_ft0.cxx @@ -0,0 +1,150 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FT0CTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "FT0Reconstruction/CTFCoder.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <cstring> + +using namespace o2::ft0; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<Digit> digits; + std::vector<ChannelData> channels; + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + + int mTime_trg_gate = 192; // #channels + constexpr int MAXChan = 4 * (Geometry::NCellsA + Geometry::NCellsC); + for (int idig = 0; idig < 1000; idig++) { + ir += 1 + gRandom->Integer(200); + uint8_t ich = gRandom->Poisson(10); + auto start = channels.size(); + int32_t tMeanA = 0, tMeanC = 0; + int32_t ampTotA = 0, ampTotC = 0; + uint8_t eventFlag = 10; + Triggers trig; + trig.triggersignals = gRandom->Integer(128); + while (ich < MAXChan) { + int16_t t = -2048 + gRandom->Integer(2048 * 2); + uint16_t q = gRandom->Integer(4096); + uint8_t chain = gRandom->Rndm() > 0.5 ? 0 : 1; + channels.emplace_back(ich, t, q, chain); + if (std::abs(t) < Geometry::mTime_trg_gate) { + if (ich < 4 * uint8_t(Geometry::NCellsA)) { + trig.nChanA++; + ampTotA += q; + tMeanA += t; + } else { + trig.nChanC++; + ampTotC += q; + tMeanC += t; + } + } + ich += 1 + gRandom->Poisson(10); + } + if (trig.nChanA) { + trig.timeA = tMeanA / trig.nChanA; + trig.amplA = ampTotA * 0.125; + } + if (trig.nChanC) { + trig.timeC = tMeanC / trig.nChanC; + trig.amplC = ampTotC * 0.125; // sum/8 + } + auto end = channels.size(); + digits.emplace_back(start, end - start, ir, trig, idig); + digits[idig].setEventStatus(eventFlag); + } + + LOG(INFO) << "Generated " << channels.size() << " channels in " << digits.size() << " digits " << sw.CpuTime() << " s"; + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, digits, channels); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + TFile flOut("test_ctf_ft0.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + auto* ctfImage = o2::ft0::CTF::get(vec.data()); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "FT0"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_ft0.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::ft0::CTF::readFromTree(vec, *(tree.get()), "FT0"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<Digit> digitsD; + std::vector<ChannelData> channelsD; + + sw.Start(); + const auto ctfImage = o2::ft0::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, digitsD, channelsD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(digitsD.size() == digits.size()); + BOOST_CHECK(channelsD.size() == channels.size()); + LOG(DEBUG) << " BOOST_CHECK digitsD.size() " << digitsD.size() << " digits.size() " << digits.size() << " BOOST_CHECK(channelsD.size() " << channelsD.size() << " channels.size()) " << channels.size(); + + for (int i = digits.size(); i--;) { + const auto& dor = digits[i]; + const auto& ddc = digitsD[i]; + LOG(DEBUG) << " dor " << dor.mTriggers.nChanA << " " << dor.mTriggers.nChanC << " " << dor.mTriggers.amplA << " " << dor.mTriggers.amplC; + LOG(DEBUG) << " ddc " << ddc.mTriggers.nChanA << " " << ddc.mTriggers.nChanC << " " << ddc.mTriggers.amplA << " " << ddc.mTriggers.amplC; + + BOOST_CHECK(dor.mIntRecord == ddc.mIntRecord); + BOOST_CHECK(dor.mTriggers.nChanA == ddc.mTriggers.nChanA); + BOOST_CHECK(dor.mTriggers.nChanC == ddc.mTriggers.nChanC); + BOOST_CHECK(dor.mTriggers.amplA == ddc.mTriggers.amplA); + BOOST_CHECK(dor.mTriggers.amplC == ddc.mTriggers.amplC); + BOOST_CHECK(dor.mTriggers.timeA == ddc.mTriggers.timeA); + BOOST_CHECK(dor.mTriggers.timeC == ddc.mTriggers.timeC); + BOOST_CHECK(dor.mTriggers.triggersignals == ddc.mTriggers.triggersignals); + } + for (int i = channels.size(); i--;) { + const auto& cor = channels[i]; + const auto& cdc = channelsD[i]; + BOOST_CHECK(cor.ChId == cdc.ChId); + BOOST_CHECK(cor.ChainQTC == cdc.ChainQTC); + BOOST_CHECK(cor.CFDTime == cdc.CFDTime); + BOOST_CHECK(cor.QTCAmpl == cdc.QTCAmpl); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_fv0.cxx b/Detectors/CTF/test/test_ctf_io_fv0.cxx new file mode 100644 index 0000000000000..efa49d28e37e9 --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_fv0.cxx @@ -0,0 +1,117 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FV0CTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "FV0Reconstruction/CTFCoder.h" +#include "FV0Base/Constants.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <cstring> + +using namespace o2::fv0; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<BCData> digits; + std::vector<ChannelData> channels; + Triggers trigger; // TODO: Actual values are not set + + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + + constexpr int MAXChan = Constants::nChannelsPerPm * Constants::nPms; // RSFIXME is this correct ? + for (int idig = 0; idig < 1000; idig++) { + ir += 1 + gRandom->Integer(200); + uint8_t ich = gRandom->Poisson(10); + auto start = channels.size(); + while (ich < MAXChan) { + int16_t t = -2048 + gRandom->Integer(2048 * 2); + uint16_t q = gRandom->Integer(4096); + channels.emplace_back(ich, t, q); + ich += 1 + gRandom->Poisson(10); + } + auto end = channels.size(); + + digits.emplace_back(start, end - start, ir, trigger); + } + + LOG(INFO) << "Generated " << channels.size() << " channels in " << digits.size() << " digits " << sw.CpuTime() << " s"; + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, digits, channels); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + TFile flOut("test_ctf_fv0.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + auto* ctfImage = o2::fv0::CTF::get(vec.data()); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "FV0"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_fv0.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::fv0::CTF::readFromTree(vec, *(tree.get()), "FV0"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<BCData> digitsD; + std::vector<ChannelData> channelsD; + + sw.Start(); + const auto ctfImage = o2::fv0::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, digitsD, channelsD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(digitsD.size() == digits.size()); + BOOST_CHECK(channelsD.size() == channels.size()); + LOG(INFO) << " BOOST_CHECK digitsD.size() " << digitsD.size() << " digits.size() " << digits.size() << " BOOST_CHECK(channelsD.size() " << channelsD.size() << " channels.size()) " << channels.size(); + + for (int i = digits.size(); i--;) { + const auto& dor = digits[i]; + const auto& ddc = digitsD[i]; + BOOST_CHECK(dor.ir == ddc.ir); + BOOST_CHECK(dor.ref == ddc.ref); + } + for (int i = channels.size(); i--;) { + const auto& cor = channels[i]; + const auto& cdc = channelsD[i]; + BOOST_CHECK(cor.pmtNumber == cdc.pmtNumber); + BOOST_CHECK(cor.time == cdc.time); + BOOST_CHECK(cor.chargeAdc == cdc.chargeAdc); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_itsmft.cxx b/Detectors/CTF/test/test_ctf_io_itsmft.cxx index 43d7353372606..ad67d55574d0e 100644 --- a/Detectors/CTF/test/test_ctf_io_itsmft.cxx +++ b/Detectors/CTF/test/test_ctf_io_itsmft.cxx @@ -69,7 +69,10 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) sw.Start(); std::vector<o2::ctf::BufferType> vec; - CTFCoder::encode(vec, rofRecVec, cclusVec, pattVec); // compress + { + CTFCoder coder(o2::detectors::DetID::ITS); + coder.encode(vec, rofRecVec, cclusVec, pattVec); // compress + } sw.Stop(); LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; @@ -103,7 +106,10 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) std::vector<unsigned char> pattVecD; sw.Start(); const auto ctfImage = o2::itsmft::CTF::getImage(vec.data()); - CTFCoder::decode(ctfImage, rofRecVecD, cclusVecD, pattVecD); // decompress + { + CTFCoder coder(o2::detectors::DetID::ITS); + coder.decode(ctfImage, rofRecVecD, cclusVecD, pattVecD); // decompress + } sw.Stop(); LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; diff --git a/Detectors/CTF/test/test_ctf_io_mid.cxx b/Detectors/CTF/test/test_ctf_io_mid.cxx new file mode 100644 index 0000000000000..80bd5c098e003 --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_mid.cxx @@ -0,0 +1,130 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDCTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "MIDCTF/CTFCoder.h" +#include "DataFormatsMID/CTF.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <TSystem.h> +#include <cstring> + +using namespace o2::mid; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<ROFRecord> rofs; + std::vector<ColumnData> cols; + // RS: don't understand why, but this library is not loaded automatically, although the dependencies are clearly + // indicated. What it more weird is that for similar tests of other detectors the library is loaded! + // Absence of the library leads to complains about the StreamerInfo and eventually segm.faul when appending the + // CTH to the tree. For this reason I am loading it here manually + gSystem->Load("libO2DetectorsCommonDataFormats"); + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + std::array<uint16_t, 5> pattern; + for (int irof = 0; irof < 1000; irof++) { + ir += 1 + gRandom->Integer(200); + uint8_t nch = 0, evtyp = gRandom->Integer(3); + while (nch == 0) { + nch = gRandom->Poisson(10); + } + auto start = cols.size(); + for (int ich = 0; ich < nch; ich++) { + uint8_t deId = gRandom->Integer(128); + uint8_t columnId = gRandom->Integer(128); + for (int i = 0; i < 5; i++) { + pattern[i] = gRandom->Integer(0x7fff); + } + cols.emplace_back(ColumnData{deId, columnId, pattern}); + } + rofs.emplace_back(ROFRecord{ir, EventType(evtyp), start, cols.size() - start}); + } + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, rofs, cols); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + auto* ctfImage = o2::mid::CTF::get(vec.data()); + TFile flOut("test_ctf_mid.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "MID"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_mid.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::mid::CTF::readFromTree(vec, *(tree.get()), "MID"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<ROFRecord> rofsD; + std::vector<ColumnData> colsD; + + sw.Start(); + const auto ctfImage = o2::mid::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, rofsD, colsD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(rofsD.size() == rofs.size()); + BOOST_CHECK(colsD.size() == cols.size()); + LOG(INFO) << " BOOST_CHECK rofsD.size() " << rofsD.size() << " rofs.size() " << rofs.size() + << " BOOST_CHECK(colsD.size() " << colsD.size() << " cols.size()) " << cols.size(); + + for (size_t i = 0; i < rofs.size(); i++) { + const auto& dor = rofs[i]; + const auto& ddc = rofsD[i]; + LOG(DEBUG) << " Orig.ROFRecord " << i << " " << dor.interactionRecord << " " << dor.firstEntry << " " << dor.nEntries; + LOG(DEBUG) << " Deco.ROFRecord " << i << " " << ddc.interactionRecord << " " << ddc.firstEntry << " " << ddc.nEntries; + + BOOST_CHECK(dor.interactionRecord == ddc.interactionRecord); + BOOST_CHECK(dor.firstEntry == ddc.firstEntry); + BOOST_CHECK(dor.nEntries == dor.nEntries); + } + + for (size_t i = 0; i < cols.size(); i++) { + const auto& cor = cols[i]; + const auto& cdc = colsD[i]; + BOOST_CHECK(cor.deId == cdc.deId); + BOOST_CHECK(cor.columnId == cdc.columnId); + for (int j = 0; j < 5; j++) { + BOOST_CHECK(cor.patterns[j] == cdc.patterns[j]); + LOG(DEBUG) << "col " << i << " pat " << j << " : " << cor.patterns[j] << " : " << cdc.patterns[j]; + } + } +} diff --git a/Detectors/CTF/test/test_ctf_io_phos.cxx b/Detectors/CTF/test/test_ctf_io_phos.cxx new file mode 100644 index 0000000000000..964eca2dcaa5d --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_phos.cxx @@ -0,0 +1,119 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PHSCTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DetectorsCommonDataFormats/NameConf.h" +#include "PHOSReconstruction/CTFCoder.h" +#include "DataFormatsPHOS/CTF.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <TSystem.h> +#include <cstring> + +using namespace o2::phos; + +BOOST_AUTO_TEST_CASE(CTFTest) +{ + std::vector<TriggerRecord> triggers; + std::vector<Cell> cells; + TStopwatch sw; + sw.Start(); + o2::InteractionRecord ir(0, 0); + for (int irof = 0; irof < 1000; irof++) { + ir += 1 + gRandom->Integer(200); + + auto start = cells.size(); + int n = 1 + gRandom->Poisson(100); + for (int i = n; i--;) { + ChannelType_t tp = gRandom->Rndm() > 0.5 ? TRU : (gRandom->Rndm() > 0.5 ? HIGH_GAIN : LOW_GAIN); + uint16_t id = tp == TRU ? 3000 : gRandom->Integer(kNmaxCell); + float timeCell = gRandom->Rndm() * 3.00e-07 - 0.3e-9; + float en = gRandom->Rndm() * 160.; + cells.emplace_back(id, en, timeCell, tp); + } + triggers.emplace_back(ir, start, cells.size() - start); + } + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, triggers, cells); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + auto* ctfImage = o2::phos::CTF::get(vec.data()); + TFile flOut("test_ctf_phos.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "PHS"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_phos.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + o2::phos::CTF::readFromTree(vec, *(tree.get()), "PHS"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<TriggerRecord> triggersD; + std::vector<Cell> cellsD; + + sw.Start(); + const auto ctfImage = o2::phos::CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, triggersD, cellsD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + BOOST_CHECK(triggersD.size() == triggers.size()); + BOOST_CHECK(cellsD.size() == cells.size()); + LOG(INFO) << " BOOST_CHECK triggersD.size() " << triggersD.size() << " triggers.size() " << triggers.size() + << " BOOST_CHECK(cellsD.size() " << cellsD.size() << " cells.size()) " << cells.size(); + + for (size_t i = 0; i < triggers.size(); i++) { + const auto& dor = triggers[i]; + const auto& ddc = triggersD[i]; + LOG(DEBUG) << " Orig.TriggerRecord " << i << " " << dor.getBCData() << " " << dor.getFirstEntry() << " " << dor.getNumberOfObjects(); + LOG(DEBUG) << " Deco.TriggerRecord " << i << " " << ddc.getBCData() << " " << ddc.getFirstEntry() << " " << ddc.getNumberOfObjects(); + + BOOST_CHECK(dor.getBCData() == ddc.getBCData()); + BOOST_CHECK(dor.getNumberOfObjects() == ddc.getNumberOfObjects()); + BOOST_CHECK(dor.getFirstEntry() == dor.getFirstEntry()); + } + + for (size_t i = 0; i < cells.size(); i++) { + const auto& cor = cells[i]; + const auto& cdc = cellsD[i]; + BOOST_CHECK(cor.getPackedID() == cdc.getPackedID()); + BOOST_CHECK(cor.getPackedTime() == cdc.getPackedTime()); + BOOST_CHECK(cor.getPackedEnergy() == cdc.getPackedEnergy()); + BOOST_CHECK(cor.getPackedCellStatus() == cdc.getPackedCellStatus()); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_tof.cxx b/Detectors/CTF/test/test_ctf_io_tof.cxx new file mode 100644 index 0000000000000..27810fc5b9292 --- /dev/null +++ b/Detectors/CTF/test/test_ctf_io_tof.cxx @@ -0,0 +1,143 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TOFCTFIO +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "DataFormatsTOF/CTF.h" +#include "TOFBase/Geo.h" +#include "TOFBase/Digit.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "TOFReconstruction/CTFCoder.h" +#include "Framework/Logger.h" +#include <TFile.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include <cstring> + +using namespace o2::tof; + +BOOST_AUTO_TEST_CASE(CompressedClustersTest) +{ + + std::vector<Digit> digits; + std::vector<ReadoutWindowData> rows; + std::vector<uint32_t> pattVec; + + TStopwatch sw; + sw.Start(); + std::vector<int> row, col; + for (int irof = 0; irof < 100; irof++) { // loop over row + auto& rofr = rows.emplace_back(); + int orbit = irof / Geo::NWINDOW_IN_ORBIT; + int bc = Geo::BC_IN_ORBIT / Geo::NWINDOW_IN_ORBIT * (irof % 3); + rofr.SetOrbit(orbit); + rofr.SetBC(bc); + int ndig = gRandom->Poisson(50); + + rofr.setFirstEntry(digits.size()); + rofr.setNEntries(ndig); + // fill empty pattern (to be changed) + rofr.setFirstEntryDia(pattVec.size()); + rofr.setNEntriesDia(0); + std::vector<int> istrip; + for (int i = 0; i < ndig; i++) { + istrip.emplace_back(gRandom->Integer(Geo::NSTRIPS)); + } + std::sort(istrip.begin(), istrip.end()); + + for (int i = 0; i < ndig; i++) { + int ch = istrip[i] * Geo::NPADS + gRandom->Integer(Geo::NPADS); + uint16_t TDC = gRandom->Integer(1024); + uint16_t TOT = gRandom->Integer(2048); + uint64_t BC = Geo::BC_IN_ORBIT * orbit + bc + gRandom->Integer(Geo::BC_IN_ORBIT / Geo::NWINDOW_IN_ORBIT); + + digits.emplace_back(ch, TDC, TOT, BC); + } + + std::sort(digits.begin(), digits.end(), + [](const Digit& a, const Digit& b) { + int strip1 = a.getChannel() / Geo::NPADS, strip2 = b.getChannel() / Geo::NPADS; + if (strip1 == strip2) { + if (a.getBC() == b.getBC()) { + return a.getTDC() < b.getTDC(); + } + return a.getBC() < b.getBC(); + } + return strip1 < strip2; + }); + + // for (int i = 0; i < ndig; i++) + // LOG(INFO) << "ROW = " << irof << " - Strip = " << digits[i].getChannel() / Geo::NPADS << " - BC = " << digits[i].getBC() << " - TDC = " << digits[i].getTDC(); + } + sw.Stop(); + LOG(INFO) << "Generated " << digits.size() << " in " << rows.size() << " ROFs in " << sw.CpuTime() << " s"; + + sw.Start(); + std::vector<o2::ctf::BufferType> vec; + { + CTFCoder coder; + coder.encode(vec, rows, digits, pattVec); // compress + } + sw.Stop(); + LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; + + // writing + { + sw.Start(); + TFile flOut("test_ctf_tof.root", "recreate"); + TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + auto* ctfImage = CTF::get(vec.data()); + ctfImage->print(); + ctfImage->appendToTree(ctfTree, "TOF"); + ctfTree.Write(); + sw.Stop(); + LOG(INFO) << "Wrote to tree in " << sw.CpuTime() << " s"; + } + + // reading + vec.clear(); + { + sw.Start(); + TFile flIn("test_ctf_tof.root"); + std::unique_ptr<TTree> tree((TTree*)flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); + BOOST_CHECK(tree); + CTF::readFromTree(vec, *(tree.get()), "TOF"); + sw.Stop(); + LOG(INFO) << "Read back from tree in " << sw.CpuTime() << " s"; + } + + std::vector<Digit> digitsD; + std::vector<ReadoutWindowData> rowsD; + std::vector<uint32_t> pattVecD; + sw.Start(); + const auto ctfImage = CTF::getImage(vec.data()); + { + CTFCoder coder; + coder.decode(ctfImage, rowsD, digitsD, pattVecD); // decompress + } + sw.Stop(); + LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; + + // + // simple checks + BOOST_CHECK(rows.size() == rowsD.size()); + BOOST_CHECK(digits.size() == digitsD.size()); + BOOST_CHECK(pattVec.size() == pattVecD.size()); + + // more sophisticated checks + + // checks on patterns + int npatt = ctfImage.getHeader().nPatternBytes; + for (int i = 0; i < npatt; i += 100) { + BOOST_CHECK(pattVecD[i] == pattVec[i]); + } +} diff --git a/Detectors/CTF/test/test_ctf_io_tpc.cxx b/Detectors/CTF/test/test_ctf_io_tpc.cxx index 342ffd6b77501..99ebe3a36a5ef 100644 --- a/Detectors/CTF/test/test_ctf_io_tpc.cxx +++ b/Detectors/CTF/test/test_ctf_io_tpc.cxx @@ -39,7 +39,10 @@ BOOST_AUTO_TEST_CASE(CTFTest) bVec.resize(sz); ccFlat = reinterpret_cast<CompressedClustersFlat*>(bVec.data()); auto buff = reinterpret_cast<void*>(reinterpret_cast<char*>(bVec.data()) + sizeCFlatBody); - CTFCoder::setCompClusAddresses(c, buff); + { + CTFCoder coder; + coder.setCompClusAddresses(c, buff); + } ccFlat->set(sz, c); // fill some data @@ -80,7 +83,10 @@ BOOST_AUTO_TEST_CASE(CTFTest) TStopwatch sw; sw.Start(); std::vector<o2::ctf::BufferType> vecIO; - CTFCoder::encode(vecIO, c); // compress + { + CTFCoder coder; + coder.encode(vecIO, c); // compress + } sw.Stop(); LOG(INFO) << "Compressed in " << sw.CpuTime() << " s"; @@ -112,7 +118,10 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector<char> vecIn; sw.Start(); const auto ctfImage = o2::tpc::CTF::getImage(vecIO.data()); - CTFCoder::decode(ctfImage, vecIn); // decompress + { + CTFCoder coder; + coder.decode(ctfImage, vecIn); // decompress + } sw.Stop(); LOG(INFO) << "Decompressed in " << sw.CpuTime() << " s"; // diff --git a/Detectors/CTF/workflow/CMakeLists.txt b/Detectors/CTF/workflow/CMakeLists.txt index 285437837236f..efc519308a0ac 100644 --- a/Detectors/CTF/workflow/CMakeLists.txt +++ b/Detectors/CTF/workflow/CMakeLists.txt @@ -15,9 +15,24 @@ o2_add_library(CTFWorkflow O2::DetectorsCommonDataFormats O2::DataFormatsITSMFT O2::DataFormatsTPC + O2::DataFormatsTOF + O2::DataFormatsFT0 + O2::DataFormatsFV0 + O2::DataFormatsFDD + O2::DataFormatsMID + O2::DataFormatsPHOS + O2::DataFormatsCPV O2::DataFormatsParameters O2::ITSMFTWorkflow O2::TPCWorkflow + O2::FT0Workflow + O2::FV0Workflow + O2::FDDWorkflow + O2::TOFWorkflow + O2::MIDWorkflow + O2::EMCALWorkflow + O2::PHOSWorkflow + O2::CPVWorkflow O2::Algorithm O2::CommonUtils) diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 967013d91b3b1..607d952cdc48e 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -40,6 +40,7 @@ class CTFReaderSpec : public o2::framework::Task private: DetID::mask_t mDets; // detectors std::vector<std::string> mInput; // input files + uint32_t mTFCounter = 0; size_t mNextToProcess = 0; TStopwatch mTimer; }; diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h index 57c40c84bf9d3..c0b95b787f9ad 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h @@ -19,6 +19,13 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" +#include "DetectorsCommonDataFormats/CTFHeader.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "CommonUtils/StringUtils.h" +#include "rANS/rans.h" +#include <vector> +#include <array> #include <TStopwatch.h> namespace o2 @@ -27,15 +34,13 @@ namespace ctf { using DetID = o2::detectors::DetID; +using FTrans = o2::rans::FrequencyTable; class CTFWriterSpec : public o2::framework::Task { public: - CTFWriterSpec(DetID::mask_t dm, uint64_t r) : mDets(dm), mRun(r) - { - mTimer.Stop(); - mTimer.Reset(); - } + CTFWriterSpec() = delete; + CTFWriterSpec(DetID::mask_t dm, uint64_t r = 0, bool doCTF = true, bool doDict = false, bool dictPerDet = false); ~CTFWriterSpec() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -43,13 +48,99 @@ class CTFWriterSpec : public o2::framework::Task bool isPresent(DetID id) const { return mDets[id]; } private: + template <typename C> + void processDet(o2::framework::ProcessingContext& pc, DetID det, CTFHeader& header, TTree* tree); + template <typename C> + void storeDictionary(DetID det, CTFHeader& header); + void storeDictionaries(); + void prepareDictionaryTreeAndFile(DetID det); + void closeDictionaryTreeAndFile(CTFHeader& header); + std::string dictionaryFileName(const std::string& detName = ""); + DetID::mask_t mDets; // detectors + bool mWriteCTF = false; + bool mCreateDict = false; + bool mDictPerDetector = false; + size_t mNTF = 0; + int mSaveDictAfter = -1; // if positive and mWriteCTF==true, save dictionary after each mSaveDictAfter TFs processed uint64_t mRun = 0; + + std::unique_ptr<TFile> mDictFileOut; // file to store dictionary + std::unique_ptr<TTree> mDictTreeOut; // tree to store dictionary + + // For the external dictionary creation we accumulate for each detector the frequency tables of its each block + // After accumulation over multiple TFs we store the dictionaries data in the standard CTF format of this detector, + // i.e. EncodedBlock stored in a tree, BUT with dictionary data only added to each block. + // The metadata of the block (min,max) will be used for the consistency check at the decoding + std::array<std::vector<FTrans>, DetID::nDetectors> mFreqsAccumulation; + std::array<std::vector<o2::ctf::Metadata>, DetID::nDetectors> mFreqsMetaData; + std::array<std::shared_ptr<void>, DetID::nDetectors> mHeaders; + TStopwatch mTimer; }; +// process data of particular detector +template <typename C> +void CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det, CTFHeader& header, TTree* tree) +{ + if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { + return; + } + auto ctfBuffer = pc.inputs().get<gsl::span<o2::ctf::BufferType>>(det.getName()); + const auto ctfImage = C::getImage(ctfBuffer.data()); + ctfImage.print(o2::utils::concat_string(det.getName(), ": ")); + if (mWriteCTF) { + ctfImage.appendToTree(*tree, det.getName()); + header.detectors.set(det); + } + if (mCreateDict) { + if (!mFreqsAccumulation[det].size()) { + mFreqsAccumulation[det].resize(C::getNBlocks()); + mFreqsMetaData[det].resize(C::getNBlocks()); + } + if (!mHeaders[det]) { // store 1st header + mHeaders[det] = ctfImage.cloneHeader(); + } + for (int ib = 0; ib < C::getNBlocks(); 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); + freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + mdSave = o2::ctf::Metadata{0, 0, md.coderType, md.streamSize, md.probabilityBits, md.opt, freq.getMinSymbol(), freq.getMaxSymbol(), (int)freq.size(), 0, 0}; + } + } + } +} + +// store dictionary of a particular detector +template <typename C> +void CTFWriterSpec::storeDictionary(DetID det, CTFHeader& header) +{ + if (!isPresent(det) || !mFreqsAccumulation[det].size()) { + return; + } + prepareDictionaryTreeAndFile(det); + // create vector whose data contains dictionary in CTF format (EncodedBlock) + auto dictBlocks = C::createDictionaryBlocks(mFreqsAccumulation[det], mFreqsMetaData[det]); + auto& h = C::get(dictBlocks.data())->getHeader(); + h = *reinterpret_cast<typename std::remove_reference<decltype(h)>::type*>(mHeaders[det].get()); + C::get(dictBlocks.data())->print(o2::utils::concat_string("Storing dictionary for ", det.getName(), ": ")); + C::get(dictBlocks.data())->appendToTree(*mDictTreeOut.get(), det.getName()); // cast to EncodedBlock + // mFreqsAccumulation[det].clear(); + // mFreqsMetaData[det].clear(); + if (mDictPerDetector) { + header.detectors.reset(); + } + header.detectors.set(det); + if (mDictPerDetector) { + closeDictionaryTreeAndFile(header); + } +} + /// create a processor spec -framework::DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, uint64_t run); +framework::DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, uint64_t run, bool doCTF = true, bool doDict = false, bool dictPerDet = false); } // namespace ctf } // namespace o2 diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 6d351421854cb..740c335622808 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -25,6 +25,14 @@ #include "DetectorsCommonDataFormats/CTFHeader.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" +#include "DataFormatsFT0/CTF.h" +#include "DataFormatsFV0/CTF.h" +#include "DataFormatsFDD/CTF.h" +#include "DataFormatsTOF/CTF.h" +#include "DataFormatsMID/CTF.h" +#include "DataFormatsEMCAL/CTF.h" +#include "DataFormatsPHOS/CTF.h" +#include "DataFormatsCPV/CTF.h" #include "Algorithm/RangeTokenizer.h" using namespace o2::framework; @@ -94,6 +102,7 @@ void CTFReaderSpec::run(ProcessingContext& pc) throw std::runtime_error(o2::utils::concat_string("failed to find output message header for ", label)); } hd->firstTForbit = ctfHeader.firstTForbit; + hd->tfCounter = mTFCounter; }; // send CTF Header @@ -124,6 +133,62 @@ void CTFReaderSpec::run(ProcessingContext& pc) setFirstTFOrbit(det.getName()); } + det = DetID::FT0; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::ft0::CTF)); + o2::ft0::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::FV0; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::fv0::CTF)); + o2::fv0::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::FDD; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::fdd::CTF)); + o2::fdd::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::TOF; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::tof::CTF)); + o2::tof::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::MID; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::mid::CTF)); + o2::mid::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::EMC; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::emcal::CTF)); + o2::emcal::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::PHS; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::phos::CTF)); + o2::phos::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + + det = DetID::CPV; + if (detsTF[det]) { + auto& bufVec = pc.outputs().make<std::vector<o2::ctf::BufferType>>({det.getName()}, sizeof(o2::cpv::CTF)); + o2::cpv::CTF::readFromTree(bufVec, *(tree.get()), det.getName()); + setFirstTFOrbit(det.getName()); + } + mTimer.Stop(); LOG(INFO) << "Read CTF " << inputFile << " in " << mTimer.CpuTime() - cput << " s"; @@ -133,6 +198,7 @@ void CTFReaderSpec::run(ProcessingContext& pc) LOGF(INFO, "CTF reading total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } + mTFCounter++; } ///_______________________________________ diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index eb2100e1f6cf3..dafa122b38d41 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -13,18 +13,23 @@ #include <vector> #include <TFile.h> #include <TTree.h> +#include <TSystem.h> #include "Framework/Logger.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/InputSpec.h" -#include "CommonUtils/StringUtils.h" #include "CTFWorkflow/CTFWriterSpec.h" -#include "DetectorsCommonDataFormats/EncodedBlocks.h" -#include "DetectorsCommonDataFormats/CTFHeader.h" -#include "DetectorsCommonDataFormats/NameConf.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" +#include "DataFormatsFT0/CTF.h" +#include "DataFormatsFV0/CTF.h" +#include "DataFormatsFDD/CTF.h" +#include "DataFormatsTOF/CTF.h" +#include "DataFormatsMID/CTF.h" +#include "DataFormatsEMCAL/CTF.h" +#include "DataFormatsPHOS/CTF.h" +#include "DataFormatsCPV/CTF.h" using namespace o2::framework; @@ -47,84 +52,168 @@ void appendToTree(TTree& tree, const std::string brname, T& ptr) br->ResetAddress(); } +CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, uint64_t r, bool doCTF, bool doDict, bool dictPerDet) + : mDets(dm), mRun(r), mWriteCTF(doCTF), mCreateDict(doDict), mDictPerDetector(dictPerDet) +{ + mTimer.Stop(); + mTimer.Reset(); + + if (doDict) { // make sure that there is no local dictonary + for (int id = 0; id < DetID::nDetectors; id++) { + DetID det(id); + if (isPresent(det)) { + auto dictName = dictionaryFileName(det.getName()); + if (gSystem->AccessPathName(dictName.c_str()) == 0) { + throw std::runtime_error(o2::utils::concat_string("CTF dictionary creation is requested but ", dictName, " already exists, remove it!")); + } + if (!mDictPerDetector) { + break; // no point in checking further + } + } + } + } +} + void CTFWriterSpec::init(InitContext& ic) { + mSaveDictAfter = ic.options().get<int>("save-dict-after"); } void CTFWriterSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - auto tfOrb = DataRefUtils::getHeader<o2::header::DataHeader*>(pc.inputs().getByPos(0))->firstTForbit; - TFile flOut(o2::base::NameConf::getCTFFileName(tfOrb).c_str(), "recreate"); - TTree ctfTree(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + + std::unique_ptr<TFile> fileOut; + std::unique_ptr<TTree> treeOut; + if (mWriteCTF) { + // fileOut.reset(TFile::Open(o2::base::NameConf::getCTFFileName(tfOrb).c_str(), "recreate")); + // RS Until the DPL will propagate the firstTForbit, we will use simple counter in CTF file name to avoid overwriting in case of multiple TFs + fileOut.reset(TFile::Open(o2::base::NameConf::getCTFFileName(mNTF).c_str(), "recreate")); + treeOut = std::make_unique<TTree>(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); + } // create header CTFHeader header{mRun, tfOrb}; - DetID det; + processDet<o2::itsmft::CTF>(pc, DetID::ITS, header, treeOut.get()); + processDet<o2::itsmft::CTF>(pc, DetID::MFT, header, treeOut.get()); + processDet<o2::tpc::CTF>(pc, DetID::TPC, header, treeOut.get()); + processDet<o2::tof::CTF>(pc, DetID::TOF, header, treeOut.get()); + processDet<o2::ft0::CTF>(pc, DetID::FT0, header, treeOut.get()); + processDet<o2::fv0::CTF>(pc, DetID::FV0, header, treeOut.get()); + processDet<o2::fdd::CTF>(pc, DetID::FDD, header, treeOut.get()); + processDet<o2::mid::CTF>(pc, DetID::MID, header, treeOut.get()); + processDet<o2::emcal::CTF>(pc, DetID::EMC, header, treeOut.get()); + processDet<o2::phos::CTF>(pc, DetID::PHS, header, treeOut.get()); + processDet<o2::cpv::CTF>(pc, DetID::CPV, header, treeOut.get()); - det = DetID::ITS; - if (isPresent(det) && pc.inputs().isValid(det.getName())) { - auto ctfBuffer = pc.inputs().get<gsl::span<o2::ctf::BufferType>>(det.getName()); - const auto ctfImage = o2::itsmft::CTF::getImage(ctfBuffer.data()); - LOG(INFO) << "CTF for " << det.getName(); - ctfImage.print(); - ctfImage.appendToTree(ctfTree, det.getName()); - header.detectors.set(det); - } + mTimer.Stop(); - det = DetID::MFT; - if (isPresent(det) && pc.inputs().isValid(det.getName())) { - auto ctfBuffer = pc.inputs().get<gsl::span<o2::ctf::BufferType>>(det.getName()); - const auto ctfImage = o2::itsmft::CTF::getImage(ctfBuffer.data()); - LOG(INFO) << "CTF for " << det.getName(); - ctfImage.print(); - ctfImage.appendToTree(ctfTree, det.getName()); - header.detectors.set(det); + if (mWriteCTF) { + appendToTree(*treeOut.get(), "CTFHeader", header); + treeOut->SetEntries(1); + treeOut->Write(); + treeOut.reset(); + fileOut->Close(); + LOG(INFO) << "TF#" << mNTF << ": wrote " << fileOut->GetName() << " with CTF{" << header << "} in " << mTimer.CpuTime() - cput << " s"; + } else { + LOG(INFO) << "TF#" << mNTF << " CTF writing is disabled"; + } + mNTF++; + if (mCreateDict && mSaveDictAfter > 0 && (mNTF % mSaveDictAfter) == 0) { + storeDictionaries(); } +} - det = DetID::TPC; - if (isPresent(det) && pc.inputs().isValid(det.getName())) { - auto ctfBuffer = pc.inputs().get<gsl::span<o2::ctf::BufferType>>(det.getName()); - const auto ctfImage = o2::tpc::CTF::getImage(ctfBuffer.data()); - LOG(INFO) << "CTF for " << det.getName(); - ctfImage.print(); - ctfImage.appendToTree(ctfTree, det.getName()); - header.detectors.set(det); +void CTFWriterSpec::endOfStream(EndOfStreamContext& ec) +{ + + if (mCreateDict) { + storeDictionaries(); } - appendToTree(ctfTree, "CTFHeader", header); + LOGF(INFO, "CTF writing total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +void CTFWriterSpec::prepareDictionaryTreeAndFile(DetID det) +{ + if (mDictPerDetector) { + if (mDictTreeOut) { + mDictTreeOut->SetEntries(1); + mDictTreeOut->Write(); + mDictTreeOut.reset(); + mDictFileOut.reset(); + } + } + if (!mDictTreeOut) { + mDictFileOut.reset(TFile::Open(dictionaryFileName(det.getName()).c_str(), "recreate")); + mDictTreeOut = std::make_unique<TTree>(std::string(o2::base::NameConf::CTFDICT).c_str(), "O2 CTF dictionary"); + } +} - ctfTree.SetEntries(1); - ctfTree.Write(); - flOut.Close(); +std::string CTFWriterSpec::dictionaryFileName(const std::string& detName) +{ + if (mDictPerDetector) { + if (detName.empty()) { + throw std::runtime_error("Per-detector dictionary files are requested but detector name is not provided"); + } + return o2::utils::concat_string(detName, "_", o2::base::NameConf::CTFDICT, ".root"); + } else { + return o2::utils::concat_string(o2::base::NameConf::CTFDICT, ".root"); + } +} - mTimer.Stop(); - LOG(INFO) << "Wrote " << flOut.GetName() << " with CTF{" << header << "} in " << mTimer.CpuTime() - cput << " s"; +void CTFWriterSpec::storeDictionaries() +{ + CTFHeader header{mRun, uint32_t(mNTF)}; + storeDictionary<o2::itsmft::CTF>(DetID::ITS, header); + storeDictionary<o2::itsmft::CTF>(DetID::MFT, header); + storeDictionary<o2::tpc::CTF>(DetID::TPC, header); + storeDictionary<o2::tof::CTF>(DetID::TOF, header); + storeDictionary<o2::ft0::CTF>(DetID::FT0, header); + storeDictionary<o2::fv0::CTF>(DetID::FV0, header); + storeDictionary<o2::fdd::CTF>(DetID::FDD, header); + storeDictionary<o2::mid::CTF>(DetID::MID, header); + storeDictionary<o2::emcal::CTF>(DetID::EMC, header); + storeDictionary<o2::phos::CTF>(DetID::PHS, header); + storeDictionary<o2::cpv::CTF>(DetID::CPV, header); + // close remnants + if (mDictTreeOut) { + closeDictionaryTreeAndFile(header); + } + LOG(INFO) << "Saved CTF dictionary after " << mNTF << " TFs processed"; } -void CTFWriterSpec::endOfStream(EndOfStreamContext& ec) +void CTFWriterSpec::closeDictionaryTreeAndFile(CTFHeader& header) { - LOGF(INFO, "CTF writing total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + if (mDictTreeOut) { + appendToTree(*mDictTreeOut.get(), "CTFHeader", header); + mDictTreeOut->SetEntries(1); + mDictTreeOut->Write(mDictTreeOut->GetName(), TObject::kSingleKey); + mDictTreeOut.reset(); + mDictFileOut.reset(); + } } -DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, uint64_t run) +DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, uint64_t run, bool doCTF, bool doDict, bool dictPerDet) { std::vector<InputSpec> inputs; + LOG(INFO) << "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); + LOG(INFO) << "Det " << DetID::getName(id) << " added"; } } return DataProcessorSpec{ "ctf-writer", inputs, Outputs{}, - AlgorithmSpec{adaptFromTask<CTFWriterSpec>(dets, run)}, - Options{}}; + AlgorithmSpec{adaptFromTask<CTFWriterSpec>(dets, run, doCTF, doDict, dictPerDet)}, + Options{{"save-dict-after", VariantType::Int, -1, {"In dictionary generation mode save it dictionary after certain number of TFs processed"}}}}; } } // namespace ctf diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index 997a1d94f568d..14c552470b370 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -22,6 +22,14 @@ // Specific detectors specs #include "ITSMFTWorkflow/EntropyDecoderSpec.h" #include "TPCWorkflow/EntropyDecoderSpec.h" +#include "FT0Workflow/EntropyDecoderSpec.h" +#include "FV0Workflow/EntropyDecoderSpec.h" +#include "FDDWorkflow/EntropyDecoderSpec.h" +#include "TOFWorkflowUtils/EntropyDecoderSpec.h" +#include "MIDWorkflow/EntropyDecoderSpec.h" +#include "EMCALWorkflow/EntropyDecoderSpec.h" +#include "PHOSWorkflow/EntropyDecoderSpec.h" +#include "CPVWorkflow/EntropyDecoderSpec.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -44,7 +52,9 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { DetID::mask_t dets; - std::string inpNames = configcontext.options().get<std::string>("ctf-input"); + WorkflowSpec specs; + + std::string inpNames; if (!configcontext.helpOnCommandLine()) { dets.set(); // by default read all auto mskOnly = DetID::getMask(configcontext.options().get<std::string>("onlyDet")); @@ -54,11 +64,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } else { dets ^= mskSkip; } - } else if (inpNames.empty()) { - throw std::runtime_error("--ctf-input <file,...> is not provided"); + if ((inpNames = configcontext.options().get<std::string>("ctf-input")).empty()) { + throw std::runtime_error("--ctf-input <file,...> is not provided"); + } } - WorkflowSpec specs; specs.push_back(o2::ctf::getCTFReaderSpec(dets, inpNames)); // add decodors for all allowed detectors. if (dets[DetID::ITS]) { @@ -70,6 +80,30 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (dets[DetID::TPC]) { specs.push_back(o2::tpc::getEntropyDecoderSpec()); } + if (dets[DetID::TOF]) { + specs.push_back(o2::tof::getEntropyDecoderSpec()); + } + if (dets[DetID::FT0]) { + specs.push_back(o2::ft0::getEntropyDecoderSpec()); + } + if (dets[DetID::FV0]) { + specs.push_back(o2::fv0::getEntropyDecoderSpec()); + } + if (dets[DetID::FDD]) { + specs.push_back(o2::fdd::getEntropyDecoderSpec()); + } + if (dets[DetID::MID]) { + specs.push_back(o2::mid::getEntropyDecoderSpec()); + } + if (dets[DetID::EMC]) { + specs.push_back(o2::emcal::getEntropyDecoderSpec()); + } + if (dets[DetID::PHS]) { + specs.push_back(o2::phos::getEntropyDecoderSpec()); + } + if (dets[DetID::CPV]) { + specs.push_back(o2::cpv::getEntropyDecoderSpec()); + } 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 b77dbfb4bf63b..1ff74331c51a2 100644 --- a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx @@ -19,9 +19,6 @@ #include "DataFormatsParameters/GRPObject.h" #include "DetectorsCommonDataFormats/DetID.h" -// Specific detectors specs -#include "ITSMFTWorkflow/EntropyEncoderSpec.h" - using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -33,6 +30,7 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, std::string{DetID::NONE}, {"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{"grpfile", VariantType::String, o2::base::NameConf::getGRPFileName(), {"name of the grp file"}}); + options.push_back(ConfigParamSpec{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}); std::swap(workflowOptions, options); } @@ -43,11 +41,28 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { DetID::mask_t dets; long run = 0; + bool doCTF = true, doDict = false; if (!configcontext.helpOnCommandLine()) { std::unique_ptr<o2::parameters::GRPObject> grp(o2::parameters::GRPObject::loadFrom(configcontext.options().get<std::string>("grpfile"))); dets = grp->getDetsReadOut(configcontext.options().get<std::string>("onlyDet"), configcontext.options().get<std::string>("skipDet")); + auto outmode = configcontext.options().get<std::string>("output-type"); + if (outmode == "ctf") { + doCTF = true; + doDict = false; + } else if (outmode == "dict") { + doCTF = false; + doDict = true; + } else if (outmode == "both") { + doCTF = true; + doDict = true; + } else if (outmode == "none") { + doCTF = false; + doDict = false; + } else { + throw std::invalid_argument("Invalid output-type"); + } run = grp->getRun(); } - WorkflowSpec specs{o2::ctf::getCTFWriterSpec(dets, run)}; + WorkflowSpec specs{o2::ctf::getCTFWriterSpec(dets, run, doCTF, doDict)}; return std::move(specs); } diff --git a/Detectors/Calibration/CMakeLists.txt b/Detectors/Calibration/CMakeLists.txt index 1bf25fbaa0173..795e51ca34a5d 100644 --- a/Detectors/Calibration/CMakeLists.txt +++ b/Detectors/Calibration/CMakeLists.txt @@ -8,16 +8,30 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -o2_add_header_only_library(DetectorsCalibration - INTERFACE_LINK_LIBRARIES O2::Headers - O2::CCDB - O2::CommonUtils - ms_gsl::ms_gsl) +o2_add_library(DetectorsCalibration + SOURCES src/TimeSlot.cxx + src/TimeSlotCalibration.cxx + src/Utils.cxx + src/MeanVertexData.cxx + src/MeanVertexCalibrator.cxx + src/MeanVertexParams.cxx + PUBLIC_LINK_LIBRARIES O2::Headers + O2::CCDB + O2::CommonUtils + ROOT::Minuit + ms_gsl::ms_gsl + O2::ReconstructionDataFormats + O2::SimConfig + O2::DataFormatsCalibration) + -#o2_target_root_dictionary(DetectorsCalibration -# HEADERS include/DetectorsCalibration/TimeSlotCalibration.h -# include/DetectorsCalibration/TimeSlot.h -# include/DetectorsCalibration/Utils.h) +o2_target_root_dictionary(DetectorsCalibration + HEADERS include/DetectorsCalibration/TimeSlotCalibration.h + include/DetectorsCalibration/TimeSlot.h + include/DetectorsCalibration/Utils.h + include/DetectorsCalibration/MeanVertexData.h + include/DetectorsCalibration/MeanVertexCalibrator.h + include/DetectorsCalibration/MeanVertexParams.h) o2_add_executable(ccdb-populator-workflow COMPONENT_NAME calibration @@ -27,3 +41,5 @@ o2_add_executable(ccdb-populator-workflow O2::DetectorsCalibration O2::DataFormatsTOF O2::CCDB) + +add_subdirectory(workflow) diff --git a/Detectors/Calibration/include/DetectorsCalibration/MeanVertexCalibrator.h b/Detectors/Calibration/include/DetectorsCalibration/MeanVertexCalibrator.h new file mode 100644 index 0000000000000..d033f35f08024 --- /dev/null +++ b/Detectors/Calibration/include/DetectorsCalibration/MeanVertexCalibrator.h @@ -0,0 +1,102 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MEAN_VERTEX_CALIBRATOR_H_ +#define MEAN_VERTEX_CALIBRATOR_H_ + +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "DetectorsCalibration/MeanVertexData.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "CCDB/CcdbObjectInfo.h" +#include "MathUtils/detail/Bracket.h" +#include <array> +#include <deque> + +namespace o2 +{ +namespace calibration +{ + +class MeanVertexCalibrator final : public o2::calibration::TimeSlotCalibration<o2::dataformats::PrimaryVertex, o2::calibration::MeanVertexData> +{ + using PVertex = o2::dataformats::PrimaryVertex; + using MeanVertexData = o2::calibration::MeanVertexData; + using TFType = uint64_t; + using Slot = o2::calibration::TimeSlot<MeanVertexData>; + using MVObject = o2::dataformats::MeanVertexObject; + using MVObjectVector = std::vector<MVObject>; + using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; + using CcdbObjectInfoVector = std::vector<CcdbObjectInfo>; + + public: + MeanVertexCalibrator(int minEnt = 500, bool useFit = false, int nBinsX = 100, float rangeX = 1.f, + int nBinsY = 100, float rangeY = 1.f, int nBinsZ = 100, float rangeZ = 20.f, + int nSlotsSMA = 5) : mMinEntries(minEnt), mUseFit(useFit), mNBinsX(nBinsX), mRangeX(rangeX), mNBinsY(nBinsY), mRangeY(rangeY), mNBinsZ(nBinsZ), mRangeZ(rangeZ), mSMAslots(nSlotsSMA) + { + mSMAdata.init(useFit, nBinsX, rangeX, nBinsY, rangeY, nBinsZ, rangeZ); + } + + ~MeanVertexCalibrator() final = default; + + bool hasEnoughData(const Slot& slot) const final + { + LOG(INFO) << "container entries = " << slot.getContainer()->entries << ", minEntries = " << mMinEntries; + return slot.getContainer()->entries >= mMinEntries; + } + void initOutput() final; + void finalizeSlot(Slot& slot) final; + Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) final; + + uint64_t getNSlotsSMA() const { return mSMAslots; } + void setNSlotsSMA(uint64_t nslots) { mSMAslots = nslots; } + + void doSimpleMovingAverage(std::deque<float>& dq, float& sma); + void doSimpleMovingAverage(std::deque<MVObject>& dq, MVObject& sma); + + const MVObjectVector& getMeanVertexObjectVector() const { return mMeanVertexVector; } + const CcdbObjectInfoVector& getMeanVertexObjectInfoVector() const { return mInfoVector; } + CcdbObjectInfoVector& getMeanVertexObjectInfoVector() { return mInfoVector; } + + private: + int mMinEntries = 0; + int mNBinsX = 0; + float mRangeX = 0.; + int mNBinsY = 0; + float mRangeY = 0.; + int mNBinsZ = 0; + float mRangeZ = 0.; + bool mUseFit = false; + uint64_t mSMAslots = 5; + CcdbObjectInfoVector mInfoVector; // vector of CCDB Infos , each element is filled with the CCDB description + // of the accompanying LHCPhase + MVObjectVector mMeanVertexVector; // vector of Mean Vertex Objects, 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. It is the simple + // moving average + std::deque<MVObject> mTmpMVobjDq; // This is the deque of MeanVertex objecs that will be used for the + // simple moving average + MVObject mSMAMVobj; // object containing the Simple Moving Average to be put to CCDB + std::deque<TFType> mTmpMVobjDqTimeStart; // This is the deque of MeanVertex objecs that will be used for the + // simple moving average, start time of used TFs + std::deque<o2::math_utils::detail::Bracket<TFType>> mTmpMVobjDqTime; // This is the deque for the start and end time of the + // slots used for the SMA + std::deque<MeanVertexData> mTmpMVdataDq; // This is the vector of Mean Vertex data to be used for the simple + // moving average + MeanVertexData mSMAdata; // This is to do the SMA when we keep the histos + + ClassDefOverride(MeanVertexCalibrator, 1); +}; + +} // end namespace calibration +} // end namespace o2 + +#endif /* TOF_LHCPHASE_CALIBRATION_H_ */ diff --git a/Detectors/Calibration/include/DetectorsCalibration/MeanVertexData.h b/Detectors/Calibration/include/DetectorsCalibration/MeanVertexData.h new file mode 100644 index 0000000000000..42f5f67920143 --- /dev/null +++ b/Detectors/Calibration/include/DetectorsCalibration/MeanVertexData.h @@ -0,0 +1,113 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MEAN_VERTEX_DATA_H_ +#define MEAN_VERTEX_DATA_H_ + +#include "DetectorsCalibration/TimeSlot.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include <array> +#include <deque> +#include <gsl/span> + +namespace o2 +{ +namespace calibration +{ + +struct MeanVertexData { + + using PVertex = o2::dataformats::PrimaryVertex; + float rangeX = 1.f; + float rangeY = 1.f; + float rangeZ = 20.f; + int nbinsX = 100; + int nbinsY = 100; + int nbinsZ = 100; + float v2BinX = nbinsX / (2 * rangeX); + float v2BinY = nbinsY / (2 * rangeY); + float v2BinZ = nbinsZ / (2 * rangeZ); + int entries = 0; + std::vector<float> histoX{0}; + std::vector<float> histoY{0}; + std::vector<float> histoZ{0}; + bool useFit = false; + + MeanVertexData(); + + ~MeanVertexData() + { + useFit = false; + nbinsX = 1000; + rangeX = 10.f; + nbinsY = 1000; + rangeY = 10.f; + nbinsZ = 1000; + rangeZ = 10.f; + v2BinX = 0.f; + v2BinY = 0.f; + v2BinZ = 0.f; + histoX.clear(); + histoY.clear(); + histoZ.clear(); + } + + MeanVertexData(bool buseFit, int nbX, float rX, int nbY, float rY, int nbZ, float rZ) : useFit(buseFit), nbinsX(nbX), rangeX(rX), v2BinX(0), nbinsY(nbY), rangeY(rY), v2BinY(0), nbinsZ(nbZ), rangeZ(rZ), v2BinZ(0) + { + if (rX <= 0. || nbX < 1 || rY <= 0. || nbY < 1 || rZ <= 0. || nbZ < 1) { + throw std::runtime_error("Wrong initialization of the histogram"); + } + v2BinX = nbinsX / (2 * rangeX); + v2BinY = nbinsY / (2 * rangeY); + v2BinZ = nbinsZ / (2 * rangeZ); + histoX.resize(nbinsX, 0.); + histoY.resize(nbinsY, 0.); + histoZ.resize(nbinsZ, 0.); + } + + MeanVertexData(MeanVertexData&& other) = default; + MeanVertexData(const MeanVertexData& other) = default; + MeanVertexData& operator=(MeanVertexData& other) = default; + MeanVertexData& operator=(MeanVertexData&& other) = default; + + //_____________________________________________ + void init(bool buseFit, int nbX, float rX, int nbY, float rY, int nbZ, float rZ) + { + + useFit = buseFit; + nbinsX = nbX; + rangeX = rX; + nbinsY = nbY; + rangeY = rY; + nbinsZ = nbZ; + rangeZ = rZ; + v2BinX = nbinsX / (2 * rangeX); + v2BinY = nbinsY / (2 * rangeY); + v2BinZ = nbinsZ / (2 * rangeZ); + histoX.resize(nbinsX, 0.); + histoY.resize(nbinsY, 0.); + histoZ.resize(nbinsZ, 0.); + } + + //_____________________________________________ + + size_t getEntries() const { return entries; } + void print() const; + void fill(const gsl::span<const PVertex> data); + void merge(const MeanVertexData* prev); + void subtract(const MeanVertexData* prev); + + ClassDefNV(MeanVertexData, 1); +}; + +} // end namespace calibration +} // end namespace o2 + +#endif /* MEAN_VERTEX_DATA_H_ */ diff --git a/Detectors/Calibration/include/DetectorsCalibration/MeanVertexParams.h b/Detectors/Calibration/include/DetectorsCalibration/MeanVertexParams.h new file mode 100644 index 0000000000000..11beb9e1435d3 --- /dev/null +++ b/Detectors/Calibration/include/DetectorsCalibration/MeanVertexParams.h @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MEANVERTEX_PARAMS_H +#define ALICEO2_MEANVERTEX_PARAMS_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace calibration +{ + +// There are configurable params for TPC-ITS matching +struct MeanVertexParams : public o2::conf::ConfigurableParamHelper<MeanVertexParams> { + + int minEntries = 100; + int nbinsX = 100; + float rangeX = 1.f; + int nbinsY = 100; + float rangeY = 1.f; + int nbinsZ = 100; + float rangeZ = 20.f; + int nSlots4SMA = 5; + bool useFit = false; + int tfPerSlot = 5; + int maxTFdelay = 3; + + O2ParamDef(MeanVertexParams, "MeanVertexCalib"); +}; + +} // namespace calibration +} // end namespace o2 + +#endif diff --git a/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h b/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h index 5c3aa126f637a..a1ed3375ad166 100644 --- a/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h +++ b/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h @@ -31,7 +31,9 @@ class TimeSlotCalibration TimeSlotCalibration() = default; virtual ~TimeSlotCalibration() = default; uint32_t getMaxSlotsDelay() const { return mMaxSlotsDelay; } - void setMaxSlotsDelay(uint32_t v) { mMaxSlotsDelay = v < 1 ? 1 : v; } + void setMaxSlotsDelay(uint32_t v) { mMaxSlotsDelay = v; } + //void setMaxSlotsDelay(uint32_t v) { (mSlotLength == 1 && mMaxSlotsDelay == 0) ? mMaxSlotsDelay = 0 : mMaxSlotsDelay = v < 1 ? 1 : v; } + //void setMaxSlotsDelay(uint32_t v) { mSlotLength == 1 ? mMaxSlotsDelay = 0 : mMaxSlotsDelay = v < 1 ? 1 : v; } uint32_t getSlotLength() const { return mSlotLength; } void setSlotLength(uint32_t v) { mSlotLength = v < 1 ? 1 : v; } @@ -50,6 +52,7 @@ class TimeSlotCalibration virtual bool process(TFType tf, const gsl::span<const Input> data); virtual void checkSlotsToFinalize(TFType tf, int maxDelay = 0); + virtual void finalizeOldestSlot(); // Methods to be implemented by the derived user class @@ -88,7 +91,7 @@ bool TimeSlotCalibration<Input, Container>::process(TFType tf, const gsl::span<c if (!mUpdateAtTheEndOfRunOnly) { int maxDelay = mMaxSlotsDelay * mSlotLength; // if (tf<mLastClosedTF || (!mSlots.empty() && getSlot(0).getTFStart() > tf + maxDelay)) { // ignore TF - if (tf < mLastClosedTF || (!mSlots.empty() && getLastSlot().getTFStart() > tf + maxDelay)) { // ignore TF + if (maxDelay != 0 && (tf < mLastClosedTF || (!mSlots.empty() && getLastSlot().getTFStart() > tf + maxDelay))) { // ignore TF LOG(INFO) << "Ignoring TF " << tf; return false; } @@ -111,7 +114,7 @@ void TimeSlotCalibration<Input, Container>::checkSlotsToFinalize(TFType tf, int // Check which slots can be finalized, provided the newly arrived TF is tf // check if some slots are done for (auto slot = mSlots.begin(); slot != mSlots.end(); slot++) { - if ((slot->getTFEnd() + maxDelay) < tf) { + if (maxDelay == 0 || (slot->getTFEnd() + maxDelay) < tf) { if (hasEnoughData(*slot)) { finalizeSlot(*slot); // will be removed after finalization } else if ((slot + 1) != mSlots.end()) { @@ -134,6 +137,20 @@ void TimeSlotCalibration<Input, Container>::checkSlotsToFinalize(TFType tf, int } } +//_________________________________________________ +template <typename Input, typename Container> +void TimeSlotCalibration<Input, Container>::finalizeOldestSlot() +{ + // Enforce finalization and removal of the oldest slot + if (mSlots.empty()) { + LOG(WARNING) << "There are no slots defined"; + return; + } + finalizeSlot(mSlots.front()); + mLastClosedTF = mSlots.front().getTFEnd() + 1; // do not accept any TF below this + mSlots.erase(mSlots.begin()); +} + //________________________________________ template <typename Input, typename Container> inline TFType TimeSlotCalibration<Input, Container>::tf2SlotMin(TFType tf) const @@ -141,8 +158,9 @@ inline TFType TimeSlotCalibration<Input, Container>::tf2SlotMin(TFType tf) const if (tf < mFirstTF) { throw std::runtime_error("invalide TF"); } - if (mUpdateAtTheEndOfRunOnly) + if (mUpdateAtTheEndOfRunOnly) { return mFirstTF; + } return TFType((tf - mFirstTF) / mSlotLength) * mSlotLength + mFirstTF; } @@ -152,9 +170,9 @@ TimeSlot<Container>& TimeSlotCalibration<Input, Container>::getSlotForTF(TFType { if (mUpdateAtTheEndOfRunOnly) { - if (!mSlots.empty() && mSlots.back().getTFEnd() < tf) + if (!mSlots.empty() && mSlots.back().getTFEnd() < tf) { mSlots.back().setTFEnd(tf); - else if (mSlots.empty()) { + } else if (mSlots.empty()) { emplaceNewSlot(true, mFirstTF, tf); } return mSlots.back(); diff --git a/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h b/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h new file mode 100644 index 0000000000000..337be0f3bbb5c --- /dev/null +++ b/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h @@ -0,0 +1,24 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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::calibration::MeanVertexData + ; +#pragma link C++ class o2::calibration::TimeSlot < o2::calibration::MeanVertexData> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::dataformats::PrimaryVertex, o2::calibration::MeanVertexData> + ; +#pragma link C++ class o2::calibration::MeanVertexCalibrator + ; +#pragma link C++ class o2::calibration::MeanVertexParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::calibration::MeanVertexParams> + ; + +#endif diff --git a/Detectors/Calibration/src/MeanVertexCalibrator.cxx b/Detectors/Calibration/src/MeanVertexCalibrator.cxx new file mode 100644 index 0000000000000..1141e872e4b3f --- /dev/null +++ b/Detectors/Calibration/src/MeanVertexCalibrator.cxx @@ -0,0 +1,214 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsCalibration/MeanVertexCalibrator.h" +#include "Framework/Logger.h" +#include "MathUtils/fit.h" +#include "CommonUtils/MemFileHelper.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsCalibration/Utils.h" + +namespace o2 +{ +namespace calibration +{ + +using Slot = o2::calibration::TimeSlot<o2::calibration::MeanVertexData>; +using o2::math_utils::fitGaus; +using clbUtils = o2::calibration::Utils; +using MeanVertexObject = o2::dataformats::MeanVertexObject; + +void MeanVertexCalibrator::initOutput() +{ + // Here we initialize the vector of our output objects + mMeanVertexVector.clear(); + mInfoVector.clear(); + return; +} + +//_____________________________________________ +void MeanVertexCalibrator::finalizeSlot(Slot& slot) +{ + // Extract results for the single slot + o2::calibration::MeanVertexData* c = slot.getContainer(); + LOG(INFO) << "Finalize slot " << slot.getTFStart() << " <= TF <= " << slot.getTFEnd() << " with " + << c->getEntries() << " entries"; + mTmpMVobjDqTime.emplace_back(slot.getTFStart(), slot.getTFEnd()); + + if (mUseFit) { + MeanVertexObject mvo; + // x coordinate + std::vector<float> fitValues; + float* array = &c->histoX[0]; + double fitres = fitGaus(c->nbinsX, array, -(c->rangeX), c->rangeX, fitValues); + if (fitres >= 0) { + LOG(INFO) << "X: Fit result (of single Slot) => " << fitres << ". Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; + } else { + LOG(ERROR) << "X: Fit failed with result = " << fitres; + } + mvo.setX(fitValues[1]); + mvo.setSigmaX(fitValues[2]); + + // y coordinate + array = &c->histoY[0]; + fitres = fitGaus(c->nbinsY, array, -(c->rangeY), c->rangeY, fitValues); + if (fitres >= 0) { + LOG(INFO) << "Y: Fit result (of single Slot) => " << fitres << ". Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; + } else { + LOG(ERROR) << "Y: Fit failed with result = " << fitres; + } + mvo.setY(fitValues[1]); + mvo.setSigmaY(fitValues[2]); + + // z coordinate + array = &c->histoZ[0]; + fitres = fitGaus(c->nbinsZ, array, -(c->rangeZ), c->rangeZ, fitValues); + if (fitres >= 0) { + LOG(INFO) << "Z: Fit result (of single Slot) => " << fitres << ". Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; + } else { + LOG(ERROR) << "Z: Fit failed with result = " << fitres; + } + mvo.setZ(fitValues[1]); + mvo.setSigmaZ(fitValues[2]); + + // now we add the object to the deque + mTmpMVobjDq.push_back(std::move(mvo)); + } else { + mTmpMVdataDq.push_back(std::move(*c)); + mSMAdata.merge(&mTmpMVdataDq.back()); + if (mTmpMVobjDqTime.size() > mSMAslots) { + mSMAdata.subtract(&mTmpMVdataDq.front()); + mTmpMVdataDq.pop_front(); + } + } + + // output object + MeanVertexObject mvo; + + if (mUseFit) { + doSimpleMovingAverage(mTmpMVobjDq, mSMAMVobj); + } else { + // now we need to fit, on the merged data + LOG(DEBUG) << "**** Printing content of SMA MVData object for x coordinate"; + for (int i = 0; i < mSMAdata.nbinsX; i++) { + LOG(DEBUG) << "i = " << i << ", content of histogram = " << mSMAdata.histoX[i]; + } + std::vector<float> fitValues; + float* array = &mSMAdata.histoX[0]; + double fitres = fitGaus(mSMAdata.nbinsX, array, -(mSMAdata.rangeX), mSMAdata.rangeX, fitValues); + if (fitres >= 0) { + LOG(INFO) << "X: Fit result (of merged Slots) => " << fitres << ". Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; + } else { + LOG(ERROR) << "X: Fit failed with result = " << fitres; + } + mSMAMVobj.setX(fitValues[1]); + mSMAMVobj.setSigmaX(fitValues[2]); + + // y coordinate + array = &mSMAdata.histoY[0]; + fitres = fitGaus(mSMAdata.nbinsY, array, -(mSMAdata.rangeY), mSMAdata.rangeY, fitValues); + if (fitres >= 0) { + LOG(INFO) << "Y: Fit result (of merged Slots) => " << fitres << ". Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; + } else { + LOG(ERROR) << "Y: Fit failed with result = " << fitres; + } + mSMAMVobj.setY(fitValues[1]); + mSMAMVobj.setSigmaY(fitValues[2]); + + // z coordinate + array = &mSMAdata.histoZ[0]; + fitres = fitGaus(mSMAdata.nbinsZ, array, -(mSMAdata.rangeZ), mSMAdata.rangeZ, fitValues); + if (fitres >= 0) { + LOG(INFO) << "Z: Fit result (of merged Slots) => " << fitres << ". Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; + } else { + LOG(ERROR) << "Z: Fit failed with result = " << fitres; + } + mSMAMVobj.setZ(fitValues[1]); + mSMAMVobj.setSigmaZ(fitValues[2]); + } + + // TODO: the timestamp is now given with the TF index, but it will have + // to become an absolute time. This is true both for the lhc phase object itself + // and the CCDB entry + if (mTmpMVobjDqTime.size() > mSMAslots) { + mTmpMVobjDqTime.pop_front(); + } + TFType startValidity = (mTmpMVobjDqTime.front().getMin() + mTmpMVobjDqTime.back().getMax()) / 2; // will be rounded to uint64_t + LOG(INFO) << "start validity = " << startValidity; + std::map<std::string, std::string> md; + auto clName = o2::utils::MemFileHelper::getClassName(mSMAMVobj); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + mInfoVector.emplace_back("GRP/MeanVertex", clName, flName, md, startValidity, 99999999999999); + mMeanVertexVector.emplace_back(mSMAMVobj); + + slot.print(); +} + +//_____________________________________________ +void MeanVertexCalibrator::doSimpleMovingAverage(std::deque<float>& dq, float& sma) +{ + + // doing simple moving average + + if (dq.size() <= mSMAslots) { + sma = std::accumulate(dq.begin(), dq.end(), 0.0) / dq.size(); + //avg = (avg * (vect.size() - 1) + vect.back()) / vect.size(); + return; + } + + // if the vector has size > mSMAslots, we calculate the SMA, and then we drop 1 element + // (note that it can have a size > mSMAslots only of 1 element!) + sma += (dq[dq.size() - 1] - dq[0]) / mSMAslots; + dq.pop_front(); + + return; +} + +//_____________________________________________ +void MeanVertexCalibrator::doSimpleMovingAverage(std::deque<MVObject>& dq, MVObject& sma) +{ + + // doing simple moving average + + if (dq.size() <= mSMAslots) { + sma.setX((sma.getX() * (dq.size() - 1) + dq.back().getX()) / dq.size()); + sma.setY((sma.getY() * (dq.size() - 1) + dq.back().getY()) / dq.size()); + sma.setZ((sma.getZ() * (dq.size() - 1) + dq.back().getZ()) / dq.size()); + sma.setSigmaX((sma.getSigmaX() * (dq.size() - 1) + dq.back().getSigmaX()) / dq.size()); + sma.setSigmaY((sma.getSigmaY() * (dq.size() - 1) + dq.back().getSigmaY()) / dq.size()); + sma.setSigmaZ((sma.getSigmaZ() * (dq.size() - 1) + dq.back().getSigmaZ()) / dq.size()); + return; + } + + // if the vector has size > mSMAslots, we calculate the SMA, and then we drop 1 element + // (note that it can have a size > mSMAslots only of 1 element!) + sma.setX(sma.getX() + (dq[dq.size() - 1].getX() - dq[0].getX()) / mSMAslots); + sma.setY(sma.getY() + (dq[dq.size() - 1].getY() - dq[0].getY()) / mSMAslots); + sma.setZ(sma.getZ() + (dq[dq.size() - 1].getZ() - dq[0].getZ()) / mSMAslots); + sma.setSigmaX(sma.getSigmaX() + (dq[dq.size() - 1].getSigmaX() - dq[0].getSigmaX()) / mSMAslots); + sma.setSigmaY(sma.getSigmaY() + (dq[dq.size() - 1].getSigmaY() - dq[0].getSigmaY()) / mSMAslots); + sma.setSigmaZ(sma.getSigmaZ() + (dq[dq.size() - 1].getSigmaZ() - dq[0].getSigmaZ()) / mSMAslots); + + dq.pop_front(); + + return; +} + +//_____________________________________________ +Slot& MeanVertexCalibrator::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<MeanVertexData>(mUseFit, mNBinsX, mRangeX, mNBinsY, mRangeY, mNBinsZ, mRangeZ)); + return slot; +} + +} // end namespace calibration +} // end namespace o2 diff --git a/Detectors/Calibration/src/MeanVertexData.cxx b/Detectors/Calibration/src/MeanVertexData.cxx new file mode 100644 index 0000000000000..dbcaf5b7616f8 --- /dev/null +++ b/Detectors/Calibration/src/MeanVertexData.cxx @@ -0,0 +1,115 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsCalibration/MeanVertexData.h" +#include "Framework/Logger.h" +#include "CommonUtils/MemFileHelper.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsCalibration/Utils.h" + +using namespace o2::calibration; + +namespace o2 +{ +namespace calibration +{ + +using Slot = o2::calibration::TimeSlot<o2::calibration::MeanVertexData>; +using PVertex = o2::dataformats::PrimaryVertex; +using clbUtils = o2::calibration::Utils; + +//_____________________________________________ +MeanVertexData::MeanVertexData() +{ + LOG(INFO) << "Default c-tor, not to be used"; +} + +//_____________________________________________ +void MeanVertexData::print() const +{ + LOG(INFO) << entries << " entries"; +} + +//_____________________________________________ +void MeanVertexData::fill(const gsl::span<const PVertex> data) +{ + // fill container + + LOG(INFO) << "input size = " << data.size(); + for (int i = data.size(); i--;) { + // filling the histogram in binned mode + auto x = data[i].getX(); + auto y = data[i].getY(); + auto z = data[i].getZ(); + LOG(DEBUG) << "i = " << i << " --> x = " << x << ", y = " << y << ", z = " << z; + auto dx = x + rangeX; + uint32_t binx = dx < 0 ? 0xffffffff : (x + rangeX) * v2BinX; + auto dy = y + rangeY; + uint32_t biny = dy < 0 ? 0xffffffff : (y + rangeY) * v2BinY; + auto dz = z + rangeZ; + uint32_t binz = dz < 0 ? 0xffffffff : (z + rangeZ) * v2BinZ; + if (binx < nbinsX || biny < nbinsY || binz < nbinsZ) { // accounts also for z<-rangeZ + histoX[binx]++; + histoY[biny]++; + histoZ[binz]++; + entries++; + } + } + + for (int i = 0; i < histoX.size(); i++) { + LOG(DEBUG) << "histoX, bin " << i << ": entries = " << histoX[i]; + } + for (int i = 0; i < histoY.size(); i++) { + LOG(DEBUG) << "histoY, bin " << i << ": entries = " << histoY[i]; + } + for (int i = 0; i < histoZ.size(); i++) { + LOG(DEBUG) << "histoZ, bin " << i << ": entries = " << histoZ[i]; + } +} + +//_____________________________________________ +void MeanVertexData::subtract(const MeanVertexData* prev) +{ + // remove entries from prev + + assert(histoX.size() == prev->histoX.size()); + assert(histoY.size() == prev->histoY.size()); + assert(histoZ.size() == prev->histoZ.size()); + + for (int i = histoX.size(); i--;) { + histoX[i] -= prev->histoX[i]; + } + for (int i = histoY.size(); i--;) { + histoY[i] -= prev->histoY[i]; + } + for (int i = histoZ.size(); i--;) { + histoZ[i] -= prev->histoZ[i]; + } + entries -= prev->entries; +} + +//_____________________________________________ +void MeanVertexData::merge(const MeanVertexData* prev) +{ + // merge data of 2 slots + assert(histoX.size() == prev->histoX.size()); + assert(histoY.size() == prev->histoY.size()); + assert(histoZ.size() == prev->histoZ.size()); + + for (int i = histoX.size(); i--;) { + histoX[i] += prev->histoX[i]; + histoY[i] += prev->histoY[i]; + histoZ[i] += prev->histoZ[i]; + } + entries += prev->entries; +} + +} // end namespace calibration +} // end namespace o2 diff --git a/Detectors/Calibration/src/MeanVertexParams.cxx b/Detectors/Calibration/src/MeanVertexParams.cxx new file mode 100644 index 0000000000000..99a7b8252f959 --- /dev/null +++ b/Detectors/Calibration/src/MeanVertexParams.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MeanVertex.h +/// \brief Configurable params for MeanVertex calibration +/// \author Chiara.Zampolli@cern.ch + +#include "DetectorsCalibration/MeanVertexParams.h" + +O2ParamImpl(o2::calibration::MeanVertexParams); diff --git a/Detectors/Calibration/src/TimeSlot.cxx b/Detectors/Calibration/src/TimeSlot.cxx new file mode 100644 index 0000000000000..af2e495a4bed0 --- /dev/null +++ b/Detectors/Calibration/src/TimeSlot.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsCalibration/TimeSlot.h" diff --git a/Detectors/Calibration/src/TimeSlotCalibration.cxx b/Detectors/Calibration/src/TimeSlotCalibration.cxx new file mode 100644 index 0000000000000..cb8ee39a9a405 --- /dev/null +++ b/Detectors/Calibration/src/TimeSlotCalibration.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsCalibration/TimeSlotCalibration.h" diff --git a/Detectors/Calibration/src/Utils.cxx b/Detectors/Calibration/src/Utils.cxx new file mode 100644 index 0000000000000..5d97b143344a8 --- /dev/null +++ b/Detectors/Calibration/src/Utils.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsCalibration/Utils.h" diff --git a/Detectors/Calibration/workflow/CMakeLists.txt b/Detectors/Calibration/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..7e7375d613a9e --- /dev/null +++ b/Detectors/Calibration/workflow/CMakeLists.txt @@ -0,0 +1,31 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(DetectorsCalibrationWorkflow + SOURCES src/MeanVertexCalibratorSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::Headers + O2::CCDB + O2::CommonUtils + ms_gsl::ms_gsl + O2::ReconstructionDataFormats + O2::DataFormatsCalibration + O2::DetectorsCalibration) + + +o2_add_executable(mean-vertex-calibration-workflow + COMPONENT_NAME calibration + SOURCES src/mean-vertex-calibration-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DetectorsCalibration + O2::DetectorsCalibrationWorkflow + O2::ReconstructionDataFormats + O2::DataFormatsCalibration + O2::CCDB) diff --git a/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h b/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h new file mode 100644 index 0000000000000..3fc11e625e4f2 --- /dev/null +++ b/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MEANVERTEX_CALIB_DEVICE_H +#define O2_MEANVERTEX_CALIB_DEVICE_H + +/// @file MeanVertexCalibratorSpec.h +/// @brief Device to calibrate MeanVertex + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DetectorsCalibration/MeanVertexCalibrator.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace calibration +{ + +class MeanVertexCalibDevice : public Task +{ + public: + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + void sendOutput(DataAllocator& output); + + std::unique_ptr<o2::calibration::MeanVertexCalibrator> mCalibrator; +}; + +} // namespace calibration + +namespace framework +{ +DataProcessorSpec getMeanVertexCalibDeviceSpec(); + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx b/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx new file mode 100644 index 0000000000000..a43f9568496a3 --- /dev/null +++ b/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx @@ -0,0 +1,123 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MeanVertexCalibratorSpec.cxx + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsCalibration/MeanVertexParams.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/CcdbObjectInfo.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace calibration +{ +void MeanVertexCalibDevice::init(InitContext& ic) +{ + + const o2::calibration::MeanVertexParams* params = &o2::calibration::MeanVertexParams::Instance(); + int minEnt = params->minEntries; + int nbX = params->nbinsX; + float rangeX = params->rangeX; + int nbY = params->nbinsY; + float rangeY = params->rangeY; + int nbZ = params->nbinsZ; + float rangeZ = params->rangeZ; + int nSlots4SMA = params->nSlots4SMA; + bool useFit = params->useFit; + int slotL = params->tfPerSlot; + int delay = params->maxTFdelay; + mCalibrator = std::make_unique<o2::calibration::MeanVertexCalibrator>(minEnt, useFit, nbX, rangeX, nbY, rangeY, nbZ, rangeZ, nSlots4SMA); + mCalibrator->setSlotLength(slotL); + mCalibrator->setMaxSlotsDelay(delay); +} + +//_____________________________________________________________ + +void MeanVertexCalibDevice::run(o2::framework::ProcessingContext& pc) +{ + + auto tfcounter = o2::header::get<o2::framework::DataProcessingHeader*>(pc.inputs().get("input").header)->startTime; + auto data = pc.inputs().get<gsl::span<o2::dataformats::PrimaryVertex>>("input"); + LOG(INFO) << "Processing TF " << tfcounter << " with " << data.size() << " tracks"; + mCalibrator->process(tfcounter, data); + sendOutput(pc.outputs()); + const auto& infoVec = mCalibrator->getMeanVertexObjectInfoVector(); + LOG(INFO) << "Created " << infoVec.size() << " objects for TF " << tfcounter; +} + +//_____________________________________________________________ + +void MeanVertexCalibDevice::endOfStream(o2::framework::EndOfStreamContext& ec) +{ + + LOG(INFO) << "Finalizing calibration"; + constexpr uint64_t INFINITE_TF = 0xffffffffffffffff; + mCalibrator->checkSlotsToFinalize(INFINITE_TF); + sendOutput(ec.outputs()); +} + +//_____________________________________________________________ + +void MeanVertexCalibDevice::sendOutput(DataAllocator& output) +{ + + // extract CCDB infos and calibration objects, convert it to TMemFile and send them to the output + // TODO in principle, this routine is generic, can be moved to Utils.h + + using clbUtils = o2::calibration::Utils; + const auto& payloadVec = mCalibrator->getMeanVertexObjectVector(); + auto& infoVec = mCalibrator->getMeanVertexObjectInfoVector(); // use non-const version as we update it + assert(payloadVec.size() == infoVec.size()); + + for (uint32_t i = 0; i < payloadVec.size(); i++) { + auto& w = infoVec[i]; + auto image = o2::ccdb::CcdbApi::createObjectImage(&payloadVec[i], &w); + LOG(INFO) << "Sending object " << w.getPath() << "/" << w.getFileName() << " of size " << image->size() + << " bytes, valid for " << w.getStartValidityTimestamp() << " : " << w.getEndValidityTimestamp(); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, i}, *image.get()); // vector<char> + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, i}, w); // root-serialized + } + if (payloadVec.size()) { + mCalibrator->initOutput(); // reset the outputs once they are already sent + } +} +} // namespace calibration + +namespace framework +{ + +DataProcessorSpec getMeanVertexCalibDeviceSpec() +{ + + using device = o2::calibration::MeanVertexCalibDevice; + using clbUtils = o2::calibration::Utils; + + std::vector<OutputSpec> outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload}); + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); + + return DataProcessorSpec{ + "mean-vertex-calibration", + Inputs{{"input", "GLO", "PVTX"}}, + outputs, + AlgorithmSpec{adaptFromTask<device>()}, + Options{ + {}}}; +} + +} // namespace framework +} // namespace o2 diff --git a/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx b/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx new file mode 100644 index 0000000000000..030a201ef954d --- /dev/null +++ b/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx @@ -0,0 +1,32 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getMeanVertexCalibDeviceSpec()); + return specs; +} diff --git a/Detectors/DCS/CMakeLists.txt b/Detectors/DCS/CMakeLists.txt new file mode 100644 index 0000000000000..83da3d6cdc4bf --- /dev/null +++ b/Detectors/DCS/CMakeLists.txt @@ -0,0 +1,92 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + DetectorsDCS + TARGETVARNAME targetName + SOURCES src/AliasExpander.cxx + src/DCSProcessor.cxx + src/DataPointCompositeObject.cxx + src/DataPointCreator.cxx + src/DataPointGenerator.cxx + src/DataPointIdentifier.cxx + src/DataPointValue.cxx + src/DeliveryType.cxx + src/GenericFunctions.cxx + src/StringUtils.cxx + src/Clock.cxx + PUBLIC_LINK_LIBRARIES O2::Headers O2::CommonUtils O2::CCDB + O2::DetectorsCalibration ms_gsl::ms_gsl) + +o2_target_root_dictionary( + DetectorsDCS + HEADERS include/DetectorsDCS/DataPointCompositeObject.h + include/DetectorsDCS/DataPointIdentifier.h + include/DetectorsDCS/DataPointValue.h + include/DetectorsDCS/DCSProcessor.h) + +o2_add_executable( + data-workflow + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-data-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + +o2_add_executable( + random-data-workflow + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-random-data-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + +o2_add_executable( + proxy + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-proxy.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::DetectorsDCS) + +o2_add_executable( + data-client + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-data-client-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DetectorsDCS) + +o2_add_executable( + sim-workflow + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-sim-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + +if(OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() + +if(BUILD_TESTING) + o2_add_test( + data-point-types + SOURCES test/testDataPointTypes.cxx + COMPONENT_NAME dcs + LABELS "dcs" + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + o2_add_test( + alias-expander + SOURCES test/testAliasExpander.cxx + COMPONENT_NAME dcs + LABELS "dcs" + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + o2_add_test( + data-point-generator + SOURCES test/testDataPointGenerator.cxx + COMPONENT_NAME dcs + LABELS "dcs" + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) +endif() diff --git a/Detectors/DCS/README.md b/Detectors/DCS/README.md new file mode 100644 index 0000000000000..16a2044e44a3f --- /dev/null +++ b/Detectors/DCS/README.md @@ -0,0 +1,24 @@ +<!-- doxy +\page refDetectorsDCS DCS +/doxy --> + +# Export of DCS to CCDB + +To be written + +# Generating DCS aliases + +For test purposes, DCS aliases can be generated making use of the helper +function `generateRandomDataPoints`. For example : + +```c++ +#include "DetectorsDCS/DataPointGenerator.h" +std::vector<std::string> patterns = { "DET/HV/Crate[0.9]/Channel[00.42]/vMon" }; +auto dps = o2::dcs::generateRandomDataPoints(patterns,0.0,1200.0); +``` + +would generate 420 data points. + +<!-- doxy +* \subpage refDetectorsDCStestWorkflow +/doxy --> diff --git a/Detectors/DCS/include/DetectorsDCS/AliasExpander.h b/Detectors/DCS/include/DetectorsDCS/AliasExpander.h new file mode 100644 index 0000000000000..ac2ea2486e92b --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/AliasExpander.h @@ -0,0 +1,69 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_ALIAS_EXPANDER_H +#define O2_DCS_ALIAS_EXPANDER_H + +#include <vector> +#include <string> + +namespace o2::dcs +{ +/** + * expandAlias converts a single pattern into a list of strings. + * + * @param pattern a pattern is made of a number of "XX[YY]" blocks (at least one) + * + * where : + * - XX is any text + * - YY describes either a integral range or a textual list + * + * An integral range is [a..b] where the formatting of the biggest of the + * two integers a and b dictates, by default, the formatting of the output + * alias. For instance [0..3] is expanded to the set 0,1,2,3 while [00..03] + * is expanded to 00,01,02,03. If you want more control on the formatting, + * you can use a python/fmt format {} e.g. [0..15{:d}] would yields 0,1, + * 2,...,14,15 simply (no 0 filling). + * + * A textual list is simply a list of values separated by commas, + * e.g. "vMon,iMon" + * + * @returns a vector of strings containing all the possible expansions of + * the pattern. That vector is not guaranteed to be sorted. + * + * For example, pattern=DET[A,B]/Channel[000,002]/[iMon,vMon] yields : + * + * - DETA/Channel000/iMon + * - DETA/Channel001/iMon + * - DETA/Channel002/iMon + * - DETA/Channel000/vMon + * - DETA/Channel001/vMon + * - DETA/Channel002/vMon + * - DETB/Channel000/iMon + * - DETB/Channel001/iMon + * - DETB/Channel002/iMon + * - DETB/Channel000/vMon + * - DETB/Channel001/vMon + * - DETB/Channel002/vMon + +*/ +std::vector<std::string> expandAlias(const std::string& pattern); + +/** expandAliases converts a list of patterns into a list of strings. + * + * each input pattern is treated by expandAlias() + * + * @returns a _sorted_ vector of strings containing all the possible + * expansions of the pattern. + */ +std::vector<std::string> expandAliases(const std::vector<std::string>& patternedAliases); +} // namespace o2::dcs + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/Clock.h b/Detectors/DCS/include/DetectorsDCS/Clock.h new file mode 100644 index 0000000000000..506626828c6aa --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/Clock.h @@ -0,0 +1,129 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: Clock.hpp + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 29 August 2016, 9:29 + */ +#ifndef O2_DCS_CLOCK_H +#define O2_DCS_CLOCK_H + +#include <ctime> +#include <cstdint> +#include <chrono> +#include <memory> +#include <string> + +namespace o2 +{ +namespace dcs +{ +/** + * Returns a simple timestamp presenting the milliseconds of time passed + * since the given time point. + * + * @param beginning The time point used as a reference for the time interval + * calculation. + * @return The amount of milliseconds passed since the given time point. + */ +inline uint64_t time_since( + const std::chrono::steady_clock::time_point beginning) noexcept +{ + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - beginning) + .count(); +} + +/** + * Returns the measured number of milliseconds passed since UNIX epoch + * (1 January 1970 01:00:00.000). This function uses system clock. + * + * @return Number of milliseconds since epoch. + * @see ADAPRO::Library::now + */ +inline uint64_t epoch_time() noexcept +{ + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count(); +} + +/** + * Returns a timestamp using steady clock. This function is suitable for + * measuring time intervals, but it's not meant to be used for calculating + * dates. + * + * @return + * @see ADAPRO::Library::epoch_time + */ +inline uint64_t now() noexcept +{ + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); +} + +/** + * Returns a timestamp of the current point of time in the local timezone. + * + * @return A simple ISO-8601-esque timestamp (<tt>YYYY-MM-DD HH:MM:SS</tt>). + * Every decimal number in the date string has leading zeros and therefore + * fixed length. + * @see ADAPRO::Control::fs_timestamp + */ +inline std::string timestamp() noexcept +{ + char buffer[20]; + std::time_t now = std::time(nullptr); + std::strftime(buffer, 32, "%F %T", std::localtime(&now)); + return std::string(buffer); +} + +/** + * Returns a timestamp of the current point of time in the local timezone. + * The format of the timestamp is specified with the parameter + * <tt>format</tt>. The format of the format string is the same as is used + * by <tt>std::strftime</tt>. + * + * @return A simple timestamp in a format specified with the parameter + * <tt>format</tt>. + */ +inline std::string timestamp(const std::string& format) noexcept +{ + char buffer[20]; + std::time_t now = std::time(nullptr); + std::strftime(buffer, 32, format.c_str(), std::localtime(&now)); + return std::string(buffer); +} + +/** + * Generates a simple timestamp usable file paths. This function is like + * <tt>ADAPRO::Control::timestamp</tt>, but with spaces replaced with + * underscores and colons with dots in order to ensure compatibility with + * (Linux) filesystems. This function uses local timezone. + * + * @return A simple ISO-8601-esque timestamp (<tt>YYYY-MM-DD_HH.MM.SS</tt>). + * Every decimal number in the date string has leading zeros and therefore + * fixed length. + * @see ADAPRO::Control::timestamp + */ +inline std::string fs_timestamp() noexcept +{ + char buffer[20]; + std::time_t now = std::time(nullptr); + std::strftime(buffer, 32, "%F_%H.%M.%S", std::localtime(&now)); + return std::string(buffer); +} +} // namespace dcs +} // namespace o2 + +#endif /* O2_DCS_CLOCK_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/DCSProcessor.h b/Detectors/DCS/include/DetectorsDCS/DCSProcessor.h new file mode 100644 index 0000000000000..8f19d1eeae923 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DCSProcessor.h @@ -0,0 +1,321 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DETECTOR_DCS_DCSPROCESSOR_H_ +#define DETECTOR_DCS_DCSPROCESSOR_H_ + +#include <memory> +#include <Rtypes.h> +#include <unordered_map> +#include <deque> +#include <numeric> +#include "Framework/Logger.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DeliveryType.h" +#include "CCDB/CcdbObjectInfo.h" +#include "CommonUtils/MemFileHelper.h" +#include "CCDB/CcdbApi.h" + +//#ifdef WITH_OPENMP +//#include <omp.h> +//#endif + +/// @brief Class to process DCS data points + +namespace o2 +{ +namespace dcs +{ + +class DCSProcessor +{ + + public: + using Ints = std::vector<int>; + using Chars = std::vector<char>; + using Doubles = std::vector<double>; + using Binaries = std::array<uint64_t, 7>; + using Strings = std::array<char, 56>; + + using DQChars = std::deque<char>; + using DQInts = std::deque<int>; + using DQDoubles = std::deque<double>; + using DQUInts = std::deque<uint32_t>; + using DQBools = std::deque<bool>; + using DQStrings = std::deque<Strings>; + using DQTimes = std::deque<uint32_t>; + using DQBinaries = std::deque<Binaries>; + + using DPID = o2::dcs::DataPointIdentifier; + using DPVAL = o2::dcs::DataPointValue; + using DPCOM = o2::dcs::DataPointCompositeObject; + + using TFType = uint64_t; + using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; + + DCSProcessor() = default; + ~DCSProcessor() = default; + + void init(const std::vector<DPID>& pidschars, const std::vector<DPID>& pidsints, + const std::vector<DPID>& pidsdoubles, const std::vector<DPID>& pidsUints, + const std::vector<DPID>& pidsbools, const std::vector<DPID>& pidsstrings, + const std::vector<DPID>& pidstimes, const std::vector<DPID>& pidsbinaries); + + void init(const std::vector<DPID>& pids); + + int processMap(const std::unordered_map<DPID, DPVAL>& map, bool isDelta = false); + + int processDP(const std::pair<DPID, DPVAL>& dpcom); + + std::unordered_map<DPID, DPVAL>::const_iterator findAndCheckPid(const DPID& pid, DeliveryType type, + const std::unordered_map<DPID, DPVAL>& map); + + template <typename T> + int processArrayType(const std::vector<DPID>& array, DeliveryType type, const std::unordered_map<DPID, DPVAL>& map, + std::vector<uint64_t>& latestTimeStamp, std::unordered_map<DPID, std::deque<T>>& destmap); + + template <typename T> + void checkFlagsAndFill(const std::pair<DPID, DPVAL>& dpcom, uint64_t& latestTimeStamp, + std::unordered_map<DPID, T>& destmap); + + virtual void processCharDP(const DPID& pid); + virtual void processIntDP(const DPID& pid); + virtual void processDoubleDP(const DPID& pid); + virtual void processUIntDP(const DPID& pid); + virtual void processBoolDP(const DPID& pid); + virtual void processStringDP(const DPID& pid); + virtual void processTimeDP(const DPID& pid); + virtual void processBinaryDP(const DPID& pid); + + virtual uint64_t processFlag(uint64_t flag, const char* pid); + + template <typename T> + void doSimpleMovingAverage(int nelements, std::deque<T>& vect, float& avg, bool& isSMA); + + DQChars& getVectorForPidChar(const DPID& id) { return mDpscharsmap[id]; } + DQInts& getVectorForPidInt(const DPID& id) { return mDpsintsmap[id]; } + DQDoubles& getVectorForPidDouble(const DPID& id) { return mDpsdoublesmap[id]; } + DQUInts& getVectorForPidUInt(const DPID& id) { return mDpsUintsmap[id]; } + DQBools& getVectorForPidBool(const DPID& id) { return mDpsboolsmap[id]; } + DQStrings& getVectorForPidString(const DPID& id) { return mDpsstringsmap[id]; } + DQTimes& getVectorForPidTime(const DPID& id) { return mDpstimesmap[id]; } + DQBinaries& getVectorForPidBinary(const DPID& id) { return mDpsbinariesmap[id]; } + + void setNThreads(int n); + int getNThreads() const { return mNThreads; } + const std::unordered_map<std::string, float>& getCCDBSimpleMovingAverage() const { return mccdbSimpleMovingAverage; } + const CcdbObjectInfo& getCCDBSimpleMovingAverageInfo() const { return mccdbSimpleMovingAverageInfo; } + CcdbObjectInfo& getCCDBSimpleMovingAverageInfo() { return mccdbSimpleMovingAverageInfo; } + + void setTF(TFType tf) { mTF = tf; } + + void setIsDelta(bool isDelta) { mIsDelta = isDelta; } + void isDelta() { mIsDelta = true; } + bool getIsDelta() const { return mIsDelta; } + + void setMaxCyclesNoFullMap(uint64_t maxCycles) { mMaxCyclesNoFullMap = maxCycles; } + uint64_t getMaxCyclesNoFullMap() const { return mMaxCyclesNoFullMap; } + + uint64_t getNCyclesNoFullMap() const { return mNCyclesNoFullMap; } + + template <typename T> + void prepareCCDBobject(T& obj, CcdbObjectInfo& info, const std::string& path, TFType tf, + const std::map<std::string, std::string>& md); + + void setName(std::string name) { mName = name; } + const std::string getName() const { return mName; } + + private: + bool mFullMapSent = false; // set to true as soon as a full map was sent. No delta can + // be received if there was never a full map sent + int64_t mNCyclesNoFullMap = 0; // number of times the delta was sent withoug a full map + int64_t mMaxCyclesNoFullMap = 6000; // max number of times when the delta can be sent between + // two full maps (assuming DCS sends data every 50 ms, this + // means a 5 minutes threshold) + bool mIsDelta = false; // set to true in case you are processing delta map + // (containing only DPs that changed) + std::unordered_map<DPID, float> mSimpleMovingAverage; // moving average for several DPs + std::unordered_map<DPID, DQChars> mDpscharsmap; + std::unordered_map<DPID, DQInts> mDpsintsmap; + std::unordered_map<DPID, DQDoubles> mDpsdoublesmap; + std::unordered_map<DPID, DQUInts> mDpsUintsmap; + std::unordered_map<DPID, DQBools> mDpsboolsmap; + std::unordered_map<DPID, DQStrings> mDpsstringsmap; + std::unordered_map<DPID, DQTimes> mDpstimesmap; + std::unordered_map<DPID, DQBinaries> mDpsbinariesmap; + std::vector<DPID> mPidschars; + std::vector<DPID> mPidsints; + std::vector<DPID> mPidsdoubles; + std::vector<DPID> mPidsUints; + std::vector<DPID> mPidsbools; + std::vector<DPID> mPidsstrings; + std::vector<DPID> mPidstimes; + std::vector<DPID> mPidsbinaries; + std::unordered_map<DPID, int> mPids; // contains all PIDs for the current processor; the value correspond to the index + // in the mPidschars/ints/doubles.. vectors + std::vector<uint64_t> mLatestTimestampchars; + std::vector<uint64_t> mLatestTimestampints; + std::vector<uint64_t> mLatestTimestampdoubles; + std::vector<uint64_t> mLatestTimestampUints; + std::vector<uint64_t> mLatestTimestampbools; + std::vector<uint64_t> mLatestTimestampstrings; + std::vector<uint64_t> mLatestTimestamptimes; + std::vector<uint64_t> mLatestTimestampbinaries; + int mNThreads = 1; // number of threads + std::unordered_map<std::string, float> mccdbSimpleMovingAverage; // unordered map in which to store the CCDB entry + // for the DPs for which we calculated the simple + // moving average + CcdbObjectInfo mccdbSimpleMovingAverageInfo; // info to store the output of the calibration for + // the DPs for which we calculated + // the simple moving average + TFType mTF = 0; // TF index for processing, + // used to store CCDB object + std::string mName = ""; // to be used to determine CCDB path + + ClassDefNV(DCSProcessor, 0); +}; + +using Ints = std::vector<int>; +using Chars = std::vector<char>; +using Doubles = std::vector<double>; +using Binaries = std::array<uint64_t, 7>; +using Strings = std::array<char, 56>; + +using DQStrings = std::deque<Strings>; +using DQBinaries = std::deque<Binaries>; + +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; + +template <typename T> +int DCSProcessor::processArrayType(const std::vector<DPID>& array, + DeliveryType type, const std::unordered_map<DPID, DPVAL>& map, + std::vector<uint64_t>& latestTimeStamp, + std::unordered_map<DPID, std::deque<T>>& destmap) +{ + + // processing the array of type T + + int found = 0; + //#ifdef WITH_OPENMP + //omp_set_num_threads(mNThreads); + //#pragma omp parallel for schedule(dynamic) + //#endif + for (size_t i = 0; i != array.size(); ++i) { + auto it = findAndCheckPid(array[i], type, map); + if (it == map.end()) { + if (!mIsDelta) { + LOG(ERROR) << "Element " << array[i] << " not found " << std::endl; + } + continue; + } + found++; + std::pair<DPID, DPVAL> pairIt = *it; + checkFlagsAndFill(pairIt, latestTimeStamp[i], destmap); + if (type == RAW_CHAR) { + processCharDP(array[i]); + } else if (type == RAW_INT) { + processIntDP(array[i]); + } else if (type == RAW_DOUBLE) { + processDoubleDP(array[i]); + } else if (type == RAW_UINT) { + processUIntDP(array[i]); + } else if (type == RAW_BOOL) { + processBoolDP(array[i]); + } else if (type == RAW_STRING) { + processStringDP(array[i]); + } else if (type == RAW_TIME) { + processTimeDP(array[i]); + } else if (type == RAW_BINARY) { + processBinaryDP(array[i]); + } + // todo: better to move the "found++" after the process, in case it fails? + } + return found; +} + +//______________________________________________________________________ + +template <typename T> +void DCSProcessor::checkFlagsAndFill(const std::pair<DPID, DPVAL>& dpcom, uint64_t& latestTimeStamp, + std::unordered_map<DPID, T>& destmap) +{ + + // check the flags for the upcoming data, and if ok, fill the accumulator + + auto& dpid = dpcom.first; + auto& val = dpcom.second; + auto flags = val.get_flags(); + if (processFlag(flags, dpid.get_alias()) == 0) { + auto etime = val.get_epoch_time(); + // fill only if new value has a timestamp different from the timestamp of the previous one + LOG(DEBUG) << "destmap[pid].size() = " << destmap[dpid].size(); + if (destmap[dpid].size() == 0 || etime != latestTimeStamp) { + LOG(DEBUG) << "adding new value"; + destmap[dpid].push_back(val.payload_pt1); + latestTimeStamp = etime; + } + } +} + +//______________________________________________________________________ + +template <> +void DCSProcessor::checkFlagsAndFill(const std::pair<DPID, DPVAL>& dpcom, uint64_t& latestTimeStamp, + std::unordered_map<DPID, DCSProcessor::DQStrings>& destmap); + +//______________________________________________________________________ + +template <> +void DCSProcessor::checkFlagsAndFill(const std::pair<DPID, DPVAL>& dpcom, uint64_t& latestTimeStamp, + std::unordered_map<DPID, DCSProcessor::DQBinaries>& destmap); + +//______________________________________________________________________ + +template <typename T> +void DCSProcessor::doSimpleMovingAverage(int nelements, std::deque<T>& vect, float& avg, bool& isSMA) +{ + + // Do simple moving average on vector of type T + + if (vect.size() <= nelements) { + //avg = std::accumulate(vect.begin(), vect.end(), 0.0) / vect.size(); + avg = (avg * (vect.size() - 1) + vect.back()) / vect.size(); + return; + } + + avg += (vect[vect.size() - 1] - vect[0]) / nelements; + vect.pop_front(); + isSMA = true; +} + +//______________________________________________________________________ + +template <typename T> +void DCSProcessor::prepareCCDBobject(T& obj, CcdbObjectInfo& info, const std::string& path, TFType tf, + const std::map<std::string, std::string>& md) +{ + + // prepare all info to be sent to CCDB for object obj + auto clName = o2::utils::MemFileHelper::getClassName(obj); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + info.setPath(path); + info.setObjectType(clName); + info.setFileName(flName); + info.setStartValidityTimestamp(tf); + info.setEndValidityTimestamp(99999999999999); + info.setMetaData(md); +} +} // namespace dcs +} // namespace o2 + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h new file mode 100644 index 0000000000000..525a74bb9edc3 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -0,0 +1,315 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: DataPointCompositeObject.h + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 23 September 2016, 11:28 + */ +#ifndef O2_DCS_DATAPOINT_COMPOSITE_OBJECT_H +#define O2_DCS_DATAPOINT_COMPOSITE_OBJECT_H + +#include <cstdint> +#include <stdexcept> +#include <ostream> +#include <string> +#include "Rtypes.h" +#include "DetectorsDCS/StringUtils.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DeliveryType.h" + +namespace o2 +{ +namespace dcs +{ +/** + * DataPointCompositeObject is a composition of a DataPointIdentifier and a + * DataPointValue. It is the unit of data points that ADAPOS provides. + * + * @see ADAPRO::ADAPOS::DataPointIdentifier + * @see ADAPRO::ADAPOS::DataPointValue + */ +struct alignas(128) DataPointCompositeObject final { + /** + * The DataPointIdentifier object, which occupies the first 64 bytes of + * the DataPointCompositeObject. This object contains the immutable + * alias and type information. + * + * @see ADAPRO::ADAPOS::DataPointIdentifier + */ + const DataPointIdentifier id; + + /** + * The DataPointValue object, which occupies the last 64 bytes of the + * DataPointCompositeObject. This object contains the mutable parts of + * DataPointCompositeObject. These parts are the ADAPOS flags, + * timestamp, and the payload data. + * + * @see ADAPRO::ADAPOS::DataPointValue + */ + DataPointValue data; + + /** + * The default constructor for DataPointCompositeObject. Uses the + * default constructors of its component, which means that their every + * field will be filled with zeroes. + * + * @see ADAPRO::ADAPOS::DataPointIdentifier + * @see ADAPRO::ADAPOS::DataPointValue + */ + DataPointCompositeObject() noexcept : id(), data() {} + + /** + * This constructor <em>copies</em> the given DataPointIdentifier and + * DataPointValue into the fields <tt>id</tt> and <tt>data</tt>. + * + * @param id The DPID component. + * @param data The DPVAL component. + */ + DataPointCompositeObject( + const DataPointIdentifier& id, + const DataPointValue& data) noexcept : id(id), data(data) {} + + /** + * Copy constructor + */ + DataPointCompositeObject(const DataPointCompositeObject& src) noexcept : DataPointCompositeObject(src.id, src.data) {} + + DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept + { + if (&src != this) { + memcpy(this, &src, sizeof(DataPointCompositeObject)); + } + return *this; + } + + /** + * Bit-by bit equality comparison of DataPointCompositeObjects. + * + * @param other The right-hand operand of equality comparison. + * @return <tt>true</tt> or <tt>false</tt>. + */ + inline bool operator==(const DataPointCompositeObject& other) const + noexcept + { + return id == other.id && data == other.data; + } + + /** + * Negation of the <tt>==</tt> operator. + * + * @param other The right-hand side operand. + * @return <tt>true</tt> or <tt>false</tt>. + */ + inline bool operator!=(const DataPointCompositeObject& other) const + noexcept + { + return id != other.id || data != other.data; + } + + /** + * Overwrites a DataPointCompositeObject with the given <tt>id</tt> and + * <tt>data</tt> values. + * + * @param id The id value. + * @param data The data value. + */ + inline void set(const DataPointIdentifier& id, DataPointValue& data) noexcept + { + DataPointIdentifier::FILL(this->id, (uint64_t*)&id); + this->data = data; + } + + /** + * Overwrites a DataPointCompositeObject as a copy of the given 128-byte + * segment (16 times <tt>sizeof(uint64_t)</tt>) of binary data. + * + * @param data Beginning of the data segment used for reading. The + * length of the segment is assumed to be exactly 128 bytes and contain + * a valid binary representation of a DataPointCompositeObject. + */ + inline void set(const uint64_t* const data) noexcept + { + DataPointIdentifier::FILL(this->id, data); + this->data.set(data + 8, id.get_type()); + } + + /** + * Overwrites the state of the <tt>data</tt> field with the state of the + * given DPVAL object, <em>except for the control flags, that will be + * cleared out</em>. + * + * @param dpval A DPVAL object representing an update event to the + * DPCOM in question. + * + * @see ADAPRO::ADAPOS::DataPointValue + * @see ADAPRO::ADAPOS::DataPointValue::CONTROL_MASK + */ + inline void update(const DataPointValue& dpval) noexcept + { + data.set( + dpval.flags & ~DataPointValue::CONTROL_MASK, + dpval.msec, + dpval.sec, + (uint64_t*)&dpval.payload_pt1, + id.get_type()); + } + + /** + * Returns a pointer to the beginning of the data part to be given to + * DIM, which depends on the <tt>DeliveryType</tt> of this DPCOM. This + * method is specific to ADAPOS Load Generator and ADAPOS Engine. + * + * @return A <tt>(void*)</tt>. + * @throws std::domain_error If the <tt>DeliveryType</tt> of this + * DPCOM object was illegal (i.e. <tt>VOID</tt> or something else than + * the enumerators of <tt>DeliveryType</tt>). + * @see ADAPRO::ADAPOS::DeliveryType + */ + inline void* dim_buffer() const + { + switch (id.get_type()) { + case RAW_INT: + case RAW_UINT: + case RAW_BOOL: + case RAW_CHAR: + case RAW_DOUBLE: + case RAW_TIME: + case RAW_STRING: + case RAW_BINARY: + return (void*)&data.payload_pt1; + case DPVAL_INT: + case DPVAL_UINT: + case DPVAL_BOOL: + case DPVAL_CHAR: + case DPVAL_DOUBLE: + case DPVAL_TIME: + case DPVAL_STRING: + case DPVAL_BINARY: + return (void*)&data; + default: + case VOID: + throw std::domain_error("Illegal DeliveryType."); + } + } + + /** + * Prints a CSV-representation of the DataPointCompositeObject into the + * ostream. The format of the payload data depends on its type and is + * handled automatically. + * + * @param os The output stream. + * @param dpcom The DataPointCompositeObject. + * @return Reference to the stream after the printing operation. + */ + inline friend std::ostream& operator<<(std::ostream& os, + const DataPointCompositeObject& dpcom) noexcept + { + std::string payload; + os << dpcom.id << ";" << dpcom.data << ";"; + union Converter { + uint64_t raw_data; + double double_value; + uint32_t uint_value; + int32_t int_value; + char char_value; + bool bool_value; + } converter; + converter.raw_data = dpcom.data.payload_pt1; + switch (dpcom.id.get_type()) { + case VOID: + return os; + case RAW_INT: + case DPVAL_INT: + return os << converter.int_value; + case RAW_UINT: + case DPVAL_UINT: + return os << converter.uint_value; + case RAW_BOOL: + case DPVAL_BOOL: + return os << (converter.bool_value ? "True" : "False"); + case RAW_CHAR: + case DPVAL_CHAR: + return os << converter.char_value; + case RAW_DOUBLE: + case DPVAL_DOUBLE: + return os << converter.double_value; + case RAW_STRING: + case DPVAL_STRING: + return os << std::string((char*)&dpcom.data.payload_pt1); + case RAW_TIME: + case DPVAL_TIME: { + // I have no idea how to properly test the time_t backend. + // It's probably even hardware/os/kernel specific... +#ifdef __linux__ + char buffer[17]; + std::time_t ts((dpcom.data.payload_pt1) >> 32); + std::strftime(buffer, 32, "%FT%T", std::gmtime(&ts)); + return os << std::string(buffer) << "." << std::setw(3) << std::setfill('0') << std::to_string((dpcom.data.payload_pt1 & 0x00000000FFFF0000) >> 16) + << "Z"; +#else + return os << "[Timestamps not supported on this platform.]"; +#endif + } + case RAW_BINARY: + case DPVAL_BINARY: + default: + return os << o2::dcs::to_hex_little_endian( + (char*)&dpcom.data.payload_pt1, 56); + } + } + + /** + * The destructor for DataPointCompositeObject so it is not deleted + * and thus DataPointCompositeObject is trivially copyable + */ + ~DataPointCompositeObject() noexcept = default; + ClassDefNV(DataPointCompositeObject, 1); +}; + +/** + * Return the value contained in the DataPointCompositeObject, if possible. + * + * @tparam T the expected type of the value + * + * @param dpcom the DataPointCompositeObject the value is extracted from + * + * @returns the value of the data point + * + * @throws if the DeliveryType of the data point is not compatible with T + */ +template <typename T> +T getValue(const DataPointCompositeObject& dpcom); + +} // namespace dcs + +/// Defining DataPointCompositeObject explicitly as messageable +namespace framework +{ +template <typename T> +struct is_messageable; +template <> +struct is_messageable<o2::dcs::DataPointCompositeObject> : std::true_type { +}; +} // namespace framework + +} // namespace o2 + +/// Defining DataPointCompositeObject explicitly as copiable +namespace std +{ +template <> +struct is_trivially_copyable<o2::dcs::DataPointCompositeObject> : std::true_type { +}; +} // namespace std + +#endif /* O2_DCS_DATAPOINT_COMPOSITE_OBJECT_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCreator.h b/Detectors/DCS/include/DetectorsDCS/DataPointCreator.h new file mode 100644 index 0000000000000..4c63e96108d15 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCreator.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_DATAPOINT_CREATOR_H +#define O2_DCS_DATAPOINT_CREATOR_H + +#include "DataPointCompositeObject.h" + +namespace o2::dcs +{ +/** + * createDataPointCompositeObject is a convenience function to + * simplify the creation of a DataPointCompositeObject. + * + * @param alias the DataPoint alias name (max 56 characters) + * @param val the value of the datapoint + * @param flags value for ADAPOS flags. + * @param milliseconds value for milliseconds. + * @param seconds value for seconds. + * + * @returns a DataPointCompositeObject + * + * The actual DeliveryType of the returned + * DataPointCompositeObject is deduced from the type of val. + * + * Note that only a few relevant specialization are actually provided + * + * - T=int32_t : DeliveryType = RAW_INT + * - T=uint32_t : DeliveryType = RAW_UINT + * - T=double : DeliveryType = RAW_DOUBLE + * - T=bool : DeliveryType = RAW_BOOL + * - T=char : DeliveryType = RAW_CHAR + * - T=std::string : DeliveryType = RAW_STRING + * + */ +template <typename T> +o2::dcs::DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, T val, uint32_t seconds, uint16_t msec, uint16_t flags = 0); +} // namespace o2::dcs + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointGenerator.h b/Detectors/DCS/include/DetectorsDCS/DataPointGenerator.h new file mode 100644 index 0000000000000..5b3bcc180dcb2 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointGenerator.h @@ -0,0 +1,43 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_DATAPOINT_GENERATOR_H +#define O2_DCS_DATAPOINT_GENERATOR_H + +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include <vector> + +namespace o2::dcs +{ +/** +* Generate random data points, uniformly distributed between two values. +* +* @tparam T the type of value of the data points to be generated. Only +* a few types are supported : double, uint32_t, int32_t, char, bool +* +* @param aliases the list of aliases to be generated. Those can use +* patterns that will be expanded, @see AliasExpander +* @param minValue the minimum value of the values to be generated +* @param maxValue the maximum value of the values to be generated +* @param refDate the date to be associated with all data points +* in `%Y-%b-%d %H:%M:%S` format. If refDate="" the current date is used. +* +* @returns a vector of DataPointCompositeObject objects +*/ +template <typename T> +std::vector<DataPointCompositeObject> generateRandomDataPoints(const std::vector<std::string>& aliases, + T min, + T max, + std::string refDate = ""); + +} // namespace o2::dcs + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h new file mode 100644 index 0000000000000..258238c69f9b7 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -0,0 +1,247 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: DataPointIdentifier.h + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 23 September 2016, 12:00 + */ +#ifndef O2_DCS_DATAPOINT_IDENTIFIER_H +#define O2_DCS_DATAPOINT_IDENTIFIER_H + +#include <cstring> +#include <string> +#include <cstdint> +#include <iostream> +#include <iomanip> +#include <functional> +#include "Rtypes.h" +#include "DetectorsDCS/StringUtils.h" +#include "DetectorsDCS/GenericFunctions.h" +#include "DetectorsDCS/DeliveryType.h" + +namespace o2 +{ +namespace dcs +{ +/** + * DataPointIdentifier object is responsible for storing the alias and type + * information of a data point. + */ +class alignas(64) DataPointIdentifier final +{ + const uint64_t pt1; + const uint64_t pt2; + const uint64_t pt3; + const uint64_t pt4; + const uint64_t pt5; + const uint64_t pt6; + const uint64_t pt7; + const uint64_t pt8; // Contains the last 6 chars of alias and the type. + + DataPointIdentifier( + const uint64_t pt1, const uint64_t pt2, const uint64_t pt3, + const uint64_t pt4, const uint64_t pt5, const uint64_t pt6, + const uint64_t pt7, const uint64_t pt8) noexcept : pt1(pt1), pt2(pt2), pt3(pt3), pt4(pt4), pt5(pt5), pt6(pt6), pt7(pt7), pt8(pt8) {} + + public: + /** + * The default constructor for DataPointIdentifier. Creates a DPID that + * contains only zeroes. To fill it with alias and type, please use the + * factory procedure <tt>fill</tt>. + * + * @see ADAPRO::ADAPOS::DataPointIdentifier::fill + */ + DataPointIdentifier() noexcept : pt1(0), pt2(0), pt3(0), pt4(0), pt5(0), pt6(0), pt7(0), pt8(0) + { + } + + /** + * A constructor for DataPointIdentifier. Copies the given arguments to + * the memory segment owned by the object under construction. + * + * @param alias The alias of the DataPointIdentifier. Maximum length is + * 62 characaters. + * @param type Type of the payload value associated with the service + * identified by this DataPointIdentifier. + */ + DataPointIdentifier(const std::string& alias, const DeliveryType type) noexcept : DataPointIdentifier() + { + strncpy((char*)this, alias.c_str(), 62); + ((char*)&pt8)[7] = type; + } + + /** + * A copy constructor for DataPointIdentifier. + */ + DataPointIdentifier(const DataPointIdentifier& src) noexcept : DataPointIdentifier(src.pt1, src.pt2, src.pt3, src.pt4, src.pt5, src.pt6, src.pt7, src.pt8) {} + + DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept + { + if (&src != this) { + memcpy(this, &src, sizeof(DataPointIdentifier)); + } + return *this; + } + + /** + * This stati procedure fills the given DataPointIdentifier object with + * the given parameters. + * + * @param dpid The DataPointIdentifier to be filled (i.e. overwritten). + * @param alias Alias of the data point. This is used for identifying + * the DIM service that publishes updates to a data point. + * @param type Type of the data point payload value. + */ + static inline void FILL( + const DataPointIdentifier& dpid, + const std::string& alias, + const DeliveryType type) noexcept + { + strncpy((char*)&dpid, alias.c_str(), 62); + ((char*)&dpid.pt8)[7] = type; + } + + /** + * This static procedure copies the given 64-byte binary segment into + * the DataPointIdentifier object. + * + * @param dpid The DataPointIdentifier to be filled (i.e. overwritten). + * @param data Beginning of the 64-byte binary segment. + */ + static inline void FILL( + const DataPointIdentifier& dpid, + const uint64_t* const data) noexcept + { + std::strncpy((char*)&dpid, (char*)data, 62); + ((char*)&dpid)[63] = ((data[7] & 0xFF00000000000000) >> 56); + } + + /** + * The equality comparison object of DPIDs. + * + * @param other The other DPID object for comparison. + * @return <tt>true</tt> if and only if the other DPID object + * has exactly (bit-by-bit) the same state. + */ + inline bool operator==(const DataPointIdentifier& other) const + { + return memcmp((char*)this, (char*)&other, 64) == 0; + } + + /** + * Negation of the equality comparison. + * + * @param other The second operand. + * @return <tt>true</tt> or <tt>false</tt>. + */ + inline bool operator!=(const DataPointIdentifier& other) const + { + return !(*this == other); + } + + /** + * Appends DataPointIdentifier object to the given stream using CSV + * format (i.e. <tt><alias> ";" <type></tt>). + */ + friend inline std::ostream& operator<<(std::ostream& os, + const DataPointIdentifier& dpid) noexcept + { + return os << std::string((char*)&dpid) << ";" << o2::dcs::show((DeliveryType)((dpid.pt8 & 0xFF00000000000000) >> 56)); + } + + /** + * Returns the alias of the DPID object as a C-style string (i.e. + * null-terminated char array). + * + * @return The alias. + */ + inline const char* const get_alias() const noexcept + { + return (char*)&pt1; + } + + /** + * Getter for the payload type information. + * + * @return a DeliveryType object. + */ + inline DeliveryType get_type() const noexcept + { + return (DeliveryType)((pt8 & 0xFF00000000000000) >> 56); + } + + /** + * Returns a hash code calculated from the alias. <em>Note that the + * hash code is recalculated every time when this function is called. + * </em> + * + * @return An unsigned integer. + */ + inline size_t hash_code() const noexcept + { + return o2::dcs::hash_code(std::string((char*)&pt1)); + } + + /** + * The destructor for DataPointIdentifier. + */ + ~DataPointIdentifier() noexcept = default; + ClassDefNV(DataPointIdentifier, 1); +}; + +/** + * This simple function object is used for calculating the hash code for a + * DataPointIdentifier object in a STL container. + * + * @param dpid The DataPointIdentifier object, whose hash code is to be + * calculated. + * @return The calculated hash code. + */ +struct DPIDHash { + uint64_t operator()(const o2::dcs::DataPointIdentifier& dpid) + const + { + return dpid.hash_code(); + } +}; +} // namespace dcs + +/// Defining DataPointIdentifier explicitly as messageable +namespace framework +{ +template <typename T> +struct is_messageable; +template <> +struct is_messageable<o2::dcs::DataPointIdentifier> : std::true_type { +}; +} // namespace framework + +} // namespace o2 + +// specailized std::hash +namespace std +{ +template <> +struct hash<o2::dcs::DataPointIdentifier> { + std::size_t operator()(const o2::dcs::DataPointIdentifier& dpid) const + { + return std::hash<uint64_t>{}(dpid.hash_code()); + } +}; + +template <> +struct is_trivially_copyable<o2::dcs::DataPointIdentifier> : std::true_type { +}; + +} // namespace std + +#endif /* O2_DCS_DATAPOINT_IDENTIFIER_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointValue.h b/Detectors/DCS/include/DetectorsDCS/DataPointValue.h new file mode 100644 index 0000000000000..6bf7f490d3fac --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointValue.h @@ -0,0 +1,707 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: DataPointValue.h + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 23 September 2016, 12:20 + */ +#ifndef O2_DCS_DATAPOINT_VALUE +#define O2_DCS_DATAPOINT_VALUE + +#include <cstring> +#include <cstdint> +#include <ctime> +#include <atomic> +#include <string> +#include <ostream> +#include <sstream> +#include <iostream> +#include <iomanip> +#include <memory> +#include <stdexcept> +#include "Rtypes.h" +#include "DetectorsDCS/StringUtils.h" +#include "DetectorsDCS/Clock.h" +#include "DetectorsDCS/DeliveryType.h" + +namespace o2 +{ +namespace dcs +{ +/** + * DataPointValue is the struct responsible for containing the flags, + * timestamp, and payload value of a data point. It represents a discrete + * update event to a data point. <em>It should be noted that the + * constructors and setters of this struct, that take an array containing + * binary data as parameter, don't check the length of the given array.</em> + */ +struct alignas(64) DataPointValue final { + /** + * <p>This flag is used for signaling that a DPVAL stream connection is + * working, but there is no data to be sent.</p> + * <p>If this flag is set, it will be denoted with letter <tt>A</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t KEEP_ALIVE_FLAG = 0x0001; + + /** + * <p>This flag is used for signaling the end of transmission.</p> + * <p>If this flag is set, it will be denoted with letter <tt>Z</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t END_FLAG = 0x0002; + + /** + * <p>This flag is set in the ADAPOS FBI Master DPCOM object.</p> + * <p>If this flag is set, it will be denoted with letter <tt>M</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t FBI_FLAG = 0x0004; + + /** + * <p>This flag is set by Producer to signal Consumer that this + * DataPointValue object belongs to a new service, that doesn't yet have + * its DataPointCompositeObject in the image.</p> + * <p>If this flag is set, it will be denoted with letter <tt>N</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t NEW_FLAG = 0x0008; + + /** + * <p>This flag is reserved for the ADAPRO/ADAPOS Producer-Consumer + * model. A DataPointValue is dirty if and only if it's stored in the + * ring buffer but not been fully processed yet. This flag should always + * be set when a DPCOM containing the DataPointValue object arrives from + * ADAPOS Engine via TCP.</p> + * <p>If this flag is set, it will be denoted with letter <tt>D</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t DIRTY_FLAG = 0x0010; + + /** + * <p>This flag is used as a part of the DPVAL mutual exclusion + * mechanism. This flag should be set iff reader has the turn.</p> + * <p>If this flag is set, it will be denoted with letter <tt>U</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint64_t TURN_FLAG = 0x0020; + + /** + * <p>This flag is used by the DPVAL mutual exclusion mechanism.</p> + * <p>If this flag is set, it will be denoted with letter <tt>W</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t WRITE_FLAG = 0x0040; + + /** + * <p>This flag is used by the DPVAL mutual exclusion mechanism.</p> + * <p>If this flag is set, it will be denoted with letter <tt>R</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t READ_FLAG = 0x0080; + + /** + * <p>This flag should be set if and only if there is a ring buffer + * overflow happening. Ring buffer overflow is manifested as an + * overwrite of a dirty DPVAL.</p> + * <p>If this flag is set, it will be denoted with letter <tt>O</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t OVERWRITE_FLAG = 0x0100; + + /** + * <p>In case of buffer overflow, this flag can be used for indicating a + * service that lost an update. <em>Not receiving this flag shouldn't be + * taken as an indication that every update was successfully + * transmitted.</em></p> + * <p>If this flag is set, it will be denoted with letter <tt>V</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t VICTIM_FLAG = 0x0200; + + /** + * <p>This flag should be set if and only if there is a DIM error + * (namely, connection failure with the DIM service) in ADAPOS Engine + * affecting this DataPointValue object.</p> + * <p>If this flag is set, it will be denoted with letter <tt>E</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t DIM_ERROR_FLAG = 0x0400; + + /** + * <p>This flag should be set if and only if the DPID of the DPCOM + * containing this DPVAL has incorrect or unexpected value.</p> + * <p>If this flag is set, it will be denoted with letter <tt>I</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t BAD_DPID_FLAG = 0x0800; + + /** + * <p>This flag should be set if and only if the flags themselves have + * bad or unexpedcted value.</p> + * <p>If this flag is set, it will be denoted with letter <tt>F</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t BAD_FLAGS_FLAG = 0x1000; + + /** + * <p>This flag should be set if and only if the timestamp is + * incorrect.</p> + * <p>If this flag is set, it will be denoted with letter <tt>T</tt> in + * the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t BAD_TIMESTAMP_FLAG = 0x2000; + + /** + * <p>This flag should be set if and only if the payload value is bad. + * </p><p>If this flag is set, it will be denoted with letter <tt>P</tt> + * in the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t BAD_PAYLOAD_FLAG = 0x4000; + + /** + * <p>This flag should be set if it a FBI contains duplicated DPCOMs + * (i.e. DPCOMs with identical DPIDs). Also, partial FBI (with missing + * DPCOMs) can be indicated with this flag.</p> + * </p><p>If this flag is set, it will be denoted with letter <tt>B</tt> + * in the CSV output generated from this DPVAL object.</p> + */ + static constexpr uint16_t BAD_FBI_FLAG = 0x8000; + + /** + * This mask covers the session flags: <tt>KEEP_ALIVE_FLAG</tt>, + * <tt>END_FLAG</tt>, and <tt>FBI_MASTER_FLAG</tt>. + * + * @see ADAPRO::ADAPOS::DataPointValue::KEEPALIVE_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::END_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::FBI_FLAG + */ + static constexpr uint16_t SESSION_MASK = 0x0007; + + /** + * This mask covers the control flags: <tt>NEW_FLAG</tt>, + * <tt>DIRTY_FLAG</tt>, <tt>TURN_FLAG</tt>, <tt>WRITE_FLAG</tt>, and + * <tt>READ_FLAG</tt>. + * + * @see ADAPRO::ADAPOS::DataPointValue::NEW_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::DIRTY_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::TURN_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::WRITE_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::READ_FLAG + */ + static constexpr uint16_t CONTROL_MASK = 0x00F8; + + /** + * This mask covers the error flags: <tt>DIM_ERROR_FLAG</tt>, + * <tt>OVERWRITE_FLAG</tt>, <tt>VICTIM_FLAG</tt>, + * <tt>BAD_DPID_FLAG</tt>, <tt>BAD_FLAGS_FLAG</tt>, + * <tt>BAD_TIMESTAMP_FLAG</tt>, <tt>BAD_PAYLOAD_FLAG</tt>, and + * <tt>BAD_FBI_FLAG</tt>. + * + * @see ADAPRO::ADAPOS::DataPointValue::DIM_ERROR_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::OVERWRITE_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::VICTIM_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_DPID_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_FLAGS_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_TIMESTAMP_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_PAYLOAD_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_FBI_FLAG + */ + static constexpr uint16_t ERROR_MASK = 0xFF00; + + /** + * The ADAPOS flags, i.e. a bitmask of the 16 different DPVAL flags. + * + * @see ADAPRO::ADAPOS::DataPointValue::KEEPALIVE_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::END_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::FBI_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::NEW_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::DIRTY_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::TURN_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::WRITE_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::READ_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::DIM_ERROR_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::OVERWRITE_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_DPID_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_FLAGS_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_TIMESTAMP_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_PAYLOAD_FLAG + * @see ADAPRO::ADAPOS::DataPointValue::BAD_FBI_FLAG + */ + uint16_t flags = 0; + + /** + * Milliseconds of the timestamp. This is the measured number of + * milliseconds passed since the UNIX epoch + * (1 January 1970, 01:00:00.000) modulo 1000. The purpose of this field + * together with <tt>sec</tt> is to store a timestamp indicating the + * moment of creation/modification of the DataPointValue object. + * + * @see ADAPRO::ADAPOS::DataPointValue::sec + */ + uint16_t msec = 0; + + /** + * Seconds of the timestamp. This is the measured number of seconds + * passed since the UNIX epoch (1 January 1970, 01:00:00.000). The + * purpose of this field together with <tt>msec</tt> is to store a + * timestamp indicating the moment of creation/modification of the + * DataPointValue object. + */ + uint32_t sec = 0; + + /** + * First part of the 56-byte binary payload value. + */ + uint64_t payload_pt1 = 0; + + /** + * Second part of the 56-byte binary payload value. + */ + uint64_t payload_pt2 = 0; + + /** + * Third part of the 56-byte binary payload value. + */ + uint64_t payload_pt3 = 0; + + /** + * Fourth part of the 56-byte binary payload value. + */ + uint64_t payload_pt4 = 0; + + /** + * Fifth part of the 56-byte binary payload value. + */ + uint64_t payload_pt5 = 0; + + /** + * Sixth part of the 56-byte binary payload value. + */ + uint64_t payload_pt6 = 0; + + /** + * Seventh part of the 56-byte binary payload value. + */ + uint64_t payload_pt7 = 0; + + /** + * The default constuctor of DataPointValue. Fills the object with + * zeroes. + */ + DataPointValue() = default; + + /** + * The trivial copy constructor of DataPointValue. + * + * @param other The DataPointValue instance, whose state is to be copied + * to the new object. + */ + DataPointValue(const DataPointValue& other) = default; + + /** + * Constructor for DataPointValue. <em>This constructor assumes that the + * <tt>payload</tt> parameter points to a <tt>unit64_t</tt> array with + * (at least) seven elements.</em> The data will be treated as binary + * and will be written to the new DataPointValue object using + * <tt>std::memcpy</tt>. Use of this constructor is discouraged, since + * it might copy garbage data. Please consider using the constructor + * with a <tt>DeliveryType</tt> parameter instead. + * + * @param flags The ADAPOS flags. + * @param milliseconds Milliseconds of the timestamp. + * @param seconds Seconds of the timestamp. + * @param payload Pointer to a memory segment containing the + * binary payload data. The next seven <tt>uint64_t</tt> values (i.e. 56 + * <tt>char</tt>s) will be copied to the DataPointValue object. + * <em>payload</em> must not be <tt>nullptr</tt>. For an empty DPVAL, + * use the default constructor. + * + * @deprecated Since ADAPRO 4.1.0, this constructor has become + * deprecated. Please consider using the constructor with a + * <tt>DeliveryType</tt> parameter instead. + */ + [[deprecated]] DataPointValue( + const uint16_t flags, + const uint16_t milliseconds, + const uint32_t seconds, + const uint64_t* const payload) noexcept : flags(flags), msec(milliseconds), sec(seconds) + { + memcpy((void*)&payload_pt1, (void*)payload, 56); + } + + /** + * <p>Constructor for DataPointValue. Copies a data segment from the + * array <tt>payload</tt>. Length of the copied data is determined by + * the argument <tt>type</tt>. <em>No sanity checks on the given + * array are performed.</em></p> + * <p>If the given <tt>DeliveryType</t> is invalid, then the payload + * of the new DataPointValue object will be filled with zeros.</p> + * + * @param flags The ADAPOS flags. + * @param milliseconds Milliseconds of the timestamp. + * @param seconds Seconds of the timestamp. + * @param payload Pointer to a contiguous block of memory + * containing the binary payload data. + * @param type Used for setting the payload correctly, using + * <tt>set_payload</tt>. If this argument is <tt>VOID</tt>, then the + * contents of the payload remain undefined. + * + * @throws std::domain_error If applied with an invalid DeliveryType. + * @see ADAPRO::Data::DataPointValue::set_payload + */ + DataPointValue( + const uint16_t flags, + const uint16_t milliseconds, + const uint32_t seconds, + const uint64_t* const payload, + const DeliveryType type) + : flags(flags), msec(milliseconds), sec(seconds) + { + set_payload(payload, type); + } + + /** + * Sets the given payload data to the DataPointValue. This setter avoids + * copying garbage, because it uses the type information. However, it + * assumes the given array to have at least the length determined by the + * payload type. + * + * @param data A binary segment containing the new payload data. + * @param type Type of the payload data. + * + * @throws std::domain_error If applied with an invalid DeliveryType. + */ + inline void set_payload( + const uint64_t* const data, + const DeliveryType type) + { + switch (type) { + case RAW_INT: + case DPVAL_INT: + case RAW_UINT: + case DPVAL_UINT: + this->payload_pt1 = data[0] & 0x00000000FFFFFFFF; + break; + case RAW_BOOL: + case DPVAL_BOOL: + this->payload_pt1 = (data[0] ? 1 : 0); + break; + case RAW_CHAR: + case DPVAL_CHAR: + this->payload_pt1 = data[0] & 0x00000000000000FF; + break; + case RAW_DOUBLE: + case DPVAL_DOUBLE: + case RAW_TIME: + case DPVAL_TIME: + this->payload_pt1 = data[0]; + break; + case RAW_STRING: + case DPVAL_STRING: + std::strncpy((char*)&payload_pt1, (char*)data, 8); + std::strncpy((char*)&payload_pt2, (char*)data + 8, 8); + std::strncpy((char*)&payload_pt3, (char*)data + 16, 8); + std::strncpy((char*)&payload_pt4, (char*)data + 24, 8); + std::strncpy((char*)&payload_pt5, (char*)data + 32, 8); + std::strncpy((char*)&payload_pt6, (char*)data + 40, 8); + std::strncpy((char*)&payload_pt7, (char*)data + 48, 8); + break; + case RAW_BINARY: + case DPVAL_BINARY: + memcpy((void*)&payload_pt1, (void*)data, 56); + case VOID: + break; + default: + throw std::domain_error("Invalid DeliveryType."); + } + } + + /** + * Returns the ADAPOS flags of the DataPointValue. + * + * @return ADAPOS flags. + */ + inline uint16_t get_flags() const noexcept + { + return flags; + } + + /** + * Updates the timestamp of this DataPointValue object using system + * clock. + */ + inline void update_timestamp() noexcept + { + const uint64_t current_time(o2::dcs::epoch_time()); + msec = current_time % 1000; + sec = current_time / 1000; + } + + /** + * <p>On Linux platforms, Returns a unique pointer to a string + * containing an ISO 8601 timestamp. The value of the timestamp will be + * calculated from the values of the fields <tt>msec</tt> and + * <tt>sec</tt> and assuming the UTC timezone. The timestamp has the + * following format:</p> + * <p><tt><YYYY-MM-DD> "T" <HH:MM:SS.SSS> "Z"</tt></p> + * + * @return An ISO 8601 compliant timestamp. + */ + inline std::unique_ptr<std::string> get_timestamp() const noexcept + { +#if defined(__linux__) || defined(__APPLE__) + // time_t should be uint64_t (compatible) on 64-bit Linux platforms: + char buffer[17]; + std::time_t ts((uint64_t)sec); + std::strftime(buffer, 32, "%FT%T", std::gmtime(&ts)); + std::ostringstream oss; + oss << std::string(buffer) << "." << std::setw(3) << std::setfill('0') << std::to_string(msec) << "Z"; + return std::make_unique<std::string>( + std::move(oss.str())); +#else + return std::make_unique<std::string>("Unsupported platform"); +#endif + } + + /** + * Returns a 64-bit unsigned integer containing the timestamp value of + * the DPVAL object. + * + * @return Milliseconds since the UNIX epoch (1.1.1970 01:00:00.000). + */ + inline uint64_t get_epoch_time() const noexcept + { + return (((uint64_t)sec) * 1000) + msec; + } + + /** + * <p>The standard copy assignment operator. Performs a normal deep copy + * operation overwriting this DataPointValue object with the other.</p> + * <p><em>Note that this operator always copies a fixed size data + * segment from the given DPVAL to this DPVAL regardless of the payload + * value type. </em>Therefore, non-zero garbage data might be copied as + * well. Using the setters with the <tt>type</tt> parameter is + * recommended.</p> + * + * @param other The DataPointValue object, whose state will be copied to + * this object. + * @return <tt>*this</tt> after performing the copying. + */ + DataPointValue& operator=(const DataPointValue& other) = default; + + /** + * Bit-by bit equality comparison of DPVAL objects. + * + * @param other The second operand of equality comparison. + * @return <tt>true</tt> or <tt>false</tt>. + */ + inline bool operator==(const DataPointValue& other) const noexcept + { + return memcmp((void*)this, (void*)&other, sizeof(DataPointValue)) == 0; + } + + /** + * Negation of the equality comparison. + * + * @param other The second operand. + * @return <tt>true</tt> or <tt>false</tt>. + */ + inline bool operator!=(const DataPointValue& other) const noexcept + { + return !(*this == other); + } + + /** + * Sets the state of the DataPointValue. Copies a data segment from the + * array <tt>payload</tt>. Length of the copied data is determined by + * the argument <tt>type</tt>. <em>No sanity checks on the given array + * are performed.</em> + * + * @param flags New value for ADAPOS flags. + * @param milliseconds New value for milliseconds. + * @param seconds New value for seconds. + * @param payload New payload data. + * @param type Used for setting the payload correctly, using + * <tt>set_payload</tt>. + * + * @throws std::domain_error If applied with an invalid DeliveryType. + * @see ADAPRO::Data::DataPointValue::set_payload + */ + inline void set( + const uint16_t flags, + const uint16_t milliseconds, + const uint32_t seconds, + const uint64_t* const payload, + const DeliveryType type) + { + this->flags = flags; + this->msec = milliseconds; + this->sec = seconds; + set_payload(payload, type); + } + + /** + * Sets the state of the DataPointValue, except for the flags. This + * setter can be used for safely setting the state of the object without + * interfering with the locking mechanism or invalidating the mutual + * exclusion. This setter was added in <em>ADAPRO 2.4.0</em>. + * + * @param milliseconds Milliseconds of the new timestamp. + * @param seconds Seconds of the new timestamp. + * @param payload New payload. + * @param type Used for setting the payload correctly, using + * <tt>set_payload</tt>. + * + * @throws std::domain_error If applied with an invalid DeliveryType. + * @see ADAPRO::Data::DataPointValue::set_payload + */ + inline void set( + const uint16_t milliseconds, + const uint32_t seconds, + const uint64_t* const payload, + const DeliveryType type) + { + msec = milliseconds; + sec = seconds; + set_payload(payload, type); + } + + /** + * Sets the state of the DataPointValue. <em>No sanity checks on the + * given array are performed.</em> The first array element is assumed to + * contain data for the flags and the timestamp with their respective + * sizes and order, while the rest of the array is assumed to contain + * the payload data. + * + * @param data New DPVAL contents. + * @param type Used for setting the payload correctly, using + * <tt>set_payload</tt>. + * + * @throws std::domain_error If applied with an invalid DeliveryType. + * @see ADAPRO::Data::DataPointValue::set_payload + */ + inline void set( + const uint64_t* const data, + const DeliveryType type) + { + this->flags = data[0]; + this->msec = data[0] >> 16; + this->sec = data[0] >> 32; + set_payload(data + 1, type); + } + + /** + * Prints the flags and timestamp of the DataPointValue object into the + * <tt>ostream</tt>, but not the payload data. The reason for omitting + * the payload value is that DataPointValue lacks payload data typing + * information, so conversion from binary to string is not meaningful. + */ + friend inline std::ostream& operator<<(std::ostream& os, + const DataPointValue& dpval) noexcept + { + return os << ((dpval.flags & KEEP_ALIVE_FLAG) ? "A" : "-") + << ((dpval.flags & END_FLAG) ? "Z" : "-") + << ((dpval.flags & FBI_FLAG) ? "M" : "-") + << ((dpval.flags & NEW_FLAG) ? "N" : "-") + << ((dpval.flags & DIRTY_FLAG) ? "D" : "-") + << ((dpval.flags & TURN_FLAG) ? "U" : "-") + << ((dpval.flags & WRITE_FLAG) ? "W" : "-") + << ((dpval.flags & READ_FLAG) ? "R " : "- ") + << ((dpval.flags & OVERWRITE_FLAG) ? "O" : "-") + << ((dpval.flags & VICTIM_FLAG) ? "V" : "-") + << ((dpval.flags & DIM_ERROR_FLAG) ? "E" : "-") + << ((dpval.flags & BAD_DPID_FLAG) ? "I" : "-") + << ((dpval.flags & BAD_FLAGS_FLAG) ? "F" : "-") + << ((dpval.flags & BAD_TIMESTAMP_FLAG) ? "T" : "-") + << ((dpval.flags & BAD_PAYLOAD_FLAG) ? "P" : "-") + << ((dpval.flags & BAD_FBI_FLAG) ? "B;" : "-;") + << *(dpval.get_timestamp()); + } + + /** + * <p>Acquires the lock for writing into this DPVAL. This method is + * based on Peterson's algorithm (with a spinlock). It can be used for + * ensuring mutual exclusion between two threads accessing this DPVAL + * object. <em>It is important to release the write lock by calling + * <tt>release_write_lock()</tt> after use.</em></p> + * <p><em>This method may or may not work due to CPU instruction + * reordering, caching, and other optimizations.</em> Using + * <tt>std::mutex</tt> instead is recommended.</p> + * + * @see ADAPRO::ADAPOS::DataPointValue::release_write_lock + */ + inline void acquire_write_lock() + { + flags |= WRITE_FLAG; + flags |= TURN_FLAG; + while (flags & READ_FLAG && flags & TURN_FLAG) { + } + } + + /** + * <p>Releases the lock for writing into this DPVAL. This function is + * based on Peterson's algorithm (with a spinlock). It can be used for + * ensuring mutual exclusion between two threads accessing this DPVAL + * object.</p> + * <p><em>This method may or may not work due to CPU instruction + * reordering, caching, and other optimizations.</em> Using + * <tt>std::mutex</tt> instead is recommended.</p> + * + * @see ADAPRO::ADAPOS::DataPointValue::acquire_write_lock + */ + inline void release_write_lock() + { + flags &= ~WRITE_FLAG; + } + + /** + * <p>Acquires the lock for reading from this DPVAL. This function is + * based on Peterson's algorithm (with a spinlock). It can be used for + * ensuring mutual exclusion between two threads accessing this DPVAL + * object. <em>It is important to release the read lock by calling + * <tt>release_read_lock()</tt> after use.</em></p> + * <p><em>This method may or may not work due to CPU instruction + * reordering, caching, and other optimizations.</em> Using + * <tt>std::mutex</tt> instead is recommended.</p> + * + * @see ADAPRO::ADAPOS::DataPointValue::release_read_lock + */ + inline void acquire_read_lock() + { + flags |= READ_FLAG; + flags &= ~TURN_FLAG; + while (flags & WRITE_FLAG && !(flags & TURN_FLAG)) { + } + } + + /** + * <p>Releases the lock for reading from this DPVAL. This function is + * based on Peterson's algorithm (with a spinlock). It can be used for + * ensuring mutual exclusion between two threads accessing this DPVAL + * object.</p> + * <p><em>This method may or may not work due to CPU instruction + * reordering, caching, and other optimizations.</em> Using + * <tt>std::mutex</tt> instead is recommended.</p> + * + * @see ADAPRO::ADAPOS::DataPointValue::acquire_read_lock + */ + inline void release_read_lock() + { + flags &= ~READ_FLAG; + } + ClassDefNV(DataPointValue, 1); +}; +} // namespace dcs +} // namespace o2 + +#endif /* O2_DCS_DATAPOINT_VALUE_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/DeliveryType.h b/Detectors/DCS/include/DetectorsDCS/DeliveryType.h new file mode 100644 index 0000000000000..8da5b2452f1bc --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DeliveryType.h @@ -0,0 +1,380 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: DeliveryType.h + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 17 July 2015, 9:54 + */ + +#ifndef O2_DCS_DELIVERY_TYPE +#define O2_DCS_DELIVERY_TYPE + +#include <string> +#include <regex> +#include <stdexcept> +#include "DetectorsDCS/GenericFunctions.h" + +namespace o2 +{ +namespace dcs +{ +/** + * This regular expression matches with strings representing payload types. + */ +static const std::regex REGEX_PT( + "^(Raw|DPVAL)/(Int|Uint|Double|Bool|Char|String|Time|Binary)$"); + +/** + * <p>DeliveryType is a piece of meta-information used for deducing types of + * DPVAL payloads and DIM service description strings used with services + * providing data to ADAPOS. DPCOMs use the DeliveryType of DPIDs to infer + * the correct interpretation of payload data when being put into a + * <tt>std::ofstream</tt>. Also, ADAPOS Engine uses DeliveryTypes for DIM + * service subscription (and Load Generator for generating services).</p> + * <p>Every DeliveryType, except <tt>VOID</tt>, has a raw and DPVAL + * variant. A DIM service publishing payloads in DPVALs has a different + * format and binary layout than a DIM service publishing only raw data, so + * this distinction is critical to ADAPOS Engine.</p> + * + * @see ADAPRO::ADAPOS::DataPointCompositeObject + * @see ADAPRO::ADAPOS::DataPointIdentifier + * @see ADAPRO::ADAPOS::DataPointValue + */ +enum DeliveryType { + /** + * This DeliveryType is included only for testing and debugging + * when the payload of a DPVAL is not going to be accessed. Accessing + * the payload of a DPVAL with this DeliveryType results in a domain + * error. + */ + VOID = 0, + + /** + * <tt>Binary</tt> is the general payload data type. This is the raw + * variant. + */ + RAW_BINARY = 64, + + /** + * <tt>Binary</tt> is the general payload data type. This is the DPVAL + * variant. + */ + DPVAL_BINARY = 192, + + /** + * <tt>Int</tt> stands for a 32-bit signed integer. This is the raw + * variant. The numerical value of <tt>RAW_INT</tt> corresponds with the + * WinCC constant <tt>DPEL_INT</tt>. + */ + RAW_INT = 21, + + /** + * <tt>Int</tt> stands for a 32-bit signed integer. This is the DPVAL + * variant. + */ + DPVAL_INT = 149, + + /** + * <tt>Uint</tt> stands for a 32-bit unsigned integer. This is the raw + * variant. The numerical value of <tt>RAW_UINT</tt> corresponds with + * the WinCC constant <tt>DPEL_UINT</tt>. + */ + RAW_UINT = 20, + + /** + * <tt>Uint</tt> stands for a 32-bit insigned integer. This is the DPVAL + * variant. + */ + DPVAL_UINT = 148, + + /** + * <tt>Double</tt> stands for IEEE 754 double precision floating point + * number (64 bit). This is the raw variant. The numerical value of + * <tt>RAW_INT</tt> corresponds with the WinCC constant + * <tt>DPEL_FLOAT</tt>. + */ + RAW_DOUBLE = 22, + + /** + * <tt>Double</tt> stands for IEEE 754 double precision floating point + * number (64 bit). This is the DPVAL variant. + */ + DPVAL_DOUBLE = 150, + + /** + * <tt>Bool</tt> stands for a boolean. This is the raw variant. The + * numerical value of <tt>RAW_BOOL</tt> corresponds with the WinCC + * constant <tt>DPEL_BOOL</tt>. + */ + RAW_BOOL = 23, + + /** + * <tt>Bool</tt> stands for a boolean. This is the DPVAL variant. + */ + DPVAL_BOOL = 151, + + /** + * <tt>Char</tt> stands for an ASCII character. This is the raw variant. + * The numerical value of <tt>RAW_CHAR</tt> corresponds with the WinCC + * constant <tt>DPEL_CHAR</tt>. + */ + RAW_CHAR = 19, + + /** + * <tt>Char</tt> stands for an ASCII character. This is the raw variant. + */ + DPVAL_CHAR = 147, + + /** + * <tt>String</tt> stands for a null-terminated array of ASCII + * characters. This is the raw variant. The numerical value of + * <tt>RAW_STRING</tt> corresponds with the WinCC constant + * <tt>DPEL_STRING</tt>. + */ + RAW_STRING = 25, + + /** + * <tt>String</tt> stands for a null-terminated array of ASCII + * characters. This is the DPVAL variant. + */ + DPVAL_STRING = 153, + + /** + * <tt>Time</tt> stands for two 32-bit (unsigned) integers containing + * the milliseconds and secodns of a UNIX timestamp. This is the raw + * variant. The numerical value of <tt>RAW_TIME</tt> corresponds with + * the WinCC constant <tt>DPEL_DYN_UINT</tt>. + */ + RAW_TIME = 4, + + /** + * <tt>Time</tt> stands for two 32-bit (unsigned) integers containing + * the milliseconds and secodns of a UNIX timestamp. This is the DPVAL + * variant. + */ + DPVAL_TIME = 132 +}; + +template <> +inline DeliveryType read(const std::string& str) +{ + if (str == "Raw/Int") { + return RAW_INT; + } else if (str == "Raw/Uint") { + return RAW_UINT; + } else if (str == "Raw/Double") { + return RAW_DOUBLE; + } else if (str == "Raw/Bool") { + return RAW_BOOL; + } else if (str == "Raw/Char") { + return RAW_CHAR; + } else if (str == "Raw/String") { + return RAW_STRING; + } else if (str == "Raw/Binary") { + return RAW_BINARY; + } else if (str == "Raw/Time") { + return RAW_TIME; + } else if (str == "DPVAL/Int") { + return DPVAL_INT; + } else if (str == "DPVAL/Uint") { + return DPVAL_UINT; + } else if (str == "DPVAL/Double") { + return DPVAL_DOUBLE; + } else if (str == "DPVAL/Bool") { + return DPVAL_BOOL; + } else if (str == "DPVAL/Char") { + return DPVAL_CHAR; + } else if (str == "DPVAL/String") { + return DPVAL_STRING; + } else if (str == "DPVAL/Binary") { + return DPVAL_BINARY; + } else if (str == "DPVAL/Time") { + return DPVAL_TIME; + } else if (str == "Void") { + return VOID; + } else { + throw std::domain_error("\"" + str + + "\" doesn't represent a DeliveryType."); + } +} + +template <> +inline std::string show(const DeliveryType type) +{ + switch (type) { + case RAW_INT: + return "Raw/Int"; + case RAW_UINT: + return "Raw/Uint"; + case RAW_BOOL: + return "Raw/Bool"; + case RAW_CHAR: + return "Raw/Char"; + case RAW_DOUBLE: + return "Raw/Double"; + case RAW_TIME: + return "Raw/Time"; + case RAW_STRING: + return "Raw/String"; + case RAW_BINARY: + return "Raw/Binary"; + case DPVAL_INT: + return "DPVAL/Int"; + case DPVAL_UINT: + return "DPVAL/Uint"; + case DPVAL_BOOL: + return "DPVAL/Bool"; + case DPVAL_CHAR: + return "DPVAL/Char"; + case DPVAL_DOUBLE: + return "DPVAL/Double"; + case DPVAL_TIME: + return "DPVAL/Time"; + case DPVAL_STRING: + return "DPVAL/String"; + case DPVAL_BINARY: + return "DPVAL/Binary"; + case VOID: + return "Void"; + default: + throw std::domain_error("Illegal DeliveryType."); + } +} + +/** + * Returns <tt>true</tt> if and only if the given DeliveryType is a DPVAL + * variant. + * + * @param type The DeliveryType to be checked. + * @return The result. + * @throws std::domain_error If applied to an invalid value. + */ +inline bool DPVAL_variant(const DeliveryType type) +{ + switch (type) { + case DPVAL_INT: + case DPVAL_UINT: + case DPVAL_BOOL: + case DPVAL_CHAR: + case DPVAL_DOUBLE: + case DPVAL_TIME: + case DPVAL_STRING: + case DPVAL_BINARY: + return true; + case RAW_INT: + case RAW_UINT: + case RAW_BOOL: + case RAW_CHAR: + case RAW_DOUBLE: + case RAW_TIME: + case RAW_STRING: + case RAW_BINARY: + case VOID: + return false; + default: + throw std::domain_error("Illegal DeliveryType."); + } +} + +/** + * Returns the DIM description string for a DIM service with the given + * DeliveryType. + * + * @param type The DeliveryType. + * @return The corresponding DIM service description string. + * @throws std::domain_error If applied to an invalid value. + */ +inline std::string dim_description(const DeliveryType type) +{ + + switch (type) { + case RAW_INT: + return "I:1"; + case RAW_UINT: + return "I:1"; + case RAW_BOOL: + return "I:1"; + case RAW_CHAR: + return "C:4"; + case RAW_DOUBLE: + return "D:1"; + case RAW_TIME: + return "I:2"; + case RAW_STRING: + return "C:55"; + case RAW_BINARY: + return "C:56"; + case DPVAL_INT: + return "S:2;I:1;I:1"; + case DPVAL_UINT: + return "S:2;I:1;I:1"; + case DPVAL_BOOL: + return "S:2;I:1;I:1"; + case DPVAL_CHAR: + return "S:2;I:1;C:4"; + case DPVAL_DOUBLE: + return "S:2;I:1;D:1"; + case DPVAL_TIME: + return "S:2;I:1;I:2"; + case DPVAL_STRING: + return "S:2;I:1;C:55"; + case DPVAL_BINARY: + return "S:2;I:1;C:56"; + case VOID: + default: + throw std::domain_error("Illegal DeliveryType."); + } +} + +/** + * Returns the size of a buffer required to store the binary contents of a + * DIM service of the given data type. + * + * @param type The DeliveryType. + * @return Size of the payload. + * @throws std::domain_error If applied to an invalid value. + */ +inline size_t dim_buffer_size(const DeliveryType type) +{ + switch (type) { + case RAW_INT: + case RAW_UINT: + case RAW_BOOL: + case RAW_CHAR: + return 4; + case RAW_DOUBLE: + case RAW_TIME: + return 8; + case RAW_STRING: + return 55; + case RAW_BINARY: + return 56; + case DPVAL_INT: + case DPVAL_UINT: + case DPVAL_BOOL: + case DPVAL_CHAR: + case DPVAL_DOUBLE: + case DPVAL_TIME: + case DPVAL_STRING: + case DPVAL_BINARY: + return 64; + case VOID: + return 0; + default: + throw std::domain_error("Illegal DeliveryType."); + } +} +} // namespace dcs + +} // namespace o2 + +#endif /* O2_DCS_DELIVERY_TYPE */ diff --git a/Detectors/DCS/include/DetectorsDCS/GenericFunctions.h b/Detectors/DCS/include/DetectorsDCS/GenericFunctions.h new file mode 100644 index 0000000000000..91937d33f30a3 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/GenericFunctions.h @@ -0,0 +1,54 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: GenericFunctions.h + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 9 June 2017, 17:29 + */ + +#ifndef O2_DCS_GENERIC_FUNCTIONS_H +#define O2_DCS_GENERIC_FUNCTIONS_H + +#include <string> + +namespace o2 +{ +namespace dcs +{ +/** + * This function template is used for converting ADAPRO enumerated values + * into strings in a fashion similar to the function show in the Show type + * class in Haskell. The exact implementation depends on the specialization. + * + * @param input A T value to be converted. + * @return A string representation of the given value. + * @throws std::domain_error The specialized function may throw a domain + * error if applied with an invalid input value. + */ +template <typename T> +std::string show(const T input); + +/** + * This function template is used for parsing strings as ADAPRO enumerated + * values. The exact implementation depends on the specialization. + * + * @param input A string to be interpreted as a T value. + * @return The T value corresponding with the input. + * @throws std::domain_error The specialized function may throw a domain + * error if the given string couldn't be converted into a T value. + */ +template <typename T> +T read(const std::string& input); +} // namespace dcs +} // namespace o2 + +#endif /* O2_DCS_GENERIC_FUNCTIONS_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/StringUtils.h b/Detectors/DCS/include/DetectorsDCS/StringUtils.h new file mode 100644 index 0000000000000..adf0dd2aad59d --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/StringUtils.h @@ -0,0 +1,156 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: StringUtils.h + * Author: John Lång (john.larry.lang@cern.ch) + * + * Created on 14 July 2015, 10:37 + * + * This library contains miscellaneous functions used in this program for + * handling (C-style) strings. + */ + +#ifndef O2_DCS_STRING_UTILS_H +#define O2_DCS_STRING_UTILS_H + +#include <string> +#include <vector> +#include <list> +#include <memory> + +namespace o2 +{ +namespace dcs +{ +/** + * Returns an uniformly distributed string of exactly the given length. The + * alphabet of the string is ASCII characters between <tt>32</tt> + * (<tt>' '</tt>) and <tt>126</tt> (<tt>'~'</tt>), inclusive. + * + * @param length Length for the random string. + * @return The random string. + */ +std::string random_string(const size_t length) noexcept; + +/** + * Returns a random string of exactly the given length. The alphabet for the + * string contains upper case letters, digits and the symbols <tt>'_'</tt>, + * <tt>'/'</tt>, and <tt>'?'</tt>. Their distribution is geometric and tries + * to roughly approximate the symbol frequencies in ALICE DCS DIM service + * aliases. + * + * @param length Length for the random string. + * @return The random string. + */ +std::string random_string2(const size_t length) noexcept; + +/** + * Calculates a hash code for the given string. Up to 52 first characters of + * the string contribute to the hash code. The hash code is case + * insensitive, so for example <tt>"abc"</tt> and <tt>"ABC"</tt> will have + * colliding hash codes. + * + * @param input A string. + * @return An unsigned integer. + */ +uint64_t hash_code(const std::string& input) noexcept; + +/** + * Converts the C-style strings of the command line parameters to a more + * civilized data type. + */ +std::unique_ptr<std::vector<std::string>> convert_strings(const int argc, + char** argv) noexcept; + +/** + * Converts the given string into upper case. <em>This function will change + * the state of the given string</em>, instead of returning a copy. + * + * @param str The string to be converted into upper case. + */ +inline void to_upper_case(std::string& str) noexcept +{ + for (char& c : str) { + c = toupper(c); + } +} + +/** + * Converts the given string into lower case. <em>This function will change + * the state of the given string</em>, instead of returning a copy. + * + * @param str The string to be converted into lower case. + */ +inline void to_lower_case(std::string& str) noexcept +{ + for (char& c : str) { + c = tolower(c); + } +} + +/** + * Splits a string using the given separator. + * + * @param source The string to be splitted. + * @param separator The delimiting character. + * @return A vector containing the substrings. + */ +std::unique_ptr<std::vector<std::string>> split(const std::string& source, + const char separator) noexcept; + +/** + * Splits a string using whitespace as separator. Only the non-whitespace + * substrings will be returned. + * + * @param source The string to be splitted. + * @return A vector containing the substrings. + */ +std::unique_ptr<std::vector<std::string>> split_by_whitespace( + const std::string& source) noexcept; + +/** + * Returns a simple big endian hexadecimal presentation of the given + * segment of memory. + * + * @param start Address of a memory segment. + * @param length Length of the memory segment. + * @return A hexadecimal string representation of the segment in + * bytes. + */ +std::string to_hex_big_endian(const char* const start, size_t length) noexcept; + +/** + * Returns a simple little endian hexadecimal presentation of the given + * segment of memory. + * + * @param start Address of a memory segment. + * @param length Length of the memory segment (in chars). + * @return A hexadecimal string representation of the segment in + * bytes. + */ +std::string to_hex_little_endian(const char* const start, size_t length) noexcept; + +/** + * Prints the given list of key-value pairs. + * + * @param logger The Logger instance used for output. + * @param list_name Name of the list to be printed as a heading. + * @param parameters The list to be printed + */ + +void print_k_v_list( + const std::string& list_name, + const std::list<std::pair<std::string, std::string>>& parameters) noexcept; + +} // namespace dcs +} // namespace o2 + +#endif /* O2_DCS_STRING_UTILS_H */ diff --git a/Detectors/DCS/src/AliasExpander.cxx b/Detectors/DCS/src/AliasExpander.cxx new file mode 100644 index 0000000000000..e2f7448ad5696 --- /dev/null +++ b/Detectors/DCS/src/AliasExpander.cxx @@ -0,0 +1,145 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/AliasExpander.h" +#include <fmt/format.h> +#include <sstream> + +namespace +{ + +std::vector<std::string> splitString(const std::string& src, char delim) +{ + std::stringstream ss(src); + std::string token; + std::vector<std::string> tokens; + + while (std::getline(ss, token, delim)) { + if (!token.empty()) { + tokens.push_back(std::move(token)); + } + } + + return tokens; +} + +std::vector<std::string> extractList(const std::string& slist) +{ + auto dots = slist.find(","); + if (dots == std::string::npos) { + return {}; + } + return splitString(slist, ','); +} + +std::vector<std::string> extractRange(std::string range) +{ + auto dots = range.find(".."); + if (dots == std::string::npos) { + return extractList(range); + } + + auto braceStart = range.find("{"); + auto braceEnd = range.find("}"); + + if ( + (braceStart != std::string::npos && + braceEnd == std::string::npos) || + (braceStart == std::string::npos && + braceEnd != std::string::npos)) { + // incomplete custom pattern + return {}; + } + + std::string intFormat; + std::string sa, sb; + + if (braceStart != std::string::npos && + braceEnd != std::string::npos) { + intFormat = range.substr(braceStart, braceEnd - braceStart + 1); + range.erase(braceStart, braceEnd); + dots = range.find(".."); + sa = range.substr(0, dots); + sb = range.substr(dots + 2); + } else { + sa = range.substr(0, dots); + sb = range.substr(dots + 2); + auto size = std::max(sa.size(), sb.size()); + intFormat = "{:" + fmt::format("0{}d", size) + "}"; + } + + auto a = std::stoi(sa); + auto b = std::stoi(sb); + std::vector<std::string> result; + + for (auto i = a; i <= b; i++) { + auto substituted = fmt::format(intFormat, i); + result.push_back(substituted); + } + return result; +} +} // namespace + +namespace o2::dcs +{ +std::vector<std::string> expandAlias(const std::string& pattern) +{ + auto leftBracket = pattern.find("["); + auto rightBracket = pattern.find("]"); + + // no bracket at all -> return pattern simply + if (leftBracket == std::string::npos && rightBracket == std::string::npos) { + return {pattern}; + } + + // no matching bracket -> wrong pattern -> return nothing + if ((leftBracket == std::string::npos && + rightBracket != std::string::npos) || + (leftBracket != std::string::npos && + rightBracket == std::string::npos)) { + return {}; + } + auto rangeStr = pattern.substr(leftBracket + 1, rightBracket - leftBracket - 1); + + auto range = extractRange(rangeStr); + + // incorrect range -> return nothing + if (range.empty()) { + return {}; + } + + auto newPattern = pattern.substr(0, leftBracket) + + "{:s}" + + pattern.substr(rightBracket + 1); + + std::vector<std::string> result; + + for (auto r : range) { + auto substituted = fmt::format(newPattern, r); + result.emplace_back(substituted); + } + + return o2::dcs::expandAliases(result); +} + +std::vector<std::string> expandAliases(const std::vector<std::string>& patternedAliases) +{ + std::vector<std::string> result; + + for (auto a : patternedAliases) { + auto e = expandAlias(a); + result.insert(result.end(), e.begin(), e.end()); + } + // sort to get a predictable result + std::sort(result.begin(), result.end()); + + return result; +} +} // namespace o2::dcs diff --git a/Detectors/DCS/src/Clock.cxx b/Detectors/DCS/src/Clock.cxx new file mode 100644 index 0000000000000..e8fb9eac9e0ea --- /dev/null +++ b/Detectors/DCS/src/Clock.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/Clock.h" diff --git a/Detectors/DCS/src/DCSProcessor.cxx b/Detectors/DCS/src/DCSProcessor.cxx new file mode 100644 index 0000000000000..3b54607a6e5ee --- /dev/null +++ b/Detectors/DCS/src/DCSProcessor.cxx @@ -0,0 +1,547 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <DetectorsDCS/DCSProcessor.h> +#include "Rtypes.h" +#include <deque> +#include <string> +#include <algorithm> +#include <iterator> + +using namespace o2::dcs; + +using DeliveryType = o2::dcs::DeliveryType; +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; + +//ClassImp(o2::dcs::DCSProcessor); + +void DCSProcessor::init(const std::vector<DPID>& pidschars, const std::vector<DPID>& pidsints, + const std::vector<DPID>& pidsdoubles, const std::vector<DPID>& pidsUints, + const std::vector<DPID>& pidsbools, const std::vector<DPID>& pidsstrings, + const std::vector<DPID>& pidstimes, const std::vector<DPID>& pidsbinaries) +{ + + // init from separate vectors of pids (one per data point type) + + // chars + for (auto it = std::begin(pidschars); it != std::end(pidschars); ++it) { + if ((*it).get_type() != DeliveryType::RAW_CHAR) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a char"; + } + mPidschars.emplace_back((*it).get_alias(), DeliveryType::RAW_CHAR); + mPids[*it] = mPidschars.size() - 1; + } + + // ints + for (auto it = std::begin(pidsints); it != std::end(pidsints); ++it) { + if ((*it).get_type() != DeliveryType::RAW_INT) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a int"; + } + mPidsints.emplace_back((*it).get_alias(), DeliveryType::RAW_INT); + mPids[*it] = mPidsints.size() - 1; + } + + // doubles + for (auto it = std::begin(pidsdoubles); it != std::end(pidsdoubles); ++it) { + if ((*it).get_type() != DeliveryType::RAW_DOUBLE) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a double"; + } + mPidsdoubles.emplace_back((*it).get_alias(), DeliveryType::RAW_DOUBLE); + mPids[*it] = mPidsdoubles.size() - 1; + } + + // uints + for (auto it = std::begin(pidsUints); it != std::end(pidsUints); ++it) { + if ((*it).get_type() != DeliveryType::RAW_UINT) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a uint"; + } + mPidsUints.emplace_back((*it).get_alias(), DeliveryType::RAW_UINT); + mPids[*it] = mPidsUints.size() - 1; + } + + // bools + for (auto it = std::begin(pidsbools); it != std::end(pidsbools); ++it) { + if ((*it).get_type() != DeliveryType::RAW_BOOL) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a bool"; + } + mPidsbools.emplace_back((*it).get_alias(), DeliveryType::RAW_BOOL); + mPids[*it] = mPidsbools.size() - 1; + } + + // strings + for (auto it = std::begin(pidsstrings); it != std::end(pidsstrings); ++it) { + if ((*it).get_type() != DeliveryType::RAW_STRING) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a string"; + } + mPidsstrings.emplace_back((*it).get_alias(), DeliveryType::RAW_STRING); + mPids[*it] = mPidsstrings.size() - 1; + } + + // times + for (auto it = std::begin(pidstimes); it != std::end(pidstimes); ++it) { + if ((*it).get_type() != DeliveryType::RAW_TIME) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a time"; + } + mPidstimes.emplace_back((*it).get_alias(), DeliveryType::RAW_TIME); + mPids[*it] = mPidstimes.size() - 1; + } + + // binaries + for (auto it = std::begin(pidsbinaries); it != std::end(pidsbinaries); ++it) { + if ((*it).get_type() != DeliveryType::RAW_BINARY) { + LOG(FATAL) << "Type for data point " << *it << " does not match with expectations! It should be a binary"; + } + mPidsbinaries.emplace_back((*it).get_alias(), DeliveryType::RAW_BINARY); + mPids[*it] = mPidsbinaries.size() - 1; + } + + mLatestTimestampchars.resize(pidschars.size(), 0); + mLatestTimestampints.resize(pidsints.size(), 0); + mLatestTimestampdoubles.resize(pidsdoubles.size(), 0); + mLatestTimestampUints.resize(pidsUints.size(), 0); + mLatestTimestampbools.resize(pidsbools.size(), 0); + mLatestTimestampstrings.resize(pidsstrings.size(), 0); + mLatestTimestamptimes.resize(pidstimes.size(), 0); + mLatestTimestampbinaries.resize(pidsbinaries.size(), 0); +} + +//______________________________________________________________________ + +void DCSProcessor::init(const std::vector<DPID>& pids) +{ + + int nchars = 0, nints = 0, ndoubles = 0, nUints = 0, + nbools = 0, nstrings = 0, ntimes = 0, nbinaries = 0; + for (auto it = std::begin(pids); it != std::end(pids); ++it) { + if ((*it).get_type() == DeliveryType::RAW_CHAR) { + mPidschars.emplace_back((*it).get_alias(), DeliveryType::RAW_CHAR); + mPids[*it] = nchars; + nchars++; + } + if ((*it).get_type() == DeliveryType::RAW_INT) { + mPidsints.emplace_back((*it).get_alias(), DeliveryType::RAW_INT); + mPids[*it] = nints; + nints++; + } + if ((*it).get_type() == DeliveryType::RAW_DOUBLE) { + mPidsdoubles.emplace_back((*it).get_alias(), DeliveryType::RAW_DOUBLE); + mPids[*it] = ndoubles; + ndoubles++; + } + if ((*it).get_type() == DeliveryType::RAW_UINT) { + mPidsUints.emplace_back((*it).get_alias(), DeliveryType::RAW_UINT); + mPids[*it] = nUints; + nUints++; + } + if ((*it).get_type() == DeliveryType::RAW_BOOL) { + mPidsbools.emplace_back((*it).get_alias(), DeliveryType::RAW_BOOL); + mPids[*it] = nbools; + nbools++; + } + if ((*it).get_type() == DeliveryType::RAW_STRING) { + mPidsstrings.emplace_back((*it).get_alias(), DeliveryType::RAW_STRING); + mPids[*it] = nstrings; + nstrings++; + } + if ((*it).get_type() == DeliveryType::RAW_TIME) { + mPidstimes.emplace_back((*it).get_alias(), DeliveryType::RAW_TIME); + mPids[*it] = ntimes; + ntimes++; + } + if ((*it).get_type() == DeliveryType::RAW_BINARY) { + mPidsbinaries.emplace_back((*it).get_alias(), DeliveryType::RAW_BINARY); + mPids[*it] = nbinaries; + nbinaries++; + } + } + + mLatestTimestampchars.resize(nchars, 0); + mLatestTimestampints.resize(nints, 0); + mLatestTimestampdoubles.resize(ndoubles, 0); + mLatestTimestampUints.resize(nUints, 0); + mLatestTimestampbools.resize(nbools, 0); + mLatestTimestampstrings.resize(nstrings, 0); + mLatestTimestamptimes.resize(ntimes, 0); + mLatestTimestampbinaries.resize(nbinaries, 0); +} + +//__________________________________________________________________ + +int DCSProcessor::processMap(const std::unordered_map<DPID, DPVAL>& map, bool isDelta) +{ + + // process function to do "something" with the DCS map that is passed + + // resetting the content of the CCDB object to be sent + + if (!isDelta) { + // full map sent + mFullMapSent = true; + } else { + if (!mFullMapSent) { + LOG(ERROR) << "We need first a full map!"; + } + mNCyclesNoFullMap++; + if (mNCyclesNoFullMap > mMaxCyclesNoFullMap) { + LOG(ERROR) << "We expected a full map!"; + } + } + + mIsDelta = isDelta; + + // we need to check if there are the Data Points that we need + + int foundChars = 0, foundInts = 0, foundDoubles = 0, foundUInts = 0, + foundBools = 0, foundStrings = 0, foundTimes = 0, foundBinaries = 0; + + // char type + foundChars = processArrayType(mPidschars, DeliveryType::RAW_CHAR, map, mLatestTimestampchars, mDpscharsmap); + + // int type + foundInts = processArrayType(mPidsints, DeliveryType::RAW_INT, map, mLatestTimestampints, mDpsintsmap); + + // double type + foundDoubles = processArrayType(mPidsdoubles, DeliveryType::RAW_DOUBLE, map, mLatestTimestampdoubles, + mDpsdoublesmap); + + // UInt type + foundUInts = processArrayType(mPidsUints, DeliveryType::RAW_UINT, map, mLatestTimestampUints, mDpsUintsmap); + + // Bool type + foundBools = processArrayType(mPidsbools, DeliveryType::RAW_BOOL, map, mLatestTimestampbools, mDpsboolsmap); + + // String type + foundStrings = processArrayType(mPidsstrings, DeliveryType::RAW_STRING, map, mLatestTimestampstrings, + mDpsstringsmap); + + // Time type + foundTimes = processArrayType(mPidstimes, DeliveryType::RAW_TIME, map, mLatestTimestamptimes, mDpstimesmap); + + // Binary type + foundBinaries = processArrayType(mPidsbinaries, DeliveryType::RAW_BINARY, map, mLatestTimestampbinaries, + mDpsbinariesmap); + + if (!isDelta) { + if (foundChars != mPidschars.size()) { + LOG(INFO) << "Not all expected char-typed DPs found!"; + } + if (foundInts != mPidsints.size()) { + LOG(INFO) << "Not all expected int-typed DPs found!"; + } + if (foundDoubles != mPidsdoubles.size()) { + LOG(INFO) << "Not all expected double-typed DPs found!"; + } + if (foundUInts != mPidsUints.size()) { + LOG(INFO) << "Not all expected uint-typed DPs found!"; + } + if (foundBools != mPidsbools.size()) { + LOG(INFO) << "Not all expected bool-typed DPs found!"; + } + if (foundStrings != mPidsstrings.size()) { + LOG(INFO) << "Not all expected string-typed DPs found!"; + } + if (foundTimes != mPidstimes.size()) { + LOG(INFO) << "Not all expected time-typed DPs found!"; + } + if (foundBinaries != mPidsbinaries.size()) { + LOG(INFO) << "Not all expected binary-typed DPs found!"; + } + } + + // filling CCDB info to be sent in output + std::map<std::string, std::string> md; + prepareCCDBobject(mccdbSimpleMovingAverage, mccdbSimpleMovingAverageInfo, + mName + "/TestDCS/SimpleMovingAverageDPs", mTF, md); + + LOG(DEBUG) << "Size of unordered_map for CCDB = " << mccdbSimpleMovingAverage.size(); + LOG(DEBUG) << "CCDB entry for TF " << mTF << " will be:"; + for (const auto& i : mccdbSimpleMovingAverage) { + LOG(DEBUG) << i.first << " --> " << i.second; + } + + return 0; +} + +//__________________________________________________________________ + +int DCSProcessor::processDP(const std::pair<DPID, DPVAL>& dpcom) +{ + + // processing single DP + + DPID dpid = dpcom.first; + DeliveryType type = dpid.get_type(); + + // first we check if the DP is in the list for the detector + auto el = mPids.find(dpid); + if (el == mPids.end()) { + LOG(ERROR) << "DP not found for this detector, please check"; + return 1; + } + int posInVector = (*el).second; + + if (type == DeliveryType::RAW_CHAR) { + checkFlagsAndFill(dpcom, mLatestTimestampchars[posInVector], mDpscharsmap); + processCharDP(dpid); + } + + else if (type == DeliveryType::RAW_INT) { + checkFlagsAndFill(dpcom, mLatestTimestampints[posInVector], mDpsintsmap); + processIntDP(dpid); + } + + else if (type == DeliveryType::RAW_DOUBLE) { + checkFlagsAndFill(dpcom, mLatestTimestampdoubles[posInVector], mDpsdoublesmap); + processDoubleDP(dpid); + } + + else if (type == DeliveryType::RAW_UINT) { + checkFlagsAndFill(dpcom, mLatestTimestampUints[posInVector], mDpsUintsmap); + processUIntDP(dpid); + } + + else if (type == DeliveryType::RAW_BOOL) { + checkFlagsAndFill(dpcom, mLatestTimestampbools[posInVector], mDpsboolsmap); + processBoolDP(dpid); + } + + else if (type == DeliveryType::RAW_STRING) { + checkFlagsAndFill(dpcom, mLatestTimestampstrings[posInVector], mDpsstringsmap); + processStringDP(dpid); + } + + else if (type == DeliveryType::RAW_TIME) { + checkFlagsAndFill(dpcom, mLatestTimestamptimes[posInVector], mDpstimesmap); + processTimeDP(dpid); + } + + else if (type == DeliveryType::RAW_BINARY) { + checkFlagsAndFill(dpcom, mLatestTimestampbinaries[posInVector], mDpsbinariesmap); + processBinaryDP(dpid); + } + + return 0; +} + +//______________________________________________________________________ + +template <> +void DCSProcessor::checkFlagsAndFill(const std::pair<DPID, DPVAL>& dpcom, uint64_t& latestTimeStamp, + std::unordered_map<DPID, DQStrings>& destmap) +{ + + // check the flags for the upcoming data, and if ok, fill the accumulator + + auto& dpid = dpcom.first; + auto& val = dpcom.second; + auto flags = val.get_flags(); + if (processFlag(flags, dpid.get_alias()) == 0) { + auto etime = val.get_epoch_time(); + // fill only if new value has a timestamp different from the timestamp of the previous one + LOG(DEBUG) << "destmap[pid].size() = " << destmap[dpid].size(); + if (destmap[dpid].size() == 0 || etime != latestTimeStamp) { + auto& tmp = destmap[dpid].emplace_back(); + std::strncpy(tmp.data(), (char*)&(val.payload_pt1), 56); + latestTimeStamp = etime; + } + } +} + +//______________________________________________________________________ + +template <> +void DCSProcessor::checkFlagsAndFill(const std::pair<DPID, DPVAL>& dpcom, uint64_t& latestTimeStamp, + std::unordered_map<DPID, DQBinaries>& destmap) +{ + + // check the flags for the upcoming data, and if ok, fill the accumulator + + auto& dpid = dpcom.first; + auto& val = dpcom.second; + auto flags = val.get_flags(); + if (processFlag(flags, dpid.get_alias()) == 0) { + auto etime = val.get_epoch_time(); + // fill only if new value has a timestamp different from the timestamp of the previous one + LOG(DEBUG) << "destmap[pid].size() = " << destmap[dpid].size(); + if (destmap[dpid].size() == 0 || etime != latestTimeStamp) { + auto& tmp = destmap[dpid].emplace_back(); + memcpy(tmp.data(), &(val.payload_pt1), 7); + latestTimeStamp = etime; + } + } +} + +//______________________________________________________________________ + +void DCSProcessor::processCharDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processIntDP(const DPID& pid) +{ + // processing the single pid of type int + bool isSMA = false; + doSimpleMovingAverage(2, mDpsintsmap[pid], mSimpleMovingAverage[pid], isSMA); + LOG(DEBUG) << "dpid = " << pid << " --> Moving average = " << mSimpleMovingAverage[pid]; + // create CCDB object + //if (isSMA) { + mccdbSimpleMovingAverage[pid.get_alias()] = mSimpleMovingAverage[pid]; + //} + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processDoubleDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processUIntDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processBoolDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processStringDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processTimeDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +void DCSProcessor::processBinaryDP(const DPID& pid) +{ + // empty for the example + return; +} + +//______________________________________________________________________ + +std::unordered_map<DPID, DPVAL>::const_iterator DCSProcessor::findAndCheckPid(const DPID& pid, + DeliveryType type, + const std::unordered_map<DPID, DPVAL>& + map) +{ + + // processing basic checks for map: all needed pids must be present + // finds dp defined by "pid" in received map "map" + + LOG(DEBUG) << "Processing " << pid; + auto it = map.find(pid); + DeliveryType tt = pid.get_type(); + if (tt != type) { + LOG(FATAL) << "Delivery Type of pid " << pid.get_alias() << " does not match definition in DCSProcessor (" + << type << ")! Please fix"; + } + return it; +} + +//______________________________________________________________________ + +uint64_t DCSProcessor::processFlag(const uint64_t flags, const char* pid) +{ + + // function to process the flag. the return code zero means that all is fine. + // anything else means that there was an issue + + if (flags & DataPointValue::KEEP_ALIVE_FLAG) { + LOG(INFO) << "KEEP_ALIVE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::END_FLAG) { + LOG(INFO) << "END_FLAG active for DP " << pid; + } + if (flags & DataPointValue::FBI_FLAG) { + LOG(INFO) << "FBI_FLAG active for DP " << pid; + } + if (flags & DataPointValue::NEW_FLAG) { + LOG(INFO) << "NEW_FLAG active for DP " << pid; + } + if (flags & DataPointValue::DIRTY_FLAG) { + LOG(INFO) << "DIRTY_FLAG active for DP " << pid; + } + if (flags & DataPointValue::TURN_FLAG) { + LOG(INFO) << "TURN_FLAG active for DP " << pid; + } + if (flags & DataPointValue::WRITE_FLAG) { + LOG(INFO) << "WRITE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::READ_FLAG) { + LOG(INFO) << "READ_FLAG active for DP " << pid; + } + if (flags & DataPointValue::OVERWRITE_FLAG) { + LOG(INFO) << "OVERWRITE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::VICTIM_FLAG) { + LOG(INFO) << "VICTIM_FLAG active for DP " << pid; + } + if (flags & DataPointValue::DIM_ERROR_FLAG) { + LOG(INFO) << "DIM_ERROR_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_DPID_FLAG) { + LOG(INFO) << "BAD_DPID_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_FLAGS_FLAG) { + LOG(INFO) << "BAD_FLAGS_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_TIMESTAMP_FLAG) { + LOG(INFO) << "BAD_TIMESTAMP_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_PAYLOAD_FLAG) { + LOG(INFO) << "BAD_PAYLOAD_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_FBI_FLAG) { + LOG(INFO) << "BAD_FBI_FLAG active for DP " << pid; + } + + return 0; +} + +//______________________________________________________________________ + +void DCSProcessor::setNThreads(int n) +{ + + // to set number of threads used to process the DPs + +#ifdef WITH_OPENMP + mNThreads = n > 0 ? n : 1; +#else + LOG(WARNING) << " Multithreading is not supported, imposing single thread"; + mNThreads = 1; +#endif +} diff --git a/Detectors/DCS/src/DataPointCompositeObject.cxx b/Detectors/DCS/src/DataPointCompositeObject.cxx new file mode 100644 index 0000000000000..2ec290215f0ad --- /dev/null +++ b/Detectors/DCS/src/DataPointCompositeObject.cxx @@ -0,0 +1,83 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointCompositeObject.h" + +using namespace o2::dcs; + +ClassImp(DataPointCompositeObject); + +namespace o2::dcs +{ +template <typename T, o2::dcs::DeliveryType dt> +T getValueImpl(const DataPointCompositeObject& dpcom) +{ + union Converter { + uint64_t raw_data; + T t_value; + }; + if (dpcom.id.get_type() != dt) { + throw std::runtime_error("DPCOM is of unexpected type " + o2::dcs::show(dt)); + } + Converter converter; + converter.raw_data = dpcom.data.payload_pt1; + return converter.t_value; +} + +// only specialize the getValue function for the types we support : +// +// - double +// - uint32_t +// - int32_t +// - char +// - bool +// +// - string + +template <> +double getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl<double, DeliveryType::RAW_DOUBLE>(dpcom); +} + +template <> +uint32_t getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl<uint32_t, DeliveryType::RAW_UINT>(dpcom); +} + +template <> +int32_t getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl<int32_t, DeliveryType::RAW_INT>(dpcom); +} + +template <> +char getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl<char, DeliveryType::RAW_CHAR>(dpcom); +} + +template <> +bool getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl<bool, DeliveryType::RAW_BOOL>(dpcom); +} + +template <> +std::string getValue(const DataPointCompositeObject& dpcom) +{ + if (dpcom.id.get_type() != o2::dcs::DeliveryType::RAW_STRING) { + throw std::runtime_error("DPCOM is of unexpected type " + o2::dcs::show(dpcom.id.get_type())); + } + return std::string((char*)&dpcom.data.payload_pt1); +} + +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointCreator.cxx b/Detectors/DCS/src/DataPointCreator.cxx new file mode 100644 index 0000000000000..6b11d76a7675e --- /dev/null +++ b/Detectors/DCS/src/DataPointCreator.cxx @@ -0,0 +1,66 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointCreator.h" + +namespace +{ +o2::dcs::DataPointCompositeObject createDPCOM(const std::string& alias, const uint64_t* val, uint32_t seconds, uint16_t msec, uint16_t flags, o2::dcs::DeliveryType dt) +{ + auto dpid = o2::dcs::DataPointIdentifier(alias, dt); + auto dpval = o2::dcs::DataPointValue( + flags, + msec, + seconds, + val, + dt); + return o2::dcs::DataPointCompositeObject(dpid, dpval); +} +} // namespace + +namespace o2::dcs +{ +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, double val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast<const uint64_t*>(&val), seconds, msec, flags, DeliveryType::RAW_DOUBLE); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, int32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast<const uint64_t*>(&val), seconds, msec, flags, DeliveryType::RAW_INT); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, uint32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast<const uint64_t*>(&val), seconds, msec, flags, DeliveryType::RAW_UINT); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, char val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast<const uint64_t*>(&val), seconds, msec, flags, DeliveryType::RAW_CHAR); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, bool val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast<const uint64_t*>(&val), seconds, msec, flags, DeliveryType::RAW_BOOL); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, std::string val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast<const uint64_t*>(val.c_str()), seconds, msec, flags, DeliveryType::RAW_STRING); +} + +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointGenerator.cxx b/Detectors/DCS/src/DataPointGenerator.cxx new file mode 100644 index 0000000000000..b0b64b550985a --- /dev/null +++ b/Detectors/DCS/src/DataPointGenerator.cxx @@ -0,0 +1,136 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/AliasExpander.h" +#include "DetectorsDCS/DataPointGenerator.h" +#include "DetectorsDCS/DataPointCreator.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/StringUtils.h" +#include "Framework/Logger.h" +#include <fmt/format.h> +#include <random> +#include <utility> +#include <type_traits> +#include <cstdint> + +namespace +{ +std::pair<uint32_t, uint16_t> getDate(const std::string& refDate) +{ + + uint32_t seconds; + if (refDate.empty()) { + auto current = std::time(nullptr); + auto t = std::localtime(¤t); + seconds = mktime(t); + } else { + std::tm t{}; + std::istringstream ss(refDate); + ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S"); + if (ss.fail()) { // let's see if it was passed as a TDatime + std::tm tt{}; + std::istringstream sss(refDate); + sss >> std::get_time(&tt, "%a %b %d %H:%M:%S %Y"); + if (sss.fail()) { + LOG(ERROR) << "We cannot parse the date"; + } + seconds = mktime(&tt); + } else { + seconds = mktime(&t); + } + } + uint16_t msec = 5; + return std::make_pair(seconds, msec); +} + +} // namespace + +namespace o2::dcs +{ + +//std::enable_if_t<std::is_arithmetic<T>::value, bool> = true> + +template <typename T> +std::vector<o2::dcs::DataPointCompositeObject> + generateRandomDataPoints(const std::vector<std::string>& aliases, + T minValue, T maxValue, std::string refDate) +{ + std::vector<o2::dcs::DataPointCompositeObject> dpcoms; + static_assert(std::is_arithmetic<T>::value, "T must be an arithmetic type"); + typedef typename std::conditional<std::is_integral<T>::value, + std::uniform_int_distribution<T>, + std::uniform_real_distribution<T>>::type distType; + + std::random_device rd; + std::mt19937 mt(rd()); + distType dist{minValue, maxValue}; + auto [seconds, msec] = getDate(refDate); + for (auto alias : expandAliases(aliases)) { + auto value = dist(mt); + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); + } + return dpcoms; +} + +// only specialize the functions for the types we support : +// +// - double +// - uint32_t +// - int32_t +// - char +// - bool +// +// - std::string + +template std::vector<o2::dcs::DataPointCompositeObject> generateRandomDataPoints<double>(const std::vector<std::string>& aliases, double minValue, double maxValue, std::string); + +template std::vector<o2::dcs::DataPointCompositeObject> generateRandomDataPoints<uint32_t>(const std::vector<std::string>& aliases, uint32_t minValue, uint32_t maxValue, std::string); + +template std::vector<o2::dcs::DataPointCompositeObject> generateRandomDataPoints<int32_t>(const std::vector<std::string>& aliases, int32_t minValue, int32_t maxValue, std::string); + +template std::vector<o2::dcs::DataPointCompositeObject> generateRandomDataPoints<char>(const std::vector<std::string>& aliases, char minValue, char maxValue, std::string); + +/** Need a specific specialization for bool as got into trouble compiling uniform_int_distribution<bool> + * on some platform (e.g. CC7). + */ +template <> +std::vector<o2::dcs::DataPointCompositeObject> generateRandomDataPoints<bool>(const std::vector<std::string>& aliases, bool minValue, bool maxValue, std::string refDate) +{ + std::vector<o2::dcs::DataPointCompositeObject> dpcoms; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist{0, 1}; + auto [seconds, msec] = getDate(refDate); + for (auto alias : expandAliases(aliases)) { + bool value = dist(mt); + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); + } + return dpcoms; +} + +/** + * Generate data points of type string, where each string is random, with + * a length between the length of the two input strings (minLength,maxLength) + */ +template <> +std::vector<o2::dcs::DataPointCompositeObject> generateRandomDataPoints<std::string>(const std::vector<std::string>& aliases, std::string minLength, std::string maxLength, std::string refDate) +{ + std::vector<o2::dcs::DataPointCompositeObject> dpcoms; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution<std::string::size_type> dist{minLength.size(), maxLength.size()}; + auto [seconds, msec] = getDate(refDate); + for (auto alias : expandAliases(aliases)) { + auto value = o2::dcs::random_string2(dist(mt)); + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); + } + return dpcoms; +} +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointIdentifier.cxx b/Detectors/DCS/src/DataPointIdentifier.cxx new file mode 100644 index 0000000000000..85906ad6dd408 --- /dev/null +++ b/Detectors/DCS/src/DataPointIdentifier.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointIdentifier.h" + +using namespace o2::dcs; + +ClassImp(DataPointIdentifier); diff --git a/Detectors/DCS/src/DataPointValue.cxx b/Detectors/DCS/src/DataPointValue.cxx new file mode 100644 index 0000000000000..dc5874b7e3a9b --- /dev/null +++ b/Detectors/DCS/src/DataPointValue.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointValue.h" + +using namespace o2::dcs; + +ClassImp(DataPointValue); diff --git a/Detectors/DCS/src/DeliveryType.cxx b/Detectors/DCS/src/DeliveryType.cxx new file mode 100644 index 0000000000000..6ff195fc8fd9d --- /dev/null +++ b/Detectors/DCS/src/DeliveryType.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DeliveryType.h" diff --git a/Detectors/DCS/src/DetectorsDCSLinkDef.h b/Detectors/DCS/src/DetectorsDCSLinkDef.h new file mode 100644 index 0000000000000..c5233fb26feef --- /dev/null +++ b/Detectors/DCS/src/DetectorsDCSLinkDef.h @@ -0,0 +1,26 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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(__CINT__) || defined(__CLING__) + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ struct o2::dcs::DataPointCompositeObject + ; +#pragma link C++ class o2::dcs::DataPointIdentifier + ; +#pragma link C++ struct o2::dcs::DataPointValue + ; +#pragma link C++ class o2::dcs::DCSProcessor + ; +#pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue> + ; +#pragma link C++ function o2::dcs::expandAlias(const std::string&); +#pragma link C++ function o2::dcs::expandAliases(const std::vector<std::string>&); +#pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, std::string> + ; + +#endif diff --git a/Detectors/DCS/src/GenericFunctions.cxx b/Detectors/DCS/src/GenericFunctions.cxx new file mode 100644 index 0000000000000..c8be0880092c4 --- /dev/null +++ b/Detectors/DCS/src/GenericFunctions.cxx @@ -0,0 +1,11 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/GenericFunctions.h" diff --git a/Detectors/DCS/src/StringUtils.cxx b/Detectors/DCS/src/StringUtils.cxx new file mode 100644 index 0000000000000..a0e96d17a5752 --- /dev/null +++ b/Detectors/DCS/src/StringUtils.cxx @@ -0,0 +1,383 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <cctype> +#include <cstdint> +#include <cmath> +#include <cstdlib> +#include <string> +#include <random> +#include <vector> +#include <list> +#include <memory> +#include <fstream> +#include <regex> +#include <map> +#include <functional> +#include <utility> +#include <sstream> +#include <istream> +#include <iomanip> +#include <sstream> +#include <iterator> +#include "DetectorsDCS/StringUtils.h" + +using namespace std; + +random_device dev; +default_random_engine gen(dev()); +uniform_int_distribution<char> uniform_dist(32, 126); +geometric_distribution<char> geom_dist(0.1); + +constexpr char ALPHABET[39]{ + 'E', 'T', 'A', 'O', 'I', '_', 'N', 'S', 'H', 'R', + '/', 'D', 'L', '1', '2', 'C', 'U', 'M', 'W', '3', + '4', '5', '6', '7', '8', '9', '0', 'F', 'G', 'Y', + 'P', 'B', 'V', 'K', 'J', 'X', 'Q', 'Z', '?'}; + +/** + * The first 52 prime numbers used for calculating the hash code. Each symbol of + * the alias will be multiplied (mod 256) with a prime of the same index and + * adding the obtained number to the sum that is the hash code. + */ +constexpr uint64_t PRIMES[52]{ + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239}; + +/** + * The modulo used in the hash function after calculating the sum. It is the + * prime number closest to 1.5 * 2^63. + */ +constexpr uint64_t MODULO(13835058055282163729ULL); + +/** + * Transforms the given character into a 8-bit unsigned code symbol. + * + * @param input + * @return + */ +uint8_t lookup(const char input) noexcept +{ + switch (input) { + case 'e': + case 'E': + return 0; + case 't': + case 'T': + return 1; + case 'a': + case 'A': + return 2; + case 'o': + case 'O': + return 3; + case 'i': + case 'I': + return 4; + case '_': + return 5; + case 'n': + case 'N': + return 6; + case 's': + case 'S': + return 7; + case 'h': + case 'H': + return 8; + case 'r': + case 'R': + return 9; + case '/': + return 10; + case 'd': + case 'D': + return 11; + case 'l': + case 'L': + return 12; + case '1': + return 13; + case '2': + return 14; + case 'c': + case 'C': + return 15; + case 'u': + case 'U': + return 16; + case 'm': + case 'M': + return 17; + case 'w': + case 'W': + return 18; + case '3': + return 19; + case '4': + return 20; + case '5': + return 21; + case '6': + return 22; + case '7': + return 23; + case '8': + return 24; + case '9': + return 25; + case '0': + return 26; + case 'f': + case 'F': + return 27; + case 'g': + case 'G': + return 28; + case 'y': + case 'Y': + return 29; + case 'p': + case 'P': + return 30; + case 'b': + case 'B': + return 31; + case 'v': + case 'V': + return 32; + case 'k': + case 'K': + return 33; + case 'j': + case 'J': + return 34; + case 'x': + case 'X': + return 35; + case 'q': + case 'Q': + return 36; + case 'z': + case 'Z': + return 37; + default: + return 38; + } +} + +/** + * An all-integer exponent function. The orgiginal algorithm was submitted as an + * answer at StackOverflow, by Elias Yarrkow. The code was obtained from + * http://stackoverflow.com/a/101613 in 14 February 2017, 11:24, and modified to + * work with the unsigned C integer types. It is well-known that these kind of + * functions usually overflow, and overflows are ignored in this case. + * + * @param base The base of the exponent function. + * @param exp The exponent. + * @return The result. + */ +uint64_t exp(uint64_t base, uint8_t exp) noexcept +{ + uint64_t result(1); + while (exp) { + if (exp & 1) { + result *= base; + } + exp >>= 1; + base *= base; + } + return result; +} + +string o2::dcs::random_string(const size_t length) noexcept +{ + string s; + s.reserve(length); + for (size_t i = 0; i < length; ++i) { + s.push_back(uniform_dist(gen)); + } + return s; +} + +string o2::dcs::random_string2(const size_t length) noexcept +{ + string s; + s.reserve(length); + for (size_t i = 0; i < length; ++i) { + s.push_back(ALPHABET[geom_dist(gen) % 39]); + } + return s; +} + +uint64_t o2::dcs::hash_code(const std::string& input) noexcept +{ + size_t result(0); + const size_t length(min(input.length(), (string::size_type)52)); + for (size_t i = 0; i < length; ++i) { + // result += exp(PRIMES[i], (uint8_t) input[i]); + result += exp(PRIMES[i], lookup(input[i])); + // result += exp(PRIMES[i], ((uint8_t) input[i]) & 0x1F); + } + return result; +} + +unique_ptr<vector<string>> o2::dcs::convert_strings(const int argc, + char** argv) noexcept +{ + vector<string>* arguments = new vector<string>(); + + for (int i = 0; i < argc; ++i) { + arguments->push_back(std::move(string(argv[i]))); + } + + return unique_ptr<vector<string>>(arguments); +} + +unique_ptr<vector<string>> o2::dcs::split(const string& source, + const char separator) noexcept +{ + stringstream ss(source); + string item; + vector<string>* substrings = new vector<string>(); + while (getline(ss, item, separator)) { + substrings->push_back(item); + } + return unique_ptr<vector<string>>(substrings); +} + +unique_ptr<vector<string>> o2::dcs::split_by_whitespace(const string& source) noexcept +{ + istringstream buffer(source); + vector<string>* ret = new vector<string>(); + + std::copy(istream_iterator<string>(buffer), + istream_iterator<string>(), + back_inserter(*ret)); + return unique_ptr<vector<string>>(ret); +} + +inline char to_alpha_numeric(const uint8_t c) noexcept +{ + switch (c) { + case 0x00: + return '0'; + case 0x01: + case 0x10: + return '1'; + case 0x02: + case 0x20: + return '2'; + case 0x03: + case 0x30: + return '3'; + case 0x04: + case 0x40: + return '4'; + case 0x05: + case 0x50: + return '5'; + case 0x06: + case 0x60: + return '6'; + case 0x07: + case 0x70: + return '7'; + case 0x08: + case 0x80: + return '8'; + case 0x09: + case 0x90: + return '9'; + case 0x0A: + case 0xA0: + return 'A'; + case 0x0B: + case 0xB0: + return 'B'; + case 0x0C: + case 0xC0: + return 'C'; + case 0x0D: + case 0xD0: + return 'D'; + case 0x0E: + case 0xE0: + return 'E'; + case 0x0F: + case 0xF0: + return 'F'; + default: + return '?'; + } +} + +string o2::dcs::to_hex_big_endian(const char* const start, const size_t length) noexcept +{ + string s; + s.reserve(length * 3); + // My machine is little endian: + size_t i = length - 1; + do { + const char next = start[i]; + s.push_back(to_alpha_numeric(next & 0xF0)); + s.push_back(to_alpha_numeric(next & 0x0F)); + s.push_back(' '); + --i; + } while (i < length); + s.pop_back(); + return s; +} + +string o2::dcs::to_hex_little_endian(const char* const start, const size_t length) noexcept +{ + string s; + s.reserve(length * 3); + // My machine is little endian: + size_t i = 0; + do { + const char next = start[i]; + s.push_back(to_alpha_numeric(next & 0xF0)); + s.push_back(to_alpha_numeric(next & 0x0F)); + s.push_back(' '); + ++i; + } while (i < length); + s.pop_back(); + return s; +} + +void o2::dcs::print_k_v_list( + const string& list_name, + const list<pair<string, string>>& parameters) noexcept +{ + stringstream ss; + ss << list_name << endl + << string(list_name.length() + 26, '-') << endl + << endl; + uint32_t key_length(0); + for (auto k_v : parameters) { + if (k_v.first.length() > key_length) { + key_length = k_v.first.length(); + } + } + key_length += 4 - (key_length % 4); + for (auto k_v : parameters) { + ss << " " << k_v.first << string(key_length - k_v.first.length(), ' ') << k_v.second << endl; + } + ss << endl + << "==========================================================" + "======================" + << endl + << endl; +} diff --git a/Detectors/DCS/test/processor_dpcom_o2.C b/Detectors/DCS/test/processor_dpcom_o2.C new file mode 100644 index 0000000000000..4d1171205055c --- /dev/null +++ b/Detectors/DCS/test/processor_dpcom_o2.C @@ -0,0 +1,115 @@ +using namespace o2::dcs; +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; + +int processor_dpcom_o2() +{ + + std::unordered_map<DPID, DPVAL> dpmap; + + o2::dcs::DCSProcessor dcsproc; + std::vector<DPID> aliasChars; + std::vector<DPID> aliasInts; + std::vector<DPID> aliasDoubles; + std::vector<DPID> aliasUInts; + std::vector<DPID> aliasBools; + std::vector<DPID> aliasStrings; + std::vector<DPID> aliasTimes; + std::vector<DPID> aliasBinaries; + + DeliveryType typechar = RAW_CHAR; + std::string dpAliaschar = "TestChar0"; + DPID charVar(dpAliaschar, typechar); + aliasChars.push_back(charVar); + + DeliveryType typeint = RAW_INT; + std::string dpAliasint0 = "TestInt0"; + DPID intVar0(dpAliasint0, typeint); + aliasInts.push_back(intVar0); + + std::string dpAliasint1 = "TestInt1"; + DPID intVar1(dpAliasint1, typeint); + aliasInts.push_back(intVar1); + + std::string dpAliasint2 = "TestInt2"; + DPID intVar2(dpAliasint2, typeint); + aliasInts.push_back(intVar2); + + DeliveryType typedouble = RAW_DOUBLE; + std::string dpAliasdouble0 = "TestDouble0"; + DPID doubleVar0(dpAliasdouble0, typedouble); + aliasDoubles.push_back(doubleVar0); + + std::string dpAliasdouble1 = "TestDouble1"; + DPID doubleVar1(dpAliasdouble1, typedouble); + aliasDoubles.push_back(doubleVar1); + + std::string dpAliasdouble2 = "TestDouble2"; + DPID doubleVar2(dpAliasdouble2, typedouble); + aliasDoubles.push_back(doubleVar2); + + std::string dpAliasdouble3 = "TestDouble3"; + DPID doubleVar3(dpAliasdouble3, typedouble); + aliasDoubles.push_back(doubleVar3); + + DeliveryType typestring = RAW_STRING; + std::string dpAliasstring0 = "TestString0"; + DPID stringVar0(dpAliasstring0, typestring); + aliasStrings.push_back(stringVar0); + + dcsproc.init(aliasChars, aliasInts, aliasDoubles, aliasUInts, aliasBools, aliasStrings, aliasTimes, aliasBinaries); + + uint16_t flags = 0; + uint16_t milliseconds = 0; + TDatime currentTime; + uint32_t seconds = currentTime.Get(); + uint64_t* payload = new uint64_t[7]; + + // loop that emulates the number of times the DCS DataPoints are sent + for (auto k = 0; k < 4; k++) { + payload[0] = (uint64_t)k + 33; // adding 33 to have visible chars and strings + + DPVAL valchar(flags, milliseconds + k * 10, seconds + k, payload, typechar); + DPVAL valint(flags, milliseconds + k * 10, seconds + k, payload, typeint); + DPVAL valdouble(flags, milliseconds + k * 10, seconds + k, payload, typedouble); + DPVAL valstring(flags, milliseconds + k * 10, seconds + k, payload, typestring); + + dpmap[charVar] = valchar; + dpmap[intVar0] = valint; + dpmap[intVar1] = valint; + dpmap[intVar2] = valint; + dpmap[doubleVar0] = valdouble; + dpmap[doubleVar1] = valdouble; + dpmap[doubleVar2] = valdouble; + dpmap[stringVar0] = valstring; + if (k != 3) + dpmap[doubleVar3] = valdouble; // to test the case when a DP is not updated + std::cout << "index = " << k << std::endl; + std::cout << charVar << std::endl + << valchar << " --> " << (char)valchar.payload_pt1 << std::endl; + std::cout << intVar0 << std::endl + << valint << " --> " << (int)valchar.payload_pt1 << std::endl; + std::cout << intVar1 << std::endl + << valint << " --> " << (int)valchar.payload_pt1 << std::endl; + std::cout << intVar2 << std::endl + << valint << " --> " << (int)valchar.payload_pt1 << std::endl; + std::cout << doubleVar0 << std::endl + << valdouble << " --> " << (double)valchar.payload_pt1 << std::endl; + std::cout << doubleVar1 << std::endl + << valdouble << " --> " << (double)valchar.payload_pt1 << std::endl; + std::cout << doubleVar2 << std::endl + << valdouble << " --> " << (double)valchar.payload_pt1 << std::endl; + std::cout << doubleVar3 << std::endl + << valdouble << " --> " << (double)valchar.payload_pt1 << std::endl; + char tt[56]; + memcpy(&tt[0], &valstring.payload_pt1, 56); + printf("tt = %s\n", tt); + std::cout << stringVar0 << std::endl + << valstring << " --> " << tt << std::endl; + + dcsproc.process(dpmap); + } + std::cout << "The map has " << dpmap.size() << " entries" << std::endl; + return 0; +} diff --git a/Detectors/DCS/test/testAliasExpander.cxx b/Detectors/DCS/test/testAliasExpander.cxx new file mode 100644 index 0000000000000..549a7a69cda65 --- /dev/null +++ b/Detectors/DCS/test/testAliasExpander.cxx @@ -0,0 +1,127 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DCS AliasExpander +#define BOOST_TEST_MAIN + +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> +#include <boost/test/data/test_case.hpp> +#include <iostream> +#include "DetectorsDCS/AliasExpander.h" + +BOOST_AUTO_TEST_CASE(ExpandAliasesIsNoopWhenNoPatternGiven) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases({"ab"}); + + std::vector<std::string> expected = {"ab"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesReturnsEmptyVectorWhenPatternIsIncorrect) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases({"ab[c"}); + + std::vector<std::string> expected = {}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); + + aliases = o2::dcs::expandAliases({"ab]c"}); + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); + + aliases = o2::dcs::expandAliases({"ab[1.2]c"}); + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithIntegerRange) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases({"a[1..2]bcde[99..101]toto"}); + + std::vector<std::string> expected = { + "a1bcde099toto", + "a1bcde100toto", + "a1bcde101toto", + "a2bcde099toto", + "a2bcde100toto", + "a2bcde101toto"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithIntegerRangeWithCustomFormat) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases({"a[1..3{:03d}]"}); + + std::vector<std::string> expected = { + "a001", + "a002", + "a003"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithIntegerRangeWithCustomFormatBis) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases({"a[1..3{:d}]"}); + + std::vector<std::string> expected = { + "a1", + "a2", + "a3"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithStringList) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases({"a[1..2]bcde[99..101][toto,titi,tata]"}); + + std::vector<std::string> expected = { + "a1bcde099tata", + "a1bcde099titi", + "a1bcde099toto", + "a1bcde100tata", + "a1bcde100titi", + "a1bcde100toto", + "a1bcde101tata", + "a1bcde101titi", + "a1bcde101toto", + "a2bcde099tata", + "a2bcde099titi", + "a2bcde099toto", + "a2bcde100tata", + "a2bcde100titi", + "a2bcde100toto", + "a2bcde101tata", + "a2bcde101titi", + "a2bcde101toto", + }; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandMch) +{ + std::vector<std::string> aliases = o2::dcs::expandAliases( + {"MchHvLvLeft/Chamber[00..03]Left/Quad1Sect[0..2].actual.[vMon,iMon]", + "MchHvLvLeft/Chamber[00..03]Left/Quad2Sect[0..2].actual.[vMon,iMon]", + "MchHvLvLeft/Chamber[04..09]Left/Slat[00..08].actual.[vMon,iMon]", + "MchHvLvLeft/Chamber[06..09]Left/Slat[09..12].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[00..03]Right/Quad0Sect[0..2].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[00..03]Right/Quad3Sect[0..2].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[04..09]Right/Slat[00..08].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[06..09]Right/Slat[09..12].actual.[vMon,iMon]"}); + + BOOST_TEST(aliases.size(), 376); +} diff --git a/Detectors/DCS/test/testDataPointGenerator.cxx b/Detectors/DCS/test/testDataPointGenerator.cxx new file mode 100644 index 0000000000000..367e52dc14558 --- /dev/null +++ b/Detectors/DCS/test/testDataPointGenerator.cxx @@ -0,0 +1,81 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DCS DataPointGenerator +#define BOOST_TEST_MAIN + +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> +#include <boost/test/data/test_case.hpp> +#include <iostream> +#include "DetectorsDCS/DataPointGenerator.h" +#include <algorithm> + +BOOST_AUTO_TEST_CASE(GenerateDouble) +{ + double fmin = 1620.0; + double fmax = 1710.5; + auto fbi = o2::dcs::generateRandomDataPoints({"TST/SECTOR[00..06]/CRATE[0..3]/voltage"}, fmin, fmax, "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 28); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_DOUBLE); + double value = o2::dcs::getValue<double>(dp); + BOOST_CHECK(value >= fmin && value <= fmax); + } +} + +BOOST_AUTO_TEST_CASE(GenerateInt) +{ + uint32_t imin = 0; + uint32_t imax = 3; + auto fbi = o2::dcs::generateRandomDataPoints({"TST/SECTOR[00..06]/CRATE[0..3]/current"}, imin, imax, "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 28); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_UINT); + double value = o2::dcs::getValue<uint32_t>(dp); + BOOST_CHECK(value >= imin && value <= imax); + BOOST_CHECK_THROW(o2::dcs::getValue<double>(dp), std::runtime_error); + BOOST_CHECK_THROW(o2::dcs::getValue<int32_t>(dp), std::runtime_error); + } +} + +BOOST_AUTO_TEST_CASE(GenerateBool) +{ + auto fbi = o2::dcs::generateRandomDataPoints<bool>({"TST/SECTOR[00..06]/status"}, 0, 1, "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 7); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_BOOL); + BOOST_CHECK_NO_THROW(o2::dcs::getValue<bool>(dp)); + BOOST_CHECK_THROW(o2::dcs::getValue<int>(dp), std::runtime_error); + } +} + +BOOST_AUTO_TEST_CASE(GenerateString) +{ + auto fbi = o2::dcs::generateRandomDataPoints<std::string>({"TST/SECTOR[00..06]/name"}, "123", "1234567", "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 7); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_STRING); + BOOST_CHECK_NO_THROW(o2::dcs::getValue<std::string>(dp)); + BOOST_CHECK_THROW(o2::dcs::getValue<int>(dp), std::runtime_error); + auto value = o2::dcs::getValue<std::string>(dp); + BOOST_CHECK(value.size() >= 3); + BOOST_CHECK(value.size() <= 7); + } +} diff --git a/Detectors/DCS/test/testDataPointTypes.cxx b/Detectors/DCS/test/testDataPointTypes.cxx new file mode 100644 index 0000000000000..91c1db1def7c2 --- /dev/null +++ b/Detectors/DCS/test/testDataPointTypes.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <type_traits> +#define BOOST_TEST_MODULE Test DetectorsDCS DataPoints +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "Framework/TypeTraits.h" +#include <vector> +#include <list> +#include <gsl/gsl> +#include <boost/mpl/list.hpp> + +typedef boost::mpl::list<o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue, o2::dcs::DataPointCompositeObject> testTypes; + +BOOST_AUTO_TEST_CASE_TEMPLATE(DataPointCompositeObjectTypeTraits, T, testTypes) +{ + BOOST_CHECK_EQUAL(std::is_trivially_copyable<T>::value, true); + BOOST_CHECK_EQUAL(std::is_polymorphic<T>::value, false); + BOOST_CHECK_EQUAL(std::is_pointer<T>::value, false); + BOOST_CHECK_EQUAL(o2::framework::is_forced_non_messageable<T>::value, false); +} + +BOOST_AUTO_TEST_CASE(DataPointsAreMessageable) +{ + BOOST_CHECK_EQUAL(o2::framework::is_messageable<o2::dcs::DataPointIdentifier>::value, true); + BOOST_CHECK_EQUAL(o2::framework::is_messageable<o2::dcs::DataPointValue>::value, true); + BOOST_CHECK_EQUAL(o2::framework::is_messageable<o2::dcs::DataPointCompositeObject>::value, true); +} diff --git a/Detectors/DCS/testWorkflow/DCSConsumerSpec.h b/Detectors/DCS/testWorkflow/DCSConsumerSpec.h new file mode 100644 index 0000000000000..9485e3d5c1488 --- /dev/null +++ b/Detectors/DCS/testWorkflow/DCSConsumerSpec.h @@ -0,0 +1,88 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_CONSUMER_H +#define O2_DCS_CONSUMER_H + +/// @file DCSConsumerSpec.h +/// @brief Consumer of DPs coming from DCS server; it is just +/// to check that we receive and pack the data correctly + +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" +#include "Framework/DataSpecUtils.h" + +using namespace o2::framework; +namespace o2h = o2::header; + +namespace o2 +{ +namespace dcs +{ +class DCSConsumer : public o2::framework::Task +{ + + using DPCOM = o2::dcs::DataPointCompositeObject; + + public: + void init(o2::framework::InitContext& ic) final + { + } + + void run(o2::framework::ProcessingContext& pc) final + { + + uint64_t tfid; + for (auto& input : pc.inputs()) { + tfid = header::get<o2::framework::DataProcessingHeader*>(input.header)->startTime; + LOG(DEBUG) << "tfid = " << tfid; + break; // we break because one input is enough to get the TF ID + } + + LOG(DEBUG) << "TF: " << tfid << " --> reading binary blob..."; + mTFs++; + auto vect = pc.inputs().get<gsl::span<DPCOM>>("COMMONDPs"); + LOG(INFO) << "vector has " << vect.size() << " Data Points inside"; + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + + LOG(INFO) << "Number of processed TFs = " << mTFs; + } + + private: + uint64_t mTFs = 0; +}; + +} // namespace dcs + +namespace framework +{ + +DataProcessorSpec getDCSConsumerSpec() +{ + return DataProcessorSpec{ + "dcs-consumer", + Inputs{{"COMMONDPs", "DCS", "COMMON", 0, Lifetime::Timeframe}}, + Outputs{}, + AlgorithmSpec{adaptFromTask<o2::dcs::DCSConsumer>()}, + Options{}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/DCS/testWorkflow/DCSDataGeneratorSpec.h b/Detectors/DCS/testWorkflow/DCSDataGeneratorSpec.h new file mode 100644 index 0000000000000..27c0bb2977b6d --- /dev/null +++ b/Detectors/DCS/testWorkflow/DCSDataGeneratorSpec.h @@ -0,0 +1,304 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_DATAGENERATOR_H +#define O2_DCS_DATAGENERATOR_H + +/// @file DataGeneratorSpec.h +/// @brief Dummy data generator +#include <unistd.h> +#include <TRandom.h> +#include <TDatime.h> +#include <TStopwatch.h> +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DeliveryType.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace dcs +{ +class DCSDataGenerator : public o2::framework::Task +{ + + using DPID = o2::dcs::DataPointIdentifier; + using DPVAL = o2::dcs::DataPointValue; + using DPCOM = o2::dcs::DataPointCompositeObject; + + public: + void init(o2::framework::InitContext& ic) final + { + mMaxTF = ic.options().get<int64_t>("max-timeframes"); + + LOG(INFO) << "mMaxTF = " << mMaxTF; + + DPID dpidtmp; + + // chars + std::string dpAliaschar = "TestChar_0"; + DPID::FILL(dpidtmp, dpAliaschar, mtypechar); + mDPIDvectFull.push_back(dpidtmp); + mNumDPsFull++; + mNumDPscharFull++; + + // ints + for (int i = 0; i < 50000; i++) { + std::string dpAliasint = "TestInt_" + std::to_string(i); + DPID::FILL(dpidtmp, dpAliasint, mtypeint); + mDPIDvectFull.push_back(dpidtmp); + mNumDPsFull++; + mNumDPsintFull++; + if (i < 100) { + mDPIDvectDelta.push_back(dpidtmp); + mNumDPsDelta++; + mNumDPsintDelta++; + } + } + + // doubles + for (int i = 0; i < 4; i++) { + std::string dpAliasdouble = "TestDouble_" + std::to_string(i); + DPID::FILL(dpidtmp, dpAliasdouble, mtypedouble); + mDPIDvectFull.push_back(dpidtmp); + mNumDPsFull++; + mNumDPsdoubleFull++; + } + + // strings + std::string dpAliasstring0 = "TestString_0"; + DPID::FILL(dpidtmp, dpAliasstring0, mtypestring); + mDPIDvectFull.push_back(dpidtmp); + mNumDPsFull++; + mNumDPsstringFull++; + + LOG(INFO) << "Number of DCS data points: " << mNumDPsFull << " (full map); " << mNumDPsDelta << " (delta map)"; + } + + void run(o2::framework::ProcessingContext& pc) final + { + + uint64_t tfid; + for (auto& input : pc.inputs()) { + tfid = header::get<o2::framework::DataProcessingHeader*>(input.header)->startTime; + LOG(DEBUG) << "tfid = " << tfid; + if (tfid >= mMaxTF) { + LOG(INFO) << "Data generator reached TF " << tfid << ", stopping"; + pc.services().get<o2::framework::ControlService>().endOfStream(); + pc.services().get<o2::framework::ControlService>().readyToQuit(o2::framework::QuitRequest::Me); + } + break; // we break because one input is enough to get the TF ID + } + + LOG(DEBUG) << "TF: " << tfid << " --> building binary blob..."; + uint16_t flags = 0; + uint16_t milliseconds = 0; + TDatime currentTime; + uint32_t seconds = currentTime.Get(); + uint64_t payload[7]; + memset(payload, 0, sizeof(uint64_t) * 7); + + payload[0] = (uint64_t)tfid + 33; // adding 33 to have visible chars and strings + + DPVAL valchar(flags, milliseconds + tfid * 10, seconds + tfid, payload, mtypechar); + DPVAL valint(flags, milliseconds + tfid * 10, seconds + tfid, payload, mtypeint); + DPVAL valdouble(flags, milliseconds + tfid * 10, seconds + tfid, payload, mtypedouble); + DPVAL valstring(flags, milliseconds + tfid * 10, seconds + tfid, payload, mtypestring); + + LOG(DEBUG) << "Value used for char DPs:"; + LOG(DEBUG) << valchar << " --> " << (char)valchar.payload_pt1; + LOG(DEBUG) << "Value used for int DPs:"; + LOG(DEBUG) << valint << " --> " << (int)valint.payload_pt1; + LOG(DEBUG) << "Value used for double DPs:"; + LOG(DEBUG) << valdouble << " --> " << (double)valdouble.payload_pt1; + char tt[56]; + memcpy(&tt[0], &valstring.payload_pt1, 56); + LOG(DEBUG) << "Value used for string DPs:"; + LOG(DEBUG) << valstring << " --> " << tt; + + // full map (all DPs) + mBuildingBinaryBlock.Start(mFirstTF); + std::vector<DPCOM> dpcomVectFull; + for (int i = 0; i < mNumDPscharFull; i++) { + dpcomVectFull.emplace_back(mDPIDvectFull[i], valchar); + } + for (int i = 0; i < mNumDPsintFull; i++) { + dpcomVectFull.emplace_back(mDPIDvectFull[mNumDPscharFull + i], valint); + } + for (int i = 0; i < mNumDPsdoubleFull; i++) { + dpcomVectFull.emplace_back(mDPIDvectFull[mNumDPscharFull + mNumDPsintFull + i], valdouble); + } + for (int i = 0; i < mNumDPsstringFull; i++) { + dpcomVectFull.emplace_back(mDPIDvectFull[mNumDPscharFull + mNumDPsintFull + mNumDPsdoubleFull + i], valstring); + } + + // delta map (only DPs that changed) + mDeltaBuildingBinaryBlock.Start(mFirstTF); + std::vector<DPCOM> dpcomVectDelta; + for (int i = 0; i < mNumDPscharDelta; i++) { + dpcomVectDelta.emplace_back(mDPIDvectDelta[i], valchar); + } + for (int i = 0; i < mNumDPsintDelta; i++) { + dpcomVectDelta.emplace_back(mDPIDvectDelta[mNumDPscharDelta + i], valint); + } + for (int i = 0; i < mNumDPsdoubleDelta; i++) { + dpcomVectDelta.emplace_back(mDPIDvectDelta[mNumDPscharDelta + mNumDPsintDelta + i], valdouble); + } + for (int i = 0; i < mNumDPsstringDelta; i++) { + dpcomVectDelta.emplace_back(mDPIDvectDelta[mNumDPscharDelta + mNumDPsintDelta + mNumDPsdoubleDelta + i], + valstring); + } + + // Full map + auto svect = dpcomVectFull.size(); + LOG(DEBUG) << "dpcomVectFull has size " << svect; + for (int i = 0; i < svect; i++) { + LOG(DEBUG) << "i = " << i << ", DPCOM = " << dpcomVectFull[i]; + } + std::vector<char> buff(mNumDPsFull * sizeof(DPCOM)); + char* dptr = buff.data(); + for (int i = 0; i < svect; i++) { + memcpy(dptr + i * sizeof(DPCOM), &dpcomVectFull[i], sizeof(DPCOM)); + } + auto sbuff = buff.size(); + LOG(DEBUG) << "size of output buffer = " << sbuff; + mBuildingBinaryBlock.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...binary blob prepared: realTime = " + << mBuildingBinaryBlock.RealTime() << ", cpuTime = " + << mBuildingBinaryBlock.CpuTime(); + LOG(DEBUG) << "TF: " << tfid << " --> sending snapshot..."; + mSnapshotSending.Start(mFirstTF); + pc.outputs().snapshot(Output{"DCS", "DATAPOINTS", 0, Lifetime::Timeframe}, buff.data(), sbuff); + mSnapshotSending.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...snapshot sent: realTime = " << mSnapshotSending.RealTime() + << ", cpuTime = " << mSnapshotSending.CpuTime(); + + // Delta map + auto svectDelta = dpcomVectDelta.size(); + LOG(DEBUG) << "dpcomVectDelta has size " << svect; + for (int i = 0; i < svectDelta; i++) { + LOG(DEBUG) << "i = " << i << ", DPCOM = " << dpcomVectDelta[i]; + } + std::vector<char> buffDelta(mNumDPsDelta * sizeof(DPCOM)); + char* dptrDelta = buffDelta.data(); + for (int i = 0; i < svectDelta; i++) { + memcpy(dptrDelta + i * sizeof(DPCOM), &dpcomVectDelta[i], sizeof(DPCOM)); + } + auto sbuffDelta = buffDelta.size(); + LOG(DEBUG) << "size of output (delta) buffer = " << sbuffDelta; + mDeltaBuildingBinaryBlock.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...binary (delta) blob prepared: realTime = " + << mDeltaBuildingBinaryBlock.RealTime() << ", cpuTime = " << mDeltaBuildingBinaryBlock.CpuTime(); + LOG(DEBUG) << "TF: " << tfid << " --> sending (delta) snapshot..."; + mDeltaSnapshotSending.Start(mFirstTF); + pc.outputs().snapshot(Output{"DCS", "DATAPOINTSdelta", 0, Lifetime::Timeframe}, buffDelta.data(), sbuffDelta); + mDeltaSnapshotSending.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...snapshot (delta) sent: realTime = " + << mDeltaSnapshotSending.RealTime() << ", cpuTime = " + << mDeltaSnapshotSending.CpuTime(); + + /* + LOG(INFO) << "Reading back"; + DPCOM dptmp; + for (int i = 0; i < svect; i++) { + memcpy(&dptmp, dptr + i * sizeof(DPCOM), sizeof(DPCOM)); + LOG(DEBUG) << "Check: Reading from generator: i = " << i << ", DPCOM = " << dptmp; + } + */ + /* + auto& tmpDPmap = pc.outputs().make<std::unordered_map<DPID, DPVAL>>(o2::framework::OutputRef{"output", 0}); + tmpDPmap[mcharVar] = valchar; + tmpDPmap[mintVar0] = valint; + tmpDPmap[mintVar1] = valint; + tmpDPmap[mintVar2] = valint; + tmpDPmap[mdoubleVar0] = valdouble; + tmpDPmap[mdoubleVar1] = valdouble; + tmpDPmap[mdoubleVar2] = valdouble; + if (tfid % 3 == 0) + tmpDPmap[mdoubleVar3] = valdouble; // to test the case when a DP is not updated, we skip some updates + tmpDPmap[mstringVar0] = valstring; + */ + mFirstTF = false; + mTFs++; + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOG(INFO) << "number of processed TF: " << mTFs; + LOG(INFO) << " --> time to prepare binary blob: realTime = " + << mBuildingBinaryBlock.RealTime() / mTFs << ", cpuTime = " + << mBuildingBinaryBlock.CpuTime() / mTFs; + LOG(INFO) << " --> time to send snapshot: realTime = " + << mSnapshotSending.RealTime() / mTFs << ", cpuTime = " + << mSnapshotSending.CpuTime() / mTFs; + LOG(INFO) << " --> time to prepare binary blob: realTime = " + << mDeltaBuildingBinaryBlock.RealTime() / mTFs << ", cpuTime = " + << mDeltaBuildingBinaryBlock.CpuTime() / mTFs; + LOG(INFO) << " --> time to send snapshot: realTime = " + << mDeltaSnapshotSending.RealTime() / mTFs << ", cpuTime = " + << mDeltaSnapshotSending.CpuTime() / mTFs; + } + + private: + uint64_t mMaxTF = 1; + + uint64_t mNumDPsFull = 0; + uint64_t mNumDPscharFull = 0; + uint64_t mNumDPsintFull = 0; + uint64_t mNumDPsdoubleFull = 0; + uint64_t mNumDPsstringFull = 0; + + uint64_t mNumDPsDelta = 0; + uint64_t mNumDPscharDelta = 0; + uint64_t mNumDPsintDelta = 0; + uint64_t mNumDPsdoubleDelta = 0; + uint64_t mNumDPsstringDelta = 0; + std::vector<DPID> mDPIDvectFull; // for full map + std::vector<DPID> mDPIDvectDelta; // for delta map (containing only DPs that changed) + DeliveryType mtypechar = RAW_CHAR; + DeliveryType mtypeint = RAW_INT; + DeliveryType mtypedouble = RAW_DOUBLE; + DeliveryType mtypestring = RAW_STRING; + + TStopwatch mBuildingBinaryBlock; + TStopwatch mDeltaBuildingBinaryBlock; + TStopwatch mSnapshotSending; + TStopwatch mDeltaSnapshotSending; + bool mFirstTF = true; + uint64_t mTFs = 0; +}; + +} // namespace dcs + +namespace framework +{ + +DataProcessorSpec getDCSDataGeneratorSpec() +{ + return DataProcessorSpec{ + "dcs-data-generator", + Inputs{}, + Outputs{{{"outputDCS"}, "DCS", "DATAPOINTS"}, {{"outputDCSdelta"}, "DCS", "DATAPOINTSdelta"}}, + AlgorithmSpec{adaptFromTask<o2::dcs::DCSDataGenerator>()}, + Options{{"max-timeframes", VariantType::Int64, 99999999999ll, {"max TimeFrames to generate"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/DCS/testWorkflow/DCSDataProcessorSpec.h b/Detectors/DCS/testWorkflow/DCSDataProcessorSpec.h new file mode 100644 index 0000000000000..1f00bcadc8744 --- /dev/null +++ b/Detectors/DCS/testWorkflow/DCSDataProcessorSpec.h @@ -0,0 +1,336 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_DATAPROCESSOR_H +#define O2_DCS_DATAPROCESSOR_H + +/// @file DataGeneratorSpec.h +/// @brief Dummy data generator + +#include <unistd.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DCSProcessor.h" +#include "DetectorsCalibration/Utils.h" +#include "CCDB/CcdbApi.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +namespace o2 +{ +namespace dcs +{ + +using namespace o2::dcs; +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; + +class DCSDataProcessor : public o2::framework::Task +{ + public: + enum Detectors { + kTest, + // kTPC, // commented out for test, when we use only 1 "Test" detector + kNdetectors + }; + + void init(o2::framework::InitContext& ic) final + { + + // stopping all stopwatches, since they start counting from the moment they are created + for (int idet = 0; idet < kNdetectors; idet++) { + mDeltaProcessingDetLoop[idet].Stop(); + mDeltaProcessingDetLoop[idet].Reset(); + } + + std::vector<DPID> pidVect; + + DPID dpidtmp; + DeliveryType typechar = RAW_CHAR; + std::string dpAliaschar = "TestChar_0"; + DPID::FILL(dpidtmp, dpAliaschar, typechar); + pidVect.push_back(dpidtmp); + + //std::vector<int> vectDet{kTest, kTPC}; // only one detector for now + std::vector<int> vectDet{kTest}; + mDetectorPid[dpidtmp] = vectDet; + + DeliveryType typeint = RAW_INT; + for (int i = 0; i < 50000; i++) { + std::string dpAliasint = "TestInt_" + std::to_string(i); + DPID::FILL(dpidtmp, dpAliasint, typeint); + pidVect.push_back(dpidtmp); + mDetectorPid[dpidtmp] = vectDet; + } + + DeliveryType typedouble = RAW_DOUBLE; + for (int i = 0; i < 4; i++) { + std::string dpAliasdouble = "TestDouble_" + std::to_string(i); + DPID::FILL(dpidtmp, dpAliasdouble, typedouble); + pidVect.push_back(dpidtmp); + mDetectorPid[dpidtmp] = vectDet; + } + + DeliveryType typestring = RAW_STRING; + std::string dpAliasstring0 = "TestString_0"; + DPID::FILL(dpidtmp, dpAliasstring0, typestring); + pidVect.push_back(dpidtmp); + mDetectorPid[dpidtmp] = vectDet; + + for (int idet = 0; idet < kNdetectors; idet++) { + mDCSprocVect[idet].init(pidVect); + mDCSprocVect[idet].setMaxCyclesNoFullMap(ic.options().get<int64_t>("max-cycles-no-full-map")); + mDCSprocVect[idet].setName("Test1Det"); + } + mProcessFullDeltaMap = ic.options().get<bool>("process-full-delta-map"); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto tfid = o2::header::get<o2::framework::DataProcessingHeader*>(pc.inputs().get("input").header)->startTime; + for (int idet = 0; idet < kNdetectors; idet++) { + mDCSprocVect[idet].setTF(tfid); + } + + TStopwatch s; + LOG(DEBUG) << "TF: " << tfid << " --> receiving binary data..."; + mReceiveBinaryData.Start(mFirstTF); + auto rawchar = pc.inputs().get<const char*>("input"); + mReceiveBinaryData.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...binary data received: realTime = " + << mReceiveBinaryData.RealTime() << ", cpuTime = " + << mReceiveBinaryData.CpuTime(); + LOG(DEBUG) << "TF: " << tfid << " --> receiving (delta) binary data..."; + mDeltaReceiveBinaryData.Start(mFirstTF); + auto rawcharDelta = pc.inputs().get<const char*>("inputDelta"); + mDeltaReceiveBinaryData.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...binary (delta) data received: realTime = " + << mDeltaReceiveBinaryData.RealTime() + << ", cpuTime = " << mDeltaReceiveBinaryData.CpuTime(); + + // full map + const auto* dh = o2::header::get<o2::header::DataHeader*>(pc.inputs().get("input").header); + auto sz = dh->payloadSize; + int nDPs = sz / sizeof(DPCOM); + std::unordered_map<DPID, DPVAL> dcsmap; + DPCOM dptmp; + LOG(DEBUG) << "TF: " << tfid << " --> building unordered_map..."; + mBuildingUnorderedMap.Start(mFirstTF); + for (int i = 0; i < nDPs; i++) { + memcpy(&dptmp, rawchar + i * sizeof(DPCOM), sizeof(DPCOM)); + dcsmap[dptmp.id] = dptmp.data; + LOG(DEBUG) << "Reading from generator: i = " << i << ", DPCOM = " << dptmp; + LOG(DEBUG) << "Reading from generator: i = " << i << ", DPID = " << dptmp.id; + } + mBuildingUnorderedMap.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...unordered_map built = " + << mBuildingUnorderedMap.RealTime() << ", cpuTime = " << mBuildingUnorderedMap.CpuTime(); + + // delta map + const auto* dhDelta = o2::header::get<o2::header::DataHeader*>(pc.inputs().get("inputDelta").header); + auto szDelta = dhDelta->payloadSize; + int nDPsDelta = szDelta / sizeof(DPCOM); + std::unordered_map<DPID, DPVAL> dcsmapDelta; + LOG(DEBUG) << "TF: " << tfid << " --> building (delta) unordered_map..."; + mDeltaBuildingUnorderedMap.Start(mFirstTF); + for (int i = 0; i < nDPsDelta; i++) { + memcpy(&dptmp, rawcharDelta + i * sizeof(DPCOM), sizeof(DPCOM)); + dcsmapDelta[dptmp.id] = dptmp.data; + LOG(DEBUG) << "Reading from generator: i = " << i << ", DPCOM = " << dptmp; + LOG(DEBUG) << "Reading from generator: i = " << i << ", DPID = " << dptmp.id; + } + mDeltaBuildingUnorderedMap.Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...unordered_map (delta) built = " + << mDeltaBuildingUnorderedMap.RealTime() << ", cpuTime = " + << mDeltaBuildingUnorderedMap.CpuTime(); + + if (tfid % 6000 == 0) { + LOG(INFO) << "Number of DPs received = " << nDPs; + for (int idet = 0; idet < kNdetectors; idet++) { + LOG(DEBUG) << "TF: " << tfid << " --> starting processing..."; + mProcessing[idet].Start(mResetStopwatchProcessing); + mDCSprocVect[idet].processMap(dcsmap, false); + mProcessing[idet].Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...processing done: realTime = " + << mProcessing[idet].RealTime() << ", cpuTime = " + << mProcessing[idet].CpuTime(); + } + mResetStopwatchProcessing = false; // from now on, we sum up the processing time + mTFsProcessing++; + } else { + LOG(INFO) << "Number of DPs received (delta map) = " << nDPsDelta; + if (mProcessFullDeltaMap) { + for (int idet = 0; idet < kNdetectors; idet++) { + LOG(DEBUG) << "TF: " << tfid << " --> starting (delta) processing..."; + mDeltaProcessing[idet].Start(mResetStopwatchDeltaProcessing); + mDCSprocVect[idet].processMap(dcsmapDelta, true); + mDeltaProcessing[idet].Stop(); + LOG(DEBUG) << "TF: " << tfid << " --> ...processing (delta) done: realTime = " + << mDeltaProcessing[idet].RealTime() + << ", cpuTime = " << mDeltaProcessing[idet].CpuTime(); + } + mResetStopwatchDeltaProcessing = false; // from now on, we sum up the processing time + mTFsDeltaProcessing++; + } else { + + // processing per DP found in the map, to be done in case of a delta map + + LOG(DEBUG) << "TF: " << tfid << " --> starting (delta) processing in detector loop..."; + for (const auto& dpcom : dcsmapDelta) { + std::vector<int> detVect = mDetectorPid[dpcom.first]; + for (int idet = 0; idet < detVect.size(); idet++) { + mDeltaProcessingDetLoop[idet].Start(mResetStopwatchDeltaProcessingDetLoop); + mDCSprocVect[idet].processDP(dpcom); + mDeltaProcessingDetLoop[idet].Stop(); + } + mResetStopwatchDeltaProcessingDetLoop = false; // from now on, we sum up the processing time + } + for (int idet = 0; idet < kNdetectors; idet++) { + LOG(DEBUG) << "TF: " << tfid << " --> ...processing (delta) in detector loop done: realTime = " + << mDeltaProcessingDetLoop[idet].RealTime() << ", cpuTime = " + << mDeltaProcessingDetLoop[idet].CpuTime(); + } + // now preparing CCDB object + for (int idet = 0; idet < kNdetectors; idet++) { + std::map<std::string, std::string> md; + mDCSprocVect[idet].prepareCCDBobject(mDCSprocVect[idet].getCCDBSimpleMovingAverage(), + mDCSprocVect[idet].getCCDBSimpleMovingAverageInfo(), + mDCSprocVect[idet].getName() + "/TestDCS/SimpleMovingAverageDPs", + tfid, md); + } + mTFsDeltaProcessingDetLoop++; + } + } + sendOutput(pc.outputs()); + mFirstTF = false; + mTFs++; + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOG(INFO) << "\n\nTIMING SUMMARY:\n"; + LOG(INFO) << "Number of processed TF: " << mTFs; + LOG(INFO) << "Number of processed TF, processing full map: " << mTFsProcessing; + LOG(INFO) << "Number of processed TF, processing delta map: " << mTFsDeltaProcessing; + LOG(INFO) << "Number of processed TF, processing delta map per DP: " << mTFsDeltaProcessingDetLoop; + LOG(INFO) << "Receiving binary data --> realTime = " + << mReceiveBinaryData.RealTime() / mTFs << ", cpuTime = " + << mReceiveBinaryData.CpuTime() / mTFs; + LOG(INFO) << "Receiving binary data (delta) --> realTime = " + << mDeltaReceiveBinaryData.RealTime() / mTFs << ", cpuTime = " + << mDeltaReceiveBinaryData.CpuTime() / mTFs; + LOG(INFO) << "Building unordered_map --> realTime = " + << mBuildingUnorderedMap.RealTime() / mTFs << ", cpuTime = " + << mBuildingUnorderedMap.CpuTime() / mTFs; + LOG(INFO) << "Building unordered_map (delta) --> realTime = " + << mDeltaBuildingUnorderedMap.RealTime() / mTFs << ", cpuTime = " + << mDeltaBuildingUnorderedMap.CpuTime() / mTFs; + for (int i = 0; i < kNdetectors; i++) { + LOG(INFO) << " --> : Detector " << i; + if (mTFsProcessing != 0) { + LOG(INFO) << "Processing full map (average over " << mTFsProcessing << " TFs) --> realTime = " + << mProcessing[i].RealTime() / mTFsProcessing << ", cpuTime = " + << mProcessing[i].CpuTime() / mTFsProcessing; + } else { + LOG(INFO) << "Full DCS map was never processed"; + } + if (mTFsDeltaProcessing != 0) { + LOG(INFO) << "Processing full delta map (average over " << mTFsDeltaProcessing << " TFs) --> realTime = " + << mDeltaProcessing[i].RealTime() / mTFsDeltaProcessing << ", cpuTime = " + << mDeltaProcessing[i].CpuTime() / mTFsDeltaProcessing; + } else { + LOG(INFO) << "Full delta DCS map was never processed"; + } + if (mTFsDeltaProcessingDetLoop != 0) { + LOG(INFO) << "Processing delta map per DP (average over " << mTFsDeltaProcessingDetLoop + << " TFs) --> realTime = " + << mDeltaProcessingDetLoop[i].RealTime() / mTFsDeltaProcessingDetLoop << ", cpuTime = " + << mDeltaProcessingDetLoop[i].CpuTime() / mTFsDeltaProcessingDetLoop; + } else { + LOG(INFO) << "Delta map was never process DP by DP"; + } + } + } + + private: + std::unordered_map<DPID, std::vector<int>> mDetectorPid; + std::array<DCSProcessor, kNdetectors> mDCSprocVect; + TStopwatch mReceiveBinaryData; + TStopwatch mDeltaReceiveBinaryData; + TStopwatch mBuildingUnorderedMap; + TStopwatch mDeltaBuildingUnorderedMap; + TStopwatch mProcessing[kNdetectors]; + TStopwatch mDeltaProcessing[kNdetectors]; + TStopwatch mDeltaProcessingDetLoop[kNdetectors]; + bool mProcessFullDeltaMap = false; + bool mFirstTF = true; + uint64_t mTFs = 0; + uint64_t mTFsProcessing = 0; + uint64_t mTFsDeltaProcessing = 0; + uint64_t mTFsDeltaProcessingDetLoop = 0; + bool mResetStopwatchProcessing = true; + bool mResetStopwatchDeltaProcessing = true; + bool mResetStopwatchDeltaProcessingDetLoop = true; + + //________________________________________________________________ + void sendOutput(DataAllocator& output) + { + // extract CCDB infos and calibration objects, convert it to TMemFile and send them to the output + // copied from LHCClockCalibratorSpec.cxx + using clbUtils = o2::calibration::Utils; + for (int idet = 0; idet < kNdetectors; idet++) { + const auto& payload = mDCSprocVect[idet].getCCDBSimpleMovingAverage(); + auto& info = mDCSprocVect[idet].getCCDBSimpleMovingAverageInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + } +}; // end class +} // namespace dcs + +namespace framework +{ + +DataProcessorSpec getDCSDataProcessorSpec() +{ + + using clbUtils = o2::calibration::Utils; + + std::vector<OutputSpec> outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload}); + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); + + return DataProcessorSpec{ + "dcs-data-processor", + Inputs{{"input", "DCS", "DATAPOINTS"}, {"inputDelta", "DCS", "DATAPOINTSdelta"}}, + outputs, + AlgorithmSpec{adaptFromTask<o2::dcs::DCSDataProcessor>()}, + Options{ + {"max-cycles-no-full-map", VariantType::Int64, 6000ll, {"max num of cycles between the sending of 2 full maps"}}, + {"process-full-delta-map", VariantType::Bool, false, {"to process the delta map as a whole instead of per DP"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h b/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h new file mode 100644 index 0000000000000..51f09c2d7570d --- /dev/null +++ b/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h @@ -0,0 +1,210 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_RANDOM_DATA_GENERATOR_SPEC_H +#define O2_DCS_RANDOM_DATA_GENERATOR_SPEC_H + +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DataPointGenerator.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DeviceSpec.h" +#include "Framework/Logger.h" +#include "Framework/Task.h" +#include <TDatime.h> +#include <random> +#include <variant> +#include <string> +#include <algorithm> + +using namespace o2::framework; + +namespace +{ +/* + * A compact representation a group of alias to be generated + */ +template <typename T> +struct DataPointHint { + std::string aliasPattern; // alias pattern e.g. DET/HV/Crate[0..2]/Channel[000..012]/vMon + T minValue; // minimum value to generate + T maxValue; // maximum value to generate +}; + +using HintType = std::variant<DataPointHint<double>, + DataPointHint<uint32_t>, + DataPointHint<int32_t>, + DataPointHint<char>, + DataPointHint<bool>, + DataPointHint<std::string>>; + +/** generate random integers uniformly distributed within a range. + * + * @param size the number of integers to generate + * @param min the minimum value to be generated + * @param max the maximum value to be generated + * + * @returns a vector of integers + */ +std::vector<int> generateIntegers(size_t size, int min, int max) +{ + std::uniform_int_distribution<int> distribution(min, max); + std::mt19937 generator(std::random_device{}()); + std::vector<int> data; + while (data.size() != size) { + data.emplace_back(distribution(generator)); + std::sort(begin(data), end(data)); + auto last = std::unique(begin(data), end(data)); // make sure we do not duplicate + data.erase(last, end(data)); + } + std::shuffle(begin(data), end(data), generator); + for (auto i = 0; i < data.size(); ++i) { + LOG(INFO) << "Generating randomly DP at index " << data[i]; + } + return data; +} + +/** generate DCS data points. + * + * @param hints vector of HintType describing what to generate + * @param fraction fraction of the generated aliases that are returned (1.0 by default) + * + * @returns a vector of DataPointCompositeObjects + */ +std::vector<o2::dcs::DataPointCompositeObject> generate(const std::vector<HintType> hints, + float fraction = 1.0, + uint64_t tfid = 0) +{ + std::vector<o2::dcs::DataPointCompositeObject> dataPoints; + + TDatime d; + auto dsec = d.Convert(); + dsec += tfid; + d.Set(dsec); + + std::string refDate = d.AsString(); + + auto GenerateVisitor = [refDate](const auto& t) { + return o2::dcs::generateRandomDataPoints({t.aliasPattern}, t.minValue, t.maxValue, refDate); + }; + + for (const auto& hint : hints) { + auto dpcoms = std::visit(GenerateVisitor, hint); + for (auto dp : dpcoms) { + dataPoints.push_back(dp); + } + } + if (fraction < 1.0) { + auto indices = generateIntegers(fraction * dataPoints.size(), 0, dataPoints.size() - 1); + std::vector<o2::dcs::DataPointCompositeObject> tmp; + tmp.swap(dataPoints); + dataPoints.clear(); + for (auto i : indices) { + dataPoints.push_back(tmp[i]); + } + } + return dataPoints; +} + +/** + * DCSRandomDataGenerator is an example device that generates random + * DCS Data Points. + * + * The actual description of what is generated is hard-coded in + * the init() method. + */ +class DCSRandomDataGenerator : public o2::framework::Task +{ + using DPID = o2::dcs::DataPointIdentifier; + using DPVAL = o2::dcs::DataPointValue; + using DPCOM = o2::dcs::DataPointCompositeObject; + + public: + void init(o2::framework::InitContext& ic) final + { + mMaxTF = ic.options().get<int64_t>("max-timeframes"); + mDeltaFraction = ic.options().get<float>("delta-fraction"); + mMaxCyclesNoFullMap = ic.options().get<int64_t>("max-cycles-no-full-map"); + + // create the list of DataPointHints to be used by the generator + // each detector should create his own when running the tests + + /*mDataPointHints.emplace_back(DataPointHint<char>{"TestChar_0", 'A', 'z'}); + mDataPointHints.emplace_back(DataPointHint<double>{"TestDouble_[0..3]", 0, 1700}); + mDataPointHints.emplace_back(DataPointHint<int32_t>{"TestInt_[0..50000{:d}]", 0, 1234}); + mDataPointHints.emplace_back(DataPointHint<bool>{"TestBool_[00..03]", 0, 1}); + mDataPointHints.emplace_back(DataPointHint<std::string>{"TestString_0", "ABC", "ABCDEF"}); + */ + // for TOF + // for test, we use less DPs that official ones + mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_vp_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_vn_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_ip_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_in_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint<int32_t>{"TOF_FEACSTATUS_[00..01]", 0, 255}); + mTOFDataPointHints.emplace_back(DataPointHint<int32_t>{"TOF_HVSTATUS_SM[00..01]MOD[0..1]", 0, 524287}); + // for TOF, official list + //mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_vp_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_vn_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_ip_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint<double>{"tof_hv_in_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint<int32_t>{"TOF_FEACSTATUS_[00..71]", 0, 255}); + //mTOFDataPointHints.emplace_back(DataPointHint<int32_t>{"TOF_HVSTATUS_SM[00..17]MOD[0..4]", 0, 524287}); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto input = pc.inputs().begin(); + uint64_t tfid = o2::header::get<o2::framework::DataProcessingHeader*>((*input).header)->startTime; + if (tfid >= mMaxTF) { + LOG(INFO) << "Data generator reached TF " << tfid << ", stopping"; + pc.services().get<o2::framework::ControlService>().endOfStream(); + pc.services().get<o2::framework::ControlService>().readyToQuit(o2::framework::QuitRequest::Me); + } + + bool generateFBI = (mTFs % mMaxCyclesNoFullMap == 0); + // fraction is one if we generate FBI (Full Buffer Image) + float fraction = (generateFBI ? 1.0 : mDeltaFraction); + + TDatime d; + auto dpcoms = generate(mDataPointHints, fraction, tfid); + auto tofdpcoms = generate(mTOFDataPointHints, fraction, tfid); + + LOG(INFO) << "***************** TF " << tfid << " has generated " << tofdpcoms.size() << " DPs for TOF"; + pc.outputs().snapshot(Output{"DCS", "DATAPOINTS", 0, Lifetime::Timeframe}, dpcoms); + pc.outputs().snapshot(Output{"DCS", "TOFDATAPOINTS", 0, Lifetime::Timeframe}, tofdpcoms); + mTFs++; + } + + private: + uint64_t mMaxTF; + uint64_t mTFs = 0; + uint64_t mMaxCyclesNoFullMap; + float mDeltaFraction; + std::vector<HintType> mDataPointHints; + std::vector<HintType> mTOFDataPointHints; +}; + +} // namespace + +DataProcessorSpec getDCSRandomDataGeneratorSpec() +{ + return DataProcessorSpec{ + "dcs-random-data-generator", + Inputs{}, + Outputs{{{"outputDCS"}, "DCS", "DATAPOINTS"}, {{"outputDCSTOF"}, "DCS", "TOFDATAPOINTS"}}, + AlgorithmSpec{adaptFromTask<DCSRandomDataGenerator>()}, + Options{ + {"max-timeframes", VariantType::Int64, 99999999999ll, {"max TimeFrames to generate"}}, + {"delta-fraction", VariantType::Float, 0.05f, {"fraction of data points to put in the delta"}}, + {"max-cycles-no-full-map", VariantType::Int64, 6000ll, {"max num of cycles between the sending of 2 full maps"}}}}; +} + +#endif diff --git a/Detectors/DCS/testWorkflow/DCStoDPLconverter.h b/Detectors/DCS/testWorkflow/DCStoDPLconverter.h new file mode 100644 index 0000000000000..8b74f07c3ea26 --- /dev/null +++ b/Detectors/DCS/testWorkflow/DCStoDPLconverter.h @@ -0,0 +1,137 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DCS_TO_DPL_CONVERTER +#define O2_DCS_TO_DPL_CONVERTER + +#include "Framework/DataSpecUtils.h" +#include "Framework/ExternalFairMQDeviceProxy.h" +#include <fairmq/FairMQParts.h> +#include <fairmq/FairMQDevice.h> +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include <unordered_map> +#include <functional> +#include <string_view> + +namespace o2h = o2::header; +namespace o2f = o2::framework; + +// we need to provide hash function for the DataDescription +namespace std +{ +template <> +struct hash<o2h::DataDescription> { + std::size_t operator()(const o2h::DataDescription& d) const noexcept + { + return std::hash<std::string_view>{}({d.str, size_t(d.size)}); + } +}; +} // namespace std + +namespace o2 +{ +namespace dcs +{ +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; + +/// A callback function to retrieve the FairMQChannel name to be used for sending +/// messages of the specified OutputSpec + +o2f::InjectorFunction dcs2dpl(std::unordered_map<DPID, o2h::DataDescription>& dpid2group, uint64_t startTime, uint64_t step, bool verbose = false) +{ + + auto timesliceId = std::make_shared<size_t>(startTime); + return [dpid2group, timesliceId, step, verbose](FairMQDevice& device, FairMQParts& parts, o2f::ChannelRetriever channelRetriever) { + static std::unordered_map<DPID, DPCOM> cache; // will keep only the latest measurement in the 1-second wide window for each DPID + static auto timer = std::chrono::high_resolution_clock::now(); + + LOG(DEBUG) << "In lambda function: ********* Size of unordered_map (--> number of defined groups) = " << dpid2group.size(); + // We first iterate over the parts of the received message + for (size_t i = 0; i < parts.Size(); ++i) { // DCS sends only 1 part, but we should be able to receive more + auto nDPCOM = parts.At(i)->GetSize() / sizeof(DPCOM); // number of DPCOM in current part + for (size_t j = 0; j < nDPCOM; j++) { + const auto& src = *(reinterpret_cast<const DPCOM*>(parts.At(i)->GetData()) + j); + // do we want to check if this DP was requested ? + auto mapEl = dpid2group.find(src.id); + if (verbose) { + LOG(INFO) << "Received DP " << src.id << " (data = " << src.data << "), matched to output-> " << (mapEl == dpid2group.end() ? "none " : mapEl->second.as<std::string>()); + } + if (mapEl != dpid2group.end()) { + auto& dst = cache[src.id] = src; // this is needed in case in the 1s window we get a new value for the same DP + } + } + } + + auto timerNow = std::chrono::high_resolution_clock::now(); + std::chrono::duration<double, std::ratio<1>> duration = timerNow - timer; + if (duration.count() > 1) { //did we accumulate for 1 sec? + *timesliceId += step; // we increment only if we send something + std::unordered_map<o2h::DataDescription, vector<DPCOM>, std::hash<o2h::DataDescription>> outputs; + // in the cache we have the final values of the DPs that we should put in the output + // distribute DPs over the vectors for each requested output + for (auto& it : cache) { + auto mapEl = dpid2group.find(it.first); + if (mapEl != dpid2group.end()) { + outputs[mapEl->second].push_back(it.second); + } + } + + // create and send output messages + for (auto& it : outputs) { + o2h::DataHeader hdr(it.first, "DCS", 0); + o2f::OutputSpec outsp{hdr.dataOrigin, hdr.dataDescription, hdr.subSpecification}; + if (it.second.empty()) { + LOG(WARNING) << "No data for OutputSpec " << outsp; + continue; + } + auto channel = channelRetriever(outsp, *timesliceId); + if (channel.empty()) { + LOG(WARNING) << "No output channel found for OutputSpec " << outsp << ", discarding its data"; + it.second.clear(); + continue; + } + + hdr.tfCounter = *timesliceId; // this also + hdr.payloadSerializationMethod = o2h::gSerializationMethodNone; + hdr.splitPayloadParts = 1; + hdr.splitPayloadIndex = 1; + hdr.payloadSize = it.second.size() * sizeof(DPCOM); + hdr.firstTForbit = 0; // this should be irrelevant for DCS + o2h::Stack headerStack{hdr, o2::framework::DataProcessingHeader{*timesliceId, 0}}; + auto fmqFactory = device.GetChannel(channel).Transport(); + auto hdMessage = fmqFactory->CreateMessage(headerStack.size(), fair::mq::Alignment{64}); + auto plMessage = fmqFactory->CreateMessage(hdr.payloadSize, fair::mq::Alignment{64}); + memcpy(hdMessage->GetData(), headerStack.data(), headerStack.size()); + memcpy(plMessage->GetData(), it.second.data(), hdr.payloadSize); + if (verbose) { + LOG(INFO) << "Pushing " << it.second.size() << " DPs to output " << it.first.as<std::string>() << " for TimeSlice " << *timesliceId; + hdr.print(); + } + it.second.clear(); + FairMQParts outParts; + outParts.AddPart(std::move(hdMessage)); + outParts.AddPart(std::move(plMessage)); + o2f::sendOnChannel(device, outParts, channel); + } + + timer = timerNow; + cache.clear(); + } + }; +} + +} // namespace dcs +} // namespace o2 + +#endif /* O2_DCS_TO_DPL_CONVERTER_H */ diff --git a/Detectors/DCS/testWorkflow/README.md b/Detectors/DCS/testWorkflow/README.md new file mode 100644 index 0000000000000..b00847e3f7761 --- /dev/null +++ b/Detectors/DCS/testWorkflow/README.md @@ -0,0 +1,10 @@ +<!-- doxy +\page refDetectorsDCStestWorkflow testWorkflow +/doxy --> + +Local example workflow with local CCDB (running on port 6464) : + +```shell +o2-dcs-random-data-workflow --max-timeframes=10 | +o2-calibration-ccdb-populator-workflow --ccdb-path http://localhost:6464 +``` diff --git a/Detectors/DCS/testWorkflow/dcs-data-client-workflow.cxx b/Detectors/DCS/testWorkflow/dcs-data-client-workflow.cxx new file mode 100644 index 0000000000000..5f6eb0337f4b0 --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-data-client-workflow.cxx @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/TypeTraits.h" +#include "Framework/DataSpecUtils.h" + +#include "Framework/DataProcessorSpec.h" +#include "DCSConsumerSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getDCSConsumerSpec()); + return specs; +} diff --git a/Detectors/DCS/testWorkflow/dcs-data-workflow.cxx b/Detectors/DCS/testWorkflow/dcs-data-workflow.cxx new file mode 100644 index 0000000000000..e22ee95a0a76d --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-data-workflow.cxx @@ -0,0 +1,43 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "Framework/TypeTraits.h" +#include <unordered_map> +namespace o2::framework +{ +template <> +struct has_root_dictionary<std::unordered_map<o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue>, void> : std::true_type { +}; +} // namespace o2::framework +#include "Framework/DataProcessorSpec.h" +#include "DCSDataGeneratorSpec.h" +#include "DCSDataProcessorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getDCSDataGeneratorSpec()); + specs.emplace_back(getDCSDataProcessorSpec()); + return specs; +} diff --git a/Detectors/DCS/testWorkflow/dcs-proxy.cxx b/Detectors/DCS/testWorkflow/dcs-proxy.cxx new file mode 100644 index 0000000000000..04f2df7105857 --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-proxy.cxx @@ -0,0 +1,82 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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: +// o2-dcs-proxy --dcs-proxy '--channel-config "name=dcs-proxy,type=pull,method=connect,address=tcp://10.11.28.22:60000,rateLogging=1,transport=zeromq"' -b + +#include "Framework/WorkflowSpec.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/Lifetime.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ExternalFairMQDeviceProxy.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DCStoDPLconverter.h" +#include <vector> +#include <unordered_map> + +using namespace o2::framework; +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DeliveryType = o2::dcs::DeliveryType; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{"verbose", VariantType::Bool, false, {"verbose output"}}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + + bool verbose = config.options().get<bool>("verbose"); + DPID dpidtmp; + + std::unordered_map<DPID, o2h::DataDescription> dpid2DataDesc; + DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000100", DeliveryType::RAW_STRING); + dpid2DataDesc[dpidtmp] = "COMMON"; // i.e. this will go to {DCS/COMMON/0} OutputSpec + DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000110", DeliveryType::RAW_STRING); + dpid2DataDesc[dpidtmp] = "COMMON"; + DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000200", DeliveryType::RAW_STRING); + dpid2DataDesc[dpidtmp] = "COMMON1"; + DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000240", DeliveryType::RAW_INT); + dpid2DataDesc[dpidtmp] = "COMMON1"; + + // RS: here we should complete the attribution of different DPs to different outputs + // ... + + // now collect all required outputs to define OutputSpecs for specifyExternalFairMQDeviceProxy + std::unordered_map<o2h::DataDescription, int, std::hash<o2h::DataDescription>> outMap; + for (auto itdp : dpid2DataDesc) { + outMap[itdp.second]++; + } + + Outputs dcsOutputs; + for (auto itout : outMap) { + dcsOutputs.emplace_back("DCS", itout.first, 0, Lifetime::Timeframe); + } + + DataProcessorSpec dcsProxy = specifyExternalFairMQDeviceProxy( + "dcs-proxy", + std::move(dcsOutputs), + "type=pull,method=connect,address=tcp://aldcsadaposactor:60000,rateLogging=1,transport=zeromq", + dcs2dpl(dpid2DataDesc, 0, 1, verbose)); + + WorkflowSpec workflow; + workflow.emplace_back(dcsProxy); + return workflow; +} diff --git a/Detectors/DCS/testWorkflow/dcs-random-data-workflow.cxx b/Detectors/DCS/testWorkflow/dcs-random-data-workflow.cxx new file mode 100644 index 0000000000000..3f1539fb63e49 --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-random-data-workflow.cxx @@ -0,0 +1,43 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "Framework/TypeTraits.h" +#include <unordered_map> +namespace o2::framework +{ +template <> +struct has_root_dictionary<std::unordered_map<o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue>, void> : std::true_type { +}; +} // namespace o2::framework +#include "Framework/DataProcessorSpec.h" +#include "DCSRandomDataGeneratorSpec.h" +#include "DCSDataProcessorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getDCSRandomDataGeneratorSpec()); + specs.emplace_back(getDCSDataProcessorSpec()); + return specs; +} diff --git a/Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx b/Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx new file mode 100644 index 0000000000000..b378578105ebf --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx @@ -0,0 +1,35 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "Framework/TypeTraits.h" +#include <unordered_map> +#include "Framework/DataProcessorSpec.h" +#include "DCSRandomDataGeneratorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getDCSRandomDataGeneratorSpec()); + return specs; +} diff --git a/Detectors/EMCAL/base/CMakeLists.txt b/Detectors/EMCAL/base/CMakeLists.txt index 62b399719b9ed..b86b579230afb 100644 --- a/Detectors/EMCAL/base/CMakeLists.txt +++ b/Detectors/EMCAL/base/CMakeLists.txt @@ -36,3 +36,7 @@ o2_add_test(Mapper COMPONENT_NAME emcal LABELS emcal ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) + +o2_add_test_root_macro(test/testGeometryRowColIndexing.C + PUBLIC_LINK_LIBRARIES O2::EMCALBase + LABELS emcal) \ No newline at end of file diff --git a/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h b/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h index b114a10c4bcac..088a8cb796292 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h +++ b/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h @@ -18,7 +18,7 @@ #include "DataFormatsEMCAL/Cell.h" #include "DataFormatsEMCAL/AnalysisCluster.h" #include "EMCALBase/Geometry.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" namespace o2 { @@ -38,7 +38,7 @@ class ClusterFactory { public: - class ClusterRangeException : public std::exception + class ClusterRangeException final : public std::exception { public: /// \brief Constructor defining the error @@ -73,7 +73,7 @@ class ClusterFactory std::string mErrorMessage; ///< Error message }; - class CellIndexRangeException : public std::exception + class CellIndexRangeException final : public std::exception { public: /// \brief Constructor defining the error @@ -178,7 +178,7 @@ class ClusterFactory /// \param clustersContainer cluster container /// \param inputsContainer cells/digits container /// \param cellsIndices for cells/digits indices - ClusterFactory(gsl::span<const o2::emcal::Cluster> clustersContainer, gsl::span<const InputType> inputsContainer, gsl::span<int> cellsIndices); + ClusterFactory(gsl::span<const o2::emcal::Cluster> clustersContainer, gsl::span<const InputType> inputsContainer, gsl::span<const int> cellsIndices); /// /// Copy constructor @@ -225,17 +225,17 @@ class ClusterFactory /// /// Calculates the center of gravity in the local EMCAL-module coordinates - void evalLocalPosition(gsl::span<int> inputsIndices, AnalysisCluster& cluster) const; + void evalLocalPosition(gsl::span<const int> inputsIndices, AnalysisCluster& cluster) const; /// /// Calculates the center of gravity in the global ALICE coordinates - void evalGlobalPosition(gsl::span<int> inputsIndices, AnalysisCluster& cluster) const; + void evalGlobalPosition(gsl::span<const int> inputsIndices, AnalysisCluster& cluster) const; void evalLocal2TrackingCSTransform() const; /// /// evaluates local position of clusters in SM - void evalLocalPositionFit(Double_t deff, Double_t w0, Double_t phiSlope, gsl::span<int> inputsIndices, AnalysisCluster& cluster) const; + void evalLocalPositionFit(Double_t deff, Double_t w0, Double_t phiSlope, gsl::span<const int> inputsIndices, AnalysisCluster& cluster) const; /// /// Applied for simulation data with threshold 3 adc @@ -250,11 +250,11 @@ class ClusterFactory /// \return the index of the cells with max enegry /// \return the maximum energy /// \return the total energy of the cluster - std::tuple<int, float, float> getMaximalEnergyIndex(gsl::span<int> inputsIndices) const; + std::tuple<int, float, float> getMaximalEnergyIndex(gsl::span<const int> inputsIndices) const; /// /// Calculates the multiplicity of digits/cells with energy larger than level*energy - int getMultiplicityAtLevel(float level, gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const; + int getMultiplicityAtLevel(float level, gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const; int getSuperModuleNumber() const { return mSuperModuleNumber; } @@ -287,7 +287,7 @@ class ClusterFactory mInputsContainer = cellContainer; } - void setCellsIndicesContainer(gsl::span<int> indicesContainer) + void setCellsIndicesContainer(gsl::span<const int> indicesContainer) { mCellsIndices = indicesContainer; } @@ -305,21 +305,21 @@ class ClusterFactory /// should be less than 2% /// Unfinished - Nov 15,2006 /// Distance is calculate in (phi,eta) units - void evalCoreEnergy(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const; + void evalCoreEnergy(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const; /// /// Calculates the dispersion of the shower at the origin of the cluster /// in cell units - void evalDispersion(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const; + void evalDispersion(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const; /// /// Calculates the axis of the shower ellipsoid in eta and phi /// in cell units - void evalElipsAxis(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const; + void evalElipsAxis(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const; /// /// Time is set to the time of the digit with the maximum energy - void evalTime(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const; + void evalTime(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const; /// /// Converts Theta (Radians) to Eta (Radians) @@ -344,9 +344,9 @@ class ClusterFactory gsl::span<const o2::emcal::Cluster> mClustersContainer; ///< Container for all the clusters in the event gsl::span<const InputType> mInputsContainer; ///< Container for all the cells/digits in the event - gsl::span<int> mCellsIndices; ///< Container for cells indices in the event + gsl::span<const int> mCellsIndices; ///< Container for cells indices in the event - ClassDefNV(ClusterFactory, 1); + ClassDefNV(ClusterFactory, 2); }; } // namespace emcal diff --git a/Detectors/EMCAL/base/include/EMCALBase/Geometry.h b/Detectors/EMCAL/base/include/EMCALBase/Geometry.h index 79ed70438ee3b..c6e58ceb7cbea 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/Geometry.h +++ b/Detectors/EMCAL/base/include/EMCALBase/Geometry.h @@ -24,7 +24,7 @@ #include "DataFormatsEMCAL/Constants.h" #include "EMCALBase/GeometryBase.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" namespace o2 { @@ -140,21 +140,21 @@ class Geometry /// Calculates the impact coordinates on EMCAL (centre of a tower/not on EMCAL surface) /// of a neutral particle emitted in the vertex vtx[3] with direction theta and phi in /// the global coordinate system - void ImpactOnEmcal(const Point3D<double>& vtx, Double_t theta, Double_t phi, Int_t& absId, Point3D<double>& vimpact) const; + void ImpactOnEmcal(const math_utils::Point3D<double>& vtx, Double_t theta, Double_t phi, Int_t& absId, math_utils::Point3D<double>& vimpact) const; /// \brief Checks whether point is inside the EMCal volume /// \param pnt Point to be checked /// \return True if the point is inside EMCAL, false otherwise /// /// See IsInEMCALOrDCAL for the definition of the acceptance check - Bool_t IsInEMCAL(const Point3D<double>& pnt) const; + Bool_t IsInEMCAL(const math_utils::Point3D<double>& pnt) const; /// \brief Checks whether point is inside the DCal volume /// \param pnt Point to be checked /// \return True if the point is inside DCAL, false otherwise /// /// See IsInEMCALOrDCAL for the definition of the acceptance check - Bool_t IsInDCAL(const Point3D<double>& pnt) const; + Bool_t IsInDCAL(const math_utils::Point3D<double>& pnt) const; /// \brief Checks whether point is inside the EMCal volume (included DCal) /// \param pnt Point to be checked @@ -164,7 +164,7 @@ class Geometry /// /// Points behind EMCAl/DCal, i.e. R > outer radius, but eta, phi in acceptance /// are considered to inside - AcceptanceType_t IsInEMCALOrDCAL(const Point3D<double>& pnt) const; + AcceptanceType_t IsInEMCALOrDCAL(const math_utils::Point3D<double>& pnt) const; ////////////////////////////////////// // Return EMCAL geometrical parameters @@ -186,9 +186,19 @@ class Geometry Float_t GetDCALStandardPhiMax() const { return mDCALStandardPhiMax; } Int_t GetNECLayers() const { return mNECLayers; } Float_t GetDCALInnerExtandedEta() const { return mDCALInnerExtandedEta; } + + /// \brief Get the number of modules in supermodule in z- (beam) direction + /// \return Number of modules Int_t GetNZ() const { return mNZ; } + + /// \brief Get the number of modules in supermodule in #eta direction + /// \return Number of modules Int_t GetNEta() const { return mNZ; } + + /// \brief Get the number of modules in supermodule in #phi direction + /// \return Number of modules Int_t GetNPhi() const { return mNPhi; } + Float_t GetECPbRadThick() const { return mECPbRadThickness; } Float_t GetECScintThick() const { return mECScintThick; } Float_t GetSampling() const { return mSampling; } @@ -229,8 +239,9 @@ class Geometry // EMCALSMType GetSMType(Int_t nSupMod) const { - if (nSupMod >= mNumberOfSuperModules) + if (nSupMod >= mNumberOfSuperModules) { throw SupermoduleIndexException(nSupMod, mNumberOfSuperModules); + } return mEMCSMSystem[nSupMod]; } @@ -336,18 +347,42 @@ class Geometry /// \brief get (Column,Row) pair of cell in global numbering scheme /// \param cellID Absolute cell ID /// \return tuple with position in global numbering scheme (0 - row, 1 - column) + /// \throw InvalidCellIDException std::tuple<int, int> GlobalRowColFromIndex(int cellID) const; /// \brief Get column number of cell in global numbering scheme /// \param cellID Absolute cell ID /// \return Column number in global numbering scheme + /// \throw InvalidCellIDException int GlobalCol(int cellID) const; /// \brief Get row number of cell in global numbering scheme /// \param cellID Absolute cell ID /// \return Row number in global numbering scheme + /// \throw InvalidCellIDException int GlobalRow(int cellID) const; + /// \brief Get the absolute cell ID from global position in the EMCAL + /// \param row Global row ID + /// \param col Global col ID + /// \return absolute cell ID + /// \throw RowColException + int GetCellAbsIDFromGlobalRowCol(int row, int col) const; + + /// \brief Get the posision (row, col) of a global row-col position + /// \param row Global row ID + /// \param col Global col ID + /// \return Position in supermodule: [0 - supermodule ID, 1 - row in supermodule - col in supermodule] + /// \throw RowColException + std::tuple<int, int, int> GetPositionInSupermoduleFromGlobalRowCol(int col, int row) const; + + /// \brief Get the cell indices from global position in the EMCAL + /// \param row Global row ID + /// \param col Global col ID + /// \return Cell indices [0 - supermodule, 1 - module, 2 - phi in module, 3 - eta in module] + /// \throw RowColException + std::tuple<int, int, int, int> GetCellIndexFromGlobalRowCol(int row, int col) const; + /// \brief Given a global eta/phi point check if it belongs to a supermodule covered region. /// \param eta pseudorapidity location /// \param phi azimutal location @@ -421,14 +456,15 @@ class Geometry Int_t GetSuperModuleNumber(Int_t absId) const; Int_t GetNumberOfModuleInPhiDirection(Int_t nSupMod) const { - if (GetSMType(nSupMod) == EMCAL_HALF) + if (GetSMType(nSupMod) == EMCAL_HALF) { return mNPhi / 2; - else if (GetSMType(nSupMod) == EMCAL_THIRD) + } else if (GetSMType(nSupMod) == EMCAL_THIRD) { return mNPhi / 3; - else if (GetSMType(nSupMod) == DCAL_EXT) + } else if (GetSMType(nSupMod) == DCAL_EXT) { return mNPhi / 3; - else + } else { return mNPhi; + } } /// \brief Transition from cell indexes (iphi, ieta) to module indexes (iphim, ietam, nModule) @@ -457,13 +493,13 @@ class Geometry /// /// Same as RelPosCellInSModule(Int_t absId, Double_t &xr, Double_t &yr, Double_t &zr) /// but taking into account position of shower max. - Point3D<double> RelPosCellInSModule(Int_t absId, Double_t distEf) const; + math_utils::Point3D<double> RelPosCellInSModule(Int_t absId, Double_t distEf) const; /// \brief Look to see what the relative position inside a given cell is for a recpoint. /// \param absId cell absolute id. number, input /// \return Point3D with x,y,z coordinates of cell with absId inside SM /// \throw InvalidCellIDException if cell ID does not exist - Point3D<double> RelPosCellInSModule(Int_t absId) const; + math_utils::Point3D<double> RelPosCellInSModule(Int_t absId) const; std::vector<EMCALSMType> GetEMCSystem() const { return mEMCSMSystem; } // EMC System, SM type list // Local Coordinates of SM @@ -643,10 +679,11 @@ class Geometry inline Bool_t Geometry::CheckAbsCellId(Int_t absId) const { - if (absId < 0 || absId >= mNCells) + if (absId < 0 || absId >= mNCells) { return kFALSE; - else + } else { return kTRUE; + } } } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h b/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h index 2e84ae6e7641d..4b760ef78792b 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h +++ b/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h @@ -35,7 +35,7 @@ const std::string DEFAULT_GEOMETRY = "EMCAL_COMPLETE12SMV1_DCAL_8SM"; /// \class InvalidModuleException /// \brief Error Handling when an invalid module ID (outside the limits) is called /// \ingroup EMCALbase -class InvalidModuleException : public std::exception +class InvalidModuleException final : public std::exception { public: /// \brief Constructor @@ -72,7 +72,7 @@ class InvalidModuleException : public std::exception /// \class InvalidPositionException /// \brief Exception handling errors due to positions not in the EMCAL area /// \ingroup EMCALbase -class InvalidPositionException : public std::exception +class InvalidPositionException final : public std::exception { public: /// \brief Constructor, setting the position raising the exception @@ -109,7 +109,7 @@ class InvalidPositionException : public std::exception /// \class InvalidCellIDException /// \brief Exception handling non-existing cell IDs /// \ingroup EMCALbase -class InvalidCellIDException : public std::exception +class InvalidCellIDException final : public std::exception { public: /// \brief Constructor, setting cell ID raising the exception @@ -139,7 +139,7 @@ class InvalidCellIDException : public std::exception /// \class InvalidSupermoduleTypeException /// \brief Exception handling improper or uninitialized supermodule types /// \ingroup EMCALbase -class InvalidSupermoduleTypeException : public std::exception +class InvalidSupermoduleTypeException final : public std::exception { public: /// \brief constructor @@ -155,7 +155,7 @@ class InvalidSupermoduleTypeException : public std::exception /// \class SupermoduleIndexException /// \brief Handling error due to invalid supermodule /// \ingroup EMCALbase -class SupermoduleIndexException : public std::exception +class SupermoduleIndexException final : public std::exception { public: /// \brief Constructor, initializing the exception @@ -189,6 +189,40 @@ class SupermoduleIndexException : public std::exception std::string mMessage; ///< Error message }; +/// \class RowColException +/// \brief Handling error for invalid positions in row-column space +/// \ingroup EMCALBase +class RowColException final : public std::exception +{ + public: + /// \brief Constructor, initializing the exception with invalid row-column position + /// \param row Row ID of the position + /// \param col Column ID of the position + RowColException(int row, int col) : mRow(row), mCol(col), mMessage("") + { + mMessage = "Invalid position: row " + std::to_string(mRow) + ", col " + std::to_string(mCol); + } + + /// \brief Destructor + ~RowColException() noexcept final = default; + + /// \brief Get row of the position raising the exception + /// \return Row ID + int getRow() const noexcept { return mRow; } + + /// \brief Get column of the position raising the exception + /// \brief Column ID + int getCol() const noexcept { return mCol; } + + /// \brief Access tp error message of the exception + /// \return Error message + const char* what() const noexcept final { return mMessage.data(); } + + private: + int mRow, mCol; + std::string mMessage; +}; + } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/base/include/EMCALBase/Hit.h b/Detectors/EMCAL/base/include/EMCALBase/Hit.h index 8d8905ef83c01..15570bb09f7fb 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/Hit.h +++ b/Detectors/EMCAL/base/include/EMCALBase/Hit.h @@ -40,8 +40,8 @@ class Hit : public o2::BasicXYZEHit<float> /// \param mom Momentum vector for the particle at the point /// \param tof Time of the hit /// \param eLoss Energy loss - Hit(Int_t primary, Int_t trackID, Int_t detID, Double_t initialEnergy, const Point3D<float>& pos, - const Vector3D<float>& mom, Double_t tof, Double_t eLoss) + Hit(Int_t primary, Int_t trackID, Int_t detID, Double_t initialEnergy, const math_utils::Point3D<float>& pos, + const math_utils::Vector3D<float>& mom, Double_t tof, Double_t eLoss) : o2::BasicXYZEHit<float>(pos.X(), pos.Y(), pos.Z(), tof, eLoss, trackID, detID), mPvector(mom), mPrimary(primary), @@ -91,9 +91,9 @@ class Hit : public o2::BasicXYZEHit<float> void PrintStream(std::ostream& stream) const; private: - Vector3D<float> mPvector; ///< Momentum Vector - Int_t mPrimary; ///< Primary particles at the origin of the hit - Double32_t mInitialEnergy; ///< Energy of the parent particle that entered the EMCAL + math_utils::Vector3D<float> mPvector; ///< Momentum Vector + Int_t mPrimary; ///< Primary particles at the origin of the hit + Double32_t mInitialEnergy; ///< Energy of the parent particle that entered the EMCAL ClassDefNV(Hit, 1); }; diff --git a/Detectors/EMCAL/base/include/EMCALBase/Mapper.h b/Detectors/EMCAL/base/include/EMCALBase/Mapper.h index 6403f29206300..518803527b487 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/Mapper.h +++ b/Detectors/EMCAL/base/include/EMCALBase/Mapper.h @@ -306,7 +306,7 @@ class MappingHandler /// /// Error thrown in queries to the MappingHandler where the DDL ID is /// out-of-range for EMCAL. - class DDLInvalid : public std::exception + class DDLInvalid final : public std::exception { public: DDLInvalid(int ddlID) : mDDL(ddlID) { mMessage = fmt::format("DDL {0} not existing for EMCAL", mDDL); }; diff --git a/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h b/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h index 98d2798e31bb0..fb3f6e640ffb1 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h +++ b/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h @@ -161,8 +161,8 @@ class RCUTrailer private: int mRCUId = -1; ///< current RCU identifier unsigned char mFirmwareVersion = 0; ///< RCU firmware version - unsigned int mTrailerSize = 0; ///< Size of the trailer - unsigned int mPayloadSize = 0; ///< Size of the payload + unsigned int mTrailerSize = 0; ///< Size of the trailer (in number of 32 bit words) + unsigned int mPayloadSize = 0; ///< Size of the payload (in nunber of 32 bit words) unsigned int mFECERRA = 0; ///< contains errors related to ALTROBUS transactions unsigned int mFECERRB = 0; ///< contains errors related to ALTROBUS transactions unsigned short mERRREG2 = 0; ///< contains errors related to ALTROBUS transactions or trailer of ALTRO channel block diff --git a/Detectors/EMCAL/base/include/EMCALBase/ShishKebabTrd1Module.h b/Detectors/EMCAL/base/include/EMCALBase/ShishKebabTrd1Module.h index 4b9162665d92f..4e92dfda6e345 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/ShishKebabTrd1Module.h +++ b/Detectors/EMCAL/base/include/EMCALBase/ShishKebabTrd1Module.h @@ -88,10 +88,11 @@ class ShishKebabTrd1Module // ieta=0 or 1 - Jun 02, 2006 const TVector2& GetCenterOfCellInLocalCoordinateofSM(Int_t ieta) const { - if (ieta <= 0) + if (ieta <= 0) { return mOK2; - else + } else { return mOK1; + } } void GetCenterOfCellInLocalCoordinateofSM(Int_t ieta, Double_t& xr, Double_t& zr) const @@ -109,10 +110,12 @@ class ShishKebabTrd1Module void GetCenterOfCellInLocalCoordinateofSM3X3(Int_t ieta, Double_t& xr, Double_t& zr) const { // 3X3 case - Nov 9,2006 - if (ieta < 0) + if (ieta < 0) { ieta = 0; // ieta = ieta<0? ieta=0 : ieta; // check index - if (ieta > 2) + } + if (ieta > 2) { ieta = 2; // ieta = ieta>2? ieta=2 : ieta; + } xr = mOK3X3[2 - ieta].Y(); zr = mOK3X3[2 - ieta].X(); } @@ -127,10 +130,11 @@ class ShishKebabTrd1Module const TVector2& GetCenterOfModuleFace() const { return mOB; } const TVector2& GetCenterOfModuleFace(Int_t ieta) const { - if (ieta <= 0) + if (ieta <= 0) { return mOB2; - else + } else { return mOB1; + } } // Jul 30, 2007 diff --git a/Detectors/EMCAL/base/src/ClusterFactory.cxx b/Detectors/EMCAL/base/src/ClusterFactory.cxx index 3232466da7d43..60a95e8b0ee6c 100644 --- a/Detectors/EMCAL/base/src/ClusterFactory.cxx +++ b/Detectors/EMCAL/base/src/ClusterFactory.cxx @@ -18,14 +18,14 @@ #include "DataFormatsEMCAL/AnalysisCluster.h" #include "DataFormatsEMCAL/Constants.h" #include "EMCALBase/Geometry.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "EMCALBase/ClusterFactory.h" using namespace o2::emcal; template <class InputType> -ClusterFactory<InputType>::ClusterFactory(gsl::span<const o2::emcal::Cluster> clustersContainer, gsl::span<const InputType> inputsContainer, gsl::span<int> cellsIndices) +ClusterFactory<InputType>::ClusterFactory(gsl::span<const o2::emcal::Cluster> clustersContainer, gsl::span<const InputType> inputsContainer, gsl::span<const int> cellsIndices) { setClustersContainer(clustersContainer); setCellsContainer(inputsContainer); @@ -46,8 +46,9 @@ void ClusterFactory<InputType>::reset() template <class InputType> o2::emcal::AnalysisCluster ClusterFactory<InputType>::buildCluster(int clusterIndex) const { - if (clusterIndex >= mClustersContainer.size()) + if (clusterIndex >= mClustersContainer.size()) { throw ClusterRangeException(clusterIndex, mClustersContainer.size()); + } o2::emcal::AnalysisCluster clusterAnalysis; clusterAnalysis.setID(clusterIndex); @@ -55,7 +56,7 @@ o2::emcal::AnalysisCluster ClusterFactory<InputType>::buildCluster(int clusterIn int firstCellIndex = mClustersContainer[clusterIndex].getCellIndexFirst(); int nCells = mClustersContainer[clusterIndex].getNCells(); - gsl::span<int> inputsIndices = gsl::span<int>(&mCellsIndices[firstCellIndex], nCells); + gsl::span<const int> inputsIndices = gsl::span<const int>(&mCellsIndices[firstCellIndex], nCells); // First calculate the index of input with maximum amplitude and get // the supermodule number where it sits. @@ -107,7 +108,7 @@ o2::emcal::AnalysisCluster ClusterFactory<InputType>::buildCluster(int clusterIn /// in cell units //____________________________________________________________________________ template <class InputType> -void ClusterFactory<InputType>::evalDispersion(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +void ClusterFactory<InputType>::evalDispersion(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { double d = 0., wtot = 0.; int nstat = 0; @@ -124,8 +125,9 @@ void ClusterFactory<InputType>::evalDispersion(gsl::span<int> inputsIndices, Ana // In case of a shared cluster, index of SM in C side, columns start at 48 and ends at 48*2 // C Side impair SM, nSupMod%2=1; A side pair SM nSupMod%2=0 - if (mSharedCluster && nSupMod % 2) + if (mSharedCluster && nSupMod % 2) { ieta += EMCAL_COLS; + } double etai = (double)ieta; double phii = (double)iphi; @@ -142,8 +144,9 @@ void ClusterFactory<InputType>::evalDispersion(gsl::span<int> inputsIndices, Ana if (wtot > 0) { phiMean /= wtot; etaMean /= wtot; - } else + } else { LOG(ERROR) << Form("Wrong weight %f\n", wtot); + } // Calculate dispersion for (auto iInput : inputsIndices) { @@ -154,8 +157,9 @@ void ClusterFactory<InputType>::evalDispersion(gsl::span<int> inputsIndices, Ana // In case of a shared cluster, index of SM in C side, columns start at 48 and ends at 48*2 // C Side impair SM, nSupMod%2=1; A side pair SM, nSupMod%2=0 - if (mSharedCluster && nSupMod % 2) + if (mSharedCluster && nSupMod % 2) { ieta += EMCAL_COLS; + } double etai = (double)ieta; double phii = (double)iphi; @@ -168,10 +172,11 @@ void ClusterFactory<InputType>::evalDispersion(gsl::span<int> inputsIndices, Ana } } - if (wtot > 0 && nstat > 1) + if (wtot > 0 && nstat > 1) { d /= wtot; - else + } else { d = 0.; + } clusterAnalysis.setDispersion(TMath::Sqrt(d)); } @@ -180,7 +185,7 @@ void ClusterFactory<InputType>::evalDispersion(gsl::span<int> inputsIndices, Ana /// Calculates the center of gravity in the local EMCAL-module coordinates //____________________________________________________________________________ template <class InputType> -void ClusterFactory<InputType>::evalLocalPosition(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +void ClusterFactory<InputType>::evalLocalPosition(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { int nstat = 0; @@ -199,13 +204,15 @@ void ClusterFactory<InputType>::evalLocalPosition(gsl::span<int> inputsIndices, } //Temporal patch, due to mapping problem, need to swap "y" in one of the 2 SM, although no effect in position calculation. GCB 05/2010 - if (mSharedCluster && mSuperModuleNumber != mGeomPtr->GetSuperModuleNumber(mInputsContainer[iInput].getTower())) + if (mSharedCluster && mSuperModuleNumber != mGeomPtr->GetSuperModuleNumber(mInputsContainer[iInput].getTower())) { xyzi[1] *= -1; + } - if (mLogWeight > 0.0) + if (mLogWeight > 0.0) { w = TMath::Max(0., mLogWeight + TMath::Log(mInputsContainer[iInput].getEnergy() / clusterAnalysis.E())); - else + } else { w = mInputsContainer[iInput].getEnergy(); // just energy + } if (w > 0.0) { wtot += w; @@ -229,12 +236,14 @@ void ClusterFactory<InputType>::evalLocalPosition(gsl::span<int> inputsIndices, clRmsXYZ[i] /= (wtot * wtot); clRmsXYZ[i] = clRmsXYZ[i] - clXYZ[i] * clXYZ[i]; - if (clRmsXYZ[i] > 0.0) + if (clRmsXYZ[i] > 0.0) { clRmsXYZ[i] = TMath::Sqrt(clRmsXYZ[i]); - else + } else { clRmsXYZ[i] = 0; - } else + } + } else { clRmsXYZ[i] = 0; + } } } else { for (int i = 0; i < 3; i++) { @@ -242,14 +251,14 @@ void ClusterFactory<InputType>::evalLocalPosition(gsl::span<int> inputsIndices, } } - clusterAnalysis.setLocalPosition(Point3D<float>(clXYZ[0], clXYZ[1], clXYZ[2])); + clusterAnalysis.setLocalPosition(math_utils::Point3D<float>(clXYZ[0], clXYZ[1], clXYZ[2])); } /// /// Calculates the center of gravity in the global ALICE coordinates //____________________________________________________________________________ template <class InputType> -void ClusterFactory<InputType>::evalGlobalPosition(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +void ClusterFactory<InputType>::evalGlobalPosition(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { int i = 0, nstat = 0; @@ -271,10 +280,11 @@ void ClusterFactory<InputType>::evalGlobalPosition(gsl::span<int> inputsIndices, // Now get the global coordinate mGeomPtr->GetGlobal(lxyzi, xyzi, mGeomPtr->GetSuperModuleNumber(mInputsContainer[iInput].getTower())); - if (mLogWeight > 0.0) + if (mLogWeight > 0.0) { w = TMath::Max(0., mLogWeight + TMath::Log(mInputsContainer[iInput].getEnergy() / clusterAnalysis.E())); - else + } else { w = mInputsContainer[iInput].getEnergy(); // just energy + } if (w > 0.0) { wtot += w; @@ -298,12 +308,14 @@ void ClusterFactory<InputType>::evalGlobalPosition(gsl::span<int> inputsIndices, clRmsXYZ[i] /= (wtot * wtot); clRmsXYZ[i] = clRmsXYZ[i] - clXYZ[i] * clXYZ[i]; - if (clRmsXYZ[i] > 0.0) + if (clRmsXYZ[i] > 0.0) { clRmsXYZ[i] = TMath::Sqrt(clRmsXYZ[i]); - else + } else { clRmsXYZ[i] = 0; - } else + } + } else { clRmsXYZ[i] = 0; + } } } else { for (i = 0; i < 3; i++) { @@ -311,7 +323,7 @@ void ClusterFactory<InputType>::evalGlobalPosition(gsl::span<int> inputsIndices, } } - clusterAnalysis.setGlobalPosition(Point3D<float>(clXYZ[0], clXYZ[1], clXYZ[2])); + clusterAnalysis.setGlobalPosition(math_utils::Point3D<float>(clXYZ[0], clXYZ[1], clXYZ[2])); } /// @@ -319,7 +331,7 @@ void ClusterFactory<InputType>::evalGlobalPosition(gsl::span<int> inputsIndices, //____________________________________________________________________________ template <class InputType> void ClusterFactory<InputType>::evalLocalPositionFit(double deff, double mLogWeight, - double phiSlope, gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const + double phiSlope, gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { int i = 0, nstat = 0; double clXYZ[3] = {0., 0., 0.}, clRmsXYZ[3] = {0., 0., 0.}, xyzi[3], wtot = 0., w = 0.; @@ -333,10 +345,11 @@ void ClusterFactory<InputType>::evalLocalPositionFit(double deff, double mLogWei continue; } - if (mLogWeight > 0.0) + if (mLogWeight > 0.0) { w = TMath::Max(0., mLogWeight + TMath::Log(mInputsContainer[iInput].getEnergy() / clusterAnalysis.E())); - else + } else { w = mInputsContainer[iInput].getEnergy(); // just energy + } if (w > 0.0) { wtot += w; @@ -360,12 +373,14 @@ void ClusterFactory<InputType>::evalLocalPositionFit(double deff, double mLogWei clRmsXYZ[i] /= (wtot * wtot); clRmsXYZ[i] = clRmsXYZ[i] - clXYZ[i] * clXYZ[i]; - if (clRmsXYZ[i] > 0.0) + if (clRmsXYZ[i] > 0.0) { clRmsXYZ[i] = TMath::Sqrt(clRmsXYZ[i]); - else + } else { clRmsXYZ[i] = 0; - } else + } + } else { clRmsXYZ[i] = 0; + } } } else { for (i = 0; i < 3; i++) { @@ -384,7 +399,7 @@ void ClusterFactory<InputType>::evalLocalPositionFit(double deff, double mLogWei clXYZ[1] = ycorr; } - clusterAnalysis.setLocalPosition(Point3D<float>(clXYZ[0], clXYZ[1], clXYZ[2])); + clusterAnalysis.setLocalPosition(math_utils::Point3D<float>(clXYZ[0], clXYZ[1], clXYZ[2])); } /// @@ -416,13 +431,14 @@ void ClusterFactory<InputType>::getDeffW0(const double esum, double& deff, doubl /// Distance is calculate in (phi,eta) units //______________________________________________________________________________ template <class InputType> -void ClusterFactory<InputType>::evalCoreEnergy(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +void ClusterFactory<InputType>::evalCoreEnergy(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { float coreEnergy = 0.; - if (!clusterAnalysis.getLocalPosition().Mag2()) + if (!clusterAnalysis.getLocalPosition().Mag2()) { evalLocalPosition(inputsIndices, clusterAnalysis); + } double phiPoint = clusterAnalysis.getLocalPosition().Phi(); double etaPoint = clusterAnalysis.getLocalPosition().Eta(); @@ -433,8 +449,9 @@ void ClusterFactory<InputType>::evalCoreEnergy(gsl::span<int> inputsIndices, Ana double distance = TMath::Sqrt((eta - etaPoint) * (eta - etaPoint) + (phi - phiPoint) * (phi - phiPoint)); - if (distance < mCoreRadius) + if (distance < mCoreRadius) { coreEnergy += mInputsContainer[iInput].getEnergy(); + } } clusterAnalysis.setCoreEnergy(coreEnergy); } @@ -444,7 +461,7 @@ void ClusterFactory<InputType>::evalCoreEnergy(gsl::span<int> inputsIndices, Ana /// in cell units //____________________________________________________________________________ template <class InputType> -void ClusterFactory<InputType>::evalElipsAxis(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +void ClusterFactory<InputType>::evalElipsAxis(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { TString gn(mGeomPtr->GetName()); @@ -464,8 +481,9 @@ void ClusterFactory<InputType>::evalElipsAxis(gsl::span<int> inputsIndices, Anal // In case of a shared cluster, index of SM in C side, columns start at 48 and ends at 48*2 // C Side impair SM, nSupMod%2=1; A side pair SM, nSupMod%2=0 - if (mSharedCluster && nSupMod % 2) + if (mSharedCluster && nSupMod % 2) { ieta += EMCAL_COLS; + } double etai = (double)ieta; double phii = (double)iphi; @@ -497,17 +515,19 @@ void ClusterFactory<InputType>::evalElipsAxis(gsl::span<int> inputsIndices, Anal lambda[0] = 0.5 * (dxx + dzz) + TMath::Sqrt(0.25 * (dxx - dzz) * (dxx - dzz) + dxz * dxz); - if (lambda[0] > 0) + if (lambda[0] > 0) { lambda[0] = TMath::Sqrt(lambda[0]); - else + } else { lambda[0] = 0; + } lambda[1] = 0.5 * (dxx + dzz) - TMath::Sqrt(0.25 * (dxx - dzz) * (dxx - dzz) + dxz * dxz); - if (lambda[1] > 0) //To avoid exception if numerical errors lead to negative lambda. + if (lambda[1] > 0) { //To avoid exception if numerical errors lead to negative lambda. lambda[1] = TMath::Sqrt(lambda[1]); - else + } else { lambda[1] = 0.; + } } else { lambda[0] = 0.; lambda[1] = 0.; @@ -521,15 +541,16 @@ void ClusterFactory<InputType>::evalElipsAxis(gsl::span<int> inputsIndices, Anal /// Finds the maximum energy in the cluster and computes the Summed amplitude of digits/cells //____________________________________________________________________________ template <class InputType> -std::tuple<int, float, float> ClusterFactory<InputType>::getMaximalEnergyIndex(gsl::span<int> inputsIndices) const +std::tuple<int, float, float> ClusterFactory<InputType>::getMaximalEnergyIndex(gsl::span<const int> inputsIndices) const { float energy = 0.; int mid = 0; float cellAmp = 0; for (auto iInput : inputsIndices) { - if (iInput >= mInputsContainer.size()) + if (iInput >= mInputsContainer.size()) { throw CellIndexRangeException(iInput, mInputsContainer.size()); + } cellAmp += mInputsContainer[iInput].getEnergy(); if (mInputsContainer[iInput].getEnergy() > energy) { energy = mInputsContainer[iInput].getEnergy(); @@ -544,12 +565,13 @@ std::tuple<int, float, float> ClusterFactory<InputType>::getMaximalEnergyIndex(g /// Calculates the multiplicity of inputs with energy larger than H*energy //____________________________________________________________________________ template <class InputType> -int ClusterFactory<InputType>::getMultiplicityAtLevel(float H, gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +int ClusterFactory<InputType>::getMultiplicityAtLevel(float H, gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { int multipl = 0; for (auto iInput : inputsIndices) { - if (mInputsContainer[iInput].getEnergy() > H * clusterAnalysis.E()) + if (mInputsContainer[iInput].getEnergy() > H * clusterAnalysis.E()) { multipl++; + } } return multipl; @@ -559,7 +581,7 @@ int ClusterFactory<InputType>::getMultiplicityAtLevel(float H, gsl::span<int> in /// Time is set to the time of the input with the maximum energy //____________________________________________________________________________ template <class InputType> -void ClusterFactory<InputType>::evalTime(gsl::span<int> inputsIndices, AnalysisCluster& clusterAnalysis) const +void ClusterFactory<InputType>::evalTime(gsl::span<const int> inputsIndices, AnalysisCluster& clusterAnalysis) const { float maxE = 0; unsigned short maxAt = 0; @@ -587,10 +609,11 @@ double ClusterFactory<InputType>::tMaxInCm(const double e, const int key) const if (e > 0.1) { tmax = TMath::Log(e) + ca; - if (key == 0) + if (key == 0) { tmax += 0.5; - else + } else { tmax -= 0.5; + } tmax *= x0; // convert to cm } @@ -633,10 +656,11 @@ bool ClusterFactory<InputType>::ClusterIterator::operator==(const ClusterFactory template <class InputType> typename ClusterFactory<InputType>::ClusterIterator& ClusterFactory<InputType>::ClusterIterator::operator++() { - if (mForward) + if (mForward) { mClusterID++; - else + } else { mClusterID--; + } mCurrentCluster = mClusterFactory.buildCluster(mClusterID); return *this; } @@ -652,10 +676,11 @@ typename ClusterFactory<InputType>::ClusterIterator ClusterFactory<InputType>::C template <class InputType> typename ClusterFactory<InputType>::ClusterIterator& ClusterFactory<InputType>::ClusterIterator::operator--() { - if (mForward) + if (mForward) { mClusterID--; - else + } else { mClusterID++; + } mCurrentCluster = mClusterFactory.buildCluster(mClusterID); return *this; } @@ -669,4 +694,4 @@ typename ClusterFactory<InputType>::ClusterIterator ClusterFactory<InputType>::C } template class o2::emcal::ClusterFactory<o2::emcal::Cell>; -template class o2::emcal::ClusterFactory<o2::emcal::Digit>; \ No newline at end of file +template class o2::emcal::ClusterFactory<o2::emcal::Digit>; diff --git a/Detectors/EMCAL/base/src/EMCALBaseLinkDef.h b/Detectors/EMCAL/base/src/EMCALBaseLinkDef.h index c80b7825b65f7..865b3fd0d5e6d 100644 --- a/Detectors/EMCAL/base/src/EMCALBaseLinkDef.h +++ b/Detectors/EMCAL/base/src/EMCALBaseLinkDef.h @@ -18,6 +18,7 @@ #pragma link C++ class o2::emcal::Geometry + ; #pragma link C++ class o2::emcal::Mapper + ; #pragma link C++ class o2::emcal::MappingHandler + ; +#pragma link C++ class o2::emcal::RCUTrailer + ; #pragma link C++ class o2::emcal::ClusterFactory < o2::emcal::Cell> + ; #pragma link C++ class o2::emcal::ClusterFactory < o2::emcal::Digit> + ; diff --git a/Detectors/EMCAL/base/src/Geometry.cxx b/Detectors/EMCAL/base/src/Geometry.cxx index cf35978498437..239abb334e42d 100644 --- a/Detectors/EMCAL/base/src/Geometry.cxx +++ b/Detectors/EMCAL/base/src/Geometry.cxx @@ -180,8 +180,9 @@ Geometry::~Geometry() } for (Int_t smod = 0; smod < mNumberOfSuperModules; smod++) { - if (SMODULEMATRIX[smod]) + if (SMODULEMATRIX[smod]) { delete SMODULEMATRIX[smod]; + } } } @@ -304,17 +305,18 @@ void Geometry::DefineSamplingFraction(const std::string_view mcname, const std:: } Float_t samplingFactorTranportModel = 1.; - if (contains(mcname, "Geant3")) + if (contains(mcname, "Geant3")) { samplingFactorTranportModel = 1.; // 0.988 // Do nothing - else if (contains(mcname, "Fluka")) + } else if (contains(mcname, "Fluka")) { samplingFactorTranportModel = 1.; // To be set - else if (contains(mcname, "Geant4")) { - if (contains(mctitle, "EMV-EMCAL")) + } else if (contains(mcname, "Geant4")) { + if (contains(mctitle, "EMV-EMCAL")) { samplingFactorTranportModel = 0.821; // EMC list but for EMCal, before 0.86 - else if (contains(mctitle, "EMV")) + } else if (contains(mctitle, "EMV")) { samplingFactorTranportModel = 1.096; // 0.906, 0.896 (OPT) - else + } else { samplingFactorTranportModel = 0.821; // 1.15 (CHIPS), 1.149 (BERT), 1.147 (BERT_CHIPS) + } } LOG(INFO) << "MC modeler <" << mcname << ">, Title <" << mctitle << ">: Sampling " << std::setw(2) @@ -340,8 +342,9 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) } } - if (contains(mGeoName, "WSUC")) + if (contains(mGeoName, "WSUC")) { mGeoName = "EMCAL_WSUC"; + } // check that we have a valid geometry name if (!(contains(mGeoName, "EMCAL_PDC06") || contains(mGeoName, "EMCAL_WSUC") || contains(mGeoName, "EMCAL_COMPLETE") || @@ -352,10 +355,12 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) // Option to know whether we have the "half" supermodule(s) or not mKey110DEG = 0; - if (contains(mGeoName, "COMPLETE") || contains(mGeoName, "PDC06") || contains(mGeoName, "12SM")) + if (contains(mGeoName, "COMPLETE") || contains(mGeoName, "PDC06") || contains(mGeoName, "12SM")) { mKey110DEG = 1; // for GetAbsCellId - if (contains(mGeoName, "COMPLETEV1")) + } + if (contains(mGeoName, "COMPLETEV1")) { mKey110DEG = 0; + } mnSupModInDCAL = 0; if (contains(mGeoName, "DCAL_DEV")) { @@ -447,10 +452,11 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) if (contains(mGeoName, "DCAL")) { mNumberOfSuperModules = 12 + mnSupModInDCAL; mArm1PhiMax = 320.0; - if (contains(mGeoName, "DCAL_8SM")) + if (contains(mGeoName, "DCAL_8SM")) { mArm1PhiMax = 340.0; // degrees, End of DCAL Phi position - else if (contains(mGeoName, "DCAL_DEV")) + } else if (contains(mGeoName, "DCAL_DEV")) { mArm1PhiMin = 40.0; // degrees, Starting EMCAL(shifted) Phi position + } mDCALPhiMin = mArm1PhiMax - 10. * mnSupModInDCAL; } } @@ -460,8 +466,9 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) mEMCSMSystem.clear(); mEMCSMSystem.resize(mNumberOfSuperModules); - for (Int_t i = 0; i < mNumberOfSuperModules; i++) + for (Int_t i = 0; i < mNumberOfSuperModules; i++) { mEMCSMSystem[i] = NOT_EXISTENT; + } Int_t iSM = 0; @@ -521,23 +528,25 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) mNCellsInSupMod = mNCellsInModule * mNPhi * mNZ; mNCells = 0; for (int i = 0; i < mNumberOfSuperModules; i++) { - if (GetSMType(i) == EMCAL_STANDARD) + if (GetSMType(i) == EMCAL_STANDARD) { mNCells += mNCellsInSupMod; - else if (GetSMType(i) == EMCAL_HALF) + } else if (GetSMType(i) == EMCAL_HALF) { mNCells += mNCellsInSupMod / 2; - else if (GetSMType(i) == EMCAL_THIRD) + } else if (GetSMType(i) == EMCAL_THIRD) { mNCells += mNCellsInSupMod / 3; - else if (GetSMType(i) == DCAL_STANDARD) + } else if (GetSMType(i) == DCAL_STANDARD) { mNCells += 2 * mNCellsInSupMod / 3; - else if (GetSMType(i) == DCAL_EXT) + } else if (GetSMType(i) == DCAL_EXT) { mNCells += mNCellsInSupMod / 3; - else + } else { LOG(ERROR) << "Uknown SuperModule Type !!\n"; + } } mNPhiSuperModule = mNumberOfSuperModules / 2; - if (mNPhiSuperModule < 1) + if (mNPhiSuperModule < 1) { mNPhiSuperModule = 1; + } mPhiTileSize = mPhiModuleSize / double(mNPHIdiv) - mLateralSteelStrip; // 13-may-05 mEtaTileSize = mEtaModuleSize / double(mNETAdiv) - mLateralSteelStrip; // 13-may-05 @@ -550,8 +559,9 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) } m2Trd1Dx2 = mEtaModuleSize + 2. * mLongModuleSize * TMath::Tan(mTrd1Angle * TMath::DegToRad() / 2.); - if (!contains(mGeoName, "WSUC")) + if (!contains(mGeoName, "WSUC")) { mShellThickness = TMath::Sqrt(mLongModuleSize * mLongModuleSize + m2Trd1Dx2 * m2Trd1Dx2); + } // These parameters are used to create the mother volume to hold the supermodules // 2cm padding added to allow for misalignments - JLK 30-May-2008 @@ -613,19 +623,20 @@ void Geometry::DefineEMC(std::string_view mcname, std::string_view mctitle) mEMCALPhiMax = mArm1PhiMin; mDCALPhiMax = mDCALPhiMin; // DCAl extention will not be included for (Int_t i = 0; i < mNumberOfSuperModules; i += 2) { - if (GetSMType(i) == EMCAL_STANDARD) + if (GetSMType(i) == EMCAL_STANDARD) { mEMCALPhiMax += 20.; - else if (GetSMType(i) == EMCAL_HALF) + } else if (GetSMType(i) == EMCAL_HALF) { mEMCALPhiMax += mPhiSuperModule / 2. + innerExtandedPhi; - else if (GetSMType(i) == EMCAL_THIRD) + } else if (GetSMType(i) == EMCAL_THIRD) { mEMCALPhiMax += mPhiSuperModule / 3. + 4.0 * innerExtandedPhi / 3.0; - else if (GetSMType(i) == DCAL_STANDARD) { + } else if (GetSMType(i) == DCAL_STANDARD) { mDCALPhiMax += 20.; mDCALStandardPhiMax = mDCALPhiMax; - } else if (GetSMType(i) == DCAL_EXT) + } else if (GetSMType(i) == DCAL_EXT) { mDCALPhiMax += mPhiSuperModule / 3. + 4.0 * innerExtandedPhi / 3.0; - else + } else { LOG(ERROR) << "Unkown SM Type!!\n"; + } } // for compatible reason // if(fNumberOfSuperModules == 4) {fEMCALPhiMax = fArm1PhiMax ;} @@ -704,25 +715,27 @@ Int_t Geometry::GetAbsCellId(Int_t nSupMod, Int_t nModule, Int_t nIphi, Int_t nI // 0 <= absid < fNCells Int_t id = 0; // have to change from 0 to fNCells-1 for (int i = 0; i < nSupMod; i++) { - if (GetSMType(i) == EMCAL_STANDARD) + if (GetSMType(i) == EMCAL_STANDARD) { id += mNCellsInSupMod; - else if (GetSMType(i) == EMCAL_HALF) + } else if (GetSMType(i) == EMCAL_HALF) { id += mNCellsInSupMod / 2; - else if (GetSMType(i) == EMCAL_THIRD) + } else if (GetSMType(i) == EMCAL_THIRD) { id += mNCellsInSupMod / 3; - else if (GetSMType(i) == DCAL_STANDARD) + } else if (GetSMType(i) == DCAL_STANDARD) { id += 2 * mNCellsInSupMod / 3; - else if (GetSMType(i) == DCAL_EXT) + } else if (GetSMType(i) == DCAL_EXT) { id += mNCellsInSupMod / 3; - else + } else { throw InvalidSupermoduleTypeException(); + } } id += mNCellsInModule * nModule; id += mNPHIdiv * nIphi; id += nIeta; - if (!CheckAbsCellId(id)) + if (!CheckAbsCellId(id)) { id = -TMath::Abs(id); // if negative something wrong + } return id; } @@ -756,6 +769,9 @@ Int_t Geometry::GetAbsCellIdFromCellIndexes(Int_t nSupMod, Int_t iphi, Int_t iet std::tuple<int, int> Geometry::GlobalRowColFromIndex(int cellID) const { + if (cellID >= GetNCells()) { + throw InvalidCellIDException(cellID); + } auto [supermodule, module, phiInModule, etaInModule] = GetCellIndex(cellID); auto [row, col] = GetCellPhiEtaIndexInSModule(supermodule, module, phiInModule, etaInModule); // add offsets (row / col per supermodule) @@ -763,8 +779,9 @@ std::tuple<int, int> Geometry::GlobalRowColFromIndex(int cellID) const // DCal odd SMs need shift of the col. index in oder to get the global col. index col += 16; } - if (supermodule % 2) + if (supermodule % 2) { col += mNZ * 2; + } int sector = supermodule / 2; if (sector > 0) { for (int isec = 0; isec < sector; isec++) { @@ -776,6 +793,63 @@ std::tuple<int, int> Geometry::GlobalRowColFromIndex(int cellID) const return std::make_tuple(row, col); } +std::tuple<int, int, int> Geometry::GetPositionInSupermoduleFromGlobalRowCol(int row, int col) const +{ + if (col < 0 || col >= 4 * GetNEta()) { + throw RowColException(row, col); + } + int side = col < GetNEta() * 2 ? 0 : 1, + colSM = col % (GetNEta() * 2); + int sector = -1, + rowSM = row; + for (int isec = 0; isec < GetNPhiSuperModule(); isec++) { + auto smtype = GetSMType(isec * 2); + auto nphism = GetNPhi() * 2; + if (smtype == EMCAL_THIRD || smtype == DCAL_EXT) { + nphism /= 3; + } + if (rowSM < nphism) { + sector = isec; + break; + } + rowSM -= nphism; + } + if (sector < 0) { + throw RowColException(row, col); + } + int supermodule = sector * 2 + side; + if (supermodule == 13 || supermodule == 15 || supermodule == 17) { + // DCal odd SMs need shift of the col. index as global col index includes PHOS hole + colSM -= 16; + if (colSM < 0) { + throw RowColException(row, col); // Position inside PHOS hole specified + } + } + if (supermodule == 12 || supermodule == 14 || supermodule == 16) { + if (colSM > 32) { + throw RowColException(row, col); // Position inside PHOS hole specified + } + } + return std::make_tuple(supermodule, rowSM, colSM); +} + +int Geometry::GetCellAbsIDFromGlobalRowCol(int row, int col) const +{ + auto [supermodule, rowSM, colSM] = GetPositionInSupermoduleFromGlobalRowCol(row, col); + return GetAbsCellIdFromCellIndexes(supermodule, rowSM, colSM); +} + +std::tuple<int, int, int, int> Geometry::GetCellIndexFromGlobalRowCol(int row, int col) const +{ + auto [supermodule, rowSM, colSM] = GetPositionInSupermoduleFromGlobalRowCol(row, col); + auto indexmod = GetModuleIndexesFromCellIndexesInSModule(supermodule, rowSM, colSM); + + Int_t colInModule = colSM % mNETAdiv, + rowInMOdule = rowSM % mNPHIdiv; + colInModule = mNETAdiv - 1 - colInModule; + return std::make_tuple(supermodule, std::get<2>(indexmod), rowInMOdule, colInModule); +} + int Geometry::GlobalCol(int cellID) const { return std::get<1>(GlobalRowColFromIndex(cellID)); @@ -788,8 +862,9 @@ int Geometry::GlobalRow(int cellID) const Int_t Geometry::SuperModuleNumberFromEtaPhi(Double_t eta, Double_t phi) const { - if (TMath::Abs(eta) > mEtaMaxOfTRD1) + if (TMath::Abs(eta) > mEtaMaxOfTRD1) { throw InvalidPositionException(eta, phi); + } phi = TVector2::Phi_0_2pi(phi); // move phi to (0,2pi) boundaries Int_t nphism = mNumberOfSuperModules / 2; @@ -798,12 +873,14 @@ Int_t Geometry::SuperModuleNumberFromEtaPhi(Double_t eta, Double_t phi) const LOG(DEBUG) << "Sec " << i << ": Min " << mPhiBoundariesOfSM[2 * i] << ", Max " << mPhiBoundariesOfSM[2 * i + 1]; if (phi >= mPhiBoundariesOfSM[2 * i] && phi <= mPhiBoundariesOfSM[2 * i + 1]) { nSupMod = 2 * i; - if (eta < 0.0) + if (eta < 0.0) { nSupMod++; + } if (GetSMType(nSupMod) == DCAL_STANDARD) { // Gap between DCAL - if (TMath::Abs(eta) < GetNEta() / 3 * mTrd1Angle * TMath::DegToRad()) + if (TMath::Abs(eta) < GetNEta() / 3 * mTrd1Angle * TMath::DegToRad()) { throw InvalidPositionException(eta, phi); + } } LOG(DEBUG) << "eta " << eta << " phi " << phi << " (" << std::setw(5) << std::setprecision(2) @@ -822,12 +899,13 @@ Int_t Geometry::GetAbsCellIdFromEtaPhi(Double_t eta, Double_t phi) const phi = TVector2::Phi_0_2pi(phi); Double_t phiLoc = phi - mPhiCentersOfSMSec[nSupMod / 2]; Int_t nphi = mPhiCentersOfCells.size(); - if (GetSMType(nSupMod) == EMCAL_HALF) + if (GetSMType(nSupMod) == EMCAL_HALF) { nphi /= 2; - else if (GetSMType(nSupMod) == EMCAL_THIRD) + } else if (GetSMType(nSupMod) == EMCAL_THIRD) { nphi /= 3; - else if (GetSMType(nSupMod) == DCAL_EXT) + } else if (GetSMType(nSupMod) == DCAL_EXT) { nphi /= 3; + } Double_t dmin = TMath::Abs(mPhiCentersOfCells[0] - phiLoc), d = 0.; @@ -848,8 +926,9 @@ Int_t Geometry::GetAbsCellIdFromEtaPhi(Double_t eta, Double_t phi) const Int_t neta = mCentersOfCellsEtaDir.size(), etaShift = iphi * neta, ieta = 0; - if (GetSMType(nSupMod) == DCAL_STANDARD) + if (GetSMType(nSupMod) == DCAL_STANDARD) { ieta += 16; // jump 16 cells for DCSM + } dmin = TMath::Abs(mEtaCentersOfCells[etaShift + ieta] - absEta); for (Int_t i = ieta + 1; i < neta; i++) { d = TMath::Abs(mEtaCentersOfCells[i + etaShift] - absEta); @@ -859,8 +938,9 @@ Int_t Geometry::GetAbsCellIdFromEtaPhi(Double_t eta, Double_t phi) const } } - if (GetSMType(nSupMod) == DCAL_STANDARD) + if (GetSMType(nSupMod) == DCAL_STANDARD) { ieta -= 16; // jump 16 cells for DCSM + } LOG(DEBUG2) << " ieta " << ieta << " : dmin " << dmin << " (eta=" << eta << ") : nSupMod " << nSupMod; @@ -868,8 +948,9 @@ Int_t Geometry::GetAbsCellIdFromEtaPhi(Double_t eta, Double_t phi) const if (nSupMod % 2 == 0) { // 47 + 16 -ieta for DCSM, 47 - ieta for others, revert the ordering on A side in order to keep convention. ieta = (neta - 1) - ieta; - if (GetSMType(nSupMod) == DCAL_STANDARD) + if (GetSMType(nSupMod) == DCAL_STANDARD) { ieta -= 16; // recover cells for DCSM + } } return GetAbsCellIdFromCellIndexes(nSupMod, iphi, ieta); @@ -877,8 +958,9 @@ Int_t Geometry::GetAbsCellIdFromEtaPhi(Double_t eta, Double_t phi) const std::tuple<int, int, int, int> Geometry::CalculateCellIndex(Int_t absId) const { - if (!CheckAbsCellId(absId)) + if (!CheckAbsCellId(absId)) { throw InvalidCellIDException(absId); + } Int_t tmp = absId; Int_t test = absId; @@ -887,17 +969,17 @@ std::tuple<int, int, int, int> Geometry::CalculateCellIndex(Int_t absId) const for (nSupMod = -1; test >= 0;) { nSupMod++; tmp = test; - if (GetSMType(nSupMod) == EMCAL_STANDARD) + if (GetSMType(nSupMod) == EMCAL_STANDARD) { test -= mNCellsInSupMod; - else if (GetSMType(nSupMod) == EMCAL_HALF) + } else if (GetSMType(nSupMod) == EMCAL_HALF) { test -= mNCellsInSupMod / 2; - else if (GetSMType(nSupMod) == EMCAL_THIRD) + } else if (GetSMType(nSupMod) == EMCAL_THIRD) { test -= mNCellsInSupMod / 3; - else if (GetSMType(nSupMod) == DCAL_STANDARD) + } else if (GetSMType(nSupMod) == DCAL_STANDARD) { test -= 2 * mNCellsInSupMod / 3; - else if (GetSMType(nSupMod) == DCAL_EXT) + } else if (GetSMType(nSupMod) == DCAL_EXT) { test -= mNCellsInSupMod / 3; - else { + } else { throw InvalidSupermoduleTypeException(); } } @@ -910,8 +992,9 @@ std::tuple<int, int, int, int> Geometry::CalculateCellIndex(Int_t absId) const std::tuple<int, int, int, int> Geometry::GetCellIndex(Int_t absId) const { - if (!CheckAbsCellId(absId)) + if (!CheckAbsCellId(absId)) { throw InvalidCellIDException(absId); + } return mCellIndexLookup[absId]; } @@ -920,14 +1003,15 @@ Int_t Geometry::GetSuperModuleNumber(Int_t absId) const { return std::get<0>(Get std::tuple<int, int> Geometry::GetModulePhiEtaIndexInSModule(Int_t nSupMod, Int_t nModule) const { Int_t nphi = -1; - if (GetSMType(nSupMod) == EMCAL_HALF) + if (GetSMType(nSupMod) == EMCAL_HALF) { nphi = mNPhi / 2; // halfSM - else if (GetSMType(nSupMod) == EMCAL_THIRD) + } else if (GetSMType(nSupMod) == EMCAL_THIRD) { nphi = mNPhi / 3; // 1/3 SM - else if (GetSMType(nSupMod) == DCAL_EXT) + } else if (GetSMType(nSupMod) == DCAL_EXT) { nphi = mNPhi / 3; // 1/3 SM - else + } else { nphi = mNPhi; // full SM + } return std::make_tuple(int(nModule % nphi), int(nModule / nphi)); } @@ -973,7 +1057,7 @@ std::tuple<int, int> Geometry::ShiftOfflineToOnlineCellIndexes(Int_t supermodule return std::tuple<int, int>(iphi, ieta); } -Point3D<double> Geometry::RelPosCellInSModule(Int_t absId) const +o2::math_utils::Point3D<double> Geometry::RelPosCellInSModule(Int_t absId) const { // Shift index taking into account the difference between standard SM // and SM of half (or one third) size in phi direction @@ -982,8 +1066,9 @@ Point3D<double> Geometry::RelPosCellInSModule(Int_t absId) const Double_t zshift = 0.5 * GetDCALInnerEdge(); Double_t xr, yr, zr; - if (!CheckAbsCellId(absId)) + if (!CheckAbsCellId(absId)) { throw InvalidCellIDException(absId); + } auto cellindex = GetCellIndex(absId); Int_t nSupMod = std::get<0>(cellindex), nModule = std::get<1>(cellindex), nIphi = std::get<2>(cellindex), @@ -993,44 +1078,51 @@ Point3D<double> Geometry::RelPosCellInSModule(Int_t absId) const // Get eta position. Careful with ALICE conventions (increase index decrease eta) Int_t ieta2 = ieta; - if (nSupMod % 2 == 0) + if (nSupMod % 2 == 0) { ieta2 = (mCentersOfCellsEtaDir.size() - 1) - ieta; // 47-ieta, revert the ordering on A side in order to keep convention. + } - if (GetSMType(nSupMod) == DCAL_STANDARD && nSupMod % 2) + if (GetSMType(nSupMod) == DCAL_STANDARD && nSupMod % 2) { ieta2 += 16; // DCAL revert the ordering on C side ... + } zr = mCentersOfCellsEtaDir[ieta2]; - if (GetSMType(nSupMod) == DCAL_STANDARD) + if (GetSMType(nSupMod) == DCAL_STANDARD) { zr -= zshift; // DCAL shift (SMALLER SM) + } xr = mCentersOfCellsXDir[ieta2]; // Get phi position. Careful with ALICE conventions (increase index increase phi) Int_t iphi2 = iphi; if (GetSMType(nSupMod) == DCAL_EXT) { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (phiindex / 3 - 1) - iphi; // 7-iphi [1/3SM], revert the ordering on C side in order to keep convention. + } yr = mCentersOfCellsPhiDir[iphi2 + phiindex / 3]; } else if (GetSMType(nSupMod) == EMCAL_HALF) { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (phiindex / 2 - 1) - iphi; // 11-iphi [1/2SM], revert the ordering on C side in order to keep - // convention. + } + // convention. yr = mCentersOfCellsPhiDir[iphi2 + phiindex / 4]; } else if (GetSMType(nSupMod) == EMCAL_THIRD) { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (phiindex / 3 - 1) - iphi; // 7-iphi [1/3SM], revert the ordering on C side in order to keep convention. + } yr = mCentersOfCellsPhiDir[iphi2 + phiindex / 3]; } else { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (phiindex - 1) - iphi; // 23-iphi, revert the ordering on C side in order to keep conventi + } yr = mCentersOfCellsPhiDir[iphi2]; } LOG(DEBUG) << "absId " << absId << " nSupMod " << nSupMod << " iphi " << iphi << " ieta " << ieta << " xr " << xr << " yr " << yr << " zr " << zr; - return Point3D<double>(xr, yr, zr); + return o2::math_utils::Point3D<double>(xr, yr, zr); } -Point3D<double> Geometry::RelPosCellInSModule(Int_t absId, Double_t distEff) const +o2::math_utils::Point3D<double> Geometry::RelPosCellInSModule(Int_t absId, Double_t distEff) const { // Shift index taking into account the difference between standard SM // and SM of half (or one third) size in phi direction @@ -1041,8 +1133,9 @@ Point3D<double> Geometry::RelPosCellInSModule(Int_t absId, Double_t distEff) con Int_t iphim = -1, ietam = -1; TVector2 v; - if (!CheckAbsCellId(absId)) + if (!CheckAbsCellId(absId)) { throw InvalidCellIDException(absId); + } auto cellindex = GetCellIndex(absId); Int_t nSupMod = std::get<0>(cellindex), nModule = std::get<1>(cellindex), nIphi = std::get<2>(cellindex), @@ -1057,45 +1150,52 @@ Point3D<double> Geometry::RelPosCellInSModule(Int_t absId, Double_t distEff) con if (nSupMod % 2 == 0) { ietam = (mCentersOfCellsEtaDir.size() / 2 - 1) - ietam; // 24-ietam, revert the ordering on A side in order to keep convention. - if (nIeta == 0) + if (nIeta == 0) { nIeta = 1; - else + } else { nIeta = 0; + } } - if (GetSMType(nSupMod) == DCAL_STANDARD && nSupMod % 2) + if (GetSMType(nSupMod) == DCAL_STANDARD && nSupMod % 2) { ietam += kDCalshift; // DCAL revert the ordering on C side .... + } const ShishKebabTrd1Module& mod = GetShishKebabModule(ietam); mod.GetPositionAtCenterCellLine(nIeta, distEff, v); xr = v.Y() - mParSM[0]; zr = v.X() - mParSM[2]; - if (GetSMType(nSupMod) == DCAL_STANDARD) + if (GetSMType(nSupMod) == DCAL_STANDARD) { zr -= zshift; // DCAL shift (SMALLER SM) + } // Get phi position. Careful with ALICE conventions (increase index increase phi) Int_t iphi2 = iphi; if (GetSMType(nSupMod) == DCAL_EXT) { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (nphiIndex / 3 - 1) - iphi; // 7-iphi [1/3SM], revert the ordering on C side in order to keep convention. + } yr = mCentersOfCellsPhiDir[iphi2 + nphiIndex / 3]; } else if (GetSMType(nSupMod) == EMCAL_HALF) { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (nphiIndex / 2 - 1) - iphi; // 11-iphi [1/2SM], revert the ordering on C side in order to keep - // convention. + } + // convention. yr = mCentersOfCellsPhiDir[iphi2 + nphiIndex / 2]; } else if (GetSMType(nSupMod) == EMCAL_THIRD) { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (nphiIndex / 3 - 1) - iphi; // 7-iphi [1/3SM], revert the ordering on C side in order to keep convention. + } yr = mCentersOfCellsPhiDir[iphi2 + nphiIndex / 3]; } else { - if (nSupMod % 2 != 0) + if (nSupMod % 2 != 0) { iphi2 = (nphiIndex - 1) - iphi; // 23-iphi, revert the ordering on C side in order to keep convention. + } yr = mCentersOfCellsPhiDir[iphi2]; } LOG(DEBUG) << "absId " << absId << " nSupMod " << nSupMod << " iphi " << iphi << " ieta " << ieta << " xr " << xr << " yr " << yr << " zr " << zr; - return Point3D<double>(xr, yr, zr); + return math_utils::Point3D<double>(xr, yr, zr); } void Geometry::CreateListOfTrd1Modules() @@ -1213,8 +1313,9 @@ void Geometry::CreateListOfTrd1Modules() const ShishKebabTrd1Module& Geometry::GetShishKebabModule(Int_t neta) const { - if (mShishKebabTrd1Modules.size() && neta >= 0 && neta < mShishKebabTrd1Modules.size()) + if (mShishKebabTrd1Modules.size() && neta >= 0 && neta < mShishKebabTrd1Modules.size()) { return mShishKebabTrd1Modules.at(neta); + } throw InvalidModuleException(neta, mShishKebabTrd1Modules.size()); } @@ -1222,26 +1323,28 @@ Bool_t Geometry::Impact(const TParticle* particle) const { Bool_t in = kFALSE; Int_t absID = 0; - Point3D<double> vimpact = {0, 0, 0}; + math_utils::Point3D<double> vimpact = {0, 0, 0}; ImpactOnEmcal({particle->Vx(), particle->Vy(), particle->Vz()}, particle->Theta(), particle->Phi(), absID, vimpact); - if (absID >= 0) + if (absID >= 0) { in = kTRUE; + } return in; } -void Geometry::ImpactOnEmcal(const Point3D<double>& vtx, Double_t theta, Double_t phi, Int_t& absId, Point3D<double>& vimpact) const +void Geometry::ImpactOnEmcal(const math_utils::Point3D<double>& vtx, Double_t theta, Double_t phi, Int_t& absId, math_utils::Point3D<double>& vimpact) const { - Vector3D<double> p(TMath::Sin(theta) * TMath::Cos(phi), TMath::Sin(theta) * TMath::Sin(phi), TMath::Cos(theta)); + math_utils::Vector3D<double> p(TMath::Sin(theta) * TMath::Cos(phi), TMath::Sin(theta) * TMath::Sin(phi), TMath::Cos(theta)); vimpact.SetXYZ(0, 0, 0); absId = -1; - if (phi == 0 || theta == 0) + if (phi == 0 || theta == 0) { return; + } - Vector3D<double> direction; + math_utils::Vector3D<double> direction; Double_t factor = (mIPDistance - vtx.Y()) / p.Y(); direction = vtx + factor * p; @@ -1266,15 +1369,17 @@ void Geometry::ImpactOnEmcal(const Point3D<double>& vtx, Double_t theta, Double_ nIeta = std::get<3>(cellindex); // look at 2 neighbours-s cell using nIphi={0,1} and nIeta={0,1} Int_t nIphi2 = -1, nIeta2 = -1, absId2 = -1, absId3 = -1; - if (nIeta == 0) + if (nIeta == 0) { nIeta2 = 1; - else + } else { nIeta2 = 0; + } absId2 = GetAbsCellId(nSupMod, nModule, nIphi, nIeta2); - if (nIphi == 0) + if (nIphi == 0) { nIphi2 = 1; - else + } else { nIphi2 = 0; + } absId3 = GetAbsCellId(nSupMod, nModule, nIphi2, nIeta); // 2nd point on emcal cell plane @@ -1316,10 +1421,11 @@ void Geometry::ImpactOnEmcal(const Point3D<double>& vtx, Double_t theta, Double_ Double_t dist = mLongModuleSize / 2.; Double_t norm = TMath::Sqrt(a * a + b * b + c * c); Double_t glob4[3] = {}; - Vector3D<double> dir = {a, b, c}; - Point3D<double> point = {glob[0], glob[1], glob[2]}; - if (point.Dot(dir) < 0) + math_utils::Vector3D<double> dir = {a, b, c}; + math_utils::Point3D<double> point = {glob[0], glob[1], glob[2]}; + if (point.Dot(dir) < 0) { dist *= -1; + } glob4[0] = glob[0] - dist * a / norm; glob4[1] = glob[1] - dist * b / norm; glob4[2] = glob[2] - dist * c / norm; @@ -1344,48 +1450,54 @@ void Geometry::ImpactOnEmcal(const Point3D<double>& vtx, Double_t theta, Double_ vimpact.SetXYZ(vimpact.Z() + dist * a / norm, vimpact.Y() + dist * b / norm, vimpact.Z() + dist * c / norm); } -Bool_t Geometry::IsInEMCAL(const Point3D<double>& pnt) const +Bool_t Geometry::IsInEMCAL(const math_utils::Point3D<double>& pnt) const { - if (IsInEMCALOrDCAL(pnt) == EMCAL_ACCEPTANCE) + if (IsInEMCALOrDCAL(pnt) == EMCAL_ACCEPTANCE) { return kTRUE; - else + } else { return kFALSE; + } } -Bool_t Geometry::IsInDCAL(const Point3D<double>& pnt) const +Bool_t Geometry::IsInDCAL(const math_utils::Point3D<double>& pnt) const { - if (IsInEMCALOrDCAL(pnt) == DCAL_ACCEPTANCE) + if (IsInEMCALOrDCAL(pnt) == DCAL_ACCEPTANCE) { return kTRUE; - else + } else { return kFALSE; + } } -o2::emcal::AcceptanceType_t Geometry::IsInEMCALOrDCAL(const Point3D<double>& pnt) const +o2::emcal::AcceptanceType_t Geometry::IsInEMCALOrDCAL(const math_utils::Point3D<double>& pnt) const { Double_t r = sqrt(pnt.X() * pnt.X() + pnt.Y() * pnt.Y()); - if (r <= mEnvelop[0]) + if (r <= mEnvelop[0]) { return NON_ACCEPTANCE; - else { + } else { Double_t theta = TMath::ATan2(r, pnt.Z()); Double_t eta; - if (theta == 0) + if (theta == 0) { eta = 9999; - else + } else { eta = -TMath::Log(TMath::Tan(theta / 2.)); - if (eta < mArm1EtaMin || eta > mArm1EtaMax) + } + if (eta < mArm1EtaMin || eta > mArm1EtaMax) { return NON_ACCEPTANCE; + } Double_t phi = TMath::ATan2(pnt.Y(), pnt.X()) * 180. / TMath::Pi(); - if (phi < 0) + if (phi < 0) { phi += 360; // phi should go from 0 to 360 in this case + } - if (phi >= mArm1PhiMin && phi <= mEMCALPhiMax) + if (phi >= mArm1PhiMin && phi <= mEMCALPhiMax) { return EMCAL_ACCEPTANCE; - else if (phi >= mDCALPhiMin && phi <= mDCALStandardPhiMax && TMath::Abs(eta) > mDCALInnerExtandedEta) + } else if (phi >= mDCALPhiMin && phi <= mDCALStandardPhiMax && TMath::Abs(eta) > mDCALInnerExtandedEta) { return DCAL_ACCEPTANCE; - else if (phi > mDCALStandardPhiMax && phi <= mDCALPhiMax) + } else if (phi > mDCALStandardPhiMax && phi <= mDCALPhiMax) { return DCAL_ACCEPTANCE; + } return NON_ACCEPTANCE; } } @@ -1455,7 +1567,7 @@ const TGeoHMatrix* Geometry::GetMatrixForSuperModuleFromGeoManager(Int_t smod) c LOG(ERROR) << "Unkown SM Type!!\n"; } - snprintf(path, buffersize, "/cave/%s_%d", smName.Data(), smOrder); + snprintf(path, buffersize, "/cave/barrel_1/%s_%d", smName.Data(), smOrder); if (!gGeoManager->cd(path)) { LOG(FATAL) << "Geo manager can not find path " << path << "!\n"; @@ -1477,7 +1589,7 @@ void Geometry::RecalculateTowerPosition(Float_t drow, Float_t dcol, const Int_t const Int_t nSMod = mNumberOfSuperModules; - gGeoManager->cd("/cave/barrel/"); + gGeoManager->cd("/cave/barrel_1/"); TGeoNode* geoXEn1 = gGeoManager->GetCurrentNode(); TGeoNodeMatrix* geoSM[nSMod]; TGeoVolume* geoSMVol[nSMod]; @@ -1541,11 +1653,12 @@ void Geometry::RecalculateTowerPosition(Float_t drow, Float_t dcol, const Int_t for (int is = 0; is <= istrip; is++) { teta1 = TMath::DegToRad() * (is * 1.5 + 0.75); - if (is == 0) + if (is == 0) { zIs = zIs + 2 * dz * TMath::Cos(teta1); - else + } else { zIs = zIs + 2 * dz * TMath::Cos(teta1) + 2 * dz * TMath::Sin(teta1) * TMath::Tan(teta1 - 0.75 * TMath::DegToRad()); + } } z0 = dz * (dcol - 2 * istrip + 0.5); @@ -1594,8 +1707,9 @@ void Geometry::RecalculateTowerPosition(Float_t drow, Float_t dcol, const Int_t void Geometry::SetMisalMatrix(const TGeoHMatrix* m, Int_t smod) const { if (smod >= 0 && smod < mNumberOfSuperModules) { - if (!SMODULEMATRIX[smod]) + if (!SMODULEMATRIX[smod]) { SMODULEMATRIX[smod] = new TGeoHMatrix(*m); // Set only if not set yet + } } else { LOG(FATAL) << "Wrong supermodule index -> " << smod << std::endl; } @@ -1603,16 +1717,18 @@ void Geometry::SetMisalMatrix(const TGeoHMatrix* m, Int_t smod) const Bool_t Geometry::IsDCALSM(Int_t iSupMod) const { - if (mEMCSMSystem[iSupMod] == DCAL_STANDARD || mEMCSMSystem[iSupMod] == DCAL_EXT) + if (mEMCSMSystem[iSupMod] == DCAL_STANDARD || mEMCSMSystem[iSupMod] == DCAL_EXT) { return kTRUE; + } return kFALSE; } Bool_t Geometry::IsDCALExtSM(Int_t iSupMod) const { - if (mEMCSMSystem[iSupMod] == DCAL_EXT) + if (mEMCSMSystem[iSupMod] == DCAL_EXT) { return kTRUE; + } return kFALSE; } @@ -1632,15 +1748,17 @@ Double_t Geometry::GetPhiCenterOfSM(Int_t nsupmod) const std::tuple<double, double> Geometry::GetPhiBoundariesOfSM(Int_t nSupMod) const { int i; - if (nSupMod < 0 || nSupMod > 12 + mnSupModInDCAL - 1) + if (nSupMod < 0 || nSupMod > 12 + mnSupModInDCAL - 1) { throw InvalidModuleException(nSupMod, 12 + mnSupModInDCAL); + } i = nSupMod / 2; return std::make_tuple((Double_t)mPhiBoundariesOfSM[2 * i], (Double_t)mPhiBoundariesOfSM[2 * i + 1]); } std::tuple<double, double> Geometry::GetPhiBoundariesOfSMGap(Int_t nPhiSec) const { - if (nPhiSec < 0 || nPhiSec > 5 + mnSupModInDCAL / 2 - 1) + if (nPhiSec < 0 || nPhiSec > 5 + mnSupModInDCAL / 2 - 1) { throw InvalidModuleException(nPhiSec, 5 + mnSupModInDCAL / 2); + } return std::make_tuple(mPhiBoundariesOfSM[2 * nPhiSec + 1], mPhiBoundariesOfSM[2 * nPhiSec + 2]); } diff --git a/Detectors/EMCAL/base/src/Hit.cxx b/Detectors/EMCAL/base/src/Hit.cxx index 3b681d19176f1..4a7f97581942b 100644 --- a/Detectors/EMCAL/base/src/Hit.cxx +++ b/Detectors/EMCAL/base/src/Hit.cxx @@ -23,8 +23,9 @@ void Hit::PrintStream(std::ostream& stream) const Bool_t Hit::operator<(const Hit& rhs) const { - if (GetTrackID() != rhs.GetTrackID()) + if (GetTrackID() != rhs.GetTrackID()) { return GetTrackID() < rhs.GetTrackID(); + } return GetDetectorID() < rhs.GetDetectorID(); } diff --git a/Detectors/EMCAL/base/src/Mapper.cxx b/Detectors/EMCAL/base/src/Mapper.cxx index 1b603689dc691..a5619a00a877e 100644 --- a/Detectors/EMCAL/base/src/Mapper.cxx +++ b/Detectors/EMCAL/base/src/Mapper.cxx @@ -59,8 +59,9 @@ void Mapper::init(const std::string_view filename) throw FileFormatException(e.what()); } - if (address > maxHardwareAddress) + if (address > maxHardwareAddress) { throw AddressRangeException(address, maxHardwareAddress); + } auto chantype = o2::emcal::intToChannelType(caloflag); @@ -73,22 +74,26 @@ void Mapper::init(const std::string_view filename) Mapper::ChannelID Mapper::getChannelID(int hardawareaddress) const { - if (!mInitStatus) + if (!mInitStatus) { throw InitStatusException(); + } auto res = mMapping.find(hardawareaddress); - if (res == mMapping.end()) + if (res == mMapping.end()) { throw AddressNotFoundException(hardawareaddress); + } return res->second; } int Mapper::getHardwareAddress(int row, int col, ChannelType_t channeltype) const { - if (!mInitStatus) + if (!mInitStatus) { throw InitStatusException(); + } ChannelID channelToFind{uint8_t(row), uint8_t(col), channeltype}; auto found = mInverseMapping.find(channelToFind); - if (found == mInverseMapping.end()) + if (found == mInverseMapping.end()) { throw ChannelNotFoundException(channelToFind); + } return found->second; } @@ -105,8 +110,9 @@ MappingHandler::MappingHandler() Mapper& MappingHandler::getMappingForDDL(int ddl) { - if (ddl > 40) + if (ddl > 40) { throw MappingHandler::DDLInvalid(ddl); + } const int NDDLSM = 2, NSIDES = 2; int ddlInSM = ddl % NDDLSM, sideID = (ddl / NDDLSM) % NSIDES; @@ -115,6 +121,6 @@ Mapper& MappingHandler::getMappingForDDL(int ddl) std::ostream& o2::emcal::operator<<(std::ostream& stream, const Mapper::ChannelID& channel) { - stream << "Row " << channel.mRow << ", Column " << channel.mColumn << ", type " << o2::emcal::channelTypeToString(channel.mChannelType); + stream << "Row " << static_cast<int>(channel.mRow) << ", Column " << static_cast<int>(channel.mColumn) << ", type " << o2::emcal::channelTypeToString(channel.mChannelType); return stream; } \ No newline at end of file diff --git a/Detectors/EMCAL/base/src/RCUTrailer.cxx b/Detectors/EMCAL/base/src/RCUTrailer.cxx index d7451cfbb02c5..5e0e0c962a03f 100644 --- a/Detectors/EMCAL/base/src/RCUTrailer.cxx +++ b/Detectors/EMCAL/base/src/RCUTrailer.cxx @@ -36,19 +36,22 @@ void RCUTrailer::reset() void RCUTrailer::constructFromRawPayload(const gsl::span<const uint32_t> payloadwords) { reset(); - int index = payloadwords.size() - 1; - auto word = payloadwords[index]; - if ((word >> 30) != 3) + int index = payloadwords.size(); + auto word = payloadwords[--index]; + if ((word >> 30) != 3) { throw Error(Error::ErrorType_t::DECODING_INVALID, "Last RCU trailer word not found!"); + } mFirmwareVersion = (word >> 16) & 0xFF; mRCUId = (int)((word >> 7) & 0x1FF); int trailerSize = (word & 0x7F); - if (trailerSize < 2) + if (trailerSize < 2) { throw Error(Error::ErrorType_t::SIZE_INVALID, fmt::format("Invalid trailer size found (%d bytes) !", trailerSize * 4).data()); + } mTrailerSize = trailerSize; + trailerSize -= 2; // Cut first and last trailer words as they are handled separately for (; trailerSize > 0; trailerSize--) { word = payloadwords[--index]; if ((word >> 30) != 2) { @@ -123,14 +126,15 @@ double RCUTrailer::getTimeSample() const void RCUTrailer::setTimeSample(double timesample) { int fq = 0; - if (std::abs(timesample - 50) < DBL_EPSILON) + if (std::abs(timesample - 50) < DBL_EPSILON) { fq = 0; - else if (std::abs(timesample - 100) < DBL_EPSILON) + } else if (std::abs(timesample - 100) < DBL_EPSILON) { fq = 1; - else if (std::abs(timesample - 200) < DBL_EPSILON) + } else if (std::abs(timesample - 200) < DBL_EPSILON) { fq = 2; - else + } else { throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format("invalid time sample: %f", timesample).data()); + } mAltroCFG2 = (mAltroCFG2 & 0x1F) | fq << 5; } @@ -153,15 +157,16 @@ void RCUTrailer::setL1Phase(double l1phase) std::vector<uint32_t> RCUTrailer::encode() const { std::vector<uint32_t> encoded; - encoded.emplace_back(mAltroCFG2 | 7 << 26); - encoded.emplace_back(mAltroCFG1 | 6 << 26); - encoded.emplace_back(mActiveFECsB | 5 << 26); - encoded.emplace_back(mActiveFECsA | 4 << 26); - encoded.emplace_back(mERRREG3 | 3 << 26); - encoded.emplace_back(mERRREG2 | 2 << 26); - encoded.emplace_back(mFECERRB >> 7 | (mFECERRA >> 7) << 13 | 1 << 26); - - uint32_t lasttrailerword = 3 << 30 | mFirmwareVersion << 16 | mRCUId << 7 | encoded.size(); + encoded.emplace_back(mPayloadSize | 2 << 30); + encoded.emplace_back(mAltroCFG2 | 7 << 26 | 2 << 30); + encoded.emplace_back(mAltroCFG1 | 6 << 26 | 2 << 30); + encoded.emplace_back(mActiveFECsB | 5 << 26 | 2 << 30); + encoded.emplace_back(mActiveFECsA | 4 << 26 | 2 << 30); + encoded.emplace_back(mERRREG3 | 3 << 26 | 2 << 30); + encoded.emplace_back(mERRREG2 | 2 << 26 | 2 << 30); + encoded.emplace_back(mFECERRB >> 7 | (mFECERRA >> 7) << 13 | 1 << 26 | 2 << 30); + + uint32_t lasttrailerword = 3 << 30 | mFirmwareVersion << 16 | mRCUId << 7 | (encoded.size() + 1); encoded.emplace_back(lasttrailerword); return encoded; diff --git a/Detectors/EMCAL/base/src/ShishKebabTrd1Module.cxx b/Detectors/EMCAL/base/src/ShishKebabTrd1Module.cxx index eb8e254cf4c72..d33c456f0bf0c 100644 --- a/Detectors/EMCAL/base/src/ShishKebabTrd1Module.cxx +++ b/Detectors/EMCAL/base/src/ShishKebabTrd1Module.cxx @@ -39,11 +39,13 @@ ShishKebabTrd1Module::ShishKebabTrd1Module(Double_t theta, Geometry* g) { std::string_view sname = g->GetName(); Int_t key = 0; - if (sname.find("v1") != std::string::npos || sname.find("V1") != std::string::npos) + if (sname.find("v1") != std::string::npos || sname.find("V1") != std::string::npos) { key = 1; // EMCAL_COMPLETEV1 vs EMCAL_COMPLETEv1 (or other) + } - if (SetParameters()) + if (SetParameters()) { DefineFirstModule(key); + } // DefineName(mTheta); LOG(DEBUG4) << "o2::emcal::ShishKebabTrd1Module - first module key=" << key << ": theta " << std::setw(1) @@ -85,8 +87,9 @@ ShishKebabTrd1Module::ShishKebabTrd1Module(const ShishKebabTrd1Module& mod) mORB(mod.mORB), mORT(mod.mORT) { - for (Int_t i = 0; i < 3; i++) + for (Int_t i = 0; i < 3; i++) { mOK3X3[i] = mod.mOK3X3[i]; + } } void ShishKebabTrd1Module::Init(Double_t A, Double_t B) diff --git a/Detectors/EMCAL/base/test/testGeometryRowColIndexing.C b/Detectors/EMCAL/base/test/testGeometryRowColIndexing.C new file mode 100644 index 0000000000000..9006dcdf43277 --- /dev/null +++ b/Detectors/EMCAL/base/test/testGeometryRowColIndexing.C @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction + +void testGeometryRowColIndexing() +{ + auto geo = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); + TH1* habsid = new TH1D("hAbsID", "Cell abs ID", 20001, -0.5, 20000.5); + TH1* hsmod = new TH1D("hsmod", "Supermodule ID", 21, -0.5, 20.5); + for (int icol = 0; icol < 96; icol++) { + for (int irow = 0; irow < 208; irow++) { + // exclude PHOS hole + if (icol >= 32 && icol < 64 && irow >= 128 && irow < 200) + continue; + int absID = geo->GetCellAbsIDFromGlobalRowCol(irow, icol); + habsid->Fill(absID); + auto [smod, mod, iphi, ieta] = geo->GetCellIndexFromGlobalRowCol(irow, icol); + hsmod->Fill(smod); + std::cout << "Col " << icol << ", row " << irow << ": ID " << absID << ", sm " << smod << ", module " << mod << ", iphi " << iphi << ", ieta " << ieta << std::endl; + } + } + auto plot = new TCanvas("geotest", "Geometry test", 1200, 700); + plot->Divide(2, 1); + plot->cd(1); + habsid->Draw(); + plot->cd(2); + hsmod->Draw(); + plot->cd(); + plot->Update(); +} \ No newline at end of file diff --git a/Detectors/EMCAL/base/test/testMapper.cxx b/Detectors/EMCAL/base/test/testMapper.cxx index 16731685057d0..78244510b9f8f 100644 --- a/Detectors/EMCAL/base/test/testMapper.cxx +++ b/Detectors/EMCAL/base/test/testMapper.cxx @@ -33,8 +33,9 @@ BOOST_AUTO_TEST_CASE(Mapper_test) const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/EMC/files/"; std::string mappingfile = inputDir + "RCU0A.data"; diff --git a/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h b/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h index 0cb355264cca5..d34a3052311a5 100644 --- a/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h +++ b/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h @@ -65,7 +65,7 @@ class CalibDB /// - Incorrect path /// - Wrong timestamp /// - Meta data not set - class ObjectNotFoundException : public std::exception + class ObjectNotFoundException final : public std::exception { public: /// \brief Constructor with query parameters @@ -123,7 +123,7 @@ class CalibDB /// a certain path and with a certain timestamp was valid, the object /// however has a different type than the expected one (something was /// screwed up when writing to the CCDB) - class TypeMismatchException : public std::exception + class TypeMismatchException final : public std::exception { public: /// \brief Constructor diff --git a/Detectors/EMCAL/calib/include/EMCALCalib/TimeCalibrationParams.h b/Detectors/EMCAL/calib/include/EMCALCalib/TimeCalibrationParams.h index e1858c1742c1d..610daefedb264 100644 --- a/Detectors/EMCAL/calib/include/EMCALCalib/TimeCalibrationParams.h +++ b/Detectors/EMCAL/calib/include/EMCALCalib/TimeCalibrationParams.h @@ -62,13 +62,13 @@ class TimeCalibrationParams /// \param cellID Absolute ID of cell /// \param time is the calibration coefficient /// \param isLowGain is flag whether this cell is LG or HG - void addTimeCalibParam(unsigned short cellID, unsigned short time, bool isLowGain); + void addTimeCalibParam(unsigned short cellID, short time, bool isLowGain); /// \brief Get the time calibration coefficient for a certain cell /// \param cellID Absolute ID of cell /// \param isLowGain is flag whether this cell is LG or HG /// \return time calibration coefficient of the cell - unsigned short getTimeCalibParam(unsigned short cellID, bool isLowGain) const; + short getTimeCalibParam(unsigned short cellID, bool isLowGain) const; /// \brief Convert the time calibration coefficient array to a histogram /// \param isLowGain is flag whether to draw for LG or HG @@ -76,8 +76,8 @@ class TimeCalibrationParams TH1* getHistogramRepresentation(bool isLowGain) const; private: - std::array<unsigned short, 17664> mTimeCalibParamsHG; ///< Container for the time calibration coefficient for the High Gain cells - std::array<unsigned short, 17664> mTimeCalibParamsLG; ///< Container for the time calibration coefficient for the Low Gain cells + std::array<short, 17664> mTimeCalibParamsHG; ///< Container for the time calibration coefficient for the High Gain cells + std::array<short, 17664> mTimeCalibParamsLG; ///< Container for the time calibration coefficient for the Low Gain cells ClassDefNV(TimeCalibrationParams, 1); }; diff --git a/Detectors/EMCAL/calib/src/BadChannelMap.cxx b/Detectors/EMCAL/calib/src/BadChannelMap.cxx index 2f75ec34bd810..4fed9e4ec7cd6 100644 --- a/Detectors/EMCAL/calib/src/BadChannelMap.cxx +++ b/Detectors/EMCAL/calib/src/BadChannelMap.cxx @@ -48,12 +48,13 @@ void BadChannelMap::addBadChannel(unsigned short channelID, MaskType_t mask) BadChannelMap::MaskType_t BadChannelMap::getChannelStatus(unsigned short channelID) const { auto status = MaskType_t::GOOD_CELL; - if (mDeadCells.test(channelID)) + if (mDeadCells.test(channelID)) { status = MaskType_t::DEAD_CELL; - else if (mBadCells.test(channelID)) + } else if (mBadCells.test(channelID)) { status = MaskType_t::BAD_CELL; - else if (mWarmCells.test(channelID)) + } else if (mWarmCells.test(channelID)) { status = MaskType_t::WARM_CELL; + } return status; } @@ -71,12 +72,13 @@ TH2* BadChannelMap::getHistogramRepresentation() const for (size_t cellID = 0; cellID < mBadCells.size(); cellID++) { int value(0); - if (mBadCells.test(cellID)) + if (mBadCells.test(cellID)) { value = 1; - else if (mDeadCells.test(cellID)) + } else if (mDeadCells.test(cellID)) { value = 2; - else if (mWarmCells.test(cellID)) + } else if (mWarmCells.test(cellID)) { value = 3; + } if (value) { auto position = geo->GlobalRowColFromIndex(cellID); hist->Fill(std::get<1>(position), std::get<0>(position), value); @@ -90,18 +92,22 @@ BadChannelMap& BadChannelMap::operator+=(const BadChannelMap& rhs) for (size_t cellID = 0; cellID < mDeadCells.size(); cellID++) { if (rhs.mDeadCells.test(cellID)) { mDeadCells.set(cellID); - if (mBadCells.test(cellID)) + if (mBadCells.test(cellID)) { mBadCells.reset(cellID); - if (mWarmCells.test(cellID)) + } + if (mWarmCells.test(cellID)) { mWarmCells.reset(cellID); + } } if (rhs.mBadCells.test(cellID) && !mDeadCells.test(cellID)) { mBadCells.set(cellID); - if (mWarmCells.test(cellID)) + if (mWarmCells.test(cellID)) { mWarmCells.reset(cellID); + } } - if (rhs.mWarmCells.test(cellID) && !(mBadCells.test(cellID) || mDeadCells.test(cellID))) + if (rhs.mWarmCells.test(cellID) && !(mBadCells.test(cellID) || mDeadCells.test(cellID))) { mWarmCells.set(cellID); + } } return *this; } @@ -118,12 +124,15 @@ void BadChannelMap::PrintStream(std::ostream& stream) const stream << "Number of dead cells: " << mDeadCells.count() << "\n"; stream << "Number of warm cells: " << mWarmCells.count() << "\n"; for (size_t cellID = 0; cellID < mBadCells.size(); cellID++) { - if (mBadCells.test(cellID)) + if (mBadCells.test(cellID)) { stream << "Channel " << cellID << ": Bad cell\n"; - if (mDeadCells.test(cellID)) + } + if (mDeadCells.test(cellID)) { stream << "Channel " << cellID << ": Dead cell\n"; - if (mWarmCells.test(cellID)) + } + if (mWarmCells.test(cellID)) { stream << "Channel " << cellID << ": Warm cell\n"; + } } } diff --git a/Detectors/EMCAL/calib/src/CalibDB.cxx b/Detectors/EMCAL/calib/src/CalibDB.cxx index c86607a23c5c0..d845f9e1bcfdc 100644 --- a/Detectors/EMCAL/calib/src/CalibDB.cxx +++ b/Detectors/EMCAL/calib/src/CalibDB.cxx @@ -32,119 +32,140 @@ void CalibDB::init() void CalibDB::storeBadChannelMap(BadChannelMap* bcm, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(bcm, "EMC/BadChannelMap", metadata, rangestart, rangeend); } void CalibDB::storeTimeCalibParam(TimeCalibrationParams* tcp, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(tcp, "EMC/TimeCalibParams", metadata, rangestart, rangeend); } void CalibDB::storeTimeCalibParamL1Phase(TimeCalibParamL1Phase* tcp, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(tcp, "EMC/TimeCalibParamsL1Phase", metadata, rangestart, rangeend); } void CalibDB::storeTempCalibParam(TempCalibrationParams* tcp, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(tcp, "EMC/TempCalibParams", metadata, rangestart, rangeend); } void CalibDB::storeTempCalibParamSM(TempCalibParamSM* tcp, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(tcp, "EMC/TempCalibParamsSM", metadata, rangestart, rangeend); } void CalibDB::storeGainCalibFactors(GainCalibrationFactors* gcf, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(gcf, "EMC/GainCalibFactors", metadata, rangestart, rangeend); } void CalibDB::storeTriggerDCSData(TriggerDCS* dcs, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFileAny(dcs, "EMC/TriggerDCS", metadata, rangestart, rangeend); } BadChannelMap* CalibDB::readBadChannelMap(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } BadChannelMap* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::BadChannelMap>("EMC/BadChannelMap", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/BadChannelMap", metadata, timestamp); + } return result; } TimeCalibrationParams* CalibDB::readTimeCalibParam(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } TimeCalibrationParams* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::TimeCalibrationParams>("EMC/TimeCalibParams", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/TimeCalibParams", metadata, timestamp); + } return result; } TimeCalibParamL1Phase* CalibDB::readTimeCalibParamL1Phase(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } TimeCalibParamL1Phase* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::TimeCalibParamL1Phase>("EMC/TimeCalibParamsL1Phase", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/TimeCalibParamsL1Phase", metadata, timestamp); + } return result; } TempCalibrationParams* CalibDB::readTempCalibParam(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } TempCalibrationParams* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::TempCalibrationParams>("EMC/TempCalibParams", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/TempCalibParams", metadata, timestamp); + } return result; } TempCalibParamSM* CalibDB::readTempCalibParamSM(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } TempCalibParamSM* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::TempCalibParamSM>("EMC/TempCalibParamsSM", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/TempCalibParamsSM", metadata, timestamp); + } return result; } GainCalibrationFactors* CalibDB::readGainCalibFactors(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } GainCalibrationFactors* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::GainCalibrationFactors>("EMC/GainCalibFactors", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/GainCalibFactors", metadata, timestamp); + } return result; } TriggerDCS* CalibDB::readTriggerDCSData(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } TriggerDCS* result = mCCDBManager.retrieveFromTFileAny<o2::emcal::TriggerDCS>("EMC/TriggerDCS", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "EMC/TriggerDCS", metadata, timestamp); + } return result; } diff --git a/Detectors/EMCAL/calib/src/GainCalibrationFactors.cxx b/Detectors/EMCAL/calib/src/GainCalibrationFactors.cxx index 3b313728bb0fa..c80d8d2322909 100644 --- a/Detectors/EMCAL/calib/src/GainCalibrationFactors.cxx +++ b/Detectors/EMCAL/calib/src/GainCalibrationFactors.cxx @@ -33,8 +33,9 @@ TH1* GainCalibrationFactors::getHistogramRepresentation() const auto hist = new TH1F("GainCalibrationFactors", "GainCalibrationFactors", 17664, 0, 17664); hist->SetDirectory(nullptr); - for (std::size_t icell{0}; icell < mGainCalibFactors.size(); ++icell) + for (std::size_t icell{0}; icell < mGainCalibFactors.size(); ++icell) { hist->SetBinContent(icell + 1, mGainCalibFactors[icell]); + } return hist; } diff --git a/Detectors/EMCAL/calib/src/TempCalibParamSM.cxx b/Detectors/EMCAL/calib/src/TempCalibParamSM.cxx index f928ca00bbf2e..efdb51af0f57a 100644 --- a/Detectors/EMCAL/calib/src/TempCalibParamSM.cxx +++ b/Detectors/EMCAL/calib/src/TempCalibParamSM.cxx @@ -33,8 +33,9 @@ TH1* TempCalibParamSM::getHistogramRepresentation() const auto hist = new TH1F("TempCalibParamSM", "Temp Calibration Params per SM", 19, 0, 19); hist->SetDirectory(nullptr); - for (std::size_t iSM{0}; iSM < mTempCalibParamsPerSM.size(); ++iSM) + for (std::size_t iSM{0}; iSM < mTempCalibParamsPerSM.size(); ++iSM) { hist->SetBinContent(iSM + 1, mTempCalibParamsPerSM[iSM]); + } return hist; } diff --git a/Detectors/EMCAL/calib/src/TempCalibrationParams.cxx b/Detectors/EMCAL/calib/src/TempCalibrationParams.cxx index 8eda3530c9de0..6e2d97aace83d 100644 --- a/Detectors/EMCAL/calib/src/TempCalibrationParams.cxx +++ b/Detectors/EMCAL/calib/src/TempCalibrationParams.cxx @@ -40,8 +40,9 @@ TH1* TempCalibrationParams::getHistogramRepresentationSlope() const auto hist = new TH1F("TempCalibrationParamsSlope", "Temp Calibration Params Slope", 17664, 0, 17664); hist->SetDirectory(nullptr); - for (std::size_t icell{0}; icell < mTempCalibParamsSlope.size(); ++icell) + for (std::size_t icell{0}; icell < mTempCalibParamsSlope.size(); ++icell) { hist->SetBinContent(icell + 1, mTempCalibParamsSlope[icell]); + } return hist; } @@ -52,8 +53,9 @@ TH1* TempCalibrationParams::getHistogramRepresentationA0() const auto hist = new TH1F("TempCalibrationParamsA0", "Temp Calibration Params A0", 17664, 0, 17664); hist->SetDirectory(nullptr); - for (std::size_t icell{0}; icell < mTempCalibParamsA0.size(); ++icell) + for (std::size_t icell{0}; icell < mTempCalibParamsA0.size(); ++icell) { hist->SetBinContent(icell + 1, mTempCalibParamsA0[icell]); + } return hist; } diff --git a/Detectors/EMCAL/calib/src/TimeCalibParamL1Phase.cxx b/Detectors/EMCAL/calib/src/TimeCalibParamL1Phase.cxx index ec23a5804709b..27c5ef90629e9 100644 --- a/Detectors/EMCAL/calib/src/TimeCalibParamL1Phase.cxx +++ b/Detectors/EMCAL/calib/src/TimeCalibParamL1Phase.cxx @@ -33,8 +33,9 @@ TH1* TimeCalibParamL1Phase::getHistogramRepresentation() const auto hist = new TH1C("hL1PhaseShift", "L1PhaseShift", 19, 0, 19); hist->SetDirectory(nullptr); - for (std::size_t iSM{0}; iSM < mTimeCalibParamsL1Phase.size(); ++iSM) + for (std::size_t iSM{0}; iSM < mTimeCalibParamsL1Phase.size(); ++iSM) { hist->SetBinContent(iSM + 1, mTimeCalibParamsL1Phase[iSM] - '0'); + } return hist; } diff --git a/Detectors/EMCAL/calib/src/TimeCalibrationParams.cxx b/Detectors/EMCAL/calib/src/TimeCalibrationParams.cxx index 019624e6a94f6..03de70b3c7427 100644 --- a/Detectors/EMCAL/calib/src/TimeCalibrationParams.cxx +++ b/Detectors/EMCAL/calib/src/TimeCalibrationParams.cxx @@ -18,20 +18,22 @@ using namespace o2::emcal; -void TimeCalibrationParams::addTimeCalibParam(unsigned short cellID, unsigned short time, bool isLowGain) +void TimeCalibrationParams::addTimeCalibParam(unsigned short cellID, short time, bool isLowGain) { - if (!isLowGain) + if (!isLowGain) { mTimeCalibParamsHG[cellID] = time; - else + } else { mTimeCalibParamsLG[cellID] = time; + } } -unsigned short TimeCalibrationParams::getTimeCalibParam(unsigned short cellID, bool isLowGain) const +short TimeCalibrationParams::getTimeCalibParam(unsigned short cellID, bool isLowGain) const { - if (isLowGain) + if (isLowGain) { return mTimeCalibParamsLG[cellID]; - else + } else { return mTimeCalibParamsHG[cellID]; + } } TH1* TimeCalibrationParams::getHistogramRepresentation(bool isLowGain) const @@ -41,16 +43,18 @@ TH1* TimeCalibrationParams::getHistogramRepresentation(bool isLowGain) const auto hist = new TH1S("TimeCalibrationParams", "Time Calibration Params HG", 17664, 0, 17664); hist->SetDirectory(nullptr); - for (std::size_t icell{0}; icell < mTimeCalibParamsHG.size(); ++icell) + for (std::size_t icell{0}; icell < mTimeCalibParamsHG.size(); ++icell) { hist->SetBinContent(icell + 1, mTimeCalibParamsHG[icell]); + } return hist; } else { auto hist = new TH1S("TimeCalibrationParams", "Time Calibration Params LG", 17664, 0, 17664); hist->SetDirectory(nullptr); - for (std::size_t icell{0}; icell < mTimeCalibParamsLG.size(); ++icell) + for (std::size_t icell{0}; icell < mTimeCalibParamsLG.size(); ++icell) { hist->SetBinContent(icell + 1, mTimeCalibParamsLG[icell]); + } return hist; } diff --git a/Detectors/EMCAL/calib/src/TriggerDCS.cxx b/Detectors/EMCAL/calib/src/TriggerDCS.cxx index 0b5074a64911b..57e7dcf302d53 100644 --- a/Detectors/EMCAL/calib/src/TriggerDCS.cxx +++ b/Detectors/EMCAL/calib/src/TriggerDCS.cxx @@ -58,8 +58,9 @@ std::string TriggerDCS::toJSON() const jsonstring << "mTRUArr:["; for (int ien = 0; ien < mTRUArr.size(); ien++) { jsonstring << "{\"TRU" << ien << "\":" << mTRUArr.at(ien).toJSON() << "}"; - if (ien != mTRUArr.size() - 1) + if (ien != mTRUArr.size() - 1) { jsonstring << ","; + } } jsonstring << "]}"; return jsonstring.str(); diff --git a/Detectors/EMCAL/calib/src/TriggerSTUErrorCounter.cxx b/Detectors/EMCAL/calib/src/TriggerSTUErrorCounter.cxx index 75d034ba2f900..38c92c0651eb5 100644 --- a/Detectors/EMCAL/calib/src/TriggerSTUErrorCounter.cxx +++ b/Detectors/EMCAL/calib/src/TriggerSTUErrorCounter.cxx @@ -39,10 +39,12 @@ bool TriggerSTUErrorCounter::isEqual(TriggerSTUErrorCounter& counter) const int TriggerSTUErrorCounter::compare(TriggerSTUErrorCounter& counter) const { - if (mTimeErrorCount.first > counter.mTimeErrorCount.first) + if (mTimeErrorCount.first > counter.mTimeErrorCount.first) { return 1; - if (mTimeErrorCount.first < counter.mTimeErrorCount.first) + } + if (mTimeErrorCount.first < counter.mTimeErrorCount.first) { return -1; + } return 0; } diff --git a/Detectors/EMCAL/calib/test/testBadChannelMap.cxx b/Detectors/EMCAL/calib/test/testBadChannelMap.cxx index 461bf10ac352c..51c896b588c4a 100644 --- a/Detectors/EMCAL/calib/test/testBadChannelMap.cxx +++ b/Detectors/EMCAL/calib/test/testBadChannelMap.cxx @@ -49,12 +49,15 @@ BOOST_AUTO_TEST_CASE(BadChannelMap_test) // the inner function (used in comparisons) a cell ID. auto refChannelCombined = [](const std::vector<unsigned short>& deadcells, const std::vector<unsigned short>& badcells, const std::vector<unsigned short>& warmcells) { return [&deadcells, &badcells, &warmcells](unsigned short cellID) { - if (std::find(deadcells.begin(), deadcells.end(), cellID) != deadcells.end()) + if (std::find(deadcells.begin(), deadcells.end(), cellID) != deadcells.end()) { return BadChannelMap::MaskType_t::DEAD_CELL; - if (std::find(badcells.begin(), badcells.end(), cellID) != badcells.end()) + } + if (std::find(badcells.begin(), badcells.end(), cellID) != badcells.end()) { return BadChannelMap::MaskType_t::BAD_CELL; - if (std::find(warmcells.begin(), warmcells.end(), cellID) != warmcells.end()) + } + if (std::find(warmcells.begin(), warmcells.end(), cellID) != warmcells.end()) { return BadChannelMap::MaskType_t::WARM_CELL; + } return BadChannelMap::MaskType_t::GOOD_CELL; }; }; @@ -115,12 +118,15 @@ BOOST_AUTO_TEST_CASE(BadChannelMap_test) filterBad(combinedWarm, combinedBad); filterBad(combinedWarm, combinedDead); filterBad(combinedBad, combinedDead); - for (auto bc : badLHC17o) + for (auto bc : badLHC17o) { lhc17omap.addBadChannel(bc, BadChannelMap::MaskType_t::BAD_CELL); - for (auto wc : warmLHC7o) + } + for (auto wc : warmLHC7o) { lhc17omap.addBadChannel(wc, BadChannelMap::MaskType_t::WARM_CELL); - for (auto dc : deadLHC17o) + } + for (auto dc : deadLHC17o) { lhc17omap.addBadChannel(dc, BadChannelMap::MaskType_t::DEAD_CELL); + } BadChannelMap summed(lhc17omap); summed += patterntest; auto getRefChannelTypeCombined = refChannelCombined(combinedDead, combinedBad, combinedWarm); @@ -143,12 +149,14 @@ BOOST_AUTO_TEST_CASE(BadChannelMap_test) std::vector<unsigned short> combineMaps(const std::vector<unsigned short>& list1, const std::vector<unsigned short> list2) { std::vector<unsigned short> combined; - for (auto l1 : list1) + for (auto l1 : list1) { combined.emplace_back(l1); + } for (auto l2 : list2) { auto found = std::find(combined.begin(), combined.end(), l2); - if (found == combined.end()) + if (found == combined.end()) { combined.emplace_back(l2); + } } std::sort(combined.begin(), combined.end(), std::less<>()); return combined; @@ -157,9 +165,11 @@ std::vector<unsigned short> combineMaps(const std::vector<unsigned short>& list1 void filterBad(std::vector<unsigned short> warmcells, const std::vector<unsigned short> badcells) { std::vector<unsigned short> todelete; - for (auto wc : warmcells) - if (std::find(badcells.begin(), badcells.end(), wc) != badcells.end()) + for (auto wc : warmcells) { + if (std::find(badcells.begin(), badcells.end(), wc) != badcells.end()) { todelete.emplace_back(wc); + } + } for (auto td : todelete) { auto it = std::find(warmcells.begin(), warmcells.end(), td); warmcells.erase(it); diff --git a/Detectors/EMCAL/calib/test/testGainCalibration.cxx b/Detectors/EMCAL/calib/test/testGainCalibration.cxx index 6a31a685b2c9b..4fcc49fd10ede 100644 --- a/Detectors/EMCAL/calib/test/testGainCalibration.cxx +++ b/Detectors/EMCAL/calib/test/testGainCalibration.cxx @@ -53,14 +53,16 @@ BOOST_AUTO_TEST_CASE(testGainCalibration) const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/EMC/files/"; std::string fileNameGainCalib = inputDir + "GainCalibrationFactors_LHC18q.txt"; std::ifstream fileGainCalib(fileNameGainCalib, std::ifstream::in); - if (!fileGainCalib.is_open()) + if (!fileGainCalib.is_open()) { std::cout << "The file GainCalibrationFactors_LHC18q was not opened\n"; + } float GainCalibFactors[17664]; @@ -78,16 +80,18 @@ BOOST_AUTO_TEST_CASE(testGainCalibration) icell++; } - for (unsigned short icell = 0; icell < 17664; icell++) + for (unsigned short icell = 0; icell < 17664; icell++) { BOOST_CHECK_EQUAL(parameter.getGainCalibFactors(icell), GainCalibFactors[icell]); + } // Writing to the other container which will be used for comparison GainCalibrationFactors parameterLHC15; std::string fileNameGainCalib_LHC15 = inputDir + "GainCalibrationFactors_LHC15.txt"; std::ifstream fileGainCalib_LHC15(fileNameGainCalib_LHC15, std::ifstream::in); - if (!fileGainCalib_LHC15.is_open()) + if (!fileGainCalib_LHC15.is_open()) { std::cout << "The file GainCalibrationFactors_LHC15 was not opened\n"; + } // Write to the container icell = 0; diff --git a/Detectors/EMCAL/calib/test/testTempCalibration.cxx b/Detectors/EMCAL/calib/test/testTempCalibration.cxx index 96800113a30bf..dcd9488510987 100644 --- a/Detectors/EMCAL/calib/test/testTempCalibration.cxx +++ b/Detectors/EMCAL/calib/test/testTempCalibration.cxx @@ -54,14 +54,16 @@ BOOST_AUTO_TEST_CASE(testTempCalibration) const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/EMC/files/"; std::string file = inputDir + "TempCalibCoeff.txt"; std::ifstream fileTemp(file, std::ifstream::in); - if (!fileTemp.is_open()) + if (!fileTemp.is_open()) { std::cout << "The file TempCalibCoeff was not opened\n"; + } float Slope[17664], A0[17664]; @@ -89,8 +91,9 @@ BOOST_AUTO_TEST_CASE(testTempCalibration) std::string fileLHC17 = inputDir + "TempCalibCoeff_LHC17.txt"; std::ifstream fileTempLHC17(fileLHC17, std::ifstream::in); - if (!fileTempLHC17.is_open()) + if (!fileTempLHC17.is_open()) { std::cout << "The file TempCalibCoeff_LHC17 was not opened\n"; + } float Slope_LHC17, A0_LHC17; diff --git a/Detectors/EMCAL/calib/test/testTempCalibrationSM.cxx b/Detectors/EMCAL/calib/test/testTempCalibrationSM.cxx index 06f5b18cf6efc..0f087bf12123d 100644 --- a/Detectors/EMCAL/calib/test/testTempCalibrationSM.cxx +++ b/Detectors/EMCAL/calib/test/testTempCalibrationSM.cxx @@ -49,14 +49,16 @@ BOOST_AUTO_TEST_CASE(testTempCalibrationSM) const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/EMC/files/"; std::string file = inputDir + "TempCalibSM_LHC18k_289166.txt"; std::ifstream fileTempCalibSM(file, std::ifstream::in); - if (!fileTempCalibSM.is_open()) + if (!fileTempCalibSM.is_open()) { std::cout << "The file TempCalibSM_LHC18k_289166.txt was not opened\n"; + } float TempCalibSM[20]; @@ -72,8 +74,9 @@ BOOST_AUTO_TEST_CASE(testTempCalibrationSM) iSM++; } - for (unsigned short ism = 0; ism < 20; ism++) + for (unsigned short ism = 0; ism < 20; ism++) { BOOST_CHECK_EQUAL(parameter.getTempCalibParamPerSM(ism), TempCalibSM[ism]); + } // Comparison test @@ -82,8 +85,9 @@ BOOST_AUTO_TEST_CASE(testTempCalibrationSM) std::string fileLHC18k = inputDir + "TempCalibSM_LHC18k_289201.txt"; std::ifstream fileTempCalibSM_LHC18k(fileLHC18k, std::ifstream::in); - if (!fileTempCalibSM_LHC18k.is_open()) + if (!fileTempCalibSM_LHC18k.is_open()) { std::cout << "The file TempCalibSM_LHC18k_289201.txt was not opened\n"; + } unsigned char TempCalibSM_LHC18k; diff --git a/Detectors/EMCAL/calib/test/testTimeCalibration.cxx b/Detectors/EMCAL/calib/test/testTimeCalibration.cxx index 357dc2f0e34e0..63e5ac314b8db 100644 --- a/Detectors/EMCAL/calib/test/testTimeCalibration.cxx +++ b/Detectors/EMCAL/calib/test/testTimeCalibration.cxx @@ -57,27 +57,31 @@ BOOST_AUTO_TEST_CASE(testTimeCalibration) const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/EMC/files/"; std::string fileHG = inputDir + "TimeCalibCoeffHG.txt"; std::ifstream allTimeAvHG(fileHG, std::ifstream::in); - if (!allTimeAvHG.is_open()) + if (!allTimeAvHG.is_open()) { std::cout << "The file TimeCalibCoeffHG was not opened\n"; + } std::string fileLG = inputDir + "TimeCalibCoeffLG.txt"; std::ifstream allTimeAvLG(fileLG, std::ifstream::in); - if (!allTimeAvLG.is_open()) + if (!allTimeAvLG.is_open()) { std::cout << "The file TimeCalibCoeffLG was not opened\n"; + } unsigned short TimeHG[17664], TimeLG[17664]; unsigned short icell = 0; while (1) { allTimeAvHG >> TimeHG[icell]; - if (!allTimeAvHG.good()) + if (!allTimeAvHG.good()) { break; + } parameter.addTimeCalibParam(icell, TimeHG[icell], 0); //HG icell++; } @@ -85,8 +89,9 @@ BOOST_AUTO_TEST_CASE(testTimeCalibration) icell = 0; while (1) { allTimeAvLG >> TimeLG[icell]; - if (!allTimeAvLG.good()) + if (!allTimeAvLG.good()) { break; + } parameter.addTimeCalibParam(icell, TimeLG[icell], 1); //LG icell++; } @@ -102,21 +107,24 @@ BOOST_AUTO_TEST_CASE(testTimeCalibration) std::string fileHG_LHC17g = inputDir + "TimeCalibCoeffHG_LHC17g.txt"; std::ifstream allTimeAvHG_LHC17g(fileHG_LHC17g, std::ifstream::in); - if (!allTimeAvHG_LHC17g.is_open()) + if (!allTimeAvHG_LHC17g.is_open()) { std::cout << "The file TimeCalibCoeffHG_LHC17g was not opened\n"; + } std::string fileLG_LHC17g = inputDir + "TimeCalibCoeffLG_LHC17g.txt"; std::ifstream allTimeAvLG_LHC17g(fileLG_LHC17g, std::ifstream::in); - if (!allTimeAvLG_LHC17g.is_open()) + if (!allTimeAvLG_LHC17g.is_open()) { std::cout << "The file TimeCalibCoeffLG_LHC17g was not opened\n"; + } unsigned short TimeHG_LHC17g, TimeLG_LHC17g; icell = 0; while (0) { allTimeAvHG_LHC17g >> TimeHG_LHC17g; - if (!allTimeAvHG_LHC17g.good()) + if (!allTimeAvHG_LHC17g.good()) { break; + } parameterLHC17g.addTimeCalibParam(icell, TimeHG_LHC17g, 0); //HG icell++; } @@ -124,8 +132,9 @@ BOOST_AUTO_TEST_CASE(testTimeCalibration) icell = 0; while (0) { allTimeAvLG_LHC17g >> TimeLG_LHC17g; - if (!allTimeAvLG_LHC17g.good()) + if (!allTimeAvLG_LHC17g.good()) { break; + } parameterLHC17g.addTimeCalibParam(icell, TimeLG_LHC17g, 1); //LG icell++; } diff --git a/Detectors/EMCAL/calib/test/testTimeL1PhaseCalib.cxx b/Detectors/EMCAL/calib/test/testTimeL1PhaseCalib.cxx index 5199d8d4e6e9e..cbe13ae87d6b2 100644 --- a/Detectors/EMCAL/calib/test/testTimeL1PhaseCalib.cxx +++ b/Detectors/EMCAL/calib/test/testTimeL1PhaseCalib.cxx @@ -49,14 +49,16 @@ BOOST_AUTO_TEST_CASE(testTimeL1PhaseCalib) const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/EMC/files/"; std::string file = inputDir + "TimeL1Phase_LHC18q_295585.txt"; std::ifstream fileL1Phase(file, std::ifstream::in); - if (!fileL1Phase.is_open()) + if (!fileL1Phase.is_open()) { std::cout << "The file TimeL1Phase_LHC18q_295585 was not opened\n"; + } unsigned char L1Phase[20]; @@ -72,8 +74,9 @@ BOOST_AUTO_TEST_CASE(testTimeL1PhaseCalib) iSM++; } - for (unsigned short iSM = 0; iSM < 20; iSM++) + for (unsigned short iSM = 0; iSM < 20; iSM++) { BOOST_CHECK_EQUAL(parameter.getTimeCalibParamL1Phase(iSM), L1Phase[iSM]); + } // Comparison test @@ -82,8 +85,9 @@ BOOST_AUTO_TEST_CASE(testTimeL1PhaseCalib) std::string fileLHC18q = inputDir + "TimeL1Phase_LHC18q_296623.txt"; std::ifstream fileL1PhaseLHC18q(fileLHC18q, std::ifstream::in); - if (!fileL1PhaseLHC18q.is_open()) + if (!fileL1PhaseLHC18q.is_open()) { std::cout << "The file TimeL1Phase_LHC18q_296623 was not opened\n"; + } unsigned char L1Phase_LHC18q; diff --git a/Detectors/EMCAL/calib/test/testTriggerDCS.cxx b/Detectors/EMCAL/calib/test/testTriggerDCS.cxx index e784b9ab5b6f2..8dee30ac50ef3 100644 --- a/Detectors/EMCAL/calib/test/testTriggerDCS.cxx +++ b/Detectors/EMCAL/calib/test/testTriggerDCS.cxx @@ -62,8 +62,9 @@ void ConfigureReferenceSTU(TriggerSTUDCS& testobject) testobject.setFw(0x2A012); testobject.setMedianMode(0); testobject.setRegion(0xffffffff); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { testobject.setPHOSScale(i, 0); + } } BOOST_AUTO_TEST_CASE(TriggerDCS_test) diff --git a/Detectors/EMCAL/calib/test/testTriggerSTUDCS.cxx b/Detectors/EMCAL/calib/test/testTriggerSTUDCS.cxx index f3b6cba8f09fb..b1c6884cfd1d2 100644 --- a/Detectors/EMCAL/calib/test/testTriggerSTUDCS.cxx +++ b/Detectors/EMCAL/calib/test/testTriggerSTUDCS.cxx @@ -44,8 +44,9 @@ void ConfigureReference(TriggerSTUDCS& testobject) testobject.setFw(0x2A012); testobject.setMedianMode(0); testobject.setRegion(0xffffffff); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { testobject.setPHOSScale(i, 0); + } } BOOST_AUTO_TEST_CASE(TriggerSTUDCS_test) diff --git a/Detectors/EMCAL/calib/test/testTriggerTRUDCS.cxx b/Detectors/EMCAL/calib/test/testTriggerTRUDCS.cxx index db234e69b4952..9c2c3f74027dc 100644 --- a/Detectors/EMCAL/calib/test/testTriggerTRUDCS.cxx +++ b/Detectors/EMCAL/calib/test/testTriggerTRUDCS.cxx @@ -63,8 +63,9 @@ BOOST_AUTO_TEST_CASE(TriggerTRUDCS_test) BOOST_CHECK_EQUAL(testobject.getGTHRL0(), GTHRL0); BOOST_CHECK_EQUAL(testobject.getRLBKSTU(), RLBKSTU); - for (int ireg = 0; ireg < 6; ireg++) + for (int ireg = 0; ireg < 6; ireg++) { BOOST_CHECK_EQUAL(testobject.getMaskReg(ireg), MaskReg[ireg]); + } BOOST_CHECK_EQUAL(testobject.getFw(), Fw); diff --git a/Detectors/EMCAL/reconstruction/CMakeLists.txt b/Detectors/EMCAL/reconstruction/CMakeLists.txt index eb94d2bdef60f..ea2bbdeef9b59 100644 --- a/Detectors/EMCAL/reconstruction/CMakeLists.txt +++ b/Detectors/EMCAL/reconstruction/CMakeLists.txt @@ -9,11 +9,9 @@ # submit itself to any jurisdiction. o2_add_library(EMCALReconstruction - SOURCES src/RawReaderFile.cxx - src/RawReaderMemory.cxx + SOURCES src/RawReaderMemory.cxx src/RawBuffer.cxx src/RawHeaderStream.cxx - src/RAWDataHeader.cxx src/RawPayload.cxx src/AltroDecoder.cxx src/Bunch.cxx @@ -21,28 +19,33 @@ o2_add_library(EMCALReconstruction src/CaloFitResults.cxx src/CaloRawFitter.cxx src/CaloRawFitterStandard.cxx + src/CaloRawFitterGamma2.cxx src/ClusterizerParameters.cxx src/Clusterizer.cxx src/ClusterizerTask.cxx src/DigitReader.cxx + src/CTFCoder.cxx + src/CTFHelper.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::Headers AliceO2::InfoLogger O2::DataFormatsEMCAL O2::DetectorsBase - O2::EMCALBase) + O2::DetectorsRaw + O2::EMCALBase + O2::rANS + ms_gsl::ms_gsl) o2_target_root_dictionary( EMCALReconstruction - HEADERS include/EMCALReconstruction/RawReaderFile.h - include/EMCALReconstruction/RawReaderMemory.h + HEADERS include/EMCALReconstruction/RawReaderMemory.h include/EMCALReconstruction/AltroDecoder.h include/EMCALReconstruction/RawPayload.h include/EMCALReconstruction/Bunch.h include/EMCALReconstruction/Channel.h - include/EMCALReconstruction/RAWDataHeader.h include/EMCALReconstruction/CaloFitResults.h include/EMCALReconstruction/CaloRawFitter.h include/EMCALReconstruction/CaloRawFitterStandard.h + include/EMCALReconstruction/CaloRawFitterGamma2.h include/EMCALReconstruction/ClusterizerParameters.h include/EMCALReconstruction/Clusterizer.h include/EMCALReconstruction/ClusterizerTask.h diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h index 7b453c05e969d..00720e2c5ad65 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h @@ -17,12 +17,7 @@ #include "EMCALBase/RCUTrailer.h" #include "EMCALReconstruction/Bunch.h" #include "EMCALReconstruction/Channel.h" - -// for template specification -#include "EMCALReconstruction/RawReaderFile.h" #include "EMCALReconstruction/RawReaderMemory.h" -#include "EMCALReconstruction/RAWDataHeader.h" -#include "Headers/RAWDataHeader.h" namespace o2 { @@ -84,13 +79,13 @@ class AltroDecoderError : public std::exception /// the payload, altro header and RCU trailer contents. /// /// Based on AliAltroRawStreamV3 and AliCaloRawStreamV3 by C. Cheshkov -template <class RawReader> + class AltroDecoder { public: /// \brief Constructor /// \param reader Raw reader instance to be decoded - AltroDecoder(RawReader& reader); + AltroDecoder(RawReaderMemory& reader); /// \brief Destructor ~AltroDecoder() = default; @@ -130,7 +125,7 @@ class AltroDecoder /// In case of failure an exception is thrown. void checkRCUTrailer(); - RawReader& mRawReader; ///< underlying raw reader + RawReaderMemory& mRawReader; ///< underlying raw reader RCUTrailer mRCUTrailer; ///< RCU trailer std::vector<Channel> mChannels; ///< vector of channels in the raw stream bool mChannelsInitialized = false; ///< check whether the channels are initialized @@ -138,12 +133,6 @@ class AltroDecoder ClassDefNV(AltroDecoder, 1); }; -// template specifications -using AltroDecoderMemoryRDHvE = AltroDecoder<RawReaderMemory<o2::emcal::RAWDataHeader>>; -using AltroDecoderMemoryRDHv4 = AltroDecoder<RawReaderMemory<o2::header::RAWDataHeaderV4>>; -using AltroDecoderFileRDHvE = AltroDecoder<RawReaderFile<o2::emcal::RAWDataHeader>>; -using AltroDecoderFileRDHv4 = AltroDecoder<RawReaderFile<o2::header::RAWDataHeaderV4>>; - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h new file mode 100644 index 0000000000000..a51ca476032d3 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of EMCAL data + +#ifndef O2_EMCAL_CTFCODER_H +#define O2_EMCAL_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include <array> +#include "DataFormatsEMCAL/CTF.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" +#include "EMCALReconstruction/CTFHelper.h" + +class TTree; + +namespace o2 +{ +namespace emcal +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::EMC) {} + ~CTFCoder() = default; + + /// entropy-encode data to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const TriggerRecord>& trigData, const gsl::span<const Cell>& cellData); + + /// entropy decode data from buffer with CTF + template <typename VTRG, typename VCELL> + void decode(const CTF::base& ec, VTRG& trigVec, VCELL& cellVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<TriggerRecord>& trigVec, std::vector<Cell>& cellVec); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const TriggerRecord>& trigData, const gsl::span<const Cell>& cellData) +{ + 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_bcIncTrig + MD::EENCODE, // BLC_orbitIncTrig + MD::EENCODE, // BLC_entriesTrig + MD::EENCODE, // BLC_towerID + MD::EENCODE, // BLC_time + MD::EENCODE, // BLC_energy + MD::EENCODE // BLC_status + }; + + CTFHelper helper(trigData, cellData); + + // book output size with some margin + auto szIni = sizeof(CTFHeader) + helper.getSize() / 4; // will be autoexpanded if needed + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(helper.createHeader()); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODEEMC(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODEEMC(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); + ENCODEEMC(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); + ENCODEEMC(helper.begin_entriesTrig(), helper.end_entriesTrig(), CTF::BLC_entriesTrig, 0); + + ENCODEEMC(helper.begin_towerID(), helper.end_towerID(), CTF::BLC_towerID, 0); + ENCODEEMC(helper.begin_time(), helper.end_time(), CTF::BLC_time, 0); + ENCODEEMC(helper.begin_energy(), helper.end_energy(), CTF::BLC_energy, 0); + ENCODEEMC(helper.begin_status(), helper.end_status(), CTF::BLC_status, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VTRG, typename VCELL> +void CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCELL& cellVec) +{ + auto header = ec.getHeader(); + ec.print(getPrefix()); + std::vector<uint16_t> bcInc, entries, energy, cellTime, tower; + std::vector<uint32_t> orbitInc; + std::vector<uint8_t> status; + +#define DECODEEMCAL(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODEEMCAL(bcInc, CTF::BLC_bcIncTrig); + DECODEEMCAL(orbitInc, CTF::BLC_orbitIncTrig); + DECODEEMCAL(entries, CTF::BLC_entriesTrig); + DECODEEMCAL(tower, CTF::BLC_towerID); + + DECODEEMCAL(cellTime, CTF::BLC_time); + DECODEEMCAL(energy, CTF::BLC_energy); + DECODEEMCAL(status, CTF::BLC_status); + // clang-format on + // + trigVec.clear(); + cellVec.clear(); + trigVec.reserve(header.nTriggers); + status.reserve(header.nCells); + + uint32_t firstEntry = 0, cellCount = 0; + o2::InteractionRecord ir(header.firstBC, header.firstOrbit); + + Cell cell; + for (uint32_t itrig = 0; itrig < header.nTriggers; itrig++) { + // restore TrigRecord + if (orbitInc[itrig]) { // non-0 increment => new orbit + ir.bc = bcInc[itrig]; // bcInc has absolute meaning + ir.orbit += orbitInc[itrig]; + } else { + ir.bc += bcInc[itrig]; + } + + firstEntry = cellVec.size(); + for (uint16_t ic = 0; ic < entries[itrig]; ic++) { + cell.setPacked(tower[cellCount], cellTime[cellCount], energy[cellCount], status[cellCount]); + cellVec.emplace_back(cell); + cellCount++; + } + trigVec.emplace_back(ir, firstEntry, entries[itrig]); + } + assert(cellCount == header.nCells); +} + +} // namespace emcal +} // namespace o2 + +#endif // O2_EMCAL_CTFCODER_H diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h new file mode 100644 index 0000000000000..4589f217226d1 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h @@ -0,0 +1,193 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.h +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for EMCAL CTF creation + +#ifndef O2_EMCAL_CTF_HELPER_H +#define O2_EMCAL_CTF_HELPER_H + +#include "DataFormatsEMCAL/CTF.h" +#include <gsl/span> + +namespace o2 +{ +namespace emcal +{ + +class CTFHelper +{ + + public: + CTFHelper(const gsl::span<const TriggerRecord>& trgData, const gsl::span<const Cell>& cellData) + : mTrigData(trgData), mCellData(cellData) {} + + CTFHeader createHeader() + { + CTFHeader h{uint32_t(mTrigData.size()), uint32_t(mCellData.size()), 0, 0}; + if (mTrigData.size()) { + h.firstOrbit = mTrigData[0].getBCData().orbit; + h.firstBC = mTrigData[0].getBCData().bc; + } + return h; + } + + size_t getSize() const { return mTrigData.size() * sizeof(TriggerRecord) + mCellData.size() * sizeof(Cell); } + + //>>> =========================== ITERATORS ======================================== + + template <typename I, typename D, typename T> + class _Iter + { + public: + using difference_type = int64_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; + using iterator_category = std::random_access_iterator_tag; + + _Iter(const gsl::span<const D>& data, bool end = false) : mData(data), mIndex(end ? data.size() : 0){}; + _Iter() = default; + + const I& operator++() + { + ++mIndex; + return (I&)(*this); + } + + const I& operator--() + { + mIndex--; + return (I&)(*this); + } + + 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); + } + + 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; } + + protected: + gsl::span<const D> mData{}; + size_t 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<Iter_bcIncTrig, TriggerRecord, uint16_t> + { + public: + using _Iter<Iter_bcIncTrig, TriggerRecord, uint16_t>::_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; + } else { + return mData[mIndex].getBCData().bc; + } + } + return 0; + } + }; + + //_______________________________________________ + // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) + class Iter_orbitIncTrig : public _Iter<Iter_orbitIncTrig, TriggerRecord, uint32_t> + { + public: + using _Iter<Iter_orbitIncTrig, TriggerRecord, uint32_t>::_Iter; + value_type operator*() const { return mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0; } + }; + + //_______________________________________________ + // Number of cells for trigger + class Iter_entriesTrig : public _Iter<Iter_entriesTrig, TriggerRecord, uint16_t> + { + public: + using _Iter<Iter_entriesTrig, TriggerRecord, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getNumberOfObjects(); } + }; + + //_______________________________________________ + class Iter_towerID : public _Iter<Iter_towerID, Cell, uint16_t> + { + public: + using _Iter<Iter_towerID, Cell, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedTowerID(); } + }; + + //_______________________________________________ + class Iter_time : public _Iter<Iter_time, Cell, uint16_t> + { + public: + using _Iter<Iter_time, Cell, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedTime(); } + }; + + //_______________________________________________ + class Iter_energy : public _Iter<Iter_energy, Cell, uint16_t> + { + public: + using _Iter<Iter_energy, Cell, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedEnergy(); } + }; + + //_______________________________________________ + class Iter_status : public _Iter<Iter_status, Cell, uint8_t> + { + public: + using _Iter<Iter_status, Cell, uint8_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedCellStatus(); } + }; + + //<<< =========================== ITERATORS ======================================== + + Iter_bcIncTrig begin_bcIncTrig() const { return Iter_bcIncTrig(mTrigData, false); } + Iter_bcIncTrig end_bcIncTrig() const { return Iter_bcIncTrig(mTrigData, true); } + + Iter_orbitIncTrig begin_orbitIncTrig() const { return Iter_orbitIncTrig(mTrigData, false); } + Iter_orbitIncTrig end_orbitIncTrig() const { return Iter_orbitIncTrig(mTrigData, true); } + + Iter_entriesTrig begin_entriesTrig() const { return Iter_entriesTrig(mTrigData, false); } + Iter_entriesTrig end_entriesTrig() const { return Iter_entriesTrig(mTrigData, true); } + + Iter_towerID begin_towerID() const { return Iter_towerID(mCellData, false); } + Iter_towerID end_towerID() const { return Iter_towerID(mCellData, true); } + + Iter_time begin_time() const { return Iter_time(mCellData, false); } + Iter_time end_time() const { return Iter_time(mCellData, true); } + + Iter_energy begin_energy() const { return Iter_energy(mCellData, false); } + Iter_energy end_energy() const { return Iter_energy(mCellData, true); } + + Iter_status begin_status() const { return Iter_status(mCellData, false); } + Iter_status end_status() const { return Iter_status(mCellData, true); } + + private: + const gsl::span<const o2::emcal::TriggerRecord> mTrigData; + const gsl::span<const o2::emcal::Cell> mCellData; +}; + +} // namespace emcal +} // namespace o2 + +#endif diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitter.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitter.h index d1ee85cd52e1e..5a91557dbe5de 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitter.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitter.h @@ -43,7 +43,11 @@ class CaloRawFitter CaloRawFitter(const char* name, const char* nameshort); /// \brief Destructor - ~CaloRawFitter() = default; + virtual ~CaloRawFitter() = default; + + virtual CaloFitResults evaluate(const std::vector<Bunch>& bunchvector, + std::optional<unsigned int> altrocfg1, + std::optional<unsigned int> altrocfg2) = 0; /// \brief Method to do the selection of what should possibly be fitted. /// \return Size of the sub-selected sample, @@ -54,7 +58,6 @@ class CaloRawFitter /// \return pedestal, /// \return first time bin, /// \return last time bin, - /// \return ampitude cut std::tuple<int, int, float, short, short, float, int, int> preFitEvaluateSamples(const std::vector<Bunch>& bunchvector, std::optional<unsigned int> altrocfg1, std::optional<unsigned int> altrocfg2, int acut); diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterGamma2.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterGamma2.h new file mode 100755 index 0000000000000..84a797d617176 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterGamma2.h @@ -0,0 +1,78 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 __CALORAWFITTERGAMMA2_H__ +#define __CALORAWFITTERGAMMA2_H__ + +#include <iosfwd> +#include <array> +#include <optional> +#include <Rtypes.h> +#include "EMCALReconstruction/CaloFitResults.h" +#include "DataFormatsEMCAL/Constants.h" +#include "EMCALReconstruction/Bunch.h" +#include "EMCALReconstruction/CaloRawFitter.h" + +namespace o2 +{ + +namespace emcal +{ + +/// \class CaloRawFitterGamma2 +/// \brief Raw data fitting: Gamma-2 function +/// \ingroup EMCALreconstruction +/// \author Martin Poghosyan <Martin.Poghosyan@cern.ch>, ORNL. +/// \since May 13th, 2020 +/// +/// Evaluation of amplitude and peak position using gamma-2 function. +/// Derivatives calculated analytically. +/// Newton's method used for solving the set of non-linear equations. +/// Ported from class AliCaloRawAnalyzerGamma2 from AliRoot + +class CaloRawFitterGamma2 final : public CaloRawFitter +{ + + public: + /// \brief Constructor + CaloRawFitterGamma2(); + + /// \brief Destructor + ~CaloRawFitterGamma2() final = default; + + void setNiterationsMax(int n) { mNiterationsMax = n; } + int getNiterations() { return mNiter; } + int getNiterationsMax() { return mNiterationsMax; } + + /// \brief Evaluation Amplitude and TOF + /// return Container with the fit results (amp, time, chi2, ...) + CaloFitResults evaluate(const std::vector<Bunch>& bunchvector, + std::optional<unsigned int> altrocfg1, + std::optional<unsigned int> altrocfg2) final; + + private: + int mNiter = 0; ///< number of iteraions + int mNiterationsMax = 15; ///< max number of iteraions + + /// \brief Fits the raw signal time distribution + /// \return chi2, fit status. + std::tuple<float, bool> doFit_1peak(int firstTimeBin, int nSamples, float& ampl, float& time); + + /// \brief Fits the raw signal time distribution + /// \return the fit parameters: amplitude, time. + std::tuple<float, float> doParabolaFit(int x) const; + + ClassDefNV(CaloRawFitterGamma2, 1); +}; // End of CaloRawFitterGamma2 + +} // namespace emcal + +} // namespace o2 +#endif diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterStandard.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterStandard.h index 801fd8efd2db7..54fe091322810 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterStandard.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CaloRawFitterStandard.h @@ -39,7 +39,7 @@ namespace emcal /// least square fit for the /// Moment assuming identical and /// independent errors (equivalent with chi square) -class CaloRawFitterStandard : public CaloRawFitter +class CaloRawFitterStandard final : public CaloRawFitter { public: @@ -47,7 +47,7 @@ class CaloRawFitterStandard : public CaloRawFitter CaloRawFitterStandard(); /// \brief Destructor - ~CaloRawFitterStandard() = default; + ~CaloRawFitterStandard() final = default; /// \brief Approximate response function of the EMCal electronics. /// \param x: bin @@ -57,9 +57,9 @@ class CaloRawFitterStandard : public CaloRawFitter /// \brief Evaluation Amplitude and TOF /// return Container with the fit results (amp, time, chi2, ...) - virtual CaloFitResults evaluate(const std::vector<Bunch>& bunchvector, - std::optional<unsigned int> altrocfg1, - std::optional<unsigned int> altrocfg2); + CaloFitResults evaluate(const std::vector<Bunch>& bunchvector, + std::optional<unsigned int> altrocfg1, + std::optional<unsigned int> altrocfg2) final; /// \brief Fits the raw signal time distribution /// \return the fit parameters: amplitude, time, chi2, fit status. diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/Clusterizer.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/Clusterizer.h index a719932567609..bc057b0a3b4a9 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/Clusterizer.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/Clusterizer.h @@ -67,6 +67,11 @@ class Clusterizer Clusterizer(); ~Clusterizer() = default; + void clear() + { + mFoundClusters.clear(); + mInputIndices.clear(); + } void initialize(double timeCut, double timeMin, double timeMax, double gradientCut, bool doEnergyGradientCut, double thresholdSeedE, double thresholdCellE); void findClusters(const gsl::span<InputType const>& inputArray); const std::vector<Cluster>* getFoundClusters() const { return &mFoundClusters; } diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RAWDataHeader.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RAWDataHeader.h deleted file mode 100644 index 652e1c41a9677..0000000000000 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RAWDataHeader.h +++ /dev/null @@ -1,109 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_RAwDATAHEADER_H -#define ALICEO2_EMCAL_RAwDATAHEADER_H - -#include <iosfwd> -#include <cstdint> -#include "Rtypes.h" - -namespace o2 -{ -namespace emcal -{ -struct RAWDataHeader { - union { - uint32_t word0 = 0xFFFFFFFF; // size of the raw data in bytes - }; - - union { - uint32_t word1 = 3 << 24; // bunch crossing, L1 trigger message and format version - struct { - uint32_t triggerBC : 14; ///< bunch crossing [0-13] - uint32_t triggermessageL1 : 8; ///< L1 trigger message [14-21] - uint32_t zero11 : 2; ///< Unassigned [22-23] - uint32_t version : 8; ///< Version [24-31] - }; - }; - - union { - uint32_t word2 = 0; //< Size and offset - struct { - uint32_t offsetToNext : 16; ///< offset [0-15] - uint32_t memorySize : 16; ///< size [16-31] - }; - }; - - union { - uint32_t word3 = 0; ///< Number of packets and linkID - struct { - uint8_t linkID : 8; ///< Link ID [0-7] - uint8_t packetCounter : 8; ///< Number of packets [8-15] - uint16_t zero31 : 16; ///< Unassigned [16-31] - }; - }; - - union { - uint32_t word4 = 0x10000; // status & error bits and mini event ID - struct { - uint32_t triggerOrbit : 12; ///< mini event ID [0-11] - uint32_t status : 16; ///< status & error bits [12-27] - uint32_t zero41 : 4; ///< Unassigned [28-31] - }; - }; - - union { - uint32_t word5; ///< First word of the tirgger types - struct { - uint32_t triggerType : 32; ///< low trigger types [0-49] - }; - }; - - union { - uint32_t word6; ///< Second word of the trigger types - struct { - uint32_t triggerTypesHigh : 18; ///< Second part of the trigger types [0-17] - uint32_t triggerTypesNext50 : 14; ///< First part of the trigger types next 50 [18-31] - }; - }; - - union { - uint32_t word7; ///< Third word of the trigger types - struct { - uint32_t triggerTypesNext50Middle : 32; ///< Second part of the trigger types next 50 - }; - }; - - union { - uint32_t word8; ///< Fourth word of the trigger types - struct { - uint32_t triggerTypesNext50High : 4; ///< Third part of the trigger types next 50 [0-3] - uint32_t zero81 : 24; ///< Unassigned [4-27] - uint32_t roi : 4; ///< First part of the roi [28-31] - }; - }; - - union { - uint32_t word9; ///< Second word of the roi - struct { - uint32_t roiHigh : 32; ///< Second part of the roi - }; - }; -}; - -std::istream& operator>>(std::istream& in, o2::emcal::RAWDataHeader& header); -std::ostream& operator<<(std::ostream& out, const o2::emcal::RAWDataHeader& header); - -} // namespace emcal - -} // namespace o2 - -#endif // _O2_EMCAL_RAwDATAHEADER_H__ \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderFile.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderFile.h deleted file mode 100644 index 9dedf2c087656..0000000000000 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderFile.h +++ /dev/null @@ -1,158 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_RAWREADERFILE_H -#define ALICEO2_EMCAL_RAWREADERFILE_H - -#include <array> -#include <bitset> -#include <cstdint> -#include <fstream> -#include <string> - -#include "Rtypes.h" -#include "RStringView.h" - -#include "Headers/RAWDataHeader.h" -#include "EMCALReconstruction/RawBuffer.h" -#include "EMCALReconstruction/RAWDataHeader.h" -#include "EMCALReconstruction/RawPayload.h" - -namespace o2 -{ - -namespace emcal -{ - -/// \class RawReaderFile -/// \brief Reader for raw data produced by the ReadoutCard from a binary file -/// \ingroup EMCALreconstruction -/// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory -/// \since Aug. 12, 2019 -/// -/// -template <class RawHeader> -class RawReaderFile -{ - public: - /// \brief Constructor - /// - /// Opening the raw file and determining its size and the number - /// of pages. - RawReaderFile(const std::string_view filename); - - /// \brief Destructor - /// - /// Closing the raw file - ~RawReaderFile(); - - /// \brief Read next payload from the stream - /// - /// Read the next pages until the stop bit is found. - void next(); - - /// \brief Read the next page from the stream (single DMA page) - /// \param resetPayload If true the raw payload is reset - /// \throw Error if the page cannot be read or header or payload cannot be deocded - /// - /// Function reading a single DMA page from the stream. It is called - /// inside the next() function for reading payload from multiple DMA - /// pages. As the function cannot handle payload from multiple pages - /// it should not be called directly by the user. - void nextPage(bool resetPayload = true); - - /// \brief Read page with a given index - /// \param page Index of the page to be decoded - /// \throw RawDecodingError if the page cannot be read or header or payload cannot be deocded - /// - /// The reader will try to read the page with a certain index. In - /// case the page cannot be decoded (page index outside range, - /// decoding of header or payload failed), and excpetion is raised. - void readPage(int page); - - /// \brief access to the raw header of the current page - /// \return Raw header of the current page - /// \throw Error with HEADER_INVALID if the header was not decoded - const RawHeader& getRawHeader() const; - - /// \brief access to the raw buffer (single DMA page) - /// \return Raw buffer of the current page - /// \throw Error with PAYLOAD_INCALID if payload was not decoded - const RawBuffer& getRawBuffer() const; - - /// \brief access to the full raw payload (single or multiple DMA pages) - /// \return Raw Payload of the data until the stop bit is received. - const RawPayload& getPayload() const { return mRawPayload; } - - /// \brief Return size of the payload - /// \return size of the payload - int getPayloadSize() const { return mRawPayload.getPayloadSize(); } - - /// \brief get the size of the file in bytes - /// \return size of the file in byte - int getFileSize() const noexcept { return mFileSize; } - - /// \brief get the number of pages in the file - /// \return number of pages in the file - int getNumberOfPages() const noexcept { return mNumData; } - - /// \brief check if more pages are available in the raw file - /// \return true if there is a next page - bool hasNext() const { return mCurrentPosition < mNumData; } - - static void readFile(const std::string_view filename); - - protected: - /// \brief Init the raw reader - /// - /// Opening the raw file and determining the number of superpages - void init(); - - /// \brief Decode the Raw Data Header - /// \throw RawDecodingError with HEADER_DECODING in case the header decoding failed - /// - /// Decoding the raw header. Function assumes that the pointer - /// is at the beginning of the raw header - void readHeader(); - - /// \brief Decode the payload - /// \throw RawDecodingError with PAYLOAD_DECODING in case the payload decoding failed - /// - /// Decoding the payload. The function assumes that the pointer is at - /// the beginning of the payload of the page. Needs the raw header of the - /// page to be decoded before in order to determine size of the payload - /// and offset. - void readPayload(); - - bool isStop(const o2::emcal::RAWDataHeader& hdr) { return true; } - bool isStop(const o2::header::RAWDataHeaderV4& hdr) { return hdr.stop; } - - private: - std::string mInputFileName; ///< Name of the input file - std::ifstream mDataFile; ///< Stream of the inputfile - RawHeader mRawHeader; ///< Raw header - RawBuffer mRawBuffer; ///< Raw buffer - RawPayload mRawPayload; ///< Raw payload (can consist of multiple pages) - int mCurrentPosition = 0; ///< Current page in file - int mFileSize = 0; ///< Size of the file in bytes - int mNumData = 0; ///< Number of pages - bool mRawHeaderInitialized = false; ///< RDH for current page initialized - bool mPayloadInitialized = false; ///< Payload for current page initialized - - ClassDefNV(RawReaderFile, 1); -}; - -// template specifications -using RawReaderFileRDHvE = RawReaderFile<o2::emcal::RAWDataHeader>; -using RawReaderFileRDHv4 = RawReaderFile<o2::header::RAWDataHeaderV4>; - -} // namespace emcal - -} // namespace o2 -#endif diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h index 208c73f0c4319..c6d544e0529ff 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h @@ -13,10 +13,11 @@ #include <gsl/span> #include <Rtypes.h> +#include "EMCALBase/RCUTrailer.h" #include "EMCALReconstruction/RawBuffer.h" -#include "EMCALReconstruction/RAWDataHeader.h" #include "EMCALReconstruction/RawPayload.h" #include "Headers/RAWDataHeader.h" +#include "Headers/RDHAny.h" namespace o2 { @@ -31,7 +32,6 @@ namespace emcal /// \since Nov. 14, 2019 /// /// -template <class RawHeader> class RawReaderMemory { public: @@ -72,7 +72,7 @@ class RawReaderMemory /// \brief access to the raw header of the current page /// \return Raw header of the current page /// \throw RawDecodingError with HEADER_INVALID if the header was not decoded - const RawHeader& getRawHeader() const; + const o2::header::RDHAny& getRawHeader() const; /// \brief access to the raw buffer (single DMA page) /// \return Raw buffer of the current page @@ -105,14 +105,15 @@ class RawReaderMemory /// Rewind stream to the first entry void init(); - bool isStop(const o2::emcal::RAWDataHeader& hdr) { return true; } - bool isStop(const o2::header::RAWDataHeaderV4& hdr) { return hdr.stop; } + o2::header::RDHAny decodeRawHeader(const void* headerwords); private: gsl::span<const char> mRawMemoryBuffer; ///< Memory block with multiple DMA pages RawBuffer mRawBuffer; ///< Raw buffer - RawHeader mRawHeader; ///< Raw header + o2::header::RDHAny mRawHeader; ///< Raw header RawPayload mRawPayload; ///< Raw payload (can consist of multiple pages) + RCUTrailer mCurrentTrailer; ///< RCU trailer + uint64_t mTrailerPayloadWords = 0; ///< Payload words in common trailer int mCurrentPosition = 0; ///< Current page in file int mNumData = 0; ///< Number of pages bool mRawHeaderInitialized = false; ///< RDH for current page initialized @@ -121,10 +122,6 @@ class RawReaderMemory ClassDefNV(RawReaderMemory, 1); }; -// For template specifications -using RawReaderMemoryRDHvE = RawReaderMemory<o2::emcal::RAWDataHeader>; -using RawReaderMemoryRDHv4 = RawReaderMemory<o2::header::RAWDataHeaderV4>; - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/reconstruction/macros/RawFitterTESTs.C b/Detectors/EMCAL/reconstruction/macros/RawFitterTESTs.C index 9eef11f2edd04..2979b0e698289 100644 --- a/Detectors/EMCAL/reconstruction/macros/RawFitterTESTs.C +++ b/Detectors/EMCAL/reconstruction/macros/RawFitterTESTs.C @@ -15,57 +15,95 @@ #include <vector> #include "RStringView.h" #include <Rtypes.h> +#include "DetectorsRaw/RawFileReader.h" +#include "DetectorsRaw/RDHUtils.h" #include "EMCALReconstruction/CaloFitResults.h" #include "EMCALReconstruction/Bunch.h" #include "EMCALReconstruction/CaloRawFitterStandard.h" #include "EMCALReconstruction/AltroDecoder.h" +#include "EMCALReconstruction/CaloRawFitterGamma2.h" //#include "EMCALReconstruction/RawHeaderStream.h" #endif using namespace o2::emcal; /// \brief Testing the standard raw fitter on run2 to run3 converted data -void RawFitterTESTs() +void RawFitterTESTs(const char* filename = "") { const Int_t NoiseThreshold = 3; - // Use the RawReaderFile to read the raw data file - const char* aliceO2env = std::getenv("O2_ROOT"); - std::string inputDir = " "; - if (aliceO2env) - inputDir = aliceO2env; - inputDir += "/share/Detectors/EMC/files/"; + std::string inputfile = filename; + if (!inputfile.length()) { + // Use the RawReaderFile to read the raw data file + const char* aliceO2env = std::getenv("O2_ROOT"); + std::string inputDir = " "; + if (aliceO2env) + inputDir = aliceO2env; + inputDir += "/share/Detectors/EMC/files/"; + inputfile = inputDir + "emcal.raw"; + } + std::cout << "Using input file " << inputfile << std::endl; - o2::emcal::RawReaderFile<o2::header::RAWDataHeaderV4> rawreader(inputDir + "emcal.raw"); + o2::raw::RawFileReader reader; + reader.setDefaultDataOrigin(o2::header::gDataOriginEMC); + reader.setDefaultDataDescription(o2::header::gDataDescriptionRawData); + reader.setDefaultReadoutCardType(o2::raw::RawFileReader::RORC); + reader.addFile(inputfile.data()); + reader.init(); // define the standard raw fitter - o2::emcal::CaloRawFitterStandard RawFitter; + //o2::emcal::CaloRawFitterStandard RawFitter; + o2::emcal::CaloRawFitterGamma2 RawFitter; RawFitter.setAmpCut(NoiseThreshold); RawFitter.setL1Phase(0.); - // loop over all the DMA pages - while (rawreader.hasNext()) { + while (1) { + int tfID = reader.getNextTFToRead(); + if (tfID >= reader.getNTimeFrames()) { + std::cerr << "nothing left to read after " << tfID << " TFs read" << std::endl; + break; + } + std::vector<char> dataBuffer; // where to put extracted data + std::cout << "Next iteration: Number of links: " << reader.getNLinks() << std::endl; + for (int il = 0; il < reader.getNLinks(); il++) { + auto& link = reader.getLink(il); + std::cout << "Decoding link " << il << std::endl; - rawreader.next(); - std::cout << "next page \n"; + auto sz = link.getNextTFSize(); // size in bytes needed for the next TF of this link + dataBuffer.resize(sz); + link.readNextTF(dataBuffer.data()); - //std::cout<<rawreader.getRawHeader()<<std::endl; + // Parse + o2::emcal::RawReaderMemory parser(dataBuffer); + while (parser.hasNext()) { + parser.next(); + std::cout << "next page \n"; + if (o2::raw::RDHUtils::getFEEID(parser.getRawHeader()) >= 40) + continue; - // use the altro decoder to decode the raw data, and extract the RCU trailer - o2::emcal::AltroDecoder<decltype(rawreader)> decoder(rawreader); - decoder.decode(); + //std::cout<<rawreader.getRawHeader()<<std::endl; - std::cout << decoder.getRCUTrailer() << std::endl; + // use the altro decoder to decode the raw data, and extract the RCU trailer + o2::emcal::AltroDecoder decoder(parser); + std::cout << "Decoding" << std::endl; + decoder.decode(); - // Loop over all the channels - for (auto& chan : decoder.getChannels()) { + std::cout << decoder.getRCUTrailer() << std::endl; + std::cout << "Found number of channels: " << decoder.getChannels().size() << std::endl; - // define the conatiner for the fit results, and perform the raw fitting using the stadnard raw fitter - o2::emcal::CaloFitResults fitResults = RawFitter.evaluate(chan.getBunches(), 0, 0); + // Loop over all the channels + for (auto& chan : decoder.getChannels()) { + std::cout << "processing next channel idx " << chan.getChannelIndex() << ", " << chan.getHardwareAddress() << std::endl; + // define the conatiner for the fit results, and perform the raw fitting using the stadnard raw fitter + continue; + o2::emcal::CaloFitResults fitResults = RawFitter.evaluate(chan.getBunches(), 0, 0); - // print the fit output - std::cout << "The Time is : " << fitResults.getTime() << " And the Amplitude is : " << fitResults.getAmp() << std::endl; + // print the fit output + std::cout << "The Time is : " << fitResults.getTime() << " And the Amplitude is : " << fitResults.getAmp() << std::endl; + } + } } + reader.setNextTFToRead(++tfID); } } diff --git a/Detectors/EMCAL/reconstruction/run/rawReaderFile.cxx b/Detectors/EMCAL/reconstruction/run/rawReaderFile.cxx index 70808de706187..badb7c785292d 100644 --- a/Detectors/EMCAL/reconstruction/run/rawReaderFile.cxx +++ b/Detectors/EMCAL/reconstruction/run/rawReaderFile.cxx @@ -8,27 +8,26 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file rawReaderFile.cxx +/// \file rawReaderFileNew.cxx /// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory #include <iostream> #include <boost/program_options.hpp> -#include "Headers/RAWDataHeader.h" -#include "EMCALReconstruction/RawHeaderStream.h" -#include "EMCALReconstruction/RAWDataHeader.h" -#include "EMCALReconstruction/RawReaderFile.h" +#include "DetectorsRaw/RawFileReader.h" #include "EMCALReconstruction/AltroDecoder.h" +#include "EMCALReconstruction/RawReaderMemory.h" +#include "FairLogger.h" namespace bpo = boost::program_options; -using namespace o2::emcal; +//using namespace o2::emcal; int main(int argc, char** argv) { bpo::variables_map vm; bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + " <cmds/options>\n" - " Tool will decode the GBTx data for SAMPA 0\n" + " Tool will decode the DDLx data for EMCAL 0\n" "Commands / Options"); bpo::options_description opt_hidden(""); bpo::options_description opt_all; @@ -39,7 +38,6 @@ int main(int argc, char** argv) add_option("help,h", "Print this help message"); add_option("verbose,v", bpo::value<uint32_t>()->default_value(0), "Select verbosity level [0 = no output]"); add_option("version", "Print version information"); - add_option("rdh,r", bpo::value<int>()->default_value(3), "RAW Data Header version (3 - CDHv3, 4 - RDHv4)"); add_option("input-file,i", bpo::value<std::string>()->required(), "Specifies input file."); add_option("debug,d", bpo::value<uint32_t>()->default_value(0), "Select debug output level [0 = no debug output]"); @@ -67,34 +65,56 @@ int main(int argc, char** argv) exit(2); } - auto decode = [](auto reader) { - while (reader.hasNext()) { - reader.next(); - std::cout << reader.getRawHeader(); - o2::emcal::AltroDecoder<decltype(reader)> decoder(reader); - decoder.decode(); - std::cout << decoder.getRCUTrailer() << std::endl; - for (auto& chan : decoder.getChannels()) { - std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; - for (auto& bunch : chan.getBunches()) { - std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; - auto adcs = bunch.getADC(); - int time = bunch.getStartTime(); - for (int i = adcs.size() - 1; i >= 0; i--) { - std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; - time--; + auto rawfilename = vm["input-file"].as<std::string>(); + + o2::raw::RawFileReader reader; + reader.setDefaultDataOrigin(o2::header::gDataOriginEMC); + reader.setDefaultDataDescription(o2::header::gDataDescriptionRawData); + reader.setDefaultReadoutCardType(o2::raw::RawFileReader::RORC); + reader.addFile(rawfilename); + reader.init(); + + while (1) { + int tfID = reader.getNextTFToRead(); + if (tfID >= reader.getNTimeFrames()) { + LOG(INFO) << "nothing left to read after " << tfID << " TFs read"; + break; + } + std::vector<char> dataBuffer; // where to put extracted data + for (int il = 0; il < reader.getNLinks(); il++) { + auto& link = reader.getLink(il); + std::cout << "Decoding link " << il << std::endl; + + auto sz = link.getNextTFSize(); // size in bytes needed for the next TF of this link + dataBuffer.resize(sz); + link.readNextTF(dataBuffer.data()); + + // Parse + o2::emcal::RawReaderMemory parser(dataBuffer); + while (parser.hasNext()) { + parser.next(); + // Exclude STU DDLs + if (o2::raw::RDHUtils::getFEEID(parser.getRawHeader()) >= 40) { + continue; + } + o2::emcal::AltroDecoder decoder(parser); + decoder.decode(); + + std::cout << decoder.getRCUTrailer() << std::endl; + for (auto& chan : decoder.getChannels()) { + std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; + for (auto& bunch : chan.getBunches()) { + std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; + auto adcs = bunch.getADC(); + int time = bunch.getStartTime(); + for (int i = adcs.size() - 1; i >= 0; i--) { + std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; + time--; + } } } } } - }; - - auto rawfilename = vm["input-file"].as<std::string>(); - auto rawHeaderVersion = vm["rdh"].as<int>(); - if (rawHeaderVersion == 4) { - decode(o2::emcal::RawReaderFileRDHv4(rawfilename)); - } else { - decode(o2::emcal::RawReaderFileRDHvE(rawfilename)); + reader.setNextTFToRead(++tfID); } - return 0; -} +} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx b/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx index 6c42966a68640..6d91dca6f21e5 100644 --- a/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx +++ b/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx @@ -8,36 +8,28 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. #include <cstring> -#include <iomanip> -#include <iostream> #include <boost/format.hpp> #include "InfoLogger/InfoLogger.hxx" -#include "Headers/RAWDataHeader.h" #include "EMCALReconstruction/AltroDecoder.h" -#include "EMCALReconstruction/RAWDataHeader.h" -#include "EMCALReconstruction/RawReaderFile.h" #include "EMCALReconstruction/RawReaderMemory.h" using namespace o2::emcal; -template <class RawReader> -AltroDecoder<RawReader>::AltroDecoder(RawReader& reader) : mRawReader(reader), - mRCUTrailer(), - mChannels(), - mChannelsInitialized(false) +AltroDecoder::AltroDecoder(RawReaderMemory& reader) : mRawReader(reader), + mRCUTrailer(), + mChannels(), + mChannelsInitialized(false) { } -template <class RawReader> -void AltroDecoder<RawReader>::decode() +void AltroDecoder::decode() { readRCUTrailer(); checkRCUTrailer(); readChannels(); } -template <class RawReader> -void AltroDecoder<RawReader>::readRCUTrailer() +void AltroDecoder::readRCUTrailer() { try { auto payloadwordsOrig = mRawReader.getPayload().getPayloadWords(); @@ -50,19 +42,16 @@ void AltroDecoder<RawReader>::readRCUTrailer() } } -template <class RawReader> -void AltroDecoder<RawReader>::checkRCUTrailer() +void AltroDecoder::checkRCUTrailer() { } -template <class RawReader> -void AltroDecoder<RawReader>::readChannels() +void AltroDecoder::readChannels() { mChannelsInitialized = false; mChannels.clear(); int currentpos = 0; auto& buffer = mRawReader.getPayload().getPayloadWords(); - std::array<uint16_t, 1024> bunchwords; while (currentpos < buffer.size() - mRCUTrailer.getTrailerSize()) { auto currentword = buffer[currentpos++]; if (currentword >> 30 != 1) { @@ -74,8 +63,8 @@ void AltroDecoder<RawReader>::readChannels() currentchannel.setBadChannel((currentword >> 29) & 0x1); /// decode all words for channel - int numberofsamples = 0, - numberofwords = (currentchannel.getPayloadSize() + 2) / 3; + int numberofwords = (currentchannel.getPayloadSize() + 2) / 3; + std::vector<uint16_t> bunchwords; for (int iword = 0; iword < numberofwords; iword++) { currentword = buffer[currentpos++]; if ((currentword >> 30) != 0) { @@ -85,41 +74,36 @@ void AltroDecoder<RawReader>::readChannels() currentpos--; continue; } - bunchwords[numberofsamples++] = (currentword >> 20) & 0x3FF; - bunchwords[numberofsamples++] = (currentword >> 10) & 0x3FF; - bunchwords[numberofsamples++] = currentword & 0x3FF; + bunchwords.push_back((currentword >> 20) & 0x3FF); + bunchwords.push_back((currentword >> 10) & 0x3FF); + bunchwords.push_back(currentword & 0x3FF); } // decode bunches int currentsample = 0; - while (currentsample < currentchannel.getPayloadSize()) { + while (currentsample < currentchannel.getPayloadSize() && bunchwords.size() > currentsample + 2) { int bunchlength = bunchwords[currentsample] - 2, // remove words for bunchlength and starttime starttime = bunchwords[currentsample + 1]; auto& currentbunch = currentchannel.createBunch(bunchlength, starttime); - currentbunch.initFromRange(gsl::span<uint16_t>(&bunchwords[currentsample + 2], std::min(bunchlength, numberofsamples - currentsample - 2))); + currentbunch.initFromRange(gsl::span<uint16_t>(&bunchwords[currentsample + 2], std::min((unsigned long)bunchlength, bunchwords.size() - currentsample - 2))); currentsample += bunchlength + 2; } } mChannelsInitialized = true; } -template <class RawReader> -const RCUTrailer& AltroDecoder<RawReader>::getRCUTrailer() const +const RCUTrailer& AltroDecoder::getRCUTrailer() const { - if (!mRCUTrailer.isInitialized()) + if (!mRCUTrailer.isInitialized()) { throw AltroDecoderError(AltroDecoderError::ErrorType_t::RCU_TRAILER_ERROR, "RCU trailer was not initialized"); + } return mRCUTrailer; } -template <class RawReader> -const std::vector<Channel>& AltroDecoder<RawReader>::getChannels() const +const std::vector<Channel>& AltroDecoder::getChannels() const { - if (!mChannelsInitialized) + if (!mChannelsInitialized) { throw AltroDecoderError(AltroDecoderError::ErrorType_t::CHANNEL_ERROR, "Channels not initizalized"); + } return mChannels; -} - -template class o2::emcal::AltroDecoder<o2::emcal::RawReaderFile<o2::emcal::RAWDataHeader>>; -template class o2::emcal::AltroDecoder<o2::emcal::RawReaderFile<o2::header::RAWDataHeaderV4>>; -template class o2::emcal::AltroDecoder<o2::emcal::RawReaderMemory<o2::emcal::RAWDataHeader>>; -template class o2::emcal::AltroDecoder<o2::emcal::RawReaderMemory<o2::header::RAWDataHeaderV4>>; \ No newline at end of file +} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/Bunch.cxx b/Detectors/EMCAL/reconstruction/src/Bunch.cxx index 1211a1852409c..fea2cd3da982c 100644 --- a/Detectors/EMCAL/reconstruction/src/Bunch.cxx +++ b/Detectors/EMCAL/reconstruction/src/Bunch.cxx @@ -13,6 +13,7 @@ using namespace o2::emcal; void Bunch::initFromRange(gsl::span<uint16_t> adcs) { - for (auto adcval : adcs) + for (auto adcval : adcs) { mADC.emplace_back(adcval); + } } \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx b/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..3fc31aaea0a7a --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of EMCAL data + +#include "EMCALReconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::emcal; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, std::vector<TriggerRecord>& trigVec, std::vector<Cell>& cellVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, trigVec, cellVec); +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + // just to get types + uint16_t bcInc = 0, entries = 0, cellTime = 0, energy = 0, tower = 0; + uint32_t orbitInc = 0; + uint8_t status = 0; +#define MAKECODER(part, slot) createCoder<decltype(part)>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(bcInc, CTF::BLC_bcIncTrig); + MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); + MAKECODER(entries, CTF::BLC_entriesTrig); + MAKECODER(tower, CTF::BLC_towerID); + MAKECODER(cellTime, CTF::BLC_time); + MAKECODER(energy, CTF::BLC_energy); + MAKECODER(status, CTF::BLC_status); + // clang-format on +} diff --git a/Detectors/EMCAL/reconstruction/src/CTFHelper.cxx b/Detectors/EMCAL/reconstruction/src/CTFHelper.cxx new file mode 100644 index 0000000000000..1e0f385b72fce --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/CTFHelper.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for EMCAL CTF creation + +#include "EMCALReconstruction/CTFHelper.h" diff --git a/Detectors/EMCAL/reconstruction/src/CaloRawFitter.cxx b/Detectors/EMCAL/reconstruction/src/CaloRawFitter.cxx index 6748b6246ed81..7e14fe7b56eab 100644 --- a/Detectors/EMCAL/reconstruction/src/CaloRawFitter.cxx +++ b/Detectors/EMCAL/reconstruction/src/CaloRawFitter.cxx @@ -90,10 +90,12 @@ std::tuple<int, int> CaloRawFitter::selectSubarray(const gsl::span<double> data, // we keep one pre- or post- sample if we can (as in online) // check though if we ended up on a 'jump', or out of bounds: if so, back up - if (firstJump || tmpfirst < 0) + if (firstJump || tmpfirst < 0) { tmpfirst++; - if (lastJump || tmplast >= data.size()) + } + if (lastJump || tmplast >= data.size()) { tmplast--; + } first = tmpfirst; last = tmplast; @@ -266,8 +268,9 @@ std::tuple<int, int, float, short, short, float, int, int> CaloRawFitter::preFit for (int i = 0; i < length; i++) { mReversed[i] = sig[length - i - 1] - ped; - if (maxf < mReversed[i]) + if (maxf < mReversed[i]) { maxf = mReversed[i]; + } } if (maxf >= acut) // possibly significant signal diff --git a/Detectors/EMCAL/reconstruction/src/CaloRawFitterGamma2.cxx b/Detectors/EMCAL/reconstruction/src/CaloRawFitterGamma2.cxx new file mode 100755 index 0000000000000..9fce37c26a2a6 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/CaloRawFitterGamma2.cxx @@ -0,0 +1,179 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CaloRawFitterGamma2.cxx +/// \author Martin Poghosyan (Martin.Poghosyan@cern.ch) + +#include "FairLogger.h" +#include <random> + +// ROOT sytem +#include "TMath.h" + +#include "EMCALReconstruction/Bunch.h" +#include "EMCALReconstruction/CaloFitResults.h" +#include "DataFormatsEMCAL/Constants.h" + +#include "EMCALReconstruction/CaloRawFitterGamma2.h" + +using namespace o2::emcal; + +CaloRawFitterGamma2::CaloRawFitterGamma2() : CaloRawFitter("Chi Square ( Gamma2 )", "Gamma2") +{ + mAlgo = FitAlgorithm::Gamma2; +} + +CaloFitResults CaloRawFitterGamma2::evaluate(const std::vector<Bunch>& bunchlist, + std::optional<unsigned int> altrocfg1, std::optional<unsigned int> altrocfg2) +{ + float time = 0; + float amp = 0; + float chi2 = 0; + int ndf = 0; + bool fitDone = false; + + auto [nsamples, bunchIndex, ampEstimate, + maxADC, timeEstimate, pedEstimate, first, last] = preFitEvaluateSamples(bunchlist, altrocfg1, altrocfg2, mAmpCut); + + if (ampEstimate >= mAmpCut) { + time = timeEstimate; + int timebinOffset = bunchlist.at(bunchIndex).getStartTime() - (bunchlist.at(bunchIndex).getBunchLength() - 1); + amp = ampEstimate; + + if (nsamples > 2 && maxADC < constants::OVERFLOWCUT) { + std::tie(amp, time) = doParabolaFit(timeEstimate - 1); + mNiter = 0; + std::tie(chi2, fitDone) = doFit_1peak(first, nsamples, amp, time); + + if (!fitDone) { + amp = ampEstimate; + time = timeEstimate; + chi2 = 1.e9; + } + + time += timebinOffset; + timeEstimate += timebinOffset; + ndf = nsamples - 2; + } + } + + if (fitDone) { + float ampAsymm = (amp - ampEstimate) / (amp + ampEstimate); + float timeDiff = time - timeEstimate; + + if ((TMath::Abs(ampAsymm) > 0.1) || (TMath::Abs(timeDiff) > 2)) { + amp = ampEstimate; + time = timeEstimate; + fitDone = false; + } + } + if (amp >= mAmpCut) { + if (!fitDone) { + std::default_random_engine generator; + std::uniform_real_distribution<float> distribution(0.0, 1.0); + amp += (0.5 - distribution(generator)); + } + time = time * constants::EMCAL_TIMESAMPLE; + time -= mL1Phase; + + return CaloFitResults(maxADC, pedEstimate, mAlgo, amp, time, (int)time, chi2, ndf); + } + return CaloFitResults(-1, -1); +} + +std::tuple<float, bool> CaloRawFitterGamma2::doFit_1peak(int firstTimeBin, int nSamples, float& ampl, float& time) +{ + + float chi2(0.); + + // fit using gamma-2 function (ORDER =2 assumed) + if (nSamples < 3) { + return std::make_tuple(chi2, false); + } + if (mNiter > mNiterationsMax) { + return std::make_tuple(chi2, false); + } + + double D, dA, dt; + double c11 = 0; + double c12 = 0; + double c21 = 0; + double c22 = 0; + double d1 = 0; + double d2 = 0; + + mNiter++; + + for (int itbin = 0; itbin < nSamples; itbin++) { + + double ti = (itbin - time) / constants::TAU; + if ((ti + 1) < 0) { + continue; + } + + double g_1i = (ti + 1) * TMath::Exp(-2 * ti); + double g_i = (ti + 1) * g_1i; + double gp_i = 2 * (g_i - g_1i); + double q1_i = (2 * ti + 1) * TMath::Exp(-2 * ti); + double q2_i = g_1i * g_1i * (4 * ti + 1); + c11 += (getReversed(itbin) - ampl * 2 * g_i) * gp_i; + c12 += g_i * g_i; + c21 += getReversed(itbin) * q1_i - ampl * q2_i; + c22 += g_i * g_1i; + double delta = ampl * g_i - getReversed(itbin); + d1 += delta * g_i; + d2 += delta * g_1i; + chi2 += (delta * delta); + } + + D = c11 * c22 - c12 * c21; + + if (TMath::Abs(D) < DBL_EPSILON) { + return std::make_tuple(chi2, false); + } + + dt = (d1 * c22 - d2 * c12) / D * constants::TAU; + dA = (d1 * c21 - d2 * c11) / D; + + time += dt; + ampl += dA; + + bool res = true; + + if (TMath::Abs(dA) > 1 || TMath::Abs(dt) > 0.01) { + std::tie(chi2, res) = doFit_1peak(firstTimeBin, nSamples, ampl, time); + } + + return std::make_tuple(chi2, res); +} + +std::tuple<float, float> CaloRawFitterGamma2::doParabolaFit(int maxTimeBin) const +{ + float amp(0.), time(0.); + + // The equation of parabola is "y = a*x^2 + b*x + c" + // We have to find "a", "b", and "c" + + double a = (getReversed(maxTimeBin + 2) + getReversed(maxTimeBin) - 2. * getReversed(maxTimeBin + 1)) / 2.; + + if (TMath::Abs(a) < 1.e09) { + amp = getReversed(maxTimeBin + 1); + time = maxTimeBin + 1; + return std::make_tuple(amp, time); + } + + double b = getReversed(maxTimeBin + 1) - getReversed(maxTimeBin) - a * (2. * maxTimeBin + 1); + double c = getReversed(maxTimeBin) - b * maxTimeBin - a * maxTimeBin * maxTimeBin; + + time = -b / 2. / a; + amp = a * time * time + b * time + c; + + return std::make_tuple(amp, time); +} diff --git a/Detectors/EMCAL/reconstruction/src/CaloRawFitterStandard.cxx b/Detectors/EMCAL/reconstruction/src/CaloRawFitterStandard.cxx index 8523ad353a87b..115d6b64e515b 100644 --- a/Detectors/EMCAL/reconstruction/src/CaloRawFitterStandard.cxx +++ b/Detectors/EMCAL/reconstruction/src/CaloRawFitterStandard.cxx @@ -40,10 +40,11 @@ double CaloRawFitterStandard::rawResponseFunction(double* x, double* par) double ped = par[4]; double xx = (x[0] - par[1] + tau) / tau; - if (xx <= 0) + if (xx <= 0) { signal = ped; - else + } else { signal = ped + par[0] * TMath::Power(xx, n) * TMath::Exp(n * (1 - xx)); + } return signal; } @@ -61,7 +62,7 @@ CaloFitResults CaloRawFitterStandard::evaluate(const std::vector<Bunch>& bunchli auto [nsamples, bunchIndex, ampEstimate, maxADC, timeEstimate, pedEstimate, first, last] = preFitEvaluateSamples(bunchlist, altrocfg1, altrocfg2, mAmpCut); - if (ampEstimate >= mAmpCut) { + if (bunchIndex >= 0 && ampEstimate >= mAmpCut) { time = timeEstimate; int timebinOffset = bunchlist.at(bunchIndex).getStartTime() - (bunchlist.at(bunchIndex).getBunchLength() - 1); amp = ampEstimate; @@ -105,8 +106,9 @@ std::tuple<float, float, float, bool> CaloRawFitterStandard::fitRaw(int firstTim int nsamples = lastTimeBin - firstTimeBin + 1; fitDone = kFALSE; - if (nsamples < 3) + if (nsamples < 3) { return std::make_tuple(amp, time, chi2, fitDone); + } TGraph gSig(nsamples); @@ -133,8 +135,9 @@ std::tuple<float, float, float, bool> CaloRawFitterStandard::fitRaw(int firstTim time = signalF.GetParameter(1); chi2 = signalF.GetChisquare(); fitDone = kTRUE; - } else + } else { fitDone = kFALSE; + } return std::make_tuple(amp, time, chi2, fitDone); } diff --git a/Detectors/EMCAL/reconstruction/src/Channel.cxx b/Detectors/EMCAL/reconstruction/src/Channel.cxx index 616067bc79558..bdd79ee9d39b5 100644 --- a/Detectors/EMCAL/reconstruction/src/Channel.cxx +++ b/Detectors/EMCAL/reconstruction/src/Channel.cxx @@ -13,29 +13,33 @@ using namespace o2::emcal; int Channel::getBranchIndex() const { - if (mHardwareAddress == -1) + if (mHardwareAddress == -1) { throw HardwareAddressError(); + } return ((mHardwareAddress >> 11) & 0x1); } int Channel::getFECIndex() const { - if (mHardwareAddress == -1) + if (mHardwareAddress == -1) { throw HardwareAddressError(); + } return ((mHardwareAddress >> 7) & 0xF); } Int_t Channel::getAltroIndex() const { - if (mHardwareAddress == -1) + if (mHardwareAddress == -1) { throw HardwareAddressError(); + } return ((mHardwareAddress >> 4) & 0x7); } Int_t Channel::getChannelIndex() const { - if (mHardwareAddress == -1) + if (mHardwareAddress == -1) { throw HardwareAddressError(); + } return (mHardwareAddress & 0xF); } diff --git a/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx b/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx index 9adcc506dbd26..3a4d33d10555d 100644 --- a/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx +++ b/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx @@ -124,8 +124,7 @@ void Clusterizer<InputType>::getTopologicalRowColumn(const InputType& input, int template <class InputType> void Clusterizer<InputType>::findClusters(const gsl::span<InputType const>& inputArray) { - mFoundClusters.clear(); - mInputIndices.clear(); + clear(); // Algorithm // - Fill cells/digits in 2D topological map diff --git a/Detectors/EMCAL/reconstruction/src/ClusterizerTask.cxx b/Detectors/EMCAL/reconstruction/src/ClusterizerTask.cxx index ef63f5bca6bce..6b5e8e2c56d7e 100644 --- a/Detectors/EMCAL/reconstruction/src/ClusterizerTask.cxx +++ b/Detectors/EMCAL/reconstruction/src/ClusterizerTask.cxx @@ -83,9 +83,9 @@ void ClusterizerTask<InputType>::process(const std::string inputFileName, const // Create output tree std::unique_ptr<TTree> outTree = std::make_unique<TTree>("o2sim", "EMCAL clusters"); - outTree->Branch("EMCCluster", &mClustersArray); - outTree->Branch("EMCClusterInputIndex", &mClustersInputIndices); - outTree->Branch("EMCClusterTRGR", &mClusterTriggerRecordsClusters); + outTree->Branch("EMCALCluster", &mClustersArray); + outTree->Branch("EMCALClusterInputIndex", &mClustersInputIndices); + outTree->Branch("EMCALClusterTRGR", &mClusterTriggerRecordsClusters); outTree->Branch("EMCIndicesTRGR", &mClusterTriggerRecordsIndices); mClustersArray->clear(); diff --git a/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h b/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h index f52ae19d5d116..3267e83f6d89f 100644 --- a/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h +++ b/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h @@ -14,21 +14,15 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::emcal::RawReaderFile < o2::emcal::RAWDataHeader> + ; -#pragma link C++ class o2::emcal::RawReaderFile < o2::header::RAWDataHeaderV4> + ; -#pragma link C++ class o2::emcal::RawReaderMemory < o2::emcal::RAWDataHeader> + ; -#pragma link C++ class o2::emcal::RawReaderMemory < o2::header::RAWDataHeaderV4> + ; -#pragma link C++ class o2::emcal::AltroDecoder < o2::emcal::RawReaderFile < o2::emcal::RAWDataHeader>> + ; -#pragma link C++ class o2::emcal::AltroDecoder < o2::emcal::RawReaderFile < o2::header::RAWDataHeaderV4>> + ; -#pragma link C++ class o2::emcal::AltroDecoder < o2::emcal::RawReaderMemory < o2::emcal::RAWDataHeader>> + ; -#pragma link C++ class o2::emcal::AltroDecoder < o2::emcal::RawReaderMemory < o2::header::RAWDataHeaderV4>> + ; +#pragma link C++ class o2::emcal::RawReaderMemory + ; +#pragma link C++ class o2::emcal::AltroDecoder + ; #pragma link C++ class o2::emcal::RawPayload + ; #pragma link C++ class o2::emcal::Bunch + ; #pragma link C++ class o2::emcal::Channel + ; -#pragma link C++ class o2::emcal::RCUTrailer + ; #pragma link C++ class o2::emcal::CaloFitResults + ; #pragma link C++ class o2::emcal::CaloRawFitter + ; #pragma link C++ class o2::emcal::CaloRawFitterStandard + ; +#pragma link C++ class o2::emcal::CaloRawFitterGamma2 + ; //#pragma link C++ namespace o2::emcal+; #pragma link C++ class o2::emcal::ClusterizerParameters + ; diff --git a/Detectors/EMCAL/reconstruction/src/RAWDataHeader.cxx b/Detectors/EMCAL/reconstruction/src/RAWDataHeader.cxx deleted file mode 100644 index 3e4e3fab514c5..0000000000000 --- a/Detectors/EMCAL/reconstruction/src/RAWDataHeader.cxx +++ /dev/null @@ -1,66 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <bitset> -#include <iostream> -#include <iomanip> -#include "EMCALReconstruction/RAWDataHeader.h" - -using namespace o2::emcal; - -std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::RAWDataHeader& header) -{ - stream << "EMCAL CRORC RAW Header:\n" - << " Size (WORD0): " << header.word0 << "(" << std::hex << header.word0 << std::dec << ")\n" - << " WORD1: " << header.word1 << "(" << std::hex << header.word1 << std::dec << ")\n" - << " Version: " << int(header.version) << "\n" - << " L1 Trigger Message: " << int(header.triggermessageL1) << "\n" - << " EventID1: " << header.triggerBC << "\n" - << " Offset and size (WORD2): " << header.word2 << "(" << std::hex << header.word2 << std::dec << ")\n" - << " Offset: " << header.offsetToNext << "\n" - << " Size: " << header.memorySize << "\n" - << " Package counter and link ID (WORD3): (" << std::hex << header.word3 << std::dec << ")\n" - << " Packet counter: " << int(header.packetCounter) << "\n" - << " Link ID: " << int(header.linkID) << "\n" - << " Status and mini eventID: " << header.word4 << " (" << std::hex << header.word4 << std::dec << ")\n" - << " Status: " << header.status << "\n" - << " Mini EventID: " << header.triggerOrbit << "\n" - /* - << " Trigger Classes: ( " << std::hex << header.words5[0] << " " << header.words5[1] << " " << header.words5[2] << std::dec << ")\n" - << " First 50: " << std::bitset<sizeof(uint64_t)*8>(header.triggerType) << "\n" - << " Second 50: " << std::bitset<sizeof(uint64_t)*8>(header.triggerTypeNext50) << "\n" - << " ROI: (" << std::hex << header.words5[3] << " " << header.words5[4] << std::dec << ")\n" - << " ROI: " << std::bitset<sizeof(uint64_t)*8>(header.roi) << "\n" - */ - << "End Header" << std::endl; - return stream; -} - -std::istream& o2::emcal::operator>>(std::istream& stream, o2::emcal::RAWDataHeader& header) -{ - //std::cout << "called, 10 words" << std::endl; - uint32_t message[10]; - auto address = reinterpret_cast<char*>(message); - for (int i = 0; i < 10; i++) { - stream.read(address + i * sizeof(uint32_t) / sizeof(char), sizeof(message[i])); - //std::cout << "Word " << i << ": " << std::hex << message[i] << std::dec << std::endl; - } - header.word0 = message[0]; - header.word1 = message[1]; - header.word2 = message[2]; - header.word3 = message[3]; - header.word4 = message[4]; - header.word5 = message[5]; - header.word6 = message[6]; - header.word7 = message[7]; - header.word8 = message[8]; - header.word9 = message[9]; - return stream; -} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/RawBuffer.cxx b/Detectors/EMCAL/reconstruction/src/RawBuffer.cxx index 932ccd4464087..fd79bcfe088a4 100644 --- a/Detectors/EMCAL/reconstruction/src/RawBuffer.cxx +++ b/Detectors/EMCAL/reconstruction/src/RawBuffer.cxx @@ -47,26 +47,30 @@ void RawBuffer::readFromMemoryBuffer(const gsl::span<const char> rawmemory) flush(); auto address = reinterpret_cast<const uint32_t*>(rawmemory.data()); for (auto iword = 0; iword < rawmemory.size() / sizeof(uint32_t); iword++) { - if ((address[iword] & 0xFFF) == 0x082) { - // Termination word - // should normally not be decoded in case the payload size - // is determined correctly - break; - } + // Run2 code, probably not needed for run3 + // if ((address[iword] & 0xFFF) == 0x082) { + // Termination word + // should normally not be decoded in case the payload size + // is determined correctly + //std::cout << "Found termination word" << std::endl; + //break; + // } mDataWords[mNDataWords++] = address[iword]; } } uint32_t RawBuffer::getWord(int index) const { - if (index >= mNDataWords) + if (index >= mNDataWords) { throw std::runtime_error("Index out of range"); + } return mDataWords[index]; } uint32_t RawBuffer::getNextDataWord() { - if (!hasNext()) + if (!hasNext()) { throw std::runtime_error("No more data words in buffer"); + } return mDataWords[mCurrentDataWord++]; } diff --git a/Detectors/EMCAL/reconstruction/src/RawPayload.cxx b/Detectors/EMCAL/reconstruction/src/RawPayload.cxx index a6bd1801c2561..b28e49bf50bb2 100644 --- a/Detectors/EMCAL/reconstruction/src/RawPayload.cxx +++ b/Detectors/EMCAL/reconstruction/src/RawPayload.cxx @@ -15,14 +15,16 @@ using namespace o2::emcal; RawPayload::RawPayload(gsl::span<const uint32_t> payloadwords, int numpages) : mPayloadWords(payloadwords.size()), mNumberOfPages(numpages) { - for (auto word : payloadwords) + for (auto word : payloadwords) { mPayloadWords.emplace_back(word); + } } void RawPayload::appendPayloadWords(const gsl::span<const uint32_t> payloadwords) { - for (auto word : payloadwords) + for (auto word : payloadwords) { mPayloadWords.emplace_back(word); + } } void RawPayload::reset() diff --git a/Detectors/EMCAL/reconstruction/src/RawReaderFile.cxx b/Detectors/EMCAL/reconstruction/src/RawReaderFile.cxx deleted file mode 100644 index d2fd6fdf1573e..0000000000000 --- a/Detectors/EMCAL/reconstruction/src/RawReaderFile.cxx +++ /dev/null @@ -1,159 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <array> -#include <iostream> -#include <iomanip> - -#include "EMCALBase/RCUTrailer.h" -#include "EMCALReconstruction/RawHeaderStream.h" -#include "EMCALReconstruction/RawReaderFile.h" -#include "EMCALReconstruction/RawDecodingError.h" - -using namespace o2::emcal; - -#define CHECK_BIT(var, pos) ((var) & (1 << (pos))) - -bool isStop(const o2::emcal::RAWDataHeader& hdr) -{ - return true; -} -bool isStop(const o2::header::RAWDataHeaderV4& hdr) { return hdr.stop; } - -template <class RawHeader> -RawReaderFile<RawHeader>::RawReaderFile(const std::string_view filename) : mInputFileName(filename), - mDataFile(), - mRawHeader() -{ - init(); -} -template <class RawHeader> -RawReaderFile<RawHeader>::~RawReaderFile() -{ - mDataFile.close(); -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::init() -{ - mDataFile.open(mInputFileName, std::ifstream::binary); - if (!mDataFile.good()) - throw std::runtime_error("Unable to open or access file " + mInputFileName); - // get length of file in bytes - mDataFile.seekg(0, mDataFile.end); - mFileSize = mDataFile.tellg(); - mDataFile.seekg(0, mDataFile.beg); - // the file is supposed to contain N x 8kB packets. So the number of packets - // can be determined by the file-size. Ideally, this is not required but the - // information is derived directly from the header size and payload size. - // *** to be adapted to header info *** - mNumData = mFileSize / (8 * 1024); -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::next() -{ - mRawPayload.reset(); - bool isDataTerminated = false; - do { - nextPage(false); - // check if we find a valid RCU trailer - // the payload must be at the end of the buffer - // if not present and error will be thrown - try { - RCUTrailer::constructFromPayloadWords(mRawBuffer.getDataWords()); - isDataTerminated = true; - } catch (...) { - } - } while (!isDataTerminated); -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::nextPage(bool doResetPayload) -{ - if (mCurrentPosition >= mNumData) - throw RawDecodingError(RawDecodingError::ErrorType_t::PAGE_NOTFOUND); - if (doResetPayload) - mRawPayload.reset(); - auto start = mDataFile.tellg(); - readHeader(); - readPayload(); - mRawPayload.appendPayloadWords(mRawBuffer.getDataWords()); - mRawPayload.increasePageCount(); - mDataFile.seekg(int(start) + mRawHeader.offsetToNext); - mCurrentPosition++; -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::readPage(int page) -{ - mRawHeaderInitialized = false; - mPayloadInitialized = false; - if (page >= mNumData) - throw RawDecodingError(RawDecodingError::ErrorType_t::PAGE_NOTFOUND); - mDataFile.seekg(page * 8192); - auto start = mDataFile.tellg(); - readHeader(); - readPayload(); - mDataFile.seekg(int(start) + mRawHeader.offsetToNext); - mCurrentPosition = page; -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::readHeader() -{ - try { - // assume the seek is at the header position - mDataFile >> mRawHeader; - } catch (...) { - throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING); - } - mRawHeaderInitialized = true; -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::readPayload() -{ - try { - // assume the seek is at the payload position - mRawBuffer.readFromStream(mDataFile, mRawHeader.memorySize - sizeof(mRawHeader)); - } catch (...) { - throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_DECODING); - } - mPayloadInitialized = true; -} - -template <class RawHeader> -const RawHeader& RawReaderFile<RawHeader>::getRawHeader() const -{ - if (!mRawHeaderInitialized) - throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_INVALID); - return mRawHeader; -} - -template <class RawHeader> -const RawBuffer& RawReaderFile<RawHeader>::getRawBuffer() const -{ - if (!mPayloadInitialized) - throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_INVALID); - return mRawBuffer; -} - -template <class RawHeader> -void RawReaderFile<RawHeader>::readFile(const std::string_view filename) -{ - RawReaderFile reader(filename); - for (int ipage = 0; ipage < reader.getNumberOfPages(); ipage++) { - reader.nextPage(); - std::cout << reader.getRawHeader(); - } -} - -template class o2::emcal::RawReaderFile<o2::emcal::RAWDataHeader>; -template class o2::emcal::RawReaderFile<o2::header::RAWDataHeaderV4>; \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx b/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx index d596bc564cc32..6340cb30b9964 100644 --- a/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx +++ b/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx @@ -10,28 +10,39 @@ #include <sstream> #include <string> -#include "EMCALBase/RCUTrailer.h" -#include "EMCALReconstruction/RawHeaderStream.h" #include "EMCALReconstruction/RawReaderMemory.h" #include "EMCALReconstruction/RawDecodingError.h" +#include "DetectorsRaw/RDHUtils.h" using namespace o2::emcal; -template <class RawHeader> -RawReaderMemory<RawHeader>::RawReaderMemory(gsl::span<const char> rawmemory) : mRawMemoryBuffer(rawmemory) +using RDHDecoder = o2::raw::RDHUtils; + +RawReaderMemory::RawReaderMemory(gsl::span<const char> rawmemory) : mRawMemoryBuffer(rawmemory) { init(); } -template <class RawHeader> -void RawReaderMemory<RawHeader>::setRawMemory(const gsl::span<const char> rawmemory) +void RawReaderMemory::setRawMemory(const gsl::span<const char> rawmemory) { mRawMemoryBuffer = rawmemory; init(); } -template <class RawHeader> -void RawReaderMemory<RawHeader>::init() +o2::header::RDHAny RawReaderMemory::decodeRawHeader(const void* payloadwords) +{ + auto headerversion = RDHDecoder::getVersion(payloadwords); + if (headerversion == 4) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV4*>(payloadwords)); + } else if (headerversion == 5) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV5*>(payloadwords)); + } else if (headerversion == 6) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV6*>(payloadwords)); + } + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING); +} + +void RawReaderMemory::init() { mCurrentPosition = 0; mRawHeaderInitialized = false; @@ -40,95 +51,134 @@ void RawReaderMemory<RawHeader>::init() mNumData = mRawMemoryBuffer.size() / 8192; // assume fixed 8 kB pages } -template <class RawHeader> -void RawReaderMemory<RawHeader>::next() +void RawReaderMemory::next() { mRawPayload.reset(); + mCurrentTrailer.reset(); bool isDataTerminated = false; do { nextPage(false); - // check if we find a valid RCU trailer - // the payload must be at the end of the buffer - // if not present and error will be thrown - try { - RCUTrailer::constructFromPayloadWords(mRawBuffer.getDataWords()); + if (hasNext()) { + auto nextheader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + /** + * eventually in the future check continuing payload based on the bc/orbit ID + auto currentbc = RDHDecoder::getTriggerBC(mRawHeader), + nextbc = RDHDecoder::getTriggerBC(nextheader); + auto currentorbit = RDHDecoder::getTriggerOrbit(mRawHeader), + nextorbit = RDHDecoder::getTriggerOrbit(nextheader); + **/ + auto nextpagecounter = RDHDecoder::getPageCounter(nextheader); + if (nextpagecounter == 0) { + isDataTerminated = true; + } else { + isDataTerminated = false; + } + } else { isDataTerminated = true; - } catch (...) { } + // Check if the data continues } while (!isDataTerminated); + // add combined trailer to payload + mRawPayload.appendPayloadWords(mCurrentTrailer.encode()); } -template <class RawHeader> -void RawReaderMemory<RawHeader>::nextPage(bool doResetPayload) +void RawReaderMemory::nextPage(bool doResetPayload) { - if (!hasNext()) + if (!hasNext()) { throw RawDecodingError(RawDecodingError::ErrorType_t::PAGE_NOTFOUND); - if (doResetPayload) + } + if (doResetPayload) { mRawPayload.reset(); + } mRawHeaderInitialized = false; mPayloadInitialized = false; - // Use std::string stream as byte stream - std::string headerwords(mRawMemoryBuffer.data() + mCurrentPosition, sizeof(RawHeader) / sizeof(char)); - std::stringstream headerstream(headerwords); // Read header try { - headerstream >> mRawHeader; + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + //RDHDecoder::printRDH(mRawHeader); + if (RDHDecoder::getOffsetToNext(mRawHeader) == RDHDecoder::getHeaderSize(mRawHeader)) { + // No Payload - jump to next rawheader + // This will eventually move, depending on whether for events without payload in the SRU we send the RCU trailer + mCurrentPosition += RDHDecoder::getHeaderSize(mRawHeader); + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + //RDHDecoder::printRDH(mRawHeader); + } mRawHeaderInitialized = true; } catch (...) { throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING); } - if (mCurrentPosition + sizeof(RawHeader) + mRawHeader.memorySize >= mRawMemoryBuffer.size()) { + if (mCurrentPosition + RDHDecoder::getMemorySize(mRawHeader) > mRawMemoryBuffer.size()) { // Payload incomplete throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_DECODING); } else { - mRawBuffer.readFromMemoryBuffer(gsl::span<const char>(mRawMemoryBuffer.data() + mCurrentPosition + sizeof(RawHeader), mRawHeader.memorySize)); - mRawPayload.appendPayloadWords(mRawBuffer.getDataWords()); + mRawBuffer.readFromMemoryBuffer(gsl::span<const char>(mRawMemoryBuffer.data() + mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader), RDHDecoder::getMemorySize(mRawHeader) - RDHDecoder::getHeaderSize(mRawHeader))); + + // Read off and chop trailer (if required) + // + // In case every page gets a trailer (intermediate format). The trailers from the single + // pages need to be removed. There will be a combined trailer which keeps the sum of the + // payloads for all trailers. This will be appended to the chopped payload. + // + // Trailer only at the last page (new format): Only last page gets trailer. The trailer is + // also chopped from the payload as it will be added later again. + auto lastword = *(mRawBuffer.getDataWords().rbegin()); + gsl::span<const uint32_t> payloadWithoutTrailer; + if (lastword >> 30 == 3) { + // lastword is a trailer word + // decode trailer and chop + auto trailer = RCUTrailer::constructFromPayloadWords(mRawBuffer.getDataWords()); + if (!mCurrentTrailer.isInitialized()) { + mCurrentTrailer = trailer; + } else { + mCurrentTrailer.setPayloadSize(mCurrentTrailer.getPayloadSize() + trailer.getPayloadSize()); + } + payloadWithoutTrailer = gsl::span<const uint32_t>(mRawBuffer.getDataWords().data(), mRawBuffer.getDataWords().size() - trailer.getTrailerSize()); + } else { + // Not a trailer word = copy page as it is + payloadWithoutTrailer = mRawBuffer.getDataWords(); // No trailer to be chopped + } + + mRawPayload.appendPayloadWords(payloadWithoutTrailer); mRawPayload.increasePageCount(); } - mCurrentPosition += mRawHeader.offsetToNext; /// Assume fixed 8 kB page size + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); /// Assume fixed 8 kB page size } -template <class RawHeader> -void RawReaderMemory<RawHeader>::readPage(int page) +void RawReaderMemory::readPage(int page) { int currentposition = 8192 * page; - if (currentposition >= mRawMemoryBuffer.size()) + if (currentposition >= mRawMemoryBuffer.size()) { throw RawDecodingError(RawDecodingError::ErrorType_t::PAGE_NOTFOUND); + } mRawHeaderInitialized = false; mPayloadInitialized = false; - // Use std::string stream as byte stream - std::string headerwords(mRawMemoryBuffer.data() + currentposition, sizeof(RawHeader) / sizeof(char)); - std::stringstream headerstream(headerwords); // Read header try { - headerstream >> mRawHeader; + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); mRawHeaderInitialized = true; } catch (...) { throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING); } - if (currentposition + sizeof(RawHeader) + mRawHeader.memorySize >= mRawMemoryBuffer.size()) { + if (currentposition + RDHDecoder::getHeaderSize(mRawHeader) + RDHDecoder::getMemorySize(mRawHeader) >= mRawMemoryBuffer.size()) { // Payload incomplete throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_DECODING); } else { - mRawBuffer.readFromMemoryBuffer(gsl::span<const char>(mRawMemoryBuffer.data() + currentposition + sizeof(RawHeader), mRawHeader.memorySize)); + mRawBuffer.readFromMemoryBuffer(gsl::span<const char>(mRawMemoryBuffer.data() + currentposition + RDHDecoder::getHeaderSize(mRawHeader), RDHDecoder::getMemorySize(mRawHeader))); } } -template <class RawHeader> -const RawHeader& RawReaderMemory<RawHeader>::getRawHeader() const +const o2::header::RDHAny& RawReaderMemory::getRawHeader() const { - if (!mRawHeaderInitialized) + if (!mRawHeaderInitialized) { throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_INVALID); + } return mRawHeader; } -template <class RawHeader> -const RawBuffer& RawReaderMemory<RawHeader>::getRawBuffer() const +const RawBuffer& RawReaderMemory::getRawBuffer() const { - if (!mPayloadInitialized) + if (!mPayloadInitialized) { throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_INVALID); + } return mRawBuffer; } - -template class o2::emcal::RawReaderMemory<o2::emcal::RAWDataHeader>; -template class o2::emcal::RawReaderMemory<o2::header::RAWDataHeaderV4>; \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/CMakeLists.txt b/Detectors/EMCAL/simulation/CMakeLists.txt index db2953b1a0f8b..6149eeda5bda5 100644 --- a/Detectors/EMCAL/simulation/CMakeLists.txt +++ b/Detectors/EMCAL/simulation/CMakeLists.txt @@ -11,7 +11,7 @@ o2_add_library(EMCALSimulation SOURCES src/Detector.cxx src/Digitizer.cxx src/DigitizerTask.cxx src/SpaceFrame.cxx src/SimParam.cxx src/LabeledDigit.cxx - src/RawWriter.cxx src/DMAOutputStream.cxx src/RawOutputPageHandler.cxx + src/RawWriter.cxx PUBLIC_LINK_LIBRARIES O2::EMCALBase O2::DetectorsBase O2::SimConfig O2::SimulationDataFormat O2::Headers O2::DetectorsRaw) o2_target_root_dictionary(EMCALSimulation @@ -19,9 +19,13 @@ o2_target_root_dictionary(EMCALSimulation include/EMCALSimulation/Digitizer.h include/EMCALSimulation/DigitizerTask.h include/EMCALSimulation/RawWriter.h - include/EMCALSimulation/DMAOutputStream.h include/EMCALSimulation/SpaceFrame.h include/EMCALSimulation/SimParam.h include/EMCALSimulation/LabeledDigit.h) +o2_add_executable(rawcreator + COMPONENT_NAME emcal + PUBLIC_LINK_LIBRARIES O2::EMCALSimulation + SOURCES src/RawCreator.cxx) + o2_data_file(COPY data DESTINATION Detectors/EMC/simulation) diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/DMAOutputStream.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/DMAOutputStream.h deleted file mode 100644 index 8bbc036732338..0000000000000 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/DMAOutputStream.h +++ /dev/null @@ -1,126 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_DMAOUTPUTSTREAM_H -#define ALICEO2_EMCAL_DMAOUTPUTSTREAM_H - -#include <exception> -#include <fstream> -#include <string> - -#include <gsl/span> - -#include "Rtypes.h" -#include "RStringView.h" - -#include "Headers/RAWDataHeader.h" - -namespace o2 -{ - -namespace emcal -{ - -/// \class DMAOutputStream -/// \brief Output stream of a payload to DMA raw files -/// \ingroup EMCALsimulation -/// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory -/// \since Nov 6, 2019 -/// -/// Stream of output payload with variable size to DMA pages in -/// a binary raw file. The output payload can be larger than the -/// size of a DMA page (default: 8 kB) the output is split into -/// multiple pages. Page counter, memory size, page size and -/// stop bit are handled internally and are overwritten in the -/// raw data header provided. All other header information must be -/// handled externally. -class DMAOutputStream -{ - public: - using RawHeader = o2::header::RAWDataHeaderV4; - - class OutputFileException : public std::exception - { - public: - OutputFileException() = default; - OutputFileException(const std::string_view filename) : std::exception(), mFilePath(filename), mMessage("Path \"" + mFilePath + "\" invalid") {} - ~OutputFileException() noexcept final = default; - - const char* what() const noexcept final - { - return mMessage.data(); - } - - private: - std::string mFilePath = ""; - std::string mMessage = ""; - }; - - /// \brief Constructor - DMAOutputStream() = default; - - /// \brief Constructor - /// \param filename Name of the output file - DMAOutputStream(const char* filename); - - /// \brief Destructor - /// - /// Closing file I/O - ~DMAOutputStream(); - - /// \brief Open the output stream - /// \throw OutputFileException - /// - /// Opening output file I/O - void open(); - - /// \brief Set the name of the output file - /// \param filename Name of the output file - void setOutputFilename(const char* filename) { mFilename = filename; } - - /// \brief Write output payload to the output stream - /// \param header Raw data header - /// \param buffer Raw data payload - /// \return Current page count - /// - /// Converting output payload to DMA papges. If the payload is larger than - /// the pagesize - header size the payload is automatically split to multiple - /// pages. Page counter, memory size, page size and stop bit of the header are - /// handled internally and are overwritten from the header provided. All other - /// header information must be provided externally. - int writeData(RawHeader header, gsl::span<const char> buffer); - - /// \brief Write single raw data header - /// \param header raw data header - /// - /// Write header with no payload assinged. Function is used for writing - /// open/close header of the timeframe - void writeSingleHeader(const RawHeader& header); - - protected: - /// \brief Write DMA page - /// \param header Raw data header for page - /// \param payload Page payload (includes size of teh payload) - /// \param pagesize Size of the DMA page (not size of the payload) - /// - /// Expects that the size of the payload is smaller than the size ot the DMA - /// page. Parameter pagesize supporting variable page size. - void writeDMAPage(const RawHeader& header, gsl::span<const char> payload, int pagesize); - - private: - std::string mFilename = ""; ///< Name of the output file - std::ofstream mOutputFile; ///< Handler for output raw file - bool mInitialized = false; ///< Switch for whether the output stream is initialized - - ClassDefNV(DMAOutputStream, 1); -}; -} // namespace emcal -} // namespace o2 - -#endif \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/Detector.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/Detector.h index ff02d1f024d57..a7fb3649ac572 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/Detector.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/Detector.h @@ -14,7 +14,7 @@ #include "DetectorsBase/Detector.h" #include "EMCALBase/Hit.h" #include "EMCALBase/GeometryBase.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "RStringView.h" #include "Rtypes.h" #include <vector> @@ -88,7 +88,7 @@ class Detector : public o2::base::DetImpl<Detector> /// /// Internally adding hits coming from the same track Hit* AddHit(Int_t trackID, Int_t primary, Double_t initialEnergy, Int_t detID, - const Point3D<float>& pos, const Vector3D<float>& mom, Double_t time, Double_t energyloss); + const math_utils::Point3D<float>& pos, const math_utils::Vector3D<float>& mom, Double_t time, Double_t energyloss); Parent* AddSuperparent(Int_t trackID, Int_t pdg, Double_t energy); diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawOutputPageHandler.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawOutputPageHandler.h deleted file mode 100644 index 7417bfec6380e..0000000000000 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawOutputPageHandler.h +++ /dev/null @@ -1,162 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <exception> -#include <map> -#include <string> -#include <vector> -#include <CommonDataFormat/InteractionRecord.h> -#include <EMCALSimulation/DMAOutputStream.h> - -namespace o2 -{ -namespace emcal -{ - -/// \class RawOutputPageHandler -/// \brief Handler for EMCAL raw page buffering, timeframe building and output streaming -/// \ingroup EMCALsimulation -/// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory -/// \since March 22, 2020 -/// -/// # General -/// The raw format for EMCAL consists of separate superpages of timeframes arranged per -/// (DDL) link. Each timeframe consists of -/// - Empty RDH without payload indicating start of timeframe. Counters are reset -/// - Pages with RDH and payload for each trigger BC within the timeframe -/// - Empty RDH without payload indicating end of timeframe. Contains the number of pages -/// in the timeframe, the stop bit and the timeframe trigger. -/// At the start of data an empty timeframe (without pages) consisting only of star and stop -/// RDH will mark the beginning of the data stream. -/// -/// # Usage -/// For each new trigger the function initTrigger is to be called with the InteractionRecord -/// of the bunch crossing corresponding to the trigger. Pages created by the raw writer and added -/// via addPageForLink are buffered per link. The HBFUtils, provided from outside, decide on whether -/// a new timeframe is triggered. In case of a new timeframe raw timeframes are created for all -/// links with pages, containing all pages of a link and streamed to file (in initTrigger). In case -/// the buffer still contains pages when the destructor is called, the pages are streamed as remaining -/// pages of the last timeframe (in the destructor). -class RawOutputPageHandler -{ - public: - using RawHeader = o2::header::RAWDataHeaderV4; - - /// \class LinkIDException - /// \brief Exception handling invalid link IDs (outside the range of EMCAL links) - class LinkIDException : public std::exception - { - public: - /// \brief Constructor, defining - LinkIDException(int linkID); - - /// \brief Destructor - ~LinkIDException() noexcept final = default; - - /// \brief Access to error message of the exception - /// \return Error message of the exception - const char* what() const noexcept final { return mMessage.data(); } - - /// \brief Access to Link ID raising the exception - /// \return Link ID raising the exception - int getLinkID() const { return mLinkID; } - - private: - int mLinkID; ///< ID of the link raising the exception - std::string mMessage; ///< Error message; - }; - - /// \class RawPageBuffer - /// \brief Buffer for raw pages - /// - /// Internal helper class for buffering raw pages within a timeframe for a certain link. - /// The functionality consists of: - /// - add page: adding new page to the raw buffer - /// - getPages: access to all pages. Mainly used when building the timeframe - /// - flush: clear buffer (after timeframe building) - class RawPageBuffer - { - public: - /// \struct PageData - /// \brief Structure for a raw page (header and payload words) - struct PageData { - RawHeader mHeader; ///< Header template of the page, containing link ID, fee ID and trigger - std::vector<char> mPage; ///< Payload page - }; - - /// \brief Constructor - RawPageBuffer() = default; - - /// \brief Destructor - ~RawPageBuffer() = default; - - /// \brief Adding new raw page to the page buffer - /// \brief header Template raw header, containing link/fee ID and trigger - /// \brief page Raw page (as char words) to be added to the buffer - void addPage(const RawHeader& header, const std::vector<char>& page) { mPages.push_back({header, page}); } - - /// \brief Cleaning page buffer - void flush() { mPages.clear(); } - - /// \brief Access to pages in the buffer - /// \return vector with all pages in the buffer - const std::vector<PageData>& getPages() const { return mPages; } - - private: - std::vector<PageData> mPages; ///< Buffer for pages - }; - - /// \brief Constructor, initializing page handler with output filename and HBF utils - /// \param rawfilename Name of the output file - RawOutputPageHandler(const char* rawfilename); - - /// \brief Destructor - /// - /// In case buffers contain payload the page is streamed as last - /// timeframe for the links containing payload pages. - ~RawOutputPageHandler(); - - /// \brief Initialize new trigger - /// \param currentIR Interaction record of the collision triggering - /// - /// Initialize new trigger. In case the Timeframe changes with the trigger - /// the buffers belonging to the previous buffer are streamed to file. For - /// each link a separate timeframe is created, starting with empty open/close - /// raw data header. Timeframes are only created for links which buffer pages. - void initTrigger(const o2::InteractionRecord& currentIR); - - /// \brief Add new page for link to the page buffer - /// \param linkID ID of the link - /// \param header Template raw header of the page - /// \param dmapage Payload page - /// \throw LinkIDException in case link ID is invalid - void addPageForLink(int linkID, const RawHeader& header, const std::vector<char>& dmapage); - - private: - /// \brief Write timeframe for an entire link - /// \param linkID ID of the link - /// \param pagebuffer Buffer with raw pages in timeframe belonging to the link - /// - /// Timeframes for raw data in EMCAL contain: - /// - Empty RDH, indicating start of timeframe. No payload assigned. Counters 0 - /// - Raw page for every trigger in the timeframe: Each raw page starts with a - /// RDH. In case the payload exceeds the 8 kB page it is split into multiple - /// pages. The page counter is calculated with respect to the first header in a - /// timeframe - /// - Empty RDH, closing the timeframe, containing the number of pages in the - /// timeframe - void writeTimeframe(int linkID, const RawPageBuffer& pagebuffer); - - int mCurrentTimeframe = -1; ///< Current timeframe ID (needed to detect whether a new timeframe starts) - std::map<int, RawPageBuffer> mRawPages; ///< Buffer with raw pages for all links - DMAOutputStream mOutputStream; ///< File output stream -}; -} // namespace emcal -} // namespace o2 \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h index dd6427c56ea38..974c749f7ab7a 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h @@ -11,6 +11,8 @@ #ifndef ALICEO2_EMCAL_RAWWRITER_H #define ALICEO2_EMCAL_RAWWRITER_H +#include <gsl/span> + #include <array> #include <fstream> #include <memory> @@ -20,9 +22,8 @@ #include "Rtypes.h" +#include "DetectorsRaw/RawFileWriter.h" #include "EMCALBase/Mapper.h" -#include "EMCALSimulation/DMAOutputStream.h" -#include "EMCALSimulation/RawOutputPageHandler.h" #include "DataFormatsEMCAL/Digit.h" #include "DataFormatsEMCAL/TriggerRecord.h" @@ -57,7 +58,7 @@ union ChannelHeader { uint32_t mPayloadSize : 10; ///< Bits 16 - 25: Payload size uint32_t mZero1 : 3; ///< Bits 26 - 28: zeroed uint32_t mBadChannel : 1; ///< Bit 29: Bad channel status - uint32_t mZero2 : 2; ///< Bits 30 - 31: zeroed + uint32_t mHeaderBits : 2; ///< Bits 30 - 31: channel header bits (1) }; }; @@ -74,42 +75,51 @@ union CaloBunchWord { class RawWriter { public: + enum class FileFor_t { + kFullDet, + kSubDet, + kLink + }; RawWriter() = default; - RawWriter(const char* rawfilename) { setRawFileName(rawfilename); } + RawWriter(const char* outputdir) { setOutputLocation(outputdir); } ~RawWriter() = default; - void setRawFileName(const char* filename) { mRawFilename = filename; } - void setDigits(std::vector<o2::emcal::Digit>* digits) { mDigits = digits; } - void setTriggerRecords(std::vector<o2::emcal::TriggerRecord>* triggers); + o2::raw::RawFileWriter& getWriter() const { return *mRawWriter; } + + void setOutputLocation(const char* outputdir) { mOutputLocation = outputdir; } + void setDigits(gsl::span<o2::emcal::Digit> digits) { mDigits = digits; } + void setFileFor(FileFor_t filefor) { mFileFor = filefor; } void setNumberOfADCSamples(int nsamples) { mNADCSamples = nsamples; } void setPedestal(int pedestal) { mPedestal = pedestal; } void setGeometry(o2::emcal::Geometry* geo) { mGeometry = geo; } - bool hasNextTrigger() const { return mCurrentTrigger != mTriggers->end(); } - void init(); - void process(); - void processNextTrigger(); + void digitsToRaw(gsl::span<o2::emcal::Digit> digits, gsl::span<o2::emcal::TriggerRecord> triggers); + bool processTrigger(const o2::emcal::TriggerRecord& trg); + + int carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data, + const char* ptr, int maxSize, int splitID, + std::vector<char>& trailer, std::vector<char>& header) const; protected: std::vector<AltroBunch> findBunches(const std::vector<o2::emcal::Digit*>& channelDigits); std::tuple<int, int, int> getOnlineID(int towerID); + std::tuple<int, int> getLinkAssignment(int ddlID); ChannelHeader createChannelHeader(int hardwareAddress, int payloadSize, bool isBadChannel); std::vector<char> createRCUTrailer(int payloadsize, int feca, int fecb, double timesample, double l1phase); std::vector<int> encodeBunchData(const std::vector<int>& data); private: - int mNADCSamples = 15; ///< Number of time samples - int mPedestal = 0; ///< Pedestal - o2::emcal::Geometry* mGeometry = nullptr; ///< EMCAL geometry - std::string mRawFilename; ///< Rawfile name - std::array<o2::emcal::Mapper, 4> mMappers; ///< EMCAL mappers - std::vector<o2::emcal::Digit>* mDigits; ///< Digits input vector - must be in digitized format including the time response - std::vector<o2::emcal::TriggerRecord>* mTriggers; ///< Trigger records, separating the data from different triggers - std::vector<SRUDigitContainer> mSRUdata; ///< Internal helper of digits assigned to SRUs - std::vector<o2::emcal::TriggerRecord>::iterator mCurrentTrigger; ///< Current trigger in the trigger records - std::unique_ptr<RawOutputPageHandler> mPageHandler; ///< Output page handler + int mNADCSamples = 15; ///< Number of time samples + int mPedestal = 0; ///< Pedestal + FileFor_t mFileFor = FileFor_t::kFullDet; ///< Granularity of the output files + o2::emcal::Geometry* mGeometry = nullptr; ///< EMCAL geometry + std::string mOutputLocation; ///< Rawfile name + std::unique_ptr<o2::emcal::MappingHandler> mMappingHandler; ///< Mapping handler + gsl::span<o2::emcal::Digit> mDigits; ///< Digits input vector - must be in digitized format including the time response + std::vector<SRUDigitContainer> mSRUdata; ///< Internal helper of digits assigned to SRUs + std::unique_ptr<o2::raw::RawFileWriter> mRawWriter; ///< Raw writer ClassDefNV(RawWriter, 1); }; diff --git a/Detectors/EMCAL/simulation/src/DMAOutputStream.cxx b/Detectors/EMCAL/simulation/src/DMAOutputStream.cxx deleted file mode 100644 index 6211b0a9f6755..0000000000000 --- a/Detectors/EMCAL/simulation/src/DMAOutputStream.cxx +++ /dev/null @@ -1,91 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "EMCALSimulation/DMAOutputStream.h" - -using namespace o2::emcal; - -DMAOutputStream::DMAOutputStream(const char* filename) : mFilename(filename) {} - -DMAOutputStream::~DMAOutputStream() -{ - if (mOutputFile.is_open()) - mOutputFile.close(); -} - -void DMAOutputStream::open() -{ - if (!mOutputFile.is_open()) { - if (!mFilename.length()) - throw OutputFileException(mFilename); - mOutputFile.open(mFilename, std::ios::out | std::ios::binary); - mInitialized = true; - } -} - -void DMAOutputStream::writeSingleHeader(const RawHeader& header) -{ - if (!mInitialized) - open(); - std::vector<char> outputpage(sizeof(RawHeader)); - RawHeader* outputheader = reinterpret_cast<RawHeader*>(outputpage.data()); - *outputheader = header; - outputheader->memorySize = sizeof(RawHeader); - outputheader->offsetToNext = sizeof(RawHeader); - mOutputFile.write(outputpage.data(), outputpage.size()); -} - -int DMAOutputStream::writeData(RawHeader header, gsl::span<const char> buffer) -{ - if (!mInitialized) - open(); - - constexpr int PAGESIZE = 8192; - // Handling of the termination word (0x001d3082): The termination word is added to the payload - // but not included in the payload size (as done in the hardware). Therefore it has to be subtracted - // from the maximum possible payload size - constexpr int MAXNWORDS = PAGESIZE - sizeof(header) - sizeof(uint32_t); - bool writeNext = true; - int pagecounter = header.pageCnt, currentindex = 0; - while (writeNext) { - int sizeRemain = buffer.size() - currentindex; - int nwordspage = MAXNWORDS; - if (sizeRemain < MAXNWORDS) { - // Last page - nwordspage = sizeRemain; - writeNext = false; - } - header.pageCnt = pagecounter; - header.memorySize = nwordspage + sizeof(RawHeader); - header.offsetToNext = 8192; - - writeDMAPage(header, gsl::span(buffer.data() + currentindex, nwordspage), PAGESIZE); - - if (writeNext) { - currentindex += nwordspage; - } - pagecounter++; - } - return pagecounter; -} - -void DMAOutputStream::writeDMAPage(const RawHeader& header, gsl::span<const char> payload, int pagesize) -{ - std::vector<char> dmapage(pagesize); - RawHeader* outheader = reinterpret_cast<RawHeader*>(dmapage.data()); - *outheader = header; - char* outpayload = dmapage.data() + sizeof(header); - memcpy(outpayload, payload.data(), payload.size()); - // Write termination character - uint32_t terminationCharacter = 0x001d3082; - char* terminationString = reinterpret_cast<char*>(&terminationCharacter); - memcpy(outpayload + payload.size(), terminationString, sizeof(uint32_t)); - mOutputFile.write(dmapage.data(), dmapage.size()); -} \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/src/Detector.cxx b/Detectors/EMCAL/simulation/src/Detector.cxx index 575a5c7670608..79c27568b7124 100644 --- a/Detectors/EMCAL/simulation/src/Detector.cxx +++ b/Detectors/EMCAL/simulation/src/Detector.cxx @@ -196,8 +196,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) LOG(DEBUG4) << "We are in sensitive volume " << v->GetName() << ": " << fMC->CurrentVolPath() << std::endl; // TODO Implement handling of parents and primary particle Double_t eloss = fMC->Edep(); - if (eloss < DBL_EPSILON) + if (eloss < DBL_EPSILON) { return false; // only process hits which actually deposit some energy in the EMCAL + } Geometry* geom = GetGeometry(); // Obtain detector ID // This is not equal to the volume ID of the fair volume @@ -264,7 +265,7 @@ Bool_t Detector::ProcessHits(FairVolume* v) smTypeID = 3; //one third (installed in 2012) supermodule } if (supermoduletype == DCAL_EXT) { - smTypeID = 3; //one third (installed in 2012) supermodule + smTypeID = 3; //one third (installed in 2012) supermodule } iphi = ((geom->GetCentersOfCellsPhiDir()).size() / smTypeID - 1) - iphi; // 23/7-iphi, revert the ordering on C side in order to keep convention. } @@ -302,8 +303,8 @@ Bool_t Detector::ProcessHits(FairVolume* v) LOG(DEBUG3) << "Adding new hit for parent " << mCurrentParentID << " and cell " << detID << std::endl; /// check handling of primary particles - AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, detID, Point3D<float>(posX, posY, posZ), - Vector3D<float>(momX, momY, momZ), time, lightyield); + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, detID, math_utils::Point3D<float>(posX, posY, posZ), + math_utils::Vector3D<float>(momX, momY, momZ), time, lightyield); o2stack->addHit(GetDetId()); } else { LOG(DEBUG3) << "Adding energy to the current hit" << std::endl; @@ -315,7 +316,7 @@ Bool_t Detector::ProcessHits(FairVolume* v) } Hit* Detector::AddHit(Int_t trackID, Int_t primary, Double_t initialEnergy, Int_t detID, - const Point3D<float>& pos, const Vector3D<float>& mom, Double_t time, Double_t eLoss) + const math_utils::Point3D<float>& pos, const math_utils::Vector3D<float>& mom, Double_t time, Double_t eLoss) { LOG(DEBUG3) << "Adding hit for track " << trackID << " with position (" << pos.X() << ", " << pos.Y() << ", " << pos.Z() << ") and momentum (" << mom.X() << ", " << mom.Y() << ", " << mom.Z() @@ -415,10 +416,11 @@ void Detector::CreateEmcalEnvelope() envelopA[2] = 20; CreateEMCALVolume(geom->GetNameOfEMCALEnvelope(), "BOX", ID_SC, envelopA, 3); - for (Int_t i = 0; i < 3; i++) + for (Int_t i = 0; i < 3; i++) { // Position the EMCAL Mother Volume (XEN1) in WSUC. // Look to AliEMCALWsuCosmicRaySetUp. TVirtualMC::GetMC()->Gspos(geom->GetNameOfEMCALEnvelope(), 1, "WSUC", 0.0, 0.0, +265., rotMatrixID, "ONLY"); + } } else { Double_t envelopA[10]; envelopA[0] = geom->GetArm1PhiMin(); // minimum phi angle @@ -508,8 +510,9 @@ void Detector::CreateShiskebabGeometry() Double_t parSCM0[5] = {0, 0, 0, 0}, *dummy = nullptr, parTRAP[11]; if (!contains(gn, "V1")) { Double_t wallThickness = g->GetPhiModuleSize() / g->GetNPHIdiv() - g->GetPhiTileSize(); - for (Int_t i = 0; i < 3; i++) + for (Int_t i = 0; i < 3; i++) { parSCM0[i] = mParEMOD[i] - wallThickness; + } parSCM0[3] = mParEMOD[3]; CreateEMCALVolume("SCM0", "TRD1", ID_AIR, parSCM0, 4); TVirtualMC::GetMC()->Gspos("SCM0", 1, "EMOD", 0., 0., 0., 0, "ONLY"); @@ -840,8 +843,9 @@ void Detector::CreateSupermoduleGeometry(const std::string_view mother) if (smodnum % 2 == 1) { phiy += 180.; - if (phiy >= 360.) + if (phiy >= 360.) { phiy -= 360.; + } phiz = 180.; zpos *= -1.; } @@ -921,8 +925,9 @@ void Detector::CreateEmcalModuleGeometry(const std::string_view mother, const st } else if (mother == "DCEXT") { iyMax /= 3; } else if (mother == "DCSM") { - if (iz < 8) + if (iz < 8) { continue; //!!!DCSM from 8th to 23th + } zpos = mod.GetPosZ() - mSmodPar2 - g->GetDCALInnerEdge() / 2.; } else if (mother.compare("SMOD")) { LOG(ERROR) << "Unknown super module Type!!"; diff --git a/Detectors/EMCAL/simulation/src/Digitizer.cxx b/Detectors/EMCAL/simulation/src/Digitizer.cxx index 39b548c52e75a..a5592ca60054f 100644 --- a/Detectors/EMCAL/simulation/src/Digitizer.cxx +++ b/Detectors/EMCAL/simulation/src/Digitizer.cxx @@ -14,7 +14,7 @@ #include "EMCALBase/Geometry.h" #include "EMCALBase/GeometryBase.h" #include "EMCALBase/Hit.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "SimulationDataFormat/MCCompLabel.h" #include <climits> @@ -121,8 +121,9 @@ void Digitizer::process(const std::vector<Hit>& hits) } MCLabel label(hit.GetTrackID(), mCurrEvID, mCurrSrcID, false, 1.0); - if (digit.getAmplitude() == 0) + if (digit.getAmplitude() == 0) { label.setAmplitudeFraction(0); + } LabeledDigit d(digit, label); mDigits[id].push_back(d); } @@ -235,12 +236,15 @@ void Digitizer::fillOutputContainer(std::vector<Digit>& digits, o2::dataformats: addNoiseDigits(ld1); } - if (mRemoveDigitsBelowThreshold && (ld1.getAmplitude() < mSimParam->getDigitThreshold() * (constants::EMCAL_ADCENERGY))) + if (mRemoveDigitsBelowThreshold && (ld1.getAmplitude() < mSimParam->getDigitThreshold() * (constants::EMCAL_ADCENERGY))) { continue; - if (ld1.getAmplitude() < 0) + } + if (ld1.getAmplitude() < 0) { continue; - if (ld1.getTimeStamp() >= mSimParam->getLiveTime()) + } + if (ld1.getTimeStamp() >= mSimParam->getLiveTime()) { continue; + } l.push_back(ld1); } diff --git a/Detectors/EMCAL/simulation/src/DigitizerTask.cxx b/Detectors/EMCAL/simulation/src/DigitizerTask.cxx index ae990751ea6bd..94b3e8a58e8eb 100644 --- a/Detectors/EMCAL/simulation/src/DigitizerTask.cxx +++ b/Detectors/EMCAL/simulation/src/DigitizerTask.cxx @@ -72,8 +72,9 @@ void DigitizerTask::Exec(Option_t* option) { FairRootManager* mgr = FairRootManager::Instance(); - if (mDigitsArray) + if (mDigitsArray) { mDigitsArray->clear(); + } mDigitizer.setEventTime(mgr->GetEventTime()); LOG(DEBUG) << "Running digitization on new event " << mEventID << " from source " << mSourceID; @@ -92,8 +93,9 @@ void DigitizerTask::FinishTask() // finalize digitization, if needed, flash remaining digits FairRootManager* mgr = FairRootManager::Instance(); mgr->SetLastFill(kTRUE); /// necessary, otherwise the data is not written out - if (mDigitsArray) + if (mDigitsArray) { mDigitsArray->clear(); + } // mDigitizer.fillOutputContainer(mDigitsArray); mDigitizer.finish(); } diff --git a/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h b/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h index 21d1a42246d59..461faa35bf6c0 100644 --- a/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h +++ b/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h @@ -18,11 +18,9 @@ #pragma link C++ class o2::base::DetImpl < o2::emcal::Detector> + ; #pragma link C++ class o2::emcal::Digitizer + ; #pragma link C++ class o2::emcal::DigitizerTask + ; -#pragma link C++ class o2::emcal::DMAOutputStream + ; #pragma link C++ class o2::emcal::SimParam + ; #pragma link C++ class o2::emcal::LabeledDigit + ; #pragma link C++ class o2::emcal::RawWriter + ; -#pragma link C++ class o2::emcal::DMAOutputStream + ; #pragma link C++ class std::list < o2::emcal::LabeledDigit > +; diff --git a/Detectors/EMCAL/simulation/src/RawCreator.cxx b/Detectors/EMCAL/simulation/src/RawCreator.cxx new file mode 100644 index 0000000000000..57c60140cea4e --- /dev/null +++ b/Detectors/EMCAL/simulation/src/RawCreator.cxx @@ -0,0 +1,113 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <memory> +#include <string> +#include <vector> +#include "Framework/Logger.h" + +#include <boost/program_options.hpp> + +#include <TFile.h> +#include <TTree.h> +#include <TTreeReader.h> +#include <TSystem.h> + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/StringUtils.h" +#include "DataFormatsEMCAL/Digit.h" +#include "DataFormatsEMCAL/TriggerRecord.h" +#include "EMCALBase/Geometry.h" +#include "EMCALSimulation/RawWriter.h" + +namespace bpo = boost::program_options; + +int main(int argc, const char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " <cmds/options>\n" + " Tool will encode emcal raw data from input file\n" + "Commands / Options"); + 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("verbose,v", bpo::value<uint32_t>()->default_value(0), "Select verbosity level [0 = no output]"); + add_option("input-file,i", bpo::value<std::string>()->default_value("emcaldigits.root"), "Specifies digit input file."); + add_option("file-for,f", bpo::value<std::string>()->default_value("all"), "single file per: all,subdet,link"); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + add_option("debug,d", bpo::value<uint32_t>()->default_value(0), "Select debug output level [0 = no debug output]"); + add_option("configKeyValues", bpo::value<std::string>()->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") || argc == 1) { + std::cout << opt_general << std::endl; + exit(0); + } + + } 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<std::string>()); + + auto digitfilename = vm["input-file"].as<std::string>(), + outputdir = vm["output-dir"].as<std::string>(), + filefor = vm["file-for"].as<std::string>(); + + // if needed, create output directory + if (gSystem->AccessPathName(outputdir.c_str())) { + if (gSystem->mkdir(outputdir.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outputdir; + } else { + LOG(INFO) << "created output directory " << outputdir; + } + } + + std::unique_ptr<TFile> digitfile(TFile::Open(digitfilename.data(), "READ")); + auto treereader = std::make_unique<TTreeReader>(static_cast<TTree*>(digitfile->Get("o2sim"))); + TTreeReaderValue<std::vector<o2::emcal::Digit>> digitbranch(*treereader, "EMCALDigit"); + TTreeReaderValue<std::vector<o2::emcal::TriggerRecord>> triggerbranch(*treereader, "EMCALDigitTRGR"); + + o2::emcal::RawWriter::FileFor_t granularity = o2::emcal::RawWriter::FileFor_t::kFullDet; + if (filefor == "all") { + granularity = o2::emcal::RawWriter::FileFor_t::kFullDet; + } else if (filefor == "subdet") { + granularity = o2::emcal::RawWriter::FileFor_t::kSubDet; + } else if (filefor == "link") { + granularity = o2::emcal::RawWriter::FileFor_t::kLink; + } + + o2::emcal::RawWriter rawwriter; + rawwriter.setOutputLocation(outputdir.data()); + rawwriter.setFileFor(granularity); + rawwriter.setGeometry(o2::emcal::Geometry::GetInstanceFromRunNumber(300000)); + rawwriter.setNumberOfADCSamples(15); // @TODO Needs to come from CCDB + rawwriter.setPedestal(0); // @TODO Needs to come from CCDB + rawwriter.init(); + + // Loop over all entries in the tree, where each tree entry corresponds to a time frame + for (auto en : *treereader) { + rawwriter.digitsToRaw(*digitbranch, *triggerbranch); + } + rawwriter.getWriter().writeConfFile("EMC", "RAWDATA", o2::utils::concat_string(outputdir, "/EMCraw.cfg")); +} diff --git a/Detectors/EMCAL/simulation/src/RawOutputPageHandler.cxx b/Detectors/EMCAL/simulation/src/RawOutputPageHandler.cxx deleted file mode 100644 index 87019c239a00d..0000000000000 --- a/Detectors/EMCAL/simulation/src/RawOutputPageHandler.cxx +++ /dev/null @@ -1,83 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <fmt/format.h> -#include <Headers/RAWDataHeader.h> -#include <DetectorsRaw/HBFUtils.h> -#include <EMCALSimulation/RawOutputPageHandler.h> - -using namespace o2::emcal; - -RawOutputPageHandler::RawOutputPageHandler(const char* rawfilename) : mOutputStream(rawfilename), mCurrentTimeframe(-1) -{ - // create page buffers for all (40) links - for (int ilink = 0; ilink < 40; ilink++) - mRawPages[ilink] = RawPageBuffer(); - mOutputStream.open(); -} - -RawOutputPageHandler::~RawOutputPageHandler() -{ - // write pages for last timeframe to file - for (const auto& [linkID, pagebuffer] : mRawPages) { - // only write pages for links which send data - if (pagebuffer.getPages().size()) - writeTimeframe(linkID, pagebuffer); - } -} - -void RawOutputPageHandler::initTrigger(const o2::InteractionRecord& irtrigger) -{ - auto currenttimeframe = raw::HBFUtils::Instance().getTF(irtrigger); - if (currenttimeframe != mCurrentTimeframe) { - // write pages to file - for (auto& [linkID, pagebuffer] : mRawPages) { - // only write pages for links which send data - if (pagebuffer.getPages().size()) - writeTimeframe(linkID, pagebuffer); - pagebuffer.flush(); - } - // set the new timeframe - mCurrentTimeframe = currenttimeframe; - } -} - -void RawOutputPageHandler::writeTimeframe(int linkID, const RawPageBuffer& pagebuffer) -{ - // write pages to file - // Write timeframe open - RawHeader timeframeheader; - timeframeheader.linkID = linkID; - mOutputStream.writeSingleHeader(timeframeheader); - int pagecounter = 1; - for (const auto& page : pagebuffer.getPages()) { - auto header = page.mHeader; - header.pageCnt = pagecounter; - pagecounter = mOutputStream.writeData(header, gsl::span<const char>(page.mPage.data(), page.mPage.size())); - } - - // end of timeframe - timeframeheader.pageCnt = pagecounter; - timeframeheader.stop = 1; - timeframeheader.triggerType = o2::trigger::TF; - mOutputStream.writeSingleHeader(timeframeheader); -} - -void RawOutputPageHandler::addPageForLink(int linkID, const RawHeader& header, const std::vector<char>& page) -{ - if (linkID > 40) - throw LinkIDException(linkID); - mRawPages[linkID].addPage(header, page); -} - -RawOutputPageHandler::LinkIDException::LinkIDException(int linkID) : std::exception(), mLinkID(linkID), mMessage() -{ - mMessage = fmt::format("Link ID invalid: %d (max 40)", linkID); -} \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/src/RawWriter.cxx b/Detectors/EMCAL/simulation/src/RawWriter.cxx index 013cbdc7fad5d..484dd76d6edae 100644 --- a/Detectors/EMCAL/simulation/src/RawWriter.cxx +++ b/Detectors/EMCAL/simulation/src/RawWriter.cxx @@ -8,31 +8,53 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "FairLogger.h" + +#include <fmt/core.h> #include <gsl/span> #include <TSystem.h> #include "DataFormatsEMCAL/Constants.h" #include "EMCALBase/Geometry.h" #include "EMCALBase/RCUTrailer.h" #include "EMCALSimulation/RawWriter.h" -#include "Headers/RAWDataHeader.h" using namespace o2::emcal; -void RawWriter::setTriggerRecords(std::vector<o2::emcal::TriggerRecord>* triggers) -{ - mTriggers = triggers; - mCurrentTrigger = triggers->begin(); -} - void RawWriter::init() { - mPageHandler = std::make_unique<RawOutputPageHandler>(mRawFilename.data()); - // initialize mappers - std::array<char, 4> sides = {{'A', 'C'}}; - for (auto iside = 0; iside < sides.size(); iside++) { - for (auto isru = 0; isru < 20; isru++) { - mMappers[iside * 2 + isru].setMapping(Form("%s/share/Detectors/EMC/file/RCU%d%c.data", gSystem->Getenv("O2_ROOT"), isru, sides[iside])); + mRawWriter = std::make_unique<o2::raw::RawFileWriter>(o2::header::gDataOriginEMC, false); + mRawWriter->setCarryOverCallBack(this); + mRawWriter->setApplyCarryOverToLastPage(true); + for (auto iddl = 0; iddl < 40; iddl++) { + // For EMCAL set + // - FEE ID = DDL ID + // - C-RORC and link increasing with DDL ID + // @TODO replace with link assignment on production FLPs, + // eventually storing in CCDB + auto [crorc, link] = getLinkAssignment(iddl); + std::string rawfilename = mOutputLocation; + switch (mFileFor) { + case FileFor_t::kFullDet: + rawfilename += "/emcal.raw"; + break; + case FileFor_t::kSubDet: { + std::string detstring; + if (iddl < 22) { + detstring = "emcal"; + } else { + detstring = "dcal"; + } + rawfilename += fmt::format("/{:s}.raw", detstring.data()); + break; + }; + case FileFor_t::kLink: + rawfilename += fmt::format("/emcal_{:d}_{:d}.raw", crorc, link); } + mRawWriter->registerLink(iddl, crorc, link, 0, rawfilename.data()); + } + // initialize mappers + if (!mMappingHandler) { + mMappingHandler = std::make_unique<o2::emcal::MappingHandler>(); } // initialize containers for SRU @@ -43,18 +65,28 @@ void RawWriter::init() } } -void RawWriter::processNextTrigger() +void RawWriter::digitsToRaw(gsl::span<o2::emcal::Digit> digitsbranch, gsl::span<o2::emcal::TriggerRecord> triggerbranch) +{ + setDigits(digitsbranch); + for (auto trg : triggerbranch) { + processTrigger(trg); + } +} + +bool RawWriter::processTrigger(const o2::emcal::TriggerRecord& trg) { - // initialize page handler when processing the first trigger - mPageHandler->initTrigger(mCurrentTrigger->getBCData()); - for (auto srucont : mSRUdata) + for (auto srucont : mSRUdata) { srucont.mChannels.clear(); + } std::vector<o2::emcal::Digit*>* bunchDigits; int lasttower = -1; - for (auto& dig : gsl::span(&mDigits->data()[mCurrentTrigger->getFirstEntry()], mCurrentTrigger->getNumberOfObjects())) { + for (auto& dig : gsl::span(mDigits.data() + trg.getFirstEntry(), trg.getNumberOfObjects())) { auto tower = dig.getTower(); if (tower != lasttower) { lasttower = tower; + if (tower > 20000) { + std::cout << "Wrong cell ID " << tower << std::endl; + } auto onlineindices = getOnlineID(tower); int sruID = std::get<0>(onlineindices); auto towerdata = mSRUdata[sruID].mChannels.find(tower); @@ -65,36 +97,54 @@ void RawWriter::processNextTrigger() } else { bunchDigits = &(towerdata->second.mDigits); } - (*bunchDigits)[int(dig.getTimeStamp())] = &dig; } + + // Get time sample of the digit: + // Digitizer stores the time sample in ns, needs to be converted to time sample dividing + // by the length of the time sample + auto timesample = int(dig.getTimeStamp() / emcal::constants::EMCAL_TIMESAMPLE); + if (timesample >= mNADCSamples) { + LOG(ERROR) << "Digit time sample " << timesample << " outside range [0," << mNADCSamples << "]"; + continue; + } + (*bunchDigits)[timesample] = &dig; } // Create and fill DMA pages for each channel + std::cout << "encode data" << std::endl; std::vector<char> payload; for (auto srucont : mSRUdata) { - // EMCAL does not set HBF triggers, only set trigger BC and orbit - o2::header::RAWDataHeaderV4 rawheader; - rawheader.triggerBC = mCurrentTrigger->getBCData().bc; - rawheader.triggerOrbit = mCurrentTrigger->getBCData().orbit; - // @TODO: Set trigger type - rawheader.feeId = srucont.mSRUid; - rawheader.linkID = srucont.mSRUid; for (const auto& [tower, channel] : srucont.mChannels) { // Find out hardware address of the channel - auto hwaddress = mMappers[srucont.mSRUid].getHardwareAddress(channel.mRow, channel.mCol, ChannelType_t::HIGH_GAIN); // @TODO distinguish between high- and low-gain cells + auto hwaddress = mMappingHandler->getMappingForDDL(srucont.mSRUid).getHardwareAddress(channel.mRow, channel.mCol, ChannelType_t::HIGH_GAIN); // @TODO distinguish between high- and low-gain cells std::vector<int> rawbunches; for (auto& bunch : findBunches(channel.mDigits)) { + rawbunches.push_back(bunch.mADCs.size() + 2); // add 2 words for header information rawbunches.push_back(bunch.mStarttime); - rawbunches.push_back(bunch.mADCs.size()); for (auto adc : bunch.mADCs) { rawbunches.push_back(adc); } } + if (!rawbunches.size()) { + continue; + } auto encodedbunches = encodeBunchData(rawbunches); - auto chanhead = createChannelHeader(hwaddress, encodedbunches.size() * 3 - 2, false); /// bad channel status eventually to be added later + auto chanhead = createChannelHeader(hwaddress, rawbunches.size(), false); /// bad channel status eventually to be added later char* chanheadwords = reinterpret_cast<char*>(&chanhead); + uint32_t* testheader = reinterpret_cast<uint32_t*>(chanheadwords); + if ((*testheader >> 30) & 1) { + // header pattern found, check that the payload size is properly reflecting the number of words + uint32_t payloadsizeRead = ((*testheader >> 16) & 0x3FF); + uint32_t nwordsRead = (payloadsizeRead + 2) / 3; + if (encodedbunches.size() != nwordsRead) { + LOG(ERROR) << "Mismatch in number of 32-bit words, encoded " << encodedbunches.size() << ", recalculated " << nwordsRead << std::endl; + LOG(ERROR) << "Payload size: " << payloadsizeRead << ", number of words: " << rawbunches.size() << ", encodeed words " << encodedbunches.size() << ", calculated words " << nwordsRead << std::endl; + } + } else { + LOG(ERROR) << "Header without header bit detected ..." << std::endl; + } for (int iword = 0; iword < sizeof(ChannelHeader) / sizeof(char); iword++) { payload.emplace_back(chanheadwords[iword]); } @@ -104,43 +154,56 @@ void RawWriter::processNextTrigger() } } + if (!payload.size()) { + continue; + } + // Create RCU trailer - auto trailerwords = createRCUTrailer(payload.size(), 16, 16, 100., 0.); - for (auto word : trailerwords) + auto trailerwords = createRCUTrailer(payload.size() / 4, 16, 16, 100., 0.); + for (auto word : trailerwords) { payload.emplace_back(word); + } - // write DMA page to stream - mPageHandler->addPageForLink(srucont.mSRUid, rawheader, payload); + // register output data + auto ddlid = srucont.mSRUid; + auto [crorc, link] = getLinkAssignment(ddlid); + LOG(DEBUG1) << "Adding payload with size " << payload.size() << " (" << payload.size() / 4 << " ALTRO words)"; + mRawWriter->addData(ddlid, crorc, link, 0, trg.getBCData(), payload, false, trg.getTriggerBits()); } + std::cout << "Done" << std::endl; + return true; } std::vector<AltroBunch> RawWriter::findBunches(const std::vector<o2::emcal::Digit*>& channelDigits) { std::vector<AltroBunch> result; AltroBunch* currentBunch = nullptr; - int starttime = 0; - for (auto ien = channelDigits.size() - 1;; ien--) { - auto dig = channelDigits[ien]; + // Digits in ALTRO bunch in time-reversed order + int itime; + for (itime = channelDigits.size() - 1; itime >= 0; itime--) { + auto dig = channelDigits[itime]; if (!dig) { - starttime++; continue; } - int adc = dig->getEnergy() / constants::EMCAL_ADCENERGY; /// conversion Energy <-> ADC := 16 MeV/ADC + int adc = dig->getAmplitudeADC(); if (adc < mPedestal) { // Stop bunch + // Set the start time to the time sample of previous (passing) digit + currentBunch->mStarttime = itime + 1; currentBunch = nullptr; - starttime++; continue; } if (!currentBunch) { // start new bunch AltroBunch bunch; - bunch.mStarttime = starttime; result.push_back(bunch); currentBunch = &(result.back()); } currentBunch->mADCs.emplace_back(adc); - starttime++; + } + // if we have a last bunch set time start time to the time bin of teh previous digit + if (currentBunch) { + currentBunch->mStarttime = itime + 1; } return result; } @@ -153,19 +216,29 @@ std::tuple<int, int, int> RawWriter::getOnlineID(int towerID) auto etaphishift = mGeometry->ShiftOfflineToOnlineCellIndexes(supermoduleID, std::get<0>(etaphi), std::get<1>(etaphi)); int row = std::get<0>(etaphishift), col = std::get<1>(etaphishift); - int sruID = -1; - if (0 <= row && row < 8) - sruID = 0; // first cable row - else if (8 <= row && row < 16 && 0 <= col && col < 24) - sruID = 0; // first half; - else if (8 <= row && row < 16 && 24 <= col && col < 48) - sruID = 1; // second half; - else if (16 <= row && row < 24) - sruID = 1; // third cable row - if (supermoduleID % 2 == 1) - sruID = 1 - sruID; // swap for odd=C side, to allow us to cable both sides the same - - return std::make_tuple(sruID, row, col); + int ddlInSupermoudel = -1; + if (0 <= row && row < 8) { + ddlInSupermoudel = 0; // first cable row + } else if (8 <= row && row < 16 && 0 <= col && col < 24) { + ddlInSupermoudel = 0; // first half; + } else if (8 <= row && row < 16 && 24 <= col && col < 48) { + ddlInSupermoudel = 1; // second half; + } else if (16 <= row && row < 24) { + ddlInSupermoudel = 1; // third cable row + } + if (supermoduleID % 2 == 1) { + ddlInSupermoudel = 1 - ddlInSupermoudel; // swap for odd=C side, to allow us to cable both sides the same + } + + return std::make_tuple(supermoduleID * 2 + ddlInSupermoudel, row, col); +} + +std::tuple<int, int> RawWriter::getLinkAssignment(int ddlID) +{ + // Temporary link assignment (till final link assignment is known - + // eventually taken from CCDB) + // - Link (0-5) and C-RORC ID linear with ddlID + return std::make_tuple(ddlID / 6, ddlID % 6); } std::vector<int> RawWriter::encodeBunchData(const std::vector<int>& data) @@ -185,15 +258,17 @@ std::vector<int> RawWriter::encodeBunchData(const std::vector<int>& data) currentword.mWord2 = adc; break; }; - if (wordnumber == 2) { + wordnumber++; + if (wordnumber == 3) { // start new word; encoded.push_back(currentword.mDataWord); currentword.mDataWord = 0; wordnumber = 0; - } else { - wordnumber++; } } + if (wordnumber) { + encoded.push_back(currentword.mDataWord); + } return encoded; } @@ -203,6 +278,7 @@ ChannelHeader RawWriter::createChannelHeader(int hardwareAddress, int payloadSiz header.mHardwareAddress = hardwareAddress; header.mPayloadSize = payloadSize; header.mBadChannel = isBadChannel ? 1 : 0; + header.mHeaderBits = 1; return header; } @@ -218,4 +294,38 @@ std::vector<char> RawWriter::createRCUTrailer(int payloadsize, int feca, int fec std::vector<char> encoded(trailerwords.size() * sizeof(uint32_t)); memcpy(encoded.data(), trailerwords.data(), trailerwords.size() * sizeof(uint32_t)); return encoded; -} \ No newline at end of file +} + +int RawWriter::carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data, + const char* ptr, int maxSize, int splitID, + std::vector<char>& trailer, std::vector<char>& header) const +{ + int offs = ptr - &data[0]; // offset wrt the head of the payload + // make sure ptr and end of the suggested block are within the payload + assert(offs >= 0 && size_t(offs + maxSize) <= data.size()); + + // Read trailer template from the end of payload + gsl::span<const uint32_t> payloadwords(reinterpret_cast<const uint32_t*>(data.data()), data.size() / sizeof(uint32_t)); + auto rcutrailer = RCUTrailer::constructFromPayloadWords(payloadwords); + + int sizeNoTrailer = maxSize - rcutrailer.getTrailerSize() * sizeof(uint32_t); + // calculate payload size for RCU trailer: + // assume actualsize is in byte + // Payload size is defined as the number of 32-bit payload words + // -> actualSize to be converted to size of 32 bit words + auto payloadsize = sizeNoTrailer / sizeof(uint32_t); + rcutrailer.setPayloadSize(payloadsize); + auto trailerwords = rcutrailer.encode(); + trailer.resize(trailerwords.size() * sizeof(uint32_t)); + memcpy(trailer.data(), trailerwords.data(), trailer.size()); + // Size to return differs between intermediate pages and last page + // - intermediate page: Size of the trailer needs to be removed as the trailer gets appended + // - last page: Size of the trailer needs to be included as the trailer gets replaced + int bytesLeft = data.size() - (ptr - &data[0]); + bool lastPage = bytesLeft <= maxSize; + int actualSize = maxSize; + if (!lastPage) { + actualSize = sizeNoTrailer; + } + return actualSize; +} diff --git a/Detectors/EMCAL/workflow/CMakeLists.txt b/Detectors/EMCAL/workflow/CMakeLists.txt index e26b42363121f..aac2708bddbbb 100644 --- a/Detectors/EMCAL/workflow/CMakeLists.txt +++ b/Detectors/EMCAL/workflow/CMakeLists.txt @@ -16,6 +16,8 @@ o2_add_library(EMCALWorkflow src/DigitsPrinterSpec.cxx src/AnalysisClusterSpec.cxx src/RawToCellConverterSpec.cxx + src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsEMCAL O2::DPLUtils O2::EMCALBase O2::EMCALReconstruction O2::Algorithm) @@ -23,3 +25,8 @@ o2_add_executable(reco-workflow COMPONENT_NAME emcal SOURCES src/emc-reco-workflow.cxx PUBLIC_LINK_LIBRARIES O2::EMCALWorkflow) + +o2_add_executable(entropy-encoder-workflow + COMPONENT_NAME emcal + SOURCES src/entropy-encoder-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::EMCALWorkflow) diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..2273f60d5a1f6 --- /dev/null +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to EMCAL digit/channels strean + +#ifndef O2_EMCAL_ENTROPYDECODER_SPEC +#define O2_EMCAL_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "EMCALReconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace emcal +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::emcal::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace emcal +} // namespace o2 + +#endif diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..f0852d9367577 --- /dev/null +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert EMCAL data to CTF (EncodedBlocks) + +#ifndef O2_EMCAL_ENTROPYENCODER_SPEC +#define O2_EMCAL_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "EMCALReconstruction/CTFCoder.h" + +namespace o2 +{ +namespace emcal +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::emcal::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace emcal +} // namespace o2 + +#endif diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h index d6a2064acc8a3..a37af9554fb6b 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h @@ -10,13 +10,13 @@ #include <vector> -#include "DataFormatsEMCAL/Cell.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "DataFormatsEMCAL/Cell.h" +#include "DataFormatsEMCAL/TriggerRecord.h" #include "EMCALBase/Geometry.h" #include "EMCALBase/Mapper.h" -#include "DataFormatsEMCAL/TriggerRecord.h" -#include "EMCALReconstruction/CaloRawFitterStandard.h" +#include "EMCALReconstruction/CaloRawFitter.h" namespace o2 { @@ -62,7 +62,7 @@ class RawToCellConverterSpec : public framework::Task int mNoiseThreshold = 0; o2::emcal::Geometry* mGeometry = nullptr; ///!<! Geometry pointer std::unique_ptr<o2::emcal::MappingHandler> mMapper = nullptr; ///!<! Mapper - o2::emcal::CaloRawFitterStandard mRawFitter; ///!<! Raw fitter + std::unique_ptr<o2::emcal::CaloRawFitter> mRawFitter; ///!<! Raw fitter std::vector<o2::emcal::Cell> mOutputCells; ///< Container with output cells std::vector<o2::emcal::TriggerRecord> mOutputTriggerRecords; ///< Container with output cells }; diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h index 0d296cb0e159f..f001e24ae88d2 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h @@ -27,11 +27,10 @@ namespace reco_workflow /// \enum InputType /// \brief Input types of the workflow /// \ingroup EMCALworkflow -enum struct InputType { Digitizer, ///< directly read digits from channel {TPC:DIGITS) - Digits, ///< read digits from file - Cells, ///< read compressed cells from file - Raw, ///< read data in raw page format from file - Clusters ///< read native clusters from file +enum struct InputType { Digits, ///< read digits from file + Cells, ///< read compressed cells from file + Raw, ///< read data in raw page format from file + Clusters ///< read native clusters from file }; /// \enum OutputType @@ -53,9 +52,10 @@ enum struct OutputType { Digits, ///< EMCAL digits /// \ingroup EMCALwokflow framework::WorkflowSpec getWorkflow(bool propagateMC = true, bool enableDigitsPrinter = false, - std::string const& cfgInput = "digits", // - std::string const& cfgOutput = "clusters" // -); + std::string const& cfgInput = "digits", // + std::string const& cfgOutput = "clusters", // + bool disableRootInput = false, + bool disableRootOutput = false); } // namespace reco_workflow } // namespace emcal diff --git a/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx b/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx index c3a65960dc82e..b6fbc65ea37dc 100644 --- a/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx @@ -14,7 +14,7 @@ #include "DataFormatsEMCAL/EMCALBlockHeader.h" #include "EMCALWorkflow/CellConverterSpec.h" #include "Framework/ControlService.h" -#include "SimulationDataFormat/MCCompLabel.h" +#include "DataFormatsEMCAL/MCLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" using namespace o2::emcal::reco_workflow; @@ -41,14 +41,15 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) gsl::span<const o2::emcal::Digit> digits(digitsAll.data() + trg.getFirstEntry(), trg.getNumberOfObjects()); for (const auto& dig : digits) { ChannelType_t chantype; - if (dig.getHighGain()) + if (dig.getHighGain()) { chantype = ChannelType_t::HIGH_GAIN; - else if (dig.getLowGain()) + } else if (dig.getLowGain()) { chantype = ChannelType_t::LOW_GAIN; - else if (dig.getTRU()) + } else if (dig.getTRU()) { chantype = ChannelType_t::TRU; - else if (dig.getLEDMon()) + } else if (dig.getLEDMon()) { chantype = ChannelType_t::LEDMON; + } mOutputCells.emplace_back(dig.getTower(), dig.getEnergy(), dig.getTimeStamp(), chantype); ncellsTrigger++; } @@ -62,7 +63,7 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) if (mPropagateMC) { // copy mc truth container without modification // as indexing doesn't change - auto truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("digitsmctr"); + auto truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::emcal::MCLabel>*>("digitsmctr"); ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLSMCTR", 0, o2::framework::Lifetime::Timeframe}, *truthcont); } } diff --git a/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx b/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx index d3ec00ddac607..8c4284945cb21 100644 --- a/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx @@ -78,9 +78,11 @@ void ClusterizerSpec<InputType>::run(framework::ProcessingContext& ctx) int currentStartClusters = mOutputClusters->size(); int currentStartIndices = mOutputCellDigitIndices->size(); for (auto iTrgRcrd : InputTriggerRecord) { - - mClusterizer.findClusters(gsl::span<const InputType>(&Inputs[iTrgRcrd.getFirstEntry()], iTrgRcrd.getNumberOfObjects())); // Find clusters on cells/digits (pass by ref) - + if (Inputs.size() && iTrgRcrd.getNumberOfObjects()) { + mClusterizer.findClusters(gsl::span<const InputType>(&Inputs[iTrgRcrd.getFirstEntry()], iTrgRcrd.getNumberOfObjects())); // Find clusters on cells/digits (pass by ref) + } else { + mClusterizer.clear(); + } // Get found clusters + cell/digit indices for output // * A cluster contains a range that correspond to the vector of cell/digit indices // * The cell/digit index vector contains the indices of the clusterized cells/digits wrt to the original cell/digit array diff --git a/Detectors/EMCAL/workflow/src/DigitsPrinterSpec.cxx b/Detectors/EMCAL/workflow/src/DigitsPrinterSpec.cxx index 1cf7daf767682..df0131cc53e28 100644 --- a/Detectors/EMCAL/workflow/src/DigitsPrinterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/DigitsPrinterSpec.cxx @@ -36,11 +36,11 @@ void DigitsPrinterSpec<InputType>::run(framework::ProcessingContext& pc) // Get the EMCAL block header and check whether it contains digits LOG(DEBUG) << "[EMCALDigitsPrinter - process] called"; std::string objectbranch; - if constexpr (std::is_same<InputType, o2::emcal::Digit>::value) + if constexpr (std::is_same<InputType, o2::emcal::Digit>::value) { objectbranch = "digits"; - else if constexpr (std::is_same<InputType, o2::emcal::Cell>::value) + } else if constexpr (std::is_same<InputType, o2::emcal::Cell>::value) { objectbranch = "cells"; - else { + } else { LOG(ERROR) << "Unsupported input type ... "; return; } diff --git a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..dec7ed2f81cdd --- /dev/null +++ b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "EMCALWorkflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace emcal +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("emcal-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& triggers = pc.outputs().make<std::vector<TriggerRecord>>(OutputRef{"triggers"}); + auto& cells = pc.outputs().make<std::vector<Cell>>(OutputRef{"cells"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::emcal::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, triggers, cells); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << cells.size() << " EMCAL cells in " << triggers.size() << " triggers in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "EMCAL Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"triggers"}, "EMC", "CELLSTRGR", 0, Lifetime::Timeframe}, + OutputSpec{{"cells"}, "EMC", "CELLS", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "emcal-entropy-decoder", + Inputs{InputSpec{"ctf", "EMC", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"emcal-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace emcal +} // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..b4372ed4d8186 --- /dev/null +++ b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "EMCALWorkflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace emcal +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("emcal-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto triggers = pc.inputs().get<gsl::span<TriggerRecord>>("triggers"); + auto cells = pc.inputs().get<gsl::span<Cell>>("cells"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"EMC", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, triggers, cells); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + // eeb->print(); + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for EMCAL in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "EMCAL Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("triggers", "EMC", "CELLSTRGR", 0, Lifetime::Timeframe); + inputs.emplace_back("cells", "EMC", "CELLS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "emcal-entropy-encoder", + inputs, + Outputs{{"EMC", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"emcal-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace emcal +} // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx b/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx index 8c79ec49b4d15..e8bddb7f752d6 100644 --- a/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx @@ -7,21 +7,28 @@ // In applying this license CERN does 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 <string> + #include "FairLogger.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" #include "DataFormatsEMCAL/EMCALBlockHeader.h" #include "DataFormatsEMCAL/TriggerRecord.h" -#include "EMCALWorkflow/RawToCellConverterSpec.h" -#include "Framework/ControlService.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsRaw/RDHUtils.h" #include "EMCALBase/Geometry.h" #include "EMCALBase/Mapper.h" #include "EMCALReconstruction/CaloFitResults.h" #include "EMCALReconstruction/Bunch.h" #include "EMCALReconstruction/CaloRawFitterStandard.h" +#include "EMCALReconstruction/CaloRawFitterGamma2.h" #include "EMCALReconstruction/AltroDecoder.h" -#include "CommonDataFormat/InteractionRecord.h" +#include "EMCALWorkflow/RawToCellConverterSpec.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" using namespace o2::emcal::reco_workflow; @@ -42,83 +49,110 @@ void RawToCellConverterSpec::init(framework::InitContext& ctx) LOG(ERROR) << "Failed to initialize mapper"; } - mRawFitter.setAmpCut(mNoiseThreshold); - mRawFitter.setL1Phase(0.); + auto fitmethod = ctx.options().get<std::string>("fitmethod"); + if (fitmethod == "standard") { + LOG(INFO) << "Using standard raw fitter"; + mRawFitter = std::unique_ptr<o2::emcal::CaloRawFitter>(new o2::emcal::CaloRawFitterStandard); + } else if (fitmethod == "gamma2") { + mRawFitter = std::unique_ptr<o2::emcal::CaloRawFitter>(new o2::emcal::CaloRawFitterGamma2); + } + + mRawFitter->setAmpCut(mNoiseThreshold); + mRawFitter->setL1Phase(0.); } void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) { LOG(DEBUG) << "[EMCALRawToCellConverter - run] called"; - mOutputCells.clear(); - mOutputTriggerRecords.clear(); + // Cache cells from for bunch crossings as the component reads timeframes from many links consecutively + std::map<o2::InteractionRecord, std::shared_ptr<std::vector<o2::emcal::Cell>>> cellBuffer; // Internal cell buffer + std::map<o2::InteractionRecord, uint32_t> triggerBuffer; int firstEntry = 0; - for (const auto& rawData : ctx.inputs()) { - + for (const auto& rawData : framework::InputRecordWalker(ctx.inputs())) { //o2::emcal::RawReaderMemory<o2::header::RAWDataHeaderV4> rawreader(gsl::span(rawData.payload, o2::framework::DataRefUtils::getPayloadSize(rawData))); - o2::emcal::RawReaderMemory<o2::header::RAWDataHeaderV4> rawreader(o2::framework::DataRefUtils::as<const char>(rawData)); - - bool first = true; - uint16_t currentTrigger = 0; - uint32_t currentorbit = 0; + o2::emcal::RawReaderMemory rawreader(o2::framework::DataRefUtils::as<const char>(rawData)); // loop over all the DMA pages while (rawreader.hasNext()) { rawreader.next(); - auto header = rawreader.getRawHeader(); - - if (!first) { // check if it is the first event in the payload - std::cout << " triggerBC " << header.triggerBC << " current Trigger " << currentTrigger << std::endl; - if (header.triggerBC > currentTrigger) { //new event - mOutputTriggerRecords.emplace_back(o2::InteractionRecord(currentTrigger, currentorbit), firstEntry, mOutputCells.size() - 1); - firstEntry = mOutputCells.size(); - - currentTrigger = header.triggerBC; - currentorbit = header.triggerOrbit; - } //new event - } else { //first - currentTrigger = header.triggerBC; - std::cout << " first is true and I set triggerBC to currentTrigger " << currentTrigger << std::endl; - currentorbit = header.triggerOrbit; - std::cout << " and set first to false " << std::endl; - first = false; + auto& header = rawreader.getRawHeader(); + auto triggerBC = o2::raw::RDHUtils::getTriggerBC(header); + auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(header); + auto feeID = o2::raw::RDHUtils::getFEEID(header); + auto triggerbits = o2::raw::RDHUtils::getTriggerType(header); + + o2::InteractionRecord currentIR(triggerBC, triggerOrbit); + std::shared_ptr<std::vector<o2::emcal::Cell>> currentCellContainer; + auto found = cellBuffer.find(currentIR); + if (found == cellBuffer.end()) { + currentCellContainer = std::make_shared<std::vector<o2::emcal::Cell>>(); + cellBuffer[currentIR] = currentCellContainer; + // also add trigger bits + triggerBuffer[currentIR] = triggerbits; + } else { + currentCellContainer = found->second; } - if (header.feeId > 40) + if (feeID > 40) { continue; //skip STU ddl + } //std::cout<<rawreader.getRawHeader()<<std::endl; // use the altro decoder to decode the raw data, and extract the RCU trailer - o2::emcal::AltroDecoder<decltype(rawreader)> decoder(rawreader); + o2::emcal::AltroDecoder decoder(rawreader); decoder.decode(); - std::cout << decoder.getRCUTrailer() << std::endl; + LOG(DEBUG) << decoder.getRCUTrailer(); - o2::emcal::Mapper map = mMapper->getMappingForDDL(header.feeId); + o2::emcal::Mapper map = mMapper->getMappingForDDL(feeID); + int iSM = feeID / 2; // Loop over all the channels for (auto& chan : decoder.getChannels()) { - int iRow = map.getRow(chan.getHardwareAddress()); - int iCol = map.getColumn(chan.getHardwareAddress()); - ChannelType_t chantype = map.getChannelType(chan.getHardwareAddress()); - int iSM = header.feeId / 2; + int iRow, iCol; + ChannelType_t chantype; + try { + iRow = map.getRow(chan.getHardwareAddress()); + iCol = map.getColumn(chan.getHardwareAddress()); + chantype = map.getChannelType(chan.getHardwareAddress()); + } catch (Mapper::AddressNotFoundException& ex) { + std::cerr << ex.what() << std::endl; + continue; + }; int CellID = mGeometry->GetAbsCellIdFromCellIndexes(iSM, iRow, iCol); // define the conatiner for the fit results, and perform the raw fitting using the stadnard raw fitter - o2::emcal::CaloFitResults fitResults = mRawFitter.evaluate(chan.getBunches(), 0, 0); - - if (fitResults.getAmp() < 0 && fitResults.getTime() < 0) { + double amp = 0., time = 0.; + o2::emcal::CaloFitResults fitResults = mRawFitter->evaluate(chan.getBunches(), 0, 0); + if (fitResults.getAmp() > 0) { fitResults.setAmp(0.); + } + if (fitResults.getTime() < 0) { fitResults.setTime(0.); } - mOutputCells.emplace_back(CellID, fitResults.getAmp(), fitResults.getTime(), chantype); + currentCellContainer->emplace_back(CellID, amp, time, chantype); + } + } + } + + // Loop over BCs, sort cells with increasing tower ID and write to output containers + mOutputCells.clear(); + mOutputTriggerRecords.clear(); + for (auto [bc, cells] : cellBuffer) { + mOutputTriggerRecords.emplace_back(bc, triggerBuffer[bc], mOutputCells.size(), cells->size()); + if (cells->size()) { + // Sort cells according to cell ID + std::sort(cells->begin(), cells->end(), [](o2::emcal::Cell& lhs, o2::emcal::Cell& rhs) { return lhs.getTower() < rhs.getTower(); }); + for (auto cell : *cells) { + mOutputCells.push_back(cell); } } } @@ -133,12 +167,13 @@ o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getRawToCellConverter std::vector<o2::framework::InputSpec> inputs; std::vector<o2::framework::OutputSpec> outputs; - inputs.emplace_back("readout-proxy", "FLP", "RAWDATA", 0, o2::framework::Lifetime::Timeframe); outputs.emplace_back("EMC", "CELLS", 0, o2::framework::Lifetime::Timeframe); outputs.emplace_back("EMC", "CELLSTRGR", 0, o2::framework::Lifetime::Timeframe); return o2::framework::DataProcessorSpec{"EMCALRawToCellConverterSpec", - inputs, + o2::framework::select("A:EMC/RAWDATA"), outputs, - o2::framework::adaptFromTask<o2::emcal::reco_workflow::RawToCellConverterSpec>()}; + o2::framework::adaptFromTask<o2::emcal::reco_workflow::RawToCellConverterSpec>(), + o2::framework::Options{ + {"fitmethod", o2::framework::VariantType::String, "standard", {"Fit method (standard or gamma2)"}}}}; } diff --git a/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx b/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx index e0cbadeacf783..d7c7fb7f03446 100644 --- a/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx +++ b/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx @@ -28,7 +28,7 @@ #include "EMCALWorkflow/PublisherSpec.h" #include "EMCALWorkflow/RawToCellConverterSpec.h" #include "Framework/DataSpecUtils.h" -#include "SimulationDataFormat/MCCompLabel.h" +#include "DataFormatsEMCAL/MCLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" namespace o2 @@ -43,26 +43,33 @@ namespace emcal namespace reco_workflow { -const std::unordered_map<std::string, InputType> InputMap{ - {"digitizer", InputType::Digitizer}, - {"digits", InputType::Digits}, - {"cells", InputType::Cells}, - {"raw", InputType::Raw}, - {"clusters", InputType::Clusters}, -}; - -const std::unordered_map<std::string, OutputType> OutputMap{ - {"digits", OutputType::Digits}, - {"cells", OutputType::Cells}, - {"raw", OutputType::Raw}, - {"clusters", OutputType::Clusters}, - {"analysisclusters", OutputType::AnalysisClusters}}; - o2::framework::WorkflowSpec getWorkflow(bool propagateMC, bool enableDigitsPrinter, std::string const& cfgInput, - std::string const& cfgOutput) + std::string const& cfgOutput, + bool disableRootInput, + bool disableRootOutput) { + + const std::unordered_map<std::string, InputType> InputMap{ + {"digits", InputType::Digits}, + {"cells", InputType::Cells}, + {"raw", InputType::Raw}, + {"clusters", InputType::Clusters}, + }; + + const std::unordered_map<std::string, OutputType> OutputMap{ + {"digits", OutputType::Digits}, + {"cells", OutputType::Cells}, + {"raw", OutputType::Raw}, + {"clusters", OutputType::Clusters}, + {"analysisclusters", OutputType::AnalysisClusters}}; + + std::unordered_map<InputType, std::vector<OutputType>> allowedIO; + allowedIO[InputType::Digits] = std::vector<OutputType>{OutputType::Cells, OutputType::Clusters, OutputType::AnalysisClusters}; + allowedIO[InputType::Cells] = std::vector<OutputType>{OutputType::Clusters, OutputType::AnalysisClusters}; + allowedIO[InputType::Raw] = std::vector<OutputType>{OutputType::Cells}; + InputType inputType; try { @@ -72,7 +79,7 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, } std::vector<OutputType> outputTypes; try { - outputTypes = RangeTokenizer::tokenize<OutputType>(cfgOutput, [](std::string const& token) { return OutputMap.at(token); }); + outputTypes = RangeTokenizer::tokenize<OutputType>(cfgOutput, [&](std::string const& token) { return OutputMap.at(token); }); } catch (std::out_of_range&) { throw std::invalid_argument(std::string("invalid output type: ") + cfgOutput); } @@ -80,20 +87,48 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, return std::find(outputTypes.begin(), outputTypes.end(), type) != outputTypes.end(); }; + auto isAllowedIOCombination = [&allowedIO](InputType inp, OutputType out) { + const auto& vout = allowedIO[inp]; + return std::find(vout.begin(), vout.end(), out) != vout.end(); + }; + + auto getOutputTypeName = [&OutputMap](OutputType out) { + std::string str; + for (const auto& o : OutputMap) { + if (o.second == out) { + str = std::string(o.first); + } + } + return str; + }; + + // make sure inputs/outputs combinatios are enabled + for (const auto outType : outputTypes) { + if (!isAllowedIOCombination(inputType, outType)) { + throw std::runtime_error(fmt::format("Input {:s} is not allowed with output {:s}", cfgInput, getOutputTypeName(outType))); + } + } + + if (inputType == InputType::Raw) { + propagateMC = false; + } + o2::framework::WorkflowSpec specs; if (inputType == InputType::Digits) { using digitInputType = std::vector<o2::emcal::Digit>; - specs.emplace_back(o2::emcal::getPublisherSpec<digitInputType>(PublisherConf{ - "emcal-digit-reader", - "o2sim", - {"digitbranch", "EMCALDigit", "Digit branch"}, - {"digittriggerbranch", "EMCALDigitTRGR", "Trigger record branch"}, - {"mcbranch", "EMCALDigitMCTruth", "MC label branch"}, - o2::framework::OutputSpec{"EMC", "DIGITS"}, - o2::framework::OutputSpec{"EMC", "DIGITSTRGR"}, - o2::framework::OutputSpec{"EMC", "DIGITSMCTR"}}, - propagateMC)); + if (!disableRootInput) { + specs.emplace_back(o2::emcal::getPublisherSpec<digitInputType>(PublisherConf{ + "emcal-digit-reader", + "o2sim", + {"digitbranch", "EMCALDigit", "Digit branch"}, + {"digittriggerbranch", "EMCALDigitTRGR", "Trigger record branch"}, + {"mcbranch", "EMCALDigitMCTruth", "MC label branch"}, + o2::framework::OutputSpec{"EMC", "DIGITS"}, + o2::framework::OutputSpec{"EMC", "DIGITSTRGR"}, + o2::framework::OutputSpec{"EMC", "DIGITSMCTR"}}, + propagateMC)); + } if (enableDigitsPrinter) { try { @@ -102,32 +137,20 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, LOG(ERROR) << "Cannot create digits printer spec: " << e.what(); } } - - if (isEnabled(OutputType::Cells)) { - // add converter for cells - specs.emplace_back(o2::emcal::reco_workflow::getCellConverterSpec(propagateMC)); - } - - if (isEnabled(OutputType::Clusters)) { - // add clusterizer - specs.emplace_back(o2::emcal::reco_workflow::getClusterizerSpec(true)); - } - if (isEnabled(OutputType::AnalysisClusters)) { - // add clusters from cells - specs.emplace_back(o2::emcal::reco_workflow::getAnalysisClusterSpec(true)); - } } else if (inputType == InputType::Cells) { using cellInputType = std::vector<o2::emcal::Cell>; - specs.emplace_back(o2::emcal::getPublisherSpec<cellInputType>(PublisherConf{ - "emcal-cell-reader", - "o2sim", - {"cellbranch", "EMCALCell", "Cell branch"}, - {"celltriggerbranch", "EMCALCellTRGR", "Trigger record branch"}, - {"mcbranch", "EMCALCellMCTruth", "MC label branch"}, - o2::framework::OutputSpec{"EMC", "CELLS"}, - o2::framework::OutputSpec{"EMC", "CELLSTRGR"}, - o2::framework::OutputSpec{"EMC", "CELLSMCTR"}}, - propagateMC)); + if (!disableRootInput) { + specs.emplace_back(o2::emcal::getPublisherSpec<cellInputType>(PublisherConf{ + "emcal-cell-reader", + "o2sim", + {"cellbranch", "EMCALCell", "Cell branch"}, + {"celltriggerbranch", "EMCALCellTRGR", "Trigger record branch"}, + {"mcbranch", "EMCALCellMCTruth", "MC label branch"}, + o2::framework::OutputSpec{"EMC", "CELLS"}, + o2::framework::OutputSpec{"EMC", "CELLSTRGR"}, + o2::framework::OutputSpec{"EMC", "CELLSMCTR"}}, + propagateMC)); + } if (enableDigitsPrinter) { try { specs.emplace_back(o2::emcal::reco_workflow::getEmcalDigitsPrinterSpec("cells")); @@ -135,24 +158,28 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, LOG(ERROR) << "Cannot create digits printer spec: " << e.what(); } } + } - if (isEnabled(OutputType::Clusters)) { - // add clusterizer from cells - specs.emplace_back(o2::emcal::reco_workflow::getClusterizerSpec(false)); - } - if (isEnabled(OutputType::AnalysisClusters)) { - // add clusters from cells - specs.emplace_back(o2::emcal::reco_workflow::getAnalysisClusterSpec(false)); - } - } else if (inputType == InputType::Raw) { - - //Subcscribe to Readout data using o2-readout-proxy - if (isEnabled(OutputType::Cells)) { - // add converter for cells + if (isEnabled(OutputType::Cells)) { + // add converter for cells + if (inputType == InputType::Digits) { + specs.emplace_back(o2::emcal::reco_workflow::getCellConverterSpec(propagateMC)); + } else { + // raw data will come from upstream specs.emplace_back(o2::emcal::reco_workflow::getRawToCellConverterSpec()); } } + if (isEnabled(OutputType::Clusters)) { + // add clusterizer + specs.emplace_back(o2::emcal::reco_workflow::getClusterizerSpec(inputType == InputType::Digits)); + } + + if (isEnabled(OutputType::AnalysisClusters)) { + // add clusters from cells + specs.emplace_back(o2::emcal::reco_workflow::getAnalysisClusterSpec(inputType == InputType::Digits)); + } + // check if the process is ready to quit // this is decided upon the meta information in the EMCAL block header, the operation is set // value kNoPayload in case of no data or no operation @@ -216,11 +243,11 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, std::move(CellsBranch), std::move(TriggerRecordBranch))); }; - - if (isEnabled(OutputType::Digits)) { + /* + // RS getting input digits and outputing them under the same outputspec will create dependency loop when piping the workflows + if (isEnabled(OutputType::Digits) && !disableRootOutput) { using DigitOutputType = std::vector<o2::emcal::Digit>; using TriggerOutputType = std::vector<o2::emcal::TriggerRecord>; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; specs.push_back(makeWriterSpec("emcal-digits-writer", inputType == InputType::Digits ? "emc-filtered-digits.root" : "emcdigits.root", "o2sim", @@ -230,30 +257,41 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, BranchDefinition<TriggerOutputType>{o2::framework::InputSpec{"trigger", "EMC", "DIGITSTRGR", 0}, "EMCALDigitTRGR", "digittrigger-branch-name"}, - BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "EMC", "DIGITSMCTR", 0}, + BranchDefinition<o2::dataformats::MCTruthContainer<o2::emcal::MCLabel>>{o2::framework::InputSpec{"mc", "EMC", "DIGITSMCTR", 0}, "EMCDigitMCTruth", "digitmc-branch-name"})()); } - - if (isEnabled(OutputType::Cells) && inputType == InputType::Digits) { - using DigitOutputType = std::vector<o2::emcal::Cell>; - using TriggerOutputType = std::vector<o2::emcal::TriggerRecord>; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - specs.push_back(makeWriterSpec("emcal-cells-writer", - inputType == InputType::Cells ? "emc-filtered-cells.root" : "emccells.root", - "o2sim", - BranchDefinition<DigitOutputType>{o2::framework::InputSpec{"data", "EMC", "CELLS", 0}, - "EMCCell", - "cell-branch-name"}, - BranchDefinition<TriggerOutputType>{o2::framework::InputSpec{"trigger", "EMC", "CELLSTRGR", 0}, - "EMCACellTRGR", - "celltrigger-branch-name"}, - BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "EMC", "CELLSMCTR", 0}, - "EMCCellMCTruth", - "cellmc-branch-name"})()); + */ + if (isEnabled(OutputType::Cells) && !disableRootOutput) { + if (inputType == InputType::Digits) { + using DigitOutputType = std::vector<o2::emcal::Cell>; + using TriggerOutputType = std::vector<o2::emcal::TriggerRecord>; + specs.push_back(makeWriterSpec("emcal-cells-writer", "emccells.root", "o2sim", + BranchDefinition<DigitOutputType>{o2::framework::InputSpec{"data", "EMC", "CELLS", 0}, + "EMCALCell", + "cell-branch-name"}, + BranchDefinition<TriggerOutputType>{o2::framework::InputSpec{"trigger", "EMC", "CELLSTRGR", 0}, + "EMCALCellTRGR", + "celltrigger-branch-name"}, + BranchDefinition<o2::dataformats::MCTruthContainer<o2::emcal::MCLabel>>{o2::framework::InputSpec{"mc", "EMC", "CELLSMCTR", 0}, + "EMCALCellMCTruth", + "cellmc-branch-name"})()); + } else { + using CellsDataType = std::vector<o2::emcal::Cell>; + using TriggerRecordDataType = std::vector<o2::emcal::TriggerRecord>; + specs.push_back(makeWriterSpec_CellsTR("emcal-cells-writer", + "emccells.root", + "o2sim", + BranchDefinition<CellsDataType>{o2::framework::InputSpec{"data", "EMC", "CELLS", 0}, + "EMCALCell", + "cell-branch-name"}, + BranchDefinition<TriggerRecordDataType>{o2::framework::InputSpec{"trigger", "EMC", "CELLSTRGR", 0}, + "EMCALCellTRGR", + "celltrigger-branch-name"})()); + } } - if (isEnabled(OutputType::Clusters)) { + if (isEnabled(OutputType::Clusters) && !disableRootOutput) { using ClusterOutputType = std::vector<o2::emcal::Cluster>; using ClusterIndicesOutputType = std::vector<o2::emcal::ClusterIndex>; using TriggerOutputType = std::vector<o2::emcal::TriggerRecord>; @@ -262,20 +300,20 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, "emcclusters.root", "o2sim", BranchDefinition<ClusterOutputType>{o2::framework::InputSpec{"clusters", "EMC", "CLUSTERS", 0}, - "EMCCluster", + "EMCALCluster", "cluster-branch-name"}, BranchDefinition<ClusterIndicesOutputType>{o2::framework::InputSpec{"clusterindices", "EMC", "INDICES", 0}, - "EMCClusterInputIndex", + "EMCALClusterInputIndex", "clusterdigitindices-branch-name"}, BranchDefinition<TriggerOutputType>{o2::framework::InputSpec{"clusterTRGR", "EMC", "CLUSTERSTRGR", 0}, - "EMCClusterTRGR", + "EMCALClusterTRGR", "clustertrigger-branch-name"}, BranchDefinition<TriggerOutputType>{o2::framework::InputSpec{"indicesTRGR", "EMC", "INDICESTRGR", 0}, "EMCIndicesTRGR", "indicestrigger-branch-name"})()); } - if (isEnabled(OutputType::AnalysisClusters)) { + if (isEnabled(OutputType::AnalysisClusters) && !disableRootOutput) { using AnalysisClusterOutputType = std::vector<o2::emcal::AnalysisCluster>; specs.push_back(makeWriterSpec_AnalysisCluster("emcal-analysis-clusters-writer", @@ -286,20 +324,6 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, "cluster-branch-name"})()); } - if (isEnabled(OutputType::Cells) && inputType == InputType::Raw) { - using CellsDataType = std::vector<o2::emcal::Cell>; - using TriggerRecordDataType = std::vector<o2::emcal::TriggerRecord>; - specs.push_back(makeWriterSpec_CellsTR("emcal-cells-writer", - "emccells.root", - "o2sim", - BranchDefinition<CellsDataType>{o2::framework::InputSpec{"data", "EMC", "CELLS", 0}, - "EMCCell", - "cell-branch-name"}, - BranchDefinition<TriggerRecordDataType>{o2::framework::InputSpec{"trigger", "EMC", "CELLSTRGR", 0}, - "EMCACellTRGR", - "celltrigger-branch-name"})()); - } - return std::move(specs); } diff --git a/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx b/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx index 7ac241a1afffe..b4579e5848808 100644 --- a/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx @@ -27,9 +27,11 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) { std::vector<o2::framework::ConfigParamSpec> options{ - {"input-type", o2::framework::VariantType::String, "digits", {"digitizer, digits, raw, clusters"}}, - {"output-type", o2::framework::VariantType::String, "digits", {"digits, raw, clusters"}}, + {"input-type", o2::framework::VariantType::String, "digits", {"digits, cells, raw, clusters"}}, + {"output-type", o2::framework::VariantType::String, "cells", {"digits, cells, raw, clusters, analysisclusters"}}, {"enable-digits-printer", o2::framework::VariantType::Bool, false, {"enable digits printer component"}}, + {"disable-root-input", o2::framework::VariantType::Bool, false, {"do not initialize root files readers"}}, + {"disable-root-output", o2::framework::VariantType::Bool, false, {"do not initialize root file writers"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable sending of MC information"}}, }; std::swap(workflowOptions, options); @@ -55,6 +57,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co return o2::emcal::reco_workflow::getWorkflow(not cfgc.options().get<bool>("disable-mc"), // cfgc.options().get<bool>("enable-digits-printer"), // cfgc.options().get<std::string>("input-type"), // - cfgc.options().get<std::string>("output-type") // - ); + cfgc.options().get<std::string>("output-type"), // + cfgc.options().get<bool>("disable-root-input"), + cfgc.options().get<bool>("disable-root-output")); } diff --git a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..a6e34eddf3e95 --- /dev/null +++ b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "EMCALWorkflow/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::emcal::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/FIT/CMakeLists.txt b/Detectors/FIT/CMakeLists.txt index c93aa1ccab557..62bf1cd0a9b11 100644 --- a/Detectors/FIT/CMakeLists.txt +++ b/Detectors/FIT/CMakeLists.txt @@ -10,7 +10,7 @@ add_subdirectory(FT0) add_subdirectory(common) +add_subdirectory(raw) add_subdirectory(FV0) add_subdirectory(FDD) -add_subdirectory(workflow) add_subdirectory(macros) diff --git a/Detectors/FIT/FDD/base/include/FDDBase/Constants.h b/Detectors/FIT/FDD/base/include/FDDBase/Constants.h index dbf0e9557f936..b5ff8e91fbea4 100644 --- a/Detectors/FIT/FDD/base/include/FDDBase/Constants.h +++ b/Detectors/FIT/FDD/base/include/FDDBase/Constants.h @@ -22,6 +22,8 @@ namespace o2 namespace fdd { constexpr short Nchannels = 16; +constexpr short Nmodules = 2; +constexpr short NChPerMod = 8; constexpr short Ntriggers = 5; constexpr float IntTimeRes = 0.4; constexpr float PhotoCathodeEfficiency = 0.18; diff --git a/Detectors/FIT/FDD/base/src/Geometry.cxx b/Detectors/FIT/FDD/base/src/Geometry.cxx index 32babeae94a83..370dd1c226afb 100644 --- a/Detectors/FIT/FDD/base/src/Geometry.cxx +++ b/Detectors/FIT/FDD/base/src/Geometry.cxx @@ -121,7 +121,6 @@ void Geometry::buildGeometry() vFDAarray->AddNode(secFDA, 3, Rz180); // ---------------> x vFDAarray->AddNode(secFDA, 4, Rx180); // 3 | 4 - /// FDD_C in the tunnel new TGeoBBox("shFDCbox", kFDCCellSideX / 2.0, kFDCCellSideY / 2.0, kFDCCelldz / 2.0); diff --git a/Detectors/FIT/FDD/reconstruction/CMakeLists.txt b/Detectors/FIT/FDD/reconstruction/CMakeLists.txt index a613f20a930bd..fe2598fa38f4d 100644 --- a/Detectors/FIT/FDD/reconstruction/CMakeLists.txt +++ b/Detectors/FIT/FDD/reconstruction/CMakeLists.txt @@ -10,8 +10,14 @@ o2_add_library(FDDReconstruction SOURCES src/Reconstructor.cxx - PUBLIC_LINK_LIBRARIES O2::DataFormatsFDD) + src/ReadRaw.cxx + src/CTFCoder.cxx + PUBLIC_LINK_LIBRARIES O2::FDDBase + O2::DataFormatsFDD + O2::DetectorsRaw) o2_target_root_dictionary( FDDReconstruction - HEADERS include/FDDReconstruction/Reconstructor.h) + HEADERS include/FDDReconstruction/Reconstructor.h + include/FDDReconstruction/ReadRaw.h + include/FDDReconstruction/CTFCoder.h) diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h new file mode 100644 index 0000000000000..9d621f75cbbd6 --- /dev/null +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -0,0 +1,193 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of FDD digits data + +#ifndef O2_FDD_CTFCODER_H +#define O2_FDD_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include "FDDBase/Geometry.h" +#include "DataFormatsFDD/CTF.h" +#include "DataFormatsFDD/Digit.h" +#include "DataFormatsFDD/ChannelData.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" + +class TTree; + +namespace o2 +{ +namespace fdd +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::FDD) {} + ~CTFCoder() = default; + + /// entropy-encode digits to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec); + + /// entropy decode clusters from buffer with CTF + template <typename VDIG, typename VCHAN> + void decode(const CTF::base& ec, VDIG& digitVec, VCHAN& channelVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + /// compres digits clusters to CompressedDigits + void compress(CompressedDigits& cd, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec); + size_t estimateCompressedSize(const CompressedDigits& cc); + + /// decompress CompressedDigits to digits + template <typename VDIG, typename VCHAN> + void decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& channelVec); + + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<Digit>& digitVec, std::vector<ChannelData>& channelVec); + + ClassDefNV(CTFCoder, 1); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec) +{ + 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_idChan + MD::EENCODE, // BLC_time + MD::EENCODE, // BLC_charge + MD::EENCODE // BLC_feeBits + }; + CompressedDigits cd; + compress(cd, digitVec, channelVec); + + // book output size with some margin + auto szIni = estimateCompressedSize(cd); + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(cd.header); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODEFDD(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODEFDD(cd.trigger, CTF::BLC_trigger, 0); + ENCODEFDD(cd.bcInc, CTF::BLC_bcInc, 0); + ENCODEFDD(cd.orbitInc, CTF::BLC_orbitInc, 0); + ENCODEFDD(cd.nChan, CTF::BLC_nChan, 0); + + ENCODEFDD(cd.idChan , CTF::BLC_idChan, 0); + ENCODEFDD(cd.time, CTF::BLC_time, 0); + ENCODEFDD(cd.charge, CTF::BLC_charge, 0); + ENCODEFDD(cd.feeBits, CTF::BLC_feeBits, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VDIG, typename VCHAN> +void CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& channelVec) +{ + CompressedDigits cd; + cd.header = ec.getHeader(); + ec.print(getPrefix()); +#define DECODEFDD(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODEFDD(cd.trigger, CTF::BLC_trigger); + DECODEFDD(cd.bcInc, CTF::BLC_bcInc); + DECODEFDD(cd.orbitInc, CTF::BLC_orbitInc); + DECODEFDD(cd.nChan, CTF::BLC_nChan); + + DECODEFDD(cd.idChan, CTF::BLC_idChan); + DECODEFDD(cd.time, CTF::BLC_time); + DECODEFDD(cd.charge, CTF::BLC_charge); + DECODEFDD(cd.feeBits, CTF::BLC_feeBits); + // clang-format on + // + decompress(cd, digitVec, channelVec); +} + +/// decompress compressed digits to standard digits +template <typename VDIG, typename VCHAN> +void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& channelVec) +{ + digitVec.clear(); + channelVec.clear(); + digitVec.reserve(cd.header.nTriggers); + channelVec.reserve(cd.idChan.size()); + + uint32_t firstEntry = 0, clCount = 0, chipCount = 0; + o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); + + for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { + // restore ROFRecord + if (cd.orbitInc[idig]) { // non-0 increment => new orbit + ir.bc = cd.bcInc[idig]; // bcInc has absolute meaning + ir.orbit += cd.orbitInc[idig]; + } else { + ir.bc += cd.bcInc[idig]; + } + Triggers trig; + trig.triggersignals = cd.trigger[idig]; + + firstEntry = channelVec.size(); + uint8_t chID = 0; + int amplA = 0, amplC = 0, timeA = 0, timeC = 0; + for (uint8_t ic = 0; ic < cd.nChan[idig]; ic++) { + auto icc = channelVec.size(); + const auto& chan = channelVec.emplace_back((chID += cd.idChan[icc]), cd.time[icc], cd.charge[icc], cd.feeBits[icc]); + // + // rebuild digit + if (chan.mPMNumber > 7) { // A side + amplA += chan.mChargeADC; + timeA += chan.mTime; + trig.nChanA++; + + } else { + amplC += chan.mChargeADC; + timeC += chan.mTime; + trig.nChanC++; + } + } + if (trig.nChanA) { + trig.timeA = timeA / trig.nChanA; + trig.amplA = amplA * 0.125; + } + if (trig.nChanC) { + trig.timeC = timeC / trig.nChanC; + trig.amplC = amplC * 0.125; + } + digitVec.emplace_back(firstEntry, cd.nChan[idig], ir, trig); + } +} + +} // namespace fdd +} // namespace o2 + +#endif // O2_FDD_CTFCODER_H diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h new file mode 100644 index 0000000000000..e5547217f285e --- /dev/null +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ReadRaw.h +/// \brief Reads raw data and converts to digits +/// \author Maciej.Slupecki@cern.ch, arvind.khuntia@cern.ch, based on the FT0 code +// RAW data format description: DataFormat/Detectors/FIT/FDD/RawEventData + +#ifndef ALICEO2_FDD_READRAW_H_ +#define ALICEO2_FDD_READRAW_H_ + +#include <fstream> +#include <iostream> +#include <iomanip> +#include <map> +#include <string> +#include <sstream> +#include <vector> +#include "TBranch.h" +#include "TTree.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFDD/Digit.h" +#include "DataFormatsFDD/ChannelData.h" +#include "DataFormatsFDD/LookUpTable.h" +#include "DataFormatsFDD/RawEventData.h" + +namespace o2 +{ +namespace fdd +{ +class ReadRaw +{ + public: + ReadRaw() = default; + ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath = "fdd.raw", const std::string outputRawFilePath = "fdddigitsFromRaw.root"); + void readRawData(const LookUpTable& lut); + void writeDigits(const std::string& outputDigitsFilePath); + void close(); + + private: + std::ifstream mRawFileIn; + std::map<o2::InteractionRecord, std::vector<ChannelData>> mDigitAccum; // digit accumulator + + template <typename T> + TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr) + { + if (auto br = tree.GetBranch(brname.c_str())) { + br->SetAddress(static_cast<void*>(ptr)); + return br; + } + // otherwise make it + return tree.Branch(brname.c_str(), ptr); + } + + ClassDefNV(ReadRaw, 1); +}; + +} // namespace fdd +} // namespace o2 +#endif diff --git a/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..82050cd60993a --- /dev/null +++ b/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,160 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of FDD digits data + +#include "FDDReconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::fdd; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, + std::vector<Digit>& digitVec, std::vector<ChannelData>& channelVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, digitVec, channelVec); +} + +///________________________________ +void CTFCoder::compress(CompressedDigits& cd, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec) +{ + // convert digits/channel to their compressed version + cd.clear(); + if (!digitVec.size()) { + return; + } + const auto& dig0 = digitVec[0]; + cd.header.nTriggers = digitVec.size(); + cd.header.firstOrbit = dig0.mIntRecord.orbit; + cd.header.firstBC = dig0.mIntRecord.bc; + + cd.trigger.resize(cd.header.nTriggers); + cd.bcInc.resize(cd.header.nTriggers); + cd.orbitInc.resize(cd.header.nTriggers); + cd.nChan.resize(cd.header.nTriggers); + + cd.idChan.resize(channelVec.size()); + cd.time.resize(channelVec.size()); + cd.charge.resize(channelVec.size()); + cd.feeBits.resize(channelVec.size()); + + uint16_t prevBC = cd.header.firstBC; + uint32_t prevOrbit = cd.header.firstOrbit; + uint32_t ccount = 0; + for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { + const auto& digit = digitVec[idig]; + const auto chanels = digit.getBunchChannelData(channelVec); // we assume the channels are sorted + + // fill trigger info + cd.trigger[idig] = digit.mTriggers.triggersignals; + if (prevOrbit == digit.mIntRecord.orbit) { + cd.bcInc[idig] = digit.mIntRecord.bc - prevBC; + cd.orbitInc[idig] = 0; + } else { + cd.bcInc[idig] = digit.mIntRecord.bc; + cd.orbitInc[idig] = digit.mIntRecord.orbit - prevOrbit; + } + prevBC = digit.mIntRecord.bc; + prevOrbit = digit.mIntRecord.orbit; + // fill channels info + cd.nChan[idig] = chanels.size(); + if (!cd.nChan[idig]) { + LOG(ERROR) << "Digits with no channels"; + continue; + } + uint8_t prevChan = 0; + for (uint8_t ic = 0; ic < cd.nChan[idig]; ic++) { + assert(prevChan <= chanels[ic].mPMNumber); + cd.idChan[ccount] = chanels[ic].mPMNumber - prevChan; + cd.time[ccount] = chanels[ic].mTime; // make sure it fits to short!!! + cd.charge[ccount] = chanels[ic].mChargeADC; // make sure we really need short!!! + cd.feeBits[ccount] = chanels[ic].mFEEBits; + prevChan = chanels[ic].mPMNumber; + ccount++; + } + } +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + CompressedDigits cd; // just to get member types +#define MAKECODER(part, slot) createCoder<decltype(part)::value_type>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(cd.trigger, CTF::BLC_trigger); + MAKECODER(cd.bcInc, CTF::BLC_bcInc); + MAKECODER(cd.orbitInc, CTF::BLC_orbitInc); + MAKECODER(cd.nChan, CTF::BLC_nChan); + + MAKECODER(cd.idChan, CTF::BLC_idChan); + MAKECODER(cd.time, CTF::BLC_time); + MAKECODER(cd.charge, CTF::BLC_charge); + MAKECODER(cd.feeBits, CTF::BLC_feeBits); + // clang-format on +} + +///________________________________ +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<decltype(vec)>::type::value_type +#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ + rans::calculateMaxBufferSize(vec.size(), reinterpret_cast<const o2::rans::LiteralEncoder64<VTP(vec)>*>(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.idChan, CTF::BLC_idChan); + sz += ESTSIZE(cd.time, CTF::BLC_time); + sz += ESTSIZE(cd.charge, CTF::BLC_charge); + sz += ESTSIZE(cd.feeBits, CTF::BLC_feeBits); + // clang-format on + + LOG(INFO) << "Estimated output size is " << sz << " bytes"; + return sz; +} diff --git a/Detectors/FIT/FDD/reconstruction/src/FDDReconstructionLinkDef.h b/Detectors/FIT/FDD/reconstruction/src/FDDReconstructionLinkDef.h index 864a7a08ccd4c..741a85871df65 100644 --- a/Detectors/FIT/FDD/reconstruction/src/FDDReconstructionLinkDef.h +++ b/Detectors/FIT/FDD/reconstruction/src/FDDReconstructionLinkDef.h @@ -15,5 +15,6 @@ #pragma link off all functions; #pragma link C++ class o2::fdd::Reconstructor + ; +#pragma link C++ class o2::fdd::ReadRaw + ; #endif diff --git a/Detectors/FIT/FDD/reconstruction/src/ReadRaw.cxx b/Detectors/FIT/FDD/reconstruction/src/ReadRaw.cxx new file mode 100644 index 0000000000000..a0935f192141e --- /dev/null +++ b/Detectors/FIT/FDD/reconstruction/src/ReadRaw.cxx @@ -0,0 +1,169 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FDDReconstruction/ReadRaw.h" +#include "DetectorsRaw/RDHUtils.h" +#include "Framework/Logger.h" +#include "TFile.h" + +using namespace o2::fdd; +using RDHUtils = o2::raw::RDHUtils; + +ClassImp(ReadRaw); + +//_____________________________________________________________________________________ +ReadRaw::ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath, const std::string outputDigitsFilePath) +{ + LOG(INFO) << "o2::fdd::ReadRaw::ReadRaw(): Read Raw file: " << inputRawFilePath.data() << " and convert to: " << outputDigitsFilePath.data(); + mRawFileIn.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mRawFileIn.open(inputRawFilePath, std::fstream::in | std::fstream::binary); + LookUpTable lut(true); + ReadRaw::readRawData(lut); + ReadRaw::writeDigits(outputDigitsFilePath.data()); +} +//_____________________________________________________________________________________ +void ReadRaw::readRawData(const LookUpTable& lut) +{ + LOG(INFO) << "o2::fdd::ReadRaw::readRawData():Start."; + constexpr int CRUWordSize = 16; + o2::header::RAWDataHeader mRDH; + ChannelData chData; // output to store digits + EventHeader eventHeader; // raw data container + TCMdata mTCMdata; // raw data container + EventData eventData[NChPerMod]; // raw data container + + // Get input RAW file size + mRawFileIn.seekg(0, mRawFileIn.end); + long rawFileSize = mRawFileIn.tellg(); + mRawFileIn.seekg(0); + + long posInFile = 0; + while (posInFile < rawFileSize - sizeof(mRDH)) { + // Read contents of the next RDH + mRawFileIn.seekg(posInFile); + mRawFileIn.read(reinterpret_cast<char*>(&mRDH), sizeof(mRDH)); + int nwords = RDHUtils::getMemorySize(mRDH); + int offset = RDHUtils::getOffsetToNext(mRDH); + int link = RDHUtils::getLinkID(mRDH); + posInFile += offset; // posInFile is now prepared for the next iteration of the main loop + + int posPayload = 0; + if (nwords > sizeof(mRDH)) { + // RDHUtils::printRDH(mRDH); + // Read the payload following the RDH (only if there is anything to read) + posPayload = int(sizeof(mRDH)); + + while (posPayload < nwords) { + mRawFileIn.read(reinterpret_cast<char*>(&eventHeader), sizeof(eventHeader)); + posPayload += sizeof(eventHeader); + LOG(DEBUG) << " Read internal EventHeader for link: " << link + << " nWords: " << (int)eventHeader.nGBTWords + << " orbit: " << int(eventHeader.orbit) + << " BC: " << int(eventHeader.bc) + << " posInFile: " << posInFile + << " posPayload: " << posPayload; + o2::InteractionRecord intrec{uint16_t(eventHeader.bc), uint32_t(eventHeader.orbit)}; + + if (link == lut.getTcmLink()) { // is TCM payload + mRawFileIn.read(reinterpret_cast<char*>(&mTCMdata), sizeof(mTCMdata)); + posPayload += sizeof(mTCMdata); + LOG(DEBUG) << " Read TCM: posPayload: " << posPayload + << " posInFile: " << posInFile; + } else { // is PM payload + posPayload += CRUWordSize - o2::fdd::EventHeader::PayloadSize; // padding is enabled + for (int i = 0; i < eventHeader.nGBTWords; ++i) { + mRawFileIn.read(reinterpret_cast<char*>(&eventData[2 * i]), o2::fdd::EventData::PayloadSizeFirstWord); + posPayload += o2::fdd::EventData::PayloadSizeFirstWord; + chData = {Short_t(lut.getChannel(link, int(eventData[2 * i].channelID))), + Float_t(eventData[2 * i].time), + Short_t(eventData[2 * i].charge), 0}; + mDigitAccum[intrec].emplace_back(chData); + LOG(DEBUG) << " Read 1st half-word: (PMchannel, globalChannel, Q, T, posPayload) = " + << std::setw(3) << int(eventData[2 * i].channelID) + << std::setw(4) << lut.getChannel(link, int(eventData[2 * i].channelID)) + << std::setw(5) << int(eventData[2 * i].charge) + << std::setw(5) << float(eventData[2 * i].time) + << std::setw(5) << posPayload; + + Short_t channelIdFirstHalfWord = chData.mPMNumber; + + mRawFileIn.read(reinterpret_cast<char*>(&eventData[2 * i + 1]), EventData::PayloadSizeSecondWord); + posPayload += o2::fdd::EventData::PayloadSizeSecondWord; + chData = {Short_t(lut.getChannel(link, (eventData[2 * i + 1].channelID))), + Float_t(eventData[2 * i + 1].time), + Short_t(eventData[2 * i + 1].charge), 0}; + if (chData.mPMNumber <= channelIdFirstHalfWord) { + // Don't save the second half-word if it is only filled with zeroes (empty-data) + // TODO: Verify if it works correctly with real data from readout + continue; + } + mDigitAccum[intrec].emplace_back(chData); + LOG(DEBUG) << " Read 2nd half-word: (PMchannel, globalChannel, Q, T, posPayload) = " + << std::setw(3) << int(eventData[2 * i + 1].channelID) + << std::setw(4) << lut.getChannel(link, int(eventData[2 * i + 1].channelID)) + << std::setw(5) << int(eventData[2 * i + 1].charge) + << std::setw(5) << float(eventData[2 * i + 1].time) + << std::setw(5) << posPayload; + } + } + } + } + } + close(); + LOG(INFO) << "o2::fdd::ReadRaw::readRawData():Finished."; +} +//_____________________________________________________________________________________ +void ReadRaw::close() +{ + if (mRawFileIn.is_open()) { + mRawFileIn.close(); + } +} +//_____________________________________________________________________________________ +void ReadRaw::writeDigits(const std::string& outputDigitsFilePath) +{ + TFile* outFile = new TFile(outputDigitsFilePath.data(), "RECREATE"); + if (!outFile || outFile->IsZombie()) { + LOG(ERROR) << "Failed to open " << outputDigitsFilePath << " output file"; + } else { + LOG(INFO) << "o2::fdd::ReadRaw::writeDigits(): Opened output file: " << outputDigitsFilePath; + } + TTree* outTree = new TTree("o2sim", "o2sim"); + std::vector<ChannelData> chDataVecTree; + std::vector<Digit> chBcVecTree; + + for (auto& digit : mDigitAccum) { + LOG(DEBUG) << " IR (" << digit.first << ") (i, PMT, Q, T):"; + for (uint16_t i = 0; i < digit.second.size(); i++) { + ChannelData* chd = &(digit.second.at(i)); + LOG(DEBUG) << " " << std::setw(3) << i + << std::setw(4) << chd->mPMNumber + << std::setw(5) << chd->mChargeADC + << std::setw(5) << chd->mTime; + } + + size_t nStored = 0; + size_t first = chDataVecTree.size(); + for (auto& sec : digit.second) { + chDataVecTree.emplace_back(int(sec.mPMNumber), float(sec.mTime), short(sec.mChargeADC), short(0)); + nStored++; + } + chBcVecTree.emplace_back(first, nStored, digit.first, Triggers()); + } + + outTree->Branch("FDDDigit", &chBcVecTree); + outTree->Branch("FDDDigitCh", &chDataVecTree); + outTree->Fill(); + + outFile->cd(); + outTree->Write(); + outFile->Close(); + LOG(INFO) << "o2::fdd::ReadRaw::writeDigits(): Finished converting " << chBcVecTree.size() << " events."; +} diff --git a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx index 30cc96d5a0aa6..7bfcca22dedb1 100644 --- a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx +++ b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx @@ -28,11 +28,13 @@ void Reconstructor::process(const o2::fdd::Digit& digitBC, gsl::span<const o2::f Float_t adc = channel.mChargeADC; Float_t time = channel.mTime; //LOG(INFO) <<adc <<" "<<time; - if (time == o2::InteractionRecord::DummyTime) + if (time == o2::InteractionRecord::DummyTime) { continue; + } Float_t timeErr = 1; - if (adc > 1) + if (adc > 1) { timeErr = 1 / adc; + } if (channel.mPMNumber < 8) { timeFDC += time / (timeErr * timeErr); weightFDC += 1. / (timeErr * timeErr); diff --git a/Detectors/FIT/FDD/simulation/CMakeLists.txt b/Detectors/FIT/FDD/simulation/CMakeLists.txt index 85bd2f2e602de..e828d6481c0b2 100644 --- a/Detectors/FIT/FDD/simulation/CMakeLists.txt +++ b/Detectors/FIT/FDD/simulation/CMakeLists.txt @@ -11,10 +11,25 @@ o2_add_library(FDDSimulation SOURCES src/Detector.cxx src/Digitizer.cxx - PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::FDDBase O2::DataFormatsFDD + src/Digits2Raw.cxx + PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat + O2::FDDBase + O2::DataFormatsFDD + O2::DetectorsRaw ROOT::Physics) o2_target_root_dictionary(FDDSimulation HEADERS include/FDDSimulation/Detector.h include/FDDSimulation/Digitizer.h - include/FDDSimulation/DigitizationParameters.h) + include/FDDSimulation/DigitizationParameters.h + include/FDDSimulation/Digits2Raw.h) + +o2_add_executable(digit2raw + COMPONENT_NAME fdd + SOURCES src/digit2raw.cxx + PUBLIC_LINK_LIBRARIES O2::FDDSimulation + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils + Boost::program_options) + diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Detector.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Detector.h index 2f5862e960568..9ea50b4ee45fb 100644 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Detector.h +++ b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Detector.h @@ -52,8 +52,9 @@ class Detector : public o2::base::DetImpl<Detector> std::vector<o2::fdd::Hit>* getHits(Int_t iColl) { - if (iColl == 0) + if (iColl == 0) { return mHits; + } return nullptr; } diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h new file mode 100644 index 0000000000000..4df9fb96c70fd --- /dev/null +++ b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h @@ -0,0 +1,68 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Digits2Raw.h +/// \brief converts digits to raw format +/// \author Maciej.Slupecki@cern.ch +// based on FV0 + +#ifndef ALICEO2_FDD_DIGITS2RAW_H_ +#define ALICEO2_FDD_DIGITS2RAW_H_ + +#include "Headers/RAWDataHeader.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFDD/RawEventData.h" +#include "DataFormatsFDD/LookUpTable.h" +#include "DataFormatsFDD/ChannelData.h" +#include "DataFormatsFDD/Digit.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsRaw/RawFileWriter.h" +#include <FairLogger.h> +#include <TStopwatch.h> +#include <iostream> +#include <string> +#include <vector> +#include <gsl/span> + +namespace o2 +{ +namespace fdd +{ +class Digits2Raw +{ + public: + Digits2Raw() = default; + void readDigits(const std::string& outDir, const std::string& fileDigitsName); + void convertDigits(o2::fdd::Digit bcdigits, + gsl::span<const ChannelData> pmchannels, + const o2::fdd::LookUpTable& lut); + + o2::raw::RawFileWriter& getWriter() { return mWriter; } + void setFilePerLink(bool v) { mOutputPerLink = v; } + bool getFilePerLink() const { return mOutputPerLink; } + + private: + static constexpr uint32_t sTcmLink = 2; + static constexpr uint16_t sCruId = 0; + static constexpr uint32_t sEndPointId = sCruId; + + void makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord); + void fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir); + RawEventData mRawEventData; + o2::raw::RawFileWriter mWriter{"FDD"}; + bool mOutputPerLink = false; + ///////////////////////////////////////////////// + + ClassDefNV(Digits2Raw, 1); +}; + +} // namespace fdd +} // namespace o2 +#endif diff --git a/Detectors/FIT/FDD/simulation/src/Digitizer.cxx b/Detectors/FIT/FDD/simulation/src/Digitizer.cxx index e3909e028b80f..fdb232dc95d66 100644 --- a/Detectors/FIT/FDD/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FDD/simulation/src/Digitizer.cxx @@ -90,17 +90,19 @@ void Digitizer::createPulse(int nPhE, int parID, double timeHit, std::array<o2:: double time0 = cachedIR[0].bc2ns(); // start time of the 1st cashed BC float timeDiff = time0 - timeHit; - if (channel < 9) + if (channel < 9) { timeDiff += parameters.TimeDelayFDC; - else + } else { timeDiff += parameters.TimeDelayFDA; + } //LOG(INFO) <<"Ch = "<<channel<<" NphE = " << nPhE <<" timeDiff "<<timeDiff; float charge = TMath::Qe() * parameters.PmGain * mBinSize / (mPmtTimeIntegral * ChargePerADC); Bool_t added[nCachedIR]; - for (int ir = 0; ir < nCachedIR; ir++) + for (int ir = 0; ir < nCachedIR; ir++) { added[ir] = kFALSE; + } constexpr float BinSizeInv = 1.0 / mBinSize; for (int iPhE = 0; iPhE < nPhE; ++iPhE) { @@ -195,8 +197,9 @@ void Digitizer::storeBC(const BCCache& bc, int nBC = digitsBC.size(); digitsBC.emplace_back(first, 16, bc, mTriggers); - for (const auto& lbl : bc.labels) + for (const auto& lbl : bc.labels) { labels.addElement(nBC, lbl); + } } //_____________________________________________________________________________ @@ -221,10 +224,11 @@ float Digitizer::simulateTimeCFD(const ChannelBCDataF& pulse) int binShift = TMath::Nint(parameters.TimeShiftCFD / mBinSize); for (int iBin = 0; iBin < NTimeBinsPerBC; ++iBin) { //if (mTime[channel][iBin] != 0) std::cout << mTime[channel][iBin] / parameters.mChargePerADC << ", "; - if (iBin >= binShift) + if (iBin >= binShift) { mTimeCFD[iBin] = 5.0 * pulse[iBin - binShift] - pulse[iBin]; - else + } else { mTimeCFD[iBin] = -1.0 * pulse[iBin]; + } } for (int iBin = 1; iBin < NTimeBinsPerBC; ++iBin) { if (mTimeCFD[iBin - 1] < 0 && mTimeCFD[iBin] >= 0) { @@ -328,8 +332,9 @@ void Digitizer::finish() {} int Digitizer::simulateLightYield(int pmt, int nPhot) { const float p = parameters.LightYield * PhotoCathodeEfficiency; - if (p == 1.0f || nPhot == 0) + if (p == 1.0f || nPhot == 0) { return nPhot; + } const int n = int(nPhot < 100 ? gRandom->Binomial(nPhot, p) : gRandom->Gaus(p * nPhot + 0.5, TMath::Sqrt(p * (1 - p) * nPhot))); return n; } @@ -350,8 +355,9 @@ Double_t Digitizer::SinglePhESpectrum(Double_t* x, Double_t*) { // this function describes the PM amplitude response to a single photoelectron Double_t y = x[0]; - if (y < 0) + if (y < 0) { return 0; + } return (TMath::Poisson(y, PMNbOfSecElec) + PMTransparency * TMath::Poisson(y, 1.0)); } //______________________________________________________________ @@ -361,8 +367,9 @@ void Digitizer::BCCache::print() const for (int ic = 0; ic < 16; ic++) { printf("Ch[%d] | ", ic); for (int ib = 0; ib < NTimeBinsPerBC; ib++) { - if (ib % 10 == 0) + if (ib % 10 == 0) { printf("%f ", pulse[ic][ib]); + } } printf("\n"); } diff --git a/Detectors/FIT/FDD/simulation/src/Digits2Raw.cxx b/Detectors/FIT/FDD/simulation/src/Digits2Raw.cxx new file mode 100644 index 0000000000000..94fa25500a227 --- /dev/null +++ b/Detectors/FIT/FDD/simulation/src/Digits2Raw.cxx @@ -0,0 +1,173 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// RAW data format - DataFormat/Detectors/FIT/FDD/RawEventData + +#include "FDDBase/Constants.h" +#include "FDDSimulation/Digits2Raw.h" +#include <TTree.h> +#include <cassert> + +using namespace o2::fdd; + +ClassImp(Digits2Raw); + +//_____________________________________________________________________________________ +void Digits2Raw::readDigits(const std::string& outDir, const std::string& fileDigitsName) +{ + LOG(INFO) << "==============FDD: Digits2Raw::convertDigits" << std::endl; + LookUpTable lut(true); + + std::string outd = outDir; + if (outd.back() != '/') { + outd += '/'; + } + + // Register PM links linearly + for (int iPmLink = 0; iPmLink < Nmodules; ++iPmLink) { + uint16_t feeId = uint16_t(iPmLink); + uint8_t linkId = uint8_t(iPmLink); + std::string outFileLink = mOutputPerLink ? (outd + "fdd_link" + std::to_string(iPmLink) + ".raw") : (outd + "fdd.raw"); + LOG(INFO) << " Register PM link: " << iPmLink << " to file: " << outFileLink; + mWriter.registerLink(feeId, sCruId, linkId, sEndPointId, outFileLink); + } + + // Register TCM link separately + std::string outFileLink = mOutputPerLink ? (outd + "fdd_link" + std::to_string(sTcmLink) + ".raw") : (outd + "fdd.raw"); + LOG(INFO) << " Register TCM link: " << outFileLink; + mWriter.registerLink(uint16_t(sTcmLink), sCruId, sTcmLink, sEndPointId, outFileLink); + + TFile* fdig = TFile::Open(fileDigitsName.data()); + assert(fdig != nullptr); + LOG(INFO) << "Open digits file: " << fileDigitsName.data(); + + TTree* digTree = (TTree*)fdig->Get("o2sim"); + std::vector<o2::fdd::Digit> digitsBC, *fddDigitPtr = &digitsBC; + std::vector<o2::fdd::ChannelData> digitsCh, *fddChDataPtr = &digitsCh; + digTree->SetBranchAddress("FDDDigit", &fddDigitPtr); + digTree->SetBranchAddress("FDDDigitCh", &fddChDataPtr); + + for (int ient = 0; ient < digTree->GetEntries(); ient++) { + digTree->GetEntry(ient); + int nbc = digitsBC.size(); + for (int ibc = 0; ibc < nbc; ibc++) { + auto& bcd = digitsBC[ibc]; + auto channels = bcd.getBunchChannelData(digitsCh); + + if (!channels.empty()) { + LOG(DEBUG); + LOG(INFO) << "o2::fdd::Digits2Raw::readDigits(): Start to convertDigits() at ibc = " << ibc << " " << bcd.mIntRecord + << " iCh0:" << bcd.ref.getFirstEntry() << " nentries:" << bcd.ref.getEntries(); + convertDigits(bcd, channels, lut); + } + } + } +} +//_____________________________________________________________________________________ +void Digits2Raw::convertDigits(o2::fdd::Digit bcdigits, gsl::span<const ChannelData> pmchannels, + const o2::fdd::LookUpTable& lut) +{ + const o2::InteractionRecord intRecord = bcdigits.getIntRecord(); + int prevPmLink = -1; + int iChannelPerLink = 0; + int nch = pmchannels.size(); + + std::stringstream ss; + ss << " Number of channels: " << nch << " (Ch, PMT, Q, T)\n"; + for (int ich = 0; ich < nch; ich++) { + if (pmchannels[ich].mChargeADC != 0) { + ss << " " << std::setw(2) << ich + << std::setw(3) << pmchannels[ich].mPMNumber + << std::setw(5) << pmchannels[ich].mChargeADC + << std::setw(7) << std::setprecision(3) << pmchannels[ich].mTime << "\n"; + } + } + LOG(DEBUG) << ss.str().substr(0, ss.str().size() - 1); + + for (int ich = 0; ich < nch; ich++) { + int nLinkPm = lut.getLink(pmchannels[ich].mPMNumber); + if (nLinkPm != prevPmLink) { + if (prevPmLink >= 0) { + fillSecondHalfWordAndAddData(iChannelPerLink, prevPmLink, intRecord); + } + makeGBTHeader(mRawEventData.mEventHeader, nLinkPm, intRecord); + iChannelPerLink = 0; + prevPmLink = nLinkPm; + } + if (pmchannels[ich].mChargeADC != 0) { + LOG(DEBUG) << " Store data for channel: " << ich << " PmLink = " << nLinkPm << " "; + auto& newData = mRawEventData.mEventData[iChannelPerLink]; + newData.charge = pmchannels[ich].mChargeADC; + newData.time = pmchannels[ich].mTime; + newData.generateFlags(); + newData.channelID = lut.getModChannel(pmchannels[ich].mPMNumber); + iChannelPerLink++; + } + if (ich == nch - 1) { + fillSecondHalfWordAndAddData(iChannelPerLink, prevPmLink, intRecord); + } + } + + // TCM + makeGBTHeader(mRawEventData.mEventHeader, sTcmLink, intRecord); + mRawEventData.mEventHeader.nGBTWords = 1; + auto& tcmdata = mRawEventData.mTCMdata; + tcmdata.vertex = 1; + tcmdata.orA = 1; + tcmdata.orC = 0; + tcmdata.sCen = 0; + tcmdata.cen = 0; + tcmdata.nChanA = 0; + tcmdata.nChanC = 0; + tcmdata.amplA = 0; + tcmdata.amplC = 0; + tcmdata.timeA = 0; + tcmdata.timeC = 0; + + auto data = mRawEventData.to_vector(kTRUE); //for tcm module + uint32_t linkId = uint32_t(sTcmLink); + uint64_t feeId = uint64_t(sTcmLink); + mWriter.addData(feeId, sCruId, linkId, sEndPointId, intRecord, data); + + // fill mEventData[iChannelPerLink] with 0s to flag that this is a dummy data + uint nGBTWords = uint((iChannelPerLink + 1) / 2); + if ((iChannelPerLink % 2) == 1) { + mRawEventData.mEventData[iChannelPerLink] = {}; + } + mRawEventData.mEventHeader.nGBTWords = nGBTWords; + // LOG(DEBUG) << " last link: " << prevPmLink; +} +//_____________________________________________________________________________________ +void Digits2Raw::makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord) +{ + eventHeader.startDescriptor = 0xf; + eventHeader.reservedField1 = 0; + eventHeader.reservedField2 = 0; + eventHeader.reservedField3 = 0; + eventHeader.bc = mIntRecord.bc; + eventHeader.orbit = mIntRecord.orbit; + LOG(DEBUG) << " makeGBTHeader for link: " << link; +} +//_____________________________________________________________________________________ +void Digits2Raw::fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir) +{ + uint nGBTWords = uint((iChannelPerLink + 1) / 2); + if ((iChannelPerLink % 2) == 1) { + mRawEventData.mEventData[iChannelPerLink] = {}; + LOG(DEBUG) << " Fill up empty second half-word."; + } + mRawEventData.mEventHeader.nGBTWords = nGBTWords; + auto data = mRawEventData.to_vector(false); + uint32_t linkId = uint32_t(prevPmLink); + uint64_t feeId = uint64_t(prevPmLink); + mWriter.addData(feeId, sCruId, linkId, sEndPointId, ir, data); + LOG(DEBUG) << " Switch prevPmLink: " << prevPmLink << ". Save data with nGBTWords=" + << nGBTWords << " in header. Last channel: " << iChannelPerLink; +} diff --git a/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h b/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h index f1383dd0e33dc..d7ba52a55bf9a 100644 --- a/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h +++ b/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h @@ -19,5 +19,6 @@ #pragma link C++ class o2::fdd::Digitizer + ; #pragma link C++ class o2::fdd::DigitizationParameters + ; #pragma link C++ class o2::dataformats::MCTruthContainer < o2::fdd::MCLabel> + ; +#pragma link C++ class o2::fdd::Digits2Raw + ; #endif diff --git a/Detectors/FIT/FDD/simulation/src/digit2raw.cxx b/Detectors/FIT/FDD/simulation/src/digit2raw.cxx new file mode 100644 index 0000000000000..250aecb36059d --- /dev/null +++ b/Detectors/FIT/FDD/simulation/src/digit2raw.cxx @@ -0,0 +1,114 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 digi2raw.cxx +/// \author ruben.shahoyan@cern.ch + +#include <boost/program_options.hpp> +#include <TSystem.h> +#include <TFile.h> +#include <TStopwatch.h> +#include <string> +#include <iomanip> +#include "Framework/Logger.h" +#include "FairLogger.h" +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "DetectorsRaw/HBFUtils.h" +#include "FDDSimulation/Digits2Raw.h" + +/// MC->raw conversion for FDD + +namespace bpo = boost::program_options; + +void digi2raw(const std::string& inpName, const std::string& outDir, bool filePerLink, uint32_t rdhV = 6, bool noEmptyHBF = false, + int superPageSizeInB = 1024 * 1024); + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " (Convert FDD digits to CRU raw 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("input-file,i", bpo::value<std::string>()->default_value(o2::base::NameConf::getDigitsFileName(o2::detectors::DetID::FDD)),"input FDD digits file"); // why not used? + add_option("input-file,i", bpo::value<std::string>()->default_value("fdddigits.root"), "input FDD digits file"); + add_option("file-per-link,l", bpo::value<bool>()->default_value(false)->implicit_value(true), "create output file per CRU (default: per layer)"); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + uint32_t defRDH = o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>(); + add_option("rdh-version,r", bpo::value<uint32_t>()->default_value(defRDH), "RDH version to use"); + add_option("no-empty-hbf,e", bpo::value<bool>()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("configKeyValues", bpo::value<std::string>()->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<std::string>()); + digi2raw(vm["input-file"].as<std::string>(), + vm["output-dir"].as<std::string>(), + vm["file-per-link"].as<bool>(), + vm["rdh-version"].as<uint32_t>(), + vm["no-empty-hbf"].as<bool>()); + + return 0; +} + +void digi2raw(const std::string& inpName, const std::string& outDir, bool filePerLink, uint32_t rdhV, bool noEmptyHBF, int superPageSizeInB) +{ + TStopwatch swTot; + swTot.Start(); + o2::fdd::Digits2Raw m2r; + m2r.setFilePerLink(filePerLink); + auto& wr = m2r.getWriter(); + wr.setSuperPageSize(superPageSizeInB); + wr.useRDHVersion(rdhV); + wr.setDontFillEmptyHBF(noEmptyHBF); + + std::string outDirName(outDir); + if (outDirName.back() != '/') { + outDirName += '/'; + } + // if needed, create output directory + if (gSystem->AccessPathName(outDirName.c_str())) { + if (gSystem->mkdir(outDirName.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outDirName; + } else { + LOG(INFO) << "created output directory " << outDirName; + } + } + + m2r.readDigits(outDirName, inpName); + wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); + //LOG(INFO)<<o2::utils::concat_string(outDirName, wr.getOrigin().str)<<"\n"; + // + swTot.Stop(); + swTot.Print(); +} diff --git a/Detectors/FIT/FDD/workflow/CMakeLists.txt b/Detectors/FIT/FDD/workflow/CMakeLists.txt index 23348ec266640..65cca7cf3aba7 100644 --- a/Detectors/FIT/FDD/workflow/CMakeLists.txt +++ b/Detectors/FIT/FDD/workflow/CMakeLists.txt @@ -9,12 +9,29 @@ # submit itself to any jurisdiction. o2_add_library(FDDWorkflow - SOURCES src/RecoWorkflow.cxx src/DigitReaderSpec.cxx - src/ReconstructorSpec.cxx src/RecPointWriterSpec.cxx + SOURCES src/DigitReaderSpec.cxx + src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx + src/RecoWorkflow.cxx + src/ReconstructorSpec.cxx + src/RecPointWriterSpec.cxx src/RecPointReaderSpec.cxx - PUBLIC_LINK_LIBRARIES O2::FDDReconstruction O2::Framework O2::DPLUtils) + PUBLIC_LINK_LIBRARIES O2::FDDReconstruction + O2::Framework + O2::DPLUtils) o2_add_executable(reco-workflow COMPONENT_NAME fdd SOURCES src/fdd-reco-workflow.cxx PUBLIC_LINK_LIBRARIES O2::FDDWorkflow) + +o2_add_executable(entropy-encoder-workflow + SOURCES src/entropy-encoder-workflow.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow) + +o2_add_executable(digit-reader-workflow + SOURCES src/digits-reader-workflow.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow) + diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/DigitReaderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/DigitReaderSpec.h index 7127408e717a2..aad71d0e33ce3 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/DigitReaderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/DigitReaderSpec.h @@ -20,7 +20,6 @@ #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/ChannelData.h" #include "DataFormatsFDD/MCLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" using namespace o2::framework; @@ -42,10 +41,6 @@ class DigitReader : public Task bool mUseMC = true; // use MC truth o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; - std::vector<o2::fdd::Digit>* mDigitsBC = nullptr; - std::vector<o2::fdd::ChannelData>* mDigitsCh = nullptr; - o2::dataformats::MCTruthContainer<o2::fdd::MCLabel>* mMCTruth = nullptr; - std::string mInputFileName = ""; std::string mDigitTreeName = "o2sim"; std::string mDigitBCBranchName = "FDDDigit"; diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..4b8b4e5bcd309 --- /dev/null +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to FDD digit/channels strean + +#ifndef O2_FDD_ENTROPYDECODER_SPEC +#define O2_FDD_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "FDDReconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace fdd +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::fdd::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace fdd +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..1a7b131fc45fc --- /dev/null +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert clusters streams to CTF (EncodedBlocks) + +#ifndef O2_FDD_ENTROPYENCODER_SPEC +#define O2_FDD_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "FDDReconstruction/CTFCoder.h" + +namespace o2 +{ +namespace fdd +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::fdd::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace fdd +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointWriterSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointWriterSpec.h index 8d9f251bf6b35..7fa95c492eb15 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointWriterSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointWriterSpec.h @@ -13,7 +13,6 @@ #ifndef O2_FDD_RECPOINTWRITER_H #define O2_FDD_RECPOINTWRITER_H - #include "Framework/DataProcessorSpec.h" using namespace o2::framework; diff --git a/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx index 82b3e98d58d3a..dbbcbe1c6c164 100644 --- a/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx @@ -16,7 +16,11 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" +#include "Framework/Logger.h" #include "FDDWorkflow/DigitReaderSpec.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include <vector> using namespace o2::framework; using namespace o2::fdd; @@ -42,6 +46,10 @@ void DigitReader::run(ProcessingContext& pc) return; } + std::vector<o2::fdd::Digit>* digitsBC = nullptr; + std::vector<o2::fdd::ChannelData>* digitsCh = nullptr; + o2::dataformats::IOMCTruthContainerView* mcTruthRootBuffer = nullptr; + { // load data from files TFile digFile(mInputFileName.c_str(), "read"); if (digFile.IsZombie()) { @@ -53,11 +61,11 @@ void DigitReader::run(ProcessingContext& pc) } LOG(INFO) << "Loaded FDD digits tree " << mDigitTreeName << " from " << mInputFileName; - digTree->SetBranchAddress(mDigitBCBranchName.c_str(), &mDigitsBC); - digTree->SetBranchAddress(mDigitChBranchName.c_str(), &mDigitsCh); + digTree->SetBranchAddress(mDigitBCBranchName.c_str(), &digitsBC); + digTree->SetBranchAddress(mDigitChBranchName.c_str(), &digitsCh); if (mUseMC) { if (digTree->GetBranch(mDigitMCTruthBranchName.c_str())) { - digTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mMCTruth); + digTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mcTruthRootBuffer); LOG(INFO) << "Will use MC-truth from " << mDigitMCTruthBranchName; } else { LOG(INFO) << "MC-truth is missing"; @@ -69,11 +77,16 @@ void DigitReader::run(ProcessingContext& pc) digFile.Close(); } - LOG(INFO) << "FDD DigitReader pushes " << mDigitsBC->size() << " digits"; - pc.outputs().snapshot(Output{mOrigin, "DIGITSBC", 0, Lifetime::Timeframe}, *mDigitsBC); - pc.outputs().snapshot(Output{mOrigin, "DIGITSCH", 0, Lifetime::Timeframe}, *mDigitsCh); + LOG(INFO) << "FDD DigitReader pushes " << digitsBC->size() << " digits"; + pc.outputs().snapshot(Output{mOrigin, "DIGITSBC", 0, Lifetime::Timeframe}, *digitsBC); + pc.outputs().snapshot(Output{mOrigin, "DIGITSCH", 0, Lifetime::Timeframe}, *digitsCh); if (mUseMC) { - pc.outputs().snapshot(Output{mOrigin, "DIGITLBL", 0, Lifetime::Timeframe}, *mMCTruth); + // TODO: To be replaced with sending ConstMCTruthContainer as soon as reco workflow supports it + std::vector<char> flatbuffer; + mcTruthRootBuffer->copyandflatten(flatbuffer); + o2::dataformats::MCTruthContainer<o2::fdd::MCLabel> mcTruth; + mcTruth.restore_from(flatbuffer.data(), flatbuffer.size()); + pc.outputs().snapshot(Output{mOrigin, "DIGITLBL", 0, Lifetime::Timeframe}, mcTruth); } mFinished = true; diff --git a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..b0af83a9fbbb4 --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "FDDWorkflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace fdd +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("fdd-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& digits = pc.outputs().make<std::vector<o2::fdd::Digit>>(OutputRef{"digits"}); + auto& channels = pc.outputs().make<std::vector<o2::fdd::ChannelData>>(OutputRef{"channels"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::fdd::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, digits, channels); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << channels.size() << " FDD channels in " << digits.size() << " digits in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FDD Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"digits"}, "FDD", "FDDDigit", 0, Lifetime::Timeframe}, + OutputSpec{{"channels"}, "FDD", "FDDDigitCh", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "fdd-entropy-decoder", + Inputs{InputSpec{"ctf", "FDD", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"fdd-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace fdd +} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..b3d9b6b332d97 --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,78 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "FDDWorkflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace fdd +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("fdd-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto digits = pc.inputs().get<gsl::span<o2::fdd::Digit>>("digits"); + auto channels = pc.inputs().get<gsl::span<o2::fdd::ChannelData>>("channels"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"FDD", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, digits, channels); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for FDD in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FDD Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("digits", "FDD", "FDDDigit", 0, Lifetime::Timeframe); + inputs.emplace_back("channels", "FDD", "FDDDigitCh", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "fdd-entropy-encoder", + inputs, + Outputs{{"FDD", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"fdd-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace fdd +} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx index baf223ac2b8f7..2fd688e96c490 100644 --- a/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx @@ -16,6 +16,7 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" #include "FDDWorkflow/RecPointReaderSpec.h" using namespace o2::framework; diff --git a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx index 9bbbf9086293b..3cacc248e5fda 100644 --- a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx @@ -13,6 +13,7 @@ #include <vector> #include "SimulationDataFormat/MCTruthContainer.h" #include "Framework/ControlService.h" +#include "Framework/Logger.h" #include "FDDWorkflow/ReconstructorSpec.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" diff --git a/Detectors/FIT/FDD/workflow/src/digits-reader-workflow.cxx b/Detectors/FIT/FDD/workflow/src/digits-reader-workflow.cxx new file mode 100644 index 0000000000000..de9c7439b9aac --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/digits-reader-workflow.cxx @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 digits-reader-workflow.cxx +/// \brief Implementation of FDD digits reader +/// +/// \author ruben.shahoyan@cern.ch + +#include "Framework/CallbackService.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "FDDWorkflow/DigitReaderSpec.h" +#include "CommonUtils/ConfigurableParam.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> 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::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + WorkflowSpec specs; + + DataProcessorSpec producer = o2::fdd::getFDDDigitReaderSpec(ctx.options().get<bool>("disable-mc")); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..87a8b05d20357 --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FDDWorkflow/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::fdd::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/FIT/FT0/CMakeLists.txt b/Detectors/FIT/FT0/CMakeLists.txt index a885f0a3f20d9..ba81588e3beb4 100644 --- a/Detectors/FIT/FT0/CMakeLists.txt +++ b/Detectors/FIT/FT0/CMakeLists.txt @@ -9,5 +9,7 @@ # submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(raw) add_subdirectory(reconstruction) add_subdirectory(simulation) +add_subdirectory(workflow) diff --git a/Detectors/FIT/FT0/base/files/FT0ChannelsTable.txt b/Detectors/FIT/FT0/base/files/FT0ChannelsTable.txt new file mode 100644 index 0000000000000..924e4b1c09f87 --- /dev/null +++ b/Detectors/FIT/FT0/base/files/FT0ChannelsTable.txt @@ -0,0 +1,225 @@ +channel# PM PM_channel HV_board HV_channel MCP_S/N HV_cable signal_cable +0 A0 PMA0/Ch01 A-Out HVAO/04 ???? FTA-D2 FTA-D2-? +1 A0 PMA0/Ch02 A-Out HVAO/04 ???? FTA-D2 FTA-D2-? +2 A0 PMA0/Ch03 A-Out HVAO/04 ???? FTA-D2 FTA-D2-? +3 A0 PMA0/Ch04 A-Out HVAO/04 ???? FTA-D2 FTA-D2-? +4 A0 PMA0/Ch05 A-Out HVAO/03 ???? FTA-D1 FTA-D1-? +5 A0 PMA0/Ch06 A-Out HVAO/03 ???? FTA-D1 FTA-D1-? +6 A0 PMA0/Ch07 A-Out HVAO/03 ???? FTA-D1 FTA-D1-? +7 A0 PMA0/Ch08 A-Out HVAO/03 ???? FTA-D1 FTA-D1-? +8 A0 PMA0/Ch09 A-Out HVAO/08 ???? FTA-E1 FTA-E1-? +9 A0 PMA0/Ch10 A-Out HVAO/08 ???? FTA-E1 FTA-E1-? +10 A0 PMA0/Ch11 A-Out HVAO/08 ???? FTA-E1 FTA-E1-? +11 A0 PMA0/Ch12 A-Out HVAO/08 ???? FTA-E1 FTA-E1-? +12 A1 PMA1/Ch01 A-Out HVAO/05 ???? FTA-D3 FTA-D3-? +13 A1 PMA1/Ch02 A-Out HVAO/05 ???? FTA-D3 FTA-D3-? +14 A1 PMA1/Ch03 A-Out HVAO/05 ???? FTA-D3 FTA-D3-? +15 A1 PMA1/Ch04 A-Out HVAO/05 ???? FTA-D3 FTA-D3-? +16 A1 PMA1/Ch05 A-Out HVAO/09 ???? FTA-E2 FTA-E2-? +17 A1 PMA1/Ch06 A-Out HVAO/09 ???? FTA-E2 FTA-E2-? +18 A1 PMA1/Ch07 A-Out HVAO/09 ???? FTA-E2 FTA-E2-? +19 A1 PMA1/Ch08 A-Out HVAO/09 ???? FTA-E2 FTA-E2-? +20 A1 PMA1/Ch09 A-Out HVAO/10 ???? FTA-E3 FTA-E3-? +21 A1 PMA1/Ch10 A-Out HVAO/10 ???? FTA-E3 FTA-E3-? +22 A1 PMA1/Ch11 A-Out HVAO/10 ???? FTA-E3 FTA-E3-? +23 A1 PMA1/Ch12 A-Out HVAO/10 ???? FTA-E3 FTA-E3-? +24 A2 PMA2/Ch01 A-Out HVAO/06 ???? FTA-D4 FTA-D4-? +25 A2 PMA2/Ch02 A-Out HVAO/06 ???? FTA-D4 FTA-D4-? +26 A2 PMA2/Ch03 A-Out HVAO/06 ???? FTA-D4 FTA-D4-? +27 A2 PMA2/Ch04 A-Out HVAO/06 ???? FTA-D4 FTA-D4-? +28 A2 PMA2/Ch05 A-Out HVAO/11 ???? FTA-E4 FTA-E4-? +29 A2 PMA2/Ch06 A-Out HVAO/11 ???? FTA-E4 FTA-E4-? +30 A2 PMA2/Ch07 A-Out HVAO/11 ???? FTA-E4 FTA-E4-? +31 A2 PMA2/Ch08 A-Out HVAO/11 ???? FTA-E4 FTA-E4-? +32 A2 PMA2/Ch09 A-Out HVAO/12 ???? FTA-E5 FTA-E5-? +33 A2 PMA2/Ch10 A-Out HVAO/12 ???? FTA-E5 FTA-E5-? +34 A2 PMA2/Ch11 A-Out HVAO/12 ???? FTA-E5 FTA-E5-? +35 A2 PMA2/Ch12 A-Out HVAO/12 ???? FTA-E5 FTA-E5-? +36 A3 PMA3/Ch01 A-Out HVAO/01 ???? FTA-C4 FTA-C4-? +37 A3 PMA3/Ch02 A-Out HVAO/01 ???? FTA-C4 FTA-C4-? +38 A3 PMA3/Ch03 A-Out HVAO/01 ???? FTA-C4 FTA-C4-? +39 A3 PMA3/Ch04 A-Out HVAO/01 ???? FTA-C4 FTA-C4-? +40 A3 PMA3/Ch05 A-Out HVAO/07 ???? FTA-D5 FTA-D5-? +41 A3 PMA3/Ch06 A-Out HVAO/07 ???? FTA-D5 FTA-D5-? +42 A3 PMA3/Ch07 A-Out HVAO/07 ???? FTA-D5 FTA-D5-? +43 A3 PMA3/Ch08 A-Out HVAO/07 ???? FTA-D5 FTA-D5-? +44 A3 PMA3/Ch09 A-Out HVAO/02 ???? FTA-C5 FTA-C5-? +45 A3 PMA3/Ch10 A-Out HVAO/02 ???? FTA-C5 FTA-C5-? +46 A3 PMA3/Ch11 A-Out HVAO/02 ???? FTA-C5 FTA-C5-? +47 A3 PMA3/Ch12 A-Out HVAO/02 ???? FTA-C5 FTA-C5-? +48 A4 PMA4/Ch01 A-In HVAI/09 ???? FTA-B4 FTA-B4-? +49 A4 PMA4/Ch02 A-In HVAI/09 ???? FTA-B4 FTA-B4-? +50 A4 PMA4/Ch03 A-In HVAI/09 ???? FTA-B4 FTA-B4-? +51 A4 PMA4/Ch04 A-In HVAI/09 ???? FTA-B4 FTA-B4-? +52 A4 PMA4/Ch05 A-In HVAI/10 ???? FTA-B5 FTA-B5-? +53 A4 PMA4/Ch06 A-In HVAI/10 ???? FTA-B5 FTA-B5-? +54 A4 PMA4/Ch07 A-In HVAI/10 ???? FTA-B5 FTA-B5-? +55 A4 PMA4/Ch08 A-In HVAI/10 ???? FTA-B5 FTA-B5-? +56 A4 PMA4/Ch09 A-In HVAI/05 ???? FTA-A5 FTA-A5-? +57 A4 PMA4/Ch10 A-In HVAI/05 ???? FTA-A5 FTA-A5-? +58 A4 PMA4/Ch11 A-In HVAI/05 ???? FTA-A5 FTA-A5-? +59 A4 PMA4/Ch12 A-In HVAI/05 ???? FTA-A5 FTA-A5-? +60 A5 PMA5/Ch01 A-In HVAI/08 ???? FTA-B3 FTA-B3-? +61 A5 PMA5/Ch02 A-In HVAI/08 ???? FTA-B3 FTA-B3-? +62 A5 PMA5/Ch03 A-In HVAI/08 ???? FTA-B3 FTA-B3-? +63 A5 PMA5/Ch04 A-In HVAI/08 ???? FTA-B3 FTA-B3-? +64 A5 PMA5/Ch05 A-In HVAI/04 ???? FTA-A4 FTA-A4-? +65 A5 PMA5/Ch06 A-In HVAI/04 ???? FTA-A4 FTA-A4-? +66 A5 PMA5/Ch07 A-In HVAI/04 ???? FTA-A4 FTA-A4-? +67 A5 PMA5/Ch08 A-In HVAI/04 ???? FTA-A4 FTA-A4-? +68 A5 PMA5/Ch09 A-In HVAI/03 ???? FTA-A3 FTA-A3-? +69 A5 PMA5/Ch10 A-In HVAI/03 ???? FTA-A3 FTA-A3-? +70 A5 PMA5/Ch11 A-In HVAI/03 ???? FTA-A3 FTA-A3-? +71 A5 PMA5/Ch12 A-In HVAI/03 ???? FTA-A3 FTA-A3-? +72 A6 PMA6/Ch01 A-In HVAI/07 ???? FTA-B2 FTA-B2-? +73 A6 PMA6/Ch02 A-In HVAI/07 ???? FTA-B2 FTA-B2-? +74 A6 PMA6/Ch03 A-In HVAI/07 ???? FTA-B2 FTA-B2-? +75 A6 PMA6/Ch04 A-In HVAI/07 ???? FTA-B2 FTA-B2-? +76 A6 PMA6/Ch05 A-In HVAI/02 ???? FTA-A2 FTA-A2-? +77 A6 PMA6/Ch06 A-In HVAI/02 ???? FTA-A2 FTA-A2-? +78 A6 PMA6/Ch07 A-In HVAI/02 ???? FTA-A2 FTA-A2-? +79 A6 PMA6/Ch08 A-In HVAI/02 ???? FTA-A2 FTA-A2-? +80 A6 PMA6/Ch09 A-In HVAI/01 ???? FTA-A1 FTA-A1-? +81 A6 PMA6/Ch10 A-In HVAI/01 ???? FTA-A1 FTA-A1-? +82 A6 PMA6/Ch11 A-In HVAI/01 ???? FTA-A1 FTA-A1-? +83 A6 PMA6/Ch12 A-In HVAI/01 ???? FTA-A1 FTA-A1-? +84 A7 PMA7/Ch01 A-In HVAI/12 ???? FTA-C2 FTA-C2-? +85 A7 PMA7/Ch02 A-In HVAI/12 ???? FTA-C2 FTA-C2-? +86 A7 PMA7/Ch03 A-In HVAI/12 ???? FTA-C2 FTA-C2-? +87 A7 PMA7/Ch04 A-In HVAI/12 ???? FTA-C2 FTA-C2-? +88 A7 PMA7/Ch05 A-In HVAI/06 ???? FTA-B1 FTA-B1-? +89 A7 PMA7/Ch06 A-In HVAI/06 ???? FTA-B1 FTA-B1-? +90 A7 PMA7/Ch07 A-In HVAI/06 ???? FTA-B1 FTA-B1-? +91 A7 PMA7/Ch08 A-In HVAI/06 ???? FTA-B1 FTA-B1-? +92 A7 PMA7/Ch09 A-In HVAI/11 ???? FTA-C1 FTA-C1-? +93 A7 PMA7/Ch10 A-In HVAI/11 ???? FTA-C1 FTA-C1-? +94 A7 PMA7/Ch11 A-In HVAI/11 ???? FTA-C1 FTA-C1-? +95 A7 PMA7/Ch12 A-In HVAI/11 ???? FTA-C1 FTA-C1-? +96 C0 PMC0/Ch01 C-Up HVCU/04 2119 FTC-B3 FTC-B3-2 +97 C0 PMC0/Ch02 C-Up HVCU/04 2119 FTC-B3 FTC-B3-1 +98 C0 PMC0/Ch03 C-Up HVCU/04 2119 FTC-B3 FTC-B3-3 +99 C0 PMC0/Ch04 C-Up HVCU/04 2119 FTC-B3 FTC-B3-4 +100 C0 PMC0/Ch05 C-Up HVCU/02 2147 FTC-A3 FTC-A3-2 +101 C0 PMC0/Ch06 C-Up HVCU/02 2147 FTC-A3 FTC-A3-1 +102 C0 PMC0/Ch07 C-Up HVCU/02 2147 FTC-A3 FTC-A3-3 +103 C0 PMC0/Ch08 C-Up HVCU/02 2147 FTC-A3 FTC-A3-4 +104 C0 PMC0/Ch09 C-Up HVCU/01 2129 FTC-A2 FTC-A2-2 +105 C0 PMC0/Ch10 C-Up HVCU/01 2129 FTC-A2 FTC-A2-1 +106 C0 PMC0/Ch11 C-Up HVCU/01 2129 FTC-A2 FTC-A2-3 +107 C0 PMC0/Ch12 C-Up HVCU/01 2129 FTC-A2 FTC-A2-4 +108 C1 PMC1/Ch01 C-Up HVCU/06 2123 FTC-C2 FTC-C2-1 +109 C1 PMC1/Ch02 C-Up HVCU/06 2123 FTC-C2 FTC-C2-2 +110 C1 PMC1/Ch03 C-Up HVCU/06 2123 FTC-C2 FTC-C2-4 +111 C1 PMC1/Ch04 C-Up HVCU/06 2123 FTC-C2 FTC-C2-3 +112 C1 PMC1/Ch05 C-Up HVCU/05 2127 FTC-C1 FTC-C1-1 +113 C1 PMC1/Ch06 C-Up HVCU/05 2127 FTC-C1 FTC-C1-2 +114 C1 PMC1/Ch07 C-Up HVCU/05 2127 FTC-C1 FTC-C1-4 +115 C1 PMC1/Ch08 C-Up HVCU/05 2127 FTC-C1 FTC-C1-3 +116 C1 PMC1/Ch09 C-Up HVCU/03 2118 FTC-B1 FTC-B1-2 +117 C1 PMC1/Ch10 C-Up HVCU/03 2118 FTC-B1 FTC-B1-3 +118 C1 PMC1/Ch11 C-Up HVCU/03 2118 FTC-B1 FTC-B1-1 +119 C1 PMC1/Ch12 C-Up HVCU/03 2118 FTC-B1 FTC-B1-4 +120 C2 PMC2/Ch01 C-Up HVCU/08 2128 FTC-D2 FTC-D2-4 +121 C2 PMC2/Ch02 C-Up HVCU/08 2128 FTC-D2 FTC-D2-3 +122 C2 PMC2/Ch03 C-Up HVCU/08 2128 FTC-D2 FTC-D2-1 +123 C2 PMC2/Ch04 C-Up HVCU/08 2128 FTC-D2 FTC-D2-2 +124 C2 PMC2/Ch05 C-Up HVCU/07 2145 FTC-D1 FTC-D1-4 +125 C2 PMC2/Ch06 C-Up HVCU/07 2145 FTC-D1 FTC-D1-3 +126 C2 PMC2/Ch07 C-Up HVCU/07 2145 FTC-D1 FTC-D1-1 +127 C2 PMC2/Ch08 C-Up HVCU/07 2145 FTC-D1 FTC-D1-2 +128 C2 PMC2/Ch09 C-Up HVCU/09 2125 FTC-E1 FTC-E1-2 +129 C2 PMC2/Ch10 C-Up HVCU/09 2125 FTC-E1 FTC-E1-1 +130 C2 PMC2/Ch11 C-Up HVCU/09 2125 FTC-E1 FTC-E1-3 +131 C2 PMC2/Ch12 C-Up HVCU/09 2125 FTC-E1 FTC-E1-4 +132 C3 PMC3/Ch01 C-Up HVCU/10 2130 FTC-E3 FTC-E3-1 +133 C3 PMC3/Ch02 C-Up HVCU/10 2130 FTC-E3 FTC-E3-2 +134 C3 PMC3/Ch03 C-Up HVCU/10 2130 FTC-E3 FTC-E3-4 +135 C3 PMC3/Ch04 C-Up HVCU/10 2130 FTC-E3 FTC-E3-3 +136 C3 PMC3/Ch05 C-Up HVCU/12 2149 FTC-F3 FTC-F3-1 +137 C3 PMC3/Ch06 C-Up HVCU/12 2149 FTC-F3 FTC-F3-2 +138 C3 PMC3/Ch07 C-Up HVCU/12 2149 FTC-F3 FTC-F3-4 +139 C3 PMC3/Ch08 C-Up HVCU/12 2149 FTC-F3 FTC-F3-3 +140 C3 PMC3/Ch09 C-Up HVCU/11 2134 FTC-F2 FTC-F2-1 +141 C3 PMC3/Ch10 C-Up HVCU/11 2134 FTC-F2 FTC-F2-2 +142 C3 PMC3/Ch11 C-Up HVCU/11 2134 FTC-F2 FTC-F2-4 +143 C3 PMC3/Ch12 C-Up HVCU/11 2134 FTC-F2 FTC-F2-3 +144 C4 PMC4/Ch01 C-Down HVCD/09 2137 FTC-E4 FTC-E4-2 +145 C4 PMC4/Ch02 C-Down HVCD/09 2137 FTC-E4 FTC-E4-1 +146 C4 PMC4/Ch03 C-Down HVCD/09 2137 FTC-E4 FTC-E4-3 +147 C4 PMC4/Ch04 C-Down HVCD/09 2137 FTC-E4 FTC-E4-4 +148 C4 PMC4/Ch05 C-Down HVCD/11 2152 FTC-F4 FTC-F4-2 +149 C4 PMC4/Ch06 C-Down HVCD/11 2152 FTC-F4 FTC-F4-1 +150 C4 PMC4/Ch07 C-Down HVCD/11 2152 FTC-F4 FTC-F4-3 +151 C4 PMC4/Ch08 C-Down HVCD/11 2152 FTC-F4 FTC-F4-4 +152 C4 PMC4/Ch09 C-Down HVCD/12 2142 FTC-F5 FTC-F5-2 +153 C4 PMC4/Ch10 C-Down HVCD/12 2142 FTC-F5 FTC-F5-1 +154 C4 PMC4/Ch11 C-Down HVCD/12 2142 FTC-F5 FTC-F5-3 +155 C4 PMC4/Ch12 C-Down HVCD/12 2142 FTC-F5 FTC-F5-4 +156 C5 PMC5/Ch01 C-Down HVCD/07 2157 FTC-D5 FTC-D5-1 +157 C5 PMC5/Ch02 C-Down HVCD/07 2157 FTC-D5 FTC-D5-2 +158 C5 PMC5/Ch03 C-Down HVCD/07 2157 FTC-D5 FTC-D5-4 +159 C5 PMC5/Ch04 C-Down HVCD/07 2157 FTC-D5 FTC-D5-3 +160 C5 PMC5/Ch05 C-Down HVCD/08 2167 FTC-D6 FTC-D6-1 +161 C5 PMC5/Ch06 C-Down HVCD/08 2167 FTC-D6 FTC-D6-2 +162 C5 PMC5/Ch07 C-Down HVCD/08 2167 FTC-D6 FTC-D6-4 +163 C5 PMC5/Ch08 C-Down HVCD/08 2167 FTC-D6 FTC-D6-3 +164 C5 PMC5/Ch09 C-Down HVCD/10 2165 FTC-E6 FTC-E6-2 +165 C5 PMC5/Ch10 C-Down HVCD/10 2165 FTC-E6 FTC-E6-3 +166 C5 PMC5/Ch11 C-Down HVCD/10 2165 FTC-E6 FTC-E6-1 +167 C5 PMC5/Ch12 C-Down HVCD/10 2165 FTC-E6 FTC-E6-4 +168 C6 PMC6/Ch01 C-Down HVCD/05 2148 FTC-C5 FTC-C5-4 +169 C6 PMC6/Ch02 C-Down HVCD/05 2148 FTC-C5 FTC-C5-3 +170 C6 PMC6/Ch03 C-Down HVCD/05 2148 FTC-C5 FTC-C5-1 +171 C6 PMC6/Ch04 C-Down HVCD/05 2148 FTC-C5 FTC-C5-2 +172 C6 PMC6/Ch05 C-Down HVCD/06 2162 FTC-C6 FTC-C6-4 +173 C6 PMC6/Ch06 C-Down HVCD/06 2162 FTC-C6 FTC-C6-3 +174 C6 PMC6/Ch07 C-Down HVCD/06 2162 FTC-C6 FTC-C6-1 +175 C6 PMC6/Ch08 C-Down HVCD/06 2162 FTC-C6 FTC-C6-2 +176 C6 PMC6/Ch09 C-Down HVCD/04 2144 FTC-B6 FTC-B6-2 +177 C6 PMC6/Ch10 C-Down HVCD/04 2144 FTC-B6 FTC-B6-1 +178 C6 PMC6/Ch11 C-Down HVCD/04 2144 FTC-B6 FTC-B6-3 +179 C6 PMC6/Ch12 C-Down HVCD/04 2144 FTC-B6 FTC-B6-4 +180 C7 PMC7/Ch01 C-Down HVCD/03 2136 FTC-B4 FTC-B4-1 +181 C7 PMC7/Ch02 C-Down HVCD/03 2136 FTC-B4 FTC-B4-2 +182 C7 PMC7/Ch03 C-Down HVCD/03 2136 FTC-B4 FTC-B4-4 +183 C7 PMC7/Ch04 C-Down HVCD/03 2136 FTC-B4 FTC-B4-3 +184 C7 PMC7/Ch05 C-Down HVCD/01 2151 FTC-A4 FTC-A4-1 +185 C7 PMC7/Ch06 C-Down HVCD/01 2151 FTC-A4 FTC-A4-2 +186 C7 PMC7/Ch07 C-Down HVCD/01 2151 FTC-A4 FTC-A4-4 +187 C7 PMC7/Ch08 C-Down HVCD/01 2151 FTC-A4 FTC-A4-3 +188 C7 PMC7/Ch09 C-Down HVCD/02 2141 FTC-A5 FTC-A5-1 +189 C7 PMC7/Ch10 C-Down HVCD/02 2141 FTC-A5 FTC-A5-2 +190 C7 PMC7/Ch11 C-Down HVCD/02 2141 FTC-A5 FTC-A5-4 +191 C7 PMC7/Ch12 C-Down HVCD/02 2141 FTC-A5 FTC-A5-3 +192 C8 PMC8/Ch01 C-Mid HVCM/01 2121 FTC-B2 FTC-B2-2 +193 C8 PMC8/Ch02 C-Mid HVCM/01 2121 FTC-B2 FTC-B2-3 +194 C8 PMC8/Ch03 C-Mid HVCM/01 2121 FTC-B2 FTC-B2-1 +195 C8 PMC8/Ch04 C-Mid HVCM/01 2121 FTC-B2 FTC-B2-4 +196 C8 PMC8/Ch05 C-Mid HVCM/02 2133 FTC-E2 FTC-E2-1 +197 C8 PMC8/Ch06 C-Mid HVCM/02 2133 FTC-E2 FTC-E2-2 +198 C8 PMC8/Ch07 C-Mid HVCM/02 2133 FTC-E2 FTC-E2-4 +199 C8 PMC8/Ch08 C-Mid HVCM/02 2133 FTC-E2 FTC-E2-3 +200 C8 PMC8/Ch09 N/A N/A N/A N/A N/A +201 C8 PMC8/Ch10 N/A N/A N/A N/A N/A +202 C8 PMC8/Ch11 N/A N/A N/A N/A N/A +203 C8 PMC8/Ch12 N/A N/A N/A N/A N/A +204 C9 PMC9/Ch01 C-Mid HVCM/03 2158 FTC-E5 FTC-E5-2 +205 C9 PMC9/Ch02 C-Mid HVCM/03 2158 FTC-E5 FTC-E5-3 +206 C9 PMC9/Ch03 C-Mid HVCM/03 2158 FTC-E5 FTC-E5-1 +207 C9 PMC9/Ch04 C-Mid HVCM/03 2158 FTC-E5 FTC-E5-4 +208 C9 PMC9/Ch05 C-Mid HVCM/04 2143 FTC-B5 FTC-B5-1 +209 C9 PMC9/Ch06 C-Mid HVCM/04 2143 FTC-B5 FTC-B5-2 +210 C9 PMC9/Ch07 C-Mid HVCM/04 2143 FTC-B5 FTC-B5-4 +211 C9 PMC9/Ch08 C-Mid HVCM/04 2143 FTC-B5 FTC-B5-3 +212 C9 PMC9/Ch09 N/A N/A N/A N/A N/A +213 C9 PMC9/Ch10 N/A N/A N/A N/A N/A +214 C9 PMC9/Ch11 N/A N/A N/A N/A N/A +215 C9 PMC9/Ch12 N/A N/A N/A N/A N/A +216 A9 PMA9/Ch01 C-Mid HVCM/12 ???? FT0-LCS FTA-LCS +217 A9 PMA9/Ch02 C-Mid HVCM/12 ???? FT0-LCS FTA-LCS-H +218 A9 PMA9/Ch03 C-Mid HVCM/12 ???? FT0-LCS FTC-LCS +219 A9 PMA9/Ch04 C-Mid HVCM/12 ???? FT0-LCS FTC-LCS-H + + + + diff --git a/Detectors/FIT/FT0/base/files/Sim2DataChannels.txt b/Detectors/FIT/FT0/base/files/Sim2DataChannels.txt new file mode 100644 index 0000000000000..fe8a1bb709f95 --- /dev/null +++ b/Detectors/FIT/FT0/base/files/Sim2DataChannels.txt @@ -0,0 +1,2 @@ +82 83 80 81 89 91 88 90 93 95 92 94 4 6 5 7 8 10 9 11 78 79 76 77 74 75 72 73 85 87 84 86 0 2 1 3 16 17 18 19 71 70 69 68 63 62 61 60 12 13 14 15 20 21 22 23 67 66 65 64 51 49 50 48 38 36 39 37 25 24 27 26 29 28 31 30 59 57 58 56 55 53 54 52 46 44 47 45 42 40 43 41 33 32 35 34 117 119 116 118 113 115 112 114 124 126 125 127 128 130 129 131 106 107 104 105 193 195 192 194 109 111 108 110 120 122 121 123 196 197 198 199 140 141 142 143 102 103 100 101 98 99 96 97 132 133 134 135 136 137 138 139 187 186 185 184 183 182 181 180 145 144 147 146 148 149 151 150 191 190 189 188 211 210 209 208 171 169 170 168 158 156 159 157 205 204 207 205 153 152 155 154 179 177 178 176 175 173 174 172 162 160 163 161 166 164 167 165 + diff --git a/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h b/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h index fc6eae1755528..f5fb4f9b7ba9d 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h @@ -16,6 +16,7 @@ #include <Rtypes.h> #include <TVector3.h> + namespace o2 { namespace ft0 @@ -36,6 +37,7 @@ class Geometry /// TVector3 centerMCP(int imcp) { return mMCP[imcp]; } + static constexpr int Nchannels = 208; // number od channels static constexpr int NCellsA = 24; // number of radiatiors on A side static constexpr int NCellsC = 28; // number of radiatiors on C side static constexpr float ZdetA = 335; // number of radiatiors on A side @@ -43,7 +45,7 @@ class Geometry static constexpr float ChannelWidth = 13.02; // channel width in ps static constexpr float MV_2_Nchannels = 2.2857143; //amplitude channel 7 mV ->16channels static constexpr float MV_2_NchannelsInverse = 0.437499997; //inverse amplitude channel 7 mV ->16channels - /// + static constexpr int mTime_trg_gate = 192; // #channels private: TVector3 mMCP[52]; diff --git a/Detectors/FIT/FT0/base/src/Geometry.cxx b/Detectors/FIT/FT0/base/src/Geometry.cxx index ef2712bc789ee..9dd83e33af0e9 100644 --- a/Detectors/FIT/FT0/base/src/Geometry.cxx +++ b/Detectors/FIT/FT0/base/src/Geometry.cxx @@ -10,9 +10,11 @@ #include <iomanip> //#include <TVector3.h> #include "FT0Base/Geometry.h" - +#include "TSystem.h" #include <FairLogger.h> #include <sstream> +#include <string> +#include <iostream> ClassImp(o2::ft0::Geometry); @@ -90,8 +92,10 @@ Geometry::Geometry() : mMCP{{0, 0, 0}} gc[i] = -1 * ac[i]; } // Set coordinate - for (int ipmt = 0; ipmt < 24; ipmt++) + for (int ipmt = 0; ipmt < 24; ipmt++) { mMCP[ipmt].SetXYZ(xa[ipmt], xa[ipmt], zDetA); - for (int ipmt = 24; ipmt < 52; ipmt++) + } + for (int ipmt = 24; ipmt < 52; ipmt++) { mMCP[ipmt].SetXYZ(xc2[ipmt - 24], yc2[ipmt - 24], zc2[ipmt - 24]); + } } diff --git a/Detectors/FIT/FT0/raw/CMakeLists.txt b/Detectors/FIT/FT0/raw/CMakeLists.txt new file mode 100644 index 0000000000000..c2bd979816935 --- /dev/null +++ b/Detectors/FIT/FT0/raw/CMakeLists.txt @@ -0,0 +1,13 @@ +#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". +# +#See http: //alice-o2.web.cern.ch/license for full licensing information. +# +#In applying this license CERN does 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(FT0Raw + SOURCES src/DataBlockFT0.cxx src/DigitBlockFT0.cxx src/RawReaderFT0Base.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::DataFormatsFT0 O2::FITRaw) diff --git a/Detectors/FIT/FT0/raw/include/FT0Raw/DataBlockFT0.h b/Detectors/FIT/FT0/raw/include/FT0Raw/DataBlockFT0.h new file mode 100644 index 0000000000000..5bb219a4b657e --- /dev/null +++ b/Detectors/FIT/FT0/raw/include/FT0Raw/DataBlockFT0.h @@ -0,0 +1,112 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DataBlockFT0.h class for RAW data format data blocks at FT0 +// +// Artur.Furs +// afurs@cern.ch +// + +#ifndef ALICEO2_FIT_DATABLOCKFT0_H_ +#define ALICEO2_FIT_DATABLOCKFT0_H_ +#include <iostream> +#include <Rtypes.h> +#include <DataFormatsFT0/RawEventData.h> +#include <FITRaw/DataBlockBase.h> + +#include <gsl/span> +#include <iostream> +#include <cassert> +using namespace o2::fit; +namespace o2 +{ +namespace ft0 +{ +using RawHeaderPM = o2::ft0::EventHeader; +using RawDataPM = o2::ft0::EventData; +using RawHeaderTCM = o2::ft0::EventHeader; +using RawDataTCM = o2::ft0::TCMdata; +using RawHeaderTCMext = o2::ft0::EventHeader; +using RawDataTCMext = o2::ft0::TCMdataExtended; + +using namespace std; + +//FT0 DATA BLOCK DEFINITIONS + +//standard data block from PM +class DataBlockPM : public DataBlockBase<DataBlockPM, RawHeaderPM, RawDataPM> +{ + public: + DataBlockPM() = default; + DataBlockPM(const DataBlockPM&) = default; + void deserialize(gsl::span<const uint8_t> srcBytes, size_t& srcByteShift) + { + DataBlockWrapper<RawHeaderPM>::deserialize(srcBytes, DataBlockWrapper<RawHeaderPM>::MaxNwords, srcByteShift); + DataBlockWrapper<RawDataPM>::deserialize(srcBytes, DataBlockWrapper<RawHeaderPM>::mData[0].nGBTWords, srcByteShift); + } + //Custom sanity checking for current deserialized block + // put here code for raw data checking + void sanityCheck(bool& flag) + { + if (DataBlockWrapper<RawDataPM>::mNelements == 0) { + flag = false; + return; + } + if (DataBlockWrapper<RawDataPM>::mNelements % 2 == 0 && DataBlockWrapper<RawDataPM>::mData[DataBlockWrapper<RawDataPM>::mNelements - 1].channelID == 0) { + DataBlockWrapper<RawDataPM>::mNelements--; //in case of half GBT-word filling + } + //TODO, Descriptor checking, Channel range + } +}; + +//standard data block from TCM +class DataBlockTCM : public DataBlockBase<DataBlockTCM, RawHeaderTCM, RawDataTCM> +{ + public: + DataBlockTCM() = default; + DataBlockTCM(const DataBlockTCM&) = default; + void deserialize(gsl::span<const uint8_t> srcBytes, size_t& srcByteShift) + { + DataBlockWrapper<RawHeaderTCM>::deserialize(srcBytes, DataBlockWrapper<RawHeaderTCM>::MaxNwords, srcByteShift); + DataBlockWrapper<RawDataTCM>::deserialize(srcBytes, DataBlockWrapper<RawHeaderTCM>::mData[0].nGBTWords, srcByteShift); + } + //Custom sanity checking for current deserialized block + // put here code for raw data checking + void sanityCheck(bool& flag) + { + //TODO, Descriptor checking + } +}; + +//extended TCM mode, 1 TCMdata + 8 TCMdataExtendedstructs +class DataBlockTCMext : public DataBlockBase<DataBlockTCMext, RawHeaderTCMext, RawDataTCM, RawDataTCMext> +{ + public: + DataBlockTCMext() = default; + DataBlockTCMext(const DataBlockTCMext&) = default; + void deserialize(gsl::span<const uint8_t> srcBytes, size_t& srcByteShift) + { + DataBlockWrapper<RawHeaderTCMext>::deserialize(srcBytes, DataBlockWrapper<RawHeaderTCMext>::MaxNwords, srcByteShift); + DataBlockWrapper<RawDataTCM>::deserialize(srcBytes, DataBlockWrapper<RawDataTCM>::MaxNwords, srcByteShift); + DataBlockWrapper<RawDataTCMext>::deserialize(srcBytes, DataBlockWrapper<RawHeaderTCMext>::mData[0].nGBTWords - DataBlockWrapper<RawDataTCM>::MaxNwords, srcByteShift); + } + + //Custom sanity checking for current deserialized block + // put here code for raw data checking + void sanityCheck(bool& flag) + { + + //TODO, Descriptor checking + } +}; + +} // namespace ft0 +} // namespace o2 +#endif diff --git a/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h b/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h new file mode 100644 index 0000000000000..cc7bd3cbdb18e --- /dev/null +++ b/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h @@ -0,0 +1,229 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DigitBlockFT0.h class for proccessing RAW data into Digits +// +// Artur.Furs +// afurs@cern.ch +// TODO: +// traites for DataBlocks +// check if the EventID filling is correct + +#ifndef ALICEO2_FIT_DIGITBLOCKFT0_H_ +#define ALICEO2_FIT_DIGITBLOCKFT0_H_ +#include <iostream> +#include <vector> +#include <algorithm> +#include <Rtypes.h> +#include "FT0Raw/DataBlockFT0.h" +#include "FITRaw/DigitBlockBase.h" + +#include <CommonDataFormat/InteractionRecord.h> +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/ChannelData.h" +#include "DataFormatsFT0/LookUpTable.h" + +#include <boost/mpl/vector.hpp> +#include <boost/mpl/contains.hpp> + +#include <gsl/span> + +using namespace o2::fit; + +namespace o2 +{ +namespace ft0 +{ +//Normal data taking mode +class DigitBlockFT0 : public DigitBlockBase<DigitBlockFT0> +{ + public: + typedef DigitBlockBase<DigitBlockFT0> DigitBlockBaseType; + DigitBlockFT0(o2::InteractionRecord intRec) { setIntRec(intRec); } + DigitBlockFT0() = default; + DigitBlockFT0(const DigitBlockFT0& other) = default; + ~DigitBlockFT0() = default; + void setIntRec(o2::InteractionRecord intRec) { mDigit.mIntRecord = intRec; } + Digit mDigit; + std::vector<ChannelData> mVecChannelData; + static o2::ft0::LookUpTable sLookupTable; + static int sEventID; + + template <class DataBlockType> + void processDigits(DataBlockType& dataBlock, int linkID) + { + if constexpr (std::is_same<DataBlockType, DataBlockPM>::value) { //Filling data from PM + for (int iEventData = 0; iEventData < dataBlock.DataBlockWrapper<RawDataPM>::mNelements; iEventData++) { + mVecChannelData.emplace_back(uint8_t(sLookupTable.getChannel(linkID, dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].channelID)), + int(dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].time), + int(dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].charge), + dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].getFlagWord()); + } + } else if constexpr (std::is_same<DataBlockType, DataBlockTCM>::value) { //Filling data from TCM (normal/extended mode) + dataBlock.DataBlockWrapper<RawDataTCM>::mData[0].fillTrigger(mDigit.mTriggers); + } + } + void getDigits(std::vector<Digit>& vecDigits, std::vector<ChannelData>& vecChannelData) + { + //last digit filling + mDigit.ref.set(vecChannelData.size(), mVecChannelData.size()); + mDigit.mEventID = sEventID; + // + vecDigits.push_back(std::move(mDigit)); + std::move(mVecChannelData.begin(), mVecChannelData.end(), std::back_inserter(vecChannelData)); + mVecChannelData.clear(); + + sEventID++; //Increasing static eventID. After each poping of the data, it will increase + } + void print() const + { + std::cout << "\n______________DIGIT DATA____________"; + std::cout << std::hex; + std::cout << "\nBC: " << mDigit.mIntRecord.bc << "| ORBIT: " << mDigit.mIntRecord.orbit; + std::cout << "\nRef first: " << mDigit.ref.getFirstEntry() << "| Ref entries: " << mDigit.ref.getEntries(); + std::cout << "\nmTrigger: " << static_cast<uint16_t>(mDigit.mTriggers.triggersignals); + std::cout << "\nnChanA: " << static_cast<uint16_t>(mDigit.mTriggers.nChanA) << " | nChanC: " << static_cast<uint16_t>(mDigit.mTriggers.nChanC); + std::cout << "\namplA: " << mDigit.mTriggers.amplA << " | amplC: " << mDigit.mTriggers.amplC; + std::cout << "\ntimeA: " << mDigit.mTriggers.timeA << " | timeC: " << mDigit.mTriggers.timeC; + + std::cout << "\n______________CHANNEL DATA____________\n"; + std::cout << "\nN channel: " << mVecChannelData.size(); + for (const auto& chData : mVecChannelData) { + std::cout << "\nChId: " << static_cast<uint16_t>(chData.ChId) << " | ChainQTC:" << static_cast<uint16_t>(chData.ChainQTC) << " | CFDTime: " << chData.CFDTime << " | QTCAmpl: " << chData.QTCAmpl; + } + std::cout << std::dec; + std::cout << "\n"; + LOG(INFO) << "______________________________________"; + } + + static void print(std::vector<Digit>& vecDigit, std::vector<ChannelData>& vecChannelData) + { + for (const auto& digit : vecDigit) { + std::cout << "\n______________DIGIT DATA____________"; + std::cout << std::hex; + std::cout << "\nBC: " << digit.mIntRecord.bc << "| ORBIT: " << digit.mIntRecord.orbit << " | EventID: " << digit.mEventID; + std::cout << "\nRef first: " << digit.ref.getFirstEntry() << "| Ref entries: " << digit.ref.getEntries(); + std::cout << "\nmTrigger: " << static_cast<uint16_t>(digit.mTriggers.triggersignals); + std::cout << "\nnChanA: " << static_cast<uint16_t>(digit.mTriggers.nChanA) << " | nChanC: " << static_cast<uint16_t>(digit.mTriggers.nChanC); + std::cout << "\namplA: " << digit.mTriggers.amplA << " | amplC: " << digit.mTriggers.amplC; + std::cout << "\ntimeA: " << digit.mTriggers.timeA << " | timeC: " << digit.mTriggers.timeC; + + std::cout << "\n______________CHANNEL DATA____________\n"; + for (int iChData = digit.ref.getFirstEntry(); iChData < digit.ref.getFirstEntry() + digit.ref.getEntries(); iChData++) { + + std::cout << "\nChId: " << static_cast<uint16_t>(vecChannelData[iChData].ChId) << " | ChainQTC:" << static_cast<uint16_t>(vecChannelData[iChData].ChainQTC) + << " | CFDTime: " << vecChannelData[iChData].CFDTime << " | QTCAmpl: " << vecChannelData[iChData].QTCAmpl; + } + std::cout << std::dec; + std::cout << "\n______________________________________\n"; + } + } +}; + +//TCM extended data taking mode +class DigitBlockFT0ext : public DigitBlockBase<DigitBlockFT0ext> +{ + public: + typedef DigitBlockBase<DigitBlockFT0ext> DigitBlockBaseType; + + DigitBlockFT0ext(o2::InteractionRecord intRec) { setIntRec(intRec); } + DigitBlockFT0ext() = default; + DigitBlockFT0ext(const DigitBlockFT0ext& other) = default; + ~DigitBlockFT0ext() = default; + void setIntRec(o2::InteractionRecord intRec) { mDigit.mIntRecord = intRec; } + DigitExt mDigit; + std::vector<ChannelData> mVecChannelData; + std::vector<TriggersExt> mVecTriggersExt; + + static o2::ft0::LookUpTable sLookupTable; + static int sEventID; + + template <class DataBlockType> + void processDigits(DataBlockType& dataBlock, int linkID) + { + if constexpr (std::is_same<DataBlockType, DataBlockPM>::value) { //Filling data from PM + for (int iEventData = 0; iEventData < dataBlock.DataBlockWrapper<RawDataPM>::mNelements; iEventData++) { + mVecChannelData.emplace_back(uint8_t(sLookupTable.getChannel(linkID, dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].channelID)), + int(dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].time), + int(dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].charge), + dataBlock.DataBlockWrapper<RawDataPM>::mData[iEventData].getFlagWord()); + } + } else if constexpr (std::is_same<DataBlockType, DataBlockTCMext>::value) { //Filling data from TCM, extended mode. Same proccess as for normal mode, for now. + dataBlock.DataBlockWrapper<RawDataTCM>::mData[0].fillTrigger(mDigit.mTriggers); + for (int iTriggerWord = 0; iTriggerWord < dataBlock.DataBlockWrapper<RawDataTCMext>::mNelements; iTriggerWord++) { + mVecTriggersExt.emplace_back(dataBlock.DataBlockWrapper<RawDataTCMext>::mData[iTriggerWord].triggerWord); + } + } + } + void getDigits(std::vector<DigitExt>& vecDigits, std::vector<ChannelData>& vecChannelData, std::vector<TriggersExt>& vecTriggersExt) + { + //last digit filling + mDigit.ref.set(vecChannelData.size(), mVecChannelData.size()); + mDigit.refExt.set(vecTriggersExt.size(), mVecTriggersExt.size()); + mDigit.mEventID = sEventID; + //Digits + vecDigits.push_back(std::move(mDigit)); + //ChannelData + std::move(mVecChannelData.begin(), mVecChannelData.end(), std::back_inserter(vecChannelData)); + mVecChannelData.clear(); + //TriggersExt + std::move(mVecTriggersExt.begin(), mVecTriggersExt.end(), std::back_inserter(vecTriggersExt)); + mVecTriggersExt.clear(); + + sEventID++; //Increasing static eventID. After each poping of the data, it will increase + } + void print() const + { + std::cout << "\n______________DIGIT DATA____________"; + std::cout << std::hex; + std::cout << "\nBC: " << mDigit.mIntRecord.bc << "| ORBIT: " << mDigit.mIntRecord.orbit; + std::cout << "\nRef first: " << mDigit.ref.getFirstEntry() << "| Ref entries: " << mDigit.ref.getEntries(); + std::cout << "\nmTrigger: " << static_cast<uint16_t>(mDigit.mTriggers.triggersignals); + std::cout << "\nnChanA: " << static_cast<uint16_t>(mDigit.mTriggers.nChanA) << " | nChanC: " << static_cast<uint16_t>(mDigit.mTriggers.nChanC); + std::cout << "\namplA: " << mDigit.mTriggers.amplA << " | amplC: " << mDigit.mTriggers.amplC; + std::cout << "\ntimeA: " << mDigit.mTriggers.timeA << " | timeC: " << mDigit.mTriggers.timeC; + + std::cout << "\n______________CHANNEL DATA____________\n"; + std::cout << "\nN channel: " << mVecChannelData.size(); + for (const auto& chData : mVecChannelData) { + std::cout << "\nChId: " << static_cast<uint16_t>(chData.ChId) << " | ChainQTC:" << static_cast<uint16_t>(chData.ChainQTC) << " | CFDTime: " << chData.CFDTime << " | QTCAmpl: " << chData.QTCAmpl; + } + std::cout << std::dec; + std::cout << "\n"; + LOG(INFO) << "______________________________________"; + } + + static void print(std::vector<DigitExt>& vecDigit, std::vector<ChannelData>& vecChannelData, std::vector<TriggersExt>& vecTriggersExt) + { + for (const auto& digit : vecDigit) { + std::cout << "\n______________DIGIT DATA____________"; + std::cout << std::hex; + std::cout << "\nBC: " << digit.mIntRecord.bc << "| ORBIT: " << digit.mIntRecord.orbit << " | EventID: " << digit.mEventID; + std::cout << "\nRef first: " << digit.ref.getFirstEntry() << "| Ref entries: " << digit.ref.getEntries(); + std::cout << "\nmTrigger: " << static_cast<uint16_t>(digit.mTriggers.triggersignals); + std::cout << "\nnChanA: " << static_cast<uint16_t>(digit.mTriggers.nChanA) << " | nChanC: " << static_cast<uint16_t>(digit.mTriggers.nChanC); + std::cout << "\namplA: " << digit.mTriggers.amplA << " | amplC: " << digit.mTriggers.amplC; + std::cout << "\ntimeA: " << digit.mTriggers.timeA << " | timeC: " << digit.mTriggers.timeC; + + std::cout << "\n______________CHANNEL DATA____________\n"; + for (int iChData = digit.ref.getFirstEntry(); iChData < digit.ref.getFirstEntry() + digit.ref.getEntries(); iChData++) { + + std::cout << "\nChId: " << static_cast<uint16_t>(vecChannelData[iChData].ChId) << " | ChainQTC:" << static_cast<uint16_t>(vecChannelData[iChData].ChainQTC) + << " | CFDTime: " << vecChannelData[iChData].CFDTime << " | QTCAmpl: " << vecChannelData[iChData].QTCAmpl; + } + std::cout << std::dec; + std::cout << "\n______________________________________\n"; + } + } +}; + +} // namespace ft0 +} // namespace o2 +#endif diff --git a/Detectors/FIT/FT0/raw/include/FT0Raw/RawReaderFT0Base.h b/Detectors/FIT/FT0/raw/include/FT0Raw/RawReaderFT0Base.h new file mode 100644 index 0000000000000..810bcad5d60fd --- /dev/null +++ b/Detectors/FIT/FT0/raw/include/FT0Raw/RawReaderFT0Base.h @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RawReaderFT0Base.h Base 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 +//Base class only provides static linkID-moduleType conformity + +#ifndef ALICEO2_FIT_RAWREADERFT0BASE_H_ +#define ALICEO2_FIT_RAWREADERFT0BASE_H_ +#include <iostream> +#include <vector> +#include <Rtypes.h> +#include "FT0Raw/DataBlockFT0.h" +#include "FT0Raw/DigitBlockFT0.h" +#include "FITRaw/RawReaderBase.h" + +#include <boost/mpl/inherit.hpp> +#include <boost/mpl/vector.hpp> + +#include <CommonDataFormat/InteractionRecord.h> +#include "Headers/RAWDataHeader.h" + +#include <gsl/span> + +using namespace o2::fit; +namespace o2 +{ +namespace ft0 +{ + +// Common raw reader for FT0 +template <class DigitBlockFT0type, class DataBlockPMtype, class DataBlockTCMtype> +class RawReaderFT0Base : public RawReaderBase<DigitBlockFT0type> +{ + public: + typedef RawReaderBase<DigitBlockFT0type> RawReaderBaseType; + RawReaderFT0Base() = default; + ~RawReaderFT0Base() = default; + //deserialize payload to raw data blocks and proccesss them to digits + void process(int linkID, gsl::span<const uint8_t> payload) + { + if (0 <= linkID && linkID < 19) { + //PM data proccessing + RawReaderBaseType::template processBinaryData<DataBlockPMtype>(payload, linkID); + } else if (linkID == 19) { + //TCM data proccessing + RawReaderBaseType::template processBinaryData<DataBlockTCMtype>(payload, linkID); + } else { + //put here code in case of bad rdh.linkID value + LOG(INFO) << "WARNING! WRONG LINK ID! " << linkID; + return; + } + + // + } +}; +//Normal TCM mode +using RawReaderFT0BaseNorm = RawReaderFT0Base<DigitBlockFT0, DataBlockPM, DataBlockTCM>; +//Extended TCM mode +using RawReaderFT0BaseExt = RawReaderFT0Base<DigitBlockFT0ext, DataBlockPM, DataBlockTCMext>; + +} // namespace ft0 +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx b/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx new file mode 100644 index 0000000000000..3a8eddd8b3a5c --- /dev/null +++ b/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FT0Raw/DataBlockFT0.h" +using namespace o2::ft0; diff --git a/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx b/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx new file mode 100644 index 0000000000000..0fdb98bed5ca1 --- /dev/null +++ b/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FT0Raw/DigitBlockFT0.h" +using namespace o2::ft0; + +int DigitBlockFT0::sEventID = 0; +o2::ft0::LookUpTable DigitBlockFT0::sLookupTable = o2::ft0::LookUpTable::readTable(); +int DigitBlockFT0ext::sEventID = 0; +o2::ft0::LookUpTable DigitBlockFT0ext::sLookupTable = o2::ft0::LookUpTable::readTable(); diff --git a/Detectors/FIT/FT0/raw/src/FT0RawLinkDef.h b/Detectors/FIT/FT0/raw/src/FT0RawLinkDef.h new file mode 100644 index 0000000000000..0928d12d07f28 --- /dev/null +++ b/Detectors/FIT/FT0/raw/src/FT0RawLinkDef.h @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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::ft0::DataBlockPM + ; +#pragma link C++ class o2::ft0::DataBlockTCM + ; +#pragma link C++ class o2::ft0::DataBlockTCMext + ; +#pragma link C++ class o2::ft0::DigitBlockFT0 + ; +#pragma link C++ class o2::ft0::DigitBlockFT0ext + ; + +#endif diff --git a/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx b/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx new file mode 100644 index 0000000000000..f4c76fb9585c0 --- /dev/null +++ b/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FT0Raw/RawReaderFT0Base.h" +using namespace o2::ft0; diff --git a/Detectors/FIT/FT0/reconstruction/CMakeLists.txt b/Detectors/FIT/FT0/reconstruction/CMakeLists.txt index dcb09b461e0fe..16a4eb8ffd97c 100644 --- a/Detectors/FIT/FT0/reconstruction/CMakeLists.txt +++ b/Detectors/FIT/FT0/reconstruction/CMakeLists.txt @@ -10,16 +10,22 @@ o2_add_library(FT0Reconstruction SOURCES src/CollisionTimeRecoTask.cxx - SOURCES src/ReadRaw.cxx + src/ReadRaw.cxx + src/CTFCoder.cxx + src/InteractionTag.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::Framework O2::FT0Base O2::DataFormatsFT0 O2::DetectorsRaw + O2::CommonDataFormat + O2::rANS O2::Headers) o2_target_root_dictionary( FT0Reconstruction HEADERS include/FT0Reconstruction/CollisionTimeRecoTask.h - HEADERS include/FT0Reconstruction/ReadRaw.h + include/FT0Reconstruction/ReadRaw.h + include/FT0Reconstruction/CTFCoder.h + include/FT0Reconstruction/InteractionTag.h ) diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h new file mode 100644 index 0000000000000..c6a922c529244 --- /dev/null +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -0,0 +1,195 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of FT0 digits data + +#ifndef O2_FT0_CTFCODER_H +#define O2_FT0_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include "FT0Base/Geometry.h" +#include "DataFormatsFT0/CTF.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/ChannelData.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" + +class TTree; + +namespace o2 +{ +namespace ft0 +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::FT0) {} + ~CTFCoder() = default; + + /// entropy-encode digits to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec); + + /// entropy decode clusters from buffer with CTF + template <typename VDIG, typename VCHAN> + void decode(const CTF::base& ec, VDIG& digitVec, VCHAN& channelVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + /// compres digits clusters to CompressedDigits + void compress(CompressedDigits& cd, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec); + size_t estimateCompressedSize(const CompressedDigits& cc); + + /// decompress CompressedDigits to digits + template <typename VDIG, typename VCHAN> + void decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& channelVec); + + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<Digit>& digitVec, std::vector<ChannelData>& channelVec); + + ClassDefNV(CTFCoder, 1); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec) +{ + 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_flags + MD::EENCODE, // BLC_idChan + MD::EENCODE, // BLC_qtcChain + MD::EENCODE, // BLC_cfdTime + MD::EENCODE // BLC_qtcAmpl + }; + CompressedDigits cd; + compress(cd, digitVec, channelVec); + + // book output size with some margin + auto szIni = estimateCompressedSize(cd); + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(cd.header); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODEFT0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODEFT0(cd.trigger, CTF::BLC_trigger, 0); + ENCODEFT0(cd.bcInc, CTF::BLC_bcInc, 0); + ENCODEFT0(cd.orbitInc, CTF::BLC_orbitInc, 0); + ENCODEFT0(cd.nChan, CTF::BLC_nChan, 0); + ENCODEFT0(cd.eventFlags, CTF::BLC_flags, 0); + ENCODEFT0(cd.idChan , CTF::BLC_idChan, 0); + ENCODEFT0(cd.qtcChain, CTF::BLC_qtcChain, 0); + ENCODEFT0(cd.cfdTime, CTF::BLC_cfdTime, 0); + ENCODEFT0(cd.qtcAmpl, CTF::BLC_qtcAmpl, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VDIG, typename VCHAN> +void CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& channelVec) +{ + CompressedDigits cd; + cd.header = ec.getHeader(); + ec.print(getPrefix()); +#define DECODEFT0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODEFT0(cd.trigger, CTF::BLC_trigger); + DECODEFT0(cd.bcInc, CTF::BLC_bcInc); + DECODEFT0(cd.orbitInc, CTF::BLC_orbitInc); + DECODEFT0(cd.nChan, CTF::BLC_nChan); + DECODEFT0(cd.eventFlags, CTF::BLC_flags); + DECODEFT0(cd.idChan, CTF::BLC_idChan); + DECODEFT0(cd.qtcChain, CTF::BLC_qtcChain); + DECODEFT0(cd.cfdTime, CTF::BLC_cfdTime); + DECODEFT0(cd.qtcAmpl, CTF::BLC_qtcAmpl); + // clang-format on + // + decompress(cd, digitVec, channelVec); +} + +/// decompress compressed digits to standard digits +template <typename VDIG, typename VCHAN> +void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& channelVec) +{ + digitVec.clear(); + channelVec.clear(); + digitVec.reserve(cd.header.nTriggers); + channelVec.reserve(cd.idChan.size()); + + uint32_t firstEntry = 0, clCount = 0, chipCount = 0; + o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); + + for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { + // restore ROFRecord + if (cd.orbitInc[idig]) { // non-0 increment => new orbit + ir.bc = cd.bcInc[idig]; // bcInc has absolute meaning + ir.orbit += cd.orbitInc[idig]; + } else { + ir.bc += cd.bcInc[idig]; + } + Triggers trig; + trig.triggersignals = cd.trigger[idig]; + + firstEntry = channelVec.size(); + uint8_t chID = 0; + int amplA = 0, amplC = 0, timeA = 0, timeC = 0; + for (uint8_t ic = 0; ic < cd.nChan[idig]; ic++) { + auto icc = channelVec.size(); + const auto& chan = channelVec.emplace_back((chID += cd.idChan[icc]), cd.cfdTime[icc], cd.qtcAmpl[icc], cd.qtcChain[icc]); + // + // rebuild digit + if (std::abs(chan.CFDTime) < Geometry::mTime_trg_gate) { + if (chan.ChId < 4 * uint8_t(Geometry::NCellsA)) { // A side + amplA += chan.QTCAmpl; + timeA += chan.CFDTime; + trig.nChanA++; + + } else { + amplC += chan.QTCAmpl; + timeC += chan.CFDTime; + trig.nChanC++; + } + } + } + if (trig.nChanA) { + trig.timeA = timeA / trig.nChanA; + trig.amplA = amplA / 8; + } + if (trig.nChanC) { + trig.timeC = timeC / trig.nChanC; + trig.amplC = amplC / 8; + } + digitVec.emplace_back(firstEntry, cd.nChan[idig], ir, trig, idig); + } +} + +} // namespace ft0 +} // namespace o2 + +#endif // O2_FT0_CTFCODER_H diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h new file mode 100644 index 0000000000000..962bb4b599ca9 --- /dev/null +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 + +#ifndef ALICEO2_FT0_INTERACTIONTAG_H +#define ALICEO2_FT0_INTERACTIONTAG_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "DataFormatsFT0/RecPoints.h" +#include "CommonConstants/LHCConstants.h" + +namespace o2 +{ +namespace ft0 +{ + +// These are configurable params for FT0 selection as interaction tag +struct InteractionTag : public o2::conf::ConfigurableParamHelper<InteractionTag> { + int minAmplitudeAC = 20; ///< use only FT0 triggers with high enough amplitude + + bool isSelected(const RecPoints& rp) const + { + return rp.isValidTime(RecPoints::TimeMean) && (rp.getTrigger().amplA + rp.getTrigger().amplC) > minAmplitudeAC; + } + + float getInteractionTimeNS(const RecPoints& rp, const o2::InteractionRecord& refIR) const + { + return rp.getInteractionRecord().differenceInBCNS(refIR); // RS FIXME do we want use precise MeanTime? + } + + O2ParamDef(InteractionTag, "ft0tag"); +}; + +} // namespace ft0 +} // end namespace o2 + +#endif diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/ReadRaw.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/ReadRaw.h index 47e83b3c16426..8b4620c443711 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/ReadRaw.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/ReadRaw.h @@ -42,8 +42,8 @@ class ReadRaw { static constexpr int Nchannels_FT0 = 208; static constexpr int Nchannels_PM = 12; - static constexpr int NPMs = 19; - static constexpr int LinkTCM = 18; + static constexpr int NPMs = 20; + static constexpr int LinkTCM = 19; static constexpr float MV_2_Nchannels = 2.2857143; //7 mV ->16channels static constexpr float CFD_NS_2_Nchannels = 76.804916; //1000.(ps)/13.02(channel); //static constexpr int GBTWORDSIZE = 80; //real size @@ -57,15 +57,6 @@ class ReadRaw void readData(const std::string fileRaw, const o2::ft0::LookUpTable& lut); void writeDigits(const std::string fileDecodeData); void close(); - static o2::ft0::LookUpTable linear() - { - std::vector<o2::ft0::Topo> lut_data(Nchannels_PM * NPMs); - for (int link = 0; link < NPMs; ++link) - for (int mcp = 0; mcp < Nchannels_PM; ++mcp) - lut_data[link * Nchannels_PM + mcp] = o2::ft0::Topo{link, mcp}; - - return o2::ft0::LookUpTable{lut_data}; - } private: std::ifstream mFileDest; diff --git a/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..fe15747186622 --- /dev/null +++ b/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,162 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of FT0 digits data + +#include "FT0Reconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::ft0; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, + std::vector<Digit>& digitVec, std::vector<ChannelData>& channelVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, digitVec, channelVec); +} + +///________________________________ +void CTFCoder::compress(CompressedDigits& cd, const gsl::span<const Digit>& digitVec, const gsl::span<const ChannelData>& channelVec) +{ + // convert digits/channel to their compressed version + cd.clear(); + if (!digitVec.size()) { + return; + } + const auto& dig0 = digitVec[0]; + cd.header.nTriggers = digitVec.size(); + cd.header.firstOrbit = dig0.getOrbit(); + cd.header.firstBC = dig0.getBC(); + + cd.trigger.resize(cd.header.nTriggers); + cd.bcInc.resize(cd.header.nTriggers); + cd.orbitInc.resize(cd.header.nTriggers); + cd.eventFlags.resize(cd.header.nTriggers); + cd.nChan.resize(cd.header.nTriggers); + + cd.idChan.resize(channelVec.size()); + cd.qtcChain.resize(channelVec.size()); + cd.cfdTime.resize(channelVec.size()); + cd.qtcAmpl.resize(channelVec.size()); + + uint16_t prevBC = cd.header.firstBC; + uint32_t prevOrbit = cd.header.firstOrbit; + uint32_t ccount = 0; + for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { + const auto& digit = digitVec[idig]; + const auto chanels = digit.getBunchChannelData(channelVec); // we assume the channels are sorted + + // fill trigger info + cd.trigger[idig] = digit.getTriggers().triggersignals; + cd.eventFlags[idig] = digit.getEventStatusWord(); + if (prevOrbit == digit.getOrbit()) { + cd.bcInc[idig] = digit.getBC() - prevBC; + cd.orbitInc[idig] = 0; + } else { + cd.bcInc[idig] = digit.getBC(); + cd.orbitInc[idig] = digit.getOrbit() - prevOrbit; + } + prevBC = digit.getBC(); + prevOrbit = digit.getOrbit(); + // fill channels info + cd.nChan[idig] = chanels.size(); + if (!cd.nChan[idig]) { + LOG(ERROR) << "Digits with no channels"; + continue; + } + uint8_t prevChan = 0; + for (uint8_t ic = 0; ic < cd.nChan[idig]; ic++) { + assert(prevChan <= chanels[ic].ChId); + cd.idChan[ccount] = chanels[ic].ChId - prevChan; + cd.qtcChain[ccount] = chanels[ic].ChainQTC; + cd.cfdTime[ccount] = chanels[ic].CFDTime; + cd.qtcAmpl[ccount] = chanels[ic].QTCAmpl; + prevChan = chanels[ic].ChId; + ccount++; + } + } +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + CompressedDigits cd; // just to get member types +#define MAKECODER(part, slot) createCoder<decltype(part)::value_type>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(cd.trigger, CTF::BLC_trigger); + MAKECODER(cd.bcInc, CTF::BLC_bcInc); + MAKECODER(cd.orbitInc, CTF::BLC_orbitInc); + MAKECODER(cd.nChan, CTF::BLC_nChan); + MAKECODER(cd.eventFlags, CTF::BLC_flags); + MAKECODER(cd.idChan, CTF::BLC_idChan); + MAKECODER(cd.qtcChain, CTF::BLC_qtcChain); + MAKECODER(cd.cfdTime, CTF::BLC_cfdTime); + MAKECODER(cd.qtcAmpl, CTF::BLC_qtcAmpl); + // clang-format on +} + +///________________________________ +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<decltype(vec)>::type::value_type +#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ + rans::calculateMaxBufferSize(vec.size(), reinterpret_cast<const o2::rans::LiteralEncoder64<VTP(vec)>*>(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.eventFlags, CTF::BLC_flags); + 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 + + LOG(INFO) << "Estimated output size is " << sz << " bytes"; + return sz; +} diff --git a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx index 538d31e3b95e3..492a588dbd90c 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -30,7 +30,7 @@ o2::ft0::RecPoints CollisionTimeRecoTask::process(o2::ft0::Digit const& bcd, gsl::span<const o2::ft0::ChannelData> inChData, gsl::span<o2::ft0::ChannelDataFloat> outChData) { - LOG(INFO) << "Running reconstruction on new event"; + LOG(DEBUG) << "Running reconstruction on new event"; Int_t ndigitsC = 0, ndigitsA = 0; @@ -42,7 +42,7 @@ o2::ft0::RecPoints CollisionTimeRecoTask::process(o2::ft0::Digit const& bcd, auto timeStamp = o2::InteractionRecord::bc2ns(bcd.mIntRecord.bc, bcd.mIntRecord.orbit); - LOG(INFO) << " event time " << timeStamp << " orbit " << bcd.mIntRecord.orbit << " bc " << bcd.mIntRecord.bc; + LOG(DEBUG) << " event time " << timeStamp << " orbit " << bcd.mIntRecord.orbit << " bc " << bcd.mIntRecord.bc; int nch = inChData.size(); for (int ich = 0; ich < nch; ich++) { @@ -76,7 +76,7 @@ o2::ft0::RecPoints CollisionTimeRecoTask::process(o2::ft0::Digit const& bcd, } else { mCollisionTime[TimeMean] = std::min(mCollisionTime[TimeA], mCollisionTime[TimeC]); } - LOG(INFO) << " Collision time " << mCollisionTime[TimeA] << " " << mCollisionTime[TimeC] << " " << mCollisionTime[TimeMean] << " " << mCollisionTime[Vertex]; + LOG(DEBUG) << " Collision time " << mCollisionTime[TimeA] << " " << mCollisionTime[TimeC] << " " << mCollisionTime[TimeMean] << " " << mCollisionTime[Vertex]; return RecPoints{ mCollisionTime, bcd.ref.getFirstEntry(), bcd.ref.getEntries(), bcd.mIntRecord, bcd.mTriggers}; } diff --git a/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h b/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h index 24b5e237b0cad..e8811907900b5 100644 --- a/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h +++ b/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h @@ -16,5 +16,7 @@ #pragma link C++ class o2::ft0::CollisionTimeRecoTask + ; #pragma link C++ class o2::ft0::ReadRaw + ; +#pragma link C++ class o2::ft0::CTFCoder + ; +#pragma link C++ class o2::ft0::InteractionTag + ; #endif diff --git a/Detectors/FIT/FT0/reconstruction/src/InteractionTag.cxx b/Detectors/FIT/FT0/reconstruction/src/InteractionTag.cxx new file mode 100644 index 0000000000000..a384e40f28895 --- /dev/null +++ b/Detectors/FIT/FT0/reconstruction/src/InteractionTag.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexerParams.cxx +/// \brief Configurable params for primary vertexer +/// \author ruben.shahoyan@cern.ch + +#include "FT0Reconstruction/InteractionTag.h" + +O2ParamImpl(o2::ft0::InteractionTag); diff --git a/Detectors/FIT/FT0/reconstruction/src/ReadRaw.cxx b/Detectors/FIT/FT0/reconstruction/src/ReadRaw.cxx index 27c6008b33ab0..451d211690ec6 100644 --- a/Detectors/FIT/FT0/reconstruction/src/ReadRaw.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/ReadRaw.cxx @@ -73,7 +73,7 @@ ReadRaw::ReadRaw(const std::string fileRaw, std::string fileDataOut) << "file to read " << fileRaw.data() << " file to write " << fileDataOut.data(); mFileDest.exceptions(std::ios_base::failbit | std::ios_base::badbit); mFileDest.open(fileRaw, std::fstream::in | std::fstream::binary); - o2::ft0::LookUpTable lut{o2::ft0::ReadRaw::linear()}; + o2::ft0::LookUpTable lut{LookUpTable::readTable()}; ReadRaw::readData(fileRaw.c_str(), lut); ReadRaw::writeDigits(fileDataOut.data()); } @@ -131,7 +131,7 @@ void ReadRaw::readData(const std::string fileRaw, const o2::ft0::LookUpTable& lu digit.setInteractionRecord(intrec); } chDgDataArr = &digitIter->second.getChDgData(); //&mDigitsTemp.getChDgData(); - if (link == 18) { + if (link == LinkTCM) { mFileDest.read(reinterpret_cast<char*>(&mTCMdata), sizeof(mTCMdata)); pos += sizeof(mTCMdata); digitIter->second.setTriggers(Bool_t(mTCMdata.orA), Bool_t(mTCMdata.orC), Bool_t(mTCMdata.vertex), Bool_t(mTCMdata.sCen), Bool_t(mTCMdata.cen), uint8_t(mTCMdata.nChanA), uint8_t(mTCMdata.nChanC), int32_t(mTCMdata.amplA), int32_t(mTCMdata.amplC), int16_t(mTCMdata.timeA), int16_t(mTCMdata.timeC)); @@ -173,8 +173,9 @@ void ReadRaw::readData(const std::string fileRaw, const o2::ft0::LookUpTable& lu void ReadRaw::close() { LOG(INFO) << " CLOSE "; - if (mFileDest.is_open()) + if (mFileDest.is_open()) { mFileDest.close(); + } } //_____________________________________________________________________________ void ReadRaw::writeDigits(std::string fileDataOut) @@ -196,8 +197,9 @@ void ReadRaw::writeDigits(std::string fileDataOut) std::vector<o2::ft0::ChannelData> chDataVec; digitVec.reserve(mDigitAccum.size()); size_t numberOfChData = 0; - for (auto const& [intrec, digit] : mDigitAccum) + for (auto const& [intrec, digit] : mDigitAccum) { numberOfChData += digit.getChDgData().size(); + } chDataVec.reserve(numberOfChData); for (auto& [intrec, digit] : mDigitAccum) { int first = gsl::narrow_cast<int>(chDataVec.size()); diff --git a/Detectors/FIT/FT0/simulation/CMakeLists.txt b/Detectors/FIT/FT0/simulation/CMakeLists.txt index 918a248854d24..23dc494992eab 100644 --- a/Detectors/FIT/FT0/simulation/CMakeLists.txt +++ b/Detectors/FIT/FT0/simulation/CMakeLists.txt @@ -17,6 +17,8 @@ o2_add_library(FT0Simulation O2::FT0Base O2::DataFormatsFT0 O2::DetectorsRaw + O2::CCDB + O2::DetectorsCalibration O2::Headers) o2_target_root_dictionary(FT0Simulation HEADERS diff --git a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Detector.h b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Detector.h index 9eea2415300ee..aade6f60fb974 100644 --- a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Detector.h +++ b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Detector.h @@ -109,6 +109,8 @@ class Detector : public o2::base::DetImpl<Detector> /// \param istream *is The input stream void Read(std::istream* is); + void DefineSim2LUTindex(); + private: /// copy constructor (used in MT) Detector(const Detector& rhs); @@ -152,7 +154,9 @@ class Detector : public o2::base::DetImpl<Detector> int mTrackIdTop; int mTrackIdMCPtop; //TEMPORARY - ClassDefOverride(Detector, 2); + int mSim2LUT[Geometry::Nchannels]; + + ClassDefOverride(Detector, 3); }; // Input and output function for standard C++ input/output. diff --git a/Detectors/FIT/FT0/simulation/include/FT0Simulation/DigitizationParameters.h b/Detectors/FIT/FT0/simulation/include/FT0Simulation/DigitizationParameters.h index 48debbc36b2f3..1552631a2bb80 100644 --- a/Detectors/FIT/FT0/simulation/include/FT0Simulation/DigitizationParameters.h +++ b/Detectors/FIT/FT0/simulation/include/FT0Simulation/DigitizationParameters.h @@ -29,12 +29,11 @@ struct DigitizationParameters { float mAmpRecordLow = -4; // integrate charge from float mAmpRecordUp = 15; // to [ns] - int mTime_trg_gate = 189; //2500/13; (+-2ns) #channels + int mTime_trg_gate = 192; // #channels // int mTime_trg_gate = 2000; //2000/13; (+-2ns) #channels no limits float mTimeDiffAC = (Geometry::ZdetA - Geometry::ZdetC) * TMath::C(); float C_side_cable_cmps = 2.86; //ns float A_side_cable_cmps = 11.110; //ns - int mSignalWidth = 189; //+-2500.ps/13.2ps #channels int mtrg_central_trh = 600.; // channels int mtrg_semicentral_trh = 300.; // channels diff --git a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h index c53064101a6e7..b3900e66cba07 100644 --- a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h +++ b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h @@ -45,12 +45,15 @@ class Digitizer void process(const std::vector<o2::ft0::HitType>* hits, std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& label); void flush(std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& label); void flush_all(std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& label); void initParameters(); void printParameters() const; @@ -94,8 +97,9 @@ class Digitizer protected: inline float signalForm(float x) const { // table lookup for the signal shape - if (x <= 0.0f) + if (x <= 0.0f) { return 0.0f; + } float const y = x / mParameters.bunchWidth * DP::SIGNAL_TABLE_SIZE; int const index = std::floor(y); if (index + 1 >= DP::SIGNAL_TABLE_SIZE) { @@ -112,8 +116,9 @@ class Digitizer auto const rem = y - index; Vc::float_v val(0); for (size_t i = 0; i < float_v::size(); ++i) { - if (y[i] < 0.0f) + if (y[i] < 0.0f) { continue; + } if (index[i] + 1 < DP::SIGNAL_TABLE_SIZE) { val[i] = mSignalTable[index[i]] + rem[i] * (mSignalTable[index[i] + 1] - mSignalTable[index[i]]); } else { @@ -148,6 +153,7 @@ class Digitizer void storeBC(BCCache& bc, std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& labels); ClassDefNV(Digitizer, 1); @@ -170,8 +176,9 @@ inline float signalForm_integral(float x) using namespace std; double const a = -0.45458; double const b = -0.83344945; - if (x < 0) + if (x < 0) { x = 0; + } return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; }; diff --git a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digits2Raw.h b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digits2Raw.h index 9cbfa837a0881..8e36a59433cea 100644 --- a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digits2Raw.h +++ b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digits2Raw.h @@ -42,8 +42,8 @@ class Digits2Raw static constexpr int Nchannels_FT0 = 208; static constexpr int Nchannels_PM = 12; - static constexpr int LinkTCM = 18; - static constexpr int NPMs = 19; + static constexpr int LinkTCM = 19; + static constexpr int NPMs = 20; static constexpr int GBTWordSize = 128; // with padding static constexpr int Max_Page_size = 8192; @@ -56,15 +56,6 @@ class Digits2Raw const o2::ft0::LookUpTable& lut, o2::InteractionRecord const& mIntRecord); - static o2::ft0::LookUpTable linear() - { - std::vector<o2::ft0::Topo> lut_data(Nchannels_PM * (NPMs - 1)); - for (int link = 0; link < NPMs - 1; ++link) - for (int mcp = 0; mcp < Nchannels_PM; ++mcp) - lut_data[link * Nchannels_PM + mcp] = o2::ft0::Topo{link, mcp}; - - return o2::ft0::LookUpTable{lut_data}; - } o2::raw::RawFileWriter& getWriter() { return mWriter; } void setFilePerLink(bool v) { mOutputPerLink = v; } diff --git a/Detectors/FIT/FT0/simulation/src/Detector.cxx b/Detectors/FIT/FT0/simulation/src/Detector.cxx index dc1693129de85..c5b0845be484d 100644 --- a/Detectors/FIT/FT0/simulation/src/Detector.cxx +++ b/Detectors/FIT/FT0/simulation/src/Detector.cxx @@ -183,6 +183,8 @@ void Detector::ConstructGeometry() tr[itr] = new TGeoTranslation(nameTr.Data(), xa[itr], ya[itr], z); tr[itr]->RegisterYourself(); stlinA->AddNode(ins, itr, tr[itr]); + LOG(INFO) << " detID " + << " " << itr << " x " << xa[itr] << " y " << ya[itr]; } TGeoRotation* rot[Geometry::NCellsC]; @@ -205,12 +207,10 @@ void Detector::ConstructGeometry() // com[itr-Geometry::NCellsA] = new TGeoCombiTrans(tr[itr],rot[itr-Geometry::NCellsA]); com[ic] = new TGeoCombiTrans(xc2[ic], yc2[ic], (zc2[ic] - 80), rot[ic]); - std::cout << ic << " " << xc2[ic] << " " << yc2[ic] << std::endl; TGeoHMatrix hm = *com[ic]; TGeoHMatrix* ph = new TGeoHMatrix(hm); stlinC->AddNode(ins, itr, ph); } - TGeoVolume* alice = gGeoManager->GetVolume("barrel"); alice->AddNode(stlinA, 1, new TGeoTranslation(0, 30., zdetA)); TGeoRotation* rotC = new TGeoRotation("rotC", 90., 0., 90., 90., 180., 0.); @@ -225,6 +225,7 @@ void Detector::ConstructOpGeometry() LOG(DEBUG) << "Creating FIT optical geometry properties"; DefineOpticalProperties(); + DefineSim2LUTindex(); } //_________________________________________ @@ -273,13 +274,10 @@ void Detector::SetOneMCP(TGeoVolume* ins) topref->AddNode(top, 1, new TGeoTranslation(0, 0, 0)); float xinv = -ptop[0] - prfv[0]; topref->AddNode(rfv, 1, new TGeoTranslation(xinv, 0, 0)); - printf(" GEOGEO refv %f , 0,0 \n", xinv); xinv = ptop[0] + prfv[0]; topref->AddNode(rfv, 2, new TGeoTranslation(xinv, 0, 0)); - printf(" GEOGEO refv %f , 0,0 \n", xinv); float yinv = -ptop[1] - prfh[1]; topref->AddNode(rfh, 1, new TGeoTranslation(0, yinv, 0)); - printf(" GEOGEO refh , 0, %f, 0 \n", yinv); yinv = ptop[1] + prfh[1]; topref->AddNode(rfh, 2, new TGeoTranslation(0, yinv, 0)); // zin = -ptop[2] - ptopblack[2]; @@ -296,6 +294,7 @@ void Detector::SetOneMCP(TGeoVolume* ins) ins->AddNode(topref, ntops, new TGeoTranslation(xin, yin, z)); z += ptopref[2] + 2. * pmcptopglass[2] + preg[2]; ins->AddNode(cat, ntops, new TGeoTranslation(xin, yin, z)); + LOG(INFO) << " n " << ntops << " x " << xin << " y " << yin; } } //Al top @@ -351,12 +350,12 @@ Bool_t Detector::ProcessHits(FairVolume* v) fMC->CurrentVolOffID(1, mcp); float time = fMC->TrackTime() * 1.0e9; //time from seconds to ns int trackID = stack->GetCurrentTrackNumber(); - int detID = 4 * mcp + quadrant - 1; + int detID = mSim2LUT[4 * mcp + quadrant - 1]; float etot = fMC->Etot(); int iPart = fMC->TrackPid(); float enDep = fMC->Edep(); Int_t parentID = stack->GetCurrentTrack()->GetMother(0); - if (fMC->TrackCharge()) { //charge particles for MCtrue + if (fMC->TrackCharge() && volname.Contains("0REG")) { //charge particles for MCtrue AddHit(x, y, z, time, 10, trackID, detID); } if (iPart == 50000050) { // If particles is photon then ... @@ -478,8 +477,9 @@ void Detector::DefineOpticalProperties() // Path of the optical properties input file TString inputDir; const char* aliceO2env = std::getenv("O2_ROOT"); - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/FT0/files/"; TString optPropPath = inputDir + "quartzOptProperties.txt"; @@ -560,8 +560,9 @@ Bool_t Detector::RegisterPhotoE(float energy) { float eff = mPMTeff->Eval(energy); float p = gRandom->Rndm(); - if (p > eff) + if (p > eff) { return kFALSE; + } return kTRUE; } @@ -638,3 +639,30 @@ Int_t Detector::ReadOptProperties(const std::string filePath) LOG(INFO) << "Optical properties taken from the file: " << filePath.c_str() << " Number of lines read: " << iLine; return 0; } + +void Detector::DefineSim2LUTindex() +{ + // Path of the LookUp table + std::string inputDir; + const char* aliceO2env = std::getenv("O2_ROOT"); + if (aliceO2env) { + inputDir = aliceO2env; + } + inputDir += "/share/Detectors/FT0/files/"; + + std::string indPath = inputDir + "Sim2DataChannels.txt"; + indPath = gSystem->ExpandPathName(indPath.data()); // Expand $(ALICE_ROOT) into real system path + + std::ifstream infile; + 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(); + } + int fromfile; + for (int iind = 0; iind < Geometry::Nchannels; iind++) { + infile >> fromfile; + mSim2LUT[iind] = fromfile; + } +} diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx index 74c8fa29c073f..e2cc4180bfe04 100644 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -121,12 +121,6 @@ Digitizer::CFDOutput Digitizer::get_time(const std::vector<float>& times, float is_positive = cfd_val > 0.0f; } } - if (!result.particle) { - LOG(DEBUG) << "CFD failed to find peak "; - for (auto t : times) { - LOG(DEBUG) << t << ", dead time " << deadTime; - } - } return result; } @@ -156,41 +150,52 @@ double Digitizer::measure_amplitude(const std::vector<float>& times) const void Digitizer::process(const std::vector<o2::ft0::HitType>* hits, std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& label) { ; //Calculating signal time, amplitude in mean_time +- time_gate -------------- - flush(digitsBC, digitsCh, label); + LOG(DEBUG) << " process firstBCinDeque " << firstBCinDeque << " mIntRecord " << mIntRecord; + if (firstBCinDeque != mIntRecord) { + flush(digitsBC, digitsCh, digitsTrig, label); + } + Int_t parent = -10; for (auto const& hit : *hits) { - if (hit.GetEnergyLoss() > 0) + if (hit.GetEnergyLoss() > 0) { continue; + } Int_t hit_ch = hit.GetDetectorID(); Bool_t is_A_side = (hit_ch < 4 * mParameters.nCellsA); Float_t time_compensate = is_A_side ? mParameters.A_side_cable_cmps : mParameters.C_side_cable_cmps; Double_t hit_time = hit.GetTime() - time_compensate; + if (hit_time > 250) { + continue; //not collect very slow particles + } auto relBC = o2::InteractionRecord{hit_time}; if (mCache.size() <= relBC.bc) { mCache.resize(relBC.bc + 1); } mCache[relBC.bc].hits.emplace_back(BCCache::particle{hit_ch, hit_time - relBC.bc2ns()}); - //charge particles in MCLabel Int_t parentID = hit.GetTrackID(); - if (parentID != parent) + if (parentID != parent) { mCache[relBC.bc].labels.emplace(parentID, mEventID, mSrcID, hit_ch); - parent = parentID; + parent = parentID; + } } } void Digitizer::storeBC(BCCache& bc, std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& labels) { - if (bc.hits.empty()) + if (bc.hits.empty()) { return; + } 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 vertex_time; @@ -204,35 +209,38 @@ void Digitizer::storeBC(BCCache& bc, 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 < mParameters.mAmp_trsh) + if (channel_end - channel_begin < mParameters.mAmp_trsh) { continue; + } channel_times.resize(channel_end - channel_begin); std::transform(channel_begin, channel_end, channel_times.begin(), [](BCCache::particle const& p) { return p.hit_time; }); int chain = (std::rand() % 2) ? 1 : 0; - auto cfd = get_time(channel_times, mDeadTimes[ipmt].intrec.bc2ns() - firstBCinDeque.bc2ns() + mDeadTimes[ipmt].deadTime); mDeadTimes[ipmt].intrec = firstBCinDeque; mDeadTimes[ipmt].deadTime = cfd.deadTime; - if (!cfd.particle) + if (!cfd.particle) { continue; + } int smeared_time = 1000. * (*cfd.particle - mParameters.mCfdShift) * mParameters.ChannelWidthInverse; bool is_time_in_signal_gate = (smeared_time > -mParameters.mTime_trg_gate && smeared_time < mParameters.mTime_trg_gate); float charge = measure_amplitude(channel_times) * mParameters.charge2amp; float amp = is_time_in_signal_gate ? mParameters.mV_2_Nchannels * charge : 0; - if (amp > 4095) + if (amp > 4095) { amp = 4095; - LOG(DEBUG) << "bc " << firstBCinDeque.bc << ", ipmt " << ipmt << ", smeared_time " << smeared_time << " nStored " << nStored; + } + LOG(DEBUG) << mEventID << " bc " << firstBCinDeque.bc << " orbit " << firstBCinDeque.orbit << ", ipmt " << ipmt << ", smeared_time " << smeared_time << " nStored " << nStored; digitsCh.emplace_back(ipmt, smeared_time, int(amp), chain); nStored++; // fill triggers Bool_t is_A_side = (ipmt <= 4 * mParameters.nCellsA); - if (smeared_time > mParameters.mTime_trg_gate || smeared_time < -mParameters.mTime_trg_gate) + if (!is_time_in_signal_gate) { continue; + } if (is_A_side) { n_hit_A++; @@ -251,26 +259,28 @@ void Digitizer::storeBC(BCCache& bc, is_SemiCentral = summ_ampl_A + summ_ampl_C >= mParameters.mtrg_semicentral_trh; vertex_time = (mean_time_A - mean_time_C) * 0.5; isVertex = is_A && is_C && (vertex_time > -mParameters.mTime_trg_gate && vertex_time < mParameters.mTime_trg_gate); - uint32_t amplA = is_A ? summ_ampl_A : 0; // sum amplitude A side - uint32_t amplC = is_C ? summ_ampl_C : 0; // sum amplitude C side + uint32_t amplA = is_A ? summ_ampl_A * 0.125 : 0; // sum amplitude A side / 8 (hardware) + uint32_t amplC = is_C ? summ_ampl_C * 0.125 : 0; // sum amplitude C side / 8 (hardware) uint16_t timeA = is_A ? mean_time_A / n_hit_A : 0; // average time A side uint16_t timeC = is_C ? mean_time_C / n_hit_C : 0; // average time C side Triggers triggers; - triggers.setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C), - amplA, amplC, timeA, timeC); - - digitsBC.emplace_back(first, nStored, firstBCinDeque, triggers, mEventID); - size_t const nBC = digitsBC.size(); - for (auto const& lbl : bc.labels) - labels.addElement(nBC - 1, lbl); - + if (nStored > 0) { + triggers.setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C), + amplA, amplC, timeA, timeC); + digitsBC.emplace_back(first, nStored, firstBCinDeque, triggers, mEventID - 1); + digitsTrig.emplace_back(firstBCinDeque, is_A, is_C, isVertex, is_Central, is_SemiCentral); + size_t const nBC = digitsBC.size(); + for (auto const& lbl : bc.labels) { + labels.addElement(nBC - 1, lbl); + } + } // Debug output ------------------------------------------------------------- LOG(INFO) << "Event ID: " << mEventID << ", bc " << firstBCinDeque.bc << ", N hit " << bc.hits.size(); LOG(INFO) << "N hit A: " << int(triggers.nChanA) << " N hit C: " << int(triggers.nChanC) << " summ ampl A: " << int(triggers.amplA) << " summ ampl C: " << int(triggers.amplC) << " mean time A: " << triggers.timeA - << " mean time C: " << triggers.timeC; + << " mean time C: " << triggers.timeC << " nStored " << nStored; LOG(INFO) << "IS A " << triggers.getOrA() << " IsC " << triggers.getOrC() << " vertex " << triggers.getVertex() << " is Central " << triggers.getCen() << " is SemiCentral " << triggers.getSCen(); } @@ -278,12 +288,14 @@ void Digitizer::storeBC(BCCache& bc, //------------------------------------------------------------------------ void Digitizer::flush(std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& labels) { - LOG(DEBUG) << "firstBCinDeque " << firstBCinDeque << " mIntRecord " << mIntRecord; + assert(firstBCinDeque <= mIntRecord); + while (firstBCinDeque < mIntRecord && !mCache.empty()) { - storeBC(mCache.front(), digitsBC, digitsCh, labels); + storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig, labels); mCache.pop_front(); ++firstBCinDeque; } @@ -292,12 +304,14 @@ void Digitizer::flush(std::vector<o2::ft0::Digit>& digitsBC, void Digitizer::flush_all(std::vector<o2::ft0::Digit>& digitsBC, std::vector<o2::ft0::ChannelData>& digitsCh, + std::vector<o2::ft0::DetTrigInput>& digitsTrig, o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>& labels) { - LOG(INFO) << "firstBCinDeque " << firstBCinDeque << " mIntRecord " << mIntRecord; + assert(firstBCinDeque <= mIntRecord); + ++mEventID; while (!mCache.empty()) { - storeBC(mCache.front(), digitsBC, digitsCh, labels); + storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig, labels); mCache.pop_front(); ++firstBCinDeque; } @@ -314,7 +328,7 @@ void Digitizer::initParameters() // set up tables with sinc function values (times noiseVar) for (size_t i = 0, n = mSincTable.size(); i < n; ++i) { float const time = i / float(n) * mParameters.mNoisePeriod; // [0 .. 1/mParameters.mNoisePeriod) - std::cout << "initParameters " << i << "/" << n << " " << time << std::endl; + LOG(DEBUG) << "initParameters " << i << "/" << n << " " << time; // we make a table of sinc values between -num_noise_samples and 2*num_noise_samples mSincTable[i].resize(3 * mNumNoiseSamples); for (int j = -mNumNoiseSamples; j < 2 * mNumNoiseSamples; ++j) { @@ -346,7 +360,7 @@ void Digitizer::printParameters() const { LOG(INFO) << " Run Digitzation with parametrs: \n" << " CFD amplitude threshold \n " << mParameters.mCFD_trsh << " CFD signal gate in ps \n" - << mParameters.mSignalWidth << "shift to have signal around zero after CFD trancformation \n" + << mParameters.mTime_trg_gate << "shift to have signal around zero after CFD trancformation \n" << mParameters.mCfdShift << "CFD distance between 0.3 of max amplitude to max \n" << mParameters.mCFDShiftPos << "MIP -> mV " << mParameters.mMip_in_V << " Pe in MIP \n" << mParameters.mPe_in_mip << "noise level " << mParameters.mNoiseVar << " noise frequency \n" diff --git a/Detectors/FIT/FT0/simulation/src/Digits2Raw.cxx b/Detectors/FIT/FT0/simulation/src/Digits2Raw.cxx index fa7040e94c7cc..a518b580897cc 100644 --- a/Detectors/FIT/FT0/simulation/src/Digits2Raw.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digits2Raw.cxx @@ -46,8 +46,11 @@ Continueous mode : for only bunches with data at least in 1 channel. #include "DetectorsRaw/HBFUtils.h" #include "DetectorsRaw/RawFileWriter.h" #include "CommonUtils/StringUtils.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" #include <Framework/Logger.h> #include <TStopwatch.h> +#include <TSystem.h> #include <cassert> #include <fstream> #include <vector> @@ -58,6 +61,7 @@ Continueous mode : for only bunches with data at least in 1 channel. #include <gsl/span> using namespace o2::ft0; +using CcdbManager = o2::ccdb::BasicCCDBManager; ClassImp(Digits2Raw); @@ -72,8 +76,25 @@ void Digits2Raw::readDigits(const std::string& outDir, const std::string& fileDi { LOG(INFO) << "**********Digits2Raw::convertDigits" << std::endl; - o2::ft0::LookUpTable lut{o2::ft0::Digits2Raw::linear()}; - LOG(DEBUG) << " ##### LookUp set "; + /* + std::string inputDir; + const char* aliceO2env = std::getenv("O2_ROOT"); + if (aliceO2env) { + inputDir = aliceO2env; + } + inputDir += "/share/Detectors/FT0/files/"; + + std::string lutPath = inputDir + "FT0ChannelsTable.txt"; + lutPath = gSystem->ExpandPathName(lutPath.data()); // Expand $(ALICE_ROOT) into real system path + + std::ifstream infile; + infile.open(lutPath.c_str()); + if (!infile.is_open()) { + LOG(ERROR) << "!!!! no LUT"; + } + */ + o2::ft0::LookUpTable lut{o2::ft0::LookUpTable::readTable()}; + LOG(INFO) << " ##### LookUp set "; std::string outd = outDir; if (outd.back() != '/') { @@ -114,8 +135,9 @@ void Digits2Raw::readDigits(const std::string& outDir, const std::string& fileDi auto& bcd = digitsBC[ibc]; intRecord = bcd.getIntRecord(); auto channels = bcd.getBunchChannelData(digitsCh); - if (!channels.empty()) + if (!channels.empty()) { convertDigits(bcd, channels, lut, intRecord); + } } } } @@ -136,8 +158,9 @@ void Digits2Raw::convertDigits(o2::ft0::Digit bcdigits, if (oldlink >= 0) { uint nGBTWords = uint((nchannels + 1) / 2); LOG(DEBUG) << " oldlink " << oldlink << " nGBTWords " << nGBTWords; - if ((nchannels % 2) == 1) + if ((nchannels % 2) == 1) { mRawEventData.mEventData[nchannels] = {}; + } mRawEventData.mEventHeader.nGBTWords = nGBTWords; auto data = mRawEventData.to_vector(false); mLinkID = uint32_t(oldlink); @@ -147,32 +170,27 @@ void Digits2Raw::convertDigits(o2::ft0::Digit bcdigits, oldlink = nlink; mRawEventData.mEventHeader = makeGBTHeader(nlink, intRecord); nchannels = 0; - // LOG(INFO) << " switch to new link " << nlink; + LOG(DEBUG) << " switch to new link " << nlink; } auto& newData = mRawEventData.mEventData[nchannels]; bool isAside = (pmchannels[ich].ChId < 96); newData.charge = pmchannels[ich].QTCAmpl; - newData.time = pmchannels[ich].CFDTime; - newData.is1TimeLostEvent = 0; - newData.is2TimeLostEvent = 0; - newData.isADCinGate = 1; - newData.isAmpHigh = 0; - newData.isDoubleEvent = 0; - newData.isEventInTVDC = 1; - newData.isTimeInfoLate = 0; - newData.isTimeInfoLost = 0; - int chain = std::rand() % 2; - newData.numberADC = chain ? 1 : 0; + newData.generateFlags(); newData.channelID = lut.getMCP(pmchannels[ich].ChId); - // LOG(INFO) << "packed GBT " << nlink << " channelID " << (int)newData.channelID << " charge " << newData.charge << " time " << newData.time << " chain " << int(newData.numberADC) << " size " << sizeof(newData); + LOG(DEBUG) << "packed GBT " << nlink << " channelID " << (int)newData.channelID << " charge " << newData.charge << " time " << newData.time << " chain " << int(newData.numberADC) << " size " << sizeof(newData); nchannels++; } // fill mEventData[nchannels] with 0s to flag that this is a dummy data uint nGBTWords = uint((nchannels + 1) / 2); - if ((nchannels % 2) == 1) + if ((nchannels % 2) == 1) { mRawEventData.mEventData[nchannels] = {}; + } mRawEventData.mEventHeader.nGBTWords = nGBTWords; + auto datalast = mRawEventData.to_vector(false); + mLinkID = uint32_t(oldlink); + mFeeID = uint64_t(oldlink); + mWriter.addData(mFeeID, mCruID, mLinkID, mEndPointID, intRecord, datalast); LOG(DEBUG) << " last " << oldlink; //TCM mRawEventData.mEventHeader = makeGBTHeader(LinkTCM, intRecord); //TCM @@ -182,10 +200,12 @@ void Digits2Raw::convertDigits(o2::ft0::Digit bcdigits, float ampA = mTriggers.amplA; float ampC = mTriggers.amplC; - if (ampA > 131071) + if (ampA > 131071) { ampA = 131071; //2^17 - if (ampC > 131071) + } + if (ampC > 131071) { ampC = 131071; //2^17 + } tcmdata.vertex = mTriggers.getVertex(); tcmdata.orA = mTriggers.getOrA(); tcmdata.orC = mTriggers.getOrC(); @@ -197,22 +217,21 @@ void Digits2Raw::convertDigits(o2::ft0::Digit bcdigits, tcmdata.amplC = ampC; tcmdata.timeA = mTriggers.timeA; tcmdata.timeC = mTriggers.timeC; - if (mVerbosity > 0) { - LOG(INFO) << " triggers read " - << " time A " << mTriggers.timeA << " time C " << mTriggers.timeC - << " amp A " << ampA << " amp C " << ampC - << " N A " << int(mTriggers.nChanA) << " N C " << int(mTriggers.nChanC) - << " trig " - << " ver " << mTriggers.getVertex() << " A " << mTriggers.getOrA() << " C " << mTriggers.getOrC(); - - LOG(INFO) << "TCMdata" - << " time A " << tcmdata.timeA << " time C " << tcmdata.timeC - << " amp A " << tcmdata.amplA << " amp C " << tcmdata.amplC - << " N A " << int(tcmdata.nChanA) << " N C " << int(tcmdata.nChanC) - << " trig " - << " ver " << tcmdata.vertex << " A " << tcmdata.orA << " C " << tcmdata.orC - << " size " << sizeof(tcmdata); - } + LOG(DEBUG) << " TCM triggers read " + << " time A " << mTriggers.timeA << " time C " << mTriggers.timeC + << " amp A " << ampA << " amp C " << ampC + << " N A " << int(mTriggers.nChanA) << " N C " << int(mTriggers.nChanC) + << " trig " + << " ver " << mTriggers.getVertex() << " A " << mTriggers.getOrA() << " C " << mTriggers.getOrC(); + + LOG(DEBUG) << "TCMdata" + << " time A " << tcmdata.timeA << " time C " << tcmdata.timeC + << " amp A " << tcmdata.amplA << " amp C " << tcmdata.amplC + << " N A " << int(tcmdata.nChanA) << " N C " << int(tcmdata.nChanC) + << " trig " + << " ver " << tcmdata.vertex << " A " << tcmdata.orA << " C " << tcmdata.orC + << " size " << sizeof(tcmdata); + auto data = mRawEventData.to_vector(1); mLinkID = uint32_t(LinkTCM); mFeeID = uint64_t(LinkTCM); diff --git a/Detectors/FIT/FT0/workflow/CMakeLists.txt b/Detectors/FIT/FT0/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..69c116c041b14 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/CMakeLists.txt @@ -0,0 +1,58 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(FT0Workflow + SOURCES src/RecoWorkflow.cxx + src/ReconstructionSpec.cxx + src/RecPointWriterSpec.cxx + src/RecPointReaderSpec.cxx + src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx + src/DigitReaderSpec.cxx + src/FT0Workflow.cxx + src/FT0DataProcessDPLSpec.cxx + src/FT0DataReaderDPLSpec.cxx + src/FT0DigitWriterDPLSpec.cxx + src/RawReaderFT0.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsFT0 + O2::FT0Reconstruction + O2::DetectorsCommonDataFormats + O2::DPLUtils + O2::FT0Raw) + +o2_add_executable(reco-workflow + SOURCES src/ft0-reco-workflow.cxx + COMPONENT_NAME ft0 + PUBLIC_LINK_LIBRARIES O2::FT0Workflow + TARGETVARNAME fitrecoexe) + +o2_add_executable(entropy-encoder-workflow + SOURCES src/entropy-encoder-workflow.cxx + COMPONENT_NAME ft0 + PUBLIC_LINK_LIBRARIES O2::FT0Workflow) + +o2_add_executable(digits-reader-workflow + SOURCES src/digits-reader-workflow.cxx + COMPONENT_NAME ft0 + PUBLIC_LINK_LIBRARIES O2::FT0Workflow) + +o2_add_executable(flp-dpl-workflow + COMPONENT_NAME ft0 + SOURCES src/ft0-flp-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::FT0Workflow + TARGETVARNAME ft0flpexe) + +if(NOT APPLE) + + set_property(TARGET ${fitrecoexe} PROPERTY LINK_WHAT_YOU_USE ON) + +endif() + diff --git a/Detectors/FIT/FT0/workflow/README.md b/Detectors/FIT/FT0/workflow/README.md new file mode 100644 index 0000000000000..0bf3d1312ed2c --- /dev/null +++ b/Detectors/FIT/FT0/workflow/README.md @@ -0,0 +1,21 @@ +<!-- doxy +\page refFITFT0workflow FIT/FT0 FLP DPL workflow +/doxy --> + +# FLP DPL workflow, for reading raw data blocks from payload, and converting them into digits. + +To run with source file: + +o2-raw-file-reader-workflow -b --input-conf /home/fitdaq/work/data_raw_reader/run_raw_reader_v6.cfg|o2-ft0-flp-dpl-workflow -b + +If you want to dump digits in reader DPL: + +o2-raw-file-reader-workflow -b --input-conf /home/fitdaq/work/data_raw_reader/run_raw_reader_v6.cfg|o2-ft0-flp-dpl-workflow -b --dump-blocks-reader + +If you need to check data at "proccessor" DPL: + +o2-raw-file-reader-workflow -b --input-conf /home/fitdaq/work/data_raw_reader/run_raw_reader_v6.cfg|o2-ft0-flp-dpl-workflow -b --dump-blocks-process --use-process + +Special TCM extended mode (only for special technical runs): + +o2-raw-file-reader-workflow -b --input-conf /home/fitdaq/work/data_raw_reader/run_raw_reader_v6.cfg|o2-ft0-flp-dpl-workflow -b --tcm-extended-mode \ No newline at end of file diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/DigitReaderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..3b0c2f2cab1c8 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/DigitReaderSpec.h @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_FT0_DIGITREADERSPEC_H +#define O2_FT0_DIGITREADERSPEC_H + +#include "TFile.h" +#include "TTree.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" + +namespace o2 +{ +namespace ft0 +{ + +class DigitReader : public framework::Task +{ + public: + DigitReader(bool useMC) : mUseMC(useMC) {} + ~DigitReader() override = default; + void init(framework::InitContext& ic) final; + void run(framework::ProcessingContext& pc) final; + + private: + bool mUseMC = true; + std::unique_ptr<TTree> mTree; + std::unique_ptr<TFile> mFile; +}; + +/// create a processor spec +/// read simulated FT0 digits from a root file +framework::DataProcessorSpec getDigitReaderSpec(bool useMC); + +} // end namespace ft0 +} // end namespace o2 + +#endif // O2_FT0_DIGITREADERSPEC_H diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..58b25bbcebe54 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to FT0 digit/channels strean + +#ifndef O2_FT0_ENTROPYDECODER_SPEC +#define O2_FT0_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "FT0Reconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace ft0 +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::ft0::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace ft0 +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..5b42eb0f2cd14 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert clusters streams to CTF (EncodedBlocks) + +#ifndef O2_FT0_ENTROPYENCODER_SPEC +#define O2_FT0_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "FT0Reconstruction/CTFCoder.h" + +namespace o2 +{ +namespace ft0 +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::ft0::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace ft0 +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h new file mode 100644 index 0000000000000..0f79510037f09 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h @@ -0,0 +1,60 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <iostream> +#include <vector> +#include <gsl/span> + +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 new file mode 100644 index 0000000000000..3925995bab968 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h @@ -0,0 +1,81 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "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 <iostream> +#include <vector> +#include <gsl/span> +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ +template <typename RawReader> +class FT0DataReaderDPLSpec : public Task +{ + public: + FT0DataReaderDPLSpec(const RawReader& rawReader) : mRawReader(rawReader) {} + FT0DataReaderDPLSpec() = default; + ~FT0DataReaderDPLSpec() override = default; + void init(InitContext& ic) final {} + void run(ProcessingContext& pc) final + { + DPLRawParser parser(pc.inputs()); + mRawReader.clear(); + LOG(INFO) << "FT0DataReaderDPLSpec"; + uint64_t count = 0; + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + //Proccessing each page + count++; + auto rdhPtr = it.get_if<o2::header::RAWDataHeader>(); + gsl::span<const uint8_t> payload(it.data(), it.size()); + mRawReader.process(rdhPtr->linkID, payload); + } + LOG(INFO) << "Pages: " << count; + mRawReader.accumulateDigits(); + mRawReader.makeSnapshot(pc); + } + RawReader mRawReader; +}; + +template <typename RawReader> +framework::DataProcessorSpec getFT0DataReaderDPLSpec(const RawReader& rawReader) +{ + LOG(INFO) << "DataProcessorSpec initDataProcSpec() for RawReaderFT0"; + std::vector<OutputSpec> outputSpec; + RawReader::prepareOutputSpec(outputSpec); + return DataProcessorSpec{ + "ft0-datareader-dpl", + o2::framework::select("TF:FT0/RAWDATA"), + outputSpec, + adaptFromTask<FT0DataReaderDPLSpec<RawReader>>(rawReader), + Options{}}; +} + +} // namespace ft0 +} // namespace o2 + +#endif /* O2_FT0DATAREADERDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DigitWriterDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DigitWriterDPLSpec.h new file mode 100644 index 0000000000000..a5f985c0149b9 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DigitWriterDPLSpec.h @@ -0,0 +1,31 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FT0DigitWriterSpec.h + +#ifndef O2_FT0DIGITWRITERDPL_H +#define O2_FT0DIGITWRITERDPL_H + +#include "Framework/DataProcessorSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +/// create a processor spec +framework::DataProcessorSpec getFT0DigitWriterDPLSpec(); + +} // namespace ft0 +} // namespace o2 + +#endif /* O2_FT0DIGITWRITER_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DigitWriterSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DigitWriterSpec.h new file mode 100644 index 0000000000000..13f5534745990 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DigitWriterSpec.h @@ -0,0 +1,31 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FT0DigitWriterSpec.h + +#ifndef O2_FT0DIGITWRITER_H +#define O2_FT0DIGITWRITER_H + +#include "Framework/DataProcessorSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +/// create a processor spec +framework::DataProcessorSpec getFT0DigitWriterSpec(); + +} // namespace ft0 +} // namespace o2 + +#endif /* O2_FT0DIGITWRITER_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h new file mode 100644 index 0000000000000..f2cd562af2e02 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h @@ -0,0 +1,27 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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); +} // 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 new file mode 100644 index 0000000000000..fc65ff17b1f18 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h @@ -0,0 +1,121 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <iostream> +#include <vector> +#include <Rtypes.h> +#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 <gsl/span> + +namespace o2 +{ +namespace ft0 +{ +//Normal TCM mode +class RawReaderFT0 : public RawReaderFT0BaseNorm +{ + public: + RawReaderFT0(bool dumpData) : mDumpData(dumpData) {} + RawReaderFT0(const RawReaderFT0&) = default; + + RawReaderFT0() = default; + ~RawReaderFT0() = default; + void clear() + { + mVecDigits.clear(); + mVecChannelData.clear(); + } + void accumulateDigits() + { + getDigits(mVecDigits, mVecChannelData); + LOG(INFO) << "Number of Digits: " << mVecDigits.size(); + LOG(INFO) << "Number of ChannelData: " << mVecChannelData.size(); + if (mDumpData) { + DigitBlockFT0::print(mVecDigits, mVecChannelData); + } + } + static void prepareOutputSpec(std::vector<o2::framework::OutputSpec>& 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); + } + 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); + } + bool mDumpData; + std::vector<Digit> mVecDigits; + std::vector<ChannelData> mVecChannelData; +}; + +//Extended TCM mode (additional raw data struct) +class RawReaderFT0ext : public RawReaderFT0BaseExt +{ + public: + RawReaderFT0ext(bool dumpData) : mDumpData(dumpData) {} + RawReaderFT0ext(const RawReaderFT0ext&) = default; + + RawReaderFT0ext() = default; + ~RawReaderFT0ext() = default; + void clear() + { + mVecDigitsExt.clear(); + mVecChannelData.clear(); + mVecTrgExt.clear(); + } + void accumulateDigits() + { + getDigits(mVecDigitsExt, mVecChannelData, mVecTrgExt); + LOG(INFO) << "Number of Digits: " << mVecDigitsExt.size(); + LOG(INFO) << "Number of ChannelData: " << mVecChannelData.size(); + LOG(INFO) << "Number of TriggerExt: " << mVecTrgExt.size(); + if (mDumpData) { + DigitBlockFT0ext::print(mVecDigitsExt, mVecChannelData, mVecTrgExt); + } + } + static void prepareOutputSpec(std::vector<o2::framework::OutputSpec>& 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); + } + void makeSnapshot(o2::framework::ProcessingContext& pc) + { + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigitsExt); + 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); + } + bool mDumpData; + std::vector<DigitExt> mVecDigitsExt; + std::vector<ChannelData> mVecChannelData; + std::vector<TriggersExt> mVecTrgExt; +}; + +} // namespace ft0 +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecPointReaderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecPointReaderSpec.h new file mode 100644 index 0000000000000..c16b3234a0541 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecPointReaderSpec.h @@ -0,0 +1,62 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RecPointReaderSpec.h + +#ifndef O2_FT0_RECPOINTREADER +#define O2_FT0_RECPOINTREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/RootSerializationSupport.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DataFormatsFT0/RecPoints.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +class RecPointReader : public Task +{ + public: + RecPointReader(bool useMC = true); + ~RecPointReader() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + + private: + void connectTree(const std::string& filename); + + std::unique_ptr<TFile> mFile; + std::unique_ptr<TTree> mTree; + + bool mUseMC = true; // use MC truth + o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; + + std::vector<o2::ft0::RecPoints>* mRecPoints = nullptr; + + std::string mInputFileName = "o2reco_ft0.root"; + std::string mRecPointTreeName = "o2sim"; + std::string mRecPointBranchName = "FT0Cluster"; +}; + +/// create a processor spec +/// read simulated ITS digits from a root file +framework::DataProcessorSpec getRecPointReaderSpec(bool useMC); + +} // namespace ft0 +} // namespace o2 + +#endif /* O2_FT0_RECPOINTREADER */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecPointWriterSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecPointWriterSpec.h new file mode 100644 index 0000000000000..a4a4609cb2045 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecPointWriterSpec.h @@ -0,0 +1,31 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RecPointWriterSpec.h + +#ifndef O2_FT0RECPOINTWRITER_H +#define O2_FT0RECPOINTWRITER_H + +#include "Framework/DataProcessorSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +/// create a processor spec +framework::DataProcessorSpec getRecPointWriterSpec(bool useMC); + +} // namespace ft0 +} // namespace o2 + +#endif /* O2_FT0RECPOINTWRITER_H */ diff --git a/Detectors/FIT/workflow/include/FITWorkflow/RecoWorkflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h similarity index 100% rename from Detectors/FIT/workflow/include/FITWorkflow/RecoWorkflow.h rename to Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h new file mode 100644 index 0000000000000..fd4af7581eae3 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ReconstructionSpec.h + +#ifndef O2_FT0RECONSTRUCTORDPL_H +#define O2_FT0RECONSTRUCTORDPL_H + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "FT0Reconstruction/CollisionTimeRecoTask.h" +#include "DataFormatsFT0/RecPoints.h" +#include "TStopwatch.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +class ReconstructionDPL : public Task +{ + public: + ReconstructionDPL(bool useMC) : mUseMC(useMC) {} + ~ReconstructionDPL() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; + + private: + bool mUseMC = true; + std::vector<o2::ft0::RecPoints> mRecPoints; + std::vector<o2::ft0::ChannelDataFloat> mRecChData; + o2::ft0::CollisionTimeRecoTask mReco; + o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getReconstructionSpec(bool useMC = true); + +} // namespace ft0 +} // namespace o2 + +#endif /* O2_FT0RECONSTRUCTORDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..ab683709b7269 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,92 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> + +#include "TTree.h" + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "FT0Workflow/DigitReaderSpec.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/ChannelData.h" +#include "DataFormatsFT0/MCLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +void DigitReader::init(InitContext& ic) +{ + auto filename = ic.options().get<std::string>("ft0-digit-infile"); + mFile = std::make_unique<TFile>(filename.c_str(), "OLD"); + if (!mFile->IsOpen()) { + LOG(ERROR) << "Cannot open the " << filename.c_str() << " file !"; + throw std::runtime_error("cannot open input digits file"); + } + mTree.reset((TTree*)mFile->Get("o2sim")); + if (!mTree) { + LOG(ERROR) << "Did not find o2sim tree in " << filename.c_str(); + throw std::runtime_error("Did not fine o2sim file in FT0 digits tree"); + } +} + +void DigitReader::run(ProcessingContext& pc) +{ + + std::vector<o2::ft0::Digit> digits, *pdigits = &digits; + std::vector<o2::ft0::ChannelData> channels, *pchannels = &channels; + mTree->SetBranchAddress("FT0DIGITSBC", &pdigits); + mTree->SetBranchAddress("FT0DIGITSCH", &pchannels); + + o2::dataformats::MCTruthContainer<o2::ft0::MCLabel> labels, *plabels = &labels; + if (mUseMC) { + mTree->SetBranchAddress("FT0DIGITSMCTR", &plabels); + } + mTree->GetEntry(0); + + LOG(INFO) << "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); + if (mUseMC) { + pc.outputs().snapshot(Output{"FT0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + } + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); +} + +DataProcessorSpec getDigitReaderSpec(bool useMC) +{ + std::vector<OutputSpec> outputs; + outputs.emplace_back("FT0", "DIGITSBC", 0, Lifetime::Timeframe); + outputs.emplace_back("FT0", "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("FT0", "DIGITSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "ft0-digit-reader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<DigitReader>(useMC)}, + Options{ + {"ft0-digit-infile", VariantType::String, "ft0digits.root", {"Name of the input file"}}}}; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..d69e174deb922 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "FT0Workflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("ft0-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& digits = pc.outputs().make<std::vector<o2::ft0::Digit>>(OutputRef{"digits"}); + auto& channels = pc.outputs().make<std::vector<o2::ft0::ChannelData>>(OutputRef{"channels"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::ft0::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, digits, channels); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << channels.size() << " FT0 channels in " << digits.size() << " digits in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FT0 Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"digits"}, "FT0", "DIGITSBC", 0, Lifetime::Timeframe}, + OutputSpec{{"channels"}, "FT0", "DIGITSCH", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "ft0-entropy-decoder", + Inputs{InputSpec{"ctf", "FT0", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"ft0-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..93e1d013ba95d --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "FT0Workflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("ft0-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto digits = pc.inputs().get<gsl::span<o2::ft0::Digit>>("digits"); + auto channels = pc.inputs().get<gsl::span<o2::ft0::ChannelData>>("channels"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"FT0", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, digits, channels); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + // eeb->print(); + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for FT0 in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FT0 Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("digits", "FT0", "DIGITSBC", 0, Lifetime::Timeframe); + inputs.emplace_back("channels", "FT0", "DIGITSCH", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "ft0-entropy-encoder", + inputs, + Outputs{{"FT0", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"ft0-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx new file mode 100644 index 0000000000000..9ad55e06a8c56 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx @@ -0,0 +1,51 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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<std::vector<Digit>>("digits"); + auto vecChannelData = pc.inputs().get<std::vector<ChannelData>>("digch"); + if (mDumpEventBlocks) { + DigitBlockFT0::print(vecDigits, vecChannelData); + } +} + +DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor) +{ + std::vector<InputSpec> 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<FT0DataProcessDPLSpec>(dumpProcessor)}, + Options{}}; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx new file mode 100644 index 0000000000000..d8451ba6ccb1e --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/FT0DigitWriterDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DigitWriterDPLSpec.cxx new file mode 100644 index 0000000000000..052f0f62a69f7 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/FT0DigitWriterDPLSpec.cxx @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FT0DigitWriterSpec.cxx + +#include <vector> + +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsFT0/ChannelData.h" +#include "DataFormatsFT0/Digit.h" +#include "FT0Workflow/FT0DigitWriterDPLSpec.h" +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +template <typename T> +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; +DataProcessorSpec getFT0DigitWriterDPLSpec() +{ + using DigitType = std::vector<o2::ft0::Digit>; + using ChanDataType = std::vector<o2::ft0::ChannelData>; + // Spectators for logging + auto logger = [](DigitType const& digits) { + LOG(INFO) << "FT0DigitWriter pulled " << digits.size() << " digits"; + }; + return MakeRootTreeWriterSpec( + "ft0-digit-writer", "o2digit_ft0.root", "o2sim", + BranchDefinition<DigitType>{InputSpec{"digits", "FT0", "DIGITSBC", 0}, + "FT0DIGITSBC", "ft0-digits-branch-name", 1, + logger}, + BranchDefinition<ChanDataType>{InputSpec{"digch", "FT0", "DIGITSCH", 0}, + "FT0DIGITSCH", "ft0-chhdata-branch-name"})(); +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DigitWriterSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DigitWriterSpec.cxx new file mode 100644 index 0000000000000..92b613f8eebf1 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/FT0DigitWriterSpec.cxx @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FT0DigitWriterSpec.cxx + +#include <vector> + +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsFT0/ChannelData.h" +#include "DataFormatsFT0/Digit.h" +#include "FT0Workflow/FT0DigitWriterSpec.h" +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +template <typename T> +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; +DataProcessorSpec getFT0DigitWriterSpec() +{ + using DigitType = std::vector<o2::ft0::Digit>; + using ChanDataType = std::vector<o2::ft0::ChannelData>; + // Spectators for logging + auto logger = [](DigitType const& digits) { + LOG(INFO) << "FT0DigitWriter pulled " << digits.size() << " digits"; + }; + return MakeRootTreeWriterSpec( + "ft0-digit-writer", "o2digit_ft0.root", "o2sim", + BranchDefinition<DigitType>{InputSpec{"digits", "FT0", "DIGITSBC", 0}, + "FT0DIGITSBC", "ft0-digits-branch-name", 1, + logger}, + BranchDefinition<ChanDataType>{InputSpec{"digch", "FT0", "DIGITSCH", 0}, + "FT0DIGITSCH", "ft0-chhdata-branch-name"})(); +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx new file mode 100644 index 0000000000000..db4187ee49169 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx @@ -0,0 +1,44 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/FT0DigitWriterDPLSpec.h" +#include "FT0Workflow/RawReaderFT0.h" +namespace o2 +{ +namespace ft0 +{ + +framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, + bool dumpProcessor, bool dumpReader, + bool disableRootOut) +{ + LOG(INFO) << "framework::WorkflowSpec getFT0Workflow"; + framework::WorkflowSpec specs; + if (isExtendedMode) { + specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0ext{dumpReader})); + } else { + specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0{dumpReader})); + } + if (useProcess) { + specs.emplace_back(o2::ft0::getFT0DataProcessDPLSpec(dumpProcessor)); + } + if (!disableRootOut) { + specs.emplace_back(o2::ft0::getFT0DigitWriterDPLSpec()); + } + return specs; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx new file mode 100644 index 0000000000000..3de0026f13b55 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 new file mode 100644 index 0000000000000..ef9c03c8b6618 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx @@ -0,0 +1,94 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RecPointReaderSpec.cxx + +#include <vector> + +#include "TTree.h" + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "FT0Workflow/RecPointReaderSpec.h" + +using namespace o2::framework; +using namespace o2::ft0; + +namespace o2 +{ +namespace ft0 +{ + +RecPointReader::RecPointReader(bool useMC) +{ + mUseMC = useMC; + if (useMC) { + LOG(WARNING) << "FT0 RecPoint reader at the moment does not process MC"; + } +} + +void RecPointReader::init(InitContext& ic) +{ + mInputFileName = ic.options().get<std::string>("ft0-recpoints-infile"); + connectTree(mInputFileName); +} + +void RecPointReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + + LOG(INFO) << "FT0 RecPointReader pushes " << mRecPoints->size() << " recpoints at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } +} + +void RecPointReader::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(mRecPointTreeName.c_str())); + assert(mTree); + + mTree->SetBranchAddress(mRecPointBranchName.c_str(), &mRecPoints); + if (mUseMC) { + LOG(WARNING) << "MC-truth is not supported for FT0 recpoints currently"; + mUseMC = false; + } + + LOG(INFO) << "Loaded FT0 RecPoints tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getRecPointReaderSpec(bool useMC) +{ + std::vector<OutputSpec> outputSpec; + outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); + if (useMC) { + LOG(WARNING) << "MC-truth is not supported for FT0 recpoints currently"; + } + + return DataProcessorSpec{ + "ft0-recpoints-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask<RecPointReader>()}, + Options{ + {"ft0-recpoints-infile", VariantType::String, "o2reco_ft0.root", {"Name of the input file"}}}}; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RecPointWriterSpec.cxx b/Detectors/FIT/FT0/workflow/src/RecPointWriterSpec.cxx new file mode 100644 index 0000000000000..cfae8f06c1b44 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/RecPointWriterSpec.cxx @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RecPointWriterSpec.cxx + +#include <vector> + +#include "FT0Workflow/RecPointWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsFT0/RecPoints.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +template <typename T> +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; +DataProcessorSpec getRecPointWriterSpec(bool useMC) +{ + using RecPointsType = std::vector<o2::ft0::RecPoints>; + using ChanDataType = std::vector<o2::ft0::ChannelDataFloat>; + // Spectators for logging + auto logger = [](RecPointsType const& recPoints) { + LOG(INFO) << "FT0RecPointWriter pulled " << recPoints.size() << " RecPoints"; + }; + return MakeRootTreeWriterSpec("ft0-recpoint-writer", + "o2reco_ft0.root", + "o2sim", + BranchDefinition<RecPointsType>{InputSpec{"recPoints", "FT0", "RECPOINTS", 0}, + "FT0Cluster", + "ft0-recpoint-branch-name", + 1, + logger}, + BranchDefinition<ChanDataType>{InputSpec{"recChData", "FT0", "RECCHDATA", 0}, + "FT0RecChData", + "ft0-rechhdata-branch-name"})(); +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx new file mode 100644 index 0000000000000..af33a654e941e --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RecoWorkflow.cxx + +#include "FT0Workflow/RecoWorkflow.h" + +#include "FT0Workflow/DigitReaderSpec.h" +#include "FT0Workflow/RecPointWriterSpec.h" +#include "FT0Workflow/ReconstructionSpec.h" +#include "FT0Workflow/FT0DataReaderDPLSpec.h" + +namespace o2 +{ +namespace fit +{ + +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +{ + framework::WorkflowSpec specs; + if (!disableRootInp) { + specs.emplace_back(o2::ft0::getDigitReaderSpec(useMC)); + } + + specs.emplace_back(o2::ft0::getReconstructionSpec(useMC)); + if (!disableRootOut) { + specs.emplace_back(o2::ft0::getRecPointWriterSpec(useMC)); + } + return specs; +} + +} // namespace fit +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx new file mode 100644 index 0000000000000..b659938e87585 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx @@ -0,0 +1,100 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ReconstructionSpec.cxx + +#include <vector> +#include "SimulationDataFormat/MCTruthContainer.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "FT0Workflow/ReconstructionSpec.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/ChannelData.h" +#include "DataFormatsFT0/MCLabel.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace ft0 +{ + +void ReconstructionDPL::init(InitContext& ic) +{ + mTimer.Stop(); + mTimer.Reset(); + LOG(INFO) << "ReconstructionDPL::init"; +} + +void ReconstructionDPL::run(ProcessingContext& pc) +{ + mTimer.Start(false); + mRecPoints.clear(); + auto digits = pc.inputs().get<gsl::span<o2::ft0::Digit>>("digits"); + //auto digits = pc.inputs().get<const std::vector<o2::ft0::Digit>>("digits"); + auto digch = pc.inputs().get<gsl::span<o2::ft0::ChannelData>>("digch"); + // RS: if we need to process MC truth, uncomment lines below + //std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>> labels; + //const o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>* lblPtr = nullptr; + if (mUseMC) { + // labels = pc.inputs().get<const o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>*>("labels"); + // lblPtr = labels.get(); + LOG(INFO) << "Ignoring MC info"; + } + int nDig = digits.size(); + LOG(DEBUG) << " nDig " << nDig; + 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<o2::ft0::ChannelDataFloat> out_ch(mRecChData); + out_ch = out_ch.subspan(digit.ref.getFirstEntry(), digit.ref.getEntries()); + mRecPoints.emplace_back(mReco.process(digit, channels, out_ch)); + } + // 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); + + mTimer.Stop(); +} + +void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FT0 reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getReconstructionSpec(bool useMC) +{ + std::vector<InputSpec> inputSpec; + std::vector<OutputSpec> 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); + } + outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); + outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECCHDATA", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "ft0-reconstructor", + inputSpec, + outputSpec, + AlgorithmSpec{adaptFromTask<ReconstructionDPL>(useMC)}, + Options{}}; +} + +} // namespace ft0 +} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/digits-reader-workflow.cxx b/Detectors/FIT/FT0/workflow/src/digits-reader-workflow.cxx new file mode 100644 index 0000000000000..09e2f407e512b --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/digits-reader-workflow.cxx @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 digits-reader-workflow.cxx +/// \brief Implementation of FT0 digits reader +/// +/// \author ruben.shahoyan@cern.ch + +#include "Framework/CallbackService.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "FT0Workflow/DigitReaderSpec.h" +#include "CommonUtils/ConfigurableParam.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> 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::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + WorkflowSpec specs; + + DataProcessorSpec producer = o2::ft0::getDigitReaderSpec(ctx.options().get<bool>("disable-mc")); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..e4a094904a820 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::ft0::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/FIT/FT0/workflow/src/ft0-flp-workflow.cxx b/Detectors/FIT/FT0/workflow/src/ft0-flp-workflow.cxx new file mode 100644 index 0000000000000..f452eee06a53f --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/ft0-flp-workflow.cxx @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FT0Workflow/FT0Workflow.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.push_back( + ConfigParamSpec{"tcm-extended-mode", + o2::framework::VariantType::Bool, + false, + {"in case of extended TCM mode (1 header + 1 TCMdata + 8 " + "TCMdataExtended)"}}); + + workflowOptions.push_back( + ConfigParamSpec{"use-process", + o2::framework::VariantType::Bool, + false, + {"enable processor for data taking/dumping"}}); + workflowOptions.push_back( + ConfigParamSpec{"dump-blocks-process", + o2::framework::VariantType::Bool, + false, + {"enable dumping of event blocks at processor side"}}); + workflowOptions.push_back( + ConfigParamSpec{"dump-blocks-reader", + o2::framework::VariantType::Bool, + false, + {"enable dumping of event blocks at reader side"}}); + workflowOptions.push_back( + ConfigParamSpec{"disable-root-output", + o2::framework::VariantType::Bool, + false, + {"disable root-files output writers"}}); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + LOG(INFO) << "WorkflowSpec defineDataProcessing"; + auto useProcessor = configcontext.options().get<bool>("use-process"); + auto dumpProcessor = configcontext.options().get<bool>("dump-blocks-process"); + auto dumpReader = configcontext.options().get<bool>("dump-blocks-reader"); + auto isExtendedMode = configcontext.options().get<bool>("tcm-extended-mode"); + auto disableRootOut = + configcontext.options().get<bool>("disable-root-output"); + LOG(INFO) << "WorkflowSpec FLPWorkflow"; + return std::move(o2::ft0::getFT0Workflow( + isExtendedMode, useProcessor, dumpProcessor, dumpReader, disableRootOut)); +} diff --git a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx new file mode 100644 index 0000000000000..9cfe9732aaa21 --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/RecoWorkflow.h" +#include "CommonUtils/ConfigurableParam.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.push_back(ConfigParamSpec{ + "disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}); + workflowOptions.push_back(ConfigParamSpec{ + "disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}); + workflowOptions.push_back(ConfigParamSpec{ + "disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}); + std::string keyvaluehelp("Semicolon separated key=value strings ..."); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + LOG(INFO) << "WorkflowSpec defineDataProcessing"; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get<std::string>("configKeyValues")); + // write the configuration used for the digitizer workflow + o2::conf::ConfigurableParam::writeINI("o2tpcits-match-recoflow_configuration.ini"); + + auto useMC = !configcontext.options().get<bool>("disable-mc"); + auto disableRootInp = configcontext.options().get<bool>("disable-root-input"); + auto disableRootOut = configcontext.options().get<bool>("disable-root-output"); + + LOG(INFO) << "WorkflowSpec getRecoWorkflow useMC " << useMC; + return std::move(o2::fit::getRecoWorkflow(useMC, disableRootInp, disableRootOut)); +} diff --git a/Detectors/FIT/FV0/CMakeLists.txt b/Detectors/FIT/FV0/CMakeLists.txt index 364119694abdb..e4164be8f405c 100644 --- a/Detectors/FIT/FV0/CMakeLists.txt +++ b/Detectors/FIT/FV0/CMakeLists.txt @@ -10,4 +10,6 @@ add_subdirectory(base) add_subdirectory(simulation) +add_subdirectory(reconstruction) +add_subdirectory(workflow) add_subdirectory(macro) diff --git a/Detectors/FIT/FV0/base/CMakeLists.txt b/Detectors/FIT/FV0/base/CMakeLists.txt index d96cb91315d66..804c004157da1 100644 --- a/Detectors/FIT/FV0/base/CMakeLists.txt +++ b/Detectors/FIT/FV0/base/CMakeLists.txt @@ -11,8 +11,9 @@ o2_add_library(FV0Base SOURCES src/Geometry.cxx PUBLIC_LINK_LIBRARIES ROOT::Geom - FairRoot::Base - O2::SimulationDataFormat) + FairRoot::Base) -o2_target_root_dictionary(FV0Base HEADERS include/FV0Base/Geometry.h) +o2_target_root_dictionary(FV0Base + HEADERS include/FV0Base/Geometry.h + include/FV0Base/Constants.h) diff --git a/Detectors/FIT/FV0/base/include/FV0Base/Constants.h b/Detectors/FIT/FV0/base/include/FV0Base/Constants.h new file mode 100644 index 0000000000000..965110873e21c --- /dev/null +++ b/Detectors/FIT/FV0/base/include/FV0Base/Constants.h @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Constants.h +/// \brief General constants in FV0 +/// +/// \author Maciej Slupecki, University of Jyvaskyla, Finland + +#ifndef ALICEO2_FV0_CONSTANTS_ +#define ALICEO2_FV0_CONSTANTS_ + +#include "FV0Base/Geometry.h" + +namespace o2 +{ +namespace fv0 +{ + +struct Constants { + static constexpr int nChannelsPerPm = 12; // Fixed now together with the production of PMs - will remain constant + static constexpr int nPms = 4; // Number of processing modules (PMs); 1 PM per ring, 2 PMs needed for ring 5 + static constexpr int nTcms = 1; // Number of trigger and clock modules (TCMs) + static constexpr int nGbtLinks = nPms + nTcms; + static constexpr int nFv0Channels = Geometry::getNumberOfReadoutChannels(); +}; + +} // namespace fv0 +} // namespace o2 +#endif diff --git a/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h b/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h index 97e9d02cbdbc6..c659c0c47c621 100644 --- a/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h +++ b/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h @@ -23,12 +23,24 @@ #include <TGeoMatrix.h> #include <TGeoVolume.h> #include <TVirtualMC.h> -#include "MathUtils/Cartesian3D.h" namespace o2 { namespace fv0 { + +struct Point3Dsimple { + float x; + float y; + float z; + void SetCoordinates(float x, float y, float z) + { + this->x = x; + this->y = y; + this->z = z; + }; +}; + /// FV0 Geometry class Geometry { @@ -98,8 +110,9 @@ class Geometry /// \param y y [cm]. /// \param z z [cm]. void getGlobalPosition(float& x, float& y, float& z); - Point3D<float>& getCellCenter(UInt_t cellId); - Point3D<float>& getReadoutCenter(UInt_t cellId); + Point3Dsimple& getCellCenter(UInt_t cellId); + Point3Dsimple& getReadoutCenter(UInt_t cellId); + static constexpr int getNumberOfReadoutChannels() { return sNumberOfReadoutChannels; }; /// Helper function to check if the cellId belongs to ring 5. /// \param cellId Id of the cell in range from 0 to 39. @@ -459,8 +472,8 @@ class Geometry TGeoMatrix* mRightTransformation; ///< Transformation for the right part of the detector /// Utility arrays derived from constants - std::array<Point3D<float>, sNumberOfCells> mCellCenter; ///< Center of each scintillator cell - std::array<Point3D<float>, sNumberOfReadoutChannels> mReadoutCenter; ///< Similar to mCellCenter, cells in r5 are additionally divided + std::array<Point3Dsimple, sNumberOfCells> mCellCenter; ///< Center of each scintillator cell + std::array<Point3Dsimple, sNumberOfReadoutChannels> mReadoutCenter; ///< Similar to mCellCenter, cells in r5 are additionally divided static Geometry* sInstance; ///< \brief Singleton instance diff --git a/Detectors/FIT/FV0/base/src/Geometry.cxx b/Detectors/FIT/FV0/base/src/Geometry.cxx index d6cc07f42982e..b40df946084af 100644 --- a/Detectors/FIT/FV0/base/src/Geometry.cxx +++ b/Detectors/FIT/FV0/base/src/Geometry.cxx @@ -116,12 +116,12 @@ void Geometry::getGlobalPosition(float& x, float& y, float& z) z = sZGlobal; } -Point3D<float>& Geometry::getCellCenter(UInt_t cellId) +Point3Dsimple& Geometry::getCellCenter(UInt_t cellId) { return mCellCenter.at(cellId); } -Point3D<float>& Geometry::getReadoutCenter(UInt_t cellId) +Point3Dsimple& Geometry::getReadoutCenter(UInt_t cellId) { return mReadoutCenter.at(cellId); } @@ -1222,7 +1222,7 @@ void Geometry::initializeCellCenters() double x = sXGlobal + r * TMath::Cos(lutSect2Phi[sCellToSector[cellId]]); double y = sYGlobal + r * TMath::Sin(lutSect2Phi[sCellToSector[cellId]]); - Point3D<float>* p = &mCellCenter.at(cellId); + Point3Dsimple* p = &mCellCenter.at(cellId); p->SetCoordinates(x, y, sZGlobal); } } @@ -1230,9 +1230,9 @@ void Geometry::initializeCellCenters() void Geometry::initializeReadoutCenters() { for (int channelId = 0; channelId < sNumberOfReadoutChannels; channelId++) { - Point3D<float>* p = &mReadoutCenter.at(channelId); + Point3Dsimple* p = &mReadoutCenter.at(channelId); if (!isRing5(channelId)) { - p->SetCoordinates(getCellCenter(channelId).X(), getCellCenter(channelId).Y(), getCellCenter(channelId).Z()); + p->SetCoordinates(getCellCenter(channelId).x, getCellCenter(channelId).y, getCellCenter(channelId).z); } else { const int numberOfSectorsR5 = sNumberOfCellSectors * 4; // from both halves of the detector const float phi0 = 78.75 * TMath::DegToRad(); // starting phi of one of the sectors diff --git a/Detectors/FIT/FV0/macro/CMakeLists.txt b/Detectors/FIT/FV0/macro/CMakeLists.txt index 1510362c7fb90..d7fadb4a3683c 100644 --- a/Detectors/FIT/FV0/macro/CMakeLists.txt +++ b/Detectors/FIT/FV0/macro/CMakeLists.txt @@ -11,6 +11,7 @@ o2_add_test_root_macro(readFV0Hits.C PUBLIC_LINK_LIBRARIES O2::FV0Simulation LABELS fv0) + o2_add_test_root_macro(readFV0Digits.C PUBLIC_LINK_LIBRARIES O2::FV0Simulation LABELS fv0) diff --git a/Detectors/FIT/FV0/macro/readFV0Digits.C b/Detectors/FIT/FV0/macro/readFV0Digits.C index 80c2914e3d5fa..02fd810e93d5d 100644 --- a/Detectors/FIT/FV0/macro/readFV0Digits.C +++ b/Detectors/FIT/FV0/macro/readFV0Digits.C @@ -21,8 +21,8 @@ #include "DataFormatsFV0/ChannelData.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "FV0Simulation/DigitizationConstant.h" -#include "FV0Simulation/MCLabel.h" +#include "FV0Base/Constants.h" +#include "DataFormatsFV0/MCLabel.h" #include "FairLogger.h" void AdjustStatBox(TH1* h, float x1ndc, float x2ndc, float y1ndc, float y2ndc) @@ -53,15 +53,15 @@ void readFV0Digits(std::string digiFName = "fv0digits.root", bool printAllToTerm { gStyle->SetOptStat("noumri"); // Settings for drawing, used with SetRangeUser() - const float tMin = 0, tMax = 50, chargeMin = 5, chargeMax = 800; + const float tMin = -30, tMax = 70, chargeMin = 0, chargeMax = 800; const float chargePerMip = 1. / 16; // Assumes 1-MIP-peak is at 16th ADC channel - const int nChannels = o2::fv0::DigitizationConstant::NCHANNELS; + const int nChannels = o2::fv0::Constants::nFv0Channels; // Init histos std::vector<std::string> vHistoNames; InitHistoNames(vHistoNames); - TH2F* hTimeCharge = new TH2F(vHistoNames.at(0).c_str(), "", 2000, 0, 200, 4096, 0, 4096); - TH2F* hTimeCh = new TH2F(vHistoNames.at(1).c_str(), "", 2000, 0, 200, nChannels, 0, nChannels); + TH2F* hTimeCharge = new TH2F(vHistoNames.at(0).c_str(), "", 400, -200, 200, 4096, 0, 4096); + TH2F* hTimeCh = new TH2F(vHistoNames.at(1).c_str(), "", 400, -200, 200, nChannels, 0, nChannels); TH2F* hChargeCh = new TH2F(vHistoNames.at(2).c_str(), "", 4096, 0, 4096, nChannels, 0, nChannels); TH2F* hMipsCh = new TH2F(vHistoNames.at(3).c_str(), "", 256, 0, 256, nChannels, 0, nChannels); TH1F* hNchReal = new TH1F(vHistoNames.at(4).c_str(), "", 600, 0, 600); @@ -118,19 +118,18 @@ void readFV0Digits(std::string digiFName = "fv0digits.root", bool printAllToTerm // Fill histos for (int ibc = 0; ibc < nbc; ibc++) { - if (ibc % (nbc / 10) == 0) { + if ((nbc > 100) && (ibc % (nbc / 100) == 0)) { std::cout << " Progress reading tree: " << ibc << "/" << nbc << " ["; std::cout << 100.0 * ibc / nbc << "%]" << std::endl; } const auto& bcd = fv0BCData[ibc]; + std::cout << ibc << " " << nbc << " - " << bcd.ir << std::endl; int chEnt = bcd.ref.getFirstEntry(); int nchPrec = 0; int nchReal = 0; for (int ic = 0; ic < bcd.ref.getEntries(); ic++) { const auto& chd = fv0ChData[chEnt++]; - if ((chd.time == -1024.f) && (chd.chargeAdc == 0.f)) { - continue; - } + std::cout << chd.pmtNumber << " " << chd.chargeAdc << " " << chd.time << std::endl; hTimeCharge->Fill(chd.time, chd.chargeAdc); hTimeCh->Fill(chd.time, chd.pmtNumber); hChargeCh->Fill(chd.chargeAdc, chd.pmtNumber); @@ -253,6 +252,7 @@ void readFV0Digits(std::string digiFName = "fv0digits.root", bool printAllToTerm hNchReal->Write(); hNchPrec->Write(); fout->Close(); + std::cout << "Digits read" << std::endl; } // Root files generated in the previous stage (readFV0Digits()) are used as inputs here @@ -302,13 +302,11 @@ int compareFV0Digits(std::string digiFName1 = "fv0digi-rawhistos.root", std::str } std::cout << " <I> Read: " << vh.size() << " histos" << std::endl; - // TH1D* ht = hTimeCharge->ProjectionX("hTime_prX"); - // TH1D* hc = hTimeCharge->ProjectionY("hCharge_prY"); const float rmargin = 0.12, lmargin = 0.13, tmargin = 0.02, bmargin = 0.15; const float statX1 = 1. - rmargin, statX2 = statX1 - 0.18; const float statH = 0.3, statY1 = 1. - tmargin, statY2 = statY1 - statH; - // Draw side-by-side comparison of TH2's + /* // Draw side-by-side comparison of TH2's for (UInt_t ih = 0; ih < 4; ih++) { std::stringstream ss; ss << "fv0digi-cmp" << ih; @@ -323,11 +321,11 @@ int compareFV0Digits(std::string digiFName1 = "fv0digi-rawhistos.root", std::str AdjustStatBox(h, statX1, statX2, statY1, statY2); } } - +*/ // Draw the comparison of TH1's Color_t col[3] = {kBlack, kRed, kBlue}; TCanvas* c = new TCanvas("fv0digi-cmp-th1", "fv0digi-cmp-th1", 1800, 500); - c->Divide(3, 1); + c->Divide(2, 1); for (UInt_t ifile = 0; ifile < nFiles; ifile++) { TH2F* h2 = (TH2F*)vh.at(ifile * nHistos); h2->SetLineColor(col[ifile]); @@ -336,27 +334,20 @@ int compareFV0Digits(std::string digiFName1 = "fv0digi-rawhistos.root", std::str ss << "p" << ifile; TH1D* ht = h2->ProjectionX((ss.str() + "t_" + h2->GetName()).c_str()); TH1D* hc = h2->ProjectionY((ss.str() + "c_" + h2->GetName()).c_str()); + hc->GetXaxis()->SetRangeUser(0, 100); c->cd(1); gPad->SetMargin(lmargin, rmargin, bmargin, tmargin); gPad->SetLogy(); + ht->SetLineWidth(3.5 - ifile); ht->Draw((ifile == 0) ? "" : "sames"); AdjustStatBox(ht, statX1, statX2, statY1 - statH * ifile, statY2 - statH * ifile); c->cd(2); gPad->SetMargin(lmargin, rmargin, bmargin, tmargin); gPad->SetLogy(); + hc->SetLineWidth(3.5 - ifile); hc->Draw((ifile == 0) ? "" : "sames"); AdjustStatBox(hc, statX1, statX2, statY1 - statH * ifile, statY2 - statH * ifile); } - c->cd(3); - gPad->SetMargin(lmargin, rmargin, bmargin, tmargin); - gPad->SetLogy(); - for (UInt_t ifile = 0; ifile < nFiles; ifile++) { - TH1* h = vh.at(ifile * nHistos + 4); - h->SetLineColor(col[ifile]); - h->SetLineWidth(2); - h->Draw((ifile == 0) ? "" : "sames"); - AdjustStatBox(h, statX1, statX2, statY1 - statH * ifile, statY2 - statH * ifile); - } return 0; } diff --git a/Detectors/FIT/FV0/reconstruction/CMakeLists.txt b/Detectors/FIT/FV0/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..7e588e3928b7b --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(FV0Reconstruction + SOURCES src/ReadRaw.cxx + src/CTFCoder.cxx + PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat + O2::Framework + O2::FV0Base + O2::DataFormatsFV0 + O2::DetectorsRaw + O2::CommonDataFormat + O2::rANS + O2::Headers) + +o2_target_root_dictionary(FV0Reconstruction + HEADERS include/FV0Reconstruction/ReadRaw.h + include/FV0Reconstruction/CTFCoder.h) + diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h new file mode 100644 index 0000000000000..ae14872cc13d2 --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -0,0 +1,165 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of FV0 digits data + +#ifndef O2_FV0_CTFCODER_H +#define O2_FV0_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include "FV0Base/Geometry.h" +#include "DataFormatsFV0/CTF.h" +#include "DataFormatsFV0/BCData.h" +#include "DataFormatsFV0/ChannelData.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" + +class TTree; + +namespace o2 +{ +namespace fv0 +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::FV0) {} + ~CTFCoder() = default; + + /// entropy-encode digits to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const BCData>& digitVec, const gsl::span<const ChannelData>& channelVec); + + /// entropy decode clusters from buffer with CTF + template <typename VDIG, typename VCHAN> + void decode(const CTF::base& ec, VDIG& digitVec, VCHAN& channelVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + /// compres digits clusters to CompressedDigits + void compress(CompressedDigits& cd, const gsl::span<const BCData>& digitVec, const gsl::span<const ChannelData>& channelVec); + size_t estimateCompressedSize(const CompressedDigits& cc); + + /// decompress CompressedDigits to digits + template <typename VDIG, typename VCHAN> + void decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& channelVec); + + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<BCData>& digitVec, std::vector<ChannelData>& channelVec); + + ClassDefNV(CTFCoder, 1); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const BCData>& digitVec, const gsl::span<const ChannelData>& channelVec) +{ + 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_time + MD::EENCODE // BLC_charge + }; + CompressedDigits cd; + compress(cd, digitVec, channelVec); + + // book output size with some margin + auto szIni = estimateCompressedSize(cd); + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(cd.header); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODEFV0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODEFV0(cd.bcInc, CTF::BLC_bcInc, 0); + ENCODEFV0(cd.orbitInc, CTF::BLC_orbitInc, 0); + ENCODEFV0(cd.nChan, CTF::BLC_nChan, 0); + + ENCODEFV0(cd.idChan , CTF::BLC_idChan, 0); + ENCODEFV0(cd.time, CTF::BLC_time, 0); + ENCODEFV0(cd.charge, CTF::BLC_charge, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VDIG, typename VCHAN> +void CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& channelVec) +{ + CompressedDigits cd; + cd.header = ec.getHeader(); + ec.print(getPrefix()); +#define DECODEFV0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODEFV0(cd.bcInc, CTF::BLC_bcInc); + DECODEFV0(cd.orbitInc, CTF::BLC_orbitInc); + DECODEFV0(cd.nChan, CTF::BLC_nChan); + + DECODEFV0(cd.idChan, CTF::BLC_idChan); + DECODEFV0(cd.time, CTF::BLC_time); + DECODEFV0(cd.charge, CTF::BLC_charge); + // clang-format on + // + decompress(cd, digitVec, channelVec); +} + +/// decompress compressed digits to standard digits +template <typename VDIG, typename VCHAN> +void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& channelVec) +{ + digitVec.clear(); + channelVec.clear(); + digitVec.reserve(cd.header.nTriggers); + channelVec.reserve(cd.idChan.size()); + + uint32_t firstEntry = 0, clCount = 0, chipCount = 0; + o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); + + for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { + // restore ROFRecord + if (cd.orbitInc[idig]) { // non-0 increment => new orbit + ir.bc = cd.bcInc[idig]; // bcInc has absolute meaning + ir.orbit += cd.orbitInc[idig]; + } else { + ir.bc += cd.bcInc[idig]; + } + + firstEntry = channelVec.size(); + uint8_t chID = 0; + for (uint8_t ic = 0; ic < cd.nChan[idig]; ic++) { + auto icc = channelVec.size(); + const auto& chan = channelVec.emplace_back((chID += cd.idChan[icc]), cd.time[icc], cd.charge[icc]); + } + Triggers triggers; // TODO: Actual values are not set + digitVec.emplace_back(firstEntry, cd.nChan[idig], ir, triggers); + } +} + +} // namespace fv0 +} // namespace o2 + +#endif // O2_FV0_CTFCODER_H diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/ReadRaw.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/ReadRaw.h new file mode 100644 index 0000000000000..ade792cc6ee0d --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/ReadRaw.h @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ReadRaw.h +/// \brief Reads raw data and converts to digits +/// \author Maciej.Slupecki@cern.ch, arvind.khuntia@cern.ch, based on the FT0 code +// RAW data format description: DataFormat/Detectors/FIT/FV0/RawEventData + +#ifndef ALICEO2_FV0_READRAW_H_ +#define ALICEO2_FV0_READRAW_H_ + +#include <fstream> +#include <iostream> +#include <iomanip> +#include <map> +#include <string> +#include <sstream> +#include <vector> +#include "TBranch.h" +#include "TTree.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFV0/BCData.h" +#include "DataFormatsFV0/ChannelData.h" +#include "DataFormatsFV0/LookUpTable.h" +#include "DataFormatsFV0/RawEventData.h" + +namespace o2 +{ +namespace fv0 +{ +class ReadRaw +{ + public: + ReadRaw() = default; + ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath = "fv0.raw", const std::string outputRawFilePath = "fv0digitsFromRaw.root"); + void readRawData(const LookUpTable& lut); + void writeDigits(const std::string& outputDigitsFilePath); + void close(); + + private: + std::ifstream mRawFileIn; + std::map<o2::InteractionRecord, std::vector<ChannelData>> mDigitAccum; // digit accumulator + + template <typename T> + TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr) + { + if (auto br = tree.GetBranch(brname.c_str())) { + br->SetAddress(static_cast<void*>(ptr)); + return br; + } + // otherwise make it + return tree.Branch(brname.c_str(), ptr); + } + + ClassDefNV(ReadRaw, 1); +}; + +} // namespace fv0 +} // namespace o2 +#endif diff --git a/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..c7b7a8d994ecf --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,152 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of FV0 digits data + +#include "FV0Reconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::fv0; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, + std::vector<BCData>& digitVec, std::vector<ChannelData>& channelVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, digitVec, channelVec); +} + +///________________________________ +void CTFCoder::compress(CompressedDigits& cd, const gsl::span<const BCData>& digitVec, const gsl::span<const ChannelData>& channelVec) +{ + // convert digits/channel to their compressed version + cd.clear(); + if (!digitVec.size()) { + return; + } + const auto& dig0 = digitVec[0]; + cd.header.nTriggers = digitVec.size(); + cd.header.firstOrbit = dig0.ir.orbit; + cd.header.firstBC = dig0.ir.bc; + + cd.bcInc.resize(cd.header.nTriggers); + cd.orbitInc.resize(cd.header.nTriggers); + cd.nChan.resize(cd.header.nTriggers); + + cd.idChan.resize(channelVec.size()); + cd.time.resize(channelVec.size()); + cd.charge.resize(channelVec.size()); + + uint16_t prevBC = cd.header.firstBC; + uint32_t prevOrbit = cd.header.firstOrbit; + uint32_t ccount = 0; + for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { + const auto& digit = digitVec[idig]; + const auto chanels = digit.getBunchChannelData(channelVec); // we assume the channels are sorted + + // fill trigger info + if (prevOrbit == digit.ir.orbit) { + cd.bcInc[idig] = digit.ir.bc - prevBC; + cd.orbitInc[idig] = 0; + } else { + cd.bcInc[idig] = digit.ir.bc; + cd.orbitInc[idig] = digit.ir.orbit - prevOrbit; + } + prevBC = digit.ir.bc; + prevOrbit = digit.ir.orbit; + // fill channels info + cd.nChan[idig] = chanels.size(); + if (!cd.nChan[idig]) { + LOG(ERROR) << "Digits with no channels"; + continue; + } + uint8_t prevChan = 0; + for (uint8_t ic = 0; ic < cd.nChan[idig]; ic++) { + assert(prevChan <= chanels[ic].pmtNumber); + cd.idChan[ccount] = chanels[ic].pmtNumber - prevChan; + cd.time[ccount] = chanels[ic].time; // make sure it fits to short!!! + cd.charge[ccount] = chanels[ic].chargeAdc; // make sure we really need short!!! + prevChan = chanels[ic].pmtNumber; + ccount++; + } + } +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + CompressedDigits cd; // just to get member types +#define MAKECODER(part, slot) createCoder<decltype(part)::value_type>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(cd.bcInc, CTF::BLC_bcInc); + MAKECODER(cd.orbitInc, CTF::BLC_orbitInc); + MAKECODER(cd.nChan, CTF::BLC_nChan); + + MAKECODER(cd.idChan, CTF::BLC_idChan); + MAKECODER(cd.time, CTF::BLC_time); + MAKECODER(cd.charge, CTF::BLC_charge); + // clang-format on +} + +///________________________________ +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<decltype(vec)>::type::value_type +#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ + rans::calculateMaxBufferSize(vec.size(), reinterpret_cast<const o2::rans::LiteralEncoder64<VTP(vec)>*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) + sz += ESTSIZE(cd.bcInc, CTF::BLC_bcInc); + sz += ESTSIZE(cd.orbitInc, CTF::BLC_orbitInc); + sz += ESTSIZE(cd.nChan, CTF::BLC_nChan); + + sz += ESTSIZE(cd.idChan, CTF::BLC_idChan); + sz += ESTSIZE(cd.time, CTF::BLC_time); + sz += ESTSIZE(cd.charge, CTF::BLC_charge); + // clang-format on + + LOG(INFO) << "Estimated output size is " << sz << " bytes"; + return sz; +} diff --git a/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h b/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h new file mode 100644 index 0000000000000..9c78ce474d9df --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h @@ -0,0 +1,19 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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::fv0::ReadRaw + ; + +#endif diff --git a/Detectors/FIT/FV0/reconstruction/src/ReadRaw.cxx b/Detectors/FIT/FV0/reconstruction/src/ReadRaw.cxx new file mode 100644 index 0000000000000..aa71f667104a5 --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/src/ReadRaw.cxx @@ -0,0 +1,169 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FV0Reconstruction/ReadRaw.h" +#include "DetectorsRaw/RDHUtils.h" +#include "Framework/Logger.h" +#include "TFile.h" + +using namespace o2::fv0; +using RDHUtils = o2::raw::RDHUtils; + +ClassImp(ReadRaw); + +ReadRaw::ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath, const std::string outputDigitsFilePath) +{ + LOG(INFO) << "o2::fv0::ReadRaw::ReadRaw(): Read Raw file: " << inputRawFilePath.data() << " and convert to: " << outputDigitsFilePath.data(); + mRawFileIn.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mRawFileIn.open(inputRawFilePath, std::fstream::in | std::fstream::binary); + LookUpTable lut(true); + ReadRaw::readRawData(lut); + ReadRaw::writeDigits(outputDigitsFilePath.data()); +} + +void ReadRaw::readRawData(const LookUpTable& lut) +{ + LOG(INFO) << "o2::fv0::ReadRaw::readRawData():Start."; + constexpr int CRUWordSize = 16; + o2::header::RAWDataHeader mRDH; + ChannelData chData; // output to store digits + EventHeader eventHeader; // raw data container + TCMdata mTCMdata; // raw data container + EventData eventData[Constants::nChannelsPerPm]; // raw data container + + // Get input RAW file size + mRawFileIn.seekg(0, mRawFileIn.end); + long rawFileSize = mRawFileIn.tellg(); + mRawFileIn.seekg(0); + + long posInFile = 0; + while (posInFile < rawFileSize - sizeof(mRDH)) { + // Read contents of the next RDH + mRawFileIn.seekg(posInFile); + mRawFileIn.read(reinterpret_cast<char*>(&mRDH), sizeof(mRDH)); + int nwords = RDHUtils::getMemorySize(mRDH); + int offset = RDHUtils::getOffsetToNext(mRDH); + int link = RDHUtils::getLinkID(mRDH); + posInFile += offset; // posInFile is now prepared for the next iteration of the main loop + + int posPayload = 0; + if (nwords > sizeof(mRDH)) { + // RDHUtils::printRDH(mRDH); + // Read the payload following the RDH (only if there is anything to read) + posPayload = int(sizeof(mRDH)); + + while (posPayload < nwords) { + mRawFileIn.read(reinterpret_cast<char*>(&eventHeader), sizeof(eventHeader)); + posPayload += sizeof(eventHeader); + LOG(DEBUG) << " Read internal EventHeader for link: " << link + << " nWords: " << (int)eventHeader.nGBTWords + << " orbit: " << int(eventHeader.orbit) + << " BC: " << int(eventHeader.bc) + << " posInFile: " << posInFile + << " posPayload: " << posPayload; + o2::InteractionRecord intrec{uint16_t(eventHeader.bc), uint32_t(eventHeader.orbit)}; + + if (link == lut.getTcmLink()) { // is TCM payload + mRawFileIn.read(reinterpret_cast<char*>(&mTCMdata), sizeof(mTCMdata)); + posPayload += sizeof(mTCMdata); + LOG(DEBUG) << " Read TCM: posPayload: " << posPayload + << " posInFile: " << posInFile; + } else { // is PM payload + posPayload += CRUWordSize - o2::fv0::EventHeader::PayloadSize; // padding is enabled + for (int i = 0; i < eventHeader.nGBTWords; ++i) { + mRawFileIn.read(reinterpret_cast<char*>(&eventData[2 * i]), o2::fv0::EventData::PayloadSizeFirstWord); + posPayload += o2::fv0::EventData::PayloadSizeFirstWord; + chData = {Short_t(lut.getChannel(link, int(eventData[2 * i].channelID))), + Float_t(eventData[2 * i].time), + Short_t(eventData[2 * i].charge)}; + mDigitAccum[intrec].emplace_back(chData); + LOG(DEBUG) << " Read 1st half-word: (PMchannel, globalChannel, Q, T, posPayload) = " + << std::setw(3) << int(eventData[2 * i].channelID) + << std::setw(4) << lut.getChannel(link, int(eventData[2 * i].channelID)) + << std::setw(5) << int(eventData[2 * i].charge) + << std::setw(5) << float(eventData[2 * i].time) + << std::setw(5) << posPayload; + + Short_t channelIdFirstHalfWord = chData.pmtNumber; + + mRawFileIn.read(reinterpret_cast<char*>(&eventData[2 * i + 1]), EventData::PayloadSizeSecondWord); + posPayload += o2::fv0::EventData::PayloadSizeSecondWord; + chData = {Short_t(lut.getChannel(link, (eventData[2 * i + 1].channelID))), + Float_t(eventData[2 * i + 1].time), + Short_t(eventData[2 * i + 1].charge)}; + if (chData.pmtNumber <= channelIdFirstHalfWord) { + // Don't save the second half-word if it is only filled with zeroes (empty-data) + // TODO: Verify if it works correctly with real data from readout + continue; + } + mDigitAccum[intrec].emplace_back(chData); + LOG(DEBUG) << " Read 2nd half-word: (PMchannel, globalChannel, Q, T, posPayload) = " + << std::setw(3) << int(eventData[2 * i + 1].channelID) + << std::setw(4) << lut.getChannel(link, int(eventData[2 * i + 1].channelID)) + << std::setw(5) << int(eventData[2 * i + 1].charge) + << std::setw(5) << float(eventData[2 * i + 1].time) + << std::setw(5) << posPayload; + } + } + } + } + } + close(); + LOG(INFO) << "o2::fv0::ReadRaw::readRawData():Finished."; +} + +void ReadRaw::close() +{ + if (mRawFileIn.is_open()) { + mRawFileIn.close(); + } +} + +void ReadRaw::writeDigits(const std::string& outputDigitsFilePath) +{ + TFile* outFile = new TFile(outputDigitsFilePath.data(), "RECREATE"); + if (!outFile || outFile->IsZombie()) { + LOG(ERROR) << "Failed to open " << outputDigitsFilePath << " output file"; + } else { + LOG(INFO) << "o2::fv0::ReadRaw::writeDigits(): Opened output file: " << outputDigitsFilePath; + } + TTree* outTree = new TTree("o2sim", "o2sim"); + std::vector<ChannelData> chDataVecTree; + std::vector<BCData> chBcVecTree; + + for (auto& digit : mDigitAccum) { + LOG(DEBUG) << " IR (" << digit.first << ") (i, PMT, Q, T):"; + for (uint16_t i = 0; i < digit.second.size(); i++) { + ChannelData* chd = &(digit.second.at(i)); + LOG(DEBUG) << " " << std::setw(3) << i + << std::setw(4) << chd->pmtNumber + << std::setw(5) << chd->chargeAdc + << std::setw(5) << chd->time; + } + + size_t nStored = 0; + size_t first = chDataVecTree.size(); + for (auto& sec : digit.second) { + chDataVecTree.emplace_back(int(sec.pmtNumber), float(sec.time), Short_t(sec.chargeAdc)); + nStored++; + } + Triggers triggers; // TODO: Actual values are not set + chBcVecTree.emplace_back(first, nStored, digit.first, triggers); + } + + outTree->Branch("FV0DigitBC", &chBcVecTree); + outTree->Branch("FV0DigitCh", &chDataVecTree); + outTree->Fill(); + + outFile->cd(); + outTree->Write(); + outFile->Close(); + LOG(INFO) << "o2::fv0::ReadRaw::writeDigits(): Finished converting " << chBcVecTree.size() << " events."; +} diff --git a/Detectors/FIT/FV0/simulation/CMakeLists.txt b/Detectors/FIT/FV0/simulation/CMakeLists.txt index 49ab353556728..99ba6485279a9 100644 --- a/Detectors/FIT/FV0/simulation/CMakeLists.txt +++ b/Detectors/FIT/FV0/simulation/CMakeLists.txt @@ -12,15 +12,30 @@ o2_add_library(FV0Simulation SOURCES src/Detector.cxx src/Digitizer.cxx src/FV0DigParam.cxx + src/Digits2Raw.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::FV0Base O2::DataFormatsFV0 - ROOT::Physics) + O2::DetectorsRaw + ROOT::Physics + O2::Headers + O2::Framework) o2_target_root_dictionary(FV0Simulation - HEADERS include/FV0Simulation/Detector.h - include/FV0Simulation/DigitizationConstant.h - include/FV0Simulation/Digitizer.h - include/FV0Simulation/FV0DigParam.h) + HEADERS include/FV0Simulation/Detector.h + include/FV0Simulation/DigitizationConstant.h + include/FV0Simulation/Digitizer.h + include/FV0Simulation/FV0DigParam.h + include/FV0Simulation/Digits2Raw.h) +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_data_file(COPY data DESTINATION Detectors/FV0/simulation) + diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Detector.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Detector.h index c70a98362c6c5..6e708568282f3 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Detector.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Detector.h @@ -108,8 +108,8 @@ class Detector : public o2::base::DetImpl<Detector> } mTrackData; //! o2::fv0::Hit* addHit(Int_t trackId, Int_t cellId, - const Point3D<float>& startPos, const Point3D<float>& endPos, - const Vector3D<float>& startMom, double startE, + const math_utils::Point3D<float>& startPos, const math_utils::Point3D<float>& endPos, + const math_utils::Vector3D<float>& startMom, double startE, double endTime, double eLoss, Int_t particlePdg); template <typename Det> diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/DigitizationConstant.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/DigitizationConstant.h index 00fca32dd7cd3..6faa00ff54bec 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/DigitizationConstant.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/DigitizationConstant.h @@ -14,15 +14,10 @@ namespace o2::fv0 { struct DigitizationConstant { - static constexpr int NCELLSA = 40; // number of scintillator cells - static constexpr int NCHANNELS = 48; // number of readout channels - static constexpr float INV_CHARGE_PER_ADC = 1. / 0.6e-12; // charge - static constexpr float N_PHOTONS_PER_MEV = 10400; // average #photons generated per 1 MeV of deposited energy - - //TODO: optimize random ring sizes to balance between sim quality and execution time - static constexpr int PHE_RANDOM_RING_SIZE = 1e5; // size of random ring to be used inside photoelectron loop - static constexpr int HIT_RANDOM_RING_SIZE = 1e4; // size of random ring to be used inside hit loop - static constexpr int NUM_PMT_RESPONSE_TABLES = 9; // number of PMT response tables + static constexpr int NCELLSA = 40; // number of scintillator cells + static constexpr float INV_CHARGE_PER_ADC = 1. / 0.6e-12; // charge conversion + static constexpr float INV_TIME_PER_TDCCHANNEL = 1. / 0.01302; // time conversion from ns to TDC channels + static constexpr float N_PHOTONS_PER_MEV = 10400; // average #photons generated per 1 MeV of deposited energy }; } // namespace o2::fv0 #endif diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h index 8800192a86843..0eba60c97bab3 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h @@ -11,7 +11,8 @@ #ifndef ALICEO2_FV0_DIGITIZER_H #define ALICEO2_FV0_DIGITIZER_H -#include <FV0Simulation/MCLabel.h> +#include "FV0Base/Constants.h" +#include <DataFormatsFV0/MCLabel.h> #include <FV0Simulation/DigitizationConstant.h> #include <FV0Simulation/FV0DigParam.h> #include <DataFormatsFV0/ChannelData.h> @@ -31,12 +32,10 @@ class Digitizer { private: using DP = DigitizationConstant; - typedef math_utils::RandomRing<float_v::size() * DP::HIT_RANDOM_RING_SIZE> HitRandomRingType; - typedef math_utils::RandomRing<float_v::size() * DP::PHE_RANDOM_RING_SIZE> PheRandomRingType; public: Digitizer() - : mTimeStamp(0), mIntRecord(), mEventId(-1), mSrcId(-1), mMCLabels(), mPmtChargeVsTime(), mNBins(), mRndScintDelay(HitRandomRingType::RandomType::CustomTF1), mRndGainVar(PheRandomRingType::RandomType::CustomTF1), mRndSignalShape(PheRandomRingType::RandomType::CustomTF1), mPmtResponseTables() + : mTimeStamp(0), mIntRecord(), mEventId(-1), mSrcId(-1), mMCLabels(), mCache(), mPmtChargeVsTime(), mNBins(), mNTimeBinsPerBC(), mPmtResponseGlobal(), mPmtResponseTemp() { } @@ -54,50 +53,83 @@ class Digitizer void setSrcId(Int_t id) { mSrcId = id; } void setInteractionRecord(const InteractionTimeRecord& ir) { mIntRecord = ir; } - void process(const std::vector<o2::fv0::Hit>& hits); - void analyseWaveformsAndStore(std::vector<fv0::BCData>& digitsBC, - std::vector<fv0::ChannelData>& digitsCh, - dataformats::MCTruthContainer<fv0::MCLabel>& labels); + void process(const std::vector<o2::fv0::Hit>& hits, std::vector<o2::fv0::BCData>& digitsBC, + std::vector<o2::fv0::ChannelData>& digitsCh, std::vector<o2::fv0::DetTrigInput>& digitsTrig, + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>& labels); + + void flush(std::vector<o2::fv0::BCData>& digitsBC, + std::vector<o2::fv0::ChannelData>& digitsCh, + std::vector<o2::fv0::DetTrigInput>& digitsTrig, + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>& labels); const InteractionRecord& getInteractionRecord() const { return mIntRecord; } InteractionRecord& getInteractionRecord(InteractionRecord& src) { return mIntRecord; } uint32_t getOrbit() const { return mIntRecord.orbit; } uint16_t getBC() const { return mIntRecord.bc; } + using ChannelBCDataF = std::vector<float>; + + struct BCCache : public o2::InteractionRecord { + std::vector<o2::fv0::MCLabel> labels; + std::array<ChannelBCDataF, Constants::nFv0Channels> mPmtChargeVsTime = {}; + + void clear() + { + for (auto& channel : mPmtChargeVsTime) { + std::fill(channel.begin(), channel.end(), 0.); + } + labels.clear(); + } + + BCCache& operator=(const o2::InteractionRecord& ir) + { + o2::InteractionRecord::operator=(ir); + return *this; + } + void print() const; + }; + private: + static constexpr int BCCacheMin = 0, BCCacheMax = 7, NBC2Cache = 1 + BCCacheMax - BCCacheMin; + void createPulse(float mipFraction, int parID, const double hitTime, std::array<o2::InteractionRecord, NBC2Cache> const& cachedIR, + int nCachedIR, const int detID); + long mTimeStamp; // TF (run) timestamp - InteractionRecord mIntRecord; // Interaction record (orbit, bc) -> InteractionTimeRecord + InteractionTimeRecord mIntRecord; // Interaction record (orbit, bc) -> InteractionTimeRecord Int_t mEventId; // ID of the current event Int_t mSrcId; // signal, background or QED - std::vector<fv0::MCLabel> mMCLabels; + std::deque<fv0::MCLabel> mMCLabels; + std::deque<BCCache> mCache; - std::array<std::vector<Float_t>, DP::NCHANNELS> mPmtChargeVsTime; // Charge time series aka analogue signal pulse from PM - UInt_t mNBins; // Number of bins in pulse series - Float_t mBinSize; // Time width of the pulse bin - HPTDC resolution - Float_t mPmtTimeIntegral; // + BCCache& setBCCache(const o2::InteractionRecord& ir); + BCCache* getBCCache(const o2::InteractionRecord& ir); - // Random rings - HitRandomRingType mRndScintDelay; - PheRandomRingType mRndGainVar; - PheRandomRingType mRndSignalShape; + void storeBC(const BCCache& bc, + std::vector<o2::fv0::BCData>& digitsBC, + std::vector<o2::fv0::ChannelData>& digitsCh, + std::vector<o2::fv0::DetTrigInput>& digitsTrig, + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>& labels); - // 8 tables starting at different sub-bin positions, i.e, [-4:4] / 8 * mBinSize - // wit each table containg values for start + [-2:2:mBinSize] * DigitizationParameters::mPmtTransitTime - std::array<std::vector<Float_t>, DP::NUM_PMT_RESPONSE_TABLES> mPmtResponseTables; + std::array<std::vector<Float_t>, Constants::nFv0Channels> mPmtChargeVsTime; // Charge time series aka analogue signal pulse from PM + UInt_t mNBins; // + UInt_t mNTimeBinsPerBC; + Float_t mBinSize; // Time width of the pulse bin - HPTDC resolution - // Internal helper methods related to conversion of energy-deposition into photons -> photoelectrons -> el. signal - Int_t SimulateLightYield(Int_t pmt, Int_t nPhot) const; - Float_t SimulateTimeCfd(Int_t channel) const; + /// vectors to store the PMT signal from cosmic muons + std::vector<Double_t> mPmtResponseGlobal; + std::vector<Double_t> mPmtResponseTemp; - static Double_t PmtResponse(Double_t x); - static Double_t PmtResponse(Double_t* x, Double_t*); - static Double_t SinglePhESpectrum(Double_t* x, Double_t* par); + /// 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(const ChannelBCDataF& pulse) const; + Float_t IntegrateCharge(const ChannelBCDataF& pulse) const; + // Float_t SimulateTimeCfd(Int_t channel, Int_t iCache) const; - // Functions related to splitting ring-5 cell signal to two readout channels + /// Functions related to splitting ring-5 cell signal to two readout channels static float getDistFromCellCenter(UInt_t cellId, double hitx, double hity); static float getSignalFraction(float distanceFromXc, bool isFirstChannel); - ClassDefNV(Digitizer, 1); + ClassDefNV(Digitizer, 2); }; // Function used to split the ring-5 cell signal into two readout channels depending on hit position diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digits2Raw.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digits2Raw.h new file mode 100644 index 0000000000000..8e5c881440f4c --- /dev/null +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digits2Raw.h @@ -0,0 +1,68 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Digits2Raw.h +/// \brief converts digits to raw format +/// \author Maciej.Slupecki@cern.ch +// based on FT0 + +#ifndef ALICEO2_FV0_DIGITS2RAW_H_ +#define ALICEO2_FV0_DIGITS2RAW_H_ + +#include "Headers/RAWDataHeader.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFV0/RawEventData.h" +#include "DataFormatsFV0/LookUpTable.h" +#include "DataFormatsFV0/ChannelData.h" +#include "DataFormatsFV0/BCData.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsRaw/RawFileWriter.h" +#include <FairLogger.h> +#include <TStopwatch.h> +#include <iostream> +#include <string> +#include <vector> +#include <gsl/span> + +namespace o2 +{ +namespace fv0 +{ +class Digits2Raw +{ + public: + Digits2Raw() = default; + void readDigits(const std::string& outDir, const std::string& fileDigitsName); + void convertDigits(o2::fv0::BCData bcdigits, + gsl::span<const ChannelData> pmchannels, + const o2::fv0::LookUpTable& lut); + + o2::raw::RawFileWriter& getWriter() { return mWriter; } + void setFilePerLink(bool v) { mOutputPerLink = v; } + bool getFilePerLink() const { return mOutputPerLink; } + + private: + static constexpr uint32_t sTcmLink = 4; + static constexpr uint16_t sCruId = 0; + static constexpr uint32_t sEndPointId = sCruId; + + void makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord); + void fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir); + RawEventData mRawEventData; + o2::raw::RawFileWriter mWriter{"FV0"}; + bool mOutputPerLink = false; + ///////////////////////////////////////////////// + + ClassDefNV(Digits2Raw, 1); +}; + +} // namespace fv0 +} // namespace o2 +#endif diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h index 2d9d0ad991d70..96d9055b60d3b 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h @@ -13,35 +13,41 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" +#include "CommonConstants/PhysicsConstants.h" namespace o2 { namespace fv0 { // parameters of FV0 digitization / transport simulation - struct FV0DigParam : public o2::conf::ConfigurableParamHelper<FV0DigParam> { float intrinsicTimeRes = 0.91; // time resolution float photoCathodeEfficiency = 0.23; // quantum efficiency = nOfPhotoE_emitted_by_photocathode / nIncidentPhotons - float lightYield = 0.1; // light collection efficiency to be tuned using collision data + float lightYield = 0.01; // light collection efficiency to be tuned using collision data [1%] float pmtGain = 5e4; // value for PMT R5924-70 at default FV0 gain float pmtTransitTime = 9.5; // PMT response time (corresponds to 1.9 ns rise time) float pmtTransparency = 0.25; // Transparency of the first dynode of the PMT - float pmtNbOfSecElec = 9.0; // Number of secondary electrons emitted from first dynode (per ph.e.) - float shapeConst = 0.029; // Crystal ball const parameter - float shapeMean = 10.2; // Crystal ball mean parameter - float shapeAlpha = -0.34; // Crystal ball alpha parameter - float shapeN = 7.6e06; // Crystal ball N parameter - float shapeSigma = 3.013; // Crystal ball sigma parameter - float timeShiftCfd = 3.3; // TODO: adjust after PM design for FV0 is fixed - int photoelMin = 0; // integration lower limit - int photoelMax = 30; // integration upper limit + float shapeConst = 1.18059e-14; // Crystal ball const parameter + float shapeMean = 9.5; // Crystal ball mean parameter + float shapeAlpha = -6.56586e-01; // Crystal ball alpha parameter + float shapeN = 2.36408e+00; // Crystal ball N parameter + float shapeSigma = 3.55445; // Crystal ball sigma parameter + //float timeShiftCfd = 3.3; // From the cosmic measurements of FV0 [keep it for reference] + float timeShiftCfd = 5.3; // TODO: adjust after FV0 with FEE measurements are done float singleMipThreshold = 3.0; // in [MeV] of deposited energy - float waveformNbins = 2000; // number of bins for the analog pulse waveform - float waveformBinWidth = 0.09765625; // number of bins for the analog (25.0 / 256.0) + float singleHitTimeThreshold = 120.0; // in [ns] to skip very slow particles + float waveformNbins = 10000; // number of bins for the analog pulse waveform + float waveformBinWidth = 0.01302; // number of bins for the analog + float avgCfdTimeForMip = 8.63; // in ns to shift the CFD time to zero TODO do ring wise + int chargeIntBinMin = (avgCfdTimeForMip - 6.0) / waveformBinWidth; //Charge integration offset (cfd mean time - 6 ns) + int chargeIntBinMax = (avgCfdTimeForMip + 14.0) / waveformBinWidth; //Charge integration offset (cfd mean time + 14 ns) + 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 - //Optimization-related, derived constants - float oneOverPmtTransitTime2 = 1.0 / (pmtTransitTime * pmtTransitTime); + ///Parameters for trigger simulation + int adcChargeHighMultTh = 3.0 * 498; //threshold value of ADC charge for high multiplicity trigger O2ParamDef(FV0DigParam, "FV0DigParam"); }; diff --git a/Detectors/FIT/FV0/simulation/src/Detector.cxx b/Detectors/FIT/FV0/simulation/src/Detector.cxx index 2a2ec90c637ec..d444deab53ea4 100644 --- a/Detectors/FIT/FV0/simulation/src/Detector.cxx +++ b/Detectors/FIT/FV0/simulation/src/Detector.cxx @@ -129,9 +129,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) // Get unique ID of the detector cell (sensitive volume) Int_t cellId = mGeometry->getCurrentCellId(fMC); - Point3D<float> posStart(mTrackData.mPositionStart.X(), mTrackData.mPositionStart.Y(), mTrackData.mPositionStart.Z()); - Point3D<float> posStop(positionStop.X(), positionStop.Y(), positionStop.Z()); - Vector3D<float> momStart(mTrackData.mMomentumStart.Px(), mTrackData.mMomentumStart.Py(), mTrackData.mMomentumStart.Pz()); + math_utils::Point3D<float> posStart(mTrackData.mPositionStart.X(), mTrackData.mPositionStart.Y(), mTrackData.mPositionStart.Z()); + math_utils::Point3D<float> posStop(positionStop.X(), positionStop.Y(), positionStop.Z()); + math_utils::Vector3D<float> momStart(mTrackData.mMomentumStart.Px(), mTrackData.mMomentumStart.Py(), mTrackData.mMomentumStart.Pz()); addHit(trackID, cellId, posStart, posStop, momStart, mTrackData.mMomentumStart.E(), positionStop.T(), mTrackData.mEnergyLoss, particlePdg); @@ -278,8 +278,8 @@ void Detector::ConstructGeometry() } o2::fv0::Hit* Detector::addHit(Int_t trackId, Int_t cellId, - const Point3D<float>& startPos, const Point3D<float>& endPos, - const Vector3D<float>& startMom, double startE, + const math_utils::Point3D<float>& startPos, const math_utils::Point3D<float>& endPos, + const math_utils::Vector3D<float>& startMom, double startE, double endTime, double eLoss, Int_t particlePdg) { mHits->emplace_back(trackId, cellId, startPos, endPos, startMom, startE, endTime, eLoss, particlePdg); diff --git a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx index d644a78e58e5c..c8cd42e29145d 100644 --- a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx @@ -10,6 +10,7 @@ #include "FV0Simulation/Digitizer.h" #include "FV0Base/Geometry.h" +#include "FV0Base/Constants.h" #include <TRandom.h> #include <algorithm> @@ -32,91 +33,59 @@ void Digitizer::clear() void Digitizer::init() { LOG(INFO) << "V0Digitizer::init -> start = "; - mNBins = FV0DigParam::Instance().waveformNbins; //Will be computed using detector set-up from CDB mBinSize = FV0DigParam::Instance().waveformBinWidth; //Will be set-up from CDB - for (Int_t i = 0; i < DP::NCHANNELS; i++) { - mPmtChargeVsTime[i].resize(mNBins); - } + mNTimeBinsPerBC = int(o2::constants::lhc::LHCBunchSpacingNS / mBinSize); - auto const roundVc = [&](int i) -> int { - return (i / Vc::float_v::Size) * Vc::float_v::Size; - }; - // set up PMT response tables - Float_t offset = -0.5f * mBinSize; // offset \in [-0.5..0.5] * mBinSize - Int_t const nBins = roundVc(std::lround(4.0f * FV0DigParam::Instance().pmtTransitTime / mBinSize)); - for (auto& table : mPmtResponseTables) { - table.resize(nBins); - Float_t t = -2.0f * FV0DigParam::Instance().pmtTransitTime + offset; // t \in offset + [-2 2] * FV0DigParam::Instance().mPmtTransitTime - for (Int_t j = 0; j < nBins; ++j) { - table[j] = Digitizer::PmtResponse(t); - t += mBinSize; - } - offset += mBinSize / Float_t(DP::NUM_PMT_RESPONSE_TABLES - 1); + for (Int_t detID = 0; detID < Constants::nFv0Channels; detID++) { + mPmtChargeVsTime[detID].resize(mNBins); } - TF1 scintDelayFn("fScintDelay", "gaus", - -6.0f * FV0DigParam::Instance().intrinsicTimeRes, - +6.0f * FV0DigParam::Instance().intrinsicTimeRes); - scintDelayFn.SetParameters(1, 0, FV0DigParam::Instance().intrinsicTimeRes); - mRndScintDelay.initialize(scintDelayFn); - - // Initialize function describing the PMT time response - TF1 pmtResponseFn("mPmtResponse", - &Digitizer::PmtResponse, - -1.0f * FV0DigParam::Instance().pmtTransitTime, - +2.0f * FV0DigParam::Instance().pmtTransitTime, 0); - pmtResponseFn.SetNpx(100); - mPmtTimeIntegral = pmtResponseFn.Integral(-1.0f * FV0DigParam::Instance().pmtTransitTime, - +2.0f * FV0DigParam::Instance().pmtTransitTime); - - // Initialize function describing PMT response to the single photoelectron - TF1 singlePhESpectrumFn("mSinglePhESpectrum", - &Digitizer::SinglePhESpectrum, - FV0DigParam::Instance().photoelMin, - FV0DigParam::Instance().photoelMax, 0); - Float_t const meansPhE = singlePhESpectrumFn.Mean(FV0DigParam::Instance().photoelMin, FV0DigParam::Instance().photoelMax); - mRndGainVar.initialize([&]() -> float { - return singlePhESpectrumFn.GetRandom(FV0DigParam::Instance().photoelMin, FV0DigParam::Instance().photoelMax) / meansPhE; - }); - + // set up PMT response function [avg] TF1 signalShapeFn("signalShape", "crystalball", 0, 200); signalShapeFn.SetParameters(FV0DigParam::Instance().shapeConst, FV0DigParam::Instance().shapeMean, FV0DigParam::Instance().shapeSigma, FV0DigParam::Instance().shapeAlpha, FV0DigParam::Instance().shapeN); - mRndSignalShape.initialize([&]() -> float { - return signalShapeFn.GetRandom(0, mBinSize * Float_t(mNBins)); - }); + + // PMT response per hit [Global] + float x = mBinSize / 2.0; /// Calculate at BinCenter + mPmtResponseGlobal.resize(mNBins); + for (Int_t j = 0; j < mPmtResponseGlobal.size(); ++j) { + mPmtResponseGlobal[j] = signalShapeFn.Eval(x); + //LOG(INFO)<<x<<" "<<mPmtResponseGlobal[j]; + x += mBinSize; + } LOG(INFO) << "V0Digitizer::init -> finished"; } -void Digitizer::process(const std::vector<o2::fv0::Hit>& hits) +void Digitizer::process(const std::vector<o2::fv0::Hit>& hits, + std::vector<o2::fv0::BCData>& digitsBC, + std::vector<o2::fv0::ChannelData>& digitsCh, + std::vector<o2::fv0::DetTrigInput>& digitsTrig, + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>& labels) { LOG(INFO) << "[FV0] Digitizer::process(): begin with " << hits.size() << " hits"; + flush(digitsBC, digitsCh, digitsTrig, labels); // flush cached signal which cannot be affect by new event std::vector<int> hitIdx(hits.size()); std::iota(std::begin(hitIdx), std::end(hitIdx), 0); - std::sort(std::begin(hitIdx), std::end(hitIdx), [&hits](int a, int b) { return hits[a].GetTrackID() < hits[b].GetTrackID(); }); - - auto const roundVc = [&](int i) -> int { - return (i / Vc::float_v::Size) * Vc::float_v::Size; - }; + std::sort(std::begin(hitIdx), std::end(hitIdx), + [&hits](int a, int b) { return hits[a].GetTrackID() < hits[b].GetTrackID(); }); Int_t parentIdPrev = -10; // use ordered 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; // TODO: check how big is inaccuracy if more than 1 'below-threshold' particles hit the same detector cell - if (hitEdep < FV0DigParam::Instance().singleMipThreshold) { + if (hitEdep < FV0DigParam::Instance().singleMipThreshold || hitTime > FV0DigParam::Instance().singleHitTimeThreshold) { continue; } - float distanceFromXc = 0; if (Geometry::instance()->isRing5(detId)) { distanceFromXc = getDistFromCellCenter(detId, hit.GetX(), hit.GetY()); @@ -126,93 +95,163 @@ void Digitizer::process(const std::vector<o2::fv0::Hit>& hits) while (iChannelPerCell < 2) { // loop over 2 channels, into which signal from each cell in ring 5 is split if (Geometry::instance()->isRing5(detId)) { // The first channel number is located counter-clockwise from the cell center - // and remains identical to the detector number, the second one is clockwise and incremented by 8 + // and remains identical to the detector number, the second one is clockwise and incremented by 8 if (iChannelPerCell == 1) { detId += 8; } - // Split signal magnitude to fractions depending on the distance of the hit from the cell center hitEdep = (hit.GetHitValue() * 1e3) * getSignalFraction(distanceFromXc, iChannelPerCell == 0); - LOG(INFO) << " detId: " << detId << "-" << iChannelPerCell << " hitEdep: " << hitEdep << " distanceFromXc: " << distanceFromXc; + // LOG(INFO) << " detId: " << detId << "-" << iChannelPerCell << " hitEdep: " << hitEdep << " distanceFromXc: " << distanceFromXc; ++iChannelPerCell; } else { iChannelPerCell = 2; // not a ring 5 cell -> don't repeat the loop } - Double_t const nPhotons = hitEdep * DP::N_PHOTONS_PER_MEV; - Int_t const nPhE = SimulateLightYield(detId, nPhotons); - Float_t const t = hit.GetTime() * 1e9 + FV0DigParam::Instance().pmtTransitTime; - Float_t const charge = TMath::Qe() * FV0DigParam::Instance().pmtGain * mBinSize / mPmtTimeIntegral; - - auto& analogSignal = mPmtChargeVsTime[detId]; - for (Int_t iPhE = 0; iPhE < nPhE; ++iPhE) { - Float_t const tPhE = t + mRndSignalShape.getNextValue(); - Int_t const firstBin = roundVc( - TMath::Max((Int_t)0, (Int_t)((tPhE - FV0DigParam::Instance().pmtTransitTime) / mBinSize))); - Int_t const lastBin = TMath::Min((Int_t)mNBins - 1, - (Int_t)((tPhE + 2. * FV0DigParam::Instance().pmtTransitTime) / mBinSize)); - Float_t const tempT = mBinSize * (0.5f + firstBin) - tPhE; - Float_t* p = analogSignal.data() + firstBin; - long iStart = std::lround((tempT + 2.0f * FV0DigParam::Instance().pmtTransitTime) / mBinSize); - float const offset = tempT + 2.0f * FV0DigParam::Instance().pmtTransitTime - Float_t(iStart) * mBinSize; - long const iOffset = std::lround(offset / mBinSize * Float_t(DP::NUM_PMT_RESPONSE_TABLES - 1)); - if (iStart < 0) { // this should not happen - LOG(ERROR) << "V0Digitizer: table lookup failure"; - } - iStart = roundVc(std::max(long(0), iStart)); - - Vc::float_v workVc; - Vc::float_v pmtVc; - Float_t const* q = mPmtResponseTables[DP::NUM_PMT_RESPONSE_TABLES / 2 + iOffset].data() + iStart; - Float_t const* qEnd = &mPmtResponseTables[DP::NUM_PMT_RESPONSE_TABLES / 2 + iOffset].back(); - for (Int_t i = firstBin, iEnd = roundVc(lastBin); q < qEnd && i < iEnd; i += Vc::float_v::Size) { - pmtVc.load(q); - q += Vc::float_v::Size; - Vc::prefetchForOneRead(q); - workVc.load(p); - workVc += mRndGainVar.getNextValueVc() * charge * pmtVc; - workVc.store(p); - p += Vc::float_v::Size; - Vc::prefetchForOneRead(p); + float const nPhE = SimulateLightYield(detId, nPhotons); + float mipFraction = float(nPhE / FV0DigParam::Instance().avgNumberPhElectronPerMip); + Float_t timeHit = hitTime; + timeHit += mIntRecord.getTimeNS(); + o2::InteractionTimeRecord irHit(timeHit); + std::array<o2::InteractionRecord, NBC2Cache> cachedIR; + int nCachedIR = 0; + for (int i = BCCacheMin; i < BCCacheMax + 1; i++) { + double tNS = timeHit + o2::constants::lhc::LHCBunchSpacingNS * i; + cachedIR[nCachedIR].setFromNS(tNS); + if (tNS < 0 && cachedIR[nCachedIR] > irHit) { + continue; // don't go to negative BC/orbit (it will wrap) } - } //photo electron loop + setBCCache(cachedIR[nCachedIR++]); // ensure existence of cached container + } //BCCache loop + createPulse(mipFraction, hit.GetTrackID(), hitTime, cachedIR, nCachedIR, detId); - // Charged particles in MCLabel - Int_t const parentId = hit.GetTrackID(); - if (parentId != parentIdPrev) { - mMCLabels.emplace_back(parentId, mEventId, mSrcId, detId); - parentIdPrev = parentId; - } + } //while loop + } //hitloop +} + +void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, + std::array<o2::InteractionRecord, NBC2Cache> const& cachedIR, int nCachedIR, const int detId) +{ + + bool added[nCachedIR]; + for (int ir = 0; ir < nCachedIR; ir++) { + added[ir] = false; + } + + for (int ir = 0; ir < NBC2Cache; ir++) { + auto bcCache = getBCCache(cachedIR[ir]); + for (int ich = 0; ich < Constants::nFv0Channels; ich++) { + (*bcCache).mPmtChargeVsTime[ich].resize(mNTimeBinsPerBC); } - } //hit loop + } + + ///Time of flight subtracted from Hit time //TODO have different TOF according to thr ring number + Int_t NBinShift = std::lround((hitTime - FV0DigParam::Instance().globalTimeOfFlight) / FV0DigParam::Instance().waveformBinWidth); + + if (NBinShift >= 0 && NBinShift < FV0DigParam::Instance().waveformNbins) { + mPmtResponseTemp.resize(FV0DigParam::Instance().waveformNbins, 0.); + std::memcpy(&mPmtResponseTemp[NBinShift], &mPmtResponseGlobal[0], + sizeof(double) * (FV0DigParam::Instance().waveformNbins - NBinShift)); + } else { + mPmtResponseTemp = mPmtResponseGlobal; + mPmtResponseTemp.erase(mPmtResponseTemp.begin(), mPmtResponseTemp.begin() + abs(NBinShift)); + mPmtResponseTemp.resize(FV0DigParam::Instance().waveformNbins); + } + + for (int ir = 0; ir < int(mPmtResponseTemp.size() / mNTimeBinsPerBC); ir++) { + auto bcCache = getBCCache(cachedIR[ir]); + + for (int iBin = 0; iBin < mNTimeBinsPerBC; iBin++) { + (*bcCache).mPmtChargeVsTime[detId][iBin] += (mPmtResponseTemp[ir * mNTimeBinsPerBC + iBin] * mipFraction); + } + added[ir] = true; + } + ///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]); + (*bcCache).labels.emplace_back(parID, mEventId, mSrcId, detId); + } + } } -void Digitizer::analyseWaveformsAndStore(std::vector<fv0::BCData>& digitsBC, - std::vector<fv0::ChannelData>& digitsCh, - dataformats::MCTruthContainer<fv0::MCLabel>& labels) +void Digitizer::flush(std::vector<o2::fv0::BCData>& digitsBC, + std::vector<o2::fv0::ChannelData>& digitsCh, + std::vector<o2::fv0::DetTrigInput>& digitsTrig, + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>& labels) +{ + int nCached = mCache.size(); + if (nCached < 1) { + return; + } + for (auto bc : mCache) { + if (mIntRecord.differenceInBC(bc) > NBC2Cache) { // Build events those are separated by NBC2Cache BCs from current BC + storeBC(bc, digitsBC, digitsCh, digitsTrig, labels); + mCache.pop_front(); + } else { + return; + } + } +} + +void Digitizer::storeBC(const BCCache& bc, + std::vector<o2::fv0::BCData>& digitsBC, + std::vector<o2::fv0::ChannelData>& digitsCh, + std::vector<o2::fv0::DetTrigInput>& digitsTrig, + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>& labels) + { - // Sum charge of all time bins to get total charge collected for a given channel - size_t const first = digitsCh.size(); + int first = digitsCh.size(); size_t nStored = 0; - for (Int_t ipmt = 0; ipmt < DP::NCHANNELS; ++ipmt) { - Float_t totalCharge = 0.0f; - auto const& analogSignal = mPmtChargeVsTime[ipmt]; - for (Int_t iTimeBin = 0; iTimeBin < mNBins; ++iTimeBin) { - Float_t const timeBinCharge = mPmtChargeVsTime[ipmt][iTimeBin]; - totalCharge += timeBinCharge; + double totalCharge = 0; + double totalChargeAllRing = 0; + double nSignalInner = 0; + double nSignalOuter = 0; + + for (int iPmt = 0; iPmt < Constants::nFv0Channels; iPmt++) { + double cfdWithOffset = SimulateTimeCfd(bc.mPmtChargeVsTime[iPmt]); + double cfdZero = cfdWithOffset - FV0DigParam::Instance().avgCfdTimeForMip; + + if (cfdZero < -FV0DigParam::Instance().cfdCheckWindow || cfdZero > FV0DigParam::Instance().cfdCheckWindow) { + continue; } + float charge = IntegrateCharge(bc.mPmtChargeVsTime[iPmt]); + totalCharge += charge; + totalChargeAllRing += charge; totalCharge *= DP::INV_CHARGE_PER_ADC; - digitsCh.emplace_back(ipmt, SimulateTimeCfd(ipmt), std::lround(totalCharge)); + cfdZero *= DP::INV_TIME_PER_TDCCHANNEL; + + digitsCh.emplace_back(iPmt, static_cast<short int>(std::round(cfdZero)), + static_cast<short int>(std::round(totalCharge))); + totalCharge = 0; ++nStored; + //---trigger--- + if (iPmt < 25) { + nSignalInner++; + } else { + nSignalOuter++; + } } - - // Send MClabels and digitsBC to storage - size_t const nBC = digitsBC.size(); - digitsBC.emplace_back(first, nStored, mIntRecord); - for (auto const& lbl : mMCLabels) { + if (nStored < 1) { + return; + } + totalChargeAllRing *= DP::INV_CHARGE_PER_ADC; + //LOG(INFO)<<"Total charge ADC " <<totalChargeAllRing ; + ///Triggers for FV0 + bool isMinBias, isMinBiasInner, isMinBiasOuter, isHighMult, isDummy; + isMinBias = nStored > 0; + isMinBiasInner = nSignalInner > 0; //ring 1,2 and 3 + isMinBiasOuter = nSignalOuter > 0; //ring 4 and 5 + isHighMult = totalChargeAllRing > FV0DigParam::Instance().adcChargeHighMultTh; + isDummy = false; + + Triggers triggers; + triggers.setTriggers(isMinBias, isMinBiasInner, isMinBiasOuter, isHighMult, isDummy, nStored, totalChargeAllRing); + digitsBC.emplace_back(first, nStored, bc, triggers); + digitsTrig.emplace_back(bc, isMinBias, isMinBiasInner, isMinBiasOuter, isHighMult, isDummy); + int nBC = digitsBC.size(); + for (const auto& lbl : bc.labels) { labels.addElement(nBC, lbl); } - mMCLabels.clear(); } // ------------------------------------------------------------------------------- @@ -228,19 +267,38 @@ Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot) const } const Int_t n = Int_t(nPhot < 100 ? gRandom->Binomial(nPhot, p) - : gRandom->Gaus(p * nPhot + 0.5, TMath::Sqrt(p * (1 - p) * nPhot))); + : gRandom->Gaus((p * nPhot) + 0.5, TMath::Sqrt(p * (1. - p) * nPhot))); return n; } - -Float_t Digitizer::SimulateTimeCfd(Int_t channel) const +//--------------------------------------------------------------------------- +Float_t Digitizer::IntegrateCharge(const ChannelBCDataF& pulse) const +{ + int chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : FV0DigParam::Instance().chargeIntBinMin; + int chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : FV0DigParam::Instance().chargeIntBinMax; + + Float_t totalCharge = 0.0f; + for (int iTimeBin = chargeIntMin; iTimeBin < chargeIntMax; iTimeBin++) { + Float_t const timeBinCharge = pulse[iTimeBin]; + //LOG(INFO)<<iTimeBin*0.013<<" "<<timeBinCharge; + totalCharge += timeBinCharge; + } + return totalCharge; +} +//--------------------------------------------------------------------------- +Float_t Digitizer::SimulateTimeCfd(/*Int_t channel, */ const ChannelBCDataF& pulse) const { Float_t timeCfd = -1024.0f; + + //auto& bc= mCache[iCache]; + + if (pulse.empty()) { + return timeCfd; + } + Int_t const binShift = TMath::Nint(FV0DigParam::Instance().timeShiftCfd / mBinSize); - Float_t sigPrev = -mPmtChargeVsTime[channel][0]; - for (Int_t iTimeBin = 1; iTimeBin < mNBins; ++iTimeBin) { - Float_t const sigCurrent = (iTimeBin >= binShift - ? 5.0f * mPmtChargeVsTime[channel][iTimeBin - binShift] - mPmtChargeVsTime[channel][iTimeBin] - : -mPmtChargeVsTime[channel][iTimeBin]); + Float_t sigPrev = -pulse[0]; //[0]; + for (Int_t iTimeBin = 1; iTimeBin < mNTimeBinsPerBC; ++iTimeBin) { + Float_t const sigCurrent = (iTimeBin >= binShift ? 5.0f * pulse[iTimeBin - binShift] - pulse[iTimeBin] : -pulse[iTimeBin]); if (sigPrev < 0.0f && sigCurrent >= 0.0f) { timeCfd = Float_t(iTimeBin) * mBinSize; break; @@ -250,47 +308,18 @@ Float_t Digitizer::SimulateTimeCfd(Int_t channel) const return timeCfd; } -Double_t Digitizer::PmtResponse(Double_t* x, Double_t*) -{ - return Digitizer::PmtResponse(x[0]); -} -//_______________________________________________________________________ -Double_t Digitizer::PmtResponse(Double_t x) -{ - // this function describes the PMT time response to a single photoelectron - if (x > 2 * FV0DigParam::Instance().pmtTransitTime) - return 0.0; - if (x < -FV0DigParam::Instance().pmtTransitTime) - return 0.0; - x += FV0DigParam::Instance().pmtTransitTime; - Double_t const x2 = x * x; - return x2 * std::exp(-x2 * FV0DigParam::Instance().oneOverPmtTransitTime2); -} - -Double_t Digitizer::SinglePhESpectrum(Double_t* x, Double_t*) -{ - // x -- number of photo-electrons emitted from the first dynode - // this function describes the PMT amplitude response to a single photoelectron - if (x[0] < 0.0) - return 0.0; - return (TMath::Poisson(x[0], FV0DigParam::Instance().pmtNbOfSecElec) + - FV0DigParam::Instance().pmtTransparency * TMath::Poisson(x[0], 1.0)); -} - -// The Distance is positive for top half-sectors (when the hit position is above the cell center (has higher y)) -// TODO: performance check needed float Digitizer::getDistFromCellCenter(UInt_t cellId, double hitx, double hity) { Geometry* geo = Geometry::instance(); // Parametrize the line (ax+by+c=0) that crosses the detector center and the cell's middle point - Point3D<float>* pCell = &geo->getCellCenter(cellId); + Point3Dsimple* pCell = &geo->getCellCenter(cellId); float x0, y0, z0; geo->getGlobalPosition(x0, y0, z0); - double a = -(y0 - pCell->Y()) / (x0 - pCell->X()); + 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); } @@ -303,3 +332,43 @@ float Digitizer::getSignalFraction(float distanceFromXc, bool isFirstChannel) return isFirstChannel ? (1. - fraction) : fraction; } } + +//_____________________________________________________________________________ +o2::fv0::Digitizer::BCCache& Digitizer::setBCCache(const o2::InteractionRecord& ir) +{ + if (mCache.empty() || mCache.back() < ir) { + mCache.emplace_back(); + auto& cb = mCache.back(); + cb = ir; + return cb; + } + if (mCache.front() > ir) { + mCache.emplace_front(); + auto& cb = mCache.front(); + cb = ir; + return cb; + } + + for (auto cb = mCache.begin(); cb != mCache.end(); cb++) { + if ((*cb) == ir) { + return *cb; + } + if (ir < (*cb)) { + auto cbnew = mCache.emplace(cb); // insert new element before cb + (*cbnew) = ir; + return (*cbnew); + } + } + return mCache.front(); +} +//_____________________________________________________________________________ +o2::fv0::Digitizer::BCCache* Digitizer::getBCCache(const o2::InteractionRecord& ir) +{ + // get pointer on existing cache + for (auto cb = mCache.begin(); cb != mCache.end(); cb++) { + if ((*cb) == ir) { + return &(*cb); + } + } + return nullptr; +} diff --git a/Detectors/FIT/FV0/simulation/src/Digits2Raw.cxx b/Detectors/FIT/FV0/simulation/src/Digits2Raw.cxx new file mode 100644 index 0000000000000..0e2424b6729dd --- /dev/null +++ b/Detectors/FIT/FV0/simulation/src/Digits2Raw.cxx @@ -0,0 +1,173 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// RAW data format - DataFormat/Detectors/FIT/FT0/RawEventData + +#include "FV0Base/Constants.h" +#include "FV0Simulation/Digits2Raw.h" +#include <TTree.h> +#include <cassert> + +using namespace o2::fv0; + +ClassImp(Digits2Raw); + +void Digits2Raw::readDigits(const std::string& outDir, const std::string& fileDigitsName) +{ + LOG(INFO) << "==============FV0: Digits2Raw::convertDigits" << std::endl; + LookUpTable lut(true); + + std::string outd = outDir; + if (outd.back() != '/') { + outd += '/'; + } + + // Register PM links linearly + for (int iPmLink = 0; iPmLink < Constants::nPms; ++iPmLink) { + uint64_t feeId = uint64_t(iPmLink); + uint32_t linkId = uint32_t(iPmLink); + std::string outFileLink = mOutputPerLink ? (outd + "fv0_link" + std::to_string(iPmLink) + ".raw") : (outd + "fv0.raw"); + LOG(INFO) << " Register PM link: " << iPmLink << " to file: " << outFileLink; + mWriter.registerLink(feeId, sCruId, linkId, sEndPointId, outFileLink); + } + + // Register TCM link separately + std::string outFileLink = mOutputPerLink ? (outd + "fv0_link" + std::to_string(sTcmLink) + ".raw") : (outd + "fv0.raw"); + LOG(INFO) << " Register TCM link: " << outFileLink; + mWriter.registerLink(uint64_t(sTcmLink), sCruId, sTcmLink, sEndPointId, outFileLink); + + TFile* fdig = TFile::Open(fileDigitsName.data()); + assert(fdig != nullptr); + LOG(INFO) << "Open digits file: " << fileDigitsName.data(); + + TTree* digTree = (TTree*)fdig->Get("o2sim"); + std::vector<o2::fv0::BCData> digitsBC, *fv0BCDataPtr = &digitsBC; + std::vector<o2::fv0::ChannelData> digitsCh, *fv0ChDataPtr = &digitsCh; + digTree->SetBranchAddress("FV0DigitBC", &fv0BCDataPtr); + digTree->SetBranchAddress("FV0DigitCh", &fv0ChDataPtr); + + for (int ient = 0; ient < digTree->GetEntries(); ient++) { + digTree->GetEntry(ient); + int nbc = digitsBC.size(); + for (int ibc = 0; ibc < nbc; ibc++) { + auto& bcd = digitsBC[ibc]; + auto channels = bcd.getBunchChannelData(digitsCh); + + if (!channels.empty()) { + LOG(DEBUG); + LOG(INFO) << "o2::fv0::Digits2Raw::readDigits(): Start to convertDigits() at ibc = " << ibc << " " << bcd.ir + << " iCh0:" << bcd.ref.getFirstEntry() << " nentries:" << bcd.ref.getEntries(); + convertDigits(bcd, channels, lut); + } + } + } +} + +void Digits2Raw::convertDigits(o2::fv0::BCData bcdigits, gsl::span<const ChannelData> pmchannels, + const o2::fv0::LookUpTable& lut) +{ + const o2::InteractionRecord intRecord = bcdigits.getIntRecord(); + int prevPmLink = -1; + int iChannelPerLink = 0; + int nch = pmchannels.size(); + + std::stringstream ss; + ss << " Number of channels: " << nch << " (Ch, PMT, Q, T)\n"; + for (int ich = 0; ich < nch; ich++) { + if (pmchannels[ich].chargeAdc != 0) { + ss << " " << std::setw(2) << ich + << std::setw(3) << pmchannels[ich].pmtNumber + << std::setw(5) << pmchannels[ich].chargeAdc + << std::setw(7) << std::setprecision(3) << pmchannels[ich].time << "\n"; + } + } + LOG(DEBUG) << ss.str().substr(0, ss.str().size() - 1); + + for (int ich = 0; ich < nch; ich++) { + int nLinkPm = lut.getLink(pmchannels[ich].pmtNumber); + if (nLinkPm != prevPmLink) { + if (prevPmLink >= 0) { + fillSecondHalfWordAndAddData(iChannelPerLink, prevPmLink, intRecord); + } + makeGBTHeader(mRawEventData.mEventHeader, nLinkPm, intRecord); + iChannelPerLink = 0; + prevPmLink = nLinkPm; + } + if (pmchannels[ich].chargeAdc != 0) { + LOG(DEBUG) << " Store data for channel: " << ich << " PmLink = " << nLinkPm << " "; + auto& newData = mRawEventData.mEventData[iChannelPerLink]; + newData.charge = pmchannels[ich].chargeAdc; + newData.time = pmchannels[ich].time; + newData.generateFlags(); + newData.channelID = lut.getPmChannel(pmchannels[ich].pmtNumber); + iChannelPerLink++; + } + if (ich == nch - 1) { + fillSecondHalfWordAndAddData(iChannelPerLink, prevPmLink, intRecord); + } + } + + // TCM + makeGBTHeader(mRawEventData.mEventHeader, sTcmLink, intRecord); + mRawEventData.mEventHeader.nGBTWords = 1; + auto& tcmdata = mRawEventData.mTCMdata; + tcmdata.vertex = 1; + tcmdata.orA = 1; + tcmdata.orC = 0; + tcmdata.sCen = 0; + tcmdata.cen = 0; + tcmdata.nChanA = 0; + tcmdata.nChanC = 0; + tcmdata.amplA = 0; + tcmdata.amplC = 0; + tcmdata.timeA = 0; + tcmdata.timeC = 0; + + auto data = mRawEventData.to_vector(kTRUE); //for tcm module + uint32_t linkId = uint32_t(sTcmLink); + uint64_t feeId = uint64_t(sTcmLink); + mWriter.addData(feeId, sCruId, linkId, sEndPointId, intRecord, data); + + // fill mEventData[iChannelPerLink] with 0s to flag that this is a dummy data + uint nGBTWords = uint((iChannelPerLink + 1) / 2); + if ((iChannelPerLink % 2) == 1) { + mRawEventData.mEventData[iChannelPerLink] = {}; + } + mRawEventData.mEventHeader.nGBTWords = nGBTWords; + // LOG(DEBUG) << " last link: " << prevPmLink; +} + +//_____________________________________________________________________________________ +void Digits2Raw::makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord) +{ + eventHeader.startDescriptor = 0xf; + eventHeader.reservedField1 = 0; + eventHeader.reservedField2 = 0; + eventHeader.reservedField3 = 0; + eventHeader.bc = mIntRecord.bc; + eventHeader.orbit = mIntRecord.orbit; + LOG(DEBUG) << " makeGBTHeader for link: " << link; +} + +void Digits2Raw::fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir) +{ + uint nGBTWords = uint((iChannelPerLink + 1) / 2); + if ((iChannelPerLink % 2) == 1) { + mRawEventData.mEventData[iChannelPerLink] = {}; + LOG(DEBUG) << " Fill up empty second half-word."; + } + mRawEventData.mEventHeader.nGBTWords = nGBTWords; + auto data = mRawEventData.to_vector(false); + uint32_t linkId = uint32_t(prevPmLink); + uint64_t feeId = uint64_t(prevPmLink); + mWriter.addData(feeId, sCruId, linkId, sEndPointId, ir, data); + LOG(DEBUG) << " Switch prevPmLink: " << prevPmLink << ". Save data with nGBTWords=" + << nGBTWords << " in header. Last channel: " << iChannelPerLink; +} diff --git a/Detectors/FIT/FV0/simulation/src/FV0SimulationLinkDef.h b/Detectors/FIT/FV0/simulation/src/FV0SimulationLinkDef.h index d12928d1f584e..96ff6069e78ba 100644 --- a/Detectors/FIT/FV0/simulation/src/FV0SimulationLinkDef.h +++ b/Detectors/FIT/FV0/simulation/src/FV0SimulationLinkDef.h @@ -17,10 +17,10 @@ #pragma link C++ class o2::fv0::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::fv0::Detector> + ; #pragma link C++ class o2::fv0::Digitizer + ; -#pragma link C++ class o2::fv0::MCLabel + ; #pragma link C++ class o2::dataformats::MCTruthContainer < o2::fv0::MCLabel> + ; #pragma link C++ class o2::fv0::DigitizationConstant + ; #pragma link C++ class o2::fv0::FV0DigParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::fv0::FV0DigParam> + ; +#pragma link C++ class o2::fv0::Digits2Raw + ; #endif diff --git a/Detectors/FIT/FV0/simulation/src/digit2raw.cxx b/Detectors/FIT/FV0/simulation/src/digit2raw.cxx new file mode 100644 index 0000000000000..00d615a5ec5f0 --- /dev/null +++ b/Detectors/FIT/FV0/simulation/src/digit2raw.cxx @@ -0,0 +1,114 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 digi2raw.cxx +/// \author ruben.shahoyan@cern.ch + +#include <boost/program_options.hpp> +#include <TSystem.h> +#include <TFile.h> +#include <TStopwatch.h> +#include <string> +#include <iomanip> +#include "Framework/Logger.h" +#include "FairLogger.h" +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "DetectorsRaw/HBFUtils.h" +#include "FV0Simulation/Digits2Raw.h" + +/// MC->raw conversion for FV0 + +namespace bpo = boost::program_options; + +void digi2raw(const std::string& inpName, const std::string& outDir, bool filePerLink, uint32_t rdhV = 6, bool noEmptyHBF = false, + int superPageSizeInB = 1024 * 1024); + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " (Convert FV0 digits to CRU raw 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("input-file,i", bpo::value<std::string>()->default_value(o2::base::NameConf::getDigitsFileName(o2::detectors::DetID::FV0)),"input FV0 digits file"); // why not used? + add_option("input-file,i", bpo::value<std::string>()->default_value("fv0digits.root"), "input FV0 digits file"); + add_option("file-per-link,l", bpo::value<bool>()->default_value(false)->implicit_value(true), "create output file per CRU (default: per layer)"); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + uint32_t defRDH = o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>(); + add_option("rdh-version,r", bpo::value<uint32_t>()->default_value(defRDH), "RDH version to use"); + add_option("no-empty-hbf,e", bpo::value<bool>()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("configKeyValues", bpo::value<std::string>()->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<std::string>()); + digi2raw(vm["input-file"].as<std::string>(), + vm["output-dir"].as<std::string>(), + vm["file-per-link"].as<bool>(), + vm["rdh-version"].as<uint32_t>(), + vm["no-empty-hbf"].as<bool>()); + + return 0; +} + +void digi2raw(const std::string& inpName, const std::string& outDir, bool filePerLink, uint32_t rdhV, bool noEmptyHBF, int superPageSizeInB) +{ + TStopwatch swTot; + swTot.Start(); + o2::fv0::Digits2Raw m2r; + m2r.setFilePerLink(filePerLink); + auto& wr = m2r.getWriter(); + wr.setSuperPageSize(superPageSizeInB); + wr.useRDHVersion(rdhV); + wr.setDontFillEmptyHBF(noEmptyHBF); + + std::string outDirName(outDir); + if (outDirName.back() != '/') { + outDirName += '/'; + } + // if needed, create output directory + if (gSystem->AccessPathName(outDirName.c_str())) { + if (gSystem->mkdir(outDirName.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outDirName; + } else { + LOG(INFO) << "created output directory " << outDirName; + } + } + + m2r.readDigits(outDirName, inpName); + wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); + //LOG(INFO)<<o2::utils::concat_string(outDirName, wr.getOrigin().str)<<"\n"; + // + swTot.Stop(); + swTot.Print(); +} diff --git a/Detectors/FIT/FV0/workflow/CMakeLists.txt b/Detectors/FIT/FV0/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..6166157112792 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/CMakeLists.txt @@ -0,0 +1,33 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(FV0Workflow + SOURCES src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx + src/DigitReaderSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsFV0 + O2::FV0Reconstruction + O2::DetectorsCommonDataFormats + O2::DPLUtils) + +o2_add_executable(entropy-encoder-workflow + SOURCES src/entropy-encoder-workflow.cxx + COMPONENT_NAME fv0 + PUBLIC_LINK_LIBRARIES O2::FV0Workflow) + +o2_add_executable(digit-reader-workflow + SOURCES src/digits-reader-workflow.cxx + COMPONENT_NAME fv0 + PUBLIC_LINK_LIBRARIES O2::FV0Workflow) + +if(NOT APPLE) + set_property(TARGET ${fitrecoexe} PROPERTY LINK_WHAT_YOU_USE ON) +endif() diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/DigitReaderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..7a71ae588a030 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/DigitReaderSpec.h @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_FV0_DIGITREADERSPEC_H +#define O2_FV0_DIGITREADERSPEC_H + +#include "TFile.h" +#include "TTree.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" + +namespace o2 +{ +namespace fv0 +{ + +class DigitReader : public framework::Task +{ + public: + DigitReader(bool useMC) : mUseMC(useMC) {} + ~DigitReader() override = default; + void init(framework::InitContext& ic) final; + void run(framework::ProcessingContext& pc) final; + + private: + bool mUseMC = true; + std::unique_ptr<TTree> mTree; + std::unique_ptr<TFile> mFile; +}; + +/// create a processor spec +/// read simulated FV0 digits from a root file +framework::DataProcessorSpec getDigitReaderSpec(bool useMC); + +} // end namespace fv0 +} // end namespace o2 + +#endif // O2_FV0_DIGITREADERSPEC_H diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..744ad441effca --- /dev/null +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to FV0 digit/channels strean + +#ifndef O2_FV0_ENTROPYDECODER_SPEC +#define O2_FV0_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "FV0Reconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace fv0 +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::fv0::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace fv0 +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..0b38260cad14f --- /dev/null +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert clusters streams to CTF (EncodedBlocks) + +#ifndef O2_FV0_ENTROPYENCODER_SPEC +#define O2_FV0_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "FV0Reconstruction/CTFCoder.h" + +namespace o2 +{ +namespace fv0 +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::fv0::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace fv0 +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..781950909edc1 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,92 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> + +#include "TTree.h" + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "FV0Workflow/DigitReaderSpec.h" +#include "DataFormatsFV0/BCData.h" +#include "DataFormatsFV0/ChannelData.h" +#include "DataFormatsFV0/MCLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace fv0 +{ + +void DigitReader::init(InitContext& ic) +{ + auto filename = ic.options().get<std::string>("fv0-digit-infile"); + mFile = std::make_unique<TFile>(filename.c_str(), "OLD"); + if (!mFile->IsOpen()) { + LOG(ERROR) << "Cannot open the " << filename.c_str() << " file !"; + throw std::runtime_error("cannot open input digits file"); + } + mTree.reset((TTree*)mFile->Get("o2sim")); + if (!mTree) { + LOG(ERROR) << "Did not find o2sim tree in " << filename.c_str(); + throw std::runtime_error("Did not fine o2sim file in FV0 digits tree"); + } +} + +void DigitReader::run(ProcessingContext& pc) +{ + + std::vector<o2::fv0::BCData> digits, *pdigits = &digits; + std::vector<o2::fv0::ChannelData> channels, *pchannels = &channels; + mTree->SetBranchAddress("FV0DigitBC", &pdigits); + mTree->SetBranchAddress("FV0DigitCh", &pchannels); + + o2::dataformats::MCTruthContainer<o2::fv0::MCLabel> labels, *plabels = &labels; + if (mUseMC) { + mTree->SetBranchAddress("FV0DigitLabels", &plabels); + } + mTree->GetEntry(0); + + LOG(INFO) << "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); + if (mUseMC) { + pc.outputs().snapshot(Output{"FV0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + } + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); +} + +DataProcessorSpec getDigitReaderSpec(bool useMC) +{ + std::vector<OutputSpec> outputs; + outputs.emplace_back("FV0", "DIGITSBC", 0, Lifetime::Timeframe); + outputs.emplace_back("FV0", "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("FV0", "DIGITSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "fv0-digit-reader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<DigitReader>(useMC)}, + Options{ + {"fv0-digit-infile", VariantType::String, "fv0digits.root", {"Name of the input file"}}}}; +} + +} // namespace fv0 +} // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..6f5cb5b0b3a06 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "FV0Workflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace fv0 +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("fv0-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& digits = pc.outputs().make<std::vector<o2::fv0::BCData>>(OutputRef{"digits"}); + auto& channels = pc.outputs().make<std::vector<o2::fv0::ChannelData>>(OutputRef{"channels"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::fv0::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, digits, channels); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << channels.size() << " FV0 channels in " << digits.size() << " digits in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FV0 Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"digits"}, "FV0", "DIGITSBC", 0, Lifetime::Timeframe}, + OutputSpec{{"channels"}, "FV0", "DIGITSCH", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "fv0-entropy-decoder", + Inputs{InputSpec{"ctf", "FV0", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"fv0-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace fv0 +} // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..ff8f7bd5f6c5a --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,78 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "FV0Workflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace fv0 +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("fv0-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto digits = pc.inputs().get<gsl::span<o2::fv0::BCData>>("digits"); + auto channels = pc.inputs().get<gsl::span<o2::fv0::ChannelData>>("channels"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"FV0", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, digits, channels); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for FV0 in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "FV0 Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("digits", "FV0", "DIGITSBC", 0, Lifetime::Timeframe); + inputs.emplace_back("channels", "FV0", "DIGITSCH", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "fv0-entropy-encoder", + inputs, + Outputs{{"FV0", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"fv0-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace fv0 +} // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/digits-reader-workflow.cxx b/Detectors/FIT/FV0/workflow/src/digits-reader-workflow.cxx new file mode 100644 index 0000000000000..a833355c2bb9b --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/digits-reader-workflow.cxx @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 digits-reader-workflow.cxx +/// \brief Implementation of FV0 digits reader +/// +/// \author ruben.shahoyan@cern.ch + +#include "Framework/CallbackService.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "FV0Workflow/DigitReaderSpec.h" +#include "CommonUtils/ConfigurableParam.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> 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::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + WorkflowSpec specs; + + DataProcessorSpec producer = o2::fv0::getDigitReaderSpec(ctx.options().get<bool>("disable-mc")); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..58455959b0854 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FV0Workflow/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::fv0::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/FIT/README.md b/Detectors/FIT/README.md index 2613f7f311154..49ca9ba65e85b 100644 --- a/Detectors/FIT/README.md +++ b/Detectors/FIT/README.md @@ -8,4 +8,6 @@ This is a top page for the FIT detector documentation. <!-- doxy * \subpage refFITbenchmark +* \subpage refFITFT0workflow +* \subpage refFITraw /doxy --> diff --git a/Detectors/FIT/macros/CMakeLists.txt b/Detectors/FIT/macros/CMakeLists.txt index 8dd4aa662d117..7912e34aae684 100644 --- a/Detectors/FIT/macros/CMakeLists.txt +++ b/Detectors/FIT/macros/CMakeLists.txt @@ -14,6 +14,9 @@ o2_add_test_root_macro(readFT0hits.C o2_add_test_root_macro(readFT0digits.C PUBLIC_LINK_LIBRARIES O2::DataFormatsFT0 LABELS fit) +o2_add_test_root_macro(uploadLookUpTable.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsFT0 + LABELS fit) o2_add_test_root_macro(FT0digits2raw.C PUBLIC_LINK_LIBRARIES O2::FT0Simulation O2::DataFormatsFT0 diff --git a/Detectors/FIT/macros/uploadLookUpTable.C b/Detectors/FIT/macros/uploadLookUpTable.C new file mode 100644 index 0000000000000..b00dc587532a4 --- /dev/null +++ b/Detectors/FIT/macros/uploadLookUpTable.C @@ -0,0 +1,71 @@ +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CcdbApi.h" +#include <DataFormatsFT0/LookUpTable.h> +#include <cstdlib> +#include <vector> +#include <fstream> +#include <iostream> +#include <map> +#include <string_view> + +o2::ft0::HVchannel::HVBoard readHVBoard(std::string_view str); +o2::ft0::Topo read_Topo(std::string_view str); + +void uploadLookUpTable() +{ + using o2::ccdb::BasicCCDBManager; + using o2::ccdb::CcdbApi; + + // std::ifstream f{filename.data()}; + std::ifstream f{"FT0ChannelsTable.txt"}; + int channel; + std::string pm, pm_channel, hv_board, hv_channel, mcp_sn, hv_cable, signal_cable; + std::vector<o2::ft0::HVchannel> table; + std::getline(f, pm); // skip one line + while (f >> channel >> pm >> pm_channel >> hv_board >> hv_channel >> mcp_sn >> hv_cable >> signal_cable) { + o2::ft0::HVchannel chan; + chan.channel = channel; + assert(std::string_view(pm_channel).substr(2, 2) == pm); + chan.pm = read_Topo(pm_channel); + chan.HV_board = readHVBoard(hv_board); + chan.MCP_SN = mcp_sn; + chan.HV_cabel = hv_cable; + chan.signal_cable = signal_cable; + table.emplace_back(chan); + } + CcdbApi api; + std::map<std::string, std::string> 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 + api.storeAsTFileAny(&table, "FT0/LookUpTable", metadata); +} + +o2::ft0::HVchannel::HVBoard readHVBoard(std::string_view str) +{ + using HVBoard = o2::ft0::HVchannel::HVBoard; + if (str == "N/A") + return HVBoard::NA; + else if (str == "A-Out") + return HVBoard::A_out; + else if (str == "A-In") + return HVBoard::A_in; + else if (str == "C-Up") + return HVBoard::C_up; + else if (str == "C-Down") + return HVBoard::C_down; + else if (str == "C-Mid") + return HVBoard::C_mid; + else { + std::cerr << "Unknown HVBoard " << str << "\n"; + std::abort(); + } +} +o2::ft0::Topo read_Topo(std::string_view str) +{ + assert(str.substr(0, 2) == "PM" && str[4] == '/' && str[5] == 'C' && str[6] == 'h'); + char side = str[2]; + uint8_t pm_num = str[3] - '0'; + uint8_t pm_ch = (str[7] - '0') * 10 + (str[8] - '0'); + assert(side == 'A' || side == 'C'); + return {(side == 'C' ? 10 : 0) + pm_num, pm_ch}; +} diff --git a/Detectors/FIT/raw/CMakeLists.txt b/Detectors/FIT/raw/CMakeLists.txt new file mode 100644 index 0000000000000..9fa77d178fedb --- /dev/null +++ b/Detectors/FIT/raw/CMakeLists.txt @@ -0,0 +1,13 @@ +#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". +# +#See http: //alice-o2.web.cern.ch/license for full licensing information. +# +#In applying this license CERN does 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(FITRaw + SOURCES src/DataBlockBase.cxx src/DigitBlockBase.cxx src/RawReaderBase.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers ms_gsl::ms_gsl) diff --git a/Detectors/FIT/raw/README.md b/Detectors/FIT/raw/README.md new file mode 100644 index 0000000000000..723ce05581370 --- /dev/null +++ b/Detectors/FIT/raw/README.md @@ -0,0 +1,46 @@ +<!-- doxy +\page refFITraw FIT raw +/doxy --> + +# DataBlockBase +DataBlockWrapper - wrapper for raw data structures\ +There should be three static fields in raw data structs, which defines its "signature":\ + payloadSize - actual payload size per one raw data struct element (can be larger than GBTword size!)\ + payloadPerGBTword - maximum payload per one GBT word\ + MaxNelements - maximum number of elements per data block(for header it should be equal to 1)\ + MinNelements - minimum number of elements per data block(for header it should be equal to 1)\ + +Also it requares several methods:\ + print() - for printing raw data structs\ + getIntRec() - for InteractionRecord extraction, should be in Header struct\ + +DataBlockBase - base class for making composition of raw data structures, uses CRTP(static polyporphism)\ +usage:\ + class DataBlockOfYourModule: public DataBlockBase< DataBlockOfYourModule, RawHeaderStruct, RawDataStruct ...>\ + define "deserialization" method with deserialization logic for current DataBlock\ + define "sanityCheck" method for checking if the DataBlock is correct\ +Warning! Classes should be simple, without refs and pointers!\ +Example: + /alice/O2/Detectors/FIT/FT0/raw/include/FT0Raw/DataBlockFT0.h +# DigitBlockBase +Base class for digit block, which containes "digit" structures\ +usage:\ + class DigitBlockYourDetector : public DigitBlockBase<DigitBlockYourDetector>\ + define "template <class DataBlockType> void processDigits(DataBlockType& dataBlock, int linkID)" with Raw2Digit conversion logic\ + define "template <class DigitType,...>void getDigits(std::vector<DigitType>&... vecDigits) for pushing digits into vectors which will be pushed through DPL channels, + variadic arguments are applied\ +Example:\ + /alice/O2/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h + +# RawReaderBase +Base class for raw reader.\ +usage:\ + class RawReaderFT0Base : public RawReaderBase<DigitBlockYourDetector>\ + just provide statically which linkID corresponds to DataBlockOfYourModule\ +Example:\ + /alice/O2/Detectors/FIT/FT0/raw/include/FT0Raw/RawReaderFT0Base.h\ + +# RawReader +Stores vectors with digit classes and manages them at DPL level\ +Example: \ + /alice/O2/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0 \ No newline at end of file diff --git a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h new file mode 100644 index 0000000000000..c200070b6c258 --- /dev/null +++ b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h @@ -0,0 +1,362 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DataBlockBase.h base class for RAW data format data blocks +// +// Artur.Furs +// afurs@cern.ch +//DataBlockWrapper - wrapper for raw data structures +//There should be three static fields in raw data structs, which defines its "signature": +// payloadSize - actual payload size per one raw data struct element (can be larger than GBTword size!) +// payloadPerGBTword - maximum payload per one GBT word +// MaxNelements - maximum number of elements per data block(for header it should be equal to 1) +// MinNelements - minimum number of elements per data block(for header it should be equal to 1) + +//Also it requares several methods: +// print() - for printing raw data structs +// getIntRec() - for InteractionRecord extraction, should be in Header struct + +//DataBlockBase - base class for making composition of raw data structures, uses CRTP(static polyporphism) +//usage: +// class DataBlockOfYourModule: public DataBlockBase< DataBlockOfYourModule, RawHeaderStruct, RawDataStruct ...> +// define "deserialization" method with deserialization logic for current DataBlock +// define "sanityCheck" method for checking if the DataBlock is correct +//Warning! Classes should be simple, without refs and pointers! +//TODO: +// need to use references on the DataBlock fileds, with fast access +// traites for classes and structs +// + +#ifndef ALICEO2_FIT_DATABLOCKBASE_H_ +#define ALICEO2_FIT_DATABLOCKBASE_H_ +#include <iostream> +#include <vector> +#include <Rtypes.h> +#include "CommonDataFormat/InteractionRecord.h" +#include <gsl/span> +#include <boost/mpl/inherit.hpp> +#include <boost/mpl/vector.hpp> +#include <Framework/Logger.h> +#include <vector> +#include <tuple> +#include <array> +#include <iostream> +#include <cassert> + +namespace o2 +{ +namespace fit +{ + +using namespace std; + +template <typename T> +struct DataBlockWrapper { + DataBlockWrapper() = default; + DataBlockWrapper(const DataBlockWrapper&) = default; + static constexpr size_t sizeWord = 16; // should be changed to gloabal variable + std::vector<uint8_t> serialize(int nWords) + { + std::vector<uint8_t> vecBytes(sizeWord * nWords); + uint8_t* srcAddress = (uint8_t*)mData; + if (nWords == 0 || nWords > MaxNwords) { + return std::move(vecBytes); + } + gsl::span<uint8_t> serializedBytes(vecBytes); + size_t countBytes = 0; + int nSteps = std::get<kNSTEPS>(sReadingLookupTable[nWords]); + for (int iStep = 0; iStep < nSteps; iStep++) { + memcpy(serializedBytes.data() + std::get<kSRCBYTEPOS>(sByteLookupTable[iStep]), srcAddress + std::get<kDESTBYTEPOS>(sByteLookupTable[iStep]), std::get<kNBYTES>(sByteLookupTable[iStep])); + countBytes += std::get<kSRCBYTEPOS>(sByteLookupTable[iStep]); + } + return std::move(vecBytes); + } + + void deserialize(const gsl::span<const uint8_t> inputBytes, size_t nWords, size_t& srcPos) + { + mNelements = 0; + mNwords = 0; + if (nWords < MinNwords || nWords > MaxNwords || inputBytes.size() - srcPos < nWords * sizeWord) { + //in case of bad fields responsible for deserialization logic, byte position will be pushed to the end of binary sequence + srcPos = inputBytes.size(); + mIsIncorrect = true; + return; + } + uint8_t* destAddress = (uint8_t*)mData; + size_t countBytes = 0; + int nSteps = std::get<kNSTEPS>(sReadingLookupTable[nWords]); + mNwords = nWords; + mNelements = std::get<kNELEMENTS>(sReadingLookupTable[nWords]); + for (int iStep = 0; iStep < nSteps; iStep++) { + memcpy(destAddress + std::get<kDESTBYTEPOS>(sByteLookupTable[iStep]), inputBytes.data() + std::get<kSRCBYTEPOS>(sByteLookupTable[iStep]) + srcPos, std::get<kNBYTES>(sByteLookupTable[iStep])); + countBytes += std::get<kSRCBYTEPOS>(sByteLookupTable[iStep]); + } + srcPos += mNwords * sizeWord; + } + + static constexpr int MaxNwords = T::PayloadSize * T::MaxNelements / T::PayloadPerGBTword + (T::PayloadSize * T::MaxNelements % T::PayloadPerGBTword > 0); //calculating max GBT words per block + static constexpr int MaxNbytes = sizeWord * MaxNwords; + + static constexpr int MinNwords = T::PayloadSize * T::MinNelements / T::PayloadPerGBTword + (T::PayloadSize * T::MinNelements % T::PayloadPerGBTword > 0); //calculating min GBT words per block + static constexpr int MinNbytes = sizeWord * MinNwords; + + //get number of byte reading steps + static constexpr size_t getNsteps() + { + int count = 0; + size_t payloadFull = T::MaxNelements * T::PayloadSize; + size_t payloadInWord = T::PayloadPerGBTword; + size_t payloadPerElem = T::PayloadSize; + while (payloadFull > 0) { + if (payloadPerElem < payloadInWord) { + count++; + payloadFull -= payloadPerElem; + payloadInWord -= payloadPerElem; + payloadPerElem = 0; + } else { + count++; + payloadFull -= payloadInWord; + payloadPerElem -= payloadInWord; + payloadInWord = 0; + } + if (payloadInWord == 0) { + payloadInWord = T::PayloadPerGBTword; + } + if (payloadPerElem == 0) { + payloadPerElem = T::PayloadSize; + } + } + return count; + } + //enumerator for tuple access: + //[Index] is index of step to read bytes + //kNBYTES - number of bytes to read from source and to write into destination + //kSRCBYTEPOS - Byte position in the source(binary raw data sequence) + //kDESTBYTEPOS - Byte position to write at destionation(memory allocation of T-element array) + //kELEMENTINDEX - element index at current step + //kWORDINDEX - word index at current step + // + enum AccessByteLUT { kNBYTES, + kSRCBYTEPOS, + kDESTBYTEPOS, + kELEMENTINDEX, + kWORDINDEX }; + static constexpr std::array<std::tuple<size_t, size_t, size_t, int, int>, getNsteps()> GetByteLookupTable() + { + std::array<std::tuple<size_t, size_t, size_t, int, int>, getNsteps()> seqBytes{}; + int count = 0; + int countElement = 0; + int countWord = 0; + size_t destBytePosPerElem = 0; + size_t srcBytePos = 0; + size_t payloadFull = T::MaxNelements * T::PayloadSize; + + size_t bytesInWord = sizeWord; + size_t payloadInWord = T::PayloadPerGBTword; + + size_t payloadPerElem = T::PayloadSize; + + uint64_t indexElem = 0; + uint64_t indexLastElem = T::MaxNelements - 1; + + while (payloadFull > 0) { + if (payloadPerElem < payloadInWord) { //new element + std::get<kNBYTES>(seqBytes[count]) = payloadPerElem; + std::get<kSRCBYTEPOS>(seqBytes[count]) = srcBytePos; + std::get<kDESTBYTEPOS>(seqBytes[count]) = destBytePosPerElem; + std::get<kELEMENTINDEX>(seqBytes[count]) = countElement; + std::get<kWORDINDEX>(seqBytes[count]) = countWord; + srcBytePos += payloadPerElem; + count++; + payloadFull -= payloadPerElem; + payloadInWord -= payloadPerElem; + bytesInWord -= payloadPerElem; + payloadPerElem = 0; + + } else { + std::get<kNBYTES>(seqBytes[count]) = payloadInWord; + std::get<kSRCBYTEPOS>(seqBytes[count]) = srcBytePos; + std::get<kDESTBYTEPOS>(seqBytes[count]) = destBytePosPerElem; + std::get<kELEMENTINDEX>(seqBytes[count]) = countElement; + std::get<kWORDINDEX>(seqBytes[count]) = countWord; + srcBytePos += bytesInWord; + count++; + destBytePosPerElem += payloadInWord; + + payloadFull -= payloadInWord; + payloadPerElem -= payloadInWord; + payloadInWord = 0; + bytesInWord = 0; + } + + if (payloadInWord == 0) { + payloadInWord = T::PayloadPerGBTword; + } + if (payloadPerElem == 0) { + payloadPerElem = T::PayloadSize; + countElement++; + destBytePosPerElem = countElement * sizeof(T); + } + if (bytesInWord == 0) { + bytesInWord = sizeWord; + countWord++; + } + } + return seqBytes; + } + static constexpr std::array<std::tuple<size_t, size_t, size_t, int, int>, getNsteps()> sByteLookupTable = GetByteLookupTable(); + + //enumerator for tuple access: + //[Index] is word index position, i.e. "Index" number of words will be deserialized + //kNELEMENTS - number of T elements will be fully deserialized in "Index+1" words + //kNSTEPS - number of steps for reading "Index" words + //kISPARTED - if one T-element is parted at current word,i.e. current word contains partially deserialized T element at the end of the word + enum AccessReadingLUT { kNELEMENTS, + kNSTEPS, + kISPARTED }; + static constexpr std::array<std::tuple<unsigned int, unsigned int, bool>, MaxNwords + 1> GetReadingLookupTable() + { + std::array<std::tuple<unsigned int, unsigned int, bool>, MaxNwords + 1> readingScheme{}; + size_t payloadPerElem = T::PayloadSize; + std::get<kNSTEPS>(readingScheme[0]) = 0; + std::get<kNELEMENTS>(readingScheme[0]) = 0; + std::get<kISPARTED>(readingScheme[0]) = false; + int countWord = 1; + for (int iStep = 0; iStep < getNsteps(); iStep++) { + if (countWord - 1 < std::get<kWORDINDEX>((GetByteLookupTable())[iStep])) { //new word + std::get<kNSTEPS>(readingScheme[countWord]) = iStep; + std::get<kNELEMENTS>(readingScheme[countWord]) = std::get<kELEMENTINDEX>((GetByteLookupTable())[iStep]); + if (payloadPerElem > 0) { + std::get<kISPARTED>(readingScheme[countWord]) = true; + } else { + std::get<kISPARTED>(readingScheme[countWord]) = false; + } + countWord++; + } + if (payloadPerElem == 0) { + payloadPerElem = T::PayloadSize; + } + payloadPerElem -= std::get<kNBYTES>((GetByteLookupTable())[iStep]); + } + //Last step checking + std::get<kNSTEPS>(readingScheme[countWord]) = getNsteps(); + if (payloadPerElem > 0) { + std::get<kISPARTED>(readingScheme[countWord]) = true; + std::get<kNELEMENTS>(readingScheme[countWord]) = std::get<kELEMENTINDEX>((GetByteLookupTable())[getNsteps() - 1]); + } else { + std::get<kISPARTED>(readingScheme[countWord]) = false; + std::get<kNELEMENTS>(readingScheme[countWord]) = std::get<kELEMENTINDEX>((GetByteLookupTable())[getNsteps() - 1]) + 1; + } + return readingScheme; + } + static constexpr std::array<std::tuple<unsigned int, unsigned int, bool>, MaxNwords + 1> sReadingLookupTable = GetReadingLookupTable(); + // + //Printing LookupTables + static void printLUT() + { + cout << endl + << "-------------------------------------------" << endl; + std::cout << "kNELEMENTS|kNSTEPS|kISPARTED" << std::endl; + for (int i = 0; i < MaxNwords + 1; i++) { + std::cout << std::endl + << std::get<kNELEMENTS>(sReadingLookupTable[i]) << "|" + << std::get<kNSTEPS>(sReadingLookupTable[i]) << "|" + << std::get<kISPARTED>(sReadingLookupTable[i]) << endl; + } + cout << endl + << "-------------------------------------------" << endl; + std::cout << "kELEMENTINDEX|kWORDINDEX|kNBYTES|kSRCBYTEPOS|kDESTBYTEPOS" << std::endl; + for (int i = 0; i < getNsteps(); i++) { + cout << endl + << std::get<kELEMENTINDEX>(sByteLookupTable[i]) << "|" + << std::get<kWORDINDEX>(sByteLookupTable[i]) << "|" + << std::get<kNBYTES>(sByteLookupTable[i]) << "|" + << std::get<kSRCBYTEPOS>(sByteLookupTable[i]) << "|" + << std::get<kDESTBYTEPOS>(sByteLookupTable[i]) << endl; + } + } + void print() const + { + assert(mNelements <= T::MaxNelements); + for (int i = 0; i < mNelements; i++) { + std::cout << "\nPringting element number: " << i << std::endl; + mData[i].print(); + } + } + T mData[T::MaxNelements]; + unsigned int mNelements; //number of deserialized elements; + unsigned int mNwords; //number of deserialized GBT words; //can be excluded + bool mIsIncorrect; +}; + +//CRTP(static polymorphism) + Composition over multiple inheritance(Header + multiple data structures) +template <class DataBlock, class Header, class... DataStructures> +class DataBlockBase : public boost::mpl::inherit<DataBlockWrapper<Header>, DataBlockWrapper<DataStructures>...>::type +{ + typedef boost::mpl::vector<DataStructures...> DataBlockTypes; + typedef DataBlockBase<DataBlock, Header, DataStructures...> TemplateHeader; + typedef typename boost::mpl::inherit<DataBlockWrapper<Header>, DataBlockWrapper<DataStructures>...>::type DataBlockDerivedBase; + + public: + DataBlockBase() = default; + DataBlockBase(const DataBlockBase&) = default; + + static void printLUT() + { + DataBlockWrapper<Header>::printLUT(); + (static_cast<void>(DataBlockWrapper<DataStructures>::printLUT()), ...); + } + + void print() const + { + LOG(INFO) << "HEADER"; + DataBlockWrapper<Header>::print(); + LOG(INFO) << "DATA"; + (static_cast<void>(DataBlockWrapper<DataStructures>::print()), ...); + } + + InteractionRecord getInteractionRecord() const + { + return DataBlockWrapper<Header>::mData[0].getIntRec(); + } + // + //use this for block decoding + void decodeBlock(gsl::span<const uint8_t> payload, size_t srcPos) + { + mSize = 0; + size_t bytePos = srcPos; + static_cast<DataBlock*>(this)->deserialize(payload, bytePos); + mSize = bytePos - srcPos; + //checking sanity and updating + update(); + } + + bool isCorrect() const { return mIsCorrect; } + + void update() + { + mIsCorrect = true; + checkDeserialization(mIsCorrect, DataBlockWrapper<Header>::mIsIncorrect); // checking deserialization status for header + (checkDeserialization(mIsCorrect, DataBlockWrapper<DataStructures>::mIsIncorrect), ...); // checking deserialization status for sub-block + static_cast<DataBlock*>(this)->sanityCheck(mIsCorrect); + } + + size_t mSize; //deserialized size + bool mIsCorrect; + + 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); } +}; + +} // namespace fit +} // namespace o2 +#endif diff --git a/Detectors/FIT/raw/include/FITRaw/DigitBlockBase.h b/Detectors/FIT/raw/include/FITRaw/DigitBlockBase.h new file mode 100644 index 0000000000000..5fd2d2738c8d8 --- /dev/null +++ b/Detectors/FIT/raw/include/FITRaw/DigitBlockBase.h @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DigitBlockBase.h base class for processing RAW data into Digits +// +// Artur.Furs +// afurs@cern.ch + +#ifndef ALICEO2_FIT_DIGITBLOCKBASE_H_ +#define ALICEO2_FIT_DIGITBLOCKBASE_H_ +#include <iostream> +#include <vector> +#include <algorithm> +#include <Rtypes.h> +#include <CommonDataFormat/InteractionRecord.h> + +#include <gsl/span> +namespace o2 +{ +namespace fit +{ +template <class DigitBlock> +class DigitBlockBase //:public DigitBlock +{ + public: + DigitBlockBase(o2::InteractionRecord intRec) + { /*static_cast<DigitBlock*>(this)->setIntRec(intRec);*/ + } + DigitBlockBase() = default; + DigitBlockBase(const DigitBlockBase& other) = default; + ~DigitBlockBase() = default; + template <class DataBlockType> + void process(DataBlockType& dataBlock, int linkID) + { + static_cast<DigitBlock*>(this)->processDigits(dataBlock, linkID); + } + template <class... DigitType> + void pop(std::vector<DigitType>&... vecDigits) + { + static_cast<DigitBlock*>(this)->getDigits(vecDigits...); + } +}; + +} // namespace fit +} // namespace o2 +#endif diff --git a/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h b/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h new file mode 100644 index 0000000000000..bea49dd68fc07 --- /dev/null +++ b/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h @@ -0,0 +1,96 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RawReaderBase.h base class for RAW data reading +// +// Artur.Furs +// afurs@cern.ch +// +//Main purpuse is to decode FT0 data blocks and push them to DigitBlockFT0 for process + +#ifndef ALICEO2_FIT_RAWREADERBASE_H_ +#define ALICEO2_FIT_RAWREADERBASE_H_ +#include <iostream> +#include <vector> +#include <map> + +#include <Rtypes.h> +#include <CommonDataFormat/InteractionRecord.h> +#include "Headers/RAWDataHeader.h" +#include <Framework/Logger.h> + +#include <gsl/span> +namespace o2 +{ +namespace fit +{ +template <class DigitBlockType> +class RawReaderBase +{ + public: + RawReaderBase() = default; + ~RawReaderBase() = default; + + std::map<InteractionRecord, DigitBlockType> mMapDigits; + + //decoding binary data into data blocks + template <class DataBlockType> + size_t decodeBlocks(const gsl::span<const uint8_t> binaryPayload, std::vector<DataBlockType>& vecDataBlocks) + { + size_t srcPos = 0; + while (srcPos < binaryPayload.size()) { //checking element + DataBlockType dataBlock; + dataBlock.decodeBlock(binaryPayload, srcPos); + srcPos += dataBlock.mSize; + if (dataBlock.isCorrect()) { + vecDataBlocks.push_back(dataBlock); //change to in-place construction? TODO + } else { + LOG(INFO) << "WARNING! INCORRECT DATA BLOCK!"; + } + } + return srcPos; + } + + //processing data blocks into digits + template <class DataBlockType> + void processBinaryData(gsl::span<const uint8_t> payload, int linkID) + { + std::vector<DataBlockType> vecDataBlocks; + auto srcPos = decodeBlocks(payload, vecDataBlocks); + + for (auto& dataBlock : vecDataBlocks) { + auto intRec = dataBlock.getInteractionRecord(); + auto [digitIter, isNew] = mMapDigits.try_emplace(intRec, intRec); + digitIter->second.template process<DataBlockType>(dataBlock, linkID); + } + } + /* + void process(int linkID, gsl::span<const uint8_t> payload) + { + static_cast<RawReader*>(this)->processDigits(linkID,payload); + } + */ + //pop digits + template <class... DigitType> + int getDigits(std::vector<DigitType>&... vecDigit) + { + int digitCounter = mMapDigits.size(); + for (auto& digit : mMapDigits) { + digit.second.pop(vecDigit...); + } + mMapDigits.clear(); + return digitCounter; + } +}; + +} // namespace fit +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/FIT/raw/src/DataBlockBase.cxx b/Detectors/FIT/raw/src/DataBlockBase.cxx new file mode 100644 index 0000000000000..82ed86360af98 --- /dev/null +++ b/Detectors/FIT/raw/src/DataBlockBase.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FITRaw/DataBlockBase.h" +using namespace o2::fit; diff --git a/Detectors/FIT/raw/src/DigitBlockBase.cxx b/Detectors/FIT/raw/src/DigitBlockBase.cxx new file mode 100644 index 0000000000000..9e626d77b064a --- /dev/null +++ b/Detectors/FIT/raw/src/DigitBlockBase.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FITRaw/DigitBlockBase.h" +using namespace o2::fit; \ No newline at end of file diff --git a/Detectors/FIT/raw/src/FITRawLinkDef.h b/Detectors/FIT/raw/src/FITRawLinkDef.h new file mode 100644 index 0000000000000..71519a02a652e --- /dev/null +++ b/Detectors/FIT/raw/src/FITRawLinkDef.h @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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; + +#endif diff --git a/Detectors/FIT/raw/src/RawReaderBase.cxx b/Detectors/FIT/raw/src/RawReaderBase.cxx new file mode 100644 index 0000000000000..24b38a3138533 --- /dev/null +++ b/Detectors/FIT/raw/src/RawReaderBase.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FITRaw/RawReaderBase.h" +using namespace o2::fit; diff --git a/Detectors/FIT/workflow/CMakeLists.txt b/Detectors/FIT/workflow/CMakeLists.txt deleted file mode 100644 index 6e147c4cf6bd9..0000000000000 --- a/Detectors/FIT/workflow/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# 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". -# -# See http://alice-o2.web.cern.ch/license for full licensing information. -# -# In applying this license CERN does 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(FITWorkflow - SOURCES src/RecoWorkflow.cxx src/FT0DigitReaderSpec.cxx - src/FT0ReconstructorSpec.cxx src/FT0RecPointWriterSpec.cxx - src/FT0RecPointReaderSpec.cxx - PUBLIC_LINK_LIBRARIES O2::FT0Reconstruction O2::Framework O2::DPLUtils) - -o2_add_executable(reco-workflow - COMPONENT_NAME fit - SOURCES src/fit-reco-workflow.cxx - PUBLIC_LINK_LIBRARIES O2::FITWorkflow - TARGETVARNAME fitrecoexe) - -if(NOT APPLE) - set_property(TARGET ${fitrecoexe} PROPERTY LINK_WHAT_YOU_USE ON) -endif() diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FT0DigitReaderSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FT0DigitReaderSpec.h deleted file mode 100644 index 1e2127a9bd738..0000000000000 --- a/Detectors/FIT/workflow/include/FITWorkflow/FT0DigitReaderSpec.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0DigitReaderSpec.h - -#ifndef O2_FT0_DIGITREADER -#define O2_FT0_DIGITREADER - -#include "TFile.h" - -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/MCLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class DigitReader : public Task -{ - public: - DigitReader(bool useMC = true); - ~DigitReader() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mFinished = false; - bool mUseMC = true; // use MC truth - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; - - std::vector<o2::ft0::Digit>* mDigitsBC = nullptr; - std::vector<o2::ft0::ChannelData>* mDigitsCH = nullptr; - o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>* mMCTruth = nullptr; - - std::string mInputFileName = ""; - std::string mDigitTreeName = "o2sim"; - std::string mDigitBranchName = "FT0DigitBC"; - std::string mDigitChBranchName = "FT0DigitCH"; - std::string mDigitMCTruthBranchName = "FT0DIGITSMCTR"; -}; - -/// create a processor spec -/// read simulated ITS digits from a root file -framework::DataProcessorSpec getFT0DigitReaderSpec(bool useMC); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0_DIGITREADER */ diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FT0RecPointReaderSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FT0RecPointReaderSpec.h deleted file mode 100644 index c03c1fc072385..0000000000000 --- a/Detectors/FIT/workflow/include/FITWorkflow/FT0RecPointReaderSpec.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0RecPointReaderSpec.h - -#ifndef O2_FT0_RECPOINTREADER -#define O2_FT0_RECPOINTREADER - -#include "TFile.h" -#include "TTree.h" - -#include "Framework/RootSerializationSupport.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/RecPoints.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class RecPointReader : public Task -{ - public: - RecPointReader(bool useMC = true); - ~RecPointReader() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - void connectTree(const std::string& filename); - - std::unique_ptr<TFile> mFile; - std::unique_ptr<TTree> mTree; - - bool mUseMC = true; // use MC truth - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; - - std::vector<o2::ft0::RecPoints>* mRecPoints = nullptr; - - std::string mInputFileName = "o2reco_ft0.root"; - std::string mRecPointTreeName = "o2sim"; - std::string mRecPointBranchName = "FT0Cluster"; -}; - -/// create a processor spec -/// read simulated ITS digits from a root file -framework::DataProcessorSpec getFT0RecPointReaderSpec(bool useMC); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0_RECPOINTREADER */ diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FT0RecPointWriterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FT0RecPointWriterSpec.h deleted file mode 100644 index c49a9e6d26565..0000000000000 --- a/Detectors/FIT/workflow/include/FITWorkflow/FT0RecPointWriterSpec.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0RecPointWriterSpec.h - -#ifndef O2_FT0RECPOINTWRITER_H -#define O2_FT0RECPOINTWRITER_H - - -#include "Framework/DataProcessorSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -/// create a processor spec -framework::DataProcessorSpec getFT0RecPointWriterSpec(bool useMC); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0RECPOINTWRITER_H */ diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FT0ReconstructorSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FT0ReconstructorSpec.h deleted file mode 100644 index 31108e52b1a89..0000000000000 --- a/Detectors/FIT/workflow/include/FITWorkflow/FT0ReconstructorSpec.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0ReconstructorSpec.h - -#ifndef O2_FT0RECONSTRUCTORDPL_H -#define O2_FT0RECONSTRUCTORDPL_H - -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "FT0Reconstruction/CollisionTimeRecoTask.h" -#include "DataFormatsFT0/RecPoints.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0ReconstructorDPL : public Task -{ - public: - FT0ReconstructorDPL(bool useMC) : mUseMC(useMC) {} - ~FT0ReconstructorDPL() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mFinished = false; - bool mUseMC = true; - std::vector<o2::ft0::RecPoints> mRecPoints; - std::vector<o2::ft0::ChannelDataFloat> mRecChData; - o2::ft0::CollisionTimeRecoTask mReco; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; -}; - -/// create a processor spec -framework::DataProcessorSpec getFT0ReconstructorSpec(bool useMC = true); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0RECONSTRUCTORDPL_H */ diff --git a/Detectors/FIT/workflow/src/FT0DigitReaderSpec.cxx b/Detectors/FIT/workflow/src/FT0DigitReaderSpec.cxx deleted file mode 100644 index ad1f56aedf129..0000000000000 --- a/Detectors/FIT/workflow/src/FT0DigitReaderSpec.cxx +++ /dev/null @@ -1,109 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0DigitReaderSpec.cxx - -#include <vector> - -#include "TTree.h" - -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "FITWorkflow/FT0DigitReaderSpec.h" - -using namespace o2::framework; -using namespace o2::ft0; - -namespace o2 -{ -namespace ft0 -{ - -DigitReader::DigitReader(bool useMC) -{ - mUseMC = useMC; -} - -void DigitReader::init(InitContext& ic) -{ - mInputFileName = ic.options().get<std::string>("ft0-digits-infile"); -} - -void DigitReader::run(ProcessingContext& pc) -{ - if (mFinished) { - return; - } - - { // load data from files - TFile digFile(mInputFileName.c_str(), "read"); - if (digFile.IsZombie()) { - LOG(FATAL) << "Failed to open FT0 digits file " << mInputFileName; - } - LOG(INFO) << " file read " << mInputFileName; - TTree* digTree = (TTree*)digFile.Get(mDigitTreeName.c_str()); - // digTree->Print(); - if (!digTree) { - LOG(FATAL) << "Failed to load FT0 digits tree " << mDigitTreeName << " from " << mInputFileName; - } - if (!digTree) { - LOG(FATAL) << "Failed to load FT0 digits tree " << mDigitTreeName << " from " << mInputFileName; - } - LOG(INFO) << "Loaded FT0 digits tree " << mDigitTreeName << " from " << mInputFileName; - - digTree->SetBranchAddress("FT0DIGITSBC", &mDigitsBC); - digTree->SetBranchAddress("FT0DIGITSCH", &mDigitsCH); - - if (mUseMC) { - if (digTree->GetBranch(mDigitMCTruthBranchName.c_str())) { - digTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mMCTruth); - LOG(INFO) << "Will use MC-truth from " << mDigitMCTruthBranchName; - } else { - LOG(INFO) << "MC-truth is missing"; - mUseMC = false; - } - } - - digTree->GetEntry(0); - delete digTree; - digFile.Close(); - } - - pc.outputs().snapshot(Output{mOrigin, "DIGITSBC", 0, Lifetime::Timeframe}, *mDigitsBC); - pc.outputs().snapshot(Output{mOrigin, "DIGITSCH", 0, Lifetime::Timeframe}, *mDigitsCH); - if (mUseMC) { - pc.outputs().snapshot(Output{mOrigin, "DIGITSMCTR", 0, Lifetime::Timeframe}, *mMCTruth); - } - - mFinished = true; - pc.services().get<ControlService>().endOfStream(); - pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); -} - -DataProcessorSpec getFT0DigitReaderSpec(bool useMC) -{ - std::vector<OutputSpec> outputSpec; - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - if (useMC) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSMCTR", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "ft0-digit-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask<DigitReader>()}, - Options{ - {"ft0-digits-infile", VariantType::String, "ft0digits.root", {"Name of the input file"}}}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/workflow/src/FT0RecPointReaderSpec.cxx b/Detectors/FIT/workflow/src/FT0RecPointReaderSpec.cxx deleted file mode 100644 index 5fbf40021a00a..0000000000000 --- a/Detectors/FIT/workflow/src/FT0RecPointReaderSpec.cxx +++ /dev/null @@ -1,93 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0RecPointReaderSpec.cxx - -#include <vector> - -#include "TTree.h" - -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "FITWorkflow/FT0RecPointReaderSpec.h" - -using namespace o2::framework; -using namespace o2::ft0; - -namespace o2 -{ -namespace ft0 -{ - -RecPointReader::RecPointReader(bool useMC) -{ - mUseMC = useMC; - if (useMC) { - LOG(WARNING) << "FT0 RecPoint reader at the moment does not process MC"; - } -} - -void RecPointReader::init(InitContext& ic) -{ - mInputFileName = ic.options().get<std::string>("ft0-recpoints-infile"); - connectTree(mInputFileName); -} - -void RecPointReader::run(ProcessingContext& pc) -{ - auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen - mTree->GetEntry(ent); - - LOG(INFO) << "FT0 RecPointReader pushes " << mRecPoints->size() << " recpoints at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); - - if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { - pc.services().get<ControlService>().endOfStream(); - pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); - } -} - -void RecPointReader::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(mRecPointTreeName.c_str())); - assert(mTree); - - mTree->SetBranchAddress(mRecPointBranchName.c_str(), &mRecPoints); - if (mUseMC) { - LOG(WARNING) << "MC-truth is not supported for FT0 recpoints currently"; - mUseMC = false; - } - - LOG(INFO) << "Loaded FT0 RecPoints tree from " << filename << " with " << mTree->GetEntries() << " entries"; -} - -DataProcessorSpec getFT0RecPointReaderSpec(bool useMC) -{ - std::vector<OutputSpec> outputSpec; - outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); - if (useMC) { - LOG(WARNING) << "MC-truth is not supported for FT0 recpoints currently"; - } - - return DataProcessorSpec{ - "ft0-recpoints-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask<RecPointReader>()}, - Options{ - {"ft0-recpoints-infile", VariantType::String, "o2reco_ft0.root", {"Name of the input file"}}}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/workflow/src/FT0RecPointWriterSpec.cxx b/Detectors/FIT/workflow/src/FT0RecPointWriterSpec.cxx deleted file mode 100644 index a50671d2f0115..0000000000000 --- a/Detectors/FIT/workflow/src/FT0RecPointWriterSpec.cxx +++ /dev/null @@ -1,50 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0RecPointWriterSpec.cxx - -#include <vector> - -#include "FITWorkflow/FT0RecPointWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsFT0/RecPoints.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -template <typename T> -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; -DataProcessorSpec getFT0RecPointWriterSpec(bool useMC) -{ - using RecPointsType = std::vector<o2::ft0::RecPoints>; - using ChanDataType = std::vector<o2::ft0::ChannelDataFloat>; - // Spectators for logging - auto logger = [](RecPointsType const& recPoints) { - LOG(INFO) << "FT0RecPointWriter pulled " << recPoints.size() << " RecPoints"; - }; - return MakeRootTreeWriterSpec("ft0-recpoint-writer", - "o2reco_ft0.root", - "o2sim", - BranchDefinition<RecPointsType>{InputSpec{"recPoints", "FT0", "RECPOINTS", 0}, - "FT0Cluster", - "ft0-recpoint-branch-name", - 1, - logger}, - BranchDefinition<ChanDataType>{InputSpec{"recChData", "FT0", "RECCHDATA", 0}, - "FT0RecChData", - "ft0-rechhdata-branch-name"})(); -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/workflow/src/FT0ReconstructorSpec.cxx b/Detectors/FIT/workflow/src/FT0ReconstructorSpec.cxx deleted file mode 100644 index 92e3a94db4f14..0000000000000 --- a/Detectors/FIT/workflow/src/FT0ReconstructorSpec.cxx +++ /dev/null @@ -1,92 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FT0ReconstructorSpec.cxx - -#include <vector> -#include "SimulationDataFormat/MCTruthContainer.h" -#include "Framework/ControlService.h" -#include "FITWorkflow/FT0ReconstructorSpec.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" -#include "DataFormatsFT0/MCLabel.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -void FT0ReconstructorDPL::init(InitContext& ic) -{ - LOG(INFO) << "FT0ReconstructorDPL::init"; -} - -void FT0ReconstructorDPL::run(ProcessingContext& pc) -{ - if (mFinished) { - return; - } - mRecPoints.clear(); - auto digits = pc.inputs().get<gsl::span<o2::ft0::Digit>>("digits"); - //auto digits = pc.inputs().get<const std::vector<o2::ft0::Digit>>("digits"); - auto digch = pc.inputs().get<gsl::span<o2::ft0::ChannelData>>("digch"); - // RS: if we need to process MC truth, uncomment lines below - //std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>> labels; - //const o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>* lblPtr = nullptr; - if (mUseMC) { - // labels = pc.inputs().get<const o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>*>("labels"); - // lblPtr = labels.get(); - LOG(INFO) << "Ignoring MC info"; - } - int nDig = digits.size(); - mRecPoints.reserve(nDig); - mRecChData.resize(digch.size()); - for (int id = 0; id < nDig; id++) { - const auto& digit = digits[id]; - auto channels = digit.getBunchChannelData(digch); - gsl::span<o2::ft0::ChannelDataFloat> out_ch(mRecChData); - out_ch = out_ch.subspan(digit.ref.getFirstEntry(), digit.ref.getEntries()); - mRecPoints.emplace_back(mReco.process(digit, channels, out_ch)); - } - // 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); - - mFinished = true; - pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); -} - -DataProcessorSpec getFT0ReconstructorSpec(bool useMC) -{ - std::vector<InputSpec> inputSpec; - std::vector<OutputSpec> 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 FT0Reconstructor does not consume and provide MC truth"; - inputSpec.emplace_back("labels", o2::header::gDataOriginFT0, "DIGITSMCTR", 0, Lifetime::Timeframe); - } - outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECCHDATA", 0, Lifetime::Timeframe); - - return DataProcessorSpec{ - "ft0-reconstructor", - inputSpec, - outputSpec, - AlgorithmSpec{adaptFromTask<FT0ReconstructorDPL>(useMC)}, - Options{}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/workflow/src/RecoWorkflow.cxx deleted file mode 100644 index 024533ab3f5fa..0000000000000 --- a/Detectors/FIT/workflow/src/RecoWorkflow.cxx +++ /dev/null @@ -1,38 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 RecoWorkflow.cxx - -#include "FITWorkflow/RecoWorkflow.h" - -#include "FITWorkflow/FT0DigitReaderSpec.h" -#include "FITWorkflow/FT0RecPointWriterSpec.h" -#include "FITWorkflow/FT0ReconstructorSpec.h" - -namespace o2 -{ -namespace fit -{ - -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) -{ - framework::WorkflowSpec specs; - if (!disableRootInp) { - specs.emplace_back(o2::ft0::getFT0DigitReaderSpec(useMC)); - } - specs.emplace_back(o2::ft0::getFT0ReconstructorSpec(useMC)); - if (!disableRootOut) { - specs.emplace_back(o2::ft0::getFT0RecPointWriterSpec(useMC)); - } - return specs; -} - -} // namespace fit -} // namespace o2 diff --git a/Detectors/FIT/workflow/src/fit-reco-workflow.cxx b/Detectors/FIT/workflow/src/fit-reco-workflow.cxx deleted file mode 100644 index 8c992576cdc6e..0000000000000 --- a/Detectors/FIT/workflow/src/fit-reco-workflow.cxx +++ /dev/null @@ -1,50 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "FITWorkflow/RecoWorkflow.h" -#include "CommonUtils/ConfigurableParam.h" - -using namespace o2::framework; - -// ------------------------------------------------------------------ - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) -{ - // option allowing to set parameters - workflowOptions.push_back(ConfigParamSpec{ - "disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}); - workflowOptions.push_back(ConfigParamSpec{ - "disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}); - workflowOptions.push_back(ConfigParamSpec{ - "disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}); - std::string keyvaluehelp("Semicolon separated key=value strings ..."); - workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); -} - -// ------------------------------------------------------------------ - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - LOG(INFO) << "WorkflowSpec defineDataProcessing"; - // Update the (declared) parameters if changed from the command line - o2::conf::ConfigurableParam::updateFromString(configcontext.options().get<std::string>("configKeyValues")); - // write the configuration used for the digitizer workflow - o2::conf::ConfigurableParam::writeINI("o2tpcits-match-recoflow_configuration.ini"); - - auto useMC = !configcontext.options().get<bool>("disable-mc"); - auto disableRootInp = configcontext.options().get<bool>("disable-root-input"); - auto disableRootOut = configcontext.options().get<bool>("disable-root-output"); - - LOG(INFO) << "WorkflowSpec getRecoWorkflow useMC " << useMC; - return std::move(o2::fit::getRecoWorkflow(useMC, disableRootInp, disableRootOut)); -} diff --git a/Detectors/GlobalTracking/CMakeLists.txt b/Detectors/GlobalTracking/CMakeLists.txt index 92d10c7906c32..da30ffd8a8d79 100644 --- a/Detectors/GlobalTracking/CMakeLists.txt +++ b/Detectors/GlobalTracking/CMakeLists.txt @@ -19,6 +19,7 @@ o2_add_library( O2::DataFormatsFT0 O2::DataFormatsTOF O2::ITSReconstruction + O2::FT0Reconstruction O2::TPCFastTransformation O2::GPUTracking O2::TPCBase diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h index 225f26075edc6..ba5c2221294f1 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h @@ -59,7 +59,7 @@ namespace globaltracking ///< original track in the currently loaded TPC-ITS reco output struct TrackLocTPCITS : public o2::track::TrackParCov { o2::dataformats::EvIndex<int, int> source; ///< track origin id - o2::utils::Bracket<float> timeBins; ///< bracketing time-bins + o2::math_utils::Bracket<float> timeBins; ///< bracketing time-bins float zMin = 0; // min possible Z of this track float zMax = 0; // max possible Z of this track int matchID = MinusOne; ///< entry (none if MinusOne) of TOF matchTOF struct in the mMatchesTOF @@ -73,6 +73,8 @@ class MatchTOF using Geo = o2::tof::Geo; using Cluster = o2::tof::Cluster; using evIdx = o2::dataformats::EvIndex<int, int>; + using timeEst = o2::dataformats::TimeStampWithError<float, float>; + using matchTrack = std::pair<o2::track::TrackParCov, timeEst>; public: ///< perform matching for provided input @@ -83,9 +85,11 @@ class MatchTOF ///< perform all initializations void init(); + void initTPConly(); ///< attach DPL data and run void run(const gsl::span<const o2::dataformats::TrackTPCITS>& trackArray, const gsl::span<const Cluster>& clusterArray, const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& toflab, const gsl::span<const o2::MCCompLabel>& itslab, const gsl::span<const o2::MCCompLabel>& tpclab); + void run(const gsl::span<const o2::tpc::TrackTPC>& trackArray, const gsl::span<const Cluster>& clusterArray, const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& toflab, const gsl::span<const o2::MCCompLabel>& tpclab); ///< set tree/chain containing tracks void setInputTreeTracks(TTree* tree) { mInputTreeTracks = tree; } @@ -169,8 +173,8 @@ class MatchTOF const std::string& getDebugTreeFileName() const { return mDebugTreeFileName; } ///< fill matching debug tree - void fillTOFmatchTree(const char* tname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, o2::dataformats::TrackTPCITS& trk, float intLength, float intTimePion, float timeTOF); - void fillTOFmatchTreeWithLabels(const char* tname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, o2::dataformats::TrackTPCITS& trk, int TPClabelTrackID, int TPClabelEventID, int TPClabelSourceID, int ITSlabelTrackID, int ITSlabelEventID, int ITSlabelSourceID, int TOFlabelTrackID0, int TOFlabelEventID0, int TOFlabelSourceID0, int TOFlabelTrackID1, int TOFlabelEventID1, int TOFlabelSourceID1, int TOFlabelTrackID2, int TOFlabelEventID2, int TOFlabelSourceID2, float intLength, float intTimePion, float timeTOF); + void fillTOFmatchTree(const char* tname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, matchTrack& trk, float intLength, float intTimePion, float timeTOF); + void fillTOFmatchTreeWithLabels(const char* tname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, matchTrack& trk, int TPClabelTrackID, int TPClabelEventID, int TPClabelSourceID, int ITSlabelTrackID, int ITSlabelEventID, int ITSlabelSourceID, int TOFlabelTrackID0, int TOFlabelEventID0, int TOFlabelSourceID0, int TOFlabelTrackID1, int TOFlabelEventID1, int TOFlabelSourceID1, int TOFlabelTrackID2, int TOFlabelEventID2, int TOFlabelSourceID2, float intLength, float intTimePion, float timeTOF); void dumpWinnerMatches(); std::vector<o2::dataformats::MatchInfoTOF>& getMatchedTrackVector() { return mMatchedTracks; } @@ -197,12 +201,16 @@ class MatchTOF private: void attachInputTrees(); + void attachInputTreesTPConly(); bool prepareTracks(); + bool prepareTPCTracks(); bool prepareTOFClusters(); bool loadTracksNextChunk(); + bool loadTPCTracksNextChunk(); bool loadTOFClustersNextChunk(); void doMatching(int sec); + void doMatchingForTPC(int sec); void selectBestMatches(); 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); @@ -233,6 +241,8 @@ class MatchTOF TTree* mTreeTPCTracks = nullptr; ///< input tree for TPC tracks TTree* mTreeTOFClusters = nullptr; ///< input tree for TOF clusters + bool mIsITSused = true; + TTree* mOutputTree = nullptr; ///< output tree for matched tracks TTree* mOutputTreeCalib = nullptr; ///< output tree for calibration infos @@ -241,13 +251,14 @@ class MatchTOF // since this info is provided by external device gsl::span<const o2::dataformats::TrackTPCITS> mTracksArrayInp; ///< input tracks std::vector<o2::dataformats::TrackTPCITS>* mTracksArrayInpVect; ///< input tracks (vector to read from tree) - std::vector<o2::tpc::TrackTPC> mTPCTracksArrayInp; ///< input TPC tracks + gsl::span<const o2::tpc::TrackTPC> mTPCTracksArrayInp; ///< input TPC tracks + std::vector<o2::tpc::TrackTPC>* mTPCTracksArrayInpVect; ///< input tracks (vector to read from tree) gsl::span<const Cluster> mTOFClustersArrayInp; ///< input TOF clusters std::vector<Cluster>* mTOFClustersArrayInpVect; ///< input TOF clusters (vector to read from tree) - o2::dataformats::MCTruthContainer<o2::MCCompLabel> mTOFClusLabels; ///< input TOF clusters MC labels - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mTOFClusLabelsPtr; ///< input TOF clusters MC labels (pointer to read from tree) - std::vector<o2::MCCompLabel> mTracksLblWork; ///<TPCITS track labels + o2::dataformats::MCTruthContainer<o2::MCCompLabel> mTOFClusLabels; ///< input TOF clusters MC labels + o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mTOFClusLabelsPtr; ///< input TOF clusters MC labels (pointer to read from tree) + std::vector<o2::MCCompLabel> mTracksLblWork; ///<TPCITS track labels gsl::span<const o2::MCCompLabel> mTPCLabels; ///< TPC label of input tracks gsl::span<const o2::MCCompLabel> mITSLabels; ///< ITS label of input tracks @@ -259,11 +270,16 @@ class MatchTOF /// <<<----- ///<working copy of the input tracks - std::vector<o2::dataformats::TrackTPCITS> mTracksWork; ///<track params prepared for matching - std::vector<Cluster> mTOFClusWork; ///<track params prepared for matching + std::vector<matchTrack> mTracksWork; ///<track params prepared for matching + time value + std::vector<float> mExtraTPCFwdTime; ///<track extra params for TPC tracks: Fws Max time + std::vector<o2::track::TrackLTIntegral> mLTinfos; ///<expected times and others + std::vector<Cluster> mTOFClusWork; ///<track params prepared for matching + std::vector<int8_t> mSideTPC; ///<track side for TPC tracks ///< per sector indices of track entry in mTracksWork std::array<std::vector<int>, o2::constants::math::NSectors> mTracksSectIndexCache; + ///< per sector indices of track entry in mTPCTracksWork + std::array<std::vector<int>, o2::constants::math::NSectors> mTPCTracksSectIndexCache; ///< per sector indices of TOF cluster entry in mTOFClusWork std::array<std::vector<int>, o2::constants::math::NSectors> mTOFClusSectIndexCache; @@ -285,17 +301,18 @@ class MatchTOF int mNumOfClusters; // number of clusters to be matched int* mMatchedClustersIndex = nullptr; //[mNumOfClusters] - std::string mTracksBranchName = "TPCITS"; ///< name of branch containing input matched tracks - std::string mTPCTracksBranchName = "Tracks"; ///< name of branch containing actual TPC tracks - std::string mTPCMCTruthBranchName = "MatchTPCMCTruth"; ///< name of branch containing TPC labels - std::string mITSMCTruthBranchName = "MatchITSMCTruth"; ///< name of branch containing ITS labels - std::string mTOFMCTruthBranchName = "TOFClusterMCTruth"; ///< name of branch containing TOF clusters labels - std::string mTOFClusterBranchName = "TOFCluster"; ///< name of branch containing input ITS clusters - std::string mOutTracksBranchName = "TOFMatchInfo"; ///< name of branch containing output matched tracks - std::string mOutCalibBranchName = "TOFCalibInfo"; ///< name of branch containing output calibration infos - std::string mOutTOFMCTruthBranchName = "MatchTOFMCTruth"; ///< name of branch containing TOF labels for output matched tracks - std::string mOutTPCMCTruthBranchName = "MatchTPCMCTruth"; ///< name of branch containing TOF labels for output matched tracks - std::string mOutITSMCTruthBranchName = "MatchITSMCTruth"; ///< name of branch containing TOF labels for output matched tracks + std::string mTracksBranchName = "TPCITS"; ///< name of branch containing input matched tracks + std::string mTPCTracksBranchName = "Tracks"; ///< name of branch containing actual TPC tracks + std::string mTPCMCTruthBranchName = "MatchTPCMCTruth"; ///< name of branch containing TPC labels + std::string mITSMCTruthBranchName = "MatchITSMCTruth"; ///< name of branch containing ITS labels + std::string mTOFMCTruthBranchName = "TOFClusterMCTruth"; ///< name of branch containing TOF clusters labels + std::string mTOFClusterBranchName = "TOFCluster"; ///< name of branch containing input ITS clusters + std::string mOutTracksBranchName = "TOFMatchInfo"; ///< name of branch containing output matched tracks + std::string mOutCalibBranchName = "TOFCalibInfo"; ///< name of branch containing output calibration infos + std::string mOutTOFMCTruthBranchName = "MatchTOFMCTruth"; ///< name of branch containing TOF labels for output matched tracks + std::string mOutTPCMCTruthBranchName = "MatchTPCMCTruth"; ///< name of branch containing TOF labels for output matched tracks + std::string mOutITSMCTruthBranchName = "MatchITSMCTruth"; ///< name of branch containing TOF labels for output matched tracks + std::string mOutTPCTrackMCTruthBranchName = "TracksMCTruth"; ///< name of branch containing TPC labels for input TPC tracks std::unique_ptr<o2::utils::TreeStreamRedirector> mDBGOut; UInt_t mDBGFlags = 0; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h index e60f0d2e037fd..c4fa56f08429c 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h @@ -30,20 +30,24 @@ #include "DetectorsBase/Propagator.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/TrackTPCITS.h" -#include "MathUtils/Bracket.h" +#include "MathUtils/Primitive2D.h" #include "CommonDataFormat/EvIndex.h" #include "CommonDataFormat/InteractionRecord.h" #include "CommonDataFormat/RangeReference.h" +#include "CommonDataFormat/BunchFilling.h" #include "SimulationDataFormat/MCCompLabel.h" #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsITSMFT/Cluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITS/TrackITS.h" #include "DataFormatsFT0/RecPoints.h" +#include "FT0Reconstruction/InteractionTag.h" #include "DataFormatsTPC/ClusterNativeHelper.h" #include "ITSReconstruction/RecoGeomHelper.h" #include "TPCFastTransform.h" +#include "GPUO2InterfaceRefit.h" #include "GlobalTracking/MatchTPCITSParams.h" +#include "CommonDataFormat/FlatHisto2D.h" class TTree; @@ -99,11 +103,11 @@ enum TrackRejFlag : int { ///< TPC track parameters propagated to reference X, with time bracket and index of ///< original track in the currently loaded TPC reco output struct TrackLocTPC : public o2::track::TrackParCov { - o2::utils::Bracket<float> timeBins; ///< bracketing time-bins - int sourceID = 0; ///< track origin id - float zMin = 0; // min possible Z of this track - float zMax = 0; // max possible Z of this track - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC + o2::math_utils::Bracketf_t timeBins; ///< bracketing time-bins + int sourceID = 0; ///< track origin id + float zMin = 0; // min possible Z of this track + float zMax = 0; // max possible Z of this track + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC TrackLocTPC(const o2::track::TrackParCov& src, int tid) : o2::track::TrackParCov(src), sourceID(tid) {} TrackLocTPC() = default; ClassDefNV(TrackLocTPC, 1); @@ -112,24 +116,37 @@ struct TrackLocTPC : public o2::track::TrackParCov { ///< 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 { - 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 TrackLocITS(const o2::track::TrackParCov& src, int tid) : o2::track::TrackParCov(src), sourceID(tid) {} TrackLocITS() = default; ClassDefNV(TrackLocITS, 1); }; ///< record TPC or ITS track associated with single ITS or TPC track and reference on -///< the next (worse chi2) matchRecord of the same TPC or ITS track -struct matchRecord { +///< the next (worse chi2) MatchRecord of the same TPC or ITS track +struct MatchRecord { float chi2 = -1.f; ///< matching chi2 int partnerID = MinusOne; ///< id of parnter track entry in mTPCWork or mITSWork containers int nextRecID = MinusOne; ///< index of eventual next record + int matchedIC = MinusOne; ///< index of eventually matched InteractionCandidate + MatchRecord(int partID, float chi2match, int nxt = MinusOne, int candIC = MinusOne) : partnerID(partID), chi2(chi2match), nextRecID(nxt), matchedIC(candIC) {} + MatchRecord() = default; + + bool isBetter(float otherChi2, int otherIC = MinusOne) const + { // prefer record with matched IC candidate, otherwise, better chi2 + if (otherIC == MinusOne) { + return matchedIC == MinusOne ? chi2 < otherChi2 : true; + } else { + return matchedIC == MinusOne ? false : chi2 < otherChi2; + } + } - matchRecord(int partID, float chi2match) : partnerID(partID), chi2(chi2match) {} - matchRecord(int partID, float chi2match, int nxt) : partnerID(partID), chi2(chi2match), nextRecID(nxt) {} - matchRecord() = default; + bool isBetter(const MatchRecord& other) const + { + return isBetter(other.chi2, other.matchedIC); + } }; ///< Link of the AfterBurner track: update at sertain cluster @@ -201,7 +218,7 @@ struct ABDebugTrack { short valid = 0; o2::track::TrackParCov tpcSeed; o2::MCCompLabel tpcLabel; - o2::utils::Bracket<float> icTimeBin; + o2::math_utils::Bracket<float> icTimeBin; std::vector<ABDebugLink> links; float chi2 = 0; uint8_t nClusTPC = 0; @@ -226,10 +243,10 @@ struct ABClusterLink { }; struct InteractionCandidate : public o2::InteractionRecord { - o2::utils::Bracket<float> timeBins; // interaction time (int TPC time bins) - int rofITS; // corresponding ITS ROF entry (in the ROFRecord vectors) - uint32_t flag; // origin, etc. - void* clRefPtr = nullptr; // pointer on cluster references container (if any) + o2::math_utils::Bracket<float> timeBins; // interaction time (int TPC time bins) + int rofITS; // corresponding ITS ROF entry (in the ROFRecord vectors) + uint32_t flag; // origin, etc. + void* clRefPtr = nullptr; // pointer on cluster references container (if any) InteractionCandidate() = default; InteractionCandidate(const o2::InteractionRecord& ir, float t, float dt, int rof, uint32_t f = 0) : o2::InteractionRecord(ir), timeBins(t - dt, t + dt), rofITS(rof), flag(f) {} }; @@ -253,15 +270,18 @@ struct ITSChipClustersRefs { class MatchTPCITS { + public: using ITSCluster = o2::BaseCluster<float>; using ClusRange = o2::dataformats::RangeReference<int, int>; - using MCLabCont = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + using MCLabContCl = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + using MCLabContTr = std::vector<o2::MCCompLabel>; + using MCLabSpan = gsl::span<const o2::MCCompLabel>; using TPCTransform = o2::gpu::TPCFastTransform; - using BracketF = o2::utils::Bracket<float>; - using Params = o2::globaltracking::MatchITSTPCParams; + using BracketF = o2::math_utils::Bracket<float>; + using BracketIR = o2::math_utils::Bracket<o2::InteractionRecord>; + using Params = o2::globaltracking::MatchTPCITSParams; using MatCorrType = o2::base::Propagator::MatCorrType; - public: MatchTPCITS(); // std::unique_ptr to forward declared type needs constructor / destructor in .cxx ~MatchTPCITS(); @@ -300,6 +320,9 @@ class MatchTPCITS void destroyLastABTrackLinksList(); void refitABTrack(int ibest) const; + void setCosmics(bool v) { mCosmics = v; } + bool isCosmics() const { return mCosmics; } + ///< perform all initializations void init(); @@ -309,10 +332,16 @@ class MatchTPCITS ///< set InteractionRecods for the beginning of the TF void setStartIR(const o2::InteractionRecord& ir) { mStartIR = ir; } + ///< set Bunch filling and init helpers for validation by BCs + void setBunchFilling(const o2::BunchFilling& bf); + ///< ITS readout mode void setITSTriggered(bool v) { mITSTriggered = v; } bool isITSTriggered() const { return mITSTriggered; } + void setUseFT0(bool v) { mUseFT0 = v; } + bool getUseFT0() const { return mUseFT0; } + ///< set ITS ROFrame duration in microseconds void setITSROFrameLengthMUS(float fums) { mITSROFrameLengthMUS = fums; } ///< set ITS ROFrame duration in BC (continuous mode only) @@ -326,6 +355,15 @@ class MatchTPCITS mMCTruthON = v; } + ///< request VDrift calibration + void setVDriftCalib(bool v) + { + mVDriftCalibOn = v; + } + + ///< get histo for tgl differences for VDrift calibration + auto getHistoDTgl() { return mHistoDTgl.get(); } + ///< set input ITS tracks void setITSTracksInp(const gsl::span<const o2::its::TrackITS> inp) { @@ -375,19 +413,19 @@ class MatchTPCITS } ///< set input ITS track MC labels - void setITSTrkLabelsInp(const MCLabCont* lbl) + void setITSTrkLabelsInp(const MCLabSpan& lbl) { mITSTrkLabels = lbl; } ///< set input ITS clusters MC labels - void setITSClsLabelsInp(const MCLabCont* lbl) + void setITSClsLabelsInp(const MCLabContCl* lbl) { mITSClsLabels = lbl; } ///< set input TPC track MC labels - void setTPCTrkLabelsInp(const MCLabCont* lbl) + void setTPCTrkLabelsInp(const MCLabSpan& lbl) { mTPCTrkLabels = lbl; } @@ -406,8 +444,8 @@ class MatchTPCITS void printCandidatesITS() const; std::vector<o2::dataformats::TrackTPCITS>& getMatchedTracks() { return mMatchedTracks; } - std::vector<o2::MCCompLabel>& getMatchedITSLabels() { return mOutITSLabels; } - std::vector<o2::MCCompLabel>& getMatchedTPCLabels() { return mOutTPCLabels; } + MCLabContTr& getMatchedITSLabels() { return mOutITSLabels; } + MCLabContTr& getMatchedTPCLabels() { return mOutTPCLabels; } //>>> ====================== options =============================>>> void setUseMatCorrFlag(MatCorrType f) { mUseMatCorrFlag = f; } @@ -447,9 +485,11 @@ class MatchTPCITS #endif private: - int findLaddersToCheckBOn(int ilr, int lad0, const o2::utils::CircleXY& circle, float errYFrac, + void updateTimeDependentParams(); + + int findLaddersToCheckBOn(int ilr, int lad0, const o2::math_utils::CircleXYf_t& circle, float errYFrac, std::array<int, MaxLadderCand>& lad2Check) const; - int findLaddersToCheckBOff(int ilr, int lad0, const o2::utils::IntervalXY& trcLinPar, float errYFrac, + int findLaddersToCheckBOff(int ilr, int lad0, const o2::math_utils::IntervalXYf_t& trcLinPar, float errYFrac, std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const; int prepareTPCTracksAfterBurner(); @@ -458,18 +498,16 @@ class MatchTPCITS bool prepareFITInfo(); int preselectChipClusters(std::vector<int>& clVecOut, const ClusRange& clRange, const ITSChipClustersRefs& clRefs, - float trackY, float trackZ, float tolerY, float tolerZ, - const o2::MCCompLabel& lblTrc) const; + float trackY, float trackZ, float tolerY, float tolerZ, const o2::MCCompLabel& lblTrc) const; void fillClustersForAfterBurner(ITSChipClustersRefs& refCont, int rofStart, int nROFs = 1); void cleanAfterBurnerClusRefCache(int currentIC, int& startIC); void flagUsedITSClusters(const o2::its::TrackITS& track, int rofOffset); void doMatching(int sec); - void refitWinners(bool loopInITS = false); - bool refitTrackTPCITSloopITS(int iITS, int& iTPC); - bool refitTrackTPCITSloopTPC(int iTPC, int& iITS); - bool refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB, float m = o2::constants::physics::MassPionCharged) const; + 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(); bool validateTPCMatch(int iTPC); @@ -481,13 +519,13 @@ class MatchTPCITS bool isDisabledITS(const TrackLocITS& t) const; int compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& tTPC, float& chi2) const; - float getPredictedChi2NoZ(const o2::track::TrackParCov& tr1, const o2::track::TrackParCov& tr2) const; + float getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, const o2::track::TrackParCov& trTPC) const; bool propagateToRefX(o2::track::TrackParCov& trc); void addLastTrackCloneForNeighbourSector(int sector); ///------------------- manipulations with matches records ---------------------- - bool registerMatchRecordTPC(int iITS, int iTPC, float chi2); - void registerMatchRecordITS(int iITS, int iTPC, float chi2); + bool registerMatchRecordTPC(int iITS, int iTPC, float chi2, int candIC = MinusOne); + void registerMatchRecordITS(int iITS, int iTPC, float chi2, int candIC = MinusOne); void suppressMatchRecordITS(int iITS, int iTPC); ///< get number of matching records for TPC track @@ -496,6 +534,9 @@ class MatchTPCITS ///< get number of matching records for ITS track int getNMatchRecordsITS(const TrackLocITS& tITS) const; + ///< convert TPC timebins bracket to IR bracket + BracketIR tpcTimeBin2IRBracket(const BracketF tbrange); + ///< convert TPC time bin to ITS ROFrame units int tpcTimeBin2ITSROFrame(float tbin) const { @@ -510,10 +551,28 @@ class MatchTPCITS ///< convert ITS ROFrame to TPC time bin units // TOREMOVE float itsROFrame2TPCTimeBin(int rof) const { return rof * mITSROFrame2TPCBin; } + ///< convert time in microseconds to TPC time bin units + float time2TPCTimeBin(float tms) const + { + return tms * mTPCTBinMUSInv; + } + + ///< convert TPC time bin to microseconds + float tpcTimeBin2MUS(float tbn) const + { + return tbn * mTPCTBinMUS; + } + + ///< convert TPC time bin to nanoseconds + float tpcTimeBin2NS(float tbn) const + { + return tbn * mTPCTBinNS; + } + ///< convert Interaction Record for TPC time bin units float intRecord2TPCTimeBin(const o2::InteractionRecord& bc) const { - return bc.differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS / 1000 / mTPCTBinMUS; + return time2TPCTimeBin(bc.differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS * 1e-3); } ///< convert Z interval to TPC time-bins @@ -527,20 +586,39 @@ class MatchTPCITS { return delta > toler ? rejFlag : (delta < -toler ? -rejFlag : Accept); } + + ///< correct TPC time0 (int TPC time-bins) + float getTPCTrackCorrectedTimeBin(const o2::tpc::TrackTPC& trc, float delta) const + { + float timeTB = trc.getTime0(); + if (trc.hasASideClustersOnly()) { + timeTB += delta; + } else if (trc.hasCSideClustersOnly()) { + timeTB -= delta; + } else { + // TODO : special treatment of tracks crossing the CE + } + return timeTB; + } + //================================================================ - bool mInitDone = false; ///< flag init already done - bool mFieldON = true; ///< flag for field ON/OFF - bool mMCTruthON = false; ///< flag availability of MC truth + bool mInitDone = false; ///< flag init already done + bool mFieldON = true; ///< flag for field ON/OFF + bool mCosmics = false; ///< flag cosmics mode + bool mMCTruthON = false; ///< flag availability of MC truth + float mBz = 0; ///< nominal Bz - o2::InteractionRecord mStartIR; ///< IR corresponding to the start of the TF + o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF ///========== Parameters to be set externally, e.g. from CCDB ==================== const Params* mParams = nullptr; + const o2::ft0::InteractionTag* mFT0Params = nullptr; MatCorrType mUseMatCorrFlag = MatCorrType::USEMatCorrTGeo; bool mITSTriggered = false; ///< ITS readout is triggered + bool mUseFT0 = false; ///< FT0 information is available ///< do we use track Z difference to reject fake matches? makes sense for triggered mode only bool mCompareTracksDZ = false; @@ -556,18 +634,30 @@ class MatchTPCITS float mTPCVDrift0 = -1.; ///< TPC nominal drift speed in cm/microseconds float mTPCVDrift0Inv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds 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 mITSROFrame2TPCBin = 0.; ///< conversion coeff from ITS ROFrame units to TPC time-bin float mTPCBin2ITSROFrame = 0.; ///< conversion coeff from TPC time-bin to ITS ROFrame units 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 + std::unique_ptr<o2::dataformats::FlatHisto2D_f> mHistoDTgl; ///< histo for VDrift calibration data + std::unique_ptr<TPCTransform> mTPCTransform; ///< TPC cluster transformation std::unique_ptr<o2::gpu::GPUParam> mTPCClusterParam; ///< TPC clusters error param + std::unique_ptr<o2::gpu::GPUTPCO2InterfaceRefit> mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction + std::vector<unsigned char> mTPCRefitterShMap; + + o2::BunchFilling mBunchFilling; + std::array<int16_t, o2::constants::lhc::LHCMaxBunches> mClosestBunchAbove; // closest filled bunch from above + std::array<int16_t, o2::constants::lhc::LHCMaxBunches> mClosestBunchBelow; // closest filled bunch from below ///>>>------ these are input arrays which should not be modified by the matching code // since this info is provided by external device @@ -580,27 +670,28 @@ class MatchTPCITS gsl::span<const o2::itsmft::ROFRecord> mITSClusterROFRec; ///< input ITS clusters ROFRecord span gsl::span<const o2::ft0::RecPoints> mFITInfo; ///< optional input FIT info span - const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices + const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - const MCLabCont* mITSTrkLabels = nullptr; ///< input ITS Track MC labels - const MCLabCont* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels - const MCLabCont* mTPCTrkLabels = nullptr; ///< input TPC Track MC labels + const MCLabContCl* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels + MCLabSpan mITSTrkLabels; ///< input ITS Track MC labels + MCLabSpan mTPCTrkLabels; ///< input TPC Track MC labels /// <<<----- - std::vector<InteractionCandidate> mInteractions; ///< possible interaction times + std::vector<InteractionCandidate> mInteractions; ///< possible interaction times + std::vector<o2::dataformats::RangeRefComp<8>> mITSROFIntCandEntries; ///< entries of InteractionCandidate vector for every ITS ROF bin ///< container for record the match of TPC track to single ITS track - std::vector<matchRecord> mMatchRecordsTPC; - ///< container for reference to matchRecord involving particular ITS track - std::vector<matchRecord> mMatchRecordsITS; - - std::vector<int> mITSROFofTPCBin; ///< aux structure for mapping of TPC time-bins on ITS ROFs - std::vector<BracketF> mITSROFTimes; ///< min/max times of ITS ROFs in TPC time-bins - std::vector<TrackLocTPC> mTPCWork; ///< TPC track params prepared for matching - std::vector<TrackLocITS> mITSWork; ///< ITS track params prepared for matching - std::vector<o2::MCCompLabel> mTPCLblWork; ///< TPC track labels - std::vector<o2::MCCompLabel> mITSLblWork; ///< ITS track labels - std::vector<float> mWinnerChi2Refit; ///< vector of refitChi2 for winners + std::vector<MatchRecord> mMatchRecordsTPC; + ///< container for reference to MatchRecord involving particular ITS track + std::vector<MatchRecord> mMatchRecordsITS; + + std::vector<int> mITSROFofTPCBin; ///< aux structure for mapping of TPC time-bins on ITS ROFs + std::vector<BracketF> mITSROFTimes; ///< min/max times of ITS ROFs in TPC time-bins + std::vector<TrackLocTPC> mTPCWork; ///< TPC track params prepared for matching + std::vector<TrackLocITS> mITSWork; ///< ITS track params prepared for matching + MCLabContTr mTPCLblWork; ///< TPC track labels + MCLabContTr mITSLblWork; ///< ITS track labels + std::vector<float> mWinnerChi2Refit; ///< vector of refitChi2 for winners std::deque<ITSChipClustersRefs> mITSChipClustersRefs; ///< range of clusters for each chip in ITS (for AfterBurner) @@ -632,8 +723,8 @@ class MatchTPCITS ///< outputs tracks container std::vector<o2::dataformats::TrackTPCITS> mMatchedTracks; - std::vector<o2::MCCompLabel> mOutITSLabels; ///< ITS label of matched track - std::vector<o2::MCCompLabel> mOutTPCLabels; ///< TPC label of matched track + MCLabContTr mOutITSLabels; ///< ITS label of matched track + MCLabContTr mOutTPCLabels; ///< TPC label of matched track o2::its::RecoGeomHelper mRGHelper; ///< helper for cluster and geometry access float mITSFiducialZCut = 9999.; ///< eliminate TPC seeds outside of this range @@ -692,7 +783,7 @@ inline void MatchTPCITS::removeTPCfromITS(int tpcID, int itsID) if (isValidatedITS(tITS)) { return; } - int topID = MinusOne, next = tITS.matchID; // ITS matchRecord + int topID = MinusOne, next = tITS.matchID; // ITS MatchRecord while (next > MinusOne) { auto& rcITS = mMatchRecordsITS[next]; if (rcITS.partnerID == tpcID) { @@ -780,7 +871,7 @@ inline void MatchTPCITS::cleanAfterBurnerClusRefCache(int currentIC, int& startI { // check if some of cached cluster reference from tables startIC to currentIC can be released, // they will be necessarily in front slots of the mITSChipClustersRefs - while (startIC < currentIC && mInteractions[currentIC].timeBins.min() - mInteractions[startIC].timeBins.max() > MinTBToCleanCache) { + while (startIC < currentIC && mInteractions[currentIC].timeBins.getMin() - mInteractions[startIC].timeBins.getMax() > MinTBToCleanCache) { LOG(INFO) << "CAN REMOVE CACHE FOR " << startIC << " curent IC=" << currentIC; while (mInteractions[startIC].clRefPtr == &mITSChipClustersRefs.front()) { LOG(INFO) << "Reset cache pointer" << mInteractions[startIC].clRefPtr << " for IC=" << startIC; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h index 98a5ba13e19cd..c10f2c13b1621 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h @@ -24,8 +24,13 @@ namespace globaltracking { // There are configurable params for TPC-ITS matching -struct MatchITSTPCParams : public o2::conf::ConfigurableParamHelper<MatchITSTPCParams> { - bool runAfterBurner = false; +struct MatchTPCITSParams : public o2::conf::ConfigurableParamHelper<MatchTPCITSParams> { + enum ValidateMatchByFIT { Disable, + Prefer, + Require }; // flags for usage of FT0 in match validation + + bool runAfterBurner = false; ///< run afterburner for TPCtrack-ITScluster matching + ValidateMatchByFIT validateMatchByFIT = Prefer; ///< when comparing ITS-TPC matches, prefer those which have time of Interaction Candidate float crudeAbsDiffCut[o2::track::kNParams] = {2.f, 2.f, 0.2f, 0.2f, 4.f}; float crudeNSigma2Cut[o2::track::kNParams] = {49.f, 49.f, 49.f, 49.f, 49.f}; @@ -40,17 +45,25 @@ struct MatchITSTPCParams : public o2::conf::ConfigurableParamHelper<MatchITSTPCP int maxMatchCandidates = 5; ///< max allowed matching candidates per TPC track - int ABRequireToReachLayer = 5; ///< AB tracks should reach at least this layer from above + int requireToReachLayerAB = 5; ///< AB tracks should reach at least this layer from above + + float safeMarginTPCITSTimeBin = 1.f; ///< safety margin (in TPC time bins) for ITS-TPC tracks time (in TPC time bins!) comparison + + float safeMarginTPCTimeEdge = 20.f; ///< safety margin in cm when estimating TPC track tMin and tMax from assigned time0 and its track Z position + + float timeBinTolerance = 10.f; ///<tolerance in time-bin for ITS-TPC time bracket matching (not used ? TODO) - float TPCITSTimeBinSafeMargin = 1.f; ///< safety margin (in TPC time bins) for ITS-TPC tracks time (in TPC time bins!) comparison + float tpcTimeICMatchingNSigma = 4.; ///< nsigma for matching TPC corrected time and InteractionCandidate from FT0 - float TPCTimeEdgeZSafeMargin = 20.f; ///< safety margin in cm when estimating TPC track tMin and tMax from assigned time0 and its track Z position + float maxVDriftUncertainty = 0.; ///< max assumed VDrift uncertainty, used only in VDrift calibration mode - float TimeBinTolerance = 10.f; ///<tolerance in time-bin for ITS-TPC time bracket matching (not used ? TODO) + float maxTglForVDriftCalib = 1.; ///< maximum ITS tgl to collect data for VDrift calibration + int nBinsTglVDriftCalib = 50; ///< number of bins in reference ITS tgl for VDrift calibration + int nBinsDTglVDriftCalib = 100; ///< number of bins in delta tgl for VDrift calibration o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; /// Material correction type - O2ParamDef(MatchITSTPCParams, "tpcitsMatch"); + O2ParamDef(MatchTPCITSParams, "tpcitsMatch"); }; } // namespace globaltracking diff --git a/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h b/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h index 3463aa2d88977..74f3ce33f51aa 100644 --- a/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h +++ b/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h @@ -18,17 +18,13 @@ #pragma link C++ class o2::globaltracking::MatchTOF + ; #pragma link C++ class o2::globaltracking::TrackLocTPC + ; #pragma link C++ class o2::globaltracking::TrackLocITS + ; -#pragma link C++ class o2::globaltracking::MatchITSTPCParams + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::MatchITSTPCParams> + ; +#pragma link C++ class o2::globaltracking::MatchTPCITSParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::MatchTPCITSParams> + ; #pragma link C++ class o2::globaltracking::ABDebugLink + ; #pragma link C++ class o2::globaltracking::ABDebugTrack + ; #pragma link C++ class std::pair < o2::dataformats::EvIndex < int, int>, o2::dataformats::MatchInfoTOF> + ; #pragma link C++ class std::vector < std::pair < o2::dataformats::EvIndex < int, int>, o2::dataformats::MatchInfoTOF>> + ; -#pragma link C++ class std::vector < o2::dataformats::TrackTPCITS> + ; -#pragma link C++ class std::vector < o2::tpc::TrackTPC> + ; -#pragma link C++ class std::vector < o2::its::TrackITS> + ; -#pragma link C++ class std::vector < o2::tof::Cluster> + ; #endif diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index fff40892e56ff..9b3d77cddf307 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -19,7 +19,7 @@ #include "DetectorsBase/Propagator.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "CommonConstants/MathConstants.h" #include "CommonConstants/PhysicsConstants.h" @@ -36,8 +36,10 @@ #include "GlobalTracking/MatchTOF.h" #include "GlobalTracking/MatchTPCITS.h" +#include "TPCBase/ParameterGas.h" +#include "TPCBase/ParameterElectronics.h" + using namespace o2::globaltracking; -using timeEst = o2::dataformats::TimeStampWithError<float, float>; using evIdx = o2::dataformats::EvIndex<int, int>; ClassImp(MatchTOF); @@ -57,84 +59,73 @@ void MatchTOF::run() prepareTOFClusters(); mTimerTot.Stop(); - printf("Timing:\n"); - printf("prepareTOFCluster: "); - mTimerTot.Print(); + LOGF(INFO, "Timing prepareTOFCluster: Cpu: %.3e s Real: %.3e s in %d slots", mTimerTot.CpuTime(), mTimerTot.RealTime(), mTimerTot.Counter() - 1); mTimerTot.Start(); if (mIsworkflowON) { LOG(DEBUG) << "Number of entries in track tree = " << mCurrTracksTreeEntry; + + if (mIsITSused) { + prepareTracks(); + } else { + prepareTPCTracks(); + } + mMatchedTracks.clear(); mOutTOFLabels.clear(); mOutTPCLabels.clear(); mOutITSLabels.clear(); - prepareTracks(); mTimerTot.Stop(); - printf("Timing:\n"); - printf("prepare tracks: "); - mTimerTot.Print(); + LOGF(INFO, "Timing prepare tracks: Cpu: %.3e s Real: %.3e s in %d slots", mTimerTot.CpuTime(), mTimerTot.RealTime(), mTimerTot.Counter() - 1); mTimerTot.Start(); for (int sec = o2::constants::math::NSectors; sec--;) { LOG(INFO) << "Doing matching for sector " << sec << "..."; - doMatching(sec); + if (mIsITSused) { + doMatching(sec); + } else { + doMatchingForTPC(sec); + } LOG(INFO) << "...done. Now check the best matches"; selectBestMatches(); } - if (0) { // enabling this creates very verbose output - mTimerTot.Stop(); - printCandidatesTOF(); - mTimerTot.Start(false); - } } // we do the matching per entry of the TPCITS matched tracks tree while (!mIsworkflowON && mCurrTracksTreeEntry + 1 < mInputTreeTracks->GetEntries()) { // we add "+1" because mCurrTracksTreeEntry starts from -1, and it is incremented in loadTracksNextChunk which is called by prepareTracks LOG(DEBUG) << "Number of entries in track tree = " << mCurrTracksTreeEntry; + + if (mIsITSused) { + prepareTracks(); + } else { + prepareTPCTracks(); + } + mMatchedTracks.clear(); mOutTOFLabels.clear(); mOutTPCLabels.clear(); mOutITSLabels.clear(); - prepareTracks(); mTimerTot.Stop(); - printf("Timing:\n"); - printf("prepare tracks: "); - mTimerTot.Print(); + LOGF(INFO, "Timing prepare tracks: Cpu: %.3e s Real: %.3e s in %d slots", mTimerTot.CpuTime(), mTimerTot.RealTime(), mTimerTot.Counter() - 1); mTimerTot.Start(); - /* Uncomment for local debug - Printf("*************** Printing the tracks before starting the matching"); - - // printing the tracks - std::array<float, 3> globalPosTmp; - int totTracks = 0; - - for (int sec = o2::constants::math::NSectors; sec--;) { - Printf("\nsector %d", sec); - auto& cacheTrkTmp = mTracksSectIndexCache[sec]; // array of cached tracks indices for this sector; reminder: they are ordered in time! - for (int itrk = 0; itrk < cacheTrkTmp.size(); itrk++){ - auto& trc = mTracksWork[cacheTrkTmp[itrk]]; - trc.getXYZGlo(globalPosTmp); - LOG(INFO) << "Track" << totTracks << " [in this sector it is the " << itrk << "]: Global coordinates After propagating to 371 cm: globalPos[0] = " << globalPosTmp[0] << ", globalPos[1] = " << globalPosTmp[1] << ", globalPos[2] = " << globalPosTmp[2]; - LOG(INFO) << "The phi angle is " << TMath::ATan2(globalPosTmp[1], globalPosTmp[0]); - totTracks++; - } - } - */ - for (int sec = o2::constants::math::NSectors; sec--;) { LOG(INFO) << "Doing matching for sector " << sec << "..."; - doMatching(sec); + if (mIsITSused) { + doMatching(sec); + } else { + doMatchingForTPC(sec); + } LOG(INFO) << "...done. Now check the best matches"; selectBestMatches(); } - if (0) { // enabling this creates very verbose output - mTimerTot.Stop(); - printCandidatesTOF(); - mTimerTot.Start(false); - } + + mTimerTot.Stop(); + LOGF(INFO, "Timing Do Matching: Cpu: %.3e s Real: %.3e s in %d slots", mTimerTot.CpuTime(), mTimerTot.RealTime(), mTimerTot.Counter() - 1); + mTimerTot.Start(); + fill(); } @@ -146,22 +137,22 @@ void MatchTOF::run() mWFInputAttached = false; mTimerTot.Stop(); - printf("Timing:\n"); - printf("Do Matching: "); - mTimerTot.Print(); + LOGF(INFO, "Timing Do Matching: Cpu: %.3e s Real: %.3e s in %d slots", mTimerTot.CpuTime(), mTimerTot.RealTime(), mTimerTot.Counter() - 1); } //______________________________________________ void MatchTOF::fill() { mOutputTree->Fill(); - if (mOutputTreeCalib) + if (mOutputTreeCalib) { mOutputTreeCalib->Fill(); + } } //______________________________________________ void MatchTOF::run(const gsl::span<const o2::dataformats::TrackTPCITS>& trackArray, const gsl::span<const Cluster>& clusterArray, const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& toflab, const gsl::span<const o2::MCCompLabel>& itslab, const gsl::span<const o2::MCCompLabel>& tpclab) { + mIsITSused = true; mTracksArrayInp = trackArray; mTOFClustersArrayInp = clusterArray; mIsworkflowON = kTRUE; @@ -174,11 +165,27 @@ void MatchTOF::run(const gsl::span<const o2::dataformats::TrackTPCITS>& trackArr mSAInitDone = true; run(); } +//______________________________________________ +void MatchTOF::run(const gsl::span<const o2::tpc::TrackTPC>& trackArray, const gsl::span<const Cluster>& clusterArray, const o2::dataformats::MCTruthContainer<o2::MCCompLabel>& toflab, const gsl::span<const o2::MCCompLabel>& tpclab) +{ + mIsITSused = false; + mTPCTracksArrayInp = trackArray; + mTOFClustersArrayInp = clusterArray; + mIsworkflowON = kTRUE; + mTOFClusLabels = toflab; + mTPCLabels = tpclab; + mMCTruthON = (mTOFClusLabels.getNElements() && mTPCLabels.size()); + mWFInputAttached = true; + mSAInitDone = true; + + run(); +} //______________________________________________ void MatchTOF::init() { ///< initizalizations + mIsITSused = true; if (mSAInitDone) { LOG(ERROR) << "Initialization was already done"; @@ -192,16 +199,70 @@ void MatchTOF::init() LOG(INFO) << "Matched tracks will be stored in " << mOutTracksBranchName << " branch of tree " << mOutputTree->GetName(); if (mMCTruthON) { - mOutputTree->Branch(mOutTPCMCTruthBranchName.data(), &mOutTPCLabels); - LOG(INFO) << "ITS Tracks Labels branch: " << mOutITSMCTruthBranchName; mOutputTree->Branch(mOutITSMCTruthBranchName.data(), &mOutITSLabels); + LOG(INFO) << "ITS Tracks Labels branch: " << mOutITSMCTruthBranchName; + mOutputTree->Branch(mOutTPCMCTruthBranchName.data(), &mOutTPCLabels); + LOG(INFO) << "TPC Tracks Labels branch: " << mOutTPCMCTruthBranchName; + mOutputTree->Branch(mOutTOFMCTruthBranchName.data(), &mOutTOFLabels); + LOG(INFO) << "TOF Tracks Labels branch: " << mOutTOFMCTruthBranchName; + } + + } else { + LOG(INFO) << "Output tree is not attached, matched tracks will not be stored"; + } + + // create output branch for calibration info + if (mOutputTreeCalib) { + mOutputTreeCalib->Branch(mOutCalibBranchName.data(), &mCalibInfoTOF); + LOG(INFO) << "Calib infos will be stored in " << mOutCalibBranchName << " branch of tree " + << mOutputTreeCalib->GetName(); + } else { + LOG(INFO) << "Calib Output tree is not attached, calib infos will not be stored"; + } + +#ifdef _ALLOW_TOF_DEBUG_ + // debug streamer + if (mDBGFlags) { + mDBGOut = std::make_unique<o2::utils::TreeStreamRedirector>(mDebugTreeFileName.data(), "recreate"); + } +#endif + + mSAInitDone = true; + + { + mTimerTot.Stop(); + mTimerTot.Reset(); + } + + print(); +} +//______________________________________________ +void MatchTOF::initTPConly() +{ + ///< initizalizations + + mIsITSused = false; + + if (mSAInitDone) { + LOG(ERROR) << "Initialization was already done"; + return; + } + attachInputTreesTPConly(); + + // create output branch with track-tof matching + if (mOutputTree) { + mOutputTree->Branch(mOutTracksBranchName.data(), &mMatchedTracks); + LOG(INFO) << "Matched tracks will be stored in " << mOutTracksBranchName << " branch of tree " + << mOutputTree->GetName(); + if (mMCTruthON) { + mOutputTree->Branch(mOutTPCMCTruthBranchName.data(), &mOutTPCLabels); LOG(INFO) << "TPC Tracks Labels branch: " << mOutTPCMCTruthBranchName; mOutputTree->Branch(mOutTOFMCTruthBranchName.data(), &mOutTOFLabels); LOG(INFO) << "TOF Tracks Labels branch: " << mOutTOFMCTruthBranchName; } } else { - LOG(ERROR) << "Output tree is not attached, matched tracks will not be stored"; + LOG(INFO) << "Output tree is not attached, matched tracks will not be stored"; } // create output branch for calibration info @@ -255,20 +316,16 @@ void MatchTOF::printCandidatesTOF() const void MatchTOF::attachInputTrees() { ///< attaching the input tree - printf("attachInputTrees\n"); + LOG(DEBUG) << "attachInputTrees"; if (!mInputTreeTracks) { LOG(FATAL) << "Input tree with tracks is not set"; } - if (!mTreeTPCTracks) { - LOG(FATAL) << "TPC tracks data input tree is not set"; - } - if (!mTreeTOFClusters) { LOG(FATAL) << "TOF clusters data input tree is not set"; } - // input tracks (this is the pais of ITS-TPC matches) + // input tracks (this is the pairs of ITS-TPC matches) if (!mInputTreeTracks->GetBranch(mTracksBranchName.data())) { LOG(FATAL) << "Did not find tracks branch " << mTracksBranchName << " in the input tree"; @@ -291,18 +348,71 @@ void MatchTOF::attachInputTrees() mTOFClusLabelsPtr = &mTOFClusLabels; mTreeTOFClusters->SetBranchAddress(mTOFMCTruthBranchName.data(), &mTOFClusLabelsPtr); LOG(INFO) << "Found TOF Clusters MCLabels branch " << mTOFMCTruthBranchName; - } else + } else { mMCTruthON = false; + } if (mInputTreeTracks->GetBranch(mTPCMCTruthBranchName.data())) { mInputTreeTracks->SetBranchAddress(mTPCMCTruthBranchName.data(), &mTPCLabelsVect); LOG(INFO) << "Found TPC tracks MCLabels branch " << mTPCMCTruthBranchName.data(); - } else + } else { mMCTruthON = false; + } if (mInputTreeTracks->GetBranch(mITSMCTruthBranchName.data())) { mInputTreeTracks->SetBranchAddress(mITSMCTruthBranchName.data(), &mITSLabelsVect); LOG(INFO) << "Found ITS tracks MCLabels branch " << mITSMCTruthBranchName.data(); - } else + } else { + mMCTruthON = false; + } + + mCurrTracksTreeEntry = -1; + mCurrTOFClustersTreeEntry = -1; +} +//______________________________________________ +void MatchTOF::attachInputTreesTPConly() +{ + ///< attaching the input tree + LOG(DEBUG) << "attachInputTrees"; + + if (!mTreeTPCTracks) { + LOG(FATAL) << "TPC tracks data input tree is not set"; + } + + if (!mTreeTOFClusters) { + LOG(FATAL) << "TOF clusters data input tree is not set"; + } + + // input tracks (this is the TPC tracks) + + if (!mTreeTPCTracks->GetBranch(mTPCTracksBranchName.data())) { + LOG(FATAL) << "Did not find tracks branch " << mTPCTracksBranchName << " in the input tree"; + } + mTreeTPCTracks->SetBranchAddress(mTPCTracksBranchName.data(), &mTPCTracksArrayInpVect); + LOG(INFO) << "Attached tracks " << mTPCTracksBranchName << " branch with " << mTreeTPCTracks->GetEntries() + << " entries"; + + // input TOF clusters + + if (!mTreeTOFClusters->GetBranch(mTOFClusterBranchName.data())) { + LOG(FATAL) << "Did not find TOF clusters branch " << mTOFClusterBranchName << " in the input tree"; + } + mTreeTOFClusters->SetBranchAddress(mTOFClusterBranchName.data(), &mTOFClustersArrayInpVect); + LOG(INFO) << "Attached TOF clusters " << mTOFClusterBranchName << " branch with " << mTreeTOFClusters->GetEntries() + << " entries"; + // is there MC info available ? + mMCTruthON = true; + if (mTreeTOFClusters->GetBranch(mTOFMCTruthBranchName.data())) { + mTOFClusLabelsPtr = &mTOFClusLabels; + mTreeTOFClusters->SetBranchAddress(mTOFMCTruthBranchName.data(), &mTOFClusLabelsPtr); + LOG(INFO) << "Found TOF Clusters MCLabels branch " << mTOFMCTruthBranchName; + } else { mMCTruthON = false; + } + if (mTreeTPCTracks->GetBranch(mOutTPCTrackMCTruthBranchName.data())) { + mTreeTPCTracks->SetBranchAddress(mOutTPCTrackMCTruthBranchName.data(), &mTPCLabelsVect); + LOG(INFO) << "Found TPC tracks MCLabels branch " << mOutTPCTrackMCTruthBranchName; + } else { + mMCTruthON = false; + } mCurrTracksTreeEntry = -1; mCurrTOFClustersTreeEntry = -1; @@ -318,14 +428,17 @@ bool MatchTOF::prepareTracks() } mNumOfTracks = mTracksArrayInp.size(); - if (mNumOfTracks == 0) + if (mNumOfTracks == 0) { return false; // no tracks to be matched + } mMatchedTracksIndex.resize(mNumOfTracks); std::fill(mMatchedTracksIndex.begin(), mMatchedTracksIndex.end(), -1); // initializing all to -1 // copy the track params, propagate to reference X and build sector tables mTracksWork.clear(); + mLTinfos.clear(); mTracksWork.reserve(mNumOfTracks); + mLTinfos.reserve(mNumOfTracks); if (mMCTruthON) { mTracksLblWork.clear(); mTracksLblWork.reserve(mNumOfTracks); @@ -338,20 +451,22 @@ bool MatchTOF::prepareTracks() // getting Bz (mag field) auto o2field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); float bzField = o2field->solenoidField(); // magnetic field in kGauss + float maxInvPt = abs(bzField) > 0.1 ? 1. / (abs(bzField) * 0.05) : 999.; - Printf("\n\nWe have %d tracks to try to match to TOF", mNumOfTracks); + LOG(DEBUG) << "\n\nWe have %d tracks to try to match to TOF: " << mNumOfTracks; int nNotPropagatedToTOF = 0; for (int it = 0; it < mNumOfTracks; it++) { const o2::dataformats::TrackTPCITS& trcOrig = mTracksArrayInp[it]; // TODO: check if we cannot directly use the o2::track::TrackParCov class instead of o2::dataformats::TrackTPCITS, and then avoid the casting below; this is the track at the vertex std::array<float, 3> globalPos; // create working copy of track param - mTracksWork.emplace_back(trcOrig); //, mCurrTracksTreeEntry, it); + mTracksWork.emplace_back(std::make_pair(trcOrig.getParamOut(), trcOrig.getTimeMUS())); //, mCurrTracksTreeEntry, it); + mLTinfos.emplace_back(trcOrig.getLTIntegralOut()); // make a copy of the TPC track that we have to propagate //o2::tpc::TrackTPC* trc = new o2::tpc::TrackTPC(trcTPCOrig); // this would take the TPCout track //auto& trc = mTracksWork.back(); // with this we take the TPCITS track propagated to the vertex - auto& trc = mTracksWork.back().getParamOut(); // with this we take the TPCITS track propagated to the vertex - auto& intLT = mTracksWork.back().getLTIntegralOut(); // we get the integrated length from TPC-ITC outward propagation + auto& trc = mTracksWork.back().first; // with this we take the TPCITS track propagated to the vertex + auto& intLT = mLTinfos.back(); // we get the integrated length from TPC-ITC outward propagation if (trc.getX() < o2::globaltracking::MatchTPCITS::XTPCOuterRef - 1.) { // tpc-its track outward propagation did not reach outer ref.radius, skip this track nNotPropagatedToTOF++; @@ -379,9 +494,156 @@ bool MatchTOF::prepareTracks() 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::utils::Angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + LOG(DEBUG) << "The track will go to sector " << o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + mTracksSectIndexCache[o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0]))].push_back(it); + //delete trc; // Check: is this needed? + } + + LOG(INFO) << "Total number of tracks = " << mNumOfTracks << ", Number of tracks that failed to be propagated to TOF = " << nNotPropagatedToTOF; + + // sort tracks in each sector according to their time (increasing in time) + for (int sec = o2::constants::math::NSectors; sec--;) { + auto& indexCache = mTracksSectIndexCache[sec]; + LOG(INFO) << "Sorting sector" << sec << " | " << indexCache.size() << " tracks"; + if (!indexCache.size()) { + continue; + } + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& trcA = mTracksWork[a].second; + auto& trcB = mTracksWork[b].second; + return ((trcA.getTimeStamp() - mSigmaTimeCut * trcA.getTimeStampError()) - (trcB.getTimeStamp() - mSigmaTimeCut * trcB.getTimeStampError()) < 0.); + }); + } // loop over tracks of single sector + + // Uncomment for local debug + /* + // printing the tracks + std::array<float, 3> globalPos; + int itmp = 0; + for (int sec = o2::constants::math::NSectors; sec--;) { + Printf("sector %d", sec); + auto& cacheTrk = mTracksSectIndexCache[sec]; // array of cached tracks indices for this sector; reminder: they are ordered in time! + for (int itrk = 0; itrk < cacheTrk.size(); itrk++){ + itmp++; + auto& trc = mTracksWork[cacheTrk[itrk]]; + trc.getXYZGlo(globalPos); + printf("Track %d: Global coordinates After propagating to 371 cm: globalPos[0] = %f, globalPos[1] = %f, globalPos[2] = %f\n", itrk, globalPos[0], globalPos[1], globalPos[2]); + // Printf("The phi angle is %f", TMath::ATan2(globalPos[1], globalPos[0])); + } + } + Printf("we have %d tracks",itmp); + */ + + return true; +} +//______________________________________________ +bool MatchTOF::prepareTPCTracks() +{ + ///< prepare the tracks that we want to match to TOF + + if (!mIsworkflowON && !loadTPCTracksNextChunk()) { + return false; + } + + mNumOfTracks = mTPCTracksArrayInp.size(); + if (mNumOfTracks == 0) { + return false; // no tracks to be matched + } + mMatchedTracksIndex.resize(mNumOfTracks); + std::fill(mMatchedTracksIndex.begin(), mMatchedTracksIndex.end(), -1); // initializing all to -1 + + // copy the track params, propagate to reference X and build sector tables + mTracksWork.clear(); + mTracksWork.reserve(mNumOfTracks); + mSideTPC.clear(); + mSideTPC.reserve(mNumOfTracks); + mExtraTPCFwdTime.clear(); + mExtraTPCFwdTime.reserve(mNumOfTracks); + + for (int sec = o2::constants::math::NSectors; sec--;) { + mTPCTracksSectIndexCache[sec].clear(); + mTPCTracksSectIndexCache[sec].reserve(100 + 1.2 * mNumOfTracks / o2::constants::math::NSectors); + } + + // getting Bz (mag field) + auto o2field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); + float bzField = o2field->solenoidField(); // magnetic field in kGauss + float maxInvPt = abs(bzField) > 0.1 ? 1. / (abs(bzField) * 0.05) : 999.; + int nclustersMin = 0; + LOG(INFO) << "Max track Inv pT allowed = " << maxInvPt; + LOG(INFO) << "Min track Nclusters allowed = " << nclustersMin; + + LOG(DEBUG) << "\n\nWe have %d tracks to try to match to TOF: " << mNumOfTracks; + int nNotPropagatedToTOF = 0; + for (int it = 0; it < mNumOfTracks; it++) { + const o2::tpc::TrackTPC& trcOrig = mTPCTracksArrayInp[it]; // TODO: check if we cannot directly use the o2::track::TrackParCov class instead of o2::dataformats::TrackTPCITS, and then avoid the casting below; this is the track at the vertex + std::array<float, 3> globalPos; + + // create working copy of track param + timeEst timeInfo; + // set + timeInfo.setTimeStamp(trcOrig.getTime0() * o2::tpc::ParameterElectronics::Instance().ZbinWidth); + timeInfo.setTimeStampError((trcOrig.getDeltaTBwd() + 5) * o2::tpc::ParameterElectronics::Instance().ZbinWidth); + mSideTPC.push_back(trcOrig.hasASideClustersOnly() ? 1 : (trcOrig.hasCSideClustersOnly() ? -1 : 0)); + mExtraTPCFwdTime.push_back((trcOrig.getDeltaTFwd() + 5) * o2::tpc::ParameterElectronics::Instance().ZbinWidth); + + o2::track::TrackLTIntegral intLT0; //mTPCTracksWork.back().getLTIntegralOut(); // we get the integrated length from TPC-ITC outward propagation + // make a copy of the TPC track that we have to propagate + //o2::tpc::TrackTPC* trc = new o2::tpc::TrackTPC(trcTPCOrig); // this would take the TPCout track + mTracksWork.emplace_back(std::make_pair(trcOrig.getOuterParam(), timeInfo)); + auto& trc = mTracksWork.back().first; + auto& intLT = mLTinfos.emplace_back(intLT0); + + if (trcOrig.getNClusters() < nclustersMin) { + nNotPropagatedToTOF++; + continue; + } + + if (std::abs(trc.getQ2Pt()) > maxInvPt) { // tpc-its track outward propagation did not reach outer ref.radius, skip this track + nNotPropagatedToTOF++; + continue; + } + + // printf("N clusters = %d\n",trcOrig.getNClusters()); + +#ifdef _ALLOW_TOF_DEBUG_ + // propagate to matching Xref + trc.getXYZGlo(globalPos); + LOG(INFO) << "Global coordinates Before propagating to 371 cm: globalPos[0] = " << globalPos[0] << ", globalPos[1] = " << globalPos[1] << ", globalPos[2] = " << globalPos[2]; + LOG(INFO) << "Radius xy Before propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1]); + LOG(INFO) << "Radius xyz Before propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1] + globalPos[2] * globalPos[2]); + // the "very rough" propagation worked; now we can propagate considering also the cov matrix +#endif + + if (!propagateToRefXWithoutCov(trc, mXRef, 10, bzField)) { // we first propagate to 371 cm without considering the covariance matrix + nNotPropagatedToTOF++; + continue; + } - mTracksSectIndexCache[o2::utils::Angle2Sector(TMath::ATan2(globalPos[1], globalPos[0]))].push_back(it); + if (trc.getX() < o2::globaltracking::MatchTPCITS::XTPCOuterRef - 1.) { + if (!propagateToRefX(trc, o2::globaltracking::MatchTPCITS::XTPCOuterRef, 10, intLT) || 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 propagation without the errors succeeded? + nNotPropagatedToTOF++; + continue; + } + } + + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT) || 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 propagation without the errors succeeded? + nNotPropagatedToTOF++; + continue; + } + + trc.getXYZGlo(globalPos); + +#ifdef _ALLOW_TOF_DEBUG_ + LOG(INFO) << "Global coordinates After propagating to 371 cm: globalPos[0] = " << globalPos[0] << ", globalPos[1] = " << globalPos[1] << ", globalPos[2] = " << globalPos[2]; + LOG(INFO) << "Radius xy After propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1]); + LOG(INFO) << "Radius xyz After propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1] + globalPos[2] * globalPos[2]); + LOG(INFO) << "The track will go to sector " << o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); +#endif + + mTracksSectIndexCache[o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0]))].push_back(it); //delete trc; // Check: is this needed? } @@ -391,12 +653,13 @@ bool MatchTOF::prepareTracks() for (int sec = o2::constants::math::NSectors; sec--;) { auto& indexCache = mTracksSectIndexCache[sec]; LOG(INFO) << "Sorting sector" << sec << " | " << indexCache.size() << " tracks"; - if (!indexCache.size()) + if (!indexCache.size()) { continue; + } std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { - auto& trcA = mTracksWork[a]; - auto& trcB = mTracksWork[b]; - return ((trcA.getTimeMUS().getTimeStamp() - mSigmaTimeCut * trcA.getTimeMUS().getTimeStampError()) - (trcB.getTimeMUS().getTimeStamp() - mSigmaTimeCut * trcB.getTimeMUS().getTimeStampError()) < 0.); + auto& trcA = mTracksWork[a].second; + auto& trcB = mTracksWork[b].second; + return ((trcA.getTimeStamp() - trcA.getTimeStampError()) - (trcB.getTimeStamp() - trcB.getTimeStampError()) < 0.); }); } // loop over tracks of single sector @@ -474,8 +737,9 @@ bool MatchTOF::prepareTOFClusters() for (int sec = o2::constants::math::NSectors; sec--;) { auto& indexCache = mTOFClusSectIndexCache[sec]; LOG(INFO) << "Sorting sector" << sec << " | " << indexCache.size() << " TOF clusters"; - if (!indexCache.size()) + if (!indexCache.size()) { continue; + } std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { auto& clA = mTOFClusWork[a]; auto& clB = mTOFClusWork[b]; @@ -483,8 +747,9 @@ bool MatchTOF::prepareTOFClusters() }); } // loop over TOF clusters of single sector - if (mMatchedClustersIndex) + if (mMatchedClustersIndex) { delete[] mMatchedClustersIndex; + } mMatchedClustersIndex = new int[mNumOfClusters]; std::fill_n(mMatchedClustersIndex, mNumOfClusters, -1); // initializing all to -1 @@ -512,12 +777,29 @@ bool MatchTOF::loadTracksNextChunk() --mCurrTracksTreeEntry; return false; } +//_____________________________________________________ +bool MatchTOF::loadTPCTracksNextChunk() +{ + ///< load next chunk of tracks to be matched to TOF + while (++mCurrTracksTreeEntry < mTreeTPCTracks->GetEntries()) { + mTreeTPCTracks->GetEntry(mCurrTracksTreeEntry); + mTPCTracksArrayInp = gsl::span<const o2::tpc::TrackTPC>{*mTPCTracksArrayInpVect}; + LOG(INFO) << "Loading TPC tracks entry " << mCurrTracksTreeEntry << " -> " << mTPCTracksArrayInp.size() + << " tracks"; + if (!mTPCTracksArrayInp.size()) { + continue; + } + return true; + } + --mCurrTracksTreeEntry; + return false; +} //______________________________________________ bool MatchTOF::loadTOFClustersNextChunk() { - printf("Loat clusters next chunck\n"); + LOG(DEBUG) << "Loat clusters next chunck"; ///< load next chunk of clusters to be matched to TOF - printf("Loading TOF clusters: number of entries in tree = %lld\n", mTreeTOFClusters->GetEntries()); + LOG(DEBUG) << "Loading TOF clusters: number of entries in tree = " << mTreeTOFClusters->GetEntries(); while (++mCurrTOFClustersTreeEntry < mTreeTOFClusters->GetEntries()) { mTreeTOFClusters->GetEntry(mCurrTOFClustersTreeEntry); mTOFClustersArrayInp = gsl::span<const Cluster>{*mTOFClustersArrayInpVect}; @@ -536,22 +818,10 @@ bool MatchTOF::loadTOFClustersNextChunk() //______________________________________________ void MatchTOF::doMatching(int sec) { + ///< do the real matching per sector mMatchedTracksPairs.clear(); // new sector - //uncomment for local debug - /* - // printing the tracks - std::array<float, 3> globalPosTmp; - Printf("sector %d", sec); - auto& cacheTrkTmp = mTracksSectIndexCache[sec]; // array of cached tracks indices for this sector; reminder: they are ordered in time! - for (int itrk = 0; itrk < cacheTrkTmp.size(); itrk++){ - auto& trc = mTracksWork[cacheTrkTmp[itrk]]; - trc.getXYZGlo(globalPosTmp); - printf("Track %d: Global coordinates After propagating to 371 cm: globalPos[0] = %f, globalPos[1] = %f, globalPos[2] = %f\n", itrk, globalPosTmp[0], globalPosTmp[1], globalPosTmp[2]); - Printf("The phi angle is %f", TMath::ATan2(globalPosTmp[1], globalPosTmp[0])); - } - */ auto& cacheTOF = mTOFClusSectIndexCache[sec]; // array of cached TOF cluster indices for this sector; reminder: they are ordered in time! auto& cacheTrk = mTracksSectIndexCache[sec]; // array of cached tracks indices for this sector; reminder: they are ordered in time! int nTracks = cacheTrk.size(), nTOFCls = cacheTOF.size(); @@ -569,6 +839,8 @@ void MatchTOF::doMatching(int sec) std::array<float, 3> posBeforeProp; float posFloat[3]; + // prematching for TPC only tracks (identify BC candidate to correct z for TPC track accordingly to v_drift) + LOG(DEBUG) << "Trying to match %d tracks" << cacheTrk.size(); for (int itrk = 0; itrk < cacheTrk.size(); itrk++) { for (int ii = 0; ii < 2; ii++) { @@ -577,11 +849,12 @@ void MatchTOF::doMatching(int sec) } int nStripsCrossedInPropagation = 0; // how many strips were hit during the propagation auto& trackWork = mTracksWork[cacheTrk[itrk]]; - auto& trefTrk = trackWork.getParamOut(); - auto& intLT = trackWork.getLTIntegralOut(); + auto& trefTrk = trackWork.first; + auto& intLT = mLTinfos[cacheTrk[itrk]]; + // Printf("intLT (before doing anything): length = %f, time (Pion) = %f", intLT.getL(), intLT.getTOF(o2::track::PID::Pion)); - float minTrkTime = (trackWork.getTimeMUS().getTimeStamp() - mSigmaTimeCut * trackWork.getTimeMUS().getTimeStampError()) * 1.E6; // minimum time in ps - float maxTrkTime = (trackWork.getTimeMUS().getTimeStamp() + mSigmaTimeCut * trackWork.getTimeMUS().getTimeStampError()) * 1.E6; // maximum time in ps + float minTrkTime = (trackWork.second.getTimeStamp() - mSigmaTimeCut * trackWork.second.getTimeStampError()) * 1.E6; // minimum time in ps + float maxTrkTime = (trackWork.second.getTimeStamp() + mSigmaTimeCut * trackWork.second.getTimeStampError()) * 1.E6; // maximum time in ps int istep = 1; // number of steps float step = 1.0; // step size in cm //uncomment for local debug @@ -612,7 +885,7 @@ void MatchTOF::doMatching(int sec) double reachedPoint = mXRef + istep * step; while (propagateToRefX(trefTrk, reachedPoint, step, intLT) && nStripsCrossedInPropagation <= 2 && reachedPoint < Geo::RMAX) { - // while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trefTrk, mXRef + istep * step, o2::constants::physics::MassPionCharged, MAXSNP, step, 1, &intLT) && nStripsCrossedInPropagation <= 2 && mXRef + istep * step < Geo::RMAX) { + // while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trefTrk, mXRef + istep * step, MAXSNP, step, 1, &intLT) && nStripsCrossedInPropagation <= 2 && mXRef + istep * step < Geo::RMAX) { trefTrk.getXYZGlo(pos); for (int ii = 0; ii < 3; ii++) { // we need to change the type... @@ -625,13 +898,15 @@ void MatchTOF::doMatching(int sec) Printf("radius xyz = %f", TMath::Sqrt(posFloat[0]*posFloat[0] + posFloat[1]*posFloat[1] + posFloat[2]*posFloat[2])); */ - for (int idet = 0; idet < 5; idet++) + for (int idet = 0; idet < 5; idet++) { detIdTemp[idet] = -1; + } Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp); + reachedPoint += step; + if (detIdTemp[2] == -1) { - reachedPoint += step; continue; } @@ -668,11 +943,12 @@ void MatchTOF::doMatching(int sec) // LOG(DEBUG) << "nStripsCrossedInPropagation = " << nStripsCrossedInPropagation << ", detId[nStripsCrossedInPropagation][0] = " << detId[nStripsCrossedInPropagation][0] << ", detIdTemp[0] = " << detIdTemp[0] << ", detId[nStripsCrossedInPropagation][1] = " << detId[nStripsCrossedInPropagation][1] << ", detIdTemp[1] = " << detIdTemp[1] << ", detId[nStripsCrossedInPropagation][2] = " << detId[nStripsCrossedInPropagation][2] << ", detIdTemp[2] = " << detIdTemp[2]; if (nStripsCrossedInPropagation == 0 || // we are crossing a strip for the first time... (nStripsCrossedInPropagation >= 1 && (detId[nStripsCrossedInPropagation - 1][0] != detIdTemp[0] || detId[nStripsCrossedInPropagation - 1][1] != detIdTemp[1] || detId[nStripsCrossedInPropagation - 1][2] != detIdTemp[2]))) { // ...or we are crossing a new strip - if (nStripsCrossedInPropagation == 0) - // LOG(DEBUG) << "We cross a strip for the first time"; - if (nStripsCrossedInPropagation == 2) { - break; // we have already matched 2 strips, we cannot match more - } + if (nStripsCrossedInPropagation == 0) { + LOG(DEBUG) << "We cross a strip for the first time"; + } + if (nStripsCrossedInPropagation == 2) { + break; // we have already matched 2 strips, we cannot match more + } nStripsCrossedInPropagation++; } //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); @@ -747,27 +1023,36 @@ void MatchTOF::doMatching(int sec) int ndigits = 1; float posCorr[3] = {0, 0, 0}; - if (trefTOF.isBitSet(Cluster::kLeft)) + if (trefTOF.isBitSet(Cluster::kLeft)) { posCorr[0] += Geo::XPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kUpLeft)) + } + if (trefTOF.isBitSet(Cluster::kUpLeft)) { posCorr[0] += Geo::XPAD, posCorr[2] -= Geo::ZPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kDownLeft)) + } + if (trefTOF.isBitSet(Cluster::kDownLeft)) { posCorr[0] += Geo::XPAD, posCorr[2] += Geo::ZPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kUp)) + } + if (trefTOF.isBitSet(Cluster::kUp)) { posCorr[2] -= Geo::ZPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kDown)) + } + if (trefTOF.isBitSet(Cluster::kDown)) { posCorr[2] += Geo::ZPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kRight)) + } + if (trefTOF.isBitSet(Cluster::kRight)) { posCorr[0] -= Geo::XPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kUpRight)) + } + if (trefTOF.isBitSet(Cluster::kUpRight)) { posCorr[0] -= Geo::XPAD, posCorr[2] -= Geo::ZPAD, ndigits++; - if (trefTOF.isBitSet(Cluster::kDownRight)) + } + if (trefTOF.isBitSet(Cluster::kDownRight)) { posCorr[0] -= Geo::XPAD, posCorr[2] += Geo::ZPAD, ndigits++; + } + float ndifInv = 1. / ndigits; if (ndigits > 1) { - posCorr[0] /= ndigits; - posCorr[1] /= ndigits; - posCorr[2] /= ndigits; + posCorr[0] *= ndifInv; + posCorr[1] *= ndifInv; + posCorr[2] *= ndifInv; } int trackIdTOF; @@ -798,12 +1083,15 @@ void MatchTOF::doMatching(int sec) fillTOFmatchTreeWithLabels("matchPossibleWithLabels", cacheTOF[itof], indices[0], indices[1], indices[2], indices[3], indices[4], cacheTrk[itrk], iPropagation, detId[iPropagation][0], detId[iPropagation][1], detId[iPropagation][2], detId[iPropagation][3], detId[iPropagation][4], resX, resZ, res, trackWork, labelTPC.getTrackID(), labelTPC.getEventID(), labelTPC.getSourceID(), labelITS.getTrackID(), labelITS.getEventID(), labelITS.getSourceID(), tofLabelTrackID[0], tofLabelEventID[0], tofLabelSourceID[0], tofLabelTrackID[1], tofLabelEventID[1], tofLabelSourceID[1], tofLabelTrackID[2], tofLabelEventID[2], tofLabelSourceID[2], trkLTInt[iPropagation].getL(), trkLTInt[iPropagation].getTOF(o2::track::PID::Pion), trefTOF.getTime()); } #endif - if (indices[0] != detId[iPropagation][0]) + if (indices[0] != detId[iPropagation][0]) { continue; - if (indices[1] != detId[iPropagation][1]) + } + if (indices[1] != detId[iPropagation][1]) { continue; - if (indices[2] != detId[iPropagation][2]) + } + if (indices[2] != detId[iPropagation][2]) { continue; + } float chi2 = res; // TODO: take into account also the time! #ifdef _ALLOW_TOF_DEBUG_ fillTOFmatchTree("match1", cacheTOF[itof], indices[0], indices[1], indices[2], indices[3], indices[4], cacheTrk[itrk], iPropagation, detId[iPropagation][0], detId[iPropagation][1], detId[iPropagation][2], detId[iPropagation][3], detId[iPropagation][4], resX, resZ, res, trackWork, trkLTInt[iPropagation].getL(), trkLTInt[iPropagation].getTOF(o2::track::PID::Pion), trefTOF.getTime()); @@ -846,6 +1134,312 @@ void MatchTOF::doMatching(int sec) return; } //______________________________________________ +void MatchTOF::doMatchingForTPC(int sec) +{ + printf("here, DoMatch\n"); + auto& gasParam = o2::tpc::ParameterGas::Instance(); + float vdrift = gasParam.DriftV; + + ///< do the real matching per sector + mMatchedTracksPairs.clear(); // new sector + + auto& cacheTOF = mTOFClusSectIndexCache[sec]; // array of cached TOF cluster indices for this sector; reminder: they are ordered in time! + auto& cacheTrk = mTracksSectIndexCache[sec]; // array of cached tracks indices for this sector; reminder: they are ordered in time! + int nTracks = cacheTrk.size(), nTOFCls = cacheTOF.size(); + LOG(INFO) << "Matching sector " << sec << ": number of tracks: " << nTracks << ", number of TOF clusters: " << nTOFCls; + if (!nTracks || !nTOFCls) { + return; + } + int itof0 = 0; // starting index in TOF clusters for matching of the track + float deltaPosTemp[3]; + std::array<float, 3> pos; + std::array<float, 3> posBeforeProp; + float posFloat[3]; + + // prematching for TPC only tracks (identify BC candidate to correct z for TPC track accordingly to v_drift) + + std::vector<unsigned long> BCcand; + + std::vector<int> nStripsCrossedInPropagation; + std::vector<std::array<std::array<int, 5>, 2>> detId; + std::vector<std::array<o2::track::TrackLTIntegral, 2>> trkLTInt; + std::vector<std::array<std::array<float, 3>, 2>> deltaPos; + std::vector<std::array<int, 2>> nStepsInsideSameStrip; + + LOG(DEBUG) << "Trying to match %d tracks" << cacheTrk.size(); + for (int itrk = 0; itrk < cacheTrk.size(); itrk++) { + auto& trackWork = mTracksWork[cacheTrk[itrk]]; + auto& trefTrk = trackWork.first; + auto& intLT = mLTinfos[cacheTrk[itrk]]; + + int nBCcand = 1; + BCcand.clear(); + nStripsCrossedInPropagation.clear(); + + int side = mSideTPC[cacheTrk[itrk]]; + + // look at BC candidates for the track + itof0 = 0; + double minTrkTime = (trackWork.second.getTimeStamp() - trackWork.second.getTimeStampError()) * 1.E6; // minimum time in ps + double maxTrkTime = (trackWork.second.getTimeStamp() + mExtraTPCFwdTime[cacheTrk[itrk]]) * 1.E6; // maximum time in ps + + for (auto itof = itof0; itof < nTOFCls; itof++) { + auto& trefTOF = mTOFClusWork[cacheTOF[itof]]; + + if (trefTOF.getTime() < minTrkTime) { // this cluster has a time that is too small for the current track, we will get to the next one + itof0 = itof + 1; + continue; + } + + if (trefTOF.getTime() > maxTrkTime) { // this cluster has a time that is too large for the current track, close loop + break; + } + + if ((trefTOF.getZ() * side < 0) && ((side > 0) != (trackWork.first.getTgl() > 0))) { + continue; + } + + unsigned long bc = (unsigned long)(trefTOF.getTime() * Geo::BC_TIME_INPS_INV); + + bool isalreadyin = false; + + for (int k = 0; k < BCcand.size(); k++) { + if (bc == BCcand[k]) { + isalreadyin = true; + } + } + + if (!isalreadyin) { + BCcand.emplace_back(bc); + nStripsCrossedInPropagation.emplace_back(0); + } + } + detId.clear(); + detId.reserve(BCcand.size()); + trkLTInt.clear(); + trkLTInt.reserve(BCcand.size()); + deltaPos.clear(); + deltaPos.reserve(BCcand.size()); + nStepsInsideSameStrip.clear(); + nStepsInsideSameStrip.reserve(BCcand.size()); + + // printf("%d) ts_error = %f -- z_error = %f\n", itrk, trackWork.second.getTimeStampError(), trackWork.second.getTimeStampError() * vdrift); + + // Printf("intLT (before doing anything): length = %f, time (Pion) = %f", intLT.getL(), intLT.getTOF(o2::track::PID::Pion)); + int istep = 1; // number of steps + float step = 1.0; // step size in cm + //uncomment for local debug + /* + //trefTrk.getXYZGlo(posBeforeProp); + //float posBeforeProp[3] = {trefTrk.getX(), trefTrk.getY(), trefTrk.getZ()}; // in local ref system + //printf("Global coordinates: posBeforeProp[0] = %f, posBeforeProp[1] = %f, posBeforeProp[2] = %f\n", posBeforeProp[0], posBeforeProp[1], posBeforeProp[2]); + //Printf("Radius xy = %f", TMath::Sqrt(posBeforeProp[0]*posBeforeProp[0] + posBeforeProp[1]*posBeforeProp[1])); + //Printf("Radius xyz = %f", TMath::Sqrt(posBeforeProp[0]*posBeforeProp[0] + posBeforeProp[1]*posBeforeProp[1] + posBeforeProp[2]*posBeforeProp[2])); + */ + +#ifdef _ALLOW_TOF_DEBUG_ + if (mDBGFlags) { + (*mDBGOut) << "propOK" + << "track=" << trefTrk << "\n"; + } +#endif + + int detIdTemp[5] = {-1, -1, -1, -1, -1}; // TOF detector id at the current propagation point + + double reachedPoint = mXRef + istep * step; + + // initializing + for (int ibc = 0; ibc < BCcand.size(); ibc++) { + for (int ii = 0; ii < 2; ii++) { + nStepsInsideSameStrip[ibc][ii] = 0; + for (int iii = 0; iii < 5; iii++) { + detId[ibc][ii][iii] = -1; + } + } + } + while (propagateToRefX(trefTrk, reachedPoint, step, intLT) && reachedPoint < Geo::RMAX) { + // while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trefTrk, mXRef + istep * step, MAXSNP, step, 1, &intLT) && nStripsCrossedInPropagation <= 2 && mXRef + istep * step < Geo::RMAX) { + + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + // uncomment below only for local debug; this will produce A LOT of output - one print per propagation step + /* + Printf("posFloat[0] = %f, posFloat[1] = %f, posFloat[2] = %f", posFloat[0], posFloat[1], posFloat[2]); + Printf("radius xy = %f", TMath::Sqrt(posFloat[0]*posFloat[0] + posFloat[1]*posFloat[1])); + Printf("radius xyz = %f", TMath::Sqrt(posFloat[0]*posFloat[0] + posFloat[1]*posFloat[1] + posFloat[2]*posFloat[2])); + */ + + reachedPoint += step; + + // check if you fall in a strip + for (int ibc = 0; ibc < BCcand.size(); ibc++) { + for (int idet = 0; idet < 5; idet++) { + detIdTemp[idet] = -1; + } + + if (side > 0) { + posFloat[2] = pos[2] - vdrift * (trackWork.second.getTimeStamp() - BCcand[ibc] * Geo::BC_TIME_INPS * 1E-6); + } else if (side < 0) { + posFloat[2] = pos[2] + vdrift * (trackWork.second.getTimeStamp() - BCcand[ibc] * Geo::BC_TIME_INPS * 1E-6); + } else { + posFloat[2] = pos[2]; + } + + Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp); + + if (detIdTemp[2] == -1) { + continue; + } + + if (nStripsCrossedInPropagation[ibc] == 0 || // we are crossing a strip for the first time... + (nStripsCrossedInPropagation[ibc] >= 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]))) { // ...or we are crossing a new strip + if (nStripsCrossedInPropagation[ibc] == 0) { + LOG(DEBUG) << "We cross a strip for the first time"; + } + if (nStripsCrossedInPropagation[ibc] == 2) { + continue; // we have already matched 2 strips, we cannot match more + } + nStripsCrossedInPropagation[ibc]++; + } + + //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); + if (nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1] == 0) { + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][0] = detIdTemp[0]; + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = detIdTemp[1]; + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = detIdTemp[2]; + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3] = detIdTemp[3]; + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4] = detIdTemp[4]; + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] = deltaPosTemp[0]; + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = deltaPosTemp[1]; + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = deltaPosTemp[2]; + + trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 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[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]++; + } + } + } + for (int ibc = 0; ibc < BCcand.size(); ibc++) { + float minTime = (BCcand[ibc] - 1) * Geo::BC_TIME_INPS; + float maxTime = (BCcand[ibc] + 1) * Geo::BC_TIME_INPS; + for (Int_t imatch = 0; imatch < nStripsCrossedInPropagation[ibc]; imatch++) { + // we take as residual the average of the residuals along the propagation in the same strip + deltaPos[ibc][imatch][0] /= nStepsInsideSameStrip[ibc][imatch]; + deltaPos[ibc][imatch][1] /= nStepsInsideSameStrip[ibc][imatch]; + deltaPos[ibc][imatch][2] /= nStepsInsideSameStrip[ibc][imatch]; + // LOG(DEBUG) << "matched strip " << imatch << ": deltaPos[0] = " << deltaPos[imatch][0] << ", deltaPos[1] = " << deltaPos[imatch][1] << ", deltaPos[2] = " << deltaPos[imatch][2] << ", residual (x, z) = " << TMath::Sqrt(deltaPos[imatch][0] * deltaPos[imatch][0] + deltaPos[imatch][2] * deltaPos[imatch][2]); + } + + if (nStripsCrossedInPropagation[ibc] == 0) { + continue; // the track never hit a TOF strip during the propagation + } + + bool foundCluster = false; + itof0 = 0; + for (auto itof = itof0; itof < nTOFCls; itof++) { + // printf("itof = %d\n", itof); + auto& trefTOF = mTOFClusWork[cacheTOF[itof]]; + // compare the times of the track and the TOF clusters - remember that they both are ordered in time! + //Printf("trefTOF.getTime() = %f, maxTrkTime = %f, minTrkTime = %f", trefTOF.getTime(), maxTrkTime, minTrkTime); + + if (trefTOF.getTime() < minTime) { // this cluster has a time that is too small for the current track, we will get to the next one + //Printf("In trefTOF.getTime() < minTrkTime"); + itof0 = itof + 1; // but for the next track that we will check, we will ignore this cluster (the time is anyway too small) + continue; + } + if (trefTOF.getTime() > maxTime) { // no more TOF clusters can be matched to this track + break; + } + + int mainChannel = trefTOF.getMainContributingChannel(); + int indices[5]; + Geo::getVolumeIndices(mainChannel, indices); + + // compute fine correction using cluster position instead of pad center + // this because in case of multiple-hit cluster position is averaged on all pads contributing to the cluster (then error position matrix can be used for Chi2 if nedeed) + int ndigits = 1; + float posCorr[3] = {0, 0, 0}; + + if (trefTOF.isBitSet(Cluster::kLeft)) { + posCorr[0] += Geo::XPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kUpLeft)) { + posCorr[0] += Geo::XPAD, posCorr[2] -= Geo::ZPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kDownLeft)) { + posCorr[0] += Geo::XPAD, posCorr[2] += Geo::ZPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kUp)) { + posCorr[2] -= Geo::ZPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kDown)) { + posCorr[2] += Geo::ZPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kRight)) { + posCorr[0] -= Geo::XPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kUpRight)) { + posCorr[0] -= Geo::XPAD, posCorr[2] -= Geo::ZPAD, ndigits++; + } + if (trefTOF.isBitSet(Cluster::kDownRight)) { + posCorr[0] -= Geo::XPAD, posCorr[2] += Geo::ZPAD, ndigits++; + } + + float ndifInv = 1. / ndigits; + if (ndigits > 1) { + posCorr[0] *= ndifInv; + posCorr[1] *= ndifInv; + posCorr[2] *= ndifInv; + } + + int trackIdTOF; + int eventIdTOF; + int sourceIdTOF; + for (auto iPropagation = 0; iPropagation < nStripsCrossedInPropagation[ibc]; iPropagation++) { + LOG(DEBUG) << "TOF Cluster [" << itof << ", " << cacheTOF[itof] << "]: indices = " << indices[0] << ", " << indices[1] << ", " << indices[2] << ", " << indices[3] << ", " << indices[4]; + LOG(DEBUG) << "Propagated Track [" << itrk << ", " << cacheTrk[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 res = TMath::Sqrt(resX * resX + resZ * resZ); + if (indices[0] != detId[ibc][iPropagation][0]) { + continue; + } + if (indices[1] != detId[ibc][iPropagation][1]) { + continue; + } + if (indices[2] != detId[ibc][iPropagation][2]) { + continue; + } + LOG(DEBUG) << "resX = " << resX << ", resZ = " << resZ << ", res = " << res; + float chi2 = res; // TODO: take into account also the time! + + if (res < mSpaceTolerance) { // matching ok! + LOG(DEBUG) << "MATCHING FOUND: We have a match! between track " << mTracksSectIndexCache[indices[0]][itrk] << " and TOF cluster " << mTOFClusSectIndexCache[indices[0]][itof]; + foundCluster = true; + // set event indexes (to be checked) + evIdx eventIndexTOFCluster(trefTOF.getEntryInTree(), mTOFClusSectIndexCache[indices[0]][itof]); + evIdx eventIndexTracks(mCurrTracksTreeEntry, mTracksSectIndexCache[indices[0]][itrk]); + mMatchedTracksPairs.emplace_back(o2::dataformats::MatchInfoTOF(eventIndexTOFCluster, chi2, trkLTInt[ibc][iPropagation], eventIndexTracks)); // TODO: check if this is correct! + } + } + } + if (!foundCluster && mMCTruthON) { + const auto& labelTPC = mTPCLabels[mTracksSectIndexCache[sec][itrk]]; + LOG(DEBUG) << "We did not find any TOF cluster for track " << cacheTrk[itrk] << " (label = " << labelTPC << ", pt = " << trefTrk.getPt(); + } + } + } + return; +} +//______________________________________________ int MatchTOF::findFITIndex(int bc) { if (mFITRecPoints.size() == 0) { @@ -860,8 +1454,9 @@ int MatchTOF::findFITIndex(int bc) int bct0 = ir.orbit * o2::constants::lhc::LHCMaxBunches + ir.bc; int dist = bc - bct0; - if (dist < 0 || dist > distMax) + if (dist < 0 || dist > distMax) { continue; + } distMax = dist; index = i; @@ -874,7 +1469,7 @@ void MatchTOF::selectBestMatches() { ///< define the track-TOFcluster pair per sector - printf("Number of pair matched = %lu\n", mMatchedTracksPairs.size()); + LOG(INFO) << "Number of pair matched = " << mMatchedTracksPairs.size(); // first, we sort according to the chi2 std::sort(mMatchedTracksPairs.begin(), mMatchedTracksPairs.end(), [this](o2::dataformats::MatchInfoTOF& a, o2::dataformats::MatchInfoTOF& b) { return (a.getChi2() < b.getChi2()); }); @@ -906,17 +1501,15 @@ void MatchTOF::selectBestMatches() // add also calibration infos mCalibInfoTOF.emplace_back(mTOFClusWork[matchingPair.getTOFClIndex()].getMainContributingChannel(), int(mTOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() * 1E12), // add time stamp - mTOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - matchingPair.getLTIntegralOut().getTOF(o2::track::PID::Pion) - t0info, + mTOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - mLTinfos[matchingPair.getTrackIndex()].getTOF(o2::track::PID::Pion) - t0info, mTOFClusWork[matchingPair.getTOFClIndex()].getTot()); if (mMCTruthON) { const auto& labelsTOF = mTOFClusLabels.getLabels(matchingPair.getTOFClIndex()); const auto& labelTPC = mTPCLabels[matchingPair.getTrackIndex()]; - const auto& labelITS = mITSLabels[matchingPair.getTrackIndex()]; // we want to store positive labels independently of how they are flagged from TPC,ITS people // o2::MCCompLabel labelTPC(abs(labelTPCor.getTrackID()), labelTPCor.getEventID(), labelTPCor.getSourceID()); // o2::MCCompLabel labelITS(abs(labelITSor.getTrackID()), labelITSor.getEventID(), labelITSor.getSourceID()); LOG(DEBUG) << "TPC label" << labelTPC; - LOG(DEBUG) << "ITS label" << labelITS; bool labelOk = false; // whether we have found or not the same TPC label of the track among the labels of the TOF cluster for (int ilabel = 0; ilabel < labelsTOF.size(); ilabel++) { @@ -929,17 +1522,21 @@ void MatchTOF::selectBestMatches() if (!labelOk) { // 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 //assert(labelsTOF.size()); - if (!labelsTOF.size()) + if (!labelsTOF.size()) { throw std::runtime_error("TOF label not found since size of label is zero. This should not happen!!!!"); + } mOutTOFLabels.emplace_back(labelsTOF[0].getTrackID(), labelsTOF[0].getEventID(), labelsTOF[0].getSourceID(), true); } mOutTPCLabels.push_back(labelTPC); - mOutITSLabels.push_back(labelITS); + if (mIsITSused) { + const auto& labelITS = mITSLabels[matchingPair.getTrackIndex()]; + LOG(DEBUG) << "ITS label" << labelITS; + mOutITSLabels.push_back(labelITS); + } } i++; } } - //______________________________________________ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float stepInCm, o2::track::TrackLTIntegral& intLT) { @@ -949,10 +1546,11 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st bool refReached = false; float xStart = trc.getX(); // the first propagation will be from 2m, if the track is not at least at 2m - if (xStart < 50.) + if (xStart < 50.) { xStart = 50.; + } int istep = 1; - bool hasPropagated = o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, xStart + istep * stepInCm, o2::constants::physics::MassPionCharged, MAXSNP, stepInCm, matCorr, &intLT); + bool hasPropagated = o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, xStart + istep * stepInCm, MAXSNP, stepInCm, matCorr, &intLT); while (hasPropagated) { if (trc.getX() > xRef) { refReached = true; // we reached the 371cm reference @@ -961,15 +1559,16 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st if (fabs(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::utils::Angle2Alpha(trc.getPhiPos()); + auto alphaNew = o2::math_utils::angle2Alpha(trc.getPhiPos()); if (!trc.rotate(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) + if (refReached) { break; - hasPropagated = o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, xStart + istep * stepInCm, o2::constants::physics::MassPionCharged, MAXSNP, stepInCm, matCorr, &intLT); + } + hasPropagated = o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, xStart + istep * stepInCm, MAXSNP, stepInCm, matCorr, &intLT); } // if (std::abs(trc.getSnp()) > MAXSNP) Printf("propagateToRefX: condition on snp not ok, returning false"); @@ -987,8 +1586,9 @@ bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef 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.) + if (xStart < 50.) { xStart = 50.; + } int istep = 1; bool hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); while (hasPropagated) { @@ -999,14 +1599,15 @@ bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef 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::utils::Angle2Alpha(trcNoCov.getPhiPos()); + 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) + 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"); @@ -1027,7 +1628,7 @@ void MatchTOF::setDebugFlag(UInt_t flag, bool on) } //_________________________________________________________ -void MatchTOF::fillTOFmatchTree(const char* trname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, o2::dataformats::TrackTPCITS& trk, float intLength, float intTimePion, float timeTOF) +void MatchTOF::fillTOFmatchTree(const char* trname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, matchTrack& trk, float intLength, float intTimePion, float timeTOF) { ///< fill debug tree for TOF tracks matching check @@ -1039,13 +1640,13 @@ void MatchTOF::fillTOFmatchTree(const char* trname, int cacheTOF, int sectTOF, i (*mDBGOut) << trname << "clusterTOF=" << cacheTOF << "sectTOF=" << sectTOF << "plateTOF=" << plateTOF << "stripTOF=" << stripTOF << "padXTOF=" << padXTOF << "padZTOF=" << padZTOF << "crossedStrip=" << crossedStrip << "sectPropagation=" << sectPropagation << "platePropagation=" << platePropagation << "stripPropagation=" << stripPropagation << "padXPropagation=" << padXPropagation - << "resX=" << resX << "resZ=" << resZ << "res=" << res << "track=" << trk << "intLength=" << intLength << "intTimePion=" << intTimePion << "timeTOF=" << timeTOF << "\n"; + << "resX=" << resX << "resZ=" << resZ << "res=" << res << "track=" << trk.first << "intLength=" << intLength << "intTimePion=" << intTimePion << "timeTOF=" << timeTOF << "\n"; } mTimerDBG.Stop(); } //_________________________________________________________ -void MatchTOF::fillTOFmatchTreeWithLabels(const char* trname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, o2::dataformats::TrackTPCITS& trk, int TPClabelTrackID, int TPClabelEventID, int TPClabelSourceID, int ITSlabelTrackID, int ITSlabelEventID, int ITSlabelSourceID, int TOFlabelTrackID0, int TOFlabelEventID0, int TOFlabelSourceID0, int TOFlabelTrackID1, int TOFlabelEventID1, int TOFlabelSourceID1, int TOFlabelTrackID2, int TOFlabelEventID2, int TOFlabelSourceID2, float intLength, float intTimePion, float timeTOF) +void MatchTOF::fillTOFmatchTreeWithLabels(const char* trname, int cacheTOF, int sectTOF, int plateTOF, int stripTOF, int padXTOF, int padZTOF, int cacheeTrk, int crossedStrip, int sectPropagation, int platePropagation, int stripPropagation, int padXPropagation, int padZPropagation, float resX, float resZ, float res, matchTrack& trk, int TPClabelTrackID, int TPClabelEventID, int TPClabelSourceID, int ITSlabelTrackID, int ITSlabelEventID, int ITSlabelSourceID, int TOFlabelTrackID0, int TOFlabelEventID0, int TOFlabelSourceID0, int TOFlabelTrackID1, int TOFlabelEventID1, int TOFlabelSourceID1, int TOFlabelTrackID2, int TOFlabelEventID2, int TOFlabelSourceID2, float intLength, float intTimePion, float timeTOF) { ///< fill debug tree for TOF tracks matching check @@ -1055,7 +1656,7 @@ void MatchTOF::fillTOFmatchTreeWithLabels(const char* trname, int cacheTOF, int (*mDBGOut) << trname << "clusterTOF=" << cacheTOF << "sectTOF=" << sectTOF << "plateTOF=" << plateTOF << "stripTOF=" << stripTOF << "padXTOF=" << padXTOF << "padZTOF=" << padZTOF << "crossedStrip=" << crossedStrip << "sectPropagation=" << sectPropagation << "platePropagation=" << platePropagation << "stripPropagation=" << stripPropagation << "padXPropagation=" << padXPropagation - << "resX=" << resX << "resZ=" << resZ << "res=" << res << "track=" << trk + << "resX=" << resX << "resZ=" << resZ << "res=" << res << "track=" << trk.first << "TPClabelTrackID=" << TPClabelTrackID << "TPClabelEventID=" << TPClabelEventID << "TPClabelSourceID=" << TPClabelSourceID << "ITSlabelTrackID=" << ITSlabelTrackID << "ITSlabelEventID=" << ITSlabelEventID << "ITSlabelSourceID=" << ITSlabelSourceID << "TOFlabelTrackID0=" << TOFlabelTrackID0 << "TOFlabelEventID0=" << TOFlabelEventID0 << "TOFlabelSourceID0=" << TOFlabelSourceID0 diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 6abd5627a7a89..2f528a3ca4d9e 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -24,7 +24,7 @@ #include "TPCBase/ParameterElectronics.h" #include "TPCBase/ParameterDetector.h" #include "TPCBase/ParameterGas.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "CommonConstants/MathConstants.h" #include "CommonConstants/PhysicsConstants.h" @@ -65,14 +65,14 @@ void MatchTPCITS::printABTracksTree(const ABTrackLinksList& llist) const return; } o2::MCCompLabel lblTrc; - if (mTPCTrkLabels) { - lblTrc = mTPCTrkLabels->getLabels(mTPCWork[llist.trackID].sourceID)[0]; + if (mMCTruthON) { + lblTrc = mTPCTrkLabels[mTPCWork[llist.trackID].sourceID]; } LOG(INFO) << "Matches for track " << llist.trackID << " lowest lr: " << int(llist.lowestLayer) << " " << lblTrc << " pT= " << mTPCWork[llist.trackID].getPt(); auto printHyp = [this, &lblTrc](int nextHyp, int cnt) { - printf("#%d Lr/IC/Lnk/ClID/Chi2/Chi2Nrm/{MC}:%c ", cnt++, mTPCTrkLabels ? (lblTrc.isFake() ? 'F' : 'C') : '-'); + printf("#%d Lr/IC/Lnk/ClID/Chi2/Chi2Nrm/{MC}:%c ", cnt++, mTPCTrkLabels.size() ? (lblTrc.isFake() ? 'F' : 'C') : '-'); int nFakeClus = 0, nTotClus = 0, parID = nextHyp; // print particular hypothesis while (1) { const auto& lnk = mABTrackLinks[parID]; @@ -90,7 +90,7 @@ void MatchTPCITS::printABTracksTree(const ABTrackLinksList& llist) const mcEv = mcTr = -999; // noise nFakeClus++; } - } else if (lnk.isDummyTop() && mTPCTrkLabels) { // top layer, use TPC MC lbl + } else if (lnk.isDummyTop() && mMCTruthON) { // top layer, use TPC MC lbl mcEv = lblTrc.getEventID(); mcTr = lblTrc.getTrackID(); } @@ -104,7 +104,7 @@ void MatchTPCITS::printABTracksTree(const ABTrackLinksList& llist) const }; int cnt = 0; // tmp - for (int lowest = llist.lowestLayer; lowest <= mParams->ABRequireToReachLayer; lowest++) { + for (int lowest = llist.lowestLayer; lowest <= mParams->requireToReachLayerAB; lowest++) { int nextHyp = llist.firstInLr[lowest]; while (nextHyp > MinusOne) { if (mABTrackLinks[nextHyp].nDaughters == 0) { // select only head links, w/o daughters @@ -138,11 +138,11 @@ void MatchTPCITS::dumpABTracksDebugTree(const ABTrackLinksList& llist) } LOG(INFO) << "Dump AB Matches for track " << llist.trackID; o2::MCCompLabel lblTrc; - if (mTPCTrkLabels) { - lblTrc = mTPCTrkLabels->getLabels(mTPCWork[llist.trackID].sourceID)[0]; // tmp + if (mMCTruthON) { + lblTrc = mTPCTrkLabels[mTPCWork[llist.trackID].sourceID]; // tmp } int ord = 0; - for (int lowest = llist.lowestLayer; lowest <= mParams->ABRequireToReachLayer; lowest++) { + for (int lowest = llist.lowestLayer; lowest <= mParams->requireToReachLayerAB; lowest++) { int nextHyp = llist.firstInLr[lowest]; while (nextHyp > MinusOne) { if (mABTrackLinks[nextHyp].nDaughters != 0) { // select only head links, w/o daughters @@ -248,10 +248,12 @@ void MatchTPCITS::run() LOG(FATAL) << "init() was not done yet"; } + updateTimeDependentParams(); + ProcInfo_t procInfoStart, procInfoStop; gSystem->GetProcInfo(&procInfoStart); constexpr uint64_t kMB = 1024 * 1024; - printf("Memory (GB) at entrance: RSS: %.3f VMem: %.3f\n", float(procInfoStart.fMemResident) / kMB, float(procInfoStart.fMemVirtual) / kMB); + LOGF(info, "Memory (GB) at entrance: RSS: %.3f VMem: %.3f\n", float(procInfoStart.fMemResident) / kMB, float(procInfoStart.fMemVirtual) / kMB); mTimer[SWTot].Start(false); @@ -260,6 +262,10 @@ void MatchTPCITS::run() if (!prepareITSTracks() || !prepareTPCTracks() || !prepareFITInfo()) { return; } + if (mUseFT0) { + prepareInteractionTimes(); + } + mTimer[SWDoMatching].Start(false); for (int sec = o2::constants::math::NSectors; sec--;) { doMatching(sec); @@ -276,7 +282,7 @@ void MatchTPCITS::run() refitWinners(); - if (Params::Instance().runAfterBurner) { + if (mUseFT0 && Params::Instance().runAfterBurner) { runAfterBurner(); } @@ -325,6 +331,7 @@ void MatchTPCITS::init() mTimer[i].Reset(); } mParams = &Params::Instance(); + mFT0Params = &o2::ft0::InteractionTag::Instance(); setUseMatCorrFlag(mParams->matCorr); auto* prop = o2::base::Propagator::Instance(); if (!prop->getMatLUT() && mParams->matCorr == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { @@ -333,40 +340,18 @@ void MatchTPCITS::init() } // make sure T2GRot matrices are loaded into ITS geometry helper - o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2GRot)); + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)); mSectEdgeMargin2 = mParams->crudeAbsDiffCut[o2::track::kY] * mParams->crudeAbsDiffCut[o2::track::kY]; ///< precalculated ^2 - - auto& gasParam = o2::tpc::ParameterGas::Instance(); - auto& elParam = o2::tpc::ParameterElectronics::Instance(); - auto& detParam = o2::tpc::ParameterDetector::Instance(); - mTPCTBinMUS = elParam.ZbinWidth; - mTPCVDrift0 = gasParam.DriftV; - mTPCZMax = detParam.TPClength; - - assert(mITSROFrameLengthMUS > 0.0f); - if (mITSTriggered) { - mITSROFrame2TPCBin = mITSROFrameLengthMUS / mTPCTBinMUS; - } else { - mITSROFrame2TPCBin = mITSROFrameLengthMUS / mTPCTBinMUS; // RSTODO use both ITS and TPC times BCs once will be available for TPC also - } - mTPCBin2ITSROFrame = 1. / mITSROFrame2TPCBin; - mTPCBin2Z = mTPCTBinMUS * mTPCVDrift0; - mZ2TPCBin = 1. / mTPCBin2Z; - mTPCVDrift0Inv = 1. / mTPCVDrift0; - mNTPCBinsFullDrift = mTPCZMax * mZ2TPCBin; - - mTPCTimeEdgeTSafeMargin = z2TPCBin(mParams->TPCTimeEdgeZSafeMargin); - std::unique_ptr<TPCTransform> fastTransform = (o2::tpc::TPCFastTransformHelperO2::instance()->create(0)); mTPCTransform = std::move(fastTransform); mTPCClusterParam = std::make_unique<o2::gpu::GPUParam>(); - mTPCClusterParam->SetDefaults(o2::base::Propagator::Instance()->getNominalBz()); // TODO this may change - auto bz = std::abs(o2::base::Propagator::Instance()->getNominalBz()); - mFieldON = bz > 0.01; - mMinTPCTrackPtInv = (mFieldON && mParams->minTPCTrackR > 0) ? 1. / std::abs(mParams->minTPCTrackR * bz * o2::constants::math::B2C) : 999.; - mMinITSTrackPtInv = (mFieldON && mParams->minITSTrackR > 0) ? 1. / std::abs(mParams->minITSTrackR * bz * o2::constants::math::B2C) : 999.; + if (mVDriftCalibOn) { + float maxDTgl = std::min(0.02f, mParams->maxVDriftUncertainty) * mParams->maxTglForVDriftCalib; + mHistoDTgl = std::make_unique<o2::dataformats::FlatHisto2D_f>(mParams->nBinsTglVDriftCalib, -mParams->maxTglForVDriftCalib, mParams->maxTglForVDriftCalib, + mParams->nBinsDTglVDriftCalib, -maxDTgl, maxDTgl); + } #ifdef _ALLOW_DEBUG_TREES_ // debug streamer @@ -377,7 +362,7 @@ void MatchTPCITS::init() mRGHelper.init(); // prepare helper for TPC track / ITS clusters matching const auto& zr = mRGHelper.layers.back().zRange; - mITSFiducialZCut = std::max(std::abs(zr.min()), std::abs(zr.max())) + 20.; + mITSFiducialZCut = std::max(std::abs(zr.getMin()), std::abs(zr.getMax())) + 20.; clear(); @@ -386,6 +371,48 @@ void MatchTPCITS::init() print(); } +//______________________________________________ +void MatchTPCITS::updateTimeDependentParams() +{ + ///< update parameters depending on time (once per TF) + auto& gasParam = o2::tpc::ParameterGas::Instance(); + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + auto& detParam = o2::tpc::ParameterDetector::Instance(); + mTPCTBinMUS = elParam.ZbinWidth; + mTPCTBinNS = mTPCTBinMUS * 1e3; + mTPCVDrift0 = gasParam.DriftV; + mTPCZMax = detParam.TPClength; + mTPCTBinMUSInv = 1. / mTPCTBinMUS; + assert(mITSROFrameLengthMUS > 0.0f); + if (mITSTriggered) { + mITSROFrame2TPCBin = mITSROFrameLengthMUS * mTPCTBinMUSInv; + } else { + mITSROFrame2TPCBin = mITSROFrameLengthMUS * mTPCTBinMUSInv; // RSTODO use both ITS and TPC times BCs once will be available for TPC also + } + mTPCBin2ITSROFrame = 1. / mITSROFrame2TPCBin; + mTPCBin2Z = mTPCTBinMUS * mTPCVDrift0; + mZ2TPCBin = 1. / mTPCBin2Z; + mTPCVDrift0Inv = 1. / mTPCVDrift0; + mNTPCBinsFullDrift = mTPCZMax * mZ2TPCBin; + mTPCTimeEdgeTSafeMargin = z2TPCBin(mParams->safeMarginTPCTimeEdge); + + mBz = o2::base::Propagator::Instance()->getNominalBz(); + mTPCClusterParam->SetDefaults(mBz); // TODO this may change + mFieldON = std::abs(mBz) > 0.01; + + mMinTPCTrackPtInv = (mFieldON && mParams->minTPCTrackR > 0) ? 1. / std::abs(mParams->minTPCTrackR * mBz * o2::constants::math::B2C) : 999.; + mMinITSTrackPtInv = (mFieldON && mParams->minITSTrackR > 0) ? 1. / std::abs(mParams->minITSTrackR * mBz * o2::constants::math::B2C) : 999.; + + // RSTODO: do we need to recreate it? It should be enough to add/use setters in GPUTPCO2InterfaceRefit + mTPCRefitterShMap.resize(mTPCClusterIdxStruct->nClustersTotal); + o2::gpu::GPUTPCO2InterfaceRefit::fillSharedClustersMap(mTPCClusterIdxStruct, mTPCTracksArray, mTPCTrackClusIdx.data(), mTPCRefitterShMap.data()); + mTPCRefitter = std::make_unique<o2::gpu::GPUTPCO2InterfaceRefit>(mTPCClusterIdxStruct, mTPCTransform.get(), mBz, mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), nullptr, o2::base::Propagator::Instance()); + + o2::math_utils::Point3D<float> p0(90., 1., 1), p1(90., 100., 100.); + auto matbd = o2::base::Propagator::Instance()->getMatBudget(mParams->matCorr, p0, p1); + mTPCmeanX0Inv = matbd.meanX2X0 / matbd.length; +} + //______________________________________________ void MatchTPCITS::selectBestMatches() { @@ -536,7 +563,7 @@ bool MatchTPCITS::prepareTPCTracks() continue; } if (mMCTruthON) { - mTPCLblWork.emplace_back(mTPCTrkLabels->getLabels(it)[0]); + mTPCLblWork.emplace_back(mTPCTrkLabels[it]); } float time0 = trcOrig.getTime0(); @@ -553,11 +580,11 @@ bool MatchTPCITS::prepareTPCTracks() // TODO : special treatment of tracks crossing the CE // cache work track index - mTPCSectIndexCache[o2::utils::Angle2Sector(trc.getAlpha())].push_back(mTPCWork.size() - 1); + mTPCSectIndexCache[o2::math_utils::angle2Sector(trc.getAlpha())].push_back(mTPCWork.size() - 1); } /// full drift time + safety margin - float maxTDriftSafe = (mNTPCBinsFullDrift + mParams->TPCITSTimeBinSafeMargin + mTPCTimeEdgeTSafeMargin); + float maxTDriftSafe = (mNTPCBinsFullDrift + mParams->safeMarginTPCITSTimeBin + mTPCTimeEdgeTSafeMargin); float maxTimeBin = 0; int nITSROFs = mITSROFTimes.size(); @@ -565,18 +592,19 @@ bool MatchTPCITS::prepareTPCTracks() for (int sec = o2::constants::math::NSectors; sec--;) { auto& indexCache = mTPCSectIndexCache[sec]; LOG(INFO) << "Sorting sector" << sec << " | " << indexCache.size() << " TPC tracks"; - if (!indexCache.size()) + if (!indexCache.size()) { continue; + } std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { auto& trcA = mTPCWork[a]; auto& trcB = mTPCWork[b]; - return (trcA.timeBins.max() - trcB.timeBins.max()) < 0.; + return (trcA.timeBins.getMax() - trcB.timeBins.getMax()) < 0.; }); // build array of 1st entries with tmax corresponding to each ITS ROF (or trigger), // TPC tracks below this entry cannot match to ITS tracks of this and higher ROFs - float tmax = mTPCWork[indexCache.back()].timeBins.max(); + float tmax = mTPCWork[indexCache.back()].timeBins.getMax(); if (maxTimeBin < tmax) { maxTimeBin = tmax; } @@ -606,13 +634,13 @@ bool MatchTPCITS::prepareTPCTracks() // create mapping from TPC time-bins to ITS ROFs if (mITSROFTimes.back() < maxTimeBin) { - maxTimeBin = mITSROFTimes.back().max(); + maxTimeBin = mITSROFTimes.back().getMax(); } int nb = int(maxTimeBin) + 1; mITSROFofTPCBin.resize(nb, -1); int itsROF = 0; for (int ib = 0; ib < nb; ib++) { - while (itsROF < nITSROFs && ib < mITSROFTimes[itsROF].min()) { + while (itsROF < nITSROFs && ib < mITSROFTimes[itsROF].getMin()) { itsROF++; } mITSROFofTPCBin[ib] = itsROF; @@ -670,8 +698,8 @@ bool MatchTPCITS::prepareITSTracks() } int cluROFOffset = mITSClusterROFRec[irof].getFirstEntry(); // clusters of this ROF start at this offset - float tmn = intRecord2TPCTimeBin(rofRec.getBCData()); // ITS track min time in TPC time-bins - mITSROFTimes.emplace_back(tmn, tmn + mITSROFrame2TPCBin); // ITS track min/max time in TPC time-bins + float tmn = intRecord2TPCTimeBin(rofRec.getBCData()); // ITS track min time in TPC time-bins + mITSROFTimes.emplace_back(tmn, tmn + mITSROFrame2TPCBin); // ITS track min/max time in TPC time-bins for (int sec = o2::constants::math::NSectors; sec--;) { // start of sector's tracks for this ROF mITSTimeBinStart[sec][irof] = mITSSectIndexCache[sec].size(); // The sorting does not affect this @@ -692,7 +720,7 @@ bool MatchTPCITS::prepareITSTracks() int nWorkTracks = mITSWork.size(); // working copy of outer track param auto& trc = mITSWork.emplace_back(static_cast<const o2::track::TrackParCov&>(trcOrig.getParamOut()), it); - if (!trc.rotate(o2::utils::Angle2Alpha(trc.getPhiPos()))) { + if (!trc.rotate(o2::math_utils::angle2Alpha(trc.getPhiPos()))) { mITSWork.pop_back(); // discard failed track continue; } @@ -702,12 +730,12 @@ bool MatchTPCITS::prepareITSTracks() continue; // add to cache only those ITS tracks which reached ref.X and have reasonable snp } if (mMCTruthON) { - mITSLblWork.emplace_back(mITSTrkLabels->getLabels(it)[0]); + mITSLblWork.emplace_back(mITSTrkLabels[it]); } trc.roFrame = irof; // cache work track index - int sector = o2::utils::Angle2Sector(trc.getAlpha()); + int sector = o2::math_utils::angle2Sector(trc.getAlpha()); mITSSectIndexCache[sector].push_back(nWorkTracks); // If the ITS track is very close to the sector edge, it may match also to a TPC track in the neighb. sector. @@ -772,7 +800,6 @@ bool MatchTPCITS::prepareFITInfo() return true; } - //_____________________________________________________ void MatchTPCITS::doMatching(int sec) { @@ -783,31 +810,32 @@ void MatchTPCITS::doMatching(int sec) auto& tbinStartITS = mITSTimeBinStart[sec]; int nTracksTPC = cacheTPC.size(), nTracksITS = cacheITS.size(); if (!nTracksTPC || !nTracksITS) { - LOG(INFO) << "Matchng sector " << sec << " : N tracks TPC:" << nTracksTPC << " ITS:" << nTracksITS << " in sector " - << sec; + LOG(INFO) << "Matchng sector " << sec << " : N tracks TPC:" << nTracksTPC << " ITS:" << nTracksITS << " in sector " << sec; return; } /// full drift time + safety margin - float maxTDriftSafe = (mNTPCBinsFullDrift + mParams->TPCITSTimeBinSafeMargin + mTPCTimeEdgeTSafeMargin); + float maxTDriftSafe = (mNTPCBinsFullDrift + mParams->safeMarginTPCITSTimeBin + mTPCTimeEdgeTSafeMargin); + + float vdErr2TB = mZ2TPCBin * mParams->maxVDriftUncertainty; // get min ROFrame (in TPC time-bins) of ITS tracks currently in cache auto minROFITS = mITSWork[cacheITS.front()].roFrame; if (minROFITS >= int(tbinStartTPC.size())) { - LOG(INFO) << "ITS min ROFrame " << minROFITS << " exceeds all cached TPC track ROF eqiuvalent " - << cacheTPC.size() - 1; + LOG(INFO) << "ITS min ROFrame " << minROFITS << " exceeds all cached TPC track ROF eqiuvalent " << cacheTPC.size() - 1; return; } int nCheckTPCControl = 0, nCheckITSControl = 0, nMatchesControl = 0; // temporary - - int idxMinTPC = tbinStartTPC[minROFITS]; // index of 1st cached TPC track within cached ITS ROFrames + int idxMinTPC = tbinStartTPC[minROFITS]; // index of 1st cached TPC track within cached ITS ROFrames + auto t2nbs = mZ2TPCBin * mParams->tpcTimeICMatchingNSigma; + bool checkInteractionCandidates = mUseFT0 && mParams->validateMatchByFIT != MatchTPCITSParams::Disable; for (int itpc = idxMinTPC; itpc < nTracksTPC; itpc++) { auto& trefTPC = mTPCWork[cacheTPC[itpc]]; // estimate ITS 1st ROframe bin this track may match to: TPC track are sorted according to their // timeMax, hence the timeMax - MaxmNTPCBinsFullDrift are non-decreasing - int itsROBin = tpcTimeBin2ITSROFrame(trefTPC.timeBins.max() - maxTDriftSafe); + int itsROBin = tpcTimeBin2ITSROFrame(trefTPC.timeBins.getMax() - maxTDriftSafe); if (itsROBin >= int(tbinStartITS.size())) { // time of TPC track exceeds the max time of ITS in the cache break; @@ -826,6 +854,19 @@ void MatchTPCITS::doMatching(int sec) if (trefTPC.timeBins > timeITS) { // its bracket precedes TPC bracket continue; } + + // is corrected TPC track time compatible with ITS ROF expressed in TPC bins? + auto deltaT = (trefITS.getZ() - trefTPC.getZ()) * mZ2TPCBin; // time difference in TPC time bins corresponding to Z differences + auto timeTB = getTPCTrackCorrectedTimeBin(mTPCTracksArray[trefTPC.sourceID], deltaT); // TPC time required to match to Z of ITS track + auto timeTBErr = std::sqrt(trefITS.getSigmaZ2() + trefTPC.getSigmaZ2()) * t2nbs; // nsigma*error in number of TPC time bins + if (mVDriftCalibOn) { + timeTBErr += vdErr2TB * (250. - abs(trefITS.getZ())); // account for the extra error from TPC VDrift uncertainty + } + o2::math_utils::Bracket<float> trange(timeTB - timeTBErr, timeTB + timeTBErr); + if (timeITS.isOutside(trange)) { + continue; + } + nCheckITSControl++; float chi2 = -1; int rejFlag = compareTPCITSTracks(trefITS, trefTPC, chi2); @@ -838,7 +879,7 @@ void MatchTPCITS::doMatching(int sec) if (rejFlag == RejectOnTgl) { // ITS tracks in each ROFrame are ordered in Tgl, hence if this check failed on Tgl check - // (i.e. tgl_its>tgl_tpc+tolerance), tnem all other ITS tracks in this ROFrame will also have tgl too large. + // (i.e. tgl_its>tgl_tpc+tolerance), then all other ITS tracks in this ROFrame will also have tgl too large. // Jump on the 1st ITS track of the next ROFrame int rof = trefITS.roFrame; bool stop = false; @@ -857,7 +898,37 @@ void MatchTPCITS::doMatching(int sec) if (rejFlag != Accept) { continue; } - registerMatchRecordTPC(cacheITS[iits], cacheTPC[itpc], chi2); // register matching candidate + int matchedIC = MinusOne; + if (!isCosmics()) { + // validate by bunch filling scheme + auto irBracket = tpcTimeBin2IRBracket(trange); + if (irBracket.isInvalid()) { + continue; + } + + if (checkInteractionCandidates) { + // check if corrected TPC track time is compatible with any of interaction times + auto interactionRefs = mITSROFIntCandEntries[trefITS.roFrame]; // reference on interaction candidates compatible with this track + int nic = interactionRefs.getEntries(); + if (nic) { + int idIC = interactionRefs.getFirstEntry(), maxIC = idIC + nic; + for (; idIC < maxIC; idIC++) { + auto cmp = mInteractions[idIC].timeBins.isOutside(trange); + if (cmp == o2::math_utils::Bracket<float>::Above) { // trange is above this interaction candidate, the following ones may match + continue; + } + if (cmp == o2::math_utils::Bracket<float>::Inside) { + matchedIC = idIC; + } + break; // we loop till 1st matching IC or the one above the trange (since IC are ordered, all others will be above too) + } + } + } + if (mParams->validateMatchByFIT == MatchTPCITSParams::Require && matchedIC == MinusOne) { + continue; + } + } + registerMatchRecordTPC(cacheITS[iits], cacheTPC[itpc], chi2, matchedIC); // register matching candidate nMatchesControl++; } } @@ -872,8 +943,8 @@ void MatchTPCITS::suppressMatchRecordITS(int itsID, int tpcID) { ///< suppress the reference on the tpcID in the list of matches recorded for itsID auto& tITS = mITSWork[itsID]; - int topID = MinusOne, recordID = tITS.matchID; // 1st entry in mMatchRecordsITS - while (recordID > MinusOne) { // navigate over records for given ITS track + int topID = MinusOne, recordID = tITS.matchID; // 1st entry in mMatchRecordsITS + while (recordID > MinusOne) { // navigate over records for given ITS track if (mMatchRecordsITS[recordID].partnerID == tpcID) { // unlink this record, connecting its child to its parrent if (topID < 0) { @@ -889,15 +960,15 @@ void MatchTPCITS::suppressMatchRecordITS(int itsID, int tpcID) } //______________________________________________ -bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2) +bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2, int candIC) { ///< record matching candidate, making sure that number of ITS candidates per TPC track, sorted ///< in matching chi2 does not exceed allowed number - auto& tTPC = mTPCWork[iTPC]; // get matchRecord structure of this TPC track, create if none - if (tTPC.matchID < 0) { // no matches yet, just add new record - registerMatchRecordITS(iITS, iTPC, chi2); // register TPC track in the ITS records - tTPC.matchID = mMatchRecordsTPC.size(); // new record will be added in the end - mMatchRecordsTPC.emplace_back(iITS, chi2); // create new record with empty reference on next match + auto& tTPC = mTPCWork[iTPC]; // get MatchRecord structure of this TPC track, create if none + if (tTPC.matchID < 0) { // no matches yet, just add new record + registerMatchRecordITS(iITS, iTPC, chi2, candIC); // register TPC track in the ITS records + tTPC.matchID = mMatchRecordsTPC.size(); // new record will be added in the end + mMatchRecordsTPC.emplace_back(iITS, chi2, MinusOne, candIC); // create new record with empty reference on next match return true; } @@ -905,14 +976,16 @@ bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2) do { auto& nextMatchRec = mMatchRecordsTPC[nextID]; count++; - if (chi2 < nextMatchRec.chi2) { // need to insert new record before nextMatchRec? + if (!nextMatchRec.isBetter(chi2, candIC)) { // need to insert new record before nextMatchRec? if (count < mParams->maxMatchCandidates) { - break; // will insert in front of nextID - } else { // max number of candidates reached, will overwrite the last one - nextMatchRec.chi2 = chi2; + break; // will insert in front of nextID + } else { // max number of candidates reached, will overwrite the last one suppressMatchRecordITS(nextMatchRec.partnerID, iTPC); // flag as disabled the overriden ITS match - registerMatchRecordITS(iITS, tTPC.matchID, chi2); // register TPC track entry in the ITS records - nextMatchRec.partnerID = iITS; // reuse the record of suppressed ITS match to store better one + registerMatchRecordITS(iITS, iTPC, chi2, candIC); // register TPC track entry in the ITS records + // reuse the record of suppressed ITS match to store better one + nextMatchRec.chi2 = chi2; + nextMatchRec.matchedIC = candIC; + nextMatchRec.partnerID = iITS; return true; } } @@ -930,8 +1003,8 @@ bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2) topID = mMatchRecordsTPC[topID].nextRecID = mMatchRecordsTPC.size(); // register to his parent } // nextID==-1 will mean that the while loop run over all candidates->the new one is the worst (goes to the end) - registerMatchRecordITS(iITS, tTPC.matchID, chi2); // register TPC track in the ITS records - mMatchRecordsTPC.emplace_back(iITS, chi2, nextID); // create new record with empty reference on next match + registerMatchRecordITS(iITS, iTPC, chi2, candIC); // register TPC track in the ITS records + mMatchRecordsTPC.emplace_back(iITS, chi2, nextID, candIC); // create new record with empty reference on next match // make sure that after addition the number of candidates don't exceed allowed number count++; while (nextID > MinusOne) { @@ -952,25 +1025,23 @@ bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2) } //______________________________________________ -void MatchTPCITS::registerMatchRecordITS(int iITS, int iTPC, float chi2) +void MatchTPCITS::registerMatchRecordITS(int iITS, int iTPC, float chi2, int candIC) { - ///< register TPC match in ITS tracks match records, ordering then in chi2 + ///< register TPC match in ITS tracks match records, ordering them in quality auto& tITS = mITSWork[iITS]; int idnew = mMatchRecordsITS.size(); - mMatchRecordsITS.emplace_back(iTPC, chi2); // associate iTPC with this record + auto& newRecord = mMatchRecordsITS.emplace_back(iTPC, chi2, MinusOne, candIC); // associate iTPC with this record if (tITS.matchID < 0) { tITS.matchID = idnew; return; } - // there are other matches for this ITS track, insert the new record preserving chi2 order + // there are other matches for this ITS track, insert the new record preserving quality order // navigate till last record or the one with worse chi2 int topID = MinusOne, nextRecord = tITS.matchID; - mMatchRecordsITS.emplace_back(iTPC, chi2); // associate iTPC with this record - auto& newRecord = mMatchRecordsITS.back(); do { - auto& recITS = mMatchRecordsITS[nextRecord]; - if (chi2 < recITS.chi2) { // insert before this one - newRecord.nextRecID = nextRecord; // new one will refer to old one it overtook + auto& nextMatchRec = mMatchRecordsITS[nextRecord]; + if (!nextMatchRec.isBetter(chi2, candIC)) { // need to insert new record before nextMatchRec? + newRecord.nextRecID = nextRecord; // new one will refer to old one it overtook if (topID < 0) { tITS.matchID = idnew; // the new one is the best match, track will refer to it } else { @@ -990,68 +1061,73 @@ void MatchTPCITS::registerMatchRecordITS(int iITS, int iTPC, float chi2) int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& tTPC, float& chi2) const { ///< compare pair of ITS and TPC tracks - auto& trackTPC = tTPC; - auto& trackITS = tITS; chi2 = -1.f; int rejFlag = Accept; float diff; // make rough check differences and their nsigmas // start with check on Tgl, since rjection on it will allow to profit from sorting - diff = trackITS.getParam(o2::track::kTgl) - trackTPC.getParam(o2::track::kTgl); + diff = tITS.getParam(o2::track::kTgl) - tTPC.getParam(o2::track::kTgl); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kTgl], RejectOnTgl))) { return rejFlag; } - diff *= diff / (trackITS.getDiagError2(o2::track::kTgl) + trackTPC.getDiagError2(o2::track::kTgl)); + auto err2Tgl = tITS.getDiagError2(o2::track::kTgl) + tTPC.getDiagError2(o2::track::kTgl); + if (mVDriftCalibOn) { + auto addErr = tITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty; + err2Tgl += addErr * addErr; // account for VDrift uncertainty + } + diff *= diff / err2Tgl; if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kTgl], RejectOnTgl + NSigmaShift))) { return rejFlag; } - - diff = trackITS.getParam(o2::track::kY) - trackTPC.getParam(o2::track::kY); + diff = tITS.getParam(o2::track::kY) - tTPC.getParam(o2::track::kY); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kY], RejectOnY))) { return rejFlag; } - diff *= diff / (trackITS.getDiagError2(o2::track::kY) + trackTPC.getDiagError2(o2::track::kY)); + diff *= diff / (tITS.getDiagError2(o2::track::kY) + tTPC.getDiagError2(o2::track::kY)); if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kY], RejectOnY + NSigmaShift))) { return rejFlag; } if (mCompareTracksDZ) { // in continuous mode we usually don't use DZ - diff = trackITS.getParam(o2::track::kZ) - trackTPC.getParam(o2::track::kZ); + diff = tITS.getParam(o2::track::kZ) - tTPC.getParam(o2::track::kZ); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kZ], RejectOnZ))) { return rejFlag; } - diff *= diff / (trackITS.getDiagError2(o2::track::kZ) + trackTPC.getDiagError2(o2::track::kZ)); + diff *= diff / (tITS.getDiagError2(o2::track::kZ) + tTPC.getDiagError2(o2::track::kZ)); if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kZ], RejectOnZ + NSigmaShift))) { return rejFlag; } } else { // in continuous mode we use special check on allowed Z range - if (trackITS.getParam(o2::track::kZ) - tTPC.zMax > mParams->crudeAbsDiffCut[o2::track::kZ]) + if (tITS.getParam(o2::track::kZ) - tTPC.zMax > mParams->crudeAbsDiffCut[o2::track::kZ]) { return RejectOnZ; - if (trackITS.getParam(o2::track::kZ) - tTPC.zMin < -mParams->crudeAbsDiffCut[o2::track::kZ]) + } + if (tITS.getParam(o2::track::kZ) - tTPC.zMin < -mParams->crudeAbsDiffCut[o2::track::kZ]) { return -RejectOnZ; + } } - diff = trackITS.getParam(o2::track::kSnp) - trackTPC.getParam(o2::track::kSnp); + diff = tITS.getParam(o2::track::kSnp) - tTPC.getParam(o2::track::kSnp); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kSnp], RejectOnSnp))) { return rejFlag; } - diff *= diff / (trackITS.getDiagError2(o2::track::kSnp) + trackTPC.getDiagError2(o2::track::kSnp)); + diff *= diff / (tITS.getDiagError2(o2::track::kSnp) + tTPC.getDiagError2(o2::track::kSnp)); if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kSnp], RejectOnSnp + NSigmaShift))) { return rejFlag; } - diff = trackITS.getParam(o2::track::kQ2Pt) - trackTPC.getParam(o2::track::kQ2Pt); + diff = tITS.getParam(o2::track::kQ2Pt) - tTPC.getParam(o2::track::kQ2Pt); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kQ2Pt], RejectOnQ2Pt))) { return rejFlag; } - diff *= diff / (trackITS.getDiagError2(o2::track::kQ2Pt) + trackTPC.getDiagError2(o2::track::kQ2Pt)); + diff *= diff / (tITS.getDiagError2(o2::track::kQ2Pt) + tTPC.getDiagError2(o2::track::kQ2Pt)); 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) + if (chi2 > mParams->cutMatchingChi2) { return RejectOnChi2; + } return Accept; } @@ -1065,12 +1141,12 @@ void MatchTPCITS::printCandidatesTPC() const for (int i = 0; i < ntpc; i++) { const auto& tTPC = mTPCWork[i]; int nm = getNMatchRecordsTPC(tTPC); - printf("*** trackTPC#%6d %6d : Ncand = %d\n", i, tTPC.sourceID, nm); + printf("*** trackTPC#%6d %6d : Ncand = %d Best = %d\n", i, tTPC.sourceID, nm, tTPC.matchID); int count = 0, recID = tTPC.matchID; while (recID > MinusOne) { const auto& rcTPC = mMatchRecordsTPC[recID]; const auto& tITS = mITSWork[rcTPC.partnerID]; - printf(" * cand %2d : ITS track %6d Chi2: %.2f\n", count, tITS.sourceID, rcTPC.chi2); + printf(" * cand %2d : ITS track %6d(src:%6d) Chi2: %.2f\n", count, rcTPC.partnerID, tITS.sourceID, rcTPC.chi2); count++; recID = rcTPC.nextRecID; } @@ -1086,12 +1162,12 @@ void MatchTPCITS::printCandidatesITS() const for (int i = 0; i < nits; i++) { const auto& tITS = mITSWork[i]; - printf("*** trackITS#%6d %6d : Ncand = %d\n", i, tITS.sourceID, getNMatchRecordsITS(tITS)); + printf("*** trackITS#%6d %6d : Ncand = %d Best = %d\n", i, tITS.sourceID, getNMatchRecordsITS(tITS), tITS.matchID); int count = 0, recID = tITS.matchID; while (recID > MinusOne) { const auto& rcITS = mMatchRecordsITS[recID]; const auto& tTPC = mTPCWork[rcITS.partnerID]; - printf(" * cand %2d : TPC track %6d Chi2: %.2f\n", count, tTPC.sourceID, rcITS.chi2); + printf(" * cand %2d : TPC track %6d(src:%6d) Chi2: %.2f\n", count, rcITS.partnerID, tTPC.sourceID, rcITS.chi2); count++; recID = rcITS.nextRecID; } @@ -1099,41 +1175,45 @@ void MatchTPCITS::printCandidatesITS() const } //______________________________________________ -float MatchTPCITS::getPredictedChi2NoZ(const o2::track::TrackParCov& tr1, const o2::track::TrackParCov& tr2) const +float MatchTPCITS::getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, const o2::track::TrackParCov& trTPC) const { /// get chi2 between 2 tracks, neglecting Z parameter. /// 2 tracks must be defined at the same parameters X,alpha (check is currently commented) - // if (std::abs(tr1.getAlpha() - tr2.getAlpha()) > FLT_EPSILON) { + // if (std::abs(trITS.getAlpha() - trTPC.getAlpha()) > FLT_EPSILON) { // LOG(ERROR) << "The reference Alpha of the tracks differ: " - // << tr1.getAlpha() << " : " << tr2.getAlpha(); + // << trITS.getAlpha() << " : " << trTPC.getAlpha(); // return 2. * o2::track::HugeF; // } - // if (std::abs(tr1.getX() - tr2.getX()) > FLT_EPSILON) { + // if (std::abs(trITS.getX() - trTPC.getX()) > FLT_EPSILON) { // LOG(ERROR) << "The reference X of the tracks differ: " - // << tr1.getX() << " : " << tr2.getX(); + // << trITS.getX() << " : " << trTPC.getX(); // return 2. * o2::track::HugeF; // } MatrixDSym4 covMat; - covMat(0, 0) = static_cast<double>(tr1.getSigmaY2()) + static_cast<double>(tr2.getSigmaY2()); - covMat(1, 0) = static_cast<double>(tr1.getSigmaSnpY()) + static_cast<double>(tr2.getSigmaSnpY()); - covMat(1, 1) = static_cast<double>(tr1.getSigmaSnp2()) + static_cast<double>(tr2.getSigmaSnp2()); - covMat(2, 0) = static_cast<double>(tr1.getSigmaTglY()) + static_cast<double>(tr2.getSigmaTglY()); - covMat(2, 1) = static_cast<double>(tr1.getSigmaTglSnp()) + static_cast<double>(tr2.getSigmaTglSnp()); - covMat(2, 2) = static_cast<double>(tr1.getSigmaTgl2()) + static_cast<double>(tr2.getSigmaTgl2()); - covMat(3, 0) = static_cast<double>(tr1.getSigma1PtY()) + static_cast<double>(tr2.getSigma1PtY()); - covMat(3, 1) = static_cast<double>(tr1.getSigma1PtSnp()) + static_cast<double>(tr2.getSigma1PtSnp()); - covMat(3, 2) = static_cast<double>(tr1.getSigma1PtTgl()) + static_cast<double>(tr2.getSigma1PtTgl()); - covMat(3, 3) = static_cast<double>(tr1.getSigma1Pt2()) + static_cast<double>(tr2.getSigma1Pt2()); + covMat(0, 0) = static_cast<double>(trITS.getSigmaY2()) + static_cast<double>(trTPC.getSigmaY2()); + covMat(1, 0) = static_cast<double>(trITS.getSigmaSnpY()) + static_cast<double>(trTPC.getSigmaSnpY()); + covMat(1, 1) = static_cast<double>(trITS.getSigmaSnp2()) + static_cast<double>(trTPC.getSigmaSnp2()); + covMat(2, 0) = static_cast<double>(trITS.getSigmaTglY()) + static_cast<double>(trTPC.getSigmaTglY()); + covMat(2, 1) = static_cast<double>(trITS.getSigmaTglSnp()) + static_cast<double>(trTPC.getSigmaTglSnp()); + covMat(2, 2) = static_cast<double>(trITS.getSigmaTgl2()) + static_cast<double>(trTPC.getSigmaTgl2()); + if (mVDriftCalibOn) { + auto addErr = trITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty; + covMat(2, 2) += addErr * addErr; + } + covMat(3, 0) = static_cast<double>(trITS.getSigma1PtY()) + static_cast<double>(trTPC.getSigma1PtY()); + covMat(3, 1) = static_cast<double>(trITS.getSigma1PtSnp()) + static_cast<double>(trTPC.getSigma1PtSnp()); + covMat(3, 2) = static_cast<double>(trITS.getSigma1PtTgl()) + static_cast<double>(trTPC.getSigma1PtTgl()); + covMat(3, 3) = static_cast<double>(trITS.getSigma1Pt2()) + static_cast<double>(trTPC.getSigma1Pt2()); if (!covMat.Invert()) { LOG(ERROR) << "Cov.matrix inversion failed: " << covMat; return 2. * o2::track::HugeF; } double chi2diag = 0., chi2ndiag = 0., - diff[o2::track::kNParams - 1] = {tr1.getParam(o2::track::kY) - tr2.getParam(o2::track::kY), - tr1.getParam(o2::track::kSnp) - tr2.getParam(o2::track::kSnp), - tr1.getParam(o2::track::kTgl) - tr2.getParam(o2::track::kTgl), - tr1.getParam(o2::track::kQ2Pt) - tr2.getParam(o2::track::kQ2Pt)}; + diff[o2::track::kNParams - 1] = {trITS.getParam(o2::track::kY) - trTPC.getParam(o2::track::kY), + trITS.getParam(o2::track::kSnp) - trTPC.getParam(o2::track::kSnp), + trITS.getParam(o2::track::kTgl) - trTPC.getParam(o2::track::kTgl), + trITS.getParam(o2::track::kQ2Pt) - trTPC.getParam(o2::track::kQ2Pt)}; for (int i = o2::track::kNParams - 1; i--;) { chi2diag += diff[i] * diff[i] * covMat(i, i); for (int j = i; j--;) { @@ -1151,13 +1231,12 @@ void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) // to their setctor edge that their matching should be checked also in the neighbouring sector mITSWork.push_back(mITSWork.back()); // clone the last track defined in given sector auto& trc = mITSWork.back(); - if (trc.rotate(o2::utils::Sector2Angle(sector)) && - o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, XMatchingRef, o2::constants::physics::MassPionCharged, MaxSnp, - 2., MatCorrType::USEMatCorrNONE)) { + if (trc.rotate(o2::math_utils::sector2Angle(sector)) && + o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, XMatchingRef, MaxSnp, 2., MatCorrType::USEMatCorrNONE)) { // TODO: use faster prop here, no 3d field, materials mITSSectIndexCache[sector].push_back(mITSWork.size() - 1); // register track CLONE if (mMCTruthON) { - mITSLblWork.emplace_back(mITSTrkLabels->getLabels(trc.sourceID)[0]); + mITSLblWork.emplace_back(mITSTrkLabels[trc.sourceID]); } } else { mITSWork.pop_back(); // rotation / propagation failed @@ -1172,8 +1251,7 @@ bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc) bool refReached = false; refReached = XMatchingRef < 10.; // RS: tmp, to cover XMatchingRef~0 int trialsLeft = 2; - while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, XMatchingRef, o2::constants::physics::MassPionCharged, - MaxSnp, 2., mUseMatCorrFlag)) { + while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { if (refReached) { break; } @@ -1185,7 +1263,7 @@ bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc) if (!trialsLeft--) { break; } - auto alphaNew = o2::utils::Angle2Alpha(trc.getPhiPos()); + auto alphaNew = o2::math_utils::angle2Alpha(trc.getPhiPos()); if (!trc.rotate(alphaNew) != 0) { break; // failed (RS: check effect on matching tracks to neighbouring sector) } @@ -1220,8 +1298,8 @@ void MatchTPCITS::print() const } printf("\n"); - printf("TPC-ITS time(bins) bracketing safety margin: %6.2f\n", mParams->TimeBinTolerance); - printf("TPC Z->time(bins) bracketing safety margin: %6.2f\n", mParams->TPCTimeEdgeZSafeMargin); + printf("TPC-ITS time(bins) bracketing safety margin: %6.2f\n", mParams->timeBinTolerance); + printf("TPC Z->time(bins) bracketing safety margin: %6.2f\n", mParams->safeMarginTPCTimeEdge); #ifdef _ALLOW_DEBUG_TREES_ @@ -1241,226 +1319,29 @@ void MatchTPCITS::print() const } //______________________________________________ -void MatchTPCITS::refitWinners(bool loopInITS) +void MatchTPCITS::refitWinners() { ///< refit winning tracks mTimer[SWRefit].Start(false); LOG(INFO) << "Refitting winner matches"; mWinnerChi2Refit.resize(mITSWork.size(), -1.f); - if (loopInITS) { - int iTPC = 0; // will be ignored - for (int iITS = 0; iITS < (int)mITSWork.size(); iITS++) { - if (!refitTrackTPCITSloopITS(iITS, iTPC)) { - continue; - } - mWinnerChi2Refit[iITS] = mMatchedTracks.back().getChi2Refit(); - } - } else { - int iITS; - for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { - if (!refitTrackTPCITSloopTPC(iTPC, iITS)) { - continue; - } - mWinnerChi2Refit[iITS] = mMatchedTracks.back().getChi2Refit(); + int iITS; + for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { + if (!refitTrackTPCITS(iTPC, iITS)) { + continue; } + mWinnerChi2Refit[iITS] = mMatchedTracks.back().getChi2Refit(); } - /* - */ - // flush last tracks mTimer[SWRefit].Stop(); } //______________________________________________ -bool MatchTPCITS::refitTrackTPCITSloopITS(int iITS, int& iTPC) +bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) { ///< refit in inward direction the pair of TPC and ITS tracks const float maxStep = 2.f; // max propagation step (TODO: tune) - - const auto& tITS = mITSWork[iITS]; - if (isDisabledITS(tITS)) { - return false; // no match - } - const auto& itsMatchRec = mMatchRecordsITS[tITS.matchID]; - iTPC = itsMatchRec.partnerID; - const auto& tTPC = mTPCWork[iTPC]; - const auto& itsTrOrig = mITSTracksArray[tITS.sourceID]; // currently we store clusterIDs in the track - - mMatchedTracks.emplace_back(tTPC, tITS); // create a copy of TPC track at xRef - auto& trfit = mMatchedTracks.back(); - // 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 - } - auto dzCorr = trfit.getZ() - tTPC.getZ(); - float deltaT = dzCorr * mZ2TPCBin; // time correction in time-bins - - // refit TPC track inward into the ITS - int nclRefit = 0, ncl = itsTrOrig.getNumberOfClusters(); - float chi2 = 0.f; - auto geom = o2::its::GeometryTGeo::Instance(); - auto propagator = o2::base::Propagator::Instance(); - // NOTE: the ITS cluster index is stored wrt 1st cluster of relevant ROF, while here we extract clusters from the - // buffer for the whole TF. Therefore, we should shift the index by the entry of the ROF's 1st cluster in the global cluster buffer - int clusIndOffs = mITSClusterROFRec[tITS.roFrame].getFirstEntry(); - - int clEntry = itsTrOrig.getFirstClusterEntry(); - for (int icl = 0; icl < ncl; icl++) { - const auto& clus = mITSClustersArray[clusIndOffs + mITSTrackClusIdx[clEntry++]]; - float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX(); - if (!trfit.rotate(alpha) || - // note: here we also calculate the L,T integral (in the inward direction, but this is irrelevant) - // 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->PropagateToXBxByBz(trfit, x, o2::constants::physics::MassPionCharged, - !propagator->propagateToX(trfit, x, propagator->getNominalBz(), o2::constants::physics::MassPionCharged, - MaxSnp, maxStep, mUseMatCorrFlag, &trfit.getLTIntegralOut())) { - break; - } - chi2 += trfit.getPredictedChi2(clus); - if (!trfit.update(clus)) { - break; - } - nclRefit++; - } - if (nclRefit != ncl) { - printf("FAILED after ncl=%d\n", nclRefit); - printf("its was: "); - tITS.print(); - printf("tpc was: "); - tTPC.print(); - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - - // we need to update the LTOF integral by the distance to the "primary vertex" - const o2::dataformats::VertexBase vtxDummy; // at the moment using dummy vertex: TODO use MeanVertex constraint instead - if (!propagator->propagateToDCA(vtxDummy, trfit, propagator->getNominalBz(), o2::constants::physics::MassPionCharged, - maxStep, mUseMatCorrFlag, nullptr, &trfit.getLTIntegralOut())) { - LOG(ERROR) << "LTOF integral might be incorrect"; - } - - /// precise time estimate - auto tpcTrOrig = mTPCTracksArray[tTPC.sourceID]; - float timeTB = tpcTrOrig.getTime0(); - if (tpcTrOrig.hasASideClustersOnly()) { - timeTB += deltaT; - } else if (tpcTrOrig.hasCSideClustersOnly()) { - timeTB -= deltaT; - } else { - // TODO : special treatment of tracks crossing the CE - } - // convert time in timebins to time in microseconds - float time = timeTB * mTPCTBinMUS; - // estimate the error on time - float timeErr = std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDrift0Inv; - - // outward refit - auto& tracOut = trfit.getParamOut(); // this track is already at the matching reference X - { - int icl = tpcTrOrig.getNClusterReferences() - 1; - uint8_t sector, prevsector, row, prevrow; - uint32_t clusterIndexInRow; - std::array<float, 2> clsYZ; - std::array<float, 3> clsCov = {}; - float clsX; - - const auto& cl = tpcTrOrig.getCluster(mTPCTrackClusIdx, icl, *mTPCClusterIdxStruct, sector, row); - mTPCTransform->Transform(sector, row, cl.getPad(), cl.getTime(), clsX, clsYZ[0], clsYZ[1], timeTB); - // rotate to 1 cluster's sector - if (!tracOut.rotate(o2::utils::Sector2Angle(sector % 18))) { - LOG(WARNING) << "Rotation to sector " << int(sector % 18) << " failed"; - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - // TODO: consider propagating in empty space till TPC entrance in large step, and then in more detailed propagation with mat. corrections - - // propagate to 1st cluster X - if (!propagator->PropagateToXBxByBz(tracOut, clsX, o2::constants::physics::MassPionCharged, MaxSnp, 10., mUseMatCorrFlag, &trfit.getLTIntegralOut())) { - LOG(WARNING) << "Propagation to 1st cluster at X=" << clsX << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - // - mTPCClusterParam->GetClusterErrors2(row, clsYZ[1], tracOut.getSnp(), tracOut.getTgl(), clsCov[0], clsCov[2]); - // - float chi2Out = tracOut.getPredictedChi2(clsYZ, clsCov); - if (!tracOut.update(clsYZ, clsCov)) { - LOG(WARNING) << "Update failed at 1st cluster, chi2 =" << chi2Out; - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - prevrow = row; - prevsector = sector; - - for (; icl--;) { - const auto& cl = tpcTrOrig.getCluster(mTPCTrackClusIdx, icl, *mTPCClusterIdxStruct, sector, row); - if (row <= prevrow) { - LOG(WARNING) << "New row/sect " << int(row) << '/' << int(sector) << " is <= the previous " << int(prevrow) - << '/' << int(prevsector) << " TrackID: " << tTPC.sourceID << " Pt:" << tracOut.getPt(); - if (row < prevrow) { - break; - } else { - continue; // just skip duplicate clusters - } - } - prevrow = row; - mTPCTransform->Transform(sector, row, cl.getPad(), cl.getTime(), clsX, clsYZ[0], clsYZ[1], timeTB); - if (prevsector != sector) { - prevsector = sector; - if (!tracOut.rotate(o2::utils::Sector2Angle(sector % 18))) { - LOG(WARNING) << "Rotation to sector " << int(sector % 18) << " failed"; - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - } - if (!propagator->PropagateToXBxByBz(tracOut, clsX, o2::constants::physics::MassPionCharged, MaxSnp, - 10., MatCorrType::USEMatCorrNONE, &trfit.getLTIntegralOut())) { // no material correction! - LOG(INFO) << "Propagation to cluster " << icl << " (of " << tpcTrOrig.getNClusterReferences() << ") at X=" - << clsX << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp() << " pT=" << tracOut.getPt(); - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - chi2Out += tracOut.getPredictedChi2(clsYZ, clsCov); - if (!tracOut.update(clsYZ, clsCov)) { - LOG(WARNING) << "Update failed at cluster " << icl << ", chi2 =" << chi2Out; - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - } - // propagate to the outer edge of the TPC, TODO: check outer radius - // Note: it is allowed to not reach the requested radius - propagator->PropagateToXBxByBz(tracOut, XTPCOuterRef, o2::constants::physics::MassPionCharged, MaxSnp, - 10., mUseMatCorrFlag, &trfit.getLTIntegralOut()); - - // LOG(INFO) << "Refitted with chi2 = " << chi2Out; - } - - trfit.setChi2Match(itsMatchRec.chi2); - trfit.setChi2Refit(chi2); - trfit.setTimeMUS(time, timeErr); - trfit.setRefTPC(tTPC.sourceID); - trfit.setRefITS(tITS.sourceID); - - if (mMCTruthON) { // store MC info - mOutITSLabels.emplace_back(mITSLblWork[iITS]); - mOutTPCLabels.emplace_back(mTPCLblWork[iTPC]); - } - - // trfit.print(); // DBG - - return true; -} - -//______________________________________________ -bool MatchTPCITS::refitTrackTPCITSloopTPC(int iTPC, int& iITS) -{ - ///< 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 @@ -1489,6 +1370,15 @@ bool MatchTPCITS::refitTrackTPCITSloopTPC(int iTPC, int& iITS) // buffer for the whole TF. Therefore, we should shift the index by the entry of the ROF's 1st cluster in the global cluster buffer int clusIndOffs = mITSClusterROFRec[tITS.roFrame].getFirstEntry(); int clEntry = itsTrOrig.getFirstClusterEntry(); + + float addErr2 = 0; + // extra error on tgl due to the assumed vdrift uncertainty + if (mVDriftCalibOn) { + addErr2 = tITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty; + addErr2 *= addErr2; + trfit.updateCov(addErr2, o2::track::kSigTgl2); + } + for (int icl = 0; icl < ncl; icl++) { const auto& clus = mITSClustersArray[clusIndOffs + mITSTrackClusIdx[clEntry++]]; float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX(); @@ -1496,8 +1386,7 @@ bool MatchTPCITS::refitTrackTPCITSloopTPC(int iTPC, int& iITS) // note: here we also calculate the L,T integral (in the inward direction, but this is irrelevant) // 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->PropagateToXBxByBz(trfit, x, o2::constants::physics::MassPionCharged, - !propagator->propagateToX(trfit, x, propagator->getNominalBz(), o2::constants::physics::MassPionCharged, + !propagator->propagateToX(trfit, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &trfit.getLTIntegralOut())) { break; } @@ -1519,7 +1408,7 @@ bool MatchTPCITS::refitTrackTPCITSloopTPC(int iTPC, int& iITS) // we need to update the LTOF integral by the distance to the "primary vertex" const o2::dataformats::VertexBase vtxDummy; // at the moment using dummy vertex: TODO use MeanVertex constraint instead - if (!propagator->propagateToDCA(vtxDummy, trfit, propagator->getNominalBz(), o2::constants::physics::MassPionCharged, + if (!propagator->propagateToDCA(vtxDummy, trfit, propagator->getNominalBz(), maxStep, mUseMatCorrFlag, nullptr, &trfit.getLTIntegralOut())) { LOG(ERROR) << "LTOF integral might be incorrect"; } @@ -1540,84 +1429,47 @@ bool MatchTPCITS::refitTrackTPCITSloopTPC(int iTPC, int& iITS) float timeErr = std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDrift0Inv; // outward refit - auto& tracOut = trfit.getParamOut(); // this track is already at the matching reference X + auto& tracOut = trfit.getParamOut(); // this is a clone of ITS outward track already at the matching reference X + auto& tofL = trfit.getLTIntegralOut(); + { - int icl = tpcTrOrig.getNClusterReferences() - 1; - uint8_t sector, prevsector, row, prevrow; - uint32_t clusterIndexInRow; - std::array<float, 2> clsYZ; - std::array<float, 3> clsCov = {}; - float clsX; - - const auto& cl = tpcTrOrig.getCluster(mTPCTrackClusIdx, icl, *mTPCClusterIdxStruct, sector, row); - mTPCTransform->Transform(sector, row, cl.getPad(), cl.getTime() - timeTB, clsX, clsYZ[0], clsYZ[1]); - // rotate to 1 cluster's sector - if (!tracOut.rotate(o2::utils::Sector2Angle(sector % 18))) { - LOG(WARNING) << "Rotation to sector " << int(sector % 18) << " failed"; + float xtogo = 0; + if (!tracOut.getXatLabR(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 return false; } - // TODO: consider propagating in empty space till TPC entrance in large step, and then in more detailed propagation with mat. corrections - - // propagate to 1st cluster X - if (!propagator->PropagateToXBxByBz(tracOut, clsX, o2::constants::physics::MassPionCharged, MaxSnp, 10., mUseMatCorrFlag, &trfit.getLTIntegralOut())) { - LOG(WARNING) << "Propagation to 1st cluster at X=" << clsX << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track - return false; + if (mVDriftCalibOn) { + tracOut.updateCov(addErr2, o2::track::kSigTgl2); } - // - mTPCClusterParam->GetClusterErrors2(row, clsYZ[1], tracOut.getSnp(), tracOut.getTgl(), clsCov[0], clsCov[2]); - // - float chi2Out = tracOut.getPredictedChi2(clsYZ, clsCov); - if (!tracOut.update(clsYZ, clsCov)) { - LOG(WARNING) << "Update failed at 1st cluster, chi2 =" << chi2Out; + float chi2Out = 0; + auto posStart = tracOut.getXYZGlo(); + int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, tpcTrOrig.getClusterRef(), timeTB, &chi2Out, true, false); // outward refit + if (retVal < 0) { + LOG(DEBUG) << "Refit failed"; mMatchedTracks.pop_back(); // destroy failed track return false; } - prevrow = row; - prevsector = sector; - - for (; icl--;) { - const auto& cl = tpcTrOrig.getCluster(mTPCTrackClusIdx, icl, *mTPCClusterIdxStruct, sector, row); - if (row <= prevrow) { - LOG(WARNING) << "New row/sect " << int(row) << '/' << int(sector) << " is <= the previous " << int(prevrow) - << '/' << int(prevsector) << " TrackID: " << tTPC.sourceID << " Pt:" << tracOut.getPt(); - if (row < prevrow) { - break; - } else { - continue; // just skip duplicate clusters - } - } - prevrow = row; - mTPCTransform->Transform(sector, row, cl.getPad(), cl.getTime() - timeTB, clsX, clsYZ[0], clsYZ[1]); - if (prevsector != sector) { - prevsector = sector; - if (!tracOut.rotate(o2::utils::Sector2Angle(sector % 18))) { - LOG(WARNING) << "Rotation to sector " << int(sector % 18) << " failed"; - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - } - if (!propagator->PropagateToXBxByBz(tracOut, clsX, o2::constants::physics::MassPionCharged, MaxSnp, - 10., MatCorrType::USEMatCorrNONE, &trfit.getLTIntegralOut())) { // no material correction! - LOG(INFO) << "Propagation to cluster " << icl << " (of " << tpcTrOrig.getNClusterReferences() << ") at X=" - << clsX << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp() << " pT=" << tracOut.getPt(); - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - chi2Out += tracOut.getPredictedChi2(clsYZ, clsCov); - if (!tracOut.update(clsYZ, clsCov)) { - LOG(WARNING) << "Update failed at cluster " << icl << ", chi2 =" << chi2Out; - mMatchedTracks.pop_back(); // destroy failed track - return false; - } - } - // propagate to the outer edge of the TPC, TODO: check outer radius - // Note: it is allowed to not reach the requested radius - propagator->PropagateToXBxByBz(tracOut, XTPCOuterRef, o2::constants::physics::MassPionCharged, MaxSnp, - 10., mUseMatCorrFlag, &trfit.getLTIntegralOut()); - - // LOG(INFO) << "Refitted with chi2 = " << chi2Out; + 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<float> posAv(0.5 * (posEnd.x() + posStart.x()), 0.5 * (posEnd.y() + posStart.y()), 0.5 * (posEnd.z() + posStart.z())); + propagator->getFiedXYZ(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); + tofL.addX2X0(lInt * mTPCmeanX0Inv); + propagator->PropagateToXBxByBz(tracOut, XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); + /* + LOG(INFO) << "TPC " << iTPC << " ITS " << iITS << " Refitted with chi2 = " << chi2Out; + tracOut.print(); + tofL.print(); + */ } trfit.setChi2Match(tpcMatchRec.chi2); @@ -1631,94 +1483,42 @@ bool MatchTPCITS::refitTrackTPCITSloopTPC(int iTPC, int& iITS) mOutTPCLabels.emplace_back(mTPCLblWork[iTPC]); } + // if requested, fill the difference of ITS and TPC tracks tgl for vdrift calibation + if (mHistoDTgl) { + auto tglITS = tITS.getTgl(); + if (std::abs(tglITS) < mHistoDTgl->getXMax()) { + auto dTgl = tglITS - tTPC.getTgl(); + mHistoDTgl->fill(tglITS, dTgl); + } + } // trfit.print(); // DBG return true; } //______________________________________________ -bool MatchTPCITS::refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB, float m) const +bool MatchTPCITS::refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB) const { // inward refit constexpr float TolSNP = 0.99; const auto& tpcTrOrig = mTPCTracksArray[trcID]; - constexpr int MaxClus = 2 * 152 + 1; // TODO - std::array<const o2::tpc::ClusterNative*, MaxClus> clsArr; - std::array<uint8_t, MaxClus> sectArr; - std::array<uint8_t, MaxClus> rowArr; - // select clusters to use - int iclPrev = 0, nclAcc = 0, icl = tpcTrOrig.getNClusterReferences(); - // the innermost cluster is last one - clsArr[nclAcc] = &tpcTrOrig.getCluster(mTPCTrackClusIdx, --icl, *mTPCClusterIdxStruct, sectArr[nclAcc], rowArr[nclAcc]); - nclAcc++; - for (; icl--;) { - clsArr[nclAcc] = &tpcTrOrig.getCluster(mTPCTrackClusIdx, icl, *mTPCClusterIdxStruct, sectArr[nclAcc], rowArr[nclAcc]); - if (rowArr[iclPrev] < rowArr[nclAcc] || // row grows - (rowArr[iclPrev] == rowArr[nclAcc] && sectArr[iclPrev] == sectArr[nclAcc])) { // split clusters? - iclPrev = nclAcc++; - } else { - break; // discard looping part - } - } - // now selected clusters span from inner to outer rows - - std::array<float, 2> clsYZ; - std::array<float, 3> clsCov = {}; - float clsX; trcIn = tpcTrOrig.getOuterParam(); - trcIn.resetCovariance(); - trcIn.setCov(tpcTrOrig.getQ2Pt() * tpcTrOrig.getQ2Pt(), o2::track::kSigQ2Pt2); // 100% error of the *original track inner param* chi2 = 0; - icl = nclAcc - 1; - - printf("RowsSpan: %d %d | %d clusters of %d\n", rowArr[0], rowArr[icl], nclAcc, tpcTrOrig.getNClusterReferences()); // tmp auto propagator = o2::base::Propagator::Instance(); - mTPCTransform->Transform(sectArr[icl], rowArr[icl], clsArr[icl]->getPad(), clsArr[icl]->getTime(), clsX, clsYZ[0], clsYZ[1], timeTB); - mTPCClusterParam->GetClusterErrors2(rowArr[icl], clsYZ[1], trcIn.getSnp(), trcIn.getTgl(), clsCov[0], clsCov[2]); - uint8_t sectCurr = sectArr[icl]; - if (!trcIn.rotate(o2::utils::Sector2Angle(sectCurr % 18))) { - LOG(WARNING) << "Rotation to sector " << int(sectCurr % 18) << " failed"; - return false; - } - trcIn.setX(clsX); - trcIn.setY(clsYZ[0]); - trcIn.setZ(clsYZ[1]); - if (!trcIn.update(clsYZ, clsCov)) { - LOG(WARNING) << "Update failed at cluster " << icl << ", chi2 =" << chi2; + int retVal = mTPCRefitter->RefitTrackAsTrackParCov(trcIn, tpcTrOrig.getClusterRef(), timeTB, &chi2, false, true); // inward refit with matrix reset + if (retVal < 0) { + LOG(WARNING) << "Refit failed"; LOG(WARNING) << trcIn.asString(); return false; } - for (; icl--;) { - if (sectArr[icl] != sectCurr) { - sectCurr = sectArr[icl]; - if (!trcIn.rotate(o2::utils::Sector2Angle(sectCurr % 18))) { - LOG(WARNING) << "Rotation to sector " << int(sectCurr % 18) << " failed"; - LOG(WARNING) << trcIn.asString(); - return false; - } - } - mTPCTransform->Transform(sectArr[icl], rowArr[icl], clsArr[icl]->getPad(), clsArr[icl]->getTime(), clsX, clsYZ[0], clsYZ[1], timeTB); - mTPCClusterParam->GetClusterErrors2(rowArr[icl], clsYZ[1], trcIn.getSnp(), trcIn.getTgl(), clsCov[0], clsCov[2]); - if (!propagator->PropagateToXBxByBz(trcIn, clsX, m, TolSNP, 10., MatCorrType::USEMatCorrNONE)) { // no material correction! - LOG(INFO) << "Propagation to cluster at X=" - << clsX << " failed, Xtr=" << trcIn.getX() << " snp=" << trcIn.getSnp() << " pT=" << trcIn.getPt(); - LOG(WARNING) << trcIn.asString(); - return false; - } - chi2 += trcIn.getPredictedChi2(clsYZ, clsCov); - if (!trcIn.update(clsYZ, clsCov)) { - LOG(WARNING) << "Update failed at cluster " << icl << ", chi2 =" << chi2; - LOG(WARNING) << trcIn.asString(); - return false; - } - } + // // propagate to the inner edge of the TPC // Note: it is allowed to not reach the requested radius - if (!propagator->PropagateToXBxByBz(trcIn, xTgt, m, MaxSnp, 2., mUseMatCorrFlag)) { - LOG(INFO) << "Propagation to target X=" << xTgt << " failed, Xtr=" << trcIn.getX() << " snp=" << trcIn.getSnp() << " pT=" << trcIn.getPt(); - LOG(WARNING) << trcIn.asString(); + if (!propagator->PropagateToXBxByBz(trcIn, xTgt, MaxSnp, 2., mUseMatCorrFlag)) { + LOG(DEBUG) << "Propagation to target X=" << xTgt << " failed, Xtr=" << trcIn.getX() << " snp=" << trcIn.getSnp() << " pT=" << trcIn.getPt(); + LOG(DEBUG) << trcIn.asString(); return false; } return true; @@ -1733,7 +1533,7 @@ int MatchTPCITS::prepareTPCTracksAfterBurner() mTPCABTimeBinStart.clear(); const auto& outerLr = mRGHelper.layers.back(); // to avoid difference between 3D field propagation and Bz-bazed getXatLabR we propagate RMax+margin - const float ROuter = outerLr.rRange.max() + 0.5f; + const float ROuter = outerLr.rRange.getMax() + 0.5f; auto propagator = o2::base::Propagator::Instance(); @@ -1745,7 +1545,7 @@ int MatchTPCITS::prepareTPCTracksAfterBurner() // which should be good assumption.... float xTgt; if (!tTPC.getXatLabR(ROuter, xTgt, propagator->getNominalBz(), o2::track::DirInward) || - !propagator->PropagateToXBxByBz(tTPC, xTgt, o2::constants::physics::MassPionCharged, MaxSnp, 2., mUseMatCorrFlag)) { + !propagator->PropagateToXBxByBz(tTPC, xTgt, MaxSnp, 2., mUseMatCorrFlag)) { continue; } mTPCABIndexCache.push_back(iTPC); @@ -1756,7 +1556,7 @@ int MatchTPCITS::prepareTPCTracksAfterBurner() std::sort(mTPCABIndexCache.begin(), mTPCABIndexCache.end(), [this](int a, int b) { auto& trcA = mTPCWork[a]; auto& trcB = mTPCWork[b]; - return (trcA.timeBins.min() - trcB.timeBins.min()) < 0.; + return (trcA.timeBins.getMin() - trcB.timeBins.getMin()) < 0.; }); return mTPCABIndexCache.size(); @@ -1766,27 +1566,37 @@ int MatchTPCITS::prepareTPCTracksAfterBurner() int MatchTPCITS::prepareInteractionTimes() { // guess interaction times from various sources and relate with ITS rofs - const float T0UncertaintyTB = 0.5 / (1e3 * mTPCTBinMUS); // assumed T0 time uncertainty (~0.5ns) in TPC timeBins + float ft0UncertaintyTB = 0.5e-3 * mTPCTBinMUSInv; // assumed T0 time uncertainty (~0.5ns) in TPC timeBins mInteractions.clear(); + mITSROFIntCandEntries.clear(); + int nITSROFs = mITSROFTimes.size(); + mITSROFIntCandEntries.resize(nITSROFs); + if (mFITInfo.size()) { + int rof = 0; for (const auto& ft : mFITInfo) { - if (!ft.isValidTime(o2::ft0::RecPoints::TimeMean)) { + if (!mFT0Params->isSelected(ft)) { continue; } - auto fitTime = intRecord2TPCTimeBin(ft.getInteractionRecord()); // FIT time in TPC timebins + auto fitTime = time2TPCTimeBin(mFT0Params->getInteractionTimeNS(ft, mStartIR) * 1e-3); // FIT time in TPC timebins // find corresponding ITS ROF, works both in cont. and trigg. modes (ignore T0 MeanTime within the BC) - int nITSROFs = mITSROFTimes.size(); - for (int rof = 0; rof < nITSROFs; rof++) { + for (; rof < nITSROFs; rof++) { if (mITSROFTimes[rof] < fitTime) { continue; } - if (fitTime >= mITSROFTimes[rof].min()) { // belongs to this ROF - mInteractions.emplace_back(ft.getInteractionRecord(), fitTime, T0UncertaintyTB, rof, o2::detectors::DetID::FT0); + if (fitTime >= mITSROFTimes[rof].getMin()) { // belongs to this ROF + auto& ref = mITSROFIntCandEntries[rof]; + if (!ref.getEntries()) { + ref.setFirstEntry(mInteractions.size()); // register entry + } + ref.changeEntriesBy(1); // increment counter + mInteractions.emplace_back(ft.getInteractionRecord(), fitTime, ft0UncertaintyTB, rof, o2::detectors::DetID::FT0); } break; // this or next ITSrof in time is > fitTime } } } + return mInteractions.size(); } @@ -1795,7 +1605,7 @@ void MatchTPCITS::runAfterBurner() { mABTrackLinks.clear(); - int nIntCand = prepareInteractionTimes(); + int nIntCand = mInteractions.size(); int nTPCCand = prepareTPCTracksAfterBurner(); LOG(INFO) << "AfterBurner will check " << nIntCand << " interaction candindates for " << nTPCCand << " TPC tracks"; if (!nIntCand || !nTPCCand) { @@ -1835,7 +1645,7 @@ void MatchTPCITS::runAfterBurner() } else if (iCRes > 0) { continue; // TPC track precedes the interaction (means orphan track?), no need to check it } else { - LOG(INFO) << "All interaction candidates precede track " << itr << " [" << tTPC.timeBins.min() << ":" << tTPC.timeBins.max() << "]"; + LOG(INFO) << "All interaction candidates precede track " << itr << " [" << tTPC.timeBins.getMin() << ":" << tTPC.timeBins.getMax() << "]"; break; // all interaction candidates precede TPC track } } @@ -1879,8 +1689,8 @@ bool MatchTPCITS::runAfterBurner(int tpcWID, int iCStart, int iCEnd) /* // tmp LOG(INFO) << "Check track TPC mtc=" << tTPC.matchID << " int.cand. " << iCC - << " [" << tTPC.timeBins.min() << ":" << tTPC.timeBins.max() << "] for interaction " - << " [" << iCCand.timeBins.min() << ":" << iCCand.timeBins.max() << "]"; + << " [" << tTPC.timeBins.getMin() << ":" << tTPC.timeBins.getMax() << "] for interaction " + << " [" << iCCand.timeBins.getMin() << ":" << iCCand.timeBins.getMax() << "]"; */ if (std::abs(topLink.getZ()) > mITSFiducialZCut) { // we can discard this seed topLink.disable(); @@ -1902,7 +1712,7 @@ bool MatchTPCITS::runAfterBurner(int tpcWID, int iCStart, int iCEnd) // printABTracksTree(abTrackLinksList); // tmp tmp } // disable link-list if neiher of seeds reached highest requested layer - if (abTrackLinksList.lowestLayer > mParams->ABRequireToReachLayer) { + if (abTrackLinksList.lowestLayer > mParams->requireToReachLayerAB) { destroyLastABTrackLinksList(); tTPC.matchID = MinusTen; return false; @@ -1928,8 +1738,8 @@ int MatchTPCITS::checkABSeedFromLr(int lrSeed, int seedID, ABTrackLinksList& lli auto propagator = o2::base::Propagator::Instance(); float xTgt; const auto& lr = mRGHelper.layers[lrTgt]; - if (!seed.getXatLabR(lr.rRange.max(), xTgt, propagator->getNominalBz(), o2::track::DirInward) || - !propagator->PropagateToXBxByBz(seed, xTgt, o2::constants::physics::MassPionCharged, MaxSnp, 2., mUseMatCorrFlag)) { + if (!seed.getXatLabR(lr.rRange.getMax(), xTgt, propagator->getNominalBz(), o2::track::DirInward) || + !propagator->PropagateToXBxByBz(seed, xTgt, MaxSnp, 2., mUseMatCorrFlag)) { return 0; } auto icCandID = seedLink.icCandID; @@ -1946,8 +1756,8 @@ int MatchTPCITS::checkABSeedFromLr(int lrSeed, int seedID, ABTrackLinksList& lli return 0; } std::vector<int> chipSelClusters; // preliminary cluster candidates //RS TODO do we keep this local / consider array instead of vector - o2::utils::CircleXY trcCircle; - o2::utils::IntervalXY trcLinPar; // line parameters for B OFF data + o2::math_utils::CircleXYf_t trcCircle; + o2::math_utils::IntervalXYf_t trcLinPar; // line parameters for B OFF data // approximate errors float errY = std::sqrt(seed.getSigmaY2() + YErr2Extra), errYFrac = errY * mRGHelper.ladderWidthInv(), errPhi = errY * lr.rInv; if (mFieldON) { @@ -1956,15 +1766,18 @@ int MatchTPCITS::checkABSeedFromLr(int lrSeed, int seedID, ABTrackLinksList& lli seed.getLineParams(trcLinPar, sna, csa); } float xCurr, yCurr; - o2::utils::rotateZ(seed.getX(), seed.getY(), xCurr, yCurr, sna, csa); + o2::math_utils::rotateZ(seed.getX(), seed.getY(), xCurr, yCurr, sna, csa); float phi = std::atan2(yCurr, xCurr); // RS: TODO : can we use fast atan2 here? // find approximate ladder and chip_in_ladder corresponding to this track extrapolation int nLad2Check = 0, ladIDguess = lr.getLadderID(phi), chipIDguess = lr.getChipID(seed.getZ() + 0.5 * zDRStep); std::array<int, MaxLadderCand> lad2Check; nLad2Check = mFieldON ? findLaddersToCheckBOn(lrTgt, ladIDguess, trcCircle, errYFrac, lad2Check) : findLaddersToCheckBOff(lrTgt, ladIDguess, trcLinPar, errYFrac, lad2Check); - const auto& tTPC = mTPCWork[llist.trackID]; // tmp - auto lblTrc = mTPCTrkLabels->getLabels(tTPC.sourceID)[0]; // tmp + const auto& tTPC = mTPCWork[llist.trackID]; // tmp + o2::MCCompLabel lblTrc; + if (mMCTruthON) { + lblTrc = mTPCTrkLabels[tTPC.sourceID]; // tmp + } for (int ilad = nLad2Check; ilad--;) { int ladID = lad2Check[ilad]; const auto& lad = lr.ladders[ladID]; @@ -1996,7 +1809,7 @@ int MatchTPCITS::checkABSeedFromLr(int lrSeed, int seedID, ABTrackLinksList& lli printf("Lr %d #%d/%d LadID: %d (phi:%+d) ChipID: %d [%d Ncl: %d from %d] (rRhi:%d Z:%+d[%+.1f:%+.1f]) | %+.3f %+.3f -> %+.3f %+.3f %+.3f (zErr: %.3f)\n", lrTgt, ilad, ich, ladID, lad.isPhiOutside(phi, errPhi), chipID, chipGID, clRange.getEntries(), clRange.getFirstEntry(), - lad.chips[chipID].xyEdges.seenByCircle(trcCircle, errYFrac), lad.chips[chipID].zRange.isOutside(zCross, 3 * errZ), lad.chips[chipID].zRange.min(), lad.chips[chipID].zRange.max(), + lad.chips[chipID].xyEdges.seenByCircle(trcCircle, errYFrac), lad.chips[chipID].zRange.isOutside(zCross, 3 * errZ), lad.chips[chipID].zRange.getMin(), lad.chips[chipID].zRange.getMax(), xCurr, yCurr, xCross, yCross, zCross, errZ); */ // track Y error in chip frame @@ -2073,7 +1886,7 @@ void MatchTPCITS::mergeABSeedsOnOverlaps(int ilrPar, ABTrackLinksList& llist) } //______________________________________________ -int MatchTPCITS::findLaddersToCheckBOn(int ilr, int lad0, const o2::utils::CircleXY& circle, float errYFrac, +int MatchTPCITS::findLaddersToCheckBOn(int ilr, int lad0, const o2::math_utils::CircleXYf_t& circle, float errYFrac, std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const { // check if ladder lad0 and at most +-MaxUpDnLadders around it are compatible with circular track of @@ -2109,7 +1922,7 @@ int MatchTPCITS::findLaddersToCheckBOn(int ilr, int lad0, const o2::utils::Circl } //______________________________________________ -int MatchTPCITS::findLaddersToCheckBOff(int ilr, int lad0, const o2::utils::IntervalXY& trcLinPar, float errYFrac, +int MatchTPCITS::findLaddersToCheckBOff(int ilr, int lad0, const o2::math_utils::IntervalXYf_t& trcLinPar, float errYFrac, std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const { // check if ladder lad0 and at most +-MaxUpDnLadders around it are compatible with linear track @@ -2156,7 +1969,7 @@ void MatchTPCITS::buildABCluster2TracksLinks() continue; } // register all clusters of all seeds starting from the innermost layer - for (int lr = trList.lowestLayer; lr <= mParams->ABRequireToReachLayer; lr++) { + for (int lr = trList.lowestLayer; lr <= mParams->requireToReachLayerAB; lr++) { int finalTrackLinkIdx = trList.firstInLr[lr]; while (finalTrackLinkIdx > MinusOne) { // loop over all links of this layer auto& finalTrackLink = mABTrackLinks[finalTrackLinkIdx]; @@ -2294,7 +2107,7 @@ float MatchTPCITS::correctTPCTrack(o2::track::TrackParCov& trc, const TrackLocTP float xTgt; auto propagator = o2::base::Propagator::Instance(); if (!trc.getXatLabR(r, xTgt, propagator->getNominalBz(), o2::track::DirInward) || - !propagator->PropagateToXBxByBz(trc, xTgt, o2::constants::physics::MassPionCharged, MaxSnp, 2., mUseMatCorrFlag)) { + !propagator->PropagateToXBxByBz(trc, xTgt, MaxSnp, 2., mUseMatCorrFlag)) { return -1; } } @@ -2460,7 +2273,7 @@ bool MatchTPCITS::validateABMatch(int ilink) if (linkCl.linkedABTrack != headID) { // best link for this cluster differs from the link we are checking return false; } - } else if (lnk.isDummyTop() && mTPCTrkLabels) { // top layer reached, this is winner + } else if (lnk.isDummyTop()) { // top layer reached, this is winner break; } parID = lnk.parentID; // check next cluster of the same link @@ -2499,7 +2312,7 @@ void MatchTPCITS::buildBestLinksList(int ilink) trList.bestOrdLinkID = start; // now check if shorter seed links from layers above need to be considered as final track candidates - while (++lowestLayer <= mParams->ABRequireToReachLayer) { + while (++lowestLayer <= mParams->requireToReachLayerAB) { nextID = trList.firstInLr[lowestLayer]; while (nextID > MinusOne) { @@ -2575,8 +2388,7 @@ void MatchTPCITS::refitABTrack(int ibest) const while (ibest > MinusOne) { const auto& lnk = mABTrackLinks[ibest]; if (!trc.rotate(lnk.getAlpha()) || - !propagator->propagateToX(trc, lnk.getX(), propagator->getNominalBz(), o2::constants::physics::MassPionCharged, - MaxSnp, maxStep, mUseMatCorrFlag, nullptr)) { + !propagator->propagateToX(trc, lnk.getX(), propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, nullptr)) { LOG(WARNING) << "Failed to rotate to " << lnk.getAlpha() << " or propagate to " << lnk.getX(); LOG(WARNING) << trc.asString(); break; @@ -2607,6 +2419,58 @@ void MatchTPCITS::setITSROFrameLengthInBC(int nbc) mITSROFrameLengthMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; } +//___________________________________________________________________ +void MatchTPCITS::setBunchFilling(const o2::BunchFilling& bf) +{ + mBunchFilling = bf; + // find closest (from above) filled bunch + int minBC = bf.getFirstFilledBC(), maxBC = bf.getLastFilledBC(); + if (minBC < 0) { + throw std::runtime_error("Bunch filling is not set in MatchTPCITS"); + } + int bcAbove = minBC; + for (int i = o2::constants::lhc::LHCMaxBunches; i--;) { + if (bf.testBC(i)) { + bcAbove = i; + } + mClosestBunchAbove[i] = bcAbove; + } + int bcBelow = maxBC; + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + if (bf.testBC(i)) { + bcBelow = i; + } + mClosestBunchBelow[i] = bcBelow; + } +} + +//___________________________________________________________________ +MatchTPCITS::BracketIR MatchTPCITS::tpcTimeBin2IRBracket(const BracketF tbrange) +{ + // convert TPC timebins bracket to IR bracket + o2::InteractionRecord irMin(mStartIR), irMax(mStartIR); + if (tbrange.getMin() > 0) { + irMin += o2::InteractionRecord(tpcTimeBin2NS(tbrange.getMin())); + } + irMax += o2::InteractionRecord(tpcTimeBin2NS(tbrange.getMax())); + irMax++; // to account for rounding + int bc = mClosestBunchAbove[irMin.bc]; + if (bc < irMin.bc) { + irMin.orbit++; + } + irMin.bc = bc; + bc = mClosestBunchBelow[irMax.bc]; + if (bc > irMax.bc) { + if (irMax.orbit == 0) { + bc = 0; + } else { + irMax.orbit--; + } + } + irMax.bc = bc; + return {irMin, irMax}; +} + //<<============================= AfterBurner for TPC-track / ITS cluster matching ===================<< #ifdef _ALLOW_DEBUG_TREES_ diff --git a/Detectors/GlobalTracking/src/MatchTPCITSParams.cxx b/Detectors/GlobalTracking/src/MatchTPCITSParams.cxx index e0f046725c737..6783099b24074 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITSParams.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITSParams.cxx @@ -13,4 +13,4 @@ /// \author ruben.shahoyan@cern.ch #include "GlobalTracking/MatchTPCITSParams.h" -O2ParamImpl(o2::globaltracking::MatchITSTPCParams); +O2ParamImpl(o2::globaltracking::MatchTPCITSParams); diff --git a/Detectors/GlobalTrackingWorkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/CMakeLists.txt index 729d108fec904..8e75ed40d0cfe 100644 --- a/Detectors/GlobalTrackingWorkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/CMakeLists.txt @@ -10,16 +10,51 @@ # FIXME: do we actually need a library here, or is the executable enough ? o2_add_library(GlobalTrackingWorkflow - SOURCES src/TrackWriterTPCITSSpec.cxx src/TPCITSMatchingSpec.cxx - src/MatchTPCITSWorkflow.cxx src/TrackTPCITSReaderSpec.cxx - PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::ITStracking O2::ITSWorkflow - O2::TPCWorkflow O2::FITWorkflow - O2::ITSMFTWorkflow) + SOURCES src/TrackWriterTPCITSSpec.cxx + src/TPCITSMatchingSpec.cxx + src/MatchTPCITSWorkflow.cxx + src/TrackTPCITSReaderSpec.cxx + src/PrimaryVertexingSpec.cxx + src/PrimaryVertexWriterSpec.cxx + src/PrimaryVertexReaderSpec.cxx + src/VertexTrackMatcherSpec.cxx + src/SecondaryVertexingSpec.cxx + src/SecondaryVertexWriterSpec.cxx + src/SecondaryVertexReaderSpec.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTracking + O2::ITStracking + O2::ITSWorkflow + O2::TPCWorkflow + O2::FT0Workflow + O2::ITSMFTWorkflow + O2::SimulationDataFormat + O2::DetectorsVertexing) o2_add_executable(match-workflow COMPONENT_NAME tpcits SOURCES src/tpcits-match-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) +o2_add_executable(vertexing-workflow + COMPONENT_NAME primary + SOURCES src/primary-vertexing-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) + +o2_add_executable(vertex-reader-workflow + COMPONENT_NAME primary + SOURCES src/primary-vertex-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) + +o2_add_executable(vertex-reader-workflow + COMPONENT_NAME secondary + SOURCES src/secondary-vertex-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) + +o2_add_executable(vertexing-workflow + COMPONENT_NAME secondary + SOURCES src/secondary-vertexing-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) + + add_subdirectory(tofworkflow) add_subdirectory(tpcinterpolationworkflow) diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/MatchTPCITSWorkflow.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/MatchTPCITSWorkflow.h index 922557c48cfea..cc9f9d3b7a403 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/MatchTPCITSWorkflow.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/MatchTPCITSWorkflow.h @@ -20,7 +20,7 @@ namespace o2 namespace globaltracking { -framework::WorkflowSpec getMatchTPCITSWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getMatchTPCITSWorkflow(bool useFT0, bool calib, bool useMC, bool disableRootInp, bool disableRootOut); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h new file mode 100644 index 0000000000000..6a32eb2c537ce --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PrimaryVertexReaderSpec.h + +#ifndef O2_PRIMARY_VERTEXREADER +#define O2_PRIMARY_VERTEXREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" + +#include "CommonDataFormat/TimeStamp.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "SimulationDataFormat/MCEventLabel.h" + +namespace o2 +{ +namespace vertexing +{ +// read primary vertices produces by the o2-primary-vertexing-workflow + +class PrimaryVertexReader : public o2::framework::Task +{ + using Label = o2::MCEventLabel; + using V2TRef = o2::dataformats::VtxTrackRef; + using PVertex = o2::dataformats::PrimaryVertex; + using GIndex = o2::dataformats::VtxTrackIndex; + + public: + PrimaryVertexReader(bool useMC) : mUseMC(useMC) {} + ~PrimaryVertexReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(); + + bool mVerbose = false; + bool mUseMC = false; + + std::vector<PVertex> mVertices, *mVerticesPtr = &mVertices; + std::vector<Label> mLabels, *mLabelsPtr = &mLabels; + std::vector<V2TRef> mPV2MatchIdxRef, *mPV2MatchIdxRefPtr = &mPV2MatchIdxRef; + std::vector<GIndex> mPV2MatchIdx, *mPV2MatchIdxPtr = &mPV2MatchIdx; + + std::unique_ptr<TFile> mFile; + std::unique_ptr<TTree> mTree; + std::string mFileName = ""; + std::string mFileNameMatches = ""; + std::string mVertexTreeName = "o2sim"; + std::string mVertexBranchName = "PrimaryVertex"; + std::string mVertexTrackIDsBranchName = "PVTrackIndices"; + std::string mVertex2TrackIDRefsBranchName = "PV2TrackRefs"; + std::string mVertexLabelsBranchName = "PVMCTruth"; +}; + +/// create a processor spec +/// read primary vertex data from a root file +o2::framework::DataProcessorSpec getPrimaryVertexReaderSpec(bool useMC); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexWriterSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexWriterSpec.h new file mode 100644 index 0000000000000..b722866e17e52 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexWriterSpec.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PrimaryVertexWriterSpec.h + +#ifndef O2_PRIMARY_VERTEX_WRITER +#define O2_PRIMARY_VERTEX_WRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace vertexing +{ + +/// create a processor spec +framework::DataProcessorSpec getPrimaryVertexWriterSpec(bool disableMatching, bool useMC); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h new file mode 100644 index 0000000000000..b419f788dc220 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PRIMARY_VERTEXER_SPEC_H +#define O2_PRIMARY_VERTEXER_SPEC_H + +/// @file PrimaryVertexingSpec.h + +#include "DetectorsVertexing/PVertexer.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TStopwatch.h" + +namespace o2 +{ +namespace vertexing +{ + +using namespace o2::framework; + +class PrimaryVertexingSpec : public Task +{ + public: + PrimaryVertexingSpec(bool validateWithFT0, bool useMC) : mUseMC(useMC), mValidateWithFT0(validateWithFT0) {} + ~PrimaryVertexingSpec() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + + private: + o2::vertexing::PVertexer mVertexer; + bool mUseMC{false}; ///< MC flag + bool mValidateWithFT0{false}; ///< require vertex validation with FT0 + TStopwatch mTimer; +}; + +/// create a processor spec +DataProcessorSpec getPrimaryVertexingSpec(bool validateWithFT0, bool useMC); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexReaderSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexReaderSpec.h new file mode 100644 index 0000000000000..c7864843e2b47 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexReaderSpec.h @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SecondaryVertexReaderSpec.h + +#ifndef O2_SECONDARY_VERTEXREADER +#define O2_SECONDARY_VERTEXREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "CommonDataFormat/RangeReference.h" +#include "ReconstructionDataFormats/V0.h" + +namespace o2 +{ +namespace vertexing +{ +// read secondary vertices produces by the o2-secondary-vertexing-workflow + +class SecondaryVertexReader : public o2::framework::Task +{ + using RRef = o2::dataformats::RangeReference<int, int>; + using V0 = o2::dataformats::V0; + + public: + SecondaryVertexReader() = default; + ~SecondaryVertexReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(); + + bool mVerbose = false; + + std::vector<V0> mV0s, *mV0sPtr = &mV0s; + std::vector<RRef> mPV2V0Ref, *mPV2V0RefPtr = &mPV2V0Ref; + + std::unique_ptr<TFile> mFile; + std::unique_ptr<TTree> mTree; + std::string mFileName = ""; + std::string mFileNameMatches = ""; + std::string mSVertexTreeName = "o2sim"; + std::string mV0BranchName = "V0s"; + std::string mPVertex2V0RefBranchName = "PV2V0Refs"; +}; + +/// create a processor spec +/// read secondary vertex data from a root file +o2::framework::DataProcessorSpec getSecondaryVertexReaderSpec(); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h new file mode 100644 index 0000000000000..76b3b1d815119 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SecondaryVertexWriterSpec.h + +#ifndef O2_SECONDARY_VERTEX_WRITER +#define O2_SECONDARY_VERTEX_WRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace vertexing +{ + +/// create a processor spec +framework::DataProcessorSpec getSecondaryVertexWriterSpec(); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h new file mode 100644 index 0000000000000..e419c3e3b570a --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h @@ -0,0 +1,48 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SECONDARY_VERTEXER_SPEC_H +#define O2_SECONDARY_VERTEXER_SPEC_H + +/// @file SecondaryVertexingSpec.h + +#include "DetectorsVertexing/SVertexer.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TStopwatch.h" + +namespace o2 +{ +namespace vertexing +{ + +using namespace o2::framework; + +class SecondaryVertexingSpec : public Task +{ + public: + SecondaryVertexingSpec() = default; + ~SecondaryVertexingSpec() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + + private: + o2::vertexing::SVertexer mVertexer; + TStopwatch mTimer; +}; + +/// create a processor spec +DataProcessorSpec getSecondaryVertexingSpec(); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h index a33aa93159abe..3d63a7b26257b 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h @@ -32,8 +32,7 @@ namespace globaltracking class TPCITSMatchingDPL : public Task { public: - TPCITSMatchingDPL(bool useMC) - : mUseMC(useMC) {} + TPCITSMatchingDPL(bool useFT0, bool calib, bool useMC) : mUseFT0(useFT0), mCalibMode(calib), mUseMC(useMC) {} ~TPCITSMatchingDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -42,12 +41,14 @@ class TPCITSMatchingDPL : public Task private: o2::globaltracking::MatchTPCITS mMatching; // matching engine o2::itsmft::TopologyDictionary mITSDict; // cluster patterns dictionary + bool mUseFT0 = false; + bool mCalibMode = false; bool mUseMC = true; TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getTPCITSMatchingSpec(bool useMC, const std::vector<int>& tpcClusLanes); +framework::DataProcessorSpec getTPCITSMatchingSpec(bool useFT0, bool calib, bool useMC, const std::vector<int>& tpcClusLanes); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/VertexTrackMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/VertexTrackMatcherSpec.h new file mode 100644 index 0000000000000..418331b3c6445 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/VertexTrackMatcherSpec.h @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_VERTEX_TRACK_MATCHER_SPEC_H +#define O2_VERTEX_TRACK_MATCHER_SPEC_H + +/// @file VertexTrackMatcherSpec.h +/// @brief Specs for vertex track association device +/// @author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/VertexTrackMatcher.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TStopwatch.h" + +namespace o2 +{ +namespace vertexing +{ + +using namespace o2::framework; + +class VertexTrackMatcherSpec : public Task +{ + public: + VertexTrackMatcherSpec() = default; + ~VertexTrackMatcherSpec() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + + private: + o2::vertexing::VertexTrackMatcher mMatcher; + TStopwatch mTimer; +}; + +/// create a processor spec +DataProcessorSpec getVertexTrackMatcherSpec(); + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/src/MatchTPCITSWorkflow.cxx b/Detectors/GlobalTrackingWorkflow/src/MatchTPCITSWorkflow.cxx index efc72a1ba758e..dd5930b3d0d28 100644 --- a/Detectors/GlobalTrackingWorkflow/src/MatchTPCITSWorkflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/MatchTPCITSWorkflow.cxx @@ -14,7 +14,7 @@ #include "ITSWorkflow/TrackReaderSpec.h" #include "TPCWorkflow/TrackReaderSpec.h" #include "TPCWorkflow/PublisherSpec.h" -#include "FITWorkflow/FT0RecPointReaderSpec.h" +#include "FT0Workflow/RecPointReaderSpec.h" #include "GlobalTrackingWorkflow/TPCITSMatchingSpec.h" #include "GlobalTrackingWorkflow/MatchTPCITSWorkflow.h" #include "GlobalTrackingWorkflow/TrackWriterTPCITSSpec.h" @@ -27,7 +27,7 @@ namespace o2 namespace globaltracking { -framework::WorkflowSpec getMatchTPCITSWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getMatchTPCITSWorkflow(bool useFT0, bool useMC, bool disableRootInp, bool disableRootOut, bool calib) { framework::WorkflowSpec specs; @@ -53,11 +53,11 @@ framework::WorkflowSpec getMatchTPCITSWorkflow(bool useMC, bool disableRootInp, tpcClusLanes}, useMC)); - if (o2::globaltracking::MatchITSTPCParams::Instance().runAfterBurner) { - specs.emplace_back(o2::ft0::getFT0RecPointReaderSpec(useMC)); + if (useFT0) { + specs.emplace_back(o2::ft0::getRecPointReaderSpec(useMC)); } } - specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(useMC, tpcClusLanes)); + specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(useFT0, calib, useMC, tpcClusLanes)); if (!disableRootOut) { specs.emplace_back(o2::globaltracking::getTrackWriterTPCITSSpec(useMC)); diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexReaderSpec.cxx new file mode 100644 index 0000000000000..a5640d7503689 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexReaderSpec.cxx @@ -0,0 +1,128 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VertexReaderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace vertexing +{ + +void PrimaryVertexReader::init(InitContext& ic) +{ + mFileName = ic.options().get<std::string>("primary-vertex-infile"); + connectTree(); +} + +void PrimaryVertexReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + 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); + + if (mUseMC) { + pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0, Lifetime::Timeframe}, mLabels); + } + + if (mVerbose) { + int cnt = 0; + for (const auto& vtx : mVertices) { + Label lb; + if (mUseMC) { + lb = mLabels[cnt]; + } + LOG(INFO) << "#" << cnt << " " << vtx << " | MC:" << lb; + LOG(INFO) << "References: " << mPV2MatchIdxRef[cnt]; + for (int is = 0; is < GIndex::NSources; is++) { + LOG(INFO) << GIndex::getSourceName(is) << " : " << mPV2MatchIdxRef[cnt].getEntriesOfSource(is) << " attached:"; + int idMin = mPV2MatchIdxRef[cnt].getFirstEntryOfSource(is), idMax = idMin + mPV2MatchIdxRef[cnt].getEntriesOfSource(is); + std::string trIDs; + int cntT = 0; + for (int i = idMin; i < idMax; i++) { + trIDs += mPV2MatchIdx[i].asString() + " "; + if (!((++cntT) % 15)) { + LOG(INFO) << trIDs; + trIDs = ""; + } + } + if (!trIDs.empty()) { + LOG(INFO) << trIDs; + } + } + cnt++; + } + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } +} + +void PrimaryVertexReader::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(mVertexTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mVertexBranchName.c_str())); + assert(mTree->GetBranch(mVertexTrackIDsBranchName.c_str())); + assert(mTree->GetBranch(mVertex2TrackIDRefsBranchName.c_str())); + + mTree->SetBranchAddress(mVertexBranchName.c_str(), &mVerticesPtr); + mTree->SetBranchAddress(mVertexTrackIDsBranchName.c_str(), &mPV2MatchIdxPtr); + mTree->SetBranchAddress(mVertex2TrackIDRefsBranchName.c_str(), &mPV2MatchIdxRefPtr); + + if (mUseMC) { + assert(mTree->GetBranch(mVertexLabelsBranchName.c_str())); + mTree->SetBranchAddress(mVertexLabelsBranchName.c_str(), &mLabelsPtr); + } + + LOG(INFO) << "Loaded " << mVertexTreeName << " tree from " << mFileName << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getPrimaryVertexReaderSpec(bool useMC) +{ + std::vector<OutputSpec> outputs; + outputs.emplace_back("GLO", "PVTX", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe); + + if (useMC) { + outputs.emplace_back("GLO", "PVTX_MCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "primary-vertex-reader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<PrimaryVertexReader>(useMC)}, + Options{ + {"primary-vertex-infile", VariantType::String, "o2_primary_vertex.root", {"Name of the input primary vertex file"}}, + {"vertex-track-matches-infile", VariantType::String, "o2_pvertex_track_matches.root", {"Name of the input file with primary vertex - tracks matches"}}}}; +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexWriterSpec.cxx new file mode 100644 index 0000000000000..14770ca5e3be2 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexWriterSpec.cxx @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PrimaryVertexWriterSpec.cxx + +#include <vector> + +#include "GlobalTrackingWorkflow/PrimaryVertexWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "CommonDataFormat/TimeStamp.h" +#include "DetectorsVertexing/PVertexerHelpers.h" +#include "CommonDataFormat/RangeReference.h" +#include "SimulationDataFormat/MCEventLabel.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace vertexing +{ + +template <typename T> +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; + +using Label = o2::MCEventLabel; + +using namespace o2::header; + +DataProcessorSpec getPrimaryVertexWriterSpec(bool disableMatching, bool useMC) +{ + auto logger = [](std::vector<PVertex> const& v) { + LOG(INFO) << "PrimaryVertexWriter pulled " << v.size() << " vertices"; + }; + auto inpID = disableMatching ? InputSpec{"vttrackID", "GLO", "PVTX_CONTID", 0} : InputSpec{"vttrackID", "GLO", "PVTX_TRMTC", 0}; + auto inpIDRef = disableMatching ? InputSpec{"v2tref", "GLO", "PVTX_CONTIDREFS", 0} : InputSpec{"v2tref", "GLO", "PVTX_TRMTCREFS", 0}; + return MakeRootTreeWriterSpec("primary-vertex-writer", + "o2_primary_vertex.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with Primary Vertices"}, + BranchDefinition<std::vector<PVertex>>{InputSpec{"vertices", "GLO", "PVTX", 0}, "PrimaryVertex", logger}, + BranchDefinition<std::vector<V2TRef>>{inpIDRef, "PV2TrackRefs"}, + BranchDefinition<std::vector<GIndex>>{inpID, "PVTrackIndices"}, + BranchDefinition<std::vector<Label>>{InputSpec{"labels", "GLO", "PVTX_MCTR", 0}, "PVMCTruth", (useMC ? 1 : 0), ""})(); +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx new file mode 100644 index 0000000000000..95e46098a117e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -0,0 +1,129 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PrimaryVertexingSpec.cxx + +#include <vector> +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "GlobalTrackingWorkflow/PrimaryVertexingSpec.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "CommonDataFormat/BunchFilling.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "Framework/ConfigParamRegistry.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace vertexing +{ + +void PrimaryVertexingSpec::init(InitContext& ic) +{ + //-------- init geometry and field --------// + o2::base::GeometryManager::loadGeometry(); + o2::base::Propagator::initFieldFromGRP("o2sim_grp.root"); + // this is a hack to provide Mat.LUT from the local file, in general will be provided by the framework from CCDB + std::string matLUTPath = ic.options().get<std::string>("material-lut-path"); + std::string matLUTFile = o2::base::NameConf::getMatLUTFileName(matLUTPath); + if (o2::base::NameConf::pathExists(matLUTFile)) { + auto* lut = o2::base::MatLayerCylSet::loadFromFile(matLUTFile); + o2::base::Propagator::Instance()->setMatLUT(lut); + LOG(INFO) << "Loaded material LUT from " << matLUTFile; + } else { + LOG(INFO) << "Material LUT " << matLUTFile << " file is absent, only TGeo can be used"; + } + mTimer.Stop(); + mTimer.Reset(); + mVertexer.setValidateWithFT0(mValidateWithFT0); + + // set bunch filling. Eventually, this should come from CCDB + const auto* digctx = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto& bcfill = digctx->getBunchFilling(); + mVertexer.setBunchFilling(bcfill); + mVertexer.init(); +} + +void PrimaryVertexingSpec::run(ProcessingContext& pc) +{ + double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); + mTimer.Start(false); + const auto tracksITSTPC = pc.inputs().get<gsl::span<o2::dataformats::TrackTPCITS>>("match"); + gsl::span<const o2::MCCompLabel> lblITS, lblTPC; + gsl::span<const o2::ft0::RecPoints> ft0Data; + if (mValidateWithFT0) { + ft0Data = pc.inputs().get<gsl::span<o2::ft0::RecPoints>>("fitInfo"); + } + if (mUseMC) { + lblITS = pc.inputs().get<gsl::span<o2::MCCompLabel>>("lblITS"); + lblTPC = pc.inputs().get<gsl::span<o2::MCCompLabel>>("lblTPC"); + } + std::vector<PVertex> vertices; + std::vector<GIndex> vertexTrackIDs; + std::vector<V2TRef> v2tRefs; + std::vector<o2::MCEventLabel> lblVtx; + + // RS FIXME this will not have effect until the 1st orbit is propagated, until that will work only for TF starting at orbit 0 + const auto* dh = o2::header::get<o2::header::DataHeader*>(pc.inputs().get("match").header); + mVertexer.setStartIR({0, dh->firstTForbit}); + + mVertexer.process(tracksITSTPC, ft0Data, vertices, vertexTrackIDs, v2tRefs, lblITS, lblTPC, lblVtx); + 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); + + if (mUseMC) { + pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0, Lifetime::Timeframe}, lblVtx); + } + + mTimer.Stop(); + LOG(INFO) << "Found " << vertices.size() << " primary vertices, timing: CPU: " + << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0 << " s"; +} + +void PrimaryVertexingSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "Primary vertexing total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getPrimaryVertexingSpec(bool validateWithFT0, bool useMC) +{ + std::vector<InputSpec> inputs; + std::vector<OutputSpec> outputs; + + inputs.emplace_back("match", "GLO", "TPCITS", 0, Lifetime::Timeframe); + if (validateWithFT0) { + inputs.emplace_back("fitInfo", "FT0", "RECPOINTS", 0, Lifetime::Timeframe); + } + + outputs.emplace_back("GLO", "PVTX", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_CONTID", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_CONTIDREFS", 0, Lifetime::Timeframe); + + if (useMC) { + inputs.emplace_back("lblITS", "GLO", "TPCITS_ITSMC", 0, Lifetime::Timeframe); + inputs.emplace_back("lblTPC", "GLO", "TPCITS_TPCMC", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_MCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "primary-vertexing", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<PrimaryVertexingSpec>(validateWithFT0, useMC)}, + Options{{"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}}}; +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexReaderSpec.cxx new file mode 100644 index 0000000000000..5b4e068369bca --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexReaderSpec.cxx @@ -0,0 +1,81 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SecondaryVertexReaderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "GlobalTrackingWorkflow/SecondaryVertexReaderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace vertexing +{ + +void SecondaryVertexReader::init(InitContext& ic) +{ + mFileName = ic.options().get<std::string>("secondary-vertex-infile"); + connectTree(); +} + +void SecondaryVertexReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(INFO) << "Pushing " << mV0sPtr->size() << " V0s at entry " << ent; + + pc.outputs().snapshot(Output{"GLO", "V0s", 0, Lifetime::Timeframe}, mV0s); + pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}, mPV2V0Ref); + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } +} + +void SecondaryVertexReader::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(mSVertexTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mV0BranchName.c_str())); + assert(mTree->GetBranch(mPVertex2V0RefBranchName.c_str())); + + mTree->SetBranchAddress(mV0BranchName.c_str(), &mV0sPtr); + mTree->SetBranchAddress(mPVertex2V0RefBranchName.c_str(), &mPV2V0RefPtr); + + LOG(INFO) << "Loaded " << mSVertexTreeName << " tree from " << mFileName << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getSecondaryVertexReaderSpec() +{ + std::vector<OutputSpec> outputs; + outputs.emplace_back("GLO", "V0s", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "secondary-vertex-reader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<SecondaryVertexReader>()}, + Options{ + {"secondary-vertex-infile", VariantType::String, "o2_secondary_vertex.root", {"Name of the input secondary vertex file"}}}}; +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx new file mode 100644 index 0000000000000..021951c35bc78 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SecondaryVertexWriterSpec.cxx + +#include <vector> + +#include "GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "CommonDataFormat/TimeStamp.h" +#include "CommonDataFormat/RangeReference.h" +#include "ReconstructionDataFormats/V0.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace vertexing +{ +using RRef = o2::dataformats::RangeReference<int, int>; +using V0 = o2::dataformats::V0; + +template <typename T> +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; + +using namespace o2::header; + +DataProcessorSpec getSecondaryVertexWriterSpec() +{ + auto logger = [](std::vector<V0> const& v) { + LOG(INFO) << "SecondaryVertexWriter pulled " << v.size() << " v0s"; + }; + auto inpID = InputSpec{"v0s", "GLO", "V0s", 0}; + auto inpIDRef = InputSpec{"pv2v0ref", "GLO", "PVTX_V0REFS", 0}; + return MakeRootTreeWriterSpec("secondary-vertex-writer", + "o2_secondary_vertex.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with Secondary Vertices"}, + BranchDefinition<std::vector<V0>>{inpID, "V0s", logger}, + BranchDefinition<std::vector<RRef>>{inpIDRef, "PV2V0Refs"})(); +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx new file mode 100644 index 0000000000000..87f51f104549c --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -0,0 +1,120 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SecondaryVertexingSpec.cxx + +#include <vector> +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "GlobalTrackingWorkflow/SecondaryVertexingSpec.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "CommonDataFormat/BunchFilling.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "Framework/ConfigParamRegistry.h" + +using namespace o2::framework; + +using GIndex = o2::dataformats::VtxTrackIndex; +using VRef = o2::dataformats::VtxTrackRef; +using PVertex = const o2::dataformats::PrimaryVertex; +using V0 = o2::dataformats::V0; +using TrackTPCITS = o2::dataformats::TrackTPCITS; +using TrackITS = o2::its::TrackITS; +using TrackTPC = o2::tpc::TrackTPC; +using RRef = o2::dataformats::RangeReference<int, int>; + +namespace o2 +{ +namespace vertexing +{ + +void SecondaryVertexingSpec::init(InitContext& ic) +{ + //-------- init geometry and field --------// + o2::base::GeometryManager::loadGeometry(); + o2::base::Propagator::initFieldFromGRP("o2sim_grp.root"); + // this is a hack to provide Mat.LUT from the local file, in general will be provided by the framework from CCDB + std::string matLUTPath = ic.options().get<std::string>("material-lut-path"); + std::string matLUTFile = o2::base::NameConf::getMatLUTFileName(matLUTPath); + if (o2::base::NameConf::pathExists(matLUTFile)) { + auto* lut = o2::base::MatLayerCylSet::loadFromFile(matLUTFile); + o2::base::Propagator::Instance()->setMatLUT(lut); + LOG(INFO) << "Loaded material LUT from " << matLUTFile; + } else { + LOG(INFO) << "Material LUT " << matLUTFile << " file is absent, only TGeo can be used"; + } + mTimer.Stop(); + mTimer.Reset(); + + mVertexer.init(); +} + +void SecondaryVertexingSpec::run(ProcessingContext& pc) +{ + double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); + mTimer.Start(false); + + const auto tracksITSTPC = pc.inputs().get<gsl::span<o2::dataformats::TrackTPCITS>>("tpcits"); + const auto tracksTPC = pc.inputs().get<gsl::span<o2::tpc::TrackTPC>>("tpc"); + const auto tracksITS = pc.inputs().get<gsl::span<o2::its::TrackITS>>("its"); + + const auto pvertices = pc.inputs().get<gsl::span<o2::dataformats::PrimaryVertex>>("pvtx"); + const auto pvtxTracks = pc.inputs().get<gsl::span<o2::dataformats::VtxTrackIndex>>("pvtx_cont"); + const auto pvtxTrackRefs = pc.inputs().get<gsl::span<o2::dataformats::VtxTrackRef>>("pvtx_tref"); + + std::vector<V0> v0s; + std::vector<RRef> pv2v0ref; + + mVertexer.process(pvertices, pvtxTracks, pvtxTrackRefs, + tracksITSTPC, tracksITS, tracksTPC, + v0s, pv2v0ref); + + pc.outputs().snapshot(Output{"GLO", "V0s", 0, Lifetime::Timeframe}, v0s); + pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}, pv2v0ref); + + mTimer.Stop(); + LOG(INFO) << "Found " << v0s.size() << " V0s, timing: CPU: " + << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0 << " s"; +} + +void SecondaryVertexingSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "Secondary vertexing total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getSecondaryVertexingSpec() +{ + std::vector<InputSpec> inputs; + std::vector<OutputSpec> outputs; + + inputs.emplace_back("pvtx", "GLO", "PVTX", 0, Lifetime::Timeframe); // prim.vertices + inputs.emplace_back("pvtx_cont", "GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe); // global ids of associated tracks + inputs.emplace_back("pvtx_tref", "GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe); // vertex - trackID refs + // + inputs.emplace_back("tpcits", "GLO", "TPCITS", 0, Lifetime::Timeframe); // matched ITS-TPC tracks + inputs.emplace_back("its", "ITS", "TRACKS", 0, Lifetime::Timeframe); // standalone ITS tracks + inputs.emplace_back("tpc", "TPC", "TRACKS", 0, Lifetime::Timeframe); // standalone TPC tracks + // + outputs.emplace_back("GLO", "V0s", 0, Lifetime::Timeframe); // found V0s + outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs + + return DataProcessorSpec{ + "secondary-vertexing", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<SecondaryVertexingSpec>()}, + Options{{"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}}}; +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 1a88857858b89..6cf75338fea35 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -18,6 +18,7 @@ #include "ReconstructionDataFormats/TrackTPCITS.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/DigitizationContext.h" #include "DataFormatsITS/TrackITS.h" #include "DataFormatsITSMFT/Cluster.h" #include "DataFormatsITSMFT/CompCluster.h" @@ -33,9 +34,16 @@ #include "ITStracking/IOUtils.h" #include "DetectorsCommonDataFormats/NameConf.h" #include "DataFormatsParameters/GRPObject.h" +#include "Headers/DataHeader.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonDataFormat/FlatHisto2D.h" + +// RSTODO to remove once the framework will start propagating the header.firstTForbit +#include "DetectorsRaw/HBFUtils.h" using namespace o2::framework; -using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; +using MCLabelsCl = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; +using MCLabelsTr = gsl::span<const o2::MCCompLabel>; namespace o2 { @@ -58,6 +66,8 @@ void TPCITSMatchingDPL::init(InitContext& ic) mMatching.setITSROFrameLengthInBC(alpParams.roFrameLengthInBC); // ITS ROFrame duration in \mus } mMatching.setMCTruthOn(mUseMC); + mMatching.setUseFT0(mUseFT0); + mMatching.setVDriftCalib(mCalibMode); // std::string dictPath = ic.options().get<std::string>("its-dictionary-path"); std::string dictFile = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, dictPath, ".bin"); @@ -78,6 +88,15 @@ void TPCITSMatchingDPL::init(InitContext& ic) } else { LOG(INFO) << "Material LUT " << matLUTFile << " file is absent, only TGeo can be used"; } + + int dbgFlags = ic.options().get<int>("debug-tree-flags"); + mMatching.setDebugFlag(dbgFlags); + + // set bunch filling. Eventually, this should come from CCDB + const auto* digctx = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto& bcfill = digctx->getBunchFilling(); + mMatching.setBunchFilling(bcfill); + mMatching.init(); // } @@ -97,7 +116,7 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) //---------------------------->> TPC Clusters loading >>------------------------------------------ int operation = 0; uint64_t activeSectors = 0; - std::bitset<o2::tpc::Constants::MAXSECTOR> validSectors = 0; + std::bitset<o2::tpc::constants::MAXSECTOR> validSectors = 0; std::map<int, DataRef> datarefs; std::vector<InputSpec> filter = { {"check", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}, Lifetime::Timeframe}, @@ -110,7 +129,7 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) throw std::runtime_error("sector header missing on header stack"); } int sector = sectorHeader->sector(); - std::bitset<o2::tpc::Constants::MAXSECTOR> sectorMask(sectorHeader->sectorBits); + std::bitset<o2::tpc::constants::MAXSECTOR> sectorMask(sectorHeader->sectorBits); LOG(INFO) << "Reading TPC cluster data, sector mask is " << sectorMask; if ((validSectors & sectorMask).any()) { // have already data for this sector, this should not happen in the current @@ -129,7 +148,7 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) << std::endl // << " input status: " << validSectors // << std::endl // - << " active sectors: " << std::bitset<o2::tpc::Constants::MAXSECTOR>(activeSectors); + << " active sectors: " << std::bitset<o2::tpc::constants::MAXSECTOR>(activeSectors); }; if (activeSectors == 0 || (activeSectors & validSectors.to_ulong()) != activeSectors) { @@ -188,28 +207,24 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) o2::tpc::ClusterNativeAccess clusterIndex; std::unique_ptr<o2::tpc::ClusterNative[]> clusterBuffer; memset(&clusterIndex, 0, sizeof(clusterIndex)); - o2::tpc::ClusterNativeHelper::Reader::fillIndex(clusterIndex, clusterBuffer, clustersTPC); + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer dummyMCOutput; + std::vector<o2::tpc::ClusterNativeHelper::ConstMCLabelContainerView> dummyMCInput; + o2::tpc::ClusterNativeHelper::Reader::fillIndex(clusterIndex, clusterBuffer, dummyMCOutput, clustersTPC, dummyMCInput); //----------------------------<< TPC Clusters loading <<------------------------------------------ // - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* lblITSPtr = nullptr; - std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>> lblITS; + MCLabelsTr lblITS; + MCLabelsTr lblTPC; - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* lblClusITSPtr = nullptr; - std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>> lblClusITS; - - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* lblTPCPtr = nullptr; - std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>> lblTPC; + const MCLabelsCl* lblClusITSPtr = nullptr; + std::unique_ptr<const MCLabelsCl> lblClusITS; if (mUseMC) { - lblITS = pc.inputs().get<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("trackITSMCTR"); - lblITSPtr = lblITS.get(); + lblITS = pc.inputs().get<gsl::span<o2::MCCompLabel>>("trackITSMCTR"); + lblTPC = pc.inputs().get<gsl::span<o2::MCCompLabel>>("trackTPCMCTR"); lblClusITS = pc.inputs().get<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("clusITSMCTR"); lblClusITSPtr = lblClusITS.get(); - - lblTPC = pc.inputs().get<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("trackTPCMCTR"); - lblTPCPtr = lblTPC.get(); } // // create ITS clusters as spacepoints in tracking frame @@ -229,18 +244,26 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) mMatching.setTPCClustersInp(&clusterIndex); if (mUseMC) { - mMatching.setITSTrkLabelsInp(lblITSPtr); + mMatching.setITSTrkLabelsInp(lblITS); + mMatching.setTPCTrkLabelsInp(lblTPC); mMatching.setITSClsLabelsInp(lblClusITSPtr); - mMatching.setTPCTrkLabelsInp(lblTPCPtr); } - if (o2::globaltracking::MatchITSTPCParams::Instance().runAfterBurner) { + if (mUseFT0) { // 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 fitInfo = pc.inputs().get<gsl::span<o2::ft0::RecPoints>>("fitInfo"); mMatching.setFITInfoInp(fitInfo); } + const auto* dh = o2::header::get<o2::header::DataHeader*>(pc.inputs().get("trackITSROF").header); + mMatching.setStartIR({0, dh->firstTForbit}); + + //RSTODO: below is a hack, to remove once the framework will start propagating the header.firstTForbit + if (tracksITSROF.size()) { + mMatching.setStartIR(o2::raw::HBFUtils::Instance().getFirstIRofTF(tracksITSROF[0].getBCData())); + } + mMatching.run(); pc.outputs().snapshot(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}, mMatching.getMatchedTracks()); @@ -248,6 +271,13 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"GLO", "TPCITS_ITSMC", 0, Lifetime::Timeframe}, mMatching.getMatchedITSLabels()); pc.outputs().snapshot(Output{"GLO", "TPCITS_TPCMC", 0, Lifetime::Timeframe}, mMatching.getMatchedTPCLabels()); } + + if (mCalibMode) { + auto* hdtgl = mMatching.getHistoDTgl(); + pc.outputs().snapshot(Output{"GLO", "TPCITS_VDHDTGL", 0, Lifetime::Timeframe}, (*hdtgl).getBase()); + hdtgl->clear(); + } + mTimer.Stop(); } @@ -257,7 +287,7 @@ void TPCITSMatchingDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTPCITSMatchingSpec(bool useMC, const std::vector<int>& tpcClusLanes) +DataProcessorSpec getTPCITSMatchingSpec(bool useFT0, bool calib, bool useMC, const std::vector<int>& tpcClusLanes) { std::vector<InputSpec> inputs; @@ -273,12 +303,16 @@ DataProcessorSpec getTPCITSMatchingSpec(bool useMC, const std::vector<int>& tpcC inputs.emplace_back("clusTPC", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}, Lifetime::Timeframe); - if (o2::globaltracking::MatchITSTPCParams::Instance().runAfterBurner) { + if (useFT0) { inputs.emplace_back("fitInfo", "FT0", "RECPOINTS", 0, Lifetime::Timeframe); } outputs.emplace_back("GLO", "TPCITS", 0, Lifetime::Timeframe); + if (calib) { + outputs.emplace_back("GLO", "TPCITS_VDHDTGL", 0, Lifetime::Timeframe); + } + if (useMC) { inputs.emplace_back("trackITSMCTR", "ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); inputs.emplace_back("trackTPCMCTR", "TPC", "TRACKSMCLBL", 0, Lifetime::Timeframe); @@ -292,10 +326,11 @@ DataProcessorSpec getTPCITSMatchingSpec(bool useMC, const std::vector<int>& tpcC "itstpc-track-matcher", inputs, outputs, - AlgorithmSpec{adaptFromTask<TPCITSMatchingDPL>(useMC)}, + AlgorithmSpec{adaptFromTask<TPCITSMatchingDPL>(useFT0, calib, useMC)}, Options{ {"its-dictionary-path", VariantType::String, "", {"Path of the cluster-topology dictionary file"}}, - {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}}}; + {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, + {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx new file mode 100644 index 0000000000000..38e5401753ddc --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx @@ -0,0 +1,109 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VertexTrackMatcherSpec.cxx +/// @brief Specs for vertex track association device +/// @author ruben.shahoyan@cern.ch + +#include "GlobalTrackingWorkflow/VertexTrackMatcherSpec.h" +#include "DataFormatsParameters/GRPObject.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace vertexing +{ + +void VertexTrackMatcherSpec::init(InitContext& ic) +{ + //-------- init geometry and field --------// + mTimer.Stop(); + mTimer.Reset(); + + mMatcher.init(); +} + +void VertexTrackMatcherSpec::run(ProcessingContext& pc) +{ + double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); + mTimer.Start(false); + + // eventually, this should be set per TF from CCDB? + if (mMatcher.getITSROFrameLengthInBC() == 0) { + std::unique_ptr<o2::parameters::GRPObject> grp{o2::parameters::GRPObject::loadFrom(o2::base::NameConf::getGRPFileName())}; + const auto& alpParams = o2::itsmft::DPLAlpideParam<o2::detectors::DetID::ITS>::Instance(); + if (grp->isDetContinuousReadOut(o2::detectors::DetID::ITS)) { + mMatcher.setITSROFrameLengthInBC(alpParams.roFrameLengthInBC); + } else { + mMatcher.setITSROFrameLengthInBC(alpParams.roFrameLengthTrig / o2::constants::lhc::LHCOrbitNS); + } + } + + // RS FIXME this will not have effect until the 1st orbit is propagated, until that will work only for TF starting at orbit 0 + const auto* dh = o2::header::get<o2::header::DataHeader*>(pc.inputs().get("vertices").header); + mMatcher.setStartIR({0, dh->firstTForbit}); + + const auto tracksITSTPC = pc.inputs().get<gsl::span<o2::dataformats::TrackTPCITS>>("tpcits"); + const auto tracksTPC = pc.inputs().get<gsl::span<o2::tpc::TrackTPC>>("tpc"); + const auto tracksITS = pc.inputs().get<gsl::span<o2::its::TrackITS>>("its"); + const auto tracksITSROF = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>("itsROF"); + const auto vertices = pc.inputs().get<gsl::span<o2::dataformats::PrimaryVertex>>("vertices"); + const auto vtxTracks = pc.inputs().get<gsl::span<o2::dataformats::VtxTrackIndex>>("vtxTracks"); + const auto vtxTrackRefs = pc.inputs().get<gsl::span<o2::dataformats::VtxTrackRef>>("vtxTrackRefs"); + + std::vector<o2::dataformats::VtxTrackIndex> trackIndex; + std::vector<o2::dataformats::VtxTrackRef> vtxRefs; + + mMatcher.process(vertices, vtxTracks, vtxTrackRefs, tracksITSTPC, tracksITS, tracksITSROF, tracksTPC, trackIndex, vtxRefs); + + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}, trackIndex); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe}, vtxRefs); + + mTimer.Stop(); + LOG(INFO) << "Made " << trackIndex.size() << " track associationgs for " << vertices.size() << " vertices, timing: CPU: " + << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0 << " s"; +} + +void VertexTrackMatcherSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "Primary vertex - track matching total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getVertexTrackMatcherSpec() +{ + std::vector<InputSpec> inputs; + std::vector<OutputSpec> outputs; + + inputs.emplace_back("tpcits", "GLO", "TPCITS", 0, Lifetime::Timeframe); + inputs.emplace_back("its", "ITS", "TRACKS", 0, Lifetime::Timeframe); + inputs.emplace_back("itsROF", "ITS", "ITSTrackROF", 0, Lifetime::Timeframe); + inputs.emplace_back("tpc", "TPC", "TRACKS", 0, Lifetime::Timeframe); + + inputs.emplace_back("vertices", "GLO", "PVTX", 0, Lifetime::Timeframe); + inputs.emplace_back("vtxTracks", "GLO", "PVTX_CONTID", 0, Lifetime::Timeframe); + inputs.emplace_back("vtxTrackRefs", "GLO", "PVTX_CONTIDREFS", 0, Lifetime::Timeframe); + + outputs.emplace_back("GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "pvertex-track-matching", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<VertexTrackMatcherSpec>()}, + Options{}}; +} + +} // namespace vertexing +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/primary-vertex-reader-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/primary-vertex-reader-workflow.cxx new file mode 100644 index 0000000000000..1342cdb22a4b0 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/primary-vertex-reader-workflow.cxx @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> 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) +{ + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get<std::string>("configKeyValues")); + + auto useMC = !configcontext.options().get<bool>("disable-mc"); + + WorkflowSpec specs; + specs.emplace_back(o2::vertexing::getPrimaryVertexReaderSpec(useMC)); + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx new file mode 100644 index 0000000000000..67e6ae1e43f2c --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "GlobalTrackingWorkflow/PrimaryVertexingSpec.h" +#include "GlobalTrackingWorkflow/PrimaryVertexWriterSpec.h" +#include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" +#include "GlobalTrackingWorkflow/VertexTrackMatcherSpec.h" +#include "ITSWorkflow/TrackReaderSpec.h" +#include "TPCWorkflow/TrackReaderSpec.h" +#include "FT0Workflow/RecPointReaderSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, + {"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"}}, + {"validate-with-ft0", o2::framework::VariantType::Bool, false, {"use FT0 time for vertex validation"}}, + {"disable-vertex-track-matching", o2::framework::VariantType::Bool, false, {"disable matching of vertex to non-contributor tracks"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + + 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<std::string>("configKeyValues")); + // write the configuration used for the workflow + o2::conf::ConfigurableParam::writeINI("o2primary-vertexing-workflow_configuration.ini"); + + auto useMC = !configcontext.options().get<bool>("disable-mc"); + auto disableRootInp = configcontext.options().get<bool>("disable-root-input"); + auto disableRootOut = configcontext.options().get<bool>("disable-root-output"); + auto validateWithFT0 = configcontext.options().get<bool>("validate-with-ft0"); + auto disableMatching = configcontext.options().get<bool>("disable-vertex-track-matching"); + + WorkflowSpec specs; + if (!disableRootInp) { + specs.emplace_back(o2::globaltracking::getTrackTPCITSReaderSpec(useMC)); + if (validateWithFT0) { + specs.emplace_back(o2::ft0::getRecPointReaderSpec(false)); + } + } + specs.emplace_back(o2::vertexing::getPrimaryVertexingSpec(validateWithFT0, useMC)); + + if (!disableMatching) { + if (!disableRootInp) { + specs.emplace_back(o2::its::getITSTrackReaderSpec(false)); + specs.emplace_back(o2::tpc::getTPCTrackReaderSpec(false)); + } + specs.emplace_back(o2::vertexing::getVertexTrackMatcherSpec()); + } + + if (!disableRootOut) { + specs.emplace_back(o2::vertexing::getPrimaryVertexWriterSpec(disableMatching, useMC)); + } + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertex-reader-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertex-reader-workflow.cxx new file mode 100644 index 0000000000000..433d095889277 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertex-reader-workflow.cxx @@ -0,0 +1,42 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "GlobalTrackingWorkflow/SecondaryVertexReaderSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> options{ + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + + 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<std::string>("configKeyValues")); + + WorkflowSpec specs; + specs.emplace_back(o2::vertexing::getSecondaryVertexReaderSpec()); + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx new file mode 100644 index 0000000000000..a81eaf9dd89c3 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -0,0 +1,64 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "GlobalTrackingWorkflow/SecondaryVertexingSpec.h" +#include "GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h" +#include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" +#include "GlobalTrackingWorkflow/PrimaryVertexReaderSpec.h" +#include "ITSWorkflow/TrackReaderSpec.h" +#include "TPCWorkflow/TrackReaderSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<o2::framework::ConfigParamSpec> 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"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + + 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<std::string>("configKeyValues")); + // write the configuration used for the workflow + o2::conf::ConfigurableParam::writeINI("o2secondary-vertexing-workflow_configuration.ini"); + + auto disableRootInp = configcontext.options().get<bool>("disable-root-input"); + auto disableRootOut = configcontext.options().get<bool>("disable-root-output"); + + WorkflowSpec specs; + if (!disableRootInp) { + specs.emplace_back(o2::vertexing::getPrimaryVertexReaderSpec(false)); + specs.emplace_back(o2::globaltracking::getTrackTPCITSReaderSpec(false)); + specs.emplace_back(o2::its::getITSTrackReaderSpec(false)); + specs.emplace_back(o2::tpc::getTPCTrackReaderSpec(false)); + } + specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec()); + if (!disableRootOut) { + specs.emplace_back(o2::vertexing::getSecondaryVertexWriterSpec()); + } + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 9562be20529df..0a1025134841b 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -22,9 +22,11 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) { // option allowing to set parameters std::vector<o2::framework::ConfigParamSpec> options{ + {"use-ft0", o2::framework::VariantType::Bool, false, {"use FT0 in matching"}}, {"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"}}, + {"produce-calibration-data", o2::framework::VariantType::Bool, false, {"produce output for TPC vdrift calibration"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; std::swap(workflowOptions, options); @@ -52,8 +54,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2matchtpcits-workflow_configuration.ini"); + auto useFT0 = configcontext.options().get<bool>("use-ft0"); auto useMC = !configcontext.options().get<bool>("disable-mc"); auto disableRootInp = configcontext.options().get<bool>("disable-root-input"); auto disableRootOut = configcontext.options().get<bool>("disable-root-output"); - return std::move(o2::globaltracking::getMatchTPCITSWorkflow(useMC, disableRootInp, disableRootOut)); + auto calib = configcontext.options().get<bool>("produce-calibration-data"); + return std::move(o2::globaltracking::getMatchTPCITSWorkflow(useFT0, useMC, disableRootInp, disableRootOut, calib)); } diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/tofworkflow/CMakeLists.txt index 0e2aed78c0327..22e5e927b488d 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/CMakeLists.txt @@ -10,14 +10,32 @@ o2_add_library(TOFWorkflow SOURCES src/RecoWorkflowSpec.cxx + src/RecoWorkflowWithTPCSpec.cxx src/TOFMatchedWriterSpec.cxx src/TOFCalibWriterSpec.cxx src/TOFMatchedReaderSpec.cxx + src/CalibInfoReaderSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::TOFBase O2::DataFormatsTOF O2::TOFReconstruction O2::GlobalTracking O2::GlobalTrackingWorkflow O2::TOFWorkflowUtils - O2::TOFCalibration O2::DataFormatsFT0 O2::FT0Reconstruction O2::FITWorkflow) + O2::TOFCalibration O2::DataFormatsFT0 O2::FT0Reconstruction O2::FT0Workflow + O2::TPCWorkflow) o2_add_executable(reco-workflow COMPONENT_NAME tof SOURCES src/tof-reco-workflow.cxx PUBLIC_LINK_LIBRARIES O2::TOFWorkflow) + +o2_add_executable(matcher-global + COMPONENT_NAME tof + SOURCES src/tof-matcher-global.cxx + PUBLIC_LINK_LIBRARIES O2::TOFWorkflow) + +o2_add_executable(matcher-tpc + COMPONENT_NAME tof + SOURCES src/tof-matcher-tpc.cxx + PUBLIC_LINK_LIBRARIES O2::TOFWorkflow) + +o2_add_executable(calib-reader + COMPONENT_NAME tof + SOURCES src/tof-calibinfo-reader.cxx + PUBLIC_LINK_LIBRARIES O2::TOFWorkflow) diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/CalibInfoReaderSpec.h b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/CalibInfoReaderSpec.h new file mode 100644 index 0000000000000..09494a2191f23 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/CalibInfoReaderSpec.h @@ -0,0 +1,58 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CalibInfoReaderSpec.h + +#ifndef O2_TOF_CALIBINFOREADER +#define O2_TOF_CALIBINFOREADER + +#include "TFile.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DataFormatsTOF/CalibInfoTOF.h" + +class TTree; + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +class CalibInfoReader : public Task +{ + public: + CalibInfoReader(int instance, int ninstances, const char* filename) : mInstance(instance), mNinstances(ninstances), mFileName(filename) {} + ~CalibInfoReader() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + + private: + int mState = 0; + int mInstance; + int mNinstances; + const char* mFileName = nullptr; + FILE* mFile = nullptr; + TTree* mTree = nullptr; + int mCurrentEntry = 0; + int mGlobalEntry = 0; + std::vector<o2::dataformats::CalibInfoTOF> mVect, *mPvect = &mVect; +}; + +/// create a processor spec +/// read simulated TOF digits from a root file +framework::DataProcessorSpec getCalibInfoReaderSpec(int instance, int ninstances, const char* filename); + +} // namespace tof +} // namespace o2 + +#endif /* O2_TOF_CALIBINFOREADER */ diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/RecoWorkflowWithTPCSpec.h b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/RecoWorkflowWithTPCSpec.h new file mode 100644 index 0000000000000..9f5d5b49dc50b --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/RecoWorkflowWithTPCSpec.h @@ -0,0 +1,27 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TOF_RECOWORKFLOWWITHTPC_H_ +#define TOF_RECOWORKFLOWWITHTPC_H_ + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/MatchInfoTOF.h" + +namespace o2 +{ +namespace tof +{ + +o2::framework::DataProcessorSpec getTOFRecoWorkflowWithTPCSpec(bool useMC, bool useFIT); + +} // end namespace tof +} // end namespace o2 + +#endif /* TOF_RECOWORKFLOWWITHTPC_H_ */ diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFCalibWriterSpec.h b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFCalibWriterSpec.h index 9a804b031da48..82d696bcf0383 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFCalibWriterSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFCalibWriterSpec.h @@ -24,7 +24,7 @@ namespace tof /// create a processor spec /// write TOF calbi info in a root file -o2::framework::DataProcessorSpec getTOFCalibWriterSpec(); +o2::framework::DataProcessorSpec getTOFCalibWriterSpec(const char* outdef = "o2calib_tof.root"); } // namespace tof } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFMatchedWriterSpec.h b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFMatchedWriterSpec.h index 9c6fe7995ba9f..bb9899addd94c 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFMatchedWriterSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/include/TOFWorkflow/TOFMatchedWriterSpec.h @@ -24,7 +24,7 @@ namespace tof /// create a processor spec /// write TOF matching info in a root file -o2::framework::DataProcessorSpec getTOFMatchedWriterSpec(bool useMC); +o2::framework::DataProcessorSpec getTOFMatchedWriterSpec(bool useMC, const char* outdef = "o2match_tof.root"); } // namespace tof } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/CalibInfoReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/CalibInfoReaderSpec.cxx new file mode 100644 index 0000000000000..d78e95a392393 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/CalibInfoReaderSpec.cxx @@ -0,0 +1,94 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> +#include <unistd.h> + +#include "TChain.h" +#include "TTree.h" + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "TOFWorkflow/CalibInfoReaderSpec.h" + +using namespace o2::framework; +using namespace o2::tof; + +namespace o2 +{ +namespace tof +{ + +void CalibInfoReader::init(InitContext& ic) +{ + LOG(INFO) << "Init CalibInfo reader!"; + mFile = fopen(mFileName, "r"); + if (!mFile) { + LOG(ERROR) << "Cannot open the " << mFileName << " file !"; + mState = 0; + return; + } + mState = 1; +} + +void CalibInfoReader::run(ProcessingContext& pc) +{ + if (mState != 1) { + return; + } + + char filename[100]; + + if ((mTree && mCurrentEntry < mTree->GetEntries()) || fscanf(mFile, "%s", filename) == 1) { + if (!mTree || mCurrentEntry >= mTree->GetEntries()) { + TFile* fin = TFile::Open(filename); + mTree = (TTree*)fin->Get("calibTOF"); + mCurrentEntry = 0; + mTree->SetBranchAddress("TOFCalibInfo", &mPvect); + } + if ((mGlobalEntry % mNinstances) == mInstance) { + mTree->GetEvent(mCurrentEntry); + LOG(INFO) << "Send " << mVect.size() << " calib infos"; + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe}, mVect); + usleep(10000); + } + mGlobalEntry++; + mCurrentEntry++; + } else { + mState = 2; + pc.services().get<ControlService>().endOfStream(); + } + return; +} + +DataProcessorSpec getCalibInfoReaderSpec(int instance, int ninstances, const char* filename) +{ + std::vector<OutputSpec> outputs; + outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe); + + const char* nameSpec; + if (ninstances == 1) { + nameSpec = "tof-calibinfo-reader"; + } else { + nameSpec = Form("tof-calibinfo-reader-%d", instance); + } + + return DataProcessorSpec{ + nameSpec, + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<CalibInfoReader>(instance, ninstances, filename)}, + Options{/* for the moment no options */}}; +} +} // namespace tof +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx index a358a8971bbae..c7def5fe1cbfe 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx @@ -115,7 +115,7 @@ class TOFDPLRecoWorkflowTask pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHTPCINFOSMC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTPCLabelsVector()); pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHITSINFOSMC", 0, Lifetime::Timeframe}, mMatcher.getMatchedITSLabelsVector()); } - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBINFOS", 0, Lifetime::Timeframe}, mMatcher.getCalibVector()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe}, mMatcher.getCalibVector()); mTimer.Stop(); } @@ -152,7 +152,7 @@ o2::framework::DataProcessorSpec getTOFRecoWorkflowSpec(bool useMC, bool useFIT) outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHTPCINFOSMC", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHITSINFOSMC", 0, Lifetime::Timeframe); } - outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBINFOS", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe); return DataProcessorSpec{ "TOFRecoWorkflow", diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowWithTPCSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowWithTPCSpec.cxx new file mode 100644 index 0000000000000..aefc81024073e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowWithTPCSpec.cxx @@ -0,0 +1,166 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/RecoWorkflowWithTPCSpec.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 <gsl/span> +#include "TStopwatch.h" + +// from FIT +#include "DataFormatsFT0/RecPoints.h" + +#include <memory> // for make_shared, make_unique, unique_ptr +#include <vector> + +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 TOFDPLRecoWorkflowWithTPCTask +{ + using evIdx = o2::dataformats::EvIndex<int, int>; + using MatchOutputType = std::vector<o2::dataformats::MatchInfoTOF>; + + bool mUseMC = true; + bool mUseFIT = false; + + public: + explicit TOFDPLRecoWorkflowWithTPCTask(bool useMC, bool useFIT) : mUseMC(useMC), mUseFIT(useFIT) {} + + void init(framework::InitContext& ic) + { + // nothing special to be set up + o2::base::GeometryManager::loadGeometry(); + o2::base::Propagator::initFieldFromGRP("o2sim_grp.root"); + mTimer.Stop(); + mTimer.Reset(); + } + + void run(framework::ProcessingContext& pc) + { + mTimer.Start(false); + //>>>---------- attach input data --------------->>> + const auto clustersRO = pc.inputs().get<gsl::span<o2::tof::Cluster>>("tofcluster"); + const auto tracksRO = pc.inputs().get<gsl::span<o2::tpc::TrackTPC>>("tracks"); + + 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<gsl::span<o2::ft0::RecPoints>>("fitrecpoints")); + mMatcher.setFITRecPoints(recPoints); + LOG(INFO) << "TOF Reco WorkflowWithTPC pulled " << recPoints.size() << " FIT RecPoints"; + } + + //-------- init geometry and field --------// + // std::string path = "./"; + // std::string inputGeom = "O2geometry.root"; + // std::string inputGRP = "o2sim_grp.root"; + + // o2::base::GeometryManager::loadGeometry(path); + // o2::base::Propagator::initFieldFromGRP(path + inputGRP); + + // call actual matching info routine + //#ifdef _ALLOW_DEBUG_TREES_ + // mMatcher.setDebugTreeFileName(path + mMatcher.getDebugTreeFileName()); + // mMatcher.setDebugFlag(o2::globaltracking::MatchTOF::MatchTreeAll); + //#endif + + // 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 *) + + o2::dataformats::MCTruthContainer<o2::MCCompLabel> toflab; + gsl::span<const o2::MCCompLabel> tpclab; + printf("get MC\n"); + if (mUseMC) { + const auto toflabel = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("tofclusterlabel"); + tpclab = pc.inputs().get<gsl::span<o2::MCCompLabel>>("tpctracklabel"); + toflab = std::move(*toflabel); + } + printf("GOT!\n"); + + mMatcher.run(tracksRO, clustersRO, toflab, tpclab); + + printf("run done\n"); + // 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, "MATCHINFOS", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector()); + if (mUseMC) { + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHTOFINFOSMC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHTPCINFOSMC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTPCLabelsVector()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHITSINFOSMC", 0, Lifetime::Timeframe}, mMatcher.getMatchedITSLabelsVector()); + } + 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); + } + + private: + o2::globaltracking::MatchTOF mMatcher; ///< Cluster finder + TStopwatch mTimer; +}; + +o2::framework::DataProcessorSpec getTOFRecoWorkflowWithTPCSpec(bool useMC, bool useFIT) +{ + std::vector<InputSpec> inputs; + std::vector<OutputSpec> outputs; + inputs.emplace_back("tofcluster", o2::header::gDataOriginTOF, "CLUSTERS", 0, Lifetime::Timeframe); + inputs.emplace_back("tracks", o2::header::gDataOriginTPC, "TRACKS", 0, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("tofclusterlabel", o2::header::gDataOriginTOF, "CLUSTERSMCTR", 0, Lifetime::Timeframe); + inputs.emplace_back("tpctracklabel", o2::header::gDataOriginTPC, "TRACKSMCLBL", 0, Lifetime::Timeframe); + } + + if (useFIT) { + inputs.emplace_back("fitrecpoints", o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); + } + + outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHINFOS", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHTOFINFOSMC", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHTPCINFOSMC", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHITSINFOSMC", 0, Lifetime::Timeframe); + } + outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "TOFRecoWorkflowWithTPC", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<TOFDPLRecoWorkflowWithTPCTask>(useMC, useFIT)}, + Options{/* for the moment no options */}}; +} + +} // end namespace tof +} // end namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFCalibWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFCalibWriterSpec.cxx index c3170fdc67db1..e3d0ab0690259 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFCalibWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFCalibWriterSpec.cxx @@ -34,16 +34,16 @@ namespace tof template <typename T> using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; using CalibInfosType = std::vector<o2::dataformats::CalibInfoTOF>; -DataProcessorSpec getTOFCalibWriterSpec() +DataProcessorSpec getTOFCalibWriterSpec(const char* outdef) { // A spectator for logging auto logger = [](CalibInfosType const& indata) { LOG(INFO) << "RECEIVED MATCHED SIZE " << indata.size(); }; return MakeRootTreeWriterSpec("TOFCalibWriter", - "o2calib_tof.root", + outdef, "calibTOF", - BranchDefinition<CalibInfosType>{InputSpec{"input", o2::header::gDataOriginTOF, "CALIBINFOS", 0}, + BranchDefinition<CalibInfosType>{InputSpec{"input", o2::header::gDataOriginTOF, "CALIBDATA", 0}, "TOFCalibInfo", "calibinfo-branch-name", 1, diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFMatchedWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFMatchedWriterSpec.cxx index be790e70d425f..6e8ad5dccd840 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFMatchedWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/TOFMatchedWriterSpec.cxx @@ -35,7 +35,7 @@ using OutputType = std::vector<o2::dataformats::MatchInfoTOF>; using LabelsType = std::vector<o2::MCCompLabel>; using namespace o2::header; -DataProcessorSpec getTOFMatchedWriterSpec(bool useMC) +DataProcessorSpec getTOFMatchedWriterSpec(bool useMC, const char* outdef) { // spectators for logging auto loggerMatched = [](OutputType const& indata) { @@ -54,7 +54,7 @@ DataProcessorSpec getTOFMatchedWriterSpec(bool useMC) // RS why do we need to repead ITS/TPC labels ? // They can be extracted from TPC-ITS matches return MakeRootTreeWriterSpec("TOFMatchedWriter", - "o2match_tof.root", + outdef, "matchTOF", BranchDefinition<OutputType>{InputSpec{"tofmatching", gDataOriginTOF, "MATCHINFOS", 0}, "TOFMatchInfo", diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx new file mode 100644 index 0000000000000..ca362c48ba2b0 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx @@ -0,0 +1,83 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tof-reco-workflow.cxx +/// @author Francesco Noferini +/// @since 2019-05-22 +/// @brief Basic DPL workflow for TOF reconstruction starting from digits + +#include "DetectorsBase/Propagator.h" +#include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" +#include "TOFWorkflow/CalibInfoReaderSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "TOFWorkflow/RecoWorkflowSpec.h" +#include "Algorithm/RangeTokenizer.h" +#include "FairLogger.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" + +#include <string> +#include <stdexcept> +#include <unordered_map> + +// add workflow options, note that customization needs to be declared before +// including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{"collection-infile", o2::framework::VariantType::String, "list-calibfile", {"Name of the collection input file"}}); + workflowOptions.push_back(ConfigParamSpec{"ninstances", o2::framework::VariantType::Int, 1, {"Number of reader instances"}}); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings ..."}}); +} + +#include "Framework/runDataProcessing.h" // the main driver + +using namespace o2::framework; + +/// The workflow executable for the stand alone TOF reconstruction workflow +/// The basic workflow for TOF reconstruction is defined in RecoWorkflow.cxx +/// and contains the following default processors +/// - digit reader +/// - clusterer +/// - cluster raw decoder +/// - track-TOF matcher +/// +/// The default workflow can be customized by specifying input and output types +/// e.g. digits, raw, clusters. +/// +/// MC info is processed by default, disabled by using command line option `--disable-mc` +/// +/// This function hooks up the the workflow specifications into the DPL driver. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec specs; + + if (!cfgc.helpOnCommandLine()) { + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + } + + int ninstances = cfgc.options().get<int>("ninstances"); + auto listname = cfgc.options().get<std::string>("collection-infile"); + + char* stringTBP = new char[listname.size()]; + sprintf(stringTBP, "%s", listname.c_str()); + + // the lane configuration defines the subspecification ids to be distributed among the lanes. + // auto tofSectors = o2::RangeTokenizer::tokenize<int>(cfgc.options().get<std::string>("tof-sectors")); + // std::vector<int> laneConfiguration = tofSectors; + + for (int i = 0; i < ninstances; i++) { + specs.emplace_back(o2::tof::getCalibInfoReaderSpec(i, ninstances, stringTBP)); + } + + LOG(INFO) << "Number of active devices = " << specs.size(); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-matcher-global.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-matcher-global.cxx new file mode 100644 index 0000000000000..3d965c015134e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-matcher-global.cxx @@ -0,0 +1,167 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tof-reco-workflow.cxx +/// @author Francesco Noferini +/// @since 2019-05-22 +/// @brief Basic DPL workflow for TOF reconstruction starting from digits + +#include "DetectorsBase/Propagator.h" +#include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" +#include "TOFWorkflowUtils/ClusterReaderSpec.h" +#include "TOFWorkflow/TOFMatchedWriterSpec.h" +#include "TOFWorkflow/TOFCalibWriterSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "TOFWorkflow/RecoWorkflowSpec.h" +#include "Algorithm/RangeTokenizer.h" +#include "FairLogger.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" + +// GRP +#include "DataFormatsParameters/GRPObject.h" + +// FIT +#include "FT0Workflow/RecPointReaderSpec.h" + +#include <string> +#include <stdexcept> +#include <unordered_map> + +// add workflow options, note that customization needs to be declared before +// including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{"input-type", o2::framework::VariantType::String, "clusters,tracks", {"clusters, tracks, fit"}}); + workflowOptions.push_back(ConfigParamSpec{"output-type", o2::framework::VariantType::String, "matching-info,calib-info", {"matching-info, calib-info"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable sending of MC information, TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-sectors", o2::framework::VariantType::String, "0-17", {"TOF sector range, e.g. 5-7,8,9 ,TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-lanes", o2::framework::VariantType::Int, 1, {"number of parallel lanes up to the matcher, TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"use-ccdb", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects"}}); + workflowOptions.push_back(ConfigParamSpec{"use-fit", o2::framework::VariantType::Bool, false, {"enable access to fit info for calibration"}}); + workflowOptions.push_back(ConfigParamSpec{"input-desc", o2::framework::VariantType::String, "CRAWDATA", {"Input specs description string"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings ..."}}); +} + +#include "Framework/runDataProcessing.h" // the main driver + +using namespace o2::framework; + +/// The workflow executable for the stand alone TOF reconstruction workflow +/// The basic workflow for TOF reconstruction is defined in RecoWorkflow.cxx +/// and contains the following default processors +/// - digit reader +/// - clusterer +/// - cluster raw decoder +/// - track-TOF matcher +/// +/// The default workflow can be customized by specifying input and output types +/// e.g. digits, raw, clusters. +/// +/// MC info is processed by default, disabled by using command line option `--disable-mc` +/// +/// This function hooks up the the workflow specifications into the DPL driver. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec specs; + + if (!cfgc.helpOnCommandLine()) { + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + o2::base::Propagator::initFieldFromGRP(inputGRP); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + if (!grp) { + LOG(ERROR) << "This workflow needs a valid GRP file to start"; + return specs; + } + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + // o2::conf::ConfigurableParam::writeINI("o2tofrecoflow_configuration.ini"); + } + // the lane configuration defines the subspecification ids to be distributed among the lanes. + // auto tofSectors = o2::RangeTokenizer::tokenize<int>(cfgc.options().get<std::string>("tof-sectors")); + // std::vector<int> laneConfiguration = tofSectors; + auto nLanes = cfgc.options().get<int>("tof-lanes"); + auto inputType = cfgc.options().get<std::string>("input-type"); + auto outputType = cfgc.options().get<std::string>("output-type"); + + bool writematching = 0; + bool writecalib = 0; + + if (outputType.rfind("matching-info") < outputType.size()) { + writematching = 1; + } + if (outputType.rfind("calib-info") < outputType.size()) { + writecalib = 1; + } + + bool clusterinput = 0; + bool trackinput = 0; + bool fitinput = 0; + + if (inputType.rfind("clusters") < inputType.size()) { + clusterinput = 1; + } + if (inputType.rfind("tracks") < inputType.size()) { + trackinput = 1; + } + auto useMC = !cfgc.options().get<bool>("disable-mc"); + auto useCCDB = cfgc.options().get<bool>("use-ccdb"); + auto useFIT = cfgc.options().get<bool>("use-fit"); + bool disableRootInput = cfgc.options().get<bool>("disable-root-input"); + bool disableRootOutput = cfgc.options().get<bool>("disable-root-output"); + + if (inputType.rfind("fit") < inputType.size()) { + fitinput = 1; + useFIT = 1; + } + + LOG(INFO) << "TOF RECO WORKFLOW configuration"; + LOG(INFO) << "TOF input = " << cfgc.options().get<std::string>("input-type"); + LOG(INFO) << "TOF output = " << cfgc.options().get<std::string>("output-type"); + LOG(INFO) << "TOF sectors = " << cfgc.options().get<std::string>("tof-sectors"); + LOG(INFO) << "TOF disable-mc = " << cfgc.options().get<std::string>("disable-mc"); + LOG(INFO) << "TOF lanes = " << cfgc.options().get<std::string>("tof-lanes"); + LOG(INFO) << "TOF use-ccdb = " << cfgc.options().get<std::string>("use-ccdb"); + LOG(INFO) << "TOF use-fit = " << cfgc.options().get<std::string>("use-fit"); + LOG(INFO) << "TOF disable-root-input = " << disableRootInput; + LOG(INFO) << "TOF disable-root-output = " << disableRootOutput; + + if (clusterinput) { + LOG(INFO) << "Insert TOF Cluster Reader"; + specs.emplace_back(o2::tof::getClusterReaderSpec(useMC)); + } + if (trackinput) { + LOG(INFO) << "Insert ITS-TPC Track Reader"; + specs.emplace_back(o2::globaltracking::getTrackTPCITSReaderSpec(useMC)); + } + + if (fitinput) { + LOG(INFO) << "Insert FIT RecPoint Reader"; + specs.emplace_back(o2::ft0::getRecPointReaderSpec(useMC)); + } + + LOG(INFO) << "Insert TOF Matching"; + specs.emplace_back(o2::tof::getTOFRecoWorkflowSpec(useMC, useFIT)); + + if (writematching && !disableRootOutput) { + LOG(INFO) << "Insert TOF Matched Info Writer"; + specs.emplace_back(o2::tof::getTOFMatchedWriterSpec(useMC)); + } + if (writecalib) { + LOG(INFO) << "Insert TOF Calib Info Writer"; + specs.emplace_back(o2::tof::getTOFCalibWriterSpec()); + } + + LOG(INFO) << "Number of active devices = " << specs.size(); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-matcher-tpc.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-matcher-tpc.cxx new file mode 100644 index 0000000000000..d2882f177a79f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-matcher-tpc.cxx @@ -0,0 +1,172 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tof-reco-workflow.cxx +/// @author Francesco Noferini +/// @since 2019-05-22 +/// @brief Basic DPL workflow for TOF reconstruction starting from digits + +#include "DetectorsBase/Propagator.h" +#include "TOFWorkflowUtils/ClusterReaderSpec.h" +#include "TOFWorkflow/TOFMatchedWriterSpec.h" +#include "TOFWorkflow/TOFCalibWriterSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "TOFWorkflow/RecoWorkflowWithTPCSpec.h" +#include "Algorithm/RangeTokenizer.h" +#include "FairLogger.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "TPCWorkflow/TrackReaderSpec.h" + +// GRP +#include "DataFormatsParameters/GRPObject.h" + +// FIT +#include "FT0Workflow/RecPointReaderSpec.h" + +#include <string> +#include <stdexcept> +#include <unordered_map> + +// add workflow options, note that customization needs to be declared before +// including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{"input-type", o2::framework::VariantType::String, "clusters,tracks", {"clusters, tracks, fit"}}); + workflowOptions.push_back(ConfigParamSpec{"output-type", o2::framework::VariantType::String, "matching-info", {"matching-info, calib-info"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable sending of MC information, TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-sectors", o2::framework::VariantType::String, "0-17", {"TOF sector range, e.g. 5-7,8,9 ,TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-lanes", o2::framework::VariantType::Int, 1, {"number of parallel lanes up to the matcher, TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"use-ccdb", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects"}}); + workflowOptions.push_back(ConfigParamSpec{"use-fit", o2::framework::VariantType::Bool, false, {"enable access to fit info for calibration"}}); + workflowOptions.push_back(ConfigParamSpec{"input-desc", o2::framework::VariantType::String, "CRAWDATA", {"Input specs description string"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings ..."}}); +} + +#include "Framework/runDataProcessing.h" // the main driver + +using namespace o2::framework; + +/// The workflow executable for the stand alone TOF reconstruction workflow +/// The basic workflow for TOF reconstruction is defined in RecoWorkflow.cxx +/// and contains the following default processors +/// - digit reader +/// - clusterer +/// - cluster raw decoder +/// - track-TOF matcher +/// +/// The default workflow can be customized by specifying input and output types +/// e.g. digits, raw, clusters. +/// +/// MC info is processed by default, disabled by using command line option `--disable-mc` +/// +/// This function hooks up the the workflow specifications into the DPL driver. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec specs; + + if (!cfgc.helpOnCommandLine()) { + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + o2::base::Propagator::initFieldFromGRP(inputGRP); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + if (!grp) { + LOG(ERROR) << "This workflow needs a valid GRP file to start"; + return specs; + } + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + // o2::conf::ConfigurableParam::writeINI("o2tofrecoflow_configuration.ini"); + } + // the lane configuration defines the subspecification ids to be distributed among the lanes. + // auto tofSectors = o2::RangeTokenizer::tokenize<int>(cfgc.options().get<std::string>("tof-sectors")); + // std::vector<int> laneConfiguration = tofSectors; + auto nLanes = cfgc.options().get<int>("tof-lanes"); + auto inputType = cfgc.options().get<std::string>("input-type"); + auto outputType = cfgc.options().get<std::string>("output-type"); + + bool writematching = 0; + bool writecalib = 0; + + if (outputType.rfind("matching-info") < outputType.size()) { + writematching = 1; + } + if (outputType.rfind("calib-info") < outputType.size()) { + writecalib = 1; + } + + bool clusterinput = 0; + bool trackinput = 0; + bool fitinput = 0; + + if (inputType.rfind("clusters") < inputType.size()) { + clusterinput = 1; + } + if (inputType.rfind("tracks") < inputType.size()) { + trackinput = 1; + } + auto useMC = !cfgc.options().get<bool>("disable-mc"); + auto useCCDB = cfgc.options().get<bool>("use-ccdb"); + auto useFIT = cfgc.options().get<bool>("use-fit"); + bool disableRootInput = cfgc.options().get<bool>("disable-root-input"); + bool disableRootOutput = cfgc.options().get<bool>("disable-root-output"); + + if (inputType.rfind("fit") < inputType.size()) { + fitinput = 1; + useFIT = 1; + } + + LOG(INFO) << "TOF RECO WORKFLOW configuration"; + LOG(INFO) << "TOF input = " << cfgc.options().get<std::string>("input-type"); + LOG(INFO) << "TOF output = " << cfgc.options().get<std::string>("output-type"); + LOG(INFO) << "TOF sectors = " << cfgc.options().get<std::string>("tof-sectors"); + LOG(INFO) << "TOF disable-mc = " << cfgc.options().get<std::string>("disable-mc"); + LOG(INFO) << "TOF lanes = " << cfgc.options().get<std::string>("tof-lanes"); + LOG(INFO) << "TOF use-ccdb = " << cfgc.options().get<std::string>("use-ccdb"); + LOG(INFO) << "TOF use-fit = " << cfgc.options().get<std::string>("use-fit"); + LOG(INFO) << "TOF disable-root-input = " << disableRootInput; + LOG(INFO) << "TOF disable-root-output = " << disableRootOutput; + + // useMC = false; + // LOG(INFO) << "TOF disable MC forced"; + // writecalib = false; + // LOG(INFO) << "TOF CalibInfo disabled (forced)"; + + if (clusterinput) { + LOG(INFO) << "Insert TOF Cluster Reader"; + specs.emplace_back(o2::tof::getClusterReaderSpec(useMC)); + } + if (trackinput) { + LOG(INFO) << "Insert TPC Track Reader"; + specs.emplace_back(o2::tpc::getTPCTrackReaderSpec(useMC)); + } + + if (fitinput) { + LOG(INFO) << "Insert FIT RecPoint Reader"; + specs.emplace_back(o2::ft0::getRecPointReaderSpec(useMC)); + } + + LOG(INFO) << "Insert TOF Matching"; + specs.emplace_back(o2::tof::getTOFRecoWorkflowWithTPCSpec(useMC, useFIT)); + + if (writematching && !disableRootOutput) { + LOG(INFO) << "Insert TOF Matched Info Writer"; + specs.emplace_back(o2::tof::getTOFMatchedWriterSpec(useMC, "o2match_toftpc.root")); + } + if (writecalib) { + LOG(INFO) << "Insert TOF Calib Info Writer"; + specs.emplace_back(o2::tof::getTOFCalibWriterSpec("o2calib_toftpc.root")); + } + + LOG(INFO) << "Number of active devices = " << specs.size(); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx index 40f11833d8b0d..054eb3d7ab1b6 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx @@ -15,15 +15,17 @@ #include "DetectorsBase/Propagator.h" #include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" -#include "TOFWorkflow/DigitReaderSpec.h" -#include "TOFWorkflow/TOFDigitWriterSpec.h" -#include "TOFWorkflow/ClusterReaderSpec.h" -#include "TOFWorkflow/TOFClusterizerSpec.h" -#include "TOFWorkflow/TOFClusterWriterSpec.h" +#include "TOFWorkflowUtils/DigitReaderSpec.h" +#include "TOFWorkflowUtils/TOFDigitWriterSpec.h" +#include "TOFWorkflowUtils/ClusterReaderSpec.h" +#include "TOFWorkflowUtils/TOFClusterizerSpec.h" +#include "TOFWorkflowUtils/TOFClusterWriterSpec.h" #include "TOFWorkflow/TOFMatchedWriterSpec.h" #include "TOFWorkflow/TOFCalibWriterSpec.h" -#include "TOFWorkflow/TOFRawWriterSpec.h" -#include "TOFWorkflow/CompressedDecodingTask.h" +#include "TOFWorkflowUtils/TOFRawWriterSpec.h" +#include "TOFWorkflowUtils/CompressedDecodingTask.h" +#include "TOFWorkflowUtils/EntropyEncoderSpec.h" +#include "TOFWorkflowUtils/EntropyDecoderSpec.h" #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamSpec.h" #include "TOFWorkflow/RecoWorkflowSpec.h" @@ -36,7 +38,7 @@ #include "DataFormatsParameters/GRPObject.h" // FIT -#include "FITWorkflow/FT0RecPointReaderSpec.h" +#include "FT0Workflow/RecPointReaderSpec.h" #include <string> #include <stdexcept> @@ -46,8 +48,8 @@ // including Framework/runDataProcessing void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) { - workflowOptions.push_back(ConfigParamSpec{"input-type", o2::framework::VariantType::String, "digits", {"digits, raw, clusters, TBI"}}); - workflowOptions.push_back(ConfigParamSpec{"output-type", o2::framework::VariantType::String, "clusters,matching-info,calib-info", {"digits,clusters, matching-info, calib-info, TBI"}}); + workflowOptions.push_back(ConfigParamSpec{"input-type", o2::framework::VariantType::String, "digits", {"digits, raw, clusters"}}); + workflowOptions.push_back(ConfigParamSpec{"output-type", o2::framework::VariantType::String, "clusters,matching-info,calib-info", {"digits, clusters, matching-info, calib-info, raw, ctf"}}); workflowOptions.push_back(ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable sending of MC information, TBI"}}); workflowOptions.push_back(ConfigParamSpec{"tof-sectors", o2::framework::VariantType::String, "0-17", {"TOF sector range, e.g. 5-7,8,9 ,TBI"}}); workflowOptions.push_back(ConfigParamSpec{"tof-lanes", o2::framework::VariantType::Int, 1, {"number of parallel lanes up to the matcher, TBI"}}); @@ -56,7 +58,10 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) workflowOptions.push_back(ConfigParamSpec{"input-desc", o2::framework::VariantType::String, "CRAWDATA", {"Input specs description string"}}); workflowOptions.push_back(ConfigParamSpec{"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}); workflowOptions.push_back(ConfigParamSpec{"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}); + workflowOptions.push_back(ConfigParamSpec{"conet-mode", o2::framework::VariantType::Bool, false, {"enable conet mode"}}); workflowOptions.push_back(ConfigParamSpec{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings ..."}}); + workflowOptions.push_back(ConfigParamSpec{"disable-row-writing", o2::framework::VariantType::Bool, false, {"disable ROW in Digit writing"}}); + workflowOptions.push_back(ConfigParamSpec{"write-decoding-errors", o2::framework::VariantType::Bool, false, {"trace errors in digits output when decoding"}}); } #include "Framework/runDataProcessing.h" // the main driver @@ -82,13 +87,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec specs; if (!cfgc.helpOnCommandLine()) { - std::string inputGRP = o2::base::NameConf::getGRPFileName(); - o2::base::Propagator::initFieldFromGRP(inputGRP); - const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); - if (!grp) { - LOG(ERROR) << "This workflow needs a valid GRP file to start"; - return specs; - } o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); // o2::conf::ConfigurableParam::writeINI("o2tofrecoflow_configuration.ini"); } @@ -104,29 +102,51 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) bool writecalib = 0; bool writedigit = 0; bool writeraw = 0; + bool writectf = 0; + bool writeerr = 0; - if (outputType.rfind("clusters") < outputType.size()) + if (outputType.rfind("clusters") < outputType.size()) { writecluster = 1; - if (outputType.rfind("matching-info") < outputType.size()) + } + if (outputType.rfind("matching-info") < outputType.size()) { writematching = 1; - if (outputType.rfind("calib-info") < outputType.size()) + } + if (outputType.rfind("calib-info") < outputType.size()) { writecalib = 1; - if (outputType.rfind("digits") < outputType.size()) + } + if (outputType.rfind("digits") < outputType.size()) { writedigit = 1; - if (outputType.rfind("raw") < outputType.size()) + } + if (outputType.rfind("raw") < outputType.size()) { writeraw = 1; + } + if (outputType.rfind("ctf") < outputType.size()) { + writectf = 1; + } bool dgtinput = 0; + bool clusterinput = 0; + bool rawinput = 0; if (inputType == "digits") { dgtinput = 1; - } - bool clusterinput = 0; - if (inputType == "clusters") { + } else if (inputType == "clusters") { clusterinput = 1; - } - bool rawinput = 0; - if (inputType == "raw") { + } else if (inputType == "raw") { rawinput = 1; + writeerr = cfgc.options().get<bool>("write-decoding-errors"); + } + + if (rawinput) { + } else { + if (!cfgc.helpOnCommandLine()) { + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + o2::base::Propagator::initFieldFromGRP(inputGRP); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + if (!grp) { + LOG(ERROR) << "This workflow needs a valid GRP file to start"; + return specs; + } + } } auto useMC = !cfgc.options().get<bool>("disable-mc"); @@ -134,6 +154,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto useFIT = cfgc.options().get<bool>("use-fit"); bool disableRootInput = cfgc.options().get<bool>("disable-root-input") || rawinput; bool disableRootOutput = cfgc.options().get<bool>("disable-root-output"); + bool conetmode = cfgc.options().get<bool>("conet-mode"); + bool disableROWwriting = cfgc.options().get<bool>("disable-row-writing"); LOG(INFO) << "TOF RECO WORKFLOW configuration"; LOG(INFO) << "TOF input = " << cfgc.options().get<std::string>("input-type"); @@ -145,15 +167,19 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) LOG(INFO) << "TOF use-fit = " << cfgc.options().get<std::string>("use-fit"); LOG(INFO) << "TOF disable-root-input = " << disableRootInput; LOG(INFO) << "TOF disable-root-output = " << disableRootOutput; + LOG(INFO) << "TOF conet-mode = " << conetmode; + LOG(INFO) << "TOF disable-row-writing = " << disableROWwriting; + LOG(INFO) << "TOF write-decoding-errors = " << writeerr; - if (clusterinput) { + if (clusterinput && !disableRootInput) { LOG(INFO) << "Insert TOF Cluster Reader"; specs.emplace_back(o2::tof::getClusterReaderSpec(useMC)); } else if (dgtinput) { // TOF clusterizer - LOG(INFO) << "Insert TOF Digit reader from file"; - specs.emplace_back(o2::tof::getDigitReaderSpec(useMC)); - + if (!disableRootInput) { + LOG(INFO) << "Insert TOF Digit reader from file"; + specs.emplace_back(o2::tof::getDigitReaderSpec(useMC)); + } if (writeraw) { LOG(INFO) << "Insert TOF Raw writer"; specs.emplace_back(o2::tof::getTOFRawWriterSpec()); @@ -161,13 +187,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } else if (rawinput) { LOG(INFO) << "Insert TOF Compressed Raw Decoder"; auto inputDesc = cfgc.options().get<std::string>("input-desc"); - specs.emplace_back(o2::tof::getCompressedDecodingSpec(inputDesc)); + specs.emplace_back(o2::tof::getCompressedDecodingSpec(inputDesc, conetmode)); useMC = 0; if (writedigit && !disableRootOutput) { // add TOF digit writer without mc labels LOG(INFO) << "Insert TOF Digit Writer"; - specs.emplace_back(o2::tof::getTOFDigitWriterSpec(0)); + specs.emplace_back(o2::tof::getTOFDigitWriterSpec(0, writeerr)); } } @@ -180,8 +206,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } } - if (useFIT) { - specs.emplace_back(o2::ft0::getFT0RecPointReaderSpec(useMC)); + if (useFIT && !disableRootInput) { + specs.emplace_back(o2::ft0::getRecPointReaderSpec(useMC)); } if (writematching || writecalib) { @@ -196,11 +222,16 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) LOG(INFO) << "Insert TOF Matched Info Writer"; specs.emplace_back(o2::tof::getTOFMatchedWriterSpec(useMC)); } - if (writecalib) { + if (writecalib && !disableRootOutput) { LOG(INFO) << "Insert TOF Calib Info Writer"; specs.emplace_back(o2::tof::getTOFCalibWriterSpec()); } } + if (writectf) { + LOG(INFO) << "Insert TOF CTF encoder"; + specs.emplace_back(o2::tof::getEntropyEncoderSpec()); + } + LOG(INFO) << "Number of active devices = " << specs.size(); return std::move(specs); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx index 314ccf03b0ae8..cf8a9a3e48e76 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -54,7 +54,7 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) //---------------------------->> TPC Clusters loading >>------------------------------------------ int operation = 0; uint64_t activeSectors = 0; - std::bitset<o2::tpc::Constants::MAXSECTOR> validSectors = 0; + std::bitset<o2::tpc::constants::MAXSECTOR> validSectors = 0; std::map<int, DataRef> datarefs; std::vector<InputSpec> filter = { {"check", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}, Lifetime::Timeframe}, @@ -67,7 +67,7 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) throw std::runtime_error("sector header missing on header stack"); } const int& sector = sectorHeader->sector(); - std::bitset<o2::tpc::Constants::MAXSECTOR> sectorMask(sectorHeader->sectorBits); + std::bitset<o2::tpc::constants::MAXSECTOR> sectorMask(sectorHeader->sectorBits); LOG(INFO) << "Reading TPC cluster data, sector mask is " << sectorMask; if ((validSectors & sectorMask).any()) { // have already data for this sector, this should not happen in the current @@ -86,7 +86,7 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) << std::endl // << " input status: " << validSectors // << std::endl // - << " active sectors: " << std::bitset<o2::tpc::Constants::MAXSECTOR>(activeSectors); + << " active sectors: " << std::bitset<o2::tpc::constants::MAXSECTOR>(activeSectors); }; if (activeSectors == 0 || (activeSectors & validSectors.to_ulong()) != activeSectors) { @@ -145,7 +145,9 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) o2::tpc::ClusterNativeAccess clusterIndex; std::unique_ptr<o2::tpc::ClusterNative[]> clusterBuffer; memset(&clusterIndex, 0, sizeof(clusterIndex)); - o2::tpc::ClusterNativeHelper::Reader::fillIndex(clusterIndex, clusterBuffer, clustersTPC); + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer dummyMCOutput; + std::vector<o2::tpc::ClusterNativeHelper::ConstMCLabelContainerView> dummyMCInput; + o2::tpc::ClusterNativeHelper::Reader::fillIndex(clusterIndex, clusterBuffer, dummyMCOutput, clustersTPC, dummyMCInput); //----------------------------<< TPC Clusters loading <<------------------------------------------ // pass input data to TrackInterpolation object @@ -162,7 +164,7 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) // not yet implemented } - printf("TPC Interpolation Workflow initialized. Start processing...\n"); + LOG(INFO) << "TPC Interpolation Workflow initialized. Start processing..."; mInterpolation.process(); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TrackInterpolationWorkflow.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TrackInterpolationWorkflow.cxx index b51dbfc9e5667..4793d8b86ea2a 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TrackInterpolationWorkflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TrackInterpolationWorkflow.cxx @@ -16,7 +16,7 @@ #include "TPCWorkflow/TrackReaderSpec.h" #include "TPCWorkflow/PublisherSpec.h" #include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" -#include "TOFWorkflow/ClusterReaderSpec.h" +#include "TOFWorkflowUtils/ClusterReaderSpec.h" #include "TOFWorkflow/TOFMatchedReaderSpec.h" #include "Algorithm/RangeTokenizer.h" #include "TPCInterpolationWorkflow/TPCResidualWriterSpec.h" diff --git a/Detectors/HMPID/base/include/HMPIDBase/Param.h b/Detectors/HMPID/base/include/HMPIDBase/Param.h index c71cf5942a331..d94a6c2caee95 100644 --- a/Detectors/HMPID/base/include/HMPIDBase/Param.h +++ b/Detectors/HMPID/base/include/HMPIDBase/Param.h @@ -119,16 +119,18 @@ class Param static Int_t InHVSector(float y); //find HV sector static Int_t Radiator(float y) { - if (InHVSector(y) < 0) + if (InHVSector(y) < 0) { return -1; + } return InHVSector(y) / 2; } // height in the radiator to estimate temperature from gradient static double HinRad(float y) { - if (Radiator(y) < 0) + if (Radiator(y) < 0) { return -1; + } return y - Radiator(y) * fgkMinPcY[Radiator(y)]; } //is point inside chamber boundaries? diff --git a/Detectors/HMPID/base/src/Param.cxx b/Detectors/HMPID/base/src/Param.cxx index 3a75748b7ab12..9358bc480e691 100644 --- a/Detectors/HMPID/base/src/Param.cxx +++ b/Detectors/HMPID/base/src/Param.cxx @@ -93,13 +93,15 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT float dead = 2.6; // cm of the dead zones between PCs-> See 2CRC2099P1 - if (noGeo == kTRUE) + if (noGeo == kTRUE) { fgInstanceType = kFALSE; //instance from ideal geometry, no actual geom is present + } if (noGeo == kFALSE && !gGeoManager) { TGeoManager::Import("geometry.root"); - if (!gGeoManager) + if (!gGeoManager) { Printf("!!!!!!No geometry loaded!!!!!!!"); + } } fgCellX = 0.8; @@ -150,7 +152,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT } } - for (Int_t i = kMinCh; i <= kMaxCh; i++) + for (Int_t i = kMinCh; i <= kMaxCh; i++) { if (gGeoManager && gGeoManager->IsClosed()) { TGeoPNEntry* pne = gGeoManager->GetAlignableEntry(Form("/HMPID/Chamber%i", i)); if (!pne) { @@ -159,9 +161,9 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT IdealPosition(i, mM[i]); } else { TGeoPhysicalNode* pnode = pne->GetPhysicalNode(); - if (pnode) + if (pnode) { mM[i] = new TGeoHMatrix(*(pnode->GetMatrix())); - else { + } else { mM[i] = new TGeoHMatrix; IdealPosition(i, mM[i]); } @@ -170,6 +172,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT mM[i] = new TGeoHMatrix; IdealPosition(i, mM[i]); } + } fgInstance = this; } //ctor //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -177,8 +180,9 @@ void Param::Print(Option_t* opt) const { // print some usefull (hopefully) info on some internal guts of HMPID parametrisation - for (Int_t i = 0; i < 7; i++) + for (Int_t i = 0; i < 7; i++) { mM[i]->Print(opt); + } } //Print() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::IdealPosition(Int_t iCh, TGeoHMatrix* pMatrix) @@ -284,10 +288,12 @@ double Param::Sigma2(double trkTheta, double trkPhi, double ckovTh, double ckovP TVector3 v(-999, -999, -999); double trkBeta = 1. / (TMath::Cos(ckovTh) * GetRefIdx()); - if (trkBeta > 1) + if (trkBeta > 1) { trkBeta = 1; //protection against bad measured thetaCer - if (trkBeta < 0) + } + if (trkBeta < 0) { trkBeta = 0.0001; // + } v.SetX(SigLoc(trkTheta, trkPhi, ckovTh, ckovPh, trkBeta)); v.SetY(SigGeom(trkTheta, trkPhi, ckovTh, ckovPh, trkBeta)); @@ -318,8 +324,9 @@ double Param::SigLoc(double trkTheta, double trkPhi, double thetaC, double phiC, double alpha = cost - tantheta * cosfd * sint; // formula (11) double k = 1. - GetRefIdx() * GetRefIdx() + alpha * alpha / (betaM * betaM); // formula (after 8 in the text) - if (k < 0) + if (k < 0) { return 1e10; + } double mu = sint * sinf + tantheta * (cost * cosfd * sinf + sinfd * cosf); // formula (10) double e = sint * cosf + tantheta * (cost * cosfd * cosf - sinfd * sinf); // formula (9) @@ -381,8 +388,9 @@ double Param::SigGeom(double trkTheta, double trkPhi, double thetaC, double phiC double alpha = cost - tantheta * cosfd * sint; // formula (11) double k = 1. - GetRefIdx() * GetRefIdx() + alpha * alpha / (betaM * betaM); // formula (after 8 in the text) - if (k < 0) + if (k < 0) { return 1e10; + } double eTr = 0.5 * RadThick() * betaM * TMath::Sqrt(k) / (GapThick() * alpha); // formula (14) double lambda = (1. - sint * sinf) * (1. + sint * sinf); // formula (15) @@ -427,8 +435,9 @@ Param* Param::Instance() // Return pointer to the AliHMPIDParam singleton. // Arguments: none // Returns: pointer to the instance of AliHMPIDParam or 0 if no geometry - if (!fgInstance) + if (!fgInstance) { new Param(kFALSE); //default setting for reconstruction, if no geometry.root -> AliFatal + } return fgInstance; } //Instance() @@ -438,8 +447,9 @@ Param* Param::InstanceNoGeo() // Return pointer to the AliHMPIDParam singleton without the geometry.root. // Arguments: none // Returns: pointer to the instance of AliHMPIDParam or 0 if no geometry - if (!fgInstance) + if (!fgInstance) { new Param(kTRUE); //to avoid AliFatal, for MOOD and displays, use ideal geometry parameters + } return fgInstance; } //Instance() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -448,9 +458,11 @@ bool Param::IsInDead(float x, float y) // Check is the current point is outside of sensitive area or in dead zones // Arguments: x,y -position // 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]) + 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 kTRUE; } @@ -461,8 +473,9 @@ bool Param::IsDeadPad(Int_t padx, Int_t pady, Int_t ch) // Arguments: padx,pady pad integer coord // Returns: kTRUE if dead, kFALSE if active - if (fgMapPad[padx - 1][pady - 1][ch]) + if (fgMapPad[padx - 1][pady - 1][ch]) { return kFALSE; //current pad active + } return kTRUE; } @@ -481,8 +494,9 @@ void Param::Lors2Pad(float x, float y, Int_t& pc, Int_t& px, Int_t& py) pc = 1; px = Int_t((x - fgkMinPcX[1]) / SizePadX()); } //PC 1 or 3 or 5 - else + else { return; + } if (y > fgkMinPcY[0] && y < fgkMaxPcY[0]) { py = Int_t(y / SizePadY()); } //PC 0 or 1 @@ -494,8 +508,9 @@ void Param::Lors2Pad(float x, float y, Int_t& pc, Int_t& px, Int_t& py) pc += 4; py = Int_t((y - fgkMinPcY[4]) / SizePadY()); } //PC 4 or 5 - else + else { return; + } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Int_t Param::InHVSector(float y) @@ -507,8 +522,9 @@ Int_t Param::InHVSector(float y) Int_t hvsec = -1; Int_t pc, px, py; Lors2Pad(1., y, pc, px, py); - if (py == -1) + if (py == -1) { return hvsec; + } hvsec = (py + (pc / 2) * (kMaxPy + 1)) / ((kMaxPy + 1) / 2); @@ -519,12 +535,15 @@ double Param::FindTemp(double tLow, double tHigh, double y) { // Model for gradient in temperature double yRad = HinRad(y); //height in a given radiator - if (tHigh < tLow) + if (tHigh < tLow) { tHigh = tLow; //if Tout < Tin consider just Tin as reference... - if (yRad < 0) + } + if (yRad < 0) { yRad = 0; //protection against fake y values - if (yRad > SizePcY()) + } + if (yRad > SizePcY()) { yRad = SizePcY(); //protection against fake y values + } double gradT = (tHigh - tLow) / SizePcY(); // linear gradient return gradT * yRad + tLow; @@ -589,13 +608,15 @@ void Param::PrintChStatus(Int_t ch) Printf(" --------- C H A M B E R %d ---------------", ch); for (Int_t pady = kMaxPcy; pady >= 0; pady--) { for (Int_t padx = 0; padx < kMaxPcx + 1; padx++) { - if (padx == 80) + if (padx == 80) { printf(" "); + } printf("%d", fgMapPad[padx][pady][ch]); } printf(" %d \n", pady + 1); - if (pady % 48 == 0) + if (pady % 48 == 0) { printf("\n"); + } } printf("\n"); } diff --git a/Detectors/HMPID/simulation/src/Detector.cxx b/Detectors/HMPID/simulation/src/Detector.cxx index 60ff9d033d970..89af501ba15cd 100644 --- a/Detectors/HMPID/simulation/src/Detector.cxx +++ b/Detectors/HMPID/simulation/src/Detector.cxx @@ -151,17 +151,19 @@ void Detector::GenFee(Float_t qtot) Double_t ranf[2]; fMC->GetRandom()->RndmArray(2, ranf); //Sample direction cthf = ranf[0] * 2 - 1.0; - if (cthf < 0) + if (cthf < 0) { continue; + } sthf = TMath::Sqrt((1. - cthf) * (1. + cthf)); phif = ranf[1] * 2 * TMath::Pi(); - if (Double_t randomNumber = fMC->GetRandom()->Rndm() <= 0.57) + if (Double_t randomNumber = fMC->GetRandom()->Rndm() <= 0.57) { enfp = 7.5e-9; - else if (randomNumber <= 0.7) + } else if (randomNumber <= 0.7) { enfp = 6.4e-9; - else + } else { enfp = 7.9e-9; + } dir[0] = sthf * TMath::Sin(phif); dir[1] = cthf; @@ -184,40 +186,49 @@ void Detector::GenFee(Float_t qtot) e3[2] = -dir[0]; vmod = 0; - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { vmod += e1[j] * e1[j]; - if (!vmod) + } + if (!vmod) { for (j = 0; j < 3; j++) { uswop = e1[j]; e1[j] = e3[j]; e3[j] = uswop; } + } vmod = 0; - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { vmod += e2[j] * e2[j]; - if (!vmod) + } + if (!vmod) { for (j = 0; j < 3; j++) { uswop = e2[j]; e2[j] = e3[j]; e3[j] = uswop; } + } vmod = 0; - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { vmod += e1[j] * e1[j]; + } vmod = TMath::Sqrt(1 / vmod); - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { e1[j] *= vmod; + } vmod = 0; - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { vmod += e2[j] * e2[j]; + } vmod = TMath::Sqrt(1 / vmod); - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { e2[j] *= vmod; + } phi = fMC->GetRandom()->Rndm() * 2 * TMath::Pi(); - for (j = 0; j < 3; j++) + for (j = 0; j < 3; j++) { pol[j] = e1[j] * TMath::Sin(phi) + e2[j] * TMath::Cos(phi); + } fMC->Gdtom(pol, pol, 2); Int_t outputNtracksStored; } //feedbacks loop @@ -242,8 +253,9 @@ Bool_t Detector::IsLostByFresnel() if (fMC->GetRandom()->Rndm() < Fresnel(p4.E() * 1e9, cotheta, 1)) { // AliDebug(1,"Photon lost"); return kTRUE; - } else + } else { return kFALSE; + } } //IsLostByFresnel() //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) @@ -296,8 +308,9 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) if (pola) { Float_t pdotr = 0.8; //DEGREE OF POLARIZATION : 1->P , -1->S fresn = 0.5 * (rp * (1 + pdotr) + rs * (1 - pdotr)); - } else + } else { fresn = 0.5 * (rp + rs); + } fresn = fresn * rO; return fresn; @@ -613,13 +626,16 @@ TGeoVolume* Detector::createChamber(int number) rad->AddNode(si1, 2, new TGeoTranslation(0 * mm, +204 * mm, -0.5 * mm)); rad->AddNode(si2, 1, new TGeoTranslation(-660 * mm, 0 * mm, -0.5 * mm)); rad->AddNode(si2, 2, new TGeoTranslation(+660 * mm, 0 * mm, -0.5 * mm)); - for (Int_t i = 0; i < 3; i++) - for (Int_t j = 0; j < 10; j++) + for (Int_t i = 0; i < 3; i++) { + for (Int_t j = 0; j < 10; j++) { rad->AddNode(spa, 10 * i + j, new TGeoTranslation(-1330 * mm / 2 + 116 * mm + j * 122 * mm, (i - 1) * 105 * mm, -0.5 * mm)); + } + } hmp->AddNode(fr4, 1, new TGeoTranslation(0 * mm, 0 * mm, 9.00 * mm)); // p.84 TDR - for (int i = 1; i <= 322; i++) + for (int i = 1; i <= 322; i++) { fr4->AddNode(col, i, new TGeoCombiTrans(0 * mm, -1296 / 2 * mm + i * 4 * mm, -5 * mm, rot)); // F4 2043P1 + } fr4->AddNode(f4a, 1, new TGeoTranslation(0 * mm, 0 * mm, 2.5 * mm)); f4a->AddNode(f4i, 1, new TGeoTranslation(0 * mm, 0 * mm, 0 * mm)); hmp->AddNode(sec, 4, new TGeoTranslation(-335 * mm, +433 * mm, 78.6 * mm)); @@ -1152,15 +1168,17 @@ TGeoVolume* Detector::CradleBaseVolume(TGeoMedium* med, Double_t l[7], const cha xtruOut->DefineSection(1, +l[4] / 2., 0., 0., 1.0); // 1= II plane z; Double_t tgalpha = 0; - if (xv[3] - xv[0] == 0) + if (xv[3] - xv[0] == 0) { tgalpha = 999999; - else + } else { tgalpha = l[2] / TMath::Abs(xv[3] - xv[0]); + } Double_t tgbeta = 0; - if (xv[2] - xv[1] == 0) + if (xv[2] - xv[1] == 0) { tgbeta = 999999; - else + } else { tgbeta = l[2] / TMath::Abs(xv[2] - xv[1]); + } xv[0] = xv[0] - l[5] / tgalpha; yv[0] = l[2] / 2 - l[5]; diff --git a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx index 49ebbd7b020e3..1d665b89db39e 100644 --- a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx +++ b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx @@ -213,8 +213,9 @@ void TestDataReader::run(ProcessingContext& pc) int pos = mNowFolderNames[i].find_last_of("/"); - if (pos != string::npos) + if (pos != string::npos) { mRunID = mNowFolderNames[i].substr(pos + 1); + } LOG(DEBUG) << "FileDone = " << mFileDone << endl; @@ -234,8 +235,9 @@ void TestDataReader::run(ProcessingContext& pc) //Getting the FileID string FileIDS; pos = mDiffFileNames[i][0].find_last_of("/"); - if (pos != string::npos) + if (pos != string::npos) { FileIDS = mDiffFileNames[i][0].substr(pos + 1); + } cout << "Before FileIDS = " << FileIDS << endl; @@ -274,8 +276,9 @@ void TestDataReader::run(ProcessingContext& pc) mErrorsVecTest.clear(); mDigitsTest.clear(); mMultiDigitsTest.clear(); - if (mFolderNames.size() < mNowFolderNames.size()) + if (mFolderNames.size() < mNowFolderNames.size()) { mFileNames.push_back(NewNextFold); + } cout << "Done!!! You should see the Canvas Updated " << endl; break; } @@ -298,8 +301,9 @@ void TestDataReader::run(ProcessingContext& pc) auto& rawErrorReader = reinterpret_cast<RawReader&>(mRawReader); while ((mChipData = mRawReader.getNextChipData(mChips))) { - if (NChip < NChipMax) + if (NChip < NChipMax) { break; + } // cout << "Pass Chip" << endl; const auto* ruInfo = rawErrorReader.getCurrRUDecodeData()->ruInfo; @@ -342,12 +346,14 @@ void TestDataReader::run(ProcessingContext& pc) TimePrint = 1; } - if (NEvent % 100000 != 0) + if (NEvent % 100000 != 0) { TimePrint = 0; + } for (int i = 0; i < o2::itsmft::GBTLinkDecodingStat::NErrorsDefined; i++) { - if (mErrors[i] < 4294967295) + if (mErrors[i] < 4294967295) { mErrors[i] = mErrors[i] + (int)statRU->errorCounts[i]; + } } if (mTrackError == 1) { @@ -355,8 +361,9 @@ void TestDataReader::run(ProcessingContext& pc) ErrorDetcted = 0; for (int i = 0; i < o2::itsmft::GBTLinkDecodingStat::NErrorsDefined; i++) { - if ((int)statRU->errorCounts[i] > 0) + if ((int)statRU->errorCounts[i] > 0) { ErrorDetcted = 1; + } } if (ErrorDetcted == 1) { @@ -375,8 +382,9 @@ void TestDataReader::run(ProcessingContext& pc) int ChipID = mChipData->getChipID(); for (auto& pixel : pixels) { - if (Index < IndexMax) + if (Index < IndexMax) { break; + } int col = pixel.getCol(); int row = pixel.getRow(); mDigits.emplace_back(ChipID, row, col, 0); @@ -390,8 +398,9 @@ void TestDataReader::run(ProcessingContext& pc) mNDigits.push_back(mTotalPixelSize); mErrorsVec.push_back(mErrors); LOG(DEBUG) << "Run " << mNowFolderNames[i] << " File " << mInputName << " Integrated Raw Pixel Pushed " << mDigits.size(); - if (mFolderNames.size() < mNowFolderNames.size()) + if (mFolderNames.size() < mNowFolderNames.size()) { mFileNames.push_back(NewNextFold); + } mFileNames[i].push_back(mInputName); fout << " END OF ERROR REPORT " << endl; } @@ -401,8 +410,9 @@ void TestDataReader::run(ProcessingContext& pc) LOG(DEBUG) << "mIndexPush Before = " << mIndexPush << " mDigits.size() = " << mDigits.size(); - if (mDigits.size() > 0) + if (mDigits.size() > 0) { PercentDone = double(mIndexPush) / double(mDigits.size()); + } cout << "Percentage Processed = " << Form("%.2f", 100. * PercentDone) << endl; if (mIndexPush < mDigits.size()) { @@ -421,8 +431,9 @@ void TestDataReader::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"ITS", "Error", 0, Lifetime::Timeframe}, mErrorsVec[j]); mIndexPushEx = mIndexPush + mNDigits[j]; LOG(DEBUG) << "IndexPushEx = " << mIndexPushEx << " mDigits.size() " << mDigits.size(); - if (mIndexPushEx > mDigits.size() - 5) + if (mIndexPushEx > mDigits.size() - 5) { mFileDone = 1; + } LOG(DEBUG) << "FileDone = " << mFileDone; LOG(DEBUG) << "FileRemain = " << mFileRemain; @@ -514,8 +525,9 @@ std::vector<string> TestDataReader::GetFName(std::string folder) //printf("%s\n", directory->d_name); - if (!(!strcmp(directory->d_name, ".") || !strcmp(directory->d_name, ".."))) + if (!(!strcmp(directory->d_name, ".") || !strcmp(directory->d_name, ".."))) { names.push_back(folder + "/" + directory->d_name); + } } closedir(dirp); diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index 69f3ae4998942..02b46cb982050 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -42,7 +42,7 @@ namespace its class GeometryTGeo : public o2::itsmft::GeometryTGeo { public: - typedef o2::Transform3D Mat3D; + typedef o2::math_utils::Transform3D Mat3D; using DetMatrixCache::getMatrixL2G; using DetMatrixCache::getMatrixT2GRot; using DetMatrixCache::getMatrixT2L; @@ -53,8 +53,9 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static GeometryTGeo* Instance() { // get (create if needed) a unique instance of the object - if (!sInstance) + if (!sInstance) { sInstance = std::unique_ptr<GeometryTGeo>(new GeometryTGeo(true, 0)); + } return sInstance.get(); } @@ -67,9 +68,9 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo // 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::utils::bit2Mask(o2::TransformType::T2L, // default transformations to load - o2::TransformType::T2G, - o2::TransformType::L2G)*/ + /*o2::base::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, // default transformations to load + o2::math_utils::TransformType::T2G, + o2::math_utils::TransformType::L2G)*/ ); /// Default destructor diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index 5f3e13e0bb2a1..eeb3917886377 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -18,7 +18,7 @@ #include "ITSBase/GeometryTGeo.h" #include "DetectorsBase/GeometryManager.h" #include "ITSMFTBase/SegmentationAlpide.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "FairLogger.h" // for LOG @@ -43,7 +43,6 @@ using namespace TMath; using namespace o2::its; using namespace o2::detectors; -using namespace o2::utils; using Segmentation = o2::itsmft::SegmentationAlpide; @@ -385,7 +384,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } // build matrices - if ((mask & o2::utils::bit2Mask(o2::TransformType::L2G)) && !getCacheL2G().isFilled()) { + 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"; auto& cacheL2G = getCacheL2G(); @@ -397,7 +396,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2L)) && !getCacheT2L().isFilled()) { + 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"; auto& cacheT2L = getCacheT2L(); @@ -408,7 +407,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2G)) && !getCacheT2G().isFilled()) { + 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"; @@ -422,7 +421,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2GRot)) && !getCacheT2GRot().isFilled()) { + 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"; auto& cacheT2Gr = getCacheT2GRot(); @@ -680,8 +679,9 @@ int GeometryTGeo::extractLayerChipType(int lay) const void GeometryTGeo::Print(Option_t*) const { printf("NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); - if (!isBuilt()) + if (!isBuilt()) { return; + } for (int i = 0; i < mNumberOfLayers; i++) { printf( @@ -709,7 +709,7 @@ void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) double xp = gloB[0] - dx * t, yp = gloB[1] - dy * t; x = Sqrt(xp * xp + yp * yp); alp = ATan2(yp, xp); - BringTo02Pi(alp); + o2::math_utils::bringTo02Pi(alp); } //__________________________________________________________________________ diff --git a/Detectors/ITSMFT/ITS/macros/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/CMakeLists.txt index b88d9a3a43c4e..433d0e3e352e0 100644 --- a/Detectors/ITSMFT/ITS/macros/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory(EVE) add_subdirectory(test) +add_subdirectory(Calib) diff --git a/Detectors/ITSMFT/ITS/macros/Calib/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/Calib/CMakeLists.txt new file mode 100644 index 0000000000000..ef2e78517ace7 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/Calib/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(MakeNoiseMapFromDigits.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT + O2::ITSMFTBase + O2::ITSBase) + +o2_add_test_root_macro(MakeNoiseMapFromClusters.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT + O2::ITSMFTBase + O2::ITSBase) + diff --git a/Detectors/ITSMFT/ITS/macros/Calib/MakeNoiseMapFromClusters.C b/Detectors/ITSMFT/ITS/macros/Calib/MakeNoiseMapFromClusters.C new file mode 100644 index 0000000000000..07badfbf0888b --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/Calib/MakeNoiseMapFromClusters.C @@ -0,0 +1,115 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include <iostream> +#include <vector> +#include <string> + +#include <TFile.h> +#include <TTree.h> + +#include "DataFormatsITSMFT/ClusterPattern.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/NoiseMap.h" + +#endif + +void MakeNoiseMapFromClusters(std::string input = "o2clus_its.root", std::string output = "noise.root", bool only1pix = false, float probT = 3e-6) +{ + TFile in(input.data()); + if (!in.IsOpen()) { + std::cerr << "Can not open the input file " << input << '\n'; + return; + } + auto clusTree = (TTree*)in.Get("o2sim"); + if (!clusTree) { + std::cerr << "Can not get cluster tree\n"; + return; + } + + // Clusters + std::vector<o2::itsmft::CompClusterExt>* clusters = nullptr; + clusTree->SetBranchAddress("ITSClusterComp", &clusters); + + // Pixel patterns + std::vector<unsigned char>* patternsPtr = nullptr; + auto pattBranch = clusTree->GetBranch("ITSClusterPatt"); + if (pattBranch) { + pattBranch->SetAddress(&patternsPtr); + } + + //RO frames + std::vector<o2::itsmft::ROFRecord>* rofVec = nullptr; + clusTree->SetBranchAddress("ITSClustersROF", &rofVec); + + o2::itsmft::NoiseMap noiseMap(24120); + + long int nStrobes = 0; + auto nevents = clusTree->GetEntries(); + for (int n = 0; n < nevents; n++) { + clusTree->GetEntry(n); + auto pattIt = patternsPtr->cbegin(); + for (const auto& rof : *rofVec) { + nStrobes++; + auto clustersInFrame = rof.getROFData(*clusters); + for (const auto& c : clustersInFrame) { + if (c.getPatternID() != o2::itsmft::CompCluster::InvalidPatternID) + continue; + + o2::itsmft::ClusterPattern patt(pattIt); + + auto id = c.getSensorID(); + auto row = c.getRow(); + auto col = c.getCol(); + auto colSpan = patt.getColumnSpan(); + auto rowSpan = patt.getRowSpan(); + + if ((rowSpan == 1) && (colSpan == 1)) { + noiseMap.increaseNoiseCount(id, row, col); + continue; + } + + if (only1pix) + continue; + + auto nBits = rowSpan * colSpan; + int ic = 0, ir = 0; + for (unsigned int i = 2; i < patt.getUsedBytes() + 2; i++) { + unsigned char tempChar = patt.getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) { + noiseMap.increaseNoiseCount(id, row + ir, col + ic); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } + + noiseMap.applyProbThreshold(probT, nStrobes); + + int fired = probT * nStrobes; + int ncalib = noiseMap.dumpAboveThreshold(fired); + std::cout << "Probalibity threshold: " << probT + << " number of pixels: " << ncalib << '\n'; + + TFile out(output.data(), "new"); + if (!out.IsOpen()) { + std::cerr << "Can not open the output file " << output << '\n'; + return; + } + out.WriteObject(&noiseMap, "Noise"); + out.Close(); +} diff --git a/Detectors/ITSMFT/ITS/macros/Calib/MakeNoiseMapFromDigits.C b/Detectors/ITSMFT/ITS/macros/Calib/MakeNoiseMapFromDigits.C new file mode 100644 index 0000000000000..3ee33831ebc39 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/Calib/MakeNoiseMapFromDigits.C @@ -0,0 +1,38 @@ +#include <TFile.h> +#include <TTree.h> +#include <iostream> +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/Digit.h" +#include "DataFormatsITSMFT/NoiseMap.h" + +void MakeNoiseMapFromDigits(std::string digifile = "itsdigits.root", int hitCut = 3) +{ + // Digits + TFile* file1 = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)file1->Get("o2sim"); + std::vector<o2::itsmft::Digit>* digArr = nullptr; + digTree->SetBranchAddress("ITSDigit", &digArr); + + o2::itsmft::NoiseMap noiseMap(24120); + + int nevD = digTree->GetEntries(), nd = 0; + for (int iev = 0; iev < nevD; iev++) { + digTree->GetEvent(iev); + for (const auto& d : *digArr) { + nd++; + auto id = d.getChipIndex(); + auto row = d.getRow(); + auto col = d.getColumn(); + noiseMap.increaseNoiseCount(id, row, col); + } + } + + TFile* fout = new TFile("ITSnoise.root", "new"); + fout->cd(); + fout->WriteObject(&noiseMap, "Noise"); + fout->Close(); + + int nPixelCalib = noiseMap.dumpAboveThreshold(hitCut); + std::cout << "Noise threshold = " << hitCut << " Noisy pixels = " << nPixelCalib << '\n'; + std::cout << "Total Digits Processed = " << nd << '\n'; +} diff --git a/Detectors/ITSMFT/ITS/macros/EVE/DisplayEventsComp.C b/Detectors/ITSMFT/ITS/macros/EVE/DisplayEventsComp.C index a1ad922b4ec2a..91aec1299b87c 100644 --- a/Detectors/ITSMFT/ITS/macros/EVE/DisplayEventsComp.C +++ b/Detectors/ITSMFT/ITS/macros/EVE/DisplayEventsComp.C @@ -438,8 +438,8 @@ void init(int entry = 0, int chip = 13, // Geometry o2::base::GeometryManager::loadGeometry(inputGeom); auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // Chip View browser->GetTabRight()->SetText("Chip View"); diff --git a/Detectors/ITSMFT/ITS/macros/EVE/geom_list_ITS.txt b/Detectors/ITSMFT/ITS/macros/EVE/geom_list_ITS.txt index ec12b17e5b6f6..ad648c2945c20 100644 --- a/Detectors/ITSMFT/ITS/macros/EVE/geom_list_ITS.txt +++ b/Detectors/ITSMFT/ITS/macros/EVE/geom_list_ITS.txt @@ -4,7 +4,7 @@ #/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1 # #/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_0 -/cave_1/barrel_1/barel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_0/ITSUHalfStave0_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_0/ITSUHalfStave0_0 #/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_1 /cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_1/ITSUHalfStave0_0 #/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_2 diff --git a/Detectors/ITSMFT/ITS/macros/EVE/rootlogon.C b/Detectors/ITSMFT/ITS/macros/EVE/rootlogon.C deleted file mode 100644 index 984d84075423f..0000000000000 --- a/Detectors/ITSMFT/ITS/macros/EVE/rootlogon.C +++ /dev/null @@ -1,56 +0,0 @@ -{ - std::cout << "\n *** This is ITS visualisation ! ***\n\n"; - gROOT->LoadMacro("./DisplayEvents.C+g"); - std::vector<std::string> digifiles{ - "data-ep4-link0", - "data-ep4-link1", - "data-ep4-link2", - "data-ep4-link3", - "data-ep4-link4", - "data-ep4-link5", - "data-ep4-link6", - "data-ep4-link7", - "data-ep4-link8", - "data-ep4-link9", - "data-ep4-link10", - "data-ep4-link11", - "data-ep5-link0", - "data-ep5-link1", - "data-ep5-link2", - "data-ep5-link3", - "data-ep5-link4", - "data-ep5-link5", - "data-ep5-link6", - "data-ep5-link7", - "data-ep5-link8", - "data-ep5-link9", - "data-ep5-link10", - "data-ep5-link11"}; - std::vector<std::string> clusfiles{ - "data-ep4-link0.root", - "data-ep4-link1.root", - "data-ep4-link2.root", - "data-ep4-link3.root", - "data-ep4-link4.root", - "data-ep4-link5.root", - "data-ep4-link6.root", - "data-ep4-link7.root", - "data-ep4-link8.root", - "data-ep4-link9.root", - "data-ep4-link10.root", - "data-ep4-link11.root", - "data-ep5-link0.root", - "data-ep5-link1.root", - "data-ep5-link2.root", - "data-ep5-link3.root", - "data-ep5-link4.root", - "data-ep5-link5.root", - "data-ep5-link6.root", - "data-ep5-link7.root", - "data-ep5-link8.root", - "data-ep5-link9.root", - "data-ep5-link10.root", - "data-ep5-link11.root"}; - init(0, 132, digifiles, true, clusfiles); - gEve->GetBrowser()->GetTabRight()->SetTab(1); -} diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index 054e0e3be7e97..656dbe4574438 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -58,29 +58,20 @@ o2_add_test_root_macro(CheckTracks.C O2::DataFormatsITSMFT LABELS its) -#o2_add_test_root_macro(DisplayTrack.C -# PUBLIC_LINK_LIBRARIES O2::ITSBase -# O2::DataFormatsITSMFT -# O2::ITSMFTSimulation -# O2::DataFormatsITS -# O2::MathUtils -# O2::SimulationDataFormat -# LABELS its) +o2_add_test_root_macro(DisplayTrack.C + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::DataFormatsITSMFT + O2::ITSMFTSimulation + O2::DataFormatsITS + O2::MathUtils + O2::SimulationDataFormat + LABELS its) #o2_add_test_root_macro(dictionary_integrity_test.C # PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT # O2::ITSMFTReconstruction # LABELS its) -o2_add_test_root_macro(run_buildTopoDict_its.C - PUBLIC_LINK_LIBRARIES O2::MathUtils - O2::ITSBase - O2::ITSMFTReconstruction - O2::DataFormatsITSMFT - O2::ITSMFTSimulation - O2::SimulationDataFormat - LABELS its) - o2_add_test_root_macro(run_digi2rawVarPage_its.C PUBLIC_LINK_LIBRARIES O2::ITSMFTReconstruction O2::DataFormatsITSMFT diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckCOG.C b/Detectors/ITSMFT/ITS/macros/test/CheckCOG.C deleted file mode 100644 index 052c3f1496790..0000000000000 --- a/Detectors/ITSMFT/ITS/macros/test/CheckCOG.C +++ /dev/null @@ -1,169 +0,0 @@ -/// \file CheckCOG.C -/// Macros to test the generation of a dictionary of topologies. Three dictionaries are generated: one with signal-cluster only, one with noise-clusters only and one with all the clusters. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include <TAxis.h> -#include <TCanvas.h> -#include <TFile.h> -#include <TH1F.h> -#include <TH2F.h> -#include <TNtuple.h> -#include <TString.h> -#include <TStyle.h> -#include <TTree.h> -#include <fstream> -#include <string> - -#include "MathUtils/Utils.h" -#include "ITSBase/GeometryTGeo.h" -#include "DataFormatsITSMFT/Cluster.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "MathUtils/Cartesian3D.h" -#include "DetectorsCommonDataFormats/NameConf.h" - -#endif - -void CheckCOG(std::string clusfile = "o2clus_its.root", std::string inputGeom = "", std::string dictionary_file = "") -{ - gStyle->SetOptStat(0); - using namespace o2::base; - using namespace o2::its; - - using o2::itsmft::Cluster; - using o2::itsmft::CompCluster; - using o2::itsmft::CompClusterExt; - using o2::itsmft::TopologyDictionary; - - if (dictionary_file.empty()) { - dictionary_file = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, "", ".bin"); - } - - // Geometry - o2::base::GeometryManager::loadGeometry(inputGeom); - auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); // request cached transforms - - // Clusters - TFile* file1 = TFile::Open(clusfile.data()); - TTree* clusTree = (TTree*)gFile->Get("o2sim"); - std::vector<CompClusterExt>* compclusArr = nullptr; - clusTree->SetBranchAddress("ITSClusterComp", &compclusArr); - std::vector<unsigned char>* patternsPtr = nullptr; - auto pattBranch = clusTree->GetBranch("ITSClusterPatt"); - if (pattBranch) { - pattBranch->SetAddress(&patternsPtr); - } - - int nevCl = clusTree->GetEntries(); // clusters in cont. readout may be grouped as few events per entry - int ievC = 0; - int lastReadHitEv = -1; - - TopologyDictionary dict; - std::ifstream file(dictionary_file.c_str()); - if (file.good()) { - LOG(INFO) << "Running with dictionary: " << dictionary_file.c_str(); - dict.readBinaryFile(dictionary_file); - } else { - LOG(INFO) << "Running without dictionary !"; - } - - TH1F* hTotalX = new TH1F("hTotalX", "All entries;x_{full} - x_{compact} (#mum);counts", 81, -40.5, 40.5); - TH1F* hTotalZ = new TH1F("hTotalZ", "All entries;z_{full} - z_{compact} (#mum);counts", 81, -40.5, 40.5); - TH1F* hCommonX = new TH1F("hCommonX", "Common topologies;x_{full} - x_{compact} (#mum);counts", 81, -40.5, 40.5); - TH1F* hCommonZ = new TH1F("hCommonZ", "Common topologies;z_{full} - z_{compact} (#mum);counts", 81, -40.5, 40.5); - TH1F* hGroupX = new TH1F("hGroupX", "Groups;x_{full} - x_{compact} (#mum);counts", 81, -40.5, 40.5); - TH1F* hGroupZ = new TH1F("hGroupZ", "Groups;z_{full} - z_{compact} (#mum);counts", 81, -40.5, 40.5); - std::ofstream fOut("COGdebug.txt"); - TCanvas* cTotal = new TCanvas("cTotal", "All topologies"); - cTotal->Divide(2, 1); - TCanvas* cCommon = new TCanvas("cCommon", "Common topologies"); - cCommon->Divide(2, 1); - TCanvas* cGroup = new TCanvas("cGroup", "Gourps of rare topologies"); - cGroup->Divide(2, 1); - for (ievC = 0; ievC < nevCl; ievC++) { - clusTree->GetEvent(ievC); - int nc = compclusArr->size(); - printf("processing cluster event %d\n", ievC); - - std::vector<unsigned char>::const_iterator pattIdx; - if (patternsPtr) - pattIdx = patternsPtr->begin(); - for (int i = 0; i < nc; i++) { - // cluster is in tracking coordinates always - Cluster& c = (*clusArr)[i]; - const auto locC = c.getXYZLoc(*gman); // convert from tracking to local frame - - float dx, dz; - Point3D<float> locComp; - CompClusterExt& cComp = (*compclusArr)[i]; - auto pattID = cComp.getPatternID(); - fOut << Form("groupID: %d\n", pattID); - if (pattID != CompCluster::InvalidPatternID) { - Point3D<float> locComp = dict.getClusterCoordinates(cComp); - - float xComp = locComp.X(); - float zComp = locComp.Z(); - - dx = (locC.X() - xComp) * 10000; - dz = (locC.Z() - zComp) * 10000; - - hTotalX->Fill(dx); - hTotalZ->Fill(dz); - - fOut << Form("groupID: %d\n", cComp.getPatternID()); - fOut << Form("is group: %o\n", dict.isGroup(cComp.getPatternID())); - fOut << Form("x_full: %.4f x_comp: %.4f dx: %.4f x_shift: %.4f\n", locC.X(), xComp, dx / 10000, dict.getXCOG(cComp.getPatternID())); - fOut << Form("z_full: %.4f z_comp: %.4f dZ: %.4f z_shift: %.4f\n", locC.Z(), zComp, dz / 10000, dict.getZCOG(cComp.getPatternID())); - fOut << dict.getPattern(cComp.getPatternID()); - fOut << Form("***************************************\n"); - - if (dict.isGroup(cComp.getPatternID())) { - if (patternsPtr) { // Restore the full pixel pattern information from the auxiliary branch - o2::itsmft::ClusterPattern patt(pattIdx); - auto locCl = dict.getClusterCoordinates(cComp, patt); - dx = (locC.X() - locCl.X()) * 10000; - dz = (locC.Z() - locCl.Z()) * 10000; - } - hGroupX->Fill(dx); - hGroupZ->Fill(dz); - } else { - hCommonX->Fill(dx); - hCommonZ->Fill(dz); - } - } - } - } - TFile output_file("COG_diff.root", "recreate"); - hTotalX->Write(); - hTotalZ->Write(); - hGroupX->Write(); - hGroupZ->Write(); - hCommonX->Write(); - hCommonZ->Write(); - cTotal->cd(1); - gPad->SetLogy(); - hTotalX->Draw(); - cTotal->cd(2); - gPad->SetLogy(); - hTotalZ->Draw(); - cTotal->Print("cTotal.pdf"); - cCommon->cd(1); - gPad->SetLogy(); - hCommonX->Draw(); - cCommon->cd(2); - gPad->SetLogy(); - hCommonZ->Draw(); - cCommon->Print("cCommon.pdf"); - cGroup->cd(1); - gPad->SetLogy(); - hGroupX->Draw(); - cGroup->cd(2); - gPad->SetLogy(); - hGroupZ->Draw(); - cGroup->Print("cGroup.pdf"); - cTotal->Write(); - cCommon->Write(); - cGroup->Write(); -} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C b/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C index e926af4580898..f311e4268a30c 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C @@ -15,7 +15,7 @@ #include "DataFormatsITSMFT/TopologyDictionary.h" #include "ITSMFTSimulation/Hit.h" #include "DataFormatsITSMFT/ROFRecord.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -48,8 +48,8 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile // Geometry o2::base::GeometryManager::loadGeometry(inputGeom); auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); // request cached transforms + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // request cached transforms // Hits TFile fileH(hitfile.data()); @@ -149,7 +149,7 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile int npix = 0; auto pattID = cluster.getPatternID(); - Point3D<float> locC; + o2::math_utils::Point3D<float> locC; if (pattID == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattID)) { o2::itsmft::ClusterPattern patt(pattIt); locC = dict.getClusterCoordinates(cluster, patt); @@ -180,7 +180,7 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile // float dx = 0, dz = 0; int ievH = lab.getEventID(); - Point3D<float> locH, locHsta; + o2::math_utils::Point3D<float> locH, locHsta; // mean local position of the hit locH = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C b/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C index 810c593ff95d0..9fa3d741f43ef 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C @@ -41,7 +41,7 @@ void CheckDigits(std::string digifile = "itsdigits.root", std::string hitfile = // Geometry o2::base::GeometryManager::loadGeometry(inputGeom); auto* gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::L2G)); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); SegmentationAlpide seg; @@ -146,7 +146,7 @@ void CheckDigits(std::string digifile = "itsdigits.root", std::string hitfile = seg.detectorToLocal(ix, iz, x, z); - const Point3D<float> locD(x, 0., z); + const o2::math_utils::Point3D<float> locD(x, 0., z); Int_t chipID = (*digArr)[iDigit].getChipIndex(); auto lab = (labels->getLabels(iDigit))[0]; diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckLUtime.C b/Detectors/ITSMFT/ITS/macros/test/CheckLUtime.C index fdfcabfe8a22d..7c4096d7286c6 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckLUtime.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckLUtime.C @@ -1,5 +1,5 @@ /// \file CheckLUtime.C -/// \brief Macro to measure the time necessaty for the identification of the topology IDs of the clusters generated in an event. A dictionary of topologies must be provided as input. +/// \brief Macro to measure the time necessaty for the identification of the topology IDs of the clusters generated in an event. A dictionary of topologies must be provided as input. An input file with the pattern for ALL the clusters must be provuded. #if !defined(__CLING__) || defined(__ROOTCLING__) #include <TAxis.h> @@ -16,8 +16,9 @@ #include "TStopwatch.h" #include "ITSMFTReconstruction/LookUp.h" -#include "DataFormatsITSMFT/Cluster.h" +#include "DataFormatsITSMFT/ClusterPattern.h" #include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/NameConf.h" #endif @@ -26,13 +27,24 @@ using namespace std; void CheckLUtime(std::string clusfile = "o2clus_its.root", std::string dictfile = "") { - using o2::itsmft::Cluster; + using o2::itsmft::ClusterPattern; using o2::itsmft::CompClusterExt; + using o2::itsmft::CompCluster; using o2::itsmft::LookUp; + using ROFRec = o2::itsmft::ROFRecord; + + TStopwatch sw; + sw.Start(); if (dictfile.empty()) { dictfile = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, "", ".bin"); } + std::ifstream file(dictfile.c_str()); + if (file.good()) { + LOG(INFO) << "Running with dictionary: " << dictfile.c_str(); + } else { + LOG(INFO) << "Running without dictionary !"; + } LookUp finder(dictfile.c_str()); ofstream time_output("time.txt"); @@ -40,11 +52,9 @@ void CheckLUtime(std::string clusfile = "o2clus_its.root", std::string dictfile realtime.open("realtime.txt", std::ofstream::out | std::ofstream::app); cputime.open("cputime.txt", std::ofstream::out | std::ofstream::app); - TStopwatch timerLookUp; - // Clusters - TFile* file1 = TFile::Open(clusfile.data()); - TTree* clusTree = (TTree*)gFile->Get("o2sim"); + TFile* fileCl = TFile::Open(clusfile.data()); + TTree* clusTree = (TTree*)fileCl->Get("o2sim"); std::vector<CompClusterExt>* clusArr = nullptr; clusTree->SetBranchAddress("ITSClusterComp", &clusArr); std::vector<unsigned char>* patternsPtr = nullptr; @@ -53,19 +63,33 @@ void CheckLUtime(std::string clusfile = "o2clus_its.root", std::string dictfile pattBranch->SetAddress(&patternsPtr); } - Int_t nevCl = clusTree->GetEntries(); // clusters in cont. readout may be grouped as few events per entry - int ievC = 0, ievH = 0; - - for (ievC = 0; ievC < nevCl; ievC++) { - clusTree->GetEvent(ievC); - Int_t nc = clusArr->size(); - printf("processing cluster event %d\n", ievC); - bool restart = false; - restart = (ievC == 0) ? true : false; - timerLookUp.Start(restart); - auto pattIdx = patternsPtr->cbegin(); - for (int i = 0; i < nc; i++) { - CompClusterExt& c = (*clusArr)[i]; + // ROFrecords + std::vector<ROFRec> rofRecVec, *rofRecVecP = &rofRecVec; + clusTree->SetBranchAddress("ITSClustersROF", &rofRecVecP); + clusTree->GetEntry(0); + int nROFRec = (int)rofRecVec.size(); + + auto pattIdx = patternsPtr->cbegin(); + + int nClusters = 0; + + for (int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = rofRecVec[irof]; + + rofRec.print(); + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + nClusters++; + int clEntry = rofRec.getFirstEntry() + icl; // entry of icl-th cluster of this ROF in the vector of clusters + // do we read MC data? + + const auto& cluster = (*clusArr)[clEntry]; + + if (cluster.getPatternID() != CompCluster::InvalidPatternID) { + LOG(WARNING) << "Clusters have already been generated with a dictionary! Quitting"; + return; + } + auto rowSpan = *pattIdx++; auto columnSpan = *pattIdx++; int nBytes = (rowSpan * columnSpan) >> 3; @@ -77,12 +101,12 @@ void CheckLUtime(std::string clusfile = "o2clus_its.root", std::string dictfile } finder.findGroupID(rowSpan, columnSpan, patt); } - timerLookUp.Stop(); } - realtime << timerLookUp.RealTime() / nevCl << std::endl; + sw.Stop(); + realtime << sw.RealTime() / nClusters << std::endl; realtime.close(); - cputime << timerLookUp.CpuTime() / nevCl << std::endl; + cputime << sw.CpuTime() / nClusters << std::endl; cputime.close(); - time_output << "Real time (s): " << timerLookUp.RealTime() / nevCl << "CPU time (s): " << timerLookUp.CpuTime() / nevCl << std::endl; - std::cout << "Real time (s): " << timerLookUp.RealTime() / nevCl << " CPU time (s): " << timerLookUp.CpuTime() / nevCl << std::endl; + time_output << "Real time (s): " << sw.RealTime() / nClusters << "CPU time (s): " << sw.CpuTime() / nClusters << std::endl; + std::cout << "Real time (s): " << sw.RealTime() / nClusters << " CPU time (s): " << sw.CpuTime() / nClusters << std::endl; } diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTopologies.C b/Detectors/ITSMFT/ITS/macros/test/CheckTopologies.C index fe1fc4381a44b..51597f8b66709 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTopologies.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTopologies.C @@ -21,63 +21,88 @@ #include "ITSMFTReconstruction/BuildTopologyDictionary.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ClusterTopology.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTSimulation/Hit.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "DetectorsCommonDataFormats/NameConf.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "Framework/Logger.h" #include <unordered_map> - #endif -void CheckTopologies(std::string clusfile = "o2clus_its.root", std::string hitfile = "o2sim_HitsITS.root", std::string inputGeom = "") +void CheckTopologies(std::string clusfile = "o2clus_its.root", + std::string hitfile = "o2sim_HitsITS.root", + std::string collContextfile = "collisioncontext.root", + std::string inputGeom = "", + float checkOutliers = 2., // reject outliers (MC dX or dZ exceeds row/col span by a factor above the threshold) + float minPtMC = 0.01) // account only MC hits with pT above threshold { + const int QEDSourceID = 99; // Clusters from this MC source correspond to QED electrons + using namespace o2::base; using namespace o2::its; using o2::itsmft::BuildTopologyDictionary; using o2::itsmft::ClusterTopology; + using o2::itsmft::CompCluster; using o2::itsmft::CompClusterExt; using o2::itsmft::Hit; using ROFRec = o2::itsmft::ROFRecord; using MC2ROF = o2::itsmft::MC2ROFRecord; using HitVec = std::vector<Hit>; using MC2HITS_map = std::unordered_map<uint64_t, int>; // maps (track_ID<<16 + chip_ID) to entry in the hit vector - + std::unordered_map<int, int> hadronicMCMap; // mapping from MC event entry to hadronic event ID std::vector<HitVec*> hitVecPool; std::vector<MC2HITS_map> mc2hitVec; - - const int QEDSourceID = 99; // Clusters from this MC source correspond to QED electrons - + const o2::steer::DigitizationContext* digContext = nullptr; TStopwatch sw; sw.Start(); - - std::ofstream output_check("check_topologies.txt"); - + float minPtMC2 = minPtMC > 0 ? minPtMC * minPtMC : -1; // Geometry o2::base::GeometryManager::loadGeometry(inputGeom); auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); // request cached transforms + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // request cached transforms // Hits TFile* fileH = nullptr; TTree* hitTree = nullptr; - std::vector<Hit>* hitArray = nullptr; - if (!hitfile.empty() && !gSystem->AccessPathName(hitfile.c_str())) { + if (!hitfile.empty() && !collContextfile.empty() && !gSystem->AccessPathName(hitfile.c_str()) && !gSystem->AccessPathName(collContextfile.c_str())) { fileH = TFile::Open(hitfile.data()); hitTree = (TTree*)fileH->Get("o2sim"); - hitTree->SetBranchAddress("ITSHit", &hitArray); mc2hitVec.resize(hitTree->GetEntries()); hitVecPool.resize(hitTree->GetEntries(), nullptr); + digContext = o2::steer::DigitizationContext::loadFromFile(collContextfile); + + auto& intGlo = digContext->getEventParts(digContext->isQEDProvided()); + int hadrID = -1, nGlo = intGlo.size(), nHadro = 0; + for (int iglo = 0; iglo < nGlo; iglo++) { + const auto& parts = intGlo[iglo]; + bool found = false; + for (auto& part : parts) { + if (part.sourceID == 0) { // we use underlying background + hadronicMCMap[iglo] = part.entryID; + found = true; + nHadro++; + break; + } + } + if (!found) { + hadronicMCMap[iglo] = -1; + } + } + if (nHadro < hitTree->GetEntries()) { + LOG(FATAL) << "N=" << nHadro << " hadronic events < " + << " N=" << hitTree->GetEntries() << " Hit enties."; + } } // Clusters - TFile* FileCl = TFile::Open(clusfile.data()); - TTree* clusTree = (TTree*)FileCl->Get("o2sim"); + TFile* fileCl = TFile::Open(clusfile.data()); + TTree* clusTree = (TTree*)fileCl->Get("o2sim"); std::vector<CompClusterExt>* clusArr = nullptr; clusTree->SetBranchAddress("ITSClusterComp", &clusArr); std::vector<unsigned char>* patternsPtr = nullptr; @@ -99,108 +124,125 @@ void CheckTopologies(std::string clusfile = "o2clus_its.root", std::string hitfi } clusTree->GetEntry(0); - Int_t nevCl = clusTree->GetEntries(); // clusters in cont. readout may be grouped as few events per entry - Int_t nevH = 0; // hits are stored as one event per entry - if (hitTree) { - nevH = hitTree->GetEntries(); - } - int ievC = 0, ievH = 0; - int lastReadHitEv = -1; - // Topologies dictionaries: 1) all clusters 2) signal clusters only 3) noise clusters only BuildTopologyDictionary completeDictionary; BuildTopologyDictionary signalDictionary; BuildTopologyDictionary noiseDictionary; - for (ievC = 0; ievC < nevCl; ievC++) { - clusTree->GetEvent(ievC); - - int nROFRec = (int)rofRecVec.size(); - std::vector<int> mcEvMin(nROFRec, hitTree ? hitTree->GetEntries() : 0), mcEvMax(nROFRec, -1); + int nROFRec = (int)rofRecVec.size(); + std::vector<int> mcEvMin, mcEvMax; - if (clusLabArr) { // >> build min and max MC events used by each ROF - for (int imc = mc2rofVec.size(); imc--;) { - const auto& mc2rof = mc2rofVec[imc]; - if (mc2rof.rofRecordID < 0) { - continue; // this MC event did not contribute to any ROF + if (clusLabArr) { // >> build min and max MC events used by each ROF + mcEvMin.resize(nROFRec, hitTree->GetEntries()); + mcEvMax.resize(nROFRec, -1); + for (int imc = mc2rofVec.size(); imc--;) { + int hadrID = hadronicMCMap[imc]; + if (hadrID < 0) { + continue; + } + const auto& mc2rof = mc2rofVec[imc]; + if (mc2rof.rofRecordID < 0) { + continue; // this MC event did not contribute to any ROF + } + for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + int irof = mc2rof.rofRecordID + irfd; + if (mcEvMin[irof] > hadrID) { + mcEvMin[irof] = hadrID; } - for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { - int irof = mc2rof.rofRecordID + irfd; - if (mcEvMin[irof] > imc) { - mcEvMin[irof] = imc; - } - if (mcEvMax[irof] < imc) { - mcEvMax[irof] = imc; - } + if (mcEvMax[irof] < hadrID) { + mcEvMax[irof] = hadrID; } } - } // << build min and max MC events used by each ROF + } + } // << build min and max MC events used by each ROF - auto pattIdx = patternsPtr->cbegin(); + auto pattIdx = patternsPtr->cbegin(); + for (int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = rofRecVec[irof]; - for (int irof = 0; irof < nROFRec; irof++) { - const auto& rofRec = rofRecVec[irof]; - rofRec.print(); - if (clusLabArr) { // >> read and map MC events contributing to this ROF - for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { - if (!hitVecPool[im]) { - hitTree->SetBranchAddress("ITSHit", &hitVecPool[im]); - hitTree->GetEntry(im); - auto& mc2hit = mc2hitVec[im]; - const auto* hitArray = hitVecPool[im]; - for (int ih = hitArray->size(); ih--;) { - const auto& hit = (*hitArray)[ih]; - uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); - mc2hit.emplace(key, ih); - } + rofRec.print(); + + if (clusLabArr) { // >> read and map MC events contributing to this ROF + for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { + if (!hitVecPool[im]) { + hitTree->SetBranchAddress("ITSHit", &hitVecPool[im]); + hitTree->GetEntry(im); + auto& mc2hit = mc2hitVec[im]; + const auto* hitArray = hitVecPool[im]; + for (int ih = hitArray->size(); ih--;) { + const auto& hit = (*hitArray)[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); } } - } // << cache MC events contributing to this ROF + } + } // << cache MC events contributing to this ROF + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + int clEntry = rofRec.getFirstEntry() + icl; // entry of icl-th cluster of this ROF in the vector of clusters + // do we read MC data? - for (int icl = 0; icl < rofRec.getNEntries(); icl++) { - int clEntry = rofRec.getFirstEntry() + icl; // entry of icl-th cluster of this ROF in the vector of clusters - // do we read MC data? - const auto& cluster = (*clusArr)[clEntry]; - ClusterTopology topology; - o2::itsmft::ClusterPattern pattern(pattIdx); - topology.setPattern(pattern); - //output_check << "iEv: " << ievC << " / " << nevCl << " iCl: " << clEntry << " / " << clusArr->size() << std::endl; - // output_check << topology << std::endl; + const auto& cluster = (*clusArr)[clEntry]; - const auto locC = o2::itsmft::TopologyDictionary::getClusterCoordinates(cluster, pattern); - float dx = BuildTopologyDictionary::IgnoreVal * 2, dz = BuildTopologyDictionary::IgnoreVal * 2; // to not use unassigned dx,dy - if (clusLabArr) { - const auto& lab = (clusLabArr->getLabels(clEntry))[0]; - auto srcID = lab.getSourceID(); - if (lab.isValid() && srcID != QEDSourceID) { // use MC truth info only for non-QED and non-noise clusters - int trID = lab.getTrackID(); - const auto& mc2hit = mc2hitVec[lab.getEventID()]; - const auto* hitArray = hitVecPool[lab.getEventID()]; - Int_t chipID = cluster.getSensorID(); - uint64_t key = (uint64_t(trID) << 32) + chipID; - auto hitEntry = mc2hit.find(key); - if (hitEntry != mc2hit.end()) { - const auto& hit = (*hitArray)[hitEntry->second]; + if (cluster.getPatternID() != CompCluster::InvalidPatternID) { + LOG(WARNING) << "Encountered patternID = " << cluster.getPatternID() << " != " << CompCluster::InvalidPatternID; + LOG(WARNING) << "Clusters have already been generated with a dictionary! Quitting"; + return; + } + + ClusterTopology topology; + o2::itsmft::ClusterPattern pattern(pattIdx); + topology.setPattern(pattern); + + float dX = BuildTopologyDictionary::IgnoreVal, dZ = BuildTopologyDictionary::IgnoreVal; + if (clusLabArr) { + const auto& lab = (clusLabArr->getLabels(clEntry))[0]; + auto srcID = lab.getSourceID(); + if (lab.isValid() && srcID != QEDSourceID) { // use MC truth info only for non-QED and non-noise clusters + auto trID = lab.getTrackID(); + const auto& mc2hit = mc2hitVec[lab.getEventID()]; + const auto* hitArray = hitVecPool[lab.getEventID()]; + Int_t chipID = cluster.getSensorID(); + uint64_t key = (uint64_t(trID) << 32) + chipID; + auto hitEntry = mc2hit.find(key); + if (hitEntry != mc2hit.end()) { + const auto& hit = (*hitArray)[hitEntry->second]; + if (minPtMC < 0.f || hit.GetMomentum().Perp2() > minPtMC2) { auto locH = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local auto locHsta = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); - const auto locC = o2::itsmft::TopologyDictionary::getClusterCoordinates(cluster, pattern); - dx = locH.X() - locC.X(); - dz = locH.Z() - locC.Z(); - } else { - printf("Failed to find MC hit entry for Tr:%d chipID:%d\n", trID, chipID); - continue; + const auto locC = o2::itsmft::TopologyDictionary::getClusterCoordinates(cluster, pattern, false); + dX = locH.X() - locC.X(); + dZ = locH.Z() - locC.Z(); + if (checkOutliers > 0.) { + if (std::abs(dX) > topology.getRowSpan() * o2::itsmft::SegmentationAlpide::PitchRow * checkOutliers || + std::abs(dZ) > topology.getColumnSpan() * o2::itsmft::SegmentationAlpide::PitchCol * checkOutliers) { // ignore outlier + dX = dZ = BuildTopologyDictionary::IgnoreVal; + } + } } - signalDictionary.accountTopology(topology, dx, dz); } else { - noiseDictionary.accountTopology(topology, dx, dz); + printf("Failed to find MC hit entry for Tr:%d chipID:%d\n", trID, chipID); + lab.print(); } + signalDictionary.accountTopology(topology, dX, dZ); + } else { + noiseDictionary.accountTopology(topology, dX, dZ); } - completeDictionary.accountTopology(topology, dx, dz); - } // Loop over clusters of a single ROF - } // loop over ROFs - } // loop over eventually multiple entries (TFs) - + } + completeDictionary.accountTopology(topology, dX, dZ); + } + // clean MC cache for events which are not needed anymore + if (clusLabArr) { + int irfNext = irof; + int limMC = irfNext == nROFRec ? hitVecPool.size() : mcEvMin[irfNext]; // can clean events up to this + for (int imc = mcEvMin[irof]; imc < limMC; imc++) { + delete hitVecPool[imc]; + hitVecPool[imc] = nullptr; + mc2hitVec[imc].clear(); + } + } + } auto dID = o2::detectors::DetID::ITS; completeDictionary.setThreshold(0.0001); @@ -219,6 +261,7 @@ void CheckTopologies(std::string clusfile = "o2clus_its.root", std::string hitfi hComplete->Draw("hist"); hComplete->Write(); cComplete->Write(); + TCanvas* cNoise = nullptr; TCanvas* cSignal = nullptr; TH1F* hNoise = nullptr; @@ -253,6 +296,7 @@ void CheckTopologies(std::string clusfile = "o2clus_its.root", std::string hitfi histogramOutput.cd(); hSignal->Write(); cSignal->Write(); + sw.Stop(); + sw.Print(); } - sw.Print(); } diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C index fd7a50706db82..27f382b289e36 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C @@ -103,7 +103,7 @@ void CheckTracks(std::string tracfile = "o2trac_its.root", std::string clusfile std::vector<TrackITS>* recArr = nullptr; recTree->SetBranchAddress("ITSTrack", &recArr); // Track MC labels - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* trkLabArr = nullptr; + std::vector<o2::MCCompLabel>* trkLabArr = nullptr; recTree->SetBranchAddress("ITSTrackMCTruth", &trkLabArr); Int_t lastEventIDcl = -1, cf = 0; @@ -154,7 +154,7 @@ void CheckTracks(std::string tracfile = "o2trac_its.root", std::string clusfile continue; int loadedEventTracks = frame; for (unsigned int i = 0; i < recArr->size(); i++) { // Find the last MC event within this reconstructed entry - auto lab = (trkLabArr->getLabels(i))[0]; + auto lab = (*trkLabArr)[i]; if (!lab.isValid()) { const TrackITS& recTrack = (*recArr)[i]; fak->Fill(recTrack.getPt()); @@ -210,7 +210,7 @@ void CheckTracks(std::string tracfile = "o2trac_its.root", std::string clusfile long lastIndex = (frame == f.lastFrame) ? f.lastIndex : nentr - 1; for (long i = firstIndex; i <= lastIndex; i++) { - auto lab = (trkLabArr->getLabels(i))[0]; + auto lab = (*trkLabArr)[i]; if (!lab.isValid() || lab.getSourceID() != 0 || lab.getEventID() != n) continue; int mcid = lab.getTrackID(); diff --git a/Detectors/ITSMFT/ITS/macros/test/DisplayTrack.C b/Detectors/ITSMFT/ITS/macros/test/DisplayTrack.C index 91493769a5746..e4d47cd3666f9 100644 --- a/Detectors/ITSMFT/ITS/macros/test/DisplayTrack.C +++ b/Detectors/ITSMFT/ITS/macros/test/DisplayTrack.C @@ -17,18 +17,21 @@ #include <TTree.h> #include "ITSBase/GeometryTGeo.h" -#include "DataFormatsITSMFT/Cluster.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" #include "ITSMFTSimulation/Hit.h" +#include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITS/TrackITS.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsCommonDataFormats/NameConf.h" #endif using namespace std; -void DisplayTrack(Int_t event = 0, Int_t track = 0, std::string tracfile = "o2trac_its.root", std::string clusfile = "o2clus_its.root", std::string hitfile = "o2sim_HitsITS.root", std::string inputGeom = "") +void DisplayTrack(Int_t event = 0, Int_t track = 0, std::string tracfile = "o2trac_its.root", std::string clusfile = "o2clus_its.root", std::string hitfile = "o2sim_HitsITS.root", std::string inputGeom = "", std::string dictfile = "") { using namespace o2::base; using namespace o2::its; @@ -47,21 +50,24 @@ void DisplayTrack(Int_t event = 0, Int_t track = 0, std::string tracfile = "o2tr o2::base::GeometryManager::loadGeometry(inputGeom); } - gGeoManager->GetVolume("obSuppCyl")->SetInvisible(); - gGeoManager->GetVolume("ibSuppCyl")->SetInvisible(); - gGeoManager->GetVolume("ITSUStave0_StaveStruct")->SetInvisible(); - gGeoManager->GetVolume("ITSUStave1_StaveStruct")->SetInvisible(); - gGeoManager->GetVolume("ITSUStave2_StaveStruct")->SetInvisible(); + gGeoManager->GetVolume("IBCYSSCylinderFoam")->SetInvisible(); gGeoManager->GetVolume("ITSUHalfStave0")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave0")->SetLineColor(2); gGeoManager->GetVolume("ITSUHalfStave1")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave1")->SetLineColor(2); gGeoManager->GetVolume("ITSUHalfStave2")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave2")->SetLineColor(2); gGeoManager->GetVolume("ITSUHalfStave3")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave3")->SetLineColor(2); gGeoManager->GetVolume("ITSUHalfStave4")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave4")->SetLineColor(2); gGeoManager->GetVolume("ITSUHalfStave5")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave5")->SetLineColor(2); gGeoManager->GetVolume("ITSUHalfStave6")->SetTransparency(50); + gGeoManager->GetVolume("ITSUHalfStave6")->SetLineColor(2); - TGeoNode* tnode = gGeoManager->GetTopVolume()->FindNode("ITSV_2"); + TGeoNode* tnode = gGeoManager->GetVolume("barrel")->FindNode("ITSV_2"); TEveGeoTopNode* evenode = new TEveGeoTopNode(gGeoManager, tnode); evenode->SetVisLevel(4); gEve->AddGlobalElement(evenode); @@ -98,11 +104,10 @@ void DisplayTrack(Int_t event = 0, Int_t track = 0, std::string tracfile = "o2tr tree->GetEvent(event); - Int_t nc = hitArr->size(), n = 0; - while (nc--) { - Hit& c = (*hitArr)[nc]; - if (c.GetTrackID() == track) { - points->SetNextPoint(c.GetX(), c.GetY(), c.GetZ()); + Int_t n = 0; + for (const auto& h : *hitArr) { + if (h.GetTrackID() == track) { + points->SetNextPoint(h.GetX(), h.GetY(), h.GetZ()); n++; } } @@ -122,40 +127,59 @@ void DisplayTrack(Int_t event = 0, Int_t track = 0, std::string tracfile = "o2tr points = new TEvePointSet(s.data()); points->SetMarkerColor(kMagenta); - std::vector<Cluster>* clusArr = nullptr; - tree->SetBranchAddress("ITSCluster", &clusArr); + std::vector<o2::itsmft::CompClusterExt>* clusArr = nullptr; + tree->SetBranchAddress("ITSClusterComp", &clusArr); + if (dictfile.empty()) { + dictfile = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, "", ".bin"); + } + o2::itsmft::TopologyDictionary dict; + std::ifstream file(dictfile.c_str()); + if (file.good()) { + LOG(INFO) << "Running with dictionary: " << dictfile.c_str(); + dict.readBinaryFile(dictfile); + } else { + LOG(INFO) << "Cannot run without the dictionary !"; + return; + } + + // ROFrecords + std::vector<o2::itsmft::ROFRecord>* rofRecVecP = nullptr; + tree->SetBranchAddress("ITSClustersROF", &rofRecVecP); + // Cluster MC labels o2::dataformats::MCTruthContainer<o2::MCCompLabel>* clsLabArr = nullptr; tree->SetBranchAddress("ITSClusterMCTruth", &clsLabArr); - int tf = 0; - int lastTF = tree->GetEntries(); - for (; tf < lastTF; ++tf) { - tree->GetEvent(tf); - int nc = clusArr->size(); - for (int i = 0; i < nc; i++) { // Find the TF containing this MC event - auto mclab = (clsLabArr->getLabels(i))[0]; + tree->GetEvent(0); + int nc = clusArr->size(); + int offset = 0; + for (auto& rof : *rofRecVecP) { + offset = rof.getFirstEntry(); + for (int i = 0; i < rof.getNEntries(); i++) { // Find the TF containing this MC event + auto mclab = (clsLabArr->getLabels(offset + i))[0]; auto id = mclab.getEventID(); if (id == event) goto found; } } - std::cout << "Time Frame containing the MC event " << event << " was not found" << std::endl; + std::cout << "RO frame containing the MC event " << event << " was not found" << std::endl; found: - std::cout << "MC event " << event << " found in the Time Frame #" << tf << std::endl; o2::its::GeometryTGeo* gman = GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2GRot)); // request cached transforms + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); // request cached transforms nc = clusArr->size(); n = 0; - while (nc--) { - Cluster& c = (*clusArr)[nc]; - auto lab = (clsLabArr->getLabels(nc))[0]; - auto gloC = c.getXYZGloRot(*gman); // convert from tracking to global frame + for (int i = 0; i < nc; i++) { + const auto& c = (*clusArr)[i]; + auto lab = (clsLabArr->getLabels(i))[0]; if (lab.getEventID() != event) continue; if (lab.getTrackID() == track) { + auto pattID = c.getPatternID(); + auto locC = dict.getClusterCoordinates(c); + auto chipID = c.getSensorID(); + auto gloC = gman->getMatrixL2G(chipID) * locC; points->SetNextPoint(gloC.X(), gloC.Y(), gloC.Z()); n++; } @@ -181,16 +205,16 @@ found: tree->SetBranchAddress("ITSTrack", &trkArr); tree->SetBranchAddress("ITSTrackClusIdx", &clIdx); // Track MC labels - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* trkLabArr = nullptr; + std::vector<o2::MCCompLabel>* trkLabArr = nullptr; tree->SetBranchAddress("ITSTrackMCTruth", &trkLabArr); - tree->GetEvent(tf); + tree->GetEvent(0); Int_t nt = trkArr->size(); n = 0; while (nt--) { const TrackITS& t = (*trkArr)[nt]; - auto lab = (trkLabArr->getLabels(nt))[0]; + auto lab = (*trkLabArr)[nt]; if (TMath::Abs(lab.getEventID()) != event) continue; if (TMath::Abs(lab.getTrackID()) != track) @@ -198,9 +222,11 @@ found: Int_t nc = t.getNumberOfClusters(); int idxRef = t.getFirstClusterEntry(); while (n < nc) { - Int_t idx = (*clIdx)[idxRef++]; - Cluster& c = (*clusArr)[idx]; - auto gloC = c.getXYZGloRot(*gman); // convert from tracking to global frame + Int_t idx = (*clIdx)[idxRef + n]; + auto& c = (*clusArr)[offset + idx]; + auto locC = dict.getClusterCoordinates(c); + auto chipID = c.getSensorID(); + auto gloC = gman->getMatrixL2G(chipID) * locC; points->SetNextPoint(gloC.X(), gloC.Y(), gloC.Z()); n++; } diff --git a/Detectors/ITSMFT/ITS/macros/test/dictionary_integrity_test.C b/Detectors/ITSMFT/ITS/macros/test/dictionary_integrity_test.C index f7ee3557dd3ba..c78e9cdd8b99d 100644 --- a/Detectors/ITSMFT/ITS/macros/test/dictionary_integrity_test.C +++ b/Detectors/ITSMFT/ITS/macros/test/dictionary_integrity_test.C @@ -5,14 +5,12 @@ #include <TFile.h> #include <string> #include <fstream> -#include "DataFormatsITSMFT/Cluster.h" #include "DataFormatsITSMFT/ClusterPattern.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "ITSMFTReconstruction/LookUp.h" #include "DetectorsCommonDataFormats/NameConf.h" #endif -using o2::itsmft::Cluster; using o2::itsmft::ClusterPattern; using o2::itsmft::LookUp; using o2::itsmft::TopologyDictionary; diff --git a/Detectors/ITSMFT/ITS/macros/test/rootlogon.C b/Detectors/ITSMFT/ITS/macros/test/rootlogon.C deleted file mode 100644 index 0ac3dadc99efa..0000000000000 --- a/Detectors/ITSMFT/ITS/macros/test/rootlogon.C +++ /dev/null @@ -1,15 +0,0 @@ -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include <iostream> -#include "TSystem.h" -#endif - -void rootlogon() -{ - cout << endl; - cout << endl; - cout << "ATTENTION: ./rootlogon.C has been used !\n"; - cout << endl; - cout << endl; - - gSystem->SetIncludePath("-I$O2_ROOT/../../ms_gsl/latest/include -I$O2_ROOT/../../FairLogger/latest/include -I$O2_ROOT/../../FairMQ/latest/include/fairmq"); -} diff --git a/Detectors/ITSMFT/ITS/macros/test/run_buildTopoDict_its.C b/Detectors/ITSMFT/ITS/macros/test/run_buildTopoDict_its.C deleted file mode 100644 index 1bd3a9eda500b..0000000000000 --- a/Detectors/ITSMFT/ITS/macros/test/run_buildTopoDict_its.C +++ /dev/null @@ -1,216 +0,0 @@ -/// \file run_buildTopoDict_its -/// Macros to generate dictionary of topologies. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include <TAxis.h> -#include <TCanvas.h> -#include <TFile.h> -#include <TH1F.h> -#include <TH2F.h> -#include <TNtuple.h> -#include <TString.h> -#include <TStyle.h> -#include <TTree.h> -#include <TStopwatch.h> -#include <TSystem.h> -#include <fstream> -#include <string> - -#include "MathUtils/Utils.h" -#include "ITSBase/GeometryTGeo.h" -#include "ITSMFTReconstruction/BuildTopologyDictionary.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ClusterTopology.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "ITSMFTSimulation/Hit.h" -#include "MathUtils/Cartesian3D.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include <unordered_map> -#include "DetectorsCommonDataFormats/NameConf.h" -#endif - -/// Build dictionary of topologies from the root file with compact clusters -/// If the hitfile is non-empty, the mean bias between the cluster COG -/// and mean MC hit position is calculated - -void run_buildTopoDict_its(std::string clusfile = "o2clus_its.root", - std::string hitfile = "o2sim_HitsITS.root", - std::string inputGeom = "") -{ - const int QEDSourceID = 99; // Clusters from this MC source correspond to QED electrons - - using namespace o2::base; - using namespace o2::its; - - using o2::itsmft::BuildTopologyDictionary; - using o2::itsmft::ClusterTopology; - using o2::itsmft::CompClusterExt; - using o2::itsmft::Hit; - using ROFRec = o2::itsmft::ROFRecord; - using MC2ROF = o2::itsmft::MC2ROFRecord; - using HitVec = std::vector<Hit>; - using MC2HITS_map = std::unordered_map<uint64_t, int>; // maps (track_ID<<16 + chip_ID) to entry in the hit vector - - std::vector<HitVec*> hitVecPool; - std::vector<MC2HITS_map> mc2hitVec; - - TStopwatch sw; - sw.Start(); - - // Geometry - o2::base::GeometryManager::loadGeometry(inputGeom); - auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); // request cached transforms - - // Hits if requested - TFile* fileH = nullptr; - TTree* hitTree = nullptr; - - if (!hitfile.empty() && !gSystem->AccessPathName(hitfile.c_str())) { - fileH = TFile::Open(hitfile.data()); - hitTree = (TTree*)fileH->Get("o2sim"); - mc2hitVec.resize(hitTree->GetEntries()); - hitVecPool.resize(hitTree->GetEntries(), nullptr); - } - - // Clusters - TFile* fileCl = TFile::Open(clusfile.data()); - TTree* clusTree = (TTree*)fileCl->Get("o2sim"); - std::vector<CompClusterExt>* clusArr = nullptr; - clusTree->SetBranchAddress("ITSClusterComp", &clusArr); - std::vector<unsigned char>* patternsPtr = nullptr; - auto pattBranch = clusTree->GetBranch("ITSClusterPatt"); - if (pattBranch) { - pattBranch->SetAddress(&patternsPtr); - } - - // ROFrecords - std::vector<ROFRec> rofRecVec, *rofRecVecP = &rofRecVec; - clusTree->SetBranchAddress("ITSClustersROF", &rofRecVecP); - - // Cluster MC labels - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* clusLabArr = nullptr; - std::vector<MC2ROF> mc2rofVec, *mc2rofVecP = &mc2rofVec; - if (hitTree && clusTree->GetBranch("ITSClusterMCTruth")) { - clusTree->SetBranchAddress("ITSClusterMCTruth", &clusLabArr); - clusTree->SetBranchAddress("ITSClustersMC2ROF", &mc2rofVecP); - } - clusTree->GetEntry(0); - - BuildTopologyDictionary dict; - - int nROFRec = (int)rofRecVec.size(); - std::vector<int> mcEvMin(nROFRec, hitTree->GetEntries()), mcEvMax(nROFRec, -1); - - if (clusLabArr) { // >> build min and max MC events used by each ROF - for (int imc = mc2rofVec.size(); imc--;) { - const auto& mc2rof = mc2rofVec[imc]; - if (mc2rof.rofRecordID < 0) { - continue; // this MC event did not contribute to any ROF - } - for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { - int irof = mc2rof.rofRecordID + irfd; - if (mcEvMin[irof] > imc) { - mcEvMin[irof] = imc; - } - if (mcEvMax[irof] < imc) { - mcEvMax[irof] = imc; - } - } - } - } // << build min and max MC events used by each ROF - - auto pattIdx = patternsPtr->cbegin(); - for (int irof = 0; irof < nROFRec; irof++) { - const auto& rofRec = rofRecVec[irof]; - - rofRec.print(); - - if (clusLabArr) { // >> read and map MC events contributing to this ROF - for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { - if (!hitVecPool[im]) { - hitTree->SetBranchAddress("ITSHit", &hitVecPool[im]); - hitTree->GetEntry(im); - auto& mc2hit = mc2hitVec[im]; - const auto* hitArray = hitVecPool[im]; - for (int ih = hitArray->size(); ih--;) { - const auto& hit = (*hitArray)[ih]; - uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); - mc2hit.emplace(key, ih); - } - } - } - } // << cache MC events contributing to this ROF - - for (int icl = 0; icl < rofRec.getNEntries(); icl++) { - int clEntry = rofRec.getFirstEntry() + icl; // entry of icl-th cluster of this ROF in the vector of clusters - // do we read MC data? - - const auto& cluster = (*clusArr)[clEntry]; - - ClusterTopology topology; - o2::itsmft::ClusterPattern pattern(pattIdx); - topology.setPattern(pattern); - // - // do we need to account for the bias of cluster COG wrt MC hit center? - float dX = BuildTopologyDictionary::IgnoreVal, dZ = BuildTopologyDictionary::IgnoreVal; - if (clusLabArr) { - const auto& lab = (clusLabArr->getLabels(clEntry))[0]; // we neglect effect of cluster contributed by multiple hits - auto srcID = lab.getSourceID(); - if (!lab.isNoise() && srcID != QEDSourceID) { // use MC truth info only for non-QED and non-noise clusters - auto trID = lab.getTrackID(); - const auto& mc2hit = mc2hitVec[lab.getEventID()]; - const auto* hitArray = hitVecPool[lab.getEventID()]; - int chipID = cluster.getSensorID(); - uint64_t key = (uint64_t(trID) << 32) + chipID; - auto hitEntry = mc2hit.find(key); - if (hitEntry != mc2hit.end()) { - const auto& hit = (*hitArray)[hitEntry->second]; - auto locH = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local - auto locHsta = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); - locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); - const auto locC = o2::itsmft::TopologyDictionary::getClusterCoordinates(cluster, pattern); - dX = locH.X() - locC.X(); - dZ = locH.Z() - locC.Z(); - } else { - printf("Failed to find MC hit entry for Tr:%d chipID:%d\n", trID, chipID); - } - } - } - dict.accountTopology(topology, dX, dZ); - } - // clean MC cache for events which are not needed anymore - int irfNext = irof; - while ((++irfNext < nROFRec) && mcEvMax[irfNext] < 0) { - } // find next ROF having MC contribution - int limMC = irfNext == nROFRec ? hitVecPool.size() : mcEvMin[irfNext]; // can clean events up to this - for (int imc = mcEvMin[irof]; imc < limMC; imc++) { - delete hitVecPool[imc]; - hitVecPool[imc] = nullptr; - mc2hitVec[imc].clear(); - } - } - - dict.setThreshold(0.0001); - dict.groupRareTopologies(); - - auto dID = o2::detectors::DetID::ITS; - dict.printDictionaryBinary(o2::base::NameConf::getDictionaryFileName(dID, "", ".bin")); - dict.printDictionary(o2::base::NameConf::getDictionaryFileName(dID, "", ".txt")); - dict.saveDictionaryRoot(o2::base::NameConf::getDictionaryFileName(dID, "", ".root")); - - TFile histogramOutput("dict_histograms.root", "recreate"); - TCanvas* cComplete = new TCanvas("cComplete", "Distribution of all the topologies"); - cComplete->cd(); - cComplete->SetLogy(); - TH1F* hComplete = nullptr; - o2::itsmft::TopologyDictionary::getTopologyDistribution(dict.getDictionary(), hComplete, "hComplete"); - hComplete->SetDirectory(0); - hComplete->Draw("hist"); - cComplete->Print("dictHisto.pdf"); - cComplete->Write(); - sw.Stop(); - sw.Print(); -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h index 9db42bd271b5f..6a4cfd2b36fd9 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h @@ -23,7 +23,7 @@ #include <vector> #include <tuple> #include "ITSBase/GeometryTGeo.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "DataFormatsITSMFT/Cluster.h" #include "DataFormatsITS/TrackITS.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -91,6 +91,7 @@ class CookedTracker clusIdx.emplace_back(idx); } trackNew.setClusterRefs(clEntry, noc); + trackNew.setPattern(0x7f); // this tracker finds only complete tracks return tracks.size(); }; process(clusters, it, dict, inserter, rof); @@ -99,8 +100,7 @@ class CookedTracker const Cluster* getCluster(Int_t index) const; void setGeometry(o2::its::GeometryTGeo* geom); - void setMCTruthContainers(const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* clsLabels, - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* trkLabels) + void setMCTruthContainers(const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* clsLabels, std::vector<o2::MCCompLabel>* trkLabels) { mClsLabels = clsLabels; mTrkLabels = trkLabels; @@ -135,7 +135,7 @@ class CookedTracker bool mContinuousMode = true; ///< triggered or cont. mode const o2::its::GeometryTGeo* mGeom = nullptr; /// interface to geometry const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mClsLabels = nullptr; /// Cluster MC labels - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mTrkLabels = nullptr; /// Track MC labels + std::vector<o2::MCCompLabel>* 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 diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h index edfe8b28c219d..5e927df91c1e1 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h @@ -18,11 +18,11 @@ #include <Rtypes.h> #include <vector> #include <array> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "MathUtils/Primitive2D.h" #include "CommonConstants/MathConstants.h" -#include "MathUtils/Bracket.h" +#include "MathUtils/Primitive2D.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTBase/SegmentationAlpide.h" @@ -32,8 +32,8 @@ namespace its { struct RecoGeomHelper { // - using BracketF = o2::utils::Bracket<float>; - using Vec2D = o2::utils::IntervalXY; + using BracketF = o2::math_utils::Bracketf_t; + using Vec2D = o2::math_utils::IntervalXYf_t; enum Relation : int { Below = -1, Inside = 0, @@ -47,7 +47,7 @@ struct RecoGeomHelper { BracketF yRange = {1.e9, -1.e9}, zRange = {1.e9, -1.e9}; // bounding box in tracking frame Vec2D xyEdges; - void updateLimits(const Point3D<float>& pnt); + void updateLimits(const o2::math_utils::Point3D<float>& pnt); void print() const; ClassDefNV(RecoChip, 0); }; @@ -67,7 +67,7 @@ struct RecoGeomHelper { std::vector<RecoChip> chips; Relation isPhiOutside(float phi, float toler = 0) const; - void updateLimits(const Point3D<float>& pnt); + void updateLimits(const o2::math_utils::Point3D<float>& pnt); void init(); void print() const; ClassDefNV(RecoLadder, 0); @@ -87,7 +87,7 @@ struct RecoGeomHelper { const RecoLadder& getLadder(int id) const { return ladders[id % nLadders]; } void init(); - void updateLimits(const Point3D<float>& pnt); + void updateLimits(const o2::math_utils::Point3D<float>& pnt); void print() const; int getLadderID(float phi) const; int getChipID(float z) const; @@ -131,7 +131,7 @@ inline int RecoGeomHelper::RecoLayer::getChipID(float z) const { // Get chip ID within the ladder corresponding to this phi // Note: this is an approximate method, one should check also the neighbouring ladders +/-1 - int ic = (z - zRange.min()) * z2chipID; + int ic = (z - zRange.getMin()) * z2chipID; return ic < 0 ? 0 : (ic < lastChipInLadder ? ic : lastChipInLadder); } @@ -141,7 +141,7 @@ inline int RecoGeomHelper::RecoLayer::getLadderID(float phi) const // Get ladder ID corresponding to phi. // Note: this is an approximate method, precise within 1/3 of average ladder width, // one should check also the neighbouring ladders +/-1 - o2::utils::BringTo02Pi(phi); + o2::math_utils::bringTo02Pi(phi); constexpr float PI2Inv = 1.f / o2::constants::math::TwoPI; return phi2ladder[int(phi * PI2Inv * phi2ladder.size())]; } diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx index 93f4fd5b5987b..033e5e2b92b87 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx @@ -13,14 +13,13 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "ITSReconstruction/ClustererTask.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "FairLogger.h" #include <TFile.h> #include <TTree.h> using namespace o2::its; -using namespace o2::utils; //_____________________________________________________________________ ClustererTask::ClustererTask(bool useMC, bool raw) : mRawDataMode(raw), diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx index 009e5c9ec5c6d..0e0303dd97701 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx @@ -38,10 +38,9 @@ using namespace o2::its; using namespace o2::itsmft; using namespace o2::constants::math; -using namespace o2::utils; using o2::field::MagneticField; using Label = o2::MCCompLabel; -using Point3Df = Point3D<float>; +using Point3Df = o2::math_utils::Point3D<float>; //************************************************ // Constants hardcoded for the moment: @@ -85,8 +84,9 @@ CookedTracker::CookedTracker(Int_t n) : mNumOfThreads(n), mBz(0.) //-------------------------------------------------------------------- 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++) + for (Int_t i = 0; i < kNLayers; i++) { sLayers[i].setR(klRadius[i]); + } } //__________________________________________________________________________ @@ -105,8 +105,9 @@ Label CookedTracker::cookLabel(TrackITSExt& t, Float_t wrong) const auto labels = mClsLabels->getLabels(idx); for (auto lab : labels) { // check all labels of the cluster - if (lab.isEmpty()) + if (lab.isEmpty()) { break; // all following labels will be empty also + } // was this label already accounted for ? labelOccurence[lab]++; } @@ -114,8 +115,9 @@ Label CookedTracker::cookLabel(TrackITSExt& t, Float_t wrong) const Label lab; Int_t maxL = 0; // find most encountered label for (auto [label, count] : labelOccurence) { - if (count <= maxL) + if (count <= maxL) { continue; + } maxL = count; lab = label; } @@ -147,8 +149,9 @@ static Double_t f1(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t 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) + if (d > 0) { crv = -crv; + } return crv; } @@ -308,8 +311,9 @@ void CookedTracker::makeSeeds(std::vector<TrackITSExt>& seeds, Int_t first, Int_ auto r3 = xyz3.rho(); zr3 = z1 + (r3 - r1) / (r2 - r1) * (z2 - z1); - if (std::abs(z3 - zr3) > 0.2 * dz3) + if (std::abs(z3 - zr3) > 0.2 * dz3) { continue; + } const Point3Df& txyz2 = c2->getXYZ(); // tracking coordinates @@ -317,16 +321,19 @@ void CookedTracker::makeSeeds(std::vector<TrackITSExt>& seeds, Int_t first, Int_ float ip[2]; seed.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - if (TMath::Abs(ip[0]) > kmaxDCAxy) + if (TMath::Abs(ip[0]) > kmaxDCAxy) { continue; - if (TMath::Abs(ip[1]) > kmaxDCAz) + } + if (TMath::Abs(ip[1]) > kmaxDCAz) { 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)) + if (!seed.correctForMaterial(xx0, xx0 * radl * rho, kTRUE)) { continue; + } } seed.setClusterIndex(kSeedingLayer1, n1); seed.setClusterIndex(kSeedingLayer3, n3); @@ -368,7 +375,7 @@ void CookedTracker::trackSeeds(std::vector<TrackITSExt>& seeds) auto x = track.getX(); auto y = track.getY(); Float_t phi = track.getAlpha() + TMath::ATan2(y, x); - BringTo02Pi(phi); + o2::math_utils::bringTo02Pi(phi); auto z = track.getZ(); auto crv = track.getCurvature(getBz()); @@ -394,31 +401,39 @@ void CookedTracker::trackSeeds(std::vector<TrackITSExt>& seeds) Int_t ci = -1; TrackITSExt t3(track); for (auto& ci3 : selec[3]) { - if (used[3][ci3]) + if (used[3][ci3]) { continue; - if (!attachCluster(volID, 3, ci3, t3, track)) + } + if (!attachCluster(volID, 3, ci3, t3, track)) { continue; + } TrackITSExt t2(t3); for (auto& ci2 : selec[2]) { - if (used[2][ci2]) + if (used[2][ci2]) { continue; - if (!attachCluster(volID, 2, ci2, t2, t3)) + } + if (!attachCluster(volID, 2, ci2, t2, t3)) { continue; + } TrackITSExt t1(t2); for (auto& ci1 : selec[1]) { - if (used[1][ci1]) + if (used[1][ci1]) { continue; - if (!attachCluster(volID, 1, ci1, t1, t2)) + } + if (!attachCluster(volID, 1, ci1, t1, t2)) { continue; + } TrackITSExt t0(t1); for (auto& ci0 : selec[0]) { - if (used[0][ci0]) + if (used[0][ci0]) { continue; - if (!attachCluster(volID, 0, ci0, t0, t1)) + } + if (!attachCluster(volID, 0, ci0, t0, t1)) { continue; + } if (t0.isBetter(best, kmaxChi2PerTrack)) { best = t0; } @@ -488,7 +503,7 @@ void CookedTracker::process(gsl::span<const o2::itsmft::CompClusterExt> const& c for (const auto& comp : clusters_in_frame) { auto pattID = comp.getPatternID(); - Point3D<float> locXYZ; + o2::math_utils::Point3D<float> locXYZ; float sigmaY2 = 0.0015 * 0.0015, sigmaZ2 = sigmaY2, sigmaYZ = 0; //Dummy COG errors (about half pixel size) if (pattID != itsmft::CompCluster::InvalidPatternID) { sigmaY2 = dict.getErr2X(pattID); @@ -501,7 +516,7 @@ void CookedTracker::process(gsl::span<const o2::itsmft::CompClusterExt> const& c } } else { o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict.getClusterCoordinates(comp, patt); + locXYZ = dict.getClusterCoordinates(comp, patt, false); } auto sensorID = comp.getSensorID(); // Inverse transformation to the local --> tracking @@ -560,8 +575,9 @@ std::tuple<int, int> CookedTracker::processLoadedClusters(TrackInserter& inserte seedArray[t] = futures[t].get(); nSeeds += seedArray[t].size(); for (auto& track : seedArray[t]) { - if (track.getNumberOfClusters() < kminNumberOfClusters) + if (track.getNumberOfClusters() < kminNumberOfClusters) { continue; + } nAllTracks = inserter(track); nTracks++; if (mTrkLabels) { @@ -571,7 +587,7 @@ std::tuple<int, int> CookedTracker::processLoadedClusters(TrackInserter& inserte } // the inserter returns the size of the track vector, the index of the last // inserted track is thus n - 1 - mTrkLabels->addElement(nAllTracks - 1, label); + mTrkLabels->emplace_back(label); } } } @@ -632,8 +648,9 @@ int CookedTracker::loadClusters() // sort them, distribute over the internal tracker arrays, etc //-------------------------------------------------------------------- - if (mClusterCache.empty()) + if (mClusterCache.empty()) { return 0; + } for (const auto& c : mClusterCache) { Int_t layer = mGeom->getLayer(c.getSensorID()); @@ -643,13 +660,15 @@ int CookedTracker::loadClusters() std::vector<std::future<void>> fut; for (Int_t l = 0; l < kNLayers; l += mNumOfThreads) { for (Int_t t = 0; t < mNumOfThreads; t++) { - if (l + t >= kNLayers) + 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 (Int_t t = 0; t < fut.size(); t++) + for (Int_t t = 0; t < fut.size(); t++) { fut[t].wait(); + } } return mClusterCache.size(); @@ -661,8 +680,9 @@ void CookedTracker::unloadClusters() // This function unloads ITSU clusters from the RAM //-------------------------------------------------------------------- mClusterCache.clear(); - for (Int_t i = 0; i < kNLayers; i++) + for (Int_t i = 0; i < kNLayers; i++) { sLayers[i].unloadClusters(); + } } const Cluster* CookedTracker::getCluster(Int_t index) const @@ -700,14 +720,15 @@ void CookedTracker::Layer::init() auto xyz = c->getXYZGloRot(*mGeom); r += xyz.rho(); Float_t phi = xyz.Phi(); - BringTo02Pi(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) + if (m) { mR = r / m; + } } void CookedTracker::Layer::unloadClusters() @@ -718,8 +739,9 @@ void CookedTracker::Layer::unloadClusters() mClusters.clear(); mAlphaRef.clear(); mPhi.clear(); - for (Int_t s = 0; s < kNSectors; s++) + for (Int_t s = 0; s < kNSectors; s++) { mSectors[s].clear(); + } } Bool_t CookedTracker::Layer::insertCluster(const Cluster* c) @@ -749,7 +771,7 @@ void CookedTracker::Layer::selectClusters(std::vector<Int_t>& selec, Float_t phi Float_t zMin = z - dz; Float_t zMax = z + dz; - BringTo02Pi(phi); + o2::math_utils::bringTo02Pi(phi); Float_t dphi = dy / mR; @@ -771,8 +793,9 @@ void CookedTracker::Layer::selectClusters(std::vector<Int_t>& selec, Float_t phi if (cdphi > kPI) { cdphi = k2PI - cdphi; } - if (cdphi > dphi) + if (cdphi > dphi) { continue; // check in Phi + } } selec.push_back(i); } @@ -793,8 +816,9 @@ Bool_t CookedTracker::attachCluster(Int_t& volID, Int_t nl, Int_t ci, TrackITSEx volID = vid; t = o; Double_t alpha = layer.getAlphaRef(ci); - if (!t.propagate(alpha, c->getX(), getBz())) + if (!t.propagate(alpha, c->getX(), getBz())) { return kFALSE; + } } Double_t chi2 = t.getPredictedChi2(*c); @@ -819,6 +843,7 @@ void CookedTracker::setGeometry(o2::its::GeometryTGeo* geom) { /// attach geometry interface mGeom = geom; - for (Int_t i = 0; i < kNLayers; i++) + for (Int_t i = 0; i < kNLayers; i++) { sLayers[i].setGeometry(geom); + } } diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx index 42061b2a5d7c9..15895fa9b54f8 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx @@ -19,7 +19,7 @@ using namespace o2::its; //_____________________________________________________________________ -void RecoGeomHelper::RecoChip::updateLimits(const Point3D<float>& pntTra) +void RecoGeomHelper::RecoChip::updateLimits(const o2::math_utils::Point3D<float>& pntTra) { // update limits from the edge point in tracking frame yRange.update(pntTra.Y()); @@ -30,29 +30,29 @@ void RecoGeomHelper::RecoChip::updateLimits(const Point3D<float>& pntTra) void RecoGeomHelper::RecoChip::print() const { printf("Ch#%4d Alp: %+.3f X:%5.2f %+6.3f<y<%+6.3f %+6.3f<z<%+6.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f}\n", - id, alp, xRef, yRange.min(), yRange.max(), zRange.min(), zRange.max(), + id, alp, xRef, yRange.getMin(), yRange.getMax(), zRange.getMin(), zRange.getMax(), xyEdges.getX0(), xyEdges.getY0(), xyEdges.getX1(), xyEdges.getY1()); } //_____________________________________________________________________ -void RecoGeomHelper::RecoLadder::updateLimits(const Point3D<float>& pntGlo) +void RecoGeomHelper::RecoLadder::updateLimits(const o2::math_utils::Point3D<float>& pntGlo) { // update limits from the point in Global frame float phi = pntGlo.phi(); // -pi:pi range - o2::utils::BringTo02Pi(phi); // temporary bring to 0:2pi range - o2::utils::BringTo02Pi(phiRange.min()); - o2::utils::BringTo02Pi(phiRange.max()); + o2::math_utils::bringTo02Pi(phi); // temporary bring to 0:2pi range + o2::math_utils::bringTo02Pi(phiRange.getMin()); + o2::math_utils::bringTo02Pi(phiRange.getMax()); phiRange.update(phi); phiMean = phiRange.mean(); dphiH = 0.5 * phiRange.delta(); - if (phiRange.delta() > o2::constants::math::PI) { // wrapping, swap - phiRange.set(phiRange.max(), phiRange.min()); // swap + if (phiRange.delta() > o2::constants::math::PI) { // wrapping, swap + phiRange.set(phiRange.getMax(), phiRange.getMin()); // swap phiMean -= o2::constants::math::PI; dphiH = o2::constants::math::PI - dphiH; } - o2::utils::BringToPMPi(phiRange.min()); // -pi:pi range - o2::utils::BringToPMPi(phiRange.max()); - o2::utils::BringToPMPi(phiMean); + o2::math_utils::bringToPMPi(phiRange.getMin()); // -pi:pi range + o2::math_utils::bringToPMPi(phiRange.getMax()); + o2::math_utils::bringToPMPi(phiMean); // zRange.update(pntGlo.Z()); } @@ -77,7 +77,7 @@ void RecoGeomHelper::RecoLadder::print() const { assert(overlapWithNext != Undefined || chips.size() == 0); // make sure there are no undefined ladders after init is done printf("Ladder %3d %.3f<phi[<%.3f>]<%.3f dPhiH:%.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f} | %3d chips | OvlNext: %s\n", - id, phiRange.min(), phiMean, phiRange.max(), dphiH, + 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) { @@ -89,7 +89,7 @@ void RecoGeomHelper::RecoLadder::print() const void RecoGeomHelper::RecoLayer::init() { auto gm = o2::its::GeometryTGeo::Instance(); - gm->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2GRot, o2::TransformType::T2L)); // more matrices ? + gm->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2L)); // more matrices ? int nHStaves = gm->getNumberOfHalfStaves(id); int nStaves = gm->getNumberOfStaves(id); @@ -115,9 +115,9 @@ void RecoGeomHelper::RecoLayer::init() auto& chip = ladder.chips.emplace_back(); chip.id = chipID; gm->getSensorXAlphaRefPlane(chipID, chip.xRef, chip.alp); - o2::utils::sincosf(chip.alp, chip.snAlp, chip.csAlp); + o2::math_utils::sincos(chip.alp, chip.snAlp, chip.csAlp); - Point3D<float> edgeLoc(-dxH, 0.f, -dzH); + o2::math_utils::Point3D<float> edgeLoc(-dxH, 0.f, -dzH); auto edgeTra = gm->getMatrixT2L(chipID) ^ (edgeLoc); // edge in tracking frame chip.updateLimits(edgeTra); auto edgeGloM = gm->getMatrixT2GRot(chipID)(edgeTra); // edge in global frame @@ -136,15 +136,15 @@ void RecoGeomHelper::RecoLayer::init() // sort according to mean phi (in -pi:pi range!!!) std::sort(ladders.begin(), ladders.end(), [](auto& a, auto& b) { float pha = a.phiMean, phb = b.phiMean; - o2::utils::BringTo02Pi(pha); - o2::utils::BringTo02Pi(phb); + o2::math_utils::bringTo02Pi(pha); + o2::math_utils::bringTo02Pi(phb); return pha < phb; }); // make sure chips within the ladder are ordered in Z, renumber ladders for (int i = nLadders; i--;) { auto& lad = ladders[i]; - std::sort(lad.chips.begin(), lad.chips.end(), [](auto& a, auto& b) { return a.zRange.min() < b.zRange.min(); }); + std::sort(lad.chips.begin(), lad.chips.end(), [](auto& a, auto& b) { return a.zRange.getMin() < b.zRange.getMin(); }); lad.id = i; lad.init(); } @@ -166,16 +166,16 @@ void RecoGeomHelper::RecoLayer::init() // find the radius of the edge at low phi float phi0 = std::atan2(lad.xyEdges.getY0(), lad.xyEdges.getX0()); float phi1 = std::atan2(lad.xyEdges.getY1(), lad.xyEdges.getX1()); - o2::utils::BringTo02Pi(phi0); // we don't know a priori if edge0/1 corresponds to low/high phi or vice versa - o2::utils::BringTo02Pi(phi1); + o2::math_utils::bringTo02Pi(phi0); // we don't know a priori if edge0/1 corresponds to low/high phi or vice versa + o2::math_utils::bringTo02Pi(phi1); float r2This = (phi0 < phi1 && phi1 - phi0 < o2::constants::math::PI) ? // pick R of lowest angle lad.xyEdges.getX0() * lad.xyEdges.getX0() + lad.xyEdges.getY0() * lad.xyEdges.getY0() : lad.xyEdges.getX1() * lad.xyEdges.getX1() + lad.xyEdges.getY1() * lad.xyEdges.getY1(); // phi0 = std::atan2(plad.xyEdges.getY0(), plad.xyEdges.getX0()); phi1 = std::atan2(plad.xyEdges.getY1(), plad.xyEdges.getX1()); - o2::utils::BringTo02Pi(phi0); // we don't know a priori if edge0/1 corresponds to low/high phi or vice versa - o2::utils::BringTo02Pi(phi1); + o2::math_utils::bringTo02Pi(phi0); // we don't know a priori if edge0/1 corresponds to low/high phi or vice versa + o2::math_utils::bringTo02Pi(phi1); float r2Prev = (phi0 < phi1 && phi1 - phi0 < o2::constants::math::PI) ? // pick R of highest angle plad.xyEdges.getX1() * plad.xyEdges.getX1() + plad.xyEdges.getY1() * plad.xyEdges.getY1() : plad.xyEdges.getX0() * plad.xyEdges.getX0() + plad.xyEdges.getY0() * plad.xyEdges.getY0(); @@ -189,7 +189,7 @@ void RecoGeomHelper::RecoLayer::init() int laddId = 0; for (int i = 0; i < ndiv; i++) { float phi = (0.5 + i) * dphi; - o2::utils::BringToPMPi(phi); + o2::math_utils::bringToPMPi(phi); while (laddId < nLadders) { const auto& lad = ladders[laddId]; auto rel = lad.isPhiOutside(phi); @@ -207,7 +207,7 @@ void RecoGeomHelper::RecoLayer::init() } //_____________________________________________________________________ -void RecoGeomHelper::RecoLayer::updateLimits(const Point3D<float>& pntGlo) +void RecoGeomHelper::RecoLayer::updateLimits(const o2::math_utils::Point3D<float>& pntGlo) { // update limits from the point in global frame rRange.update(pntGlo.Rho()); @@ -218,7 +218,7 @@ void RecoGeomHelper::RecoLayer::updateLimits(const Point3D<float>& pntGlo) void RecoGeomHelper::RecoLayer::print() const { printf("\nLayer %d %.2f<r<%.2f %+.2f<z<%+.2f %d ladders\n", - id, rRange.min(), rRange.max(), zRange.min(), zRange.max(), (int)ladders.size()); + id, rRange.getMin(), rRange.getMax(), zRange.getMin(), zRange.getMax(), (int)ladders.size()); for (const auto& ld : ladders) { ld.print(); } diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx index 788b8a862be67..6eb0cfbb36f5e 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx @@ -27,7 +27,7 @@ using namespace o2::itsmft; using namespace o2::its; -using Point3Df = Point3D<float>; +using Point3Df = o2::math_utils::Point3D<float>; TrivialVertexer::TrivialVertexer() = default; diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index 621bda9c95823..4fb226daa8a6e 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -282,8 +282,9 @@ Bool_t Detector::ProcessHits(FairVolume* vol) while ((lay < sNumberLayers) && (notSens = (volID != mLayerID[lay]))) { ++lay; } - if (notSens) + if (notSens) { return kFALSE; // RS: can this happen? This method must be called for sensors only? + } // Is it needed to keep a track reference when the outer ITS volume is encountered? auto stack = (o2::data::Stack*)fMC->GetStack(); @@ -323,10 +324,12 @@ Bool_t Detector::ProcessHits(FairVolume* vol) } // increment energy loss at all steps except entrance - if (!startHit) + if (!startHit) { mTrackData.mEnergyLoss += fMC->Edep(); - if (!(startHit | stopHit)) + } + if (!(startHit | stopHit)) { return kFALSE; // do noting + } if (startHit) { mTrackData.mEnergyLoss = 0.; diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx index a4599f208d01e..86fa07c29c7a1 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx @@ -477,8 +477,9 @@ TGeoVolume* V3Layer::createStave(const TGeoManager* /*mgr*/) mechStaveVol = createStaveStructInnerB(); if (mechStaveVol) { ypos = (static_cast<TGeoBBox*>(modVol->GetShape()))->GetDY() - ypos; - if (mStaveModel != Detector::kIBModel4) + if (mStaveModel != Detector::kIBModel4) { ypos += (static_cast<TGeoBBox*>(mechStaveVol->GetShape()))->GetDY(); + } staveVol->AddNode(mechStaveVol, 1, new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", 0, 0, 180))); } } else { @@ -490,15 +491,16 @@ TGeoVolume* V3Layer::createStave(const TGeoManager* /*mgr*/) xpos = (static_cast<TGeoBBox*>(hstaveVol->GetShape()))->GetDX() - sOBHalfStaveXOverlap / 2; // ypos is now a parameter to avoid HS displacement wrt nominal radii ypos = sOBHalfStaveYPos; - staveVol->AddNode(hstaveVol, 0, new TGeoTranslation(-xpos, ypos, 0)); - staveVol->AddNode(hstaveVol, 1, new TGeoTranslation(xpos, ypos + sOBHalfStaveYTrans, 0)); + staveVol->AddNode(hstaveVol, 0, new TGeoTranslation(-xpos, ypos + sOBHalfStaveYTrans, 0)); + staveVol->AddNode(hstaveVol, 1, new TGeoTranslation(xpos, ypos, 0)); mHierarchy[kHalfStave] = 2; // RS mechStaveVol = createSpaceFrameOuterB(); if (mechStaveVol) { - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon staveVol->AddNode(mechStaveVol, 1, new TGeoCombiTrans(0, -sOBSFrameULegHeight1, 0, new TGeoRotation("", 180, 0, 0))); + } } } } @@ -547,10 +549,11 @@ TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) char chipName[nameLen], sensName[nameLen], volumeName[nameLen]; // For material budget studies - if (mBuildLevel < 6) + if (mBuildLevel < 6) { dummyChip = kFALSE; // will be made of Si - else + } else { dummyChip = kTRUE; // will be made of Air + } // First create the single chip snprintf(chipName, nameLen, "%s%d", GeometryTGeo::getITSChipPattern(), mLayerNumber); @@ -581,8 +584,9 @@ TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) Double_t yano = (static_cast<TGeoBBox*>(aluAnodeCableVol->GetShape()))->GetDY(); ytot = ymod; - if (mStaveModel == Detector::kIBModel4) + if (mStaveModel == Detector::kIBModel4) { ytot += (sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitorYHi / 2); + } TGeoBBox* module = new TGeoBBox(xtot, ytot, ztot); @@ -615,8 +619,9 @@ TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) if (mStaveModel == Detector::kIBModel4) { ypos += (ymod + glue->GetDY()); - if (mBuildLevel < 2) // Glue + if (mBuildLevel < 2) { // Glue modVol->AddNode(glueVol, 1, new TGeoTranslation(xpos, ypos, 0)); + } ypos += glue->GetDY(); if (mBuildLevel < 4) { // Kapton @@ -738,20 +743,22 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz nCapacitors++; for (Int_t j = 0; j < sIBChipsPerRow; j++) { - if (j == (sIBChipsPerRow - 1)) + if (j == (sIBChipsPerRow - 1)) { xpos = xGroup4[1]; - else + } else { xpos = xGroup4[0]; + } zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup4[j]; modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += sIBChipsPerRow; for (Int_t j = 0; j < nGroup5A; j++) { - if (j == 0) + if (j == 0) { xpos = xGroup5A[0]; - else + } else { xpos = xGroup5A[1]; + } zpos = zGroup5A[j]; modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } @@ -799,8 +806,9 @@ TGeoVolume* V3Layer::createIBFPCAlGnd(const Double_t xcable, const Double_t zcab aluminumVol->SetFillColor(kCyan); ypos = coverlay->GetDY() - aluminum->GetDY(); - if (mBuildLevel < 1) // Aluminum + if (mBuildLevel < 1) { // Aluminum coverlayVol->AddNode(aluminumVol, 1, new TGeoTranslation(0, ypos, 0)); + } return coverlayVol; } @@ -850,8 +858,9 @@ TGeoVolume* V3Layer::createIBFPCAlAnode(const Double_t xcable, const Double_t zc aluminumVol->SetFillColor(kCyan); ypos = -coverlay->GetDY() + aluminum->GetZ(1); - if (mBuildLevel < 1) // Aluminum + if (mBuildLevel < 1) { // Aluminum coverlayVol->AddNode(aluminumVol, 1, new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", 0, -90, 0))); + } return coverlayVol; } @@ -1154,16 +1163,19 @@ TGeoVolume* V3Layer::createStaveModelInnerB4(const TGeoManager* mgr) // Now build up the half stave ypos = glue->GetDY(); - if (mBuildLevel < 2) // Glue + if (mBuildLevel < 2) { // Glue mechStavVol->AddNode(glueVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (glue->GetDY() + fleecbot->GetDY()); - if (mBuildLevel < 5) // Carbon + if (mBuildLevel < 5) { // Carbon mechStavVol->AddNode(fleecbotVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (fleecbot->GetDY() + cfplate->GetDY()); - if (mBuildLevel < 5) // Carbon + if (mBuildLevel < 5) { // Carbon mechStavVol->AddNode(cfplateVol, 1, new TGeoTranslation(0, ypos, 0)); + } ylay = ypos + cfplate->GetDY(); // The level where tubes etc. lay @@ -1835,10 +1847,11 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) ymod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDY(); zmod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDZ(); - if (mLayerNumber <= 4) + if (mLayerNumber <= 4) { zlen = sOBColdPlateZLenML / 2; // Middle Layer - else + } else { zlen = sOBColdPlateZLenOL / 2; // Outer Layer + } xlen = sOBColdPlateXWidth / 2; @@ -1873,8 +1886,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) new TGeoTubeSeg("FleecTube", rCoolMax + yGraph, rCoolMax + yCFleece + yGraph, zlen, 180., 360.); TGeoTube* gammaConvRod; - if (mAddGammaConv) + if (mAddGammaConv) { gammaConvRod = new TGeoTube("GammaConver", 0, 0.5 * mGammaConvDiam, zlen - sOBCPConnHollowZLen); + } // TGeoBBox* flex1_5cm = new TGeoBBox("Flex1MV_5cm", xHalfSt, yFlex1 / 2, flexOverlap / 2); // TGeoBBox* flex2_5cm = new TGeoBBox("Flex2MV_5cm", xHalfSt, yFlex2 / 2, flexOverlap / 2); @@ -1887,8 +1901,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) // The half stave container (an XTru to avoid overlaps between neightbours) xHalfSt = xmod; // add the cross cables when done! yHalfSt = ypowbus + ymod + coldPlate->GetDY() + 2 * fleeccent->GetDY() + graphlat->GetDY() + fleeclat->GetDY(); - if (mAddGammaConv) + if (mAddGammaConv) { yHalfSt += mGammaConvDiam; + } xtru[0] = xHalfSt; ytru[0] = 0; @@ -1898,8 +1913,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) ytru[2] = ytru[1]; xtru[3] = xtru[2]; ytru[3] = ytru[2] - (coolTube->GetRmax() + fleectub->GetRmax()); - if (mAddGammaConv) + if (mAddGammaConv) { ytru[3] -= mGammaConvDiam; + } xtru[4] = sOBCoolTubeXDist / 2 - fleectub->GetRmax(); ytru[4] = ytru[3]; xtru[5] = xtru[4]; @@ -1923,8 +1939,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) new TGeoBBox("connCsideOB", sOBCPConnectorXWidth / 2, sOBCPConnBlockYHei / 2, sOBCPConnBlockZLen / 2); // The StaveStruct container, a Composite Shape - if (mAddGammaConv) + if (mAddGammaConv) { yHalfSt -= mGammaConvDiam; + } ypos = 2 * yHalfSt + connAside->GetDY() - sOBCPConnHollowYHei; zpos = zlen + connAside->GetDZ() - sOBCPConnHollowZLen; snprintf(volname, nameLen, "transAsideOB%d", mLayerNumber); @@ -2048,18 +2065,21 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) ypos -= ymod; ypos -= fleeccent->GetDY(); - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon halfStaveVol->AddNode(fleeccentVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos -= fleeccent->GetDY(); ypos -= coldPlate->GetDY(); - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon halfStaveVol->AddNode(coldPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos -= coldPlate->GetDY(); ypos -= fleeccent->GetDY(); - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon halfStaveVol->AddNode(fleeccentVol, 2, new TGeoTranslation(0, ypos, 0)); + } xpos = sOBCoolTubeXDist / 2; ypos1 = ypos - (fleeccent->GetDY() + coolTube->GetRmax()); @@ -2236,45 +2256,55 @@ TGeoVolume* V3Layer::createOBPowerBiasBuses(const Double_t zcable, const TGeoMan // Volumes are piled up from bottom to top ypos = -pnbBus->GetDY() + kapPB->GetDY(); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (kapPB->GetDY() + gndPB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(gndPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (gndPB->GetDY() + dielPB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(dielPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (dielPB->GetDY() + topPB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(topPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (topPB->GetDY() + kapPB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapPBVol, 2, new TGeoTranslation(0, ypos, 0)); + } // ypos += (kapPB->GetDY() + kapBB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (kapBB->GetDY() + botBB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(botBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (botBB->GetDY() + dielBB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(dielBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (dielBB->GetDY() + topBB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(topBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (topBB->GetDY() + kapBB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapBBVol, 2, new TGeoTranslation(0, ypos, 0)); + } // return pnbBusVol; @@ -3380,10 +3410,11 @@ TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) // First create all needed shapes // For material budget studies - if (mBuildLevel < 7) + if (mBuildLevel < 7) { dummyChip = kFALSE; // will be made of Si - else + } else { dummyChip = kTRUE; // will be made of Air + } // The chip (the same as for IB) snprintf(chipName, nameLen, "%s%d", GeometryTGeo::getITSChipPattern(), mLayerNumber); @@ -3453,8 +3484,9 @@ TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) // Now build up the module ypos = -module->GetDY() + glueCP->GetDY(); - if (mBuildLevel < 3) // Glue + if (mBuildLevel < 3) { // Glue modVol->AddNode(glueCPVol, 1, new TGeoTranslation(0, ypos, 0)); + } xpos = xchip + xGap / 2; ypos += (ychip + glueCP->GetDY()); @@ -3478,8 +3510,9 @@ TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) } ypos += (ychip + glueFPC->GetDY()); - if (mBuildLevel < 3) // Glue + if (mBuildLevel < 3) { // Glue modVol->AddNode(glueFPCVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += glueFPC->GetDY(); if (mBuildLevel < 5) { // Kapton @@ -3536,8 +3569,9 @@ TGeoVolume* V3Layer::createOBFPCCuGnd(const Double_t zcable, const TGeoManager* copperVol->SetFillColor(kCyan); ypos = -soldmask->GetDY() + copper->GetDY(); - if (mBuildLevel < 1) // Copper + if (mBuildLevel < 1) { // Copper soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + } return soldmaskVol; } @@ -3581,8 +3615,9 @@ TGeoVolume* V3Layer::createOBFPCCuSig(const Double_t zcable, const TGeoManager* copperVol->SetFillColor(kCyan); ypos = soldmask->GetDY() - copper->GetDY(); - if (mBuildLevel < 1) // Copper + if (mBuildLevel < 1) { // Copper soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + } return soldmaskVol; } diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx index b0d44c1c9c32b..38294eb0ca47f 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx @@ -81,8 +81,9 @@ TGeoVolume* V3Services::createIBEndWheelsSideA(const TGeoManager* mgr) TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideA"); endWheelsVol->SetVisibility(kTRUE); - for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) { ibEndWheelSideA(jLay, endWheelsVol, mgr); + } // Return the wheels return endWheelsVol; @@ -108,8 +109,9 @@ TGeoVolume* V3Services::createIBEndWheelsSideC(const TGeoManager* mgr) TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideC"); endWheelsVol->SetVisibility(kTRUE); - for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) { ibEndWheelSideC(jLay, endWheelsVol, mgr); + } // Return the wheels return endWheelsVol; @@ -182,8 +184,9 @@ void V3Services::createMBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* m // Created: 24 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) { obEndWheelSideA(jLay, mother, mgr); + } } void V3Services::createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) @@ -202,8 +205,9 @@ void V3Services::createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* m // Created: 26 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) { mbEndWheelSideC(jLay, mother, mgr); + } } void V3Services::createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr) @@ -222,8 +226,9 @@ void V3Services::createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* m // Created: 27 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) { obEndWheelSideA(jLay + sNumberMiddlLayers, mother, mgr); + } } void V3Services::createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) @@ -242,8 +247,9 @@ void V3Services::createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* m // Created: 27 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) { obEndWheelSideC(jLay, mother, mgr); + } } void V3Services::createOBConeSideA(TGeoVolume* mother, const TGeoManager* mgr) @@ -668,10 +674,11 @@ void V3Services::ibEndWheelSideC(const Int_t iLay, TGeoVolume* endWheel, const T endwcbshmata->RegisterYourself(); TGeoTranslation* endwcbshmatb = new TGeoTranslation(Form("endwcbshmat%dl%db", ihole, iLay), -xpos, -ypos, zpos); endwcbshmatb->RegisterYourself(); - if ((ihole > 1 && ihole < 5) || (ihole > 5 && ihole < 9)) // Small holes + if ((ihole > 1 && ihole < 5) || (ihole > 5 && ihole < 9)) { // Small holes strcpy(holename, endwcbasShol->GetName()); - else + } else { strcpy(holename, endwcbasBhol->GetName()); + } endWheelComposite += Form("-%s:endwcbshmat%dl%da-%s:endwcbshmat%dl%db", holename, ihole, iLay, holename, ihole, iLay); } @@ -1270,10 +1277,11 @@ TString V3Services::ibCreateHollowsCyssFlangeSideA(const Double_t zlen) cyssFlangeAHollows += Form("-roundhalf:roundtr%d-roundhalf:roundtr%d", j + 2, j + 5); phi = 360 - phi - 0.05; - if (j == 3) + if (j == 3) { dphi = 360 - sCyssFlangeAHollowPhi0 + 0.05; - else + } else { dphi = phi + (sCyssFlangeAHole1PhiStep - sCyssFlangeAHollowPhi1) + 0.1; + } TGeoTubeSeg* hollow1 = new TGeoTubeSeg(Form("hollow%d", j), rmin, rmax, 2 * zlen, phi, dphi); @@ -1611,10 +1619,11 @@ void V3Services::obEndWheelSideA(const Int_t iLay, TGeoVolume* mother, const TGe // Finally put everything in the mother volume // In blueprints the Z position is given wrt the shelf holes // First the ring - if (iLay < sNumberMiddlLayers) + if (iLay < sNumberMiddlLayers) { zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos; - else + } else { zpos = sOBWheelsZpos + sOBWheelShelfHoleZpos; + } zpos -= outerRingSh->GetDz(); mother->AddNode(ringOuterVol, 1, new TGeoTranslation(0, 0, zpos)); @@ -1697,10 +1706,11 @@ void V3Services::mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGe TGeoTube* innerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); // The outer ring with the flange - if (iLay == 0) + if (iLay == 0) { nsect = 6; - else + } else { nsect = 4; + } TGeoPcon* outerRingSh = new TGeoPcon(0, 360, nsect); @@ -1722,17 +1732,19 @@ void V3Services::mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGe } // The lower ring - if (iLay == 0) + if (iLay == 0) { zlen = sOBWheelSuppZlen[iLay] - sOBWheelFlangeZlen - 2 * sOBWheelThickness; - else + } else { zlen = sOBWheelSuppZlen[iLay] - sOBWheelThickness - outerRingSh->GetZ(nsect - 1); + } rmax = sOBWheelSuppRmin[iLay] + sOBWheelThickness; TGeoTube* lowerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], rmax, zlen / 2); // The upper ring - if (iLay == 1) // For odd layers the upper and lower rings length is the same + if (iLay == 1) { // For odd layers the upper and lower rings length is the same zlen = sOBWheelSuppZlen[iLay] - 2 * sOBWheelThickness; + } rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; TGeoTube* upperRingSh = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); @@ -1929,8 +1941,9 @@ void V3Services::obEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGe mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, -zpos)); - if (iLay == 1) + if (iLay == 1) { zpos -= (sOBWheelThickness + (static_cast<TGeoTube*>(upperRingSh))->GetDz()); + } mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, -zpos)); } @@ -2165,10 +2178,11 @@ void V3Services::obConeTraysSideA(TGeoVolume* mother, const TGeoManager* mgr) // Finally put everything in the mother volume for (Int_t j = 0; j < 2; j++) { - if (j == 0) + if (j == 0) { zpos = sOBConesZpos - sOBTrayZpos[j] - sOBTrayZlen[j]; - else + } else { zpos = sOBConesZpos + sOBTrayZpos[j]; + } mother->AddNode(obTrayVol[j], 1, new TGeoTranslation(0, 0, zpos)); mother->AddNode(obTrayVol[j], 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); diff --git a/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx b/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx index e1fe768bf41b5..539165a1db713 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx @@ -250,10 +250,10 @@ void setupLinks(o2::itsmft::MC2RawEncoder<MAP>& m2r, std::string_view outDir, st if (acceptRU && lnkAs[il]) { nLinks++; auto& ru = *m2r.getRUDecode(ruID); - uint32_t lanes = mp.getCablesOnRUType(ru.ruInfo->ruType); // lanes patter of this RU + uint32_t lanes = mp.getCablesOnRUType(ru.ruInfo->ruType); // lanes pattern of this RU ru.links[il] = m2r.addGBTLink(); auto link = m2r.getGBTLink(ru.links[il]); - link->lanes = lanes & ((0x1 << lnkAs[il]) - 1) << (accL); + link->lanes = lanes & (((0x1 << lnkAs[il]) - 1) << (accL)); // RS FIXME link->idInCRU = linkID; link->cruID = cruID; link->feeID = mp.RUSW2FEEId(ruID, il); @@ -273,7 +273,8 @@ void setupLinks(o2::itsmft::MC2RawEncoder<MAP>& m2r, std::string_view outDir, st outFileLink = o2::utils::concat_string(outDir, "/", outPrefix, "_cru", std::to_string(cruID), ".raw"); } else if (fileFor == "link") { outFileLink = o2::utils::concat_string(outDir, "/", outPrefix, "_cru", std::to_string(cruID), - "_link", std::to_string(linkID), "_ep", std::to_string(link->endPointID), ".raw"); + "_link", std::to_string(linkID), "_ep", std::to_string(link->endPointID), + "_feeid", std::to_string(link->feeID), ".raw"); } else { throw std::runtime_error("invalid option provided for file grouping"); } diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 5465d25b3ab33..85faf7d5f69bd 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -13,8 +13,6 @@ o2_add_library(ITStracking SOURCES src/ClusterLines.cxx src/Cluster.cxx src/ROframe.cxx - src/Graph.cxx - src/DBScan.cxx src/IOUtils.cxx src/Label.cxx src/PrimaryVertexContext.cxx @@ -37,8 +35,9 @@ o2_add_library(ITStracking o2_target_root_dictionary(ITStracking HEADERS include/ITStracking/ClusterLines.h include/ITStracking/Tracklet.h - include/ITStracking/DBScan.h + include/ITStracking/Cluster.h include/ITStracking/TrackingConfigParam.h + include/ITStracking/StandaloneDebugger.h LINKDEF src/TrackingLinkDef.h) if(CUDA_ENABLED) diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/cuda/CMakeLists.txt index 939755448b999..4b38a02a1da3f 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/cuda/CMakeLists.txt @@ -21,7 +21,6 @@ o2_add_library(ITStrackingCUDA src/Utils.cu PUBLIC_LINK_LIBRARIES O2::ITStracking O2::SimConfig - cub::cub O2::SimulationDataFormat TARGETVARNAME targetName) diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Array.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Array.h index f59c9885e97c2..615b1eb3b9d6a 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Array.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Array.h @@ -21,7 +21,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { namespace @@ -64,7 +64,7 @@ struct Array final { T InternalArray[Size]; }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/ClusterLinesGPU.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/ClusterLinesGPU.h index bb77765d6acd3..02dd6902152e6 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/ClusterLinesGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/ClusterLinesGPU.h @@ -22,7 +22,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { struct GPUVertex final { @@ -66,7 +66,7 @@ class ClusterLinesGPU final float mVertex[3]; // cluster centroid position }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 #endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Context.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Context.h index 246dfa76a1979..4ce54b25b2ac4 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Context.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Context.h @@ -23,7 +23,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { struct DeviceProperties final { @@ -62,7 +62,7 @@ class Context final int mDevicesNum; std::vector<DeviceProperties> mDeviceProperties; }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreNV.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreNV.h index 2b17798c2d0d7..a70d69d8fdd55 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreNV.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreNV.h @@ -29,7 +29,8 @@ namespace o2 { namespace its { -namespace GPU + +namespace gpu { class DeviceStoreNV final @@ -38,81 +39,99 @@ class DeviceStoreNV final DeviceStoreNV(); UniquePointer<DeviceStoreNV> initialise(const float3&, - const std::array<std::vector<Cluster>, constants::its::LayersNumber>&, - const std::array<std::vector<Tracklet>, constants::its::TrackletsPerRoad>&, - const std::array<std::vector<Cell>, constants::its::CellsPerRoad>&, - const std::array<std::vector<int>, constants::its::CellsPerRoad - 1>&); + const std::array<std::vector<Cluster>, constants::its2::LayersNumber>&, + const std::array<std::vector<Tracklet>, constants::its2::TrackletsPerRoad>&, + const std::array<std::vector<Cell>, constants::its2::CellsPerRoad>&, + const std::array<std::vector<int>, constants::its2::CellsPerRoad - 1>&, + const std::array<float, constants::its2::LayersNumber>&, + const std::array<float, constants::its2::LayersNumber>&); GPU_DEVICE const float3& getPrimaryVertex(); - GPU_HOST_DEVICE Array<Vector<Cluster>, constants::its::LayersNumber>& getClusters(); - GPU_DEVICE Array<Array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, - constants::its::TrackletsPerRoad>& + GPU_HOST_DEVICE Array<Vector<Cluster>, constants::its2::LayersNumber>& getClusters(); + GPU_DEVICE Array<Array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, + constants::its2::TrackletsPerRoad>& getIndexTables(); - GPU_HOST_DEVICE Array<Vector<Tracklet>, constants::its::TrackletsPerRoad>& getTracklets(); - GPU_HOST_DEVICE Array<Vector<int>, constants::its::CellsPerRoad>& getTrackletsLookupTable(); - GPU_HOST_DEVICE Array<Vector<int>, constants::its::CellsPerRoad>& getTrackletsPerClusterTable(); - GPU_HOST_DEVICE Array<Vector<Cell>, constants::its::CellsPerRoad>& getCells(); - GPU_HOST_DEVICE Array<Vector<int>, constants::its::CellsPerRoad - 1>& getCellsLookupTable(); - GPU_HOST_DEVICE Array<Vector<int>, constants::its::CellsPerRoad - 1>& getCellsPerTrackletTable(); - Array<Vector<int>, constants::its::CellsPerRoad>& getTempTableArray(); + GPU_HOST_DEVICE Array<Vector<Tracklet>, constants::its2::TrackletsPerRoad>& getTracklets(); + GPU_HOST_DEVICE Array<Vector<int>, constants::its2::CellsPerRoad>& getTrackletsLookupTable(); + GPU_HOST_DEVICE Array<Vector<int>, constants::its2::CellsPerRoad>& getTrackletsPerClusterTable(); + GPU_HOST_DEVICE Array<Vector<Cell>, constants::its2::CellsPerRoad>& getCells(); + GPU_HOST_DEVICE Array<Vector<int>, constants::its2::CellsPerRoad - 1>& getCellsLookupTable(); + GPU_HOST_DEVICE Array<Vector<int>, constants::its2::CellsPerRoad - 1>& getCellsPerTrackletTable(); + Array<Vector<int>, constants::its2::CellsPerRoad>& getTempTableArray(); + + GPU_HOST_DEVICE float getRmin(int layer); + GPU_HOST_DEVICE float getRmax(int layer); private: UniquePointer<float3> mPrimaryVertex; - Array<Vector<Cluster>, constants::its::LayersNumber> mClusters; - Array<Array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, constants::its::TrackletsPerRoad> + Array<Vector<Cluster>, constants::its2::LayersNumber> mClusters; + Array<float, constants::its2::LayersNumber> mRmin; + Array<float, constants::its2::LayersNumber> mRmax; + Array<Array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, constants::its2::TrackletsPerRoad> mIndexTables; - Array<Vector<Tracklet>, constants::its::TrackletsPerRoad> mTracklets; - Array<Vector<int>, constants::its::CellsPerRoad> mTrackletsLookupTable; - Array<Vector<int>, constants::its::CellsPerRoad> mTrackletsPerClusterTable; - Array<Vector<Cell>, constants::its::CellsPerRoad> mCells; - Array<Vector<int>, constants::its::CellsPerRoad - 1> mCellsLookupTable; - Array<Vector<int>, constants::its::CellsPerRoad - 1> mCellsPerTrackletTable; + Array<Vector<Tracklet>, constants::its2::TrackletsPerRoad> mTracklets; + Array<Vector<int>, constants::its2::CellsPerRoad> mTrackletsLookupTable; + Array<Vector<int>, constants::its2::CellsPerRoad> mTrackletsPerClusterTable; + Array<Vector<Cell>, constants::its2::CellsPerRoad> mCells; + Array<Vector<int>, constants::its2::CellsPerRoad - 1> mCellsLookupTable; + Array<Vector<int>, constants::its2::CellsPerRoad - 1> mCellsPerTrackletTable; }; GPU_DEVICE inline const float3& DeviceStoreNV::getPrimaryVertex() { return *mPrimaryVertex; } -GPU_HOST_DEVICE inline Array<Vector<Cluster>, constants::its::LayersNumber>& DeviceStoreNV::getClusters() +GPU_HOST_DEVICE inline Array<Vector<Cluster>, constants::its2::LayersNumber>& DeviceStoreNV::getClusters() { return mClusters; } -GPU_DEVICE inline Array<Array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, - constants::its::TrackletsPerRoad>& +GPU_DEVICE inline Array<Array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, + constants::its2::TrackletsPerRoad>& DeviceStoreNV::getIndexTables() { return mIndexTables; } -GPU_DEVICE inline Array<Vector<Tracklet>, constants::its::TrackletsPerRoad>& DeviceStoreNV::getTracklets() +GPU_DEVICE inline Array<Vector<Tracklet>, constants::its2::TrackletsPerRoad>& DeviceStoreNV::getTracklets() { return mTracklets; } -GPU_DEVICE inline Array<Vector<int>, constants::its::CellsPerRoad>& DeviceStoreNV::getTrackletsLookupTable() +GPU_DEVICE inline Array<Vector<int>, constants::its2::CellsPerRoad>& DeviceStoreNV::getTrackletsLookupTable() { return mTrackletsLookupTable; } -GPU_DEVICE inline Array<Vector<int>, constants::its::CellsPerRoad>& DeviceStoreNV::getTrackletsPerClusterTable() +GPU_DEVICE inline Array<Vector<int>, constants::its2::CellsPerRoad>& DeviceStoreNV::getTrackletsPerClusterTable() { return mTrackletsPerClusterTable; } -GPU_HOST_DEVICE inline Array<Vector<Cell>, constants::its::CellsPerRoad>& DeviceStoreNV::getCells() +GPU_HOST_DEVICE inline Array<Vector<Cell>, constants::its2::CellsPerRoad>& DeviceStoreNV::getCells() { return mCells; } -GPU_HOST_DEVICE inline Array<Vector<int>, constants::its::CellsPerRoad - 1>& DeviceStoreNV::getCellsLookupTable() +GPU_HOST_DEVICE inline Array<Vector<int>, constants::its2::CellsPerRoad - 1>& DeviceStoreNV::getCellsLookupTable() { return mCellsLookupTable; } -GPU_HOST_DEVICE inline Array<Vector<int>, constants::its::CellsPerRoad - 1>& +GPU_HOST_DEVICE inline Array<Vector<int>, constants::its2::CellsPerRoad - 1>& DeviceStoreNV::getCellsPerTrackletTable() { return mCellsPerTrackletTable; } -} // namespace GPU + +GPU_HOST_DEVICE inline float DeviceStoreNV::getRmin(int layer) +{ + return mRmin[layer]; +} + +GPU_HOST_DEVICE inline float DeviceStoreNV::getRmax(int layer) +{ + return mRmax[layer]; +} + +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreVertexerGPU.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreVertexerGPU.h index 5d2d7a9d8cd78..ecbcdc24356d7 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreVertexerGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/DeviceStoreVertexerGPU.h @@ -16,7 +16,9 @@ #ifndef O2_ITS_TRACKING_DEVICE_STORE_VERTEXER_GPU_H_ #define O2_ITS_TRACKING_DEVICE_STORE_VERTEXER_GPU_H_ +#ifndef GPUCA_GPUCODE_GENRTC #include <cub/cub.cuh> +#endif #include "ITStracking/Cluster.h" #include "ITStracking/Constants.h" @@ -33,7 +35,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { enum class TrackletingLayerOrder { @@ -55,7 +57,7 @@ class DeviceStoreVertexerGPU final ~DeviceStoreVertexerGPU() = default; UniquePointer<DeviceStoreVertexerGPU> initialise(const std::array<std::vector<Cluster>, constants::its::LayersNumberVertexer>&, - const std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, + const std::array<std::array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, constants::its::LayersNumberVertexer>&); // RO APIs @@ -143,7 +145,7 @@ inline std::vector<int> DeviceStoreVertexerGPU::getNFoundTrackletsFromGPU(const std::vector<int> nFoundDuplets; nFoundDuplets.resize(sizes[1]); - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); } else { mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); @@ -163,7 +165,7 @@ inline std::vector<Tracklet> DeviceStoreVertexerGPU::getRawDupletsFromGPU(const std::vector<int> nFoundDuplets; nFoundDuplets.resize(sizes[1]); - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); mDuplets01.copyIntoSizedVector(tmpDuplets); } else { @@ -186,7 +188,7 @@ inline std::vector<Tracklet> DeviceStoreVertexerGPU::getDupletsFromGPU(const Ord nFoundDuplets.resize(sizes[1]); std::vector<Tracklet> shrinkedDuplets; - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); mDuplets01.copyIntoSizedVector(tmpDuplets); } else { @@ -242,7 +244,7 @@ inline std::vector<int> DeviceStoreVertexerGPU::getHistogramZFromGPU() inline void DeviceStoreVertexerGPU::updateDuplets(const Order order, std::vector<Tracklet>& duplets) { - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mDuplets01.reset(duplets.data(), static_cast<int>(duplets.size())); } else { mDuplets12.reset(duplets.data(), static_cast<int>(duplets.size())); @@ -251,7 +253,7 @@ inline void DeviceStoreVertexerGPU::updateDuplets(const Order order, std::vector inline void DeviceStoreVertexerGPU::updateFoundDuplets(const Order order, std::vector<int>& nDuplets) { - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].reset(nDuplets.data(), static_cast<int>(nDuplets.size())); } else { mNFoundDuplets[1].reset(nDuplets.data(), static_cast<int>(nDuplets.size())); @@ -294,7 +296,7 @@ inline std::vector<Line> DeviceStoreVertexerGPU::getLinesFromGPU() return lines; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 #endif //O2_ITS_TRACKING_DEVICE_STORE_VERTEXER_GPU_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/PrimaryVertexContextNV.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/PrimaryVertexContextNV.h index 51c30fdb10fa8..5eb5b2a459d33 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/PrimaryVertexContextNV.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/PrimaryVertexContextNV.h @@ -38,97 +38,98 @@ class PrimaryVertexContextNV final : public PrimaryVertexContext PrimaryVertexContextNV() = default; virtual ~PrimaryVertexContextNV() = default; - virtual void initialise(const MemoryParameters& memParam, const std::array<std::vector<Cluster>, constants::its::LayersNumber>& cl, - const std::array<float, 3>& pv, const int iteration); - - GPU::DeviceStoreNV& getDeviceContext(); - GPU::Array<GPU::Vector<Cluster>, constants::its::LayersNumber>& getDeviceClusters(); - GPU::Array<GPU::Vector<Tracklet>, constants::its::TrackletsPerRoad>& getDeviceTracklets(); - GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad>& getDeviceTrackletsLookupTable(); - GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad>& getDeviceTrackletsPerClustersTable(); - GPU::Array<GPU::Vector<Cell>, constants::its::CellsPerRoad>& getDeviceCells(); - GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad - 1>& getDeviceCellsLookupTable(); - GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad - 1>& getDeviceCellsPerTrackletTable(); - std::array<GPU::Vector<int>, constants::its::CellsPerRoad>& getTempTableArray(); - std::array<GPU::Vector<Tracklet>, constants::its::CellsPerRoad>& getTempTrackletArray(); - std::array<GPU::Vector<Cell>, constants::its::CellsPerRoad - 1>& getTempCellArray(); + void initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, + const std::vector<std::vector<Cluster>>& cl, const std::array<float, 3>& pv, const int iteration) override; + + gpu::DeviceStoreNV& getDeviceContext(); + gpu::Array<gpu::Vector<Cluster>, constants::its2::LayersNumber>& getDeviceClusters(); + gpu::Array<gpu::Vector<Tracklet>, constants::its2::TrackletsPerRoad>& getDeviceTracklets(); + gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad>& getDeviceTrackletsLookupTable(); + gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad>& getDeviceTrackletsPerClustersTable(); + gpu::Array<gpu::Vector<Cell>, constants::its2::CellsPerRoad>& getDeviceCells(); + gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad - 1>& getDeviceCellsLookupTable(); + gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad - 1>& getDeviceCellsPerTrackletTable(); + std::array<gpu::Vector<int>, constants::its2::CellsPerRoad>& getTempTableArray(); + std::array<gpu::Vector<Tracklet>, constants::its2::CellsPerRoad>& getTempTrackletArray(); + std::array<gpu::Vector<Cell>, constants::its2::CellsPerRoad - 1>& getTempCellArray(); void updateDeviceContext(); private: - GPU::DeviceStoreNV mGPUContext; - GPU::UniquePointer<GPU::DeviceStoreNV> mGPUContextDevicePointer; - std::array<GPU::Vector<int>, constants::its::CellsPerRoad> mTempTableArray; - std::array<GPU::Vector<Tracklet>, constants::its::CellsPerRoad> mTempTrackletArray; - std::array<GPU::Vector<Cell>, constants::its::CellsPerRoad - 1> mTempCellArray; + gpu::DeviceStoreNV mGPUContext; + gpu::UniquePointer<gpu::DeviceStoreNV> mGPUContextDevicePointer; + std::array<gpu::Vector<int>, constants::its2::CellsPerRoad> mTempTableArray; + std::array<gpu::Vector<Tracklet>, constants::its2::CellsPerRoad> mTempTrackletArray; + std::array<gpu::Vector<Cell>, constants::its2::CellsPerRoad - 1> mTempCellArray; }; -inline GPU::DeviceStoreNV& PrimaryVertexContextNV::getDeviceContext() +inline gpu::DeviceStoreNV& PrimaryVertexContextNV::getDeviceContext() { return *mGPUContextDevicePointer; } -inline GPU::Array<GPU::Vector<Cluster>, constants::its::LayersNumber>& PrimaryVertexContextNV::getDeviceClusters() +inline gpu::Array<gpu::Vector<Cluster>, constants::its2::LayersNumber>& PrimaryVertexContextNV::getDeviceClusters() { return mGPUContext.getClusters(); } -inline GPU::Array<GPU::Vector<Tracklet>, constants::its::TrackletsPerRoad>& PrimaryVertexContextNV::getDeviceTracklets() +inline gpu::Array<gpu::Vector<Tracklet>, constants::its2::TrackletsPerRoad>& PrimaryVertexContextNV::getDeviceTracklets() { return mGPUContext.getTracklets(); } -inline GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad>& PrimaryVertexContextNV::getDeviceTrackletsLookupTable() +inline gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceTrackletsLookupTable() { return mGPUContext.getTrackletsLookupTable(); } -inline GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad>& +inline gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceTrackletsPerClustersTable() { return mGPUContext.getTrackletsPerClusterTable(); } -inline GPU::Array<GPU::Vector<Cell>, constants::its::CellsPerRoad>& PrimaryVertexContextNV::getDeviceCells() +inline gpu::Array<gpu::Vector<Cell>, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceCells() { return mGPUContext.getCells(); } -inline GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad - 1>& PrimaryVertexContextNV::getDeviceCellsLookupTable() +inline gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getDeviceCellsLookupTable() { return mGPUContext.getCellsLookupTable(); } -inline GPU::Array<GPU::Vector<int>, constants::its::CellsPerRoad - 1>& +inline gpu::Array<gpu::Vector<int>, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getDeviceCellsPerTrackletTable() { return mGPUContext.getCellsPerTrackletTable(); } -inline std::array<GPU::Vector<int>, constants::its::CellsPerRoad>& PrimaryVertexContextNV::getTempTableArray() +inline std::array<gpu::Vector<int>, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getTempTableArray() { return mTempTableArray; } -inline std::array<GPU::Vector<Tracklet>, constants::its::CellsPerRoad>& PrimaryVertexContextNV::getTempTrackletArray() +inline std::array<gpu::Vector<Tracklet>, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getTempTrackletArray() { return mTempTrackletArray; } -inline std::array<GPU::Vector<Cell>, constants::its::CellsPerRoad - 1>& PrimaryVertexContextNV::getTempCellArray() +inline std::array<gpu::Vector<Cell>, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getTempCellArray() { return mTempCellArray; } inline void PrimaryVertexContextNV::updateDeviceContext() { - mGPUContextDevicePointer = GPU::UniquePointer<GPU::DeviceStoreNV>{mGPUContext}; + mGPUContextDevicePointer = gpu::UniquePointer<gpu::DeviceStoreNV>{mGPUContext}; } -inline void PrimaryVertexContextNV::initialise(const MemoryParameters& memParam, const std::array<std::vector<Cluster>, constants::its::LayersNumber>& cl, - const std::array<float, 3>& pv, const int iteration) +inline void PrimaryVertexContextNV::initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, + const std::vector<std::vector<Cluster>>& cl, const std::array<float, 3>& pv, const int iteration) { - this->PrimaryVertexContext::initialise(memParam, cl, pv, iteration); - mGPUContextDevicePointer = mGPUContext.initialise(mPrimaryVertex, mClusters, mTracklets, mCells, mCellsLookupTable); + ///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 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Stream.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Stream.h index 52460ad6ff955..f00b5f374c884 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Stream.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Stream.h @@ -21,7 +21,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { class Stream final @@ -39,7 +39,7 @@ class Stream final private: GPUStream mStream; }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/TrackerTraitsNV.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/TrackerTraitsNV.h index ea90e52114428..b7bac25a170dd 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/TrackerTraitsNV.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/TrackerTraitsNV.h @@ -30,11 +30,11 @@ class TrackerTraitsNV : public TrackerTraits { public: TrackerTraitsNV(); - virtual ~TrackerTraitsNV(); + ~TrackerTraitsNV() override; void computeLayerCells() final; void computeLayerTracklets() final; - void refitTracks(const std::array<std::vector<TrackingFrameInfo>, 7>& tf, std::vector<TrackITSExt>& tracks) final; + void refitTracks(const std::vector<std::vector<TrackingFrameInfo>>& tf, std::vector<TrackITSExt>& tracks) override; }; extern "C" TrackerTraits* createTrackerTraitsNV(); diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/UniquePointer.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/UniquePointer.h index e82d58f32add2..74ff53865f36d 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/UniquePointer.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/UniquePointer.h @@ -21,7 +21,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { namespace @@ -81,8 +81,8 @@ UniquePointer<T>::UniquePointer(const T& ref) { try { - Utils::Host::gpuMalloc(reinterpret_cast<void**>(&mDevicePointer), sizeof(T)); - Utils::Host::gpuMemcpyHostToDevice(mDevicePointer, &ref, sizeof(T)); + utils::host::gpuMalloc(reinterpret_cast<void**>(&mDevicePointer), sizeof(T)); + utils::host::gpuMemcpyHostToDevice(mDevicePointer, &ref, sizeof(T)); } catch (...) { @@ -118,7 +118,7 @@ void UniquePointer<T>::destroy() { if (mDevicePointer != nullptr) { - Utils::Host::gpuFree(mDevicePointer); + utils::host::gpuFree(mDevicePointer); } } @@ -145,7 +145,7 @@ GPU_HOST_DEVICE const T& UniquePointer<T>::operator*() const noexcept { return PointerTraits::getReference(mDevicePointer); } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Utils.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Utils.h index 5babb15320f6b..54dacb9217207 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Utils.h @@ -22,13 +22,13 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { -namespace Utils +namespace utils { -namespace Host +namespace host { #ifdef __CUDACC__ @@ -49,16 +49,16 @@ void gpuMemcpyHostToDeviceAsync(void*, const void*, int, Stream&); void gpuMemcpyDeviceToHost(void*, const void*, int); // void gpuStartProfiler(); // void gpuStopProfiler(); -} // namespace Host +} // namespace host -namespace Device +namespace device { GPUd() int getLaneIndex(); GPUd() int shareToWarp(const int, const int); GPUd() int gpuAtomicAdd(int*, const int); -} // namespace Device +} // namespace device } // namespace Utils -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Vector.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Vector.h index bf09f269e0449..5e7d58f253eb5 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Vector.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/Vector.h @@ -15,7 +15,7 @@ #ifndef TRAKINGITSU_INCLUDE_GPU_VECTOR_H_ #define TRAKINGITSU_INCLUDE_GPU_VECTOR_H_ -#include <assert.h> +#include <cassert> #include <new> #include <type_traits> #include <vector> @@ -28,7 +28,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { template <typename T> @@ -100,17 +100,17 @@ Vector<T>::Vector(const T* const source, const int size, const int initialSize) if (size > 0) { try { - Utils::Host::gpuMalloc(reinterpret_cast<void**>(&mArrayPointer), size * sizeof(T)); - Utils::Host::gpuMalloc(reinterpret_cast<void**>(&mDeviceSize), sizeof(int)); + utils::host::gpuMalloc(reinterpret_cast<void**>(&mArrayPointer), size * sizeof(T)); + utils::host::gpuMalloc(reinterpret_cast<void**>(&mDeviceSize), sizeof(int)); if (source != nullptr) { - Utils::Host::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); - Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + utils::host::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); + utils::host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); } else { - Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); + utils::host::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); } } catch (...) { @@ -179,7 +179,7 @@ template <typename T> int Vector<T>::getSizeFromDevice() const { int size; - Utils::Host::gpuMemcpyDeviceToHost(&size, mDeviceSize, sizeof(int)); + utils::host::gpuMemcpyDeviceToHost(&size, mDeviceSize, sizeof(int)); return size; } @@ -187,7 +187,7 @@ int Vector<T>::getSizeFromDevice() const template <typename T> void Vector<T>::resize(const int size) { - Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + utils::host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); } template <typename T> @@ -201,21 +201,21 @@ void Vector<T>::reset(const T* const source, const int size, const int initialSi { if (size > mCapacity) { if (mArrayPointer != nullptr) { - Utils::Host::gpuFree(mArrayPointer); + utils::host::gpuFree(mArrayPointer); } - Utils::Host::gpuMalloc(reinterpret_cast<void**>(&mArrayPointer), size * sizeof(T)); + utils::host::gpuMalloc(reinterpret_cast<void**>(&mArrayPointer), size * sizeof(T)); mCapacity = size; } if (source != nullptr) { - Utils::Host::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); - Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + utils::host::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); + utils::host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); } else { if (mDeviceSize == nullptr) { - Utils::Host::gpuMalloc(reinterpret_cast<void**>(&mDeviceSize), sizeof(int)); + utils::host::gpuMalloc(reinterpret_cast<void**>(&mDeviceSize), sizeof(int)); } - Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); + utils::host::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); } } @@ -228,7 +228,7 @@ void Vector<T>::copyIntoVector(std::vector<T>& destinationVector, const int size try { hostPrimitivePointer = static_cast<T*>(malloc(size * sizeof(T))); - Utils::Host::gpuMemcpyDeviceToHost(hostPrimitivePointer, mArrayPointer, size * sizeof(T)); + utils::host::gpuMemcpyDeviceToHost(hostPrimitivePointer, mArrayPointer, size * sizeof(T)); destinationVector = std::move(std::vector<T>(hostPrimitivePointer, hostPrimitivePointer + size)); @@ -246,7 +246,7 @@ void Vector<T>::copyIntoVector(std::vector<T>& destinationVector, const int size template <typename T> void Vector<T>::copyIntoSizedVector(std::vector<T>& destinationVector) { - Utils::Host::gpuMemcpyDeviceToHost(destinationVector.data(), mArrayPointer, destinationVector.size() * sizeof(T)); + utils::host::gpuMemcpyDeviceToHost(destinationVector.data(), mArrayPointer, destinationVector.size() * sizeof(T)); } template <typename T> @@ -254,12 +254,12 @@ inline void Vector<T>::destroy() { if (mArrayPointer != nullptr) { - Utils::Host::gpuFree(mArrayPointer); + utils::host::gpuFree(mArrayPointer); } if (mDeviceSize != nullptr) { - Utils::Host::gpuFree(mDeviceSize); + utils::host::gpuFree(mDeviceSize); } } @@ -291,7 +291,7 @@ template <typename T> GPU_HOST inline T Vector<T>::getElementFromDevice(const int index) const { T element; - Utils::Host::gpuMemcpyDeviceToHost(&element, mArrayPointer + index, sizeof(T)); + utils::host::gpuMemcpyDeviceToHost(&element, mArrayPointer + index, sizeof(T)); return element; } @@ -305,7 +305,7 @@ GPU_DEVICE inline int Vector<T>::size() const template <typename T> GPU_DEVICE int Vector<T>::extend(const int sizeIncrement) const { - const int startIndex = Utils::Device::gpuAtomicAdd(mDeviceSize, sizeIncrement); + const int startIndex = utils::device::gpuAtomicAdd(mDeviceSize, sizeIncrement); assert(size() <= mCapacity); return startIndex; @@ -324,7 +324,7 @@ GPU_HOST_DEVICE void Vector<T>::dump() printf("mArrayPointer = %p\nmDeviceSize = %p\nmCapacity = %d\nmIsWeak = %s\n", mArrayPointer, mDeviceSize, mCapacity, mIsWeak ? "true" : "false"); } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/VertexerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/VertexerTraitsGPU.h index a3447e55c586f..8ae1b578cc261 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/VertexerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/cuda/include/ITStrackingCUDA/VertexerTraitsGPU.h @@ -37,17 +37,17 @@ namespace its { class ROframe; -using constants::index_table::InversePhiBinSize; +using constants::its2::InversePhiBinSize; class VertexerTraitsGPU : public VertexerTraits { public: #ifdef _ALLOW_DEBUG_TREES_ITS_ VertexerTraitsGPU(); - virtual ~VertexerTraitsGPU(); + ~VertexerTraitsGPU() override; #else VertexerTraitsGPU(); - virtual ~VertexerTraitsGPU() = default; + ~VertexerTraitsGPU() = default; #endif void initialise(ROframe*) override; void computeTracklets() override; @@ -59,23 +59,23 @@ class VertexerTraitsGPU : public VertexerTraits // GPU-specific getters GPUd() static const int2 getBinsPhiRectWindow(const Cluster&, float maxdeltaphi); - GPUhd() GPU::DeviceStoreVertexerGPU& getDeviceContext(); + GPUhd() gpu::DeviceStoreVertexerGPU& getDeviceContext(); protected: - GPU::DeviceStoreVertexerGPU mStoreVertexerGPU; - GPU::UniquePointer<GPU::DeviceStoreVertexerGPU> mStoreVertexerGPUPtr; + gpu::DeviceStoreVertexerGPU mStoreVertexerGPU; + gpu::UniquePointer<gpu::DeviceStoreVertexerGPU> mStoreVertexerGPUPtr; }; inline GPUd() const int2 VertexerTraitsGPU::getBinsPhiRectWindow(const Cluster& currentCluster, float phiCut) { // This function returns the lowest PhiBin and the number of phi bins to be spanned, In the form int2{phiBinLow, PhiBinSpan} - const int phiBinMin{index_table_utils::getPhiBinIndex( + const int phiBinMin{constants::its2::getPhiBinIndex( math_utils::getNormalizedPhiCoordinate(currentCluster.phiCoordinate - phiCut))}; const int phiBinSpan{static_cast<int>(MATH_CEIL(phiCut * InversePhiBinSize))}; return int2{phiBinMin, phiBinSpan}; } -inline GPU::DeviceStoreVertexerGPU& VertexerTraitsGPU::getDeviceContext() +inline gpu::DeviceStoreVertexerGPU& VertexerTraitsGPU::getDeviceContext() { return *mStoreVertexerGPUPtr; } diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/ClusterLinesGPU.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/ClusterLinesGPU.cu index 9c1f048292b52..64d861ad16091 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/ClusterLinesGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/ClusterLinesGPU.cu @@ -14,7 +14,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { GPUd() ClusterLinesGPU::ClusterLinesGPU(const Line& firstLine, const Line& secondLine) @@ -129,6 +129,6 @@ GPUd() void ClusterLinesGPU::computeClusterCentroid() mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / determinant; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/Context.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/Context.cu index 633b4c4abf9f1..ef73009aa38e7 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/Context.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/Context.cu @@ -77,10 +77,10 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { -using Utils::Host::checkCUDAError; +using utils::host::checkCUDAError; Context::Context(bool dumpDevices) { @@ -169,6 +169,6 @@ const DeviceProperties& Context::getDeviceProperties(const int deviceIndex) return mDeviceProperties[deviceIndex]; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreNV.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreNV.cu index 353804900e5e1..a6c5a259be63c 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreNV.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreNV.cu @@ -23,7 +23,7 @@ namespace using namespace o2::its; -__device__ void fillIndexTables(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +__device__ void fillIndexTables(o2::its::gpu::DeviceStoreNV& primaryVertexContext, const int layerIndex) { const int currentClusterIndex{static_cast<int>(blockDim.x * blockIdx.x + threadIdx.x)}; @@ -57,7 +57,7 @@ __device__ void fillIndexTables(GPU::DeviceStoreNV& primaryVertexContext, const if (currentClusterIndex == nextLayerClustersNum - 1) { - for (int iBin{currentBinIndex + 1}; iBin <= o2::its::constants::index_table::ZBins * o2::its::constants::index_table::PhiBins; + for (int iBin{currentBinIndex + 1}; iBin <= o2::its::constants::its2::ZBins * o2::its::constants::its2::PhiBins; iBin++) { primaryVertexContext.getIndexTables()[layerIndex][iBin] = nextLayerClustersNum; @@ -66,7 +66,7 @@ __device__ void fillIndexTables(GPU::DeviceStoreNV& primaryVertexContext, const } } -__device__ void fillTrackletsPerClusterTables(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +__device__ void fillTrackletsPerClusterTables(o2::its::gpu::DeviceStoreNV& primaryVertexContext, const int layerIndex) { const int currentClusterIndex{static_cast<int>(blockDim.x * blockIdx.x + threadIdx.x)}; const int clustersSize{static_cast<int>(primaryVertexContext.getClusters()[layerIndex + 1].size())}; @@ -77,7 +77,7 @@ __device__ void fillTrackletsPerClusterTables(GPU::DeviceStoreNV& primaryVertexC } } -__device__ void fillCellsPerClusterTables(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +__device__ void fillCellsPerClusterTables(o2::its::gpu::DeviceStoreNV& primaryVertexContext, const int layerIndex) { const int totalThreadNum{static_cast<int>(primaryVertexContext.getClusters()[layerIndex + 1].size())}; const int trackletsSize{static_cast<int>(primaryVertexContext.getTracklets()[layerIndex + 1].capacity())}; @@ -92,16 +92,16 @@ __device__ void fillCellsPerClusterTables(GPU::DeviceStoreNV& primaryVertexConte } } -__global__ void fillDeviceStructures(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +__global__ void fillDeviceStructures(o2::its::gpu::DeviceStoreNV& primaryVertexContext, const int layerIndex) { fillIndexTables(primaryVertexContext, layerIndex); - if (layerIndex < o2::its::constants::its::CellsPerRoad) { + if (layerIndex < o2::its::constants::its2::CellsPerRoad) { fillTrackletsPerClusterTables(primaryVertexContext, layerIndex); } - if (layerIndex < o2::its::constants::its::CellsPerRoad - 1) { + if (layerIndex < o2::its::constants::its2::CellsPerRoad - 1) { fillCellsPerClusterTables(primaryVertexContext, layerIndex); } @@ -112,39 +112,40 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { -DeviceStoreNV::DeviceStoreNV() -{ - // Nothing to do -} +DeviceStoreNV::DeviceStoreNV() = default; UniquePointer<DeviceStoreNV> DeviceStoreNV::initialise(const float3& primaryVertex, - const std::array<std::vector<Cluster>, constants::its::LayersNumber>& clusters, - const std::array<std::vector<Tracklet>, constants::its::TrackletsPerRoad>& tracklets, - const std::array<std::vector<Cell>, constants::its::CellsPerRoad>& cells, - const std::array<std::vector<int>, constants::its::CellsPerRoad - 1>& cellsLookupTable) + const std::array<std::vector<Cluster>, constants::its2::LayersNumber>& clusters, + const std::array<std::vector<Tracklet>, constants::its2::TrackletsPerRoad>& tracklets, + const std::array<std::vector<Cell>, constants::its2::CellsPerRoad>& cells, + const std::array<std::vector<int>, constants::its2::CellsPerRoad - 1>& cellsLookupTable, + const std::array<float, constants::its2::LayersNumber>& rmin, + const std::array<float, constants::its2::LayersNumber>& rmax) { mPrimaryVertex = UniquePointer<float3>{primaryVertex}; - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::LayersNumber; ++iLayer) { + this->mRmin[iLayer] = rmin[iLayer]; + this->mRmax[iLayer] = rmax[iLayer]; this->mClusters[iLayer] = Vector<Cluster>{&clusters[iLayer][0], static_cast<int>(clusters[iLayer].size())}; - if (iLayer < constants::its::TrackletsPerRoad) { + if (iLayer < constants::its2::TrackletsPerRoad) { this->mTracklets[iLayer].reset(tracklets[iLayer].capacity()); } - if (iLayer < constants::its::CellsPerRoad) { + if (iLayer < constants::its2::CellsPerRoad) { this->mTrackletsLookupTable[iLayer].reset(static_cast<int>(clusters[iLayer + 1].size())); this->mTrackletsPerClusterTable[iLayer].reset(static_cast<int>(clusters[iLayer + 1].size())); this->mCells[iLayer].reset(static_cast<int>(cells[iLayer].capacity())); } - if (iLayer < constants::its::CellsPerRoad - 1) { + if (iLayer < constants::its2::CellsPerRoad - 1) { this->mCellsLookupTable[iLayer].reset(static_cast<int>(cellsLookupTable[iLayer].size())); this->mCellsPerTrackletTable[iLayer].reset(static_cast<int>(cellsLookupTable[iLayer].size())); @@ -153,14 +154,14 @@ UniquePointer<DeviceStoreNV> DeviceStoreNV::initialise(const float3& primaryVert UniquePointer<DeviceStoreNV> gpuContextDevicePointer{*this}; - std::array<Stream, constants::its::LayersNumber> streamArray; + std::array<Stream, constants::its2::LayersNumber> streamArray; - for (int iLayer{0}; iLayer < constants::its::TrackletsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::TrackletsPerRoad; ++iLayer) { const int nextLayerClustersNum = static_cast<int>(clusters[iLayer + 1].size()); - dim3 threadsPerBlock{Utils::Host::getBlockSize(nextLayerClustersNum)}; - dim3 blocksGrid{Utils::Host::getBlocksGrid(threadsPerBlock, nextLayerClustersNum)}; + dim3 threadsPerBlock{utils::host::getBlockSize(nextLayerClustersNum)}; + dim3 blocksGrid{utils::host::getBlocksGrid(threadsPerBlock, nextLayerClustersNum)}; fillDeviceStructures<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(*gpuContextDevicePointer, iLayer); @@ -179,6 +180,6 @@ UniquePointer<DeviceStoreNV> DeviceStoreNV::initialise(const float3& primaryVert return gpuContextDevicePointer; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreVertexerGPU.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreVertexerGPU.cu index cdcec77a16029..0830ff6d2b529 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreVertexerGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/DeviceStoreVertexerGPU.cu @@ -21,7 +21,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { GPUg() void defaultInitArrayKernel(int* array, const size_t arraySize, const int initValue = 0) { @@ -47,7 +47,7 @@ DeviceStoreVertexerGPU::DeviceStoreVertexerGPU() mBeamPosition = Vector<float>{2, 2}; for (int iTable{0}; iTable < 2; ++iTable) { - mIndexTables[iTable] = Vector<int>{constants::index_table::ZBins * constants::index_table::PhiBins + 1}; // 2*20*20+1 * sizeof(int) = 802B + mIndexTables[iTable] = Vector<int>{constants::its2::ZBins * constants::its2::PhiBins + 1}; // 2*20*20+1 * sizeof(int) = 802B } for (int iLayer{0}; iLayer < constants::its::LayersNumberVertexer; ++iLayer) { // 4e4 * 3 * sizof(Cluster) = 3.36MB mClusters[iLayer] = Vector<Cluster>{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; @@ -65,10 +65,10 @@ DeviceStoreVertexerGPU::DeviceStoreVertexerGPU() } mSizes = Vector<int>{constants::its::LayersNumberVertexer}; #endif -} // namespace GPU +} // namespace gpu UniquePointer<DeviceStoreVertexerGPU> DeviceStoreVertexerGPU::initialise(const std::array<std::vector<Cluster>, constants::its::LayersNumberVertexer>& clusters, - const std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, + const std::array<std::array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, constants::its::LayersNumberVertexer>& indexTables) { #ifdef _ALLOW_DEBUG_TREES_ITS_ @@ -83,8 +83,8 @@ UniquePointer<DeviceStoreVertexerGPU> DeviceStoreVertexerGPU::initialise(const s mIndexTables[0].reset(indexTables[0].data(), static_cast<int>(indexTables[0].size())); mIndexTables[1].reset(indexTables[2].data(), static_cast<int>(indexTables[2].size())); - const dim3 threadsPerBlock{Utils::Host::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{utils::host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{utils::host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; UniquePointer<DeviceStoreVertexerGPU> deviceStoreVertexerPtr{*this}; @@ -104,6 +104,6 @@ GPUd() const Vector<int>& DeviceStoreVertexerGPU::getIndexTable(const VertexerLa return mIndexTables[1]; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/Stream.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/Stream.cu index 6313bac5157e5..1734a5be7a332 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/Stream.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/Stream.cu @@ -18,7 +18,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { Stream::Stream() @@ -36,6 +36,6 @@ const GPUStream& Stream::get() const return mStream; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/TrackerTraitsNV.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/TrackerTraitsNV.cu index de818bfe605ca..48bb31053c875 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/TrackerTraitsNV.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/TrackerTraitsNV.cu @@ -18,9 +18,10 @@ #include <sstream> #include <iostream> +#ifndef GPUCA_GPUCODE_GENRTC #include <cooperative_groups.h> - #include "cub/cub.cuh" +#endif #include "ITStracking/Constants.h" #include "ITStracking/Configuration.h" @@ -36,10 +37,52 @@ namespace o2 { namespace its { -namespace GPU + +using namespace constants::its2; +GPU_DEVICE const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, + const float z1, const float z2, float maxdeltaz, float maxdeltaphi) { + const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; + const float phiRangeMin = currentCluster.phiCoordinate - maxdeltaphi; + const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; + const float phiRangeMax = currentCluster.phiCoordinate + maxdeltaphi; + + if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || + zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { -__constant__ TrackingParameters kTrkPar; + 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)}; +} + +namespace gpu +{ + +struct StaticTrackingParameters { + StaticTrackingParameters& operator=(const StaticTrackingParameters& t); + + int CellMinimumLevel(); + + /// General parameters + int ClusterSharing = 0; + int MinTrackLength = 7; + /// Trackleting cuts + float TrackletMaxDeltaPhi = 0.3f; + float TrackletMaxDeltaZ[constants::its2::TrackletsPerRoad] = {0.1f, 0.1f, 0.3f, 0.3f, 0.3f, 0.3f}; + /// Cell finding cuts + float CellMaxDeltaTanLambda = 0.025f; + float CellMaxDCA[constants::its2::CellsPerRoad] = {0.05f, 0.04f, 0.05f, 0.2f, 0.4f}; + float CellMaxDeltaPhi = 0.14f; + float CellMaxDeltaZ[constants::its2::CellsPerRoad] = {0.2f, 0.4f, 0.5f, 0.6f, 3.0f}; + /// Neighbour finding cuts + float NeighbourMaxDeltaCurvature[constants::its2::CellsPerRoad - 1] = {0.008f, 0.0025f, 0.003f, 0.0035f}; + float NeighbourMaxDeltaN[constants::its2::CellsPerRoad - 1] = {0.002f, 0.0090f, 0.002f, 0.005f}; +}; +__constant__ StaticTrackingParameters kTrkPar; __device__ void computeLayerTracklets(DeviceStoreNV& devStore, const int layerIndex, Vector<Tracklet>& trackletsVector) @@ -58,10 +101,11 @@ __device__ void computeLayerTracklets(DeviceStoreNV& devStore, const int layerIn }*/ const float tanLambda{(currentCluster.zCoordinate - devStore.getPrimaryVertex().z) / currentCluster.rCoordinate}; - const float directionZIntersection{tanLambda * ((constants::its::LayersRCoordinate())[layerIndex + 1] - currentCluster.rCoordinate) + currentCluster.zCoordinate}; + const float zAtRmin{tanLambda * (devStore.getRmin(layerIndex + 1) - currentCluster.rCoordinate) + currentCluster.zCoordinate}; + const float zAtRmax{tanLambda * (devStore.getRmax(layerIndex + 1) - currentCluster.rCoordinate) + currentCluster.zCoordinate}; - const int4 selectedBinsRect{TrackerTraits::getBinsRect(currentCluster, layerIndex, directionZIntersection, - kTrkPar.TrackletMaxDeltaZ[layerIndex], kTrkPar.TrackletMaxDeltaPhi)}; + const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, zAtRmin, zAtRmax, + kTrkPar.TrackletMaxDeltaZ[layerIndex], kTrkPar.TrackletMaxDeltaPhi)}; if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { @@ -70,13 +114,13 @@ __device__ void computeLayerTracklets(DeviceStoreNV& devStore, const int layerIn if (phiBinsNum < 0) { - phiBinsNum += constants::index_table::PhiBins; + phiBinsNum += constants::its2::PhiBins; } for (int iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; - iPhiBin = ++iPhiBin == constants::index_table::PhiBins ? 0 : iPhiBin, iPhiCount++) { + iPhiBin = ++iPhiBin == constants::its2::PhiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int firstBinIndex{constants::its2::getBinIndex(selectedBinsRect.x, iPhiBin)}; const int firstRowClusterIndex = devStore.getIndexTables()[layerIndex][firstBinIndex]; const int maxRowClusterIndex = devStore.getIndexTables()[layerIndex][{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}]; @@ -85,11 +129,11 @@ __device__ void computeLayerTracklets(DeviceStoreNV& devStore, const int layerIn const Cluster& nextCluster{nextLayerClusters[iNextLayerCluster]}; - const float deltaZ{gpu::GPUCommonMath::Abs( + const float deltaZ{o2::gpu::GPUCommonMath::Abs( tanLambda * (nextCluster.rCoordinate - currentCluster.rCoordinate) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - const float deltaPhi{gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate)}; - if (deltaZ < kTrkPar.TrackletMaxDeltaZ[layerIndex] && (deltaPhi < kTrkPar.TrackletMaxDeltaPhi || gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.TrackletMaxDeltaPhi)) { + if (deltaZ < kTrkPar.TrackletMaxDeltaZ[layerIndex] && (deltaPhi < kTrkPar.TrackletMaxDeltaPhi || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.TrackletMaxDeltaPhi)) { cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); int currentIndex{}; @@ -145,14 +189,14 @@ __device__ void computeLayerCells(DeviceStoreNV& devStore, const int layerIndex, iNextLayerTracklet < nextLayerTrackletsNum && devStore.getTracklets()[layerIndex + 1][iNextLayerTracklet].firstClusterIndex == nextLayerClusterIndex; ++iNextLayerTracklet) { const Tracklet& nextTracklet{devStore.getTracklets()[layerIndex + 1][iNextLayerTracklet]}; - const float deltaTanLambda{gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - const float deltaPhi{gpu::GPUCommonMath::Abs(currentTracklet.phiCoordinate - nextTracklet.phiCoordinate)}; + const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentTracklet.phiCoordinate - nextTracklet.phiCoordinate)}; - if (deltaTanLambda < kTrkPar.CellMaxDeltaTanLambda && (deltaPhi < kTrkPar.CellMaxDeltaPhi || gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.CellMaxDeltaPhi)) { + if (deltaTanLambda < kTrkPar.CellMaxDeltaTanLambda && (deltaPhi < kTrkPar.CellMaxDeltaPhi || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.CellMaxDeltaPhi)) { const float averageTanLambda{0.5f * (currentTracklet.tanLambda + nextTracklet.tanLambda)}; const float directionZIntersection{-averageTanLambda * firstCellCluster.rCoordinate + firstCellCluster.zCoordinate}; - const float deltaZ{gpu::GPUCommonMath::Abs(directionZIntersection - primaryVertex.z)}; + const float deltaZ{o2::gpu::GPUCommonMath::Abs(directionZIntersection - primaryVertex.z)}; if (deltaZ < kTrkPar.CellMaxDeltaZ[layerIndex]) { @@ -166,20 +210,20 @@ __device__ void computeLayerCells(DeviceStoreNV& devStore, const int layerIndex, float3 cellPlaneNormalVector{math_utils::crossProduct(firstDeltaVector, secondDeltaVector)}; - const float vectorNorm{gpu::GPUCommonMath::Sqrt( + const float vectorNorm{o2::gpu::GPUCommonMath::Sqrt( cellPlaneNormalVector.x * cellPlaneNormalVector.x + cellPlaneNormalVector.y * cellPlaneNormalVector.y + cellPlaneNormalVector.z * cellPlaneNormalVector.z)}; - if (!(vectorNorm < constants::math::FloatMinThreshold || gpu::GPUCommonMath::Abs(cellPlaneNormalVector.z) < constants::math::FloatMinThreshold)) { + if (!(vectorNorm < constants::math::FloatMinThreshold || o2::gpu::GPUCommonMath::Abs(cellPlaneNormalVector.z) < constants::math::FloatMinThreshold)) { const float inverseVectorNorm{1.0f / vectorNorm}; const float3 normalizedPlaneVector{cellPlaneNormalVector.x * inverseVectorNorm, cellPlaneNormalVector.y * inverseVectorNorm, cellPlaneNormalVector.z * inverseVectorNorm}; const float planeDistance{-normalizedPlaneVector.x * (secondCellCluster.xCoordinate - primaryVertex.x) - (normalizedPlaneVector.y * secondCellCluster.yCoordinate - primaryVertex.y) - normalizedPlaneVector.z * secondCellClusterQuadraticRCoordinate}; const float normalizedPlaneVectorQuadraticZCoordinate{normalizedPlaneVector.z * normalizedPlaneVector.z}; - const float cellTrajectoryRadius{gpu::GPUCommonMath::Sqrt( + const float cellTrajectoryRadius{o2::gpu::GPUCommonMath::Sqrt( (1.0f - normalizedPlaneVectorQuadraticZCoordinate - 4.0f * planeDistance * normalizedPlaneVector.z) / (4.0f * normalizedPlaneVectorQuadraticZCoordinate))}; const float2 circleCenter{-0.5f * normalizedPlaneVector.x / normalizedPlaneVector.z, -0.5f * normalizedPlaneVector.y / normalizedPlaneVector.z}; - const float distanceOfClosestApproach{gpu::GPUCommonMath::Abs( - cellTrajectoryRadius - gpu::GPUCommonMath::Sqrt(circleCenter.x * circleCenter.x + circleCenter.y * circleCenter.y))}; + const float distanceOfClosestApproach{o2::gpu::GPUCommonMath::Abs( + cellTrajectoryRadius - o2::gpu::GPUCommonMath::Sqrt(circleCenter.x * circleCenter.x + circleCenter.y * circleCenter.y))}; if (distanceOfClosestApproach <= kTrkPar.CellMaxDCA[layerIndex]) { @@ -259,7 +303,7 @@ __global__ void sortCellsKernel(DeviceStoreNV& devStore, const int layerIndex, } } -} // namespace GPU +} // namespace gpu TrackerTraits* createTrackerTraitsNV() { @@ -280,12 +324,12 @@ void TrackerTraitsNV::computeLayerTracklets() { PrimaryVertexContextNV* primaryVertexContext = static_cast<PrimaryVertexContextNV*>(mPrimaryVertexContext); - cudaMemcpyToSymbol(GPU::kTrkPar, &mTrkParams, sizeof(TrackingParameters)); - std::array<size_t, constants::its::CellsPerRoad> tempSize; - std::array<int, constants::its::CellsPerRoad> trackletsNum; - std::array<GPU::Stream, constants::its::TrackletsPerRoad> streamArray; + // cudaMemcpyToSymbol(gpu::kTrkPar, &mTrkParams, sizeof(TrackingParameters)); + std::array<size_t, constants::its2::CellsPerRoad> tempSize; + std::array<int, constants::its2::CellsPerRoad> trackletsNum; + std::array<gpu::Stream, constants::its2::TrackletsPerRoad> streamArray; - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::CellsPerRoad; ++iLayer) { tempSize[iLayer] = 0; primaryVertexContext->getTempTrackletArray()[iLayer].reset( @@ -301,21 +345,21 @@ void TrackerTraitsNV::computeLayerTracklets() cudaDeviceSynchronize(); - for (int iLayer{0}; iLayer < constants::its::TrackletsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::TrackletsPerRoad; ++iLayer) { - const GPU::DeviceProperties& deviceProperties = GPU::Context::getInstance().getDeviceProperties(); + const gpu::DeviceProperties& deviceProperties = gpu::Context::getInstance().getDeviceProperties(); const int clustersNum{static_cast<int>(primaryVertexContext->getClusters()[iLayer].size())}; - dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(clustersNum, 1, 192)}; - dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, clustersNum)}; + dim3 threadsPerBlock{gpu::utils::host::getBlockSize(clustersNum, 1, 192)}; + dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, clustersNum)}; if (iLayer == 0) { - GPU::layerTrackletsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), + gpu::layerTrackletsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), iLayer, primaryVertexContext->getDeviceTracklets()[iLayer].getWeakCopy()); } else { - GPU::layerTrackletsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), + gpu::layerTrackletsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), iLayer, primaryVertexContext->getTempTrackletArray()[iLayer - 1].getWeakCopy()); } @@ -333,7 +377,7 @@ void TrackerTraitsNV::computeLayerTracklets() cudaDeviceSynchronize(); - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::CellsPerRoad; ++iLayer) { trackletsNum[iLayer] = primaryVertexContext->getTempTrackletArray()[iLayer].getSizeFromDevice(); if (trackletsNum[iLayer] == 0) { @@ -346,10 +390,10 @@ void TrackerTraitsNV::computeLayerTracklets() primaryVertexContext->getDeviceTrackletsLookupTable()[iLayer].get(), primaryVertexContext->getClusters()[iLayer + 1].size(), streamArray[iLayer + 1].get()); - dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsNum[iLayer])}; - dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; + dim3 threadsPerBlock{gpu::utils::host::getBlockSize(trackletsNum[iLayer])}; + dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; - GPU::sortTrackletsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer + 1].get()>>>(primaryVertexContext->getDeviceContext(), + gpu::sortTrackletsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer + 1].get()>>>(primaryVertexContext->getDeviceContext(), iLayer + 1, primaryVertexContext->getTempTrackletArray()[iLayer].getWeakCopy()); cudaError_t error = cudaGetLastError(); @@ -369,12 +413,12 @@ void TrackerTraitsNV::computeLayerCells() { PrimaryVertexContextNV* primaryVertexContext = static_cast<PrimaryVertexContextNV*>(mPrimaryVertexContext); - std::array<size_t, constants::its::CellsPerRoad - 1> tempSize; - std::array<int, constants::its::CellsPerRoad - 1> trackletsNum; - std::array<int, constants::its::CellsPerRoad - 1> cellsNum; - std::array<GPU::Stream, constants::its::CellsPerRoad> streamArray; + std::array<size_t, constants::its2::CellsPerRoad - 1> tempSize; + std::array<int, constants::its2::CellsPerRoad - 1> trackletsNum; + std::array<int, constants::its2::CellsPerRoad - 1> cellsNum; + std::array<gpu::Stream, constants::its2::CellsPerRoad> streamArray; - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad - 1; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::CellsPerRoad - 1; ++iLayer) { tempSize[iLayer] = 0; trackletsNum[iLayer] = primaryVertexContext->getDeviceTracklets()[iLayer + 1].getSizeFromDevice(); @@ -392,23 +436,23 @@ void TrackerTraitsNV::computeLayerCells() cudaDeviceSynchronize(); - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad; ++iLayer) { - const GPU::DeviceProperties& deviceProperties = GPU::Context::getInstance().getDeviceProperties(); + for (int iLayer{0}; iLayer < constants::its2::CellsPerRoad; ++iLayer) { + const gpu::DeviceProperties& deviceProperties = gpu::Context::getInstance().getDeviceProperties(); const int trackletsSize = primaryVertexContext->getDeviceTracklets()[iLayer].getSizeFromDevice(); if (trackletsSize == 0) { continue; } - dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsSize)}; - dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsSize)}; + dim3 threadsPerBlock{gpu::utils::host::getBlockSize(trackletsSize)}; + dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, trackletsSize)}; if (iLayer == 0) { - GPU::layerCellsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), + gpu::layerCellsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), iLayer, primaryVertexContext->getDeviceCells()[iLayer].getWeakCopy()); } else { - GPU::layerCellsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), + gpu::layerCellsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer].get()>>>(primaryVertexContext->getDeviceContext(), iLayer, primaryVertexContext->getTempCellArray()[iLayer - 1].getWeakCopy()); } @@ -426,7 +470,7 @@ void TrackerTraitsNV::computeLayerCells() cudaDeviceSynchronize(); - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad - 1; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::CellsPerRoad - 1; ++iLayer) { cellsNum[iLayer] = primaryVertexContext->getTempCellArray()[iLayer].getSizeFromDevice(); if (cellsNum[iLayer] == 0) { continue; @@ -438,10 +482,10 @@ void TrackerTraitsNV::computeLayerCells() primaryVertexContext->getDeviceCellsLookupTable()[iLayer].get(), trackletsNum[iLayer], streamArray[iLayer + 1].get()); - dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsNum[iLayer])}; - dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; + dim3 threadsPerBlock{gpu::utils::host::getBlockSize(trackletsNum[iLayer])}; + dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; - GPU::sortCellsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer + 1].get()>>>(primaryVertexContext->getDeviceContext(), + gpu::sortCellsKernel<<<blocksGrid, threadsPerBlock, 0, streamArray[iLayer + 1].get()>>>(primaryVertexContext->getDeviceContext(), iLayer + 1, primaryVertexContext->getTempCellArray()[iLayer].getWeakCopy()); cudaError_t error = cudaGetLastError(); @@ -458,19 +502,21 @@ void TrackerTraitsNV::computeLayerCells() cudaDeviceSynchronize(); - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < constants::its2::CellsPerRoad; ++iLayer) { int cellsSize = 0; if (iLayer == 0) { cellsSize = primaryVertexContext->getDeviceCells()[iLayer].getSizeFromDevice(); - if (cellsSize == 0) + if (cellsSize == 0) { continue; + } } else { cellsSize = cellsNum[iLayer - 1]; - if (cellsSize == 0) + if (cellsSize == 0) { continue; + } primaryVertexContext->getDeviceCellsLookupTable()[iLayer - 1].copyIntoVector( primaryVertexContext->getCellsLookupTable()[iLayer - 1], trackletsNum[iLayer - 1]); } @@ -479,7 +525,7 @@ void TrackerTraitsNV::computeLayerCells() } } -void TrackerTraitsNV::refitTracks(const std::array<std::vector<TrackingFrameInfo>, 7>& tf, std::vector<TrackITSExt>& tracks) +void TrackerTraitsNV::refitTracks(const std::vector<std::vector<TrackingFrameInfo>>& tf, std::vector<TrackITSExt>& tracks) { PrimaryVertexContextNV* pvctx = static_cast<PrimaryVertexContextNV*>(mPrimaryVertexContext); @@ -491,7 +537,8 @@ void TrackerTraitsNV::refitTracks(const std::array<std::vector<TrackingFrameInfo for (int iLayer = 0; iLayer < 7; iLayer++) { clusters[iLayer] = pvctx->getDeviceClusters()[iLayer].get(); } - mChainRunITSTrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); + //TODO: restore this + // mChainRunITSTrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); } } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/Utils.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/Utils.cu index 5ab543d03c20e..26720b80e9193 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/Utils.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/Utils.cu @@ -17,8 +17,10 @@ #include <sstream> #include <stdexcept> +#ifndef GPUCA_GPUCODE_GENRTC #include <cuda_profiler_api.h> #include <cooperative_groups.h> +#endif #include "ITStrackingCUDA/Context.h" @@ -68,10 +70,10 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { -void Utils::Host::checkCUDAError(const cudaError_t error, const char* file, const int line) +void utils::host::checkCUDAError(const cudaError_t error, const char* file, const int line) { if (error != cudaSuccess) { @@ -84,18 +86,18 @@ void Utils::Host::checkCUDAError(const cudaError_t error, const char* file, cons } } -dim3 Utils::Host::getBlockSize(const int colsNum) +dim3 utils::host::getBlockSize(const int colsNum) { return getBlockSize(colsNum, 1); } -dim3 Utils::Host::getBlockSize(const int colsNum, const int rowsNum) +dim3 utils::host::getBlockSize(const int colsNum, const int rowsNum) { const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); return getBlockSize(colsNum, rowsNum, deviceProperties.cudaCores / deviceProperties.maxBlocksPerSM); } -dim3 Utils::Host::getBlockSize(const int colsNum, const int rowsNum, const int maxThreadsPerBlock) +dim3 utils::host::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); @@ -117,59 +119,59 @@ dim3 Utils::Host::getBlockSize(const int colsNum, const int rowsNum, const int m return dim3{static_cast<unsigned int>(xThreads), static_cast<unsigned int>(yThreads)}; } -dim3 Utils::Host::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum) +dim3 utils::host::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum) { return getBlocksGrid(threadsPerBlock, rowsNum, 1); } -dim3 Utils::Host::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum, const int colsNum) +dim3 utils::host::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum, const int colsNum) { return dim3{1 + (rowsNum - 1) / threadsPerBlock.x, 1 + (colsNum - 1) / threadsPerBlock.y}; } -void Utils::Host::gpuMalloc(void** p, const int size) +void utils::host::gpuMalloc(void** p, const int size) { checkCUDAError(cudaMalloc(p, size), __FILE__, __LINE__); } -void Utils::Host::gpuFree(void* p) +void utils::host::gpuFree(void* p) { checkCUDAError(cudaFree(p), __FILE__, __LINE__); } -void Utils::Host::gpuMemset(void* p, int value, int size) +void utils::host::gpuMemset(void* p, int value, int size) { checkCUDAError(cudaMemset(p, value, size), __FILE__, __LINE__); } -void Utils::Host::gpuMemcpyHostToDevice(void* dst, const void* src, int size) +void utils::host::gpuMemcpyHostToDevice(void* dst, const void* src, int size) { checkCUDAError(cudaMemcpy(dst, src, size, cudaMemcpyHostToDevice), __FILE__, __LINE__); } -void Utils::Host::gpuMemcpyHostToDeviceAsync(void* dst, const void* src, int size, Stream& stream) +void utils::host::gpuMemcpyHostToDeviceAsync(void* dst, const void* src, int size, Stream& stream) { checkCUDAError(cudaMemcpyAsync(dst, src, size, cudaMemcpyHostToDevice, stream.get()), __FILE__, __LINE__); } -void Utils::Host::gpuMemcpyDeviceToHost(void* dst, const void* src, int size) +void utils::host::gpuMemcpyDeviceToHost(void* dst, const void* src, int size) { checkCUDAError(cudaMemcpy(dst, src, size, cudaMemcpyDeviceToHost), __FILE__, __LINE__); } -// void Utils::Host::gpuStartProfiler() +// void utils::host::gpuStartProfiler() // { // checkCUDAError(cudaProfilerStart(), __FILE__, __LINE__); // } -// void Utils::Host::gpuStopProfiler() +// void utils::host::gpuStopProfiler() // { // checkCUDAError(cudaProfilerStop(), __FILE__, __LINE__); // } -GPUd() int Utils::Device::getLaneIndex() +GPUd() int utils::device::getLaneIndex() { uint32_t laneIndex; asm volatile("mov.u32 %0, %%laneid;" @@ -177,17 +179,17 @@ GPUd() int Utils::Device::getLaneIndex() return static_cast<int>(laneIndex); } -GPUd() int Utils::Device::shareToWarp(const int value, const int laneIndex) +GPUd() int utils::device::shareToWarp(const int value, const int laneIndex) { cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); return threadGroup.shfl(value, laneIndex); } -GPUd() int Utils::Device::gpuAtomicAdd(int* p, const int incrementSize) +GPUd() int utils::device::gpuAtomicAdd(int* p, const int incrementSize) { return atomicAdd(p, incrementSize); } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/cuda/src/VertexerTraitsGPU.cu b/Detectors/ITSMFT/ITS/tracking/cuda/src/VertexerTraitsGPU.cu index d914e6499d2a0..336465f6a8ddf 100644 --- a/Detectors/ITSMFT/ITS/tracking/cuda/src/VertexerTraitsGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/cuda/src/VertexerTraitsGPU.cu @@ -15,8 +15,11 @@ #include <iostream> #include <sstream> #include <array> -#include <assert.h> +#include <cassert> + +#ifndef GPUCA_GPUCODE_GENRTC #include <cub/cub.cuh> +#endif #include "ITStracking/MathUtils.h" #include "ITStracking/Configuration.h" @@ -34,16 +37,31 @@ namespace o2 namespace its { -using constants::index_table::PhiBins; -using constants::index_table::ZBins; -using constants::its::LayersRCoordinate; -using constants::its::LayersZCoordinate; using constants::its::VertexerHistogramVolume; using constants::math::TwoPi; -using index_table_utils::getPhiBinIndex; -using index_table_utils::getZBinIndex; using math_utils::getNormalizedPhiCoordinate; +using namespace constants::its2; +GPU_DEVICE 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.phiCoordinate - maxdeltaphi; + const float zRangeMax = z1 + maxdeltaz; + const float phiRangeMax = currentCluster.phiCoordinate + 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(); @@ -79,10 +97,11 @@ void VertexerTraitsGPU::initialise(ROframe* event) { reset(); arrangeClusters(event); - mStoreVertexerGPUPtr = mStoreVertexerGPU.initialise(mClusters, mIndexTables); + //TODO: restore this + // mStoreVertexerGPUPtr = mStoreVertexerGPU.initialise(mClusters, mIndexTables); } -namespace GPU +namespace gpu { template <typename... Args> @@ -144,20 +163,20 @@ GPUg() void trackleterKernel( const size_t stride{currentClusterIndex * store.getConfig().maxTrackletsPerCluster}; const Cluster& currentCluster = store.getClusters()[1][currentClusterIndex]; // assign-constructor may be a problem, check const VertexerLayerName adjacentLayerIndex{layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer ? VertexerLayerName::innermostLayer : VertexerLayerName::outerLayer}; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, static_cast<int>(adjacentLayerIndex), 0.f, 50.f, phiCut / 2)}; + const int4 selectedBinsRect{getBinsRect(currentCluster, static_cast<int>(adjacentLayerIndex), 0.f, 50.f, phiCut / 2)}; 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; } const size_t nClustersAdjacentLayer = store.getClusters()[static_cast<int>(adjacentLayerIndex)].size(); - for (size_t iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + for (size_t iPhiBin{(size_t)selectedBinsRect.y}, iPhiCount{0}; iPhiCount < (size_t)phiBinsNum; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { + const int firstBinIndex{constants::its2::getBinIndex(selectedBinsRect.x, iPhiBin)}; const int firstRowClusterIndex{store.getIndexTable(adjacentLayerIndex)[firstBinIndex]}; const int maxRowClusterIndex{store.getIndexTable(adjacentLayerIndex)[firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1]}; - for (size_t iAdjacentCluster{firstRowClusterIndex}; iAdjacentCluster < maxRowClusterIndex && iAdjacentCluster < nClustersAdjacentLayer; ++iAdjacentCluster) { + for (size_t iAdjacentCluster{(size_t)firstRowClusterIndex}; iAdjacentCluster < (size_t)maxRowClusterIndex && iAdjacentCluster < nClustersAdjacentLayer; ++iAdjacentCluster) { const Cluster& adjacentCluster = store.getClusters()[static_cast<int>(adjacentLayerIndex)][iAdjacentCluster]; // assign-constructor may be a problem, check - if (gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - adjacentCluster.phiCoordinate) < phiCut) { + if (o2::gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - adjacentCluster.phiCoordinate) < phiCut) { if (storedTracklets < store.getConfig().maxTrackletsPerCluster) { if (layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer) { store.getDuplets01().emplace(stride + storedTracklets, iAdjacentCluster, currentClusterIndex, adjacentCluster, currentCluster); @@ -189,8 +208,8 @@ GPUg() void trackletSelectionKernel( int validTracklets{0}; for (int iTracklet12{0}; iTracklet12 < store.getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer)[currentClusterIndex]; ++iTracklet12) { for (int iTracklet01{0}; iTracklet01 < store.getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer)[currentClusterIndex] && validTracklets < store.getConfig().maxTrackletsPerCluster; ++iTracklet01) { - const float deltaTanLambda{gpu::GPUCommonMath::Abs(store.getDuplets01()[stride + iTracklet01].tanLambda - store.getDuplets12()[stride + iTracklet12].tanLambda)}; - const float deltaPhi{gpu::GPUCommonMath::Abs(store.getDuplets01()[stride + iTracklet01].phiCoordinate - store.getDuplets12()[stride + iTracklet12].phiCoordinate)}; + const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(store.getDuplets01()[stride + iTracklet01].tanLambda - store.getDuplets12()[stride + iTracklet12].tanLambda)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(store.getDuplets01()[stride + iTracklet01].phiCoordinate - store.getDuplets12()[stride + iTracklet12].phiCoordinate)}; if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != store.getConfig().maxTrackletsPerCluster) { assert(store.getDuplets01()[stride + iTracklet01].secondClusterIndex == store.getDuplets12()[stride + iTracklet12].firstClusterIndex); if (!isInitRun) { @@ -257,7 +276,7 @@ GPUg() void computeZCentroidsKernel(DeviceStoreVertexerGPU& store, float tmpX{store.getConfig().histConf.lowHistBoundariesXYZ[0] + store.getTmpVertexPositionBins()[0].key * store.getConfig().histConf.binSizeHistX + store.getConfig().histConf.binSizeHistX / 2}; int sumWX{store.getTmpVertexPositionBins()[0].value}; float wX{tmpX * store.getTmpVertexPositionBins()[0].value}; - for (int iBin{gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[0].key - binOpeningX)}; iBin < gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[0].key + binOpeningX + 1, store.getConfig().histConf.nBinsXYZ[0] - 1); ++iBin) { + for (int iBin{o2::gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[0].key + binOpeningX + 1, store.getConfig().histConf.nBinsXYZ[0] - 1); ++iBin) { if (iBin != store.getTmpVertexPositionBins()[0].key) { wX += (store.getConfig().histConf.lowHistBoundariesXYZ[0] + iBin * store.getConfig().histConf.binSizeHistX + store.getConfig().histConf.binSizeHistX / 2) * store.getHistogramXYZ()[0].get()[iBin]; sumWX += store.getHistogramXYZ()[0].get()[iBin]; @@ -266,7 +285,7 @@ GPUg() void computeZCentroidsKernel(DeviceStoreVertexerGPU& store, float tmpY{store.getConfig().histConf.lowHistBoundariesXYZ[1] + store.getTmpVertexPositionBins()[1].key * store.getConfig().histConf.binSizeHistY + store.getConfig().histConf.binSizeHistY / 2}; int sumWY{store.getTmpVertexPositionBins()[1].value}; float wY{tmpY * store.getTmpVertexPositionBins()[1].value}; - for (int iBin{gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[1].key - binOpeningY)}; iBin < gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[1].key + binOpeningY + 1, store.getConfig().histConf.nBinsXYZ[1] - 1); ++iBin) { + for (int iBin{o2::gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[1].key + binOpeningY + 1, store.getConfig().histConf.nBinsXYZ[1] - 1); ++iBin) { if (iBin != store.getTmpVertexPositionBins()[1].key) { wY += (store.getConfig().histConf.lowHistBoundariesXYZ[1] + iBin * store.getConfig().histConf.binSizeHistY + store.getConfig().histConf.binSizeHistY / 2) * store.getHistogramXYZ()[1].get()[iBin]; sumWY += store.getHistogramXYZ()[1].get()[iBin]; @@ -276,7 +295,7 @@ GPUg() void computeZCentroidsKernel(DeviceStoreVertexerGPU& store, store.getBeamPosition().emplace(1, wY / sumWY); float fakeBeamPoint1[3] = {store.getBeamPosition()[0], store.getBeamPosition()[1], -1}; // get two points laying at different z, to create line object float fakeBeamPoint2[3] = {store.getBeamPosition()[0], store.getBeamPosition()[1], 1}; - Line pseudoBeam = Line::Line(fakeBeamPoint1, fakeBeamPoint2); + Line pseudoBeam = {fakeBeamPoint1, fakeBeamPoint2}; if (Line::getDCA(store.getLines()[currentThreadIndex], pseudoBeam) < pairCut) { ClusterLinesGPU cluster{store.getLines()[currentThreadIndex], pseudoBeam}; store.getZCentroids().emplace(currentThreadIndex, cluster.getVertex()[2]); @@ -298,7 +317,7 @@ GPUg() void computeVertexKernel(DeviceStoreVertexerGPU& store, const int vertInd float ez{0.f}; int sumWZ{store.getTmpVertexPositionBins()[2].value}; float wZ{z * store.getTmpVertexPositionBins()[2].value}; - for (int iBin{gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[2].key - binOpeningZ)}; iBin < gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[2].key + binOpeningZ + 1, store.getConfig().histConf.nBinsXYZ[2] - 1); ++iBin) { + for (int iBin{o2::gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[2].key + binOpeningZ + 1, store.getConfig().histConf.nBinsXYZ[2] - 1); ++iBin) { if (iBin != store.getTmpVertexPositionBins()[2].key) { wZ += (store.getConfig().histConf.lowHistBoundariesXYZ[2] + iBin * store.getConfig().histConf.binSizeHistZ + store.getConfig().histConf.binSizeHistZ / 2) * store.getHistogramXYZ()[2].get()[iBin]; sumWZ += store.getHistogramXYZ()[2].get()[iBin]; @@ -316,7 +335,7 @@ GPUg() void computeVertexKernel(DeviceStoreVertexerGPU& store, const int vertInd } } } -} // namespace GPU +} // namespace gpu void VertexerTraitsGPU::computeTracklets() { @@ -324,17 +343,17 @@ void VertexerTraitsGPU::computeTracklets() std::cout << "\t\tno clusters on layer 1. Returning.\n"; return; } - const dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{gpu::utils::host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; - GPU::trackleterKernel<<<blocksGrid, threadsPerBlock>>>( + gpu::trackleterKernel<<<blocksGrid, threadsPerBlock>>>( getDeviceContext(), - GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, + gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer, mVrtParams.phiCut); - GPU::trackleterKernel<<<blocksGrid, threadsPerBlock>>>( + gpu::trackleterKernel<<<blocksGrid, threadsPerBlock>>>( getDeviceContext(), - GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, + gpu::TrackletingLayerOrder::fromMiddleToOuterLayer, mVrtParams.phiCut); gpuThrowOnError(); @@ -342,8 +361,8 @@ void VertexerTraitsGPU::computeTracklets() #ifdef _ALLOW_DEBUG_TREES_ITS_ if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { mDebugger->fillCombinatoricsTree(mClusters, - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer), mEvent); } #endif @@ -355,11 +374,11 @@ void VertexerTraitsGPU::computeTrackletMatching() std::cout << "\t\tno clusters on layer 1. Returning.\n"; return; } - const dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{gpu::utils::host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); - GPU::trackletSelectionKernel<<<blocksGrid, threadsPerBlock>>>( + gpu::trackletSelectionKernel<<<blocksGrid, threadsPerBlock>>>( getDeviceContext(), true, // isInitRun mVrtParams.tanLambdaCut, @@ -371,7 +390,7 @@ void VertexerTraitsGPU::computeTrackletMatching() mStoreVertexerGPU.getNExclusiveFoundLines().get(), mClusters[1].size()); - GPU::trackletSelectionKernel<<<blocksGrid, threadsPerBlock>>>( + gpu::trackletSelectionKernel<<<blocksGrid, threadsPerBlock>>>( getDeviceContext(), false, // isInitRun mVrtParams.tanLambdaCut, @@ -382,8 +401,8 @@ void VertexerTraitsGPU::computeTrackletMatching() #ifdef _ALLOW_DEBUG_TREES_ITS_ if (isDebugFlag(VertexerDebug::TrackletTreeAll)) { mDebugger->fillTrackletSelectionTree(mClusters, - mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), - mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer), mStoreVertexerGPU.getDupletIndicesFromGPU(), mEvent); } @@ -403,15 +422,15 @@ void VertexerTraitsGPU::computeVertices() std::cout << "\t\tno clusters on layer 1. Returning.\n"; return; } - const dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{gpu::utils::host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{gpu::utils::host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); int nLines = mStoreVertexerGPU.getNExclusiveFoundLines().getElementFromDevice(mClusters[1].size() - 1) + mStoreVertexerGPU.getNFoundLines().getElementFromDevice(mClusters[1].size() - 1); int nCentroids{static_cast<int>(nLines * (nLines - 1) / 2)}; int* histogramXY[2] = {mStoreVertexerGPU.getHistogramXYZ()[0].get(), mStoreVertexerGPU.getHistogramXYZ()[1].get()}; float tmpArrayLow[2] = {mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[1]}; float tmpArrayHigh[2] = {mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[1]}; - GPU::computeCentroidsKernel<<<blocksGrid, threadsPerBlock>>>(getDeviceContext(), + gpu::computeCentroidsKernel<<<blocksGrid, threadsPerBlock>>>(getDeviceContext(), mVrtParams.histPairCut); cub::DeviceHistogram::MultiHistogramEven<2, 2>(reinterpret_cast<void*>(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage @@ -432,7 +451,7 @@ void VertexerTraitsGPU::computeVertices() histogramXY[1], mStoreVertexerGPU.getTmpVertexPositionBins().get() + 1, mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0]); - GPU::computeZCentroidsKernel<<<blocksGrid, threadsPerBlock>>>(getDeviceContext(), mVrtParams.histPairCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[0], mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[1]); + gpu::computeZCentroidsKernel<<<blocksGrid, threadsPerBlock>>>(getDeviceContext(), mVrtParams.histPairCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[0], mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[1]); cub::DeviceHistogram::HistogramEven(reinterpret_cast<void*>(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage bufferSize, // temp_storage_bytes mStoreVertexerGPU.getZCentroids().get(), // d_samples @@ -456,9 +475,9 @@ void VertexerTraitsGPU::computeVertices() mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2] - 1}); } #endif - GPU::computeVertexKernel<<<blocksGrid, 5>>>(getDeviceContext(), iVertex, mVrtParams.clusterContributorsCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[2]); + gpu::computeVertexKernel<<<blocksGrid, 5>>>(getDeviceContext(), iVertex, mVrtParams.clusterContributorsCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[2]); } - std::vector<GPU::GPUVertex> vertices; + std::vector<gpu::GPUVertex> vertices; vertices.resize(mStoreVertexerGPU.getConfig().nMaxVertices); mStoreVertexerGPU.getVertices().copyIntoSizedVector(vertices); @@ -474,23 +493,23 @@ void VertexerTraitsGPU::computeVertices() #ifdef _ALLOW_DEBUG_TREES_ITS_ void VertexerTraitsGPU::computeMCFiltering() { - std::vector<Tracklet> tracklets01 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); - std::vector<Tracklet> tracklets12 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); - std::vector<int> labels01 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); - std::vector<int> labels12 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); + std::vector<Tracklet> tracklets01 = mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector<Tracklet> tracklets12 = mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer); + std::vector<int> labels01 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector<int> labels12 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer); VertexerStoreConfigurationGPU tmpGPUConf; const int stride = tmpGPUConf.maxTrackletsPerCluster; filterTrackletsWithMC(tracklets01, tracklets12, labels01, labels12, stride); - mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, labels01); - mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, tracklets01); - mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, labels12); - mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, tracklets12); + mStoreVertexerGPU.updateFoundDuplets(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer, labels01); + mStoreVertexerGPU.updateDuplets(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer, tracklets01); + mStoreVertexerGPU.updateFoundDuplets(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer, labels12); + mStoreVertexerGPU.updateDuplets(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer, tracklets12); if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { mDebugger->fillCombinatoricsTree(mClusters, - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer), mEvent); } } diff --git a/Detectors/ITSMFT/ITS/tracking/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/hip/CMakeLists.txt index f4c90624a626a..b857741663c64 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/hip/CMakeLists.txt @@ -10,10 +10,7 @@ set(CMAKE_CXX_COMPILER ${hip_HIPCC_EXECUTABLE}) set(CMAKE_CXX_EXTENSIONS OFF) - -if(DEFINED HIP_AMDGPUTARGET) - set(TMP_TARGET "(GPU Target ${HIP_AMDGPUTARGET})") -endif() +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc") message(STATUS "Building ITS HIP vertexer") o2_add_library(ITStrackingHIP @@ -24,7 +21,7 @@ o2_add_library(ITStrackingHIP src/StreamHIP.hip.cxx # src/TrackerTraitsHIP.hip.cxx src/VertexerTraitsHIP.hip.cxx - src/UtilsHIP.hip.cxx + src/UtilsHIP.hip.cxx PUBLIC_LINK_LIBRARIES O2::ITStracking hip::host hip::device @@ -35,13 +32,6 @@ target_compile_definitions( ${targetName} PRIVATE $<TARGET_PROPERTY:O2::ITStracking,COMPILE_DEFINITIONS>) if(HIP_AMDGPUTARGET) + # Need to add gpu target also to link flags due to gpu-rdc option target_link_options(${targetName} PUBLIC --amdgpu-target=${HIP_AMDGPUTARGET}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --amdgpu-target=${HIP_AMDGPUTARGET}") endif() - -target_compile_options(${targetName} - PUBLIC -Wno-invalid-command-line-argument - -Wno-unused-command-line-argument - -Wno-invalid-constexpr - -Wno-ignored-optimization-argument - -Wno-unused-private-field) \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ArrayHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ArrayHIP.h index e728a0ae7b530..3002c6b68d9b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ArrayHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ArrayHIP.h @@ -23,7 +23,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { namespace @@ -66,7 +66,7 @@ struct ArrayHIP final { T InternalArray[Size]; }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ClusterLinesHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ClusterLinesHIP.h index 3a8f92998dc8e..2da251e7919b0 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ClusterLinesHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ClusterLinesHIP.h @@ -23,7 +23,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { struct GPUVertex final { @@ -67,7 +67,7 @@ class ClusterLinesHIP final float mVertex[3]; // cluster centroid position }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 #endif /* O2_ITS_TRACKING_INCLUDE_CLUSTERLINES_HIP_H_ */ \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ContextHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ContextHIP.h index 4b11d7d876a28..5eb74fe85e16b 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ContextHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/ContextHIP.h @@ -25,7 +25,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { struct DeviceProperties final { @@ -64,7 +64,7 @@ class ContextHIP final int mDevicesNum; std::vector<DeviceProperties> mDeviceProperties; }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/DeviceStoreVertexerHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/DeviceStoreVertexerHIP.h index 8b41060de7728..e2bdea5eb3d87 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/DeviceStoreVertexerHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/DeviceStoreVertexerHIP.h @@ -33,7 +33,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { enum class TrackletingLayerOrder { @@ -55,7 +55,7 @@ class DeviceStoreVertexerHIP final ~DeviceStoreVertexerHIP() = default; UniquePointer<DeviceStoreVertexerHIP> initialise(const std::array<std::vector<Cluster>, constants::its::LayersNumberVertexer>&, - const std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, + const std::array<std::array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, constants::its::LayersNumberVertexer>&); // RO APIs @@ -141,7 +141,7 @@ inline std::vector<int> DeviceStoreVertexerHIP::getNFoundTrackletsFromGPU(const std::vector<int> nFoundDuplets; nFoundDuplets.resize(sizes[1]); - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); } else { mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); @@ -161,7 +161,7 @@ inline std::vector<Tracklet> DeviceStoreVertexerHIP::getRawDupletsFromGPU(const std::vector<int> nFoundDuplets; nFoundDuplets.resize(sizes[1]); - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); mDuplets01.copyIntoSizedVector(tmpDuplets); } else { @@ -184,7 +184,7 @@ inline std::vector<Tracklet> DeviceStoreVertexerHIP::getDupletsFromGPU(const Ord nFoundDuplets.resize(sizes[1]); std::vector<Tracklet> shrinkedDuplets; - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); mDuplets01.copyIntoSizedVector(tmpDuplets); } else { @@ -242,7 +242,7 @@ inline std::vector<int> DeviceStoreVertexerHIP::getHistogramZFromGPU() inline void DeviceStoreVertexerHIP::updateDuplets(const Order order, std::vector<Tracklet>& duplets) { - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mDuplets01.reset(duplets.data(), static_cast<int>(duplets.size())); } else { mDuplets12.reset(duplets.data(), static_cast<int>(duplets.size())); @@ -251,7 +251,7 @@ inline void DeviceStoreVertexerHIP::updateDuplets(const Order order, std::vector inline void DeviceStoreVertexerHIP::updateFoundDuplets(const Order order, std::vector<int>& nDuplets) { - if (order == GPU::Order::fromInnermostToMiddleLayer) { + if (order == gpu::Order::fromInnermostToMiddleLayer) { mNFoundDuplets[0].reset(nDuplets.data(), static_cast<int>(nDuplets.size())); } else { mNFoundDuplets[1].reset(nDuplets.data(), static_cast<int>(nDuplets.size())); @@ -291,7 +291,7 @@ inline std::vector<Line> DeviceStoreVertexerHIP::getLinesFromGPU() return lines; } #endif -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 #endif //O2_ITS_TRACKING_INCLUDE_DEVICESTOREVERTEXER_HIP_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/StreamHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/StreamHIP.h index d0006fd40ace9..0377e20d3e84e 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/StreamHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/StreamHIP.h @@ -21,7 +21,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { class StreamHIP final @@ -39,7 +39,7 @@ class StreamHIP final private: hipStream_t mStream; }; -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UniquePointerHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UniquePointerHIP.h index 444fb9e199390..372e2ee31f19f 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UniquePointerHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UniquePointerHIP.h @@ -22,7 +22,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { namespace @@ -146,7 +146,7 @@ GPUhd() const T& UniquePointer<T>::operator*() const noexcept { return PointerTraits::getReference(mDevicePointer); } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UtilsHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UtilsHIP.h index f536f12529f2f..34434e9ec242c 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UtilsHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/UtilsHIP.h @@ -23,7 +23,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { namespace Utils @@ -59,7 +59,7 @@ GPUd() int shareToWarp(const int, const int); GPUd() int gpuAtomicAdd(int*, const int); } // namespace Device } // namespace Utils -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VectorHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VectorHIP.h index 42c69111a9f5f..d7aaa7c3b2a4a 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VectorHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VectorHIP.h @@ -28,7 +28,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { template <typename T> @@ -323,7 +323,7 @@ GPUhd() void VectorHIP<T>::dump() printf("mArrayPointer = %p\nmDeviceSize = %p\nmCapacity = %d\nmIsWeak = %s\n", mArrayPointer, mDeviceSize, mCapacity, mIsWeak ? "true" : "false"); } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VertexerTraitsHIP.h b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VertexerTraitsHIP.h index 355b1243c127f..245af2485a55f 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VertexerTraitsHIP.h +++ b/Detectors/ITSMFT/ITS/tracking/hip/include/ITStrackingHIP/VertexerTraitsHIP.h @@ -37,7 +37,7 @@ namespace its { class ROframe; -using constants::index_table::InversePhiBinSize; +using constants::its2::InversePhiBinSize; class VertexerTraitsHIP : public VertexerTraits { @@ -59,32 +59,32 @@ class VertexerTraitsHIP : public VertexerTraits // GPU-specific getters GPUd() static const int2 getBinsPhiRectWindow(const Cluster&, float maxdeltaphi); - GPUhd() GPU::DeviceStoreVertexerHIP& getDeviceContext(); - GPUhd() GPU::DeviceStoreVertexerHIP* getDeviceContextPtr(); + GPUhd() gpu::DeviceStoreVertexerHIP& getDeviceContext(); + GPUhd() gpu::DeviceStoreVertexerHIP* getDeviceContextPtr(); protected: // #ifdef _ALLOW_DEBUG_TREES_ITS_ // StandaloneDebugger* mDebugger; // #endif - GPU::DeviceStoreVertexerHIP mStoreVertexerGPU; - GPU::UniquePointer<GPU::DeviceStoreVertexerHIP> mStoreVertexerGPUPtr; + gpu::DeviceStoreVertexerHIP mStoreVertexerGPU; + gpu::UniquePointer<gpu::DeviceStoreVertexerHIP> mStoreVertexerGPUPtr; }; GPUdi() const int2 VertexerTraitsHIP::getBinsPhiRectWindow(const Cluster& currentCluster, float phiCut) { // This function returns the lowest PhiBin and the number of phi bins to be spanned, In the form int2{phiBinLow, PhiBinSpan} - const int phiBinMin{index_table_utils::getPhiBinIndex( + const int phiBinMin{constants::its2::getPhiBinIndex( math_utils::getNormalizedPhiCoordinate(currentCluster.phiCoordinate - phiCut))}; const int phiBinSpan{static_cast<int>(MATH_CEIL(phiCut * InversePhiBinSize))}; return int2{phiBinMin, phiBinSpan}; } -GPUhdi() GPU::DeviceStoreVertexerHIP& VertexerTraitsHIP::getDeviceContext() +GPUhdi() gpu::DeviceStoreVertexerHIP& VertexerTraitsHIP::getDeviceContext() { return *mStoreVertexerGPUPtr; } -GPUhdi() GPU::DeviceStoreVertexerHIP* VertexerTraitsHIP::getDeviceContextPtr() +GPUhdi() gpu::DeviceStoreVertexerHIP* VertexerTraitsHIP::getDeviceContextPtr() { return mStoreVertexerGPUPtr.get(); } diff --git a/Detectors/ITSMFT/ITS/tracking/hip/src/ClusterLinesHIP.hip.cxx b/Detectors/ITSMFT/ITS/tracking/hip/src/ClusterLinesHIP.hip.cxx index 79c13119e5e21..00f5788108660 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/src/ClusterLinesHIP.hip.cxx +++ b/Detectors/ITSMFT/ITS/tracking/hip/src/ClusterLinesHIP.hip.cxx @@ -18,7 +18,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { GPUd() ClusterLinesHIP::ClusterLinesHIP(const Line& firstLine, const Line& secondLine) @@ -133,6 +133,6 @@ GPUd() void ClusterLinesHIP::computeClusterCentroid() mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / determinant; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/hip/src/ContextHIP.hip.cxx b/Detectors/ITSMFT/ITS/tracking/hip/src/ContextHIP.hip.cxx index 0c6744b58b5d5..dc6ea9cb3a08b 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/src/ContextHIP.hip.cxx +++ b/Detectors/ITSMFT/ITS/tracking/hip/src/ContextHIP.hip.cxx @@ -40,7 +40,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { using Utils::HostHIP::checkHIPError; @@ -129,6 +129,6 @@ const DeviceProperties& ContextHIP::getDeviceProperties(const int deviceIndex) return mDeviceProperties[deviceIndex]; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx b/Detectors/ITSMFT/ITS/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx index 39a05cfecb033..de82d3835f7be 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx +++ b/Detectors/ITSMFT/ITS/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx @@ -21,7 +21,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { GPUg() void defaultInitArrayKernel(int* array, const size_t arraySize, const int initValue = 0) { @@ -47,7 +47,7 @@ DeviceStoreVertexerHIP::DeviceStoreVertexerHIP() mBeamPosition = VectorHIP<float>{2, 2}; for (int iTable{0}; iTable < 2; ++iTable) { - mIndexTables[iTable] = VectorHIP<int>{constants::index_table::ZBins * constants::index_table::PhiBins + 1}; // 2*20*20+1 * sizeof(int) = 802B + mIndexTables[iTable] = VectorHIP<int>{constants::its2::ZBins * constants::its2::PhiBins + 1}; // 2*20*20+1 * sizeof(int) = 802B } for (int iLayer{0}; iLayer < constants::its::LayersNumberVertexer; ++iLayer) { // 4e4 * 3 * sizof(Cluster) = 3.36MB mClusters[iLayer] = VectorHIP<Cluster>{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; @@ -65,10 +65,10 @@ DeviceStoreVertexerHIP::DeviceStoreVertexerHIP() } mSizes = VectorHIP<int>{constants::its::LayersNumberVertexer}; #endif -} // namespace GPU +} // namespace gpu UniquePointer<DeviceStoreVertexerHIP> DeviceStoreVertexerHIP::initialise(const std::array<std::vector<Cluster>, constants::its::LayersNumberVertexer>& clusters, - const std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, + const std::array<std::array<int, constants::its2::ZBins * constants::its2::PhiBins + 1>, constants::its::LayersNumberVertexer>& indexTables) { #ifdef _ALLOW_DEBUG_TREES_ITS_ @@ -105,6 +105,6 @@ GPUd() const VectorHIP<int>& DeviceStoreVertexerHIP::getIndexTable(const Vertexe return mIndexTables[1]; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/src/StreamHIP.hip.cxx b/Detectors/ITSMFT/ITS/tracking/hip/src/StreamHIP.hip.cxx index 50e24c6eb041f..28ab175f155d8 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/src/StreamHIP.hip.cxx +++ b/Detectors/ITSMFT/ITS/tracking/hip/src/StreamHIP.hip.cxx @@ -18,7 +18,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { StreamHIP::StreamHIP() @@ -36,6 +36,6 @@ const hipStream_t& StreamHIP::get() const return mStream; } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/src/UtilsHIP.hip.cxx b/Detectors/ITSMFT/ITS/tracking/hip/src/UtilsHIP.hip.cxx index bd58b7c9c3e0f..da9a149181a60 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/src/UtilsHIP.hip.cxx +++ b/Detectors/ITSMFT/ITS/tracking/hip/src/UtilsHIP.hip.cxx @@ -55,7 +55,7 @@ namespace o2 { namespace its { -namespace GPU +namespace gpu { void Utils::HostHIP::checkHIPError(const hipError_t error, const char* file, const int line) @@ -167,6 +167,6 @@ GPUd() int Utils::DeviceHIP::getLaneIndex() // return atomicAdd(p, incrementSize); // } -} // namespace GPU +} // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/hip/src/VertexerTraitsHIP.hip.cxx b/Detectors/ITSMFT/ITS/tracking/hip/src/VertexerTraitsHIP.hip.cxx index 425d7c04d98d5..1fc760a87d639 100644 --- a/Detectors/ITSMFT/ITS/tracking/hip/src/VertexerTraitsHIP.hip.cxx +++ b/Detectors/ITSMFT/ITS/tracking/hip/src/VertexerTraitsHIP.hip.cxx @@ -34,16 +34,35 @@ namespace o2 namespace its { -using constants::index_table::PhiBins; -using constants::index_table::ZBins; -using constants::its::LayersRCoordinate; -using constants::its::LayersZCoordinate; using constants::its::VertexerHistogramVolume; +using constants::its2::getPhiBinIndex; +using constants::its2::getZBinIndex; +using constants::its2::LayersRCoordinate; +using constants::its2::LayersZCoordinate; +using constants::its2::PhiBins; +using constants::its2::ZBins; using constants::math::TwoPi; -using index_table_utils::getPhiBinIndex; -using index_table_utils::getZBinIndex; using math_utils::getNormalizedPhiCoordinate; +GPUhd() 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.phiCoordinate - maxdeltaphi; + const float zRangeMax = z1 + maxdeltaz; + const float phiRangeMax = currentCluster.phiCoordinate + maxdeltaphi; + + if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || + zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + return constants::its2::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() { hipError_t error = hipGetLastError(); @@ -79,10 +98,10 @@ void VertexerTraitsHIP::initialise(ROframe* event) { reset(); arrangeClusters(event); - mStoreVertexerGPUPtr = mStoreVertexerGPU.initialise(mClusters, mIndexTables); + // mStoreVertexerGPUPtr = mStoreVertexerGPU.initialise(mClusters, mIndexTables); } -namespace GPU +namespace gpu { template <typename... Args> @@ -144,7 +163,7 @@ GPUg() void trackleterKernel( const unsigned int stride{currentClusterIndex * store->getConfig().maxTrackletsPerCluster}; const Cluster& currentCluster = store->getClusters()[1][currentClusterIndex]; // assign-constructor may be a problem, check const VertexerLayerName adjacentLayerIndex{layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer ? VertexerLayerName::innermostLayer : VertexerLayerName::outerLayer}; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, static_cast<int>(adjacentLayerIndex), 0.f, 50.f, phiCut / 2)}; + const int4 selectedBinsRect{getBinsRect(currentCluster, static_cast<int>(adjacentLayerIndex), 0.f, 50.f, phiCut / 2)}; if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; if (phiBinsNum < 0) { @@ -152,12 +171,12 @@ GPUg() void trackleterKernel( } const int nClustersAdjacentLayer = store->getClusters()[static_cast<int>(adjacentLayerIndex)].size(); for (size_t iPhiBin{static_cast<size_t>(selectedBinsRect.y)}, iPhiCount{0}; (int)iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int firstBinIndex{constants::its2::getBinIndex(selectedBinsRect.x, iPhiBin)}; const int firstRowClusterIndex{store->getIndexTable(adjacentLayerIndex)[firstBinIndex]}; const int maxRowClusterIndex{store->getIndexTable(adjacentLayerIndex)[firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1]}; for (int iAdjacentCluster{firstRowClusterIndex}; iAdjacentCluster < maxRowClusterIndex && iAdjacentCluster < nClustersAdjacentLayer; ++iAdjacentCluster) { const Cluster& adjacentCluster = store->getClusters()[static_cast<int>(adjacentLayerIndex)][iAdjacentCluster]; // assign-constructor may be a problem, check - if (gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - adjacentCluster.phiCoordinate) < phiCut) { + if (o2::gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - adjacentCluster.phiCoordinate) < phiCut) { if (storedTracklets < store->getConfig().maxTrackletsPerCluster) { if (layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer) { store->getDuplets01().emplace(stride + storedTracklets, iAdjacentCluster, currentClusterIndex, adjacentCluster, currentCluster); @@ -189,8 +208,8 @@ GPUg() void trackletSelectionKernel( int validTracklets{0}; for (int iTracklet12{0}; iTracklet12 < store->getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer)[currentClusterIndex]; ++iTracklet12) { for (int iTracklet01{0}; iTracklet01 < store->getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer)[currentClusterIndex] && validTracklets < store->getConfig().maxTrackletsPerCluster; ++iTracklet01) { - const float deltaTanLambda{gpu::GPUCommonMath::Abs(store->getDuplets01()[stride + iTracklet01].tanLambda - store->getDuplets12()[stride + iTracklet12].tanLambda)}; - const float deltaPhi{gpu::GPUCommonMath::Abs(store->getDuplets01()[stride + iTracklet01].phiCoordinate - store->getDuplets12()[stride + iTracklet12].phiCoordinate)}; + const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(store->getDuplets01()[stride + iTracklet01].tanLambda - store->getDuplets12()[stride + iTracklet12].tanLambda)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(store->getDuplets01()[stride + iTracklet01].phiCoordinate - store->getDuplets12()[stride + iTracklet12].phiCoordinate)}; if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != store->getConfig().maxTrackletsPerCluster) { assert(store->getDuplets01()[stride + iTracklet01].secondClusterIndex == store->getDuplets12()[stride + iTracklet12].firstClusterIndex); if (!isInitRun) { @@ -258,7 +277,7 @@ GPUg() void computeZCentroidsKernel(DeviceStoreVertexerHIP* store, float tmpX{store->getConfig().histConf.lowHistBoundariesXYZ[0] + store->getTmpVertexPositionBins()[0].key * store->getConfig().histConf.binSizeHistX + store->getConfig().histConf.binSizeHistX / 2}; int sumWX{store->getTmpVertexPositionBins()[0].value}; float wX{tmpX * store->getTmpVertexPositionBins()[0].value}; - for (int iBin{gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[0].key - binOpeningX)}; iBin < gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[0].key + binOpeningX + 1, store->getConfig().histConf.nBinsXYZ[0] - 1); ++iBin) { + for (int iBin{o2::gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[0].key + binOpeningX + 1, store->getConfig().histConf.nBinsXYZ[0] - 1); ++iBin) { if (iBin != store->getTmpVertexPositionBins()[0].key) { wX += (store->getConfig().histConf.lowHistBoundariesXYZ[0] + iBin * store->getConfig().histConf.binSizeHistX + store->getConfig().histConf.binSizeHistX / 2) * store->getHistogramXYZ()[0].get()[iBin]; sumWX += store->getHistogramXYZ()[0].get()[iBin]; @@ -267,7 +286,7 @@ GPUg() void computeZCentroidsKernel(DeviceStoreVertexerHIP* store, float tmpY{store->getConfig().histConf.lowHistBoundariesXYZ[1] + store->getTmpVertexPositionBins()[1].key * store->getConfig().histConf.binSizeHistY + store->getConfig().histConf.binSizeHistY / 2}; int sumWY{store->getTmpVertexPositionBins()[1].value}; float wY{tmpY * store->getTmpVertexPositionBins()[1].value}; - for (int iBin{gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[1].key - binOpeningY)}; iBin < gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[1].key + binOpeningY + 1, store->getConfig().histConf.nBinsXYZ[1] - 1); ++iBin) { + for (int iBin{o2::gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[1].key + binOpeningY + 1, store->getConfig().histConf.nBinsXYZ[1] - 1); ++iBin) { if (iBin != store->getTmpVertexPositionBins()[1].key) { wY += (store->getConfig().histConf.lowHistBoundariesXYZ[1] + iBin * store->getConfig().histConf.binSizeHistY + store->getConfig().histConf.binSizeHistY / 2) * store->getHistogramXYZ()[1].get()[iBin]; sumWY += store->getHistogramXYZ()[1].get()[iBin]; @@ -299,7 +318,7 @@ GPUg() void computeVertexKernel(DeviceStoreVertexerHIP* store, const int vertInd float ez{0.f}; int sumWZ{store->getTmpVertexPositionBins()[2].value}; float wZ{z * store->getTmpVertexPositionBins()[2].value}; - for (int iBin{gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[2].key - binOpeningZ)}; iBin < gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[2].key + binOpeningZ + 1, store->getConfig().histConf.nBinsXYZ[2] - 1); ++iBin) { + for (int iBin{o2::gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[2].key + binOpeningZ + 1, store->getConfig().histConf.nBinsXYZ[2] - 1); ++iBin) { if (iBin != store->getTmpVertexPositionBins()[2].key) { wZ += (store->getConfig().histConf.lowHistBoundariesXYZ[2] + iBin * store->getConfig().histConf.binSizeHistZ + store->getConfig().histConf.binSizeHistZ / 2) * store->getHistogramXYZ()[2].get()[iBin]; sumWZ += store->getHistogramXYZ()[2].get()[iBin]; @@ -317,7 +336,7 @@ GPUg() void computeVertexKernel(DeviceStoreVertexerHIP* store, const int vertInd } } } -} // namespace GPU +} // namespace gpu void VertexerTraitsHIP::computeTracklets() { @@ -325,17 +344,17 @@ void VertexerTraitsHIP::computeTracklets() std::cout << "\t\tno clusters on layer 1. Returning.\n"; return; } - const dim3 threadsPerBlock{GPU::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{GPU::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{gpu::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{gpu::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; - hipLaunchKernelGGL((GPU::trackleterKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + hipLaunchKernelGGL((gpu::trackleterKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), - GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, + gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer, mVrtParams.phiCut); - hipLaunchKernelGGL((GPU::trackleterKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + hipLaunchKernelGGL((gpu::trackleterKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), - GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, + gpu::TrackletingLayerOrder::fromMiddleToOuterLayer, mVrtParams.phiCut); gpuThrowOnError(); @@ -343,8 +362,8 @@ void VertexerTraitsHIP::computeTracklets() #ifdef _ALLOW_DEBUG_TREES_ITS_ if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { mDebugger->fillCombinatoricsTree(mClusters, - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer), mEvent); } #endif @@ -356,11 +375,11 @@ void VertexerTraitsHIP::computeTrackletMatching() std::cout << "\t\tno clusters on layer 1. Returning.\n"; return; } - const dim3 threadsPerBlock{GPU::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{GPU::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{gpu::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{gpu::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); - hipLaunchKernelGGL((GPU::trackletSelectionKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + hipLaunchKernelGGL((gpu::trackletSelectionKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), true, // isInitRun mVrtParams.tanLambdaCut, @@ -372,7 +391,7 @@ void VertexerTraitsHIP::computeTrackletMatching() mStoreVertexerGPU.getNExclusiveFoundLines().get(), mClusters[1].size()); - hipLaunchKernelGGL((GPU::trackletSelectionKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + hipLaunchKernelGGL((gpu::trackletSelectionKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), false, // isInitRun mVrtParams.tanLambdaCut, @@ -383,8 +402,8 @@ void VertexerTraitsHIP::computeTrackletMatching() #ifdef _ALLOW_DEBUG_TREES_ITS_ if (isDebugFlag(VertexerDebug::TrackletTreeAll)) { mDebugger->fillTrackletSelectionTree(mClusters, - mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), - mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer), mStoreVertexerGPU.getDupletIndicesFromGPU(), mEvent); } @@ -404,15 +423,15 @@ void VertexerTraitsHIP::computeVertices() std::cout << "\t\tno clusters on layer 1. Returning.\n"; return; } - const dim3 threadsPerBlock{GPU::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; - const dim3 blocksGrid{GPU::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + const dim3 threadsPerBlock{gpu::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{gpu::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); int nLines = mStoreVertexerGPU.getNExclusiveFoundLines().getElementFromDevice(mClusters[1].size() - 1) + mStoreVertexerGPU.getNFoundLines().getElementFromDevice(mClusters[1].size() - 1); int nCentroids{static_cast<int>(nLines * (nLines - 1) / 2)}; int* histogramXY[2] = {mStoreVertexerGPU.getHistogramXYZ()[0].get(), mStoreVertexerGPU.getHistogramXYZ()[1].get()}; float tmpArrayLow[2] = {mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[1]}; float tmpArrayHigh[2] = {mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[1]}; - hipLaunchKernelGGL((GPU::computeCentroidsKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), + hipLaunchKernelGGL((gpu::computeCentroidsKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), mVrtParams.histPairCut); (void)hipcub::DeviceHistogram::MultiHistogramEven<2, 2>(reinterpret_cast<void*>(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage @@ -435,7 +454,7 @@ void VertexerTraitsHIP::computeVertices() mStoreVertexerGPU.getTmpVertexPositionBins().get() + 1, mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0]); - hipLaunchKernelGGL((GPU::computeZCentroidsKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), mVrtParams.histPairCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[0], mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[1]); + hipLaunchKernelGGL((gpu::computeZCentroidsKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), mVrtParams.histPairCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[0], mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[1]); (void)hipcub::DeviceHistogram::HistogramEven(reinterpret_cast<void*>(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage bufferSize, // temp_storage_bytes @@ -461,9 +480,9 @@ void VertexerTraitsHIP::computeVertices() mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2] - 1}); } #endif - hipLaunchKernelGGL((GPU::computeVertexKernel), dim3(blocksGrid), dim3(5), 0, 0, getDeviceContextPtr(), iVertex, mVrtParams.clusterContributorsCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[2]); + hipLaunchKernelGGL((gpu::computeVertexKernel), dim3(blocksGrid), dim3(5), 0, 0, getDeviceContextPtr(), iVertex, mVrtParams.clusterContributorsCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[2]); } - std::vector<GPU::GPUVertex> vertices; + std::vector<gpu::GPUVertex> vertices; vertices.resize(mStoreVertexerGPU.getConfig().nMaxVertices); mStoreVertexerGPU.getVertices().copyIntoSizedVector(vertices); @@ -479,23 +498,23 @@ void VertexerTraitsHIP::computeVertices() #ifdef _ALLOW_DEBUG_TREES_ITS_ void VertexerTraitsHIP::computeMCFiltering() { - std::vector<Tracklet> tracklets01 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); - std::vector<Tracklet> tracklets12 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); - std::vector<int> labels01 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); - std::vector<int> labels12 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); + std::vector<Tracklet> tracklets01 = mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector<Tracklet> tracklets12 = mStoreVertexerGPU.getRawDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer); + std::vector<int> labels01 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector<int> labels12 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer); VertexerStoreConfigurationGPU tmpGPUConf; const int stride = tmpGPUConf.maxTrackletsPerCluster; filterTrackletsWithMC(tracklets01, tracklets12, labels01, labels12, stride); - mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, labels01); - mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, tracklets01); - mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, labels12); - mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, tracklets12); + mStoreVertexerGPU.updateFoundDuplets(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer, labels01); + mStoreVertexerGPU.updateDuplets(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer, tracklets01); + mStoreVertexerGPU.updateFoundDuplets(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer, labels12); + mStoreVertexerGPU.updateDuplets(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer, tracklets12); if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { mDebugger->fillCombinatoricsTree(mClusters, - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), - mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(gpu::TrackletingLayerOrder::fromMiddleToOuterLayer), mEvent); } } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index 244e15f495426..8e71bade83e5f 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -15,7 +15,7 @@ #ifndef TRACKINGITSU_INCLUDE_CACELL_H_ #define TRACKINGITSU_INCLUDE_CACELL_H_ -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #include <vector> #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index 66bfab666c23b..7a64698998b37 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -15,25 +15,27 @@ #ifndef TRACKINGITSU_INCLUDE_CACLUSTER_H_ #define TRACKINGITSU_INCLUDE_CACLUSTER_H_ -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #endif +#include "GPUCommonRtypes.h" #include "ITStracking/Definitions.h" #include "ITStracking/MathUtils.h" -#include "ITStracking/IndexTableUtils.h" namespace o2 { namespace its { +class IndexTableUtils; + struct Cluster final { Cluster() = default; Cluster(const float x, const float y, const float z, const int idx); - Cluster(const int, const Cluster&); - Cluster(const int, const float3&, const Cluster&); - void Init(const int, const float3&, const Cluster&); + 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&); float xCoordinate; // = -999.f; float yCoordinate; // = -999.f; @@ -42,6 +44,8 @@ struct Cluster final { float rCoordinate; // = -999.f; int clusterId; // = -1; int indexTableBinIndex; // = -1; + + ClassDefNV(Cluster, 1); }; struct TrackingFrameInfo { @@ -55,6 +59,8 @@ struct TrackingFrameInfo { float alphaTrackingFrame; GPUArray<float, 2> positionTrackingFrame = {-1., -1.}; GPUArray<float, 3> covarianceTrackingFrame = {999., 999., 999.}; + + ClassDefNV(TrackingFrameInfo, 1); }; } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index b179c35e3b482..4f1b6c4c8cab1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -88,11 +88,12 @@ GPUhdi() Line::Line(const float firstPoint[3], const float secondPoint[3]) cosinesDirector[i] = secondPoint[i] - firstPoint[i]; } - float inverseNorm{1.f / gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; + 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) + for (int index{0}; index < 3; ++index) { cosinesDirector[index] *= inverseNorm; + } } GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) @@ -105,11 +106,12 @@ GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, cons cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; - float inverseNorm{1.f / gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; + 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) + for (int index{0}; index < 3; ++index) { cosinesDirector[index] *= inverseNorm; + } } #ifdef _ALLOW_DEBUG_TREES_ITS_ @@ -123,8 +125,8 @@ GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, cons cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; - float inverseNorm{1.f / gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; + 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; @@ -143,20 +145,21 @@ inline float Line::getDistanceFromPoint(const Line& line, const std::array<float DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); } - return gpu::CAMath::Sqrt(DCASquared); + 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) + 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 gpu::CAMath::Sqrt(DCASquared); + return o2::gpu::CAMath::Sqrt(DCASquared); } GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) @@ -175,7 +178,7 @@ GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const distance += (secondLine.originPoint[i] - firstLine.originPoint[i]) * normalVector[i]; } if (norm > precision) { - return gpu::CAMath::Abs(distance / gpu::CAMath::Sqrt(norm)); + return o2::gpu::CAMath::Abs(distance / o2::gpu::CAMath::Sqrt(norm)); } else { #if defined(__CUDACC__) || defined(__HIPCC__) float stdOriginPoint[3]; @@ -193,8 +196,9 @@ GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], float destArray[6]) { float cdelta{0.}; - for (int i{0}; i < 3; ++i) + 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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 866e3480cd69a..930aa273d3766 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -15,10 +15,12 @@ #ifndef TRACKINGITSU_INCLUDE_CONFIGURATION_H_ #define TRACKINGITSU_INCLUDE_CONFIGURATION_H_ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #include <climits> #include <vector> #include <cmath> +#endif #include "ITStracking/Constants.h" @@ -44,32 +46,43 @@ class Configuration : public Param }; struct TrackingParameters { - TrackingParameters& operator=(const TrackingParameters& t); + TrackingParameters& operator=(const TrackingParameters& t) = default; int CellMinimumLevel(); + int CellsPerRoad() const { return NLayers - 2; } + int TrackletsPerRoad() const { return NLayers - 1; } + + int NLayers = 7; + std::vector<float> LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; + std::vector<float> LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; + int ZBins{256}; + int PhiBins{128}; /// General parameters int ClusterSharing = 0; int MinTrackLength = 7; /// Trackleting cuts float TrackletMaxDeltaPhi = 0.3f; - float TrackletMaxDeltaZ[constants::its::TrackletsPerRoad] = {0.1f, 0.1f, 0.3f, 0.3f, 0.3f, 0.3f}; + std::vector<float> TrackletMaxDeltaZ = {0.1f, 0.1f, 0.3f, 0.3f, 0.3f, 0.3f}; /// Cell finding cuts float CellMaxDeltaTanLambda = 0.025f; - float CellMaxDCA[constants::its::CellsPerRoad] = {0.05f, 0.04f, 0.05f, 0.2f, 0.4f}; + std::vector<float> CellMaxDCA = {0.05f, 0.04f, 0.05f, 0.2f, 0.4f}; float CellMaxDeltaPhi = 0.14f; - float CellMaxDeltaZ[constants::its::CellsPerRoad] = {0.2f, 0.4f, 0.5f, 0.6f, 3.0f}; + std::vector<float> CellMaxDeltaZ = {0.2f, 0.4f, 0.5f, 0.6f, 3.0f}; /// Neighbour finding cuts - float NeighbourMaxDeltaCurvature[constants::its::CellsPerRoad - 1] = {0.008f, 0.0025f, 0.003f, 0.0035f}; - float NeighbourMaxDeltaN[constants::its::CellsPerRoad - 1] = {0.002f, 0.0090f, 0.002f, 0.005f}; + std::vector<float> NeighbourMaxDeltaCurvature = {0.008f, 0.0025f, 0.003f, 0.0035f}; + std::vector<float> NeighbourMaxDeltaN = {0.002f, 0.0090f, 0.002f, 0.005f}; + /// Fitter parameters + bool UseMatBudLUT = false; + std::array<float, 2> FitIterationMaxChi2 = {o2::constants::math::VeryBig, o2::constants::math::VeryBig}; }; struct MemoryParameters { /// Memory coefficients - MemoryParameters& operator=(const MemoryParameters& t); + MemoryParameters& operator=(const MemoryParameters& t) = default; int MemoryOffset = 256; - float CellsMemoryCoefficients[constants::its::CellsPerRoad] = {2.3208e-08f, 2.104e-08f, 1.6432e-08f, 1.2412e-08f, 1.3543e-08f}; - float TrackletsMemoryCoefficients[constants::its::TrackletsPerRoad] = {0.0016353f, 0.0013627f, 0.000984f, 0.00078135f, 0.00057934f, 0.00052217f}; + std::vector<float> CellsMemoryCoefficients = {2.3208e-08f, 2.104e-08f, 1.6432e-08f, 1.2412e-08f, 1.3543e-08f}; + std::vector<float> TrackletsMemoryCoefficients = {0.0016353f, 0.0013627f, 0.000984f, 0.00078135f, 0.00057934f, 0.00052217f}; }; inline int TrackingParameters::CellMinimumLevel() @@ -77,40 +90,12 @@ inline int TrackingParameters::CellMinimumLevel() return MinTrackLength - constants::its::ClustersPerCell + 1; } -inline TrackingParameters& TrackingParameters::operator=(const TrackingParameters& t) -{ - this->ClusterSharing = t.ClusterSharing; - this->MinTrackLength = t.MinTrackLength; - /// Trackleting cuts - this->TrackletMaxDeltaPhi = t.TrackletMaxDeltaPhi; - for (int iT = 0; iT < constants::its::TrackletsPerRoad; ++iT) - this->TrackletMaxDeltaZ[iT] = t.TrackletMaxDeltaZ[iT]; - /// Cell finding cuts - this->CellMaxDeltaTanLambda = t.CellMaxDeltaTanLambda; - this->CellMaxDeltaPhi = t.CellMaxDeltaPhi; - for (int iC = 0; iC < constants::its::CellsPerRoad; ++iC) { - this->CellMaxDCA[iC] = t.CellMaxDCA[iC]; - this->CellMaxDeltaZ[iC] = t.CellMaxDeltaZ[iC]; - } - /// Neighbour finding cuts - for (int iC = 0; iC < constants::its::CellsPerRoad - 1; ++iC) { - this->NeighbourMaxDeltaCurvature[iC] = t.NeighbourMaxDeltaCurvature[iC]; - this->NeighbourMaxDeltaN[iC] = t.NeighbourMaxDeltaN[iC]; - } - return *this; -} - -inline MemoryParameters& MemoryParameters::operator=(const MemoryParameters& t) -{ - this->MemoryOffset = t.MemoryOffset; - for (int iC = 0; iC < constants::its::CellsPerRoad; ++iC) - this->CellsMemoryCoefficients[iC] = t.CellsMemoryCoefficients[iC]; - for (int iT = 0; iT < constants::its::TrackletsPerRoad; ++iT) - this->TrackletsMemoryCoefficients[iT] = t.TrackletsMemoryCoefficients[iT]; - return *this; -} - struct VertexingParameters { + std::vector<float> LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; + std::vector<float> LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; + int ZBins{256}; + int PhiBins{128}; + float zCut = 0.002f; //0.002f float phiCut = 0.005f; //0.005f float pairCut = 0.04f; @@ -162,7 +147,7 @@ struct VertexerStoreConfigurationGPU { int maxTrkCap, int maxVert); - // o2::its::GPU::Vector constructor requires signed size for initialisation + // o2::its::gpu::Vector constructor requires signed size for initialisation int tmpCUBBufferSize = 25e5; int maxTrackletsPerCluster = 2e2; int clustersPerLayerCapacity = 4e4; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index d4e25ea415014..5f9f22a51e035 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -15,12 +15,15 @@ #ifndef TRACKINGITSU_INCLUDE_CONSTANTS_H_ #define TRACKINGITSU_INCLUDE_CONSTANTS_H_ -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <climits> #include <vector> #endif #include "ITStracking/Definitions.h" +#include "CommonConstants/MathConstants.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" namespace o2 { @@ -41,42 +44,68 @@ constexpr float FloatMinThreshold{1e-20f}; namespace its { -constexpr int LayersNumber{7}; constexpr int LayersNumberVertexer{3}; -constexpr int TrackletsPerRoad{LayersNumber - 1}; -constexpr int CellsPerRoad{LayersNumber - 2}; constexpr int ClustersPerCell{3}; constexpr int UnusedIndex{-1}; constexpr float Resolution{0.0005f}; -GPU_HOST_DEVICE constexpr GPUArray<float, LayersNumber> LayersZCoordinate() +GPUhdi() constexpr GPUArray<float, 3> VertexerHistogramVolume() +{ + return GPUArray<float, 3>{{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<float, LayersNumber> LayersZCoordinate() { constexpr double s = 1.; // safety margin return GPUArray<float, LayersNumber>{{16.333f + s, 16.333f + s, 16.333f + s, 42.140f + s, 42.140f + s, 73.745f + s, 73.745f + s}}; } -GPU_HOST_DEVICE constexpr GPUArray<float, LayersNumber> LayersRCoordinate() +GPUhdi() constexpr GPUArray<float, LayersNumber> LayersRCoordinate() { return GPUArray<float, LayersNumber>{{2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}}; } -GPU_HOST_DEVICE constexpr GPUArray<float, 3> VertexerHistogramVolume() + +constexpr int ZBins{256}; +constexpr int PhiBins{128}; +constexpr float InversePhiBinSize{PhiBins / constants::math::TwoPi}; +GPUhdi() constexpr GPUArray<float, LayersNumber> InverseZBinSize() { - return GPUArray<float, 3>{{1.98, 1.98, 40.f}}; + constexpr auto zSize = LayersZCoordinate(); + return GPUArray<float, LayersNumber>{{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]; } -} // namespace its -namespace index_table +GPUhdi() int getPhiBinIndex(const float currentPhi) { -constexpr int ZBins{20}; -constexpr int PhiBins{20}; -constexpr float InversePhiBinSize{constants::index_table::PhiBins / constants::math::TwoPi}; -GPU_HOST_DEVICE constexpr GPUArray<float, its::LayersNumber> InverseZBinSize() + return (currentPhi * InversePhiBinSize); +} + +GPUhdi() int getBinIndex(const int zIndex, const int phiIndex) { - constexpr auto zSize = its::LayersZCoordinate(); - return GPUArray<float, its::LayersNumber>{{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])}}; + return o2::gpu::GPUCommonMath::Min(phiIndex * ZBins + zIndex, + ZBins * PhiBins - 1); } -} // namespace index_table + +GPUhdi() constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } + +} // namespace its2 namespace pdgcodes { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/DBScan.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/DBScan.h deleted file mode 100644 index 1e8d16b42bdea..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/DBScan.h +++ /dev/null @@ -1,141 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 DBScan.h -/// \brief -/// - -#ifndef O2_ITS_TRACKING_DBSCAN_H_ -#define O2_ITS_TRACKING_DBSCAN_H_ - -#include <algorithm> -#include "ITStracking/Graph.h" - -namespace o2 -{ -namespace its -{ - -typedef std::pair<int, unsigned char> State; - -template <typename T> -class DBScan : Graph<T> -{ - public: - DBScan() = delete; - explicit DBScan(const size_t nThreads); - void init(std::vector<T>&, std::function<bool(const T& v1, const T& v2)>); - void classifyVertices(const int); - void classifyVertices(std::function<unsigned char(std::vector<Edge>&)> classFunction); - void classifyVertices(std::function<unsigned char(std::vector<Edge>&)> classFunction, std::function<bool(State&, State&)> sortFunction); - std::vector<State> getStates() const { return mStates; } - std::vector<int> getCores(); - std::vector<std::vector<int>> computeClusters(); - - private: - std::vector<State> mStates; - std::function<unsigned char(std::vector<Edge>&)> mClassFunction; -}; - -template <typename T> -DBScan<T>::DBScan(const size_t nThreads) : Graph<T>(nThreads) -{ -} - -template <typename T> -void DBScan<T>::init(std::vector<T>& vertices, std::function<bool(const T& v1, const T& v2)> discFunction) -{ - this->Graph<T>::init(vertices); - this->Graph<T>::computeEdges(discFunction); -} - -template <typename T> -void DBScan<T>::classifyVertices(const int nContributors) -{ - classifyVertices([nContributors](std::vector<o2::its::Edge>& edges) { return edges.size() == 0 ? 0 : edges.size() >= static_cast<size_t>(nContributors - 1) ? 2 : 1; }, - [](State& s1, State& s2) { return static_cast<int>(s1.second) > static_cast<int>(s2.second); }); -} - -template <typename T> -void DBScan<T>::classifyVertices(std::function<unsigned char(std::vector<Edge>& edges)> classFunction) -{ - mClassFunction = classFunction; - const size_t size = {this->mVertices->size()}; - mStates.resize(size); - - if (!this->isMultiThreading()) { - for (size_t iVertex{0}; iVertex < size; ++iVertex) { - mStates[iVertex] = std::make_pair<int, unsigned char>(iVertex, classFunction(this->getEdges()[iVertex])); - } - } else { - const size_t stride{static_cast<size_t>(std::ceil(this->mVertices->size() / static_cast<size_t>(this->mExecutors.size())))}; - for (size_t iExecutor{0}; iExecutor < this->mExecutors.size(); ++iExecutor) { - // We cannot pass a template function to std::thread(), using lambda instead - this->mExecutors[iExecutor] = std::thread( - [iExecutor, stride, this](const auto& classFunction) { - for (size_t iVertex{iExecutor * stride}; iVertex < stride * (iExecutor + 1) && iVertex < this->mVertices->size(); ++iVertex) { - mStates[iVertex] = std::make_pair<int, unsigned char>(iVertex, classFunction(this->getEdges()[iVertex])); - } - }, - mClassFunction); - } - } - for (auto&& thread : this->mExecutors) { - thread.join(); - } -} - -template <typename T> -void DBScan<T>::classifyVertices(std::function<unsigned char(std::vector<Edge>&)> classFunction, std::function<bool(State&, State&)> sortFunction) -{ - classifyVertices(classFunction); - std::sort(mStates.begin(), mStates.end(), sortFunction); -} - -template <typename T> -std::vector<int> DBScan<T>::getCores() -{ - std::vector<State> cores; - std::vector<int> coreIndices; - std::copy_if(mStates.begin(), mStates.end(), std::back_inserter(cores), [](const State& state) { return state.second == 2; }); - std::transform(cores.begin(), cores.end(), std::back_inserter(coreIndices), [](const State& state) -> int { return state.first; }); - return coreIndices; -} - -template <typename T> -std::vector<std::vector<int>> DBScan<T>::computeClusters() -{ - std::vector<std::vector<int>> clusters; - std::vector<int> cores = getCores(); - std::vector<unsigned char> usedVertices(this->mVertices->size(), false); - - for (size_t core{0}; core < cores.size(); ++core) { - if (!usedVertices[cores[core]]) { - std::vector<unsigned char> clusterFlags = this->getCluster(cores[core]); - std::transform(usedVertices.begin(), usedVertices.end(), clusterFlags.begin(), usedVertices.begin(), std::logical_or<>()); - clusters.emplace_back(std::move(this->getClusterIndices(clusterFlags))); - } - } - return clusters; -} - -struct Centroid final { - Centroid() = default; - Centroid(int* indices, float* position); - void init(); - static float ComputeDistance(const Centroid& c1, const Centroid& c2); - - int mIndices[2]; - float mPosition[3]; -}; - -} // namespace its -} // namespace o2 -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h index 13ad78a0362bb..b54b91801b267 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h @@ -17,7 +17,7 @@ // #define _ALLOW_DEBUG_TREES_ITS_ // to allow debug (vertexer only) -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #endif @@ -29,9 +29,9 @@ #define CA_DEBUGGER(x) \ do { \ } while (0) -#ifndef NDEBUG -#define NDEBUG 1 -#endif +// #ifndef NDEBUG +// #define NDEBUG 1 +// #endif #endif #if defined(CUDA_ENABLED) @@ -59,11 +59,13 @@ #define MATH_CEIL ceil +#ifndef GPUCA_GPUCODE_DEVICE #include <cstddef> +#endif #include "ITStrackingCUDA/Array.h" -template <typename T, std::size_t Size> -using GPUArray = o2::its::GPU::Array<T, Size>; +template <typename T, size_t Size> +using GPUArray = o2::its::gpu::Array<T, Size>; typedef cudaStream_t GPUStream; @@ -92,7 +94,7 @@ using GPUArray = std::array<T, Size>; #include "ITStrackingCUDA/Array.h" template <typename T, size_t Size> -using GPUArray = o2::its::GPU::Array<T, Size>; +using GPUArray = o2::its::gpu::Array<T, Size>; #endif typedef struct _dummyStream { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Graph.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Graph.h deleted file mode 100644 index d1fca077e29c6..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Graph.h +++ /dev/null @@ -1,232 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Graph.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ALGORITHMS_H_ -#define TRACKINGITSU_INCLUDE_ALGORITHMS_H_ - -#include <array> -#include <cmath> -#include <condition_variable> -#include <functional> -#include <iostream> -#include <mutex> -#include <queue> -#include <thread> -#include <utility> -#include <vector> - -namespace o2 -{ -namespace its -{ - -typedef int Edge; - -class Barrier -{ - public: - explicit Barrier(std::size_t count) : count(count) {} - void Wait(); - - private: - std::mutex mutex; - std::condition_variable condition; - std::size_t count; -}; - -template <typename T> -class Graph -{ - public: - Graph() = delete; - explicit Graph(const size_t nThreads = 1); - void init(std::vector<T>&); - std::vector<unsigned char> getCluster(const int); - std::vector<int> getClusterIndices(const int); - std::vector<int> getClusterIndices(const std::vector<unsigned char> /* , const int*/); - void computeEdges(std::function<bool(const T& v1, const T& v2)>); - std::vector<std::vector<Edge>> getEdges() const { return mEdges; } - char isMultiThreading() const { return mIsMultiThread; } - - std::vector<T>* mVertices = nullptr; // Observer pointer - std::vector<std::thread> mExecutors; // Difficult to pass - - private: - void findVertexEdges(std::vector<Edge>& localEdges, const T& vertex, const size_t vId, const size_t size); - - // Multithread block - size_t mNThreads; - char mIsMultiThread; - - // Common data members - std::function<bool(const T&, const T&)> mLinkFunction; - std::vector<std::vector<Edge>> mEdges; - std::vector<unsigned char> mVisited; -}; - -template <typename T> -Graph<T>::Graph(const size_t nThreads) : mNThreads{nThreads} -{ - mIsMultiThread = nThreads > 1 ? true : false; -} - -template <typename T> -void Graph<T>::init(std::vector<T>& vertices) -{ - - // Graph initialization - mVertices = &vertices; - if (mIsMultiThread) { - mNThreads = std::min(static_cast<const size_t>(std::thread::hardware_concurrency()), mNThreads); - mExecutors.resize(mNThreads); - } - - mEdges.resize(vertices.size()); - mVisited.resize(vertices.size(), false); -} - -template <typename T> -void Graph<T>::computeEdges(std::function<bool(const T& v1, const T& v2)> linkFunction) -{ - mLinkFunction = linkFunction; - int tot_nedges = 0; - const size_t size = {mVertices->size()}; - if (!mIsMultiThread) { - for (size_t iVertex{0}; iVertex < size; ++iVertex) { - findVertexEdges(mEdges[iVertex], (*mVertices)[iVertex], iVertex, size); - tot_nedges += static_cast<int>(mEdges[iVertex].size()); - } - } else { - mNThreads = std::min(static_cast<const size_t>(std::thread::hardware_concurrency()), mNThreads); - mExecutors.resize(mNThreads); - const size_t stride{static_cast<size_t>(std::ceil(mVertices->size() / static_cast<size_t>(mExecutors.size())))}; - for (size_t iExecutor{0}; iExecutor < mExecutors.size(); ++iExecutor) { - // We cannot pass a template function to std::thread(), using lambda instead - mExecutors[iExecutor] = std::thread( - [iExecutor, stride, this](const auto& linkFunction) { - for (size_t iVertex1{iExecutor * stride}; iVertex1 < stride * (iExecutor + 1) && iVertex1 < mVertices->size(); ++iVertex1) { - for (size_t iVertex2{0}; iVertex2 < mVertices->size(); ++iVertex2) { - if (iVertex1 != iVertex2 && linkFunction((*mVertices)[iVertex1], (*mVertices)[iVertex2])) { - mEdges[iVertex1].emplace_back(iVertex2); - } - } - } - }, - mLinkFunction); - } - } - for (auto&& thread : mExecutors) { - thread.join(); - } -} -template <typename T> -void Graph<T>::findVertexEdges(std::vector<Edge>& localEdges, const T& vertex, const size_t vId, const size_t size) -{ - for (size_t iVertex2{0}; iVertex2 < size; ++iVertex2) { - if (vId != iVertex2 && mLinkFunction(vertex, (*mVertices)[iVertex2])) { - localEdges.emplace_back(iVertex2); - } - } -} - -template <typename T> -std::vector<unsigned char> Graph<T>::getCluster(const int vertexId) -{ - // This method uses a BFS algorithm to return all the graph - // vertex ids belonging to a graph - std::vector<int> indices; - std::vector<unsigned char> visited(mVertices->size(), false); - - if (!mIsMultiThread) { - std::queue<int> idQueue; - idQueue.emplace(vertexId); - visited[vertexId] = true; - - // Consume the queue - while (!idQueue.empty()) { - const int id = idQueue.front(); - idQueue.pop(); - for (Edge edge : mEdges[id]) { - if (!visited[edge]) { - idQueue.emplace(edge); - indices.emplace_back(edge); - visited[edge] = true; - } - } - } - } else { - const size_t stride{static_cast<size_t>(std::ceil(static_cast<float>(this->mVertices->size()) / static_cast<size_t>(this->mExecutors.size())))}; - std::vector<unsigned char> frontier(mVertices->size(), false); - std::vector<unsigned char> flags(mVertices->size(), false); - - frontier[vertexId] = true; - int counter{0}; - while (std::any_of(frontier.begin(), frontier.end(), [](const char t) { return t; })) { - flags.resize(mVertices->size(), false); - Barrier barrier(mExecutors.size()); - for (size_t iExecutor{0}; iExecutor < this->mExecutors.size(); ++iExecutor) { - mExecutors[iExecutor] = std::thread( - [&stride, &frontier, &visited, &barrier, &flags, this](const int executorId) { - for (size_t iVertex{executorId * stride}; iVertex < stride * (executorId + 1) && iVertex < this->mVertices->size(); ++iVertex) { - if (frontier[iVertex]) { - flags[iVertex] = true; - frontier[iVertex] = false; - visited[iVertex] = true; - } - } - barrier.Wait(); - for (size_t iVertex{executorId * stride}; iVertex < stride * (executorId + 1) && iVertex < this->mVertices->size(); ++iVertex) { - if (flags[iVertex]) { - for (auto& edge : mEdges[iVertex]) { - if (!visited[edge]) { - frontier[edge] = true; - } - } - } - } - }, - iExecutor); - } - for (auto&& thread : mExecutors) { - thread.join(); - } - } - } - return visited; -} - -template <typename T> -std::vector<int> Graph<T>::getClusterIndices(const std::vector<unsigned char> visited) -{ - // Return a smaller vector only with the int IDs of the vertices belonging to cluster - std::vector<int> indices; - for (size_t iVisited{0}; iVisited < visited.size(); ++iVisited) { - if (visited[iVisited]) { - indices.emplace_back(iVisited); - } - } - return indices; -} - -template <typename T> -std::vector<int> Graph<T>::getClusterIndices(const int vertexId) -{ - std::vector<unsigned char> visited = std::move(getCluster(vertexId)); - return getClusterIndices(visited); -} - -} // namespace its -} // namespace o2 - -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h index 8afe8dbbe4b78..621052f088f91 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h @@ -55,7 +55,6 @@ constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5 constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; -void loadConfigurations(const std::string&); std::vector<ROframe> loadEventData(const std::string&); void loadEventData(ROframe& events, gsl::span<const itsmft::CompClusterExt> clusters, gsl::span<const unsigned char>::iterator& pattIt, const itsmft::TopologyDictionary& dict, @@ -63,7 +62,7 @@ void loadEventData(ROframe& events, gsl::span<const itsmft::CompClusterExt> clus int loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& events, gsl::span<const itsmft::CompClusterExt> clusters, gsl::span<const unsigned char>::iterator& pattIt, const itsmft::TopologyDictionary& dict, const dataformats::MCTruthContainer<MCCompLabel>* mClsLabels = nullptr); -void generateSimpleData(ROframe& event, const int phiDivs, const int zDivs); +// void generateSimpleData(ROframe& event, const int phiDivs, const int zDivs); void convertCompactClusters(gsl::span<const itsmft::CompClusterExt> clusters, gsl::span<const unsigned char>::iterator& pattIt, diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h index f703711ee3679..3f9e85f887896 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h @@ -15,13 +15,14 @@ #ifndef TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ #define TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #include <utility> #include <vector> #endif #include "ITStracking/Constants.h" +#include "ITStracking/Configuration.h" #include "ITStracking/Definitions.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" @@ -31,42 +32,64 @@ namespace o2 namespace its { -namespace index_table_utils +class IndexTableUtils { -float getInverseZBinSize(const int); -GPUhdi() int getZBinIndex(const int, const float); -GPUhdi() int getPhiBinIndex(const float); -GPUhdi() int getBinIndex(const int, const int); -GPUhdi() int countRowSelectedBins( - const GPUArray<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>&, const int, const int, - const int); -} // namespace index_table_utils + public: + template <class T> + void setTrackingParameters(const T& params); + float getInverseZCoordinate(const int layerIndex) const; + GPUhdi() int getZBinIndex(const int, const float) const; + GPUhdi() int getPhiBinIndex(const float) const; + GPUhdi() int getBinIndex(const int, const int) const; + GPUhdi() int countRowSelectedBins(const int*, const int, const int, const int) const; -inline float getInverseZCoordinate(const int layerIndex) + GPUhdi() int getNzBins() const { return mNzBins; } + GPUhdi() int getNphiBins() const { return mNphiBins; } + GPUhdi() float getLayerZ(int i) const { return mLayerZ[i]; } + + private: + int mNzBins = 0; + int mNphiBins = 0; + float mInversePhiBinSize = 0.f; + std::vector<float> mLayerZ; + std::vector<float> mInverseZBinSize; +}; + +template <class T> +inline void IndexTableUtils::setTrackingParameters(const T& params) +{ + mInversePhiBinSize = params.PhiBins / constants::math::TwoPi; + mInverseZBinSize.resize(params.LayerZ.size()); + mNzBins = params.ZBins; + mNphiBins = params.PhiBins; + mLayerZ = params.LayerZ; + for (unsigned int iL{0}; iL < mInverseZBinSize.size(); ++iL) { + mInverseZBinSize[iL] = 0.5f * params.ZBins / params.LayerZ[iL]; + } +} + +inline float IndexTableUtils::getInverseZCoordinate(const int layerIndex) const { - return 0.5f * constants::index_table::ZBins / constants::its::LayersZCoordinate()[layerIndex]; + return 0.5f * mNzBins / mLayerZ[layerIndex]; } -GPUhdi() int index_table_utils::getZBinIndex(const int layerIndex, const float zCoordinate) +GPUhdi() int IndexTableUtils::getZBinIndex(const int layerIndex, const float zCoordinate) const { - return (zCoordinate + constants::its::LayersZCoordinate()[layerIndex]) * - constants::index_table::InverseZBinSize()[layerIndex]; + return (zCoordinate + mLayerZ[layerIndex]) * mInverseZBinSize[layerIndex]; } -GPUhdi() int index_table_utils::getPhiBinIndex(const float currentPhi) +GPUhdi() int IndexTableUtils::getPhiBinIndex(const float currentPhi) const { - return (currentPhi * constants::index_table::InversePhiBinSize); + return (currentPhi * mInversePhiBinSize); } -GPUhdi() int index_table_utils::getBinIndex(const int zIndex, const int phiIndex) +GPUhdi() int IndexTableUtils::getBinIndex(const int zIndex, const int phiIndex) const { - return gpu::GPUCommonMath::Min(phiIndex * constants::index_table::ZBins + zIndex, - constants::index_table::ZBins * constants::index_table::PhiBins - 1); + return o2::gpu::GPUCommonMath::Min(phiIndex * mNzBins + zIndex, mNzBins * mNphiBins - 1); } -GPUhdi() int index_table_utils::countRowSelectedBins( - const GPUArray<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>& indexTable, - const int phiBinIndex, const int minZBinIndex, const int maxZBinIndex) +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}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h index d6cfa1d6f6670..8e8df9e9c7a74 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -15,7 +15,7 @@ #ifndef TRACKINGITSU_INCLUDE_CAUTILS_H_ #define TRACKINGITSU_INCLUDE_CAUTILS_H_ -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #include <cmath> #include <cassert> @@ -47,7 +47,7 @@ GPUhdni() float computeTanDipAngle(float x1, float y1, float x2, float y2, float GPUhdi() float math_utils::calculatePhiCoordinate(const float xCoordinate, const float yCoordinate) { //return o2::gpu::CAMath::ATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; - return utils::FastATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; + return o2::math_utils::fastATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; } GPUhdi() float math_utils::calculateRCoordinate(const float xCoordinate, const float yCoordinate) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/PrimaryVertexContext.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/PrimaryVertexContext.h index a91aef364af3c..112c3fe77d3dd 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/PrimaryVertexContext.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/PrimaryVertexContext.h @@ -24,6 +24,7 @@ #include "ITStracking/Configuration.h" #include "ITStracking/Constants.h" #include "ITStracking/Definitions.h" +#include "ITStracking/IndexTableUtils.h" #include "ITStracking/Road.h" #include "ITStracking/Tracklet.h" @@ -42,94 +43,55 @@ class PrimaryVertexContext PrimaryVertexContext(const PrimaryVertexContext&) = delete; PrimaryVertexContext& operator=(const PrimaryVertexContext&) = delete; - virtual void initialise(const MemoryParameters& memParam, const std::array<std::vector<Cluster>, constants::its::LayersNumber>& cl, - const std::array<float, 3>& pv, const int iteration); - const float3& getPrimaryVertex() const; - std::array<std::vector<Cluster>, constants::its::LayersNumber>& getClusters(); - std::array<std::vector<Cell>, constants::its::CellsPerRoad>& getCells(); - std::array<std::vector<int>, constants::its::CellsPerRoad - 1>& getCellsLookupTable(); - std::array<std::vector<std::vector<int>>, constants::its::CellsPerRoad - 1>& getCellsNeighbours(); - std::vector<Road>& getRoads(); + virtual void initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, + const std::vector<std::vector<Cluster>>& cl, const std::array<float, 3>& pv, const int iteration); + const float3& getPrimaryVertex() const { return mPrimaryVertex; } + auto& getClusters() { return mClusters; } + auto& getCells() { return mCells; } + auto& getCellsLookupTable() { return mCellsLookupTable; } + auto& getCellsNeighbours() { return mCellsNeighbours; } + auto& getRoads() { return mRoads; } - bool isClusterUsed(int layer, int clusterId) const; + float getMinR(int layer) { return mMinR[layer]; } + float getMaxR(int layer) { return mMaxR[layer]; } + + bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } void markUsedCluster(int layer, int clusterId); - std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, - constants::its::TrackletsPerRoad>& - getIndexTables(); - std::array<std::vector<Tracklet>, constants::its::TrackletsPerRoad>& getTracklets(); - std::array<std::vector<int>, constants::its::CellsPerRoad>& getTrackletsLookupTable(); + auto& getIndexTables() { return mIndexTables; } + auto& getTracklets() { return mTracklets; } + auto& getTrackletsLookupTable() { return mTrackletsLookupTable; } 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; + IndexTableUtils mIndexTableUtils; + protected: float3 mPrimaryVertex; - std::array<std::vector<Cluster>, constants::its::LayersNumber> mUnsortedClusters; - std::array<std::vector<Cluster>, constants::its::LayersNumber> mClusters; - std::array<std::vector<bool>, constants::its::LayersNumber> mUsedClusters; - std::array<std::vector<Cell>, constants::its::CellsPerRoad> mCells; - std::array<std::vector<int>, constants::its::CellsPerRoad - 1> mCellsLookupTable; - std::array<std::vector<std::vector<int>>, constants::its::CellsPerRoad - 1> mCellsNeighbours; + std::vector<float> mMinR; + std::vector<float> mMaxR; + std::vector<std::vector<Cluster>> mClusters; + std::vector<std::vector<bool>> mUsedClusters; + std::vector<std::vector<Cell>> mCells; + std::vector<std::vector<int>> mCellsLookupTable; + std::vector<std::vector<std::vector<int>>> mCellsNeighbours; std::vector<Road> mRoads; - std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, - constants::its::TrackletsPerRoad> - mIndexTables; - std::array<std::vector<Tracklet>, constants::its::TrackletsPerRoad> mTracklets; - std::array<std::vector<int>, constants::its::CellsPerRoad> mTrackletsLookupTable; + // std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, + // constants::its::TrackletsPerRoad> + std::vector<std::vector<int>> mIndexTables; + std::vector<std::vector<Tracklet>> mTracklets; + std::vector<std::vector<int>> mTrackletsLookupTable; std::vector<std::pair<unsigned long long, bool>> mRoadLabels; }; -inline const float3& PrimaryVertexContext::getPrimaryVertex() const { return mPrimaryVertex; } - -inline std::array<std::vector<Cluster>, constants::its::LayersNumber>& PrimaryVertexContext::getClusters() -{ - return mClusters; -} - -inline std::array<std::vector<Cell>, constants::its::CellsPerRoad>& PrimaryVertexContext::getCells() { return mCells; } - -inline std::array<std::vector<int>, constants::its::CellsPerRoad - 1>& PrimaryVertexContext::getCellsLookupTable() -{ - return mCellsLookupTable; -} - -inline std::array<std::vector<std::vector<int>>, constants::its::CellsPerRoad - 1>& - PrimaryVertexContext::getCellsNeighbours() -{ - return mCellsNeighbours; -} - -inline std::vector<Road>& PrimaryVertexContext::getRoads() { return mRoads; } - -inline bool PrimaryVertexContext::isClusterUsed(int layer, int clusterId) const -{ - return mUsedClusters[layer][clusterId]; -} inline void PrimaryVertexContext::markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } -inline std::array<std::array<int, constants::index_table::ZBins * constants::index_table::PhiBins + 1>, - constants::its::TrackletsPerRoad>& - PrimaryVertexContext::getIndexTables() -{ - return mIndexTables; -} - -inline std::array<std::vector<Tracklet>, constants::its::TrackletsPerRoad>& PrimaryVertexContext::getTracklets() -{ - return mTracklets; -} - -inline std::array<std::vector<int>, constants::its::CellsPerRoad>& PrimaryVertexContext::getTrackletsLookupTable() -{ - return mTrackletsLookupTable; -} - inline void PrimaryVertexContext::initialiseRoadLabels() { mRoadLabels.clear(); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h index 99c1578540e4d..45ba3e4498ce3 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h @@ -38,7 +38,7 @@ using Vertex = o2::dataformats::Vertex<o2::dataformats::TimeStamp<int>>; class ROframe final { public: - ROframe(int ROframeId); + ROframe(int ROframeId, int nLayers); int getROFrameId() const; const float3& getPrimaryVertex(const int) const; int getPrimaryVerticesNum() const; @@ -49,10 +49,10 @@ class ROframe final int getTotalClusters() const; bool empty() const; - const std::array<std::vector<Cluster>, constants::its::LayersNumber>& getClusters() const; + const auto& getClusters() const { return mClusters; } const std::vector<Cluster>& getClustersOnLayer(int layerId) const; const std::vector<TrackingFrameInfo>& getTrackingFrameInfoOnLayer(int layerId) const; - const std::array<std::vector<TrackingFrameInfo>, constants::its::LayersNumber>& getTrackingFrameInfo() const; + const auto& getTrackingFrameInfo() const { return mTrackingFrameInfo; } const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; const MCCompLabel& getClusterLabels(int layerId, const Cluster& cl) const; @@ -73,10 +73,10 @@ class ROframe final private: const int mROframeId; std::vector<float3> mPrimaryVertices; - std::array<std::vector<Cluster>, constants::its::LayersNumber> mClusters; - std::array<std::vector<TrackingFrameInfo>, constants::its::LayersNumber> mTrackingFrameInfo; - std::array<std::vector<MCCompLabel>, constants::its::LayersNumber> mClusterLabels; - std::array<std::vector<int>, constants::its::LayersNumber> mClusterExternalIndices; + std::vector<std::vector<Cluster>> mClusters; + std::vector<std::vector<TrackingFrameInfo>> mTrackingFrameInfo; + std::vector<std::vector<MCCompLabel>> mClusterLabels; + std::vector<std::vector<int>> mClusterExternalIndices; }; inline int ROframe::getROFrameId() const { return mROframeId; } @@ -87,11 +87,6 @@ inline int ROframe::getPrimaryVerticesNum() const { return mPrimaryVertices.size inline bool ROframe::empty() const { return getTotalClusters() == 0; } -inline const std::array<std::vector<Cluster>, constants::its::LayersNumber>& ROframe::getClusters() const -{ - return mClusters; -} - inline const std::vector<Cluster>& ROframe::getClustersOnLayer(int layerId) const { return mClusters[layerId]; @@ -102,11 +97,6 @@ inline const std::vector<TrackingFrameInfo>& ROframe::getTrackingFrameInfoOnLaye return mTrackingFrameInfo[layerId]; } -inline const std::array<std::vector<TrackingFrameInfo>, constants::its::LayersNumber>& ROframe::getTrackingFrameInfo() const -{ - return mTrackingFrameInfo; -} - inline const TrackingFrameInfo& ROframe::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const { return mTrackingFrameInfo[layerId][cl.clusterId]; @@ -157,7 +147,7 @@ inline void ROframe::addClusterExternalIndexToLayer(int layer, const int idx) inline void ROframe::clear() { - for (int iL = 0; iL < constants::its::LayersNumber; ++iL) { + for (unsigned int iL = 0; iL < mClusters.size(); ++iL) { mClusters[iL].clear(); mTrackingFrameInfo[iL].clear(); mClusterLabels[iL].clear(); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h index 51762c5261239..04946fad243e7 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h @@ -15,7 +15,7 @@ #ifndef TRACKINGITSU_INCLUDE_ROAD_H_ #define TRACKINGITSU_INCLUDE_ROAD_H_ -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <array> #endif @@ -43,8 +43,10 @@ class Road final void resetRoad(); void addCell(int, int); + static constexpr int mMaxRoadSize = 13; + private: - int mCellIds[constants::its::CellsPerRoad]; + int mCellIds[mMaxRoadSize]; int mRoadSize; int mLabel; bool mIsFakeRoad; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/StandaloneDebugger.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/StandaloneDebugger.h index 6693d13f93d90..2b360a706aa28 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/StandaloneDebugger.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/StandaloneDebugger.h @@ -15,11 +15,17 @@ #ifndef O2_ITS_STANDALONE_DEBUGGER_H_ #define O2_ITS_STANDALONE_DEBUGGER_H_ +#include <string> +#include <iterator> + +// Tracker +#include "DataFormatsITS/TrackITS.h" +#include "ITStracking/PrimaryVertexContext.h" +#include "ITStracking/ROframe.h" + namespace o2 { -class MCCompLabel; - namespace utils { class TreeStreamRedirector; @@ -30,6 +36,93 @@ namespace its class Tracklet; class Line; class ROframe; +class ClusterLines; + +using constants::its::UnusedIndex; + +template <int numClusters = TrackITSExt::MaxClusters> +struct FakeTrackInfo { + public: + FakeTrackInfo(); + FakeTrackInfo(PrimaryVertexContext* pvc, const ROframe& event, TrackITSExt& track, bool storeClusters) : isFake{false}, isAmbiguousId{false}, mainLabel{UnusedIndex, UnusedIndex, UnusedIndex, false} + { + occurrences.clear(); + for (auto& c : clusStatuses) { + c = -1; + } + for (size_t iCluster{0}; iCluster < numClusters; ++iCluster) { + int extIndex = track.getClusterIndex(iCluster); + if (extIndex == -1) { + continue; + } + o2::MCCompLabel mcLabel = event.getClusterLabels(iCluster, extIndex); + bool found = false; + + for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { + std::pair<o2::MCCompLabel, int>& occurrence = occurrences[iOcc]; + if (mcLabel == occurrence.first) { + ++occurrence.second; + found = true; + break; + } + } + if (!found) { + occurrences.emplace_back(mcLabel, 1); + } + } + + if (occurrences.size() > 1) { + isFake = true; + } + std::sort(std::begin(occurrences), std::end(occurrences), [](auto e1, auto e2) { + return e1.second > e2.second; + }); + mainLabel = occurrences[0].first; + + for (size_t iOcc{1}; iOcc < occurrences.size(); ++iOcc) { + if (occurrences[iOcc].second == occurrences[0].second) { + isAmbiguousId = true; + break; + } + } + + for (size_t iCluster{0}; iCluster < numClusters; ++iCluster) { + int extIndex = track.getClusterIndex(iCluster); + if (extIndex == -1) { + continue; + } + o2::MCCompLabel lbl = event.getClusterLabels(iCluster, extIndex); + if (lbl == mainLabel && occurrences[0].second > 1 && !lbl.isNoise()) { // if we have MaxClusters fake clusters -> occurrences[0].second = 1 + clusStatuses[iCluster] = 1; + } else { + clusStatuses[iCluster] = 0; + ++nFakeClusters; + } + } + if (storeClusters) { + for (auto iCluster{0}; iCluster < numClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index != constants::its::UnusedIndex) { + clusters[iCluster] = pvc->getClusters()[iCluster][index]; + trackingFrameInfos[iCluster] = event.getTrackingFrameInfoOnLayer(iCluster).at(index); + } + } + } + } + + // Data + std::vector<std::pair<MCCompLabel, int>> + occurrences; + MCCompLabel mainLabel; + std::array<int, numClusters> clusStatuses; + std::array<o2::its::Cluster, numClusters> clusters; + std::array<o2::its::TrackingFrameInfo, numClusters> trackingFrameInfos; + + bool isFake; + bool isAmbiguousId; + int nFakeClusters = 0; + ClassDefNV(FakeTrackInfo, 1); +}; // namespace its class StandaloneDebugger { @@ -59,6 +152,9 @@ class StandaloneDebugger void fillXYZHistogramTree(std::array<std::vector<int>, 3>, const std::array<int, 3>); void fillVerticesInfoTree(float x, float y, float z, int size, int rId, int eId, float pur); + // Tracker debug utilities + void dumpTrackToBranchWithInfo(std::string branchName, o2::its::TrackITSExt track, const ROframe event, PrimaryVertexContext* pvc, const bool dumpClusters = false); + static int getBinIndex(const float, const int, const float, const float); private: diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 4c8e35914f5eb..76c5acd946066 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -25,6 +25,8 @@ #include <utility> #include "ITStracking/Configuration.h" +#include "DetectorsBase/MatLayerCylSet.h" +#include "CommonConstants/MathConstants.h" #include "ITStracking/Definitions.h" #include "ITStracking/ROframe.h" #include "ITStracking/MathUtils.h" @@ -34,6 +36,12 @@ #include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCCompLabel.h" +#include "Framework/Logger.h" + +#ifdef CA_DEBUG +#include "ITStracking/StandaloneDebugger.h" +#endif + namespace o2 { namespace gpu @@ -60,13 +68,16 @@ class Tracker float getBz() const; std::vector<TrackITSExt>& getTracks(); - dataformats::MCTruthContainer<MCCompLabel>& getTrackLabels(); + auto& getTrackLabels() { return mTrackLabels; } + bool isMatLUT(); void clustersToTracks(const ROframe&, std::ostream& = std::cout); void setROFrame(std::uint32_t f) { mROFrame = f; } std::uint32_t getROFrame() const { return mROFrame; } void setParameters(const std::vector<MemoryParameters>&, const std::vector<TrackingParameters>&); + void initMatBudLUTFromFile(); + void getGlobalConfiguration(); private: track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const Cluster& cluster3, @@ -78,7 +89,8 @@ class Tracker void findCellsNeighbours(int& iteration); void findRoads(int& iteration); void findTracks(const ROframe& ev); - bool fitTrack(const ROframe& event, TrackITSExt& track, int start, int end, int step); + bool fitTrack(const ROframe& event, TrackITSExt& track, int start, int end, int step, + const float chi2cut = o2::constants::math::VeryBig); void traverseCellsTree(const int, const int); void computeRoadsMClabels(const ROframe&); void computeTracksMClabels(const ROframe&); @@ -97,8 +109,13 @@ class Tracker float mBz = 5.f; std::uint32_t mROFrame = 0; std::vector<TrackITSExt> mTracks; - dataformats::MCTruthContainer<MCCompLabel> mTrackLabels; + std::vector<MCCompLabel> mTrackLabels; + o2::base::MatLayerCylSet* mMatLayerCylSet = nullptr; o2::gpu::GPUChainITS* mRecoChain = nullptr; + +#ifdef CA_DEBUG + StandaloneDebugger* mDebugger; +#endif }; inline void Tracker::setParameters(const std::vector<MemoryParameters>& memPars, const std::vector<TrackingParameters>& trkPars) @@ -117,6 +134,16 @@ inline void Tracker::setBz(float bz) mBz = bz; } +inline void Tracker::initMatBudLUTFromFile() +{ + mMatLayerCylSet = o2::base::MatLayerCylSet::loadFromFile(); +} + +inline bool Tracker::isMatLUT() +{ + return mMatLayerCylSet; +} + template <typename... T> void Tracker::initialisePrimaryVertexContext(T&&... args) { @@ -128,11 +155,6 @@ inline std::vector<TrackITSExt>& Tracker::getTracks() return mTracks; } -inline dataformats::MCTruthContainer<MCCompLabel>& Tracker::getTrackLabels() -{ - return mTrackLabels; -} - template <typename... T> float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, std::ostream& ostream, T&&... args) @@ -147,10 +169,12 @@ float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, s std::chrono::duration<double, std::milli> diff_t{end - start}; diff = diff_t.count(); - if (taskName == nullptr) { - ostream << diff << "\t"; - } else { - ostream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms" << std::endl; + if (fair::Logger::Logging(fair::Severity::info)) { + if (taskName == nullptr) { + ostream << diff << "\t"; + } else { + ostream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms" << std::endl; + } } } else { (this->*task)(std::forward<T>(args)...); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 9a33762586cf1..249d2ea838d36 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -41,7 +41,7 @@ namespace its { class TrackITSExt; -typedef std::function<int(o2::gpu::GPUChainITS&, std::vector<Road>& roads, std::array<const Cluster*, 7>, std::array<const Cell*, 5>, const std::array<std::vector<TrackingFrameInfo>, 7>&, std::vector<TrackITSExt>&)> FuncRunITSTrackFit_t; +typedef std::function<int(o2::gpu::GPUChainITS&, std::vector<Road>& roads, std::vector<const Cluster*>&, std::vector<const Cell*>&, const std::vector<std::vector<TrackingFrameInfo>>&, std::vector<TrackITSExt>&)> FuncRunITSTrackFit_t; class TrackerTraits { @@ -49,7 +49,7 @@ class TrackerTraits virtual ~TrackerTraits() = default; GPU_HOST_DEVICE static constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - GPU_DEVICE static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); + GPU_DEVICE const int4 getBinsRect(const Cluster&, const int, const float, const float, float maxdeltaz, float maxdeltaphi); void SetRecoChain(o2::gpu::GPUChainITS* chain, FuncRunITSTrackFit_t&& funcRunITSTrackFit) { @@ -59,7 +59,7 @@ class TrackerTraits virtual void computeLayerTracklets(){}; virtual void computeLayerCells(){}; - virtual void refitTracks(const std::array<std::vector<TrackingFrameInfo>, 7>&, std::vector<TrackITSExt>&){}; + virtual void refitTracks(const std::vector<std::vector<TrackingFrameInfo>>&, std::vector<TrackITSExt>&){}; void UpdateTrackingParameters(const TrackingParameters& trkPar); PrimaryVertexContext* getPrimaryVertexContext() { return mPrimaryVertexContext; } @@ -78,23 +78,24 @@ inline void TrackerTraits::UpdateTrackingParameters(const TrackingParameters& tr } inline GPU_DEVICE const int4 TrackerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float directionZIntersection, float maxdeltaz, float maxdeltaphi) + const float z1, const float z2, float maxdeltaz, float maxdeltaphi) { - const float zRangeMin = directionZIntersection - 2 * maxdeltaz; + const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; const float phiRangeMin = currentCluster.phiCoordinate - maxdeltaphi; - const float zRangeMax = directionZIntersection + 2 * maxdeltaz; + const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; const float phiRangeMax = currentCluster.phiCoordinate + maxdeltaphi; - if (zRangeMax < -constants::its::LayersZCoordinate()[layerIndex + 1] || - zRangeMin > constants::its::LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + if (zRangeMax < -mTrkParams.LayerZ[layerIndex + 1] || + zRangeMin > mTrkParams.LayerZ[layerIndex + 1] || zRangeMin > zRangeMax) { return getEmptyBinsRect(); } - return int4{gpu::GPUCommonMath::Max(0, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMin)), - index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMin)), - gpu::GPUCommonMath::Min(constants::index_table::ZBins - 1, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMax)), - index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMax))}; + const IndexTableUtils& utils{mPrimaryVertexContext->mIndexTableUtils}; + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), + utils.getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMin)), + o2::gpu::GPUCommonMath::Min(mTrkParams.ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), + utils.getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMax))}; } } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraitsCPU.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraitsCPU.h index 865ae121abe04..a43fc44b28ef1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraitsCPU.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraitsCPU.h @@ -44,7 +44,7 @@ class TrackerTraitsCPU : public TrackerTraits void computeLayerCells() final; void computeLayerTracklets() final; - void refitTracks(const std::array<std::vector<TrackingFrameInfo>, 7>& tf, std::vector<TrackITSExt>& tracks) final; + void refitTracks(const std::vector<std::vector<TrackingFrameInfo>>& tf, std::vector<TrackITSExt>& tracks) final; protected: std::vector<std::vector<Tracklet>> mTracklets; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index eec5b41210e2b..deed8732f6ebd 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -19,7 +19,6 @@ namespace o2 namespace its { -class VertexingParameters; struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper<VertexerParamConfig> { // geometrical cuts @@ -36,7 +35,14 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper<VertexerPa O2ParamDef(VertexerParamConfig, "ITSVertexerParam"); }; -// VertexerParamConfig VertexerParamConfig::sInstance; +struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper<TrackerParamConfig> { + + // Use lookup table for mat. budget + bool useMatBudLUT = false; + + O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); +}; + } // namespace its } // namespace o2 #endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h index e2b39b99efbe3..23f2be272ec5c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h @@ -51,8 +51,8 @@ GPUdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int second secondClusterIndex{secondClusterOrderingIndex}, tanLambda{(firstCluster.zCoordinate - secondCluster.zCoordinate) / (firstCluster.rCoordinate - secondCluster.rCoordinate)}, - phiCoordinate{gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, - firstCluster.xCoordinate - secondCluster.xCoordinate)} + phiCoordinate{o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, + firstCluster.xCoordinate - secondCluster.xCoordinate)} { // Nothing to do } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index ba73fb6ca00c6..88984c94daf32 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -31,6 +31,8 @@ #include "ITStracking/Tracklet.h" #include "ITStracking/Cluster.h" +#include "Framework/Logger.h" + class TTree; namespace o2 @@ -137,8 +139,10 @@ inline std::vector<Vertex> Vertexer::exportVertices() { std::vector<Vertex> vertices; for (auto& vertex : mTraits->getVertices()) { - std::cout << "\t\tFound vertex with: " << std::setw(6) << vertex.mContributors << " contributors" << std::endl; - vertices.emplace_back(Point3D<float>(vertex.mX, vertex.mY, vertex.mZ), vertex.mRMS2, vertex.mContributors, vertex.mAvgDistance2); + if (fair::Logger::Logging(fair::Severity::info)) { + std::cout << "\t\tFound vertex with: " << std::setw(6) << vertex.mContributors << " contributors" << std::endl; + } + vertices.emplace_back(o2::math_utils::Point3D<float>(vertex.mX, vertex.mY, vertex.mZ), vertex.mRMS2, vertex.mContributors, vertex.mAvgDistance2); vertices.back().setTimeStamp(vertex.mTimeStamp); } return vertices; @@ -158,10 +162,12 @@ float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, std::chrono::duration<double, std::milli> diff_t{end - start}; diff = diff_t.count(); - if (taskName == nullptr) { - ostream << diff << "\t"; - } else { - ostream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms" << std::endl; + if (fair::Logger::Logging(fair::Severity::info)) { + if (taskName == nullptr) { + ostream << diff << "\t"; + } else { + ostream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms" << std::endl; + } } } else { (this->*task)(std::forward<T>(args)...); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index be341b69a39a3..ff657120d0b70 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -23,6 +23,7 @@ #include "ITStracking/Configuration.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Definitions.h" +#include "ITStracking/IndexTableUtils.h" #ifdef _ALLOW_DEBUG_TREES_ITS_ #include "ITStracking/StandaloneDebugger.h" #endif @@ -45,8 +46,6 @@ namespace its class ROframe; -using constants::index_table::PhiBins; -using constants::index_table::ZBins; using constants::its::LayersNumberVertexer; struct lightVertex { @@ -106,8 +105,11 @@ class VertexerTraits { return int4{0, 0, 0, 0}; } - GPUhd() static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); - GPUhd() static const int2 getPhiBins(float phi, float deltaPhi); + 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 reset(); @@ -128,8 +130,9 @@ class VertexerTraits void updateVertexingParameters(const VertexingParameters& vrtPar); VertexingParameters getVertexingParameters() const { return mVrtParams; } - static const std::vector<std::pair<int, int>> selectClusters(const std::array<int, ZBins * PhiBins + 1>& indexTable, - const std::array<int, 4>& selectedBinsRect); + static const std::vector<std::pair<int, int>> selectClusters(const int* indexTable, + const std::array<int, 4>& selectedBinsRect, + const IndexTableUtils& utils); std::vector<lightVertex> getVertices() const { return mVertices; } // utils @@ -160,7 +163,8 @@ class VertexerTraits #endif VertexingParameters mVrtParams; - std::array<std::array<int, ZBins * PhiBins + 1>, LayersNumberVertexer> mIndexTables; + IndexTableUtils mIndexTableUtils; + std::array<std::vector<int>, LayersNumberVertexer> mIndexTables; std::vector<lightVertex> mVertices; // Frame related quantities @@ -177,6 +181,9 @@ class VertexerTraits inline void VertexerTraits::initialise(ROframe* event) { reset(); + if (!mIndexTableUtils.getNzBins()) { + updateVertexingParameters(mVrtParams); + } arrangeClusters(event); setIsGPU(false); } @@ -189,32 +196,51 @@ inline void VertexerTraits::setIsGPU(const unsigned char isgpu) inline void VertexerTraits::updateVertexingParameters(const VertexingParameters& vrtPar) { mVrtParams = vrtPar; + mIndexTableUtils.setTrackingParameters(vrtPar); + mVrtParams.phiSpan = static_cast<int>(std::ceil(mIndexTableUtils.getNphiBins() * mVrtParams.phiCut / + constants::math::TwoPi)); + mVrtParams.zSpan = static_cast<int>(std::ceil(mVrtParams.zCut * mIndexTableUtils.getInverseZCoordinate(0))); + for (auto& table : mIndexTables) { + table.resize(mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1, 0); + } } GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi) { - return int2{index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phi - dPhi)), - index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phi + 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::getNormalizedPhiCoordinate(phi - dPhi)), + utils.getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phi + dPhi))}; } GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float directionZIntersection, float maxdeltaz, float maxdeltaphi) + const float directionZIntersection, float maxdeltaz, float maxdeltaphi, + const IndexTableUtils& utils) { const float zRangeMin = directionZIntersection - 2 * maxdeltaz; const float phiRangeMin = currentCluster.phiCoordinate - maxdeltaphi; const float zRangeMax = directionZIntersection + 2 * maxdeltaz; const float phiRangeMax = currentCluster.phiCoordinate + maxdeltaphi; - if (zRangeMax < -constants::its::LayersZCoordinate()[layerIndex + 1] || - zRangeMin > constants::its::LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + if (zRangeMax < -utils.getLayerZ(layerIndex + 1) || + zRangeMin > utils.getLayerZ(layerIndex + 1) || zRangeMin > zRangeMax) { return getEmptyBinsRect(); } - return int4{gpu::GPUCommonMath::Max(0, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMin)), - index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMin)), - gpu::GPUCommonMath::Min(constants::index_table::ZBins - 1, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMax)), - index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMax))}; + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), + utils.getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMin)), + o2::gpu::GPUCommonMath::Min(utils.getNzBins() - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), + utils.getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(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); } // debug diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h index 1a7efb0394021..f6bf18f6e04e5 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h @@ -6293,10 +6293,11 @@ class binary_writer } case value_t::boolean: { - if (add_prefix) + if (add_prefix) { oa->write_character(j.m_value.boolean ? static_cast<CharType>('T') : static_cast<CharType>('F')); + } break; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx b/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx index ed96496fcfb6e..7f6b4e49d2c79 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx @@ -13,9 +13,8 @@ /// #include "ITStracking/Cluster.h" - -#include "ITStracking/IndexTableUtils.h" #include "ITStracking/MathUtils.h" +#include "ITStracking/IndexTableUtils.h" namespace o2 { @@ -38,21 +37,21 @@ Cluster::Cluster(const float x, const float y, const float z, const int index) // Nothing to do } -Cluster::Cluster(const int layerIndex, const Cluster& other) +Cluster::Cluster(const int layerIndex, const IndexTableUtils& utils, const Cluster& other) : xCoordinate{other.xCoordinate}, yCoordinate{other.yCoordinate}, zCoordinate{other.zCoordinate}, phiCoordinate{getNormalizedPhiCoordinate(calculatePhiCoordinate(other.xCoordinate, other.yCoordinate))}, rCoordinate{calculateRCoordinate(other.xCoordinate, other.yCoordinate)}, clusterId{other.clusterId}, - indexTableBinIndex{index_table_utils::getBinIndex(index_table_utils::getZBinIndex(layerIndex, zCoordinate), - index_table_utils::getPhiBinIndex(phiCoordinate))} + indexTableBinIndex{utils.getBinIndex(utils.getZBinIndex(layerIndex, zCoordinate), + utils.getPhiBinIndex(phiCoordinate))} //, montecarloId{ other.montecarloId } { // Nothing to do } -Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const Cluster& other) +Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const IndexTableUtils& utils, const Cluster& other) : xCoordinate{other.xCoordinate}, yCoordinate{other.yCoordinate}, zCoordinate{other.zCoordinate}, @@ -60,13 +59,13 @@ Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const Cluste calculatePhiCoordinate(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y))}, rCoordinate{calculateRCoordinate(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y)}, clusterId{other.clusterId}, - indexTableBinIndex{index_table_utils::getBinIndex(index_table_utils::getZBinIndex(layerIndex, zCoordinate), - index_table_utils::getPhiBinIndex(phiCoordinate))} + indexTableBinIndex{utils.getBinIndex(utils.getZBinIndex(layerIndex, zCoordinate), + utils.getPhiBinIndex(phiCoordinate))} { // Nothing to do } -void Cluster::Init(const int layerIndex, const float3& primaryVertex, const Cluster& other) +void Cluster::Init(const int layerIndex, const float3& primaryVertex, const IndexTableUtils& utils, const Cluster& other) { xCoordinate = other.xCoordinate; yCoordinate = other.yCoordinate; @@ -75,8 +74,8 @@ void Cluster::Init(const int layerIndex, const float3& primaryVertex, const Clus calculatePhiCoordinate(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y)); rCoordinate = calculateRCoordinate(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y); clusterId = other.clusterId; - indexTableBinIndex = index_table_utils::getBinIndex(index_table_utils::getZBinIndex(layerIndex, zCoordinate), - index_table_utils::getPhiBinIndex(phiCoordinate)); + indexTableBinIndex = utils.getBinIndex(utils.getZBinIndex(layerIndex, zCoordinate), + utils.getPhiBinIndex(phiCoordinate)); } TrackingFrameInfo::TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, GPUArray<float, 2>&& posTF, diff --git a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx index 8ff4b4635b9ba..d152499f74f1b 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx @@ -27,8 +27,9 @@ Line::Line(std::array<float, 3> firstPoint, std::array<float, 3> secondPoint) float inverseNorm{1.f / std::sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + cosinesDirector[2] * cosinesDirector[2])}; - for (int index{0}; index < 3; ++index) + for (int index{0}; index < 3; ++index) { cosinesDirector[index] *= inverseNorm; + } } bool Line::areParallel(const Line& firstLine, const Line& secondLine, const float precision) @@ -37,22 +38,25 @@ bool Line::areParallel(const Line& firstLine, const Line& secondLine, const floa firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]}; float module{std::abs(firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2]) + std::abs(firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1])}; - if (std::abs(crossProdX) > precision * module) + if (std::abs(crossProdX) > precision * module) { return false; + } float crossProdY{-firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2] + firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]}; module = std::abs(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2]) + std::abs(firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]); - if (std::abs(crossProdY) > precision * module) + if (std::abs(crossProdY) > precision * module) { return false; + } float crossProdZ = firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1] - firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]; module = std::abs(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1]) + std::abs(firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]); - if (std::abs(crossProdZ) > precision * module) + if (std::abs(crossProdZ) > precision * module) { return false; + } return true; } @@ -61,8 +65,9 @@ std::array<float, 6> Line::getDCAComponents(const Line& line, const std::array<f { std::array<float, 6> components{0., 0., 0., 0., 0., 0.}; float cdelta{0.}; - for (int i{0}; i < 3; ++i) + for (int i{0}; i < 3; ++i) { cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); + } components[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; components[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; @@ -84,8 +89,9 @@ ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const in std::array<float, 3> covarianceFirst{1., 1., 1.}; std::array<float, 3> covarianceSecond{1., 1., 1.}; - for (int i{0}; i < 6; ++i) + for (int i{0}; i < 6; ++i) { mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; + } float determinantFirst = firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + @@ -165,8 +171,8 @@ ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const in computeClusterCentroid(); // RMS2 - mRMS2 = std::move(Line::getDCAComponents(firstLine, mVertex)); - const std::array<float, 6> tmpRMS2Line2 = std::move(Line::getDCAComponents(secondLine, mVertex)); + mRMS2 = Line::getDCAComponents(firstLine, mVertex); + const std::array<float, 6> tmpRMS2Line2 = Line::getDCAComponents(secondLine, mVertex); std::transform(mRMS2.begin(), mRMS2.end(), tmpRMS2Line2.begin(), mRMS2.begin(), [&](const float a, const float b) { return a + (b - a) / mLabels.size(); }); // AvgDistance2 @@ -187,8 +193,9 @@ ClusterLines::ClusterLines(const Line& firstLine, const Line& secondLine) std::array<float, 3> covarianceFirst{1., 1., 1.}; std::array<float, 3> covarianceSecond{1., 1., 1.}; - for (int i{0}; i < 6; ++i) + for (int i{0}; i < 6; ++i) { mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; + } float determinantFirst = firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + @@ -282,8 +289,9 @@ void ClusterLines::add(const int& lineLabel, const Line& line, const bool& weigh #endif std::array<float, 3> covariance{1., 1., 1.}; - for (int i{0}; i < 6; ++i) + for (int i{0}; i < 6; ++i) { mWeightMatrix[i] += line.weightMatrix[i]; + } // if(weight) line->GetSigma2P0(covariance); float determinant{line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[0] * covariance[1] + diff --git a/Detectors/ITSMFT/ITS/tracking/src/DBScan.cxx b/Detectors/ITSMFT/ITS/tracking/src/DBScan.cxx deleted file mode 100644 index 52795b0287246..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/DBScan.cxx +++ /dev/null @@ -1,41 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 DBScan.cxx -/// \brief -/// - -#include "ITStracking/DBScan.h" -#include "ITStracking/Definitions.h" -#include "GPUCommonMath.h" - -namespace o2 -{ -namespace its -{ - -Centroid::Centroid(int* indices, float* position) -{ - for (int i{0}; i < 2; ++i) { - mIndices[i] = indices[i]; - } - for (int i{0}; i < 3; ++i) { - mPosition[i] = position[i]; - } -} - -float Centroid::ComputeDistance(const Centroid& c1, const Centroid& c2) -{ - return gpu::GPUCommonMath::Sqrt((c1.mPosition[0] - c2.mPosition[0]) * (c1.mPosition[0] - c2.mPosition[0]) + - (c1.mPosition[1] - c2.mPosition[1]) * (c1.mPosition[1] - c2.mPosition[1]) + - (c1.mPosition[2] - c2.mPosition[2]) * (c1.mPosition[2] - c2.mPosition[2])); -} -} // namespace its -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/src/Graph.cxx b/Detectors/ITSMFT/ITS/tracking/src/Graph.cxx deleted file mode 100644 index a43b8c036056e..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/Graph.cxx +++ /dev/null @@ -1,33 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Graph.cxx -/// \brief -/// - -#include "ITStracking/Graph.h" - -namespace o2 -{ -namespace its -{ - -void Barrier::Wait() -{ - std::unique_lock<std::mutex> lock(mutex); - if (--count == 0) { - condition.notify_all(); - } else { - condition.wait(lock, [this] { return count == 0; }); - } -} - -} // namespace its -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx index 174100f3166c4..e21d75cf2adb2 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/IOUtils.cxx @@ -38,9 +38,6 @@ constexpr int PrimaryVertexLayerId{-1}; constexpr int EventLabelsSeparator{-1}; } // namespace -using o2::its::constants::its::LayersRCoordinate; -using o2::its::constants::its::LayersZCoordinate; - namespace o2 { namespace its @@ -60,7 +57,7 @@ void ioutils::convertCompactClusters(gsl::span<const itsmft::CompClusterExt> clu GeometryTGeo* geom = GeometryTGeo::Instance(); for (auto& c : clusters) { auto pattID = c.getPatternID(); - Point3D<float> locXYZ; + o2::math_utils::Point3D<float> locXYZ; float sigmaY2 = ioutils::DefClusError2Row, sigmaZ2 = ioutils::DefClusError2Col, sigmaYZ = 0; //Dummy COG errors (about half pixel size) if (pattID != itsmft::CompCluster::InvalidPatternID) { sigmaY2 = dict.getErr2X(pattID); @@ -73,25 +70,13 @@ void ioutils::convertCompactClusters(gsl::span<const itsmft::CompClusterExt> clu } } else { o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict.getClusterCoordinates(c, patt); + locXYZ = dict.getClusterCoordinates(c, patt, false); } auto& cl3d = output.emplace_back(c.getSensorID(), geom->getMatrixT2L(c.getSensorID()) ^ locXYZ); // local --> tracking cl3d.setErrors(sigmaY2, sigmaZ2, sigmaYZ); } } -void ioutils::loadConfigurations(const std::string& fileName) -{ - if (!fileName.empty()) { - std::ifstream inputStream; - inputStream.open(fileName); - nlohmann::json j; - inputStream >> j; - static_cast<TrackingParameters&>(Configuration<TrackingParameters>::getInstance()) = j.at("TrackingParameters").get<TrackingParameters>(); - //static_cast<IndexTableParameters&>(Configuration<IndexTableParameters>::getInstance()) = j.at("IndexTableParameters").get<IndexTableParameters>(); - } -} - std::vector<ROframe> ioutils::loadEventData(const std::string& fileName) { std::vector<ROframe> events{}; @@ -114,7 +99,7 @@ std::vector<ROframe> ioutils::loadEventData(const std::string& fileName) if (layerId == PrimaryVertexLayerId) { if (clusterId != 0) { - events.emplace_back(events.size()); + events.emplace_back(events.size(), 7); } events.back().addPrimaryVertex(xCoordinate, yCoordinate, zCoordinate); @@ -152,14 +137,14 @@ void ioutils::loadEventData(ROframe& event, gsl::span<const itsmft::CompClusterE } event.clear(); GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(utils::bit2Mask(TransformType::T2L, TransformType::L2G)); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); int clusterId{0}; for (auto& c : clusters) { int layer = geom->getLayer(c.getSensorID()); auto pattID = c.getPatternID(); - Point3D<float> locXYZ; + o2::math_utils::Point3D<float> locXYZ; float sigmaY2 = ioutils::DefClusError2Row, sigmaZ2 = ioutils::DefClusError2Col, sigmaYZ = 0; //Dummy COG errors (about half pixel size) if (pattID != itsmft::CompCluster::InvalidPatternID) { sigmaY2 = dict.getErr2X(pattID); @@ -172,7 +157,7 @@ void ioutils::loadEventData(ROframe& event, gsl::span<const itsmft::CompClusterE } } else { o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict.getClusterCoordinates(c, patt); + locXYZ = dict.getClusterCoordinates(c, patt, false); } auto sensorID = c.getSensorID(); // Inverse transformation to the local --> tracking @@ -199,7 +184,7 @@ int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, g { event.clear(); GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(utils::bit2Mask(TransformType::T2L, TransformType::L2G)); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); int clusterId{0}; auto first = rof.getFirstEntry(); @@ -208,7 +193,7 @@ int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, g int layer = geom->getLayer(c.getSensorID()); auto pattID = c.getPatternID(); - Point3D<float> locXYZ; + o2::math_utils::Point3D<float> locXYZ; float sigmaY2 = ioutils::DefClusError2Row, sigmaZ2 = ioutils::DefClusError2Col, sigmaYZ = 0; //Dummy COG errors (about half pixel size) if (pattID != itsmft::CompCluster::InvalidPatternID) { sigmaY2 = dict.getErr2X(pattID); @@ -221,7 +206,7 @@ int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, g } } else { o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict.getClusterCoordinates(c, patt); + locXYZ = dict.getClusterCoordinates(c, patt, false); } auto sensorID = c.getSensorID(); // Inverse transformation to the local --> tracking @@ -244,33 +229,33 @@ int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, g return clusters_in_frame.size(); } -void ioutils::generateSimpleData(ROframe& event, const int phiDivs, const int zDivs = 1) -{ - const float angleOffset = constants::math::TwoPi / static_cast<float>(phiDivs); - // Maximum z allowed on innermost layer should be: ~9,75 - const float zOffsetFirstLayer = (zDivs == 1) ? 0 : 1.5 * (LayersZCoordinate()[6] * LayersRCoordinate()[0]) / (LayersRCoordinate()[6] * (static_cast<float>(zDivs) - 1)); - std::vector<float> x, y; - std::array<std::vector<float>, 7> z; - for (size_t j{0}; j < zDivs; ++j) { - for (size_t i{0}; i < phiDivs; ++i) { - x.emplace_back(cos(i * angleOffset + 0.001)); // put an epsilon to move from periods (e.g. 20 clusters vs 20 cells) - y.emplace_back(sin(i * angleOffset + 0.001)); - const float zFirstLayer{-static_cast<float>((zDivs - 1.) / 2.) * zOffsetFirstLayer + zOffsetFirstLayer * static_cast<float>(j)}; - z[0].emplace_back(zFirstLayer); - for (size_t iLayer{1}; iLayer < constants::its::LayersNumber; ++iLayer) { - z[iLayer].emplace_back(zFirstLayer * LayersRCoordinate()[iLayer] / LayersRCoordinate()[0]); - } - } - } - - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { - for (int i = 0; i < phiDivs * zDivs; i++) { - o2::MCCompLabel label{i, 0, 0, false}; - event.addClusterLabelToLayer(iLayer, label); //last argument : label, goes into mClustersLabel - event.addClusterToLayer(iLayer, LayersRCoordinate()[iLayer] * x[i], LayersRCoordinate()[iLayer] * y[i], z[iLayer][i], i); //uses 1st constructor for clusters - } - } -} +// void ioutils::generateSimpleData(ROframe& event, const int phiDivs, const int zDivs = 1) +// { +// const float angleOffset = constants::math::TwoPi / static_cast<float>(phiDivs); +// // Maximum z allowed on innermost layer should be: ~9,75 +// const float zOffsetFirstLayer = (zDivs == 1) ? 0 : 1.5 * (LayersZCoordinate()[6] * LayersRCoordinate()[0]) / (LayersRCoordinate()[6] * (static_cast<float>(zDivs) - 1)); +// std::vector<float> x, y; +// std::array<std::vector<float>, 7> z; +// for (size_t j{0}; j < zDivs; ++j) { +// for (size_t i{0}; i < phiDivs; ++i) { +// x.emplace_back(cos(i * angleOffset + 0.001)); // put an epsilon to move from periods (e.g. 20 clusters vs 20 cells) +// y.emplace_back(sin(i * angleOffset + 0.001)); +// const float zFirstLayer{-static_cast<float>((zDivs - 1.) / 2.) * zOffsetFirstLayer + zOffsetFirstLayer * static_cast<float>(j)}; +// z[0].emplace_back(zFirstLayer); +// for (size_t iLayer{1}; iLayer < 7; ++iLayer) { +// z[iLayer].emplace_back(zFirstLayer * LayersRCoordinate()[iLayer] / LayersRCoordinate()[0]); +// } +// } +// } + +// for (int iLayer{0}; iLayer < 7; ++iLayer) { +// for (int i = 0; i < phiDivs * zDivs; i++) { +// o2::MCCompLabel label{i, 0, 0, false}; +// event.addClusterLabelToLayer(iLayer, label); //last argument : label, goes into mClustersLabel +// event.addClusterToLayer(iLayer, LayersRCoordinate()[iLayer] * x[i], LayersRCoordinate()[iLayer] * y[i], z[iLayer][i], i); //uses 1st constructor for clusters +// } +// } +// } std::vector<std::unordered_map<int, Label>> ioutils::loadLabels(const int eventsNum, const std::string& fileName) { @@ -364,70 +349,7 @@ void ioutils::writeRoadsReport(std::ofstream& correctRoadsOutputStream, std::ofs } } -void to_json(nlohmann::json& j, const TrackingParameters& par) -{ - std::array<float, constants::its::TrackletsPerRoad> tmpTrackletMaxDeltaZ; - std::copy(par.TrackletMaxDeltaZ, par.TrackletMaxDeltaZ + tmpTrackletMaxDeltaZ.size(), tmpTrackletMaxDeltaZ.begin()); - std::array<float, constants::its::CellsPerRoad> tmpCellMaxDCA; - std::copy(par.CellMaxDCA, par.CellMaxDCA + tmpCellMaxDCA.size(), tmpCellMaxDCA.begin()); - std::array<float, constants::its::CellsPerRoad> tmpCellMaxDeltaZ; - std::copy(par.CellMaxDeltaZ, par.CellMaxDeltaZ + tmpCellMaxDeltaZ.size(), tmpCellMaxDeltaZ.begin()); - std::array<float, constants::its::CellsPerRoad - 1> tmpNeighbourMaxDeltaCurvature; - std::copy(par.NeighbourMaxDeltaCurvature, par.NeighbourMaxDeltaCurvature + tmpNeighbourMaxDeltaCurvature.size(), tmpNeighbourMaxDeltaCurvature.begin()); - std::array<float, constants::its::CellsPerRoad - 1> tmpNeighbourMaxDeltaN; - std::copy(par.NeighbourMaxDeltaN, par.NeighbourMaxDeltaN + tmpNeighbourMaxDeltaN.size(), tmpNeighbourMaxDeltaN.begin()); - j = nlohmann::json{ - {"ClusterSharing", par.ClusterSharing}, - {"MinTrackLength", par.MinTrackLength}, - {"TrackletMaxDeltaPhi", par.TrackletMaxDeltaPhi}, - {"TrackletMaxDeltaZ", tmpTrackletMaxDeltaZ}, - {"CellMaxDeltaTanLambda", par.CellMaxDeltaTanLambda}, - {"CellMaxDCA", tmpCellMaxDCA}, - {"CellMaxDeltaPhi", par.CellMaxDeltaPhi}, - {"CellMaxDeltaZ", tmpCellMaxDeltaZ}, - {"NeighbourMaxDeltaCurvature", tmpNeighbourMaxDeltaCurvature}, - {"NeighbourMaxDeltaN", tmpNeighbourMaxDeltaN}}; -} - -void from_json(const nlohmann::json& j, TrackingParameters& par) -{ - par.ClusterSharing = j.at("ClusterSharing").get<int>(); - par.MinTrackLength = j.at("MinTrackLength").get<int>(); - par.TrackletMaxDeltaPhi = j.at("TrackletMaxDeltaPhi").get<float>(); - par.CellMaxDeltaTanLambda = j.at("CellMaxDeltaTanLambda").get<float>(); - par.CellMaxDeltaPhi = j.at("CellMaxDeltaPhi").get<float>(); - auto tmpTrackletMaxDeltaZ = j.at("TrackletMaxDeltaZ").get<std::array<float, constants::its::TrackletsPerRoad>>(); - std::copy(tmpTrackletMaxDeltaZ.begin(), tmpTrackletMaxDeltaZ.end(), par.TrackletMaxDeltaZ); - auto tmpCellMaxDCA = j.at("CellMaxDCA").get<std::array<float, constants::its::CellsPerRoad>>(); - std::copy(tmpCellMaxDCA.begin(), tmpCellMaxDCA.end(), par.CellMaxDCA); - auto tmpCellMaxDeltaZ = j.at("CellMaxDeltaZ").get<std::array<float, constants::its::CellsPerRoad>>(); - std::copy(tmpCellMaxDCA.begin(), tmpCellMaxDeltaZ.end(), par.CellMaxDeltaZ); - auto tmpNeighbourMaxDeltaCurvature = j.at("NeighbourMaxDeltaCurvature").get<std::array<float, constants::its::CellsPerRoad - 1>>(); - std::copy(tmpNeighbourMaxDeltaCurvature.begin(), tmpNeighbourMaxDeltaCurvature.end(), par.NeighbourMaxDeltaCurvature); - auto tmpNeighbourMaxDeltaN = j.at("NeighbourMaxDeltaN").get<std::array<float, constants::its::CellsPerRoad - 1>>(); - std::copy(tmpNeighbourMaxDeltaN.begin(), tmpNeighbourMaxDeltaN.end(), par.NeighbourMaxDeltaN); -} -void to_json(nlohmann::json& j, const MemoryParameters& par) -{ - std::array<float, constants::its::CellsPerRoad> tmpCellsMemoryCoefficients; - std::copy(par.CellsMemoryCoefficients, par.CellsMemoryCoefficients + tmpCellsMemoryCoefficients.size(), tmpCellsMemoryCoefficients.begin()); - std::array<float, constants::its::TrackletsPerRoad> tmpTrackletsMemoryCoefficients; - std::copy(par.TrackletsMemoryCoefficients, par.TrackletsMemoryCoefficients + tmpTrackletsMemoryCoefficients.size(), tmpTrackletsMemoryCoefficients.begin()); - j = nlohmann::json{ - {"MemoryOffset", par.MemoryOffset}, - {"CellsMemoryCoefficients", tmpCellsMemoryCoefficients}, - {"TrackletsMemoryCoefficients", tmpTrackletsMemoryCoefficients}}; -} - -void from_json(const nlohmann::json& j, MemoryParameters& par) -{ - par.MemoryOffset = j.at("MemoryOffset").get<int>(); - auto tmpCellsMemoryCoefficients = j.at("CellsMemoryCoefficients").get<std::array<float, constants::its::CellsPerRoad>>(); - std::copy(tmpCellsMemoryCoefficients.begin(), tmpCellsMemoryCoefficients.end(), par.CellsMemoryCoefficients); - auto tmpTrackletsMemoryCoefficients = j.at("TrackletsMemoryCoefficients").get<std::array<float, constants::its::TrackletsPerRoad>>(); - std::copy(tmpTrackletsMemoryCoefficients.begin(), tmpTrackletsMemoryCoefficients.end(), par.TrackletsMemoryCoefficients); -} } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/PrimaryVertexContext.cxx b/Detectors/ITSMFT/ITS/tracking/src/PrimaryVertexContext.cxx index 87ec5bb5719f3..9567162b40376 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/PrimaryVertexContext.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/PrimaryVertexContext.cxx @@ -21,8 +21,8 @@ namespace o2 namespace its { -void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const std::array<std::vector<Cluster>, constants::its::LayersNumber>& cl, - const std::array<float, 3>& pVtx, const int iteration) +void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, + const std::vector<std::vector<Cluster>>& cl, const std::array<float, 3>& pVtx, const int iteration) { struct ClusterHelper { @@ -38,7 +38,20 @@ void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const st std::vector<ClusterHelper> cHelper; - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { + mMinR.resize(trkParam.NLayers, 10000.); + mMaxR.resize(trkParam.NLayers, -1.); + mClusters.resize(trkParam.NLayers); + mUsedClusters.resize(trkParam.NLayers); + mCells.resize(trkParam.CellsPerRoad()); + mCellsLookupTable.resize(trkParam.CellsPerRoad() - 1); + mCellsNeighbours.resize(trkParam.CellsPerRoad() - 1); + mIndexTables.resize(trkParam.TrackletsPerRoad(), std::vector<int>(trkParam.ZBins * trkParam.PhiBins + 1, 0)); + mTracklets.resize(trkParam.TrackletsPerRoad()); + mTrackletsLookupTable.resize(trkParam.CellsPerRoad()); + mIndexTableUtils.setTrackingParameters(trkParam); + + std::vector<int> clsPerBin(trkParam.PhiBins * trkParam.ZBins, 0); + for (unsigned int iLayer{0}; iLayer < mClusters.size(); ++iLayer) { const auto& currentLayer{cl[iLayer]}; const int clustersNum{static_cast<int>(currentLayer.size())}; @@ -48,32 +61,34 @@ void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const st mUsedClusters[iLayer].clear(); mUsedClusters[iLayer].resize(clustersNum, false); - constexpr int _size = constants::index_table::PhiBins * constants::index_table::ZBins; - std::array<int, _size> clsPerBin; - for (int iB{0}; iB < _size; ++iB) { - clsPerBin[iB] = 0; - } + std::fill(clsPerBin.begin(), clsPerBin.end(), 0); cHelper.clear(); cHelper.resize(clustersNum); + mMinR[iLayer] = 1000.f; + mMaxR[iLayer] = -1.f; + for (int iCluster{0}; iCluster < clustersNum; ++iCluster) { const Cluster& c = currentLayer[iCluster]; ClusterHelper& h = cHelper[iCluster]; float x = c.xCoordinate - mPrimaryVertex.x; float y = c.yCoordinate - mPrimaryVertex.y; float phi = math_utils::calculatePhiCoordinate(x, y); - int bin = index_table_utils::getBinIndex(index_table_utils::getZBinIndex(iLayer, c.zCoordinate), - index_table_utils::getPhiBinIndex(phi)); + const int zBin{mIndexTableUtils.getZBinIndex(iLayer, c.zCoordinate)}; + int bin = mIndexTableUtils.getBinIndex(zBin, mIndexTableUtils.getPhiBinIndex(phi)); + CA_DEBUGGER(assert(zBin > 0)); h.phi = phi; h.r = math_utils::calculateRCoordinate(x, y); + mMinR[iLayer] = o2::gpu::GPUCommonMath::Min(h.r, mMinR[iLayer]); + mMaxR[iLayer] = o2::gpu::GPUCommonMath::Max(h.r, mMaxR[iLayer]); h.bin = bin; h.ind = clsPerBin[bin]++; } - std::array<int, _size> lutPerBin; + std::vector<int> lutPerBin(clsPerBin.size()); lutPerBin[0] = 0; - for (int iB{1}; iB < _size; ++iB) { + for (unsigned int iB{1}; iB < lutPerBin.size(); ++iB) { lutPerBin[iB] = lutPerBin[iB - 1] + clsPerBin[iB - 1]; } @@ -87,10 +102,10 @@ void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const st } if (iLayer > 0) { - for (int iB{0}; iB < _size; ++iB) { + for (unsigned int iB{0}; iB < clsPerBin.size(); ++iB) { mIndexTables[iLayer - 1][iB] = lutPerBin[iB]; } - for (int iB{_size}; iB < (int)mIndexTables[iLayer - 1].size(); iB++) { + for (auto iB{clsPerBin.size()}; iB < mIndexTables[iLayer - 1].size(); iB++) { mIndexTables[iLayer - 1][iB] = clustersNum; } } @@ -99,8 +114,8 @@ void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const st mRoads.clear(); - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { - if (iLayer < constants::its::CellsPerRoad) { + for (unsigned int iLayer{0}; iLayer < mClusters.size(); ++iLayer) { + if (iLayer < mCells.size()) { mCells[iLayer].clear(); float cellsMemorySize = memParam.MemoryOffset + @@ -113,32 +128,32 @@ void PrimaryVertexContext::initialise(const MemoryParameters& memParam, const st } } - if (iLayer < constants::its::CellsPerRoad - 1) { + if (iLayer < mCells.size() - 1) { mCellsLookupTable[iLayer].clear(); - mCellsLookupTable[iLayer].resize( - std::max(cl[iLayer + 1].size(), cl[iLayer + 2].size()) + - std::ceil((memParam.TrackletsMemoryCoefficients[iLayer + 1] * - cl[iLayer + 1].size()) * - cl[iLayer + 2].size()), - constants::its::UnusedIndex); + mCellsLookupTable[iLayer].resize(memParam.MemoryOffset + + std::max(cl[iLayer + 1].size(), cl[iLayer + 2].size()) + + std::ceil((memParam.TrackletsMemoryCoefficients[iLayer + 1] * + cl[iLayer + 1].size()) * + cl[iLayer + 2].size()), + constants::its::UnusedIndex); mCellsNeighbours[iLayer].clear(); } } - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { - if (iLayer < constants::its::TrackletsPerRoad) { + for (unsigned int iLayer{0}; iLayer < mClusters.size(); ++iLayer) { + if (iLayer < mTracklets.size()) { mTracklets[iLayer].clear(); - float trackletsMemorySize = - std::max(cl[iLayer].size(), cl[iLayer + 1].size()) + - std::ceil((memParam.TrackletsMemoryCoefficients[iLayer] * cl[iLayer].size()) * - cl[iLayer + 1].size()); + float trackletsMemorySize = memParam.MemoryOffset + + std::max(cl[iLayer].size(), cl[iLayer + 1].size()) + + std::ceil((memParam.TrackletsMemoryCoefficients[iLayer] * cl[iLayer].size()) * + cl[iLayer + 1].size()); if (trackletsMemorySize > mTracklets[iLayer].capacity()) { mTracklets[iLayer].reserve(trackletsMemorySize); } } - if (iLayer < constants::its::CellsPerRoad) { + if (iLayer < mCells.size()) { mTrackletsLookupTable[iLayer].clear(); mTrackletsLookupTable[iLayer].resize(cl[iLayer + 1].size(), constants::its::UnusedIndex); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx b/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx index d2139bffc5102..76ba90171c3cf 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx @@ -21,8 +21,12 @@ namespace o2 namespace its { -ROframe::ROframe(const int ROframeId) : mROframeId{ROframeId} +ROframe::ROframe(int ROframeId, int nLayers) : mROframeId{ROframeId} { + mClusters.resize(nLayers); + mTrackingFrameInfo.resize(nLayers); + mClusterLabels.resize(nLayers); + mClusterExternalIndices.resize(nLayers); } void ROframe::addPrimaryVertex(const float xCoordinate, const float yCoordinate, const float zCoordinate) @@ -51,8 +55,9 @@ void ROframe::printPrimaryVertices() const int ROframe::getTotalClusters() const { size_t totalClusters{0}; - for (auto& clusters : mClusters) + for (auto& clusters : mClusters) { totalClusters += clusters.size(); + } return int(totalClusters); } } // namespace its diff --git a/Detectors/ITSMFT/ITS/tracking/src/Road.cxx b/Detectors/ITSMFT/ITS/tracking/src/Road.cxx index da5748bbcd75a..facb1f43fa1e2 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Road.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Road.cxx @@ -13,6 +13,8 @@ /// #include "ITStracking/Road.h" +#include <cassert> +#include <iostream> namespace o2 { @@ -25,7 +27,7 @@ Road::Road(int cellLayer, int cellId) : Road() { addCell(cellLayer, cellId); } void Road::resetRoad() { - for (int i = 0; i < constants::its::CellsPerRoad; i++) { + for (int i = 0; i < mMaxRoadSize; i++) { mCellIds[i] = constants::its::UnusedIndex; } mRoadSize = 0; diff --git a/Detectors/ITSMFT/ITS/tracking/src/StandaloneDebugger.cxx b/Detectors/ITSMFT/ITS/tracking/src/StandaloneDebugger.cxx index 7a7b495946db0..32625e1ed643d 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/StandaloneDebugger.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/StandaloneDebugger.cxx @@ -12,14 +12,12 @@ /// \brief /// \author matteo.concas@cern.ch -#include <string> -#include <iterator> #include "ITStracking/Cluster.h" #include "ITStracking/Tracklet.h" #include "ITStracking/ClusterLines.h" #include "CommonUtils/TreeStreamRedirector.h" -#include "ITStracking/ROframe.h" #include "ITStracking/StandaloneDebugger.h" + #include "TH1I.h" #include "TMath.h" @@ -257,5 +255,20 @@ int StandaloneDebugger::getBinIndex(const float value, const int size, const flo return std::distance(divisions.begin(), TMath::BinarySearch(divisions.begin(), divisions.end(), value)); } +// Tracker +void StandaloneDebugger::dumpTrackToBranchWithInfo(std::string branchName, o2::its::TrackITSExt track, const ROframe event, PrimaryVertexContext* pvc, const bool dumpClusters) +{ + FakeTrackInfo<7> t{pvc, event, track, dumpClusters}; + + (*mTreeStream) + << branchName.data() + << track + << "\n"; + + (*mTreeStream) + << "TracksInfo" + << t + << "\n"; +} } // namespace its } // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 844c90aa56e7f..ff42caa5086c9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -14,13 +14,13 @@ #include "ITStracking/Tracker.h" -#include "CommonConstants/MathConstants.h" #include "ITStracking/Cell.h" #include "ITStracking/Constants.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/Tracklet.h" #include "ITStracking/TrackerTraits.h" #include "ITStracking/TrackerTraitsCPU.h" +#include "ITStracking/TrackingConfigParam.h" #include "ReconstructionDataFormats/Track.h" #include <cassert> @@ -39,12 +39,20 @@ Tracker::Tracker(o2::its::TrackerTraits* traits) /// Initialise standard configuration with 1 iteration mTrkParams.resize(1); mMemParams.resize(1); - assert(mTracks != nullptr); mTraits = traits; mPrimaryVertexContext = mTraits->getPrimaryVertexContext(); +#ifdef CA_DEBUG + mDebugger = new StandaloneDebugger("dbg_ITSTrackerCPU.root"); +#endif } - +#ifdef CA_DEBUG +Tracker::~Tracker() +{ + delete mDebugger; +} +#else Tracker::~Tracker() = default; +#endif void Tracker::clustersToTracks(const ROframe& event, std::ostream& timeBenchmarkOutputStream) { @@ -57,22 +65,31 @@ void Tracker::clustersToTracks(const ROframe& event, std::ostream& timeBenchmark float total{0.f}; for (int iteration = 0; iteration < mTrkParams.size(); ++iteration) { + + int numCls = 0; + for (unsigned int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { + numCls += event.getClusters()[iLayer].size(); + } + if (numCls < mTrkParams[iteration].MinTrackLength) { + continue; + } + mTraits->UpdateTrackingParameters(mTrkParams[iteration]); /// Ugly hack -> Unifiy float3 definition in CPU and CUDA/HIP code int pass = iteration + iVertex; /// Do not reinitialise the context if we analyse pile-up events std::array<float, 3> pV = {event.getPrimaryVertex(iVertex).x, event.getPrimaryVertex(iVertex).y, event.getPrimaryVertex(iVertex).z}; total += evaluateTask(&Tracker::initialisePrimaryVertexContext, "Context initialisation", - timeBenchmarkOutputStream, mMemParams[iteration], event.getClusters(), pV, pass); + timeBenchmarkOutputStream, mMemParams[iteration], mTrkParams[iteration], event.getClusters(), pV, pass); total += evaluateTask(&Tracker::computeTracklets, "Tracklet finding", timeBenchmarkOutputStream); total += evaluateTask(&Tracker::computeCells, "Cell finding", timeBenchmarkOutputStream); total += evaluateTask(&Tracker::findCellsNeighbours, "Neighbour finding", timeBenchmarkOutputStream, iteration); total += evaluateTask(&Tracker::findRoads, "Road finding", timeBenchmarkOutputStream, iteration); total += evaluateTask(&Tracker::findTracks, "Track finding", timeBenchmarkOutputStream, event); } - - if (constants::DoTimeBenchmarks) + if (constants::DoTimeBenchmarks && fair::Logger::Logging(fair::Severity::info)) { timeBenchmarkOutputStream << std::setw(2) << " - " << "Vertex processing completed in: " << total << "ms" << std::endl; + } } if (event.hasMCinformation()) { computeTracksMClabels(event); @@ -93,7 +110,7 @@ void Tracker::computeCells() void Tracker::findCellsNeighbours(int& iteration) { - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad - 1; ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad() - 1; ++iLayer) { if (mPrimaryVertexContext->getCells()[iLayer + 1].empty() || mPrimaryVertexContext->getCellsLookupTable()[iLayer].empty()) { @@ -101,6 +118,8 @@ void Tracker::findCellsNeighbours(int& iteration) } int layerCellsNum{static_cast<int>(mPrimaryVertexContext->getCells()[iLayer].size())}; + const int nextLayerCellsNum{static_cast<int>(mPrimaryVertexContext->getCells()[iLayer + 1].size())}; + mPrimaryVertexContext->getCellsNeighbours()[iLayer].resize(nextLayerCellsNum); for (int iCell{0}; iCell < layerCellsNum; ++iCell) { @@ -111,16 +130,13 @@ void Tracker::findCellsNeighbours(int& iteration) mPrimaryVertexContext->getCells()[iLayer + 1][nextLayerFirstCellIndex].getFirstTrackletIndex() == nextLayerTrackletIndex) { - const int nextLayerCellsNum{static_cast<int>(mPrimaryVertexContext->getCells()[iLayer + 1].size())}; - mPrimaryVertexContext->getCellsNeighbours()[iLayer].resize(nextLayerCellsNum); - - for (int iNextLayerCell{nextLayerFirstCellIndex}; - iNextLayerCell < nextLayerCellsNum && - mPrimaryVertexContext->getCells()[iLayer + 1][iNextLayerCell].getFirstTrackletIndex() == - nextLayerTrackletIndex; - ++iNextLayerCell) { + for (int iNextLayerCell{nextLayerFirstCellIndex}; iNextLayerCell < nextLayerCellsNum; ++iNextLayerCell) { Cell& nextCell{mPrimaryVertexContext->getCells()[iLayer + 1][iNextLayerCell]}; + if (nextCell.getFirstTrackletIndex() != nextLayerTrackletIndex) { + break; + } + const float3 currentCellNormalVector{currentCell.getNormalVectorCoordinates()}; const float3 nextCellNormalVector{nextCell.getNormalVectorCoordinates()}; const float3 normalVectorsDeltaVector{currentCellNormalVector.x - nextCellNormalVector.x, @@ -152,11 +168,11 @@ void Tracker::findCellsNeighbours(int& iteration) void Tracker::findRoads(int& iteration) { - for (int iLevel{constants::its::CellsPerRoad}; iLevel >= mTrkParams[iteration].CellMinimumLevel(); --iLevel) { + for (int iLevel{mTrkParams[iteration].CellsPerRoad()}; iLevel >= mTrkParams[iteration].CellMinimumLevel(); --iLevel) { CA_DEBUGGER(int nRoads = -mPrimaryVertexContext->getRoads().size()); const int minimumLevel{iLevel - 1}; - for (int iLayer{constants::its::CellsPerRoad - 1}; iLayer >= minimumLevel; --iLayer) { + for (int iLayer{mTrkParams[iteration].CellsPerRoad() - 1}; iLayer >= minimumLevel; --iLayer) { const int levelCellsNum{static_cast<int>(mPrimaryVertexContext->getCells()[iLayer].size())}; @@ -218,19 +234,19 @@ void Tracker::findTracks(const ROframe& event) std::vector<TrackITSExt> tracks; tracks.reserve(mPrimaryVertexContext->getRoads().size()); #ifdef CA_DEBUG - std::array<int, 4> roadCounters{0, 0, 0, 0}; - std::array<int, 4> fitCounters{0, 0, 0, 0}; - std::array<int, 4> backpropagatedCounters{0, 0, 0, 0}; - std::array<int, 4> refitCounters{0, 0, 0, 0}; - std::array<int, 4> nonsharingCounters{0, 0, 0, 0}; + std::vector<int> roadCounters(mTrkParams[0].NLayers - 3, 0); + std::vector<int> fitCounters(mTrkParams[0].NLayers - 3, 0); + std::vector<int> backpropagatedCounters(mTrkParams[0].NLayers - 3, 0); + std::vector<int> refitCounters(mTrkParams[0].NLayers - 3, 0); + std::vector<int> nonsharingCounters(mTrkParams[0].NLayers - 3, 0); #endif for (auto& road : mPrimaryVertexContext->getRoads()) { - std::array<int, 7> clusters{constants::its::UnusedIndex, constants::its::UnusedIndex, constants::its::UnusedIndex, constants::its::UnusedIndex, constants::its::UnusedIndex, constants::its::UnusedIndex, constants::its::UnusedIndex}; + std::vector<int> clusters(mTrkParams[0].NLayers, constants::its::UnusedIndex); int lastCellLevel = constants::its::UnusedIndex; CA_DEBUGGER(int nClusters = 2); - for (int iCell{0}; iCell < constants::its::CellsPerRoad; ++iCell) { + for (int iCell{0}; iCell < mTrkParams[0].CellsPerRoad(); ++iCell) { const int cellIndex = road[iCell]; if (cellIndex == constants::its::UnusedIndex) { continue; @@ -246,11 +262,12 @@ void Tracker::findTracks(const ROframe& event) } } - assert(nClusters >= mTrkParams[0].MinTrackLength); + CA_DEBUGGER(assert(nClusters >= mTrkParams[0].MinTrackLength)); CA_DEBUGGER(roadCounters[nClusters - 4]++); - if (lastCellLevel == constants::its::UnusedIndex) + if (lastCellLevel == constants::its::UnusedIndex) { continue; + } /// From primary vertex context index to event index (== the one used as input of the tracking code) for (int iC{0}; iC < clusters.size(); iC++) { @@ -270,24 +287,29 @@ void Tracker::findTracks(const ROframe& event) for (size_t iC = 0; iC < clusters.size(); ++iC) { temporaryTrack.setExternalClusterIndex(iC, clusters[iC], clusters[iC] != constants::its::UnusedIndex); } - bool fitSuccess = fitTrack(event, temporaryTrack, constants::its::LayersNumber - 4, -1, -1); - if (!fitSuccess) + bool fitSuccess = fitTrack(event, temporaryTrack, mTrkParams[0].NLayers - 4, -1, -1); + if (!fitSuccess) { continue; + } CA_DEBUGGER(fitCounters[nClusters - 4]++); temporaryTrack.resetCovariance(); - fitSuccess = fitTrack(event, temporaryTrack, 0, constants::its::LayersNumber, 1); - if (!fitSuccess) + fitSuccess = fitTrack(event, temporaryTrack, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].FitIterationMaxChi2[0]); + if (!fitSuccess) { continue; + } CA_DEBUGGER(backpropagatedCounters[nClusters - 4]++); temporaryTrack.getParamOut() = temporaryTrack; temporaryTrack.resetCovariance(); - fitSuccess = fitTrack(event, temporaryTrack, constants::its::LayersNumber - 1, -1, -1); - if (!fitSuccess) + fitSuccess = fitTrack(event, temporaryTrack, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].FitIterationMaxChi2[1]); +#ifdef CA_DEBUG + mDebugger->dumpTrackToBranchWithInfo("testBranch", temporaryTrack, event, mPrimaryVertexContext, true); +#endif + if (!fitSuccess) { continue; + } CA_DEBUGGER(refitCounters[nClusters - 4]++); - temporaryTrack.setROFrame(mROFrame); tracks.emplace_back(temporaryTrack); - assert(nClusters == temporaryTrack.getNumberOfClusters()); + CA_DEBUGGER(assert(nClusters == temporaryTrack.getNumberOfClusters())); } //mTraits->refitTracks(event.getTrackingFrameInfo(), tracks); @@ -295,19 +317,19 @@ void Tracker::findTracks(const ROframe& event) [](TrackITSExt& track1, TrackITSExt& track2) { return track1.isBetter(track2, 1.e6f); }); #ifdef CA_DEBUG - std::array<int, 26> sharingMatrix{0}; - int prevNclusters = 7; - auto cumulativeIndex = [](int ncl) -> int { - constexpr int idx[5] = {0, 5, 11, 18, 26}; - return idx[ncl - 4]; - }; - std::array<int, 4> xcheckCounters{0}; + // std::array<int, 26> sharingMatrix{0}; + // int prevNclusters = 7; + // auto cumulativeIndex = [](int ncl) -> int { + // constexpr int idx[5] = {0, 5, 11, 18, 26}; + // return idx[ncl - 4]; + // }; + // std::array<int, 4> xcheckCounters{0}; #endif for (auto& track : tracks) { CA_DEBUGGER(int nClusters = 0); int nShared = 0; - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::its::UnusedIndex) { continue; } @@ -315,24 +337,24 @@ void Tracker::findTracks(const ROframe& event) CA_DEBUGGER(nClusters++); } -#ifdef CA_DEBUG - assert(nClusters == track.getNumberOfClusters()); - xcheckCounters[nClusters - 4]++; - assert(nShared <= nClusters); - sharingMatrix[cumulativeIndex(nClusters) + nShared]++; -#endif + // #ifdef CA_DEBUG + // assert(nClusters == track.getNumberOfClusters()); + // xcheckCounters[nClusters - 4]++; + // assert(nShared <= nClusters); + // sharingMatrix[cumulativeIndex(nClusters) + nShared]++; + // #endif if (nShared > mTrkParams[0].ClusterSharing) { continue; } -#ifdef CA_DEBUG - nonsharingCounters[nClusters - 4]++; - assert(nClusters <= prevNclusters); - prevNclusters = nClusters; -#endif + // #ifdef CA_DEBUG + // nonsharingCounters[nClusters - 4]++; + // assert(nClusters <= prevNclusters); + // prevNclusters = nClusters; + // #endif - for (int iLayer{0}; iLayer < constants::its::LayersNumber; ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::its::UnusedIndex) { continue; } @@ -362,30 +384,30 @@ void Tracker::findTracks(const ROframe& event) std::cout << count << "\t"; std::cout << std::endl; - std::cout << "+++ Cross check counters for 4, 5, 6 and 7 clusters:\t"; - for (size_t iCount = 0; iCount < refitCounters.size(); ++iCount) { - std::cout << xcheckCounters[iCount] << "\t"; - //assert(refitCounters[iCount] == xcheckCounters[iCount]); - } - std::cout << std::endl; - - std::cout << "+++ Nonsharing candidates with 4, 5, 6 and 7 clusters:\t"; - for (int count : nonsharingCounters) - std::cout << count << "\t"; - std::cout << std::endl; - - std::cout << "+++ Sharing matrix:\n"; - for (int iCl = 4; iCl <= 7; ++iCl) { - std::cout << "+++ "; - for (int iSh = cumulativeIndex(iCl); iSh < cumulativeIndex(iCl + 1); ++iSh) { - std::cout << sharingMatrix[iSh] << "\t"; - } - std::cout << std::endl; - } + // std::cout << "+++ Cross check counters for 4, 5, 6 and 7 clusters:\t"; + // for (size_t iCount = 0; iCount < refitCounters.size(); ++iCount) { + // std::cout << xcheckCounters[iCount] << "\t"; + // //assert(refitCounters[iCount] == xcheckCounters[iCount]); + // } + // std::cout << std::endl; + + // std::cout << "+++ Nonsharing candidates with 4, 5, 6 and 7 clusters:\t"; + // for (int count : nonsharingCounters) + // std::cout << count << "\t"; + // std::cout << std::endl; + + // std::cout << "+++ Sharing matrix:\n"; + // for (int iCl = 4; iCl <= 7; ++iCl) { + // std::cout << "+++ "; + // for (int iSh = cumulativeIndex(iCl); iSh < cumulativeIndex(iCl + 1); ++iSh) { + // std::cout << sharingMatrix[iSh] << "\t"; + // } + // std::cout << std::endl; + // } #endif } -bool Tracker::fitTrack(const ROframe& event, TrackITSExt& track, int start, int end, int step) +bool Tracker::fitTrack(const ROframe& event, TrackITSExt& track, int start, int end, int step, const float chi2cut) { track.setChi2(0); for (int iLayer{start}; iLayer != end; iLayer += step) { @@ -394,22 +416,44 @@ bool Tracker::fitTrack(const ROframe& event, TrackITSExt& track, int start, int } const TrackingFrameInfo& trackingHit = event.getTrackingFrameInfoOnLayer(iLayer).at(track.getClusterIndex(iLayer)); - if (!track.rotate(trackingHit.alphaTrackingFrame)) + if (!track.rotate(trackingHit.alphaTrackingFrame)) { return false; + } - if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) + if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) { return false; - - track.setChi2(track.getChi2() + - track.getPredictedChi2(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)); - if (!track.TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) + } + auto predChi2{track.getPredictedChi2(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if (predChi2 > chi2cut) { return false; + } + track.setChi2(track.getChi2() + predChi2); + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + return false; + } - const float xx0 = (iLayer > 2) ? 0.008f : 0.003f; // Rough layer thickness - constexpr float radiationLength = 9.36f; // Radiation length of Si [cm] - constexpr float density = 2.33f; // Density of Si [g/cm^3] - if (!track.correctForMaterial(xx0, xx0 * radiationLength * density, true)) + float xx0 = ((iLayer > 2) ? 0.008f : 0.003f); // Rough layer thickness + float radiationLength = 9.36f; // Radiation length of Si [cm] + float density = 2.33f; // Density of Si [g/cm^3] + float distance = xx0; // Default thickness + + if (mMatLayerCylSet) { + if ((iLayer + step) != end) { + const auto cl_0 = mPrimaryVertexContext->getClusters()[iLayer][track.getClusterIndex(iLayer)]; + const auto cl_1 = mPrimaryVertexContext->getClusters()[iLayer + step][track.getClusterIndex(iLayer + step)]; + + auto matbud = mMatLayerCylSet->getMatBudget(cl_0.xCoordinate, cl_0.yCoordinate, cl_0.zCoordinate, cl_1.xCoordinate, cl_1.yCoordinate, cl_1.zCoordinate); + xx0 = matbud.meanX2X0; + density = matbud.meanRho; + distance = matbud.length; + } + } + // The correctForMaterial should be called with anglecorr==true if the material budget is the "mean budget in vertical direction" and with false if the the estimated budget already accounts for the track inclination. + // Here using !mMatLayerCylSet as its presence triggers update of parameters + + if (!track.correctForMaterial(xx0, ((start < end) ? -1. : 1.) * distance * density, !mMatLayerCylSet)) { // ~0.14 GeV: mass of charged pion is used by default return false; + } } return true; } @@ -421,8 +465,7 @@ void Tracker::traverseCellsTree(const int currentCellId, const int currentLayerI mPrimaryVertexContext->getRoads().back().addCell(currentLayerId, currentCellId); - if (currentLayerId > 0) { - + if (currentLayerId > 0 && currentCellLevel > 1) { const int cellNeighboursNum{static_cast<int>( mPrimaryVertexContext->getCellsNeighbours()[currentLayerId - 1][currentCellId].size())}; bool isFirstValidNeighbour = true; @@ -471,7 +514,7 @@ void Tracker::computeRoadsMClabels(const ROframe& event) bool isFakeRoad{false}; bool isFirstRoadCell{true}; - for (int iCell{0}; iCell < constants::its::CellsPerRoad; ++iCell) { + for (int iCell{0}; iCell < mTrkParams[0].CellsPerRoad(); ++iCell) { const int currentCellIndex{currentRoad[iCell]}; if (currentCellIndex == constants::its::UnusedIndex) { @@ -548,7 +591,6 @@ void Tracker::computeTracksMClabels(const ROframe& event) if (index == constants::its::UnusedIndex) { continue; } - const MCCompLabel& currentLabel = event.getClusterLabels(iCluster, index); if (currentLabel == maxOccurrencesValue) { ++count; @@ -568,7 +610,7 @@ void Tracker::computeTracksMClabels(const ROframe& event) if (isFakeTrack) { maxOccurrencesValue.setFakeFlag(); } - mTrackLabels.addElement(mTrackLabels.getIndexedSize(), maxOccurrencesValue); + mTrackLabels.emplace_back(maxOccurrencesValue); } } @@ -623,5 +665,14 @@ track::TrackParCov Tracker::buildTrackSeed(const Cluster& cluster1, const Cluste s2 * fy * cy, 0.f, s2 * cy * cy}); } +void Tracker::getGlobalConfiguration() +{ + auto& tc = o2::its::TrackerParamConfig::Instance(); + + if (tc.useMatBudLUT) { + initMatBudLUTFromFile(); + } +} + } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraitsCPU.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraitsCPU.cxx index b34bfce0cb928..456168f74554f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraitsCPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraitsCPU.cxx @@ -34,9 +34,9 @@ namespace its void TrackerTraitsCPU::computeLayerTracklets() { PrimaryVertexContext* primaryVertexContext = mPrimaryVertexContext; - for (int iLayer{0}; iLayer < constants::its::TrackletsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams.TrackletsPerRoad(); ++iLayer) { if (primaryVertexContext->getClusters()[iLayer].empty() || primaryVertexContext->getClusters()[iLayer + 1].empty()) { - return; + continue; } const float3& primaryVertex = primaryVertexContext->getPrimaryVertex(); @@ -50,11 +50,14 @@ void TrackerTraitsCPU::computeLayerTracklets() } const float tanLambda{(currentCluster.zCoordinate - primaryVertex.z) / currentCluster.rCoordinate}; - const float directionZIntersection{tanLambda * (constants::its::LayersRCoordinate()[iLayer + 1] - - currentCluster.rCoordinate) + - currentCluster.zCoordinate}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, iLayer, directionZIntersection, + const float zAtRmin{tanLambda * (mPrimaryVertexContext->getMinR(iLayer + 1) - + currentCluster.rCoordinate) + + currentCluster.zCoordinate}; + const float zAtRmax{tanLambda * (mPrimaryVertexContext->getMaxR(iLayer + 1) - + currentCluster.rCoordinate) + + currentCluster.zCoordinate}; + + const int4 selectedBinsRect{getBinsRect(currentCluster, iLayer, zAtRmin, zAtRmax, mTrkParams.TrackletMaxDeltaZ[iLayer], mTrkParams.TrackletMaxDeltaPhi)}; if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { @@ -64,12 +67,12 @@ void TrackerTraitsCPU::computeLayerTracklets() int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; if (phiBinsNum < 0) { - phiBinsNum += constants::index_table::PhiBins; + phiBinsNum += mTrkParams.PhiBins; } for (int iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; - iPhiBin = ++iPhiBin == constants::index_table::PhiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + iPhiBin = ++iPhiBin == mTrkParams.PhiBins ? 0 : iPhiBin, iPhiCount++) { + const int firstBinIndex{primaryVertexContext->mIndexTableUtils.getBinIndex(selectedBinsRect.x, iPhiBin)}; const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; const int firstRowClusterIndex = primaryVertexContext->getIndexTables()[iLayer][firstBinIndex]; const int maxRowClusterIndex = primaryVertexContext->getIndexTables()[iLayer][maxBinIndex]; @@ -77,19 +80,23 @@ void TrackerTraitsCPU::computeLayerTracklets() for (int iNextLayerCluster{firstRowClusterIndex}; iNextLayerCluster < maxRowClusterIndex; ++iNextLayerCluster) { + if (iNextLayerCluster >= (int)primaryVertexContext->getClusters()[iLayer + 1].size()) { + break; + } + const Cluster& nextCluster{primaryVertexContext->getClusters()[iLayer + 1][iNextLayerCluster]}; if (primaryVertexContext->isClusterUsed(iLayer + 1, nextCluster.clusterId)) { continue; } - const float deltaZ{gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.rCoordinate - currentCluster.rCoordinate) + - currentCluster.zCoordinate - nextCluster.zCoordinate)}; - const float deltaPhi{gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate)}; + const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.rCoordinate - currentCluster.rCoordinate) + + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate)}; if (deltaZ < mTrkParams.TrackletMaxDeltaZ[iLayer] && (deltaPhi < mTrkParams.TrackletMaxDeltaPhi || - gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < mTrkParams.TrackletMaxDeltaPhi)) { + o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < mTrkParams.TrackletMaxDeltaPhi)) { if (iLayer > 0 && primaryVertexContext->getTrackletsLookupTable()[iLayer - 1][iCluster] == constants::its::UnusedIndex) { @@ -104,13 +111,25 @@ void TrackerTraitsCPU::computeLayerTracklets() } } } + if (iLayer > 0 && iLayer < mTrkParams.TrackletsPerRoad() - 1 && + primaryVertexContext->getTracklets()[iLayer].size() > primaryVertexContext->getCellsLookupTable()[iLayer - 1].size()) { + std::cout << "**** FATAL: not enough memory in the CellsLookupTable, increase the tracklet memory coefficients ****" << std::endl; + exit(1); + } + } +#ifdef CA_DEBUG + std::cout << "+++ Number of tracklets per layer: "; + for (int iLayer{0}; iLayer < mTrkParams.TrackletsPerRoad(); ++iLayer) { + std::cout << primaryVertexContext->getTracklets()[iLayer].size() << "\t"; } + std::cout << std::endl; +#endif } void TrackerTraitsCPU::computeLayerCells() { PrimaryVertexContext* primaryVertexContext = mPrimaryVertexContext; - for (int iLayer{0}; iLayer < constants::its::CellsPerRoad; ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams.CellsPerRoad(); ++iLayer) { if (primaryVertexContext->getTracklets()[iLayer + 1].empty() || primaryVertexContext->getTracklets()[iLayer].empty()) { @@ -226,17 +245,24 @@ void TrackerTraitsCPU::computeLayerCells() } } } +#ifdef CA_DEBUG + std::cout << "+++ Number of cells per layer: "; + for (int iLayer{0}; iLayer < mTrkParams.CellsPerRoad(); ++iLayer) { + std::cout << primaryVertexContext->getCells()[iLayer].size() << "\t"; + } + std::cout << std::endl; +#endif } -void TrackerTraitsCPU::refitTracks(const std::array<std::vector<TrackingFrameInfo>, 7>& tf, std::vector<TrackITSExt>& tracks) +void TrackerTraitsCPU::refitTracks(const std::vector<std::vector<TrackingFrameInfo>>& tf, std::vector<TrackITSExt>& tracks) { - std::array<const Cell*, 5> cells; - for (int iLayer = 0; iLayer < 5; iLayer++) { - cells[iLayer] = mPrimaryVertexContext->getCells()[iLayer].data(); + std::vector<const Cell*> cells; + for (int iLayer = 0; iLayer < mTrkParams.CellsPerRoad(); iLayer++) { + cells.push_back(mPrimaryVertexContext->getCells()[iLayer].data()); } - std::array<const Cluster*, 7> clusters; - for (int iLayer = 0; iLayer < 7; iLayer++) { - clusters[iLayer] = mPrimaryVertexContext->getClusters()[iLayer].data(); + std::vector<const Cluster*> clusters; + for (int iLayer = 0; iLayer < mTrkParams.NLayers; iLayer++) { + clusters.push_back(mPrimaryVertexContext->getClusters()[iLayer].data()); } mChainRunITSTrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx index bcea094aab68b..c903b41843b1e 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx @@ -16,7 +16,9 @@ namespace o2 namespace its { static auto& sVertexerParamITS = o2::its::VertexerParamConfig::Instance(); +static auto& sCATrackerParamITS = o2::its::TrackerParamConfig::Instance(); -O2ParamImpl(o2::its::VertexerParamConfig) +O2ParamImpl(o2::its::VertexerParamConfig); +O2ParamImpl(o2::its::TrackerParamConfig); } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index afc832a3a6b24..385cec4ef8450 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -16,9 +16,11 @@ #pragma link C++ class o2::its::ClusterLines + ; #pragma link C++ class o2::its::Tracklet + ; -#pragma link C++ class o2::its::Centroid + ; #pragma link C++ class o2::its::VertexerParamConfig + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper <o2::its::VertexerParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::VertexerParamConfig> + ; + +#pragma link C++ class o2::its::TrackerParamConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper <o2::its::TrackerParamConfig> + ; #endif diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index c55fca3840077..a8e823c38399f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -37,31 +37,30 @@ namespace o2 namespace its { using boost::histogram::indexed; -using constants::index_table::PhiBins; -using constants::index_table::ZBins; -using constants::its::LayersRCoordinate; -using constants::its::LayersZCoordinate; using constants::math::TwoPi; -using index_table_utils::getZBinIndex; void trackleterKernelSerial( const std::vector<Cluster>& clustersNextLayer, // 0 2 const std::vector<Cluster>& clustersCurrentLayer, // 1 1 - const std::array<int, ZBins * PhiBins + 1>& indexTableNext, + const int* indexTableNext, const unsigned char pairOfLayers, const float phiCut, std::vector<Tracklet>& Tracklets, std::vector<int>& foundTracklets, + const IndexTableUtils& utils, // const ROframe* evt = nullptr, const int maxTrackletsPerCluster = static_cast<int>(2e3)) { + const int PhiBins{utils.getNphiBins()}; + const int ZBins{utils.getNzBins()}; + foundTracklets.resize(clustersCurrentLayer.size(), 0); // loop on layer1 clusters for (unsigned int iCurrentLayerClusterIndex{0}; iCurrentLayerClusterIndex < clustersCurrentLayer.size(); ++iCurrentLayerClusterIndex) { int storedTracklets{0}; const Cluster currentCluster{clustersCurrentLayer[iCurrentLayerClusterIndex]}; const int layerIndex{pairOfLayers == LAYER0_TO_LAYER1 ? 0 : 2}; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, layerIndex, 0.f, 50.f, phiCut / 2)}; + const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, layerIndex, 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) { @@ -69,13 +68,13 @@ void trackleterKernelSerial( } // loop on phi bins next layer for (int iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + 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 < static_cast<int>(clustersNextLayer.size()); ++iNextLayerClusterIndex) { const Cluster& nextCluster{clustersNextLayer[iNextLayerClusterIndex]}; - if (gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate) < phiCut) { + if (o2::gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate) < phiCut) { if (storedTracklets < maxTrackletsPerCluster) { if (pairOfLayers == LAYER0_TO_LAYER1) { Tracklets.emplace_back(iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster); @@ -115,8 +114,8 @@ void trackletSelectionKernelSerial( int validTracklets{0}; for (int iTracklet12{offset12}; iTracklet12 < offset12 + foundTracklets12[iCurrentLayerClusterIndex]; ++iTracklet12) { for (int iTracklet01{offset01}; iTracklet01 < offset01 + foundTracklets01[iCurrentLayerClusterIndex]; ++iTracklet01) { - const float deltaTanLambda{gpu::GPUCommonMath::Abs(tracklets01[iTracklet01].tanLambda - tracklets12[iTracklet12].tanLambda)}; - const float deltaPhi{gpu::GPUCommonMath::Abs(tracklets01[iTracklet01].phiCoordinate - tracklets12[iTracklet12].phiCoordinate)}; + const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklets01[iTracklet01].tanLambda - tracklets12[iTracklet12].tanLambda)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(tracklets01[iTracklet01].phiCoordinate - tracklets12[iTracklet12].phiCoordinate)}; if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTracklets) { assert(tracklets01[iTracklet01].secondClusterIndex == tracklets12[iTracklet12].firstClusterIndex); #ifdef _ALLOW_DEBUG_TREES_ITS_ @@ -150,9 +149,6 @@ VertexerTraits::VertexerTraits() : mAverageClustersRadii{std::array<float, 3>{0. VertexerTraits::VertexerTraits() : mAverageClustersRadii{std::array<float, 3>{0.f, 0.f, 0.f}}, mMaxDirectorCosine3{0.f} { - mVrtParams.phiSpan = static_cast<int>(std::ceil(constants::index_table::PhiBins * mVrtParams.phiCut / - constants::math::TwoPi)); - mVrtParams.zSpan = static_cast<int>(std::ceil(mVrtParams.zCut * constants::index_table::InverseZBinSize()[0])); } #endif @@ -169,7 +165,7 @@ void VertexerTraits::reset() { for (int iLayer{0}; iLayer < constants::its::LayersNumberVertexer; ++iLayer) { mClusters[iLayer].clear(); - mIndexTables[iLayer].fill(0); + std::fill(mIndexTables[iLayer].begin(), mIndexTables[iLayer].end(), 0); } mTracklets.clear(); @@ -193,6 +189,7 @@ std::vector<int> VertexerTraits::getMClabelsLayer(const int layer) const void VertexerTraits::arrangeClusters(ROframe* event) { + const auto& LayersZCoordinate = mVrtParams.LayerZ; mEvent = event; for (int iLayer{0}; iLayer < constants::its::LayersNumberVertexer; ++iLayer) { const auto& currentLayer{event->getClustersOnLayer(iLayer)}; @@ -202,7 +199,7 @@ void VertexerTraits::arrangeClusters(ROframe* event) mClusters[iLayer].reserve(clustersNum); } for (unsigned int iCluster{0}; iCluster < clustersNum; ++iCluster) { - mClusters[iLayer].emplace_back(iLayer, currentLayer.at(iCluster)); + mClusters[iLayer].emplace_back(iLayer, mIndexTableUtils, currentLayer.at(iCluster)); mAverageClustersRadii[iLayer] += mClusters[iLayer].back().rCoordinate; } mAverageClustersRadii[iLayer] *= 1.f / clustersNum; @@ -221,7 +218,7 @@ void VertexerTraits::arrangeClusters(ROframe* event) previousBinIndex = currentBinIndex; } } - for (int iBin{previousBinIndex + 1}; iBin <= ZBins * PhiBins; iBin++) { + for (int iBin{previousBinIndex + 1}; iBin <= mIndexTableUtils.getNzBins() * mIndexTableUtils.getNphiBins(); iBin++) { mIndexTables[iLayer][iBin] = static_cast<int>(clustersNum); } } @@ -229,24 +226,25 @@ void VertexerTraits::arrangeClusters(ROframe* event) mDeltaRadii10 = mAverageClustersRadii[1] - mAverageClustersRadii[0]; mDeltaRadii21 = mAverageClustersRadii[2] - mAverageClustersRadii[1]; mMaxDirectorCosine3 = - LayersZCoordinate()[2] / std::sqrt(LayersZCoordinate()[2] * LayersZCoordinate()[2] + - (mDeltaRadii10 + mDeltaRadii21) * (mDeltaRadii10 + mDeltaRadii21)); + LayersZCoordinate[2] / std::hypot(LayersZCoordinate[2], mDeltaRadii10 + mDeltaRadii21); } -const std::vector<std::pair<int, int>> VertexerTraits::selectClusters(const std::array<int, ZBins * PhiBins + 1>& indexTable, - const std::array<int, 4>& selectedBinsRect) +const std::vector<std::pair<int, int>> VertexerTraits::selectClusters(const int* indexTable, + const std::array<int, 4>& selectedBinsRect, + const IndexTableUtils& utils) { std::vector<std::pair<int, int>> filteredBins{}; int phiBinsNum{selectedBinsRect[3] - selectedBinsRect[1] + 1}; - if (phiBinsNum < 0) - phiBinsNum += PhiBins; + if (phiBinsNum < 0) { + phiBinsNum += utils.getNphiBins(); + } filteredBins.reserve(phiBinsNum); for (int iPhiBin{selectedBinsRect[1]}, iPhiCount{0}; iPhiCount < phiBinsNum; - iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect[0], iPhiBin)}; + iPhiBin = ++iPhiBin == utils.getNphiBins() ? 0 : iPhiBin, iPhiCount++) { + const int firstBinIndex{utils.getBinIndex(selectedBinsRect[0], iPhiBin)}; filteredBins.emplace_back( indexTable[firstBinIndex], - index_table_utils::countRowSelectedBins(indexTable, iPhiBin, selectedBinsRect[0], selectedBinsRect[2])); + utils.countRowSelectedBins(indexTable, iPhiBin, selectedBinsRect[0], selectedBinsRect[2])); } return filteredBins; } @@ -311,20 +309,22 @@ void VertexerTraits::computeTracklets() trackleterKernelSerial( mClusters[0], mClusters[1], - mIndexTables[0], + mIndexTables[0].data(), LAYER0_TO_LAYER1, mVrtParams.phiCut, mComb01, - mFoundTracklets01); + mFoundTracklets01, + mIndexTableUtils); trackleterKernelSerial( mClusters[2], mClusters[1], - mIndexTables[2], + mIndexTables[2].data(), LAYER1_TO_LAYER2, mVrtParams.phiCut, mComb12, - mFoundTracklets12); + mFoundTracklets12, + mIndexTableUtils); #ifdef _ALLOW_DEBUG_TREES_ITS_ if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { @@ -396,11 +396,13 @@ void VertexerTraits::computeVertices() std::vector<bool> usedTracklets{}; usedTracklets.resize(mTracklets.size(), false); for (int tracklet1{0}; tracklet1 < numTracklets; ++tracklet1) { - if (usedTracklets[tracklet1]) + if (usedTracklets[tracklet1]) { continue; + } for (int tracklet2{tracklet1 + 1}; tracklet2 < numTracklets; ++tracklet2) { - if (usedTracklets[tracklet2]) + if (usedTracklets[tracklet2]) { continue; + } if (Line::getDCA(mTracklets[tracklet1], mTracklets[tracklet2]) <= mVrtParams.pairCut) { mTrackletClusters.emplace_back(tracklet1, mTracklets[tracklet1], tracklet2, mTracklets[tracklet2]); std::array<float, 3> tmpVertex{mTrackletClusters.back().getVertex()}; @@ -411,8 +413,9 @@ void VertexerTraits::computeVertices() usedTracklets[tracklet1] = true; usedTracklets[tracklet2] = true; for (int tracklet3{0}; tracklet3 < numTracklets; ++tracklet3) { - if (usedTracklets[tracklet3]) + if (usedTracklets[tracklet3]) { continue; + } if (Line::getDistanceFromPoint(mTracklets[tracklet3], tmpVertex) < mVrtParams.pairCut) { mTrackletClusters.back().add(tracklet3, mTracklets[tracklet3]); usedTracklets[tracklet3] = true; diff --git a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index 790d7390334f0..60788887fd94f 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -10,6 +10,7 @@ o2_add_library(ITSWorkflow SOURCES src/RecoWorkflow.cxx + src/ClusterWriterWorkflow.cxx src/DigitReaderSpec.cxx src/ClustererSpec.cxx src/ClusterWriterSpec.cxx @@ -33,3 +34,8 @@ o2_add_executable(reco-workflow COMPONENT_NAME its PUBLIC_LINK_LIBRARIES O2::ITSWorkflow) +o2_add_executable(cluster-writer-workflow + SOURCES src/its-cluster-writer-workflow.cxx + COMPONENT_NAME its + PUBLIC_LINK_LIBRARIES O2::ITSWorkflow) + diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h new file mode 100644 index 0000000000000..3ac0c47d5e9c9 --- /dev/null +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h @@ -0,0 +1,30 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CLUSTER_WRITER_WORKFLOW_H +#define O2_ITS_CLUSTER_WRITER_WORKFLOW_H + +/// @file ClusterWriterWorkflow.h + +#include "Framework/WorkflowSpec.h" + +namespace o2 +{ +namespace its +{ + +namespace cluster_writer_workflow +{ +framework::WorkflowSpec getWorkflow(bool useMC); +} + +} // namespace its +} // namespace o2 +#endif diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h index 66be2b63d23f3..7ef7ae7ae7ca3 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h @@ -27,7 +27,7 @@ namespace its namespace reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU, +framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, bool async, o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, bool eencode = false); } diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h index 1e3c1cbe8a192..066b679c1c026 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h @@ -48,7 +48,7 @@ class TrackReader : public o2::framework::Task std::vector<o2::its::TrackITS> mTracks, *mTracksInp = &mTracks; std::vector<Vertex> mVertices, *mVerticesInp = &mVertices; std::vector<int> mClusInd, *mClusIndInp = &mClusInd; - o2::dataformats::MCTruthContainer<o2::MCCompLabel> mMCTruth, *mMCTruthInp = &mMCTruth; + std::vector<o2::MCCompLabel> mMCTruth, *mMCTruthInp = &mMCTruth; o2::header::DataOrigin mOrigin = o2::header::gDataOriginITS; diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h index cf62efe019dd0..374371f344005 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h @@ -38,7 +38,7 @@ namespace its class TrackerDPL : public framework::Task { public: - TrackerDPL(bool isMC, o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU); // : mIsMC{isMC} {} + TrackerDPL(bool isMC, bool async, o2::gpu::GPUDataTypes::DeviceType dType = o2::gpu::GPUDataTypes::DeviceType::CPU); // : mIsMC{isMC} {} ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; @@ -46,6 +46,7 @@ class TrackerDPL : public framework::Task private: bool mIsMC = false; + bool mAsyncMode = false; o2::itsmft::TopologyDictionary mDict; std::unique_ptr<o2::gpu::GPUReconstruction> mRecChain = nullptr; std::unique_ptr<parameters::GRPObject> mGRP = nullptr; @@ -56,7 +57,7 @@ class TrackerDPL : public framework::Task /// create a processor spec /// run ITS CA tracker -framework::DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::GPUDataTypes::DeviceType dType); +framework::DataProcessorSpec getTrackerSpec(bool useMC, bool async, o2::gpu::GPUDataTypes::DeviceType dType); } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx new file mode 100644 index 0000000000000..1330f30f7e42c --- /dev/null +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -0,0 +1,35 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ClusterWriterWorkflow.cxx + +#include "ITSWorkflow/ClusterWriterWorkflow.h" +#include "ITSWorkflow/ClusterWriterSpec.h" + +namespace o2 +{ +namespace its +{ + +namespace cluster_writer_workflow +{ + +framework::WorkflowSpec getWorkflow(bool useMC) +{ + framework::WorkflowSpec specs; + + specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + + return specs; +} + +} // namespace cluster_writer_workflow +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx index c5af41e8a4575..094b60ec5370d 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx @@ -20,7 +20,7 @@ #include "ITSMFTReconstruction/ClustererParam.h" #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsParameters/GRPObject.h" #include "ITSMFTReconstruction/DigitPixelReader.h" @@ -79,22 +79,24 @@ void ClustererDPL::run(ProcessingContext& pc) auto digits = pc.inputs().get<gsl::span<o2::itsmft::Digit>>("digits"); auto rofs = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>("ROframes"); - std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>> labels; gsl::span<const o2::itsmft::MC2ROFRecord> mc2rofs; + gsl::span<const char> labelbuffer; if (mUseMC) { - labels = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("labels"); + labelbuffer = pc.inputs().get<gsl::span<char>>("labels"); mc2rofs = pc.inputs().get<gsl::span<o2::itsmft::MC2ROFRecord>>("MC2ROframes"); } + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> labels(labelbuffer); LOG(INFO) << "ITSClusterer pulled " << digits.size() << " digits, in " << rofs.size() << " RO frames"; + LOG(INFO) << "ITSClusterer pulled " << labels.getNElements() << " labels "; o2::itsmft::DigitPixelReader reader; reader.setDigits(digits); reader.setROFRecords(rofs); if (mUseMC) { reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.get()); + reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); } reader.init(); auto orig = o2::header::gDataOriginITS; diff --git a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx index e601c49953408..1103157c3adbf 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx @@ -63,8 +63,8 @@ void CookedTrackerDPL::init(InitContext& ic) o2::base::GeometryManager::loadGeometry(); o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::T2G)); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::T2G)); mTracker.setGeometry(geom); double origD[3] = {0., 0., 0.}; @@ -112,14 +112,14 @@ void CookedTrackerDPL::run(ProcessingContext& pc) LOG(INFO) << "ITSCookedTracker pulled " << compClusters.size() << " clusters, in " << rofs.size() << " RO frames"; - o2::dataformats::MCTruthContainer<o2::MCCompLabel> trackLabels; + std::vector<o2::MCCompLabel> trackLabels; if (mUseMC) { mTracker.setMCTruthContainers(labels.get(), &trackLabels); } o2::its::VertexerTraits vertexerTraits; o2::its::Vertexer vertexer(&vertexerTraits); - o2::its::ROframe event(0); + o2::its::ROframe event(0, 7); auto& vertROFvec = pc.outputs().make<std::vector<o2::itsmft::ROFRecord>>(Output{"ITS", "VERTICESROF", 0, Lifetime::Timeframe}); auto& vertices = pc.outputs().make<std::vector<Vertex>>(Output{"ITS", "VERTICES", 0, Lifetime::Timeframe}); diff --git a/Detectors/ITSMFT/ITS/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/DigitReaderSpec.cxx index cc5d7ab29a378..c5c4d7f77170f 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/DigitReaderSpec.cxx @@ -16,11 +16,14 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" +#include "Framework/Logger.h" #include "ITSWorkflow/DigitReaderSpec.h" #include "DataFormatsITSMFT/Digit.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" #include "DataFormatsITSMFT/ROFRecord.h" +#include <cmath> using namespace o2::framework; using namespace o2::itsmft; @@ -58,7 +61,8 @@ void DigitReader::run(ProcessingContext& pc) std::vector<ROFRecord> rofs, *profs = &rofs; treeDig->SetBranchAddress("ITSDigitROF", &profs); - o2::dataformats::MCTruthContainer<o2::MCCompLabel> labels, *plabels = &labels; + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; + std::vector<MC2ROFRecord> mc2rofs, *pmc2rofs = &mc2rofs; if (mUseMC) { treeDig->SetBranchAddress("ITSDigitMCTruth", &plabels); @@ -72,7 +76,9 @@ void DigitReader::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, digits); pc.outputs().snapshot(Output{"ITS", "DIGITSROF", 0, Lifetime::Timeframe}, *profs); if (mUseMC) { - pc.outputs().snapshot(Output{"ITS", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{"ITS", "DIGITSMCTR", 0, Lifetime::Timeframe}); + plabels->copyandflatten(sharedlabels); + delete plabels; pc.outputs().snapshot(Output{"ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe}, *pmc2rofs); } } else { diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index caecf2ea11bdc..1b8cbaa7525e0 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -28,7 +28,7 @@ namespace its namespace reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, o2::gpu::GPUDataTypes::DeviceType dtype, +framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, bool async, o2::gpu::GPUDataTypes::DeviceType dtype, bool upstreamDigits, bool upstreamClusters, bool disableRootOutput, bool eencode) { @@ -45,7 +45,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, o2::gpu::GPUD specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); } if (useCAtracker) { - specs.emplace_back(o2::its::getTrackerSpec(useMC, dtype)); + specs.emplace_back(o2::its::getTrackerSpec(useMC, async, dtype)); } else { specs.emplace_back(o2::its::getCookedTrackerSpec(useMC)); } diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx index 1e2fc141fb06b..f67dce3a96f79 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx @@ -30,7 +30,7 @@ using Vertex = o2::dataformats::Vertex<o2::dataformats::TimeStamp<int>>; template <typename T> using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; -using LabelsType = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; +using LabelsType = std::vector<o2::MCCompLabel>; using ROFRecLblT = std::vector<o2::itsmft::MC2ROFRecord>; using namespace o2::header; diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index d49c94467aee7..166f04419044b 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -35,6 +35,7 @@ #include "ITSReconstruction/FastMultEstConfig.h" #include "ITSReconstruction/FastMultEst.h" +#include <fmt/format.h> namespace o2 { @@ -43,8 +44,7 @@ namespace its { using Vertex = o2::dataformats::Vertex<o2::dataformats::TimeStamp<int>>; -TrackerDPL::TrackerDPL(bool isMC, o2::gpu::GPUDataTypes::DeviceType dType) : mIsMC{isMC}, - mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)} +TrackerDPL::TrackerDPL(bool isMC, bool async, o2::gpu::GPUDataTypes::DeviceType dType) : mIsMC{isMC}, mAsyncMode{async}, mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)} { } @@ -61,15 +61,27 @@ void TrackerDPL::init(InitContext& ic) base::GeometryManager::loadGeometry(); GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(utils::bit2Mask(TransformType::T2L, TransformType::T2GRot, - TransformType::T2G)); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::T2G)); auto* chainITS = mRecChain->AddChain<o2::gpu::GPUChainITS>(); mRecChain->Init(); mVertexer = std::make_unique<Vertexer>(chainITS->GetITSVertexerTraits()); mTracker = std::make_unique<Tracker>(chainITS->GetITSTrackerTraits()); + if (mAsyncMode) { + std::vector<TrackingParameters> trackParams(3); + trackParams[0].TrackletMaxDeltaPhi = 0.05f; + trackParams[1].TrackletMaxDeltaPhi = 0.1f; + trackParams[2].MinTrackLength = 4; + trackParams[2].TrackletMaxDeltaPhi = 0.3; + std::vector<MemoryParameters> memParams(3); + mTracker->setParameters(memParams, trackParams); + LOG(INFO) << "Initializing tracker in async. phase reconstruction with " << trackParams.size() << " passes"; + } mVertexer->getGlobalConfiguration(); - // mVertexer->dumpTraits(); + mTracker->getGlobalConfiguration(); + LOG(INFO) << Form("%ssing lookup table for material budget approximation", (mTracker->isMatLUT() ? "U" : "Not u")); + double origD[3] = {0., 0., 0.}; mTracker->setBz(field->getBz(origD)); } else { @@ -112,15 +124,15 @@ void TrackerDPL::run(ProcessingContext& pc) std::vector<o2::its::TrackITSExt> tracks; auto& allClusIdx = pc.outputs().make<std::vector<int>>(Output{"ITS", "TRACKCLSID", 0, Lifetime::Timeframe}); - o2::dataformats::MCTruthContainer<o2::MCCompLabel> trackLabels; + std::vector<o2::MCCompLabel> trackLabels; auto& allTracks = pc.outputs().make<std::vector<o2::its::TrackITS>>(Output{"ITS", "TRACKS", 0, Lifetime::Timeframe}); - o2::dataformats::MCTruthContainer<o2::MCCompLabel> allTrackLabels; + std::vector<o2::MCCompLabel> allTrackLabels; auto& vertROFvec = pc.outputs().make<std::vector<o2::itsmft::ROFRecord>>(Output{"ITS", "VERTICESROF", 0, Lifetime::Timeframe}); auto& vertices = pc.outputs().make<std::vector<Vertex>>(Output{"ITS", "VERTICES", 0, Lifetime::Timeframe}); std::uint32_t roFrame = 0; - ROframe event(0); + ROframe event(0, 7); bool continuous = mGRP->isDetContinuousReadOut("ITS"); LOG(INFO) << "ITSTracker RO: continuous=" << continuous; @@ -132,10 +144,18 @@ void TrackerDPL::run(ProcessingContext& pc) auto copyTracks = [](auto& tracks, auto& allTracks, auto& allClusIdx, int offset = 0) { for (auto& trc : tracks) { trc.setFirstClusterEntry(allClusIdx.size()); // before adding tracks, create final cluster indices - int ncl = trc.getNumberOfClusters(); - for (int ic = ncl; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! - allClusIdx.push_back(trc.getClusterIndex(ic) + offset); + int ncl = trc.getNumberOfClusters(), nclf = 0; + uint8_t patt = 0; + for (int ic = TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! + auto clid = trc.getClusterIndex(ic); + if (clid >= 0) { + allClusIdx.push_back(clid + offset); + nclf++; + patt |= 0x1 << ic; + } } + assert(ncl == nclf); + trc.setPattern(patt); allTracks.emplace_back(trc); } }; @@ -193,13 +213,13 @@ void TrackerDPL::run(ProcessingContext& pc) tracks.swap(mTracker->getTracks()); LOG(INFO) << "Found tracks: " << tracks.size(); int number = tracks.size(); - trackLabels = mTracker->getTrackLabels(); /// FIXME: assignment ctor is not optimal. - int shiftIdx = -rof.getFirstEntry(); // cluster entry!!! + trackLabels.swap(mTracker->getTrackLabels()); /// FIXME: assignment ctor is not optimal. + int shiftIdx = -rof.getFirstEntry(); // cluster entry!!! rof.setFirstEntry(first); rof.setNEntries(number); copyTracks(tracks, allTracks, allClusIdx, shiftIdx); - allTrackLabels.mergeAtBack(trackLabels); - + std::copy(trackLabels.begin(), trackLabels.end(), std::back_inserter(allTrackLabels)); + trackLabels.clear(); vtxROF.setNEntries(vtxVecLoc.size()); for (const auto& vtx : vtxVecLoc) { vertices.push_back(vtx); @@ -214,7 +234,7 @@ void TrackerDPL::run(ProcessingContext& pc) mTracker->clustersToTracks(event); tracks.swap(mTracker->getTracks()); copyTracks(tracks, allTracks, allClusIdx); - allTrackLabels = mTracker->getTrackLabels(); /// FIXME: assignment ctor is not optimal. + allTrackLabels.swap(mTracker->getTrackLabels()); /// FIXME: assignment ctor is not optimal. } LOG(INFO) << "ITSTracker pushed " << allTracks.size() << " tracks"; @@ -231,7 +251,7 @@ void TrackerDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::GPUDataTypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, bool async, o2::gpu::GPUDataTypes::DeviceType dType) { std::vector<InputSpec> inputs; inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); @@ -257,7 +277,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::GPUDataTypes::DeviceType d "its-tracker", inputs, outputs, - AlgorithmSpec{adaptFromTask<TrackerDPL>(useMC, dType)}, + AlgorithmSpec{adaptFromTask<TrackerDPL>(useMC, async, dType)}, Options{ {"grp-file", VariantType::String, "o2sim_grp.root", {"Name of the grp file"}}, {"its-dictionary-path", VariantType::String, "", {"Path of the cluster-topology dictionary file"}}}}; diff --git a/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx index 02c8b31ac169e..31a3d29dacfed 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx @@ -14,6 +14,7 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" #include "ITSWorkflow/VertexReaderSpec.h" using namespace o2::framework; diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx new file mode 100644 index 0000000000000..92c538b24283a --- /dev/null +++ b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx @@ -0,0 +1,33 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "ITSWorkflow/ClusterWriterWorkflow.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "disable-mc", + o2::framework::VariantType::Bool, + false, + {"disable MC propagation even if available"}}); +} + +#include "Framework/runDataProcessing.h" +#include "Framework/Logger.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + auto useMC = !configcontext.options().get<bool>("disable-mc"); + return std::move(o2::its::cluster_writer_workflow::getWorkflow(useMC)); +} diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx index ff864e259cec4..6ca83b2160247 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx @@ -31,6 +31,7 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"trackerCA", o2::framework::VariantType::Bool, false, {"use trackerCA (default: trackerCM)"}}, + {"async-phase", o2::framework::VariantType::Bool, false, {"perform multiple passes for async. phase reconstruction"}}, {"entropy-encoding", o2::framework::VariantType::Bool, false, {"produce entropy encoded data"}}, {"gpuDevice", o2::framework::VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}}}; @@ -43,6 +44,7 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) // ------------------------------------------------------------------ #include "Framework/runDataProcessing.h" +#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { @@ -53,10 +55,16 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get<bool>("disable-mc"); auto useCAtracker = configcontext.options().get<bool>("trackerCA"); + auto async = configcontext.options().get<bool>("async-phase"); auto gpuDevice = static_cast<o2::gpu::GPUDataTypes::DeviceType>(configcontext.options().get<int>("gpuDevice")); auto extDigits = configcontext.options().get<bool>("digits-from-upstream"); auto extClusters = configcontext.options().get<bool>("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get<bool>("disable-root-output"); auto eencode = configcontext.options().get<bool>("entropy-encoding"); - return std::move(o2::its::reco_workflow::getWorkflow(useMC, useCAtracker, gpuDevice, extDigits, extClusters, disableRootOutput, eencode)); + + if (async && !useCAtracker) { + LOG(ERROR) << "Async.mode is not supported by CookedTracker, use --trackerCA"; + throw std::runtime_error("incompatible options provided"); + } + return std::move(o2::its::reco_workflow::getWorkflow(useMC, useCAtracker, async, gpuDevice, extDigits, extClusters, disableRootOutput, eencode)); } diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h index a710b02723446..59d724b2f886c 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h @@ -35,7 +35,7 @@ namespace mft class GeometryTGeo : public o2::itsmft::GeometryTGeo { public: - typedef o2::Transform3D Mat3D; + typedef o2::math_utils::Transform3D Mat3D; using DetMatrixCache::getMatrixL2G; using DetMatrixCache::getMatrixT2G; using DetMatrixCache::getMatrixT2L; @@ -43,8 +43,9 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static GeometryTGeo* Instance() { // get (create if needed) a unique instance of the object - if (!sInstance) + if (!sInstance) { sInstance = std::unique_ptr<GeometryTGeo>(new GeometryTGeo(true, 0)); + } return sInstance.get(); } @@ -57,9 +58,9 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo // NEVER use it, it will throw exception if the class instance was already // created. Use GeometryTGeo::Instance() instead GeometryTGeo(Bool_t build = kFALSE, Int_t loadTrans = 0 - /*o2::base::utils::bit2Mask(o2::TransformType::T2L, // default transformations to load - o2::TransformType::T2G, - o2::TransformType::L2G)*/ + /*o2::base::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, // default transformations to load + o2::math_utils::TransformType::T2G, + o2::math_utils::TransformType::L2G)*/ ); /// Default destructor diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfDetector.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfDetector.h index 1c6a5ddf8d3a4..8797d4b2e797b 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfDetector.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfDetector.h @@ -18,7 +18,6 @@ #include "TNamed.h" #include "TGeoVolume.h" -#include "MFTBase/PowerSupplyUnit.h" namespace o2 { @@ -51,8 +50,6 @@ class HalfDetector : public TNamed private: HalfSegmentation* mSegmentation; ///< \brief Pointer to the half-MFT segmentation - PowerSupplyUnit* mPSU; - void createHalfDisks(); ClassDefOverride(HalfDetector, 1); diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfSegmentation.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfSegmentation.h index 96a6e2b6e5e61..45a3efd7d6768 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfSegmentation.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/HalfSegmentation.h @@ -52,10 +52,11 @@ class HalfSegmentation : public VSegmentation HalfDiskSegmentation* getHalfDisk(Int_t iDisk) const { - if (iDisk >= 0 && iDisk < mHalfDisks->GetEntries()) + if (iDisk >= 0 && iDisk < mHalfDisks->GetEntries()) { return (HalfDiskSegmentation*)mHalfDisks->At(iDisk); - else + } else { return nullptr; + } } private: diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/HeatExchanger.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/HeatExchanger.h index 6cff3879dc02b..c6794a1311ed3 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/HeatExchanger.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/HeatExchanger.h @@ -42,6 +42,7 @@ class HeatExchanger void createHalfDisk3(Int_t half); void createHalfDisk4(Int_t half); void createManifold(Int_t disk); + void createCoolingPipes(Int_t half, Int_t disk); Double_t getWaterRadius() { return mRWater; } void setWaterRadius(Double_t& Rwater) { mRWater = Rwater; } diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/MFTBaseParam.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/MFTBaseParam.h index 16ed9d3a4d5fb..bd981afebdfcd 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/MFTBaseParam.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/MFTBaseParam.h @@ -34,7 +34,7 @@ struct MFTBaseParam : public o2::conf::ConfigurableParamHelper<MFTBaseParam> { bool buildCone = true; bool buildBarrel = true; bool buildPatchPanel = true; - bool buildPCBSupports = false; + bool buildPCBSupports = true; bool buildPCBs = true; bool buildPSU = true; diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/Support.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/Support.h index b0a97a5ee1269..a61575c759358 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/Support.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/Support.h @@ -136,10 +136,11 @@ auto serialBoolOperation(L&& base, SHAPE&& shape, EL&& elements, OP&& op) localTranslation = new TGeoTranslation(par[3], par[4], par[5]); //The first subtraction needs a shape, the base shape - if (!localCS) + if (!localCS) { localCS = new TGeoCompositeShape(nullptr, compositeOperation(base, localshape, localTranslation, std::forward<OP>(op))); - else + } else { localCS = new TGeoCompositeShape(nullptr, compositeOperation(localCS, localshape, localTranslation, std::forward<OP>(op))); + } } return localCS; } diff --git a/Detectors/ITSMFT/MFT/base/src/Barrel.cxx b/Detectors/ITSMFT/MFT/base/src/Barrel.cxx index 39840d06535a3..c1b4ba90865c5 100644 --- a/Detectors/ITSMFT/MFT/base/src/Barrel.cxx +++ b/Detectors/ITSMFT/MFT/base/src/Barrel.cxx @@ -38,13 +38,13 @@ TGeoVolumeAssembly* Barrel::createBarrel() // TGeoMedium* kMeAl = gGeoManager->GetMedium("MFT_Alu$"); TGeoMedium* mCu = gGeoManager->GetMedium("MFT_Cu$"); - TGeoMedium* mCarbon = gGeoManager->GetMedium("MFT_CarbonFiberM46J"); - TGeoMedium* mRohacell = gGeoManager->GetMedium("MFT_Rohacell"); + TGeoMedium* mCarbon = gGeoManager->GetMedium("MFT_CarbonFiberM46J$"); + TGeoMedium* mRohacell = gGeoManager->GetMedium("MFT_Rohacell$"); TGeoMedium* mKapton = gGeoManager->GetMedium("MFT_Kapton$"); TGeoMedium* mWater = gGeoManager->GetMedium("MFT_Water$"); TGeoMedium* mAir = gGeoManager->GetMedium("MFT_Air$"); - TGeoMedium* mPolypropylene = gGeoManager->GetMedium("MFT_Polypropylene"); - TGeoMedium* mPolyurethane = gGeoManager->GetMedium("MFT_Polyurethane"); + TGeoMedium* mPolypropylene = gGeoManager->GetMedium("MFT_Polypropylene$"); + TGeoMedium* mPolyurethane = gGeoManager->GetMedium("MFT_Polyurethane$"); // define shape of joints TGeoVolume* BarrelJoint0 = gGeoManager->MakeTubs( "Barrel_joint0", kMeAl, 49.600, 49.85, 1.05, 207.2660445, 332.7339555); diff --git a/Detectors/ITSMFT/MFT/base/src/Geometry.cxx b/Detectors/ITSMFT/MFT/base/src/Geometry.cxx index 76b88158cea6d..37e81f9ac3955 100644 --- a/Detectors/ITSMFT/MFT/base/src/Geometry.cxx +++ b/Detectors/ITSMFT/MFT/base/src/Geometry.cxx @@ -97,8 +97,9 @@ Geometry* Geometry::sInstance = nullptr; Geometry* Geometry::instance() { - if (!sInstance) + if (!sInstance) { sInstance = new Geometry(); + } return sInstance; } @@ -125,12 +126,14 @@ void Geometry::build() { // load the detector segmentation - if (!mSegmentation) + if (!mSegmentation) { mSegmentation = new Segmentation(gSystem->ExpandPathName("$(VMCWORKDIR)/Detectors/Geometry/MFT/data/Geometry.xml")); + } // build the geometry - if (!mBuilder) + if (!mBuilder) { mBuilder = new GeometryBuilder(); + } mBuilder->buildGeometry(); delete mBuilder; } @@ -162,8 +165,9 @@ Int_t Geometry::getDiskNSensors(Int_t diskId) const Int_t nSensors = 0; for (int iHalf = 0; iHalf < 2; iHalf++) { HalfDiskSegmentation* diskSeg = mSegmentation->getHalf(iHalf)->getHalfDisk(diskId); - if (diskSeg) + if (diskSeg) { nSensors += diskSeg->getNChips(); + } } return nSensors; } diff --git a/Detectors/ITSMFT/MFT/base/src/GeometryBuilder.cxx b/Detectors/ITSMFT/MFT/base/src/GeometryBuilder.cxx index 7185b5a7790d8..7608c734e6a69 100644 --- a/Detectors/ITSMFT/MFT/base/src/GeometryBuilder.cxx +++ b/Detectors/ITSMFT/MFT/base/src/GeometryBuilder.cxx @@ -22,6 +22,7 @@ #include "MFTBase/Barrel.h" #include "MFTBase/PatchPanel.h" #include "MFTBase/MFTBaseParam.h" +#include "MFTBase/PowerSupplyUnit.h" #include "TGeoVolume.h" #include "TGeoManager.h" @@ -60,7 +61,18 @@ void GeometryBuilder::buildGeometry() delete halfMFT; } - /// \todo Add the service, Barrel, etc Those objects will probably be defined into the COMMON ITSMFT area. + // Add the service, Barrel, etc + if (!mftBaseParam.minimal && mftBaseParam.buildPSU) { + auto* mPSU = new PowerSupplyUnit(); + TGeoVolumeAssembly* mHalfPSU = mPSU->create(); + TGeoTranslation* t_PSU = new TGeoTranslation("t_PSU", 0, 0.0, -72.6); + t_PSU->RegisterYourself(); + auto* r_PSU = new TGeoRotation("rotation_PSU", 0.0, 0.0, 180.0); + auto* p_PSU = new TGeoCombiTrans(*t_PSU, *r_PSU); + volMFT->AddNode(mHalfPSU, 1, t_PSU); + volMFT->AddNode(mHalfPSU, 2, p_PSU); + } + if (!mftBaseParam.minimal && mftBaseParam.buildCone) { auto* halfCone = new HalfCone(); TGeoVolumeAssembly* halfCone1 = halfCone->createHalfCone(0); diff --git a/Detectors/ITSMFT/MFT/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/MFT/base/src/GeometryTGeo.cxx index 0fe8dea7a0639..1757e32facfff 100644 --- a/Detectors/ITSMFT/MFT/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/MFT/base/src/GeometryTGeo.cxx @@ -18,7 +18,7 @@ #include "MFTBase/GeometryTGeo.h" #include "DetectorsBase/GeometryManager.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "FairLogger.h" // for LOG @@ -43,7 +43,6 @@ using namespace TMath; using namespace o2::mft; using namespace o2::detectors; -using namespace o2::utils; using AlpideSegmentation = o2::itsmft::SegmentationAlpide; @@ -132,8 +131,9 @@ void GeometryTGeo::Build(Int_t loadTrans) mLadderId2Index[j].resize(numberOfLadders + 1); Int_t nL = 0; for (Int_t nSensor = MinSensorsPerLadder; nSensor <= MaxSensorsPerLadder; nSensor++) { - if (mNumberOfLadders[j][nSensor] == 0) + if (mNumberOfLadders[j][nSensor] == 0) { continue; + } Int_t n = extractNumberOfLadders(i, j, nSensor, nL); } // nSensor @@ -414,10 +414,10 @@ void GeometryTGeo::fillMatrixCache(Int_t mask) Build(mask); return; } - // LOG(INFO) << "mask " << mask << " o2::utils::bit2Mask " << o2::utils::bit2Mask(o2::TransformType::L2G) << + // LOG(INFO) << "mask " << mask << " o2::math_utils::bit2Mask " << o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G) << // FairLogger::endl; // build matrices - if ((mask & o2::utils::bit2Mask(o2::TransformType::L2G)) && !getCacheL2G().isFilled()) { + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) && !getCacheL2G().isFilled()) { LOG(INFO) << "Loading MFT L2G matrices from TGeo"; auto& cacheL2G = getCacheL2G(); cacheL2G.setSize(mSize); @@ -427,7 +427,7 @@ void GeometryTGeo::fillMatrixCache(Int_t mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2L)) && !getCacheT2L().isFilled()) { + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) && !getCacheT2L().isFilled()) { // matrices for Tracking to Local frame transformation LOG(INFO) << "Loading MFT T2L matrices from TGeo"; auto& cacheT2L = getCacheT2L(); @@ -438,7 +438,7 @@ void GeometryTGeo::fillMatrixCache(Int_t mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2G)) && !getCacheT2G().isFilled()) { + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) && !getCacheT2G().isFilled()) { // matrices for Tracking to Global frame transformation LOG(INFO) << "Loading MFT T2G matrices from TGeo"; auto& cacheT2G = getCacheT2G(); @@ -474,8 +474,9 @@ void GeometryTGeo::extractSensorXAlpha(int index, float& x, float& alpha) {} //__________________________________________________________________________ Bool_t GeometryTGeo::getSensorID(Int_t index, Int_t& half, Int_t& disk, Int_t& ladder, Int_t& sensor) const { - if (index < 0 || index >= mTotalNumberOfSensors) + if (index < 0 || index >= mTotalNumberOfSensors) { return kFALSE; + } half = index / (mTotalNumberOfSensors / mNumberOfHalves); index = index % (mTotalNumberOfSensors / mNumberOfHalves); diff --git a/Detectors/ITSMFT/MFT/base/src/HalfCone.cxx b/Detectors/ITSMFT/MFT/base/src/HalfCone.cxx index 64712be2d2c9b..7b101838470e8 100644 --- a/Detectors/ITSMFT/MFT/base/src/HalfCone.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HalfCone.cxx @@ -12,22 +12,24 @@ /// \brief Class building geometry of one half of one MFT half-cone /// \author sbest@pucp.pe, eric.endress@gmx.de, franck.manso@clermont.in2p3.fr /// \Carlos csoncco@pucp.edu.pe -/// \date 13/03/2019 +/// \date 01/07/2020 -#include "TGeoManager.h" -#include "TGeoMatrix.h" -#include "TGeoManager.h" +#include "TGeoBBox.h" +#include "TGeoBoolNode.h" #include "TGeoCompositeShape.h" -#include "TGeoShape.h" #include "TGeoCone.h" -#include "TGeoVolume.h" +#include "TGeoManager.h" #include "TGeoMaterial.h" +#include "TGeoMatrix.h" #include "TGeoMedium.h" -#include "TGeoTube.h" +#include "TGeoShape.h" #include "TGeoTrd1.h" -#include "TMath.h" +#include "TGeoTube.h" +#include "TGeoVolume.h" #include "TGeoXtru.h" +#include "TMath.h" +#include "MFTBase/Constants.h" #include "MFTBase/HalfCone.h" using namespace o2::mft; @@ -50,7 +52,35 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) auto* HalfConeVolume = new TGeoVolumeAssembly("HalfConeVolume"); + TGeoElement* Silicon = new TGeoElement("Silicon", "Silicon", 14, 28.0855); + TGeoElement* Iron = new TGeoElement("Iron", "Iron", 26, 55.845); + TGeoElement* Copper = new TGeoElement("Copper", "Copper", 29, 63.546); + TGeoElement* Manganese = + new TGeoElement("Manganese", "Manganese", 25, 54.938049); + TGeoElement* Magnesium = + new TGeoElement("Magnesium", "Magnesium", 12, 24.3050); + TGeoElement* Zinc = new TGeoElement("Zinc", "Zinc", 30, 65.39); + TGeoElement* Titanium = new TGeoElement("Titanium", "Titanium", 22, 47.867); + TGeoElement* Chromium = new TGeoElement("Chromium", "Chromium", 24, 51.9961); + TGeoElement* Aluminum = new TGeoElement("Aluminum", "Aluminum", 13, 26981538); + + TGeoMixture* alu5083 = new TGeoMixture("alu5083", 9, 2.650); // g/cm3 + alu5083->AddElement(Iron, 0.004); + alu5083->AddElement(Silicon, 0.004); + alu5083->AddElement(Copper, 0.001); + alu5083->AddElement(Manganese, 0.002); + alu5083->AddElement(Magnesium, 0.0025); + alu5083->AddElement(Zinc, 0.0025); + alu5083->AddElement(Titanium, 0.0015); + alu5083->AddElement(Chromium, 0.0010); + alu5083->AddElement(Aluminum, 0.9815); + // alu5083->SetTemperature(); // 25. + alu5083->SetDensity(2.65); // g/cm3 + alu5083->SetState(TGeoMaterial::kMatStateSolid); + TGeoMedium* kMedAlu = gGeoManager->GetMedium("MFT_Alu$"); + TGeoMedium* malu5083 = + new TGeoMedium("malu5083", 2, alu5083); // name, numer medio, material // Rotation TGeoRotation* rot1 = new TGeoRotation("rot1", 180, -180, 0); @@ -68,7 +98,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoRotation* rot_base = new TGeoRotation("rot_base", 180, 180, 0); // rail_r rot_base->RegisterYourself(); - TGeoCombiTrans* combi1 = new TGeoCombiTrans(0, -10.3, 1.29, rot1); // y=-10.80 belt + TGeoCombiTrans* combi1 = + new TGeoCombiTrans(0, -10.3, 1.29, rot1); // y=-10.80 belt combi1->RegisterYourself(); TGeoCombiTrans* combi2 = new TGeoCombiTrans(-16.8, 0., 0., rot2); combi2->RegisterYourself(); @@ -96,7 +127,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoCombiTrans* acombi_1h_mb0 = new TGeoCombiTrans(5.2, 0, 0, rot_1hole_mb0); acombi_1h_mb0->SetName("acombi_1h_mb0"); acombi_1h_mb0->RegisterYourself(); - TGeoCombiTrans* bcombi_1h_mb0 = new TGeoCombiTrans(-5.2, 0, 0, rot_1hole_mb0); //y= + TGeoCombiTrans* bcombi_1h_mb0 = + new TGeoCombiTrans(-5.2, 0, 0, rot_1hole_mb0); // y= bcombi_1h_mb0->SetName("bcombi_1h_mb0"); bcombi_1h_mb0->RegisterYourself(); @@ -109,31 +141,53 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) rot_2hole_mb0->SetName("rot_2hole_mb0"); rot_2hole_mb0->RegisterYourself(); - TGeoCombiTrans* combi_2hole_mb0 = new TGeoCombiTrans(6.7, 0, 0, rot_2hole_mb0); + TGeoCombiTrans* combi_2hole_mb0 = + new TGeoCombiTrans(6.7, 0, 0, rot_2hole_mb0); combi_2hole_mb0->SetName("combi_2hole_mb0"); combi_2hole_mb0->RegisterYourself(); - TGeoCombiTrans* combi_2hole_mb0_b = new TGeoCombiTrans(-6.7, 0, 0, rot_2hole_mb0); //y= + TGeoCombiTrans* combi_2hole_mb0_b = + new TGeoCombiTrans(-6.7, 0, 0, rot_2hole_mb0); // y= combi_2hole_mb0_b->SetName("combi_2hole_mb0_b"); combi_2hole_mb0_b->RegisterYourself(); // shape for cross_mb0.. - // TGeoShape* box_mb0 = new TGeoBBox("s_box_mb0", x_boxmb0 / 2, y_boxmb0 / 2, z_boxmb0 / 2); + // TGeoShape* box_mb0 = new TGeoBBox("s_box_mb0", x_boxmb0 / 2, y_boxmb0 / 2, + // z_boxmb0 / 2); new TGeoBBox("box_mb0", x_boxmb0 / 2, y_boxmb0 / 2, z_boxmb0 / 2); new TGeoTube("hole1_mb0", radin_1hmb0, radout_1hmb0, high_1hmb0 / 2); new TGeoTube("hole2_mb0", radin_2hmb0, radout_2hmb0, high_2hmb0 / 2); + /// composite shape for mb0 - ///composite shape for mb0 - - auto* c_mb0_Shape_0 = new TGeoCompositeShape("c_mb0_Shape_0", "box_mb0 - hole1_mb0:acombi_1h_mb0 - hole1_mb0:bcombi_1h_mb0 - hole2_mb0:combi_2hole_mb0 - hole2_mb0:combi_2hole_mb0_b"); + auto* c_mb0_Shape_0 = new TGeoCompositeShape( + "c_mb0_Shape_0", + "box_mb0 - hole2_mb0:combi_2hole_mb0 - " + "hole2_mb0:combi_2hole_mb0_b"); //- hole1_mb0:acombi_1h_mb0 - + // hole1_mb0:bcombi_1h_mb0 - auto* cross_mb0_Volume = new TGeoVolume("cross_mb0_Volume", c_mb0_Shape_0, kMedAlu); + /// auto* cross_mb0_Volume = new TGeoVolume("cross_mb0_Volume", c_mb0_Shape_0, + /// kMedAlu); + auto* cross_mb0_Volume = + new TGeoVolume("cross_mb0_Volume", c_mb0_Shape_0, malu5083); Cross_mb0->AddNode(cross_mb0_Volume, 1); // 2nd piece cross beam MFT (cbeam) auto* Cross_mft = new TGeoVolumeAssembly("Cross_mft"); + auto* Cross_mft_2 = new TGeoVolumeAssembly("Cross_mft_2"); + auto* Cross_mft_3 = new TGeoVolumeAssembly("Cross_mft_3"); + auto* Cross_mft_4 = new TGeoVolumeAssembly("Cross_mft_4"); + + // 2020_cross_beam + // tub-main + Double_t radin_cb = 0.; + Double_t radout_cb = 0.3; + Double_t high_cb = 14.2; + + TGeoRotation* rot_cb = new TGeoRotation("rot_cb", 90, 90, 0); + rot_cb->SetName("rot_cb"); + rot_cb->RegisterYourself(); // using the same "box" of the 1 piece // 2hole coaxial @@ -143,24 +197,45 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) // same rotation in tub aximatAl "rot_2hole_mb0" - TGeoCombiTrans* combi_hole_1cbeam = new TGeoCombiTrans(6.8, 0, 0, rot_2hole_mb0); + TGeoCombiTrans* combi_hole_1cbeam = + new TGeoCombiTrans(6.8, 0, 0, rot_2hole_mb0); combi_hole_1cbeam->SetName("combi_hole_1cbeam"); combi_hole_1cbeam->RegisterYourself(); - TGeoCombiTrans* combi_hole_2cbeam = new TGeoCombiTrans(-6.8, 0, 0, rot_2hole_mb0); + TGeoCombiTrans* combi_hole_2cbeam = + new TGeoCombiTrans(-6.8, 0, 0, rot_2hole_mb0); combi_hole_2cbeam->SetName("combi_hole_2cbeam"); combi_hole_2cbeam->RegisterYourself(); // shape for shape cross beam - new TGeoTube("hole_cbeam", radin_hole_cbeam, radout_hole_cbeam, high_hole_cbeam / 2); + new TGeoTube("hole_cbeam", radin_hole_cbeam, radout_hole_cbeam, + high_hole_cbeam / 2); + new TGeoTube("s_cb", radin_cb, radout_cb, high_cb / 2); // new // composite shape for cross beam (using the same box of mb0) - auto* c_cbeam_Shape = new TGeoCompositeShape("c_cbeam_Shape", "box_mb0 - hole_cbeam:combi_hole_1cbeam - hole_cbeam:combi_hole_2cbeam"); - - auto* Cross_mft_Volume = new TGeoVolume("Cross_mft_Volume", c_cbeam_Shape, kMedAlu); + /// auto* c_cbeam_Shape = new TGeoCompositeShape("c_cbeam_Shape", "box_mb0 - + /// hole_cbeam:combi_hole_1cbeam - hole_cbeam:combi_hole_2cbeam"); + auto* c_cbeam_Shape = new TGeoCompositeShape( + "c_cbeam_Shape", + " s_cb:rot_cb - hole_cbeam:combi_hole_1cbeam - " + "hole_cbeam:combi_hole_2cbeam"); + + /// auto* Cross_mft_Volume = new TGeoVolume("Cross_mft_Volume", c_cbeam_Shape, + /// kMedAlu); + auto* Cross_mft_Volume = + new TGeoVolume("Cross_mft_Volume", c_cbeam_Shape, malu5083); + auto* Cross_mft_Volume_2 = + new TGeoVolume("Cross_mft_Volume_2", c_cbeam_Shape, malu5083); + auto* Cross_mft_Volume_3 = + new TGeoVolume("Cross_mft_Volume_3", c_cbeam_Shape, malu5083); + auto* Cross_mft_Volume_4 = + new TGeoVolume("Cross_mft_Volume_4", c_cbeam_Shape, malu5083); Cross_mft->AddNode(Cross_mft_Volume, 1); + Cross_mft_2->AddNode(Cross_mft_Volume_2, 1); + Cross_mft_3->AddNode(Cross_mft_Volume_3, 1); + Cross_mft_4->AddNode(Cross_mft_Volume_4, 1); // 3th piece Framework front @@ -168,6 +243,69 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) auto* Fra_front_L = new TGeoVolumeAssembly("Fra_front_L"); auto* Fra_front_R = new TGeoVolumeAssembly("Fra_front_R"); + // new fra_front seg tub + Double_t radin_fwf = 15.2; + Double_t radout_fwf = 15.9; + Double_t high_fwf = 0.6; + Double_t ang_in_fwf = 180. + 21.0; // 21.44 + Double_t ang_fin_fwf = 180 + 60.21; // + + // box to quit -inferior part + Double_t x_box_qdown = 1.6; + Double_t y_box_qdown = 1.6; + Double_t z_box_qdown = 0.65; + + TGeoTranslation* tr_qdown = + new TGeoTranslation("tr_qdown", -7.1, -13.7985, 0.); + tr_qdown->RegisterYourself(); + + // box to add -inferior part + Double_t x_box_addown = 1.83; + Double_t y_box_addown = 0.6; + Double_t z_box_addown = 0.6; + + TGeoTranslation* tr_addown = + new TGeoTranslation("tr_addown", -8.015, -12.6985, 0.); + tr_addown->RegisterYourself(); + //// + TGeoXtru* tria_fwf = new TGeoXtru(2); + tria_fwf->SetName("tria_fwf"); + + Double_t x_tria_fwf[3] = {-13.2, -10.3, -10.3}; + Double_t y_tria_fwf[3] = {-8.0, -11.4, -8.0}; + tria_fwf->DefinePolygon(3, x_tria_fwf, y_tria_fwf); + tria_fwf->DefineSection(0, -0.3, 0., 0., 1); + tria_fwf->DefineSection(1, 0.3, 0., 0., 1); + ////////// + TGeoXtru* top_adfwf = new TGeoXtru(2); + top_adfwf->SetName("top_adfwf"); + + Double_t x_top_adfwf[4] = {-14.8, -14.2, -14.2, -14.8}; + Double_t y_top_adfwf[4] = {-3.6 - 0.12, -3.6 - 0.12, -5.56, + -5.83}; // 5.52 , 5.9 --5.83 + top_adfwf->DefinePolygon(4, x_top_adfwf, y_top_adfwf); + top_adfwf->DefineSection(0, -0.3, 0., 0., + 1); //(plane,-zplane/ +zplane, x0, y0,(x/y)) + top_adfwf->DefineSection(1, 0.3, 0., 0., 1); + + // box to quit up part + Double_t x_q_upbox = 0.4; + Double_t y_q_upbox = 4.; + Double_t z_q_upbox = 1.; + + TGeoTranslation* tr_q_upbox = + new TGeoTranslation("tr_q_upbox", -14.8 - 0.2, -3.6 - 0.6, 0.); + tr_q_upbox->RegisterYourself(); + + TGeoRotation* rot_180yR = + new TGeoRotation("rot_180yR", 180, -180, 0); // half0 + rot_180yR->RegisterYourself(); + TGeoCombiTrans* combi_fwb_R = new TGeoCombiTrans(0, 0, 0, rot_180yR); + combi_fwb_R->SetName("combi_fwb_R"); + combi_fwb_R->RegisterYourself(); + + /////////////////////////////////////////// + Double_t x_box_up = 0.6; // cm Double_t y_box_up = 0.605; Double_t z_box_up = 2.84; @@ -218,11 +356,20 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) tr_tubdown->RegisterYourself(); // shape for framewor front + auto* fwf_tub = new TGeoTubeSeg("fwf_tub", radin_fwf, radout_fwf, + high_fwf / 2, ang_in_fwf, ang_fin_fwf); + auto* box_qdown = new TGeoBBox("box_qdown", x_box_qdown / 2, y_box_qdown / 2, + z_box_qdown / 2); + auto* box_addown = new TGeoBBox("box_addown", x_box_addown / 2, + y_box_addown / 2, z_box_addown / 2); + auto* q_upbox = + new TGeoBBox("q_upbox", x_q_upbox / 2, y_q_upbox / 2, z_q_upbox / 2); new TGeoBBox("box_up", x_box_up / 2, y_box_up / 2, z_box_up / 2); new TGeoTube("tub_up", 0., dia_tub_up / 2, high_tub_up / 2); // - new TGeoTubeSeg("seg_tub", radin_segtub, radout_segtub, high_segtub / 2, ang_in_segtub, ang_fin_segtub); + new TGeoTubeSeg("seg_tub", radin_segtub, radout_segtub, high_segtub / 2, + ang_in_segtub, ang_fin_segtub); new TGeoBBox("boxB_down", x_boxB_down / 2, y_boxB_down / 2, z_boxB_down / 2); @@ -231,32 +378,74 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) new TGeoTube("tubdown", 0., dia_tubdown / 2, high_tubdown / 2); // Composite shapes for Fra_front - new TGeoCompositeShape("fra_front_Shape_0", "box_up:tr1_up + seg_tub:combi_3b + boxB_down:tr3_box + boxA_down:tr_2_box"); - auto* fra_front_Shape_1 = new TGeoCompositeShape("fra_front_Shape_1", "fra_front_Shape_0 - tubdown:tr_tubdown - tub_up:combi_3a"); + new TGeoCompositeShape("fra_front_Shape_0", + "box_up:tr1_up + seg_tub:combi_3b + boxB_down:tr3_box " + "+ boxA_down:tr_2_box"); + + auto* fra_front_Shape_1 = new TGeoCompositeShape( + "fra_front_Shape_1", + "fra_front_Shape_0 - tubdown:tr_tubdown - tub_up:combi_3a"); - TGeoRotation* rot_z180x90 = new TGeoRotation("rot_z180x90", 180, 90, 0); //half0_R + TGeoRotation* rot_z180x90 = + new TGeoRotation("rot_z180x90", 180, 90, 0); // half0_R rot_z180x90->RegisterYourself(); - TGeoRotation* rot_halfR = new TGeoRotation("rot_halfR", 180, 180, 0); // half0_R + TGeoRotation* rot_halfR = + new TGeoRotation("rot_halfR", 180, 180, 0); // half0_R rot_halfR->RegisterYourself(); - TGeoCombiTrans* combi_front_L = new TGeoCombiTrans(-7.1, -16.2, 32.5 + 0.675, rot_90x); // x=7.35, y=0, z=15.79 + /// TGeoCombiTrans* combi_front_L = new TGeoCombiTrans(-7.1, -16.2, 32.5 + + /// 0.675, rot_90x); // x=7.35, y=0, z=15.79 + TGeoCombiTrans* combi_front_L = + new TGeoCombiTrans(-7.1, -16.2, 32.5 + 0.675, rot_90x); + combi_front_L->SetName("combi_front_L"); combi_front_L->RegisterYourself(); - TGeoCombiTrans* combi_front_R = new TGeoCombiTrans(7.1, -16.2, 32.5 + 0.675, rot_z180x90); //x=7.35, y=0, z=15.79 + TGeoTranslation* tr_ff = new TGeoTranslation( + "tr_ff", 0, -2.5 - 0.31, 32.5 + 0.675); // 7.1 , -16.2 z32.5 + tr_ff->RegisterYourself(); + // TGeoRotation *rot_180yR = new TGeoRotation("rot_180yR", 180,-180,0); // + // half0 rot_180yR->RegisterYourself(); + + // TGeoCombiTrans* combi_front_R = new TGeoCombiTrans(7.1, -16.2, 32.5 + + // 0.675, rot_z180x90); //x=7.35, y=0, z=15.79 + TGeoCombiTrans* combi_front_R = + new TGeoCombiTrans(0, -2.5 - 0.31, 32.5 + 0.675, rot_180yR); combi_front_R->SetName("combi_front_R"); combi_front_R->RegisterYourself(); - // auto * fra_front_Shape_3 = new TGeoCompositeShape("fra_front_Shape_3","fra_front_Shape_2:rot_halfR "); + auto* fra_front_Shape_2 = new TGeoCompositeShape( + "Fra_front_Shape_2", + "fwf_tub - box_qdown:tr_qdown + box_addown:tr_addown + tria_fwf + " + "top_adfwf - q_upbox:tr_q_upbox"); + + // auto * fra_front_Shape_3 = new + // TGeoCompositeShape("Fra_front_Shape_3","Fra_front_Shape_2 + + // Fra_front_Shape_2:combi_front_R "); - auto* Fra_front_Volume = new TGeoVolume("Fra_front_Volume", fra_front_Shape_1, kMedAlu); + auto* fra_front_Shape_3 = new TGeoCompositeShape( + "Fra_front_Shape_3", "Fra_front_Shape_2 + Fra_front_Shape_2:rot_180yR"); - Fra_front_L->AddNode(Fra_front_Volume, 1, combi_front_L); - Fra_front_R->AddNode(Fra_front_Volume, 1, combi_front_R); + // auto * fra_front_Shape_3 = new + // TGeoCompositeShape("fra_front_Shape_3","fra_front_Shape_2:rot_halfR "); - Fra_front->AddNode(Fra_front_L, 1); - Fra_front->AddNode(Fra_front_R, 2); + /// auto* Fra_front_Volume = new TGeoVolume("Fra_front_Volume", + /// fra_front_Shape_1, kMedAlu); + auto* Fra_front_Volume_R = + new TGeoVolume("Fra_front_Volume_R", fra_front_Shape_2, malu5083); + auto* Fra_front_Volume_L = + new TGeoVolume("Fra_front_Volume_L", fra_front_Shape_2, malu5083); + + auto* Fra_front_Volume_RL = + new TGeoVolume("Fra_front_Volume_RL", fra_front_Shape_3, malu5083); + + // Fra_front_L->AddNode(Fra_front_Volume, 1, tr_ff); + // Fra_front_R->AddNode(Fra_front_Volume, 1, combi_front_R); + + // Fra_front->AddNode(Fra_front_L, 1); + // Fra_front->AddNode(Fra_front_R, 2); + Fra_front->AddNode(Fra_front_Volume_RL, 1, tr_ff); // 4th piece "BASE" framework half support @@ -314,20 +503,23 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t yc_box = 5.772; Double_t zc_box = 1.4; - TGeoTranslation* tr_cbox = new TGeoTranslation("tr_cbox", -xc_box / 2, -radout_disc + 0.888, 0); + TGeoTranslation* tr_cbox = + new TGeoTranslation("tr_cbox", -xc_box / 2, -radout_disc + 0.888, 0); tr_cbox->RegisterYourself(); // box 4 lamine 1 Double_t x_labox = 60.0; Double_t y_labox = 30.3; Double_t z_labox = 0.305; - TGeoTranslation* tr_la = new TGeoTranslation("tr_la", 0, -y_labox / 2 - 9.3, high_disc / 2); + TGeoTranslation* tr_la = + new TGeoTranslation("tr_la", 0, -y_labox / 2 - 9.3, high_disc / 2); tr_la->RegisterYourself(); // box 5 lamin 2 Double_t x_2labox = 51.2; Double_t y_2labox = 2.8; // C-B Double_t z_2labox = 0.303; - TGeoTranslation* tr_2la = new TGeoTranslation("tr_2la", 0, -8.1, high_disc / 2); // + TGeoTranslation* tr_2la = + new TGeoTranslation("tr_2la", 0, -8.1, high_disc / 2); // tr_2la->RegisterYourself(); // circular border C SEG_BORD @@ -337,57 +529,67 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t high_bord = 1.355; // 13.5 Double_t ang_in_bord = 0; Double_t ang_fin_bord = 90; - // TGeoRotation *rot_bord1 = new TGeoRotation("rot_bord1", ang_in_1hole +0.0167,0,0); + // TGeoRotation* rot_bord1 = new TGeoRotation("rot_bord1", ang_in_1hole + // +0.0167,0,0); TGeoRotation* rot1_bord1 = new TGeoRotation("rot1_bord1", 14.8, 0, 0); rot1_bord1->RegisterYourself(); - TGeoCombiTrans* combi_bord1 = new TGeoCombiTrans(-26.7995, -13.0215, 0, rot1_bord1); // y= + TGeoCombiTrans* combi_bord1 = + new TGeoCombiTrans(-26.7995, -13.0215, 0, rot1_bord1); // y= combi_bord1->SetName("combi_bord1"); combi_bord1->RegisterYourself(); TGeoRotation* rot2_bord1 = new TGeoRotation("rot2_bord1", -50, 0, 0); rot2_bord1->RegisterYourself(); - TGeoCombiTrans* combi2_bord1 = new TGeoCombiTrans(-21.3795, -20.7636, 0, rot2_bord1); // y= + TGeoCombiTrans* combi2_bord1 = + new TGeoCombiTrans(-21.3795, -20.7636, 0, rot2_bord1); // y= combi2_bord1->SetName("combi2_bord1"); combi2_bord1->RegisterYourself(); // TGeoRotation* rot1_bord2 = new TGeoRotation("rot1_bord2", 250, 0, 0); rot1_bord2->RegisterYourself(); - TGeoCombiTrans* combi1_bord2 = new TGeoCombiTrans(-9.0527, -23.3006, 0, rot1_bord2); // y= + TGeoCombiTrans* combi1_bord2 = + new TGeoCombiTrans(-9.0527, -23.3006, 0, rot1_bord2); // y= combi1_bord2->SetName("combi1_bord2"); combi1_bord2->RegisterYourself(); // e|°____°| TGeoRotation* rot_cent_bord = new TGeoRotation("rot_cent_bord", 90, 0, 0); rot_cent_bord->RegisterYourself(); - TGeoCombiTrans* combi_cent_bord = new TGeoCombiTrans(-6.5, -27.094, 0, rot_cent_bord); // y= + TGeoCombiTrans* combi_cent_bord = + new TGeoCombiTrans(-6.5, -27.094, 0, rot_cent_bord); // y= combi_cent_bord->SetName("combi_cent_bord"); combi_cent_bord->RegisterYourself(); // box tonge Double_t x_tong = 2.0; - Double_t y_tong = 2.81; + Double_t y_tong = 1.5; Double_t z_tong = 1.35; - TGeoTranslation* tr_tong = new TGeoTranslation("tr_tong", 0, -28.6, 0); // + TGeoTranslation* tr_tong = new TGeoTranslation("tr_tong", 0, -26.75, 0); // tr_tong->RegisterYourself(); // circular central hole1 to conexion with other parts Double_t radin_hole1 = 0; Double_t radout_hole1 = 0.4; Double_t high_hole1 = 1.36; - TGeoTranslation* tr_hole1 = new TGeoTranslation("tr_hole1", 0, -28.0, 0); // tonge + TGeoTranslation* tr_hole1 = + new TGeoTranslation("tr_hole1", 0, -28.0, 0); // tonge tr_hole1->RegisterYourself(); - TGeoTranslation* tr2_hole1 = new TGeoTranslation("tr2_hole1", -26.5, -8.5, 0); // left + TGeoTranslation* tr2_hole1 = + new TGeoTranslation("tr2_hole1", -26.5, -8.5, 0); // left tr2_hole1->RegisterYourself(); - TGeoTranslation* tr3_hole1 = new TGeoTranslation("tr3_hole1", 26.5, -8.5, 0); // right + TGeoTranslation* tr3_hole1 = + new TGeoTranslation("tr3_hole1", 26.5, -8.5, 0); // right tr3_hole1->RegisterYourself(); // circular hole2 ; hole2 r=6.7 Double_t radin_hole2 = 0; - Double_t radout_hole2 = 0.335; // diameter 6.7 - Double_t high_hole2 = 1.36; // 13.5 - TGeoTranslation* tr1_hole2 = new TGeoTranslation("tr1_hole2", -28.0, -8.5, 0); // + Double_t radout_hole2 = 0.335; // diameter 6.7 + Double_t high_hole2 = 1.36; // 13.5 + TGeoTranslation* tr1_hole2 = + new TGeoTranslation("tr1_hole2", -28.0, -8.5, 0); // tr1_hole2->RegisterYourself(); - TGeoTranslation* tr2_hole2 = new TGeoTranslation("tr2_hole2", 28.0, -8.5, 0); // + TGeoTranslation* tr2_hole2 = + new TGeoTranslation("tr2_hole2", 28.0, -8.5, 0); // tr2_hole2->RegisterYourself(); //////////// hole "0" two tubs together @@ -398,10 +600,19 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t radin_T2 = 0; Double_t radout_T2 = 1.1; Double_t high_T2 = 1.2; // dz 6 + // seg tong xy + Double_t radin_ccut = 27.5; + Double_t radout_ccut = 29.; // 304 + Double_t high_ccut = 1.4; /// 13.5 + Double_t ang_in_ccut = 260; + Double_t ang_fin_ccut = 280; // shape for base - new TGeoTubeSeg("disc", radin_disc, radout_disc, high_disc / 2, ang_in_disc, ang_fin_disc); + new TGeoTubeSeg("disc", radin_disc, radout_disc, high_disc / 2, ang_in_disc, + ang_fin_disc); + new TGeoTubeSeg("c_cut", radin_ccut, radout_ccut, high_ccut / 2, ang_in_ccut, + ang_fin_ccut); new TGeoBBox("box1", x_1box / 2, y_1box / 2, z_1box / 2); new TGeoBBox("box2", x_2box / 2, y_2box / 2, z_2box / 2); @@ -411,10 +622,14 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) new TGeoBBox("cbox", xc_box / 2, yc_box / 2, zc_box / 2); new TGeoBBox("tongbox", x_tong / 2, y_tong / 2, z_tong / 2); - new TGeoTubeSeg("seg_1hole", radin_1hole, radout_1hole, high_1hole / 2, ang_in_1hole, ang_fin_1hole); // r_in,r_out,dZ,ang,ang - new TGeoTubeSeg("seg_2hole", radin_2hole, radout_2hole, high_2hole / 2, ang_in_2hole, ang_fin_2hole); - new TGeoTubeSeg("seg_3hole", radin_3hole, radout_3hole, high_3hole / 2, ang_in_3hole, ang_fin_3hole); // y|u| - new TGeoTubeSeg("seg_bord", radin_bord, radout_bord, high_bord / 2, ang_in_bord, ang_fin_bord); + new TGeoTubeSeg("seg_1hole", radin_1hole, radout_1hole, high_1hole / 2, + ang_in_1hole, ang_fin_1hole); // r_in,r_out,dZ,ang,ang + new TGeoTubeSeg("seg_2hole", radin_2hole, radout_2hole, high_2hole / 2, + ang_in_2hole, ang_fin_2hole); + new TGeoTubeSeg("seg_3hole", radin_3hole, radout_3hole, high_3hole / 2, + ang_in_3hole, ang_fin_3hole); // y|u| + new TGeoTubeSeg("seg_bord", radin_bord, radout_bord, high_bord / 2, + ang_in_bord, ang_fin_bord); new TGeoTube("circ_hole1", radin_hole1, radout_hole1, high_hole1 / 2); @@ -424,125 +639,219 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) // composite shape for base - new TGeoCompositeShape("base_Shape_0", " disc - box1 - box2 - box3 - circ_holeB:tr1_holeB - circ_holeB:tr2_holeB"); - new TGeoCompositeShape("base_Shape_1", "(seg_1hole - seg_bord:combi_bord1 - seg_bord:combi2_bord1) + seg_2hole -seg_bord:combi1_bord2 + cbox:tr_cbox"); + // new TGeoCompositeShape("base_Shape_0", " disc - box1 - box2 - box3 - + // circ_holeB:tr1_holeB - circ_holeB:tr2_holeB"); + new TGeoCompositeShape("base_Shape_0", " disc - box1 - box2 - box3"); + new TGeoCompositeShape( + "base_Shape_1", + "(seg_1hole - seg_bord:combi_bord1 - seg_bord:combi2_bord1) + seg_2hole " + "-seg_bord:combi1_bord2 + cbox:tr_cbox"); - new TGeoCompositeShape("base_Shape_2", " seg_3hole + seg_bord:combi_cent_bord"); // seg_bord:combi_cent_bord + new TGeoCompositeShape( + "base_Shape_2", + " seg_3hole + seg_bord:combi_cent_bord"); // seg_bord:combi_cent_bord new TGeoCompositeShape("base_Shape_3", " labox1:tr_la + labox2:tr_2la "); - auto* base_Shape_4 = new TGeoCompositeShape("base_Shape_4", "base_Shape_0 - base_Shape_1 - base_Shape_1:rot1 + base_Shape_2 + tongbox:tr_tong - circ_hole1:tr_hole1 - circ_hole1:tr2_hole1 - circ_hole1:tr3_hole1 - circ_hole2:tr1_hole2 - circ_hole2:tr2_hole2 - base_Shape_3 "); - - // auto * base_Shape_5 = new TGeoCompositeShape("base_Shape_5","disc-box1 -box2 -box3 -seg_1hole -seg_2hole +seg_3hole -seg_1hole:rot1-seg_2hole:rot1 - cbox:tr_cbox - labox:tr_la - labox2:tr_2la + seg_bord "); - - // auto * base0_Volume = new TGeoVolume("base0_Volume",base_Shape_0,kMedAlu); - // auto * base1_Volume = new TGeoVolume("base1_Volume",base_Shape_1,kMedAlu); - // auto * base2_Volume = new TGeoVolume("base2_Volume",base_Shape_2,kMedAlu); - // auto * base3_Volume = new TGeoVolume("base3_Volume",base_Shape_3,kMedAlu); - - auto* base4_Volume = new TGeoVolume("base4_Volume", base_Shape_4, kMedAlu); + /// auto* base_Shape_4 = new TGeoCompositeShape("base_Shape_4", "base_Shape_0 + /// - base_Shape_1 - base_Shape_1:rot1 + base_Shape_2 + tongbox:tr_tong - + /// circ_hole1:tr_hole1 - circ_hole1:tr2_hole1 - circ_hole1:tr3_hole1 - + /// circ_hole2:tr1_hole2 - circ_hole2:tr2_hole2 - base_Shape_3 "); + auto* base_Shape_4 = new TGeoCompositeShape( + "base_Shape_4", + "base_Shape_0 - base_Shape_1 - base_Shape_1:rot1 + base_Shape_2 - " + "base_Shape_3 + tongbox:tr_tong - c_cut"); //- circ_hole1:tr_hole1 - + // circ_hole1:tr2_hole1 - + // circ_hole1:tr3_hole1 - + // circ_hole2:tr1_hole2 - + // circ_hole2:tr2_hole2 + + // auto * base_Shape_5 = new TGeoCompositeShape("base_Shape_5","disc-box1 + // -box2 -box3 -seg_1hole -seg_2hole +seg_3hole -seg_1hole:rot1-seg_2hole:rot1 + // - cbox:tr_cbox - labox:tr_la - labox2:tr_2la + seg_bord "); + + auto* base4_Volume = new TGeoVolume("base4_Volume", base_Shape_4, malu5083); base->AddNode(base4_Volume, 2, rot_base); // base->AddNode(base4_Volume,2); - // 5th piece MIDLE Framework midle - - auto* midle = new TGeoVolumeAssembly("Midle"); - auto* midle_L = new TGeoVolumeAssembly("Midle_L"); - auto* midle_R = new TGeoVolumeAssembly("Midle_R"); + // 5th piece middle Framework middle + + auto* middle = new TGeoVolumeAssembly("middle"); + auto* middle_L = new TGeoVolumeAssembly("middle_L"); + auto* middle_R = new TGeoVolumeAssembly("middle_R"); + + ////new2020 framework middle + Double_t radin_fwm = 14.406; + Double_t radout_fwm = 15.185; // + Double_t high_fwm = 0.6; /// + Double_t ang_in_fwm = 180. + 12.93; + Double_t ang_fin_fwm = 180. + 58.65; + + ////box add up + Double_t x_fwm_1box = 0.8; // dx=4 + Double_t y_fwm_1box = 1.45; + Double_t z_fwm_1box = 0.6; // 6.5 -> 6.6 to quit + TGeoTranslation* tr_fwm_1box = + new TGeoTranslation("tr_fwm_1box", -14.4, -3.398 + 1.45 / 2, 0); // 81 + tr_fwm_1box->RegisterYourself(); + + ////box quit down + Double_t x_fwm_2box = 0.8; // dx=4 + Double_t y_fwm_2box = 1.2; + Double_t z_fwm_2box = 0.7; // 6.5 -> 6.6 to quit + TGeoTranslation* tr_fwm_2box = + new TGeoTranslation("tr_fwm_2box", -14.4 + 6.9, -3.398 - 9.1, 0); // 81 + tr_fwm_2box->RegisterYourself(); + + //// + TGeoXtru* tria_fwm = new TGeoXtru(2); + tria_fwm->SetName("tria_fwm"); + + Double_t x_tria_fwm[3] = {-13.5, -10., -10.}; + Double_t y_tria_fwm[3] = {-5.94, -5.94, -10.8}; + tria_fwm->DefinePolygon(3, x_tria_fwm, y_tria_fwm); + tria_fwm->DefineSection(0, -0.3, 0., 0., 1); + tria_fwm->DefineSection(1, 0.3, 0., 0., 1); + ////////// // box up to quit and to join - Double_t x_midle = 0.8; //dx=4 - Double_t y_midle = 3.495; //y=34.9 - Double_t z_midle = 0.62; //z=6 + Double_t x_middle = 0.8; // dx=4 + Double_t y_middle = 3.495; // y=34.9 + Double_t z_middle = 0.62; // z=6 // tr1 to join with arc - TGeoTranslation* tr1_midle_box = new TGeoTranslation("tr1_midle_box", -14.4, -0.745, 0); // -152,-17.45,0 - tr1_midle_box->RegisterYourself(); + TGeoTranslation* tr1_middle_box = + new TGeoTranslation("tr1_middle_box", -14.4, -0.745, 0); // -152,-17.45,0 + tr1_middle_box->RegisterYourself(); // tr2 to quiet - TGeoTranslation* tr2_midle_box = new TGeoTranslation("tr2_midle_box", -15.2, -0.745, 0); // -152,-17.45,0 - tr2_midle_box->RegisterYourself(); + TGeoTranslation* tr2_middle_box = + new TGeoTranslation("tr2_middle_box", -15.2, -0.745, 0); // -152,-17.45,0 + tr2_middle_box->RegisterYourself(); // box down_1 - Double_t x_midle_d1box = 0.4; // dx=4 - Double_t y_midle_d1box = 0.28; - Double_t z_midle_d1box = 0.66; - TGeoTranslation* tr_midle_d1box = new TGeoTranslation("tr_midle_d1box", -7.3, -11.96, 0.); // 81 - tr_midle_d1box->RegisterYourself(); + Double_t x_middle_d1box = 0.4; // dx=4 + Double_t y_middle_d1box = 0.28; + Double_t z_middle_d1box = 0.66; + TGeoTranslation* tr_middle_d1box = + new TGeoTranslation("tr_middle_d1box", -7.3, -11.96, 0.); // 81 + tr_middle_d1box->RegisterYourself(); // box down_2 - Double_t x_midle_d2box = 0.8; // dx=4 - Double_t y_midle_d2box = 1.0; - Double_t z_midle_d2box = 0.66; // - TGeoTranslation* tr_midle_d2box = new TGeoTranslation("tr_midle_d2box", -7.5, -12.6249, 0); // 81 - tr_midle_d2box->RegisterYourself(); + Double_t x_middle_d2box = 0.8; // dx=4 + Double_t y_middle_d2box = 1.0; + Double_t z_middle_d2box = 0.66; // + TGeoTranslation* tr_middle_d2box = + new TGeoTranslation("tr_middle_d2box", -7.5, -12.6249, 0); // 81 + tr_middle_d2box->RegisterYourself(); // arc circ part - Double_t radin_midle = 14.0; - Double_t radout_midle = 15.0; // - Double_t high_midle = 0.6; // - Double_t ang_in_midle = 180; - Double_t ang_fin_midle = 238.21; // alfa=57.60 + Double_t radin_middle = 14.0; + Double_t radout_middle = 15.0; // + Double_t high_middle = 0.6; // + Double_t ang_in_middle = 180; + Double_t ang_fin_middle = 238.21; // alfa=57.60 - // circular hole1 ; hole_midle d=3.5 + // circular hole1 ; hole_middle d=3.5 Double_t radin_mid_1hole = 0.; Double_t radout_mid_1hole = 0.175; // diameter 3.5 Double_t high_mid_1hole = 1.5; // 2.4 TGeoRotation* rot_mid_1hole = new TGeoRotation("rot_mid_1hole", 90, 90, 0); rot_mid_1hole->RegisterYourself(); - TGeoCombiTrans* combi_mid_1tubhole = new TGeoCombiTrans(-14.2, 0.325, 0, rot_mid_1hole); // + TGeoCombiTrans* combi_mid_1tubhole = + new TGeoCombiTrans(-14.2, 0.325, 0, rot_mid_1hole); // combi_mid_1tubhole->SetName("combi_mid_1tubhole"); combi_mid_1tubhole->RegisterYourself(); - // circular hole2 ; hole_midle d=3 + // circular hole2 ; hole_middle d=3 Double_t radin_mid_2hole = 0.; Double_t radout_mid_2hole = 0.15; // diameter 3 Double_t high_mid_2hole = 1.8; // - TGeoCombiTrans* combi_mid_2tubhole = new TGeoCombiTrans(-7.7, -12.355, 0, rot_mid_1hole); //x=81 + TGeoCombiTrans* combi_mid_2tubhole = + new TGeoCombiTrans(-7.7, -12.355, 0, rot_mid_1hole); // x=81 combi_mid_2tubhole->SetName("combi_mid_2tubhole"); combi_mid_2tubhole->RegisterYourself(); - // shape for midle - new TGeoBBox("midle_box", x_midle / 2, y_midle / 2, z_midle / 2); + // shape for middle + new TGeoBBox("middle_box", x_middle / 2, y_middle / 2, z_middle / 2); + + new TGeoBBox("middle_d1box", x_middle_d1box / 2, y_middle_d1box / 2, + z_middle_d1box / 2); + + new TGeoBBox("middle_d2box", x_middle_d2box / 2, y_middle_d2box / 2, + z_middle_d2box / 2); + + new TGeoTubeSeg("arc_middle", radin_middle, radout_middle, high_middle / 2, + ang_in_middle, ang_fin_middle); + + new TGeoTube("mid_1tubhole", radin_mid_1hole, radout_mid_1hole, + high_mid_1hole / 2); - new TGeoBBox("midle_d1box", x_midle_d1box / 2, y_midle_d1box / 2, z_midle_d1box / 2); + new TGeoTube("mid_2tubhole", radin_mid_2hole, radout_mid_2hole, + high_mid_2hole / 2); - new TGeoBBox("midle_d2box", x_midle_d2box / 2, y_midle_d2box / 2, z_midle_d2box / 2); + auto* tube_fwm = new TGeoTubeSeg("tube_fwm", radin_fwm, radout_fwm, + high_fwm / 2, ang_in_fwm, ang_fin_fwm); + auto* fwm_1box = + new TGeoBBox("fwm_1box", x_fwm_1box / 2, y_fwm_1box / 2, z_fwm_1box / 2); + auto* fwm_2box = + new TGeoBBox("fwm_2box", x_fwm_2box / 2, y_fwm_2box / 2, z_fwm_2box / 2); - new TGeoTubeSeg("arc_midle", radin_midle, radout_midle, high_midle / 2, ang_in_midle, ang_fin_midle); + // composite shape for middle - new TGeoTube("mid_1tubhole", radin_mid_1hole, radout_mid_1hole, high_mid_1hole / 2); + new TGeoCompositeShape( + "middle_Shape_0", + " arc_middle + middle_box:tr1_middle_box - middle_box:tr2_middle_box - " + "middle_d1box:tr_middle_d1box - middle_d2box:tr_middle_d2box"); - new TGeoTube("mid_2tubhole", radin_mid_2hole, radout_mid_2hole, high_mid_2hole / 2); + auto* middle_Shape_1 = new TGeoCompositeShape( + "middle_Shape_1", + " middle_Shape_0 " + "-mid_1tubhole:combi_mid_1tubhole-mid_2tubhole:combi_mid_2tubhole"); - // composite shape for midle + TGeoRotation* rot_middlez = new TGeoRotation("rot_middley", 180, 180, 0); + rot_middlez->RegisterYourself(); + TGeoCombiTrans* combi_middle_L = new TGeoCombiTrans( + 0, -7.625, 24.15 + 0.675, + rot_90x); // x=7.35, y=0, z=15.79- 0,-7.625,24.15+0.675-80) + combi_middle_L->SetName("combi_middle_L"); + combi_middle_L->RegisterYourself(); - new TGeoCompositeShape("midle_Shape_0", " arc_midle + midle_box:tr1_midle_box - midle_box:tr2_midle_box - midle_d1box:tr_midle_d1box - midle_d2box:tr_midle_d2box"); + TGeoTranslation* tr_middle_L = new TGeoTranslation( + "tr_middle_L", 0, -4.45 - 0.1, 24.85 + 0.675); //+2.5,, -152,-17.45,0 + tr_middle_L->RegisterYourself(); - auto* midle_Shape_1 = new TGeoCompositeShape("midle_Shape_1", " midle_Shape_0 -mid_1tubhole:combi_mid_1tubhole-mid_2tubhole:combi_mid_2tubhole"); + TGeoCombiTrans* combi_middle_R = + new TGeoCombiTrans(0, -4.45 - 0.1, 24.85 + 0.675, + rot_middlez); // x=7.35, y=0, z=15.79 y7.625 ++2.5 + combi_middle_R->SetName("combi_middle_R"); + combi_middle_R->RegisterYourself(); - TGeoRotation* rot_midlez = new TGeoRotation("rot_midley", 180, 180, 0); - rot_midlez->RegisterYourself(); - TGeoCombiTrans* combi_midle_L = new TGeoCombiTrans(0, -7.625, 24.15 + 0.675, rot_90x); // x=7.35, y=0, z=15.79- 0,-7.625,24.15+0.675-80) - combi_midle_L->SetName("combi_midle_L"); - combi_midle_L->RegisterYourself(); + auto* middle_Shape_3 = new TGeoCompositeShape( + "middle_Shape_3", + " tube_fwm + fwm_1box:tr_fwm_1box - fwm_2box:tr_fwm_2box +tria_fwm"); - TGeoTranslation* tr_midle_L = new TGeoTranslation("tr_midle_L", 0, -7.625, 24.15 + 0.675); // -152,-17.45,0 - tr_midle_L->RegisterYourself(); + auto* middle_Shape_4 = new TGeoCompositeShape( + "middle_Shape_4", + " tube_fwm + fwm_1box:tr_fwm_1box - fwm_2box:tr_fwm_2box +tria_fwm"); - TGeoCombiTrans* combi_midle_R = new TGeoCombiTrans(0, -7.625, 24.15 + 0.675, rot_midlez); // x=7.35, y=0, z=15.79 - combi_midle_R->SetName("combi_midle_R"); - combi_midle_R->RegisterYourself(); + // auto* middle_Volume = new TGeoVolume("middle_Volume", middle_Shape_1, + // kMedAlu); + auto* middle_Volume_L = + new TGeoVolume("middle_Volume_L", middle_Shape_3, malu5083); + auto* middle_Volume_R = + new TGeoVolume("middle_Volume_R", middle_Shape_4, malu5083); - auto* midle_Volume = new TGeoVolume("midle_Volume", midle_Shape_1, kMedAlu); + TGeoTranslation* tr_middle = new TGeoTranslation( + "tr_middle", 0, -4.45 - 0.1, 24.85 + 0.675); //+2.5,, -152,-17.45,0 + tr_middle->RegisterYourself(); - midle_L->AddNode(midle_Volume, 1, tr_midle_L); - midle_R->AddNode(midle_Volume, 1, combi_midle_R); + middle_L->AddNode(middle_Volume_L, 1, tr_middle); + middle_R->AddNode(middle_Volume_R, 1, combi_middle_R); // combi_midle_R - // midle->AddNode(midle_Volume,1); - midle->AddNode(midle_L, 1); - midle->AddNode(midle_R, 2); + middle->AddNode(middle_L, 1); + middle->AddNode(middle_R, 2); // new piece _/ \_ // Support_rail_L & Support_rail_R @@ -553,45 +862,51 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) auto* rail_L = new TGeoVolumeAssembly("rail_L"); // box down_2 - Double_t x_RL_1box = 3.0; // dx=15 - Double_t y_RL_1box = 1.21; // dy=6, -dy=6 - Double_t z_RL_1box = 0.8; // dz=4 to quit - TGeoTranslation* tr_RL_1box = new TGeoTranslation(0, y_RL_1box / 2, 1.825); // 81 + Double_t x_RL_1box = 3.0; // dx=15 + Double_t y_RL_1box = 1.21; // dy=6, -dy=6 + Double_t z_RL_1box = 0.8; // dz=4 to quit + TGeoTranslation* tr_RL_1box = + new TGeoTranslation(0, y_RL_1box / 2, 1.825); // 81 tr_RL_1box->SetName("tr_RL_1box"); tr_RL_1box->RegisterYourself(); TGeoXtru* xtru_RL1 = new TGeoXtru(2); xtru_RL1->SetName("S_XTRU_RL1"); - Double_t x_RL1[5] = {-1.5, 1.5, 0.5, 0.5, -1.5}; // 93,93,73,73,-15}; //vertices - Double_t y_RL1[5] = {1.2, 1.2, 2.2, 8.2, 8.2}; // 357.5,357.5,250.78,145.91}; + Double_t x_RL1[5] = {-1.5, 1.5, 0.5, 0.5, + -1.5}; // 93,93,73,73,-15}; //vertices + Double_t y_RL1[5] = {1.2, 1.2, 2.2, 8.2, 8.2}; // 357.5,357.5,250.78,145.91}; xtru_RL1->DefinePolygon(5, x_RL1, y_RL1); - xtru_RL1->DefineSection(0, -2.225, 0., 0., 1); // (plane,-zplane/ +zplane, x0, y0,(x/y)) + xtru_RL1->DefineSection(0, -2.225, 0., 0., + 1); // (plane,-zplane/ +zplane, x0, y0,(x/y)) xtru_RL1->DefineSection(1, 2.225, 0., 0., 1); - TGeoXtru* xtru_RL2 = new TGeoXtru(2); - xtru_RL2->SetName("S_XTRU_RL2"); + /// TGeoXtru* xtru_RL2 = new TGeoXtru(2); + /// xtru_RL2->SetName("S_XTRU_RL2"); - Double_t x_RL2[8] = {-1.5, 0.5, 0.5, 9.3, 9.3, 7.3, 7.3, -1.5}; // vertices - Double_t y_RL2[8] = {8.2, 8.2, 13.863, 24.35, 35.75, 35.75, 25.078, 14.591}; + /// Double_t x_RL2[8] = {-1.5, 0.5, 0.5, 9.3, 9.3, 7.3, 7.3, -1.5}; // + /// vertices Double_t y_RL2[8] = + /// {8.2, 8.2, 13.863, 24.35, 35.75, 35.75, 25.078, 14.591}; - xtru_RL2->DefinePolygon(8, x_RL2, y_RL2); + /// xtru_RL2->DefinePolygon(8, x_RL2, y_RL2); - xtru_RL2->DefineSection(0, 0.776, 0, 0, 1); // (plane,-zplane/+zplane, x0, y0,(x/y)) - xtru_RL2->DefineSection(1, 2.225, 0, 0, 1); + /// xtru_RL2->DefineSection(0, 0.776, 0, 0, 1); // (plane,-zplane/+zplane, x0, + /// y0,(x/y)) xtru_RL2->DefineSection(1, 2.225, 0, 0, 1); // box knee - Double_t x_RL_kneebox = 1.5; // dx=7.5 - Double_t y_RL_kneebox = 3.5; // dy=17.5 - Double_t z_RL_kneebox = 1.5; // dz=7.5 to quit - TGeoTranslation* tr_RL_kneebox = new TGeoTranslation(0, 0, 0); // 81 x =-2.5, y=145.91 + Double_t x_RL_kneebox = 1.5; // dx=7.5 + Double_t y_RL_kneebox = 3.5; // dy=17.5 + Double_t z_RL_kneebox = 1.5; // dz=7.5 to quit + TGeoTranslation* tr_RL_kneebox = + new TGeoTranslation(0, 0, 0); // 81 x =-2.5, y=145.91 tr_RL_kneebox->SetName("tr_RL_kneebox"); tr_RL_kneebox->RegisterYourself(); TGeoRotation* rot_knee = new TGeoRotation("rot_knee", -40, 0, 0); rot_knee->SetName("rot_knee"); rot_knee->RegisterYourself(); - TGeoCombiTrans* combi_knee = new TGeoCombiTrans(0.96, 1.75 + 0.81864, 0, rot_knee); // y= + TGeoCombiTrans* combi_knee = + new TGeoCombiTrans(0.96, 1.75 + 0.81864, 0, rot_knee); // y= combi_knee->SetName("combi_knee"); combi_knee->RegisterYourself(); // quit diagona-> qdi @@ -601,7 +916,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoRotation* rot_qdi = new TGeoRotation("rot_qdi", 0, 24.775, 0); rot_qdi->RegisterYourself(); - TGeoCombiTrans* combi_qdi = new TGeoCombiTrans(0, 5.579, -2.087, rot_qdi); // y= + TGeoCombiTrans* combi_qdi = + new TGeoCombiTrans(0, 5.579, -2.087, rot_qdi); // y= combi_qdi->SetName("combi_qdi"); combi_qdi->RegisterYourself(); // knee small @@ -613,7 +929,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t y_3RL[6] = {-1.75, -1.75, 1.203, 3.465, 4.4311, 1.75}; xtru3_RL->DefinePolygon(6, x_3RL, y_3RL); - xtru3_RL->DefineSection(0, -0.75, 0, 0, 1); // (plane,-zplane/+zplane, x0, y0,(x/y)) + xtru3_RL->DefineSection(0, -0.75, 0, 0, + 1); // (plane,-zplane/+zplane, x0, y0,(x/y)) xtru3_RL->DefineSection(1, 0.76, 0, 0, 1); TGeoTranslation* tr_vol3_RL = new TGeoTranslation(-0.25, 12.66, 0); // @@ -628,7 +945,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoRotation* rot_RL1hole = new TGeoRotation("rot_RL1hole", 0, 0, 0); rot_RL1hole->RegisterYourself(); - TGeoCombiTrans* combi_RL1hole = new TGeoCombiTrans(0.7, 0.6, 1.85, rot_RL1hole); //y= + TGeoCombiTrans* combi_RL1hole = + new TGeoCombiTrans(0.7, 0.6, 1.85, rot_RL1hole); // y= combi_RL1hole->SetName("combi_RL1hole"); combi_RL1hole->RegisterYourself(); // similar hole for R Join. @@ -639,36 +957,133 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoRotation* rot_ir_RL = new TGeoRotation("rot_ir_RL", 90, 90, 0); rot_ir_RL->RegisterYourself(); // in y = l_253.5 - 6. enter in (0,6,0) - TGeoCombiTrans* combi_ir1_RL = new TGeoCombiTrans(8.62, 24.75, 1.5, rot_ir_RL); + TGeoCombiTrans* combi_ir1_RL = + new TGeoCombiTrans(8.62, 24.75, 1.5, rot_ir_RL); combi_ir1_RL->SetName("combi_ir1_RL"); combi_ir1_RL->RegisterYourself(); TGeoCombiTrans* combi_ir2_RL = new TGeoCombiTrans(8.6, 33.15, 1.5, rot_ir_RL); combi_ir2_RL->SetName("combi_ir2_RL"); combi_ir2_RL->RegisterYourself(); + // + // 1) modification + TGeoXtru* xtru_RL2 = new TGeoXtru(2); + xtru_RL2->SetName("S_XTRU_RL2"); + + // modi the arm L---> new L + Double_t x_RL2[8] = {-1.5, 0.5, 0.5, 9.3, 9.3, 7.3, 7.3, -1.5}; + Double_t y_RL2[8] = {8.2, 8.2, 13.863, 24.35, 25.65, 25.65, 25.078, 14.591}; + + xtru_RL2->DefinePolygon(8, x_RL2, y_RL2); + xtru_RL2->DefineSection(0, 0.7752, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) 0.775 + xtru_RL2->DefineSection(1, 2.225, 0, 0, 1); + + // 2) modi adding box new element 1 box + TGeoXtru* adi1_RL = new TGeoXtru(2); + adi1_RL->SetName("S_ADI1_RL"); + + Double_t x_adi1RL[4] = {-1.5, -1.5, 0.5, 0.5}; + Double_t y_adi1RL[4] = {2.2, 13.863, 13.863, 2.2}; + + /// Double_t x_adi1RL[4]={-0.5,-0.5,1.5,1.5}; //vertices + ////Double_t y_adi1RL[4]={2.2,13.86,13.86,2.2}; + + adi1_RL->DefinePolygon(4, x_adi1RL, y_adi1RL); + adi1_RL->DefineSection(0, -0.75, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) + adi1_RL->DefineSection(1, 0.775, 0, 0, 1); // 0.76 + + //// 3) modi adding new knee new element 2 new knee + TGeoXtru* adi2_RL = new TGeoXtru(2); // + adi2_RL->SetName("S_ADI2_RL"); + Double_t x_adi2RL[6] = {-1.5, 0.5, 9.3, 9.3, 7.8, 7.8}; + Double_t y_adi2RL[6] = {13.863, 13.863, 24.35, 25.65, 25.65, 25.078}; + + adi2_RL->DefinePolygon(6, x_adi2RL, y_adi2RL); + adi2_RL->DefineSection(0, -0.75, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) + adi2_RL->DefineSection(1, 0.7755, 0, 0, 1); // 0.76, 0.775 + + // 4)modi to quit ---> trap + Double_t RL_dx1 = 2.66; // at -z + Double_t RL_dx2 = 1; // dat +z + Double_t RL_dy = 2.2; // dz=7.5 to quit + Double_t RL_dz = 1.5; // dz=1.5 to quit + // auto *trap1 = new TGeoTrd1("TRAP1",RL_dx1,RL_dx2 ,RL_dy ,RL_dz); + + TGeoRotation* rot_RL_Z50 = new TGeoRotation("rot_RL_Z50", 50, 0, 0); + rot_RL_Z50->RegisterYourself(); + TGeoCombiTrans* combi_RL_trap = + new TGeoCombiTrans(5, 18.633, -1.5 - 0.025, rot_RL_Z50); // y= + combi_RL_trap->SetName("combi_RL_trap"); + combi_RL_trap->RegisterYourself(); + + ///// 5) modi to quit inferior part box + Double_t x_qinf_box = 10.66; // + Double_t y_qinf_box = 10.2; // + Double_t z_qinf_box = 3.; // dz =1.5 + auto* s_RL_qinf_box = new TGeoBBox("S_RL_QINF_BOX", x_qinf_box / 2, + y_qinf_box / 2, z_qinf_box / 2); + TGeoCombiTrans* combi_RL_qbox = + new TGeoCombiTrans(7, 23., -1.5 - 0.025, rot_RL_Z50); // y= , z=-0.75-0.75 + combi_RL_qbox->SetName("combi_RL_qbox"); + combi_RL_qbox->RegisterYourself(); + + // 6) modi. add penta face z + TGeoXtru* pentfa_RL = new TGeoXtru(2); // not work + pentfa_RL->SetName("S_PENTFA_RL"); + Double_t x_pentfaRL[5] = {-1., -1., 0.13, 1., 1.}; + Double_t y_pentfaRL[5] = {1.125, 0.045, -1.125, -1.125, 1.125}; // 1.125 + + pentfa_RL->DefinePolygon(5, x_pentfaRL, y_pentfaRL); + pentfa_RL->DefineSection(0, -5.05, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) // 0.75 + pentfa_RL->DefineSection(1, 5.055, 0, 0, 1); // 0.76.. 0.9036 + + TGeoRotation* rot_X90 = new TGeoRotation("rot_X90", 0, 90, 0); + rot_X90->RegisterYourself(); + TGeoCombiTrans* combi_RL_pent = new TGeoCombiTrans( + 8.3, 30.705, 1.125 - 0.025, rot_X90); // x =8.3 , z= 1.125 + combi_RL_pent->SetName("combi_RL_pent"); + combi_RL_pent->RegisterYourself(); + // // shape for Rail L geom new TGeoBBox("RL_1box", x_RL_1box / 2, y_RL_1box / 2, z_RL_1box / 2); - new TGeoBBox("RL_kneebox", x_RL_kneebox / 2, y_RL_kneebox / 2, z_RL_kneebox / 2); // no_ used + new TGeoBBox("RL_kneebox", x_RL_kneebox / 2, y_RL_kneebox / 2, + z_RL_kneebox / 2); // no_ used new TGeoBBox("qdi_box", x_qdi_box / 2, y_qdi_box / 2, z_qdi_box / 2); - - // auto *s_RL1hole=new TGeoTube("S_RL1HOLE",radin_RL1hole,radout_RL1hole,high_RL1hole/2); - // auto *s_irL_hole=new TGeoTube("S_irL_HOLE",radin_ir_railL,radout_ir_railL,high_ir_railL/2); + new TGeoTrd1("TRAP1", RL_dx1, RL_dx2, RL_dy, RL_dz); + // auto *s_RL1hole=new + // TGeoTube("S_RL1HOLE",radin_RL1hole,radout_RL1hole,high_RL1hole/2); auto + // *s_irL_hole=new + // TGeoTube("S_irL_HOLE",radin_ir_railL,radout_ir_railL,high_ir_railL/2); // composite shape for rail L - auto* RL_Shape_0 = new TGeoCompositeShape("RL_Shape_0", " xtru3_RL:tr_vol3_RL + S_XTRU_RL1 + S_XTRU_RL2 + RL_1box:tr_RL_1box - qdi_box:combi_qdi"); + auto* RL_Shape_0 = new TGeoCompositeShape( + "RL_Shape_0", + " S_XTRU_RL1 + S_XTRU_RL2 + RL_1box:tr_RL_1box - " + "qdi_box:combi_qdi + " + "S_ADI1_RL + S_ADI2_RL - TRAP1:combi_RL_trap - " + "S_RL_QINF_BOX:combi_RL_qbox + " + "S_PENTFA_RL:combi_RL_pent"); // xtru3_RL:tr_vol3_RL + // + - TGeoVolume* rail_L_vol0 = new TGeoVolume("RAIL_L_VOL0", RL_Shape_0, kMedAlu); + TGeoVolume* rail_L_vol0 = new TGeoVolume("RAIL_L_VOL0", RL_Shape_0, malu5083); + // TGeoVolume* rail_L_vol0 = new TGeoVolume("RAIL_L_VOL0", RL_Shape_0, + // kMedAlu); rail_L->AddNode(rail_L_vol0, 1, new TGeoTranslation(0., 0., 1.5)); // piece 7th RAIL RIGHT // auto *rail_R = new TGeoVolumeAssembly("rail_R"); - Double_t x_RR_1box = 3.0; // dx=15 - Double_t y_RR_1box = 1.2; // dy=6, -dy=6 - Double_t z_RR_1box = 0.8; // dz=4 to quit - TGeoTranslation* tr_RR_1box = new TGeoTranslation("tr_RR_1box", 0, 0.6, 1.825); // 81 + Double_t x_RR_1box = 3.0; // dx=15 + Double_t y_RR_1box = 1.2; // dy=6, -dy=6 + Double_t z_RR_1box = 0.8; // dz=4 to quit + TGeoTranslation* tr_RR_1box = + new TGeoTranslation("tr_RR_1box", 0, 0.6, 1.825); // 81 tr_RR_1box->RegisterYourself(); TGeoXtru* part_RR1 = new TGeoXtru(2); @@ -680,34 +1095,39 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t y_RR1[5] = {1.2, 2.2, 8.2, 8.2, 1.2}; // 357.5,357.5,250.78,145.91}; part_RR1->DefinePolygon(5, x_RR1, y_RR1); - part_RR1->DefineSection(0, -2.225, 0, 0, 1); // (plane,-zplane/ +zplane, x0, y0,(x/y)) + part_RR1->DefineSection(0, -2.225, 0, 0, + 1); // (plane,-zplane/ +zplane, x0, y0,(x/y)) part_RR1->DefineSection(1, 2.225, 0, 0, 1); - TGeoXtru* part_RR2 = new TGeoXtru(2); - part_RR2->SetName("part_RR2"); + // TGeoXtru* part_RR2 = new TGeoXtru(2); + // part_RR2->SetName("part_RR2"); // TGeoVolume *vol_RR2 = gGeoManager->MakeXtru("part_RR2",Al,2); // TGeoXtru *xtru_RR2 = (TGeoXtru*)vol_RR2->GetShape(); - Double_t x_RR2[8] = {-0.5, -0.5, -9.3, -9.3, -7.3, -7.3, 1.5, 1.5}; // K,E,F,G,H,I,J,L // vertices - Double_t y_RR2[8] = {8.2, 13.863, 24.35, 35.75, 35.75, 25.078, 14.591, 8.2}; + // Double_t x_RR2[8] = {-0.5, -0.5, -9.3, -9.3, -7.3, -7.3, 1.5, 1.5}; // + // K,E,F,G,H,I,J,L // vertices Double_t y_RR2[8] = + // {8.2, 13.863, 24.35, 35.75, 35.75, 25.078, 14.591, 8.2}; - part_RR2->DefinePolygon(8, x_RR2, y_RR2); - part_RR2->DefineSection(0, 0.776, 0, 0, 1); // (plane,-zplane/+zplane, x0, y0,(x/y)) - part_RR2->DefineSection(1, 2.225, 0, 0, 1); + // part_RR2->DefinePolygon(8, x_RR2, y_RR2); + // part_RR2->DefineSection(0, 0.776, 0, 0, 1); // (plane,-zplane/+zplane, x0, + // y0,(x/y)) part_RR2->DefineSection(1, 2.225, 0, 0, 1); // knee (small) TGeoXtru* part_RR3 = new TGeoXtru(2); part_RR3->SetName("part_RR3"); - Double_t x_3RR[6] = {1.0, 1.0, -1.2497, -2.2138, -0.5, -0.5}; // R,Q,P,O,N.M // vertices + Double_t x_3RR[6] = {1.0, 1.0, -1.2497, + -2.2138, -0.5, -0.5}; // R,Q,P,O,N.M // vertices Double_t y_3RR[6] = {10.91, 14.41, 17.0911, 15.9421, 13.86, 10.91}; part_RR3->DefinePolygon(6, x_3RR, y_3RR); - part_RR3->DefineSection(0, -0.75, 0, 0, 1); // (plane,-zplane/+zplane, x0, y0,(x/y)) + part_RR3->DefineSection(0, -0.75, 0, 0, + 1); // (plane,-zplane/+zplane, x0, y0,(x/y)) part_RR3->DefineSection(1, 0.78, 0, 0, 1); - TGeoTranslation* tr_vol3_RR = new TGeoTranslation("tr_vol3_RR", -0.25, 12.66, 0); // + TGeoTranslation* tr_vol3_RR = + new TGeoTranslation("tr_vol3_RR", -0.25, 12.66, 0); // tr_vol3_RR->RegisterYourself(); // quit diagona-> qdi @@ -717,7 +1137,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoRotation* rot_Rqdi = new TGeoRotation("rot_Rqdi", 0, 24.775, 0); rot_Rqdi->RegisterYourself(); - TGeoCombiTrans* combi_Rqdi = new TGeoCombiTrans(0, 5.579, -2.087, rot_Rqdi); // y= + TGeoCombiTrans* combi_Rqdi = + new TGeoCombiTrans(0, 5.579, -2.087, rot_Rqdi); // y= combi_Rqdi->SetName("combi_Rqdi"); combi_Rqdi->RegisterYourself(); @@ -726,7 +1147,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t radout_a_rail = 0.325; // diameter 3.5 Double_t high_a_rail = 0.82; // - TGeoTranslation* tr_a_RR = new TGeoTranslation("tr_a_RR", -0.7, 0.6, 1.825); // right + TGeoTranslation* tr_a_RR = + new TGeoTranslation("tr_a_RR", -0.7, 0.6, 1.825); // right tr_a_RR->RegisterYourself(); // circular hole_ir. diameter=M3 (3 mm)) prof trou:8, tar:6mm Double_t radin_ir_rail = 0.; @@ -735,18 +1157,22 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) TGeoRotation* rot_ir_RR = new TGeoRotation("rot_ir_RR", 90, 90, 0); rot_ir_RR->RegisterYourself(); // in y = l_253.5 - 6. center in (0,6,0) - TGeoCombiTrans* combi_ir_RR = new TGeoCombiTrans(-8.62, 24.75, 1.5, rot_ir_RR); + TGeoCombiTrans* combi_ir_RR = + new TGeoCombiTrans(-8.62, 24.75, 1.5, rot_ir_RR); combi_ir_RR->SetName("combi_ir_RR"); combi_ir_RR->RegisterYourself(); - TGeoCombiTrans* combi_ir2_RR = new TGeoCombiTrans(-8.6, 33.15, 1.5, rot_ir_RR); + TGeoCombiTrans* combi_ir2_RR = + new TGeoCombiTrans(-8.6, 33.15, 1.5, rot_ir_RR); combi_ir2_RR->SetName("combi_ir2_RR"); combi_ir2_RR->RegisterYourself(); - TGeoCombiTrans* combi_rail_R = new TGeoCombiTrans(24.1, -1.825, 0, rot_90x); // y= + TGeoCombiTrans* combi_rail_R = + new TGeoCombiTrans(24.1, -1.825, 0, rot_90x); // y= combi_rail_R->SetName("combi_rail_R"); combi_rail_R->RegisterYourself(); - TGeoCombiTrans* combi_rail_L = new TGeoCombiTrans(-24.1, -1.825, 0, rot_90x); // y= + TGeoCombiTrans* combi_rail_L = + new TGeoCombiTrans(-24.1, -1.825, 0, rot_90x); // y= combi_rail_L->SetName("combi_rail_L"); combi_rail_L->RegisterYourself(); @@ -755,29 +1181,129 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) tr_sr_l->RegisterYourself(); TGeoTranslation* tr_sr_r = new TGeoTranslation("tr_sr_r", 15.01, 0, 0); // tr_sr_r->RegisterYourself(); + // + //////// new modfi b /////// cut arm + TGeoXtru* part_RR2 = new TGeoXtru(2); + part_RR2->SetName("part_RR2"); + //-TGeoVolume *vol_RR2 = gGeoManager->MakeXtru("S_XTRU_RR2",Al,2); + //-TGeoXtru *xtru_RR2 = (TGeoXtru*)vol_RR2->GetShape(); + // 1b) modi, reducing arm + // 1b) modi, reducing arm + Double_t x_RR2[8] = {-0.5, -0.5, -9.3, -9.3, + -7.3, -7.3, 1.5, 1.5}; // K,E,F,G,H,I,J,L//vertices + Double_t y_RR2[8] = {8.2, 13.863, 24.35, 25.65, + 25.65, 25.078, 14.591, 8.2}; // 35.75, 35.75 -->25.65 + + part_RR2->DefinePolygon(8, x_RR2, y_RR2); + part_RR2->DefineSection(0, 0.776, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) + part_RR2->DefineSection(1, 2.225, 0, 0, 1); + + // 2b) modi adding box new element 1 box + TGeoXtru* adi1_RR = new TGeoXtru(2); + adi1_RR->SetName("S_ADI1_RR"); + + Double_t x_adi1RR[4] = {-0.5, -.5, 1.5, 1.5}; // vertices + Double_t y_adi1RR[4] = {2.2, 13.863, 13.863, 2.2}; + + adi1_RR->DefinePolygon(4, x_adi1RR, y_adi1RR); + adi1_RR->DefineSection(0, -0.75, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) + adi1_RR->DefineSection(1, 0.775, 0, 0, 1); // 0.76 + + // 3b) modi adding new knee new element 2 new knee + TGeoXtru* adi2_RR = new TGeoXtru(2); // + adi2_RR->SetName("S_ADI2_RR"); + Double_t x_adi2RR[6] = {1.5, -0.5, -9.3, -9.3, -7.8, -7.8}; + Double_t y_adi2RR[6] = {13.863, 13.863, 24.35, 25.65, 25.65, 25.078}; + + adi2_RR->DefinePolygon(6, x_adi2RR, y_adi2RR); + adi2_RR->DefineSection(0, -0.75, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) + adi2_RR->DefineSection(1, 0.7755, 0, 0, 1); // 0.775 + + // 4)modi to quit ---> trap + // Double_t RL_dx1=2.66; //at -z (defined in rail L) + // Double_t RL_dx2=1; // dat +z + // Double_t RL_dy=2.2; // dz=7.5 to quit + // Double_t RL_dz=1.5; // dz=1.5 to quit + // auto *trap1 = new TGeoTrd1("TRAP1",RL_dx1,RL_dx2 ,RL_dy ,RL_dz); + TGeoRotation* rot_RR_Z310 = new TGeoRotation("rot_RR_Z310", -50, 0, 0); + rot_RR_Z310->RegisterYourself(); + TGeoCombiTrans* combi_RR_trap = + new TGeoCombiTrans(-5, 18.633, -1.5 - 0.025, rot_RR_Z310); // y= + combi_RR_trap->SetName("combi_RR_trap"); + combi_RR_trap->RegisterYourself(); + + // 5) modi to quit inferior part box + // Double_t x_qinf_box=10.66; // defined in RL + // Double_t y_qinf_box=10.2; // + /// Double_t z_qinf_box=3.; // dz =1.5 + /// auto *s_RL_qinf_box =new TGeoBBox("S_RL_QINF_BOX", + /// x_qinf_box/2,y_qinf_box/2,z_qinf_box/2); + TGeoCombiTrans* combi_RR_qbox = + new TGeoCombiTrans(-7, 23., -1.5 - 0.025, rot_RR_Z310); // rot to RR + combi_RR_qbox->SetName("combi_RR_qbox"); + combi_RR_qbox->RegisterYourself(); + + // 6) modi. add penta face z + TGeoXtru* pentfa_RR = new TGeoXtru(2); + pentfa_RR->SetName("S_PENTFA_RR"); + Double_t x_pentfaRR[5] = {1., 1., -0.13, -1., -1.}; + Double_t y_pentfaRR[5] = {1.125, 0.045, -1.125, -1.125, 1.125}; // 1.125 + + pentfa_RR->DefinePolygon(5, x_pentfaRR, y_pentfaRR); + pentfa_RR->DefineSection(0, -5.05, 0, 0, + 1); //(plane,-zplane/+zplane, x0, y0,(x/y)) // 0.75 + pentfa_RR->DefineSection(1, 5.055, 0, 0, 1); // 0.76.. 0.9036 + + // TGeoRotation *rot_X90 = new TGeoRotation("rot_X90", 0,90,0); + // rot_X90->RegisterYourself(); + TGeoCombiTrans* combi_RR_pent = new TGeoCombiTrans( + -8.3, 30.705, 1.125 - 0.025, rot_X90); // x =8.3 , z= 1.125 + combi_RR_pent->SetName("combi_RR_pent"); + combi_RR_pent->RegisterYourself(); // shape for rail R new TGeoBBox("RR_1box", x_RR_1box / 2, y_RR_1box / 2, z_RR_1box / 2); - // auto *s_qdi_Rbox =new TGeoBBox("S_QDI_RBOX", x_qdi_Rbox/2,y_qdi_Rbox/2,z_qdi_Rbox/2); + // auto *s_qdi_Rbox =new TGeoBBox("S_QDI_RBOX", + // x_qdi_Rbox/2,y_qdi_Rbox/2,z_qdi_Rbox/2); - // auto *s_ir_hole=new TGeoTube("S_ir_HOLE",radin_ir_rail,radout_ir_rail,high_ir_rail/2); + // auto *s_ir_hole=new + // TGeoTube("S_ir_HOLE",radin_ir_rail,radout_ir_rail,high_ir_rail/2); - // auto *s_cc_hole=new TGeoTube("S_CC_HOLE",radin_cc_rail,radout_cc_rail,high_cc_rail/2); + // auto *s_cc_hole=new + // TGeoTube("S_CC_HOLE",radin_cc_rail,radout_cc_rail,high_cc_rail/2); // composite shape for rail R - new TGeoCompositeShape("RR_Shape_0", "RR_1box:tr_RR_1box + part_RR1 + part_RR2 + part_RR3 - qdi_box:combi_qdi "); - - // auto * RR_Shape_0 = new TGeoCompositeShape("RR_Shape_0","RR_1box:tr_RR_1box+ S_part_RR1 + part_RR2 +part_RR3- qdi_box:combi_qdi + S_ir_HOLE:combi_ir_RR +S_ir_HOLE:combi_ir2_RR "); //-RR_1box:tr_RL_1box- S_b_HOLE:tr_b_RR -S_CC_HOLE:combi_cc2_RR + new TGeoCompositeShape( + "RR_Shape_0", + "RR_1box:tr_RR_1box + part_RR1 + part_RR2 - qdi_box:combi_qdi + " + "S_ADI1_RR + S_ADI2_RR - TRAP1:combi_RR_trap - " + "S_RL_QINF_BOX:combi_RR_qbox +S_PENTFA_RR:combi_RR_pent "); // quit + + // part_RR3 + // old knee + + // auto * RR_Shape_0 = new + // TGeoCompositeShape("RR_Shape_0","RR_1box:tr_RR_1box+ S_part_RR1 + part_RR2 + // +part_RR3- qdi_box:combi_qdi + S_ir_HOLE:combi_ir_RR + // +S_ir_HOLE:combi_ir2_RR "); //-RR_1box:tr_RL_1box- S_b_HOLE:tr_b_RR + // -S_CC_HOLE:combi_cc2_RR // JOIN only for show L and R parts - auto* rail_L_R_Shape = new TGeoCompositeShape("RAIL_L_R_Shape", " RL_Shape_0:combi_rail_L + RR_Shape_0:combi_rail_R"); + auto* rail_L_R_Shape = new TGeoCompositeShape( + "RAIL_L_R_Shape", " RL_Shape_0:combi_rail_L + RR_Shape_0:combi_rail_R"); - TGeoVolume* rail_L_R_vol0 = new TGeoVolume("RAIL_L_R_VOL0", rail_L_R_Shape, kMedAlu); + TGeoVolume* rail_L_R_vol0 = + new TGeoVolume("RAIL_L_R_VOL0", rail_L_R_Shape, malu5083); + // TGeoVolume* rail_L_R_vol0 = new TGeoVolume("RAIL_L_R_VOL0", rail_L_R_Shape, + // kMedAlu); TGeoRotation* rot_rLR = new TGeoRotation("rot_rLR", 180, 180, 0); rot_rLR->RegisterYourself(); - TGeoCombiTrans* combi_rLR = new TGeoCombiTrans(0, -6.9, -0.5, rot_rLR); // 0,-6.9,-0.5-80 + TGeoCombiTrans* combi_rLR = + new TGeoCombiTrans(0, -6.9, -0.5, rot_rLR); // 0,-6.9,-0.5-80 combi_rLR->SetName("combi_rLR"); combi_rLR->RegisterYourself(); @@ -787,6 +1313,22 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) auto* sup_rail_MBL = new TGeoVolumeAssembly("sup_rail_MBL"); + /// new sup_rail_MB_L 2020 -------------------------------- + + TGeoXtru* sup_MB_L = new TGeoXtru(2); + sup_MB_L->SetName("sup_MB_L"); + + // vertices a,b,c,d,e,f,g,h new... + Double_t x_sMB_L[11] = {0., 0., 8.12, 24.55, 24.55, 28.25, + 28.25, 34.55, 34.55, 31.737, 6.287}; + Double_t y_sMB_L[11] = {0., 1.8, 1.8, 9.934, 12.6, 12.6, + 13.4, 13.4, 12.6, 12.6, 0.}; + + sup_MB_L->DefinePolygon(11, x_sMB_L, y_sMB_L); + sup_MB_L->DefineSection(0, -0.4, 0, 0, + 1); //(plane, -zplane/ +zplane,x0,y0,(x/y)) + sup_MB_L->DefineSection(1, 0.4, 0, 0, 1); + TGeoXtru* part_MBL_0 = new TGeoXtru(2); part_MBL_0->SetName("part_MBL_0"); // V-MBL_0 @@ -795,53 +1337,71 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t y[8] = {-0.4, 0.4, 0.4, 13.0, 13.0, 12.2, 12.2, -0.4}; part_MBL_0->DefinePolygon(8, x, y); - part_MBL_0->DefineSection(0, -0.4, 0, 0, 1); // (plane, -zplane/ +zplane,x0,y0,(x/y)) + part_MBL_0->DefineSection(0, -0.4, 0, 0, + 1); // (plane, -zplane/ +zplane,x0,y0,(x/y)) part_MBL_0->DefineSection(1, 0.4, 0, 0, 1); TGeoRotation* rot1_MBL_0 = new TGeoRotation("rot1_MBL_0", -90, -90, 90); rot1_MBL_0->RegisterYourself(); // quit box in diag - Double_t x_mb_box = 0.8; // dx=4 - Double_t y_mb_box = 0.8; // dy=4 - Double_t z_mb_box = 0.81; // dz=4 to quit - TGeoTranslation* tr_mb_box = new TGeoTranslation("tr_mb_box", 24.05, 9.55, 0); // 240.5 + Double_t x_mb_box = 0.8; // dx=4 + Double_t y_mb_box = 0.8; // dy=4 + Double_t z_mb_box = 0.81; // dz=4 to quit + TGeoTranslation* tr_mb_box = + new TGeoTranslation("tr_mb_box", 24.05, 9.55, 0); // 240.5 tr_mb_box->RegisterYourself(); // lateral hole-box - Double_t x_lat_box = 0.7; //dx=0.35 - Double_t y_lat_box = 1.8; // dy=0.9 - Double_t z_lat_box = 0.2; // dz=0.1 - TGeoTranslation* tr_lat1L_box = new TGeoTranslation("tr_lat1L_box", 4.6, 0, 0.4); // + Double_t x_lat_box = 0.7; // dx=0.35 + Double_t y_lat_box = 1.8; // dy=0.9 + Double_t z_lat_box = 0.2; // dz=0.1 + TGeoTranslation* tr_lat1L_box = + new TGeoTranslation("tr_lat1L_box", 4.6, 0, 0.4); // tr_lat1L_box->RegisterYourself(); - TGeoTranslation* tr_lat2L_box = new TGeoTranslation("tr_lat2L_box", 9.6, 1.65, 0.4); // + TGeoTranslation* tr_lat2L_box = + new TGeoTranslation("tr_lat2L_box", 9.6, 1.65, 0.4); // tr_lat2L_box->RegisterYourself(); - TGeoTranslation* tr_lat3L_box = new TGeoTranslation("tr_lat3L_box", 18.53, 6.1, 0.4); // + /// TGeoTranslation* tr_lat3L_box = new + /// TGeoTranslation("tr_lat3L_box", 18.53, 6.1, 0.4); // + TGeoTranslation* tr_lat3L_box = + new TGeoTranslation("tr_lat3L_box", 17.35, 5.923, 0.4); // 18.53 tr_lat3L_box->RegisterYourself(); - TGeoTranslation* tr_lat4L_box = new TGeoTranslation("tr_lat4L_box", 26.45, 10, 0.4); // + TGeoTranslation* tr_lat4L_box = + new TGeoTranslation("tr_lat4L_box", 26.45, 10, 0.4); // tr_lat4L_box->RegisterYourself(); - TGeoTranslation* tr_lat5L_box = new TGeoTranslation("tr_lat5L_box", 29.9, 11.6, 0.4); // + TGeoTranslation* tr_lat5L_box = + new TGeoTranslation("tr_lat5L_box", 29.9, 11.6, 0.4); // tr_lat5L_box->RegisterYourself(); - TGeoTranslation* tr_lat1R_box = new TGeoTranslation("tr_lat1R_box", 4.6, 0, -0.4); // + TGeoTranslation* tr_lat1R_box = + new TGeoTranslation("tr_lat1R_box", 4.6, 0, -0.4); // tr_lat1R_box->RegisterYourself(); - TGeoTranslation* tr_lat2R_box = new TGeoTranslation("tr_lat2R_box", 9.6, 1.65, -0.4); // + TGeoTranslation* tr_lat2R_box = + new TGeoTranslation("tr_lat2R_box", 9.6, 1.65, -0.4); // tr_lat2R_box->RegisterYourself(); - TGeoTranslation* tr_lat3R_box = new TGeoTranslation("tr_lat3R_box", 18.53, 6.1, -0.4); // + /// TGeoTranslation* tr_lat3R_box = new + /// TGeoTranslation("tr_lat3R_box", 18.53, 6.1, -0.4); // + TGeoTranslation* tr_lat3R_box = + new TGeoTranslation("tr_lat3R_box", 17.35, 5.923, -0.4); // tr_lat3R_box->RegisterYourself(); - TGeoTranslation* tr_lat4R_box = new TGeoTranslation("tr_lat4R_box", 26.45, 10, -0.4); // + TGeoTranslation* tr_lat4R_box = + new TGeoTranslation("tr_lat4R_box", 26.45, 10, -0.4); // tr_lat4R_box->RegisterYourself(); - TGeoTranslation* tr_lat5R_box = new TGeoTranslation("tr_lat5R_box", 29.9, 11.6, -0.4); // + TGeoTranslation* tr_lat5R_box = + new TGeoTranslation("tr_lat5R_box", 29.9, 11.6, -0.4); // tr_lat5R_box->RegisterYourself(); // circular hole_1mbl. diameter=3.5 H9 Double_t radin_1mb = 0.; - Double_t radout_1mb = 0.175; // diameter 3.5mm _0.35 cm - Double_t high_1mb = 2.825; // dh=+/- 4 - TGeoTranslation* tr1_mb = new TGeoTranslation("tr1_mb", 18.48, 6.1, 0.); // right + Double_t radout_1mb = 0.175; // diameter 3.5mm _0.35 cm + Double_t high_1mb = 2.825; // dh=+/- 4 + TGeoTranslation* tr1_mb = + new TGeoTranslation("tr1_mb", 18.48, 6.1, 0.); // right tr1_mb->RegisterYourself(); - TGeoTranslation* tr2_mb = new TGeoTranslation("tr2_mb", 24.15, 8.9, 0.); // right + TGeoTranslation* tr2_mb = + new TGeoTranslation("tr2_mb", 24.15, 8.9, 0.); // right tr2_mb->RegisterYourself(); // circular hole_2mbl inclined and hole-up.diameter=M3 (3 mm)) prof , tar:8mm @@ -859,7 +1419,8 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) tr_mbr->RegisterYourself(); // hole up || hup - TGeoCombiTrans* combi_hup_mb = new TGeoCombiTrans(32.5, 12.6, 0, rot_90x); //y= + TGeoCombiTrans* combi_hup_mb = + new TGeoCombiTrans(32.5, 12.6, 0, rot_90x); // y= combi_hup_mb->SetName("combi_hup_mb"); combi_hup_mb->RegisterYourself(); @@ -870,33 +1431,56 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) new TGeoBBox("lat_box", x_lat_box / 2, y_lat_box / 2, z_lat_box / 2); // composite shape for rail_MB R + L - - // auto * MB_Shape_0 = new TGeoCompositeShape("MB_Shape_0"," V_MBL_0 - mb_box:tr_mb_box - hole_1mbl:tr1_mb + hole_1mbl:tr2_mb -hole_2mbl:combi_hup_mb "); - new TGeoCompositeShape("MB_Shape_0", "part_MBL_0 - mb_box:tr_mb_box - hole_1mbl:tr1_mb - hole_2mbl:combi_hup_mb"); - - new TGeoCompositeShape("MB_Shape_0L", "MB_Shape_0 - lat_box:tr_lat1L_box - lat_box:tr_lat2L_box - lat_box:tr_lat3L_box - lat_box:tr_lat4L_box - lat_box:tr_lat5L_box"); - - new TGeoCompositeShape("MB_Shape_0R", "MB_Shape_0 - lat_box:tr_lat1R_box - lat_box:tr_lat2R_box - lat_box:tr_lat3R_box - lat_box:tr_lat4R_box - lat_box:tr_lat5R_box"); - - new TGeoCompositeShape("MB_Shape_1L", "MB_Shape_0L:rot1_MBL_0 - hole_2mbl"); // one piece "completed" + auto* MB_Shape_0 = new TGeoCompositeShape( + "MB_Shape_0", " sup_MB_L - hole_2mbl:combi_hup_mb "); // new2020 + auto* MB_Shape_0L = new TGeoCompositeShape( + "MB_Shape_0L", "MB_Shape_0 - lat_box:tr_lat3L_box "); + auto* MB_Shape_0R = new TGeoCompositeShape( + "MB_Shape_0R", "MB_Shape_0 - lat_box:tr_lat3R_box "); + + // auto * MB_Shape_0 = new TGeoCompositeShape("MB_Shape_0"," V_MBL_0 - + // mb_box:tr_mb_box - hole_1mbl:tr1_mb + hole_1mbl:tr2_mb + // -hole_2mbl:combi_hup_mb "); + ////new TGeoCompositeShape("MB_Shape_0", "part_MBL_0 - mb_box:tr_mb_box - + /// hole_1mbl:tr1_mb - hole_2mbl:combi_hup_mb"); + + /// new TGeoCompositeShape("MB_Shape_0L", "MB_Shape_0 - lat_box:tr_lat1L_box - + /// lat_box:tr_lat2L_box - lat_box:tr_lat3L_box - lat_box:tr_lat4L_box - + /// lat_box:tr_lat5L_box"); + + /// new TGeoCompositeShape("MB_Shape_0R", "MB_Shape_0 - lat_box:tr_lat1R_box - + /// lat_box:tr_lat2R_box - lat_box:tr_lat3R_box - lat_box:tr_lat4R_box - + /// lat_box:tr_lat5R_box"); + + new TGeoCompositeShape( + "MB_Shape_1L", + "MB_Shape_0L:rot1_MBL_0 - hole_2mbl"); // one piece "completed" // left and right new TGeoCompositeShape("MB_Shape_1R", "MB_Shape_0R:rot1_MBL_0 - hole_2mbl"); - auto* MB_Shape_2 = new TGeoCompositeShape("MB_Shape_2", " MB_Shape_1L:tr_mbl + MB_Shape_1R:tr_mbr "); + auto* MB_Shape_2 = new TGeoCompositeShape( + "MB_Shape_2", " MB_Shape_1L:tr_mbl + MB_Shape_1R:tr_mbr "); - // TGeoVolume *sup_rail_MBL_vol0 = new TGeoVolume("SUPPORT_MBL_VOL0",MB_Shape_0,Al); - TGeoVolume* sup_rail_MBL_vol = new TGeoVolume("SUPPORT_MBL_VOL", MB_Shape_2, kMedAlu); + // TGeoVolume *sup_rail_MBL_vol0 = new + // TGeoVolume("SUPPORT_MBL_VOL0",MB_Shape_0,Al); + TGeoVolume* sup_rail_MBL_vol = + new TGeoVolume("SUPPORT_MBL_VOL", MB_Shape_2, malu5083); sup_rail_MBL->AddNode(sup_rail_MBL_vol, 1, rot_halfR); auto* stair = new TGeoVolumeAssembly("stair"); - stair->AddNode(sup_rail_MBL, 1, new TGeoTranslation(0, 0 - 28.8, 0 + 0.675)); - stair->AddNode(Cross_mft, 2, new TGeoTranslation(0, -28.8, 4.55 + 0.675)); - stair->AddNode(Cross_mb0, 3, new TGeoTranslation(0, 1.65 - 28.8, 9.55 + 0.675)); - stair->AddNode(Cross_mb0, 4, new TGeoTranslation(0, 6.1 - 28.8, 18.48 + 0.675)); - stair->AddNode(Cross_mft, 6, new TGeoTranslation(0, 10.0 - 28.8, 26.4 + 0.675)); - stair->AddNode(Cross_mft, 7, new TGeoTranslation(0, 11.6 - 28.8, 29.85 + 0.675)); + stair->AddNode(sup_rail_MBL, 1, + new TGeoTranslation(0, 0 - 28.8 - 0.4, 0 + 0.675)); + stair->AddNode(Cross_mft, 1, new TGeoTranslation(0, -28.8, 4.55 + 0.675)); + stair->AddNode(Cross_mft_2, 2, + new TGeoTranslation(0, 1.65 - 28.8, 9.55 + 0.675)); + stair->AddNode(Cross_mb0, 4, + new TGeoTranslation(0, 5.423 - 28.8, 17.35 + 0.675)); // 6.1 + stair->AddNode(Cross_mft_3, 5, + new TGeoTranslation(0, 11.7 - 28.8, 25.55 + 0.675)); + stair->AddNode(Cross_mft_4, 6, + new TGeoTranslation(0, 12.5 - 28.8, 29.05 + 0.675)); Double_t t_final_x; Double_t t_final_y; @@ -906,30 +1490,179 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Double_t r_final_y; Double_t r_final_z; + Double_t tyMB0; + Double_t tzMB0; + if (half == 0) { t_final_x = 0; - t_final_y = 0; - t_final_z = -80; + t_final_y = 0.0; + t_final_z = -80 - 0.675 - 0.15; r_final_x = 0; r_final_y = 0; r_final_z = 0; + + tyMB0 = -16.72; + tzMB0 = -(45.3 + 46.7) / 2; } if (half == 1) { t_final_x = 0; - t_final_y = 0; - t_final_z = -80; + t_final_y = 0.0; + t_final_z = -80 - 0.675 - 0.15; r_final_x = 0; r_final_y = 0; r_final_z = 180; + + tyMB0 = 16.72; + tzMB0 = -(45.3 + 46.7) / 2; } - auto* t_final = new TGeoTranslation("t_final", t_final_x, t_final_y, t_final_z); + auto* t_final = + new TGeoTranslation("t_final", t_final_x, t_final_y, t_final_z); auto* r_final = new TGeoRotation("r_final", r_final_x, r_final_y, r_final_z); auto* c_final = new TGeoCombiTrans(*t_final, *r_final); + // 9th new 2020 ELEMENT middle framework back ----------------------------- + auto* frame_back = new TGeoVolumeAssembly("frame_back"); + /// variables + // rectangular box1 to quit + Double_t x_box_fwb = 15.8; // dx= 7.2 cm + Double_t y_box_fwb = 5; + Double_t z_box_fwb = 1; + + // rectangular box2 to add + Double_t x_box2_fwb = 1.9; // dx= 7.2 cm + Double_t y_box2_fwb = 0.5; + Double_t z_box2_fwb = 0.6; + + ///// holes tub 1hole tranversal + Double_t radin_fwb = 25.75; + Double_t radout_fwb = 26.75; // diameter 3.5 H9 (0.35cm) + Double_t high_fwb = 0.6; /// + + // seg tub_back + Double_t radin_stub = 23.6; // 25.75 + Double_t radout_stub = 24.4; // 26.05 + Double_t high_stub = 0.6; + Double_t ang_in_stub = 288.9; // 270 + 19.56 + Double_t ang_fin_stub = 342.; // 360-17.56 + + TGeoRotation* rot_1hole_fwb = new TGeoRotation("rot_1hole_fwb", 0, 90, 0); + rot_1hole_fwb->RegisterYourself(); + /// h= hole + TGeoCombiTrans* acombi_fwb = new TGeoCombiTrans(5.2, 0, 0, rot_1hole_fwb); + acombi_fwb->SetName("acombi_1h_fwb"); + acombi_fwb->RegisterYourself(); + + TGeoTranslation* tr_box_y24 = + new TGeoTranslation("tr_box_y24", 0, -24., 0.); // + tr_box_y24->RegisterYourself(); + + TGeoTranslation* tr_box2_fwb = new TGeoTranslation( + "tr_box2_fwb", 24.4 - 1.9 / 2, -7.121 - 0.5 / 2, 0.); // + tr_box2_fwb->RegisterYourself(); + + TGeoRotation* rot_Z180_X180 = new TGeoRotation("rot_Z180_X180", 180, 180, 0); + rot_Z180_X180->RegisterYourself(); + + TGeoTranslation* tr_fb = + new TGeoTranslation("tr_fb", 0, -2.3 - 0.06, 13.85 + 0.675); // + tr_fb->RegisterYourself(); + + // + auto* q_box_fwb = + new TGeoBBox("q_box_fwb", x_box_fwb / 2, y_box_fwb / 2, z_box_fwb / 2); + auto* box2_fwb = + new TGeoBBox("box2_fwb", x_box2_fwb / 2, y_box2_fwb / 2, z_box2_fwb / 2); + auto* s_tub_fwb = + new TGeoTube("s_tub_fwb", radin_fwb, radout_fwb, high_fwb / 2); + // auto *s_ctub_fwb=new + // TGeoTube("S_ctub_fwb",radin_fwb,radout_fwb,high_2hole_mb0/2); + auto* s_stub_fwb = + new TGeoTubeSeg("s_stub_fwb", radin_stub, radout_stub, high_stub / 2, + ang_in_stub, ang_fin_stub); // r_in,r_out,dZ,ang,ang + + /// composite shape for mb0 + + auto* fwb_Shape_0 = new TGeoCompositeShape( + "fwb_Shape_0", + " s_stub_fwb - q_box_fwb:tr_box_y24 + box2_fwb:tr_box2_fwb "); + auto* fwb_Shape_1 = new TGeoCompositeShape( + "fwb_Shape_1", "fwb_Shape_0 + fwb_Shape_0:rot_Z180_X180"); + + auto* fwb_Volume = + new TGeoVolume("fwb_Volume", fwb_Shape_1, malu5083); // malu5083 + frame_back->AddNode(fwb_Volume, 1, tr_fb); + + //////////////////////////////////////////////// + /// 10 th colonne_support_MB012 new 2020 + auto* colonne_mb = new TGeoVolumeAssembly("colonne_mb"); + /// variables + // rectangular box + Double_t x_box_cmb = 1.9; // dx= 7.2 cm + Double_t y_box_cmb = 0.6; + Double_t z_box_cmb = 2.2033; + + ///// holes tub 1hole tranversal + Double_t radin_c_mb = 0.; + Double_t radout_c_mb = 0.3; // diameter 3.5 H9 (0.35cm) + Double_t high_c_mb = 2.2033; /// + + TGeoRotation* rot_1c_mb0 = new TGeoRotation("rot_1c_mb0", 0, 90, 0); + rot_1c_mb0->RegisterYourself(); + /// h= hole + TGeoCombiTrans* acombi_1c_mb0 = new TGeoCombiTrans(0.95, 0, 0, rot_1c_mb0); + acombi_1c_mb0->SetName("acombi_1c_mb0"); + acombi_1c_mb0->RegisterYourself(); + TGeoCombiTrans* bcombi_1c_mb0 = + new TGeoCombiTrans(-0.95, 0, 0, rot_1c_mb0); // y= + bcombi_1c_mb0->SetName("bcombi_1c_mb0"); + bcombi_1c_mb0->RegisterYourself(); + + // box to cut + Double_t x_boxq_cmb = 3.; // dx= 7.2 cm + Double_t y_boxq_cmb = 1.05; + Double_t z_boxq_cmb = 4.; + + TGeoRotation* rot_X19 = new TGeoRotation("rot_X19", 0, -19, 0); + rot_X19->RegisterYourself(); + TGeoCombiTrans* combi_qbox = + new TGeoCombiTrans(0, +2.1 / 2 + 0.5, 0, rot_X19); // x =8.3 , z= 1.125 + combi_qbox->SetName("combi_qbox"); + combi_qbox->RegisterYourself(); + + // shapes + + auto* box_cmb = + new TGeoBBox("box_cmb", x_box_cmb / 2, y_box_cmb / 2, z_box_cmb / 2); + auto* tub_cmb = + new TGeoTube("tub_cmb", radin_c_mb, radout_c_mb, high_c_mb / 2); + auto* boxq_cmb = + new TGeoBBox("boxq_cmb", x_boxq_cmb / 2, y_boxq_cmb / 2, z_boxq_cmb / 2); + + // auto *s_2hole_mb0=new + // TGeoTube("S_2HOLE_MB0",radin_2hole_mb0,radout_2hole_mb0,high_2hole_mb0/2); + + /// composite shape for mb0 + + auto* c_mb_Shape_0 = + new TGeoCompositeShape( + "box_cmb:rot_1c_mb0 + tub_cmb:acombi_1c_mb0 + " + "tub_cmb:bcombi_1c_mb0 - boxq_cmb:combi_qbox"); + + TGeoTranslation* tr_cmb = new TGeoTranslation( + "tr_cmb", 0, 5.923 - 28.8 + 2.2033 / 2 - 0.2, 17.35 + 0.675); // + tr_cmb->RegisterYourself(); + /////////////////// + // auto * cross_mb0_Volume = new + // TGeoVolume("cross_mb0_Volume",c_mb0_Shape_0,Al); // + auto* colonne_mb_Volume = + new TGeoVolume("colonne_mb_Volume", c_mb_Shape_0, malu5083); // malu5083 + colonne_mb->AddNode(colonne_mb_Volume, 1, tr_cmb); + + // auto* Half_3 = new TGeoVolumeAssembly("Half_3"); // Shell radii @@ -937,18 +1670,25 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) Float_t Shell_rmin = 37.5 + .7; // Rotations and translations - auto* tShell_0 = new TGeoTranslation("tShell_0", 0., 0., 3.1 + (25.15 + 1.) / 2.); - auto* tShell_1 = new TGeoTranslation("tShell_1", 0., 0., -1.6 - (25.15 + 1.) / 2.); - auto* tShellHole = new TGeoTranslation("tShellHole", 0., 0., 2. / 2. + (25.15 + 1.) / 2.); - auto* tShellHole_0 = new TGeoTranslation("tShellHole_0", 0., -6.9, -26.1 / 2. - 6.2 / 2. - .1); - auto* tShellHole_1 = new TGeoTranslation("tShellHole_1", 0., 0., -26.1 / 2. - 6.2 / 2. - .1); + auto* tShell_0 = + new TGeoTranslation("tShell_0", 0., 0., 3.1 + (25.15 + 1.) / 2.); + auto* tShell_1 = + new TGeoTranslation("tShell_1", 0., 0., -1.6 - (25.15 + 1.) / 2.); + auto* tShellHole = + new TGeoTranslation("tShellHole", 0., 0., 2. / 2. + (25.15 + 1.) / 2.); + auto* tShellHole_0 = + new TGeoTranslation("tShellHole_0", 0., -6.9, -26.1 / 2. - 6.2 / 2. - .1); + auto* tShellHole_1 = + new TGeoTranslation("tShellHole_1", 0., 0., -26.1 / 2. - 6.2 / 2. - .1); auto* tShell_Cut = new TGeoTranslation("tShell_Cut", 0., 25. / 2., 0.); auto* tShell_Cut_1 = new TGeoTranslation("tShell_Cut_1", -23., 0., -8.); - auto* tShell_Cut_1_inv = new TGeoTranslation("tShell_Cut_1_inv", 23., 0., -8.); + auto* tShell_Cut_1_inv = + new TGeoTranslation("tShell_Cut_1_inv", 23., 0., -8.); auto* Rz = new TGeoRotation("Rz", 50., 0., 0.); auto* Rz_inv = new TGeoRotation("Rz_inv", -50., 0., 0.); auto* RShell_Cut = new TGeoRotation("RShell_Cut", 90., 90. - 24., -7.5); - auto* RShell_Cut_inv = new TGeoRotation("RShell_Cut_inv", 90., 90. + 24., -7.5); + auto* RShell_Cut_inv = + new TGeoRotation("RShell_Cut_inv", 90., 90. + 24., -7.5); auto* cShell_Cut = new TGeoCombiTrans(*tShell_Cut_1, *RShell_Cut); auto* cShell_Cut_inv = new TGeoCombiTrans(*tShell_Cut_1_inv, *RShell_Cut_inv); @@ -968,50 +1708,529 @@ TGeoVolumeAssembly* HalfCone::createHalfCone(Int_t half) cShell_Cut_inv->RegisterYourself(); // Basic shapes for Half_3 - TGeoShape* Shell_0 = new TGeoTubeSeg("Shell_0", Shell_rmax / 2. - .1, Shell_rmax / 2., 6.2 / 2., 12., 168.); - TGeoShape* Shell_1 = new TGeoTubeSeg("Shell_1", Shell_rmin / 2. - .1, Shell_rmin / 2., 3.2 / 2., 0., 180.); - new TGeoConeSeg("Shell_2", (25.15 + 1.0) / 2., Shell_rmin / 2. - .1, Shell_rmin / 2., Shell_rmax / 2. - .1, Shell_rmax / 2., 0., 180.); - TGeoShape* Shell_3 = new TGeoTube("Shell_3", 0., Shell_rmin / 2. + .1, .1 / 2.); - TGeoShape* ShellHole_0 = new TGeoTrd1("ShellHole_0", 17.5 / 4., 42.5 / 4., 80. / 2., (25.15 + 1.) / 2.); - TGeoShape* ShellHole_1 = new TGeoBBox("ShellHole_1", 42.5 / 4., 80. / 2., 2. / 2. + 0.00001); - TGeoShape* ShellHole_2 = new TGeoBBox("ShellHole_2", 58.9 / 4., (Shell_rmin - 2.25) / 2., .4 / 2. + 0.00001); - TGeoShape* ShellHole_3 = new TGeoBBox("ShellHole_3", 80. / 4., (Shell_rmin - 11.6) / 2., .4 / 2. + 0.00001); + TGeoShape* Shell_0 = new TGeoTubeSeg("Shell_0", Shell_rmax / 2. - .1, + Shell_rmax / 2., 6.2 / 2., 12., 168.); + TGeoShape* Shell_1 = new TGeoTubeSeg("Shell_1", Shell_rmin / 2. - .1, + Shell_rmin / 2., 3.2 / 2., 0., 180.); + new TGeoConeSeg("Shell_2", (25.15 + 1.0) / 2., Shell_rmin / 2. - .1, + Shell_rmin / 2., Shell_rmax / 2. - .1, Shell_rmax / 2., 0., + 180.); + TGeoShape* Shell_3 = + new TGeoTube("Shell_3", 0., Shell_rmin / 2. + .1, .1 / 2.); + TGeoShape* ShellHole_0 = new TGeoTrd1("ShellHole_0", 17.5 / 4., 42.5 / 4., + 80. / 2., (25.15 + 1.) / 2.); + TGeoShape* ShellHole_1 = + new TGeoBBox("ShellHole_1", 42.5 / 4., 80. / 2., 2. / 2. + 0.00001); + TGeoShape* ShellHole_2 = new TGeoBBox( + "ShellHole_2", 58.9 / 4., (Shell_rmin - 2.25) / 2., .4 / 2. + 0.00001); + TGeoShape* ShellHole_3 = new TGeoBBox( + "ShellHole_3", 80. / 4., (Shell_rmin - 11.6) / 2., .4 / 2. + 0.00001); // For the extra cut, not sure if this is the shape (apprx. distances) TGeoShape* Shell_Cut_0 = new TGeoTube("Shell_Cut_0", 0., 3.5, 5. / 2.); - TGeoShape* Shell_Cut_1 = new TGeoBBox("Shell_Cut_1", 7. / 2., 25. / 2., 5. / 2.); + TGeoShape* Shell_Cut_1 = + new TGeoBBox("Shell_Cut_1", 7. / 2., 25. / 2., 5. / 2.); // Composite shapes for Half_3 - auto* Half_3_Shape_0 = new TGeoCompositeShape("Half_3_Shape_0", "Shell_Cut_0+Shell_Cut_1:tShell_Cut"); - new TGeoCompositeShape("Half_3_Shape_1", "Shell_2 - Half_3_Shape_0:cShell_Cut - Half_3_Shape_0:cShell_Cut_inv"); - auto* Half_3_Shape_2 = new TGeoCompositeShape("Half_3_Shape_2", "ShellHole_0+ShellHole_1:tShellHole"); - new TGeoCompositeShape("Half_3_Shape_3", "Shell_3:tShellHole_1 -(ShellHole_2:tShellHole_1 + ShellHole_3:tShellHole_0)"); - auto* Half_3_Shape_4 = new TGeoCompositeShape("Half_3_Shape_4", - "(Shell_0:tShell_0 + Half_3_Shape_1+ Shell_1:tShell_1) - (Half_3_Shape_2 + " - "Half_3_Shape_2:Rz + Half_3_Shape_2:Rz_inv)+Half_3_Shape_3"); - - auto* Half_3_Volume = new TGeoVolume("Half_3_Volume", Half_3_Shape_4, kMedAlu); - // Position of the piece relative to the origin which for this code is the center of the the Framework piece (See - // Half_2) + auto* Half_3_Shape_0 = new TGeoCompositeShape( + "Half_3_Shape_0", "Shell_Cut_0+Shell_Cut_1:tShell_Cut"); + new TGeoCompositeShape( + "Half_3_Shape_1", + "Shell_2 - Half_3_Shape_0:cShell_Cut - Half_3_Shape_0:cShell_Cut_inv"); + auto* Half_3_Shape_2 = new TGeoCompositeShape( + "Half_3_Shape_2", "ShellHole_0+ShellHole_1:tShellHole"); + new TGeoCompositeShape("Half_3_Shape_3", + "Shell_3:tShellHole_1 -(ShellHole_2:tShellHole_1 + " + "ShellHole_3:tShellHole_0)"); + auto* Half_3_Shape_4 = new TGeoCompositeShape( + "Half_3_Shape_4", + "(Shell_0:tShell_0 + Half_3_Shape_1+ Shell_1:tShell_1) - (Half_3_Shape_2 " + "+ " + "Half_3_Shape_2:Rz + Half_3_Shape_2:Rz_inv)+Half_3_Shape_3"); + + auto* Half_3_Volume = + new TGeoVolume("Half_3_Volume", Half_3_Shape_4, kMedAlu); + // Position of the piece relative to the origin which for this code is the + // center of the the Framework piece (See Half_2) // Half_3->AddNode(Half_3_Volume, 1, new TGeoTranslation(0., 0., -19.)); - TGeoRotation* rot_z180 = new TGeoRotation("rot_z180", 0, 180, 0); // orig: (180,0,0) + TGeoRotation* rot_z180 = + new TGeoRotation("rot_z180", 0, 180, 0); // orig: (180,0,0) rot_z180->RegisterYourself(); // in y = l_253.5 - 6. center in (0,6,0) - TGeoCombiTrans* combi_coat = new TGeoCombiTrans(0, 0, 19.5 - 0.45, rot_z180); // TGeoCombiTrans(0,0, -19.5,rot_z180) // -0.5 ->0.45 + TGeoCombiTrans* combi_coat = new TGeoCombiTrans(0, 0, 19.5 - 0.45, rot_z180); combi_coat->SetName("combi_coat"); combi_coat->RegisterYourself(); Half_3->AddNode(Half_3_Volume, 1, combi_coat); - // Half_3_Volume->SetLineColor(1); HalfConeVolume->AddNode(stair, 1, c_final); // HalfConeVolume->AddNode(base, 2, c_final); HalfConeVolume->AddNode(rail_L_R, 3, c_final); // R&L HalfConeVolume->AddNode(Fra_front, 4, c_final); - HalfConeVolume->AddNode(midle, 5, c_final); // - HalfConeVolume->AddNode(Half_3, 6, c_final); - // HalfConeVolume->AddNode(Half_3,8, new TGeoCombiTrans(0,0,0-0.5,rot_halfR)); //-0.675 + HalfConeVolume->AddNode(middle, 5, c_final); // + HalfConeVolume->AddNode(frame_back, 6, c_final); // + HalfConeVolume->AddNode(colonne_mb, 7, c_final); // + + //========================== Mother Boards + //========================================= + + // ============= MotherBoard 0 and 1 + Double_t mMB0cu[3]; + Double_t mMB0fr4; + Double_t mMB0pol; + Double_t mMB0epo; + // Sizes + mMB0cu[0] = {13.65}; + mMB0cu[1] = {0.00615}; // 122.5 microns * taux d'occupation 50% = 61.5 microns + mMB0cu[2] = {2.39}; + mMB0fr4 = 0.1; // 1 mm + mMB0pol = 0.0150; // 150 microns + mMB0epo = 0.0225; // 225 microns + // Materials + auto* mCu = gGeoManager->GetMedium("MFT_Cu$"); + auto* mFR4 = gGeoManager->GetMedium("MFT_FR4$"); + auto* mPol = gGeoManager->GetMedium("MFT_Polyimide$"); + auto* mEpo = gGeoManager->GetMedium("MFT_Epoxy$"); + auto* mInox = gGeoManager->GetMedium("MFT_Inox$"); + + auto* MotherBoard0 = new TGeoVolumeAssembly(Form("MotherBoard0_H%d", half)); + // 4 layers + TGeoVolume* vMB0cu = gGeoManager->MakeBox("vMB0cu", mCu, mMB0cu[0] / 2, + mMB0cu[1] / 2, mMB0cu[2] / 2); + TGeoVolume* vMB0fr4 = gGeoManager->MakeBox("vMB0fr4", mFR4, mMB0cu[0] / 2, + mMB0fr4 / 2, mMB0cu[2] / 2); + TGeoVolume* vMB0pol = gGeoManager->MakeBox("vMB0pol", mPol, mMB0cu[0] / 2, + mMB0pol / 2, mMB0cu[2] / 2); + TGeoVolume* vMB0epo = gGeoManager->MakeBox("vMB0epo", mEpo, mMB0cu[0] / 2, + mMB0epo / 2, mMB0cu[2] / 2); + // Screws = Head + Thread + TGeoVolume* vMB0screwH = gGeoManager->MakeTube("vMB0screwH", mInox, 0.0, + 0.7 / 2, 0.35 / 2); // tete + TGeoVolume* vMB0screwT = gGeoManager->MakeTube("vMB0screwT", mInox, 0.0, + 0.4 / 2, 1.2 / 2); // filetage + // Insert Sertitec + TGeoVolume* vMB0serti = gGeoManager->MakeTube("vMB0serti", mInox, 0.16 / 2, + 0.556 / 2, 0.15 / 2); // tete + + vMB0cu->SetLineColor(kRed); + vMB0fr4->SetLineColor(kBlack); + vMB0pol->SetLineColor(kGreen); + vMB0epo->SetLineColor(kBlue); + vMB0screwH->SetLineColor(kOrange); + vMB0screwT->SetLineColor(kOrange); + vMB0serti->SetLineColor(kOrange); + // Positioning the layers + MotherBoard0->AddNode(vMB0cu, 1); + Int_t signe; + if (half == 0) { + signe = -1; + } + if (half == 1) { + signe = +1; + } + auto* t_MB0fr4 = new TGeoTranslation("translation_fr4", 0.0, + signe * (mMB0fr4 + mMB0cu[1]) / 2, 0.0); + t_MB0fr4->RegisterYourself(); + MotherBoard0->AddNode(vMB0fr4, 1, t_MB0fr4); + auto* t_MB0pol = + new TGeoTranslation("translation_pol", 0.0, + signe * (mMB0fr4 + (mMB0cu[1] + mMB0pol) / 2), 0.0); + t_MB0pol->RegisterYourself(); + MotherBoard0->AddNode(vMB0pol, 1, t_MB0pol); + auto* t_MB0epo = new TGeoTranslation( + "translation_epo", 0.0, + signe * (mMB0fr4 + mMB0pol + (mMB0cu[1] + mMB0epo) / 2), 0.0); + t_MB0epo->RegisterYourself(); + MotherBoard0->AddNode(vMB0epo, 1, t_MB0epo); + auto* r_MB0screw = new TGeoRotation("rotation_vMB0screw", 0, 90, 0); + auto* t_MB0screwH1 = new TGeoCombiTrans( + mMB0cu[0] / 2 - 1.65, + signe * (mMB0fr4 + mMB0pol + mMB0epo + (mMB0cu[1] + 0.35) / 2), 0.0, + r_MB0screw); + t_MB0screwH1->RegisterYourself(); + auto* t_MB0screwT1 = new TGeoCombiTrans( + mMB0cu[0] / 2 - 1.65, -signe * (mMB0cu[1] + 1.2) / 2, 0.0, r_MB0screw); + t_MB0screwT1->RegisterYourself(); + auto* t_MB0screwH2 = new TGeoCombiTrans( + -(mMB0cu[0] / 2 - 1.65), + signe * (mMB0fr4 + mMB0pol + mMB0epo + (mMB0cu[1] + 0.35) / 2), 0.0, + r_MB0screw); + t_MB0screwH2->RegisterYourself(); + auto* t_MB0screwT2 = new TGeoCombiTrans( + -(mMB0cu[0] / 2 - 1.65), -signe * (mMB0cu[1] + 1.2) / 2, 0.0, r_MB0screw); + t_MB0screwT2->RegisterYourself(); + auto* t_MB0serti1 = new TGeoCombiTrans( + mMB0cu[0] / 2 - 2.65, + signe * (mMB0fr4 + mMB0pol + mMB0epo + (mMB0cu[1] + 0.153) / 2), 0.0, + r_MB0screw); + t_MB0serti1->RegisterYourself(); + auto* t_MB0serti2 = new TGeoCombiTrans( + -(mMB0cu[0] / 2 - 2.65), + signe * (mMB0fr4 + mMB0pol + mMB0epo + (mMB0cu[1] + 0.153) / 2), 0.0, + r_MB0screw); + t_MB0serti2->RegisterYourself(); + MotherBoard0->AddNode(vMB0screwH, 1, t_MB0screwH1); + MotherBoard0->AddNode(vMB0screwT, 1, t_MB0screwT1); + MotherBoard0->AddNode(vMB0screwH, 1, t_MB0screwH2); + MotherBoard0->AddNode(vMB0screwT, 1, t_MB0screwT2); + MotherBoard0->AddNode(vMB0serti, 1, t_MB0serti1); + MotherBoard0->AddNode(vMB0serti, 1, t_MB0serti2); + + // Positioning the board + auto* t_MB0 = new TGeoTranslation("translation_MB0", 0.0, tyMB0, tzMB0); + t_MB0->RegisterYourself(); + auto* t_MB1 = new TGeoTranslation( + "translation_MB1", 0.0, tyMB0, + tzMB0 - 3.3); // 3.3 cm is the interdistance between disk 0 and 1 + t_MB1->RegisterYourself(); + auto* r_MB0 = new TGeoRotation("rotation_MB0", 0.0, 0.0, 0.0); + r_MB0->RegisterYourself(); + auto* p_MB0 = new TGeoCombiTrans(*t_MB0, *r_MB0); + p_MB0->RegisterYourself(); + auto* p_MB1 = new TGeoCombiTrans(*t_MB1, *r_MB0); + p_MB1->RegisterYourself(); + // Final addition of the board + HalfConeVolume->AddNode(MotherBoard0, 1, p_MB0); + HalfConeVolume->AddNode(MotherBoard0, 1, p_MB1); + + auto* MotherBoard0_1 = + new TGeoVolumeAssembly(Form("MotherBoard0_1_H%d", half)); + // 4 layers + TGeoVolume* vMB0cu_1 = + gGeoManager->MakeBox("vMB0cu_1", mCu, 18.0 / 2, mMB0cu[1] / 2, 1.2 / 2); + TGeoVolume* vMB0fr4_1 = + gGeoManager->MakeBox("vMB0fr4_1", mFR4, 18.0 / 2, mMB0fr4 / 2, 1.2 / 2); + TGeoVolume* vMB0pol_1 = + gGeoManager->MakeBox("vMB0pol_1", mPol, 18.0 / 2, mMB0pol / 2, 1.2 / 2); + TGeoVolume* vMB0epo_1 = + gGeoManager->MakeBox("vMB0epo_1", mEpo, 18.0 / 2, mMB0epo / 2, 1.2 / 2); + vMB0cu_1->SetLineColor(kRed); + vMB0fr4_1->SetLineColor(kBlack); + vMB0pol_1->SetLineColor(kGreen); + vMB0epo_1->SetLineColor(kBlue); + + MotherBoard0_1->AddNode(vMB0cu_1, 1); + MotherBoard0_1->AddNode(vMB0fr4_1, 1, t_MB0fr4); + MotherBoard0_1->AddNode(vMB0pol_1, 1, t_MB0pol); + MotherBoard0_1->AddNode(vMB0epo_1, 1, t_MB0epo); + + // ================ MotherBoard 2 + Double_t mMB2cu[4]; + Double_t mMB2fr4; + Double_t mMB2pol; + Double_t mMB2epo; + // Sizes + mMB2cu[0] = {24.0}; + mMB2cu[1] = {21.0}; + mMB2cu[2] = {0.0079}; // 315 microns * taux d'occupation 25% = 79 microns + mMB2cu[3] = {8.5}; + mMB2fr4 = 0.2; // 2 mm + mMB2pol = 0.0175; // 175 microns + mMB2epo = 0.0075; // 75 microns + auto* MotherBoard2 = new TGeoVolumeAssembly(Form("MotherBoard2_H%d", half)); + // 4 layers + TGeoVolume* vMB2cu = + gGeoManager->MakeTrd1("vMB2cu", mCu, mMB2cu[0] / 2, mMB2cu[1] / 2, + mMB2cu[2] / 2, mMB2cu[3] / 2); + TGeoVolume* vMB2fr4 = + gGeoManager->MakeTrd1("vMB2fr4", mFR4, mMB2cu[0] / 2, mMB2cu[1] / 2, + mMB2fr4 / 2, mMB2cu[3] / 2); + TGeoVolume* vMB2pol = + gGeoManager->MakeTrd1("vMB2pol", mPol, mMB2cu[0] / 2, mMB2cu[1] / 2, + mMB2pol / 2, mMB2cu[3] / 2); + TGeoVolume* vMB2epo = + gGeoManager->MakeTrd1("vMB2epo", mEpo, mMB2cu[0] / 2, mMB2cu[1] / 2, + mMB2epo / 2, mMB2cu[3] / 2); + + vMB2cu->SetLineColor(kRed); + vMB2fr4->SetLineColor(kBlack); + vMB2pol->SetLineColor(kGreen); + vMB2epo->SetLineColor(kBlue); + + auto* t_MB2fr4 = new TGeoTranslation("translation_fr4", 0.0, + signe * (mMB2fr4 + mMB2cu[2]) / 2, 0.0); + t_MB2fr4->RegisterYourself(); + auto* t_MB2pol = + new TGeoTranslation("translation_pol", 0.0, + signe * (mMB2fr4 + (mMB2cu[2] + mMB2pol) / 2), 0.0); + t_MB2pol->RegisterYourself(); + auto* t_MB2epo = new TGeoTranslation( + "translation_epo", 0.0, + signe * (mMB2fr4 + mMB2pol + (mMB2cu[2] + mMB2epo) / 2), 0.0); + t_MB2epo->RegisterYourself(); + + MotherBoard2->AddNode(vMB2cu, 1); + MotherBoard2->AddNode(vMB2fr4, 1, t_MB2fr4); + MotherBoard2->AddNode(vMB2pol, 1, t_MB2pol); + MotherBoard2->AddNode(vMB2epo, 1, t_MB2epo); + for (Float_t i = -1; i < 3; i++) { + auto* t_MB2serti1 = new TGeoTranslation( + "translationMB2serti1", 8.5, -signe * (mMB2cu[2] + 0.153) / 2, 1.3 * i); + t_MB2serti1->RegisterYourself(); + auto* t_MB2serti2 = + new TGeoTranslation("translationMB2serti2", -8.5, + -signe * (mMB2cu[2] + 0.153) / 2, 1.3 * i); + t_MB2serti2->RegisterYourself(); + auto* p_MB2serti1 = new TGeoCombiTrans(*t_MB2serti1, *r_MB0screw); + p_MB2serti1->RegisterYourself(); + auto* p_MB2serti2 = new TGeoCombiTrans(*t_MB2serti2, *r_MB0screw); + p_MB2serti2->RegisterYourself(); + MotherBoard2->AddNode(vMB0serti, 1, p_MB2serti1); + MotherBoard2->AddNode(vMB0serti, 1, p_MB2serti2); + } + for (Float_t i = -2; i < 1; i++) { + auto* t_MB2serti3 = new TGeoTranslation( + "translationMB2serti3", 0.7, -signe * (mMB2cu[2] + 0.153) / 2, 1.3 * i); + t_MB2serti3->RegisterYourself(); + auto* t_MB2serti4 = + new TGeoTranslation("translationMB2serti4", -0.7, + -signe * (mMB2cu[2] + 0.153) / 2, 1.3 * i); + t_MB2serti4->RegisterYourself(); + auto* p_MB2serti3 = new TGeoCombiTrans(*t_MB2serti3, *r_MB0screw); + p_MB2serti3->RegisterYourself(); + auto* p_MB2serti4 = new TGeoCombiTrans(*t_MB2serti4, *r_MB0screw); + p_MB2serti4->RegisterYourself(); + MotherBoard2->AddNode(vMB0serti, 1, p_MB2serti3); + MotherBoard2->AddNode(vMB0serti, 1, p_MB2serti4); + } + + for (Float_t i = -2; i < 2; i++) { + auto* t_MB2serti5 = + new TGeoTranslation("translationMB2serti5", 7.0 * i + 3.5, + -signe * (mMB2cu[2] + 0.153) / 2, -2.5); + t_MB2serti5->RegisterYourself(); + auto* p_MB2serti5 = new TGeoCombiTrans(*t_MB2serti5, *r_MB0screw); + p_MB2serti5->RegisterYourself(); + auto* t_MB2serti6 = + new TGeoTranslation("translationMB2serti6", 7.0 * i + 3.5, + -signe * (mMB2cu[2] + 0.153) / 2, -3.5); + t_MB2serti6->RegisterYourself(); + auto* p_MB2serti6 = new TGeoCombiTrans(*t_MB2serti6, *r_MB0screw); + p_MB2serti6->RegisterYourself(); + MotherBoard2->AddNode(vMB0serti, 1, p_MB2serti5); + MotherBoard2->AddNode(vMB0serti, 1, p_MB2serti6); + } + // Connector board of MB0 on MB2 + auto* t_MotherBoard0_1 = + new TGeoTranslation("translation_MB0_1", 0.0, -signe * (-0.5), 3.5); + t_MotherBoard0_1->RegisterYourself(); + auto* t_MotherBoard0_2 = + new TGeoTranslation("translation_MB0_2", 0.0, -signe * (-0.5), 1.5); + t_MotherBoard0_2->RegisterYourself(); + MotherBoard2->AddNode(MotherBoard0_1, 1, t_MotherBoard0_1); + MotherBoard2->AddNode(MotherBoard0_1, 1, t_MotherBoard0_2); + // Positioning the board + auto* t_MotherBoard2 = new TGeoTranslation( + "translation_MB2", 0.0, + -signe * (-20.52 + mMB2fr4 + mMB2pol + mMB2epo + 2.2 * TMath::Sin(19.0)), + -62.8 + 2.2 * TMath::Cos(19.0)); + t_MotherBoard2->RegisterYourself(); + auto* r_MotherBoard2 = + new TGeoRotation("rotation_MB2", 0.0, -signe * (-19.0), 0.0); + r_MotherBoard2->RegisterYourself(); + auto* p_MB2 = new TGeoCombiTrans(*t_MotherBoard2, *r_MotherBoard2); + p_MB2->RegisterYourself(); + HalfConeVolume->AddNode(MotherBoard2, 1, p_MB2); + + //======================== Air ventilation ========================== + TGeoMedium* mVentilation = gGeoManager->GetMedium("MFT_Polypropylene$"); + // Bottom + TGeoSubtraction* vent_subB1; + Float_t lB1 = 5.5; // half length + Float_t xB1 = 0.3; + Float_t yB1 = 0.4; + auto* ventB1 = new TGeoBBox(Form("ventB1_H%d", half), xB1, yB1, lB1); + auto* ventB1_int = + new TGeoBBox(Form("ventB1_int_H%d", half), 0.2, 0.3, lB1 + 0.0001); + vent_subB1 = new TGeoSubtraction(ventB1, ventB1_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalB1 = + new TGeoCompositeShape(Form("vent_finalB1_H%d", half), vent_subB1); + auto* vent_B1 = + new TGeoVolume(Form("ventB1_H%d", half), vent_finalB1, mVentilation); + vent_B1->SetLineColor(kGray); + auto* t_airB1 = new TGeoTranslation("t_airB1", signe * (15.3 - xB1), + -8.75 - yB1 - 0.1, -45.570 - lB1); + t_airB1->RegisterYourself(); + auto* r_airB1 = new TGeoRotation("r_airB1", 0.0, 0.0, 0.0); + r_airB1->RegisterYourself(); + auto* p_airB1 = new TGeoCombiTrans(*t_airB1, *r_airB1); + p_airB1->RegisterYourself(); + HalfConeVolume->AddNode(vent_B1, 1, p_airB1); + + TGeoSubtraction* vent_subB2; + Float_t lB2 = 10.6; // half length + auto* ventB2 = new TGeoBBox(Form("ventB2_H%d", half), yB1, xB1, lB2); + auto* ventB2_int = + new TGeoBBox(Form("ventB2_int_H%d", half), 0.3, 0.2, lB2 + 0.0001); + vent_subB2 = new TGeoSubtraction(ventB2, ventB2_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalB2 = + new TGeoCompositeShape(Form("vent_finalB2_H%d", half), vent_subB2); + auto* vent_B2 = + new TGeoVolume(Form("ventB2_H%d", half), vent_finalB2, mVentilation); + vent_B2->SetLineColor(kGray); + Float_t theta = -signe * 32.; + Float_t phi = signe * 23.; + Float_t thetaRad = theta * TMath::Pi() / 180.; + Float_t phiRad = phi * TMath::Pi() / 180.; + auto* r_airB2 = new TGeoRotation("r_airB2", 90.0 - phi, theta, 0.); + r_airB2->RegisterYourself(); + Float_t XairB2 = + signe * + (15.3 + lB2 * TMath::Sin(TMath::Abs(thetaRad) * TMath::Cos(phiRad))); + Float_t YairB2 = + -8.75 - 2 * yB1 + TMath::Sin(phiRad) * TMath::Sin(thetaRad) * lB2 + 0.2; + Float_t ZairB2 = -45.570 - 2 * lB1 - TMath::Cos(thetaRad) * lB2 - 0.2; + auto* t_airB2 = new TGeoTranslation("t_airB2", XairB2, YairB2, ZairB2); + t_airB2->RegisterYourself(); + auto* p_airB2 = new TGeoCombiTrans(*t_airB2, *r_airB2); + p_airB2->RegisterYourself(); + HalfConeVolume->AddNode(vent_B2, 1, p_airB2); + + TGeoSubtraction* vent_subB3; + Float_t lB3 = 4.8; // half length + auto* ventB3 = new TGeoBBox(Form("ventB3_H%d", half), yB1, xB1, lB3); + auto* ventB3_int = + new TGeoBBox(Form("ventB3_int_H%d", half), 0.3, 0.2, lB3 + 0.0001); + vent_subB3 = new TGeoSubtraction(ventB3, ventB3_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalB3 = + new TGeoCompositeShape(Form("vent_finalB3_H%d", half), vent_subB3); + auto* vent_B3 = + new TGeoVolume(Form("ventB3_H%d", half), vent_finalB3, mVentilation); + vent_B3->SetLineColor(kGray); + auto* r_airB3 = new TGeoRotation("r_airB3", 90.0 - phi, theta, 0.); + r_airB3->RegisterYourself(); + Float_t XairB3 = + signe * + (15.3 + + (2 * lB2 - lB3) * TMath::Sin(TMath::Abs(thetaRad) * TMath::Cos(phiRad)) - + xB1); + Float_t YairB3 = -8.75 - 2 * yB1 + + TMath::Sin(phiRad) * TMath::Sin(thetaRad) * (2 * lB2 - lB3) + + 0.2 - 1.9 * yB1; + Float_t ZairB3 = + -45.570 - 2 * lB1 - TMath::Cos(thetaRad) * (2 * lB2 - lB3) - 0.2; + + auto* t_airB3 = new TGeoTranslation("t_airB3", XairB3, YairB3, ZairB3); + + t_airB3->RegisterYourself(); + auto* p_airB3 = new TGeoCombiTrans(*t_airB3, *r_airB3); + p_airB3->RegisterYourself(); + HalfConeVolume->AddNode(vent_B3, 1, p_airB3); + + TGeoSubtraction* vent_subB4; + Float_t lB4 = 4.5; // half length + Float_t xB4 = 0.3; + Float_t yB4 = 0.8; + auto* ventB4 = new TGeoBBox(Form("ventB4_H%d", half), yB4, xB4, lB4); + auto* ventB4_int = + new TGeoBBox(Form("ventB4_int_H%d", half), 0.7, 0.2, lB4 + 0.0001); + vent_subB4 = new TGeoSubtraction(ventB4, ventB4_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalB4 = + new TGeoCompositeShape(Form("vent_finalB4_H%d", half), vent_subB4); + auto* vent_B4 = + new TGeoVolume(Form("ventB3_H%d", half), vent_finalB4, mVentilation); + vent_B4->SetLineColor(kGray); + auto* r_airB4 = + new TGeoRotation("r_airB4", 90.0 - signe * 25., -signe * 5, 0.); + r_airB4->RegisterYourself(); + auto* t_airB4 = new TGeoTranslation( + "t_airB4", + XairB2 + + signe * (lB2 * TMath::Sin(TMath::Abs(thetaRad) * TMath::Cos(phiRad)) + + 0.4), + YairB2 + TMath::Sin(phiRad) * TMath::Sin(thetaRad) * lB2 - 0.6, + ZairB3 - TMath::Cos(thetaRad) * lB2 * 0.965); + t_airB4->RegisterYourself(); + auto* p_airB4 = new TGeoCombiTrans(*t_airB4, *r_airB4); + p_airB4->RegisterYourself(); + HalfConeVolume->AddNode(vent_B4, 1, p_airB4); + + // Top + TGeoSubtraction* vent_subT1; + auto* ventT1 = new TGeoBBox(Form("ventT1_H%d", half), xB1, yB1, lB1); + auto* ventT1_int = + new TGeoBBox(Form("ventT1_int_H%d", half), 0.2, 0.3, lB1 + 0.0001); + vent_subT1 = new TGeoSubtraction(ventB1, ventB1_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalT1 = + new TGeoCompositeShape(Form("vent_finalT1_H%d", half), vent_subT1); + auto* vent_T1 = + new TGeoVolume(Form("ventT1_H%d", half), vent_finalT1, mVentilation); + vent_T1->SetLineColor(kGray); + auto* t_airT1 = new TGeoTranslation("t_airT1", signe * (15.3 - xB1), + -(-8.75 - yB1 - 0.1), -45.570 - lB1); + t_airB1->RegisterYourself(); + auto* r_airT1 = new TGeoRotation("r_airT1", 0.0, 0.0, 0.0); + r_airT1->RegisterYourself(); + auto* p_airT1 = new TGeoCombiTrans(*t_airT1, *r_airT1); + p_airT1->RegisterYourself(); + HalfConeVolume->AddNode(vent_T1, 1, p_airT1); + + TGeoSubtraction* vent_subT2; + auto* ventT2 = new TGeoBBox(Form("ventT2_H%d", half), yB1, xB1, lB2); + auto* ventT2_int = + new TGeoBBox(Form("ventT2_int_H%d", half), 0.3, 0.2, lB2 + 0.0001); + vent_subT2 = new TGeoSubtraction(ventT2, ventT2_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalT2 = + new TGeoCompositeShape(Form("vent_finalT2_H%d", half), vent_subT2); + auto* vent_T2 = + new TGeoVolume(Form("ventT2_H%d", half), vent_finalT2, mVentilation); + vent_T2->SetLineColor(kGray); + theta = -signe * 32.; + phi = signe * 23.; + thetaRad = theta * TMath::Pi() / 180.; + phiRad = phi * TMath::Pi() / 180.; + auto* r_airT2 = new TGeoRotation("r_airT2", 90.0 - phi, -theta, 0.); + r_airT2->RegisterYourself(); + auto* t_airT2 = new TGeoTranslation("t_airT2", -XairB2, -YairB2, ZairB2); + t_airT2->RegisterYourself(); + auto* p_airT2 = new TGeoCombiTrans(*t_airT2, *r_airT2); + p_airT2->RegisterYourself(); + HalfConeVolume->AddNode(vent_T2, 1, p_airT2); + + TGeoSubtraction* vent_subT3; + auto* ventT3 = new TGeoBBox(Form("ventT3_H%d", half), yB1, xB1, lB3); + auto* ventT3_int = + new TGeoBBox(Form("ventT3_int_H%d", half), 0.3, 0.2, lB3 + 0.0001); + vent_subT3 = new TGeoSubtraction(ventT3, ventT3_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalT3 = + new TGeoCompositeShape(Form("vent_finalT3_H%d", half), vent_subT3); + auto* vent_T3 = + new TGeoVolume(Form("ventT3_H%d", half), vent_finalT3, mVentilation); + vent_T3->SetLineColor(kGray); + auto* r_airT3 = new TGeoRotation("r_airT3", 90.0 - phi, -theta, 0.); + r_airT3->RegisterYourself(); + auto* t_airT3 = new TGeoTranslation("t_airT3", -XairB3, -YairB3, ZairB3); + t_airT3->RegisterYourself(); + auto* p_airT3 = new TGeoCombiTrans(*t_airT3, *r_airT3); + p_airT3->RegisterYourself(); + HalfConeVolume->AddNode(vent_T3, 1, p_airT3); + + TGeoSubtraction* vent_subT4; + auto* ventT4 = new TGeoBBox(Form("ventT4_H%d", half), yB4, xB4, lB4); + auto* ventT4_int = + new TGeoBBox(Form("ventT4_int_H%d", half), 0.7, 0.2, lB4 + 0.0001); + vent_subT4 = new TGeoSubtraction(ventT4, ventT4_int, nullptr, nullptr); + TGeoCompositeShape* vent_finalT4 = + new TGeoCompositeShape(Form("vent_finalT4_H%d", half), vent_subT4); + auto* vent_T4 = + new TGeoVolume(Form("ventT4_H%d", half), vent_finalT4, mVentilation); + vent_T4->SetLineColor(kGray); + auto* r_airT4 = + new TGeoRotation("r_airT4", 90.0 - signe * 25., signe * 5, 0.); + r_airT4->RegisterYourself(); + auto* t_airT4 = new TGeoTranslation( + "t_airT4", + -(XairB2 + + signe * (lB2 * TMath::Sin(TMath::Abs(thetaRad) * TMath::Cos(phiRad)) + + 0.4)), + -(YairB2 + TMath::Sin(phiRad) * TMath::Sin(thetaRad) * lB2 - 0.6), + ZairB3 - TMath::Cos(thetaRad) * lB2 * 0.965); + t_airT4->RegisterYourself(); + auto* p_airT4 = new TGeoCombiTrans(*t_airT4, *r_airT4); + p_airT4->RegisterYourself(); + HalfConeVolume->AddNode(vent_T4, 1, p_airT4); + //=================================================================== return HalfConeVolume; } diff --git a/Detectors/ITSMFT/MFT/base/src/HalfDetector.cxx b/Detectors/ITSMFT/MFT/base/src/HalfDetector.cxx index 51f6b102166dc..b3a133dbae734 100644 --- a/Detectors/ITSMFT/MFT/base/src/HalfDetector.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HalfDetector.cxx @@ -21,7 +21,7 @@ #include "MFTBase/HalfDisk.h" #include "MFTBase/Geometry.h" #include "MFTBase/HalfDetector.h" -#include "MFTBase/PowerSupplyUnit.h" +//#include "MFTBase/PowerSupplyUnit.h" #include "MFTBase/MFTBaseParam.h" using namespace o2::mft; @@ -31,12 +31,12 @@ ClassImp(o2::mft::HalfDetector); /// \brief Default constructor //_____________________________________________________________________________ -HalfDetector::HalfDetector() : TNamed(), mHalfVolume(nullptr), mSegmentation(nullptr), mPSU(nullptr) {} +HalfDetector::HalfDetector() : TNamed(), mHalfVolume(nullptr), mSegmentation(nullptr) {} /// \brief Constructor //_____________________________________________________________________________ -HalfDetector::HalfDetector(HalfSegmentation* seg) : TNamed(), mHalfVolume(nullptr), mSegmentation(seg), mPSU(nullptr) +HalfDetector::HalfDetector(HalfSegmentation* seg) : TNamed(), mHalfVolume(nullptr), mSegmentation(seg) { Geometry* mftGeom = Geometry::instance(); @@ -49,7 +49,7 @@ HalfDetector::HalfDetector(HalfSegmentation* seg) : TNamed(), mHalfVolume(nullpt mHalfVolume = new TGeoVolumeAssembly(GetName()); - mPSU = new PowerSupplyUnit(); + //mPSU = new PowerSupplyUnit(); createHalfDisks(); } @@ -72,11 +72,4 @@ void HalfDetector::createHalfDisks() mHalfVolume->AddNode(halfDisk->getVolume(), halfDiskId, halfDiskSeg->getTransformation()); delete halfDisk; } - - if (!mftBaseParam.minimal && mftBaseParam.buildPSU) { - TGeoVolumeAssembly* mHalfPSU = mPSU->create(); - TGeoTranslation* tHalfPSU = new TGeoTranslation("tHalfPSU", 0, 0.4, -72.6 + 46.0); - tHalfPSU->RegisterYourself(); - mHalfVolume->AddNode(mHalfPSU, 0, tHalfPSU); - } } diff --git a/Detectors/ITSMFT/MFT/base/src/HalfDisk.cxx b/Detectors/ITSMFT/MFT/base/src/HalfDisk.cxx index a6d41e1122df3..6783e8440d0a0 100644 --- a/Detectors/ITSMFT/MFT/base/src/HalfDisk.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HalfDisk.cxx @@ -141,8 +141,9 @@ void HalfDisk::createLadders() for (Int_t iLadder = 0; iLadder < mSegmentation->getNLadders(); iLadder++) { LadderSegmentation* ladderSeg = mSegmentation->getLadder(iLadder); - if (!ladderSeg) + if (!ladderSeg) { Fatal("CreateLadders", Form("No Segmentation found for ladder %d ", iLadder), 0, 0); + } auto* ladder = new Ladder(ladderSeg); TGeoVolume* ladVol = ladder->createVolume(); diff --git a/Detectors/ITSMFT/MFT/base/src/HalfDiskSegmentation.cxx b/Detectors/ITSMFT/MFT/base/src/HalfDiskSegmentation.cxx index 252de65243f34..5b5651c566712 100644 --- a/Detectors/ITSMFT/MFT/base/src/HalfDiskSegmentation.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HalfDiskSegmentation.cxx @@ -60,10 +60,11 @@ HalfDiskSegmentation::HalfDiskSegmentation(const HalfDiskSegmentation& input) { // copy constructor - if (input.mLadders) + if (input.mLadders) { mLadders = new TClonesArray(*(input.mLadders)); - else + } else { mLadders = new TClonesArray("o2::mft::LadderSegmentation"); + } mLadders->SetOwner(kTRUE); } @@ -76,8 +77,9 @@ HalfDiskSegmentation::~HalfDiskSegmentation() { Clear(""); } void HalfDiskSegmentation::Clear(const Option_t* /*opt*/) { - if (mLadders) + if (mLadders) { mLadders->Delete(); + } delete mLadders; mLadders = nullptr; } @@ -184,7 +186,8 @@ void HalfDiskSegmentation::print(Option_t* opt) getTransformation()->Print(); if (opt && (strstr(opt, "ladder") || strstr(opt, "l"))) { - for (int i = 0; i < getNLadders(); i++) + for (int i = 0; i < getNLadders(); i++) { getLadder(i)->Print(opt); + } } } diff --git a/Detectors/ITSMFT/MFT/base/src/HalfSegmentation.cxx b/Detectors/ITSMFT/MFT/base/src/HalfSegmentation.cxx index 838961cafdd90..0d6521e6cf3d1 100644 --- a/Detectors/ITSMFT/MFT/base/src/HalfSegmentation.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HalfSegmentation.cxx @@ -37,8 +37,9 @@ HalfSegmentation::HalfSegmentation() : VSegmentation(), mHalfDisks(nullptr) {} HalfSegmentation::HalfSegmentation(const HalfSegmentation& source) : VSegmentation(source), mHalfDisks(nullptr) { - if (source.mHalfDisks) + if (source.mHalfDisks) { mHalfDisks = new TClonesArray(*(source.mHalfDisks)); + } } /// Constructor @@ -86,8 +87,9 @@ HalfSegmentation::HalfSegmentation(const Char_t* nameGeomFile, const Short_t id) HalfSegmentation::~HalfSegmentation() { - if (mHalfDisks) + if (mHalfDisks) { mHalfDisks->Delete(); + } delete mHalfDisks; } @@ -97,8 +99,9 @@ HalfSegmentation::~HalfSegmentation() void HalfSegmentation::Clear(const Option_t* /*opt*/) { - if (mHalfDisks) + if (mHalfDisks) { mHalfDisks->Delete(); + } delete mHalfDisks; mHalfDisks = nullptr; } diff --git a/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx b/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx index 2d2d123c51154..a42bd352d99d0 100644 --- a/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx @@ -237,15 +237,6 @@ void HeatExchanger::createManifold(Int_t disk) lengthTop2 = cornerRadiusTop[disk]; Double_t lengthMiddle1[5]; - /* - lengthMiddle1[0] = 6.71; - lengthMiddle1[1] = 6.71; - lengthMiddle1[2] = 6.71; - //lengthMiddle1[3] = 9.20; - //lengthMiddle1[4] = 9.20; - lengthMiddle1[3] = 6.71; - lengthMiddle1[4] = 6.71; - */ lengthMiddle1[0] = mSupportYDimensions[0][0]; lengthMiddle1[1] = mSupportYDimensions[1][0]; lengthMiddle1[2] = mSupportYDimensions[2][0]; @@ -256,8 +247,6 @@ void HeatExchanger::createManifold(Int_t disk) Double_t lengthBottom1; lengthBottom1 = lengthMiddle1[disk]; - auto* dummy = new TGeoBBox("dummy", 0, 0, 0); - auto* Top1 = new TGeoBBox(Form("Top1MF%d", disk), lengthTop1[disk] / 2, widthTop1[disk] / 2, thicknessTop[disk] / 2); auto* Top2 = new TGeoBBox(Form("Top2MF%d", disk), lengthTop2 / 2, widthTop2 / 2, thicknessTop[disk] / 2); auto* Top3 = new TGeoTube(Form("Top3MF%d", disk), 0, cornerRadiusTop[disk], thicknessTop[disk] / 2); @@ -273,8 +262,9 @@ void HeatExchanger::createManifold(Int_t disk) tTop[5] = new TGeoTranslation(Form("tTop6MF%d", disk), -lengthTop1[disk] / 2, widthTop1[disk] / 2 - cornerRadiusTop[disk], thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); tTop[6] = new TGeoTranslation(Form("tTop7MF%d", disk), -lengthTop1[disk] / 2, -(widthTop1[disk] / 2 - cornerRadiusTop[disk]), thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - for (Int_t i = 0; i < 7; ++i) + for (Int_t i = 0; i < 7; ++i) { tTop[i]->RegisterYourself(); + } TGeoTranslation* tMiddle1 = new TGeoTranslation(Form("tMiddle1MF%d", disk), 0, 0, 0); TGeoTranslation* tBottom1 = new TGeoTranslation(Form("tBottom1MF%d", disk), 0, 0, -(thicknessMiddle[disk] / 2 + thicknessBottom[disk] / 2)); @@ -328,8 +318,6 @@ void HeatExchanger::createManifold(Int_t disk) lengthBulge[0] = 0.395; lengthBulge[1] = 0.395; lengthBulge[2] = 0.395; - //lengthBulge[3] = 0.400; - //lengthBulge[4] = 0.400; lengthBulge[3] = 0.395; lengthBulge[4] = 0.395; @@ -372,10 +360,11 @@ void HeatExchanger::createManifold(Int_t disk) tPipe = new TGeoTranslation(Form("tPipe%dMF%d", iPipeRow + 1, disk), lengthMiddle1[disk] / 2 - offsetPipeRow[disk][iPipeRow], 0, 0); tPipe->RegisterYourself(); - if (iPipeRow == nPipeRow[disk] - 1) + if (iPipeRow == nPipeRow[disk] - 1) { namePipe += Form("shapePipeMF%d:tPipe%dMF%d", disk, iPipeRow + 1, disk); - else + } else { namePipe += Form("shapePipeMF%d:tPipe%dMF%d +", disk, iPipeRow + 1, disk); + } } TGeoCompositeShape* posiPipeOneCol = new TGeoCompositeShape(Form("posiPipeOneColMF%d", disk), namePipe); @@ -459,8 +448,9 @@ void HeatExchanger::createManifold(Int_t disk) tcoverBodyBathtub[5] = new TGeoTranslation(Form("tcoverBodyBathtub6MF%d", disk), -(lengthBodyBathtub1 / 2), widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); tcoverBodyBathtub[6] = new TGeoTranslation(Form("tcoverBodyBathtub7MF%d", disk), -(lengthBodyBathtub1 / 2), -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); - for (Int_t i = 0; i < 7; ++i) + for (Int_t i = 0; i < 7; ++i) { tcoverBodyBathtub[i]->RegisterYourself(); + } TGeoCompositeShape* shapeCoverBathtub = new TGeoCompositeShape(Form("shapeCoverBathtubMF%d", disk), Form("coverBodyBathtub1MF%d + coverBodyBathtub2MF%d:tcoverBodyBathtub2MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub3MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub4MF%d + coverBodyBathtub2MF%d:tcoverBodyBathtub5MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub6MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub7MF%d", disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk)); @@ -478,8 +468,6 @@ void HeatExchanger::createManifold(Int_t disk) lengthStep1[0] = 5.61; lengthStep1[1] = 5.61; lengthStep1[2] = 5.61; - //lengthStep1[3] = 7.90; - //lengthStep1[4] = 7.90; lengthStep1[3] = 5.61; lengthStep1[4] = 5.61; Double_t widthStep1[5]; @@ -532,12 +520,15 @@ void HeatExchanger::createManifold(Int_t disk) TGeoRotation* rcoverBodyStep = new TGeoRotation(Form("rcoverBodyStep4MF%d", disk), 45, 0, 0); rcoverBodyStep->RegisterYourself(); - TGeoCompositeShape* coverBodyStep4rotated = new TGeoCompositeShape(Form("coverBodyStep4rotatedMF%d", disk), Form("dummy + coverBodyStep4MF%d:rcoverBodyStep4MF%d", disk, disk)); + TGeoCombiTrans* combtcoverBodyStep[4]; + combtcoverBodyStep[3] = new TGeoCombiTrans(Form("combtcoverBodyStep4MF%d", disk), -(lengthStep1[disk] - lengthStep2) / 2, -widthStep1[disk] / 2, 0., rcoverBodyStep); + combtcoverBodyStep[3]->RegisterYourself(); - for (Int_t i = 0; i < 4; ++i) + for (Int_t i = 0; i < 4; ++i) { tcoverBodyStep[i]->RegisterYourself(); + } - TGeoCompositeShape* shapeStep = new TGeoCompositeShape(Form("shapeStepMF%d", disk), Form("coverBodyStep1MF%d:tcoverBodyStep1MF%d + coverBodyStep2MF%d:tcoverBodyStep2MF%d + coverBodyStep3MF%d:tcoverBodyStep3MF%d + coverBodyStep4rotatedMF%d:tcoverBodyStep4MF%d", disk, disk, disk, disk, disk, disk, disk, disk)); + TGeoCompositeShape* shapeStep = new TGeoCompositeShape(Form("shapeStepMF%d", disk), Form("coverBodyStep1MF%d:tcoverBodyStep1MF%d + coverBodyStep2MF%d:tcoverBodyStep2MF%d + coverBodyStep3MF%d:tcoverBodyStep3MF%d + coverBodyStep4MF%d:combtcoverBodyStep4MF%d", disk, disk, disk, disk, disk, disk, disk, disk)); TGeoTranslation* tcoverStep = new TGeoTranslation(Form("tcoverStepMF%d", disk), -(lengthMiddle1[disk] / 2 - (lengthStep1[disk] / 2 - lengthStep2 / 2)), (widthBody / 2 - widthStep1[disk] / 2), -(thicknessBody[disk] / 2 - thicknessStep1[disk] / 2)); @@ -572,12 +563,13 @@ void HeatExchanger::createManifold(Int_t disk) TGeoRotation* rcoverBodyBulgeSub = new TGeoRotation(Form("rcoverBodyBulgeSubMF%d", disk), 0, 90, 45); rcoverBodyBulgeSub->RegisterYourself(); - TGeoCompositeShape* coverBodyBulgeSubrotated = new TGeoCompositeShape(Form("coverBodyBulgeSubrotatedMF%d", disk), Form("dummy + coverBodyBulgeSubMF%d:rcoverBodyBulgeSubMF%d", disk, disk)); - TGeoTranslation* tcoverBodyBulgeSub = new TGeoTranslation(Form("tcoverBodyBulgeSubMF%d", disk), -lengthBulge[disk] / 2, 0, 0); tcoverBodyBulgeSub->RegisterYourself(); - TGeoCompositeShape* shapeBulge = new TGeoCompositeShape(Form("shapeBulgeMF%d", disk), Form("coverBodyBulgeMF%d - coverBodyBulgeSubrotatedMF%d:tcoverBodyBulgeSubMF%d", disk, disk, disk)); + TGeoCombiTrans* combtcoverBodyBulgeSub = new TGeoCombiTrans(Form("combtcoverBodyBulgeSubMF%d", disk), -lengthBulge[disk] / 2, 0, 0, rcoverBodyBulgeSub); + combtcoverBodyBulgeSub->RegisterYourself(); + + TGeoCompositeShape* shapeBulge = new TGeoCompositeShape(Form("shapeBulgeMF%d", disk), Form("coverBodyBulgeMF%d - coverBodyBulgeSubMF%d:combtcoverBodyBulgeSubMF%d", disk, disk, disk)); TGeoTranslation* tcoverBulge = new TGeoTranslation(Form("tcoverBulgeMF%d", disk), -(lengthMiddle1[disk] / 2 + lengthBulge[disk] / 2), -(widthBody / 2 - widthBulge[disk] / 2), 0); tcoverBulge->RegisterYourself(); @@ -604,15 +596,17 @@ void HeatExchanger::createManifold(Int_t disk) TGeoRotation* rshapeManifold2 = new TGeoRotation(Form("rshapeManifold2MF%d", disk), 0, 180, 0); rshapeManifold2->RegisterYourself(); - TGeoCompositeShape* shapeManifold2rotated = new TGeoCompositeShape(Form("shapeManifold2rotatedMF%d", disk), Form("dummy + shapeManifold2MF%d:rshapeManifold2MF%d", disk, disk)); - TGeoTranslation* tshapeManifold1 = new TGeoTranslation(Form("tshapeManifold1MF%d", disk), 0, 0, -((thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - (thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2)); tshapeManifold1->RegisterYourself(); + TGeoTranslation* tshapeManifold2 = new TGeoTranslation(Form("tshapeManifold2MF%d", disk), 0, 0, (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - thicknessBody[disk] / 2); tshapeManifold2->RegisterYourself(); - TGeoCompositeShape* shapeManifold = new TGeoCompositeShape("shapeManifold", Form("shapeManifold1MF%d:tshapeManifold1MF%d + shapeManifold2rotatedMF%d:tshapeManifold2MF%d", disk, disk, disk, disk)); + TGeoCombiTrans* combtshapeManifold2 = new TGeoCombiTrans(Form("combtshapeManifold2MF%d", disk), 0, 0, (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - thicknessBody[disk] / 2, rshapeManifold2); + combtshapeManifold2->RegisterYourself(); + + TGeoCompositeShape* shapeManifold = new TGeoCompositeShape("shapeManifold", Form("shapeManifold1MF%d:tshapeManifold1MF%d + shapeManifold2MF%d:combtshapeManifold2MF%d", disk, disk, disk, disk)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Manifold3 @@ -660,8 +654,6 @@ void HeatExchanger::createManifold(Int_t disk) TGeoTranslation* tPlug5sub = new TGeoTranslation(Form("tPlug5subMF%d", disk), 0, outerRadiusPlug5 * TMath::Cos(anglesubPlug5) + outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)) / 2, 0.); tPlug5sub->RegisterYourself(); - TGeoCompositeShape* disPlug5sub = new TGeoCompositeShape(Form("disPlug5subMF%d", disk), Form("dummy + plug5subMF%d:tPlug5subMF%d", disk, disk)); - TGeoRotation* rPlug5sub[nSidePlug5]; TString namePlug5 = Form("plug5mainMF%d", disk); @@ -669,7 +661,9 @@ void HeatExchanger::createManifold(Int_t disk) for (Int_t index = 0; index < nSidePlug5; ++index) { rPlug5sub[index] = new TGeoRotation(Form("rPlug5sub%dMF%d", index, disk), index * 60, 0, 0); rPlug5sub[index]->RegisterYourself(); - namePlug5 += Form(" - disPlug5subMF%d:rPlug5sub%dMF%d", disk, index, disk); + TGeoCombiTrans* combtPlug5sub = new TGeoCombiTrans(Form("combtPlug5subMF%d", disk), 0, outerRadiusPlug5 * TMath::Cos(anglesubPlug5) + outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)) / 2, 0., rPlug5sub[index]); + combtPlug5sub->RegisterYourself(); + namePlug5 += Form(" - plug5subMF%d:combtPlug5subMF%d", disk, disk); } TGeoCompositeShape* plug5 = new TGeoCompositeShape(Form("plug5MF%d", disk), namePlug5); @@ -730,8 +724,6 @@ void HeatExchanger::createManifold(Int_t disk) TGeoTranslation* tPlug12sub = new TGeoTranslation(Form("tPlug12subMF%d", disk), 0, outerRadiusPlug12 * TMath::Cos(anglesubPlug12) + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)) / 2, 0.); tPlug12sub->RegisterYourself(); - TGeoCompositeShape* disPlug12sub = new TGeoCompositeShape(Form("disPlug12subMF%d", disk), Form("dummy + plug12subMF%d:tPlug12subMF%d", disk, disk)); - TGeoRotation* rPlug12sub[nSidePlug12]; TString namePlug12 = Form("plug12mainMF%d", disk); @@ -739,7 +731,9 @@ void HeatExchanger::createManifold(Int_t disk) for (Int_t index = 0; index < nSidePlug12; ++index) { rPlug12sub[index] = new TGeoRotation(Form("rPlug12sub%dMF%d", index, disk), index * 60, 0, 0); rPlug12sub[index]->RegisterYourself(); - namePlug12 += Form(" - disPlug12subMF%d:rPlug12sub%dMF%d", disk, index, disk); + TGeoCombiTrans* combtPlug12sub = new TGeoCombiTrans(Form("combtPlug12subMF%d", disk), 0, outerRadiusPlug12 * TMath::Cos(anglesubPlug12) + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)) / 2, 0., rPlug12sub[index]); + combtPlug12sub->RegisterYourself(); + namePlug12 += Form(" - plug12subMF%d:combtPlug12subMF%d", disk, disk); } TGeoCompositeShape* plug12 = new TGeoCompositeShape(Form("plug12MF%d", disk), namePlug12); @@ -763,12 +757,13 @@ void HeatExchanger::createManifold(Int_t disk) TString namePlug = ""; for (Int_t ipart = 0; ipart < 12; ++ipart) { tPlug[ipart]->RegisterYourself(); - if (ipart == 0) + if (ipart == 0) { namePlug += Form("plug1MF%d:tPlug1MF%d", disk, disk); - else if (ipart == 11) + } else if (ipart == 11) { namePlug += Form(" - plug%dMF%d:tPlug%dMF%d", ipart + 1, disk, ipart + 1, disk); - else + } else { namePlug += Form(" + plug%dMF%d:tPlug%dMF%d", ipart + 1, disk, ipart + 1, disk); + } } TGeoCompositeShape* shapePlug = new TGeoCompositeShape(Form("shapePlugMF%d", disk), namePlug); @@ -799,8 +794,9 @@ void HeatExchanger::createManifold(Int_t disk) twater[5] = new TGeoTranslation(Form("twater6MF%d", disk), -(lengthBodyBathtub1 / 2), widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); twater[6] = new TGeoTranslation(Form("twater7MF%d", disk), -(lengthBodyBathtub1 / 2), -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); - for (Int_t i = 0; i < 7; ++i) + for (Int_t i = 0; i < 7; ++i) { twater[i]->RegisterYourself(); + } TGeoCompositeShape* shapeWater = new TGeoCompositeShape(Form("shapeCoverBathtubMF%d", disk), Form("water1MF%d + water2MF%d:twater2MF%d + water3MF%d:twater3MF%d + water3MF%d:twater4MF%d + water2MF%d:twater5MF%d +" @@ -826,8 +822,9 @@ void HeatExchanger::createManifold(Int_t disk) Double_t mfY = 6.8 - 0.1; // height, decrease to avoid overlap with support, to be solved, fm Double_t mfZ = 1.7 - 0.85; // thickness, decrease to avoid overlap with support, to be solved, fm Double_t fShift = 0; - if (disk == 3 || disk == 4) + if (disk == 3 || disk == 4) { fShift = 0.015; // to avoid overlap with the 2 curved water pipes on the 2 upstream chambers + } auto* MF01 = new TGeoVolume(Form("MF%d1", disk), shapeManifold, kMedPeek); auto* MFplug01 = new TGeoVolume(Form("MFplug%d1", disk), shapePlug, kMed_plug); @@ -848,7 +845,6 @@ void HeatExchanger::createManifold(Int_t disk) mHalfDisk->AddNode(MFplug01, 1, transformationplug1); transformationwater1 = - //new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift + (thicknessTotMF/2 - watherThickness[disk]/2) - (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk]),mfY / 2 + deltay, mZPlan[disk], rotation1); new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + (thicknessTotMF / 2 - watherThickness[disk] / 2) - (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk]), mHalfDiskGap + mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation1); mHalfDisk->AddNode(MFwater01, 1, transformationwater1); @@ -859,20 +855,17 @@ void HeatExchanger::createManifold(Int_t disk) TGeoRotation* rotation2 = new TGeoRotation(Form("rotation2MF%d", disk), 90, 90., 0.); transformation2 = - //new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift, -mfY / 2 - deltay, mZPlan[disk], rotation2); new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2, -mHalfDiskGap - mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation2); mHalfDisk->AddNode(MF02, 1, transformation2); TGeoRotation* rotationplug2 = new TGeoRotation(Form("rotationplug1MF%d", disk), -90, 90., 90.); transformationplug2 = - //new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift + thicknessTotMF/2 + refposPlug - (thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11),-mfY / 2 - deltay + ((lengthBody)/2 - holeOffset[3]), mZPlan[disk], rotationplug2); new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + thicknessTotMF / 2 + refposPlug - (thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11), -mHalfDiskGap - mSupportYDimensions[disk][0] / 2 + ((lengthBody) / 2 - holeOffset[3]), mZPlan[disk], rotationplug2); mHalfDisk->AddNode(MFplug02, 1, transformationplug2); transformationwater2 = - //new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift + ((thicknessTotMF/2 - watherThickness[disk]/2) - (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk])),-mfY / 2 - deltay, mZPlan[disk], rotation2); new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + ((thicknessTotMF / 2 - watherThickness[disk] / 2) - (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk])), -mHalfDiskGap - mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation2); mHalfDisk->AddNode(MFwater02, 1, transformationwater2); @@ -884,17 +877,18 @@ void HeatExchanger::createHalfDisk0(Int_t half) Int_t disk = 0; - if (half == Top) + if (half == Top) { printf("Creating MFT heat exchanger for disk0 top\n"); - else if (half == Bottom) + } else if (half == Bottom) { printf("Creating MFT heat exchanger for disk0 bottom\n"); - else + } else { printf("No valid option for MFT heat exchanger on disk0\n"); + } mCarbon = gGeoManager->GetMedium("MFT_CarbonFiber$"); mWater = gGeoManager->GetMedium("MFT_Water$"); - mRohacell = gGeoManager->GetMedium("MFT_Rohacell"); - mPipe = gGeoManager->GetMedium("MFT_Polyimide"); + mRohacell = gGeoManager->GetMedium("MFT_Rohacell$"); + mPipe = gGeoManager->GetMedium("MFT_Polyimide$"); mPeek = gGeoManager->GetMedium("MFT_PEEK$"); auto* cooling = new TGeoVolumeAssembly(Form("cooling_D0_H%d", half)); @@ -1262,10 +1256,12 @@ void HeatExchanger::createHalfDisk0(Int_t half) // Passage du beam pipe TGeoBoolNode* rohacellBase; - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { rohacellBase = new TGeoSubtraction(rohacellBase0, holeRohacell0, t01, t02); - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell0, t01, t02); + } auto* rh0 = new TGeoCompositeShape(Form("rohacellTore%d_D0_H%d", 0, half), rohacellBase); auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D0_H%d", half), rh0, mRohacell); @@ -1371,8 +1367,9 @@ void HeatExchanger::createHalfDisk0(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D0_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), partRohacell0, mRohacell); + } } if (Geometry::sGrooves == 1) { @@ -1381,8 +1378,9 @@ void HeatExchanger::createHalfDisk0(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D0_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + } } //=========================================================================================================== //=========================================================================================================== @@ -1395,8 +1393,9 @@ void HeatExchanger::createHalfDisk0(Int_t half) TGeoTranslation* tinsert0; TGeoVolume* insert0 = gGeoManager->MakeBox(Form("insert0_H%d_%d", half, ipart), mPeek, 1.0, 0.35 / 2., mRohacellThickness); Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap - 0.35 / 2.; - for (Int_t ip = 1; ip < mNPart[disk]; ip++) + for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; + } tinsert0 = new TGeoTranslation("tinsert0", 0., -ylocation, 0.); tinsert0->RegisterYourself(); mHalfDisk->AddNode(insert0, 0., tinsert0); @@ -1409,6 +1408,7 @@ void HeatExchanger::createHalfDisk0(Int_t half) mHalfDisk->AddNode(rohacellPlate, 1, transformation); createManifold(disk); + createCoolingPipes(half, disk); } //_____________________________________________________________________________ @@ -1417,17 +1417,18 @@ void HeatExchanger::createHalfDisk1(Int_t half) Int_t disk = 1; - if (half == Top) + if (half == Top) { printf("Creating MFT heat exchanger for disk1 top\n"); - else if (half == Bottom) + } else if (half == Bottom) { printf("Creating MFT heat exchanger for disk1 bottom\n"); - else + } else { printf("No valid option for MFT heat exchanger on disk1\n"); + } mCarbon = gGeoManager->GetMedium("MFT_CarbonFiber$"); mWater = gGeoManager->GetMedium("MFT_Water$"); - mRohacell = gGeoManager->GetMedium("MFT_Rohacell"); - mPipe = gGeoManager->GetMedium("MFT_Polyimide"); + mRohacell = gGeoManager->GetMedium("MFT_Rohacell$"); + mPipe = gGeoManager->GetMedium("MFT_Polyimide$"); mPeek = gGeoManager->GetMedium("MFT_PEEK$"); auto* cooling = new TGeoVolumeAssembly(Form("cooling_D1_H%d", half)); @@ -1800,10 +1801,12 @@ void HeatExchanger::createHalfDisk1(Int_t half) // Passage du beam pipe TGeoBoolNode* rohacellBase; - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { rohacellBase = new TGeoSubtraction(rohacellBase1, holeRohacell1, t11, t12); - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell1, t11, t12); + } auto* rh1 = new TGeoCompositeShape(Form("rohacellBase1_D1_H%d", half), rohacellBase); auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D1_H%d", half), rh1, mRohacell); @@ -1914,8 +1917,9 @@ void HeatExchanger::createHalfDisk1(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D1_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), partRohacell0, mRohacell); + } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { @@ -1923,8 +1927,9 @@ void HeatExchanger::createHalfDisk1(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D1_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + } } //=========================================================================================================== @@ -1938,8 +1943,9 @@ void HeatExchanger::createHalfDisk1(Int_t half) TGeoTranslation* tinsert1; TGeoVolume* insert1 = gGeoManager->MakeBox(Form("insert1_H%d_%d", half, ipart), mPeek, 1.0, 0.35 / 2., mRohacellThickness); Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap - 0.35 / 2.; - for (Int_t ip = 1; ip < mNPart[disk]; ip++) + for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; + } tinsert1 = new TGeoTranslation("tinsert1", 0., -ylocation, 0.); tinsert1->RegisterYourself(); mHalfDisk->AddNode(insert1, 0., tinsert1); @@ -1952,6 +1958,7 @@ void HeatExchanger::createHalfDisk1(Int_t half) mHalfDisk->AddNode(rohacellPlate, 2, transformation); createManifold(disk); + createCoolingPipes(half, disk); } //_____________________________________________________________________________ @@ -1960,17 +1967,18 @@ void HeatExchanger::createHalfDisk2(Int_t half) Int_t disk = 2; - if (half == Top) + if (half == Top) { printf("Creating MFT heat exchanger for disk2 top\n"); - else if (half == Bottom) + } else if (half == Bottom) { printf("Creating MFT heat exchanger for disk2 bottom\n"); - else + } else { printf("No valid option for MFT heat exchanger on disk2\n"); + } mCarbon = gGeoManager->GetMedium("MFT_CarbonFiber$"); mWater = gGeoManager->GetMedium("MFT_Water$"); - mRohacell = gGeoManager->GetMedium("MFT_Rohacell"); - mPipe = gGeoManager->GetMedium("MFT_Polyimide"); + mRohacell = gGeoManager->GetMedium("MFT_Rohacell$"); + mPipe = gGeoManager->GetMedium("MFT_Polyimide$"); mPeek = gGeoManager->GetMedium("MFT_PEEK$"); auto* cooling = new TGeoVolumeAssembly(Form("cooling_D2_H%d", half)); @@ -2437,10 +2445,12 @@ void HeatExchanger::createHalfDisk2(Int_t half) // Passage du beam pipe TGeoBoolNode* rohacellBase; - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { rohacellBase = new TGeoSubtraction(rohacellBase2, holeRohacell2, t21, t22); - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell2, t21, t22); + } auto* rh2 = new TGeoCompositeShape(Form("rohacellTore%d_D2_H%d", 0, half), rohacellBase); auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D2_H%d", half), rh2, mRohacell); @@ -2561,8 +2571,9 @@ void HeatExchanger::createHalfDisk2(Int_t half) auto* rhinit2 = new TGeoCompositeShape(Form("rhinit2%d_D2_H%d", 0, half), partRohacellini2); partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), rhinit2, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), partRohacell0, mRohacell); + } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { @@ -2572,8 +2583,9 @@ void HeatExchanger::createHalfDisk2(Int_t half) auto* rhinit2 = new TGeoCompositeShape(Form("rhinit2%d_D2_H%d", 0, half), partRohacellini2); partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), rhinit2, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + } } //=========================================================================================================== @@ -2587,8 +2599,9 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoTranslation* tinsert2; TGeoVolume* insert2 = gGeoManager->MakeBox(Form("insert2_H%d_%d", half, ipart), mPeek, 1.0, 0.40 / 2., mRohacellThickness); Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap - 0.80; - for (Int_t ip = 1; ip < mNPart[disk]; ip++) + for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; + } tinsert2 = new TGeoTranslation("tinsert2", 0., -ylocation, 0.); tinsert2->RegisterYourself(); mHalfDisk->AddNode(insert2, 0., tinsert2); @@ -2601,6 +2614,7 @@ void HeatExchanger::createHalfDisk2(Int_t half) mHalfDisk->AddNode(rohacellPlate, 2, transformation); createManifold(disk); + createCoolingPipes(half, disk); } //_____________________________________________________________________________ @@ -2609,17 +2623,18 @@ void HeatExchanger::createHalfDisk3(Int_t half) Int_t disk = 3; - if (half == Top) + if (half == Top) { printf("Creating MFT heat exchanger for disk3 top\n"); - else if (half == Bottom) + } else if (half == Bottom) { printf("Creating MFT heat exchanger for disk3 bottom\n"); - else + } else { printf("No valid option for MFT heat exchanger on disk3\n"); + } mCarbon = gGeoManager->GetMedium("MFT_CarbonFiber$"); mWater = gGeoManager->GetMedium("MFT_Water$"); - mRohacell = gGeoManager->GetMedium("MFT_Rohacell"); - mPipe = gGeoManager->GetMedium("MFT_Polyimide"); + mRohacell = gGeoManager->GetMedium("MFT_Rohacell$"); + mPipe = gGeoManager->GetMedium("MFT_Polyimide$"); mPeek = gGeoManager->GetMedium("MFT_PEEK$"); auto* cooling = new TGeoVolumeAssembly(Form("cooling_D3_H%d", half)); @@ -2987,10 +3002,12 @@ void HeatExchanger::createHalfDisk3(Int_t half) // ********************************************************************************************************** // Passage du beam pipe TGeoBoolNode* rohacellBase; - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { rohacellBase = new TGeoSubtraction(rohacellBase3, holeRohacell3, t31, t32); - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell3, t31, t32); + } auto* rh3 = new TGeoCompositeShape(Form("rohacellTore%d_D0_H%d", 0, half), rohacellBase); auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D3_H%d", half), rh3, mRohacell); @@ -3105,8 +3122,9 @@ void HeatExchanger::createHalfDisk3(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D3_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), partRohacell0, mRohacell); + } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { @@ -3114,8 +3132,9 @@ void HeatExchanger::createHalfDisk3(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D3_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + } } //=========================================================================================================== @@ -3128,8 +3147,9 @@ void HeatExchanger::createHalfDisk3(Int_t half) TGeoTranslation* tinsert3; TGeoVolume* insert3 = gGeoManager->MakeBox(Form("insert3_H%d_%d", half, ipart), mPeek, 4.0 / 2., 0.44 / 2., mRohacellThickness); Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap + 0.44 / 2. - ynotch; - for (Int_t ip = 1; ip < mNPart[disk]; ip++) + for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; + } tinsert3 = new TGeoTranslation("tinsert3", 0., -ylocation, 0.); tinsert3->RegisterYourself(); mHalfDisk->AddNode(insert3, 0., tinsert3); @@ -3143,6 +3163,7 @@ void HeatExchanger::createHalfDisk3(Int_t half) mHalfDisk->AddNode(rohacellPlate, 2, transformation); createManifold(disk); + createCoolingPipes(half, disk); } //_____________________________________________________________________________ @@ -3151,17 +3172,18 @@ void HeatExchanger::createHalfDisk4(Int_t half) Int_t disk = 4; - if (half == Top) + if (half == Top) { printf("Creating MFT heat exchanger for disk4 top\n"); - else if (half == Bottom) + } else if (half == Bottom) { printf("Creating MFT heat exchanger for disk4 bottom\n"); - else + } else { printf("No valid option for MFT heat exchanger on disk4\n"); + } mCarbon = gGeoManager->GetMedium("MFT_CarbonFiber$"); mWater = gGeoManager->GetMedium("MFT_Water$"); - mRohacell = gGeoManager->GetMedium("MFT_Rohacell"); - mPipe = gGeoManager->GetMedium("MFT_Polyimide"); + mRohacell = gGeoManager->GetMedium("MFT_Rohacell$"); + mPipe = gGeoManager->GetMedium("MFT_Polyimide$"); mPeek = gGeoManager->GetMedium("MFT_PEEK$"); auto* cooling = new TGeoVolumeAssembly(Form("cooling_D4_H%d", half)); @@ -3534,10 +3556,12 @@ void HeatExchanger::createHalfDisk4(Int_t half) // Passage du beam pipe TGeoBoolNode* rohacellBase; - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { rohacellBase = new TGeoSubtraction(rohacellBase4, holeRohacell4, t41, t42); - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell4, t41, t42); + } auto* rh4 = new TGeoCompositeShape(Form("rohacellTore%d_D4_H%d", 0, half), rohacellBase); auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D4_H%d", half), rh4, mRohacell); @@ -3652,8 +3676,9 @@ void HeatExchanger::createHalfDisk4(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D4_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), partRohacell0, mRohacell); + } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { @@ -3661,8 +3686,9 @@ void HeatExchanger::createHalfDisk4(Int_t half) auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D4_H%d", 0, half), partRohacellini); partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), rhinit, mRohacell); } - if (ipart < (mNPart[disk] - 1)) + if (ipart < (mNPart[disk] - 1)) { partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + } } //=========================================================================================================== //=========================================================================================================== @@ -3674,8 +3700,9 @@ void HeatExchanger::createHalfDisk4(Int_t half) TGeoTranslation* tinsert4; TGeoVolume* insert4 = gGeoManager->MakeBox(Form("insert4_H%d_%d", half, ipart), mPeek, 4.0 / 2., 0.44 / 2., mRohacellThickness); Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap + 0.44 / 2. - ynotch; - for (Int_t ip = 1; ip < mNPart[disk]; ip++) + for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; + } tinsert4 = new TGeoTranslation("tinsert4", 0., -ylocation, 0.); tinsert4->RegisterYourself(); mHalfDisk->AddNode(insert4, 0., tinsert4); @@ -3689,6 +3716,484 @@ void HeatExchanger::createHalfDisk4(Int_t half) mHalfDisk->AddNode(rohacellPlate, 2, transformation); createManifold(disk); + createCoolingPipes(half, disk); +} + +//_____________________________________________________________________________ +void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) +{ + mPipe = gGeoManager->GetMedium("MFT_Polyurethane$"); + mWater = gGeoManager->GetMedium("MFT_Water$"); + Float_t length1; + Float_t length2; + Float_t rin; + Float_t rout; + TGeoVolume* Tube1 = nullptr; + TGeoVolume* Torus1 = nullptr; + TGeoVolume* TubeW1 = nullptr; + TGeoVolume* TorusW1 = nullptr; + TGeoRotation* rTorus1 = nullptr; + TGeoCombiTrans* transfoTorus1 = nullptr; + Float_t radius1; + //----------------------------------------------------------------- + if (disk == 0 || disk == 1 || disk == 2) { + auto* mCoolingPipe1 = new TGeoVolumeAssembly(Form("cooling_pipe1_H%d", half)); + if (disk == 0) { + length1 = 5.3; + } + if (disk == 1) { + length1 = 4.87; + } + if (disk == 2) { + length1 = 4.45; + } + rin = 0.25 / 2; + rout = 0.4 / 2; + Tube1 = gGeoManager->MakeTube(Form("Tube1_H%d_D%d", half, disk), mPipe, rin, rout, length1 / 2); + TubeW1 = gGeoManager->MakeTube(Form("TubeW1_H%d_D%d", half, disk), mWater, 0., rin, length1 / 2); + TGeoTranslation* tTube1 = new TGeoTranslation(0.0, 0.0, 0.0); + tTube1->RegisterYourself(); + + radius1 = 0.4; + Torus1 = gGeoManager->MakeTorus(Form("Torus1_H%d_D%d", half, disk), mPipe, radius1, rin, rout, 0., 90.); + TorusW1 = gGeoManager->MakeTorus(Form("TorusW1_H%d_D%d", half, disk), mWater, radius1, 0., rin, 0., 90.); + rTorus1 = new TGeoRotation("rotationTorus1", 0.0, 90.0, 0.0); + rTorus1->RegisterYourself(); + transfoTorus1 = new TGeoCombiTrans(-radius1, 0., length1 / 2, rTorus1); + transfoTorus1->RegisterYourself(); + + if (disk == 0) { + length2 = 13.2; + } + if (disk == 1) { + length2 = 9.7; + } + if (disk == 2) { + length2 = 6.1; + } + TGeoVolume* Tube2 = gGeoManager->MakeTube(Form("Tube2_H%d_D%d", half, disk), mPipe, rin, rout, length2 / 2); + TGeoVolume* TubeW2 = gGeoManager->MakeTube(Form("TubeW2_H%d_D%d", half, disk), mWater, 0., rin, length2 / 2); + TGeoRotation* rTube2 = new TGeoRotation("rotationTube2", 90.0, 90.0, 0.0); + rTube2->RegisterYourself(); + TGeoCombiTrans* transfoTube2 = new TGeoCombiTrans(-length2 / 2 - radius1, 0., length1 / 2 + radius1, rTube2); + transfoTube2->RegisterYourself(); + + Float_t radius2 = 4.; + if (disk == 2) { + radius2 = 3.5; + } + TGeoVolume* Torus2 = gGeoManager->MakeTorus(Form("Torus2_H%d_D%d", half, disk), mPipe, radius2, rin, rout, 0., -90.); + TGeoVolume* TorusW2 = gGeoManager->MakeTorus(Form("TorusW2_H%d_D%d", half, disk), mWater, radius2, 0., rin, 0., -90.); + TGeoRotation* rTorus2 = new TGeoRotation("rotationTorus2", 180.0, 0.0, 0.0); + rTorus2->RegisterYourself(); + TGeoCombiTrans* transfoTorus2 = new TGeoCombiTrans(-length2 - radius1, -radius2, length1 / 2 + radius1, rTorus2); + transfoTorus2->RegisterYourself(); + + Float_t length3; + if (disk == 0) { + length3 = 3.9; + } + if (disk == 1) { + length3 = 3.85; + } + if (disk == 2) { + length3 = 4.25; + } + TGeoVolume* Tube3 = gGeoManager->MakeTube(Form("Tube3_H%d_D%d", half, disk), mPipe, rin, rout, length3 / 2); + TGeoVolume* TubeW3 = gGeoManager->MakeTube(Form("TubeW3_H%d_D%d", half, disk), mWater, 0., rin, length3 / 2); + TGeoRotation* rTube3 = new TGeoRotation("rotationTube3", 0.0, -90.0, 0.0); + rTube3->RegisterYourself(); + TGeoCombiTrans* transfoTube3 = new TGeoCombiTrans(-length2 - radius2 - radius1, -radius2 - length3 / 2, length1 / 2 + radius1, rTube3); + transfoTube3->RegisterYourself(); + + Float_t length4 = 10.; //12.5; // one single pipe instead of 3 pipes coming from the 3 first disks + Float_t rin4 = 0.216; + Float_t rout4 = 0.346; + TGeoVolume* Tube4 = gGeoManager->MakeTube(Form("Tube4_H%d_D%d", half, disk), mPipe, rin4, rout4, length4 / 2); + TGeoVolume* TubeW4 = gGeoManager->MakeTube(Form("TubeW4_H%d_D%d", half, disk), mWater, 0., rin4, length4 / 2); + Float_t theta4 = 19.; // horizontal plane angle + Float_t phi4 = 37; // vertical plane angle + TGeoRotation* rTube4 = new TGeoRotation("rotationTube4", 90.0 + theta4, 90.0 + phi4, 0.0); + rTube4->RegisterYourself(); + // next line, the x and z axis are reversed in the location... + Float_t xTube4 = length1 / 2. + radius1 + TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Sin(phi4 * TMath::DegToRad()) * length4 / 2 * 0.8; + Float_t yTube4 = -radius2 - length3 - TMath::Sin(theta4 * TMath::DegToRad()) * length4 / 2 * 0.8 - 0.2; + Float_t zTube4 = -radius1 - length2 - radius2 - TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Cos(phi4 * TMath::DegToRad()) * length4 / 2 * 0.8; + TGeoCombiTrans* transfoTube4 = new TGeoCombiTrans(zTube4, yTube4 - 0.2, xTube4 - 0.1, rTube4); + transfoTube4->RegisterYourself(); + + Float_t length5 = 13.; //14.5; // one single pipe instead of 5 pipes + Double_t theta = 180. * TMath::Pi() / 180.; + Double_t phi = 0. * TMath::Pi() / 180.; + Double_t nlow[3]; + nlow[0] = TMath::Sin(theta) * TMath::Cos(phi); + nlow[1] = TMath::Sin(theta) * TMath::Sin(phi); + nlow[2] = TMath::Cos(theta); + theta = 15. * TMath::Pi() / 180.; + phi = -90. * TMath::Pi() / 180.; + Double_t nhi[3]; + nhi[0] = TMath::Sin(theta) * TMath::Cos(phi); + nhi[1] = TMath::Sin(theta) * TMath::Sin(phi); + nhi[2] = TMath::Cos(theta); + Float_t rin5 = 0.278; + Float_t rout5 = 0.447; + TGeoVolume* Tube5 = gGeoManager->MakeCtub(Form("Tube5_H%d_D%d", half, disk), mPipe, rin5, rout5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoVolume* TubeW5 = gGeoManager->MakeCtub(Form("TubeW5_H%d_D%d", half, disk), mWater, 0., rin5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + Float_t theta5 = 8.5; // angle from the "horizontal" plane x,z + Float_t phi5 = 12.5; // "azimutal" angle + TGeoRotation* rTube5 = new TGeoRotation("rotationTube5", 90.0 + theta5, 90.0 + phi5, 0.0); + rTube5->RegisterYourself(); + Float_t xTube5 = xTube4 + TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Sin(phi4 * TMath::DegToRad()) * length4 / 2 + TMath::Cos(theta5 * TMath::DegToRad()) * TMath::Sin(phi5 * TMath::DegToRad()) * length5 / 2 * 1.03; + Float_t yTube5 = yTube4 - TMath::Sin(theta4 * TMath::DegToRad()) * length4 / 2 - TMath::Sin(theta5 * TMath::DegToRad()) * length5 / 2 * 1.03 + 0.2; + Float_t zTube5 = zTube4 - TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Cos(phi4 * TMath::DegToRad()) * length4 / 2 - TMath::Cos(theta5 * TMath::DegToRad()) * TMath::Cos(phi5 * TMath::DegToRad()) * length5 / 2 * 1.03; + TGeoCombiTrans* transfoTube5 = new TGeoCombiTrans(zTube5, yTube5, xTube5, rTube5); + transfoTube5->RegisterYourself(); + + Tube1->SetLineColor(kGray); + Torus1->SetLineColor(kGray); + Tube2->SetLineColor(kGray); + Torus2->SetLineColor(kGray); + Tube3->SetLineColor(kGray); + Tube4->SetLineColor(kGray); + Tube5->SetLineColor(kGray); + TubeW3->SetLineColor(kBlue); + TubeW4->SetLineColor(kBlue); + TubeW5->SetLineColor(kBlue); + + mCoolingPipe1->AddNode(Tube1, 1, tTube1); + mCoolingPipe1->AddNode(Torus1, 1, transfoTorus1); + mCoolingPipe1->AddNode(Tube2, 1, transfoTube2); + mCoolingPipe1->AddNode(Torus2, 1, transfoTorus2); + mCoolingPipe1->AddNode(Tube3, 1, transfoTube3); + mCoolingPipe1->AddNode(TubeW1, 1, tTube1); + mCoolingPipe1->AddNode(TorusW1, 1, transfoTorus1); + mCoolingPipe1->AddNode(TubeW2, 1, transfoTube2); + mCoolingPipe1->AddNode(TorusW2, 1, transfoTorus2); + mCoolingPipe1->AddNode(TubeW3, 1, transfoTube3); + + if (disk == 0) { // to create only one time Tube4 and Tube5 + mCoolingPipe1->AddNode(Tube4, 1, transfoTube4); + mCoolingPipe1->AddNode(Tube5, 1, transfoTube5); + mCoolingPipe1->AddNode(TubeW4, 1, transfoTube4); + mCoolingPipe1->AddNode(TubeW5, 1, transfoTube5); + } + + //----------------------------------------------------------------- + auto* mCoolingPipe2 = new TGeoVolumeAssembly(Form("cooling_pipe2_H%d_D%d", half, disk)); + TGeoVolume* Tube1p = gGeoManager->MakeTube(Form("Tube1p_H%d_D%d", half, disk), mPipe, rin, rout, length1 / 2); + TGeoVolume* TubeW1p = gGeoManager->MakeTube(Form("TubeW1p_H%d_D%d", half, disk), mWater, 0., rin, length1 / 2); + + TGeoVolume* Torus1p = gGeoManager->MakeTorus(Form("Torus1p_H%d_D%d", half, disk), mPipe, radius1, rin, rout, 0., 90.); + TGeoVolume* TorusW1p = gGeoManager->MakeTorus(Form("TorusW1p_H%d_D%d", half, disk), mWater, radius1, 0., rin, 0., 90.); + + TGeoVolume* Tube2p = gGeoManager->MakeTube(Form("Tube2p_H%d_D%d", half, disk), mPipe, rin, rout, length2 / 2); + TGeoVolume* TubeW2p = gGeoManager->MakeTube(Form("TubeW2p_H%d_D%d", half, disk), mWater, 0., rin, length2 / 2); + + TGeoVolume* Torus2p = gGeoManager->MakeTorus(Form("Torus2p_H%d_D%d", half, disk), mPipe, radius2, rin, rout, 0., 90.); + TGeoVolume* TorusW2p = gGeoManager->MakeTorus(Form("TorusW2p_H%d_D%d", half, disk), mWater, radius2, 0., rin, 0., 90.); + + TGeoVolume* Tube3p = gGeoManager->MakeTube(Form("Tube3p_H%d_D%d", half, disk), mPipe, rin, rout, length3 / 2); + TGeoVolume* TubeW3p = gGeoManager->MakeTube(Form("TubeW3p_H%d_D%d", half, disk), mWater, 0., rin, length3 / 2); + + TGeoRotation* rTorus2p = new TGeoRotation("rotationTorus2p", 180.0, 0.0, 0.0); + rTorus2p->RegisterYourself(); + TGeoCombiTrans* transfoTorus2p = new TGeoCombiTrans(-length2 - radius1, radius2, length1 / 2 + radius1, rTorus2p); + transfoTorus2p->RegisterYourself(); + TGeoCombiTrans* transfoTube3p = new TGeoCombiTrans(-length2 - radius2 - radius1, radius2 + length3 / 2, length1 / 2 + radius1, rTube3); + transfoTube3p->RegisterYourself(); + TGeoRotation* rTube4p = new TGeoRotation(Form("rotationTube4p_H%d_D%d", half, disk), 90.0 - theta4, phi4 - 90.0, 0.0); + rTube4p->RegisterYourself(); + + TGeoCombiTrans* transfoTube4p = new TGeoCombiTrans(zTube4, -yTube4 + 0.2, xTube4 - 0.1, rTube4p); + transfoTube4p->RegisterYourself(); + + Tube1p->SetLineColor(kGray); + Torus1p->SetLineColor(kGray); + Tube2p->SetLineColor(kGray); + Torus2p->SetLineColor(kGray); + Tube3p->SetLineColor(kGray); + TubeW3p->SetLineColor(kBlue); + + mCoolingPipe2->AddNode(Tube1p, 1, tTube1); + mCoolingPipe2->AddNode(Torus1p, 1, transfoTorus1); + mCoolingPipe2->AddNode(Tube2p, 1, transfoTube2); + mCoolingPipe2->AddNode(Torus2p, 1, transfoTorus2p); + mCoolingPipe2->AddNode(Tube3p, 1, transfoTube3p); + mCoolingPipe2->AddNode(TubeW1p, 1, tTube1); + mCoolingPipe2->AddNode(TorusW1p, 1, transfoTorus1); + mCoolingPipe2->AddNode(TubeW2p, 1, transfoTube2); + mCoolingPipe2->AddNode(TorusW2p, 1, transfoTorus2p); + mCoolingPipe2->AddNode(TubeW3p, 1, transfoTube3p); + + if (disk == 0) { + + TGeoVolume* Tube4p = gGeoManager->MakeTube(Form("Tube4p_H%d_D%d", half, disk), mPipe, rin4, rout4, length4 / 2); + TGeoVolume* TubeW4p = gGeoManager->MakeTube(Form("TubeW4p_H%d_D%d", half, disk), mWater, 0., rin4, length4 / 2); + Tube4p->SetLineColor(kGray); + TubeW4p->SetLineColor(kBlue); + + mCoolingPipe2->AddNode(Tube4p, 1, transfoTube4p); + mCoolingPipe2->AddNode(TubeW4p, 1, transfoTube4p); + theta = 180. * TMath::Pi() / 180.; + phi = 0. * TMath::Pi() / 180.; + nlow[0] = TMath::Sin(theta) * TMath::Cos(phi); + nlow[1] = TMath::Sin(theta) * TMath::Sin(phi); + nlow[2] = TMath::Cos(theta); + theta = -15. * TMath::Pi() / 180.; + phi = 270. * TMath::Pi() / 180.; + nhi[0] = TMath::Sin(theta) * TMath::Cos(phi); + nhi[1] = TMath::Sin(theta) * TMath::Sin(phi); + nhi[2] = TMath::Cos(theta); + TGeoVolume* Tube5p = gGeoManager->MakeCtub(Form("Tube5p_H%d_D%d", half, disk), mPipe, rin5, rout5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoVolume* TubeW5p = gGeoManager->MakeCtub(Form("TubeW5p_H%d_D%d", half, disk), mWater, 0., rin5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoRotation* rTube5p = new TGeoRotation("rotationTube5p", -90.0 - theta5, -(90.0 + phi5), 0.0); + rTube5p->RegisterYourself(); + TGeoCombiTrans* transfoTube5p; + transfoTube5p = new TGeoCombiTrans(zTube5, -yTube5, xTube5, rTube5p); + transfoTube5p->RegisterYourself(); + Tube5p->SetLineColor(kGray); + TubeW5p->SetLineColor(kBlue); + mCoolingPipe2->AddNode(Tube5p, 1, transfoTube5p); + mCoolingPipe2->AddNode(TubeW5p, 1, transfoTube5p); + } + TGeoCombiTrans* transfoCoolingPipe1 = nullptr; + TGeoCombiTrans* transfoCoolingPipe2 = nullptr; + + TGeoRotation* rotation1 = new TGeoRotation("rotation1", 90., 90., 90.); + rotation1->RegisterYourself(); + // 0.75 = Y location from manifold line 836 + transfoCoolingPipe1 = new TGeoCombiTrans(13.8 + length1 / 2, 0.75, 0.0, rotation1); + transfoCoolingPipe1->RegisterYourself(); + TGeoRotation* rotation2 = new TGeoRotation("rotation2", 90., 90., 90.); + rotation2->RegisterYourself(); + transfoCoolingPipe2 = new TGeoCombiTrans(13.8 + length1 / 2, -0.75, 0.0, rotation2); + transfoCoolingPipe2->RegisterYourself(); + mHalfDisk->AddNode(mCoolingPipe1, 1, transfoCoolingPipe1); + mHalfDisk->AddNode(mCoolingPipe2, 1, transfoCoolingPipe2); + } + + //================================================================= + //================================================================= + if (disk == 3) { + // One diagonal side + auto* mCoolingPipe3 = new TGeoVolumeAssembly(Form("cooling_pipe3_H%d_D%d", half, disk)); + Float_t length1_3 = 2.; + TGeoVolume* Tube1_3 = gGeoManager->MakeTube(Form("Tube1_3_H%d_D%d", half, disk), mPipe, rin, rout, length1_3 / 2); + TGeoVolume* TubeW1_3 = gGeoManager->MakeTube(Form("TubeW1_3_H%d_D%d", half, disk), mWater, 0., rin, length1_3 / 2); + TGeoTranslation* tTube1_3 = new TGeoTranslation(0.0, 0.0, 0.0); + tTube1_3->RegisterYourself(); + Float_t radius1_3 = 0.4; + TGeoVolume* Torus1_3 = gGeoManager->MakeTorus(Form("Torus1_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); + TGeoVolume* TorusW1_3 = gGeoManager->MakeTorus(Form("TorusW1_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); + TGeoRotation* rTorus1_3 = new TGeoRotation("rotationTorus1_3", 90.0, 90.0, 0.0); + rTorus1_3->RegisterYourself(); + TGeoCombiTrans* transfoTorus1_3 = new TGeoCombiTrans(0.0, -radius1_3, length1_3 / 2, rTorus1_3); + transfoTorus1_3->RegisterYourself(); + + Float_t length2_3; + if (disk == 3) { + length2_3 = 10.4; + } + TGeoVolume* Tube2_3 = gGeoManager->MakeTube(Form("Tube2_3_H%d_D%d", half, disk), mPipe, rin, rout, length2_3 / 2); + TGeoVolume* TubeW2_3 = gGeoManager->MakeTube(Form("TubeW2_3_H%d_D%d", half, disk), mWater, 0., rin, length2_3 / 2); + TGeoRotation* rTube2_3 = new TGeoRotation("rotationTube2_3", 180.0, 90.0, 90.0); + rTube2_3->RegisterYourself(); + TGeoCombiTrans* transfoTube2_3 = new TGeoCombiTrans(0., -length2_3 / 2 - radius1_3, length1_3 / 2 + radius1_3, rTube2_3); + transfoTube2_3->RegisterYourself(); + TGeoVolume* Torus2_3 = gGeoManager->MakeTorus(Form("Torus2_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); + TGeoVolume* TorusW2_3 = gGeoManager->MakeTorus(Form("TorusW2_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); + TGeoRotation* rTorus2_3 = new TGeoRotation("rotationTorus2_3", 90.0, 90.0, 180.0); + rTorus2_3->RegisterYourself(); + TGeoCombiTrans* transfoTorus2_3 = new TGeoCombiTrans(0.0, -length2_3 - radius1_3, length1_3 / 2 + radius1_3 + radius1_3, rTorus2_3); + transfoTorus2_3->RegisterYourself(); + + Float_t length3_3; + if (disk == 3) { + length3_3 = 4.4; + } + TGeoVolume* Tube3_3 = gGeoManager->MakeTube(Form("Tube3_3_H%d_D%d", half, disk), mPipe, rin, rout, length3_3 / 2); + TGeoVolume* TubeW3_3 = gGeoManager->MakeTube(Form("TubeW3_3_H%d_D%d", half, disk), mWater, 0., rin, length3_3 / 2); + TGeoRotation* rTube3_3 = new TGeoRotation("rotationTube3_3", 0.0, 0.0, 0.0); + rTube3_3->RegisterYourself(); + TGeoCombiTrans* transfoTube3_3 = new TGeoCombiTrans(0., -length2_3 - radius1_3 - radius1_3, length1_3 / 2 + radius1_3 + radius1_3 + length3_3 / 2, rTube3_3); + transfoTube3_3->RegisterYourself(); + + Tube1_3->SetLineColor(kGray); + Torus1_3->SetLineColor(kGray); + Tube2_3->SetLineColor(kGray); + Torus2_3->SetLineColor(kGray); + Tube3_3->SetLineColor(kGray); + TubeW3_3->SetLineColor(kBlue); + + mCoolingPipe3->AddNode(Tube1_3, 1, tTube1_3); + mCoolingPipe3->AddNode(Torus1_3, 1, transfoTorus1_3); + mCoolingPipe3->AddNode(Tube2_3, 1, transfoTube2_3); + mCoolingPipe3->AddNode(Torus2_3, 1, transfoTorus2_3); + mCoolingPipe3->AddNode(Tube3_3, 1, transfoTube3_3); + mCoolingPipe3->AddNode(TubeW1_3, 1, tTube1_3); + mCoolingPipe3->AddNode(TorusW1_3, 1, transfoTorus1_3); + mCoolingPipe3->AddNode(TubeW2_3, 1, transfoTube2_3); + mCoolingPipe3->AddNode(TorusW2_3, 1, transfoTorus2_3); + mCoolingPipe3->AddNode(TubeW3_3, 1, transfoTube3_3); + + TGeoCombiTrans* transfoCoolingPipe3_3 = nullptr; + TGeoRotation* rotation3_3 = new TGeoRotation("rotation3_3", 90., 90., 76.); + rotation3_3->RegisterYourself(); + transfoCoolingPipe3_3 = new TGeoCombiTrans(17. + length1_3 / 2, 0.75, 0.0, rotation3_3); + + // ------------------Other diagonal side + auto* mCoolingPipe4 = new TGeoVolumeAssembly(Form("cooling_pipe4_H%d_D%d", half, disk)); + + TGeoVolume* Tube1p_3 = gGeoManager->MakeTube(Form("Tube1p_3_H%d_D%d", half, disk), mPipe, rin, rout, length1_3 / 2); + TGeoVolume* Torus1p_3 = gGeoManager->MakeTorus(Form("Torus1p_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); + TGeoVolume* Tube2p_3 = gGeoManager->MakeTube(Form("Tube2p_3_H%d_D%d", half, disk), mPipe, rin, rout, length2_3 / 2); + TGeoVolume* Torus2p_3 = gGeoManager->MakeTorus(Form("Torus2p_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); + TGeoVolume* Tube3p_3 = gGeoManager->MakeTube(Form("Tube3p_3_H%d_D%d", half, disk), mPipe, rin, rout, length3_3 / 2); + TGeoVolume* TubeW1p_3 = gGeoManager->MakeTube(Form("TubeW1p_3_H%d_D%d", half, disk), mWater, 0, rin, length1_3 / 2); + TGeoVolume* TorusW1p_3 = gGeoManager->MakeTorus(Form("TorusW1p_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); + TGeoVolume* TubeW2p_3 = gGeoManager->MakeTube(Form("TubeW2p_3_H%d_D%d", half, disk), mWater, 0., rin, length2_3 / 2); + TGeoVolume* TorusW2p_3 = gGeoManager->MakeTorus(Form("TorusW2p_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); + TGeoVolume* TubeW3p_3 = gGeoManager->MakeTube(Form("TubeW3p_3_H%d_D%d", half, disk), mWater, 0., rin, length3_3 / 2); + + Tube1p_3->SetLineColor(kGray); + Torus1p_3->SetLineColor(kGray); + Tube2p_3->SetLineColor(kGray); + Torus2p_3->SetLineColor(kGray); + Tube3p_3->SetLineColor(kGray); + TubeW3p_3->SetLineColor(kBlue); + + mCoolingPipe4->AddNode(Tube1p_3, 1, tTube1_3); + mCoolingPipe4->AddNode(Torus1p_3, 1, transfoTorus1_3); + mCoolingPipe4->AddNode(Tube2p_3, 1, transfoTube2_3); + mCoolingPipe4->AddNode(Torus2p_3, 1, transfoTorus2_3); + mCoolingPipe4->AddNode(Tube3p_3, 1, transfoTube3_3); + mCoolingPipe4->AddNode(TubeW1p_3, 1, tTube1_3); + mCoolingPipe4->AddNode(TorusW1p_3, 1, transfoTorus1_3); + mCoolingPipe4->AddNode(TubeW2p_3, 1, transfoTube2_3); + mCoolingPipe4->AddNode(TorusW2p_3, 1, transfoTorus2_3); + mCoolingPipe4->AddNode(TubeW3p_3, 1, transfoTube3_3); + + TGeoCombiTrans* transfoCoolingPipe4_3 = nullptr; + TGeoRotation* rotation4_3 = new TGeoRotation("rotation4_3", 90., 90., -76.); + rotation4_3->RegisterYourself(); + transfoCoolingPipe4_3 = new TGeoCombiTrans(17. + length1_3 / 2, -0.75, 0.0, rotation4_3); + transfoCoolingPipe4_3->RegisterYourself(); + + mHalfDisk->AddNode(mCoolingPipe3, 1, transfoCoolingPipe3_3); + mHalfDisk->AddNode(mCoolingPipe4, 1, transfoCoolingPipe4_3); + } + + //================================================================= + if (disk == 4) { + // One diagonal side + auto* mCoolingPipe3 = new TGeoVolumeAssembly(Form("cooling_pipe3_H%d_D%d", half, disk)); + Float_t length1_4 = 2.; + TGeoVolume* Tube1_4 = gGeoManager->MakeTube(Form("Tube1_4_H%d_D%d", half, disk), mPipe, rin, rout, length1_4 / 2); + TGeoVolume* TubeW1_4 = gGeoManager->MakeTube(Form("TubeW1_4_H%d_D%d", half, disk), mWater, 0., rin, length1_4 / 2); + TGeoTranslation* tTube1_4 = new TGeoTranslation(0.0, 0.0, 0.0); + tTube1_4->RegisterYourself(); + + Float_t radius1_4 = 0.4; + TGeoVolume* Torus1_4 = gGeoManager->MakeTorus(Form("Torus1_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); + TGeoVolume* TorusW1_4 = gGeoManager->MakeTorus(Form("TorusW1_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); + TGeoRotation* rTorus1_4 = new TGeoRotation("rotationTorus1_4", 90.0, 90.0, 0.0); + rTorus1_4->RegisterYourself(); + TGeoCombiTrans* transfoTorus1_4 = new TGeoCombiTrans(0.0, -radius1_4, length1_4 / 2, rTorus1_4); + transfoTorus1_4->RegisterYourself(); + + Float_t length2_4; + if (disk == 4) { + length2_4 = 10.8; + } + TGeoVolume* Tube2_4 = gGeoManager->MakeTube("Tube2_4", mPipe, rin, rout, length2_4 / 2); + TGeoVolume* TubeW2_4 = gGeoManager->MakeTube("TubeW2_4", mWater, 0., rin, length2_4 / 2); + TGeoRotation* rTube2_4 = new TGeoRotation("rotationTube2_4", 180.0, 90.0, 90.0); + rTube2_4->RegisterYourself(); + TGeoCombiTrans* transfoTube2_4 = new TGeoCombiTrans(0., -length2_4 / 2 - radius1_4, length1_4 / 2 + radius1_4, rTube2_4); + transfoTube2_4->RegisterYourself(); + + TGeoVolume* Torus2_4 = gGeoManager->MakeTorus(Form("Torus2_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); + TGeoVolume* TorusW2_4 = gGeoManager->MakeTorus(Form("TorusW2_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); + TGeoRotation* rTorus2_4 = new TGeoRotation("rotationTorus2_4", 90.0, 90.0, 180.0); + rTorus2_4->RegisterYourself(); + TGeoCombiTrans* transfoTorus2_4 = new TGeoCombiTrans(0.0, -length2_4 - radius1_4, length1_4 / 2 + radius1_4 + radius1_4, rTorus2_4); + transfoTorus2_4->RegisterYourself(); + + Float_t length3_4; + if (disk == 4) { + length3_4 = 5.2; + } + TGeoVolume* Tube3_4 = gGeoManager->MakeTube(Form("Tube3_4_H%d_D%d", half, disk), mPipe, rin, rout, length3_4 / 2); + TGeoVolume* TubeW3_4 = gGeoManager->MakeTube(Form("TubeW3_4_H%d_D%d", half, disk), mWater, 0., rin, length3_4 / 2); + TGeoRotation* rTube3_4 = new TGeoRotation("rotationTube3_4", 0.0, 0.0, 0.0); + rTube3_4->RegisterYourself(); + TGeoCombiTrans* transfoTube3_4 = new TGeoCombiTrans(0., -length2_4 - radius1_4 - radius1_4, length1_4 / 2 + radius1_4 + radius1_4 + length3_4 / 2, rTube3_4); + transfoTube3_4->RegisterYourself(); + + Tube1_4->SetLineColor(kGray); + Torus1_4->SetLineColor(kGray); + Tube2_4->SetLineColor(kGray); + Torus2_4->SetLineColor(kGray); + Tube3_4->SetLineColor(kGray); + TubeW3_4->SetLineColor(kBlue); + + mCoolingPipe3->AddNode(Tube1_4, 1, tTube1_4); + mCoolingPipe3->AddNode(Torus1_4, 1, transfoTorus1_4); + mCoolingPipe3->AddNode(Tube2_4, 1, transfoTube2_4); + mCoolingPipe3->AddNode(Torus2_4, 1, transfoTorus2_4); + mCoolingPipe3->AddNode(Tube3_4, 1, transfoTube3_4); + mCoolingPipe3->AddNode(TubeW1_4, 1, tTube1_4); + mCoolingPipe3->AddNode(TorusW1_4, 1, transfoTorus1_4); + mCoolingPipe3->AddNode(TubeW2_4, 1, transfoTube2_4); + mCoolingPipe3->AddNode(TorusW2_4, 1, transfoTorus2_4); + mCoolingPipe3->AddNode(TubeW3_4, 1, transfoTube3_4); + + TGeoCombiTrans* transfoCoolingPipe3_4 = nullptr; + TGeoRotation* rotation3_4 = new TGeoRotation("rotation3_4", 90., 90., 100.); + rotation3_4->RegisterYourself(); + transfoCoolingPipe3_4 = new TGeoCombiTrans(17. + length1_4 / 2, 0.75, 0.0, rotation3_4); + transfoCoolingPipe3_4->RegisterYourself(); + // ------------------Other diagonal side + auto* mCoolingPipe4 = new TGeoVolumeAssembly(Form("cooling_pipe4_H%d_D%d", half, disk)); + TGeoVolume* Tube1p_4 = gGeoManager->MakeTube(Form("Tube1p_4_H%d_D%d", half, disk), mPipe, rin, rout, length1_4 / 2); + TGeoVolume* Torus1p_4 = gGeoManager->MakeTorus(Form("Torus1p_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); + TGeoVolume* Tube2p_4 = gGeoManager->MakeTube(Form("Tube2p_4_H%d_D%d", half, disk), mPipe, rin, rout, length2_4 / 2); + TGeoVolume* Torus2p_4 = gGeoManager->MakeTorus(Form("Torus2p_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); + TGeoVolume* Tube3p_4 = gGeoManager->MakeTube(Form("Tube3p_4_H%d_D%d", half, disk), mPipe, rin, rout, length3_4 / 2); + TGeoVolume* TubeW1p_4 = gGeoManager->MakeTube(Form("TubeW1p_4_H%d_D%d", half, disk), mWater, 0., rin, length1_4 / 2); + TGeoVolume* TorusW1p_4 = gGeoManager->MakeTorus(Form("TorusW1p_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); + TGeoVolume* TubeW2p_4 = gGeoManager->MakeTube(Form("TubeW2p_4_H%d_D%d", half, disk), mWater, 0., rin, length2_4 / 2); + TGeoVolume* TorusW2p_4 = gGeoManager->MakeTorus(Form("TorusW2p_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); + TGeoVolume* TubeW3p_4 = gGeoManager->MakeTube(Form("TubeW3p_4_H%d_D%d", half, disk), mWater, 0., rin, length3_4 / 2); + + Tube1p_4->SetLineColor(kGray); + Torus1p_4->SetLineColor(kGray); + Tube2p_4->SetLineColor(kGray); + Torus2p_4->SetLineColor(kGray); + Tube3p_4->SetLineColor(kGray); + TubeW3p_4->SetLineColor(kBlue); + + mCoolingPipe4->AddNode(Tube1p_4, 1, tTube1_4); + mCoolingPipe4->AddNode(Torus1p_4, 1, transfoTorus1_4); + mCoolingPipe4->AddNode(Tube2p_4, 1, transfoTube2_4); + mCoolingPipe4->AddNode(Torus2p_4, 1, transfoTorus2_4); + mCoolingPipe4->AddNode(Tube3p_4, 1, transfoTube3_4); + mCoolingPipe4->AddNode(TubeW1p_4, 1, tTube1_4); + mCoolingPipe4->AddNode(TorusW1p_4, 1, transfoTorus1_4); + mCoolingPipe4->AddNode(TubeW2p_4, 1, transfoTube2_4); + mCoolingPipe4->AddNode(TorusW2p_4, 1, transfoTorus2_4); + mCoolingPipe4->AddNode(TubeW3p_4, 1, transfoTube3_4); + + TGeoCombiTrans* transfoCoolingPipe4_4 = nullptr; + TGeoRotation* rotation4_4 = new TGeoRotation("rotation4_4", 90., 90., -100.); + rotation4_4->RegisterYourself(); + transfoCoolingPipe4_4 = new TGeoCombiTrans(17. + length1_4 / 2, -0.75, 0.0, rotation4_4); + transfoCoolingPipe4_4->RegisterYourself(); + + mHalfDisk->AddNode(mCoolingPipe3, 1, transfoCoolingPipe3_4); + mHalfDisk->AddNode(mCoolingPipe4, 1, transfoCoolingPipe4_4); + } + //================================================================= } //_____________________________________________________________________________ @@ -3707,10 +4212,12 @@ void HeatExchanger::initParameters() } } - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { mRohacellThickness = mHeatExchangerThickness / 2. - 2. * mCarbonThickness - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness - 2 * Geometry::sKaptonGlueThickness - 2 * (mRWater + mDRPipe); // smaller rohacell thickness, no grooves - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { mRohacellThickness = mHeatExchangerThickness / 2. - 2. * mCarbonThickness - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness - 2 * Geometry::sKaptonGlueThickness; // with grooves + } mHalfDiskGap = 0.1; diff --git a/Detectors/ITSMFT/MFT/base/src/LadderSegmentation.cxx b/Detectors/ITSMFT/MFT/base/src/LadderSegmentation.cxx index 4ade7baad61bb..6791d13307e4d 100644 --- a/Detectors/ITSMFT/MFT/base/src/LadderSegmentation.cxx +++ b/Detectors/ITSMFT/MFT/base/src/LadderSegmentation.cxx @@ -53,10 +53,11 @@ LadderSegmentation::LadderSegmentation(const LadderSegmentation& ladder) { // copy constructor - if (ladder.mChips) + if (ladder.mChips) { mChips = new TClonesArray(*(ladder.mChips)); - else + } else { mChips = new TClonesArray("o2::mft::ChipSegmentation", mNSensors); + } mChips->SetOwner(kTRUE); } @@ -142,8 +143,9 @@ void LadderSegmentation::createSensors(TXMLEngine* xml, XMLNodePointer_t node) ChipSegmentation* LadderSegmentation::getSensor(Int_t sensorID) const { - if (sensorID < 0 || sensorID >= mNSensors) + if (sensorID < 0 || sensorID >= mNSensors) { return nullptr; + } ChipSegmentation* chip = (ChipSegmentation*)mChips->At(sensorID); @@ -159,7 +161,8 @@ void LadderSegmentation::print(Option_t* opt) getTransformation()->Print(); if (opt && (strstr(opt, "sensor") || strstr(opt, "s"))) { - for (int i = 0; i < getNSensors(); i++) + for (int i = 0; i < getNSensors(); i++) { getSensor(i)->Print(""); + } } } diff --git a/Detectors/ITSMFT/MFT/base/src/PCBSupport.cxx b/Detectors/ITSMFT/MFT/base/src/PCBSupport.cxx index 71112e3ee8e3f..e6d1ca89ca65a 100644 --- a/Detectors/ITSMFT/MFT/base/src/PCBSupport.cxx +++ b/Detectors/ITSMFT/MFT/base/src/PCBSupport.cxx @@ -56,21 +56,23 @@ TGeoVolumeAssembly* PCBSupport::create(Int_t half, Int_t disk) mSomeBox = new TGeoBBox(boxName, mBoxCuts[disk][cut][0] / 2. + 2 * mT_delta, mBoxCuts[disk][cut][1] / 2. + 2 * mT_delta, mFR4Thickness + mT_delta); mSomeTranslation = new TGeoTranslation(mBoxCuts[disk][cut][2], mBoxCuts[disk][cut][3], 0.); //The first subtraction needs a shape, the base tube - if (cut == 0) + if (cut == 0) { mSomeSubtraction = new TGeoSubtraction(PCBCu, mSomeBox, nullptr, mSomeTranslation); - else + } else { mSomeSubtraction = new TGeoSubtraction(mPCBCu, mSomeBox, nullptr, mSomeTranslation); + } mPCBCu = new TGeoCompositeShape(boxCSName, mSomeSubtraction); - if (cut == 0) + if (cut == 0) { mSomeSubtraction = new TGeoSubtraction(PCBFR4, mSomeBox, nullptr, mSomeTranslation); - else + } else { mSomeSubtraction = new TGeoSubtraction(mPCBFR4, mSomeBox, nullptr, mSomeTranslation); + } mPCBFR4 = new TGeoCompositeShape(boxCSName, mSomeSubtraction); } //Info("Create",Form("Cutting Boxes PCB_H%d_D%d", half,disk),0,0); - if (mNumberOfBoxAdd[disk] != 0) + if (mNumberOfBoxAdd[disk] != 0) { for (auto iBox = 0; iBox < mNumberOfBoxAdd[disk]; iBox++) { auto* boxName = Form("PCBBoxAdd_%d_H%d_D%d", iBox, half, disk); auto* boxCSName = Form("PCBBoxAddCS_%d_H%d_D%d", iBox, half, disk); @@ -82,6 +84,7 @@ TGeoVolumeAssembly* PCBSupport::create(Int_t half, Int_t disk) mSomeUnion = new TGeoUnion(mPCBCu, mSomeBox, nullptr, mSomeTranslation); mPCBCu = new TGeoCompositeShape(boxCSName, mSomeUnion); } + } // ================= Holes ================== diff --git a/Detectors/ITSMFT/MFT/base/src/PowerSupplyUnit.cxx b/Detectors/ITSMFT/MFT/base/src/PowerSupplyUnit.cxx index 1d8192d85ca94..efa230c97e14d 100644 --- a/Detectors/ITSMFT/MFT/base/src/PowerSupplyUnit.cxx +++ b/Detectors/ITSMFT/MFT/base/src/PowerSupplyUnit.cxx @@ -9,8 +9,8 @@ // or submit itself to any jurisdiction. /// \file PowerSupplyUnit.cxx -/// \brief Class building the MFT heat exchanger -/// \author P. Demongodin, Raphael Tieulent <raphael.tieulent@cern.ch> +/// \brief Class building the MFT Power Supply Unit +/// \author Satoshi Yano #include "TMath.h" #include "TGeoManager.h" @@ -20,6 +20,7 @@ #include "TGeoCone.h" #include "TGeoBoolNode.h" #include "TGeoBBox.h" +#include "TGeoSphere.h" #include "TGeoVolume.h" #include "FairLogger.h" #include "MFTBase/Constants.h" @@ -40,703 +41,1231 @@ PowerSupplyUnit::PowerSupplyUnit() TGeoVolumeAssembly* PowerSupplyUnit::create() { - //fm auto* mPowerSupplyUnit = new TGeoVolumeAssembly(); - - new TGeoBBox("dummy", 0, 0, 0); + //TGeoManager *gManager = new TGeoManager(); TGeoMedium* kMedPeek = gGeoManager->GetMedium("MFT_PEEK$"); TGeoMedium* kMed_Water = gGeoManager->GetMedium("MFT_Water$"); TGeoMedium* kMedAlu = gGeoManager->GetMedium("MFT_Alu$"); - TGeoMedium* kMedCu = gGeoManager->GetMedium("MFT_Cu$"); + TGeoMedium* kMedPolyPipe = gGeoManager->GetMedium("MFT_Polyimide$"); TGeoVolumeAssembly* mHalfPSU = new TGeoVolumeAssembly("PSU"); + Double_t delta_thickness = 0.2; + + Double_t water_pipe_main_position_radius = (18.62 + 18.18) / 2.; + + Double_t block_widest_angle = 126.07; + Double_t block_radius = (19.5 + 17.7) / 2.; + Double_t block_angle_index[] = {126.08, 114.03, 101.91, 89.96, 77.88, 66.06, 54.16, 42.06, 30, 17.99, 6.07}; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - //Middle peek part + //Middle Spacer //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Double_t middle_board_thickness = 0.8; - Double_t middle_board_max_radius1 = 26.432; - Double_t middle_board_min_radius1 = 17.0; - Double_t middleBox_sub_angle2 = 112.26 * TMath::Pi() / 180.; - Double_t middleBox_sub_width1 = 20.8; - - Double_t middleBox_sub_height1 = middle_board_max_radius1 * (1 - sqrt(1 - pow(middleBox_sub_width1 / 2. / middle_board_max_radius1, 2))); - - Double_t middleBox_sub_width2 = 2 * middle_board_max_radius1; - Double_t middleBox_sub_height2 = 2 * middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2); - - new TGeoTubeSeg("middleTubeSeg1", middle_board_min_radius1, middle_board_max_radius1, middle_board_thickness / 2, 180, 0); - new TGeoBBox("middleBox_sub1", middleBox_sub_width1 / 2, middleBox_sub_height1 / 2, middle_board_thickness); - new TGeoBBox("middleBox_sub2", middleBox_sub_width2 / 2, middleBox_sub_height2 / 2, middle_board_thickness); - - auto* tmiddleBox_sub1 = new TGeoTranslation("tmiddleBox_sub1", 0, -(middle_board_max_radius1 - middleBox_sub_height1 / 2), 0); - auto* tmiddleBox_sub2 = new TGeoTranslation("tmiddleBox_sub2", 0., 0., 0.); - tmiddleBox_sub1->RegisterYourself(); - tmiddleBox_sub2->RegisterYourself(); - - Double_t middleBox_edge_width1 = 11.4; - Double_t middleBox_edge_height1 = 4.996; - - Double_t middleBox_edge_sub_width1 = 1.8 + 0.5 * 2; - Double_t middleBox_edge_sub_height1 = 6.025 + 0.5 * 2; + std::string name_middle_spacer_main_cover = ""; + + //main + Double_t middle_spacer_main_thickness = 0.50; + Double_t middle_spacer_main_min_radius1 = 17.0; + Double_t middle_spacer_main_max_radius1 = 21.7; + Double_t middle_spacer_main_sub_rectangle_top_height = 5 + 2.787; + Double_t middle_spacer_main_sub_rectangle_top_width = middle_spacer_main_max_radius1 * 2; + + TGeoTubeSeg* middle_spacer_main_arc = new TGeoTubeSeg("middle_spacer_main_arc", middle_spacer_main_min_radius1, middle_spacer_main_max_radius1, middle_spacer_main_thickness / 2, 180, 0); + TGeoBBox* middle_spacer_main_sub_rectangle_top = new TGeoBBox("middle_spacer_main_sub_rectangle_top", middle_spacer_main_sub_rectangle_top_width / 2., middle_spacer_main_sub_rectangle_top_height / 2., middle_spacer_main_thickness / 2. + delta_thickness); + TGeoTranslation* trans_middle_spacer_main_sub_rectangle_top = new TGeoTranslation("trans_middle_spacer_main_sub_rectangle_top", 0, -middle_spacer_main_sub_rectangle_top_height / 2, 0); + trans_middle_spacer_main_sub_rectangle_top->RegisterYourself(); + + Double_t middle_spacer_main_add_rectangle_side_height = 4.5 + 1.5; + Double_t middle_spacer_main_add_rectangle_side_width = 9.0; + TGeoBBox* middle_spacer_main_add_rectangle_side = new TGeoBBox("middle_spacer_main_add_rectangle_side", middle_spacer_main_add_rectangle_side_width / 2., middle_spacer_main_add_rectangle_side_height / 2., middle_spacer_main_thickness / 2.); + TGeoTranslation* trans_middle_spacer_main_add_rectangle_side_left = new TGeoTranslation("trans_middle_spacer_main_add_rectangle_side_left", 28.4 / 2. + middle_spacer_main_add_rectangle_side_width / 2., -5 - middle_spacer_main_add_rectangle_side_height / 2., 0); + TGeoTranslation* trans_middle_spacer_main_add_rectangle_side_right = new TGeoTranslation("trans_middle_spacer_main_add_rectangle_side_right", -(28.4 / 2. + middle_spacer_main_add_rectangle_side_width / 2.), -5 - middle_spacer_main_add_rectangle_side_height / 2., 0); + trans_middle_spacer_main_add_rectangle_side_left->RegisterYourself(); + trans_middle_spacer_main_add_rectangle_side_right->RegisterYourself(); + + Double_t middle_spacer_main_add_rectangle_side_small_thickness = 1.18; + Double_t middle_spacer_main_add_rectangle_side_small_height = 1.5; + Double_t middle_spacer_main_add_rectangle_side_small_width = 2.4; + TGeoBBox* middle_spacer_main_add_rectangle_side_small = new TGeoBBox("middle_spacer_main_add_rectangle_side_small", middle_spacer_main_add_rectangle_side_small_width / 2., middle_spacer_main_add_rectangle_side_small_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2.); + TGeoTranslation* trans_middle_spacer_main_add_rectangle_side_small_left = new TGeoTranslation("trans_middle_spacer_main_add_rectangle_side_small_left", 28.4 / 2. + middle_spacer_main_add_rectangle_side_width + middle_spacer_main_add_rectangle_side_small_width / 2, -5 - middle_spacer_main_add_rectangle_side_small_height / 2., 0); + TGeoTranslation* trans_middle_spacer_main_add_rectangle_side_small_right = new TGeoTranslation("trans_middle_spacer_main_add_rectangle_side_small_right", -(28.4 / 2. + middle_spacer_main_add_rectangle_side_width + middle_spacer_main_add_rectangle_side_small_width / 2), -5 - middle_spacer_main_add_rectangle_side_small_height / 2., 0); + trans_middle_spacer_main_add_rectangle_side_small_left->RegisterYourself(); + trans_middle_spacer_main_add_rectangle_side_small_right->RegisterYourself(); + + TGeoCompositeShape* middle_spacer_main = new TGeoCompositeShape("middle_spacer_main", + "middle_spacer_main_arc - middle_spacer_main_sub_rectangle_top:trans_middle_spacer_main_sub_rectangle_top" + " + middle_spacer_main_add_rectangle_side:trans_middle_spacer_main_add_rectangle_side_left + middle_spacer_main_add_rectangle_side:trans_middle_spacer_main_add_rectangle_side_right" + " + middle_spacer_main_add_rectangle_side_small:trans_middle_spacer_main_add_rectangle_side_small_left + middle_spacer_main_add_rectangle_side_small:trans_middle_spacer_main_add_rectangle_side_small_right"); + + //Cover + + Double_t middle_spacer_cover_thickness = 0.15; + Double_t middle_spacer_cover_arc_max_radius = 19.5; + Double_t middle_spacer_cover_arc_min_radius = 17.0; + TGeoTubeSeg* middle_spacer_cover_arc = new TGeoTubeSeg("middle_spacer_cover_arc", middle_spacer_cover_arc_min_radius, middle_spacer_cover_arc_max_radius, middle_spacer_cover_thickness / 2, 180, 0); + TGeoTranslation* trans_middle_spacer_cover_arc = new TGeoTranslation("trans_middle_spacer_cover_arc", 0, 0, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + trans_middle_spacer_cover_arc->RegisterYourself(); + + Double_t middle_spacer_cover_sub_rectangle_top_height = 5.0 + 1.5 + 1.9; + Double_t middle_spacer_cover_sub_rectangle_top_width = middle_spacer_cover_arc_max_radius * 2; + TGeoBBox* middle_spacer_cover_sub_rectangle_top = new TGeoBBox("middle_spacer_cover_sub_rectangle_top", middle_spacer_cover_sub_rectangle_top_width / 2., middle_spacer_cover_sub_rectangle_top_height, middle_spacer_cover_thickness / 2. + delta_thickness); + TGeoTranslation* trans_middle_spacer_cover_sub_rectangle_top = new TGeoTranslation("trans_middle_spacer_cover_sub_rectangle_top", 0, 0, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + trans_middle_spacer_cover_sub_rectangle_top->RegisterYourself(); + + Double_t middle_spacer_cover_rectangle_side1_height = 1.9; + Double_t middle_spacer_cover_rectangle_side1_width_left = 4.2; //rahgh + Double_t middle_spacer_cover_rectangle_side1_width_right = 8.8; //rahgh + TGeoBBox* middle_spacer_cover_rectangle_side1_left = new TGeoBBox("middle_spacer_cover_rectangle_side1_left", middle_spacer_cover_rectangle_side1_width_left / 2., middle_spacer_cover_rectangle_side1_height / 2., middle_spacer_cover_thickness / 2.); + TGeoBBox* middle_spacer_cover_rectangle_side1_right = new TGeoBBox("middle_spacer_cover_rectangle_side1_right", middle_spacer_cover_rectangle_side1_width_right / 2., middle_spacer_cover_rectangle_side1_height / 2., middle_spacer_cover_thickness / 2.); + TGeoTranslation* trans_middle_spacer_cover_rectangle_side1_left = new TGeoTranslation("trans_middle_spacer_cover_rectangle_side1_left", 28.4 / 2 + middle_spacer_cover_rectangle_side1_width_left / 2, -5 - 1.5 - 1.9 + middle_spacer_cover_rectangle_side1_height / 2, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + TGeoTranslation* trans_middle_spacer_cover_rectangle_side1_right = new TGeoTranslation("trans_middle_spacer_cover_rectangle_side1_right", -(28.4 / 2 + middle_spacer_cover_rectangle_side1_width_right / 2), -5 - 1.5 - 1.9 + middle_spacer_cover_rectangle_side1_height / 2, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + trans_middle_spacer_cover_rectangle_side1_left->RegisterYourself(); + trans_middle_spacer_cover_rectangle_side1_right->RegisterYourself(); + + Double_t middle_spacer_cover_rectangle_side2_height = 1.5; + Double_t middle_spacer_cover_rectangle_side2_width = 2.0; //rahgh + TGeoBBox* middle_spacer_cover_rectangle_side2 = new TGeoBBox("middle_spacer_cover_rectangle_side2", middle_spacer_cover_rectangle_side2_width / 2., middle_spacer_cover_rectangle_side2_height / 2., middle_spacer_cover_thickness / 2.); + TGeoTranslation* trans_middle_spacer_cover_rectangle_side2_left = new TGeoTranslation("trans_middle_spacer_cover_rectangle_side2_left", 28.4 / 2 + middle_spacer_cover_rectangle_side2_width / 2, -5 - 1.5 - 2.787 + middle_spacer_cover_rectangle_side2_height / 2, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + TGeoTranslation* trans_middle_spacer_cover_rectangle_side2_right = new TGeoTranslation("trans_middle_spacer_cover_rectangle_side2_right", -(28.4 / 2 + middle_spacer_cover_rectangle_side2_width / 2), -5 - 1.5 - 2.787 + middle_spacer_cover_rectangle_side2_height / 2, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + trans_middle_spacer_cover_rectangle_side2_left->RegisterYourself(); + trans_middle_spacer_cover_rectangle_side2_right->RegisterYourself(); + + Double_t middle_spacer_cover_rectangle_side3_height = 1.9; + Double_t middle_spacer_cover_rectangle_side3_width = 2.1; //rahgh + TGeoBBox* middle_spacer_cover_rectangle_side3 = new TGeoBBox("middle_spacer_cover_rectangle_side3", middle_spacer_cover_rectangle_side3_width / 2., middle_spacer_cover_rectangle_side3_height / 2., middle_spacer_cover_thickness / 2.); + TGeoTranslation* trans_middle_spacer_cover_rectangle_side3 = new TGeoTranslation("trans_middle_spacer_cover_rectangle_side3", 28.4 / 2 + 8.8 - middle_spacer_cover_rectangle_side3_width / 2, -5 - 1.5 - middle_spacer_cover_rectangle_side3_height / 2, middle_spacer_main_thickness / 2 + middle_spacer_cover_thickness / 2); + trans_middle_spacer_cover_rectangle_side3->RegisterYourself(); + + name_middle_spacer_main_cover += + " middle_spacer_cover_arc:trans_middle_spacer_cover_arc - middle_spacer_cover_sub_rectangle_top:trans_middle_spacer_cover_sub_rectangle_top" + " + middle_spacer_cover_rectangle_side1_left:trans_middle_spacer_cover_rectangle_side1_left + middle_spacer_cover_rectangle_side1_right:trans_middle_spacer_cover_rectangle_side1_right" + " + middle_spacer_cover_rectangle_side2:trans_middle_spacer_cover_rectangle_side2_left + middle_spacer_cover_rectangle_side2:trans_middle_spacer_cover_rectangle_side2_right" + " + middle_spacer_cover_rectangle_side3:trans_middle_spacer_cover_rectangle_side3"; + + Double_t middle_spacer_cover_block_thickness = 0.19; + Double_t middle_spacer_cover_block_height = 1.0; + Double_t middle_spacer_cover_block_width = 1.0; + Double_t middle_spacer_cover_block_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + middle_spacer_cover_block_thickness / 2.; + + TGeoBBox* middle_spacer_cover_block = new TGeoBBox("middle_spacer_cover_block", middle_spacer_cover_block_width / 2, middle_spacer_cover_block_height / 2, middle_spacer_cover_block_thickness / 2); + + TGeoCompositeShape* rotated_middle_spacer_cover_block[24]; + for (Int_t iB = 0; iB < 11; ++iB) { + + Double_t block_angle = (180. - block_angle_index[iB]) / 2. * TMath::Pi() / 180.; + + TGeoRotation* rotate_electric_board_sub_box_left = new TGeoRotation(Form("rotate_middle_spacer_cover_block_No%d", iB * 2), 180 - block_angle * 180. / TMath::Pi(), 0, 0); + TGeoRotation* rotate_electric_board_sub_box_right = new TGeoRotation(Form("rotate_middle_spacer_cover_block_No%d", iB * 2 + 1), block_angle * 180. / TMath::Pi(), 0, 0); + rotate_electric_board_sub_box_left->RegisterYourself(); + rotate_electric_board_sub_box_right->RegisterYourself(); + + Double_t cent_block_left[] = {block_radius * TMath::Cos(block_angle), -block_radius * TMath::Sin(block_angle), middle_spacer_cover_block_position_z}; + Double_t cent_block_right[] = {block_radius * TMath::Cos(TMath::Pi() - block_angle), -block_radius * TMath::Sin(TMath::Pi() - block_angle), middle_spacer_cover_block_position_z}; + + TGeoCombiTrans* combtrans_electric_board_sub_box_left = new TGeoCombiTrans(Form("combtrans_spacer_cover_block_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2], rotate_electric_board_sub_box_left); + TGeoCombiTrans* combtrans_electric_board_sub_box_right = new TGeoCombiTrans(Form("combtrans_spacer_cover_block_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2], rotate_electric_board_sub_box_right); + combtrans_electric_board_sub_box_left->RegisterYourself(); + combtrans_electric_board_sub_box_right->RegisterYourself(); + + name_middle_spacer_main_cover += Form("+middle_spacer_cover_block:combtrans_spacer_cover_block_No%d", 2 * iB); + name_middle_spacer_main_cover += Form("+middle_spacer_cover_block:combtrans_spacer_cover_block_No%d", 2 * iB + 1); + } - Double_t middleBox_edge_sub_width2 = 1.1; - Double_t middleBox_edge_sub_height2 = 1.0; + TGeoTranslation* trans_spacer_cover_block_left = new TGeoTranslation("trans_spacer_cover_block_left", 22.259 - middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, middle_spacer_cover_block_position_z); + trans_spacer_cover_block_left->RegisterYourself(); + name_middle_spacer_main_cover += "+middle_spacer_cover_block:trans_spacer_cover_block_left"; - Double_t middleBox_edge_sub_height3 = 1.3; + TGeoTranslation* trans_spacer_cover_block_right = new TGeoTranslation("trans_spacer_cover_block_right", -22.247 + middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, middle_spacer_cover_block_position_z); + trans_spacer_cover_block_right->RegisterYourself(); + name_middle_spacer_main_cover += "+middle_spacer_cover_block:trans_spacer_cover_block_right"; - new TGeoBBox("middleBox_edge1", middleBox_edge_width1 / 2, middleBox_edge_height1 / 2, middle_board_thickness / 2); - new TGeoBBox("middleBox_edge_sub1", middleBox_edge_sub_width1 / 2, middleBox_edge_sub_height1 / 2, middle_board_thickness); - new TGeoBBox("middleBox_edge_sub2", middleBox_edge_sub_width2 / 2, middleBox_edge_sub_height2 / 2, middle_board_thickness); - - auto* tmiddleBox_edge_right1 = new TGeoTranslation("tmiddleBox_edge_right1", - middle_board_min_radius1 * TMath::Sin(middleBox_sub_angle2 / 2) + middleBox_edge_width1 / 2, - -(middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2) - middleBox_edge_height1 / 2), 0); - auto* tmiddleBox_edge_sub_right1 = new TGeoTranslation("tmiddleBox_edge_sub_right1", - middle_board_min_radius1 * TMath::Sin(middleBox_sub_angle2 / 2) + middleBox_edge_width1 - middleBox_edge_sub_width1 / 2, - -middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2) + middleBox_edge_height1 - middleBox_edge_sub_height2 - middleBox_edge_sub_height3 - middleBox_edge_sub_height1 / 2, - 0); - auto* tmiddleBox_edge_sub_right2 = new TGeoTranslation("tmiddleBox_edge_sub_right2", - middle_board_min_radius1 * TMath::Sin(middleBox_sub_angle2 / 2) + middleBox_edge_width1 - middleBox_edge_sub_width2 / 2, - -(middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2) - middleBox_edge_height1 / 2) + middleBox_edge_height1 / 2 - middleBox_edge_sub_height2 / 2, - 0); - - auto* tmiddleBox_edge_left1 = new TGeoTranslation("tmiddleBox_edge_left1", - -(middle_board_min_radius1 * TMath::Sin(middleBox_sub_angle2 / 2) + middleBox_edge_width1 / 2), - -(middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2) - middleBox_edge_height1 / 2), 0); - auto* tmiddleBox_edge_sub_left1 = new TGeoTranslation("tmiddleBox_edge_sub_left1", - -(middle_board_min_radius1 * TMath::Sin(middleBox_sub_angle2 / 2) + middleBox_edge_width1 - middleBox_edge_sub_width1 / 2), - -middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2) + middleBox_edge_height1 - middleBox_edge_sub_height2 - middleBox_edge_sub_height3 - middleBox_edge_sub_height1 / 2, - 0); - auto* tmiddleBox_edge_sub_left2 = new TGeoTranslation("tmiddleBox_edge_sub_left2", - -(middle_board_min_radius1 * TMath::Sin(middleBox_sub_angle2 / 2) + middleBox_edge_width1 - middleBox_edge_sub_width2 / 2), - -(middle_board_min_radius1 * TMath::Cos(middleBox_sub_angle2 / 2) - middleBox_edge_height1 / 2) + middleBox_edge_height1 / 2 - middleBox_edge_sub_height2 / 2, - 0); - - tmiddleBox_edge_right1->RegisterYourself(); - tmiddleBox_edge_sub_right1->RegisterYourself(); - tmiddleBox_edge_sub_right2->RegisterYourself(); - - tmiddleBox_edge_left1->RegisterYourself(); - tmiddleBox_edge_sub_left1->RegisterYourself(); - tmiddleBox_edge_sub_left2->RegisterYourself(); - - Double_t middleBox_side_lack_radius1 = 25.0; - Double_t middleBox_side_lack_height1 = 0.937 + 0.5; - Double_t middleBox_side_lack_angle1 = 24.75 + (2 * 0.5 * 360) / (2 * TMath::Pi() * middleBox_side_lack_radius1); - - Double_t middleBox_side_lack_position_angle_min_right1 = 270 - middleBox_side_lack_angle1 - 9.83 - TMath::ASin(middleBox_sub_width1 / 2 / middle_board_max_radius1) * 180 / TMath::Pi(); - Double_t middleBox_side_lack_position_angle_max_right1 = 270 - 9.83 - TMath::ASin(middleBox_sub_width1 / 2 / middle_board_max_radius1) * 180 / TMath::Pi(); - - new TGeoTubeSeg("middleBox_side_lack_right1", middleBox_side_lack_radius1, middle_board_max_radius1, middle_board_thickness, middleBox_side_lack_position_angle_min_right1, middleBox_side_lack_position_angle_max_right1); - - Double_t middleBox_side_lack_position_angle_min_left1 = 360 - middleBox_side_lack_angle1 - 9.83 - TMath::ASin(middleBox_sub_width1 / 2 / middle_board_max_radius1) * 180 / TMath::Pi(); - Double_t middleBox_side_lack_position_angle_max_left1 = 360 - 9.83 - TMath::ASin(middleBox_sub_width1 / 2 / middle_board_max_radius1) * 180 / TMath::Pi(); - - new TGeoTubeSeg("middleBox_side_lack_left1", middleBox_side_lack_radius1, middle_board_max_radius1, middle_board_thickness, middleBox_side_lack_position_angle_min_left1, middleBox_side_lack_position_angle_max_left1); + TGeoCompositeShape* middle_spacer_cover = new TGeoCompositeShape("middle_spacer_cover", name_middle_spacer_main_cover.c_str()); - new TGeoCompositeShape("middle_board_shape", - "middleTubeSeg1" - "-middleBox_sub1:tmiddleBox_sub1" - "-middleBox_sub2:tmiddleBox_sub2" - "+middleBox_edge1:tmiddleBox_edge_right1 - middleBox_edge_sub1:tmiddleBox_edge_sub_right1 - middleBox_edge_sub2:tmiddleBox_edge_sub_right2" - "+middleBox_edge1:tmiddleBox_edge_left1 - middleBox_edge_sub1:tmiddleBox_edge_sub_left1 - middleBox_edge_sub2:tmiddleBox_edge_sub_left2" - "-middleBox_side_lack_right1 - middleBox_side_lack_left1"); - - Double_t pipe_sub_radius1 = 0.2; - Double_t center_pipe_sub_torus_radius1 = 18.7 - pipe_sub_radius1; - Double_t center_pipe_sub_torus_angle1 = 114.84; - - new TGeoTorus("middleCenterWaterPipeTorus_sub1", center_pipe_sub_torus_radius1, pipe_sub_radius1, pipe_sub_radius1, 0, center_pipe_sub_torus_angle1); - auto* rmiddleCenterWaterPipeTorus_sub1 = new TGeoRotation("rmiddleCenterWaterPipeTorus_sub1", -(180 - 0.5 * (180 - center_pipe_sub_torus_angle1)), 0, 0); - rmiddleCenterWaterPipeTorus_sub1->RegisterYourself(); - - Double_t side_pipe_sub_torus_radius1 = 1.2 - pipe_sub_radius1; - Double_t side_pipe_sub_torus_angle1 = 57.42; - - new TGeoTorus("middleSideWaterPipeTorus_sub1", side_pipe_sub_torus_radius1, pipe_sub_radius1, pipe_sub_radius1, 0, side_pipe_sub_torus_angle1); - - auto* rmiddleSideWaterPipeTorus_sub_right1 = new TGeoRotation("rmiddleSideWaterPipeTorus_sub_right1", 90, 0, 0); - auto* rmiddleSideWaterPipeTorus_sub_left1 = new TGeoRotation("rmiddleSideWaterPipeTorus_sub_left1", 90 - side_pipe_sub_torus_angle1, 0, 0); - rmiddleSideWaterPipeTorus_sub_right1->RegisterYourself(); - rmiddleSideWaterPipeTorus_sub_left1->RegisterYourself(); - - new TGeoCompositeShape("rotated_middleSideWaterPipeTorus_sub_right1", - "dummy + middleSideWaterPipeTorus_sub1:rmiddleSideWaterPipeTorus_sub_right1"); - new TGeoCompositeShape("rotated_middleSideWaterPipeTorus_sub_left1", - "dummy + middleSideWaterPipeTorus_sub1:rmiddleSideWaterPipeTorus_sub_left1"); - - auto* tmiddleSideWaterPipeTorus_sub_right1 = new TGeoTranslation("tmiddleSideWaterPipeTorus_sub_right1", - center_pipe_sub_torus_radius1 * TMath::Sin(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180.) + side_pipe_sub_torus_radius1 * TMath::Sin(side_pipe_sub_torus_angle1 * TMath::Pi() / 180.), - -center_pipe_sub_torus_radius1 * TMath::Cos(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180.) - side_pipe_sub_torus_radius1 * TMath::Cos(side_pipe_sub_torus_angle1 * TMath::Pi() / 180.), 0); - tmiddleSideWaterPipeTorus_sub_right1->RegisterYourself(); - - auto* tmiddleSideWaterPipeTorus_sub_left1 = new TGeoTranslation("tmiddleSideWaterPipeTorus_sub_left1", - -(center_pipe_sub_torus_radius1 * TMath::Sin(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180.) + side_pipe_sub_torus_radius1 * TMath::Sin(side_pipe_sub_torus_angle1 * TMath::Pi() / 180.)), - -center_pipe_sub_torus_radius1 * TMath::Cos(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180.) - side_pipe_sub_torus_radius1 * TMath::Cos(side_pipe_sub_torus_angle1 * TMath::Pi() / 180.), 0); - tmiddleSideWaterPipeTorus_sub_left1->RegisterYourself(); - - Double_t side_pipe_sub_tube_length1 = 6.568; - - new TGeoTube("middleSideWaterPipeTube_sub1", 0, pipe_sub_radius1, side_pipe_sub_tube_length1 / 2); - - auto* rmiddleSideWaterPipeTube_sub1 = new TGeoRotation("rmiddleSideWaterPipeTube_sub1", 90, 90, 0); - rmiddleSideWaterPipeTube_sub1->RegisterYourself(); - - new TGeoCompositeShape("rotated_middleSideWaterPipeTube_sub1", "dummy+middleSideWaterPipeTube_sub1:rmiddleSideWaterPipeTube_sub1"); - - TGeoTranslation* tmiddleSideWaterPipeTube_sub_right1 = new TGeoTranslation("tmiddleSideWaterPipeTube_sub_right1", - center_pipe_sub_torus_radius1 * TMath::Sin(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_sub_torus_radius1 * TMath::Sin(side_pipe_sub_torus_angle1 * TMath::Pi() / 180) + side_pipe_sub_tube_length1 / 2, - -center_pipe_sub_torus_radius1 * TMath::Cos(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_sub_torus_radius1 * (1 - TMath::Sin((90 - side_pipe_sub_torus_angle1) * TMath::Pi() / 180)), - 0); - TGeoTranslation* tmiddleSideWaterPipeTube_sub_left1 = new TGeoTranslation("tmiddleSideWaterPipeTube_sub_left1", - -(center_pipe_sub_torus_radius1 * TMath::Sin(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_sub_torus_radius1 * TMath::Sin(side_pipe_sub_torus_angle1 * TMath::Pi() / 180) + side_pipe_sub_tube_length1 / 2), - -center_pipe_sub_torus_radius1 * TMath::Cos(center_pipe_sub_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_sub_torus_radius1 * (1 - TMath::Sin((90 - side_pipe_sub_torus_angle1) * TMath::Pi() / 180)), - 0); - tmiddleSideWaterPipeTube_sub_right1->RegisterYourself(); - tmiddleSideWaterPipeTube_sub_left1->RegisterYourself(); - - new TGeoCompositeShape("middleWaterPipeTorus_sub_shape", - "dummy + middleCenterWaterPipeTorus_sub1:rmiddleCenterWaterPipeTorus_sub1" - "+rotated_middleSideWaterPipeTorus_sub_right1:tmiddleSideWaterPipeTorus_sub_right1" - "+rotated_middleSideWaterPipeTube_sub1:tmiddleSideWaterPipeTube_sub_right1" - "+rotated_middleSideWaterPipeTorus_sub_left1:tmiddleSideWaterPipeTorus_sub_left1" - "+rotated_middleSideWaterPipeTube_sub1:tmiddleSideWaterPipeTube_sub_left1"); - - TGeoTranslation* tmiddleWaterPipeTorus_sub_shape1 = new TGeoTranslation("tmiddleWaterPipeTorus_sub_shape1", 0, 0, middle_board_thickness / 2); - TGeoTranslation* tmiddleWaterPipeTorus_sub_shape2 = new TGeoTranslation("tmiddleWaterPipeTorus_sub_shape2", 0, 0, -middle_board_thickness / 2); - tmiddleWaterPipeTorus_sub_shape1->RegisterYourself(); - tmiddleWaterPipeTorus_sub_shape2->RegisterYourself(); - - TGeoCompositeShape* middle_board_pipeSubtracted_shape = new TGeoCompositeShape("middle_board_pipeSubtracted_shape", - "middle_board_shape-middleWaterPipeTorus_sub_shape:tmiddleWaterPipeTorus_sub_shape1" - "-middleWaterPipeTorus_sub_shape:tmiddleWaterPipeTorus_sub_shape2"); + TGeoRotation* rotate_middle_spacer_cover_back = new TGeoRotation("rotate_middle_spacer_cover_back", 180, 180, 0); + rotate_middle_spacer_cover_back->RegisterYourself(); - TGeoVolume* middle_peek_board = new TGeoVolume("middle_peek_board", middle_board_pipeSubtracted_shape, kMedPeek); - - mHalfPSU->AddNode(middle_peek_board, 0, nullptr); + TGeoCompositeShape* middle_spacer_cover_bothside = new TGeoCompositeShape("middle_spacer_cover_bothside", "middle_spacer_cover + middle_spacer_cover:rotate_middle_spacer_cover_back"); - /////////////////////////////////////////////////////////////////////////////////////////////////////////// - //water pipe part - /////////////////////////////////////////////////////////////////////////////////////////////////////////// + //Water pipe hole - Double_t pipe_min_radius1 = 0.075; - Double_t pipe_max_radius1 = 0.100; - Double_t center_pipe_torus_radius1 = 18.5; - Double_t center_pipe_torus_angle1 = 114.84; + Double_t water_pipe_inner_radius = 0.3 / 2.; + Double_t water_pipe_outer_radius = 0.4 / 2.; - new TGeoTorus("middleCenterWaterPipeTorus1", center_pipe_torus_radius1, pipe_min_radius1, pipe_max_radius1, 0, center_pipe_torus_angle1); - - auto* rmiddleCenterWaterPipeTorus1 = new TGeoRotation("rmiddleCenterWaterPipeTorus1", -(180 - 0.5 * (180 - center_pipe_torus_angle1)), 0, 0); - rmiddleCenterWaterPipeTorus1->RegisterYourself(); - - Double_t side_pipe_torus_radius1 = 1.0; - Double_t side_pipe_torus_angle1 = 57.42; - - new TGeoTorus("middleSideWaterPipeTorus1", side_pipe_torus_radius1, pipe_min_radius1, pipe_max_radius1, 0, side_pipe_torus_angle1); - - auto* rmiddleSideWaterPipeTorus_right1 = new TGeoRotation("rmiddleSideWaterPipeTorus_right1", 90, 0, 0); - auto* rmiddleSideWaterPipeTorus_left1 = new TGeoRotation("rmiddleSideWaterPipeTorus_left1", 90 - side_pipe_torus_angle1, 0, 0); - rmiddleSideWaterPipeTorus_right1->RegisterYourself(); - rmiddleSideWaterPipeTorus_left1->RegisterYourself(); - - new TGeoCompositeShape("rotated_middleSideWaterPipeTorus_right1", - "dummy + middleSideWaterPipeTorus1:rmiddleSideWaterPipeTorus_right1"); - new TGeoCompositeShape("rotated_middleSideWaterPipeTorus_left1", - "dummy + middleSideWaterPipeTorus1:rmiddleSideWaterPipeTorus_left1"); - - auto* tmiddleSideWaterPipeTorus_right1 = new TGeoTranslation("tmiddleSideWaterPipeTorus_right1", - center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180.), - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) - side_pipe_torus_radius1 * TMath::Cos(side_pipe_torus_angle1 * TMath::Pi() / 180.), 0); - auto* tmiddleSideWaterPipeTorus_left1 = new TGeoTranslation("tmiddleSideWaterPipeTorus_left1", - -(center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180.)), - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) - side_pipe_torus_radius1 * TMath::Cos(side_pipe_torus_angle1 * TMath::Pi() / 180.), 0); - tmiddleSideWaterPipeTorus_right1->RegisterYourself(); - tmiddleSideWaterPipeTorus_left1->RegisterYourself(); - - Double_t side_pipe_tube_length1 = 6.868; - - new TGeoTube("middleSideWaterPipeTube1", pipe_min_radius1, pipe_max_radius1, side_pipe_tube_length1 / 2); - auto* rmiddleSideWaterPipeTube1 = new TGeoRotation("rmiddleSideWaterPipeTube1", 90, 90, 0); - rmiddleSideWaterPipeTube1->RegisterYourself(); - - new TGeoCompositeShape("rotated_middleSideWaterPipeTube1", "dummy+middleSideWaterPipeTube1:rmiddleSideWaterPipeTube1"); - - TGeoTranslation* tmiddleSideWaterPipeTube_right1 = new TGeoTranslation("tmiddleSideWaterPipeTube_right1", - center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180) + side_pipe_tube_length1 / 2, - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * (1 - TMath::Sin((90 - side_pipe_torus_angle1) * TMath::Pi() / 180)), - 0); - TGeoTranslation* tmiddleSideWaterPipeTube_left1 = new TGeoTranslation("tmiddleSideWaterPipeTube_left1", - -(center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180) + side_pipe_tube_length1 / 2), - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * (1 - TMath::Sin((90 - side_pipe_torus_angle1) * TMath::Pi() / 180)), - 0); - tmiddleSideWaterPipeTube_right1->RegisterYourself(); - tmiddleSideWaterPipeTube_left1->RegisterYourself(); - - new TGeoCompositeShape("water_pipe_shape", - "middleCenterWaterPipeTorus1:rmiddleCenterWaterPipeTorus1" - " + rotated_middleSideWaterPipeTorus_right1:tmiddleSideWaterPipeTorus_right1 + rotated_middleSideWaterPipeTorus_left1:tmiddleSideWaterPipeTorus_left1" - " + rotated_middleSideWaterPipeTube1:tmiddleSideWaterPipeTube_right1 + rotated_middleSideWaterPipeTube1:tmiddleSideWaterPipeTube_left1"); - - TGeoTranslation* tmiddleWaterPipeTorus_shape1 = new TGeoTranslation("tmiddleWaterPipeTorus_shape1", 0, 0, middle_board_thickness / 2 - pipe_max_radius1); - TGeoTranslation* tmiddleWaterPipeTorus_shape2 = new TGeoTranslation("tmiddleWaterPipeTorus_shape2", 0, 0, -middle_board_thickness / 2 + pipe_max_radius1); - tmiddleWaterPipeTorus_shape1->RegisterYourself(); - tmiddleWaterPipeTorus_shape2->RegisterYourself(); - - TGeoCompositeShape* water_pipe_2side_shape = new TGeoCompositeShape("water_pipe_2side_shape", "water_pipe_shape:tmiddleWaterPipeTorus_shape1+water_pipe_shape:tmiddleWaterPipeTorus_shape2"); - - TGeoVolume* water_pipe = new TGeoVolume("water_pipe", water_pipe_2side_shape, kMedAlu); - water_pipe->SetLineColor(kGray); + Double_t water_pipe_main_angle = 126.19; - mHalfPSU->AddNode(water_pipe, 1, nullptr); + Double_t water_pipe_side_position_radius = (1.8 + 1.4) / 2; + Double_t water_pipe_side_angle = 63.1; - /////////////////////////////////////////////////////////////////////////////////////////////////////////// - //water in the pipe part - /////////////////////////////////////////////////////////////////////////////////////////////////////////// + Double_t water_pipe_straight_tube_side1_length = 2.865 / 2.; + Double_t water_pipe_straight_tube_side2_length = 8.950 / 2; - new TGeoTorus("middleCenterWaterTorus1", center_pipe_torus_radius1, 0, pipe_min_radius1, 0, center_pipe_torus_angle1); - auto* rmiddleCenterWaterTorus1 = new TGeoRotation("rmiddleCenterWaterTorus1", -(180 - 0.5 * (180 - center_pipe_torus_angle1)), 0, 0); - rmiddleCenterWaterTorus1->RegisterYourself(); + TGeoTorus* middle_spacer_sub_water_pipe_main_torus = new TGeoTorus("middle_spacer_sub_water_pipe_main_torus", water_pipe_main_position_radius, 0., water_pipe_outer_radius, 180 + (180 - water_pipe_main_angle) / 2., water_pipe_main_angle); - new TGeoTorus("middleSideWaterTorus1", side_pipe_torus_radius1, 0, pipe_min_radius1, 0, side_pipe_torus_angle1); + TGeoTorus* middle_spacer_sub_water_pipe_side_torus_left1 = new TGeoTorus("middle_spacer_sub_water_pipe_side_torus_left1", water_pipe_side_position_radius, 0., water_pipe_outer_radius, 90, water_pipe_side_angle); + TGeoTorus* middle_spacer_sub_water_pipe_side_torus_right1 = new TGeoTorus("middle_spacer_sub_water_pipe_side_torus_right1", water_pipe_side_position_radius, 0., water_pipe_outer_radius, 90 - water_pipe_side_angle, water_pipe_side_angle); - auto* rmiddleSideWaterTorus_right1 = new TGeoRotation("rmiddleSideWaterTorus_right1", 90, 0, 0); - auto* rmiddleSideWaterTorus_left1 = new TGeoRotation("rmiddleSideWaterTorus_left1", 90 - side_pipe_torus_angle1, 0, 0); - rmiddleSideWaterTorus_right1->RegisterYourself(); - rmiddleSideWaterTorus_left1->RegisterYourself(); + TGeoTranslation* trans_middle_spacer_sub_water_pipe_side_torus_left1 = new TGeoTranslation("trans_middle_spacer_sub_water_pipe_side_torus_left1", (water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.), -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.), 0); + TGeoTranslation* trans_middle_spacer_sub_water_pipe_side_torus_right1 = new TGeoTranslation("trans_middle_spacer_sub_water_pipe_side_torus_right1", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.), -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.), 0); + trans_middle_spacer_sub_water_pipe_side_torus_left1->RegisterYourself(); + trans_middle_spacer_sub_water_pipe_side_torus_right1->RegisterYourself(); - new TGeoCompositeShape("rotated_middleSideWaterTorus_right1", "dummy + middleSideWaterTorus1:rmiddleSideWaterTorus_right1"); - new TGeoCompositeShape("rotated_middleSideWaterTorus_left1", "dummy + middleSideWaterTorus1:rmiddleSideWaterTorus_left1"); + TGeoTubeSeg* middle_spacer_sub_water_pipe_straight_tube_side1 = new TGeoTubeSeg("middle_spacer_sub_water_pipe_straight_tube_side1", 0., water_pipe_outer_radius, water_pipe_straight_tube_side1_length, 0, 360); + TGeoRotation* rotate_middle_spacer_sub_water_pipe_straight_tube_side1 = new TGeoRotation("rotate_middle_spacer_sub_water_pipe_straight_tube_side1", 90, 90, 0); + rotate_middle_spacer_sub_water_pipe_straight_tube_side1->RegisterYourself(); - auto* tmiddleSideWaterTorus_right1 = new TGeoTranslation("tmiddleSideWaterTorus_right1", - center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180.), - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) - side_pipe_torus_radius1 * TMath::Cos(side_pipe_torus_angle1 * TMath::Pi() / 180.), 0); - auto* tmiddleSideWaterTorus_left1 = new TGeoTranslation("tmiddleSideWaterTorus_left1", - -(center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180.)), - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180.) - side_pipe_torus_radius1 * TMath::Cos(side_pipe_torus_angle1 * TMath::Pi() / 180.), 0); - tmiddleSideWaterTorus_right1->RegisterYourself(); - tmiddleSideWaterTorus_left1->RegisterYourself(); + TGeoCombiTrans* combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_left = new TGeoCombiTrans("combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_left", (water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius, 0, rotate_middle_spacer_sub_water_pipe_straight_tube_side1); + TGeoCombiTrans* combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_right = new TGeoCombiTrans("combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_right", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius, 0, rotate_middle_spacer_sub_water_pipe_straight_tube_side1); + combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_left->RegisterYourself(); + combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_right->RegisterYourself(); - new TGeoTube("middleSideWaterTube1", 0, pipe_min_radius1, side_pipe_tube_length1 / 2); + TGeoTorus* middle_spacer_sub_water_pipe_side_torus_left2 = new TGeoTorus("middle_spacer_sub_water_pipe_side_torus_left2", water_pipe_side_position_radius, 0., water_pipe_outer_radius, 0, 90); + TGeoTorus* middle_spacer_sub_water_pipe_side_torus_right2 = new TGeoTorus("middle_spacer_sub_water_pipe_side_torus_right2", water_pipe_side_position_radius, 0., water_pipe_outer_radius, 90, 90); - auto* rmiddleSideWaterTube1 = new TGeoRotation("rmiddleSideWaterTube1", 90, 90, 0); - rmiddleSideWaterTube1->RegisterYourself(); + TGeoTranslation* trans_middle_spacer_sub_water_pipe_side_torus_left2 = new TGeoTranslation("trans_middle_spacer_sub_water_pipe_side_torus_left2", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius, 0); + TGeoTranslation* trans_middle_spacer_sub_water_pipe_side_torus_right2 = new TGeoTranslation("trans_middle_spacer_sub_water_pipe_side_torus_right2", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius, 0); + trans_middle_spacer_sub_water_pipe_side_torus_left2->RegisterYourself(); + trans_middle_spacer_sub_water_pipe_side_torus_right2->RegisterYourself(); - new TGeoCompositeShape("rotated_middleSideWaterTube1", "dummy+middleSideWaterTube1:rmiddleSideWaterTube1"); + TGeoTubeSeg* middle_spacer_sub_water_pipe_straight_tube_side2 = new TGeoTubeSeg("middle_spacer_sub_water_pipe_straight_tube_side2", 0., water_pipe_outer_radius, water_pipe_straight_tube_side2_length, 0, 360); - TGeoTranslation* tmiddleSideWaterTube_right1 = new TGeoTranslation("tmiddleSideWaterTube_right1", - center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180) + side_pipe_tube_length1 / 2, - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * (1 - TMath::Sin((90 - side_pipe_torus_angle1) * TMath::Pi() / 180)), - 0); - TGeoTranslation* tmiddleSideWaterTube_left1 = new TGeoTranslation("tmiddleSideWaterTube_left1", - -(center_pipe_torus_radius1 * TMath::Sin(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * TMath::Sin(side_pipe_torus_angle1 * TMath::Pi() / 180) + side_pipe_tube_length1 / 2), - -center_pipe_torus_radius1 * TMath::Cos(center_pipe_torus_angle1 / 2 * TMath::Pi() / 180) + side_pipe_torus_radius1 * (1 - TMath::Sin((90 - side_pipe_torus_angle1) * TMath::Pi() / 180)), - 0); - tmiddleSideWaterTube_right1->RegisterYourself(); - tmiddleSideWaterTube_left1->RegisterYourself(); + TGeoRotation* rotate_middle_spacer_sub_water_pipe_straight_tube_side2 = new TGeoRotation("rotate_middle_spacer_sub_water_pipe_straight_tube_side2", 0, 90, 0); + rotate_middle_spacer_sub_water_pipe_straight_tube_side2->RegisterYourself(); - new TGeoCompositeShape("water_shape", - "middleCenterWaterTorus1:rmiddleCenterWaterTorus1" - " + rotated_middleSideWaterTorus_right1:tmiddleSideWaterTorus_right1 + rotated_middleSideWaterTorus_left1:tmiddleSideWaterTorus_left1" - " + rotated_middleSideWaterTube1:tmiddleSideWaterTube_right1 + rotated_middleSideWaterTube1:tmiddleSideWaterTube_left1"); + TGeoCombiTrans* combtrans_middle_spacer_sub_water_pipe_straight_tube_side_left2 = new TGeoCombiTrans("combtrans_middle_spacer_sub_water_pipe_straight_tube_side_left2", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2 + water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius - water_pipe_straight_tube_side2_length, 0, rotate_middle_spacer_sub_water_pipe_straight_tube_side2); - TGeoTranslation* tmiddleWaterTorus_shape1 = new TGeoTranslation("tmiddleWaterTorus_shape1", 0, 0, middle_board_thickness / 2 - pipe_max_radius1); - TGeoTranslation* tmiddleWaterTorus_shape2 = new TGeoTranslation("tmiddleWaterTorus_shape2", 0, 0, -middle_board_thickness / 2 + pipe_max_radius1); - tmiddleWaterTorus_shape1->RegisterYourself(); - tmiddleWaterTorus_shape2->RegisterYourself(); + TGeoCombiTrans* combtrans_middle_spacer_sub_water_pipe_straight_tube_side_right2 = new TGeoCombiTrans("combtrans_middle_spacer_sub_water_pipe_straight_tube_side_right2", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2 - water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius - water_pipe_straight_tube_side2_length, 0, rotate_middle_spacer_sub_water_pipe_straight_tube_side2); - TGeoCompositeShape* water_2side_shape = new TGeoCompositeShape("water_2side_shape", "water_shape:tmiddleWaterTorus_shape1+water_shape:tmiddleWaterTorus_shape2"); + combtrans_middle_spacer_sub_water_pipe_straight_tube_side_left2->RegisterYourself(); + combtrans_middle_spacer_sub_water_pipe_straight_tube_side_right2->RegisterYourself(); - TGeoVolume* water = new TGeoVolume("water", water_2side_shape, kMed_Water); - water->SetLineColor(kBlue); - mHalfPSU->AddNode(water, 1, nullptr); + TGeoCompositeShape* middle_spacer_shape = new TGeoCompositeShape("middle_spacer_shape", + "middle_spacer_main + middle_spacer_cover_bothside" + " - middle_spacer_sub_water_pipe_main_torus - middle_spacer_sub_water_pipe_side_torus_left1:trans_middle_spacer_sub_water_pipe_side_torus_left1 - middle_spacer_sub_water_pipe_side_torus_right1:trans_middle_spacer_sub_water_pipe_side_torus_right1" + " - middle_spacer_sub_water_pipe_straight_tube_side1:combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_left - middle_spacer_sub_water_pipe_straight_tube_side1:combtrans_rotated_middle_spacer_sub_water_pipe_straight_tube_side1_right" + " - middle_spacer_sub_water_pipe_side_torus_left2:trans_middle_spacer_sub_water_pipe_side_torus_left2 - middle_spacer_sub_water_pipe_side_torus_right2:trans_middle_spacer_sub_water_pipe_side_torus_right2" + " - middle_spacer_sub_water_pipe_straight_tube_side2:combtrans_middle_spacer_sub_water_pipe_straight_tube_side_left2 - middle_spacer_sub_water_pipe_straight_tube_side2:combtrans_middle_spacer_sub_water_pipe_straight_tube_side_right2"); + + TGeoVolume* middle_spacer = new TGeoVolume("middle_spacer", middle_spacer_shape, kMedAlu); + middle_spacer->SetLineColor(kTeal - 4); + mHalfPSU->AddNode(middle_spacer, 0, nullptr); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //Electoric board + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////////// - //PSU surface - /////////////////////////////////////////////////////////////////////////////////////////////////////////// - - Double_t surface_board_thickness = 0.16; - Double_t surface_board_max_radius1 = 26.432; - Double_t surface_board_min_radius1 = 17.0; - Double_t surfaceBox_sub_angle2 = 113.29 * TMath::Pi() / 180.; - Double_t surfaceBox_sub_width1 = 20.8; + Double_t electric_board_thickness = 0.171; + Double_t electric_board_max_radius1 = 26.706; + Double_t electric_board_min_radius1 = 17.0; + Double_t electric_board_sub_rectangle_middle_height = 5 + 4.347; + Double_t electric_board_sub_rectangle_middle_width = electric_board_max_radius1 * 2; + Double_t electric_board_sub_rectangle_bottom_height = 10.00; //laugh + Double_t electric_board_sub_rectangle_bottom_width = 20.80; + Double_t electric_board_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness / 2; - Double_t surfaceBox_sub_height1 = surface_board_max_radius1 * (1 - sqrt(1 - pow(surfaceBox_sub_width1 / 2. / surface_board_max_radius1, 2))); + TGeoTubeSeg* electric_board_main = new TGeoTubeSeg("electric_board_main", electric_board_min_radius1, electric_board_max_radius1, electric_board_thickness / 2, 180, 0); + TGeoTranslation* trans_electric_board_main = new TGeoTranslation("trans_electric_board_main", 0, 0, electric_board_position_z); + trans_electric_board_main->RegisterYourself(); - Double_t surfaceBox_sub_width2 = 2 * surface_board_max_radius1; - Double_t surfaceBox_sub_height2 = 2 * surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2); - - new TGeoTubeSeg("surfaceTubeSeg1", surface_board_min_radius1, surface_board_max_radius1, surface_board_thickness / 2, 180, 0); - new TGeoBBox("surfaceBox_sub1", surfaceBox_sub_width1 / 2, surfaceBox_sub_height1 / 2, surface_board_thickness); - new TGeoBBox("surfaceBox_sub2", surfaceBox_sub_width2 / 2, surfaceBox_sub_height2 / 2, surface_board_thickness); + TGeoBBox* electric_board_sub_rectangle_middle = new TGeoBBox("electric_board_sub_rectangle_middle", electric_board_sub_rectangle_middle_width / 2., electric_board_sub_rectangle_middle_height / 2., electric_board_thickness / 2. + delta_thickness); + TGeoBBox* electric_board_sub_rectangle_bottom = new TGeoBBox("electric_board_sub_rectangle_bottom", electric_board_sub_rectangle_bottom_width / 2., electric_board_sub_rectangle_bottom_height / 2., electric_board_thickness / 2. + delta_thickness); - auto* tsurfaceBox_sub1 = new TGeoTranslation("tsurfaceBox_sub1", 0, -(surface_board_max_radius1 - surfaceBox_sub_height1 / 2), 0); - auto* tsurfaceBox_sub2 = new TGeoTranslation("tsurfaceBox_sub2", 0., 0., 0.); - tsurfaceBox_sub1->RegisterYourself(); - tsurfaceBox_sub2->RegisterYourself(); + TGeoTranslation* trans_electric_board_sub_rectangle_middle = new TGeoTranslation("trans_electric_board_sub_rectangle_middle", 0, -electric_board_sub_rectangle_middle_height / 2., electric_board_position_z); + TGeoTranslation* trans_electric_board_sub_rectangle_bottom = new TGeoTranslation("trans_electric_board_sub_rectangle_bottom", 0, -(24.6 + electric_board_sub_rectangle_bottom_height / 2.), electric_board_position_z); + trans_electric_board_sub_rectangle_middle->RegisterYourself(); + trans_electric_board_sub_rectangle_bottom->RegisterYourself(); - Double_t surfaceBox_edge_width1 = 8.8; - Double_t surfaceBox_edge_height1 = 4.347; + Double_t electric_board_add_rectangle_side_height = 4.347; + Double_t electric_board_add_rectangle_side_width = 8.800; - Double_t surfaceBox_edge_sub_width1 = 5.; - Double_t surfaceBox_edge_sub_height1 = 8.025; + TGeoBBox* electric_board_add_rectangle_side = new TGeoBBox("electric_board_add_rectangle_side", electric_board_add_rectangle_side_width / 2., electric_board_add_rectangle_side_height / 2., electric_board_thickness / 2.); + TGeoTranslation* trans_electric_board_add_rectangle_side_left = new TGeoTranslation("trans_electric_board_add_rectangle_side_left", 28.4 / 2. + electric_board_add_rectangle_side_width / 2., -5.0 - electric_board_add_rectangle_side_height / 2., electric_board_position_z); + TGeoTranslation* trans_electric_board_add_rectangle_side_right = new TGeoTranslation("trans_electric_board_add_rectangle_side_right", -(28.4 / 2. + electric_board_add_rectangle_side_width / 2.), -5.0 - electric_board_add_rectangle_side_height / 2., electric_board_position_z); + trans_electric_board_add_rectangle_side_left->RegisterYourself(); + trans_electric_board_add_rectangle_side_right->RegisterYourself(); - new TGeoBBox("surfaceBox_edge1", surfaceBox_edge_width1 / 2, surfaceBox_edge_height1 / 2, surface_board_thickness / 2); - new TGeoBBox("surfaceBox_sub_edge1", surfaceBox_edge_sub_width1 / 2, surfaceBox_edge_sub_height1 / 2, surface_board_thickness); + Double_t electric_board_sub_rectangle_side_width = 10.; //raugh + Double_t electric_board_sub_rectangle_side_height = 8.576; - auto* tsurfaceBox_edge_right1 = new TGeoTranslation("tsurfaceBox_edge_right1", - surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 / 2, - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 / 2, 0); - auto* tsurfaceBox_edge_sub_right1 = new TGeoTranslation("tsurfaceBox_edge_sub_right1", - surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 + surfaceBox_edge_sub_width1 / 2, - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 - surfaceBox_edge_sub_height1 / 2, 0); - auto* tsurfaceBox_edge_left1 = new TGeoTranslation("tsurfaceBox_edge_left1", - -(surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 / 2), - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 / 2, 0); - auto* tsurfaceBox_edge_sub_left1 = new TGeoTranslation("tsurfaceBox_edge_sub_left1", - -(surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 + surfaceBox_edge_sub_width1 / 2), - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 - surfaceBox_edge_sub_height1 / 2, 0); + TGeoBBox* electric_board_sub_rectangle_side = new TGeoBBox("electric_board_sub_rectangle_side", electric_board_sub_rectangle_side_width / 2., electric_board_sub_rectangle_side_height / 2., electric_board_thickness / 2. + delta_thickness); + TGeoTranslation* trans_electric_board_sub_rectangle_side_left = new TGeoTranslation("trans_electric_board_sub_rectangle_side_left", 28.4 / 2 + 8.8 + electric_board_sub_rectangle_side_width / 2., -5.0 - electric_board_sub_rectangle_side_height / 2., electric_board_position_z); + TGeoTranslation* trans_electric_board_sub_rectangle_side_right = new TGeoTranslation("trans_electric_board_sub_rectangle_side_right", -(28.4 / 2 + 8.8 + electric_board_sub_rectangle_side_width / 2.), -5.0 - electric_board_sub_rectangle_side_height / 2., electric_board_position_z); + trans_electric_board_sub_rectangle_side_left->RegisterYourself(); + trans_electric_board_sub_rectangle_side_right->RegisterYourself(); - tsurfaceBox_edge_right1->RegisterYourself(); - tsurfaceBox_edge_sub_right1->RegisterYourself(); + Double_t electric_board_sub_box_height = 1.1; + Double_t electric_board_sub_box_width = 1.1; - tsurfaceBox_edge_left1->RegisterYourself(); - tsurfaceBox_edge_sub_left1->RegisterYourself(); + TGeoBBox* electric_board_sub_box = new TGeoBBox("electric_board_sub_box", electric_board_sub_box_width / 2, electric_board_sub_box_height / 2, electric_board_thickness / 2 + delta_thickness); - Double_t surfaceBox_center_sub_min_radius1 = 18.0; - Double_t surfaceBox_center_sub_max_radius1 = 19.0; - Double_t surfaceBox_center_sub_max_angle1 = 130; + TGeoCompositeShape* rotated_electric_board_sub_box[24]; - new TGeoTubeSeg("surfaceCenterTubeSeg_sub1", surfaceBox_center_sub_min_radius1, surfaceBox_center_sub_max_radius1, middle_board_thickness, - 180 + (180 - surfaceBox_center_sub_max_angle1) / 2, 180 + (180 - surfaceBox_center_sub_max_angle1) / 2 + surfaceBox_center_sub_max_angle1); + std::string name_electric_board_shape = + "electric_board_main:trans_electric_board_main - electric_board_sub_rectangle_middle:trans_electric_board_sub_rectangle_middle - electric_board_sub_rectangle_bottom:trans_electric_board_sub_rectangle_bottom" + " + electric_board_add_rectangle_side:trans_electric_board_add_rectangle_side_left + electric_board_add_rectangle_side:trans_electric_board_add_rectangle_side_right" + " - electric_board_sub_rectangle_side:trans_electric_board_sub_rectangle_side_left - electric_board_sub_rectangle_side:trans_electric_board_sub_rectangle_side_right"; - Double_t surfaceBox_edge_sub_width2 = 1.; - Double_t surfaceBox_edge_sub_height2 = 1.; - Double_t surfaceBox_edge_sub_refposi_x = 0.76; - Double_t surfaceBox_edge_sub_refposi_y = 4.004; + for (Int_t iB = 0; iB < 11; ++iB) { - new TGeoBBox("surfaceBox_square_sub2", surfaceBox_edge_sub_width2 / 2, surfaceBox_edge_sub_height2 / 2, surface_board_thickness); - auto* tsurfaceBox_square_sub_right2 = new TGeoTranslation("tsurfaceBox_square_sub_right2", - surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 - surfaceBox_edge_sub_refposi_x - surfaceBox_edge_sub_width2 / 2, - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 - surfaceBox_edge_sub_refposi_y - surfaceBox_edge_sub_height2 / 2, 0); - auto* tsurfaceBox_square_sub_left2 = new TGeoTranslation("tsurfaceBox_square_sub_left2", - -(surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 - surfaceBox_edge_sub_refposi_x - surfaceBox_edge_sub_width2 / 2), - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 - surfaceBox_edge_sub_refposi_y - surfaceBox_edge_sub_height2 / 2, 0); - tsurfaceBox_square_sub_right2->RegisterYourself(); - tsurfaceBox_square_sub_left2->RegisterYourself(); + Double_t block_angle = (180. - block_angle_index[iB]) / 2. * TMath::Pi() / 180.; - new TGeoCompositeShape("surface_board_shape", - "surfaceTubeSeg1 - surfaceBox_sub1:tsurfaceBox_sub1 - surfaceBox_sub2:tsurfaceBox_sub2" - "+surfaceBox_edge1:tsurfaceBox_edge_right1 - surfaceBox_sub_edge1:tsurfaceBox_edge_sub_right1" - "+surfaceBox_edge1:tsurfaceBox_edge_left1 - surfaceBox_sub_edge1:tsurfaceBox_edge_sub_left1" - "-surfaceCenterTubeSeg_sub1 - surfaceBox_square_sub2:tsurfaceBox_square_sub_right2" - "-surfaceCenterTubeSeg_sub1 - surfaceBox_square_sub2:tsurfaceBox_square_sub_left2"); + TGeoRotation* rotate_electric_board_sub_box_left = new TGeoRotation(Form("rotate_electric_board_sub_box_No%d", iB * 2), 180 - block_angle * 180. / TMath::Pi(), 0, 0); + TGeoRotation* rotate_electric_board_sub_box_right = new TGeoRotation(Form("rotate_electric_board_sub_box_No%d", iB * 2 + 1), block_angle * 180. / TMath::Pi(), 0, 0); + rotate_electric_board_sub_box_left->RegisterYourself(); + rotate_electric_board_sub_box_right->RegisterYourself(); - auto* tsurface_board_shape1 = new TGeoTranslation("tsurface_board_shape1", 0, 0, middle_board_thickness / 2 + surface_board_thickness / 2); - auto* tsurface_board_shape2 = new TGeoTranslation("tsurface_board_shape2", 0, 0, -(middle_board_thickness / 2 + surface_board_thickness / 2)); - tsurface_board_shape1->RegisterYourself(); - tsurface_board_shape2->RegisterYourself(); + Double_t cent_block_left[] = {block_radius * TMath::Cos(block_angle), -block_radius * TMath::Sin(block_angle), electric_board_position_z}; + Double_t cent_block_right[] = {block_radius * TMath::Cos(TMath::Pi() - block_angle), -block_radius * TMath::Sin(TMath::Pi() - block_angle), electric_board_position_z}; - TGeoCompositeShape* surface_board_2side_shape = new TGeoCompositeShape("surface_board_2side_shape", "surface_board_shape:tsurface_board_shape1 + surface_board_shape:tsurface_board_shape2"); + TGeoCombiTrans* combtrans_electric_board_sub_box_left = new TGeoCombiTrans(Form("combtrans_electric_board_sub_box_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2], rotate_electric_board_sub_box_left); + TGeoCombiTrans* combtrans_electric_board_sub_box_right = new TGeoCombiTrans(Form("combtrans_electric_board_sub_box_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2], rotate_electric_board_sub_box_right); + combtrans_electric_board_sub_box_left->RegisterYourself(); + combtrans_electric_board_sub_box_right->RegisterYourself(); - TGeoVolume* surface_board = new TGeoVolume("surface_board", surface_board_2side_shape, kMedPeek); - surface_board->SetLineColor(kGreen + 3); + name_electric_board_shape += Form("-electric_board_sub_box:combtrans_electric_board_sub_box_No%d", 2 * iB); + name_electric_board_shape += Form("-electric_board_sub_box:combtrans_electric_board_sub_box_No%d", 2 * iB + 1); + } - mHalfPSU->AddNode(surface_board, 1, nullptr); + TGeoTranslation* trans_electric_board_sub_box_left = new TGeoTranslation("trans_electric_board_sub_box_left", 22.259 - electric_board_sub_box_width / 2, -8.305 + electric_board_sub_box_height / 2, electric_board_position_z); + trans_electric_board_sub_box_left->RegisterYourself(); + name_electric_board_shape += "-electric_board_sub_box:trans_electric_board_sub_box_left"; - /////////////////////////////////////////////////////////////////////////////////////////////////////////// - //PSU DCDC - /////////////////////////////////////////////////////////////////////////////////////////////////////////// + TGeoTranslation* trans_electric_board_sub_box_right = new TGeoTranslation("trans_electric_board_sub_box_right", -22.247 + electric_board_sub_box_width / 2, -8.305 + electric_board_sub_box_height / 2, electric_board_position_z); + trans_electric_board_sub_box_right->RegisterYourself(); + name_electric_board_shape += "-electric_board_sub_box:trans_electric_board_sub_box_right"; - Int_t nDCDC = 22; + TGeoCompositeShape* electric_board_shape = new TGeoCompositeShape("electric_board_shape", name_electric_board_shape.c_str()); - Double_t DCDC_main_width1 = 1.4; - Double_t DCDC_main_height1 = 1.85; - Double_t DCDC_main_thickness1 = 0.8; + TGeoRotation* trans_electric_board_front = new TGeoRotation("trans_electric_board_front", 0, 0, 0); + TGeoRotation* trans_electric_board_back = new TGeoRotation("trans_electric_board_back", 180, 180, 0); - new TGeoBBox("DCDC_main_shape1", DCDC_main_width1 / 2, DCDC_main_height1 / 2, DCDC_main_thickness1 / 2); - - Double_t DCDC_width2 = 1.694; - Double_t DCDC_height2 = 4.348; - Double_t DCDC_thickness2 = 0.04; + TGeoVolume* electric_board = new TGeoVolume("electric_board", electric_board_shape, kMedPeek); + electric_board->SetLineColor(kGreen + 2); + mHalfPSU->AddNode(electric_board, 0, trans_electric_board_front); + mHalfPSU->AddNode(electric_board, 0, trans_electric_board_back); - new TGeoBBox("DCDC_shape2", DCDC_width2 / 2, DCDC_height2 / 2, DCDC_thickness2 / 2); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //Water pipe + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Double_t DCDC_sub_edge_thickness1 = 0.02; - Double_t DCDC_sub_width1 = DCDC_main_width1 - DCDC_sub_edge_thickness1 * 2; - Double_t DCDC_sub_height1 = DCDC_main_height1 - DCDC_sub_edge_thickness1 * 2; - Double_t DCDC_sub_thickness1 = DCDC_main_thickness1; - - new TGeoBBox("DCDC_sub_shape1", DCDC_sub_width1 / 2, DCDC_sub_height1 / 2, DCDC_sub_thickness1 / 2); - - TGeoTranslation* tDCDC_sub_shape1 = new TGeoTranslation("tDCDC_sub_shape1", 0, 0, DCDC_sub_edge_thickness1); - tDCDC_sub_shape1->RegisterYourself(); - - new TGeoCompositeShape("DCDC_shape1", "DCDC_main_shape1 - DCDC_sub_shape1:tDCDC_sub_shape1"); + TGeoTorus* water_pipe_main_torus = new TGeoTorus("water_pipe_main_torus", water_pipe_main_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 180 + (180 - water_pipe_main_angle) / 2., water_pipe_main_angle); + + TGeoTorus* water_pipe_side_torus_left1 = new TGeoTorus("water_pipe_side_torus_left1", water_pipe_side_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 90, water_pipe_side_angle); + TGeoTorus* water_pipe_side_torus_right1 = new TGeoTorus("water_pipe_side_torus_right1", water_pipe_side_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 90 - water_pipe_side_angle, water_pipe_side_angle); + + TGeoTranslation* trans_water_pipe_side_torus_left1 = new TGeoTranslation("trans_water_pipe_side_torus_left1", (water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.), -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.), 0); + TGeoTranslation* trans_water_pipe_side_torus_right1 = new TGeoTranslation("trans_water_pipe_side_torus_right1", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.), -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.), 0); + trans_water_pipe_side_torus_left1->RegisterYourself(); + trans_water_pipe_side_torus_right1->RegisterYourself(); + + TGeoTubeSeg* water_pipe_straight_tube_side1 = new TGeoTubeSeg("water_pipe_straight_tube_side1", water_pipe_inner_radius, water_pipe_outer_radius, water_pipe_straight_tube_side1_length, 0, 360); + + TGeoRotation* rotate_water_pipe_straight_tube_side1 = new TGeoRotation("rotate_water_pipe_straight_tube_side1", 90, 90, 0); + rotate_water_pipe_straight_tube_side1->RegisterYourself(); + + TGeoCombiTrans* combtrans_rotated_water_pipe_straight_tube_side1_left = new TGeoCombiTrans("combtrans_rotated_water_pipe_straight_tube_side1_left", (water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius, 0, rotate_water_pipe_straight_tube_side1); + TGeoCombiTrans* combtrans_rotated_water_pipe_straight_tube_side1_right = new TGeoCombiTrans("combtrans_rotated_water_pipe_straight_tube_side1_right", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius, 0, rotate_water_pipe_straight_tube_side1); + combtrans_rotated_water_pipe_straight_tube_side1_left->RegisterYourself(); + combtrans_rotated_water_pipe_straight_tube_side1_right->RegisterYourself(); + + TGeoTorus* water_pipe_side_torus_left2 = new TGeoTorus("water_pipe_side_torus_left2", water_pipe_side_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 0, 90); + TGeoTorus* water_pipe_side_torus_right2 = new TGeoTorus("water_pipe_side_torus_right2", water_pipe_side_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 90, 90); + TGeoTranslation* trans_water_pipe_side_torus_left2 = new TGeoTranslation("trans_water_pipe_side_torus_left2", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2, -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius, 0); + TGeoTranslation* trans_water_pipe_side_torus_right2 = new TGeoTranslation("trans_water_pipe_side_torus_right2", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2, -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius, 0); + trans_water_pipe_side_torus_left2->RegisterYourself(); + trans_water_pipe_side_torus_right2->RegisterYourself(); + + TGeoTubeSeg* water_pipe_straight_tube_side2 = new TGeoTubeSeg("water_pipe_straight_tube_side2", water_pipe_inner_radius, water_pipe_outer_radius, water_pipe_straight_tube_side2_length, 0, 360); + TGeoRotation* rotate_water_pipe_straight_tube_side2 = new TGeoRotation("rotate_water_pipe_straight_tube_side2", 0, 90, 0); + + rotate_water_pipe_straight_tube_side2->RegisterYourself(); + + TGeoCombiTrans* combtrans_water_pipe_straight_tube_side_left2 = new TGeoCombiTrans("combtrans_water_pipe_straight_tube_side_left2", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2 + water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius - water_pipe_straight_tube_side2_length, 0, rotate_water_pipe_straight_tube_side2); + + TGeoCombiTrans* combtrans_water_pipe_straight_tube_side_right2 = new TGeoCombiTrans("combtrans_water_pipe_straight_tube_side_right2", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2 - water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius - water_pipe_straight_tube_side2_length, 0, rotate_water_pipe_straight_tube_side2); + + combtrans_water_pipe_straight_tube_side_left2->RegisterYourself(); + combtrans_water_pipe_straight_tube_side_right2->RegisterYourself(); + + //==================== PIPE - connection to the patch panel ====================== + TGeoTorus* pipe_side_torus_left3 = new TGeoTorus("pipe_side_torus_left3", water_pipe_side_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 0, 90); + TGeoRotation* rotate_water_torus_left3 = new TGeoRotation("rotate_water_torus_left3", -90, 90, 0); + TGeoCombiTrans* combtrans_water_torus_left3 = new TGeoCombiTrans("combtrans_water_torus_left3", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2 + water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - 2 * water_pipe_straight_tube_side2_length, -water_pipe_side_position_radius, rotate_water_torus_left3); + combtrans_water_torus_left3->RegisterYourself(); + Float_t water_pipe_straight_tube_side3_length = 5; + TGeoTubeSeg* pipe_straight_tube_left3 = new TGeoTubeSeg("pipe_straight_tube_left3", water_pipe_inner_radius, water_pipe_outer_radius, water_pipe_straight_tube_side3_length, 0, 360); + TGeoRotation* rotate_water_straight_tube_left3 = new TGeoRotation("rotate_water_straight_tube_left3", 0, 0, 0); + TGeoCombiTrans* combtrans_water_straight_tube_side_left3 = new TGeoCombiTrans("combtrans_water_straight_tube_side_left3", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2 + water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - 2 * water_pipe_straight_tube_side2_length - water_pipe_side_position_radius, -water_pipe_straight_tube_side3_length - water_pipe_side_position_radius, rotate_water_straight_tube_left3); + combtrans_water_straight_tube_side_left3->RegisterYourself(); + + TGeoTorus* pipe_side_torus_rigth3 = new TGeoTorus("pipe_side_torus_rigth3", water_pipe_side_position_radius, water_pipe_inner_radius, water_pipe_outer_radius, 0, 90); + TGeoRotation* rotate_water_torus_rigth3 = new TGeoRotation("rotate_water_torus_rigth3", -90, 90, 0); + TGeoCombiTrans* combtrans_water_torus_rigth3 = new TGeoCombiTrans("combtrans_water_torus_rigth3", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2 - water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - 2 * water_pipe_straight_tube_side2_length, -water_pipe_side_position_radius, rotate_water_torus_rigth3); + combtrans_water_torus_rigth3->RegisterYourself(); + + TGeoTubeSeg* pipe_straight_tube_rigth3 = new TGeoTubeSeg("pipe_straight_tube_rigth3", water_pipe_inner_radius, water_pipe_outer_radius, water_pipe_straight_tube_side3_length, 0, 360); + TGeoRotation* rotate_water_straight_tube_rigth3 = new TGeoRotation("rotate_water_straight_tube_rigth3", 0, 0, 0); + TGeoCombiTrans* combtrans_water_straight_tube_side_rigth3 = new TGeoCombiTrans("combtrans_water_straight_tube_side_rigth3", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2 - water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - 2 * water_pipe_straight_tube_side2_length - water_pipe_side_position_radius, -water_pipe_straight_tube_side3_length - water_pipe_side_position_radius, rotate_water_straight_tube_rigth3); + combtrans_water_straight_tube_side_rigth3->RegisterYourself(); + + TGeoCompositeShape* water_pipe_toPatchPanel = new TGeoCompositeShape("water_pipe_toPatchPanel", + "pipe_straight_tube_left3:combtrans_water_straight_tube_side_left3" + " + pipe_side_torus_left3:combtrans_water_torus_left3" + " + pipe_straight_tube_rigth3:combtrans_water_straight_tube_side_rigth3" + " + pipe_side_torus_rigth3:combtrans_water_torus_rigth3"); + TGeoVolume* poly_pipe = new TGeoVolume("poly_pipe_toPatchPanel", water_pipe_toPatchPanel, kMedPolyPipe); + poly_pipe->SetLineColor(kGray); + mHalfPSU->AddNode(poly_pipe, 1, nullptr); + + //================================================== + + TGeoCompositeShape* water_pipe_shape = new TGeoCompositeShape("water_pipe_shape", + "water_pipe_main_torus + water_pipe_side_torus_left1:trans_water_pipe_side_torus_left1 + water_pipe_side_torus_right1:trans_water_pipe_side_torus_right1" + " + water_pipe_straight_tube_side1:combtrans_rotated_water_pipe_straight_tube_side1_left + water_pipe_straight_tube_side1:combtrans_rotated_water_pipe_straight_tube_side1_right" + " + water_pipe_side_torus_left2:trans_water_pipe_side_torus_left2 + water_pipe_side_torus_right2:trans_water_pipe_side_torus_right2" + " + water_pipe_straight_tube_side2:combtrans_water_pipe_straight_tube_side_left2 + water_pipe_straight_tube_side2:combtrans_water_pipe_straight_tube_side_right2" + " + pipe_straight_tube_left3:combtrans_water_straight_tube_side_left3" + " + pipe_side_torus_left3:combtrans_water_torus_left3" + " + pipe_straight_tube_rigth3:combtrans_water_straight_tube_side_rigth3" + " + pipe_side_torus_rigth3:combtrans_water_torus_rigth3"); + + TGeoVolume* water_pipe = new TGeoVolume("water_pipe", water_pipe_shape, kMedAlu); + water_pipe->SetLineColor(kGray); + mHalfPSU->AddNode(water_pipe, 1, nullptr); - Double_t full_angle = 126; - Double_t one_angle = 126. / (nDCDC - 1); - Double_t start_DCDC_angle = 180 + (180 - full_angle) / 2; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //Water + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + TGeoTorus* water_main_torus = new TGeoTorus("water_main_torus", water_pipe_main_position_radius, 0, water_pipe_inner_radius, 180 + (180 - water_pipe_main_angle) / 2., water_pipe_main_angle); + + TGeoTorus* water_side_torus_left1 = new TGeoTorus("water_side_torus_left1", water_pipe_side_position_radius, 0, water_pipe_inner_radius, 90, water_pipe_side_angle); + TGeoTorus* water_side_torus_right1 = new TGeoTorus("water_side_torus_right1", water_pipe_side_position_radius, 0, water_pipe_inner_radius, 90 - water_pipe_side_angle, water_pipe_side_angle); + + TGeoTranslation* trans_water_side_torus_left1 = new TGeoTranslation("trans_water_side_torus_left1", (water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.), -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.), 0); + TGeoTranslation* trans_water_side_torus_right1 = new TGeoTranslation("trans_water_side_torus_right1", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.), -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.), 0); + trans_water_side_torus_left1->RegisterYourself(); + trans_water_side_torus_right1->RegisterYourself(); + + TGeoTubeSeg* water_straight_tube_side1 = new TGeoTubeSeg("water_straight_tube_side1", 0, water_pipe_inner_radius, water_pipe_straight_tube_side1_length, 0, 360); + TGeoRotation* rotate_water_straight_tube_side1 = new TGeoRotation("rotate_water_straight_tube_side1", 90, 90, 0); + rotate_water_straight_tube_side1->RegisterYourself(); + + TGeoCombiTrans* combtrans_rotated_water_straight_tube_side1_left = new TGeoCombiTrans("combtrans_rotated_water_straight_tube_side1_left", (water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length, -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius, 0, rotate_water_straight_tube_side1); + TGeoCombiTrans* combtrans_rotated_water_straight_tube_side1_right = new TGeoCombiTrans("combtrans_rotated_water_straight_tube_side1_right", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length, -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius, 0, rotate_water_straight_tube_side1); + combtrans_rotated_water_straight_tube_side1_left->RegisterYourself(); + combtrans_rotated_water_straight_tube_side1_right->RegisterYourself(); + + TGeoTorus* water_side_torus_left2 = new TGeoTorus("water_side_torus_left2", water_pipe_side_position_radius, 0, water_pipe_inner_radius, 0, 90); + TGeoTorus* water_side_torus_right2 = new TGeoTorus("water_side_torus_right2", water_pipe_side_position_radius, 0, water_pipe_inner_radius, 90, 90); + TGeoTranslation* trans_water_side_torus_left2 = new TGeoTranslation("trans_water_side_torus_left2", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2, -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius, 0); + TGeoTranslation* trans_water_side_torus_right2 = new TGeoTranslation("trans_water_side_torus_right2", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2, -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius, 0); + trans_water_side_torus_left2->RegisterYourself(); + trans_water_side_torus_right2->RegisterYourself(); + + TGeoTubeSeg* water_straight_tube_side2 = new TGeoTubeSeg("water_straight_tube_side2", 0, water_pipe_inner_radius, water_pipe_straight_tube_side2_length, 0, 360); + TGeoRotation* rotate_water_straight_tube_side2 = new TGeoRotation("rotate_water_straight_tube_side2", 0, 90, 0); + rotate_water_straight_tube_side2->RegisterYourself(); + + TGeoCombiTrans* combtrans_water_straight_tube_side_left2 = new TGeoCombiTrans("combtrans_water_straight_tube_side_left2", +(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_straight_tube_side1_length * 2 + water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius - water_pipe_straight_tube_side2_length, 0, rotate_water_straight_tube_side2); + + TGeoCombiTrans* combtrans_water_straight_tube_side_right2 = new TGeoCombiTrans("combtrans_water_straight_tube_side_right2", -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Cos((90 - water_pipe_side_angle) * TMath::Pi() / 180.) - water_pipe_straight_tube_side1_length * 2 - water_pipe_side_position_radius, + -(water_pipe_main_position_radius + water_pipe_side_position_radius) * TMath::Sin((90 - water_pipe_side_angle) * TMath::Pi() / 180.) + water_pipe_side_position_radius - water_pipe_side_position_radius - water_pipe_straight_tube_side2_length, 0, rotate_water_straight_tube_side2); + + combtrans_water_straight_tube_side_left2->RegisterYourself(); + combtrans_water_straight_tube_side_right2->RegisterYourself(); + + //============ WATER, Connecting to the Patch Panel =============== + TGeoTorus* water_side_torus_left3 = new TGeoTorus("water_side_torus_left3", water_pipe_side_position_radius, 0, water_pipe_inner_radius, 0, 90); + TGeoTubeSeg* water_straight_tube_left3 = new TGeoTubeSeg("water_straight_tube_left3", 0, water_pipe_inner_radius, water_pipe_straight_tube_side3_length, 0, 360); + TGeoTorus* water_side_torus_rigth3 = new TGeoTorus("water_side_torus_rigth3", water_pipe_side_position_radius, 0, water_pipe_inner_radius, 0, 90); + TGeoTubeSeg* water_straight_tube_rigth3 = new TGeoTubeSeg("water_straight_tube_rigth3", 0, water_pipe_inner_radius, water_pipe_straight_tube_side3_length, 0, 360); + //================================================================== + + TGeoCompositeShape* water_shape = new TGeoCompositeShape("water_shape", + "water_main_torus + water_side_torus_left1:trans_water_side_torus_left1 + water_side_torus_right1:trans_water_side_torus_right1" + " + water_straight_tube_side1:combtrans_rotated_water_straight_tube_side1_left + water_straight_tube_side1:combtrans_rotated_water_straight_tube_side1_right" + " + water_side_torus_left2:trans_water_side_torus_left2 + water_side_torus_right2:trans_water_side_torus_right2" + " + water_straight_tube_side2:combtrans_water_straight_tube_side_left2 + water_straight_tube_side2:combtrans_water_straight_tube_side_right2" + " + water_straight_tube_left3:combtrans_water_straight_tube_side_left3" + " + water_side_torus_left3:combtrans_water_torus_left3" + " + water_straight_tube_rigth3:combtrans_water_straight_tube_side_rigth3" + " + water_side_torus_rigth3:combtrans_water_torus_rigth3"); + + TGeoVolume* water = new TGeoVolume("water", water_shape, kMed_Water); + water->SetLineColor(kBlue); + mHalfPSU->AddNode(water, 1, nullptr); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //DCDC cobverter + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Double_t position_radius = 18.5; - - TGeoRotation* rDCDC_shape1[22]; - TGeoTranslation* tDCDC_shape1[22]; - TGeoCompositeShape* rotated_DCDC_shape1[22]; - - TGeoRotation* rDCDC_shape2[22]; - TGeoTranslation* tDCDC_shape2[22]; - TGeoCompositeShape* rotated_DCDC_shape2[22]; - - TString string_all_DCDC_shape1 = ""; - TString string_all_DCDC_shape2 = ""; + //Sheet1 - for (Int_t iDCDC = 0; iDCDC < nDCDC; ++iDCDC) { - rDCDC_shape1[iDCDC] = new TGeoRotation(Form("rDCDC_shape1_angle%d", iDCDC + 1), full_angle / 2 - one_angle * iDCDC, 0, 0); - rDCDC_shape2[iDCDC] = new TGeoRotation(Form("rDCDC_shape2_angle%d", iDCDC + 1), full_angle / 2 - one_angle * iDCDC, 0, 0); - rDCDC_shape1[iDCDC]->RegisterYourself(); - rDCDC_shape2[iDCDC]->RegisterYourself(); + Double_t DCDC_sheet1_thickness = 0.040; + Double_t DCDC_sheet1_height = 1.694; + Double_t DCDC_sheet1_width = 4.348; + Double_t DCDC_sheet1_radius = (block_radius + DCDC_sheet1_width / 2. - 0.673 - 1.85 + 1.85 / 2.); - rotated_DCDC_shape1[iDCDC] = new TGeoCompositeShape(Form("rotated_DCDC_shape1_angle%d", iDCDC + 1), Form("dummy+DCDC_shape1:rDCDC_shape1_angle%d", iDCDC + 1)); - rotated_DCDC_shape2[iDCDC] = new TGeoCompositeShape(Form("rotated_DCDC_shape2_angle%d", iDCDC + 1), Form("dummy+DCDC_shape2:rDCDC_shape2_angle%d", iDCDC + 1)); + Double_t DCDC_sheet1_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + middle_spacer_cover_block_thickness + DCDC_sheet1_thickness / 2; + //Double_t DCDC_sheet1_position_z = electric_board_position_z + electric_board_thickness/2. + DCDC_sheet1_thickness/2.; - tDCDC_shape1[iDCDC] = new TGeoTranslation(Form("tDCDC_shape1_pos%d", iDCDC + 1), - -position_radius * TMath::Cos((start_DCDC_angle + one_angle * iDCDC) * TMath::Pi() / 180.), - position_radius * TMath::Sin((start_DCDC_angle + one_angle * iDCDC) * TMath::Pi() / 180.), 0); - tDCDC_shape2[iDCDC] = new TGeoTranslation(Form("tDCDC_shape2_pos%d", iDCDC + 1), - -(position_radius + DCDC_height2 / 5) * TMath::Cos((start_DCDC_angle + one_angle * iDCDC) * TMath::Pi() / 180.), - (position_radius + DCDC_height2 / 5) * TMath::Sin((start_DCDC_angle + one_angle * iDCDC) * TMath::Pi() / 180.), 0); - tDCDC_shape1[iDCDC]->RegisterYourself(); - tDCDC_shape2[iDCDC]->RegisterYourself(); + TGeoBBox* DCDC_sheet1_box = new TGeoBBox("DCDC_sheet1_box", DCDC_sheet1_width / 2., DCDC_sheet1_height / 2., DCDC_sheet1_thickness / 2.); - if (iDCDC + 1 == nDCDC) { - string_all_DCDC_shape1 += Form("rotated_DCDC_shape1_angle%d:tDCDC_shape1_pos%d", iDCDC + 1, iDCDC + 1); - string_all_DCDC_shape2 += Form("rotated_DCDC_shape2_angle%d:tDCDC_shape2_pos%d", iDCDC + 1, iDCDC + 1); + TGeoCompositeShape* rotated_DCDC_sheet1_box[24]; + + std::string name_DCDC_sheet1_box = ""; + + for (Int_t iB = 0; iB < 11; ++iB) { + + Double_t block_angle = (180. - block_angle_index[iB]) / 2. * TMath::Pi() / 180.; + + TGeoRotation* rotate_DCDC_sheet1_box_left = new TGeoRotation(Form("rotate_DCDC_sheet1_box_No%d", iB * 2), 180 - block_angle * 180. / TMath::Pi(), 0, 0); + TGeoRotation* rotate_DCDC_sheet1_box_right = new TGeoRotation(Form("rotate_DCDC_sheet1_box_No%d", iB * 2 + 1), block_angle * 180. / TMath::Pi(), 0, 0); + rotate_DCDC_sheet1_box_left->RegisterYourself(); + rotate_DCDC_sheet1_box_right->RegisterYourself(); + + Double_t cent_block_left[] = {DCDC_sheet1_radius * TMath::Cos(block_angle), -DCDC_sheet1_radius * TMath::Sin(block_angle), DCDC_sheet1_position_z}; + Double_t cent_block_right[] = {DCDC_sheet1_radius * TMath::Cos(TMath::Pi() - block_angle), -DCDC_sheet1_radius * TMath::Sin(TMath::Pi() - block_angle), DCDC_sheet1_position_z}; + + TGeoCombiTrans* combtrans_DCDC_sheet1_box_left = new TGeoCombiTrans(Form("combtrans_DCDC_sheet1_box_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2], rotate_DCDC_sheet1_box_left); + TGeoCombiTrans* combtrans_DCDC_sheet1_box_right = new TGeoCombiTrans(Form("combtrans_DCDC_sheet1_box_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2], rotate_DCDC_sheet1_box_right); + combtrans_DCDC_sheet1_box_left->RegisterYourself(); + combtrans_DCDC_sheet1_box_right->RegisterYourself(); + + if (iB == 0) { + name_DCDC_sheet1_box += Form("DCDC_sheet1_box:combtrans_DCDC_sheet1_box_No%d", 2 * iB); } else { - string_all_DCDC_shape1 += Form("rotated_DCDC_shape1_angle%d:tDCDC_shape1_pos%d+", iDCDC + 1, iDCDC + 1); - string_all_DCDC_shape2 += Form("rotated_DCDC_shape2_angle%d:tDCDC_shape2_pos%d+", iDCDC + 1, iDCDC + 1); + name_DCDC_sheet1_box += Form("+DCDC_sheet1_box:combtrans_DCDC_sheet1_box_No%d", 2 * iB); } + + name_DCDC_sheet1_box += Form("+DCDC_sheet1_box:combtrans_DCDC_sheet1_box_No%d", 2 * iB + 1); } - string_all_DCDC_shape1 += "+ DCDC_shape1:tsurfaceBox_square_sub_right2 + DCDC_shape1:tsurfaceBox_square_sub_left2"; + TGeoBBox* DCDC_sheet1_box_side = new TGeoBBox("DCDC_sheet1_box_side", DCDC_sheet1_height / 2., DCDC_sheet1_width / 2., DCDC_sheet1_thickness / 2.); - auto* tDCDC_shape_side_right2 = new TGeoTranslation("tDCDC_shape_side_right2", - surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 - surfaceBox_edge_sub_refposi_x - surfaceBox_edge_sub_width2 / 2, - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 - surfaceBox_edge_sub_refposi_y - surfaceBox_edge_sub_height2 / 2 + DCDC_height2 / 5., - 0); + TGeoTranslation* trans_DCDC_sheet1_box_left = new TGeoTranslation("trans_DCDC_sheet1_box_left", 22.259 - middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2 - 0.576, DCDC_sheet1_position_z); + trans_DCDC_sheet1_box_left->RegisterYourself(); + name_DCDC_sheet1_box += "+DCDC_sheet1_box_side:trans_DCDC_sheet1_box_left"; - auto* tDCDC_shape_side_left2 = new TGeoTranslation("tDCDC_shape_side_left2", - -(surface_board_min_radius1 * TMath::Sin(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_width1 - surfaceBox_edge_sub_refposi_x - surfaceBox_edge_sub_width2 / 2), - -surface_board_min_radius1 * TMath::Cos(surfaceBox_sub_angle2 / 2) + surfaceBox_edge_height1 - surfaceBox_edge_sub_refposi_y - surfaceBox_edge_sub_height2 / 2 + DCDC_height2 / 5., - 0); - tDCDC_shape_side_right2->RegisterYourself(); - tDCDC_shape_side_left2->RegisterYourself(); + TGeoTranslation* trans_DCDC_sheet1_box_right = new TGeoTranslation("trans_DCDC_sheet1_box_right", -22.247 + middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2 - 0.576, DCDC_sheet1_position_z); + trans_DCDC_sheet1_box_right->RegisterYourself(); + name_DCDC_sheet1_box += "+DCDC_sheet1_box_side:trans_DCDC_sheet1_box_right"; - string_all_DCDC_shape2 += "+ DCDC_shape2:tDCDC_shape_side_right2 + DCDC_shape2:tDCDC_shape_side_left2"; + TGeoCompositeShape* DCDC_sheet1_shape = new TGeoCompositeShape("DCDC_sheet1_shape", name_DCDC_sheet1_box.c_str()); - new TGeoCompositeShape("all_DCDC_shape1", string_all_DCDC_shape1); + TGeoVolume* DCDC_sheet1 = new TGeoVolume("DCDC_sheet1", DCDC_sheet1_shape, kMedPeek); + DCDC_sheet1->SetLineColor(kGreen); - TGeoRotation* rall_DCDC_shape1 = new TGeoRotation("rall_DCDC_shape1", 0, 180, 180); - rall_DCDC_shape1->RegisterYourself(); + TGeoRotation* trans_DCDC_sheet1_front = new TGeoRotation("trans_DCDC_sheet1_front", 0, 0, 0); + TGeoRotation* trans_DCDC_sheet1_back = new TGeoRotation("trans_DCDC_sheet1_back", 180, 180, 0); - new TGeoCompositeShape("all_DCDC_shape_opposit", "dummy+all_DCDC_shape1:rall_DCDC_shape1"); + mHalfPSU->AddNode(DCDC_sheet1, 0, trans_DCDC_sheet1_front); + mHalfPSU->AddNode(DCDC_sheet1, 0, trans_DCDC_sheet1_back); - new TGeoCompositeShape("all_DCDC_base_shape", string_all_DCDC_shape2); + //Cover box - auto* tall_DCDC_shape_shape1 = new TGeoTranslation("tall_DCDC_shape_shape1", 0, 0, middle_board_thickness / 2 + surface_board_thickness + DCDC_thickness2 + DCDC_main_thickness1 / 2); - auto* tall_DCDC_shape_shape2 = new TGeoTranslation("tall_DCDC_shape_shape2", 0, 0, -(middle_board_thickness / 2 + surface_board_thickness + DCDC_thickness2 + DCDC_main_thickness1 / 2)); - tall_DCDC_shape_shape1->RegisterYourself(); - tall_DCDC_shape_shape2->RegisterYourself(); + Double_t DCDC_cover_thickness = 0.800; + Double_t DCDC_cover_outer_height = 1.400; + Double_t DCDC_cover_outer_width = 1.85; + Double_t DCDC_cover_depth = 0.05; + Double_t DCDC_cover_inner_width = DCDC_cover_outer_width - 2 * DCDC_cover_depth; + Double_t DCDC_cover_inner_height = DCDC_cover_outer_height - 2 * DCDC_cover_depth; - auto* tall_DCDC_base_shape_shape1 = new TGeoTranslation("tall_DCDC_base_shape_shape1", 0, 0, middle_board_thickness / 2 + surface_board_thickness + DCDC_thickness2 / 2); - auto* tall_DCDC_base_shape_shape2 = new TGeoTranslation("tall_DCDC_base_shape_shape2", 0, 0, -(middle_board_thickness / 2 + surface_board_thickness + DCDC_thickness2 / 2)); - tall_DCDC_base_shape_shape1->RegisterYourself(); - tall_DCDC_base_shape_shape2->RegisterYourself(); + Double_t DCDC_cover_position_z = DCDC_sheet1_position_z + DCDC_sheet1_thickness / 2. + DCDC_cover_thickness / 2.; - TGeoCompositeShape* two_side_all_DCDC_shape = new TGeoCompositeShape("two_side_all_DCDC_shape", "all_DCDC_shape_opposit:tall_DCDC_shape_shape1 + all_DCDC_shape1:tall_DCDC_shape_shape2"); + TGeoBBox* DCDC_cover_outer_box = new TGeoBBox("DCDC_cover_outer_box", DCDC_cover_outer_width / 2., DCDC_cover_outer_height / 2., DCDC_cover_thickness / 2.); + TGeoBBox* DCDC_cover_innner_box = new TGeoBBox("DCDC_cover_inner_box", DCDC_cover_inner_width / 2., DCDC_cover_inner_height / 2., DCDC_cover_thickness / 2.); - TGeoCompositeShape* two_side_all_DCDC_base_shape = new TGeoCompositeShape("two_side_all_DCDC_base_shape", - "all_DCDC_base_shape:tall_DCDC_base_shape_shape1 + all_DCDC_base_shape:tall_DCDC_base_shape_shape2"); + TGeoCompositeShape* rotated_DCDC_cover_outer_box[23]; + TGeoCompositeShape* rotated_DCDC_cover_inner_box[23]; - TGeoVolume* DCDC1 = new TGeoVolume("DCDC1", two_side_all_DCDC_shape, kMedAlu); - DCDC1->SetLineColor(kGray); + std::string name_DCDC_cover_box = ""; - TGeoVolume* DCDC2 = new TGeoVolume("DCDC2", two_side_all_DCDC_base_shape, kMedAlu); - DCDC2->SetLineColor(kSpring); + for (Int_t iB = 0; iB < 11; ++iB) { - mHalfPSU->AddNode(DCDC1, 1, nullptr); - mHalfPSU->AddNode(DCDC2, 1, nullptr); + Double_t block_angle = (180. - block_angle_index[iB]) / 2. * TMath::Pi() / 180.; - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // DC-DC coil part - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + TGeoRotation* rotate_DCDC_cover_box_left = new TGeoRotation(Form("rotate_DCDC_cover_box_No%d", iB * 2), 180 - block_angle * 180. / TMath::Pi(), 0, 0); + TGeoRotation* rotate_DCDC_cover_box_right = new TGeoRotation(Form("rotate_DCDC_cover_box_No%d", iB * 2 + 1), block_angle * 180. / TMath::Pi(), 0, 0); + rotate_DCDC_cover_box_left->RegisterYourself(); + rotate_DCDC_cover_box_right->RegisterYourself(); - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Very detaild coil shape part - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Double_t cent_block_left[] = {block_radius * TMath::Cos(block_angle), -block_radius * TMath::Sin(block_angle), DCDC_cover_position_z}; + Double_t cent_block_right[] = {block_radius * TMath::Cos(TMath::Pi() - block_angle), -block_radius * TMath::Sin(TMath::Pi() - block_angle), DCDC_cover_position_z}; - Double_t DCDC_coil_radius1 = 0.1; - Double_t DCDC_coil_min_radius1 = 0; - Double_t DCDC_coil_max_radius1 = DCDC_coil_radius1 - 0.075; - Double_t DCDC_coil_straight_gap = DCDC_coil_radius1 / 20.; - Double_t DCDC_coil_torus_part_radius = 2. / 5. * DCDC_width2 / 2.; - - new TGeoTorus("DCDC_coil_main_shape", DCDC_coil_radius1, 0, DCDC_coil_max_radius1, 0, 360); - - TGeoTranslation* tDCDC_coil_straight[9]; - tDCDC_coil_straight[0] = new TGeoTranslation("tDCDC_coil_straight_No0", 0, 0, 0); - tDCDC_coil_straight[1] = new TGeoTranslation("tDCDC_coil_straight_No1", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * 1); - tDCDC_coil_straight[2] = new TGeoTranslation("tDCDC_coil_straight_No2", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * 2); - tDCDC_coil_straight[3] = new TGeoTranslation("tDCDC_coil_straight_No3", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * 3); - tDCDC_coil_straight[4] = new TGeoTranslation("tDCDC_coil_straight_No4", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * 4); - tDCDC_coil_straight[5] = new TGeoTranslation("tDCDC_coil_straight_No5", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * -1); - tDCDC_coil_straight[6] = new TGeoTranslation("tDCDC_coil_straight_No6", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * -2); - tDCDC_coil_straight[7] = new TGeoTranslation("tDCDC_coil_straight_No7", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * -3); - tDCDC_coil_straight[8] = new TGeoTranslation("tDCDC_coil_straight_No8", 0, 0, (DCDC_coil_max_radius1 * 2 + DCDC_coil_straight_gap) * -4); - - for (Int_t i = 0; i < 9; ++i) - tDCDC_coil_straight[i]->RegisterYourself(); - - new TGeoCompositeShape("DCDC_coil_straight_shape1", - "DCDC_coil_main_shape:tDCDC_coil_straight_No0" - "+ DCDC_coil_main_shape:tDCDC_coil_straight_No1 + DCDC_coil_main_shape:tDCDC_coil_straight_No2" - "+ DCDC_coil_main_shape:tDCDC_coil_straight_No3 + DCDC_coil_main_shape:tDCDC_coil_straight_No4" - "+ DCDC_coil_main_shape:tDCDC_coil_straight_No5 + DCDC_coil_main_shape:tDCDC_coil_straight_No6" - "+ DCDC_coil_main_shape:tDCDC_coil_straight_No7 + DCDC_coil_main_shape:tDCDC_coil_straight_No8"); - TGeoRotation* rDCDC_coil_torus[7]; - Double_t DCDC_coil_torus_part_angle[] = {15, 30, 60, 90, 120, 150, 165}; - - TGeoCompositeShape* rotated_DCDC_coil_main_shape[7]; - for (Int_t i = 0; i < 7; ++i) { - rDCDC_coil_torus[i] = new TGeoRotation(Form("rDCDC_coil_torus_No%d", i), -90, DCDC_coil_torus_part_angle[i], 0); - rDCDC_coil_torus[i]->RegisterYourself(); - rotated_DCDC_coil_main_shape[i] = new TGeoCompositeShape(Form("rotated_DCDC_coil_main_shape_No%d", i), Form("dummy + DCDC_coil_main_shape:rDCDC_coil_torus_No%d", i)); + TGeoCombiTrans* combtrans_DCDC_cover_outer_box_left = new TGeoCombiTrans(Form("combtrans_DCDC_cover_outer_box_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2], rotate_DCDC_cover_box_left); + TGeoCombiTrans* combtrans_DCDC_cover_outer_box_right = new TGeoCombiTrans(Form("combtrans_DCDC_cover_outer_box_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2], rotate_DCDC_cover_box_right); + combtrans_DCDC_cover_outer_box_left->RegisterYourself(); + combtrans_DCDC_cover_outer_box_right->RegisterYourself(); + + TGeoCombiTrans* combtrans_DCDC_cover_inner_box_left = new TGeoCombiTrans(Form("combtrans_DCDC_cover_inner_box_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2] - 2 * DCDC_cover_depth, rotate_DCDC_cover_box_left); + TGeoCombiTrans* combtrans_DCDC_cover_inner_box_right = new TGeoCombiTrans(Form("combtrans_DCDC_cover_inner_box_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2] - 2 * DCDC_cover_depth, rotate_DCDC_cover_box_right); + combtrans_DCDC_cover_inner_box_left->RegisterYourself(); + combtrans_DCDC_cover_inner_box_right->RegisterYourself(); + + if (iB == 0) { + name_DCDC_cover_box += Form("DCDC_cover_outer_box:combtrans_DCDC_cover_outer_box_No%d - DCDC_cover_inner_box:combtrans_DCDC_cover_inner_box_No%d", 2 * iB, 2 * iB); + } else { + name_DCDC_cover_box += Form("+DCDC_cover_outer_box:combtrans_DCDC_cover_outer_box_No%d - DCDC_cover_inner_box:combtrans_DCDC_cover_inner_box_No%d", 2 * iB, 2 * iB); + } + name_DCDC_cover_box += Form("+DCDC_cover_outer_box:combtrans_DCDC_cover_outer_box_No%d - DCDC_cover_inner_box:combtrans_DCDC_cover_inner_box_No%d", 2 * iB + 1, 2 * iB + 1); } - TGeoTranslation* trotated_DCDC_coil_main_shape[7]; + TGeoBBox* DCDC_cover_outer_box_side = new TGeoBBox("DCDC_cover_outer_box_side", DCDC_cover_outer_height / 2., DCDC_cover_outer_width / 2., DCDC_cover_thickness / 2.); + TGeoBBox* DCDC_cover_innner_box_side = new TGeoBBox("DCDC_cover_inner_box_side", DCDC_cover_inner_height / 2., DCDC_cover_inner_width / 2., DCDC_cover_thickness / 2.); + + TGeoTranslation* trans_DCDC_cover_outer_box_left = new TGeoTranslation("trans_DCDC_cover_outer_box_left", 22.259 - middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, DCDC_cover_position_z); + TGeoTranslation* trans_DCDC_cover_inner_box_left = new TGeoTranslation("trans_DCDC_cover_inner_box_left", 22.259 - middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, DCDC_cover_position_z - 2 * DCDC_cover_depth); + trans_DCDC_cover_outer_box_left->RegisterYourself(); + trans_DCDC_cover_inner_box_left->RegisterYourself(); + name_DCDC_cover_box += "+DCDC_cover_outer_box_side:trans_DCDC_cover_outer_box_left - DCDC_cover_inner_box_side:trans_DCDC_cover_inner_box_left"; + + TGeoTranslation* trans_DCDC_cover_outer_box_right = new TGeoTranslation("trans_DCDC_cover_outer_box_right", -22.247 + middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, DCDC_cover_position_z); + TGeoTranslation* trans_DCDC_cover_inner_box_right = new TGeoTranslation("trans_DCDC_cover_inner_box_right", -22.247 + middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, DCDC_cover_position_z - 2 * DCDC_cover_depth); + trans_DCDC_cover_outer_box_right->RegisterYourself(); + trans_DCDC_cover_inner_box_right->RegisterYourself(); + name_DCDC_cover_box += "+DCDC_cover_outer_box_side:trans_DCDC_cover_outer_box_right - DCDC_cover_inner_box_side:trans_DCDC_cover_inner_box_right"; + + TGeoCompositeShape* DCDC_cover_shape = new TGeoCompositeShape("DCDC_cover_shape", name_DCDC_cover_box.c_str()); + TGeoVolume* DCDC_cover = new TGeoVolume("DCDC_cover", DCDC_cover_shape, kMedAlu); + DCDC_cover->SetLineColor(kGray); + + TGeoRotation* trans_DCDC_cover_front = new TGeoRotation("trans_DCDC_cover_front", 0, 0, 0); + TGeoRotation* trans_DCDC_cover_back = new TGeoRotation("trans_DCDC_cover_back", 180, 180, 0); + + mHalfPSU->AddNode(DCDC_cover, 0, trans_DCDC_cover_front); + mHalfPSU->AddNode(DCDC_cover, 0, trans_DCDC_cover_back); + + //DCDC converter connector + + Double_t DCDC_connector_thickness = 0.225; + Double_t DCDC_connector_height = 1.44; + Double_t DCDC_connector_width = 0.305; + Double_t DCDC_connector_radius = (block_radius + DCDC_sheet1_width / 2. - 0.673 - 1.85 + 1.85 / 2. + DCDC_sheet1_width / 2 - 0.55 - 0.305 + 0.305 / 2.); + Double_t DCDC_connector_position_z = DCDC_sheet1_position_z + DCDC_sheet1_thickness / 2. + DCDC_connector_thickness / 2; + + TGeoBBox* DCDC_connector_box = new TGeoBBox("DCDC_connector_box", DCDC_connector_width / 2., DCDC_connector_height / 2., DCDC_connector_thickness / 2.); + + TGeoCompositeShape* rotated_DCDC_connector_box[23]; + + std::string name_DCDC_connector_box = ""; + + for (Int_t iB = 0; iB < 11; ++iB) { - for (Int_t i = 0; i < 7; ++i) { - trotated_DCDC_coil_main_shape[i] = new TGeoTranslation(Form("trotated_DCDC_coil_main_shape_No%d", i), - DCDC_coil_torus_part_radius * TMath::Cos(DCDC_coil_torus_part_angle[i] * TMath::Pi() / 180.), - 0, - DCDC_coil_torus_part_radius * TMath::Sin(DCDC_coil_torus_part_angle[i] * TMath::Pi() / 180.)); - trotated_DCDC_coil_main_shape[i]->RegisterYourself(); + Double_t block_angle = (180. - block_angle_index[iB]) / 2. * TMath::Pi() / 180.; + + TGeoRotation* rotate_DCDC_connector_box_left = new TGeoRotation(Form("rotate_DCDC_connector_box_No%d", iB * 2), 180 - block_angle * 180. / TMath::Pi(), 0, 0); + TGeoRotation* rotate_DCDC_connector_box_right = new TGeoRotation(Form("rotate_DCDC_connector_box_No%d", iB * 2 + 1), block_angle * 180. / TMath::Pi(), 0, 0); + rotate_DCDC_connector_box_left->RegisterYourself(); + rotate_DCDC_connector_box_right->RegisterYourself(); + Double_t cent_block_left[] = {DCDC_connector_radius * TMath::Cos(block_angle), -DCDC_connector_radius * TMath::Sin(block_angle), DCDC_connector_position_z}; + Double_t cent_block_right[] = {DCDC_connector_radius * TMath::Cos(TMath::Pi() - block_angle), -DCDC_connector_radius * TMath::Sin(TMath::Pi() - block_angle), DCDC_connector_position_z}; + + TGeoCombiTrans* combtrans_DCDC_connector_box_left = new TGeoCombiTrans(Form("combtrans_DCDC_connector_box_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2], rotate_DCDC_connector_box_left); + TGeoCombiTrans* combtrans_DCDC_connector_box_right = new TGeoCombiTrans(Form("combtrans_DCDC_connector_box_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2], rotate_DCDC_connector_box_right); + combtrans_DCDC_connector_box_left->RegisterYourself(); + combtrans_DCDC_connector_box_right->RegisterYourself(); + + if (iB == 0) { + name_DCDC_connector_box += Form("DCDC_connector_box:combtrans_DCDC_connector_box_No%d", 2 * iB); + } else { + name_DCDC_connector_box += Form("+DCDC_connector_box:combtrans_DCDC_connector_box_No%d", 2 * iB); + } + name_DCDC_connector_box += Form("+DCDC_connector_box:combtrans_DCDC_connector_box_No%d", 2 * iB + 1); } - new TGeoCompositeShape("DCDC_coil_torus_shape1", - "rotated_DCDC_coil_main_shape_No0:trotated_DCDC_coil_main_shape_No0" - "+ rotated_DCDC_coil_main_shape_No1:trotated_DCDC_coil_main_shape_No1" - "+ rotated_DCDC_coil_main_shape_No2:trotated_DCDC_coil_main_shape_No2" - "+ rotated_DCDC_coil_main_shape_No3:trotated_DCDC_coil_main_shape_No3" - "+ rotated_DCDC_coil_main_shape_No4:trotated_DCDC_coil_main_shape_No4" - "+ rotated_DCDC_coil_main_shape_No5:trotated_DCDC_coil_main_shape_No5" - "+ rotated_DCDC_coil_main_shape_No6:trotated_DCDC_coil_main_shape_No6"); - - TGeoTranslation* tDCDC_coil_straight_shape1[2]; - tDCDC_coil_straight_shape1[0] = new TGeoTranslation("tDCDC_coil_straight_shape1_right", DCDC_coil_torus_part_radius, 0, 0); - tDCDC_coil_straight_shape1[1] = new TGeoTranslation("tDCDC_coil_straight_shape1_left", -DCDC_coil_torus_part_radius, 0, 0); - tDCDC_coil_straight_shape1[0]->RegisterYourself(); - tDCDC_coil_straight_shape1[1]->RegisterYourself(); - - TGeoRotation* rDCDC_coil_torus_shape1[2]; - rDCDC_coil_torus_shape1[0] = new TGeoRotation("rDCDC_coil_torus_shape1_top", 0, 0, 0); - rDCDC_coil_torus_shape1[1] = new TGeoRotation("rDCDC_coil_torus_shape1_bottom", 0, 180, 0); - rDCDC_coil_torus_shape1[0]->RegisterYourself(); - rDCDC_coil_torus_shape1[1]->RegisterYourself(); - - TGeoCompositeShape* rotated_DCDC_coil_torus_shape1[2]; - rotated_DCDC_coil_torus_shape1[0] = new TGeoCompositeShape("rotated_DCDC_coil_torus_shape1_top", "dummy+DCDC_coil_torus_shape1:rDCDC_coil_torus_shape1_top"); - rotated_DCDC_coil_torus_shape1[1] = new TGeoCompositeShape("rotated_DCDC_coil_torus_shape1_bottom", "dummy+DCDC_coil_torus_shape1:rDCDC_coil_torus_shape1_bottom"); - - TGeoTranslation* trotated_DCDC_coil_torus_shape1[2]; - trotated_DCDC_coil_torus_shape1[0] = new TGeoTranslation("trotated_DCDC_coil_torus_shape1_top", 0, 0, (DCDC_coil_max_radius1 + DCDC_coil_straight_gap / 4.) * 9); - trotated_DCDC_coil_torus_shape1[1] = new TGeoTranslation("trotated_DCDC_coil_torus_shape1_bottom", 0, 0, -(DCDC_coil_max_radius1 + DCDC_coil_straight_gap / 4.) * 9); - trotated_DCDC_coil_torus_shape1[0]->RegisterYourself(); - trotated_DCDC_coil_torus_shape1[1]->RegisterYourself(); - /* - TGeoCompositeShape *DCDC_coil_shape1 = new TGeoCompositeShape("DCDC_coil_shape1", - "DCDC_coil_straight_shape1:tDCDC_coil_straight_shape1_right + DCDC_coil_straight_shape1:tDCDC_coil_straight_shape1_left" - "+rotated_DCDC_coil_torus_shape1_top:trotated_DCDC_coil_torus_shape1_top" - "+rotated_DCDC_coil_torus_shape1_bottom:trotated_DCDC_coil_torus_shape1_bottom"); - */ + TGeoBBox* DCDC_connector_box_side = new TGeoBBox("DCDC_connector_box_side", DCDC_connector_height / 2., DCDC_connector_width / 2., DCDC_connector_thickness / 2.); + + TGeoTranslation* trans_DCDC_connector_box_left = new TGeoTranslation("trans_DCDC_connector_box_left", 22.259 - middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2 - 2.019, DCDC_connector_position_z); + trans_DCDC_connector_box_left->RegisterYourself(); + name_DCDC_connector_box += "+DCDC_connector_box_side:trans_DCDC_connector_box_left"; + + TGeoTranslation* trans_DCDC_connector_box_right = new TGeoTranslation("trans_DCDC_connector_box_right", -22.247 + middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2 - 2.019, DCDC_connector_position_z); + trans_DCDC_connector_box_right->RegisterYourself(); + name_DCDC_connector_box += "+DCDC_connector_box_side:trans_DCDC_connector_box_right"; + + TGeoCompositeShape* DCDC_connector_shape = new TGeoCompositeShape("DCDC_connector_shape", name_DCDC_connector_box.c_str()); + + TGeoVolume* DCDC_connector = new TGeoVolume("DCDC_connector", DCDC_connector_shape, kMedPeek); + DCDC_connector->SetLineColor(kGray + 2); + + TGeoRotation* trans_DCDC_connector_front = new TGeoRotation("trans_DCDC_connector_front", 0, 0, 0); + TGeoRotation* trans_DCDC_connector_back = new TGeoRotation("trans_DCDC_connector_back", 180, 180, 0); + + mHalfPSU->AddNode(DCDC_connector, 0, trans_DCDC_connector_front); + mHalfPSU->AddNode(DCDC_connector, 0, trans_DCDC_connector_back); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //Mezzanine + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + //prop + + Double_t mezzanine_prop_main_length = 1.615; + Double_t mezzanine_prop_main_radius = 0.476 / 2.; + Double_t mezzanine_prop_small_length = 0.16; + Double_t mezzanine_prop_small_radius = 0.3 / 2.; + Double_t mezzanine_prop_lid_radius = 0.57 / 2.; + + Double_t mezzanine_prop_main_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length / 2; + Double_t mezzanine_prop_small_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length + mezzanine_prop_small_length / 2.; + Double_t mezzanine_prop_lid_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length + mezzanine_prop_small_length; + + TGeoTubeSeg* mezzanine_prop_main_tube = new TGeoTubeSeg("mezzanine_prop_main_tube", 0, mezzanine_prop_main_radius, mezzanine_prop_main_length / 2., 0, 360); + TGeoTubeSeg* mezzanine_prop_small_tube = new TGeoTubeSeg("mezzanine_prop_small_tube", 0, mezzanine_prop_small_radius, mezzanine_prop_small_length / 2., 0, 360); + TGeoSphere* mezzanine_prop_lid_sphere = new TGeoSphere("mezzanine_prop_lid_sphere", 0, mezzanine_prop_lid_radius, 0, 90, 0, 360); + TGeoTranslation* trans_mezzanine_prop_main_tube = new TGeoTranslation("trans_mezzanine_prop_main_tube", 0, 0, mezzanine_prop_main_position_z); + TGeoTranslation* trans_mezzanine_prop_small_tube = new TGeoTranslation("trans_mezzanine_prop_small_tube", 0, 0, mezzanine_prop_small_position_z); + TGeoTranslation* trans_mezzanine_prop_lid_sphere = new TGeoTranslation("trans_mezzanine_prop_lid_sphere", 0, 0, mezzanine_prop_lid_position_z); + trans_mezzanine_prop_main_tube->RegisterYourself(); + trans_mezzanine_prop_small_tube->RegisterYourself(); + trans_mezzanine_prop_lid_sphere->RegisterYourself(); + + TGeoCompositeShape* mazzanine_prop_shape = new TGeoCompositeShape("mazzanine_prop_shape", "mezzanine_prop_main_tube:trans_mezzanine_prop_main_tube + mezzanine_prop_small_tube:trans_mezzanine_prop_small_tube + mezzanine_prop_lid_sphere:trans_mezzanine_prop_lid_sphere"); + TGeoTranslation* trans_mezzanine_prop_left = new TGeoTranslation("trans_mezzanine_prop_left", +8, -21.5, 0); + TGeoTranslation* trans_mezzanine_prop_right = new TGeoTranslation("trans_mezzanine_prop_right", -8, -21.5, 0); + trans_mezzanine_prop_left->RegisterYourself(); + trans_mezzanine_prop_right->RegisterYourself(); + TGeoCompositeShape* mazzanine_prop_shape_bothside = new TGeoCompositeShape("mazzanine_prop_shape_bothside", "mazzanine_prop_shape:trans_mezzanine_prop_left+mazzanine_prop_shape:trans_mezzanine_prop_right"); + + TGeoVolume* mazzanine_prop = new TGeoVolume("mazzanine_prop", mazzanine_prop_shape_bothside, kMedAlu); + mazzanine_prop->SetLineColor(kAzure - 3); + + TGeoRotation* trans_mazzanine_prop_front = new TGeoRotation("trans_mazzanine_prop_front", 0, 0, 0); + TGeoRotation* trans_mazzanine_prop_back = new TGeoRotation("trans_mazzanine_prop_back", 180, 180, 0); + + mHalfPSU->AddNode(mazzanine_prop, 0, trans_mazzanine_prop_front); + mHalfPSU->AddNode(mazzanine_prop, 0, trans_mazzanine_prop_back); + + //main + + Double_t mezzanine_main_thickness = mezzanine_prop_small_length; + Double_t mezzanine_main_width = 20.7; + Double_t mezzanine_main_height = 9.5; + Double_t mezzanine_main_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length + mezzanine_main_thickness / 2.; + + TGeoBBox* mezzanine_main_box = new TGeoBBox("mezzanine_main_box", mezzanine_main_width / 2., mezzanine_main_height / 2., mezzanine_main_thickness / 2.); + TGeoTubeSeg* mezzanine_main_sub_arc = new TGeoTubeSeg("mezzanine_main_sub_arc", 0, electric_board_min_radius1, mezzanine_main_thickness / 2 + delta_thickness, 180, 0); + TGeoTubeSeg* mezzanine_main_sub_hole = new TGeoTubeSeg("mezzanine_main_sub_hole", 0, mezzanine_prop_small_radius, mezzanine_main_thickness / 2 + delta_thickness, 0, 360); + + TGeoTranslation* trans_mezzanine_main_box = new TGeoTranslation("trans_mezzanine_main_box", 0, -15.1 - mezzanine_main_height / 2., mezzanine_main_position_z); + TGeoTranslation* trans_mezzanine_main_sub_arc = new TGeoTranslation("trans_mezzanine_main_sub_arc", 0, 0, mezzanine_main_position_z); + TGeoTranslation* trans_mezzanine_main_sub_hole_left = new TGeoTranslation("trans_mezzanine_main_sub_hole_left", +8, -21.5, mezzanine_main_position_z); + TGeoTranslation* trans_mezzanine_main_sub_hole_right = new TGeoTranslation("trans_mezzanine_main_sub_hole_right", +8, -21.5, mezzanine_main_position_z); + trans_mezzanine_main_box->RegisterYourself(); + trans_mezzanine_main_sub_arc->RegisterYourself(); + trans_mezzanine_main_sub_hole_right->RegisterYourself(); + trans_mezzanine_main_sub_hole_left->RegisterYourself(); + + TGeoCompositeShape* mezzanine_shape = new TGeoCompositeShape("mezzanine_shape", "mezzanine_main_box:trans_mezzanine_main_box - mezzanine_main_sub_arc:trans_mezzanine_main_sub_arc - mezzanine_main_sub_hole:trans_mezzanine_main_sub_hole_left - mezzanine_main_sub_hole:trans_mezzanine_main_sub_hole_right"); + TGeoVolume* mezzanine = new TGeoVolume("mezzanine", mezzanine_shape, kMedPeek); + mezzanine->SetLineColor(kGreen + 2); + + TGeoRotation* trans_mezzanine_front = new TGeoRotation("trans_mazzanine_front", 0, 0, 0); + TGeoRotation* trans_mazzanine_back = new TGeoRotation("trans_mazzanine_back", 180, 180, 0); + + mHalfPSU->AddNode(mezzanine, 0, trans_mezzanine_front); + mHalfPSU->AddNode(mezzanine, 0, trans_mazzanine_back); + + //connector + + Double_t mezzanine_connector_base_box_thickness = 1.186; + Double_t mezzanine_connector_base_box_width = 6.778; + Double_t mezzanine_connector_base_box_height = 1.595; + Double_t mezzanine_connector_base_box_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_connector_base_box_thickness / 2; + + Double_t mezzanine_connector_base_sub_box_thickness = mezzanine_connector_base_box_thickness - 0.307; + Double_t mezzanine_connector_base_sub_box_width = 6.778; + Double_t mezzanine_connector_base_sub_box_height = 1.468; + + TGeoBBox* mezzanine_connector_base_box = new TGeoBBox("mezzanine_connector_base", mezzanine_connector_base_box_width / 2., mezzanine_connector_base_box_height / 2., mezzanine_connector_base_box_thickness / 2.); + TGeoBBox* mezzanine_connector_base_sub_box = new TGeoBBox("mezzanine_connector_base_sub_box", mezzanine_connector_base_sub_box_width / 2. + delta_thickness, mezzanine_connector_base_sub_box_height / 2., mezzanine_connector_base_sub_box_thickness / 2.); + + TGeoTranslation* trans_mezzanine_connector_base_box = new TGeoTranslation("trans_mezzanine_connector_base_box", 0, -22.421 - mezzanine_connector_base_box_height / 2.0, mezzanine_connector_base_box_position_z); + TGeoTranslation* trans_mezzanine_connector_base_sub_box = new TGeoTranslation("trans_mezzanine_connector_base_sub_box", 0, -22.421 - mezzanine_connector_base_box_height / 2.0, mezzanine_connector_base_box_position_z + (mezzanine_connector_base_box_thickness - mezzanine_connector_base_sub_box_thickness) / 2.); + trans_mezzanine_connector_base_box->RegisterYourself(); + trans_mezzanine_connector_base_sub_box->RegisterYourself(); + + TGeoCompositeShape* mezzanine_connector_base_shape = new TGeoCompositeShape("mezzanine_connector_base_shape", "mezzanine_connector_base:trans_mezzanine_connector_base_box - mezzanine_connector_base_sub_box:trans_mezzanine_connector_base_sub_box"); + TGeoVolume* mezzanine_connector_base = new TGeoVolume("mezzanine_connector_base", mezzanine_connector_base_shape, kMedPeek); + mezzanine_connector_base->SetLineColor(kOrange + 7); + + TGeoRotation* trans_mezzanine_connector_base_front = new TGeoRotation("trans_mezzanine_connector_base_front", 0, 0, 0); + TGeoRotation* trans_mezzanine_connector_base_back = new TGeoRotation("trans_mezzanine_connector_base_back", 180, 180, 0); + mHalfPSU->AddNode(mezzanine_connector_base, 0, trans_mezzanine_connector_base_front); + mHalfPSU->AddNode(mezzanine_connector_base, 0, trans_mezzanine_connector_base_back); + + Double_t mezzanine_connector_lid_bottom_box_thickness = 0.112; + Double_t mezzanine_connector_lid_bottom_box_width = 6.778; + Double_t mezzanine_connector_lid_bottom_box_height = 1.468; + Double_t mezzanine_connector_lid_bottom_box_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + 0.967 + mezzanine_connector_lid_bottom_box_thickness / 2.; + + TGeoBBox* mezzanine_connector_lid_bottom_box = new TGeoBBox("mezzanine_connector_lid_bottom_box", mezzanine_connector_lid_bottom_box_width / 2., mezzanine_connector_lid_bottom_box_height / 2., mezzanine_connector_lid_bottom_box_thickness / 2.); + TGeoVolume* mezzanine_connector_lid_bottom = new TGeoVolume("mezzanine_connector_lid_bottom", mezzanine_connector_lid_bottom_box, kMedPeek); + mezzanine_connector_lid_bottom->SetLineColor(kGray + 2); + + TGeoTranslation* trans_mezzanine_connector_lid_bottom_front = new TGeoTranslation("trans_mezzanine_connector_lid_bottom_front", 0, 0 - 22.421 - mezzanine_connector_base_box_height / 2., mezzanine_connector_lid_bottom_box_position_z); + TGeoTranslation* trans_mezzanine_connector_lid_bottom_back = new TGeoTranslation("trans_mezzanine_connector_lid_bottom_back", 0, 0 - 22.421 - mezzanine_connector_base_box_height / 2., -mezzanine_connector_lid_bottom_box_position_z); + mHalfPSU->AddNode(mezzanine_connector_lid_bottom, 0, trans_mezzanine_connector_lid_bottom_front); + mHalfPSU->AddNode(mezzanine_connector_lid_bottom, 0, trans_mezzanine_connector_lid_bottom_back); + + Double_t mezzanine_connector_lid_top_box_thickness = mezzanine_main_position_z - mezzanine_main_thickness / 2 - mezzanine_connector_lid_bottom_box_position_z - mezzanine_connector_lid_bottom_box_thickness / 2.; + Double_t mezzanine_connector_lid_top_box_width = 6.660; + Double_t mezzanine_connector_lid_top_box_height = 1.328; + Double_t mezzanine_connector_lid_top_box_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + 0.967 + mezzanine_connector_lid_bottom_box_thickness + mezzanine_connector_lid_top_box_thickness / 2.; + TGeoBBox* mezzanine_connector_lid_top_box = new TGeoBBox("mezzanine_connector_lid_top_box", mezzanine_connector_lid_top_box_width / 2., mezzanine_connector_lid_top_box_height / 2., mezzanine_connector_lid_top_box_thickness / 2.); + TGeoVolume* mezzanine_connector_lid_top = new TGeoVolume("mezzanine_connector_lid_top", mezzanine_connector_lid_top_box, kMedPeek); + mezzanine_connector_lid_top->SetLineColor(kGray + 2); + + TGeoTranslation* trans_mezzanine_connector_lid_top_front = new TGeoTranslation("trans_mezzanine_connector_lid_top_front", 0, 0 - 22.421 - mezzanine_connector_base_box_height / 2., mezzanine_connector_lid_top_box_position_z); + TGeoTranslation* trans_mezzanine_connector_lid_top_back = new TGeoTranslation("trans_mezzanine_connector_lid_top_back", 0, 0 - 22.421 - mezzanine_connector_base_box_height / 2., -mezzanine_connector_lid_top_box_position_z); + mHalfPSU->AddNode(mezzanine_connector_lid_top, 0, trans_mezzanine_connector_lid_top_front); + mHalfPSU->AddNode(mezzanine_connector_lid_top, 0, trans_mezzanine_connector_lid_top_back); + + //DCDC converter on mezzanine + + Double_t spacer_sheet1_mezzanine_box_thickness = 0.086; + Double_t spacer_sheet1_mezzanine_box_width = 1.397; + Double_t spacer_sheet1_mezzanine_box_height = 0.343; + Double_t spacer_sheet1_mezzanine_box_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length - spacer_sheet1_mezzanine_box_thickness / 2.; + Double_t gap_sheet1_on_mezzanine = 0.1; + + TGeoBBox* spacer_sheet1_mezzanine_box = new TGeoBBox("spacer_sheet1_mezzanine_box", spacer_sheet1_mezzanine_box_width / 2., spacer_sheet1_mezzanine_box_height / 2., spacer_sheet1_mezzanine_box_thickness / 2.); + TGeoTranslation* trans_spacer_sheet1_mezzanine_box_front[5]; + trans_spacer_sheet1_mezzanine_box_front[0] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_front_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_front[1] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_front_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_front[2] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_front_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_front[3] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_front_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_front[4] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_front_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), spacer_sheet1_mezzanine_box_position_z); + TGeoTranslation* trans_spacer_sheet1_mezzanine_box_back[5]; + trans_spacer_sheet1_mezzanine_box_back[0] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_back_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_back[1] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_back_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_back[2] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_back_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_back[3] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_back_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -spacer_sheet1_mezzanine_box_position_z); + trans_spacer_sheet1_mezzanine_box_back[4] = new TGeoTranslation("trans_spacer_sheet1_mezzanine_box_back_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -spacer_sheet1_mezzanine_box_position_z); + + TGeoVolume* spacer_sheet1_mezzanine = new TGeoVolume("spacer_sheet1_mezzanine", spacer_sheet1_mezzanine_box, kMedPeek); + spacer_sheet1_mezzanine->SetLineColor(kGray + 2); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_front[0]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_front[1]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_front[2]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_front[3]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_front[4]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_back[0]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_back[1]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_back[2]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_back[3]); + mHalfPSU->AddNode(spacer_sheet1_mezzanine, 0, trans_spacer_sheet1_mezzanine_box_back[4]); + + Double_t sheet1_on_mezzanine_box_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length - spacer_sheet1_mezzanine_box_thickness - DCDC_sheet1_thickness / 2.; + + TGeoTranslation* trans_sheet1_on_mezzanine_box_front[5]; + trans_sheet1_on_mezzanine_box_front[0] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_front_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_front[1] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_front_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_front[2] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_front_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_front[3] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_front_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_front[4] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_front_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), sheet1_on_mezzanine_box_position_z); + TGeoTranslation* trans_sheet1_on_mezzanine_box_back[5]; + trans_sheet1_on_mezzanine_box_back[0] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_back_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), -sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_back[1] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_back_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), -sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_back[2] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_back_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), -sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_back[3] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_back_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), -sheet1_on_mezzanine_box_position_z); + trans_sheet1_on_mezzanine_box_back[4] = new TGeoTranslation("trans_sheet1_on_mezzanine_box_back_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_sheet1_width / 2.), -sheet1_on_mezzanine_box_position_z); + + TGeoVolume* sheet1_on_mezzanine = new TGeoVolume("sheet1_on_mezzanine", DCDC_sheet1_box_side, kMedPeek); + sheet1_on_mezzanine->SetLineColor(kGreen); + + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_front[0]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_front[1]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_front[2]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_front[3]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_front[4]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_back[0]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_back[1]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_back[2]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_back[3]); + mHalfPSU->AddNode(sheet1_on_mezzanine, 0, trans_sheet1_on_mezzanine_box_back[4]); + + Double_t DCDC_connector_on_mezzanine_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length - spacer_sheet1_mezzanine_box_thickness - DCDC_sheet1_thickness - DCDC_connector_thickness / 2.; + + TGeoVolume* DCDC_connector_on_mezzanine = new TGeoVolume("DCDC_connector_on_mezzanine", DCDC_connector_box_side, kMedPeek); + DCDC_connector_on_mezzanine->SetLineColor(kGray + 2); + TGeoTranslation* trans_DCDC_connector_on_mezzanine_front[5]; + trans_DCDC_connector_on_mezzanine_front[0] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_front_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_front[1] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_front_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_front[2] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_front_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_front[3] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_front_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_front[4] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_front_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), DCDC_connector_on_mezzanine_position_z); + TGeoTranslation* trans_DCDC_connector_on_mezzanine_back[5]; + trans_DCDC_connector_on_mezzanine_back[0] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_back_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_back[1] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_back_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_back[2] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_back_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_back[3] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_back_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -DCDC_connector_on_mezzanine_position_z); + trans_DCDC_connector_on_mezzanine_back[4] = new TGeoTranslation("trans_DCDC_connector_on_mezzanine_back_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (spacer_sheet1_mezzanine_box_width / 2. + 0.531), -DCDC_connector_on_mezzanine_position_z); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_front[0]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_front[1]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_front[2]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_front[3]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_front[4]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_back[0]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_back[1]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_back[2]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_back[3]); + mHalfPSU->AddNode(DCDC_connector_on_mezzanine, 0, trans_DCDC_connector_on_mezzanine_back[4]); + + Double_t DCDC_cover_on_mezzanine_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length - spacer_sheet1_mezzanine_box_thickness - DCDC_sheet1_thickness - DCDC_cover_thickness / 2.; + + TGeoTranslation* trans_DCDC_cover_innner_box_back = new TGeoTranslation("trans_DCDC_cover_innner_box_back", 0, 0, -2 * DCDC_cover_depth); + TGeoTranslation* trans_DCDC_cover_innner_box_front = new TGeoTranslation("trans_DCDC_cover_innner_box_front", 0, 0, +2 * DCDC_cover_depth); + trans_DCDC_cover_innner_box_back->RegisterYourself(); + trans_DCDC_cover_innner_box_front->RegisterYourself(); + + TGeoRotation* rotate_DCDC_cover_on_mezzanine_shape = new TGeoRotation("rotate_DCDC_cover_on_mezzanine_shape", 0, 180, 180); + rotate_DCDC_cover_on_mezzanine_shape->RegisterYourself(); + + TGeoCompositeShape* DCDC_cover_on_mezzanine_shape_back = new TGeoCompositeShape("DCDC_cover_on_mezzanine_shape_back", "DCDC_cover_outer_box_side - DCDC_cover_inner_box_side:trans_DCDC_cover_innner_box_back"); + TGeoCompositeShape* DCDC_cover_on_mezzanine_shape_front = new TGeoCompositeShape("DCDC_cover_on_mezzanine_shape_front", "DCDC_cover_outer_box_side - DCDC_cover_inner_box_side:trans_DCDC_cover_innner_box_front"); + + TGeoVolume* DCDC_cover_on_mezzanine_front = new TGeoVolume("DCDC_cover_on_mezzanine_front", DCDC_cover_on_mezzanine_shape_front, kMedPeek); + TGeoVolume* DCDC_cover_on_mezzanine_back = new TGeoVolume("DCDC_cover_on_mezzanine_back", DCDC_cover_on_mezzanine_shape_back, kMedPeek); + DCDC_cover_on_mezzanine_front->SetLineColor(kGray); + DCDC_cover_on_mezzanine_back->SetLineColor(kGray); + + TGeoTranslation* trans_DCDC_cover_on_mezzanine_front[5]; + trans_DCDC_cover_on_mezzanine_front[0] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_front_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_front[1] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_front_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_front[2] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_front_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_front[3] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_front_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_front[4] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_front_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), DCDC_cover_on_mezzanine_position_z); + TGeoTranslation* trans_DCDC_cover_on_mezzanine_back[5]; + trans_DCDC_cover_on_mezzanine_back[0] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_back_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_back[1] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_back_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_back[2] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_back_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_back[3] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_back_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -DCDC_cover_on_mezzanine_position_z); + trans_DCDC_cover_on_mezzanine_back[4] = new TGeoTranslation("trans_DCDC_cover_on_mezzanine_back_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -DCDC_cover_on_mezzanine_position_z); + + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_front, 0, trans_DCDC_cover_on_mezzanine_front[0]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_front, 0, trans_DCDC_cover_on_mezzanine_front[1]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_front, 0, trans_DCDC_cover_on_mezzanine_front[2]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_front, 0, trans_DCDC_cover_on_mezzanine_front[3]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_front, 0, trans_DCDC_cover_on_mezzanine_front[4]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_back, 0, trans_DCDC_cover_on_mezzanine_back[0]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_back, 0, trans_DCDC_cover_on_mezzanine_back[1]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_back, 0, trans_DCDC_cover_on_mezzanine_back[2]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_back, 0, trans_DCDC_cover_on_mezzanine_back[3]); + mHalfPSU->AddNode(DCDC_cover_on_mezzanine_back, 0, trans_DCDC_cover_on_mezzanine_back[4]); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //Connector + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + Double_t main_connector1_thickness = 0.752; + Double_t main_connector1_width = 2.350; + Double_t main_connector1_height = 0.160; + TGeoBBox* connector1_box = new TGeoBBox("connector1_box", main_connector1_width / 2., main_connector1_height / 2., main_connector1_thickness / 2.); + + Double_t main_connector2_thickness = 0.564; + Double_t main_connector2_width = 2.086; + Double_t main_connector2_height = 0.499; + TGeoBBox* connector2_box = new TGeoBBox("connector2_box", main_connector2_width / 2., main_connector2_height / 2., main_connector2_thickness / 2.); + + Double_t main_connector3_thickness = 0.742; + Double_t main_connector3_width = 2.567; + Double_t main_connector3_height = 0.579; + TGeoBBox* connector3_box = new TGeoBBox("connector3_box", main_connector3_width / 2., main_connector3_height / 2., main_connector3_thickness / 2.); + + TGeoVolume* main_connector1 = new TGeoVolume("main_connector1", connector1_box, kMedPeek); + main_connector1->SetLineColor(kGray + 2); + TGeoVolume* main_connector2 = new TGeoVolume("main_connector2", connector2_box, kMedPeek); + main_connector2->SetLineColor(kGray + 2); + TGeoVolume* main_connector3 = new TGeoVolume("main_connector3", connector3_box, kMedPeek); + main_connector3->SetLineColor(kGray + 2); + + TGeoTranslation* trans_main_connector1_front[10]; + trans_main_connector1_front[0] = new TGeoTranslation("trans_main_connector1_front_No0", 14.462 + main_connector2_width / 2., -4.276 - main_connector1_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector1_front[1] = new TGeoTranslation("trans_main_connector1_front_No1", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1, -4.276 - main_connector1_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector1_front[2] = new TGeoTranslation("trans_main_connector1_front_No2", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2, -4.276 - main_connector1_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector1_front[3] = new TGeoTranslation("trans_main_connector1_front_No3", -(14.462 + main_connector2_width / 2.), -4.276 - main_connector1_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector1_front[4] = new TGeoTranslation("trans_main_connector1_front_No4", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1), -4.276 - main_connector1_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector1_front[5] = new TGeoTranslation("trans_main_connector1_front_No5", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2), -4.276 - main_connector1_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + TGeoTranslation* trans_main_connector1_back[10]; + trans_main_connector1_back[0] = new TGeoTranslation("trans_main_connector1_back_No0", 14.462 + main_connector2_width / 2., -4.276 - main_connector1_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector1_back[1] = new TGeoTranslation("trans_main_connector1_back_No1", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1, -4.276 - main_connector1_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector1_back[2] = new TGeoTranslation("trans_main_connector1_back_No2", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2, -4.276 - main_connector1_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector1_back[3] = new TGeoTranslation("trans_main_connector1_back_No3", -(14.462 + main_connector2_width / 2.), -4.276 - main_connector1_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector1_back[4] = new TGeoTranslation("trans_main_connector1_back_No4", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1), -4.276 - main_connector1_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector1_back[5] = new TGeoTranslation("trans_main_connector1_back_No5", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2), -4.276 - main_connector1_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + + TGeoTranslation* trans_main_connector2_front[10]; + trans_main_connector2_front[0] = new TGeoTranslation("trans_main_connector2_front_No0", 14.462 + main_connector2_width / 2., -4.276 - main_connector1_height - main_connector2_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector2_front[1] = new TGeoTranslation("trans_main_connector2_front_No1", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1, -4.276 - main_connector1_height - main_connector2_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector2_front[2] = new TGeoTranslation("trans_main_connector2_front_No2", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2, -4.276 - main_connector1_height - main_connector2_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector2_front[3] = new TGeoTranslation("trans_main_connector2_front_No3", -(14.462 + main_connector2_width / 2.), -4.276 - main_connector1_height - main_connector2_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector2_front[4] = new TGeoTranslation("trans_main_connector2_front_No4", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1), -4.276 - main_connector1_height - main_connector2_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + trans_main_connector2_front[5] = new TGeoTranslation("trans_main_connector2_front_No5", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2), -4.276 - main_connector1_height - main_connector2_height / 2., middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2); + TGeoTranslation* trans_main_connector2_back[10]; + trans_main_connector2_back[0] = new TGeoTranslation("trans_main_connector2_back_No0", 14.462 + main_connector2_width / 2., -4.276 - main_connector1_height - main_connector2_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector2_back[1] = new TGeoTranslation("trans_main_connector2_back_No1", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1, -4.276 - main_connector1_height - main_connector2_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector2_back[2] = new TGeoTranslation("trans_main_connector2_back_No2", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2, -4.276 - main_connector1_height - main_connector2_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector2_back[3] = new TGeoTranslation("trans_main_connector2_back_No3", -(14.462 + main_connector2_width / 2.), -4.276 - main_connector1_height - main_connector2_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector2_back[4] = new TGeoTranslation("trans_main_connector2_back_No4", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1), -4.276 - main_connector1_height - main_connector2_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + trans_main_connector2_back[5] = new TGeoTranslation("trans_main_connector2_back_No5", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2), -4.276 - main_connector1_height - main_connector2_height / 2., -(middle_spacer_main_add_rectangle_side_small_thickness / 2. + main_connector3_thickness / 2)); + + TGeoTranslation* trans_main_connector3_front[10]; + trans_main_connector3_front[0] = new TGeoTranslation("trans_main_connector3_front_No0", 14.462 + main_connector2_width / 2., -4.436 - main_connector2_height - main_connector3_height / 2., electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2); + trans_main_connector3_front[1] = new TGeoTranslation("trans_main_connector3_front_No1", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1, -4.436 - main_connector2_height - main_connector3_height / 2., electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2); + trans_main_connector3_front[2] = new TGeoTranslation("trans_main_connector3_front_No2", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2, -4.436 - main_connector2_height - main_connector3_height / 2., electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2); + trans_main_connector3_front[3] = new TGeoTranslation("trans_main_connector3_front_No3", -(14.462 + main_connector2_width / 2.), -4.436 - main_connector2_height - main_connector3_height / 2., electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2); + trans_main_connector3_front[4] = new TGeoTranslation("trans_main_connector3_front_No4", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1), -4.436 - main_connector2_height - main_connector3_height / 2., electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2); + trans_main_connector3_front[5] = new TGeoTranslation("trans_main_connector3_front_No5", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2), -4.436 - main_connector2_height - main_connector3_height / 2., electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2); + TGeoTranslation* trans_main_connector3_back[10]; + trans_main_connector3_back[0] = new TGeoTranslation("trans_main_connector3_back_No0", 14.462 + main_connector2_width / 2., -4.436 - main_connector2_height - main_connector3_height / 2., -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2)); + trans_main_connector3_back[1] = new TGeoTranslation("trans_main_connector3_back_No1", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1, -4.436 - main_connector2_height - main_connector3_height / 2., -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2)); + trans_main_connector3_back[2] = new TGeoTranslation("trans_main_connector3_back_No2", 14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2, -4.436 - main_connector2_height - main_connector3_height / 2., -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2)); + trans_main_connector3_back[3] = new TGeoTranslation("trans_main_connector3_back_No3", -(14.462 + main_connector2_width / 2.), -4.436 - main_connector2_height - main_connector3_height / 2., -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2)); + trans_main_connector3_back[4] = new TGeoTranslation("trans_main_connector3_back_No4", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 1), -4.436 - main_connector2_height - main_connector3_height / 2., -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2)); + trans_main_connector3_back[5] = new TGeoTranslation("trans_main_connector3_back_No5", -(14.462 + main_connector2_width / 2. + (0.766 + main_connector2_width) * 2), -4.436 - main_connector2_height - main_connector3_height / 2., -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2)); + + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_front[0]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_front[1]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_front[2]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_front[3]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_front[4]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_front[5]); + + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_front[0]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_front[1]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_front[2]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_front[3]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_front[4]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_front[5]); + + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_front[0]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_front[1]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_front[2]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_front[3]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_front[4]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_front[5]); + + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_back[0]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_back[1]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_back[2]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_back[3]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_back[4]); + mHalfPSU->AddNode(main_connector1, 0, trans_main_connector1_back[5]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_back[0]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_back[1]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_back[2]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_back[3]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_back[4]); + mHalfPSU->AddNode(main_connector2, 0, trans_main_connector2_back[5]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_back[0]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_back[1]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_back[2]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_back[3]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_back[4]); + mHalfPSU->AddNode(main_connector3, 0, trans_main_connector3_back[5]); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Rough coil shape part //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - new TGeoTorus("DCDC_rough_coil_torus_shape", DCDC_coil_torus_part_radius, DCDC_coil_radius1 - DCDC_coil_max_radius1, - DCDC_coil_radius1 + DCDC_coil_max_radius1, 0, 180); - new TGeoTube("DCDC_rough_coil_straight_shape", DCDC_coil_radius1 - DCDC_coil_max_radius1, DCDC_coil_radius1 + DCDC_coil_max_radius1, - DCDC_coil_max_radius1 * 9 + DCDC_coil_straight_gap * 4); - - TGeoRotation* rDCDC_rough_coil_torus_shape1 = new TGeoRotation("rDCDC_rough_coil_torus_shape1", 0, 90, 0); - TGeoRotation* rDCDC_rough_coil_torus_shape2 = new TGeoRotation("rDCDC_rough_coil_torus_shape2", 0, -90, 0); - rDCDC_rough_coil_torus_shape1->RegisterYourself(); - rDCDC_rough_coil_torus_shape2->RegisterYourself(); - - new TGeoCompositeShape("rotated_DCDC_rough_coil_torus_shape1", "dummy+DCDC_rough_coil_torus_shape:rDCDC_rough_coil_torus_shape1"); - new TGeoCompositeShape("rotated_DCDC_rough_coil_torus_shape2", "dummy+DCDC_rough_coil_torus_shape:rDCDC_rough_coil_torus_shape2"); - - TGeoTranslation* tDCDC_rough_coil_straight_shape1 = new TGeoTranslation("tDCDC_rough_coil_straight_shape1", DCDC_coil_torus_part_radius, 0, 0); - TGeoTranslation* tDCDC_rough_coil_straight_shape2 = new TGeoTranslation("tDCDC_rough_coil_straight_shape2", -DCDC_coil_torus_part_radius, 0, 0); - tDCDC_rough_coil_straight_shape1->RegisterYourself(); - tDCDC_rough_coil_straight_shape2->RegisterYourself(); - TGeoTranslation* tDCDC_rough_coil_torus_shape1 = new TGeoTranslation("tDCDC_rough_coil_torus_shape1", 0, 0, DCDC_coil_max_radius1 * 9 + DCDC_coil_straight_gap * 4); - TGeoTranslation* tDCDC_rough_coil_torus_shape2 = new TGeoTranslation("tDCDC_rough_coil_torus_shape2", 0, 0, -(DCDC_coil_max_radius1 * 9 + DCDC_coil_straight_gap * 4)); - tDCDC_rough_coil_torus_shape1->RegisterYourself(); - tDCDC_rough_coil_torus_shape2->RegisterYourself(); - - new TGeoCompositeShape("DCDC_coil_shape1", - "DCDC_rough_coil_straight_shape:tDCDC_rough_coil_straight_shape1+" - "DCDC_rough_coil_straight_shape:tDCDC_rough_coil_straight_shape2+" - "rotated_DCDC_rough_coil_torus_shape1:tDCDC_rough_coil_torus_shape1+" - "rotated_DCDC_rough_coil_torus_shape2:tDCDC_rough_coil_torus_shape2"); - TGeoRotation* rDCDC_coil_shape1[nDCDC]; - TGeoTranslation* tDCDC_coil_shape1[nDCDC]; - TGeoCompositeShape* rotated_DCDC_coil_shape1[nDCDC]; - TString string_all_DCDC_coil_shape1 = ""; - - for (Int_t iDCDC = 0; iDCDC < nDCDC; ++iDCDC) { - rDCDC_coil_shape1[iDCDC] = new TGeoRotation(Form("rDCDC_coil_shape1_angle%d", iDCDC + 1), full_angle / 2 - one_angle * iDCDC, 90, 0); - rDCDC_coil_shape1[iDCDC]->RegisterYourself(); - - rotated_DCDC_coil_shape1[iDCDC] = new TGeoCompositeShape(Form("rotated_DCDC_coil_shape1_angle%d", iDCDC + 1), Form("dummy+DCDC_coil_shape1:rDCDC_coil_shape1_angle%d", iDCDC + 1)); - - tDCDC_shape1[iDCDC] = new TGeoTranslation(Form("tDCDC_coil_shape1_pos%d", iDCDC + 1), - -position_radius * TMath::Cos((start_DCDC_angle + one_angle * iDCDC) * TMath::Pi() / 180.), - position_radius * TMath::Sin((start_DCDC_angle + one_angle * iDCDC) * TMath::Pi() / 180.), 0); - tDCDC_shape1[iDCDC]->RegisterYourself(); - - if (iDCDC + 1 == nDCDC) { - string_all_DCDC_coil_shape1 += Form("rotated_DCDC_coil_shape1_angle%d:tDCDC_shape1_pos%d", iDCDC + 1, iDCDC + 1); + + Double_t coil_torus_inner_radius1 = TMath::Min(DCDC_cover_inner_width, DCDC_cover_inner_height) / 10.0; + Double_t coil_torus_outer_radius1 = TMath::Min(DCDC_cover_inner_width, DCDC_cover_inner_height) / 8.0; + Double_t coil_radius1 = TMath::Min(DCDC_cover_inner_width, DCDC_cover_inner_height) / 3.0 - coil_torus_outer_radius1; + Double_t coil_position_z = DCDC_sheet1_position_z + DCDC_sheet1_thickness / 2. + coil_torus_outer_radius1; + + TGeoTorus* coil_torus = new TGeoTorus("coil_torus", coil_radius1, coil_torus_inner_radius1, coil_torus_outer_radius1, 0, 360); + + std::string name_coil = ""; + + TGeoCompositeShape* rotated_coil_torus[23]; + + for (Int_t iB = 0; iB < 11; ++iB) { + + Double_t block_angle = (180. - block_angle_index[iB]) / 2. * TMath::Pi() / 180.; + + TGeoRotation* rotate_coil_torus_left = new TGeoRotation(Form("rotate_coil_torus_No%d", iB * 2), 180 - block_angle * 180. / TMath::Pi(), 0, 0); + TGeoRotation* rotate_coil_torus_right = new TGeoRotation(Form("rotate_coil_torus_No%d", iB * 2 + 1), block_angle * 180. / TMath::Pi(), 0, 0); + rotate_coil_torus_left->RegisterYourself(); + rotate_coil_torus_right->RegisterYourself(); + + Double_t cent_block_left[] = {block_radius * TMath::Cos(block_angle), -block_radius * TMath::Sin(block_angle), coil_position_z}; + Double_t cent_block_right[] = {block_radius * TMath::Cos(TMath::Pi() - block_angle), -block_radius * TMath::Sin(TMath::Pi() - block_angle), coil_position_z}; + + TGeoCombiTrans* combtrans_coil_torus_left = new TGeoCombiTrans(Form("combtrans_coil_torus_No%d", 2 * iB), cent_block_left[0], cent_block_left[1], cent_block_left[2], rotate_coil_torus_left); + TGeoCombiTrans* combtrans_coil_torus_right = new TGeoCombiTrans(Form("combtrans_coil_torus_No%d", 2 * iB + 1), cent_block_right[0], cent_block_right[1], cent_block_right[2], rotate_coil_torus_right); + combtrans_coil_torus_left->RegisterYourself(); + combtrans_coil_torus_right->RegisterYourself(); + + if (iB == 0) { + name_coil += Form("coil_torus:combtrans_coil_torus_No%d", 2 * iB); } else { - string_all_DCDC_coil_shape1 += Form("rotated_DCDC_coil_shape1_angle%d:tDCDC_shape1_pos%d+", iDCDC + 1, iDCDC + 1); + name_coil += Form("+coil_torus:combtrans_coil_torus_No%d", 2 * iB); } + name_coil += Form("+coil_torus:combtrans_coil_torus_No%d", 2 * iB + 1); } - TGeoRotation* rDCDC_side_coil_shape1 = new TGeoRotation("rDCDC_side_coil_shape1", 0, 90, 0); - rDCDC_side_coil_shape1->RegisterYourself(); - new TGeoCompositeShape("rotated_DCDC_side_coil_shape", "dummy+DCDC_coil_shape1:rDCDC_side_coil_shape1"); + TGeoTorus* coil_torus_side = new TGeoTorus("coil_torus_side", coil_radius1, coil_torus_inner_radius1, coil_torus_outer_radius1, 0, 360); + + TGeoTranslation* trans_coil_torus_side_left = new TGeoTranslation("trans_coil_torus_side_left", 22.259 - middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, coil_position_z); + trans_coil_torus_side_left->RegisterYourself(); + name_coil += "+coil_torus_side:trans_coil_torus_side_left"; + + TGeoTranslation* trans_coil_torus_side_right = new TGeoTranslation("trans_coil_torus_side_right", -22.247 + middle_spacer_cover_block_width / 2, -8.305 + middle_spacer_cover_block_height / 2, coil_position_z); + trans_coil_torus_side_right->RegisterYourself(); + name_coil += "+coil_torus_side:trans_coil_torus_side_right"; + + TGeoCompositeShape* coil_shape = new TGeoCompositeShape("coil_shape", name_coil.c_str()); + + TGeoVolume* coil = new TGeoVolume("coil", coil_shape, kMedAlu); + coil->SetLineColor(kYellow); + + TGeoRotation* trans_coil_front = new TGeoRotation("trans_coil_front", 0, 0, 0); + TGeoRotation* trans_coil_back = new TGeoRotation("trans_coil_back", 180, 180, 0); + + mHalfPSU->AddNode(coil, 0, trans_coil_front); + mHalfPSU->AddNode(coil, 0, trans_coil_back); + + //On Mazzenine + + Double_t coil_on_mezzanine_position_z = middle_spacer_main_thickness / 2. + middle_spacer_cover_thickness + electric_board_thickness + mezzanine_prop_main_length - spacer_sheet1_mezzanine_box_thickness - DCDC_sheet1_thickness - coil_torus_outer_radius1; + + TGeoTranslation* trans_coil_on_mezzanine_front[5]; + trans_coil_on_mezzanine_front[0] = new TGeoTranslation("trans_coil_on_mezzanine_front_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_front[1] = new TGeoTranslation("trans_coil_on_mezzanine_front_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_front[2] = new TGeoTranslation("trans_coil_on_mezzanine_front_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_front[3] = new TGeoTranslation("trans_coil_on_mezzanine_front_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_front[4] = new TGeoTranslation("trans_coil_on_mezzanine_front_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), coil_on_mezzanine_position_z); + TGeoTranslation* trans_coil_on_mezzanine_back[5]; + trans_coil_on_mezzanine_back[0] = new TGeoTranslation("trans_coil_on_mezzanine_back_No0", +2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_back[1] = new TGeoTranslation("trans_coil_on_mezzanine_back_No1", +1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_back[2] = new TGeoTranslation("trans_coil_on_mezzanine_back_No2", 0 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_back[3] = new TGeoTranslation("trans_coil_on_mezzanine_back_No3", -1 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -coil_on_mezzanine_position_z); + trans_coil_on_mezzanine_back[4] = new TGeoTranslation("trans_coil_on_mezzanine_back_No4", -2 * (DCDC_sheet1_height + gap_sheet1_on_mezzanine), -17.952 - (DCDC_cover_outer_width / 2. + 1.825), -coil_on_mezzanine_position_z); + + TGeoVolume* coil_on_mezzanine = new TGeoVolume("coil_on_mezzanine", coil_torus, kMedAlu); + coil_on_mezzanine->SetLineColor(kYellow); + + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_front[0]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_front[1]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_front[2]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_front[3]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_front[4]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_back[0]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_back[1]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_back[2]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_back[3]); + mHalfPSU->AddNode(coil_on_mezzanine, 0, trans_coil_on_mezzanine_back[4]); + + //small connector borrom + + Double_t main_connector_angle1 = 52 * TMath::Pi() / 180.; + Double_t main_connector_angle2 = 45 * TMath::Pi() / 180.; + + //front + + //left + + TGeoRotation* rotate_main_connector_box_angle1_left = new TGeoRotation("rotate_main_connector_box_angle1_left", 0, 0, 90 - main_connector_angle1 * 180 / TMath::Pi()); + TGeoRotation* rotate_main_connector_box_angle2_left = new TGeoRotation("rotate_main_connector_box_angle2_left", 0, 0, 90 - main_connector_angle2 * 180 / TMath::Pi()); + TGeoRotation* rotate_main_connector_box_angle1_right = new TGeoRotation("rotate_main_connector_box_angle1_right", 0, 0, -90 + main_connector_angle1 * 180 / TMath::Pi()); + TGeoRotation* rotate_main_connector_box_angle2_right = new TGeoRotation("rotate_main_connector_box_angle2_right", 0, 0, -90 + main_connector_angle2 * 180 / TMath::Pi()); + rotate_main_connector_box_angle1_left->RegisterYourself(); + rotate_main_connector_box_angle2_left->RegisterYourself(); + rotate_main_connector_box_angle1_right->RegisterYourself(); + rotate_main_connector_box_angle2_right->RegisterYourself(); + + TGeoTranslation* trans_connector1_box = new TGeoTranslation("trans_connector1_box", 0, -main_connector3_height / 2. - main_connector2_height - main_connector1_height / 2., 0); + TGeoTranslation* trans_connector2_box = new TGeoTranslation("trans_connector2_box", 0, -main_connector3_height / 2. - main_connector2_height / 2., 0); + TGeoTranslation* trans_connector3_box = new TGeoTranslation("trans_connector3_box", 0, 0, 0); + trans_connector1_box->RegisterYourself(); + trans_connector2_box->RegisterYourself(); + trans_connector3_box->RegisterYourself(); + + TGeoCompositeShape* comp_connector_box = new TGeoCompositeShape("comp_connector_box", "connector1_box:trans_connector1_box+connector2_box:trans_connector2_box+connector3_box:trans_connector3_box"); + + TGeoVolume* connector = new TGeoVolume("connector", comp_connector_box, kMedPeek); + connector->SetLineColor(kGray + 2); + + TGeoCombiTrans* trans_connector1_front = new TGeoCombiTrans("trans_connector1_front", +(17.064 + 15.397) / 2., -(21.795 + 19.758) / 2, electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2, rotate_main_connector_box_angle1_left); + TGeoCombiTrans* trans_connector2_front = new TGeoCombiTrans("trans_connector2_front", +(19.345 + 17.941) / 2, -(19.757 + 17.531) / 2, electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2, rotate_main_connector_box_angle2_left); + TGeoCombiTrans* trans_connector3_front = new TGeoCombiTrans("trans_connector1_front", -(17.064 + 15.397) / 2., -(21.795 + 19.758) / 2, electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2, rotate_main_connector_box_angle1_right); + TGeoCombiTrans* trans_connector4_front = new TGeoCombiTrans("trans_connector2_front", -(19.345 + 17.941) / 2, -(19.757 + 17.531) / 2, electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2, rotate_main_connector_box_angle2_right); + + mHalfPSU->AddNode(connector, 0, trans_connector1_front); + mHalfPSU->AddNode(connector, 0, trans_connector2_front); + mHalfPSU->AddNode(connector, 0, trans_connector3_front); + mHalfPSU->AddNode(connector, 0, trans_connector4_front); - string_all_DCDC_coil_shape1 += "+rotated_DCDC_side_coil_shape:tsurfaceBox_square_sub_right2 + rotated_DCDC_side_coil_shape:tsurfaceBox_square_sub_left2"; + TGeoCombiTrans* trans_connector1_back = new TGeoCombiTrans("trans_connector1_back", +(17.064 + 15.397) / 2., -(21.795 + 19.758) / 2, -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2), rotate_main_connector_box_angle1_left); + TGeoCombiTrans* trans_connector2_back = new TGeoCombiTrans("trans_connector2_back", +(19.345 + 17.941) / 2, -(19.757 + 17.531) / 2, -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2), rotate_main_connector_box_angle2_left); + TGeoCombiTrans* trans_connector3_back = new TGeoCombiTrans("trans_connector1_back", -(17.064 + 15.397) / 2., -(21.795 + 19.758) / 2, -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2), rotate_main_connector_box_angle1_right); + TGeoCombiTrans* trans_connector4_back = new TGeoCombiTrans("trans_connector2_back", -(19.345 + 17.941) / 2, -(19.757 + 17.531) / 2, -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2), rotate_main_connector_box_angle2_right); - new TGeoCompositeShape("all_DCDC_coil_shape1", string_all_DCDC_coil_shape1); + mHalfPSU->AddNode(connector, 0, trans_connector1_back); + mHalfPSU->AddNode(connector, 0, trans_connector2_back); + mHalfPSU->AddNode(connector, 0, trans_connector3_back); + mHalfPSU->AddNode(connector, 0, trans_connector4_back); - auto* tall_DCDC_coil_shape1 = new TGeoTranslation("tall_DCDC_coil_shape1", 0, 0, middle_board_thickness / 2 + surface_board_thickness + DCDC_thickness2 + DCDC_coil_radius1); - auto* tall_DCDC_coil_shape2 = new TGeoTranslation("tall_DCDC_coil_shape2", 0, 0, -(middle_board_thickness / 2 + surface_board_thickness + DCDC_thickness2 + DCDC_coil_radius1)); - tall_DCDC_coil_shape1->RegisterYourself(); - tall_DCDC_coil_shape2->RegisterYourself(); + //large connector bottom + + Double_t main_large_connector_bottom_angle1 = 36 * TMath::Pi() / 180.; - TGeoCompositeShape* two_side_all_DCDC_coil_shape1 = new TGeoCompositeShape("two_side_all_DCDC_coil_shape1", - "all_DCDC_coil_shape1:tall_DCDC_coil_shape1+" - "all_DCDC_coil_shape1:tall_DCDC_coil_shape2"); - TGeoVolume* DCDC_coil1 = new TGeoVolume("DCDC_coil1", two_side_all_DCDC_coil_shape1, kMedCu); + Double_t main_large_connector1_thickness = 0.752; + Double_t main_large_connector1_width = 3.95; + Double_t main_large_connector1_height = 0.16; - mHalfPSU->AddNode(DCDC_coil1, 1, nullptr); - //TGeoTranslation* tHalfPSU = new TGeoTranslation("tHalfPSU",0, -4.2 - (middleBox_sub_height1/2-surfaceBox_edge_height1/2), -72.6 + 46.0); - //TGeoTranslation* tHalfPSU = new TGeoTranslation("tHalfPSU",0,0, -72.6 + 46.0); - //tHalfPSU->RegisterYourself(); - //mHalfVolume->AddNode(mHalfPSU,0,tHalfPSU); + Double_t main_large_connector2_thickness = 0.56; + Double_t main_large_connector2_width = 3.95; + Double_t main_large_connector2_height = 0.536; + + Double_t main_large_connector3_thickness = 0.626; + Double_t main_large_connector3_width = 4.167; + Double_t main_large_connector3_height = 0.579; + + TGeoBBox* large_connector_bottom1_box = new TGeoBBox("large_connector1_box", main_large_connector1_width / 2., main_large_connector1_height / 2., main_large_connector1_thickness / 2.); + TGeoBBox* large_connector_bottom2_box = new TGeoBBox("large_connector2_box", main_large_connector2_width / 2., main_large_connector2_height / 2., main_large_connector2_thickness / 2.); + TGeoBBox* large_connector_bottom3_box = new TGeoBBox("large_connector3_box", main_large_connector3_width / 2., main_large_connector3_height / 2., main_large_connector3_thickness / 2.); + + TGeoTranslation* trans_large_connector1_box = new TGeoTranslation("trans_large_connector1_box", 0, -main_large_connector3_height / 2. - main_large_connector2_height - main_large_connector1_height / 2., 0); + TGeoTranslation* trans_large_connector2_box = new TGeoTranslation("trans_large_connector2_box", 0, -main_large_connector3_height / 2. - main_large_connector2_height / 2., 0); + TGeoTranslation* trans_large_connector3_box = new TGeoTranslation("trans_large_connector3_box", 0, 0, 0); + trans_large_connector1_box->RegisterYourself(); + trans_large_connector2_box->RegisterYourself(); + trans_large_connector3_box->RegisterYourself(); + + TGeoCompositeShape* comp_large_connector_box = new TGeoCompositeShape("comp_large_connector_box", "large_connector1_box:trans_large_connector1_box+large_connector2_box:trans_large_connector2_box+large_connector3_box:trans_large_connector3_box"); + + TGeoRotation* rotate_large_connector_bottom_box_front = new TGeoRotation("rotate_large_connector_bottom_box_front", 0, 0, -(90 - main_large_connector_bottom_angle1 * 180 / TMath::Pi())); + TGeoRotation* rotate_large_connector_bottom_box_back = new TGeoRotation("rotate_large_connector_bottom_box_back", 0, 0, (90 - main_large_connector_bottom_angle1 * 180 / TMath::Pi())); + rotate_large_connector_bottom_box_front->RegisterYourself(); + rotate_large_connector_bottom_box_back->RegisterYourself(); + + TGeoCombiTrans* combtrans_rotated_large_connector_front = new TGeoCombiTrans("combtrans_rotated_large_connector_front", -(22.268 + 20.287) / 2, (-17.315 - 13.603) / 2, +(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2), rotate_large_connector_bottom_box_front); + TGeoCombiTrans* combtrans_rotated_large_connector_back = new TGeoCombiTrans("combtrans_rotated_large_connector_back", +(22.268 + 20.287) / 2, (-17.315 - 13.603) / 2, -(electric_board_position_z + electric_board_thickness / 2 + main_connector3_thickness / 2), rotate_large_connector_bottom_box_back); + + TGeoVolume* large_connector = new TGeoVolume("large_connector", comp_large_connector_box, kMedPeek); + large_connector->SetLineColor(kGray + 2); + + mHalfPSU->AddNode(large_connector, 0, combtrans_rotated_large_connector_front); + mHalfPSU->AddNode(large_connector, 0, combtrans_rotated_large_connector_back); return mHalfPSU; } diff --git a/Detectors/ITSMFT/MFT/base/src/Segmentation.cxx b/Detectors/ITSMFT/MFT/base/src/Segmentation.cxx index 251b27c5d9f92..9da51d9a88b28 100644 --- a/Detectors/ITSMFT/MFT/base/src/Segmentation.cxx +++ b/Detectors/ITSMFT/MFT/base/src/Segmentation.cxx @@ -51,8 +51,9 @@ Segmentation::Segmentation(const Char_t* nameGeomFile) : mHalves(nullptr) Segmentation::~Segmentation() { - if (mHalves) + if (mHalves) { mHalves->Delete(); + } delete mHalves; } @@ -95,16 +96,18 @@ Int_t Segmentation::getDetElemLocalID(Int_t half, Int_t disk, Int_t ladder, Int_ Int_t localId = 0; - if (half == 1) + if (half == 1) { localId += getHalf(0)->getHalfDisk(disk)->getNChips(); + } for (Int_t iLad = 0; iLad < getHalf(half)->getHalfDisk(disk)->getNLadders(); iLad++) { if (iLad < ladder) { localId += getHalf(half)->getHalfDisk(disk)->getLadder(iLad)->getNSensors(); } else { for (Int_t iSens = 0; iSens < getHalf(half)->getHalfDisk(disk)->getLadder(iLad)->getNSensors(); iSens++) { - if (iSens == sensor) + if (iSens == sensor) { return localId; + } localId++; } } diff --git a/Detectors/ITSMFT/MFT/base/src/VSegmentation.cxx b/Detectors/ITSMFT/MFT/base/src/VSegmentation.cxx index 6f25b95e86f77..646cdf923b3ac 100644 --- a/Detectors/ITSMFT/MFT/base/src/VSegmentation.cxx +++ b/Detectors/ITSMFT/MFT/base/src/VSegmentation.cxx @@ -38,8 +38,9 @@ void VSegmentation::setRotationAngles(const Double_t* ang) { /// Set Rotation Angles - if (!mTransformation) + if (!mTransformation) { mTransformation = new TGeoCombiTrans(); + } auto* rot = new TGeoRotation(); rot->SetAngles(ang[0], ang[1], ang[2]); // all angles in degrees mTransformation->SetRotation(rot); diff --git a/Detectors/ITSMFT/MFT/macros/CMakeLists.txt b/Detectors/ITSMFT/MFT/macros/CMakeLists.txt index 261554a493d41..016c6ec06431e 100644 --- a/Detectors/ITSMFT/MFT/macros/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/macros/CMakeLists.txt @@ -9,3 +9,4 @@ # submit itself to any jurisdiction. add_subdirectory(mapping) +add_subdirectory(test) diff --git a/Detectors/ITSMFT/MFT/macros/test/CMakeLists.txt b/Detectors/ITSMFT/MFT/macros/test/CMakeLists.txt new file mode 100644 index 0000000000000..dc80010644a91 --- /dev/null +++ b/Detectors/ITSMFT/MFT/macros/test/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(CheckTopologies.C + PUBLIC_LINK_LIBRARIES O2::MathUtils + O2::MFTBase + O2::ITSMFTReconstruction + O2::ITSMFTSimulation + O2::DataFormatsITSMFT + O2::SimulationDataFormat + LABELS mft) + diff --git a/Detectors/ITSMFT/MFT/macros/test/CheckTopologies.C b/Detectors/ITSMFT/MFT/macros/test/CheckTopologies.C new file mode 100644 index 0000000000000..5b67403432d68 --- /dev/null +++ b/Detectors/ITSMFT/MFT/macros/test/CheckTopologies.C @@ -0,0 +1,300 @@ +/// \file CheckTopologies.C +/// Macros to test the generation of a dictionary of topologies. Three dictionaries are generated: one with signal-cluster only, one with noise-clusters only and one with all the clusters. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include <TAxis.h> +#include <TCanvas.h> +#include <TSystem.h> +#include <TFile.h> +#include <TH1F.h> +#include <TH2F.h> +#include <TNtuple.h> +#include <TString.h> +#include <TStyle.h> +#include <TTree.h> +#include <TStopwatch.h> +#include <fstream> +#include <string> + +#include "MathUtils/Utils.h" +#include "MFTBase/GeometryTGeo.h" +#include "ITSMFTReconstruction/BuildTopologyDictionary.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ClusterTopology.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "ITSMFTSimulation/Hit.h" +#include "MathUtils/Cartesian.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "Framework/Logger.h" +#include <unordered_map> +#endif + +void CheckTopologies(std::string clusfile = "mftclusters.root", + std::string hitfile = "o2sim_HitsMFT.root", + std::string collContextfile = "collisioncontext.root", + std::string inputGeom = "", + float checkOutliers = 2. // reject outliers (MC dX or dZ exceeds row/col span by a factor above the threshold) +) +{ + const int QEDSourceID = 99; // Clusters from this MC source correspond to QED electrons + + using namespace o2::base; + using namespace o2::mft; + + using o2::itsmft::BuildTopologyDictionary; + using o2::itsmft::ClusterTopology; + using o2::itsmft::CompCluster; + using o2::itsmft::CompClusterExt; + using o2::itsmft::Hit; + using ROFRec = o2::itsmft::ROFRecord; + using MC2ROF = o2::itsmft::MC2ROFRecord; + using HitVec = std::vector<Hit>; + using MC2HITS_map = std::unordered_map<uint64_t, int>; // maps (track_ID<<16 + chip_ID) to entry in the hit vector + std::unordered_map<int, int> hadronicMCMap; // mapping from MC event entry to hadronic event ID + std::vector<HitVec*> hitVecPool; + std::vector<MC2HITS_map> mc2hitVec; + const o2::steer::DigitizationContext* digContext = nullptr; + TStopwatch sw; + sw.Start(); + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto gman = o2::mft::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // request cached transforms + + // Hits + TFile* fileH = nullptr; + TTree* hitTree = nullptr; + + if (!hitfile.empty() && !collContextfile.empty() && !gSystem->AccessPathName(hitfile.c_str()) && !gSystem->AccessPathName(collContextfile.c_str())) { + fileH = TFile::Open(hitfile.data()); + hitTree = (TTree*)fileH->Get("o2sim"); + mc2hitVec.resize(hitTree->GetEntries()); + hitVecPool.resize(hitTree->GetEntries(), nullptr); + digContext = o2::steer::DigitizationContext::loadFromFile(collContextfile); + + auto& intGlo = digContext->getEventParts(digContext->isQEDProvided()); + int hadrID = -1, nGlo = intGlo.size(), nHadro = 0; + for (int iglo = 0; iglo < nGlo; iglo++) { + const auto& parts = intGlo[iglo]; + bool found = false; + for (auto& part : parts) { + if (part.sourceID == 0) { // we use underlying background + hadronicMCMap[iglo] = part.entryID; + found = true; + nHadro++; + break; + } + } + if (!found) { + hadronicMCMap[iglo] = -1; + } + } + if (nHadro < hitTree->GetEntries()) { + LOG(FATAL) << "N=" << nHadro << " hadronic events < " + << " N=" << hitTree->GetEntries() << " Hit enties."; + } + } + + // Clusters + TFile* fileCl = TFile::Open(clusfile.data()); + TTree* clusTree = (TTree*)fileCl->Get("o2sim"); + std::vector<CompClusterExt>* clusArr = nullptr; + clusTree->SetBranchAddress("MFTClusterComp", &clusArr); + std::vector<unsigned char>* patternsPtr = nullptr; + auto pattBranch = clusTree->GetBranch("MFTClusterPatt"); + if (pattBranch) { + pattBranch->SetAddress(&patternsPtr); + } + + // ROFrecords + std::vector<ROFRec> rofRecVec, *rofRecVecP = &rofRecVec; + clusTree->SetBranchAddress("MFTClustersROF", &rofRecVecP); + + // Cluster MC labels + o2::dataformats::MCTruthContainer<o2::MCCompLabel>* clusLabArr = nullptr; + std::vector<MC2ROF> mc2rofVec, *mc2rofVecP = &mc2rofVec; + if (hitTree && clusTree->GetBranch("MFTClusterMCTruth")) { + clusTree->SetBranchAddress("MFTClusterMCTruth", &clusLabArr); + clusTree->SetBranchAddress("MFTClustersMC2ROF", &mc2rofVecP); + } + clusTree->GetEntry(0); + + // Topologies dictionaries: 1) all clusters 2) signal clusters only 3) noise clusters only + BuildTopologyDictionary completeDictionary; + BuildTopologyDictionary signalDictionary; + BuildTopologyDictionary noiseDictionary; + + int nROFRec = (int)rofRecVec.size(); + std::vector<int> mcEvMin, mcEvMax; + + if (clusLabArr) { // >> build min and max MC events used by each ROF + mcEvMin.resize(nROFRec, hitTree->GetEntries()); + mcEvMax.resize(nROFRec, -1); + for (int imc = mc2rofVec.size(); imc--;) { + int hadrID = hadronicMCMap[imc]; + if (hadrID < 0) { + continue; + } + const auto& mc2rof = mc2rofVec[imc]; + if (mc2rof.rofRecordID < 0) { + continue; // this MC event did not contribute to any ROF + } + for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + int irof = mc2rof.rofRecordID + irfd; + if (mcEvMin[irof] > hadrID) { + mcEvMin[irof] = hadrID; + } + if (mcEvMax[irof] < hadrID) { + mcEvMax[irof] = hadrID; + } + } + } + } // << build min and max MC events used by each ROF + + auto pattIdx = patternsPtr->cbegin(); + for (int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = rofRecVec[irof]; + + rofRec.print(); + + if (clusLabArr) { // >> read and map MC events contributing to this ROF + for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { + if (!hitVecPool[im]) { + hitTree->SetBranchAddress("MFTHit", &hitVecPool[im]); + hitTree->GetEntry(im); + auto& mc2hit = mc2hitVec[im]; + const auto* hitArray = hitVecPool[im]; + for (int ih = hitArray->size(); ih--;) { + const auto& hit = (*hitArray)[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + } // << cache MC events contributing to this ROF + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + int clEntry = rofRec.getFirstEntry() + icl; // entry of icl-th cluster of this ROF in the vector of clusters + // do we read MC data? + + const auto& cluster = (*clusArr)[clEntry]; + + if (cluster.getPatternID() != CompCluster::InvalidPatternID) { + LOG(WARNING) << "Encountered patternID = " << cluster.getPatternID() << " != " << CompCluster::InvalidPatternID; + LOG(WARNING) << "Clusters have already been generated with a dictionary! Quitting"; + return; + } + + ClusterTopology topology; + o2::itsmft::ClusterPattern pattern(pattIdx); + topology.setPattern(pattern); + + float dX = BuildTopologyDictionary::IgnoreVal, dZ = BuildTopologyDictionary::IgnoreVal; + if (clusLabArr) { + const auto& lab = (clusLabArr->getLabels(clEntry))[0]; + auto srcID = lab.getSourceID(); + if (lab.isValid() && srcID != QEDSourceID) { // use MC truth info only for non-QED and non-noise clusters + auto trID = lab.getTrackID(); + const auto& mc2hit = mc2hitVec[lab.getEventID()]; + const auto* hitArray = hitVecPool[lab.getEventID()]; + Int_t chipID = cluster.getSensorID(); + uint64_t key = (uint64_t(trID) << 32) + chipID; + auto hitEntry = mc2hit.find(key); + if (hitEntry != mc2hit.end()) { + const auto& hit = (*hitArray)[hitEntry->second]; + auto locH = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local + auto locHsta = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); + locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); + const auto locC = o2::itsmft::TopologyDictionary::getClusterCoordinates(cluster, pattern); + dX = locH.X() - locC.X(); + dZ = locH.Z() - locC.Z(); + if (checkOutliers > 0.) { + if (std::abs(dX) > topology.getRowSpan() * o2::itsmft::SegmentationAlpide::PitchRow * checkOutliers || + std::abs(dZ) > topology.getColumnSpan() * o2::itsmft::SegmentationAlpide::PitchCol * checkOutliers) { // ignore outlier + dX = dZ = BuildTopologyDictionary::IgnoreVal; + } + } + } else { + printf("Failed to find MC hit entry for Tr:%d chipID:%d\n", trID, chipID); + lab.print(); + } + signalDictionary.accountTopology(topology, dX, dZ); + } else { + noiseDictionary.accountTopology(topology, dX, dZ); + } + } + completeDictionary.accountTopology(topology, dX, dZ); + } + // clean MC cache for events which are not needed anymore + if (clusLabArr) { + int irfNext = irof; + int limMC = irfNext == nROFRec ? hitVecPool.size() : mcEvMin[irfNext]; // can clean events up to this + for (int imc = mcEvMin[irof]; imc < limMC; imc++) { + delete hitVecPool[imc]; + hitVecPool[imc] = nullptr; + mc2hitVec[imc].clear(); + } + } + } + auto dID = o2::detectors::DetID::MFT; + + completeDictionary.setThreshold(0.0001); + completeDictionary.groupRareTopologies(); + completeDictionary.printDictionaryBinary(o2::base::NameConf::getDictionaryFileName(dID, "", ".bin")); + completeDictionary.printDictionary(o2::base::NameConf::getDictionaryFileName(dID, "", ".txt")); + completeDictionary.saveDictionaryRoot(o2::base::NameConf::getDictionaryFileName(dID, "", ".root")); + + TFile histogramOutput("histograms.root", "recreate"); + TCanvas* cComplete = new TCanvas("cComplete", "Distribution of all the topologies"); + cComplete->cd(); + cComplete->SetLogy(); + TH1F* hComplete = nullptr; + o2::itsmft::TopologyDictionary::getTopologyDistribution(completeDictionary.getDictionary(), hComplete, "hComplete"); + hComplete->SetDirectory(0); + hComplete->Draw("hist"); + hComplete->Write(); + cComplete->Write(); + + TCanvas* cNoise = nullptr; + TCanvas* cSignal = nullptr; + TH1F* hNoise = nullptr; + TH1F* hSignal = nullptr; + + if (clusLabArr) { + noiseDictionary.setThreshold(0.0001); + noiseDictionary.groupRareTopologies(); + noiseDictionary.printDictionaryBinary(o2::base::NameConf::getDictionaryFileName(dID, "noise", ".bin")); + noiseDictionary.printDictionary(o2::base::NameConf::getDictionaryFileName(dID, "noise", ".txt")); + noiseDictionary.saveDictionaryRoot(o2::base::NameConf::getDictionaryFileName(dID, "noise", ".root")); + signalDictionary.setThreshold(0.0001); + signalDictionary.groupRareTopologies(); + signalDictionary.printDictionaryBinary(o2::base::NameConf::getDictionaryFileName(dID, "signal", ".bin")); + signalDictionary.printDictionary(o2::base::NameConf::getDictionaryFileName(dID, "signal", ".txt")); + signalDictionary.saveDictionaryRoot(o2::base::NameConf::getDictionaryFileName(dID, "signal", ".root")); + cNoise = new TCanvas("cNoise", "Distribution of noise topologies"); + cNoise->cd(); + cNoise->SetLogy(); + o2::itsmft::TopologyDictionary::getTopologyDistribution(noiseDictionary.getDictionary(), hNoise, "hNoise"); + hNoise->SetDirectory(0); + hNoise->Draw("hist"); + histogramOutput.cd(); + hNoise->Write(); + cNoise->Write(); + cSignal = new TCanvas("cSignal", "cSignal"); + cSignal->cd(); + cSignal->SetLogy(); + o2::itsmft::TopologyDictionary::getTopologyDistribution(signalDictionary.getDictionary(), hSignal, "hSignal"); + hSignal->SetDirectory(0); + hSignal->Draw("hist"); + histogramOutput.cd(); + hSignal->Write(); + cSignal->Write(); + sw.Stop(); + sw.Print(); + } +} diff --git a/Detectors/ITSMFT/MFT/reconstruction/src/ClustererTask.cxx b/Detectors/ITSMFT/MFT/reconstruction/src/ClustererTask.cxx index a8f6c55266d36..4bff2797b2856 100644 --- a/Detectors/ITSMFT/MFT/reconstruction/src/ClustererTask.cxx +++ b/Detectors/ITSMFT/MFT/reconstruction/src/ClustererTask.cxx @@ -22,7 +22,6 @@ #include <TTree.h> using namespace o2::mft; -using namespace o2::utils; //_____________________________________________________________________ ClustererTask::ClustererTask(bool useMC, bool raw) : mRawDataMode(raw), diff --git a/Detectors/ITSMFT/MFT/simulation/data/simcuts.dat b/Detectors/ITSMFT/MFT/simulation/data/simcuts.dat index e69de29bb2d1d..23e72e91d1dca 100644 --- a/Detectors/ITSMFT/MFT/simulation/data/simcuts.dat +++ b/Detectors/ITSMFT/MFT/simulation/data/simcuts.dat @@ -0,0 +1,62 @@ +* MFT +* === +* +* Med GAM ELEC NHAD CHAD MUON EBREM MUHAB EDEL MUDEL MUPA ANNI BREM COMP DCAY DRAY HADR LOSS MULS PAIR PHOT RAYL STRA +* "Air" +MFT 0 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Vacuum" +MFT 1 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Si" +MFT 2 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Readout" +MFT 3 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Support" +MFT 4 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Carbon" +MFT 5 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Be" +MFT 6 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Alu" +MFT 7 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Water" +MFT 8 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "SiO2" +MFT 9 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Inox" +MFT 10 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Kapton" +MFT 11 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Epoxy" +MFT 12 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "SE4445" +MFT 13 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "CarbonFiber" +MFT 14 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Rohacell" +MFT 15 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Polyimide" +MFT 16 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "PEEK" +MFT 17 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "FR4" +MFT 18 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Cu" +MFT 19 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "X7Rcapacitors" +MFT 20 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "X7Rweld" +MFT 21 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "CarbonFleece" +MFT 22 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "CarbonFiberM46J" +MFT 23 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Polypropylene" +MFT 24 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "Polyurethane" +MFT 25 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "ALPIDE_METALSTACK" +ALPIDE 0 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "ALPIDE_AIR" +ALPIDE 1 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* "ALPIDE_SI" +ALPIDE 2 3.e-5 3.e-5 3.e-5 3.e-5 1.e-3 3.e-5 3.e-5 3.e-5 3.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 diff --git a/Detectors/ITSMFT/MFT/simulation/include/MFTSimulation/Detector.h b/Detectors/ITSMFT/MFT/simulation/include/MFTSimulation/Detector.h index 8d0a015fbaeb9..70d01ef726ef5 100644 --- a/Detectors/ITSMFT/MFT/simulation/include/MFTSimulation/Detector.h +++ b/Detectors/ITSMFT/MFT/simulation/include/MFTSimulation/Detector.h @@ -90,45 +90,13 @@ class Detector : public o2::base::DetImpl<Detector> /// Creating materials for the detector void createMaterials(); - - enum EMedia { - Zero, - Air, - Vacuum, - Si, - Readout, - Support, - Carbon, - Be, - Alu, - Water, - SiO2, - Inox, - Kapton, - Epoxy, - CarbonFiber, - CarbonEpoxy, - Rohacell, - Polyimide, - PEEK, - FR4, - Cu, - X7R, - X7Rw, - CarbonFleece, - SE4445, - CarbonFiberM46J, - Polypropylene, - Polyurethane - - }; // media IDs used in CreateMaterials - void setDensitySupportOverSi(Double_t density) { - if (density > 1e-6) + if (density > 1e-6) { mDensitySupportOverSi = density; - else + } else { mDensitySupportOverSi = 1e-6; + } } void createGeometry(); diff --git a/Detectors/ITSMFT/MFT/simulation/src/Detector.cxx b/Detectors/ITSMFT/MFT/simulation/src/Detector.cxx index f79f11f21a1fb..a1c934cc90aa3 100644 --- a/Detectors/ITSMFT/MFT/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/MFT/simulation/src/Detector.cxx @@ -111,8 +111,9 @@ Bool_t Detector::ProcessHits(FairVolume* vol) Int_t copy; // Check if hit is into a MFT sensor volume - if (fMC->CurrentVolID(copy) != mftGeo->getSensorVolumeID()) + if (fMC->CurrentVolID(copy) != mftGeo->getSensorVolumeID()) { return kFALSE; + } // Get The Sensor Unique ID Int_t sensorID = -1, ladderID = -1, diskID = -1, halfID = -1, level = 0; @@ -155,10 +156,12 @@ Bool_t Detector::ProcessHits(FairVolume* vol) } // increment energy loss at all steps except entrance - if (!startHit) + if (!startHit) { mTrackData.mEnergyLoss += fMC->Edep(); - if (!(startHit | stopHit)) + } + if (!(startHit | stopHit)) { return kFALSE; // do noting + } if (startHit) { @@ -263,10 +266,12 @@ void Detector::createMaterials() Float_t zRohacell[nRohacell] = {1., 6., 8.}; Float_t wRohacell[nRohacell] = {0.0858, 0.5964, 0.3178}; Float_t dRohacell; - if (Geometry::sGrooves == 0) + if (Geometry::sGrooves == 0) { dRohacell = 0.032 / (1 - 0.15); // No grooves, water pipes outside rohacell ==> smaller thcikness, greater rohacell density by 15% - if (Geometry::sGrooves == 1) + } + if (Geometry::sGrooves == 1) { dRohacell = 0.032; // With grooves, usual rohacell density: 0.032 g/cm3 rohacell 31, 0.075 g/cm3 rohacell 71; + } // Polyimide pipe mixture const Int_t nPolyimide = 4; @@ -353,24 +358,31 @@ void Detector::createMaterials() LOG(DEBUG) << "Detector::createMaterials >>>>> fieldType " << fieldType << " maxField " << maxField; - o2::base::Detector::Mixture(++matId, "Air$", aAir, zAir, dAir, nAir, wAir); - o2::base::Detector::Medium(Air, "Air$", matId, unsens, fieldType, maxField, tmaxfd, stemax, deemax, epsil, stmin); + matId = 0; // starting value + + o2::base::Detector::Mixture(matId, "Air$", aAir, zAir, dAir, nAir, wAir); + o2::base::Detector::Medium(matId, "Air$", matId, unsens, fieldType, maxField, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Vacuum$", aAir, zAir, dAirVacuum, nAir, wAir); - o2::base::Detector::Medium(Vacuum, "Vacuum$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Vacuum$", aAir, zAir, dAirVacuum, nAir, wAir); + o2::base::Detector::Medium(matId, "Vacuum$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Material(++matId, "Si$", aSi, zSi, dSi, radSi, absSi); - o2::base::Detector::Medium(Si, "Si$", matId, sens, fieldType, maxField, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, + o2::base::Detector::Material(matId, "Si$", aSi, zSi, dSi, radSi, absSi); + o2::base::Detector::Medium(matId, "Si$", matId, sens, fieldType, maxField, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + matId++; - o2::base::Detector::Material(++matId, "Readout$", aSi, zSi, dSi, radSi, absSi); - o2::base::Detector::Medium(Readout, "Readout$", matId, unsens, fieldType, maxField, tmaxfdSi, stemaxSi, deemaxSi, + o2::base::Detector::Material(matId, "Readout$", aSi, zSi, dSi, radSi, absSi); + o2::base::Detector::Medium(matId, "Readout$", matId, unsens, fieldType, maxField, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + matId++; - o2::base::Detector::Material(++matId, "Support$", aSi, zSi, dSi * mDensitySupportOverSi, + o2::base::Detector::Material(matId, "Support$", aSi, zSi, dSi * mDensitySupportOverSi, radSi / mDensitySupportOverSi, absSi / mDensitySupportOverSi); - o2::base::Detector::Medium(Support, "Support$", matId, unsens, fieldType, maxField, tmaxfdSi, stemaxSi, deemaxSi, + o2::base::Detector::Medium(matId, "Support$", matId, unsens, fieldType, maxField, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + matId++; Double_t maxBending = 0; // Max Angle Double_t maxStepSize = 0.001; // Max step size @@ -388,76 +400,97 @@ void Detector::createMaterials() maxStepSize = .01; precision = .003; minStepSize = .003; - o2::base::Detector::Material(++matId, "Carbon$", aCarb, zCarb, dCarb, radCarb, absCarb); - o2::base::Detector::Medium(Carbon, "Carbon$", matId, 0, fieldType, maxField, maxBending, maxStepSize, maxEnergyLoss, + o2::base::Detector::Material(matId, "Carbon$", aCarb, zCarb, dCarb, radCarb, absCarb); + o2::base::Detector::Medium(matId, "Carbon$", matId, 0, fieldType, maxField, maxBending, maxStepSize, maxEnergyLoss, precision, minStepSize); + matId++; - o2::base::Detector::Material(++matId, "Be$", aBe, zBe, dBe, radBe, absBe); - o2::base::Detector::Medium(Be, "Be$", matId, unsens, fieldType, maxField, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Material(matId, "Be$", aBe, zBe, dBe, radBe, absBe); + o2::base::Detector::Medium(matId, "Be$", matId, unsens, fieldType, maxField, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Material(++matId, "Alu$", aAlu, zAlu, dAlu, radAlu, absAlu); - o2::base::Detector::Medium(Alu, "Alu$", matId, unsens, fieldType, maxField, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Material(matId, "Alu$", aAlu, zAlu, dAlu, radAlu, absAlu); + o2::base::Detector::Medium(matId, "Alu$", matId, unsens, fieldType, maxField, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Water$", aWater, zWater, dWater, nWater, wWater); - o2::base::Detector::Medium(Water, "Water$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Water$", aWater, zWater, dWater, nWater, wWater); + o2::base::Detector::Medium(matId, "Water$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "SiO2$", aSiO2, zSiO2, dSiO2, nSiO2, wSiO2); - o2::base::Detector::Medium(SiO2, "SiO2$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "SiO2$", aSiO2, zSiO2, dSiO2, nSiO2, wSiO2); + o2::base::Detector::Medium(matId, "SiO2$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Inox$", aInox, zInox, dInox, nInox, wInox); - o2::base::Detector::Medium(Inox, "Inox$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Inox$", aInox, zInox, dInox, nInox, wInox); + o2::base::Detector::Medium(matId, "Inox$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Kapton$", aKapton, zKapton, dKapton, 4, wKapton); - o2::base::Detector::Medium(Kapton, "Kapton$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Kapton$", aKapton, zKapton, dKapton, 4, wKapton); + o2::base::Detector::Medium(matId, "Kapton$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Epoxy$", aEpoxy, zEpoxy, dEpoxy, -3, wEpoxy); - o2::base::Detector::Medium(Epoxy, "Epoxy$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Epoxy$", aEpoxy, zEpoxy, dEpoxy, -3, wEpoxy); + o2::base::Detector::Medium(matId, "Epoxy$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "SE4445$", aSE4445, zSE4445, dSE4445, -5, wSE4445); - o2::base::Detector::Medium(SE4445, "SE4445$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "SE4445$", aSE4445, zSE4445, dSE4445, -5, wSE4445); + o2::base::Detector::Medium(matId, "SE4445$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "CarbonFiber$", aCM55J, zCM55J, dCM55J, 4, wCM55J); - o2::base::Detector::Medium(CarbonEpoxy, "CarbonFiber$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, + o2::base::Detector::Mixture(matId, "CarbonFiber$", aCM55J, zCM55J, dCM55J, 4, wCM55J); + o2::base::Detector::Medium(matId, "CarbonFiber$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Rohacell", aRohacell, zRohacell, dRohacell, nRohacell, wRohacell); - o2::base::Detector::Medium(Rohacell, "Rohacell", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Rohacell$", aRohacell, zRohacell, dRohacell, nRohacell, wRohacell); + o2::base::Detector::Medium(matId, "Rohacell$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Polyimide", aPolyimide, zPolyimide, dPolyimide, nPolyimide, wPolyimide); - o2::base::Detector::Medium(Polyimide, "Polyimide", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, + o2::base::Detector::Mixture(matId, "Polyimide$", aPolyimide, zPolyimide, dPolyimide, nPolyimide, wPolyimide); + o2::base::Detector::Medium(matId, "Polyimide$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "PEEK$", aPEEK, zPEEK, dPEEK, nPEEK, wPEEK); - o2::base::Detector::Medium(PEEK, "PEEK$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "PEEK$", aPEEK, zPEEK, dPEEK, nPEEK, wPEEK); + o2::base::Detector::Medium(matId, "PEEK$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "FR4$", aFR4, zFR4, dFR4, nFR4, wFR4); - o2::base::Detector::Medium(FR4, "FR4$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "FR4$", aFR4, zFR4, dFR4, nFR4, wFR4); + o2::base::Detector::Medium(matId, "FR4$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Material(++matId, "Cu$", aCu, zCu, dCu, radCu, absCu); - o2::base::Detector::Medium(Cu, "Cu$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Material(matId, "Cu$", aCu, zCu, dCu, radCu, absCu); + o2::base::Detector::Medium(matId, "Cu$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "X7Rcapacitors$", aX7R, zX7R, dX7R, 6, wX7R); - o2::base::Detector::Medium(X7R, "X7Rcapacitors$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, + o2::base::Detector::Mixture(matId, "X7Rcapacitors$", aX7R, zX7R, dX7R, 6, wX7R); + o2::base::Detector::Medium(matId, "X7Rcapacitors$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "X7Rweld$", aX7Rweld, zX7Rweld, dX7Rweld, 2, wX7Rweld); - o2::base::Detector::Medium(X7Rw, "X7Rweld$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "X7Rweld$", aX7Rweld, zX7Rweld, dX7Rweld, 2, wX7Rweld); + o2::base::Detector::Medium(matId, "X7Rweld$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; // Carbon fleece from AliITSSUv2.cxx - o2::base::Detector::Material(++matId, "CarbonFleece$", 12.0107, 6, 0.4, radCarb, absCarb); // 999,999); why 999??? - o2::base::Detector::Medium(CarbonFleece, "CarbonFleece$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, + o2::base::Detector::Material(matId, "CarbonFleece$", 12.0107, 6, 0.4, radCarb, absCarb); // 999,999); why 999??? + o2::base::Detector::Medium(matId, "CarbonFleece$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; //Barrel Materials - o2::base::Detector::Mixture(++matId, "CarbonFiberM46J", aCM46J, zCM46J, dCM46J, nCM46J, wCM46J); - o2::base::Detector::Medium(CarbonFiberM46J, "CarbonFiberM46J", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "CarbonFiberM46J$", aCM46J, zCM46J, dCM46J, nCM46J, wCM46J); + o2::base::Detector::Medium(matId, "CarbonFiberM46J$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Polypropylene", aPolyppln, zPolyppln, dPolyppln, nPolyppln, wPolyppln); - o2::base::Detector::Medium(Polypropylene, "Polypropylene", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Polypropylene$", aPolyppln, zPolyppln, dPolyppln, nPolyppln, wPolyppln); + o2::base::Detector::Medium(matId, "Polypropylene$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; - o2::base::Detector::Mixture(++matId, "Polyurethane", aPolyurthn, zPolyurthn, dPolyurthn, nPolyurthn, wPolyurthn); - o2::base::Detector::Medium(Polyurethane, "Polyurethane", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + o2::base::Detector::Mixture(matId, "Polyurethane$", aPolyurthn, zPolyurthn, dPolyurthn, nPolyurthn, wPolyurthn); + o2::base::Detector::Medium(matId, "Polyurethane$", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); + matId++; LOG(DEBUG) << "Detector::createMaterials -----> matId = " << matId; } diff --git a/Detectors/ITSMFT/MFT/simulation/src/DigitizerTask.cxx b/Detectors/ITSMFT/MFT/simulation/src/DigitizerTask.cxx index 72229977cf3c6..b1070a2a1da18 100644 --- a/Detectors/ITSMFT/MFT/simulation/src/DigitizerTask.cxx +++ b/Detectors/ITSMFT/MFT/simulation/src/DigitizerTask.cxx @@ -61,7 +61,7 @@ InitStatus DigitizerTask::Init() mgr->RegisterAny("MFTDigitMCTruth", mMCTruthArrayPtr, kTRUE); GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::L2G)); // make sure L2G matrices are loaded + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); // make sure L2G matrices are loaded mDigitizer.setGeometry(geom); mDigitizer.setDigits(mDigitsArrayPtr); diff --git a/Detectors/ITSMFT/MFT/simulation/src/digi2raw.cxx b/Detectors/ITSMFT/MFT/simulation/src/digi2raw.cxx index 109c3d2056b00..d7ccb8e6e50eb 100644 --- a/Detectors/ITSMFT/MFT/simulation/src/digi2raw.cxx +++ b/Detectors/ITSMFT/MFT/simulation/src/digi2raw.cxx @@ -209,8 +209,9 @@ void setupLinks(o2::itsmft::MC2RawEncoder<MAP>& m2r, std::string_view outDir, st for (int ir = 0; ir < nruLr; ir++) { - if (h != (ir / 4)) + if (h != (ir / 4)) { continue; + } // RU software id ruSW = nruLr * ilr + (nruLr / 2) * (ir / 4) + (ir % 4); @@ -227,7 +228,6 @@ void setupLinks(o2::itsmft::MC2RawEncoder<MAP>& m2r, std::string_view outDir, st int ruID = nRUtot++; bool accept = !(ruSW < m2r.getRUSWMin() || ruSW > m2r.getRUSWMax()); // ignored RUs ? - int accL = 0; if (accept) { m2r.getCreateRUDecode(ruSW); // create RU container nRU++; @@ -236,12 +236,11 @@ void setupLinks(o2::itsmft::MC2RawEncoder<MAP>& m2r, std::string_view outDir, st uint32_t lanes = mp.getCablesOnRUType(ru.ruInfo->ruType); // lanes patter of this RU ru.links[0] = m2r.addGBTLink(); auto link = m2r.getGBTLink(ru.links[0]); - link->lanes = lanes & ((0x1 << lnkAs) - 1) << (accL); + link->lanes = lanes; link->idInCRU = linkID; link->cruID = cruIDtmp * 100 + o2::detectors::DetID::MFT; link->feeID = mp.RUSW2FEEId(ruSW); link->endPointID = 0; // 0 or 1 - accL += lnkAs; // register the link in the writer, if not done here, its data will be dumped to common default file //printf("Register link: FeeID 0x%02x , CRU ID 0x%x , link ID %2d \n", link->feeID, link->cruID, link->idInCRU); //printf("RU SW: %2d HW: 0x%02x Type: %2d %s \n", ruSW, ruHW, ruType, outFileLink.data()); diff --git a/Detectors/ITSMFT/MFT/tracking/CMakeLists.txt b/Detectors/ITSMFT/MFT/tracking/CMakeLists.txt index 118ab44c59117..bd38dc6de0270 100644 --- a/Detectors/ITSMFT/MFT/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/tracking/CMakeLists.txt @@ -11,14 +11,12 @@ o2_add_library(MFTTracking TARGETVARNAME targetName SOURCES src/Tracker.cxx - src/TrackParamMFT.cxx src/ROframe.cxx src/Cluster.cxx src/IOUtils.cxx - src/TrackFitter.cxx - src/FitterTrackMFT.cxx - src/TrackExtrap.cxx - src/MFTTrackingParam.cxx + src/TrackFitter.cxx + src/MFTTrackingParam.cxx + src/TrackerConfig.cxx PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::DataFormatsITSMFT O2::SimulationDataFormat @@ -29,8 +27,6 @@ o2_add_library(MFTTracking o2_target_root_dictionary(MFTTracking HEADERS include/MFTTracking/TrackCA.h HEADERS include/MFTTracking/TrackFitter.h - HEADERS include/MFTTracking/TrackExtrap.h - HEADERS include/MFTTracking/FitterTrackMFT.h - HEADERS include/MFTTracking/TrackParamMFT.h HEADERS include/MFTTracking/MFTTrackingParam.h + HEADERS include/MFTTracking/TrackerConfig.h LINKDEF src/MFTTrackingLinkDef.h) diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cell.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cell.h index 68b6a47150fe4..4b1a78cbb2f32 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cell.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cell.h @@ -26,10 +26,11 @@ namespace o2 namespace mft { -class Cell final +class Cell { public: Cell(); + /// layer1, layer2, clsInLayer1, clsInLayer2, cellId; set level = 1 Cell(const Int_t, const Int_t, const Int_t, const Int_t, const Int_t); const Int_t getFirstLayerId() const; @@ -51,6 +52,23 @@ class Cell final const UChar_t getNLeftNeighbours() const { return mNLeftNeighbours; } const UChar_t getNRightNeighbours() const { return mNRightNeighbours; } + void setCoordinates(Float_t* coord) + { + mCoord[0] = coord[0]; // X1 + mCoord[1] = coord[1]; // Y1 + mCoord[2] = coord[2]; // Z1 + mCoord[3] = coord[3]; // X2 + mCoord[4] = coord[4]; // Y2 + mCoord[5] = coord[5]; // Z2 + } + + const Float_t getX1() const { return mCoord[0]; } + const Float_t getY1() const { return mCoord[1]; } + const Float_t getZ1() const { return mCoord[2]; } + const Float_t getX2() const { return mCoord[3]; } + const Float_t getY2() const { return mCoord[4]; } + const Float_t getZ2() const { return mCoord[5]; } + private: const Int_t mFirstLayerId; const Int_t mSecondLayerId; @@ -64,6 +82,7 @@ class Cell final UChar_t mNRightNeighbours; std::array<std::pair<Int_t, Int_t>, constants::mft::MaxCellNeighbours> mLeftNeighbours; std::array<std::pair<Int_t, Int_t>, constants::mft::MaxCellNeighbours> mRightNeighbours; + Float_t mCoord[6]; }; inline Cell::Cell() @@ -93,7 +112,6 @@ inline Cell::Cell(const Int_t firstLayerId, const Int_t secondLayerId, const Int mNLeftNeighbours{0}, mNRightNeighbours{0} { - // Nothing to do } inline const Int_t Cell::getFirstLayerId() const { return mFirstLayerId; } @@ -113,24 +131,16 @@ inline void Cell::setLevel(const Int_t level) { mLevel = level; } inline void Cell::addRightNeighbour(const Int_t layer, const Int_t clusterId) { - //std::cout << "Cell::addRightNeighbour " << layer << " " << clusterId << std::endl; - try { - mRightNeighbours.at(mNRightNeighbours++) = std::pair<Int_t, Int_t>(layer, clusterId); - } catch (const std::out_of_range& err) { - std::cout << "Maximum number of right neighbours for this cell!" << std::endl; - } - //std::cout << "Cell::addRightNeighbour done..." << std::endl; + mRightNeighbours.at(mNRightNeighbours++) = std::pair<Int_t, Int_t>(layer, clusterId); } inline void Cell::addLeftNeighbour(const Int_t layer, const Int_t clusterId) { - //std::cout << "Cell::addLeftNeighbour " << layer << " " << clusterId << std::endl; try { mLeftNeighbours.at(mNLeftNeighbours++) = std::pair<Int_t, Int_t>(layer, clusterId); } catch (const std::out_of_range& err) { std::cout << "Maximum number of left neighbours for this cell!" << std::endl; } - //std::cout << "Cell::addLeftNeighbour done..." << std::endl; } inline const std::array<std::pair<Int_t, Int_t>, constants::mft::MaxCellNeighbours>& Cell::getLeftNeighbours() const diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cluster.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cluster.h index 63bdaada3f836..a6fb4ac8392ad 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cluster.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Cluster.h @@ -16,9 +16,7 @@ #define O2_MFT_CLUSTER_H_ #include <array> - -#include "MFTTracking/IndexTableUtils.h" - +#include "ReconstructionDataFormats/BaseCluster.h" #include "GPUCommonDef.h" namespace o2 @@ -26,27 +24,29 @@ namespace o2 namespace mft { -struct Cluster final { - Cluster(const Float_t x, const Float_t y, const Float_t z, const Float_t phi, const Float_t r, const Int_t idx, const Int_t bin) - : xCoordinate{x}, - yCoordinate{y}, - zCoordinate{z}, +struct Cluster : public o2::BaseCluster<float> { + Cluster() = default; + Cluster(const Float_t x, const Float_t y, const Float_t z, const Float_t phi, const Float_t r, const Int_t id, const Int_t bin, const Float_t sigX2, const Float_t sigY2, const Int_t sensorID) + : BaseCluster(sensorID, x, y, z), phiCoordinate{phi}, rCoordinate{r}, - clusterId{idx}, - indexTableBin{bin} {}; + clusterId{id}, + indexTableBin{bin}, + sigmaX2{sigX2}, + sigmaY2{sigY2}, + isUsed{false} {}; Cluster(const Float_t x, const Float_t y, const Float_t z, const Int_t index); - Cluster(const Int_t layerIndex, const Cluster& other); - Float_t xCoordinate; - Float_t yCoordinate; - Float_t zCoordinate; + void setUsed(Bool_t bval) { isUsed = bval; } + const Bool_t getUsed() { return isUsed; } + Float_t phiCoordinate; Float_t rCoordinate; Int_t clusterId; Int_t indexTableBin; - Float_t sigmaY2; Float_t sigmaX2; + Float_t sigmaY2; + Bool_t isUsed; }; } // namespace mft diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Constants.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Constants.h index 26a1f51555f1a..51ca134d3a204 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Constants.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Constants.h @@ -17,6 +17,7 @@ #include <climits> #include <vector> +#include <array> #include <Rtypes.h> @@ -47,11 +48,9 @@ constexpr std::array<Float_t, LayersNumber> InverseLayerZCoordinate() { return std::array<Float_t, LayersNumber>{-1. / 45.3, -1. / 46.7, -1. / 48.6, -1. / 50.0, -1. / 52.4, -1. / 53.8, -1. / 67.7, -1. / 69.1, -1. / 76.1, -1. / 77.5}; } -constexpr Int_t MinTrackPoints{4}; -constexpr Int_t MaxTrackPoints{20}; -constexpr Float_t LTFclsRCut{0.0100}; -constexpr Float_t ROADclsRCut{0.0400}; -constexpr Int_t MaxCellNeighbours{10}; +constexpr Int_t MaxCellNeighbours{50}; +constexpr Int_t MaxPointsInRoad{100}; +constexpr Int_t MaxCellsInRoad{100}; } // namespace mft namespace index_table @@ -62,33 +61,7 @@ constexpr Float_t RMax{16.0}; constexpr Float_t PhiMin{0.}; constexpr Float_t PhiMax{o2::constants::math::TwoPI}; // [rad] -constexpr Int_t RBins{50}; -constexpr Int_t PhiBins{50}; - -constexpr Float_t InversePhiBinSize{PhiBins / (PhiMax - PhiMin)}; -constexpr Float_t InverseRBinSize{RBins / (RMax - RMin)}; - -constexpr UChar_t LTFseed2BinWin{3}; -constexpr UChar_t LTFinterBinWin{3}; - -constexpr Int_t getRBinIndex(const Float_t r) -{ - return (Int_t)((r - RMin) * InverseRBinSize); -} - -constexpr Int_t getPhiBinIndex(const Float_t phi) -{ - return (Int_t)((phi - PhiMin) * InversePhiBinSize); -} - -constexpr Int_t getBinIndex(const Int_t rIndex, const Int_t phiIndex) -{ - if (0 <= rIndex && rIndex < RBins && - 0 <= phiIndex && phiIndex < PhiBins) { - return (phiIndex * RBins + rIndex); - } - return -1; -} +constexpr Int_t MaxRPhiBins{100 * 100}; } // namespace index_table } // namespace constants diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/FitterTrackMFT.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/FitterTrackMFT.h deleted file mode 100644 index d3a05bb70e637..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/FitterTrackMFT.h +++ /dev/null @@ -1,135 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FitterTrackMFT.h -/// \brief Definition of the MFT track for internal use by the fitter -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#ifndef ALICEO2_MFT_FITTERTRACK_H_ -#define ALICEO2_MFT_FITTERTRACK_H_ - -#include <list> -#include <memory> - -#include "MFTTracking/Cluster.h" -#include "MFTTracking/TrackParamMFT.h" -#include "SimulationDataFormat/MCCompLabel.h" - -namespace o2 -{ -namespace mft -{ - -/// track for internal use byt the MFT Track Fitter -class FitterTrackMFT -{ - public: - FitterTrackMFT() = default; - ~FitterTrackMFT() = default; - - FitterTrackMFT(const FitterTrackMFT& track); - FitterTrackMFT& operator=(const FitterTrackMFT& track) = delete; - //FitterTrackMFT(FitterTrackMFT&&) = delete; - FitterTrackMFT& operator=(FitterTrackMFT&&) = delete; - - /// Return a reference to the track parameters at vertex - const TrackParamMFT& getParamAtVertex() const { return mParamAtVertex; } - - /// Return the number of attached clusters - const int getNClusters() const { return mParamAtClusters.size(); } - - // Set and Get MCCompLabels - const std::array<MCCompLabel, 10>& getMCCompLabels() const { return mMCCompLabels; } // constants::mft::LayersNumber = 10 - void setMCCompLabels(const std::array<MCCompLabel, 10>& labels, int nPoints) - { - mMCCompLabels = labels; - mNPoints = nPoints; - } - - /// Return a reference to the track parameters at first cluster - const TrackParamMFT& first() const { return mParamAtClusters.front(); } - /// Return a reference to the track parameters at last cluster - const TrackParamMFT& last() const { return mParamAtClusters.back(); } - - /// Return an iterator to the track parameters at clusters (point to the first one) - auto begin() { return mParamAtClusters.begin(); } - auto begin() const { return mParamAtClusters.begin(); } - /// Return an iterator passing the track parameters at last cluster - auto end() { return mParamAtClusters.end(); } - auto end() const { return mParamAtClusters.end(); } - /// Return a reverse iterator to the track parameters at clusters (point to the last one) - auto rbegin() { return mParamAtClusters.rbegin(); } - auto rbegin() const { return mParamAtClusters.rbegin(); } - /// Return a reverse iterator passing the track parameters at first cluster - auto rend() { return mParamAtClusters.rend(); } - auto rend() const { return mParamAtClusters.rend(); } - - TrackParamMFT& createParamAtCluster(const Cluster& cluster); - void addParamAtCluster(const TrackParamMFT& param); - /// Remove the given track parameters from the internal list and return an iterator to the parameters that follow - auto removeParamAtCluster(std::list<TrackParamMFT>::iterator& itParam) { return mParamAtClusters.erase(itParam); } - - bool isBetter(const FitterTrackMFT& track) const; - - void tagRemovableClusters(uint8_t requestedStationMask); - - void setCurrentParam(const TrackParamMFT& param, int chamber); - TrackParamMFT& getCurrentParam(); - /// get a reference to the current chamber on which the current parameters are given - const int& getCurrentChamber() const { return mCurrentLayer; } - /// check whether the current track parameters exist - bool hasCurrentParam() const { return mCurrentParam ? true : false; } - /// check if the current parameters are valid - bool areCurrentParamValid() const { return (mCurrentLayer > -1); } - /// invalidate the current parameters - void invalidateCurrentParam() { mCurrentLayer = -1; } - - /// set the flag telling if this track shares cluster(s) with another - void connected(bool connected = true) { mConnected = connected; } - /// return the flag telling if this track shares cluster(s) with another - bool isConnected() const { return mConnected; } - - /// set the flag telling if this track should be deleted - void removable(bool removable = true) { mRemovable = removable; } - /// return the flag telling if this track should be deleted - bool isRemovable() const { return mRemovable; } - - const Int_t getNPoints() const { return mNPoints; } - - // Charge and momentum from quadratic regression of clusters X,Y positions - void setInvQPtQuadtratic(Double_t invqpt) { mInvQPtQuadtratic = invqpt; } - const Double_t getInvQPtQuadtratic() const { return mInvQPtQuadtratic; } // Inverse charged pt - const Double_t getPtQuadtratic() const { return TMath::Abs(1.f / getInvQPtQuadtratic()); } - const Double_t getChargeQuadratic() const { return TMath::Sign(1., getInvQPtQuadtratic()); } - void setChi2QPtQuadtratic(Double_t chi2) { mQuadraticFitChi2 = chi2; } - const Double_t getChi2QPtQuadtratic() const { return mQuadraticFitChi2; } - - private: - TrackParamMFT mParamAtVertex{}; ///< track parameters at vertex - std::list<TrackParamMFT> mParamAtClusters{}; ///< list of track parameters at each cluster - std::unique_ptr<TrackParamMFT> mCurrentParam{}; ///< current track parameters used during tracking - int mCurrentLayer = -1; ///< current chamber on which the current parameters are given - bool mConnected = false; ///< flag telling if this track shares cluster(s) with another - bool mRemovable = false; ///< flag telling if this track should be deleted - Int_t mNPoints{0}; // Number of clusters - std::array<MCCompLabel, 10> mMCCompLabels; // constants::mft::LayersNumber = 10 - - // Results from quadratic regression of clusters X,Y positions - // Chi2 of the quadratic regression used to estimate track pT and charge - Double_t mQuadraticFitChi2 = 0.; - // inversed charged momentum from quadratic regression - Double_t mInvQPtQuadtratic; -}; - -} // namespace mft -} // namespace o2 - -#endif // ALICEO2_MFT_FITTERTRACK_H_ diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IOUtils.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IOUtils.h index 99d2b3a15aaf7..b11522c6eb496 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IOUtils.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IOUtils.h @@ -21,6 +21,7 @@ #include <vector> #include <gsl/gsl> +#include "MFTTracking/Tracker.h" #include "MFTTracking/ROframe.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -56,7 +57,7 @@ constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; int loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& events, gsl::span<const itsmft::CompClusterExt> clusters, gsl::span<const unsigned char>::iterator& pattIt, const itsmft::TopologyDictionary& dict, - const dataformats::MCTruthContainer<MCCompLabel>* mClsLabels = nullptr); + const dataformats::MCTruthContainer<MCCompLabel>* mClsLabels = nullptr, const o2::mft::Tracker* tracker = nullptr); } // namespace ioutils } // namespace mft diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IndexTableUtils.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IndexTableUtils.h deleted file mode 100644 index 85729dabe08b5..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/IndexTableUtils.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 IndexTableUtils.h -/// \brief May collect the functions to place clusters into R-Phi bins (to be fixed) -/// - -#ifndef O2_MFT_INDEXTABLEUTILS_H_ -#define O2_MFT_INDEXTABLEUTILS_H_ - -#include <array> -#include <utility> -#include <vector> - -#include "MFTTracking/Constants.h" - -namespace o2 -{ -namespace mft -{ - -namespace index_table_utils -{ -Int_t getRBinIndex(const Int_t, const Float_t); -Int_t getPhiBinIndex(const Float_t); -Int_t getBinIndex(const Int_t, const Int_t); -} // namespace IndexTableUtils - -inline Int_t index_table_utils::getRBinIndex(const Int_t layerIndex, const Float_t rCoordinate) -{ - return -1; -} - -inline Int_t index_table_utils::getPhiBinIndex(const Float_t currentPhi) -{ - return -1; -} - -inline Int_t index_table_utils::getBinIndex(const Int_t rIndex, const Int_t phiIndex) -{ - return -1; -} -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_INDEXTABLEUTILS_H_ */ diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h index 6ac8d87a8a885..af47f2122d629 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h @@ -21,12 +21,6 @@ namespace o2 namespace mft { -enum MFTTrackingSeed { - AB, // - CE, - DH -}; - enum MFTTrackModel { Helix, Quadratic, @@ -37,13 +31,32 @@ enum MFTTrackModel { // ** Parameters for MFT tracking configuration // ** struct MFTTrackingParam : public o2::conf::ConfigurableParamHelper<MFTTrackingParam> { - Int_t seed = MFTTrackingSeed::DH; Int_t trackmodel = MFTTrackModel::Helix; - double sigmaboost = 1e0; - double seedH_k = 1.0; - double MFTRadLenghts = 0.041; // MFT average material budget within acceptance + double MFTRadLength = 1.0; // MFT average material budget within acceptance. Should be 0.041 bool verbose = false; + /// tracking algorithm (LTF and CA) parameters + /// minimum number of points for a LTF track + Int_t MinTrackPointsLTF = 5; + /// minimum number of points for a CA track + Int_t MinTrackPointsCA = 4; + /// minimum number of detector stations for a LTF track + Int_t MinTrackStationsLTF = 4; + /// minimum number of detector stations for a CA track + Int_t MinTrackStationsCA = 4; + /// maximum distance for a cluster to be attached to a seed line (LTF) + Float_t LTFclsRCut = 0.0100; + /// maximum distance for a cluster to be attached to a seed line (CA road) + Float_t ROADclsRCut = 0.0400; + /// number of bins in r-direction + Int_t RBins = 50; + /// number of bins in phi-direction + Int_t PhiBins = 50; + /// RPhi search window bin width for the second point of a seed (LTF and CA) + Int_t LTFseed2BinWin = 3; + /// RPhi search window bin width for the intermediate points + Int_t LTFinterBinWin = 3; + O2ParamDef(MFTTrackingParam, "MFTTracking"); }; diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/ROframe.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/ROframe.h index 4b4711d45640a..6ac9e28d00def 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/ROframe.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/ROframe.h @@ -39,104 +39,62 @@ namespace mft class TrackCA; class TrackLTF; -class ROframe final +class ROframe { public: ROframe(Int_t ROframeId); - Int_t getROFrameId() const; + Int_t getROFrameId() const { return mROframeId; } Int_t getTotalClusters() const; void setROFrameId(const Int_t rofid) { mROframeId = rofid; } - const std::array<std::vector<Cluster>, constants::mft::LayersNumber>& getClusters() const; - const std::vector<Cluster>& getClustersInLayer(Int_t layerId) const; - const MCCompLabel& getClusterLabels(Int_t layerId, const Cluster& cl) const; - const MCCompLabel& getClusterLabels(Int_t layerId, const Int_t clId) const; - const std::map<Int_t, std::pair<Int_t, Int_t>>& getClusterBinIndexRange(Int_t layerId) const; - Int_t getClusterExternalIndex(Int_t layerId, const Int_t clId) const; - const std::array<std::vector<Cell>, constants::mft::LayersNumber>& getCells() const; - const std::vector<Cell>& getCellsInLayer(Int_t layerId) const; + std::vector<Cluster>& getClustersInLayer(Int_t layerId) { return mClusters[layerId]; } + + const MCCompLabel& getClusterLabels(Int_t layerId, const Int_t clusterId) const { return mClusterLabels[layerId][clusterId]; } + + const std::array<std::pair<Int_t, Int_t>, constants::index_table::MaxRPhiBins>& getClusterBinIndexRange(Int_t layerId) const { return mClusterBinIndexRange[layerId]; } + + const Int_t getClusterExternalIndex(Int_t layerId, const Int_t clusterId) const { return mClusterExternalIndices[layerId][clusterId]; } + std::vector<TrackLTF>& getTracksLTF(); TrackLTF& getCurrentTrackLTF(); - void removeCurrentTrackLTF(); - Road& getCurrentRoad(); - void removeCurrentRoad(); - std::vector<Road>& getRoads(); + std::vector<TrackCA>& getTracksCA(); TrackCA& getCurrentTrackCA(); - void removeCurrentTrackCA(); - template <typename... T> - void addClusterToLayer(Int_t layer, T&&... args); + Road& getCurrentRoad(); template <typename... T> - void addCellToLayer(Int_t layer, T&&... args); + void addClusterToLayer(Int_t layer, T&&... args); void addClusterLabelToLayer(Int_t layer, const MCCompLabel label); void addClusterExternalIndexToLayer(Int_t layer, const Int_t idx); - void addClusterBinIndexRangeToLayer(Int_t layer, const std::pair<Int_t, std::pair<Int_t, Int_t>> range); - void addTrackLTF(); - void addTrackCA(); + + void addTrackLTF() { mTracksLTF.emplace_back(); } + + void addTrackCA(const Int_t); + void addRoad(); - void initialise(); - void sortClusters(); + void initialize(); - Bool_t isClusterUsed(Int_t layer, Int_t clusterId) const; - void markUsedCluster(Int_t layer, Int_t clusterId); + void sortClusters(); void clear(); + const Int_t getNClustersInLayer(Int_t layerId) const { return mClusters[layerId].size(); } + private: Int_t mROframeId; std::array<std::vector<Cluster>, constants::mft::LayersNumber> mClusters; std::array<std::vector<MCCompLabel>, constants::mft::LayersNumber> mClusterLabels; std::array<std::vector<Int_t>, constants::mft::LayersNumber> mClusterExternalIndices; - std::array<std::map<Int_t, std::pair<Int_t, Int_t>>, constants::mft::LayersNumber> mClusterBinIndexRange; - std::array<std::vector<Bool_t>, constants::mft::LayersNumber> mUsedClusters; - std::array<std::vector<Cell>, constants::mft::LayersNumber> mCells; + std::array<std::array<std::pair<Int_t, Int_t>, constants::index_table::MaxRPhiBins>, constants::mft::LayersNumber> mClusterBinIndexRange; std::vector<TrackLTF> mTracksLTF; std::vector<TrackCA> mTracksCA; std::vector<Road> mRoads; }; -inline Int_t ROframe::getROFrameId() const { return mROframeId; } - -inline const std::array<std::vector<Cluster>, constants::mft::LayersNumber>& ROframe::getClusters() const -{ - return mClusters; -} - -inline const std::vector<Cluster>& ROframe::getClustersInLayer(Int_t layerId) const -{ - return mClusters[layerId]; -} - -inline const MCCompLabel& ROframe::getClusterLabels(Int_t layerId, const Cluster& cl) const -{ - return mClusterLabels[layerId][cl.clusterId]; -} - -inline const MCCompLabel& ROframe::getClusterLabels(Int_t layerId, const Int_t clId) const -{ - return mClusterLabels[layerId][clId]; -} - -inline Int_t ROframe::getClusterExternalIndex(Int_t layerId, const Int_t clId) const -{ - return mClusterExternalIndices[layerId][clId]; -} - -inline const std::map<Int_t, std::pair<Int_t, Int_t>>& ROframe::getClusterBinIndexRange(Int_t layerId) const -{ - return mClusterBinIndexRange[layerId]; -} - -inline const std::vector<Cell>& ROframe::getCellsInLayer(Int_t layerId) const -{ - return mCells[layerId]; -} - template <typename... T> void ROframe::addClusterToLayer(Int_t layer, T&&... values) { @@ -150,36 +108,11 @@ inline void ROframe::addClusterExternalIndexToLayer(Int_t layer, const Int_t idx mClusterExternalIndices[layer].push_back(idx); } -inline void ROframe::addClusterBinIndexRangeToLayer(Int_t layer, const std::pair<Int_t, std::pair<Int_t, Int_t>> range) -{ - mClusterBinIndexRange[layer].insert(range); -} - -template <typename... T> -void ROframe::addCellToLayer(Int_t layer, T&&... values) -{ - mCells[layer].emplace_back(layer, std::forward<T>(values)...); -} - -inline Bool_t ROframe::isClusterUsed(Int_t layer, Int_t clusterId) const -{ - return mUsedClusters[layer][clusterId]; -} - -inline void ROframe::markUsedCluster(Int_t layer, Int_t clusterId) { mUsedClusters[layer][clusterId] = kTRUE; } - -inline void ROframe::addTrackLTF() { mTracksLTF.emplace_back(); } - inline TrackLTF& ROframe::getCurrentTrackLTF() { return mTracksLTF.back(); } -inline void ROframe::removeCurrentTrackLTF() -{ - mTracksLTF.pop_back(); -} - inline std::vector<TrackLTF>& ROframe::getTracksLTF() { return mTracksLTF; @@ -188,7 +121,6 @@ inline std::vector<TrackLTF>& ROframe::getTracksLTF() inline void ROframe::addRoad() { mRoads.emplace_back(); - mRoads.back().setRoadId(mRoads.size() - 1); } inline Road& ROframe::getCurrentRoad() @@ -196,28 +128,16 @@ inline Road& ROframe::getCurrentRoad() return mRoads.back(); } -inline void ROframe::removeCurrentRoad() +inline void ROframe::addTrackCA(const Int_t roadId) { - mRoads.pop_back(); + mTracksCA.emplace_back(); } -inline std::vector<Road>& ROframe::getRoads() -{ - return mRoads; -} - -inline void ROframe::addTrackCA() { mTracksCA.emplace_back(); } - inline TrackCA& ROframe::getCurrentTrackCA() { return mTracksCA.back(); } -inline void ROframe::removeCurrentTrackCA() -{ - mTracksCA.pop_back(); -} - inline std::vector<TrackCA>& ROframe::getTracksCA() { return mTracksCA; @@ -229,8 +149,9 @@ inline void ROframe::clear() mClusters[iLayer].clear(); mClusterLabels[iLayer].clear(); mClusterExternalIndices[iLayer].clear(); - mClusterBinIndexRange[iLayer].clear(); - mCells[iLayer].clear(); + for (Int_t iBin = 0; iBin < constants::index_table::MaxRPhiBins; ++iBin) { + mClusterBinIndexRange[iLayer][iBin] = std::pair<Int_t, Int_t>(0, -1); + } } mTracksLTF.clear(); mTracksCA.clear(); diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Road.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Road.h index c458549c4bdad..ba1471bbc6d8a 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Road.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Road.h @@ -17,99 +17,85 @@ #include "MFTTracking/Cell.h" #include "MFTTracking/Constants.h" -#include "SimulationDataFormat/MCCompLabel.h" namespace o2 { namespace mft { -class Road final +class Road { public: - Road(); - void setPoint(const Float_t x, const Float_t y, const Float_t z, const Int_t layer, const Int_t clusterId, const MCCompLabel label, Bool_t& newPoint); + Road() { mRoadId = 0; }; + void reset(); + void initialize(); + + void setPoint(const Int_t layer, const Int_t clusterId) + { + mClusterId[layer].push_back(clusterId); + } + void setRoadId(const Int_t id) { mRoadId = id; } const Int_t getRoadId() const { return mRoadId; } - void setNDisks(const Int_t nd) { mNDisks = nd; } - const Int_t getNDisks() const { return mNDisks; } - const Int_t getNPoints() const { return mNPoints; } + const Int_t getNPointsInLayer(Int_t layer) const; + void getLength(Int_t& layer1, Int_t& layer2) const; - void setHasTracksCA() { mHasTracksCA = kTRUE; } - const Bool_t hasTracksCA() const; - const std::vector<Float_t>& getXCoordinatesInLayer(Int_t layer) const { return mX[layer]; } - const std::vector<Float_t>& getYCoordinatesInLayer(Int_t layer) const { return mY[layer]; } - const std::vector<Float_t>& getZCoordinatesInLayer(Int_t layer) const { return mZ[layer]; } const std::vector<Int_t>& getClustersIdInLayer(Int_t layer) const { return mClusterId[layer]; } - const std::vector<MCCompLabel>& getMCCompLabelsInLayer(Int_t layer) const { return mMCCompLabel[layer]; } template <typename... T> - void addCellToLayer(Int_t layer, T&&... args); + Cell& addCellInLayer(Int_t layer, T&&... args); + + std::vector<Cell>& getCellsInLayer(Int_t); - const std::vector<Cell>& getCellsInLayer(Int_t) const; void addLeftNeighbourToCell(const Int_t, const Int_t, const Int_t, const Int_t); void addRightNeighbourToCell(const Int_t, const Int_t, const Int_t, const Int_t); + void incrementCellLevel(const Int_t, const Int_t); void updateCellLevel(const Int_t, const Int_t); + + void setCellLevel(const Int_t, const Int_t, const Int_t); const Int_t getCellLevel(const Int_t, const Int_t) const; - const Bool_t isCellUsed(const Int_t, const Int_t) const; void setCellUsed(const Int_t, const Int_t, const Bool_t); - void setCellLevel(const Int_t, const Int_t, const Int_t); + const Bool_t isCellUsed(const Int_t, const Int_t) const; private: Int_t mRoadId; - Int_t mNDisks; - Int_t mNPoints; - Bool_t mHasTracksCA; - std::array<std::vector<Float_t>, constants::mft::LayersNumber> mX; - std::array<std::vector<Float_t>, constants::mft::LayersNumber> mY; - std::array<std::vector<Float_t>, constants::mft::LayersNumber> mZ; std::array<std::vector<Int_t>, constants::mft::LayersNumber> mClusterId; - std::array<std::vector<MCCompLabel>, constants::mft::LayersNumber> mMCCompLabel; std::array<std::vector<Cell>, (constants::mft::LayersNumber - 1)> mCell; }; -inline Road::Road() - : mRoadId{0}, - mNDisks{0}, - mNPoints{0} +inline void Road::reset() { - // Nothing to do + Int_t layer; + for (layer = 0; layer < (constants::mft::LayersNumber - 1); ++layer) { + mCell[layer].clear(); + mClusterId[layer].clear(); + } + mClusterId[layer].clear(); } -inline void Road::setPoint(const Float_t x, const Float_t y, const Float_t z, const Int_t layer, const Int_t clusterId, const MCCompLabel label, Bool_t& newPoint) +inline void Road::initialize() { - if (!newPoint) { - if (!mZ.empty()) { - mX[layer].pop_back(); - mY[layer].pop_back(); - mZ[layer].pop_back(); - mClusterId[layer].pop_back(); - mMCCompLabel[layer].pop_back(); - } - } else { // end replace point - newPoint = kFALSE; + Int_t layer; + for (layer = 0; layer < (constants::mft::LayersNumber - 1); ++layer) { + mCell[layer].reserve(constants::mft::MaxCellsInRoad); + mClusterId[layer].reserve(constants::mft::MaxPointsInRoad); } - mX[layer].push_back(x); - mY[layer].push_back(y); - mZ[layer].push_back(z); - mClusterId[layer].push_back(clusterId); - mMCCompLabel[layer].emplace_back(label); - ++mNPoints; + mClusterId[layer].reserve(constants::mft::MaxPointsInRoad); } inline const Int_t Road::getNPointsInLayer(Int_t layer) const { - return mX[layer].size(); + return mClusterId[layer].size(); } inline void Road::getLength(Int_t& layer1, Int_t& layer2) const { layer1 = -1, layer2 = 10; for (Int_t layer = 0; layer < constants::mft::LayersNumber; ++layer) { - if (mX[layer].size() > 0) { + if (mClusterId[layer].size() > 0) { if (layer1 < 0) { layer1 = layer; } @@ -118,18 +104,14 @@ inline void Road::getLength(Int_t& layer1, Int_t& layer2) const } } -inline const Bool_t Road::hasTracksCA() const -{ - return mHasTracksCA; -} - template <typename... T> -void Road::addCellToLayer(Int_t layer, T&&... values) +Cell& Road::addCellInLayer(Int_t layer, T&&... values) { mCell[layer].emplace_back(layer, std::forward<T>(values)...); + return mCell[layer].back(); } -inline const std::vector<Cell>& Road::getCellsInLayer(Int_t layer) const +inline std::vector<Cell>& Road::getCellsInLayer(Int_t layer) { return mCell[layer]; } diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackCA.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackCA.h index e5295e831239e..8dc5a213314c0 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackCA.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackCA.h @@ -16,8 +16,10 @@ #define O2_MFT_TRACKCA_H_ #include <array> +#include "DataFormatsMFT/TrackMFT.h" #include "SimulationDataFormat/MCCompLabel.h" #include "MFTTracking/Constants.h" +#include "MFTTracking/Cluster.h" #include <fairlogger/Logger.h> namespace o2 @@ -25,150 +27,143 @@ namespace o2 namespace mft { -class TrackCA final +class TrackLTF : public TrackMFTExt { public: - TrackCA() = default; - ~TrackCA() = default; - void addCell(const Int_t, const Int_t); - void removeLastCell(Int_t&, Int_t&); - const Int_t getNCells() const; - void setRoadId(const Int_t rid) { mRoadId = rid; } - const Int_t getRoadId() const; - void setPoint(const Float_t x, const Float_t y, const Float_t z, const Int_t layer, const Int_t clusterId, const MCCompLabel label, Bool_t& newPoint); - const Int_t getNPoints() const { return mNPoints; } - void setChiSquareZX(const Float_t chisq) { mChiSquareZX = chisq; } - void setChiSquareZY(const Float_t chisq) { mChiSquareZY = chisq; } - const Float_t getChiSquareZX() const { return mChiSquareZX; } - const Float_t getChiSquareZY() const { return mChiSquareZY; } + TrackLTF() = default; + TrackLTF(const TrackLTF& t) = default; + ~TrackLTF() = default; const std::array<Float_t, constants::mft::LayersNumber>& getXCoordinates() const { return mX; } const std::array<Float_t, constants::mft::LayersNumber>& getYCoordinates() const { return mY; } const std::array<Float_t, constants::mft::LayersNumber>& getZCoordinates() const { return mZ; } + const std::array<Float_t, constants::mft::LayersNumber>& getSigmasX2() const { return mSigmaX2; } + const std::array<Float_t, constants::mft::LayersNumber>& getSigmasY2() const { return mSigmaY2; } const std::array<Int_t, constants::mft::LayersNumber>& getLayers() const { return mLayer; } const std::array<Int_t, constants::mft::LayersNumber>& getClustersId() const { return mClusterId; } - const std::array<Int_t, constants::mft::LayersNumber>& getCellsLayer() const { return mCellLayer; } - const std::array<Int_t, constants::mft::LayersNumber>& getCellsId() const { return mCellId; } const std::array<MCCompLabel, constants::mft::LayersNumber>& getMCCompLabels() const { return mMCCompLabels; } + void setPoint(const Cluster& cl, const Int_t layer, const Int_t clusterId, const MCCompLabel label, const Int_t extClsIndex); + + void sort(); private: - Int_t mNPoints{0}; - Int_t mNCells{0}; - Int_t mRoadId{-1}; - Float_t mChiSquareZX{0.}; - Float_t mChiSquareZY{0.}; std::array<Float_t, constants::mft::LayersNumber> mX = {-25., -25., -25., -25., -25., -25., -25., -25., -25., -25.}; std::array<Float_t, constants::mft::LayersNumber> mY = {-25., -25., -25., -25., -25., -25., -25., -25., -25., -25.}; std::array<Float_t, constants::mft::LayersNumber> mZ = {-120., -120., -120., -120., -120., -120., -120., -120., -120., -120.}; + std::array<Float_t, constants::mft::LayersNumber> mSigmaX2 = {0}; + std::array<Float_t, constants::mft::LayersNumber> mSigmaY2 = {0}; std::array<Int_t, constants::mft::LayersNumber> mLayer; std::array<Int_t, constants::mft::LayersNumber> mClusterId; - std::array<Int_t, constants::mft::LayersNumber> mCellLayer; - std::array<Int_t, constants::mft::LayersNumber> mCellId; std::array<MCCompLabel, constants::mft::LayersNumber> mMCCompLabels; - ClassDefNV(TrackCA, 1); -}; -inline void TrackCA::addCell(const Int_t layer, const Int_t cellId) -{ - mCellLayer[mNCells] = layer; - mCellId[mNCells] = cellId; - mNCells++; -} + ClassDefNV(TrackLTF, 10); +}; -inline void TrackCA::removeLastCell(Int_t& layer, Int_t& cellId) +//_________________________________________________________________________________________________ +class TrackCA : public TrackLTF { - layer = mCellLayer[mNCells - 1]; - cellId = mCellId[mNCells - 1]; - - if (mNPoints == 2) { // we have only a single cell in the track - mNPoints--; + public: + TrackCA() + { + TrackLTF(); + this->setCA(true); } - mNPoints--; - mNCells--; -} - -inline const Int_t TrackCA::getNCells() const -{ - return mNCells; -} + TrackCA(const TrackCA& t) = default; + ~TrackCA() = default; -inline const Int_t TrackCA::getRoadId() const -{ - return mRoadId; -} + private: + ClassDefNV(TrackCA, 10); +}; -inline void TrackCA::setPoint(const Float_t x, const Float_t y, const Float_t z, const Int_t layer, const Int_t clusterId, const MCCompLabel label, Bool_t& newPoint) +//_________________________________________________________________________________________________ +inline void TrackLTF::setPoint(const Cluster& cl, const Int_t layer, const Int_t clusterId, const MCCompLabel label, const Int_t extClsIndex) { - if (newPoint) { - if (mNPoints == constants::mft::LayersNumber) { - LOG(WARN) << "MFT TrackLTF Overflow"; + auto nPoints = getNumberOfPoints(); + if (nPoints > 0) { + if (mZ[nPoints - 1] == cl.getZ()) { + LOG(WARN) << "MFT TrackLTF: skipping setPoint (1 cluster per layer!)"; return; } - mX[mNPoints] = x; - mY[mNPoints] = y; - mZ[mNPoints] = z; - mLayer[mNPoints] = layer; - mClusterId[mNPoints] = clusterId; - mMCCompLabels[mNPoints] = label; - mNPoints++; - } else { - mX[mNPoints] = x; - mY[mNPoints] = y; - mZ[mNPoints] = z; - mLayer[mNPoints] = layer; - mClusterId[mNPoints] = clusterId; - mMCCompLabels[mNPoints] = label; } + if (nPoints > constants::mft::LayersNumber) { + LOG(WARN) << "MFT TrackLTF Overflow"; + return; + } + mX[nPoints] = cl.getX(); + mY[nPoints] = cl.getY(); + mZ[nPoints] = cl.getZ(); + mSigmaX2[nPoints] = cl.sigmaX2; + mSigmaY2[nPoints] = cl.sigmaY2; + mLayer[nPoints] = layer; + mClusterId[nPoints] = clusterId; + mMCCompLabels[nPoints] = label; + setExternalClusterIndex(nPoints, extClsIndex); + setNumberOfPoints(nPoints + 1); } -class TrackLTF final +//_________________________________________________________________________________________________ +inline void TrackLTF::sort() { - public: - TrackLTF() = default; - ~TrackLTF() = default; - void setPoint(const Float_t x, const Float_t y, const Float_t z, const Int_t layer, const Int_t clusterId, const MCCompLabel label, Bool_t& newPoint); - const Int_t getNPoints() const { return mNPoints; } - const std::array<Float_t, constants::mft::LayersNumber>& getXCoordinates() const { return mX; } - const std::array<Float_t, constants::mft::LayersNumber>& getYCoordinates() const { return mY; } - const std::array<Float_t, constants::mft::LayersNumber>& getZCoordinates() const { return mZ; } - const std::array<Int_t, constants::mft::LayersNumber>& getLayers() const { return mLayer; } - const std::array<Int_t, constants::mft::LayersNumber>& getClustersId() const { return mClusterId; } - const std::array<MCCompLabel, constants::mft::LayersNumber>& getMCCompLabels() const { return mMCCompLabels; } + // Orders elements along z position + struct ClusterData { + Float_t x; + Float_t y; + Float_t z; + Float_t sigmaX2; + Float_t sigmaY2; + Int_t layer; + Int_t clusterId; + MCCompLabel label; + Int_t extClsIndex; + }; + std::vector<ClusterData> points; - private: - Int_t mNPoints = 0; - std::array<Float_t, constants::mft::LayersNumber> mX = {-25., -25., -25., -25., -25., -25., -25., -25., -25., -25.}; - std::array<Float_t, constants::mft::LayersNumber> mY = {-25., -25., -25., -25., -25., -25., -25., -25., -25., -25.}; - std::array<Float_t, constants::mft::LayersNumber> mZ = {-120., -120., -120., -120., -120., -120., -120., -120., -120., -120.}; - std::array<Int_t, constants::mft::LayersNumber> mLayer; - std::array<Int_t, constants::mft::LayersNumber> mClusterId; - std::array<MCCompLabel, constants::mft::LayersNumber> mMCCompLabels; - ClassDefNV(TrackLTF, 1); -}; + // Loading cluster data + for (Int_t point = 0; point < getNumberOfPoints(); ++point) { + auto& somepoint = points.emplace_back(); + somepoint.x = mX[point]; + somepoint.y = mY[point]; + somepoint.z = mZ[point]; + somepoint.sigmaX2 = mSigmaX2[point]; + somepoint.sigmaY2 = mSigmaY2[point]; + somepoint.layer = mLayer[point]; + somepoint.clusterId = mClusterId[point]; + somepoint.label = mMCCompLabels[point]; + somepoint.extClsIndex = mExtClsIndex[point]; + } -inline void TrackLTF::setPoint(const Float_t x, const Float_t y, const Float_t z, const Int_t layer, const Int_t clusterId, const MCCompLabel label, Bool_t& newPoint) -{ - if (newPoint) { - if (mNPoints == constants::mft::LayersNumber) { - LOG(WARN) << "MFT TrackLTF Overflow"; - return; - } - mX[mNPoints] = x; - mY[mNPoints] = y; - mZ[mNPoints] = z; - mLayer[mNPoints] = layer; - mClusterId[mNPoints] = clusterId; - mMCCompLabels[mNPoints] = label; - mNPoints++; - } else { - mX[mNPoints] = x; - mY[mNPoints] = y; - mZ[mNPoints] = z; - mLayer[mNPoints] = layer; - mClusterId[mNPoints] = clusterId; - mMCCompLabels[mNPoints] = label; + // Sorting cluster data + std::sort(points.begin(), points.end(), [](ClusterData a, ClusterData b) { return a.z > b.z; }); + + // Storing sorted cluster data + for (Int_t point = 0; point < getNumberOfPoints(); ++point) { + mX[point] = points[point].x; + mY[point] = points[point].y; + mZ[point] = points[point].z; + mSigmaX2[point] = points[point].sigmaX2; + mSigmaY2[point] = points[point].sigmaY2; + mLayer[point] = points[point].layer; + mClusterId[point] = points[point].clusterId; + mMCCompLabels[point] = points[point].label; + mExtClsIndex[point] = points[point].extClsIndex; } } } // namespace mft + +namespace framework +{ +template <typename T> +struct is_messageable; +template <> +struct is_messageable<o2::mft::TrackCA> : std::true_type { +}; + +template <typename T> +struct is_messageable; +template <> +struct is_messageable<o2::mft::TrackLTF> : std::true_type { +}; + +} // namespace framework } // namespace o2 #endif /* O2_MFT_TRACKCA_H_ */ diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackExtrap.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackExtrap.h deleted file mode 100644 index 59b3f74ad2de8..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackExtrap.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackExtrap.h -/// \brief Definition of tools for track extrapolation -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#ifndef ALICEO2_MFT_TRACKEXTRAP_H_ -#define ALICEO2_MFT_TRACKEXTRAP_H_ - -#include <cstddef> - -#include <TMatrixD.h> -#include "MFTTracking/MFTTrackingParam.h" - -namespace o2 -{ -namespace mft -{ - -class TrackParamMFT; - -/// Class holding tools for track extrapolation -class TrackExtrap -{ - public: - // static class - TrackExtrap() = default; - ~TrackExtrap() = default; - - TrackExtrap(const TrackExtrap&) = delete; - TrackExtrap& operator=(const TrackExtrap&) = delete; - TrackExtrap(TrackExtrap&&) = delete; - TrackExtrap& operator=(TrackExtrap&&) = delete; - - void setBz(float bZ) { mBZField = bZ; } /// Set the magnetic field for the MFT - const float getBz() const { return mBZField; } - const int getSignBz() const { return std::copysign(1, mBZField); } - /// Return true if the field is switched ON - const bool isFieldON() { return mIsFieldON; } - - bool extrapToZ(TrackParamMFT* TrackParamMFT, double zEnd, bool isFieldON = true); - void extrapToZCov(TrackParamMFT* TrackParamMFT, double zEnd, bool updatePropagator = false, bool isFieldON = true); - void linearExtrapToZ(TrackParamMFT* TrackParamMFT, double zEnd); - void linearExtrapToZCov(TrackParamMFT* TrackParamMFT, double zEnd, bool updatePropagator); - void quadraticExtrapToZ(TrackParamMFT* TrackParamMFT, double zEnd); - void quadraticExtrapToZCov(TrackParamMFT* TrackParamMFT, double zEnd, bool updatePropagator); - void helixExtrapToZ(TrackParamMFT* TrackParamMFT, double zEnd); - void helixExtrapToZCov(TrackParamMFT* TrackParamMFT, double zEnd, bool updatePropagator); - void addMCSEffect(TrackParamMFT* TrackParamMFT, double dZ, double x0); - - private: - Float_t mBZField; // kiloGauss. - bool mIsFieldON = false; ///< true if the field is switched ON -}; - -} // namespace mft -} // namespace o2 - -#endif // ALICEO2_MFT_TRACKEXTRAP_H_ diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackFitter.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackFitter.h index 68702029abd2c..91601db595087 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackFitter.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackFitter.h @@ -13,17 +13,13 @@ /// /// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS -#ifndef ALICEO2_MFT_TRACKFITTER_H_ -#define ALICEO2_MFT_TRACKFITTER_H_ +#ifndef ALICEO2_MFT_TrackFitter_H_ +#define ALICEO2_MFT_TrackFitter_H_ #include "MFTTracking/Cluster.h" #include "MFTTracking/TrackCA.h" -#include "MFTTracking/FitterTrackMFT.h" -#include "MFTTracking/TrackParamMFT.h" -#include "MFTTracking/TrackExtrap.h" #include "MFTTracking/MFTTrackingParam.h" #include "DataFormatsMFT/TrackMFT.h" - #include <TLinearFitter.h> #include <list> @@ -35,6 +31,10 @@ namespace mft /// Class to fit a track to a set of clusters class TrackFitter { + + using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>; + using SMatrix5 = ROOT::Math::SVector<Double_t, 5>; + public: TrackFitter() = default; ~TrackFitter() = default; @@ -46,40 +46,29 @@ class TrackFitter void setBz(float bZ); - /// Enable/disable the smoother (and the saving of related parameters) - void smoothTracks(bool smooth) { mSmooth = smooth; } - /// Return the smoother enable/disable flag - bool isSmootherEnabled() { return mSmooth; } - - bool fit(FitterTrackMFT& track, bool smooth = true, bool finalize = true, - std::list<TrackParamMFT>::reverse_iterator* itStartingParam = nullptr); - - bool runKalmanFilter(TrackParamMFT& trackParam); + bool initTrack(TrackLTF& track, bool outward = false); + bool fit(TrackLTF& track, bool outward = false); /// Return the maximum chi2 above which the track can be considered as abnormal static constexpr double getMaxChi2() { return SMaxChi2; } private: - void initTrack(const Cluster& cl, TrackParamMFT& param); - bool addCluster(const TrackParamMFT& startingParam, const Cluster& cl, TrackParamMFT& param); - bool smoothTrack(FitterTrackMFT& track, bool finalize); - bool runSmoother(const TrackParamMFT& previousParam, TrackParamMFT& param); + bool computeCluster(TrackLTF& track, int cluster); + Float_t mBZField; // kiloGauss. static constexpr double SMaxChi2 = 2.e10; ///< maximum chi2 above which the track can be considered as abnormal /// default layer thickness in X0 for reconstruction //FIXME: set values for the MFT static constexpr double SLayerThicknessInX0[10] = {0.065, 0.065, 0.075, 0.075, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035}; - bool mSmooth = false; ///< switch ON/OFF the smoother bool mFieldON = true; - o2::mft::TrackExtrap mTrackExtrap; }; // Functions to estimate momentum and charge from track curvature -Double_t invQPtFromParabola(const FitterTrackMFT& track, double bFieldZ, Double_t& chi2); -Double_t QuadraticRegression(Int_t nVal, Double_t* xVal, Double_t* yVal, Double_t& p0, Double_t& p1, Double_t& p2); +Double_t invQPtFromFCF(const TrackLTF& track, Double_t bFieldZ, Double_t& chi2); +Bool_t LinearRegression(Int_t nVal, Double_t* xVal, Double_t* yVal, Double_t* yErr, Double_t& a, Double_t& ae, Double_t& b, Double_t& be); } // namespace mft } // namespace o2 -#endif // ALICEO2_MFT_TRACKFITTER_H_ +#endif // ALICEO2_MFT_TrackFitter_H_ diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackParamMFT.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackParamMFT.h deleted file mode 100644 index 64c63a6076a81..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackParamMFT.h +++ /dev/null @@ -1,182 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackParamMFT.h -/// \brief Definition of the MFT track parameters for internal use -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#ifndef ALICEO2_MFT_TRACKPARAMMFT_H_ -#define ALICEO2_MFT_TRACKPARAMMFT_H_ - -#include <TMatrixD.h> -#include <TMath.h> - -#include "MFTBase/Constants.h" -#include "MFTTracking/Cluster.h" - -namespace o2 -{ -namespace mft -{ - - -/// track parameters for internal use -class TrackParamMFT -{ - public: - TrackParamMFT() = default; - ~TrackParamMFT() = default; - - TrackParamMFT(const TrackParamMFT& tp); - TrackParamMFT& operator=(const TrackParamMFT& tp); - TrackParamMFT(TrackParamMFT&&) = delete; - TrackParamMFT& operator=(TrackParamMFT&&) = delete; - - /// return Z coordinate (cm) - Double_t getZ() const { return mZ; } - /// set Z coordinate (cm) - void setZ(Double_t z) { mZ = z; } - Double_t getX() const { return mParameters(0, 0); } - void setX(Double_t x) { mParameters(0, 0) = x; } - - Double_t getY() const { return mParameters(1, 0); } - void setY(Double_t y) { mParameters(1, 0) = y; } - - void setPhi(Double_t phi) { mParameters(2, 0) = phi; } - Double_t getPhi() const { return mParameters(2, 0); } - - void setTanl(Double_t tanl) { mParameters(3, 0) = tanl; } - Double_t getTanl() const { return mParameters(3, 0); } - - void setInvQPt(Double_t invqpt) { mParameters(4, 0) = invqpt; } - Double_t getInvQPt() const { return mParameters(4, 0); } // return Inverse charged pt - Double_t getPt() const { return TMath::Abs(1.f / mParameters(4, 0)); } - Double_t getInvPt() const { return TMath::Abs(mParameters(4, 0)); } - - Double_t getPx() const { return TMath::Cos(getPhi()) * getPt(); } // return px - Double_t getInvPx() const { return 1. / getPx(); } // return invpx - - Double_t getPy() const { return TMath::Sin(getPhi()) * getPt(); } // return py - Double_t getInvPy() const { return 1. / getPx(); } // return invpy - - Double_t getPz() const { return getTanl() * getPt(); } // return pz - Double_t getInvPz() const { return 1. / getPz(); } // return invpz - - Double_t getP() const { return getPt() * TMath::Sqrt(1. + getTanl() * getTanl()); } // return total momentum - Double_t getInverseMomentum() const { return 1.f / getP(); } - - /// return the charge (assumed forward motion) - Double_t getCharge() const { return TMath::Sign(1., mParameters(4, 0)); } - /// set the charge (assumed forward motion) - void setCharge(Double_t charge) - { - if (charge * mParameters(4, 0) < 0.) - mParameters(4, 0) *= -1.; - } - - /// return track parameters - const TMatrixD& getParameters() const { return mParameters; } - /// set track parameters - void setParameters(const TMatrixD& parameters) { mParameters = parameters; } - /// add track parameters - void addParameters(const TMatrixD& parameters) { mParameters += parameters; } - - /// return kTRUE if the covariance matrix exist, kFALSE if not - Bool_t hasCovariances() const { return (mCovariances) ? kTRUE : kFALSE; } - - const TMatrixD& getCovariances() const; - void setCovariances(const TMatrixD& covariances); - void setCovariances(const Double_t matrix[5][5]); - void setVariances(const Double_t matrix[5][5]); - void deleteCovariances(); - - const TMatrixD& getPropagator() const; - void resetPropagator(); - void updatePropagator(const TMatrixD& propagator); - - const TMatrixD& getExtrapParameters() const; - void setExtrapParameters(const TMatrixD& parameters); - - const TMatrixD& getExtrapCovariances() const; - void setExtrapCovariances(const TMatrixD& covariances); - - const TMatrixD& getSmoothParameters() const; - void setSmoothParameters(const TMatrixD& parameters); - - const TMatrixD& getSmoothCovariances() const; - void setSmoothCovariances(const TMatrixD& covariances); - - /// get pointer to associated cluster - const Cluster* getClusterPtr() const { return mClusterPtr; } - /// set pointer to associated cluster - void setClusterPtr(const Cluster* cluster) { mClusterPtr = cluster; } - - /// return true if the associated cluster can be removed from the track it belongs to - Bool_t isRemovable() const { return mRemovable; } - /// set the flag telling whether the associated cluster can be removed from the track it belongs to or not - void setRemovable(Bool_t removable) { mRemovable = removable; } - - /// return the chi2 of the track when the associated cluster was attached - Double_t getTrackChi2() const { return mTrackChi2; } - /// set the chi2 of the track when the associated cluster was attached - void setTrackChi2(Double_t chi2) { mTrackChi2 = chi2; } - /// return the local chi2 of the associated cluster with respect to the track - Double_t getLocalChi2() const { return mLocalChi2; } - /// set the local chi2 of the associated cluster with respect to the track - void setLocalChi2(Double_t chi2) { mLocalChi2 = chi2; } - - Bool_t isCompatibleTrackParamMFT(const TrackParamMFT& TrackParamMFT, Double_t sigma2Cut, Double_t& normChi2) const; - - void print() const; - - void clear(); - - private: - Double_t mZ = 0.; ///< Z coordinate (cm) - - /// Track parameters ordered as follow: <pre> - /// X = X coordinate (cm) - /// Y = Y coordinate (cm) - /// PHI = azimutal angle - /// TANL = tangent of \lambda (dip angle) - /// INVQPT = Inverse transverse momentum (GeV/c ** -1) times charge (assumed forward motion) </pre> - TMatrixD mParameters{5, 1}; ///< \brief Track parameters - - /// Covariance matrix of track parameters, ordered as follows: <pre> - /// <X,X> <Y,X> <PHI,X> <TANL,X> <INVQPT,X> - /// <X,Y> <Y,Y> <PHI,Y> <TANL,Y> <INVQPT,Y> - /// <X,PHI> <Y,PHI> <PHI,PHI> <TANL,PHI> <INVQPT,PHI> - /// <X,TANL> <Y,TANL> <PHI,TANL> <TANL,TANL> <INVQPT,TANL> - /// <X,INVQPT> <Y,INVQPT> <PHI,INVQPT> <TANL,INVQPT> <INVQPT,INVQPT> </pre> - mutable std::unique_ptr<TMatrixD> mCovariances{}; ///< \brief Covariance matrix of track parameters - - /// Jacobian used to extrapolate the track parameters and covariances to the actual z position - mutable std::unique_ptr<TMatrixD> mPropagator{}; - /// Track parameters extrapolated to the actual z position (not filtered by Kalman) - mutable std::unique_ptr<TMatrixD> mExtrapParameters{}; - /// Covariance matrix extrapolated to the actual z position (not filtered by Kalman) - mutable std::unique_ptr<TMatrixD> mExtrapCovariances{}; - - mutable std::unique_ptr<TMatrixD> mSmoothParameters{}; ///< Track parameters obtained using smoother - mutable std::unique_ptr<TMatrixD> mSmoothCovariances{}; ///< Covariance matrix obtained using smoother - - const Cluster* mClusterPtr = nullptr; ///< Pointer to the associated cluster if any - - Bool_t mRemovable = false; ///< kTRUE if the associated cluster can be removed from the track it belongs to - - Double_t mTrackChi2 = 0.; ///< Chi2 of the track when the associated cluster was attached - Double_t mLocalChi2 = 0.; ///< Local chi2 of the associated cluster with respect to the track -}; - -} // namespace mft -} // namespace o2 - -#endif // ALICEO2_MFT_TRACKPARAMMFT_H_ diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Tracker.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Tracker.h index 189b8ec2b79c5..e67ec732334f8 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Tracker.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/Tracker.h @@ -18,12 +18,14 @@ #include "MFTTracking/ROframe.h" #include "MFTTracking/TrackFitter.h" #include "MFTTracking/Cluster.h" +#include "MFTTracking/TrackerConfig.h" #include "MathUtils/Utils.h" -#include "MathUtils/Cartesian2D.h" +#include "MathUtils/Cartesian.h" #include "DataFormatsMFT/TrackMFT.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsParameters/GRPObject.h" namespace o2 { @@ -32,7 +34,7 @@ namespace mft class TrackLTF; -class Tracker +class Tracker : public TrackerConfig { public: @@ -42,157 +44,109 @@ class Tracker Tracker(const Tracker&) = delete; Tracker& operator=(const Tracker&) = delete; - void setBz(Float_t bz) { mBz = bz; } + void setBz(Float_t bz); const Float_t getBz() const { return mBz; } - std::vector<TrackMFT>& getTracks(); - std::vector<TrackLTF>& getTracksLTF(); - o2::dataformats::MCTruthContainer<MCCompLabel>& getTrackLabels(); + std::vector<TrackMFTExt>& getTracks() { return mTracks; } + std::vector<TrackLTF>& getTracksLTF() { return mTracksLTF; } + o2::dataformats::MCTruthContainer<MCCompLabel>& getTrackLabels() { return mTrackLabels; } void clustersToTracks(ROframe&, std::ostream& = std::cout); + template <class T> + void computeTracksMClabels(const T&); + void setROFrame(std::uint32_t f) { mROFrame = f; } std::uint32_t getROFrame() const { return mROFrame; } + void initialize(); + void initConfig(const MFTTrackingParam& trkParam); + private: void findTracks(ROframe&); void findTracksLTF(ROframe&); void findTracksCA(ROframe&); - void computeCells(ROframe&); - void computeCellsInRoad(Road&); - void runForwardInRoad(ROframe&); + void computeCellsInRoad(ROframe&); + void runForwardInRoad(); void runBackwardInRoad(ROframe&); - void updateCellStatusInRoad(Road&); + void updateCellStatusInRoad(); - const Int_t isDiskFace(Int_t layer) const; + bool fitTracks(ROframe&); + const Int_t isDiskFace(Int_t layer) const { return (layer % 2); } const Float_t getDistanceToSeed(const Cluster&, const Cluster&, const Cluster&) const; - void getRPhiProjectionBin(const Cluster&, const Int_t, const Int_t, Int_t&, Int_t&) const; - Bool_t getBinClusterRange(const ROframe&, const Int_t, const Int_t, Int_t&, Int_t&) const; - const Float_t getCellDeviation(const ROframe&, const Cell&, const Cell&) const; - const Bool_t getCellsConnect(const ROframe&, const Cell&, const Cell&) const; - const Float_t getCellChisquare(ROframe&, const Cell&) const; - const Bool_t addCellToCurrentTrackCA(const Int_t, const Int_t, ROframe&); - - const Bool_t LinearRegression(Int_t, Float_t*, Float_t*, Float_t*, Float_t&, Float_t&, Float_t&, Float_t&, Float_t&, Int_t skip = -1) const; + void getBinClusterRange(const ROframe&, const Int_t, const Int_t, Int_t&, Int_t&) const; + const Float_t getCellDeviation(const Cell&, const Cell&) const; + const Bool_t getCellsConnect(const Cell&, const Cell&) const; + void addCellToCurrentTrackCA(const Int_t, const Int_t, ROframe&); + void addCellToCurrentRoad(ROframe&, const Int_t, const Int_t, const Int_t, const Int_t, Int_t&); Float_t mBz = 5.f; std::uint32_t mROFrame = 0; - std::vector<TrackMFT> mTracks; + std::vector<TrackMFTExt> mTracks; std::vector<TrackLTF> mTracksLTF; std::vector<Cluster> mClusters; o2::dataformats::MCTruthContainer<MCCompLabel> mTrackLabels; + std::unique_ptr<o2::mft::TrackFitter> mTrackFitter = nullptr; Int_t mMaxCellLevel = 0; bool mUseMC = false; -}; - -inline std::vector<TrackMFT>& Tracker::getTracks() -{ - return mTracks; -} - -inline std::vector<TrackLTF>& Tracker::getTracksLTF() -{ - return mTracksLTF; -} -inline o2::dataformats::MCTruthContainer<MCCompLabel>& Tracker::getTrackLabels() -{ - return mTrackLabels; -} + std::array<std::array<std::array<std::vector<Int_t>, constants::index_table::MaxRPhiBins>, (constants::mft::LayersNumber - 1)>, (constants::mft::LayersNumber - 1)> mBinsS; + std::array<std::array<std::array<std::vector<Int_t>, constants::index_table::MaxRPhiBins>, (constants::mft::LayersNumber - 1)>, (constants::mft::LayersNumber - 1)> mBins; + + /// helper to store points of a track candidate + struct TrackElement { + TrackElement() = default; + TrackElement(Int_t la, Int_t id) + { + layer = la; + idInLayer = id; + }; + Int_t layer; + Int_t idInLayer; + }; + + /// current road for CA algorithm + Road mRoad; +}; +//_________________________________________________________________________________________________ inline const Float_t Tracker::getDistanceToSeed(const Cluster& cluster1, const Cluster& cluster2, const Cluster& cluster) const { // the seed is between "cluster1" and "cluster2" and cuts the plane // of the "cluster" at a distance dR from it - Float_t dxSeed, dySeed, dzSeed, dz, dR, xSeed, ySeed; - dxSeed = cluster2.xCoordinate - cluster1.xCoordinate; - dySeed = cluster2.yCoordinate - cluster1.yCoordinate; - dzSeed = cluster2.zCoordinate - cluster1.zCoordinate; - dz = cluster.zCoordinate - cluster1.zCoordinate; - xSeed = cluster1.xCoordinate + dxSeed * dz / dzSeed; - ySeed = cluster1.yCoordinate + dySeed * dz / dzSeed; - dR = std::sqrt((cluster.xCoordinate - xSeed) * (cluster.xCoordinate - xSeed) + (cluster.yCoordinate - ySeed) * (cluster.yCoordinate - ySeed)); - return dR; -} - -inline void Tracker::getRPhiProjectionBin(const Cluster& cluster1, const Int_t layer1, const Int_t layer, Int_t& binR_proj, Int_t& binPhi_proj) const -{ - Float_t dz, x_proj, y_proj, r_proj, phi_proj; - dz = constants::mft::LayerZCoordinate()[layer] - constants::mft::LayerZCoordinate()[layer1]; - x_proj = cluster1.xCoordinate + dz * cluster1.xCoordinate * constants::mft::InverseLayerZCoordinate()[layer1]; - y_proj = cluster1.yCoordinate + dz * cluster1.yCoordinate * constants::mft::InverseLayerZCoordinate()[layer1]; - auto clsPoint2D = Point2D<Float_t>(x_proj, y_proj); - r_proj = clsPoint2D.R(); - phi_proj = clsPoint2D.Phi(); - o2::utils::BringTo02PiGen(phi_proj); - binR_proj = constants::index_table::getRBinIndex(r_proj); - binPhi_proj = constants::index_table::getPhiBinIndex(phi_proj); - return; + Float_t dxSeed, dySeed, dzSeed, invdzSeed, dz, dR2, xSeed, ySeed; + dxSeed = cluster2.getX() - cluster1.getX(); + dySeed = cluster2.getY() - cluster1.getY(); + dzSeed = cluster2.getZ() - cluster1.getZ(); + dz = cluster.getZ() - cluster1.getZ(); + invdzSeed = dz / dzSeed; + xSeed = cluster1.getX() + dxSeed * invdzSeed; + ySeed = cluster1.getY() + dySeed * invdzSeed; + dR2 = (cluster.getX() - xSeed) * (cluster.getX() - xSeed) + (cluster.getY() - ySeed) * (cluster.getY() - ySeed); + return dR2; } -inline Bool_t Tracker::getBinClusterRange(const ROframe& event, const Int_t layer, const Int_t bin, Int_t& clsMinIndex, Int_t& clsMaxIndex) const +//_________________________________________________________________________________________________ +inline void Tracker::getBinClusterRange(const ROframe& event, const Int_t layer, const Int_t bin, Int_t& clsMinIndex, Int_t& clsMaxIndex) const { - const auto pair2 = event.getClusterBinIndexRange(layer).find(bin); - if (pair2 == event.getClusterBinIndexRange(layer).end()) - return kFALSE; - Int_t binIndex = pair2->first; - // get the range in ordered cluster index within this bin - std::pair<Int_t, Int_t> pair1 = pair2->second; - clsMinIndex = pair1.first; - clsMaxIndex = pair1.second; - return kTRUE; + const auto& pair = event.getClusterBinIndexRange(layer)[bin]; + clsMinIndex = pair.first; + clsMaxIndex = pair.second; } -inline const Int_t Tracker::isDiskFace(Int_t layer) const +//_________________________________________________________________________________________________ +inline const Float_t Tracker::getCellDeviation(const Cell& cell1, const Cell& cell2) const { - return (layer % 2); -} - -inline const Float_t Tracker::getCellDeviation(const ROframe& event, const Cell& cell1, const Cell& cell2) const -{ - Int_t cell1layer1 = cell1.getFirstLayerId(); - Int_t cell1layer2 = cell1.getSecondLayerId(); - - Int_t cell2layer1 = cell2.getFirstLayerId(); - Int_t cell2layer2 = cell2.getSecondLayerId(); - - Int_t cell1cls1 = cell1.getFirstClusterIndex(); - Int_t cell1cls2 = cell1.getSecondClusterIndex(); + Float_t cell1dx = cell1.getX2() - cell1.getX1(); + Float_t cell1dy = cell1.getY2() - cell1.getY1(); + Float_t cell1dz = cell1.getZ2() - cell1.getZ1(); - Int_t cell2cls1 = cell2.getFirstClusterIndex(); - Int_t cell2cls2 = cell2.getSecondClusterIndex(); - - auto cluster11 = event.getClustersInLayer(cell1layer1)[cell1cls1]; - auto cluster12 = event.getClustersInLayer(cell1layer2)[cell1cls2]; - auto cluster21 = event.getClustersInLayer(cell2layer1)[cell2cls1]; - auto cluster22 = event.getClustersInLayer(cell2layer2)[cell2cls2]; - - Float_t cell1x1 = cluster11.xCoordinate; - Float_t cell1y1 = cluster11.yCoordinate; - Float_t cell1z1 = cluster11.zCoordinate; - - Float_t cell1x2 = cluster12.xCoordinate; - Float_t cell1y2 = cluster12.yCoordinate; - Float_t cell1z2 = cluster12.zCoordinate; - - Float_t cell2x1 = cluster21.xCoordinate; - Float_t cell2y1 = cluster21.yCoordinate; - Float_t cell2z1 = cluster21.zCoordinate; - - Float_t cell2x2 = cluster22.xCoordinate; - Float_t cell2y2 = cluster22.yCoordinate; - Float_t cell2z2 = cluster22.zCoordinate; - - Float_t cell1dx = cell1x2 - cell1x1; - Float_t cell1dy = cell1y2 - cell1y1; - Float_t cell1dz = cell1z2 - cell1z1; - - Float_t cell2dx = cell2x2 - cell2x1; - Float_t cell2dy = cell2y2 - cell2y1; - Float_t cell2dz = cell2z2 - cell2z1; + Float_t cell2dx = cell2.getX2() - cell2.getX1(); + Float_t cell2dy = cell2.getY2() - cell2.getY1(); + Float_t cell2dz = cell2.getZ2() - cell2.getZ1(); Float_t cell1mod = std::sqrt(cell1dx * cell1dx + cell1dy * cell1dy + cell1dz * cell1dz); Float_t cell2mod = std::sqrt(cell2dx * cell2dx + cell2dy * cell2dy + cell2dz * cell2dz); @@ -202,59 +156,64 @@ inline const Float_t Tracker::getCellDeviation(const ROframe& event, const Cell& return std::acos(cosAngle); } -inline const Bool_t Tracker::getCellsConnect(const ROframe& event, const Cell& cell1, const Cell& cell2) const +//_________________________________________________________________________________________________ +inline const Bool_t Tracker::getCellsConnect(const Cell& cell1, const Cell& cell2) const { - Int_t cell1layer1 = cell1.getFirstLayerId(); - Int_t cell1layer2 = cell1.getSecondLayerId(); - - Int_t cell2layer1 = cell2.getFirstLayerId(); - Int_t cell2layer2 = cell2.getSecondLayerId(); - - Int_t cell1cls1 = cell1.getFirstClusterIndex(); - Int_t cell1cls2 = cell1.getSecondClusterIndex(); - - Int_t cell2cls1 = cell2.getFirstClusterIndex(); - Int_t cell2cls2 = cell2.getSecondClusterIndex(); - - auto cluster11 = event.getClustersInLayer(cell1layer1)[cell1cls1]; - auto cluster12 = event.getClustersInLayer(cell1layer2)[cell1cls2]; - auto cluster21 = event.getClustersInLayer(cell2layer1)[cell2cls1]; - auto cluster22 = event.getClustersInLayer(cell2layer2)[cell2cls2]; - - Float_t cell1x1 = cluster11.xCoordinate; - Float_t cell1y1 = cluster11.yCoordinate; - //Float_t cell1z1 = cluster11.zCoordinate; - - Float_t cell1x2 = cluster12.xCoordinate; - Float_t cell1y2 = cluster12.yCoordinate; - //Float_t cell1z2 = cluster12.zCoordinate; - - Float_t cell2x1 = cluster21.xCoordinate; - Float_t cell2y1 = cluster21.yCoordinate; - //Float_t cell2z1 = cluster21.zCoordinate; - - Float_t cell2x2 = cluster22.xCoordinate; - Float_t cell2y2 = cluster22.yCoordinate; - //Float_t cell2z2 = cluster22.zCoordinate; - - Float_t cell1dx = cell1x2 - cell1x1; - Float_t cell1dy = cell1y2 - cell1y1; - //Float_t cell1dz = cell1z2 - cell1z1; - - Float_t cell2dx = cell2x2 - cell2x1; - Float_t cell2dy = cell2y2 - cell2y1; - //Float_t cell2dz = cell2z2 - cell2z1; - + Float_t cell1x2 = cell1.getX2(); + Float_t cell1y2 = cell1.getY2(); + Float_t cell2x1 = cell2.getX1(); + Float_t cell2y1 = cell2.getY1(); Float_t dx = cell1x2 - cell2x1; Float_t dy = cell1y2 - cell2y1; - Float_t dr = std::sqrt(dx * dx + dy * dy); + Float_t dr2 = dx * dx + dy * dy; - if (dr > constants::mft::Resolution) { + if (dr2 > (constants::mft::Resolution * constants::mft::Resolution)) { return kFALSE; } return kTRUE; } +//_________________________________________________________________________________________________ +template <class T> +inline void Tracker::computeTracksMClabels(const T& tracks) +{ + /// Moore's Voting Algorithm + for (auto& track : tracks) { + MCCompLabel maxOccurrencesValue{-1, -1, -1, false}; + int count{0}; + bool isFakeTrack{false}; + auto nClusters = track.getNumberOfPoints(); + for (int iCluster = 0; iCluster < nClusters; ++iCluster) { + const MCCompLabel& currentLabel = track.getMCCompLabels()[iCluster]; + if (currentLabel == maxOccurrencesValue) { + ++count; + } else { + if (count != 0) { // only in the first iteration count can be 0 at this point + --count; + } + if (count == 0) { + maxOccurrencesValue = currentLabel; + count = 1; + } + } + } + count = 0; + for (int iCluster = 0; iCluster < nClusters; ++iCluster) { + if (track.getMCCompLabels()[iCluster] == maxOccurrencesValue) { + count++; + } + } + + auto labelratio = 1.0 * count / nClusters; + if (labelratio >= 0.8) { + } else { + isFakeTrack = true; + maxOccurrencesValue.setFakeFlag(); + } + mTrackLabels.addElement(mTrackLabels.getIndexedSize(), maxOccurrencesValue); + } +} + } // namespace mft } // namespace o2 diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackerConfig.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackerConfig.h new file mode 100644 index 0000000000000..8b75506dd5c21 --- /dev/null +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/TrackerConfig.h @@ -0,0 +1,83 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GeometryTGeo.h +/// \brief Definition of the GeometryTGeo class +/// \author bogdan.vulpescu@clermont.in2p3.fr - adapted from ITS, 21.09.2017 + +#ifndef ALICEO2_MFT_TRACKERCONFIG_H_ +#define ALICEO2_MFT_TRACKERCONFIG_H_ + +#include "MFTTracking/Constants.h" +#include "MFTTracking/MFTTrackingParam.h" + +namespace o2 +{ +namespace mft +{ +class TrackerConfig +{ + public: + TrackerConfig(); + TrackerConfig(const TrackerConfig& conf) = default; + TrackerConfig& operator=(const TrackerConfig& conf) = default; + + void initialize(const MFTTrackingParam& trkParam); + + const Int_t getRBinIndex(const Float_t r) const; + const Int_t getPhiBinIndex(const Float_t phi) const; + const Int_t getBinIndex(const Int_t rIndex, const Int_t phiIndex) const; + + // tracking configuration parameters + Int_t mMinTrackPointsLTF = 5; + Int_t mMinTrackPointsCA = 4; + Int_t mMinTrackStationsLTF = 4; + Int_t mMinTrackStationsCA = 4; + Float_t mLTFclsRCut = 0.0100; + Float_t mLTFclsR2Cut = 0.0100 * 0.0100; + Float_t mROADclsRCut = 0.0400; + Float_t mROADclsR2Cut = 0.0400 * 0.0400; + Int_t mLTFseed2BinWin = 3; + Int_t mLTFinterBinWin = 3; + Int_t mRBins = 50; + Int_t mPhiBins = 50; + Int_t mRPhiBins = 50 * 50; + Float_t mRBinSize = (constants::index_table::RMax - constants::index_table::RMin) / 50.; + Float_t mPhiBinSize = constants::index_table::PhiMax / 50.; + Float_t mInverseRBinSize = 50. / (constants::index_table::RMax - constants::index_table::RMin); + Float_t mInversePhiBinSize = 50. / constants::index_table::PhiMax; + + private: + ClassDefNV(TrackerConfig, 1); +}; + +inline const Int_t TrackerConfig::getRBinIndex(const Float_t r) const +{ + return (Int_t)((r - constants::index_table::RMin) * mInverseRBinSize); +} + +inline const Int_t TrackerConfig::getPhiBinIndex(const Float_t phi) const +{ + return (Int_t)((phi - constants::index_table::PhiMin) * mInversePhiBinSize); +} + +inline const Int_t TrackerConfig::getBinIndex(const Int_t rIndex, const Int_t phiIndex) const +{ + if (0 <= rIndex && rIndex < mRBins && + 0 <= phiIndex && phiIndex < mPhiBins) { + return (phiIndex * mRBins + rIndex); + } + return (mRBins * mPhiBins); +} + +} // namespace mft +} // namespace o2 + +#endif diff --git a/Detectors/ITSMFT/MFT/tracking/src/Cluster.cxx b/Detectors/ITSMFT/MFT/tracking/src/Cluster.cxx index 244e7ea521f9f..ac21ab2e30935 100644 --- a/Detectors/ITSMFT/MFT/tracking/src/Cluster.cxx +++ b/Detectors/ITSMFT/MFT/tracking/src/Cluster.cxx @@ -12,10 +12,9 @@ /// #include "MFTTracking/Cluster.h" -#include "MFTTracking/IndexTableUtils.h" #include "MathUtils/Utils.h" -#include "MathUtils/Cartesian2D.h" +#include "MathUtils/Cartesian.h" namespace o2 { @@ -23,34 +22,16 @@ namespace mft { Cluster::Cluster(const Float_t x, const Float_t y, const Float_t z, const Int_t index) - : xCoordinate{x}, - yCoordinate{y}, - zCoordinate{z}, + : BaseCluster(1, x, y, z), phiCoordinate{0.}, rCoordinate{0.}, clusterId{index}, indexTableBin{0} { - auto clsPoint2D = Point2D<Float_t>(x, y); + auto clsPoint2D = math_utils::Point2D<Float_t>(x, y); rCoordinate = clsPoint2D.R(); phiCoordinate = clsPoint2D.Phi(); - o2::utils::BringTo02PiGen(phiCoordinate); -} - -Cluster::Cluster(const Int_t layerIndex, const Cluster& other) - : xCoordinate{other.xCoordinate}, - yCoordinate{other.yCoordinate}, - zCoordinate{other.zCoordinate}, - phiCoordinate{0.}, - rCoordinate{0.}, - clusterId{other.clusterId}, - indexTableBin{index_table_utils::getBinIndex(index_table_utils::getRBinIndex(layerIndex, rCoordinate), - index_table_utils::getPhiBinIndex(phiCoordinate))} -{ - auto clsPoint2D = Point2D<Float_t>(other.xCoordinate, other.yCoordinate); - rCoordinate = clsPoint2D.R(); - phiCoordinate = clsPoint2D.Phi(); - o2::utils::BringTo02PiGen(phiCoordinate); + o2::math_utils::bringTo02PiGen(phiCoordinate); } } // namespace mft diff --git a/Detectors/ITSMFT/MFT/tracking/src/FitterTrackMFT.cxx b/Detectors/ITSMFT/MFT/tracking/src/FitterTrackMFT.cxx deleted file mode 100644 index 47a83a4a17814..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/src/FitterTrackMFT.cxx +++ /dev/null @@ -1,137 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FitterTrackMFT.cxx -/// \brief Implementation of the MFT track for internal use -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#include "MFTTracking/FitterTrackMFT.h" - -#include <iostream> - -#include <FairMQLogger.h> - -namespace o2 -{ -namespace mft -{ - -using namespace std; - -//__________________________________________________________________________ -FitterTrackMFT::FitterTrackMFT(const FitterTrackMFT& track) - : mParamAtVertex(track.mParamAtVertex), - mParamAtClusters(track.mParamAtClusters), - mCurrentParam(nullptr), - mCurrentLayer(-1), - mConnected(track.mConnected), - mRemovable(track.mRemovable), - mMCCompLabels(track.mMCCompLabels), - mNPoints(track.mNPoints) - -{ -} - -//__________________________________________________________________________ -TrackParamMFT& FitterTrackMFT::createParamAtCluster(const Cluster& cluster) -{ - /// Create the object to hold the track parameters at the given cluster - /// Only the z position of the track is set to the cluster z position - /// Keep the internal list of track parameters at clusters sorted in z - /// Return a reference to the newly created parameters - - // find the iterator before which the new element will be constructed - auto itParam = mParamAtClusters.begin(); - for (; itParam != mParamAtClusters.end(); ++itParam) { - if (cluster.zCoordinate >= itParam->getZ()) { - break; - } - } - - // add the new track parameters - mParamAtClusters.emplace(itParam); - --itParam; - itParam->setX(cluster.xCoordinate); - itParam->setY(cluster.yCoordinate); - itParam->setZ(cluster.zCoordinate); - itParam->setClusterPtr(&cluster); - mNPoints++; - return *itParam; -} - -//__________________________________________________________________________ -void FitterTrackMFT::addParamAtCluster(const TrackParamMFT& param) -{ - /// Add a copy of the given track parameters in the internal list - /// The parameters must be associated with a cluster - /// Keep the internal list of track parameters sorted in clusters z - - const Cluster* cluster = param.getClusterPtr(); - if (cluster == nullptr) { - LOG(ERROR) << "The TrackParamMFT must be associated with a cluster --> not added"; - return; - } - - // find the iterator before which the new element will be constructed - auto itParam = mParamAtClusters.begin(); - for (; itParam != mParamAtClusters.end(); ++itParam) { - if (cluster->zCoordinate >= itParam->getZ()) { - break; - } - } - - // add the new track parameters - mParamAtClusters.emplace(itParam, param); -} - -//__________________________________________________________________________ -bool FitterTrackMFT::isBetter(const FitterTrackMFT& track) const -{ - /// Return true if this track is better than the one given as parameter - /// It is better if it has more clusters or a better chi2 in case of equality - int nCl1 = this->getNClusters(); - int nCl2 = track.getNClusters(); - return ((nCl1 > nCl2) || ((nCl1 == nCl2) && (this->first().getTrackChi2() < track.first().getTrackChi2()))); -} - -//__________________________________________________________________________ -void FitterTrackMFT::tagRemovableClusters(uint8_t requestedStationMask) -{ - /// Identify clusters that can be removed from the track, - /// with the only requirements to have at least 1 cluster per requested station - /// and at least 2 chambers over 4 in stations 4 & 5 that contain cluster(s) -} - -//__________________________________________________________________________ -void FitterTrackMFT::setCurrentParam(const TrackParamMFT& param, int chamber) -{ - /// set the current track parameters and the associated chamber - if (mCurrentParam) { - *mCurrentParam = param; - } else { - mCurrentParam = std::make_unique<TrackParamMFT>(param); - } - mCurrentParam->setClusterPtr(nullptr); - mCurrentLayer = chamber; -} - -//__________________________________________________________________________ -TrackParamMFT& FitterTrackMFT::getCurrentParam() -{ - /// get a reference to the current track parameters. Create dummy parameters if needed - if (!mCurrentParam) { - mCurrentParam = std::make_unique<TrackParamMFT>(); - } - return *mCurrentParam; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/MFT/tracking/src/IOUtils.cxx index 68dd0dd85bf82..5d25a724cfa7b 100644 --- a/Detectors/ITSMFT/MFT/tracking/src/IOUtils.cxx +++ b/Detectors/ITSMFT/MFT/tracking/src/IOUtils.cxx @@ -25,7 +25,7 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "MathUtils/Utils.h" -#include "MathUtils/Cartesian2D.h" +#include "MathUtils/Cartesian.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -35,23 +35,25 @@ namespace mft { int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, gsl::span<const itsmft::CompClusterExt> clusters, gsl::span<const unsigned char>::iterator& pattIt, const itsmft::TopologyDictionary& dict, - const dataformats::MCTruthContainer<MCCompLabel>* mcLabels) + const dataformats::MCTruthContainer<MCCompLabel>* mcLabels, const o2::mft::Tracker* tracker) { event.clear(); GeometryTGeo* geom = GeometryTGeo::Instance(); - geom->fillMatrixCache(utils::bit2Mask(TransformType::T2L, TransformType::L2G)); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); int clusterId{0}; auto first = rof.getFirstEntry(); auto clusters_in_frame = rof.getROFData(clusters); for (auto& c : clusters_in_frame) { int layer = geom->getLayer(c.getSensorID()); - auto pattID = c.getPatternID(); - Point3D<float> locXYZ; - float sigmaY2 = ioutils::DefClusError2Row, sigmaZ2 = ioutils::DefClusError2Col, sigmaYZ = 0; //Dummy COG errors (about half pixel size) + o2::math_utils::Point3D<float> locXYZ; + float sigmaX2 = ioutils::DefClusError2Row, sigmaY2 = ioutils::DefClusError2Col; //Dummy COG errors (about half pixel size) if (pattID != itsmft::CompCluster::InvalidPatternID) { - sigmaY2 = dict.getErr2X(pattID); - sigmaZ2 = dict.getErr2Z(pattID); + //sigmaX2 = dict.getErr2X(pattID); // ALPIDE local X coordinate => MFT global X coordinate (ALPIDE rows) + //sigmaY2 = dict.getErr2Z(pattID); // ALPIDE local Z coordinate => MFT global Y coordinate (ALPIDE columns) + // temporary, until ITS bug fix + sigmaX2 = dict.getErrX(pattID) * dict.getErrX(pattID); + sigmaY2 = dict.getErrZ(pattID) * dict.getErrZ(pattID); if (!dict.isGroup(pattID)) { locXYZ = dict.getClusterCoordinates(c); } else { @@ -66,15 +68,15 @@ int ioutils::loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& event, g // Transformation to the local --> global auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - auto clsPoint2D = Point2D<Float_t>(gloXYZ.x(), gloXYZ.y()); + auto clsPoint2D = math_utils::Point2D<Float_t>(gloXYZ.x(), gloXYZ.y()); Float_t rCoord = clsPoint2D.R(); Float_t phiCoord = clsPoint2D.Phi(); - o2::utils::BringTo02PiGen(phiCoord); - int rBinIndex = constants::index_table::getRBinIndex(rCoord); - int phiBinIndex = constants::index_table::getPhiBinIndex(phiCoord); - int binIndex = constants::index_table::getBinIndex(rBinIndex, phiBinIndex); - - event.addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), phiCoord, rCoord, event.getClustersInLayer(layer).size(), binIndex); + o2::math_utils::bringTo02PiGen(phiCoord); + int rBinIndex = tracker->getRBinIndex(rCoord); + int phiBinIndex = tracker->getPhiBinIndex(phiCoord); + int binIndex = tracker->getBinIndex(rBinIndex, phiBinIndex); + // TODO: Check consistency of sigmaX2 and sigmaY2 + event.addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), phiCoord, rCoord, event.getClustersInLayer(layer).size(), binIndex, sigmaX2, sigmaY2, sensorID); if (mcLabels) { event.addClusterLabelToLayer(layer, *(mcLabels->getLabels(first + clusterId).begin())); } diff --git a/Detectors/ITSMFT/MFT/tracking/src/MFTTrackingLinkDef.h b/Detectors/ITSMFT/MFT/tracking/src/MFTTrackingLinkDef.h index ff17bbc9ef49b..b409fc2de6379 100644 --- a/Detectors/ITSMFT/MFT/tracking/src/MFTTrackingLinkDef.h +++ b/Detectors/ITSMFT/MFT/tracking/src/MFTTrackingLinkDef.h @@ -15,9 +15,9 @@ #pragma link off all functions; #pragma link C++ class o2::mft::TrackLTF + ; #pragma link C++ class o2::mft::TrackCA + ; -#pragma link C++ class o2::mft::TrackParamMFT + ; #pragma link C++ class std::vector < o2::mft::TrackLTF> + ; #pragma link C++ class std::vector < o2::mft::TrackCA> + ; +#pragma link C++ class o2::mft::TrackerConfig + ; #pragma link C++ class o2::mft::MFTTrackingParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::mft::MFTTrackingParam> + ; diff --git a/Detectors/ITSMFT/MFT/tracking/src/ROframe.cxx b/Detectors/ITSMFT/MFT/tracking/src/ROframe.cxx index bd4f39c7863ee..21b1ae72a27a7 100644 --- a/Detectors/ITSMFT/MFT/tracking/src/ROframe.cxx +++ b/Detectors/ITSMFT/MFT/tracking/src/ROframe.cxx @@ -27,19 +27,15 @@ ROframe::ROframe(const Int_t ROframeId) : mROframeId{ROframeId} Int_t ROframe::getTotalClusters() const { size_t totalClusters{0}; - for (auto& clusters : mClusters) + for (auto& clusters : mClusters) { totalClusters += clusters.size(); + } return Int_t(totalClusters); } -void ROframe::initialise() +void ROframe::initialize() { sortClusters(); - - for (Int_t layer = 0; layer < constants::mft::LayersNumber; ++layer) { - mUsedClusters[layer].clear(); - mUsedClusters[layer].resize(mClusters[layer].size(), kFALSE); - } } void ROframe::sortClusters() @@ -47,29 +43,34 @@ void ROframe::sortClusters() Int_t nClsInLayer, binPrevIndex, clsMinIndex, clsMaxIndex, jClsLayer; // sort the clusters in R-Phi for (Int_t iLayer = 0; iLayer < constants::mft::LayersNumber; ++iLayer) { - if (mClusters[iLayer].size() == 0) + if (mClusters[iLayer].size() == 0) { continue; + } // sort clusters in layer according to the bin index sort(mClusters[iLayer].begin(), mClusters[iLayer].end(), [](Cluster& c1, Cluster& c2) { return c1.indexTableBin < c2.indexTableBin; }); // find the cluster local index range in each bin - // index = element position vector in the vector + // index = element position in the vector nClsInLayer = mClusters[iLayer].size(); binPrevIndex = mClusters[iLayer].at(0).indexTableBin; clsMinIndex = 0; for (jClsLayer = 1; jClsLayer < nClsInLayer; ++jClsLayer) { - if (mClusters[iLayer].at(jClsLayer).indexTableBin == binPrevIndex) + if (mClusters[iLayer].at(jClsLayer).indexTableBin == binPrevIndex) { continue; + } + clsMaxIndex = jClsLayer - 1; - std::pair<Int_t, Int_t> pair1(clsMinIndex, clsMaxIndex); - mClusterBinIndexRange[iLayer].insert(std::pair<Int_t, std::pair<Int_t, Int_t>>(binPrevIndex, pair1)); + + mClusterBinIndexRange[iLayer][binPrevIndex] = std::pair<Int_t, Int_t>(clsMinIndex, clsMaxIndex); + binPrevIndex = mClusters[iLayer].at(jClsLayer).indexTableBin; clsMinIndex = jClsLayer; } // clusters + // last cluster clsMaxIndex = jClsLayer - 1; - std::pair<Int_t, Int_t> pair1(clsMinIndex, clsMaxIndex); - mClusterBinIndexRange[iLayer].insert(std::pair<Int_t, std::pair<Int_t, Int_t>>(binPrevIndex, pair1)); + + mClusterBinIndexRange[iLayer][binPrevIndex] = std::pair<Int_t, Int_t>(clsMinIndex, clsMaxIndex); } // layers } diff --git a/Detectors/ITSMFT/MFT/tracking/src/TrackExtrap.cxx b/Detectors/ITSMFT/MFT/tracking/src/TrackExtrap.cxx deleted file mode 100644 index eb6d5e8e285da..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/src/TrackExtrap.cxx +++ /dev/null @@ -1,441 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackExtrap.cxx -/// \brief Implementation of tools for track extrapolation -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#include "MFTTracking/TrackExtrap.h" -#include "MFTTracking/TrackParamMFT.h" - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include <TGeoGlobalMagField.h> -#include <TGeoManager.h> -#include <TGeoMaterial.h> -#include <TGeoNode.h> -#include <TGeoShape.h> -#include <TMath.h> - -#include <FairMQLogger.h> - -namespace o2 -{ -namespace mft -{ - -//__________________________________________________________________________ -void TrackExtrap::linearExtrapToZ(TrackParamMFT* trackParam, double zEnd) -{ - /// Track parameters linearly extrapolated to the plane at "zEnd". - /// On return, results from the extrapolation are updated in trackParam. - - if (trackParam->getZ() == zEnd) { - return; // nothing to be done if same z - } - - // Compute track parameters - double dZ = (zEnd - trackParam->getZ()); - double x0 = trackParam->getX(); - double y0 = trackParam->getY(); - double phi0 = trackParam->getPhi(); - double cosphi0, sinphi0; - o2::utils::sincos(phi0, sinphi0, cosphi0); - double invtanl0 = 1.0 / trackParam->getTanl(); - double n = dZ * invtanl0; - double x = x0 + n * cosphi0; - double y = y0 + n * sinphi0; - trackParam->setX(x); - trackParam->setY(y); - trackParam->setZ(zEnd); -} - -//__________________________________________________________________________ -void TrackExtrap::linearExtrapToZCov(TrackParamMFT* trackParam, double zEnd, bool updatePropagator) -{ - /// Track parameters and their covariances linearly extrapolated to the plane at "zEnd". - /// On return, results from the extrapolation are updated in trackParam. - - // Calculate the jacobian related to the track parameters extrapolated to "zEnd" - double dZ = (zEnd - trackParam->getZ()); - double phi0 = trackParam->getPhi(); - double tanl0 = trackParam->getTanl(); - double invtanl0 = 1.0 / tanl0; - // double invqpt0 = trackParam->getInvQPt(); - double cosphi0, sinphi0; - o2::utils::sincos(phi0, sinphi0, cosphi0); - // double k = TMath::Abs(o2::constants::math::B2C * getBz()); - double n = dZ * invtanl0; - double m = n * invtanl0; - // double theta = -invqpt0 * dZ * k * invtanl0; - // auto Hz = getSignBz(); - - linearExtrapToZ(trackParam, zEnd); - - // Calculate Jacobian - TMatrixD jacob(5, 5); - jacob.UnitMatrix(); - jacob(0, 2) = -n * sinphi0; - jacob(0, 3) = -m * cosphi0; - jacob(1, 2) = n * cosphi0; - jacob(1, 3) = -m * sinphi0; - - // Extrapolate track parameter covariances to "zEnd" - TMatrixD tmp(trackParam->getCovariances(), TMatrixD::kMultTranspose, jacob); - TMatrixD tmp2(jacob, TMatrixD::kMult, tmp); - trackParam->setCovariances(tmp2); - - // Update the propagator if required - if (updatePropagator) { - trackParam->updatePropagator(jacob); - } -} - -//__________________________________________________________________________ -void TrackExtrap::quadraticExtrapToZ(TrackParamMFT* trackParam, double zEnd) -{ - /// Track parameters extrapolated to the plane at "zEnd" considering a helix - /// On return, results from the extrapolation are updated in trackParam. - - if (trackParam->getZ() == zEnd) { - return; // nothing to be done if same z - } - - // Compute track parameters - double dZ = (zEnd - trackParam->getZ()); - double x0 = trackParam->getX(); - double y0 = trackParam->getY(); - double phi0 = trackParam->getPhi(); - double cosphi0, sinphi0; - o2::utils::sincos(phi0, sinphi0, cosphi0); - double invtanl0 = 1.0 / trackParam->getTanl(); - double invqpt0 = trackParam->getInvQPt(); - auto Hz = getSignBz(); - double k = TMath::Abs(o2::constants::math::B2C * getBz()); - double n = dZ * invtanl0; - double theta = -invqpt0 * dZ * k * invtanl0; - double deltax = n * cosphi0 - 0.5 * n * theta * Hz * sinphi0; - double deltay = n * sinphi0 + 0.5 * n * theta * Hz * cosphi0; - - double x = x0 + deltax; - double y = y0 + deltay; - double phi = phi0 + Hz * theta; - - trackParam->setX(x); - trackParam->setY(y); - trackParam->setZ(zEnd); - trackParam->setPhi(phi); -} - -//__________________________________________________________________________ -void TrackExtrap::helixExtrapToZ(TrackParamMFT* trackParam, double zEnd) -{ - /// Track parameters extrapolated to the plane at "zEnd" considering a helix - /// On return, results from the extrapolation are updated in trackParam. - - if (trackParam->getZ() == zEnd) { - return; // nothing to be done if same z - } - - // Compute track parameters - double dZ = (zEnd - trackParam->getZ()); - double x0 = trackParam->getX(); - double y0 = trackParam->getY(); - double px0 = trackParam->getPx(); - double py0 = trackParam->getPy(); - double invtanl0 = 1.0 / trackParam->getTanl(); - double invqpt0 = trackParam->getInvQPt(); - auto q = trackParam->getCharge(); - auto Hz = getSignBz(); - double k = TMath::Abs(o2::constants::math::B2C * getBz()); - auto invk = 1.0 / k; - double theta = -invqpt0 * dZ * k * invtanl0; - double costheta, sintheta; - o2::utils::sincos(theta, sintheta, costheta); - double deltax = Hz * py0 * invk * (1.0 - costheta) - px0 * q * invk * sintheta; - double deltay = -Hz * px0 * invk * (1.0 - costheta) - py0 * q * invk * sintheta; - - double x = x0 + deltax; - double y = y0 + deltay; - double phi = trackParam->getPhi() + Hz * theta; - - trackParam->setX(x); - trackParam->setY(y); - trackParam->setZ(zEnd); - trackParam->setPhi(phi); -} - -//__________________________________________________________________________ -void TrackExtrap::quadraticExtrapToZCov(TrackParamMFT* trackParam, double zEnd, bool updatePropagator) -{ - - // Calculate the jacobian related to the track parameters extrapolated to "zEnd" - double dZ = (zEnd - trackParam->getZ()); - double phi0 = trackParam->getPhi(); - double tanl0 = trackParam->getTanl(); - double invtanl0 = 1.0 / tanl0; - double invqpt0 = trackParam->getInvQPt(); - double cosphi0, sinphi0; - o2::utils::sincos(phi0, sinphi0, cosphi0); - double k = TMath::Abs(o2::constants::math::B2C * getBz()); - double n = dZ * invtanl0; - double m = n * invtanl0; - double theta = -invqpt0 * dZ * k * invtanl0; - auto Hz = getSignBz(); - - quadraticExtrapToZ(trackParam, zEnd); - - // Calculate Jacobian - TMatrixD jacob(5, 5); - jacob.UnitMatrix(); - jacob(0, 2) = -n * theta * 0.5 * Hz * cosphi0 - n * sinphi0; - jacob(0, 3) = Hz * m * theta * sinphi0 - m * cosphi0; - jacob(0, 4) = k * m * 0.5 * Hz * dZ * sinphi0; - jacob(1, 2) = -n * theta * 0.5 * Hz * sinphi0 + n * cosphi0; - jacob(1, 3) = -Hz * m * theta * cosphi0 - m * sinphi0; - jacob(1, 4) = -k * m * 0.5 * Hz * dZ * cosphi0; - jacob(2, 3) = -Hz * theta * invtanl0; - jacob(2, 4) = -Hz * k * n; - - // Extrapolate track parameter covariances to "zEnd" - TMatrixD tmp(trackParam->getCovariances(), TMatrixD::kMultTranspose, jacob); - TMatrixD tmp2(jacob, TMatrixD::kMult, tmp); - trackParam->setCovariances(tmp2); - - // Update the propagator if required - if (updatePropagator) { - trackParam->updatePropagator(jacob); - } -} - -//__________________________________________________________________________ -void TrackExtrap::helixExtrapToZCov(TrackParamMFT* trackParam, double zEnd, bool updatePropagator) -{ - - // Calculate the jacobian related to the track parameters extrapolated to "zEnd" - double dZ = (zEnd - trackParam->getZ()); - double phi0 = trackParam->getPhi(); - double tanl0 = trackParam->getTanl(); - double invtanl0 = 1.0 / tanl0; - double invqpt0 = trackParam->getInvQPt(); - auto qpt0 = 1.0 / invqpt0; - double cosphi0, sinphi0; - o2::utils::sincos(phi0, sinphi0, cosphi0); - double k = TMath::Abs(o2::constants::math::B2C * getBz()); - double invk = 1.0 / k; - double theta = -invqpt0 * dZ * k * invtanl0; - double costheta, sintheta; - o2::utils::sincos(theta, sintheta, costheta); - auto Hz = getSignBz(); - auto L = qpt0 * qpt0 * invk; - auto N = dZ * invtanl0 * qpt0; - auto O = sintheta * cosphi0; - auto P = sinphi0 * costheta; - auto R = sinphi0 * sintheta; - auto S = cosphi0 * costheta; - auto Y = sinphi0 * qpt0 * invk; - auto X = cosphi0 * qpt0 * invk; - auto YC = Y * costheta; - auto YS = Y * sintheta; - auto XC = X * costheta; - auto XS = X * sintheta; - auto T = qpt0 * costheta; - auto U = qpt0 * sintheta; - auto V = qpt0; - double n = dZ * invtanl0; - double m = n * invtanl0; - - // Extrapolate track parameters to "zEnd" - helixExtrapToZ(trackParam, zEnd); - - // Calculate Jacobian - TMatrixD jacob(5, 5); - jacob.UnitMatrix(); - jacob(0, 2) = Hz * X - Hz * XC + YS; - jacob(0, 3) = Hz * R * m - S * m; - jacob(0, 4) = -Hz * N * R + Hz * T * Y - Hz * V * Y + N * S + U * X; - jacob(1, 2) = Hz * Y - Hz * YC - XS; - jacob(1, 3) = -Hz * O * m - P * m; - jacob(1, 4) = Hz * N * O - Hz * T * X + Hz * V * X + N * P + U * Y; - jacob(2, 3) = -Hz * theta * invtanl0; - jacob(2, 4) = -Hz * k * n; - - // Extrapolate track parameter covariances to "zEnd" - TMatrixD tmp(trackParam->getCovariances(), TMatrixD::kMultTranspose, jacob); - TMatrixD tmp2(jacob, TMatrixD::kMult, tmp); - trackParam->setCovariances(tmp2); - - // Update the propagator if required - if (updatePropagator) { - trackParam->updatePropagator(jacob); - } -} - -//__________________________________________________________________________ -bool TrackExtrap::extrapToZ(TrackParamMFT* trackParam, double zEnd, bool isFieldON) -{ - /// Interface to track parameter extrapolation to the plane at "Z". - /// On return, the track parameters resulting from the extrapolation are updated in trackParam. - if (!isFieldON) { - linearExtrapToZ(trackParam, zEnd); - return true; - } else { - - quadraticExtrapToZ(trackParam, zEnd); - return true; - } -} - -//__________________________________________________________________________ -void TrackExtrap::extrapToZCov(TrackParamMFT* trackParam, double zEnd, bool updatePropagator, bool isFieldON) -{ - /// Track parameters and their covariances extrapolated to the plane at "zEnd". - /// On return, results from the extrapolation are updated in trackParam. - - auto& mftTrackingParam = MFTTrackingParam::Instance(); - - if (!isFieldON) { // linear extrapolation if no magnetic field - linearExtrapToZCov(trackParam, zEnd, updatePropagator); - return; - } else { - // Extrapolate track parameters to "zEnd" - - switch (mftTrackingParam.trackmodel) { - case Helix: - helixExtrapToZCov(trackParam, zEnd, updatePropagator); - return; - break; - case Quadratic: - quadraticExtrapToZCov(trackParam, zEnd, updatePropagator); - return; - break; - case Linear: - linearExtrapToZCov(trackParam, zEnd, updatePropagator); - return; - break; - } - } -} - -//__________________________________________________________________________ -void TrackExtrap::addMCSEffect(TrackParamMFT* trackParam, double dZ, double x0) -{ - /// Add to the track parameter covariances the effects of multiple Coulomb scattering - /// through a material of thickness "abs(dZ)" and of radiation length "x0" - /// assuming linear propagation and using the small angle approximation. - /// All scattering evaluated happens at the position of the first cluster - - bool debug = false; - double phi0 = trackParam->getPhi(); - double tanl0 = trackParam->getTanl(); - double invtanl0 = 1.0 / tanl0; - double invqpt0 = trackParam->getInvQPt(); - double p = trackParam->getP(); - - double cosphi0, sinphi0; - o2::utils::sincos(phi0, sinphi0, cosphi0); - - double csclambda = TMath::Abs(TMath::Sqrt(1 + tanl0 * tanl0) * invtanl0); - double pathLengthOverX0 = x0 * csclambda; - - // Angular dispersion square of the track (variance) in a plane perpendicular to the trajectory - double sigmathetasq = 0.0136 * invqpt0 * (1 + 0.038 * TMath::Log(pathLengthOverX0)); - sigmathetasq *= sigmathetasq * pathLengthOverX0; - - // Get covariance matrix - TMatrixD newParamCov(trackParam->getCovariances()); - if (debug) { - std::cout << "Track covariances before MCS:"; - newParamCov.Print(); - } - - if (dZ > 0) { - double A = tanl0 * tanl0 + 1; - double B = dZ * cosphi0 * invtanl0; - double C = dZ * sinphi0 * invtanl0; - double D = A * B * invtanl0; - double E = -A * C * invtanl0; - double F = -C - D; - double G = B + E; - double H = -invqpt0 * tanl0; - - newParamCov(0, 0) += sigmathetasq * F * F; - - newParamCov(0, 1) += sigmathetasq * F * G; - newParamCov(1, 0) += sigmathetasq * F * G; - - newParamCov(1, 1) += sigmathetasq * G * G; - - newParamCov(2, 0) += sigmathetasq * F; - newParamCov(0, 2) += sigmathetasq * F; - - newParamCov(2, 1) += sigmathetasq * G; - newParamCov(1, 2) += sigmathetasq * G; - - newParamCov(2, 2) += sigmathetasq; - - newParamCov(3, 0) += sigmathetasq * A * F; - newParamCov(0, 3) += sigmathetasq * A * F; - - newParamCov(3, 1) += sigmathetasq * A * G; - newParamCov(1, 3) += sigmathetasq * A * G; - - newParamCov(3, 2) += sigmathetasq * A; - newParamCov(2, 3) += sigmathetasq * A; - - newParamCov(3, 3) += sigmathetasq * A * A; - - newParamCov(4, 0) += sigmathetasq * F * H; - newParamCov(0, 4) += sigmathetasq * F * H; - - newParamCov(4, 1) += sigmathetasq * G * H; - newParamCov(1, 4) += sigmathetasq * G * H; - - newParamCov(4, 2) += sigmathetasq * H; - newParamCov(2, 4) += sigmathetasq * H; - - newParamCov(4, 3) += sigmathetasq * A * H; - newParamCov(3, 4) += sigmathetasq * A * H; - - newParamCov(4, 4) += sigmathetasq * tanl0 * tanl0 * invqpt0 * invqpt0; - } else { - - double A = tanl0 * tanl0 + 1; - double H = -invqpt0 * tanl0; - - newParamCov(2, 2) += sigmathetasq; - - newParamCov(3, 2) += sigmathetasq * A; - newParamCov(2, 3) += sigmathetasq * A; - - newParamCov(3, 3) += sigmathetasq * A * A; - - newParamCov(4, 2) += sigmathetasq * H; - newParamCov(2, 4) += sigmathetasq * H; - - newParamCov(4, 3) += sigmathetasq * A * H; - newParamCov(3, 4) += sigmathetasq * A * H; - - newParamCov(4, 4) += sigmathetasq * tanl0 * tanl0 * invqpt0 * invqpt0; - } - - if (debug) { - std::cout << "Track covariances after MCS:"; - newParamCov.Print(); - std::cout << " **********************************************************\n"; - } - - // Set new covariances - trackParam->setCovariances(newParamCov); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/tracking/src/TrackFitter.cxx b/Detectors/ITSMFT/MFT/tracking/src/TrackFitter.cxx index 418cf3f8232cd..e21d0bc11c2bd 100644 --- a/Detectors/ITSMFT/MFT/tracking/src/TrackFitter.cxx +++ b/Detectors/ITSMFT/MFT/tracking/src/TrackFitter.cxx @@ -16,7 +16,6 @@ #include "MFTBase/Constants.h" #include "MFTTracking/TrackFitter.h" #include "MFTTracking/TrackCA.h" -#include "MFTTracking/TrackExtrap.h" #include "MFTTracking/Cluster.h" #include "DataFormatsMFT/TrackMFT.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -27,10 +26,10 @@ #include <TF1.h> #include <TF2.h> #include "CommonConstants/MathConstants.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "MathUtils/Utils.h" -using o2::math_utils::math_base::fitGaus; +using o2::math_utils::fitGaus; namespace o2 { @@ -44,7 +43,6 @@ void TrackFitter::setBz(float bZ) /// Set the magnetic field for the MFT mBZField = bZ; - mTrackExtrap.setBz(bZ); if (mftTrackingParam.verbose) { LOG(INFO) << "Setting Fitter field = " << bZ; @@ -52,520 +50,469 @@ void TrackFitter::setBz(float bZ) } //_________________________________________________________________________________________________ -bool TrackFitter::fit(FitterTrackMFT& track, bool smooth, bool finalize, - std::list<TrackParamMFT>::reverse_iterator* itStartingParam) +bool TrackFitter::fit(TrackLTF& track, bool outward) { - /// Fit a track to its attached clusters - /// Smooth the track if requested and the smoother enabled - /// If finalize = true: copy the smoothed parameters, if any, into the regular ones - /// Fit the entire track or only the part upstream itStartingParam + + /// Fit a track using its attached clusters /// Returns false in case of failure auto& mftTrackingParam = MFTTrackingParam::Instance(); + auto nClusters = track.getNumberOfPoints(); if (mftTrackingParam.verbose) { - std::cout << "\n ***************************** Start Fitting new track ***************************** \n"; - std::cout << "N Clusters = " << track.getNPoints() << std::endl; + std::cout << "Seed covariances: \n" + << track.getCovariances() << std::endl + << std::endl; } - // initialize the starting track parameters and cluster - double chi2invqptquad; - double invpqtquad; - - auto itParam(track.rbegin()); - if (itStartingParam != nullptr) { - // use the ones pointed to by itStartingParam - if (*itStartingParam == track.rend()) { - LOG(INFO) << "MFT Fit ERROR: invalid track starting parameters"; - track.removable(); - return false; + // recursively compute clusters, updating the track parameters + if (!outward) { // Inward for vertexing + nClusters--; + while (nClusters-- > 0) { + if (!computeCluster(track, nClusters)) { + return false; + } } - itParam = *itStartingParam; - } else { - // or start from the last cluster and compute the track parameters from its position - // and the one of the first previous cluster found on a different layer - auto itPreviousParam(itParam); - ++itPreviousParam; - invpqtquad = invQPtFromParabola(track, mBZField, chi2invqptquad); - track.setInvQPtQuadtratic(invpqtquad); - track.setChi2QPtQuadtratic(chi2invqptquad); - (*itParam).setInvQPt(track.getInvQPtQuadtratic()); // Initial momentum estimate - initTrack(*itParam->getClusterPtr(), *itParam); - } - - if (mftTrackingParam.verbose) { - std::cout << "Seed covariances:"; - itParam->getCovariances().Print(); - } - - // recusively add the upstream clusters and update the track parameters - TrackParamMFT* startingParam = &*itParam; - while (++itParam != track.rend()) { - if (!addCluster(*startingParam, *itParam->getClusterPtr(), *itParam)) { - track.removable(); - return false; + } else { // Outward for MCH matching + int ncl = 1; + while (ncl < nClusters) { + if (!computeCluster(track, ncl)) { + return false; + } + ncl++; } - startingParam = &*itParam; } - - itParam--; if (mftTrackingParam.verbose) { - std::cout << "Track covariances:"; - itParam->getCovariances().Print(); - std::cout << "Track Chi2 = " << itParam->getTrackChi2() << std::endl; + // Print final covariances? std::cout << "Track covariances:"; track->getCovariances().Print(); + std::cout << "Track Chi2 = " << track.getTrackChi2() << std::endl; std::cout << " ***************************** Done fitting *****************************\n"; } - // smooth the track if requested and the smoother enabled - if (smooth && mSmooth) { - if (!smoothTrack(track, finalize)) { - track.removable(); - return false; - } - } return true; } //_________________________________________________________________________________________________ -void TrackFitter::initTrack(const Cluster& cl, TrackParamMFT& param) +bool TrackFitter::initTrack(TrackLTF& track, bool outward) { - /// Compute the initial track parameters at the z position of the last cluster (cl) - /// The covariance matrix is computed such that the last cluster is the only constraint - /// (by assigning an infinite dispersion to the other cluster) - /// These parameters are the seed for the Kalman filter - auto& mftTrackingParam = MFTTrackingParam::Instance(); - // compute the track parameters at the last cluster - double x0 = cl.xCoordinate; - double y0 = cl.yCoordinate; - double z0 = cl.zCoordinate; - double pt = TMath::Sqrt(x0 * x0 + y0 * y0); - double pz = z0; - double phi0 = TMath::ATan2(y0, x0); - double tanl = pz / pt; - double r0sq = x0 * x0 + y0 * y0; - double r0cu = r0sq * TMath::Sqrt(r0sq); - double invr0sq = 1.0 / r0sq; - double invr0cu = 1.0 / r0cu; - double sigmax0sq = cl.sigmaX2; - double sigmay0sq = cl.sigmaY2; - double sigmaDeltaZsq = 5.0; // Primary vertex distribution: beam interaction diamond - double sigmaboost = mftTrackingParam.sigmaboost; // Boost q/pt seed covariances - double seedH_k = mftTrackingParam.seedH_k; // SeedH constant - - param.setX(x0); - param.setY(y0); - param.setZ(z0); - param.setPhi(phi0); - param.setTanl(tanl); - - // Configure the track seed - switch (mftTrackingParam.seed) { - case AB: - if (mftTrackingParam.verbose) - std::cout << " Init track with Seed A / B; sigmaboost = " << sigmaboost << ".\n"; - param.setInvQPt(1.0 / pt); // Seeds A & B - break; - case CE: - if (mftTrackingParam.verbose) - std::cout << " Init track with Seed C / E; sigmaboost = " << sigmaboost << ".\n"; - param.setInvQPt(std::copysign(1.0, param.getInvQPt()) / pt); // Seeds C & E - break; - case DH: - if (mftTrackingParam.verbose) - std::cout << " Init track with Seed H; (k = " << seedH_k << "); sigmaboost = " << sigmaboost << ".\n"; - param.setInvQPt(param.getInvQPt() / seedH_k); // SeedH - break; - default: - if (mftTrackingParam.verbose) - std::cout << " Init track with Seed D.\n"; - break; + // initialize the starting track parameters and cluster + double sigmainvQPtsq; + double chi2invqptquad; + auto invQPt0 = invQPtFromFCF(track, mBZField, sigmainvQPtsq); + auto nPoints = track.getNumberOfPoints(); + auto k = TMath::Abs(o2::constants::math::B2C * mBZField); + auto Hz = std::copysign(1, mBZField); + + if (mftTrackingParam.verbose) { + std::cout << "\n ***************************** Start Fitting new track ***************************** \n"; + std::cout << "N Clusters = " << nPoints << std::endl; + } + + track.setInvQPtSeed(invQPt0); + track.setChi2QPtSeed(chi2invqptquad); + track.setInvQPt(invQPt0); + + /// Compute the initial track parameters to seed the Kalman filter + + int first_cls, last_cls; + if (outward) { // MCH matching + first_cls = 0; + last_cls = nPoints - 1; + } else { // Vertexing + first_cls = nPoints - 1; + last_cls = 0; } + auto x0 = track.getXCoordinates()[first_cls]; + auto y0 = track.getYCoordinates()[first_cls]; + auto z0 = track.getZCoordinates()[first_cls]; + + auto deltaX = track.getXCoordinates()[nPoints - 1] - track.getXCoordinates()[0]; + auto deltaY = track.getYCoordinates()[nPoints - 1] - track.getYCoordinates()[0]; + auto deltaZ = track.getZCoordinates()[nPoints - 1] - track.getZCoordinates()[0]; + auto deltaR = TMath::Sqrt(deltaX * deltaX + deltaY * deltaY); + auto tanl0 = 0.5 * TMath::Sqrt2() * (deltaZ / deltaR) * + TMath::Sqrt(TMath::Sqrt((invQPt0 * deltaR * k) * (invQPt0 * deltaR * k) + 1) + 1); + auto phi0 = TMath::ATan2(deltaY, deltaX) - 0.5 * Hz * invQPt0 * deltaZ * k / tanl0; + auto sigmax0sq = track.getSigmasX2()[first_cls]; + auto sigmay0sq = track.getSigmasY2()[first_cls]; + auto sigmax1sq = track.getSigmasX2()[last_cls]; + auto sigmay1sq = track.getSigmasY2()[last_cls]; + auto sigmaDeltaXsq = sigmax0sq + sigmax1sq; + auto sigmaDeltaYsq = sigmay0sq + sigmay1sq; + + track.setX(x0); + track.setY(y0); + track.setZ(z0); + track.setPhi(phi0); + track.setTanl(tanl0); + if (mftTrackingParam.verbose) { + std::cout << " Init " << (track.isCA() ? "CA Track " : "LTF Track") << std::endl; auto model = (mftTrackingParam.trackmodel == Helix) ? "Helix" : (mftTrackingParam.trackmodel == Quadratic) ? "Quadratic" : "Linear"; std::cout << "Track Model: " << model << std::endl; - std::cout << "initTrack Cluster X = " << x0 << " Y = " << y0 << " Z = " << z0 << std::endl; - std::cout << " seed Phi, Tanl, InvQpt = " << param.getPhi() << " " << param.getTanl() << " " << param.getInvQPt() << std::endl; + std::cout << " initTrack: X = " << x0 << " Y = " << y0 << " Z = " << z0 << " Tgl = " << tanl0 << " Phi = " << phi0 << " pz = " << track.getPz() << " qpt = " << 1.0 / track.getInvQPt() << std::endl; + std::cout << " Variances: sigma2_x0 = " << TMath::Sqrt(sigmax0sq) << " sigma2_y0 = " << TMath::Sqrt(sigmay0sq) << " sigma2_q/pt = " << TMath::Sqrt(sigmainvQPtsq) << std::endl; } - // compute the track parameter covariances at the last cluster (as if the other clusters did not exist) - TMatrixD lastParamCov(5, 5); - lastParamCov.Zero(); - lastParamCov(0, 0) = sigmaboost * sigmax0sq; // <X,X> - lastParamCov(0, 1) = 0; // <Y,X> - lastParamCov(0, 2) = sigmaboost * -sigmax0sq * y0 * invr0sq; // <PHI,X> - lastParamCov(0, 3) = sigmaboost * -z0 * sigmax0sq * x0 * invr0cu; // <TANL,X> - lastParamCov(0, 4) = sigmaboost * -x0 * sigmax0sq * invr0cu; // <INVQPT,X> - - lastParamCov(1, 1) = sigmaboost * sigmay0sq; // <Y,Y> - lastParamCov(1, 2) = sigmaboost * sigmay0sq * x0 * invr0sq; // <PHI,Y> - lastParamCov(1, 3) = sigmaboost * -z0 * sigmay0sq * y0 * invr0cu; // <TANL,Y> - lastParamCov(1, 4) = sigmaboost * y0 * sigmay0sq * invr0cu; //1e-2; // <INVQPT,Y> - - lastParamCov(2, 2) = sigmaboost * (sigmax0sq * y0 * y0 + sigmay0sq * x0 * x0) * invr0sq * invr0sq; // <PHI,PHI> - lastParamCov(2, 3) = sigmaboost * z0 * x0 * y0 * (sigmax0sq - sigmay0sq) * invr0sq * invr0cu; // <TANL,PHI> - lastParamCov(2, 4) = sigmaboost * y0 * x0 * invr0cu * invr0sq * (sigmax0sq - sigmay0sq); // <INVQPT,PHI> - - lastParamCov(3, 3) = sigmaboost * z0 * z0 * (sigmax0sq * x0 * x0 + sigmay0sq * y0 * y0) * invr0cu * invr0cu + sigmaDeltaZsq * invr0sq; // <TANL,TANL> - lastParamCov(3, 4) = sigmaboost * z0 * invr0cu * invr0cu * (sigmax0sq * x0 * x0 + sigmay0sq * y0 * y0); // <INVQPT,TANL> - - lastParamCov(4, 4) = sigmaboost * sigmaboost * (sigmax0sq * x0 * x0 + sigmay0sq * y0 * y0) * invr0cu * invr0cu; // <INVQPT,INVQPT> - - lastParamCov(1, 0) = lastParamCov(0, 1); // - lastParamCov(2, 0) = lastParamCov(0, 2); // - lastParamCov(2, 1) = lastParamCov(1, 2); // - lastParamCov(3, 0) = lastParamCov(0, 3); // - lastParamCov(3, 1) = lastParamCov(1, 3); // - lastParamCov(3, 2) = lastParamCov(2, 3); // - lastParamCov(4, 0) = lastParamCov(0, 4); // - lastParamCov(4, 1) = lastParamCov(1, 4); // - lastParamCov(4, 2) = lastParamCov(2, 4); // - lastParamCov(4, 3) = lastParamCov(3, 4); // - - param.setCovariances(lastParamCov); - - // set other parameters - param.setClusterPtr(&cl); - param.setTrackChi2(0.); + auto deltaR2 = deltaR * deltaR; + auto deltaR3 = deltaR2 * deltaR; + auto deltaR4 = deltaR2 * deltaR2; + auto k2 = k * k; + auto A = TMath::Sqrt(track.getInvQPt() * track.getInvQPt() * deltaR2 * k2 + 1); + auto A2 = A * A; + auto B = A + 1.0; + auto B2 = B * B; + auto B3 = B * B * B; + auto B12 = TMath::Sqrt(B); + auto B32 = B * B12; + auto B52 = B * B32; + auto C = invQPt0 * k; + auto C2 = C * C; + auto C3 = C * C2; + auto D = 1.0 / (A2 * B2 * B2 * deltaR4); + auto E = D * deltaZ / (B * deltaR); + auto F = deltaR * deltaX * C3 * Hz / (A * B32); + auto G = 0.5 * TMath::Sqrt2() * A * B32 * C * Hz * deltaR; + auto Gx = G * deltaX; + auto Gy = G * deltaY; + auto H = -0.25 * TMath::Sqrt2() * B12 * C3 * Hz * deltaR3; + auto Hx = H * deltaX; + auto Hy = H * deltaY; + auto I = A * B2; + auto Ix = I * deltaX; + auto Iy = I * deltaY; + auto J = 2 * B * deltaR3 * deltaR3 * k2; + auto K = 0.5 * A * B - 0.25 * C2 * deltaR2; + auto L0 = Gx + Hx + Iy; + auto M0 = -Gy - Hy + Ix; + auto N = -0.5 * B3 * C * Hz * deltaR3 * deltaR4 * k2; + auto O = 0.125 * C2 * deltaR4 * deltaR4 * k2; + auto P = -K * k * Hz * deltaR / A; + auto Q = deltaZ * deltaZ / (A2 * B * deltaR3 * deltaR3); + auto R = 0.25 * C * deltaZ * TMath::Sqrt2() * deltaR * k / (A * B12); + + SMatrix55 lastParamCov; + lastParamCov(0, 0) = sigmax0sq; // <X,X> + lastParamCov(0, 1) = 0; // <Y,X> + lastParamCov(0, 2) = 0; // <PHI,X> + lastParamCov(0, 3) = 0; // <TANL,X> + lastParamCov(0, 4) = 0; // <INVQPT,X> + + lastParamCov(1, 1) = sigmay0sq; // <Y,Y> + lastParamCov(1, 2) = 0; // <PHI,Y> + lastParamCov(1, 3) = 0; // <TANL,Y> + lastParamCov(1, 4) = 0; // <INVQPT,Y> + + lastParamCov(2, 2) = D * (J * K * K * sigmainvQPtsq + L0 * L0 * sigmaDeltaXsq + M0 * M0 * sigmaDeltaYsq); // <PHI,PHI> + lastParamCov(2, 3) = E * K * (TMath::Sqrt2() * B52 * (L0 * deltaX * sigmaDeltaXsq - deltaY * sigmaDeltaYsq * M0) + N * sigmainvQPtsq); // <TANL,PHI> + lastParamCov(2, 4) = P * sigmainvQPtsq * TMath::Sqrt2() / B32; // <INVQPT,PHI> + + lastParamCov(3, 3) = Q * (2 * K * K * (deltaX * deltaX * sigmaDeltaXsq + deltaY * deltaY * sigmaDeltaYsq) + O * sigmainvQPtsq); // <TANL,TANL> + lastParamCov(3, 4) = R * sigmainvQPtsq; // <INVQPT,TANL> + + lastParamCov(4, 4) = sigmainvQPtsq; // <INVQPT,INVQPT> + + track.setCovariances(lastParamCov); + track.setTrackChi2(0.); + + return true; } //_________________________________________________________________________________________________ -bool TrackFitter::addCluster(const TrackParamMFT& startingParam, const Cluster& cl, TrackParamMFT& param) +bool TrackFitter::computeCluster(TrackLTF& track, int cluster) { - /// Extrapolate the starting track parameters to the z position of the new cluster + /// Propagate track to the z position of the new cluster /// accounting for MCS dispersion in the current layer and the other(s) crossed /// Recompute the parameters adding the cluster constraint with the Kalman filter /// Returns false in case of failure auto& mftTrackingParam = MFTTrackingParam::Instance(); - - if (cl.zCoordinate <= startingParam.getZ()) { - LOG(INFO) << "AddCluster ERROR: The new cluster must be upstream! Bug on TrackFinder. "; - return false; + const auto& clx = track.getXCoordinates()[cluster]; + const auto& cly = track.getYCoordinates()[cluster]; + const auto& clz = track.getZCoordinates()[cluster]; + const auto& sigmaX2 = track.getSigmasX2()[cluster]; + const auto& sigmaY2 = track.getSigmasY2()[cluster]; + + if (track.getZ() == clz) { + LOG(INFO) << "AddCluster ERROR: The new cluster must be upstream! Bug on TrackFinder. " << (track.isCA() ? " CATrack" : "LTFTrack"); + LOG(INFO) << "track.getZ() = " << track.getZ() << " ; newClusterZ = " << clz << " ==> Skipping point."; + return true; + } + if (mftTrackingParam.verbose) { + std::cout << "computeCluster: X = " << clx << " Y = " << cly << " Z = " << clz << " nCluster = " << cluster << std::endl; } - if (mftTrackingParam.verbose) - std::cout << "addCluster: X = " << cl.xCoordinate << " Y = " << cl.yCoordinate << " Z = " << cl.zCoordinate << std::endl; - // copy the current parameters into the new ones - param.setParameters(startingParam.getParameters()); - param.setZ(startingParam.getZ()); - param.setCovariances(startingParam.getCovariances()); - param.setTrackChi2(startingParam.getTrackChi2()); // add MCS effects for the new cluster using o2::mft::constants::LayerZPosition; int startingLayerID, newLayerID; - double dZ = TMath::Abs(cl.zCoordinate - startingParam.getZ()); + auto dZ = clz - track.getZ(); //LayerID of each cluster from ZPosition // TODO: Use ChipMapping - for (auto layer = 10; layer--;) - if (startingParam.getZ() < LayerZPosition[layer] + .3 & startingParam.getZ() > LayerZPosition[layer] - .3) + for (auto layer = 10; layer--;) { + if (track.getZ() < LayerZPosition[layer] + .3 & track.getZ() > LayerZPosition[layer] - .3) { startingLayerID = layer; - for (auto layer = 10; layer--;) - if (cl.zCoordinate<LayerZPosition[layer] + .3 & cl.zCoordinate> LayerZPosition[layer] - .3) + } + } + for (auto layer = 10; layer--;) { + if (clz<LayerZPosition[layer] + .3 & clz> LayerZPosition[layer] - .3) { newLayerID = layer; + } + } // Number of disks crossed by this tracklet - int NDisksMS = (startingLayerID % 2 == 0) ? (startingLayerID - newLayerID) / 2 : (startingLayerID - newLayerID + 1) / 2; + int NDisksMS; + if (clz - track.getZ() > 0) { + NDisksMS = (startingLayerID % 2 == 0) ? (startingLayerID - newLayerID) / 2 : (startingLayerID - newLayerID + 1) / 2; + } else { + NDisksMS = (startingLayerID % 2 == 0) ? (newLayerID - startingLayerID + 1) / 2 : (newLayerID - startingLayerID) / 2; + } - double MFTDiskThicknessInX0 = mftTrackingParam.MFTRadLenghts / 5.0; + auto MFTDiskThicknessInX0 = mftTrackingParam.MFTRadLength / 5.0; if (mftTrackingParam.verbose) { std::cout << "startingLayerID = " << startingLayerID << " ; " << "newLayerID = " << newLayerID << " ; "; - std::cout << "cl.zCoordinate = " << cl.zCoordinate << " ; "; - std::cout << "startingParam.getZ() = " << startingParam.getZ() << " ; "; + std::cout << "cl.getZ() = " << clz << " ; "; + std::cout << "startingParam.getZ() = " << track.getZ() << " ; "; std::cout << "NDisksMS = " << NDisksMS << std::endl; } - // Add MCS effects - if ((NDisksMS * MFTDiskThicknessInX0) != 0) - mTrackExtrap.addMCSEffect(¶m, -1, NDisksMS * MFTDiskThicknessInX0); - - // reset propagator for smoother - if (mSmooth) { - param.resetPropagator(); + if ((NDisksMS * MFTDiskThicknessInX0) != 0) { + track.addMCSEffect(-1, NDisksMS * MFTDiskThicknessInX0); } - if (mftTrackingParam.verbose) - std::cout << " BeforeExtrap: X = " << param.getX() << " Y = " << param.getY() << " Z = " << param.getZ() << " Tgl = " << param.getTanl() << " Phi = " << param.getPhi() << " pz = " << param.getPz() << " qpt = " << 1.0 / param.getInvQPt() << std::endl; - - // extrapolate to the z position of the new cluster - mTrackExtrap.extrapToZCov(¶m, cl.zCoordinate, mSmooth); + if (mftTrackingParam.verbose) { + std::cout << " BeforeExtrap: X = " << track.getX() << " Y = " << track.getY() << " Z = " << track.getZ() << " Tgl = " << track.getTanl() << " Phi = " << track.getPhi() << " pz = " << track.getPz() << " qpt = " << 1.0 / track.getInvQPt() << std::endl; + } - if (mftTrackingParam.verbose) - std::cout << " AfterExtrap: X = " << param.getX() << " Y = " << param.getY() << " Z = " << param.getZ() << " Tgl = " << param.getTanl() << " Phi = " << param.getPhi() << " pz = " << param.getPz() << " qpt = " << 1.0 / param.getInvQPt() << std::endl; + // Propagate track to the z position of the new cluster + switch (mftTrackingParam.trackmodel) { + case Linear: + track.propagateToZlinear(clz); + break; + case Quadratic: + track.propagateToZquadratic(clz, mBZField); + break; + case Helix: + track.propagateToZhelix(clz, mBZField); + break; + default: + std::cout << " Invalid track model.\n"; + return false; + break; + } - // save extrapolated parameters and covariances for smoother - if (mSmooth) { - param.setExtrapParameters(param.getParameters()); - param.setExtrapCovariances(param.getCovariances()); + if (mftTrackingParam.verbose) { + std::cout << " AfterExtrap: X = " << track.getX() << " Y = " << track.getY() << " Z = " << track.getZ() << " Tgl = " << track.getTanl() << " Phi = " << track.getPhi() << " pz = " << track.getPz() << " qpt = " << 1.0 / track.getInvQPt() << std::endl; } - // recompute the parameters - param.setClusterPtr(&cl); - if (runKalmanFilter(param)) { + // recompute parameters + const std::array<float, 2>& pos = {clx, cly}; + const std::array<float, 2>& cov = {sigmaX2, sigmaY2}; + + if (track.update(pos, cov)) { if (mftTrackingParam.verbose) { - std::cout << " New Cluster: X = " << cl.xCoordinate << " Y = " << cl.yCoordinate << " Z = " << cl.zCoordinate << std::endl; - std::cout << " AfterKalman: X = " << param.getX() << " Y = " << param.getY() << " Z = " << param.getZ() << " Tgl = " << param.getTanl() << " Phi = " << param.getPhi() << " pz = " << param.getPz() << " qpt = " << 1.0 / param.getInvQPt() << std::endl; + std::cout << " New Cluster: X = " << clx << " Y = " << cly << " Z = " << clz << std::endl; + std::cout << " AfterKalman: X = " << track.getX() << " Y = " << track.getY() << " Z = " << track.getZ() << " Tgl = " << track.getTanl() << " Phi = " << track.getPhi() << " pz = " << track.getPz() << " qpt = " << 1.0 / track.getInvQPt() << std::endl; std::cout << std::endl; // Outputs track covariance matrix: // param.getCovariances().Print(); } return true; - } else - return false; + } + return false; } //_________________________________________________________________________________________________ -bool TrackFitter::smoothTrack(FitterTrackMFT& track, bool finalize) +Double_t invQPtFromFCF(const TrackLTF& track, Double_t bFieldZ, Double_t& sigmainvqptsq) { - /// Recompute the track parameters at each cluster using the Smoother - /// Smoothed parameters are stored in dedicated data members - /// If finalize, they are copied in the regular parameters in case of success - /// Returns false in case of failure - - auto itCurrentParam(track.begin()); - auto itPreviousParam(itCurrentParam); - ++itCurrentParam; - // smoothed parameters and covariances at first cluster = filtered parameters and covariances - itPreviousParam->setSmoothParameters(itPreviousParam->getParameters()); - itPreviousParam->setSmoothCovariances(itPreviousParam->getCovariances()); - - // local chi2 at first cluster = last additional chi2 provided by Kalman - itPreviousParam->setLocalChi2(itPreviousParam->getTrackChi2() - itCurrentParam->getTrackChi2()); - - // recursively smooth the next parameters and covariances - do { - if (!runSmoother(*itPreviousParam, *itCurrentParam)) { - return false; - } - ++itPreviousParam; - } while (++itCurrentParam != track.end()); - - // update the regular parameters and covariances if requested - if (finalize) { - for (auto& param : track) { - param.setParameters(param.getSmoothParameters()); - param.setCovariances(param.getSmoothCovariances()); + const std::array<Float_t, constants::mft::LayersNumber>& xPositions = track.getXCoordinates(); + const std::array<Float_t, constants::mft::LayersNumber>& yPositions = track.getYCoordinates(); + const std::array<Float_t, constants::mft::LayersNumber>& zPositions = track.getZCoordinates(); + const std::array<Float_t, constants::mft::LayersNumber>& SigmasX2 = track.getSigmasX2(); + const std::array<Float_t, constants::mft::LayersNumber>& SigmasY2 = track.getSigmasY2(); + + // Fast Circle Fit (Hansroul, Jeremie, Savard, 1987) + auto nPoints = track.getNumberOfPoints(); + Double_t* xVal = new Double_t[nPoints]; + Double_t* yVal = new Double_t[nPoints]; + Double_t* zVal = new Double_t[nPoints]; + Double_t* xErr = new Double_t[nPoints]; + Double_t* yErr = new Double_t[nPoints]; + Double_t* uVal = new Double_t[nPoints - 1]; + Double_t* vVal = new Double_t[nPoints - 1]; + Double_t* vErr = new Double_t[nPoints - 1]; + Double_t* fweight = new Double_t[nPoints - 1]; + Double_t* Rn = new Double_t[nPoints - 1]; + Double_t* Pn = new Double_t[nPoints - 1]; + Double_t A, Aerr, B, Berr, x2, y2, invx2y2, a, b, r, sigmaRsq, u2, sigma; + Double_t F0, F1, F2, F3, F4, SumSRn, SumSPn, SumRn, SumUPn, SumRP; + + SumSRn = SumSPn = SumRn = SumUPn = SumRP = 0.0; + F0 = F1 = F2 = F3 = F4 = 0.0; + + for (auto np = 0; np < nPoints; np++) { + xErr[np] = SigmasX2[np]; + yErr[np] = SigmasY2[np]; + if (np > 0) { + xVal[np] = xPositions[np] - xVal[0]; + yVal[np] = yPositions[np] - yVal[0]; + xErr[np] *= std::sqrt(2.); + yErr[np] *= std::sqrt(2.); + } else { + xVal[np] = 0.; + yVal[np] = 0.; } + zVal[np] = zPositions[np]; } - return true; -} - -//_________________________________________________________________________________________________ -bool TrackFitter::runKalmanFilter(TrackParamMFT& trackParam) -{ - /// Compute the new track parameters including the attached cluster with the Kalman filter - /// The current parameters are supposed to have been extrapolated to the cluster z position - /// Retruns false in case of failure - - // get actual track parameters (p) - TMatrixD param(trackParam.getParameters()); - - // get new cluster parameters (m) - const Cluster* cluster = trackParam.getClusterPtr(); - TMatrixD clusterParam(5, 1); - clusterParam.Zero(); - clusterParam(0, 0) = cluster->xCoordinate; - clusterParam(1, 0) = cluster->yCoordinate; - - // compute the actual parameter weight (W) - TMatrixD paramWeight(trackParam.getCovariances()); - if (paramWeight.Determinant() != 0) { - paramWeight.Invert(); - } else { - LOG(INFO) << "runKalmanFilter ERROR: Determinant = 0"; - return false; + for (int i = 0; i < (nPoints - 1); i++) { + x2 = xVal[i + 1] * xVal[i + 1]; + y2 = yVal[i + 1] * yVal[i + 1]; + invx2y2 = 1. / (x2 + y2); + uVal[i] = xVal[i + 1] * invx2y2; + vVal[i] = yVal[i + 1] * invx2y2; + vErr[i] = std::sqrt(8. * xErr[i + 1] * xErr[i + 1] * x2 * y2 + 2. * yErr[i + 1] * yErr[i + 1] * (x2 - y2) * (x2 - y2)) * invx2y2 * invx2y2; + u2 = uVal[i] * uVal[i]; + fweight[i] = 1. / vErr[i]; + F0 += fweight[i]; + F1 += fweight[i] * uVal[i]; + F2 += fweight[i] * u2; + F3 += fweight[i] * uVal[i] * u2; + F4 += fweight[i] * u2 * u2; } - // compute the new cluster weight (U) - TMatrixD clusterWeight(5, 5); - clusterWeight.Zero(); - clusterWeight(0, 0) = 1. / cluster->sigmaX2; // 1. / cluster->getEx2(); - clusterWeight(1, 1) = 1. / cluster->sigmaY2; // 1. / cluster->getEy2(); + double Rn_det1 = F2 * F4 - F3 * F3; + double Rn_det2 = F1 * F4 - F2 * F3; + double Rn_det3 = F1 * F3 - F2 * F2; + double Pn_det1 = Rn_det2; + double Pn_det2 = F0 * F4 - F2 * F2; + double Pn_det3 = F0 * F3 - F1 * F2; - // compute the new parameters covariance matrix ((W+U)^-1) - TMatrixD newParamCov(paramWeight, TMatrixD::kPlus, clusterWeight); - if (newParamCov.Determinant() != 0) { - newParamCov.Invert(); - } else { - LOG(INFO) << "runKalmanFilter ERROR: Determinant = 0"; - return false; - } - trackParam.setCovariances(newParamCov); - - // compute the new parameters (p' = ((W+U)^-1)U(m-p) + p) - TMatrixD tmp(clusterParam, TMatrixD::kMinus, param); // m-p - TMatrixD tmp2(clusterWeight, TMatrixD::kMult, tmp); // U(m-p) - TMatrixD newParam(newParamCov, TMatrixD::kMult, tmp2); // ((W+U)^-1)U(m-p) - newParam += param; // ((W+U)^-1)U(m-p) + p - trackParam.setParameters(newParam); - - // compute the additional chi2 (= ((p'-p)^-1)W(p'-p) + ((p'-m)^-1)U(p'-m)) - tmp = newParam; // p' - tmp -= param; // (p'-p) - TMatrixD tmp3(paramWeight, TMatrixD::kMult, tmp); // W(p'-p) - TMatrixD addChi2Track(tmp, TMatrixD::kTransposeMult, tmp3); // ((p'-p)^-1)W(p'-p) - tmp = newParam; // p' - tmp -= clusterParam; // (p'-m) - TMatrixD tmp4(clusterWeight, TMatrixD::kMult, tmp); // U(p'-m) - addChi2Track += TMatrixD(tmp, TMatrixD::kTransposeMult, tmp4); // ((p'-p)^-1)W(p'-p) + ((p'-m)^-1)U(p'-m) - trackParam.setTrackChi2(trackParam.getTrackChi2() + addChi2Track(0, 0)); + for (int j = 0; j < (nPoints - 1); j++) { + Rn[j] = fweight[j] * (Rn_det1 - uVal[j] * Rn_det2 + uVal[j] * uVal[j] * Rn_det3); + SumSRn += Rn[j] * Rn[j] * vErr[j] * vErr[j]; + SumRn += Rn[j]; - return true; -} - -//_________________________________________________________________________________________________ -bool TrackFitter::runSmoother(const TrackParamMFT& previousParam, TrackParamMFT& param) -{ - /// Recompute the track parameters starting from the previous ones - /// Returns false in case of failure + Pn[j] = fweight[j] * (-Pn_det1 + uVal[j] * Pn_det2 - uVal[j] * uVal[j] * Pn_det3); + SumSPn += Pn[j] * Pn[j] * vErr[j] * vErr[j]; + SumUPn += uVal[j] * Pn[j]; - // get variables - const TMatrixD& extrapParameters = previousParam.getExtrapParameters(); // X(k+1 k) - const TMatrixD& filteredParameters = param.getParameters(); // X(k k) - const TMatrixD& previousSmoothParameters = previousParam.getSmoothParameters(); // X(k+1 n) - const TMatrixD& propagator = previousParam.getPropagator(); // F(k) - const TMatrixD& extrapCovariances = previousParam.getExtrapCovariances(); // C(k+1 k) - const TMatrixD& filteredCovariances = param.getCovariances(); // C(k k) - const TMatrixD& previousSmoothCovariances = previousParam.getSmoothCovariances(); // C(k+1 n) - - // compute smoother gain: A(k) = C(kk) * F(k)^t * (C(k+1 k))^-1 - TMatrixD extrapWeight(extrapCovariances); - if (extrapWeight.Determinant() != 0) { - extrapWeight.Invert(); // (C(k+1 k))^-1 - } else { - LOG(INFO) << "Smoother ERROR: Determinant = 0"; - return false; + SumRP += Rn[j] * Pn[j] * vErr[j] * vErr[j] * vErr[j]; } - TMatrixD smootherGain(filteredCovariances, TMatrixD::kMultTranspose, propagator); // C(kk) * F(k)^t - smootherGain *= extrapWeight; // C(kk) * F(k)^t * (C(k+1 k))^-1 - - // compute smoothed parameters: X(k n) = X(k k) + A(k) * (X(k+1 n) - X(k+1 k)) - TMatrixD tmpParam(previousSmoothParameters, TMatrixD::kMinus, extrapParameters); // X(k+1 n) - X(k+1 k) - TMatrixD smoothParameters(smootherGain, TMatrixD::kMult, tmpParam); // A(k) * (X(k+1 n) - X(k+1 k)) - smoothParameters += filteredParameters; // X(k k) + A(k) * (X(k+1 n) - X(k+1 k)) - param.setSmoothParameters(smoothParameters); - - // compute smoothed covariances: C(k n) = C(k k) + A(k) * (C(k+1 n) - C(k+1 k)) * (A(k))^t - TMatrixD tmpCov(previousSmoothCovariances, TMatrixD::kMinus, extrapCovariances); // C(k+1 n) - C(k+1 k) - TMatrixD tmpCov2(tmpCov, TMatrixD::kMultTranspose, smootherGain); // (C(k+1 n) - C(k+1 k)) * (A(k))^t - TMatrixD smoothCovariances(smootherGain, TMatrixD::kMult, tmpCov2); // A(k) * (C(k+1 n) - C(k+1 k)) * (A(k))^t - smoothCovariances += filteredCovariances; // C(k k) + A(k) * (C(k+1 n) - C(k+1 k)) * (A(k))^t - param.setSmoothCovariances(smoothCovariances); - - // compute smoothed residual: r(k n) = cluster - X(k n) - const Cluster* cluster = param.getClusterPtr(); - TMatrixD smoothResidual(2, 1); - smoothResidual.Zero(); - smoothResidual(0, 0) = cluster->xCoordinate - smoothParameters(0, 0); - smoothResidual(1, 0) = cluster->yCoordinate - smoothParameters(1, 0); - - // compute weight of smoothed residual: W(k n) = (clusterCov - C(k n))^-1 - TMatrixD smoothResidualWeight(2, 2); - smoothResidualWeight(0, 0) = cluster->sigmaX2 - smoothCovariances(0, 0); // cluster->getEx2() - smoothCovariances(0, 0); - smoothResidualWeight(0, 1) = -smoothCovariances(0, 2); - smoothResidualWeight(1, 0) = -smoothCovariances(2, 0); - smoothResidualWeight(1, 1) = cluster->sigmaY2 - smoothCovariances(2, 2); // cluster->getEy2() - smoothCovariances(2, 2); - if (smoothResidualWeight.Determinant() != 0) { - smoothResidualWeight.Invert(); - } else { - LOG(INFO) << "Smoother ERROR: Determinant = 0"; - return false; + + Double_t invqpt_fcf; + Int_t qfcf; + // chi2 = 0.; + if (LinearRegression((nPoints - 1), uVal, vVal, vErr, B, Berr, A, Aerr)) { + // v = a * u + b + // circle passing through (0,0): + // (x - rx)^2 + (y - ry)^2 = r^2 + // ---> a = - rx / ry; + // ---> b = 1 / (2 * ry) + b = 1. / (2. * A); + a = -B * b; + r = std::sqrt(a * a + b * b); + double_t invR = 1. / r; + + // pt ---> + Double_t invpt = 1. / (o2::constants::math::B2C * bFieldZ * r); + + // sign(q) ---> + // rotate around the first point (0,0) to bring the last point + // on the x axis (y = 0) and check the y sign of the rotated + // center of the circle + Double_t x = xVal[nPoints - 1], y = yVal[nPoints - 1], z = zVal[nPoints - 1]; + Double_t slope = TMath::ATan2(y, x); + Double_t cosSlope = TMath::Cos(slope); + Double_t sinSlope = TMath::Sin(slope); + Double_t rxRot = a * cosSlope + b * sinSlope; + Double_t ryRot = a * sinSlope - b * cosSlope; + qfcf = (ryRot > 0.) ? -1 : +1; + + Double_t alpha = 2.0 * std::abs(TMath::ATan2(rxRot, ryRot)); + Double_t x0 = xVal[0], y0 = yVal[0], z0 = zVal[0]; + Double_t dxyz2 = (x - x0) * (x - x0) + (y - y0) * (y - y0) + (z - z0) * (z - z0); + Double_t cst = 1000.; + Double_t c_alpha = cst * alpha; + Double_t p, pt, pz; + pt = 1. / invpt; + p = std::sqrt(dxyz2) / c_alpha; + pz = std::sqrt(p * p - pt * pt); + + invqpt_fcf = qfcf * invpt; + + //error calculations: + double invA2 = 1. / (A * A); + + double sigmaAsq = SumSRn / (SumRn * SumRn); + double sigmaBsq = SumSPn / (SumUPn * SumUPn); + double sigmaAB = SumRP / (SumRn * SumUPn); + + double sigmaasq_FCF = TMath::Abs(0.25 * invA2 * invA2 * (B * B * sigmaAsq + A * A * sigmaBsq - A * B * sigmaAB)); + double sigmabsq_FCF = TMath::Abs(0.25 * invA2 * invA2 * sigmaAsq); + double sigma2R = invR * invR * (b * b * sigmaasq_FCF + a * a * sigmabsq_FCF + 2 * a * b * TMath::Sqrt(sigmaasq_FCF) * TMath::Sqrt(sigmabsq_FCF)); + + sigmainvqptsq = sigma2R * invpt * invpt * invR * invR; + + } else { // the linear regression failed... + LOG(WARN) << "LinearRegression failed!"; + invqpt_fcf = 1. / 100.; } - // compute local chi2 = (r(k n))^t * W(k n) * r(k n) - TMatrixD tmpChi2(smoothResidual, TMatrixD::kTransposeMult, smoothResidualWeight); // (r(k n))^t * W(k n) - TMatrixD localChi2(tmpChi2, TMatrixD::kMult, smoothResidual); // (r(k n))^t * W(k n) * r(k n) - param.setLocalChi2(localChi2(0, 0)); - return true; + return invqpt_fcf; } -//__________________________________________________________________________ -Double_t invQPtFromParabola(const FitterTrackMFT& track, Double_t bFieldZ, Double_t& chi2) +////_________________________________________________________________________________________________ +Bool_t LinearRegression(Int_t nVal, Double_t* xVal, Double_t* yVal, Double_t* yErr, Double_t& B, Double_t& Berr, Double_t& A, Double_t& Aerr) { - //rotate track to stabilize quadratic fitting - auto deltax = track.rbegin()->getX() - track.first().getX(); - auto deltay = track.rbegin()->getY() - track.first().getY(); - auto x_m = (track.rbegin()->getX() + track.first().getX()) / 2; - auto y_m = (track.rbegin()->getY() + track.first().getY()) / 2; - auto theta = -TMath::ATan2(deltay, deltax); - auto costheta = TMath::Cos(theta), sintheta = TMath::Sin(theta); - - bool verbose = false; - if (verbose) { - std::cout << "First and last cluster X,Y => " << track.first().getX() << " , " << track.first().getY() << " / " << track.rbegin()->getX() << " , " << track.rbegin()->getY() << std::endl; - std::cout << " Angle to rotate: " << theta << " ( " << theta * TMath::RadToDeg() << " deg ) " << std::endl; - } - - auto nPoints = track.getNClusters(); - Double_t* x = new Double_t[nPoints]; - Double_t* y = new Double_t[nPoints]; - int n = 0; - for (auto trackparam = track.begin(); trackparam != track.end(); trackparam++) { - auto x_0 = trackparam->getClusterPtr()->xCoordinate - x_m; - auto y_0 = trackparam->getClusterPtr()->yCoordinate - y_m; - x[n] = x_0 * costheta - y_0 * sintheta; - y[n] = x_0 * sintheta + y_0 * costheta; - //std::cout << " adding rotated point to fit at z = " << trackparam->getClusterPtr()->getZ() << " (" << x[n] << "," << y[n] << ") "<< std::endl; - n++; + // linear regression y = B * x + A + + Double_t S1, SXY, SX, SY, SXX, SsXY, SsXX, SsYY, Xm, Ym, s, delta, difx; + Double_t invYErr2; + + S1 = SXY = SX = SY = SXX = 0.0; + SsXX = SsYY = SsXY = Xm = Ym = 0.; + difx = 0.; + for (Int_t i = 0; i < nVal; i++) { + invYErr2 = 1. / (yErr[i] * yErr[i]); + S1 += invYErr2; + SXY += xVal[i] * yVal[i] * invYErr2; + SX += xVal[i] * invYErr2; + SY += yVal[i] * invYErr2; + SXX += xVal[i] * xVal[i] * invYErr2; + if (i > 0) { + difx += TMath::Abs(xVal[i] - xVal[i - 1]); + } + Xm += xVal[i]; + Ym += yVal[i]; + SsXX += xVal[i] * xVal[i]; + SsYY += yVal[i] * yVal[i]; + SsXY += xVal[i] * yVal[i]; } - - Double_t q0, q1, q2; - chi2 = QuadraticRegression(nPoints, x, y, q0, q1, q2); - Double_t radiusParabola = 0.5 / q2; - auto invqpt_parabola = q2 / (o2::constants::math::B2C * bFieldZ * 0.5); // radiusParabola; // radius = 0.5/q2 - - if (verbose) { - std::cout << "--------------------------------------------" << std::endl; - std::cout << " Fit QuadraticRegression: " << std::endl; - std::cout << " Fit Parameters [0] = " << q0 << " [1] = " << q1 << " [2] = " << q2 << std::endl; - std::cout << " Radius from QuadraticRegression = " << 0.5 / q2 << std::endl; - std::cout << " Seed qpt = " << 1.0 / invqpt_parabola << std::endl; - std::cout << "--------------------------------------------" << std::endl; + delta = SXX * S1 - SX * SX; + if (delta == 0.) { + return kFALSE; } - - return invqpt_parabola; -} - -//__________________________________________________________________________ -Double_t QuadraticRegression(Int_t nVal, Double_t* xVal, Double_t* yVal, Double_t& p0, Double_t& p1, Double_t& p2) -{ - /// Perform a Quadratic Regression - /// Assume same error on all clusters = 1 - /// Return ~ Chi2 - - TMatrixD y(nVal, 1); - TMatrixD x(nVal, 3); - TMatrixD xtrans(3, nVal); - - for (int i = 0; i < nVal; i++) { - y(i, 0) = yVal[i]; - x(i, 0) = 1.; - x(i, 1) = xVal[i]; - x(i, 2) = xVal[i] * xVal[i]; - xtrans(0, i) = 1.; - xtrans(1, i) = xVal[i]; - xtrans(2, i) = xVal[i] * xVal[i]; + B = (SXY * S1 - SX * SY) / delta; + A = (SY * SXX - SX * SXY) / delta; + + Ym /= (Double_t)nVal; + Xm /= (Double_t)nVal; + SsYY -= (Double_t)nVal * (Ym * Ym); + SsXX -= (Double_t)nVal * (Xm * Xm); + SsXY -= (Double_t)nVal * (Ym * Xm); + Double_t eps = 1.E-24; + if ((nVal > 2) && (TMath::Abs(difx) > eps) && ((SsYY - (SsXY * SsXY) / SsXX) > 0.)) { + s = TMath::Sqrt((SsYY - (SsXY * SsXY) / SsXX) / (nVal - 2)); + Aerr = s * TMath::Sqrt(1. / (Double_t)nVal + (Xm * Xm) / SsXX); + Berr = s / TMath::Sqrt(SsXX); + } else { + Aerr = 0.; + Berr = 0.; } - TMatrixD tmp(xtrans, TMatrixD::kMult, x); - tmp.Invert(); - - TMatrixD tmp2(xtrans, TMatrixD::kMult, y); - TMatrixD b(tmp, TMatrixD::kMult, tmp2); - - p0 = b(0, 0); - p1 = b(1, 0); - p2 = b(2, 0); - - // chi2 = (y-xb)^t . W . (y-xb) - TMatrixD tmp3(x, TMatrixD::kMult, b); - TMatrixD tmp4(y, TMatrixD::kMinus, tmp3); - TMatrixD chi2(tmp4, TMatrixD::kTransposeMult, tmp4); - - return chi2(0, 0); + return kTRUE; } } // namespace mft diff --git a/Detectors/ITSMFT/MFT/tracking/src/TrackParamMFT.cxx b/Detectors/ITSMFT/MFT/tracking/src/TrackParamMFT.cxx deleted file mode 100644 index 06f058c389d10..0000000000000 --- a/Detectors/ITSMFT/MFT/tracking/src/TrackParamMFT.cxx +++ /dev/null @@ -1,374 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackParamMFT.cxx -/// \brief Implementation of the MFT track parameters for internal use -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#include "MFTTracking/TrackParamMFT.h" - -#include <iomanip> -#include <iostream> - -#include <TMath.h> - -#include <FairMQLogger.h> - -namespace o2 -{ -namespace mft -{ - -using namespace std; - -//_________________________________________________________________________ -TrackParamMFT::TrackParamMFT(const TrackParamMFT& tp) - : mZ(tp.mZ), - mParameters(tp.mParameters), - mClusterPtr(tp.mClusterPtr), - mRemovable(tp.mRemovable), - mTrackChi2(tp.mTrackChi2), - mLocalChi2(tp.mLocalChi2) -{ - /// Copy constructor - if (tp.mCovariances) - mCovariances = std::make_unique<TMatrixD>(*(tp.mCovariances)); - if (tp.mPropagator) - mPropagator = std::make_unique<TMatrixD>(*(tp.mPropagator)); - if (tp.mExtrapParameters) - mExtrapParameters = std::make_unique<TMatrixD>(*(tp.mExtrapParameters)); - if (tp.mExtrapCovariances) - mExtrapCovariances = std::make_unique<TMatrixD>(*(tp.mExtrapCovariances)); - if (tp.mSmoothParameters) - mSmoothParameters = std::make_unique<TMatrixD>(*(tp.mSmoothParameters)); - if (tp.mSmoothCovariances) - mSmoothCovariances = std::make_unique<TMatrixD>(*(tp.mSmoothCovariances)); -} - -//_________________________________________________________________________ -TrackParamMFT& TrackParamMFT::operator=(const TrackParamMFT& tp) -{ - /// Assignment operator - if (this == &tp) - return *this; - - mZ = tp.mZ; - - mParameters = tp.mParameters; - - if (tp.mCovariances) { - if (mCovariances) - *mCovariances = *(tp.mCovariances); - else - mCovariances = std::make_unique<TMatrixD>(*(tp.mCovariances)); - } else - mCovariances.reset(); - - if (tp.mPropagator) { - if (mPropagator) - *mPropagator = *(tp.mPropagator); - else - mPropagator = std::make_unique<TMatrixD>(*(tp.mPropagator)); - } else - mPropagator.reset(); - - if (tp.mExtrapParameters) { - if (mExtrapParameters) - *mExtrapParameters = *(tp.mExtrapParameters); - else - mExtrapParameters = std::make_unique<TMatrixD>(*(tp.mExtrapParameters)); - } else - mExtrapParameters.reset(); - - if (tp.mExtrapCovariances) { - if (mExtrapCovariances) - *mExtrapCovariances = *(tp.mExtrapCovariances); - else - mExtrapCovariances = std::make_unique<TMatrixD>(*(tp.mExtrapCovariances)); - } else - mExtrapCovariances.reset(); - - if (tp.mSmoothParameters) { - if (mSmoothParameters) - *mSmoothParameters = *(tp.mSmoothParameters); - else - mSmoothParameters = std::make_unique<TMatrixD>(*(tp.mSmoothParameters)); - } else - mSmoothParameters.reset(); - - if (tp.mSmoothCovariances) { - if (mSmoothCovariances) - *mSmoothCovariances = *(tp.mSmoothCovariances); - else - mSmoothCovariances = std::make_unique<TMatrixD>(*(tp.mSmoothCovariances)); - } else - mSmoothCovariances.reset(); - - mClusterPtr = tp.mClusterPtr; - - mRemovable = tp.mRemovable; - - mTrackChi2 = tp.mTrackChi2; - mLocalChi2 = tp.mLocalChi2; - - return *this; -} - -//__________________________________________________________________________ -void TrackParamMFT::clear() -{ - /// clear memory - deleteCovariances(); - mPropagator.reset(); - mExtrapParameters.reset(); - mExtrapCovariances.reset(); - mSmoothParameters.reset(); - mSmoothCovariances.reset(); -} - -//__________________________________________________________________________ -const TMatrixD& TrackParamMFT::getCovariances() const -{ - /// Return the covariance matrix (create it before if needed) - if (!mCovariances) { - mCovariances = std::make_unique<TMatrixD>(5, 5); - mCovariances->Zero(); - } - return *mCovariances; -} - -//__________________________________________________________________________ -void TrackParamMFT::setCovariances(const TMatrixD& covariances) -{ - /// Set the covariance matrix - if (mCovariances) - *mCovariances = covariances; - else - mCovariances = std::make_unique<TMatrixD>(covariances); -} - -//__________________________________________________________________________ -void TrackParamMFT::setCovariances(const Double_t matrix[5][5]) -{ - /// Set the covariance matrix - if (mCovariances) - mCovariances->SetMatrixArray(&(matrix[0][0])); - else - mCovariances = std::make_unique<TMatrixD>(5, 5, &(matrix[0][0])); -} - -//__________________________________________________________________________ -void TrackParamMFT::setVariances(const Double_t matrix[5][5]) -{ - /// Set the diagonal terms of the covariance matrix (variances) - if (!mCovariances) - mCovariances = std::make_unique<TMatrixD>(5, 5); - mCovariances->Zero(); - for (Int_t i = 0; i < 5; i++) - (*mCovariances)(i, i) = matrix[i][i]; -} - -//__________________________________________________________________________ -void TrackParamMFT::deleteCovariances() -{ - /// Delete the covariance matrix - mCovariances.reset(); -} - -//__________________________________________________________________________ -const TMatrixD& TrackParamMFT::getPropagator() const -{ - /// Return the propagator (create it before if needed) - if (!mPropagator) { - mPropagator = std::make_unique<TMatrixD>(5, 5); - mPropagator->UnitMatrix(); - } - return *mPropagator; -} - -//__________________________________________________________________________ -void TrackParamMFT::resetPropagator() -{ - /// Reset the propagator - if (mPropagator) - mPropagator->UnitMatrix(); -} - -//__________________________________________________________________________ -void TrackParamMFT::updatePropagator(const TMatrixD& propagator) -{ - /// Update the propagator - if (mPropagator) - *mPropagator = TMatrixD(propagator, TMatrixD::kMult, *mPropagator); - else - mPropagator = std::make_unique<TMatrixD>(propagator); -} - -//__________________________________________________________________________ -const TMatrixD& TrackParamMFT::getExtrapParameters() const -{ - /// Return extrapolated parameters (create it before if needed) - if (!mExtrapParameters) { - mExtrapParameters = std::make_unique<TMatrixD>(5, 1); - mExtrapParameters->Zero(); - } - return *mExtrapParameters; -} - -//__________________________________________________________________________ -void TrackParamMFT::setExtrapParameters(const TMatrixD& extrapParameters) -{ - /// Set extrapolated parameters - if (mExtrapParameters) - *mExtrapParameters = extrapParameters; - else - mExtrapParameters = std::make_unique<TMatrixD>(extrapParameters); -} - -//__________________________________________________________________________ -const TMatrixD& TrackParamMFT::getExtrapCovariances() const -{ - /// Return the extrapolated covariance matrix (create it before if needed) - if (!mExtrapCovariances) { - mExtrapCovariances = std::make_unique<TMatrixD>(5, 5); - mExtrapCovariances->Zero(); - } - return *mExtrapCovariances; -} - -//__________________________________________________________________________ -void TrackParamMFT::setExtrapCovariances(const TMatrixD& extrapCovariances) -{ - /// Set the extrapolated covariance matrix - if (mExtrapCovariances) - *mExtrapCovariances = extrapCovariances; - else - mExtrapCovariances = std::make_unique<TMatrixD>(extrapCovariances); -} - -//__________________________________________________________________________ -const TMatrixD& TrackParamMFT::getSmoothParameters() const -{ - /// Return the smoothed parameters (create it before if needed) - if (!mSmoothParameters) { - mSmoothParameters = std::make_unique<TMatrixD>(5, 1); - mSmoothParameters->Zero(); - } - return *mSmoothParameters; -} - -//__________________________________________________________________________ -void TrackParamMFT::setSmoothParameters(const TMatrixD& smoothParameters) -{ - /// Set the smoothed parameters - if (mSmoothParameters) { - *mSmoothParameters = smoothParameters; - } else { - mSmoothParameters = std::make_unique<TMatrixD>(smoothParameters); - } -} - -//__________________________________________________________________________ -const TMatrixD& TrackParamMFT::getSmoothCovariances() const -{ - /// Return the smoothed covariance matrix (create it before if needed) - if (!mSmoothCovariances) { - mSmoothCovariances = std::make_unique<TMatrixD>(5, 5); - mSmoothCovariances->Zero(); - } - return *mSmoothCovariances; -} - -//__________________________________________________________________________ -void TrackParamMFT::setSmoothCovariances(const TMatrixD& smoothCovariances) -{ - /// Set the smoothed covariance matrix - if (mSmoothCovariances) { - *mSmoothCovariances = smoothCovariances; - } else { - mSmoothCovariances = std::make_unique<TMatrixD>(smoothCovariances); - } -} - -//__________________________________________________________________________ -Bool_t TrackParamMFT::isCompatibleTrackParamMFT(const TrackParamMFT& TrackParamMFT, Double_t sigma2Cut, Double_t& chi2) const -{ - /// Return kTRUE if the two set of track parameters are compatible within sigma2Cut - /// Set chi2 to the compatible chi2 value - /// Note that parameter covariances must exist for at least one set of parameters - /// Note also that if parameters are not given at the same Z, results will be meaningless - - // reset chi2 value - chi2 = 0.; - - // ckeck covariance matrices - if (!mCovariances && !TrackParamMFT.mCovariances) { - LOG(ERROR) << "Covariance matrix must exist for at least one set of parameters"; - return kFALSE; - } - - Double_t maxChi2 = 5. * sigma2Cut * sigma2Cut; // 5 degrees of freedom - - // check Z parameters - if (mZ != TrackParamMFT.mZ) { - LOG(WARN) << "Parameters are given at different Z position (" << mZ << " : " << TrackParamMFT.mZ - << "): results are meaningless"; - } - - // compute the parameter residuals - TMatrixD deltaParam(mParameters, TMatrixD::kMinus, TrackParamMFT.mParameters); - - // build the error matrix - TMatrixD weight(5, 5); - if (mCovariances) { - weight += *mCovariances; - } - if (TrackParamMFT.mCovariances) { - weight += *(TrackParamMFT.mCovariances); - } - - // invert the error matrix to get the parameter weights if possible - if (weight.Determinant() == 0) { - LOG(ERROR) << "Cannot compute the compatibility chi2"; - return kFALSE; - } - weight.Invert(); - - // compute the compatibility chi2 - TMatrixD tmp(deltaParam, TMatrixD::kTransposeMult, weight); - TMatrixD mChi2(tmp, TMatrixD::kMult, deltaParam); - - // set chi2 value - chi2 = mChi2(0, 0); - - // check compatibility - if (chi2 > maxChi2) { - return kFALSE; - } - - return kTRUE; -} - -//__________________________________________________________________________ -void TrackParamMFT::print() const -{ - /// Printing TrackParamMFT informations - LOG(INFO) << "TrackParamMFT: p =" << setw(5) << setprecision(3) << getP() - << " Tanl = " << setw(5) << setprecision(3) << getTanl() - << " phi = " << setw(5) << setprecision(3) << getPhi() - << " pz = " << setw(5) << setprecision(3) << getPz() - << " pt = " << setw(5) << setprecision(3) << getPt() - << " charge = " << setw(5) << setprecision(3) << getCharge() - << " chi2 = " << setw(5) << setprecision(3) << getTrackChi2() << endl; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/tracking/src/Tracker.cxx b/Detectors/ITSMFT/MFT/tracking/src/Tracker.cxx index 593b96763d1d4..1fe47aa36e96a 100644 --- a/Detectors/ITSMFT/MFT/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/MFT/tracking/src/Tracker.cxx @@ -15,10 +15,9 @@ #include "MFTTracking/Cluster.h" #include "MFTTracking/Cell.h" #include "MFTTracking/TrackCA.h" -#include "MFTTracking/TrackFitter.h" #include "DataFormatsMFT/TrackMFT.h" - #include "ReconstructionDataFormats/Track.h" + #include "Framework/Logger.h" namespace o2 @@ -26,231 +25,304 @@ namespace o2 namespace mft { -Tracker::Tracker(bool useMC) : mUseMC{useMC} {} +//_________________________________________________________________________________________________ +Tracker::Tracker(bool useMC) : mUseMC{useMC} +{ + mTrackFitter = std::make_unique<o2::mft::TrackFitter>(); +} + +//_________________________________________________________________________________________________ +void Tracker::setBz(Float_t bz) +{ + /// Configure track propagation + mBz = bz; + mTrackFitter->setBz(bz); +} + +//_________________________________________________________________________________________________ +void Tracker::initConfig(const MFTTrackingParam& trkParam) +{ + /// initialize from MFTTrackingParam (command line configuration parameters) + + mMinTrackPointsLTF = trkParam.MinTrackPointsLTF; + mMinTrackPointsCA = trkParam.MinTrackPointsCA; + mMinTrackStationsLTF = trkParam.MinTrackStationsLTF; + mMinTrackStationsCA = trkParam.MinTrackStationsCA; + mLTFclsRCut = trkParam.LTFclsRCut; + mROADclsRCut = trkParam.ROADclsRCut; + mLTFseed2BinWin = trkParam.LTFseed2BinWin; + mLTFinterBinWin = trkParam.LTFinterBinWin; + + mRBins = trkParam.RBins; + mPhiBins = trkParam.PhiBins; + mRPhiBins = trkParam.RBins * trkParam.PhiBins; + if (mRPhiBins > constants::index_table::MaxRPhiBins) { + LOG(WARN) << "To many RPhiBins for this configuration!"; + mRPhiBins = constants::index_table::MaxRPhiBins; + mRBins = sqrt(constants::index_table::MaxRPhiBins); + mPhiBins = sqrt(constants::index_table::MaxRPhiBins); + LOG(WARN) << "Using instead RBins " << mRBins << " and PhiBins " << mPhiBins; + } + mRBinSize = (constants::index_table::RMax - constants::index_table::RMin) / mRBins; + mPhiBinSize = (constants::index_table::PhiMax - constants::index_table::PhiMin) / mPhiBins; +} + +//_________________________________________________________________________________________________ +void Tracker::initialize() +{ + /// calculate Look-Up-Table of the R-Phi bins projection from one layer to another + /// layer1 + global R-Phi bin index ---> layer2 + R bin index + Phi bin index + + Float_t dz, x, y, r, phi, x_proj, y_proj, r_proj, phi_proj; + Int_t binIndex1, binIndex2, binIndex2S, binR_proj, binPhi_proj; + + for (Int_t layer1 = 0; layer1 < (constants::mft::LayersNumber - 1); ++layer1) { + + for (Int_t iRBin = 0; iRBin < mRBins; ++iRBin) { + + r = (iRBin + 0.5) * mRBinSize + constants::index_table::RMin; + + for (Int_t iPhiBin = 0; iPhiBin < mPhiBins; ++iPhiBin) { + + phi = (iPhiBin + 0.5) * mPhiBinSize + constants::index_table::PhiMin; + + binIndex1 = getBinIndex(iRBin, iPhiBin); + + x = r * TMath::Cos(phi); + y = r * TMath::Sin(phi); + + for (Int_t layer2 = (layer1 + 1); layer2 < constants::mft::LayersNumber; ++layer2) { + + dz = constants::mft::LayerZCoordinate()[layer2] - constants::mft::LayerZCoordinate()[layer1]; + x_proj = x + dz * x * constants::mft::InverseLayerZCoordinate()[layer1]; + y_proj = y + dz * y * constants::mft::InverseLayerZCoordinate()[layer1]; + auto clsPoint2D = math_utils::Point2D<Float_t>(x_proj, y_proj); + r_proj = clsPoint2D.R(); + phi_proj = clsPoint2D.Phi(); + o2::math_utils::bringTo02PiGen(phi_proj); + + binR_proj = getRBinIndex(r_proj); + binPhi_proj = getPhiBinIndex(phi_proj); + + int binRS, binPhiS; + + int binwRS = mLTFseed2BinWin; + int binhwRS = binwRS / 2; + + int binwPhiS = mLTFseed2BinWin; + int binhwPhiS = binwPhiS / 2; + for (Int_t iR = 0; iR < binwRS; ++iR) { + binRS = binR_proj + (iR - binhwRS); + if (binRS < 0) { + continue; + } + + for (Int_t iPhi = 0; iPhi < binwPhiS; ++iPhi) { + binPhiS = binPhi_proj + (iPhi - binhwPhiS); + if (binPhiS < 0) { + continue; + } + + binIndex2S = getBinIndex(binRS, binPhiS); + mBinsS[layer1][layer2 - 1][binIndex1].emplace_back(binIndex2S); + } + } + + int binR, binPhi; + + int binwR = mLTFinterBinWin; + int binhwR = binwR / 2; + + int binwPhi = mLTFinterBinWin; + int binhwPhi = binwPhi / 2; + + for (Int_t iR = 0; iR < binwR; ++iR) { + binR = binR_proj + (iR - binhwR); + if (binR < 0) { + continue; + } + + for (Int_t iPhi = 0; iPhi < binwPhi; ++iPhi) { + binPhi = binPhi_proj + (iPhi - binhwPhi); + if (binPhi < 0) { + continue; + } + + binIndex2 = getBinIndex(binR, binPhi); + mBins[layer1][layer2 - 1][binIndex1].emplace_back(binIndex2); + } + } + + } // end loop layer2 + } // end loop PhiBinIndex + } // end loop RBinIndex + } // end loop layer1 + + mRoad.initialize(); +} + +//_________________________________________________________________________________________________ void Tracker::clustersToTracks(ROframe& event, std::ostream& timeBenchmarkOutputStream) { mTracks.clear(); mTrackLabels.clear(); findTracks(event); + fitTracks(event); } +//_________________________________________________________________________________________________ void Tracker::findTracks(ROframe& event) { - //computeCells(event); findTracksLTF(event); findTracksCA(event); } -void Tracker::computeCells(ROframe& event) -{ - MCCompLabel mcCompLabel; - Int_t layer1, layer2; - Int_t nClsInLayer1, nClsInLayer2; - Int_t disk1, disk2; - Int_t binR_proj, binPhi_proj, bin; - Int_t binIndex, clsMinIndex, clsMaxIndex; - std::array<Int_t, 3> binsR, binsPhi; - Int_t cellId = 0; - for (layer1 = 0; layer1 < (constants::mft::LayersNumber - 1); ++layer1) { - nClsInLayer1 = event.getClustersInLayer(layer1).size(); - disk1 = layer1 / 2; - layer2 = layer1 + 1; // only (L+1) tracklets ! - disk2 = layer2 / 2; - nClsInLayer2 = event.getClustersInLayer(layer2).size(); - for (Int_t clsLayer1 = 0; clsLayer1 < nClsInLayer1; ++clsLayer1) { - const Cluster& cluster1 = event.getClustersInLayer(layer1).at(clsLayer1); - // project to next layer and get the bin index in R and Phi - getRPhiProjectionBin(cluster1, layer1, layer2, binR_proj, binPhi_proj); - // define the search window in bins x bins (3x3, 5x5, etc.) - for (Int_t i = 0; i < constants::index_table::LTFinterBinWin; ++i) { - binsR[i] = binR_proj + (i - constants::index_table::LTFinterBinWin / 2); - binsPhi[i] = binPhi_proj + (i - constants::index_table::LTFinterBinWin / 2); - } - // loop over the bins in the search window - for (auto binR : binsR) { - for (auto binPhi : binsPhi) { - // the global bin index - bin = constants::index_table::getBinIndex(binR, binPhi); - if (!getBinClusterRange(event, layer2, bin, clsMinIndex, clsMaxIndex)) { - continue; - } - for (Int_t clsLayer2 = clsMinIndex; clsLayer2 <= clsMaxIndex; ++clsLayer2) { - event.addCellToLayer(layer1, layer2, clsLayer1, clsLayer2, cellId++); - } // end clusters bin layer2 - } // end binPhi - } // end binR - } // end clusters layer1 - } // end layers -} - +//_________________________________________________________________________________________________ void Tracker::findTracksLTF(ROframe& event) { // find (high momentum) tracks by the Linear Track Finder (LTF) method MCCompLabel mcCompLabel; Int_t layer1, layer2, nPointDisks; - Int_t nClsInLayer1, nClsInLayer2, nClsInLayer; Int_t binR_proj, binPhi_proj, bin; Int_t binIndex, clsMinIndex, clsMaxIndex, clsMinIndexS, clsMaxIndexS; - Float_t dR, dRmin, dRcut = constants::mft::LTFclsRCut; - std::vector<Int_t> binsR, binsPhi, binsRS, binsPhiS; - Bool_t hasDisk[constants::mft::DisksNumber], newPoint, seed = kTRUE; + Int_t extClsIndex; + Float_t dR2, dR2min, dR2cut = mLTFclsR2Cut; + Bool_t hasDisk[constants::mft::DisksNumber], newPoint, seed; - binsRS.resize(constants::index_table::LTFseed2BinWin); - binsPhiS.resize(constants::index_table::LTFseed2BinWin); + Int_t clsInLayer1, clsInLayer2, clsInLayer; - binsR.resize(constants::index_table::LTFinterBinWin); - binsPhi.resize(constants::index_table::LTFinterBinWin); + Int_t nPoints; + TrackElement trackPoints[constants::mft::LayersNumber]; Int_t step = 0; - + seed = kTRUE; layer1 = 0; while (seed) { - if (step == 0) { - layer2 = constants::mft::LayersNumber - 1; - } else { - layer2--; - } - + layer2 = (step == 0) ? (constants::mft::LayersNumber - 1) : (layer2 - 1); step++; - if (layer2 < layer1 + (constants::mft::MinTrackPoints - 1)) { + if (layer2 < layer1 + (mMinTrackPointsLTF - 1)) { ++layer1; - if (layer1 > (constants::mft::LayersNumber - (constants::mft::MinTrackPoints - 1))) { + if (layer1 > (constants::mft::LayersNumber - (mMinTrackPointsLTF - 1))) { break; } step = 0; continue; } - nClsInLayer1 = event.getClustersInLayer(layer1).size(); - nClsInLayer2 = event.getClustersInLayer(layer2).size(); - - for (Int_t clsLayer1 = 0; clsLayer1 < nClsInLayer1; ++clsLayer1) { - if (event.isClusterUsed(layer1, clsLayer1)) { + for (std::vector<Cluster>::iterator it1 = event.getClustersInLayer(layer1).begin(); it1 != event.getClustersInLayer(layer1).end(); ++it1) { + Cluster& cluster1 = *it1; + if (cluster1.getUsed()) { continue; } - const Cluster& cluster1 = event.getClustersInLayer(layer1)[clsLayer1]; - - // project to the second seed layer and get the bin index in R and Phi - getRPhiProjectionBin(cluster1, layer1, layer2, binR_proj, binPhi_proj); - // define the search window in bins x bins (3x3, 5x5, etc.) - for (Int_t i = 0; i < constants::index_table::LTFseed2BinWin; ++i) { - binsRS[i] = binR_proj + (i - constants::index_table::LTFseed2BinWin / 2); - binsPhiS[i] = binPhi_proj + (i - constants::index_table::LTFseed2BinWin / 2); - } + clsInLayer1 = it1 - event.getClustersInLayer(layer1).begin(); + // loop over the bins in the search window - for (auto binRS : binsRS) { - for (auto binPhiS : binsPhiS) { - // the global bin index - bin = constants::index_table::getBinIndex(binRS, binPhiS); - if (!getBinClusterRange(event, layer2, bin, clsMinIndexS, clsMaxIndexS)) { + for (auto& binS : mBinsS[layer1][layer2 - 1][cluster1.indexTableBin]) { + + getBinClusterRange(event, layer2, binS, clsMinIndexS, clsMaxIndexS); + + for (std::vector<Cluster>::iterator it2 = (event.getClustersInLayer(layer2).begin() + clsMinIndexS); it2 != (event.getClustersInLayer(layer2).begin() + clsMaxIndexS + 1); ++it2) { + Cluster& cluster2 = *it2; + if (cluster2.getUsed()) { continue; } - for (Int_t clsLayer2 = clsMinIndexS; clsLayer2 <= clsMaxIndexS; ++clsLayer2) { - if (event.isClusterUsed(layer2, clsLayer2)) { - continue; - } - const Cluster& cluster2 = event.getClustersInLayer(layer2)[clsLayer2]; + clsInLayer2 = it2 - event.getClustersInLayer(layer2).begin(); - for (Int_t i = 0; i < (constants::mft::DisksNumber); i++) { - hasDisk[i] = kFALSE; - } + // start a TrackLTF + nPoints = 0; - hasDisk[layer1 / 2] = kTRUE; - hasDisk[layer2 / 2] = kTRUE; + // add the first seed point + trackPoints[nPoints].layer = layer1; + trackPoints[nPoints].idInLayer = clsInLayer1; + nPoints++; - // start a track LTF - event.addTrackLTF(); + // intermediate layers + for (Int_t layer = (layer1 + 1); layer <= (layer2 - 1); ++layer) { - // add the first seed-point - mcCompLabel = mUseMC ? event.getClusterLabels(layer1, cluster1.clusterId) : MCCompLabel(); newPoint = kTRUE; - event.getCurrentTrackLTF().setPoint(cluster1.xCoordinate, cluster1.yCoordinate, cluster1.zCoordinate, layer1, clsLayer1, mcCompLabel, newPoint); - - for (Int_t layer = (layer1 + 1); layer <= (layer2 - 1); ++layer) { - nClsInLayer = event.getClustersInLayer(layer).size(); + // loop over the bins in the search window + dR2min = dR2cut; + for (auto& bin : mBins[layer1][layer - 1][cluster1.indexTableBin]) { - newPoint = kTRUE; + getBinClusterRange(event, layer, bin, clsMinIndex, clsMaxIndex); - // project to the intermediate layer and get the bin index in R and Phi - getRPhiProjectionBin(cluster1, layer1, layer, binR_proj, binPhi_proj); + for (std::vector<Cluster>::iterator it = (event.getClustersInLayer(layer).begin() + clsMinIndex); it != (event.getClustersInLayer(layer).begin() + clsMaxIndex + 1); ++it) { + Cluster& cluster = *it; + if (cluster.getUsed()) { + continue; + } + clsInLayer = it - event.getClustersInLayer(layer).begin(); - // define the search window in bins x bins (3x3, 5x5, etc.) - for (Int_t i = 0; i < constants::index_table::LTFinterBinWin; ++i) { - binsR[i] = binR_proj + (i - constants::index_table::LTFinterBinWin / 2); - binsPhi[i] = binPhi_proj + (i - constants::index_table::LTFinterBinWin / 2); - } - // loop over the bins in the search window - dRmin = dRcut; - for (auto binR : binsR) { - for (auto binPhi : binsPhi) { - // the global bin index - bin = constants::index_table::getBinIndex(binR, binPhi); - if (!getBinClusterRange(event, layer, bin, clsMinIndex, clsMaxIndex)) { - continue; - } - for (Int_t clsLayer = clsMinIndex; clsLayer <= clsMaxIndex; ++clsLayer) { - if (event.isClusterUsed(layer, clsLayer)) { - continue; - } - const Cluster& cluster = event.getClustersInLayer(layer)[clsLayer]; - - dR = getDistanceToSeed(cluster1, cluster2, cluster); - // retain the closest point within a radius dRcut - if (dR >= dRmin) { - continue; - } - dRmin = dR; - - hasDisk[layer / 2] = kTRUE; - mcCompLabel = mUseMC ? event.getClusterLabels(layer, cluster.clusterId) : MCCompLabel(); - event.getCurrentTrackLTF().setPoint(cluster.xCoordinate, cluster.yCoordinate, cluster.zCoordinate, layer, clsLayer, mcCompLabel, newPoint); - } // end clusters bin intermediate layer - } // end intermediate layers - } // end binPhi - } // end binR - - // add the second seed-point - mcCompLabel = mUseMC ? event.getClusterLabels(layer2, cluster2.clusterId) : MCCompLabel(); - newPoint = kTRUE; - event.getCurrentTrackLTF().setPoint(cluster2.xCoordinate, cluster2.yCoordinate, cluster2.zCoordinate, layer2, clsLayer2, mcCompLabel, newPoint); + dR2 = getDistanceToSeed(cluster1, cluster2, cluster); + // retain the closest point within a radius dR2cut + if (dR2 >= dR2min) { + continue; + } + dR2min = dR2; - // keep only tracks fulfilling the minimum length condition - if (event.getCurrentTrackLTF().getNPoints() < constants::mft::MinTrackPoints) { - event.removeCurrentTrackLTF(); - continue; - } - nPointDisks = 0; - for (Int_t disk = 0; disk < (constants::mft::DisksNumber); ++disk) { - if (hasDisk[disk]) - ++nPointDisks; - } - if (nPointDisks < constants::mft::MinTrackPoints) { - event.removeCurrentTrackLTF(); - continue; - } - // mark the used clusters - //Int_t lay, layMin = 10, layMax = -1; - for (Int_t point = 0; point < event.getCurrentTrackLTF().getNPoints(); ++point) { - event.markUsedCluster(event.getCurrentTrackLTF().getLayers()[point], event.getCurrentTrackLTF().getClustersId()[point]); - //lay = event.getCurrentTrackLTF().getLayers()[point]; - //layMin = (lay < layMin) ? lay : layMin; - //layMax = (lay > layMax) ? lay : layMax; + if (newPoint) { + trackPoints[nPoints].layer = layer; + trackPoints[nPoints].idInLayer = clsInLayer; + nPoints++; + } + // retain only the closest point in DistanceToSeed + newPoint = false; + } // end clusters bin intermediate layer + } // end intermediate layers + } // end binRPhi + + // add the second seed point + trackPoints[nPoints].layer = layer2; + trackPoints[nPoints].idInLayer = clsInLayer2; + nPoints++; + + // keep only tracks fulfilling the minimum length condition + if (nPoints < mMinTrackPointsLTF) { + continue; + } + for (Int_t i = 0; i < (constants::mft::DisksNumber); i++) { + hasDisk[i] = kFALSE; + } + for (Int_t point = 0; point < nPoints; ++point) { + auto layer = trackPoints[point].layer; + hasDisk[layer / 2] = kTRUE; + } + nPointDisks = 0; + for (Int_t disk = 0; disk < (constants::mft::DisksNumber); ++disk) { + if (hasDisk[disk]) { + ++nPointDisks; } + } + if (nPointDisks < mMinTrackStationsLTF) { + continue; + } - } // end seed clusters bin layer2 - } // end binPhi - } // end binR - } // end clusters layer1 + // add a new TrackLTF + event.addTrackLTF(); + for (Int_t point = 0; point < nPoints; ++point) { + auto layer = trackPoints[point].layer; + auto clsInLayer = trackPoints[point].idInLayer; + Cluster& cluster = event.getClustersInLayer(layer)[clsInLayer]; + mcCompLabel = mUseMC ? event.getClusterLabels(layer, cluster.clusterId) : MCCompLabel(); + extClsIndex = event.getClusterExternalIndex(layer, cluster.clusterId); + event.getCurrentTrackLTF().setPoint(cluster, layer, clsInLayer, mcCompLabel, extClsIndex); + // mark the used clusters + cluster.setUsed(true); + } + } // end seed clusters bin layer2 + } // end binRPhi + } // end clusters layer1 } // end seeding } +//_________________________________________________________________________________________________ void Tracker::findTracksCA(ROframe& event) { // layers: 0, 1, 2, ..., 9 @@ -261,200 +333,180 @@ void Tracker::findTracksCA(ROframe& event) // 3 with 8, 9 Int_t layer1Min = 0, layer1Max = 3; Int_t layer2Min[4] = {6, 6, 8, 8}; - Int_t layer2Max[4] = {9, 9, 9, 9}; + constexpr Int_t layer2Max = constants::mft::LayersNumber - 1; MCCompLabel mcCompLabel; Int_t roadId, nPointDisks; - Int_t nClsInLayer1, nClsInLayer2, nClsInLayer; Int_t binR_proj, binPhi_proj, bin; Int_t binIndex, clsMinIndex, clsMaxIndex, clsMinIndexS, clsMaxIndexS; - Float_t dR, dRcut = constants::mft::ROADclsRCut; - std::vector<Int_t> binsR, binsPhi, binsRS, binsPhiS; - Bool_t hasDisk[constants::mft::DisksNumber], newPoint; + Float_t dR2, dR2cut = mROADclsR2Cut; + Bool_t hasDisk[constants::mft::DisksNumber]; - binsRS.resize(constants::index_table::LTFseed2BinWin); - binsPhiS.resize(constants::index_table::LTFseed2BinWin); + Int_t clsInLayer1, clsInLayer2, clsInLayer; - binsR.resize(constants::index_table::LTFinterBinWin); - binsPhi.resize(constants::index_table::LTFinterBinWin); + Int_t nPoints; + std::vector<TrackElement> roadPoints; roadId = 0; for (Int_t layer1 = layer1Min; layer1 <= layer1Max; ++layer1) { - nClsInLayer1 = event.getClustersInLayer(layer1).size(); - - for (Int_t layer2 = layer2Max[layer1]; layer2 >= layer2Min[layer1]; --layer2) { + for (Int_t layer2 = layer2Max; layer2 >= layer2Min[layer1]; --layer2) { - nClsInLayer2 = event.getClustersInLayer(layer2).size(); - - for (Int_t clsLayer1 = 0; clsLayer1 < nClsInLayer1; ++clsLayer1) { - - if (event.isClusterUsed(layer1, clsLayer1)) { + for (std::vector<Cluster>::iterator it1 = event.getClustersInLayer(layer1).begin(); it1 != event.getClustersInLayer(layer1).end(); ++it1) { + Cluster& cluster1 = *it1; + if (cluster1.getUsed()) { continue; } - const Cluster& cluster1 = event.getClustersInLayer(layer1)[clsLayer1]; - - // project to the second seed layer and get the bin index in R and Phi - getRPhiProjectionBin(cluster1, layer1, layer2, binR_proj, binPhi_proj); - // define the search window in bins x bins (3x3, 5x5, etc.) - for (Int_t i = 0; i < constants::index_table::LTFseed2BinWin; ++i) { - binsRS[i] = binR_proj + (i - constants::index_table::LTFseed2BinWin / 2); - binsPhiS[i] = binPhi_proj + (i - constants::index_table::LTFseed2BinWin / 2); - } + clsInLayer1 = it1 - event.getClustersInLayer(layer1).begin(); // loop over the bins in the search window - for (auto binRS : binsRS) { - for (auto binPhiS : binsPhiS) { - // the global bin index - bin = constants::index_table::getBinIndex(binRS, binPhiS); - if (!getBinClusterRange(event, layer2, bin, clsMinIndexS, clsMaxIndexS)) { + for (auto& binS : mBinsS[layer1][layer2 - 1][cluster1.indexTableBin]) { + + getBinClusterRange(event, layer2, binS, clsMinIndexS, clsMaxIndexS); + + for (std::vector<Cluster>::iterator it2 = (event.getClustersInLayer(layer2).begin() + clsMinIndexS); it2 != (event.getClustersInLayer(layer2).begin() + clsMaxIndexS + 1); ++it2) { + Cluster& cluster2 = *it2; + if (cluster2.getUsed()) { continue; } - for (Int_t clsLayer2 = clsMinIndexS; clsLayer2 <= clsMaxIndexS; ++clsLayer2) { - if (event.isClusterUsed(layer2, clsLayer2)) { - continue; - } - const Cluster& cluster2 = event.getClustersInLayer(layer2)[clsLayer2]; + clsInLayer2 = it2 - event.getClustersInLayer(layer2).begin(); - for (Int_t i = 0; i < (constants::mft::DisksNumber); i++) { - hasDisk[i] = kFALSE; - } + // start a road + roadPoints.clear(); - hasDisk[layer1 / 2] = kTRUE; - hasDisk[layer2 / 2] = kTRUE; + // add the first seed point + roadPoints.emplace_back(layer1, clsInLayer1); - // start a road - event.addRoad(); + for (Int_t layer = (layer1 + 1); layer <= (layer2 - 1); ++layer) { - // add the 1st/2nd road points - mcCompLabel = mUseMC ? event.getClusterLabels(layer1, cluster1.clusterId) : MCCompLabel(); - newPoint = kTRUE; - event.getCurrentRoad().setPoint(cluster1.xCoordinate, cluster1.yCoordinate, cluster1.zCoordinate, layer1, clsLayer1, mcCompLabel, newPoint); + // loop over the bins in the search window + for (auto& bin : mBins[layer1][layer - 1][cluster1.indexTableBin]) { - for (Int_t layer = (layer1 + 1); layer <= (layer2 - 1); ++layer) { + getBinClusterRange(event, layer, bin, clsMinIndex, clsMaxIndex); - nClsInLayer = event.getClustersInLayer(layer).size(); + for (std::vector<Cluster>::iterator it = (event.getClustersInLayer(layer).begin() + clsMinIndex); it != (event.getClustersInLayer(layer).begin() + clsMaxIndex + 1); ++it) { + Cluster& cluster = *it; + if (cluster.getUsed()) { + continue; + } + clsInLayer = it - event.getClustersInLayer(layer).begin(); - // project to the intermediate layer and get the bin index in R and Phi - getRPhiProjectionBin(cluster1, layer1, layer, binR_proj, binPhi_proj); - // define the search window in bins x bins (3x3, 5x5, etc.) - for (Int_t i = 0; i < constants::index_table::LTFinterBinWin; ++i) { - binsR[i] = binR_proj + (i - constants::index_table::LTFinterBinWin / 2); - binsPhi[i] = binPhi_proj + (i - constants::index_table::LTFinterBinWin / 2); - } + dR2 = getDistanceToSeed(cluster1, cluster2, cluster); + // add all points within a radius dR2cut + if (dR2 >= dR2cut) { + continue; + } - // loop over the bins in the search window - for (auto binR : binsR) { - for (auto binPhi : binsPhi) { - // the global bin index - bin = constants::index_table::getBinIndex(binR, binPhi); - if (!getBinClusterRange(event, layer, bin, clsMinIndex, clsMaxIndex)) { - continue; - } - for (Int_t clsLayer = clsMinIndex; clsLayer <= clsMaxIndex; ++clsLayer) { - if (event.isClusterUsed(layer, clsLayer)) { - continue; - } - const Cluster& cluster = event.getClustersInLayer(layer)[clsLayer]; - - dR = getDistanceToSeed(cluster1, cluster2, cluster); - // add all points within a radius dRcut - if (dR >= dRcut) { - continue; - } - - hasDisk[layer / 2] = kTRUE; - mcCompLabel = mUseMC ? event.getClusterLabels(layer, cluster.clusterId) : MCCompLabel(); - newPoint = kTRUE; - event.getCurrentRoad().setPoint(cluster.xCoordinate, cluster.yCoordinate, cluster.zCoordinate, layer, clsLayer, mcCompLabel, newPoint); - - } // end clusters bin intermediate layer - } // end intermediate layers - } // end binPhi - } // end binR - - // add the second seed-point - mcCompLabel = mUseMC ? event.getClusterLabels(layer2, cluster2.clusterId) : MCCompLabel(); - newPoint = kTRUE; - event.getCurrentRoad().setPoint(cluster2.xCoordinate, cluster2.yCoordinate, cluster2.zCoordinate, layer2, clsLayer2, mcCompLabel, newPoint); - - // keep only roads fulfilling the minimum length condition - if (event.getCurrentRoad().getNPoints() < constants::mft::MinTrackPoints) { - event.removeCurrentRoad(); - continue; - } - nPointDisks = 0; - for (Int_t disk = 0; disk < (constants::mft::DisksNumber); ++disk) { - if (hasDisk[disk]) - ++nPointDisks; - } - if (nPointDisks < constants::mft::MinTrackPoints) { - event.removeCurrentRoad(); - continue; + roadPoints.emplace_back(layer, clsInLayer); + + } // end clusters bin intermediate layer + } // end intermediate layers + } // end binR + + // add the second seed point + roadPoints.emplace_back(layer2, clsInLayer2); + nPoints = roadPoints.size(); + + // keep only roads fulfilling the minimum length condition + if (nPoints < mMinTrackPointsCA) { + continue; + } + for (Int_t i = 0; i < (constants::mft::DisksNumber); i++) { + hasDisk[i] = kFALSE; + } + for (Int_t point = 0; point < nPoints; ++point) { + auto layer = roadPoints[point].layer; + hasDisk[layer / 2] = kTRUE; + } + nPointDisks = 0; + for (Int_t disk = 0; disk < (constants::mft::DisksNumber); ++disk) { + if (hasDisk[disk]) { + ++nPointDisks; } - event.getCurrentRoad().setNDisks(nPointDisks); - event.getCurrentRoad().setRoadId(roadId); - ++roadId; - - computeCellsInRoad(event.getCurrentRoad()); - runForwardInRoad(event); - runBackwardInRoad(event); - - } // end clusters bin layer2 - } // end binPhiS - } // end binRS - } // end clusters in layer1 - } // end layer2 - } // end layer1 + } + if (nPointDisks < mMinTrackStationsCA) { + continue; + } + + mRoad.reset(); + for (Int_t point = 0; point < nPoints; ++point) { + auto layer = roadPoints[point].layer; + auto clsInLayer = roadPoints[point].idInLayer; + mRoad.setPoint(layer, clsInLayer); + } + mRoad.setRoadId(roadId); + ++roadId; + + computeCellsInRoad(event); + runForwardInRoad(); + runBackwardInRoad(event); + + } // end clusters in layer2 + } // end binRPhi + } // end clusters in layer1 + } // end layer2 + } // end layer1 } -void Tracker::computeCellsInRoad(Road& road) +//_________________________________________________________________________________________________ +void Tracker::computeCellsInRoad(ROframe& event) { Int_t layer1, layer1min, layer1max, layer2, layer2min, layer2max; Int_t nPtsInLayer1, nPtsInLayer2; - Int_t clsLayer1, clsLayer2; + Int_t clsInLayer1, clsInLayer2; + Int_t cellId; Bool_t noCell; - road.getLength(layer1min, layer1max); + mRoad.getLength(layer1min, layer1max); --layer1max; - Int_t cellId = 0; for (layer1 = layer1min; layer1 <= layer1max; ++layer1) { + + cellId = 0; + layer2min = layer1 + 1; layer2max = std::min(layer1 + (constants::mft::DisksNumber - isDiskFace(layer1)), constants::mft::LayersNumber - 1); - nPtsInLayer1 = road.getNPointsInLayer(layer1); + + nPtsInLayer1 = mRoad.getNPointsInLayer(layer1); + for (Int_t point1 = 0; point1 < nPtsInLayer1; ++point1) { - clsLayer1 = road.getClustersIdInLayer(layer1)[point1]; + + clsInLayer1 = mRoad.getClustersIdInLayer(layer1)[point1]; + layer2 = layer2min; + noCell = kTRUE; while (noCell && (layer2 <= layer2max)) { - nPtsInLayer2 = road.getNPointsInLayer(layer2); + + nPtsInLayer2 = mRoad.getNPointsInLayer(layer2); /* if (nPtsInLayer2 > 1) { - LOG(INFO) << "BV===== more than one point in road " << road.getRoadId() << " in layer " << layer2 << " : " << nPtsInLayer2 << "\n"; + LOG(INFO) << "BV===== more than one point in road " << mRoad.getRoadId() << " in layer " << layer2 << " : " << nPtsInLayer2 << "\n"; } */ for (Int_t point2 = 0; point2 < nPtsInLayer2; ++point2) { - clsLayer2 = road.getClustersIdInLayer(layer2)[point2]; + + clsInLayer2 = mRoad.getClustersIdInLayer(layer2)[point2]; + noCell = kFALSE; // create a cell - road.addCellToLayer(layer1, layer2, clsLayer1, clsLayer2, cellId++); + addCellToCurrentRoad(event, layer1, layer2, clsInLayer1, clsInLayer2, cellId); } // end points in layer2 ++layer2; + } // end while(noCell && (layer2 <= layer2max)) } // end points in layer1 } // end layer1 } -void Tracker::runForwardInRoad(ROframe& event) +//_________________________________________________________________________________________________ +void Tracker::runForwardInRoad() { Int_t layerR, layerL, icellR, icellL; Int_t iter = 0; Bool_t levelChange = kTRUE; - Road& road = event.getCurrentRoad(); - while (levelChange) { levelChange = kFALSE; @@ -463,148 +515,115 @@ void Tracker::runForwardInRoad(ROframe& event) // R = right, L = left for (layerL = 0; layerL < (constants::mft::LayersNumber - 2); ++layerL) { - for (icellL = 0; icellL < road.getCellsInLayer(layerL).size(); ++icellL) { - const Cell& cellL = road.getCellsInLayer(layerL)[icellL]; + for (icellL = 0; icellL < mRoad.getCellsInLayer(layerL).size(); ++icellL) { - if (cellL.getLevel() == 0) { - continue; - } + Cell& cellL = mRoad.getCellsInLayer(layerL)[icellL]; layerR = cellL.getSecondLayerId(); - if (layerR >= (constants::mft::LayersNumber - 1)) { + + if (layerR == (constants::mft::LayersNumber - 1)) { continue; } - for (icellR = 0; icellR < road.getCellsInLayer(layerR).size(); ++icellR) { - const Cell& cellR = road.getCellsInLayer(layerR)[icellR]; + for (icellR = 0; icellR < mRoad.getCellsInLayer(layerR).size(); ++icellR) { - if (cellR.getLevel() == 0) { - continue; - } - if ((cellL.getLevel() == cellR.getLevel()) && getCellsConnect(event, cellL, cellR)) { + Cell& cellR = mRoad.getCellsInLayer(layerR)[icellR]; + + if ((cellL.getLevel() == cellR.getLevel()) && getCellsConnect(cellL, cellR)) { if (iter == 1) { - road.addRightNeighbourToCell(layerL, icellL, layerR, icellR); - road.addLeftNeighbourToCell(layerR, icellR, layerL, icellL); + mRoad.addRightNeighbourToCell(layerL, icellL, layerR, icellR); + mRoad.addLeftNeighbourToCell(layerR, icellR, layerL, icellL); } - road.incrementCellLevel(layerR, icellR); + mRoad.incrementCellLevel(layerR, icellR); levelChange = kTRUE; + } // end matching cells } // end loop cellR } // end loop cellL } // end loop layer - updateCellStatusInRoad(road); + updateCellStatusInRoad(); - } // end while (step) + } // end while (levelChange) } +//_________________________________________________________________________________________________ void Tracker::runBackwardInRoad(ROframe& event) { - if (mMaxCellLevel == 1) + if (mMaxCellLevel == 1) { return; // we have only isolated cells + } Bool_t addCellToNewTrack, hasDisk[constants::mft::DisksNumber]; - Int_t iSelectChisquare, iSelectDeviation, lastCellLayer, lastCellId; - Int_t icell, layerC, cellIdC, nPointDisks; - Float_t chisquarePrev, deviationPrev, deviation, chisquare; + Int_t lastCellLayer, lastCellId, icell; + Int_t cellId, layerC, cellIdC, layerRC, cellIdRC, layerL, cellIdL; + Int_t nPointDisks; + Float_t deviationPrev, deviation; - // start layer Int_t minLayer = 6; Int_t maxLayer = 8; - Road& road = event.getCurrentRoad(); + Int_t nCells; + TrackElement trackCells[constants::mft::LayersNumber - 1]; for (Int_t layer = maxLayer; layer >= minLayer; --layer) { - for (icell = 0; icell < road.getCellsInLayer(layer).size(); ++icell) { - if (road.getCellLevel(layer, icell) == 0) { - continue; - } - if (road.isCellUsed(layer, icell)) { - continue; - } - if (road.getCellLevel(layer, icell) < (constants::mft::MinTrackPoints - 1)) { + for (cellId = 0; cellId < mRoad.getCellsInLayer(layer).size(); ++cellId) { + + if (mRoad.isCellUsed(layer, cellId) || (mRoad.getCellLevel(layer, cellId) < (mMinTrackPointsCA - 1))) { continue; } - // start a track CA - event.addTrackCA(); - event.getCurrentTrackCA().setRoadId(road.getRoadId()); - if (addCellToCurrentTrackCA(layer, icell, event)) { - road.setCellUsed(layer, icell, kTRUE); - } + // start a TrackCA + nCells = 0; - // add cells to new track + trackCells[nCells].layer = layer; + trackCells[nCells].idInLayer = cellId; + nCells++; + + // add cells to the new track addCellToNewTrack = kTRUE; while (addCellToNewTrack) { - Int_t layerRC = event.getCurrentTrackCA().getCellsLayer()[event.getCurrentTrackCA().getNCells() - 1]; - Int_t cellIdRC = event.getCurrentTrackCA().getCellsId()[event.getCurrentTrackCA().getNCells() - 1]; - const Cell& cellRC = road.getCellsInLayer(layerRC)[cellIdRC]; - addCellToNewTrack = kFALSE; - // find the left neighbor giving the smalles chisquare - iSelectChisquare = 0; - chisquarePrev = 0.; + layerRC = trackCells[nCells - 1].layer; + cellIdRC = trackCells[nCells - 1].idInLayer; - // ... or + const Cell& cellRC = mRoad.getCellsInLayer(layerRC)[cellIdRC]; - // find the left neighbor giving the smallest deviation - iSelectDeviation = 0; - deviationPrev = -1.; + addCellToNewTrack = kFALSE; // loop over left neighbours deviationPrev = o2::constants::math::TwoPI; - chisquarePrev = 1.E5; for (Int_t iLN = 0; iLN < cellRC.getNLeftNeighbours(); ++iLN) { + auto leftNeighbour = cellRC.getLeftNeighbours()[iLN]; - Int_t layerL = leftNeighbour.first; - Int_t cellIdL = leftNeighbour.second; + layerL = leftNeighbour.first; + cellIdL = leftNeighbour.second; - const Cell& cellL = road.getCellsInLayer(layerL)[cellIdL]; + const Cell& cellL = mRoad.getCellsInLayer(layerL)[cellIdL]; - if (road.getCellLevel(layerL, cellIdL) == 0) { - continue; - } - if (road.isCellUsed(layerL, cellIdL)) { - continue; - } - if (road.getCellLevel(layerL, cellIdL) != (road.getCellLevel(layerRC, cellIdRC) - 1)) { + if (mRoad.isCellUsed(layerL, cellIdL) || (mRoad.getCellLevel(layerL, cellIdL) != (mRoad.getCellLevel(layerRC, cellIdRC) - 1))) { continue; } - /* - // ... smallest deviation - deviation = getCellDeviation(event, cellL, cellRC); + + deviation = getCellDeviation(cellL, cellRC); + if (deviation < deviationPrev) { + deviationPrev = deviation; - if (leftNeighbour != cellRC.getLeftNeighbours().front()) { - event.getCurrentTrackCA().removeLastCell(lastCellLayer, lastCellId); - road.setCellUsed(lastCellLayer, lastCellId, kFALSE); - road.setCellLevel(lastCellLayer, lastCellId, 1); - } - if (addCellToCurrentTrackCA(layerL, cellIdL, event)) { - addCellToNewTrack = kTRUE; - road.setCellUsed(layerL, cellIdL, kTRUE); - } - } - */ - // ... smallest chisquare - chisquare = getCellChisquare(event, cellL); - if (chisquare > 0.0 && chisquare < chisquarePrev) { - chisquarePrev = chisquare; - - if (leftNeighbour != cellRC.getLeftNeighbours().front()) { - event.getCurrentTrackCA().removeLastCell(lastCellLayer, lastCellId); - road.setCellUsed(lastCellLayer, lastCellId, kFALSE); - road.setCellLevel(lastCellLayer, lastCellId, 1); - } - if (addCellToCurrentTrackCA(layerL, cellIdL, event)) { - addCellToNewTrack = kTRUE; - road.setCellUsed(layerL, cellIdL, kTRUE); - } else { - //LOG(INFO) << "***** Failed to add cell to the current CA track! *****\n"; + + if (iLN > 0) { + // delete the last added cell + nCells--; } + + trackCells[nCells].layer = layerL; + trackCells[nCells].idInLayer = cellIdL; + nCells++; + + addCellToNewTrack = kTRUE; } } // end loop left neighbour @@ -614,232 +633,125 @@ void Tracker::runBackwardInRoad(ROframe& event) for (Int_t i = 0; i < (constants::mft::DisksNumber); ++i) { hasDisk[i] = kFALSE; } - for (icell = 0; icell < event.getCurrentTrackCA().getNCells(); ++icell) { - layerC = event.getCurrentTrackCA().getCellsLayer()[icell]; - cellIdC = event.getCurrentTrackCA().getCellsId()[icell]; - const Cell& cellC = road.getCellsInLayer(layerC)[cellIdC]; - hasDisk[cellC.getFirstLayerId() / 2] = kTRUE; - hasDisk[cellC.getSecondLayerId() / 2] = kTRUE; + + layerC = trackCells[0].layer; + cellIdC = trackCells[0].idInLayer; + const Cell& cellC = mRoad.getCellsInLayer(layerC)[cellIdC]; + hasDisk[cellC.getSecondLayerId() / 2] = kTRUE; + for (icell = 0; icell < nCells; ++icell) { + layerC = trackCells[icell].layer; + cellIdC = trackCells[icell].idInLayer; + hasDisk[layerC / 2] = kTRUE; } + nPointDisks = 0; for (Int_t disk = 0; disk < (constants::mft::DisksNumber); ++disk) { if (hasDisk[disk]) { ++nPointDisks; } } - if (nPointDisks < constants::mft::MinTrackPoints) { - for (icell = 0; icell < event.getCurrentTrackCA().getNCells(); ++icell) { - layerC = event.getCurrentTrackCA().getCellsLayer()[icell]; - cellIdC = event.getCurrentTrackCA().getCellsId()[icell]; - road.setCellUsed(layerC, cellIdC, kFALSE); - road.setCellLevel(layerC, cellIdC, 1); - } - event.removeCurrentTrackCA(); + + if (nPointDisks < mMinTrackStationsCA) { continue; } - // marked the used clusters - for (icell = 0; icell < event.getCurrentTrackCA().getNCells(); ++icell) { - layerC = event.getCurrentTrackCA().getCellsLayer()[icell]; - cellIdC = event.getCurrentTrackCA().getCellsId()[icell]; - const Cell& cellC = event.getCurrentRoad().getCellsInLayer(layerC)[cellIdC]; - event.markUsedCluster(cellC.getFirstLayerId(), cellC.getFirstClusterIndex()); - event.markUsedCluster(cellC.getSecondLayerId(), cellC.getSecondClusterIndex()); - } + // add a new TrackCA + event.addTrackCA(mRoad.getRoadId()); + for (icell = 0; icell < nCells; ++icell) { + layerC = trackCells[icell].layer; + cellIdC = trackCells[icell].idInLayer; + addCellToCurrentTrackCA(layerC, cellIdC, event); + mRoad.setCellUsed(layerC, cellIdC, kTRUE); + // marked the used clusters + const Cell& cellC = mRoad.getCellsInLayer(layerC)[cellIdC]; + event.getClustersInLayer(cellC.getFirstLayerId())[cellC.getFirstClusterIndex()].setUsed(true); + event.getClustersInLayer(cellC.getSecondLayerId())[cellC.getSecondClusterIndex()].setUsed(true); + } } // end loop cells } // end loop start layer } -void Tracker::updateCellStatusInRoad(Road& road) +//_________________________________________________________________________________________________ +void Tracker::updateCellStatusInRoad() { - for (Int_t layer = 0; layer < (constants::mft::LayersNumber - 1); ++layer) { - for (Int_t icell = 0; icell < road.getCellsInLayer(layer).size(); ++icell) { - road.updateCellLevel(layer, icell); - mMaxCellLevel = std::max(mMaxCellLevel, road.getCellLevel(layer, icell)); + Int_t layerMin, layerMax; + mRoad.getLength(layerMin, layerMax); + for (Int_t layer = layerMin; layer < layerMax; ++layer) { + for (Int_t icell = 0; icell < mRoad.getCellsInLayer(layer).size(); ++icell) { + mRoad.updateCellLevel(layer, icell); + mMaxCellLevel = std::max(mMaxCellLevel, mRoad.getCellLevel(layer, icell)); } } } -const Float_t Tracker::getCellChisquare(ROframe& event, const Cell& cell) const +//_________________________________________________________________________________________________ +void Tracker::addCellToCurrentRoad(ROframe& event, const Int_t layer1, const Int_t layer2, const Int_t clsInLayer1, const Int_t clsInLayer2, Int_t& cellId) { - // returns the new chisquare of the previous cells plus the new one - TrackCA& trackCA = event.getCurrentTrackCA(); - const Int_t layer2 = cell.getSecondLayerId(); - const Int_t cls2Id = cell.getSecondClusterIndex(); - const Cluster& cluster2 = event.getClustersInLayer(layer2)[cls2Id]; + Cell& cell = mRoad.addCellInLayer(layer1, layer2, clsInLayer1, clsInLayer2, cellId); - Float_t x[constants::mft::MaxTrackPoints], y[constants::mft::MaxTrackPoints], z[constants::mft::MaxTrackPoints], err[constants::mft::MaxTrackPoints]; - Int_t point; + Cluster& cluster1 = event.getClustersInLayer(layer1)[clsInLayer1]; + Cluster& cluster2 = event.getClustersInLayer(layer2)[clsInLayer2]; - for (point = 0; point < trackCA.getNPoints(); ++point) { - x[point] = trackCA.getXCoordinates()[point]; - y[point] = trackCA.getYCoordinates()[point]; - z[point] = trackCA.getZCoordinates()[point]; - err[point] = constants::mft::Resolution; // FIXME - } - x[point] = cluster2.xCoordinate; - y[point] = cluster2.yCoordinate; - z[point] = cluster2.zCoordinate; - err[point] = constants::mft::Resolution; // FIXME - - // linear regression in the plane z:x - // x = zxApar * z + zxBpar - Float_t zxApar, zxBpar, zxAparErr, zxBparErr, chisqZX = 0.0; - if (!LinearRegression(trackCA.getNPoints() + 1, z, x, err, zxApar, zxAparErr, zxBpar, zxBparErr, chisqZX)) { - return -1.0; - } - // linear regression in the plane z:y - // y = zyApar * z + zyBpar - Float_t zyApar, zyBpar, zyAparErr, zyBparErr, chisqZY = 0.0; - if (!LinearRegression(trackCA.getNPoints() + 1, z, y, err, zyApar, zyAparErr, zyBpar, zyBparErr, chisqZY)) { - return -1.0; - } + Float_t coord[6]; + coord[0] = cluster1.getX(); + coord[1] = cluster1.getY(); + coord[2] = cluster1.getZ(); + coord[3] = cluster2.getX(); + coord[4] = cluster2.getY(); + coord[5] = cluster2.getZ(); - Int_t nDegFree = 2 * (trackCA.getNPoints() + 1) - 4; - return (chisqZX + chisqZY) / (Float_t)nDegFree; + cell.setCoordinates(coord); + cellId++; } -const Bool_t Tracker::addCellToCurrentTrackCA(const Int_t layer1, const Int_t cellId, ROframe& event) +//_________________________________________________________________________________________________ +void Tracker::addCellToCurrentTrackCA(const Int_t layer1, const Int_t cellId, ROframe& event) { TrackCA& trackCA = event.getCurrentTrackCA(); - Road& road = event.getCurrentRoad(); - const Cell& cell = road.getCellsInLayer(layer1)[cellId]; + const Cell& cell = mRoad.getCellsInLayer(layer1)[cellId]; const Int_t layer2 = cell.getSecondLayerId(); - const Int_t cls1Id = cell.getFirstClusterIndex(); - const Int_t cls2Id = cell.getSecondClusterIndex(); - - const Cluster& cluster1 = event.getClustersInLayer(layer1)[cls1Id]; - const Cluster& cluster2 = event.getClustersInLayer(layer2)[cls2Id]; - - if (trackCA.getNPoints() > 0) { - const Float_t xLast = trackCA.getXCoordinates()[trackCA.getNPoints() - 1]; - const Float_t yLast = trackCA.getYCoordinates()[trackCA.getNPoints() - 1]; - Float_t dx = xLast - cluster2.xCoordinate; - Float_t dy = yLast - cluster2.yCoordinate; - Float_t dr = std::sqrt(dx * dx + dy * dy); - if (dr > constants::mft::Resolution) { - return kFALSE; - } - } + const Int_t clsInLayer1 = cell.getFirstClusterIndex(); + const Int_t clsInLayer2 = cell.getSecondClusterIndex(); + + Cluster& cluster1 = event.getClustersInLayer(layer1)[clsInLayer1]; + Cluster& cluster2 = event.getClustersInLayer(layer2)[clsInLayer2]; MCCompLabel mcCompLabel1 = mUseMC ? event.getClusterLabels(layer1, cluster1.clusterId) : MCCompLabel(); MCCompLabel mcCompLabel2 = mUseMC ? event.getClusterLabels(layer2, cluster2.clusterId) : MCCompLabel(); - Bool_t newPoint; - - if (trackCA.getNPoints() == 0) { - newPoint = kTRUE; - trackCA.setPoint(cluster2.xCoordinate, cluster2.yCoordinate, cluster2.zCoordinate, layer2, cls2Id, mcCompLabel2, newPoint); - } - - newPoint = kTRUE; - trackCA.setPoint(cluster1.xCoordinate, cluster1.yCoordinate, cluster1.zCoordinate, layer1, cls1Id, mcCompLabel1, newPoint); - - trackCA.addCell(layer1, cellId); - - // update the chisquare - if (trackCA.getNPoints() == 2) { - - trackCA.setChiSquareZX(0.0); - trackCA.setChiSquareZY(0.0); - return kTRUE; - } - - Float_t x[constants::mft::MaxTrackPoints], y[constants::mft::MaxTrackPoints], z[constants::mft::MaxTrackPoints], err[constants::mft::MaxTrackPoints]; - for (Int_t point = 0; point < trackCA.getNPoints(); ++point) { - x[point] = trackCA.getXCoordinates()[point]; - y[point] = trackCA.getYCoordinates()[point]; - z[point] = trackCA.getZCoordinates()[point]; - err[point] = constants::mft::Resolution; // FIXME - } - - // linear regression in the plane z:x - // x = zxApar * z + zxBpar - Float_t zxApar, zxBpar, zxAparErr, zxBparErr, chisqZX = 0.0; - if (LinearRegression(trackCA.getNPoints(), z, x, err, zxApar, zxAparErr, zxBpar, zxBparErr, chisqZX)) { - trackCA.setChiSquareZX(chisqZX); - } else { - return kFALSE; - } + Int_t extClsIndex; - // linear regression in the plane z:y - // y = zyApar * z + zyBpar - Float_t zyApar, zyBpar, zyAparErr, zyBparErr, chisqZY = 0.0; - if (LinearRegression(trackCA.getNPoints(), z, y, err, zyApar, zyAparErr, zyBpar, zyBparErr, chisqZY)) { - trackCA.setChiSquareZY(chisqZY); - } else { - return kFALSE; + if (trackCA.getNumberOfPoints() == 0) { + extClsIndex = event.getClusterExternalIndex(layer2, cluster2.clusterId); + trackCA.setPoint(cluster2, layer2, clsInLayer2, mcCompLabel2, extClsIndex); } - return kTRUE; + extClsIndex = event.getClusterExternalIndex(layer1, cluster1.clusterId); + trackCA.setPoint(cluster1, layer1, clsInLayer1, mcCompLabel1, extClsIndex); } -const Bool_t Tracker::LinearRegression(Int_t npoints, Float_t* x, Float_t* y, Float_t* yerr, Float_t& apar, Float_t& aparerr, Float_t& bpar, Float_t& bparerr, Float_t& chisq, Int_t skippoint) const +//_________________________________________________________________________________________________ +bool Tracker::fitTracks(ROframe& event) { - // y = apar * x + bpar - - // work with only part of the points - Float_t xCl[constants::mft::MaxTrackPoints], yCl[constants::mft::MaxTrackPoints], yClErr[constants::mft::MaxTrackPoints]; - Int_t ipoints = 0; - for (Int_t i = 0; i < npoints; ++i) { - if (i == skippoint) { - continue; - } - xCl[ipoints] = x[i]; - yCl[ipoints] = y[i]; - yClErr[ipoints] = yerr[i]; - ipoints++; + for (auto& track : event.getTracksLTF()) { + TrackLTF outParam = track; + mTrackFitter->initTrack(track); + mTrackFitter->fit(track); + mTrackFitter->initTrack(outParam, true); + mTrackFitter->fit(outParam, true); + track.setOutParam(outParam); } - - // calculate the regression parameters - Float_t S1, SXY, SX, SY, SXX, SsXY, SsXX, SsYY, Xm, Ym, s, delta, difx; - S1 = SXY = SX = SY = SXX = 0.0; - SsXX = SsYY = SsXY = Xm = Ym = 0.0; - difx = 0.; - for (Int_t i = 0; i < ipoints; ++i) { - S1 += 1.0 / (yClErr[i] * yClErr[i]); - SXY += xCl[i] * yCl[i] / (yClErr[i] * yClErr[i]); - SX += xCl[i] / (yClErr[i] * yClErr[i]); - SY += yCl[i] / (yClErr[i] * yClErr[i]); - SXX += xCl[i] * xCl[i] / (yClErr[i] * yClErr[i]); - if (i > 0) - difx += TMath::Abs(xCl[i] - xCl[i - 1]); - Xm += xCl[i]; - Ym += yCl[i]; - SsXX += xCl[i] * xCl[i]; - SsYY += yCl[i] * yCl[i]; - SsXY += xCl[i] * yCl[i]; - } - delta = SXX * S1 - SX * SX; - if (delta == 0.) { - return kFALSE; - } - apar = (SXY * S1 - SX * SY) / delta; - bpar = (SY * SXX - SX * SXY) / delta; - - // calculate the chisquare - chisq = 0.0; - for (Int_t i = 0; i < ipoints; ++i) { - chisq += (yCl[i] - (apar * xCl[i] + bpar)) * (yCl[i] - (apar * xCl[i] + bpar)) / (yerr[i] * yerr[i]); - } - - // calculate the errors of the regression parameters - Ym /= (Float_t)ipoints; - Xm /= (Float_t)ipoints; - SsYY -= (Float_t)ipoints * (Ym * Ym); - SsXX -= (Float_t)ipoints * (Xm * Xm); - SsXY -= (Float_t)ipoints * (Ym * Xm); - Float_t eps = 1.E-24; - if ((ipoints > 2) && (TMath::Abs(difx) > eps) && ((SsYY - (SsXY * SsXY) / SsXX) > 0.0)) { - s = std::sqrt((SsYY - (SsXY * SsXY) / SsXX) / (ipoints - 2)); - bparerr = s * std::sqrt(1. / (Float_t)ipoints + (Xm * Xm) / SsXX); - aparerr = s / std::sqrt(SsXX); - } else { - bparerr = 0.; - aparerr = 0.; + for (auto& track : event.getTracksCA()) { + track.sort(); + TrackCA outParam = track; + mTrackFitter->initTrack(track); + mTrackFitter->fit(track); + mTrackFitter->initTrack(outParam, true); + mTrackFitter->fit(outParam, true); + track.setOutParam(outParam); } - return kTRUE; + return true; } } // namespace mft diff --git a/Detectors/ITSMFT/MFT/tracking/src/TrackerConfig.cxx b/Detectors/ITSMFT/MFT/tracking/src/TrackerConfig.cxx new file mode 100644 index 0000000000000..af0384b308ad1 --- /dev/null +++ b/Detectors/ITSMFT/MFT/tracking/src/TrackerConfig.cxx @@ -0,0 +1,70 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GeometryTGeo.cxx +/// \brief Implementation of the GeometryTGeo class +/// \author bogdan.vulpescu@clermont.in2p3.fr - adapted from ITS, 21.09.2017 + +#include "MFTTracking/TrackerConfig.h" + +#include <fairlogger/Logger.h> + +//__________________________________________________________________________ +o2::mft::TrackerConfig::TrackerConfig() + : mMinTrackPointsLTF{5}, + mMinTrackPointsCA{4}, + mMinTrackStationsLTF{4}, + mMinTrackStationsCA{4}, + mLTFclsRCut{0.0100}, + mLTFclsR2Cut{0.0100 * 0.0100}, + mROADclsRCut{0.0400}, + mROADclsR2Cut{0.0400 * 0.0400}, + mLTFseed2BinWin{3}, + mLTFinterBinWin{3}, + mRBins{50}, + mPhiBins{50}, + mRPhiBins{50 * 50}, + mRBinSize{(constants::index_table::RMax - constants::index_table::RMin) / 50.}, + mPhiBinSize{(constants::index_table::PhiMax - constants::index_table::PhiMin) / 50.}, + mInverseRBinSize{50. / (constants::index_table::RMax - constants::index_table::RMin)}, + mInversePhiBinSize{50. / (constants::index_table::PhiMax - constants::index_table::PhiMin)} +{ + /// default constructor +} + +//__________________________________________________________________________ +void o2::mft::TrackerConfig::initialize(const MFTTrackingParam& trkParam) +{ + /// initialize from MFTTrackingParam (command line configuration parameters) + + mMinTrackPointsLTF = trkParam.MinTrackPointsLTF; + mMinTrackPointsCA = trkParam.MinTrackPointsCA; + mMinTrackStationsLTF = trkParam.MinTrackStationsLTF; + mMinTrackStationsCA = trkParam.MinTrackStationsCA; + mLTFclsRCut = trkParam.LTFclsRCut; + mLTFclsR2Cut = mLTFclsRCut * mLTFclsRCut; + mROADclsRCut = trkParam.ROADclsRCut; + mROADclsR2Cut = mROADclsRCut * mROADclsRCut; + mLTFseed2BinWin = trkParam.LTFseed2BinWin; + mLTFinterBinWin = trkParam.LTFinterBinWin; + + mRBins = trkParam.RBins; + mPhiBins = trkParam.PhiBins; + mRPhiBins = trkParam.RBins * trkParam.PhiBins; + if (mRPhiBins > constants::index_table::MaxRPhiBins) { + LOG(WARN) << "To many RPhiBins for this configuration!"; + mRPhiBins = constants::index_table::MaxRPhiBins; + mRBins = sqrt(constants::index_table::MaxRPhiBins); + mPhiBins = sqrt(constants::index_table::MaxRPhiBins); + LOG(WARN) << "Using instead RBins " << mRBins << " and PhiBins " << mPhiBins; + } + mRBinSize = (constants::index_table::RMax - constants::index_table::RMin) / mRBins; + mPhiBinSize = (constants::index_table::PhiMax - constants::index_table::PhiMin) / mPhiBins; +} diff --git a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt index b5691a014497a..ebfe2f71cebdc 100644 --- a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt @@ -16,7 +16,6 @@ o2_add_library(MFTWorkflow src/ClusterWriterSpec.cxx src/ClusterReaderSpec.cxx src/TrackerSpec.cxx - src/TrackFitterSpec.cxx src/TrackWriterSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::SimConfig @@ -29,4 +28,3 @@ o2_add_executable(reco-workflow SOURCES src/mft-reco-workflow.cxx COMPONENT_NAME mft PUBLIC_LINK_LIBRARIES O2::MFTWorkflow) - diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackFitterSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackFitterSpec.h deleted file mode 100644 index 198a36e700cf4..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackFitterSpec.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackFitterSpec.h -/// \brief Definition of a data processor to read, refit and send tracks with attached clusters -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#ifndef ALICEO2_MFT_TRACKFITTERSPEC_H_ -#define ALICEO2_MFT_TRACKFITTERSPEC_H_ - -#include "MFTTracking/TrackFitter.h" -#include "DataFormatsParameters/GRPObject.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -namespace o2 -{ -namespace mft -{ - -using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>; -using SMatrix5 = ROOT::Math::SVector<Double_t, 5>; - -class TrackFitterTask : public o2::framework::Task -{ - public: - TrackFitterTask(bool useMC) : mUseMC(useMC) {} - ~TrackFitterTask() 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<o2::parameters::GRPObject> mGRP = nullptr; - std::unique_ptr<o2::mft::TrackFitter> mTrackFitter = nullptr; -}; - -template <typename T, typename O, typename C> -void convertTrack(const T& inTrack, O& outTrack, C& clusters); - -SMatrix55 TtoSMatrixSym55(TMatrixD inMatrix); -SMatrix5 TtoSMatrix5(TMatrixD inMatrix); - -o2::framework::DataProcessorSpec getTrackFitterSpec(bool useMC); - -} // end namespace mft -} // end namespace o2 - -#endif // ALICEO2_MFT_TRACKFITTERSPEC_H_ diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h index 50624a8de2c76..a18f2f37a766a 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h @@ -19,6 +19,7 @@ #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/TopologyDictionary.h" +#include "TStopwatch.h" namespace o2 { @@ -32,12 +33,14 @@ class TrackerDPL : public o2::framework::Task ~TrackerDPL() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; private: bool mUseMC = false; o2::itsmft::TopologyDictionary mDict; std::unique_ptr<o2::parameters::GRPObject> mGRP = nullptr; std::unique_ptr<o2::mft::Tracker> mTracker = nullptr; + TStopwatch mTimer; }; /// create a processor spec diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClusterReaderSpec.cxx index 70c2af1ac969d..896aacf27d59b 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/ClusterReaderSpec.cxx @@ -17,6 +17,7 @@ #include "TTree.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -44,8 +45,9 @@ void ClusterReader::init(InitContext& ic) void ClusterReader::run(ProcessingContext& pc) { - if (mState != 1) + if (mState != 1) { return; + } std::unique_ptr<TTree> tree((TTree*)mFile->Get("o2sim")); diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx index bd428ff4ec17f..388f18514b414 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx @@ -19,7 +19,7 @@ #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsParameters/GRPObject.h" #include "ITSMFTReconstruction/DigitPixelReader.h" @@ -72,9 +72,9 @@ void ClustererDPL::init(InitContext& ic) std::string dictFile = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::MFT, dictPath, ".bin"); if (o2::base::NameConf::pathExists(dictFile)) { mClusterer->loadDictionary(dictFile); - LOG(INFO) << "ITSClusterer running with a provided dictionary: " << dictFile; + LOG(INFO) << "MFTClusterer running with a provided dictionary: " << dictFile; } else { - LOG(INFO) << "Dictionary " << dictFile << " is absent, ITSClusterer expects cluster patterns"; + LOG(INFO) << "Dictionary " << dictFile << " is absent, MFTClusterer expects cluster patterns"; } mState = 1; mClusterer->print(); @@ -85,12 +85,13 @@ void ClustererDPL::run(ProcessingContext& pc) auto digits = pc.inputs().get<gsl::span<o2::itsmft::Digit>>("digits"); auto rofs = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>("ROframes"); - std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>> labels; gsl::span<const o2::itsmft::MC2ROFRecord> mc2rofs; + gsl::span<const char> labelbuffer; if (mUseMC) { - labels = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("labels"); + labelbuffer = pc.inputs().get<gsl::span<char>>("labels"); mc2rofs = pc.inputs().get<gsl::span<o2::itsmft::MC2ROFRecord>>("MC2ROframes"); } + const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> labels(labelbuffer); LOG(INFO) << "MFTClusterer pulled " << digits.size() << " digits, in " << rofs.size() << " RO frames"; @@ -100,7 +101,7 @@ void ClustererDPL::run(ProcessingContext& pc) reader.setROFRecords(rofs); if (mUseMC) { reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.get()); + reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); } reader.init(); auto orig = o2::header::gDataOriginMFT; diff --git a/Detectors/ITSMFT/MFT/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/DigitReaderSpec.cxx index 031133a161f23..7baf9b858ab9a 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/DigitReaderSpec.cxx @@ -17,9 +17,11 @@ #include "TTree.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" #include "DataFormatsITSMFT/Digit.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" #include "DataFormatsITSMFT/ROFRecord.h" using namespace o2::framework; @@ -44,8 +46,9 @@ void DigitReader::init(InitContext& ic) void DigitReader::run(ProcessingContext& pc) { - if (mState != 1) + if (mState != 1) { return; + } std::unique_ptr<TTree> treeDig((TTree*)mFile->Get("o2sim")); @@ -57,7 +60,7 @@ void DigitReader::run(ProcessingContext& pc) std::vector<ROFRecord> rofs, *profs = &rofs; treeDig->SetBranchAddress("MFTDigitROF", &profs); - o2::dataformats::MCTruthContainer<o2::MCCompLabel> labels, *plabels = &labels; + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; std::vector<MC2ROFRecord> mc2rofs, *pmc2rofs = &mc2rofs; if (mUseMC) { treeDig->SetBranchAddress("MFTDigitMCTruth", &plabels); @@ -71,7 +74,9 @@ void DigitReader::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"MFT", "DIGITS", 0, Lifetime::Timeframe}, digits); pc.outputs().snapshot(Output{"MFT", "DIGITSROF", 0, Lifetime::Timeframe}, *profs); if (mUseMC) { - pc.outputs().snapshot(Output{"MFT", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{"MFT", "DIGITSMCTR", 0, Lifetime::Timeframe}); + plabels->copyandflatten(sharedlabels); + delete plabels; pc.outputs().snapshot(Output{"MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe}, *pmc2rofs); } } else { diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 7e3e4832d8645..055820074eb96 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -18,7 +18,6 @@ #include "MFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/ClusterReaderSpec.h" #include "MFTWorkflow/TrackerSpec.h" -#include "MFTWorkflow/TrackFitterSpec.h" #include "MFTWorkflow/TrackWriterSpec.h" namespace o2 @@ -43,7 +42,6 @@ framework::WorkflowSpec getWorkflow(bool useMC, bool upstreamDigits, bool upstre specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); } specs.emplace_back(o2::mft::getTrackerSpec(useMC)); - specs.emplace_back(o2::mft::getTrackFitterSpec(useMC)); if (!disableRootOutput) { specs.emplace_back(o2::mft::getTrackWriterSpec(useMC)); } diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackFitterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackFitterSpec.cxx deleted file mode 100644 index ca4046a33dca9..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackFitterSpec.cxx +++ /dev/null @@ -1,198 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackFitterSpec.cxx -/// \brief Implementation of a data processor to read, refit and send tracks with attached clusters -/// -/// \author Philippe Pillot, Subatech; adapted by Rafael Pezzi, UFRGS - -#include "MFTWorkflow/TrackFitterSpec.h" -#include "Field/MagneticField.h" -#include "TGeoGlobalMagField.h" -#include "DetectorsBase/Propagator.h" - -#include <stdexcept> -#include <list> - -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -#include "MFTTracking/TrackParamMFT.h" -#include "MFTTracking/TrackCA.h" -#include "MFTTracking/FitterTrackMFT.h" -#include "MFTTracking/TrackFitter.h" -#include "MFTTracking/TrackExtrap.h" -#include "MFTTracking/Cluster.h" - -using namespace std; -using namespace o2::framework; - -namespace o2 -{ -namespace mft -{ - -void TrackFitterTask::init(InitContext& ic) -{ - /// Prepare the track extrapolation tools - LOG(INFO) << "initializing track fitter"; - mTrackFitter = std::make_unique<o2::mft::TrackFitter>(); - - auto filename = ic.options().get<std::string>("grp-file"); - const auto grp = o2::parameters::GRPObject::loadFrom(filename.c_str()); - if (grp) { - mGRP.reset(grp); - o2::base::Propagator::initFieldFromGRP(grp); - auto field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); - - double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT - mTrackFitter->setBz(field->getBz(centerMFT)); - - } else { - LOG(ERROR) << "Cannot retrieve GRP from the " << filename.c_str() << " file !"; - mState = 0; - } - mState = 1; -} - -//_________________________________________________________________________________________________ -void TrackFitterTask::run(ProcessingContext& pc) -{ - - if (mState != 1) - return; - - auto tracksLTF = pc.inputs().get<gsl::span<o2::mft::TrackLTF>>("tracksltf"); - auto tracksCA = pc.inputs().get<gsl::span<o2::mft::TrackCA>>("tracksca"); - - int nTracksCA = 0; - int nFailedTracksCA = 0; - int nTracksLTF = 0; - int nFailedTracksLTF = 0; - std::vector<o2::mft::FitterTrackMFT> fittertracks(tracksLTF.size() + tracksCA.size()); - std::list<Cluster> clusters; - - // Fit LTF tracks - for (const auto& track : tracksLTF) { - auto& temptrack = fittertracks.at(nTracksLTF + nFailedTracksLTF); - convertTrack(track, temptrack, clusters); - mTrackFitter->fit(temptrack, false) ? nTracksLTF++ : nFailedTracksLTF++; - } // end fit LTF tracks - - // Fit CA tracks - for (const auto& track : tracksCA) { - auto& temptrack = fittertracks.at(nTracksLTF + nFailedTracksLTF + nTracksCA + nFailedTracksCA); - convertTrack(track, temptrack, clusters); - mTrackFitter->fit(temptrack, false) ? nTracksCA++ : nFailedTracksCA++; - } // end fit CA tracks - - auto& finalMFTtracks = pc.outputs().make<std::vector<o2::mft::TrackMFT>>(Output{"MFT", "TRACKS", 0, Lifetime::Timeframe}); - finalMFTtracks.resize(nTracksLTF + nTracksCA); - - auto nTotalTracks = 0; - // Convert fitter tracks to the final Standalone MFT Track - for (const auto& track : fittertracks) { - if (!track.isRemovable()) { - auto& temptrack = finalMFTtracks.at(nTotalTracks); - temptrack.setZ(track.first().getZ()); - temptrack.setParameters(TtoSMatrix5(track.first().getParameters())); - temptrack.setCovariances(TtoSMatrixSym55(track.first().getCovariances())); - temptrack.setTrackChi2(track.first().getTrackChi2()); - temptrack.setMCCompLabels(track.getMCCompLabels(), track.getNPoints()); - temptrack.setInvQPtQuadtratic(track.getInvQPtQuadtratic()); - temptrack.setChi2QPtQuadtratic(track.getChi2QPtQuadtratic()); - //finalMFTtracks.back().printMCCompLabels(); - nTotalTracks++; - } - } - - LOG(INFO) << "MFTFitter loaded " << tracksLTF.size() << " LTF tracks"; - LOG(INFO) << "MFTFitter loaded " << tracksCA.size() << " CA tracks"; - LOG(INFO) << "MFTFitter pushed " << fittertracks.size() << " tracks"; - LOG(INFO) << "MFTFitter dropped " << nFailedTracksLTF << " LTF tracks"; - LOG(INFO) << "MFTFitter dropped " << nFailedTracksCA << " CA tracks"; - - mState = 2; - pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); -} - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackFitterSpec(bool useMC) -{ - std::vector<InputSpec> inputs; - inputs.emplace_back("tracksltf", "MFT", "TRACKSLTF", 0, Lifetime::Timeframe); - inputs.emplace_back("tracksca", "MFT", "TRACKSCA", 0, Lifetime::Timeframe); - - std::vector<OutputSpec> outputs; - outputs.emplace_back("MFT", "TRACKS", 0, Lifetime::Timeframe); - - return DataProcessorSpec{ - "mft-track-fitter", - inputs, - outputs, - AlgorithmSpec{adaptFromTask<TrackFitterTask>(useMC)}, - Options{ - {"grp-file", VariantType::String, "o2sim_grp.root", {"Name of the output file"}}, - }}; -} - -//_________________________________________________________________________________________________ -template <typename T, typename O, typename C> -void convertTrack(const T& inTrack, O& outTrack, C& clusters) -{ - //auto fittedTrack = FitterTrackMFT(); - //TMatrixD covariances(5,5); - auto xpos = inTrack.getXCoordinates(); - auto ypos = inTrack.getYCoordinates(); - auto zpos = inTrack.getZCoordinates(); - auto clusterIDs = inTrack.getClustersId(); - auto nClusters = inTrack.getNPoints(); - static int ntrack = 0; - - // Add clusters to Tracker's cluster vector & set fittedTrack cluster range. - // TODO: get rid of this cluster vector - for (auto cls = 0; cls < nClusters; cls++) { - Cluster& tempcluster = clusters.emplace_back(xpos[cls], ypos[cls], zpos[cls], clusterIDs[cls]); - tempcluster.sigmaY2 = 5.43e-4; // FIXME: Use clusters errors once available - tempcluster.sigmaX2 = 5.0e-4; - outTrack.createParamAtCluster(tempcluster); - } - outTrack.setMCCompLabels(inTrack.getMCCompLabels(), nClusters); -} - -//_________________________________________________________________________________________________ -SMatrix55 TtoSMatrixSym55(TMatrixD inMatrix) -{ - // TMatrix to sym SMatrix - SMatrix55 outMatrix; - for (auto i = 5; i--;) { - outMatrix(i, i) = inMatrix(i, i); - for (auto j = i; j--;) { - outMatrix(i, j) = inMatrix(i, j); - } - } - return outMatrix; -} - -//_________________________________________________________________________________________________ -SMatrix5 TtoSMatrix5(TMatrixD inMatrix) -{ - SMatrix5 outMatrix; - for (auto i = 0; i < 5; i++) - outMatrix(i) = inMatrix(i, 0); - return outMatrix; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx index 69e0fae8717d9..25ee745f2bb9d 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx @@ -49,10 +49,8 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) BranchDefinition<std::vector<o2::mft::TrackMFT>>{InputSpec{"tracks", "MFT", "TRACKS", 0}, "MFTTrack", tracksSizeGetter}, - BranchDefinition<std::vector<o2::mft::TrackLTF>>{InputSpec{"tracksltf", "MFT", "TRACKSLTF", 0}, - "MFTTrackLTF"}, - BranchDefinition<std::vector<o2::mft::TrackCA>>{InputSpec{"tracksca", "MFT", "TRACKSCA", 0}, - "MFTTrackCA"}, + BranchDefinition<std::vector<int>>{InputSpec{"trackClIdx", "MFT", "TRACKCLSID", 0}, + "MFTTrackClusIdx"}, BranchDefinition<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>{InputSpec{"labels", "MFT", "TRACKSMCTR", 0}, "MFTTrackMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx index b3df99bf9a45f..93357ba762838 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx @@ -12,6 +12,7 @@ #include "MFTWorkflow/TrackerSpec.h" +#include "MFTTracking/TrackerConfig.h" #include "MFTTracking/ROframe.h" #include "MFTTracking/IOUtils.h" #include "MFTTracking/Tracker.h" @@ -43,6 +44,8 @@ namespace mft void TrackerDPL::init(InitContext& ic) { + mTimer.Stop(); + mTimer.Reset(); auto filename = ic.options().get<std::string>("grp-file"); const auto grp = o2::parameters::GRPObject::loadFrom(filename.c_str()); if (grp) { @@ -52,18 +55,23 @@ void TrackerDPL::init(InitContext& ic) o2::base::GeometryManager::loadGeometry(); o2::mft::GeometryTGeo* geom = o2::mft::GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::T2G)); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::T2G)); + // tracking configuration parameters + auto& mftTrackingParam = MFTTrackingParam::Instance(); + // create the tracker: set the B-field, the configuration and initialize mTracker = std::make_unique<o2::mft::Tracker>(mUseMC); - double origD[3] = {0., 0., 0.}; - mTracker->setBz(field->getBz(origD)); + double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT + mTracker->setBz(field->getBz(centerMFT)); + mTracker->initConfig(mftTrackingParam); + mTracker->initialize(); } else { throw std::runtime_error(o2::utils::concat_string("Cannot retrieve GRP from the ", filename)); } - std::string dictPath = ic.options().get<std::string>("its-dictionary-path"); - std::string dictFile = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, dictPath, ".bin"); + std::string dictPath = ic.options().get<std::string>("mft-dictionary-path"); + std::string dictFile = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::MFT, dictPath, ".bin"); if (o2::base::NameConf::pathExists(dictFile)) { mDict.readBinaryFile(dictFile); LOG(INFO) << "Tracker running with a provided dictionary: " << dictFile; @@ -74,8 +82,11 @@ void TrackerDPL::init(InitContext& ic) void TrackerDPL::run(ProcessingContext& pc) { + mTimer.Start(false); gsl::span<const unsigned char> patterns = pc.inputs().get<gsl::span<unsigned char>>("patterns"); auto compClusters = pc.inputs().get<const std::vector<o2::itsmft::CompClusterExt>>("compClusters"); + auto nTracksLTF = 0; + auto nTracksCA = 0; // code further down does assignment to the rofs and the altered object is used for output // we therefore need a copy of the vector rather than an object created directly on the input data, @@ -97,14 +108,12 @@ void TrackerDPL::run(ProcessingContext& pc) } //std::vector<o2::mft::TrackMFTExt> tracks; - std::vector<int> allClusIdx; + auto& allClusIdx = pc.outputs().make<std::vector<int>>(Output{"MFT", "TRACKCLSID", 0, Lifetime::Timeframe}); o2::dataformats::MCTruthContainer<o2::MCCompLabel> trackLabels; - std::vector<o2::mft::TrackMFT> allTracks; o2::dataformats::MCTruthContainer<o2::MCCompLabel> allTrackLabels; std::vector<o2::mft::TrackLTF> tracksLTF; - auto& allTracksLTF = pc.outputs().make<std::vector<o2::mft::TrackLTF>>(Output{"MFT", "TRACKSLTF", 0, Lifetime::Timeframe}); std::vector<o2::mft::TrackCA> tracksCA; - auto& allTracksCA = pc.outputs().make<std::vector<o2::mft::TrackCA>>(Output{"MFT", "TRACKSCA", 0, Lifetime::Timeframe}); + auto& allTracksMFT = pc.outputs().make<std::vector<o2::mft::TrackMFT>>(Output{"MFT", "TRACKS", 0, Lifetime::Timeframe}); std::uint32_t roFrame = 0; o2::mft::ROframe event(0); @@ -113,12 +122,13 @@ void TrackerDPL::run(ProcessingContext& pc) LOG(INFO) << "MFTTracker RO: continuous=" << continuous; // snippet to convert found tracks to final output tracks with separate cluster indices - auto copyTracks = [](auto& tracks, auto& allTracks, auto& allClusIdx, int offset = 0) { + auto copyTracks = [&event](auto& tracks, auto& allTracks, auto& allClusIdx) { for (auto& trc : tracks) { - trc.setFirstClusterEntry(allClusIdx.size()); // before adding tracks, create final cluster indices - int ncl = trc.getNumberOfClusters(); + trc.setExternalClusterIndexOffset(allClusIdx.size()); + int ncl = trc.getNumberOfPoints(); for (int ic = 0; ic < ncl; ic++) { - allClusIdx.push_back(trc.getClusterIndex(ic) + offset); + auto externalClusterID = trc.getExternalClusterIndex(ic); + allClusIdx.push_back(externalClusterID); } allTracks.emplace_back(trc); } @@ -126,37 +136,54 @@ void TrackerDPL::run(ProcessingContext& pc) gsl::span<const unsigned char>::iterator pattIt = patterns.begin(); if (continuous) { - for (const auto& rof : rofs) { - int nclUsed = ioutils::loadROFrameData(rof, event, compClusters, pattIt, mDict, labels); + for (auto& rof : rofs) { + int nclUsed = ioutils::loadROFrameData(rof, event, compClusters, pattIt, mDict, labels, mTracker.get()); if (nclUsed) { event.setROFrameId(roFrame); - event.initialise(); + event.initialize(); LOG(INFO) << "ROframe: " << roFrame << ", clusters loaded : " << nclUsed; mTracker->setROFrame(roFrame); mTracker->clustersToTracks(event); tracksLTF.swap(event.getTracksLTF()); tracksCA.swap(event.getTracksCA()); + nTracksLTF += tracksLTF.size(); + nTracksCA += tracksCA.size(); + + if (mUseMC) { + mTracker->computeTracksMClabels(tracksLTF); + mTracker->computeTracksMClabels(tracksCA); + trackLabels = mTracker->getTrackLabels(); /// FIXME: assignment ctor is not optimal. + allTrackLabels.mergeAtBack(trackLabels); + } + LOG(INFO) << "Found tracks LTF: " << tracksLTF.size(); LOG(INFO) << "Found tracks CA: " << tracksCA.size(); - trackLabels = mTracker->getTrackLabels(); /// FIXME: assignment ctor is not optimal. - int first = allTracks.size(); - int shiftIdx = -rof.getFirstEntry(); - rofs[roFrame].setFirstEntry(first); - std::copy(tracksLTF.begin(), tracksLTF.end(), std::back_inserter(allTracksLTF)); - std::copy(tracksCA.begin(), tracksCA.end(), std::back_inserter(allTracksCA)); - allTrackLabels.mergeAtBack(trackLabels); + int first = allTracksMFT.size(); + int number = tracksLTF.size() + tracksCA.size(); + rof.setFirstEntry(first); + rof.setNEntries(number); + copyTracks(tracksLTF, allTracksMFT, allClusIdx); + copyTracks(tracksCA, allTracksMFT, allClusIdx); } roFrame++; } } - //LOG(INFO) << "MFTTracker pushed " << allTracks.size() << " tracks"; - LOG(INFO) << "MFTTracker pushed " << allTracksLTF.size() << " tracks LTF"; - LOG(INFO) << "MFTTracker pushed " << allTracksCA.size() << " tracks CA"; + LOG(INFO) << "MFTTracker found " << nTracksLTF << " tracks LTF"; + LOG(INFO) << "MFTTracker found " << nTracksCA << " tracks CA"; + LOG(INFO) << "MFTTracker pushed " << allTracksMFT.size() << " tracks"; + if (mUseMC) { pc.outputs().snapshot(Output{"MFT", "TRACKSMCTR", 0, Lifetime::Timeframe}, allTrackLabels); pc.outputs().snapshot(Output{"MFT", "TRACKSMC2ROF", 0, Lifetime::Timeframe}, mc2rofs); } + mTimer.Stop(); +} + +void TrackerDPL::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "MFT Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } DataProcessorSpec getTrackerSpec(bool useMC) @@ -167,9 +194,9 @@ DataProcessorSpec getTrackerSpec(bool useMC) inputs.emplace_back("ROframes", "MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); std::vector<OutputSpec> outputs; - outputs.emplace_back("MFT", "TRACKSLTF", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "TRACKSCA", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKS", 0, Lifetime::Timeframe); outputs.emplace_back("MFT", "TRACKSROF", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKCLSID", 0, Lifetime::Timeframe); if (useMC) { inputs.emplace_back("labels", "MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); @@ -185,7 +212,7 @@ DataProcessorSpec getTrackerSpec(bool useMC) AlgorithmSpec{adaptFromTask<TrackerDPL>(useMC)}, Options{ {"grp-file", VariantType::String, "o2sim_grp.root", {"Name of the output file"}}, - {"its-dictionary-path", VariantType::String, "", {"Path of the cluster-topology dictionary file"}}}}; + {"mft-dictionary-path", VariantType::String, "", {"Path of the cluster-topology dictionary file"}}}}; } } // namespace mft diff --git a/Detectors/ITSMFT/common/base/include/ITSMFTBase/SegmentationAlpide.h b/Detectors/ITSMFT/common/base/include/ITSMFTBase/SegmentationAlpide.h index 6e80d92560feb..394aa268d410c 100644 --- a/Detectors/ITSMFT/common/base/include/ITSMFTBase/SegmentationAlpide.h +++ b/Detectors/ITSMFT/common/base/include/ITSMFTBase/SegmentationAlpide.h @@ -15,7 +15,7 @@ #define ALICEO2_ITSMFT_SEGMENTATIONALPIDE_H_ #include <Rtypes.h> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" namespace o2 { @@ -78,12 +78,12 @@ class SegmentationAlpide /// or -0.5*Dz() is returned. static bool detectorToLocal(int iRow, int iCol, float& xRow, float& zCol); static bool detectorToLocal(float row, float col, float& xRow, float& zCol); - static bool detectorToLocal(float row, float col, Point3D<float>& loc); + static bool detectorToLocal(float row, float col, math_utils::Point3D<float>& loc); // same but w/o check for row/col range static void detectorToLocalUnchecked(int iRow, int iCol, float& xRow, float& zCol); static void detectorToLocalUnchecked(float row, float col, float& xRow, float& zCol); - static void detectorToLocalUnchecked(float row, float col, Point3D<float>& loc); + static void detectorToLocalUnchecked(float row, float col, math_utils::Point3D<float>& loc); static constexpr float getFirstRowCoordinate() { @@ -104,10 +104,12 @@ inline void SegmentationAlpide::localToDetectorUnchecked(float xRow, float zCol, zCol += 0.5 * ActiveMatrixSizeCols; // coordinate wrt left edge of Active matrix iRow = int(xRow / PitchRow); iCol = int(zCol / PitchCol); - if (xRow < 0) + if (xRow < 0) { iRow -= 1; - if (zCol < 0) + } + if (zCol < 0) { iCol -= 1; + } } //_________________________________________________________________________________________________ @@ -140,7 +142,7 @@ inline void SegmentationAlpide::detectorToLocalUnchecked(float row, float col, f } //_________________________________________________________________________________________________ -inline void SegmentationAlpide::detectorToLocalUnchecked(float row, float col, Point3D<float>& loc) +inline void SegmentationAlpide::detectorToLocalUnchecked(float row, float col, math_utils::Point3D<float>& loc) { loc.SetCoordinates(getFirstRowCoordinate() - row * PitchRow, 0.f, col * PitchCol + getFirstColCoordinate()); } @@ -148,8 +150,9 @@ inline void SegmentationAlpide::detectorToLocalUnchecked(float row, float col, P //_________________________________________________________________________________________________ inline bool SegmentationAlpide::detectorToLocal(int iRow, int iCol, float& xRow, float& zCol) { - if (iRow < 0 || iRow >= NRows || iCol < 0 || iCol >= NCols) + if (iRow < 0 || iRow >= NRows || iCol < 0 || iCol >= NCols) { return false; + } detectorToLocalUnchecked(iRow, iCol, xRow, zCol); return true; } @@ -157,17 +160,19 @@ inline bool SegmentationAlpide::detectorToLocal(int iRow, int iCol, float& xRow, //_________________________________________________________________________________________________ inline bool SegmentationAlpide::detectorToLocal(float row, float col, float& xRow, float& zCol) { - if (row < 0 || row >= NRows || col < 0 || col >= NCols) + if (row < 0 || row >= NRows || col < 0 || col >= NCols) { return false; + } detectorToLocalUnchecked(row, col, xRow, zCol); return true; } //_________________________________________________________________________________________________ -inline bool SegmentationAlpide::detectorToLocal(float row, float col, Point3D<float>& loc) +inline bool SegmentationAlpide::detectorToLocal(float row, float col, math_utils::Point3D<float>& loc) { - if (row < 0 || row >= NRows || col < 0 || col >= NCols) + if (row < 0 || row >= NRows || col < 0 || col >= NCols) { return false; + } detectorToLocalUnchecked(row, col, loc); return true; } diff --git a/Detectors/ITSMFT/common/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/common/reconstruction/CMakeLists.txt index 6d5b8f97fd9eb..a1de05e673e1a 100644 --- a/Detectors/ITSMFT/common/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/common/reconstruction/CMakeLists.txt @@ -27,6 +27,7 @@ o2_add_library(ITSMFTReconstruction src/RUDecodeData.cxx src/RawPixelDecoder.cxx src/CTFCoder.cxx + src/DecodingStat.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTBase O2::CommonDataFormat O2::DetectorsRaw @@ -55,7 +56,8 @@ o2_target_root_dictionary( include/ITSMFTReconstruction/AlpideCoder.h include/ITSMFTReconstruction/GBTWord.h include/ITSMFTReconstruction/PayLoadCont.h - include/ITSMFTReconstruction/PayLoadSG.h + include/ITSMFTReconstruction/PayLoadSG.h + include/ITSMFTReconstruction/DecodingStat.h include/ITSMFTReconstruction/RUInfo.h) if (OpenMP_CXX_FOUND) diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h index 1ce80e2c1ec43..57de757a01253 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h @@ -17,11 +17,16 @@ #include <vector> #include <string> #include <cstdint> -#include <FairLogger.h> -#include <iostream> +#include "Framework/Logger.h" #include "PayLoadCont.h" +#include <map> +#include <fmt/format.h> #include "ITSMFTReconstruction/PixelData.h" +#include "ITSMFTReconstruction/DecodingStat.h" +#include "DataFormatsITSMFT/NoiseMap.h" + +#define ALPIDE_DECODING_STAT /// \file AlpideCoder.h /// \brief class for the ALPIDE data decoding/encoding @@ -66,6 +71,7 @@ class AlpideCoder static constexpr uint32_t ExpectChipEmpty = 0x1 << 2; static constexpr uint32_t ExpectRegion = 0x1 << 3; static constexpr uint32_t ExpectData = 0x1 << 4; + static constexpr uint32_t ExpectBUSY = 0x1 << 5; static constexpr int NRows = 512; static constexpr int NCols = 1024; static constexpr int NRegions = 32; @@ -78,7 +84,10 @@ class AlpideCoder static constexpr uint32_t MaskDColID = MaskEncoder | MaskPixID; // mask for encoder + dcolumn combination static constexpr uint32_t MaskRegion = 0x1f; // region ID takes 5 bits max (0:31) static constexpr uint32_t MaskChipID = 0x0f; // chip id in module takes 4 bit max - static constexpr uint32_t MaskROFlags = 0x0f; // RO flags in chip header takes 4 bit max + static constexpr uint32_t MaskROFlags = 0x0f; // RO flags in chip trailer takes 4 bit max + static constexpr uint8_t MaskErrBusyViolation = 0x1 << 3; + static constexpr uint8_t MaskErrDataOverrun = 0x3 << 2; + static constexpr uint8_t MaskErrFatal = 0x7 << 1; static constexpr uint32_t MaskTimeStamp = 0xff; // Time stamps as BUNCH_COUNTER[10:3] bits static constexpr uint32_t MaskReserved = 0xff; // mask for reserved byte static constexpr uint32_t MaskHitMap = 0x7f; // mask for hit map: at most 7 hits in bits (0:6) @@ -90,6 +99,8 @@ class AlpideCoder static constexpr uint32_t CHIPEMPTY = 0xe0; // flag for empty chip static constexpr uint32_t DATALONG = 0x0000; // flag for DATALONG static constexpr uint32_t DATASHORT = 0x4000; // flag for DATASHORT + static constexpr uint32_t BUSYOFF = 0xf0; // flag for BUSY_OFF + static constexpr uint32_t BUSYON = 0xf1; // flag for BUSY_ON // true if corresponds to DATALONG or DATASHORT: highest bit must be 0 static bool isData(uint16_t v) { return (v & (0x1 << 15)) == 0; } @@ -103,9 +114,11 @@ class AlpideCoder static bool isEmptyChip(uint8_t b) { return (b & CHIPEMPTY) == CHIPEMPTY; } + static void setNoisyPixels(const NoiseMap* noise) { mNoisyPixels = noise; } + /// decode alpide data for the next non-empty chip from the buffer - template <class T> - static int decodeChip(ChipPixelData& chipData, T& buffer) + template <class T, typename CG> + static int decodeChip(ChipPixelData& chipData, T& buffer, CG cidGetter) { // read record for single non-empty chip, updating on change module and cycle. // return number of records filled (>0), EOFFlag or Error @@ -127,8 +140,11 @@ class AlpideCoder uint8_t dataCM = dataC & (~MaskChipID); // if ((expectInp & ExpectChipEmpty) && dataCM == CHIPEMPTY) { // empty chip was expected - chipData.setChipID(dataC & MaskChipID); // here we set the chip ID within the module + chipData.setChipID(cidGetter(dataC & MaskChipID)); // here we set the global chip ID if (!buffer.next(timestamp)) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::TruncatedChipEmpty); +#endif return unexpectedEOF("CHIP_EMPTY:Timestamp"); } expectInp = ExpectChipHeader | ExpectChipEmpty; @@ -136,8 +152,11 @@ class AlpideCoder } if ((expectInp & ExpectChipHeader) && dataCM == CHIPHEADER) { // chip header was expected - chipData.setChipID(dataC & MaskChipID); // here we set the chip ID within the module + chipData.setChipID(cidGetter(dataC & MaskChipID)); // here we set the global chip ID if (!buffer.next(timestamp)) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::TruncatedChipHeader); +#endif return unexpectedEOF("CHIP_HEADER"); } expectInp = ExpectRegion; // now expect region info @@ -154,13 +173,31 @@ class AlpideCoder if ((expectInp & ExpectChipTrailer) && dataCM == CHIPTRAILER) { // chip trailer was expected expectInp = ExpectChipHeader | ExpectChipEmpty; chipData.setROFlags(dataC & MaskROFlags); +#ifdef ALPIDE_DECODING_STAT + uint8_t roErr = dataC & MaskROFlags; + if (roErr) { + if (roErr == MaskErrBusyViolation) { + chipData.setError(ChipStat::BusyViolation); + } else if (roErr == MaskErrDataOverrun) { + chipData.setError(ChipStat::DataOverrun); + } else if (roErr == MaskErrFatal) { + chipData.setError(ChipStat::Fatal); + } + } +#endif // in case there are entries in the "right" columns buffer, add them to the container if (nRightCHits) { colDPrev++; for (int ihr = 0; ihr < nRightCHits; ihr++) { - chipData.getData().emplace_back(rightColHits[ihr], colDPrev); + addHit(chipData, rightColHits[ihr], colDPrev); } } + if (!chipData.getData().size() && !chipData.isErrorSet()) { + nRightCHits = 0; + colDPrev = 0xffff; + chipData.clear(); + continue; + } break; } @@ -170,6 +207,9 @@ class AlpideCoder // note that here we are checking on the byte rather than the short, need complete to ushort dataS = dataC << 8; if (!buffer.next(dataC)) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::TruncatedRegion); +#endif return unexpectedEOF("CHIPDATA"); } dataS |= dataC; @@ -186,7 +226,7 @@ class AlpideCoder if (colD != colDPrev) { colDPrev++; for (int ihr = 0; ihr < nRightCHits; ihr++) { - chipData.getData().emplace_back(rightColHits[ihr], colDPrev); + addHit(chipData, rightColHits[ihr], colDPrev); } colDPrev = colD; nRightCHits = 0; // reset the buffer @@ -200,14 +240,22 @@ class AlpideCoder if (rightC) { rightColHits[nRightCHits++] = row; // col = colD+1 } else { - chipData.getData().emplace_back(row, colD); // col = colD, left column hits are added directly to the container + addHit(chipData, row, colD); // col = colD, left column hits are added directly to the container } if ((dataS & (~MaskDColID)) == DATALONG) { // multiple hits ? uint8_t hitsPattern = 0; if (!buffer.next(hitsPattern)) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::TruncatedLondData); +#endif return unexpectedEOF("CHIP_DATA_LONG:Pattern"); } +#ifdef ALPIDE_DECODING_STAT + if (hitsPattern & (~MaskHitMap)) { + chipData.setError(ChipStat::WrongDataLongPattern); + } +#endif for (int ip = 0; ip < HitMapSize; ip++) { if (hitsPattern & (0x1 << ip)) { uint16_t addr = pixID + ip + 1, rowE = addr >> 1; @@ -216,12 +264,15 @@ class AlpideCoder if (rightC) { // same as above rightColHits[nRightCHits++] = rowE; } else { - chipData.getData().emplace_back(rowE, colD + rightC); // left column hits are added directly to the container + addHit(chipData, rowE, colD + rightC); // left column hits are added directly to the container } } } } } else { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::NoDataFound); +#endif LOG(ERROR) << "Expected DataShort or DataLong mask, got : " << dataS; return Error; } @@ -229,13 +280,27 @@ class AlpideCoder continue; // end of DATA(SHORT or LONG) processing } + if (dataC == BUSYON) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::BusyOn); +#endif + continue; + } + if (dataC == BUSYOFF) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::BusyOff); +#endif + continue; + } + if (!dataC) { buffer.clear(); // 0 padding reached (end of the cable data), no point in continuing break; } - std::stringstream stream; - stream << "Unknown word 0x" << std::hex << int(dataC) << " [mode = 0x" << int(expectInp) << "]"; - return unexpectedEOF(stream.str().c_str()); // error +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::UnknownWord); +#endif + return unexpectedEOF(fmt::format("Unknown word 0x{:x} [expectation = 0x{:x}]", int(dataC), int(expectInp))); // error } return chipData.getData().size(); @@ -263,9 +328,21 @@ class AlpideCoder // void print() const; void reset(); - // private: + /// Output a non-noisy fired pixel + static void addHit(ChipPixelData& chipData, short row, short col) + { + if (mNoisyPixels) { + auto chipID = chipData.getChipID(); + if (mNoisyPixels->isNoisy(chipID, row, col)) { + return; + } + } + + chipData.getData().emplace_back(row, col); + } + ///< add pixed to compressed matrix, the data must be provided sorted in row/col, no check is done void addPixel(short row, short col) { @@ -338,19 +415,18 @@ class AlpideCoder void resetMap(); ///< error message on unexpected EOF - static int unexpectedEOF(const char* message) - { - printf("Error: unexpected EOF on %s\n", message); - return Error; - } + static int unexpectedEOF(const std::string& message); // ===================================================================== // + + static const NoiseMap* mNoisyPixels; + // cluster map used for the ENCODING only - std::vector<short> mFirstInRow; //! entry of 1st pixel of each non-empty row in the mPix2Encode + std::vector<int> mFirstInRow; //! entry of 1st pixel of each non-empty row in the mPix2Encode std::vector<PixLink> mPix2Encode; //! pool of links: fired pixel + index of the next one in the row // - ClassDefNV(AlpideCoder, 1); + ClassDefNV(AlpideCoder, 3); }; } // namespace itsmft diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/BuildTopologyDictionary.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/BuildTopologyDictionary.h index 12114b7f9ce5a..97d28e542bd1b 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/BuildTopologyDictionary.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/BuildTopologyDictionary.h @@ -82,7 +82,7 @@ class BuildTopologyDictionary std::unordered_map<long unsigned, TopologyInfo> mMapInfo; - ClassDefNV(BuildTopologyDictionary, 3); + ClassDefNV(BuildTopologyDictionary, 4); }; } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index c4dfa639e4071..3934e84220578 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -22,6 +22,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" #include "rANS/rans.h" class TTree; @@ -31,27 +32,33 @@ namespace o2 namespace itsmft { -class CTFCoder +class CTFCoder : public o2::ctf::CTFCoderBase { public: + CTFCoder(o2::detectors::DetID det) : o2::ctf::CTFCoderBase(CTF::getNBlocks(), det) {} + ~CTFCoder() = default; + /// entropy-encode clusters to buffer with CTF template <typename VEC> - static void encode(VEC& buff, const gsl::span<const ROFRecord>& rofRecVec, const gsl::span<const CompClusterExt>& cclusVec, const gsl::span<const unsigned char>& pattVec); + void encode(VEC& buff, const gsl::span<const ROFRecord>& rofRecVec, const gsl::span<const CompClusterExt>& cclusVec, const gsl::span<const unsigned char>& pattVec); /// entropy decode clusters from buffer with CTF template <typename VROF, typename VCLUS, typename VPAT> - static void decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec); + void decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); private: /// compres compact clusters to CompressedClusters - static void compress(CompressedClusters& cc, const gsl::span<const ROFRecord>& rofRecVec, const gsl::span<const CompClusterExt>& cclusVec, const gsl::span<const unsigned char>& pattVec); + void compress(CompressedClusters& cc, const gsl::span<const ROFRecord>& rofRecVec, const gsl::span<const CompClusterExt>& cclusVec, const gsl::span<const unsigned char>& pattVec); + size_t estimateCompressedSize(const CompressedClusters& cc); /// decompress CompressedClusters to compact clusters template <typename VROF, typename VCLUS, typename VPAT> - static void decompress(const CompressedClusters& cc, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec); + void decompress(const CompressedClusters& cc, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec); - static void appendToTree(TTree& tree, o2::detectors::DetID id, CTF& ec); - static void readFromTree(TTree& tree, int entry, o2::detectors::DetID id, std::vector<ROFRecord>& rofRecVec, std::vector<CompClusterExt>& cclusVec, std::vector<unsigned char>& pattVec); + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<ROFRecord>& rofRecVec, std::vector<CompClusterExt>& cclusVec, std::vector<unsigned char>& pattVec); protected: ClassDefNV(CTFCoder, 1); @@ -77,6 +84,10 @@ void CTFCoder::encode(VEC& buff, const gsl::span<const ROFRecord>& rofRecVec, co }; CompressedClusters cc; compress(cc, rofRecVec, cclusVec, pattVec); + // book output size with some margin + auto szIni = estimateCompressedSize(cc); + buff.resize(szIni); + auto ec = CTF::create(buff); using ECB = CTF::base; @@ -84,20 +95,21 @@ void CTFCoder::encode(VEC& buff, const gsl::span<const ROFRecord>& rofRecVec, co ec->getANSHeader().majorVersion = 0; ec->getANSHeader().minorVersion = 1; // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec -#define ENCODE CTF::get(buff.data())->encode +#define ENCODEITSMFT(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); // clang-format off - ENCODE(cc.firstChipROF, CTF::BLCfirstChipROF, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCfirstChipROF], &buff); - ENCODE(cc.bcIncROF, CTF::BLCbcIncROF , o2::rans::ProbabilityBits16Bit, optField[CTF::BLCbcIncROF], &buff); - ENCODE(cc.orbitIncROF, CTF::BLCorbitIncROF, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCorbitIncROF], &buff); - ENCODE(cc.nclusROF, CTF::BLCnclusROF, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCnclusROF], &buff); + ENCODEITSMFT(cc.firstChipROF, CTF::BLCfirstChipROF, 0); + ENCODEITSMFT(cc.bcIncROF, CTF::BLCbcIncROF, 0); + ENCODEITSMFT(cc.orbitIncROF, CTF::BLCorbitIncROF, 0); + ENCODEITSMFT(cc.nclusROF, CTF::BLCnclusROF, 0); // - ENCODE(cc.chipInc, CTF::BLCchipInc, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCchipInc], &buff); - ENCODE(cc.chipMul, CTF::BLCchipMul, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCchipMul], &buff); - ENCODE(cc.row, CTF::BLCrow, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCrow], &buff); - ENCODE(cc.colInc, CTF::BLCcolInc, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCcolInc], &buff); - ENCODE(cc.pattID, CTF::BLCpattID, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCpattID], &buff); - ENCODE(cc.pattMap, CTF::BLCpattMap, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCpattMap], &buff); + ENCODEITSMFT(cc.chipInc, CTF::BLCchipInc, 0); + ENCODEITSMFT(cc.chipMul, CTF::BLCchipMul, 0); + ENCODEITSMFT(cc.row, CTF::BLCrow, 0); + ENCODEITSMFT(cc.colInc, CTF::BLCcolInc, 0); + ENCODEITSMFT(cc.pattID, CTF::BLCpattID, 0); + ENCODEITSMFT(cc.pattMap, CTF::BLCpattMap, 0); // clang-format on + CTF::get(buff.data())->print(getPrefix()); } /// decode entropy-encoded clusters to standard compact clusters @@ -106,18 +118,20 @@ void CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPA { CompressedClusters cc; cc.header = ec.getHeader(); + ec.print(getPrefix()); +#define DECODEITSMFT(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) // clang-format off - ec.decode(cc.firstChipROF, CTF::BLCfirstChipROF); - ec.decode(cc.bcIncROF, CTF::BLCbcIncROF); - ec.decode(cc.orbitIncROF, CTF::BLCorbitIncROF); - ec.decode(cc.nclusROF, CTF::BLCnclusROF); - // - ec.decode(cc.chipInc, CTF::BLCchipInc); - ec.decode(cc.chipMul, CTF::BLCchipMul); - ec.decode(cc.row, CTF::BLCrow); - ec.decode(cc.colInc, CTF::BLCcolInc); - ec.decode(cc.pattID, CTF::BLCpattID); - ec.decode(cc.pattMap, CTF::BLCpattMap); + DECODEITSMFT(cc.firstChipROF, CTF::BLCfirstChipROF); + DECODEITSMFT(cc.bcIncROF, CTF::BLCbcIncROF); + DECODEITSMFT(cc.orbitIncROF, CTF::BLCorbitIncROF); + DECODEITSMFT(cc.nclusROF, CTF::BLCnclusROF); + // + DECODEITSMFT(cc.chipInc, CTF::BLCchipInc); + DECODEITSMFT(cc.chipMul, CTF::BLCchipMul); + DECODEITSMFT(cc.row, CTF::BLCrow); + DECODEITSMFT(cc.colInc, CTF::BLCcolInc); + DECODEITSMFT(cc.pattID, CTF::BLCpattID); + DECODEITSMFT(cc.pattMap, CTF::BLCpattMap); // clang-format on // decompress(cc, rofRecVec, cclusVec, pattVec); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h index 1956756505de2..3090224312f34 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h @@ -28,6 +28,8 @@ namespace o2 namespace itsmft { +#define _OVERRIDE_RUID_HACK_ + class ChipMappingITS { public: @@ -67,6 +69,9 @@ class ChipMappingITS lr = feeID >> 12; ruOnLr = feeID & 0x3f; link = (feeID >> 8) & 0x3; +#ifdef _OVERRIDE_RUID_HACK_ + ruOnLr %= NStavesOnLr[lr]; +#endif } ///< impose user defined FEEId -> ruSW (staveID) conversion, to be used only for forced decoding of corrupted data @@ -129,7 +134,15 @@ class ChipMappingITS } ///< get SW id of the RU from RU HW id - uint8_t FEEId2RUSW(uint16_t hw) const { return mFEEId2RUSW[hw]; } + uint8_t FEEId2RUSW(uint16_t hw) const + { +#ifdef _OVERRIDE_RUID_HACK_ + uint16_t lr, ruOnLr, link; + expandFEEId(hw, lr, ruOnLr, link); + hw = composeFEEId(lr, ruOnLr, link); +#endif + return mFEEId2RUSW[hw]; + } ///< get FEEId of the RU (software id of the RU), read via given link uint16_t RUSW2FEEId(uint16_t sw, uint16_t linkID = 0) const @@ -159,6 +172,9 @@ class ChipMappingITS ///< convert HW cable ID to SW ID for given RU type (see ChipOnRUInfo.cableSW explanation) uint8_t cableHW2SW(uint8_t ruType, uint8_t hwid) const { return mCableHW2SW[ruType][hwid]; } + ///< convert cable iterator ID to the position on the ActiveLanes word in the GBT.header for given RU type; MFT lanes position compatible + uint8_t cablePos(uint8_t ruType, uint8_t id) const { return id; } + ///< get number of chips served by single cable on given RU type int getNChipsPerCable(int ruType) { return NChipsPerCableSB[ruType]; } @@ -166,7 +182,7 @@ class ChipMappingITS int getNCablesOnRUType(int ruType) const { return NCablesPerStaveSB[ruType]; } ///< get pattern of lanes on the RU served by a given RU type - int getCablesOnRUType(int ruType) const { return CablesOnStaveSB[ruType]; } + int getCablesOnRUType(int ruType) const { return mCablesOnStaveSB[ruType]; } ///< get number of chips served by RU of given type (i.e. RU type for ITS) int getNChipsOnRUType(int ruType) const { return NChipsPerStaveSB[ruType]; } @@ -201,8 +217,9 @@ class ChipMappingITS { int sid = 0; for (int i = 0; i < NLayers; i++) { - if (i >= lr) + if (i >= lr) { break; + } sid += NStavesOnLr[i]; } return sid + ruOnLr; @@ -252,11 +269,6 @@ class ChipMappingITS NCablesPerModule[MB] * NModulesPerStaveSB[MB], NCablesPerModule[OB] * NModulesPerStaveSB[OB]}; - ///< pattern of cables per stave of sub-barrel - static constexpr std::array<int, NSubB> CablesOnStaveSB = {(0x1 << NCablesPerModule[IB] * NModulesPerStaveSB[IB]) - 1, - (0x1 << NCablesPerModule[MB] * NModulesPerStaveSB[MB]) - 1, - (0x1 << NCablesPerModule[OB] * NModulesPerStaveSB[OB]) - 1}; - ///< number of chips per sub-barrel static constexpr std::array<int, NSubB> NChipsSB = {NChipsPerStaveSB[IB] * NStavesSB[IB], NChipsPerStaveSB[MB] * NStavesSB[MB], @@ -288,6 +300,8 @@ class ChipMappingITS std::vector<uint8_t> mCableHW2Pos[NSubB]; ///< table of cables positions in the ActiveLanes mask for each RU type std::vector<uint8_t> mCableHWFirstChip[NSubB]; ///< 1st chip of module (relative to the 1st chip of the stave) served by each cable + std::array<int, NSubB> mCablesOnStaveSB = {0}; ///< pattern of cables per stave of sub-barrel + ClassDefNV(ChipMappingITS, 1); }; } // namespace itsmft diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h index af425caef1564..9232ff0df8419 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h @@ -61,10 +61,10 @@ class ChipMappingMFT static constexpr Int_t getNRUs() { return NRUs; } ///< get FEEId of the RU (software id of the RU), read via given link - uint8_t FEEId2RUSW(uint16_t hw) const { return mFEEId2RUSW[hw]; } + uint8_t FEEId2RUSW(uint16_t hw) const { return mFEEId2RUSW[hw & 0xff]; } ///< get HW id of the RU (software id of the RU) - uint16_t RUSW2FEEId(uint16_t sw, uint16_t linkID = 0) const { return mRUInfo[sw].idHW; } + uint16_t RUSW2FEEId(uint16_t sw, uint16_t linkID = 0) const { return ((linkID << 8) + mRUInfo[sw].idHW); } ///< compose FEEid for given stave (ru) relative to layer and link, see documentation in the constructor uint16_t composeFEEId(uint16_t layer, uint16_t ruOnLayer, uint16_t link) const @@ -78,14 +78,14 @@ class ChipMappingMFT auto ddisk = std::div(layer, 2); uint16_t disk = ddisk.quot; uint16_t plane = layer % 2; - return (half << 6) + (disk << 3) + (plane << 2) + zone; + return (link << 8) + (half << 6) + (disk << 3) + (plane << 2) + zone; } ///< decompose FEEid to layer, stave (ru) relative to layer, link, see documentation in the constructor void expandFEEId(uint16_t feeID, uint16_t& layer, uint16_t& ruOnLayer, uint16_t& link) const { - link = 0; - uint16_t half = feeID >> 6; + link = feeID >> 8; + uint16_t half = (feeID >> 6) & 0x1; uint16_t disk = (feeID >> 3) & 0x7; uint16_t plane = (feeID >> 2) & 0x1; uint16_t zone = feeID & 0x3; @@ -99,7 +99,7 @@ class ChipMappingMFT ///< get number of chips served by single cable on given RU type uint8_t getGBTHeaderRUType(Int_t ruType, Int_t cableHW) { - return (cableHW & 0x1f); + return ((0x1 << 7) + (cableHW & 0x1f)); } ///< convert HW cable ID to its position on the ActiveLanes word in the GBT.header for given RU type @@ -108,6 +108,9 @@ class ChipMappingMFT ///< convert HW cable ID to SW ID for give RU type uint8_t cableHW2SW(uint8_t ruType, uint8_t hwid) const { return mCableHW2SW[ruType][hwid]; } + ///< convert cable iterator ID to its position on the ActiveLanes word in the GBT.header for given RU type + uint8_t cablePos(uint8_t ruType, uint8_t id) const { return mCablePos[ruType][id]; } + ///< get chip global SW ID from chipID on module, cable SW ID and stave (RU) info uint16_t getGlobalChipID(uint16_t chOnModuleHW, int cableHW, const RUInfo& ruInfo) const { @@ -198,8 +201,14 @@ class ChipMappingMFT static constexpr std::int16_t getRUDetectorField() { return 0x0; } - ///< get pattern of lanes on the RU served by a given RU type - Int_t getCablesOnRUType(Int_t ruType) const { return (0x1 << NChipsOnRUType[ruType]) - 1; } + uint32_t getCablesOnRUType(Int_t ruType) const + { + uint32_t pattern = 0; + for (Int_t i = 0; i < NRUCables; i++) { + pattern |= (0x1 << mCableHW2Pos[ruType][i]); + } + return pattern; + } ///< get info on sw RU const RUInfo* getRUInfoSW(int ruSW) const { return &mRUInfo[ruSW]; } @@ -209,8 +218,9 @@ class ChipMappingMFT { int sid = 0; for (int i = 0; i < NLayers; i++) { - if (i >= layer) + if (i >= layer) { break; + } sid += NZonesPerLayer; } return sid + ruOnLayer; @@ -276,6 +286,7 @@ class ChipMappingMFT std::vector<uint8_t> mCableHW2SW[NRUs]; ///< table of cables HW to SW conversion for each RU type std::vector<uint8_t> mCableHW2Pos[NRUs]; ///< table of cables positions in the ActiveLanes mask for each RU type + std::vector<uint8_t> mCablePos[NRUs]; ///< reverse table of cables positions in the ActiveLanes mask for each RU type std::vector<uint8_t> mCableHWFirstChip[NRUs]; ///< 1st chip of module (relative to the 1st chip of the stave) served by each cable std::array<std::vector<uint16_t>, NRUs> mRUGlobalChipID; diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index 206cff40aa820..c2c9a3333cb07 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -42,6 +42,8 @@ class MCCompLabel; namespace dataformats { template <typename T> +class ConstMCTruthContainerView; +template <typename T> class MCTruthContainer; } @@ -63,6 +65,7 @@ class Clusterer using CompClusterExt = o2::itsmft::CompClusterExt; using Label = o2::MCCompLabel; using MCTruth = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + using ConstMCTruth = o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>; public: static constexpr int MaxLabels = 10; @@ -95,7 +98,7 @@ class Clusterer uint16_t currCol = 0xffff; ///< Column being processed bool noLeftCol = true; ///< flag that there is no column on the left to check std::array<Label, MaxLabels> labelsBuff; //! temporary buffer for building cluster labels - std::array<PixelData, ClusterPattern::MaxPatternBits * 2> pixArrBuff; //! temporary buffer for pattern calc. + std::vector<PixelData> pixArrBuff; //! temporary buffer for pattern calc. // /// temporary storage for the thread output CompClusCont compClusters; @@ -129,15 +132,20 @@ class Clusterer curr[row] = lastIndex; // store index of the new precluster in the current column buffer } - void fetchMCLabels(int digID, const MCTruth* labelsDig, int& nfilled); + void streamCluster(const std::vector<PixelData>& pixbuf, uint16_t rowMin, uint16_t rowSpanW, uint16_t colMin, uint16_t colSpanW, + uint16_t chipID, + CompClusCont* compClusPtr, PatternCont* patternsPtr, + MCTruth* labelsClusPtr, int nlab, bool isHuge = false); + + void fetchMCLabels(int digID, const ConstMCTruth* labelsDig, int& nfilled); void initChip(const ChipPixelData* curChipData, uint32_t first); void updateChip(const ChipPixelData* curChipData, uint32_t ip); void finishChip(ChipPixelData* curChipData, CompClusCont* compClus, PatternCont* patterns, - const MCTruth* labelsDig, MCTruth* labelsClus); + const ConstMCTruth* labelsDig, MCTruth* labelsClus); void finishChipSingleHitFast(uint32_t hit, ChipPixelData* curChipData, CompClusCont* compClusPtr, - PatternCont* patternsPtr, const MCTruth* labelsDigPtr, MCTruth* labelsClusPTr); + PatternCont* patternsPtr, const ConstMCTruth* labelsDigPtr, MCTruth* labelsClusPTr); void process(uint16_t chip, uint16_t nChips, CompClusCont* compClusPtr, PatternCont* patternsPtr, - const MCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr); + const ConstMCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr); ClustererThread(Clusterer* par = nullptr) : parent(par), curr(column2 + 1), prev(column1 + 1) { diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h new file mode 100644 index 0000000000000..260160052ab2d --- /dev/null +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h @@ -0,0 +1,161 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DecodingStat.h +/// \brief Alpide Chip and GBT link decoding statistics + +#ifndef _ALICEO2_DECODINGSTAT_H_ +#define _ALICEO2_DECODINGSTAT_H_ + +#include <string> +#include <array> +#include <Rtypes.h> +#include "ITSMFTReconstruction/GBTWord.h" + +namespace o2 +{ +namespace itsmft +{ + +struct ChipStat { + + enum DecErrors : int { + BusyViolation, + DataOverrun, + Fatal, + BusyOn, + BusyOff, + TruncatedChipEmpty, // Data was truncated after ChipEmpty + TruncatedChipHeader, // Data was truncated after ChipHeader + TruncatedRegion, // Data was truncated after Region record + TruncatedLondData, // Data was truncated in the LongData record + WrongDataLongPattern, // LongData pattern has highest bit set + NoDataFound, // Region is not followed by Short or Long data + UnknownWord, // Unknow word was seen + NErrorsDefined + }; + + static constexpr std::array<std::string_view, NErrorsDefined> ErrNames = { + "BusyViolation flag ON", // BusyViolation + "DataOverrun flag ON", // DataOverrun + "Fatal flag ON", // Fatal + "BusyON", // BusyOn + "BusyOFF", // BusyOff + "Data truncated after ChipEmpty", // TruncatedChipEmpty + "Data truncated after ChipHeader", // TruncatedChipHeader + "Data truncated after Region", // TruncatedRegion + "Data truncated after LongData", // TruncatedLondData + "LongData pattern has highest bit set", // WrongDataLongPattern + "Region is not followed by Short or Long data", // NoDataFound + "Unknow word" // UnknownWord + }; + + uint16_t chipID = 0; + size_t nHits = 0; + std::array<uint32_t, NErrorsDefined> errorCounts = {}; + ChipStat() = default; + ChipStat(uint16_t id) : chipID(id) {} + + void clear() + { + memset(errorCounts.data(), 0, sizeof(uint32_t) * errorCounts.size()); + nHits = 0; + } + + uint32_t getNErrors() const; + + void addErrors(uint32_t mask) + { + if (mask) { + for (int i = NErrorsDefined; i--;) { + if (mask & (0x1 << i)) { + errorCounts[i]++; + } + } + } + } + + void print(bool skipEmpty = true) const; + + ClassDefNV(ChipStat, 1); +}; + +/// Statistics for per-link decoding +struct GBTLinkDecodingStat { + /// counters for format checks + + enum DecErrors : int { + ErrNoRDHAtStart, // page does not start with RDH + ErrPageNotStopped, // RDH is stopped, but the time is not matching the ~stop packet + ErrStopPageNotEmpty, // Page with RDH.stop is not empty + ErrPageCounterDiscontinuity, // RDH page counters for the same RU/trigger are not continuous + ErrRDHvsGBTHPageCnt, // RDH and GBT header page counters are not consistent + ErrMissingGBTTrigger, // GBT trigger word was expected but not found + ErrMissingGBTHeader, // GBT payload header was expected but not found + ErrMissingGBTTrailer, // GBT payload trailer was expected but not found + ErrNonZeroPageAfterStop, // all lanes were stopped but the page counter in not 0 + ErrUnstoppedLanes, // end of FEE data reached while not all lanes received stop + ErrDataForStoppedLane, // data was received for stopped lane + ErrNoDataForActiveLane, // no data was seen for lane (which was not in timeout) + ErrIBChipLaneMismatch, // chipID (on module) was different from the lane ID on the IB stave + ErrCableDataHeadWrong, // cable data does not start with chip header or empty chip + ErrInvalidActiveLanes, // active lanes pattern conflicts with expected for given RU type + ErrPacketCounterJump, // jump in RDH.packetCounter + ErrPacketDoneMissing, // packet done is missing in the trailer while CRU page is not over + ErrMissingDiagnosticWord, // missing diagnostic word after RDH with stop + ErrGBTWordNotRecognized, // GBT word not recognized + NErrorsDefined + }; + static constexpr std::array<std::string_view, NErrorsDefined> ErrNames = { + "Page data not start with expected RDH", // ErrNoRDHAtStart + "RDH is stopped, but the time is not matching the ~stop packet", // ErrPageNotStopped + "Page with RDH.stop does not contain diagnostic word only", // ErrStopPageNotEmpty + "RDH page counters for the same RU/trigger are not continuous", // ErrPageCounterDiscontinuity + "RDH and GBT header page counters are not consistent", // ErrRDHvsGBTHPageCnt + "GBT trigger word was expected but not found", // ErrMissingGBTTrigger + "GBT payload header was expected but not found", // ErrMissingGBTHeader + "GBT payload trailer was expected but not found", // ErrMissingGBTTrailer + "All lanes were stopped but the page counter in not 0", // ErrNonZeroPageAfterStop + "End of FEE data reached while not all lanes received stop", // ErrUnstoppedLanes + "Data was received for stopped lane", // ErrDataForStoppedLane + "No data was seen for lane (which was not in timeout)", // ErrNoDataForActiveLane + "ChipID (on module) was different from the lane ID on the IB stave", // ErrIBChipLaneMismatch + "Cable data does not start with chip header or empty chip", // ErrCableDataHeadWrong + "Active lanes pattern conflicts with expected for given RU type", // ErrInvalidActiveLanes + "Jump in RDH_packetCounter", // ErrPacketCounterJump + "Packet done is missing in the trailer while CRU page is not over", // ErrPacketDoneMissing + "Missing diagnostic GBT word after RDH with stop", // ErrMissingDiagnosticWord + "GBT word not recognized" // ErrGBTWordNotRecognized + }; + + uint32_t ruLinkID = 0; // Link ID within RU + + // Note: packet here is meant as a group of CRU pages belonging to the same trigger + uint32_t nPackets = 0; // total number of packets (RDH pages) + uint32_t nTriggers = 0; // total number of triggers (ROFs) + std::array<uint32_t, NErrorsDefined> errorCounts = {}; // error counters + std::array<uint32_t, GBTDataTrailer::MaxStateCombinations> packetStates = {}; // packet status from the trailer + + void clear() + { + nPackets = 0; + nTriggers = 0; + errorCounts.fill(0); + packetStates.fill(0); + } + + void print(bool skipEmpty = true) const; + + ClassDefNV(GBTLinkDecodingStat, 2); +}; + +} // namespace itsmft +} // namespace o2 +#endif diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DigitPixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DigitPixelReader.h index 2b45facce2e53..165d149810ed3 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DigitPixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DigitPixelReader.h @@ -19,7 +19,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/Digit.h" #include "DetectorsCommonDataFormats/DetID.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include <TTree.h> #include <vector> @@ -59,12 +59,12 @@ class DigitPixelReader : public PixelReader mIdDig = 0; } - void setDigitsMCTruth(const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* m) + void setDigitsMCTruth(const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* m) { mDigitsMCTruth = m; } - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* getDigitsMCTruth() const override + const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* getDigitsMCTruth() const override { return mDigitsMCTruth; } @@ -99,13 +99,13 @@ class DigitPixelReader : public PixelReader std::vector<o2::itsmft::Digit>* mDigitsSelf = nullptr; std::vector<o2::itsmft::ROFRecord>* mROFRecVecSelf = nullptr; std::vector<o2::itsmft::MC2ROFRecord>* mMC2ROFRecVecSelf = nullptr; - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mDigitsMCTruthSelf = nullptr; + const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* mDigitsMCTruthSelf = nullptr; gsl::span<const o2::itsmft::Digit> mDigits; gsl::span<const o2::itsmft::ROFRecord> mROFRecVec; gsl::span<const o2::itsmft::MC2ROFRecord> mMC2ROFRecVec; - const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mDigitsMCTruth = nullptr; + const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* mDigitsMCTruth = nullptr; Int_t mIdDig = 0; // Digits slot read within ROF Int_t mIdROF = 0; // ROFRecord being red diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h index 54f77b739362d..c38aae4d8e215 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h @@ -23,15 +23,18 @@ #include "ITSMFTReconstruction/PayLoadSG.h" #include "ITSMFTReconstruction/GBTWord.h" #include "ITSMFTReconstruction/RUDecodeData.h" +#include "ITSMFTReconstruction/DecodingStat.h" #include "ITSMFTReconstruction/RUInfo.h" #include "Headers/RAWDataHeader.h" #include "DetectorsRaw/RDHUtils.h" #include "CommonDataFormat/InteractionRecord.h" -#define GBTLINK_DECODE_ERRORCHECK(what) \ - if ((what) == Abort) { \ - discardData(); \ - return AbortedOnError; \ +#define GBTLINK_DECODE_ERRORCHECK(errRes, errEval) \ + errRes = errEval; \ + if ((errRes) == Abort) { \ + discardData(); \ + LOG(ERROR) << "Aborting decoding"; \ + return AbortedOnError; \ } namespace o2 @@ -39,69 +42,6 @@ namespace o2 namespace itsmft { -/// Statistics for per-link decoding -struct GBTLinkDecodingStat { - /// counters for format checks - - enum DecErrors : int { - ErrNoRDHAtStart, // page does not start with RDH - ErrPageNotStopped, // new HB/trigger page started w/o stopping previous one - ErrStopPageNotEmpty, // Page with RDH.stop is not empty - ErrPageCounterDiscontinuity, // RDH page counters for the same RU/trigger are not continuous - ErrRDHvsGBTHPageCnt, // RDH and GBT header page counters are not consistent - ErrMissingGBTTrigger, // GBT trigger word was expected but not found - ErrMissingGBTHeader, // GBT payload header was expected but not found - ErrMissingGBTTrailer, // GBT payload trailer was expected but not found - ErrNonZeroPageAfterStop, // all lanes were stopped but the page counter in not 0 - ErrUnstoppedLanes, // end of FEE data reached while not all lanes received stop - ErrDataForStoppedLane, // data was received for stopped lane - ErrNoDataForActiveLane, // no data was seen for lane (which was not in timeout) - ErrIBChipLaneMismatch, // chipID (on module) was different from the lane ID on the IB stave - ErrCableDataHeadWrong, // cable data does not start with chip header or empty chip - ErrInvalidActiveLanes, // active lanes pattern conflicts with expected for given RU type - ErrPacketCounterJump, // jump in RDH.packetCounter - ErrPacketDoneMissing, // packet done is missing in the trailer while CRU page is not over - NErrorsDefined - }; - static constexpr std::array<std::string_view, NErrorsDefined> ErrNames = { - "Page data not start with expected RDH", // ErrNoRDHAtStart - "New HB/trigger page started w/o stopping previous one", // ErrPageNotStopped - "Page with RDH.stop is not empty", // ErrStopPageNotEmpty - "RDH page counters for the same RU/trigger are not continuous", // ErrPageCounterDiscontinuity - "RDH and GBT header page counters are not consistent", // ErrRDHvsGBTHPageCnt - "GBT trigger word was expected but not found", // ErrMissingGBTTrigger - "GBT payload header was expected but not found", // ErrMissingGBTHeader - "GBT payload trailer was expected but not found", // ErrMissingGBTTrailer - "All lanes were stopped but the page counter in not 0", // ErrNonZeroPageAfterStop - "End of FEE data reached while not all lanes received stop", // ErrUnstoppedLanes - "Data was received for stopped lane", // ErrDataForStoppedLane - "No data was seen for lane (which was not in timeout)", // ErrNoDataForActiveLane - "ChipID (on module) was different from the lane ID on the IB stave", // ErrIBChipLaneMismatch - "Cable data does not start with chip header or empty chip", // ErrCableDataHeadWrong - "Active lanes pattern conflicts with expected for given RU type", // ErrInvalidActiveLanes - "Jump in RDH_packetCounter", // ErrPacketCounterJump - "Packet done is missing in the trailer while CRU page is not over" // ErrPacketDoneMissing - }; - - uint32_t ruLinkID = 0; // Link ID within RU - - // Note: packet here is meant as a group of CRU pages belonging to the same trigger - uint32_t nPackets = 0; // total number of packets - std::array<int, NErrorsDefined> errorCounts = {}; // error counters - std::array<int, GBTDataTrailer::MaxStateCombinations> packetStates = {}; // packet status from the trailer - - void clear() - { - nPackets = 0; - errorCounts.fill(0); - packetStates.fill(0); - } - - void print(bool skipEmpty = true) const; - - ClassDefNV(GBTLinkDecodingStat, 1); -}; - struct RUDecodeData; // forward declaration to allow its linking in the GBTlink struct GBTHeader; struct GBTTrailer; @@ -121,6 +61,7 @@ struct GBTLink { enum ErrorType : int8_t { NoError, Warning, + Skip, Abort }; enum Verbosity : int8_t { Silent = -1, @@ -159,7 +100,8 @@ struct GBTLink { uint32_t errorBits = 0; // bits of the error code of last frame decoding (if any) const RDH* lastRDH = nullptr; o2::InteractionRecord ir; // interaction record - GBTLinkDecodingStat statistics; // decoding statistics + GBTLinkDecodingStat statistics; // link decoding statistics + ChipStat chipStat; // chip decoding statistics RUDecodeData* ruPtr = nullptr; // pointer on the parent RU PayLoadSG rawData; // scatter-gatter buffer for cached CRU pages, each starting with RDH @@ -184,7 +126,12 @@ struct GBTLink { void discardData() { rawData.setDone(); } void printTrigger(const GBTTrigger* gbtTrg); void printHeader(const GBTDataHeader* gbtH); + void printHeader(const GBTDataHeaderL* gbtH); void printTrailer(const GBTDataTrailer* gbtT); + void printDiagnostic(const GBTDiagnostic* gbtD); + void printCableDiagnostic(const GBTCableDiagnostic* gbtD); + void printCalibrationWord(const GBTCalibration* gbtCal); + void printCableStatus(const GBTCableStatus* gbtS); bool nextCRUPage(); #ifndef _RAW_READER_ERROR_CHECKS_ // define dummy inline check methods, will be compiled out @@ -196,23 +143,30 @@ struct GBTLink { ErrorType checkErrorsRDHStopPageEmpty(const RDH& rdh) const { return NoError; } ErrorType checkErrorsTriggerWord(const GBTTrigger* gbtTrg) const { return NoError; } ErrorType checkErrorsHeaderWord(const GBTDataHeader* gbtH) const { return NoError; } + ErrorType checkErrorsHeaderWord(const GBTDataHeaderL* gbtH) const { return NoError; } ErrorType checkErrorsActiveLanes(int cables) const { return NoError; } ErrorType checkErrorsGBTData(int cablePos) const { return NoError; } ErrorType checkErrorsTrailerWord(const GBTDataTrailer* gbtT) const { return NoError; } ErrorType checkErrorsPacketDoneMissing(const GBTDataTrailer* gbtT, bool notEnd) const { return NoError; } ErrorType checkErrorsLanesStops() const { return NoError; } + ErrorType checkErrorsDiagnosticWord(const GBTDiagnostic* gbtD) const { return NoError; } + ErrorType checkErrorsCalibrationWord(const GBTCalibration* gbtCal) const { return NoError; } #else ErrorType checkErrorsRDH(const RDH& rdh); ErrorType checkErrorsRDHStop(const RDH& rdh); ErrorType checkErrorsRDHStopPageEmpty(const RDH& rdh); ErrorType checkErrorsTriggerWord(const GBTTrigger* gbtTrg); ErrorType checkErrorsHeaderWord(const GBTDataHeader* gbtH); + ErrorType checkErrorsHeaderWord(const GBTDataHeaderL* gbtH); ErrorType checkErrorsActiveLanes(int cables); ErrorType checkErrorsGBTData(int cablePos); ErrorType checkErrorsTrailerWord(const GBTDataTrailer* gbtT); ErrorType checkErrorsPacketDoneMissing(const GBTDataTrailer* gbtT, bool notEnd); ErrorType checkErrorsLanesStops(); + ErrorType checkErrorsDiagnosticWord(const GBTDiagnostic* gbtD); + ErrorType checkErrorsCalibrationWord(const GBTCalibration* gbtCal); #endif + ErrorType checkErrorsGBTDataID(const GBTData* dbtD); ClassDefNV(GBTLink, 1); }; @@ -226,6 +180,7 @@ GBTLink::CollectedDataStatus GBTLink::collectROFCableData(const Mapping& chmap) int nw = 0; status = None; auto* currRawPiece = rawData.currentPiece(); + GBTLink::ErrorType errRes = GBTLink::NoError; while (currRawPiece) { // we may loop over multiple CRU page if (dataOffset >= currRawPiece->size) { dataOffset = 0; // start of the RDH @@ -238,18 +193,54 @@ GBTLink::CollectedDataStatus GBTLink::collectROFCableData(const Mapping& chmap) if (verbosity >= VerboseHeaders) { RDHUtils::printRDH(rdh); } - GBTLINK_DECODE_ERRORCHECK(checkErrorsRDH(*rdh)); // make sure we are dealing with RDH - GBTLINK_DECODE_ERRORCHECK(checkErrorsRDHStop(*rdh)); // if new HB starts, the lastRDH must have stop - GBTLINK_DECODE_ERRORCHECK(checkErrorsRDHStopPageEmpty(*rdh)); // end of HBF should be an empty page with stop + + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsRDH(*rdh)); // make sure we are dealing with RDH + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsRDHStop(*rdh)); // if new HB starts, the lastRDH must have stop + // GBTLINK_DECODE_ERRORCHECK(checkErrorsRDHStopPageEmpty(*rdh)); // end of HBF should be an empty page with stop lastRDH = rdh; statistics.nPackets++; + dataOffset += sizeof(RDH); + auto psz = RDHUtils::getMemorySize(*rdh); + if (psz == sizeof(RDH)) { + continue; // filter out empty page + } + if (format == NewFormat && RDHUtils::getStop(*rdh)) { // only diagnostic word can be present after the stop + auto gbtDiag = reinterpret_cast<const GBTDiagnostic*>(&currRawPiece->data[dataOffset]); + if (verbosity >= VerboseHeaders) { + printDiagnostic(gbtDiag); + } + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsDiagnosticWord(gbtDiag)); + dataOffset += RDHUtils::getOffsetToNext(*rdh) - sizeof(RDH); + continue; + } + + // data must start with the GBTHeader + auto gbtH = reinterpret_cast<const GBTDataHeader*>(&currRawPiece->data[dataOffset]); // process GBT header + dataOffset += GBTPaddedWordLength; + if (verbosity >= VerboseHeaders) { + printHeader(gbtH); + } + if (format == OldFormat) { + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsHeaderWord(reinterpret_cast<const GBTDataHeaderL*>(gbtH))); + lanesActive = reinterpret_cast<const GBTDataHeaderL*>(gbtH)->activeLanesL; // TODO do we need to update this for every page? + } else { + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsHeaderWord(gbtH)); + lanesActive = gbtH->activeLanes; // TODO do we need to update this for every page? + } + + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsActiveLanes(chmap.getCablesOnRUType(ruPtr->ruInfo->ruType))); + if (format == OldFormat && reinterpret_cast<const GBTDataHeaderL*>(gbtH)->packetIdx == 0) { // reset flags in case of 1st page of new ROF (old format: judge by RDH) + lanesStop = 0; + lanesWithData = 0; + } + continue; } ruPtr->nCables = ruPtr->ruInfo->nCables; // RSTODO is this needed? TOREMOVE - // data must start with GBT trigger word (unless we work with old format) + // then we expect GBT trigger word (unless we work with old format) const GBTTrigger* gbtTrg = nullptr; if (format == NewFormat) { gbtTrg = reinterpret_cast<const GBTTrigger*>(&currRawPiece->data[dataOffset]); // process GBT trigger @@ -257,37 +248,39 @@ GBTLink::CollectedDataStatus GBTLink::collectROFCableData(const Mapping& chmap) if (verbosity >= VerboseHeaders) { printTrigger(gbtTrg); } - GBTLINK_DECODE_ERRORCHECK(checkErrorsTriggerWord(gbtTrg)); - } - - // next the GBTHeader must come - auto gbtH = reinterpret_cast<const GBTDataHeader*>(&currRawPiece->data[dataOffset]); // process GBT header - dataOffset += GBTPaddedWordLength; - if (verbosity >= VerboseHeaders) { - printHeader(gbtH); - } - GBTLINK_DECODE_ERRORCHECK(checkErrorsHeaderWord(gbtH)); - lanesActive = gbtH->activeLanes; // TODO do we need to update this for every page? - GBTLINK_DECODE_ERRORCHECK(checkErrorsActiveLanes(chmap.getCablesOnRUType(ruPtr->ruInfo->ruType))); - if (format == OldFormat && RDHUtils::getPageCounter(*lastRDH)) { // RSTODO reset flags in case of 1st page of new ROF (old format: judge by RDH) - lanesStop = 0; - lanesWithData = 0; - } else if (gbtH->packetIdx == 0) { // reset flags in case of 1st page of new ROF (new format: judge by header) + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsTriggerWord(gbtTrg)); + statistics.nTriggers++; + if (gbtTrg->noData) { // emtpy trigger + return status; + } lanesStop = 0; lanesWithData = 0; } + if (format == NewFormat) { // at the moment just check if calibration word is there + auto gbtC = reinterpret_cast<const o2::itsmft::GBTCalibration*>(&currRawPiece->data[dataOffset]); + if (gbtC->isCalibrationWord()) { + if (verbosity >= VerboseHeaders) { + printCalibrationWord(gbtC); + } + dataOffset += GBTPaddedWordLength; + } + } auto gbtD = reinterpret_cast<const o2::itsmft::GBTData*>(&currRawPiece->data[dataOffset]); + while (!gbtD->isDataTrailer()) { // start reading real payload nw++; - int cableHW = gbtD->getCableID(), cableSW = chmap.cableHW2SW(ruPtr->ruInfo->ruType, cableHW); if (verbosity >= VerboseData) { gbtD->printX(); } - GBTLINK_DECODE_ERRORCHECK(checkErrorsGBTData(chmap.cableHW2Pos(ruPtr->ruInfo->ruType, cableHW))); - ruPtr->cableData[cableSW].add(gbtD->getW8(), 9); - ruPtr->cableHWID[cableSW] = cableHW; - ruPtr->cableLinkID[cableSW] = idInRU; - ruPtr->cableLinkPtr[cableSW] = this; + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsGBTDataID(gbtD)); + if (errRes != GBTLink::Skip) { + int cableHW = gbtD->getCableID(), cableSW = chmap.cableHW2SW(ruPtr->ruInfo->ruType, cableHW); + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsGBTData(chmap.cableHW2Pos(ruPtr->ruInfo->ruType, cableHW))); + ruPtr->cableData[cableSW].add(gbtD->getW8(), 9); + ruPtr->cableHWID[cableSW] = cableHW; + ruPtr->cableLinkID[cableSW] = idInRU; + ruPtr->cableLinkPtr[cableSW] = this; + } dataOffset += GBTPaddedWordLength; gbtD = reinterpret_cast<const o2::itsmft::GBTData*>(&currRawPiece->data[dataOffset]); } // we are at the trailer, packet is over, check if there are more data on the next page @@ -297,13 +290,15 @@ GBTLink::CollectedDataStatus GBTLink::collectROFCableData(const Mapping& chmap) if (verbosity >= VerboseHeaders) { printTrailer(gbtT); } - GBTLINK_DECODE_ERRORCHECK(checkErrorsTrailerWord(gbtT)); + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsTrailerWord(gbtT)); // we finished the GBT page, but there might be continuation on the next CRU page if (!gbtT->packetDone) { - GBTLINK_DECODE_ERRORCHECK(checkErrorsPacketDoneMissing(gbtT, dataOffset < currRawPiece->size)); + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsPacketDoneMissing(gbtT, dataOffset < currRawPiece->size)); continue; // keep reading next CRU page } - GBTLINK_DECODE_ERRORCHECK(checkErrorsLanesStops()); + if (format == OldFormat) { + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsLanesStops()); + } // accumulate packet states statistics.packetStates[gbtT->getPacketState()]++; // before quitting, store the trigger and IR @@ -312,9 +307,10 @@ GBTLink::CollectedDataStatus GBTLink::collectROFCableData(const Mapping& chmap) ir.orbit = gbtTrg->orbit; trigger = gbtTrg->triggerType; } else { - ir = RDHUtils::getTriggerIR(lastRDH); - trigger = RDHUtils::getTriggerType(lastRDH); + ir = RDHUtils::getTriggerIR(*lastRDH); + trigger = RDHUtils::getTriggerType(*lastRDH); } + return (status = DataSeen); } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTWord.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTWord.h index bc0d61c93ab95..43b063ed4baef 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTWord.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTWord.h @@ -29,7 +29,11 @@ constexpr uint8_t GBTFlagDataHeader = 0xe0; /// GBT payload trailer flag constexpr uint8_t GBTFlagDataTrailer = 0xf0; /// GBT trigger status word flag -constexpr uint8_t GBTFlagTrigger = 0xc0; +constexpr uint8_t GBTFlagTrigger = 0xe8; +/// GBT diagnostic status word flag +constexpr uint8_t GBTFlagDiagnostic = 0xe4; +/// GBT calibration status word flag +constexpr uint8_t GBTFlagCalibration = 0xf8; // GBT header flag in the RDH constexpr uint8_t GBTFlagRDH = 0x00; @@ -37,6 +41,15 @@ constexpr uint8_t GBTFlagRDH = 0x00; constexpr uint8_t GBTFlagDataIB = 0x20; // GBT header flag for the ITS OB: 010 bb ccc with bb -> Connector Number (00,01,10,11), ccc -> Lane Number (0-6) constexpr uint8_t GBTFlagDataOB = 0x40; +// GBT header flag for the MFT +constexpr uint8_t GBTFlagDataMFT = 0x80; +// GBT header flag for the ITS IB idagnostic : 101 bbbbb with bbbbb -> Lane Number (0-8) +constexpr uint8_t GBTFlagDiagnosticIB = 0xa0; +// GBT header flag for the ITS OB diagnostic word: 110 bb ccc with bb -> Connector Number (00,01,10,11), ccc -> Lane Number (0-6) +constexpr uint8_t GBTFlagDiagnosticOB = 0x60; + +// GBT header flag for the ITS Status word : 111 bbbbb with bbbbb -> Lane Number +constexpr uint8_t GBTFlagStatus = 0xe0; constexpr int GBTWordLength = 10; // lentgh in bytes constexpr int GBTPaddedWordLength = 16; // lentgh in bytes with padding @@ -44,18 +57,26 @@ constexpr int GBTPaddedWordLength = 16; // lentgh in bytes with padding struct GBTWord { /// GBT word of 80 bits, bits 72:79 are reserver for GBT Header flag, the rest depends on specifications union { - struct { - uint64_t packetIdx : 16; /// 0:15 Index of Data Packet within trigger - uint64_t activeLanes : 28; /// 16:43 Bit map of lanes active and eligible for readout - uint64_t na0h : 20; /// 44:71 reserved - uint64_t na1h : 8; /// 44:71 reserved + // RS: packing will be needed only if some of the members cross 64 bit boundary + struct /*__attribute__((packed))*/ { + uint64_t activeLanes : 28; /// 0:27 Bit map of lanes active and eligible for readout + uint64_t na0hn : 36; /// 28:71 reserved + uint64_t na1hn : 8; /// 28:71 reserved uint64_t id : 8; /// 72:79 0xe0; Header Status Word (HSW) identifier - }; // HEADER - struct { - uint64_t lanesStops : 28; /// 0:27 Bit map of “Valid Lane stops received”, 1 bit per lane - uint64_t na0t : 4; /// 28:32 reserved - uint64_t lanesTimeout : 28; /// 32:59 Bit map of “Lane timeouts received”, 1 bit per lane - uint64_t na1t : 4; /// 60:63 reserved + }; // HEADER NEW + + struct /*__attribute__((packed))*/ { + uint64_t packetIdx : 16; /// 0:15 Index of Data Packet within trigger + uint64_t activeLanesL : 28; /// 16:43 Bit map of lanes active and eligible for readout + uint64_t na0h : 20; /// 44:64 reserved + uint64_t na1h : 8; /// 64:71 reserved + // uint64_t id : 8; /// 72:79 0xe0; Header Status Word (HSW) identifier + }; // HEADER Legacy + + struct /*__attribute__((packed))*/ { + uint64_t lanesStops : 28; /// 0:27 Bit map of “Valid Lane stops received”, 1 bit per lane, NOT USED + uint64_t lanesTimeout : 28; /// 28:55 Bit map of “Lane timeouts received”, 1 bit per lane, NOT USED + uint64_t na1t : 8; /// 56:63 reserved uint64_t packetDone : 1; /// 64 = 1 when current trigger packets transmission done uint64_t transmissionTimeout : 1; /// 65 = 1 if timeout while waiting for data on lanes uint64_t packetOverflow : 1; /// 66 = 1 if max number of packets reached @@ -64,7 +85,8 @@ struct GBTWord { uint64_t na2t : 3; /// 69:71 reserved // uint8_t id : 8; /// = 0xf0; Trailer Status Word (TSW) identifier }; // TRAILER - struct { + + struct /*__attribute__((packed))*/ { uint64_t triggerType : 12; /// 0:11 12 lowest bits of trigger type received from CTP uint64_t internal : 1; /// 12 Used in Continuous Mode for internally generated trigger uint64_t noData : 1; /// 13 No data expected (too close to previous trigger or error) @@ -77,6 +99,23 @@ struct GBTWord { // uint8_t id : 8; /// = 0xc0; Trigger Status Word (TSW) identifier }; // TRIGGER + struct /*__attribute__((packed))*/ { + uint64_t na0diag : 64; /// + // uint64_t id : 8; /// 72:79 0xe4; diagnostic word identifier + }; // HEADER Legacy + + struct __attribute__((packed)) { + uint64_t calibUserField : 48; /// 0:47 user field + uint64_t calibCounter : 24; /// 48:71 elf-incrementing counter of + // uint64_t id : 8; /// 72:79 0xf8; Calibration Status Word (HSW) identifier + }; /// Calibration Data Word + + struct { + uint64_t diagnosticData : 64; /// 0:63 Error specific diagnostic data + uint64_t laneErrorID : 8; /// 64:71 Identifier of the specific error condition + // uint64_t id : 8; /// 72:79 0xe0; Status Word (HSW) identifier + }; // HEADER Legacy + uint8_t data8[16]; // 80 bits GBT word + optional padding to 128 bits uint64_t data64[2] = {0}; }; @@ -92,12 +131,34 @@ struct GBTWord { /// check if the GBT Header corresponds to GBT trigger word bool isTriggerWord() const { return id == GBTFlagTrigger; } + /// check if the GBT Header corresponds to Diagnostic data + bool isDiagnosticWord() const { return id == GBTFlagDiagnostic; } + + /// check if the GBT Header corresponds to Calibration word + bool isCalibrationWord() const { return id == GBTFlagCalibration; } + /// check if the GBT Header corresponds to ITS IB data (header is combined with lanes info) bool isDataIB() const { return (id & 0xe0) == GBTFlagDataIB; } + /// check if the GBT Header corresponds to ITS IB diagnostics data (header is combined with lanes info) + bool isCableDiagnosticIB() const { return (id & 0xe0) == GBTFlagDiagnosticIB; } + /// check if the GBT Header corresponds to ITS OB data (header is combined with lanes/connector info) bool isDataOB() const { return (id & 0xe0) == GBTFlagDataOB; } + /// check if the GBT Header corresponds to MFT data (header is combined with cable number) + bool isDataMFT() const { return (id & 0xe0) == GBTFlagDataMFT; } + + /// check if the GBT Header corresponds to ITS OB diagnostics data (header is combined with lanes info) + bool isCableDiagnosticOB() const { return (id & 0xe0) == GBTFlagDiagnosticIB; } + + /// check if the GBT Header corresponds to ITS IB or OB data (header is combined with lanes/connector info) + bool isData() const { return isDataIB() || isDataOB() || isDataMFT(); } + + bool isCableDiagnostic() const { return isCableDiagnosticIB() || isCableDiagnosticIB(); } + + bool isStatus() const { return (id & 0xe0) == GBTFlagStatus; } + const uint64_t* getW64() const { return data64; } const uint8_t* getW8() const { return data8; } @@ -110,6 +171,23 @@ struct GBTWord { }; struct GBTDataHeader : public GBTWord { + /// Definition of ITS/MFT GBT Header: 80 bits long word + /// In CRU data it must be the 1st word of the payload + /// + /// bits 0 : 27, Active lanes pattern + /// bits 28 : 71, not used + /// bits 72 : 79, header/trailer indicator + + GBTDataHeader() { id = GBTFlagDataHeader; } + GBTDataHeader(uint32_t lanes) + { + id = GBTFlagDataHeader; + activeLanes = lanes; + } + ClassDefNV(GBTDataHeader, 1); +}; + +struct GBTDataHeaderL : public GBTWord { // legacy version /// Definition of ITS/MFT GBT Header: 80 bits long word /// In CRU data it must be the 1st word of the payload /// @@ -118,24 +196,23 @@ struct GBTDataHeader : public GBTWord { /// bits 44 : 71, not used /// bits 72 : 79, header/trailer indicator - GBTDataHeader() { id = GBTFlagDataHeader; } - GBTDataHeader(int packetID, uint32_t lanes) + GBTDataHeaderL() { id = GBTFlagDataHeader; } + GBTDataHeaderL(int packetID, uint32_t lanes) { id = GBTFlagDataHeader; - activeLanes = lanes; + activeLanesL = lanes; packetIdx = packetID; } - ClassDefNV(GBTDataHeader, 1); + ClassDefNV(GBTDataHeaderL, 1); }; struct GBTDataTrailer : public GBTWord { /// Definition of ITS/MFT GBT trailer: 80 bits long word /// In CRU data it must be the last word of the payload /// - /// bits 0 : 27, Lanes stops received - /// bits 28 : 31, not used - /// bits 32 : 59, Lane timeouts received - /// bits 60 : 63, not used + /// bits 0 : 27, Lanes stops received // not used at the moment + /// bits 28 : 55, Lane timeouts received // not used at the moment + /// bits 56 : 63, not used /// bits 64 : 71, State of GBT_Packet: /// 4: lane_timeouts, if at least 1 lane timed out /// 3: lane_starts_violation, if at least 1 lane had a start violation @@ -219,6 +296,57 @@ struct GBTData : public GBTWord { ClassDefNV(GBTData, 1); }; + +struct GBTDiagnostic : public GBTWord { + /// Definition of GBT diagnostic word + /// In CRU data it must be the only word after the RDH with stop + /// + /// bits 0 : 71, reserved + /// bits 72 : 79, diagnostic flag + + GBTDiagnostic() { id = GBTFlagDiagnostic; } + ClassDefNV(GBTDiagnostic, 1); +}; + +struct GBTCableDiagnostic : public GBTWord { + /// Definition of cable diagnostic word + /// + /// bits 0 : 64, Error specific diagnostic data + /// bits 63 : 71, Identifier of the specific error condition + /// bits 72 : 79, IB or OB diagnostic flag + cable id + + GBTCableDiagnostic(bool ib = true, int lane = 0) { id = (ib ? GBTFlagDiagnosticIB : GBTFlagDiagnosticOB) | (lane & 0x1f); } + int getCableID() const { return id & 0x1f; } // combined connector and lane + bool isIB() const { return (id & 0xe0) == GBTFlagDiagnosticIB; } + bool isOB() const { return (id & 0xe0) == GBTFlagDiagnosticOB; } + ClassDefNV(GBTCableDiagnostic, 1); +}; + +struct GBTCableStatus : public GBTWord { // not sure this is correct, FIXME + /// Definition of cable status word + /// + /// bits 72 : 79, Status flag + cable id + + GBTCableStatus(int lane = 0) { id = GBTFlagStatus | (lane & 0x1f); } + int getCableID() const { return id & 0x1f; } // combined connector and lane + ClassDefNV(GBTCableStatus, 1); +}; + +struct GBTCalibration : public GBTWord { // calibration data word + /// bits 0 : 47, user-written tagging fields + /// bits 48 : 71, self-incrementing counter of CDW words + /// bits 72 : 79, calibration indicator + + GBTCalibration() { id = GBTFlagCalibration; } + GBTCalibration(uint64_t userData, uint16_t counter = 0) + { + id = GBTFlagCalibration; + calibUserField = userData & ((0x1UL << 48) - 1); + calibCounter = counter & ((0x1 << 24) - 1); + } + ClassDefNV(GBTCalibration, 1); +}; + } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h index fd504e6f4694c..44594da48d07b 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h @@ -45,7 +45,7 @@ class LookUp TopologyDictionary mDictionary; int mTopologiesOverThreshold; - ClassDefNV(LookUp, 2); + ClassDefNV(LookUp, 3); }; } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelData.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelData.h index 43548be90a3c7..9472b40263bfd 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelData.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelData.h @@ -15,6 +15,8 @@ #include "DataFormatsITSMFT/Digit.h" #include "CommonDataFormat/InteractionRecord.h" +#include "ITSMFTReconstruction/DecodingStat.h" + #include <vector> #include <utility> #include <cstdint> @@ -116,11 +118,18 @@ class ChipPixelData void setFirstUnmasked(uint32_t n) { mFirstUnmasked = n; } void setTrigger(uint32_t t) { mTrigger = t; } + void setError(ChipStat::DecErrors i) { mErrors |= 0x1 << i; } + void setErrorFlags(uint32_t f) { mErrors |= f; } + bool isErrorSet(ChipStat::DecErrors i) const { return mErrors & (0x1 << i); } + bool isErrorSet() const { return mErrors != 0; } + uint32_t getErrorFlags() const { return mErrors; } + void clear() { mPixels.clear(); mROFlags = 0; mFirstUnmasked = 0; + mErrors = 0; } void swap(ChipPixelData& other) @@ -222,6 +231,7 @@ class ChipPixelData uint32_t mFirstUnmasked = 0; // first unmasked entry in the mPixels uint32_t mStartID = 0; // entry of the 1st pixel data in the whole detector data, for MCtruth access uint32_t mTrigger = 0; // trigger pattern + uint32_t mErrors = 0; // errors set during decoding o2::InteractionRecord mInteractionRecord = {}; // interaction record std::vector<PixelData> mPixels; // vector of pixeld diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h index 7017cc55e2e7d..6a34bd5d1b08a 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h @@ -17,7 +17,7 @@ #include <Rtypes.h> #include "ITSMFTReconstruction/PixelData.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "CommonDataFormat/InteractionRecord.h" #include <vector> @@ -45,7 +45,7 @@ class PixelReader // prepare data of next trigger, return number of non-empty links or chips virtual int decodeNextTrigger() = 0; - virtual const o2::dataformats::MCTruthContainer<o2::MCCompLabel>* getDigitsMCTruth() const + virtual const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* getDigitsMCTruth() const { return nullptr; } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RUDecodeData.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RUDecodeData.h index 00391e9294ec8..e8cda7a633bd4 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RUDecodeData.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RUDecodeData.h @@ -37,7 +37,7 @@ struct RUDecodeData { std::array<int, MaxLinksPerRU> links; // link entry RSTODO: consider removing this and using pointer std::array<uint8_t, MaxCablesPerRU> cableHWID; // HW ID of cable whose data is in the corresponding slot of cableData std::array<uint8_t, MaxCablesPerRU> cableLinkID; // ID of the GBT link transmitting this cable data - std::array<const GBTLink*, MaxCablesPerRU> cableLinkPtr; // Ptr of the GBT link transmitting this cable data + std::array<GBTLink*, MaxCablesPerRU> cableLinkPtr; // Ptr of the GBT link transmitting this cable data int ruSWID = -1; // SW (stave) ID int nCables = 0; // total number of cables decoded for single trigger @@ -55,6 +55,7 @@ struct RUDecodeData { void setROFInfo(ChipPixelData* chipData, const GBTLink* lnk); template <class Mapping> int decodeROF(const Mapping& mp); + void fillChipStatistics(int icab, const ChipPixelData* chipData); ClassDefNV(RUDecodeData, 1); }; @@ -72,18 +73,20 @@ int RUDecodeData::decodeROF(const Mapping& mp) if (!cableData[icab].getSize()) { continue; } - int nhits = 0; - while ((nhits = AlpideCoder::decodeChip(*chipData, cableData[icab]))) { // we register only chips with hits or errors flags set - if (nhits > 0) { - // convert HW chip id within the module to absolute chip id - chipData->setChipID(mp.getGlobalChipID(chipData->getChipID(), cableHWID[icab], *ruInfo)); - setROFInfo(chipData, cableLinkPtr[icab]); - ntot += nhits; - if (++nChipsFired < chipsData.size()) { // fetch next free chip - chipData = &chipsData[nChipsFired]; - } else { - break; // last chip decoded - } + auto cabHW = cableHWID[icab]; + auto chIdGetter = [this, &mp, cabHW](int cid) { + return mp.getGlobalChipID(cid, cabHW, *this->ruInfo); + }; + while (AlpideCoder::decodeChip(*chipData, cableData[icab], chIdGetter) || chipData->isErrorSet()) { // we register only chips with hits or errors flags set + setROFInfo(chipData, cableLinkPtr[icab]); + ntot += chipData->getData().size(); +#ifdef ALPIDE_DECODING_STAT + fillChipStatistics(icab, chipData); +#endif + if (++nChipsFired < chipsData.size()) { // fetch next free chip + chipData = &chipsData[nChipsFired]; + } else { + break; // last chip decoded } } } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h index 1ed100134198c..0b6467b84b6a7 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h @@ -20,6 +20,7 @@ #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "DetectorsRaw/HBFUtils.h" #include "Headers/RAWDataHeader.h" +#include "Headers/DataHeader.h" #include "CommonDataFormat/InteractionRecord.h" #include "ITSMFTReconstruction/GBTLink.h" #include "ITSMFTReconstruction/RUDecodeData.h" @@ -39,7 +40,7 @@ namespace itsmft class ChipPixelData; template <class Mapping> -class RawPixelDecoder : public PixelReader +class RawPixelDecoder final : public PixelReader { using RDH = o2::header::RAWDataHeader; @@ -63,6 +64,14 @@ class RawPixelDecoder : public PixelReader int fillDecodedDigits(DigitContainer& digits, ROFContainer& rofs); const RUDecodeData* getRUDecode(int ruSW) const { return mRUEntry[ruSW] < 0 ? nullptr : &mRUDecodeVec[mRUEntry[ruSW]]; } + const GBTLink* getGBTLink(int i) const { return i < 0 ? nullptr : &mGBTLinks[i]; } + int getNLinks() const { return mGBTLinks.size(); } + + auto getUserDataOrigin() const { return mUserDataOrigin; } + void setUserDataOrigin(header::DataOrigin orig) { mUserDataOrigin = orig; } + + auto getUserDataDescription() const { return mUserDataDescription; } + void setUserDataDescription(header::DataDescription desc) { mUserDataDescription = desc; } void setNThreads(int n); int getNThreads() const { return mNThreads; } @@ -70,7 +79,9 @@ class RawPixelDecoder : public PixelReader void setVerbosity(int v); int getVerbosity() const { return mVerbosity; } - void printReport() const; + void printReport(bool decstat = false, bool skipEmpty = true) const; + + void clearStat(); TStopwatch& getTimerTFStart() { return mTimerTFStart; } TStopwatch& getTimerDecode() { return mTimerDecode; } @@ -89,17 +100,17 @@ class RawPixelDecoder : public PixelReader int getRUEntrySW(int ruSW) const { return mRUEntry[ruSW]; } RUDecodeData* getRUDecode(int ruSW) { return &mRUDecodeVec[mRUEntry[ruSW]]; } GBTLink* getGBTLink(int i) { return i < 0 ? nullptr : &mGBTLinks[i]; } - const GBTLink* getGBTLink(int i) const { return i < 0 ? nullptr : &mGBTLinks[i]; } RUDecodeData& getCreateRUDecode(int ruSW); static constexpr uint16_t NORUDECODED = 0xffff; // this must be > than max N RUs std::vector<GBTLink> mGBTLinks; // active links pool std::unordered_map<uint32_t, LinkEntry> mSubsSpec2LinkID; // link subspec to link entry in the pool mapping - - std::vector<RUDecodeData> mRUDecodeVec; // set of active RUs - std::array<int, Mapping::getNRUs()> mRUEntry; // entry of the RU with given SW ID in the mRUDecodeVec + std::vector<RUDecodeData> mRUDecodeVec; // set of active RUs + std::array<short, Mapping::getNRUs()> mRUEntry; // entry of the RU with given SW ID in the mRUDecodeVec std::string mSelfName; // self name + header::DataOrigin mUserDataOrigin = o2::header::gDataOriginInvalid; // alternative user-provided data origin to pick + header::DataDescription mUserDataDescription = o2::header::gDataDescriptionInvalid; // alternative user-provided description to pick uint16_t mCurRUDecodeID = NORUDECODED; // index of currently processed RUDecode container int mLastReadChipID = -1; // chip ID returned by previous getNextChipData call, used for ordering checks Mapping mMAP; // chip mapping @@ -110,6 +121,7 @@ class RawPixelDecoder : public PixelReader o2::itsmft::ROFRecord::ROFtype mROFCounter = 0; // RSTODO is this needed? eliminate from ROFRecord ? uint32_t mNChipsFiredROF = 0; // counter within the ROF uint32_t mNPixelsFiredROF = 0; // counter within the ROF + uint32_t mNLinksDone = 0; // number of links reached end of data size_t mNChipsFired = 0; // global counter size_t mNPixelsFired = 0; // global counter TStopwatch mTimerTFStart; diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h index aa7840e0ab061..baca178dfd0f6 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h @@ -302,10 +302,12 @@ class RawPixelReader : public PixelReader auto& pixels = chipData.getData(); std::sort(pixels.begin(), pixels.end(), [](auto lhs, auto rhs) { - if (lhs.getRow() < rhs.getRow()) + if (lhs.getRow() < rhs.getRow()) { return true; - if (lhs.getRow() > rhs.getRow()) + } + if (lhs.getRow() > rhs.getRow()) { return false; + } return lhs.getCol() < rhs.getCol(); }); ruData.cableData[chip.cableHWPos].ensureFreeCapacity(40 * (2 + pixels.size())); // make sure buffer has enough capacity @@ -371,7 +373,7 @@ class RawPixelReader : public PixelReader link->data.ensureFreeCapacity(MaxGBTPacketBytes); link->data.addFast(reinterpret_cast<uint8_t*>(&rdh), RDHUtils::getHeaderSize(rdh)); // write RDH for current packet link->nTriggers++; // acknowledge the page, note: here we count pages, not triggers - o2::itsmft::GBTDataHeader gbtHeader(0, link->lanes); + o2::itsmft::GBTDataHeaderL gbtHeader(0, link->lanes); o2::itsmft::GBTDataTrailer gbtTrailer; // lanes will be set on closing the last page gbtHeader.packetIdx = RDHUtils::getPageCounter(rdh); @@ -780,7 +782,7 @@ class RawPixelReader : public PixelReader mDecodingStat.nPagesProcessed++; raw += RDHUtils::getHeaderSize(rdh); int nGBTWords = (RDHUtils::getMemorySize(rdh) - RDHUtils::getHeaderSize(rdh)) / mGBTWordSize - 2; // number of GBT words excluding header/trailer - auto gbtH = reinterpret_cast<const o2::itsmft::GBTDataHeader*>(raw); // process GBT header + auto gbtH = reinterpret_cast<const o2::itsmft::GBTDataHeaderL*>(raw); // process GBT header #ifdef _RAW_READER_ERROR_CHECKS_ if (mVerbose) { @@ -1049,7 +1051,7 @@ class RawPixelReader : public PixelReader raw += RDHUtils::getHeaderSize(rdh); // number of 128 b GBT words excluding header/trailer int nGBTWords = (RDHUtils::getMemorySize(rdh) - RDHUtils::getHeaderSize(rdh)) / o2::itsmft::GBTPaddedWordLength - 2; - auto gbtH = reinterpret_cast<const o2::itsmft::GBTDataHeader*>(raw); // process GBT header + auto gbtH = reinterpret_cast<const o2::itsmft::GBTDataHeaderL*>(raw); // process GBT header #ifdef _RAW_READER_ERROR_CHECKS_ if (mVerbose) { @@ -1254,8 +1256,12 @@ class RawPixelReader : public PixelReader RDHUtils::printRDH(reinterpret_cast<const o2::header::RAWDataHeader*>(getGBTLink(decData.links[decData.cableLinkID[icab]])->lastRDH)); } #endif - - while ((res = mCoder.decodeChip(*chipData, cableData))) { // we register only chips with hits or errors flags set + auto cabHW = decData.cableHWID[icab]; + auto ri = decData.ruInfo; + auto chIdGetter = [this, cabHW, ri](int cid) { + return this->mMAP.getGlobalChipID(cid, cabHW, *ri); + }; + while ((res = mCoder.decodeChip(*chipData, cableData, chIdGetter))) { // we register only chips with hits or errors flags set if (res > 0) { #ifdef _RAW_READER_ERROR_CHECKS_ // for the IB staves check if the cable ID is the same as the chip ID on the module @@ -1269,7 +1275,7 @@ class RawPixelReader : public PixelReader } #endif // convert HW chip id within the module to absolute chip id - chipData->setChipID(mMAP.getGlobalChipID(chipData->getChipID(), decData.cableHWID[icab], *decData.ruInfo)); + // chipData->setChipID(mMAP.getGlobalChipID(chipData->getChipID(), decData.cableHWID[icab], *decData.ruInfo)); chipData->setInteractionRecord(mInteractionRecord); chipData->setTrigger(mTrigger); mDecodingStat.nNonEmptyChips++; diff --git a/Detectors/ITSMFT/common/reconstruction/src/AlpideCoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/AlpideCoder.cxx index 4bedd22889528..8008767b69d08 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/AlpideCoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/AlpideCoder.cxx @@ -13,9 +13,12 @@ #include "ITSMFTReconstruction/AlpideCoder.h" #include <TClass.h> +#include <TFile.h> using namespace o2::itsmft; +const NoiseMap* AlpideCoder::mNoisyPixels = nullptr; + //_____________________________________ void AlpideCoder::print() const { @@ -145,3 +148,8 @@ void AlpideCoder::resetMap() } //_____________________________________ +int AlpideCoder::unexpectedEOF(const std::string& message) +{ + LOG(ERROR) << message; + return Error; +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/BuildTopologyDictionary.cxx b/Detectors/ITSMFT/common/reconstruction/src/BuildTopologyDictionary.cxx index bbd5b74b2b01f..0e553517db7de 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/BuildTopologyDictionary.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/BuildTopologyDictionary.cxx @@ -83,10 +83,11 @@ void BuildTopologyDictionary::setThreshold(double thr) mDictionary.mGroupMap.clear(); mFrequencyThreshold = thr; for (auto& q : mTopologyFrequency) { - if (((double)q.first) / mTotClusters > thr) + if (((double)q.first) / mTotClusters > thr) { mNCommonTopologies++; - else + } else { break; + } } } @@ -108,8 +109,9 @@ void BuildTopologyDictionary::setNCommon(unsigned int nCommon) void BuildTopologyDictionary::setThresholdCumulative(double cumulative) { mTopologyFrequency.clear(); - if (cumulative <= 0. || cumulative >= 1.) + if (cumulative <= 0. || cumulative >= 1.) { cumulative = 0.99; + } double totFreq = 0.; for (auto&& p : mTopologyMap) { // p os pair<ulong,TopoStat> mTopologyFrequency.emplace_back(std::make_pair(p.second.countsTotal, p.first)); @@ -124,12 +126,14 @@ void BuildTopologyDictionary::setThresholdCumulative(double cumulative) totFreq += ((double)(q.first)) / mTotClusters; if (totFreq < cumulative) { mNCommonTopologies++; - } else + } else { break; + } } mFrequencyThreshold = ((double)(mTopologyFrequency[--mNCommonTopologies].first)) / mTotClusters; - while (std::fabs(((double)mTopologyFrequency[mNCommonTopologies].first) / mTotClusters - mFrequencyThreshold) < 1.e-15) + while (std::fabs(((double)mTopologyFrequency[mNCommonTopologies].first) / mTotClusters - mFrequencyThreshold) < 1.e-15) { mNCommonTopologies--; + } mFrequencyThreshold = ((double)mTopologyFrequency[mNCommonTopologies++].first) / mTotClusters; } @@ -156,54 +160,53 @@ void BuildTopologyDictionary::groupRareTopologies() mDictionary.mVectorOfIDs.push_back(gr); } // groupRareTopologies based on binning over number of rows and columns (TopologyDictionary::NumberOfRowClasses * - // NumberOfColClasse) + // NumberOfColClasses) std::unordered_map<int, std::pair<GroupStruct, unsigned long>> tmp_GroupMap; //<group ID, <Group struct, counts>> int grNum = 0; int rowBinEdge = 0; int colBinEdge = 0; - for (int iRowClass = 0; iRowClass < TopologyDictionary::MaxNumberOfClasses; iRowClass++) { - for (int iColClass = 0; iColClass < TopologyDictionary::MaxNumberOfClasses; iColClass++) { - if ((iRowClass + 1) * (iColClass + 1) <= TopologyDictionary::MaxNumberOfClasses) { - rowBinEdge = (iRowClass + 1) * TopologyDictionary::RowClassSpan; - colBinEdge = (iColClass + 1) * TopologyDictionary::ColClassSpan; - grNum = LookUp::groupFinder(rowBinEdge, colBinEdge); - // Create a structure for a group of rare topologies - GroupStruct gr; - gr.mHash = (((unsigned long)(grNum)) << 32) & 0xffffffff00000000; - gr.mErrX = (rowBinEdge)*o2::itsmft::SegmentationAlpide::PitchRow / std::sqrt(12); - gr.mErrZ = (colBinEdge)*o2::itsmft::SegmentationAlpide::PitchCol / std::sqrt(12); - gr.mXCOG = 0; - gr.mZCOG = 0; - gr.mNpixels = rowBinEdge * colBinEdge; - gr.mIsGroup = true; - gr.mFrequency = 0.; - /// A dummy pattern with all fired pixels in the bounding box is assigned to groups of rare topologies. - unsigned char dummyPattern[ClusterPattern::kExtendedPatternBytes] = {0}; - dummyPattern[0] = (unsigned char)rowBinEdge; - dummyPattern[1] = (unsigned char)colBinEdge; - int nBits = rowBinEdge * colBinEdge; - int nBytes = nBits / 8; - for (int iB = 2; iB < nBytes + 2; iB++) { - dummyPattern[iB] = (unsigned char)255; - } - int residualBits = nBits % 8; - if (residualBits) { - unsigned char tempChar = 0; - while (residualBits > 0) { - residualBits--; - tempChar |= 1 << (7 - residualBits); - } - dummyPattern[nBytes + 2] = tempChar; + for (int iRowClass = 0; iRowClass < TopologyDictionary::MaxNumberOfRowClasses; iRowClass++) { + for (int iColClass = 0; iColClass < TopologyDictionary::MaxNumberOfColClasses; iColClass++) { + rowBinEdge = (iRowClass + 1) * TopologyDictionary::RowClassSpan; + colBinEdge = (iColClass + 1) * TopologyDictionary::ColClassSpan; + grNum = LookUp::groupFinder(rowBinEdge, colBinEdge); + // Create a structure for a group of rare topologies + GroupStruct gr; + gr.mHash = (((unsigned long)(grNum)) << 32) & 0xffffffff00000000; + gr.mErrX = (rowBinEdge)*o2::itsmft::SegmentationAlpide::PitchRow / std::sqrt(12); + gr.mErrZ = (colBinEdge)*o2::itsmft::SegmentationAlpide::PitchCol / std::sqrt(12); + gr.mErr2X = gr.mErrX * gr.mErrX; + gr.mErr2Z = gr.mErrZ * gr.mErrZ; + gr.mXCOG = 0; + gr.mZCOG = 0; + gr.mNpixels = rowBinEdge * colBinEdge; + gr.mIsGroup = true; + gr.mFrequency = 0.; + /// A dummy pattern with all fired pixels in the bounding box is assigned to groups of rare topologies. + unsigned char dummyPattern[ClusterPattern::kExtendedPatternBytes] = {0}; + dummyPattern[0] = (unsigned char)rowBinEdge; + dummyPattern[1] = (unsigned char)colBinEdge; + int nBits = rowBinEdge * colBinEdge; + int nBytes = nBits / 8; + for (int iB = 2; iB < nBytes + 2; iB++) { + dummyPattern[iB] = (unsigned char)255; + } + int residualBits = nBits % 8; + if (residualBits) { + unsigned char tempChar = 0; + while (residualBits > 0) { + residualBits--; + tempChar |= 1 << (7 - residualBits); } - gr.mPattern.setPattern(dummyPattern); - // Filling the map for groups - tmp_GroupMap[grNum] = std::make_pair(gr, 0); + dummyPattern[nBytes + 2] = tempChar; } + gr.mPattern.setPattern(dummyPattern); + // Filling the map for groups + tmp_GroupMap[grNum] = std::make_pair(gr, 0); } } - int rs; int cs; int index; @@ -230,8 +233,9 @@ void BuildTopologyDictionary::groupRareTopologies() GroupStruct& gr = mDictionary.mVectorOfIDs[iKey]; if (!gr.mIsGroup) { mDictionary.mCommonMap.insert(std::make_pair(gr.mHash, iKey)); - if (gr.mPattern.getUsedBytes() == 1) + if (gr.mPattern.getUsedBytes() == 1) { mDictionary.mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.mBitmap[2]] = iKey; + } } else { mDictionary.mGroupMap.insert(std::make_pair((int)(gr.mHash >> 32) & 0x00000000ffffffff, iKey)); } diff --git a/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx index 3e50e5434cfca..c06dc9c60f555 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx @@ -20,19 +20,18 @@ using namespace o2::itsmft; ///___________________________________________________________________________________ // Register encoded data in the tree (Fill is not called, will be done by caller) -void CTFCoder::appendToTree(TTree& tree, o2::detectors::DetID id, CTF& ec) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) { - ec.appendToTree(tree, id.getName()); + ec.appendToTree(tree, mDet.getName()); } ///___________________________________________________________________________________ // extract and decode data from the tree -void CTFCoder::readFromTree(TTree& tree, int entry, o2::detectors::DetID id, - std::vector<ROFRecord>& rofRecVec, std::vector<CompClusterExt>& cclusVec, std::vector<unsigned char>& pattVec) +void CTFCoder::readFromTree(TTree& tree, int entry, std::vector<ROFRecord>& rofRecVec, std::vector<CompClusterExt>& cclusVec, std::vector<unsigned char>& pattVec) { assert(entry >= 0 && entry < tree.GetEntries()); CTF ec; - ec.readFromTree(tree, id.getName(), entry); + ec.readFromTree(tree, mDet.getName(), entry); decode(ec, rofRecVec, cclusVec, pattVec); } @@ -119,3 +118,71 @@ void CTFCoder::compress(CompressedClusters& cc, // store explicit patters as they are memcpy(cc.pattMap.data(), pattVec.data(), cc.header.nPatternBytes); // RSTODO: do we need this? } + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + CompressedClusters cc; // just to get member types +#define MAKECODER(part, slot) createCoder<decltype(part)::value_type>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(cc.firstChipROF, CTF::BLCfirstChipROF); + MAKECODER(cc.bcIncROF, CTF::BLCbcIncROF ); + MAKECODER(cc.orbitIncROF, CTF::BLCorbitIncROF ); + MAKECODER(cc.nclusROF, CTF::BLCnclusROF ); + // + MAKECODER(cc.chipInc, CTF::BLCchipInc ); + MAKECODER(cc.chipMul, CTF::BLCchipMul ); + MAKECODER(cc.row, CTF::BLCrow ); + MAKECODER(cc.colInc, CTF::BLCcolInc ); + MAKECODER(cc.pattID, CTF::BLCpattID ); + MAKECODER(cc.pattMap, CTF::BLCpattMap ); + // clang-format on +} + +///________________________________ +size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) +{ + 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<decltype(vec)>::type::value_type +#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ + rans::calculateMaxBufferSize(vec.size(), reinterpret_cast<const o2::rans::LiteralEncoder64<VTP(vec)>*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) + sz += ESTSIZE(cc.firstChipROF, CTF::BLCfirstChipROF); + sz += ESTSIZE(cc.bcIncROF, CTF::BLCbcIncROF ); + sz += ESTSIZE(cc.orbitIncROF, CTF::BLCorbitIncROF ); + sz += ESTSIZE(cc.nclusROF, CTF::BLCnclusROF ); + // + sz += ESTSIZE(cc.chipInc, CTF::BLCchipInc ); + sz += ESTSIZE(cc.chipMul, CTF::BLCchipMul ); + sz += ESTSIZE(cc.row, CTF::BLCrow ); + sz += ESTSIZE(cc.colInc, CTF::BLCcolInc ); + sz += ESTSIZE(cc.pattID, CTF::BLCpattID ); + sz += ESTSIZE(cc.pattMap, CTF::BLCpattMap ); + + // clang-format on + + LOG(INFO) << "Estimated output size is " << sz << " bytes"; + return sz; +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx index 6302f9d9b3f7c..4e78c426392ff 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx @@ -24,7 +24,6 @@ constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::NModulesAlongSt constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::NChipsPerModuleSB; constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::NModulesPerStaveSB; constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::NCablesPerStaveSB; -constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::CablesOnStaveSB; constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::NStavesSB; constexpr std::array<int, ChipMappingITS::NSubB> ChipMappingITS::NChipsPerStaveSB; @@ -88,9 +87,13 @@ ChipMappingITS::ChipMappingITS() cInfo.chipOnCable = 0; // every chip is master mCableHW2SW[IB][cInfo.cableHW] = cInfo.cableSW; mCableHW2Pos[IB][cInfo.cableHW] = cInfo.cableHWPos; + mCablesOnStaveSB[IB] |= 0x1 << cInfo.cableHWPos; // account in lanes pattern mCableHWFirstChip[IB][i] = 0; // stave and module are the same } + // [i][j] gives lane id for lowest(i=0) and highest(i=1) 7 chips of HW module (j+1) (1-4 for ML, 1-7 for OL) + const int LANEID[2][7] = {{6, 5, 4, 3, 2, 1, 0}, {0, 1, 2, 3, 4, 5, 6}}; + const int maxModulesPerStave = NModulesPerStaveSB[OB]; const int chipsOnCable = 7; for (int bid = MB; bid <= OB; bid++) { // MB and OB staves have similar layout mChipInfoEntrySB[bid] = ctrChip; @@ -108,10 +111,13 @@ ChipMappingITS::ChipMappingITS() cInfo.chipOnModuleSW = i % NChipsPerModuleSB[bid]; cInfo.chipOnModuleHW = ChipOBModSW2HW[cInfo.chipOnModuleSW]; - uint8_t connector = (hstave << 1) + (cInfo.chipOnModuleSW < (NChipsPerModuleSB[bid] / 2) ? 0 : 1); - cInfo.cableHW = (connector << 3) + (cInfo.moduleHW - 1); + bool upper7 = cInfo.chipOnModuleSW >= (NChipsPerModuleSB[bid] / 2); + + uint8_t connector = 2 * hstave + upper7; + cInfo.cableHW = (connector << 3) + LANEID[upper7][cInfo.moduleHW - 1]; cInfo.cableSW = i / chipsOnCable; - cInfo.cableHWPos = (cInfo.moduleHW - 1) + connector * (NModulesPerStaveSB[bid] / 2); + cInfo.cableHWPos = LANEID[upper7][cInfo.moduleHW - 1] + connector * maxModulesPerStave / 2; + mCablesOnStaveSB[bid] |= 0x1 << cInfo.cableHWPos; // account in lanes pattern cInfo.chipOnCable = cInfo.chipOnModuleSW % (NChipsPerModuleSB[bid] / 2); // each cable serves half module mCableHW2SW[bid][cInfo.cableHW] = cInfo.cableSW; mCableHW2Pos[bid][cInfo.cableHW] = cInfo.cableHWPos; diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx index 5f777fc8c3faf..b59a1ca99b26a 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx @@ -1652,6 +1652,7 @@ ChipMappingMFT::ChipMappingMFT() mChipInfoEntryRU[iRU] = ctrChip; mCableHW2SW[iRU].resize(NRUCables, 0xff); mCableHW2Pos[iRU].resize(NRUCables, 0xff); + mCablePos[iRU].resize(NRUCables, 0xff); mCableHWFirstChip[iRU].resize(NRUCables, 0xff); } else { if ((layer != curLayer) || (zone != curZone) || (half != curHalf)) { @@ -1676,10 +1677,11 @@ ChipMappingMFT::ChipMappingMFT() chInfo.cableHW = ChipConnectorCable[chInfo.moduleHW][chInfo.chipOnModuleSW]; chInfo.cableSW = chipOnRU; - chInfo.cableHWPos = chipOnRU; + chInfo.cableHWPos = chInfo.cableHW; chInfo.chipOnCable = 0; + mCablePos[iRU][chInfo.id] = chInfo.cableHWPos; mCableHW2Pos[iRU][chInfo.cableHW] = chInfo.cableHWPos; mCableHW2SW[iRU][chInfo.cableHW] = chInfo.cableSW; mCableHWFirstChip[iRU][chInfo.cableHW] = 0; diff --git a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx index 27832c45ec84d..741b0fd28d6fc 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx @@ -156,7 +156,7 @@ void Clusterer::process(int nThreads, PixelReader& reader, CompClusCont* compClu //__________________________________________________ void Clusterer::ClustererThread::process(uint16_t chip, uint16_t nChips, CompClusCont* compClusPtr, PatternCont* patternsPtr, - const MCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr) + const ConstMCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr) { if (stats.empty() || stats.back().firstChip + stats.back().nChips < chip) { // there is a jump, register new block stats.emplace_back(ThreadStat{chip, 0, uint32_t(compClusPtr->size()), patternsPtr ? uint32_t(patternsPtr->size()) : 0, 0, 0}); @@ -199,7 +199,7 @@ void Clusterer::ClustererThread::process(uint16_t chip, uint16_t nChips, CompClu //__________________________________________________ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClusCont* compClusPtr, - PatternCont* patternsPtr, const MCTruth* labelsDigPtr, MCTruth* labelsClusPTr) + PatternCont* patternsPtr, const ConstMCTruth* labelsDigPtr, MCTruth* labelsClusPtr) { auto clustersCount = compClusPtr->size(); const auto& pixData = curChipData->getData(); @@ -210,19 +210,18 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus } uint16_t rowMax = 0, rowMin = 65535; uint16_t colMax = 0, colMin = 65535; - int nlab = 0, npix = 0; + int nlab = 0; int next = preClusterHeads[i1]; + pixArrBuff.clear(); while (next >= 0) { const auto& pixEntry = pixels[next]; const auto pix = pixData[pixEntry.second]; - if (npix < pixArrBuff.size()) { - pixArrBuff[npix++] = pix; // needed for cluster topology - adjustBoundingBox(pix.getRowDirect(), pix.getCol(), rowMin, rowMax, colMin, colMax); - if (labelsClusPTr) { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second - fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab); - } - next = pixEntry.first; + pixArrBuff.push_back(pix); // needed for cluster topology + adjustBoundingBox(pix.getRowDirect(), pix.getCol(), rowMin, rowMax, colMin, colMax); + if (labelsClusPtr) { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second + fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab); } + next = pixEntry.first; } preClusterIndices[i1] = -1; for (int i2 = i1 + 1; i2 < preClusterHeads.size(); ++i2) { @@ -233,84 +232,117 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus while (next >= 0) { const auto& pixEntry = pixels[next]; const auto pix = pixData[pixEntry.second]; // PixelData - if (npix < pixArrBuff.size()) { - pixArrBuff[npix++] = pix; // needed for cluster topology - adjustBoundingBox(pix.getRowDirect(), pix.getCol(), rowMin, rowMax, colMin, colMax); - if (labelsClusPTr) { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second - fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab); - } - next = pixEntry.first; + pixArrBuff.push_back(pix); // needed for cluster topology + adjustBoundingBox(pix.getRowDirect(), pix.getCol(), rowMin, rowMax, colMin, colMax); + if (labelsClusPtr) { // the MCtruth for this pixel is at curChipData->startID+pixEntry.second + fetchMCLabels(pixEntry.second + curChipData->getStartID(), labelsDigPtr, nlab); } + next = pixEntry.first; } preClusterIndices[i2] = -1; } - uint16_t rowSpan = rowMax - rowMin + 1, colSpan = colMax - colMin + 1; - uint16_t colSpanW = colSpan, rowSpanW = rowSpan; - if (colSpan * rowSpan > ClusterPattern::MaxPatternBits) { // need to store partial info - // will curtail largest dimension - if (colSpan > rowSpan) { - if ((colSpanW = ClusterPattern::MaxPatternBits / rowSpan) == 0) { - colSpanW = 1; - rowSpanW = ClusterPattern::MaxPatternBits; - } - } else { - if ((rowSpanW = ClusterPattern::MaxPatternBits / colSpan) == 0) { - rowSpanW = 1; - colSpanW = ClusterPattern::MaxPatternBits; - } + + auto chipID = curChipData->getChipID(); + uint16_t colSpan = (colMax - colMin + 1); + uint16_t rowSpan = (rowMax - rowMin + 1); + if (colSpan <= o2::itsmft::ClusterPattern::MaxColSpan && + rowSpan <= o2::itsmft::ClusterPattern::MaxRowSpan) { + streamCluster(pixArrBuff, rowMin, rowSpan, colMin, colSpan, chipID, + compClusPtr, patternsPtr, labelsClusPtr, nlab); + } else { + LOG(WARNING) << "Splitting a huge cluster ! ChipID: " << chipID; + + colSpan %= o2::itsmft::ClusterPattern::MaxColSpan; + if (colSpan == 0) { + colSpan = o2::itsmft::ClusterPattern::MaxColSpan; } - } - if (labelsClusPTr) { // MC labels were requested - auto cnt = compClusPtr->size(); - for (int i = nlab; i--;) { - labelsClusPTr->addElement(cnt, labelsBuff[i]); + rowSpan %= o2::itsmft::ClusterPattern::MaxRowSpan; + if (rowSpan == 0) { + rowSpan = o2::itsmft::ClusterPattern::MaxRowSpan; } + + do { + uint16_t r = rowMin, rsp = rowSpan; + + do { + // Select a subset of pixels fitting the reduced bounding box + std::vector<PixelData> pixbuf; + auto colMax = colMin + colSpan, rowMax = r + rsp; + for (const auto& pix : pixArrBuff) { + if (pix.getRowDirect() >= r && pix.getRowDirect() < rowMax && + pix.getCol() >= colMin && pix.getCol() < colMax) { + pixbuf.push_back(pix); + } + } + // Stream a piece of cluster only if the reduced bounding box is not empty + if (!pixbuf.empty()) { + streamCluster(pixbuf, r, rsp, colMin, colSpan, chipID, + compClusPtr, patternsPtr, labelsClusPtr, nlab, true); + } + r += rsp; + rsp = o2::itsmft::ClusterPattern::MaxRowSpan; + } while (r < rowMax); + + colMin += colSpan; + colSpan = o2::itsmft::ClusterPattern::MaxColSpan; + } while (colMin < colMax); } + } +} - // add to compact clusters, which must be always filled - unsigned char patt[ClusterPattern::MaxPatternBytes] = {0}; // RSTODO FIX pattern filling - for (int i = 0; i < npix; i++) { - const auto pix = pixArrBuff[i]; - unsigned short ir = pix.getRowDirect() - rowMin, ic = pix.getCol() - colMin; - if (ir < rowSpanW && ic < colSpanW) { - int nbits = ir * colSpanW + ic; - patt[nbits >> 3] |= (0x1 << (7 - (nbits % 8))); - } +void Clusterer::ClustererThread::streamCluster(const std::vector<PixelData>& pixbuf, uint16_t rowMin, uint16_t rowSpanW, uint16_t colMin, uint16_t colSpanW, uint16_t chipID, CompClusCont* compClusPtr, PatternCont* patternsPtr, MCTruth* labelsClusPtr, int nlab, bool isHuge) +{ + if (labelsClusPtr) { // MC labels were requested + auto cnt = compClusPtr->size(); + for (int i = nlab; i--;) { + labelsClusPtr->addElement(cnt, labelsBuff[i]); } - uint16_t pattID = (parent->mPattIdConverter.size() == 0) ? CompCluster::InvalidPatternID : parent->mPattIdConverter.findGroupID(rowSpanW, colSpanW, patt); - if (pattID == CompCluster::InvalidPatternID || parent->mPattIdConverter.isGroup(pattID)) { + } + + // add to compact clusters, which must be always filled + unsigned char patt[ClusterPattern::MaxPatternBytes] = {0}; // RSTODO FIX pattern filling + for (const auto& pix : pixbuf) { + unsigned short ir = pix.getRowDirect() - rowMin, ic = pix.getCol() - colMin; + int nbits = ir * colSpanW + ic; + patt[nbits >> 3] |= (0x1 << (7 - (nbits % 8))); + } + uint16_t pattID = (isHuge || parent->mPattIdConverter.size() == 0) ? CompCluster::InvalidPatternID : parent->mPattIdConverter.findGroupID(rowSpanW, colSpanW, patt); + if (pattID == CompCluster::InvalidPatternID || parent->mPattIdConverter.isGroup(pattID)) { + if (pattID != CompCluster::InvalidPatternID) { + //For groupped topologies, the reference pixel is the COG pixel float xCOG = 0., zCOG = 0.; ClusterPattern::getCOG(rowSpanW, colSpanW, patt, xCOG, zCOG); rowMin += round(xCOG); colMin += round(zCOG); - if (patternsPtr) { - patternsPtr->emplace_back((unsigned char)rowSpanW); - patternsPtr->emplace_back((unsigned char)colSpanW); - int nBytes = rowSpanW * colSpanW / 8; - if (((rowSpanW * colSpanW) % 8) != 0) - nBytes++; - patternsPtr->insert(patternsPtr->end(), std::begin(patt), std::begin(patt) + nBytes); + } + if (patternsPtr) { + patternsPtr->emplace_back((unsigned char)rowSpanW); + patternsPtr->emplace_back((unsigned char)colSpanW); + int nBytes = rowSpanW * colSpanW / 8; + if (((rowSpanW * colSpanW) % 8) != 0) { + nBytes++; } + patternsPtr->insert(patternsPtr->end(), std::begin(patt), std::begin(patt) + nBytes); } - compClusPtr->emplace_back(rowMin, colMin, pattID, curChipData->getChipID()); } + compClusPtr->emplace_back(rowMin, colMin, pattID, chipID); } //__________________________________________________ void Clusterer::ClustererThread::finishChipSingleHitFast(uint32_t hit, ChipPixelData* curChipData, CompClusCont* compClusPtr, - PatternCont* patternsPtr, const MCTruth* labelsDigPtr, MCTruth* labelsClusPTr) + PatternCont* patternsPtr, const ConstMCTruth* labelsDigPtr, MCTruth* labelsClusPtr) { auto clustersCount = compClusPtr->size(); auto pix = curChipData->getData()[hit]; uint16_t row = pix.getRowDirect(), col = pix.getCol(); - if (labelsClusPTr) { // MC labels were requested + if (labelsClusPtr) { // MC labels were requested int nlab = 0; fetchMCLabels(curChipData->getStartID() + hit, labelsDigPtr, nlab); auto cnt = compClusPtr->size(); for (int i = nlab; i--;) { - labelsClusPTr->addElement(cnt, labelsBuff[i]); + labelsClusPtr->addElement(cnt, labelsBuff[i]); } } @@ -409,7 +441,7 @@ void Clusterer::ClustererThread::updateChip(const ChipPixelData* curChipData, ui } //__________________________________________________ -void Clusterer::ClustererThread::fetchMCLabels(int digID, const MCTruth* labelsDig, int& nfilled) +void Clusterer::ClustererThread::fetchMCLabels(int digID, const ConstMCTruth* labelsDig, int& nfilled) { // transfer MC labels to cluster if (nfilled >= MaxLabels) { diff --git a/Detectors/ITSMFT/common/reconstruction/src/DecodingStat.cxx b/Detectors/ITSMFT/common/reconstruction/src/DecodingStat.cxx new file mode 100644 index 0000000000000..295a458cc9a46 --- /dev/null +++ b/Detectors/ITSMFT/common/reconstruction/src/DecodingStat.cxx @@ -0,0 +1,68 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ChipStat.cxx +/// \brief Alpide Chip decoding statistics + +#include <bitset> +#include "ITSMFTReconstruction/DecodingStat.h" +#include "Framework/Logger.h" + +using namespace o2::itsmft; +constexpr std::array<std::string_view, ChipStat::NErrorsDefined> ChipStat::ErrNames; + +///_________________________________________________________________ +/// print chip decoding statistics +void ChipStat::print(bool skipEmpty) const +{ + uint32_t nErr = 0; + for (int i = NErrorsDefined; i--;) { + nErr += errorCounts[i]; + } + LOGF(INFO, "Chip#%5d NHits: %9zu errors: %u", chipID, nHits, nErr); + for (int i = 0; i < NErrorsDefined; i++) { + if (!skipEmpty || errorCounts[i]) { + LOGF(INFO, "%-70s: %u", ErrNames[i].data(), errorCounts[i]); + } + } +} + +///_________________________________________________________________ +uint32_t ChipStat::getNErrors() const +{ + uint32_t nerr = 0; + for (int i = NErrorsDefined; i--;) { + nerr += errorCounts[i]; + } + return nerr; +} + +///_________________________________________________________________ +/// print link decoding statistics +void GBTLinkDecodingStat::print(bool skipEmpty) const +{ + int nErr = 0; + for (int i = NErrorsDefined; i--;) { + nErr += errorCounts[i]; + } + LOGF(INFO, "GBTLink#0x%d Packet States Statistics (total packets: %d, triggers: %d)", ruLinkID, nPackets, nTriggers); + for (int i = 0; i < GBTDataTrailer::MaxStateCombinations; i++) { + if (packetStates[i]) { + std::bitset<GBTDataTrailer::NStatesDefined> patt(i); + LOGF(INFO, "counts for triggers B[%s] : %d", patt.to_string().c_str(), packetStates[i]); + } + } + LOGF(INFO, "Decoding errors: %u", nErr); + for (int i = 0; i < NErrorsDefined; i++) { + if (!skipEmpty || errorCounts[i]) { + LOGF(INFO, "%-70s: %u", ErrNames[i].data(), errorCounts[i]); + } + } +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx index f69a93a0f020d..44fd0cb25f224 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx @@ -125,8 +125,9 @@ bool DigitPixelReader::readNextEntry() // load next entry from the self-managed input auto nev = mInputTree->GetEntries(); auto evID = mInputTree->GetReadEntry(); - if (evID < -1) + if (evID < -1) { evID = -1; + } if (++evID < nev) { init(); mInputTree->GetEntry(evID); diff --git a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx index dc9e5dc3009b0..efafcf3422abc 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx @@ -21,38 +21,9 @@ using namespace o2::itsmft; -///====================================================================== -/// Decoding statistics for a single GBT link -///====================================================================== -using GBTS = GBTLinkDecodingStat; using RDHUtils = o2::raw::RDHUtils; using RDH = o2::header::RAWDataHeader; -constexpr std::array<std::string_view, GBTS::NErrorsDefined> GBTS::ErrNames; - -///_________________________________________________________________ -/// print link decoding statistics -void GBTS::print(bool skipEmpty) const -{ - int nErr = 0; - for (int i = NErrorsDefined; i--;) { - nErr += errorCounts[i]; - } - printf("GBTLink#0x%d Packet States Statistics (total packets: %d)\n", ruLinkID, nPackets); - for (int i = 0; i < GBTDataTrailer::MaxStateCombinations; i++) { - if (packetStates[i]) { - std::bitset<GBTDataTrailer::NStatesDefined> patt(i); - printf("counts for triggers B[%s] : %d\n", patt.to_string().c_str(), packetStates[i]); - } - } - printf("Decoding errors: %d\n", nErr); - for (int i = 0; i < NErrorsDefined; i++) { - if (!skipEmpty || errorCounts[i]) { - printf("%-70s: %d\n", ErrNames[i].data(), errorCounts[i]); - } - } -} - ///====================================================================== /// GBT Link data decoding class ///====================================================================== @@ -99,7 +70,15 @@ void GBTLink::clear(bool resetStat, bool resetTFRaw) void GBTLink::printTrigger(const GBTTrigger* gbtTrg) { gbtTrg->printX(); - LOG(INFO) << "Trigger : Orbit " << gbtTrg->orbit << " BC: " << gbtTrg->bc; + std::bitset<12> trb(gbtTrg->triggerType); + LOG(INFO) << "Trigger : Orbit " << gbtTrg->orbit << " BC: " << gbtTrg->bc << " Trigger: " << trb << " noData:" << gbtTrg->noData << " internal:" << gbtTrg->internal; +} + +///_________________________________________________________________ +void GBTLink::printCalibrationWord(const GBTCalibration* gbtCal) +{ + gbtCal->printX(); + LOGF(INFO, "Calibration word %5d | user_data 0x%06lx", gbtCal->calibCounter, gbtCal->calibUserField); } ///_________________________________________________________________ @@ -110,6 +89,14 @@ void GBTLink::printHeader(const GBTDataHeader* gbtH) LOG(INFO) << "Header : Active Lanes " << LA; } +///_________________________________________________________________ +void GBTLink::printHeader(const GBTDataHeaderL* gbtH) +{ + gbtH->printX(); + std::bitset<28> LA(gbtH->activeLanesL); + LOG(INFO) << "HeaderL : Active Lanes " << LA; +} + ///_________________________________________________________________ void GBTLink::printTrailer(const GBTDataTrailer* gbtT) { @@ -118,6 +105,27 @@ void GBTLink::printTrailer(const GBTDataTrailer* gbtT) LOG(INFO) << "Trailer: Done=" << gbtT->packetDone << " Lanes TO: " << LT << " | Lanes ST: " << LS; } +///_________________________________________________________________ +void GBTLink::printDiagnostic(const GBTDiagnostic* gbtD) +{ + gbtD->printX(); + LOG(INFO) << "Diagnostic word"; +} + +///_________________________________________________________________ +void GBTLink::printCableDiagnostic(const GBTCableDiagnostic* gbtD) +{ + gbtD->printX(); + LOGF(INFO, "Diagnostic for %s Lane %d | errorID: %d data 0x%016lx", gbtD->isIB() ? "IB" : "OB", gbtD->getCableID(), gbtD->laneErrorID, gbtD->diagnosticData); +} + +///_________________________________________________________________ +void GBTLink::printCableStatus(const GBTCableStatus* gbtS) +{ + gbtS->printX(); + LOGF(INFO, "Status data, not processed at the moment"); +} + ///==================================================================== #ifdef _RAW_READER_ERROR_CHECKS_ @@ -126,32 +134,35 @@ void GBTLink::printTrailer(const GBTDataTrailer* gbtT) /// Check RDH correctness GBTLink::ErrorType GBTLink::checkErrorsRDH(const RDH& rdh) { + ErrorType err = NoError; if (!RDHUtils::checkRDH(rdh, true)) { - statistics.errorCounts[GBTS::ErrNoRDHAtStart]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrNoRDHAtStart]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrNoRDHAtStart]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrNoRDHAtStart]; } - errorBits |= 0x1 << int(GBTS::ErrNoRDHAtStart); - return Abort; // fatal error + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrNoRDHAtStart); + err = Abort; + return err; // fatal error } if (format == OldFormat && RDHUtils::getVersion(rdh) > 4) { if (verbosity >= VerboseErrors) { LOG(ERROR) << "Requested old format requires data with RDH version 3 or 4, RDH version " << RDHUtils::getVersion(rdh) << " is found"; } - return Abort; + err = Abort; + return err; } if ((RDHUtils::getPacketCounter(rdh) > packetCounter + 1) && packetCounter >= 0) { - statistics.errorCounts[GBTS::ErrPacketCounterJump]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrPacketCounterJump]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrPacketCounterJump] + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrPacketCounterJump] << " : jump from " << int(packetCounter) << " to " << int(RDHUtils::getPacketCounter(rdh)); } - errorBits |= 0x1 << int(GBTS::ErrPacketCounterJump); - return Warning; + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrPacketCounterJump); + err = Warning; } packetCounter = RDHUtils::getPacketCounter(rdh); - return NoError; + return err; } ///_________________________________________________________________ @@ -160,13 +171,13 @@ GBTLink::ErrorType GBTLink::checkErrorsRDHStop(const RDH& rdh) { if (format == NewFormat && lastRDH && RDHUtils::getHeartBeatOrbit(*lastRDH) != RDHUtils::getHeartBeatOrbit(rdh) // new HB starts && !RDHUtils::getStop(*lastRDH)) { - statistics.errorCounts[GBTS::ErrPageNotStopped]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrPageNotStopped]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrPageNotStopped]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrPageNotStopped]; RDHUtils::printRDH(*lastRDH); RDHUtils::printRDH(rdh); } - errorBits |= 0x1 << int(GBTS::ErrPageNotStopped); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrPageNotStopped); return Warning; } return NoError; @@ -176,13 +187,13 @@ GBTLink::ErrorType GBTLink::checkErrorsRDHStop(const RDH& rdh) /// Check if the RDH Stop page is empty GBTLink::ErrorType GBTLink::checkErrorsRDHStopPageEmpty(const RDH& rdh) { - if (format == NewFormat && RDHUtils::getStop(rdh) && RDHUtils::getMemorySize(rdh) != sizeof(RDH)) { - statistics.errorCounts[GBTS::ErrStopPageNotEmpty]++; + if (format == NewFormat && RDHUtils::getStop(rdh) && RDHUtils::getMemorySize(rdh) != sizeof(RDH) + sizeof(GBTDiagnostic)) { + statistics.errorCounts[GBTLinkDecodingStat::ErrStopPageNotEmpty]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrStopPageNotEmpty]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrStopPageNotEmpty]; RDHUtils::printRDH(rdh); } - errorBits |= 0x1 << int(GBTS::ErrStopPageNotEmpty); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrStopPageNotEmpty); return Warning; } return NoError; @@ -194,50 +205,74 @@ GBTLink::ErrorType GBTLink::checkErrorsTriggerWord(const GBTTrigger* gbtTrg) { if (!gbtTrg->isTriggerWord()) { // check trigger word gbtTrg->printX(); - statistics.errorCounts[GBTS::ErrMissingGBTTrigger]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrMissingGBTTrigger]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrMissingGBTTrigger]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrMissingGBTTrigger]; } - errorBits |= 0x1 << int(GBTS::ErrMissingGBTTrigger); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrMissingGBTTrigger); return Abort; } return NoError; } +///_________________________________________________________________ +/// Check the GBT Calibration word correctness +GBTLink::ErrorType GBTLink::checkErrorsCalibrationWord(const GBTCalibration* gbtCal) +{ + // at the moment do nothing + return NoError; +} + ///_________________________________________________________________ /// Check the GBT Header word correctness GBTLink::ErrorType GBTLink::checkErrorsHeaderWord(const GBTDataHeader* gbtH) { if (!gbtH->isDataHeader()) { // check header word - statistics.errorCounts[GBTS::ErrMissingGBTHeader]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrMissingGBTHeader]++; gbtH->printX(); if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrMissingGBTHeader]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrMissingGBTHeader]; } - errorBits |= 0x1 << int(GBTS::ErrMissingGBTHeader); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrMissingGBTHeader); + return Abort; + } + return NoError; +} + +///_________________________________________________________________ +/// Check the GBT Header word correctness +GBTLink::ErrorType GBTLink::checkErrorsHeaderWord(const GBTDataHeaderL* gbtH) +{ + if (!gbtH->isDataHeader()) { // check header word + statistics.errorCounts[GBTLinkDecodingStat::ErrMissingGBTHeader]++; + gbtH->printX(); + if (verbosity >= VerboseErrors) { + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrMissingGBTHeader]; + } + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrMissingGBTHeader); return Abort; } int cnt = RDHUtils::getPageCounter(*lastRDH); // RSTODO: this makes sense only for old format, where every trigger has its RDH - if (format == OldFormat && gbtH->packetIdx != cnt) { - statistics.errorCounts[GBTS::ErrRDHvsGBTHPageCnt]++; + if (gbtH->packetIdx != cnt) { + statistics.errorCounts[GBTLinkDecodingStat::ErrRDHvsGBTHPageCnt]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrRDHvsGBTHPageCnt] << ": diff in GBT header " + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrRDHvsGBTHPageCnt] << ": diff in GBT header " << gbtH->packetIdx << " and RDH page " << cnt << " counters"; } - errorBits |= 0x1<<int(GBTS::ErrRDHvsGBTHPageCnt); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrRDHvsGBTHPageCnt); return Warning; } // RSTODO CHECK if (lanesActive == lanesStop) { // all lanes received their stop, new page 0 expected //if (cnt) { // makes sens for old format only if (gbtH->packetIdx) { - statistics.errorCounts[GBTS::ErrNonZeroPageAfterStop]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrNonZeroPageAfterStop]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrNonZeroPageAfterStop] + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrNonZeroPageAfterStop] << ": Non-0 page counter (" << cnt << ") while all lanes were stopped"; } - errorBits |= 0x1 << int(GBTS::ErrNonZeroPageAfterStop); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrNonZeroPageAfterStop); return Warning; } } @@ -249,13 +284,13 @@ GBTLink::ErrorType GBTLink::checkErrorsHeaderWord(const GBTDataHeader* gbtH) GBTLink::ErrorType GBTLink::checkErrorsActiveLanes(int cbl) { if (~cbl & lanesActive) { // are there wrong lanes? - statistics.errorCounts[GBTS::ErrInvalidActiveLanes]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrInvalidActiveLanes]++; std::bitset<32> expectL(cbl), gotL(lanesActive); if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrInvalidActiveLanes] + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrInvalidActiveLanes] << ' ' << gotL << " vs " << expectL << " skip page"; } - errorBits |= 0x1 << int(GBTS::ErrInvalidActiveLanes); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrInvalidActiveLanes); return Warning; } return NoError; @@ -267,29 +302,47 @@ GBTLink::ErrorType GBTLink::checkErrorsGBTData(int cablePos) { lanesWithData |= 0x1 << cablePos; // flag that the data was seen on this lane if (lanesStop & (0x1 << cablePos)) { // make sure stopped lanes do not transmit the data - statistics.errorCounts[GBTS::ErrDataForStoppedLane]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrDataForStoppedLane]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrDataForStoppedLane] + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrDataForStoppedLane] << cablePos; } - errorBits |= 0x1 << int(GBTS::ErrDataForStoppedLane); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrDataForStoppedLane); return Warning; } return NoError; } +///_________________________________________________________________ +/// Check GBT Data word ID: it might be diagnostic or status data +GBTLink::ErrorType GBTLink::checkErrorsGBTDataID(const GBTData* gbtD) +{ + if (gbtD->isData()) { + return NoError; + } + if (gbtD->isCableDiagnostic()) { + printCableDiagnostic((GBTCableDiagnostic*)gbtD); + } else if (gbtD->isStatus()) { + printCableStatus((GBTCableStatus*)gbtD); + } + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrGBTWordNotRecognized]; + gbtD->printX(true); + statistics.errorCounts[GBTLinkDecodingStat::ErrGBTWordNotRecognized]++; + return Skip; +} + ///_________________________________________________________________ /// Check the GBT Trailer word correctness GBTLink::ErrorType GBTLink::checkErrorsTrailerWord(const GBTDataTrailer* gbtT) { if (!gbtT->isDataTrailer()) { gbtT->printX(); - statistics.errorCounts[GBTS::ErrMissingGBTTrailer]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrMissingGBTTrailer]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrMissingGBTTrailer]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrMissingGBTTrailer]; } - errorBits |= 0x1 << int(GBTS::ErrMissingGBTTrailer); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrMissingGBTTrailer); return Abort; } lanesTimeOut |= gbtT->lanesTimeout; // register timeouts @@ -302,11 +355,11 @@ GBTLink::ErrorType GBTLink::checkErrorsTrailerWord(const GBTDataTrailer* gbtT) GBTLink::ErrorType GBTLink::checkErrorsPacketDoneMissing(const GBTDataTrailer* gbtT, bool notEnd) { if (!gbtT->packetDone && notEnd) { // Done may be missing only in case of carry-over to new CRU page - statistics.errorCounts[GBTS::ErrPacketDoneMissing]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrPacketDoneMissing]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrPacketDoneMissing]; + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrPacketDoneMissing]; } - errorBits |= 0x1 << int(GBTS::ErrPacketDoneMissing); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrPacketDoneMissing); return Warning; } return NoError; @@ -320,28 +373,44 @@ GBTLink::ErrorType GBTLink::checkErrorsLanesStops() auto err = NoError; if ((lanesActive & ~lanesStop)) { if (RDHUtils::getTriggerType(*lastRDH) != o2::trigger::SOT) { // only SOT trigger allows unstopped lanes? - statistics.errorCounts[GBTS::ErrUnstoppedLanes]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrUnstoppedLanes]++; std::bitset<32> active(lanesActive), stopped(lanesStop); if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrUnstoppedLanes] + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrUnstoppedLanes] << " | active: " << active << " stopped: " << stopped; } - errorBits |= 0x1 << int(GBTS::ErrUnstoppedLanes); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrUnstoppedLanes); } err = Warning; } // make sure all active lanes (except those in time-out) have sent some data if ((~lanesWithData & lanesActive) != lanesTimeOut) { std::bitset<32> withData(lanesWithData), active(lanesActive), timeOut(lanesTimeOut); - statistics.errorCounts[GBTS::ErrNoDataForActiveLane]++; + statistics.errorCounts[GBTLinkDecodingStat::ErrNoDataForActiveLane]++; if (verbosity >= VerboseErrors) { - LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTS::ErrNoDataForActiveLane] + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrNoDataForActiveLane] << " | with data: " << withData << " active: " << active << " timeOut: " << timeOut; } - errorBits |= 0x1 << int(GBTS::ErrNoDataForActiveLane); + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrNoDataForActiveLane); err = Warning; } return err; } +///_________________________________________________________________ +/// Check diagnostic word +GBTLink::ErrorType GBTLink::checkErrorsDiagnosticWord(const GBTDiagnostic* gbtD) +{ + if (RDHUtils::getMemorySize(lastRDH) != sizeof(RDH) + sizeof(GBTDiagnostic) || !gbtD->isDiagnosticWord()) { // + statistics.errorCounts[GBTLinkDecodingStat::ErrMissingDiagnosticWord]++; + gbtD->printX(); + if (verbosity >= VerboseErrors) { + LOG(ERROR) << describe() << ' ' << statistics.ErrNames[GBTLinkDecodingStat::ErrMissingDiagnosticWord]; + } + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrMissingDiagnosticWord); + return Abort; + } + return NoError; +} + #endif diff --git a/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h b/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h index 19d2a85352fd2..168ba17660404 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h +++ b/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h @@ -33,7 +33,13 @@ #pragma link C++ class o2::itsmft::AlpideCoder + ; #pragma link C++ class o2::itsmft::GBTWord + ; #pragma link C++ class o2::itsmft::GBTDataHeader + ; +#pragma link C++ class o2::itsmft::GBTDataHeaderL + ; #pragma link C++ class o2::itsmft::GBTDataTrailer + ; +#pragma link C++ class o2::itsmft::GBTTrigger + ; +#pragma link C++ class o2::itsmft::GBTDiagnostic + ; +#pragma link C++ class o2::itsmft::GBTCableDiagnostic + ; +#pragma link C++ class o2::itsmft::GBTCableStatus + ; +#pragma link C++ class o2::itsmft::GBTCalibration + ; #pragma link C++ class o2::itsmft::GBTData + ; #pragma link C++ class o2::itsmft::PayLoadCont + ; #pragma link C++ class o2::itsmft::PayLoadSG + ; @@ -41,6 +47,7 @@ #pragma link C++ class o2::itsmft::GBTLink + ; #pragma link C++ class o2::itsmft::RUDecodeData + ; #pragma link C++ class o2::itsmft::RawDecodingStat + ; +#pragma link C++ class o2::itsmft::ChipStat + ; #pragma link C++ class std::map < unsigned long, std::pair < o2::itsmft::ClusterTopology, unsigned long>> + ; diff --git a/Detectors/ITSMFT/common/reconstruction/src/LookUp.cxx b/Detectors/ITSMFT/common/reconstruction/src/LookUp.cxx index 2aaf9cda3162b..ee7f6be12ee32 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/LookUp.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/LookUp.cxx @@ -47,11 +47,13 @@ int LookUp::groupFinder(int nRow, int nCol) if (nCol % TopologyDictionary::RowClassSpan == 0) { col_index--; } - if (row_index > TopologyDictionary::MaxNumberOfClasses || col_index > TopologyDictionary::MaxNumberOfClasses) { - return TopologyDictionary::NumberOfRareGroups - 1; + int grNum = -1; + if (row_index > TopologyDictionary::MaxNumberOfRowClasses || col_index > TopologyDictionary::MaxNumberOfColClasses) { + grNum = TopologyDictionary::NumberOfRareGroups - 1; } else { - return row_index * TopologyDictionary::MaxNumberOfClasses + col_index; + grNum = row_index * TopologyDictionary::MaxNumberOfColClasses + col_index; } + return grNum; } int LookUp::findGroupID(int nRow, int nCol, const unsigned char patt[ClusterPattern::MaxPatternBytes]) @@ -60,9 +62,9 @@ int LookUp::findGroupID(int nRow, int nCol, const unsigned char patt[ClusterPatt // Small topology if (nBits < 9) { int ID = mDictionary.mSmallTopologiesLUT[(nCol - 1) * 255 + (int)patt[0]]; - if (ID >= 0) + if (ID >= 0) { return ID; - else { //small rare topology (inside groups) + } else { //small rare topology (inside groups) int index = groupFinder(nRow, nCol); return mDictionary.mGroupMap[index]; } @@ -70,9 +72,9 @@ int LookUp::findGroupID(int nRow, int nCol, const unsigned char patt[ClusterPatt // Big topology unsigned long hash = ClusterTopology::getCompleteHash(nRow, nCol, patt); auto ret = mDictionary.mCommonMap.find(hash); - if (ret != mDictionary.mCommonMap.end()) + if (ret != mDictionary.mCommonMap.end()) { return ret->second; - else { // Big rare topology (inside groups) + } else { // Big rare topology (inside groups) int index = groupFinder(nRow, nCol); return mDictionary.mGroupMap[index]; } diff --git a/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx b/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx index 4006892dfaaa0..3641991899487 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx @@ -47,5 +47,13 @@ void RUDecodeData::setROFInfo(ChipPixelData* chipData, const GBTLink* lnk) chipData->setInteractionRecord(lnk->ir); } +///_________________________________________________________________ +/// fill chip decoding statistics +void RUDecodeData::fillChipStatistics(int icab, const ChipPixelData* chipData) +{ + cableLinkPtr[icab]->chipStat.nHits += chipData->getData().size(); + cableLinkPtr[icab]->chipStat.addErrors(chipData->getErrorFlags()); +} + } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx index 1ecb790a1db81..24710c25b7209 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx @@ -39,7 +39,7 @@ RawPixelDecoder<Mapping>::RawPixelDecoder() ///______________________________________________________________ /// template <class Mapping> -void RawPixelDecoder<Mapping>::printReport() const +void RawPixelDecoder<Mapping>::printReport(bool decstat, bool skipEmpty) const { LOGF(INFO, "%s Decoded %zu hits in %zu non-empty chips in %u ROFs with %d threads", mSelfName, mNPixelsFired, mNChipsFired, mROFCounter, mNThreads); double cpu = 0, real = 0; @@ -57,6 +57,15 @@ void RawPixelDecoder<Mapping>::printReport() const real += tmrF.RealTime(); LOGF(INFO, "%s Timing Total: CPU = %.3e Real = %.3e in %d slots in %s mode", mSelfName, cpu, real, tmrS.Counter() - 1, mDecodeNextAuto ? "AutoDecode" : "ExternalCall"); + + if (decstat) { + LOG(INFO) << "GBT Links decoding statistics"; + for (auto& lnk : mGBTLinks) { + LOG(INFO) << lnk.describe(); + lnk.statistics.print(skipEmpty); + lnk.chipStat.print(skipEmpty); + } + } } ///______________________________________________________________ @@ -69,37 +78,41 @@ int RawPixelDecoder<Mapping>::decodeNextTrigger() mNPixelsFiredROF = 0; mInteractionRecord.clear(); int nLinksWithData = 0, nru = mRUDecodeVec.size(); + do { #ifdef WITH_OPENMP - omp_set_num_threads(mNThreads); + omp_set_num_threads(mNThreads); #pragma omp parallel for schedule(dynamic) reduction(+ \ : nLinksWithData, mNChipsFiredROF, mNPixelsFiredROF) #endif - for (int iru = 0; iru < nru; iru++) { - nLinksWithData += decodeNextTrigger(iru); - mNChipsFiredROF += mRUDecodeVec[iru].nChipsFired; - int npix = 0; - for (int ic = mRUDecodeVec[iru].nChipsFired; ic--;) { - npix += mRUDecodeVec[iru].chipsData[ic].getData().size(); + for (int iru = 0; iru < nru; iru++) { + nLinksWithData += decodeNextTrigger(iru); + mNChipsFiredROF += mRUDecodeVec[iru].nChipsFired; + int npix = 0; + for (int ic = mRUDecodeVec[iru].nChipsFired; ic--;) { + npix += mRUDecodeVec[iru].chipsData[ic].getData().size(); + } + mNPixelsFiredROF += npix; } - mNPixelsFiredROF += npix; - } - if (nLinksWithData) { // fill some statistics - mROFCounter++; - mNChipsFired += mNChipsFiredROF; - mNPixelsFired += mNPixelsFiredROF; - mCurRUDecodeID = 0; // getNextChipData will start from here - mLastReadChipID = -1; - // set IR and trigger from the 1st non empty link - for (const auto& link : mGBTLinks) { - if (link.status == GBTLink::DataSeen) { - mInteractionRecord = link.ir; - mInteractionRecordHB = o2::raw::RDHUtils::getHeartBeatIR(*link.lastRDH); - mTrigger = link.trigger; - break; + if (nLinksWithData) { // fill some statistics + mROFCounter++; + mNChipsFired += mNChipsFiredROF; + mNPixelsFired += mNPixelsFiredROF; + mCurRUDecodeID = 0; // getNextChipData will start from here + mLastReadChipID = -1; + // set IR and trigger from the 1st non empty link + for (const auto& link : mGBTLinks) { + if (link.status == GBTLink::DataSeen) { + mInteractionRecord = link.ir; + mInteractionRecordHB = o2::raw::RDHUtils::getHeartBeatIR(*link.lastRDH); + mTrigger = link.trigger; + break; + } } + break; } - } + + } while (mNLinksDone < mGBTLinks.size()); mTimerDecode.Stop(); // LOG(INFO) << "Chips Fired: " << mNChipsFiredROF << " NPixels: " << mNPixelsFiredROF << " at IR " << mInteractionRecord << " of HBF " << mInteractionRecordHB; return nLinksWithData; @@ -119,6 +132,7 @@ void RawPixelDecoder<Mapping>::startNewTF(InputRecord& inputs) ru.clear(); } setupLinks(inputs); + mNLinksDone = 0; mTimerTFStart.Stop(); } @@ -136,6 +150,8 @@ int RawPixelDecoder<Mapping>::decodeNextTrigger(int iru) auto res = link->collectROFCableData(mMAP); if (res == GBTLink::DataSeen) { // at the moment process only DataSeen ndec++; + } else if (res == GBTLink::StoppedOnEndOfData || res == GBTLink::AbortedOnError) { // this link has exhausted its data or it has to be discarded due to the error + mNLinksDone++; } } } @@ -152,7 +168,9 @@ void RawPixelDecoder<Mapping>::setupLinks(InputRecord& inputs) { mCurRUDecodeID = NORUDECODED; auto nLinks = mGBTLinks.size(); - std::vector<InputSpec> filter{InputSpec{"filter", ConcreteDataTypeMatcher{mMAP.getOrigin(), "RAWDATA"}, Lifetime::Timeframe}}; + auto origin = (mUserDataOrigin == o2::header::gDataOriginInvalid) ? mMAP.getOrigin() : mUserDataOrigin; + auto datadesc = (mUserDataDescription == o2::header::gDataDescriptionInvalid) ? o2::header::gDataDescriptionRawData : mUserDataDescription; + std::vector<InputSpec> filter{InputSpec{"filter", ConcreteDataTypeMatcher{origin, datadesc}, Lifetime::Timeframe}}; DPLRawParser parser(inputs, filter); uint32_t currSSpec = 0xffffffff; // dummy starting subspec int linksAdded = 0; @@ -299,5 +317,15 @@ void RawPixelDecoder<Mapping>::setFormat(GBTLink::Format f) mFormat = f; } +///______________________________________________________________________ +template <class Mapping> +void RawPixelDecoder<Mapping>::clearStat() +{ + // clear statistics + for (auto& lnk : mGBTLinks) { + lnk.clear(true, false); + } +} + template class o2::itsmft::RawPixelDecoder<o2::itsmft::ChipMappingITS>; template class o2::itsmft::RawPixelDecoder<o2::itsmft::ChipMappingMFT>; diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/ClusterShape.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/ClusterShape.h index 05480b06afc12..3d6b85a459fcc 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/ClusterShape.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/ClusterShape.h @@ -109,8 +109,9 @@ class ClusterShape : public TObject std::stringstream out; for (UInt_t i = 0; i < shape.size(); ++i) { out << shape[i]; - if (i < shape.size() - 1) + if (i < shape.size() - 1) { out << " "; + } } return out.str(); } @@ -121,21 +122,26 @@ class ClusterShape : public TObject for (Int_t r = -1; r < (Int_t)v.mNrows; ++r) { for (UInt_t c = 0; c < v.mNcols; ++c) { if (r == -1) { - if (c == 0) + if (c == 0) { out << " "; + } out << c; - if (c < v.mNcols - 1) + if (c < v.mNcols - 1) { out << " "; + } } else { - if (c == 0) + if (c == 0) { out << r << " "; + } index = r * v.mNcols + c; - if (std::find(begin(v.mShape), end(v.mShape), index) != end(v.mShape)) + if (std::find(begin(v.mShape), end(v.mShape), index) != end(v.mShape)) { out << "X"; - else + } else { out << " "; - if (c < v.mNcols - 1) + } + if (c < v.mNcols - 1) { out << " "; + } } } out << std::endl; diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Hit.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Hit.h index bc28d5ed5a54c..5ee373f84a9c1 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Hit.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Hit.h @@ -56,7 +56,7 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> double endTime, double eLoss, unsigned char statusStart, unsigned char status); // Entrance position getters - Point3D<Float_t> GetPosStart() const { return mPosStart; } + math_utils::Point3D<Float_t> 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(); } @@ -68,8 +68,8 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> z = GetStartZ(); } // momentum getters - Vector3D<Float_t> GetMomentum() const { return mMomentum; } - Vector3D<Float_t>& GetMomentum() { return mMomentum; } + math_utils::Vector3D<Float_t> GetMomentum() const { return mMomentum; } + math_utils::Vector3D<Float_t>& GetMomentum() { return mMomentum; } Float_t GetPx() const { return mMomentum.X(); } Float_t GetPy() const { return mMomentum.Y(); } Float_t GetPz() const { return mMomentum.Z(); } @@ -108,11 +108,11 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> } private: - Vector3D<Float_t> mMomentum; ///< momentum at entrance - Point3D<Float_t> mPosStart; ///< position at entrance (base mPos give position on exit) - Float_t mE; ///< total energy at entrance - UChar_t mTrackStatusEnd; ///< MC status flag at exit - UChar_t mTrackStatusStart; ///< MC status at starting point + math_utils::Vector3D<Float_t> mMomentum; ///< momentum at entrance + math_utils::Point3D<Float_t> mPosStart; ///< position at entrance (base mPos give position on exit) + Float_t mE; ///< total energy at entrance + UChar_t mTrackStatusEnd; ///< MC status flag at exit + UChar_t mTrackStatusStart; ///< MC status at starting point ClassDefNV(Hit, 3); }; diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/MC2RawEncoder.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/MC2RawEncoder.h index a62e482b58066..d91b677989c79 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/MC2RawEncoder.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/MC2RawEncoder.h @@ -22,6 +22,7 @@ #include "ITSMFTReconstruction/RUDecodeData.h" #include "DetectorsRaw/RawFileWriter.h" #include "DetectorsRaw/RDHUtils.h" +#include <unordered_map> namespace o2 { @@ -87,6 +88,8 @@ class MC2RawEncoder int carryOverMethod(const o2::header::RDHAny* rdh, const gsl::span<char> data, const char* ptr, int maxSize, int splitID, std::vector<char>& trailer, std::vector<char>& header) const; + void newRDHMethod(const header::RDHAny* rdh, bool empty, std::vector<char>& toAdd) const; + // create new gbt link int addGBTLink() { @@ -120,7 +123,8 @@ class MC2RawEncoder std::array<RUDecodeData, Mapping::getNRUs()> mRUDecodeVec; /// decoding buffers for all active RUs std::array<int, Mapping::getNRUs()> mRUEntry; /// entry of the RU with given SW ID in the mRUDecodeVec std::vector<GBTLink> mGBTLinks; - + std::unordered_map<uint16_t, const GBTLink*> mFEEId2Link; + std::unordered_map<uint16_t, GBTDataHeader> mFEEId2GBTHeader; ClassDefNV(MC2RawEncoder, 1); }; diff --git a/Detectors/ITSMFT/common/simulation/src/AlpideChip.cxx b/Detectors/ITSMFT/common/simulation/src/AlpideChip.cxx index bd623967fc7d9..6b643b7d0a01f 100644 --- a/Detectors/ITSMFT/common/simulation/src/AlpideChip.cxx +++ b/Detectors/ITSMFT/common/simulation/src/AlpideChip.cxx @@ -94,10 +94,11 @@ TGeoVolume* AlpideChip::createChip(const Double_t ychip, TGeoMedium* medChip; - if (dummy) + if (dummy) { medChip = medAir; - else + } else { medChip = medSi; + } TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medChip); chipVol->SetVisibility(kTRUE); diff --git a/Detectors/ITSMFT/common/simulation/src/AlpideSimResponse.cxx b/Detectors/ITSMFT/common/simulation/src/AlpideSimResponse.cxx index af4db658eeab4..4557bc97b1ebf 100644 --- a/Detectors/ITSMFT/common/simulation/src/AlpideSimResponse.cxx +++ b/Detectors/ITSMFT/common/simulation/src/AlpideSimResponse.cxx @@ -73,8 +73,9 @@ void AlpideSimResponse::initData() LOG(FATAL) << "Failed to open file " << inpfname; } - while (inpGrid >> mStepInvRow && inpGrid.good()) + while (inpGrid >> mStepInvRow && inpGrid.good()) { mNBinRow++; + } if (!mNBinRow || mStepInvRow < kTiny) { LOG(FATAL) << "Failed to read Y(row) binning from " << inpfname; } @@ -130,15 +131,18 @@ void AlpideSimResponse::initData() << iz << " from " << inpfname; } - if (mDptMax < -1e9) + if (mDptMax < -1e9) { mDptMax = gz; - if (mDptMin > gz) + } + if (mDptMin > gz) { mDptMin = gz; + } // normalize float norm = 1.f / nele; - for (int ip = npix * npix; ip--;) + for (int ip = npix * npix; ip--;) { (*arr)[ip] *= norm; + } mData.push_back(mat); // store in the final container } // loop over z @@ -206,20 +210,23 @@ bool AlpideSimResponse::getResponse(float vRow, float vCol, float vDepth, Alpide LOG(FATAL) << "response object is not initialized"; } bool flipCol = false, flipRow = true; - if (vDepth < mDptMin || vDepth > mDptMax) + if (vDepth < mDptMin || vDepth > mDptMax) { return false; + } if (vCol < 0) { vCol = -vCol; flipCol = true; } - if (vCol > mColMax) + if (vCol > mColMax) { return false; + } if (vRow < 0) { vRow = -vRow; flipRow = false; } - if (vRow > mRowMax) + if (vRow > mRowMax) { return false; + } size_t bin = getDepthBin(vDepth) + mNBinDpt * (getRowBin(vRow) + mNBinRow * getColBin(vCol)); if (bin >= mData.size()) { @@ -245,24 +252,27 @@ const AlpideRespSimMat* AlpideSimResponse::getResponse(float vRow, float vCol, f if (!mNBinDpt) { LOG(FATAL) << "response object is not initialized"; } - if (vDepth < mDptMin || vDepth > mDptMax) + if (vDepth < mDptMin || vDepth > mDptMax) { return nullptr; + } if (vCol < 0) { vCol = -vCol; flipCol = true; } else { flipCol = false; } - if (vCol > mColMax) + if (vCol > mColMax) { return nullptr; + } if (vRow < 0) { vRow = -vRow; flipRow = false; } else { flipRow = true; } - if (vRow > mRowMax) + if (vRow > mRowMax) { return nullptr; + } size_t bin = getDepthBin(vDepth) + mNBinDpt * (getRowBin(vRow) + mNBinRow * getColBin(vCol)); if (bin >= mData.size()) { diff --git a/Detectors/ITSMFT/common/simulation/src/ClusterShape.cxx b/Detectors/ITSMFT/common/simulation/src/ClusterShape.cxx index 4b4e3b6ab1dcf..0a0b2f0cdde37 100644 --- a/Detectors/ITSMFT/common/simulation/src/ClusterShape.cxx +++ b/Detectors/ITSMFT/common/simulation/src/ClusterShape.cxx @@ -56,16 +56,19 @@ ClusterShape::~ClusterShape() = default; Bool_t ClusterShape::IsValidShape() { // Check the size - if (mShape.size() > mNrows * mNcols) + if (mShape.size() > mNrows * mNcols) { return false; + } // Check for duplicates and the validity of the position std::sort(mShape.begin(), mShape.end()); for (size_t i = 0; i < mShape.size() - 1; i++) { - if (mShape[i] >= mNrows * mNcols || mShape[i + 1] >= mNrows * mNcols) + if (mShape[i] >= mNrows * mNcols || mShape[i + 1] >= mNrows * mNcols) { return false; - if (mShape[i] == mShape[i + 1]) + } + if (mShape[i] == mShape[i + 1]) { return false; + } } return true; @@ -88,10 +91,12 @@ Long64_t ClusterShape::GetShapeID() const Bool_t ClusterShape::HasElement(UInt_t value) const { for (auto& el : mShape) { - if (el > value) + if (el > value) { break; - if (el == value) + } + if (el == value) { return true; + } } return false; } diff --git a/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx b/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx index 88e9792706c1e..86023fff83424 100644 --- a/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx +++ b/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx @@ -22,7 +22,6 @@ using namespace o2::itsmft; DigiParams::DigiParams() { // make sure the defaults are consistent - setROFrameLength(mROFrameLength); setNSimSteps(mNSimSteps); } diff --git a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx index 8bb45bdb97922..d18d55d1476b7 100644 --- a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx +++ b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx @@ -14,7 +14,7 @@ #include "DataFormatsITSMFT/Digit.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSMFTSimulation/Digitizer.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "SimulationDataFormat/MCTruthContainer.h" #include <TRandom.h> @@ -214,14 +214,14 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, UInt_t& maxFr, int evID, float nStepsInv = mParams.getNSimStepsInv(); int nSteps = mParams.getNSimSteps(); const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID()); - Vector3D<float> xyzLocS(matrix ^ (hit.GetPosStart())); // start position in sensor frame - Vector3D<float> xyzLocE(matrix ^ (hit.GetPos())); // end position in sensor frame + math_utils::Vector3D<float> xyzLocS(matrix ^ (hit.GetPosStart())); // start position in sensor frame + math_utils::Vector3D<float> xyzLocE(matrix ^ (hit.GetPos())); // end position in sensor frame - Vector3D<float> step(xyzLocE); + math_utils::Vector3D<float> step(xyzLocE); step -= xyzLocS; step *= nStepsInv; // position increment at each step // the electrons will injected in the middle of each step - Vector3D<float> stepH(step * 0.5); + math_utils::Vector3D<float> stepH(step * 0.5); xyzLocS += stepH; xyzLocE -= stepH; @@ -361,10 +361,12 @@ void Digitizer::registerDigits(ChipDigitsContainer& chip, UInt_t roFrame, float if (nEleROF < mParams.getMinChargeToAccount()) { continue; } - if (roFr > mEventROFrameMax) + if (roFr > mEventROFrameMax) { mEventROFrameMax = roFr; - if (roFr < mEventROFrameMin) + } + if (roFr < mEventROFrameMin) { mEventROFrameMin = roFr; + } auto key = chip.getOrderingKey(roFr, row, col); PreDigit* pd = chip.findDigit(key); if (!pd) { diff --git a/Detectors/ITSMFT/common/simulation/src/MC2RawEncoder.cxx b/Detectors/ITSMFT/common/simulation/src/MC2RawEncoder.cxx index b85c57166f54c..d7e97d515ca39 100644 --- a/Detectors/ITSMFT/common/simulation/src/MC2RawEncoder.cxx +++ b/Detectors/ITSMFT/common/simulation/src/MC2RawEncoder.cxx @@ -23,6 +23,7 @@ void MC2RawEncoder<Mapping>::init() { assert(mWriter.isReadOutModeSet()); mWriter.setCarryOverCallBack(this); + mWriter.setNewRDHCallBack(this); // limit RUs to convert to existing ones mRUSWMax = (mRUSWMax < uint8_t(mMAP.getNRUs())) ? mRUSWMax : mMAP.getNRUs() - 1; @@ -34,7 +35,7 @@ void MC2RawEncoder<Mapping>::init() for (int il = 0; il < RUDecodeData::MaxLinksPerRU; il++) { auto* link = getGBTLink(ruData.links[il]); if (link) { - auto subspec = o2::raw::RDHUtils::getSubSpec(link->cruID, link->idInCRU, link->endPointID, link->feeID); + auto subspec = RDHUtils::getSubSpec(link->cruID, link->idInCRU, link->endPointID, link->feeID); if (!mWriter.isLinkRegistered(subspec)) { LOGF(INFO, "RU%3d FEEId 0x%04x Link %02d of CRU=0x%94x will be writing to default sink %s", int(ru), link->feeID, link->idInCRU, link->cruID, mDefaultSinkName); @@ -64,8 +65,13 @@ void MC2RawEncoder<Mapping>::init() mNLinks++; } } - assert(mNLinks > 0); + // create mapping from feeID to links for fast access + for (int i = 0; i < mNLinks; i++) { + const auto* lnk = getGBTLink(i); + mFEEId2Link[lnk->feeID] = lnk; + mFEEId2GBTHeader[lnk->feeID].activeLanes = lnk->lanes; + } } ///______________________________________________________________________ @@ -174,27 +180,23 @@ void MC2RawEncoder<Mapping>::fillGBTLinks(RUDecodeData& ru) // estimate real payload size in GBT words int nPayLoadWordsNeeded = 0; // number of payload words filled to link buffer (RDH not included) for current IR for (int icab = ru.nCables; icab--;) { // calculate number of GBT words per link - if ((link->lanes & (0x1 << icab))) { - int nb = ru.cableData[icab].getSize(); + if ((link->lanes & (0x1 << mMAP.cablePos(ru.ruInfo->ruType, icab)))) { + int nb = ru.cableData[mMAP.cablePos(ru.ruInfo->ruType, icab)].getSize(); nPayLoadWordsNeeded += nb ? 1 + (nb - 1) / 9 : 0; // single GBT word carries at most 9 payload bytes } } // reserve space for payload + trigger + header + trailer link->data.ensureFreeCapacity((3 + nPayLoadWordsNeeded) * GBTPaddedWordLength); - link->data.addFast(gbtTrigger.getW8(), GBTPaddedWordLength); // write GBT trigger in the beginning of the buffer - GBTDataHeader gbtHeader; - gbtHeader.packetIdx = 0; - gbtHeader.activeLanes = link->lanes; - link->data.addFast(gbtHeader.getW8(), GBTPaddedWordLength); // write GBT header + link->data.addFast(gbtTrigger.getW8(), GBTPaddedWordLength); // write GBT trigger // now loop over the lanes served by this link, writing each time at most 9 bytes, untill all lanes are copied bool hasData = true; while (hasData) { hasData = false; for (int icab = 0; icab < ru.nCables; icab++) { - if ((link->lanes & (0x1 << icab))) { - auto& cableData = ru.cableData[icab]; + if ((link->lanes & (0x1 << mMAP.cablePos(ru.ruInfo->ruType, icab)))) { + auto& cableData = ru.cableData[mMAP.cablePos(ru.ruInfo->ruType, icab)]; int nb = cableData.getUnusedSize(); if (!nb) { continue; // write 80b word only if there is something to write @@ -202,10 +204,10 @@ void MC2RawEncoder<Mapping>::fillGBTLinks(RUDecodeData& ru) if (nb > 9) { nb = 9; } - int gbtWordStart = link->data.getSize(); // beginning of the current GBT word in the link - link->data.addFast(cableData.getPtr(), nb); // fill payload of cable - link->data.addFast(zero16, GBTPaddedWordLength - nb); // fill the rest of the GBT word by 0 - link->data[gbtWordStart + 9] = mMAP.getGBTHeaderRUType(ru.ruInfo->ruType, ru.cableHWID[icab]); // set cable flag + int gbtWordStart = link->data.getSize(); // beginning of the current GBT word in the link + link->data.addFast(cableData.getPtr(), nb); // fill payload of cable + link->data.addFast(zero16, GBTPaddedWordLength - nb); // fill the rest of the GBT word by 0 + link->data[gbtWordStart + 9] = mMAP.getGBTHeaderRUType(ru.ruInfo->ruType, ru.cableHWID[mMAP.cablePos(ru.ruInfo->ruType, icab)]); // set cable flag cableData.setPtr(cableData.getPtr() + nb); hasData = true; } // storing data of single cable @@ -214,8 +216,8 @@ void MC2RawEncoder<Mapping>::fillGBTLinks(RUDecodeData& ru) // all payload was dumped, write final trailer GBTDataTrailer gbtTrailer; // lanes will be set on closing the trigger - gbtTrailer.lanesStops = link->lanes; - gbtTrailer.packetDone = true; + // gbtTrailer.lanesStops = link->lanes; // RS CURRENTLY NOT USED + gbtTrailer.packetDone = true; // RS CURRENTLY NOT USED link->data.addFast(gbtTrailer.getW8(), GBTPaddedWordLength); // write GBT trailer for the last packet LOGF(DEBUG, "Filled %s with %d GBT words", link->describe(), nPayLoadWordsNeeded + 3); @@ -280,39 +282,61 @@ int MC2RawEncoder<Mapping>::carryOverMethod(const header::RDHAny* rdh, const gsl // In case returned actualSize == 0, current CRU page will be closed w/o adding anything, and new // query of this method will be done on the new CRU page - constexpr int TrigHeadSize = sizeof(GBTTrigger) + sizeof(GBTDataHeader); - constexpr int TotServiceSize = sizeof(GBTTrigger) + sizeof(GBTDataHeader) + sizeof(GBTDataTrailer); + // During the carry-over ITS needs to repeat the GBTTrigger and GBTDataTrailer words. + // Also the GBTDataHeader needs to be repeated right after continuation RDH, but it will be provided in the newRDHMethod + constexpr int TrigHeadSize = sizeof(GBTTrigger); + constexpr int TotServiceSize = sizeof(GBTTrigger) + sizeof(GBTDataTrailer); int offs = ptr - &data[0]; // offset wrt the head of the payload // make sure ptr and end of the suggested block are within the payload assert(offs >= 0 && size_t(offs + maxSize) <= data.size()); - if ((maxSize <= TotServiceSize)) { // we cannot split trigger+header - return 0; // suggest moving the whole payload to the new CRU page - } - // this is where we would usually split: account for the trailer to add int actualSize = maxSize - sizeof(GBTDataTrailer); - char* trailPtr = &data[data.size() - sizeof(GBTDataTrailer)]; // pointer on the payload trailer - if (ptr + actualSize >= trailPtr) { // we need to split at least 1 GBT word before the trailer - actualSize = trailPtr - ptr - GBTPaddedWordLength; + + if ((maxSize <= TotServiceSize)) { // we cannot split trigger+header + actualSize = 0; // suggest moving the whole payload to the new CRU page + if (offs == 0) { // just carry over everything, trigger+header was not yet written + return actualSize; + } + } else { + if (ptr + actualSize >= trailPtr) { // we need to split at least 1 GBT word before the trailer + actualSize = trailPtr - ptr - GBTPaddedWordLength; + } } // copy the GBTTrigger and GBTHeader from the head of the payload header.resize(TrigHeadSize); memcpy(header.data(), &data[0], TrigHeadSize); - GBTDataHeader& gbtHeader = *reinterpret_cast<GBTDataHeader*>(&header[sizeof(GBTTrigger)]); // 1st trigger then header are written - gbtHeader.packetIdx = splitID + 1; // update the ITS specific packets counter // copy the GBTTrailer from the end of the payload trailer.resize(sizeof(GBTDataTrailer)); memcpy(trailer.data(), trailPtr, sizeof(GBTDataTrailer)); GBTDataTrailer& gbtTrailer = *reinterpret_cast<GBTDataTrailer*>(&trailer[0]); gbtTrailer.packetDone = false; // intermediate trailers should not have done=true - gbtTrailer.lanesStops = 0; // intermediate trailers should not have lanes closed + // gbtTrailer.lanesStops = 0; // intermediate trailers should not have lanes closed // RS CURRENTLY NOT USED return actualSize; } +///______________________________________________________________________ +template <class Mapping> +void MC2RawEncoder<Mapping>::newRDHMethod(const header::RDHAny* rdh, bool empty, std::vector<char>& toAdd) const +{ + // these method is called by the writer when it opens a new RDH page to fill some data. + // empty tells if the previous RDH page had some payload (to differentiate between automatic open/close of empty RDH pages, handled by + // the emptyHBFFunc and read data filling + if (RDHUtils::getStop(rdh)) { // the RDH was added to close previous HBF, do we want to add diagnostic data? + if (!empty) { + GBTDiagnostic diag; + toAdd.resize(GBTPaddedWordLength); + memcpy(toAdd.data(), diag.getW8(), GBTPaddedWordLength); + } + } else { // we need to add GBTDataHeader + toAdd.resize(GBTPaddedWordLength); + memcpy(toAdd.data(), mFEEId2GBTHeader.find(RDHUtils::getFEEID(rdh))->second.getW8(), GBTPaddedWordLength); + } +} + template class o2::itsmft::MC2RawEncoder<o2::itsmft::ChipMappingITS>; template class o2::itsmft::MC2RawEncoder<o2::itsmft::ChipMappingMFT>; diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h index 0b83758ba0537..f624eb1781b1f 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h @@ -17,6 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Headers/DataHeader.h" +#include "ITSMFTReconstruction/CTFCoder.h" #include <TStopwatch.h> namespace o2 @@ -29,11 +30,13 @@ class EntropyDecoderSpec : public o2::framework::Task public: EntropyDecoderSpec(o2::header::DataOrigin orig); ~EntropyDecoderSpec() override = default; + void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; private: o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + o2::itsmft::CTFCoder mCTFCoder; TStopwatch mTimer; }; diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h index 8607687028a8f..997f0f5eb192b 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h @@ -18,6 +18,7 @@ #include "Framework/Task.h" #include "Headers/DataHeader.h" #include <TStopwatch.h> +#include "ITSMFTReconstruction/CTFCoder.h" namespace o2 { @@ -30,10 +31,12 @@ class EntropyEncoderSpec : public o2::framework::Task EntropyEncoderSpec(o2::header::DataOrigin orig); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; private: o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + o2::itsmft::CTFCoder mCTFCoder; TStopwatch mTimer; }; diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h index 166eecd60da12..66567aa47a32c 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h @@ -37,7 +37,7 @@ template <class Mapping> class STFDecoder : public Task { public: - STFDecoder(bool clusters = true, bool pattern = true, bool digits = false, std::string_view dict = ""); + STFDecoder(bool clusters = true, bool pattern = true, bool digits = false, std::string_view dict = "", std::string_view noise = ""); ~STFDecoder() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -53,6 +53,7 @@ class STFDecoder : public Task size_t mTFCounter = 0; std::string mSelfName; std::string mDictName; + std::string mNoiseName; std::unique_ptr<RawPixelDecoder<Mapping>> mDecoder; std::unique_ptr<Clusterer> mClusterer; }; @@ -61,8 +62,8 @@ using STFDecoderITS = STFDecoder<ChipMappingITS>; using STFDecoderMFT = STFDecoder<ChipMappingMFT>; /// create a processor spec -o2::framework::DataProcessorSpec getSTFDecoderITSSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict); -o2::framework::DataProcessorSpec getSTFDecoderMFTSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict); +o2::framework::DataProcessorSpec getSTFDecoderITSSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict, const std::string& noise); +o2::framework::DataProcessorSpec getSTFDecoderMFTSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict, const std::string& noise); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index b8ba2a8564306..5047a52807531 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -16,6 +16,7 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" #include <cassert> diff --git a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx index e8bc9f3f70e31..f01f6ae485031 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx @@ -16,7 +16,8 @@ #include "Headers/DataHeader.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" #include "SimulationDataFormat/MCCompLabel.h" #include <vector> #include <string> @@ -32,7 +33,7 @@ namespace itsmft template <typename T> using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; -using MCCont = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; +using MCCont = o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>; /// create the processor spec /// describing a processor receiving digits for ITS/MFT and writing them to file @@ -44,12 +45,45 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, o2::header::DataOrigin detOri auto logger = [](std::vector<o2::itsmft::Digit> const& inDigits) { LOG(INFO) << "RECEIVED DIGITS SIZE " << inDigits.size(); }; + + // the callback to be set as hook for custom action when the writer is closed + auto finishWriting = [](TFile* outputfile, TTree* outputtree) { + const auto* brArr = outputtree->GetListOfBranches(); + int64_t nent = 0; + for (const auto* brc : *brArr) { + int64_t n = ((const TBranch*)brc)->GetEntries(); + if (nent && (nent != n)) { + LOG(ERROR) << "Branches have different number of entries"; + } + nent = n; + } + outputtree->SetEntries(nent); + outputtree->Write("", TObject::kOverwrite); + outputfile->Close(); + }; + + // handler for labels + // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. + // We therefore convert it to a special split class. + auto fillLabels = [](TBranch& branch, std::vector<char> const& labelbuffer, DataRef const& /*ref*/) { + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> labels(labelbuffer); + LOG(INFO) << "WRITING " << labels.getNElements() << " LABELS "; + + o2::dataformats::IOMCTruthContainerView outputcontainer; + auto br = framework::RootTreeWriter::remapBranch(branch, &outputcontainer); + outputcontainer.adopt(labelbuffer); + br->Fill(); + br->ResetAddress(); + }; + return MakeRootTreeWriterSpec((detStr + "DigitWriter").c_str(), (detStrL + "digits.root").c_str(), MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Digits tree"}, - BranchDefinition<MCCont>{InputSpec{"digitsMCTR", detOrig, "DIGITSMCTR", 0}, - (detStr + "DigitMCTruth").c_str(), - (mctruth ? 1 : 0)}, + MakeRootTreeWriterSpec::CustomClose(finishWriting), + // in case of labels we first read them as std::vector<char> and process them correctly in the fillLabels hook + BranchDefinition<std::vector<char>>{InputSpec{"digitsMCTR", detOrig, "DIGITSMCTR", 0}, + (detStr + "DigitMCTruth").c_str(), + (mctruth ? 1 : 0), fillLabels}, BranchDefinition<std::vector<itsmft::MC2ROFRecord>>{InputSpec{"digitsMC2ROF", detOrig, "DIGITSMC2ROF", 0}, (detStr + "DigitMC2ROF").c_str(), (mctruth ? 1 : 0)}, diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx index 193497bee8010..ffd61641dfd4b 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx @@ -15,7 +15,6 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "DataFormatsITSMFT/CompCluster.h" -#include "ITSMFTReconstruction/CTFCoder.h" #include "ITSMFTWorkflow/EntropyDecoderSpec.h" using namespace o2::framework; @@ -25,13 +24,22 @@ namespace o2 namespace itsmft { -EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig) : mOrigin(orig) +EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig) + : mOrigin(orig), mCTFCoder(orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT) { assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mTimer.Stop(); mTimer.Reset(); } +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>((mOrigin == o2::header::gDataOriginITS) ? "its-ctf-dictionary" : "mft-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + void EntropyDecoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); @@ -45,7 +53,7 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object const auto ctfImage = o2::itsmft::CTF::getImage(buff.data()); - CTFCoder::decode(ctfImage, rofs, compcl, patterns); + mCTFCoder.decode(ctfImage, rofs, compcl, patterns); mTimer.Stop(); LOG(INFO) << "Decoded " << compcl.size() << " clusters in " << rofs.size() << " RO frames in " << mTimer.CpuTime() - cput << " s"; @@ -64,12 +72,14 @@ DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig) OutputSpec{{"patterns"}, orig, "PATTERNS", 0, Lifetime::Timeframe}, OutputSpec{{"ROframes"}, orig, "CLUSTERSROF", 0, Lifetime::Timeframe}}; + std::string dictOptName = (orig == o2::header::gDataOriginITS) ? "its-ctf-dictionary" : "mft-ctf-dictionary"; + return DataProcessorSpec{ orig == o2::header::gDataOriginITS ? "its-entropy-decoder" : "mft-entropy-decoder", Inputs{InputSpec{"ctf", orig, "CTFDATA", 0, Lifetime::Timeframe}}, outputs, AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>(orig)}, - Options{}}; + Options{{dictOptName, VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; } } // namespace itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx index 6bd7baec5f1e7..cb9af0129f9ab 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx @@ -15,8 +15,8 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "DataFormatsITSMFT/CompCluster.h" -#include "ITSMFTReconstruction/CTFCoder.h" #include "ITSMFTWorkflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; @@ -25,13 +25,22 @@ namespace o2 namespace itsmft { -EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig) : mOrigin(orig) +EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig) + : mOrigin(orig), mCTFCoder(orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT) { assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mTimer.Stop(); mTimer.Reset(); } +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>((mOrigin == o2::header::gDataOriginITS) ? "its-ctf-dictionary" : "mft-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); @@ -41,7 +50,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) auto rofs = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>("ROframes"); auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{mOrigin, "CTFDATA", 0, Lifetime::Timeframe}); - CTFCoder::encode(buffer, rofs, compClusters, pspan); + mCTFCoder.encode(buffer, rofs, compClusters, pspan); auto eeb = CTF::get(buffer.data()); // cast to container pointer eeb->compactify(); // eliminate unnecessary padding buffer.resize(eeb->size()); // shrink buffer to strictly necessary size @@ -63,12 +72,14 @@ DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig) inputs.emplace_back("patterns", orig, "PATTERNS", 0, Lifetime::Timeframe); inputs.emplace_back("ROframes", orig, "CLUSTERSROF", 0, Lifetime::Timeframe); + std::string dictOptName = (orig == o2::header::gDataOriginITS) ? "its-ctf-dictionary" : "mft-ctf-dictionary"; + return DataProcessorSpec{ orig == o2::header::gDataOriginITS ? "its-entropy-encoder" : "mft-entropy-encoder", inputs, Outputs{{orig, "CTFDATA", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>(orig)}, - Options{}}; + Options{{dictOptName, VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; } } // namespace itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx index e9b53db160b6d..224a0ada3a283 100644 --- a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx @@ -40,8 +40,8 @@ using namespace o2::framework; ///_______________________________________ template <class Mapping> -STFDecoder<Mapping>::STFDecoder(bool doClusters, bool doPatterns, bool doDigits, std::string_view dict) - : mDoClusters(doClusters), mDoPatterns(doPatterns), mDoDigits(doDigits), mDictName(dict) +STFDecoder<Mapping>::STFDecoder(bool doClusters, bool doPatterns, bool doDigits, std::string_view dict, std::string_view noise) + : mDoClusters(doClusters), mDoPatterns(doPatterns), mDoDigits(doDigits), mDictName(dict), mNoiseName(noise) { mSelfName = o2::utils::concat_string(Mapping::getName(), "STFDecoder"); mTimer.Stop(); @@ -60,6 +60,17 @@ void STFDecoder<Mapping>::init(InitContext& ic) mDecoder->setNThreads(mNThreads); mDecoder->setFormat(ic.options().get<bool>("old-format") ? GBTLink::OldFormat : GBTLink::NewFormat); mDecoder->setVerbosity(ic.options().get<int>("decoder-verbosity")); + + std::string noiseFile = o2::base::NameConf::getDictionaryFileName(detID, mNoiseName, ".root"); + if (o2::base::NameConf::pathExists(noiseFile)) { + TFile* f = TFile::Open(noiseFile.data(), "old"); + auto pnoise = (NoiseMap*)f->Get("Noise"); + AlpideCoder::setNoisyPixels(pnoise); + LOG(INFO) << mSelfName << " loading noise map file: " << noiseFile; + } else { + LOG(INFO) << mSelfName << " Noise file " << noiseFile << " is absent, " << Mapping::getName() << " running without noise suppression"; + } + if (mDoClusters) { mClusterer = std::make_unique<Clusterer>(); mClusterer->setNChips(Mapping::getNChips()); @@ -149,7 +160,7 @@ void STFDecoder<Mapping>::endOfStream(EndOfStreamContext& ec) } } -DataProcessorSpec getSTFDecoderITSSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict) +DataProcessorSpec getSTFDecoderITSSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict, const std::string& noise) { std::vector<OutputSpec> outputs; auto orig = o2::header::gDataOriginITS; @@ -168,14 +179,14 @@ DataProcessorSpec getSTFDecoderITSSpec(bool doClusters, bool doPatterns, bool do "its-stf-decoder", Inputs{{"stf", ConcreteDataTypeMatcher{orig, "RAWDATA"}, Lifetime::Timeframe}}, outputs, - AlgorithmSpec{adaptFromTask<STFDecoder<ChipMappingITS>>(doClusters, doPatterns, doDigits, dict)}, + AlgorithmSpec{adaptFromTask<STFDecoder<ChipMappingITS>>(doClusters, doPatterns, doDigits, dict, noise)}, Options{ {"nthreads", VariantType::Int, 1, {"Number of decoding/clustering threads"}}, {"old-format", VariantType::Bool, false, {"Use old format (1 trigger per CRU page)"}}, {"decoder-verbosity", VariantType::Int, 0, {"Verbosity level (-1: silent, 0: errors, 1: headers, 2: data)"}}}}; } -DataProcessorSpec getSTFDecoderMFTSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict) +DataProcessorSpec getSTFDecoderMFTSpec(bool doClusters, bool doPatterns, bool doDigits, const std::string& dict, const std::string& noise) { std::vector<OutputSpec> outputs; auto orig = o2::header::gDataOriginMFT; @@ -196,7 +207,7 @@ DataProcessorSpec getSTFDecoderMFTSpec(bool doClusters, bool doPatterns, bool do "mft-stf-decoder", Inputs{{"stf", ConcreteDataTypeMatcher{orig, "RAWDATA"}, Lifetime::Timeframe}}, outputs, - AlgorithmSpec{adaptFromTask<STFDecoder<ChipMappingMFT>>(doClusters, doPatterns, doDigits, dict)}, + AlgorithmSpec{adaptFromTask<STFDecoder<ChipMappingMFT>>(doClusters, doPatterns, doDigits, dict, noise)}, Options{ {"nthreads", VariantType::Int, 1, {"Number of decoding/clustering threads"}}, {"old-format", VariantType::Bool, false, {"Use old format (1 trigger per CRU page)"}}, diff --git a/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx index e93cab39236f0..f969f197380af 100644 --- a/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx @@ -22,7 +22,7 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) // option allowing to set parameters std::vector<ConfigParamSpec> options{ ConfigParamSpec{"disable-mc", VariantType::Bool, false, {"disable mc truth"}}, - ConfigParamSpec{"mft", VariantType::Bool, false, {"expect MFT data"}}, + ConfigParamSpec{"runmft", VariantType::Bool, false, {"expect MFT data"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); @@ -40,7 +40,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); - if (cfgc.options().get<bool>("mft")) { + if (cfgc.options().get<bool>("runmft")) { wf.emplace_back(o2::itsmft::getMFTDigitWriterSpec(useMC)); } else { wf.emplace_back(o2::itsmft::getITSDigitWriterSpec(useMC)); diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index bb7d0868c3d19..2859ab6bcd0c3 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -21,7 +21,7 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) { // option allowing to set parameters std::vector<ConfigParamSpec> options{ - ConfigParamSpec{"mft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, + ConfigParamSpec{"runmft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); @@ -37,7 +37,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); - if (cfgc.options().get<bool>("mft")) { + if (cfgc.options().get<bool>("runmft")) { wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT")); } else { wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS")); diff --git a/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx index 839faa6d8fbe8..55992bcce62be 100644 --- a/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx @@ -21,11 +21,12 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) { // option allowing to set parameters std::vector<ConfigParamSpec> options{ - ConfigParamSpec{"mft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, + ConfigParamSpec{"runmft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, ConfigParamSpec{"no-clusters", VariantType::Bool, false, {"do not produce clusters (def: produce)"}}, ConfigParamSpec{"no-cluster-patterns", VariantType::Bool, false, {"do not produce clusters patterns (def: produce)"}}, ConfigParamSpec{"digits", VariantType::Bool, false, {"produce digits (def: skip)"}}, ConfigParamSpec{"dict-file", VariantType::String, "", {"name of the cluster-topology dictionary file"}}, + ConfigParamSpec{"noise-file", VariantType::String, "", {"name of the noise map file"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); @@ -42,14 +43,15 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto doPatterns = doClusters && !cfgc.options().get<bool>("no-cluster-patterns"); auto doDigits = cfgc.options().get<bool>("digits"); auto dict = cfgc.options().get<std::string>("dict-file"); + auto noise = cfgc.options().get<std::string>("noise-file"); // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); - if (cfgc.options().get<bool>("mft")) { - wf.emplace_back(o2::itsmft::getSTFDecoderMFTSpec(doClusters, doPatterns, doDigits, dict)); + if (cfgc.options().get<bool>("runmft")) { + wf.emplace_back(o2::itsmft::getSTFDecoderMFTSpec(doClusters, doPatterns, doDigits, dict, noise)); } else { - wf.emplace_back(o2::itsmft::getSTFDecoderITSSpec(doClusters, doPatterns, doDigits, dict)); + wf.emplace_back(o2::itsmft::getSTFDecoderITSSpec(doClusters, doPatterns, doDigits, dict, noise)); } return wf; } diff --git a/Detectors/ITSMFT/test/HitAnalysis/src/HitAnalysis.cxx b/Detectors/ITSMFT/test/HitAnalysis/src/HitAnalysis.cxx index d5f520fa9cfd3..2b8ce3edbd0f6 100644 --- a/Detectors/ITSMFT/test/HitAnalysis/src/HitAnalysis.cxx +++ b/Detectors/ITSMFT/test/HitAnalysis/src/HitAnalysis.cxx @@ -27,7 +27,7 @@ #include "ITSBase/GeometryTGeo.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSMFTSimulation/Hit.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" using Segmentation = o2::itsmft::SegmentationAlpide; @@ -81,9 +81,10 @@ InitStatus HitAnalysis::Init() // Create geometry, initialize chip array GeometryTGeo* geom = GeometryTGeo::Instance(); - if (!geom->isBuilt()) + if (!geom->isBuilt()) { geom->Build(true); - geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::L2G)); // make sure T2L matrices are loaded + } + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); // make sure T2L matrices are loaded mGeometry = geom; diff --git a/Detectors/MUON/MCH/Base/CMakeLists.txt b/Detectors/MUON/MCH/Base/CMakeLists.txt index 4d0bd10526828..c586850ba1c9f 100644 --- a/Detectors/MUON/MCH/Base/CMakeLists.txt +++ b/Detectors/MUON/MCH/Base/CMakeLists.txt @@ -12,8 +12,8 @@ o2_add_library(MCHBase SOURCES src/ClusterBlock.cxx src/Digit.cxx - src/PreCluster.cxx - src/TrackBlock.cxx + src/PreCluster.cxx + src/TrackBlock.cxx PUBLIC_LINK_LIBRARIES ROOT::Core FairRoot::Base FairMQ::FairMQ ms_gsl::ms_gsl) o2_target_root_dictionary(MCHBase diff --git a/Detectors/MUON/MCH/Base/src/MCHBaseLinkDef.h b/Detectors/MUON/MCH/Base/src/MCHBaseLinkDef.h index b2afadd958883..3519cd9ed1239 100644 --- a/Detectors/MUON/MCH/Base/src/MCHBaseLinkDef.h +++ b/Detectors/MUON/MCH/Base/src/MCHBaseLinkDef.h @@ -18,6 +18,6 @@ #pragma link C++ namespace o2::mch; #pragma link C++ class o2::mch::Digit + ; -#pragma link C++ class std::vector < o2::mch::Digit> + ; +#pragma link C++ class std::vector < o2::mch::Digit > +; #endif diff --git a/Detectors/MUON/MCH/CMakeLists.txt b/Detectors/MUON/MCH/CMakeLists.txt index a4cf37dfb10d3..b9d77796fc981 100644 --- a/Detectors/MUON/MCH/CMakeLists.txt +++ b/Detectors/MUON/MCH/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(Base) add_subdirectory(Contour) add_subdirectory(Mapping) add_subdirectory(PreClustering) +add_subdirectory(Geometry) add_subdirectory(Clustering) add_subdirectory(Simulation) add_subdirectory(Tracking) diff --git a/Detectors/MUON/MCH/Clustering/src/ClusterFinderOriginal.cxx b/Detectors/MUON/MCH/Clustering/src/ClusterFinderOriginal.cxx index 9efb874af05ea..065d50fb6e9d1 100644 --- a/Detectors/MUON/MCH/Clustering/src/ClusterFinderOriginal.cxx +++ b/Detectors/MUON/MCH/Clustering/src/ClusterFinderOriginal.cxx @@ -173,7 +173,7 @@ void ClusterFinderOriginal::resetPreCluster(gsl::span<const Digit>& digits) double y = mSegmentation->padPositionY(padID); double dx = mSegmentation->padSizeX(padID) / 2.; double dy = mSegmentation->padSizeY(padID) / 2.; - double charge = static_cast<double>(digit.getADC()) / std::numeric_limits<unsigned long>::max() * 1024; + double charge = static_cast<double>(digit.getADC()) / static_cast<double>(std::numeric_limits<unsigned long>::max()) * 1024; bool isSaturated = digit.getTime().time > 0; int plane = mSegmentation->isBendingPad(padID) ? 0 : 1; diff --git a/Detectors/MUON/MCH/Geometry/CMakeLists.txt b/Detectors/MUON/MCH/Geometry/CMakeLists.txt new file mode 100644 index 0000000000000..404d396bf2574 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(Transformer) +add_subdirectory(Creator) +if (BUILD_TESTING) +add_subdirectory(Test) +endif() + diff --git a/Detectors/MUON/MCH/Geometry/Creator/CMakeLists.txt b/Detectors/MUON/MCH/Geometry/Creator/CMakeLists.txt new file mode 100644 index 0000000000000..2790fdf26d6eb --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Creator/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + MCHGeometryCreator + SOURCES src/Geometry.cxx + src/Materials.cxx + src/Materials.h + src/Station1Geometry.cxx + src/Station1Geometry.h + src/Station2Geometry.cxx + src/Station2Geometry.h + src/Station345Geometry.cxx + src/Station345Geometry.h + PUBLIC_LINK_LIBRARIES ROOT::Geom RapidJSON::RapidJSON + O2::MCHGeometryTransformer O2::DetectorsBase) + +o2_target_root_dictionary(MCHGeometryCreator + HEADERS include/MCHGeometryCreator/Geometry.h) diff --git a/Detectors/MUON/MCH/Geometry/Creator/README.md b/Detectors/MUON/MCH/Geometry/Creator/README.md new file mode 100644 index 0000000000000..619b98b3e666d --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Creator/README.md @@ -0,0 +1,9 @@ +<!-- doxy +\page refDetectorsMUONMCHGeometryCreator Creation +/doxy --> + +# MUON MCH Geometry Creator + +This package hosts the functions to create, from scratch, the TGeo-based + geometry for MCH. + diff --git a/Detectors/MUON/MCH/Geometry/Creator/include/MCHGeometryCreator/Geometry.h b/Detectors/MUON/MCH/Geometry/Creator/include/MCHGeometryCreator/Geometry.h new file mode 100644 index 0000000000000..b645386c8b668 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Creator/include/MCHGeometryCreator/Geometry.h @@ -0,0 +1,60 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Geometry.h +/// @brief Interface for MCH geometry creation + +#ifndef O2_MCH_GEOMETRY_CREATOR_GEOMETRY_H +#define O2_MCH_GEOMETRY_CREATOR_GEOMETRY_H + +#include <vector> +#include <iostream> + +class TGeoVolume; +class TGeoManager; + +namespace o2::mch::geo +{ + +/** createGeometry creates MCH geometry. + * + * Geometry comprises volumes, materials and alignable volumes. + * + * Note that the geometry of stations 1 and 2 is attached to volume YOUT1 + * if it exist, or to topVolume otherwise. + * + * Geometry for stations 3, 4 and 5 are always attached to topVolume. + * + * @param topVolume the volume the MCH geometry is attached topVolume + */ +void createGeometry(TGeoManager& geom, TGeoVolume& topVolume); + +/** get a list of MCH sensitive volumes. + * @returns a vector of all the MCH volumes that participate in the + * particle tracking (in the transport sense). + */ +std::vector<TGeoVolume*> getSensitiveVolumes(); + +/** Add alignable MCH volumes to the global geometry. + * + * Creates entries for alignable volumes associating symbolic volume + * names with corresponding volume paths. + * + * @warning It also closes the geometry if it is not yet closed. + * + * @param geoManager the (global) TGeoManager instance, which thus + * must exist before calling this function. + * + */ +void addAlignableVolumes(TGeoManager& geom); + +} // namespace o2::mch::geo + +#endif diff --git a/Detectors/MUON/MCH/Geometry/Creator/src/Geometry.cxx b/Detectors/MUON/MCH/Geometry/Creator/src/Geometry.cxx new file mode 100644 index 0000000000000..d1d019f9a03fb --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Geometry.cxx @@ -0,0 +1,139 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "MCHGeometryCreator/Geometry.h" +#include "MCHGeometryTransformer/VolumePaths.h" +#include "Station1Geometry.h" +#include "Station2Geometry.h" +#include "Station345Geometry.h" +#include "Materials.h" +#include <iostream> +#include <TGeoPhysicalNode.h> +#include <fmt/format.h> +#include "TGeoVolume.h" +#include "TGeoManager.h" +#include "Framework/Logger.h" + +namespace impl +{ +void addAlignableVolumesHalfChamber(TGeoManager& geom, int hc, std::string& parent) +{ + // + // Add alignable volumes for a half chamber and its daughters + // + std::vector<std::vector<int>> DEofHC{{100, 103}, + {101, 102}, + {200, 203}, + {201, 202}, + {300, 303}, + {301, 302}, + {400, 403}, + {401, 402}, + {500, 501, 502, 503, 504, 514, 515, 516, 517}, + {505, 506, 507, 508, 509, 510, 511, 512, 513}, + {600, 601, 602, 603, 604, 614, 615, 616, 617}, + {605, 606, 607, 608, 609, 610, 611, 612, 613}, + {700, 701, 702, 703, 704, 705, 706, 720, 721, 722, 723, 724, 725}, + {707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719}, + {800, 801, 802, 803, 804, 805, 806, 820, 821, 822, 823, 824, 825}, + {807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819}, + {900, 901, 902, 903, 904, 905, 906, 920, 921, 922, 923, 924, 925}, + {907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919}, + {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1020, 1021, 1022, 1023, 1024, 1025}, + {1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019}}; + + for (int i = 0; i < DEofHC[hc].size(); i++) { + std::string volPathName = o2::mch::geo::volumePathName(DEofHC[hc][i]); + + TString path = Form("%s%s", parent.c_str(), volPathName.c_str()); + TString sname = Form("MCH/HC%d/DE%d", hc, DEofHC[hc][i]); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!geom.SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + } + + return; +} + +} // namespace impl + +namespace o2::mch::geo +{ + +void createGeometry(TGeoManager& geom, TGeoVolume& topVolume) +{ + createMaterials(); + + auto volYOUT1 = geom.GetVolume("YOUT1"); + + createStation1Geometry((volYOUT1) ? *volYOUT1 : topVolume); + createStation2Geometry((volYOUT1) ? *volYOUT1 : topVolume); + + createStation345Geometry(topVolume); +} + +std::vector<TGeoVolume*> getSensitiveVolumes() +{ + auto st1 = getStation1SensitiveVolumes(); + auto st2 = getStation2SensitiveVolumes(); + auto st345 = getStation345SensitiveVolumes(); + + auto vol = st1; + vol.insert(vol.end(), st2.begin(), st2.end()); + vol.insert(vol.end(), st345.begin(), st345.end()); + + return vol; +} + +void addAlignableVolumes(TGeoManager& geom) +{ + if (!geom.IsClosed()) { + geom.CloseGeometry(); + } + + LOG(INFO) << "Add MCH alignable volumes"; + + for (int hc = 0; hc < 20; hc++) { + int nCh = hc / 2 + 1; + + std::string volPathName = geom.GetTopVolume()->GetName(); + + if (nCh <= 4 && geom.GetVolume("YOUT1")) { + volPathName += "/YOUT1_1/"; + } else if ((nCh == 5 || nCh == 6) && geom.GetVolume("DDIP")) { + volPathName += "/DDIP_1/"; + } else if (nCh >= 7 && geom.GetVolume("YOUT2")) { + volPathName += "/YOUT2_1/"; + } else { + volPathName += "/"; + } + + std::string path = fmt::format("{0}SC{1}{2}{3}_{4}", volPathName.c_str(), nCh < 10 ? "0" : "", nCh, hc % 2 ? "O" : "I", hc); + std::string sname = fmt::format("MCH/HC{}", hc); + + LOG(DEBUG) << sname << " <-> " << path; + + auto ae = geom.SetAlignableEntry(sname.c_str(), path.c_str()); + if (!ae) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + Int_t lastUID = 0; + + impl::addAlignableVolumesHalfChamber(geom, hc, volPathName); + } + + return; +} + +} // namespace o2::mch::geo diff --git a/Detectors/MUON/MCH/Geometry/Creator/src/MCHGeometryCreatorLinkDef.h b/Detectors/MUON/MCH/Geometry/Creator/src/MCHGeometryCreatorLinkDef.h new file mode 100644 index 0000000000000..e08a6167bd921 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Creator/src/MCHGeometryCreatorLinkDef.h @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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++ namespace o2::mch::geo; + +#pragma link C++ function o2::mch::geo::addAlignableVolumes; +#pragma link C++ function o2::mch::geo::createGeometry; +#pragma link C++ function o2::mch::geo::getSensitiveVolumes; + +#endif diff --git a/Detectors/MUON/MCH/Simulation/src/Materials.cxx b/Detectors/MUON/MCH/Geometry/Creator/src/Materials.cxx similarity index 100% rename from Detectors/MUON/MCH/Simulation/src/Materials.cxx rename to Detectors/MUON/MCH/Geometry/Creator/src/Materials.cxx diff --git a/Detectors/MUON/MCH/Simulation/src/Materials.h b/Detectors/MUON/MCH/Geometry/Creator/src/Materials.h similarity index 83% rename from Detectors/MUON/MCH/Simulation/src/Materials.h rename to Detectors/MUON/MCH/Geometry/Creator/src/Materials.h index acb3480688326..ed7b94881b0c4 100644 --- a/Detectors/MUON/MCH/Simulation/src/Materials.h +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Materials.h @@ -13,15 +13,12 @@ /// \author Florian Damas <florian.damas@cern.ch> /// \date 22 march 2018 -#ifndef O2_MCH_SIMULATION_MATERIALS_H -#define O2_MCH_SIMULATION_MATERIALS_H +#ifndef O2_MCH_GEOMETRY_CREATOR_MATERIALS_H +#define O2_MCH_GEOMETRY_CREATOR_MATERIALS_H -#include <TGeoMedium.h> +class TGeoMedium; -namespace o2 -{ - -namespace mch +namespace o2::mch { enum Medium { @@ -48,7 +45,6 @@ TGeoMedium* assertMedium(int imed); void createMaterials(); -} // namespace mch -} // namespace o2 +} // namespace o2::mch -#endif // O2_MCH_SIMULATION_MATERIALS_H +#endif diff --git a/Detectors/MUON/MCH/Simulation/src/Station1Geometry.cxx b/Detectors/MUON/MCH/Geometry/Creator/src/Station1Geometry.cxx similarity index 99% rename from Detectors/MUON/MCH/Simulation/src/Station1Geometry.cxx rename to Detectors/MUON/MCH/Geometry/Creator/src/Station1Geometry.cxx index b067c15772541..046f16d23f5f0 100644 --- a/Detectors/MUON/MCH/Simulation/src/Station1Geometry.cxx +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Station1Geometry.cxx @@ -107,8 +107,8 @@ const float kDeltaQuadLHC = 2.6; // LHC Origin wrt Quadrant Origin const float kFrameOffset = 5.2; // pad plane offsets -const float kPadXOffsetBP = 0.50 - 0.63 / 2; // = 0.185 -const float kPadYOffsetBP = 0.31 + 0.42 / 2; // = 0.52 +const float kPadXOffsetBP = 0.50 - 0.63 / 2; // = 0.185 +const float kPadYOffsetBP = -0.31 - 0.42 / 2; // = -0.52 const int kFoamBoxNameOffset = 200; const int kFR4BoxNameOffset = 400; const int kDaughterCopyNoOffset = 1000; @@ -674,8 +674,9 @@ void createFrame(int chamber) vy[15] = 0.; // shift center in the middle - for (int i = 0; i < nv; i++) + for (int i = 0; i < nv; i++) { vy[i] += dy4 / 2; + } TGeoXtru* xtruS1 = new TGeoXtru(nz); xtruS1->DefinePolygon(nv, vx, vy); @@ -1119,8 +1120,9 @@ void createFrame(int chamber) x = kDeltaQuadLHC + scruX[i] + 0.1; y = kDeltaQuadLHC + scruY[i] + 0.1; Mlayer->AddNode(vol43, i + 1, new TGeoTranslation(x, y, z - kHzInHFrame - kSCRUHLE)); - if (chamber == 1) + if (chamber == 1) { gGeoManager->GetVolume("SQ40")->AddNode(vol44, i + 1, new TGeoTranslation(x - kMidHXPos, y - kMidHYPos, z - kMidHZPos)); + } Mlayer->AddNode(vol45, i + 1, new TGeoTranslation(x, y, z + kHzInHFrame + kSCRUNLE)); } @@ -1134,8 +1136,9 @@ void createFrame(int chamber) Mlayer->AddNode(vol43, kNScrews, new TGeoTranslation(x, y, z - kHzInHFrame - kSCRUHLE)); - if (chamber == 1) + if (chamber == 1) { gGeoManager->GetVolume("SQ40")->AddNode(vol44, kNScrews, new TGeoTranslation(x - kMidHXPos, y - kMidHYPos, z - kMidHZPos)); + } Mlayer->AddNode(vol45, kNScrews, new TGeoTranslation(x, y, z + kHzInHFrame + kSCRUNLE)); @@ -1162,8 +1165,9 @@ void createFrame(int chamber) Mlayer->AddNode(vol43, i, new TGeoTranslation(x, y, z - kHzInHFrame - kSCRUHLE)); - if (chamber == 1) + if (chamber == 1) { gGeoManager->GetVolume("SQ00")->AddNode(vol44, i, new TGeoTranslation(x - kMidVXPos, y - kMidVYPos, z - kMidVZPos)); + } Mlayer->AddNode(vol45, i, new TGeoTranslation(x, y, z + kHzInHFrame + kSCRUNLE)); } @@ -1189,8 +1193,9 @@ void createFrame(int chamber) y = kDeltaQuadLHC + scruY[i - 1] + 0.1; Mlayer->AddNode(vol43, i, new TGeoTranslation(x, y, z - kHzInHFrame - kSCRUHLE)); - if (chamber == 1) + if (chamber == 1) { gGeoManager->GetVolume("SQ25")->AddNode(vol44, i, new TGeoTranslation(x - kMidOVXPos, y - kMidOVYPos, z - kMidOVZPos)); + } Mlayer->AddNode(vol45, i, new TGeoTranslation(x, y, z + kHzInHFrame + kSCRUNLE)); } @@ -1200,8 +1205,9 @@ void createFrame(int chamber) y = kDeltaQuadLHC + scruY[firstScrew - 1] + 0.1 - kMidHYPos; z = -kMidHZPos; - if (chamber == 1) + if (chamber == 1) { gGeoManager->GetVolume("SQ40")->AddNode(vol44, firstScrew, new TGeoTranslation(x, y, z)); + } // inner arc of Frame, screw positions and numbers firstScrew = 58; @@ -1223,8 +1229,9 @@ void createFrame(int chamber) Mlayer->AddNode(vol43, i + 1, new TGeoTranslation(x, y, z - kHzInHFrame - kSCRUHLE)); - if (chamber == 1) + if (chamber == 1) { gGeoManager->GetVolume("SQ42")->AddNode(vol44, i + 1, new TGeoTranslation(x - kMidArcXPos, y - kMidArcYPos, z - kMidArcZPos)); + } Mlayer->AddNode(vol45, i + 1, new TGeoTranslation(x, y, z + kHzInHFrame + kSCRUNLE)); } @@ -1261,28 +1268,32 @@ TGeoVolume* createPlaneSegment(int iSegment, float halfLength, float halfHeight, y = 0.75; z = -0.1; - if (is267or351) + if (is267or351) { y += kPadYOffsetBP; + } foam->AddNode(gGeoManager->GetVolume("Spacer5A"), 1, new TGeoTranslation(x, y, z)); y = -0.75; - if (is267or351) + if (is267or351) { y += kPadYOffsetBP; + } foam->AddNode(gGeoManager->GetVolume("Spacer5A"), 2, new TGeoTranslation(x, y, z)); y = 0.; z = 1.1515; - if (is267or351) + if (is267or351) { y += kPadYOffsetBP; + } foam->AddNode(gGeoManager->GetVolume("Spacer6"), 1, new TGeoTranslation(x, y, z)); y = 0.; z = 0.; - if (is267or351) + if (is267or351) { y += kPadYOffsetBP; + } foam->AddNode(gGeoManager->GetVolume("Spacer7A"), 1, new TGeoTranslation(x, y, z)); } @@ -1539,8 +1550,8 @@ void createStation1Geometry(TGeoVolume& topVolume) std::array<TGeoRotation*, kNQuadrants> rot = {rot0, rot1, rot2, rot3}; // initialize the quadrant positions - float x[kNQuadrants] = {-1, 1, 1, -1}; - float y[kNQuadrants] = {-1, -1, 1, 1}; + float x[kNQuadrants] = {1, -1, -1, 1}; + float y[kNQuadrants] = {1, 1, -1, -1}; for (int i = 0; i < kNQuadrants; i++) { x[i] *= kPadXOffsetBP; @@ -1568,10 +1579,11 @@ void createStation1Geometry(TGeoVolume& topVolume) // compute the detection element ID detElemID = 100 * ich + i; - if (x[i] < 0) { + if (x[i] > 0) { in->AddNode(quadrant, detElemID, new TGeoCombiTrans(x[i], y[i], z, rot[i])); - } else + } else { out->AddNode(quadrant, detElemID, new TGeoCombiTrans(x[i], y[i], z, rot[i])); + } } // place the half-chambers in the top volume diff --git a/Detectors/MUON/MCH/Simulation/src/Station1Geometry.h b/Detectors/MUON/MCH/Geometry/Creator/src/Station1Geometry.h similarity index 79% rename from Detectors/MUON/MCH/Simulation/src/Station1Geometry.h rename to Detectors/MUON/MCH/Geometry/Creator/src/Station1Geometry.h index 5137e1bf93a93..8018515a0e448 100644 --- a/Detectors/MUON/MCH/Simulation/src/Station1Geometry.h +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Station1Geometry.h @@ -13,22 +13,19 @@ /// \author Florian Damas <florian.damas@cern.ch> /// \date 16 mai 2018 -#ifndef O2_MCH_SIMULATION_STATION1GEOMETRY_H -#define O2_MCH_SIMULATION_STATION1GEOMETRY_H +#ifndef O2_MCH_GEOMETRY_CREATOR_STATION1GEOMETRY_H +#define O2_MCH_GEOMETRY_CREATOR_STATION1GEOMETRY_H #include <vector> class TGeoVolume; -namespace o2 -{ -namespace mch +namespace o2::mch { void createStation1Geometry(TGeoVolume& topVolume); std::vector<TGeoVolume*> getStation1SensitiveVolumes(); -} // namespace mch -} // namespace o2 -#endif // O2_MCH_SIMULATION_STATION1GEOMETRY_H +} // namespace o2::mch +#endif diff --git a/Detectors/MUON/MCH/Simulation/src/Station2Geometry.cxx b/Detectors/MUON/MCH/Geometry/Creator/src/Station2Geometry.cxx similarity index 99% rename from Detectors/MUON/MCH/Simulation/src/Station2Geometry.cxx rename to Detectors/MUON/MCH/Geometry/Creator/src/Station2Geometry.cxx index 0414a5ea6eafd..db98d78535296 100644 --- a/Detectors/MUON/MCH/Simulation/src/Station2Geometry.cxx +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Station2Geometry.cxx @@ -221,8 +221,9 @@ void createFrames() for (int i = 1; i <= kNFrames; i++) { // in this loop, we only create box frames - if (i == 3 || i == 7) + if (i == 3 || i == 7) { continue; // go to next frame + } // create the frame auto frame = new TGeoVolumeAssembly(Form("Frame %d", i)); @@ -325,8 +326,9 @@ TGeoVolume* createQuadrant() const double kSegXPos[kNSegments] = {kSegmentRadius[0] + kBoxSegHalfLength[0], 0., -kBoxSegHalfLength[2]}; const double kSegYPos[kNSegments] = {-kBoxSegHalfHeight[0], 0., kSegmentRadius[0] + kBoxSegHalfHeight[2]}; - for (int i = 0; i < kNSegments; i++) + for (int i = 0; i < kNSegments; i++) { quadrant->AddNode(createSegment(i), 0, new TGeoTranslation(kSegXPos[i], kSegYPos[i], 0.)); + } // create and place the frames in the quadrant createFrames(); @@ -354,9 +356,10 @@ TGeoVolume* createQuadrant() kSegYPos[0], // frame n°8 aligned with the segment 0 }; - for (int i = 1; i <= kNFrames; i++) + for (int i = 1; i <= kNFrames; i++) { quadrant->AddNode(gGeoManager->GetVolume(Form("Frame %d", i)), 1, new TGeoTranslation(kFrameXPos[i - 1], kFrameYPos[i - 1], 0.)); + } return quadrant; } @@ -399,8 +402,9 @@ void createStation2Geometry(TGeoVolume& topVolume) if (i == 0 || i == 3) { in->AddNode(quadrant, detElemID, new TGeoCombiTrans(0., 0., z, rot[i])); - } else + } else { out->AddNode(quadrant, detElemID, new TGeoCombiTrans(0., 0., z, rot[i])); + } } // place the half-chambers in the top volume diff --git a/Detectors/MUON/MCH/Simulation/src/Station2Geometry.h b/Detectors/MUON/MCH/Geometry/Creator/src/Station2Geometry.h similarity index 79% rename from Detectors/MUON/MCH/Simulation/src/Station2Geometry.h rename to Detectors/MUON/MCH/Geometry/Creator/src/Station2Geometry.h index ad87a22be83f9..da9a1921430d0 100644 --- a/Detectors/MUON/MCH/Simulation/src/Station2Geometry.h +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Station2Geometry.h @@ -13,22 +13,18 @@ /// \author Florian Damas <florian.damas@cern.ch> /// \date 23 mai 2018 -#ifndef O2_MCH_SIMULATION_STATION2GEOMETRY_H -#define O2_MCH_SIMULATION_STATION2GEOMETRY_H +#ifndef O2_MCH_GEOMETRY_CREATOR_STATION2GEOMETRY_H +#define O2_MCH_GEOMETRY_CREATOR_STATION2GEOMETRY_H #include <vector> class TGeoVolume; -namespace o2 +namespace o2::mch { -namespace mch -{ - void createStation2Geometry(TGeoVolume& topVolume); std::vector<TGeoVolume*> getStation2SensitiveVolumes(); -} // namespace mch -} // namespace o2 -#endif // O2_MCH_SIMULATION_STATION2GEOMETRY_H +} // namespace o2::mch +#endif diff --git a/Detectors/MUON/MCH/Simulation/src/Station345Geometry.cxx b/Detectors/MUON/MCH/Geometry/Creator/src/Station345Geometry.cxx similarity index 99% rename from Detectors/MUON/MCH/Simulation/src/Station345Geometry.cxx rename to Detectors/MUON/MCH/Geometry/Creator/src/Station345Geometry.cxx index 7460012f0c2d3..d672a32f66039 100644 --- a/Detectors/MUON/MCH/Simulation/src/Station345Geometry.cxx +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Station345Geometry.cxx @@ -411,11 +411,13 @@ void createSlats() // compute the LV cable length float cableHalfLength = (typeName.find('3') < typeName.size()) ? kSt45SupportHalfLength : kCh5SupportHalfLength; - if (typeName == "122200N") + if (typeName == "122200N") { cableHalfLength = kCh6SupportHalfLength; + } cableHalfLength -= halfLength; - if (typeName == "122330N") + if (typeName == "122330N") { cableHalfLength -= kGasLength / 2; + } float rightSpacerHalfHeight = kSlatPanelHalfHeight; if (isRounded(typeName)) { @@ -542,8 +544,9 @@ void createSlats() x = halfLength - kVertSpacerHalfLength; slat->AddNode(leftSpacer, 1, new TGeoTranslation(-(x - panelShift), 0., 0.)); // don't place a right spacer for S(N)R1 slat - if (typeName.back() != '1') + if (typeName.back() != '1') { slat->AddNode(rightSpacer, 1, new TGeoTranslation(x + panelShift, 0., 0.)); + } // place the LV cables (top and bottom) auto LVcable = gGeoManager->MakeBox(Form("%s LV cable", name), assertMedium(Medium::Copper), cableHalfLength + kVertSpacerHalfLength, kLVCableHalfHeight, kLVCableHalfThickness); @@ -644,8 +647,9 @@ void buildHalfChambers(TGeoVolume& topVolume) // loop over the objects (half-chambers) of the array for (const auto& halfCh : hChs.GetArray()) { // check that "halfCh" is an object - if (!halfCh.IsObject()) + if (!halfCh.IsObject()) { throw runtime_error("Can't create the half-chambers : wrong Value input"); + } int moduleID = halfCh["moduleID"].GetInt(); const string name = halfCh["name"].GetString(); @@ -657,15 +661,17 @@ void buildHalfChambers(TGeoVolume& topVolume) // place the support panel corresponding to the chamber number auto supRot = new TGeoRotation(); - if (moduleID % 2) + if (moduleID % 2) { supRot->RotateY(180.); + } halfChVol->AddNode(gGeoManager->GetVolume(Form("Chamber %d support panel", nCh)), moduleID, supRot); // place the slat volumes on the different nodes of the half-chamber for (const auto& slat : halfCh["nodes"].GetArray()) { // check that "slat" is an object - if (!slat.IsObject()) + if (!slat.IsObject()) { throw runtime_error("Can't create the slat : wrong Value input"); + } int detID = slat["detID"].GetInt(); diff --git a/Detectors/MUON/MCH/Simulation/src/Station345Geometry.h b/Detectors/MUON/MCH/Geometry/Creator/src/Station345Geometry.h similarity index 83% rename from Detectors/MUON/MCH/Simulation/src/Station345Geometry.h rename to Detectors/MUON/MCH/Geometry/Creator/src/Station345Geometry.h index f538dd240d4ce..9e25c100f796b 100644 --- a/Detectors/MUON/MCH/Simulation/src/Station345Geometry.h +++ b/Detectors/MUON/MCH/Geometry/Creator/src/Station345Geometry.h @@ -13,22 +13,18 @@ /// \author Florian Damas <florian.damas@cern.ch> /// \date 22 march 2018 -#ifndef O2_MCH_SIMULATION_STATION345GEOMETRY_H -#define O2_MCH_SIMULATION_STATION345GEOMETRY_H +#ifndef O2_MCH_GEOMETRY_CREATOR_STATION345GEOMETRY_H +#define O2_MCH_GEOMETRY_CREATOR_STATION345GEOMETRY_H #include <vector> class TGeoVolume; -namespace o2 +namespace o2::mch { -namespace mch -{ - void createStation345Geometry(TGeoVolume& topVolume); std::vector<TGeoVolume*> getStation345SensitiveVolumes(); -} // namespace mch -} // namespace o2 +} // namespace o2::mch #endif diff --git a/Detectors/MUON/MCH/Geometry/README.md b/Detectors/MUON/MCH/Geometry/README.md new file mode 100644 index 0000000000000..7ceb822f51c85 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/README.md @@ -0,0 +1,15 @@ +<!-- doxy +\page refDetectorsMUONMCHGeometry Geometry +/doxy --> + +# MUON MCH Geometry + +The Geometry package is split in two parts : the [creation part](./Creator) (to +be used only for simulation) and the [transformation part](./Transformer) (to +be used from both simulation and reconstruction). + +<!-- doxy +\subpage refDetectorsMUONMCHGeometryTransformer +\subpage refDetectorsMUONMCHGeometryCreator +\subpage refDetectorsMUONMCHGeometryTest +/doxy --> diff --git a/Detectors/MUON/MCH/Geometry/Test/CMakeLists.txt b/Detectors/MUON/MCH/Geometry/Test/CMakeLists.txt new file mode 100644 index 0000000000000..b654993ec641a --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + MCHGeometryTest + SOURCES Helpers.cxx + PUBLIC_LINK_LIBRARIES O2::MCHGeometryCreator) + +o2_target_root_dictionary(MCHGeometryTest HEADERS include/MCHGeometryTest/Helpers.h LINKDEF LinkDef.h) + +o2_add_test( + geometry-creator + COMPONENT_NAME mch + SOURCES testGeometryCreator.cxx + PUBLIC_LINK_LIBRARIES O2::MCHGeometryTest + LABELS muon mch) + +o2_add_test( + geometry-transformer NAME mch/geometry-transformer + COMPONENT_NAME mch + SOURCES testGeometryTransformer.cxx + PUBLIC_LINK_LIBRARIES O2::MCHGeometryTest + COMMAND_LINE_ARGS ${CMAKE_CURRENT_LIST_DIR}/ideal-geometry-o2.json + LABELS muon mch) + +o2_add_test( + geometry-transformer-legacy NAME mch/geometry-transformer-legacy + COMPONENT_NAME mch + SOURCES testGeometryTransformer.cxx + PUBLIC_LINK_LIBRARIES O2::MCHGeometryTest + COMMAND_LINE_ARGS ${CMAKE_CURRENT_LIST_DIR}/ideal-geometry-aliroot.json + LABELS muon mch) + +o2_add_test_root_macro( + drawMCHGeometry.C + PUBLIC_LINK_LIBRARIES O2::MCHGeometryCreator O2::MathUtils + LABELS muon mch) diff --git a/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx b/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx new file mode 100644 index 0000000000000..d5a3530be4be9 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/Helpers.cxx @@ -0,0 +1,241 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/GeometryManager.h" +#include "DetectorsBase/MaterialManager.h" +#include "MCHGeometryCreator/Geometry.h" +#include "MCHGeometryTest/Helpers.h" +#include "MCHGeometryTransformer/Transformations.h" +#include "Math/GenVector/Cartesian3D.h" +#include "TGLRnrCtx.h" +#include "TGLViewer.h" +#include "TGeoManager.h" +#include "TGeoVolume.h" +#include "TH2F.h" +#include "TPRegexp.h" +#include "TVirtualPad.h" +#include <iostream> + +namespace o2 +{ +namespace mch +{ +namespace test +{ + +TGeoVolume* createAirVacuumCave(const char* name) +{ + // create the air medium (only used for the geometry test) + auto& mgr = o2::base::MaterialManager::Instance(); + + const int nAir = 4; + Float_t aAir[nAir] = {12.0107, 14.0067, 15.9994, 39.948}; + Float_t zAir[nAir] = {6., 7., 8., 18.}; + Float_t wAir[nAir] = {0.000124, 0.755267, 0.231781, 0.012827}; + Float_t dAirVacuum = 1.20479E-10; + const int kID = 90; // to avoid conflicts with definitions of other MCH materials + + mgr.Mixture("MCH", kID, "Air", aAir, zAir, dAirVacuum, nAir, wAir); + mgr.Medium("MCH", kID, "Air", kID, + false, /* isvol */ + 0, /* ifield */ + -1.0, /* fieldm */ + -1.0, /* tmaxfd */ + -1.0, /* stemax */ + -1.0, /* deemax */ + -1.0, /* epsil */ + -1.0 /* stmin */); + return gGeoManager->MakeBox(name, gGeoManager->GetMedium("MCH_Air"), 2000.0, 2000.0, 3000.0); +} + +void dump(std::ostream& out, const TGeoNode& n, int level, int maxdepth, std::string prefix) +{ + if (level >= maxdepth) { + return; + } + + if (level == 0) { + out << n.GetName() << "\n"; + } + + if (level < maxdepth) { + for (int i = 0; i < n.GetNdaughters(); i++) { + TGeoNode* d = n.GetDaughter(i); + if (i == n.GetNdaughters() - 1) { + out << prefix + "└──" << d->GetName() + << "\n"; + dump(out, *d, level + 1, maxdepth, prefix + " "); + } else { + out << prefix + "├──" << d->GetName() + << "\n"; + dump(out, *d, level + 1, maxdepth, prefix + "│ "); + } + } + } +} + +void showGeometryAsTextTree(const char* fromPath, int maxdepth, std::ostream& out) +{ + if (!gGeoManager) { + return; + } + + TGeoNavigator* nav = gGeoManager->GetCurrentNavigator(); + + if (strlen(fromPath)) { + if (!nav->cd(fromPath)) { + std::cerr << "Could not get path " << fromPath << "\n"; + return; + } + } + + TGeoNode* node = nav->GetCurrentNode(); + + dump(out, *node, 0, maxdepth, ""); +} + +void createStandaloneGeometry() +{ + if (gGeoManager && gGeoManager->GetTopVolume()) { + std::cerr << "Can only call this function with an empty geometry, i.e. gGeoManager==nullptr " + << " or gGeoManager->GetTopVolume()==nullptr\n"; + } + TGeoManager* g = new TGeoManager("MCH-ONLY", "ALICE MCH Standalone Geometry"); + TGeoVolume* top = createAirVacuumCave("cave"); + g->SetTopVolume(top); + o2::mch::geo::createGeometry(*g, *top); +} + +void setVolumeVisibility(const char* pattern, bool visible, bool visibleDaughters) +{ + TPRegexp re(pattern); + TIter next(gGeoManager->GetListOfVolumes()); + TGeoVolume* vol; + + while ((vol = static_cast<TGeoVolume*>(next()))) { + if (TString(vol->GetName()).Contains(re)) { + vol->SetVisibility(visible); + vol->SetVisDaughters(visibleDaughters); + } + } +} + +void setVolumeColor(const char* pattern, int lineColor, int fillColor) +{ + TPRegexp re(pattern); + TIter next(gGeoManager->GetListOfVolumes()); + TGeoVolume* vol; + + while ((vol = static_cast<TGeoVolume*>(next()))) { + if (TString(vol->GetName()).Contains(re)) { + vol->SetFillColor(fillColor); + vol->SetLineColor(lineColor); + } + } +} + +void drawOptionPresetBasic() +{ + gGeoManager->SetVisLevel(4); + + setVolumeVisibility("cave", false, true); + + // Hide to half-chamber top volumes + setVolumeVisibility("^SC", false, true); + + // Hide St345 support panels + setVolumeVisibility("support panel", false, false); + + // Hide St345 LV wires + setVolumeVisibility(" LV ", false, false); + + // Make St345 carbon panels dark gray + setVolumeColor("panel carbon", kGray + 3); + + // Make St345 insulators dark green + setVolumeColor("insulator", kGreen + 3); + + // Hide most of St1 + setVolumeVisibility("SQ", false, true); + + // Only reveal gas module + setVolumeVisibility("SA", true, true); + setVolumeColor("SA", kCyan - 10); +} + +void drawGeometry() +{ + // minimal macro to test setup of the geometry + + createStandaloneGeometry(); + + drawOptionPresetBasic(); + + gGeoManager->GetTopVolume()->Draw("ogl"); + + TGLViewer* gl = static_cast<TGLViewer*>(gPad->GetViewer3D("ogl")); + TGLCamera& c = gl->CurrentCamera(); + + // gl->SetStyle(TGLRnrCtx::kWireFrame); + gl->SetStyle(TGLRnrCtx::kOutline); + // gl->SetStyle(TGLRnrCtx::kFill); +} + +o2::base::GeometryManager::MatBudgetExt getMatBudgetExt(const o2::math_utils::Transform3D& t, math_utils::Vector3D<double>& n, float x, float y, float thickness) +{ + math_utils::Point3D<double> point; + t.LocalToMaster(math_utils::Point3D<double>{x, y, 0}, point); + return o2::base::GeometryManager::meanMaterialBudgetExt(math_utils::Point3D<double>{point + n * thickness / 2.0}, math_utils::Point3D<double>{point - n * thickness / 2.0}); +} + +std::ostream& operator<<(std::ostream& os, o2::base::GeometryManager::MatBudgetExt m) +{ + os << "L=" << m.length << " <Rho>=" << m.meanRho << " <A>=" << m.meanA + << " <Z>=" << m.meanZ << " <x/x0>=" << m.meanX2X0 << " nCross=" << m.nCross; + return os; +} + +math_utils::Vector3D<double> getNormalVector(const o2::math_utils::Transform3D& t) +{ + math_utils::Point3D<double> px, py, po; + t.LocalToMaster(math_utils::Point3D<double>{0, 1, 0}, py); + t.LocalToMaster(math_utils::Point3D<double>{1, 0, 0}, px); + t.LocalToMaster(math_utils::Point3D<double>{0, 0, 0}, po); + math_utils::Vector3D<double> a{px - po}; + math_utils::Vector3D<double> b{py - po}; + return a.Cross(b).Unit(); +} + +TH2* getRadio(int detElemId, float xmin, float ymin, float xmax, float ymax, float xstep, float ystep, float thickness) +{ + if (xmin >= xmax || ymin >= ymax) { + std::cerr << "incorrect limits\n"; + return nullptr; + } + TH2* hmatb = new TH2F("hmatb", "hmatb", (int)((xmax - xmin) / xstep), xmin, xmax, (int)((ymax - ymin) / ystep), ymin, ymax); + + auto transformation = o2::mch::geo::transformationFromTGeoManager(*gGeoManager); + auto t = transformation(detElemId); + + auto normal = getNormalVector(t); + + for (auto x = xmin; x < xmax; x += xstep) { + for (auto y = ymin; y < ymax; y += ystep) { + auto matb = getMatBudgetExt(t, normal, x, y, thickness); + if (std::isfinite(matb.meanX2X0)) { + hmatb->Fill(x, y, matb.meanX2X0); + } + } + } + return hmatb; +} +} // namespace test +} // namespace mch +} // namespace o2 diff --git a/Detectors/MUON/MCH/Geometry/Test/LinkDef.h b/Detectors/MUON/MCH/Geometry/Test/LinkDef.h new file mode 100644 index 0000000000000..c51f6bec0eb82 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/LinkDef.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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++ namespace o2::mch::test; + +#pragma link C++ class o2::mch::test::Dummy; + +#pragma link C++ function o2::mch::test::createStandaloneGeometry; +#pragma link C++ function o2::mch::test::createRegularGeometry; +#pragma link C++ function o2::mch::test::drawGeometry; +#pragma link C++ function o2::mch::test::getRadio; +#pragma link C++ function o2::mch::test::showGeometryAsTextTree; +#pragma link C++ function o2::mch::test::setVolumeVisibility; +#pragma link C++ function o2::mch::test::setVolumeColor; + +#endif diff --git a/Detectors/MUON/MCH/Geometry/Test/README.md b/Detectors/MUON/MCH/Geometry/Test/README.md new file mode 100644 index 0000000000000..2ccf93b48c1fa --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/README.md @@ -0,0 +1,13 @@ +<!-- doxy +\page refDetectorsMUONMCHGeometryTest Test +/doxy --> + +# MCH Geometry Helpers + +The [MCHGeometryTest](./include/MCHGeometryTest/Helpers.h) library offers a few +utility functions to help debug the MCH geometry : [draw +it](./drawMCHGeometry.C), dump it as text, create a standalone version of it +(i.e. without other ALICE volumes) + +Also provides a `getRadio` function to get a radiation length plot of a given +detection element. diff --git a/Detectors/MUON/MCH/Geometry/Test/drawMCHGeometry.C b/Detectors/MUON/MCH/Geometry/Test/drawMCHGeometry.C new file mode 100644 index 0000000000000..80235f0bb4ff6 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/drawMCHGeometry.C @@ -0,0 +1,8 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "MCHGeometryTest/Helpers.h" +#endif + +void drawMCHGeometry() +{ + o2::mch::test::drawGeometry(); +} diff --git a/Detectors/MUON/MCH/Geometry/Test/ideal-geometry-aliroot.json b/Detectors/MUON/MCH/Geometry/Test/ideal-geometry-aliroot.json new file mode 100644 index 0000000000000..e12ccadb6d8cd --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/ideal-geometry-aliroot.json @@ -0,0 +1,2224 @@ +{ + "alignables": [ + { + "symname": "/MUON/GM0", + "transform": { + "tx": 0, + "ty": 0, + "tz": -526.1599731445312, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "symname": "/MUON/GM1", + "transform": { + "tx": 0, + "ty": 0, + "tz": -545.239990234375, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "symname": "/MUON/GM2", + "transform": { + "tx": 0, + "ty": 0, + "tz": -676.4000244140625, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "symname": "/MUON/GM3", + "transform": { + "tx": 0, + "ty": 0, + "tz": -695.4000244140625, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "symname": "/MUON/GM4", + "transform": { + "tx": 0, + "ty": -0.16628965477512675, + "tz": -967.4988477676281, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM5", + "transform": { + "tx": 0, + "ty": 0.16628965477512675, + "tz": -967.5011522323719, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM6", + "transform": { + "tx": 0, + "ty": -0.16628965477512675, + "tz": -998.4988477676281, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM7", + "transform": { + "tx": 0, + "ty": 0.16628965477512675, + "tz": -998.5011522323719, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM8", + "transform": { + "tx": 0, + "ty": -0.16628965477512675, + "tz": -1276.4988477676281, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM9", + "transform": { + "tx": 0, + "ty": 0.16628965477512675, + "tz": -1276.5011522323719, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM10", + "transform": { + "tx": 0, + "ty": -0.16628965477512675, + "tz": -1307.4988477676281, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM11", + "transform": { + "tx": 0, + "ty": 0.16628965477512675, + "tz": -1307.5011522323719, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM12", + "transform": { + "tx": 0, + "ty": -0.16628965477512675, + "tz": -1406.5988233535656, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM13", + "transform": { + "tx": 0, + "ty": 0.16628965477512675, + "tz": -1406.6011278183094, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM14", + "transform": { + "tx": 0, + "ty": -0.16628965477512675, + "tz": -1437.5988233535656, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "symname": "/MUON/GM15", + "transform": { + "tx": 0, + "ty": 0.16628965477512675, + "tz": -1437.6011278183094, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 100, + "symname": "/MUON/GM0/DE100", + "transform": { + "tx": 0.185, + "ty": -0.52, + "tz": -529.9099731445312, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 101, + "symname": "/MUON/GM0/DE101", + "transform": { + "tx": -0.185, + "ty": -0.52, + "tz": -522.4099731445312, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 102, + "symname": "/MUON/GM0/DE102", + "transform": { + "tx": -0.185, + "ty": 0.52, + "tz": -529.9099731445312, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 103, + "symname": "/MUON/GM0/DE103", + "transform": { + "tx": 0.185, + "ty": 0.52, + "tz": -522.4099731445312, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 200, + "symname": "/MUON/GM1/DE200", + "transform": { + "tx": 0.185, + "ty": -0.52, + "tz": -548.989990234375, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 201, + "symname": "/MUON/GM1/DE201", + "transform": { + "tx": -0.185, + "ty": -0.52, + "tz": -541.489990234375, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 202, + "symname": "/MUON/GM1/DE202", + "transform": { + "tx": -0.185, + "ty": 0.52, + "tz": -548.989990234375, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 203, + "symname": "/MUON/GM1/DE203", + "transform": { + "tx": 0.185, + "ty": 0.52, + "tz": -541.489990234375, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 300, + "symname": "/MUON/GM2/DE300", + "transform": { + "tx": 0, + "ty": 0, + "tz": -679.8000245094299, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 301, + "symname": "/MUON/GM2/DE301", + "transform": { + "tx": 0, + "ty": 0, + "tz": -673.0000243186951, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 302, + "symname": "/MUON/GM2/DE302", + "transform": { + "tx": 0, + "ty": 0, + "tz": -679.8000245094299, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 303, + "symname": "/MUON/GM2/DE303", + "transform": { + "tx": 0, + "ty": 0, + "tz": -673.0000243186951, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 400, + "symname": "/MUON/GM3/DE400", + "transform": { + "tx": 0, + "ty": 0, + "tz": -698.8000245094299, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 401, + "symname": "/MUON/GM3/DE401", + "transform": { + "tx": 0, + "ty": 0, + "tz": -692.0000243186951, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 402, + "symname": "/MUON/GM3/DE402", + "transform": { + "tx": 0, + "ty": 0, + "tz": -698.8000245094299, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": true + }, + { + "deid": 403, + "symname": "/MUON/GM3/DE403", + "transform": { + "tx": 0, + "ty": 0, + "tz": -692.0000243186951, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": true + }, + { + "deid": 500, + "symname": "/MUON/GM4/DE500", + "transform": { + "tx": 81.25, + "ty": -0.003464367807815144, + "tz": -955.7499759951589, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 501, + "symname": "/MUON/GM4/DE501", + "transform": { + "tx": 81.25, + "ty": 37.68204556750454, + "tz": -964.2730202422136, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 502, + "symname": "/MUON/GM4/DE502", + "transform": { + "tx": 81.25, + "ty": 75.48928617018588, + "tz": -956.7962150731191, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 503, + "symname": "/MUON/GM4/DE503", + "transform": { + "tx": 61.25, + "ty": 112.67484792951139, + "tz": -965.3123306374202, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 504, + "symname": "/MUON/GM4/DE504", + "transform": { + "tx": 41.25, + "ty": 146.48246879531905, + "tz": -957.7800955305386, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 505, + "symname": "/MUON/GM5/DE505", + "transform": { + "tx": -41.25, + "ty": 146.48939753093464, + "tz": -981.2801435402207, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 506, + "symname": "/MUON/GM5/DE506", + "transform": { + "tx": -61.25, + "ty": 112.90349620482719, + "tz": -972.8139149569315, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 507, + "symname": "/MUON/GM5/DE507", + "transform": { + "tx": -81.25, + "ty": 75.49621490580152, + "tz": -980.2962630828013, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 508, + "symname": "/MUON/GM5/DE508", + "transform": { + "tx": -81.25, + "ty": 37.91069384282034, + "tz": -971.7746045617249, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 509, + "symname": "/MUON/GM5/DE509", + "transform": { + "tx": -81.25, + "ty": 0.003464367807815144, + "tz": -979.2500240048411, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 510, + "symname": "/MUON/GM5/DE510", + "transform": { + "tx": -81.25, + "ty": -37.68204556750454, + "tz": -970.7269797577864, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 511, + "symname": "/MUON/GM5/DE511", + "transform": { + "tx": -81.25, + "ty": -75.48928617018588, + "tz": -978.2037849268809, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 512, + "symname": "/MUON/GM5/DE512", + "transform": { + "tx": -61.25, + "ty": -112.67484792951139, + "tz": -969.6876693625798, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 513, + "symname": "/MUON/GM5/DE513", + "transform": { + "tx": -41.25, + "ty": -146.48246879531905, + "tz": -977.2199044694614, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 514, + "symname": "/MUON/GM4/DE514", + "transform": { + "tx": 41.25, + "ty": -146.48939753093464, + "tz": -953.7198564597793, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 515, + "symname": "/MUON/GM4/DE515", + "transform": { + "tx": 61.25, + "ty": -112.90349620482719, + "tz": -962.1860850430685, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 516, + "symname": "/MUON/GM4/DE516", + "transform": { + "tx": 81.25, + "ty": -75.49621490580152, + "tz": -954.7037369171987, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 517, + "symname": "/MUON/GM4/DE517", + "transform": { + "tx": 81.25, + "ty": -37.91069384282034, + "tz": -963.2253954382751, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 600, + "symname": "/MUON/GM6/DE600", + "transform": { + "tx": 81.25, + "ty": -0.003464367807815144, + "tz": -986.7499759951589, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 601, + "symname": "/MUON/GM6/DE601", + "transform": { + "tx": 81.25, + "ty": 37.68204556750454, + "tz": -995.2730202422136, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 602, + "symname": "/MUON/GM6/DE602", + "transform": { + "tx": 81.25, + "ty": 75.48928617018588, + "tz": -987.7962150731191, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 603, + "symname": "/MUON/GM6/DE603", + "transform": { + "tx": 61.25, + "ty": 112.67484792951139, + "tz": -996.3123306374202, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 604, + "symname": "/MUON/GM6/DE604", + "transform": { + "tx": 41.25, + "ty": 146.48246879531905, + "tz": -988.7800955305386, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 605, + "symname": "/MUON/GM7/DE605", + "transform": { + "tx": -41.25, + "ty": 146.48939753093464, + "tz": -1012.2801435402207, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 606, + "symname": "/MUON/GM7/DE606", + "transform": { + "tx": -61.25, + "ty": 112.90349620482719, + "tz": -1003.8139149569315, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 607, + "symname": "/MUON/GM7/DE607", + "transform": { + "tx": -81.25, + "ty": 75.49621490580152, + "tz": -1011.2962630828013, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 608, + "symname": "/MUON/GM7/DE608", + "transform": { + "tx": -81.25, + "ty": 37.91069384282034, + "tz": -1002.7746045617249, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 609, + "symname": "/MUON/GM7/DE609", + "transform": { + "tx": -81.25, + "ty": 0.003464367807815144, + "tz": -1010.2500240048411, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 610, + "symname": "/MUON/GM7/DE610", + "transform": { + "tx": -81.25, + "ty": -37.68204556750454, + "tz": -1001.7269797577864, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 611, + "symname": "/MUON/GM7/DE611", + "transform": { + "tx": -81.25, + "ty": -75.48928617018588, + "tz": -1009.2037849268809, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 612, + "symname": "/MUON/GM7/DE612", + "transform": { + "tx": -61.25, + "ty": -112.67484792951139, + "tz": -1000.6876693625798, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 613, + "symname": "/MUON/GM7/DE613", + "transform": { + "tx": -41.25, + "ty": -146.48246879531905, + "tz": -1008.2199044694614, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 614, + "symname": "/MUON/GM6/DE614", + "transform": { + "tx": 41.25, + "ty": -146.48939753093464, + "tz": -984.7198564597793, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 615, + "symname": "/MUON/GM6/DE615", + "transform": { + "tx": 61.25, + "ty": -112.90349620482719, + "tz": -993.1860850430685, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 616, + "symname": "/MUON/GM6/DE616", + "transform": { + "tx": 81.25, + "ty": -75.49621490580152, + "tz": -985.7037369171987, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 617, + "symname": "/MUON/GM6/DE617", + "transform": { + "tx": 81.25, + "ty": -37.91069384282034, + "tz": -994.2253954382751, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 700, + "symname": "/MUON/GM8/DE700", + "transform": { + "tx": 140, + "ty": 0, + "tz": -1264.5, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 701, + "symname": "/MUON/GM8/DE701", + "transform": { + "tx": 121.25, + "ty": 38.07854431768339, + "tz": -1273.5285392470098, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 702, + "symname": "/MUON/GM8/DE702", + "transform": { + "tx": 101.25, + "ty": 72.5930350970798, + "tz": -1265.5060524959688, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 703, + "symname": "/MUON/GM8/DE703", + "transform": { + "tx": 101.25, + "ty": 109.07173075714752, + "tz": -1274.5124197572914, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 704, + "symname": "/MUON/GM8/DE704", + "transform": { + "tx": 81.25, + "ty": 138.48670131804144, + "tz": -1266.4192597655297, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 705, + "symname": "/MUON/GM8/DE705", + "transform": { + "tx": 61.25, + "ty": 175.36536009609583, + "tz": -1275.4311700364894, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 706, + "symname": "/MUON/GM8/DE706", + "transform": { + "tx": 41.25, + "ty": 204.08040855067148, + "tz": -1267.3283099628795, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 707, + "symname": "/MUON/GM9/DE707", + "transform": { + "tx": -41.25, + "ty": 204.08040855067148, + "tz": -1291.3283099628795, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 708, + "symname": "/MUON/GM9/DE708", + "transform": { + "tx": -61.25, + "ty": 175.60093710702725, + "tz": -1282.4328023656828, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 709, + "symname": "/MUON/GM9/DE709", + "transform": { + "tx": -81.25, + "ty": 138.48670131804144, + "tz": -1290.4192597655297, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 710, + "symname": "/MUON/GM9/DE710", + "transform": { + "tx": -101.25, + "ty": 109.30730776807894, + "tz": -1281.5140520864848, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 711, + "symname": "/MUON/GM9/DE711", + "transform": { + "tx": -101.25, + "ty": 72.5930350970798, + "tz": -1289.5060524959688, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 712, + "symname": "/MUON/GM9/DE712", + "transform": { + "tx": -121.25, + "ty": 38.314121328614824, + "tz": -1280.5301715762032, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 713, + "symname": "/MUON/GM9/DE713", + "transform": { + "tx": -140, + "ty": 0, + "tz": -1288.5, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 714, + "symname": "/MUON/GM9/DE714", + "transform": { + "tx": -121.25, + "ty": -38.07854431768339, + "tz": -1279.4714607529902, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 715, + "symname": "/MUON/GM9/DE715", + "transform": { + "tx": -101.25, + "ty": -72.5930350970798, + "tz": -1287.4939475040312, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 716, + "symname": "/MUON/GM9/DE716", + "transform": { + "tx": -101.25, + "ty": -109.07173075714752, + "tz": -1278.4875802427086, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 717, + "symname": "/MUON/GM9/DE717", + "transform": { + "tx": -81.25, + "ty": -138.48670131804144, + "tz": -1286.5807402344703, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 718, + "symname": "/MUON/GM9/DE718", + "transform": { + "tx": -61.25, + "ty": -175.36536009609583, + "tz": -1277.5688299635106, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 719, + "symname": "/MUON/GM9/DE719", + "transform": { + "tx": -41.25, + "ty": -204.08040855067148, + "tz": -1285.6716900371205, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 720, + "symname": "/MUON/GM8/DE720", + "transform": { + "tx": 41.25, + "ty": -204.08040855067148, + "tz": -1261.6716900371205, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 721, + "symname": "/MUON/GM8/DE721", + "transform": { + "tx": 61.25, + "ty": -175.60093710702725, + "tz": -1270.5671976343172, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 722, + "symname": "/MUON/GM8/DE722", + "transform": { + "tx": 81.25, + "ty": -138.48670131804144, + "tz": -1262.5807402344703, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 723, + "symname": "/MUON/GM8/DE723", + "transform": { + "tx": 101.25, + "ty": -109.30730776807894, + "tz": -1271.4859479135152, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 724, + "symname": "/MUON/GM8/DE724", + "transform": { + "tx": 101.25, + "ty": -72.5930350970798, + "tz": -1263.4939475040312, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 725, + "symname": "/MUON/GM8/DE725", + "transform": { + "tx": 121.25, + "ty": -38.314121328614824, + "tz": -1272.4698284237968, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 800, + "symname": "/MUON/GM10/DE800", + "transform": { + "tx": 140, + "ty": 0, + "tz": -1295.5, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 801, + "symname": "/MUON/GM10/DE801", + "transform": { + "tx": 121.25, + "ty": 38.07854431768339, + "tz": -1304.5285392470098, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 802, + "symname": "/MUON/GM10/DE802", + "transform": { + "tx": 101.25, + "ty": 76.04270077880811, + "tz": -1296.553860729427, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 803, + "symname": "/MUON/GM10/DE803", + "transform": { + "tx": 101.25, + "ty": 113.4713097976769, + "tz": -1305.5733926518537, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 804, + "symname": "/MUON/GM10/DE804", + "transform": { + "tx": 81.25, + "ty": 142.986269230902, + "tz": -1297.4816183860703, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 805, + "symname": "/MUON/GM10/DE805", + "transform": { + "tx": 61.25, + "ty": 179.86492800895635, + "tz": -1306.49352865703, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 806, + "symname": "/MUON/GM10/DE806", + "transform": { + "tx": 41.25, + "ty": 208.57997646353203, + "tz": -1298.3906685834202, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 807, + "symname": "/MUON/GM11/DE807", + "transform": { + "tx": -41.25, + "ty": 208.57997646353203, + "tz": -1322.3906685834202, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 808, + "symname": "/MUON/GM11/DE808", + "transform": { + "tx": -61.25, + "ty": 180.10050501988778, + "tz": -1313.4951609862237, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 809, + "symname": "/MUON/GM11/DE809", + "transform": { + "tx": -81.25, + "ty": 142.986269230902, + "tz": -1321.4816183860703, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 810, + "symname": "/MUON/GM11/DE810", + "transform": { + "tx": -101.25, + "ty": 113.70688680860832, + "tz": -1312.5750249810471, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 811, + "symname": "/MUON/GM11/DE811", + "transform": { + "tx": -101.25, + "ty": 76.04270077880811, + "tz": -1320.553860729427, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 812, + "symname": "/MUON/GM11/DE812", + "transform": { + "tx": -121.25, + "ty": 38.314121328614824, + "tz": -1311.5301715762032, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 813, + "symname": "/MUON/GM11/DE813", + "transform": { + "tx": -140, + "ty": 0, + "tz": -1319.5, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 814, + "symname": "/MUON/GM11/DE814", + "transform": { + "tx": -121.25, + "ty": -38.07854431768339, + "tz": -1310.4714607529902, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 815, + "symname": "/MUON/GM11/DE815", + "transform": { + "tx": -101.25, + "ty": -76.04270077880811, + "tz": -1318.446139270573, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 816, + "symname": "/MUON/GM11/DE816", + "transform": { + "tx": -101.25, + "ty": -113.4713097976769, + "tz": -1309.4266073481463, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 817, + "symname": "/MUON/GM11/DE817", + "transform": { + "tx": -81.25, + "ty": -142.986269230902, + "tz": -1317.5183816139297, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 818, + "symname": "/MUON/GM11/DE818", + "transform": { + "tx": -61.25, + "ty": -179.86492800895635, + "tz": -1308.50647134297, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 819, + "symname": "/MUON/GM11/DE819", + "transform": { + "tx": -41.25, + "ty": -208.57997646353203, + "tz": -1316.6093314165798, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 820, + "symname": "/MUON/GM10/DE820", + "transform": { + "tx": 41.25, + "ty": -208.57997646353203, + "tz": -1292.6093314165798, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 821, + "symname": "/MUON/GM10/DE821", + "transform": { + "tx": 61.25, + "ty": -180.10050501988778, + "tz": -1301.5048390137763, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 822, + "symname": "/MUON/GM10/DE822", + "transform": { + "tx": 81.25, + "ty": -142.986269230902, + "tz": -1293.5183816139297, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 823, + "symname": "/MUON/GM10/DE823", + "transform": { + "tx": 101.25, + "ty": -113.70688680860832, + "tz": -1302.4249750189529, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 824, + "symname": "/MUON/GM10/DE824", + "transform": { + "tx": 101.25, + "ty": -76.04270077880811, + "tz": -1294.446139270573, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 825, + "symname": "/MUON/GM10/DE825", + "transform": { + "tx": 121.25, + "ty": -38.314121328614824, + "tz": -1303.4698284237968, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 900, + "symname": "/MUON/GM12/DE900", + "transform": { + "tx": 140, + "ty": 0, + "tz": -1394.5999755859375, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 901, + "symname": "/MUON/GM12/DE901", + "transform": { + "tx": 121.25, + "ty": 38.07854431768339, + "tz": -1403.6285148329473, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 902, + "symname": "/MUON/GM12/DE902", + "transform": { + "tx": 121.25, + "ty": 76.09269902930468, + "tz": -1395.6545292312157, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 903, + "symname": "/MUON/GM12/DE903", + "transform": { + "tx": 121.25, + "ty": 113.57129867000808, + "tz": -1404.6747539637695, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 904, + "symname": "/MUON/GM12/DE904", + "transform": { + "tx": 101.25, + "ty": 150.9855010759874, + "tz": -1396.692453741858, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 905, + "symname": "/MUON/GM12/DE905", + "transform": { + "tx": 81.25, + "ty": 187.91415810453836, + "tz": -1405.705056928669, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 906, + "symname": "/MUON/GM12/DE906", + "transform": { + "tx": 61.25, + "ty": 224.77841789836523, + "tz": -1397.7151351610146, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 907, + "symname": "/MUON/GM13/DE907", + "transform": { + "tx": -61.25, + "ty": 224.77841789836523, + "tz": -1421.7151351610146, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 908, + "symname": "/MUON/GM13/DE908", + "transform": { + "tx": -81.25, + "ty": 188.14973511546978, + "tz": -1412.7066892578623, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 909, + "symname": "/MUON/GM13/DE909", + "transform": { + "tx": -101.25, + "ty": 150.9855010759874, + "tz": -1420.692453741858, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 910, + "symname": "/MUON/GM13/DE910", + "transform": { + "tx": -121.25, + "ty": 113.8068756809395, + "tz": -1411.6763862929631, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 911, + "symname": "/MUON/GM13/DE911", + "transform": { + "tx": -121.25, + "ty": 76.09269902930468, + "tz": -1419.6545292312157, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 912, + "symname": "/MUON/GM13/DE912", + "transform": { + "tx": -121.25, + "ty": 38.314121328614824, + "tz": -1410.6301471621407, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 913, + "symname": "/MUON/GM13/DE913", + "transform": { + "tx": -140, + "ty": 0, + "tz": -1418.5999755859375, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 914, + "symname": "/MUON/GM13/DE914", + "transform": { + "tx": -121.25, + "ty": -38.07854431768339, + "tz": -1409.5714363389277, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 915, + "symname": "/MUON/GM13/DE915", + "transform": { + "tx": -121.25, + "ty": -76.09269902930468, + "tz": -1417.5454219406593, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 916, + "symname": "/MUON/GM13/DE916", + "transform": { + "tx": -121.25, + "ty": -113.57129867000808, + "tz": -1408.5251972081055, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 917, + "symname": "/MUON/GM13/DE917", + "transform": { + "tx": -101.25, + "ty": -150.9855010759874, + "tz": -1416.507497430017, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 918, + "symname": "/MUON/GM13/DE918", + "transform": { + "tx": -81.25, + "ty": -187.91415810453836, + "tz": -1407.494894243206, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 919, + "symname": "/MUON/GM13/DE919", + "transform": { + "tx": -61.25, + "ty": -224.77841789836523, + "tz": -1415.4848160108604, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 920, + "symname": "/MUON/GM12/DE920", + "transform": { + "tx": 61.25, + "ty": -224.77841789836523, + "tz": -1391.4848160108604, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 921, + "symname": "/MUON/GM12/DE921", + "transform": { + "tx": 81.25, + "ty": -188.14973511546978, + "tz": -1400.4932619140127, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 922, + "symname": "/MUON/GM12/DE922", + "transform": { + "tx": 101.25, + "ty": -150.9855010759874, + "tz": -1392.507497430017, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 923, + "symname": "/MUON/GM12/DE923", + "transform": { + "tx": 121.25, + "ty": -113.8068756809395, + "tz": -1401.5235648789119, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 924, + "symname": "/MUON/GM12/DE924", + "transform": { + "tx": 121.25, + "ty": -76.09269902930468, + "tz": -1393.5454219406593, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 925, + "symname": "/MUON/GM12/DE925", + "transform": { + "tx": 121.25, + "ty": -38.314121328614824, + "tz": -1402.5698040097343, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1000, + "symname": "/MUON/GM14/DE1000", + "transform": { + "tx": 140, + "ty": 0, + "tz": -1425.5999755859375, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1001, + "symname": "/MUON/GM14/DE1001", + "transform": { + "tx": 121.25, + "ty": 38.07854431768339, + "tz": -1434.6285148329473, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1002, + "symname": "/MUON/GM14/DE1002", + "transform": { + "tx": 121.25, + "ty": 76.09269902930468, + "tz": -1426.6545292312157, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1003, + "symname": "/MUON/GM14/DE1003", + "transform": { + "tx": 121.25, + "ty": 113.57129867000808, + "tz": -1435.6747539637695, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1004, + "symname": "/MUON/GM14/DE1004", + "transform": { + "tx": 101.25, + "ty": 150.9855010759874, + "tz": -1427.692453741858, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1005, + "symname": "/MUON/GM14/DE1005", + "transform": { + "tx": 81.25, + "ty": 187.91415810453836, + "tz": -1436.705056928669, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1006, + "symname": "/MUON/GM14/DE1006", + "transform": { + "tx": 61.25, + "ty": 224.77841789836523, + "tz": -1428.7151351610146, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1007, + "symname": "/MUON/GM15/DE1007", + "transform": { + "tx": -61.25, + "ty": 224.77841789836523, + "tz": -1452.7151351610146, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1008, + "symname": "/MUON/GM15/DE1008", + "transform": { + "tx": -81.25, + "ty": 188.14973511546978, + "tz": -1443.7066892578623, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1009, + "symname": "/MUON/GM15/DE1009", + "transform": { + "tx": -101.25, + "ty": 150.9855010759874, + "tz": -1451.692453741858, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1010, + "symname": "/MUON/GM15/DE1010", + "transform": { + "tx": -121.25, + "ty": 113.8068756809395, + "tz": -1442.6763862929631, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1011, + "symname": "/MUON/GM15/DE1011", + "transform": { + "tx": -121.25, + "ty": 76.09269902930468, + "tz": -1450.6545292312157, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1012, + "symname": "/MUON/GM15/DE1012", + "transform": { + "tx": -121.25, + "ty": 38.314121328614824, + "tz": -1441.6301471621407, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1013, + "symname": "/MUON/GM15/DE1013", + "transform": { + "tx": -140, + "ty": 0, + "tz": -1449.5999755859375, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1014, + "symname": "/MUON/GM15/DE1014", + "transform": { + "tx": -121.25, + "ty": -38.07854431768339, + "tz": -1440.5714363389277, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1015, + "symname": "/MUON/GM15/DE1015", + "transform": { + "tx": -121.25, + "ty": -76.09269902930468, + "tz": -1448.5454219406593, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1016, + "symname": "/MUON/GM15/DE1016", + "transform": { + "tx": -121.25, + "ty": -113.57129867000808, + "tz": -1439.5251972081055, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1017, + "symname": "/MUON/GM15/DE1017", + "transform": { + "tx": -101.25, + "ty": -150.9855010759874, + "tz": -1447.507497430017, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1018, + "symname": "/MUON/GM15/DE1018", + "transform": { + "tx": -81.25, + "ty": -187.91415810453836, + "tz": -1438.494894243206, + "yaw": -180, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1019, + "symname": "/MUON/GM15/DE1019", + "transform": { + "tx": -61.25, + "ty": -224.77841789836523, + "tz": -1446.4848160108604, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1020, + "symname": "/MUON/GM14/DE1020", + "transform": { + "tx": 61.25, + "ty": -224.77841789836523, + "tz": -1422.4848160108604, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1021, + "symname": "/MUON/GM14/DE1021", + "transform": { + "tx": 81.25, + "ty": -188.14973511546978, + "tz": -1431.4932619140127, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1022, + "symname": "/MUON/GM14/DE1022", + "transform": { + "tx": 101.25, + "ty": -150.9855010759874, + "tz": -1423.507497430017, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1023, + "symname": "/MUON/GM14/DE1023", + "transform": { + "tx": 121.25, + "ty": -113.8068756809395, + "tz": -1432.5235648789119, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + }, + { + "deid": 1024, + "symname": "/MUON/GM14/DE1024", + "transform": { + "tx": 121.25, + "ty": -76.09269902930468, + "tz": -1424.5454219406593, + "yaw": -0, + "pitch": 0, + "roll": 179.2059999704361 + }, + "aligned": true + }, + { + "deid": 1025, + "symname": "/MUON/GM14/DE1025", + "transform": { + "tx": 121.25, + "ty": -38.314121328614824, + "tz": -1433.5698040097343, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000295639039 + }, + "aligned": true + } + ] +} diff --git a/Detectors/MUON/MCH/Geometry/Test/ideal-geometry-o2.json b/Detectors/MUON/MCH/Geometry/Test/ideal-geometry-o2.json new file mode 100644 index 0000000000000..a385539ed7e07 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/ideal-geometry-o2.json @@ -0,0 +1,2272 @@ +{ + "alignables": [ + { + "symname": "MCH/HC0", + "transform": { + "tx": 0, + "ty": 0, + "tz": -526.1599731445312, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC1", + "transform": { + "tx": 0, + "ty": 0, + "tz": -526.1599731445312, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC2", + "transform": { + "tx": 0, + "ty": 0, + "tz": -545.239990234375, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC3", + "transform": { + "tx": 0, + "ty": 0, + "tz": -545.239990234375, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC4", + "transform": { + "tx": 0, + "ty": 0, + "tz": -676.4, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC5", + "transform": { + "tx": 0, + "ty": 0, + "tz": -676.4, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC6", + "transform": { + "tx": 0, + "ty": 0, + "tz": -695.4, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC7", + "transform": { + "tx": 0, + "ty": 0, + "tz": -695.4, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "symname": "MCH/HC8", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -959.75, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC9", + "transform": { + "tx": 0, + "ty": 0.1074, + "tz": -975.25, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC10", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -990.75, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC11", + "transform": { + "tx": 0, + "ty": 0.1074, + "tz": -1006.25, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC12", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1259.75, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC13", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1284.25, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC14", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1299.75, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC15", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1315.25, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC16", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1398.85, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC17", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1414.35, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC18", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1429.85, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "symname": "MCH/HC19", + "transform": { + "tx": 0, + "ty": -0.1074, + "tz": -1445.35, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 100, + "symname": "MCH/HC0/DE100", + "transform": { + "tx": 0.1850000023841858, + "ty": -0.5199999809265137, + "tz": -529.9099731445312, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 101, + "symname": "MCH/HC1/DE101", + "transform": { + "tx": -0.1850000023841858, + "ty": -0.5199999809265137, + "tz": -522.4099731445312, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 102, + "symname": "MCH/HC1/DE102", + "transform": { + "tx": -0.1850000023841858, + "ty": 0.5199999809265137, + "tz": -529.9099731445312, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 103, + "symname": "MCH/HC0/DE103", + "transform": { + "tx": 0.1850000023841858, + "ty": 0.5199999809265137, + "tz": -522.4099731445312, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 200, + "symname": "MCH/HC2/DE200", + "transform": { + "tx": 0.1850000023841858, + "ty": -0.5199999809265137, + "tz": -548.989990234375, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 201, + "symname": "MCH/HC3/DE201", + "transform": { + "tx": -0.1850000023841858, + "ty": -0.5199999809265137, + "tz": -541.489990234375, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 202, + "symname": "MCH/HC3/DE202", + "transform": { + "tx": -0.1850000023841858, + "ty": 0.5199999809265137, + "tz": -548.989990234375, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 203, + "symname": "MCH/HC2/DE203", + "transform": { + "tx": 0.1850000023841858, + "ty": 0.5199999809265137, + "tz": -541.489990234375, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 300, + "symname": "MCH/HC4/DE300", + "transform": { + "tx": 0, + "ty": 0, + "tz": -680.3, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 301, + "symname": "MCH/HC5/DE301", + "transform": { + "tx": 0, + "ty": 0, + "tz": -672.5, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 302, + "symname": "MCH/HC5/DE302", + "transform": { + "tx": 0, + "ty": 0, + "tz": -680.3, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 303, + "symname": "MCH/HC4/DE303", + "transform": { + "tx": 0, + "ty": 0, + "tz": -672.5, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 400, + "symname": "MCH/HC6/DE400", + "transform": { + "tx": 0, + "ty": 0, + "tz": -699.3, + "yaw": -0, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 401, + "symname": "MCH/HC7/DE401", + "transform": { + "tx": 0, + "ty": 0, + "tz": -691.5, + "yaw": -180, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 402, + "symname": "MCH/HC7/DE402", + "transform": { + "tx": 0, + "ty": 0, + "tz": -699.3, + "yaw": -180, + "pitch": 0, + "roll": -0 + }, + "aligned": false + }, + { + "deid": 403, + "symname": "MCH/HC6/DE403", + "transform": { + "tx": 0, + "ty": 0, + "tz": -691.5, + "yaw": -0, + "pitch": 0, + "roll": -180 + }, + "aligned": false + }, + { + "deid": 500, + "symname": "MCH/HC8/DE500", + "transform": { + "tx": 81.25, + "ty": -0.05197011713870941, + "tz": -955.7503840774286, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 501, + "symname": "MCH/HC8/DE501", + "transform": { + "tx": 81.25, + "ty": 37.63354058543762, + "tz": -964.2734283156105, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 502, + "symname": "MCH/HC8/DE502", + "transform": { + "tx": 81.25, + "ty": 75.44078042139485, + "tz": -956.7966231164355, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 503, + "symname": "MCH/HC8/DE503", + "transform": { + "tx": 61.25, + "ty": 112.62633913364976, + "tz": -965.3127386192597, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 504, + "symname": "MCH/HC8/DE504", + "transform": { + "tx": 41.25, + "ty": 146.43396304703566, + "tz": -957.7805035372235, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 505, + "symname": "MCH/HC9/DE505", + "transform": { + "tx": -41.25, + "ty": 146.5379032813131, + "tz": -981.2797353823661, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 506, + "symname": "MCH/HC9/DE506", + "transform": { + "tx": -61.25, + "ty": 112.95199889937234, + "tz": -972.8135067741171, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 507, + "symname": "MCH/HC9/DE507", + "transform": { + "tx": -81.25, + "ty": 75.54472065567226, + "tz": -980.2958549615781, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 508, + "symname": "MCH/HC9/DE508", + "transform": { + "tx": -81.25, + "ty": 37.9592003511602, + "tz": -971.7741964704679, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 509, + "symname": "MCH/HC9/DE509", + "transform": { + "tx": -81.25, + "ty": 0.05197011713870941, + "tz": -979.2496159225714, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 510, + "symname": "MCH/HC9/DE510", + "transform": { + "tx": -81.25, + "ty": -37.63354058543762, + "tz": -970.7265716843895, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 511, + "symname": "MCH/HC9/DE511", + "transform": { + "tx": -81.25, + "ty": -75.44078042139485, + "tz": -978.2033768835645, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 512, + "symname": "MCH/HC9/DE512", + "transform": { + "tx": -61.25, + "ty": -112.62633913364976, + "tz": -969.6872613807403, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 513, + "symname": "MCH/HC9/DE513", + "transform": { + "tx": -41.25, + "ty": -146.43396304703566, + "tz": -977.2194964627765, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 514, + "symname": "MCH/HC8/DE514", + "transform": { + "tx": 41.25, + "ty": -146.5379032813131, + "tz": -953.7202646176339, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 515, + "symname": "MCH/HC8/DE515", + "transform": { + "tx": 61.25, + "ty": -112.95199889937234, + "tz": -962.1864932258829, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 516, + "symname": "MCH/HC8/DE516", + "transform": { + "tx": 81.25, + "ty": -75.54472065567226, + "tz": -954.7041450384219, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 517, + "symname": "MCH/HC8/DE517", + "transform": { + "tx": 81.25, + "ty": -37.9592003511602, + "tz": -963.2258035295321, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 600, + "symname": "MCH/HC10/DE600", + "transform": { + "tx": 81.25, + "ty": -0.05197011713870941, + "tz": -986.7503840774286, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 601, + "symname": "MCH/HC10/DE601", + "transform": { + "tx": 81.25, + "ty": 37.63354058543762, + "tz": -995.2734283156105, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 602, + "symname": "MCH/HC10/DE602", + "transform": { + "tx": 81.25, + "ty": 75.44078042139485, + "tz": -987.7966231164355, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 603, + "symname": "MCH/HC10/DE603", + "transform": { + "tx": 61.25, + "ty": 112.62633913364976, + "tz": -996.3127386192597, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 604, + "symname": "MCH/HC10/DE604", + "transform": { + "tx": 41.25, + "ty": 146.43396304703566, + "tz": -988.7805035372235, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 605, + "symname": "MCH/HC11/DE605", + "transform": { + "tx": -41.25, + "ty": 146.5379032813131, + "tz": -1012.2797353823661, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 606, + "symname": "MCH/HC11/DE606", + "transform": { + "tx": -61.25, + "ty": 112.95199889937234, + "tz": -1003.8135067741171, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 607, + "symname": "MCH/HC11/DE607", + "transform": { + "tx": -81.25, + "ty": 75.54472065567226, + "tz": -1011.2958549615781, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 608, + "symname": "MCH/HC11/DE608", + "transform": { + "tx": -81.25, + "ty": 37.9592003511602, + "tz": -1002.7741964704679, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 609, + "symname": "MCH/HC11/DE609", + "transform": { + "tx": -81.25, + "ty": 0.05197011713870941, + "tz": -1010.2496159225714, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 610, + "symname": "MCH/HC11/DE610", + "transform": { + "tx": -81.25, + "ty": -37.63354058543762, + "tz": -1001.7265716843895, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 611, + "symname": "MCH/HC11/DE611", + "transform": { + "tx": -81.25, + "ty": -75.44078042139485, + "tz": -1009.2033768835645, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 612, + "symname": "MCH/HC11/DE612", + "transform": { + "tx": -61.25, + "ty": -112.62633913364976, + "tz": -1000.6872613807403, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 613, + "symname": "MCH/HC11/DE613", + "transform": { + "tx": -41.25, + "ty": -146.43396304703566, + "tz": -1008.2194964627765, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 614, + "symname": "MCH/HC10/DE614", + "transform": { + "tx": 41.25, + "ty": -146.5379032813131, + "tz": -984.7202646176339, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 615, + "symname": "MCH/HC10/DE615", + "transform": { + "tx": 61.25, + "ty": -112.95199889937234, + "tz": -993.1864932258829, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 616, + "symname": "MCH/HC10/DE616", + "transform": { + "tx": 81.25, + "ty": -75.54472065567226, + "tz": -985.7041450384219, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 617, + "symname": "MCH/HC10/DE617", + "transform": { + "tx": 81.25, + "ty": -37.9592003511602, + "tz": -994.2258035295321, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 700, + "symname": "MCH/HC12/DE700", + "transform": { + "tx": 140, + "ty": -0.04850574945987875, + "tz": -1255.500408082268, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 701, + "symname": "MCH/HC12/DE701", + "transform": { + "tx": 121.25, + "ty": 38.03003781001593, + "tz": -1264.5289472990573, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 702, + "symname": "MCH/HC12/DE702", + "transform": { + "tx": 101.25, + "ty": 72.54452324520946, + "tz": -1256.5064604562003, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 703, + "symname": "MCH/HC12/DE703", + "transform": { + "tx": 101.25, + "ty": 109.02322043565675, + "tz": -1265.5128277198453, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 704, + "symname": "MCH/HC12/DE704", + "transform": { + "tx": 81.25, + "ty": 138.43819556957186, + "tz": -1257.4196677763402, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 705, + "symname": "MCH/HC12/DE705", + "transform": { + "tx": 61.25, + "ty": 175.31685435227624, + "tz": -1266.4315780282711, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 706, + "symname": "MCH/HC12/DE706", + "transform": { + "tx": 41.25, + "ty": 204.43185829199854, + "tz": -1258.3342608435514, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 707, + "symname": "MCH/HC13/DE707", + "transform": { + "tx": -41.25, + "ty": 204.31406979091827, + "tz": -1291.3334446790154, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 708, + "symname": "MCH/HC13/DE708", + "transform": { + "tx": -61.25, + "ty": 175.4346428533565, + "tz": -1282.4323941928071, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 709, + "symname": "MCH/HC13/DE709", + "transform": { + "tx": -81.25, + "ty": 138.3204070684916, + "tz": -1290.4188516118043, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 710, + "symname": "MCH/HC13/DE710", + "transform": { + "tx": -101.25, + "ty": 109.141008936737, + "tz": -1281.5136438843813, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 711, + "symname": "MCH/HC13/DE711", + "transform": { + "tx": -101.25, + "ty": 72.42673474412922, + "tz": -1289.5056442916643, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 712, + "symname": "MCH/HC13/DE712", + "transform": { + "tx": -121.25, + "ty": 38.147826311096175, + "tz": -1280.5297634635933, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 713, + "symname": "MCH/HC13/DE713", + "transform": { + "tx": -140, + "ty": -0.16629425054012126, + "tz": -1288.499591917732, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 714, + "symname": "MCH/HC13/DE714", + "transform": { + "tx": -121.25, + "ty": -38.24483781001593, + "tz": -1279.4710527009427, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 715, + "symname": "MCH/HC13/DE715", + "transform": { + "tx": -101.25, + "ty": -72.75932324520946, + "tz": -1287.4935395437997, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 716, + "symname": "MCH/HC13/DE716", + "transform": { + "tx": -101.25, + "ty": -109.23802043565675, + "tz": -1278.4871722801547, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 717, + "symname": "MCH/HC13/DE717", + "transform": { + "tx": -81.25, + "ty": -138.65299556957189, + "tz": -1286.5803322236598, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 718, + "symname": "MCH/HC13/DE718", + "transform": { + "tx": -61.25, + "ty": -175.53165435227626, + "tz": -1277.5684219717289, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 719, + "symname": "MCH/HC13/DE719", + "transform": { + "tx": -41.25, + "ty": -204.64665829199856, + "tz": -1285.6657391564486, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 720, + "symname": "MCH/HC12/DE720", + "transform": { + "tx": 41.25, + "ty": -204.5288697909183, + "tz": -1252.6665553209846, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 721, + "symname": "MCH/HC12/DE721", + "transform": { + "tx": 61.25, + "ty": -175.64944285335653, + "tz": -1261.5676058071929, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 722, + "symname": "MCH/HC12/DE722", + "transform": { + "tx": 81.25, + "ty": -138.53520706849162, + "tz": -1253.5811483881957, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 723, + "symname": "MCH/HC12/DE723", + "transform": { + "tx": 101.25, + "ty": -109.35580893673699, + "tz": -1262.4863561156187, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 724, + "symname": "MCH/HC12/DE724", + "transform": { + "tx": 101.25, + "ty": -72.64153474412922, + "tz": -1254.4943557083357, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 725, + "symname": "MCH/HC12/DE725", + "transform": { + "tx": 121.25, + "ty": -38.36262631109617, + "tz": -1263.4702365364067, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 800, + "symname": "MCH/HC14/DE800", + "transform": { + "tx": 140, + "ty": -0.04850574945987875, + "tz": -1295.500408082268, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 801, + "symname": "MCH/HC14/DE801", + "transform": { + "tx": 121.25, + "ty": 38.03003781001593, + "tz": -1304.5289472990573, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 802, + "symname": "MCH/HC14/DE802", + "transform": { + "tx": 101.25, + "ty": 75.99419197842722, + "tz": -1296.5542687301684, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 803, + "symname": "MCH/HC14/DE803", + "transform": { + "tx": 101.25, + "ty": 113.42279795048519, + "tz": -1305.5738005909927, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 804, + "symname": "MCH/HC14/DE804", + "transform": { + "tx": 81.25, + "ty": 142.9377634824646, + "tz": -1297.4820263945592, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 805, + "symname": "MCH/HC14/DE805", + "transform": { + "tx": 61.25, + "ty": 179.81642226516897, + "tz": -1306.49393664649, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 806, + "symname": "MCH/HC14/DE806", + "transform": { + "tx": 41.25, + "ty": 208.53146461263412, + "tz": -1298.3910764734842, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 807, + "symname": "MCH/HC15/DE807", + "transform": { + "tx": -41.25, + "ty": 208.41367611155385, + "tz": -1322.3902603089484, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 808, + "symname": "MCH/HC15/DE808", + "transform": { + "tx": -61.25, + "ty": 179.93421076624924, + "tz": -1313.494752811026, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 809, + "symname": "MCH/HC15/DE809", + "transform": { + "tx": -81.25, + "ty": 142.81997498138432, + "tz": -1321.4812102300232, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 810, + "symname": "MCH/HC15/DE810", + "transform": { + "tx": -101.25, + "ty": 113.54058645156543, + "tz": -1312.5746167555287, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 811, + "symname": "MCH/HC15/DE811", + "transform": { + "tx": -101.25, + "ty": 75.87640347734698, + "tz": -1320.5534525656324, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 812, + "symname": "MCH/HC15/DE812", + "transform": { + "tx": -121.25, + "ty": 38.147826311096175, + "tz": -1311.5297634635933, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 813, + "symname": "MCH/HC15/DE813", + "transform": { + "tx": -140, + "ty": -0.16629425054012126, + "tz": -1319.499591917732, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 814, + "symname": "MCH/HC15/DE814", + "transform": { + "tx": -121.25, + "ty": -38.24483781001593, + "tz": -1310.4710527009427, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 815, + "symname": "MCH/HC15/DE815", + "transform": { + "tx": -101.25, + "ty": -76.20899197842722, + "tz": -1318.4457312698316, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 816, + "symname": "MCH/HC15/DE816", + "transform": { + "tx": -101.25, + "ty": -113.63759795048519, + "tz": -1309.4261994090073, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 817, + "symname": "MCH/HC15/DE817", + "transform": { + "tx": -81.25, + "ty": -143.15256348246461, + "tz": -1317.5179736054408, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 818, + "symname": "MCH/HC15/DE818", + "transform": { + "tx": -61.25, + "ty": -180.031222265169, + "tz": -1308.50606335351, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 819, + "symname": "MCH/HC15/DE819", + "transform": { + "tx": -41.25, + "ty": -208.74626461263415, + "tz": -1316.6089235265158, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 820, + "symname": "MCH/HC14/DE820", + "transform": { + "tx": 41.25, + "ty": -208.62847611155388, + "tz": -1292.6097396910516, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 821, + "symname": "MCH/HC14/DE821", + "transform": { + "tx": 61.25, + "ty": -180.14901076624926, + "tz": -1301.505247188974, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 822, + "symname": "MCH/HC14/DE822", + "transform": { + "tx": 81.25, + "ty": -143.03477498138434, + "tz": -1293.5187897699768, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 823, + "symname": "MCH/HC14/DE823", + "transform": { + "tx": 101.25, + "ty": -113.75538645156543, + "tz": -1302.4253832444713, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 824, + "symname": "MCH/HC14/DE824", + "transform": { + "tx": 101.25, + "ty": -76.09120347734698, + "tz": -1294.4465474343676, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 825, + "symname": "MCH/HC14/DE825", + "transform": { + "tx": 121.25, + "ty": -38.36262631109617, + "tz": -1303.4702365364067, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 900, + "symname": "MCH/HC16/DE900", + "transform": { + "tx": 140, + "ty": -0.04850574945987875, + "tz": -1394.600408082268, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 901, + "symname": "MCH/HC16/DE901", + "transform": { + "tx": 121.25, + "ty": 38.03003781001593, + "tz": -1403.6289472990572, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 902, + "symname": "MCH/HC16/DE902", + "transform": { + "tx": 121.25, + "ty": 76.04418717745936, + "tz": -1395.654961603704, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 903, + "symname": "MCH/HC16/DE903", + "transform": { + "tx": 121.25, + "ty": 113.52278834854948, + "tz": -1404.6751863380641, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 904, + "symname": "MCH/HC16/DE904", + "transform": { + "tx": 101.25, + "ty": 150.93699532760724, + "tz": -1396.6928861602817, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 905, + "symname": "MCH/HC16/DE905", + "transform": { + "tx": 81.25, + "ty": 187.86564930934375, + "tz": -1405.7054892857484, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 906, + "symname": "MCH/HC16/DE906", + "transform": { + "tx": 61.25, + "ty": 224.72990909904797, + "tz": -1397.7155674990724, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 907, + "symname": "MCH/HC17/DE907", + "transform": { + "tx": -61.25, + "ty": 224.6121205979677, + "tz": -1421.7147513345365, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 908, + "symname": "MCH/HC17/DE908", + "transform": { + "tx": -81.25, + "ty": 187.98343781042402, + "tz": -1412.7063054502844, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 909, + "symname": "MCH/HC17/DE909", + "transform": { + "tx": -101.25, + "ty": 150.81920682652697, + "tz": -1420.6920699957457, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 910, + "symname": "MCH/HC17/DE910", + "transform": { + "tx": -121.25, + "ty": 113.64057684962972, + "tz": -1411.6760025026, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 911, + "symname": "MCH/HC17/DE911", + "transform": { + "tx": -121.25, + "ty": 75.92639867637912, + "tz": -1419.654145439168, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 912, + "symname": "MCH/HC17/DE912", + "transform": { + "tx": -121.25, + "ty": 38.147826311096175, + "tz": -1410.6297634635932, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 913, + "symname": "MCH/HC17/DE913", + "transform": { + "tx": -140, + "ty": -0.16629425054012126, + "tz": -1418.599591917732, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 914, + "symname": "MCH/HC17/DE914", + "transform": { + "tx": -121.25, + "ty": -38.24483781001593, + "tz": -1409.5710527009426, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 915, + "symname": "MCH/HC17/DE915", + "transform": { + "tx": -121.25, + "ty": -76.25898717745936, + "tz": -1417.545038396296, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 916, + "symname": "MCH/HC17/DE916", + "transform": { + "tx": -121.25, + "ty": -113.73758834854948, + "tz": -1408.5248136619357, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 917, + "symname": "MCH/HC17/DE917", + "transform": { + "tx": -101.25, + "ty": -151.15179532760726, + "tz": -1416.5071138397182, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 918, + "symname": "MCH/HC17/DE918", + "transform": { + "tx": -81.25, + "ty": -188.08044930934378, + "tz": -1407.4945107142514, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 919, + "symname": "MCH/HC17/DE919", + "transform": { + "tx": -61.25, + "ty": -224.944709099048, + "tz": -1415.4844325009274, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 920, + "symname": "MCH/HC16/DE920", + "transform": { + "tx": 61.25, + "ty": -224.82692059796773, + "tz": -1391.4852486654634, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 921, + "symname": "MCH/HC16/DE921", + "transform": { + "tx": 81.25, + "ty": -188.19823781042405, + "tz": -1400.4936945497154, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 922, + "symname": "MCH/HC16/DE922", + "transform": { + "tx": 101.25, + "ty": -151.034006826527, + "tz": -1392.5079300042541, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 923, + "symname": "MCH/HC16/DE923", + "transform": { + "tx": 121.25, + "ty": -113.85537684962972, + "tz": -1401.5239974973997, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 924, + "symname": "MCH/HC16/DE924", + "transform": { + "tx": 121.25, + "ty": -76.14119867637912, + "tz": -1393.545854560832, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 925, + "symname": "MCH/HC16/DE925", + "transform": { + "tx": 121.25, + "ty": -38.36262631109617, + "tz": -1402.5702365364066, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1000, + "symname": "MCH/HC18/DE1000", + "transform": { + "tx": 140, + "ty": -0.04850574945987875, + "tz": -1425.600408082268, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1001, + "symname": "MCH/HC18/DE1001", + "transform": { + "tx": 121.25, + "ty": 38.03003781001593, + "tz": -1434.6289472990572, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1002, + "symname": "MCH/HC18/DE1002", + "transform": { + "tx": 121.25, + "ty": 76.04418717745936, + "tz": -1426.654961603704, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1003, + "symname": "MCH/HC18/DE1003", + "transform": { + "tx": 121.25, + "ty": 113.52278834854948, + "tz": -1435.6751863380641, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1004, + "symname": "MCH/HC18/DE1004", + "transform": { + "tx": 101.25, + "ty": 150.93699532760724, + "tz": -1427.6928861602817, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1005, + "symname": "MCH/HC18/DE1005", + "transform": { + "tx": 81.25, + "ty": 187.86564930934375, + "tz": -1436.7054892857484, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1006, + "symname": "MCH/HC18/DE1006", + "transform": { + "tx": 61.25, + "ty": 224.72990909904797, + "tz": -1428.7155674990724, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1007, + "symname": "MCH/HC19/DE1007", + "transform": { + "tx": -61.25, + "ty": 224.6121205979677, + "tz": -1452.7147513345365, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1008, + "symname": "MCH/HC19/DE1008", + "transform": { + "tx": -81.25, + "ty": 187.98343781042402, + "tz": -1443.7063054502844, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1009, + "symname": "MCH/HC19/DE1009", + "transform": { + "tx": -101.25, + "ty": 150.81920682652697, + "tz": -1451.6920699957457, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1010, + "symname": "MCH/HC19/DE1010", + "transform": { + "tx": -121.25, + "ty": 113.64057684962972, + "tz": -1442.6760025026, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1011, + "symname": "MCH/HC19/DE1011", + "transform": { + "tx": -121.25, + "ty": 75.92639867637912, + "tz": -1450.654145439168, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1012, + "symname": "MCH/HC19/DE1012", + "transform": { + "tx": -121.25, + "ty": 38.147826311096175, + "tz": -1441.6297634635932, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1013, + "symname": "MCH/HC19/DE1013", + "transform": { + "tx": -140, + "ty": -0.16629425054012126, + "tz": -1449.599591917732, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1014, + "symname": "MCH/HC19/DE1014", + "transform": { + "tx": -121.25, + "ty": -38.24483781001593, + "tz": -1440.5710527009426, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1015, + "symname": "MCH/HC19/DE1015", + "transform": { + "tx": -121.25, + "ty": -76.25898717745936, + "tz": -1448.545038396296, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1016, + "symname": "MCH/HC19/DE1016", + "transform": { + "tx": -121.25, + "ty": -113.73758834854948, + "tz": -1439.5248136619357, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1017, + "symname": "MCH/HC19/DE1017", + "transform": { + "tx": -101.25, + "ty": -151.15179532760726, + "tz": -1447.5071138397182, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1018, + "symname": "MCH/HC19/DE1018", + "transform": { + "tx": -81.25, + "ty": -188.08044930934378, + "tz": -1438.4945107142514, + "yaw": -180, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1019, + "symname": "MCH/HC19/DE1019", + "transform": { + "tx": -61.25, + "ty": -224.944709099048, + "tz": -1446.4844325009274, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1020, + "symname": "MCH/HC18/DE1020", + "transform": { + "tx": 61.25, + "ty": -224.82692059796773, + "tz": -1422.4852486654634, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1021, + "symname": "MCH/HC18/DE1021", + "transform": { + "tx": 81.25, + "ty": -188.19823781042405, + "tz": -1431.4936945497154, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1022, + "symname": "MCH/HC18/DE1022", + "transform": { + "tx": 101.25, + "ty": -151.034006826527, + "tz": -1423.5079300042541, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1023, + "symname": "MCH/HC18/DE1023", + "transform": { + "tx": 121.25, + "ty": -113.85537684962972, + "tz": -1432.5239974973997, + "yaw": -0, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + }, + { + "deid": 1024, + "symname": "MCH/HC18/DE1024", + "transform": { + "tx": 121.25, + "ty": -76.14119867637912, + "tz": -1424.545854560832, + "yaw": -0, + "pitch": 0, + "roll": 179.20600000000002 + }, + "aligned": false + }, + { + "deid": 1025, + "symname": "MCH/HC18/DE1025", + "transform": { + "tx": 121.25, + "ty": -38.36262631109617, + "tz": -1433.5702365364066, + "yaw": -180, + "pitch": 0, + "roll": -0.7940000000000002 + }, + "aligned": false + } + ] +} diff --git a/Detectors/MUON/MCH/Geometry/Test/include/MCHGeometryTest/Helpers.h b/Detectors/MUON/MCH/Geometry/Test/include/MCHGeometryTest/Helpers.h new file mode 100644 index 0000000000000..9ab8c9bd52cd2 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/include/MCHGeometryTest/Helpers.h @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MCH_GEOMETRY_TEST_HELPERS_H +#define O2_MCH_GEOMETRY_TEST_HELPERS_H + +#include <iostream> + +class TH2; + +namespace o2::mch::test +{ + +/// creates MCH geometry from scratch (i.e. from a null TGeoManager) +/// usefull for tests or drawing for instance. +void createStandaloneGeometry(); + +/// tree like textual dump of the geometry nodes +void showGeometryAsTextTree(const char* fromPath = "", int maxdepth = 2, std::ostream& out = std::cout); + +/// basic drawing of the geometry +void drawGeometry(); + +/// set the volume and daughter visibility for all volumes with a name matching the regexp pattern +void setVolumeVisibility(const char* pattern, bool visible, bool visibleDaughters); + +/// set the volume line and fill for all volumes with a name matching the regexp pattern +void setVolumeColor(const char* pattern, int lineColor, int fillColor); +inline void setVolumeColor(const char* pattern, int color) +{ + setVolumeColor(pattern, color, color); +} + +/// get a radlen radiograph of a given detection element within box with the given granularity +TH2* getRadio(int detElemId, float xmin, float ymin, float xmax, float ymax, float xstep, float ystep, float thickness = 5 /* cm */); + +class Dummy +{ + // to force Root produce a dictionary for namespace test (seems it is doing it fully if there are only functions in the namespace) +}; +} // namespace o2::mch::test + +#endif diff --git a/Detectors/MUON/MCH/Simulation/macros/rootlogon.C b/Detectors/MUON/MCH/Geometry/Test/rootlogon.C similarity index 100% rename from Detectors/MUON/MCH/Simulation/macros/rootlogon.C rename to Detectors/MUON/MCH/Geometry/Test/rootlogon.C diff --git a/Detectors/MUON/MCH/Geometry/Test/testGeometryCreator.cxx b/Detectors/MUON/MCH/Geometry/Test/testGeometryCreator.cxx new file mode 100644 index 0000000000000..af0fe9ebdb581 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/testGeometryCreator.cxx @@ -0,0 +1,372 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Laurent Aphecetche + +#define BOOST_TEST_MODULE Test MCHSimulation Geometry +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN + +#include <boost/test/unit_test.hpp> + +#include "MCHGeometryCreator/Geometry.h" +#include "MCHGeometryTest/Helpers.h" +#include "TGeoManager.h" +#include "boost/format.hpp" +#include <boost/test/data/test_case.hpp> +#include <iomanip> +#include <iostream> +#include <fmt/format.h> + +namespace bdata = boost::unit_test::data; + +struct GEOMETRY { + GEOMETRY() + { + if (!gGeoManager) { + o2::mch::test::createStandaloneGeometry(); + o2::mch::geo::addAlignableVolumes(*gGeoManager); + } + }; +}; + +const std::array<std::string, 8> quadrantChamberNames{"SC01I", "SC01O", "SC02I", "SC02O", "SC03I", "SC03O", + "SC04I", "SC04O"}; + +const std::array<std::string, 12> slatChamberNames{"SC05I", "SC05O", "SC06I", "SC06O", "SC07I", "SC07O", + "SC08I", "SC08O", "SC09I", "SC09O", "SC10I", "SC10O"}; + +const std::vector<std::vector<std::string>> deSymNames{ + {"DE100", "DE103"}, + {"DE101", "DE102"}, + {"DE200", "DE203"}, + {"DE201", "DE202"}, + {"DE300", "DE303"}, + {"DE301", "DE302"}, + {"DE400", "DE403"}, + {"DE401", "DE402"}, + {"DE500", "DE501", "DE502", "DE503", "DE504", "DE514", "DE515", "DE516", "DE517"}, + {"DE505", "DE506", "DE507", "DE508", "DE509", "DE510", "DE511", "DE512", "DE513"}, + {"DE600", "DE601", "DE602", "DE603", "DE604", "DE614", "DE615", "DE616", "DE617"}, + {"DE605", "DE606", "DE607", "DE608", "DE609", "DE610", "DE611", "DE612", "DE613"}, + {"DE700", "DE701", "DE702", "DE703", "DE704", "DE705", "DE706", "DE720", "DE721", "DE722", "DE723", "DE724", "DE725"}, + {"DE707", "DE708", "DE709", "DE710", "DE711", "DE712", "DE713", "DE714", "DE715", "DE716", "DE717", "DE718", "DE719"}, + {"DE800", "DE801", "DE802", "DE803", "DE804", "DE805", "DE806", "DE820", "DE821", "DE822", "DE823", "DE824", "DE825"}, + {"DE807", "DE808", "DE809", "DE810", "DE811", "DE812", "DE813", "DE814", "DE815", "DE816", "DE817", "DE818", "DE819"}, + {"DE900", "DE901", "DE902", "DE903", "DE904", "DE905", "DE906", "DE920", "DE921", "DE922", "DE923", "DE924", "DE925"}, + {"DE907", "DE908", "DE909", "DE910", "DE911", "DE912", "DE913", "DE914", "DE915", "DE916", "DE917", "DE918", "DE919"}, + {"DE1000", "DE1001", "DE1002", "DE1003", "DE1004", "DE1005", "DE1006", "DE1020", "DE1021", "DE1022", "DE1023", "DE1024", "DE1025"}, + {"DE1007", "DE1008", "DE1009", "DE1010", "DE1011", "DE1012", "DE1013", "DE1014", "DE1015", "DE1016", "DE1017", "DE1018", "DE1019"}}; + +BOOST_FIXTURE_TEST_SUITE(geometrycreator, GEOMETRY) + +BOOST_AUTO_TEST_CASE(CanGetAllChambers) +{ + std::vector<std::string> chamberNames{quadrantChamberNames.begin(), quadrantChamberNames.end()}; + + chamberNames.insert(chamberNames.end(), slatChamberNames.begin(), slatChamberNames.end()); + + for (auto chname : chamberNames) { + auto vol = gGeoManager->GetVolume(chname.c_str()); + BOOST_TEST_REQUIRE((vol != nullptr)); + } +} + +std::vector<TGeoNode*> getSlatNodes() +{ + std::vector<TGeoNode*> slats; + for (auto chname : slatChamberNames) { + auto vol = gGeoManager->GetVolume(chname.c_str()); + TIter next(vol->GetNodes()); + while (TGeoNode* node = static_cast<TGeoNode*>(next())) { + if (strstr(node->GetName(), "support") == nullptr) { + slats.push_back(node); + } + } + } + return slats; +} + +std::vector<TGeoNode*> getQuadrantNodes() +{ + std::vector<TGeoNode*> quadrants; + for (auto chname : quadrantChamberNames) { + auto vol = gGeoManager->GetVolume(chname.c_str()); + TIter next(vol->GetNodes()); + while (TGeoNode* node = static_cast<TGeoNode*>(next())) { + quadrants.push_back(node); + } + } + return quadrants; +} + +BOOST_AUTO_TEST_CASE(GetRightNumberOfSlats) +{ + auto slats = getSlatNodes(); + BOOST_CHECK_EQUAL(slats.size(), 140); +} + +BOOST_AUTO_TEST_CASE(GetRightNumberOfQuadrants) +{ + auto quadrants = getQuadrantNodes(); + BOOST_CHECK_EQUAL(quadrants.size(), 16); +} + +BOOST_AUTO_TEST_CASE(GetDetElemVolumePath, *boost::unit_test::disabled() * boost::unit_test::label("debug")) +{ + TIter next(gGeoManager->GetTopNode()->GetNodes()); + TGeoNode* node; + TGeoNode* n2; + + std::vector<std::string> codeLines; + + while ((node = static_cast<TGeoNode*>(next()))) { + std::cout << node->GetName() << "\n"; + TIter next2(node->GetNodes()); + while ((n2 = static_cast<TGeoNode*>(next2()))) { + std::string n2name{n2->GetName()}; + auto index = n2name.find_last_of('_'); + int detElemId = std::atoi(n2name.substr(index + 1).c_str()); + if (detElemId >= 100) { + std::stringstream s; + s << "if (detElemId==" << detElemId << ") {\n"; + s << R"( return ")" << node->GetName() << "/" << n2name << "\";\n"; + s << "}\n"; + codeLines.push_back(s.str()); + } + } + } + + for (auto s : codeLines) { + std::cout << s; + } + BOOST_CHECK_EQUAL(codeLines.size(), 156); +} + +BOOST_AUTO_TEST_CASE(TextualTreeDump) +{ + const std::string expected = + R"(cave_1 +├──SC01I_0 +│ ├──Quadrant (chamber 1)_100 +│ └──Quadrant (chamber 1)_103 +├──SC01O_1 +│ ├──Quadrant (chamber 1)_101 +│ └──Quadrant (chamber 1)_102 +├──SC02I_2 +│ ├──Quadrant (chamber 2)_200 +│ └──Quadrant (chamber 2)_203 +├──SC02O_3 +│ ├──Quadrant (chamber 2)_201 +│ └──Quadrant (chamber 2)_202 +├──SC03I_4 +│ ├──Station 2 quadrant_300 +│ └──Station 2 quadrant_303 +├──SC03O_5 +│ ├──Station 2 quadrant_301 +│ └──Station 2 quadrant_302 +├──SC04I_6 +│ ├──Station 2 quadrant_400 +│ └──Station 2 quadrant_403 +├──SC04O_7 +│ ├──Station 2 quadrant_401 +│ └──Station 2 quadrant_402 +├──SC05I_8 +│ ├──Chamber 5 support panel_8 +│ ├──122000SR1_500 +│ ├──112200SR2_501 +│ ├──122200S_502 +│ ├──222000N_503 +│ ├──220000N_504 +│ ├──220000N_514 +│ ├──222000N_515 +│ ├──122200S_516 +│ └──112200SR2_517 +├──SC05O_9 +│ ├──Chamber 5 support panel_9 +│ ├──220000N_505 +│ ├──222000N_506 +│ ├──122200S_507 +│ ├──112200SR2_508 +│ ├──122000SR1_509 +│ ├──112200SR2_510 +│ ├──122200S_511 +│ ├──222000N_512 +│ └──220000N_513 +├──SC06I_10 +│ ├──Chamber 6 support panel_10 +│ ├──122000NR1_600 +│ ├──112200NR2_601 +│ ├──122200N_602 +│ ├──222000N_603 +│ ├──220000N_604 +│ ├──220000N_614 +│ ├──222000N_615 +│ ├──122200N_616 +│ └──112200NR2_617 +├──SC06O_11 +│ ├──Chamber 6 support panel_11 +│ ├──220000N_605 +│ ├──222000N_606 +│ ├──122200N_607 +│ ├──112200NR2_608 +│ ├──122000NR1_609 +│ ├──112200NR2_610 +│ ├──122200N_611 +│ ├──222000N_612 +│ └──220000N_613 +├──SC07I_12 +│ ├──Chamber 7 support panel_12 +│ ├──122330N_700 +│ ├──112233NR3_701 +│ ├──112230N_702 +│ ├──222330N_703 +│ ├──223300N_704 +│ ├──333000N_705 +│ ├──330000N_706 +│ ├──330000N_720 +│ ├──333000N_721 +│ ├──223300N_722 +│ ├──222330N_723 +│ ├──112230N_724 +│ └──112233NR3_725 +├──SC07O_13 +│ ├──Chamber 7 support panel_13 +│ ├──330000N_707 +│ ├──333000N_708 +│ ├──223300N_709 +│ ├──222330N_710 +│ ├──112230N_711 +│ ├──112233NR3_712 +│ ├──122330N_713 +│ ├──112233NR3_714 +│ ├──112230N_715 +│ ├──222330N_716 +│ ├──223300N_717 +│ ├──333000N_718 +│ └──330000N_719 +├──SC08I_14 +│ ├──Chamber 8 support panel_14 +│ ├──122330N_800 +│ ├──112233NR3_801 +│ ├──112230N_802 +│ ├──222330N_803 +│ ├──223300N_804 +│ ├──333000N_805 +│ ├──330000N_806 +│ ├──330000N_820 +│ ├──333000N_821 +│ ├──223300N_822 +│ ├──222330N_823 +│ ├──112230N_824 +│ └──112233NR3_825 +├──SC08O_15 +│ ├──Chamber 8 support panel_15 +│ ├──330000N_807 +│ ├──333000N_808 +│ ├──223300N_809 +│ ├──222330N_810 +│ ├──112230N_811 +│ ├──112233NR3_812 +│ ├──122330N_813 +│ ├──112233NR3_814 +│ ├──112230N_815 +│ ├──222330N_816 +│ ├──223300N_817 +│ ├──333000N_818 +│ └──330000N_819 +├──SC09I_16 +│ ├──Chamber 9 support panel_16 +│ ├──122330N_900 +│ ├──112233NR3_901 +│ ├──112233N_902 +│ ├──222333N_903 +│ ├──223330N_904 +│ ├──333300N_905 +│ ├──333000N_906 +│ ├──333000N_920 +│ ├──333300N_921 +│ ├──223330N_922 +│ ├──222333N_923 +│ ├──112233N_924 +│ └──112233NR3_925 +├──SC09O_17 +│ ├──Chamber 9 support panel_17 +│ ├──333000N_907 +│ ├──333300N_908 +│ ├──223330N_909 +│ ├──222333N_910 +│ ├──112233N_911 +│ ├──112233NR3_912 +│ ├──122330N_913 +│ ├──112233NR3_914 +│ ├──112233N_915 +│ ├──222333N_916 +│ ├──223330N_917 +│ ├──333300N_918 +│ └──333000N_919 +├──SC10I_18 +│ ├──Chamber 10 support panel_18 +│ ├──122330N_1000 +│ ├──112233NR3_1001 +│ ├──112233N_1002 +│ ├──222333N_1003 +│ ├──223330N_1004 +│ ├──333300N_1005 +│ ├──333000N_1006 +│ ├──333000N_1020 +│ ├──333300N_1021 +│ ├──223330N_1022 +│ ├──222333N_1023 +│ ├──112233N_1024 +│ └──112233NR3_1025 +└──SC10O_19 + ├──Chamber 10 support panel_19 + ├──333000N_1007 + ├──333300N_1008 + ├──223330N_1009 + ├──222333N_1010 + ├──112233N_1011 + ├──112233NR3_1012 + ├──122330N_1013 + ├──112233NR3_1014 + ├──112233N_1015 + ├──222333N_1016 + ├──223330N_1017 + ├──333300N_1018 + └──333000N_1019 +)"; + + std::ostringstream str; + o2::mch::test::showGeometryAsTextTree("/cave_1", 2, str); + BOOST_CHECK(expected == str.str()); +} + +BOOST_AUTO_TEST_CASE(GetAlignableHalfChambers) +{ + BOOST_REQUIRE(gGeoManager != nullptr); + + for (int i = 0; i < 20; i++) { + BOOST_CHECK((gGeoManager->GetAlignableEntry((fmt::format("MCH/HC{}", i)).c_str()))); + } +} + +BOOST_AUTO_TEST_CASE(GetAlignableDetectionElements) +{ + BOOST_REQUIRE(gGeoManager != nullptr); + + for (int hc = 0; hc < 20; hc++) { + for (int de = 0; de < deSymNames[hc].size(); de++) { + BOOST_CHECK((gGeoManager->GetAlignableEntry((fmt::format("MCH/HC{}/{}", hc, deSymNames[hc][de].c_str())).c_str()))); + } + } +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/Detectors/MUON/MCH/Geometry/Test/testGeometryTransformer.cxx b/Detectors/MUON/MCH/Geometry/Test/testGeometryTransformer.cxx new file mode 100644 index 0000000000000..1f915f6ac282c --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Test/testGeometryTransformer.cxx @@ -0,0 +1,227 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Laurent Aphecetche + +#define BOOST_TEST_MODULE Test MCHSimulation GeometryTransformer +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> + +#include "MCHGeometryCreator/Geometry.h" +#include "MCHGeometryTest/Helpers.h" +#include "MCHGeometryTransformer/Transformations.h" +#include "TGeoManager.h" +#include "boost/format.hpp" +#include <boost/test/data/test_case.hpp> +#include <fstream> +#include <iomanip> +#include <iostream> +#include <fmt/format.h> +#include <random> + +namespace bdata = boost::unit_test::data; + +BOOST_TEST_DONT_PRINT_LOG_VALUE(o2::mch::geo::TransformationCreator) + +constexpr int ntrans{2}; + +o2::mch::geo::TransformationCreator transformation(int i) +{ + static std::vector<o2::mch::geo::TransformationCreator> vtrans; + + BOOST_REQUIRE(boost::unit_test::framework::master_test_suite().argc == 2); + + if (vtrans.empty()) { + if (!gGeoManager) { + o2::mch::test::createStandaloneGeometry(); + o2::mch::geo::addAlignableVolumes(*gGeoManager); + } + BOOST_TEST_REQUIRE(boost::unit_test::framework::master_test_suite().argc == 2); + std::string jsonInput = boost::unit_test::framework::master_test_suite().argv[1]; + std::ifstream in(jsonInput); + vtrans = { + o2::mch::geo::transformationFromJSON(in), + o2::mch::geo::transformationFromTGeoManager(*gGeoManager)}; + } + return vtrans[i]; +} + +constexpr double rad2deg = 180.0 / 3.14159265358979323846; +constexpr double deg2rad = 3.14159265358979323846 / 180.0; + +void dumpMat(gsl::span<double> m) +{ + std::cout << fmt::format( + "{:7.3f} {:7.3f} {:7.3f}\n" + "{:7.3f} {:7.3f} {:7.3f}\n" + "{:7.3f} {:7.3f} {:7.3f}\n", + m[0], m[1], m[2], + m[3], m[4], m[5], + m[6], m[7], m[8]); +} + +BOOST_DATA_TEST_CASE(GetTransformationMustNotThrowForValidDetElemId, bdata::xrange(ntrans), tindex) +{ + for (auto detElemId : o2::mch::geo::allDeIds) { + BOOST_REQUIRE_NO_THROW((transformation(tindex))(detElemId)); + } +} + +BOOST_DATA_TEST_CASE(GetTransformationMustThrowForInvalidDetElemId, bdata::xrange(ntrans), tindex) +{ + const auto someInvalidDetElemIds = {99, 105, 1026}; + + for (auto detElemId : someInvalidDetElemIds) { + BOOST_CHECK_THROW((transformation(tindex)(detElemId)), std::runtime_error); + } +} + +struct CoarseLocation { + bool isRight; + bool isTop; +}; + +constexpr CoarseLocation topRight{true, true}; +constexpr CoarseLocation topLeft{false, true}; +constexpr CoarseLocation bottomRight{true, false}; +constexpr CoarseLocation bottomLeft{false, false}; + +std::string asString(const CoarseLocation& q) +{ + std::string s = q.isTop ? "TOP" : "BOTTOM"; + s += q.isRight ? "RIGHT" : "LEFT"; + return s; +} + +bool operator==(const CoarseLocation& a, const CoarseLocation& b) +{ + return a.isRight == b.isRight && a.isTop == b.isTop; +} + +CoarseLocation getDetElemCoarseLocation(int detElemId, o2::mch::geo::TransformationCreator transformation) +{ + auto t = transformation(detElemId); + + o2::math_utils::Point3D<double> localTestPos{0.0, 0.0, 0.0}; // slat center + + if (detElemId < 500) { + // in the rough ballpark of the center + // of the quadrants + localTestPos.SetXYZ(60, 60, 0); + } + + // for slats around the middle (y closest to 0) we have to be a bit + // more precise, so take a given pad reference, chosen to be + // the most top right or most top left pad + + switch (detElemId) { + case 500: + case 509: + localTestPos.SetXYZ(-72.50, 19.75, 0.0); // ds 107 + break; + case 600: + case 609: + localTestPos.SetXYZ(-77.50, 19.75, 0.0); // ds 108 + break; + case 700: + case 713: + case 800: + case 813: + case 900: + case 913: + case 1000: + case 1013: + localTestPos.SetXYZ(95.0, -19.75, 0); // ds 104 + break; + } + o2::math_utils::Point3D<double> master; + + t.LocalToMaster(localTestPos, master); + bool right = master.x() > 10.; + bool top = master.y() > -10.; + + return CoarseLocation{right, top}; +} + +void setExpectation(int firstDeId, int lastDeId, CoarseLocation q, std::map<int, CoarseLocation>& expected) +{ + for (int deid = firstDeId; deid <= lastDeId; deid++) { + expected.emplace(deid, q); + } +} + +BOOST_DATA_TEST_CASE(DetectionElementMustBeInTheRightCoarseLocation, bdata::xrange(ntrans), tindex) +{ + std::map<int, CoarseLocation> expected; + + for (int i = 0; i < 4; i++) { + expected[100 + i * 100] = topRight; + expected[101 + i * 100] = topLeft; + expected[102 + i * 100] = bottomLeft; + expected[103 + i * 100] = bottomRight; + } + + // note that by convention we consider slats in the middle to be "top" + for (int i = 0; i < 2; i++) { + setExpectation(500 + i * 100, 504 + i * 100, topRight, expected); + setExpectation(505 + i * 100, 509 + i * 100, topLeft, expected); + setExpectation(510 + i * 100, 513 + i * 100, bottomLeft, expected); + setExpectation(514 + i * 100, 517 + i * 100, bottomRight, expected); + } + + for (int i = 0; i < 4; i++) { + setExpectation(700 + i * 100, 706 + i * 100, topRight, expected); + setExpectation(707 + i * 100, 713 + i * 100, topLeft, expected); + setExpectation(714 + i * 100, 719 + i * 100, bottomLeft, expected); + setExpectation(720 + i * 100, 725 + i * 100, bottomRight, expected); + } + + for (auto detElemId : o2::mch::geo::allDeIds) { + if (expected.find(detElemId) == expected.end()) { + std::cout << "got no expectation for DE=" << detElemId << "\n"; + return; + } + BOOST_TEST_INFO_SCOPE(fmt::format("DeId {}", detElemId)); + BOOST_CHECK_EQUAL(asString(getDetElemCoarseLocation(detElemId, transformation(tindex))), asString(expected[detElemId])); + }; +} + +BOOST_AUTO_TEST_CASE(Angle2Matrix2Angle) +{ + std::random_device rd; + std::mt19937 mt(rd()); + std::vector<std::tuple<double, double, double>> testAngles; + int n{100}; + + testAngles.resize(n); + constexpr double pi = 3.14159265358979323846; + std::uniform_real_distribution<double> dist{-pi / 2.0, pi / 2.0}; + std::uniform_real_distribution<double> dist2{-pi, pi}; + std::generate(testAngles.begin(), testAngles.end(), [&dist, &dist2, &mt] { + return std::tuple<double, double, double>{dist2(mt), dist(mt), dist2(mt)}; + }); + + testAngles.emplace_back(-pi, pi / 2.0, 0); + for (auto a : testAngles) { + auto [yaw, pitch, roll] = a; + auto m = o2::mch::geo::angles2matrix(yaw, pitch, roll); + auto [y, p, r] = o2::mch::geo::matrix2angles(m); + BOOST_TEST_INFO_SCOPE(fmt::format( + " input yaw {:7.2f} pitch {:7.2f} roll {:7.2f}\n" + "output yaw {:7.2f} pitch {:7.2f} roll {:7.2f}\n", + yaw, pitch, roll, + y, p, r)); + BOOST_CHECK_CLOSE(y, yaw, 1E-6); + BOOST_CHECK_CLOSE(p, pitch, 1E-6); + BOOST_CHECK_CLOSE(r, roll, 1E-6); + } +} diff --git a/Detectors/MUON/MCH/Geometry/Transformer/CMakeLists.txt b/Detectors/MUON/MCH/Geometry/Transformer/CMakeLists.txt new file mode 100644 index 0000000000000..c2d5a03164308 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + MCHGeometryTransformer + SOURCES src/Transformations.cxx src/VolumePaths.cxx + PUBLIC_LINK_LIBRARIES ROOT::Geom O2::MathUtils ms_gsl::ms_gsl + RapidJSON::RapidJSON) + +o2_add_executable( + convert-geometry + SOURCES src/convert-geometry.cxx + PUBLIC_LINK_LIBRARIES ROOT::Geom ms_gsl::ms_gsl Boost::program_options + O2::MCHGeometryTransformer + COMPONENT_NAME mch) diff --git a/Detectors/MUON/MCH/Geometry/Transformer/README.md b/Detectors/MUON/MCH/Geometry/Transformer/README.md new file mode 100644 index 0000000000000..5e394b82e47b9 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/README.md @@ -0,0 +1,26 @@ +<!-- doxy +\page refDetectorsMUONMCHGeometryTransformer Transformations +/doxy --> + +# MCH Geometry Transformations + +Here you will find functions to perform transformations on MCH geometry : local +to global, global to local, and various (mis)alignement ones. + +# MCH Transformations in JSON format + +Also available is a CLI utility to extract MCH transformations (rotations and +translations) from a Root geometry file and export them in JSON format : + +```shell +o2-mch-convert-geometry --geom o2sim_geometry.root > geom.json +``` + +The JSON output can be manipulated (like any other json file) with e.g. [jq](https://stedolan.github.io/jq/) + +For instance, to sort the output per detection element id : + +```shell +cat output.json | jq '.alignables|=sort_by(.deid)' +``` + diff --git a/Detectors/MUON/MCH/Geometry/Transformer/include/MCHGeometryTransformer/Transformations.h b/Detectors/MUON/MCH/Geometry/Transformer/include/MCHGeometryTransformer/Transformations.h new file mode 100644 index 0000000000000..16e56d6bac296 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/include/MCHGeometryTransformer/Transformations.h @@ -0,0 +1,73 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// get the local-to-global transformation for a given detection element + +#ifndef O2_MCH_GEOMETRY_TRANSFORMER_TRANSFORMATIONS_H +#define O2_MCH_GEOMETRY_TRANSFORMER_TRANSFORMATIONS_H + +#include "MathUtils/Cartesian.h" +#include <array> +#include <tuple> +#include <gsl/span> +#include <functional> + +class TGeoManager; + +namespace o2::mch::geo +{ + +/** A geometry transformation creator must be able to + * create a transformation for any valid MCH detection element id. + * + * That transformation is used to convert coordinates from local + * (x,y in detection element plane) to global (x,y,z in Alice Coordinate + * System) and vice-versa + * + * @param detElemId must be a valid detection element + * @param geo a reference to a GeoManager that must contain MCH volumes + * + * @throw if detElemId is not valid + */ +using TransformationCreator = std::function<o2::math_utils::Transform3D(int detElemId)>; + +/** Tranformation creator using TGeoManager. + * + * @param geo a reference to a GeoManager that must contain MCH volumes + */ +TransformationCreator transformationFromTGeoManager(const TGeoManager& geo); + +TransformationCreator transformationFromJSON(std::istream& in); + +/** The list of detection element identifiers for MCH + */ +extern std::array<int, 156> allDeIds; + +/** Convert the 3 Tait–Bryan angles (yaw,pitch,roll) into the 9 matrix + * elements of a rotation matrix. + * + * @param yaw rotation around z-axis, in radian + * @param pitch rotation around y'-axis (new y-axis resulting from yaw + * rotation), in radian + * @param roll rotation around x''-axis (new x-axis resulting from yaw + * and pitch rotations), in radian + */ +std::array<double, 9> angles2matrix(double yaw, double pitch, double roll); + +/** Convert the 9 matrix elements of a rotation matrix + * into 3 Tait-Bryan angles (yaw,pitch,roll). + * + * @param rot a 9-elements vector + * @returns a tuple of the 3 angles <yaw,pitch,roll>. The angles are + * expressed in radian + */ +std::tuple<double, double, double> matrix2angles(gsl::span<double> rot); +} // namespace o2::mch::geo + +#endif diff --git a/Detectors/MUON/MCH/Geometry/Transformer/include/MCHGeometryTransformer/VolumePaths.h b/Detectors/MUON/MCH/Geometry/Transformer/include/MCHGeometryTransformer/VolumePaths.h new file mode 100644 index 0000000000000..edf1630976ffe --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/include/MCHGeometryTransformer/VolumePaths.h @@ -0,0 +1,22 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// get the local-to-global transformation for a given detection element + +#ifndef O2_MCH_GEOMETRY_TRANSFORMER_VOLUME_PATHS_H +#define O2_MCH_GEOMETRY_TRANSFORMER_VOLUME_PATHS_H + +#include <string> + +namespace o2::mch::geo +{ +std::string volumePathName(int deId); +} + +#endif diff --git a/Detectors/MUON/MCH/Geometry/Transformer/src/Transformations.cxx b/Detectors/MUON/MCH/Geometry/Transformer/src/Transformations.cxx new file mode 100644 index 0000000000000..448ea24a7580e --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/src/Transformations.cxx @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "MCHGeometryTransformer/Transformations.h" +#include "MCHGeometryTransformer/VolumePaths.h" +#include <array> +#include <string> +#include <vector> +#include <TGeoManager.h> +#include <TGeoMatrix.h> +#include <rapidjson/document.h> +#include <rapidjson/istreamwrapper.h> +#include <fmt/format.h> +namespace o2::mch::geo +{ + +std::array<int, 156> allDeIds = { + 100, 101, 102, 103, + 200, 201, 202, 203, + 300, 301, 302, 303, + 400, 401, 402, 403, + 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, + 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, + 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, + 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, + 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, + 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025}; + +TransformationCreator transformationFromTGeoManager(const TGeoManager& geo) +{ + return [&geo](int detElemId) -> o2::math_utils::Transform3D { + if (std::find(begin(allDeIds), end(allDeIds), detElemId) == end(allDeIds)) { + throw std::runtime_error("Wrong detection element Id"); + } + + std::string volPathName = geo.GetTopVolume()->GetName(); + + int nCh = detElemId / 100; + + if (nCh <= 4 && geo.GetVolume("YOUT1")) { + volPathName += "/YOUT1_1/"; + } else if ((nCh == 5 || nCh == 6) && geo.GetVolume("DDIP")) { + volPathName += "/DDIP_1/"; + } else if (nCh >= 7 && geo.GetVolume("YOUT2")) { + volPathName += "/YOUT2_1/"; + } else { + volPathName += "/"; + } + + volPathName += volumePathName(detElemId); + + TGeoNavigator* navig = gGeoManager->GetCurrentNavigator(); + + if (!navig->cd(volPathName.c_str())) { + throw std::runtime_error("could not get to volPathName=" + volPathName); + } + + return o2::math_utils::Transform3D{*(navig->GetCurrentMatrix())}; + }; +} + +TransformationCreator transformationFromJSON(std::istream& in) +{ + rapidjson::IStreamWrapper isw(in); + + rapidjson::Document d; + d.ParseStream(isw); + + rapidjson::Value& alignables = d["alignables"]; + assert(alignables.IsArray()); + + std::map<int, std::tuple<double, double, double>> angles; + std::map<int, std::tuple<double, double, double>> translations; + + // loop over json document and extract Tait-Bryan angles (yaw,pitch,roll) + // as well as translation vector (tx,ty,tz) + // for each detection element + + constexpr double deg2rad = 3.14159265358979323846 / 180.0; + + for (auto& al : alignables.GetArray()) { + auto itr = al.FindMember("deid"); + if (itr != al.MemberEnd()) { + int deid = itr->value.GetInt(); + auto t = al["transform"].GetObject(); + angles[deid] = { + deg2rad * t["yaw"].GetDouble(), + deg2rad * t["pitch"].GetDouble(), + deg2rad * t["roll"].GetDouble()}; + translations[deid] = { + t["tx"].GetDouble(), + t["ty"].GetDouble(), + t["tz"].GetDouble()}; + } + } + + return [angles, translations](int detElemId) -> o2::math_utils::Transform3D { + if (std::find(begin(allDeIds), end(allDeIds), detElemId) == end(allDeIds)) { + throw std::runtime_error("Wrong detection element Id"); + } + auto [yaw, pitch, roll] = angles.at(detElemId); + auto [tx, ty, tz] = translations.at(detElemId); + double tr[3] = {tx, ty, tz}; + // get the angles, convert them to a matrix and build a Transform3D + // from it + auto rot = o2::mch::geo::angles2matrix(yaw, pitch, roll); + TGeoHMatrix m; + m.SetRotation(&rot[0]); + m.SetTranslation(tr); + return o2::math_utils::Transform3D(m); + }; +} // namespace o2::mch::geo + +std::array<double, 9> angles2matrix(double yaw, double pitch, double roll) +{ + std::array<double, 9> rot; + + double sinpsi = std::sin(roll); + double cospsi = std::cos(roll); + double sinthe = std::sin(pitch); + double costhe = std::cos(pitch); + double sinphi = std::sin(yaw); + double cosphi = std::cos(yaw); + rot[0] = costhe * cosphi; + rot[1] = -costhe * sinphi; + rot[2] = sinthe; + rot[3] = sinpsi * sinthe * cosphi + cospsi * sinphi; + rot[4] = -sinpsi * sinthe * sinphi + cospsi * cosphi; + rot[5] = -costhe * sinpsi; + rot[6] = -cospsi * sinthe * cosphi + sinpsi * sinphi; + rot[7] = cospsi * sinthe * sinphi + sinpsi * cosphi; + rot[8] = costhe * cospsi; + return rot; +} + +std::tuple<double, double, double> matrix2angles(gsl::span<double> rot) +{ + double roll = std::atan2(-rot[5], rot[8]); + double pitch = std::asin(rot[2]); + double yaw = std::atan2(-rot[1], rot[0]); + return std::make_tuple(yaw, + pitch, + roll); +} + +} // namespace o2::mch::geo diff --git a/Detectors/MUON/MCH/Geometry/Transformer/src/VolumePaths.cxx b/Detectors/MUON/MCH/Geometry/Transformer/src/VolumePaths.cxx new file mode 100644 index 0000000000000..da07e7db7127f --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/src/VolumePaths.cxx @@ -0,0 +1,492 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "MCHGeometryTransformer/VolumePaths.h" + +namespace o2::mch::geo +{ +// return the path of the mother volume of detElemId, relative to MCH geometry +// (i.e. excluding general top node) +std::string volumePathName(int detElemId) +{ + std::string vp{"incorrect detElemId"}; + + if (detElemId == 100) { + return "SC01I_0/Quadrant (chamber 1)_100"; + } + if (detElemId == 103) { + return "SC01I_0/Quadrant (chamber 1)_103"; + } + if (detElemId == 101) { + return "SC01O_1/Quadrant (chamber 1)_101"; + } + if (detElemId == 102) { + return "SC01O_1/Quadrant (chamber 1)_102"; + } + if (detElemId == 200) { + return "SC02I_2/Quadrant (chamber 2)_200"; + } + if (detElemId == 203) { + return "SC02I_2/Quadrant (chamber 2)_203"; + } + if (detElemId == 201) { + return "SC02O_3/Quadrant (chamber 2)_201"; + } + if (detElemId == 202) { + return "SC02O_3/Quadrant (chamber 2)_202"; + } + if (detElemId == 300) { + return "SC03I_4/Station 2 quadrant_300"; + } + if (detElemId == 303) { + return "SC03I_4/Station 2 quadrant_303"; + } + if (detElemId == 301) { + return "SC03O_5/Station 2 quadrant_301"; + } + if (detElemId == 302) { + return "SC03O_5/Station 2 quadrant_302"; + } + if (detElemId == 400) { + return "SC04I_6/Station 2 quadrant_400"; + } + if (detElemId == 403) { + return "SC04I_6/Station 2 quadrant_403"; + } + if (detElemId == 401) { + return "SC04O_7/Station 2 quadrant_401"; + } + if (detElemId == 402) { + return "SC04O_7/Station 2 quadrant_402"; + } + if (detElemId == 500) { + return "SC05I_8/122000SR1_500"; + } + if (detElemId == 501) { + return "SC05I_8/112200SR2_501"; + } + if (detElemId == 502) { + return "SC05I_8/122200S_502"; + } + if (detElemId == 503) { + return "SC05I_8/222000N_503"; + } + if (detElemId == 504) { + return "SC05I_8/220000N_504"; + } + if (detElemId == 514) { + return "SC05I_8/220000N_514"; + } + if (detElemId == 515) { + return "SC05I_8/222000N_515"; + } + if (detElemId == 516) { + return "SC05I_8/122200S_516"; + } + if (detElemId == 517) { + return "SC05I_8/112200SR2_517"; + } + if (detElemId == 505) { + return "SC05O_9/220000N_505"; + } + if (detElemId == 506) { + return "SC05O_9/222000N_506"; + } + if (detElemId == 507) { + return "SC05O_9/122200S_507"; + } + if (detElemId == 508) { + return "SC05O_9/112200SR2_508"; + } + if (detElemId == 509) { + return "SC05O_9/122000SR1_509"; + } + if (detElemId == 510) { + return "SC05O_9/112200SR2_510"; + } + if (detElemId == 511) { + return "SC05O_9/122200S_511"; + } + if (detElemId == 512) { + return "SC05O_9/222000N_512"; + } + if (detElemId == 513) { + return "SC05O_9/220000N_513"; + } + if (detElemId == 600) { + return "SC06I_10/122000NR1_600"; + } + if (detElemId == 601) { + return "SC06I_10/112200NR2_601"; + } + if (detElemId == 602) { + return "SC06I_10/122200N_602"; + } + if (detElemId == 603) { + return "SC06I_10/222000N_603"; + } + if (detElemId == 604) { + return "SC06I_10/220000N_604"; + } + if (detElemId == 614) { + return "SC06I_10/220000N_614"; + } + if (detElemId == 615) { + return "SC06I_10/222000N_615"; + } + if (detElemId == 616) { + return "SC06I_10/122200N_616"; + } + if (detElemId == 617) { + return "SC06I_10/112200NR2_617"; + } + if (detElemId == 605) { + return "SC06O_11/220000N_605"; + } + if (detElemId == 606) { + return "SC06O_11/222000N_606"; + } + if (detElemId == 607) { + return "SC06O_11/122200N_607"; + } + if (detElemId == 608) { + return "SC06O_11/112200NR2_608"; + } + if (detElemId == 609) { + return "SC06O_11/122000NR1_609"; + } + if (detElemId == 610) { + return "SC06O_11/112200NR2_610"; + } + if (detElemId == 611) { + return "SC06O_11/122200N_611"; + } + if (detElemId == 612) { + return "SC06O_11/222000N_612"; + } + if (detElemId == 613) { + return "SC06O_11/220000N_613"; + } + if (detElemId == 700) { + return "SC07I_12/122330N_700"; + } + if (detElemId == 701) { + return "SC07I_12/112233NR3_701"; + } + if (detElemId == 702) { + return "SC07I_12/112230N_702"; + } + if (detElemId == 703) { + return "SC07I_12/222330N_703"; + } + if (detElemId == 704) { + return "SC07I_12/223300N_704"; + } + if (detElemId == 705) { + return "SC07I_12/333000N_705"; + } + if (detElemId == 706) { + return "SC07I_12/330000N_706"; + } + if (detElemId == 720) { + return "SC07I_12/330000N_720"; + } + if (detElemId == 721) { + return "SC07I_12/333000N_721"; + } + if (detElemId == 722) { + return "SC07I_12/223300N_722"; + } + if (detElemId == 723) { + return "SC07I_12/222330N_723"; + } + if (detElemId == 724) { + return "SC07I_12/112230N_724"; + } + if (detElemId == 725) { + return "SC07I_12/112233NR3_725"; + } + if (detElemId == 707) { + return "SC07O_13/330000N_707"; + } + if (detElemId == 708) { + return "SC07O_13/333000N_708"; + } + if (detElemId == 709) { + return "SC07O_13/223300N_709"; + } + if (detElemId == 710) { + return "SC07O_13/222330N_710"; + } + if (detElemId == 711) { + return "SC07O_13/112230N_711"; + } + if (detElemId == 712) { + return "SC07O_13/112233NR3_712"; + } + if (detElemId == 713) { + return "SC07O_13/122330N_713"; + } + if (detElemId == 714) { + return "SC07O_13/112233NR3_714"; + } + if (detElemId == 715) { + return "SC07O_13/112230N_715"; + } + if (detElemId == 716) { + return "SC07O_13/222330N_716"; + } + if (detElemId == 717) { + return "SC07O_13/223300N_717"; + } + if (detElemId == 718) { + return "SC07O_13/333000N_718"; + } + if (detElemId == 719) { + return "SC07O_13/330000N_719"; + } + if (detElemId == 800) { + return "SC08I_14/122330N_800"; + } + if (detElemId == 801) { + return "SC08I_14/112233NR3_801"; + } + if (detElemId == 802) { + return "SC08I_14/112230N_802"; + } + if (detElemId == 803) { + return "SC08I_14/222330N_803"; + } + if (detElemId == 804) { + return "SC08I_14/223300N_804"; + } + if (detElemId == 805) { + return "SC08I_14/333000N_805"; + } + if (detElemId == 806) { + return "SC08I_14/330000N_806"; + } + if (detElemId == 820) { + return "SC08I_14/330000N_820"; + } + if (detElemId == 821) { + return "SC08I_14/333000N_821"; + } + if (detElemId == 822) { + return "SC08I_14/223300N_822"; + } + if (detElemId == 823) { + return "SC08I_14/222330N_823"; + } + if (detElemId == 824) { + return "SC08I_14/112230N_824"; + } + if (detElemId == 825) { + return "SC08I_14/112233NR3_825"; + } + if (detElemId == 807) { + return "SC08O_15/330000N_807"; + } + if (detElemId == 808) { + return "SC08O_15/333000N_808"; + } + if (detElemId == 809) { + return "SC08O_15/223300N_809"; + } + if (detElemId == 810) { + return "SC08O_15/222330N_810"; + } + if (detElemId == 811) { + return "SC08O_15/112230N_811"; + } + if (detElemId == 812) { + return "SC08O_15/112233NR3_812"; + } + if (detElemId == 813) { + return "SC08O_15/122330N_813"; + } + if (detElemId == 814) { + return "SC08O_15/112233NR3_814"; + } + if (detElemId == 815) { + return "SC08O_15/112230N_815"; + } + if (detElemId == 816) { + return "SC08O_15/222330N_816"; + } + if (detElemId == 817) { + return "SC08O_15/223300N_817"; + } + if (detElemId == 818) { + return "SC08O_15/333000N_818"; + } + if (detElemId == 819) { + return "SC08O_15/330000N_819"; + } + if (detElemId == 900) { + return "SC09I_16/122330N_900"; + } + if (detElemId == 901) { + return "SC09I_16/112233NR3_901"; + } + if (detElemId == 902) { + return "SC09I_16/112233N_902"; + } + if (detElemId == 903) { + return "SC09I_16/222333N_903"; + } + if (detElemId == 904) { + return "SC09I_16/223330N_904"; + } + if (detElemId == 905) { + return "SC09I_16/333300N_905"; + } + if (detElemId == 906) { + return "SC09I_16/333000N_906"; + } + if (detElemId == 920) { + return "SC09I_16/333000N_920"; + } + if (detElemId == 921) { + return "SC09I_16/333300N_921"; + } + if (detElemId == 922) { + return "SC09I_16/223330N_922"; + } + if (detElemId == 923) { + return "SC09I_16/222333N_923"; + } + if (detElemId == 924) { + return "SC09I_16/112233N_924"; + } + if (detElemId == 925) { + return "SC09I_16/112233NR3_925"; + } + if (detElemId == 907) { + return "SC09O_17/333000N_907"; + } + if (detElemId == 908) { + return "SC09O_17/333300N_908"; + } + if (detElemId == 909) { + return "SC09O_17/223330N_909"; + } + if (detElemId == 910) { + return "SC09O_17/222333N_910"; + } + if (detElemId == 911) { + return "SC09O_17/112233N_911"; + } + if (detElemId == 912) { + return "SC09O_17/112233NR3_912"; + } + if (detElemId == 913) { + return "SC09O_17/122330N_913"; + } + if (detElemId == 914) { + return "SC09O_17/112233NR3_914"; + } + if (detElemId == 915) { + return "SC09O_17/112233N_915"; + } + if (detElemId == 916) { + return "SC09O_17/222333N_916"; + } + if (detElemId == 917) { + return "SC09O_17/223330N_917"; + } + if (detElemId == 918) { + return "SC09O_17/333300N_918"; + } + if (detElemId == 919) { + return "SC09O_17/333000N_919"; + } + if (detElemId == 1000) { + return "SC10I_18/122330N_1000"; + } + if (detElemId == 1001) { + return "SC10I_18/112233NR3_1001"; + } + if (detElemId == 1002) { + return "SC10I_18/112233N_1002"; + } + if (detElemId == 1003) { + return "SC10I_18/222333N_1003"; + } + if (detElemId == 1004) { + return "SC10I_18/223330N_1004"; + } + if (detElemId == 1005) { + return "SC10I_18/333300N_1005"; + } + if (detElemId == 1006) { + return "SC10I_18/333000N_1006"; + } + if (detElemId == 1020) { + return "SC10I_18/333000N_1020"; + } + if (detElemId == 1021) { + return "SC10I_18/333300N_1021"; + } + if (detElemId == 1022) { + return "SC10I_18/223330N_1022"; + } + if (detElemId == 1023) { + return "SC10I_18/222333N_1023"; + } + if (detElemId == 1024) { + return "SC10I_18/112233N_1024"; + } + if (detElemId == 1025) { + return "SC10I_18/112233NR3_1025"; + } + if (detElemId == 1007) { + return "SC10O_19/333000N_1007"; + } + if (detElemId == 1008) { + return "SC10O_19/333300N_1008"; + } + if (detElemId == 1009) { + return "SC10O_19/223330N_1009"; + } + if (detElemId == 1010) { + return "SC10O_19/222333N_1010"; + } + if (detElemId == 1011) { + return "SC10O_19/112233N_1011"; + } + if (detElemId == 1012) { + return "SC10O_19/112233NR3_1012"; + } + if (detElemId == 1013) { + return "SC10O_19/122330N_1013"; + } + if (detElemId == 1014) { + return "SC10O_19/112233NR3_1014"; + } + if (detElemId == 1015) { + return "SC10O_19/112233N_1015"; + } + if (detElemId == 1016) { + return "SC10O_19/222333N_1016"; + } + if (detElemId == 1017) { + return "SC10O_19/223330N_1017"; + } + if (detElemId == 1018) { + return "SC10O_19/333300N_1018"; + } + if (detElemId == 1019) { + return "SC10O_19/333000N_1019"; + } + + return vp; +} +} // namespace o2::mch::geo diff --git a/Detectors/MUON/MCH/Geometry/Transformer/src/convert-geometry.cxx b/Detectors/MUON/MCH/Geometry/Transformer/src/convert-geometry.cxx new file mode 100644 index 0000000000000..59e1b5ea85766 --- /dev/null +++ b/Detectors/MUON/MCH/Geometry/Transformer/src/convert-geometry.cxx @@ -0,0 +1,210 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <boost/program_options.hpp> +#include <iostream> +#include <stdexcept> +#include <TGeoManager.h> +#include <TFile.h> +#include <sstream> +#include <tuple> +#include <vector> +#include <string> +#include "TGeoPhysicalNode.h" +#include <rapidjson/document.h> +#include <rapidjson/ostreamwrapper.h> +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.h> +#include <gsl/span> +#include <cmath> +#include <array> +#include "MCHGeometryTransformer/Transformations.h" + +namespace po = boost::program_options; + +std::vector<std::string> splitString(const std::string& src, char delim) +{ + std::stringstream ss(src); + std::string token; + std::vector<std::string> tokens; + + while (std::getline(ss, token, delim)) { + if (!token.empty()) { + tokens.push_back(std::move(token)); + } + } + + return tokens; +} + +TGeoManager* readFromFile(std::string filename) +{ + TFile* f = TFile::Open(filename.c_str()); + if (f->IsZombie()) { + throw std::runtime_error("can not open " + filename); + } + + auto possibleGeoNames = {"ALICE", "FAIRGeom", "MCH-ONLY", "MCH-BASICS"}; + + TGeoManager* geo{nullptr}; + + for (auto name : possibleGeoNames) { + geo = static_cast<TGeoManager*>(f->Get(name)); + if (geo) { + break; + } + } + if (!geo) { + f->ls(); + throw std::runtime_error("could not find ALICE geometry (using ALICE or FAIRGeom names)"); + } + return geo; +} + +template <typename WRITER> +void matrix2json(const TGeoHMatrix& matrix, WRITER& w) +{ + + constexpr double rad2deg = 180.0 / 3.14159265358979323846; + + const Double_t* t = matrix.GetTranslation(); + const Double_t* m = matrix.GetRotationMatrix(); + gsl::span<double> mat(const_cast<double*>(m), 9); + auto [yaw, pitch, roll] = o2::mch::geo::matrix2angles(mat); + w.Key("tx"); + w.Double(t[0]); + w.Key("ty"); + w.Double(t[1]); + w.Key("tz"); + w.Double(t[2]); + w.Key("yaw"); + w.Double(rad2deg * yaw); + w.Key("pitch"); + w.Double(rad2deg * pitch); + w.Key("roll"); + w.Double(rad2deg * roll); +} + +std::tuple<bool, uint16_t> isMCH(std::string alignableName) +{ + auto parts = splitString(alignableName, '/'); + bool aliroot = parts[0] == "MUON"; + bool o2 = parts[0] == "MCH"; + if (!o2 && !aliroot) { + return {false, 0}; + } + auto id = std::stoi(parts[1].substr(2)); + bool ok = (aliroot && (id <= 15)) || (o2 && (id <= 19)); + uint16_t deId{0}; + if (ok && parts.size() > 2) { + deId = std::stoi(parts[2].substr(2)); + } + return {ok, deId}; +} + +template <typename WRITER> +void writeMatrix(const char* name, const TGeoHMatrix* matrix, WRITER& w) +{ + if (matrix) { + w.Key(name); + w.StartObject(); + matrix2json(*matrix, w); + w.EndObject(); + } +} + +/** convert geometry into a json document. + */ +void convertGeom(const TGeoManager& geom) +{ + rapidjson::OStreamWrapper osw(std::cout); + rapidjson::Writer<rapidjson::OStreamWrapper> writer(osw); + + writer.StartObject(); + writer.Key("alignables"); + writer.StartArray(); + + for (auto i = 0; i < geom.GetNAlignable(); i++) { + auto ae = geom.GetAlignableEntry(i); + std::string symname = ae->GetName(); + auto [mch, deId] = isMCH(symname); + if (!mch) { + continue; + } + writer.StartObject(); + if (deId > 0) { + writer.Key("deid"); + writer.Int(deId); + } + writer.Key("symname"); + writer.String(symname.c_str()); + auto matrix = ae->GetMatrix(); + bool aligned{true}; + if (!matrix) { + matrix = ae->GetGlobalOrig(); + aligned = false; + } + writeMatrix("transform", matrix, writer); + writer.Key("aligned"); + writer.Bool(aligned); + writer.EndObject(); + } + writer.EndArray(); + writer.EndObject(); +} + +int main(int argc, char** argv) +{ + po::variables_map vm; + po::options_description options; + + // clang-format off + options.add_options() + ("help,h","help") + ("geom",po::value<std::string>()->required(),"geometry.root file"); + // clang-format on + + po::options_description cmdline; + cmdline.add(options); + + po::store(po::command_line_parser(argc, argv).options(cmdline).run(), vm); + + if (vm.count("help")) { + std::cout << "This program extract MCH geometry transformation from " + "a geometry root file and write them in json format.\n"; + std::cout << "\n"; + std::cout << options << "\n"; + std::cout << "\n"; + std::cout << "Note that the json format can then be further manipulated using e.g." + "the jq utility\n"; + std::cout << "For instance sorting by deid:\n"; + std::cout << "cat output.json | jq '.alignables|=sort_by(.deid)'\n"; + std::cout << "\n"; + return 2; + } + + try { + po::notify(vm); + } catch (boost::program_options::error& e) { + std::cout << "Error: " << e.what() << "\n"; + std::cout << options << "\n"; + exit(1); + } + + TGeoManager* geom = readFromFile(vm["geom"].as<std::string>()); + + if (geom) { + convertGeom(*geom); + return 0; + } else { + return 3; + } + return 0; +} diff --git a/Detectors/MUON/MCH/PreClustering/src/PreClusterFinder.cxx b/Detectors/MUON/MCH/PreClustering/src/PreClusterFinder.cxx index 03e6261bf5708..608aef049b23f 100644 --- a/Detectors/MUON/MCH/PreClustering/src/PreClusterFinder.cxx +++ b/Detectors/MUON/MCH/PreClustering/src/PreClusterFinder.cxx @@ -256,14 +256,18 @@ void PreClusterFinder::addPad(DetectionElement& de, uint16_t iPad, PreCluster& c } cluster.lastPad = de.nOrderedPads[0]; ++de.nOrderedPads[0]; - if (pad.area[0][0] < cluster.area[0][0]) + if (pad.area[0][0] < cluster.area[0][0]) { cluster.area[0][0] = pad.area[0][0]; - if (pad.area[0][1] > cluster.area[0][1]) + } + if (pad.area[0][1] > cluster.area[0][1]) { cluster.area[0][1] = pad.area[0][1]; - if (pad.area[1][0] < cluster.area[1][0]) + } + if (pad.area[1][0] < cluster.area[1][0]) { cluster.area[1][0] = pad.area[1][0]; - if (pad.area[1][1] > cluster.area[1][1]) + } + if (pad.area[1][1] > cluster.area[1][1]) { cluster.area[1][1] = pad.area[1][1]; + } pad.useMe = false; diff --git a/Detectors/MUON/MCH/README.md b/Detectors/MUON/MCH/README.md index d19561f506d46..07fd2af3abda9 100644 --- a/Detectors/MUON/MCH/README.md +++ b/Detectors/MUON/MCH/README.md @@ -8,7 +8,10 @@ This is a top page for the MCH detector documentation. <!-- doxy \subpage refDetectorsMUONMCHContour +\subpage refDetectorsMUONMCHClustering \subpage refDetectorsMUONMCHRaw \subpage refDetectorsMUONMCHMapping +\subpage refDetectorsMUONMCHTracking \subpage refDetectorsMUONMCHWorkflow +\subpage refDetectorsMUONMCHGeometry /doxy --> diff --git a/Detectors/MUON/MCH/Raw/Common/include/MCHRawCommon/SampaCluster.h b/Detectors/MUON/MCH/Raw/Common/include/MCHRawCommon/SampaCluster.h index 28f14f04332c0..b5a4f659d51d8 100644 --- a/Detectors/MUON/MCH/Raw/Common/include/MCHRawCommon/SampaCluster.h +++ b/Detectors/MUON/MCH/Raw/Common/include/MCHRawCommon/SampaCluster.h @@ -67,6 +67,9 @@ struct SampaCluster { /// needed to store this cluster uint16_t nof10BitWords() const; + /// sum returns the total charge in the cluster + uint32_t sum() const; + uint10_t sampaTime; //< 10 bits for a local time stamp uint20_t bunchCrossing; //< 20 bits for bunch crossing counter uint20_t chargeSum; //< 20 bits for a cluster sum diff --git a/Detectors/MUON/MCH/Raw/Common/src/SampaCluster.cxx b/Detectors/MUON/MCH/Raw/Common/src/SampaCluster.cxx index 256a7afc1e863..3ef6cc8e36745 100644 --- a/Detectors/MUON/MCH/Raw/Common/src/SampaCluster.cxx +++ b/Detectors/MUON/MCH/Raw/Common/src/SampaCluster.cxx @@ -65,13 +65,25 @@ uint16_t SampaCluster::nof10BitWords() const if (isClusterSum()) { n10 += 2; // 20 bits (chargesum) } else { - for (auto s : samples) { - ++n10; // 10 bits for each sample - } + n10 += samples.size(); } return n10; } +uint32_t SampaCluster::sum() const +{ + uint32_t tot(0); + if (isClusterSum()) { + tot = chargeSum; + } else { + for (const auto& s : samples) { + tot += s; + } + } + + return tot; +} + std::ostream& operator<<(std::ostream& os, const SampaCluster& sc) { os << fmt::format("ts {:4d} ", sc.sampaTime); diff --git a/Detectors/MUON/MCH/Raw/Decoder/CMakeLists.txt b/Detectors/MUON/MCH/Raw/Decoder/CMakeLists.txt index a0759f2cb6505..4241929ec6314 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/CMakeLists.txt +++ b/Detectors/MUON/MCH/Raw/Decoder/CMakeLists.txt @@ -9,8 +9,9 @@ # submit itself to any jurisdiction. o2_add_library(MCHRawDecoder - SOURCES src/PageDecoder.cxx src/RDHManip.cxx - PUBLIC_LINK_LIBRARIES O2::MCHRawCommon O2::MCHRawElecMap O2::DetectorsRaw + SOURCES src/PageDecoder.cxx src/DataDecoder.cxx src/RDHManip.cxx src/OrbitInfo.cxx + PUBLIC_LINK_LIBRARIES O2::MCHRawCommon O2::MCHBase O2::MCHMappingInterface O2::MCHMappingImpl4 O2::MCHRawElecMap + O2::DetectorsRaw PRIVATE_LINK_LIBRARIES O2::MCHRawImplHelpers) if(BUILD_TESTING) diff --git a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/DataDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/DataDecoder.h new file mode 100644 index 0000000000000..f28440bcedf77 --- /dev/null +++ b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/DataDecoder.h @@ -0,0 +1,82 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DataDecoder.h +/// \author Andrea Ferrero +/// +/// \brief Definition of the decoder for the MCH data +/// + +#include <gsl/span> +#include <unordered_set> + +#include "Headers/RDHAny.h" +#include "MCHBase/Digit.h" +#include "MCHRawDecoder/OrbitInfo.h" +#include "MCHRawDecoder/PageDecoder.h" + +namespace o2 +{ +namespace mch +{ +namespace raw +{ + +using RdhHandler = std::function<void(o2::header::RDHAny*)>; + +// custom hash for OrbitInfo objects +struct OrbitInfoHash { + std::size_t operator()(const OrbitInfo& info) const noexcept + { + return std::hash<uint64_t>{}(info.get()); + } +}; + +//_________________________________________________________________ +// +// Data decoder +//_________________________________________________________________ +class DataDecoder +{ + public: + DataDecoder(SampaChannelHandler channelHandler, RdhHandler rdhHandler, std::string mapCRUfile, std::string mapFECfile, bool ds2manu, bool verbose); + + void reset(); + void decodeBuffer(gsl::span<const std::byte> page); + + const std::vector<o2::mch::Digit>& getOutputDigits() const { return mOutputDigits; } + const std::unordered_set<OrbitInfo, OrbitInfoHash>& getOrbits() const { return mOrbits; } + + private: + void initElec2DetMapper(std::string filename); + void initFee2SolarMapper(std::string filename); + void init(); + + Elec2DetMapper mElec2Det{nullptr}; + FeeLink2SolarMapper mFee2Solar{nullptr}; + o2::mch::raw::PageDecoder mDecoder; + size_t mNrdhs{0}; + std::vector<o2::mch::Digit> mOutputDigits; + std::unordered_set<OrbitInfo, OrbitInfoHash> mOrbits; ///< list of orbits in the processed buffer + + SampaChannelHandler mChannelHandler; + std::function<void(o2::header::RDHAny*)> mRdhHandler; + + std::string mMapCRUfile; + std::string mMapFECfile; + + bool mDebug{false}; + bool mDs2manu{false}; +}; + +} // namespace raw +} // namespace mch +} // end namespace o2 diff --git a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/DecodedDataHandlers.h b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/DecodedDataHandlers.h new file mode 100644 index 0000000000000..d286e2098ccb1 --- /dev/null +++ b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/DecodedDataHandlers.h @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MCH_DECODED_DATA_HANDLES_H +#define O2_MCH_DECODED_DATA_HANDLES_H + +#include <functional> + +#include "MCHRawCommon/SampaCluster.h" +#include "MCHRawElecMap/DsElecId.h" + +namespace o2 +{ +namespace mch +{ +namespace raw +{ +/// A SampaChannelHandler is a function that takes a pair to identify +/// a readout sampa channel and a SampaCluster containing the channel data. +using SampaChannelHandler = std::function<void(DsElecId dsId, + uint8_t channel, + SampaCluster)>; + +/// A SampaChannSampaHeartBeatHandler is a function that takes a chip index and +/// a bunch crossing counter value found in a HeartBeat packet +using SampaErrorHandler = std::function<void(DsElecId dsId, + int8_t chip, + uint32_t error)>; + +struct DecodedDataHandlers { + SampaChannelHandler sampaChannelHandler; + SampaErrorHandler sampaErrorHandler; +}; + +} // namespace raw +} // namespace mch +} // namespace o2 + +#endif diff --git a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/ErrorCodes.h b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/ErrorCodes.h new file mode 100644 index 0000000000000..60da703c981f3 --- /dev/null +++ b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/ErrorCodes.h @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MCH_RAW_ERROR_CODES_H +#define O2_MCH_RAW_ERROR_CODES_H + +namespace o2 +{ +namespace mch +{ +namespace raw +{ + +enum ErrorCodes { + ErrorParity = 1, // 1 + ErrorHammingCorrectable = 1 << 1, // 2 + ErrorHammingUncorrectable = 1 << 2, // 4 + ErrorBadClusterSize = 1 << 3, // 8 + ErrorBadPacketType = 1 << 4, // 16 + ErrorBadHeartBeatPacket = 1 << 5, // 32 + ErrorBadIncompleteWord = 1 << 6, // 64 + ErrorTruncatedData = 1 << 7, // 128 + ErrorBadELinkID = 1 << 8, // 256 + ErrorBadLinkID = 1 << 9, // 512 + ErrorUnknownLinkID = 1 << 10 // 1024 +}; + +} // namespace raw +} // namespace mch +} // namespace o2 + +#endif diff --git a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/OrbitInfo.h b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/OrbitInfo.h new file mode 100644 index 0000000000000..87f0944456ae7 --- /dev/null +++ b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/OrbitInfo.h @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 OrbitInfo.h + * C++ Muon MCH class that holds information about processed orbits + * @author Andrea Ferrero + */ + +#ifndef ALICEO2_MCH_BASE_ORBITINFO_H_ +#define ALICEO2_MCH_BASE_ORBITINFO_H_ + +#include <gsl/span> + +#include "Rtypes.h" + +namespace o2 +{ +namespace mch +{ + +// \class OrbitInfo +/// \brief MCH orbit info implementation +class OrbitInfo +{ + public: + OrbitInfo() = default; + + OrbitInfo(gsl::span<const std::byte> rdhBuffer); + ~OrbitInfo() = default; + + uint64_t get() const { return mOrbitInfo; } + uint32_t getOrbit() const { return (mOrbitInfo & 0xFFFFFFFF); } + uint8_t getLinkID() const { return ((mOrbitInfo >> 32) & 0xFF); } + uint16_t getFeeID() const { return ((mOrbitInfo >> 40) & 0xFF); } + + friend bool operator==(const OrbitInfo& o1, const OrbitInfo& o2); + friend bool operator!=(const OrbitInfo& o1, const OrbitInfo& o2); + + private: + uint64_t mOrbitInfo = {0}; + + ClassDefNV(OrbitInfo, 1); +}; //class OrbitInfo + +bool operator==(const OrbitInfo& o1, const OrbitInfo& o2); +bool operator!=(const OrbitInfo& o1, const OrbitInfo& o2); + +} //namespace mch +} //namespace o2 +#endif // ALICEO2_MCH_BASE_ORBITINFO_H_ diff --git a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/PageDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/PageDecoder.h index 42abc29c69987..039acaeab6f9e 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/PageDecoder.h +++ b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/PageDecoder.h @@ -14,7 +14,7 @@ #include <functional> #include <gsl/span> #include <map> -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" #include "MCHRawElecMap/Mapper.h" namespace o2::mch::raw @@ -32,11 +32,11 @@ using RawBuffer = gsl::span<const std::byte>; // // @param rdhBuffer a raw memory buffer containing (at least) one RDH // which information is used to decide which PageDecoder implementation to choose -// @param channelHandler (optional) a callable object that will be called for each -// decoded SampaCluster +// @param decodedDataHandlers a structure with various callable objects (optional) that +/// will be called for each decoded Sampa packet and in case of decoding errors // PageDecoder createPageDecoder(RawBuffer rdhBuffer, - SampaChannelHandler channelHandler); + DecodedDataHandlers decodedDataHandlers); // Same as above but only to be used for special cases, e.g. when // trying to decode test beam data with an electronic mapping that @@ -46,9 +46,17 @@ PageDecoder createPageDecoder(RawBuffer rdhBuffer, // object into a solarId. // PageDecoder createPageDecoder(RawBuffer rdhBuffer, - SampaChannelHandler channelHandler, + DecodedDataHandlers decodedDataHandlers, FeeLink2SolarMapper fee2solar); +// Alternative versions of the same functions, taking a SampaChannelHandler as parameter. +[[deprecated("Use createPageDecoder(RawBuffer,DecodedDataHandlers) instead.")]] PageDecoder createPageDecoder(RawBuffer rdhBuffer, + SampaChannelHandler channelHandler); + +[[deprecated("Use createPageDecoder(RawBuffer,DecodedDataHandlers,fee2solar) instead.")]] PageDecoder createPageDecoder(RawBuffer rdhBuffer, + SampaChannelHandler channelHandler, + FeeLink2SolarMapper fee2solar); + // A PageParser loops over the given buffer and apply the given page decoder // to each page. using PageParser = std::function<void(RawBuffer buffer, PageDecoder pageDecoder)>; diff --git a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/SampaChannelHandler.h b/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/SampaChannelHandler.h deleted file mode 100644 index 58470c4bc772c..0000000000000 --- a/Detectors/MUON/MCH/Raw/Decoder/include/MCHRawDecoder/SampaChannelHandler.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_MCH_RAW_SAMPA_CHANNEL_HANDLER_H -#define O2_MCH_RAW_SAMPA_CHANNEL_HANDLER_H - -#include <functional> - -#include "MCHRawCommon/SampaCluster.h" -#include "MCHRawElecMap/DsElecId.h" - -namespace o2 -{ -namespace mch -{ -namespace raw -{ -/// A SampaChannelHandler is a function that takes a pair to identify -/// a readout sampa channel and a SampaCluster containing the channel data. -using SampaChannelHandler = std::function<void(DsElecId dsId, - uint8_t channel, - SampaCluster)>; -} // namespace raw -} // namespace mch -} // namespace o2 - -#endif diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/BareElinkDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/src/BareElinkDecoder.h index 04a455ae9b3ca..84bf6f0b00b92 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/BareElinkDecoder.h +++ b/Detectors/MUON/MCH/Raw/Decoder/src/BareElinkDecoder.h @@ -14,7 +14,7 @@ #include "Assertions.h" #include "MCHRawCommon/DataFormats.h" #include "MCHRawCommon/SampaHeader.h" -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" #include <bitset> #include <fmt/format.h> #include <fmt/printf.h> @@ -33,7 +33,7 @@ namespace o2::mch::raw /// /// Bits coming from parts of the GBT words are added to the Elink using the /// append() method and each time a SampaCluster is decoded, -/// it is passed to the SampaChannelHandler for further processing (or none). +/// it is passed to the DecodedDataHandlers for further processing (or none). /// /// \nosubgrouping /// @@ -44,9 +44,9 @@ class BareElinkDecoder /// Constructor. /// \param dsId the (electronic) id of the dual sampa this elink /// is connected to - /// \param sampaChannelHandler a callable that is passed - /// each SampaCluster that will be decoded - BareElinkDecoder(DsElecId dsId, SampaChannelHandler sampaChannelHandler); + /// \param decodedDataHandlers a structure with various callable that + /// handle the Sampa packets and decoding errors + BareElinkDecoder(DsElecId dsId, DecodedDataHandlers decodedDataHandlers); /** @name Main interface */ @@ -104,7 +104,7 @@ class BareElinkDecoder private: DsElecId mDsId; - SampaChannelHandler mSampaChannelHandler; //< The callable that will deal with the SampaCluster objects we decode + DecodedDataHandlers mDecodedDataHandlers; //< The structure with the callables that deal with the Sampa packets and the decoding errors SampaHeader mSampaHeader; //< Current SampaHeader uint64_t mBitBuffer; //< Our internal bit stream buffer /** @name internal global counters @@ -154,9 +154,9 @@ std::string bitBufferString(const std::bitset<50>& bs, int imax) template <typename CHARGESUM> BareElinkDecoder<CHARGESUM>::BareElinkDecoder(DsElecId dsId, - SampaChannelHandler sampaChannelHandler) + DecodedDataHandlers decodedDataHandlers) : mDsId{dsId}, - mSampaChannelHandler{sampaChannelHandler}, + mDecodedDataHandlers{decodedDataHandlers}, mSampaHeader{}, mBitBuffer{}, mNofSync{}, @@ -422,20 +422,20 @@ std::ostream& operator<<(std::ostream& os, const o2::mch::raw::BareElinkDecoder< template <> void BareElinkDecoder<ChargeSumMode>::sendCluster() { - if (mSampaChannelHandler) { - mSampaChannelHandler(mDsId, - channelNumber64(mSampaHeader), - SampaCluster(mTimestamp, mSampaHeader.bunchCrossingCounter(), mClusterSum, mClusterSize)); + SampaChannelHandler handler = mDecodedDataHandlers.sampaChannelHandler; + if (handler) { + handler(mDsId, channelNumber64(mSampaHeader), + SampaCluster(mTimestamp, mSampaHeader.bunchCrossingCounter(), mClusterSum, mClusterSize)); } } template <> void BareElinkDecoder<SampleMode>::sendCluster() { - if (mSampaChannelHandler) { - mSampaChannelHandler(mDsId, - channelNumber64(mSampaHeader), - SampaCluster(mTimestamp, mSampaHeader.bunchCrossingCounter(), mSamples)); + SampaChannelHandler handler = mDecodedDataHandlers.sampaChannelHandler; + if (handler) { + handler(mDsId, channelNumber64(mSampaHeader), + SampaCluster(mTimestamp, mSampaHeader.bunchCrossingCounter(), mSamples)); } mSamples.clear(); } diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/BareGBTDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/src/BareGBTDecoder.h index 2699e7b2b0d4f..f95f4ab6a5899 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/BareGBTDecoder.h +++ b/Detectors/MUON/MCH/Raw/Decoder/src/BareGBTDecoder.h @@ -13,7 +13,7 @@ #include <array> #include "BareElinkDecoder.h" -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" #include <gsl/span> #include <fmt/printf.h> #include <fmt/format.h> @@ -40,8 +40,9 @@ class BareGBTDecoder : public PayloadDecoder<BareGBTDecoder<CHARGESUM>> public: /// Constructor. /// \param solarId - /// \param sampaChannelHandler the callable that will handle each SampaCluster - BareGBTDecoder(uint16_t solarId, SampaChannelHandler sampaChannelHandler); + /// \param decodedDataHandlers a structure with various callable that + /// handle the Sampa packets and decoding errors + BareGBTDecoder(uint16_t solarId, DecodedDataHandlers decodedDataHandlers); /** @name Main interface */ @@ -81,10 +82,10 @@ using namespace boost::multiprecision; template <typename CHARGESUM> BareGBTDecoder<CHARGESUM>::BareGBTDecoder(uint16_t solarId, - SampaChannelHandler sampaChannelHandler) - : PayloadDecoder<BareGBTDecoder<CHARGESUM>>(sampaChannelHandler), + DecodedDataHandlers decodedDataHandlers) + : PayloadDecoder<BareGBTDecoder<CHARGESUM>>(decodedDataHandlers), mSolarId{solarId}, - mElinks{impl::makeArray<40>([=](uint8_t i) { return BareElinkDecoder<CHARGESUM>(DsElecId{solarId, static_cast<uint8_t>(i / 5), static_cast<uint8_t>(i % 5)}, sampaChannelHandler); })}, + mElinks{impl::makeArray<40>([=](uint8_t i) { return BareElinkDecoder<CHARGESUM>(DsElecId{solarId, static_cast<uint8_t>(i / 5), static_cast<uint8_t>(i % 5)}, decodedDataHandlers); })}, mNofGbtWordsSeens{0} { } diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/DataDecoder.cxx b/Detectors/MUON/MCH/Raw/Decoder/src/DataDecoder.cxx new file mode 100644 index 0000000000000..68c4482aa1df5 --- /dev/null +++ b/Detectors/MUON/MCH/Raw/Decoder/src/DataDecoder.cxx @@ -0,0 +1,282 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DataDecoder.cxx +/// \author Andrea Ferrero +/// +/// \brief Implementation of a data processor to run the raw decoding +/// + +#include "MCHRawDecoder/DataDecoder.h" + +#include <fstream> +#include "Headers/RAWDataHeader.h" +#include "DetectorsRaw/RDHUtils.h" +#include "MCHMappingInterface/Segmentation.h" + +namespace o2 +{ +namespace mch +{ +namespace raw +{ + +using namespace o2; +//using namespace o2::framework; +using namespace o2::mch::mapping; +using RDH = o2::header::RDHAny; + +// conversion matrix between the original channels numbering of the RUN2 readout electronics and the final version of the RUN3 DualSAMPA-based readout +static std::array<int, 64> refManu2ds_st345_v5 = { + 63, 62, 61, 60, 59, 57, 56, 53, 51, 50, 47, 45, 44, 41, 38, 35, + 36, 33, 34, 37, 32, 39, 40, 42, 43, 46, 48, 49, 52, 54, 55, 58, + 7, 8, 5, 2, 6, 1, 3, 0, 4, 9, 10, 15, 17, 18, 22, 25, + 31, 30, 29, 28, 27, 26, 24, 23, 20, 21, 16, 19, 12, 14, 11, 13}; + +// conversion matrix between the original channels numbering of the RUN2 readout electronics and the intermediate version of the RUN3 DualSAMPA-based readout +static std::array<int, 64> refManu2ds_st345_v2 = { + 62, 61, 63, 60, 59, 55, 58, 57, 56, 54, 50, 46, 42, 39, 37, 41, + 35, 36, 33, 34, 32, 38, 43, 40, 45, 44, 47, 48, 49, 52, 51, 53, + 7, 6, 5, 4, 2, 3, 1, 0, 9, 11, 13, 15, 17, 19, 21, 23, + 31, 30, 29, 28, 27, 26, 25, 24, 22, 20, 18, 16, 14, 12, 10, 8}; + +#define refManu2ds_st345 refManu2ds_st345_v5 + +// inverse channel conversion matrix +static std::array<int, 64> refDs2manu_st345; + +// function returning the RUN3 DualSAMPA channel number given the original RUN2 channel +static int manu2ds(int i) +{ + return refManu2ds_st345[i]; +} + +// function returning the original RUN2 channel number given the RUN3 DualSAMPA channel +static int ds2manu(int i) +{ + return refDs2manu_st345[i]; +} + +static void patchPage(gsl::span<const std::byte> rdhBuffer, bool verbose) +{ + static int mNrdhs = 0; + auto& rdhAny = *reinterpret_cast<RDH*>(const_cast<std::byte*>(&(rdhBuffer[0]))); + mNrdhs++; + + auto cruId = o2::raw::RDHUtils::getCRUID(rdhAny) & 0xFF; + auto flags = o2::raw::RDHUtils::getCRUID(rdhAny) & 0xFF00; + auto endpoint = o2::raw::RDHUtils::getEndPointID(rdhAny); + uint32_t feeId = cruId * 2 + endpoint + flags; + o2::raw::RDHUtils::setFEEID(rdhAny, feeId); + + if (verbose) { + std::cout << mNrdhs << "--\n"; + o2::raw::RDHUtils::printRDH(rdhAny); + } +}; + +//======================= +// Data decoder + +DataDecoder::DataDecoder(SampaChannelHandler channelHandler, RdhHandler rdhHandler, std::string mapCRUfile, std::string mapFECfile, bool ds2manu, bool verbose) + : mChannelHandler(channelHandler), mRdhHandler(rdhHandler), mMapCRUfile(mapCRUfile), mMapFECfile(mapFECfile), mDs2manu(ds2manu), mDebug(verbose) +{ + init(); +} + +static bool isValidDeID(int deId) +{ + for (auto id : deIdsForAllMCH) { + if (id == deId) { + return true; + } + } + + return false; +} + +void DataDecoder::decodeBuffer(gsl::span<const std::byte> page) +{ + size_t ndigits{0}; + + uint32_t orbit; + + auto channelHandler = [&](DsElecId dsElecId, uint8_t channel, o2::mch::raw::SampaCluster sc) { + if (mChannelHandler) { + mChannelHandler(dsElecId, channel, sc); + } + + if (mDs2manu) { + channel = ds2manu(int(channel)); + } + + uint32_t digitadc = sc.sum(); + + int deId{-1}; + int dsIddet{-1}; + if (auto opt = mElec2Det(dsElecId); opt.has_value()) { + DsDetId dsDetId = opt.value(); + dsIddet = dsDetId.dsId(); + deId = dsDetId.deId(); + } + if (mDebug) { + auto s = asString(dsElecId); + auto ch = fmt::format("{}-CH{:02d}", s, channel); + std::cout << ch << " " + << "deId " << deId << " dsIddet " << dsIddet << std::endl; + } + + if (deId < 0 || dsIddet < 0 || !isValidDeID(deId)) { + return; + } + + int padId = -1; + const Segmentation& segment = segmentation(deId); + + padId = segment.findPadByFEE(dsIddet, int(channel)); + if (mDebug) { + auto s = asString(dsElecId); + auto ch = fmt::format("{}-CH{:02d}", s, channel); + std::cout << ch << " " + << fmt::format("PAD ({:04d} {:04d} {:04d})\tADC {:06d} TIME ({} {} {:02d}) SIZE {} END {}", + deId, dsIddet, padId, digitadc, orbit, sc.bunchCrossing, sc.sampaTime, sc.nofSamples(), (sc.sampaTime + sc.nofSamples() - 1)) + << (((sc.sampaTime + sc.nofSamples() - 1) >= 98) ? " *" : "") << std::endl; + } + + // skip channels not associated to any pad + if (padId < 0) { + return; + } + + Digit::Time time; + time.sampaTime = sc.sampaTime; + time.bunchCrossing = sc.bunchCrossing; + time.orbit = orbit; + + mOutputDigits.emplace_back(o2::mch::Digit(deId, padId, digitadc, time, sc.nofSamples())); + + if (mDebug) { + std::cout << "DIGIT STORED:\nADC " << mOutputDigits.back().getADC() << " DE# " << mOutputDigits.back().getDetID() << " PadId " << mOutputDigits.back().getPadID() << " time " << mOutputDigits.back().getTime().sampaTime << std::endl; + } + ++ndigits; + }; + + const auto dumpDigits = [&](bool bending) { + for (auto d : mOutputDigits) { + if (d.getPadID() < 0) { + continue; + } + const Segmentation& segment = segmentation(d.getDetID()); + bool bend = segment.isBendingPad(d.getPadID()); + if (bending != segment.isBendingPad(d.getPadID())) { + continue; + } + float X = segment.padPositionX(d.getPadID()); + float Y = segment.padPositionY(d.getPadID()); + std::cout << fmt::format(" DE {:4d} PAD {:5d} ADC {:6d} TIME ({} {} {:4d})", + d.getDetID(), d.getPadID(), d.getADC(), d.getTime().orbit, d.getTime().bunchCrossing, d.getTime().sampaTime); + std::cout << fmt::format("\tC {} PAD_XY {:+2.2f} , {:+2.2f}", (bending ? (int)0 : (int)1), X, Y); + std::cout << std::endl; + } + }; + + patchPage(page, mDebug); + + auto& rdhAny = *reinterpret_cast<RDH*>(const_cast<std::byte*>(&(page[0]))); + orbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdhAny); + + if (mRdhHandler) { + mRdhHandler(&rdhAny); + } + + // add orbit to vector if not present yet + mOrbits.emplace(page); + + if (!mDecoder) { + DecodedDataHandlers handlers; + handlers.sampaChannelHandler = channelHandler; + mDecoder = mFee2Solar ? o2::mch::raw::createPageDecoder(page, handlers, mFee2Solar) + : o2::mch::raw::createPageDecoder(page, handlers); + } + mDecoder(page); + + if (mDebug) { + std::cout << "[decodeBuffer] mOutputDigits size: " << mOutputDigits.size() << std::endl; + dumpDigits(true); + dumpDigits(false); + } +}; + +static std::string readFileContent(std::string& filename) +{ + std::string content; + std::string s; + std::ifstream in(filename); + while (std::getline(in, s)) { + content += s; + content += "\n"; + } + std::cout << "readFileContent(" << filename << "):" << std::endl + << content << std::endl; + return content; +}; + +void DataDecoder::initElec2DetMapper(std::string filename) +{ + std::cout << "[initElec2DetMapper] filename=" << filename << std::endl; + if (filename.empty()) { + mElec2Det = createElec2DetMapper<ElectronicMapperGenerated>(); + } else { + ElectronicMapperString::sFecMap = readFileContent(filename); + mElec2Det = createElec2DetMapper<ElectronicMapperString>(); + } +}; + +void DataDecoder::initFee2SolarMapper(std::string filename) +{ + std::cout << "[initFee2SolarMapper] filename=" << filename << std::endl; + if (filename.empty()) { + mFee2Solar = createFeeLink2SolarMapper<ElectronicMapperGenerated>(); + } else { + ElectronicMapperString::sCruMap = readFileContent(filename); + mFee2Solar = createFeeLink2SolarMapper<ElectronicMapperString>(); + } +}; + +//_________________________________________________________________________________________________ +void DataDecoder::init() +{ + mNrdhs = 0; + + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { + if (refManu2ds_st345[j] != i) { + continue; + } + refDs2manu_st345[i] = j; + break; + } + } + + initFee2SolarMapper(mMapCRUfile); + initElec2DetMapper(mMapFECfile); +}; + +//_________________________________________________________________________________________________ +void DataDecoder::reset() +{ + mOutputDigits.clear(); + mOrbits.clear(); +} + +} // namespace raw +} // namespace mch +} // end namespace o2 diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/OrbitInfo.cxx b/Detectors/MUON/MCH/Raw/Decoder/src/OrbitInfo.cxx new file mode 100644 index 0000000000000..df9f12de6d43d --- /dev/null +++ b/Detectors/MUON/MCH/Raw/Decoder/src/OrbitInfo.cxx @@ -0,0 +1,42 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "Headers/RAWDataHeader.h" +#include "DetectorsRaw/RDHUtils.h" +#include "MCHRawDecoder/OrbitInfo.h" + +namespace o2::mch +{ + +using RDH = o2::header::RDHAny; + +OrbitInfo::OrbitInfo(gsl::span<const std::byte> rdhBuffer) +{ + auto& rdhAny = *reinterpret_cast<RDH*>(const_cast<std::byte*>(&(rdhBuffer[0]))); + auto orbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdhAny); + auto linkId = o2::raw::RDHUtils::getLinkID(rdhAny); + auto feeId = o2::raw::RDHUtils::getFEEID(rdhAny); + + mOrbitInfo = orbit; + mOrbitInfo += ((static_cast<uint64_t>(linkId) << 32) & 0xFF00000000); + mOrbitInfo += ((static_cast<uint64_t>(feeId) << 40) & 0xFF0000000000); +} + +bool operator==(const OrbitInfo& o1, const OrbitInfo& o2) +{ + return (o1.mOrbitInfo == o2.mOrbitInfo); +} + +bool operator!=(const OrbitInfo& o1, const OrbitInfo& o2) +{ + return !(o1 == o2); +} + +} // namespace o2::mch diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/PageDecoder.cxx b/Detectors/MUON/MCH/Raw/Decoder/src/PageDecoder.cxx index 975360cb98e2d..e5c99d99cae7d 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/PageDecoder.cxx +++ b/Detectors/MUON/MCH/Raw/Decoder/src/PageDecoder.cxx @@ -26,20 +26,20 @@ uint16_t CHARGESUM_MASK = 0x100; template <typename FORMAT, typename CHARGESUM> struct PayloadDecoderImpl { - using type = struct { + using type = struct PayloadDecoderImplReturnType { void process(uint32_t, gsl::span<const std::byte>); }; - type operator()(const FeeLinkId& feeLinkId, SampaChannelHandler sampaChannelHandler, FeeLink2SolarMapper fee2solar); + type operator()(const FeeLinkId& feeLinkId, DecodedDataHandlers decodedDataHandlers, FeeLink2SolarMapper fee2solar); }; template <typename CHARGESUM> struct PayloadDecoderImpl<UserLogicFormat, CHARGESUM> { using type = UserLogicEndpointDecoder<CHARGESUM>; - type operator()(const FeeLinkId& feeLinkId, SampaChannelHandler sampaChannelHandler, FeeLink2SolarMapper fee2solar) + type operator()(const FeeLinkId& feeLinkId, DecodedDataHandlers decodedDataHandlers, FeeLink2SolarMapper fee2solar) { - return std::move(UserLogicEndpointDecoder<CHARGESUM>(feeLinkId.feeId(), fee2solar, sampaChannelHandler)); + return std::move(UserLogicEndpointDecoder<CHARGESUM>(feeLinkId.feeId(), fee2solar, decodedDataHandlers)); } }; @@ -47,13 +47,13 @@ template <typename CHARGESUM> struct PayloadDecoderImpl<BareFormat, CHARGESUM> { using type = BareGBTDecoder<CHARGESUM>; - type operator()(const FeeLinkId& feeLinkId, SampaChannelHandler sampaChannelHandler, FeeLink2SolarMapper fee2solar) + type operator()(const FeeLinkId& feeLinkId, DecodedDataHandlers decodedDataHandlers, FeeLink2SolarMapper fee2solar) { auto solarId = fee2solar(feeLinkId); if (!solarId.has_value()) { throw std::logic_error(fmt::format("{} could not get solarId from feelinkid={}\n", __PRETTY_FUNCTION__, feeLinkId)); } - return std::move(BareGBTDecoder<CHARGESUM>(solarId.value(), sampaChannelHandler)); + return std::move(BareGBTDecoder<CHARGESUM>(solarId.value(), decodedDataHandlers)); } }; @@ -61,7 +61,7 @@ template <typename FORMAT, typename CHARGESUM> class PageDecoderImpl { public: - PageDecoderImpl(SampaChannelHandler sampaChannelHandler, FeeLink2SolarMapper fee2solar) : mSampaChannelHandler{sampaChannelHandler}, + PageDecoderImpl(DecodedDataHandlers decodedDataHandlers, FeeLink2SolarMapper fee2solar) : mDecodedDataHandlers{decodedDataHandlers}, mFee2SolarMapper(fee2solar) { } @@ -79,25 +79,28 @@ class PageDecoderImpl auto p = mPayloadDecoders.find(feeLinkId); if (p == mPayloadDecoders.end()) { - mPayloadDecoders.emplace(feeLinkId, PayloadDecoderImpl<FORMAT, CHARGESUM>()(feeLinkId, mSampaChannelHandler, mFee2SolarMapper)); + mPayloadDecoders.emplace(feeLinkId, PayloadDecoderImpl<FORMAT, CHARGESUM>()(feeLinkId, mDecodedDataHandlers, mFee2SolarMapper)); p = mPayloadDecoders.find(feeLinkId); } uint32_t orbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdhP); auto rdhSize = o2::raw::RDHUtils::getHeaderSize(rdhP); auto payloadSize = o2::raw::RDHUtils::getMemorySize(rdhP) - rdhSize; - p->second.process(orbit, page.subspan(rdhSize, payloadSize)); + // skip empty payloads, otherwise the orbit jumps are not correctly detected + if (payloadSize > 0) { + p->second.process(orbit, page.subspan(rdhSize, payloadSize)); + } } private: - SampaChannelHandler mSampaChannelHandler; + DecodedDataHandlers mDecodedDataHandlers; FeeLink2SolarMapper mFee2SolarMapper; std::map<FeeLinkId, typename PayloadDecoderImpl<FORMAT, CHARGESUM>::type> mPayloadDecoders; }; } // namespace impl -PageDecoder createPageDecoder(RawBuffer rdhBuffer, SampaChannelHandler channelHandler, FeeLink2SolarMapper fee2solar) +PageDecoder createPageDecoder(RawBuffer rdhBuffer, DecodedDataHandlers decodedDataHandlers, FeeLink2SolarMapper fee2solar) { const void* rdhP = reinterpret_cast<const void*>(rdhBuffer.data()); bool ok = o2::raw::RDHUtils::checkRDH(rdhP, true); @@ -108,23 +111,37 @@ PageDecoder createPageDecoder(RawBuffer rdhBuffer, SampaChannelHandler channelHa auto feeId = o2::raw::RDHUtils::getFEEID(rdhP); if (linkId == 15) { if (feeId & impl::CHARGESUM_MASK) { - return impl::PageDecoderImpl<UserLogicFormat, ChargeSumMode>(channelHandler, fee2solar); + return impl::PageDecoderImpl<UserLogicFormat, ChargeSumMode>(decodedDataHandlers, fee2solar); } else { - return impl::PageDecoderImpl<UserLogicFormat, SampleMode>(channelHandler, fee2solar); + return impl::PageDecoderImpl<UserLogicFormat, SampleMode>(decodedDataHandlers, fee2solar); } } else { if (feeId & impl::CHARGESUM_MASK) { - return impl::PageDecoderImpl<BareFormat, ChargeSumMode>(channelHandler, fee2solar); + return impl::PageDecoderImpl<BareFormat, ChargeSumMode>(decodedDataHandlers, fee2solar); } else { - return impl::PageDecoderImpl<BareFormat, SampleMode>(channelHandler, fee2solar); + return impl::PageDecoderImpl<BareFormat, SampleMode>(decodedDataHandlers, fee2solar); } } } -PageDecoder createPageDecoder(RawBuffer rdhBuffer, SampaChannelHandler channelHandler) +PageDecoder createPageDecoder(RawBuffer rdhBuffer, DecodedDataHandlers decodedDataHandlers) { auto fee2solar = createFeeLink2SolarMapper<ElectronicMapperGenerated>(); - return createPageDecoder(rdhBuffer, channelHandler, fee2solar); + return createPageDecoder(rdhBuffer, decodedDataHandlers, fee2solar); +} + +PageDecoder createPageDecoder(RawBuffer rdhBuffer, SampaChannelHandler channelHandler) +{ + DecodedDataHandlers handlers; + handlers.sampaChannelHandler = channelHandler; + return createPageDecoder(rdhBuffer, handlers); +} + +PageDecoder createPageDecoder(RawBuffer rdhBuffer, SampaChannelHandler channelHandler, FeeLink2SolarMapper fee2solar) +{ + DecodedDataHandlers handlers; + handlers.sampaChannelHandler = channelHandler; + return createPageDecoder(rdhBuffer, handlers, fee2solar); } PageParser createPageParser() diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/PayloadDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/src/PayloadDecoder.h index 1b5adddc542cb..78ee061da59ed 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/PayloadDecoder.h +++ b/Detectors/MUON/MCH/Raw/Decoder/src/PayloadDecoder.h @@ -12,7 +12,7 @@ #define O2_MCH_RAW_PAYLOAD_DECODER_H #include "Headers/RAWDataHeader.h" -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" #include "MCHRawDecoder/PageDecoder.h" #include <map> #include <cstdlib> @@ -38,9 +38,9 @@ class PayloadDecoder { public: /// Constructs a decoder - /// \param channelHandler the handler that will be called for each - /// piece of sampa data (a SampaCluster, i.e. a part of a time window) - PayloadDecoder(SampaChannelHandler channelHandler); + /// \param decodedDataHandlers decodedDataHandlers a structure with various callable objects that + /// will be called for each decoded Sampa packet and in case of decoding errors + PayloadDecoder(DecodedDataHandlers decodedDataHandlers); /// decode the buffer (=payload only) /// \return the number of bytes used from the buffer @@ -48,12 +48,12 @@ class PayloadDecoder private: uint32_t mOrbit; - SampaChannelHandler mChannelHandler; + DecodedDataHandlers mDecodedDataHandlers; }; template <typename T> -PayloadDecoder<T>::PayloadDecoder(SampaChannelHandler channelHandler) - : mChannelHandler(channelHandler) +PayloadDecoder<T>::PayloadDecoder(DecodedDataHandlers decodedDataHandlers) + : mDecodedDataHandlers(decodedDataHandlers) { } diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicElinkDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicElinkDecoder.h index f53dac245abbd..9b398a67f869a 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicElinkDecoder.h +++ b/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicElinkDecoder.h @@ -14,7 +14,8 @@ #include "Debug.h" #include "MCHRawCommon/DataFormats.h" #include "MCHRawCommon/SampaHeader.h" -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" +#include "MCHRawDecoder/ErrorCodes.h" #include "MCHRawElecMap/DsElecId.h" #include <bitset> #include <fmt/format.h> @@ -32,7 +33,7 @@ template <typename CHARGESUM> class UserLogicElinkDecoder { public: - UserLogicElinkDecoder(DsElecId dsId, SampaChannelHandler sampaChannelHandler); + UserLogicElinkDecoder(DsElecId dsId, DecodedDataHandlers decodedDataHandlers); /// Append 50 bits-worth of data void append(uint64_t data50, uint8_t error); @@ -57,6 +58,8 @@ class UserLogicElinkDecoder template <typename T> friend std::ostream& operator<<(std::ostream& os, const o2::mch::raw::UserLogicElinkDecoder<T>& e); + const DsElecId& dsId() const { return mDsId; } + void clear(); bool hasError() const; bool isHeaderComplete() const { return mHeaderParts.size() == 5; } @@ -69,6 +72,7 @@ class UserLogicElinkDecoder void oneLess10BitWord(); void prepareAndSendCluster(); void sendCluster(const SampaCluster& sc) const; + void sendError(int8_t chip, uint32_t error) const; void setClusterSize(uint10_t value); void setClusterTime(uint10_t value); void setHeaderPart(uint10_t data10); @@ -77,7 +81,7 @@ class UserLogicElinkDecoder private: DsElecId mDsId; - SampaChannelHandler mSampaChannelHandler; + DecodedDataHandlers mDecodedDataHandlers; State mState; std::vector<uint10_t> mSamples{}; std::vector<uint10_t> mHeaderParts{}; @@ -102,8 +106,8 @@ constexpr bool isIncomplete(uint8_t error) template <typename CHARGESUM> UserLogicElinkDecoder<CHARGESUM>::UserLogicElinkDecoder(DsElecId dsId, - SampaChannelHandler sampaChannelHandler) - : mDsId{dsId}, mSampaChannelHandler{sampaChannelHandler}, mState{State::WaitingSync} + DecodedDataHandlers decodedDataHandlers) + : mDsId{dsId}, mDecodedDataHandlers{decodedDataHandlers}, mState{State::WaitingSync} { } @@ -149,10 +153,11 @@ void UserLogicElinkDecoder<CHARGESUM>::append(uint64_t data50, uint8_t error) } } - if (isIncomplete(error) && (i == 5)) { + if (isIncomplete(error) && (i == 5) && (mState != State::WaitingSync)) { #ifdef ULDEBUG debugHeader() << (*this) << " data packet end not found when isIncomplete --> resetting\n"; #endif + sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadIncompleteWord)); reset(); } } // namespace o2::mch::raw @@ -331,7 +336,21 @@ void UserLogicElinkDecoder<CHARGESUM>::sendCluster(const SampaCluster& sc) const channelNumber64(mSampaHeader), o2::mch::raw::asString(sc)); #endif - mSampaChannelHandler(mDsId, channelNumber64(mSampaHeader), sc); + mDecodedDataHandlers.sampaChannelHandler(mDsId, channelNumber64(mSampaHeader), sc); +} + +template <typename CHARGESUM> +void UserLogicElinkDecoder<CHARGESUM>::sendError(int8_t chip, uint32_t error) const +{ +#ifdef ULDEBUG + debugHeader() << (*this) << " --> " + << fmt::format(" calling errorHandler for {} chip {} = {}\n", + o2::mch::raw::asString(mDsId), chip, error); +#endif + SampaErrorHandler handler = mDecodedDataHandlers.sampaErrorHandler; + if (handler) { + handler(mDsId, chip, error); + } } template <typename CHARGESUM> @@ -354,9 +373,11 @@ void UserLogicElinkDecoder<CHARGESUM>::setClusterSize(uint10_t value) mErrorMessage = std::nullopt; if (mClusterSize == 0) { mErrorMessage = "cluster size is zero"; + sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadClusterSize)); } if (checkSize > 0) { mErrorMessage = "number of samples bigger than nof10BitWords"; + sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadClusterSize)); } #ifdef ULDEBUG debugHeader() << (*this) << " --> size=" << mClusterSize << " samples=" << mSamplesToRead << "\n"; @@ -401,7 +422,7 @@ void UserLogicElinkDecoder<CHARGESUM>::setSample(uint10_t sample) template <> void UserLogicElinkDecoder<SampleMode>::prepareAndSendCluster() { - if (mSampaChannelHandler) { + if (mDecodedDataHandlers.sampaChannelHandler) { SampaCluster sc(mClusterTime, mSampaHeader.bunchCrossingCounter(), mSamples); sendCluster(sc); } @@ -414,9 +435,11 @@ void UserLogicElinkDecoder<ChargeSumMode>::prepareAndSendCluster() if (mSamples.size() != 2) { throw std::invalid_argument(fmt::format("expected sample size to be 2 but it is {}", mSamples.size())); } - uint32_t q = (((static_cast<uint32_t>(mSamples[1]) & 0x3FF) << 10) | (static_cast<uint32_t>(mSamples[0]) & 0x3FF)); - SampaCluster sc(mClusterTime, mSampaHeader.bunchCrossingCounter(), q, mClusterSize); - sendCluster(sc); + if (mDecodedDataHandlers.sampaChannelHandler) { + uint32_t q = (((static_cast<uint32_t>(mSamples[1]) & 0x3FF) << 10) | (static_cast<uint32_t>(mSamples[0]) & 0x3FF)); + SampaCluster sc(mClusterTime, mSampaHeader.bunchCrossingCounter(), q, mClusterSize); + sendCluster(sc); + } mSamples.clear(); } @@ -441,7 +464,7 @@ std::ostream& operator<<(std::ostream& os, const o2::mch::raw::UserLogicElinkDec for (auto s : e.mSamples) { os << fmt::format("{:4d} ", s); } - if (!e.mSampaChannelHandler) { + if (!e.mDecodedDataHandlers.sampaChannelHandler) { os << " empty handler "; } diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicEndpointDecoder.h b/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicEndpointDecoder.h index 8eefdf5d3348d..a5a325f72db57 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicEndpointDecoder.h +++ b/Detectors/MUON/MCH/Raw/Decoder/src/UserLogicEndpointDecoder.h @@ -13,7 +13,7 @@ #include <array> #include "UserLogicElinkDecoder.h" -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" #include <gsl/span> #include <fmt/printf.h> #include <fmt/format.h> @@ -48,7 +48,7 @@ class UserLogicEndpointDecoder : public PayloadDecoder<UserLogicEndpointDecoder< /// \param sampaChannelHandler the callable that will handle each SampaCluster UserLogicEndpointDecoder(uint16_t feeId, std::function<std::optional<uint16_t>(FeeLinkId id)> fee2SolarMapper, - SampaChannelHandler sampaChannelHandler); + DecodedDataHandlers decodedDataHandlers); /** @name Main interface */ @@ -74,7 +74,7 @@ class UserLogicEndpointDecoder : public PayloadDecoder<UserLogicEndpointDecoder< private: uint16_t mFeeId; std::function<std::optional<uint16_t>(FeeLinkId id)> mFee2SolarMapper; - SampaChannelHandler mChannelHandler; + DecodedDataHandlers mDecodedDataHandlers; std::map<uint16_t, std::array<ElinkDecoder, 40>> mElinkDecoders; int mNofGbtWordsSeens; }; @@ -85,11 +85,11 @@ using namespace boost::multiprecision; template <typename CHARGESUM> UserLogicEndpointDecoder<CHARGESUM>::UserLogicEndpointDecoder(uint16_t feeId, std::function<std::optional<uint16_t>(FeeLinkId id)> fee2SolarMapper, - SampaChannelHandler sampaChannelHandler) - : PayloadDecoder<UserLogicEndpointDecoder<CHARGESUM>>(sampaChannelHandler), + DecodedDataHandlers decodedDataHandlers) + : PayloadDecoder<UserLogicEndpointDecoder<CHARGESUM>>(decodedDataHandlers), mFeeId{feeId}, mFee2SolarMapper{fee2SolarMapper}, - mChannelHandler(sampaChannelHandler), + mDecodedDataHandlers(decodedDataHandlers), mNofGbtWordsSeens{0} { } @@ -123,7 +123,13 @@ size_t UserLogicEndpointDecoder<CHARGESUM>::append(Payload buffer) int gbt = (word >> 59) & 0x1F; if (gbt < 0 || gbt > 11) { - throw fmt::format("warning : out-of-range gbt {} word={:08X}\n", gbt, word); + SampaErrorHandler handler = mDecodedDataHandlers.sampaErrorHandler; + if (handler) { + DsElecId dsId{static_cast<uint16_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(0)}; + handler(dsId, -1, ErrorBadLinkID); + } else { + throw fmt::format("warning : out-of-range gbt {} word={:08X}\n", gbt, word); + } } // Get the corresponding decoders array, or allocate it if does not exist yet @@ -136,19 +142,34 @@ size_t UserLogicEndpointDecoder<CHARGESUM>::append(Payload buffer) auto solarId = mFee2SolarMapper(feeLinkId); if (!solarId.has_value()) { - throw std::logic_error(fmt::format("{} Could not get solarId from feeLinkId={}\n", __PRETTY_FUNCTION__, asString(feeLinkId))); + SampaErrorHandler handler = mDecodedDataHandlers.sampaErrorHandler; + if (handler) { + DsElecId dsId{static_cast<uint16_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(0)}; + handler(dsId, -1, ErrorUnknownLinkID); + } else { + throw std::logic_error(fmt::format("{} Could not get solarId from feeLinkId={}\n", __PRETTY_FUNCTION__, asString(feeLinkId))); + } } mElinkDecoders.emplace(static_cast<uint16_t>(gbt), impl::makeArray<40>([=](size_t i) { DsElecId dselec{solarId.value(), static_cast<uint8_t>(i / 5), static_cast<uint8_t>(i % 5)}; - return ElinkDecoder(dselec, mChannelHandler); + return ElinkDecoder(dselec, mDecodedDataHandlers); })); d = mElinkDecoders.find(gbt); } // in the 14 MSB(Most Significant Bits) 6 are used to specify the Dual Sampa index (0..39) uint16_t dsid = (word >> 53) & 0x3F; + if (dsid > 39) { + SampaErrorHandler handler = mDecodedDataHandlers.sampaErrorHandler; + if (handler) { + DsElecId dsId{static_cast<uint16_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(0)}; + handler(dsId, -1, ErrorBadELinkID); + } else { + throw fmt::format("warning : out-of-range DS ID {} word={:08X}\n", dsid, word); + } + } // bits 50..52 are error bits int8_t error = static_cast<uint8_t>((word >> 50) & 0x7); diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/testBareElinkDecoder.cxx b/Detectors/MUON/MCH/Raw/Decoder/src/testBareElinkDecoder.cxx index e8447c580a14c..4dd218bb18d4a 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/testBareElinkDecoder.cxx +++ b/Detectors/MUON/MCH/Raw/Decoder/src/testBareElinkDecoder.cxx @@ -49,7 +49,9 @@ BOOST_AUTO_TEST_CASE(Decoding10) helper(dsId, channel, sh); }; - BareElinkDecoder<SampleMode> e(DsElecId{0, 0, 0}, hp); + DecodedDataHandlers rh; + rh.sampaChannelHandler = hp; + BareElinkDecoder<SampleMode> e(DsElecId{0, 0, 0}, rh); std::string enc("1100100010000000000011110000001010101010101010101011111010011100000000000010000000000000000000000000100000000000101000000010100000010000100100100000000000101000000000000000000000000100000000001001100000100110001010011000111110100110100000000000101100000000000000000000001100000000001000001000100000101010000010011000001001001000010110000000000011111000000000000000000000001000000000110110010011011001101101100101110110011111011001"); @@ -70,7 +72,9 @@ BOOST_AUTO_TEST_CASE(Decoding20) helper(dsId, channel, sh); }; - BareElinkDecoder<ChargeSumMode> e(DsElecId{0, 0, 0}, hp); + DecodedDataHandlers rh; + rh.sampaChannelHandler = hp; + BareElinkDecoder<ChargeSumMode> e(DsElecId{0, 0, 0}, rh); std::string enc("11001000100000000000111100000010101010101010101010110110100100100000000000100000000000000000000000001000000000001010000010100110000000000000010000100100100000000000101000000000000000000000001000000000001001100010011111100000000000000110100100100000000000101100000000000000000000001000000000001000001010000100101000000000110110100100100000000000111110000000000000000000001000000000001101100111011100001100000000"); for (int i = 0; i < enc.size() - 1; i += 2) { @@ -82,7 +86,7 @@ BOOST_AUTO_TEST_CASE(Decoding20) // same thing but with a decoder without a channel handler // so we don't "see" any packet in this case npackets = 0; - BareElinkDecoder<ChargeSumMode> e2(DsElecId{0, 0, 0}, nullptr); + BareElinkDecoder<ChargeSumMode> e2(DsElecId{0, 0, 0}, DecodedDataHandlers{}); for (int i = 0; i < enc.size() - 1; i += 2) { e2.append(enc[i] == 1, enc[i + 1] == 1); } diff --git a/Detectors/MUON/MCH/Raw/Decoder/src/testUserLogicEndpointDecoder.cxx b/Detectors/MUON/MCH/Raw/Decoder/src/testUserLogicEndpointDecoder.cxx index 80e3e60cf0117..9ceb3cfa92495 100644 --- a/Detectors/MUON/MCH/Raw/Decoder/src/testUserLogicEndpointDecoder.cxx +++ b/Detectors/MUON/MCH/Raw/Decoder/src/testUserLogicEndpointDecoder.cxx @@ -22,7 +22,7 @@ #include "MCHRawCommon/DataFormats.h" #include "MCHRawCommon/SampaHeader.h" #include "MCHRawDecoder/PageDecoder.h" -#include "MCHRawDecoder/SampaChannelHandler.h" +#include "MCHRawDecoder/DecodedDataHandlers.h" #include "MCHRawEncoderPayload/DataBlock.h" #include "MCHRawEncoderPayload/PayloadEncoder.h" #include "MoveBuffer.h" @@ -33,7 +33,74 @@ #include <iostream> using namespace o2::mch::raw; -using o2::header::RAWDataHeaderV4; + +const uint64_t CruPageOK[] = { + 0x00000A0000124006ul, + 0x000C4C0F00A000A0ul, + 0x010E853D00000570ul, + 0x0000000000000000ul, + 0x0000000000006000ul, + 0x0000000000000000ul, + 0x0000000000000000ul, + 0x0000000000000000ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x3F04ECA103E5Cul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x0000040215C0Dul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x00000C0301004ul, + ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x0000000000400ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1F080CA100E4Dul, + ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x00044C0100001ul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul}; + +const uint64_t CruPageBadClusterSize[] = { + 0x00000A0000124006ul, + 0x000C4C0F00A000A0ul, + 0x010E853D00000570ul, + 0x0000000000000000ul, + 0x0000000000006000ul, + 0x0000000000000000ul, + 0x0000000000000000ul, + 0x0000000000000000ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x3F04ECA103E5Cul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x0000040215C0Eul, // <== the cluster size is increased from 13 (0xD) to 14 (0xE) + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x00000C0301004ul, // now the cluster size does not match anymore with the + ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x0000000000400ul, // number of 10-bit words in the SAMPA header, which will trigger + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, // a ErrorBadClusterSize error. + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1F080CA100E4Dul, + ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x00044C0100001ul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul}; + +const uint64_t CruPageBadN10bitWords[] = { + 0x00000A0000124006ul, + 0x000C4C0F00A000A0ul, + 0x010E853D00000570ul, + 0x0000000000000000ul, + 0x0000000000006000ul, + 0x0000000000000000ul, + 0x0000000000000000ul, + 0x0000000000000000ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x3F04ECA103E5Cul, + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x0000040215C08ul, // <== the cluster size is decreased from 13 (0xD) to 8 (0x8) + //((0x0200ul<<50)&0xFFFC000000000000ul) + 0x00000C0301004ul, // and one 50-bit word is removed. In this case the cluster + ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x0000000000400ul, // size matches the number of samples in the data, but the + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, // end of the SAMPA packet arrives too early with respect to + ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1F080CA100E4Dul, // the number of 10-bit words in the SAMPA header. This will + ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x00044C0100001ul, // trigger a ErrorBadIncompleteWord error. + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, // <== a word is added at the end in order to match the + ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul // payload size in the RDH +}; SampaChannelHandler handlePacket(std::string& result) { @@ -50,6 +117,14 @@ SampaChannelHandler handlePacket(std::string& result) }; } +SampaErrorHandler handleError(std::string& result) +{ + return [&result](DsElecId dsId, int8_t chip, uint32_t error) { + result += fmt::format("{}-chip-{}-error-{}", asString(dsId), chip, error); + result += "\n"; + }; +} + std::vector<std::byte> convertBuffer2PayloadBuffer(gsl::span<const std::byte> buffer, std::optional<size_t> insertSync = std::nullopt) { @@ -89,7 +164,10 @@ std::string decodeBuffer(int feeId, gsl::span<const std::byte> buffer) { std::string results; auto fee2solar = createFeeLink2SolarMapper<ElectronicMapperGenerated>(); - UserLogicEndpointDecoder<CHARGESUM> dec(feeId, fee2solar, handlePacket(results)); + DecodedDataHandlers handlers; + handlers.sampaChannelHandler = handlePacket(results); + handlers.sampaErrorHandler = handleError(results); + UserLogicEndpointDecoder<CHARGESUM> dec(feeId, fee2solar, handlers); dec.append(buffer); return results; } @@ -139,6 +217,27 @@ std::string testPayloadDecode(DsElecId ds1, return decodeBuffer<CHARGESUM>(feeId, payloadBuffer); } +std::string testPayloadDecodeCruPages(gsl::span<const uint64_t> page) +{ + auto solar2feelink = createSolar2FeeLinkMapper<ElectronicMapperGenerated>(); + + const void* rdhP = reinterpret_cast<const void*>(page.data()); + uint16_t feeId = o2::raw::RDHUtils::getFEEID(rdhP); + auto rdhSize = o2::raw::RDHUtils::getHeaderSize(rdhP); + auto payloadSize = o2::raw::RDHUtils::getMemorySize(rdhP) - rdhSize; + + gsl::span<const std::byte> buffer(reinterpret_cast<const std::byte*>(page.data()), page.size() * 8); + gsl::span<const std::byte> payloadBuffer = buffer.subspan(rdhSize, payloadSize); + + const uint16_t CRUID_MASK = 0xFF; + const uint16_t CHARGESUM_MASK = 0x100; + if (feeId & CHARGESUM_MASK) { + return decodeBuffer<ChargeSumMode>(feeId & CRUID_MASK, payloadBuffer); + } else { + return decodeBuffer<SampleMode>(feeId & CRUID_MASK, payloadBuffer); + } +} + BOOST_AUTO_TEST_SUITE(o2_mch_raw) BOOST_AUTO_TEST_SUITE(userlogicdsdecoder) @@ -227,5 +326,31 @@ BOOST_AUTO_TEST_CASE(SyncInTheMiddleChargeSumModeTwoChannels) "S361-J6-DS2-ch-63-ts-346-q-789012-cs-345\n"); } +BOOST_AUTO_TEST_CASE(TestCruPageOK) +{ + gsl::span<const uint64_t> page = CruPageOK; + std::string r = testPayloadDecodeCruPages(page); + BOOST_CHECK_EQUAL(r, + "S81-J0-DS0-ch-42-ts-87-q-2-1-0-4-4-3-3-0-0-1-0-0-0\n" + "S81-J0-DS0-ch-42-ts-0-q-1\n"); +} + +BOOST_AUTO_TEST_CASE(TestCruPageBadClusterSize) +{ + gsl::span<const uint64_t> page = CruPageBadClusterSize; + std::string r = testPayloadDecodeCruPages(page); + BOOST_CHECK_EQUAL(r, + fmt::format("S81-J0-DS0-chip-1-error-{}\nS81-J0-DS0-ch-42-ts-0-q-1\n", ErrorBadClusterSize)); +} + +BOOST_AUTO_TEST_CASE(TestCruPageBadN10bitWords) +{ + gsl::span<const uint64_t> page = CruPageBadN10bitWords; + std::string r = testPayloadDecodeCruPages(page); + BOOST_CHECK_EQUAL(r, + fmt::format("S81-J0-DS0-ch-42-ts-87-q-2-1-0-0-1-0-0-0\nS81-J0-DS0-chip-1-error-{}\nS81-J0-DS0-ch-42-ts-0-q-1\n", + ErrorBadIncompleteWord)); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/Detectors/MUON/MCH/Raw/Tools/rawdump.cxx b/Detectors/MUON/MCH/Raw/Tools/rawdump.cxx index 2f5a5a3380647..3b2846c51c4d0 100644 --- a/Detectors/MUON/MCH/Raw/Tools/rawdump.cxx +++ b/Detectors/MUON/MCH/Raw/Tools/rawdump.cxx @@ -177,7 +177,9 @@ std::map<std::string, ChannelStat> rawdump(std::string input, DumpOptions opt) npages++; bytesRead += in.gcount(); if (!decode) { - decode = createPageDecoder(page, channelHandler); + DecodedDataHandlers handlers; + handlers.sampaChannelHandler = channelHandler; + decode = createPageDecoder(page, handlers); } patchPage(buffer); decode(page); diff --git a/Detectors/MUON/MCH/Raw/test/testClosureCoDec.cxx b/Detectors/MUON/MCH/Raw/test/testClosureCoDec.cxx index e5727cdc8acee..08ab1a50947bb 100644 --- a/Detectors/MUON/MCH/Raw/test/testClosureCoDec.cxx +++ b/Detectors/MUON/MCH/Raw/test/testClosureCoDec.cxx @@ -134,7 +134,9 @@ bool testDecode(gsl::span<const std::byte> testBuffer, gsl::span<std::string> ex { std::vector<std::string> result; - auto pageDecoder = createPageDecoder(testBuffer, handlePacketStoreAsVec(result)); + DecodedDataHandlers handlers; + handlers.sampaChannelHandler = handlePacketStoreAsVec(result); + auto pageDecoder = createPageDecoder(testBuffer, handlers); auto parser = createPageParser(); diff --git a/Detectors/MUON/MCH/Simulation/CMakeLists.txt b/Detectors/MUON/MCH/Simulation/CMakeLists.txt index 3cc9ca36c5694..0952360e2f5cb 100644 --- a/Detectors/MUON/MCH/Simulation/CMakeLists.txt +++ b/Detectors/MUON/MCH/Simulation/CMakeLists.txt @@ -11,30 +11,18 @@ o2_add_library(MCHSimulation SOURCES src/Detector.cxx src/Digitizer.cxx - src/Geometry.cxx - src/GeometryTest.cxx src/Hit.cxx - src/Materials.cxx - src/Materials.h - src/Response.cxx - src/Station1Geometry.cxx - src/Station1Geometry.h - src/Station2Geometry.cxx - src/Station2Geometry.h - src/Station345Geometry.cxx - src/Station345Geometry.h src/Stepper.cxx src/Stepper.h + src/Response.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::MCHBase O2::DetectorsPassive O2::MCHMappingImpl4 O2::DetectorsBase - RapidJSON::RapidJSON) + O2::MCHGeometryCreator) o2_target_root_dictionary(MCHSimulation HEADERS include/MCHSimulation/Detector.h include/MCHSimulation/Digitizer.h - include/MCHSimulation/Geometry.h - include/MCHSimulation/GeometryTest.h include/MCHSimulation/Hit.h include/MCHSimulation/Response.h) @@ -42,7 +30,4 @@ o2_data_file(COPY data DESTINATION Detectors/MCH/simulation) if(BUILD_TESTING) add_subdirectory(test) - o2_add_test_root_macro(macros/drawMCHGeometry.C - PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase - LABELS "muon;mch") endif() diff --git a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Digitizer.h b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Digitizer.h index dd2f177dc2888..72d727241d9c3 100644 --- a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Digitizer.h +++ b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Digitizer.h @@ -40,6 +40,7 @@ class Digitizer void process(const std::vector<Hit> hits, std::vector<Digit>& digits, o2::dataformats::MCTruthContainer<o2::MCCompLabel>& mcContainer); void provideMC(o2::dataformats::MCTruthContainer<o2::MCCompLabel>& mcContainer); void mergeDigits(); + void generateNoiseDigits(); //external pile-up adding up void mergeDigits(std::vector<Digit>& digits, o2::dataformats::MCTruthContainer<o2::MCCompLabel>& mcContainer); @@ -56,22 +57,33 @@ class Digitizer void setEventID(int v); int getEventID() const { return mEventID; } + void setNoise(bool val) { mNoise = val; } + bool isNoise() const { return mNoise; } + //for debugging std::vector<Digit> getDigits() { return mDigits; } std::vector<o2::MCCompLabel> getTrackLabels() { return mTrackLabels; } private: - double mEventTime; + int mEventTime; int mEventID = 0; int mSrcID = 0; bool mContinuous = false; + bool mNoise = true; //time difference allowed for pileup (in ns (assuming that event time is in ns)) float mDeltat = 100.; //number of detector elements const static int mNdE = 156; + + //noise above threshold probability within read-out window + float mProbNoise = 1e-5; + //sum_i 1/padcount_i where i is the detelemID + float mInvPadSum = 0.0450832; + float mNormProbNoise = mProbNoise / mInvPadSum; + // digit per pad std::vector<Digit> mDigits; @@ -80,7 +92,7 @@ class Digitizer //MCLabel container (output) o2::dataformats::MCTruthContainer<o2::MCCompLabel> mMCTruthOutputContainer; - int processHit(const Hit& hit, int detID, double event_time); + int processHit(const Hit& hit, int detID, int event_time); }; } // namespace mch diff --git a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Geometry.h b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Geometry.h deleted file mode 100644 index 958ec940a117d..0000000000000 --- a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Geometry.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Geometry.h -/// @brief Interface for MCH geometry creation - -#ifndef O2_MCH_SIMULATION_GEOMETRY_H -#define O2_MCH_SIMULATION_GEOMETRY_H - -#include <vector> -#include <iostream> -#include "MathUtils/Cartesian3D.h" - -class TGeoVolume; -class TGeoManager; - -namespace o2 -{ -namespace mch -{ - -/// createGeometry creates MCH geometry and attach it to existing topVolume -void createGeometry(TGeoVolume& topVolume); - -/// get a list of MCH sensitive volumes -std::vector<TGeoVolume*> getSensitiveVolumes(); - -/// Add alignable mch volumes -void addAlignableVolumesMCH(); - -/// get the local-to-global transformation for a given detection element -o2::Transform3D getTransformation(int detElemId, const TGeoManager& geo); - -} // namespace mch -} // namespace o2 - -#endif // O2_MCH_SIMULATION_GEOMETRY_H diff --git a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/GeometryTest.h b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/GeometryTest.h deleted file mode 100644 index 52815f9d5e14b..0000000000000 --- a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/GeometryTest.h +++ /dev/null @@ -1,65 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_MCH_SIMULATION_GEOMETRYTEST_H -#define O2_MCH_SIMULATION_GEOMETRYTEST_H - -#include <iostream> - -class TH2; - -namespace o2 -{ -namespace mch -{ -namespace test -{ - -/// creates MCH geometry from scratch (i.e. from a null TGeoManager) -/// usefull for tests or drawing for instance. -void createStandaloneGeometry(); - -/// creates MCH geometry with the beam shielding and the dipole -/// from scratch (i.e. from a null TGeoManager). -/// usefull for tests. -void createRegularGeometry(); - -/// creates MCH regular geometry and adds alignable volumes -/// useull for tests. -void addAlignableVolumes(); - -/// tree like textual dump of the geometry nodes -void showGeometryAsTextTree(const char* fromPath = "", int maxdepth = 2, std::ostream& out = std::cout); - -/// basic drawing of the geometry -void drawGeometry(); - -/// set the volume and daughter visibility for all volumes with a name matching the regexp pattern -void setVolumeVisibility(const char* pattern, bool visible, bool visibleDaughters); - -/// set the volume line and fill for all volumes with a name matching the regexp pattern -void setVolumeColor(const char* pattern, int lineColor, int fillColor); -inline void setVolumeColor(const char* pattern, int color) -{ - setVolumeColor(pattern, color, color); -} - -/// get a radlen radiograph of a given detection element within box with the given granularity -TH2* getRadio(int detElemId, float xmin, float ymin, float xmax, float ymax, float xstep, float ystep, float thickness = 5 /* cm */); - -class Dummy -{ - // to force Root produce a dictionary for namespace test (seems it is doing it fully if there are only functions in the namespace) -}; -} // namespace test -} // namespace mch -} // namespace o2 - -#endif diff --git a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Hit.h b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Hit.h index e8d0c0a0b2cff..8d0d639b39271 100644 --- a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Hit.h +++ b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Hit.h @@ -23,20 +23,20 @@ class Hit : public ::o2::BasicXYZEHit<float> { public: - Hit(int trackId = 0, short detElemId = 0, Point3D<float> entrancePoint = {}, const Point3D<float> exitPoint = {}, + Hit(int trackId = 0, short detElemId = 0, math_utils::Point3D<float> entrancePoint = {}, const math_utils::Point3D<float> exitPoint = {}, float eloss = 0.0, float length = 0.0, float tof = 0.0) : ::o2::BasicXYZEHit<float>(entrancePoint.x(), entrancePoint.y(), entrancePoint.z(), tof, eloss, trackId, detElemId), mLength{length}, mExitPoint(exitPoint) { } - Point3D<float> entrancePoint() const { return GetPos(); } - Point3D<float> exitPoint() const { return mExitPoint; } + math_utils::Point3D<float> entrancePoint() const { return GetPos(); } + math_utils::Point3D<float> exitPoint() const { return mExitPoint; } short detElemId() const { return GetDetectorID(); } private: float mLength = {}; - Point3D<float> mExitPoint = {}; + math_utils::Point3D<float> mExitPoint = {}; ClassDefNV(Hit, 1); }; diff --git a/Detectors/MUON/MCH/Simulation/macros/drawMCHGeometry.C b/Detectors/MUON/MCH/Simulation/macros/drawMCHGeometry.C deleted file mode 100644 index 82b56b2fafbdd..0000000000000 --- a/Detectors/MUON/MCH/Simulation/macros/drawMCHGeometry.C +++ /dev/null @@ -1,8 +0,0 @@ -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "MCHSimulation/GeometryTest.h" -#endif - -void drawMCHGeometry() -{ - o2::mch::test::drawGeometry(); -} diff --git a/Detectors/MUON/MCH/Simulation/src/Detector.cxx b/Detectors/MUON/MCH/Simulation/src/Detector.cxx index 6d244e77a4151..e77e6930837d4 100644 --- a/Detectors/MUON/MCH/Simulation/src/Detector.cxx +++ b/Detectors/MUON/MCH/Simulation/src/Detector.cxx @@ -9,7 +9,7 @@ // or submit itself to any jurisdiction. #include "MCHSimulation/Detector.h" -#include "MCHSimulation/Geometry.h" +#include "MCHGeometryCreator/Geometry.h" #include "SimulationDataFormat/Stack.h" #include "Stepper.h" #include "TGeoManager.h" @@ -42,7 +42,7 @@ Detector::~Detector() void Detector::defineSensitiveVolumes() { - for (auto* vol : getSensitiveVolumes()) { + for (auto* vol : geo::getSensitiveVolumes()) { AddSensitiveVolume(vol); } } @@ -58,7 +58,7 @@ void Detector::ConstructGeometry() if (!top) { throw std::runtime_error("Cannot create MCH geometry without a top volume"); } - createGeometry(*top); + geo::createGeometry(*gGeoManager, *top); } void Detector::addAlignableVolumes() const @@ -66,7 +66,7 @@ void Detector::addAlignableVolumes() const if (!gGeoManager) { throw std::runtime_error("Cannot add alignable volumes without TGeoManager"); } - addAlignableVolumesMCH(); + geo::addAlignableVolumes(*gGeoManager); } Bool_t Detector::ProcessHits(FairVolume* v) diff --git a/Detectors/MUON/MCH/Simulation/src/Digitizer.cxx b/Detectors/MUON/MCH/Simulation/src/Digitizer.cxx index c9b7773eb6fe6..8ea0b4514f99b 100644 --- a/Detectors/MUON/MCH/Simulation/src/Digitizer.cxx +++ b/Detectors/MUON/MCH/Simulation/src/Digitizer.cxx @@ -11,14 +11,19 @@ #include "MCHSimulation/Digitizer.h" #include "MCHMappingInterface/Segmentation.h" -#include "MCHSimulation/Geometry.h" +#include "MCHGeometryCreator/Geometry.h" +#include "MCHGeometryTransformer/Transformations.h" #include "MCHSimulation/Response.h" #include "TGeoManager.h" #include "TMath.h" +#include "TRandom.h" #include <algorithm> #include <cassert> #include <fairlogger/Logger.h> +#include <iostream> +using namespace std; + using namespace o2::mch; namespace @@ -97,23 +102,32 @@ void Digitizer::process(const std::vector<Hit> hits, std::vector<Digit>& digits, mMCTruthOutputContainer.addElement(digitIndex, label); } //loop over digits to generate MCdigits } //loop over hits + + //generate noise-only digits + if (mNoise) { + generateNoiseDigits(); + } + fillOutputContainer(digits); provideMC(mcContainer); } //______________________________________________________________________ -int Digitizer::processHit(const Hit& hit, int detID, double event_time) +int Digitizer::processHit(const Hit& hit, int detID, int event_time) { - Point3D<float> pos(hit.GetX(), hit.GetY(), hit.GetZ()); + math_utils::Point3D<float> pos(hit.GetX(), hit.GetY(), hit.GetZ()); Response& resp = response(isStation1(detID)); //convert energy to charge auto charge = resp.etocharge(hit.GetEnergyLoss()); - auto time = event_time + hit.GetTime(); + + //convert float ns time to BC counts + auto time = event_time & int(hit.GetTime() / 25.); //transformation from global to local - auto t = o2::mch::getTransformation(detID, *gGeoManager); - Point3D<float> lpos; + auto transformation = o2::mch::geo::transformationFromTGeoManager(*gGeoManager); + auto t = transformation(detID); + math_utils::Point3D<float> lpos; t.MasterToLocal(pos, lpos); auto anodpos = resp.getAnod(lpos.X()); @@ -170,6 +184,31 @@ int Digitizer::processHit(const Hit& hit, int detID, double event_time) return ndigits; } //______________________________________________________________________ +void Digitizer::generateNoiseDigits() +{ + + o2::mch::mapping::forEachDetectionElement([& digits = this->mDigits, &normProbNoise = this->mNormProbNoise, + &eventTime = this->mEventTime, &eventID = this->mEventID, + &srcID = this->mSrcID, &mcTruthOutputContainer = this->mMCTruthOutputContainer](int detID) { + auto& seg = segmentation(detID); + auto nPads = seg.nofPads(); + auto nNoisyPadsAv = (float)nPads * normProbNoise; + int nNoisyPads = TMath::Nint(gRandom->Gaus(nNoisyPadsAv, TMath::Sqrt(nNoisyPadsAv))); + for (int i = 0; i < nNoisyPads; i++) { + int padid = gRandom->Integer(nNoisyPads + 1); + Digit::Time dtime; + dtime.sampaTime = static_cast<uint16_t>(eventTime) & 0x3FF; + digits.emplace_back(detID, padid, 0.6, dtime); + //just to roun adbove threshold when added + MCCompLabel label(-1, eventID, srcID, true); + mcTruthOutputContainer.addElement(digits.size() - 1, label); + } + }); + //not clear how to normalise to time: + //assume that only "one" event equivalent, + //otherwise the probability will strongly depend on read-out-frame time length +} +//______________________________________________________________________ void Digitizer::mergeDigits() { std::vector<int> indices(mDigits.size()); @@ -249,8 +288,9 @@ void Digitizer::mergeDigits(std::vector<Digit>& digits, o2::dataformats::MCTruth void Digitizer::fillOutputContainer(std::vector<Digit>& digits) { // filling the digit container - if (mDigits.empty()) + if (mDigits.empty()) { return; + } digits.clear(); digits.reserve(mDigits.size()); @@ -285,8 +325,9 @@ void Digitizer::provideMC(o2::dataformats::MCTruthContainer<o2::MCCompLabel>& mc { //fill MCtruth info mcContainer.clear(); - if (mMCTruthOutputContainer.getNElements() == 0) + if (mMCTruthOutputContainer.getNElements() == 0) { return; + } //need to fill groups of labels not only single labels, since index in addElements // is the data index diff --git a/Detectors/MUON/MCH/Simulation/src/Geometry.cxx b/Detectors/MUON/MCH/Simulation/src/Geometry.cxx deleted file mode 100644 index ea8976b91e6f7..0000000000000 --- a/Detectors/MUON/MCH/Simulation/src/Geometry.cxx +++ /dev/null @@ -1,654 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "MCHSimulation/Geometry.h" - -#include "Station1Geometry.h" -#include "Station2Geometry.h" -#include "Station345Geometry.h" -#include "Materials.h" -#include <iostream> -#include <fmt/format.h> -#include "TGeoVolume.h" -#include "TGeoManager.h" -#include "Framework/Logger.h" - -namespace o2 -{ -namespace mch -{ -void createGeometry(TGeoVolume& topVolume) -{ - createMaterials(); - - auto volYOUT1 = gGeoManager->GetVolume("YOUT1"); - - createStation1Geometry((volYOUT1) ? *volYOUT1 : topVolume); - createStation2Geometry((volYOUT1) ? *volYOUT1 : topVolume); - - createStation345Geometry(topVolume); -} - -std::vector<TGeoVolume*> getSensitiveVolumes() -{ - auto st1 = getStation1SensitiveVolumes(); - auto st2 = getStation2SensitiveVolumes(); - auto st345 = getStation345SensitiveVolumes(); - - auto vol = st1; - vol.insert(vol.end(), st2.begin(), st2.end()); - vol.insert(vol.end(), st345.begin(), st345.end()); - - return vol; -} - -namespace impl -{ -// return the path of the mother volume of detElemId, relative to MCH geometry -// (i.e. excluding general top node) -std::string getVolumePathName(int detElemId) -{ - std::string vp{"incorrect detElemId"}; - - if (detElemId == 100) { - return "SC01I_0/Quadrant (chamber 1)_100"; - } - if (detElemId == 103) { - return "SC01I_0/Quadrant (chamber 1)_103"; - } - if (detElemId == 101) { - return "SC01O_1/Quadrant (chamber 1)_101"; - } - if (detElemId == 102) { - return "SC01O_1/Quadrant (chamber 1)_102"; - } - if (detElemId == 200) { - return "SC02I_2/Quadrant (chamber 2)_200"; - } - if (detElemId == 203) { - return "SC02I_2/Quadrant (chamber 2)_203"; - } - if (detElemId == 201) { - return "SC02O_3/Quadrant (chamber 2)_201"; - } - if (detElemId == 202) { - return "SC02O_3/Quadrant (chamber 2)_202"; - } - if (detElemId == 300) { - return "SC03I_4/Station 2 quadrant_300"; - } - if (detElemId == 303) { - return "SC03I_4/Station 2 quadrant_303"; - } - if (detElemId == 301) { - return "SC03O_5/Station 2 quadrant_301"; - } - if (detElemId == 302) { - return "SC03O_5/Station 2 quadrant_302"; - } - if (detElemId == 400) { - return "SC04I_6/Station 2 quadrant_400"; - } - if (detElemId == 403) { - return "SC04I_6/Station 2 quadrant_403"; - } - if (detElemId == 401) { - return "SC04O_7/Station 2 quadrant_401"; - } - if (detElemId == 402) { - return "SC04O_7/Station 2 quadrant_402"; - } - if (detElemId == 500) { - return "SC05I_8/122000SR1_500"; - } - if (detElemId == 501) { - return "SC05I_8/112200SR2_501"; - } - if (detElemId == 502) { - return "SC05I_8/122200S_502"; - } - if (detElemId == 503) { - return "SC05I_8/222000N_503"; - } - if (detElemId == 504) { - return "SC05I_8/220000N_504"; - } - if (detElemId == 514) { - return "SC05I_8/220000N_514"; - } - if (detElemId == 515) { - return "SC05I_8/222000N_515"; - } - if (detElemId == 516) { - return "SC05I_8/122200S_516"; - } - if (detElemId == 517) { - return "SC05I_8/112200SR2_517"; - } - if (detElemId == 505) { - return "SC05O_9/220000N_505"; - } - if (detElemId == 506) { - return "SC05O_9/222000N_506"; - } - if (detElemId == 507) { - return "SC05O_9/122200S_507"; - } - if (detElemId == 508) { - return "SC05O_9/112200SR2_508"; - } - if (detElemId == 509) { - return "SC05O_9/122000SR1_509"; - } - if (detElemId == 510) { - return "SC05O_9/112200SR2_510"; - } - if (detElemId == 511) { - return "SC05O_9/122200S_511"; - } - if (detElemId == 512) { - return "SC05O_9/222000N_512"; - } - if (detElemId == 513) { - return "SC05O_9/220000N_513"; - } - if (detElemId == 600) { - return "SC06I_10/122000NR1_600"; - } - if (detElemId == 601) { - return "SC06I_10/112200NR2_601"; - } - if (detElemId == 602) { - return "SC06I_10/122200N_602"; - } - if (detElemId == 603) { - return "SC06I_10/222000N_603"; - } - if (detElemId == 604) { - return "SC06I_10/220000N_604"; - } - if (detElemId == 614) { - return "SC06I_10/220000N_614"; - } - if (detElemId == 615) { - return "SC06I_10/222000N_615"; - } - if (detElemId == 616) { - return "SC06I_10/122200N_616"; - } - if (detElemId == 617) { - return "SC06I_10/112200NR2_617"; - } - if (detElemId == 605) { - return "SC06O_11/220000N_605"; - } - if (detElemId == 606) { - return "SC06O_11/222000N_606"; - } - if (detElemId == 607) { - return "SC06O_11/122200N_607"; - } - if (detElemId == 608) { - return "SC06O_11/112200NR2_608"; - } - if (detElemId == 609) { - return "SC06O_11/122000NR1_609"; - } - if (detElemId == 610) { - return "SC06O_11/112200NR2_610"; - } - if (detElemId == 611) { - return "SC06O_11/122200N_611"; - } - if (detElemId == 612) { - return "SC06O_11/222000N_612"; - } - if (detElemId == 613) { - return "SC06O_11/220000N_613"; - } - if (detElemId == 700) { - return "SC07I_12/122330N_700"; - } - if (detElemId == 701) { - return "SC07I_12/112233NR3_701"; - } - if (detElemId == 702) { - return "SC07I_12/112230N_702"; - } - if (detElemId == 703) { - return "SC07I_12/222330N_703"; - } - if (detElemId == 704) { - return "SC07I_12/223300N_704"; - } - if (detElemId == 705) { - return "SC07I_12/333000N_705"; - } - if (detElemId == 706) { - return "SC07I_12/330000N_706"; - } - if (detElemId == 720) { - return "SC07I_12/330000N_720"; - } - if (detElemId == 721) { - return "SC07I_12/333000N_721"; - } - if (detElemId == 722) { - return "SC07I_12/223300N_722"; - } - if (detElemId == 723) { - return "SC07I_12/222330N_723"; - } - if (detElemId == 724) { - return "SC07I_12/112230N_724"; - } - if (detElemId == 725) { - return "SC07I_12/112233NR3_725"; - } - if (detElemId == 707) { - return "SC07O_13/330000N_707"; - } - if (detElemId == 708) { - return "SC07O_13/333000N_708"; - } - if (detElemId == 709) { - return "SC07O_13/223300N_709"; - } - if (detElemId == 710) { - return "SC07O_13/222330N_710"; - } - if (detElemId == 711) { - return "SC07O_13/112230N_711"; - } - if (detElemId == 712) { - return "SC07O_13/112233NR3_712"; - } - if (detElemId == 713) { - return "SC07O_13/122330N_713"; - } - if (detElemId == 714) { - return "SC07O_13/112233NR3_714"; - } - if (detElemId == 715) { - return "SC07O_13/112230N_715"; - } - if (detElemId == 716) { - return "SC07O_13/222330N_716"; - } - if (detElemId == 717) { - return "SC07O_13/223300N_717"; - } - if (detElemId == 718) { - return "SC07O_13/333000N_718"; - } - if (detElemId == 719) { - return "SC07O_13/330000N_719"; - } - if (detElemId == 800) { - return "SC08I_14/122330N_800"; - } - if (detElemId == 801) { - return "SC08I_14/112233NR3_801"; - } - if (detElemId == 802) { - return "SC08I_14/112230N_802"; - } - if (detElemId == 803) { - return "SC08I_14/222330N_803"; - } - if (detElemId == 804) { - return "SC08I_14/223300N_804"; - } - if (detElemId == 805) { - return "SC08I_14/333000N_805"; - } - if (detElemId == 806) { - return "SC08I_14/330000N_806"; - } - if (detElemId == 820) { - return "SC08I_14/330000N_820"; - } - if (detElemId == 821) { - return "SC08I_14/333000N_821"; - } - if (detElemId == 822) { - return "SC08I_14/223300N_822"; - } - if (detElemId == 823) { - return "SC08I_14/222330N_823"; - } - if (detElemId == 824) { - return "SC08I_14/112230N_824"; - } - if (detElemId == 825) { - return "SC08I_14/112233NR3_825"; - } - if (detElemId == 807) { - return "SC08O_15/330000N_807"; - } - if (detElemId == 808) { - return "SC08O_15/333000N_808"; - } - if (detElemId == 809) { - return "SC08O_15/223300N_809"; - } - if (detElemId == 810) { - return "SC08O_15/222330N_810"; - } - if (detElemId == 811) { - return "SC08O_15/112230N_811"; - } - if (detElemId == 812) { - return "SC08O_15/112233NR3_812"; - } - if (detElemId == 813) { - return "SC08O_15/122330N_813"; - } - if (detElemId == 814) { - return "SC08O_15/112233NR3_814"; - } - if (detElemId == 815) { - return "SC08O_15/112230N_815"; - } - if (detElemId == 816) { - return "SC08O_15/222330N_816"; - } - if (detElemId == 817) { - return "SC08O_15/223300N_817"; - } - if (detElemId == 818) { - return "SC08O_15/333000N_818"; - } - if (detElemId == 819) { - return "SC08O_15/330000N_819"; - } - if (detElemId == 900) { - return "SC09I_16/122330N_900"; - } - if (detElemId == 901) { - return "SC09I_16/112233NR3_901"; - } - if (detElemId == 902) { - return "SC09I_16/112233N_902"; - } - if (detElemId == 903) { - return "SC09I_16/222333N_903"; - } - if (detElemId == 904) { - return "SC09I_16/223330N_904"; - } - if (detElemId == 905) { - return "SC09I_16/333300N_905"; - } - if (detElemId == 906) { - return "SC09I_16/333000N_906"; - } - if (detElemId == 920) { - return "SC09I_16/333000N_920"; - } - if (detElemId == 921) { - return "SC09I_16/333300N_921"; - } - if (detElemId == 922) { - return "SC09I_16/223330N_922"; - } - if (detElemId == 923) { - return "SC09I_16/222333N_923"; - } - if (detElemId == 924) { - return "SC09I_16/112233N_924"; - } - if (detElemId == 925) { - return "SC09I_16/112233NR3_925"; - } - if (detElemId == 907) { - return "SC09O_17/333000N_907"; - } - if (detElemId == 908) { - return "SC09O_17/333300N_908"; - } - if (detElemId == 909) { - return "SC09O_17/223330N_909"; - } - if (detElemId == 910) { - return "SC09O_17/222333N_910"; - } - if (detElemId == 911) { - return "SC09O_17/112233N_911"; - } - if (detElemId == 912) { - return "SC09O_17/112233NR3_912"; - } - if (detElemId == 913) { - return "SC09O_17/122330N_913"; - } - if (detElemId == 914) { - return "SC09O_17/112233NR3_914"; - } - if (detElemId == 915) { - return "SC09O_17/112233N_915"; - } - if (detElemId == 916) { - return "SC09O_17/222333N_916"; - } - if (detElemId == 917) { - return "SC09O_17/223330N_917"; - } - if (detElemId == 918) { - return "SC09O_17/333300N_918"; - } - if (detElemId == 919) { - return "SC09O_17/333000N_919"; - } - if (detElemId == 1000) { - return "SC10I_18/122330N_1000"; - } - if (detElemId == 1001) { - return "SC10I_18/112233NR3_1001"; - } - if (detElemId == 1002) { - return "SC10I_18/112233N_1002"; - } - if (detElemId == 1003) { - return "SC10I_18/222333N_1003"; - } - if (detElemId == 1004) { - return "SC10I_18/223330N_1004"; - } - if (detElemId == 1005) { - return "SC10I_18/333300N_1005"; - } - if (detElemId == 1006) { - return "SC10I_18/333000N_1006"; - } - if (detElemId == 1020) { - return "SC10I_18/333000N_1020"; - } - if (detElemId == 1021) { - return "SC10I_18/333300N_1021"; - } - if (detElemId == 1022) { - return "SC10I_18/223330N_1022"; - } - if (detElemId == 1023) { - return "SC10I_18/222333N_1023"; - } - if (detElemId == 1024) { - return "SC10I_18/112233N_1024"; - } - if (detElemId == 1025) { - return "SC10I_18/112233NR3_1025"; - } - if (detElemId == 1007) { - return "SC10O_19/333000N_1007"; - } - if (detElemId == 1008) { - return "SC10O_19/333300N_1008"; - } - if (detElemId == 1009) { - return "SC10O_19/223330N_1009"; - } - if (detElemId == 1010) { - return "SC10O_19/222333N_1010"; - } - if (detElemId == 1011) { - return "SC10O_19/112233N_1011"; - } - if (detElemId == 1012) { - return "SC10O_19/112233NR3_1012"; - } - if (detElemId == 1013) { - return "SC10O_19/122330N_1013"; - } - if (detElemId == 1014) { - return "SC10O_19/112233NR3_1014"; - } - if (detElemId == 1015) { - return "SC10O_19/112233N_1015"; - } - if (detElemId == 1016) { - return "SC10O_19/222333N_1016"; - } - if (detElemId == 1017) { - return "SC10O_19/223330N_1017"; - } - if (detElemId == 1018) { - return "SC10O_19/333300N_1018"; - } - if (detElemId == 1019) { - return "SC10O_19/333000N_1019"; - } - - return vp; -} - -void addAlignableVolumesHalfChamber(int hc, std::string& parent) -{ - // - // Add alignable volumes for a half chamber and its daughters - // - std::vector<std::vector<int>> DEofHC{{100, 103}, - {101, 102}, - {200, 203}, - {201, 202}, - {300, 303}, - {301, 302}, - {400, 403}, - {401, 402}, - {500, 501, 502, 503, 504, 514, 515, 516, 517}, - {505, 506, 507, 508, 509, 510, 511, 512, 513}, - {600, 601, 602, 603, 604, 614, 615, 616, 617}, - {605, 606, 607, 608, 609, 610, 611, 612, 613}, - {700, 701, 702, 703, 704, 705, 706, 720, 721, 722, 723, 724, 725}, - {707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719}, - {800, 801, 802, 803, 804, 805, 806, 820, 821, 822, 823, 824, 825}, - {807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819}, - {900, 901, 902, 903, 904, 905, 906, 920, 921, 922, 923, 924, 925}, - {907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919}, - {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1020, 1021, 1022, 1023, 1024, 1025}, - {1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019}}; - - for (int i = 0; i < DEofHC[hc].size(); i++) { - std::string volPathName = impl::getVolumePathName(DEofHC[hc][i]); - - TString path = Form("%s%s", parent.c_str(), volPathName.c_str()); - TString sname = Form("MCH/HC%d/DE%d", hc, DEofHC[hc][i]); - - LOG(DEBUG) << "Add " << sname << " <-> " << path; - - if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { - LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; - } - } - - return; -} - -} // namespace impl - -o2::Transform3D getTransformation(int detElemId, const TGeoManager& geo) -{ - - int nCh = detElemId / 100; - - if (nCh < 1 || nCh > 10) { - throw std::runtime_error("Wrong detection element Id"); - } - - std::string volPathName = geo.GetTopVolume()->GetName(); - - if (nCh <= 4 && geo.GetVolume("YOUT1")) { - volPathName += "/YOUT1_1/"; - } else if ((nCh == 5 || nCh == 6) && geo.GetVolume("DDIP")) { - volPathName += "/DDIP_1/"; - } else if (nCh >= 7 && geo.GetVolume("YOUT2")) { - volPathName += "/YOUT2_1/"; - } else { - volPathName += "/"; - } - - volPathName += impl::getVolumePathName(detElemId); - - TGeoNavigator* navig = gGeoManager->GetCurrentNavigator(); - - if (!navig->cd(volPathName.c_str())) { - throw std::runtime_error("could not get to volPathName=" + volPathName); - } - - return o2::Transform3D{*(navig->GetCurrentMatrix())}; -} - -void addAlignableVolumesMCH() -{ - // - // Creates entries for alignable volumes associating the symbolic volume - // name with the corresponding volume path. - // - - LOG(INFO) << "Add MCH alignable volumes"; - - for (int hc = 0; hc < 20; hc++) { - int nCh = hc / 2 + 1; - - if (nCh < 1 || nCh > 10) { - throw std::runtime_error("Wrong detection element Id"); - } - - std::string volPathName = gGeoManager->GetTopVolume()->GetName(); - - if (nCh <= 4 && gGeoManager->GetVolume("YOUT1")) { - volPathName += "/YOUT1_1/"; - } else if ((nCh == 5 || nCh == 6) && gGeoManager->GetVolume("DDIP")) { - volPathName += "/DDIP_1/"; - } else if (nCh >= 7 && gGeoManager->GetVolume("YOUT2")) { - volPathName += "/YOUT2_1/"; - } else { - volPathName += "/"; - } - - std::string path = fmt::format("{0}SC{1}{2}{3}_{4}", volPathName.c_str(), nCh < 10 ? "0" : "", nCh, hc % 2 ? "O" : "I", hc); - std::string sname = fmt::format("MCH/HC{}", hc); - - LOG(DEBUG) << sname << " <-> " << path; - - if (!gGeoManager->SetAlignableEntry(sname.c_str(), path.c_str())) { - LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; - } - - Int_t lastUID = 0; - - impl::addAlignableVolumesHalfChamber(hc, volPathName); - } - - return; -} - -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Simulation/src/GeometryTest.cxx b/Detectors/MUON/MCH/Simulation/src/GeometryTest.cxx deleted file mode 100644 index c024f9dd95809..0000000000000 --- a/Detectors/MUON/MCH/Simulation/src/GeometryTest.cxx +++ /dev/null @@ -1,277 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "MCHSimulation/GeometryTest.h" - -#include "DetectorsBase/GeometryManager.h" -#include "DetectorsBase/MaterialManager.h" -#include "MCHSimulation/Geometry.h" -#include "Math/GenVector/Cartesian3D.h" -#include "TGeoManager.h" -#include "TGeoVolume.h" -#include "TH2F.h" -#include <iostream> -#include "TPRegexp.h" -#include "TGLViewer.h" -#include "TGLRnrCtx.h" -#include "TVirtualPad.h" -#include "DetectorsPassive/Cave.h" -#include "DetectorsPassive/Dipole.h" -#include "DetectorsPassive/Absorber.h" -#include "DetectorsPassive/Compensator.h" -#include "DetectorsPassive/Shil.h" -#include "DetectorsPassive/Pipe.h" -#include "MCHSimulation/Detector.h" - -namespace o2 -{ -namespace mch -{ -namespace test -{ - -TGeoVolume* createAirVacuumCave(const char* name) -{ - // create the air medium (only used for the geometry test) - auto& mgr = o2::base::MaterialManager::Instance(); - - const int nAir = 4; - Float_t aAir[nAir] = {12.0107, 14.0067, 15.9994, 39.948}; - Float_t zAir[nAir] = {6., 7., 8., 18.}; - Float_t wAir[nAir] = {0.000124, 0.755267, 0.231781, 0.012827}; - Float_t dAirVacuum = 1.20479E-10; - const int kID = 90; // to avoid conflicts with definitions of other MCH materials - - mgr.Mixture("MCH", kID, "Air", aAir, zAir, dAirVacuum, nAir, wAir); - mgr.Medium("MCH", kID, "Air", kID, - false, /* isvol */ - 0, /* ifield */ - -1.0, /* fieldm */ - -1.0, /* tmaxfd */ - -1.0, /* stemax */ - -1.0, /* deemax */ - -1.0, /* epsil */ - -1.0 /* stmin */); - return gGeoManager->MakeBox(name, gGeoManager->GetMedium("MCH_Air"), 2000.0, 2000.0, 3000.0); -} - -void dump(std::ostream& out, const TGeoNode& n, int level, int maxdepth, std::string prefix) -{ - if (level >= maxdepth) { - return; - } - - if (level == 0) { - out << n.GetName() << "\n"; - } - - if (level < maxdepth) { - for (int i = 0; i < n.GetNdaughters(); i++) { - TGeoNode* d = n.GetDaughter(i); - if (i == n.GetNdaughters() - 1) { - out << prefix + "└──" << d->GetName() - << "\n"; - dump(out, *d, level + 1, maxdepth, prefix + " "); - } else { - out << prefix + "├──" << d->GetName() - << "\n"; - dump(out, *d, level + 1, maxdepth, prefix + "│ "); - } - } - } -} - -void showGeometryAsTextTree(const char* fromPath, int maxdepth, std::ostream& out) -{ - if (!gGeoManager) { - return; - } - - TGeoNavigator* nav = gGeoManager->GetCurrentNavigator(); - - if (strlen(fromPath)) { - if (!nav->cd(fromPath)) { - std::cerr << "Could not get path " << fromPath << "\n"; - return; - } - } - - TGeoNode* node = nav->GetCurrentNode(); - - dump(out, *node, 0, maxdepth, ""); -} - -void createStandaloneGeometry() -{ - if (gGeoManager && gGeoManager->GetTopVolume()) { - std::cerr << "Can only call this function with an empty geometry, i.e. gGeoManager==nullptr " - << " or gGeoManager->GetTopVolume()==nullptr\n"; - } - TGeoManager* g = new TGeoManager("MCH-ONLY", "ALICE MCH Standalone Geometry"); - TGeoVolume* top = createAirVacuumCave("cave"); - g->SetTopVolume(top); - o2::mch::createGeometry(*top); -} - -void createRegularGeometry() -{ - if (gGeoManager && gGeoManager->GetTopVolume()) { - std::cerr << "Can only call this function with an empty geometry, i.e. gGeoManager==nullptr " - << " or gGeoManager->GetTopVolume()==nullptr\n"; - } - TGeoManager* g = new TGeoManager("MCH-BASICS", "ALICE MCH Regular Geometry"); - o2::passive::Cave("CAVE", "Cave (for MCH Basics)").ConstructGeometry(); - o2::passive::Dipole("DIPO", "Alice Dipole (for MCH Basics)").ConstructGeometry(); - o2::passive::Compensator("COMP", "Alice Compensator Dipole (for MCH Basics)").ConstructGeometry(); - o2::passive::Pipe("PIPE", "Beam pipe (for MCH Basics)").ConstructGeometry(); - o2::passive::Shil("SHIL", "Small angle beam shield (for MCH Basics)").ConstructGeometry(); - o2::passive::Absorber("ABSO", "Absorber (for MCH Basics)").ConstructGeometry(); - o2::mch::Detector(true).ConstructGeometry(); -} - -void addAlignableVolumes() -{ - if (!gGeoManager) { - std::cerr << "gGeoManager == nullptr, must create a geometry first\n"; - return; - } - // If not closed, we need to close it - if (!gGeoManager->IsClosed()) { - gGeoManager->CloseGeometry(); - } - // Then add the alignable volumes - o2::mch::Detector(true).addAlignableVolumes(); -} - -void setVolumeVisibility(const char* pattern, bool visible, bool visibleDaughters) -{ - TPRegexp re(pattern); - TIter next(gGeoManager->GetListOfVolumes()); - TGeoVolume* vol; - - while ((vol = static_cast<TGeoVolume*>(next()))) { - if (TString(vol->GetName()).Contains(re)) { - vol->SetVisibility(visible); - vol->SetVisDaughters(visibleDaughters); - } - } -} - -void setVolumeColor(const char* pattern, int lineColor, int fillColor) -{ - TPRegexp re(pattern); - TIter next(gGeoManager->GetListOfVolumes()); - TGeoVolume* vol; - - while ((vol = static_cast<TGeoVolume*>(next()))) { - if (TString(vol->GetName()).Contains(re)) { - vol->SetFillColor(fillColor); - vol->SetLineColor(lineColor); - } - } -} - -void drawOptionPresetBasic() -{ - gGeoManager->SetVisLevel(4); - - setVolumeVisibility("cave", false, true); - - // Hide to half-chamber top volumes - setVolumeVisibility("^SC", false, true); - - // Hide St345 support panels - setVolumeVisibility("support panel", false, false); - - // Hide St345 LV wires - setVolumeVisibility(" LV ", false, false); - - // Make St345 carbon panels dark gray - setVolumeColor("panel carbon", kGray + 3); - - // Make St345 insulators dark green - setVolumeColor("insulator", kGreen + 3); - - // Hide most of St1 - setVolumeVisibility("SQ", false, true); - - // Only reveal gas module - setVolumeVisibility("SA", true, true); - setVolumeColor("SA", kCyan - 10); -} - -void drawGeometry() -{ - // minimal macro to test setup of the geometry - - createStandaloneGeometry(); - - drawOptionPresetBasic(); - - gGeoManager->GetTopVolume()->Draw("ogl"); - - TGLViewer* gl = static_cast<TGLViewer*>(gPad->GetViewer3D("ogl")); - TGLCamera& c = gl->CurrentCamera(); - - // gl->SetStyle(TGLRnrCtx::kWireFrame); - gl->SetStyle(TGLRnrCtx::kOutline); - // gl->SetStyle(TGLRnrCtx::kFill); -} - -o2::base::GeometryManager::MatBudgetExt getMatBudgetExt(const o2::Transform3D& t, Vector3D<double>& n, float x, float y, float thickness) -{ - Point3D<double> point; - t.LocalToMaster(Point3D<double>{x, y, 0}, point); - return o2::base::GeometryManager::meanMaterialBudgetExt(Point3D<double>{point + n * thickness / 2.0}, Point3D<double>{point - n * thickness / 2.0}); -} - -std::ostream& operator<<(std::ostream& os, o2::base::GeometryManager::MatBudgetExt m) -{ - os << "L=" << m.length << " <Rho>=" << m.meanRho << " <A>=" << m.meanA - << " <Z>=" << m.meanZ << " <x/x0>=" << m.meanX2X0 << " nCross=" << m.nCross; - return os; -} - -Vector3D<double> getNormalVector(const o2::Transform3D& t) -{ - Point3D<double> px, py, po; - t.LocalToMaster(Point3D<double>{0, 1, 0}, py); - t.LocalToMaster(Point3D<double>{1, 0, 0}, px); - t.LocalToMaster(Point3D<double>{0, 0, 0}, po); - Vector3D<double> a{px - po}; - Vector3D<double> b{py - po}; - return a.Cross(b).Unit(); -} - -TH2* getRadio(int detElemId, float xmin, float ymin, float xmax, float ymax, float xstep, float ystep, float thickness) -{ - if (xmin >= xmax || ymin >= ymax) { - std::cerr << "incorrect limits\n"; - return nullptr; - } - TH2* hmatb = new TH2F("hmatb", "hmatb", (int)((xmax - xmin) / xstep), xmin, xmax, (int)((ymax - ymin) / ystep), ymin, ymax); - - auto t = o2::mch::getTransformation(detElemId, *gGeoManager); - - auto normal = getNormalVector(t); - - for (auto x = xmin; x < xmax; x += xstep) { - for (auto y = ymin; y < ymax; y += ystep) { - auto matb = getMatBudgetExt(t, normal, x, y, thickness); - if (std::isfinite(matb.meanX2X0)) { - hmatb->Fill(x, y, matb.meanX2X0); - } - } - } - return hmatb; -} -} // namespace test -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Simulation/src/MCHSimulationLinkDef.h b/Detectors/MUON/MCH/Simulation/src/MCHSimulationLinkDef.h index 723051d17c642..c359a98595f53 100644 --- a/Detectors/MUON/MCH/Simulation/src/MCHSimulationLinkDef.h +++ b/Detectors/MUON/MCH/Simulation/src/MCHSimulationLinkDef.h @@ -16,25 +16,10 @@ #pragma link C++ namespace o2; #pragma link C++ namespace o2::mch; -#pragma link C++ namespace o2::mch::test; -#pragma link C++ class o2::mch::test::Dummy; #pragma link C++ class o2::mch::Detector + ; #pragma link C++ class o2::mch::Hit + ; -#pragma link C++ class std::vector < o2::mch::Hit> + ; -#pragma link C++ class o2::base::DetImpl < o2::mch::Detector> + ; - -#pragma link C++ function o2::mch::addAlignableVolumesMCH; -#pragma link C++ function o2::mch::createGeometry; -#pragma link C++ function o2::mch::getSensitiveVolumes; - -#pragma link C++ function o2::mch::test::addAlignableVolumes; -#pragma link C++ function o2::mch::test::createStandaloneGeometry; -#pragma link C++ function o2::mch::test::createRegularGeometry; -#pragma link C++ function o2::mch::test::drawGeometry; -#pragma link C++ function o2::mch::test::getRadio; -#pragma link C++ function o2::mch::test::showGeometryAsTextTree; -#pragma link C++ function o2::mch::test::setVolumeVisibility; -#pragma link C++ function o2::mch::test::setVolumeColor; +#pragma link C++ class std::vector < o2::mch::Hit > +; +#pragma link C++ class o2::base::DetImpl < o2::mch::Detector > +; #endif diff --git a/Detectors/MUON/MCH/Simulation/src/Response.cxx b/Detectors/MUON/MCH/Simulation/src/Response.cxx index e9511ea0e517c..4bb0c73703ab5 100644 --- a/Detectors/MUON/MCH/Simulation/src/Response.cxx +++ b/Detectors/MUON/MCH/Simulation/src/Response.cxx @@ -69,12 +69,14 @@ float Response::etocharge(float edepos) //expression in PH, i.e. ADC! int nel = int(edepos * 1.e9 / 27.4); float charge = 0; - if (nel == 0) + if (nel == 0) { nel = 1; + } for (int i = 1; i <= nel; i++) { float arg = 0.; - while (!arg) + while (!arg) { arg = gRandom->Rndm(); + } charge -= mChargeSlope * TMath::Log(arg); } //no translation to fC, as in Aliroot @@ -117,10 +119,12 @@ unsigned long Response::response(unsigned long adc) adc = TMath::Nint(adc_out + pedestalMean + adcNoise + 0.5); - if (adc_out < TMath::Nint(pedestalMean + fgNSigma * pedestalSigma + 0.5)) + if (adc_out < TMath::Nint(pedestalMean + fgNSigma * pedestalSigma + 0.5)) { adc = 0; - if (adc > mMaxADC) + } + if (adc > mMaxADC) { adc = mMaxADC; + } return adc; } //______________________________________________________________________ diff --git a/Detectors/MUON/MCH/Simulation/src/Stepper.cxx b/Detectors/MUON/MCH/Simulation/src/Stepper.cxx index 214eda57ed184..67c50340f2ac9 100644 --- a/Detectors/MUON/MCH/Simulation/src/Stepper.cxx +++ b/Detectors/MUON/MCH/Simulation/src/Stepper.cxx @@ -34,6 +34,12 @@ Stepper::~Stepper() void Stepper::process(const TVirtualMC& vmc) { + + if (!(vmc.TrackCharge())) { + // Only charged particles + return; + } + o2::SimTrackStatus t{vmc}; int detElemId; @@ -61,7 +67,7 @@ void Stepper::process(const TVirtualMC& vmc) float x, y, z; vmc.TrackPosition(x, y, z); mHits->emplace_back(stack->GetCurrentTrackNumber(), detElemId, mEntrancePoint, - Point3D<float>{x, y, z}, mTrackEloss, mTrackLength); + math_utils::Point3D<float>{x, y, z}, mTrackEloss, mTrackLength); resetStep(); } } diff --git a/Detectors/MUON/MCH/Simulation/src/Stepper.h b/Detectors/MUON/MCH/Simulation/src/Stepper.h index 78576c48bc51c..02837f32be475 100644 --- a/Detectors/MUON/MCH/Simulation/src/Stepper.h +++ b/Detectors/MUON/MCH/Simulation/src/Stepper.h @@ -43,7 +43,7 @@ class Stepper float mTrackEloss{0.0}; float mTrackLength{0.0}; std::vector<o2::mch::Hit>* mHits{nullptr}; - Point3D<float> mEntrancePoint; + math_utils::Point3D<float> mEntrancePoint; }; } // namespace mch diff --git a/Detectors/MUON/MCH/Simulation/test/CMakeLists.txt b/Detectors/MUON/MCH/Simulation/test/CMakeLists.txt index 9e0508c8e0d5b..8a7129866b533 100644 --- a/Detectors/MUON/MCH/Simulation/test/CMakeLists.txt +++ b/Detectors/MUON/MCH/Simulation/test/CMakeLists.txt @@ -8,28 +8,30 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -o2_add_test(geometry-standalone - NAME o2-test-mch-geometry-standalone +o2_add_test(digit-merging COMPONENT_NAME mch - SOURCES testGeometry.cxx - PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase - COMMAND_LINE_ARGS standalone + SOURCES testDigitMerging.cxx DigitMerging.cxx + PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase O2::DetectorsPassive LABELS muon mch sim) -o2_add_test(geometry-regular - NAME o2-test-mch-geometry-regular - COMPONENT_NAME mch - SOURCES testGeometry.cxx - PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase - COMMAND_LINE_ARGS regular - LABELS muon mch sim) +# o2_add_test(digitization +# COMPONENT_NAME mch +# SOURCES testDigitization.cxx +# PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase O2::DetectorsPassive +# LABELS muon mch long sim) +# +# o2_add_test(response +# COMPONENT_NAME mch +# SOURCES testResponse.cxx +# PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase O2::DetectorsPassive +# LABELS muon mch sim) -o2_add_test(simulation +o2_add_test(regular-geometry COMPONENT_NAME mch - SOURCES testDigitMerging.cxx DigitMerging.cxx - testDigitization.cxx testResponse.cxx - PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase - LABELS muon mch long sim) + SOURCES + testRegularGeometry.cxx + PUBLIC_LINK_LIBRARIES O2::MCHSimulation O2::MCHBase O2::DetectorsPassive + LABELS muon mch sim) if(benchmark_FOUND) o2_add_executable( diff --git a/Detectors/MUON/MCH/Simulation/test/testDigitMerging.cxx b/Detectors/MUON/MCH/Simulation/test/testDigitMerging.cxx index 9fde668423ca0..2c6b1d763472d 100644 --- a/Detectors/MUON/MCH/Simulation/test/testDigitMerging.cxx +++ b/Detectors/MUON/MCH/Simulation/test/testDigitMerging.cxx @@ -8,9 +8,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test MCHSimulation Digitization +#define BOOST_TEST_MODULE Test MCHSimulation DigitMerging #define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN #include <boost/test/unit_test.hpp> @@ -25,8 +24,6 @@ using o2::mch::Digit; -BOOST_AUTO_TEST_SUITE(o2_mch_simulation) - std::vector<Digit> createNonOverlappingDigits() { return std::vector<Digit>{ @@ -102,5 +99,3 @@ BOOST_DATA_TEST_CASE(DigitMerging, boost::unit_test::data::make(mergingFunctions auto m = mergingFunction(createOverlappingDigits(), createLabelsOverlappingDigits()); BOOST_CHECK(std::is_permutation(m.begin(), m.end(), expected().begin())); } - -BOOST_AUTO_TEST_SUITE_END() diff --git a/Detectors/MUON/MCH/Simulation/test/testDigitization.cxx b/Detectors/MUON/MCH/Simulation/test/testDigitization.cxx index 2359b427139af..ebd63b59f1e44 100644 --- a/Detectors/MUON/MCH/Simulation/test/testDigitization.cxx +++ b/Detectors/MUON/MCH/Simulation/test/testDigitization.cxx @@ -11,20 +11,22 @@ /// \brief This task tests the Digitizer and the Response of the MCH digitization /// \author Michael Winn, DPhN/IRFU/CEA, michael.winn@cern.ch +#define BOOST_TEST_MODULE Test MCHSimulation Digitization #define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> -#include "TGeoManager.h" #include "MCHBase/Digit.h" +#include "MCHGeometryTransformer/Transformations.h" +#include "MCHMappingInterface/Segmentation.h" #include "MCHSimulation/Digitizer.h" +#include "MCHGeometryCreator/Geometry.h" +#include "MCHGeometryCreator/GeometryTest.h" #include "MCHSimulation/Hit.h" -#include "MCHSimulation/Geometry.h" -#include "MCHSimulation/GeometryTest.h" -#include "MCHMappingInterface/Segmentation.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "TGeoManager.h" +#include "TGeoManager.h" #include "boost/format.hpp" #include <boost/test/data/test_case.hpp> @@ -39,18 +41,16 @@ struct GEOMETRY { namespace { -Point3D<float> entrancePoint1(-17.7993, 8.929883, -522.201); //x,y,z coordinates in cm -Point3D<float> exitPoint1(-17.8136, 8.93606, -522.62); -Point3D<float> entrancePoint2(-49.2793, 28.8673, -1441.25); -Point3D<float> exitPoint2(-49.2965, 28.8806, -1441.75); +o2::math_utils::Point3D<float> entrancePoint1(-17.7993, 8.929883, -522.201); //x,y,z coordinates in cm +o2::math_utils::Point3D<float> exitPoint1(-17.8136, 8.93606, -522.62); +o2::math_utils::Point3D<float> entrancePoint2(-49.2793, 28.8673, -1441.25); +o2::math_utils::Point3D<float> exitPoint2(-49.2965, 28.8806, -1441.75); } // namespace /// \brief Test of the Digitization /// A couple of values are filled into Hits and we check whether we get reproducible output in terms of digits /// and MClabels -BOOST_AUTO_TEST_SUITE(o2_mch_simulation) - BOOST_FIXTURE_TEST_SUITE(digitization, GEOMETRY) BOOST_TEST_DECORATOR(*boost::unit_test::disabled()) @@ -82,6 +82,8 @@ BOOST_AUTO_TEST_CASE(DigitizerTest) int digitcounter2 = 0; int count = 0; + auto transformation = o2::mch::geo::transformationFromTGeoManager(*gGeoManager); + for (auto& digit : digits) { int padid = digit.getPadID(); @@ -91,16 +93,17 @@ BOOST_AUTO_TEST_CASE(DigitizerTest) if (trackID == trackId1) { bool check = seg1.isValid(digit.getPadID()); - if (!check) + if (!check) { BOOST_FAIL(" digit-pad not belonging to hit det-element-ID "); + } double padposX = seg1.padPositionX(padid); double padsizeX = seg1.padSizeX(padid); double padposY = seg1.padPositionY(padid); double padsizeY = seg1.padSizeY(padid); - auto t = o2::mch::getTransformation(detElemId1, *gGeoManager); + auto t = transformation(detElemId1); - Point3D<float> pos(hits.at(0).GetX(), hits.at(0).GetY(), hits.at(0).GetZ()); - Point3D<float> lpos; + o2::math_utils::Point3D<float> pos(hits.at(0).GetX(), hits.at(0).GetY(), hits.at(0).GetZ()); + o2::math_utils::Point3D<float> lpos; t.MasterToLocal(pos, lpos); BOOST_CHECK_CLOSE(lpos.x(), padposX, padsizeX * 4.0); @@ -109,16 +112,17 @@ BOOST_AUTO_TEST_CASE(DigitizerTest) digitcounter1++; } else if (trackID == trackId2) { bool check = seg2.isValid(digit.getPadID()); - if (!check) + if (!check) { BOOST_FAIL(" digit-pad not belonging to hit det-element-ID "); + } double padposX = seg2.padPositionX(padid); double padsizeX = seg2.padSizeX(padid); double padposY = seg2.padPositionY(padid); double padsizeY = seg2.padSizeY(padid); - auto t = o2::mch::getTransformation(detElemId2, *gGeoManager); + auto t = transformation(detElemId2); - Point3D<float> pos(hits.at(1).GetX(), hits.at(1).GetY(), hits.at(1).GetZ()); - Point3D<float> lpos; + o2::math_utils::Point3D<float> pos(hits.at(1).GetX(), hits.at(1).GetY(), hits.at(1).GetZ()); + o2::math_utils::Point3D<float> lpos; t.MasterToLocal(pos, lpos); BOOST_CHECK_CLOSE(lpos.x(), padposX, padsizeX * 4.0); @@ -130,14 +134,18 @@ BOOST_AUTO_TEST_CASE(DigitizerTest) }; } - if (digitcounter1 == 0) + if (digitcounter1 == 0) { BOOST_FAIL(" no digit at all from hit in station 1 "); - if (digitcounter1 > 9) + } + if (digitcounter1 > 9) { BOOST_FAIL("more than 10 digits for one hit in station 1 "); - if (digitcounter2 == 0) + } + if (digitcounter2 == 0) { BOOST_FAIL(" no digit at all from hit in station 2 "); - if (digitcounter2 > 9) + } + if (digitcounter2 > 9) { BOOST_FAIL(" more than 10 digits for one hit in station 2 "); + } } BOOST_TEST_DECORATOR(*boost::unit_test::disabled()) @@ -198,4 +206,3 @@ BOOST_AUTO_TEST_CASE(mergingDigitizer) } //testing BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() diff --git a/Detectors/MUON/MCH/Simulation/test/testGeometry.cxx b/Detectors/MUON/MCH/Simulation/test/testGeometry.cxx deleted file mode 100644 index 8e0c531d63e9f..0000000000000 --- a/Detectors/MUON/MCH/Simulation/test/testGeometry.cxx +++ /dev/null @@ -1,511 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Laurent Aphecetche - -#define BOOST_TEST_MODULE Test MCHSimulation Geometry -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN - -#include <boost/test/unit_test.hpp> - -#include "MCHMappingInterface/CathodeSegmentation.h" -#include "MCHSimulation/Geometry.h" -#include "MCHSimulation/GeometryTest.h" -#include "TGeoManager.h" -#include "boost/format.hpp" -#include <boost/test/data/test_case.hpp> -#include <fstream> -#include <iomanip> -#include <iostream> -#include <fmt/format.h> - -namespace bdata = boost::unit_test::data; - -struct GEOMETRY { - GEOMETRY() - { - if (!gGeoManager) { - std::string param = boost::unit_test::framework::master_test_suite().argv[1]; - if (param == "standalone") { - o2::mch::test::createStandaloneGeometry(); - } else if (param == "regular") { - o2::mch::test::createRegularGeometry(); - } else { - throw std::invalid_argument(fmt::format("only possible argument value are 'regular' or 'standalone', not {}", param.c_str())); - } - o2::mch::test::addAlignableVolumes(); - } - } -}; - -const std::array<std::string, 8> quadrantChamberNames{"SC01I", "SC01O", "SC02I", "SC02O", "SC03I", "SC03O", - "SC04I", "SC04O"}; - -const std::array<std::string, 12> slatChamberNames{"SC05I", "SC05O", "SC06I", "SC06O", "SC07I", "SC07O", - "SC08I", "SC08O", "SC09I", "SC09O", "SC10I", "SC10O"}; - -const std::vector<std::vector<std::string>> deSymNames{ - {"DE100", "DE103"}, - {"DE101", "DE102"}, - {"DE200", "DE203"}, - {"DE201", "DE202"}, - {"DE300", "DE303"}, - {"DE301", "DE302"}, - {"DE400", "DE403"}, - {"DE401", "DE402"}, - {"DE500", "DE501", "DE502", "DE503", "DE504", "DE514", "DE515", "DE516", "DE517"}, - {"DE505", "DE506", "DE507", "DE508", "DE509", "DE510", "DE511", "DE512", "DE513"}, - {"DE600", "DE601", "DE602", "DE603", "DE604", "DE614", "DE615", "DE616", "DE617"}, - {"DE605", "DE606", "DE607", "DE608", "DE609", "DE610", "DE611", "DE612", "DE613"}, - {"DE700", "DE701", "DE702", "DE703", "DE704", "DE705", "DE706", "DE720", "DE721", "DE722", "DE723", "DE724", "DE725"}, - {"DE707", "DE708", "DE709", "DE710", "DE711", "DE712", "DE713", "DE714", "DE715", "DE716", "DE717", "DE718", "DE719"}, - {"DE800", "DE801", "DE802", "DE803", "DE804", "DE805", "DE806", "DE820", "DE821", "DE822", "DE823", "DE824", "DE825"}, - {"DE807", "DE808", "DE809", "DE810", "DE811", "DE812", "DE813", "DE814", "DE815", "DE816", "DE817", "DE818", "DE819"}, - {"DE900", "DE901", "DE902", "DE903", "DE904", "DE905", "DE906", "DE920", "DE921", "DE922", "DE923", "DE924", "DE925"}, - {"DE907", "DE908", "DE909", "DE910", "DE911", "DE912", "DE913", "DE914", "DE915", "DE916", "DE917", "DE918", "DE919"}, - {"DE1000", "DE1001", "DE1002", "DE1003", "DE1004", "DE1005", "DE1006", "DE1020", "DE1021", "DE1022", "DE1023", "DE1024", "DE1025"}, - {"DE1007", "DE1008", "DE1009", "DE1010", "DE1011", "DE1012", "DE1013", "DE1014", "DE1015", "DE1016", "DE1017", "DE1018", "DE1019"}}; - -BOOST_AUTO_TEST_SUITE(o2_mch_simulation) - -BOOST_FIXTURE_TEST_SUITE(geometrytransformer, GEOMETRY) - -BOOST_AUTO_TEST_CASE(CanGetAllChambers) -{ - std::vector<std::string> chamberNames{quadrantChamberNames.begin(), quadrantChamberNames.end()}; - - chamberNames.insert(chamberNames.end(), slatChamberNames.begin(), slatChamberNames.end()); - - for (auto chname : chamberNames) { - auto vol = gGeoManager->GetVolume(chname.c_str()); - BOOST_TEST_REQUIRE((vol != nullptr)); - } -} - -std::vector<TGeoNode*> getSlatNodes() -{ - std::vector<TGeoNode*> slats; - for (auto chname : slatChamberNames) { - auto vol = gGeoManager->GetVolume(chname.c_str()); - TIter next(vol->GetNodes()); - while (TGeoNode* node = static_cast<TGeoNode*>(next())) { - if (strstr(node->GetName(), "support") == nullptr) { - slats.push_back(node); - } - } - } - return slats; -} - -std::vector<TGeoNode*> getQuadrantNodes() -{ - std::vector<TGeoNode*> quadrants; - for (auto chname : quadrantChamberNames) { - auto vol = gGeoManager->GetVolume(chname.c_str()); - TIter next(vol->GetNodes()); - while (TGeoNode* node = static_cast<TGeoNode*>(next())) { - quadrants.push_back(node); - } - } - return quadrants; -} - -BOOST_AUTO_TEST_CASE(GetRightNumberOfSlats) -{ - auto slats = getSlatNodes(); - BOOST_CHECK_EQUAL(slats.size(), 140); -} - -BOOST_AUTO_TEST_CASE(GetRightNumberOfQuadrants) -{ - auto quadrants = getQuadrantNodes(); - BOOST_CHECK_EQUAL(quadrants.size(), 16); -} - -BOOST_AUTO_TEST_CASE(GetDetElemVolumePath, *boost::unit_test::disabled() * boost::unit_test::label("debug")) -{ - TIter next(gGeoManager->GetTopNode()->GetNodes()); - TGeoNode* node; - TGeoNode* n2; - - std::vector<std::string> codeLines; - - while ((node = static_cast<TGeoNode*>(next()))) { - std::cout << node->GetName() << "\n"; - TIter next2(node->GetNodes()); - while ((n2 = static_cast<TGeoNode*>(next2()))) { - std::string n2name{n2->GetName()}; - auto index = n2name.find_last_of('_'); - int detElemId = std::atoi(n2name.substr(index + 1).c_str()); - if (detElemId >= 100) { - std::stringstream s; - s << "if (detElemId==" << detElemId << ") {\n"; - s << R"( return ")" << node->GetName() << "/" << n2name << "\";\n"; - s << "}\n"; - codeLines.push_back(s.str()); - } - } - } - - for (auto s : codeLines) { - std::cout << s; - } - BOOST_CHECK_EQUAL(codeLines.size(), 156); -} - -BOOST_AUTO_TEST_CASE(GetTransformationMustNotThrowForValidDetElemId) -{ - BOOST_REQUIRE(gGeoManager != nullptr); - - o2::mch::mapping::forEachDetectionElement([](int detElemId) { - BOOST_CHECK_NO_THROW((o2::mch::getTransformation(detElemId, *gGeoManager))); - }); -} - -struct CoarseLocation { - bool isRight; - bool isTop; -}; - -constexpr CoarseLocation topRight{true, true}; -constexpr CoarseLocation topLeft{false, true}; -constexpr CoarseLocation bottomRight{true, false}; -constexpr CoarseLocation bottomLeft{false, false}; - -std::string asString(const CoarseLocation& q) -{ - std::string s = q.isTop ? "TOP" : "BOTTOM"; - s += q.isRight ? "RIGHT" : "LEFT"; - return s; -} - -bool operator==(const CoarseLocation& a, const CoarseLocation& b) -{ - return a.isRight == b.isRight && a.isTop == b.isTop; -} - -CoarseLocation getDetElemCoarseLocation(int detElemId) -{ - auto t = o2::mch::getTransformation(detElemId, *gGeoManager); - Point3D<double> localTestPos{0.0, 0.0, 0.0}; // slat center - - if (detElemId < 500) { - // in the rough ballpark of the center - // of the quadrants - localTestPos.SetXYZ(60, 60, 0); - } - - // for slats around the middle (y closest to 0) we have to be a bit - // more precise, so take a given pad reference, chosen to be - // the most top right or most top left pad - - switch (detElemId) { - case 500: - case 509: - localTestPos.SetXYZ(-72.50, 19.75, 0.0); // ds 107 - break; - case 600: - case 609: - localTestPos.SetXYZ(-77.50, 19.75, 0.0); // ds 108 - break; - case 700: - case 713: - case 800: - case 813: - case 900: - case 913: - case 1000: - case 1013: - localTestPos.SetXYZ(95.0, -19.75, 0); // ds 104 - break; - } - Point3D<double> master; - - t.LocalToMaster(localTestPos, master); - bool right = master.x() > 10.; - bool top = master.y() > -10.; - - return CoarseLocation{right, top}; -} - -void setExpectation(int firstDeId, int lastDeId, CoarseLocation q, std::map<int, CoarseLocation>& expected) -{ - for (int deid = firstDeId; deid <= lastDeId; deid++) { - expected.emplace(deid, q); - } -} - -BOOST_AUTO_TEST_CASE(DetectionElementMustBeInTheRightCoarseLocation) -{ - std::map<int, CoarseLocation> expected; - - for (int i = 0; i < 4; i++) { - expected[100 + i * 100] = topRight; - expected[101 + i * 100] = topLeft; - expected[102 + i * 100] = bottomLeft; - expected[103 + i * 100] = bottomRight; - } - - // note that by convention we consider slats in the middle to be "top" - for (int i = 0; i < 2; i++) { - setExpectation(500 + i * 100, 504 + i * 100, topRight, expected); - setExpectation(505 + i * 100, 509 + i * 100, topLeft, expected); - setExpectation(510 + i * 100, 513 + i * 100, bottomLeft, expected); - setExpectation(514 + i * 100, 517 + i * 100, bottomRight, expected); - } - - for (int i = 0; i < 4; i++) { - setExpectation(700 + i * 100, 706 + i * 100, topRight, expected); - setExpectation(707 + i * 100, 713 + i * 100, topLeft, expected); - setExpectation(714 + i * 100, 719 + i * 100, bottomLeft, expected); - setExpectation(720 + i * 100, 725 + i * 100, bottomRight, expected); - } - - o2::mch::mapping::forEachDetectionElement([&expected](int detElemId) { - if (expected.find(detElemId) == expected.end()) { - std::cout << "got no expectation for DE=" << detElemId << "\n"; - return; - } - BOOST_TEST_INFO(fmt::format("DeId {}", detElemId)); - BOOST_CHECK_EQUAL(asString(getDetElemCoarseLocation(detElemId)), asString(expected[detElemId])); - }); -} - -BOOST_AUTO_TEST_CASE(TextualTreeDump) -{ - std::string param = boost::unit_test::framework::master_test_suite().argv[1]; - if (param != "standalone") { - // no point in checking the full hierarchy and have this test - // depending on all the details of the geometry of all detectors, - // so this test only makes sense for standalone geometry - BOOST_CHECK(true); - return; - } - - const std::string expected = - R"(cave_1 -├──SC01I_0 -│ ├──Quadrant (chamber 1)_100 -│ └──Quadrant (chamber 1)_103 -├──SC01O_1 -│ ├──Quadrant (chamber 1)_101 -│ └──Quadrant (chamber 1)_102 -├──SC02I_2 -│ ├──Quadrant (chamber 2)_200 -│ └──Quadrant (chamber 2)_203 -├──SC02O_3 -│ ├──Quadrant (chamber 2)_201 -│ └──Quadrant (chamber 2)_202 -├──SC03I_4 -│ ├──Station 2 quadrant_300 -│ └──Station 2 quadrant_303 -├──SC03O_5 -│ ├──Station 2 quadrant_301 -│ └──Station 2 quadrant_302 -├──SC04I_6 -│ ├──Station 2 quadrant_400 -│ └──Station 2 quadrant_403 -├──SC04O_7 -│ ├──Station 2 quadrant_401 -│ └──Station 2 quadrant_402 -├──SC05I_8 -│ ├──Chamber 5 support panel_8 -│ ├──122000SR1_500 -│ ├──112200SR2_501 -│ ├──122200S_502 -│ ├──222000N_503 -│ ├──220000N_504 -│ ├──220000N_514 -│ ├──222000N_515 -│ ├──122200S_516 -│ └──112200SR2_517 -├──SC05O_9 -│ ├──Chamber 5 support panel_9 -│ ├──220000N_505 -│ ├──222000N_506 -│ ├──122200S_507 -│ ├──112200SR2_508 -│ ├──122000SR1_509 -│ ├──112200SR2_510 -│ ├──122200S_511 -│ ├──222000N_512 -│ └──220000N_513 -├──SC06I_10 -│ ├──Chamber 6 support panel_10 -│ ├──122000NR1_600 -│ ├──112200NR2_601 -│ ├──122200N_602 -│ ├──222000N_603 -│ ├──220000N_604 -│ ├──220000N_614 -│ ├──222000N_615 -│ ├──122200N_616 -│ └──112200NR2_617 -├──SC06O_11 -│ ├──Chamber 6 support panel_11 -│ ├──220000N_605 -│ ├──222000N_606 -│ ├──122200N_607 -│ ├──112200NR2_608 -│ ├──122000NR1_609 -│ ├──112200NR2_610 -│ ├──122200N_611 -│ ├──222000N_612 -│ └──220000N_613 -├──SC07I_12 -│ ├──Chamber 7 support panel_12 -│ ├──122330N_700 -│ ├──112233NR3_701 -│ ├──112230N_702 -│ ├──222330N_703 -│ ├──223300N_704 -│ ├──333000N_705 -│ ├──330000N_706 -│ ├──330000N_720 -│ ├──333000N_721 -│ ├──223300N_722 -│ ├──222330N_723 -│ ├──112230N_724 -│ └──112233NR3_725 -├──SC07O_13 -│ ├──Chamber 7 support panel_13 -│ ├──330000N_707 -│ ├──333000N_708 -│ ├──223300N_709 -│ ├──222330N_710 -│ ├──112230N_711 -│ ├──112233NR3_712 -│ ├──122330N_713 -│ ├──112233NR3_714 -│ ├──112230N_715 -│ ├──222330N_716 -│ ├──223300N_717 -│ ├──333000N_718 -│ └──330000N_719 -├──SC08I_14 -│ ├──Chamber 8 support panel_14 -│ ├──122330N_800 -│ ├──112233NR3_801 -│ ├──112230N_802 -│ ├──222330N_803 -│ ├──223300N_804 -│ ├──333000N_805 -│ ├──330000N_806 -│ ├──330000N_820 -│ ├──333000N_821 -│ ├──223300N_822 -│ ├──222330N_823 -│ ├──112230N_824 -│ └──112233NR3_825 -├──SC08O_15 -│ ├──Chamber 8 support panel_15 -│ ├──330000N_807 -│ ├──333000N_808 -│ ├──223300N_809 -│ ├──222330N_810 -│ ├──112230N_811 -│ ├──112233NR3_812 -│ ├──122330N_813 -│ ├──112233NR3_814 -│ ├──112230N_815 -│ ├──222330N_816 -│ ├──223300N_817 -│ ├──333000N_818 -│ └──330000N_819 -├──SC09I_16 -│ ├──Chamber 9 support panel_16 -│ ├──122330N_900 -│ ├──112233NR3_901 -│ ├──112233N_902 -│ ├──222333N_903 -│ ├──223330N_904 -│ ├──333300N_905 -│ ├──333000N_906 -│ ├──333000N_920 -│ ├──333300N_921 -│ ├──223330N_922 -│ ├──222333N_923 -│ ├──112233N_924 -│ └──112233NR3_925 -├──SC09O_17 -│ ├──Chamber 9 support panel_17 -│ ├──333000N_907 -│ ├──333300N_908 -│ ├──223330N_909 -│ ├──222333N_910 -│ ├──112233N_911 -│ ├──112233NR3_912 -│ ├──122330N_913 -│ ├──112233NR3_914 -│ ├──112233N_915 -│ ├──222333N_916 -│ ├──223330N_917 -│ ├──333300N_918 -│ └──333000N_919 -├──SC10I_18 -│ ├──Chamber 10 support panel_18 -│ ├──122330N_1000 -│ ├──112233NR3_1001 -│ ├──112233N_1002 -│ ├──222333N_1003 -│ ├──223330N_1004 -│ ├──333300N_1005 -│ ├──333000N_1006 -│ ├──333000N_1020 -│ ├──333300N_1021 -│ ├──223330N_1022 -│ ├──222333N_1023 -│ ├──112233N_1024 -│ └──112233NR3_1025 -└──SC10O_19 - ├──Chamber 10 support panel_19 - ├──333000N_1007 - ├──333300N_1008 - ├──223330N_1009 - ├──222333N_1010 - ├──112233N_1011 - ├──112233NR3_1012 - ├──122330N_1013 - ├──112233NR3_1014 - ├──112233N_1015 - ├──222333N_1016 - ├──223330N_1017 - ├──333300N_1018 - └──333000N_1019 -)"; - - std::ostringstream str; - o2::mch::test::showGeometryAsTextTree("/cave_1", 2, str); - BOOST_CHECK(expected == str.str()); -} - -BOOST_AUTO_TEST_CASE(GetAlignableHalfChambers) -{ - BOOST_REQUIRE(gGeoManager != nullptr); - - for (int i = 0; i < 20; i++) { - BOOST_CHECK((gGeoManager->GetAlignableEntry((fmt::format("MCH/HC{}", i)).c_str()))); - } -} - -BOOST_AUTO_TEST_CASE(GetAlignableDetectionElements) -{ - BOOST_REQUIRE(gGeoManager != nullptr); - - for (int hc = 0; hc < 20; hc++) { - for (int de = 0; de < deSymNames[hc].size(); de++) { - BOOST_CHECK((gGeoManager->GetAlignableEntry((fmt::format("MCH/HC{}/{}", hc, deSymNames[hc][de].c_str())).c_str()))); - } - } -} -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() diff --git a/Detectors/MUON/MCH/Simulation/test/testRegularGeometry.cxx b/Detectors/MUON/MCH/Simulation/test/testRegularGeometry.cxx new file mode 100644 index 0000000000000..271aae7060603 --- /dev/null +++ b/Detectors/MUON/MCH/Simulation/test/testRegularGeometry.cxx @@ -0,0 +1,42 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MCHSimulation RegularGeometry +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> +#include "DetectorsPassive/Absorber.h" +#include "DetectorsPassive/Cave.h" +#include "DetectorsPassive/Compensator.h" +#include "DetectorsPassive/Dipole.h" +#include "DetectorsPassive/Pipe.h" +#include "DetectorsPassive/Shil.h" +#include "MCHSimulation/Detector.h" +#include "TGeoManager.h" +#include <boost/test/data/test_case.hpp> +#include <fstream> +#include <iomanip> +#include <iostream> + +BOOST_AUTO_TEST_CASE(DoNotThrow) +{ + if (gGeoManager && gGeoManager->GetTopVolume()) { + std::cerr << "Can only call this function with an empty geometry, i.e. gGeoManager==nullptr " + << " or gGeoManager->GetTopVolume()==nullptr\n"; + } + TGeoManager* g = new TGeoManager("MCH-BASICS", "ALICE MCH Regular Geometry"); + o2::passive::Cave("CAVE", "Cave (for MCH Basics)").ConstructGeometry(); + o2::passive::Dipole("DIPO", "Alice Dipole (for MCH Basics)").ConstructGeometry(); + o2::passive::Compensator("COMP", "Alice Compensator Dipole (for MCH Basics)").ConstructGeometry(); + o2::passive::Pipe("PIPE", "Beam pipe (for MCH Basics)").ConstructGeometry(); + o2::passive::Shil("SHIL", "Small angle beam shield (for MCH Basics)").ConstructGeometry(); + o2::passive::Absorber("ABSO", "Absorber (for MCH Basics)").ConstructGeometry(); + BOOST_CHECK_NO_THROW((o2::mch::Detector(true).ConstructGeometry())); +} diff --git a/Detectors/MUON/MCH/Simulation/test/testResponse.cxx b/Detectors/MUON/MCH/Simulation/test/testResponse.cxx index d87a52b10cdc6..2acddbf5f0e5e 100644 --- a/Detectors/MUON/MCH/Simulation/test/testResponse.cxx +++ b/Detectors/MUON/MCH/Simulation/test/testResponse.cxx @@ -11,7 +11,9 @@ /// \brief This task tests of the Response of the MCH digitization /// \author Michael Winn, DPhN/IRFU/CEA, michael.winn@cern.ch +#define BOOST_TEST_MODULE Test MCHSimulation Response #define BOOST_TEST_DYN_LINK + #include <boost/test/unit_test.hpp> #include <memory> #include <vector> diff --git a/Detectors/MUON/MCH/Tracking/CMakeLists.txt b/Detectors/MUON/MCH/Tracking/CMakeLists.txt index c69ac3d56014f..9aee91ff5e676 100644 --- a/Detectors/MUON/MCH/Tracking/CMakeLists.txt +++ b/Detectors/MUON/MCH/Tracking/CMakeLists.txt @@ -17,33 +17,4 @@ o2_add_library(MCHTracking src/TrackFitter.cxx src/TrackFinderOriginal.cxx src/TrackFinder.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::Field O2::MCHBase) - -o2_add_executable(trackfitter-workflow - SOURCES - src/TrackFitterWorkflow.cxx - src/TrackSamplerSpec.cxx - src/TrackFitterSpec.cxx - src/TrackSinkSpec.cxx - COMPONENT_NAME mch - PUBLIC_LINK_LIBRARIES O2::MCHTracking) - -o2_add_executable(trackfinderoriginal-workflow - SOURCES - src/TrackFinderOriginalWorkflow.cxx - src/ClusterSamplerSpec.cxx - src/TrackFinderOriginalSpec.cxx - src/TrackSinkSpec.cxx - COMPONENT_NAME mch - PUBLIC_LINK_LIBRARIES O2::MCHTracking) - - o2_add_executable(trackfinder-workflow - SOURCES - src/TrackFinderWorkflow.cxx - src/ClusterSamplerSpec.cxx - src/VertexSamplerSpec.cxx - src/TrackFinderSpec.cxx - src/TrackAtVertexSpec.cxx - src/TrackSinkSpec.cxx - COMPONENT_NAME mch - PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::MCHTracking) + PUBLIC_LINK_LIBRARIES O2::Field O2::MCHBase) diff --git a/Detectors/MUON/MCH/Tracking/README.md b/Detectors/MUON/MCH/Tracking/README.md new file mode 100644 index 0000000000000..df99f211f7559 --- /dev/null +++ b/Detectors/MUON/MCH/Tracking/README.md @@ -0,0 +1,135 @@ +<!-- doxy +\page refDetectorsMUONMCHTracking Tracking +/doxy --> + +# TrackExtrap.h(cxx) + +Contains all the tools to: +- extrapolate the track parameters (and covariances) from one z position to another, taking into account or not the +magnetic field. +- add the dispersion from Multiple Coulomb Scattering (MCS) to the covariance matrix when crossing a tracking chamber. +- extrapolate the track parameters (and covariances) through the front absorber, taking into account (or not) the +effects from MCS and energy loss. + +# TrackFitter.h(cxx) + +Fit a track to the clusters attached to it, using the Kalman Filter. + +## Short description of the algorithm + +The seeds for the track parameters and covariances are computed using the position and resolution of the 2 clusters in +the last two fired chambers. An estimate of the bending momentum is obtained by assuming that the track is coming from +the vertex (0,0,0) and crosses a constant magnetic field (both with sufficiently large uncertainties to do not bias the +final result). +The initial track parameters and covariances are then propagated to every clusters, up to the most upstream one, and are +updated at each step by "attaching" the cluster with the Kalman filter. The dispersion from MCS is added to the +covariances each time a chamber is crossed. +This algorithm gives the track parameters and covariances at the first cluster (the last attached one). Optionally, the +Smoother algorithm can be run to fit back the track and compute the track parameters and covariances at every clusters. +If the pointer to the parameters at one of the attached clusters is given, the fit is resumed starting from this point. + +# TrackFinderOriginal.h(cxx) + +This is a reimplementation of the original tracking algorithm from AliRoot. + +## Input / Output + +It takes as input a list of reconstructed clusters per chambers, the clusters' position being given in the global +coordinate system. It returns the list of reconstructed tracks, each of them containing (in the TrackParams) the +pointers to the associated clusters. + +## Short description of the algorithm + +The original tracking algorithm is described in detail here: +https://edms.cern.ch/ui/file/1054937/1/ALICE-INT-2009-044.pdf + +The main difference with the original implementation is in the order the tracking of the initial candidates (made of +clusters in stations 4&5) is performed. In the original implementation, all candidates are propagated to station 3 to +search for compatible clusters, tracks are duplicated to consider all possibilities, then all candidates are propagated +to station 2, and so on. In some events with very high multiplicity, it results in a temporarily huge number of +candidates in memory at the same time (>10000) and a limitation is needed to abort the tracking in such case to do not +break the reconstruction. In the new implementation, each candidate is treated one by one. The current candidate is +propagated to station 3 to search for compatible clusters, duplicated to consider every posibilities, then each of them +is tracked one by one to station 2, then to station 1. This limits drastically the number of candidates in memory at the +same time, thus avoiding to abort the tracking for any event, while reconstructing exactly the same tracks in the end. + +# TrackFinder.h(cxx) + +This is the implementation of the new tracking algorithm. + +## Input / Output + +It takes as input a list of reconstructed clusters mapped per DE, the clusters' position being given in the global +coordinate system. It returns the list of reconstructed tracks, each of them containing (in the TrackParams) the +pointers to the associated clusters. + +## Short description of the algorithm + +### General ideas to improve the original algorithm: +- Consider the overlaps between DE on the same chamber during the tracking (the original algorithm considers only one +cluster per chamber during the tracking, duplicating the tracks with 2 clusters in overlapping DE to consider both), so +there is no need to complete the tracks to add the missing clusters afterward and to cleanup the duplicates. We assume +there is no overlap between the 2 half-planes of chambers 5 to 10. +- Do not try to recover tracks by removing one of the previously attached clusters in the previous station if no +compatible cluster is found in the current one. Assume that a good track will not be lost if several clusters are +attached in a station and some of them are bad (i.e. not belonging to that track). +- Further reduce the number of track duplication and memory consumption by pursuing the tracking of the current +candidate with the newly found compatible clusters down to station 1, or until the tracking fails, before testing other +clusters, so that we duplicate the original candidate only to store valid complete tracks. +- Speedup the cleanup of connected tracks in the end by reducing the number of imbricated loops. +- Speedup the track extrapolation in the magnetic field by reducing the number of steps to reach to desired z position. + +### Algorithm: +- Build candidates on station 5 starting from pairs of clusters on chambers 9 and 10 and looking for compatible clusters +in overlap DE. Consider every combinations of different clusters and discard candidates whose parameters are outside of +acceptance limits within uncertainties. +- Propagate the candidates to station 4 and look for compatible clusters, taking into account overlaps. At least one +cluster is requested and the track is duplicated to consider every possibilities, eliminating the ones driving the track +parameters outside of acceptance limits within uncertainties. +- Repeat the two steps above starting from station 4 then going to station 5 to look for additional candidates with only +one chamber fired on station 5. +- Propagate each candidate found in the previous steps from chamber 6 to 1, looking for compatible clusters taking into +account the overlaps. This is a recursive procedure. For each cluster or couple of clusters found on one chamber, the +current parameters of the initial candidate are updated and the tracking continue to the next chamber, and so on and so +forth, until chamber 1 or until the tracking fails (at least one cluster per station is requested to continue). Only at +the end, when/if the track reaches the first station, it is duplicated and the clusters found in the process are +attached to the new track. Clusters that drive the track parameters outside of acceptance limits within uncertainties +are discarded. +- Improve the tracks: run the smoother to recompute the local chi2 at each cluster, remove the worst cluster if it does +not pass a stricter chi2 cut, refit the track and repeat the procedure until all clusters pass the cut or one of them +cannot be removed (the track must contain at least 1 cluster per station), in which case the track is removed. +- Remove connected tracks in station 3, 4 and 5. If two tracks share at least one cluster in these stations, remove the +one with the smallest number of clusters or with the highest chi2 in case of equality, assuming it is a fake track. + +In all stations, the search for compatible clusters is done in a way to consider every possibilities, i.e. every +combinations of 1 to 4 clusters, while skipping the already tested combinations. This includes subsets of previously +found combinations, so that we always attach the maximum number of clusters to a candidate (e.g. if we can attach 4 +clusters in one station because of the overlaps, we do not consider the possibility to attach only 3, 2, or 1 of them +even if the track would still be valid). + +A more detailed description of the various parts of the algorithm is given in the code itself. + +### Available options: +- Find more track candidates, with only one chamber fired on station 4 and one on station 5, taking into account the +overlaps between DE and excluding those whose parameters are outside of acceptance limits within uncertainties. +- Do not request certain stations. When building initial candidates on stations 4 and 5, it means to keep the candidates +found on station 5(4) even if no compatible cluster is found on station 4(5) if that station is not requested. However, +if one or more compatible clusters are found we do not consider the possibility to do not attach any cluster on that +station. When tracking the candidates to station 3 to 1, it means to consider the possibility to continue the tracking +without attaching any cluster on the station that is not requested, even if compatible clusters are found on that +station. However the clusters found upstream after having attached cluster(s) on that station are skipped. + +## Examples of workflow + +- The line below allows to read the clusters from the file `clusters.in`, run the new tracking algorithm, read the +associated vertex from the file `vertices.in`, extrapolate the tracks to the vertex and write the result (tracks at +vertex, MCH tracks and associated clusters) in the file `tracks.out`: + +`o2-mch-clusters-sampler-workflow --infile "clusters.in" | o2-mch-clusters-to-tracks-workflow --l3Current 29999.998047 --dipoleCurrent 5999.966797 | o2-mch-vertex-sampler-workflow --infile "vertices.in" | o2-mch-tracks-to-tracks-at-vertex-workflow --l3Current 29999.998047 --dipoleCurrent 5999.966797 | o2-mch-tracks-sink-workflow --outfile "tracks.out"` + +- The line below allows to read the MCH tracks and associated clusters from the file `tracks.in`, refit them and write +them in the file `tracks.out`: + +`o2-mch-tracks-sampler-workflow --infile "tracks.in" --forTrackFitter | o2-mch-tracks-to-tracks-workflow --l3Current -30000.025391 --dipoleCurrent -5999.954590 | o2-mch-tracks-sink-workflow --outfile "tracks.out" --mchTracksOnly` + +See the [workflow documentation](../Workflow/README.md) for more details about each individual workflow. diff --git a/Detectors/MUON/MCH/Tracking/include/MCHTracking/Cluster.h b/Detectors/MUON/MCH/Tracking/include/MCHTracking/Cluster.h index 076237f3e47e5..d31e907f26c1b 100644 --- a/Detectors/MUON/MCH/Tracking/include/MCHTracking/Cluster.h +++ b/Detectors/MUON/MCH/Tracking/include/MCHTracking/Cluster.h @@ -36,8 +36,8 @@ class Cluster Cluster(const Cluster& cl) = default; Cluster& operator=(const Cluster& cl) = default; - Cluster(Cluster&&) = delete; - Cluster& operator=(Cluster&&) = delete; + Cluster(Cluster&&) = default; + Cluster& operator=(Cluster&&) = default; ClusterStruct getClusterStruct() const; diff --git a/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackExtrap.h b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackExtrap.h new file mode 100644 index 0000000000000..431353bb27249 --- /dev/null +++ b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackExtrap.h @@ -0,0 +1,141 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackExtrap.h +/// \brief Definition of tools for track extrapolation +/// +/// \author Philippe Pillot, Subatech + +#ifndef ALICEO2_MCH_TRACKEXTRAP_H_ +#define ALICEO2_MCH_TRACKEXTRAP_H_ + +#include <cstddef> + +#include <TMatrixD.h> + +namespace o2 +{ +namespace mch +{ + +class TrackParam; + +/// Class holding tools for track extrapolation +class TrackExtrap +{ + public: + // static class + TrackExtrap() = delete; + ~TrackExtrap() = delete; + + TrackExtrap(const TrackExtrap&) = delete; + TrackExtrap& operator=(const TrackExtrap&) = delete; + TrackExtrap(TrackExtrap&&) = delete; + TrackExtrap& operator=(TrackExtrap&&) = delete; + + static void setField(); + + /// Return true if the field is switched ON + static bool isFieldON() { return sFieldON; } + + /// Switch to Runge-Kutta extrapolation v2 + static void useExtrapV2(bool extrapV2 = true) { sExtrapV2 = extrapV2; } + + static double getImpactParamFromBendingMomentum(double bendingMomentum); + static double getBendingMomentumFromImpactParam(double impactParam); + + static void linearExtrapToZ(TrackParam* trackParam, double zEnd); + static void linearExtrapToZCov(TrackParam* trackParam, double zEnd, bool updatePropagator = false); + + static bool extrapToZ(TrackParam* trackParam, double zEnd); + static bool extrapToZCov(TrackParam* trackParam, double zEnd, bool updatePropagator = false); + + static bool extrapToVertex(TrackParam* trackParam, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx) + { + /// Extrapolate track parameters to vertex, corrected for multiple scattering and energy loss effects + /// Add branson correction resolution and energy loss fluctuation to parameter covariances + return extrapToVertex(trackParam, xVtx, yVtx, zVtx, errXVtx, errYVtx, true, true); + } + static bool extrapToVertexWithoutELoss(TrackParam* trackParam, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx) + { + /// Extrapolate track parameters to vertex, corrected for multiple scattering effects only + /// Add branson correction resolution to parameter covariances + return extrapToVertex(trackParam, xVtx, yVtx, zVtx, errXVtx, errYVtx, true, false); + } + static bool extrapToVertexWithoutBranson(TrackParam* trackParam, double zVtx) + { + /// Extrapolate track parameters to vertex, corrected for energy loss effects only + /// Add dispersion due to multiple scattering and energy loss fluctuation to parameter covariances + return extrapToVertex(trackParam, 0., 0., zVtx, 0., 0., false, true); + } + static bool extrapToVertexUncorrected(TrackParam* trackParam, double zVtx) + { + /// Extrapolate track parameters to vertex without multiple scattering and energy loss corrections + /// Add dispersion due to multiple scattering to parameter covariances + return extrapToVertex(trackParam, 0., 0., zVtx, 0., 0., false, false); + } + + static double getMCSAngle2(const TrackParam& param, double dZ, double x0); + static void addMCSEffect(TrackParam* trackParam, double dZ, double x0); + + static void printNCalls(); + + private: + static bool extrapToVertex(TrackParam* trackParam, double xVtx, double yVtx, double zVtx, + double errXVtx, double errYVtx, bool correctForMCS, bool correctForEnergyLoss); + + static bool getAbsorberCorrectionParam(double trackXYZIn[3], double trackXYZOut[3], double pTotal, + double& pathLength, double& f0, double& f1, double& f2, + double& meanRho, double& totalELoss, double& sigmaELoss2); + + static void addMCSEffectInAbsorber(TrackParam* param, double signedPathLength, double f0, double f1, double f2); + + static double betheBloch(double pTotal, double pathLength, double rho, double atomicZ, double atomicZoverA); + static double energyLossFluctuation(double pTotal, double pathLength, double rho, double atomicZoverA); + + static bool correctMCSEffectInAbsorber(TrackParam* param, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx, + double absZBeg, double pathLength, double f0, double f1, double f2); + static void correctELossEffectInAbsorber(TrackParam* param, double eLoss, double sigmaELoss2); + + static void cov2CovP(const TMatrixD& param, TMatrixD& cov); + static void covP2Cov(const TMatrixD& param, TMatrixD& covP); + + static void convertTrackParamForExtrap(TrackParam* trackParam, double forwardBackward, double* v3); + static void recoverTrackParam(double* v3, double Charge, TrackParam* trackParam); + + static bool extrapToZRungekutta(TrackParam* trackParam, double zEnd); + static bool extrapToZRungekuttaV2(TrackParam* trackParam, double zEnd); + static bool extrapOneStepRungekutta(double charge, double step, const double* vect, double* vout); + + static constexpr double SMuMass = 0.105658; ///< Muon mass (GeV/c2) + static constexpr double SAbsZBeg = -90.; ///< Position of the begining of the absorber (cm) + static constexpr double SAbsZEnd = -505.; ///< Position of the end of the absorber (cm) + static constexpr double SSimpleBPosition = -0.5 * (994.05 + 986.6); ///< Position of the dipole (cm) + static constexpr double SSimpleBLength = 0.5 * (502.1 + 309.4); ///< Length of the dipole (cm) + static constexpr int SMaxStepNumber = 5000; ///< Maximum number of steps for track extrapolation + static constexpr double SRungeKuttaMaxResidue = 0.002; ///< Max z-distance to destination to stop the track extrap (cm) + static constexpr double SRungeKuttaMaxResidueV2 = 0.01; ///< Max z-distance to destination to stop the track extrap v2 (cm) + /// Most probable value (GeV/c) of muon momentum in bending plane (used when B = 0) + /// Needed to get some "reasonable" corrections for MCS and E loss even if B = 0 + static constexpr double SMostProbBendingMomentum = 2.; + + static bool sExtrapV2; ///< switch to Runge-Kutta extrapolation v2 + + static double sSimpleBValue; ///< Magnetic field value at the centre + static bool sFieldON; ///< true if the field is switched ON + + static std::size_t sNCallExtrapToZCov; ///< number of times the method extrapToZCov(...) is called + static std::size_t sNCallField; ///< number of times the method Field(...) is called +}; + +} // namespace mch +} // namespace o2 + +#endif // ALICEO2_MCH_TRACKEXTRAP_H_ diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinder.h b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFinder.h similarity index 85% rename from Detectors/MUON/MCH/Tracking/src/TrackFinder.h rename to Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFinder.h index c540fee52ccb5..1680b9eae2165 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinder.h +++ b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFinder.h @@ -26,7 +26,7 @@ #include "MCHTracking/Cluster.h" #include "MCHTracking/Track.h" -#include "TrackFitter.h" +#include "MCHTracking/TrackFitter.h" namespace o2 { @@ -52,6 +52,9 @@ class TrackFinder /// set the flag to try to find more track candidates starting from 1 cluster in each of station (1..) 4 and 5 void findMoreTrackCandidates(bool moreCandidates) { mMoreCandidates = moreCandidates; } + /// set the flag to refine the tracks in the end using cluster resolution + void refineTracks(bool refine) { mRefineTracks = refine; } + /// set the debug level defining the verbosity void debug(int debugLevel) { mDebugLevel = debugLevel; } @@ -80,6 +83,8 @@ class TrackFinder void removeConnectedTracks(int stMin, int stMax); + void refineTracks(); + void finalize(); void createTrack(const Cluster& cl1, const Cluster& cl2); @@ -113,6 +118,15 @@ class TrackFinder /// return the chamber to which this plane belong to int getChamberId(int plane) { return (plane < 8) ? plane / 2 : 4 + (plane - 8) / 4; } + /// return the chamber resolution square in x direction + static constexpr double chamberResolutionX2() { return SChamberResolutionX * SChamberResolutionX; } + /// return the chamber resolution square in y direction + static constexpr double chamberResolutionY2() { return SChamberResolutionY * SChamberResolutionY; } + + /// chamber resolution in x direction used as cluster resolution during tracking + static constexpr double SChamberResolutionX = 0.2; + /// chamber resolution in y direction used as cluster resolution during tracking + static constexpr double SChamberResolutionY = 0.2; /// sigma cut to select clusters (local chi2) and tracks (global chi2) during tracking static constexpr double SSigmaCutForTracking = 5.; /// sigma cut to select clusters (local chi2) and tracks (global chi2) during improvement @@ -125,8 +139,8 @@ class TrackFinder static constexpr double SBendingVertexDispersion = 70.; ///< vertex dispersion (cm) in bending plane static constexpr double SMinBendingMomentum = 0.8; ///< minimum value (GeV/c) of momentum in bending plane /// z position of the chambers - static constexpr float SDefaultChamberZ[10] = {-526.16, -545.24, -676.4, -695.4, -967.5, - -998.5, -1276.5, -1307.5, -1406.6, -1437.6}; + static constexpr double SDefaultChamberZ[10] = {-526.16, -545.24, -676.4, -695.4, -967.5, + -998.5, -1276.5, -1307.5, -1406.6, -1437.6}; /// default chamber thickness in X0 for reconstruction static constexpr double SChamberThicknessInX0[10] = {0.065, 0.065, 0.075, 0.075, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035}; @@ -142,7 +156,8 @@ class TrackFinder double mMaxMCSAngle2[10]{}; ///< maximum angle dispersion due to MCS - bool mMoreCandidates = false; ///< try to find more track candidates starting from 1 cluster in each of station (1..) 4 and 5 + bool mMoreCandidates = false; ///< find more track candidates starting from 1 cluster in each of station (1..) 4 and 5 + bool mRefineTracks = true; ///< refine the tracks in the end using cluster resolution int mDebugLevel = 0; ///< debug level defining the verbosity @@ -155,6 +170,7 @@ class TrackFinder std::chrono::duration<double> mTimeFollowTracks{}; ///< timer std::chrono::duration<double> mTimeImproveTracks{}; ///< timer std::chrono::duration<double> mTimeCleanTracks{}; ///< timer + std::chrono::duration<double> mTimeRefineTracks{}; ///< timer }; } // namespace mch diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.h b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFinderOriginal.h similarity index 78% rename from Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.h rename to Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFinderOriginal.h index f740959a86268..bfe1145b373a2 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.h +++ b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFinderOriginal.h @@ -20,7 +20,7 @@ #include "MCHTracking/Cluster.h" #include "MCHTracking/Track.h" -#include "TrackFitter.h" +#include "MCHTracking/TrackFitter.h" namespace o2 { @@ -45,6 +45,9 @@ class TrackFinderOriginal /// set the flag to try to find more track candidates starting from 1 cluster in each of station (1..) 4 and 5 void findMoreTrackCandidates(bool moreCandidates) { mMoreCandidates = moreCandidates; } + /// set the flag to refine the tracks in the end using cluster resolution + void refineTracks(bool refine) { mRefineTracks = refine; } + /// set the debug level defining the verbosity void debug(int debugLevel) { mDebugLevel = debugLevel; } @@ -70,6 +73,7 @@ class TrackFinderOriginal std::list<Track>::iterator recoverTrack(std::list<Track>::iterator& itTrack, int nextStation); bool completeTracks(); void improveTracks(); + void refineTracks(); void finalize(); uint8_t requestedStationMask() const; int getTrackIndex(const std::list<Track>::iterator& itCurrentTrack) const; @@ -78,24 +82,33 @@ class TrackFinderOriginal template <class... Args> void print(Args... args) const; + /// return the chamber resolution square in x direction + static constexpr double chamberResolutionX2() { return SChamberResolutionX * SChamberResolutionX; } + /// return the chamber resolution square in y direction + static constexpr double chamberResolutionY2() { return SChamberResolutionY * SChamberResolutionY; } + + /// chamber resolution in x direction used as cluster resolution during tracking + static constexpr double SChamberResolutionX = 0.2; + /// chamber resolution in y direction used as cluster resolution during tracking + static constexpr double SChamberResolutionY = 0.2; /// sigma cut to select clusters (local chi2) and tracks (global chi2) during tracking static constexpr double SSigmaCutForTracking = 5.; /// sigma cut to select clusters (local chi2) and tracks (global chi2) during improvement static constexpr double SSigmaCutForImprovement = 4.; - ///< maximum distance to the track to search for compatible cluster(s) in non bending direction + /// maximum distance to the track to search for compatible cluster(s) in non bending direction static constexpr double SMaxNonBendingDistanceToTrack = 1.; - ///< maximum distance to the track to search for compatible cluster(s) in bending direction + /// maximum distance to the track to search for compatible cluster(s) in bending direction static constexpr double SMaxBendingDistanceToTrack = 1.; static constexpr double SNonBendingVertexDispersion = 70.; ///< vertex dispersion (cm) in non bending plane static constexpr double SBendingVertexDispersion = 70.; ///< vertex dispersion (cm) in bending plane static constexpr double SMinBendingMomentum = 0.8; ///< minimum value (GeV/c) of momentum in bending plane /// z position of the chambers - static constexpr float SDefaultChamberZ[10] = {-526.16, -545.24, -676.4, -695.4, -967.5, - -998.5, -1276.5, -1307.5, -1406.6, -1437.6}; + static constexpr double SDefaultChamberZ[10] = {-526.16, -545.24, -676.4, -695.4, -967.5, + -998.5, -1276.5, -1307.5, -1406.6, -1437.6}; /// default chamber thickness in X0 for reconstruction static constexpr double SChamberThicknessInX0[10] = {0.065, 0.065, 0.075, 0.075, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035}; - ///< if true, at least one cluster in the station is requested to validate the track + /// if true, at least one cluster in the station is requested to validate the track static constexpr bool SRequestStation[5] = {true, true, true, true, true}; TrackFitter mTrackFitter{}; /// track fitter @@ -105,7 +118,8 @@ class TrackFinderOriginal double mMaxMCSAngle2[10]{}; ///< maximum angle dispersion due to MCS - bool mMoreCandidates = false; ///< try to find more track candidates starting from 1 cluster in each of station (1..) 4 and 5 + bool mMoreCandidates = false; ///< find more track candidates starting from 1 cluster in each of station (1..) 4 and 5 + bool mRefineTracks = true; ///< refine the tracks in the end using cluster resolution int mDebugLevel = 0; ///< debug level defining the verbosity @@ -119,6 +133,7 @@ class TrackFinderOriginal std::chrono::duration<double> mTimeCompleteTracks{}; ///< timer std::chrono::duration<double> mTimeImproveTracks{}; ///< timer std::chrono::duration<double> mTimeCleanTracks{}; ///< timer + std::chrono::duration<double> mTimeRefineTracks{}; ///< timer }; } // namespace mch diff --git a/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFitter.h b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFitter.h new file mode 100644 index 0000000000000..2e523540f7a22 --- /dev/null +++ b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackFitter.h @@ -0,0 +1,95 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackFitter.h +/// \brief Definition of a class to fit a track to a set of clusters +/// +/// \author Philippe Pillot, Subatech + +#ifndef ALICEO2_MCH_TRACKFITTER_H_ +#define ALICEO2_MCH_TRACKFITTER_H_ + +#include "MCHTracking/Cluster.h" +#include "MCHTracking/Track.h" +#include "MCHTracking/TrackParam.h" + +namespace o2 +{ +namespace mch +{ + +/// Class to fit a track to a set of clusters +class TrackFitter +{ + public: + TrackFitter() = default; + ~TrackFitter() = default; + + TrackFitter(const TrackFitter&) = delete; + TrackFitter& operator=(const TrackFitter&) = delete; + TrackFitter(TrackFitter&&) = delete; + TrackFitter& operator=(TrackFitter&&) = delete; + + void initField(float l3Current, float dipoleCurrent); + + /// Enable/disable the smoother (and the saving of related parameters) + void smoothTracks(bool smooth) { mSmooth = smooth; } + /// Return the smoother enable/disable flag + bool isSmootherEnabled() { return mSmooth; } + + /// Use the own resolution of each cluster during the fit (default) + void useClusterResolution() { mUseChamberResolution = false; } + /// Use the chamber resolution instead of cluster resolution during the fit + void useChamberResolution() { mUseChamberResolution = true; } + // Set the chamber resolution in x and y directions + void setChamberResolution(double ex, double ey); + + void fit(Track& track, bool smooth = true, bool finalize = true, + std::list<TrackParam>::reverse_iterator* itStartingParam = nullptr); + + void runKalmanFilter(TrackParam& trackParam); + + /// Return the maximum chi2 above which the track can be considered as abnormal + static constexpr double getMaxChi2() { return SMaxChi2; } + + private: + void initTrack(const Cluster& cl1, const Cluster& cl2, TrackParam& param); + void addCluster(const TrackParam& startingParam, const Cluster& cl, TrackParam& param); + void smoothTrack(Track& track, bool finalize); + void runSmoother(const TrackParam& previousParam, TrackParam& param); + + static constexpr double SMaxChi2 = 2.e10; ///< maximum chi2 above which the track can be considered as abnormal + static constexpr double SBendingVertexDispersion = 70.; ///< vertex dispersion (cm) in bending plane + /// z position of the chambers + static constexpr double SDefaultChamberZ[10] = {-526.16, -545.24, -676.4, -695.4, -967.5, + -998.5, -1276.5, -1307.5, -1406.6, -1437.6}; + /// default chamber thickness in X0 for reconstruction + static constexpr double SChamberThicknessInX0[10] = {0.065, 0.065, 0.075, 0.075, 0.035, + 0.035, 0.035, 0.035, 0.035, 0.035}; + + bool mSmooth = false; ///< switch ON/OFF the smoother + + bool mUseChamberResolution = false; ///< switch between using chamber or cluster resolution + double mChamberResolutionX2 = 0.04; ///< chamber resolution square in x direction + double mChamberResolutionY2 = 0.04; ///< chamber resolution square in y direction +}; + +//_________________________________________________________________________________________________ +inline void TrackFitter::setChamberResolution(double ex, double ey) +{ + /// Set the chamber resolution in x and y directions + mChamberResolutionX2 = ex * ex; + mChamberResolutionY2 = ey * ey; +} + +} // namespace mch +} // namespace o2 + +#endif // ALICEO2_MCH_TRACKFITTER_H_ diff --git a/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackParam.h b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackParam.h index 0dc37971fdef7..9415f238b54a6 100644 --- a/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackParam.h +++ b/Detectors/MUON/MCH/Tracking/include/MCHTracking/TrackParam.h @@ -16,6 +16,7 @@ #ifndef ALICEO2_MCH_TRACKPARAM_H_ #define ALICEO2_MCH_TRACKPARAM_H_ +#include <memory> // for std::unique_ptr #include <TMatrixD.h> #include "MCHBase/TrackBlock.h" @@ -32,6 +33,8 @@ class TrackParam { public: TrackParam() = default; + TrackParam(Double_t z, const Double_t param[5]); + TrackParam(Double_t z, const Double_t param[5], const Double_t cov[15]); ~TrackParam() = default; TrackParam(const TrackParam& tp); @@ -68,14 +71,17 @@ class TrackParam /// set the charge (assumed forward motion) void setCharge(Double_t charge) { - if (charge * mParameters(4, 0) < 0.) + if (charge * mParameters(4, 0) < 0.) { mParameters(4, 0) *= -1.; + } } /// return track parameters const TMatrixD& getParameters() const { return mParameters; } /// set track parameters void setParameters(const TMatrixD& parameters) { mParameters = parameters; } + /// set track parameters from the array + void setParameters(const Double_t parameters[5]) { mParameters.SetMatrixArray(parameters); } /// add track parameters void addParameters(const TMatrixD& parameters) { mParameters += parameters; } @@ -89,8 +95,8 @@ class TrackParam const TMatrixD& getCovariances() const; void setCovariances(const TMatrixD& covariances); - void setCovariances(const Double_t matrix[5][5]); - void setVariances(const Double_t matrix[5][5]); + void setCovariances(const Double_t covariances[15]); + void setVariances(const Double_t covariances[15]); void deleteCovariances(); const TMatrixD& getPropagator() const; diff --git a/Detectors/MUON/MCH/Tracking/src/TrackAtVertexSpec.cxx b/Detectors/MUON/MCH/Tracking/src/TrackAtVertexSpec.cxx deleted file mode 100644 index 6456f5ecf7b93..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackAtVertexSpec.cxx +++ /dev/null @@ -1,379 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackAtVertexSpec.cxx -/// \brief Implementation of a data processor to extrapolate the tracks to the vertex -/// -/// \author Philippe Pillot, Subatech - -#include "TrackAtVertexSpec.h" - -#include <chrono> -#include <stdexcept> -#include <list> - -#include <TMath.h> -#include <TGeoManager.h> -#include <TGeoGlobalMagField.h> - -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -#include "DetectorsBase/GeometryManager.h" -#include "MathUtils/Cartesian3D.h" -#include "Field/MagneticField.h" -#include "MCHBase/ClusterBlock.h" -#include "MCHBase/TrackBlock.h" -#include "MCHTracking/TrackParam.h" -#include "TrackExtrap.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class TrackAtVertexTask -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Prepare the track extrapolation tools - - LOG(INFO) << "initializing track extrapolation to vertex"; - - if (!gGeoManager) { - o2::base::GeometryManager::loadGeometry(); - if (!gGeoManager) { - throw runtime_error("cannot load the geometry"); - } - } - - if (!TGeoGlobalMagField::Instance()->GetField()) { - auto l3Current = ic.options().get<float>("l3Current"); - auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); - auto field = o2::field::MagneticField::createFieldMap(l3Current, dipoleCurrent, o2::field::MagneticField::kConvLHC, false, - 3500., "A-A", "$(O2_ROOT)/share/Common/maps/mfchebKGI_sym.root"); - TGeoGlobalMagField::Instance()->SetField(field); - TGeoGlobalMagField::Instance()->Lock(); - TrackExtrap::setField(); - } - - auto stop = [this]() { - LOG(INFO) << "track propagation to vertex duration = " << mElapsedTime.count() << " s"; - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// read the tracks with attached clusters of the current event, - /// propagate them to the corresponding vertex and send the new version - - // get the vertex - Point3D<double> vertex(0., 0., 0.); - int eventVtx = readVertex(pc.inputs().get<gsl::span<char>>("vertex"), vertex); - - // get the tracks - std::list<TrackStruct> tracks{}; - int eventTracks = readTracks(pc.inputs().get<gsl::span<char>>("tracks"), tracks); - - if (eventVtx > -1 && eventVtx != eventTracks) { - throw runtime_error("vertex and tracks are from different events"); - } - - // propagate the tracks to the vertex - auto tStart = std::chrono::high_resolution_clock::now(); - for (auto itTrack = tracks.begin(); itTrack != tracks.end();) { - if (extrapTrackToVertex(*itTrack, vertex)) { - ++itTrack; - } else { - itTrack = tracks.erase(itTrack); - } - } - auto tEnd = std::chrono::high_resolution_clock::now(); - mElapsedTime += tEnd - tStart; - - // calculate the size of the payload for the output message, excluding the event header - int trackSize = getSize(tracks); - - // create the output message - auto msgOut = pc.outputs().make<char>(Output{"MCH", "TRACKSATVERTEX", 0, Lifetime::Timeframe}, 2 * SSizeOfInt + trackSize); - auto bufferPtrOut = msgOut.data(); - - // write the event header - writeHeader(eventTracks, trackSize, bufferPtrOut); - - // write the tracks - if (trackSize > 0) { - writeTracks(tracks, bufferPtrOut); - } - } - - private: - struct TrackStruct { - TrackParamStruct paramAtVertex{}; - double dca = 0.; - double rAbs = 0.; - TrackParamStruct paramAt1stCluster{}; - double chi2 = 0.; - std::vector<ClusterStruct> clusters{}; - }; - - //_________________________________________________________________________________________________ - int readVertex(const gsl::span<const char>& msgIn, Point3D<double>& vertex) const - { - /// get the vertex and return the event number - /// throw an exception in case of error - - auto bufferPtr = msgIn.data(); - int size = msgIn.size(); - - if (size != SSizeOfInt + SSizeOfPoint3D) { - throw length_error("incorrect payload size"); - } - - const int& event = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - - vertex = *reinterpret_cast<const Point3D<double>*>(bufferPtr); - - return event; - } - - //_________________________________________________________________________________________________ - int readTracks(const gsl::span<const char>& msgIn, std::list<TrackStruct>& tracks) const - { - /// get the tracks and return the event number - /// throw an exception in case of error - - auto bufferPtr = msgIn.data(); - int size = msgIn.size(); - - if (size < 2 * SSizeOfInt) { - throw out_of_range("missing event header"); - } - const int& event = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - int sizeLeft = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - - if (sizeLeft != size - 2 * SSizeOfInt) { - throw length_error("incorrect payload size"); - } - - if (sizeLeft > 0) { - - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing number of tracks"); - } - const int& nTracks = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - for (int iTrack = 0; iTrack < nTracks; ++iTrack) { - - tracks.emplace_back(); - auto& track = tracks.back(); - - if (sizeLeft < SSizeOfTrackParamStruct) { - throw out_of_range("missing track parameters"); - } - track.paramAt1stCluster = *reinterpret_cast<const TrackParamStruct*>(bufferPtr); - bufferPtr += SSizeOfTrackParamStruct; - sizeLeft -= SSizeOfTrackParamStruct; - - if (sizeLeft < SSizeOfDouble) { - throw out_of_range("missing chi2"); - } - track.chi2 = *reinterpret_cast<const double*>(bufferPtr); - bufferPtr += SSizeOfDouble; - sizeLeft -= SSizeOfDouble; - - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing number of clusters"); - } - const int& nClusters = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - for (int iCl = 0; iCl < nClusters; ++iCl) { - - if (sizeLeft < SSizeOfClusterStruct) { - throw out_of_range("missing cluster"); - } - track.clusters.emplace_back(*reinterpret_cast<const ClusterStruct*>(bufferPtr)); - bufferPtr += SSizeOfClusterStruct; - sizeLeft -= SSizeOfClusterStruct; - } - } - - if (sizeLeft != 0) { - throw length_error("incorrect payload size"); - } - } - - return event; - } - - //_________________________________________________________________________________________________ - bool extrapTrackToVertex(TrackStruct& track, Point3D<double>& vertex) - { - /// compute the track parameters at vertex, at DCA and at the end of the absorber - - // convert parameters at first cluster in internal format - TrackParam trackParam; - trackParam.setNonBendingCoor(track.paramAt1stCluster.x); - trackParam.setBendingCoor(track.paramAt1stCluster.y); - trackParam.setZ(track.paramAt1stCluster.z); - trackParam.setNonBendingSlope(track.paramAt1stCluster.px / track.paramAt1stCluster.pz); - trackParam.setBendingSlope(track.paramAt1stCluster.py / track.paramAt1stCluster.pz); - trackParam.setInverseBendingMomentum(track.paramAt1stCluster.sign / TMath::Sqrt(track.paramAt1stCluster.py * track.paramAt1stCluster.py + track.paramAt1stCluster.pz * track.paramAt1stCluster.pz)); - - // extrapolate to vertex - TrackParam trackParamAtVertex(trackParam); - if (!TrackExtrap::extrapToVertex(&trackParamAtVertex, vertex.x(), vertex.y(), vertex.z(), 0., 0.)) { - return false; - } - track.paramAtVertex.x = trackParamAtVertex.getNonBendingCoor(); - track.paramAtVertex.y = trackParamAtVertex.getBendingCoor(); - track.paramAtVertex.z = trackParamAtVertex.getZ(); - track.paramAtVertex.px = trackParamAtVertex.px(); - track.paramAtVertex.py = trackParamAtVertex.py(); - track.paramAtVertex.pz = trackParamAtVertex.pz(); - track.paramAtVertex.sign = trackParamAtVertex.getCharge(); - - // extrapolate to DCA - TrackParam trackParamAtDCA(trackParam); - if (!TrackExtrap::extrapToVertexWithoutBranson(&trackParamAtDCA, vertex.z())) { - return false; - } - double dcaX = trackParamAtDCA.getNonBendingCoor() - vertex.x(); - double dcaY = trackParamAtDCA.getBendingCoor() - vertex.y(); - track.dca = TMath::Sqrt(dcaX * dcaX + dcaY * dcaY); - - // extrapolate to the end of the absorber - if (!TrackExtrap::extrapToZ(&trackParam, -505.)) { - return false; - } - double xAbs = trackParam.getNonBendingCoor(); - double yAbs = trackParam.getBendingCoor(); - track.rAbs = TMath::Sqrt(xAbs * xAbs + yAbs * yAbs); - - return true; - } - - //_________________________________________________________________________________________________ - int getSize(const std::list<TrackStruct>& tracks) - { - /// calculate the total number of bytes requested to store the tracks - - int size(0); - for (const auto& track : tracks) { - size += 2 * SSizeOfTrackParamStruct + 3 * SSizeOfDouble + SSizeOfInt + track.clusters.size() * SSizeOfClusterStruct; - } - if (size > 0) { - size += SSizeOfInt; - } - - return size; - } - - //_________________________________________________________________________________________________ - void writeHeader(int event, int trackSize, char*& bufferPtr) const - { - /// write header informations in the output buffer and move the buffer ptr - - // write the event number - memcpy(bufferPtr, &event, SSizeOfInt); - bufferPtr += SSizeOfInt; - - // write the size of the payload - memcpy(bufferPtr, &trackSize, SSizeOfInt); - bufferPtr += SSizeOfInt; - } - - //_________________________________________________________________________________________________ - void writeTracks(const std::list<TrackStruct>& tracks, char*& bufferPtr) const - { - /// write the track informations in the buffer and move the buffer ptr - - // write the number of tracks - int nTracks = tracks.size(); - memcpy(bufferPtr, &nTracks, SSizeOfInt); - bufferPtr += SSizeOfInt; - - for (const auto& track : tracks) { - - // write track parameters at vertex - memcpy(bufferPtr, &(track.paramAtVertex), SSizeOfTrackParamStruct); - bufferPtr += SSizeOfTrackParamStruct; - - // write dca - memcpy(bufferPtr, &(track.dca), SSizeOfDouble); - bufferPtr += SSizeOfDouble; - - // write rAbs - memcpy(bufferPtr, &(track.rAbs), SSizeOfDouble); - bufferPtr += SSizeOfDouble; - - // write track parameters at first cluster - memcpy(bufferPtr, &(track.paramAt1stCluster), SSizeOfTrackParamStruct); - bufferPtr += SSizeOfTrackParamStruct; - - // write track chi2 - memcpy(bufferPtr, &(track.chi2), SSizeOfDouble); - bufferPtr += SSizeOfDouble; - - // write the number of clusters - int nClusters = track.clusters.size(); - memcpy(bufferPtr, &nClusters, SSizeOfInt); - bufferPtr += SSizeOfInt; - - // write clusters - for (const auto& cluster : track.clusters) { - memcpy(bufferPtr, &cluster, SSizeOfClusterStruct); - bufferPtr += SSizeOfClusterStruct; - } - } - } - - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SSizeOfDouble = sizeof(double); - static constexpr int SSizeOfTrackParamStruct = sizeof(TrackParamStruct); - static constexpr int SSizeOfClusterStruct = sizeof(ClusterStruct); - static constexpr int SSizeOfPoint3D = sizeof(Point3D<double>); - - std::chrono::duration<double> mElapsedTime{}; ///< timer -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackAtVertexSpec() -{ - return DataProcessorSpec{ - "TrackAtVertex", - Inputs{InputSpec{"vertex", "MCH", "VERTEX", 0, Lifetime::Timeframe}, - InputSpec{"tracks", "MCH", "TRACKS", 0, Lifetime::Timeframe}}, - Outputs{OutputSpec{"MCH", "TRACKSATVERTEX", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<TrackAtVertexTask>()}, - Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, - {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}}}; -} - -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackExtrap.cxx b/Detectors/MUON/MCH/Tracking/src/TrackExtrap.cxx index 38424a5b43a51..9fe21ca5a455f 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackExtrap.cxx +++ b/Detectors/MUON/MCH/Tracking/src/TrackExtrap.cxx @@ -13,7 +13,7 @@ /// /// \author Philippe Pillot, Subatech -#include "TrackExtrap.h" +#include "MCHTracking/TrackExtrap.h" #include <TGeoGlobalMagField.h> #include <TGeoManager.h> diff --git a/Detectors/MUON/MCH/Tracking/src/TrackExtrap.h b/Detectors/MUON/MCH/Tracking/src/TrackExtrap.h deleted file mode 100644 index 659f6fe0f68b7..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackExtrap.h +++ /dev/null @@ -1,141 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackExtrap.h -/// \brief Definition of tools for track extrapolation -/// -/// \author Philippe Pillot, Subatech - -#ifndef ALICEO2_MCH_TRACKEXTRAP_H_ -#define ALICEO2_MCH_TRACKEXTRAP_H_ - -#include <cstddef> - -#include <TMatrixD.h> - -namespace o2 -{ -namespace mch -{ - -class TrackParam; - -/// Class holding tools for track extrapolation -class TrackExtrap -{ - public: - // static class - TrackExtrap() = delete; - ~TrackExtrap() = delete; - - TrackExtrap(const TrackExtrap&) = delete; - TrackExtrap& operator=(const TrackExtrap&) = delete; - TrackExtrap(TrackExtrap&&) = delete; - TrackExtrap& operator=(TrackExtrap&&) = delete; - - static void setField(); - - /// Return true if the field is switched ON - static bool isFieldON() { return sFieldON; } - - /// Switch to Runge-Kutta extrapolation v2 - static void useExtrapV2() { sExtrapV2 = true; } - - static double getImpactParamFromBendingMomentum(double bendingMomentum); - static double getBendingMomentumFromImpactParam(double impactParam); - - static void linearExtrapToZ(TrackParam* trackParam, double zEnd); - static void linearExtrapToZCov(TrackParam* trackParam, double zEnd, bool updatePropagator = false); - - static bool extrapToZ(TrackParam* trackParam, double zEnd); - static bool extrapToZCov(TrackParam* trackParam, double zEnd, bool updatePropagator = false); - - static bool extrapToVertex(TrackParam* trackParam, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx) - { - /// Extrapolate track parameters to vertex, corrected for multiple scattering and energy loss effects - /// Add branson correction resolution and energy loss fluctuation to parameter covariances - return extrapToVertex(trackParam, xVtx, yVtx, zVtx, errXVtx, errYVtx, true, true); - } - static bool extrapToVertexWithoutELoss(TrackParam* trackParam, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx) - { - /// Extrapolate track parameters to vertex, corrected for multiple scattering effects only - /// Add branson correction resolution to parameter covariances - return extrapToVertex(trackParam, xVtx, yVtx, zVtx, errXVtx, errYVtx, true, false); - } - static bool extrapToVertexWithoutBranson(TrackParam* trackParam, double zVtx) - { - /// Extrapolate track parameters to vertex, corrected for energy loss effects only - /// Add dispersion due to multiple scattering and energy loss fluctuation to parameter covariances - return extrapToVertex(trackParam, 0., 0., zVtx, 0., 0., false, true); - } - static bool extrapToVertexUncorrected(TrackParam* trackParam, double zVtx) - { - /// Extrapolate track parameters to vertex without multiple scattering and energy loss corrections - /// Add dispersion due to multiple scattering to parameter covariances - return extrapToVertex(trackParam, 0., 0., zVtx, 0., 0., false, false); - } - - static double getMCSAngle2(const TrackParam& param, double dZ, double x0); - static void addMCSEffect(TrackParam* trackParam, double dZ, double x0); - - static void printNCalls(); - - private: - static bool extrapToVertex(TrackParam* trackParam, double xVtx, double yVtx, double zVtx, - double errXVtx, double errYVtx, bool correctForMCS, bool correctForEnergyLoss); - - static bool getAbsorberCorrectionParam(double trackXYZIn[3], double trackXYZOut[3], double pTotal, - double& pathLength, double& f0, double& f1, double& f2, - double& meanRho, double& totalELoss, double& sigmaELoss2); - - static void addMCSEffectInAbsorber(TrackParam* param, double signedPathLength, double f0, double f1, double f2); - - static double betheBloch(double pTotal, double pathLength, double rho, double atomicZ, double atomicZoverA); - static double energyLossFluctuation(double pTotal, double pathLength, double rho, double atomicZoverA); - - static bool correctMCSEffectInAbsorber(TrackParam* param, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx, - double absZBeg, double pathLength, double f0, double f1, double f2); - static void correctELossEffectInAbsorber(TrackParam* param, double eLoss, double sigmaELoss2); - - static void cov2CovP(const TMatrixD& param, TMatrixD& cov); - static void covP2Cov(const TMatrixD& param, TMatrixD& covP); - - static void convertTrackParamForExtrap(TrackParam* trackParam, double forwardBackward, double* v3); - static void recoverTrackParam(double* v3, double Charge, TrackParam* trackParam); - - static bool extrapToZRungekutta(TrackParam* trackParam, double zEnd); - static bool extrapToZRungekuttaV2(TrackParam* trackParam, double zEnd); - static bool extrapOneStepRungekutta(double charge, double step, const double* vect, double* vout); - - static constexpr double SMuMass = 0.105658; ///< Muon mass (GeV/c2) - static constexpr double SAbsZBeg = -90.; ///< Position of the begining of the absorber (cm) - static constexpr double SAbsZEnd = -505.; ///< Position of the end of the absorber (cm) - static constexpr double SSimpleBPosition = -0.5 * (994.05 + 986.6); ///< Position of the dipole (cm) - static constexpr double SSimpleBLength = 0.5 * (502.1 + 309.4); ///< Length of the dipole (cm) - static constexpr int SMaxStepNumber = 5000; ///< Maximum number of steps for track extrapolation - static constexpr double SRungeKuttaMaxResidue = 0.002; ///< Max z-distance to destination to stop the track extrap (cm) - static constexpr double SRungeKuttaMaxResidueV2 = 0.01; ///< Max z-distance to destination to stop the track extrap v2 (cm) - /// Most probable value (GeV/c) of muon momentum in bending plane (used when B = 0) - /// Needed to get some "reasonable" corrections for MCS and E loss even if B = 0 - static constexpr double SMostProbBendingMomentum = 2.; - - static bool sExtrapV2; ///< switch to Runge-Kutta extrapolation v2 - - static double sSimpleBValue; ///< Magnetic field value at the centre - static bool sFieldON; ///< true if the field is switched ON - - static std::size_t sNCallExtrapToZCov; ///< number of times the method extrapToZCov(...) is called - static std::size_t sNCallField; ///< number of times the method Field(...) is called -}; - -} // namespace mch -} // namespace o2 - -#endif // ALICEO2_MCH_TRACKEXTRAP_H_ diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinder.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFinder.cxx index 73395f545e7e3..19c5177a68165 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinder.cxx +++ b/Detectors/MUON/MCH/Tracking/src/TrackFinder.cxx @@ -13,7 +13,7 @@ /// /// \author Philippe Pillot, Subatech -#include "TrackFinder.h" +#include "MCHTracking/TrackFinder.h" #include <cassert> #include <iostream> @@ -24,7 +24,7 @@ #include <TMath.h> #include "Field/MagneticField.h" -#include "TrackExtrap.h" +#include "MCHTracking/TrackExtrap.h" namespace o2 { @@ -33,7 +33,7 @@ namespace mch using namespace std; -constexpr float TrackFinder::SDefaultChamberZ[10]; +constexpr double TrackFinder::SDefaultChamberZ[10]; constexpr double TrackFinder::SChamberThicknessInX0[10]; constexpr bool TrackFinder::SRequestStation[5]; constexpr int TrackFinder::SNDE[10]; @@ -49,6 +49,9 @@ void TrackFinder::init(float l3Current, float dipoleCurrent) // enable the track smoother mTrackFitter.smoothTracks(true); + // set the chamber resolution used for fitting the tracks during the tracking + mTrackFitter.setChamberResolution(SChamberResolutionX, SChamberResolutionY); + // use the Runge-Kutta extrapolation v2 TrackExtrap::useExtrapV2(); @@ -147,6 +150,9 @@ const std::list<Track>& TrackFinder::findTracks(const std::unordered_map<int, st } } + // use the chamber resolution when fitting the tracks during the tracking + mTrackFitter.useChamberResolution(); + // find track candidates on stations 4 and 5 auto tStart = std::chrono::high_resolution_clock::now(); findTrackCandidates(); @@ -181,14 +187,22 @@ const std::list<Track>& TrackFinder::findTracks(const std::unordered_map<int, st tEnd = std::chrono::high_resolution_clock::now(); mTimeImproveTracks += tEnd - tStart; - // Remove connected tracks in stations(1..) 3, 4 and 5 + // remove connected tracks in stations(1..) 3, 4 and 5 tStart = std::chrono::high_resolution_clock::now(); removeConnectedTracks(2, 4); tEnd = std::chrono::high_resolution_clock::now(); mTimeCleanTracks += tEnd - tStart; - // Set the final track parameters and covariances - finalize(); + // refine the tracks using cluster resolution or just finalize them + if (mRefineTracks) { + tStart = std::chrono::high_resolution_clock::now(); + mTrackFitter.useClusterResolution(); + refineTracks(); + tEnd = std::chrono::high_resolution_clock::now(); + mTimeRefineTracks += tEnd - tStart; + } else { + finalize(); + } return mTracks; } @@ -628,7 +642,7 @@ std::list<Track>::iterator TrackFinder::findTrackCandidates(int plane1, int plan // check if non bending impact parameter is within tolerances double nonBendingSlope = (cluster1.getX() - cluster2.getX()) / dZ; double nonBendingImpactParam = TMath::Abs(cluster1.getX() - cluster1.getZ() * nonBendingSlope); - double nonBendingImpactParamErr = TMath::Sqrt((z1 * z1 * cluster2.getEx2() + z2 * z2 * cluster1.getEx2()) / dZ / dZ + impactMCS2); + double nonBendingImpactParamErr = TMath::Sqrt((z1 * z1 * chamberResolutionX2() + z2 * z2 * chamberResolutionX2()) / dZ / dZ + impactMCS2); if ((nonBendingImpactParam - SSigmaCutForTracking * nonBendingImpactParamErr) > (3. * SNonBendingVertexDispersion)) { continue; } @@ -637,7 +651,7 @@ std::list<Track>::iterator TrackFinder::findTrackCandidates(int plane1, int plan if (TrackExtrap::isFieldON()) { // depending whether the field is ON or OFF // check if bending momentum is within tolerances double bendingImpactParam = cluster1.getY() - cluster1.getZ() * bendingSlope; - double bendingImpactParamErr2 = (z1 * z1 * cluster2.getEy2() + z2 * z2 * cluster1.getEy2()) / dZ / dZ + impactMCS2; + double bendingImpactParamErr2 = (z1 * z1 * chamberResolutionY2() + z2 * z2 * chamberResolutionY2()) / dZ / dZ + impactMCS2; double bendingMomentum = TMath::Abs(TrackExtrap::getBendingMomentumFromImpactParam(bendingImpactParam)); double bendingMomentumErr = TMath::Sqrt((bendingVertexDispersion2 + bendingImpactParamErr2) / bendingImpactParam / bendingImpactParam + 0.01) * bendingMomentum; if ((bendingMomentum + 3. * bendingMomentumErr) < SMinBendingMomentum) { @@ -646,7 +660,7 @@ std::list<Track>::iterator TrackFinder::findTrackCandidates(int plane1, int plan } else { // or check if bending impact parameter is within tolerances double bendingImpactParam = TMath::Abs(cluster1.getY() - cluster1.getZ() * bendingSlope); - double bendingImpactParamErr = TMath::Sqrt((z1 * z1 * cluster2.getEy2() + z2 * z2 * cluster1.getEy2()) / dZ / dZ + impactMCS2); + double bendingImpactParamErr = TMath::Sqrt((z1 * z1 * chamberResolutionY2() + z2 * z2 * chamberResolutionY2()) / dZ / dZ + impactMCS2); if ((bendingImpactParam - SSigmaCutForTracking * bendingImpactParamErr) > (3. * SBendingVertexDispersion)) { continue; } @@ -1222,6 +1236,22 @@ void TrackFinder::removeConnectedTracks(int stMin, int stMax) } } +//_________________________________________________________________________________________________ +void TrackFinder::refineTracks() +{ + /// Refit, smooth and finalize the reconstructed tracks + + for (auto itTrack = mTracks.begin(); itTrack != mTracks.end();) { + try { + mTrackFitter.fit(*itTrack); + ++itTrack; + } catch (exception const&) { + print("refineTracks: removing candidate at position #", getTrackIndex(itTrack)); + itTrack = mTracks.erase(itTrack); + } + } +} + //_________________________________________________________________________________________________ void TrackFinder::finalize() { @@ -1489,8 +1519,8 @@ bool TrackFinder::tryOneClusterFast(const TrackParam& param, const Cluster& clus double dX = cluster.getX() - (param.getNonBendingCoor() + param.getNonBendingSlope() * dZ); double dY = cluster.getY() - (param.getBendingCoor() + param.getBendingSlope() * dZ); const TMatrixD& paramCov = param.getCovariances(); - double errX2 = paramCov(0, 0) + dZ * dZ * paramCov(1, 1) + 2. * dZ * paramCov(0, 1) + cluster.getEx2(); - double errY2 = paramCov(2, 2) + dZ * dZ * paramCov(3, 3) + 2. * dZ * paramCov(2, 3) + cluster.getEy2(); + double errX2 = paramCov(0, 0) + dZ * dZ * paramCov(1, 1) + 2. * dZ * paramCov(0, 1) + chamberResolutionX2(); + double errY2 = paramCov(2, 2) + dZ * dZ * paramCov(3, 3) + 2. * dZ * paramCov(2, 3) + chamberResolutionY2(); double dXmax = SSigmaCutForTracking * TMath::Sqrt(2. * errX2) + SMaxNonBendingDistanceToTrack; double dYmax = SSigmaCutForTracking * TMath::Sqrt(2. * errY2) + SMaxBendingDistanceToTrack; @@ -1524,8 +1554,8 @@ double TrackFinder::tryOneCluster(const TrackParam& param, const Cluster& cluste // Combine the cluster and track resolutions and covariances const TMatrixD& paramCov = paramAtCluster.getCovariances(); - double sigmaX2 = paramCov(0, 0) + cluster.getEx2(); - double sigmaY2 = paramCov(2, 2) + cluster.getEy2(); + double sigmaX2 = paramCov(0, 0) + chamberResolutionX2(); + double sigmaY2 = paramCov(2, 2) + chamberResolutionY2(); double covXY = paramCov(0, 2); double det = sigmaX2 * sigmaY2 - covXY * covXY; @@ -1635,6 +1665,7 @@ void TrackFinder::printTimers() const LOG(INFO) << "followTracks duration = " << mTimeFollowTracks.count() << " s"; LOG(INFO) << "improveTracks duration = " << mTimeImproveTracks.count() << " s"; LOG(INFO) << "removeConnectedTracks duration = " << mTimeCleanTracks.count() << " s"; + LOG(INFO) << "refineTracks duration = " << mTimeRefineTracks.count() << " s"; } } // namespace mch diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.cxx index 704f3c068d77d..b032bb5813e37 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.cxx +++ b/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.cxx @@ -13,7 +13,7 @@ /// /// \author Philippe Pillot, Subatech -#include "TrackFinderOriginal.h" +#include "MCHTracking/TrackFinderOriginal.h" #include <iostream> #include <stdexcept> @@ -23,7 +23,7 @@ #include <TMath.h> #include "Field/MagneticField.h" -#include "TrackExtrap.h" +#include "MCHTracking/TrackExtrap.h" namespace o2 { @@ -32,7 +32,7 @@ namespace mch using namespace std; -constexpr float TrackFinderOriginal::SDefaultChamberZ[10]; +constexpr double TrackFinderOriginal::SDefaultChamberZ[10]; constexpr double TrackFinderOriginal::SChamberThicknessInX0[10]; constexpr bool TrackFinderOriginal::SRequestStation[5]; @@ -47,6 +47,9 @@ void TrackFinderOriginal::init(float l3Current, float dipoleCurrent) // Enable the track smoother mTrackFitter.smoothTracks(true); + // Set the chamber resolution used for fitting the tracks during the tracking + mTrackFitter.setChamberResolution(SChamberResolutionX, SChamberResolutionY); + // Set the maximum MCS angle in chamber from the minimum acceptable momentum TrackParam param{}; double inverseBendingP = (SMinBendingMomentum > 0.) ? 1. / SMinBendingMomentum : 1.; @@ -59,12 +62,15 @@ void TrackFinderOriginal::init(float l3Current, float dipoleCurrent) //_________________________________________________________________________________________________ const std::list<Track>& TrackFinderOriginal::findTracks(const std::array<std::list<Cluster>, 10>* clusters) { - /// Run the orginal track finder algorithm + /// Run the original track finder algorithm print("\n------------------ Start the original track finder ------------------"); mClusters = clusters; mTracks.clear(); + // Use the chamber resolution when fitting the tracks during the tracking + mTrackFitter.useChamberResolution(); + // Look for candidates from clusters in stations(1..) 4 and 5 print("\n--> Step 1: find track candidates\n"); auto tStart = std::chrono::high_resolution_clock::now(); @@ -117,13 +123,18 @@ const std::list<Track>& TrackFinderOriginal::findTracks(const std::array<std::li tEnd = std::chrono::high_resolution_clock::now(); mTimeCleanTracks += tEnd - tStart; - // Set the final track parameters and covariances - finalize(); + // Refine the tracks using cluster resolution or just finalize them + if (mRefineTracks) { + tStart = std::chrono::high_resolution_clock::now(); + mTrackFitter.useClusterResolution(); + refineTracks(); + tEnd = std::chrono::high_resolution_clock::now(); + mTimeRefineTracks += tEnd - tStart; + } else { + finalize(); + } printTracks(); - /* - // Refit the reconstructed tracks with a different resolution for mono-cathod clusters - discardMonoCathodClusters(); -*/ + return mTracks; } @@ -304,7 +315,7 @@ std::list<Track>::iterator TrackFinderOriginal::findTrackCandidates(int ch1, int // check if non bending impact parameter is within tolerances double nonBendingSlope = (cluster1.getX() - cluster2.getX()) / dZ; double nonBendingImpactParam = TMath::Abs(cluster1.getX() - cluster1.getZ() * nonBendingSlope); - double nonBendingImpactParamErr = TMath::Sqrt((z1 * z1 * cluster2.getEx2() + z2 * z2 * cluster1.getEx2()) / dZ / dZ + impactMCS2); + double nonBendingImpactParamErr = TMath::Sqrt((z1 * z1 * chamberResolutionX2() + z2 * z2 * chamberResolutionX2()) / dZ / dZ + impactMCS2); if ((nonBendingImpactParam - SSigmaCutForTracking * nonBendingImpactParamErr) > (3. * SNonBendingVertexDispersion)) { continue; } @@ -313,7 +324,7 @@ std::list<Track>::iterator TrackFinderOriginal::findTrackCandidates(int ch1, int if (TrackExtrap::isFieldON()) { // depending whether the field is ON or OFF // check if bending momentum is within tolerances double bendingImpactParam = cluster1.getY() - cluster1.getZ() * bendingSlope; - double bendingImpactParamErr2 = (z1 * z1 * cluster2.getEy2() + z2 * z2 * cluster1.getEy2()) / dZ / dZ + impactMCS2; + double bendingImpactParamErr2 = (z1 * z1 * chamberResolutionY2() + z2 * z2 * chamberResolutionY2()) / dZ / dZ + impactMCS2; double bendingMomentum = TMath::Abs(TrackExtrap::getBendingMomentumFromImpactParam(bendingImpactParam)); double bendingMomentumErr = TMath::Sqrt((bendingVertexDispersion2 + bendingImpactParamErr2) / bendingImpactParam / bendingImpactParam + 0.01) * bendingMomentum; if ((bendingMomentum + 3. * bendingMomentumErr) < SMinBendingMomentum) { @@ -322,7 +333,7 @@ std::list<Track>::iterator TrackFinderOriginal::findTrackCandidates(int ch1, int } else { // or check if bending impact parameter is within tolerances double bendingImpactParam = TMath::Abs(cluster1.getY() - cluster1.getZ() * bendingSlope); - double bendingImpactParamErr = TMath::Sqrt((z1 * z1 * cluster2.getEy2() + z2 * z2 * cluster1.getEy2()) / dZ / dZ + impactMCS2); + double bendingImpactParamErr = TMath::Sqrt((z1 * z1 * chamberResolutionY2() + z2 * z2 * chamberResolutionY2()) / dZ / dZ + impactMCS2); if ((bendingImpactParam - SSigmaCutForTracking * bendingImpactParamErr) > (3. * SBendingVertexDispersion)) { continue; } @@ -402,15 +413,15 @@ void TrackFinderOriginal::createTrack(const Cluster& cl1, const Cluster& cl2) TMatrixD paramCov(5, 5); paramCov.Zero(); // Non bending plane - double cl1Ex2 = cl1.getEx2(); - double cl2Ex2 = cl2.getEx2(); + double cl1Ex2 = chamberResolutionX2(); + double cl2Ex2 = chamberResolutionX2(); paramCov(0, 0) = cl1Ex2; paramCov(0, 1) = cl1Ex2 / dZ; paramCov(1, 0) = paramCov(0, 1); paramCov(1, 1) = (cl1Ex2 + cl2Ex2) / dZ / dZ; // Bending plane - double cl1Ey2 = cl1.getEy2(); - double cl2Ey2 = cl2.getEy2(); + double cl1Ey2 = chamberResolutionY2(); + double cl2Ey2 = chamberResolutionY2(); paramCov(2, 2) = cl1Ey2; paramCov(2, 3) = cl1Ey2 / dZ; paramCov(3, 2) = paramCov(2, 3); @@ -913,8 +924,8 @@ bool TrackFinderOriginal::tryOneClusterFast(const TrackParam& param, const Clust double dX = cluster.getX() - (param.getNonBendingCoor() + param.getNonBendingSlope() * dZ); double dY = cluster.getY() - (param.getBendingCoor() + param.getBendingSlope() * dZ); const TMatrixD& paramCov = param.getCovariances(); - double errX2 = paramCov(0, 0) + dZ * dZ * paramCov(1, 1) + 2. * dZ * paramCov(0, 1) + cluster.getEx2(); - double errY2 = paramCov(2, 2) + dZ * dZ * paramCov(3, 3) + 2. * dZ * paramCov(2, 3) + cluster.getEy2(); + double errX2 = paramCov(0, 0) + dZ * dZ * paramCov(1, 1) + 2. * dZ * paramCov(0, 1) + chamberResolutionX2(); + double errY2 = paramCov(2, 2) + dZ * dZ * paramCov(3, 3) + 2. * dZ * paramCov(2, 3) + chamberResolutionY2(); double dXmax = SSigmaCutForTracking * TMath::Sqrt(2. * errX2) + SMaxNonBendingDistanceToTrack; double dYmax = SSigmaCutForTracking * TMath::Sqrt(2. * errY2) + SMaxBendingDistanceToTrack; @@ -949,8 +960,8 @@ double TrackFinderOriginal::tryOneCluster(const TrackParam& param, const Cluster // Combine the cluster and track resolutions and covariances const TMatrixD& paramCov = paramAtCluster.getCovariances(); - double sigmaX2 = paramCov(0, 0) + cluster.getEx2(); - double sigmaY2 = paramCov(2, 2) + cluster.getEy2(); + double sigmaX2 = paramCov(0, 0) + chamberResolutionX2(); + double sigmaY2 = paramCov(2, 2) + chamberResolutionY2(); double covXY = paramCov(0, 2); double det = sigmaX2 * sigmaY2 - covXY * covXY; @@ -994,7 +1005,7 @@ void TrackFinderOriginal::updateTrack(Track& track, TrackParam& trackParamAtClus const Cluster* cluster1(trackParamAtCluster1.getClusterPtr()); double deltaX = trackParamAtCluster1.getNonBendingCoor() - cluster1->getX(); double deltaY = trackParamAtCluster1.getBendingCoor() - cluster1->getY(); - double localChi2 = deltaX * deltaX / cluster1->getEx2() + deltaY * deltaY / cluster1->getEy2(); + double localChi2 = deltaX * deltaX / chamberResolutionX2() + deltaY * deltaY / chamberResolutionY2(); trackParamAtCluster1.setLocalChi2(localChi2); // Compute the local chi2 at cluster2 @@ -1003,7 +1014,7 @@ void TrackFinderOriginal::updateTrack(Track& track, TrackParam& trackParamAtClus TrackExtrap::extrapToZ(&extrapTrackParamAtCluster2, trackParamAtCluster2.getZ()); deltaX = extrapTrackParamAtCluster2.getNonBendingCoor() - cluster2->getX(); deltaY = extrapTrackParamAtCluster2.getBendingCoor() - cluster2->getY(); - localChi2 = deltaX * deltaX / cluster2->getEx2() + deltaY * deltaY / cluster2->getEy2(); + localChi2 = deltaX * deltaX / chamberResolutionX2() + deltaY * deltaY / chamberResolutionY2(); trackParamAtCluster2.setLocalChi2(localChi2); // Add the parameters at the new clusters @@ -1260,6 +1271,28 @@ void TrackFinderOriginal::improveTracks() } } +//_________________________________________________________________________________________________ +void TrackFinderOriginal::refineTracks() +{ + /// Refit, improve and finalize the reconstructed tracks + + print("Refine tracks"); + + for (auto itTrack = mTracks.begin(); itTrack != mTracks.end();) { + try { + mTrackFitter.fit(*itTrack, false); + ++itTrack; + } catch (exception const&) { + print("Removing candidate at position #", getTrackIndex(itTrack)); + itTrack = mTracks.erase(itTrack); + } + } + + improveTracks(); + + finalize(); +} + //_________________________________________________________________________________________________ void TrackFinderOriginal::finalize() { @@ -1375,6 +1408,7 @@ void TrackFinderOriginal::printTimers() const LOG(INFO) << "completeTracks duration = " << mTimeCompleteTracks.count() << " s"; LOG(INFO) << "improveTracks duration = " << mTimeImproveTracks.count() << " s"; LOG(INFO) << "removeConnectedTracks duration = " << mTimeCleanTracks.count() << " s"; + LOG(INFO) << "refineTracks duration = " << mTimeRefineTracks.count() << " s"; } } // namespace mch diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalSpec.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalSpec.cxx deleted file mode 100644 index 661c0aa2d3d51..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalSpec.cxx +++ /dev/null @@ -1,242 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackFinderOriginalSpec.cxx -/// \brief Implementation of a data processor to read clusters, reconstruct tracks and send them -/// -/// \author Philippe Pillot, Subatech - -#include "Framework/ConfigParamRegistry.h" -#include "TrackFinderOriginalSpec.h" - -#include <chrono> -#include <list> -#include <stdexcept> - -#include "Framework/CallbackService.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -#include "MCHBase/ClusterBlock.h" -#include "MCHBase/TrackBlock.h" -#include "MCHTracking/TrackParam.h" -#include "MCHTracking/Cluster.h" -#include "MCHTracking/Track.h" -#include "TrackFinderOriginal.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class TrackFinderTask -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Prepare the track extrapolation tools - - LOG(INFO) << "initializing track finder"; - - auto l3Current = ic.options().get<float>("l3Current"); - auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); - mTrackFinder.init(l3Current, dipoleCurrent); - - auto moreCandidates = ic.options().get<bool>("moreCandidates"); - mTrackFinder.findMoreTrackCandidates(moreCandidates); - - auto debugLevel = ic.options().get<int>("debug"); - mTrackFinder.debug(debugLevel); - - auto stop = [this]() { - mTrackFinder.printStats(); - mTrackFinder.printTimers(); - LOG(INFO) << "tracking duration = " << mElapsedTime.count() << " s"; - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// read the clusters of the current event, find tracks and send them - - // get the input buffer - auto msgIn = pc.inputs().get<gsl::span<char>>("clusters"); - auto bufferPtr = msgIn.data(); - int sizeLeft = msgIn.size(); - - // get the event number - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing event header"); - } - const int& event = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - // get the input clusters - std::array<std::list<Cluster>, 10> clusters{}; - readClusters(bufferPtr, sizeLeft, clusters); - - // run the track finder - auto tStart = std::chrono::high_resolution_clock::now(); - const auto& tracks = mTrackFinder.findTracks(&clusters); - auto tEnd = std::chrono::high_resolution_clock::now(); - mElapsedTime += tEnd - tStart; - - // calculate the size of the payload for the output message, excluding the event header - int trackSize = getSize(tracks); - - // create the output message - auto msgOut = pc.outputs().make<char>(Output{"MCH", "TRACKS", 0, Lifetime::Timeframe}, SHeaderSize + trackSize); - auto bufferPtrOut = msgOut.data(); - - // write the event header - writeHeader(event, trackSize, bufferPtrOut); - - // write the tracks - if (trackSize > 0) { - writeTracks(tracks, bufferPtrOut); - } - } - - private: - //_________________________________________________________________________________________________ - void readClusters(const char*& bufferPtr, int& sizeLeft, std::array<std::list<Cluster>, 10>& clusters) - { - /// read the cluster informations from the buffer - /// move the buffer ptr and decrease the size left - /// throw an exception in case of error - - // read the number of clusters - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing number of clusters"); - } - const int& nClusters = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - for (int iCl = 0; iCl < nClusters; ++iCl) { - - // read cluster info - if (sizeLeft < SSizeOfClusterStruct) { - throw out_of_range("missing cluster"); - } - const auto cluster = reinterpret_cast<const ClusterStruct*>(bufferPtr); - clusters[cluster->getChamberId()].emplace_back(*cluster); - bufferPtr += SSizeOfClusterStruct; - sizeLeft -= SSizeOfClusterStruct; - } - - if (sizeLeft != 0) { - throw length_error("incorrect payload"); - } - } - - //_________________________________________________________________________________________________ - int getSize(const std::list<Track>& tracks) - { - /// calculate the total number of bytes requested to store the tracks - - int size(0); - for (const auto& track : tracks) { - size += SSizeOfTrackParamStruct + SSizeOfDouble + SSizeOfInt + track.getNClusters() * SSizeOfClusterStruct; - } - if (size > 0) { - size += SSizeOfInt; - } - - return size; - } - - //_________________________________________________________________________________________________ - void writeHeader(int event, int trackSize, char*& bufferPtr) const - { - /// write header informations in the output buffer and move the buffer ptr - - // write the event number - memcpy(bufferPtr, &event, SSizeOfInt); - bufferPtr += SSizeOfInt; - - // write the size of the payload - memcpy(bufferPtr, &trackSize, SSizeOfInt); - bufferPtr += SSizeOfInt; - } - - //_________________________________________________________________________________________________ - void writeTracks(const std::list<Track>& tracks, char*& bufferPtr) const - { - /// write the track informations in the buffer and move the buffer ptr - - // write the number of tracks - int nTracks = tracks.size(); - memcpy(bufferPtr, &nTracks, SSizeOfInt); - bufferPtr += SSizeOfInt; - - for (const auto& track : tracks) { - - // write track parameters - TrackParamStruct paramStruct = track.first().getTrackParamStruct(); - memcpy(bufferPtr, ¶mStruct, SSizeOfTrackParamStruct); - bufferPtr += SSizeOfTrackParamStruct; - - // write track chi2 - double chi2 = track.first().getTrackChi2(); - memcpy(bufferPtr, &chi2, SSizeOfDouble); - bufferPtr += SSizeOfDouble; - - // write the number of clusters - int nClusters = track.getNClusters(); - memcpy(bufferPtr, &nClusters, SSizeOfInt); - bufferPtr += SSizeOfInt; - - for (const auto& param : track) { - - // write cluster info - ClusterStruct clusterStruct = param.getClusterPtr()->getClusterStruct(); - memcpy(bufferPtr, &clusterStruct, SSizeOfClusterStruct); - bufferPtr += SSizeOfClusterStruct; - } - } - } - - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SSizeOfDouble = sizeof(double); - static constexpr int SHeaderSize = 2 * SSizeOfInt; - static constexpr int SSizeOfClusterStruct = sizeof(ClusterStruct); - static constexpr int SSizeOfTrackParamStruct = sizeof(TrackParamStruct); - - TrackFinderOriginal mTrackFinder{}; ///< track finder - std::chrono::duration<double> mElapsedTime{}; ///< timer -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackFinderOriginalSpec() -{ - return DataProcessorSpec{ - "TrackFinderOriginal", - Inputs{InputSpec{"clusters", "MCH", "CLUSTERS", 0, Lifetime::Timeframe}}, - Outputs{OutputSpec{"MCH", "TRACKS", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<TrackFinderTask>()}, - Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, - {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}, - {"moreCandidates", VariantType::Bool, false, {"Find more track candidates"}}, - {"debug", VariantType::Int, 0, {"debug level"}}}}; -} - -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalWorkflow.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalWorkflow.cxx deleted file mode 100644 index 130952e254a30..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalWorkflow.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" - -#include "ClusterSamplerSpec.h" -#include "TrackFinderOriginalSpec.h" -#include "TrackSinkSpec.h" - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - o2::mch::getClusterSamplerSpec(), - o2::mch::getTrackFinderOriginalSpec(), - o2::mch::getTrackSinkSpec("TRACKS")}; -} diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderSpec.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFinderSpec.cxx deleted file mode 100644 index 00492d6f016c9..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinderSpec.cxx +++ /dev/null @@ -1,243 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackFinderSpec.cxx -/// \brief Implementation of a data processor to read clusters, reconstruct tracks and send them -/// -/// \author Philippe Pillot, Subatech - -#include "TrackFinderSpec.h" - -#include <chrono> -#include <unordered_map> -#include <list> -#include <stdexcept> - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -#include "MCHBase/ClusterBlock.h" -#include "MCHBase/TrackBlock.h" -#include "MCHTracking/TrackParam.h" -#include "MCHTracking/Cluster.h" -#include "MCHTracking/Track.h" -#include "TrackFinder.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class TrackFinderTask -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Prepare the track extrapolation tools - - LOG(INFO) << "initializing track finder"; - - auto l3Current = ic.options().get<float>("l3Current"); - auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); - mTrackFinder.init(l3Current, dipoleCurrent); - - auto moreCandidates = ic.options().get<bool>("moreCandidates"); - mTrackFinder.findMoreTrackCandidates(moreCandidates); - - auto debugLevel = ic.options().get<int>("debug"); - mTrackFinder.debug(debugLevel); - - auto stop = [this]() { - mTrackFinder.printStats(); - mTrackFinder.printTimers(); - LOG(INFO) << "tracking duration = " << mElapsedTime.count() << " s"; - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// read the clusters of the current event, find tracks and send them - - // get the input buffer - auto msgIn = pc.inputs().get<gsl::span<char>>("clusters"); - auto bufferPtr = msgIn.data(); - int sizeLeft = msgIn.size(); - - // get the event number - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing event header"); - } - const int& event = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - // get the input clusters - std::unordered_map<int, std::list<Cluster>> clusters{}; - readClusters(bufferPtr, sizeLeft, clusters); - - // run the track finder - auto tStart = std::chrono::high_resolution_clock::now(); - const auto& tracks = mTrackFinder.findTracks(clusters); - auto tEnd = std::chrono::high_resolution_clock::now(); - mElapsedTime += tEnd - tStart; - - // calculate the size of the payload for the output message, excluding the event header - int trackSize = getSize(tracks); - - // create the output message - auto msgOut = pc.outputs().make<char>(Output{"MCH", "TRACKS", 0, Lifetime::Timeframe}, SHeaderSize + trackSize); - auto bufferPtrOut = msgOut.data(); - - // write the event header - writeHeader(event, trackSize, bufferPtrOut); - - // write the tracks - if (trackSize > 0) { - writeTracks(tracks, bufferPtrOut); - } - } - - private: - //_________________________________________________________________________________________________ - void readClusters(const char*& bufferPtr, int& sizeLeft, std::unordered_map<int, std::list<Cluster>>& clusters) - { - /// read the cluster informations from the buffer - /// move the buffer ptr and decrease the size left - /// throw an exception in case of error - - // read the number of clusters - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing number of clusters"); - } - const int& nClusters = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - for (int iCl = 0; iCl < nClusters; ++iCl) { - - // read cluster info - if (sizeLeft < SSizeOfClusterStruct) { - throw out_of_range("missing cluster"); - } - const auto cluster = reinterpret_cast<const ClusterStruct*>(bufferPtr); - clusters[cluster->getDEId()].emplace_back(*cluster); - bufferPtr += SSizeOfClusterStruct; - sizeLeft -= SSizeOfClusterStruct; - } - - if (sizeLeft != 0) { - throw length_error("incorrect payload"); - } - } - - //_________________________________________________________________________________________________ - int getSize(const std::list<Track>& tracks) - { - /// calculate the total number of bytes requested to store the tracks - - int size(0); - for (const auto& track : tracks) { - size += SSizeOfTrackParamStruct + SSizeOfDouble + SSizeOfInt + track.getNClusters() * SSizeOfClusterStruct; - } - if (size > 0) { - size += SSizeOfInt; - } - - return size; - } - - //_________________________________________________________________________________________________ - void writeHeader(int event, int trackSize, char*& bufferPtr) const - { - /// write header informations in the output buffer and move the buffer ptr - - // write the event number - memcpy(bufferPtr, &event, SSizeOfInt); - bufferPtr += SSizeOfInt; - - // write the size of the payload - memcpy(bufferPtr, &trackSize, SSizeOfInt); - bufferPtr += SSizeOfInt; - } - - //_________________________________________________________________________________________________ - void writeTracks(const std::list<Track>& tracks, char*& bufferPtr) const - { - /// write the track informations in the buffer and move the buffer ptr - - // write the number of tracks - int nTracks = tracks.size(); - memcpy(bufferPtr, &nTracks, SSizeOfInt); - bufferPtr += SSizeOfInt; - - for (const auto& track : tracks) { - - // write track parameters - TrackParamStruct paramStruct = track.first().getTrackParamStruct(); - memcpy(bufferPtr, ¶mStruct, SSizeOfTrackParamStruct); - bufferPtr += SSizeOfTrackParamStruct; - - // write track chi2 - double chi2 = track.first().getTrackChi2(); - memcpy(bufferPtr, &chi2, SSizeOfDouble); - bufferPtr += SSizeOfDouble; - - // write the number of clusters - int nClusters = track.getNClusters(); - memcpy(bufferPtr, &nClusters, SSizeOfInt); - bufferPtr += SSizeOfInt; - - for (const auto& param : track) { - - // write cluster info - ClusterStruct clusterStruct = param.getClusterPtr()->getClusterStruct(); - memcpy(bufferPtr, &clusterStruct, SSizeOfClusterStruct); - bufferPtr += SSizeOfClusterStruct; - } - } - } - - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SSizeOfDouble = sizeof(double); - static constexpr int SHeaderSize = 2 * SSizeOfInt; - static constexpr int SSizeOfClusterStruct = sizeof(ClusterStruct); - static constexpr int SSizeOfTrackParamStruct = sizeof(TrackParamStruct); - - TrackFinder mTrackFinder{}; ///< track finder - std::chrono::duration<double> mElapsedTime{}; ///< timer -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackFinderSpec() -{ - return DataProcessorSpec{ - "TrackFinder", - Inputs{InputSpec{"clusters", "MCH", "CLUSTERS", 0, Lifetime::Timeframe}}, - Outputs{OutputSpec{"MCH", "TRACKS", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<TrackFinderTask>()}, - Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, - {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}, - {"moreCandidates", VariantType::Bool, false, {"Find more track candidates"}}, - {"debug", VariantType::Int, 0, {"debug level"}}}}; -} - -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderWorkflow.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFinderWorkflow.cxx deleted file mode 100644 index 5ad3f0d82f04c..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFinderWorkflow.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" - -#include "ClusterSamplerSpec.h" -#include "TrackFinderSpec.h" -#include "VertexSamplerSpec.h" -#include "TrackAtVertexSpec.h" -#include "TrackSinkSpec.h" - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - o2::mch::getClusterSamplerSpec(), - o2::mch::getTrackFinderSpec(), - o2::mch::getVertexSamplerSpec(), - o2::mch::getTrackAtVertexSpec(), - o2::mch::getTrackSinkSpec("TRACKSATVERTEX")}; -} diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFitter.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFitter.cxx index 0052bee51abfa..312c5ab4c7c91 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackFitter.cxx +++ b/Detectors/MUON/MCH/Tracking/src/TrackFitter.cxx @@ -13,7 +13,7 @@ /// /// \author Philippe Pillot, Subatech -#include "TrackFitter.h" +#include "MCHTracking/TrackFitter.h" #include <stdexcept> @@ -21,7 +21,7 @@ #include <TMatrixD.h> #include "Field/MagneticField.h" -#include "TrackExtrap.h" +#include "MCHTracking/TrackExtrap.h" namespace o2 { @@ -30,7 +30,7 @@ namespace mch using namespace std; -constexpr float TrackFitter::SDefaultChamberZ[10]; +constexpr double TrackFitter::SDefaultChamberZ[10]; constexpr double TrackFitter::SChamberThicknessInX0[10]; //_________________________________________________________________________________________________ @@ -117,14 +117,26 @@ void TrackFitter::initTrack(const Cluster& cl1, const Cluster& cl2, TrackParam& // compute the track parameter covariances at the last cluster (as if the other clusters did not exist) TMatrixD lastParamCov(5, 5); lastParamCov.Zero(); + double cl1Ey2(0.); + if (mUseChamberResolution) { + // Non bending plane + lastParamCov(0, 0) = mChamberResolutionX2; + lastParamCov(1, 1) = (1000. * mChamberResolutionX2 + lastParamCov(0, 0)) / dZ / dZ; + // Bending plane + lastParamCov(2, 2) = mChamberResolutionY2; + cl1Ey2 = mChamberResolutionY2; + } else { + // Non bending plane + lastParamCov(0, 0) = cl2.getEx2(); + lastParamCov(1, 1) = (1000. * cl1.getEx2() + lastParamCov(0, 0)) / dZ / dZ; + // Bending plane + lastParamCov(2, 2) = cl2.getEy2(); + cl1Ey2 = cl1.getEy2(); + } // Non bending plane - lastParamCov(0, 0) = cl2.getEx2(); lastParamCov(0, 1) = -lastParamCov(0, 0) / dZ; lastParamCov(1, 0) = lastParamCov(0, 1); - lastParamCov(1, 1) = (1000. * cl1.getEx2() + lastParamCov(0, 0)) / dZ / dZ; // Bending plane - double cl1Ey2 = cl1.getEy2(); - lastParamCov(2, 2) = cl2.getEy2(); lastParamCov(2, 3) = -lastParamCov(2, 2) / dZ; lastParamCov(3, 2) = lastParamCov(2, 3); lastParamCov(3, 3) = (1000. * cl1Ey2 + lastParamCov(2, 2)) / dZ / dZ; @@ -267,8 +279,13 @@ void TrackFitter::runKalmanFilter(TrackParam& trackParam) // compute the new cluster weight (U) TMatrixD clusterWeight(5, 5); clusterWeight.Zero(); - clusterWeight(0, 0) = 1. / cluster->getEx2(); - clusterWeight(2, 2) = 1. / cluster->getEy2(); + if (mUseChamberResolution) { + clusterWeight(0, 0) = 1. / mChamberResolutionX2; + clusterWeight(2, 2) = 1. / mChamberResolutionY2; + } else { + clusterWeight(0, 0) = 1. / cluster->getEx2(); + clusterWeight(2, 2) = 1. / cluster->getEy2(); + } // compute the new parameters covariance matrix ((W+U)^-1) TMatrixD newParamCov(paramWeight, TMatrixD::kPlus, clusterWeight); @@ -345,10 +362,15 @@ void TrackFitter::runSmoother(const TrackParam& previousParam, TrackParam& param // compute weight of smoothed residual: W(k n) = (clusterCov - C(k n))^-1 TMatrixD smoothResidualWeight(2, 2); - smoothResidualWeight(0, 0) = cluster->getEx2() - smoothCovariances(0, 0); + if (mUseChamberResolution) { + smoothResidualWeight(0, 0) = mChamberResolutionX2 - smoothCovariances(0, 0); + smoothResidualWeight(1, 1) = mChamberResolutionY2 - smoothCovariances(2, 2); + } else { + smoothResidualWeight(0, 0) = cluster->getEx2() - smoothCovariances(0, 0); + smoothResidualWeight(1, 1) = cluster->getEy2() - smoothCovariances(2, 2); + } smoothResidualWeight(0, 1) = -smoothCovariances(0, 2); smoothResidualWeight(1, 0) = -smoothCovariances(2, 0); - smoothResidualWeight(1, 1) = cluster->getEy2() - smoothCovariances(2, 2); if (smoothResidualWeight.Determinant() != 0) { smoothResidualWeight.Invert(); } else { diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFitter.h b/Detectors/MUON/MCH/Tracking/src/TrackFitter.h deleted file mode 100644 index 1f95cd4d25385..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFitter.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackFitter.h -/// \brief Definition of a class to fit a track to a set of clusters -/// -/// \author Philippe Pillot, Subatech - -#ifndef ALICEO2_MCH_TRACKFITTER_H_ -#define ALICEO2_MCH_TRACKFITTER_H_ - -#include "MCHTracking/Cluster.h" -#include "MCHTracking/Track.h" -#include "MCHTracking/TrackParam.h" - -namespace o2 -{ -namespace mch -{ - -/// Class to fit a track to a set of clusters -class TrackFitter -{ - public: - TrackFitter() = default; - ~TrackFitter() = default; - - TrackFitter(const TrackFitter&) = delete; - TrackFitter& operator=(const TrackFitter&) = delete; - TrackFitter(TrackFitter&&) = delete; - TrackFitter& operator=(TrackFitter&&) = delete; - - void initField(float l3Current, float dipoleCurrent); - - /// Enable/disable the smoother (and the saving of related parameters) - void smoothTracks(bool smooth) { mSmooth = smooth; } - /// Return the smoother enable/disable flag - bool isSmootherEnabled() { return mSmooth; } - - void fit(Track& track, bool smooth = true, bool finalize = true, - std::list<TrackParam>::reverse_iterator* itStartingParam = nullptr); - - void runKalmanFilter(TrackParam& trackParam); - - /// Return the maximum chi2 above which the track can be considered as abnormal - static constexpr double getMaxChi2() { return SMaxChi2; } - - private: - void initTrack(const Cluster& cl1, const Cluster& cl2, TrackParam& param); - void addCluster(const TrackParam& startingParam, const Cluster& cl, TrackParam& param); - void smoothTrack(Track& track, bool finalize); - void runSmoother(const TrackParam& previousParam, TrackParam& param); - - static constexpr double SMaxChi2 = 2.e10; ///< maximum chi2 above which the track can be considered as abnormal - static constexpr double SBendingVertexDispersion = 70.; ///< vertex dispersion (cm) in bending plane - /// z position of the chambers - static constexpr float SDefaultChamberZ[10] = {-526.16, -545.24, -676.4, -695.4, -967.5, - -998.5, -1276.5, -1307.5, -1406.6, -1437.6}; - /// default chamber thickness in X0 for reconstruction - static constexpr double SChamberThicknessInX0[10] = {0.065, 0.065, 0.075, 0.075, 0.035, - 0.035, 0.035, 0.035, 0.035, 0.035}; - - bool mSmooth = false; ///< switch ON/OFF the smoother -}; - -} // namespace mch -} // namespace o2 - -#endif // ALICEO2_MCH_TRACKFITTER_H_ diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFitterSpec.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFitterSpec.cxx deleted file mode 100644 index 52d2035aa75a7..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFitterSpec.cxx +++ /dev/null @@ -1,219 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackFitterSpec.cxx -/// \brief Implementation of a data processor to read, refit and send tracks with attached clusters -/// -/// \author Philippe Pillot, Subatech - -#include "TrackFitterSpec.h" - -#include <stdexcept> -#include <list> - -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -#include "MCHBase/ClusterBlock.h" -#include "MCHBase/TrackBlock.h" -#include "MCHTracking/TrackParam.h" -#include "MCHTracking/Cluster.h" -#include "MCHTracking/Track.h" -#include "TrackFitter.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class TrackFitterTask -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Prepare the track extrapolation tools - LOG(INFO) << "initializing track fitter"; - auto l3Current = ic.options().get<float>("l3Current"); - auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); - mTrackFitter.initField(l3Current, dipoleCurrent); - mTrackFitter.smoothTracks(true); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// read the tracks with attached clusters of the current event, - /// refit them and send the new version - - // get the input buffer - auto msgIn = pc.inputs().get<gsl::span<char>>("tracks"); - auto bufferPtr = msgIn.data(); - int sizeLeft = msgIn.size(); - - // create the output message - auto msgOut = pc.outputs().make<char>(Output{"MCH", "REFITTRACKS", 0, Lifetime::Timeframe}, sizeLeft); - auto bufferPtrOut = msgOut.data(); - - // copy header info - copyHeader(bufferPtr, sizeLeft, bufferPtrOut); - - // get the number of tracks and copy it to the output message - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing number of tracks"); - } - const int& nTracks = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - memcpy(bufferPtrOut, &nTracks, SSizeOfInt); - bufferPtrOut += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - std::list<Cluster> clusters{}; - for (int iTrack = 0; iTrack < nTracks; ++iTrack) { - - // get the input track - Track track{}; - readTrack(bufferPtr, sizeLeft, track, clusters); - - // refit the track - try { - mTrackFitter.fit(track); - } catch (exception const& e) { - throw runtime_error(std::string("Track fit failed: ") + e.what()); - } - - // write the refitted track to the ouput message - writeTrack(track, bufferPtrOut); - } - - if (sizeLeft != 0) { - throw length_error("incorrect payload"); - } - } - - private: - //_________________________________________________________________________________________________ - void copyHeader(const char*& bufferPtr, int& sizeLeft, char*& bufferPtrOut) const - { - /// copy header informations from the input buffer to the output message - /// move the buffer ptr and decrease the size left - /// throw an exception in case of error - - if (sizeLeft < SHeaderSize) { - throw out_of_range("missing event header"); - } - - const int& event = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - memcpy(bufferPtrOut, &event, SSizeOfInt); - bufferPtrOut += SSizeOfInt; - - const int& size = *reinterpret_cast<const int*>(bufferPtr); - bufferPtr += SSizeOfInt; - memcpy(bufferPtrOut, &size, SSizeOfInt); - bufferPtrOut += SSizeOfInt; - - sizeLeft -= SHeaderSize; - if (sizeLeft != size) { - throw length_error("incorrect payload size"); - } - } - - //_________________________________________________________________________________________________ - void readTrack(const char*& bufferPtr, int& sizeLeft, Track& track, std::list<Cluster>& clusters) const - { - /// read the track informations from the buffer - /// move the buffer ptr and decrease the size left - /// throw an exception in case of error - - // skip the track parameters - if (sizeLeft < SSizeOfTrackParamStruct) { - throw out_of_range("missing track parameters"); - } - bufferPtr += SSizeOfTrackParamStruct; - sizeLeft -= SSizeOfTrackParamStruct; - - // read number of clusters - if (sizeLeft < SSizeOfInt) { - throw out_of_range("missing number of clusters"); - } - const int& nClusters = *reinterpret_cast<const int*>(bufferPtr); - if (nClusters > 20) { - throw length_error("too many (>20) clusters attached to the track"); - } - bufferPtr += SSizeOfInt; - sizeLeft -= SSizeOfInt; - - for (int iCl = 0; iCl < nClusters; ++iCl) { - - // read cluster info - if (sizeLeft < SSizeOfClusterStruct) { - throw out_of_range("missing cluster"); - } - clusters.emplace_back(*reinterpret_cast<const ClusterStruct*>(bufferPtr)); - track.createParamAtCluster(clusters.back()); - bufferPtr += SSizeOfClusterStruct; - sizeLeft -= SSizeOfClusterStruct; - } - } - - //_________________________________________________________________________________________________ - void writeTrack(Track& track, char*& bufferPtrOut) const - { - /// write the track informations to the buffer and move the buffer ptr - - // fill track parameters - TrackParamStruct paramStruct = track.first().getTrackParamStruct(); - memcpy(bufferPtrOut, ¶mStruct, SSizeOfTrackParamStruct); - bufferPtrOut += SSizeOfTrackParamStruct; - - // fill number of clusters - int nClusters = track.getNClusters(); - memcpy(bufferPtrOut, &nClusters, SSizeOfInt); - bufferPtrOut += SSizeOfInt; - - for (const auto& param : track) { - - // fill cluster info - ClusterStruct clusterStruct = param.getClusterPtr()->getClusterStruct(); - memcpy(bufferPtrOut, &clusterStruct, SSizeOfClusterStruct); - bufferPtrOut += SSizeOfClusterStruct; - } - } - - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SHeaderSize = 2 * SSizeOfInt; - static constexpr int SSizeOfTrackParamStruct = sizeof(TrackParamStruct); - static constexpr int SSizeOfClusterStruct = sizeof(ClusterStruct); - - TrackFitter mTrackFitter{}; ///< track fitter -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackFitterSpec() -{ - return DataProcessorSpec{ - "TrackFitter", - Inputs{InputSpec{"tracks", "MCH", "TRACKS", 0, Lifetime::Timeframe}}, - Outputs{OutputSpec{"MCH", "REFITTRACKS", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<TrackFitterTask>()}, - Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, - {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}}}; -} - -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFitterWorkflow.cxx b/Detectors/MUON/MCH/Tracking/src/TrackFitterWorkflow.cxx deleted file mode 100644 index 397a17500a01a..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackFitterWorkflow.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/runDataProcessing.h" - -#include "TrackSamplerSpec.h" -#include "TrackFitterSpec.h" -#include "TrackSinkSpec.h" - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - o2::mch::getTrackSamplerSpec(), - o2::mch::getTrackFitterSpec(), - o2::mch::getTrackSinkSpec("REFITTRACKS")}; -} diff --git a/Detectors/MUON/MCH/Tracking/src/TrackParam.cxx b/Detectors/MUON/MCH/Tracking/src/TrackParam.cxx index f7c88c0774ad8..b445f660f7178 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackParam.cxx +++ b/Detectors/MUON/MCH/Tracking/src/TrackParam.cxx @@ -1,4 +1,4 @@ -#// Copyright CERN and copyright holders of ALICE O2. This software is +// 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". // @@ -31,6 +31,21 @@ namespace mch using namespace std; +//_________________________________________________________________________ +TrackParam::TrackParam(Double_t z, const Double_t param[5]) : mZ(z) +{ + /// constructor with given parameters + setParameters(param); +} + +//_________________________________________________________________________ +TrackParam::TrackParam(Double_t z, const Double_t param[5], const Double_t cov[15]) : mZ(z) +{ + /// constructor with given parameters and covariances + setParameters(param); + setCovariances(cov); +} + //_________________________________________________________________________ TrackParam::TrackParam(const TrackParam& tp) : mZ(tp.mZ), @@ -41,78 +56,97 @@ TrackParam::TrackParam(const TrackParam& tp) mLocalChi2(tp.mLocalChi2) { /// Copy constructor - if (tp.mCovariances) + if (tp.mCovariances) { mCovariances = std::make_unique<TMatrixD>(*(tp.mCovariances)); - if (tp.mPropagator) + } + if (tp.mPropagator) { mPropagator = std::make_unique<TMatrixD>(*(tp.mPropagator)); - if (tp.mExtrapParameters) + } + if (tp.mExtrapParameters) { mExtrapParameters = std::make_unique<TMatrixD>(*(tp.mExtrapParameters)); - if (tp.mExtrapCovariances) + } + if (tp.mExtrapCovariances) { mExtrapCovariances = std::make_unique<TMatrixD>(*(tp.mExtrapCovariances)); - if (tp.mSmoothParameters) + } + if (tp.mSmoothParameters) { mSmoothParameters = std::make_unique<TMatrixD>(*(tp.mSmoothParameters)); - if (tp.mSmoothCovariances) + } + if (tp.mSmoothCovariances) { mSmoothCovariances = std::make_unique<TMatrixD>(*(tp.mSmoothCovariances)); + } } //_________________________________________________________________________ TrackParam& TrackParam::operator=(const TrackParam& tp) { /// Assignment operator - if (this == &tp) + if (this == &tp) { return *this; + } mZ = tp.mZ; mParameters = tp.mParameters; if (tp.mCovariances) { - if (mCovariances) + if (mCovariances) { *mCovariances = *(tp.mCovariances); - else + } else { mCovariances = std::make_unique<TMatrixD>(*(tp.mCovariances)); - } else + } + } else { mCovariances.reset(); + } if (tp.mPropagator) { - if (mPropagator) + if (mPropagator) { *mPropagator = *(tp.mPropagator); - else + } else { mPropagator = std::make_unique<TMatrixD>(*(tp.mPropagator)); - } else + } + } else { mPropagator.reset(); + } if (tp.mExtrapParameters) { - if (mExtrapParameters) + if (mExtrapParameters) { *mExtrapParameters = *(tp.mExtrapParameters); - else + } else { mExtrapParameters = std::make_unique<TMatrixD>(*(tp.mExtrapParameters)); - } else + } + } else { mExtrapParameters.reset(); + } if (tp.mExtrapCovariances) { - if (mExtrapCovariances) + if (mExtrapCovariances) { *mExtrapCovariances = *(tp.mExtrapCovariances); - else + } else { mExtrapCovariances = std::make_unique<TMatrixD>(*(tp.mExtrapCovariances)); - } else + } + } else { mExtrapCovariances.reset(); + } if (tp.mSmoothParameters) { - if (mSmoothParameters) + if (mSmoothParameters) { *mSmoothParameters = *(tp.mSmoothParameters); - else + } else { mSmoothParameters = std::make_unique<TMatrixD>(*(tp.mSmoothParameters)); - } else + } + } else { mSmoothParameters.reset(); + } if (tp.mSmoothCovariances) { - if (mSmoothCovariances) + if (mSmoothCovariances) { *mSmoothCovariances = *(tp.mSmoothCovariances); - else + } else { mSmoothCovariances = std::make_unique<TMatrixD>(*(tp.mSmoothCovariances)); - } else + } + } else { mSmoothCovariances.reset(); + } mClusterPtr = tp.mClusterPtr; @@ -142,7 +176,7 @@ Double_t TrackParam::px() const /// return p_x from track parameters Double_t pZ; if (TMath::Abs(mParameters(4, 0)) > 0) { - Double_t pYZ = (TMath::Abs(mParameters(4, 0)) > 0) ? TMath::Abs(1.0 / mParameters(4, 0)) : FLT_MAX; + Double_t pYZ = TMath::Abs(1.0 / mParameters(4, 0)); pZ = -pYZ / (TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0))); // spectro. (z<0) } else { pZ = -FLT_MAX / TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0) + mParameters(1, 0) * mParameters(1, 0)); @@ -156,7 +190,7 @@ Double_t TrackParam::py() const /// return p_y from track parameters Double_t pZ; if (TMath::Abs(mParameters(4, 0)) > 0) { - Double_t pYZ = (TMath::Abs(mParameters(4, 0)) > 0) ? TMath::Abs(1.0 / mParameters(4, 0)) : FLT_MAX; + Double_t pYZ = TMath::Abs(1.0 / mParameters(4, 0)); pZ = -pYZ / (TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0))); // spectro. (z<0) } else { pZ = -FLT_MAX / TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0) + mParameters(1, 0) * mParameters(1, 0)); @@ -171,8 +205,9 @@ Double_t TrackParam::pz() const if (TMath::Abs(mParameters(4, 0)) > 0) { Double_t pYZ = TMath::Abs(1.0 / mParameters(4, 0)); return -pYZ / (TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0))); // spectro. (z<0) - } else + } else { return -FLT_MAX / TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0) + mParameters(1, 0) * mParameters(1, 0)); + } } //__________________________________________________________________________ @@ -183,8 +218,9 @@ Double_t TrackParam::p() const Double_t pYZ = TMath::Abs(1.0 / mParameters(4, 0)); Double_t pZ = -pYZ / (TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0))); // spectro. (z<0) return -pZ * TMath::Sqrt(1.0 + mParameters(3, 0) * mParameters(3, 0) + mParameters(1, 0) * mParameters(1, 0)); - } else + } else { return FLT_MAX; + } } //__________________________________________________________________________ @@ -202,31 +238,49 @@ const TMatrixD& TrackParam::getCovariances() const void TrackParam::setCovariances(const TMatrixD& covariances) { /// Set the covariance matrix - if (mCovariances) + if (mCovariances) { *mCovariances = covariances; - else + } else { mCovariances = std::make_unique<TMatrixD>(covariances); + } } //__________________________________________________________________________ -void TrackParam::setCovariances(const Double_t matrix[5][5]) +void TrackParam::setCovariances(const Double_t covariances[15]) { - /// Set the covariance matrix - if (mCovariances) - mCovariances->SetMatrixArray(&(matrix[0][0])); - else - mCovariances = std::make_unique<TMatrixD>(5, 5, &(matrix[0][0])); + /// Set the covariance matrix from the reduced matrix formated as follow: <pre> + /// [0] = <X,X> + /// [1] = <SlopeX,X> [2] = <SlopeX,SlopeX> + /// [3] = <Y,X> [4] = <Y,SlopeX> [5] = <Y,Y> + /// [6] = <SlopeY,X> [7] = <SlopeY,SlopeX> [8] = <SlopeY,Y> [9] = <SlopeY,SlopeY> + /// [10]= <q/pYZ,X> [11]= <q/pYZ,SlopeX> [12]= <q/pYZ,Y> [13]= <q/pYZ,SlopeY> [14]= <q/pYZ,q/pYZ> </pre> + if (!mCovariances) { + mCovariances = std::make_unique<TMatrixD>(5, 5); + } + for (Int_t i = 0; i < 5; i++) { + for (Int_t j = 0; j <= i; j++) { + (*mCovariances)(i, j) = (*mCovariances)(j, i) = covariances[i * (i + 1) / 2 + j]; + } + } } //__________________________________________________________________________ -void TrackParam::setVariances(const Double_t matrix[5][5]) +void TrackParam::setVariances(const Double_t covariances[15]) { - /// Set the diagonal terms of the covariance matrix (variances) - if (!mCovariances) + /// Set the diagonal terms of the covariance matrix (variances) from the reduced matrix formated as follow: <pre> + /// [0] = <X,X> + /// [1] = <SlopeX,X> [2] = <SlopeX,SlopeX> + /// [3] = <Y,X> [4] = <Y,SlopeX> [5] = <Y,Y> + /// [6] = <SlopeY,X> [7] = <SlopeY,SlopeX> [8] = <SlopeY,Y> [9] = <SlopeY,SlopeY> + /// [10]= <q/pYZ,X> [11]= <q/pYZ,SlopeX> [12]= <q/pYZ,Y> [13]= <q/pYZ,SlopeY> [14]= <q/pYZ,q/pYZ> </pre> + static constexpr int varIdx[5] = {0, 2, 5, 9, 14}; + if (!mCovariances) { mCovariances = std::make_unique<TMatrixD>(5, 5); + } mCovariances->Zero(); - for (Int_t i = 0; i < 5; i++) - (*mCovariances)(i, i) = matrix[i][i]; + for (Int_t i = 0; i < 5; i++) { + (*mCovariances)(i, i) = covariances[varIdx[i]]; + } } //__________________________________________________________________________ @@ -251,18 +305,20 @@ const TMatrixD& TrackParam::getPropagator() const void TrackParam::resetPropagator() { /// Reset the propagator - if (mPropagator) + if (mPropagator) { mPropagator->UnitMatrix(); + } } //__________________________________________________________________________ void TrackParam::updatePropagator(const TMatrixD& propagator) { /// Update the propagator - if (mPropagator) + if (mPropagator) { *mPropagator = TMatrixD(propagator, TMatrixD::kMult, *mPropagator); - else + } else { mPropagator = std::make_unique<TMatrixD>(propagator); + } } //__________________________________________________________________________ @@ -280,10 +336,11 @@ const TMatrixD& TrackParam::getExtrapParameters() const void TrackParam::setExtrapParameters(const TMatrixD& extrapParameters) { /// Set extrapolated parameters - if (mExtrapParameters) + if (mExtrapParameters) { *mExtrapParameters = extrapParameters; - else + } else { mExtrapParameters = std::make_unique<TMatrixD>(extrapParameters); + } } //__________________________________________________________________________ @@ -301,10 +358,11 @@ const TMatrixD& TrackParam::getExtrapCovariances() const void TrackParam::setExtrapCovariances(const TMatrixD& extrapCovariances) { /// Set the extrapolated covariance matrix - if (mExtrapCovariances) + if (mExtrapCovariances) { *mExtrapCovariances = extrapCovariances; - else + } else { mExtrapCovariances = std::make_unique<TMatrixD>(extrapCovariances); + } } //__________________________________________________________________________ @@ -322,10 +380,11 @@ const TMatrixD& TrackParam::getSmoothParameters() const void TrackParam::setSmoothParameters(const TMatrixD& smoothParameters) { /// Set the smoothed parameters - if (mSmoothParameters) + if (mSmoothParameters) { *mSmoothParameters = smoothParameters; - else + } else { mSmoothParameters = std::make_unique<TMatrixD>(smoothParameters); + } } //__________________________________________________________________________ @@ -343,10 +402,11 @@ const TMatrixD& TrackParam::getSmoothCovariances() const void TrackParam::setSmoothCovariances(const TMatrixD& smoothCovariances) { /// Set the smoothed covariance matrix - if (mSmoothCovariances) + if (mSmoothCovariances) { *mSmoothCovariances = smoothCovariances; - else + } else { mSmoothCovariances = std::make_unique<TMatrixD>(smoothCovariances); + } } //__________________________________________________________________________ @@ -379,10 +439,12 @@ Bool_t TrackParam::isCompatibleTrackParam(const TrackParam& trackParam, Double_t // build the error matrix TMatrixD weight(5, 5); - if (mCovariances) + if (mCovariances) { weight += *mCovariances; - if (trackParam.mCovariances) + } + if (trackParam.mCovariances) { weight += *(trackParam.mCovariances); + } // invert the error matrix to get the parameter weights if possible if (weight.Determinant() == 0) { @@ -399,8 +461,9 @@ Bool_t TrackParam::isCompatibleTrackParam(const TrackParam& trackParam, Double_t chi2 = mChi2(0, 0); // check compatibility - if (chi2 > maxChi2) + if (chi2 > maxChi2) { return kFALSE; + } return kTRUE; } diff --git a/Detectors/MUON/MCH/Tracking/src/TrackSamplerSpec.cxx b/Detectors/MUON/MCH/Tracking/src/TrackSamplerSpec.cxx deleted file mode 100644 index 2fe970caf4f6c..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackSamplerSpec.cxx +++ /dev/null @@ -1,117 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackSamplerSpec.cxx -/// \brief Implementation of a data processor to read and send tracks -/// -/// \author Philippe Pillot, Subatech - -#include "TrackSamplerSpec.h" - -#include <iostream> -#include <fstream> - -#include <stdexcept> - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class TrackSamplerTask -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Get the input file from the context - LOG(INFO) << "initializing track sampler"; - - auto inputFileName = ic.options().get<std::string>("infile"); - mInputFile.open(inputFileName, ios::binary); - if (!mInputFile.is_open()) { - throw invalid_argument("Cannot open input file" + inputFileName); - } - - auto stop = [this]() { - /// close the input file - LOG(INFO) << "stop track sampler"; - this->mInputFile.close(); - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// send the tracks with attached clusters of the current event - - int event(0); - mInputFile.read(reinterpret_cast<char*>(&event), SSizeOfInt); - if (mInputFile.fail()) { - return; // probably reached eof - } - - int size(0); - mInputFile.read(reinterpret_cast<char*>(&size), SSizeOfInt); - if (size == 0) { - LOG(INFO) << "event " << event + 1 << " is empty"; - return; // skip empty event - } - - // create the output message - auto msgOut = pc.outputs().make<char>(Output{"MCH", "TRACKS", 0, Lifetime::Timeframe}, SHeaderSize + size); - if (msgOut.size() != SHeaderSize + size) { - throw length_error("incorrect message payload"); - } - - auto bufferPtr = msgOut.data(); - - // fill header info - memcpy(bufferPtr, &event, SSizeOfInt); - bufferPtr += SSizeOfInt; - memcpy(bufferPtr, &size, SSizeOfInt); - bufferPtr += SSizeOfInt; - - // fill tracks and clusters info - mInputFile.read(bufferPtr, size); - } - - private: - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SHeaderSize = 2 * SSizeOfInt; - - std::ifstream mInputFile{}; ///< input file -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackSamplerSpec() -{ - return DataProcessorSpec{ - "TrackSampler", - Inputs{}, - Outputs{OutputSpec{"MCH", "TRACKS", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<TrackSamplerTask>()}, - Options{{"infile", VariantType::String, "", {"input filename"}}}}; -} - -} // end namespace mch -} // end namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackSinkSpec.cxx b/Detectors/MUON/MCH/Tracking/src/TrackSinkSpec.cxx deleted file mode 100644 index eeb0b41b5dda0..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/TrackSinkSpec.cxx +++ /dev/null @@ -1,85 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TrackSinkSpec.cxx -/// \brief Implementation of a data processor to print the tracks -/// -/// \author Philippe Pillot, Subatech - -#include "TrackSinkSpec.h" - -#include <iostream> -#include <fstream> - -#include <stdexcept> - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Task.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class TrackSinkTask -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Get the output file from the context - LOG(INFO) << "initializing track sink"; - - auto outputFileName = ic.options().get<std::string>("outfile"); - mOutputFile.open(outputFileName, ios::out | ios::binary); - if (!mOutputFile.is_open()) { - throw invalid_argument("Cannot open output file" + outputFileName); - } - - auto stop = [this]() { - /// close the output file - LOG(INFO) << "stop track sink"; - this->mOutputFile.close(); - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// dump the tracks with attached clusters of the current event - auto msgIn = pc.inputs().get<gsl::span<char>>("tracks"); - mOutputFile.write(msgIn.data(), msgIn.size()); - } - - private: - std::ofstream mOutputFile{}; ///< output file -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getTrackSinkSpec(o2::header::DataDescription description) -{ - return DataProcessorSpec{ - "TrackSink", - Inputs{InputSpec{"tracks", "MCH", description, 0, Lifetime::Timeframe}}, - Outputs{}, - AlgorithmSpec{adaptFromTask<TrackSinkTask>()}, - Options{{"outfile", VariantType::String, "AliESDs.out.dat", {"output filename"}}}}; -} - -} // end namespace mch -} // end namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/VertexSamplerSpec.cxx b/Detectors/MUON/MCH/Tracking/src/VertexSamplerSpec.cxx deleted file mode 100644 index 7a7422e27c4d3..0000000000000 --- a/Detectors/MUON/MCH/Tracking/src/VertexSamplerSpec.cxx +++ /dev/null @@ -1,135 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 VertexSamplerSpec.cxx -/// \brief Implementation of a data processor to read and send vertices -/// -/// \author Philippe Pillot, Subatech - -#include "VertexSamplerSpec.h" - -#include <iostream> -#include <fstream> -#include <string> - -#include <stdexcept> - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" - -#include "MathUtils/Cartesian3D.h" - -namespace o2 -{ -namespace mch -{ - -using namespace std; -using namespace o2::framework; - -class VertexSamplerSpec -{ - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Get the input file from the context - LOG(INFO) << "initializing vertex sampler"; - - auto inputFileName = ic.options().get<std::string>("infile"); - if (!inputFileName.empty()) { - mInputFile.open(inputFileName, ios::binary); - if (!mInputFile.is_open()) { - throw invalid_argument("Cannot open input file" + inputFileName); - } - } - - auto stop = [this]() { - /// close the input file - LOG(INFO) << "stop vertex sampler"; - if (mInputFile.is_open()) { - this->mInputFile.close(); - } - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - /// send the vertex of the current event if an input file is provided - /// or the default vertex (0.,0.,0.) otherwise - - // get the current event - auto msgIn = pc.inputs().get<gsl::span<char>>("tracks"); - if (msgIn.size() < SSizeOfInt) { - throw out_of_range("missing event header"); - } - const int& eventTracks = *reinterpret_cast<const int*>(msgIn.data()); - - // read the corresponding vertex or set it to (0,0,0) - int eventVtx(-1); - Point3D<double> vertex(0., 0., 0.); - if (mInputFile.is_open()) { - do { - mInputFile.read(reinterpret_cast<char*>(&eventVtx), SSizeOfInt); - if (mInputFile.fail()) { - throw out_of_range(std::string("missing vertex for event ") + eventTracks); - } - VertexStruct vtx{}; - mInputFile.read(reinterpret_cast<char*>(&vtx), SSizeOfVertexStruct); - vertex.SetCoordinates(vtx.x, vtx.y, vtx.z); - } while (eventVtx != eventTracks); - } - - // create the output message - auto msgOut = pc.outputs().make<char>(Output{"MCH", "VERTEX", 0, Lifetime::Timeframe}, SSizeOfInt + SSizeOfPoint3D); - if (msgOut.size() != SSizeOfInt + SSizeOfPoint3D) { - throw length_error("incorrect message payload"); - } - - // fill it - auto bufferPtr = msgOut.data(); - memcpy(bufferPtr, &eventVtx, SSizeOfInt); - bufferPtr += SSizeOfInt; - memcpy(bufferPtr, &vertex, SSizeOfPoint3D); - } - - private: - struct VertexStruct { - double x; - double y; - double z; - }; - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SSizeOfVertexStruct = sizeof(VertexStruct); - static constexpr int SSizeOfPoint3D = sizeof(Point3D<double>); - - std::ifstream mInputFile{}; ///< input file -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getVertexSamplerSpec() -{ - return DataProcessorSpec{ - "VertexSampler", - Inputs{InputSpec{"tracks", "MCH", "TRACKS", 0, Lifetime::Timeframe}}, - Outputs{OutputSpec{"MCH", "VERTEX", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<VertexSamplerSpec>()}, - Options{{"infile", VariantType::String, "", {"input filename"}}}}; -} - -} // end namespace mch -} // end namespace o2 diff --git a/Detectors/MUON/MCH/Workflow/CMakeLists.txt b/Detectors/MUON/MCH/Workflow/CMakeLists.txt index d3d2366f7c4b6..80022eab2b4de 100644 --- a/Detectors/MUON/MCH/Workflow/CMakeLists.txt +++ b/Detectors/MUON/MCH/Workflow/CMakeLists.txt @@ -12,16 +12,9 @@ o2_add_library(MCHWorkflow SOURCES src/DataDecoderSpec.cxx src/PreClusterFinderSpec.cxx src/ClusterFinderOriginalSpec.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::MCHRawDecoder Boost::program_options - O2::MCHRawImplHelpers RapidJSON::RapidJSON O2::MCHMappingInterface O2::MCHPreClustering - O2::MCHMappingImpl3 O2::MCHRawElecMap O2::MCHBase O2::MCHClustering) - -o2_add_executable( - file-to-digits-workflow - SOURCES src/file-to-digits-workflow.cxx - COMPONENT_NAME mch - PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::MCHRawDecoder Boost::program_options - O2::MCHRawImplHelpers O2::MCHMappingInterface - O2::MCHMappingImpl3 O2::MCHRawElecMap O2::MCHBase) + O2::MCHRawImplHelpers RapidJSON::RapidJSON O2::MCHMappingInterface + O2::MCHPreClustering O2::MCHMappingImpl4 O2::MCHRawElecMap O2::MCHBase + O2::MCHClustering) o2_add_executable( cru-page-reader-workflow @@ -33,7 +26,7 @@ o2_add_executable( digits-sink-workflow SOURCES src/digits-sink-workflow.cxx COMPONENT_NAME mch - PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils Boost::program_options O2::MCHBase) + PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils Boost::program_options O2::MCHBase O2::MCHRawDecoder) o2_add_executable( raw-to-digits-workflow @@ -41,6 +34,12 @@ o2_add_executable( COMPONENT_NAME mch PUBLIC_LINK_LIBRARIES O2::MCHWorkflow) +o2_add_executable( + cru-page-to-digits-workflow + SOURCES src/cru-page-to-digits-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::MCHWorkflow) + o2_add_executable( digits-to-preclusters-workflow SOURCES src/digits-to-preclusters-workflow.cxx @@ -70,3 +69,51 @@ o2_add_executable( SOURCES src/ClusterSinkSpec.cxx src/clusters-sink-workflow.cxx COMPONENT_NAME mch PUBLIC_LINK_LIBRARIES O2::MCHWorkflow) + +o2_add_executable( + clusters-sampler-workflow + SOURCES src/ClusterSamplerSpec.cxx src/clusters-sampler-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::MCHBase) + +o2_add_executable( + clusters-to-tracks-original-workflow + SOURCES src/TrackFinderOriginalSpec.cxx src/clusters-to-tracks-original-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsMCH O2::MCHTracking) + +o2_add_executable( + clusters-to-tracks-workflow + SOURCES src/TrackFinderSpec.cxx src/clusters-to-tracks-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsMCH O2::MCHTracking) + +o2_add_executable( + vertex-sampler-workflow + SOURCES src/VertexSamplerSpec.cxx src/vertex-sampler-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework) + +o2_add_executable( + tracks-to-tracks-at-vertex-workflow + SOURCES src/TrackAtVertexSpec.cxx src/tracks-to-tracks-at-vertex-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DataFormatsMCH O2::MCHTracking) + +o2_add_executable( + tracks-sink-workflow + SOURCES src/TrackSinkSpec.cxx src/tracks-sink-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsMCH O2::MCHBase) + +o2_add_executable( + tracks-sampler-workflow + SOURCES src/TrackSamplerSpec.cxx src/tracks-sampler-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsMCH O2::MCHBase) + +o2_add_executable( + tracks-to-tracks-workflow + SOURCES src/TrackFitterSpec.cxx src/tracks-to-tracks-workflow.cxx + COMPONENT_NAME mch + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsMCH O2::MCHTracking) diff --git a/Detectors/MUON/MCH/Workflow/README.md b/Detectors/MUON/MCH/Workflow/README.md index cae197321fb29..9e6c8fb179ded 100644 --- a/Detectors/MUON/MCH/Workflow/README.md +++ b/Detectors/MUON/MCH/Workflow/README.md @@ -4,25 +4,157 @@ # MCH Workflows -## Raw data decoding: +<!-- vim-markdown-toc GFM --> + +* [Raw to digits](#raw-to-digits) +* [Digits to preclusters](#digits-to-preclusters) +* [Tracking](#tracking) + * [Original track finder](#original-track-finder) + * [New track finder](#new-track-finder) +* [Track extrapolation to vertex](#track-extrapolation-to-vertex) +* [Track fitter](#track-fitter) +* [Samplers](#samplers) + * [Cluster sampler](#cluster-sampler) + * [Track sampler](#track-sampler) + * [Vertex sampler](#vertex-sampler) +* [Sinks](#sinks) + * [Preclusters sink](#preclusters-sink) + * [Track sink](#track-sink) + +<!-- vim-markdown-toc --> + +## Raw to digits `o2-mch-raw-to-digits-workflow` -## Pre-clustering: +## Digits to preclusters `o2-mch-digits-to-preclusters-workflow` -## Pre-clustering sink: +Example of a DPL chain to go from a raw data file to a file of preclusters : + +```shell +o2-raw-file-reader-workflow --conf file-reader.cfg --loop 0 -b | +o2-mch-raw-to-digits-workflow -b | +o2-mch-digits-to-preclusters-workflow -b | +o2-mch-preclusters-sink-workflow -b +``` + +where the `file-reader.cfg` looks like this: + +```ini +[input-0] +dataOrigin = MCH +dataDescription = RAWDATA +filePath = /home/data/data-de819-ped-raw.raw +``` + +## Tracking + +### Original track finder + +`o2-mch-clusters-to-tracks-original-workflow` + +Take as input the list of clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) of the current event with the data description "CLUSTERS" and send the list of MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) and the list of associated clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) in two separate messages with the data description "TRACKS" and "TRACKCLUSTERS", respectively. + +Options `--l3Current xxx` and `--dipoleCurrent yyy` allow to specify the current in L3 and in the dipole to be used to set the magnetic field. + +Option `--moreCandidates` allows to enable the search for more track candidates in stations 4 & 5. + +Option `--debug x` allows to enable the debug level x (0 = no debug, 1 or 2). + +### New track finder + +`o2-mch-clusters-to-tracks-workflow` + +Same behavior and options as [Original track finder](#original-track-finder) + +## Track extrapolation to vertex + +`o2-mch-tracks-to-tracks-at-vertex-workflow` + +Take as input the vertex position (`Point3D<double>`) and the list of MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) of the current event with the data description "VERTEX" and "TRACKS", respectively, and send the list of tracks at vertex (`TrackAtVtxStruct` as described below) with the data description "TRACKSATVERTEX". + +```c++ +struct TrackAtVtxStruct { + TrackParamStruct paramAtVertex{}; + double dca = 0.; + double rAbs = 0.; + int mchTrackIdx = 0; +}; +``` + +Options `--l3Current xxx` and `--dipoleCurrent yyy` allow to specify the current in L3 and in the dipole to be used to set the magnetic field. + +## Track fitter + +`o2-mch-tracks-to-tracks-workflow` + +Take as input the list of MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) and the list of associated clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) of the current event with the data description "TRACKSIN" and "TRACKCLUSTERSIN", respectively, and send the list of refitted MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) and the list of associated clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) in two separate messages with the data description "TRACKS" and "TRACKCLUSTERS", respectively. + +Options `--l3Current xxx` and `--dipoleCurrent yyy` allow to specify the current in L3 and in the dipole to be used to set the magnetic field. + +## Samplers + +### Cluster sampler + +```shell +o2-mch-clusters-sampler-workflow --infile "clusters.in" +``` + +where `clusters.in` is a binary file containing for each event: + +* number of clusters (int) +* number of associated digits (int) +* list of clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) +* list of associated digits ([Digit](../Base/include/MCHBase/Digit.h)) + +Send the list of clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) of the current event with the data description "CLUSTERS". + +### Track sampler + +`o2-mch-tracks-sampler-workflow --infile "tracks.in"` + +where `tracks.in` is a binary file with the same format as the one written by the workflow [o2-mch-tracks-sink-workflow](#track-sink) + +Send the list of MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) and the list of associated clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) in two separate messages with the data description "TRACKS" and "TRACKCLUSTERS", respectively. + +Option `--forTrackFitter` allows to send the messages with the data description "TRACKSIN" and "TRACKCLUSTERSIN", respectively, as expected by the workflow [o2-mch-tracks-to-tracks-workflow](#track-fitter). + +### Vertex sampler + +`o2-mch-vertex-sampler-workflow` + +Send the vertex position (`Point3D<double>`) of the current event with the data description "VERTEX". + +Option `--infile "vertices.in"` allows to read the position of the vertex from the binary file `vertices.in` containing for each event: + +* event number (int) +* x (double) +* y (double) +* z (double) + +If no binary file is provided, the vertex is always set to (0,0,0). + +## Sinks + +### Preclusters sink `o2-mch-preclusters-sink-workflow` -## Example of DPL chain: +### Track sink -`o2-raw-file-reader-workflow --conf file-reader.cfg --loop 0 --message-per-tf -b | o2-mch-raw-to-digits-workflow -b | o2-mch-digits-to-preclusters-workflow -b | o2-mch-preclusters-sink-workflow -b` +`o2-mch-tracks-sink-workflow --outfile "tracks.out"` -where the `file-reader.cfg` looks like this: +Take as input the list of tracks at vertex ([TrackAtVtxStruct](#track-extrapolation-to-vertex)), the list of MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) and the list of associated clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) of the current event with the data description "TRACKSATVERTEX", "TRACKS" and "TRACKCLUSTERS", respectively, and write them in the binary file `tracks.out` with the following format: + +* number of tracks at vertex (int) +* number of MCH tracks (int) +* number of associated clusters (int) +* list of tracks at vertex ([TrackAtVtxStruct](#track-extrapolation-to-vertex)) +* list of MCH tracks ([TrackMCH](../../../../DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/TrackMCH.h)) +* list of associated clusters ([ClusterStruct](../Base/include/MCHBase/ClusterBlock.h)) + +Option `--tracksAtVertexOnly` allows to take as input and write only the tracks at vertex (number of MCH tracks and number of associated clusters = 0). - [input-0] - dataOrigin = MCH - dataDescription = RAWDATA - filePath = /home/data/data-de819-ped-raw.raw +Option `--mchTracksOnly` allows to take as input and write only the MCH tracks and associated clusters (number of tracks at vertex = 0). diff --git a/Detectors/MUON/MCH/Workflow/include/MCHWorkflow/DataDecoderSpec.h b/Detectors/MUON/MCH/Workflow/include/MCHWorkflow/DataDecoderSpec.h index f8bc1c21582a4..57820cd722f47 100644 --- a/Detectors/MUON/MCH/Workflow/include/MCHWorkflow/DataDecoderSpec.h +++ b/Detectors/MUON/MCH/Workflow/include/MCHWorkflow/DataDecoderSpec.h @@ -27,7 +27,7 @@ namespace mch namespace raw { -o2::framework::DataProcessorSpec getDecodingSpec(); +o2::framework::DataProcessorSpec getDecodingSpec(std::string inputSpec = "TF:MCH/RAWDATA"); } // end namespace raw } // end namespace mch diff --git a/Detectors/MUON/MCH/Workflow/src/ClusterFinderOriginalSpec.cxx b/Detectors/MUON/MCH/Workflow/src/ClusterFinderOriginalSpec.cxx index b45f9ff46ff93..1b18156c737b3 100644 --- a/Detectors/MUON/MCH/Workflow/src/ClusterFinderOriginalSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/ClusterFinderOriginalSpec.cxx @@ -29,6 +29,7 @@ #include "Framework/Lifetime.h" #include "Framework/Output.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "MCHBase/Digit.h" #include "MCHBase/PreCluster.h" diff --git a/Detectors/MUON/MCH/Tracking/src/ClusterSamplerSpec.cxx b/Detectors/MUON/MCH/Workflow/src/ClusterSamplerSpec.cxx similarity index 76% rename from Detectors/MUON/MCH/Tracking/src/ClusterSamplerSpec.cxx rename to Detectors/MUON/MCH/Workflow/src/ClusterSamplerSpec.cxx index 04da59e121c1b..58b89e1da2b44 100644 --- a/Detectors/MUON/MCH/Tracking/src/ClusterSamplerSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/ClusterSamplerSpec.cxx @@ -27,8 +27,10 @@ #include "Framework/Lifetime.h" #include "Framework/Output.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "MCHBase/ClusterBlock.h" +#include "MCHBase/Digit.h" namespace o2 { @@ -66,43 +68,37 @@ class ClusterSamplerTask { /// send the clusters of the current event - int event(0); - mInputFile.read(reinterpret_cast<char*>(&event), SSizeOfInt); + // get the number of clusters and associated digits + int nClusters(0); + mInputFile.read(reinterpret_cast<char*>(&nClusters), sizeof(int)); if (mInputFile.fail()) { pc.services().get<ControlService>().endOfStream(); return; // probably reached eof } + int nDigits(0); + mInputFile.read(reinterpret_cast<char*>(&nDigits), sizeof(int)); - int nClusters(0); - mInputFile.read(reinterpret_cast<char*>(&nClusters), SSizeOfInt); - if (nClusters == 0) { - return; // skip empty event - } - - // create the output message - auto size = nClusters * SSizeOfClusterStruct; - auto msgOut = pc.outputs().make<char>(Output{"MCH", "CLUSTERS", 0, Lifetime::Timeframe}, SHeaderSize + size); - if (msgOut.size() != SHeaderSize + size) { + if (nClusters < 0 || nDigits < 0) { throw length_error("incorrect message payload"); } - auto bufferPtr = msgOut.data(); + // create the output message + auto clusters = pc.outputs().make<ClusterStruct>(Output{"MCH", "CLUSTERS", 0, Lifetime::Timeframe}, nClusters); - // fill header info - memcpy(bufferPtr, &event, SSizeOfInt); - bufferPtr += SSizeOfInt; - memcpy(bufferPtr, &nClusters, SSizeOfInt); - bufferPtr += SSizeOfInt; + // fill clusters in O2 format, if any + if (nClusters > 0) { + mInputFile.read(reinterpret_cast<char*>(clusters.data()), clusters.size_bytes()); + } else { + LOG(INFO) << "event is empty"; + } - // fill tracks and clusters info - mInputFile.read(bufferPtr, size); + // skip the digits if any + if (nDigits > 0) { + mInputFile.seekg(nDigits * sizeof(Digit), std::ios::cur); + } } private: - static constexpr int SSizeOfInt = sizeof(int); - static constexpr int SHeaderSize = 2 * SSizeOfInt; - static constexpr int SSizeOfClusterStruct = sizeof(ClusterStruct); - std::ifstream mInputFile{}; ///< input file }; diff --git a/Detectors/MUON/MCH/Tracking/src/ClusterSamplerSpec.h b/Detectors/MUON/MCH/Workflow/src/ClusterSamplerSpec.h similarity index 100% rename from Detectors/MUON/MCH/Tracking/src/ClusterSamplerSpec.h rename to Detectors/MUON/MCH/Workflow/src/ClusterSamplerSpec.h diff --git a/Detectors/MUON/MCH/Workflow/src/ClusterSinkSpec.cxx b/Detectors/MUON/MCH/Workflow/src/ClusterSinkSpec.cxx index d666faaceeb09..d8ed38121c68b 100644 --- a/Detectors/MUON/MCH/Workflow/src/ClusterSinkSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/ClusterSinkSpec.cxx @@ -28,6 +28,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Lifetime.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "MCHBase/Digit.h" #include "MCHBase/ClusterBlock.h" diff --git a/Detectors/MUON/MCH/Workflow/src/DataDecoderSpec.cxx b/Detectors/MUON/MCH/Workflow/src/DataDecoderSpec.cxx index eb2ad9abad33a..015849586713a 100644 --- a/Detectors/MUON/MCH/Workflow/src/DataDecoderSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/DataDecoderSpec.cxx @@ -8,7 +8,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// /// /// \file DatDecoderSpec.cxx /// \author Andrea Ferrero @@ -20,6 +19,9 @@ #include <iostream> #include <fstream> #include <stdexcept> +#include <array> +#include <functional> + #include "Framework/CallbackService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" @@ -29,21 +31,16 @@ #include "Framework/Task.h" #include "Framework/WorkflowSpec.h" +#include "Headers/RAWDataHeader.h" +#include "DetectorsRaw/RDHUtils.h" #include "DPLUtils/DPLRawParser.h" + #include "MCHBase/Digit.h" -#include "Headers/RAWDataHeader.h" #include "MCHRawCommon/DataFormats.h" -#include "MCHRawDecoder/PageDecoder.h" +#include "MCHRawDecoder/DataDecoder.h" #include "MCHRawElecMap/Mapper.h" #include "MCHMappingInterface/Segmentation.h" #include "MCHWorkflow/DataDecoderSpec.h" -#include <array> -#include "DetectorsRaw/RDHUtils.h" - -namespace o2::header -{ -extern std::ostream& operator<<(std::ostream&, const o2::header::RAWDataHeaderV4&); -} namespace o2 { @@ -57,201 +54,49 @@ using namespace o2::framework; using namespace o2::mch::mapping; using RDH = o2::header::RDHAny; -std::array<int, 64> refManu2ds_st345 = { - 63, 62, 61, 60, 59, 57, 56, 53, 51, 50, 47, 45, 44, 41, 38, 35, - 36, 33, 34, 37, 32, 39, 40, 42, 43, 46, 48, 49, 52, 54, 55, 58, - 7, 8, 5, 2, 6, 1, 3, 0, 4, 9, 10, 15, 17, 18, 22, 25, - 31, 30, 29, 28, 27, 26, 24, 23, 20, 21, 16, 19, 12, 14, 11, 13}; -std::array<int, 64> refDs2manu_st345; - -int manu2ds(int i) -{ - return refManu2ds_st345[i]; -} - -int ds2manu(int i) -{ - return refDs2manu_st345[i]; -} - //======================= // Data decoder class DataDecoderTask { - private: - void decodeBuffer(gsl::span<const std::byte> page, std::vector<o2::mch::Digit>& digits) - { - size_t ndigits{0}; - - uint32_t orbit; - - auto channelHandler = [&](DsElecId dsElecId, uint8_t channel, o2::mch::raw::SampaCluster sc) { - if (mDs2manu) { - channel = ds2manu(int(channel)); - } - if (mPrint) { - auto s = asString(dsElecId); - auto ch = fmt::format("{}-CH{}", s, channel); - std::cout << "dsElecId: " << ch << std::endl; - } - uint32_t digitadc(0); - if (sc.isClusterSum()) { - digitadc = sc.chargeSum; - } else { - for (auto& s : sc.samples) { - digitadc += s; - } - } - - int deId{-1}; - int dsIddet{-1}; - if (auto opt = mElec2Det(dsElecId); opt.has_value()) { - DsDetId dsDetId = opt.value(); - dsIddet = dsDetId.dsId(); - deId = dsDetId.deId(); - } - if (mPrint) { - std::cout << "deId " << deId << " dsIddet " << dsIddet << " channel " << (int)channel << std::endl; - } - - if (deId < 0 || dsIddet < 0) { - return; - } - - int padId = -1; - try { - const Segmentation& segment = segmentation(deId); - padId = segment.findPadByFEE(dsIddet, int(channel)); - if (mPrint) { - std::cout << "DS " << (int)dsElecId.elinkId() << " CHIP " << ((int)channel) / 32 << " CH " << ((int)channel) % 32 << " ADC " << digitadc << " DE# " << deId << " DSid " << dsIddet << " PadId " << padId << std::endl; - } - } catch (const std::exception& e) { - std::cout << "Failed to get padId: " << e.what() << std::endl; - return; - } - - Digit::Time time; - time.sampaTime = sc.sampaTime; - time.bunchCrossing = sc.bunchCrossing; - time.orbit = orbit; - - digits.emplace_back(o2::mch::Digit(deId, padId, digitadc, time, sc.nofSamples())); - - if (mPrint) - std::cout << "DIGIT STORED:\nADC " << digits.back().getADC() << " DE# " << digits.back().getDetID() << " PadId " << digits.back().getPadID() << " time " << digits.back().getTime().sampaTime << std::endl; - ++ndigits; - }; - - const auto patchPage = [&](gsl::span<const std::byte> rdhBuffer) { - auto& rdhAny = *reinterpret_cast<RDH*>(const_cast<std::byte*>(&(rdhBuffer[0]))); - mNrdhs++; - auto cruId = o2::raw::RDHUtils::getCRUID(rdhAny) & 0xFF; - auto flags = o2::raw::RDHUtils::getCRUID(rdhAny) & 0xFF00; - auto endpoint = o2::raw::RDHUtils::getEndPointID(rdhAny); - auto feeId = cruId * 2 + endpoint + flags; - o2::raw::RDHUtils::setFEEID(rdhAny, feeId); - orbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdhAny); - if (mPrint) { - std::cout << mNrdhs << "--\n"; - o2::raw::RDHUtils::printRDH(rdhAny); - } - }; - - patchPage(page); - - if (!mDecoder) { - mDecoder = mFee2Solar ? o2::mch::raw::createPageDecoder(page, channelHandler, mFee2Solar) - : o2::mch::raw::createPageDecoder(page, channelHandler); - } - mDecoder(page); - } - - private: - std::string readFileContent(const std::string& filename) - { - std::string content; - std::string s; - std::ifstream in(filename); - while (std::getline(in, s)) { - content += s; - content += " "; - } - return content; - } - - void initElec2DetMapper(const std::string& filename) - { - if (filename.empty()) { - mElec2Det = createElec2DetMapper<ElectronicMapperGenerated>(); - } else { - ElectronicMapperString::sFecMap = readFileContent(filename); - mElec2Det = createElec2DetMapper<ElectronicMapperString>(); - } - } - - void initFee2SolarMapper(const std::string& filename) - { - if (filename.empty()) { - mFee2Solar = createFeeLink2SolarMapper<ElectronicMapperGenerated>(); - } else { - ElectronicMapperString::sCruMap = readFileContent(filename); - mFee2Solar = createFeeLink2SolarMapper<ElectronicMapperString>(); - } - } - public: //_________________________________________________________________________________________________ void init(framework::InitContext& ic) { - mNrdhs = 0; + SampaChannelHandler channelHandler; + RdhHandler rdhHandler; - for (int i = 0; i < 64; i++) { - for (int j = 0; j < 64; j++) { - if (refManu2ds_st345[j] != i) { - continue; - } - refDs2manu_st345[i] = j; - break; - } - } - - mDs2manu = ic.options().get<bool>("ds2manu"); + auto ds2manu = ic.options().get<bool>("ds2manu"); mPrint = ic.options().get<bool>("print"); - auto mapCRUfile = ic.options().get<std::string>("cru-map"); auto mapFECfile = ic.options().get<std::string>("fec-map"); - initFee2SolarMapper(mapCRUfile); - initElec2DetMapper(mapFECfile); + mDecoder = new DataDecoder(channelHandler, rdhHandler, mapCRUfile, mapFECfile, ds2manu, mPrint); } //_________________________________________________________________________________________________ - void - decodeTF(framework::ProcessingContext& pc, std::vector<o2::mch::Digit>& digits) + // the decodeTF() function processes the the messages generated by the (sub)TimeFrame builder + void decodeTF(framework::ProcessingContext& pc) { // get the input buffer auto& inputs = pc.inputs(); DPLRawParser parser(inputs, o2::framework::select("TF:MCH/RAWDATA")); for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - // retrieving RDH v4 - auto const* rdh = it.get_if<o2::header::RAWDataHeaderV4>(); - // retrieving the raw pointer of the page + auto const* rdh = it.get_if<RDH>(); auto const* raw = it.raw(); - // size of payload size_t payloadSize = it.size(); - if (payloadSize == 0) { continue; } - gsl::span<const std::byte> buffer(reinterpret_cast<const std::byte*>(raw), sizeof(o2::header::RAWDataHeaderV4) + payloadSize); - decodeBuffer(buffer, digits); + gsl::span<const std::byte> buffer(reinterpret_cast<const std::byte*>(raw), sizeof(RDH) + payloadSize); + mDecoder->decodeBuffer(buffer); } } //_________________________________________________________________________________________________ - void decodeReadout(const o2::framework::DataRef& input, std::vector<o2::mch::Digit>& digits) + // the decodeReadout() function processes the messages generated by o2-mch-cru-page-reader-workflow + void decodeReadout(const o2::framework::DataRef& input) { static int nFrame = 1; // get the input buffer @@ -277,57 +122,68 @@ class DataDecoderTask } gsl::span<const std::byte> buffer(reinterpret_cast<const std::byte*>(raw), payloadSize); - decodeBuffer(buffer, digits); + mDecoder->decodeBuffer(buffer); } //_________________________________________________________________________________________________ void run(framework::ProcessingContext& pc) { - std::vector<o2::mch::Digit> digits; + auto createBuffer = [&](auto& vec, size_t& size) { + size = vec.empty() ? 0 : sizeof(*(vec.begin())) * vec.size(); + char* buf = nullptr; + if (size > 0) { + buf = (char*)malloc(size); + if (buf) { + char* p = buf; + size_t sizeofElement = sizeof(*(vec.begin())); + for (auto& element : vec) { + memcpy(p, &element, sizeofElement); + p += sizeofElement; + } + } + } + return buf; + }; - decodeTF(pc, digits); + mDecoder->reset(); + decodeTF(pc); for (auto&& input : pc.inputs()) { - if (input.spec->binding == "readout") - decodeReadout(input, digits); + if (input.spec->binding == "readout") { + decodeReadout(input); + } } + auto& digits = mDecoder->getOutputDigits(); + auto& orbits = mDecoder->getOrbits(); + if (mPrint) { for (auto d : digits) { std::cout << " DE# " << d.getDetID() << " PadId " << d.getPadID() << " ADC " << d.getADC() << " time " << d.getTime().sampaTime << std::endl; } } - - const size_t OUT_SIZE = sizeof(o2::mch::Digit) * digits.size(); - // send the output buffer via DPL - char* outbuffer = nullptr; - outbuffer = (char*)realloc(outbuffer, OUT_SIZE); - memcpy(outbuffer, digits.data(), OUT_SIZE); + size_t digitsSize, orbitsSize; + char* digitsBuffer = createBuffer(digits, digitsSize); + char* orbitsBuffer = createBuffer(orbits, orbitsSize); // create the output message auto freefct = [](void* data, void*) { free(data); }; - pc.outputs().adoptChunk(Output{"MCH", "DIGITS", 0}, outbuffer, OUT_SIZE, freefct, nullptr); + pc.outputs().adoptChunk(Output{"MCH", "DIGITS", 0}, digitsBuffer, digitsSize, freefct, nullptr); + pc.outputs().adoptChunk(Output{"MCH", "ORBITS", 0}, orbitsBuffer, orbitsSize, freefct, nullptr); } private: - Elec2DetMapper mElec2Det{nullptr}; - FeeLink2SolarMapper mFee2Solar{nullptr}; - o2::mch::raw::PageDecoder mDecoder; - size_t mNrdhs{0}; - - std::ifstream mInputFile{}; ///< input file - bool mDs2manu = false; ///< print convert channel numbering from Run3 to Run1-2 order - bool mPrint = false; ///< print digits -}; // namespace raw + bool mPrint = {false}; + DataDecoder* mDecoder = {nullptr}; +}; //_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getDecodingSpec() +o2::framework::DataProcessorSpec getDecodingSpec(std::string inputSpec) { return DataProcessorSpec{ "DataDecoder", - //o2::framework::select("TF:MCH/RAWDATA, re:ROUT/RAWDATA"), - o2::framework::select("readout:ROUT/RAWDATA"), - Outputs{OutputSpec{"MCH", "DIGITS", 0, Lifetime::Timeframe}}, + o2::framework::select(inputSpec.c_str()), + Outputs{OutputSpec{"MCH", "DIGITS", 0, Lifetime::Timeframe}, OutputSpec{"MCH", "ORBITS", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask<DataDecoderTask>()}, Options{{"print", VariantType::Bool, false, {"print digits"}}, {"cru-map", VariantType::String, "", {"custom CRU mapping"}}, diff --git a/Detectors/MUON/MCH/Workflow/src/DigitSamplerSpec.cxx b/Detectors/MUON/MCH/Workflow/src/DigitSamplerSpec.cxx index 336232406092b..0d5338affcaaf 100644 --- a/Detectors/MUON/MCH/Workflow/src/DigitSamplerSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/DigitSamplerSpec.cxx @@ -31,6 +31,7 @@ #include "Framework/Lifetime.h" #include "Framework/Output.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "MCHBase/Digit.h" diff --git a/Detectors/MUON/MCH/Workflow/src/PreClusterFinderSpec.cxx b/Detectors/MUON/MCH/Workflow/src/PreClusterFinderSpec.cxx index 6784d39fe917a..6e833233243c3 100644 --- a/Detectors/MUON/MCH/Workflow/src/PreClusterFinderSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/PreClusterFinderSpec.cxx @@ -29,6 +29,7 @@ #include "Framework/Lifetime.h" #include "Framework/Output.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "MCHBase/Digit.h" #include "MCHBase/PreCluster.h" @@ -42,6 +43,12 @@ namespace mch using namespace std; using namespace o2::framework; +enum tCheckNoLeftoverDigits { + CHECK_NO_LEFTOVER_DIGITS_OFF, + CHECK_NO_LEFTOVER_DIGITS_ERROR, + CHECK_NO_LEFTOVER_DIGITS_FATAL +}; + class PreClusterFinderTask { public: @@ -66,6 +73,15 @@ class PreClusterFinderTask << std::chrono::duration<double, std::milli>(tEnd - tStart).count() << " ms"; }; ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + + auto checkNoLeftoverDigits = ic.options().get<std::string>("check-no-leftover-digits"); + if (checkNoLeftoverDigits == "off") { + mCheckNoLeftoverDigits = CHECK_NO_LEFTOVER_DIGITS_OFF; + } else if (checkNoLeftoverDigits == "error") { + mCheckNoLeftoverDigits = CHECK_NO_LEFTOVER_DIGITS_ERROR; + } else if (checkNoLeftoverDigits == "fatal") { + mCheckNoLeftoverDigits = CHECK_NO_LEFTOVER_DIGITS_FATAL; + } } //_________________________________________________________________________________________________ @@ -101,9 +117,24 @@ class PreClusterFinderTask mPreClusters.reserve(nPreClusters); // to avoid reallocation if mUsedDigits.reserve(digits.size()); // the capacity is exceeded mPreClusterFinder.getPreClusters(mPreClusters, mUsedDigits); - if (mUsedDigits.size() != digits.size()) { - throw runtime_error("some digits have been lost during the preclustering"); - } + + // check sizes of input and output digits vectors + bool digitsSizesDiffer = (mUsedDigits.size() != digits.size()); + switch (mCheckNoLeftoverDigits) { + case CHECK_NO_LEFTOVER_DIGITS_OFF: + break; + case CHECK_NO_LEFTOVER_DIGITS_ERROR: + if (digitsSizesDiffer) { + LOG(ERROR) << "some digits have been lost during the preclustering"; + } + break; + case CHECK_NO_LEFTOVER_DIGITS_FATAL: + if (digitsSizesDiffer) { + throw runtime_error("some digits have been lost during the preclustering"); + } + break; + }; + tEnd = std::chrono::high_resolution_clock::now(); mTimeStorePreClusters += tEnd - tStart; @@ -117,6 +148,8 @@ class PreClusterFinderTask std::vector<PreCluster> mPreClusters{}; ///< vector of preclusters std::vector<Digit> mUsedDigits{}; ///< vector of digits in the preclusters + int mCheckNoLeftoverDigits{CHECK_NO_LEFTOVER_DIGITS_ERROR}; ///< digits vector size check option + std::chrono::duration<double, std::milli> mTimeResetPreClusterFinder{}; ///< timer std::chrono::duration<double, std::milli> mTimeLoadDigits{}; ///< timer std::chrono::duration<double, std::milli> mTimePreClusterFinder{}; ///< timer @@ -126,13 +159,14 @@ class PreClusterFinderTask //_________________________________________________________________________________________________ o2::framework::DataProcessorSpec getPreClusterFinderSpec() { + std::string helpstr = "check that all digits are included in pre-clusters"; return DataProcessorSpec{ "PreClusterFinder", Inputs{InputSpec{"digits", "MCH", "DIGITS", 0, Lifetime::Timeframe}}, Outputs{OutputSpec{"MCH", "PRECLUSTERS", 0, Lifetime::Timeframe}, OutputSpec{"MCH", "PRECLUSTERDIGITS", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask<PreClusterFinderTask>()}, - Options{}}; + Options{{"check-no-leftover-digits", VariantType::String, "error", {helpstr}}}}; } } // end namespace mch diff --git a/Detectors/MUON/MCH/Workflow/src/PreClusterSinkSpec.cxx b/Detectors/MUON/MCH/Workflow/src/PreClusterSinkSpec.cxx index fccb8fe77cfb0..2721d8c624a2b 100644 --- a/Detectors/MUON/MCH/Workflow/src/PreClusterSinkSpec.cxx +++ b/Detectors/MUON/MCH/Workflow/src/PreClusterSinkSpec.cxx @@ -28,6 +28,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Lifetime.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "MCHBase/Digit.h" #include "MCHBase/PreCluster.h" diff --git a/Detectors/MUON/MCH/Workflow/src/TrackAtVertexSpec.cxx b/Detectors/MUON/MCH/Workflow/src/TrackAtVertexSpec.cxx new file mode 100644 index 0000000000000..b52e5677f026f --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/TrackAtVertexSpec.cxx @@ -0,0 +1,203 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackAtVertexSpec.cxx +/// \brief Implementation of a data processor to extrapolate the tracks to the vertex +/// +/// \author Philippe Pillot, Subatech + +#include "TrackAtVertexSpec.h" + +#include <chrono> +#include <stdexcept> +#include <list> + +#include <gsl/span> + +#include <TMath.h> +#include <TGeoManager.h> +#include <TGeoGlobalMagField.h> + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Output.h" +#include "Framework/Task.h" + +#include "DetectorsBase/GeometryManager.h" +#include "MathUtils/Cartesian.h" +#include "Field/MagneticField.h" +#include "DataFormatsMCH/TrackMCH.h" +#include "MCHBase/TrackBlock.h" +#include "MCHTracking/TrackParam.h" +#include "MCHTracking/TrackExtrap.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class TrackAtVertexTask +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Prepare the track extrapolation tools + + LOG(INFO) << "initializing track extrapolation to vertex"; + + if (!gGeoManager) { + o2::base::GeometryManager::loadGeometry("O2geometry.root"); + if (!gGeoManager) { + throw std::runtime_error("cannot load the geometry"); + } + } + + if (!TGeoGlobalMagField::Instance()->GetField()) { + auto l3Current = ic.options().get<float>("l3Current"); + auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); + auto field = o2::field::MagneticField::createFieldMap(l3Current, dipoleCurrent, o2::field::MagneticField::kConvLHC, false, + 3500., "A-A", "$(O2_ROOT)/share/Common/maps/mfchebKGI_sym.root"); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + TrackExtrap::setField(); + } + + auto stop = [this]() { + LOG(INFO) << "track propagation to vertex duration = " << mElapsedTime.count() << " s"; + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// propagate the MCH tracks to the vertex and send the results + + // get the vertex + auto vertex = pc.inputs().get<math_utils::Point3D<double>>("vertex"); + + // get the tracks + auto tracks = pc.inputs().get<gsl::span<TrackMCH>>("tracks"); + + // propagate the tracks to the vertex + auto tStart = std::chrono::high_resolution_clock::now(); + extrapTracksToVertex(tracks, vertex); + auto tEnd = std::chrono::high_resolution_clock::now(); + mElapsedTime += tEnd - tStart; + + // create the output message + auto msgOut = pc.outputs().make<char>(Output{"MCH", "TRACKSATVERTEX", 0, Lifetime::Timeframe}, + sizeof(int) + mTracksAtVtx.size() * sizeof(TrackAtVtxStruct)); + + // write the tracks + writeTracks(msgOut.data()); + } + + private: + struct TrackAtVtxStruct { + TrackParamStruct paramAtVertex{}; + double dca = 0.; + double rAbs = 0.; + int mchTrackIdx = 0; + }; + + //_________________________________________________________________________________________________ + void extrapTracksToVertex(gsl::span<const TrackMCH>& tracks, const math_utils::Point3D<double>& vertex) + { + /// compute the tracks parameters at vertex, at DCA and at the end of the absorber + + mTracksAtVtx.clear(); + int trackIdx(-1); + + for (const auto& track : tracks) { + + // create a new track at vertex pointing to the current track + mTracksAtVtx.emplace_back(); + auto& trackAtVtx = mTracksAtVtx.back(); + trackAtVtx.mchTrackIdx = ++trackIdx; + + // extrapolate to vertex + TrackParam trackParamAtVertex(track.getZ(), track.getParameters()); + if (!TrackExtrap::extrapToVertex(&trackParamAtVertex, vertex.x(), vertex.y(), vertex.z(), 0., 0.)) { + mTracksAtVtx.pop_back(); + continue; + } + trackAtVtx.paramAtVertex.x = trackParamAtVertex.getNonBendingCoor(); + trackAtVtx.paramAtVertex.y = trackParamAtVertex.getBendingCoor(); + trackAtVtx.paramAtVertex.z = trackParamAtVertex.getZ(); + trackAtVtx.paramAtVertex.px = trackParamAtVertex.px(); + trackAtVtx.paramAtVertex.py = trackParamAtVertex.py(); + trackAtVtx.paramAtVertex.pz = trackParamAtVertex.pz(); + trackAtVtx.paramAtVertex.sign = trackParamAtVertex.getCharge(); + + // extrapolate to DCA + TrackParam trackParamAtDCA(track.getZ(), track.getParameters()); + if (!TrackExtrap::extrapToVertexWithoutBranson(&trackParamAtDCA, vertex.z())) { + mTracksAtVtx.pop_back(); + continue; + } + double dcaX = trackParamAtDCA.getNonBendingCoor() - vertex.x(); + double dcaY = trackParamAtDCA.getBendingCoor() - vertex.y(); + trackAtVtx.dca = TMath::Sqrt(dcaX * dcaX + dcaY * dcaY); + + // extrapolate to the end of the absorber + TrackParam trackParamAtRAbs(track.getZ(), track.getParameters()); + if (!TrackExtrap::extrapToZ(&trackParamAtRAbs, -505.)) { + mTracksAtVtx.pop_back(); + continue; + } + double xAbs = trackParamAtRAbs.getNonBendingCoor(); + double yAbs = trackParamAtRAbs.getBendingCoor(); + trackAtVtx.rAbs = TMath::Sqrt(xAbs * xAbs + yAbs * yAbs); + } + } + + //_________________________________________________________________________________________________ + void writeTracks(char* bufferPtr) const + { + /// write the track informations in the message payload + + // write the number of tracks + int nTracks = mTracksAtVtx.size(); + memcpy(bufferPtr, &nTracks, sizeof(int)); + bufferPtr += sizeof(int); + + // write the tracks + if (nTracks > 0) { + memcpy(bufferPtr, mTracksAtVtx.data(), nTracks * sizeof(TrackAtVtxStruct)); + } + } + + std::vector<TrackAtVtxStruct> mTracksAtVtx{}; ///< list of tracks extrapolated to vertex + std::chrono::duration<double> mElapsedTime{}; ///< timer +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getTrackAtVertexSpec() +{ + return DataProcessorSpec{ + "TrackAtVertex", + Inputs{InputSpec{"vertex", "MCH", "VERTEX", 0, Lifetime::Timeframe}, + InputSpec{"tracks", "MCH", "TRACKS", 0, Lifetime::Timeframe}, + InputSpec{"clusters", "MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}}, + Outputs{OutputSpec{"MCH", "TRACKSATVERTEX", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<TrackAtVertexTask>()}, + Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, + {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}}}; +} + +} // namespace mch +} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackAtVertexSpec.h b/Detectors/MUON/MCH/Workflow/src/TrackAtVertexSpec.h similarity index 100% rename from Detectors/MUON/MCH/Tracking/src/TrackAtVertexSpec.h rename to Detectors/MUON/MCH/Workflow/src/TrackAtVertexSpec.h diff --git a/Detectors/MUON/MCH/Workflow/src/TrackFinderOriginalSpec.cxx b/Detectors/MUON/MCH/Workflow/src/TrackFinderOriginalSpec.cxx new file mode 100644 index 0000000000000..ead732c2302a0 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/TrackFinderOriginalSpec.cxx @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackFinderOriginalSpec.cxx +/// \brief Implementation of a data processor to read clusters, reconstruct tracks and send them +/// +/// \author Philippe Pillot, Subatech + +#include "TrackFinderOriginalSpec.h" + +#include <chrono> +#include <array> +#include <list> +#include <stdexcept> + +#include <gsl/span> + +#include "Framework/CallbackService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Output.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +#include "DataFormatsMCH/TrackMCH.h" +#include "MCHBase/ClusterBlock.h" +#include "MCHTracking/TrackParam.h" +#include "MCHTracking/Cluster.h" +#include "MCHTracking/Track.h" +#include "MCHTracking/TrackFinderOriginal.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class TrackFinderTask +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Prepare the track extrapolation tools + + LOG(INFO) << "initializing track finder"; + + auto l3Current = ic.options().get<float>("l3Current"); + auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); + mTrackFinder.init(l3Current, dipoleCurrent); + + auto moreCandidates = ic.options().get<bool>("moreCandidates"); + mTrackFinder.findMoreTrackCandidates(moreCandidates); + + auto refineTracks = !ic.options().get<bool>("noRefinement"); + mTrackFinder.refineTracks(refineTracks); + + auto debugLevel = ic.options().get<int>("debug"); + mTrackFinder.debug(debugLevel); + + auto stop = [this]() { + mTrackFinder.printStats(); + mTrackFinder.printTimers(); + LOG(INFO) << "tracking duration = " << mElapsedTime.count() << " s"; + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// read the clusters of the current event, find tracks and send them + + // get the input clusters + auto clustersIn = pc.inputs().get<gsl::span<ClusterStruct>>("clusters"); + std::array<std::list<Cluster>, 10> clusters{}; + for (const auto& cluster : clustersIn) { + clusters[cluster.getChamberId()].emplace_back(cluster); + } + + // run the track finder + auto tStart = std::chrono::high_resolution_clock::now(); + const auto& tracks = mTrackFinder.findTracks(&clusters); + auto tEnd = std::chrono::high_resolution_clock::now(); + mElapsedTime += tEnd - tStart; + + // create the output messages for tracks and attached clusters + auto& mchTracks = pc.outputs().make<std::vector<TrackMCH>>(Output{"MCH", "TRACKS", 0, Lifetime::Timeframe}); + auto& usedClusters = pc.outputs().make<std::vector<ClusterStruct>>(Output{"MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}); + + // write the messages + if (tracks.size() > 0) { + writeTracks(tracks, mchTracks, usedClusters); + } + } + + private: + //_________________________________________________________________________________________________ + void writeTracks(const std::list<Track>& tracks, + std::vector<TrackMCH, o2::pmr::polymorphic_allocator<TrackMCH>>& mchTracks, + std::vector<ClusterStruct, o2::pmr::polymorphic_allocator<ClusterStruct>>& usedClusters) const + { + /// fill the output messages with tracks and attached clusters + + // avoid memory reallocation + mchTracks.reserve(tracks.size()); + usedClusters.reserve(20. * tracks.size()); + + for (const auto& track : tracks) { + + const auto& param = track.first(); + mchTracks.emplace_back(param.getZ(), param.getParameters(), param.getCovariances(), + param.getTrackChi2(), usedClusters.size(), track.getNClusters()); + + for (const auto& param : track) { + usedClusters.emplace_back(param.getClusterPtr()->getClusterStruct()); + } + } + } + + TrackFinderOriginal mTrackFinder{}; ///< track finder + std::chrono::duration<double> mElapsedTime{}; ///< timer +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getTrackFinderOriginalSpec() +{ + return DataProcessorSpec{ + "TrackFinderOriginal", + Inputs{InputSpec{"clusters", "MCH", "CLUSTERS", 0, Lifetime::Timeframe}}, + Outputs{OutputSpec{"MCH", "TRACKS", 0, Lifetime::Timeframe}, + OutputSpec{"MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<TrackFinderTask>()}, + Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, + {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}, + {"moreCandidates", VariantType::Bool, false, {"Find more track candidates"}}, + {"noRefinement", VariantType::Bool, false, {"Disable the track refinement"}}, + {"debug", VariantType::Int, 0, {"debug level"}}}}; +} + +} // namespace mch +} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalSpec.h b/Detectors/MUON/MCH/Workflow/src/TrackFinderOriginalSpec.h similarity index 100% rename from Detectors/MUON/MCH/Tracking/src/TrackFinderOriginalSpec.h rename to Detectors/MUON/MCH/Workflow/src/TrackFinderOriginalSpec.h diff --git a/Detectors/MUON/MCH/Workflow/src/TrackFinderSpec.cxx b/Detectors/MUON/MCH/Workflow/src/TrackFinderSpec.cxx new file mode 100644 index 0000000000000..62ecc54218e46 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/TrackFinderSpec.cxx @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackFinderSpec.cxx +/// \brief Implementation of a data processor to read clusters, reconstruct tracks and send them +/// +/// \author Philippe Pillot, Subatech + +#include "TrackFinderSpec.h" + +#include <chrono> +#include <unordered_map> +#include <list> +#include <stdexcept> + +#include <gsl/span> + +#include "Framework/CallbackService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Output.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +#include "DataFormatsMCH/TrackMCH.h" +#include "MCHBase/ClusterBlock.h" +#include "MCHTracking/TrackParam.h" +#include "MCHTracking/Cluster.h" +#include "MCHTracking/Track.h" +#include "MCHTracking/TrackFinder.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class TrackFinderTask +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Prepare the track extrapolation tools + + LOG(INFO) << "initializing track finder"; + + auto l3Current = ic.options().get<float>("l3Current"); + auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); + mTrackFinder.init(l3Current, dipoleCurrent); + + auto moreCandidates = ic.options().get<bool>("moreCandidates"); + mTrackFinder.findMoreTrackCandidates(moreCandidates); + + auto refineTracks = !ic.options().get<bool>("noRefinement"); + mTrackFinder.refineTracks(refineTracks); + + auto debugLevel = ic.options().get<int>("debug"); + mTrackFinder.debug(debugLevel); + + auto stop = [this]() { + mTrackFinder.printStats(); + mTrackFinder.printTimers(); + LOG(INFO) << "tracking duration = " << mElapsedTime.count() << " s"; + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// read the clusters of the current event, find tracks and send them + + // get the input clusters + auto clustersIn = pc.inputs().get<gsl::span<ClusterStruct>>("clusters"); + std::unordered_map<int, std::list<Cluster>> clusters{}; + for (const auto& cluster : clustersIn) { + clusters[cluster.getDEId()].emplace_back(cluster); + } + + // run the track finder + auto tStart = std::chrono::high_resolution_clock::now(); + const auto& tracks = mTrackFinder.findTracks(clusters); + auto tEnd = std::chrono::high_resolution_clock::now(); + mElapsedTime += tEnd - tStart; + + // create the output messages for tracks and attached clusters + auto& mchTracks = pc.outputs().make<std::vector<TrackMCH>>(Output{"MCH", "TRACKS", 0, Lifetime::Timeframe}); + auto& usedClusters = pc.outputs().make<std::vector<ClusterStruct>>(Output{"MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}); + + // write the messages + if (tracks.size() > 0) { + writeTracks(tracks, mchTracks, usedClusters); + } + } + + private: + //_________________________________________________________________________________________________ + void writeTracks(const std::list<Track>& tracks, + std::vector<TrackMCH, o2::pmr::polymorphic_allocator<TrackMCH>>& mchTracks, + std::vector<ClusterStruct, o2::pmr::polymorphic_allocator<ClusterStruct>>& usedClusters) const + { + /// fill the output messages with tracks and attached clusters + + // avoid memory reallocation + mchTracks.reserve(tracks.size()); + usedClusters.reserve(20. * tracks.size()); + + for (const auto& track : tracks) { + + const auto& param = track.first(); + mchTracks.emplace_back(param.getZ(), param.getParameters(), param.getCovariances(), + param.getTrackChi2(), usedClusters.size(), track.getNClusters()); + + for (const auto& param : track) { + usedClusters.emplace_back(param.getClusterPtr()->getClusterStruct()); + } + } + } + + TrackFinder mTrackFinder{}; ///< track finder + std::chrono::duration<double> mElapsedTime{}; ///< timer +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getTrackFinderSpec() +{ + return DataProcessorSpec{ + "TrackFinder", + Inputs{InputSpec{"clusters", "MCH", "CLUSTERS", 0, Lifetime::Timeframe}}, + Outputs{OutputSpec{"MCH", "TRACKS", 0, Lifetime::Timeframe}, + OutputSpec{"MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<TrackFinderTask>()}, + Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, + {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}, + {"moreCandidates", VariantType::Bool, false, {"Find more track candidates"}}, + {"noRefinement", VariantType::Bool, false, {"Disable the track refinement"}}, + {"debug", VariantType::Int, 0, {"debug level"}}}}; +} + +} // namespace mch +} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFinderSpec.h b/Detectors/MUON/MCH/Workflow/src/TrackFinderSpec.h similarity index 100% rename from Detectors/MUON/MCH/Tracking/src/TrackFinderSpec.h rename to Detectors/MUON/MCH/Workflow/src/TrackFinderSpec.h diff --git a/Detectors/MUON/MCH/Workflow/src/TrackFitterSpec.cxx b/Detectors/MUON/MCH/Workflow/src/TrackFitterSpec.cxx new file mode 100644 index 0000000000000..30c23b87daf3f --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/TrackFitterSpec.cxx @@ -0,0 +1,128 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackFitterSpec.cxx +/// \brief Implementation of a data processor to read, refit and send tracks with attached clusters +/// +/// \author Philippe Pillot, Subatech + +#include "TrackFitterSpec.h" + +#include <stdexcept> +#include <list> +#include <vector> + +#include <gsl/span> + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Output.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +#include "DataFormatsMCH/TrackMCH.h" +#include "MCHBase/ClusterBlock.h" +#include "MCHTracking/TrackParam.h" +#include "MCHTracking/Cluster.h" +#include "MCHTracking/Track.h" +#include "MCHTracking/TrackExtrap.h" +#include "MCHTracking/TrackFitter.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class TrackFitterTask +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Prepare the track extrapolation tools + LOG(INFO) << "initializing track fitter"; + auto l3Current = ic.options().get<float>("l3Current"); + auto dipoleCurrent = ic.options().get<float>("dipoleCurrent"); + mTrackFitter.initField(l3Current, dipoleCurrent); + mTrackFitter.smoothTracks(true); + TrackExtrap::useExtrapV2(); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// read the tracks with attached clusters, refit them and send the new version + + // get the input tracks and attached clusters + auto tracksIn = pc.inputs().get<gsl::span<TrackMCH>>("tracks"); + auto clustersIn = pc.inputs().get<gsl::span<ClusterStruct>>("clusters"); + + // create the output messages for refitted tracks and attached clusters + auto& tracksOut = pc.outputs().make<std::vector<TrackMCH>>(Output{"MCH", "TRACKS", 0, Lifetime::Timeframe}); + auto& clustersOut = pc.outputs().make<std::vector<ClusterStruct>>(Output{"MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}); + + // avoid memory reallocation + tracksOut.reserve(tracksIn.size()); + clustersOut.reserve(clustersIn.size()); + + for (const auto& mchTrack : tracksIn) { + + // get the clusters attached to the track + auto trackClusters = clustersIn.subspan(mchTrack.getFirstClusterIdx(), mchTrack.getNClusters()); + + // create the internal track + Track track{}; + std::list<Cluster> clusters{}; + for (const auto& cluster : trackClusters) { + clusters.emplace_back(cluster); + track.createParamAtCluster(clusters.back()); + } + + // refit the track + try { + mTrackFitter.fit(track); + } catch (exception const& e) { + LOG(ERROR) << "Track fit failed: " << e.what(); + continue; + } + + // write the refitted track and attached clusters (same as those of the input track) + const auto& param = track.first(); + tracksOut.emplace_back(param.getZ(), param.getParameters(), param.getCovariances(), + param.getTrackChi2(), clustersOut.size(), track.getNClusters()); + clustersOut.insert(clustersOut.end(), trackClusters.begin(), trackClusters.end()); + } + } + + private: + TrackFitter mTrackFitter{}; ///< track fitter +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getTrackFitterSpec() +{ + return DataProcessorSpec{ + "TrackFitter", + Inputs{InputSpec{"tracks", "MCH", "TRACKSIN", 0, Lifetime::Timeframe}, + InputSpec{"clusters", "MCH", "TRACKCLUSTERSIN", 0, Lifetime::Timeframe}}, + Outputs{OutputSpec{"MCH", "TRACKS", 0, Lifetime::Timeframe}, + OutputSpec{"MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<TrackFitterTask>()}, + Options{{"l3Current", VariantType::Float, -30000.0f, {"L3 current"}}, + {"dipoleCurrent", VariantType::Float, -6000.0f, {"Dipole current"}}}}; +} + +} // namespace mch +} // namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackFitterSpec.h b/Detectors/MUON/MCH/Workflow/src/TrackFitterSpec.h similarity index 100% rename from Detectors/MUON/MCH/Tracking/src/TrackFitterSpec.h rename to Detectors/MUON/MCH/Workflow/src/TrackFitterSpec.h diff --git a/Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.cxx b/Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.cxx new file mode 100644 index 0000000000000..3c46fc36bce87 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.cxx @@ -0,0 +1,139 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackSamplerSpec.cxx +/// \brief Implementation of a data processor to read and send tracks +/// +/// \author Philippe Pillot, Subatech + +#include "TrackSamplerSpec.h" + +#include <iostream> +#include <fstream> +#include <stdexcept> + +#include "Framework/CallbackService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Output.h" +#include "Framework/OutputRef.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +#include "DataFormatsMCH/TrackMCH.h" +#include "MCHBase/ClusterBlock.h" +#include "MCHBase/TrackBlock.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class TrackSamplerTask +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Get the input file from the context + LOG(INFO) << "initializing track sampler"; + + auto inputFileName = ic.options().get<std::string>("infile"); + mInputFile.open(inputFileName, ios::binary); + if (!mInputFile.is_open()) { + throw invalid_argument("Cannot open input file" + inputFileName); + } + + auto stop = [this]() { + /// close the input file + LOG(INFO) << "stop track sampler"; + this->mInputFile.close(); + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// send the tracks with attached clusters of the current event + + // read the number of tracks at vertex, MCH tracks and attached clusters + int nTracksAtVtx(-1); + mInputFile.read(reinterpret_cast<char*>(&nTracksAtVtx), sizeof(int)); + if (mInputFile.fail()) { + pc.services().get<ControlService>().endOfStream(); + return; // probably reached eof + } + int nMCHTracks(-1); + mInputFile.read(reinterpret_cast<char*>(&nMCHTracks), sizeof(int)); + int nClusters(-1); + mInputFile.read(reinterpret_cast<char*>(&nClusters), sizeof(int)); + + if (nTracksAtVtx < 0 || nMCHTracks < 0 || nClusters < 0) { + throw length_error("invalid data input"); + } + if (nMCHTracks > 0 && nClusters == 0) { + throw out_of_range("clusters are missing"); + } + + // create the output messages + auto tracks = pc.outputs().make<TrackMCH>(OutputRef{"tracks"}, nMCHTracks); + auto clusters = pc.outputs().make<ClusterStruct>(OutputRef{"clusters"}, nClusters); + + // skip the tracks at vertex if any + if (nTracksAtVtx > 0) { + mInputFile.seekg(nTracksAtVtx * sizeof(TrackAtVtxStruct), std::ios::cur); + } + + // read the MCH tracks and the attached clusters + if (nMCHTracks > 0) { + mInputFile.read(reinterpret_cast<char*>(tracks.data()), tracks.size_bytes()); + mInputFile.read(reinterpret_cast<char*>(clusters.data()), clusters.size_bytes()); + } + } + + private: + struct TrackAtVtxStruct { + TrackParamStruct paramAtVertex{}; + double dca = 0.; + double rAbs = 0.; + int mchTrackIdx = 0; + }; + + std::ifstream mInputFile{}; ///< input file +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getTrackSamplerSpec(bool forTrackFitter) +{ + Outputs outputs{}; + if (forTrackFitter) { + outputs.emplace_back(OutputLabel{"tracks"}, "MCH", "TRACKSIN", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"clusters"}, "MCH", "TRACKCLUSTERSIN", 0, Lifetime::Timeframe); + } else { + outputs.emplace_back(OutputLabel{"tracks"}, "MCH", "TRACKS", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"clusters"}, "MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "TrackSampler", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<TrackSamplerTask>()}, + Options{{"infile", VariantType::String, "", {"input filename"}}}}; +} + +} // end namespace mch +} // end namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackSamplerSpec.h b/Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.h similarity index 91% rename from Detectors/MUON/MCH/Tracking/src/TrackSamplerSpec.h rename to Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.h index eb4f57d7d35a7..993d514dbeac8 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackSamplerSpec.h +++ b/Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.h @@ -23,7 +23,7 @@ namespace o2 namespace mch { -o2::framework::DataProcessorSpec getTrackSamplerSpec(); +o2::framework::DataProcessorSpec getTrackSamplerSpec(bool forTrackFitter); } // end namespace mch } // end namespace o2 diff --git a/Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.cxx b/Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.cxx new file mode 100644 index 0000000000000..30efb7d8eadc3 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.cxx @@ -0,0 +1,129 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TrackSinkSpec.cxx +/// \brief Implementation of a data processor to print the tracks +/// +/// \author Philippe Pillot, Subatech + +#include "TrackSinkSpec.h" + +#include <iostream> +#include <fstream> +#include <stdexcept> + +#include <gsl/span> + +#include "Framework/CallbackService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +#include "DataFormatsMCH/TrackMCH.h" +#include "MCHBase/ClusterBlock.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class TrackSinkTask +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Get the output file from the context + LOG(INFO) << "initializing track sink"; + + auto outputFileName = ic.options().get<std::string>("outfile"); + mOutputFile.open(outputFileName, ios::out | ios::binary); + if (!mOutputFile.is_open()) { + throw invalid_argument("Cannot open output file" + outputFileName); + } + + auto stop = [this]() { + /// close the output file + LOG(INFO) << "stop track sink"; + this->mOutputFile.close(); + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// dump the tracks with attached clusters of the current event + + // get the input messages + gsl::span<const TrackMCH> tracks{}; + if (pc.inputs().getPos("tracks") >= 0) { + tracks = pc.inputs().get<gsl::span<TrackMCH>>("tracks"); + } + gsl::span<const ClusterStruct> clusters{}; + if (pc.inputs().getPos("clusters") >= 0) { + clusters = pc.inputs().get<gsl::span<ClusterStruct>>("clusters"); + } + gsl::span<const char> tracksAtVtx{}; + if (pc.inputs().getPos("tracksAtVtx") >= 0) { + tracksAtVtx = pc.inputs().get<gsl::span<char>>("tracksAtVtx"); + } + + // write the number of tracks at vertex, MCH tracks and attached clusters + int nTracksAtVtx = tracksAtVtx.empty() ? 0 : *reinterpret_cast<const int*>(tracksAtVtx.data()); + mOutputFile.write(reinterpret_cast<char*>(&nTracksAtVtx), sizeof(int)); + int nTracks = tracks.size(); + mOutputFile.write(reinterpret_cast<char*>(&nTracks), sizeof(int)); + int nClusters = clusters.size(); + mOutputFile.write(reinterpret_cast<char*>(&nClusters), sizeof(int)); + + // write the tracks at vertex, MCH tracks and attached clusters + if (tracksAtVtx.size() > sizeof(int)) { + mOutputFile.write(&tracksAtVtx[sizeof(int)], tracksAtVtx.size() - sizeof(int)); + } + mOutputFile.write(reinterpret_cast<const char*>(tracks.data()), tracks.size_bytes()); + mOutputFile.write(reinterpret_cast<const char*>(clusters.data()), clusters.size_bytes()); + } + + private: + std::ofstream mOutputFile{}; ///< output file +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getTrackSinkSpec(bool mchTracks, bool tracksAtVtx) +{ + Inputs inputs{}; + if (mchTracks) { + inputs.emplace_back("tracks", "MCH", "TRACKS", 0, Lifetime::Timeframe); + inputs.emplace_back("clusters", "MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe); + } + if (tracksAtVtx) { + inputs.emplace_back("tracksAtVtx", "MCH", "TRACKSATVERTEX", 0, Lifetime::Timeframe); + } + if (inputs.empty()) { + throw invalid_argument("nothing to write"); + } + + return DataProcessorSpec{ + "TrackSink", + inputs, + Outputs{}, + AlgorithmSpec{adaptFromTask<TrackSinkTask>()}, + Options{{"outfile", VariantType::String, "AliESDs.out.dat", {"output filename"}}}}; +} + +} // end namespace mch +} // end namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/TrackSinkSpec.h b/Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.h similarity index 86% rename from Detectors/MUON/MCH/Tracking/src/TrackSinkSpec.h rename to Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.h index 13e99835bbdce..64e9ba6af64ac 100644 --- a/Detectors/MUON/MCH/Tracking/src/TrackSinkSpec.h +++ b/Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.h @@ -17,14 +17,13 @@ #define O2_MCH_TRACKSINKSPEC_H_ #include "Framework/DataProcessorSpec.h" -#include "Headers/DataHeader.h" namespace o2 { namespace mch { -o2::framework::DataProcessorSpec getTrackSinkSpec(o2::header::DataDescription description); +o2::framework::DataProcessorSpec getTrackSinkSpec(bool mchTracks, bool tracksAtVtx); } // end namespace mch } // end namespace o2 diff --git a/Detectors/MUON/MCH/Workflow/src/VertexSamplerSpec.cxx b/Detectors/MUON/MCH/Workflow/src/VertexSamplerSpec.cxx new file mode 100644 index 0000000000000..541ad1568f692 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/VertexSamplerSpec.cxx @@ -0,0 +1,117 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VertexSamplerSpec.cxx +/// \brief Implementation of a data processor to read and send vertices +/// +/// \author Philippe Pillot, Subatech + +#include "VertexSamplerSpec.h" + +#include <iostream> +#include <fstream> +#include <string> + +#include <stdexcept> + +#include "Framework/CallbackService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Lifetime.h" +#include "Framework/Output.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +#include "MathUtils/Cartesian.h" + +namespace o2 +{ +namespace mch +{ + +using namespace std; +using namespace o2::framework; + +class VertexSamplerSpec +{ + public: + //_________________________________________________________________________________________________ + void init(framework::InitContext& ic) + { + /// Get the input file from the context + LOG(INFO) << "initializing vertex sampler"; + + auto inputFileName = ic.options().get<std::string>("infile"); + if (!inputFileName.empty()) { + mInputFile.open(inputFileName, ios::binary); + if (!mInputFile.is_open()) { + throw invalid_argument("Cannot open input file" + inputFileName); + } + } + + auto stop = [this]() { + /// close the input file + LOG(INFO) << "stop vertex sampler"; + if (mInputFile.is_open()) { + this->mInputFile.close(); + } + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); + } + + //_________________________________________________________________________________________________ + void run(framework::ProcessingContext& pc) + { + /// send the vertex of the current event if an input file is provided + /// or the default vertex (0.,0.,0.) otherwise + + // read the corresponding vertex or set it to (0,0,0) + math_utils::Point3D<double> vertex(0., 0., 0.); + if (mInputFile.is_open()) { + int event(-1); + mInputFile.read(reinterpret_cast<char*>(&event), sizeof(int)); + if (mInputFile.fail()) { + throw out_of_range("missing vertex"); + } + VertexStruct vtx{}; + mInputFile.read(reinterpret_cast<char*>(&vtx), sizeof(VertexStruct)); + vertex.SetCoordinates(vtx.x, vtx.y, vtx.z); + } + + // create the output message + pc.outputs().snapshot(Output{"MCH", "VERTEX", 0, Lifetime::Timeframe}, vertex); + } + + private: + struct VertexStruct { + double x; + double y; + double z; + }; + + std::ifstream mInputFile{}; ///< input file +}; + +//_________________________________________________________________________________________________ +o2::framework::DataProcessorSpec getVertexSamplerSpec() +{ + return DataProcessorSpec{ + "VertexSampler", + // the input message is just used to synchronize the sending of the vertex with the track reconstruction + Inputs{InputSpec{"tracks", "MCH", "TRACKS", 0, Lifetime::Timeframe}, + InputSpec{"clusters", "MCH", "TRACKCLUSTERS", 0, Lifetime::Timeframe}}, + Outputs{OutputSpec{"MCH", "VERTEX", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<VertexSamplerSpec>()}, + Options{{"infile", VariantType::String, "", {"input filename"}}}}; +} + +} // end namespace mch +} // end namespace o2 diff --git a/Detectors/MUON/MCH/Tracking/src/VertexSamplerSpec.h b/Detectors/MUON/MCH/Workflow/src/VertexSamplerSpec.h similarity index 100% rename from Detectors/MUON/MCH/Tracking/src/VertexSamplerSpec.h rename to Detectors/MUON/MCH/Workflow/src/VertexSamplerSpec.h diff --git a/Detectors/MUON/MCH/Workflow/src/clusters-sampler-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/clusters-sampler-workflow.cxx new file mode 100644 index 0000000000000..ed21436dc0bb9 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/clusters-sampler-workflow.cxx @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 clusters-sampler-workflow.cxx +/// \brief Implementation of a DPL device to send clusters read from a binary file +/// +/// \author Philippe Pillot, Subatech + +#include "Framework/runDataProcessing.h" + +#include "ClusterSamplerSpec.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + return WorkflowSpec{o2::mch::getClusterSamplerSpec()}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/clusters-to-tracks-original-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/clusters-to-tracks-original-workflow.cxx new file mode 100644 index 0000000000000..a5b0e2ab7e65d --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/clusters-to-tracks-original-workflow.cxx @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 clusters-to-tracks-original-workflow.cxx +/// \brief Implementation of a DPL device to run the original track finder algorithm +/// +/// \author Philippe Pillot, Subatech + +#include "Framework/runDataProcessing.h" + +#include "TrackFinderOriginalSpec.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + return WorkflowSpec{o2::mch::getTrackFinderOriginalSpec()}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/clusters-to-tracks-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/clusters-to-tracks-workflow.cxx new file mode 100644 index 0000000000000..b999e23056d41 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/clusters-to-tracks-workflow.cxx @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 clusters-to-tracks-workflow.cxx +/// \brief Implementation of a DPL device to run the track finder algorithm +/// +/// \author Philippe Pillot, Subatech + +#include "Framework/runDataProcessing.h" + +#include "TrackFinderSpec.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + return WorkflowSpec{o2::mch::getTrackFinderSpec()}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/cru-page-reader-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/cru-page-reader-workflow.cxx index c3e58b2fd19e2..ab31062abb47b 100644 --- a/Detectors/MUON/MCH/Workflow/src/cru-page-reader-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/cru-page-reader-workflow.cxx @@ -62,6 +62,7 @@ class FileReaderTask LOG(INFO) << "initializing file reader"; mFrameMax = ic.options().get<int>("nframes"); mPrint = ic.options().get<bool>("print"); + mFullHBF = ic.options().get<bool>("full-hbf"); auto inputFileName = ic.options().get<std::string>("infile"); mInputFile.open(inputFileName, std::ios::binary); @@ -82,80 +83,103 @@ class FileReaderTask { /// send one RDH block via DPL RDH rdh; + char* buf{nullptr}; + size_t bufSize{0}; - // stop if the required number of frames has been reached - if (mFrameMax == 0) { - pc.services().get<ControlService>().endOfStream(); - return; - } + while (true) { - if (mPrint) { - printf("mFrameMax: %d\n", mFrameMax); - } - if (mFrameMax > 0) { - mFrameMax -= 1; - } + // stop if the required number of frames has been reached + if (mFrameMax == 0) { + pc.services().get<ControlService>().endOfStream(); + return; + } - // read the next RDH, stop if no more data is available - mInputFile.read((char*)(&rdh), sizeof(RDH)); - if (mInputFile.fail()) { if (mPrint) { - std::cout << "end of file reached" << std::endl; + printf("mFrameMax: %d\n", mFrameMax); + } + if (mFrameMax > 0) { + mFrameMax -= 1; } - pc.services().get<ControlService>().endOfStream(); - return; // probably reached eof - } - // check that the RDH version is ok (only RDH versions from 4 to 6 are supported at the moment) - auto rdhVersion = o2::raw::RDHUtils::getVersion(rdh); - auto rdhHeaderSize = o2::raw::RDHUtils::getHeaderSize(rdh); - if (mPrint) { - std::cout << "header_version=" << (int)rdhVersion << std::endl; - } - if (rdhVersion < 4 || rdhVersion > 6 || rdhHeaderSize != 64) { - return; - } + // read the next RDH, stop if no more data is available + mInputFile.read((char*)(&rdh), sizeof(RDH)); + if (mInputFile.fail()) { + if (mPrint) { + std::cout << "end of file reached" << std::endl; + } + pc.services().get<ControlService>().endOfStream(); + return; // probably reached eof + } - // get the frame size from the RDH offsetToNext field - auto frameSize = o2::raw::RDHUtils::getOffsetToNext(rdh); - if (mPrint) { - std::cout << "frameSize=" << frameSize << std::endl; - } + // check that the RDH version is ok (only RDH versions from 4 to 6 are supported at the moment) + auto rdhVersion = o2::raw::RDHUtils::getVersion(rdh); + auto rdhHeaderSize = o2::raw::RDHUtils::getHeaderSize(rdh); + if (mPrint) { + std::cout << "header_version=" << (int)rdhVersion << std::endl; + } + if (rdhVersion < 4 || rdhVersion > 6 || rdhHeaderSize != 64) { + return; + } - // stop if the frame size is too small - if (frameSize < rdhHeaderSize) { - std::cout << mFrameMax << " - frameSize too small: " << frameSize << std::endl; - pc.services().get<ControlService>().endOfStream(); - return; - } + // get the frame size from the RDH offsetToNext field + auto frameSize = o2::raw::RDHUtils::getOffsetToNext(rdh); + if (mPrint) { + std::cout << "frameSize=" << frameSize << std::endl; + } - // allocate the output buffer - char* buf = (char*)malloc(frameSize); + // stop if the frame size is too small + if (frameSize < rdhHeaderSize) { + std::cout << mFrameMax << " - frameSize too small: " << frameSize << std::endl; + pc.services().get<ControlService>().endOfStream(); + return; + } - // copy the RDH into the output buffer - memcpy(buf, &rdh, rdhHeaderSize); + // allocate the output buffer + buf = (char*)realloc(buf, bufSize + frameSize); + if (buf == nullptr) { + std::cout << mFrameMax << " - failed to allocate buffer" << std::endl; + pc.services().get<ControlService>().endOfStream(); + return; + } - // read the frame payload into the output buffer - mInputFile.read(buf + rdhHeaderSize, frameSize - rdhHeaderSize); + // copy the RDH into the output buffer + memcpy(buf + bufSize, &rdh, rdhHeaderSize); - // stop if data cannot be read completely - if (mInputFile.fail()) { - if (mPrint) { - std::cout << "end of file reached" << std::endl; + // read the frame payload into the output buffer + mInputFile.read(buf + bufSize + rdhHeaderSize, frameSize - rdhHeaderSize); + + // stop if data cannot be read completely + if (mInputFile.fail()) { + if (mPrint) { + std::cout << "end of file reached" << std::endl; + } + free(buf); + pc.services().get<ControlService>().endOfStream(); + return; // probably reached eof } - free(buf); - pc.services().get<ControlService>().endOfStream(); - return; // probably reached eof - } - // create the output message - auto freefct = [](void* data, void* /*hint*/) { free(data); }; - pc.outputs().adoptChunk(Output{"ROUT", "RAWDATA"}, buf, frameSize, freefct, nullptr); + // increment the total buffer size + bufSize += frameSize; + + auto stopBit = o2::raw::RDHUtils::getStop(rdh); + + // when requesting full HBframes, the output message is sent only when the stop RDH is reached + // otherwise we send one message for each CRU page + if ((stopBit != 0) || (mFullHBF == false)) { + // create the output message + auto freefct = [](void* data, void* /*hint*/) { free(data); }; + pc.outputs().adoptChunk(Output{"ROUT", "RAWDATA"}, buf, bufSize, freefct, nullptr); + + // stop the readout loop + break; + } + } // while (true) } private: std::ifstream mInputFile{}; ///< input file int mFrameMax; ///< number of frames to process + bool mFullHBF; ///< send full HeartBeat frames bool mPrint = false; ///< print debug messages }; @@ -170,6 +194,7 @@ o2::framework::DataProcessorSpec getFileReaderSpec() AlgorithmSpec{adaptFromTask<FileReaderTask>()}, Options{{"infile", VariantType::String, "", {"input file name"}}, {"nframes", VariantType::Int, -1, {"number of frames to process"}}, + {"full-hbf", VariantType::Bool, false, {"send full HeartBeat frames"}}, {"print", VariantType::Bool, false, {"verbose output"}}}}; } // clang-format on diff --git a/Detectors/MUON/MCH/Workflow/src/cru-page-to-digits-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/cru-page-to-digits-workflow.cxx new file mode 100644 index 0000000000000..2c73d33108a41 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/cru-page-to-digits-workflow.cxx @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 cru-page-to-digits-workflow.cxx +/// \author Andrea Ferrero +/// +/// \brief This is an executable that runs the decoding via DPL. +/// +/// This is an executable that takes a raw buffer from the Data Processing Layer, runs the decoding and sends the digits via the Data Processing Layer. +/// The decoder expects an input buffer in the format generated by o2-mch-cru-page-reader-workflow +/// + +#include "Framework/WorkflowSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/CallbackService.h" +#include "Framework/ControlService.h" +#include "Framework/Task.h" +#include "Framework/runDataProcessing.h" +#include "MCHWorkflow/DataDecoderSpec.h" + +using namespace o2; +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + WorkflowSpec specs; + + DataProcessorSpec producer = o2::mch::raw::getDecodingSpec("readout:ROUT/RAWDATA"); + specs.push_back(producer); + + return specs; +} diff --git a/Detectors/MUON/MCH/Workflow/src/digits-sink-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/digits-sink-workflow.cxx index a73d8776d7e7b..9fcd58628ce44 100644 --- a/Detectors/MUON/MCH/Workflow/src/digits-sink-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/digits-sink-workflow.cxx @@ -38,6 +38,7 @@ #include "DPLUtils/DPLRawParser.h" #include "MCHBase/Digit.h" +#include "MCHRawDecoder/OrbitInfo.h" using namespace o2; using namespace o2::framework; @@ -82,8 +83,14 @@ class DigitsSinkTask { // get the input digits auto digits = pc.inputs().get<gsl::span<Digit>>("digits"); + auto orbits = pc.inputs().get<gsl::span<OrbitInfo>>("orbits"); if (mText) { + for (auto o : orbits) { + mOutputFile << std::endl + << " FEEID " << o.getFeeID() << " LINK " << (int)o.getLinkID() << " ORBIT " << o.getOrbit() << std::endl; + } + mOutputFile << "---------------" << std::endl; for (auto d : digits) { mOutputFile << " DE# " << d.getDetID() << " PadId " << d.getPadID() << " ADC " << d.getADC() << " time " << d.getTime().sampaTime << std::endl; } @@ -111,7 +118,7 @@ WorkflowSpec defineDataProcessing(const ConfigContext&) // The producer to generate some data in the workflow DataProcessorSpec producer{ "DigitsSink", - Inputs{InputSpec{"digits", "MCH", "DIGITS", 0, Lifetime::Timeframe}}, + Inputs{InputSpec{"digits", "MCH", "DIGITS", 0, Lifetime::Timeframe}, InputSpec{"orbits", "MCH", "ORBITS", 0, Lifetime::Timeframe}}, Outputs{}, AlgorithmSpec{adaptFromTask<o2::mch::raw::DigitsSinkTask>()}, Options{ { "outfile", VariantType::String, "digits.out", { "output file name" } }, diff --git a/Detectors/MUON/MCH/Workflow/src/file-to-digits-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/file-to-digits-workflow.cxx deleted file mode 100644 index 4406ddb7ca3a6..0000000000000 --- a/Detectors/MUON/MCH/Workflow/src/file-to-digits-workflow.cxx +++ /dev/null @@ -1,324 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 runFileReader.cxx -/// \author Andrea Ferrero -/// -/// \brief This is an executable that reads a data file from disk and sends the data to QC via DPL. -/// -/// This is an executable that reads a data file from disk and sends the data to QC via the Data Processing Layer. -/// It can be used as a data source for QC development. For example, one can do: -/// \code{.sh} -/// o2-qc-run-file-reader --infile=some_data_file | o2-qc --config json://${QUALITYCONTROL_ROOT}/etc/your_config.json -/// \endcode -/// - -#include <random> -#include <iostream> -#include <fstream> -#include <stdexcept> -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/Task.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/runDataProcessing.h" - -#include "DPLUtils/DPLRawParser.h" -#include "MCHBase/Digit.h" -#include "Headers/RAWDataHeader.h" -#include "MCHRawCommon/DataFormats.h" -#include "MCHRawDecoder/PageDecoder.h" -#include "MCHRawElecMap/Mapper.h" -#include "MCHMappingInterface/Segmentation.h" -#include "DetectorsRaw/RDHUtils.h" - -using namespace o2; -using namespace o2::framework; - -struct CRUheader { - uint8_t header_version; - uint8_t header_size; - uint16_t block_length; - uint16_t fee_id; - uint8_t priority_bit; - uint8_t reserved_1; - uint16_t next_packet_offset; - uint16_t memory_size; - uint8_t link_id; - uint8_t packet_counter; - uint16_t cru_id : 12; - uint8_t dpw_id : 4; - uint32_t hb_orbit; - //uint16_t cru_id; - //uint8_t dummy1; - //uint64_t dummy2; -}; - -namespace o2::header -{ -extern std::ostream& operator<<(std::ostream&, const o2::header::RAWDataHeaderV4&); -} - -namespace o2 -{ -namespace mch -{ -namespace raw -{ - -using namespace o2; -using namespace o2::framework; -using namespace o2::mch::mapping; -using RDHv4 = o2::header::RAWDataHeaderV4; - -std::array<int, 64> refManu2ds_st345 = { - 63, 62, 61, 60, 59, 57, 56, 53, 51, 50, 47, 45, 44, 41, 38, 35, - 36, 33, 34, 37, 32, 39, 40, 42, 43, 46, 48, 49, 52, 54, 55, 58, - 7, 8, 5, 2, 6, 1, 3, 0, 4, 9, 10, 15, 17, 18, 22, 25, - 31, 30, 29, 28, 27, 26, 24, 23, 20, 21, 16, 19, 12, 14, 11, 13}; -std::array<int, 64> refDs2manu_st345; - -int manu2ds(int i) -{ - return refManu2ds_st345[i]; -} - -int ds2manu(int i) -{ - return refDs2manu_st345[i]; -} - -class FileReaderTask -{ - void decodeBuffer(gsl::span<const std::byte> page, std::vector<o2::mch::Digit>& digits) - { - size_t ndigits{0}; - - auto channelHandler = [&](DsElecId dsElecId, uint8_t channel, o2::mch::raw::SampaCluster sc) { - auto s = asString(dsElecId); - channel = ds2manu(int(channel)); - if (mPrint) { - auto ch = fmt::format("{}-CH{} samples={}", s, channel, sc.samples.size()); - std::cout << ch << std::endl; - } - double digitadc(0); - //for (auto d = 0; d < sc.nofSamples(); d++) { - for (auto d = 0; d < sc.samples.size(); d++) { - digitadc += sc.samples[d]; - } - - int deId = -1; - int dsIddet = -1; - if (auto opt = Elec2Det(dsElecId); opt.has_value()) { - DsDetId dsDetId = opt.value(); - dsIddet = dsDetId.dsId(); - deId = dsDetId.deId(); - } - if (dsIddet < 0 || deId < 0) { - std::cout << "SOLAR " << (int)dsElecId.solarId() - << " DS " << (int)dsElecId.elinkId() << " (" << (int)dsElecId.elinkGroupId() << "," << (int)dsElecId.elinkIndexInGroup() << ")" - << " CHIP " << ((int)channel) / 32 << " CH " << ((int)channel) % 32 << " ADC " << digitadc << " DE# " << deId << " DSid " << dsIddet << std::endl; - return; - } - - int padId = -1; - try { - const Segmentation& segment = segmentation(deId); - //Segmentation segment(deId); - - padId = segment.findPadByFEE(dsIddet, int(channel)); - if (mPrint) - std::cout << "DS " << (int)dsElecId.elinkId() << " CHIP " << ((int)channel) / 32 << " CH " << ((int)channel) % 32 << " ADC " << digitadc << " DE# " << deId << " DSid " << dsIddet << " PadId " << padId << std::endl; - } catch (const std::exception& e) { - return; - } - - o2::mch::Digit::Time time; - - digits.emplace_back(o2::mch::Digit(deId, padId, digitadc, time, sc.nofSamples())); - //o2::mch::Digit& mchdigit = digits.back(); - //mchdigit.setDetID(deId); - //mchdigit.setPadID(padId); - //mchdigit.setADC(digitadc); - //mchdigit.setTimeStamp(time); - - if (mPrint) - std::cout << "DIGIT STORED:\nADC " << digits.back().getADC() << " DE# " << digits.back().getDetID() << " PadId " << digits.back().getPadID() << " time " << digits.back().getTime().sampaTime << std::endl; - ++ndigits; - }; - - const auto patchPage = [&](gsl::span<const std::byte> rdhBuffer) { - auto rdhPtr = const_cast<void*>(reinterpret_cast<const void*>(rdhBuffer.data())); - nrdhs++; - auto cruId = o2::raw::RDHUtils::getCRUID(rdhPtr); - auto endpoint = o2::raw::RDHUtils::getEndPointID(rdhPtr); - o2::raw::RDHUtils::setFEEID(rdhPtr, cruId * 2 + endpoint); - if (mPrint) { - std::cout << nrdhs << "--\n"; - o2::raw::RDHUtils::printRDH(rdhPtr); - } - }; - - patchPage(page); - if (!decoder.has_value()) - decoder = o2::mch::raw::createPageDecoder(page, channelHandler); - decoder.value()(page); - } - - public: - //_________________________________________________________________________________________________ - void init(framework::InitContext& ic) - { - /// Get the input file and other options from the context - LOG(INFO) << "initializing file reader"; - - for (int i = 0; i < 64; i++) { - for (int j = 0; j < 64; j++) { - if (refManu2ds_st345[j] != i) - continue; - refDs2manu_st345[i] = j; - break; - } - } - - Elec2Det = createElec2DetMapper<ElectronicMapperGenerated>(); - fee2Solar = o2::mch::raw::createFeeLink2SolarMapper<ElectronicMapperGenerated>(); - nrdhs = 0; - - mFrameMax = ic.options().get<int>("frames"); - mPrint = ic.options().get<bool>("print"); - - auto inputFileName = ic.options().get<std::string>("infile"); - mInputFile.open(inputFileName, std::ios::binary); - if (!mInputFile.is_open()) { - throw std::invalid_argument("Cannot open input file \"" + inputFileName + "\""); - } - - auto stop = [this]() { - /// close the input file - LOG(INFO) << "stop file reader"; - this->mInputFile.close(); - }; - ic.services().get<CallbackService>().set(CallbackService::Id::Stop, stop); - } - - //_________________________________________________________________________________________________ - void run(framework::ProcessingContext& pc) - { - std::vector<o2::mch::Digit> digits; - - uint32_t CRUbuf[4 * 4]; - CRUheader CRUh; - /// send one RDH block via DPL - - int RDH_BLOCK_SIZE = 8192; - - if (mFrameMax == 0) - return; - //printf("mFrameMax: %d\n", mFrameMax); - if (mFrameMax > 0) - mFrameMax -= 1; - - mInputFile.read((char*)(&CRUbuf), sizeof(CRUbuf)); - memcpy(&CRUh, CRUbuf, sizeof(CRUheader)); - if (CRUh.header_version != 4 || CRUh.header_size != 64) - return; - - RDH_BLOCK_SIZE = CRUh.next_packet_offset; - - char* buf = (char*)malloc(RDH_BLOCK_SIZE); - memcpy(buf, CRUbuf, CRUh.header_size); - - mInputFile.read(buf + CRUh.header_size, RDH_BLOCK_SIZE - CRUh.header_size); - if (mInputFile.fail()) { - if (mPrint) { - LOG(INFO) << "end of file reached"; - } - free(buf); - pc.services().get<ControlService>().endOfStream(); - return; // probably reached eof - } - - gsl::span<const std::byte> buffer(reinterpret_cast<const std::byte*>(buf), RDH_BLOCK_SIZE); - decodeBuffer(buffer, digits); - - if (mPrint) { - for (auto d : digits) { - std::cout << " DE# " << d.getDetID() << " PadId " << d.getPadID() << " ADC " << d.getADC() << " time " << d.getTime().sampaTime << std::endl; - } - } - - const size_t OUT_SIZE = sizeof(o2::mch::Digit) * digits.size(); - - /// send the output buffer via DPL - char* outbuffer = nullptr; - outbuffer = (char*)realloc(outbuffer, OUT_SIZE); - memcpy(outbuffer, digits.data(), OUT_SIZE); - - // create the output message - auto freefct = [](void* data, void*) { free(data); }; - pc.outputs().adoptChunk(Output{"MCH", "DIGITS", 0}, outbuffer, OUT_SIZE, freefct, nullptr); - } - - private: - std::function<std::optional<DsDetId>(DsElecId)> Elec2Det; - std::function<std::optional<uint16_t>(FeeLinkId id)> fee2Solar; - std::optional<o2::mch::raw::PageDecoder> decoder; - size_t nrdhs{0}; - - std::ifstream mInputFile{}; ///< input file - int mFrameMax; ///< number of frames to process - bool mPrint = false; ///< print digits -}; - -//_________________________________________________________________________________________________ -o2::framework::DataProcessorSpec getFileReaderSpec() -{ - return DataProcessorSpec{ - "FileReader", - Inputs{}, - Outputs{OutputSpec{"MCH", "DIGITS", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<FileReaderTask>()}, - Options{{"infile", VariantType::String, "data.raw", {"input file name"}}}}; -} - -} // end namespace raw -} // end namespace mch -} // end namespace o2 - -using namespace o2; -using namespace o2::framework; - -// clang-format off -WorkflowSpec defineDataProcessing(const ConfigContext&) -{ - WorkflowSpec specs; - - // The producer to generate some data in the workflow - DataProcessorSpec producer{ - "FileReader", - Inputs{}, - Outputs{OutputSpec{"MCH", "DIGITS", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask<o2::mch::raw::FileReaderTask>()}, - Options{ { "infile", VariantType::String, "", { "input file name" } }, - {"print", VariantType::Bool, false, {"print digits"}}, - { "frames", VariantType::Int, -1, { "number of frames to process" } }} - }; - specs.push_back(producer); - - return specs; -} -// clang-format on diff --git a/Detectors/MUON/MCH/Workflow/src/raw-to-digits-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/raw-to-digits-workflow.cxx index efbf0357c50c6..2cad8309540fd 100644 --- a/Detectors/MUON/MCH/Workflow/src/raw-to-digits-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/raw-to-digits-workflow.cxx @@ -15,6 +15,7 @@ /// \brief This is an executable that runs the decoding via DPL. /// /// This is an executable that takes a raw buffer from the Data Processing Layer, runs the decoding and sends the digits via the Data Processing Layer. +/// The decoder expects an input buffer in the format generated by o2-raw-file-reader-workflow /// #include "Framework/WorkflowSpec.h" diff --git a/Detectors/MUON/MCH/Workflow/src/tracks-sampler-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/tracks-sampler-workflow.cxx new file mode 100644 index 0000000000000..1f1c59af61ff9 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/tracks-sampler-workflow.cxx @@ -0,0 +1,37 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tracks-sampler-workflow.cxx +/// \brief Implementation of a DPL device to send tracks and attached clusters read from a binary file +/// +/// \author Philippe Pillot, Subatech + +#include <vector> + +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{"forTrackFitter", VariantType::Bool, false, {"message description as expected by TrackFitterSpec"}}); +} + +#include "Framework/runDataProcessing.h" + +#include "TrackSamplerSpec.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& config) +{ + bool forTrackFitter = config.options().get<bool>("forTrackFitter"); + return WorkflowSpec{o2::mch::getTrackSamplerSpec(forTrackFitter)}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/tracks-sink-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/tracks-sink-workflow.cxx new file mode 100644 index 0000000000000..16e3b9572f39d --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/tracks-sink-workflow.cxx @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tracks-sink-workflow.cxx +/// \brief Implementation of a DPL device to write tracks and attached clusters into a binary file +/// +/// \author Philippe Pillot, Subatech + +#include <vector> + +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{"mchTracksOnly", VariantType::Bool, false, {"only write MCH tracks and attached clusters"}}); + workflowOptions.push_back( + ConfigParamSpec{"tracksAtVertexOnly", VariantType::Bool, false, {"only write track parameters at vertex"}}); +} + +#include "Framework/runDataProcessing.h" + +#include "TrackSinkSpec.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& config) +{ + bool mchTracks = !config.options().get<bool>("tracksAtVertexOnly"); + bool tracksAtVtx = !config.options().get<bool>("mchTracksOnly"); + return WorkflowSpec{o2::mch::getTrackSinkSpec(mchTracks, tracksAtVtx)}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/tracks-to-tracks-at-vertex-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/tracks-to-tracks-at-vertex-workflow.cxx new file mode 100644 index 0000000000000..4f5d55ca857a0 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/tracks-to-tracks-at-vertex-workflow.cxx @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tracks-to-tracks-at-vertex-workflow.cxx +/// \brief Implementation of a DPL device to run the algorithm that extrapolates tracks to the vertex +/// +/// \author Philippe Pillot, Subatech + +#include "Framework/runDataProcessing.h" + +#include "TrackAtVertexSpec.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + return WorkflowSpec{o2::mch::getTrackAtVertexSpec()}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/tracks-to-tracks-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/tracks-to-tracks-workflow.cxx new file mode 100644 index 0000000000000..6f3673cd35f7d --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/tracks-to-tracks-workflow.cxx @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tracks-to-tracks-workflow.cxx +/// \brief Implementation of a DPL device to refit the tracks from the attached clusters +/// +/// \author Philippe Pillot, Subatech + +#include "Framework/runDataProcessing.h" + +#include "TrackFitterSpec.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + return WorkflowSpec{o2::mch::getTrackFitterSpec()}; +} diff --git a/Detectors/MUON/MCH/Workflow/src/vertex-sampler-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/vertex-sampler-workflow.cxx new file mode 100644 index 0000000000000..f43ded9067fc2 --- /dev/null +++ b/Detectors/MUON/MCH/Workflow/src/vertex-sampler-workflow.cxx @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 vertex-sampler-workflow.cxx +/// \brief Implementation of a DPL device to send vertices read from a binary file +/// +/// \author Philippe Pillot, Subatech + +#include "Framework/runDataProcessing.h" + +#include "VertexSamplerSpec.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(const ConfigContext&) +{ + return WorkflowSpec{o2::mch::getVertexSamplerSpec()}; +} diff --git a/Detectors/MUON/MID/Base/include/MIDBase/GeometryTransformer.h b/Detectors/MUON/MID/Base/include/MIDBase/GeometryTransformer.h index de4797f90e7cd..141f936d14da0 100644 --- a/Detectors/MUON/MID/Base/include/MIDBase/GeometryTransformer.h +++ b/Detectors/MUON/MID/Base/include/MIDBase/GeometryTransformer.h @@ -18,7 +18,7 @@ #include <array> #include "MIDBase/DetectorParameters.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" class TGeoManager; @@ -36,44 +36,44 @@ class GeometryTransformer inline const ROOT::Math::Transform3D getMatrix(int deId) { return mTransformations[deId]; } template <typename T> - Point3D<T> localToGlobal(int deId, const Point3D<T>& position) const + math_utils::Point3D<T> localToGlobal(int deId, const math_utils::Point3D<T>& position) const { /// Converts local coordinates into global ones return mTransformations[deId](position); } template <typename T> - Point3D<T> globalToLocal(int deId, const Point3D<T>& position) const + math_utils::Point3D<T> globalToLocal(int deId, const math_utils::Point3D<T>& position) const { /// Converts global coordinates into local ones return mTransformations[deId].ApplyInverse(position); } template <typename T> - Point3D<T> localToGlobal(int deId, T xPos, T yPos) const + math_utils::Point3D<T> localToGlobal(int deId, T xPos, T yPos) const { /// Converts local coordinates into global ones - return localToGlobal(deId, Point3D<T>(xPos, yPos, 0.)); + return localToGlobal(deId, math_utils::Point3D<T>(xPos, yPos, 0.)); } template <typename T> - Point3D<T> globalToLocal(int deId, T xPos, T yPos, T zPos) const + math_utils::Point3D<T> globalToLocal(int deId, T xPos, T yPos, T zPos) const { /// Converts global coordinates into local ones - return globalToLocal(deId, Point3D<T>(xPos, yPos, zPos)); + return globalToLocal(deId, math_utils::Point3D<T>(xPos, yPos, zPos)); } template <typename T> - Vector3D<T> localToGlobal(int deId, const Vector3D<T>& direction) const + math_utils::Vector3D<T> localToGlobal(int deId, const math_utils::Vector3D<T>& direction) const { /// Converts direction in local coordinates into global ones return mTransformations[deId](direction); } template <typename T> - Vector3D<T> globalToLocal(int deId, const Vector3D<T>& direction) const + math_utils::Vector3D<T> globalToLocal(int deId, const math_utils::Vector3D<T>& direction) const { /// Converts direction in global coordinates into a local ones return mTransformations[deId].ApplyInverse(direction); } private: - std::array<o2::Transform3D, detparams::NDetectionElements> mTransformations; ///< Array of transformation matrices + std::array<o2::math_utils::Transform3D, detparams::NDetectionElements> mTransformations; ///< Array of transformation matrices }; ROOT::Math::Transform3D getDefaultChamberTransform(int ichamber); diff --git a/Detectors/MUON/MID/Base/src/GeometryTransformer.cxx b/Detectors/MUON/MID/Base/src/GeometryTransformer.cxx index c02da32a9c7de..a379ec2d646ee 100644 --- a/Detectors/MUON/MID/Base/src/GeometryTransformer.cxx +++ b/Detectors/MUON/MID/Base/src/GeometryTransformer.cxx @@ -98,7 +98,7 @@ GeometryTransformer createTransformationFromManager(const TGeoManager* geoManage if (!navig->cd(volPath.str().c_str())) { throw std::runtime_error("Could not get to volPathName=" + volPath.str()); } - geoTrans.setMatrix(ide, o2::Transform3D{*(navig->GetCurrentMatrix())}); + geoTrans.setMatrix(ide, o2::math_utils::Transform3D{*(navig->GetCurrentMatrix())}); } return geoTrans; } diff --git a/Detectors/MUON/MID/Base/src/MpArea.cxx b/Detectors/MUON/MID/Base/src/MpArea.cxx index 03da62ea81e3e..cc8880339ab15 100644 --- a/Detectors/MUON/MID/Base/src/MpArea.cxx +++ b/Detectors/MUON/MID/Base/src/MpArea.cxx @@ -61,8 +61,9 @@ bool MpArea::isValid() const { /// Check if area is valid for (int icoor = 0; icoor < 2; ++icoor) { - if (mPositions[icoor] == mPositions[icoor + 2]) + if (mPositions[icoor] == mPositions[icoor + 2]) { return false; + } } return true; } diff --git a/Detectors/MUON/MID/CMakeLists.txt b/Detectors/MUON/MID/CMakeLists.txt index 8615904d2a6b8..f24f88c11354c 100644 --- a/Detectors/MUON/MID/CMakeLists.txt +++ b/Detectors/MUON/MID/CMakeLists.txt @@ -10,7 +10,9 @@ add_subdirectory(Base) add_subdirectory(Clustering) +add_subdirectory(QC) add_subdirectory(Raw) +add_subdirectory(CTF) add_subdirectory(Simulation) add_subdirectory(TestingSimTools) add_subdirectory(Tracking) diff --git a/Detectors/MUON/MID/CTF/CMakeLists.txt b/Detectors/MUON/MID/CTF/CMakeLists.txt new file mode 100644 index 0000000000000..2f0fc3b383a68 --- /dev/null +++ b/Detectors/MUON/MID/CTF/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(MIDCTF + SOURCES src/CTFCoder.cxx src/CTFHelper.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsMID + O2::DetectorsBase + O2::CommonDataFormat + O2::DetectorsCommonDataFormats + O2::rANS + ms_gsl::ms_gsl) + diff --git a/Detectors/MUON/MID/CTF/README.md b/Detectors/MUON/MID/CTF/README.md new file mode 100644 index 0000000000000..675a20501ac1b --- /dev/null +++ b/Detectors/MUON/MID/CTF/README.md @@ -0,0 +1,7 @@ +<!-- doxy +\page refMUONMIDCTF MID CTF encoding library +/doxy --> + +# MID CTF +This directory contains the classes to handle entropy encoding of MID +ROFRecord and ColumnData data. diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h new file mode 100644 index 0000000000000..da7a2cd56ba9a --- /dev/null +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of MID column data + +#ifndef O2_MID_CTFCODER_H +#define O2_MID_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include <array> +#include "DataFormatsMID/CTF.h" +#include "DataFormatsMID/ROFRecord.h" +#include "DataFormatsMID/ColumnData.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" +#include "MIDCTF/CTFHelper.h" + +class TTree; + +namespace o2 +{ +namespace mid +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::MID) {} + ~CTFCoder() = default; + + /// entropy-encode data to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const ROFRecord>& rofData, const gsl::span<const ColumnData>& colData); + + /// entropy decode data from buffer with CTF + template <typename VROF, typename VCOL> + void decode(const CTF::base& ec, VROF& rofVec, VCOL& colVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<ROFRecord>& rofVec, std::vector<ColumnData>& colVec); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const ROFRecord>& rofData, const gsl::span<const ColumnData>& colData) +{ + 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_bcIncROF + MD::EENCODE, // BLC_orbitIncROF + MD::EENCODE, // BLC_entriesROF + MD::EENCODE, // BLC_evtypeROF + MD::EENCODE, // BLC_pattern + MD::EENCODE, // BLC_deId + MD::EENCODE // BLC_colId + }; + CTFHelper helper(rofData, colData); + + // book output size with some margin + auto szIni = sizeof(CTFHeader) + helper.getSize() / 4; // will be autoexpanded if needed + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(helper.createHeader()); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODEMID(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODEMID(helper.begin_bcIncROF(), helper.end_bcIncROF(), CTF::BLC_bcIncROF, 0); + ENCODEMID(helper.begin_orbitIncROF(), helper.end_orbitIncROF(), CTF::BLC_orbitIncROF, 0); + ENCODEMID(helper.begin_entriesROF(), helper.end_entriesROF(), CTF::BLC_entriesROF, 0); + ENCODEMID(helper.begin_evtypeROF(), helper.end_evtypeROF(), CTF::BLC_evtypeROF, 0); + + ENCODEMID(helper.begin_pattern(), helper.end_pattern(), CTF::BLC_pattern, 0); + ENCODEMID(helper.begin_deId(), helper.end_deId(), CTF::BLC_deId, 0); + ENCODEMID(helper.begin_colId(), helper.end_colId(), CTF::BLC_colId, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VROF, typename VCOL> +void CTFCoder::decode(const CTF::base& ec, VROF& rofVec, VCOL& colVec) +{ + auto header = ec.getHeader(); + ec.print(getPrefix()); + std::vector<uint16_t> bcInc, entries, pattern; + std::vector<uint32_t> orbitInc; + std::vector<uint8_t> evType, deId, colId; + +#define DECODEMID(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODEMID(bcInc, CTF::BLC_bcIncROF); + DECODEMID(orbitInc, CTF::BLC_orbitIncROF); + DECODEMID(entries, CTF::BLC_entriesROF); + DECODEMID(evType, CTF::BLC_evtypeROF); + + DECODEMID(pattern, CTF::BLC_pattern); + DECODEMID(deId, CTF::BLC_deId); + DECODEMID(colId, CTF::BLC_colId); + // clang-format on + // + rofVec.clear(); + colVec.clear(); + rofVec.reserve(header.nROFs); + colVec.reserve(header.nColumns); + + uint32_t firstEntry = 0, rofCount = 0, colCount = 0, pCount = 0; + o2::InteractionRecord ir(header.firstBC, header.firstOrbit); + + for (uint32_t irof = 0; irof < header.nROFs; irof++) { + // restore ROFRecord + if (orbitInc[irof]) { // non-0 increment => new orbit + ir.bc = bcInc[irof]; // bcInc has absolute meaning + ir.orbit += orbitInc[irof]; + } else { + ir.bc += bcInc[irof]; + } + + firstEntry = colVec.size(); + for (uint8_t ic = 0; ic < entries[irof]; ic++) { + colVec.emplace_back(ColumnData{deId[colCount], colId[colCount], std::array{pattern[pCount], pattern[pCount + 1], pattern[pCount + 2], pattern[pCount + 3], pattern[pCount + 4]}}); + pCount += 5; + colCount++; + } + rofVec.emplace_back(ROFRecord{ir, EventType(evType[irof]), firstEntry, entries[irof]}); + } + assert(colCount == header.nColumns); +} + +} // namespace mid +} // namespace o2 + +#endif // O2_MID_CTFCODER_H diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFHelper.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFHelper.h new file mode 100644 index 0000000000000..832870fa9f5e5 --- /dev/null +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFHelper.h @@ -0,0 +1,196 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.h +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for MID CTF creation + +#ifndef O2_MID_CTF_HELPER_H +#define O2_MID_CTF_HELPER_H + +#include "DataFormatsMID/ROFRecord.h" +#include "DataFormatsMID/ColumnData.h" +#include "DataFormatsMID/CTF.h" +#include <gsl/span> + +namespace o2 +{ +namespace mid +{ + +class CTFHelper +{ + + public: + CTFHelper(const gsl::span<const o2::mid::ROFRecord>& rofData, const gsl::span<const o2::mid::ColumnData>& colData) + : mROFData(rofData), mColData(colData) {} + + CTFHeader createHeader() + { + CTFHeader h{uint32_t(mROFData.size()), uint32_t(mColData.size()), 0, 0}; + if (mROFData.size()) { + h.firstOrbit = mROFData[0].interactionRecord.orbit; + h.firstBC = mROFData[0].interactionRecord.bc; + } + return h; + } + + size_t getSize() const { return mROFData.size() * sizeof(o2::mid::ROFRecord) + mColData.size() * sizeof(o2::mid::ColumnData); } + + //>>> =========================== ITERATORS ======================================== + + template <typename I, typename D, typename T, int M = 1> + class _Iter + { + public: + using difference_type = int64_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; + using iterator_category = std::random_access_iterator_tag; + + _Iter(const gsl::span<const D>& data, bool end = false) : mData(data), mIndex(end ? M * data.size() : 0){}; + _Iter() = default; + + const I& operator++() + { + ++mIndex; + return (I&)(*this); + } + + const I& operator--() + { + mIndex--; + return (I&)(*this); + } + + 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); + } + + 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; } + + protected: + gsl::span<const D> mData{}; + size_t 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_bcIncROF : public _Iter<Iter_bcIncROF, ROFRecord, uint16_t> + { + public: + using _Iter<Iter_bcIncROF, ROFRecord, uint16_t>::_Iter; + value_type operator*() const + { + if (mIndex) { + if (mData[mIndex].interactionRecord.orbit == mData[mIndex - 1].interactionRecord.orbit) { + return mData[mIndex].interactionRecord.bc - mData[mIndex - 1].interactionRecord.bc; + } else { + return mData[mIndex].interactionRecord.bc; + } + } + return 0; + } + }; + + //_______________________________________________ + // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) + class Iter_orbitIncROF : public _Iter<Iter_orbitIncROF, ROFRecord, uint32_t> + { + public: + using _Iter<Iter_orbitIncROF, ROFRecord, uint32_t>::_Iter; + value_type operator*() const { return mIndex ? mData[mIndex].interactionRecord.orbit - mData[mIndex - 1].interactionRecord.orbit : 0; } + }; + + //_______________________________________________ + // Number of entries in the ROF + class Iter_entriesROF : public _Iter<Iter_entriesROF, ROFRecord, uint16_t> + { + public: + using _Iter<Iter_entriesROF, ROFRecord, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].nEntries; } + }; + + //_______________________________________________ + // Event type for the ROF + class Iter_evtypeROF : public _Iter<Iter_evtypeROF, ROFRecord, uint8_t> + { + public: + using _Iter<Iter_evtypeROF, ROFRecord, uint8_t>::_Iter; + value_type operator*() const { return value_type(mData[mIndex].eventType); } + }; + + //_______________________________________________ + class Iter_pattern : public _Iter<Iter_pattern, ColumnData, uint16_t, 5> + { + public: + using _Iter<Iter_pattern, ColumnData, uint16_t, 5>::_Iter; + value_type operator*() const { return mData[mIndex / 5].patterns[mIndex % 5]; } + }; + + //_______________________________________________ + class Iter_deId : public _Iter<Iter_deId, ColumnData, uint8_t> + { + public: + using _Iter<Iter_deId, ColumnData, uint8_t>::_Iter; + value_type operator*() const { return mData[mIndex].deId; } + }; + + //_______________________________________________ + class Iter_colId : public _Iter<Iter_colId, ColumnData, uint8_t> + { + public: + using _Iter<Iter_colId, ColumnData, uint8_t>::_Iter; + value_type operator*() const { return mData[mIndex].columnId; } + }; + + //<<< =========================== ITERATORS ======================================== + + Iter_bcIncROF begin_bcIncROF() const { return Iter_bcIncROF(mROFData, false); } + Iter_bcIncROF end_bcIncROF() const { return Iter_bcIncROF(mROFData, true); } + + Iter_orbitIncROF begin_orbitIncROF() const { return Iter_orbitIncROF(mROFData, false); } + Iter_orbitIncROF end_orbitIncROF() const { return Iter_orbitIncROF(mROFData, true); } + + Iter_entriesROF begin_entriesROF() const { return Iter_entriesROF(mROFData, false); } + Iter_entriesROF end_entriesROF() const { return Iter_entriesROF(mROFData, true); } + + Iter_evtypeROF begin_evtypeROF() const { return Iter_evtypeROF(mROFData, false); } + Iter_evtypeROF end_evtypeROF() const { return Iter_evtypeROF(mROFData, true); } + + Iter_pattern begin_pattern() const { return Iter_pattern(mColData, false); } + Iter_pattern end_pattern() const { return Iter_pattern(mColData, true); } + + Iter_deId begin_deId() const { return Iter_deId(mColData, false); } + Iter_deId end_deId() const { return Iter_deId(mColData, true); } + + Iter_colId begin_colId() const { return Iter_colId(mColData, false); } + Iter_colId end_colId() const { return Iter_colId(mColData, true); } + + private: + const gsl::span<const o2::mid::ROFRecord> mROFData; + const gsl::span<const o2::mid::ColumnData> mColData; +}; + +} // namespace mid +} // namespace o2 + +#endif diff --git a/Detectors/MUON/MID/CTF/src/CTFCoder.cxx b/Detectors/MUON/MID/CTF/src/CTFCoder.cxx new file mode 100644 index 0000000000000..0dc55d6763ae1 --- /dev/null +++ b/Detectors/MUON/MID/CTF/src/CTFCoder.cxx @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of MID data + +#include "MIDCTF/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::mid; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, std::vector<ROFRecord>& rofVec, std::vector<ColumnData>& colVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, rofVec, colVec); +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + // just to get types + uint16_t bcInc = 0, entries = 0, pattern = 0; + uint32_t orbitInc = 0; + uint8_t evType = 0, deId = 0, colId = 0; +#define MAKECODER(part, slot) createCoder<decltype(part)>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(bcInc, CTF::BLC_bcIncROF); + MAKECODER(orbitInc, CTF::BLC_orbitIncROF); + MAKECODER(entries, CTF::BLC_entriesROF); + MAKECODER(evType, CTF::BLC_evtypeROF); + MAKECODER(pattern, CTF::BLC_pattern); + MAKECODER(deId, CTF::BLC_deId); + MAKECODER(colId, CTF::BLC_colId); + // clang-format on +} diff --git a/Detectors/MUON/MID/CTF/src/CTFHelper.cxx b/Detectors/MUON/MID/CTF/src/CTFHelper.cxx new file mode 100644 index 0000000000000..e512ac535fff4 --- /dev/null +++ b/Detectors/MUON/MID/CTF/src/CTFHelper.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for MID CTF creation + +#include "MIDCTF/CTFHelper.h" diff --git a/Detectors/MUON/MID/QC/CMakeLists.txt b/Detectors/MUON/MID/QC/CMakeLists.txt new file mode 100644 index 0000000000000..d7440379f2571 --- /dev/null +++ b/Detectors/MUON/MID/QC/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + MIDQC + SOURCES src/GBTRawDataChecker.cxx src/RawDataChecker.cxx + PUBLIC_LINK_LIBRARIES ms_gsl::ms_gsl O2::MIDRaw) + +add_subdirectory(exe) diff --git a/Detectors/MUON/MID/QC/exe/CMakeLists.txt b/Detectors/MUON/MID/QC/exe/CMakeLists.txt new file mode 100644 index 0000000000000..87be3a2e49bbf --- /dev/null +++ b/Detectors/MUON/MID/QC/exe/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + raw-checker + COMPONENT_NAME mid + SOURCES raw-checker.cxx + PUBLIC_LINK_LIBRARIES O2::MIDQC) diff --git a/Detectors/MUON/MID/QC/exe/README.md b/Detectors/MUON/MID/QC/exe/README.md new file mode 100644 index 0000000000000..05068536f3268 --- /dev/null +++ b/Detectors/MUON/MID/QC/exe/README.md @@ -0,0 +1,46 @@ +<!-- doxy +\page refMUONMIDQCExe MID QC executable +/doxy --> + +# Checkers for MID RAW data +This directory contains executable code to verify the raw data. +This is particularly important for testing and debugging the MID read-out. + +## MID raw file checker +This utility allows to test files produced by the CRU w/o using the User Logic. +Basic usage: +```bash +o2-mid-raw-checker --feeId-config-file feeId_filename filename [filename_2 filename_3 ...] +``` +The `feeId_filename` is a file allowing to tell which feeId is readout by the configured GBT. +The file should be in the form explained [here](../../Raw/README.md) + +The output is a file (or a series of files if a list of inputs is provided), named after the input file as `check_filename.txt`, so that it is easy to understand to which input file the output is referring to. +The different e-links are read out and then grouped according to their interaction record. +The output file contains: +- a list of possible problems (if any) for each event processed +- a summary with the number of faulty events + +The list of problems is provided per interaction record. +This should be unique, but it is not always the case, since it can happen that either the decoded local clock information is wrong, or some reset is performed and the same interaction record is repeated. +The data corresponding to one interaction record can be found in different pages. Notice that this also happens for a perfectly good event in case the information does not fit inside the maximum page size. +For debugging purposes, we try to keep track of the page (HB) where the data corresponding to the interaction record were found. +This is rather accurate, although sometimes the data can be found in the preceding page. +We therefore print the interaction records and the corresponding pages (HB), together with the line in the file where the page start. +The line is counted assuming that one reads the file as a series of 4 words of 32 bits each (this is the typical way the binary file is converted into text during the tests). + +For a list of the other available options: +```bash +o2-mid-raw-checker --help +``` +### Performed checks +The decoded information is read HB per HB (multiple pages are read out when needed). +For each HB, the decoded information are gathered according to their interaction. +Notice that, in principle, events belonging to different HBs should have different interaction records, so one could in principle read the full file and then perform the check. +However, in the tests the RDH is not always correctly set, and the orbit number might not increase. That is why we read the HB one by one, and limit the tests to 1 HB at the time. This makes it easier to tag HBs with issues in the RO. +For each interaction record, a number of checks are performed: +- The number of local cards read must be compatible with the number of non-null cards provided by the regional cards. Notice that a mismatch can occur when the local board is busy (but does not correctly signal it) and we therefore get the regional info but not the corresponding local card. An incompatibility can also appear in case of corrupted data reading. +- The word describing the event (SOX, EOX, HC, Calibration, etc.) should be the same for all cards in the interaction record. +- The number of non-zero patterns read must match the information written in the corresponding word that indicates the non-zero detector planes. +- For each card we check that the information is consistent. For example we cannot have SOX and Calibration bits fired at the same time. Also, during an HC, we expect to have no chamber fired for the local boards. Notice that during tests this information is added by hand, so we should not have any issue by design. +- When the overwritten bit is fired, the readout data is actually filled with the masks. In this case we therefore check that the masks are as expected (i.e. they are compatible with the masks that are transmitted at the SOX) diff --git a/Detectors/MUON/MID/QC/exe/raw-checker.cxx b/Detectors/MUON/MID/QC/exe/raw-checker.cxx new file mode 100644 index 0000000000000..da7adc2ed9e1b --- /dev/null +++ b/Detectors/MUON/MID/QC/exe/raw-checker.cxx @@ -0,0 +1,200 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/QC/exe/raw-checker.cxx +/// \brief Raw data checker for MID +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 09 December 2019 + +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> +#include <vector> +#include "boost/program_options.hpp" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsMID/ROFRecord.h" +#include "MIDQC/RawDataChecker.h" +#include "MIDRaw/CrateMasks.h" +#include "MIDRaw/Decoder.h" +#include "MIDRaw/ElectronicsDelay.h" +#include "MIDRaw/FEEIdConfig.h" +#include "MIDRaw/LocalBoardRO.h" +#include "MIDRaw/RawFileReader.h" + +namespace po = boost::program_options; + +std::string getOutFilename(const char* inFilename, const char* outDir) +{ + std::string basename(inFilename); + std::string fdir = "./"; + auto pos = basename.find_last_of("/"); + if (pos != std::string::npos) { + basename.erase(0, pos + 1); + fdir = inFilename; + fdir.erase(pos); + } + basename.insert(0, "check_"); + basename += ".txt"; + std::string outputDir(outDir); + if (outputDir.empty()) { + outputDir = fdir; + } + if (outputDir.back() != '/') { + outputDir += "/"; + } + std::string outFilename = outputDir + basename; + return outFilename; +} + +int process(po::variables_map& vm) +{ + std::vector<std::string> inputfiles{vm["input"].as<std::vector<std::string>>()}; + + std::unique_ptr<o2::mid::Decoder> decoder{nullptr}; + o2::mid::RawDataChecker checker; + + o2::mid::FEEIdConfig feeIdConfig; + if (vm.count("feeId-config-file")) { + feeIdConfig = o2::mid::FEEIdConfig(vm["feeId-config-file"].as<std::string>().c_str()); + } + + o2::mid::ElectronicsDelay electronicsDelay; + if (vm.count("electronics-delay-file")) { + electronicsDelay = o2::mid::readElectronicsDelay(vm["electronics-delay-file"].as<std::string>().c_str()); + checker.setElectronicsDelay(electronicsDelay); + } + + if (vm.count("sync-trigger")) { + checker.setSyncTrigger(vm["sync-trigger"].as<uint32_t>()); + } + + o2::mid::CrateMasks crateMasks; + if (vm.count("crate-masks-file")) { + crateMasks = o2::mid::CrateMasks(vm["crate-masks-file"].as<std::string>().c_str()); + } + checker.init(crateMasks); + + auto nHBs = vm["nHBs"].as<unsigned long int>(); + auto nMaxErrors = vm["max-errors"].as<unsigned long int>(); + + for (auto& filename : inputfiles) { + o2::mid::RawFileReader rawFileReader; + if (!rawFileReader.init(filename.c_str())) { + return 2; + } + if (vm.count("custom-memory-size")) { + rawFileReader.setCustomPayloadSize(vm["custom-memory-size"].as<uint16_t>()); + } + std::string outFilename = getOutFilename(filename.c_str(), vm["output-dir"].as<std::string>().c_str()); + std::ofstream outFile(outFilename.c_str()); + if (!outFile.is_open()) { + std::cout << "Error: cannot create " << outFilename << std::endl; + return 2; + } + std::cout << "Writing output to: " << outFilename << " ..." << std::endl; + + std::vector<o2::mid::LocalBoardRO> data; + std::vector<o2::mid::ROFRecord> rofRecords; + std::vector<o2::mid::ROFRecord> hbRecords; + + checker.clear(); + unsigned long int iHB = 0; + std::stringstream summary; + while (rawFileReader.readHB(vm.count("only-closed-HBs") > 0)) { + if (!decoder) { + auto const* rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(rawFileReader.getData().data()); + decoder = o2::mid::createDecoder(*rdhPtr, true, electronicsDelay, crateMasks, feeIdConfig); + } + decoder->process(rawFileReader.getData()); + rawFileReader.clear(); + size_t offset = data.size(); + data.insert(data.end(), decoder->getData().begin(), decoder->getData().end()); + for (auto& rof : decoder->getROFRecords()) { + rofRecords.emplace_back(rof.interactionRecord, rof.eventType, rof.firstEntry + offset, rof.nEntries); + } + o2::InteractionRecord hb(0, iHB); + hbRecords.emplace_back(hb, o2::mid::EventType::Noise, offset, decoder->getData().size()); + ++iHB; + if ((nHBs > 0 && iHB >= nHBs)) { + break; + } + if (!checker.process(data, rofRecords, hbRecords)) { + outFile << checker.getDebugMessage() << "\n"; + } + data.clear(); + rofRecords.clear(); + hbRecords.clear(); + + if (checker.getNEventsFaulty() >= nMaxErrors) { + summary << "Too many errors found: abort check!\n"; + break; + } + } + // Check the remaining data + if (data.size() > 0 && !checker.process(data, rofRecords, hbRecords)) { + outFile << checker.getDebugMessage() << "\n"; + } + summary << "Number of busy raised: " << checker.getNBusyRaised() << "\n"; + summary << "Fraction of faulty events: " << checker.getNEventsFaulty() << " / " << checker.getNEventsProcessed() << " = " << static_cast<double>(checker.getNEventsFaulty()) / ((checker.getNEventsProcessed() > 0) ? static_cast<double>(checker.getNEventsProcessed()) : 1.) << "\n"; + outFile << summary.str(); + std::cout << summary.str(); + + outFile.close(); + } + + return 0; +} + +int main(int argc, char* argv[]) +{ + po::variables_map vm; + po::options_description generic("Generic options"); + + // clang-format off + generic.add_options() + ("help", "produce help message") + ("nHBs", po::value<unsigned long int>()->default_value(0),"Number of HBs read") + ("only-closed-HBs", po::value<bool>()->implicit_value(true),"Return only closed HBs") + ("custom-memory-size", po::value<uint16_t>()->implicit_value(0x2000 - 0x100),"Ignore read RDH. Use custom memory size") + ("max-errors", po::value<unsigned long int>()->default_value(10000),"Maximum number of errors before aborting") + ("feeId-config-file", po::value<std::string>(),"Filename with crate FEE ID correspondence") + ("crate-masks-file", po::value<std::string>(),"Filename with crate masks") + ("electronics-delay-file", po::value<std::string>(),"Filename with electronics delay") + ("output-dir", po::value<std::string>()->default_value(""),"Output directory") + ("sync-trigger", po::value<unsigned int>(),"Trigger used for synchronisation (default is orbit 0x1)"); + + + po::options_description hidden("hidden options"); + hidden.add_options() + ("input", po::value<std::vector<std::string>>(),"Input filename"); + // clang-format on + + po::options_description cmdline; + cmdline.add(generic).add(hidden); + + po::positional_options_description pos; + pos.add("input", -1); + + po::store(po::command_line_parser(argc, argv).options(cmdline).positional(pos).run(), vm); + po::notify(vm); + + if (vm.count("help")) { + std::cout << "Usage: " << argv[0] << " <input_raw_filename> [input_raw_filename_1 ...]\n"; + std::cout << generic << std::endl; + return 2; + } + if (vm.count("input") == 0) { + std::cout << "no input file specified" << std::endl; + return 1; + } + + return process(vm); +} diff --git a/Detectors/MUON/MID/QC/include/MIDQC/GBTRawDataChecker.h b/Detectors/MUON/MID/QC/include/MIDQC/GBTRawDataChecker.h new file mode 100644 index 0000000000000..89f131c6106c9 --- /dev/null +++ b/Detectors/MUON/MID/QC/include/MIDQC/GBTRawDataChecker.h @@ -0,0 +1,117 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDQC/GBTRawDataChecker.h +/// \brief Class to check the raw data from a GBT link +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 28 April 2020 +#ifndef O2_MID_GBTRawDataChecker_H +#define O2_MID_GBTRawDataChecker_H + +#include <cstdint> +#include <map> +#include <string> +#include <vector> +#include <unordered_map> +#include <gsl/gsl> +#include "DataFormatsMID/ROFRecord.h" +#include "MIDRaw/ElectronicsDelay.h" +#include "MIDRaw/LocalBoardRO.h" + +namespace o2 +{ +namespace mid +{ +class GBTRawDataChecker +{ + public: + void init(uint16_t feeId, uint8_t mask); + bool process(gsl::span<const LocalBoardRO> localBoards, gsl::span<const ROFRecord> rofRecords, gsl::span<const ROFRecord> pageRecords); + /// Gets the number of processed events + unsigned int getNEventsProcessed() const { return mStatistics[0]; } + /// Gets the number of faulty events + unsigned int getNEventsFaulty() const { return mStatistics[1]; } + /// Gets the number of busy raised + unsigned int getNBusyRaised() const { return mStatistics[2]; } + /// Gets the + std::string getDebugMessage() const { return mDebugMsg; } + void clear(); + + /// Sets the delay in the electronics + void setElectronicsDelay(const ElectronicsDelay& electronicsDelay) { mElectronicsDelay = electronicsDelay; } + + /// Sets the trigger use to verify if all data of an event where received + void setSyncTrigger(uint32_t syncTrigger) { mSyncTrigger = syncTrigger; } + + private: + struct Mask { + std::array<uint16_t, 4> patternsBP{}; /// Bending plane mask + std::array<uint16_t, 4> patternsNBP{}; /// Non-bending plane mask + }; + + struct GBT { + std::vector<LocalBoardRO> regs{}; /// Regional boards + std::vector<LocalBoardRO> locs{}; /// Local boards + std::vector<long int> pages{}; /// Pages information + }; + + struct BoardInfo { + LocalBoardRO board{}; + o2::InteractionRecord interactionRecord{}; + long int page{-1}; + }; + + void clearChecked(bool isTriggered, bool clearTrigEvents); + bool checkEvent(bool isTriggered, const std::vector<LocalBoardRO>& regs, const std::vector<LocalBoardRO>& locs); + bool checkEvents(bool isTriggered); + bool checkConsistency(const LocalBoardRO& board); + bool checkConsistency(const std::vector<LocalBoardRO>& boards); + bool checkMasks(const std::vector<LocalBoardRO>& locs); + bool checkLocalBoardSize(const LocalBoardRO& board); + bool checkLocalBoardSize(const std::vector<LocalBoardRO>& boards); + bool checkRegLocConsistency(const std::vector<LocalBoardRO>& regs, const std::vector<LocalBoardRO>& locs); + uint8_t getElinkId(const LocalBoardRO& board) const; + unsigned int getLastCompleteTrigEvent(); + bool isCompleteSelfTrigEvent(const o2::InteractionRecord& ir) const; + std::string printBoards(const std::vector<LocalBoardRO>& boards) const; + bool runCheckEvents(unsigned int completeMask); + void sortEvents(bool isTriggered); + + std::string mEventDebugMsg{}; /// Debug message for the event + std::string mDebugMsg{}; /// Debug message + std::array<unsigned long int, 3> mStatistics{}; /// Processed events statistics + std::unordered_map<uint8_t, Mask> mMasks; /// Masks + uint8_t mCrateMask{0xFF}; /// Crate mask + uint16_t mFeeId{0}; /// FeeId + uint16_t mResetVal{0}; /// Reset value + ElectronicsDelay mElectronicsDelay{}; /// Delays in the electronics + uint32_t mSyncTrigger{raw::sORB}; /// Trigger for synchronization + + std::map<o2::InteractionRecord, uint16_t> mTrigEvents{}; ///! Index of triggered events + + std::unordered_map<uint8_t, bool> mBusyFlagTrig; /// Busy flag for triggered events + std::unordered_map<uint8_t, bool> mBusyFlagSelfTrig; /// Busy flag for self-triggered events + + std::unordered_map<uint8_t, std::vector<BoardInfo>> mBoardsTrig{}; ///! Boards with triggered events + std::unordered_map<uint8_t, std::vector<BoardInfo>> mBoardsSelfTrig{}; ///! Boards with self-triggered events + + std::map<o2::InteractionRecord, std::vector<std::pair<uint8_t, size_t>>> mOrderedIndexesTrig{}; ///! Ordered indexes for triggered boards + std::map<o2::InteractionRecord, std::vector<std::pair<uint8_t, size_t>>> mOrderedIndexesSelfTrig{}; ///! Ordered indexes for self-triggered boards + + std::unordered_map<uint8_t, long int> mLastIndexTrig{}; ///! Last checked index for triggered boards + std::unordered_map<uint8_t, long int> mLastIndexSelfTrig{}; ///! Last checked index for self-triggered boards + + o2::InteractionRecord mLastCompleteIRTrig{}; ///! Last complete IR for triggered boards + o2::InteractionRecord mLastCompleteIRSelfTrig{}; ///! Last complete IR for self-triggered boards +}; +} // namespace mid +} // namespace o2 + +#endif /* O2_MID_GBTRawDataChecker_H */ diff --git a/Detectors/MUON/MID/QC/include/MIDQC/RawDataChecker.h b/Detectors/MUON/MID/QC/include/MIDQC/RawDataChecker.h new file mode 100644 index 0000000000000..231acfa4b603b --- /dev/null +++ b/Detectors/MUON/MID/QC/include/MIDQC/RawDataChecker.h @@ -0,0 +1,59 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDQC/RawDataChecker.h +/// \brief Class to check the raw data +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 9 December 2019 +#ifndef O2_MID_RAWDATACHECKER_H +#define O2_MID_RAWDATACHECKER_H + +#include <array> +#include <string> +#include <gsl/gsl> +#include "DataFormatsMID/ROFRecord.h" +#include "MIDRaw/CrateMasks.h" +#include "MIDRaw/ElectronicsDelay.h" +#include "MIDRaw/LocalBoardRO.h" +#include "MIDQC/GBTRawDataChecker.h" + +namespace o2 +{ +namespace mid +{ +class RawDataChecker +{ + public: + void init(const CrateMasks& masks); + bool process(gsl::span<const LocalBoardRO> localBoards, gsl::span<const ROFRecord> rofRecords, gsl::span<const ROFRecord> pageRecords); + /// Gets the number of processed events + unsigned int getNEventsProcessed() const; + /// Gets the number of faulty events + unsigned int getNEventsFaulty() const; + /// Gets the number of busy raised + unsigned int getNBusyRaised() const; + /// Gets the debug message + std::string getDebugMessage() const { return mDebugMsg; } + void clear(); + + /// Sets the delay in the electronics + void setElectronicsDelay(const ElectronicsDelay& electronicsDelay) { mElectronicsDelay = electronicsDelay; } + + void setSyncTrigger(uint32_t syncTrigger); + + private: + std::array<GBTRawDataChecker, crateparams::sNGBTs> mCheckers{}; /// GBT raw data checker + std::string mDebugMsg{}; /// Debug message + ElectronicsDelay mElectronicsDelay{}; /// Delays in the electronics +}; +} // namespace mid +} // namespace o2 + +#endif /* O2_MID_RAWDATACHECKER_H */ diff --git a/Detectors/MUON/MID/QC/src/GBTRawDataChecker.cxx b/Detectors/MUON/MID/QC/src/GBTRawDataChecker.cxx new file mode 100644 index 0000000000000..6626df2e11aaf --- /dev/null +++ b/Detectors/MUON/MID/QC/src/GBTRawDataChecker.cxx @@ -0,0 +1,522 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/QC/src/GBTRawDataChecker.cxx +/// \brief Class to check the raw data from a GBT link +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 28 April 2020 + +#include "MIDQC/GBTRawDataChecker.h" + +#include <sstream> +#include <fmt/format.h> +#include "MIDRaw/CrateParameters.h" + +namespace o2 +{ +namespace mid +{ + +void GBTRawDataChecker::init(uint16_t feeId, uint8_t mask) +{ + /// Initializer + mFeeId = feeId; + mCrateMask = mask; +} + +bool GBTRawDataChecker::checkLocalBoardSize(const LocalBoardRO& board) +{ + /// Checks that the board has the expected non-null patterns + + // This test only make sense when we have a self-trigger, + // since in this case we expect to have a variable number of non-zero pattern + // as indicated by the corresponding word. + for (int ich = 0; ich < 4; ++ich) { + bool isExpectedNull = (((board.firedChambers >> ich) & 0x1) == 0); + bool isNull = (board.patternsBP[ich] == 0 && board.patternsNBP[ich] == 0); + if (isExpectedNull != isNull) { + std::stringstream ss; + ss << "wrong size for local board:\n"; + ss << board << "\n"; + mEventDebugMsg += ss.str(); + return false; + } + } + return true; +} + +bool GBTRawDataChecker::checkLocalBoardSize(const std::vector<LocalBoardRO>& boards) +{ + /// Checks that the boards have the expected non-null patterns + for (auto& board : boards) { + if (!checkLocalBoardSize(board)) { + return false; + } + } + return true; +} + +bool GBTRawDataChecker::checkConsistency(const LocalBoardRO& board) +{ + /// Checks that the event information is consistent + + bool isSoxOrReset = board.triggerWord & (raw::sSOX | raw::sEOX | raw::sRESET); + bool isCalib = raw::isCalibration(board.triggerWord); + bool isPhys = board.triggerWord & raw::sPHY; + + if (isPhys) { + if (isCalib) { + mEventDebugMsg += "inconsistent trigger: calibration and physics trigger cannot be fired together\n"; + return false; + } + if (raw::isLoc(board.statusWord)) { + if (board.firedChambers) { + mEventDebugMsg += "inconsistent trigger: fired chambers should be 0\n"; + return false; + } + } + } + if (isSoxOrReset && (isCalib || isPhys)) { + mEventDebugMsg += "inconsistent trigger: cannot be SOX and calibration\n"; + return false; + } + + return true; +} + +bool GBTRawDataChecker::checkConsistency(const std::vector<LocalBoardRO>& boards) +{ + /// Checks that the event information is consistent + for (auto& board : boards) { + if (!checkConsistency(board)) { + std::stringstream ss; + ss << board << "\n"; + mEventDebugMsg += ss.str(); + return false; + } + } + return true; +} + +bool GBTRawDataChecker::checkMasks(const std::vector<LocalBoardRO>& locs) +{ + /// Checks the masks + for (auto loc : locs) { + // The board patterns coincide with the masks ("overwritten" mode) + if (loc.statusWord & raw::sOVERWRITTEN) { + auto maskItem = mMasks.find(loc.boardId); + for (int ich = 0; ich < 4; ++ich) { + uint16_t maskBP = 0; + uint16_t maskNBP = 0; + if (maskItem != mMasks.end()) { + maskBP = maskItem->second.patternsBP[ich]; + maskNBP = maskItem->second.patternsNBP[ich]; + } + if (maskBP != loc.patternsBP[ich] || maskNBP != loc.patternsNBP[ich]) { + std::stringstream ss; + ss << "Pattern is not compatible with mask for:\n"; + ss << loc << "\n"; + mEventDebugMsg += ss.str(); + return false; + } + } + } + } + return true; +} + +bool GBTRawDataChecker::checkRegLocConsistency(const std::vector<LocalBoardRO>& regs, const std::vector<LocalBoardRO>& locs) +{ + /// Checks consistency between local and regional info + uint8_t regFired{0}; + for (auto& reg : regs) { + uint8_t ireg = crateparams::getLocId(reg.boardId) % 2; + auto busyItem = mBusyFlagSelfTrig.find(8 + ireg); + if (reg.triggerWord == 0) { + // Self-triggered event: check the decision + regFired |= (reg.firedChambers << (4 * ireg)); + } else { + // Triggered event: all active boards must answer + regFired |= (mCrateMask & (0xF << (4 * ireg))); + } + } + uint8_t locFired{0}, locBusy{0}; + for (auto& loc : locs) { + auto linkId = getElinkId(loc); + uint8_t mask = (1 << linkId); + if (loc.triggerWord == 0) { + // Self-triggered event: check the decision + if (loc.firedChambers) { + locFired |= mask; + } + } else { + // Triggered event: all active boards must answer + locFired |= mask; + } + } + + // The XOR returns 1 in case of a difference + uint8_t problems = (regFired ^ locFired); + + if (problems) { + // It can be that a busy signal was raised by one of the board in previous events + // If the board is still busy it will not answer. + uint8_t busy{0}; + for (uint8_t iboard = 0; iboard < crateparams::sNELinksPerGBT; ++iboard) { + auto busyItem = mBusyFlagSelfTrig.find(iboard); + if (busyItem != mBusyFlagSelfTrig.end() && busyItem->second) { + busy |= (iboard < crateparams::sMaxNBoardsInLink) ? (1 << iboard) : (0xF << (4 * (iboard % 2))); + } + } + + if (problems & ~busy) { + std::stringstream ss; + ss << fmt::format("loc-reg inconsistency: fired locals ({:08b}) != expected from reg ({:08b});\n", locFired, regFired); + ss << printBoards(regs); + ss << printBoards(locs); + mEventDebugMsg += ss.str(); + return false; + } + } + return true; +} + +std::string GBTRawDataChecker::printBoards(const std::vector<LocalBoardRO>& boards) const +{ + /// Prints the boards + std::stringstream ss; + for (auto& board : boards) { + ss << board << "\n"; + } + return ss.str(); +} + +bool GBTRawDataChecker::checkEvent(bool isTriggered, const std::vector<LocalBoardRO>& regs, const std::vector<LocalBoardRO>& locs) +{ + /// Checks the cards belonging to the same BC + mEventDebugMsg.clear(); + if (!checkRegLocConsistency(regs, locs)) { + return false; + } + + if (!checkConsistency(regs) || !checkConsistency(locs)) { + return false; + } + + if (!isTriggered) { + if (!checkLocalBoardSize(locs)) { + return false; + } + + if (!checkMasks(locs)) { + return false; + } + } + + return true; +} + +uint8_t GBTRawDataChecker::getElinkId(const LocalBoardRO& board) const +{ + /// Returns the e-link ID + if (raw::isLoc(board.statusWord)) { + return board.boardId % 8; + } + return 8 + board.boardId % 8; +} + +void GBTRawDataChecker::clearChecked(bool isTriggered, bool clearTrigEvents) +{ + /// Clears the checked events + + auto& boards = isTriggered ? mBoardsTrig : mBoardsSelfTrig; + auto& lastIndexes = isTriggered ? mLastIndexTrig : mLastIndexSelfTrig; + // Create a new board map with the checked events stripped + std::unordered_map<uint8_t, std::vector<BoardInfo>> newBoards{}; + for (auto& lastIdxItem : lastIndexes) { + auto firstIdx = lastIdxItem.second + 1; + auto& boardVec = boards[lastIdxItem.first]; + if (firstIdx < boardVec.size()) { + auto& newVec = newBoards[lastIdxItem.first]; + newVec.insert(newVec.end(), boardVec.begin() + firstIdx, boardVec.end()); + } + } + boards.swap(newBoards); + + if (clearTrigEvents) { + // Clears the map with the processed triggers + auto& lastCompleteTrigIR = isTriggered ? mLastCompleteIRTrig : mLastCompleteIRSelfTrig; + auto low = mTrigEvents.begin(); + auto up = mTrigEvents.upper_bound(lastCompleteTrigIR); + mTrigEvents.erase(low, up); + } +} + +bool GBTRawDataChecker::isCompleteSelfTrigEvent(const o2::InteractionRecord& ir) const +{ + /// Checks if the self-triggered events are complete + + // The regional board information in self-triggered events is delayed + // compared to triggered events. + // So, we expect information from a previous orbit after having received an orbit trigger. + // Let us check that we have all boards with the same orbit + + bool isIncluded = false; + for (uint8_t ireg = 8; ireg < 10; ++ireg) { + auto item = mBoardsSelfTrig.find(ireg); + if (item != mBoardsSelfTrig.end()) { + if (item->second.back().interactionRecord.orbit == ir.orbit) { + return false; + } + if (item->second.front().interactionRecord.orbit <= ir.orbit) { + isIncluded = true; + } + } + } + return isIncluded; +} + +unsigned int GBTRawDataChecker::getLastCompleteTrigEvent() +{ + /// Checks if we have a triggered event with the information from all active boards + /// The function returns true if it finds a complete event + /// and it returns its interaction record as well. + + // The information for an event comes at different times for different boards, + // depending on the length of the self-triggered event for that board. + // So, before testing the consistency of the event, + // we must wait to have received the information from all boards. + // This can be checked in triggered events, since all boards should be present. + + unsigned int completeMask = 0; + + // Check if we have a triggered event with the information from all active boards + mLastCompleteIRSelfTrig = o2::InteractionRecord(); + uint16_t fullMask = (3 << 8) | mCrateMask; + auto trigEventIt = mTrigEvents.rbegin(); + auto end = mTrigEvents.rend(); + for (; trigEventIt != end; ++trigEventIt) { + if ((trigEventIt->second & fullMask) == fullMask) { + // The trigger events contain the unprocessed events for both triggered and self-triggered events + // These might not be synchronized (typically the latest complete self-triggered events lie behind) + // If the latest IR in memory is more recent than the current complete event found, + // then it means that we need to wait for more HBs. + if (mLastCompleteIRTrig.isDummy() || mLastCompleteIRTrig < trigEventIt->first) { + completeMask |= 1; + mLastCompleteIRTrig = trigEventIt->first; + } + auto trIt = trigEventIt; + while (trIt != end) { + if (isCompleteSelfTrigEvent(trIt->first)) { + completeMask |= (1 << 1); + mLastCompleteIRSelfTrig = trIt->first; + break; + } + ++trIt; + } + + return completeMask; + } + } + + return completeMask; +} + +bool GBTRawDataChecker::runCheckEvents(unsigned int completeMask) +{ + /// Runs the checker if needed + + bool isOk = true; + + if (completeMask & 0x1) { + sortEvents(true); + isOk &= checkEvents(true); + clearChecked(true, mBoardsSelfTrig.empty()); + } + + if (completeMask & 0x2) { + sortEvents(false); + isOk &= checkEvents(false); + clearChecked(false, true); + } + + return isOk; +} + +void GBTRawDataChecker::sortEvents(bool isTriggered) +{ + /// Sorts the event in time + auto& orderedIndexes = isTriggered ? mOrderedIndexesTrig : mOrderedIndexesSelfTrig; + auto& lastIndexes = isTriggered ? mLastIndexTrig : mLastIndexSelfTrig; + auto& boards = isTriggered ? mBoardsTrig : mBoardsSelfTrig; + auto& lastCompleteTrigEventIR = isTriggered ? mLastCompleteIRTrig : mLastCompleteIRSelfTrig; + orderedIndexes.clear(); + lastIndexes.clear(); + for (auto& boardItem : boards) { + long int lastIdx = -1; + for (auto boardIt = boardItem.second.begin(), end = boardItem.second.end(); boardIt != end; ++boardIt) { + if (boardIt->interactionRecord > lastCompleteTrigEventIR) { + break; + } + lastIdx = std::distance(boardItem.second.begin(), boardIt); + orderedIndexes[boardIt->interactionRecord].emplace_back(boardItem.first, lastIdx); + } + lastIndexes[boardItem.first] = lastIdx; + } +} + +bool GBTRawDataChecker::checkEvents(bool isTriggered) +{ + /// Checks the events + bool isOk = true; + auto& boards = isTriggered ? mBoardsTrig : mBoardsSelfTrig; + auto& orderedIndexes = isTriggered ? mOrderedIndexesTrig : mOrderedIndexesSelfTrig; + auto& busyFlag = isTriggered ? mBusyFlagTrig : mBusyFlagSelfTrig; + // Loop on the event indexes + for (auto& evtIdxItem : orderedIndexes) { + // All of these boards have the same timestamp + GBT gbtEvent; + bool busyRaised = false; + for (auto& evtPair : evtIdxItem.second) { + auto& boardInfo = boards[evtPair.first][evtPair.second]; + uint8_t triggerId = boardInfo.board.triggerWord; + auto elinkId = getElinkId(boardInfo.board); + + bool isBusy = ((boardInfo.board.statusWord & raw::sREJECTING) != 0); + busyRaised |= isBusy; + busyFlag[elinkId] = isBusy; + if (isBusy && !isTriggered) { + // This is a special event that just signals a busy. + // Do not add the board to the events to be tested. + // Even because this event can have the same IR and triggerWord (0) of a self-triggered event + continue; + } + if (raw::isLoc(boardInfo.board.statusWord)) { + gbtEvent.locs.push_back(boardInfo.board); + } else { + gbtEvent.regs.push_back(boardInfo.board); + } + if (boardInfo.page >= 0) { + if (std::find(gbtEvent.pages.begin(), gbtEvent.pages.end(), boardInfo.page) == gbtEvent.pages.end()) { + gbtEvent.pages.push_back(boardInfo.page); + } + } + } + if (busyRaised && !isTriggered) { + ++mStatistics[2]; + } + ++mStatistics[0]; + if (!checkEvent(isTriggered, gbtEvent.regs, gbtEvent.locs)) { + std::stringstream ss; + ss << fmt::format("BCid: 0x{:x} Orbit: 0x{:x}", evtIdxItem.first.bc, evtIdxItem.first.orbit); + if (!gbtEvent.pages.empty()) { + ss << " [in"; + for (auto& page : gbtEvent.pages) { + ss << std::dec << " page: " << page << " (line: " << 512 * page + 1 << ") "; + } + ss << "]"; + } + ss << "\n"; + isOk = false; + ss << mEventDebugMsg << "\n"; + mDebugMsg += ss.str(); + ++mStatistics[1]; + } + } + + return isOk; +} + +bool GBTRawDataChecker::process(gsl::span<const LocalBoardRO> localBoards, gsl::span<const ROFRecord> rofRecords, gsl::span<const ROFRecord> pageRecords) +{ + /// Checks the raw data + mDebugMsg.clear(); + + // Fill board information + for (auto rofIt = rofRecords.begin(); rofIt != rofRecords.end(); ++rofIt) { + if (rofIt->interactionRecord.orbit == 0xffffffff) { + // Protection for event with orbit 0 + continue; + } + for (auto locIt = localBoards.begin() + rofIt->firstEntry; locIt != localBoards.begin() + rofIt->firstEntry + rofIt->nEntries; ++locIt) { + // Find what page this event corresponds to. + // This is useful for debugging. + long int page = -1; + for (auto& rofPage : pageRecords) { + if (rofIt->firstEntry >= rofPage.firstEntry && rofIt->firstEntry < rofPage.firstEntry + rofPage.nEntries) { + page = rofPage.interactionRecord.orbit; + break; + } + } + + // Store the information per local board. + // The information should be already ordered in time + auto id = getElinkId(*locIt); + auto& elinkVec = (locIt->triggerWord == 0) ? mBoardsSelfTrig[id] : mBoardsTrig[id]; + elinkVec.push_back({*locIt, rofIt->interactionRecord, page}); + + if (locIt->triggerWord == 0) { + continue; + } + + // Keep track of the busy + if (locIt->statusWord & raw::sREJECTING) { + auto& selfVec = mBoardsSelfTrig[id]; + auto board = *locIt; + board.triggerWord = 0; + auto ir = rofIt->interactionRecord; + if (id >= crateparams::sMaxNBoardsInLink) { + uint16_t delayRegLocal = mElectronicsDelay.regToLocal; + if (rofIt->interactionRecord.bc < delayRegLocal) { + ir -= (constants::lhc::LHCMaxBunches - mResetVal - 1); + } + ir -= delayRegLocal; + } + selfVec.push_back({*locIt, ir, page}); + } + + // Keep track of the trigger chosen for synchronisation + if (locIt->triggerWord & mSyncTrigger) { + mTrigEvents[rofIt->interactionRecord] |= (1 << id); + mResetVal = rofIt->interactionRecord.bc; + } + + // Compute the masks + if (locIt->triggerWord & raw::sSOX) { + if (raw::isLoc(locIt->statusWord)) { + auto maskItem = mMasks.find(locIt->boardId); + // Check if we have already a mask for this + if (maskItem == mMasks.end()) { + // If not, read the map + auto& mask = mMasks[locIt->boardId]; + for (int ich = 0; ich < 4; ++ich) { + mask.patternsBP[ich] = locIt->patternsBP[ich]; + mask.patternsNBP[ich] = locIt->patternsNBP[ich]; + } + } + } + } + } // loop on local boards + } // loop on ROF records + + return runCheckEvents(getLastCompleteTrigEvent()); +} + +void GBTRawDataChecker::clear() +{ + /// Resets the masks and flags + mMasks.clear(); + mBusyFlagTrig.clear(); + mBusyFlagSelfTrig.clear(); + mStatistics.fill(0); +} + +} // namespace mid +} // namespace o2 diff --git a/Detectors/MUON/MID/QC/src/RawDataChecker.cxx b/Detectors/MUON/MID/QC/src/RawDataChecker.cxx new file mode 100644 index 0000000000000..96d8dba849748 --- /dev/null +++ b/Detectors/MUON/MID/QC/src/RawDataChecker.cxx @@ -0,0 +1,105 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/QC/src/RawDataChecker.cxx +/// \brief Class to check the raw data +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 9 December 2019 + +#include "MIDQC/RawDataChecker.h" + +#include <unordered_map> +#include "MIDRaw/CrateParameters.h" + +namespace o2 +{ +namespace mid +{ + +void RawDataChecker::init(const CrateMasks& crateMasks) +{ + /// Initializes the checkers + for (uint16_t igbt = 0; igbt < crateparams::sNGBTs; ++igbt) { + mCheckers[igbt].setElectronicsDelay(mElectronicsDelay); + mCheckers[igbt].init(igbt, crateMasks.getMask(igbt)); + } +} + +bool RawDataChecker::process(gsl::span<const LocalBoardRO> localBoards, gsl::span<const ROFRecord> rofRecords, gsl::span<const ROFRecord> pageRecords) +{ + /// Checks the raw data + + bool isOk = true; + mDebugMsg.clear(); + std::unordered_map<uint16_t, std::vector<ROFRecord>> rofs; + for (auto& rof : rofRecords) { + auto& loc = localBoards[rof.firstEntry]; + auto crateId = crateparams::getCrateId(loc.boardId); + auto linkId = crateparams::getGBTIdFromBoardInCrate(crateparams::getLocId(loc.boardId)); + auto feeId = crateparams::makeROId(crateId, linkId); + rofs[feeId].emplace_back(rof); + } + + for (auto& item : rofs) { + isOk &= mCheckers[item.first].process(localBoards, item.second, pageRecords); + mDebugMsg += mCheckers[item.first].getDebugMessage(); + } + + return isOk; +} + +void RawDataChecker::setSyncTrigger(uint32_t syncTrigger) +{ + /// Sets the trigger use to verify if all data of an event where received + for (auto& checker : mCheckers) { + checker.setSyncTrigger(syncTrigger); + } +} + +unsigned int RawDataChecker::getNEventsProcessed() const +{ + /// Gets the number of processed events + unsigned int sum = 0; + for (auto& checker : mCheckers) { + sum += checker.getNEventsProcessed(); + } + return sum; +} + +unsigned int RawDataChecker::getNEventsFaulty() const +{ + /// Gets the number of faulty events + unsigned int sum = 0; + for (auto& checker : mCheckers) { + sum += checker.getNEventsFaulty(); + } + return sum; +} + +unsigned int RawDataChecker::getNBusyRaised() const +{ + /// Gets the number of busy raised + unsigned int sum = 0; + for (auto& checker : mCheckers) { + sum += checker.getNBusyRaised(); + } + return sum; +} + +void RawDataChecker::clear() +{ + /// Clears the statistics + for (auto& checker : mCheckers) { + checker.clear(); + } +} + +} // namespace mid +} // namespace o2 diff --git a/Detectors/MUON/MID/README.md b/Detectors/MUON/MID/README.md index 07a0b28aaefa1..af0b8e510c19d 100644 --- a/Detectors/MUON/MID/README.md +++ b/Detectors/MUON/MID/README.md @@ -8,8 +8,10 @@ This is a top page for the MID detector documentation. <!-- doxy * \subpage refMUONMIDClustering +* \subpage refMUONMIDQCExe * \subpage refMUONMIDRaw * \subpage refMUONMIDRawExe * \subpage refMUONMIDTracking * \subpage refMUONMIDWorkflow +* \subpage refMUONMIDCTF /doxy --> diff --git a/Detectors/MUON/MID/Raw/CMakeLists.txt b/Detectors/MUON/MID/Raw/CMakeLists.txt index cfa1931853db8..93ca140fbaa52 100644 --- a/Detectors/MUON/MID/Raw/CMakeLists.txt +++ b/Detectors/MUON/MID/Raw/CMakeLists.txt @@ -13,17 +13,15 @@ o2_add_library( SOURCES src/ColumnDataToLocalBoard.cxx src/CrateMapper.cxx src/CrateMasks.cxx - src/CRUBareDecoder.cxx src/Decoder.cxx + src/ElectronicsDelay.cxx src/ELinkDecoder.cxx src/Encoder.cxx src/FEEIdConfig.cxx - src/GBTBareDecoder.cxx + src/GBTDecoder.cxx src/GBTOutputHandler.cxx - src/GBTUserLogicDecoder.cxx src/GBTUserLogicEncoder.cxx src/LocalBoardRO.cxx - src/RawBuffer.cxx src/DecodedDataAggregator.cxx src/RawFileReader.cxx PUBLIC_LINK_LIBRARIES diff --git a/Detectors/MUON/MID/Raw/exe/CMakeLists.txt b/Detectors/MUON/MID/Raw/exe/CMakeLists.txt index c1255b85b24eb..cd2f0dabeda44 100644 --- a/Detectors/MUON/MID/Raw/exe/CMakeLists.txt +++ b/Detectors/MUON/MID/Raw/exe/CMakeLists.txt @@ -11,11 +11,5 @@ o2_add_executable( rawdump COMPONENT_NAME mid - SOURCES mid-rawdump.cxx - PUBLIC_LINK_LIBRARIES O2::MIDRaw) - -o2_add_executable( - raw-checker - COMPONENT_NAME mid - SOURCES mid-raw-checker.cxx CRUBareDataChecker.cxx + SOURCES rawdump.cxx PUBLIC_LINK_LIBRARIES O2::MIDRaw) diff --git a/Detectors/MUON/MID/Raw/exe/CRUBareDataChecker.cxx b/Detectors/MUON/MID/Raw/exe/CRUBareDataChecker.cxx deleted file mode 100644 index 77590341b759f..0000000000000 --- a/Detectors/MUON/MID/Raw/exe/CRUBareDataChecker.cxx +++ /dev/null @@ -1,199 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 CRUBareDataChecker.cxx -/// \brief Class to check the bare data from the CRU -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 9 December 2019 - -#include "CRUBareDataChecker.h" - -#include <array> -#include <sstream> - -namespace o2 -{ -namespace mid -{ - -bool CRUBareDataChecker::checkSameEventWord(const std::vector<LocalBoardRO>& boards, uint8_t refEventWord) const -{ - /// Checks the event word - for (auto loc : boards) { - if (loc.triggerWord != refEventWord) { - return false; - } - } - return true; -} - -bool CRUBareDataChecker::checkPatterns(const LocalBoardRO& board, uint8_t expected) const -{ - /// Checks that the board has the expected non-null patterns - uint8_t inputs = (expected > 0xF) ? board.firedChambers : expected; - for (int ich = 0; ich < 4; ++ich) { - bool isExpectedNull = (((inputs >> ich) & 0x1) == 0); - bool isNull = (board.patternsBP[ich] == 0 && board.patternsNBP[ich] == 0); - if (isExpectedNull != isNull) { - return false; - } - } - return true; -} - -bool CRUBareDataChecker::checkPatterns(const std::vector<LocalBoardRO>& boards, uint8_t expected) const -{ - /// Checks that the boards have the expected non-null patterns - for (auto& board : boards) { - if (!checkPatterns(board, expected)) { - return false; - } - } - return true; -} - -bool CRUBareDataChecker::checkConsistency(const LocalBoardRO& board) const -{ - /// Checks that the event information is consistent - - bool isSoxOrReset = board.triggerWord & 0xc2; - bool isCalib = raw::isCalibration(board.triggerWord); - bool isPhysOrHC = board.triggerWord & 0x5; - - if (isPhysOrHC) { - if (isCalib) { - return false; - } - if (raw::isLoc(board.statusWord)) { - if (board.firedChambers) { - return false; - } - } - } - if (isSoxOrReset && (isCalib || isPhysOrHC)) { - return false; - } - - return true; -} - -bool CRUBareDataChecker::checkConsistency(const std::vector<LocalBoardRO>& boards) const -{ - /// Checks that the event information is consistent - for (auto& board : boards) { - if (!checkConsistency(board)) { - return false; - } - } - return true; -} - -bool CRUBareDataChecker::checkBC(const std::vector<LocalBoardRO>& regs, const std::vector<LocalBoardRO>& locs, std::string& debugMsg) -{ - /// Checks the cards belonging to the same BC - bool isOk = true; - - if (locs.size() != 4 * regs.size()) { - std::stringstream ss; - ss << "missing cards info: nLocs (" << locs.size() << ") != 4 x nRegs (" << regs.size() << "); "; - debugMsg += ss.str(); - isOk = false; - } - - uint8_t refEventWord = 0; - if (!regs.empty()) { - refEventWord = regs.front().triggerWord; - } else if (!locs.empty()) { - // FIXME: in some files, a series of 0xeeee wrongly added before the new RDH - // This is a known problem, so we do not check further in this case - // if (locs.front().statusWord == 0xee && locs.front().triggerWord == 0xee && locs.front().firedChambers == 0xe) { - // return true; - // } - refEventWord = locs.front().triggerWord; - } - - if (!checkSameEventWord(regs, refEventWord) || !checkSameEventWord(locs, refEventWord)) { - debugMsg += "wrong event word; "; - isOk = false; - } - - if (!checkPatterns(regs, 0) || !checkPatterns(locs)) { - debugMsg += "wrong size; "; - isOk = false; - } - - if (!checkConsistency(regs) || !checkConsistency(locs)) { - debugMsg += "inconsistency in the event; "; - isOk = false; - } - - return isOk; -} - -bool CRUBareDataChecker::process(gsl::span<const LocalBoardRO> localBoards, gsl::span<const ROFRecord> rofRecords, bool resetStat) -{ - /// Checks the raw data - - bool isOk = true; - if (resetStat) { - mStatistics.fill(0); - } - - mDebugMsg.clear(); - - // Fill the map with ordered events - for (auto rofIt = rofRecords.begin(); rofIt != rofRecords.end(); ++rofIt) { - mOrderIndexes[rofIt->interactionRecord.toLong()].emplace_back(rofIt - rofRecords.begin()); - } - - std::vector<LocalBoardRO> locs; - std::vector<LocalBoardRO> regs; - - for (auto& item : mOrderIndexes) { - for (auto& idx : item.second) { - // In principle all of these ROF records have the same timestamp - for (size_t iloc = rofRecords[idx].firstEntry; iloc < rofRecords[idx].firstEntry + rofRecords[idx].nEntries; ++iloc) { - if (raw::isLoc(localBoards[iloc].statusWord)) { - // This is a local card - locs.push_back(localBoards[iloc]); - } else { - regs.push_back(localBoards[iloc]); - } - } - } - // std::sort(locs.begin(), locs.end(), [](const LocalBoardRO& a, const LocalBoardRO& b) { return a.boardId < b.boardId; }); - ++mStatistics[0]; - std::string debugStr; - if (!checkBC(regs, locs, debugStr)) { - isOk = false; - std::stringstream ss; - ss << std::hex << std::showbase << rofRecords[item.second.front()].interactionRecord << " problems: " << debugStr << "\n"; - for (auto& reg : regs) { - ss << " " << reg << "\n"; - } - for (auto& loc : locs) { - ss << " " << loc << "\n"; - } - mDebugMsg += ss.str(); - ++mStatistics[1]; - } - - locs.clear(); - regs.clear(); - } - - // Clear the inner objects when the computation is done - mOrderIndexes.clear(); - - return isOk; -} - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/exe/CRUBareDataChecker.h b/Detectors/MUON/MID/Raw/exe/CRUBareDataChecker.h deleted file mode 100644 index 9d4e7d5a12f73..0000000000000 --- a/Detectors/MUON/MID/Raw/exe/CRUBareDataChecker.h +++ /dev/null @@ -1,56 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 CRUBareDataChecker.h -/// \brief Class to check the bare data from the CRU -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 9 December 2019 -#ifndef O2_MID_CRUBAREDATACHECKER_H -#define O2_MID_CRUBAREDATACHECKER_H - -#include <cstdint> -#include <vector> -#include <string> -#include <map> -#include <gsl/gsl> -#include "DataFormatsMID/ROFRecord.h" -#include "MIDRaw/LocalBoardRO.h" - -namespace o2 -{ -namespace mid -{ -class CRUBareDataChecker -{ - public: - bool process(gsl::span<const LocalBoardRO> localBoards, gsl::span<const ROFRecord> rofRecords, bool resetStat = true); - /// Gets the number of processed events - unsigned int getNBCsProcessed() const { return mStatistics[0]; } - /// Gets the number of faulty events - unsigned int getNBCsFaulty() const { return mStatistics[1]; } - /// Gets the - std::string getDebugMessage() const { return mDebugMsg; } - - private: - bool checkBC(const std::vector<LocalBoardRO>& regs, const std::vector<LocalBoardRO>& locs, std::string& debugMsg); - bool checkSameEventWord(const std::vector<LocalBoardRO>& boards, uint8_t refEventWord) const; - bool checkConsistency(const LocalBoardRO& board) const; - bool checkConsistency(const std::vector<LocalBoardRO>& boards) const; - bool checkPatterns(const LocalBoardRO& board, uint8_t expected = 0xFF) const; - bool checkPatterns(const std::vector<LocalBoardRO>& boards, uint8_t expected = 0xFF) const; - - std::map<uint64_t, std::vector<size_t>> mOrderIndexes; /// Map for time ordering the entries - std::string mDebugMsg{}; /// Debug message - std::array<unsigned long int, 2> mStatistics{}; /// Processed events statistics -}; -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_CRUBAREDATACHECKER_H */ diff --git a/Detectors/MUON/MID/Raw/exe/README.md b/Detectors/MUON/MID/Raw/exe/README.md index 386f75dbfbae7..1e9bad6ea0a9b 100644 --- a/Detectors/MUON/MID/Raw/exe/README.md +++ b/Detectors/MUON/MID/Raw/exe/README.md @@ -3,7 +3,7 @@ /doxy --> # Utilities for MID RAW data -This directory contains two utilities that can be executed to verify the raw data. +This directory contains utilities that can be executed to test the raw data. This is particularly important for testing and debugging the MID Read-out. ## MID raw dumper @@ -20,33 +20,3 @@ For a list of the other available options: ```bash o2-mid-rawdump --help ``` - -## MID raw file checker -This utility allows to test files produced by the CRU w/o using the User Logic. -Basic usage: -```bash -o2-mid-raw-checker filename [filename_2 filename_3 ...] -``` -The output is a file (or a series of files if a list of inputs is provided), named after the input file as `check_filename.txt`, so that it is easy to understand to which input file the output is referring to. -The different e-links are read out and then grouped according to their interaction record. -The output file contains: -- a list of possible problems (if any) for each event processed - -- a summary with the number of faulty events -The list of problems is preceded by the identifier of the processed HB and the corresponding "line" in the file where the HB is. -The line is counted assuming that one reads the file as a series of 4 words of 32 bits each (this is the typical way the binary file is converted into text during the tests). -Notice that one can have more than 1 HB per event, since the data might be split into different pages if the maximum page size is reached. -For a list of the other available options: -```bash -o2-mid-raw-checker --help -``` -### Performed checks -The decoded information is read HB per HB (multiple pages are read out when needed). -For each HB, the decoded information are gathered according to their interaction. -Notice that, in principle, events belonging to different HBs should have different interaction records, so one could in principle read the full file and then perform the check. -However, in the tests the RDH is not always correctly set, and the orbit number might not increase. That is why we read the HB one by one, and limit the tests to 1 HB at the time. This makes it easier to tag HBs with issues in the RO. -For each interaction record, a number of checks are performed: -- The number of local cards read must be 4 times the number of regional cards. Notice that this error often implies that there is a mismatch between the regional and local clocks (so the cards gets split into different interaction records) -- The word describing the event (SOX, EOX, HC, Calibration, etc.) should be the same for all cards in the interaction record. -- The number of non-zero patterns read must match the information written in the corresponding word that indicates the non-zero detector planes. Notice that this might not be true when the patterns represent the masks, since in this case it is fine to transmit a zero pattern. However, for the sake of simplicity, we do not account for this possibility. Of course, this implies that must run the check on tests where all masks are non-zero. -- For each card we check that the information is consistent. For example we cannot have SOX and Calibration bits fired at the same time. Also, during an HC, we expect to have no chamber fired for the local boards. Notice that during tests this information is added by hand, so we should not have any issue by design. \ No newline at end of file diff --git a/Detectors/MUON/MID/Raw/exe/mid-raw-checker.cxx b/Detectors/MUON/MID/Raw/exe/mid-raw-checker.cxx deleted file mode 100644 index 38faa81c0aeac..0000000000000 --- a/Detectors/MUON/MID/Raw/exe/mid-raw-checker.cxx +++ /dev/null @@ -1,139 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 mid-raw-checker.cxx -/// \brief CRU bare data checker for MID -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 09 December 2019 - -#include <iostream> -#include <fstream> -#include "boost/program_options.hpp" -#include "MIDRaw/CRUBareDecoder.h" -#include "MIDRaw/RawFileReader.h" -#include "CRUBareDataChecker.h" - -namespace po = boost::program_options; - -o2::header::RAWDataHeader buildCustomRDH() -{ - o2::header::RAWDataHeader rdh; - rdh.word1 |= 0x2000; - rdh.word1 |= ((0x2000 - 0x100)) << 16; - return rdh; -} - -int main(int argc, char* argv[]) -{ - po::variables_map vm; - po::options_description generic("Generic options"); - unsigned long int nHBs = 0; - bool onlyClosedHBs = false; - bool ignoreRDH = true; - - // clang-format off - generic.add_options() - ("help", "produce help message") - ("nHBs", po::value<unsigned long int>(&nHBs),"Number of HBs read") - ("only-closed-HBs", po::value<bool>(&onlyClosedHBs)->implicit_value(true),"Return only closed HBs") - ("ignore-RDH", po::value<bool>(&ignoreRDH)->implicit_value(true),"Ignore read RDH. Use custom one instead"); - - - po::options_description hidden("hidden options"); - hidden.add_options() - ("input", po::value<std::vector<std::string>>(),"Input filename"); - // clang-format on - - po::options_description cmdline; - cmdline.add(generic).add(hidden); - - po::positional_options_description pos; - pos.add("input", -1); - - po::store(po::command_line_parser(argc, argv).options(cmdline).positional(pos).run(), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << "Usage: " << argv[0] << " <input_raw_filename> [input_raw_filename_1 ...]\n"; - std::cout << generic << std::endl; - return 2; - } - if (vm.count("input") == 0) { - std::cout << "no input file specified" << std::endl; - return 1; - } - - std::vector<std::string> inputfiles{vm["input"].as<std::vector<std::string>>()}; - - o2::mid::CRUBareDecoder decoder; - decoder.init(true); - - o2::mid::CRUBareDataChecker checker; - - unsigned long int iHB = 0; - - for (auto& filename : inputfiles) { - o2::mid::RawFileReader<uint8_t> rawFileReader; - std::string outFilename = filename; - auto pos = outFilename.find_last_of("/"); - if (pos == std::string::npos) { - pos = 0; - } else { - ++pos; - } - outFilename.insert(pos, "check_"); - outFilename += ".txt"; - std::ofstream outFile(outFilename.c_str()); - if (!rawFileReader.init(filename.c_str())) { - return 2; - } - if (ignoreRDH) { - rawFileReader.setCustomRDH(buildCustomRDH()); - } - std::vector<o2::mid::LocalBoardRO> data; - std::vector<o2::mid::ROFRecord> rofRecords; - std::stringstream ss; - bool isFirst = true; - while (rawFileReader.getState() == 0) { - rawFileReader.readHB(onlyClosedHBs); - decoder.process(rawFileReader.getData()); - rawFileReader.clear(); - size_t offset = data.size(); - std::copy(decoder.getData().begin(), decoder.getData().end(), std::back_inserter(data)); - for (auto& rof : decoder.getROFRecords()) { - rofRecords.emplace_back(rof.interactionRecord, rof.eventType, rof.firstEntry + offset, rof.nEntries); - } - ss << " HB: " << iHB << " (line: " << 512 * iHB + 1 << ")"; - if (decoder.isComplete()) { - // The check assumes that we have all data corresponding to one event. - // However this might not be true since we read one HB at the time. - // So we must test that the event was fully read before running the check. - if (!checker.process(data, rofRecords, isFirst)) { - outFile << ss.str() << "\n"; - outFile << checker.getDebugMessage() << "\n"; - } - isFirst = false; - std::stringstream clean; - ss.swap(clean); - data.clear(); - rofRecords.clear(); - } - ++iHB; - if (nHBs > 0 && iHB >= nHBs) { - break; - } - } - outFile << "Fraction of faulty events: " << checker.getNBCsFaulty() << " / " << checker.getNBCsProcessed() << " = " << static_cast<double>(checker.getNBCsFaulty()) / static_cast<double>(checker.getNBCsProcessed()); - - outFile.close(); - } - - return 0; -} diff --git a/Detectors/MUON/MID/Raw/exe/mid-rawdump.cxx b/Detectors/MUON/MID/Raw/exe/mid-rawdump.cxx deleted file mode 100644 index 47f8519ba00d6..0000000000000 --- a/Detectors/MUON/MID/Raw/exe/mid-rawdump.cxx +++ /dev/null @@ -1,95 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 mid-rawdump.cxx -/// \brief Raw dumper for MID -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 05 December 2019 - -#include <iostream> -#include "boost/program_options.hpp" -#include "MIDRaw/CRUBareDecoder.h" -#include "MIDRaw/RawFileReader.h" - -namespace po = boost::program_options; - -int main(int argc, char* argv[]) -{ - po::variables_map vm; - po::options_description generic("Generic options"); - std::string outFilename = "mid_raw.txt"; - unsigned long int nHBs = 0; - bool onlyClosedHBs = false; - - // clang-format off - generic.add_options() - ("help", "produce help message") - ("output", po::value<std::string>(&outFilename),"Output text file") - ("nHBs", po::value<unsigned long int>(&nHBs),"Number of HBs read") - ("only-closed-HBs", po::value<bool>(&onlyClosedHBs)->implicit_value(true),"Return only closed HBs"); - - - po::options_description hidden("hidden options"); - hidden.add_options() - ("input", po::value<std::vector<std::string>>(),"Input filename"); - // clang-format on - - po::options_description cmdline; - cmdline.add(generic).add(hidden); - - po::positional_options_description pos; - pos.add("input", -1); - - po::store(po::command_line_parser(argc, argv).options(cmdline).positional(pos).run(), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << "Usage: " << argv[0] << " <input_raw_filename> [input_raw_filename_1 ...]\n"; - std::cout << generic << std::endl; - return 2; - } - if (vm.count("input") == 0) { - std::cout << "no input file specified" << std::endl; - return 1; - } - - std::vector<std::string> inputfiles{vm["input"].as<std::vector<std::string>>()}; - - o2::mid::CRUBareDecoder decoder; - - std::ofstream outFile(outFilename); - - unsigned long int iHB = 0; - - for (auto& filename : inputfiles) { - o2::mid::RawFileReader<uint8_t> rawFileReader; - if (!rawFileReader.init(filename.c_str())) { - return 2; - } - while (rawFileReader.getState() == 0) { - rawFileReader.readHB(onlyClosedHBs); - decoder.process(rawFileReader.getData()); - rawFileReader.clear(); - for (auto& rof : decoder.getROFRecords()) { - outFile << "Orbit: " << rof.interactionRecord.orbit << " bc: " << rof.interactionRecord.bc << std::endl; - for (auto colIt = decoder.getData().begin() + rof.firstEntry; colIt != decoder.getData().begin() + rof.firstEntry + rof.nEntries; ++colIt) { - outFile << *colIt << std::endl; - } - } - ++iHB; - if (nHBs > 0 && iHB >= nHBs) { - break; - } - } - } - outFile.close(); - - return 0; -} diff --git a/Detectors/MUON/MID/Raw/exe/rawdump.cxx b/Detectors/MUON/MID/Raw/exe/rawdump.cxx new file mode 100644 index 0000000000000..e61651bd32cb4 --- /dev/null +++ b/Detectors/MUON/MID/Raw/exe/rawdump.cxx @@ -0,0 +1,173 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/Raw/exe/rawdump.cxx +/// \brief Raw data dumper for MID +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 05 December 2019 + +#include <iostream> +#include "fmt/format.h" +#include "boost/program_options.hpp" +#include "DPLUtils/RawParser.h" +#include "MIDRaw/Decoder.h" +#include "MIDRaw/RawFileReader.h" + +namespace po = boost::program_options; + +std::string getOutFilename(const char* inFilename, const char* outDir) +{ + std::string basename(inFilename); + std::string fdir = "./"; + auto pos = basename.find_last_of("/"); + if (pos != std::string::npos) { + basename.erase(0, pos + 1); + fdir = inFilename; + fdir.erase(pos); + } + basename.insert(0, "dump_"); + basename += ".txt"; + std::string outputDir(outDir); + if (outputDir.empty()) { + outputDir = fdir; + } + if (outputDir.back() != '/') { + outputDir += "/"; + } + std::string outFilename = outputDir + basename; + return outFilename; +} + +template <class RDH> +void decode(o2::mid::Decoder& decoder, gsl::span<const uint8_t> payload, const RDH& rdh, std::ostream& out) +{ + decoder.clear(); + decoder.process(payload, rdh); + for (auto& rof : decoder.getROFRecords()) { + out << fmt::format("BCid: 0x{:x} Orbit: 0x{:x}", rof.interactionRecord.bc, rof.interactionRecord.orbit) << std::endl; + for (auto colIt = decoder.getData().begin() + rof.firstEntry; colIt != decoder.getData().begin() + rof.firstEntry + rof.nEntries; ++colIt) { + out << *colIt << std::endl; + } + } +} + +int main(int argc, char* argv[]) +{ + po::variables_map vm; + po::options_description generic("Generic options"); + std::string outFilename = ""; + unsigned long int nHBs = 0; + unsigned long int firstHB = 0; + + // clang-format off + generic.add_options() + ("help", "produce help message") + ("output", po::value<std::string>(&outFilename),"Output text file") + ("first", po::value<unsigned long int>(&firstHB),"First HB to read") + ("nHBs", po::value<unsigned long int>(&nHBs),"Number of HBs read") + ("rdh-only", po::value<bool>()->implicit_value(true),"Only show RDHs") + ("decode", po::value<bool>()->implicit_value(true),"Decode output") + ("feeId-config-file", po::value<std::string>()->default_value(""),"Filename with crate FEE ID correspondence"); + + + po::options_description hidden("hidden options"); + hidden.add_options() + ("input", po::value<std::vector<std::string>>(),"Input filename"); + // clang-format on + + po::options_description cmdline; + cmdline.add(generic).add(hidden); + + po::positional_options_description pos; + pos.add("input", -1); + + po::store(po::command_line_parser(argc, argv).options(cmdline).positional(pos).run(), vm); + po::notify(vm); + + if (vm.count("help")) { + std::cout << "Usage: " << argv[0] << " <input_raw_filename> [input_raw_filename_1 ...]\n"; + std::cout << generic << std::endl; + return 2; + } + if (vm.count("input") == 0) { + std::cout << "no input file specified" << std::endl; + return 1; + } + + std::vector<std::string> inputfiles{vm["input"].as<std::vector<std::string>>()}; + + bool runDecoder = (vm.count("decode") > 0); + std::unique_ptr<o2::mid::Decoder> decoder{nullptr}; + + std::ofstream outFile; + std::ostream& out = (outFilename.empty()) ? std::cout : (outFile.open(outFilename), outFile); + + unsigned long int iHB = 0; + bool isRdhOnly = vm.count("rdh-only") > 0; + + for (auto& filename : inputfiles) { + // Here we use a custom file reader to be able to read all kind of raw data, + // even those with a malformed structure in terms of number of HBFs per time frame + o2::mid::RawFileReader rawFileReader; + if (!rawFileReader.init(filename.c_str())) { + return 2; + } + while (rawFileReader.readHB()) { + if (iHB >= firstHB) { + o2::framework::RawParser parser(rawFileReader.getData().data(), rawFileReader.getData().size()); + auto it = parser.begin(); // We only have 1 HB + auto const* rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(it.raw()); + if (!runDecoder) { + o2::raw::RDHUtils::printRDH(rdhPtr); + } + if (it.size() > 0) { + gsl::span<const uint8_t> payload(it.data(), it.size()); + if (runDecoder) { + if (!decoder) { + decoder = o2::mid::createDecoder(*rdhPtr, true, "", "", vm["feeId-config-file"].as<std::string>().c_str()); + } + decode(*decoder, payload, *rdhPtr, out); + } else if (!isRdhOnly) { + bool isBare = (o2::raw::RDHUtils::getLinkID(rdhPtr) != o2::mid::raw::sUserLogicLinkID); + size_t wordLength = isBare ? 16 : 32; + for (size_t iword = 0; iword < payload.size(); iword += wordLength) { + auto word = payload.subspan(iword, wordLength); + if (isBare) { + for (auto it = word.rbegin(); it != word.rend(); ++it) { + auto ibInWord = word.rend() - it; + if (ibInWord == 4 || ibInWord == 9) { + out << " "; + } + if (ibInWord == 5 || ibInWord == 10) { + out << " "; + } + out << fmt::format("{:02x}", static_cast<int>(*it)); + } + } else { + for (auto it = word.begin(); it != word.end(); ++it) { + out << fmt::format("{:02x}", static_cast<int>(*it)); + } + } + out << "\n"; + } + } + } + } + rawFileReader.clear(); + ++iHB; + if (nHBs > 0 && iHB >= nHBs + firstHB) { + break; + } + } // loop on HBs + } // loop on files + outFile.close(); + + return 0; +} diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/CRUBareDecoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/CRUBareDecoder.h deleted file mode 100644 index a041527fc95a9..0000000000000 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/CRUBareDecoder.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDRaw/CRUBareDecoder.h -/// \brief MID CRU core decoder -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 18 November 2019 -#ifndef O2_MID_CRUBAREDECODER_H -#define O2_MID_CRUBAREDECODER_H - -#include <cstdint> -#include <vector> -#include <gsl/gsl> -#include "DataFormatsMID/ROFRecord.h" -#include "MIDRaw/CrateMapper.h" -#include "MIDRaw/CrateParameters.h" -#include "MIDRaw/ELinkDecoder.h" -#include "MIDRaw/LocalBoardRO.h" -#include "MIDRaw/RawBuffer.h" - -namespace o2 -{ -namespace mid -{ -class CRUBareDecoder -{ - public: - void init(bool debugMode = false); - void process(gsl::span<const uint8_t> bytes); - /// Gets the vector of data - const std::vector<LocalBoardRO>& getData() const { return mData; } - - /// Gets the vector of data RO frame records - const std::vector<ROFRecord>& getROFRecords() const { return mROFRecords; } - - bool isComplete() const; - - private: - RawBuffer<uint8_t> mBuffer{}; /// Raw buffer handler - std::vector<LocalBoardRO> mData{}; /// Vector of output data - std::vector<ROFRecord> mROFRecords{}; /// List of ROF records - CrateMapper mCrateMapper{}; /// Crate mapper - uint8_t mCrateId{0}; /// Crate ID - std::array<uint16_t, crateparams::sNELinksPerGBT> mCalibClocks{}; /// Calibration clock - std::array<ELinkDecoder, crateparams::sNELinksPerGBT> mELinkDecoders{}; /// E-link decoders - std::function<void(size_t)> mAddReg{[](size_t) {}}; ///! Add regional board - - std::function<bool(size_t)> mCheckBoard{std::bind(&CRUBareDecoder::checkBoard, this, std::placeholders::_1)}; ///! Check board - - bool nextGBTWord(); - void processGBT(size_t offset); - void reset(); - void addBoard(size_t ilink); - bool checkBoard(size_t ilink); - void addLoc(size_t ilink); - uint16_t getPattern(uint16_t pattern, bool invert) const; -}; -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_CRUBAREDECODER_H */ diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/ColumnDataToLocalBoard.h b/Detectors/MUON/MID/Raw/include/MIDRaw/ColumnDataToLocalBoard.h index 7e653b96f9568..2df9b6a7acf49 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/ColumnDataToLocalBoard.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/ColumnDataToLocalBoard.h @@ -34,13 +34,18 @@ class ColumnDataToLocalBoard void process(gsl::span<const ColumnData> data); /// Gets the output data per GBT link const std::unordered_map<uint16_t, std::vector<LocalBoardRO>> getData() { return mGBTMap; } + /// Sets debug mode + void setDebugMode(bool debugMode = true) { mDebugMode = debugMode; } private: - std::unordered_map<uint16_t, LocalBoardRO> mLocalBoardsMap{}; /// Map of data per board + bool keepBoard(const LocalBoardRO& loc) const; + std::unordered_map<uint8_t, LocalBoardRO> mLocalBoardsMap{}; /// Map of data per board std::unordered_map<uint16_t, std::vector<LocalBoardRO>> mGBTMap{}; /// Map of data per GBT link CrateMapper mCrateMapper{}; /// Crate mapper Mapping mMapping{}; /// Segmentation + bool mDebugMode{false}; /// Debug mode (no zero suppression) }; + } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/CrateMapper.h b/Detectors/MUON/MID/Raw/include/MIDRaw/CrateMapper.h index 2ca9f7ef90cf0..dc6a8bda346f2 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/CrateMapper.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/CrateMapper.h @@ -36,14 +36,14 @@ class CrateMapper static uint8_t getColumnId(uint16_t deBoardId) { return (deBoardId >> 4) & 0x7; } /// Gets the line ID from the DE board ID static uint8_t getLineId(uint16_t deBoardId) { return (deBoardId >> 7) & 0x3; } - uint16_t deLocalBoardToRO(uint8_t deId, uint8_t columnId, uint8_t lineId) const; + uint8_t deLocalBoardToRO(uint8_t deId, uint8_t columnId, uint8_t lineId) const; uint16_t roLocalBoardToDE(uint8_t crateId, uint8_t boardId) const; private: void init(); - std::unordered_map<uint16_t, uint16_t> mROToDEMap; /// Correspondence between RO and DE board - std::unordered_map<uint16_t, uint16_t> mDEToROMap; /// Correspondence between DE and RO board + std::unordered_map<uint8_t, uint16_t> mROToDEMap; /// Correspondence between RO and DE board + std::unordered_map<uint16_t, uint8_t> mDEToROMap; /// Correspondence between DE and RO board }; } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/Decoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/Decoder.h index b872acaa9be81..2b5e1ff4fe46b 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/Decoder.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/Decoder.h @@ -20,11 +20,12 @@ #include <array> #include <gsl/gsl> #include "DataFormatsMID/ROFRecord.h" +#include "DetectorsRaw/RDHUtils.h" #include "MIDRaw/CrateMasks.h" #include "MIDRaw/CrateParameters.h" +#include "MIDRaw/ElectronicsDelay.h" #include "MIDRaw/FEEIdConfig.h" -#include "MIDRaw/GBTBareDecoder.h" -#include "MIDRaw/GBTUserLogicDecoder.h" +#include "MIDRaw/GBTDecoder.h" #include "MIDRaw/LocalBoardRO.h" namespace o2 @@ -32,22 +33,18 @@ namespace o2 namespace mid { -template <typename GBTDECODER> class Decoder { public: - Decoder(); - ~Decoder() = default; - void setFeeIdConfig(const FEEIdConfig& feeIdConfig) { mFEEIdConfig = feeIdConfig; } - void setCrateMasks(const CrateMasks& masks) { mMasks = masks; } - void init(bool isDebugMode = false); + Decoder(bool isDebugMode = false, bool isBare = false, const ElectronicsDelay& electronicsDelay = ElectronicsDelay(), const CrateMasks& crateMasks = CrateMasks(), const FEEIdConfig& feeIdConfig = FEEIdConfig()); + virtual ~Decoder() = default; void process(gsl::span<const uint8_t> bytes); - template <typename RDH = o2::header::RAWDataHeader> + template <class RDH> void process(gsl::span<const uint8_t> payload, const RDH& rdh) { /// Processes the page - uint16_t feeId = mFEEIdConfig.getFeeId(o2::raw::RDHUtils::getLinkID(rdh), o2::raw::RDHUtils::getEndPointID(rdh), o2::raw::RDHUtils::getCRUID(rdh)); - mGBTDecoders[feeId].process(payload, o2::raw::RDHUtils::getHeartBeatBC(rdh), o2::raw::RDHUtils::getHeartBeatOrbit(rdh), o2::raw::RDHUtils::getPageCounter(rdh)); + auto feeId = mGetFEEID(rdh); + mGBTDecoders[feeId]->process(payload, o2::raw::RDHUtils::getHeartBeatOrbit(rdh), mData, mROFRecords); } /// Gets the vector of data const std::vector<LocalBoardRO>& getData() const { return mData; } @@ -55,20 +52,22 @@ class Decoder /// Gets the vector of data RO frame records const std::vector<ROFRecord>& getROFRecords() const { return mROFRecords; } - void flush(); - void clear(); - bool isComplete() const; + protected: + /// Gets the feeID + std::function<uint16_t(const o2::header::RDHAny& rdh)> mGetFEEID{[](const o2::header::RDHAny& rdh) { return o2::raw::RDHUtils::getFEEID(rdh); }}; + + std::array<std::unique_ptr<GBTDecoder>, crateparams::sNGBTs> mGBTDecoders{nullptr}; /// GBT decoders private: - std::vector<LocalBoardRO> mData{}; /// Vector of output data - std::vector<ROFRecord> mROFRecords{}; /// List of ROF records - std::array<GBTDECODER, crateparams::sNGBTs> mGBTDecoders{}; /// GBT decoders - FEEIdConfig mFEEIdConfig{}; /// Crate FEEID mapper - CrateMasks mMasks{}; /// Crate masks + std::vector<LocalBoardRO> mData{}; /// Vector of output data + std::vector<ROFRecord> mROFRecords{}; /// List of ROF records }; +std::unique_ptr<Decoder> createDecoder(const o2::header::RDHAny& rdh, bool isDebugMode, ElectronicsDelay& electronicsDelay, const CrateMasks& crateMasks, const FEEIdConfig& feeIdConfig); +std::unique_ptr<Decoder> createDecoder(const o2::header::RDHAny& rdh, bool isDebugMode, const char* electronicsDelayFile = "", const char* crateMasksFile = "", const char* feeIdConfigFile = ""); + } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/ELinkDecoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/ELinkDecoder.h index 6d2a081635990..43c2c372f268d 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/ELinkDecoder.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/ELinkDecoder.h @@ -17,6 +17,9 @@ #include <cstdint> #include <vector> +#include <gsl/gsl> + +#include "MIDRaw/LocalBoardRO.h" namespace o2 { @@ -25,29 +28,76 @@ namespace mid class ELinkDecoder { public: - void add(const uint8_t byte); - bool add(const uint8_t byte, uint8_t expectedStart); + /// Adds a byte + inline void add(const uint8_t byte) { mBytes.emplace_back(byte); } + void addAndComputeSize(const uint8_t byte); + template <class ITERATOR> + bool addCore(ITERATOR& it, const ITERATOR& end) + { + /// Adds the first 5 bytes + auto remaining = sMinimumSize - mBytes.size(); + return add(it, end, remaining); + } + template <class ITERATOR> + bool add(ITERATOR& it, const ITERATOR& end) + { + /// Adds the board bytes + auto remaining = mTotalSize - mBytes.size(); + if (add(it, end, remaining)) { + if (mTotalSize == sMinimumSize) { + computeSize(); + remaining = mTotalSize - mBytes.size(); + if (remaining) { + return add(it, end, remaining); + } + } + return true; + } + return false; + } + + /// Adds the first 5 bytes + inline bool addCore(size_t& idx, gsl::span<const uint8_t> payload, size_t step) { return add(idx, payload, sMinimumSize - mBytes.size(), step); } + + bool add(size_t& idx, gsl::span<const uint8_t> payload, size_t step); + + /// Checks if this is a zero + inline bool isZero(uint8_t byte) const { return (mBytes.empty() && (byte & raw::sSTARTBIT) == 0); } + /// Checks if we have all of the information needed for the decoding - bool isComplete() const { return mBytes.size() == mTotalSize; }; + inline bool isComplete() const { return mBytes.size() == mTotalSize; }; /// Gets the status word - uint8_t getStatusWord() const { return mBytes[0]; } + inline uint8_t getStatusWord() const { return mBytes[0]; } /// Gets the trigger word - uint8_t getTriggerWord() const { return mBytes[1]; } + inline uint8_t getTriggerWord() const { return mBytes[1]; } /// Gets the counter - uint16_t getCounter() const { return joinBytes(2); } - // uint16_t getCounter() const { return (mBytes[2] << 8) | mBytes[3]; } + inline uint16_t getCounter() const { return joinBytes(2); } /// Gets the card ID - uint8_t getId() const { return (mBytes[4] >> 4) & 0xF; } + inline uint8_t getId() const { return (mBytes[4] >> 4) & 0xF; } /// Gets the inputs - uint8_t getInputs() const { return (mBytes[4] & 0xF); } + inline uint8_t getInputs() const { return (mBytes[4] & 0xF); } uint16_t getPattern(int cathode, int chamber) const; /// Gets the number of bytes read - size_t getNBytes() const { return mBytes.size(); } + inline size_t getNBytes() const { return mBytes.size(); } void reset(); private: inline uint16_t joinBytes(int idx) const { return (mBytes[idx] << 8 | mBytes[idx + 1]); }; + template <class ITERATOR> + bool add(ITERATOR& it, const ITERATOR& end, size_t nBytes) + { + /// Fills inner bytes vector + auto nToEnd = std::distance(it, end); + auto nAdded = nBytes < nToEnd ? nBytes : nToEnd; + mBytes.insert(mBytes.end(), it, it + nAdded); + it += nAdded; + return (nAdded == nBytes); + } + + bool add(size_t& idx, gsl::span<const uint8_t> payload, size_t nBytes, size_t step); + + void computeSize(); static constexpr size_t sMinimumSize{5}; /// Minimum size of the buffer static constexpr size_t sMaximumSize{21}; /// Maximum size of the buffer diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/ElectronicsDelay.h b/Detectors/MUON/MID/Raw/include/MIDRaw/ElectronicsDelay.h new file mode 100644 index 0000000000000..a689ed2a6d033 --- /dev/null +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/ElectronicsDelay.h @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDRaw/ElectronicsDelay.h +/// \brief Delay parameters for MID electronics +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 27 July 2020 +#ifndef O2_MID_ELECTRONICSDELAY_H +#define O2_MID_ELECTRONICSDELAY_H + +#include <cstdint> +#include <iostream> + +namespace o2 +{ +namespace mid +{ + +struct ElectronicsDelay { + // The delays are in local clocks, and correspond to the LHC clocks (aka BCs) + uint16_t calibToFET{9}; + uint16_t BCToLocal{0}; + uint16_t regToLocal{6}; +}; + +std::ostream& operator<<(std::ostream& os, const ElectronicsDelay& delay); + +ElectronicsDelay readElectronicsDelay(const char* filename); + +} // namespace mid +} // namespace o2 + +#endif /* O2_MID_ELECTRONICSDELAY_H */ diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/Encoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/Encoder.h index f025fbf27356d..871b12546a0d2 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/Encoder.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/Encoder.h @@ -18,6 +18,7 @@ #include <cstdint> #include <array> #include <map> +#include <vector> #include <gsl/gsl> #include "CommonDataFormat/InteractionRecord.h" #include "DetectorsRaw/RawFileWriter.h" @@ -28,6 +29,8 @@ #include "MIDRaw/GBTUserLogicEncoder.h" #include "MIDRaw/LocalBoardRO.h" +class RDHAny; + namespace o2 { namespace mid @@ -35,7 +38,7 @@ namespace mid class Encoder { public: - void init(const char* filename, int verbosity = 0); + void init(const char* filename, bool perLink = false, int verbosity = 0, bool debugMode = false); void process(gsl::span<const ColumnData> data, const InteractionRecord& ir, EventType eventType = EventType::Standard); /// Sets the maximum size of the superpage void setSuperpageSize(int maxSize) { mRawWriter.setSuperPageSize(maxSize); } @@ -44,18 +47,26 @@ class Encoder auto& getWriter() { return mRawWriter; } + void emptyHBFMethod(const o2::header::RDHAny* rdh, std::vector<char>& toAdd) const; + private: - void flush(uint16_t feeId, const InteractionRecord& ir); - void hbTrigger(const InteractionRecord& ir); + void completeWord(std::vector<char>& buffer); + void writePayload(uint16_t feeId, const InteractionRecord& ir); + void onOrbitChange(uint32_t orbit); + // Returns the interaction record expected for the orbit trigger + inline InteractionRecord getOrbitIR(uint32_t orbit) const { return {o2::constants::lhc::LHCMaxBunches - 1, orbit}; } o2::raw::RawFileWriter mRawWriter{o2::header::gDataOriginMID}; /// Raw file writer + std::map<uint16_t, LocalBoardRO> mROData{}; /// Map of data per board ColumnDataToLocalBoard mConverter{}; /// ColumnData to LocalBoardRO converter FEEIdConfig mFEEIdConfig{}; /// Crate FEEId mapper InteractionRecord mLastIR{}; /// Last interaction record - std::array<GBTUserLogicEncoder, crateparams::sNGBTs> mGBTEncoders{}; /// Array of encoders per link - std::array<uint32_t, crateparams::sNGBTs> mGBTIds{}; /// Array of GBT Ids + std::array<GBTUserLogicEncoder, crateparams::sNGBTs> mGBTEncoders{}; /// Array of encoders per link + std::array<std::vector<char>, crateparams::sNGBTs> mOrbitResponse{}; /// Response to orbit trigger + std::array<std::vector<char>, crateparams::sNGBTs> mOrbitResponseWord{}; /// CRU word for response to orbit trigger + std::array<uint32_t, crateparams::sNGBTs> mGBTIds{}; /// Array of GBT Ids }; } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/FEEIdConfig.h b/Detectors/MUON/MID/Raw/include/MIDRaw/FEEIdConfig.h index efb24922d6753..6c0f057c4a90c 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/FEEIdConfig.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/FEEIdConfig.h @@ -55,6 +55,10 @@ class FEEIdConfig std::unordered_map<uint32_t, uint16_t> mGBTIdToFeeId; /// Correspondence between GBT Id and FeeId }; +namespace raw +{ +static constexpr uint8_t sUserLogicLinkID = 15; +} } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTBareDecoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTBareDecoder.h deleted file mode 100644 index db5289ef12287..0000000000000 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTBareDecoder.h +++ /dev/null @@ -1,71 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDRaw/GBTBareDecoder.h -/// \brief MID GBT decoder without user logic -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 12 March 2020 -#ifndef O2_MID_GBTBAREDECODER_H -#define O2_MID_GBTBAREDECODER_H - -#include <cstdint> -#include <array> -#include <vector> -#include <gsl/gsl> -#include "DataFormatsMID/ROFRecord.h" -#include "MIDRaw/CrateParameters.h" -#include "MIDRaw/ELinkDecoder.h" -#include "MIDRaw/GBTOutputHandler.h" -#include "MIDRaw/LocalBoardRO.h" - -namespace o2 -{ -namespace mid -{ -class GBTBareDecoder -{ - public: - void init(uint16_t feeId, uint8_t mask, bool isDebugMode = false); - void process(gsl::span<const uint8_t> bytes, uint16_t bc, uint32_t orbit, uint16_t pageCnt); - /// Gets the vector of data - const std::vector<LocalBoardRO>& getData() const { return mOutputHandler.getData(); } - - /// Gets the vector of data RO frame records - const std::vector<ROFRecord>& getROFRecords() const { return mOutputHandler.getROFRecords(); } - - bool isComplete() const; - - /// Clears the decoded data - void clear() { mOutputHandler.clear(); } - - private: - GBTOutputHandler mOutputHandler{}; /// GBT output handler - uint8_t mMask{0xFF}; /// GBT mask - uint16_t mIsFeeding{0}; /// Flag to check if the e-link is feeding - - std::array<ELinkDecoder, crateparams::sNELinksPerGBT> mELinkDecoders{}; /// E-link decoders - - // Here we are using a function pointer instead of a std::function because it is faster. - // The std::function adds an overhead at each function call, - // which results in a considerable slowing done of the code if the function is executed often - typedef void (GBTOutputHandler::*OnDoneFunction)(size_t, const ELinkDecoder&); - typedef void (GBTBareDecoder::*ProcessFunction)(size_t, uint8_t); - - OnDoneFunction mOnDoneLoc{&GBTOutputHandler::onDoneLoc}; ///! Processes the local board - ProcessFunction mProcessReg{&GBTBareDecoder::processReg}; ///! Processes the regional board - - void processLoc(size_t ilink, uint8_t byte); - void processReg(size_t, uint8_t){}; /// Dummy function. We usually do not process the regional cards, except when we are debugging the code - void processRegDebug(size_t ilink, uint8_t byte); -}; -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_GBTBAREDECODER_H */ diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTDecoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTDecoder.h new file mode 100644 index 0000000000000..60d96fc26b38e --- /dev/null +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTDecoder.h @@ -0,0 +1,54 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDRaw/GBTDecoder.h +/// \brief Class interface for the MID GBT decoder +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 07 November 2020 +#ifndef O2_MID_GBTDECODER_H +#define O2_MID_GBTDECODER_H + +#include <cstdint> +#include <vector> +#include <gsl/gsl> +#include "DetectorsRaw/RDHUtils.h" +#include "Headers/RAWDataHeader.h" +#include "DataFormatsMID/ROFRecord.h" +#include "MIDRaw/ElectronicsDelay.h" +#include "MIDRaw/LocalBoardRO.h" + +namespace o2 +{ +namespace mid +{ + +class GBTDecoder +{ + public: + GBTDecoder(std::function<void(gsl::span<const uint8_t>, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs)> decode) : mDecode(decode) {} + void process(gsl::span<const uint8_t> payload, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs); + + template <class RDH> + void process(gsl::span<const uint8_t> payload, const RDH& rdh, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) + { + process(payload, o2::raw::RDHUtils::getHeartBeatOrbit(rdh), data, rofs); + } + + protected: + std::function<void(gsl::span<const uint8_t>, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs)> mDecode{nullptr}; +}; + +std::unique_ptr<GBTDecoder> createGBTDecoder(const o2::header::RDHAny& rdh, uint16_t feeId, bool isDebugMode, uint8_t mask, const ElectronicsDelay& electronicsDelay); +std::unique_ptr<GBTDecoder> createGBTDecoder(uint16_t feeId, bool isBare = false, bool isDebugMode = false, uint8_t mask = 0xFF, const ElectronicsDelay& electronicsDelay = ElectronicsDelay()); + +} // namespace mid +} // namespace o2 + +#endif /* O2_MID_GBTDECODER_H */ diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTOutputHandler.h b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTOutputHandler.h index 9aa02fb34ce42..425d2771e2cf2 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTOutputHandler.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTOutputHandler.h @@ -21,6 +21,7 @@ #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsMID/ROFRecord.h" #include "MIDRaw/CrateParameters.h" +#include "MIDRaw/ElectronicsDelay.h" #include "MIDRaw/ELinkDecoder.h" #include "MIDRaw/LocalBoardRO.h" @@ -34,36 +35,34 @@ class GBTOutputHandler /// Sets the FEE Id void setFeeId(uint16_t feeId) { mFeeId = feeId; } - void setIR(uint16_t bc, uint32_t orbit, int pageCnt); + void set(uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs); void onDoneLoc(size_t ilink, const ELinkDecoder& decoder); void onDoneLocDebug(size_t ilink, const ELinkDecoder& decoder); void onDoneReg(size_t, const ELinkDecoder&){}; /// Dummy function void onDoneRegDebug(size_t ilink, const ELinkDecoder& decoder); - /// Gets the vector of data - const std::vector<LocalBoardRO>& getData() const { return mData; } - - /// Gets the vector of data RO frame records - const std::vector<ROFRecord>& getROFRecords() const { return mROFRecords; } - - void clear(); + /// Sets the delay in the electronics + void setElectronicsDelay(const ElectronicsDelay& electronicsDelay) { mElectronicsDelay = electronicsDelay; } private: - std::vector<LocalBoardRO> mData{}; /// Vector of output data - std::vector<ROFRecord> mROFRecords{}; /// List of ROF records - uint16_t mFeeId{0}; /// FEE ID - InteractionRecord mIRFirstPage{}; /// Interaction record of the first page + std::vector<LocalBoardRO>* mData{nullptr}; ///! Vector of output data. Not owner + std::vector<ROFRecord>* mROFRecords{nullptr}; /// List of ROF records. Not owner + uint16_t mFeeId{0}; /// FEE ID + uint32_t mOrbit{}; /// RDH orbit + uint16_t mReceivedCalibration{0}; /// Word with one bit per e-link indicating if the calibration trigger was received by the e-link + ElectronicsDelay mElectronicsDelay{}; /// Delays in the electronics - std::array<InteractionRecord, crateparams::sNELinksPerGBT> mIRs{}; /// Interaction records per link - std::array<uint16_t, crateparams::sNELinksPerGBT> mCalibClocks{}; /// Calibration clock - std::array<uint16_t, crateparams::sNELinksPerGBT> mLastClock{}; /// Last clock per link + std::array<InteractionRecord, crateparams::sNELinksPerGBT> mIRs{}; /// Interaction records per link + std::array<uint16_t, crateparams::sNELinksPerGBT> mExpectedFETClock{}; /// Expected FET clock + std::array<uint16_t, crateparams::sNELinksPerGBT> mLastClock{}; /// Last clock per link - void addBoard(size_t ilink, const ELinkDecoder& decoder); - void addLoc(size_t ilink, const ELinkDecoder& decoder); + void addLoc(size_t ilink, const ELinkDecoder& decoder, EventType eventType, uint16_t correctedClock); bool checkLoc(size_t ilink, const ELinkDecoder& decoder); - bool updateIR(size_t ilink, const ELinkDecoder& decoder); - bool invertPattern(LocalBoardRO& loc); + EventType processCalibrationTrigger(size_t ilink, uint16_t localClock); + void processOrbitTrigger(size_t ilink, uint16_t localClock, uint8_t triggerWord); + EventType processSelfTriggered(size_t ilink, uint16_t localClock, uint16_t& correctedClock); + bool processTrigger(size_t ilink, const ELinkDecoder& decoder, EventType& eventType, uint16_t& correctedClock); }; } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicDecoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicDecoder.h deleted file mode 100644 index 42bb55a156698..0000000000000 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicDecoder.h +++ /dev/null @@ -1,66 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDRaw/GBTUserLogicDecoder.h -/// \brief MID GBT decoder with user logic zero suppression -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 15 April 2020 -#ifndef O2_MID_GBTUSERLOGICDECODER_H -#define O2_MID_GBTUSERLOGICDECODER_H - -#include <cstdint> -#include <vector> -#include <gsl/gsl> -#include "DetectorsRaw/RDHUtils.h" -#include "DataFormatsMID/ROFRecord.h" -#include "MIDRaw/ELinkDecoder.h" -#include "MIDRaw/GBTOutputHandler.h" -#include "MIDRaw/LocalBoardRO.h" - -namespace o2 -{ -namespace mid -{ -class GBTUserLogicDecoder -{ - public: - void init(uint16_t feeId, bool isDebugMode = false); - void process(gsl::span<const uint8_t> bytes, uint16_t bc, uint32_t orbit, uint16_t pageCnt); - template <typename RDH> - void process(gsl::span<const uint8_t> bytes, const RDH& rdh) - { - process(bytes, o2::raw::RDHUtils::getHeartBeatBC(rdh), o2::raw::RDHUtils::getHeartBeatOrbit(rdh), o2::raw::RDHUtils::getPageCounter(rdh)); - } - /// Gets the vector of data - const std::vector<LocalBoardRO>& getData() const { return mOutputHandler.getData(); } - - /// Gets the vector of data RO frame records - const std::vector<ROFRecord>& getROFRecords() const { return mOutputHandler.getROFRecords(); } - - /// Checks that the link has finished reading - bool isComplete() const { return mELinkDecoder.isComplete(); } - - /// Clears the decoded data - void clear() { mOutputHandler.clear(); } - - private: - GBTOutputHandler mOutputHandler{}; /// GBT output handler - - ELinkDecoder mELinkDecoder{}; /// E-link decoder - - typedef void (GBTOutputHandler::*OnDoneFunction)(size_t, const ELinkDecoder&); - - OnDoneFunction mOnDoneLoc{&GBTOutputHandler::onDoneLoc}; ///! Processes the local board - OnDoneFunction mOnDoneReg{&GBTOutputHandler::onDoneReg}; ///! Processes the regional board -}; -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_GBTUSERLOGICDECODER_H */ diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicEncoder.h b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicEncoder.h index 9e5c78872e919..aee2f00a2a278 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicEncoder.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/GBTUserLogicEncoder.h @@ -16,10 +16,11 @@ #define O2_MID_GBTUSERLOGICENCODER_H #include <cstdint> -#include <vector> +#include <map> #include <gsl/gsl> -#include "Headers/RAWDataHeader.h" +#include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsMID/ROFRecord.h" +#include "MIDRaw/ElectronicsDelay.h" #include "MIDRaw/LocalBoardRO.h" namespace o2 @@ -29,14 +30,13 @@ namespace mid class GBTUserLogicEncoder { public: - void process(gsl::span<const LocalBoardRO> data, const uint16_t bc, uint8_t triggerWord = 0); - void processTrigger(uint16_t bc, uint8_t triggerWord); + void process(gsl::span<const LocalBoardRO> data, const InteractionRecord& ir); + void processTrigger(const InteractionRecord& ir, uint8_t triggerWord); - /// Gets the buffer - const std::vector<uint8_t>& getBuffer() { return mBytes; } + void flush(std::vector<char>& buffer, const InteractionRecord& ir); - /// Gets the buffer size in bytes - size_t getBufferSize() const { return mBytes.size(); } + // Encoder has no data left + bool isEmpty() { return mBoards.empty(); } /// Sets the mask void setMask(uint8_t mask) { mMask = mask; } @@ -44,22 +44,17 @@ class GBTUserLogicEncoder /// Sets the feeID void setFeeId(uint16_t feeId) { mFeeId = feeId; } - /// Clears the buffer - void clear() { mBytes.clear(); } + /// Sets the delay in the electronics + void setElectronicsDelay(const ElectronicsDelay& electronicsDelay) { mElectronicsDelay = electronicsDelay; } private: - /// Adds the board id and the fired chambers - inline void addIdAndChambers(uint8_t id, uint8_t firedChambers) { mBytes.emplace_back((id << 4) | firedChambers); } + void addRegionalBoards(uint8_t activeBoards, InteractionRecord ir); + void addShort(std::vector<char>& buffer, uint16_t shortWord) const; - void addBoard(uint8_t statusWord, uint8_t triggerWord, uint16_t localClock, uint8_t id, uint8_t firedChambers); - void addLoc(const LocalBoardRO& loc, uint16_t bc, uint8_t triggerWord); - void addReg(uint16_t bc, uint8_t triggerWord, uint8_t id, uint8_t firedChambers); - void addShort(uint16_t shortWord); - bool checkAndAdd(gsl::span<const LocalBoardRO> data, uint16_t bc, uint8_t triggerWord); - - std::vector<uint8_t> mBytes{}; /// Vector with encoded information - uint16_t mFeeId{0}; /// FEE ID - uint8_t mMask{0xFF}; /// GBT mask + std::map<InteractionRecord, std::vector<LocalBoardRO>> mBoards{}; /// Vector with boards + uint16_t mFeeId{0}; /// FEE ID + uint8_t mMask{0xFF}; /// GBT mask + ElectronicsDelay mElectronicsDelay; /// Delays in the electronics }; } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/RawBuffer.h b/Detectors/MUON/MID/Raw/include/MIDRaw/RawBuffer.h deleted file mode 100644 index 6f7e8078d9134..0000000000000 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/RawBuffer.h +++ /dev/null @@ -1,72 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDRaw/RawBuffer.h -/// \brief Handler of the RAW buffer -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 28 November 2019 -#ifndef O2_MID_RAWBUFFER_H -#define O2_MID_RAWBUFFER_H - -#include <cstdint> -#include <vector> -#include <gsl/gsl> -#include "Headers/RAWDataHeader.h" - -namespace o2 -{ -namespace mid -{ -template <typename T> -class RawBuffer -{ - public: - enum class ResetMode { - all, // Reset buffer and indexes - keepUnconsumed, // Keep the last unconsumed HB - bufferOnly // Do not reset indexes - }; - - void setBuffer(gsl::span<const T> bytes, ResetMode resetMode = ResetMode::keepUnconsumed); - - /// Gets the current RDH - const header::RAWDataHeader* getRDH() { return mRDH; } - - unsigned int next(unsigned int nBits); - T next(); - - bool nextHeader(); - - bool hasNext(unsigned int nBytes); - - void skipOverhead(); - - bool isHBClosed(); - - private: - gsl::span<const T> mBytes{}; /// gsl span with encoded information - gsl::span<const T> mCurrentBuffer{}; /// gsl span with the current encoded information - std::vector<T> mUnconsumed{}; /// Unconsumed buffer - size_t mElementIndex{0}; /// Index of the current element in the buffer - size_t mBitIndex{0}; /// Index of the current bit - size_t mHeaderIndex{0}; /// Index of the current header - size_t mNextHeaderIndex{0}; /// Index of the next header - size_t mEndOfPayloadIndex{0}; /// Index of the end of payload - const unsigned int mElementSizeInBytes{sizeof(T)}; /// Element size in bytes - const unsigned int mElementSizeInBits{mElementSizeInBytes * 8}; /// Element size in bits - const header::RAWDataHeader* mRDH{nullptr}; /// Current header (not owner) - - bool nextPayload(); - void reset(); -}; -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_RAWBUFFER_H */ diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/RawFileReader.h b/Detectors/MUON/MID/Raw/include/MIDRaw/RawFileReader.h index 9f91f830cf254..0df1da74bc6c8 100644 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/RawFileReader.h +++ b/Detectors/MUON/MID/Raw/include/MIDRaw/RawFileReader.h @@ -15,48 +15,40 @@ #ifndef O2_MID_RAWFILEREADER_H #define O2_MID_RAWFILEREADER_H +#include <cstdint> #include <fstream> #include <vector> -#include <array> -#include "MIDRaw/CrateParameters.h" -#include "MIDRaw/RawBuffer.h" -#include "MIDRaw/RawUnit.h" +#include "Headers/RAWDataHeader.h" namespace o2 { namespace mid { -template <typename T> class RawFileReader { public: bool init(const char* inFilename, bool readContinuous = false); - bool readAllGBTs(bool reset = true); - bool readHB(bool sendCompleteHBs = true); + bool readHB(bool sendCompleteHBs = false); void clear(); - /// Gets the number of HBs read - unsigned long int getNumberOfHBs(uint16_t feeId) { return mHBCounters.at(feeId); } - /// Gets the state int getState() { return mState; } /// Gets the vector of data - const std::vector<T>& getData() { return mBytes; } + const std::vector<uint8_t>& getData() { return mBytes; } void setCustomRDH(const header::RAWDataHeader& rdh) { mCustomRDH = rdh; } + void setCustomPayloadSize(uint16_t memorySize = 0x2000, uint16_t offsetToNext = 0x2000); private: void read(size_t nBytes); - bool hasFullInfo(); bool replaceRDH(size_t headerIndex); - std::ifstream mFile{}; /// Raw file - std::vector<T> mBytes; /// Buffer - RawBuffer<T> mBuffer; /// Raw buffer handler - std::array<unsigned long int, crateparams::sNGBTs> mHBCounters{}; /// HB counters per GBT - bool mReadContinuous{false}; /// Continuous readout mode - int mState{0}; /// Status flag + std::ifstream mFile{}; /// Raw file + std::vector<uint8_t> mBytes; /// Buffer + static constexpr unsigned int sHeaderSize{64}; /// Header size in bytes + bool mReadContinuous{false}; /// Continuous readout mode + int mState{0}; /// Status flag header::RAWDataHeader mCustomRDH{}; /// Custom RDH }; diff --git a/Detectors/MUON/MID/Raw/include/MIDRaw/RawUnit.h b/Detectors/MUON/MID/Raw/include/MIDRaw/RawUnit.h deleted file mode 100644 index 0487a9fa55528..0000000000000 --- a/Detectors/MUON/MID/Raw/include/MIDRaw/RawUnit.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDRaw/RawUnit.h -/// \brief Raw data format MID -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 30 September 2019 -#ifndef O2_MID_RAWUNIT_H -#define O2_MID_RAWUNIT_H - -#include <cstdint> -#include <cstddef> -#include "Headers/RAWDataHeader.h" - -namespace o2 -{ -namespace mid -{ -namespace raw -{ -typedef uint32_t RawUnit; - -// Buffer size -static constexpr size_t sElementSizeInBytes = sizeof(RawUnit); -static constexpr size_t sMaxBufferSize = 8192 / sElementSizeInBytes; -static constexpr size_t sElementSizeInBits = 8 * sElementSizeInBytes; - -// Header size -static constexpr size_t sHeaderSizeInBytes = sizeof(header::RAWDataHeader); -static constexpr size_t sHeaderSizeInElements = sHeaderSizeInBytes / sElementSizeInBytes; - -} // namespace raw -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_RAWUNIT_H */ \ No newline at end of file diff --git a/Detectors/MUON/MID/Raw/src/CRUBareDecoder.cxx b/Detectors/MUON/MID/Raw/src/CRUBareDecoder.cxx deleted file mode 100644 index 3ec0291141401..0000000000000 --- a/Detectors/MUON/MID/Raw/src/CRUBareDecoder.cxx +++ /dev/null @@ -1,152 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Raw/src/CRUBareDecoder.cxx -/// \brief MID CRU core decoder -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 30 September 2019 - -#include "MIDRaw/CRUBareDecoder.h" - -#include "RawInfo.h" -#include "DetectorsRaw/RDHUtils.h" - -namespace o2 -{ -namespace mid -{ -using RDHUtils = o2::raw::RDHUtils; - -void CRUBareDecoder::reset() -{ - /// Rewind bytes - mData.clear(); - mROFRecords.clear(); -} - -void CRUBareDecoder::init(bool debugMode) -{ - /// Initializes the task - if (debugMode == true) { - mAddReg = std::bind(&CRUBareDecoder::addBoard, this, std::placeholders::_1); - mCheckBoard = [](size_t) { return true; }; - } -} - -void CRUBareDecoder::process(gsl::span<const uint8_t> bytes) -{ - /// Decodes the buffer - reset(); - - mBuffer.setBuffer(bytes); - - // Each CRU word consists of 128 bits, i.e 16 bytes - while (mBuffer.hasNext(16)) { - processGBT(0); - processGBT(5); - // The GBT word consists of 10 bytes - // but the CRU word is 16 bytes - // So we have 6 empty bytes - for (int iword = 0; iword < 6; ++iword) { - mBuffer.next(); - } - } -} - -void CRUBareDecoder::processGBT(size_t offset) -{ - /// Processes the GBT - - // Regional link - // loc#0 loc#1 loc#2 loc#3 reg - std::array<uint8_t, 5> bytes{mBuffer.next(), mBuffer.next(), mBuffer.next(), mBuffer.next(), mBuffer.next()}; - - // Byte corresponding to regional - size_t ibyte = 4; - size_t ilink = offset + ibyte; - - if (mELinkDecoders[ilink].add(bytes[ibyte], 0x80) && mELinkDecoders[ilink].isComplete()) { - // In principle, in the same HB we should have the info of only 1 crate - // So it should be safe to store this information - // and apply it to all subsequent e-links, without checking the clock - mCrateId = mELinkDecoders[ilink].getId(); - mAddReg(ilink); - mELinkDecoders[ilink].reset(); - } - - // local links - for (ibyte = 0; ibyte < 4; ++ibyte) { - ilink = offset + ibyte; - if (mELinkDecoders[ilink].add(bytes[ibyte], 0xc0) && mELinkDecoders[ilink].isComplete()) { - if (mCheckBoard(ilink)) { - addLoc(ilink); - } - mELinkDecoders[ilink].reset(); - } - } -} - -bool CRUBareDecoder::checkBoard(size_t ilink) -{ - /// Performs checks on the board - uint8_t expectedId = ilink - (ilink / 5); - return (expectedId == mELinkDecoders[ilink].getId() % 8); -} - -void CRUBareDecoder::addBoard(size_t ilink) -{ - /// Adds the local or regional board to the output data vector - uint16_t localClock = mELinkDecoders[ilink].getCounter(); - EventType eventType = EventType::Standard; - if (mELinkDecoders[ilink].getTriggerWord() & (1 << 4)) { - mCalibClocks[ilink] = localClock; - eventType = EventType::Noise; - } else if (localClock == mCalibClocks[ilink] + sDelayCalibToFET) { - eventType = EventType::Dead; - } - auto firstEntry = mData.size(); - InteractionRecord intRec(localClock - sDelayBCToLocal, RDHUtils::getTriggerOrbit(*mBuffer.getRDH())); - mData.push_back({mELinkDecoders[ilink].getStatusWord(), mELinkDecoders[ilink].getTriggerWord(), crateparams::makeUniqueLocID(mCrateId, mELinkDecoders[ilink].getId()), mELinkDecoders[ilink].getInputs()}); - mROFRecords.emplace_back(intRec, eventType, firstEntry, 1); -} - -void CRUBareDecoder::addLoc(size_t ilink) -{ - /// Adds the local board to the output data vector - addBoard(ilink); - bool invert = (mROFRecords.back().eventType == EventType::Dead); - for (int ich = 0; ich < 4; ++ich) { - if ((mData.back().firedChambers & (1 << ich))) { - mData.back().patternsBP[ich] = getPattern(mELinkDecoders[ilink].getPattern(0, ich), invert); - mData.back().patternsNBP[ich] = getPattern(mELinkDecoders[ilink].getPattern(1, ich), invert); - } - } - mELinkDecoders[ilink].reset(); -} - -uint16_t CRUBareDecoder::getPattern(uint16_t pattern, bool invert) const -{ - /// Gets the proper pattern - return (invert) ? ~pattern : pattern; -} - -bool CRUBareDecoder::isComplete() const -{ - /// Checks that all links have finished reading - for (auto& elink : mELinkDecoders) { - if (elink.getNBytes() > 0) { - return false; - } - } - return true; -} - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/ColumnDataToLocalBoard.cxx b/Detectors/MUON/MID/Raw/src/ColumnDataToLocalBoard.cxx index 7c85f66e197c6..539f65542d0e5 100644 --- a/Detectors/MUON/MID/Raw/src/ColumnDataToLocalBoard.cxx +++ b/Detectors/MUON/MID/Raw/src/ColumnDataToLocalBoard.cxx @@ -23,6 +23,16 @@ namespace o2 namespace mid { +bool ColumnDataToLocalBoard::keepBoard(const LocalBoardRO& loc) const +{ + for (int ich = 0; ich < 4; ++ich) { + if (loc.patternsBP[ich] && loc.patternsNBP[ich]) { + return true; + } + } + return false; +} + void ColumnDataToLocalBoard::process(gsl::span<const ColumnData> data) { /// Converts incoming data to FEE format @@ -37,7 +47,7 @@ void ColumnDataToLocalBoard::process(gsl::span<const ColumnData> data) auto uniqueLocId = mCrateMapper.deLocalBoardToRO(col.deId, col.columnId, iline); auto& roData = mLocalBoardsMap[uniqueLocId]; roData.statusWord = raw::sSTARTBIT | raw::sCARDTYPE; - roData.boardId = crateparams::getLocId(uniqueLocId); + roData.boardId = uniqueLocId; int ich = detparams::getChamber(col.deId); roData.firedChambers |= (1 << ich); roData.patternsBP[ich] = col.getBendPattern(iline); @@ -48,9 +58,11 @@ void ColumnDataToLocalBoard::process(gsl::span<const ColumnData> data) // Then group the boards belonging to the same GBT link for (auto& item : mLocalBoardsMap) { - auto crateId = crateparams::getCrateId(item.first); - auto feeId = crateparams::makeROId(crateId, crateparams::getGBTIdFromBoardInCrate(item.second.boardId)); - mGBTMap[feeId].emplace_back(item.second); + if (mDebugMode || keepBoard(item.second)) { + auto crateId = crateparams::getCrateId(item.first); + auto feeId = crateparams::makeROId(crateId, crateparams::getGBTIdFromBoardInCrate(crateparams::getLocId(item.second.boardId))); + mGBTMap[feeId].emplace_back(item.second); + } } } diff --git a/Detectors/MUON/MID/Raw/src/CrateMapper.cxx b/Detectors/MUON/MID/Raw/src/CrateMapper.cxx index a691669da7657..6ad86e3680076 100644 --- a/Detectors/MUON/MID/Raw/src/CrateMapper.cxx +++ b/Detectors/MUON/MID/Raw/src/CrateMapper.cxx @@ -130,7 +130,7 @@ void CrateMapper::init() } } -uint16_t CrateMapper::deLocalBoardToRO(uint8_t deId, uint8_t columnId, uint8_t lineId) const +uint8_t CrateMapper::deLocalBoardToRO(uint8_t deId, uint8_t columnId, uint8_t lineId) const { /// Converts the local board ID in in MT11 right to the local board ID in FEE auto item = mDEToROMap.find(deBoardId(detparams::getRPCLine(deId), columnId, lineId)); diff --git a/Detectors/MUON/MID/Raw/src/CrateMasks.cxx b/Detectors/MUON/MID/Raw/src/CrateMasks.cxx index c247765569827..7a03728310ac9 100644 --- a/Detectors/MUON/MID/Raw/src/CrateMasks.cxx +++ b/Detectors/MUON/MID/Raw/src/CrateMasks.cxx @@ -94,7 +94,7 @@ bool CrateMasks::load(const char* filename) std::getline(ss, token, ' '); uint16_t feeId = std::atoi(token.c_str()); std::getline(ss, token, ' '); - uint8_t mask = std::atoi(token.c_str()); + uint8_t mask = static_cast<uint8_t>(std::strtol(token.c_str(), nullptr, 16)); mActiveBoards[feeId] = mask; } inFile.close(); diff --git a/Detectors/MUON/MID/Raw/src/Decoder.cxx b/Detectors/MUON/MID/Raw/src/Decoder.cxx index 5cd6b8491e98e..f29d4cb4dde88 100644 --- a/Detectors/MUON/MID/Raw/src/Decoder.cxx +++ b/Detectors/MUON/MID/Raw/src/Decoder.cxx @@ -15,7 +15,7 @@ #include "MIDRaw/Decoder.h" -#include "Headers/RAWDataHeader.h" +#include "Headers/RDHAny.h" #include "DPLUtils/RawParser.h" namespace o2 @@ -23,36 +23,38 @@ namespace o2 namespace mid { -template <typename GBTDECODER> -Decoder<GBTDECODER>::Decoder() : mData(), mROFRecords(), mGBTDecoders(), mFEEIdConfig(), mMasks() +// namespace impl +// { +class FEEIDGetterImpl { - /// Default constructor - init(); + public: + FEEIDGetterImpl(const FEEIdConfig& feeIdConfig) : mFeeIdConfig(feeIdConfig) {} + uint16_t operator()(const o2::header::RDHAny& rdh) { return mFeeIdConfig.getFeeId(o2::raw::RDHUtils::getLinkID(rdh), o2::raw::RDHUtils::getEndPointID(rdh), o2::raw::RDHUtils::getCRUID(rdh)); } + + private: + FEEIdConfig mFeeIdConfig{}; +}; +// } // namespace impl + +Decoder::Decoder(bool isDebugMode, bool isBare, const ElectronicsDelay& electronicsDelay, const CrateMasks& crateMasks, const FEEIdConfig& feeIdConfig) : mData(), mROFRecords(), mGBTDecoders() +{ + /// Constructor + for (uint16_t igbt = 0; igbt < crateparams::sNGBTs; ++igbt) { + mGBTDecoders[igbt] = createGBTDecoder(igbt, isBare, isDebugMode, crateMasks.getMask(igbt), electronicsDelay); + } + if (isBare) { + mGetFEEID = FEEIDGetterImpl(feeIdConfig); + } } -template <typename GBTDECODER> -void Decoder<GBTDECODER>::clear() +void Decoder::clear() { /// Clears the decoded data mData.clear(); mROFRecords.clear(); } -template <typename GBTDECODER> -void Decoder<GBTDECODER>::init(bool isDebugMode) -{ - /// Initializes the decoder - for (uint16_t igbt = 0; igbt < crateparams::sNGBTs; ++igbt) { - if constexpr (std::is_same_v<GBTDECODER, GBTBareDecoder>) { - mGBTDecoders[igbt].init(igbt, mMasks.getMask(igbt), isDebugMode); - } else { - mGBTDecoders[igbt].init(igbt, isDebugMode); - } - } -} - -template <typename GBTDECODER> -void Decoder<GBTDECODER>::process(gsl::span<const uint8_t> bytes) +void Decoder::process(gsl::span<const uint8_t> bytes) { /// Decodes the buffer clear(); @@ -65,41 +67,37 @@ void Decoder<GBTDECODER>::process(gsl::span<const uint8_t> bytes) auto const* rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(it.raw()); process(payload, *rdhPtr); } - flush(); } -template <typename GBTDECODER> -void Decoder<GBTDECODER>::flush() +std::unique_ptr<Decoder> createDecoder(const o2::header::RDHAny& rdh, bool isDebugMode, ElectronicsDelay& electronicsDelay, const CrateMasks& crateMasks, const FEEIdConfig& feeIdConfig) { - /// Flushes the GBT data - for (auto& gbtDec : mGBTDecoders) { - if (!gbtDec.getData().empty()) { - size_t firstEntry = mData.size(); - mData.insert(mData.end(), gbtDec.getData().begin(), gbtDec.getData().end()); - size_t lastRof = mROFRecords.size(); - mROFRecords.insert(mROFRecords.end(), gbtDec.getROFRecords().begin(), gbtDec.getROFRecords().end()); - for (auto rofIt = mROFRecords.begin() + lastRof; rofIt != mROFRecords.end(); ++rofIt) { - rofIt->firstEntry += firstEntry; - } - gbtDec.clear(); - } - } + /// Creates the decoder from the RDH info + bool isBare = (o2::raw::RDHUtils::getLinkID(rdh) != raw::sUserLogicLinkID); + return std::make_unique<Decoder>(isDebugMode, isBare, electronicsDelay, crateMasks, feeIdConfig); } - -template <typename GBTDECODER> -bool Decoder<GBTDECODER>::isComplete() const +std::unique_ptr<Decoder> createDecoder(const o2::header::RDHAny& rdh, bool isDebugMode, const char* electronicsDelayFile, const char* crateMasksFile, const char* feeIdConfigFile) { - /// Checks that all links have finished reading - for (auto& decoder : mGBTDecoders) { - if (!decoder.isComplete()) { - return false; - } + /// Creates the decoder from the RDH info + o2::mid::ElectronicsDelay electronicsDelay; + std::string filename = electronicsDelayFile; + if (!filename.empty()) { + electronicsDelay = o2::mid::readElectronicsDelay(filename.c_str()); } - return true; -} -template class Decoder<GBTBareDecoder>; -template class Decoder<GBTUserLogicDecoder>; + o2::mid::CrateMasks crateMasks; + filename = crateMasksFile; + if (!filename.empty()) { + crateMasks = o2::mid::CrateMasks(filename.c_str()); + } + + o2::mid::FEEIdConfig feeIdConfig; + filename = feeIdConfigFile; + if (!filename.empty()) { + feeIdConfig = o2::mid::FEEIdConfig(filename.c_str()); + } + + return createDecoder(rdh, isDebugMode, electronicsDelay, crateMasks, feeIdConfig); +} } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/ELinkDecoder.cxx b/Detectors/MUON/MID/Raw/src/ELinkDecoder.cxx index 9ec3c7b9d3c73..c18234952963f 100644 --- a/Detectors/MUON/MID/Raw/src/ELinkDecoder.cxx +++ b/Detectors/MUON/MID/Raw/src/ELinkDecoder.cxx @@ -15,39 +15,66 @@ #include "MIDRaw/ELinkDecoder.h" -#include "MIDRaw/LocalBoardRO.h" - namespace o2 { namespace mid { -void ELinkDecoder::add(uint8_t byte) +bool ELinkDecoder::add(size_t& idx, gsl::span<const uint8_t> payload, size_t nBytes, size_t step) { - /// Adds next byte - mBytes.emplace_back(byte); - if (mBytes.size() == sMinimumSize) { - if (raw::isLoc(mBytes[0])) { - // This is a local card - uint8_t mask = getInputs(); - for (int ich = 0; ich < 4; ++ich) { - if ((mask >> ich) & 0x1) { - // We expect 2 bytes for the BP and 2 for the NBP - mTotalSize += 4; - } + /// Fills inner bytes vector + auto size = payload.size(); + auto end = idx + step * nBytes; + if (size < end) { + end = size; + } + size_t nAdded = 0; + for (; idx < end; idx += step) { + mBytes.emplace_back(payload[idx]); + ++nAdded; + } + return (nAdded == nBytes); +} + +bool ELinkDecoder::add(size_t& idx, gsl::span<const uint8_t> payload, size_t step) +{ + /// Adds the bytes of the board + auto remaining = mTotalSize - mBytes.size(); + if (add(idx, payload, remaining, step)) { + if (mTotalSize == sMinimumSize) { + computeSize(); + remaining = mTotalSize - mBytes.size(); + if (remaining) { + return add(idx, payload, remaining, step); } } + return true; } + return false; } -bool ELinkDecoder::add(uint8_t byte, uint8_t expectedStart) +void ELinkDecoder::addAndComputeSize(uint8_t byte) { - /// Adds next byte, checking the first one - if (mBytes.empty() && (byte & 0xc0) != expectedStart) { - return false; + /// Adds next byte and computes the expected data size + mBytes.emplace_back(byte); + if (mBytes.size() == sMinimumSize) { + computeSize(); + } +} + +void ELinkDecoder::computeSize() +{ + /// Computes the board size + if (raw::isLoc(mBytes[0])) { + // This is a local card + uint8_t mask = getInputs(); + for (int ich = 0; ich < 4; ++ich) { + if ((mask >> ich) & 0x1) { + // We expect 2 bytes for the BP and 2 for the NBP + mTotalSize += 4; + } + } } - add(byte); - return true; } void ELinkDecoder::reset() diff --git a/Detectors/MUON/MID/Raw/src/ElectronicsDelay.cxx b/Detectors/MUON/MID/Raw/src/ElectronicsDelay.cxx new file mode 100644 index 0000000000000..48a7955e2ef5d --- /dev/null +++ b/Detectors/MUON/MID/Raw/src/ElectronicsDelay.cxx @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDRaw/ElectronicsDelay.cxx +/// \brief Delay parameters for MID electronics +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 27 July 2020 + +#include "MIDRaw/ElectronicsDelay.h" + +#include <algorithm> +#include <fstream> +#include <string> + +namespace o2 +{ +namespace mid +{ + +std::ostream& operator<<(std::ostream& os, const ElectronicsDelay& delay) +{ + /// Output streamer for ElectronicsDelay + os << "calibToFET: " << delay.calibToFET << "\n"; + os << "BCToLocal: " << delay.BCToLocal << "\n"; + os << "regToLocal: " << delay.regToLocal << "\n"; + return os; +} + +ElectronicsDelay readElectronicsDelay(const char* filename) +{ + /// Reads the electronic delays from file + ElectronicsDelay electronicsDelay; + std::ifstream inFile(filename); + if (inFile.is_open()) { + std::string line; + while (std::getline(inFile, line)) { + line.erase(std::remove_if(line.begin(), line.end(), [](unsigned char x) { return std::isspace(x); }), line.end()); + auto pos = line.find(":"); + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + uint16_t val = std::atoi(line.substr(pos + 1).c_str()); + if (key == "calibToFET") { + electronicsDelay.calibToFET = val; + } else if (key == "BCToLocal") { + electronicsDelay.BCToLocal = val; + } else if (key == "regToLocal") { + electronicsDelay.regToLocal = val; + } + } + } + } else { + std::cout << "Error: cannot open file " << filename << std::endl; + } + return electronicsDelay; +} + +} // namespace mid +} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/Encoder.cxx b/Detectors/MUON/MID/Raw/src/Encoder.cxx index c1b7de3a5a0c0..97aa55913797e 100644 --- a/Detectors/MUON/MID/Raw/src/Encoder.cxx +++ b/Detectors/MUON/MID/Raw/src/Encoder.cxx @@ -18,13 +18,14 @@ #include "DetectorsRaw/HBFUtils.h" #include "DetectorsRaw/RDHUtils.h" #include "MIDRaw/CrateMasks.h" +#include <fmt/format.h> namespace o2 { namespace mid { -void Encoder::init(const char* filename, int verbosity) +void Encoder::init(const char* filename, bool perLink, int verbosity, bool debugMode) { /// Initializes links @@ -32,62 +33,89 @@ void Encoder::init(const char* filename, int verbosity) auto gbtIds = mFEEIdConfig.getConfiguredGBTIds(); mRawWriter.setVerbosity(verbosity); + int lcnt = 0; for (auto& gbtId : gbtIds) { auto feeId = mFEEIdConfig.getFeeId(gbtId); - mRawWriter.registerLink(feeId, mFEEIdConfig.getCRUId(gbtId), mFEEIdConfig.getLinkId(gbtId), mFEEIdConfig.getEndPointId(gbtId), filename); + mRawWriter.registerLink(feeId, mFEEIdConfig.getCRUId(gbtId), raw::sUserLogicLinkID, mFEEIdConfig.getEndPointId(gbtId), perLink ? fmt::format("{:s}_L{:d}.raw", filename, lcnt) : fmt::format("{:s}.raw", filename)); mGBTEncoders[feeId].setFeeId(feeId); mGBTEncoders[feeId].setMask(masks.getMask(feeId)); mGBTIds[feeId] = gbtId; + lcnt++; + + // Initializes the trigger response to be added to the empty HBs + auto ir = getOrbitIR(0); + mGBTEncoders[feeId].processTrigger(ir, raw::sORB); + mGBTEncoders[feeId].flush(mOrbitResponse[feeId], ir); + mOrbitResponseWord[feeId] = mOrbitResponse[feeId]; + completeWord(mOrbitResponseWord[feeId]); } + + mRawWriter.setEmptyPageCallBack(this); + + mConverter.setDebugMode(debugMode); } -void Encoder::hbTrigger(const InteractionRecord& ir) +void Encoder::emptyHBFMethod(const o2::header::RDHAny* rdh, std::vector<char>& toAdd) const { - /// Processes HB trigger - if (mLastIR.isDummy()) { - // This is the first HB - for (uint16_t feeId = 0; feeId < crateparams::sNGBTs; ++feeId) { - mGBTEncoders[feeId].processTrigger(o2::constants::lhc::LHCMaxBunches, raw::sORB); + /// Response to orbit triggers in empty HBFs + auto feeId = o2::raw::RDHUtils::getFEEID(rdh); + toAdd = mOrbitResponseWord[feeId]; +} + +void Encoder::onOrbitChange(uint32_t orbit) +{ + /// Performs action when orbit changes + auto ir = getOrbitIR(orbit); + for (uint16_t feeId = 0; feeId < crateparams::sNGBTs; ++feeId) { + // Write the data corresponding to the previous orbit + if (!mGBTEncoders[feeId].isEmpty()) { + writePayload(feeId, ir); } - mLastIR = o2::raw::HBFUtils::Instance().getFirstIR(); - return; } +} - std::vector<InteractionRecord> HBIRVec; - o2::raw::HBFUtils::Instance().fillHBIRvector(HBIRVec, mLastIR + 1, ir); - for (auto& hbIr : HBIRVec) { - for (uint16_t feeId = 0; feeId < crateparams::sNGBTs; ++feeId) { - flush(feeId, mLastIR); - mGBTEncoders[feeId].processTrigger(o2::constants::lhc::LHCMaxBunches, raw::sORB); - } - mLastIR = hbIr; +void Encoder::completeWord(std::vector<char>& buffer) +{ + /// Completes the buffer with zeros to reach the expected CRU word size + size_t dataSize = buffer.size(); + size_t cruWord = 2 * o2::raw::RDHUtils::GBTWord; + size_t modulo = dataSize % cruWord; + if (modulo) { + dataSize += cruWord - modulo; + buffer.resize(dataSize, static_cast<char>(0)); } - mLastIR = ir; } -void Encoder::flush(uint16_t feeId, const InteractionRecord& ir) +void Encoder::writePayload(uint16_t feeId, const InteractionRecord& ir) { - /// Flushes data + /// Writes data - if (mGBTEncoders[feeId].getBufferSize() == 0) { - return; - } - size_t dataSize = mGBTEncoders[feeId].getBufferSize(); - size_t resto = dataSize % o2::raw::RDHUtils::GBTWord; - if (dataSize % o2::raw::RDHUtils::GBTWord) { - dataSize += o2::raw::RDHUtils::GBTWord - resto; - } - std::vector<char> buf(dataSize); - memcpy(buf.data(), mGBTEncoders[feeId].getBuffer().data(), mGBTEncoders[feeId].getBufferSize()); - mRawWriter.addData(feeId, mFEEIdConfig.getCRUId(mGBTIds[feeId]), mFEEIdConfig.getLinkId(mGBTIds[feeId]), mFEEIdConfig.getEndPointId(mGBTIds[feeId]), ir, buf); - mGBTEncoders[feeId].clear(); + // The first part of data is the answer to the orbit trigger + std::vector<char> buf = mOrbitResponse[feeId]; + // Then we flush the received data + mGBTEncoders[feeId].flush(buf, ir); + completeWord(buf); + mRawWriter.addData(feeId, mFEEIdConfig.getCRUId(mGBTIds[feeId]), raw::sUserLogicLinkID, mFEEIdConfig.getEndPointId(mGBTIds[feeId]), ir, buf); } void Encoder::finalize(bool closeFile) { - /// Finish the flushing and closes the + /// Writes remaining data and closes the file + if (mLastIR.isDummy()) { + mLastIR.bc = mRawWriter.getHBFUtils().bcFirst; + mLastIR.orbit = mRawWriter.getHBFUtils().orbitFirst; + } + auto ir = getOrbitIR(mLastIR.orbit); for (uint16_t feeId = 0; feeId < crateparams::sNGBTs; ++feeId) { - flush(feeId, mLastIR); + // Write the last payload + writePayload(feeId, ir); + if (!mGBTEncoders[feeId].isEmpty()) { + // Since the regional response comes after few clocks, + // we might have the corresponding regional cards in the next orbit. + // If this is the case, we flush all data of the next orbit + ++ir.orbit; + writePayload(feeId, ir); + } } if (closeFile) { mRawWriter.close(); @@ -97,13 +125,16 @@ void Encoder::finalize(bool closeFile) void Encoder::process(gsl::span<const ColumnData> data, const InteractionRecord& ir, EventType eventType) { /// Encodes data - hbTrigger(ir); + if (ir.orbit != mLastIR.orbit) { + onOrbitChange(mLastIR.orbit); + } mConverter.process(data); for (auto& item : mConverter.getData()) { - mGBTEncoders[item.first].process(item.second, ir.bc); + mGBTEncoders[item.first].process(item.second, ir); } + mLastIR = ir; } } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/GBTBareDecoder.cxx b/Detectors/MUON/MID/Raw/src/GBTBareDecoder.cxx deleted file mode 100644 index 13d255cef1998..0000000000000 --- a/Detectors/MUON/MID/Raw/src/GBTBareDecoder.cxx +++ /dev/null @@ -1,107 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Raw/src/GBTBareDecoder.cxx -/// \brief MID GBT decoder without user logic -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 12 March 2020 - -#include "MIDRaw/GBTBareDecoder.h" - -namespace o2 -{ -namespace mid -{ - -void GBTBareDecoder::init(uint16_t feeId, uint8_t mask, bool isDebugMode) -{ - /// Initializes the task - mOutputHandler.setFeeId(feeId); - mMask = mask; - if (isDebugMode) { - mOnDoneLoc = &GBTOutputHandler::onDoneLocDebug; - mProcessReg = &GBTBareDecoder::processRegDebug; - } -} - -void GBTBareDecoder::process(gsl::span<const uint8_t> bytes, uint16_t bc, uint32_t orbit, uint16_t pageCnt) -{ - /// Decodes the buffer - mOutputHandler.setIR(bc, orbit, pageCnt); - - uint8_t byte = 0; - size_t ilink = 0, linkMask = 0, byteOffset = 0; - - for (size_t idx = 0; idx < bytes.size(); idx += 16) { - for (int ireg = 0; ireg < 2; ++ireg) { - byteOffset = idx + 5 * ireg; - for (int ib = 0; ib < 4; ++ib) { - byte = bytes[byteOffset + ib]; - ilink = ib + 4 * ireg; - linkMask = (1 << ilink); - if ((mMask & linkMask) && ((mIsFeeding & linkMask) || byte)) { - processLoc(ilink, byte); - } - } // loop on locs - byte = bytes[byteOffset + 4]; - ilink = 8 + ireg; - linkMask = (1 << ilink); - if ((mIsFeeding & linkMask) || byte) { - std::invoke(mProcessReg, this, ilink, byte); - } - } // loop on half regional - } // loop on buffer index -} - -void GBTBareDecoder::processLoc(size_t ilink, uint8_t byte) -{ - /// Processes the local board information - if (mELinkDecoders[ilink].getNBytes() > 0) { - mELinkDecoders[ilink].add(byte); - if (mELinkDecoders[ilink].isComplete()) { - std::invoke(mOnDoneLoc, mOutputHandler, ilink, mELinkDecoders[ilink]); - mELinkDecoders[ilink].reset(); - mIsFeeding &= (~(1 << ilink)); - } - } else if ((byte & (raw::sSTARTBIT | raw::sCARDTYPE)) == (raw::sSTARTBIT | raw::sCARDTYPE)) { - mELinkDecoders[ilink].add(byte); - mIsFeeding |= (1 << ilink); - } -} - -void GBTBareDecoder::processRegDebug(size_t ilink, uint8_t byte) -{ - /// Processes the regional board information in debug mode - if (mELinkDecoders[ilink].getNBytes() > 0) { - mELinkDecoders[ilink].add(byte); - if (mELinkDecoders[ilink].isComplete()) { - mOutputHandler.onDoneRegDebug(ilink, mELinkDecoders[ilink]); - mELinkDecoders[ilink].reset(); - mIsFeeding &= (~(1 << ilink)); - } - } else if (byte & raw::sSTARTBIT) { - mELinkDecoders[ilink].add(byte); - mIsFeeding |= (1 << ilink); - } -} - -bool GBTBareDecoder::isComplete() const -{ - /// Checks that all links have finished reading - for (auto& elink : mELinkDecoders) { - if (elink.getNBytes() > 0) { - return false; - } - } - return true; -} - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/GBTDecoder.cxx b/Detectors/MUON/MID/Raw/src/GBTDecoder.cxx new file mode 100644 index 0000000000000..b3c5a84058651 --- /dev/null +++ b/Detectors/MUON/MID/Raw/src/GBTDecoder.cxx @@ -0,0 +1,362 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/Raw/src/GBTDecoder.cxx +/// \brief Class interface for the MID GBT decoder +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 07 November 2020 + +#include "MIDRaw/GBTDecoder.h" + +#include "MIDRaw/FEEIdConfig.h" +#include "MIDRaw/GBTOutputHandler.h" +#include "MIDRaw/LocalBoardRO.h" + +namespace o2 +{ +namespace mid +{ +namespace impl +{ +class GBTUserLogicDecoderImpl +{ + public: + GBTUserLogicDecoderImpl(uint16_t feeId, bool isDebugMode = false, uint8_t mask = 0xFF, const ElectronicsDelay& electronicsDelay = ElectronicsDelay()) + { + mOutputHandler.setFeeId(feeId); + mOutputHandler.setElectronicsDelay(electronicsDelay); + if (isDebugMode) { + mOnDoneLoc = &GBTOutputHandler::onDoneLocDebug; + mOnDoneReg = &GBTOutputHandler::onDoneRegDebug; + } + } + + void operator()(gsl::span<const uint8_t> payload, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) + { + /// Decodes the buffer + mOutputHandler.set(orbit, data, rofs); + // for (auto& byte : payload) { + auto it = payload.begin(); + auto end = payload.end(); + while (it != end) { + if (mELinkDecoder.isZero(*it)) { + // The e-link decoder is empty, meaning that we expect a new board. + // The first byte of the board should have the STARTBIT on. + // If this is not the case, we move to the next byte. + // Notice that the payload has zeros until the end of the 256 bits word: + // a) when moving from the regional high to regional low + // b) at the end of the HBF + ++it; + continue; + } + if (mELinkDecoder.add(it, end)) { + if (raw::isLoc(mELinkDecoder.getStatusWord())) { + std::invoke(mOnDoneLoc, mOutputHandler, mELinkDecoder.getId() % 8, mELinkDecoder); + } else { + size_t ilink = 8 + mELinkDecoder.getId() % 8; + if (ilink <= 9) { + std::invoke(mOnDoneReg, mOutputHandler, ilink, mELinkDecoder); + } + } + mELinkDecoder.reset(); + } + } + } + + private: + GBTOutputHandler mOutputHandler{}; /// GBT output handler + ELinkDecoder mELinkDecoder{}; /// E-link decoder + + typedef void (GBTOutputHandler::*OnDoneFunction)(size_t, const ELinkDecoder&); + + OnDoneFunction mOnDoneLoc{&GBTOutputHandler::onDoneLoc}; ///! Processes the local board + OnDoneFunction mOnDoneReg{&GBTOutputHandler::onDoneReg}; ///! Processes the regional board +}; + +class GBTBareDecoderImpl +{ + public: + GBTBareDecoderImpl(uint16_t feeId, bool isDebugMode = false, uint8_t mask = 0xFF, const ElectronicsDelay& electronicsDelay = ElectronicsDelay()) : mIsDebugMode(isDebugMode), mMask(mask) + { + /// Constructor + mOutputHandler.setFeeId(feeId); + mOutputHandler.setElectronicsDelay(electronicsDelay); + if (isDebugMode) { + mOnDoneLoc = &GBTOutputHandler::onDoneLocDebug; + } + } + + void operator()(gsl::span<const uint8_t> payload, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) + { + /// Decodes the buffer + mOutputHandler.set(orbit, data, rofs); + + uint8_t byte = 0; + size_t ilink = 0, linkMask = 0, byteOffset = 0; + + for (int ireg = 0; ireg < 2; ++ireg) { + byteOffset = 5 * ireg; + if (mIsDebugMode) { + ilink = 8 + ireg; + linkMask = (1 << ilink); + for (size_t idx = byteOffset + 4; idx < payload.size(); idx += 16) { + byte = payload[idx]; + if ((mIsFeeding & linkMask) || byte) { + processRegDebug(ilink, byte); + } + } + } + for (int ib = 0; ib < 4; ++ib) { + ilink = ib + 4 * ireg; + linkMask = (1 << ilink); + if (mMask & linkMask) { + for (size_t idx = byteOffset + ib; idx < payload.size(); idx += 16) { + byte = payload[idx]; + if ((mIsFeeding & linkMask) || byte) { + processLoc(ilink, byte); + } + } + } + } + } + } + + private: + void processLoc(size_t ilink, uint8_t byte) + { + /// Processes the local board information + if (mELinkDecoders[ilink].getNBytes() > 0) { + mELinkDecoders[ilink].addAndComputeSize(byte); + if (mELinkDecoders[ilink].isComplete()) { + std::invoke(mOnDoneLoc, mOutputHandler, ilink, mELinkDecoders[ilink]); + mELinkDecoders[ilink].reset(); + mIsFeeding &= (~(1 << ilink)); + } + } else if ((byte & (raw::sSTARTBIT | raw::sCARDTYPE)) == (raw::sSTARTBIT | raw::sCARDTYPE)) { + mELinkDecoders[ilink].add(byte); + mIsFeeding |= (1 << ilink); + } + } + + void processRegDebug(size_t ilink, uint8_t byte) + { + /// Processes the regional board information in debug mode + if (mELinkDecoders[ilink].getNBytes() > 0) { + mELinkDecoders[ilink].add(byte); + if (mELinkDecoders[ilink].isComplete()) { + mOutputHandler.onDoneRegDebug(ilink, mELinkDecoders[ilink]); + mELinkDecoders[ilink].reset(); + mIsFeeding &= (~(1 << ilink)); + } + } else if (byte & raw::sSTARTBIT) { + mELinkDecoders[ilink].add(byte); + mIsFeeding |= (1 << ilink); + } + } + + bool mIsDebugMode{false}; /// Debug mode + uint8_t mMask{0xFF}; /// GBT mask + uint16_t mIsFeeding{0}; /// Flag to check if the e-link is feeding + GBTOutputHandler mOutputHandler{}; /// GBT output handler + + std::array<ELinkDecoder, crateparams::sNELinksPerGBT> mELinkDecoders{}; /// E-link decoders + + typedef void (GBTOutputHandler::*OnDoneFunction)(size_t, const ELinkDecoder&); + + OnDoneFunction mOnDoneLoc{&GBTOutputHandler::onDoneLoc}; ///! Processes the local board +}; + +/// Alternative bare decoder implementation +/// Data are first ranged per link and then each link is decoded in a similar way to what is done for the user logic +/// CAVEAT: abandoned since filling the vector per link is much slower +/// Kept here for future reference (parallelization?) +class GBTBareDecoderLinkImpl +{ + public: + GBTBareDecoderLinkImpl(uint16_t feeId, bool isDebugMode = false, uint8_t mask = 0xFF, const ElectronicsDelay& electronicsDelay = ElectronicsDelay()) : mIsDebugMode(isDebugMode), mMask(mask) + { + /// Constructor + mOutputHandler.setFeeId(feeId); + mOutputHandler.setElectronicsDelay(electronicsDelay); + mIsDebugMode = isDebugMode; + if (isDebugMode) { + mOnDoneLoc = &GBTOutputHandler::onDoneLocDebug; + } + mLinkPayload.reserve(0x20000); + } + + void operator()(gsl::span<const uint8_t> payload, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) + { + /// Decodes the buffer + mOutputHandler.set(orbit, data, rofs); + + for (int ireg = 0; ireg < 2; ++ireg) { + size_t byteOffset = 5 * ireg; + + if (mIsDebugMode) { + // Treat regional cards + size_t ilink = 8 + ireg; + mLinkPayload.clear(); + for (size_t idx = byteOffset + 4, end = payload.size(); idx < end; idx += 16) { + mLinkPayload.emplace_back(payload[idx]); + } + auto it = mLinkPayload.begin(); + auto end = mLinkPayload.end(); + while (it != end) { + if (mELinkDecoders[ilink].isZero(*it)) { + ++it; + continue; + } + if (mELinkDecoders[ilink].addCore(it, end)) { + mOutputHandler.onDoneRegDebug(ilink, mELinkDecoders[ilink]); + mELinkDecoders[ilink].reset(); + } + } + } + + // Treat local cards + for (int ib = 0; ib < 4; ++ib) { + size_t ilink = ib + 4 * ireg; + if (mMask & (1 << ilink)) { + mLinkPayload.clear(); + for (size_t idx = byteOffset + ib, end = payload.size(); idx < end; idx += 16) { + mLinkPayload.emplace_back(payload[idx]); + } + auto it = mLinkPayload.begin(); + auto end = mLinkPayload.end(); + while (it != end) { + if (mELinkDecoders[ilink].isZero(*it)) { + ++it; + continue; + } + if (mELinkDecoders[ilink].add(it, end)) { + std::invoke(mOnDoneLoc, mOutputHandler, mELinkDecoders[ilink].getId() % 8, mELinkDecoders[ilink]); + mELinkDecoders[ilink].reset(); + } + } + } + } + } + } + + private: + bool mIsDebugMode{false}; /// Debug mode + uint8_t mMask{0xFF}; /// GBT mask + GBTOutputHandler mOutputHandler{}; /// GBT output handler + + std::array<ELinkDecoder, crateparams::sNELinksPerGBT> mELinkDecoders{}; /// E-link decoders + std::vector<uint8_t> mLinkPayload{}; /// Link payload + + typedef void (GBTOutputHandler::*OnDoneFunction)(size_t, const ELinkDecoder&); + + OnDoneFunction mOnDoneLoc{&GBTOutputHandler::onDoneLoc}; ///! Processes the local board +}; + +/// Alternative implementation of the bare decoder +/// When a start bit is found, we try to add all of the expected bytes. +/// This should in principle allow to avoid performing a check on the expected data size at each newly added byte. +/// But tests show that the implementation is slightly slower than the standard one. +class GBTBareDecoderInsertImpl +{ + public: + GBTBareDecoderInsertImpl(uint16_t feeId, bool isDebugMode = false, uint8_t mask = 0xFF, const ElectronicsDelay& electronicsDelay = ElectronicsDelay()) : mIsDebugMode(isDebugMode), mMask(mask) + { + /// Constructor + mOutputHandler.setFeeId(feeId); + mOutputHandler.setElectronicsDelay(electronicsDelay); + if (isDebugMode) { + mOnDoneLoc = &GBTOutputHandler::onDoneLocDebug; + } + } + + void operator()(gsl::span<const uint8_t> payload, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) + { + /// Decodes the buffer + mOutputHandler.set(orbit, data, rofs); + + size_t step = 16; + size_t end = payload.size(); + + for (int ireg = 0; ireg < 2; ++ireg) { + size_t byteOffset = 5 * ireg; + + if (mIsDebugMode) { + // Treat regional cards + size_t ilink = 8 + ireg; + size_t idx = byteOffset + 4; + + while (idx < end) { + if (mELinkDecoders[ilink].isZero(payload[idx])) { + idx += step; + } else if (mELinkDecoders[ilink].addCore(idx, payload, step)) { + mOutputHandler.onDoneRegDebug(ilink, mELinkDecoders[ilink]); + mELinkDecoders[ilink].reset(); + } + } + } + + // Treat local cards + for (int ib = 0; ib < 4; ++ib) { + size_t ilink = ib + 4 * ireg; + if (mMask & (1 << ilink)) { + size_t idx = byteOffset + ib; + while (idx < end) { + if (mELinkDecoders[ilink].isZero(payload[idx])) { + idx += step; + } else if (mELinkDecoders[ilink].add(idx, payload, step)) { + std::invoke(mOnDoneLoc, mOutputHandler, mELinkDecoders[ilink].getId() % 8, mELinkDecoders[ilink]); + mELinkDecoders[ilink].reset(); + } + } + } + } + } + } + + private: + uint8_t mMask{0xFF}; /// GBT mask + bool mIsDebugMode{false}; /// Debug mode + GBTOutputHandler mOutputHandler{}; /// GBT output handler + + std::array<ELinkDecoder, crateparams::sNELinksPerGBT> mELinkDecoders{}; /// E-link decoders + + typedef void (GBTOutputHandler::*OnDoneFunction)(size_t, const ELinkDecoder&); + + OnDoneFunction mOnDoneLoc{&GBTOutputHandler::onDoneLoc}; ///! Processes the local board +}; + +} // namespace impl + +void GBTDecoder::process(gsl::span<const uint8_t> payload, uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) +{ + /// Decodes the data + mDecode(payload, orbit, data, rofs); +} + +std::unique_ptr<GBTDecoder> createGBTDecoder(const o2::header::RDHAny& rdh, uint16_t feeId, bool isDebugMode, uint8_t mask, const ElectronicsDelay& electronicsDelay) +{ + /// Creates the correct implementation of the GBT decoder + if (o2::raw::RDHUtils::getLinkID(rdh) == raw::sUserLogicLinkID) { + return std::make_unique<GBTDecoder>(impl::GBTUserLogicDecoderImpl(feeId, isDebugMode, mask, electronicsDelay)); + } + return std::make_unique<GBTDecoder>(impl::GBTBareDecoderImpl(feeId, isDebugMode, mask, electronicsDelay)); +} + +std::unique_ptr<GBTDecoder> createGBTDecoder(uint16_t feeId, bool isBare, bool isDebugMode, uint8_t mask, const ElectronicsDelay& electronicsDelay) +{ + /// Creates the correct implementation of the GBT decoder + if (isBare) { + return std::make_unique<GBTDecoder>(impl::GBTBareDecoderImpl(feeId, isDebugMode, mask, electronicsDelay)); + } + return std::make_unique<GBTDecoder>(impl::GBTUserLogicDecoderImpl(feeId, isDebugMode, mask, electronicsDelay)); +} + +} // namespace mid +} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/GBTOutputHandler.cxx b/Detectors/MUON/MID/Raw/src/GBTOutputHandler.cxx index 8b8a57c15c7e4..01b743f88a3c3 100644 --- a/Detectors/MUON/MID/Raw/src/GBTOutputHandler.cxx +++ b/Detectors/MUON/MID/Raw/src/GBTOutputHandler.cxx @@ -15,44 +15,28 @@ #include "MIDRaw/GBTOutputHandler.h" -#include "RawInfo.h" - namespace o2 { namespace mid { -void GBTOutputHandler::clear() +void GBTOutputHandler::set(uint32_t orbit, std::vector<LocalBoardRO>& data, std::vector<ROFRecord>& rofs) { - /// Rewind bytes - mData.clear(); - mROFRecords.clear(); -} + /// Sets the orbit and the output data vectors + mOrbit = orbit; + mData = &data; + mROFRecords = &rofs; -void GBTOutputHandler::setIR(uint16_t bc, uint32_t orbit, int pageCnt) -{ - /// Sets the interaction record if needed if (mIRs[0].isDummy()) { for (auto& ir : mIRs) { - ir.bc = bc; - ir.orbit = orbit; - } - for (auto& clock : mLastClock) { - clock = constants::lhc::LHCMaxBunches; - } - for (auto& clock : mCalibClocks) { - clock = constants::lhc::LHCMaxBunches; + ir.bc = 0; + // The reset changes depending on the way we synch with the orbit + // (see processOrbitTrigger for details) + // FIXME: pick one of the two + ir.orbit = orbit - 1; // with orbit increase + // ir.orbit = orbit; // with reset to RDH } - } - - if (pageCnt == 0) { - // FIXME: in the tests, the BC counter increases at each RDH - // However, the inner clock of the RO is not reset, - // so if we want to have the absolute BC, - // we need to store only the BC counter of the first page. - // Not sure how it will work on data... - mIRFirstPage.bc = bc; - mIRFirstPage.orbit = orbit; + mLastClock.fill(constants::lhc::LHCMaxBunches); } } @@ -62,112 +46,150 @@ bool GBTOutputHandler::checkLoc(size_t ilink, const ELinkDecoder& decoder) return (ilink == decoder.getId() % 8); } -void GBTOutputHandler::addBoard(size_t ilink, const ELinkDecoder& decoder) +EventType GBTOutputHandler::processSelfTriggered(size_t ilink, uint16_t localClock, uint16_t& correctedClock) { - /// Adds the local or regional board to the output data vector - uint16_t localClock = decoder.getCounter(); - EventType eventType = EventType::Standard; - if (decoder.getTriggerWord() & raw::sCALIBRATE) { - mCalibClocks[ilink] = localClock; - eventType = EventType::Noise; - } else if (localClock == mCalibClocks[ilink] + sDelayCalibToFET) { - eventType = EventType::Dead; + /// Processes the self-triggered event + correctedClock = localClock - mElectronicsDelay.BCToLocal; + uint16_t linkMask = 1 << ilink; + if ((mReceivedCalibration & linkMask) && (localClock == mExpectedFETClock[ilink])) { + // Reset the calibration flag for this e-link + mReceivedCalibration &= ~linkMask; + return EventType::Dead; } - auto firstEntry = mData.size(); - mData.push_back({decoder.getStatusWord(), decoder.getTriggerWord(), crateparams::makeUniqueLocID(crateparams::getCrateIdFromROId(mFeeId), decoder.getId()), decoder.getInputs()}); - InteractionRecord intRec(mIRs[ilink].bc + localClock - sDelayBCToLocal, mIRs[ilink].orbit); - mROFRecords.emplace_back(intRec, eventType, firstEntry, 1); + return EventType::Standard; } -void GBTOutputHandler::addLoc(size_t ilink, const ELinkDecoder& decoder) +EventType GBTOutputHandler::processCalibrationTrigger(size_t ilink, uint16_t localClock) { - /// Adds the local board to the output data vector - addBoard(ilink, decoder); - for (int ich = 0; ich < 4; ++ich) { - if ((mData.back().firedChambers & (1 << ich))) { - mData.back().patternsBP[ich] = decoder.getPattern(0, ich); - mData.back().patternsNBP[ich] = decoder.getPattern(1, ich); - } + /// Processes the calibration event + mExpectedFETClock[ilink] = localClock + mElectronicsDelay.calibToFET; + mReceivedCalibration |= (1 << ilink); + return EventType::Noise; +} + +void GBTOutputHandler::processOrbitTrigger(size_t ilink, uint16_t localClock, uint8_t triggerWord) +{ + /// Processes the orbit trigger event + + // The local clock is reset: we are now in synch with the new HB + // We have to way to account for the orbit change: + // - increase the orbit counter by 1 for this e-link + // (CAVEAT: synch is lost if we lose some orbit) + // - set the orbit to the one found in RDH + // (CAVEAT: synch is lost if we have lot of data, spanning over two orbits) + // FIXME: pick one of the two + ++mIRs[ilink].orbit; // orbit increase + // mIRs[ilink].orbit = mOrbit; // reset to RDH + if ((triggerWord & raw::sSOX) == 0) { + mLastClock[ilink] = localClock; } - if (mROFRecords.back().eventType == EventType::Dead) { - if (invertPattern(mData.back())) { - mData.pop_back(); - mROFRecords.pop_back(); - } + // The orbit trigger resets the clock. + // If we received a calibration trigger, we need to change the value of the expected clock accordingly + if (mReceivedCalibration & (1 << ilink)) { + mExpectedFETClock[ilink] -= (localClock + 1); } } -bool GBTOutputHandler::updateIR(size_t ilink, const ELinkDecoder& decoder) +bool GBTOutputHandler::processTrigger(size_t ilink, const ELinkDecoder& decoder, EventType& eventType, uint16_t& correctedClock) { - /// Updates the interaction record for the link + /// Processes the trigger information + /// Returns true if the event should be further processed, + /// returns false otherwise. + uint16_t linkMask = 1 << ilink; + auto localClock = decoder.getCounter(); + + if (decoder.getTriggerWord() == 0) { + // This is a self-triggered event + eventType = processSelfTriggered(ilink, localClock, correctedClock); + return true; + } + + // From here we treat triggered events + bool goOn = false; + correctedClock = localClock; + if (decoder.getTriggerWord() & raw::sCALIBRATE) { + // This is an answer to a calibration trigger + eventType = processCalibrationTrigger(ilink, localClock); + goOn = true; + } + if (decoder.getTriggerWord() & raw::sORB) { // This is the answer to an orbit trigger - // The local clock is reset: we are now in synch with the new HB - mIRs[ilink] = mIRFirstPage; - if (!(decoder.getTriggerWord() & (raw::sSOX | raw::sEOX))) { - mLastClock[ilink] = decoder.getCounter(); + processOrbitTrigger(ilink, localClock, decoder.getTriggerWord()); + eventType = EventType::Standard; + } + + return goOn; +} + +void GBTOutputHandler::addLoc(size_t ilink, const ELinkDecoder& decoder, EventType eventType, uint16_t correctedClock) +{ + /// Adds the local board to the output data vector + auto firstEntry = mData->size(); + mData->push_back({decoder.getStatusWord(), decoder.getTriggerWord(), crateparams::makeUniqueLocID(crateparams::getCrateIdFromROId(mFeeId), decoder.getId()), decoder.getInputs()}); + InteractionRecord intRec(mIRs[ilink].bc + correctedClock, mIRs[ilink].orbit); + mROFRecords->emplace_back(intRec, eventType, firstEntry, 1); + for (int ich = 0; ich < 4; ++ich) { + if ((mData->back().firedChambers & (1 << ich))) { + mData->back().patternsBP[ich] = decoder.getPattern(0, ich); + mData->back().patternsNBP[ich] = decoder.getPattern(1, ich); } - return true; } - return false; } void GBTOutputHandler::onDoneLoc(size_t ilink, const ELinkDecoder& decoder) { /// Performs action on decoded local board - if (updateIR(ilink, decoder)) { - return; - } - if (checkLoc(ilink, decoder)) { - addLoc(ilink, decoder); + EventType eventType; + uint16_t correctedClock; + if (processTrigger(ilink, decoder, eventType, correctedClock) && checkLoc(ilink, decoder)) { + addLoc(ilink, decoder, eventType, correctedClock); } } void GBTOutputHandler::onDoneLocDebug(size_t ilink, const ELinkDecoder& decoder) { - /// This always adds the local board to the output, without performing tests - updateIR(ilink, decoder); - addLoc(ilink, decoder); + EventType eventType; + uint16_t correctedClock; + processTrigger(ilink, decoder, eventType, correctedClock); + addLoc(ilink, decoder, eventType, correctedClock); + if (decoder.getTriggerWord() & raw::sORB) { + // The local clock is increased when receiving an orbit trigger, + // but the local counter returned in answering the trigger + // belongs to the previous orbit + --mROFRecords->back().interactionRecord.orbit; + } } void GBTOutputHandler::onDoneRegDebug(size_t ilink, const ELinkDecoder& decoder) { /// Performs action on decoded regional board in debug mode. - updateIR(ilink, decoder); - addBoard(ilink, decoder); - // The board creation is optimized for the local boards, not the regional - // (which are transmitted only in debug mode). - // So, at this point, for the regional board, the local Id is actually the crate ID. + EventType eventType; + uint16_t correctedClock; + processTrigger(ilink, decoder, eventType, correctedClock); // If we want to distinguish the two regional e-links, we can use the link ID instead - mData.back().boardId = crateparams::makeUniqueLocID(crateparams::getCrateIdFromROId(mFeeId), ilink + 8 * (crateparams::getGBTIdInCrate(mFeeId) - 1)); - if (mData.back().triggerWord == 0) { - if (mROFRecords.back().interactionRecord.bc < sDelayRegToLocal) { + auto firstEntry = mData->size(); + mData->push_back({decoder.getStatusWord(), decoder.getTriggerWord(), crateparams::makeUniqueLocID(crateparams::getCrateIdFromROId(mFeeId), ilink + 8 * (crateparams::getGBTIdInCrate(mFeeId) - 1)), decoder.getInputs()}); + + auto orbit = (decoder.getTriggerWord() & raw::sORB) ? mIRs[ilink].orbit - 1 : mIRs[ilink].orbit; + + InteractionRecord intRec(mIRs[ilink].bc + correctedClock, orbit); + if (decoder.getTriggerWord() == 0) { + if (intRec.bc < mElectronicsDelay.regToLocal) { // In the tests, the HB does not really correspond to a change of orbit // So we need to keep track of the last clock at which the HB was received // and come back to that value // FIXME: Remove this part as well as mLastClock when tests are no more needed - mROFRecords.back().interactionRecord -= (constants::lhc::LHCMaxBunches - mLastClock[ilink] - 1); + intRec -= (constants::lhc::LHCMaxBunches - mLastClock[ilink] - 1); } // This is a self-triggered event. // In this case the regional card needs to wait to receive the tracklet decision of each local // which result in a delay that needs to be subtracted if we want to be able to synchronize // local and regional cards for the checks - mROFRecords.back().interactionRecord -= sDelayRegToLocal; - } -} - -bool GBTOutputHandler::invertPattern(LocalBoardRO& loc) -{ - /// Gets the proper pattern - for (int ich = 0; ich < 4; ++ich) { - loc.patternsBP[ich] = ~loc.patternsBP[ich]; - loc.patternsNBP[ich] = ~loc.patternsNBP[ich]; - if (loc.patternsBP[ich] == 0 && loc.patternsNBP[ich] == 0) { - loc.firedChambers &= ~(1 << ich); - } + intRec -= mElectronicsDelay.regToLocal; } - return (loc.firedChambers == 0); + mROFRecords->emplace_back(intRec, eventType, firstEntry, 1); } } // namespace mid diff --git a/Detectors/MUON/MID/Raw/src/GBTUserLogicDecoder.cxx b/Detectors/MUON/MID/Raw/src/GBTUserLogicDecoder.cxx deleted file mode 100644 index 0e4e78c14dbd2..0000000000000 --- a/Detectors/MUON/MID/Raw/src/GBTUserLogicDecoder.cxx +++ /dev/null @@ -1,65 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Raw/src/GBTUserLogicDecoder.cxx -/// \brief MID GBT decoder with user logic zero suppression -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 15 April 2020 - -#include "MIDRaw/GBTUserLogicDecoder.h" - -namespace o2 -{ -namespace mid -{ - -void GBTUserLogicDecoder::init(uint16_t feeId, bool isDebugMode) -{ - /// Initializes the task - mOutputHandler.setFeeId(feeId); - if (isDebugMode) { - mOnDoneLoc = &GBTOutputHandler::onDoneLocDebug; - mOnDoneReg = &GBTOutputHandler::onDoneRegDebug; - } -} - -void GBTUserLogicDecoder::process(gsl::span<const uint8_t> bytes, uint16_t bc, uint32_t orbit, uint16_t pageCnt) -{ - /// Decodes the buffer - mOutputHandler.setIR(bc, orbit, pageCnt); - - bool isFeeding = false; - for (auto& byte : bytes) { - if (mELinkDecoder.getNBytes() == 0 && (byte & raw::sSTARTBIT) == 0) { - // The e-link decoder is empty, meaning that we expect a new board. - // The first byte of the board should have the STARTBIT on. - // If this is not the case, it means that: - // a) there was a problem in the decoding - // b) we reached the end of the payload (and we have zeros until the end of the 256 bits word) - // In both cases, we need to stop - break; - } - mELinkDecoder.add(byte); - if (mELinkDecoder.isComplete()) { - if (raw::isLoc(mELinkDecoder.getStatusWord())) { - std::invoke(mOnDoneLoc, mOutputHandler, mELinkDecoder.getId() % 8, mELinkDecoder); - } else { - size_t ilink = 8 + mELinkDecoder.getId() % 8; - if (ilink > 9) { - continue; - } - std::invoke(mOnDoneReg, mOutputHandler, 8 + mELinkDecoder.getId() % 8, mELinkDecoder); - } - mELinkDecoder.reset(); - } - } -} -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/GBTUserLogicEncoder.cxx b/Detectors/MUON/MID/Raw/src/GBTUserLogicEncoder.cxx index ff3dd4541f18a..617ec58e67f29 100644 --- a/Detectors/MUON/MID/Raw/src/GBTUserLogicEncoder.cxx +++ b/Detectors/MUON/MID/Raw/src/GBTUserLogicEncoder.cxx @@ -16,122 +16,95 @@ #include "MIDRaw/GBTUserLogicEncoder.h" #include "MIDRaw/CrateParameters.h" -#include "RawInfo.h" namespace o2 { namespace mid { -void GBTUserLogicEncoder::addShort(uint16_t shortWord) +void GBTUserLogicEncoder::addShort(std::vector<char>& buffer, uint16_t shortWord) const { /// Adds a 16 bits word - mBytes.emplace_back((shortWord >> 8) & 0xFF); - mBytes.emplace_back(shortWord & 0xFF); + buffer.emplace_back((shortWord >> 8) & 0xFF); + buffer.emplace_back(shortWord & 0xFF); } -void GBTUserLogicEncoder::addBoard(uint8_t statusWord, uint8_t triggerWord, uint16_t localClock, uint8_t id, uint8_t firedChambers) -{ - /// Adds the board information - mBytes.emplace_back(statusWord); - mBytes.emplace_back(triggerWord); - addShort(localClock); - addIdAndChambers(id, firedChambers); -} - -void GBTUserLogicEncoder::processTrigger(const uint16_t bc, uint8_t triggerWord) +void GBTUserLogicEncoder::processTrigger(const InteractionRecord& ir, uint8_t triggerWord) { /// Adds the information in triggered mode - for (int ireg = 0; ireg < 2; ++ireg) { + auto& vec = mBoards[ir]; + for (uint8_t ireg = 0; ireg < 2; ++ireg) { uint8_t firedLoc = (mMask >> (4 * ireg)) & 0xF; - addReg(bc, triggerWord, ireg, firedLoc); + vec.push_back({raw::sSTARTBIT, triggerWord, ireg, firedLoc}); } - for (int iloc = 0; iloc < 8; ++iloc) { + for (uint8_t iloc = 0; iloc < 8; ++iloc) { if (mMask & (1 << iloc)) { - addBoard(raw::sSTARTBIT | raw::sCARDTYPE, triggerWord, bc, iloc + 8 * crateparams::getGBTIdInCrate(mFeeId), 0); + vec.push_back({raw::sSTARTBIT | raw::sCARDTYPE, triggerWord, static_cast<uint8_t>(iloc + 8 * crateparams::getGBTIdInCrate(mFeeId)), 0}); if (triggerWord & (raw::sSOX | raw::sEOX)) { /// Write masks for (int ich = 0; ich < 4; ++ich) { - addShort(0xFFFF); // BP - addShort(0xFFFF); // NBP + vec.back().patternsBP[ich] = 0xFFFF; + vec.back().patternsNBP[ich] = 0xFFFF; } } } } } -bool GBTUserLogicEncoder::checkAndAdd(gsl::span<const LocalBoardRO> data, const uint16_t bc, uint8_t triggerWord) +void GBTUserLogicEncoder::addRegionalBoards(uint8_t activeBoards, InteractionRecord ir) { - /// Checks the local boards and write the regional and local output if needed - uint8_t activeBoards = 0; - for (auto& loc : data) { - int regId = loc.boardId / 4; - for (int ich = 0; ich < 4; ++ich) { - if (loc.patternsBP[ich] && loc.patternsNBP[ich]) { - activeBoards |= (1 << (loc.boardId % 8)); - } - } - } - for (int ireg = 0; ireg < 2; ++ireg) { + /// Adds the regional board information + ir += mElectronicsDelay.BCToLocal + mElectronicsDelay.regToLocal; + auto& vec = mBoards[ir]; + for (uint8_t ireg = 0; ireg < 2; ++ireg) { uint8_t firedLoc = (activeBoards >> (4 * ireg)) & 0xF; if (firedLoc > 0) { - addReg(bc, triggerWord, ireg, firedLoc); - } - } - for (auto& loc : data) { - if (activeBoards & (1 << (loc.boardId % 8))) { - addLoc(loc, bc, triggerWord); + vec.push_back({raw::sSTARTBIT, 0, ireg, firedLoc}); } } - return (activeBoards > 0); -} - -void GBTUserLogicEncoder::addReg(uint16_t bc, uint8_t triggerWord, uint8_t id, uint8_t firedChambers) -{ - /// Adds the regional board information - mBytes.emplace_back(raw::sSTARTBIT); - mBytes.emplace_back(triggerWord); - uint16_t localClock = bc; - if (triggerWord == 0) { - localClock += sDelayBCToLocal + sDelayRegToLocal; - } - addShort(localClock); - addIdAndChambers(id + 8 * crateparams::getGBTIdInCrate(mFeeId), firedChambers); } -void GBTUserLogicEncoder::addLoc(const LocalBoardRO& loc, uint16_t bc, uint8_t triggerWord) +void GBTUserLogicEncoder::process(gsl::span<const LocalBoardRO> data, const InteractionRecord& ir) { - /// Adds the local board information - - mBytes.emplace_back(loc.statusWord); - mBytes.emplace_back(triggerWord ? triggerWord : loc.triggerWord); - - uint16_t localClock = bc; - if (loc.triggerWord == 0) { - localClock += sDelayBCToLocal; - } - addShort(localClock); - - addIdAndChambers(loc.boardId, loc.firedChambers); - for (int ich = 4; ich >= 0; --ich) { - if (loc.firedChambers & (1 << ich)) { - addShort(loc.patternsBP[ich]); - addShort(loc.patternsNBP[ich]); + /// Encode data + auto& vec = mBoards[ir]; + uint8_t activeBoards = 0; + for (auto& loc : data) { + for (int ich = 0; ich < 4; ++ich) { + if (loc.patternsBP[ich] && loc.patternsNBP[ich]) { + activeBoards |= (1 << (crateparams::getLocId(loc.boardId) % 8)); + } } + vec.emplace_back(loc); } + addRegionalBoards(activeBoards, ir); } -void GBTUserLogicEncoder::process(gsl::span<const LocalBoardRO> data, const uint16_t bc, uint8_t triggerWord) +void GBTUserLogicEncoder::flush(std::vector<char>& buffer, const InteractionRecord& ir) { - /// Encode data - if (triggerWord != 0) { - processTrigger(bc, triggerWord); - } - checkAndAdd(data, bc, triggerWord); - if (triggerWord == raw::sCALIBRATE) { - // Add FET - checkAndAdd(data, bc + sDelayCalibToFET, 0); + /// Flush buffer + std::map<InteractionRecord, std::vector<LocalBoardRO>> tmpBoards; + for (auto& item : mBoards) { + if (item.first <= ir) { + for (auto& loc : item.second) { + buffer.emplace_back(loc.statusWord); + buffer.emplace_back(loc.triggerWord); + addShort(buffer, item.first.bc); + buffer.emplace_back((crateparams::getLocId(loc.boardId) << 4) | loc.firedChambers); + if (raw::isLoc(loc.statusWord)) { + for (int ich = 4; ich >= 0; --ich) { + if (loc.firedChambers & (1 << ich)) { + addShort(buffer, loc.patternsBP[ich]); + addShort(buffer, loc.patternsNBP[ich]); + } + } + } + } + } else { + tmpBoards[item.first] = item.second; + } } + mBoards.swap(tmpBoards); } } // namespace mid diff --git a/Detectors/MUON/MID/Raw/src/RawBuffer.cxx b/Detectors/MUON/MID/Raw/src/RawBuffer.cxx deleted file mode 100644 index 713f0a6a09757..0000000000000 --- a/Detectors/MUON/MID/Raw/src/RawBuffer.cxx +++ /dev/null @@ -1,181 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Raw/src/RawBuffer.cxx -/// \brief MID CRU user logic decoder -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 30 September 2019 - -#include "MIDRaw/RawBuffer.h" - -#include "MIDRaw/RawUnit.h" -#include "RawInfo.h" -#include "DetectorsRaw/RDHUtils.h" - -namespace o2 -{ -namespace mid -{ -using RDHUtils = o2::raw::RDHUtils; - -template <typename T> -unsigned int RawBuffer<T>::next(unsigned int nBits) -{ - /// Reads the next nBits - if (mNextHeaderIndex == 0) { - // This is the first entry - nextPayload(); - } - unsigned int value = 0; - for (int ibit = 0; ibit < nBits; ++ibit) { - if (mBitIndex == mElementSizeInBits) { - mBitIndex = 0; - ++mElementIndex; - nextPayload(); - } - bool isOn = (mBytes[mElementIndex] >> mBitIndex) & 0x1; - if (isOn) { - value |= (1 << ibit); - } - ++mBitIndex; - } - return value; -} - -template <typename T> -T RawBuffer<T>::next() -{ - /// Reads the next word - nextPayload(); - return mBytes[mElementIndex++]; -} - -template <typename T> -bool RawBuffer<T>::nextHeader() -{ - /// Goes to next RDH - if (mNextHeaderIndex >= mBytes.size()) { - // This is the end of the buffer - if (mUnconsumed.empty()) { - mElementIndex = mNextHeaderIndex; - mEndOfPayloadIndex = mNextHeaderIndex; - return false; - } - // We were reading the unconsumed part: switch to new buffer - mUnconsumed.clear(); - reset(); - mBytes = mCurrentBuffer; - } - mHeaderIndex = mNextHeaderIndex; - mRDH = reinterpret_cast<const header::RAWDataHeader*>(&mBytes[mHeaderIndex]); - mEndOfPayloadIndex = mHeaderIndex + (RDHUtils::getMemorySize(*mRDH) / mElementSizeInBytes); - // Go to end of header, i.e. beginning of payload - mElementIndex = mHeaderIndex + (RDHUtils::getHeaderSize(*mRDH) / mElementSizeInBytes); - mNextHeaderIndex = mHeaderIndex + RDHUtils::getOffsetToNext(*mRDH) / mElementSizeInBytes; - mBitIndex = 0; - - return true; -} - -template <typename T> -bool RawBuffer<T>::nextPayload() -{ - /// Goes to next payload - while (mElementIndex == mEndOfPayloadIndex) { - if (!nextHeader()) { - return false; - } - } - return true; -} - -template <typename T> -void RawBuffer<T>::reset() -{ - /// Rewind bytes - mElementIndex = 0; - mHeaderIndex = 0; - mNextHeaderIndex = 0; - mEndOfPayloadIndex = 0; - mBitIndex = 0; - mRDH = nullptr; - mUnconsumed.clear(); -} - -template <typename T> -void RawBuffer<T>::setBuffer(gsl::span<const T> bytes, ResetMode resetMode) -{ - /// Sets the buffer and reset the internal indexes - if (resetMode == ResetMode::keepUnconsumed && !mUnconsumed.empty()) { - // There are some unconsumed bytes from the previous buffer - mNextHeaderIndex -= mHeaderIndex; - mEndOfPayloadIndex -= mHeaderIndex; - mElementIndex -= mHeaderIndex; - mHeaderIndex = 0; - mBytes = gsl::span<const T>(mUnconsumed); - } else { - mBytes = bytes; - if (resetMode != ResetMode::bufferOnly) { - reset(); - } - } - mCurrentBuffer = bytes; -} - -template <typename T> -bool RawBuffer<T>::isHBClosed() -{ - /// Tests if the HB is closed - if (!mRDH) { - return false; - } - return RDHUtils::getStop(*mRDH); -} - -template <typename T> -void RawBuffer<T>::skipOverhead() -{ - /// This function must be called after reading a block of data - /// if the payload is not provided and/or readout in bytes. - /// - /// In this case, indeed, there can be an overhead between the last useful bit and the declared memory size, - /// which is in bytes. - /// Calling this function allows to jump directly to the next header when needed - if (mElementIndex == mEndOfPayloadIndex - 1) { - mElementIndex = mEndOfPayloadIndex; - mBitIndex = 0; - } -} - -template <typename T> -bool RawBuffer<T>::hasNext(unsigned int nBytes) -{ - /// Tests if the buffer has nBytes left - - // We first need to go to the next payload - // If we do not, we could have a set of empty HBs in front of us - // With lot of memory left in the buffer but no payload - nextPayload(); - bool isOk = mCurrentBuffer.size() + mUnconsumed.size() - mNextHeaderIndex + mEndOfPayloadIndex - mElementIndex >= nBytes / mElementSizeInBytes; - if (!isOk && mElementIndex != mCurrentBuffer.size()) { - // Store the remaining bits for further use - // We need to do it here because the vector of which the mBytes is just a span might not be valid afterwards - // (e.g. when we do the next setBuffer) - // If we do not want to invalidate the mRDH pointer, we need to copy bytes from the last header - mUnconsumed.insert(mUnconsumed.end(), mBytes.begin() + mHeaderIndex, mBytes.end()); - } - return isOk; -} - -template class RawBuffer<raw::RawUnit>; -template class RawBuffer<uint8_t>; - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/RawFileReader.cxx b/Detectors/MUON/MID/Raw/src/RawFileReader.cxx index 763d37d7adc44..a039749839075 100644 --- a/Detectors/MUON/MID/Raw/src/RawFileReader.cxx +++ b/Detectors/MUON/MID/Raw/src/RawFileReader.cxx @@ -17,16 +17,15 @@ #include <iostream> #include "Headers/RAWDataHeader.h" +#include "Headers/RDHAny.h" +#include "DPLUtils/RawParser.h" #include "DetectorsRaw/RDHUtils.h" namespace o2 { namespace mid { -using RDHUtils = o2::raw::RDHUtils; - -template <typename T> -bool RawFileReader<T>::init(const char* inFilename, bool readContinuous) +bool RawFileReader::init(const char* inFilename, bool readContinuous) { /// Initializes the raw file reader mFile.open(inFilename, std::ios::binary); @@ -35,71 +34,41 @@ bool RawFileReader<T>::init(const char* inFilename, bool readContinuous) mState = 2; return false; } - mBytes.reserve(2 * raw::sMaxBufferSize); mReadContinuous = readContinuous; - mHBCounters.fill(0); - return true; } -template <typename T> -void RawFileReader<T>::read(size_t nBytes) +void RawFileReader::read(size_t nBytes) { /// Reads nBytes of the file size_t currentIndex = mBytes.size(); - mBytes.resize(currentIndex + nBytes / sizeof(T)); + mBytes.resize(currentIndex + nBytes); mFile.read(reinterpret_cast<char*>(&(mBytes[currentIndex])), nBytes); } -template <typename T> -void RawFileReader<T>::clear() +void RawFileReader::clear() { /// Clears the bytes and counters mBytes.clear(); - mBuffer.setBuffer(mBytes, RawBuffer<T>::ResetMode::all); - mHBCounters.fill(0); -} - -template <typename T> -bool RawFileReader<T>::hasFullInfo() -{ - /// Tests if we have read the same number of HBs for all GBT links - - // We assume here that we read the data of one HV for all of the GBT links - // before moving to the next HB - for (uint16_t feeId = 1; feeId < crateparams::sNGBTs; ++feeId) { - if (mHBCounters[feeId] != mHBCounters[0]) { - return false; - } - } - return mHBCounters[0] != 0; } -template <typename T> -bool RawFileReader<T>::readAllGBTs(bool reset) +void RawFileReader::setCustomPayloadSize(uint16_t memorySize, uint16_t offsetToNext) { - /// Keeps reading the file until it reads the information of all GBT links - /// It returns the number of HBs read - - if (reset) { - clear(); - } - - while (!hasFullInfo()) { - if (!readHB()) { - return false; - } - } - return true; + /// Sets a custom memory and payload size + /// This is done to be able to correctly read test data + /// that have a wrong RDH + o2::header::RAWDataHeader rdh; + rdh.word1 |= offsetToNext; + rdh.word1 |= (memorySize << 16); + setCustomRDH(rdh); } -template <typename T> -bool RawFileReader<T>::replaceRDH(size_t headerIndex) +bool RawFileReader::replaceRDH(size_t headerIndex) { /// Replaces the current RDH with a custom one if needed. /// This is done to be able to correctly read test data /// that have a wrong RDH - if (RDHUtils::getOffsetToNext(mCustomRDH) > 0) { + if (o2::raw::RDHUtils::getOffsetToNext(mCustomRDH) > 0) { header::RAWDataHeader* rdh = reinterpret_cast<header::RAWDataHeader*>(&mBytes[headerIndex]); *rdh = mCustomRDH; return true; @@ -107,19 +76,17 @@ bool RawFileReader<T>::replaceRDH(size_t headerIndex) return false; } -template <typename T> -bool RawFileReader<T>::readHB(bool sendCompleteHBs) +bool RawFileReader::readHB(bool sendCompleteHBs) { /// Reads one HB if (mState != 0) { return false; } - auto gbtId = 0; bool isHBClosed = false; while (!isHBClosed) { // Read header size_t headerIndex = mBytes.size(); - read(raw::sHeaderSizeInBytes); + read(sHeaderSize); // The check on the eof needs to be placed here and not at the beginning of the function. // The reason is that the eof flag is set if we try to read after the eof @@ -132,7 +99,7 @@ bool RawFileReader<T>::readHB(bool sendCompleteHBs) if (mReadContinuous) { mFile.clear(); mFile.seekg(0, std::ios::beg); - read(raw::sHeaderSizeInBytes); + read(sHeaderSize); } else { mState = 1; return false; @@ -140,38 +107,23 @@ bool RawFileReader<T>::readHB(bool sendCompleteHBs) } replaceRDH(headerIndex); // We use the buffer only to correctly initialize the RDH - mBuffer.setBuffer(mBytes, RawBuffer<T>::ResetMode::bufferOnly); - mBuffer.nextHeader(); - isHBClosed = mBuffer.isHBClosed(); - gbtId = RDHUtils::getFEEID(*mBuffer.getRDH()); - if (gbtId >= crateparams::sNGBTs) { - // FIXME: this is a problem of the header of some test files - gbtId = 0; + o2::framework::RawParser parser(mBytes.data(), mBytes.size()); + auto lastIt = parser.begin(); + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + lastIt = it; } - if (RDHUtils::getOffsetToNext(*mBuffer.getRDH()) > raw::sHeaderSizeInBytes) { - read(RDHUtils::getOffsetToNext(*mBuffer.getRDH()) - raw::sHeaderSizeInBytes); - // CAVEAT: to save memory / CPU time, the RawBuffer does not hold a copy of the buffer, - // but just a span of it. - // If we add bytes to mBytes, the vector can go beyond the capacity - // and the memory is re-allocated. - // If this happens, the span is no longer valid, and we can no longer - // call mBuffer.getRDH() until we pass it mBytes again - // To do so, you need to call: - // mBuffer.setBuffer(mBytes, RawBuffer<T>::ResetMode::bufferOnly); - // mBuffer.nextHeader(); + auto const* rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(lastIt.raw()); + isHBClosed = o2::raw::RDHUtils::getStop(rdhPtr); + auto offsetNext = o2::raw::RDHUtils::getOffsetToNext(rdhPtr); + if (offsetNext > sHeaderSize) { + read(offsetNext - sHeaderSize); } if (!sendCompleteHBs) { break; } } - - ++mHBCounters[gbtId]; - return true; } -template class RawFileReader<raw::RawUnit>; -template class RawFileReader<uint8_t>; - } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Raw/src/RawInfo.h b/Detectors/MUON/MID/Raw/src/RawInfo.h deleted file mode 100644 index 83b5505210650..0000000000000 --- a/Detectors/MUON/MID/Raw/src/RawInfo.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Raw/src/RawInfo.h -/// \brief Raw data format MID -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 30 September 2019 -#ifndef O2_MID_RAWINFO_H -#define O2_MID_RAWINFO_H - -#include <cstdint> - -namespace o2 -{ -namespace mid -{ - -/// Parameters for local board encoding/decoding -// The delays are in local clocks, and correspond to the LHC clocks (aka BCs) -static constexpr uint16_t sDelayCalibToFET = 10; -static constexpr uint16_t sDelayBCToLocal = 0; -static constexpr uint16_t sDelayRegToLocal = 3; - -} // namespace mid -} // namespace o2 - -#endif /* O2_MID_RAWINFO_H */ diff --git a/Detectors/MUON/MID/Raw/test/bench_Raw.cxx b/Detectors/MUON/MID/Raw/test/bench_Raw.cxx index f2a66b98231dd..ebceb7727644e 100644 --- a/Detectors/MUON/MID/Raw/test/bench_Raw.cxx +++ b/Detectors/MUON/MID/Raw/test/bench_Raw.cxx @@ -19,10 +19,11 @@ #include "CommonDataFormat/InteractionRecord.h" #include "DetectorsRaw/RawFileReader.h" #include "DPLUtils/RawParser.h" +#include "DataFormatsMID/ColumnData.h" #include "MIDBase/DetectorParameters.h" #include "MIDRaw/Decoder.h" -#include "MIDRaw/GBTUserLogicDecoder.h" #include "MIDRaw/Encoder.h" +#include "MIDRaw/GBTDecoder.h" o2::mid::ColumnData getColData(uint8_t deId, uint8_t columnId, uint16_t nbp = 0, uint16_t bp1 = 0, uint16_t bp2 = 0, uint16_t bp3 = 0, uint16_t bp4 = 0) { @@ -60,10 +61,11 @@ std::vector<uint8_t> generateTestData(size_t nTF, size_t nDataInTF, size_t nColD auto severity = fair::Logger::GetConsoleSeverity(); fair::Logger::SetConsoleSeverity(fair::Severity::WARNING); - std::string tmpFilename = "tmp_mid_raw.dat"; + std::string tmpFilename = "tmp_mid_raw.raw"; o2::mid::Encoder encoder; encoder.init(tmpFilename.c_str()); - + std::string tmpConfigFilename = "tmp_MIDConfig.cfg"; + encoder.getWriter().writeConfFile("MID", "RAWDATA", tmpConfigFilename.c_str(), false); // Fill TF for (size_t itf = 0; itf < nTF; ++itf) { for (int ilocal = 0; ilocal < nDataInTF; ++ilocal) { @@ -73,8 +75,7 @@ std::vector<uint8_t> generateTestData(size_t nTF, size_t nDataInTF, size_t nColD } encoder.finalize(); - o2::raw::RawFileReader rawReader; - rawReader.addFile(tmpFilename.c_str()); + o2::raw::RawFileReader rawReader(tmpConfigFilename.c_str()); rawReader.init(); size_t nActiveLinks = rawReader.getNLinks() < nLinks ? rawReader.getNLinks() : nLinks; std::vector<char> buffer; @@ -94,6 +95,7 @@ std::vector<uint8_t> generateTestData(size_t nTF, size_t nDataInTF, size_t nColD fair::Logger::SetConsoleSeverity(severity); std::remove(tmpFilename.c_str()); + std::remove(tmpConfigFilename.c_str()); std::vector<uint8_t> data(buffer.size()); memcpy(data.data(), buffer.data(), buffer.size()); @@ -103,7 +105,7 @@ std::vector<uint8_t> generateTestData(size_t nTF, size_t nDataInTF, size_t nColD static void BM_Decoder(benchmark::State& state) { - o2::mid::Decoder<o2::mid::GBTUserLogicDecoder> decoder; + o2::mid::Decoder decoder; int nTF = state.range(0); int nEventPerTF = state.range(1); @@ -122,8 +124,7 @@ static void BM_Decoder(benchmark::State& state) static void BM_GBTDecoder(benchmark::State& state) { - o2::mid::GBTUserLogicDecoder decoder; - decoder.init(0, false); + auto decoder = o2::mid::createGBTDecoder(0); int nTF = state.range(0); int nEventPerTF = state.range(1); @@ -131,9 +132,12 @@ static void BM_GBTDecoder(benchmark::State& state) double num{0}; auto inputData = generateTestData(nTF, nEventPerTF, nFiredPerEvent, 1); + std::vector<o2::mid::LocalBoardRO> data; + std::vector<o2::mid::ROFRecord> rofs; for (auto _ : state) { - decoder.clear(); + data.clear(); + rofs.clear(); o2::framework::RawParser parser(inputData.data(), inputData.size()); for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { if (it.size() == 0) { @@ -141,7 +145,7 @@ static void BM_GBTDecoder(benchmark::State& state) } auto* rdhPtr = it.template get_if<o2::header::RAWDataHeader>(); gsl::span<const uint8_t> payload(it.data(), it.size()); - decoder.process(payload, *rdhPtr); + decoder->process(payload, *rdhPtr, data, rofs); } ++num; } diff --git a/Detectors/MUON/MID/Raw/test/testRaw.cxx b/Detectors/MUON/MID/Raw/test/testRaw.cxx index a009ec8af063a..1951ffc2ee84b 100644 --- a/Detectors/MUON/MID/Raw/test/testRaw.cxx +++ b/Detectors/MUON/MID/Raw/test/testRaw.cxx @@ -32,7 +32,7 @@ #include "MIDRaw/CrateParameters.h" #include "MIDRaw/DecodedDataAggregator.h" #include "MIDRaw/Decoder.h" -#include "MIDRaw/GBTUserLogicDecoder.h" +#include "MIDRaw/GBTDecoder.h" #include "MIDRaw/GBTUserLogicEncoder.h" #include "MIDRaw/Encoder.h" @@ -54,7 +54,9 @@ o2::mid::ColumnData getColData(uint8_t deId, uint8_t columnId, uint16_t nbp = 0, std::vector<o2::mid::ColumnData> sortData(const std::vector<o2::mid::ColumnData>& data, size_t first, size_t last) { std::vector<o2::mid::ColumnData> sortedData(data.begin() + first, data.begin() + last); - std::sort(sortedData.begin(), sortedData.end(), [](o2::mid::ColumnData& a, o2::mid::ColumnData& b) { if (a.deId == b.deId ) return (a.columnId < b.columnId); return (a.deId < b.deId); }); + std::sort(sortedData.begin(), sortedData.end(), [](o2::mid::ColumnData& a, o2::mid::ColumnData& b) { if (a.deId == b.deId ) { return (a.columnId < b.columnId); + +}return (a.deId < b.deId); }); return sortedData; } @@ -85,16 +87,18 @@ std::tuple<std::vector<o2::mid::ColumnData>, std::vector<o2::mid::ROFRecord>> en { auto severity = fair::Logger::GetConsoleSeverity(); fair::Logger::SetConsoleSeverity(fair::Severity::WARNING); - std::string tmpFilename = "tmp_mid_raw.dat"; + std::string tmpFilename0 = "tmp_mid_raw"; + std::string tmpFilename = tmpFilename0 + ".raw"; o2::mid::Encoder encoder; - encoder.init(tmpFilename.c_str()); + encoder.init(tmpFilename0.c_str()); + std::string tmpConfigFilename = "tmp_MIDConfig.cfg"; + encoder.getWriter().writeConfFile("MID", "RAWDATA", tmpConfigFilename.c_str(), false); for (auto& item : inData) { encoder.process(item.second, item.first, inEventType); } encoder.finalize(); - o2::raw::RawFileReader rawReader; - rawReader.addFile(tmpFilename.c_str()); + o2::raw::RawFileReader rawReader(tmpConfigFilename.c_str()); rawReader.init(); std::vector<char> buffer; for (size_t itf = 0; itf < rawReader.getNTimeFrames(); ++itf) { @@ -113,8 +117,9 @@ std::tuple<std::vector<o2::mid::ColumnData>, std::vector<o2::mid::ROFRecord>> en fair::Logger::SetConsoleSeverity(severity); std::remove(tmpFilename.c_str()); + std::remove(tmpConfigFilename.c_str()); - o2::mid::Decoder<o2::mid::GBTUserLogicDecoder> decoder; + o2::mid::Decoder decoder; gsl::span<const uint8_t> data(reinterpret_cast<uint8_t*>(buffer.data()), buffer.size()); decoder.process(data); @@ -133,24 +138,23 @@ BOOST_AUTO_TEST_CASE(ColumnDataConverter) ir.bc = 200; inData[ir].emplace_back(getColData(3, 4, 0xFF00, 0xFF)); - inData[ir].emplace_back(getColData(12, 4, 0xFF)); + inData[ir].emplace_back(getColData(12, 4, 0, 0, 0xFF)); + + ir.bc = 400; + inData[ir].emplace_back(getColData(5, 1, 0xFF00, 0xFF)); + inData[ir].emplace_back(getColData(14, 1, 0, 0, 0, 0xFF)); std::vector<o2::mid::ROFRecord> rofs; std::vector<o2::mid::LocalBoardRO> outData; auto inEventType = o2::mid::EventType::Standard; o2::mid::ColumnDataToLocalBoard converter; + converter.setDebugMode(true); for (auto& item : inData) { converter.process(item.second); auto firstEntry = outData.size(); for (auto& gbtItem : converter.getData()) { - auto crateId = o2::mid::crateparams::getCrateIdFromROId(gbtItem.first); for (auto& loc : gbtItem.second) { - // The crate ID information is not encoded in the local board information, - // since it is not needed at this level (it is encoded in the feeId) - // However, when we put the info back together, we need to know the crate ID - // So we encode it in the local board ID in the output outData.emplace_back(loc); - outData.back().boardId = o2::mid::crateparams::makeUniqueLocID(crateId, loc.boardId); } rofs.push_back({item.first, inEventType, firstEntry, outData.size() - firstEntry}); } @@ -193,22 +197,27 @@ BOOST_AUTO_TEST_CASE(GBTUserLogicDecoder) o2::mid::GBTUserLogicEncoder encoder; encoder.setFeeId(feeId); for (auto& item : inData) { - encoder.process(item.second, item.first); + encoder.process(item.second, o2::InteractionRecord(item.first, 0)); } + std::vector<char> buf; + encoder.flush(buf, o2::InteractionRecord()); o2::header::RAWDataHeader rdh; - auto memSize = encoder.getBufferSize() + 64; + auto memSize = buf.size() + 64; rdh.word1 |= (memSize | (memSize << 16)); // Sets the feeId rdh.word0 |= ((5 * 2) << 16); - o2::mid::GBTUserLogicDecoder decoder; - decoder.init(feeId); - decoder.process(encoder.getBuffer(), rdh); - BOOST_REQUIRE(decoder.getROFRecords().size() == inData.size()); + auto decoder = o2::mid::createGBTDecoder(feeId); + std::vector<o2::mid::LocalBoardRO> data; + std::vector<o2::mid::ROFRecord> rofs; + std::vector<uint8_t> convertedBuffer(buf.size()); + memcpy(convertedBuffer.data(), buf.data(), buf.size()); + decoder->process(convertedBuffer, rdh, data, rofs); + BOOST_REQUIRE(rofs.size() == inData.size()); auto inItMap = inData.begin(); - for (auto rofIt = decoder.getROFRecords().begin(); rofIt != decoder.getROFRecords().end(); ++rofIt) { + for (auto rofIt = rofs.begin(); rofIt != rofs.end(); ++rofIt) { BOOST_TEST(rofIt->interactionRecord.bc == inItMap->first); BOOST_TEST(rofIt->nEntries == inItMap->second.size()); - auto outLoc = decoder.getData().begin() + rofIt->firstEntry; + auto outLoc = data.begin() + rofIt->firstEntry; for (auto inLoc = inItMap->second.begin(); inLoc != inItMap->second.end(); ++inLoc) { BOOST_TEST(inLoc->statusWord == outLoc->statusWord); BOOST_TEST(inLoc->triggerWord == outLoc->triggerWord); @@ -240,11 +249,15 @@ BOOST_AUTO_TEST_CASE(SmallSample) inData[ir].emplace_back(getColData(5, 1, 0xFFFF, 0, 0xF, 0xF0)); // Crate 10 link 1 and crate 11 link 1 inData[ir].emplace_back(getColData(41, 2, 0xFF0F, 0, 0xF0FF, 0xF)); - ir.bc = 200; - ir.orbit = 1; + ir.bc = 0xde6; + ir.orbit = 2; // Crate 12 link 1 inData[ir].emplace_back(getColData(70, 3, 0xFF00, 0xFF)); + ir.bc = 0xdea; + ir.orbit = 3; + inData[ir].emplace_back(getColData(70, 3, 0xFF00, 0xFF)); + auto [data, rofs] = encodeDecode(inData); doTest(inData, rofs, data); @@ -255,7 +268,7 @@ BOOST_AUTO_TEST_CASE(LargeBufferSample) o2::mid::Mapping mapping; std::map<o2::InteractionRecord, std::vector<o2::mid::ColumnData>> inData; // Big event that should pass the 8kB - o2::InteractionRecord ir(0, 0); + o2::InteractionRecord ir(0, 1); for (int irepeat = 0; irepeat < 4000; ++irepeat) { ++ir; for (int ide = 0; ide < o2::mid::detparams::NDetectionElements; ++ide) { diff --git a/Detectors/MUON/MID/Simulation/include/MIDSimulation/Digitizer.h b/Detectors/MUON/MID/Simulation/include/MIDSimulation/Digitizer.h index a9963b1978601..e1165f727198d 100644 --- a/Detectors/MUON/MID/Simulation/include/MIDSimulation/Digitizer.h +++ b/Detectors/MUON/MID/Simulation/include/MIDSimulation/Digitizer.h @@ -18,7 +18,7 @@ #include <random> #include <vector> #include <array> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "MIDBase/Mapping.h" #include "MIDBase/GeometryTransformer.h" diff --git a/Detectors/MUON/MID/Simulation/include/MIDSimulation/Hit.h b/Detectors/MUON/MID/Simulation/include/MIDSimulation/Hit.h index dcf0a42efdbee..62c5623018a49 100644 --- a/Detectors/MUON/MID/Simulation/include/MIDSimulation/Hit.h +++ b/Detectors/MUON/MID/Simulation/include/MIDSimulation/Hit.h @@ -29,18 +29,18 @@ class Hit : public ::o2::BasicXYZEHit<float> { public: - Hit(int trackId = 0, short deId = 0, Point3D<float> entrancePoint = {}, Point3D<float> exitPoint = {}, + Hit(int trackId = 0, short deId = 0, math_utils::Point3D<float> entrancePoint = {}, math_utils::Point3D<float> exitPoint = {}, float eloss = 0.0, float length = 0.0, float tof = 0.0); - Point3D<float> entrancePoint() const { return GetPos(); } - Point3D<float> exitPoint() const { return mExitPoint; } - Point3D<float> middlePoint() const; + math_utils::Point3D<float> entrancePoint() const { return GetPos(); } + math_utils::Point3D<float> exitPoint() const { return mExitPoint; } + math_utils::Point3D<float> middlePoint() const; short detElemId() const { return GetDetectorID(); } private: float mLength = {}; - Point3D<float> mExitPoint = {}; + math_utils::Point3D<float> mExitPoint = {}; ClassDefNV(Hit, 1); }; diff --git a/Detectors/MUON/MID/Simulation/include/MIDSimulation/Stepper.h b/Detectors/MUON/MID/Simulation/include/MIDSimulation/Stepper.h index 9d623b3067ecc..1c3551563f930 100644 --- a/Detectors/MUON/MID/Simulation/include/MIDSimulation/Stepper.h +++ b/Detectors/MUON/MID/Simulation/include/MIDSimulation/Stepper.h @@ -41,7 +41,7 @@ class Stepper float mTrackEloss{0.0}; float mTrackLength{0.0}; std::vector<o2::mid::Hit>* mHits{nullptr}; - Point3D<float> mEntrancePoint; + o2::math_utils::Point3D<float> mEntrancePoint; }; } // namespace mid diff --git a/Detectors/MUON/MID/Simulation/src/Digitizer.cxx b/Detectors/MUON/MID/Simulation/src/Digitizer.cxx index be5b6686f7804..210440d9ac147 100644 --- a/Detectors/MUON/MID/Simulation/src/Digitizer.cxx +++ b/Detectors/MUON/MID/Simulation/src/Digitizer.cxx @@ -95,7 +95,7 @@ bool Digitizer::hitToDigits(const Hit& hit) // Convert point from global to local coordinates auto midPt = hit.middlePoint(); int deId = hit.GetDetectorID(); - Point3D<double> localPoint = mTransformer.globalToLocal(deId, (double)midPt.x(), (double)midPt.y(), (double)midPt.z()); + math_utils::Point3D<double> localPoint = mTransformer.globalToLocal(deId, (double)midPt.x(), (double)midPt.y(), (double)midPt.z()); // First get the touched BP strip Mapping::MpStripIndex stripIndex = mMapping.stripByPosition(localPoint.x(), localPoint.y(), 0, deId); diff --git a/Detectors/MUON/MID/Simulation/src/Hit.cxx b/Detectors/MUON/MID/Simulation/src/Hit.cxx index 75e712b9045ab..395f7566835a0 100644 --- a/Detectors/MUON/MID/Simulation/src/Hit.cxx +++ b/Detectors/MUON/MID/Simulation/src/Hit.cxx @@ -21,15 +21,15 @@ namespace o2 { namespace mid { -Hit::Hit(int trackId, short deId, Point3D<float> entrancePoint, Point3D<float> exitPoint, +Hit::Hit(int trackId, short deId, math_utils::Point3D<float> entrancePoint, math_utils::Point3D<float> exitPoint, float eloss, float length, float tof) : o2::BasicXYZEHit<float>(entrancePoint.x(), entrancePoint.y(), entrancePoint.z(), tof, eloss, trackId, deId), mLength{length}, mExitPoint(exitPoint) { } -Point3D<float> Hit::middlePoint() const +math_utils::Point3D<float> Hit::middlePoint() const { /// Returns the point in between the entrance and exit - Point3D<float> middle(0.5 * (entrancePoint().x() + exitPoint().x()), 0.5 * (entrancePoint().y() + exitPoint().y()), 0.5 * (entrancePoint().z() + exitPoint().z())); + math_utils::Point3D<float> middle(0.5 * (entrancePoint().x() + exitPoint().x()), 0.5 * (entrancePoint().y() + exitPoint().y()), 0.5 * (entrancePoint().z() + exitPoint().z())); return std::move(middle); } diff --git a/Detectors/MUON/MID/Simulation/src/Stepper.cxx b/Detectors/MUON/MID/Simulation/src/Stepper.cxx index 25b0b9da6efb2..59451eafbc6a8 100644 --- a/Detectors/MUON/MID/Simulation/src/Stepper.cxx +++ b/Detectors/MUON/MID/Simulation/src/Stepper.cxx @@ -29,6 +29,14 @@ Stepper::~Stepper() bool Stepper::process(const TVirtualMC& vmc) { + + if (!(vmc.TrackCharge())) { + // Only charged particles + return false; + } + + // TODO: Update basing on AliRoot + o2::SimTrackStatus ts{vmc}; int detElemId; @@ -56,7 +64,7 @@ bool Stepper::process(const TVirtualMC& vmc) float x, y, z; vmc.TrackPosition(x, y, z); mHits->emplace_back(stack->GetCurrentTrackNumber(), detElemId, mEntrancePoint, - Point3D<float>{x, y, z}, mTrackEloss, mTrackLength); + math_utils::Point3D<float>{x, y, z}, mTrackEloss, mTrackLength); resetStep(); } diff --git a/Detectors/MUON/MID/Simulation/test/testGeometry.cxx b/Detectors/MUON/MID/Simulation/test/testGeometry.cxx index 2a8e5a973cfda..785e030925c3d 100644 --- a/Detectors/MUON/MID/Simulation/test/testGeometry.cxx +++ b/Detectors/MUON/MID/Simulation/test/testGeometry.cxx @@ -22,7 +22,7 @@ #include <boost/test/data/test_case.hpp> #include <fstream> #include <iostream> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "TGeoManager.h" #include "MIDSimulation/Geometry.h" #include "MIDBase/GeometryTransformer.h" @@ -72,14 +72,17 @@ bool areEqual(double a, double b) return std::fabs(b - a) < 1E-4; // 1E-4 cm = 1 micron } -bool areEqual(const Point3D<double>& p1, const Point3D<double>& p2) +bool areEqual(const o2::math_utils::Point3D<double>& p1, const o2::math_utils::Point3D<double>& p2) { - if (!areEqual(p1.x(), p2.x())) + if (!areEqual(p1.x(), p2.x())) { return false; - if (!areEqual(p1.y(), p2.y())) + } + if (!areEqual(p1.y(), p2.y())) { return false; - if (!areEqual(p1.z(), p2.z())) + } + if (!areEqual(p1.z(), p2.z())) { return false; + } return true; } diff --git a/Detectors/MUON/MID/Simulation/test/testSimulation.cxx b/Detectors/MUON/MID/Simulation/test/testSimulation.cxx index 663a6fdccf13e..47c92c41cf7c6 100644 --- a/Detectors/MUON/MID/Simulation/test/testSimulation.cxx +++ b/Detectors/MUON/MID/Simulation/test/testSimulation.cxx @@ -14,7 +14,7 @@ #include <boost/test/data/test_case.hpp> #include <sstream> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "CommonConstants/LHCConstants.h" #include "DataFormatsMID/Cluster2D.h" #include "DataFormatsMID/Cluster3D.h" @@ -117,11 +117,11 @@ std::vector<Hit> generateHits(size_t nHits, int deId, const Mapping& mapping, co std::uniform_real_distribution<double> distX(-127.5, 127.5); std::uniform_real_distribution<double> distY(-40., 40.); while (hits.size() < nHits) { - Point3D<float> point(distX(mt), distY(mt), 0.); + math_utils::Point3D<float> point(distX(mt), distY(mt), 0.); if (!mapping.stripByPosition(point.x(), point.y(), 0, deId, false).isValid()) { continue; } - Point3D<float> globalPoint = geoTrans.localToGlobal(deId, point); + math_utils::Point3D<float> globalPoint = geoTrans.localToGlobal(deId, point); hits.emplace_back(hits.size(), deId, globalPoint, globalPoint); } return hits; diff --git a/Detectors/MUON/MID/TestingSimTools/include/MIDTestingSimTools/HitFinder.h b/Detectors/MUON/MID/TestingSimTools/include/MIDTestingSimTools/HitFinder.h index 239a8c5921f75..b86ddb96984fa 100644 --- a/Detectors/MUON/MID/TestingSimTools/include/MIDTestingSimTools/HitFinder.h +++ b/Detectors/MUON/MID/TestingSimTools/include/MIDTestingSimTools/HitFinder.h @@ -16,7 +16,7 @@ #ifndef O2_MID_HITFINDER_H #define O2_MID_HITFINDER_H -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "DataFormatsMID/Cluster2D.h" #include "DataFormatsMID/Track.h" #include "MIDBase/GeometryTransformer.h" @@ -35,7 +35,7 @@ class HitFinder std::vector<Cluster2D> getLocalPositions(const Track& track, int chamber, bool withUncertainty = false) const; private: - Point3D<double> getIntersectInDefaultPlane(const Track& track, int chamber) const; + math_utils::Point3D<double> getIntersectInDefaultPlane(const Track& track, int chamber) const; Cluster2D getIntersect(const Track& track, int deId) const; int guessRPC(double yPos, int chamber) const; void addUncertainty(Cluster2D& cl, Track track) const; diff --git a/Detectors/MUON/MID/TestingSimTools/src/HitFinder.cxx b/Detectors/MUON/MID/TestingSimTools/src/HitFinder.cxx index cc977e01a6580..6f39180deaa2b 100644 --- a/Detectors/MUON/MID/TestingSimTools/src/HitFinder.cxx +++ b/Detectors/MUON/MID/TestingSimTools/src/HitFinder.cxx @@ -33,13 +33,13 @@ HitFinder::HitFinder(const GeometryTransformer& geoTrans) } //______________________________________________________________________________ -Point3D<double> HitFinder::getIntersectInDefaultPlane(const Track& track, int chamber) const +math_utils::Point3D<double> HitFinder::getIntersectInDefaultPlane(const Track& track, int chamber) const { /// Get the intersection point in the default chamber plane double defaultZ = geoparams::DefaultChamberZ[chamber]; double linePar = ((track.getPositionZ() - defaultZ) * mTanTheta - track.getPositionY()) / (track.getDirectionY() - track.getDirectionZ() * mTanTheta); - Point3D<double> point; + math_utils::Point3D<double> point; point.SetX(track.getPositionX() + linePar * track.getDirectionX()); point.SetY(track.getPositionY() + linePar * track.getDirectionY()); point.SetZ(track.getPositionZ() + linePar * track.getDirectionZ()); @@ -51,16 +51,16 @@ Cluster2D HitFinder::getIntersect(const Track& track, int deId) const { /// Get the intersection point in the specified detection elements /// The point is expressed in local coordinates - Point3D<float> localPoint = mGeometryTransformer.globalToLocal(deId, track.getPositionX(), track.getPositionY(), track.getPositionZ()); + math_utils::Point3D<float> localPoint = mGeometryTransformer.globalToLocal(deId, track.getPositionX(), track.getPositionY(), track.getPositionZ()); // Track localTrack(track); // localTrack.propagateToZ(localTrack.getPosition().z() + 20.); - // Point3D<float> localPoint2 = mGeometryTransformer.globalToLocal(deId, localTrack.getPosition()); + // math_utils::Point3D<float> localPoint2 = mGeometryTransformer.globalToLocal(deId, localTrack.getPosition()); // localTrack.setPosition(localPoint.x(), localPoint.y(), localPoint.z()); // float dZ = localPoint2.z() - localPoint.z(); // localTrack.setDirection((localPoint2.x() - localPoint.x()) / dZ, (localPoint2.y() - localPoint.y()) / dZ, 1.); - Vector3D<float> localDirection = mGeometryTransformer.globalToLocal(deId, Vector3D<float>(track.getDirectionX(), track.getDirectionY(), track.getDirectionZ())); + math_utils::Vector3D<float> localDirection = mGeometryTransformer.globalToLocal(deId, math_utils::Vector3D<float>(track.getDirectionX(), track.getDirectionY(), track.getDirectionZ())); Track localTrack; localTrack.setPosition(localPoint.x(), localPoint.y(), localPoint.z()); localTrack.setDirection(localDirection.x() / localDirection.z(), localDirection.y() / localDirection.z(), 1.); @@ -88,7 +88,7 @@ std::vector<int> HitFinder::getFiredDE(const Track& track, int chamber) const /// @param chamber Chamber ID (0-3) /// @return Vector with the list of the detection element IDs potentially fired std::vector<int> deIdList; - Point3D<double> defPos = getIntersectInDefaultPlane(track, chamber); + math_utils::Point3D<double> defPos = getIntersectInDefaultPlane(track, chamber); double xPos = defPos.x(); double xErr = std::sqrt(track.getCovarianceParameter(Track::CovarianceParamIndex::VarX)); double yPos = defPos.y() / mCosTheta; diff --git a/Detectors/MUON/MID/Tracking/include/MIDTracking/Tracker.h b/Detectors/MUON/MID/Tracking/include/MIDTracking/Tracker.h index 79a06f830da9a..52fa2a0b8eee2 100644 --- a/Detectors/MUON/MID/Tracking/include/MIDTracking/Tracker.h +++ b/Detectors/MUON/MID/Tracking/include/MIDTracking/Tracker.h @@ -89,7 +89,7 @@ class Tracker GeometryTransformer mTransformer{}; ///< Geometry transformer typedef bool (Tracker::*TrackerMemFn)(const Track&, bool, bool); - TrackerMemFn mFollowTrack{nullptr}; ///! Choice of the function to follow the track + TrackerMemFn mFollowTrack{&Tracker::followTrackKeepBest}; ///! Choice of the function to follow the track }; } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/CMakeLists.txt b/Detectors/MUON/MID/Workflow/CMakeLists.txt index 487556cd975f8..1bb8568e2124d 100644 --- a/Detectors/MUON/MID/Workflow/CMakeLists.txt +++ b/Detectors/MUON/MID/Workflow/CMakeLists.txt @@ -8,71 +8,85 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. +o2_add_library(MIDWorkflow + TARGETVARNAME targetName + SOURCES src/ClusterizerMCSpec.cxx + src/ClusterizerSpec.cxx + src/DigitReaderSpec.cxx + src/EntropyDecoderSpec.cxx + src/EntropyEncoderSpec.cxx + src/RawAggregatorSpec.cxx + src/RawDecoderSpec.cxx + src/RawWriterSpec.cxx + src/TrackerMCSpec.cxx + src/TrackerSpec.cxx + src/ZeroSuppressionSpec.cxx + PUBLIC_LINK_LIBRARIES + O2::Framework + O2::SimConfig + ms_gsl::ms_gsl + O2::DetectorsBase + O2::DPLUtils + O2::SimulationDataFormat + O2::DataFormatsMID + O2::MIDClustering + O2::MIDCTF + O2::MIDRaw + O2::MIDSimulation + O2::MIDTracking + ) + o2_add_executable( - reco-workflow-mc + digits-to-raw-workflow COMPONENT_NAME mid - SOURCES src/mid-reco-workflow-mc.cxx - src/ClusterizerMCSpec.cxx - src/DigitReaderSpec.cxx - src/RecoWorkflowMC.cxx - src/TrackerMCSpec.cxx - TARGETVARNAME - exenamerecomc + SOURCES src/digits-to-raw-workflow.cxx + TARGETVARNAME exenameraw PUBLIC_LINK_LIBRARIES - O2::Framework - O2::SimConfig - ms_gsl::ms_gsl - O2::DetectorsBase - O2::SimulationDataFormat - O2::DataFormatsMID - O2::DPLUtils - O2::MIDSimulation - O2::MIDClustering - O2::MIDTracking) + O2::MIDWorkflow) target_include_directories( - ${exenamerecomc} PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) + ${exenameraw} PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) o2_add_executable( - reco-workflow + entropy-encoder-workflow COMPONENT_NAME mid - SOURCES src/mid-reco-workflow.cxx - src/ClusterizerSpec.cxx - src/RecoWorkflow.cxx - src/RawDecoderSpec.cxx - src/RawAggregatorSpec.cxx - src/TrackerSpec.cxx - TARGETVARNAME - exenamereco + SOURCES src/entropy-encoder-workflow.cxx + TARGETVARNAME exenameentropy PUBLIC_LINK_LIBRARIES - O2::Framework - O2::SimConfig - ms_gsl::ms_gsl - O2::DetectorsBase - O2::DataFormatsMID - O2::DPLUtils - O2::MIDClustering - O2::MIDRaw - O2::MIDTracking) + O2::MIDWorkflow) target_include_directories( - ${exenamereco} PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) + ${exenameentropy} PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) o2_add_executable( - digits-to-raw-workflow + raw-to-digits-workflow COMPONENT_NAME mid - SOURCES src/mid-digits-to-raw.cxx src/DigitReaderSpec.cxx - src/DigitsToRawWorkflow.cxx src/RawWriterSpec.cxx TARGETVARNAME - exenameraw - PUBLIC_LINK_LIBRARIES - O2::Framework - O2::SimConfig - ms_gsl::ms_gsl - O2::SimulationDataFormat - O2::DataFormatsMID - O2::DPLUtils - O2::MIDSimulation - O2::MIDRaw) + SOURCES src/raw-to-digits-workflow.cxx + TARGETVARNAME exanamedigitstoraw + PUBLIC_LINK_LIBRARIES O2::MIDWorkflow) target_include_directories( - ${exenameraw} PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) + ${exanamedigitstoraw} + PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) + +o2_add_executable( + reco-workflow + COMPONENT_NAME mid + SOURCES src/reco-workflow.cxx + TARGETVARNAME exanamereco + PUBLIC_LINK_LIBRARIES O2::MIDWorkflow) + +target_include_directories( + ${exanamereco} + PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) + +o2_add_executable( + digits-reader-workflow + COMPONENT_NAME mid + SOURCES src/digits-reader-workflow.cxx + TARGETVARNAME exanamedigitsreader + PUBLIC_LINK_LIBRARIES O2::MIDWorkflow) + +target_include_directories( + ${exanamedigitsreader} + PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) diff --git a/Detectors/MUON/MID/Workflow/README.md b/Detectors/MUON/MID/Workflow/README.md index 6ac5affc46b45..219e5b3a87950 100644 --- a/Detectors/MUON/MID/Workflow/README.md +++ b/Detectors/MUON/MID/Workflow/README.md @@ -3,45 +3,109 @@ /doxy --> # MID reconstruction workflow -The MID reconstruction can start either from simulated digits or from raw data. -The reconstruction algorithm is the same in the two cases, but the workflow is slightly different. -Indeed, in the case of MC digits, the MC labels are propagated as well, thus allowing to relate the reconstructed tracks with the corresponding generated particles. + +The MID reconstruction starts from the digits and produced MID tracks. +The input digits can be: + +- MC digits +- digits obtained after decoding raw data +- digits read from CTF + +In the case of the MC digits, the MC labels are propagated as well, thus allowing to relate the reconstructed tracks with the corresponding generated particles. The procedure to run the reconstruction, either from the digits or from raw data, is detailed in the following. ## Preface: getting the digits + If you do not have the digits, you can obtain a sample with: + ```bash o2-sim -g fwmugen -m MID -n 100 o2-sim-digitizer-workflow ``` + ## Reconstruction from MC digits -To run the MC reconstruction workflow, run: + +To reconstruct the MC digits, run: + ```bash -o2-mid-reco-workflow-mc +o2-mid-digits-reader-workflow | o2-mid-reco-workflow +``` + +### Zero suppression + +The MID electronics has a default zero suppression mode. Digits are transmitted only if there is at least one strip fired in both the bending and non-bending plane in at least one of the 4 RPCs which are read-out by a local board. +The zero suppression is not applied to the MC digits that are stored on disk. +This allows to decide whether to apply the zero suppression or not at a later stage, since this mode can be disabled on data. + +The digit reader workflow reads the MC digits and applies the zero suppression as default, so that the output is compatible with what would be expected from raw data. +However, one can disable the zero suppression by running the digits reader with: + +```bash +o2-mid-digits-reader-workflow --disable-zero-suppression ``` ## Reconstruction from raw data + +To reconstruct the raw data (either from converted MC digits or real data), run: + +```bash +o2-raw-file-reader-workflow --input-conf MIDraw.cfg | o2-mid-raw-to-digits-workflow | o2-mid-reco-workflow --disable-mc +``` + The reconstruction from raw data can also be tested using as input raw data obtained from the MC digits. -### From MC digits to Raw data + +### From MC digits to raw data + To convert the MC digits into raw data format, run: + ```bash o2-mid-digits-to-raw-workflow ``` + The output will be a binary file named by default *raw_mid.dat*. -Notice that the executable also generate a configuration file that is needed to read the file with the raw reader workflow (see [here](../../../Raw/README.md) for further details) +Notice that the executable also generates a configuration file that is needed to read the file with the raw reader workflow (see [here](../../../Raw/README.md) for further details) + +## From CTF + +The CTF for MID corresponds to the digit. +So one can retrieve the digits from the CTF and run the reconstruction with the usual workflow with: -### Reconstruction from raw data -To reconstruct the raw data (either from converted MC digits or real data), run: ```bash -o2-raw-file-reader-workflow --conf mid_raw.cfg --message-per-tf | o2-mid-reco-workflow +o2-ctf-reader-workflow --ctf-input o2_ctf_0000000000.root --onlyDet MID | o2-mid-reco-workflow --disable-mc +``` + +### Generate CTF + +The MID contribution can be added to CTF by attaching the `o2-mid-entropy-encoder-workflow` device to reconstruction workflow ending by CTF writer, e.g.: + +```bash +o2-raw-file-reader-workflow --input-conf MIDraw.cfg | o2-mid-raw-to-digits-workflow | o2-mid-entropy-encoder-workflow | o2-ctf-writer-workflow ``` ## Timing + In each device belonging to the reconstruction workflow, the execution time is measured using the `chrono` c++ library. At the end of the execution, when the *stop* command is launched, the execution time is written to the `LOG(INFO)`. An example output is the following: -``` + +```less Processing time / 90 ROFs: full: 3.55542 us tracking: 2.02182 us ``` + Two timing values are provided: one is for the full execution of the device (including retrieval and sending of the DPL messages) and one which concerns only the execution of the algorithm (the tracking algorithm in the above example) -The timing refers to the time needed to process one read-out-frame, i.e. one event. \ No newline at end of file +The timing refers to the time needed to process one read-out-frame, i.e. one event. + +## Reconstruction options + +By default, the reconstruction produces clusters and tracks that are written on file. +It is however possible to only run clustering with: + +```bash +o2-mid-reco-workflow --disable-tracking +``` + +It is also possible to avoid producing a root file with: + +```bash +o2-mid-reco-workflow --disable-root-output +``` diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitReaderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitReaderSpec.h index 9fb82249b7a41..334e61e1c61c7 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitReaderSpec.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitReaderSpec.h @@ -22,7 +22,7 @@ namespace o2 { namespace mid { -framework::DataProcessorSpec getDigitReaderSpec(); +framework::DataProcessorSpec getDigitReaderSpec(bool useMC, const char* baseDescription = "DATAMC"); } } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitsToRawWorkflow.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitsToRawWorkflow.h deleted file mode 100644 index ce30b26368090..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DigitsToRawWorkflow.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDWorkflow/DigitsToRawWorkflow.h -/// \brief Definition of the reconstruction workflow for MID MC -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 11 April 2019 - -#ifndef O2_MID_DIGITSTORAWWORKFLOWSPEC_H -#define O2_MID_DIGITSTORAWWORKFLOWSPEC_H - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::WorkflowSpec getDigitsToRawWorkflow(); -} -} // namespace o2 - -#endif //O2_MID_DIGITSTORAWWORKFLOWSPEC_H diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..97c413ae8b37d --- /dev/null +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to MID ROFRecords/ColumnData stream + +#ifndef O2_MID_ENTROPYDECODER_SPEC +#define O2_MID_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "MIDCTF/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace mid +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::mid::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace mid +} // namespace o2 + +#endif diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..7dc6a90be029d --- /dev/null +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h @@ -0,0 +1,48 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert MID data to CTF (EncodedBlocks) +/// @author ruben.shahoyan@cern.ch + +#ifndef O2_MID_ENTROPYENCODER_SPEC +#define O2_MID_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "MIDCTF/CTFCoder.h" + +namespace o2 +{ +namespace mid +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::mid::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace mid +} // namespace o2 + +#endif diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawDecoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawDecoderSpec.h index 8e4388bef8ef1..f662d10681eb1 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawDecoderSpec.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawDecoderSpec.h @@ -17,12 +17,17 @@ #define O2_MID_RAWDECODERSPEC_H #include "Framework/DataProcessorSpec.h" +#include "MIDRaw/CrateMasks.h" +#include "MIDRaw/ElectronicsDelay.h" +#include "MIDRaw/FEEIdConfig.h" namespace o2 { namespace mid { -framework::DataProcessorSpec getRawDecoderSpec(bool isBare = false); +framework::DataProcessorSpec getRawDecoderSpec(bool isDebugMode = false); +framework::DataProcessorSpec getRawDecoderSpec(bool isDebugMode, const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks, const ElectronicsDelay& electronicsDelay); +framework::DataProcessorSpec getRawDecoderSpec(bool isDebugMode, const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks, const ElectronicsDelay& electronicsDelay, header::DataHeader::SubSpecificationType subSpec); } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RecoWorkflow.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RecoWorkflow.h deleted file mode 100644 index 1be6a027add3a..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RecoWorkflow.h +++ /dev/null @@ -1,30 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDWorkflow/RecoWorkflow.h -/// \brief Definition of the reconstruction workflow for MID -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 11 April 2019 - -#ifndef O2_MID_RECOWORKFLOWSPEC_H -#define O2_MID_RECOWORKFLOWSPEC_H - -#include "Framework/RootSerializationSupport.h" -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::WorkflowSpec getRecoWorkflow(); -} -} // namespace o2 - -#endif //O2_MID_RECOWORKFLOWSPEC_H diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RecoWorkflowMC.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RecoWorkflowMC.h deleted file mode 100644 index 2c3ef96078642..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RecoWorkflowMC.h +++ /dev/null @@ -1,30 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MIDWorkflow/RecoWorkflowMC.h -/// \brief Definition of the reconstruction workflow for MID MC -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 11 April 2019 - -#ifndef O2_MID_RECOWORKFLOWMCSPEC_H -#define O2_MID_RECOWORKFLOWMCSPEC_H - -#include "Framework/RootSerializationSupport.h" -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::WorkflowSpec getRecoWorkflowMC(); -} -} // namespace o2 - -#endif //O2_MID_RECOWORKFLOWMCSPEC_H diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ZeroSuppressionSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ZeroSuppressionSpec.h new file mode 100644 index 0000000000000..b819acafaccfd --- /dev/null +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ZeroSuppressionSpec.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MIDWorkflow/ZeroSuppressionSpec.h +/// \brief MID zero suppression spec +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 23 October 2020 + +#ifndef O2_MID_ZEROSUPPRESSIONSPEC_H +#define O2_MID_ZEROSUPPRESSIONSPEC_H + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace mid +{ +framework::DataProcessorSpec getZeroSuppressionSpec(bool useMC = true); +} +} // namespace o2 + +#endif //O2_MID_ZEROSUPPRESSIONSPEC_H diff --git a/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx b/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx index c69ba2b03dbd8..1e87b11e22648 100644 --- a/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx @@ -15,18 +15,23 @@ #include "MIDWorkflow/DigitReaderSpec.h" -#include "Framework/ControlService.h" +#include <sstream> +#include <string> +#include "fmt/format.h" +#include "TFile.h" +#include "TTree.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataSpecUtils.h" #include "Framework/Logger.h" #include "Framework/Output.h" #include "Framework/Task.h" +#include "Framework/WorkflowSpec.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "DataFormatsMID/ColumnData.h" #include "DataFormatsMID/ROFRecord.h" #include "MIDSimulation/ColumnDataMC.h" #include "MIDSimulation/MCLabel.h" -#include "TFile.h" -#include "TTree.h" namespace of = o2::framework; @@ -38,6 +43,7 @@ namespace mid class DigitsReaderDeviceDPL { public: + DigitsReaderDeviceDPL(bool useMC, const std::vector<header::DataDescription>& descriptions) : mUseMC(useMC), mDescriptions(descriptions) {} void init(o2::framework::InitContext& ic) { auto filename = ic.options().get<std::string>("mid-digit-infile"); @@ -54,7 +60,9 @@ class DigitsReaderDeviceDPL return; } mTree->SetBranchAddress("MIDDigit", &mDigits); - mTree->SetBranchAddress("MIDDigitMCLabels", &mMCContainer); + if (mUseMC) { + mTree->SetBranchAddress("MIDDigitMCLabels", &mMCContainer); + } mTree->SetBranchAddress("MIDROFRecords", &mROFRecords); mState = 0; } @@ -72,16 +80,19 @@ class DigitsReaderDeviceDPL for (auto ientry = 0; ientry < mTree->GetEntries(); ++ientry) { mTree->GetEntry(ientry); digits.insert(digits.end(), mDigits->begin(), mDigits->end()); - mcContainer.mergeAtBack(*mMCContainer); rofRecords.insert(rofRecords.end(), mROFRecords->begin(), mROFRecords->end()); + if (mUseMC) { + mcContainer.mergeAtBack(*mMCContainer); + } } LOG(DEBUG) << "MIDDigitsReader pushed " << digits.size() << " merged digits"; - pc.outputs().snapshot(of::Output{"MID", "DATA", 0, of::Lifetime::Timeframe}, digits); - pc.outputs().snapshot(of::Output{"MID", "DATAROF", 0, of::Lifetime::Timeframe}, rofRecords); - LOG(DEBUG) << "MIDDigitsReader pushed " << mcContainer.getIndexedSize() << " indexed digits"; - pc.outputs().snapshot(of::Output{"MID", "DATALABELS", 0, of::Lifetime::Timeframe}, mcContainer); - + pc.outputs().snapshot(of::Output{header::gDataOriginMID, mDescriptions[0], 0, of::Lifetime::Timeframe}, digits); + pc.outputs().snapshot(of::Output{header::gDataOriginMID, mDescriptions[1], 0, of::Lifetime::Timeframe}, rofRecords); + LOG(DEBUG) << "MIDDigitsReader pushed " << digits.size() << " indexed digits"; + if (mUseMC) { + pc.outputs().snapshot(of::Output{header::gDataOriginMID, mDescriptions[2], 0, of::Lifetime::Timeframe}, mcContainer); + } mState = 2; pc.services().get<of::ControlService>().endOfStream(); } @@ -92,21 +103,33 @@ class DigitsReaderDeviceDPL std::vector<o2::mid::ColumnDataMC>* mDigits{nullptr}; // not owner o2::dataformats::MCTruthContainer<MCLabel>* mMCContainer{nullptr}; // not owner std::vector<o2::mid::ROFRecord>* mROFRecords{nullptr}; // not owner + std::vector<header::DataDescription> mDescriptions{}; int mState = 0; + bool mUseMC = true; }; -framework::DataProcessorSpec getDigitReaderSpec() +framework::DataProcessorSpec getDigitReaderSpec(bool useMC, const char* baseDescription) { + std::vector<of::OutputSpec> outputs; + std::vector<header::DataDescription> descriptions; + std::stringstream ss; + ss << "A:" << header::gDataOriginMID.as<std::string>() << "/" << baseDescription << "/0"; + ss << ";B:" << header::gDataOriginMID.as<std::string>() << "/" << baseDescription << "ROF/0"; + if (useMC) { + ss << ";C:" << header::gDataOriginMID.as<std::string>() << "/" << baseDescription << "LABELS/0"; + } + auto matchers = of::select(ss.str().c_str()); + for (auto& matcher : matchers) { + outputs.emplace_back(of::DataSpecUtils::asOutputSpec(matcher)); + descriptions.emplace_back(of::DataSpecUtils::asConcreteDataDescription(matcher)); + } + return of::DataProcessorSpec{ "MIDDigitsReader", of::Inputs{}, - of::Outputs{ - of::OutputSpec{"MID", "DATA"}, - of::OutputSpec{"MID", "DATAROF"}, - of::OutputSpec{"MID", "DATALABELS"}}, - of::AlgorithmSpec{of::adaptFromTask<o2::mid::DigitsReaderDeviceDPL>()}, - of::Options{ - {"mid-digit-infile", of::VariantType::String, "middigits.root", {"Name of the input file"}}}}; + outputs, + of::AlgorithmSpec{of::adaptFromTask<o2::mid::DigitsReaderDeviceDPL>(useMC, descriptions)}, + of::Options{{"mid-digit-infile", of::VariantType::String, "middigits.root", {"Name of the input file"}}}}; } } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/DigitsToRawWorkflow.cxx b/Detectors/MUON/MID/Workflow/src/DigitsToRawWorkflow.cxx deleted file mode 100644 index b1b8c89794a40..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/DigitsToRawWorkflow.cxx +++ /dev/null @@ -1,38 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Workflow/src/DigitsToRawWorkflow.cxx -/// \brief Definition of MID reconstruction workflow for MC -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 27 September 2019 - -#include "MIDWorkflow/DigitsToRawWorkflow.h" - -#include "DPLUtils/Utils.h" -#include "MIDWorkflow/DigitReaderSpec.h" -#include "MIDWorkflow/RawWriterSpec.h" - -namespace of = o2::framework; - -namespace o2 -{ -namespace mid -{ - -of::WorkflowSpec getDigitsToRawWorkflow() -{ - of::WorkflowSpec specs; - - specs.emplace_back(getDigitReaderSpec()); - specs.emplace_back(getRawWriterSpec()); - return specs; -} -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..ac4f7364faa95 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "MIDWorkflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace mid +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("mid-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& rofs = pc.outputs().make<std::vector<o2::mid::ROFRecord>>(OutputRef{"rofs"}); + auto& cols = pc.outputs().make<std::vector<o2::mid::ColumnData>>(OutputRef{"cols"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::mid::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, rofs, cols); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << cols.size() << " MID columns in " << rofs.size() << " ROFRecords in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "MID Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"rofs"}, "MID", "DATAROF", 0, Lifetime::Timeframe}, + OutputSpec{{"cols"}, "MID", "DATA", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "mid-entropy-decoder", + Inputs{InputSpec{"ctf", "MID", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"mid-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace mid +} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..e3aca8c542833 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,81 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx +/// @brief Convert MID DATA to CTF (EncodedBlocks) +/// @author ruben.shahoyan@cern.ch + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "MIDWorkflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace mid +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("mid-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto rofs = pc.inputs().get<gsl::span<o2::mid::ROFRecord>>("rofs"); + auto cols = pc.inputs().get<gsl::span<o2::mid::ColumnData>>("cols"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"MID", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, rofs, cols); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + // eeb->print(); + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for MID in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "MID Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("rofs", "MID", "DATAROF", 0, Lifetime::Timeframe); + inputs.emplace_back("cols", "MID", "DATA", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "mid-entropy-encoder", + inputs, + Outputs{{"MID", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"mid-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace mid +} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/RawDecoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/RawDecoderSpec.cxx index b383a9aebd7a1..2b83d571e56fe 100644 --- a/Detectors/MUON/MID/Workflow/src/RawDecoderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/RawDecoderSpec.cxx @@ -16,18 +16,13 @@ #include "MIDWorkflow/RawDecoderSpec.h" #include <chrono> -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" +#include "Framework/CallbackService.h" #include "Framework/Logger.h" #include "Framework/Output.h" #include "Framework/Task.h" -#include "Framework/WorkflowSpec.h" #include "DPLUtils/DPLRawParser.h" -#include "MIDRaw/FEEIdConfig.h" -#include "MIDRaw/CrateMasks.h" +#include "Headers/RDHAny.h" #include "MIDRaw/Decoder.h" -#include "MIDRaw/GBTBareDecoder.h" -#include "MIDRaw/GBTUserLogicDecoder.h" namespace of = o2::framework; @@ -36,91 +31,88 @@ namespace o2 namespace mid { -template <typename GBTDECODER> class RawDecoderDeviceDPL { public: + RawDecoderDeviceDPL(bool isDebugMode, const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks, const ElectronicsDelay& electronicsDelay, header::DataHeader::SubSpecificationType subSpec) : mIsDebugMode(isDebugMode), mFeeIdConfig(feeIdConfig), mCrateMasks(crateMasks), mElectronicsDelay(electronicsDelay), mSubSpec(subSpec) {} + void init(of::InitContext& ic) { auto stop = [this]() { - LOG(INFO) << "Capacities: ROFRecords: " << mDecoder.getROFRecords().capacity() << " LocalBoards: " << mDecoder.getData().capacity(); + LOG(INFO) << "Capacities: ROFRecords: " << mDecoder->getROFRecords().capacity() << " LocalBoards: " << mDecoder->getData().capacity(); double scaleFactor = 1.e6 / mNROFs; LOG(INFO) << "Processing time / " << mNROFs << " ROFs: full: " << mTimer.count() * scaleFactor << " us decoding: " << mTimerAlgo.count() * scaleFactor << " us"; }; ic.services().get<of::CallbackService>().set(of::CallbackService::Id::Stop, stop); - - auto feeIdConfigFilename = ic.options().get<std::string>("feeId-config-file"); - if (!feeIdConfigFilename.empty()) { - o2::mid::FEEIdConfig feeIdConfig(feeIdConfigFilename.c_str()); - mDecoder.setFeeIdConfig(feeIdConfig); - } - auto crateMasksFilename = ic.options().get<std::string>("crate-masks-file"); - if (!crateMasksFilename.empty()) { - o2::mid::CrateMasks crateMasks(crateMasksFilename.c_str()); - mDecoder.setCrateMasks(crateMasks); - } - - auto isDebugMode = ic.options().get<bool>("debug-mode"); - mDecoder.init(isDebugMode); } void run(of::ProcessingContext& pc) { auto tStart = std::chrono::high_resolution_clock::now(); - auto msg = pc.inputs().get("mid_raw"); - auto buffer = of::DataRefUtils::as<const uint8_t>(msg); - auto tAlgoStart = std::chrono::high_resolution_clock::now(); of::DPLRawParser parser(pc.inputs()); - mDecoder.clear(); + if (!mDecoder) { + auto const* rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(parser.begin().raw()); + mDecoder = createDecoder(*rdhPtr, mIsDebugMode, mElectronicsDelay, mCrateMasks, mFeeIdConfig); + } + + mDecoder->clear(); for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - // retrieving RDH v4 - auto rdhPtr = it.get_if<o2::header::RAWDataHeader>(); + auto const* rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(it.raw()); gsl::span<const uint8_t> payload(it.data(), it.size()); - mDecoder.process(payload, *rdhPtr); + mDecoder->process(payload, *rdhPtr); } - mDecoder.flush(); mTimerAlgo += std::chrono::high_resolution_clock::now() - tAlgoStart; - pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DECODED", 0, of::Lifetime::Timeframe}, mDecoder.getData()); - pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DECODEDROF", 0, of::Lifetime::Timeframe}, mDecoder.getROFRecords()); + pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DECODED", mSubSpec}, mDecoder->getData()); + pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DECODEDROF", mSubSpec}, mDecoder->getROFRecords()); mTimer += std::chrono::high_resolution_clock::now() - tStart; - mNROFs += mDecoder.getROFRecords().size(); + mNROFs += mDecoder->getROFRecords().size(); } private: - Decoder<GBTDECODER> mDecoder{}; + std::unique_ptr<Decoder> mDecoder{nullptr}; + bool mIsDebugMode{false}; + FEEIdConfig mFeeIdConfig{}; + CrateMasks mCrateMasks{}; + ElectronicsDelay mElectronicsDelay{}; + header::DataHeader::SubSpecificationType mSubSpec{0}; std::chrono::duration<double> mTimer{0}; ///< full timer std::chrono::duration<double> mTimerAlgo{0}; ///< algorithm timer unsigned int mNROFs{0}; /// Total number of processed ROFs }; -of::AlgorithmSpec getAlgorithmSpec(bool isBare) -{ - if (isBare) { - return of::adaptFromTask<o2::mid::RawDecoderDeviceDPL<o2::mid::GBTBareDecoder>>(); - } - return of::adaptFromTask<o2::mid::RawDecoderDeviceDPL<o2::mid::GBTUserLogicDecoder>>(); -} - -framework::DataProcessorSpec getRawDecoderSpec(bool isBare) +of::DataProcessorSpec getRawDecoderSpec(bool isDebugMode, const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks, const ElectronicsDelay& electronicsDelay, const std::vector<of::InputSpec>& inputSpecs, o2::header::DataHeader::SubSpecificationType subSpecType) { - std::vector<of::InputSpec> inputSpecs{of::InputSpec{"mid_raw", of::ConcreteDataTypeMatcher{header::gDataOriginMID, header::gDataDescriptionRawData}, of::Lifetime::Timeframe}}; - std::vector<of::OutputSpec> outputSpecs{of::OutputSpec{header::gDataOriginMID, "DECODED", 0, of::Lifetime::Timeframe}, of::OutputSpec{header::gDataOriginMID, "DECODEDROF", 0, of::Lifetime::Timeframe}}; - + std::vector<of::OutputSpec> outputSpecs{of::OutputSpec{header::gDataOriginMID, "DECODED", subSpecType, of::Lifetime::Timeframe}, of::OutputSpec{header::gDataOriginMID, "DECODEDROF", subSpecType, of::Lifetime::Timeframe}}; return of::DataProcessorSpec{ "MIDRawDecoder", {inputSpecs}, {outputSpecs}, - getAlgorithmSpec(isBare), - of::Options{ - {"feeId-config-file", of::VariantType::String, "", {"Filename with crate FEE ID correspondence"}}, - {"crate-masks-file", of::VariantType::String, "", {"Filename with crate masks"}}, - {"debug-mode", of::VariantType::Bool, false, {"Debug mode: sends all boards"}}}}; + of::adaptFromTask<o2::mid::RawDecoderDeviceDPL>(isDebugMode, feeIdConfig, crateMasks, electronicsDelay, subSpecType)}; +} + +of::DataProcessorSpec getRawDecoderSpec(bool isDebugMode) +{ + return getRawDecoderSpec(isDebugMode, FEEIdConfig(), CrateMasks(), ElectronicsDelay()); +} + +of::DataProcessorSpec getRawDecoderSpec(bool isDebugMode, const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks, const ElectronicsDelay& electronicsDelay) +{ + std::vector<of::InputSpec> inputSpecs{{"mid_raw", of::ConcreteDataTypeMatcher{header::gDataOriginMID, header::gDataDescriptionRawData}, of::Lifetime::Timeframe}}; + header::DataHeader::SubSpecificationType subSpec{0}; + return getRawDecoderSpec(isDebugMode, FEEIdConfig(), CrateMasks(), ElectronicsDelay(), inputSpecs, subSpec); +} + +of::DataProcessorSpec getRawDecoderSpec(bool isDebugMode, const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks, const ElectronicsDelay& electronicsDelay, header::DataHeader::SubSpecificationType subSpec) +{ + std::vector<of::InputSpec> inputSpecs{{"mid_raw", header::gDataOriginMID, header::gDataDescriptionRawData, subSpec, o2::framework::Lifetime::Timeframe}}; + + return getRawDecoderSpec(isDebugMode, feeIdConfig, crateMasks, electronicsDelay, inputSpecs, subSpec); } } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/RawWriterSpec.cxx b/Detectors/MUON/MID/Workflow/src/RawWriterSpec.cxx index 6133d59ef291d..ce4741c29b1b5 100644 --- a/Detectors/MUON/MID/Workflow/src/RawWriterSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/RawWriterSpec.cxx @@ -44,6 +44,7 @@ class RawWriterDeviceDPL { auto filename = ic.options().get<std::string>("mid-raw-outfile"); auto dirname = ic.options().get<std::string>("mid-raw-outdir"); + auto perLink = ic.options().get<bool>("mid-raw-perlink"); if (gSystem->AccessPathName(dirname.c_str())) { if (gSystem->mkdir(dirname.c_str(), kTRUE)) { LOG(FATAL) << "could not create output directory " << dirname; @@ -53,7 +54,7 @@ class RawWriterDeviceDPL } std::string fullFName = o2::utils::concat_string(dirname, "/", filename); - mEncoder.init(fullFName.c_str()); + mEncoder.init(fullFName.c_str(), perLink); std::string inputGRP = o2::base::NameConf::getGRPFileName(); std::unique_ptr<o2::parameters::GRPObject> grp{o2::parameters::GRPObject::loadFrom(inputGRP)}; @@ -70,10 +71,10 @@ class RawWriterDeviceDPL void run(o2::framework::ProcessingContext& pc) { - auto msg = pc.inputs().get("mid_data"); + auto msg = pc.inputs().get("mid_data_mc"); gsl::span<const ColumnData> data = of::DataRefUtils::as<const ColumnData>(msg); - auto msgROF = pc.inputs().get("mid_data_rof"); + auto msgROF = pc.inputs().get("mid_data_mc_rof"); gsl::span<const ROFRecord> rofRecords = of::DataRefUtils::as<const ROFRecord>(msgROF); for (auto& rofRecord : rofRecords) { @@ -88,7 +89,7 @@ class RawWriterDeviceDPL framework::DataProcessorSpec getRawWriterSpec() { - std::vector<of::InputSpec> inputSpecs{of::InputSpec{"mid_data", header::gDataOriginMID, "DATA"}, of::InputSpec{"mid_data_rof", header::gDataOriginMID, "DATAROF"}, of::InputSpec{"mid_data_labels", header::gDataOriginMID, "DATALABELS"}}; + std::vector<of::InputSpec> inputSpecs{of::InputSpec{"mid_data_mc", header::gDataOriginMID, "DATAMC"}, of::InputSpec{"mid_data_mc_rof", header::gDataOriginMID, "DATAMCROF"}}; return of::DataProcessorSpec{ "MIDRawWriter", @@ -97,7 +98,8 @@ framework::DataProcessorSpec getRawWriterSpec() of::AlgorithmSpec{of::adaptFromTask<o2::mid::RawWriterDeviceDPL>()}, of::Options{ {"mid-raw-outdir", of::VariantType::String, ".", {"Raw file output directory"}}, - {"mid-raw-outfile", of::VariantType::String, "mid.raw", {"Raw output file name"}}, + {"mid-raw-outfile", of::VariantType::String, "mid", {"Raw output file name"}}, + {"mid-raw-perlink", of::VariantType::Bool, false, {"Output file per link"}}, {"mid-raw-header-offset", of::VariantType::Bool, false, {"Header offset in bytes"}}}}; } } // namespace mid diff --git a/Detectors/MUON/MID/Workflow/src/RecoWorkflow.cxx b/Detectors/MUON/MID/Workflow/src/RecoWorkflow.cxx deleted file mode 100644 index d46f3a4d2d2a7..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/RecoWorkflow.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Workflow/src/RecoWorkflow.cxx -/// \brief Definition of MID reconstruction workflow -/// \author Gabriele G. Fronze <gfronze at cern.ch> -/// \date 11 July 2018 - -#include "MIDWorkflow/RecoWorkflow.h" - -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsMID/ROFRecord.h" -#include "MIDWorkflow/ClusterizerSpec.h" -#include "MIDWorkflow/RawAggregatorSpec.h" -#include "MIDWorkflow/RawDecoderSpec.h" -#include "MIDWorkflow/TrackerSpec.h" - -namespace of = o2::framework; - -namespace o2 -{ -namespace mid -{ - -of::WorkflowSpec getRecoWorkflow() -{ - - auto checkReady = [](o2::framework::DataRef const& ref) { - // The default checkReady function is not defined in MakeRootTreeWriterSpec: - // this leads to a std::exception in DPL. - // While the exception seems harmless (the processing goes on without apparent consequence), - // it is quite ugly to see. - // So, let us define checkReady here. - // FIXME: to be fixed in MakeRootTreeWriterSpec - return false; - }; - - of::WorkflowSpec specs; - - specs.emplace_back(getRawDecoderSpec(false)); - specs.emplace_back(getRawAggregatorSpec()); - specs.emplace_back(getClusterizerSpec()); - specs.emplace_back(getTrackerSpec()); - specs.emplace_back(of::MakeRootTreeWriterSpec("MIDTracksWriter", - "mid-tracks.root", - "midtracks", - of::MakeRootTreeWriterSpec::TerminationPolicy::Process, - of::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - of::MakeRootTreeWriterSpec::BranchDefinition<const char*>{of::InputSpec{"mid_tracks", "MID", "TRACKS"}, "MIDTrack"}, - of::MakeRootTreeWriterSpec::BranchDefinition<const char*>{of::InputSpec{"mid_trackClusters", "MID", "TRACKCLUSTERS"}, "MIDTrackClusters"}, - of::MakeRootTreeWriterSpec::BranchDefinition<std::vector<ROFRecord>>{of::InputSpec{"mid_tracks_rof", "MID", "TRACKSROF"}, "MIDTrackROF"}, - of::MakeRootTreeWriterSpec::BranchDefinition<std::vector<ROFRecord>>{of::InputSpec{"mid_trclus_rof", "MID", "TRCLUSROF"}, "MIDTrackClusterROF"})()); - - return specs; -} -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/RecoWorkflowMC.cxx b/Detectors/MUON/MID/Workflow/src/RecoWorkflowMC.cxx deleted file mode 100644 index cb0e9d06bcf60..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/RecoWorkflowMC.cxx +++ /dev/null @@ -1,68 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 MID/Workflow/src/RecoWorkflowMC.cxx -/// \brief Definition of MID reconstruction workflow for MC -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 27 September 2019 - -#include "MIDWorkflow/RecoWorkflowMC.h" - -#include "DPLUtils/Utils.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "DataFormatsMID/ROFRecord.h" -#include "MIDWorkflow/ClusterizerMCSpec.h" -#include "MIDWorkflow/DigitReaderSpec.h" -#include "MIDWorkflow/TrackerMCSpec.h" -#include "MIDSimulation/MCClusterLabel.h" - -namespace of = o2::framework; - -namespace o2 -{ -namespace mid -{ - -of::WorkflowSpec getRecoWorkflowMC() -{ - - auto checkReady = [](o2::framework::DataRef const& ref) { - // The default checkReady function is not defined in MakeRootTreeWriterSpec: - // this leads to a std::exception in DPL. - // While the exception seems harmless (the processing goes on without apparent consequence), - // it is quite ugly to see. - // So, let us define checkReady here. - // FIXME: to be fixed in MakeRootTreeWriterSpec - return false; - }; - - of::WorkflowSpec specs; - - specs.emplace_back(getDigitReaderSpec()); - specs.emplace_back(getClusterizerMCSpec()); - specs.emplace_back(getTrackerMCSpec()); - specs.emplace_back(of::MakeRootTreeWriterSpec("MIDTrackLabelsWriter", - "mid-track-labels.root", - "midtracklabels", - of::MakeRootTreeWriterSpec::TerminationPolicy::Workflow, - of::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - of::MakeRootTreeWriterSpec::BranchDefinition<const char*>{of::InputSpec{"mid_tracks", "MID", "TRACKS"}, "MIDTrack"}, - of::MakeRootTreeWriterSpec::BranchDefinition<const char*>{of::InputSpec{"mid_trackClusters", "MID", "TRACKCLUSTERS"}, "MIDTrackClusters"}, - of::MakeRootTreeWriterSpec::BranchDefinition<std::vector<ROFRecord>>{of::InputSpec{"mid_tracks_rof", "MID", "TRACKSROF"}, "MIDTrackROF"}, - of::MakeRootTreeWriterSpec::BranchDefinition<std::vector<ROFRecord>>{of::InputSpec{"mid_trclus_rof", "MID", "TRCLUSROF"}, "MIDTrackClusterROF"}, - of::MakeRootTreeWriterSpec::BranchDefinition<dataformats::MCTruthContainer<MCCompLabel>>{of::InputSpec{"mid_track_labels", "MID", "TRACKSLABELS"}, "MIDTrackLabels"}, - of::MakeRootTreeWriterSpec::BranchDefinition<dataformats::MCTruthContainer<MCClusterLabel>>{of::InputSpec{"mid_trclus_labels", "MID", "TRCLUSLABELS"}, "MIDTrackClusterLabels"})()); - - return specs; -} -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx b/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx new file mode 100644 index 0000000000000..12a7e0940c3e5 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx @@ -0,0 +1,118 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/Workflow/src/ZeroSuppressionSpec.cxx +/// \brief MID zero suppression spec +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 23 October 2020 + +#include "MIDWorkflow/ZeroSuppressionSpec.h" + +#include <vector> +#include <gsl/gsl> +#include "Framework/Output.h" +#include "Framework/Task.h" +#include "DataFormatsMID/ColumnData.h" +#include "DataFormatsMID/ROFRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "MIDRaw/ColumnDataToLocalBoard.h" +#include "MIDRaw/DecodedDataAggregator.h" +#include "MIDSimulation/MCLabel.h" + +namespace of = o2::framework; + +namespace o2 +{ +namespace mid +{ + +class ZeroSuppressionDeviceDPL +{ + public: + ZeroSuppressionDeviceDPL(bool useMC) : mUseMC(useMC) {} + + void init(o2::framework::InitContext& ic) + { + } + + void run(o2::framework::ProcessingContext& pc) + { + const auto patterns = pc.inputs().get<gsl::span<ColumnData>>("mid_data_mc"); + + const auto inROFRecords = pc.inputs().get<gsl::span<ROFRecord>>("mid_data_mc_rof"); + + const auto inMCContainer = mUseMC ? pc.inputs().get<const o2::dataformats::MCTruthContainer<MCLabel>*>("mid_data_mc_labels") : nullptr; + + o2::dataformats::MCTruthContainer<MCLabel> outMCContainer; + + std::vector<ROFRecord> zsROFs, tmpROFs(1); + std::vector<ColumnData> zsData; + for (auto& rof : inROFRecords) { + mConverter.process(patterns.subspan(rof.firstEntry, rof.nEntries)); + if (!mConverter.getData().empty()) { + std::vector<LocalBoardRO> decodedData; + for (auto& item : mConverter.getData()) { + decodedData.insert(decodedData.end(), item.second.begin(), item.second.end()); + } + tmpROFs.front().interactionRecord = rof.interactionRecord; + tmpROFs.front().eventType = rof.eventType; + tmpROFs.front().firstEntry = 0; + tmpROFs.front().nEntries = decodedData.size(); + mAggregator.process(decodedData, tmpROFs); + auto& tmpOut = mAggregator.getData(); + zsROFs.emplace_back(rof.interactionRecord, rof.eventType, zsData.size(), tmpOut.size()); + zsData.insert(zsData.end(), tmpOut.begin(), tmpOut.end()); + + if (mUseMC) { + for (auto outColIt = zsData.begin() + zsROFs.back().firstEntry, outEnd = zsData.begin() + zsROFs.back().firstEntry + zsROFs.back().nEntries; outColIt != outEnd; ++outColIt) { + for (auto inColIt = patterns.begin() + rof.firstEntry, inEnd = patterns.begin() + rof.firstEntry + rof.nEntries; inColIt != inEnd; ++inColIt) { + if (inColIt->deId == outColIt->deId && inColIt->columnId == outColIt->columnId) { + auto inIdx = std::distance(patterns.begin(), inColIt); + auto outIdx = std::distance(zsData.begin(), outColIt); + outMCContainer.addElements(outIdx, inMCContainer->getLabels(inIdx)); + break; + } + } + } + } + } + } + + pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DATA", 0, of::Lifetime::Timeframe}, zsData); + pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DATAROF", 0, of::Lifetime::Timeframe}, zsROFs); + if (mUseMC) { + pc.outputs().snapshot(of::Output{header::gDataOriginMID, "DATALABELS", 0, of::Lifetime::Timeframe}, outMCContainer); + } + } + + private: + ColumnDataToLocalBoard mConverter{}; + DecodedDataAggregator mAggregator{}; + bool mUseMC{true}; +}; + +framework::DataProcessorSpec getZeroSuppressionSpec(bool useMC) +{ + std::vector<of::InputSpec> inputSpecs{of::InputSpec{"mid_data_mc", header::gDataOriginMID, "DATAMC"}, of::InputSpec{"mid_data_mc_rof", header::gDataOriginMID, "DATAMCROF"}}; + + std::vector<of::OutputSpec> outputSpecs{of::OutputSpec{header::gDataOriginMID, "DATA"}, of::OutputSpec{header::gDataOriginMID, "DATAROF"}}; + if (useMC) { + inputSpecs.emplace_back(of::InputSpec{"mid_data_mc_labels", header::gDataOriginMID, "DATAMCLABELS"}); + outputSpecs.emplace_back(of::OutputSpec{header::gDataOriginMID, "DATALABELS"}); + } + + return of::DataProcessorSpec{ + "MIDZeroSuppression", + {inputSpecs}, + {outputSpecs}, + of::AlgorithmSpec{of::adaptFromTask<o2::mid::ZeroSuppressionDeviceDPL>(useMC)}}; +} +} // namespace mid +} // namespace o2 \ No newline at end of file diff --git a/Detectors/MUON/MID/Workflow/src/digits-reader-workflow.cxx b/Detectors/MUON/MID/Workflow/src/digits-reader-workflow.cxx new file mode 100644 index 0000000000000..fed38802c2c04 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/digits-reader-workflow.cxx @@ -0,0 +1,52 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/Workflow/src/digits-reader-workflow.cxx +/// \brief MID digits reader workflow +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 23 October 2020 + +#include <string> +#include <vector> +#include "Framework/Variant.h" +#include "Framework/ConfigParamSpec.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsMID/ColumnData.h" +#include "DataFormatsMID/ROFRecord.h" +#include "MIDSimulation/MCLabel.h" +#include "MIDWorkflow/DigitReaderSpec.h" +#include "MIDWorkflow/ZeroSuppressionSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"disable-mc", VariantType::Bool, false, {"Do not propagate MC info"}}, + {"disable-zero-suppression", VariantType::Bool, false, {"Do not apply zero suppression"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + bool disableZS = cfgc.options().get<bool>("disable-zero-suppression"); + bool useMC = !cfgc.options().get<bool>("disable-mc"); + + WorkflowSpec specs; + specs.emplace_back(o2::mid::getDigitReaderSpec(useMC, disableZS ? "DATA" : "DATAMC")); + if (!disableZS) { + specs.emplace_back(o2::mid::getZeroSuppressionSpec(useMC)); + } + + return specs; +} diff --git a/Detectors/MUON/MID/Workflow/src/digits-to-raw-workflow.cxx b/Detectors/MUON/MID/Workflow/src/digits-to-raw-workflow.cxx new file mode 100644 index 0000000000000..385d2735373e8 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/digits-to-raw-workflow.cxx @@ -0,0 +1,42 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 mid-digits-to-raw.cxx +/// \brief MID raw to digits workflow +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 02 October 2019 + +#include <string> +#include <vector> +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" +#include "MIDWorkflow/DigitReaderSpec.h" +#include "MIDWorkflow/RawWriterSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::string keyvaluehelp("Semicolon separated key=value strings ..."); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + specs.emplace_back(o2::mid::getDigitReaderSpec(false)); + specs.emplace_back(o2::mid::getRawWriterSpec()); + + return specs; +} diff --git a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..171291272c322 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "MIDWorkflow/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::mid::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/MUON/MID/Workflow/src/mid-digits-to-raw.cxx b/Detectors/MUON/MID/Workflow/src/mid-digits-to-raw.cxx deleted file mode 100644 index 906655715aba6..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/mid-digits-to-raw.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 mid-digits-to-raw.cxx -/// \brief MID raw to digits workflow -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 02 October 2019 - -#include <string> -#include <vector> -#include "Framework/Variant.h" -#include "CommonUtils/ConfigurableParam.h" -#include "MIDWorkflow/DigitsToRawWorkflow.h" - -using namespace o2::framework; - -// ------------------------------------------------------------------ - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) -{ - std::string keyvaluehelp("Semicolon separated key=value strings ..."); - workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); -} - -// ------------------------------------------------------------------ - -#include "Framework/ConfigParamSpec.h" -#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<std::string>("configKeyValues")); - // write the configuration used for the digitizer workflow - o2::conf::ConfigurableParam::writeINI("o2mid-recoflow_configuration.ini"); - - return std::move(o2::mid::getDigitsToRawWorkflow()); -} diff --git a/Detectors/MUON/MID/Workflow/src/mid-reco-workflow-mc.cxx b/Detectors/MUON/MID/Workflow/src/mid-reco-workflow-mc.cxx deleted file mode 100644 index 11d63c0d1ee2d..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/mid-reco-workflow-mc.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 mid-reco-workflow.cxx -/// \brief MID reconstruction workflow -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 27 September 2019 - -#include <string> -#include <vector> -#include "Framework/Variant.h" -#include "CommonUtils/ConfigurableParam.h" -#include "MIDWorkflow/RecoWorkflowMC.h" - -using namespace o2::framework; - -// ------------------------------------------------------------------ - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) -{ - std::string keyvaluehelp("Semicolon separated key=value strings ..."); - workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); -} - -// ------------------------------------------------------------------ - -#include "Framework/ConfigParamSpec.h" -#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<std::string>("configKeyValues")); - // write the configuration used for the digitizer workflow - o2::conf::ConfigurableParam::writeINI("o2mid-recoflow_configuration.ini"); - - return std::move(o2::mid::getRecoWorkflowMC()); -} diff --git a/Detectors/MUON/MID/Workflow/src/mid-reco-workflow.cxx b/Detectors/MUON/MID/Workflow/src/mid-reco-workflow.cxx deleted file mode 100644 index f97d70d667e8f..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/mid-reco-workflow.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 mid-reco-workflow.cxx -/// \brief MID reconstruction workflow -/// \author Diego Stocco <Diego.Stocco at cern.ch> -/// \date 12 June 2019 - -#include <string> -#include <vector> -#include "Framework/Variant.h" -#include "CommonUtils/ConfigurableParam.h" -#include "MIDWorkflow/RecoWorkflow.h" - -using namespace o2::framework; - -// ------------------------------------------------------------------ - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) -{ - std::string keyvaluehelp("Semicolon separated key=value strings ..."); - workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); -} - -// ------------------------------------------------------------------ - -#include "Framework/ConfigParamSpec.h" -#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<std::string>("configKeyValues")); - // write the configuration used for the digitizer workflow - o2::conf::ConfigurableParam::writeINI("o2mid-recoflow_configuration.ini"); - - return std::move(o2::mid::getRecoWorkflow()); -} diff --git a/Detectors/MUON/MID/Workflow/src/raw-to-digits-workflow.cxx b/Detectors/MUON/MID/Workflow/src/raw-to-digits-workflow.cxx new file mode 100644 index 0000000000000..9f42c3dc6623d --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/raw-to-digits-workflow.cxx @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/Workflow/src/raw-to-digits-workflow.cxx +/// \brief MID raw to digits workflow +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 22 September 2020 + +#include <string> +#include <vector> +#include "Framework/Variant.h" +#include "Framework/ConfigParamSpec.h" +#include "MIDWorkflow/RawDecoderSpec.h" +#include "MIDWorkflow/RawAggregatorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> + options{ + {"feeId-config-file", VariantType::String, "", {"Filename with crate FEE ID correspondence"}}, + {"crate-masks-file", VariantType::String, "", {"Filename with crate masks"}}, + {"electronics-delay-file", VariantType::String, "", {"Filename with electronics delay"}}, + {"decode-only", o2::framework::VariantType::Bool, false, {"Output decoded boards instead of digits"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + auto feeIdConfigFilename = cfgc.options().get<std::string>("feeId-config-file"); + o2::mid::FEEIdConfig feeIdConfig; + if (!feeIdConfigFilename.empty()) { + feeIdConfig = o2::mid::FEEIdConfig(feeIdConfigFilename.c_str()); + } + auto crateMasksFilename = cfgc.options().get<std::string>("crate-masks-file"); + o2::mid::CrateMasks crateMasks; + if (!crateMasksFilename.empty()) { + crateMasks = o2::mid::CrateMasks(crateMasksFilename.c_str()); + } + auto electronicsDelayFilename = cfgc.options().get<std::string>("electronics-delay-file"); + o2::mid::ElectronicsDelay electronicsDelay; + if (!electronicsDelayFilename.empty()) { + electronicsDelay = o2::mid::readElectronicsDelay(electronicsDelayFilename.c_str()); + } + + bool decodeOnly = cfgc.options().get<bool>("decode-only"); + + o2::framework::WorkflowSpec specs; + specs.emplace_back(o2::mid::getRawDecoderSpec(false, feeIdConfig, crateMasks, electronicsDelay)); + if (!decodeOnly) { + specs.emplace_back(o2::mid::getRawAggregatorSpec()); + } + return specs; +} diff --git a/Detectors/MUON/MID/Workflow/src/reco-workflow.cxx b/Detectors/MUON/MID/Workflow/src/reco-workflow.cxx new file mode 100644 index 0000000000000..bf4a6167ae181 --- /dev/null +++ b/Detectors/MUON/MID/Workflow/src/reco-workflow.cxx @@ -0,0 +1,77 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MID/Workflow/src/reco-workflow.cxx +/// \brief MID reconstruction workflow from digits +/// \author Diego Stocco <Diego.Stocco at cern.ch> +/// \date 23 October 2020 + +#include <array> +#include <string> +#include <vector> +#include "Framework/Variant.h" +#include "Framework/ConfigParamSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsMID/ROFRecord.h" +#include "MIDSimulation/MCClusterLabel.h" +#include "MIDWorkflow/ClusterizerMCSpec.h" +#include "MIDWorkflow/ClusterizerSpec.h" +#include "MIDWorkflow/TrackerMCSpec.h" +#include "MIDWorkflow/TrackerSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> + options{ + {"disable-mc", VariantType::Bool, false, {"Do not propagate MC labels"}}, + {"disable-tracking", VariantType::Bool, false, {"Only run clustering"}}, + {"disable-root-output", VariantType::Bool, false, {"Do not write output to file"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + bool disableMC = cfgc.options().get<bool>("disable-mc"); + bool disableTracking = cfgc.options().get<bool>("disable-tracking"); + bool disableFile = cfgc.options().get<bool>("disable-root-output"); + + WorkflowSpec specs; + specs.emplace_back(disableMC ? o2::mid::getClusterizerSpec() : o2::mid::getClusterizerMCSpec()); + if (!disableTracking) { + specs.emplace_back(disableMC ? o2::mid::getTrackerSpec() : o2::mid::getTrackerMCSpec()); + } + if (!disableFile) { + std::array<o2::header::DataDescription, 2> clusterDescriptions{"CLUSTERS", "TRACKCLUSTERS"}; + std::array<o2::header::DataDescription, 2> clusterROFDescriptions{"CLUSTERSROF", "TRCLUSROF"}; + std::array<o2::header::DataDescription, 2> clusterLabelDescriptions{"CLUSTERSLABELS", "TRCLUSLABELS"}; + std::array<std::string, 2> clusterBranch{"MIDCluster", "MIDTrackCluster"}; + std::array<std::string, 2> clusterROFBranch{"MIDClusterROF", "MIDTrackClusterROF"}; + std::array<std::string, 2> clusterLabelBranch{"MIDClusterLabels", "MIDTrackClusterLabels"}; + int idx = disableTracking ? 0 : 1; + specs.emplace_back(MakeRootTreeWriterSpec("MIDRecoWriter", + "mid-reco.root", + "midreco", + MakeRootTreeWriterSpec::BranchDefinition<const char*>{InputSpec{"mid_tracks", o2::header::gDataOriginMID, "TRACKS"}, "MIDTrack", disableTracking ? 0 : 1}, + MakeRootTreeWriterSpec::BranchDefinition<const char*>{InputSpec{"mid_trackClusters", o2::header::gDataOriginMID, clusterDescriptions[idx]}, clusterBranch[idx]}, + MakeRootTreeWriterSpec::BranchDefinition<std::vector<o2::mid::ROFRecord>>{InputSpec{"mid_tracks_rof", o2::header::gDataOriginMID, "TRACKSROF"}, "MIDTrackROF", disableTracking ? 0 : 1}, + MakeRootTreeWriterSpec::BranchDefinition<std::vector<o2::mid::ROFRecord>>{InputSpec{"mid_trclus_rof", o2::header::gDataOriginMID, clusterROFDescriptions[idx]}, clusterROFBranch[idx]}, + MakeRootTreeWriterSpec::BranchDefinition<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>{InputSpec{"mid_track_labels", o2::header::gDataOriginMID, "TRACKSLABELS"}, "MIDTrackLabels", (disableTracking || disableMC) ? 0 : 1}, + MakeRootTreeWriterSpec::BranchDefinition<o2::dataformats::MCTruthContainer<o2::mid::MCClusterLabel>>{InputSpec{"mid_trclus_labels", o2::header::gDataOriginMID, clusterLabelDescriptions[idx]}, clusterLabelBranch[idx], disableMC ? 0 : 1})()); + } + + return specs; +} diff --git a/Detectors/PHOS/base/CMakeLists.txt b/Detectors/PHOS/base/CMakeLists.txt index 7c95cac985a23..cd8ee57bc8a17 100644 --- a/Detectors/PHOS/base/CMakeLists.txt +++ b/Detectors/PHOS/base/CMakeLists.txt @@ -12,10 +12,15 @@ o2_add_library(PHOSBase SOURCES src/Geometry.cxx src/Hit.cxx src/PHOSSimParams.cxx + src/RCUTrailer.cxx + src/Mapping.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::SimConfig) o2_target_root_dictionary(PHOSBase HEADERS include/PHOSBase/Geometry.h include/PHOSBase/Hit.h - include/PHOSBase/PHOSSimParams.h) + include/PHOSBase/PHOSSimParams.h + include/PHOSBase/RCUTrailer.h + include/PHOSBase/Mapping.h) +o2_data_file(COPY files DESTINATION Detectors/PHOS) diff --git a/Detectors/PHOS/base/files/Mod0RCU0.data b/Detectors/PHOS/base/files/Mod0RCU0.data new file mode 100644 index 0000000000000..4739df6e3e61e --- /dev/null +++ b/Detectors/PHOS/base/files/Mod0RCU0.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 0 0 2 + 1 0 1 2 + 2 0 2 2 + 3 0 3 2 + 4 0 4 2 + 5 0 5 2 + 6 0 6 2 + 7 0 7 2 + 8 0 8 2 + 9 0 9 2 + 10 0 10 2 + 11 0 11 2 + 12 0 12 2 + 13 0 13 2 + 14 0 14 2 + 15 0 15 2 + 16 0 16 2 + 17 0 17 2 + 18 0 18 2 + 19 0 19 2 + 20 0 20 2 + 21 0 21 2 + 22 0 22 2 + 23 0 23 2 + 24 0 24 2 + 25 0 25 2 + 26 0 26 2 + 27 0 27 2 + 28 0 28 2 + 29 0 29 2 + 30 0 30 2 + 31 0 31 2 + 32 0 32 2 + 33 0 33 2 + 34 0 34 2 + 35 0 35 2 + 36 0 36 2 + 37 0 37 2 + 38 0 38 2 + 39 0 39 2 + 40 0 40 2 + 41 0 41 2 + 42 0 42 2 + 43 0 43 2 + 44 0 44 2 + 45 0 45 2 + 46 0 46 2 + 47 0 47 2 + 48 0 48 2 + 49 0 49 2 + 50 0 50 2 + 51 0 51 2 + 52 0 52 2 + 53 0 53 2 + 54 0 54 2 + 55 0 55 2 + 56 0 56 2 + 57 0 57 2 + 58 0 58 2 + 59 0 59 2 + 60 0 60 2 + 61 0 61 2 + 62 0 62 2 + 63 0 63 2 + 64 0 64 2 + 65 0 65 2 + 66 0 66 2 + 67 0 67 2 + 68 0 68 2 + 69 0 69 2 + 70 0 70 2 + 71 0 71 2 + 72 0 72 2 + 73 0 73 2 + 74 0 74 2 + 75 0 75 2 + 76 0 76 2 + 77 0 77 2 + 78 0 78 2 + 79 0 79 2 + 80 0 80 2 + 81 0 81 2 + 82 0 82 2 + 83 0 83 2 + 84 0 84 2 + 85 0 85 2 + 86 0 86 2 + 87 0 87 2 + 88 0 88 2 + 89 0 89 2 + 90 0 90 2 + 91 0 91 2 + 92 0 92 2 + 93 0 93 2 + 94 0 94 2 + 95 0 95 2 + 96 0 96 2 + 97 0 97 2 + 98 0 98 2 + 99 0 99 2 + 100 0 100 2 + 101 0 101 2 + 102 0 102 2 + 103 0 103 2 + 104 0 104 2 + 105 0 105 2 + 106 0 106 2 + 107 0 107 2 + 108 0 108 2 + 109 0 109 2 + 110 0 110 2 + 111 0 111 2 + 112 0 112 2 + 113 0 113 2 + 114 0 114 2 + 115 0 115 2 + 116 0 116 2 + 117 0 117 2 + 118 0 118 2 + 119 0 119 2 + 120 0 120 2 + 121 0 121 2 + 122 0 122 2 + 123 0 123 2 + 124 0 124 2 + 125 0 125 2 + 126 0 126 2 + 127 0 127 2 +2048 0 2048 2 +2049 0 2049 2 +2050 0 2050 2 +2051 0 2051 2 +2052 0 2052 2 +2053 0 2053 2 +2054 0 2054 2 +2055 0 2055 2 +2056 0 2056 2 +2057 0 2057 2 +2058 0 2058 2 +2059 0 2059 2 +2060 0 2060 2 +2061 0 2061 2 +2062 0 2062 2 +2063 0 2063 2 +2064 0 2064 2 +2065 0 2065 2 +2066 0 2066 2 +2067 0 2067 2 +2068 0 2068 2 +2069 0 2069 2 +2070 0 2070 2 +2071 0 2071 2 +2072 0 2072 2 +2073 0 2073 2 +2074 0 2074 2 +2075 0 2075 2 +2076 0 2076 2 +2077 0 2077 2 +2078 0 2078 2 +2079 0 2079 2 +2080 0 2080 2 +2081 0 2081 2 +2082 0 2082 2 +2083 0 2083 2 +2084 0 2084 2 +2085 0 2085 2 +2086 0 2086 2 +2087 0 2087 2 +2088 0 2088 2 +2089 0 2089 2 +2090 0 2090 2 +2091 0 2091 2 +2092 0 2092 2 +2093 0 2093 2 +2094 0 2094 2 +2095 0 2095 2 +2096 0 2096 2 +2097 0 2097 2 +2098 0 2098 2 +2099 0 2099 2 +2100 0 2100 2 +2101 0 2101 2 +2102 0 2102 2 +2103 0 2103 2 +2104 0 2104 2 +2105 0 2105 2 +2106 0 2106 2 +2107 0 2107 2 +2108 0 2108 2 +2109 0 2109 2 +2110 0 2110 2 +2111 0 2111 2 +2112 0 2112 2 +2113 0 2113 2 +2114 0 2114 2 +2115 0 2115 2 +2116 0 2116 2 +2117 0 2117 2 +2118 0 2118 2 +2119 0 2119 2 +2120 0 2120 2 +2121 0 2121 2 +2122 0 2122 2 +2123 0 2123 2 +2124 0 2124 2 +2125 0 2125 2 +2126 0 2126 2 +2127 0 2127 2 +2128 0 2128 2 +2129 0 2129 2 +2130 0 2130 2 +2131 0 2131 2 +2132 0 2132 2 +2133 0 2133 2 +2134 0 2134 2 +2135 0 2135 2 +2136 0 2136 2 +2137 0 2137 2 +2138 0 2138 2 +2139 0 2139 2 +2140 0 2140 2 +2141 0 2141 2 +2142 0 2142 2 +2143 0 2143 2 +2144 0 2144 2 +2145 0 2145 2 +2146 0 2146 2 +2147 0 2147 2 +2148 0 2148 2 +2149 0 2149 2 +2150 0 2150 2 +2151 0 2151 2 +2152 0 2152 2 +2153 0 2153 2 +2154 0 2154 2 +2155 0 2155 2 +2156 0 2156 2 +2157 0 2157 2 +2158 0 2158 2 +2159 0 2159 2 +2160 0 2160 2 +2161 0 2161 2 +2162 0 2162 2 +2163 0 2163 2 +2164 0 2164 2 +2165 0 2165 2 +2166 0 2166 2 +2167 0 2167 2 +2168 0 2168 2 +2169 0 2169 2 +2170 0 2170 2 +2171 0 2171 2 +2172 0 2172 2 +2173 0 2173 2 +2174 0 2174 2 +2175 0 2175 2 + 128 11 29 0 + 129 11 29 1 + 130 11 28 0 + 131 11 28 1 + 132 10 29 0 + 133 10 29 1 + 134 10 28 0 + 135 10 28 1 + 136 8 28 1 + 137 8 28 0 + 138 8 29 1 + 139 8 29 0 + 140 9 28 1 + 141 9 28 0 + 142 9 29 1 + 143 9 29 0 + 160 3 28 0 + 161 3 28 1 + 162 3 29 0 + 163 3 29 1 + 164 2 28 0 + 165 2 28 1 + 166 2 29 0 + 167 2 29 1 + 168 0 29 1 + 169 0 29 0 + 170 0 28 1 + 171 0 28 0 + 172 1 29 1 + 173 1 29 0 + 174 1 28 1 + 175 1 28 0 + 176 4 28 0 + 177 4 28 1 + 178 4 29 0 + 179 4 29 1 + 180 5 28 0 + 181 5 28 1 + 182 5 29 0 + 183 5 29 1 + 184 7 29 1 + 185 7 29 0 + 186 7 28 1 + 187 7 28 0 + 188 6 29 1 + 189 6 29 0 + 190 6 28 1 + 191 6 28 0 + 192 12 29 0 + 193 12 29 1 + 194 12 28 0 + 195 12 28 1 + 196 13 29 0 + 197 13 29 1 + 198 13 28 0 + 199 13 28 1 + 200 15 28 1 + 201 15 28 0 + 202 15 29 1 + 203 15 29 0 + 204 14 28 1 + 205 14 28 0 + 206 14 29 1 + 207 14 29 0 + 256 11 31 0 + 257 11 31 1 + 258 11 30 0 + 259 11 30 1 + 260 10 31 0 + 261 10 31 1 + 262 10 30 0 + 263 10 30 1 + 264 8 30 1 + 265 8 30 0 + 266 8 31 1 + 267 8 31 0 + 268 9 30 1 + 269 9 30 0 + 270 9 31 1 + 271 9 31 0 + 288 3 30 0 + 289 3 30 1 + 290 3 31 0 + 291 3 31 1 + 292 2 30 0 + 293 2 30 1 + 294 2 31 0 + 295 2 31 1 + 296 0 31 1 + 297 0 31 0 + 298 0 30 1 + 299 0 30 0 + 300 1 31 1 + 301 1 31 0 + 302 1 30 1 + 303 1 30 0 + 304 4 30 0 + 305 4 30 1 + 306 4 31 0 + 307 4 31 1 + 308 5 30 0 + 309 5 30 1 + 310 5 31 0 + 311 5 31 1 + 312 7 31 1 + 313 7 31 0 + 314 7 30 1 + 315 7 30 0 + 316 6 31 1 + 317 6 31 0 + 318 6 30 1 + 319 6 30 0 + 320 12 31 0 + 321 12 31 1 + 322 12 30 0 + 323 12 30 1 + 324 13 31 0 + 325 13 31 1 + 326 13 30 0 + 327 13 30 1 + 328 15 30 1 + 329 15 30 0 + 330 15 31 1 + 331 15 31 0 + 332 14 30 1 + 333 14 30 0 + 334 14 31 1 + 335 14 31 0 + 384 11 33 0 + 385 11 33 1 + 386 11 32 0 + 387 11 32 1 + 388 10 33 0 + 389 10 33 1 + 390 10 32 0 + 391 10 32 1 + 392 8 32 1 + 393 8 32 0 + 394 8 33 1 + 395 8 33 0 + 396 9 32 1 + 397 9 32 0 + 398 9 33 1 + 399 9 33 0 + 416 3 32 0 + 417 3 32 1 + 418 3 33 0 + 419 3 33 1 + 420 2 32 0 + 421 2 32 1 + 422 2 33 0 + 423 2 33 1 + 424 0 33 1 + 425 0 33 0 + 426 0 32 1 + 427 0 32 0 + 428 1 33 1 + 429 1 33 0 + 430 1 32 1 + 431 1 32 0 + 432 4 32 0 + 433 4 32 1 + 434 4 33 0 + 435 4 33 1 + 436 5 32 0 + 437 5 32 1 + 438 5 33 0 + 439 5 33 1 + 440 7 33 1 + 441 7 33 0 + 442 7 32 1 + 443 7 32 0 + 444 6 33 1 + 445 6 33 0 + 446 6 32 1 + 447 6 32 0 + 448 12 33 0 + 449 12 33 1 + 450 12 32 0 + 451 12 32 1 + 452 13 33 0 + 453 13 33 1 + 454 13 32 0 + 455 13 32 1 + 456 15 32 1 + 457 15 32 0 + 458 15 33 1 + 459 15 33 0 + 460 14 32 1 + 461 14 32 0 + 462 14 33 1 + 463 14 33 0 + 512 11 35 0 + 513 11 35 1 + 514 11 34 0 + 515 11 34 1 + 516 10 35 0 + 517 10 35 1 + 518 10 34 0 + 519 10 34 1 + 520 8 34 1 + 521 8 34 0 + 522 8 35 1 + 523 8 35 0 + 524 9 34 1 + 525 9 34 0 + 526 9 35 1 + 527 9 35 0 + 544 3 34 0 + 545 3 34 1 + 546 3 35 0 + 547 3 35 1 + 548 2 34 0 + 549 2 34 1 + 550 2 35 0 + 551 2 35 1 + 552 0 35 1 + 553 0 35 0 + 554 0 34 1 + 555 0 34 0 + 556 1 35 1 + 557 1 35 0 + 558 1 34 1 + 559 1 34 0 + 560 4 34 0 + 561 4 34 1 + 562 4 35 0 + 563 4 35 1 + 564 5 34 0 + 565 5 34 1 + 566 5 35 0 + 567 5 35 1 + 568 7 35 1 + 569 7 35 0 + 570 7 34 1 + 571 7 34 0 + 572 6 35 1 + 573 6 35 0 + 574 6 34 1 + 575 6 34 0 + 576 12 35 0 + 577 12 35 1 + 578 12 34 0 + 579 12 34 1 + 580 13 35 0 + 581 13 35 1 + 582 13 34 0 + 583 13 34 1 + 584 15 34 1 + 585 15 34 0 + 586 15 35 1 + 587 15 35 0 + 588 14 34 1 + 589 14 34 0 + 590 14 35 1 + 591 14 35 0 + 640 11 37 0 + 641 11 37 1 + 642 11 36 0 + 643 11 36 1 + 644 10 37 0 + 645 10 37 1 + 646 10 36 0 + 647 10 36 1 + 648 8 36 1 + 649 8 36 0 + 650 8 37 1 + 651 8 37 0 + 652 9 36 1 + 653 9 36 0 + 654 9 37 1 + 655 9 37 0 + 672 3 36 0 + 673 3 36 1 + 674 3 37 0 + 675 3 37 1 + 676 2 36 0 + 677 2 36 1 + 678 2 37 0 + 679 2 37 1 + 680 0 37 1 + 681 0 37 0 + 682 0 36 1 + 683 0 36 0 + 684 1 37 1 + 685 1 37 0 + 686 1 36 1 + 687 1 36 0 + 688 4 36 0 + 689 4 36 1 + 690 4 37 0 + 691 4 37 1 + 692 5 36 0 + 693 5 36 1 + 694 5 37 0 + 695 5 37 1 + 696 7 37 1 + 697 7 37 0 + 698 7 36 1 + 699 7 36 0 + 700 6 37 1 + 701 6 37 0 + 702 6 36 1 + 703 6 36 0 + 704 12 37 0 + 705 12 37 1 + 706 12 36 0 + 707 12 36 1 + 708 13 37 0 + 709 13 37 1 + 710 13 36 0 + 711 13 36 1 + 712 15 36 1 + 713 15 36 0 + 714 15 37 1 + 715 15 37 0 + 716 14 36 1 + 717 14 36 0 + 718 14 37 1 + 719 14 37 0 + 768 11 39 0 + 769 11 39 1 + 770 11 38 0 + 771 11 38 1 + 772 10 39 0 + 773 10 39 1 + 774 10 38 0 + 775 10 38 1 + 776 8 38 1 + 777 8 38 0 + 778 8 39 1 + 779 8 39 0 + 780 9 38 1 + 781 9 38 0 + 782 9 39 1 + 783 9 39 0 + 800 3 38 0 + 801 3 38 1 + 802 3 39 0 + 803 3 39 1 + 804 2 38 0 + 805 2 38 1 + 806 2 39 0 + 807 2 39 1 + 808 0 39 1 + 809 0 39 0 + 810 0 38 1 + 811 0 38 0 + 812 1 39 1 + 813 1 39 0 + 814 1 38 1 + 815 1 38 0 + 816 4 38 0 + 817 4 38 1 + 818 4 39 0 + 819 4 39 1 + 820 5 38 0 + 821 5 38 1 + 822 5 39 0 + 823 5 39 1 + 824 7 39 1 + 825 7 39 0 + 826 7 38 1 + 827 7 38 0 + 828 6 39 1 + 829 6 39 0 + 830 6 38 1 + 831 6 38 0 + 832 12 39 0 + 833 12 39 1 + 834 12 38 0 + 835 12 38 1 + 836 13 39 0 + 837 13 39 1 + 838 13 38 0 + 839 13 38 1 + 840 15 38 1 + 841 15 38 0 + 842 15 39 1 + 843 15 39 0 + 844 14 38 1 + 845 14 38 0 + 846 14 39 1 + 847 14 39 0 + 896 11 41 0 + 897 11 41 1 + 898 11 40 0 + 899 11 40 1 + 900 10 41 0 + 901 10 41 1 + 902 10 40 0 + 903 10 40 1 + 904 8 40 1 + 905 8 40 0 + 906 8 41 1 + 907 8 41 0 + 908 9 40 1 + 909 9 40 0 + 910 9 41 1 + 911 9 41 0 + 928 3 40 0 + 929 3 40 1 + 930 3 41 0 + 931 3 41 1 + 932 2 40 0 + 933 2 40 1 + 934 2 41 0 + 935 2 41 1 + 936 0 41 1 + 937 0 41 0 + 938 0 40 1 + 939 0 40 0 + 940 1 41 1 + 941 1 41 0 + 942 1 40 1 + 943 1 40 0 + 944 4 40 0 + 945 4 40 1 + 946 4 41 0 + 947 4 41 1 + 948 5 40 0 + 949 5 40 1 + 950 5 41 0 + 951 5 41 1 + 952 7 41 1 + 953 7 41 0 + 954 7 40 1 + 955 7 40 0 + 956 6 41 1 + 957 6 41 0 + 958 6 40 1 + 959 6 40 0 + 960 12 41 0 + 961 12 41 1 + 962 12 40 0 + 963 12 40 1 + 964 13 41 0 + 965 13 41 1 + 966 13 40 0 + 967 13 40 1 + 968 15 40 1 + 969 15 40 0 + 970 15 41 1 + 971 15 41 0 + 972 14 40 1 + 973 14 40 0 + 974 14 41 1 + 975 14 41 0 +1024 11 43 0 +1025 11 43 1 +1026 11 42 0 +1027 11 42 1 +1028 10 43 0 +1029 10 43 1 +1030 10 42 0 +1031 10 42 1 +1032 8 42 1 +1033 8 42 0 +1034 8 43 1 +1035 8 43 0 +1036 9 42 1 +1037 9 42 0 +1038 9 43 1 +1039 9 43 0 +1056 3 42 0 +1057 3 42 1 +1058 3 43 0 +1059 3 43 1 +1060 2 42 0 +1061 2 42 1 +1062 2 43 0 +1063 2 43 1 +1064 0 43 1 +1065 0 43 0 +1066 0 42 1 +1067 0 42 0 +1068 1 43 1 +1069 1 43 0 +1070 1 42 1 +1071 1 42 0 +1072 4 42 0 +1073 4 42 1 +1074 4 43 0 +1075 4 43 1 +1076 5 42 0 +1077 5 42 1 +1078 5 43 0 +1079 5 43 1 +1080 7 43 1 +1081 7 43 0 +1082 7 42 1 +1083 7 42 0 +1084 6 43 1 +1085 6 43 0 +1086 6 42 1 +1087 6 42 0 +1088 12 43 0 +1089 12 43 1 +1090 12 42 0 +1091 12 42 1 +1092 13 43 0 +1093 13 43 1 +1094 13 42 0 +1095 13 42 1 +1096 15 42 1 +1097 15 42 0 +1098 15 43 1 +1099 15 43 0 +1100 14 42 1 +1101 14 42 0 +1102 14 43 1 +1103 14 43 0 +1152 11 45 0 +1153 11 45 1 +1154 11 44 0 +1155 11 44 1 +1156 10 45 0 +1157 10 45 1 +1158 10 44 0 +1159 10 44 1 +1160 8 44 1 +1161 8 44 0 +1162 8 45 1 +1163 8 45 0 +1164 9 44 1 +1165 9 44 0 +1166 9 45 1 +1167 9 45 0 +1184 3 44 0 +1185 3 44 1 +1186 3 45 0 +1187 3 45 1 +1188 2 44 0 +1189 2 44 1 +1190 2 45 0 +1191 2 45 1 +1192 0 45 1 +1193 0 45 0 +1194 0 44 1 +1195 0 44 0 +1196 1 45 1 +1197 1 45 0 +1198 1 44 1 +1199 1 44 0 +1200 4 44 0 +1201 4 44 1 +1202 4 45 0 +1203 4 45 1 +1204 5 44 0 +1205 5 44 1 +1206 5 45 0 +1207 5 45 1 +1208 7 45 1 +1209 7 45 0 +1210 7 44 1 +1211 7 44 0 +1212 6 45 1 +1213 6 45 0 +1214 6 44 1 +1215 6 44 0 +1216 12 45 0 +1217 12 45 1 +1218 12 44 0 +1219 12 44 1 +1220 13 45 0 +1221 13 45 1 +1222 13 44 0 +1223 13 44 1 +1224 15 44 1 +1225 15 44 0 +1226 15 45 1 +1227 15 45 0 +1228 14 44 1 +1229 14 44 0 +1230 14 45 1 +1231 14 45 0 +1280 11 47 0 +1281 11 47 1 +1282 11 46 0 +1283 11 46 1 +1284 10 47 0 +1285 10 47 1 +1286 10 46 0 +1287 10 46 1 +1288 8 46 1 +1289 8 46 0 +1290 8 47 1 +1291 8 47 0 +1292 9 46 1 +1293 9 46 0 +1294 9 47 1 +1295 9 47 0 +1312 3 46 0 +1313 3 46 1 +1314 3 47 0 +1315 3 47 1 +1316 2 46 0 +1317 2 46 1 +1318 2 47 0 +1319 2 47 1 +1320 0 47 1 +1321 0 47 0 +1322 0 46 1 +1323 0 46 0 +1324 1 47 1 +1325 1 47 0 +1326 1 46 1 +1327 1 46 0 +1328 4 46 0 +1329 4 46 1 +1330 4 47 0 +1331 4 47 1 +1332 5 46 0 +1333 5 46 1 +1334 5 47 0 +1335 5 47 1 +1336 7 47 1 +1337 7 47 0 +1338 7 46 1 +1339 7 46 0 +1340 6 47 1 +1341 6 47 0 +1342 6 46 1 +1343 6 46 0 +1344 12 47 0 +1345 12 47 1 +1346 12 46 0 +1347 12 46 1 +1348 13 47 0 +1349 13 47 1 +1350 13 46 0 +1351 13 46 1 +1352 15 46 1 +1353 15 46 0 +1354 15 47 1 +1355 15 47 0 +1356 14 46 1 +1357 14 46 0 +1358 14 47 1 +1359 14 47 0 +1408 11 49 0 +1409 11 49 1 +1410 11 48 0 +1411 11 48 1 +1412 10 49 0 +1413 10 49 1 +1414 10 48 0 +1415 10 48 1 +1416 8 48 1 +1417 8 48 0 +1418 8 49 1 +1419 8 49 0 +1420 9 48 1 +1421 9 48 0 +1422 9 49 1 +1423 9 49 0 +1440 3 48 0 +1441 3 48 1 +1442 3 49 0 +1443 3 49 1 +1444 2 48 0 +1445 2 48 1 +1446 2 49 0 +1447 2 49 1 +1448 0 49 1 +1449 0 49 0 +1450 0 48 1 +1451 0 48 0 +1452 1 49 1 +1453 1 49 0 +1454 1 48 1 +1455 1 48 0 +1456 4 48 0 +1457 4 48 1 +1458 4 49 0 +1459 4 49 1 +1460 5 48 0 +1461 5 48 1 +1462 5 49 0 +1463 5 49 1 +1464 7 49 1 +1465 7 49 0 +1466 7 48 1 +1467 7 48 0 +1468 6 49 1 +1469 6 49 0 +1470 6 48 1 +1471 6 48 0 +1472 12 49 0 +1473 12 49 1 +1474 12 48 0 +1475 12 48 1 +1476 13 49 0 +1477 13 49 1 +1478 13 48 0 +1479 13 48 1 +1480 15 48 1 +1481 15 48 0 +1482 15 49 1 +1483 15 49 0 +1484 14 48 1 +1485 14 48 0 +1486 14 49 1 +1487 14 49 0 +1536 11 51 0 +1537 11 51 1 +1538 11 50 0 +1539 11 50 1 +1540 10 51 0 +1541 10 51 1 +1542 10 50 0 +1543 10 50 1 +1544 8 50 1 +1545 8 50 0 +1546 8 51 1 +1547 8 51 0 +1548 9 50 1 +1549 9 50 0 +1550 9 51 1 +1551 9 51 0 +1568 3 50 0 +1569 3 50 1 +1570 3 51 0 +1571 3 51 1 +1572 2 50 0 +1573 2 50 1 +1574 2 51 0 +1575 2 51 1 +1576 0 51 1 +1577 0 51 0 +1578 0 50 1 +1579 0 50 0 +1580 1 51 1 +1581 1 51 0 +1582 1 50 1 +1583 1 50 0 +1584 4 50 0 +1585 4 50 1 +1586 4 51 0 +1587 4 51 1 +1588 5 50 0 +1589 5 50 1 +1590 5 51 0 +1591 5 51 1 +1592 7 51 1 +1593 7 51 0 +1594 7 50 1 +1595 7 50 0 +1596 6 51 1 +1597 6 51 0 +1598 6 50 1 +1599 6 50 0 +1600 12 51 0 +1601 12 51 1 +1602 12 50 0 +1603 12 50 1 +1604 13 51 0 +1605 13 51 1 +1606 13 50 0 +1607 13 50 1 +1608 15 50 1 +1609 15 50 0 +1610 15 51 1 +1611 15 51 0 +1612 14 50 1 +1613 14 50 0 +1614 14 51 1 +1615 14 51 0 +1664 11 53 0 +1665 11 53 1 +1666 11 52 0 +1667 11 52 1 +1668 10 53 0 +1669 10 53 1 +1670 10 52 0 +1671 10 52 1 +1672 8 52 1 +1673 8 52 0 +1674 8 53 1 +1675 8 53 0 +1676 9 52 1 +1677 9 52 0 +1678 9 53 1 +1679 9 53 0 +1696 3 52 0 +1697 3 52 1 +1698 3 53 0 +1699 3 53 1 +1700 2 52 0 +1701 2 52 1 +1702 2 53 0 +1703 2 53 1 +1704 0 53 1 +1705 0 53 0 +1706 0 52 1 +1707 0 52 0 +1708 1 53 1 +1709 1 53 0 +1710 1 52 1 +1711 1 52 0 +1712 4 52 0 +1713 4 52 1 +1714 4 53 0 +1715 4 53 1 +1716 5 52 0 +1717 5 52 1 +1718 5 53 0 +1719 5 53 1 +1720 7 53 1 +1721 7 53 0 +1722 7 52 1 +1723 7 52 0 +1724 6 53 1 +1725 6 53 0 +1726 6 52 1 +1727 6 52 0 +1728 12 53 0 +1729 12 53 1 +1730 12 52 0 +1731 12 52 1 +1732 13 53 0 +1733 13 53 1 +1734 13 52 0 +1735 13 52 1 +1736 15 52 1 +1737 15 52 0 +1738 15 53 1 +1739 15 53 0 +1740 14 52 1 +1741 14 52 0 +1742 14 53 1 +1743 14 53 0 +1792 11 55 0 +1793 11 55 1 +1794 11 54 0 +1795 11 54 1 +1796 10 55 0 +1797 10 55 1 +1798 10 54 0 +1799 10 54 1 +1800 8 54 1 +1801 8 54 0 +1802 8 55 1 +1803 8 55 0 +1804 9 54 1 +1805 9 54 0 +1806 9 55 1 +1807 9 55 0 +1824 3 54 0 +1825 3 54 1 +1826 3 55 0 +1827 3 55 1 +1828 2 54 0 +1829 2 54 1 +1830 2 55 0 +1831 2 55 1 +1832 0 55 1 +1833 0 55 0 +1834 0 54 1 +1835 0 54 0 +1836 1 55 1 +1837 1 55 0 +1838 1 54 1 +1839 1 54 0 +1840 4 54 0 +1841 4 54 1 +1842 4 55 0 +1843 4 55 1 +1844 5 54 0 +1845 5 54 1 +1846 5 55 0 +1847 5 55 1 +1848 7 55 1 +1849 7 55 0 +1850 7 54 1 +1851 7 54 0 +1852 6 55 1 +1853 6 55 0 +1854 6 54 1 +1855 6 54 0 +1856 12 55 0 +1857 12 55 1 +1858 12 54 0 +1859 12 54 1 +1860 13 55 0 +1861 13 55 1 +1862 13 54 0 +1863 13 54 1 +1864 15 54 1 +1865 15 54 0 +1866 15 55 1 +1867 15 55 0 +1868 14 54 1 +1869 14 54 0 +1870 14 55 1 +1871 14 55 0 +2176 11 27 0 +2177 11 27 1 +2178 11 26 0 +2179 11 26 1 +2180 10 27 0 +2181 10 27 1 +2182 10 26 0 +2183 10 26 1 +2184 8 26 1 +2185 8 26 0 +2186 8 27 1 +2187 8 27 0 +2188 9 26 1 +2189 9 26 0 +2190 9 27 1 +2191 9 27 0 +2208 3 26 0 +2209 3 26 1 +2210 3 27 0 +2211 3 27 1 +2212 2 26 0 +2213 2 26 1 +2214 2 27 0 +2215 2 27 1 +2216 0 27 1 +2217 0 27 0 +2218 0 26 1 +2219 0 26 0 +2220 1 27 1 +2221 1 27 0 +2222 1 26 1 +2223 1 26 0 +2224 4 26 0 +2225 4 26 1 +2226 4 27 0 +2227 4 27 1 +2228 5 26 0 +2229 5 26 1 +2230 5 27 0 +2231 5 27 1 +2232 7 27 1 +2233 7 27 0 +2234 7 26 1 +2235 7 26 0 +2236 6 27 1 +2237 6 27 0 +2238 6 26 1 +2239 6 26 0 +2240 12 27 0 +2241 12 27 1 +2242 12 26 0 +2243 12 26 1 +2244 13 27 0 +2245 13 27 1 +2246 13 26 0 +2247 13 26 1 +2248 15 26 1 +2249 15 26 0 +2250 15 27 1 +2251 15 27 0 +2252 14 26 1 +2253 14 26 0 +2254 14 27 1 +2255 14 27 0 +2304 11 25 0 +2305 11 25 1 +2306 11 24 0 +2307 11 24 1 +2308 10 25 0 +2309 10 25 1 +2310 10 24 0 +2311 10 24 1 +2312 8 24 1 +2313 8 24 0 +2314 8 25 1 +2315 8 25 0 +2316 9 24 1 +2317 9 24 0 +2318 9 25 1 +2319 9 25 0 +2336 3 24 0 +2337 3 24 1 +2338 3 25 0 +2339 3 25 1 +2340 2 24 0 +2341 2 24 1 +2342 2 25 0 +2343 2 25 1 +2344 0 25 1 +2345 0 25 0 +2346 0 24 1 +2347 0 24 0 +2348 1 25 1 +2349 1 25 0 +2350 1 24 1 +2351 1 24 0 +2352 4 24 0 +2353 4 24 1 +2354 4 25 0 +2355 4 25 1 +2356 5 24 0 +2357 5 24 1 +2358 5 25 0 +2359 5 25 1 +2360 7 25 1 +2361 7 25 0 +2362 7 24 1 +2363 7 24 0 +2364 6 25 1 +2365 6 25 0 +2366 6 24 1 +2367 6 24 0 +2368 12 25 0 +2369 12 25 1 +2370 12 24 0 +2371 12 24 1 +2372 13 25 0 +2373 13 25 1 +2374 13 24 0 +2375 13 24 1 +2376 15 24 1 +2377 15 24 0 +2378 15 25 1 +2379 15 25 0 +2380 14 24 1 +2381 14 24 0 +2382 14 25 1 +2383 14 25 0 +2432 11 23 0 +2433 11 23 1 +2434 11 22 0 +2435 11 22 1 +2436 10 23 0 +2437 10 23 1 +2438 10 22 0 +2439 10 22 1 +2440 8 22 1 +2441 8 22 0 +2442 8 23 1 +2443 8 23 0 +2444 9 22 1 +2445 9 22 0 +2446 9 23 1 +2447 9 23 0 +2464 3 22 0 +2465 3 22 1 +2466 3 23 0 +2467 3 23 1 +2468 2 22 0 +2469 2 22 1 +2470 2 23 0 +2471 2 23 1 +2472 0 23 1 +2473 0 23 0 +2474 0 22 1 +2475 0 22 0 +2476 1 23 1 +2477 1 23 0 +2478 1 22 1 +2479 1 22 0 +2480 4 22 0 +2481 4 22 1 +2482 4 23 0 +2483 4 23 1 +2484 5 22 0 +2485 5 22 1 +2486 5 23 0 +2487 5 23 1 +2488 7 23 1 +2489 7 23 0 +2490 7 22 1 +2491 7 22 0 +2492 6 23 1 +2493 6 23 0 +2494 6 22 1 +2495 6 22 0 +2496 12 23 0 +2497 12 23 1 +2498 12 22 0 +2499 12 22 1 +2500 13 23 0 +2501 13 23 1 +2502 13 22 0 +2503 13 22 1 +2504 15 22 1 +2505 15 22 0 +2506 15 23 1 +2507 15 23 0 +2508 14 22 1 +2509 14 22 0 +2510 14 23 1 +2511 14 23 0 +2560 11 21 0 +2561 11 21 1 +2562 11 20 0 +2563 11 20 1 +2564 10 21 0 +2565 10 21 1 +2566 10 20 0 +2567 10 20 1 +2568 8 20 1 +2569 8 20 0 +2570 8 21 1 +2571 8 21 0 +2572 9 20 1 +2573 9 20 0 +2574 9 21 1 +2575 9 21 0 +2592 3 20 0 +2593 3 20 1 +2594 3 21 0 +2595 3 21 1 +2596 2 20 0 +2597 2 20 1 +2598 2 21 0 +2599 2 21 1 +2600 0 21 1 +2601 0 21 0 +2602 0 20 1 +2603 0 20 0 +2604 1 21 1 +2605 1 21 0 +2606 1 20 1 +2607 1 20 0 +2608 4 20 0 +2609 4 20 1 +2610 4 21 0 +2611 4 21 1 +2612 5 20 0 +2613 5 20 1 +2614 5 21 0 +2615 5 21 1 +2616 7 21 1 +2617 7 21 0 +2618 7 20 1 +2619 7 20 0 +2620 6 21 1 +2621 6 21 0 +2622 6 20 1 +2623 6 20 0 +2624 12 21 0 +2625 12 21 1 +2626 12 20 0 +2627 12 20 1 +2628 13 21 0 +2629 13 21 1 +2630 13 20 0 +2631 13 20 1 +2632 15 20 1 +2633 15 20 0 +2634 15 21 1 +2635 15 21 0 +2636 14 20 1 +2637 14 20 0 +2638 14 21 1 +2639 14 21 0 +2688 11 19 0 +2689 11 19 1 +2690 11 18 0 +2691 11 18 1 +2692 10 19 0 +2693 10 19 1 +2694 10 18 0 +2695 10 18 1 +2696 8 18 1 +2697 8 18 0 +2698 8 19 1 +2699 8 19 0 +2700 9 18 1 +2701 9 18 0 +2702 9 19 1 +2703 9 19 0 +2720 3 18 0 +2721 3 18 1 +2722 3 19 0 +2723 3 19 1 +2724 2 18 0 +2725 2 18 1 +2726 2 19 0 +2727 2 19 1 +2728 0 19 1 +2729 0 19 0 +2730 0 18 1 +2731 0 18 0 +2732 1 19 1 +2733 1 19 0 +2734 1 18 1 +2735 1 18 0 +2736 4 18 0 +2737 4 18 1 +2738 4 19 0 +2739 4 19 1 +2740 5 18 0 +2741 5 18 1 +2742 5 19 0 +2743 5 19 1 +2744 7 19 1 +2745 7 19 0 +2746 7 18 1 +2747 7 18 0 +2748 6 19 1 +2749 6 19 0 +2750 6 18 1 +2751 6 18 0 +2752 12 19 0 +2753 12 19 1 +2754 12 18 0 +2755 12 18 1 +2756 13 19 0 +2757 13 19 1 +2758 13 18 0 +2759 13 18 1 +2760 15 18 1 +2761 15 18 0 +2762 15 19 1 +2763 15 19 0 +2764 14 18 1 +2765 14 18 0 +2766 14 19 1 +2767 14 19 0 +2816 11 17 0 +2817 11 17 1 +2818 11 16 0 +2819 11 16 1 +2820 10 17 0 +2821 10 17 1 +2822 10 16 0 +2823 10 16 1 +2824 8 16 1 +2825 8 16 0 +2826 8 17 1 +2827 8 17 0 +2828 9 16 1 +2829 9 16 0 +2830 9 17 1 +2831 9 17 0 +2848 3 16 0 +2849 3 16 1 +2850 3 17 0 +2851 3 17 1 +2852 2 16 0 +2853 2 16 1 +2854 2 17 0 +2855 2 17 1 +2856 0 17 1 +2857 0 17 0 +2858 0 16 1 +2859 0 16 0 +2860 1 17 1 +2861 1 17 0 +2862 1 16 1 +2863 1 16 0 +2864 4 16 0 +2865 4 16 1 +2866 4 17 0 +2867 4 17 1 +2868 5 16 0 +2869 5 16 1 +2870 5 17 0 +2871 5 17 1 +2872 7 17 1 +2873 7 17 0 +2874 7 16 1 +2875 7 16 0 +2876 6 17 1 +2877 6 17 0 +2878 6 16 1 +2879 6 16 0 +2880 12 17 0 +2881 12 17 1 +2882 12 16 0 +2883 12 16 1 +2884 13 17 0 +2885 13 17 1 +2886 13 16 0 +2887 13 16 1 +2888 15 16 1 +2889 15 16 0 +2890 15 17 1 +2891 15 17 0 +2892 14 16 1 +2893 14 16 0 +2894 14 17 1 +2895 14 17 0 +2944 11 15 0 +2945 11 15 1 +2946 11 14 0 +2947 11 14 1 +2948 10 15 0 +2949 10 15 1 +2950 10 14 0 +2951 10 14 1 +2952 8 14 1 +2953 8 14 0 +2954 8 15 1 +2955 8 15 0 +2956 9 14 1 +2957 9 14 0 +2958 9 15 1 +2959 9 15 0 +2976 3 14 0 +2977 3 14 1 +2978 3 15 0 +2979 3 15 1 +2980 2 14 0 +2981 2 14 1 +2982 2 15 0 +2983 2 15 1 +2984 0 15 1 +2985 0 15 0 +2986 0 14 1 +2987 0 14 0 +2988 1 15 1 +2989 1 15 0 +2990 1 14 1 +2991 1 14 0 +2992 4 14 0 +2993 4 14 1 +2994 4 15 0 +2995 4 15 1 +2996 5 14 0 +2997 5 14 1 +2998 5 15 0 +2999 5 15 1 +3000 7 15 1 +3001 7 15 0 +3002 7 14 1 +3003 7 14 0 +3004 6 15 1 +3005 6 15 0 +3006 6 14 1 +3007 6 14 0 +3008 12 15 0 +3009 12 15 1 +3010 12 14 0 +3011 12 14 1 +3012 13 15 0 +3013 13 15 1 +3014 13 14 0 +3015 13 14 1 +3016 15 14 1 +3017 15 14 0 +3018 15 15 1 +3019 15 15 0 +3020 14 14 1 +3021 14 14 0 +3022 14 15 1 +3023 14 15 0 +3072 11 13 0 +3073 11 13 1 +3074 11 12 0 +3075 11 12 1 +3076 10 13 0 +3077 10 13 1 +3078 10 12 0 +3079 10 12 1 +3080 8 12 1 +3081 8 12 0 +3082 8 13 1 +3083 8 13 0 +3084 9 12 1 +3085 9 12 0 +3086 9 13 1 +3087 9 13 0 +3104 3 12 0 +3105 3 12 1 +3106 3 13 0 +3107 3 13 1 +3108 2 12 0 +3109 2 12 1 +3110 2 13 0 +3111 2 13 1 +3112 0 13 1 +3113 0 13 0 +3114 0 12 1 +3115 0 12 0 +3116 1 13 1 +3117 1 13 0 +3118 1 12 1 +3119 1 12 0 +3120 4 12 0 +3121 4 12 1 +3122 4 13 0 +3123 4 13 1 +3124 5 12 0 +3125 5 12 1 +3126 5 13 0 +3127 5 13 1 +3128 7 13 1 +3129 7 13 0 +3130 7 12 1 +3131 7 12 0 +3132 6 13 1 +3133 6 13 0 +3134 6 12 1 +3135 6 12 0 +3136 12 13 0 +3137 12 13 1 +3138 12 12 0 +3139 12 12 1 +3140 13 13 0 +3141 13 13 1 +3142 13 12 0 +3143 13 12 1 +3144 15 12 1 +3145 15 12 0 +3146 15 13 1 +3147 15 13 0 +3148 14 12 1 +3149 14 12 0 +3150 14 13 1 +3151 14 13 0 +3200 11 11 0 +3201 11 11 1 +3202 11 10 0 +3203 11 10 1 +3204 10 11 0 +3205 10 11 1 +3206 10 10 0 +3207 10 10 1 +3208 8 10 1 +3209 8 10 0 +3210 8 11 1 +3211 8 11 0 +3212 9 10 1 +3213 9 10 0 +3214 9 11 1 +3215 9 11 0 +3232 3 10 0 +3233 3 10 1 +3234 3 11 0 +3235 3 11 1 +3236 2 10 0 +3237 2 10 1 +3238 2 11 0 +3239 2 11 1 +3240 0 11 1 +3241 0 11 0 +3242 0 10 1 +3243 0 10 0 +3244 1 11 1 +3245 1 11 0 +3246 1 10 1 +3247 1 10 0 +3248 4 10 0 +3249 4 10 1 +3250 4 11 0 +3251 4 11 1 +3252 5 10 0 +3253 5 10 1 +3254 5 11 0 +3255 5 11 1 +3256 7 11 1 +3257 7 11 0 +3258 7 10 1 +3259 7 10 0 +3260 6 11 1 +3261 6 11 0 +3262 6 10 1 +3263 6 10 0 +3264 12 11 0 +3265 12 11 1 +3266 12 10 0 +3267 12 10 1 +3268 13 11 0 +3269 13 11 1 +3270 13 10 0 +3271 13 10 1 +3272 15 10 1 +3273 15 10 0 +3274 15 11 1 +3275 15 11 0 +3276 14 10 1 +3277 14 10 0 +3278 14 11 1 +3279 14 11 0 +3328 11 9 0 +3329 11 9 1 +3330 11 8 0 +3331 11 8 1 +3332 10 9 0 +3333 10 9 1 +3334 10 8 0 +3335 10 8 1 +3336 8 8 1 +3337 8 8 0 +3338 8 9 1 +3339 8 9 0 +3340 9 8 1 +3341 9 8 0 +3342 9 9 1 +3343 9 9 0 +3360 3 8 0 +3361 3 8 1 +3362 3 9 0 +3363 3 9 1 +3364 2 8 0 +3365 2 8 1 +3366 2 9 0 +3367 2 9 1 +3368 0 9 1 +3369 0 9 0 +3370 0 8 1 +3371 0 8 0 +3372 1 9 1 +3373 1 9 0 +3374 1 8 1 +3375 1 8 0 +3376 4 8 0 +3377 4 8 1 +3378 4 9 0 +3379 4 9 1 +3380 5 8 0 +3381 5 8 1 +3382 5 9 0 +3383 5 9 1 +3384 7 9 1 +3385 7 9 0 +3386 7 8 1 +3387 7 8 0 +3388 6 9 1 +3389 6 9 0 +3390 6 8 1 +3391 6 8 0 +3392 12 9 0 +3393 12 9 1 +3394 12 8 0 +3395 12 8 1 +3396 13 9 0 +3397 13 9 1 +3398 13 8 0 +3399 13 8 1 +3400 15 8 1 +3401 15 8 0 +3402 15 9 1 +3403 15 9 0 +3404 14 8 1 +3405 14 8 0 +3406 14 9 1 +3407 14 9 0 +3456 11 7 0 +3457 11 7 1 +3458 11 6 0 +3459 11 6 1 +3460 10 7 0 +3461 10 7 1 +3462 10 6 0 +3463 10 6 1 +3464 8 6 1 +3465 8 6 0 +3466 8 7 1 +3467 8 7 0 +3468 9 6 1 +3469 9 6 0 +3470 9 7 1 +3471 9 7 0 +3488 3 6 0 +3489 3 6 1 +3490 3 7 0 +3491 3 7 1 +3492 2 6 0 +3493 2 6 1 +3494 2 7 0 +3495 2 7 1 +3496 0 7 1 +3497 0 7 0 +3498 0 6 1 +3499 0 6 0 +3500 1 7 1 +3501 1 7 0 +3502 1 6 1 +3503 1 6 0 +3504 4 6 0 +3505 4 6 1 +3506 4 7 0 +3507 4 7 1 +3508 5 6 0 +3509 5 6 1 +3510 5 7 0 +3511 5 7 1 +3512 7 7 1 +3513 7 7 0 +3514 7 6 1 +3515 7 6 0 +3516 6 7 1 +3517 6 7 0 +3518 6 6 1 +3519 6 6 0 +3520 12 7 0 +3521 12 7 1 +3522 12 6 0 +3523 12 6 1 +3524 13 7 0 +3525 13 7 1 +3526 13 6 0 +3527 13 6 1 +3528 15 6 1 +3529 15 6 0 +3530 15 7 1 +3531 15 7 0 +3532 14 6 1 +3533 14 6 0 +3534 14 7 1 +3535 14 7 0 +3584 11 5 0 +3585 11 5 1 +3586 11 4 0 +3587 11 4 1 +3588 10 5 0 +3589 10 5 1 +3590 10 4 0 +3591 10 4 1 +3592 8 4 1 +3593 8 4 0 +3594 8 5 1 +3595 8 5 0 +3596 9 4 1 +3597 9 4 0 +3598 9 5 1 +3599 9 5 0 +3616 3 4 0 +3617 3 4 1 +3618 3 5 0 +3619 3 5 1 +3620 2 4 0 +3621 2 4 1 +3622 2 5 0 +3623 2 5 1 +3624 0 5 1 +3625 0 5 0 +3626 0 4 1 +3627 0 4 0 +3628 1 5 1 +3629 1 5 0 +3630 1 4 1 +3631 1 4 0 +3632 4 4 0 +3633 4 4 1 +3634 4 5 0 +3635 4 5 1 +3636 5 4 0 +3637 5 4 1 +3638 5 5 0 +3639 5 5 1 +3640 7 5 1 +3641 7 5 0 +3642 7 4 1 +3643 7 4 0 +3644 6 5 1 +3645 6 5 0 +3646 6 4 1 +3647 6 4 0 +3648 12 5 0 +3649 12 5 1 +3650 12 4 0 +3651 12 4 1 +3652 13 5 0 +3653 13 5 1 +3654 13 4 0 +3655 13 4 1 +3656 15 4 1 +3657 15 4 0 +3658 15 5 1 +3659 15 5 0 +3660 14 4 1 +3661 14 4 0 +3662 14 5 1 +3663 14 5 0 +3712 11 3 0 +3713 11 3 1 +3714 11 2 0 +3715 11 2 1 +3716 10 3 0 +3717 10 3 1 +3718 10 2 0 +3719 10 2 1 +3720 8 2 1 +3721 8 2 0 +3722 8 3 1 +3723 8 3 0 +3724 9 2 1 +3725 9 2 0 +3726 9 3 1 +3727 9 3 0 +3744 3 2 0 +3745 3 2 1 +3746 3 3 0 +3747 3 3 1 +3748 2 2 0 +3749 2 2 1 +3750 2 3 0 +3751 2 3 1 +3752 0 3 1 +3753 0 3 0 +3754 0 2 1 +3755 0 2 0 +3756 1 3 1 +3757 1 3 0 +3758 1 2 1 +3759 1 2 0 +3760 4 2 0 +3761 4 2 1 +3762 4 3 0 +3763 4 3 1 +3764 5 2 0 +3765 5 2 1 +3766 5 3 0 +3767 5 3 1 +3768 7 3 1 +3769 7 3 0 +3770 7 2 1 +3771 7 2 0 +3772 6 3 1 +3773 6 3 0 +3774 6 2 1 +3775 6 2 0 +3776 12 3 0 +3777 12 3 1 +3778 12 2 0 +3779 12 2 1 +3780 13 3 0 +3781 13 3 1 +3782 13 2 0 +3783 13 2 1 +3784 15 2 1 +3785 15 2 0 +3786 15 3 1 +3787 15 3 0 +3788 14 2 1 +3789 14 2 0 +3790 14 3 1 +3791 14 3 0 +3840 11 1 0 +3841 11 1 1 +3842 11 0 0 +3843 11 0 1 +3844 10 1 0 +3845 10 1 1 +3846 10 0 0 +3847 10 0 1 +3848 8 0 1 +3849 8 0 0 +3850 8 1 1 +3851 8 1 0 +3852 9 0 1 +3853 9 0 0 +3854 9 1 1 +3855 9 1 0 +3872 3 0 0 +3873 3 0 1 +3874 3 1 0 +3875 3 1 1 +3876 2 0 0 +3877 2 0 1 +3878 2 1 0 +3879 2 1 1 +3880 0 1 1 +3881 0 1 0 +3882 0 0 1 +3883 0 0 0 +3884 1 1 1 +3885 1 1 0 +3886 1 0 1 +3887 1 0 0 +3888 4 0 0 +3889 4 0 1 +3890 4 1 0 +3891 4 1 1 +3892 5 0 0 +3893 5 0 1 +3894 5 1 0 +3895 5 1 1 +3896 7 1 1 +3897 7 1 0 +3898 7 0 1 +3899 7 0 0 +3900 6 1 1 +3901 6 1 0 +3902 6 0 1 +3903 6 0 0 +3904 12 1 0 +3905 12 1 1 +3906 12 0 0 +3907 12 0 1 +3908 13 1 0 +3909 13 1 1 +3910 13 0 0 +3911 13 0 1 +3912 15 0 1 +3913 15 0 0 +3914 15 1 1 +3915 15 1 0 +3916 14 0 1 +3917 14 0 0 +3918 14 1 1 +3919 14 1 0 diff --git a/Detectors/PHOS/base/files/Mod0RCU1.data b/Detectors/PHOS/base/files/Mod0RCU1.data new file mode 100644 index 0000000000000..db125226496cf --- /dev/null +++ b/Detectors/PHOS/base/files/Mod0RCU1.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 1 0 2 + 1 1 1 2 + 2 1 2 2 + 3 1 3 2 + 4 1 4 2 + 5 1 5 2 + 6 1 6 2 + 7 1 7 2 + 8 1 8 2 + 9 1 9 2 + 10 1 10 2 + 11 1 11 2 + 12 1 12 2 + 13 1 13 2 + 14 1 14 2 + 15 1 15 2 + 16 1 16 2 + 17 1 17 2 + 18 1 18 2 + 19 1 19 2 + 20 1 20 2 + 21 1 21 2 + 22 1 22 2 + 23 1 23 2 + 24 1 24 2 + 25 1 25 2 + 26 1 26 2 + 27 1 27 2 + 28 1 28 2 + 29 1 29 2 + 30 1 30 2 + 31 1 31 2 + 32 1 32 2 + 33 1 33 2 + 34 1 34 2 + 35 1 35 2 + 36 1 36 2 + 37 1 37 2 + 38 1 38 2 + 39 1 39 2 + 40 1 40 2 + 41 1 41 2 + 42 1 42 2 + 43 1 43 2 + 44 1 44 2 + 45 1 45 2 + 46 1 46 2 + 47 1 47 2 + 48 1 48 2 + 49 1 49 2 + 50 1 50 2 + 51 1 51 2 + 52 1 52 2 + 53 1 53 2 + 54 1 54 2 + 55 1 55 2 + 56 1 56 2 + 57 1 57 2 + 58 1 58 2 + 59 1 59 2 + 60 1 60 2 + 61 1 61 2 + 62 1 62 2 + 63 1 63 2 + 64 1 64 2 + 65 1 65 2 + 66 1 66 2 + 67 1 67 2 + 68 1 68 2 + 69 1 69 2 + 70 1 70 2 + 71 1 71 2 + 72 1 72 2 + 73 1 73 2 + 74 1 74 2 + 75 1 75 2 + 76 1 76 2 + 77 1 77 2 + 78 1 78 2 + 79 1 79 2 + 80 1 80 2 + 81 1 81 2 + 82 1 82 2 + 83 1 83 2 + 84 1 84 2 + 85 1 85 2 + 86 1 86 2 + 87 1 87 2 + 88 1 88 2 + 89 1 89 2 + 90 1 90 2 + 91 1 91 2 + 92 1 92 2 + 93 1 93 2 + 94 1 94 2 + 95 1 95 2 + 96 1 96 2 + 97 1 97 2 + 98 1 98 2 + 99 1 99 2 + 100 1 100 2 + 101 1 101 2 + 102 1 102 2 + 103 1 103 2 + 104 1 104 2 + 105 1 105 2 + 106 1 106 2 + 107 1 107 2 + 108 1 108 2 + 109 1 109 2 + 110 1 110 2 + 111 1 111 2 + 112 1 112 2 + 113 1 113 2 + 114 1 114 2 + 115 1 115 2 + 116 1 116 2 + 117 1 117 2 + 118 1 118 2 + 119 1 119 2 + 120 1 120 2 + 121 1 121 2 + 122 1 122 2 + 123 1 123 2 + 124 1 124 2 + 125 1 125 2 + 126 1 126 2 + 127 1 127 2 +2048 1 2048 2 +2049 1 2049 2 +2050 1 2050 2 +2051 1 2051 2 +2052 1 2052 2 +2053 1 2053 2 +2054 1 2054 2 +2055 1 2055 2 +2056 1 2056 2 +2057 1 2057 2 +2058 1 2058 2 +2059 1 2059 2 +2060 1 2060 2 +2061 1 2061 2 +2062 1 2062 2 +2063 1 2063 2 +2064 1 2064 2 +2065 1 2065 2 +2066 1 2066 2 +2067 1 2067 2 +2068 1 2068 2 +2069 1 2069 2 +2070 1 2070 2 +2071 1 2071 2 +2072 1 2072 2 +2073 1 2073 2 +2074 1 2074 2 +2075 1 2075 2 +2076 1 2076 2 +2077 1 2077 2 +2078 1 2078 2 +2079 1 2079 2 +2080 1 2080 2 +2081 1 2081 2 +2082 1 2082 2 +2083 1 2083 2 +2084 1 2084 2 +2085 1 2085 2 +2086 1 2086 2 +2087 1 2087 2 +2088 1 2088 2 +2089 1 2089 2 +2090 1 2090 2 +2091 1 2091 2 +2092 1 2092 2 +2093 1 2093 2 +2094 1 2094 2 +2095 1 2095 2 +2096 1 2096 2 +2097 1 2097 2 +2098 1 2098 2 +2099 1 2099 2 +2100 1 2100 2 +2101 1 2101 2 +2102 1 2102 2 +2103 1 2103 2 +2104 1 2104 2 +2105 1 2105 2 +2106 1 2106 2 +2107 1 2107 2 +2108 1 2108 2 +2109 1 2109 2 +2110 1 2110 2 +2111 1 2111 2 +2112 1 2112 2 +2113 1 2113 2 +2114 1 2114 2 +2115 1 2115 2 +2116 1 2116 2 +2117 1 2117 2 +2118 1 2118 2 +2119 1 2119 2 +2120 1 2120 2 +2121 1 2121 2 +2122 1 2122 2 +2123 1 2123 2 +2124 1 2124 2 +2125 1 2125 2 +2126 1 2126 2 +2127 1 2127 2 +2128 1 2128 2 +2129 1 2129 2 +2130 1 2130 2 +2131 1 2131 2 +2132 1 2132 2 +2133 1 2133 2 +2134 1 2134 2 +2135 1 2135 2 +2136 1 2136 2 +2137 1 2137 2 +2138 1 2138 2 +2139 1 2139 2 +2140 1 2140 2 +2141 1 2141 2 +2142 1 2142 2 +2143 1 2143 2 +2144 1 2144 2 +2145 1 2145 2 +2146 1 2146 2 +2147 1 2147 2 +2148 1 2148 2 +2149 1 2149 2 +2150 1 2150 2 +2151 1 2151 2 +2152 1 2152 2 +2153 1 2153 2 +2154 1 2154 2 +2155 1 2155 2 +2156 1 2156 2 +2157 1 2157 2 +2158 1 2158 2 +2159 1 2159 2 +2160 1 2160 2 +2161 1 2161 2 +2162 1 2162 2 +2163 1 2163 2 +2164 1 2164 2 +2165 1 2165 2 +2166 1 2166 2 +2167 1 2167 2 +2168 1 2168 2 +2169 1 2169 2 +2170 1 2170 2 +2171 1 2171 2 +2172 1 2172 2 +2173 1 2173 2 +2174 1 2174 2 +2175 1 2175 2 + 128 27 29 0 + 129 27 29 1 + 130 27 28 0 + 131 27 28 1 + 132 26 29 0 + 133 26 29 1 + 134 26 28 0 + 135 26 28 1 + 136 24 28 1 + 137 24 28 0 + 138 24 29 1 + 139 24 29 0 + 140 25 28 1 + 141 25 28 0 + 142 25 29 1 + 143 25 29 0 + 160 19 28 0 + 161 19 28 1 + 162 19 29 0 + 163 19 29 1 + 164 18 28 0 + 165 18 28 1 + 166 18 29 0 + 167 18 29 1 + 168 16 29 1 + 169 16 29 0 + 170 16 28 1 + 171 16 28 0 + 172 17 29 1 + 173 17 29 0 + 174 17 28 1 + 175 17 28 0 + 176 20 28 0 + 177 20 28 1 + 178 20 29 0 + 179 20 29 1 + 180 21 28 0 + 181 21 28 1 + 182 21 29 0 + 183 21 29 1 + 184 23 29 1 + 185 23 29 0 + 186 23 28 1 + 187 23 28 0 + 188 22 29 1 + 189 22 29 0 + 190 22 28 1 + 191 22 28 0 + 192 28 29 0 + 193 28 29 1 + 194 28 28 0 + 195 28 28 1 + 196 29 29 0 + 197 29 29 1 + 198 29 28 0 + 199 29 28 1 + 200 31 28 1 + 201 31 28 0 + 202 31 29 1 + 203 31 29 0 + 204 30 28 1 + 205 30 28 0 + 206 30 29 1 + 207 30 29 0 + 256 27 31 0 + 257 27 31 1 + 258 27 30 0 + 259 27 30 1 + 260 26 31 0 + 261 26 31 1 + 262 26 30 0 + 263 26 30 1 + 264 24 30 1 + 265 24 30 0 + 266 24 31 1 + 267 24 31 0 + 268 25 30 1 + 269 25 30 0 + 270 25 31 1 + 271 25 31 0 + 288 19 30 0 + 289 19 30 1 + 290 19 31 0 + 291 19 31 1 + 292 18 30 0 + 293 18 30 1 + 294 18 31 0 + 295 18 31 1 + 296 16 31 1 + 297 16 31 0 + 298 16 30 1 + 299 16 30 0 + 300 17 31 1 + 301 17 31 0 + 302 17 30 1 + 303 17 30 0 + 304 20 30 0 + 305 20 30 1 + 306 20 31 0 + 307 20 31 1 + 308 21 30 0 + 309 21 30 1 + 310 21 31 0 + 311 21 31 1 + 312 23 31 1 + 313 23 31 0 + 314 23 30 1 + 315 23 30 0 + 316 22 31 1 + 317 22 31 0 + 318 22 30 1 + 319 22 30 0 + 320 28 31 0 + 321 28 31 1 + 322 28 30 0 + 323 28 30 1 + 324 29 31 0 + 325 29 31 1 + 326 29 30 0 + 327 29 30 1 + 328 31 30 1 + 329 31 30 0 + 330 31 31 1 + 331 31 31 0 + 332 30 30 1 + 333 30 30 0 + 334 30 31 1 + 335 30 31 0 + 384 27 33 0 + 385 27 33 1 + 386 27 32 0 + 387 27 32 1 + 388 26 33 0 + 389 26 33 1 + 390 26 32 0 + 391 26 32 1 + 392 24 32 1 + 393 24 32 0 + 394 24 33 1 + 395 24 33 0 + 396 25 32 1 + 397 25 32 0 + 398 25 33 1 + 399 25 33 0 + 416 19 32 0 + 417 19 32 1 + 418 19 33 0 + 419 19 33 1 + 420 18 32 0 + 421 18 32 1 + 422 18 33 0 + 423 18 33 1 + 424 16 33 1 + 425 16 33 0 + 426 16 32 1 + 427 16 32 0 + 428 17 33 1 + 429 17 33 0 + 430 17 32 1 + 431 17 32 0 + 432 20 32 0 + 433 20 32 1 + 434 20 33 0 + 435 20 33 1 + 436 21 32 0 + 437 21 32 1 + 438 21 33 0 + 439 21 33 1 + 440 23 33 1 + 441 23 33 0 + 442 23 32 1 + 443 23 32 0 + 444 22 33 1 + 445 22 33 0 + 446 22 32 1 + 447 22 32 0 + 448 28 33 0 + 449 28 33 1 + 450 28 32 0 + 451 28 32 1 + 452 29 33 0 + 453 29 33 1 + 454 29 32 0 + 455 29 32 1 + 456 31 32 1 + 457 31 32 0 + 458 31 33 1 + 459 31 33 0 + 460 30 32 1 + 461 30 32 0 + 462 30 33 1 + 463 30 33 0 + 512 27 35 0 + 513 27 35 1 + 514 27 34 0 + 515 27 34 1 + 516 26 35 0 + 517 26 35 1 + 518 26 34 0 + 519 26 34 1 + 520 24 34 1 + 521 24 34 0 + 522 24 35 1 + 523 24 35 0 + 524 25 34 1 + 525 25 34 0 + 526 25 35 1 + 527 25 35 0 + 544 19 34 0 + 545 19 34 1 + 546 19 35 0 + 547 19 35 1 + 548 18 34 0 + 549 18 34 1 + 550 18 35 0 + 551 18 35 1 + 552 16 35 1 + 553 16 35 0 + 554 16 34 1 + 555 16 34 0 + 556 17 35 1 + 557 17 35 0 + 558 17 34 1 + 559 17 34 0 + 560 20 34 0 + 561 20 34 1 + 562 20 35 0 + 563 20 35 1 + 564 21 34 0 + 565 21 34 1 + 566 21 35 0 + 567 21 35 1 + 568 23 35 1 + 569 23 35 0 + 570 23 34 1 + 571 23 34 0 + 572 22 35 1 + 573 22 35 0 + 574 22 34 1 + 575 22 34 0 + 576 28 35 0 + 577 28 35 1 + 578 28 34 0 + 579 28 34 1 + 580 29 35 0 + 581 29 35 1 + 582 29 34 0 + 583 29 34 1 + 584 31 34 1 + 585 31 34 0 + 586 31 35 1 + 587 31 35 0 + 588 30 34 1 + 589 30 34 0 + 590 30 35 1 + 591 30 35 0 + 640 27 37 0 + 641 27 37 1 + 642 27 36 0 + 643 27 36 1 + 644 26 37 0 + 645 26 37 1 + 646 26 36 0 + 647 26 36 1 + 648 24 36 1 + 649 24 36 0 + 650 24 37 1 + 651 24 37 0 + 652 25 36 1 + 653 25 36 0 + 654 25 37 1 + 655 25 37 0 + 672 19 36 0 + 673 19 36 1 + 674 19 37 0 + 675 19 37 1 + 676 18 36 0 + 677 18 36 1 + 678 18 37 0 + 679 18 37 1 + 680 16 37 1 + 681 16 37 0 + 682 16 36 1 + 683 16 36 0 + 684 17 37 1 + 685 17 37 0 + 686 17 36 1 + 687 17 36 0 + 688 20 36 0 + 689 20 36 1 + 690 20 37 0 + 691 20 37 1 + 692 21 36 0 + 693 21 36 1 + 694 21 37 0 + 695 21 37 1 + 696 23 37 1 + 697 23 37 0 + 698 23 36 1 + 699 23 36 0 + 700 22 37 1 + 701 22 37 0 + 702 22 36 1 + 703 22 36 0 + 704 28 37 0 + 705 28 37 1 + 706 28 36 0 + 707 28 36 1 + 708 29 37 0 + 709 29 37 1 + 710 29 36 0 + 711 29 36 1 + 712 31 36 1 + 713 31 36 0 + 714 31 37 1 + 715 31 37 0 + 716 30 36 1 + 717 30 36 0 + 718 30 37 1 + 719 30 37 0 + 768 27 39 0 + 769 27 39 1 + 770 27 38 0 + 771 27 38 1 + 772 26 39 0 + 773 26 39 1 + 774 26 38 0 + 775 26 38 1 + 776 24 38 1 + 777 24 38 0 + 778 24 39 1 + 779 24 39 0 + 780 25 38 1 + 781 25 38 0 + 782 25 39 1 + 783 25 39 0 + 800 19 38 0 + 801 19 38 1 + 802 19 39 0 + 803 19 39 1 + 804 18 38 0 + 805 18 38 1 + 806 18 39 0 + 807 18 39 1 + 808 16 39 1 + 809 16 39 0 + 810 16 38 1 + 811 16 38 0 + 812 17 39 1 + 813 17 39 0 + 814 17 38 1 + 815 17 38 0 + 816 20 38 0 + 817 20 38 1 + 818 20 39 0 + 819 20 39 1 + 820 21 38 0 + 821 21 38 1 + 822 21 39 0 + 823 21 39 1 + 824 23 39 1 + 825 23 39 0 + 826 23 38 1 + 827 23 38 0 + 828 22 39 1 + 829 22 39 0 + 830 22 38 1 + 831 22 38 0 + 832 28 39 0 + 833 28 39 1 + 834 28 38 0 + 835 28 38 1 + 836 29 39 0 + 837 29 39 1 + 838 29 38 0 + 839 29 38 1 + 840 31 38 1 + 841 31 38 0 + 842 31 39 1 + 843 31 39 0 + 844 30 38 1 + 845 30 38 0 + 846 30 39 1 + 847 30 39 0 + 896 27 41 0 + 897 27 41 1 + 898 27 40 0 + 899 27 40 1 + 900 26 41 0 + 901 26 41 1 + 902 26 40 0 + 903 26 40 1 + 904 24 40 1 + 905 24 40 0 + 906 24 41 1 + 907 24 41 0 + 908 25 40 1 + 909 25 40 0 + 910 25 41 1 + 911 25 41 0 + 928 19 40 0 + 929 19 40 1 + 930 19 41 0 + 931 19 41 1 + 932 18 40 0 + 933 18 40 1 + 934 18 41 0 + 935 18 41 1 + 936 16 41 1 + 937 16 41 0 + 938 16 40 1 + 939 16 40 0 + 940 17 41 1 + 941 17 41 0 + 942 17 40 1 + 943 17 40 0 + 944 20 40 0 + 945 20 40 1 + 946 20 41 0 + 947 20 41 1 + 948 21 40 0 + 949 21 40 1 + 950 21 41 0 + 951 21 41 1 + 952 23 41 1 + 953 23 41 0 + 954 23 40 1 + 955 23 40 0 + 956 22 41 1 + 957 22 41 0 + 958 22 40 1 + 959 22 40 0 + 960 28 41 0 + 961 28 41 1 + 962 28 40 0 + 963 28 40 1 + 964 29 41 0 + 965 29 41 1 + 966 29 40 0 + 967 29 40 1 + 968 31 40 1 + 969 31 40 0 + 970 31 41 1 + 971 31 41 0 + 972 30 40 1 + 973 30 40 0 + 974 30 41 1 + 975 30 41 0 +1024 27 43 0 +1025 27 43 1 +1026 27 42 0 +1027 27 42 1 +1028 26 43 0 +1029 26 43 1 +1030 26 42 0 +1031 26 42 1 +1032 24 42 1 +1033 24 42 0 +1034 24 43 1 +1035 24 43 0 +1036 25 42 1 +1037 25 42 0 +1038 25 43 1 +1039 25 43 0 +1056 19 42 0 +1057 19 42 1 +1058 19 43 0 +1059 19 43 1 +1060 18 42 0 +1061 18 42 1 +1062 18 43 0 +1063 18 43 1 +1064 16 43 1 +1065 16 43 0 +1066 16 42 1 +1067 16 42 0 +1068 17 43 1 +1069 17 43 0 +1070 17 42 1 +1071 17 42 0 +1072 20 42 0 +1073 20 42 1 +1074 20 43 0 +1075 20 43 1 +1076 21 42 0 +1077 21 42 1 +1078 21 43 0 +1079 21 43 1 +1080 23 43 1 +1081 23 43 0 +1082 23 42 1 +1083 23 42 0 +1084 22 43 1 +1085 22 43 0 +1086 22 42 1 +1087 22 42 0 +1088 28 43 0 +1089 28 43 1 +1090 28 42 0 +1091 28 42 1 +1092 29 43 0 +1093 29 43 1 +1094 29 42 0 +1095 29 42 1 +1096 31 42 1 +1097 31 42 0 +1098 31 43 1 +1099 31 43 0 +1100 30 42 1 +1101 30 42 0 +1102 30 43 1 +1103 30 43 0 +1152 27 45 0 +1153 27 45 1 +1154 27 44 0 +1155 27 44 1 +1156 26 45 0 +1157 26 45 1 +1158 26 44 0 +1159 26 44 1 +1160 24 44 1 +1161 24 44 0 +1162 24 45 1 +1163 24 45 0 +1164 25 44 1 +1165 25 44 0 +1166 25 45 1 +1167 25 45 0 +1184 19 44 0 +1185 19 44 1 +1186 19 45 0 +1187 19 45 1 +1188 18 44 0 +1189 18 44 1 +1190 18 45 0 +1191 18 45 1 +1192 16 45 1 +1193 16 45 0 +1194 16 44 1 +1195 16 44 0 +1196 17 45 1 +1197 17 45 0 +1198 17 44 1 +1199 17 44 0 +1200 20 44 0 +1201 20 44 1 +1202 20 45 0 +1203 20 45 1 +1204 21 44 0 +1205 21 44 1 +1206 21 45 0 +1207 21 45 1 +1208 23 45 1 +1209 23 45 0 +1210 23 44 1 +1211 23 44 0 +1212 22 45 1 +1213 22 45 0 +1214 22 44 1 +1215 22 44 0 +1216 28 45 0 +1217 28 45 1 +1218 28 44 0 +1219 28 44 1 +1220 29 45 0 +1221 29 45 1 +1222 29 44 0 +1223 29 44 1 +1224 31 44 1 +1225 31 44 0 +1226 31 45 1 +1227 31 45 0 +1228 30 44 1 +1229 30 44 0 +1230 30 45 1 +1231 30 45 0 +1280 27 47 0 +1281 27 47 1 +1282 27 46 0 +1283 27 46 1 +1284 26 47 0 +1285 26 47 1 +1286 26 46 0 +1287 26 46 1 +1288 24 46 1 +1289 24 46 0 +1290 24 47 1 +1291 24 47 0 +1292 25 46 1 +1293 25 46 0 +1294 25 47 1 +1295 25 47 0 +1312 19 46 0 +1313 19 46 1 +1314 19 47 0 +1315 19 47 1 +1316 18 46 0 +1317 18 46 1 +1318 18 47 0 +1319 18 47 1 +1320 16 47 1 +1321 16 47 0 +1322 16 46 1 +1323 16 46 0 +1324 17 47 1 +1325 17 47 0 +1326 17 46 1 +1327 17 46 0 +1328 20 46 0 +1329 20 46 1 +1330 20 47 0 +1331 20 47 1 +1332 21 46 0 +1333 21 46 1 +1334 21 47 0 +1335 21 47 1 +1336 23 47 1 +1337 23 47 0 +1338 23 46 1 +1339 23 46 0 +1340 22 47 1 +1341 22 47 0 +1342 22 46 1 +1343 22 46 0 +1344 28 47 0 +1345 28 47 1 +1346 28 46 0 +1347 28 46 1 +1348 29 47 0 +1349 29 47 1 +1350 29 46 0 +1351 29 46 1 +1352 31 46 1 +1353 31 46 0 +1354 31 47 1 +1355 31 47 0 +1356 30 46 1 +1357 30 46 0 +1358 30 47 1 +1359 30 47 0 +1408 27 49 0 +1409 27 49 1 +1410 27 48 0 +1411 27 48 1 +1412 26 49 0 +1413 26 49 1 +1414 26 48 0 +1415 26 48 1 +1416 24 48 1 +1417 24 48 0 +1418 24 49 1 +1419 24 49 0 +1420 25 48 1 +1421 25 48 0 +1422 25 49 1 +1423 25 49 0 +1440 19 48 0 +1441 19 48 1 +1442 19 49 0 +1443 19 49 1 +1444 18 48 0 +1445 18 48 1 +1446 18 49 0 +1447 18 49 1 +1448 16 49 1 +1449 16 49 0 +1450 16 48 1 +1451 16 48 0 +1452 17 49 1 +1453 17 49 0 +1454 17 48 1 +1455 17 48 0 +1456 20 48 0 +1457 20 48 1 +1458 20 49 0 +1459 20 49 1 +1460 21 48 0 +1461 21 48 1 +1462 21 49 0 +1463 21 49 1 +1464 23 49 1 +1465 23 49 0 +1466 23 48 1 +1467 23 48 0 +1468 22 49 1 +1469 22 49 0 +1470 22 48 1 +1471 22 48 0 +1472 28 49 0 +1473 28 49 1 +1474 28 48 0 +1475 28 48 1 +1476 29 49 0 +1477 29 49 1 +1478 29 48 0 +1479 29 48 1 +1480 31 48 1 +1481 31 48 0 +1482 31 49 1 +1483 31 49 0 +1484 30 48 1 +1485 30 48 0 +1486 30 49 1 +1487 30 49 0 +1536 27 51 0 +1537 27 51 1 +1538 27 50 0 +1539 27 50 1 +1540 26 51 0 +1541 26 51 1 +1542 26 50 0 +1543 26 50 1 +1544 24 50 1 +1545 24 50 0 +1546 24 51 1 +1547 24 51 0 +1548 25 50 1 +1549 25 50 0 +1550 25 51 1 +1551 25 51 0 +1568 19 50 0 +1569 19 50 1 +1570 19 51 0 +1571 19 51 1 +1572 18 50 0 +1573 18 50 1 +1574 18 51 0 +1575 18 51 1 +1576 16 51 1 +1577 16 51 0 +1578 16 50 1 +1579 16 50 0 +1580 17 51 1 +1581 17 51 0 +1582 17 50 1 +1583 17 50 0 +1584 20 50 0 +1585 20 50 1 +1586 20 51 0 +1587 20 51 1 +1588 21 50 0 +1589 21 50 1 +1590 21 51 0 +1591 21 51 1 +1592 23 51 1 +1593 23 51 0 +1594 23 50 1 +1595 23 50 0 +1596 22 51 1 +1597 22 51 0 +1598 22 50 1 +1599 22 50 0 +1600 28 51 0 +1601 28 51 1 +1602 28 50 0 +1603 28 50 1 +1604 29 51 0 +1605 29 51 1 +1606 29 50 0 +1607 29 50 1 +1608 31 50 1 +1609 31 50 0 +1610 31 51 1 +1611 31 51 0 +1612 30 50 1 +1613 30 50 0 +1614 30 51 1 +1615 30 51 0 +1664 27 53 0 +1665 27 53 1 +1666 27 52 0 +1667 27 52 1 +1668 26 53 0 +1669 26 53 1 +1670 26 52 0 +1671 26 52 1 +1672 24 52 1 +1673 24 52 0 +1674 24 53 1 +1675 24 53 0 +1676 25 52 1 +1677 25 52 0 +1678 25 53 1 +1679 25 53 0 +1696 19 52 0 +1697 19 52 1 +1698 19 53 0 +1699 19 53 1 +1700 18 52 0 +1701 18 52 1 +1702 18 53 0 +1703 18 53 1 +1704 16 53 1 +1705 16 53 0 +1706 16 52 1 +1707 16 52 0 +1708 17 53 1 +1709 17 53 0 +1710 17 52 1 +1711 17 52 0 +1712 20 52 0 +1713 20 52 1 +1714 20 53 0 +1715 20 53 1 +1716 21 52 0 +1717 21 52 1 +1718 21 53 0 +1719 21 53 1 +1720 23 53 1 +1721 23 53 0 +1722 23 52 1 +1723 23 52 0 +1724 22 53 1 +1725 22 53 0 +1726 22 52 1 +1727 22 52 0 +1728 28 53 0 +1729 28 53 1 +1730 28 52 0 +1731 28 52 1 +1732 29 53 0 +1733 29 53 1 +1734 29 52 0 +1735 29 52 1 +1736 31 52 1 +1737 31 52 0 +1738 31 53 1 +1739 31 53 0 +1740 30 52 1 +1741 30 52 0 +1742 30 53 1 +1743 30 53 0 +1792 27 55 0 +1793 27 55 1 +1794 27 54 0 +1795 27 54 1 +1796 26 55 0 +1797 26 55 1 +1798 26 54 0 +1799 26 54 1 +1800 24 54 1 +1801 24 54 0 +1802 24 55 1 +1803 24 55 0 +1804 25 54 1 +1805 25 54 0 +1806 25 55 1 +1807 25 55 0 +1824 19 54 0 +1825 19 54 1 +1826 19 55 0 +1827 19 55 1 +1828 18 54 0 +1829 18 54 1 +1830 18 55 0 +1831 18 55 1 +1832 16 55 1 +1833 16 55 0 +1834 16 54 1 +1835 16 54 0 +1836 17 55 1 +1837 17 55 0 +1838 17 54 1 +1839 17 54 0 +1840 20 54 0 +1841 20 54 1 +1842 20 55 0 +1843 20 55 1 +1844 21 54 0 +1845 21 54 1 +1846 21 55 0 +1847 21 55 1 +1848 23 55 1 +1849 23 55 0 +1850 23 54 1 +1851 23 54 0 +1852 22 55 1 +1853 22 55 0 +1854 22 54 1 +1855 22 54 0 +1856 28 55 0 +1857 28 55 1 +1858 28 54 0 +1859 28 54 1 +1860 29 55 0 +1861 29 55 1 +1862 29 54 0 +1863 29 54 1 +1864 31 54 1 +1865 31 54 0 +1866 31 55 1 +1867 31 55 0 +1868 30 54 1 +1869 30 54 0 +1870 30 55 1 +1871 30 55 0 +2176 27 27 0 +2177 27 27 1 +2178 27 26 0 +2179 27 26 1 +2180 26 27 0 +2181 26 27 1 +2182 26 26 0 +2183 26 26 1 +2184 24 26 1 +2185 24 26 0 +2186 24 27 1 +2187 24 27 0 +2188 25 26 1 +2189 25 26 0 +2190 25 27 1 +2191 25 27 0 +2208 19 26 0 +2209 19 26 1 +2210 19 27 0 +2211 19 27 1 +2212 18 26 0 +2213 18 26 1 +2214 18 27 0 +2215 18 27 1 +2216 16 27 1 +2217 16 27 0 +2218 16 26 1 +2219 16 26 0 +2220 17 27 1 +2221 17 27 0 +2222 17 26 1 +2223 17 26 0 +2224 20 26 0 +2225 20 26 1 +2226 20 27 0 +2227 20 27 1 +2228 21 26 0 +2229 21 26 1 +2230 21 27 0 +2231 21 27 1 +2232 23 27 1 +2233 23 27 0 +2234 23 26 1 +2235 23 26 0 +2236 22 27 1 +2237 22 27 0 +2238 22 26 1 +2239 22 26 0 +2240 28 27 0 +2241 28 27 1 +2242 28 26 0 +2243 28 26 1 +2244 29 27 0 +2245 29 27 1 +2246 29 26 0 +2247 29 26 1 +2248 31 26 1 +2249 31 26 0 +2250 31 27 1 +2251 31 27 0 +2252 30 26 1 +2253 30 26 0 +2254 30 27 1 +2255 30 27 0 +2304 27 25 0 +2305 27 25 1 +2306 27 24 0 +2307 27 24 1 +2308 26 25 0 +2309 26 25 1 +2310 26 24 0 +2311 26 24 1 +2312 24 24 1 +2313 24 24 0 +2314 24 25 1 +2315 24 25 0 +2316 25 24 1 +2317 25 24 0 +2318 25 25 1 +2319 25 25 0 +2336 19 24 0 +2337 19 24 1 +2338 19 25 0 +2339 19 25 1 +2340 18 24 0 +2341 18 24 1 +2342 18 25 0 +2343 18 25 1 +2344 16 25 1 +2345 16 25 0 +2346 16 24 1 +2347 16 24 0 +2348 17 25 1 +2349 17 25 0 +2350 17 24 1 +2351 17 24 0 +2352 20 24 0 +2353 20 24 1 +2354 20 25 0 +2355 20 25 1 +2356 21 24 0 +2357 21 24 1 +2358 21 25 0 +2359 21 25 1 +2360 23 25 1 +2361 23 25 0 +2362 23 24 1 +2363 23 24 0 +2364 22 25 1 +2365 22 25 0 +2366 22 24 1 +2367 22 24 0 +2368 28 25 0 +2369 28 25 1 +2370 28 24 0 +2371 28 24 1 +2372 29 25 0 +2373 29 25 1 +2374 29 24 0 +2375 29 24 1 +2376 31 24 1 +2377 31 24 0 +2378 31 25 1 +2379 31 25 0 +2380 30 24 1 +2381 30 24 0 +2382 30 25 1 +2383 30 25 0 +2432 27 23 0 +2433 27 23 1 +2434 27 22 0 +2435 27 22 1 +2436 26 23 0 +2437 26 23 1 +2438 26 22 0 +2439 26 22 1 +2440 24 22 1 +2441 24 22 0 +2442 24 23 1 +2443 24 23 0 +2444 25 22 1 +2445 25 22 0 +2446 25 23 1 +2447 25 23 0 +2464 19 22 0 +2465 19 22 1 +2466 19 23 0 +2467 19 23 1 +2468 18 22 0 +2469 18 22 1 +2470 18 23 0 +2471 18 23 1 +2472 16 23 1 +2473 16 23 0 +2474 16 22 1 +2475 16 22 0 +2476 17 23 1 +2477 17 23 0 +2478 17 22 1 +2479 17 22 0 +2480 20 22 0 +2481 20 22 1 +2482 20 23 0 +2483 20 23 1 +2484 21 22 0 +2485 21 22 1 +2486 21 23 0 +2487 21 23 1 +2488 23 23 1 +2489 23 23 0 +2490 23 22 1 +2491 23 22 0 +2492 22 23 1 +2493 22 23 0 +2494 22 22 1 +2495 22 22 0 +2496 28 23 0 +2497 28 23 1 +2498 28 22 0 +2499 28 22 1 +2500 29 23 0 +2501 29 23 1 +2502 29 22 0 +2503 29 22 1 +2504 31 22 1 +2505 31 22 0 +2506 31 23 1 +2507 31 23 0 +2508 30 22 1 +2509 30 22 0 +2510 30 23 1 +2511 30 23 0 +2560 27 21 0 +2561 27 21 1 +2562 27 20 0 +2563 27 20 1 +2564 26 21 0 +2565 26 21 1 +2566 26 20 0 +2567 26 20 1 +2568 24 20 1 +2569 24 20 0 +2570 24 21 1 +2571 24 21 0 +2572 25 20 1 +2573 25 20 0 +2574 25 21 1 +2575 25 21 0 +2592 19 20 0 +2593 19 20 1 +2594 19 21 0 +2595 19 21 1 +2596 18 20 0 +2597 18 20 1 +2598 18 21 0 +2599 18 21 1 +2600 16 21 1 +2601 16 21 0 +2602 16 20 1 +2603 16 20 0 +2604 17 21 1 +2605 17 21 0 +2606 17 20 1 +2607 17 20 0 +2608 20 20 0 +2609 20 20 1 +2610 20 21 0 +2611 20 21 1 +2612 21 20 0 +2613 21 20 1 +2614 21 21 0 +2615 21 21 1 +2616 23 21 1 +2617 23 21 0 +2618 23 20 1 +2619 23 20 0 +2620 22 21 1 +2621 22 21 0 +2622 22 20 1 +2623 22 20 0 +2624 28 21 0 +2625 28 21 1 +2626 28 20 0 +2627 28 20 1 +2628 29 21 0 +2629 29 21 1 +2630 29 20 0 +2631 29 20 1 +2632 31 20 1 +2633 31 20 0 +2634 31 21 1 +2635 31 21 0 +2636 30 20 1 +2637 30 20 0 +2638 30 21 1 +2639 30 21 0 +2688 27 19 0 +2689 27 19 1 +2690 27 18 0 +2691 27 18 1 +2692 26 19 0 +2693 26 19 1 +2694 26 18 0 +2695 26 18 1 +2696 24 18 1 +2697 24 18 0 +2698 24 19 1 +2699 24 19 0 +2700 25 18 1 +2701 25 18 0 +2702 25 19 1 +2703 25 19 0 +2720 19 18 0 +2721 19 18 1 +2722 19 19 0 +2723 19 19 1 +2724 18 18 0 +2725 18 18 1 +2726 18 19 0 +2727 18 19 1 +2728 16 19 1 +2729 16 19 0 +2730 16 18 1 +2731 16 18 0 +2732 17 19 1 +2733 17 19 0 +2734 17 18 1 +2735 17 18 0 +2736 20 18 0 +2737 20 18 1 +2738 20 19 0 +2739 20 19 1 +2740 21 18 0 +2741 21 18 1 +2742 21 19 0 +2743 21 19 1 +2744 23 19 1 +2745 23 19 0 +2746 23 18 1 +2747 23 18 0 +2748 22 19 1 +2749 22 19 0 +2750 22 18 1 +2751 22 18 0 +2752 28 19 0 +2753 28 19 1 +2754 28 18 0 +2755 28 18 1 +2756 29 19 0 +2757 29 19 1 +2758 29 18 0 +2759 29 18 1 +2760 31 18 1 +2761 31 18 0 +2762 31 19 1 +2763 31 19 0 +2764 30 18 1 +2765 30 18 0 +2766 30 19 1 +2767 30 19 0 +2816 27 17 0 +2817 27 17 1 +2818 27 16 0 +2819 27 16 1 +2820 26 17 0 +2821 26 17 1 +2822 26 16 0 +2823 26 16 1 +2824 24 16 1 +2825 24 16 0 +2826 24 17 1 +2827 24 17 0 +2828 25 16 1 +2829 25 16 0 +2830 25 17 1 +2831 25 17 0 +2848 19 16 0 +2849 19 16 1 +2850 19 17 0 +2851 19 17 1 +2852 18 16 0 +2853 18 16 1 +2854 18 17 0 +2855 18 17 1 +2856 16 17 1 +2857 16 17 0 +2858 16 16 1 +2859 16 16 0 +2860 17 17 1 +2861 17 17 0 +2862 17 16 1 +2863 17 16 0 +2864 20 16 0 +2865 20 16 1 +2866 20 17 0 +2867 20 17 1 +2868 21 16 0 +2869 21 16 1 +2870 21 17 0 +2871 21 17 1 +2872 23 17 1 +2873 23 17 0 +2874 23 16 1 +2875 23 16 0 +2876 22 17 1 +2877 22 17 0 +2878 22 16 1 +2879 22 16 0 +2880 28 17 0 +2881 28 17 1 +2882 28 16 0 +2883 28 16 1 +2884 29 17 0 +2885 29 17 1 +2886 29 16 0 +2887 29 16 1 +2888 31 16 1 +2889 31 16 0 +2890 31 17 1 +2891 31 17 0 +2892 30 16 1 +2893 30 16 0 +2894 30 17 1 +2895 30 17 0 +2944 27 15 0 +2945 27 15 1 +2946 27 14 0 +2947 27 14 1 +2948 26 15 0 +2949 26 15 1 +2950 26 14 0 +2951 26 14 1 +2952 24 14 1 +2953 24 14 0 +2954 24 15 1 +2955 24 15 0 +2956 25 14 1 +2957 25 14 0 +2958 25 15 1 +2959 25 15 0 +2976 19 14 0 +2977 19 14 1 +2978 19 15 0 +2979 19 15 1 +2980 18 14 0 +2981 18 14 1 +2982 18 15 0 +2983 18 15 1 +2984 16 15 1 +2985 16 15 0 +2986 16 14 1 +2987 16 14 0 +2988 17 15 1 +2989 17 15 0 +2990 17 14 1 +2991 17 14 0 +2992 20 14 0 +2993 20 14 1 +2994 20 15 0 +2995 20 15 1 +2996 21 14 0 +2997 21 14 1 +2998 21 15 0 +2999 21 15 1 +3000 23 15 1 +3001 23 15 0 +3002 23 14 1 +3003 23 14 0 +3004 22 15 1 +3005 22 15 0 +3006 22 14 1 +3007 22 14 0 +3008 28 15 0 +3009 28 15 1 +3010 28 14 0 +3011 28 14 1 +3012 29 15 0 +3013 29 15 1 +3014 29 14 0 +3015 29 14 1 +3016 31 14 1 +3017 31 14 0 +3018 31 15 1 +3019 31 15 0 +3020 30 14 1 +3021 30 14 0 +3022 30 15 1 +3023 30 15 0 +3072 27 13 0 +3073 27 13 1 +3074 27 12 0 +3075 27 12 1 +3076 26 13 0 +3077 26 13 1 +3078 26 12 0 +3079 26 12 1 +3080 24 12 1 +3081 24 12 0 +3082 24 13 1 +3083 24 13 0 +3084 25 12 1 +3085 25 12 0 +3086 25 13 1 +3087 25 13 0 +3104 19 12 0 +3105 19 12 1 +3106 19 13 0 +3107 19 13 1 +3108 18 12 0 +3109 18 12 1 +3110 18 13 0 +3111 18 13 1 +3112 16 13 1 +3113 16 13 0 +3114 16 12 1 +3115 16 12 0 +3116 17 13 1 +3117 17 13 0 +3118 17 12 1 +3119 17 12 0 +3120 20 12 0 +3121 20 12 1 +3122 20 13 0 +3123 20 13 1 +3124 21 12 0 +3125 21 12 1 +3126 21 13 0 +3127 21 13 1 +3128 23 13 1 +3129 23 13 0 +3130 23 12 1 +3131 23 12 0 +3132 22 13 1 +3133 22 13 0 +3134 22 12 1 +3135 22 12 0 +3136 28 13 0 +3137 28 13 1 +3138 28 12 0 +3139 28 12 1 +3140 29 13 0 +3141 29 13 1 +3142 29 12 0 +3143 29 12 1 +3144 31 12 1 +3145 31 12 0 +3146 31 13 1 +3147 31 13 0 +3148 30 12 1 +3149 30 12 0 +3150 30 13 1 +3151 30 13 0 +3200 27 11 0 +3201 27 11 1 +3202 27 10 0 +3203 27 10 1 +3204 26 11 0 +3205 26 11 1 +3206 26 10 0 +3207 26 10 1 +3208 24 10 1 +3209 24 10 0 +3210 24 11 1 +3211 24 11 0 +3212 25 10 1 +3213 25 10 0 +3214 25 11 1 +3215 25 11 0 +3232 19 10 0 +3233 19 10 1 +3234 19 11 0 +3235 19 11 1 +3236 18 10 0 +3237 18 10 1 +3238 18 11 0 +3239 18 11 1 +3240 16 11 1 +3241 16 11 0 +3242 16 10 1 +3243 16 10 0 +3244 17 11 1 +3245 17 11 0 +3246 17 10 1 +3247 17 10 0 +3248 20 10 0 +3249 20 10 1 +3250 20 11 0 +3251 20 11 1 +3252 21 10 0 +3253 21 10 1 +3254 21 11 0 +3255 21 11 1 +3256 23 11 1 +3257 23 11 0 +3258 23 10 1 +3259 23 10 0 +3260 22 11 1 +3261 22 11 0 +3262 22 10 1 +3263 22 10 0 +3264 28 11 0 +3265 28 11 1 +3266 28 10 0 +3267 28 10 1 +3268 29 11 0 +3269 29 11 1 +3270 29 10 0 +3271 29 10 1 +3272 31 10 1 +3273 31 10 0 +3274 31 11 1 +3275 31 11 0 +3276 30 10 1 +3277 30 10 0 +3278 30 11 1 +3279 30 11 0 +3328 27 9 0 +3329 27 9 1 +3330 27 8 0 +3331 27 8 1 +3332 26 9 0 +3333 26 9 1 +3334 26 8 0 +3335 26 8 1 +3336 24 8 1 +3337 24 8 0 +3338 24 9 1 +3339 24 9 0 +3340 25 8 1 +3341 25 8 0 +3342 25 9 1 +3343 25 9 0 +3360 19 8 0 +3361 19 8 1 +3362 19 9 0 +3363 19 9 1 +3364 18 8 0 +3365 18 8 1 +3366 18 9 0 +3367 18 9 1 +3368 16 9 1 +3369 16 9 0 +3370 16 8 1 +3371 16 8 0 +3372 17 9 1 +3373 17 9 0 +3374 17 8 1 +3375 17 8 0 +3376 20 8 0 +3377 20 8 1 +3378 20 9 0 +3379 20 9 1 +3380 21 8 0 +3381 21 8 1 +3382 21 9 0 +3383 21 9 1 +3384 23 9 1 +3385 23 9 0 +3386 23 8 1 +3387 23 8 0 +3388 22 9 1 +3389 22 9 0 +3390 22 8 1 +3391 22 8 0 +3392 28 9 0 +3393 28 9 1 +3394 28 8 0 +3395 28 8 1 +3396 29 9 0 +3397 29 9 1 +3398 29 8 0 +3399 29 8 1 +3400 31 8 1 +3401 31 8 0 +3402 31 9 1 +3403 31 9 0 +3404 30 8 1 +3405 30 8 0 +3406 30 9 1 +3407 30 9 0 +3456 27 7 0 +3457 27 7 1 +3458 27 6 0 +3459 27 6 1 +3460 26 7 0 +3461 26 7 1 +3462 26 6 0 +3463 26 6 1 +3464 24 6 1 +3465 24 6 0 +3466 24 7 1 +3467 24 7 0 +3468 25 6 1 +3469 25 6 0 +3470 25 7 1 +3471 25 7 0 +3488 19 6 0 +3489 19 6 1 +3490 19 7 0 +3491 19 7 1 +3492 18 6 0 +3493 18 6 1 +3494 18 7 0 +3495 18 7 1 +3496 16 7 1 +3497 16 7 0 +3498 16 6 1 +3499 16 6 0 +3500 17 7 1 +3501 17 7 0 +3502 17 6 1 +3503 17 6 0 +3504 20 6 0 +3505 20 6 1 +3506 20 7 0 +3507 20 7 1 +3508 21 6 0 +3509 21 6 1 +3510 21 7 0 +3511 21 7 1 +3512 23 7 1 +3513 23 7 0 +3514 23 6 1 +3515 23 6 0 +3516 22 7 1 +3517 22 7 0 +3518 22 6 1 +3519 22 6 0 +3520 28 7 0 +3521 28 7 1 +3522 28 6 0 +3523 28 6 1 +3524 29 7 0 +3525 29 7 1 +3526 29 6 0 +3527 29 6 1 +3528 31 6 1 +3529 31 6 0 +3530 31 7 1 +3531 31 7 0 +3532 30 6 1 +3533 30 6 0 +3534 30 7 1 +3535 30 7 0 +3584 27 5 0 +3585 27 5 1 +3586 27 4 0 +3587 27 4 1 +3588 26 5 0 +3589 26 5 1 +3590 26 4 0 +3591 26 4 1 +3592 24 4 1 +3593 24 4 0 +3594 24 5 1 +3595 24 5 0 +3596 25 4 1 +3597 25 4 0 +3598 25 5 1 +3599 25 5 0 +3616 19 4 0 +3617 19 4 1 +3618 19 5 0 +3619 19 5 1 +3620 18 4 0 +3621 18 4 1 +3622 18 5 0 +3623 18 5 1 +3624 16 5 1 +3625 16 5 0 +3626 16 4 1 +3627 16 4 0 +3628 17 5 1 +3629 17 5 0 +3630 17 4 1 +3631 17 4 0 +3632 20 4 0 +3633 20 4 1 +3634 20 5 0 +3635 20 5 1 +3636 21 4 0 +3637 21 4 1 +3638 21 5 0 +3639 21 5 1 +3640 23 5 1 +3641 23 5 0 +3642 23 4 1 +3643 23 4 0 +3644 22 5 1 +3645 22 5 0 +3646 22 4 1 +3647 22 4 0 +3648 28 5 0 +3649 28 5 1 +3650 28 4 0 +3651 28 4 1 +3652 29 5 0 +3653 29 5 1 +3654 29 4 0 +3655 29 4 1 +3656 31 4 1 +3657 31 4 0 +3658 31 5 1 +3659 31 5 0 +3660 30 4 1 +3661 30 4 0 +3662 30 5 1 +3663 30 5 0 +3712 27 3 0 +3713 27 3 1 +3714 27 2 0 +3715 27 2 1 +3716 26 3 0 +3717 26 3 1 +3718 26 2 0 +3719 26 2 1 +3720 24 2 1 +3721 24 2 0 +3722 24 3 1 +3723 24 3 0 +3724 25 2 1 +3725 25 2 0 +3726 25 3 1 +3727 25 3 0 +3744 19 2 0 +3745 19 2 1 +3746 19 3 0 +3747 19 3 1 +3748 18 2 0 +3749 18 2 1 +3750 18 3 0 +3751 18 3 1 +3752 16 3 1 +3753 16 3 0 +3754 16 2 1 +3755 16 2 0 +3756 17 3 1 +3757 17 3 0 +3758 17 2 1 +3759 17 2 0 +3760 20 2 0 +3761 20 2 1 +3762 20 3 0 +3763 20 3 1 +3764 21 2 0 +3765 21 2 1 +3766 21 3 0 +3767 21 3 1 +3768 23 3 1 +3769 23 3 0 +3770 23 2 1 +3771 23 2 0 +3772 22 3 1 +3773 22 3 0 +3774 22 2 1 +3775 22 2 0 +3776 28 3 0 +3777 28 3 1 +3778 28 2 0 +3779 28 2 1 +3780 29 3 0 +3781 29 3 1 +3782 29 2 0 +3783 29 2 1 +3784 31 2 1 +3785 31 2 0 +3786 31 3 1 +3787 31 3 0 +3788 30 2 1 +3789 30 2 0 +3790 30 3 1 +3791 30 3 0 +3840 27 1 0 +3841 27 1 1 +3842 27 0 0 +3843 27 0 1 +3844 26 1 0 +3845 26 1 1 +3846 26 0 0 +3847 26 0 1 +3848 24 0 1 +3849 24 0 0 +3850 24 1 1 +3851 24 1 0 +3852 25 0 1 +3853 25 0 0 +3854 25 1 1 +3855 25 1 0 +3872 19 0 0 +3873 19 0 1 +3874 19 1 0 +3875 19 1 1 +3876 18 0 0 +3877 18 0 1 +3878 18 1 0 +3879 18 1 1 +3880 16 1 1 +3881 16 1 0 +3882 16 0 1 +3883 16 0 0 +3884 17 1 1 +3885 17 1 0 +3886 17 0 1 +3887 17 0 0 +3888 20 0 0 +3889 20 0 1 +3890 20 1 0 +3891 20 1 1 +3892 21 0 0 +3893 21 0 1 +3894 21 1 0 +3895 21 1 1 +3896 23 1 1 +3897 23 1 0 +3898 23 0 1 +3899 23 0 0 +3900 22 1 1 +3901 22 1 0 +3902 22 0 1 +3903 22 0 0 +3904 28 1 0 +3905 28 1 1 +3906 28 0 0 +3907 28 0 1 +3908 29 1 0 +3909 29 1 1 +3910 29 0 0 +3911 29 0 1 +3912 31 0 1 +3913 31 0 0 +3914 31 1 1 +3915 31 1 0 +3916 30 0 1 +3917 30 0 0 +3918 30 1 1 +3919 30 1 0 diff --git a/Detectors/PHOS/base/files/Mod0RCU2.data b/Detectors/PHOS/base/files/Mod0RCU2.data new file mode 100644 index 0000000000000..21de94d7ace53 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod0RCU2.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 2 0 2 + 1 2 1 2 + 2 2 2 2 + 3 2 3 2 + 4 2 4 2 + 5 2 5 2 + 6 2 6 2 + 7 2 7 2 + 8 2 8 2 + 9 2 9 2 + 10 2 10 2 + 11 2 11 2 + 12 2 12 2 + 13 2 13 2 + 14 2 14 2 + 15 2 15 2 + 16 2 16 2 + 17 2 17 2 + 18 2 18 2 + 19 2 19 2 + 20 2 20 2 + 21 2 21 2 + 22 2 22 2 + 23 2 23 2 + 24 2 24 2 + 25 2 25 2 + 26 2 26 2 + 27 2 27 2 + 28 2 28 2 + 29 2 29 2 + 30 2 30 2 + 31 2 31 2 + 32 2 32 2 + 33 2 33 2 + 34 2 34 2 + 35 2 35 2 + 36 2 36 2 + 37 2 37 2 + 38 2 38 2 + 39 2 39 2 + 40 2 40 2 + 41 2 41 2 + 42 2 42 2 + 43 2 43 2 + 44 2 44 2 + 45 2 45 2 + 46 2 46 2 + 47 2 47 2 + 48 2 48 2 + 49 2 49 2 + 50 2 50 2 + 51 2 51 2 + 52 2 52 2 + 53 2 53 2 + 54 2 54 2 + 55 2 55 2 + 56 2 56 2 + 57 2 57 2 + 58 2 58 2 + 59 2 59 2 + 60 2 60 2 + 61 2 61 2 + 62 2 62 2 + 63 2 63 2 + 64 2 64 2 + 65 2 65 2 + 66 2 66 2 + 67 2 67 2 + 68 2 68 2 + 69 2 69 2 + 70 2 70 2 + 71 2 71 2 + 72 2 72 2 + 73 2 73 2 + 74 2 74 2 + 75 2 75 2 + 76 2 76 2 + 77 2 77 2 + 78 2 78 2 + 79 2 79 2 + 80 2 80 2 + 81 2 81 2 + 82 2 82 2 + 83 2 83 2 + 84 2 84 2 + 85 2 85 2 + 86 2 86 2 + 87 2 87 2 + 88 2 88 2 + 89 2 89 2 + 90 2 90 2 + 91 2 91 2 + 92 2 92 2 + 93 2 93 2 + 94 2 94 2 + 95 2 95 2 + 96 2 96 2 + 97 2 97 2 + 98 2 98 2 + 99 2 99 2 + 100 2 100 2 + 101 2 101 2 + 102 2 102 2 + 103 2 103 2 + 104 2 104 2 + 105 2 105 2 + 106 2 106 2 + 107 2 107 2 + 108 2 108 2 + 109 2 109 2 + 110 2 110 2 + 111 2 111 2 + 112 2 112 2 + 113 2 113 2 + 114 2 114 2 + 115 2 115 2 + 116 2 116 2 + 117 2 117 2 + 118 2 118 2 + 119 2 119 2 + 120 2 120 2 + 121 2 121 2 + 122 2 122 2 + 123 2 123 2 + 124 2 124 2 + 125 2 125 2 + 126 2 126 2 + 127 2 127 2 +2048 2 2048 2 +2049 2 2049 2 +2050 2 2050 2 +2051 2 2051 2 +2052 2 2052 2 +2053 2 2053 2 +2054 2 2054 2 +2055 2 2055 2 +2056 2 2056 2 +2057 2 2057 2 +2058 2 2058 2 +2059 2 2059 2 +2060 2 2060 2 +2061 2 2061 2 +2062 2 2062 2 +2063 2 2063 2 +2064 2 2064 2 +2065 2 2065 2 +2066 2 2066 2 +2067 2 2067 2 +2068 2 2068 2 +2069 2 2069 2 +2070 2 2070 2 +2071 2 2071 2 +2072 2 2072 2 +2073 2 2073 2 +2074 2 2074 2 +2075 2 2075 2 +2076 2 2076 2 +2077 2 2077 2 +2078 2 2078 2 +2079 2 2079 2 +2080 2 2080 2 +2081 2 2081 2 +2082 2 2082 2 +2083 2 2083 2 +2084 2 2084 2 +2085 2 2085 2 +2086 2 2086 2 +2087 2 2087 2 +2088 2 2088 2 +2089 2 2089 2 +2090 2 2090 2 +2091 2 2091 2 +2092 2 2092 2 +2093 2 2093 2 +2094 2 2094 2 +2095 2 2095 2 +2096 2 2096 2 +2097 2 2097 2 +2098 2 2098 2 +2099 2 2099 2 +2100 2 2100 2 +2101 2 2101 2 +2102 2 2102 2 +2103 2 2103 2 +2104 2 2104 2 +2105 2 2105 2 +2106 2 2106 2 +2107 2 2107 2 +2108 2 2108 2 +2109 2 2109 2 +2110 2 2110 2 +2111 2 2111 2 +2112 2 2112 2 +2113 2 2113 2 +2114 2 2114 2 +2115 2 2115 2 +2116 2 2116 2 +2117 2 2117 2 +2118 2 2118 2 +2119 2 2119 2 +2120 2 2120 2 +2121 2 2121 2 +2122 2 2122 2 +2123 2 2123 2 +2124 2 2124 2 +2125 2 2125 2 +2126 2 2126 2 +2127 2 2127 2 +2128 2 2128 2 +2129 2 2129 2 +2130 2 2130 2 +2131 2 2131 2 +2132 2 2132 2 +2133 2 2133 2 +2134 2 2134 2 +2135 2 2135 2 +2136 2 2136 2 +2137 2 2137 2 +2138 2 2138 2 +2139 2 2139 2 +2140 2 2140 2 +2141 2 2141 2 +2142 2 2142 2 +2143 2 2143 2 +2144 2 2144 2 +2145 2 2145 2 +2146 2 2146 2 +2147 2 2147 2 +2148 2 2148 2 +2149 2 2149 2 +2150 2 2150 2 +2151 2 2151 2 +2152 2 2152 2 +2153 2 2153 2 +2154 2 2154 2 +2155 2 2155 2 +2156 2 2156 2 +2157 2 2157 2 +2158 2 2158 2 +2159 2 2159 2 +2160 2 2160 2 +2161 2 2161 2 +2162 2 2162 2 +2163 2 2163 2 +2164 2 2164 2 +2165 2 2165 2 +2166 2 2166 2 +2167 2 2167 2 +2168 2 2168 2 +2169 2 2169 2 +2170 2 2170 2 +2171 2 2171 2 +2172 2 2172 2 +2173 2 2173 2 +2174 2 2174 2 +2175 2 2175 2 + 128 43 29 0 + 129 43 29 1 + 130 43 28 0 + 131 43 28 1 + 132 42 29 0 + 133 42 29 1 + 134 42 28 0 + 135 42 28 1 + 136 40 28 1 + 137 40 28 0 + 138 40 29 1 + 139 40 29 0 + 140 41 28 1 + 141 41 28 0 + 142 41 29 1 + 143 41 29 0 + 160 35 28 0 + 161 35 28 1 + 162 35 29 0 + 163 35 29 1 + 164 34 28 0 + 165 34 28 1 + 166 34 29 0 + 167 34 29 1 + 168 32 29 1 + 169 32 29 0 + 170 32 28 1 + 171 32 28 0 + 172 33 29 1 + 173 33 29 0 + 174 33 28 1 + 175 33 28 0 + 176 36 28 0 + 177 36 28 1 + 178 36 29 0 + 179 36 29 1 + 180 37 28 0 + 181 37 28 1 + 182 37 29 0 + 183 37 29 1 + 184 39 29 1 + 185 39 29 0 + 186 39 28 1 + 187 39 28 0 + 188 38 29 1 + 189 38 29 0 + 190 38 28 1 + 191 38 28 0 + 192 44 29 0 + 193 44 29 1 + 194 44 28 0 + 195 44 28 1 + 196 45 29 0 + 197 45 29 1 + 198 45 28 0 + 199 45 28 1 + 200 47 28 1 + 201 47 28 0 + 202 47 29 1 + 203 47 29 0 + 204 46 28 1 + 205 46 28 0 + 206 46 29 1 + 207 46 29 0 + 256 43 31 0 + 257 43 31 1 + 258 43 30 0 + 259 43 30 1 + 260 42 31 0 + 261 42 31 1 + 262 42 30 0 + 263 42 30 1 + 264 40 30 1 + 265 40 30 0 + 266 40 31 1 + 267 40 31 0 + 268 41 30 1 + 269 41 30 0 + 270 41 31 1 + 271 41 31 0 + 288 35 30 0 + 289 35 30 1 + 290 35 31 0 + 291 35 31 1 + 292 34 30 0 + 293 34 30 1 + 294 34 31 0 + 295 34 31 1 + 296 32 31 1 + 297 32 31 0 + 298 32 30 1 + 299 32 30 0 + 300 33 31 1 + 301 33 31 0 + 302 33 30 1 + 303 33 30 0 + 304 36 30 0 + 305 36 30 1 + 306 36 31 0 + 307 36 31 1 + 308 37 30 0 + 309 37 30 1 + 310 37 31 0 + 311 37 31 1 + 312 39 31 1 + 313 39 31 0 + 314 39 30 1 + 315 39 30 0 + 316 38 31 1 + 317 38 31 0 + 318 38 30 1 + 319 38 30 0 + 320 44 31 0 + 321 44 31 1 + 322 44 30 0 + 323 44 30 1 + 324 45 31 0 + 325 45 31 1 + 326 45 30 0 + 327 45 30 1 + 328 47 30 1 + 329 47 30 0 + 330 47 31 1 + 331 47 31 0 + 332 46 30 1 + 333 46 30 0 + 334 46 31 1 + 335 46 31 0 + 384 43 33 0 + 385 43 33 1 + 386 43 32 0 + 387 43 32 1 + 388 42 33 0 + 389 42 33 1 + 390 42 32 0 + 391 42 32 1 + 392 40 32 1 + 393 40 32 0 + 394 40 33 1 + 395 40 33 0 + 396 41 32 1 + 397 41 32 0 + 398 41 33 1 + 399 41 33 0 + 416 35 32 0 + 417 35 32 1 + 418 35 33 0 + 419 35 33 1 + 420 34 32 0 + 421 34 32 1 + 422 34 33 0 + 423 34 33 1 + 424 32 33 1 + 425 32 33 0 + 426 32 32 1 + 427 32 32 0 + 428 33 33 1 + 429 33 33 0 + 430 33 32 1 + 431 33 32 0 + 432 36 32 0 + 433 36 32 1 + 434 36 33 0 + 435 36 33 1 + 436 37 32 0 + 437 37 32 1 + 438 37 33 0 + 439 37 33 1 + 440 39 33 1 + 441 39 33 0 + 442 39 32 1 + 443 39 32 0 + 444 38 33 1 + 445 38 33 0 + 446 38 32 1 + 447 38 32 0 + 448 44 33 0 + 449 44 33 1 + 450 44 32 0 + 451 44 32 1 + 452 45 33 0 + 453 45 33 1 + 454 45 32 0 + 455 45 32 1 + 456 47 32 1 + 457 47 32 0 + 458 47 33 1 + 459 47 33 0 + 460 46 32 1 + 461 46 32 0 + 462 46 33 1 + 463 46 33 0 + 512 43 35 0 + 513 43 35 1 + 514 43 34 0 + 515 43 34 1 + 516 42 35 0 + 517 42 35 1 + 518 42 34 0 + 519 42 34 1 + 520 40 34 1 + 521 40 34 0 + 522 40 35 1 + 523 40 35 0 + 524 41 34 1 + 525 41 34 0 + 526 41 35 1 + 527 41 35 0 + 544 35 34 0 + 545 35 34 1 + 546 35 35 0 + 547 35 35 1 + 548 34 34 0 + 549 34 34 1 + 550 34 35 0 + 551 34 35 1 + 552 32 35 1 + 553 32 35 0 + 554 32 34 1 + 555 32 34 0 + 556 33 35 1 + 557 33 35 0 + 558 33 34 1 + 559 33 34 0 + 560 36 34 0 + 561 36 34 1 + 562 36 35 0 + 563 36 35 1 + 564 37 34 0 + 565 37 34 1 + 566 37 35 0 + 567 37 35 1 + 568 39 35 1 + 569 39 35 0 + 570 39 34 1 + 571 39 34 0 + 572 38 35 1 + 573 38 35 0 + 574 38 34 1 + 575 38 34 0 + 576 44 35 0 + 577 44 35 1 + 578 44 34 0 + 579 44 34 1 + 580 45 35 0 + 581 45 35 1 + 582 45 34 0 + 583 45 34 1 + 584 47 34 1 + 585 47 34 0 + 586 47 35 1 + 587 47 35 0 + 588 46 34 1 + 589 46 34 0 + 590 46 35 1 + 591 46 35 0 + 640 43 37 0 + 641 43 37 1 + 642 43 36 0 + 643 43 36 1 + 644 42 37 0 + 645 42 37 1 + 646 42 36 0 + 647 42 36 1 + 648 40 36 1 + 649 40 36 0 + 650 40 37 1 + 651 40 37 0 + 652 41 36 1 + 653 41 36 0 + 654 41 37 1 + 655 41 37 0 + 672 35 36 0 + 673 35 36 1 + 674 35 37 0 + 675 35 37 1 + 676 34 36 0 + 677 34 36 1 + 678 34 37 0 + 679 34 37 1 + 680 32 37 1 + 681 32 37 0 + 682 32 36 1 + 683 32 36 0 + 684 33 37 1 + 685 33 37 0 + 686 33 36 1 + 687 33 36 0 + 688 36 36 0 + 689 36 36 1 + 690 36 37 0 + 691 36 37 1 + 692 37 36 0 + 693 37 36 1 + 694 37 37 0 + 695 37 37 1 + 696 39 37 1 + 697 39 37 0 + 698 39 36 1 + 699 39 36 0 + 700 38 37 1 + 701 38 37 0 + 702 38 36 1 + 703 38 36 0 + 704 44 37 0 + 705 44 37 1 + 706 44 36 0 + 707 44 36 1 + 708 45 37 0 + 709 45 37 1 + 710 45 36 0 + 711 45 36 1 + 712 47 36 1 + 713 47 36 0 + 714 47 37 1 + 715 47 37 0 + 716 46 36 1 + 717 46 36 0 + 718 46 37 1 + 719 46 37 0 + 768 43 39 0 + 769 43 39 1 + 770 43 38 0 + 771 43 38 1 + 772 42 39 0 + 773 42 39 1 + 774 42 38 0 + 775 42 38 1 + 776 40 38 1 + 777 40 38 0 + 778 40 39 1 + 779 40 39 0 + 780 41 38 1 + 781 41 38 0 + 782 41 39 1 + 783 41 39 0 + 800 35 38 0 + 801 35 38 1 + 802 35 39 0 + 803 35 39 1 + 804 34 38 0 + 805 34 38 1 + 806 34 39 0 + 807 34 39 1 + 808 32 39 1 + 809 32 39 0 + 810 32 38 1 + 811 32 38 0 + 812 33 39 1 + 813 33 39 0 + 814 33 38 1 + 815 33 38 0 + 816 36 38 0 + 817 36 38 1 + 818 36 39 0 + 819 36 39 1 + 820 37 38 0 + 821 37 38 1 + 822 37 39 0 + 823 37 39 1 + 824 39 39 1 + 825 39 39 0 + 826 39 38 1 + 827 39 38 0 + 828 38 39 1 + 829 38 39 0 + 830 38 38 1 + 831 38 38 0 + 832 44 39 0 + 833 44 39 1 + 834 44 38 0 + 835 44 38 1 + 836 45 39 0 + 837 45 39 1 + 838 45 38 0 + 839 45 38 1 + 840 47 38 1 + 841 47 38 0 + 842 47 39 1 + 843 47 39 0 + 844 46 38 1 + 845 46 38 0 + 846 46 39 1 + 847 46 39 0 + 896 43 41 0 + 897 43 41 1 + 898 43 40 0 + 899 43 40 1 + 900 42 41 0 + 901 42 41 1 + 902 42 40 0 + 903 42 40 1 + 904 40 40 1 + 905 40 40 0 + 906 40 41 1 + 907 40 41 0 + 908 41 40 1 + 909 41 40 0 + 910 41 41 1 + 911 41 41 0 + 928 35 40 0 + 929 35 40 1 + 930 35 41 0 + 931 35 41 1 + 932 34 40 0 + 933 34 40 1 + 934 34 41 0 + 935 34 41 1 + 936 32 41 1 + 937 32 41 0 + 938 32 40 1 + 939 32 40 0 + 940 33 41 1 + 941 33 41 0 + 942 33 40 1 + 943 33 40 0 + 944 36 40 0 + 945 36 40 1 + 946 36 41 0 + 947 36 41 1 + 948 37 40 0 + 949 37 40 1 + 950 37 41 0 + 951 37 41 1 + 952 39 41 1 + 953 39 41 0 + 954 39 40 1 + 955 39 40 0 + 956 38 41 1 + 957 38 41 0 + 958 38 40 1 + 959 38 40 0 + 960 44 41 0 + 961 44 41 1 + 962 44 40 0 + 963 44 40 1 + 964 45 41 0 + 965 45 41 1 + 966 45 40 0 + 967 45 40 1 + 968 47 40 1 + 969 47 40 0 + 970 47 41 1 + 971 47 41 0 + 972 46 40 1 + 973 46 40 0 + 974 46 41 1 + 975 46 41 0 +1024 43 43 0 +1025 43 43 1 +1026 43 42 0 +1027 43 42 1 +1028 42 43 0 +1029 42 43 1 +1030 42 42 0 +1031 42 42 1 +1032 40 42 1 +1033 40 42 0 +1034 40 43 1 +1035 40 43 0 +1036 41 42 1 +1037 41 42 0 +1038 41 43 1 +1039 41 43 0 +1056 35 42 0 +1057 35 42 1 +1058 35 43 0 +1059 35 43 1 +1060 34 42 0 +1061 34 42 1 +1062 34 43 0 +1063 34 43 1 +1064 32 43 1 +1065 32 43 0 +1066 32 42 1 +1067 32 42 0 +1068 33 43 1 +1069 33 43 0 +1070 33 42 1 +1071 33 42 0 +1072 36 42 0 +1073 36 42 1 +1074 36 43 0 +1075 36 43 1 +1076 37 42 0 +1077 37 42 1 +1078 37 43 0 +1079 37 43 1 +1080 39 43 1 +1081 39 43 0 +1082 39 42 1 +1083 39 42 0 +1084 38 43 1 +1085 38 43 0 +1086 38 42 1 +1087 38 42 0 +1088 44 43 0 +1089 44 43 1 +1090 44 42 0 +1091 44 42 1 +1092 45 43 0 +1093 45 43 1 +1094 45 42 0 +1095 45 42 1 +1096 47 42 1 +1097 47 42 0 +1098 47 43 1 +1099 47 43 0 +1100 46 42 1 +1101 46 42 0 +1102 46 43 1 +1103 46 43 0 +1152 43 45 0 +1153 43 45 1 +1154 43 44 0 +1155 43 44 1 +1156 42 45 0 +1157 42 45 1 +1158 42 44 0 +1159 42 44 1 +1160 40 44 1 +1161 40 44 0 +1162 40 45 1 +1163 40 45 0 +1164 41 44 1 +1165 41 44 0 +1166 41 45 1 +1167 41 45 0 +1184 35 44 0 +1185 35 44 1 +1186 35 45 0 +1187 35 45 1 +1188 34 44 0 +1189 34 44 1 +1190 34 45 0 +1191 34 45 1 +1192 32 45 1 +1193 32 45 0 +1194 32 44 1 +1195 32 44 0 +1196 33 45 1 +1197 33 45 0 +1198 33 44 1 +1199 33 44 0 +1200 36 44 0 +1201 36 44 1 +1202 36 45 0 +1203 36 45 1 +1204 37 44 0 +1205 37 44 1 +1206 37 45 0 +1207 37 45 1 +1208 39 45 1 +1209 39 45 0 +1210 39 44 1 +1211 39 44 0 +1212 38 45 1 +1213 38 45 0 +1214 38 44 1 +1215 38 44 0 +1216 44 45 0 +1217 44 45 1 +1218 44 44 0 +1219 44 44 1 +1220 45 45 0 +1221 45 45 1 +1222 45 44 0 +1223 45 44 1 +1224 47 44 1 +1225 47 44 0 +1226 47 45 1 +1227 47 45 0 +1228 46 44 1 +1229 46 44 0 +1230 46 45 1 +1231 46 45 0 +1280 43 47 0 +1281 43 47 1 +1282 43 46 0 +1283 43 46 1 +1284 42 47 0 +1285 42 47 1 +1286 42 46 0 +1287 42 46 1 +1288 40 46 1 +1289 40 46 0 +1290 40 47 1 +1291 40 47 0 +1292 41 46 1 +1293 41 46 0 +1294 41 47 1 +1295 41 47 0 +1312 35 46 0 +1313 35 46 1 +1314 35 47 0 +1315 35 47 1 +1316 34 46 0 +1317 34 46 1 +1318 34 47 0 +1319 34 47 1 +1320 32 47 1 +1321 32 47 0 +1322 32 46 1 +1323 32 46 0 +1324 33 47 1 +1325 33 47 0 +1326 33 46 1 +1327 33 46 0 +1328 36 46 0 +1329 36 46 1 +1330 36 47 0 +1331 36 47 1 +1332 37 46 0 +1333 37 46 1 +1334 37 47 0 +1335 37 47 1 +1336 39 47 1 +1337 39 47 0 +1338 39 46 1 +1339 39 46 0 +1340 38 47 1 +1341 38 47 0 +1342 38 46 1 +1343 38 46 0 +1344 44 47 0 +1345 44 47 1 +1346 44 46 0 +1347 44 46 1 +1348 45 47 0 +1349 45 47 1 +1350 45 46 0 +1351 45 46 1 +1352 47 46 1 +1353 47 46 0 +1354 47 47 1 +1355 47 47 0 +1356 46 46 1 +1357 46 46 0 +1358 46 47 1 +1359 46 47 0 +1408 43 49 0 +1409 43 49 1 +1410 43 48 0 +1411 43 48 1 +1412 42 49 0 +1413 42 49 1 +1414 42 48 0 +1415 42 48 1 +1416 40 48 1 +1417 40 48 0 +1418 40 49 1 +1419 40 49 0 +1420 41 48 1 +1421 41 48 0 +1422 41 49 1 +1423 41 49 0 +1440 35 48 0 +1441 35 48 1 +1442 35 49 0 +1443 35 49 1 +1444 34 48 0 +1445 34 48 1 +1446 34 49 0 +1447 34 49 1 +1448 32 49 1 +1449 32 49 0 +1450 32 48 1 +1451 32 48 0 +1452 33 49 1 +1453 33 49 0 +1454 33 48 1 +1455 33 48 0 +1456 36 48 0 +1457 36 48 1 +1458 36 49 0 +1459 36 49 1 +1460 37 48 0 +1461 37 48 1 +1462 37 49 0 +1463 37 49 1 +1464 39 49 1 +1465 39 49 0 +1466 39 48 1 +1467 39 48 0 +1468 38 49 1 +1469 38 49 0 +1470 38 48 1 +1471 38 48 0 +1472 44 49 0 +1473 44 49 1 +1474 44 48 0 +1475 44 48 1 +1476 45 49 0 +1477 45 49 1 +1478 45 48 0 +1479 45 48 1 +1480 47 48 1 +1481 47 48 0 +1482 47 49 1 +1483 47 49 0 +1484 46 48 1 +1485 46 48 0 +1486 46 49 1 +1487 46 49 0 +1536 43 51 0 +1537 43 51 1 +1538 43 50 0 +1539 43 50 1 +1540 42 51 0 +1541 42 51 1 +1542 42 50 0 +1543 42 50 1 +1544 40 50 1 +1545 40 50 0 +1546 40 51 1 +1547 40 51 0 +1548 41 50 1 +1549 41 50 0 +1550 41 51 1 +1551 41 51 0 +1568 35 50 0 +1569 35 50 1 +1570 35 51 0 +1571 35 51 1 +1572 34 50 0 +1573 34 50 1 +1574 34 51 0 +1575 34 51 1 +1576 32 51 1 +1577 32 51 0 +1578 32 50 1 +1579 32 50 0 +1580 33 51 1 +1581 33 51 0 +1582 33 50 1 +1583 33 50 0 +1584 36 50 0 +1585 36 50 1 +1586 36 51 0 +1587 36 51 1 +1588 37 50 0 +1589 37 50 1 +1590 37 51 0 +1591 37 51 1 +1592 39 51 1 +1593 39 51 0 +1594 39 50 1 +1595 39 50 0 +1596 38 51 1 +1597 38 51 0 +1598 38 50 1 +1599 38 50 0 +1600 44 51 0 +1601 44 51 1 +1602 44 50 0 +1603 44 50 1 +1604 45 51 0 +1605 45 51 1 +1606 45 50 0 +1607 45 50 1 +1608 47 50 1 +1609 47 50 0 +1610 47 51 1 +1611 47 51 0 +1612 46 50 1 +1613 46 50 0 +1614 46 51 1 +1615 46 51 0 +1664 43 53 0 +1665 43 53 1 +1666 43 52 0 +1667 43 52 1 +1668 42 53 0 +1669 42 53 1 +1670 42 52 0 +1671 42 52 1 +1672 40 52 1 +1673 40 52 0 +1674 40 53 1 +1675 40 53 0 +1676 41 52 1 +1677 41 52 0 +1678 41 53 1 +1679 41 53 0 +1696 35 52 0 +1697 35 52 1 +1698 35 53 0 +1699 35 53 1 +1700 34 52 0 +1701 34 52 1 +1702 34 53 0 +1703 34 53 1 +1704 32 53 1 +1705 32 53 0 +1706 32 52 1 +1707 32 52 0 +1708 33 53 1 +1709 33 53 0 +1710 33 52 1 +1711 33 52 0 +1712 36 52 0 +1713 36 52 1 +1714 36 53 0 +1715 36 53 1 +1716 37 52 0 +1717 37 52 1 +1718 37 53 0 +1719 37 53 1 +1720 39 53 1 +1721 39 53 0 +1722 39 52 1 +1723 39 52 0 +1724 38 53 1 +1725 38 53 0 +1726 38 52 1 +1727 38 52 0 +1728 44 53 0 +1729 44 53 1 +1730 44 52 0 +1731 44 52 1 +1732 45 53 0 +1733 45 53 1 +1734 45 52 0 +1735 45 52 1 +1736 47 52 1 +1737 47 52 0 +1738 47 53 1 +1739 47 53 0 +1740 46 52 1 +1741 46 52 0 +1742 46 53 1 +1743 46 53 0 +1792 43 55 0 +1793 43 55 1 +1794 43 54 0 +1795 43 54 1 +1796 42 55 0 +1797 42 55 1 +1798 42 54 0 +1799 42 54 1 +1800 40 54 1 +1801 40 54 0 +1802 40 55 1 +1803 40 55 0 +1804 41 54 1 +1805 41 54 0 +1806 41 55 1 +1807 41 55 0 +1824 35 54 0 +1825 35 54 1 +1826 35 55 0 +1827 35 55 1 +1828 34 54 0 +1829 34 54 1 +1830 34 55 0 +1831 34 55 1 +1832 32 55 1 +1833 32 55 0 +1834 32 54 1 +1835 32 54 0 +1836 33 55 1 +1837 33 55 0 +1838 33 54 1 +1839 33 54 0 +1840 36 54 0 +1841 36 54 1 +1842 36 55 0 +1843 36 55 1 +1844 37 54 0 +1845 37 54 1 +1846 37 55 0 +1847 37 55 1 +1848 39 55 1 +1849 39 55 0 +1850 39 54 1 +1851 39 54 0 +1852 38 55 1 +1853 38 55 0 +1854 38 54 1 +1855 38 54 0 +1856 44 55 0 +1857 44 55 1 +1858 44 54 0 +1859 44 54 1 +1860 45 55 0 +1861 45 55 1 +1862 45 54 0 +1863 45 54 1 +1864 47 54 1 +1865 47 54 0 +1866 47 55 1 +1867 47 55 0 +1868 46 54 1 +1869 46 54 0 +1870 46 55 1 +1871 46 55 0 +2176 43 27 0 +2177 43 27 1 +2178 43 26 0 +2179 43 26 1 +2180 42 27 0 +2181 42 27 1 +2182 42 26 0 +2183 42 26 1 +2184 40 26 1 +2185 40 26 0 +2186 40 27 1 +2187 40 27 0 +2188 41 26 1 +2189 41 26 0 +2190 41 27 1 +2191 41 27 0 +2208 35 26 0 +2209 35 26 1 +2210 35 27 0 +2211 35 27 1 +2212 34 26 0 +2213 34 26 1 +2214 34 27 0 +2215 34 27 1 +2216 32 27 1 +2217 32 27 0 +2218 32 26 1 +2219 32 26 0 +2220 33 27 1 +2221 33 27 0 +2222 33 26 1 +2223 33 26 0 +2224 36 26 0 +2225 36 26 1 +2226 36 27 0 +2227 36 27 1 +2228 37 26 0 +2229 37 26 1 +2230 37 27 0 +2231 37 27 1 +2232 39 27 1 +2233 39 27 0 +2234 39 26 1 +2235 39 26 0 +2236 38 27 1 +2237 38 27 0 +2238 38 26 1 +2239 38 26 0 +2240 44 27 0 +2241 44 27 1 +2242 44 26 0 +2243 44 26 1 +2244 45 27 0 +2245 45 27 1 +2246 45 26 0 +2247 45 26 1 +2248 47 26 1 +2249 47 26 0 +2250 47 27 1 +2251 47 27 0 +2252 46 26 1 +2253 46 26 0 +2254 46 27 1 +2255 46 27 0 +2304 43 25 0 +2305 43 25 1 +2306 43 24 0 +2307 43 24 1 +2308 42 25 0 +2309 42 25 1 +2310 42 24 0 +2311 42 24 1 +2312 40 24 1 +2313 40 24 0 +2314 40 25 1 +2315 40 25 0 +2316 41 24 1 +2317 41 24 0 +2318 41 25 1 +2319 41 25 0 +2336 35 24 0 +2337 35 24 1 +2338 35 25 0 +2339 35 25 1 +2340 34 24 0 +2341 34 24 1 +2342 34 25 0 +2343 34 25 1 +2344 32 25 1 +2345 32 25 0 +2346 32 24 1 +2347 32 24 0 +2348 33 25 1 +2349 33 25 0 +2350 33 24 1 +2351 33 24 0 +2352 36 24 0 +2353 36 24 1 +2354 36 25 0 +2355 36 25 1 +2356 37 24 0 +2357 37 24 1 +2358 37 25 0 +2359 37 25 1 +2360 39 25 1 +2361 39 25 0 +2362 39 24 1 +2363 39 24 0 +2364 38 25 1 +2365 38 25 0 +2366 38 24 1 +2367 38 24 0 +2368 44 25 0 +2369 44 25 1 +2370 44 24 0 +2371 44 24 1 +2372 45 25 0 +2373 45 25 1 +2374 45 24 0 +2375 45 24 1 +2376 47 24 1 +2377 47 24 0 +2378 47 25 1 +2379 47 25 0 +2380 46 24 1 +2381 46 24 0 +2382 46 25 1 +2383 46 25 0 +2432 43 23 0 +2433 43 23 1 +2434 43 22 0 +2435 43 22 1 +2436 42 23 0 +2437 42 23 1 +2438 42 22 0 +2439 42 22 1 +2440 40 22 1 +2441 40 22 0 +2442 40 23 1 +2443 40 23 0 +2444 41 22 1 +2445 41 22 0 +2446 41 23 1 +2447 41 23 0 +2464 35 22 0 +2465 35 22 1 +2466 35 23 0 +2467 35 23 1 +2468 34 22 0 +2469 34 22 1 +2470 34 23 0 +2471 34 23 1 +2472 32 23 1 +2473 32 23 0 +2474 32 22 1 +2475 32 22 0 +2476 33 23 1 +2477 33 23 0 +2478 33 22 1 +2479 33 22 0 +2480 36 22 0 +2481 36 22 1 +2482 36 23 0 +2483 36 23 1 +2484 37 22 0 +2485 37 22 1 +2486 37 23 0 +2487 37 23 1 +2488 39 23 1 +2489 39 23 0 +2490 39 22 1 +2491 39 22 0 +2492 38 23 1 +2493 38 23 0 +2494 38 22 1 +2495 38 22 0 +2496 44 23 0 +2497 44 23 1 +2498 44 22 0 +2499 44 22 1 +2500 45 23 0 +2501 45 23 1 +2502 45 22 0 +2503 45 22 1 +2504 47 22 1 +2505 47 22 0 +2506 47 23 1 +2507 47 23 0 +2508 46 22 1 +2509 46 22 0 +2510 46 23 1 +2511 46 23 0 +2560 43 21 0 +2561 43 21 1 +2562 43 20 0 +2563 43 20 1 +2564 42 21 0 +2565 42 21 1 +2566 42 20 0 +2567 42 20 1 +2568 40 20 1 +2569 40 20 0 +2570 40 21 1 +2571 40 21 0 +2572 41 20 1 +2573 41 20 0 +2574 41 21 1 +2575 41 21 0 +2592 35 20 0 +2593 35 20 1 +2594 35 21 0 +2595 35 21 1 +2596 34 20 0 +2597 34 20 1 +2598 34 21 0 +2599 34 21 1 +2600 32 21 1 +2601 32 21 0 +2602 32 20 1 +2603 32 20 0 +2604 33 21 1 +2605 33 21 0 +2606 33 20 1 +2607 33 20 0 +2608 36 20 0 +2609 36 20 1 +2610 36 21 0 +2611 36 21 1 +2612 37 20 0 +2613 37 20 1 +2614 37 21 0 +2615 37 21 1 +2616 39 21 1 +2617 39 21 0 +2618 39 20 1 +2619 39 20 0 +2620 38 21 1 +2621 38 21 0 +2622 38 20 1 +2623 38 20 0 +2624 44 21 0 +2625 44 21 1 +2626 44 20 0 +2627 44 20 1 +2628 45 21 0 +2629 45 21 1 +2630 45 20 0 +2631 45 20 1 +2632 47 20 1 +2633 47 20 0 +2634 47 21 1 +2635 47 21 0 +2636 46 20 1 +2637 46 20 0 +2638 46 21 1 +2639 46 21 0 +2688 43 19 0 +2689 43 19 1 +2690 43 18 0 +2691 43 18 1 +2692 42 19 0 +2693 42 19 1 +2694 42 18 0 +2695 42 18 1 +2696 40 18 1 +2697 40 18 0 +2698 40 19 1 +2699 40 19 0 +2700 41 18 1 +2701 41 18 0 +2702 41 19 1 +2703 41 19 0 +2720 35 18 0 +2721 35 18 1 +2722 35 19 0 +2723 35 19 1 +2724 34 18 0 +2725 34 18 1 +2726 34 19 0 +2727 34 19 1 +2728 32 19 1 +2729 32 19 0 +2730 32 18 1 +2731 32 18 0 +2732 33 19 1 +2733 33 19 0 +2734 33 18 1 +2735 33 18 0 +2736 36 18 0 +2737 36 18 1 +2738 36 19 0 +2739 36 19 1 +2740 37 18 0 +2741 37 18 1 +2742 37 19 0 +2743 37 19 1 +2744 39 19 1 +2745 39 19 0 +2746 39 18 1 +2747 39 18 0 +2748 38 19 1 +2749 38 19 0 +2750 38 18 1 +2751 38 18 0 +2752 44 19 0 +2753 44 19 1 +2754 44 18 0 +2755 44 18 1 +2756 45 19 0 +2757 45 19 1 +2758 45 18 0 +2759 45 18 1 +2760 47 18 1 +2761 47 18 0 +2762 47 19 1 +2763 47 19 0 +2764 46 18 1 +2765 46 18 0 +2766 46 19 1 +2767 46 19 0 +2816 43 17 0 +2817 43 17 1 +2818 43 16 0 +2819 43 16 1 +2820 42 17 0 +2821 42 17 1 +2822 42 16 0 +2823 42 16 1 +2824 40 16 1 +2825 40 16 0 +2826 40 17 1 +2827 40 17 0 +2828 41 16 1 +2829 41 16 0 +2830 41 17 1 +2831 41 17 0 +2848 35 16 0 +2849 35 16 1 +2850 35 17 0 +2851 35 17 1 +2852 34 16 0 +2853 34 16 1 +2854 34 17 0 +2855 34 17 1 +2856 32 17 1 +2857 32 17 0 +2858 32 16 1 +2859 32 16 0 +2860 33 17 1 +2861 33 17 0 +2862 33 16 1 +2863 33 16 0 +2864 36 16 0 +2865 36 16 1 +2866 36 17 0 +2867 36 17 1 +2868 37 16 0 +2869 37 16 1 +2870 37 17 0 +2871 37 17 1 +2872 39 17 1 +2873 39 17 0 +2874 39 16 1 +2875 39 16 0 +2876 38 17 1 +2877 38 17 0 +2878 38 16 1 +2879 38 16 0 +2880 44 17 0 +2881 44 17 1 +2882 44 16 0 +2883 44 16 1 +2884 45 17 0 +2885 45 17 1 +2886 45 16 0 +2887 45 16 1 +2888 47 16 1 +2889 47 16 0 +2890 47 17 1 +2891 47 17 0 +2892 46 16 1 +2893 46 16 0 +2894 46 17 1 +2895 46 17 0 +2944 43 15 0 +2945 43 15 1 +2946 43 14 0 +2947 43 14 1 +2948 42 15 0 +2949 42 15 1 +2950 42 14 0 +2951 42 14 1 +2952 40 14 1 +2953 40 14 0 +2954 40 15 1 +2955 40 15 0 +2956 41 14 1 +2957 41 14 0 +2958 41 15 1 +2959 41 15 0 +2976 35 14 0 +2977 35 14 1 +2978 35 15 0 +2979 35 15 1 +2980 34 14 0 +2981 34 14 1 +2982 34 15 0 +2983 34 15 1 +2984 32 15 1 +2985 32 15 0 +2986 32 14 1 +2987 32 14 0 +2988 33 15 1 +2989 33 15 0 +2990 33 14 1 +2991 33 14 0 +2992 36 14 0 +2993 36 14 1 +2994 36 15 0 +2995 36 15 1 +2996 37 14 0 +2997 37 14 1 +2998 37 15 0 +2999 37 15 1 +3000 39 15 1 +3001 39 15 0 +3002 39 14 1 +3003 39 14 0 +3004 38 15 1 +3005 38 15 0 +3006 38 14 1 +3007 38 14 0 +3008 44 15 0 +3009 44 15 1 +3010 44 14 0 +3011 44 14 1 +3012 45 15 0 +3013 45 15 1 +3014 45 14 0 +3015 45 14 1 +3016 47 14 1 +3017 47 14 0 +3018 47 15 1 +3019 47 15 0 +3020 46 14 1 +3021 46 14 0 +3022 46 15 1 +3023 46 15 0 +3072 43 13 0 +3073 43 13 1 +3074 43 12 0 +3075 43 12 1 +3076 42 13 0 +3077 42 13 1 +3078 42 12 0 +3079 42 12 1 +3080 40 12 1 +3081 40 12 0 +3082 40 13 1 +3083 40 13 0 +3084 41 12 1 +3085 41 12 0 +3086 41 13 1 +3087 41 13 0 +3104 35 12 0 +3105 35 12 1 +3106 35 13 0 +3107 35 13 1 +3108 34 12 0 +3109 34 12 1 +3110 34 13 0 +3111 34 13 1 +3112 32 13 1 +3113 32 13 0 +3114 32 12 1 +3115 32 12 0 +3116 33 13 1 +3117 33 13 0 +3118 33 12 1 +3119 33 12 0 +3120 36 12 0 +3121 36 12 1 +3122 36 13 0 +3123 36 13 1 +3124 37 12 0 +3125 37 12 1 +3126 37 13 0 +3127 37 13 1 +3128 39 13 1 +3129 39 13 0 +3130 39 12 1 +3131 39 12 0 +3132 38 13 1 +3133 38 13 0 +3134 38 12 1 +3135 38 12 0 +3136 44 13 0 +3137 44 13 1 +3138 44 12 0 +3139 44 12 1 +3140 45 13 0 +3141 45 13 1 +3142 45 12 0 +3143 45 12 1 +3144 47 12 1 +3145 47 12 0 +3146 47 13 1 +3147 47 13 0 +3148 46 12 1 +3149 46 12 0 +3150 46 13 1 +3151 46 13 0 +3200 43 11 0 +3201 43 11 1 +3202 43 10 0 +3203 43 10 1 +3204 42 11 0 +3205 42 11 1 +3206 42 10 0 +3207 42 10 1 +3208 40 10 1 +3209 40 10 0 +3210 40 11 1 +3211 40 11 0 +3212 41 10 1 +3213 41 10 0 +3214 41 11 1 +3215 41 11 0 +3232 35 10 0 +3233 35 10 1 +3234 35 11 0 +3235 35 11 1 +3236 34 10 0 +3237 34 10 1 +3238 34 11 0 +3239 34 11 1 +3240 32 11 1 +3241 32 11 0 +3242 32 10 1 +3243 32 10 0 +3244 33 11 1 +3245 33 11 0 +3246 33 10 1 +3247 33 10 0 +3248 36 10 0 +3249 36 10 1 +3250 36 11 0 +3251 36 11 1 +3252 37 10 0 +3253 37 10 1 +3254 37 11 0 +3255 37 11 1 +3256 39 11 1 +3257 39 11 0 +3258 39 10 1 +3259 39 10 0 +3260 38 11 1 +3261 38 11 0 +3262 38 10 1 +3263 38 10 0 +3264 44 11 0 +3265 44 11 1 +3266 44 10 0 +3267 44 10 1 +3268 45 11 0 +3269 45 11 1 +3270 45 10 0 +3271 45 10 1 +3272 47 10 1 +3273 47 10 0 +3274 47 11 1 +3275 47 11 0 +3276 46 10 1 +3277 46 10 0 +3278 46 11 1 +3279 46 11 0 +3328 43 9 0 +3329 43 9 1 +3330 43 8 0 +3331 43 8 1 +3332 42 9 0 +3333 42 9 1 +3334 42 8 0 +3335 42 8 1 +3336 40 8 1 +3337 40 8 0 +3338 40 9 1 +3339 40 9 0 +3340 41 8 1 +3341 41 8 0 +3342 41 9 1 +3343 41 9 0 +3360 35 8 0 +3361 35 8 1 +3362 35 9 0 +3363 35 9 1 +3364 34 8 0 +3365 34 8 1 +3366 34 9 0 +3367 34 9 1 +3368 32 9 1 +3369 32 9 0 +3370 32 8 1 +3371 32 8 0 +3372 33 9 1 +3373 33 9 0 +3374 33 8 1 +3375 33 8 0 +3376 36 8 0 +3377 36 8 1 +3378 36 9 0 +3379 36 9 1 +3380 37 8 0 +3381 37 8 1 +3382 37 9 0 +3383 37 9 1 +3384 39 9 1 +3385 39 9 0 +3386 39 8 1 +3387 39 8 0 +3388 38 9 1 +3389 38 9 0 +3390 38 8 1 +3391 38 8 0 +3392 44 9 0 +3393 44 9 1 +3394 44 8 0 +3395 44 8 1 +3396 45 9 0 +3397 45 9 1 +3398 45 8 0 +3399 45 8 1 +3400 47 8 1 +3401 47 8 0 +3402 47 9 1 +3403 47 9 0 +3404 46 8 1 +3405 46 8 0 +3406 46 9 1 +3407 46 9 0 +3456 43 7 0 +3457 43 7 1 +3458 43 6 0 +3459 43 6 1 +3460 42 7 0 +3461 42 7 1 +3462 42 6 0 +3463 42 6 1 +3464 40 6 1 +3465 40 6 0 +3466 40 7 1 +3467 40 7 0 +3468 41 6 1 +3469 41 6 0 +3470 41 7 1 +3471 41 7 0 +3488 35 6 0 +3489 35 6 1 +3490 35 7 0 +3491 35 7 1 +3492 34 6 0 +3493 34 6 1 +3494 34 7 0 +3495 34 7 1 +3496 32 7 1 +3497 32 7 0 +3498 32 6 1 +3499 32 6 0 +3500 33 7 1 +3501 33 7 0 +3502 33 6 1 +3503 33 6 0 +3504 36 6 0 +3505 36 6 1 +3506 36 7 0 +3507 36 7 1 +3508 37 6 0 +3509 37 6 1 +3510 37 7 0 +3511 37 7 1 +3512 39 7 1 +3513 39 7 0 +3514 39 6 1 +3515 39 6 0 +3516 38 7 1 +3517 38 7 0 +3518 38 6 1 +3519 38 6 0 +3520 44 7 0 +3521 44 7 1 +3522 44 6 0 +3523 44 6 1 +3524 45 7 0 +3525 45 7 1 +3526 45 6 0 +3527 45 6 1 +3528 47 6 1 +3529 47 6 0 +3530 47 7 1 +3531 47 7 0 +3532 46 6 1 +3533 46 6 0 +3534 46 7 1 +3535 46 7 0 +3584 43 5 0 +3585 43 5 1 +3586 43 4 0 +3587 43 4 1 +3588 42 5 0 +3589 42 5 1 +3590 42 4 0 +3591 42 4 1 +3592 40 4 1 +3593 40 4 0 +3594 40 5 1 +3595 40 5 0 +3596 41 4 1 +3597 41 4 0 +3598 41 5 1 +3599 41 5 0 +3616 35 4 0 +3617 35 4 1 +3618 35 5 0 +3619 35 5 1 +3620 34 4 0 +3621 34 4 1 +3622 34 5 0 +3623 34 5 1 +3624 32 5 1 +3625 32 5 0 +3626 32 4 1 +3627 32 4 0 +3628 33 5 1 +3629 33 5 0 +3630 33 4 1 +3631 33 4 0 +3632 36 4 0 +3633 36 4 1 +3634 36 5 0 +3635 36 5 1 +3636 37 4 0 +3637 37 4 1 +3638 37 5 0 +3639 37 5 1 +3640 39 5 1 +3641 39 5 0 +3642 39 4 1 +3643 39 4 0 +3644 38 5 1 +3645 38 5 0 +3646 38 4 1 +3647 38 4 0 +3648 44 5 0 +3649 44 5 1 +3650 44 4 0 +3651 44 4 1 +3652 45 5 0 +3653 45 5 1 +3654 45 4 0 +3655 45 4 1 +3656 47 4 1 +3657 47 4 0 +3658 47 5 1 +3659 47 5 0 +3660 46 4 1 +3661 46 4 0 +3662 46 5 1 +3663 46 5 0 +3712 43 3 0 +3713 43 3 1 +3714 43 2 0 +3715 43 2 1 +3716 42 3 0 +3717 42 3 1 +3718 42 2 0 +3719 42 2 1 +3720 40 2 1 +3721 40 2 0 +3722 40 3 1 +3723 40 3 0 +3724 41 2 1 +3725 41 2 0 +3726 41 3 1 +3727 41 3 0 +3744 35 2 0 +3745 35 2 1 +3746 35 3 0 +3747 35 3 1 +3748 34 2 0 +3749 34 2 1 +3750 34 3 0 +3751 34 3 1 +3752 32 3 1 +3753 32 3 0 +3754 32 2 1 +3755 32 2 0 +3756 33 3 1 +3757 33 3 0 +3758 33 2 1 +3759 33 2 0 +3760 36 2 0 +3761 36 2 1 +3762 36 3 0 +3763 36 3 1 +3764 37 2 0 +3765 37 2 1 +3766 37 3 0 +3767 37 3 1 +3768 39 3 1 +3769 39 3 0 +3770 39 2 1 +3771 39 2 0 +3772 38 3 1 +3773 38 3 0 +3774 38 2 1 +3775 38 2 0 +3776 44 3 0 +3777 44 3 1 +3778 44 2 0 +3779 44 2 1 +3780 45 3 0 +3781 45 3 1 +3782 45 2 0 +3783 45 2 1 +3784 47 2 1 +3785 47 2 0 +3786 47 3 1 +3787 47 3 0 +3788 46 2 1 +3789 46 2 0 +3790 46 3 1 +3791 46 3 0 +3840 43 1 0 +3841 43 1 1 +3842 43 0 0 +3843 43 0 1 +3844 42 1 0 +3845 42 1 1 +3846 42 0 0 +3847 42 0 1 +3848 40 0 1 +3849 40 0 0 +3850 40 1 1 +3851 40 1 0 +3852 41 0 1 +3853 41 0 0 +3854 41 1 1 +3855 41 1 0 +3872 35 0 0 +3873 35 0 1 +3874 35 1 0 +3875 35 1 1 +3876 34 0 0 +3877 34 0 1 +3878 34 1 0 +3879 34 1 1 +3880 32 1 1 +3881 32 1 0 +3882 32 0 1 +3883 32 0 0 +3884 33 1 1 +3885 33 1 0 +3886 33 0 1 +3887 33 0 0 +3888 36 0 0 +3889 36 0 1 +3890 36 1 0 +3891 36 1 1 +3892 37 0 0 +3893 37 0 1 +3894 37 1 0 +3895 37 1 1 +3896 39 1 1 +3897 39 1 0 +3898 39 0 1 +3899 39 0 0 +3900 38 1 1 +3901 38 1 0 +3902 38 0 1 +3903 38 0 0 +3904 44 1 0 +3905 44 1 1 +3906 44 0 0 +3907 44 0 1 +3908 45 1 0 +3909 45 1 1 +3910 45 0 0 +3911 45 0 1 +3912 47 0 1 +3913 47 0 0 +3914 47 1 1 +3915 47 1 0 +3916 46 0 1 +3917 46 0 0 +3918 46 1 1 +3919 46 1 0 diff --git a/Detectors/PHOS/base/files/Mod0RCU3.data b/Detectors/PHOS/base/files/Mod0RCU3.data new file mode 100644 index 0000000000000..43bd76fc42791 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod0RCU3.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 3 0 2 + 1 3 1 2 + 2 3 2 2 + 3 3 3 2 + 4 3 4 2 + 5 3 5 2 + 6 3 6 2 + 7 3 7 2 + 8 3 8 2 + 9 3 9 2 + 10 3 10 2 + 11 3 11 2 + 12 3 12 2 + 13 3 13 2 + 14 3 14 2 + 15 3 15 2 + 16 3 16 2 + 17 3 17 2 + 18 3 18 2 + 19 3 19 2 + 20 3 20 2 + 21 3 21 2 + 22 3 22 2 + 23 3 23 2 + 24 3 24 2 + 25 3 25 2 + 26 3 26 2 + 27 3 27 2 + 28 3 28 2 + 29 3 29 2 + 30 3 30 2 + 31 3 31 2 + 32 3 32 2 + 33 3 33 2 + 34 3 34 2 + 35 3 35 2 + 36 3 36 2 + 37 3 37 2 + 38 3 38 2 + 39 3 39 2 + 40 3 40 2 + 41 3 41 2 + 42 3 42 2 + 43 3 43 2 + 44 3 44 2 + 45 3 45 2 + 46 3 46 2 + 47 3 47 2 + 48 3 48 2 + 49 3 49 2 + 50 3 50 2 + 51 3 51 2 + 52 3 52 2 + 53 3 53 2 + 54 3 54 2 + 55 3 55 2 + 56 3 56 2 + 57 3 57 2 + 58 3 58 2 + 59 3 59 2 + 60 3 60 2 + 61 3 61 2 + 62 3 62 2 + 63 3 63 2 + 64 3 64 2 + 65 3 65 2 + 66 3 66 2 + 67 3 67 2 + 68 3 68 2 + 69 3 69 2 + 70 3 70 2 + 71 3 71 2 + 72 3 72 2 + 73 3 73 2 + 74 3 74 2 + 75 3 75 2 + 76 3 76 2 + 77 3 77 2 + 78 3 78 2 + 79 3 79 2 + 80 3 80 2 + 81 3 81 2 + 82 3 82 2 + 83 3 83 2 + 84 3 84 2 + 85 3 85 2 + 86 3 86 2 + 87 3 87 2 + 88 3 88 2 + 89 3 89 2 + 90 3 90 2 + 91 3 91 2 + 92 3 92 2 + 93 3 93 2 + 94 3 94 2 + 95 3 95 2 + 96 3 96 2 + 97 3 97 2 + 98 3 98 2 + 99 3 99 2 + 100 3 100 2 + 101 3 101 2 + 102 3 102 2 + 103 3 103 2 + 104 3 104 2 + 105 3 105 2 + 106 3 106 2 + 107 3 107 2 + 108 3 108 2 + 109 3 109 2 + 110 3 110 2 + 111 3 111 2 + 112 3 112 2 + 113 3 113 2 + 114 3 114 2 + 115 3 115 2 + 116 3 116 2 + 117 3 117 2 + 118 3 118 2 + 119 3 119 2 + 120 3 120 2 + 121 3 121 2 + 122 3 122 2 + 123 3 123 2 + 124 3 124 2 + 125 3 125 2 + 126 3 126 2 + 127 3 127 2 +2048 3 2048 2 +2049 3 2049 2 +2050 3 2050 2 +2051 3 2051 2 +2052 3 2052 2 +2053 3 2053 2 +2054 3 2054 2 +2055 3 2055 2 +2056 3 2056 2 +2057 3 2057 2 +2058 3 2058 2 +2059 3 2059 2 +2060 3 2060 2 +2061 3 2061 2 +2062 3 2062 2 +2063 3 2063 2 +2064 3 2064 2 +2065 3 2065 2 +2066 3 2066 2 +2067 3 2067 2 +2068 3 2068 2 +2069 3 2069 2 +2070 3 2070 2 +2071 3 2071 2 +2072 3 2072 2 +2073 3 2073 2 +2074 3 2074 2 +2075 3 2075 2 +2076 3 2076 2 +2077 3 2077 2 +2078 3 2078 2 +2079 3 2079 2 +2080 3 2080 2 +2081 3 2081 2 +2082 3 2082 2 +2083 3 2083 2 +2084 3 2084 2 +2085 3 2085 2 +2086 3 2086 2 +2087 3 2087 2 +2088 3 2088 2 +2089 3 2089 2 +2090 3 2090 2 +2091 3 2091 2 +2092 3 2092 2 +2093 3 2093 2 +2094 3 2094 2 +2095 3 2095 2 +2096 3 2096 2 +2097 3 2097 2 +2098 3 2098 2 +2099 3 2099 2 +2100 3 2100 2 +2101 3 2101 2 +2102 3 2102 2 +2103 3 2103 2 +2104 3 2104 2 +2105 3 2105 2 +2106 3 2106 2 +2107 3 2107 2 +2108 3 2108 2 +2109 3 2109 2 +2110 3 2110 2 +2111 3 2111 2 +2112 3 2112 2 +2113 3 2113 2 +2114 3 2114 2 +2115 3 2115 2 +2116 3 2116 2 +2117 3 2117 2 +2118 3 2118 2 +2119 3 2119 2 +2120 3 2120 2 +2121 3 2121 2 +2122 3 2122 2 +2123 3 2123 2 +2124 3 2124 2 +2125 3 2125 2 +2126 3 2126 2 +2127 3 2127 2 +2128 3 2128 2 +2129 3 2129 2 +2130 3 2130 2 +2131 3 2131 2 +2132 3 2132 2 +2133 3 2133 2 +2134 3 2134 2 +2135 3 2135 2 +2136 3 2136 2 +2137 3 2137 2 +2138 3 2138 2 +2139 3 2139 2 +2140 3 2140 2 +2141 3 2141 2 +2142 3 2142 2 +2143 3 2143 2 +2144 3 2144 2 +2145 3 2145 2 +2146 3 2146 2 +2147 3 2147 2 +2148 3 2148 2 +2149 3 2149 2 +2150 3 2150 2 +2151 3 2151 2 +2152 3 2152 2 +2153 3 2153 2 +2154 3 2154 2 +2155 3 2155 2 +2156 3 2156 2 +2157 3 2157 2 +2158 3 2158 2 +2159 3 2159 2 +2160 3 2160 2 +2161 3 2161 2 +2162 3 2162 2 +2163 3 2163 2 +2164 3 2164 2 +2165 3 2165 2 +2166 3 2166 2 +2167 3 2167 2 +2168 3 2168 2 +2169 3 2169 2 +2170 3 2170 2 +2171 3 2171 2 +2172 3 2172 2 +2173 3 2173 2 +2174 3 2174 2 +2175 3 2175 2 + 128 59 29 0 + 129 59 29 1 + 130 59 28 0 + 131 59 28 1 + 132 58 29 0 + 133 58 29 1 + 134 58 28 0 + 135 58 28 1 + 136 56 28 1 + 137 56 28 0 + 138 56 29 1 + 139 56 29 0 + 140 57 28 1 + 141 57 28 0 + 142 57 29 1 + 143 57 29 0 + 160 51 28 0 + 161 51 28 1 + 162 51 29 0 + 163 51 29 1 + 164 50 28 0 + 165 50 28 1 + 166 50 29 0 + 167 50 29 1 + 168 48 29 1 + 169 48 29 0 + 170 48 28 1 + 171 48 28 0 + 172 49 29 1 + 173 49 29 0 + 174 49 28 1 + 175 49 28 0 + 176 52 28 0 + 177 52 28 1 + 178 52 29 0 + 179 52 29 1 + 180 53 28 0 + 181 53 28 1 + 182 53 29 0 + 183 53 29 1 + 184 55 29 1 + 185 55 29 0 + 186 55 28 1 + 187 55 28 0 + 188 54 29 1 + 189 54 29 0 + 190 54 28 1 + 191 54 28 0 + 192 60 29 0 + 193 60 29 1 + 194 60 28 0 + 195 60 28 1 + 196 61 29 0 + 197 61 29 1 + 198 61 28 0 + 199 61 28 1 + 200 63 28 1 + 201 63 28 0 + 202 63 29 1 + 203 63 29 0 + 204 62 28 1 + 205 62 28 0 + 206 62 29 1 + 207 62 29 0 + 256 59 31 0 + 257 59 31 1 + 258 59 30 0 + 259 59 30 1 + 260 58 31 0 + 261 58 31 1 + 262 58 30 0 + 263 58 30 1 + 264 56 30 1 + 265 56 30 0 + 266 56 31 1 + 267 56 31 0 + 268 57 30 1 + 269 57 30 0 + 270 57 31 1 + 271 57 31 0 + 288 51 30 0 + 289 51 30 1 + 290 51 31 0 + 291 51 31 1 + 292 50 30 0 + 293 50 30 1 + 294 50 31 0 + 295 50 31 1 + 296 48 31 1 + 297 48 31 0 + 298 48 30 1 + 299 48 30 0 + 300 49 31 1 + 301 49 31 0 + 302 49 30 1 + 303 49 30 0 + 304 52 30 0 + 305 52 30 1 + 306 52 31 0 + 307 52 31 1 + 308 53 30 0 + 309 53 30 1 + 310 53 31 0 + 311 53 31 1 + 312 55 31 1 + 313 55 31 0 + 314 55 30 1 + 315 55 30 0 + 316 54 31 1 + 317 54 31 0 + 318 54 30 1 + 319 54 30 0 + 320 60 31 0 + 321 60 31 1 + 322 60 30 0 + 323 60 30 1 + 324 61 31 0 + 325 61 31 1 + 326 61 30 0 + 327 61 30 1 + 328 63 30 1 + 329 63 30 0 + 330 63 31 1 + 331 63 31 0 + 332 62 30 1 + 333 62 30 0 + 334 62 31 1 + 335 62 31 0 + 384 59 33 0 + 385 59 33 1 + 386 59 32 0 + 387 59 32 1 + 388 58 33 0 + 389 58 33 1 + 390 58 32 0 + 391 58 32 1 + 392 56 32 1 + 393 56 32 0 + 394 56 33 1 + 395 56 33 0 + 396 57 32 1 + 397 57 32 0 + 398 57 33 1 + 399 57 33 0 + 416 51 32 0 + 417 51 32 1 + 418 51 33 0 + 419 51 33 1 + 420 50 32 0 + 421 50 32 1 + 422 50 33 0 + 423 50 33 1 + 424 48 33 1 + 425 48 33 0 + 426 48 32 1 + 427 48 32 0 + 428 49 33 1 + 429 49 33 0 + 430 49 32 1 + 431 49 32 0 + 432 52 32 0 + 433 52 32 1 + 434 52 33 0 + 435 52 33 1 + 436 53 32 0 + 437 53 32 1 + 438 53 33 0 + 439 53 33 1 + 440 55 33 1 + 441 55 33 0 + 442 55 32 1 + 443 55 32 0 + 444 54 33 1 + 445 54 33 0 + 446 54 32 1 + 447 54 32 0 + 448 60 33 0 + 449 60 33 1 + 450 60 32 0 + 451 60 32 1 + 452 61 33 0 + 453 61 33 1 + 454 61 32 0 + 455 61 32 1 + 456 63 32 1 + 457 63 32 0 + 458 63 33 1 + 459 63 33 0 + 460 62 32 1 + 461 62 32 0 + 462 62 33 1 + 463 62 33 0 + 512 59 35 0 + 513 59 35 1 + 514 59 34 0 + 515 59 34 1 + 516 58 35 0 + 517 58 35 1 + 518 58 34 0 + 519 58 34 1 + 520 56 34 1 + 521 56 34 0 + 522 56 35 1 + 523 56 35 0 + 524 57 34 1 + 525 57 34 0 + 526 57 35 1 + 527 57 35 0 + 544 51 34 0 + 545 51 34 1 + 546 51 35 0 + 547 51 35 1 + 548 50 34 0 + 549 50 34 1 + 550 50 35 0 + 551 50 35 1 + 552 48 35 1 + 553 48 35 0 + 554 48 34 1 + 555 48 34 0 + 556 49 35 1 + 557 49 35 0 + 558 49 34 1 + 559 49 34 0 + 560 52 34 0 + 561 52 34 1 + 562 52 35 0 + 563 52 35 1 + 564 53 34 0 + 565 53 34 1 + 566 53 35 0 + 567 53 35 1 + 568 55 35 1 + 569 55 35 0 + 570 55 34 1 + 571 55 34 0 + 572 54 35 1 + 573 54 35 0 + 574 54 34 1 + 575 54 34 0 + 576 60 35 0 + 577 60 35 1 + 578 60 34 0 + 579 60 34 1 + 580 61 35 0 + 581 61 35 1 + 582 61 34 0 + 583 61 34 1 + 584 63 34 1 + 585 63 34 0 + 586 63 35 1 + 587 63 35 0 + 588 62 34 1 + 589 62 34 0 + 590 62 35 1 + 591 62 35 0 + 640 59 37 0 + 641 59 37 1 + 642 59 36 0 + 643 59 36 1 + 644 58 37 0 + 645 58 37 1 + 646 58 36 0 + 647 58 36 1 + 648 56 36 1 + 649 56 36 0 + 650 56 37 1 + 651 56 37 0 + 652 57 36 1 + 653 57 36 0 + 654 57 37 1 + 655 57 37 0 + 672 51 36 0 + 673 51 36 1 + 674 51 37 0 + 675 51 37 1 + 676 50 36 0 + 677 50 36 1 + 678 50 37 0 + 679 50 37 1 + 680 48 37 1 + 681 48 37 0 + 682 48 36 1 + 683 48 36 0 + 684 49 37 1 + 685 49 37 0 + 686 49 36 1 + 687 49 36 0 + 688 52 36 0 + 689 52 36 1 + 690 52 37 0 + 691 52 37 1 + 692 53 36 0 + 693 53 36 1 + 694 53 37 0 + 695 53 37 1 + 696 55 37 1 + 697 55 37 0 + 698 55 36 1 + 699 55 36 0 + 700 54 37 1 + 701 54 37 0 + 702 54 36 1 + 703 54 36 0 + 704 60 37 0 + 705 60 37 1 + 706 60 36 0 + 707 60 36 1 + 708 61 37 0 + 709 61 37 1 + 710 61 36 0 + 711 61 36 1 + 712 63 36 1 + 713 63 36 0 + 714 63 37 1 + 715 63 37 0 + 716 62 36 1 + 717 62 36 0 + 718 62 37 1 + 719 62 37 0 + 768 59 39 0 + 769 59 39 1 + 770 59 38 0 + 771 59 38 1 + 772 58 39 0 + 773 58 39 1 + 774 58 38 0 + 775 58 38 1 + 776 56 38 1 + 777 56 38 0 + 778 56 39 1 + 779 56 39 0 + 780 57 38 1 + 781 57 38 0 + 782 57 39 1 + 783 57 39 0 + 800 51 38 0 + 801 51 38 1 + 802 51 39 0 + 803 51 39 1 + 804 50 38 0 + 805 50 38 1 + 806 50 39 0 + 807 50 39 1 + 808 48 39 1 + 809 48 39 0 + 810 48 38 1 + 811 48 38 0 + 812 49 39 1 + 813 49 39 0 + 814 49 38 1 + 815 49 38 0 + 816 52 38 0 + 817 52 38 1 + 818 52 39 0 + 819 52 39 1 + 820 53 38 0 + 821 53 38 1 + 822 53 39 0 + 823 53 39 1 + 824 55 39 1 + 825 55 39 0 + 826 55 38 1 + 827 55 38 0 + 828 54 39 1 + 829 54 39 0 + 830 54 38 1 + 831 54 38 0 + 832 60 39 0 + 833 60 39 1 + 834 60 38 0 + 835 60 38 1 + 836 61 39 0 + 837 61 39 1 + 838 61 38 0 + 839 61 38 1 + 840 63 38 1 + 841 63 38 0 + 842 63 39 1 + 843 63 39 0 + 844 62 38 1 + 845 62 38 0 + 846 62 39 1 + 847 62 39 0 + 896 59 41 0 + 897 59 41 1 + 898 59 40 0 + 899 59 40 1 + 900 58 41 0 + 901 58 41 1 + 902 58 40 0 + 903 58 40 1 + 904 56 40 1 + 905 56 40 0 + 906 56 41 1 + 907 56 41 0 + 908 57 40 1 + 909 57 40 0 + 910 57 41 1 + 911 57 41 0 + 928 51 40 0 + 929 51 40 1 + 930 51 41 0 + 931 51 41 1 + 932 50 40 0 + 933 50 40 1 + 934 50 41 0 + 935 50 41 1 + 936 48 41 1 + 937 48 41 0 + 938 48 40 1 + 939 48 40 0 + 940 49 41 1 + 941 49 41 0 + 942 49 40 1 + 943 49 40 0 + 944 52 40 0 + 945 52 40 1 + 946 52 41 0 + 947 52 41 1 + 948 53 40 0 + 949 53 40 1 + 950 53 41 0 + 951 53 41 1 + 952 55 41 1 + 953 55 41 0 + 954 55 40 1 + 955 55 40 0 + 956 54 41 1 + 957 54 41 0 + 958 54 40 1 + 959 54 40 0 + 960 60 41 0 + 961 60 41 1 + 962 60 40 0 + 963 60 40 1 + 964 61 41 0 + 965 61 41 1 + 966 61 40 0 + 967 61 40 1 + 968 63 40 1 + 969 63 40 0 + 970 63 41 1 + 971 63 41 0 + 972 62 40 1 + 973 62 40 0 + 974 62 41 1 + 975 62 41 0 +1024 59 43 0 +1025 59 43 1 +1026 59 42 0 +1027 59 42 1 +1028 58 43 0 +1029 58 43 1 +1030 58 42 0 +1031 58 42 1 +1032 56 42 1 +1033 56 42 0 +1034 56 43 1 +1035 56 43 0 +1036 57 42 1 +1037 57 42 0 +1038 57 43 1 +1039 57 43 0 +1056 51 42 0 +1057 51 42 1 +1058 51 43 0 +1059 51 43 1 +1060 50 42 0 +1061 50 42 1 +1062 50 43 0 +1063 50 43 1 +1064 48 43 1 +1065 48 43 0 +1066 48 42 1 +1067 48 42 0 +1068 49 43 1 +1069 49 43 0 +1070 49 42 1 +1071 49 42 0 +1072 52 42 0 +1073 52 42 1 +1074 52 43 0 +1075 52 43 1 +1076 53 42 0 +1077 53 42 1 +1078 53 43 0 +1079 53 43 1 +1080 55 43 1 +1081 55 43 0 +1082 55 42 1 +1083 55 42 0 +1084 54 43 1 +1085 54 43 0 +1086 54 42 1 +1087 54 42 0 +1088 60 43 0 +1089 60 43 1 +1090 60 42 0 +1091 60 42 1 +1092 61 43 0 +1093 61 43 1 +1094 61 42 0 +1095 61 42 1 +1096 63 42 1 +1097 63 42 0 +1098 63 43 1 +1099 63 43 0 +1100 62 42 1 +1101 62 42 0 +1102 62 43 1 +1103 62 43 0 +1152 59 45 0 +1153 59 45 1 +1154 59 44 0 +1155 59 44 1 +1156 58 45 0 +1157 58 45 1 +1158 58 44 0 +1159 58 44 1 +1160 56 44 1 +1161 56 44 0 +1162 56 45 1 +1163 56 45 0 +1164 57 44 1 +1165 57 44 0 +1166 57 45 1 +1167 57 45 0 +1184 51 44 0 +1185 51 44 1 +1186 51 45 0 +1187 51 45 1 +1188 50 44 0 +1189 50 44 1 +1190 50 45 0 +1191 50 45 1 +1192 48 45 1 +1193 48 45 0 +1194 48 44 1 +1195 48 44 0 +1196 49 45 1 +1197 49 45 0 +1198 49 44 1 +1199 49 44 0 +1200 52 44 0 +1201 52 44 1 +1202 52 45 0 +1203 52 45 1 +1204 53 44 0 +1205 53 44 1 +1206 53 45 0 +1207 53 45 1 +1208 55 45 1 +1209 55 45 0 +1210 55 44 1 +1211 55 44 0 +1212 54 45 1 +1213 54 45 0 +1214 54 44 1 +1215 54 44 0 +1216 60 45 0 +1217 60 45 1 +1218 60 44 0 +1219 60 44 1 +1220 61 45 0 +1221 61 45 1 +1222 61 44 0 +1223 61 44 1 +1224 63 44 1 +1225 63 44 0 +1226 63 45 1 +1227 63 45 0 +1228 62 44 1 +1229 62 44 0 +1230 62 45 1 +1231 62 45 0 +1280 59 47 0 +1281 59 47 1 +1282 59 46 0 +1283 59 46 1 +1284 58 47 0 +1285 58 47 1 +1286 58 46 0 +1287 58 46 1 +1288 56 46 1 +1289 56 46 0 +1290 56 47 1 +1291 56 47 0 +1292 57 46 1 +1293 57 46 0 +1294 57 47 1 +1295 57 47 0 +1312 51 46 0 +1313 51 46 1 +1314 51 47 0 +1315 51 47 1 +1316 50 46 0 +1317 50 46 1 +1318 50 47 0 +1319 50 47 1 +1320 48 47 1 +1321 48 47 0 +1322 48 46 1 +1323 48 46 0 +1324 49 47 1 +1325 49 47 0 +1326 49 46 1 +1327 49 46 0 +1328 52 46 0 +1329 52 46 1 +1330 52 47 0 +1331 52 47 1 +1332 53 46 0 +1333 53 46 1 +1334 53 47 0 +1335 53 47 1 +1336 55 47 1 +1337 55 47 0 +1338 55 46 1 +1339 55 46 0 +1340 54 47 1 +1341 54 47 0 +1342 54 46 1 +1343 54 46 0 +1344 60 47 0 +1345 60 47 1 +1346 60 46 0 +1347 60 46 1 +1348 61 47 0 +1349 61 47 1 +1350 61 46 0 +1351 61 46 1 +1352 63 46 1 +1353 63 46 0 +1354 63 47 1 +1355 63 47 0 +1356 62 46 1 +1357 62 46 0 +1358 62 47 1 +1359 62 47 0 +1408 59 49 0 +1409 59 49 1 +1410 59 48 0 +1411 59 48 1 +1412 58 49 0 +1413 58 49 1 +1414 58 48 0 +1415 58 48 1 +1416 56 48 1 +1417 56 48 0 +1418 56 49 1 +1419 56 49 0 +1420 57 48 1 +1421 57 48 0 +1422 57 49 1 +1423 57 49 0 +1440 51 48 0 +1441 51 48 1 +1442 51 49 0 +1443 51 49 1 +1444 50 48 0 +1445 50 48 1 +1446 50 49 0 +1447 50 49 1 +1448 48 49 1 +1449 48 49 0 +1450 48 48 1 +1451 48 48 0 +1452 49 49 1 +1453 49 49 0 +1454 49 48 1 +1455 49 48 0 +1456 52 48 0 +1457 52 48 1 +1458 52 49 0 +1459 52 49 1 +1460 53 48 0 +1461 53 48 1 +1462 53 49 0 +1463 53 49 1 +1464 55 49 1 +1465 55 49 0 +1466 55 48 1 +1467 55 48 0 +1468 54 49 1 +1469 54 49 0 +1470 54 48 1 +1471 54 48 0 +1472 60 49 0 +1473 60 49 1 +1474 60 48 0 +1475 60 48 1 +1476 61 49 0 +1477 61 49 1 +1478 61 48 0 +1479 61 48 1 +1480 63 48 1 +1481 63 48 0 +1482 63 49 1 +1483 63 49 0 +1484 62 48 1 +1485 62 48 0 +1486 62 49 1 +1487 62 49 0 +1536 59 51 0 +1537 59 51 1 +1538 59 50 0 +1539 59 50 1 +1540 58 51 0 +1541 58 51 1 +1542 58 50 0 +1543 58 50 1 +1544 56 50 1 +1545 56 50 0 +1546 56 51 1 +1547 56 51 0 +1548 57 50 1 +1549 57 50 0 +1550 57 51 1 +1551 57 51 0 +1568 51 50 0 +1569 51 50 1 +1570 51 51 0 +1571 51 51 1 +1572 50 50 0 +1573 50 50 1 +1574 50 51 0 +1575 50 51 1 +1576 48 51 1 +1577 48 51 0 +1578 48 50 1 +1579 48 50 0 +1580 49 51 1 +1581 49 51 0 +1582 49 50 1 +1583 49 50 0 +1584 52 50 0 +1585 52 50 1 +1586 52 51 0 +1587 52 51 1 +1588 53 50 0 +1589 53 50 1 +1590 53 51 0 +1591 53 51 1 +1592 55 51 1 +1593 55 51 0 +1594 55 50 1 +1595 55 50 0 +1596 54 51 1 +1597 54 51 0 +1598 54 50 1 +1599 54 50 0 +1600 60 51 0 +1601 60 51 1 +1602 60 50 0 +1603 60 50 1 +1604 61 51 0 +1605 61 51 1 +1606 61 50 0 +1607 61 50 1 +1608 63 50 1 +1609 63 50 0 +1610 63 51 1 +1611 63 51 0 +1612 62 50 1 +1613 62 50 0 +1614 62 51 1 +1615 62 51 0 +1664 59 53 0 +1665 59 53 1 +1666 59 52 0 +1667 59 52 1 +1668 58 53 0 +1669 58 53 1 +1670 58 52 0 +1671 58 52 1 +1672 56 52 1 +1673 56 52 0 +1674 56 53 1 +1675 56 53 0 +1676 57 52 1 +1677 57 52 0 +1678 57 53 1 +1679 57 53 0 +1696 51 52 0 +1697 51 52 1 +1698 51 53 0 +1699 51 53 1 +1700 50 52 0 +1701 50 52 1 +1702 50 53 0 +1703 50 53 1 +1704 48 53 1 +1705 48 53 0 +1706 48 52 1 +1707 48 52 0 +1708 49 53 1 +1709 49 53 0 +1710 49 52 1 +1711 49 52 0 +1712 52 52 0 +1713 52 52 1 +1714 52 53 0 +1715 52 53 1 +1716 53 52 0 +1717 53 52 1 +1718 53 53 0 +1719 53 53 1 +1720 55 53 1 +1721 55 53 0 +1722 55 52 1 +1723 55 52 0 +1724 54 53 1 +1725 54 53 0 +1726 54 52 1 +1727 54 52 0 +1728 60 53 0 +1729 60 53 1 +1730 60 52 0 +1731 60 52 1 +1732 61 53 0 +1733 61 53 1 +1734 61 52 0 +1735 61 52 1 +1736 63 52 1 +1737 63 52 0 +1738 63 53 1 +1739 63 53 0 +1740 62 52 1 +1741 62 52 0 +1742 62 53 1 +1743 62 53 0 +1792 59 55 0 +1793 59 55 1 +1794 59 54 0 +1795 59 54 1 +1796 58 55 0 +1797 58 55 1 +1798 58 54 0 +1799 58 54 1 +1800 56 54 1 +1801 56 54 0 +1802 56 55 1 +1803 56 55 0 +1804 57 54 1 +1805 57 54 0 +1806 57 55 1 +1807 57 55 0 +1824 51 54 0 +1825 51 54 1 +1826 51 55 0 +1827 51 55 1 +1828 50 54 0 +1829 50 54 1 +1830 50 55 0 +1831 50 55 1 +1832 48 55 1 +1833 48 55 0 +1834 48 54 1 +1835 48 54 0 +1836 49 55 1 +1837 49 55 0 +1838 49 54 1 +1839 49 54 0 +1840 52 54 0 +1841 52 54 1 +1842 52 55 0 +1843 52 55 1 +1844 53 54 0 +1845 53 54 1 +1846 53 55 0 +1847 53 55 1 +1848 55 55 1 +1849 55 55 0 +1850 55 54 1 +1851 55 54 0 +1852 54 55 1 +1853 54 55 0 +1854 54 54 1 +1855 54 54 0 +1856 60 55 0 +1857 60 55 1 +1858 60 54 0 +1859 60 54 1 +1860 61 55 0 +1861 61 55 1 +1862 61 54 0 +1863 61 54 1 +1864 63 54 1 +1865 63 54 0 +1866 63 55 1 +1867 63 55 0 +1868 62 54 1 +1869 62 54 0 +1870 62 55 1 +1871 62 55 0 +2176 59 27 0 +2177 59 27 1 +2178 59 26 0 +2179 59 26 1 +2180 58 27 0 +2181 58 27 1 +2182 58 26 0 +2183 58 26 1 +2184 56 26 1 +2185 56 26 0 +2186 56 27 1 +2187 56 27 0 +2188 57 26 1 +2189 57 26 0 +2190 57 27 1 +2191 57 27 0 +2208 51 26 0 +2209 51 26 1 +2210 51 27 0 +2211 51 27 1 +2212 50 26 0 +2213 50 26 1 +2214 50 27 0 +2215 50 27 1 +2216 48 27 1 +2217 48 27 0 +2218 48 26 1 +2219 48 26 0 +2220 49 27 1 +2221 49 27 0 +2222 49 26 1 +2223 49 26 0 +2224 52 26 0 +2225 52 26 1 +2226 52 27 0 +2227 52 27 1 +2228 53 26 0 +2229 53 26 1 +2230 53 27 0 +2231 53 27 1 +2232 55 27 1 +2233 55 27 0 +2234 55 26 1 +2235 55 26 0 +2236 54 27 1 +2237 54 27 0 +2238 54 26 1 +2239 54 26 0 +2240 60 27 0 +2241 60 27 1 +2242 60 26 0 +2243 60 26 1 +2244 61 27 0 +2245 61 27 1 +2246 61 26 0 +2247 61 26 1 +2248 63 26 1 +2249 63 26 0 +2250 63 27 1 +2251 63 27 0 +2252 62 26 1 +2253 62 26 0 +2254 62 27 1 +2255 62 27 0 +2304 59 25 0 +2305 59 25 1 +2306 59 24 0 +2307 59 24 1 +2308 58 25 0 +2309 58 25 1 +2310 58 24 0 +2311 58 24 1 +2312 56 24 1 +2313 56 24 0 +2314 56 25 1 +2315 56 25 0 +2316 57 24 1 +2317 57 24 0 +2318 57 25 1 +2319 57 25 0 +2336 51 24 0 +2337 51 24 1 +2338 51 25 0 +2339 51 25 1 +2340 50 24 0 +2341 50 24 1 +2342 50 25 0 +2343 50 25 1 +2344 48 25 1 +2345 48 25 0 +2346 48 24 1 +2347 48 24 0 +2348 49 25 1 +2349 49 25 0 +2350 49 24 1 +2351 49 24 0 +2352 52 24 0 +2353 52 24 1 +2354 52 25 0 +2355 52 25 1 +2356 53 24 0 +2357 53 24 1 +2358 53 25 0 +2359 53 25 1 +2360 55 25 1 +2361 55 25 0 +2362 55 24 1 +2363 55 24 0 +2364 54 25 1 +2365 54 25 0 +2366 54 24 1 +2367 54 24 0 +2368 60 25 0 +2369 60 25 1 +2370 60 24 0 +2371 60 24 1 +2372 61 25 0 +2373 61 25 1 +2374 61 24 0 +2375 61 24 1 +2376 63 24 1 +2377 63 24 0 +2378 63 25 1 +2379 63 25 0 +2380 62 24 1 +2381 62 24 0 +2382 62 25 1 +2383 62 25 0 +2432 59 23 0 +2433 59 23 1 +2434 59 22 0 +2435 59 22 1 +2436 58 23 0 +2437 58 23 1 +2438 58 22 0 +2439 58 22 1 +2440 56 22 1 +2441 56 22 0 +2442 56 23 1 +2443 56 23 0 +2444 57 22 1 +2445 57 22 0 +2446 57 23 1 +2447 57 23 0 +2464 51 22 0 +2465 51 22 1 +2466 51 23 0 +2467 51 23 1 +2468 50 22 0 +2469 50 22 1 +2470 50 23 0 +2471 50 23 1 +2472 48 23 1 +2473 48 23 0 +2474 48 22 1 +2475 48 22 0 +2476 49 23 1 +2477 49 23 0 +2478 49 22 1 +2479 49 22 0 +2480 52 22 0 +2481 52 22 1 +2482 52 23 0 +2483 52 23 1 +2484 53 22 0 +2485 53 22 1 +2486 53 23 0 +2487 53 23 1 +2488 55 23 1 +2489 55 23 0 +2490 55 22 1 +2491 55 22 0 +2492 54 23 1 +2493 54 23 0 +2494 54 22 1 +2495 54 22 0 +2496 60 23 0 +2497 60 23 1 +2498 60 22 0 +2499 60 22 1 +2500 61 23 0 +2501 61 23 1 +2502 61 22 0 +2503 61 22 1 +2504 63 22 1 +2505 63 22 0 +2506 63 23 1 +2507 63 23 0 +2508 62 22 1 +2509 62 22 0 +2510 62 23 1 +2511 62 23 0 +2560 59 21 0 +2561 59 21 1 +2562 59 20 0 +2563 59 20 1 +2564 58 21 0 +2565 58 21 1 +2566 58 20 0 +2567 58 20 1 +2568 56 20 1 +2569 56 20 0 +2570 56 21 1 +2571 56 21 0 +2572 57 20 1 +2573 57 20 0 +2574 57 21 1 +2575 57 21 0 +2592 51 20 0 +2593 51 20 1 +2594 51 21 0 +2595 51 21 1 +2596 50 20 0 +2597 50 20 1 +2598 50 21 0 +2599 50 21 1 +2600 48 21 1 +2601 48 21 0 +2602 48 20 1 +2603 48 20 0 +2604 49 21 1 +2605 49 21 0 +2606 49 20 1 +2607 49 20 0 +2608 52 20 0 +2609 52 20 1 +2610 52 21 0 +2611 52 21 1 +2612 53 20 0 +2613 53 20 1 +2614 53 21 0 +2615 53 21 1 +2616 55 21 1 +2617 55 21 0 +2618 55 20 1 +2619 55 20 0 +2620 54 21 1 +2621 54 21 0 +2622 54 20 1 +2623 54 20 0 +2624 60 21 0 +2625 60 21 1 +2626 60 20 0 +2627 60 20 1 +2628 61 21 0 +2629 61 21 1 +2630 61 20 0 +2631 61 20 1 +2632 63 20 1 +2633 63 20 0 +2634 63 21 1 +2635 63 21 0 +2636 62 20 1 +2637 62 20 0 +2638 62 21 1 +2639 62 21 0 +2688 59 19 0 +2689 59 19 1 +2690 59 18 0 +2691 59 18 1 +2692 58 19 0 +2693 58 19 1 +2694 58 18 0 +2695 58 18 1 +2696 56 18 1 +2697 56 18 0 +2698 56 19 1 +2699 56 19 0 +2700 57 18 1 +2701 57 18 0 +2702 57 19 1 +2703 57 19 0 +2720 51 18 0 +2721 51 18 1 +2722 51 19 0 +2723 51 19 1 +2724 50 18 0 +2725 50 18 1 +2726 50 19 0 +2727 50 19 1 +2728 48 19 1 +2729 48 19 0 +2730 48 18 1 +2731 48 18 0 +2732 49 19 1 +2733 49 19 0 +2734 49 18 1 +2735 49 18 0 +2736 52 18 0 +2737 52 18 1 +2738 52 19 0 +2739 52 19 1 +2740 53 18 0 +2741 53 18 1 +2742 53 19 0 +2743 53 19 1 +2744 55 19 1 +2745 55 19 0 +2746 55 18 1 +2747 55 18 0 +2748 54 19 1 +2749 54 19 0 +2750 54 18 1 +2751 54 18 0 +2752 60 19 0 +2753 60 19 1 +2754 60 18 0 +2755 60 18 1 +2756 61 19 0 +2757 61 19 1 +2758 61 18 0 +2759 61 18 1 +2760 63 18 1 +2761 63 18 0 +2762 63 19 1 +2763 63 19 0 +2764 62 18 1 +2765 62 18 0 +2766 62 19 1 +2767 62 19 0 +2816 59 17 0 +2817 59 17 1 +2818 59 16 0 +2819 59 16 1 +2820 58 17 0 +2821 58 17 1 +2822 58 16 0 +2823 58 16 1 +2824 56 16 1 +2825 56 16 0 +2826 56 17 1 +2827 56 17 0 +2828 57 16 1 +2829 57 16 0 +2830 57 17 1 +2831 57 17 0 +2848 51 16 0 +2849 51 16 1 +2850 51 17 0 +2851 51 17 1 +2852 50 16 0 +2853 50 16 1 +2854 50 17 0 +2855 50 17 1 +2856 48 17 1 +2857 48 17 0 +2858 48 16 1 +2859 48 16 0 +2860 49 17 1 +2861 49 17 0 +2862 49 16 1 +2863 49 16 0 +2864 52 16 0 +2865 52 16 1 +2866 52 17 0 +2867 52 17 1 +2868 53 16 0 +2869 53 16 1 +2870 53 17 0 +2871 53 17 1 +2872 55 17 1 +2873 55 17 0 +2874 55 16 1 +2875 55 16 0 +2876 54 17 1 +2877 54 17 0 +2878 54 16 1 +2879 54 16 0 +2880 60 17 0 +2881 60 17 1 +2882 60 16 0 +2883 60 16 1 +2884 61 17 0 +2885 61 17 1 +2886 61 16 0 +2887 61 16 1 +2888 63 16 1 +2889 63 16 0 +2890 63 17 1 +2891 63 17 0 +2892 62 16 1 +2893 62 16 0 +2894 62 17 1 +2895 62 17 0 +2944 59 15 0 +2945 59 15 1 +2946 59 14 0 +2947 59 14 1 +2948 58 15 0 +2949 58 15 1 +2950 58 14 0 +2951 58 14 1 +2952 56 14 1 +2953 56 14 0 +2954 56 15 1 +2955 56 15 0 +2956 57 14 1 +2957 57 14 0 +2958 57 15 1 +2959 57 15 0 +2976 51 14 0 +2977 51 14 1 +2978 51 15 0 +2979 51 15 1 +2980 50 14 0 +2981 50 14 1 +2982 50 15 0 +2983 50 15 1 +2984 48 15 1 +2985 48 15 0 +2986 48 14 1 +2987 48 14 0 +2988 49 15 1 +2989 49 15 0 +2990 49 14 1 +2991 49 14 0 +2992 52 14 0 +2993 52 14 1 +2994 52 15 0 +2995 52 15 1 +2996 53 14 0 +2997 53 14 1 +2998 53 15 0 +2999 53 15 1 +3000 55 15 1 +3001 55 15 0 +3002 55 14 1 +3003 55 14 0 +3004 54 15 1 +3005 54 15 0 +3006 54 14 1 +3007 54 14 0 +3008 60 15 0 +3009 60 15 1 +3010 60 14 0 +3011 60 14 1 +3012 61 15 0 +3013 61 15 1 +3014 61 14 0 +3015 61 14 1 +3016 63 14 1 +3017 63 14 0 +3018 63 15 1 +3019 63 15 0 +3020 62 14 1 +3021 62 14 0 +3022 62 15 1 +3023 62 15 0 +3072 59 13 0 +3073 59 13 1 +3074 59 12 0 +3075 59 12 1 +3076 58 13 0 +3077 58 13 1 +3078 58 12 0 +3079 58 12 1 +3080 56 12 1 +3081 56 12 0 +3082 56 13 1 +3083 56 13 0 +3084 57 12 1 +3085 57 12 0 +3086 57 13 1 +3087 57 13 0 +3104 51 12 0 +3105 51 12 1 +3106 51 13 0 +3107 51 13 1 +3108 50 12 0 +3109 50 12 1 +3110 50 13 0 +3111 50 13 1 +3112 48 13 1 +3113 48 13 0 +3114 48 12 1 +3115 48 12 0 +3116 49 13 1 +3117 49 13 0 +3118 49 12 1 +3119 49 12 0 +3120 52 12 0 +3121 52 12 1 +3122 52 13 0 +3123 52 13 1 +3124 53 12 0 +3125 53 12 1 +3126 53 13 0 +3127 53 13 1 +3128 55 13 1 +3129 55 13 0 +3130 55 12 1 +3131 55 12 0 +3132 54 13 1 +3133 54 13 0 +3134 54 12 1 +3135 54 12 0 +3136 60 13 0 +3137 60 13 1 +3138 60 12 0 +3139 60 12 1 +3140 61 13 0 +3141 61 13 1 +3142 61 12 0 +3143 61 12 1 +3144 63 12 1 +3145 63 12 0 +3146 63 13 1 +3147 63 13 0 +3148 62 12 1 +3149 62 12 0 +3150 62 13 1 +3151 62 13 0 +3200 59 11 0 +3201 59 11 1 +3202 59 10 0 +3203 59 10 1 +3204 58 11 0 +3205 58 11 1 +3206 58 10 0 +3207 58 10 1 +3208 56 10 1 +3209 56 10 0 +3210 56 11 1 +3211 56 11 0 +3212 57 10 1 +3213 57 10 0 +3214 57 11 1 +3215 57 11 0 +3232 51 10 0 +3233 51 10 1 +3234 51 11 0 +3235 51 11 1 +3236 50 10 0 +3237 50 10 1 +3238 50 11 0 +3239 50 11 1 +3240 48 11 1 +3241 48 11 0 +3242 48 10 1 +3243 48 10 0 +3244 49 11 1 +3245 49 11 0 +3246 49 10 1 +3247 49 10 0 +3248 52 10 0 +3249 52 10 1 +3250 52 11 0 +3251 52 11 1 +3252 53 10 0 +3253 53 10 1 +3254 53 11 0 +3255 53 11 1 +3256 55 11 1 +3257 55 11 0 +3258 55 10 1 +3259 55 10 0 +3260 54 11 1 +3261 54 11 0 +3262 54 10 1 +3263 54 10 0 +3264 60 11 0 +3265 60 11 1 +3266 60 10 0 +3267 60 10 1 +3268 61 11 0 +3269 61 11 1 +3270 61 10 0 +3271 61 10 1 +3272 63 10 1 +3273 63 10 0 +3274 63 11 1 +3275 63 11 0 +3276 62 10 1 +3277 62 10 0 +3278 62 11 1 +3279 62 11 0 +3328 59 9 0 +3329 59 9 1 +3330 59 8 0 +3331 59 8 1 +3332 58 9 0 +3333 58 9 1 +3334 58 8 0 +3335 58 8 1 +3336 56 8 1 +3337 56 8 0 +3338 56 9 1 +3339 56 9 0 +3340 57 8 1 +3341 57 8 0 +3342 57 9 1 +3343 57 9 0 +3360 51 8 0 +3361 51 8 1 +3362 51 9 0 +3363 51 9 1 +3364 50 8 0 +3365 50 8 1 +3366 50 9 0 +3367 50 9 1 +3368 48 9 1 +3369 48 9 0 +3370 48 8 1 +3371 48 8 0 +3372 49 9 1 +3373 49 9 0 +3374 49 8 1 +3375 49 8 0 +3376 52 8 0 +3377 52 8 1 +3378 52 9 0 +3379 52 9 1 +3380 53 8 0 +3381 53 8 1 +3382 53 9 0 +3383 53 9 1 +3384 55 9 1 +3385 55 9 0 +3386 55 8 1 +3387 55 8 0 +3388 54 9 1 +3389 54 9 0 +3390 54 8 1 +3391 54 8 0 +3392 60 9 0 +3393 60 9 1 +3394 60 8 0 +3395 60 8 1 +3396 61 9 0 +3397 61 9 1 +3398 61 8 0 +3399 61 8 1 +3400 63 8 1 +3401 63 8 0 +3402 63 9 1 +3403 63 9 0 +3404 62 8 1 +3405 62 8 0 +3406 62 9 1 +3407 62 9 0 +3456 59 7 0 +3457 59 7 1 +3458 59 6 0 +3459 59 6 1 +3460 58 7 0 +3461 58 7 1 +3462 58 6 0 +3463 58 6 1 +3464 56 6 1 +3465 56 6 0 +3466 56 7 1 +3467 56 7 0 +3468 57 6 1 +3469 57 6 0 +3470 57 7 1 +3471 57 7 0 +3488 51 6 0 +3489 51 6 1 +3490 51 7 0 +3491 51 7 1 +3492 50 6 0 +3493 50 6 1 +3494 50 7 0 +3495 50 7 1 +3496 48 7 1 +3497 48 7 0 +3498 48 6 1 +3499 48 6 0 +3500 49 7 1 +3501 49 7 0 +3502 49 6 1 +3503 49 6 0 +3504 52 6 0 +3505 52 6 1 +3506 52 7 0 +3507 52 7 1 +3508 53 6 0 +3509 53 6 1 +3510 53 7 0 +3511 53 7 1 +3512 55 7 1 +3513 55 7 0 +3514 55 6 1 +3515 55 6 0 +3516 54 7 1 +3517 54 7 0 +3518 54 6 1 +3519 54 6 0 +3520 60 7 0 +3521 60 7 1 +3522 60 6 0 +3523 60 6 1 +3524 61 7 0 +3525 61 7 1 +3526 61 6 0 +3527 61 6 1 +3528 63 6 1 +3529 63 6 0 +3530 63 7 1 +3531 63 7 0 +3532 62 6 1 +3533 62 6 0 +3534 62 7 1 +3535 62 7 0 +3584 59 5 0 +3585 59 5 1 +3586 59 4 0 +3587 59 4 1 +3588 58 5 0 +3589 58 5 1 +3590 58 4 0 +3591 58 4 1 +3592 56 4 1 +3593 56 4 0 +3594 56 5 1 +3595 56 5 0 +3596 57 4 1 +3597 57 4 0 +3598 57 5 1 +3599 57 5 0 +3616 51 4 0 +3617 51 4 1 +3618 51 5 0 +3619 51 5 1 +3620 50 4 0 +3621 50 4 1 +3622 50 5 0 +3623 50 5 1 +3624 48 5 1 +3625 48 5 0 +3626 48 4 1 +3627 48 4 0 +3628 49 5 1 +3629 49 5 0 +3630 49 4 1 +3631 49 4 0 +3632 52 4 0 +3633 52 4 1 +3634 52 5 0 +3635 52 5 1 +3636 53 4 0 +3637 53 4 1 +3638 53 5 0 +3639 53 5 1 +3640 55 5 1 +3641 55 5 0 +3642 55 4 1 +3643 55 4 0 +3644 54 5 1 +3645 54 5 0 +3646 54 4 1 +3647 54 4 0 +3648 60 5 0 +3649 60 5 1 +3650 60 4 0 +3651 60 4 1 +3652 61 5 0 +3653 61 5 1 +3654 61 4 0 +3655 61 4 1 +3656 63 4 1 +3657 63 4 0 +3658 63 5 1 +3659 63 5 0 +3660 62 4 1 +3661 62 4 0 +3662 62 5 1 +3663 62 5 0 +3712 59 3 0 +3713 59 3 1 +3714 59 2 0 +3715 59 2 1 +3716 58 3 0 +3717 58 3 1 +3718 58 2 0 +3719 58 2 1 +3720 56 2 1 +3721 56 2 0 +3722 56 3 1 +3723 56 3 0 +3724 57 2 1 +3725 57 2 0 +3726 57 3 1 +3727 57 3 0 +3744 51 2 0 +3745 51 2 1 +3746 51 3 0 +3747 51 3 1 +3748 50 2 0 +3749 50 2 1 +3750 50 3 0 +3751 50 3 1 +3752 48 3 1 +3753 48 3 0 +3754 48 2 1 +3755 48 2 0 +3756 49 3 1 +3757 49 3 0 +3758 49 2 1 +3759 49 2 0 +3760 52 2 0 +3761 52 2 1 +3762 52 3 0 +3763 52 3 1 +3764 53 2 0 +3765 53 2 1 +3766 53 3 0 +3767 53 3 1 +3768 55 3 1 +3769 55 3 0 +3770 55 2 1 +3771 55 2 0 +3772 54 3 1 +3773 54 3 0 +3774 54 2 1 +3775 54 2 0 +3776 60 3 0 +3777 60 3 1 +3778 60 2 0 +3779 60 2 1 +3780 61 3 0 +3781 61 3 1 +3782 61 2 0 +3783 61 2 1 +3784 63 2 1 +3785 63 2 0 +3786 63 3 1 +3787 63 3 0 +3788 62 2 1 +3789 62 2 0 +3790 62 3 1 +3791 62 3 0 +3840 59 1 0 +3841 59 1 1 +3842 59 0 0 +3843 59 0 1 +3844 58 1 0 +3845 58 1 1 +3846 58 0 0 +3847 58 0 1 +3848 56 0 1 +3849 56 0 0 +3850 56 1 1 +3851 56 1 0 +3852 57 0 1 +3853 57 0 0 +3854 57 1 1 +3855 57 1 0 +3872 51 0 0 +3873 51 0 1 +3874 51 1 0 +3875 51 1 1 +3876 50 0 0 +3877 50 0 1 +3878 50 1 0 +3879 50 1 1 +3880 48 1 1 +3881 48 1 0 +3882 48 0 1 +3883 48 0 0 +3884 49 1 1 +3885 49 1 0 +3886 49 0 1 +3887 49 0 0 +3888 52 0 0 +3889 52 0 1 +3890 52 1 0 +3891 52 1 1 +3892 53 0 0 +3893 53 0 1 +3894 53 1 0 +3895 53 1 1 +3896 55 1 1 +3897 55 1 0 +3898 55 0 1 +3899 55 0 0 +3900 54 1 1 +3901 54 1 0 +3902 54 0 1 +3903 54 0 0 +3904 60 1 0 +3905 60 1 1 +3906 60 0 0 +3907 60 0 1 +3908 61 1 0 +3909 61 1 1 +3910 61 0 0 +3911 61 0 1 +3912 63 0 1 +3913 63 0 0 +3914 63 1 1 +3915 63 1 0 +3916 62 0 1 +3917 62 0 0 +3918 62 1 1 +3919 62 1 0 diff --git a/Detectors/PHOS/base/files/Mod1RCU0.data b/Detectors/PHOS/base/files/Mod1RCU0.data new file mode 100644 index 0000000000000..4739df6e3e61e --- /dev/null +++ b/Detectors/PHOS/base/files/Mod1RCU0.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 0 0 2 + 1 0 1 2 + 2 0 2 2 + 3 0 3 2 + 4 0 4 2 + 5 0 5 2 + 6 0 6 2 + 7 0 7 2 + 8 0 8 2 + 9 0 9 2 + 10 0 10 2 + 11 0 11 2 + 12 0 12 2 + 13 0 13 2 + 14 0 14 2 + 15 0 15 2 + 16 0 16 2 + 17 0 17 2 + 18 0 18 2 + 19 0 19 2 + 20 0 20 2 + 21 0 21 2 + 22 0 22 2 + 23 0 23 2 + 24 0 24 2 + 25 0 25 2 + 26 0 26 2 + 27 0 27 2 + 28 0 28 2 + 29 0 29 2 + 30 0 30 2 + 31 0 31 2 + 32 0 32 2 + 33 0 33 2 + 34 0 34 2 + 35 0 35 2 + 36 0 36 2 + 37 0 37 2 + 38 0 38 2 + 39 0 39 2 + 40 0 40 2 + 41 0 41 2 + 42 0 42 2 + 43 0 43 2 + 44 0 44 2 + 45 0 45 2 + 46 0 46 2 + 47 0 47 2 + 48 0 48 2 + 49 0 49 2 + 50 0 50 2 + 51 0 51 2 + 52 0 52 2 + 53 0 53 2 + 54 0 54 2 + 55 0 55 2 + 56 0 56 2 + 57 0 57 2 + 58 0 58 2 + 59 0 59 2 + 60 0 60 2 + 61 0 61 2 + 62 0 62 2 + 63 0 63 2 + 64 0 64 2 + 65 0 65 2 + 66 0 66 2 + 67 0 67 2 + 68 0 68 2 + 69 0 69 2 + 70 0 70 2 + 71 0 71 2 + 72 0 72 2 + 73 0 73 2 + 74 0 74 2 + 75 0 75 2 + 76 0 76 2 + 77 0 77 2 + 78 0 78 2 + 79 0 79 2 + 80 0 80 2 + 81 0 81 2 + 82 0 82 2 + 83 0 83 2 + 84 0 84 2 + 85 0 85 2 + 86 0 86 2 + 87 0 87 2 + 88 0 88 2 + 89 0 89 2 + 90 0 90 2 + 91 0 91 2 + 92 0 92 2 + 93 0 93 2 + 94 0 94 2 + 95 0 95 2 + 96 0 96 2 + 97 0 97 2 + 98 0 98 2 + 99 0 99 2 + 100 0 100 2 + 101 0 101 2 + 102 0 102 2 + 103 0 103 2 + 104 0 104 2 + 105 0 105 2 + 106 0 106 2 + 107 0 107 2 + 108 0 108 2 + 109 0 109 2 + 110 0 110 2 + 111 0 111 2 + 112 0 112 2 + 113 0 113 2 + 114 0 114 2 + 115 0 115 2 + 116 0 116 2 + 117 0 117 2 + 118 0 118 2 + 119 0 119 2 + 120 0 120 2 + 121 0 121 2 + 122 0 122 2 + 123 0 123 2 + 124 0 124 2 + 125 0 125 2 + 126 0 126 2 + 127 0 127 2 +2048 0 2048 2 +2049 0 2049 2 +2050 0 2050 2 +2051 0 2051 2 +2052 0 2052 2 +2053 0 2053 2 +2054 0 2054 2 +2055 0 2055 2 +2056 0 2056 2 +2057 0 2057 2 +2058 0 2058 2 +2059 0 2059 2 +2060 0 2060 2 +2061 0 2061 2 +2062 0 2062 2 +2063 0 2063 2 +2064 0 2064 2 +2065 0 2065 2 +2066 0 2066 2 +2067 0 2067 2 +2068 0 2068 2 +2069 0 2069 2 +2070 0 2070 2 +2071 0 2071 2 +2072 0 2072 2 +2073 0 2073 2 +2074 0 2074 2 +2075 0 2075 2 +2076 0 2076 2 +2077 0 2077 2 +2078 0 2078 2 +2079 0 2079 2 +2080 0 2080 2 +2081 0 2081 2 +2082 0 2082 2 +2083 0 2083 2 +2084 0 2084 2 +2085 0 2085 2 +2086 0 2086 2 +2087 0 2087 2 +2088 0 2088 2 +2089 0 2089 2 +2090 0 2090 2 +2091 0 2091 2 +2092 0 2092 2 +2093 0 2093 2 +2094 0 2094 2 +2095 0 2095 2 +2096 0 2096 2 +2097 0 2097 2 +2098 0 2098 2 +2099 0 2099 2 +2100 0 2100 2 +2101 0 2101 2 +2102 0 2102 2 +2103 0 2103 2 +2104 0 2104 2 +2105 0 2105 2 +2106 0 2106 2 +2107 0 2107 2 +2108 0 2108 2 +2109 0 2109 2 +2110 0 2110 2 +2111 0 2111 2 +2112 0 2112 2 +2113 0 2113 2 +2114 0 2114 2 +2115 0 2115 2 +2116 0 2116 2 +2117 0 2117 2 +2118 0 2118 2 +2119 0 2119 2 +2120 0 2120 2 +2121 0 2121 2 +2122 0 2122 2 +2123 0 2123 2 +2124 0 2124 2 +2125 0 2125 2 +2126 0 2126 2 +2127 0 2127 2 +2128 0 2128 2 +2129 0 2129 2 +2130 0 2130 2 +2131 0 2131 2 +2132 0 2132 2 +2133 0 2133 2 +2134 0 2134 2 +2135 0 2135 2 +2136 0 2136 2 +2137 0 2137 2 +2138 0 2138 2 +2139 0 2139 2 +2140 0 2140 2 +2141 0 2141 2 +2142 0 2142 2 +2143 0 2143 2 +2144 0 2144 2 +2145 0 2145 2 +2146 0 2146 2 +2147 0 2147 2 +2148 0 2148 2 +2149 0 2149 2 +2150 0 2150 2 +2151 0 2151 2 +2152 0 2152 2 +2153 0 2153 2 +2154 0 2154 2 +2155 0 2155 2 +2156 0 2156 2 +2157 0 2157 2 +2158 0 2158 2 +2159 0 2159 2 +2160 0 2160 2 +2161 0 2161 2 +2162 0 2162 2 +2163 0 2163 2 +2164 0 2164 2 +2165 0 2165 2 +2166 0 2166 2 +2167 0 2167 2 +2168 0 2168 2 +2169 0 2169 2 +2170 0 2170 2 +2171 0 2171 2 +2172 0 2172 2 +2173 0 2173 2 +2174 0 2174 2 +2175 0 2175 2 + 128 11 29 0 + 129 11 29 1 + 130 11 28 0 + 131 11 28 1 + 132 10 29 0 + 133 10 29 1 + 134 10 28 0 + 135 10 28 1 + 136 8 28 1 + 137 8 28 0 + 138 8 29 1 + 139 8 29 0 + 140 9 28 1 + 141 9 28 0 + 142 9 29 1 + 143 9 29 0 + 160 3 28 0 + 161 3 28 1 + 162 3 29 0 + 163 3 29 1 + 164 2 28 0 + 165 2 28 1 + 166 2 29 0 + 167 2 29 1 + 168 0 29 1 + 169 0 29 0 + 170 0 28 1 + 171 0 28 0 + 172 1 29 1 + 173 1 29 0 + 174 1 28 1 + 175 1 28 0 + 176 4 28 0 + 177 4 28 1 + 178 4 29 0 + 179 4 29 1 + 180 5 28 0 + 181 5 28 1 + 182 5 29 0 + 183 5 29 1 + 184 7 29 1 + 185 7 29 0 + 186 7 28 1 + 187 7 28 0 + 188 6 29 1 + 189 6 29 0 + 190 6 28 1 + 191 6 28 0 + 192 12 29 0 + 193 12 29 1 + 194 12 28 0 + 195 12 28 1 + 196 13 29 0 + 197 13 29 1 + 198 13 28 0 + 199 13 28 1 + 200 15 28 1 + 201 15 28 0 + 202 15 29 1 + 203 15 29 0 + 204 14 28 1 + 205 14 28 0 + 206 14 29 1 + 207 14 29 0 + 256 11 31 0 + 257 11 31 1 + 258 11 30 0 + 259 11 30 1 + 260 10 31 0 + 261 10 31 1 + 262 10 30 0 + 263 10 30 1 + 264 8 30 1 + 265 8 30 0 + 266 8 31 1 + 267 8 31 0 + 268 9 30 1 + 269 9 30 0 + 270 9 31 1 + 271 9 31 0 + 288 3 30 0 + 289 3 30 1 + 290 3 31 0 + 291 3 31 1 + 292 2 30 0 + 293 2 30 1 + 294 2 31 0 + 295 2 31 1 + 296 0 31 1 + 297 0 31 0 + 298 0 30 1 + 299 0 30 0 + 300 1 31 1 + 301 1 31 0 + 302 1 30 1 + 303 1 30 0 + 304 4 30 0 + 305 4 30 1 + 306 4 31 0 + 307 4 31 1 + 308 5 30 0 + 309 5 30 1 + 310 5 31 0 + 311 5 31 1 + 312 7 31 1 + 313 7 31 0 + 314 7 30 1 + 315 7 30 0 + 316 6 31 1 + 317 6 31 0 + 318 6 30 1 + 319 6 30 0 + 320 12 31 0 + 321 12 31 1 + 322 12 30 0 + 323 12 30 1 + 324 13 31 0 + 325 13 31 1 + 326 13 30 0 + 327 13 30 1 + 328 15 30 1 + 329 15 30 0 + 330 15 31 1 + 331 15 31 0 + 332 14 30 1 + 333 14 30 0 + 334 14 31 1 + 335 14 31 0 + 384 11 33 0 + 385 11 33 1 + 386 11 32 0 + 387 11 32 1 + 388 10 33 0 + 389 10 33 1 + 390 10 32 0 + 391 10 32 1 + 392 8 32 1 + 393 8 32 0 + 394 8 33 1 + 395 8 33 0 + 396 9 32 1 + 397 9 32 0 + 398 9 33 1 + 399 9 33 0 + 416 3 32 0 + 417 3 32 1 + 418 3 33 0 + 419 3 33 1 + 420 2 32 0 + 421 2 32 1 + 422 2 33 0 + 423 2 33 1 + 424 0 33 1 + 425 0 33 0 + 426 0 32 1 + 427 0 32 0 + 428 1 33 1 + 429 1 33 0 + 430 1 32 1 + 431 1 32 0 + 432 4 32 0 + 433 4 32 1 + 434 4 33 0 + 435 4 33 1 + 436 5 32 0 + 437 5 32 1 + 438 5 33 0 + 439 5 33 1 + 440 7 33 1 + 441 7 33 0 + 442 7 32 1 + 443 7 32 0 + 444 6 33 1 + 445 6 33 0 + 446 6 32 1 + 447 6 32 0 + 448 12 33 0 + 449 12 33 1 + 450 12 32 0 + 451 12 32 1 + 452 13 33 0 + 453 13 33 1 + 454 13 32 0 + 455 13 32 1 + 456 15 32 1 + 457 15 32 0 + 458 15 33 1 + 459 15 33 0 + 460 14 32 1 + 461 14 32 0 + 462 14 33 1 + 463 14 33 0 + 512 11 35 0 + 513 11 35 1 + 514 11 34 0 + 515 11 34 1 + 516 10 35 0 + 517 10 35 1 + 518 10 34 0 + 519 10 34 1 + 520 8 34 1 + 521 8 34 0 + 522 8 35 1 + 523 8 35 0 + 524 9 34 1 + 525 9 34 0 + 526 9 35 1 + 527 9 35 0 + 544 3 34 0 + 545 3 34 1 + 546 3 35 0 + 547 3 35 1 + 548 2 34 0 + 549 2 34 1 + 550 2 35 0 + 551 2 35 1 + 552 0 35 1 + 553 0 35 0 + 554 0 34 1 + 555 0 34 0 + 556 1 35 1 + 557 1 35 0 + 558 1 34 1 + 559 1 34 0 + 560 4 34 0 + 561 4 34 1 + 562 4 35 0 + 563 4 35 1 + 564 5 34 0 + 565 5 34 1 + 566 5 35 0 + 567 5 35 1 + 568 7 35 1 + 569 7 35 0 + 570 7 34 1 + 571 7 34 0 + 572 6 35 1 + 573 6 35 0 + 574 6 34 1 + 575 6 34 0 + 576 12 35 0 + 577 12 35 1 + 578 12 34 0 + 579 12 34 1 + 580 13 35 0 + 581 13 35 1 + 582 13 34 0 + 583 13 34 1 + 584 15 34 1 + 585 15 34 0 + 586 15 35 1 + 587 15 35 0 + 588 14 34 1 + 589 14 34 0 + 590 14 35 1 + 591 14 35 0 + 640 11 37 0 + 641 11 37 1 + 642 11 36 0 + 643 11 36 1 + 644 10 37 0 + 645 10 37 1 + 646 10 36 0 + 647 10 36 1 + 648 8 36 1 + 649 8 36 0 + 650 8 37 1 + 651 8 37 0 + 652 9 36 1 + 653 9 36 0 + 654 9 37 1 + 655 9 37 0 + 672 3 36 0 + 673 3 36 1 + 674 3 37 0 + 675 3 37 1 + 676 2 36 0 + 677 2 36 1 + 678 2 37 0 + 679 2 37 1 + 680 0 37 1 + 681 0 37 0 + 682 0 36 1 + 683 0 36 0 + 684 1 37 1 + 685 1 37 0 + 686 1 36 1 + 687 1 36 0 + 688 4 36 0 + 689 4 36 1 + 690 4 37 0 + 691 4 37 1 + 692 5 36 0 + 693 5 36 1 + 694 5 37 0 + 695 5 37 1 + 696 7 37 1 + 697 7 37 0 + 698 7 36 1 + 699 7 36 0 + 700 6 37 1 + 701 6 37 0 + 702 6 36 1 + 703 6 36 0 + 704 12 37 0 + 705 12 37 1 + 706 12 36 0 + 707 12 36 1 + 708 13 37 0 + 709 13 37 1 + 710 13 36 0 + 711 13 36 1 + 712 15 36 1 + 713 15 36 0 + 714 15 37 1 + 715 15 37 0 + 716 14 36 1 + 717 14 36 0 + 718 14 37 1 + 719 14 37 0 + 768 11 39 0 + 769 11 39 1 + 770 11 38 0 + 771 11 38 1 + 772 10 39 0 + 773 10 39 1 + 774 10 38 0 + 775 10 38 1 + 776 8 38 1 + 777 8 38 0 + 778 8 39 1 + 779 8 39 0 + 780 9 38 1 + 781 9 38 0 + 782 9 39 1 + 783 9 39 0 + 800 3 38 0 + 801 3 38 1 + 802 3 39 0 + 803 3 39 1 + 804 2 38 0 + 805 2 38 1 + 806 2 39 0 + 807 2 39 1 + 808 0 39 1 + 809 0 39 0 + 810 0 38 1 + 811 0 38 0 + 812 1 39 1 + 813 1 39 0 + 814 1 38 1 + 815 1 38 0 + 816 4 38 0 + 817 4 38 1 + 818 4 39 0 + 819 4 39 1 + 820 5 38 0 + 821 5 38 1 + 822 5 39 0 + 823 5 39 1 + 824 7 39 1 + 825 7 39 0 + 826 7 38 1 + 827 7 38 0 + 828 6 39 1 + 829 6 39 0 + 830 6 38 1 + 831 6 38 0 + 832 12 39 0 + 833 12 39 1 + 834 12 38 0 + 835 12 38 1 + 836 13 39 0 + 837 13 39 1 + 838 13 38 0 + 839 13 38 1 + 840 15 38 1 + 841 15 38 0 + 842 15 39 1 + 843 15 39 0 + 844 14 38 1 + 845 14 38 0 + 846 14 39 1 + 847 14 39 0 + 896 11 41 0 + 897 11 41 1 + 898 11 40 0 + 899 11 40 1 + 900 10 41 0 + 901 10 41 1 + 902 10 40 0 + 903 10 40 1 + 904 8 40 1 + 905 8 40 0 + 906 8 41 1 + 907 8 41 0 + 908 9 40 1 + 909 9 40 0 + 910 9 41 1 + 911 9 41 0 + 928 3 40 0 + 929 3 40 1 + 930 3 41 0 + 931 3 41 1 + 932 2 40 0 + 933 2 40 1 + 934 2 41 0 + 935 2 41 1 + 936 0 41 1 + 937 0 41 0 + 938 0 40 1 + 939 0 40 0 + 940 1 41 1 + 941 1 41 0 + 942 1 40 1 + 943 1 40 0 + 944 4 40 0 + 945 4 40 1 + 946 4 41 0 + 947 4 41 1 + 948 5 40 0 + 949 5 40 1 + 950 5 41 0 + 951 5 41 1 + 952 7 41 1 + 953 7 41 0 + 954 7 40 1 + 955 7 40 0 + 956 6 41 1 + 957 6 41 0 + 958 6 40 1 + 959 6 40 0 + 960 12 41 0 + 961 12 41 1 + 962 12 40 0 + 963 12 40 1 + 964 13 41 0 + 965 13 41 1 + 966 13 40 0 + 967 13 40 1 + 968 15 40 1 + 969 15 40 0 + 970 15 41 1 + 971 15 41 0 + 972 14 40 1 + 973 14 40 0 + 974 14 41 1 + 975 14 41 0 +1024 11 43 0 +1025 11 43 1 +1026 11 42 0 +1027 11 42 1 +1028 10 43 0 +1029 10 43 1 +1030 10 42 0 +1031 10 42 1 +1032 8 42 1 +1033 8 42 0 +1034 8 43 1 +1035 8 43 0 +1036 9 42 1 +1037 9 42 0 +1038 9 43 1 +1039 9 43 0 +1056 3 42 0 +1057 3 42 1 +1058 3 43 0 +1059 3 43 1 +1060 2 42 0 +1061 2 42 1 +1062 2 43 0 +1063 2 43 1 +1064 0 43 1 +1065 0 43 0 +1066 0 42 1 +1067 0 42 0 +1068 1 43 1 +1069 1 43 0 +1070 1 42 1 +1071 1 42 0 +1072 4 42 0 +1073 4 42 1 +1074 4 43 0 +1075 4 43 1 +1076 5 42 0 +1077 5 42 1 +1078 5 43 0 +1079 5 43 1 +1080 7 43 1 +1081 7 43 0 +1082 7 42 1 +1083 7 42 0 +1084 6 43 1 +1085 6 43 0 +1086 6 42 1 +1087 6 42 0 +1088 12 43 0 +1089 12 43 1 +1090 12 42 0 +1091 12 42 1 +1092 13 43 0 +1093 13 43 1 +1094 13 42 0 +1095 13 42 1 +1096 15 42 1 +1097 15 42 0 +1098 15 43 1 +1099 15 43 0 +1100 14 42 1 +1101 14 42 0 +1102 14 43 1 +1103 14 43 0 +1152 11 45 0 +1153 11 45 1 +1154 11 44 0 +1155 11 44 1 +1156 10 45 0 +1157 10 45 1 +1158 10 44 0 +1159 10 44 1 +1160 8 44 1 +1161 8 44 0 +1162 8 45 1 +1163 8 45 0 +1164 9 44 1 +1165 9 44 0 +1166 9 45 1 +1167 9 45 0 +1184 3 44 0 +1185 3 44 1 +1186 3 45 0 +1187 3 45 1 +1188 2 44 0 +1189 2 44 1 +1190 2 45 0 +1191 2 45 1 +1192 0 45 1 +1193 0 45 0 +1194 0 44 1 +1195 0 44 0 +1196 1 45 1 +1197 1 45 0 +1198 1 44 1 +1199 1 44 0 +1200 4 44 0 +1201 4 44 1 +1202 4 45 0 +1203 4 45 1 +1204 5 44 0 +1205 5 44 1 +1206 5 45 0 +1207 5 45 1 +1208 7 45 1 +1209 7 45 0 +1210 7 44 1 +1211 7 44 0 +1212 6 45 1 +1213 6 45 0 +1214 6 44 1 +1215 6 44 0 +1216 12 45 0 +1217 12 45 1 +1218 12 44 0 +1219 12 44 1 +1220 13 45 0 +1221 13 45 1 +1222 13 44 0 +1223 13 44 1 +1224 15 44 1 +1225 15 44 0 +1226 15 45 1 +1227 15 45 0 +1228 14 44 1 +1229 14 44 0 +1230 14 45 1 +1231 14 45 0 +1280 11 47 0 +1281 11 47 1 +1282 11 46 0 +1283 11 46 1 +1284 10 47 0 +1285 10 47 1 +1286 10 46 0 +1287 10 46 1 +1288 8 46 1 +1289 8 46 0 +1290 8 47 1 +1291 8 47 0 +1292 9 46 1 +1293 9 46 0 +1294 9 47 1 +1295 9 47 0 +1312 3 46 0 +1313 3 46 1 +1314 3 47 0 +1315 3 47 1 +1316 2 46 0 +1317 2 46 1 +1318 2 47 0 +1319 2 47 1 +1320 0 47 1 +1321 0 47 0 +1322 0 46 1 +1323 0 46 0 +1324 1 47 1 +1325 1 47 0 +1326 1 46 1 +1327 1 46 0 +1328 4 46 0 +1329 4 46 1 +1330 4 47 0 +1331 4 47 1 +1332 5 46 0 +1333 5 46 1 +1334 5 47 0 +1335 5 47 1 +1336 7 47 1 +1337 7 47 0 +1338 7 46 1 +1339 7 46 0 +1340 6 47 1 +1341 6 47 0 +1342 6 46 1 +1343 6 46 0 +1344 12 47 0 +1345 12 47 1 +1346 12 46 0 +1347 12 46 1 +1348 13 47 0 +1349 13 47 1 +1350 13 46 0 +1351 13 46 1 +1352 15 46 1 +1353 15 46 0 +1354 15 47 1 +1355 15 47 0 +1356 14 46 1 +1357 14 46 0 +1358 14 47 1 +1359 14 47 0 +1408 11 49 0 +1409 11 49 1 +1410 11 48 0 +1411 11 48 1 +1412 10 49 0 +1413 10 49 1 +1414 10 48 0 +1415 10 48 1 +1416 8 48 1 +1417 8 48 0 +1418 8 49 1 +1419 8 49 0 +1420 9 48 1 +1421 9 48 0 +1422 9 49 1 +1423 9 49 0 +1440 3 48 0 +1441 3 48 1 +1442 3 49 0 +1443 3 49 1 +1444 2 48 0 +1445 2 48 1 +1446 2 49 0 +1447 2 49 1 +1448 0 49 1 +1449 0 49 0 +1450 0 48 1 +1451 0 48 0 +1452 1 49 1 +1453 1 49 0 +1454 1 48 1 +1455 1 48 0 +1456 4 48 0 +1457 4 48 1 +1458 4 49 0 +1459 4 49 1 +1460 5 48 0 +1461 5 48 1 +1462 5 49 0 +1463 5 49 1 +1464 7 49 1 +1465 7 49 0 +1466 7 48 1 +1467 7 48 0 +1468 6 49 1 +1469 6 49 0 +1470 6 48 1 +1471 6 48 0 +1472 12 49 0 +1473 12 49 1 +1474 12 48 0 +1475 12 48 1 +1476 13 49 0 +1477 13 49 1 +1478 13 48 0 +1479 13 48 1 +1480 15 48 1 +1481 15 48 0 +1482 15 49 1 +1483 15 49 0 +1484 14 48 1 +1485 14 48 0 +1486 14 49 1 +1487 14 49 0 +1536 11 51 0 +1537 11 51 1 +1538 11 50 0 +1539 11 50 1 +1540 10 51 0 +1541 10 51 1 +1542 10 50 0 +1543 10 50 1 +1544 8 50 1 +1545 8 50 0 +1546 8 51 1 +1547 8 51 0 +1548 9 50 1 +1549 9 50 0 +1550 9 51 1 +1551 9 51 0 +1568 3 50 0 +1569 3 50 1 +1570 3 51 0 +1571 3 51 1 +1572 2 50 0 +1573 2 50 1 +1574 2 51 0 +1575 2 51 1 +1576 0 51 1 +1577 0 51 0 +1578 0 50 1 +1579 0 50 0 +1580 1 51 1 +1581 1 51 0 +1582 1 50 1 +1583 1 50 0 +1584 4 50 0 +1585 4 50 1 +1586 4 51 0 +1587 4 51 1 +1588 5 50 0 +1589 5 50 1 +1590 5 51 0 +1591 5 51 1 +1592 7 51 1 +1593 7 51 0 +1594 7 50 1 +1595 7 50 0 +1596 6 51 1 +1597 6 51 0 +1598 6 50 1 +1599 6 50 0 +1600 12 51 0 +1601 12 51 1 +1602 12 50 0 +1603 12 50 1 +1604 13 51 0 +1605 13 51 1 +1606 13 50 0 +1607 13 50 1 +1608 15 50 1 +1609 15 50 0 +1610 15 51 1 +1611 15 51 0 +1612 14 50 1 +1613 14 50 0 +1614 14 51 1 +1615 14 51 0 +1664 11 53 0 +1665 11 53 1 +1666 11 52 0 +1667 11 52 1 +1668 10 53 0 +1669 10 53 1 +1670 10 52 0 +1671 10 52 1 +1672 8 52 1 +1673 8 52 0 +1674 8 53 1 +1675 8 53 0 +1676 9 52 1 +1677 9 52 0 +1678 9 53 1 +1679 9 53 0 +1696 3 52 0 +1697 3 52 1 +1698 3 53 0 +1699 3 53 1 +1700 2 52 0 +1701 2 52 1 +1702 2 53 0 +1703 2 53 1 +1704 0 53 1 +1705 0 53 0 +1706 0 52 1 +1707 0 52 0 +1708 1 53 1 +1709 1 53 0 +1710 1 52 1 +1711 1 52 0 +1712 4 52 0 +1713 4 52 1 +1714 4 53 0 +1715 4 53 1 +1716 5 52 0 +1717 5 52 1 +1718 5 53 0 +1719 5 53 1 +1720 7 53 1 +1721 7 53 0 +1722 7 52 1 +1723 7 52 0 +1724 6 53 1 +1725 6 53 0 +1726 6 52 1 +1727 6 52 0 +1728 12 53 0 +1729 12 53 1 +1730 12 52 0 +1731 12 52 1 +1732 13 53 0 +1733 13 53 1 +1734 13 52 0 +1735 13 52 1 +1736 15 52 1 +1737 15 52 0 +1738 15 53 1 +1739 15 53 0 +1740 14 52 1 +1741 14 52 0 +1742 14 53 1 +1743 14 53 0 +1792 11 55 0 +1793 11 55 1 +1794 11 54 0 +1795 11 54 1 +1796 10 55 0 +1797 10 55 1 +1798 10 54 0 +1799 10 54 1 +1800 8 54 1 +1801 8 54 0 +1802 8 55 1 +1803 8 55 0 +1804 9 54 1 +1805 9 54 0 +1806 9 55 1 +1807 9 55 0 +1824 3 54 0 +1825 3 54 1 +1826 3 55 0 +1827 3 55 1 +1828 2 54 0 +1829 2 54 1 +1830 2 55 0 +1831 2 55 1 +1832 0 55 1 +1833 0 55 0 +1834 0 54 1 +1835 0 54 0 +1836 1 55 1 +1837 1 55 0 +1838 1 54 1 +1839 1 54 0 +1840 4 54 0 +1841 4 54 1 +1842 4 55 0 +1843 4 55 1 +1844 5 54 0 +1845 5 54 1 +1846 5 55 0 +1847 5 55 1 +1848 7 55 1 +1849 7 55 0 +1850 7 54 1 +1851 7 54 0 +1852 6 55 1 +1853 6 55 0 +1854 6 54 1 +1855 6 54 0 +1856 12 55 0 +1857 12 55 1 +1858 12 54 0 +1859 12 54 1 +1860 13 55 0 +1861 13 55 1 +1862 13 54 0 +1863 13 54 1 +1864 15 54 1 +1865 15 54 0 +1866 15 55 1 +1867 15 55 0 +1868 14 54 1 +1869 14 54 0 +1870 14 55 1 +1871 14 55 0 +2176 11 27 0 +2177 11 27 1 +2178 11 26 0 +2179 11 26 1 +2180 10 27 0 +2181 10 27 1 +2182 10 26 0 +2183 10 26 1 +2184 8 26 1 +2185 8 26 0 +2186 8 27 1 +2187 8 27 0 +2188 9 26 1 +2189 9 26 0 +2190 9 27 1 +2191 9 27 0 +2208 3 26 0 +2209 3 26 1 +2210 3 27 0 +2211 3 27 1 +2212 2 26 0 +2213 2 26 1 +2214 2 27 0 +2215 2 27 1 +2216 0 27 1 +2217 0 27 0 +2218 0 26 1 +2219 0 26 0 +2220 1 27 1 +2221 1 27 0 +2222 1 26 1 +2223 1 26 0 +2224 4 26 0 +2225 4 26 1 +2226 4 27 0 +2227 4 27 1 +2228 5 26 0 +2229 5 26 1 +2230 5 27 0 +2231 5 27 1 +2232 7 27 1 +2233 7 27 0 +2234 7 26 1 +2235 7 26 0 +2236 6 27 1 +2237 6 27 0 +2238 6 26 1 +2239 6 26 0 +2240 12 27 0 +2241 12 27 1 +2242 12 26 0 +2243 12 26 1 +2244 13 27 0 +2245 13 27 1 +2246 13 26 0 +2247 13 26 1 +2248 15 26 1 +2249 15 26 0 +2250 15 27 1 +2251 15 27 0 +2252 14 26 1 +2253 14 26 0 +2254 14 27 1 +2255 14 27 0 +2304 11 25 0 +2305 11 25 1 +2306 11 24 0 +2307 11 24 1 +2308 10 25 0 +2309 10 25 1 +2310 10 24 0 +2311 10 24 1 +2312 8 24 1 +2313 8 24 0 +2314 8 25 1 +2315 8 25 0 +2316 9 24 1 +2317 9 24 0 +2318 9 25 1 +2319 9 25 0 +2336 3 24 0 +2337 3 24 1 +2338 3 25 0 +2339 3 25 1 +2340 2 24 0 +2341 2 24 1 +2342 2 25 0 +2343 2 25 1 +2344 0 25 1 +2345 0 25 0 +2346 0 24 1 +2347 0 24 0 +2348 1 25 1 +2349 1 25 0 +2350 1 24 1 +2351 1 24 0 +2352 4 24 0 +2353 4 24 1 +2354 4 25 0 +2355 4 25 1 +2356 5 24 0 +2357 5 24 1 +2358 5 25 0 +2359 5 25 1 +2360 7 25 1 +2361 7 25 0 +2362 7 24 1 +2363 7 24 0 +2364 6 25 1 +2365 6 25 0 +2366 6 24 1 +2367 6 24 0 +2368 12 25 0 +2369 12 25 1 +2370 12 24 0 +2371 12 24 1 +2372 13 25 0 +2373 13 25 1 +2374 13 24 0 +2375 13 24 1 +2376 15 24 1 +2377 15 24 0 +2378 15 25 1 +2379 15 25 0 +2380 14 24 1 +2381 14 24 0 +2382 14 25 1 +2383 14 25 0 +2432 11 23 0 +2433 11 23 1 +2434 11 22 0 +2435 11 22 1 +2436 10 23 0 +2437 10 23 1 +2438 10 22 0 +2439 10 22 1 +2440 8 22 1 +2441 8 22 0 +2442 8 23 1 +2443 8 23 0 +2444 9 22 1 +2445 9 22 0 +2446 9 23 1 +2447 9 23 0 +2464 3 22 0 +2465 3 22 1 +2466 3 23 0 +2467 3 23 1 +2468 2 22 0 +2469 2 22 1 +2470 2 23 0 +2471 2 23 1 +2472 0 23 1 +2473 0 23 0 +2474 0 22 1 +2475 0 22 0 +2476 1 23 1 +2477 1 23 0 +2478 1 22 1 +2479 1 22 0 +2480 4 22 0 +2481 4 22 1 +2482 4 23 0 +2483 4 23 1 +2484 5 22 0 +2485 5 22 1 +2486 5 23 0 +2487 5 23 1 +2488 7 23 1 +2489 7 23 0 +2490 7 22 1 +2491 7 22 0 +2492 6 23 1 +2493 6 23 0 +2494 6 22 1 +2495 6 22 0 +2496 12 23 0 +2497 12 23 1 +2498 12 22 0 +2499 12 22 1 +2500 13 23 0 +2501 13 23 1 +2502 13 22 0 +2503 13 22 1 +2504 15 22 1 +2505 15 22 0 +2506 15 23 1 +2507 15 23 0 +2508 14 22 1 +2509 14 22 0 +2510 14 23 1 +2511 14 23 0 +2560 11 21 0 +2561 11 21 1 +2562 11 20 0 +2563 11 20 1 +2564 10 21 0 +2565 10 21 1 +2566 10 20 0 +2567 10 20 1 +2568 8 20 1 +2569 8 20 0 +2570 8 21 1 +2571 8 21 0 +2572 9 20 1 +2573 9 20 0 +2574 9 21 1 +2575 9 21 0 +2592 3 20 0 +2593 3 20 1 +2594 3 21 0 +2595 3 21 1 +2596 2 20 0 +2597 2 20 1 +2598 2 21 0 +2599 2 21 1 +2600 0 21 1 +2601 0 21 0 +2602 0 20 1 +2603 0 20 0 +2604 1 21 1 +2605 1 21 0 +2606 1 20 1 +2607 1 20 0 +2608 4 20 0 +2609 4 20 1 +2610 4 21 0 +2611 4 21 1 +2612 5 20 0 +2613 5 20 1 +2614 5 21 0 +2615 5 21 1 +2616 7 21 1 +2617 7 21 0 +2618 7 20 1 +2619 7 20 0 +2620 6 21 1 +2621 6 21 0 +2622 6 20 1 +2623 6 20 0 +2624 12 21 0 +2625 12 21 1 +2626 12 20 0 +2627 12 20 1 +2628 13 21 0 +2629 13 21 1 +2630 13 20 0 +2631 13 20 1 +2632 15 20 1 +2633 15 20 0 +2634 15 21 1 +2635 15 21 0 +2636 14 20 1 +2637 14 20 0 +2638 14 21 1 +2639 14 21 0 +2688 11 19 0 +2689 11 19 1 +2690 11 18 0 +2691 11 18 1 +2692 10 19 0 +2693 10 19 1 +2694 10 18 0 +2695 10 18 1 +2696 8 18 1 +2697 8 18 0 +2698 8 19 1 +2699 8 19 0 +2700 9 18 1 +2701 9 18 0 +2702 9 19 1 +2703 9 19 0 +2720 3 18 0 +2721 3 18 1 +2722 3 19 0 +2723 3 19 1 +2724 2 18 0 +2725 2 18 1 +2726 2 19 0 +2727 2 19 1 +2728 0 19 1 +2729 0 19 0 +2730 0 18 1 +2731 0 18 0 +2732 1 19 1 +2733 1 19 0 +2734 1 18 1 +2735 1 18 0 +2736 4 18 0 +2737 4 18 1 +2738 4 19 0 +2739 4 19 1 +2740 5 18 0 +2741 5 18 1 +2742 5 19 0 +2743 5 19 1 +2744 7 19 1 +2745 7 19 0 +2746 7 18 1 +2747 7 18 0 +2748 6 19 1 +2749 6 19 0 +2750 6 18 1 +2751 6 18 0 +2752 12 19 0 +2753 12 19 1 +2754 12 18 0 +2755 12 18 1 +2756 13 19 0 +2757 13 19 1 +2758 13 18 0 +2759 13 18 1 +2760 15 18 1 +2761 15 18 0 +2762 15 19 1 +2763 15 19 0 +2764 14 18 1 +2765 14 18 0 +2766 14 19 1 +2767 14 19 0 +2816 11 17 0 +2817 11 17 1 +2818 11 16 0 +2819 11 16 1 +2820 10 17 0 +2821 10 17 1 +2822 10 16 0 +2823 10 16 1 +2824 8 16 1 +2825 8 16 0 +2826 8 17 1 +2827 8 17 0 +2828 9 16 1 +2829 9 16 0 +2830 9 17 1 +2831 9 17 0 +2848 3 16 0 +2849 3 16 1 +2850 3 17 0 +2851 3 17 1 +2852 2 16 0 +2853 2 16 1 +2854 2 17 0 +2855 2 17 1 +2856 0 17 1 +2857 0 17 0 +2858 0 16 1 +2859 0 16 0 +2860 1 17 1 +2861 1 17 0 +2862 1 16 1 +2863 1 16 0 +2864 4 16 0 +2865 4 16 1 +2866 4 17 0 +2867 4 17 1 +2868 5 16 0 +2869 5 16 1 +2870 5 17 0 +2871 5 17 1 +2872 7 17 1 +2873 7 17 0 +2874 7 16 1 +2875 7 16 0 +2876 6 17 1 +2877 6 17 0 +2878 6 16 1 +2879 6 16 0 +2880 12 17 0 +2881 12 17 1 +2882 12 16 0 +2883 12 16 1 +2884 13 17 0 +2885 13 17 1 +2886 13 16 0 +2887 13 16 1 +2888 15 16 1 +2889 15 16 0 +2890 15 17 1 +2891 15 17 0 +2892 14 16 1 +2893 14 16 0 +2894 14 17 1 +2895 14 17 0 +2944 11 15 0 +2945 11 15 1 +2946 11 14 0 +2947 11 14 1 +2948 10 15 0 +2949 10 15 1 +2950 10 14 0 +2951 10 14 1 +2952 8 14 1 +2953 8 14 0 +2954 8 15 1 +2955 8 15 0 +2956 9 14 1 +2957 9 14 0 +2958 9 15 1 +2959 9 15 0 +2976 3 14 0 +2977 3 14 1 +2978 3 15 0 +2979 3 15 1 +2980 2 14 0 +2981 2 14 1 +2982 2 15 0 +2983 2 15 1 +2984 0 15 1 +2985 0 15 0 +2986 0 14 1 +2987 0 14 0 +2988 1 15 1 +2989 1 15 0 +2990 1 14 1 +2991 1 14 0 +2992 4 14 0 +2993 4 14 1 +2994 4 15 0 +2995 4 15 1 +2996 5 14 0 +2997 5 14 1 +2998 5 15 0 +2999 5 15 1 +3000 7 15 1 +3001 7 15 0 +3002 7 14 1 +3003 7 14 0 +3004 6 15 1 +3005 6 15 0 +3006 6 14 1 +3007 6 14 0 +3008 12 15 0 +3009 12 15 1 +3010 12 14 0 +3011 12 14 1 +3012 13 15 0 +3013 13 15 1 +3014 13 14 0 +3015 13 14 1 +3016 15 14 1 +3017 15 14 0 +3018 15 15 1 +3019 15 15 0 +3020 14 14 1 +3021 14 14 0 +3022 14 15 1 +3023 14 15 0 +3072 11 13 0 +3073 11 13 1 +3074 11 12 0 +3075 11 12 1 +3076 10 13 0 +3077 10 13 1 +3078 10 12 0 +3079 10 12 1 +3080 8 12 1 +3081 8 12 0 +3082 8 13 1 +3083 8 13 0 +3084 9 12 1 +3085 9 12 0 +3086 9 13 1 +3087 9 13 0 +3104 3 12 0 +3105 3 12 1 +3106 3 13 0 +3107 3 13 1 +3108 2 12 0 +3109 2 12 1 +3110 2 13 0 +3111 2 13 1 +3112 0 13 1 +3113 0 13 0 +3114 0 12 1 +3115 0 12 0 +3116 1 13 1 +3117 1 13 0 +3118 1 12 1 +3119 1 12 0 +3120 4 12 0 +3121 4 12 1 +3122 4 13 0 +3123 4 13 1 +3124 5 12 0 +3125 5 12 1 +3126 5 13 0 +3127 5 13 1 +3128 7 13 1 +3129 7 13 0 +3130 7 12 1 +3131 7 12 0 +3132 6 13 1 +3133 6 13 0 +3134 6 12 1 +3135 6 12 0 +3136 12 13 0 +3137 12 13 1 +3138 12 12 0 +3139 12 12 1 +3140 13 13 0 +3141 13 13 1 +3142 13 12 0 +3143 13 12 1 +3144 15 12 1 +3145 15 12 0 +3146 15 13 1 +3147 15 13 0 +3148 14 12 1 +3149 14 12 0 +3150 14 13 1 +3151 14 13 0 +3200 11 11 0 +3201 11 11 1 +3202 11 10 0 +3203 11 10 1 +3204 10 11 0 +3205 10 11 1 +3206 10 10 0 +3207 10 10 1 +3208 8 10 1 +3209 8 10 0 +3210 8 11 1 +3211 8 11 0 +3212 9 10 1 +3213 9 10 0 +3214 9 11 1 +3215 9 11 0 +3232 3 10 0 +3233 3 10 1 +3234 3 11 0 +3235 3 11 1 +3236 2 10 0 +3237 2 10 1 +3238 2 11 0 +3239 2 11 1 +3240 0 11 1 +3241 0 11 0 +3242 0 10 1 +3243 0 10 0 +3244 1 11 1 +3245 1 11 0 +3246 1 10 1 +3247 1 10 0 +3248 4 10 0 +3249 4 10 1 +3250 4 11 0 +3251 4 11 1 +3252 5 10 0 +3253 5 10 1 +3254 5 11 0 +3255 5 11 1 +3256 7 11 1 +3257 7 11 0 +3258 7 10 1 +3259 7 10 0 +3260 6 11 1 +3261 6 11 0 +3262 6 10 1 +3263 6 10 0 +3264 12 11 0 +3265 12 11 1 +3266 12 10 0 +3267 12 10 1 +3268 13 11 0 +3269 13 11 1 +3270 13 10 0 +3271 13 10 1 +3272 15 10 1 +3273 15 10 0 +3274 15 11 1 +3275 15 11 0 +3276 14 10 1 +3277 14 10 0 +3278 14 11 1 +3279 14 11 0 +3328 11 9 0 +3329 11 9 1 +3330 11 8 0 +3331 11 8 1 +3332 10 9 0 +3333 10 9 1 +3334 10 8 0 +3335 10 8 1 +3336 8 8 1 +3337 8 8 0 +3338 8 9 1 +3339 8 9 0 +3340 9 8 1 +3341 9 8 0 +3342 9 9 1 +3343 9 9 0 +3360 3 8 0 +3361 3 8 1 +3362 3 9 0 +3363 3 9 1 +3364 2 8 0 +3365 2 8 1 +3366 2 9 0 +3367 2 9 1 +3368 0 9 1 +3369 0 9 0 +3370 0 8 1 +3371 0 8 0 +3372 1 9 1 +3373 1 9 0 +3374 1 8 1 +3375 1 8 0 +3376 4 8 0 +3377 4 8 1 +3378 4 9 0 +3379 4 9 1 +3380 5 8 0 +3381 5 8 1 +3382 5 9 0 +3383 5 9 1 +3384 7 9 1 +3385 7 9 0 +3386 7 8 1 +3387 7 8 0 +3388 6 9 1 +3389 6 9 0 +3390 6 8 1 +3391 6 8 0 +3392 12 9 0 +3393 12 9 1 +3394 12 8 0 +3395 12 8 1 +3396 13 9 0 +3397 13 9 1 +3398 13 8 0 +3399 13 8 1 +3400 15 8 1 +3401 15 8 0 +3402 15 9 1 +3403 15 9 0 +3404 14 8 1 +3405 14 8 0 +3406 14 9 1 +3407 14 9 0 +3456 11 7 0 +3457 11 7 1 +3458 11 6 0 +3459 11 6 1 +3460 10 7 0 +3461 10 7 1 +3462 10 6 0 +3463 10 6 1 +3464 8 6 1 +3465 8 6 0 +3466 8 7 1 +3467 8 7 0 +3468 9 6 1 +3469 9 6 0 +3470 9 7 1 +3471 9 7 0 +3488 3 6 0 +3489 3 6 1 +3490 3 7 0 +3491 3 7 1 +3492 2 6 0 +3493 2 6 1 +3494 2 7 0 +3495 2 7 1 +3496 0 7 1 +3497 0 7 0 +3498 0 6 1 +3499 0 6 0 +3500 1 7 1 +3501 1 7 0 +3502 1 6 1 +3503 1 6 0 +3504 4 6 0 +3505 4 6 1 +3506 4 7 0 +3507 4 7 1 +3508 5 6 0 +3509 5 6 1 +3510 5 7 0 +3511 5 7 1 +3512 7 7 1 +3513 7 7 0 +3514 7 6 1 +3515 7 6 0 +3516 6 7 1 +3517 6 7 0 +3518 6 6 1 +3519 6 6 0 +3520 12 7 0 +3521 12 7 1 +3522 12 6 0 +3523 12 6 1 +3524 13 7 0 +3525 13 7 1 +3526 13 6 0 +3527 13 6 1 +3528 15 6 1 +3529 15 6 0 +3530 15 7 1 +3531 15 7 0 +3532 14 6 1 +3533 14 6 0 +3534 14 7 1 +3535 14 7 0 +3584 11 5 0 +3585 11 5 1 +3586 11 4 0 +3587 11 4 1 +3588 10 5 0 +3589 10 5 1 +3590 10 4 0 +3591 10 4 1 +3592 8 4 1 +3593 8 4 0 +3594 8 5 1 +3595 8 5 0 +3596 9 4 1 +3597 9 4 0 +3598 9 5 1 +3599 9 5 0 +3616 3 4 0 +3617 3 4 1 +3618 3 5 0 +3619 3 5 1 +3620 2 4 0 +3621 2 4 1 +3622 2 5 0 +3623 2 5 1 +3624 0 5 1 +3625 0 5 0 +3626 0 4 1 +3627 0 4 0 +3628 1 5 1 +3629 1 5 0 +3630 1 4 1 +3631 1 4 0 +3632 4 4 0 +3633 4 4 1 +3634 4 5 0 +3635 4 5 1 +3636 5 4 0 +3637 5 4 1 +3638 5 5 0 +3639 5 5 1 +3640 7 5 1 +3641 7 5 0 +3642 7 4 1 +3643 7 4 0 +3644 6 5 1 +3645 6 5 0 +3646 6 4 1 +3647 6 4 0 +3648 12 5 0 +3649 12 5 1 +3650 12 4 0 +3651 12 4 1 +3652 13 5 0 +3653 13 5 1 +3654 13 4 0 +3655 13 4 1 +3656 15 4 1 +3657 15 4 0 +3658 15 5 1 +3659 15 5 0 +3660 14 4 1 +3661 14 4 0 +3662 14 5 1 +3663 14 5 0 +3712 11 3 0 +3713 11 3 1 +3714 11 2 0 +3715 11 2 1 +3716 10 3 0 +3717 10 3 1 +3718 10 2 0 +3719 10 2 1 +3720 8 2 1 +3721 8 2 0 +3722 8 3 1 +3723 8 3 0 +3724 9 2 1 +3725 9 2 0 +3726 9 3 1 +3727 9 3 0 +3744 3 2 0 +3745 3 2 1 +3746 3 3 0 +3747 3 3 1 +3748 2 2 0 +3749 2 2 1 +3750 2 3 0 +3751 2 3 1 +3752 0 3 1 +3753 0 3 0 +3754 0 2 1 +3755 0 2 0 +3756 1 3 1 +3757 1 3 0 +3758 1 2 1 +3759 1 2 0 +3760 4 2 0 +3761 4 2 1 +3762 4 3 0 +3763 4 3 1 +3764 5 2 0 +3765 5 2 1 +3766 5 3 0 +3767 5 3 1 +3768 7 3 1 +3769 7 3 0 +3770 7 2 1 +3771 7 2 0 +3772 6 3 1 +3773 6 3 0 +3774 6 2 1 +3775 6 2 0 +3776 12 3 0 +3777 12 3 1 +3778 12 2 0 +3779 12 2 1 +3780 13 3 0 +3781 13 3 1 +3782 13 2 0 +3783 13 2 1 +3784 15 2 1 +3785 15 2 0 +3786 15 3 1 +3787 15 3 0 +3788 14 2 1 +3789 14 2 0 +3790 14 3 1 +3791 14 3 0 +3840 11 1 0 +3841 11 1 1 +3842 11 0 0 +3843 11 0 1 +3844 10 1 0 +3845 10 1 1 +3846 10 0 0 +3847 10 0 1 +3848 8 0 1 +3849 8 0 0 +3850 8 1 1 +3851 8 1 0 +3852 9 0 1 +3853 9 0 0 +3854 9 1 1 +3855 9 1 0 +3872 3 0 0 +3873 3 0 1 +3874 3 1 0 +3875 3 1 1 +3876 2 0 0 +3877 2 0 1 +3878 2 1 0 +3879 2 1 1 +3880 0 1 1 +3881 0 1 0 +3882 0 0 1 +3883 0 0 0 +3884 1 1 1 +3885 1 1 0 +3886 1 0 1 +3887 1 0 0 +3888 4 0 0 +3889 4 0 1 +3890 4 1 0 +3891 4 1 1 +3892 5 0 0 +3893 5 0 1 +3894 5 1 0 +3895 5 1 1 +3896 7 1 1 +3897 7 1 0 +3898 7 0 1 +3899 7 0 0 +3900 6 1 1 +3901 6 1 0 +3902 6 0 1 +3903 6 0 0 +3904 12 1 0 +3905 12 1 1 +3906 12 0 0 +3907 12 0 1 +3908 13 1 0 +3909 13 1 1 +3910 13 0 0 +3911 13 0 1 +3912 15 0 1 +3913 15 0 0 +3914 15 1 1 +3915 15 1 0 +3916 14 0 1 +3917 14 0 0 +3918 14 1 1 +3919 14 1 0 diff --git a/Detectors/PHOS/base/files/Mod1RCU1.data b/Detectors/PHOS/base/files/Mod1RCU1.data new file mode 100644 index 0000000000000..db125226496cf --- /dev/null +++ b/Detectors/PHOS/base/files/Mod1RCU1.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 1 0 2 + 1 1 1 2 + 2 1 2 2 + 3 1 3 2 + 4 1 4 2 + 5 1 5 2 + 6 1 6 2 + 7 1 7 2 + 8 1 8 2 + 9 1 9 2 + 10 1 10 2 + 11 1 11 2 + 12 1 12 2 + 13 1 13 2 + 14 1 14 2 + 15 1 15 2 + 16 1 16 2 + 17 1 17 2 + 18 1 18 2 + 19 1 19 2 + 20 1 20 2 + 21 1 21 2 + 22 1 22 2 + 23 1 23 2 + 24 1 24 2 + 25 1 25 2 + 26 1 26 2 + 27 1 27 2 + 28 1 28 2 + 29 1 29 2 + 30 1 30 2 + 31 1 31 2 + 32 1 32 2 + 33 1 33 2 + 34 1 34 2 + 35 1 35 2 + 36 1 36 2 + 37 1 37 2 + 38 1 38 2 + 39 1 39 2 + 40 1 40 2 + 41 1 41 2 + 42 1 42 2 + 43 1 43 2 + 44 1 44 2 + 45 1 45 2 + 46 1 46 2 + 47 1 47 2 + 48 1 48 2 + 49 1 49 2 + 50 1 50 2 + 51 1 51 2 + 52 1 52 2 + 53 1 53 2 + 54 1 54 2 + 55 1 55 2 + 56 1 56 2 + 57 1 57 2 + 58 1 58 2 + 59 1 59 2 + 60 1 60 2 + 61 1 61 2 + 62 1 62 2 + 63 1 63 2 + 64 1 64 2 + 65 1 65 2 + 66 1 66 2 + 67 1 67 2 + 68 1 68 2 + 69 1 69 2 + 70 1 70 2 + 71 1 71 2 + 72 1 72 2 + 73 1 73 2 + 74 1 74 2 + 75 1 75 2 + 76 1 76 2 + 77 1 77 2 + 78 1 78 2 + 79 1 79 2 + 80 1 80 2 + 81 1 81 2 + 82 1 82 2 + 83 1 83 2 + 84 1 84 2 + 85 1 85 2 + 86 1 86 2 + 87 1 87 2 + 88 1 88 2 + 89 1 89 2 + 90 1 90 2 + 91 1 91 2 + 92 1 92 2 + 93 1 93 2 + 94 1 94 2 + 95 1 95 2 + 96 1 96 2 + 97 1 97 2 + 98 1 98 2 + 99 1 99 2 + 100 1 100 2 + 101 1 101 2 + 102 1 102 2 + 103 1 103 2 + 104 1 104 2 + 105 1 105 2 + 106 1 106 2 + 107 1 107 2 + 108 1 108 2 + 109 1 109 2 + 110 1 110 2 + 111 1 111 2 + 112 1 112 2 + 113 1 113 2 + 114 1 114 2 + 115 1 115 2 + 116 1 116 2 + 117 1 117 2 + 118 1 118 2 + 119 1 119 2 + 120 1 120 2 + 121 1 121 2 + 122 1 122 2 + 123 1 123 2 + 124 1 124 2 + 125 1 125 2 + 126 1 126 2 + 127 1 127 2 +2048 1 2048 2 +2049 1 2049 2 +2050 1 2050 2 +2051 1 2051 2 +2052 1 2052 2 +2053 1 2053 2 +2054 1 2054 2 +2055 1 2055 2 +2056 1 2056 2 +2057 1 2057 2 +2058 1 2058 2 +2059 1 2059 2 +2060 1 2060 2 +2061 1 2061 2 +2062 1 2062 2 +2063 1 2063 2 +2064 1 2064 2 +2065 1 2065 2 +2066 1 2066 2 +2067 1 2067 2 +2068 1 2068 2 +2069 1 2069 2 +2070 1 2070 2 +2071 1 2071 2 +2072 1 2072 2 +2073 1 2073 2 +2074 1 2074 2 +2075 1 2075 2 +2076 1 2076 2 +2077 1 2077 2 +2078 1 2078 2 +2079 1 2079 2 +2080 1 2080 2 +2081 1 2081 2 +2082 1 2082 2 +2083 1 2083 2 +2084 1 2084 2 +2085 1 2085 2 +2086 1 2086 2 +2087 1 2087 2 +2088 1 2088 2 +2089 1 2089 2 +2090 1 2090 2 +2091 1 2091 2 +2092 1 2092 2 +2093 1 2093 2 +2094 1 2094 2 +2095 1 2095 2 +2096 1 2096 2 +2097 1 2097 2 +2098 1 2098 2 +2099 1 2099 2 +2100 1 2100 2 +2101 1 2101 2 +2102 1 2102 2 +2103 1 2103 2 +2104 1 2104 2 +2105 1 2105 2 +2106 1 2106 2 +2107 1 2107 2 +2108 1 2108 2 +2109 1 2109 2 +2110 1 2110 2 +2111 1 2111 2 +2112 1 2112 2 +2113 1 2113 2 +2114 1 2114 2 +2115 1 2115 2 +2116 1 2116 2 +2117 1 2117 2 +2118 1 2118 2 +2119 1 2119 2 +2120 1 2120 2 +2121 1 2121 2 +2122 1 2122 2 +2123 1 2123 2 +2124 1 2124 2 +2125 1 2125 2 +2126 1 2126 2 +2127 1 2127 2 +2128 1 2128 2 +2129 1 2129 2 +2130 1 2130 2 +2131 1 2131 2 +2132 1 2132 2 +2133 1 2133 2 +2134 1 2134 2 +2135 1 2135 2 +2136 1 2136 2 +2137 1 2137 2 +2138 1 2138 2 +2139 1 2139 2 +2140 1 2140 2 +2141 1 2141 2 +2142 1 2142 2 +2143 1 2143 2 +2144 1 2144 2 +2145 1 2145 2 +2146 1 2146 2 +2147 1 2147 2 +2148 1 2148 2 +2149 1 2149 2 +2150 1 2150 2 +2151 1 2151 2 +2152 1 2152 2 +2153 1 2153 2 +2154 1 2154 2 +2155 1 2155 2 +2156 1 2156 2 +2157 1 2157 2 +2158 1 2158 2 +2159 1 2159 2 +2160 1 2160 2 +2161 1 2161 2 +2162 1 2162 2 +2163 1 2163 2 +2164 1 2164 2 +2165 1 2165 2 +2166 1 2166 2 +2167 1 2167 2 +2168 1 2168 2 +2169 1 2169 2 +2170 1 2170 2 +2171 1 2171 2 +2172 1 2172 2 +2173 1 2173 2 +2174 1 2174 2 +2175 1 2175 2 + 128 27 29 0 + 129 27 29 1 + 130 27 28 0 + 131 27 28 1 + 132 26 29 0 + 133 26 29 1 + 134 26 28 0 + 135 26 28 1 + 136 24 28 1 + 137 24 28 0 + 138 24 29 1 + 139 24 29 0 + 140 25 28 1 + 141 25 28 0 + 142 25 29 1 + 143 25 29 0 + 160 19 28 0 + 161 19 28 1 + 162 19 29 0 + 163 19 29 1 + 164 18 28 0 + 165 18 28 1 + 166 18 29 0 + 167 18 29 1 + 168 16 29 1 + 169 16 29 0 + 170 16 28 1 + 171 16 28 0 + 172 17 29 1 + 173 17 29 0 + 174 17 28 1 + 175 17 28 0 + 176 20 28 0 + 177 20 28 1 + 178 20 29 0 + 179 20 29 1 + 180 21 28 0 + 181 21 28 1 + 182 21 29 0 + 183 21 29 1 + 184 23 29 1 + 185 23 29 0 + 186 23 28 1 + 187 23 28 0 + 188 22 29 1 + 189 22 29 0 + 190 22 28 1 + 191 22 28 0 + 192 28 29 0 + 193 28 29 1 + 194 28 28 0 + 195 28 28 1 + 196 29 29 0 + 197 29 29 1 + 198 29 28 0 + 199 29 28 1 + 200 31 28 1 + 201 31 28 0 + 202 31 29 1 + 203 31 29 0 + 204 30 28 1 + 205 30 28 0 + 206 30 29 1 + 207 30 29 0 + 256 27 31 0 + 257 27 31 1 + 258 27 30 0 + 259 27 30 1 + 260 26 31 0 + 261 26 31 1 + 262 26 30 0 + 263 26 30 1 + 264 24 30 1 + 265 24 30 0 + 266 24 31 1 + 267 24 31 0 + 268 25 30 1 + 269 25 30 0 + 270 25 31 1 + 271 25 31 0 + 288 19 30 0 + 289 19 30 1 + 290 19 31 0 + 291 19 31 1 + 292 18 30 0 + 293 18 30 1 + 294 18 31 0 + 295 18 31 1 + 296 16 31 1 + 297 16 31 0 + 298 16 30 1 + 299 16 30 0 + 300 17 31 1 + 301 17 31 0 + 302 17 30 1 + 303 17 30 0 + 304 20 30 0 + 305 20 30 1 + 306 20 31 0 + 307 20 31 1 + 308 21 30 0 + 309 21 30 1 + 310 21 31 0 + 311 21 31 1 + 312 23 31 1 + 313 23 31 0 + 314 23 30 1 + 315 23 30 0 + 316 22 31 1 + 317 22 31 0 + 318 22 30 1 + 319 22 30 0 + 320 28 31 0 + 321 28 31 1 + 322 28 30 0 + 323 28 30 1 + 324 29 31 0 + 325 29 31 1 + 326 29 30 0 + 327 29 30 1 + 328 31 30 1 + 329 31 30 0 + 330 31 31 1 + 331 31 31 0 + 332 30 30 1 + 333 30 30 0 + 334 30 31 1 + 335 30 31 0 + 384 27 33 0 + 385 27 33 1 + 386 27 32 0 + 387 27 32 1 + 388 26 33 0 + 389 26 33 1 + 390 26 32 0 + 391 26 32 1 + 392 24 32 1 + 393 24 32 0 + 394 24 33 1 + 395 24 33 0 + 396 25 32 1 + 397 25 32 0 + 398 25 33 1 + 399 25 33 0 + 416 19 32 0 + 417 19 32 1 + 418 19 33 0 + 419 19 33 1 + 420 18 32 0 + 421 18 32 1 + 422 18 33 0 + 423 18 33 1 + 424 16 33 1 + 425 16 33 0 + 426 16 32 1 + 427 16 32 0 + 428 17 33 1 + 429 17 33 0 + 430 17 32 1 + 431 17 32 0 + 432 20 32 0 + 433 20 32 1 + 434 20 33 0 + 435 20 33 1 + 436 21 32 0 + 437 21 32 1 + 438 21 33 0 + 439 21 33 1 + 440 23 33 1 + 441 23 33 0 + 442 23 32 1 + 443 23 32 0 + 444 22 33 1 + 445 22 33 0 + 446 22 32 1 + 447 22 32 0 + 448 28 33 0 + 449 28 33 1 + 450 28 32 0 + 451 28 32 1 + 452 29 33 0 + 453 29 33 1 + 454 29 32 0 + 455 29 32 1 + 456 31 32 1 + 457 31 32 0 + 458 31 33 1 + 459 31 33 0 + 460 30 32 1 + 461 30 32 0 + 462 30 33 1 + 463 30 33 0 + 512 27 35 0 + 513 27 35 1 + 514 27 34 0 + 515 27 34 1 + 516 26 35 0 + 517 26 35 1 + 518 26 34 0 + 519 26 34 1 + 520 24 34 1 + 521 24 34 0 + 522 24 35 1 + 523 24 35 0 + 524 25 34 1 + 525 25 34 0 + 526 25 35 1 + 527 25 35 0 + 544 19 34 0 + 545 19 34 1 + 546 19 35 0 + 547 19 35 1 + 548 18 34 0 + 549 18 34 1 + 550 18 35 0 + 551 18 35 1 + 552 16 35 1 + 553 16 35 0 + 554 16 34 1 + 555 16 34 0 + 556 17 35 1 + 557 17 35 0 + 558 17 34 1 + 559 17 34 0 + 560 20 34 0 + 561 20 34 1 + 562 20 35 0 + 563 20 35 1 + 564 21 34 0 + 565 21 34 1 + 566 21 35 0 + 567 21 35 1 + 568 23 35 1 + 569 23 35 0 + 570 23 34 1 + 571 23 34 0 + 572 22 35 1 + 573 22 35 0 + 574 22 34 1 + 575 22 34 0 + 576 28 35 0 + 577 28 35 1 + 578 28 34 0 + 579 28 34 1 + 580 29 35 0 + 581 29 35 1 + 582 29 34 0 + 583 29 34 1 + 584 31 34 1 + 585 31 34 0 + 586 31 35 1 + 587 31 35 0 + 588 30 34 1 + 589 30 34 0 + 590 30 35 1 + 591 30 35 0 + 640 27 37 0 + 641 27 37 1 + 642 27 36 0 + 643 27 36 1 + 644 26 37 0 + 645 26 37 1 + 646 26 36 0 + 647 26 36 1 + 648 24 36 1 + 649 24 36 0 + 650 24 37 1 + 651 24 37 0 + 652 25 36 1 + 653 25 36 0 + 654 25 37 1 + 655 25 37 0 + 672 19 36 0 + 673 19 36 1 + 674 19 37 0 + 675 19 37 1 + 676 18 36 0 + 677 18 36 1 + 678 18 37 0 + 679 18 37 1 + 680 16 37 1 + 681 16 37 0 + 682 16 36 1 + 683 16 36 0 + 684 17 37 1 + 685 17 37 0 + 686 17 36 1 + 687 17 36 0 + 688 20 36 0 + 689 20 36 1 + 690 20 37 0 + 691 20 37 1 + 692 21 36 0 + 693 21 36 1 + 694 21 37 0 + 695 21 37 1 + 696 23 37 1 + 697 23 37 0 + 698 23 36 1 + 699 23 36 0 + 700 22 37 1 + 701 22 37 0 + 702 22 36 1 + 703 22 36 0 + 704 28 37 0 + 705 28 37 1 + 706 28 36 0 + 707 28 36 1 + 708 29 37 0 + 709 29 37 1 + 710 29 36 0 + 711 29 36 1 + 712 31 36 1 + 713 31 36 0 + 714 31 37 1 + 715 31 37 0 + 716 30 36 1 + 717 30 36 0 + 718 30 37 1 + 719 30 37 0 + 768 27 39 0 + 769 27 39 1 + 770 27 38 0 + 771 27 38 1 + 772 26 39 0 + 773 26 39 1 + 774 26 38 0 + 775 26 38 1 + 776 24 38 1 + 777 24 38 0 + 778 24 39 1 + 779 24 39 0 + 780 25 38 1 + 781 25 38 0 + 782 25 39 1 + 783 25 39 0 + 800 19 38 0 + 801 19 38 1 + 802 19 39 0 + 803 19 39 1 + 804 18 38 0 + 805 18 38 1 + 806 18 39 0 + 807 18 39 1 + 808 16 39 1 + 809 16 39 0 + 810 16 38 1 + 811 16 38 0 + 812 17 39 1 + 813 17 39 0 + 814 17 38 1 + 815 17 38 0 + 816 20 38 0 + 817 20 38 1 + 818 20 39 0 + 819 20 39 1 + 820 21 38 0 + 821 21 38 1 + 822 21 39 0 + 823 21 39 1 + 824 23 39 1 + 825 23 39 0 + 826 23 38 1 + 827 23 38 0 + 828 22 39 1 + 829 22 39 0 + 830 22 38 1 + 831 22 38 0 + 832 28 39 0 + 833 28 39 1 + 834 28 38 0 + 835 28 38 1 + 836 29 39 0 + 837 29 39 1 + 838 29 38 0 + 839 29 38 1 + 840 31 38 1 + 841 31 38 0 + 842 31 39 1 + 843 31 39 0 + 844 30 38 1 + 845 30 38 0 + 846 30 39 1 + 847 30 39 0 + 896 27 41 0 + 897 27 41 1 + 898 27 40 0 + 899 27 40 1 + 900 26 41 0 + 901 26 41 1 + 902 26 40 0 + 903 26 40 1 + 904 24 40 1 + 905 24 40 0 + 906 24 41 1 + 907 24 41 0 + 908 25 40 1 + 909 25 40 0 + 910 25 41 1 + 911 25 41 0 + 928 19 40 0 + 929 19 40 1 + 930 19 41 0 + 931 19 41 1 + 932 18 40 0 + 933 18 40 1 + 934 18 41 0 + 935 18 41 1 + 936 16 41 1 + 937 16 41 0 + 938 16 40 1 + 939 16 40 0 + 940 17 41 1 + 941 17 41 0 + 942 17 40 1 + 943 17 40 0 + 944 20 40 0 + 945 20 40 1 + 946 20 41 0 + 947 20 41 1 + 948 21 40 0 + 949 21 40 1 + 950 21 41 0 + 951 21 41 1 + 952 23 41 1 + 953 23 41 0 + 954 23 40 1 + 955 23 40 0 + 956 22 41 1 + 957 22 41 0 + 958 22 40 1 + 959 22 40 0 + 960 28 41 0 + 961 28 41 1 + 962 28 40 0 + 963 28 40 1 + 964 29 41 0 + 965 29 41 1 + 966 29 40 0 + 967 29 40 1 + 968 31 40 1 + 969 31 40 0 + 970 31 41 1 + 971 31 41 0 + 972 30 40 1 + 973 30 40 0 + 974 30 41 1 + 975 30 41 0 +1024 27 43 0 +1025 27 43 1 +1026 27 42 0 +1027 27 42 1 +1028 26 43 0 +1029 26 43 1 +1030 26 42 0 +1031 26 42 1 +1032 24 42 1 +1033 24 42 0 +1034 24 43 1 +1035 24 43 0 +1036 25 42 1 +1037 25 42 0 +1038 25 43 1 +1039 25 43 0 +1056 19 42 0 +1057 19 42 1 +1058 19 43 0 +1059 19 43 1 +1060 18 42 0 +1061 18 42 1 +1062 18 43 0 +1063 18 43 1 +1064 16 43 1 +1065 16 43 0 +1066 16 42 1 +1067 16 42 0 +1068 17 43 1 +1069 17 43 0 +1070 17 42 1 +1071 17 42 0 +1072 20 42 0 +1073 20 42 1 +1074 20 43 0 +1075 20 43 1 +1076 21 42 0 +1077 21 42 1 +1078 21 43 0 +1079 21 43 1 +1080 23 43 1 +1081 23 43 0 +1082 23 42 1 +1083 23 42 0 +1084 22 43 1 +1085 22 43 0 +1086 22 42 1 +1087 22 42 0 +1088 28 43 0 +1089 28 43 1 +1090 28 42 0 +1091 28 42 1 +1092 29 43 0 +1093 29 43 1 +1094 29 42 0 +1095 29 42 1 +1096 31 42 1 +1097 31 42 0 +1098 31 43 1 +1099 31 43 0 +1100 30 42 1 +1101 30 42 0 +1102 30 43 1 +1103 30 43 0 +1152 27 45 0 +1153 27 45 1 +1154 27 44 0 +1155 27 44 1 +1156 26 45 0 +1157 26 45 1 +1158 26 44 0 +1159 26 44 1 +1160 24 44 1 +1161 24 44 0 +1162 24 45 1 +1163 24 45 0 +1164 25 44 1 +1165 25 44 0 +1166 25 45 1 +1167 25 45 0 +1184 19 44 0 +1185 19 44 1 +1186 19 45 0 +1187 19 45 1 +1188 18 44 0 +1189 18 44 1 +1190 18 45 0 +1191 18 45 1 +1192 16 45 1 +1193 16 45 0 +1194 16 44 1 +1195 16 44 0 +1196 17 45 1 +1197 17 45 0 +1198 17 44 1 +1199 17 44 0 +1200 20 44 0 +1201 20 44 1 +1202 20 45 0 +1203 20 45 1 +1204 21 44 0 +1205 21 44 1 +1206 21 45 0 +1207 21 45 1 +1208 23 45 1 +1209 23 45 0 +1210 23 44 1 +1211 23 44 0 +1212 22 45 1 +1213 22 45 0 +1214 22 44 1 +1215 22 44 0 +1216 28 45 0 +1217 28 45 1 +1218 28 44 0 +1219 28 44 1 +1220 29 45 0 +1221 29 45 1 +1222 29 44 0 +1223 29 44 1 +1224 31 44 1 +1225 31 44 0 +1226 31 45 1 +1227 31 45 0 +1228 30 44 1 +1229 30 44 0 +1230 30 45 1 +1231 30 45 0 +1280 27 47 0 +1281 27 47 1 +1282 27 46 0 +1283 27 46 1 +1284 26 47 0 +1285 26 47 1 +1286 26 46 0 +1287 26 46 1 +1288 24 46 1 +1289 24 46 0 +1290 24 47 1 +1291 24 47 0 +1292 25 46 1 +1293 25 46 0 +1294 25 47 1 +1295 25 47 0 +1312 19 46 0 +1313 19 46 1 +1314 19 47 0 +1315 19 47 1 +1316 18 46 0 +1317 18 46 1 +1318 18 47 0 +1319 18 47 1 +1320 16 47 1 +1321 16 47 0 +1322 16 46 1 +1323 16 46 0 +1324 17 47 1 +1325 17 47 0 +1326 17 46 1 +1327 17 46 0 +1328 20 46 0 +1329 20 46 1 +1330 20 47 0 +1331 20 47 1 +1332 21 46 0 +1333 21 46 1 +1334 21 47 0 +1335 21 47 1 +1336 23 47 1 +1337 23 47 0 +1338 23 46 1 +1339 23 46 0 +1340 22 47 1 +1341 22 47 0 +1342 22 46 1 +1343 22 46 0 +1344 28 47 0 +1345 28 47 1 +1346 28 46 0 +1347 28 46 1 +1348 29 47 0 +1349 29 47 1 +1350 29 46 0 +1351 29 46 1 +1352 31 46 1 +1353 31 46 0 +1354 31 47 1 +1355 31 47 0 +1356 30 46 1 +1357 30 46 0 +1358 30 47 1 +1359 30 47 0 +1408 27 49 0 +1409 27 49 1 +1410 27 48 0 +1411 27 48 1 +1412 26 49 0 +1413 26 49 1 +1414 26 48 0 +1415 26 48 1 +1416 24 48 1 +1417 24 48 0 +1418 24 49 1 +1419 24 49 0 +1420 25 48 1 +1421 25 48 0 +1422 25 49 1 +1423 25 49 0 +1440 19 48 0 +1441 19 48 1 +1442 19 49 0 +1443 19 49 1 +1444 18 48 0 +1445 18 48 1 +1446 18 49 0 +1447 18 49 1 +1448 16 49 1 +1449 16 49 0 +1450 16 48 1 +1451 16 48 0 +1452 17 49 1 +1453 17 49 0 +1454 17 48 1 +1455 17 48 0 +1456 20 48 0 +1457 20 48 1 +1458 20 49 0 +1459 20 49 1 +1460 21 48 0 +1461 21 48 1 +1462 21 49 0 +1463 21 49 1 +1464 23 49 1 +1465 23 49 0 +1466 23 48 1 +1467 23 48 0 +1468 22 49 1 +1469 22 49 0 +1470 22 48 1 +1471 22 48 0 +1472 28 49 0 +1473 28 49 1 +1474 28 48 0 +1475 28 48 1 +1476 29 49 0 +1477 29 49 1 +1478 29 48 0 +1479 29 48 1 +1480 31 48 1 +1481 31 48 0 +1482 31 49 1 +1483 31 49 0 +1484 30 48 1 +1485 30 48 0 +1486 30 49 1 +1487 30 49 0 +1536 27 51 0 +1537 27 51 1 +1538 27 50 0 +1539 27 50 1 +1540 26 51 0 +1541 26 51 1 +1542 26 50 0 +1543 26 50 1 +1544 24 50 1 +1545 24 50 0 +1546 24 51 1 +1547 24 51 0 +1548 25 50 1 +1549 25 50 0 +1550 25 51 1 +1551 25 51 0 +1568 19 50 0 +1569 19 50 1 +1570 19 51 0 +1571 19 51 1 +1572 18 50 0 +1573 18 50 1 +1574 18 51 0 +1575 18 51 1 +1576 16 51 1 +1577 16 51 0 +1578 16 50 1 +1579 16 50 0 +1580 17 51 1 +1581 17 51 0 +1582 17 50 1 +1583 17 50 0 +1584 20 50 0 +1585 20 50 1 +1586 20 51 0 +1587 20 51 1 +1588 21 50 0 +1589 21 50 1 +1590 21 51 0 +1591 21 51 1 +1592 23 51 1 +1593 23 51 0 +1594 23 50 1 +1595 23 50 0 +1596 22 51 1 +1597 22 51 0 +1598 22 50 1 +1599 22 50 0 +1600 28 51 0 +1601 28 51 1 +1602 28 50 0 +1603 28 50 1 +1604 29 51 0 +1605 29 51 1 +1606 29 50 0 +1607 29 50 1 +1608 31 50 1 +1609 31 50 0 +1610 31 51 1 +1611 31 51 0 +1612 30 50 1 +1613 30 50 0 +1614 30 51 1 +1615 30 51 0 +1664 27 53 0 +1665 27 53 1 +1666 27 52 0 +1667 27 52 1 +1668 26 53 0 +1669 26 53 1 +1670 26 52 0 +1671 26 52 1 +1672 24 52 1 +1673 24 52 0 +1674 24 53 1 +1675 24 53 0 +1676 25 52 1 +1677 25 52 0 +1678 25 53 1 +1679 25 53 0 +1696 19 52 0 +1697 19 52 1 +1698 19 53 0 +1699 19 53 1 +1700 18 52 0 +1701 18 52 1 +1702 18 53 0 +1703 18 53 1 +1704 16 53 1 +1705 16 53 0 +1706 16 52 1 +1707 16 52 0 +1708 17 53 1 +1709 17 53 0 +1710 17 52 1 +1711 17 52 0 +1712 20 52 0 +1713 20 52 1 +1714 20 53 0 +1715 20 53 1 +1716 21 52 0 +1717 21 52 1 +1718 21 53 0 +1719 21 53 1 +1720 23 53 1 +1721 23 53 0 +1722 23 52 1 +1723 23 52 0 +1724 22 53 1 +1725 22 53 0 +1726 22 52 1 +1727 22 52 0 +1728 28 53 0 +1729 28 53 1 +1730 28 52 0 +1731 28 52 1 +1732 29 53 0 +1733 29 53 1 +1734 29 52 0 +1735 29 52 1 +1736 31 52 1 +1737 31 52 0 +1738 31 53 1 +1739 31 53 0 +1740 30 52 1 +1741 30 52 0 +1742 30 53 1 +1743 30 53 0 +1792 27 55 0 +1793 27 55 1 +1794 27 54 0 +1795 27 54 1 +1796 26 55 0 +1797 26 55 1 +1798 26 54 0 +1799 26 54 1 +1800 24 54 1 +1801 24 54 0 +1802 24 55 1 +1803 24 55 0 +1804 25 54 1 +1805 25 54 0 +1806 25 55 1 +1807 25 55 0 +1824 19 54 0 +1825 19 54 1 +1826 19 55 0 +1827 19 55 1 +1828 18 54 0 +1829 18 54 1 +1830 18 55 0 +1831 18 55 1 +1832 16 55 1 +1833 16 55 0 +1834 16 54 1 +1835 16 54 0 +1836 17 55 1 +1837 17 55 0 +1838 17 54 1 +1839 17 54 0 +1840 20 54 0 +1841 20 54 1 +1842 20 55 0 +1843 20 55 1 +1844 21 54 0 +1845 21 54 1 +1846 21 55 0 +1847 21 55 1 +1848 23 55 1 +1849 23 55 0 +1850 23 54 1 +1851 23 54 0 +1852 22 55 1 +1853 22 55 0 +1854 22 54 1 +1855 22 54 0 +1856 28 55 0 +1857 28 55 1 +1858 28 54 0 +1859 28 54 1 +1860 29 55 0 +1861 29 55 1 +1862 29 54 0 +1863 29 54 1 +1864 31 54 1 +1865 31 54 0 +1866 31 55 1 +1867 31 55 0 +1868 30 54 1 +1869 30 54 0 +1870 30 55 1 +1871 30 55 0 +2176 27 27 0 +2177 27 27 1 +2178 27 26 0 +2179 27 26 1 +2180 26 27 0 +2181 26 27 1 +2182 26 26 0 +2183 26 26 1 +2184 24 26 1 +2185 24 26 0 +2186 24 27 1 +2187 24 27 0 +2188 25 26 1 +2189 25 26 0 +2190 25 27 1 +2191 25 27 0 +2208 19 26 0 +2209 19 26 1 +2210 19 27 0 +2211 19 27 1 +2212 18 26 0 +2213 18 26 1 +2214 18 27 0 +2215 18 27 1 +2216 16 27 1 +2217 16 27 0 +2218 16 26 1 +2219 16 26 0 +2220 17 27 1 +2221 17 27 0 +2222 17 26 1 +2223 17 26 0 +2224 20 26 0 +2225 20 26 1 +2226 20 27 0 +2227 20 27 1 +2228 21 26 0 +2229 21 26 1 +2230 21 27 0 +2231 21 27 1 +2232 23 27 1 +2233 23 27 0 +2234 23 26 1 +2235 23 26 0 +2236 22 27 1 +2237 22 27 0 +2238 22 26 1 +2239 22 26 0 +2240 28 27 0 +2241 28 27 1 +2242 28 26 0 +2243 28 26 1 +2244 29 27 0 +2245 29 27 1 +2246 29 26 0 +2247 29 26 1 +2248 31 26 1 +2249 31 26 0 +2250 31 27 1 +2251 31 27 0 +2252 30 26 1 +2253 30 26 0 +2254 30 27 1 +2255 30 27 0 +2304 27 25 0 +2305 27 25 1 +2306 27 24 0 +2307 27 24 1 +2308 26 25 0 +2309 26 25 1 +2310 26 24 0 +2311 26 24 1 +2312 24 24 1 +2313 24 24 0 +2314 24 25 1 +2315 24 25 0 +2316 25 24 1 +2317 25 24 0 +2318 25 25 1 +2319 25 25 0 +2336 19 24 0 +2337 19 24 1 +2338 19 25 0 +2339 19 25 1 +2340 18 24 0 +2341 18 24 1 +2342 18 25 0 +2343 18 25 1 +2344 16 25 1 +2345 16 25 0 +2346 16 24 1 +2347 16 24 0 +2348 17 25 1 +2349 17 25 0 +2350 17 24 1 +2351 17 24 0 +2352 20 24 0 +2353 20 24 1 +2354 20 25 0 +2355 20 25 1 +2356 21 24 0 +2357 21 24 1 +2358 21 25 0 +2359 21 25 1 +2360 23 25 1 +2361 23 25 0 +2362 23 24 1 +2363 23 24 0 +2364 22 25 1 +2365 22 25 0 +2366 22 24 1 +2367 22 24 0 +2368 28 25 0 +2369 28 25 1 +2370 28 24 0 +2371 28 24 1 +2372 29 25 0 +2373 29 25 1 +2374 29 24 0 +2375 29 24 1 +2376 31 24 1 +2377 31 24 0 +2378 31 25 1 +2379 31 25 0 +2380 30 24 1 +2381 30 24 0 +2382 30 25 1 +2383 30 25 0 +2432 27 23 0 +2433 27 23 1 +2434 27 22 0 +2435 27 22 1 +2436 26 23 0 +2437 26 23 1 +2438 26 22 0 +2439 26 22 1 +2440 24 22 1 +2441 24 22 0 +2442 24 23 1 +2443 24 23 0 +2444 25 22 1 +2445 25 22 0 +2446 25 23 1 +2447 25 23 0 +2464 19 22 0 +2465 19 22 1 +2466 19 23 0 +2467 19 23 1 +2468 18 22 0 +2469 18 22 1 +2470 18 23 0 +2471 18 23 1 +2472 16 23 1 +2473 16 23 0 +2474 16 22 1 +2475 16 22 0 +2476 17 23 1 +2477 17 23 0 +2478 17 22 1 +2479 17 22 0 +2480 20 22 0 +2481 20 22 1 +2482 20 23 0 +2483 20 23 1 +2484 21 22 0 +2485 21 22 1 +2486 21 23 0 +2487 21 23 1 +2488 23 23 1 +2489 23 23 0 +2490 23 22 1 +2491 23 22 0 +2492 22 23 1 +2493 22 23 0 +2494 22 22 1 +2495 22 22 0 +2496 28 23 0 +2497 28 23 1 +2498 28 22 0 +2499 28 22 1 +2500 29 23 0 +2501 29 23 1 +2502 29 22 0 +2503 29 22 1 +2504 31 22 1 +2505 31 22 0 +2506 31 23 1 +2507 31 23 0 +2508 30 22 1 +2509 30 22 0 +2510 30 23 1 +2511 30 23 0 +2560 27 21 0 +2561 27 21 1 +2562 27 20 0 +2563 27 20 1 +2564 26 21 0 +2565 26 21 1 +2566 26 20 0 +2567 26 20 1 +2568 24 20 1 +2569 24 20 0 +2570 24 21 1 +2571 24 21 0 +2572 25 20 1 +2573 25 20 0 +2574 25 21 1 +2575 25 21 0 +2592 19 20 0 +2593 19 20 1 +2594 19 21 0 +2595 19 21 1 +2596 18 20 0 +2597 18 20 1 +2598 18 21 0 +2599 18 21 1 +2600 16 21 1 +2601 16 21 0 +2602 16 20 1 +2603 16 20 0 +2604 17 21 1 +2605 17 21 0 +2606 17 20 1 +2607 17 20 0 +2608 20 20 0 +2609 20 20 1 +2610 20 21 0 +2611 20 21 1 +2612 21 20 0 +2613 21 20 1 +2614 21 21 0 +2615 21 21 1 +2616 23 21 1 +2617 23 21 0 +2618 23 20 1 +2619 23 20 0 +2620 22 21 1 +2621 22 21 0 +2622 22 20 1 +2623 22 20 0 +2624 28 21 0 +2625 28 21 1 +2626 28 20 0 +2627 28 20 1 +2628 29 21 0 +2629 29 21 1 +2630 29 20 0 +2631 29 20 1 +2632 31 20 1 +2633 31 20 0 +2634 31 21 1 +2635 31 21 0 +2636 30 20 1 +2637 30 20 0 +2638 30 21 1 +2639 30 21 0 +2688 27 19 0 +2689 27 19 1 +2690 27 18 0 +2691 27 18 1 +2692 26 19 0 +2693 26 19 1 +2694 26 18 0 +2695 26 18 1 +2696 24 18 1 +2697 24 18 0 +2698 24 19 1 +2699 24 19 0 +2700 25 18 1 +2701 25 18 0 +2702 25 19 1 +2703 25 19 0 +2720 19 18 0 +2721 19 18 1 +2722 19 19 0 +2723 19 19 1 +2724 18 18 0 +2725 18 18 1 +2726 18 19 0 +2727 18 19 1 +2728 16 19 1 +2729 16 19 0 +2730 16 18 1 +2731 16 18 0 +2732 17 19 1 +2733 17 19 0 +2734 17 18 1 +2735 17 18 0 +2736 20 18 0 +2737 20 18 1 +2738 20 19 0 +2739 20 19 1 +2740 21 18 0 +2741 21 18 1 +2742 21 19 0 +2743 21 19 1 +2744 23 19 1 +2745 23 19 0 +2746 23 18 1 +2747 23 18 0 +2748 22 19 1 +2749 22 19 0 +2750 22 18 1 +2751 22 18 0 +2752 28 19 0 +2753 28 19 1 +2754 28 18 0 +2755 28 18 1 +2756 29 19 0 +2757 29 19 1 +2758 29 18 0 +2759 29 18 1 +2760 31 18 1 +2761 31 18 0 +2762 31 19 1 +2763 31 19 0 +2764 30 18 1 +2765 30 18 0 +2766 30 19 1 +2767 30 19 0 +2816 27 17 0 +2817 27 17 1 +2818 27 16 0 +2819 27 16 1 +2820 26 17 0 +2821 26 17 1 +2822 26 16 0 +2823 26 16 1 +2824 24 16 1 +2825 24 16 0 +2826 24 17 1 +2827 24 17 0 +2828 25 16 1 +2829 25 16 0 +2830 25 17 1 +2831 25 17 0 +2848 19 16 0 +2849 19 16 1 +2850 19 17 0 +2851 19 17 1 +2852 18 16 0 +2853 18 16 1 +2854 18 17 0 +2855 18 17 1 +2856 16 17 1 +2857 16 17 0 +2858 16 16 1 +2859 16 16 0 +2860 17 17 1 +2861 17 17 0 +2862 17 16 1 +2863 17 16 0 +2864 20 16 0 +2865 20 16 1 +2866 20 17 0 +2867 20 17 1 +2868 21 16 0 +2869 21 16 1 +2870 21 17 0 +2871 21 17 1 +2872 23 17 1 +2873 23 17 0 +2874 23 16 1 +2875 23 16 0 +2876 22 17 1 +2877 22 17 0 +2878 22 16 1 +2879 22 16 0 +2880 28 17 0 +2881 28 17 1 +2882 28 16 0 +2883 28 16 1 +2884 29 17 0 +2885 29 17 1 +2886 29 16 0 +2887 29 16 1 +2888 31 16 1 +2889 31 16 0 +2890 31 17 1 +2891 31 17 0 +2892 30 16 1 +2893 30 16 0 +2894 30 17 1 +2895 30 17 0 +2944 27 15 0 +2945 27 15 1 +2946 27 14 0 +2947 27 14 1 +2948 26 15 0 +2949 26 15 1 +2950 26 14 0 +2951 26 14 1 +2952 24 14 1 +2953 24 14 0 +2954 24 15 1 +2955 24 15 0 +2956 25 14 1 +2957 25 14 0 +2958 25 15 1 +2959 25 15 0 +2976 19 14 0 +2977 19 14 1 +2978 19 15 0 +2979 19 15 1 +2980 18 14 0 +2981 18 14 1 +2982 18 15 0 +2983 18 15 1 +2984 16 15 1 +2985 16 15 0 +2986 16 14 1 +2987 16 14 0 +2988 17 15 1 +2989 17 15 0 +2990 17 14 1 +2991 17 14 0 +2992 20 14 0 +2993 20 14 1 +2994 20 15 0 +2995 20 15 1 +2996 21 14 0 +2997 21 14 1 +2998 21 15 0 +2999 21 15 1 +3000 23 15 1 +3001 23 15 0 +3002 23 14 1 +3003 23 14 0 +3004 22 15 1 +3005 22 15 0 +3006 22 14 1 +3007 22 14 0 +3008 28 15 0 +3009 28 15 1 +3010 28 14 0 +3011 28 14 1 +3012 29 15 0 +3013 29 15 1 +3014 29 14 0 +3015 29 14 1 +3016 31 14 1 +3017 31 14 0 +3018 31 15 1 +3019 31 15 0 +3020 30 14 1 +3021 30 14 0 +3022 30 15 1 +3023 30 15 0 +3072 27 13 0 +3073 27 13 1 +3074 27 12 0 +3075 27 12 1 +3076 26 13 0 +3077 26 13 1 +3078 26 12 0 +3079 26 12 1 +3080 24 12 1 +3081 24 12 0 +3082 24 13 1 +3083 24 13 0 +3084 25 12 1 +3085 25 12 0 +3086 25 13 1 +3087 25 13 0 +3104 19 12 0 +3105 19 12 1 +3106 19 13 0 +3107 19 13 1 +3108 18 12 0 +3109 18 12 1 +3110 18 13 0 +3111 18 13 1 +3112 16 13 1 +3113 16 13 0 +3114 16 12 1 +3115 16 12 0 +3116 17 13 1 +3117 17 13 0 +3118 17 12 1 +3119 17 12 0 +3120 20 12 0 +3121 20 12 1 +3122 20 13 0 +3123 20 13 1 +3124 21 12 0 +3125 21 12 1 +3126 21 13 0 +3127 21 13 1 +3128 23 13 1 +3129 23 13 0 +3130 23 12 1 +3131 23 12 0 +3132 22 13 1 +3133 22 13 0 +3134 22 12 1 +3135 22 12 0 +3136 28 13 0 +3137 28 13 1 +3138 28 12 0 +3139 28 12 1 +3140 29 13 0 +3141 29 13 1 +3142 29 12 0 +3143 29 12 1 +3144 31 12 1 +3145 31 12 0 +3146 31 13 1 +3147 31 13 0 +3148 30 12 1 +3149 30 12 0 +3150 30 13 1 +3151 30 13 0 +3200 27 11 0 +3201 27 11 1 +3202 27 10 0 +3203 27 10 1 +3204 26 11 0 +3205 26 11 1 +3206 26 10 0 +3207 26 10 1 +3208 24 10 1 +3209 24 10 0 +3210 24 11 1 +3211 24 11 0 +3212 25 10 1 +3213 25 10 0 +3214 25 11 1 +3215 25 11 0 +3232 19 10 0 +3233 19 10 1 +3234 19 11 0 +3235 19 11 1 +3236 18 10 0 +3237 18 10 1 +3238 18 11 0 +3239 18 11 1 +3240 16 11 1 +3241 16 11 0 +3242 16 10 1 +3243 16 10 0 +3244 17 11 1 +3245 17 11 0 +3246 17 10 1 +3247 17 10 0 +3248 20 10 0 +3249 20 10 1 +3250 20 11 0 +3251 20 11 1 +3252 21 10 0 +3253 21 10 1 +3254 21 11 0 +3255 21 11 1 +3256 23 11 1 +3257 23 11 0 +3258 23 10 1 +3259 23 10 0 +3260 22 11 1 +3261 22 11 0 +3262 22 10 1 +3263 22 10 0 +3264 28 11 0 +3265 28 11 1 +3266 28 10 0 +3267 28 10 1 +3268 29 11 0 +3269 29 11 1 +3270 29 10 0 +3271 29 10 1 +3272 31 10 1 +3273 31 10 0 +3274 31 11 1 +3275 31 11 0 +3276 30 10 1 +3277 30 10 0 +3278 30 11 1 +3279 30 11 0 +3328 27 9 0 +3329 27 9 1 +3330 27 8 0 +3331 27 8 1 +3332 26 9 0 +3333 26 9 1 +3334 26 8 0 +3335 26 8 1 +3336 24 8 1 +3337 24 8 0 +3338 24 9 1 +3339 24 9 0 +3340 25 8 1 +3341 25 8 0 +3342 25 9 1 +3343 25 9 0 +3360 19 8 0 +3361 19 8 1 +3362 19 9 0 +3363 19 9 1 +3364 18 8 0 +3365 18 8 1 +3366 18 9 0 +3367 18 9 1 +3368 16 9 1 +3369 16 9 0 +3370 16 8 1 +3371 16 8 0 +3372 17 9 1 +3373 17 9 0 +3374 17 8 1 +3375 17 8 0 +3376 20 8 0 +3377 20 8 1 +3378 20 9 0 +3379 20 9 1 +3380 21 8 0 +3381 21 8 1 +3382 21 9 0 +3383 21 9 1 +3384 23 9 1 +3385 23 9 0 +3386 23 8 1 +3387 23 8 0 +3388 22 9 1 +3389 22 9 0 +3390 22 8 1 +3391 22 8 0 +3392 28 9 0 +3393 28 9 1 +3394 28 8 0 +3395 28 8 1 +3396 29 9 0 +3397 29 9 1 +3398 29 8 0 +3399 29 8 1 +3400 31 8 1 +3401 31 8 0 +3402 31 9 1 +3403 31 9 0 +3404 30 8 1 +3405 30 8 0 +3406 30 9 1 +3407 30 9 0 +3456 27 7 0 +3457 27 7 1 +3458 27 6 0 +3459 27 6 1 +3460 26 7 0 +3461 26 7 1 +3462 26 6 0 +3463 26 6 1 +3464 24 6 1 +3465 24 6 0 +3466 24 7 1 +3467 24 7 0 +3468 25 6 1 +3469 25 6 0 +3470 25 7 1 +3471 25 7 0 +3488 19 6 0 +3489 19 6 1 +3490 19 7 0 +3491 19 7 1 +3492 18 6 0 +3493 18 6 1 +3494 18 7 0 +3495 18 7 1 +3496 16 7 1 +3497 16 7 0 +3498 16 6 1 +3499 16 6 0 +3500 17 7 1 +3501 17 7 0 +3502 17 6 1 +3503 17 6 0 +3504 20 6 0 +3505 20 6 1 +3506 20 7 0 +3507 20 7 1 +3508 21 6 0 +3509 21 6 1 +3510 21 7 0 +3511 21 7 1 +3512 23 7 1 +3513 23 7 0 +3514 23 6 1 +3515 23 6 0 +3516 22 7 1 +3517 22 7 0 +3518 22 6 1 +3519 22 6 0 +3520 28 7 0 +3521 28 7 1 +3522 28 6 0 +3523 28 6 1 +3524 29 7 0 +3525 29 7 1 +3526 29 6 0 +3527 29 6 1 +3528 31 6 1 +3529 31 6 0 +3530 31 7 1 +3531 31 7 0 +3532 30 6 1 +3533 30 6 0 +3534 30 7 1 +3535 30 7 0 +3584 27 5 0 +3585 27 5 1 +3586 27 4 0 +3587 27 4 1 +3588 26 5 0 +3589 26 5 1 +3590 26 4 0 +3591 26 4 1 +3592 24 4 1 +3593 24 4 0 +3594 24 5 1 +3595 24 5 0 +3596 25 4 1 +3597 25 4 0 +3598 25 5 1 +3599 25 5 0 +3616 19 4 0 +3617 19 4 1 +3618 19 5 0 +3619 19 5 1 +3620 18 4 0 +3621 18 4 1 +3622 18 5 0 +3623 18 5 1 +3624 16 5 1 +3625 16 5 0 +3626 16 4 1 +3627 16 4 0 +3628 17 5 1 +3629 17 5 0 +3630 17 4 1 +3631 17 4 0 +3632 20 4 0 +3633 20 4 1 +3634 20 5 0 +3635 20 5 1 +3636 21 4 0 +3637 21 4 1 +3638 21 5 0 +3639 21 5 1 +3640 23 5 1 +3641 23 5 0 +3642 23 4 1 +3643 23 4 0 +3644 22 5 1 +3645 22 5 0 +3646 22 4 1 +3647 22 4 0 +3648 28 5 0 +3649 28 5 1 +3650 28 4 0 +3651 28 4 1 +3652 29 5 0 +3653 29 5 1 +3654 29 4 0 +3655 29 4 1 +3656 31 4 1 +3657 31 4 0 +3658 31 5 1 +3659 31 5 0 +3660 30 4 1 +3661 30 4 0 +3662 30 5 1 +3663 30 5 0 +3712 27 3 0 +3713 27 3 1 +3714 27 2 0 +3715 27 2 1 +3716 26 3 0 +3717 26 3 1 +3718 26 2 0 +3719 26 2 1 +3720 24 2 1 +3721 24 2 0 +3722 24 3 1 +3723 24 3 0 +3724 25 2 1 +3725 25 2 0 +3726 25 3 1 +3727 25 3 0 +3744 19 2 0 +3745 19 2 1 +3746 19 3 0 +3747 19 3 1 +3748 18 2 0 +3749 18 2 1 +3750 18 3 0 +3751 18 3 1 +3752 16 3 1 +3753 16 3 0 +3754 16 2 1 +3755 16 2 0 +3756 17 3 1 +3757 17 3 0 +3758 17 2 1 +3759 17 2 0 +3760 20 2 0 +3761 20 2 1 +3762 20 3 0 +3763 20 3 1 +3764 21 2 0 +3765 21 2 1 +3766 21 3 0 +3767 21 3 1 +3768 23 3 1 +3769 23 3 0 +3770 23 2 1 +3771 23 2 0 +3772 22 3 1 +3773 22 3 0 +3774 22 2 1 +3775 22 2 0 +3776 28 3 0 +3777 28 3 1 +3778 28 2 0 +3779 28 2 1 +3780 29 3 0 +3781 29 3 1 +3782 29 2 0 +3783 29 2 1 +3784 31 2 1 +3785 31 2 0 +3786 31 3 1 +3787 31 3 0 +3788 30 2 1 +3789 30 2 0 +3790 30 3 1 +3791 30 3 0 +3840 27 1 0 +3841 27 1 1 +3842 27 0 0 +3843 27 0 1 +3844 26 1 0 +3845 26 1 1 +3846 26 0 0 +3847 26 0 1 +3848 24 0 1 +3849 24 0 0 +3850 24 1 1 +3851 24 1 0 +3852 25 0 1 +3853 25 0 0 +3854 25 1 1 +3855 25 1 0 +3872 19 0 0 +3873 19 0 1 +3874 19 1 0 +3875 19 1 1 +3876 18 0 0 +3877 18 0 1 +3878 18 1 0 +3879 18 1 1 +3880 16 1 1 +3881 16 1 0 +3882 16 0 1 +3883 16 0 0 +3884 17 1 1 +3885 17 1 0 +3886 17 0 1 +3887 17 0 0 +3888 20 0 0 +3889 20 0 1 +3890 20 1 0 +3891 20 1 1 +3892 21 0 0 +3893 21 0 1 +3894 21 1 0 +3895 21 1 1 +3896 23 1 1 +3897 23 1 0 +3898 23 0 1 +3899 23 0 0 +3900 22 1 1 +3901 22 1 0 +3902 22 0 1 +3903 22 0 0 +3904 28 1 0 +3905 28 1 1 +3906 28 0 0 +3907 28 0 1 +3908 29 1 0 +3909 29 1 1 +3910 29 0 0 +3911 29 0 1 +3912 31 0 1 +3913 31 0 0 +3914 31 1 1 +3915 31 1 0 +3916 30 0 1 +3917 30 0 0 +3918 30 1 1 +3919 30 1 0 diff --git a/Detectors/PHOS/base/files/Mod1RCU2.data b/Detectors/PHOS/base/files/Mod1RCU2.data new file mode 100644 index 0000000000000..21de94d7ace53 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod1RCU2.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 2 0 2 + 1 2 1 2 + 2 2 2 2 + 3 2 3 2 + 4 2 4 2 + 5 2 5 2 + 6 2 6 2 + 7 2 7 2 + 8 2 8 2 + 9 2 9 2 + 10 2 10 2 + 11 2 11 2 + 12 2 12 2 + 13 2 13 2 + 14 2 14 2 + 15 2 15 2 + 16 2 16 2 + 17 2 17 2 + 18 2 18 2 + 19 2 19 2 + 20 2 20 2 + 21 2 21 2 + 22 2 22 2 + 23 2 23 2 + 24 2 24 2 + 25 2 25 2 + 26 2 26 2 + 27 2 27 2 + 28 2 28 2 + 29 2 29 2 + 30 2 30 2 + 31 2 31 2 + 32 2 32 2 + 33 2 33 2 + 34 2 34 2 + 35 2 35 2 + 36 2 36 2 + 37 2 37 2 + 38 2 38 2 + 39 2 39 2 + 40 2 40 2 + 41 2 41 2 + 42 2 42 2 + 43 2 43 2 + 44 2 44 2 + 45 2 45 2 + 46 2 46 2 + 47 2 47 2 + 48 2 48 2 + 49 2 49 2 + 50 2 50 2 + 51 2 51 2 + 52 2 52 2 + 53 2 53 2 + 54 2 54 2 + 55 2 55 2 + 56 2 56 2 + 57 2 57 2 + 58 2 58 2 + 59 2 59 2 + 60 2 60 2 + 61 2 61 2 + 62 2 62 2 + 63 2 63 2 + 64 2 64 2 + 65 2 65 2 + 66 2 66 2 + 67 2 67 2 + 68 2 68 2 + 69 2 69 2 + 70 2 70 2 + 71 2 71 2 + 72 2 72 2 + 73 2 73 2 + 74 2 74 2 + 75 2 75 2 + 76 2 76 2 + 77 2 77 2 + 78 2 78 2 + 79 2 79 2 + 80 2 80 2 + 81 2 81 2 + 82 2 82 2 + 83 2 83 2 + 84 2 84 2 + 85 2 85 2 + 86 2 86 2 + 87 2 87 2 + 88 2 88 2 + 89 2 89 2 + 90 2 90 2 + 91 2 91 2 + 92 2 92 2 + 93 2 93 2 + 94 2 94 2 + 95 2 95 2 + 96 2 96 2 + 97 2 97 2 + 98 2 98 2 + 99 2 99 2 + 100 2 100 2 + 101 2 101 2 + 102 2 102 2 + 103 2 103 2 + 104 2 104 2 + 105 2 105 2 + 106 2 106 2 + 107 2 107 2 + 108 2 108 2 + 109 2 109 2 + 110 2 110 2 + 111 2 111 2 + 112 2 112 2 + 113 2 113 2 + 114 2 114 2 + 115 2 115 2 + 116 2 116 2 + 117 2 117 2 + 118 2 118 2 + 119 2 119 2 + 120 2 120 2 + 121 2 121 2 + 122 2 122 2 + 123 2 123 2 + 124 2 124 2 + 125 2 125 2 + 126 2 126 2 + 127 2 127 2 +2048 2 2048 2 +2049 2 2049 2 +2050 2 2050 2 +2051 2 2051 2 +2052 2 2052 2 +2053 2 2053 2 +2054 2 2054 2 +2055 2 2055 2 +2056 2 2056 2 +2057 2 2057 2 +2058 2 2058 2 +2059 2 2059 2 +2060 2 2060 2 +2061 2 2061 2 +2062 2 2062 2 +2063 2 2063 2 +2064 2 2064 2 +2065 2 2065 2 +2066 2 2066 2 +2067 2 2067 2 +2068 2 2068 2 +2069 2 2069 2 +2070 2 2070 2 +2071 2 2071 2 +2072 2 2072 2 +2073 2 2073 2 +2074 2 2074 2 +2075 2 2075 2 +2076 2 2076 2 +2077 2 2077 2 +2078 2 2078 2 +2079 2 2079 2 +2080 2 2080 2 +2081 2 2081 2 +2082 2 2082 2 +2083 2 2083 2 +2084 2 2084 2 +2085 2 2085 2 +2086 2 2086 2 +2087 2 2087 2 +2088 2 2088 2 +2089 2 2089 2 +2090 2 2090 2 +2091 2 2091 2 +2092 2 2092 2 +2093 2 2093 2 +2094 2 2094 2 +2095 2 2095 2 +2096 2 2096 2 +2097 2 2097 2 +2098 2 2098 2 +2099 2 2099 2 +2100 2 2100 2 +2101 2 2101 2 +2102 2 2102 2 +2103 2 2103 2 +2104 2 2104 2 +2105 2 2105 2 +2106 2 2106 2 +2107 2 2107 2 +2108 2 2108 2 +2109 2 2109 2 +2110 2 2110 2 +2111 2 2111 2 +2112 2 2112 2 +2113 2 2113 2 +2114 2 2114 2 +2115 2 2115 2 +2116 2 2116 2 +2117 2 2117 2 +2118 2 2118 2 +2119 2 2119 2 +2120 2 2120 2 +2121 2 2121 2 +2122 2 2122 2 +2123 2 2123 2 +2124 2 2124 2 +2125 2 2125 2 +2126 2 2126 2 +2127 2 2127 2 +2128 2 2128 2 +2129 2 2129 2 +2130 2 2130 2 +2131 2 2131 2 +2132 2 2132 2 +2133 2 2133 2 +2134 2 2134 2 +2135 2 2135 2 +2136 2 2136 2 +2137 2 2137 2 +2138 2 2138 2 +2139 2 2139 2 +2140 2 2140 2 +2141 2 2141 2 +2142 2 2142 2 +2143 2 2143 2 +2144 2 2144 2 +2145 2 2145 2 +2146 2 2146 2 +2147 2 2147 2 +2148 2 2148 2 +2149 2 2149 2 +2150 2 2150 2 +2151 2 2151 2 +2152 2 2152 2 +2153 2 2153 2 +2154 2 2154 2 +2155 2 2155 2 +2156 2 2156 2 +2157 2 2157 2 +2158 2 2158 2 +2159 2 2159 2 +2160 2 2160 2 +2161 2 2161 2 +2162 2 2162 2 +2163 2 2163 2 +2164 2 2164 2 +2165 2 2165 2 +2166 2 2166 2 +2167 2 2167 2 +2168 2 2168 2 +2169 2 2169 2 +2170 2 2170 2 +2171 2 2171 2 +2172 2 2172 2 +2173 2 2173 2 +2174 2 2174 2 +2175 2 2175 2 + 128 43 29 0 + 129 43 29 1 + 130 43 28 0 + 131 43 28 1 + 132 42 29 0 + 133 42 29 1 + 134 42 28 0 + 135 42 28 1 + 136 40 28 1 + 137 40 28 0 + 138 40 29 1 + 139 40 29 0 + 140 41 28 1 + 141 41 28 0 + 142 41 29 1 + 143 41 29 0 + 160 35 28 0 + 161 35 28 1 + 162 35 29 0 + 163 35 29 1 + 164 34 28 0 + 165 34 28 1 + 166 34 29 0 + 167 34 29 1 + 168 32 29 1 + 169 32 29 0 + 170 32 28 1 + 171 32 28 0 + 172 33 29 1 + 173 33 29 0 + 174 33 28 1 + 175 33 28 0 + 176 36 28 0 + 177 36 28 1 + 178 36 29 0 + 179 36 29 1 + 180 37 28 0 + 181 37 28 1 + 182 37 29 0 + 183 37 29 1 + 184 39 29 1 + 185 39 29 0 + 186 39 28 1 + 187 39 28 0 + 188 38 29 1 + 189 38 29 0 + 190 38 28 1 + 191 38 28 0 + 192 44 29 0 + 193 44 29 1 + 194 44 28 0 + 195 44 28 1 + 196 45 29 0 + 197 45 29 1 + 198 45 28 0 + 199 45 28 1 + 200 47 28 1 + 201 47 28 0 + 202 47 29 1 + 203 47 29 0 + 204 46 28 1 + 205 46 28 0 + 206 46 29 1 + 207 46 29 0 + 256 43 31 0 + 257 43 31 1 + 258 43 30 0 + 259 43 30 1 + 260 42 31 0 + 261 42 31 1 + 262 42 30 0 + 263 42 30 1 + 264 40 30 1 + 265 40 30 0 + 266 40 31 1 + 267 40 31 0 + 268 41 30 1 + 269 41 30 0 + 270 41 31 1 + 271 41 31 0 + 288 35 30 0 + 289 35 30 1 + 290 35 31 0 + 291 35 31 1 + 292 34 30 0 + 293 34 30 1 + 294 34 31 0 + 295 34 31 1 + 296 32 31 1 + 297 32 31 0 + 298 32 30 1 + 299 32 30 0 + 300 33 31 1 + 301 33 31 0 + 302 33 30 1 + 303 33 30 0 + 304 36 30 0 + 305 36 30 1 + 306 36 31 0 + 307 36 31 1 + 308 37 30 0 + 309 37 30 1 + 310 37 31 0 + 311 37 31 1 + 312 39 31 1 + 313 39 31 0 + 314 39 30 1 + 315 39 30 0 + 316 38 31 1 + 317 38 31 0 + 318 38 30 1 + 319 38 30 0 + 320 44 31 0 + 321 44 31 1 + 322 44 30 0 + 323 44 30 1 + 324 45 31 0 + 325 45 31 1 + 326 45 30 0 + 327 45 30 1 + 328 47 30 1 + 329 47 30 0 + 330 47 31 1 + 331 47 31 0 + 332 46 30 1 + 333 46 30 0 + 334 46 31 1 + 335 46 31 0 + 384 43 33 0 + 385 43 33 1 + 386 43 32 0 + 387 43 32 1 + 388 42 33 0 + 389 42 33 1 + 390 42 32 0 + 391 42 32 1 + 392 40 32 1 + 393 40 32 0 + 394 40 33 1 + 395 40 33 0 + 396 41 32 1 + 397 41 32 0 + 398 41 33 1 + 399 41 33 0 + 416 35 32 0 + 417 35 32 1 + 418 35 33 0 + 419 35 33 1 + 420 34 32 0 + 421 34 32 1 + 422 34 33 0 + 423 34 33 1 + 424 32 33 1 + 425 32 33 0 + 426 32 32 1 + 427 32 32 0 + 428 33 33 1 + 429 33 33 0 + 430 33 32 1 + 431 33 32 0 + 432 36 32 0 + 433 36 32 1 + 434 36 33 0 + 435 36 33 1 + 436 37 32 0 + 437 37 32 1 + 438 37 33 0 + 439 37 33 1 + 440 39 33 1 + 441 39 33 0 + 442 39 32 1 + 443 39 32 0 + 444 38 33 1 + 445 38 33 0 + 446 38 32 1 + 447 38 32 0 + 448 44 33 0 + 449 44 33 1 + 450 44 32 0 + 451 44 32 1 + 452 45 33 0 + 453 45 33 1 + 454 45 32 0 + 455 45 32 1 + 456 47 32 1 + 457 47 32 0 + 458 47 33 1 + 459 47 33 0 + 460 46 32 1 + 461 46 32 0 + 462 46 33 1 + 463 46 33 0 + 512 43 35 0 + 513 43 35 1 + 514 43 34 0 + 515 43 34 1 + 516 42 35 0 + 517 42 35 1 + 518 42 34 0 + 519 42 34 1 + 520 40 34 1 + 521 40 34 0 + 522 40 35 1 + 523 40 35 0 + 524 41 34 1 + 525 41 34 0 + 526 41 35 1 + 527 41 35 0 + 544 35 34 0 + 545 35 34 1 + 546 35 35 0 + 547 35 35 1 + 548 34 34 0 + 549 34 34 1 + 550 34 35 0 + 551 34 35 1 + 552 32 35 1 + 553 32 35 0 + 554 32 34 1 + 555 32 34 0 + 556 33 35 1 + 557 33 35 0 + 558 33 34 1 + 559 33 34 0 + 560 36 34 0 + 561 36 34 1 + 562 36 35 0 + 563 36 35 1 + 564 37 34 0 + 565 37 34 1 + 566 37 35 0 + 567 37 35 1 + 568 39 35 1 + 569 39 35 0 + 570 39 34 1 + 571 39 34 0 + 572 38 35 1 + 573 38 35 0 + 574 38 34 1 + 575 38 34 0 + 576 44 35 0 + 577 44 35 1 + 578 44 34 0 + 579 44 34 1 + 580 45 35 0 + 581 45 35 1 + 582 45 34 0 + 583 45 34 1 + 584 47 34 1 + 585 47 34 0 + 586 47 35 1 + 587 47 35 0 + 588 46 34 1 + 589 46 34 0 + 590 46 35 1 + 591 46 35 0 + 640 43 37 0 + 641 43 37 1 + 642 43 36 0 + 643 43 36 1 + 644 42 37 0 + 645 42 37 1 + 646 42 36 0 + 647 42 36 1 + 648 40 36 1 + 649 40 36 0 + 650 40 37 1 + 651 40 37 0 + 652 41 36 1 + 653 41 36 0 + 654 41 37 1 + 655 41 37 0 + 672 35 36 0 + 673 35 36 1 + 674 35 37 0 + 675 35 37 1 + 676 34 36 0 + 677 34 36 1 + 678 34 37 0 + 679 34 37 1 + 680 32 37 1 + 681 32 37 0 + 682 32 36 1 + 683 32 36 0 + 684 33 37 1 + 685 33 37 0 + 686 33 36 1 + 687 33 36 0 + 688 36 36 0 + 689 36 36 1 + 690 36 37 0 + 691 36 37 1 + 692 37 36 0 + 693 37 36 1 + 694 37 37 0 + 695 37 37 1 + 696 39 37 1 + 697 39 37 0 + 698 39 36 1 + 699 39 36 0 + 700 38 37 1 + 701 38 37 0 + 702 38 36 1 + 703 38 36 0 + 704 44 37 0 + 705 44 37 1 + 706 44 36 0 + 707 44 36 1 + 708 45 37 0 + 709 45 37 1 + 710 45 36 0 + 711 45 36 1 + 712 47 36 1 + 713 47 36 0 + 714 47 37 1 + 715 47 37 0 + 716 46 36 1 + 717 46 36 0 + 718 46 37 1 + 719 46 37 0 + 768 43 39 0 + 769 43 39 1 + 770 43 38 0 + 771 43 38 1 + 772 42 39 0 + 773 42 39 1 + 774 42 38 0 + 775 42 38 1 + 776 40 38 1 + 777 40 38 0 + 778 40 39 1 + 779 40 39 0 + 780 41 38 1 + 781 41 38 0 + 782 41 39 1 + 783 41 39 0 + 800 35 38 0 + 801 35 38 1 + 802 35 39 0 + 803 35 39 1 + 804 34 38 0 + 805 34 38 1 + 806 34 39 0 + 807 34 39 1 + 808 32 39 1 + 809 32 39 0 + 810 32 38 1 + 811 32 38 0 + 812 33 39 1 + 813 33 39 0 + 814 33 38 1 + 815 33 38 0 + 816 36 38 0 + 817 36 38 1 + 818 36 39 0 + 819 36 39 1 + 820 37 38 0 + 821 37 38 1 + 822 37 39 0 + 823 37 39 1 + 824 39 39 1 + 825 39 39 0 + 826 39 38 1 + 827 39 38 0 + 828 38 39 1 + 829 38 39 0 + 830 38 38 1 + 831 38 38 0 + 832 44 39 0 + 833 44 39 1 + 834 44 38 0 + 835 44 38 1 + 836 45 39 0 + 837 45 39 1 + 838 45 38 0 + 839 45 38 1 + 840 47 38 1 + 841 47 38 0 + 842 47 39 1 + 843 47 39 0 + 844 46 38 1 + 845 46 38 0 + 846 46 39 1 + 847 46 39 0 + 896 43 41 0 + 897 43 41 1 + 898 43 40 0 + 899 43 40 1 + 900 42 41 0 + 901 42 41 1 + 902 42 40 0 + 903 42 40 1 + 904 40 40 1 + 905 40 40 0 + 906 40 41 1 + 907 40 41 0 + 908 41 40 1 + 909 41 40 0 + 910 41 41 1 + 911 41 41 0 + 928 35 40 0 + 929 35 40 1 + 930 35 41 0 + 931 35 41 1 + 932 34 40 0 + 933 34 40 1 + 934 34 41 0 + 935 34 41 1 + 936 32 41 1 + 937 32 41 0 + 938 32 40 1 + 939 32 40 0 + 940 33 41 1 + 941 33 41 0 + 942 33 40 1 + 943 33 40 0 + 944 36 40 0 + 945 36 40 1 + 946 36 41 0 + 947 36 41 1 + 948 37 40 0 + 949 37 40 1 + 950 37 41 0 + 951 37 41 1 + 952 39 41 1 + 953 39 41 0 + 954 39 40 1 + 955 39 40 0 + 956 38 41 1 + 957 38 41 0 + 958 38 40 1 + 959 38 40 0 + 960 44 41 0 + 961 44 41 1 + 962 44 40 0 + 963 44 40 1 + 964 45 41 0 + 965 45 41 1 + 966 45 40 0 + 967 45 40 1 + 968 47 40 1 + 969 47 40 0 + 970 47 41 1 + 971 47 41 0 + 972 46 40 1 + 973 46 40 0 + 974 46 41 1 + 975 46 41 0 +1024 43 43 0 +1025 43 43 1 +1026 43 42 0 +1027 43 42 1 +1028 42 43 0 +1029 42 43 1 +1030 42 42 0 +1031 42 42 1 +1032 40 42 1 +1033 40 42 0 +1034 40 43 1 +1035 40 43 0 +1036 41 42 1 +1037 41 42 0 +1038 41 43 1 +1039 41 43 0 +1056 35 42 0 +1057 35 42 1 +1058 35 43 0 +1059 35 43 1 +1060 34 42 0 +1061 34 42 1 +1062 34 43 0 +1063 34 43 1 +1064 32 43 1 +1065 32 43 0 +1066 32 42 1 +1067 32 42 0 +1068 33 43 1 +1069 33 43 0 +1070 33 42 1 +1071 33 42 0 +1072 36 42 0 +1073 36 42 1 +1074 36 43 0 +1075 36 43 1 +1076 37 42 0 +1077 37 42 1 +1078 37 43 0 +1079 37 43 1 +1080 39 43 1 +1081 39 43 0 +1082 39 42 1 +1083 39 42 0 +1084 38 43 1 +1085 38 43 0 +1086 38 42 1 +1087 38 42 0 +1088 44 43 0 +1089 44 43 1 +1090 44 42 0 +1091 44 42 1 +1092 45 43 0 +1093 45 43 1 +1094 45 42 0 +1095 45 42 1 +1096 47 42 1 +1097 47 42 0 +1098 47 43 1 +1099 47 43 0 +1100 46 42 1 +1101 46 42 0 +1102 46 43 1 +1103 46 43 0 +1152 43 45 0 +1153 43 45 1 +1154 43 44 0 +1155 43 44 1 +1156 42 45 0 +1157 42 45 1 +1158 42 44 0 +1159 42 44 1 +1160 40 44 1 +1161 40 44 0 +1162 40 45 1 +1163 40 45 0 +1164 41 44 1 +1165 41 44 0 +1166 41 45 1 +1167 41 45 0 +1184 35 44 0 +1185 35 44 1 +1186 35 45 0 +1187 35 45 1 +1188 34 44 0 +1189 34 44 1 +1190 34 45 0 +1191 34 45 1 +1192 32 45 1 +1193 32 45 0 +1194 32 44 1 +1195 32 44 0 +1196 33 45 1 +1197 33 45 0 +1198 33 44 1 +1199 33 44 0 +1200 36 44 0 +1201 36 44 1 +1202 36 45 0 +1203 36 45 1 +1204 37 44 0 +1205 37 44 1 +1206 37 45 0 +1207 37 45 1 +1208 39 45 1 +1209 39 45 0 +1210 39 44 1 +1211 39 44 0 +1212 38 45 1 +1213 38 45 0 +1214 38 44 1 +1215 38 44 0 +1216 44 45 0 +1217 44 45 1 +1218 44 44 0 +1219 44 44 1 +1220 45 45 0 +1221 45 45 1 +1222 45 44 0 +1223 45 44 1 +1224 47 44 1 +1225 47 44 0 +1226 47 45 1 +1227 47 45 0 +1228 46 44 1 +1229 46 44 0 +1230 46 45 1 +1231 46 45 0 +1280 43 47 0 +1281 43 47 1 +1282 43 46 0 +1283 43 46 1 +1284 42 47 0 +1285 42 47 1 +1286 42 46 0 +1287 42 46 1 +1288 40 46 1 +1289 40 46 0 +1290 40 47 1 +1291 40 47 0 +1292 41 46 1 +1293 41 46 0 +1294 41 47 1 +1295 41 47 0 +1312 35 46 0 +1313 35 46 1 +1314 35 47 0 +1315 35 47 1 +1316 34 46 0 +1317 34 46 1 +1318 34 47 0 +1319 34 47 1 +1320 32 47 1 +1321 32 47 0 +1322 32 46 1 +1323 32 46 0 +1324 33 47 1 +1325 33 47 0 +1326 33 46 1 +1327 33 46 0 +1328 36 46 0 +1329 36 46 1 +1330 36 47 0 +1331 36 47 1 +1332 37 46 0 +1333 37 46 1 +1334 37 47 0 +1335 37 47 1 +1336 39 47 1 +1337 39 47 0 +1338 39 46 1 +1339 39 46 0 +1340 38 47 1 +1341 38 47 0 +1342 38 46 1 +1343 38 46 0 +1344 44 47 0 +1345 44 47 1 +1346 44 46 0 +1347 44 46 1 +1348 45 47 0 +1349 45 47 1 +1350 45 46 0 +1351 45 46 1 +1352 47 46 1 +1353 47 46 0 +1354 47 47 1 +1355 47 47 0 +1356 46 46 1 +1357 46 46 0 +1358 46 47 1 +1359 46 47 0 +1408 43 49 0 +1409 43 49 1 +1410 43 48 0 +1411 43 48 1 +1412 42 49 0 +1413 42 49 1 +1414 42 48 0 +1415 42 48 1 +1416 40 48 1 +1417 40 48 0 +1418 40 49 1 +1419 40 49 0 +1420 41 48 1 +1421 41 48 0 +1422 41 49 1 +1423 41 49 0 +1440 35 48 0 +1441 35 48 1 +1442 35 49 0 +1443 35 49 1 +1444 34 48 0 +1445 34 48 1 +1446 34 49 0 +1447 34 49 1 +1448 32 49 1 +1449 32 49 0 +1450 32 48 1 +1451 32 48 0 +1452 33 49 1 +1453 33 49 0 +1454 33 48 1 +1455 33 48 0 +1456 36 48 0 +1457 36 48 1 +1458 36 49 0 +1459 36 49 1 +1460 37 48 0 +1461 37 48 1 +1462 37 49 0 +1463 37 49 1 +1464 39 49 1 +1465 39 49 0 +1466 39 48 1 +1467 39 48 0 +1468 38 49 1 +1469 38 49 0 +1470 38 48 1 +1471 38 48 0 +1472 44 49 0 +1473 44 49 1 +1474 44 48 0 +1475 44 48 1 +1476 45 49 0 +1477 45 49 1 +1478 45 48 0 +1479 45 48 1 +1480 47 48 1 +1481 47 48 0 +1482 47 49 1 +1483 47 49 0 +1484 46 48 1 +1485 46 48 0 +1486 46 49 1 +1487 46 49 0 +1536 43 51 0 +1537 43 51 1 +1538 43 50 0 +1539 43 50 1 +1540 42 51 0 +1541 42 51 1 +1542 42 50 0 +1543 42 50 1 +1544 40 50 1 +1545 40 50 0 +1546 40 51 1 +1547 40 51 0 +1548 41 50 1 +1549 41 50 0 +1550 41 51 1 +1551 41 51 0 +1568 35 50 0 +1569 35 50 1 +1570 35 51 0 +1571 35 51 1 +1572 34 50 0 +1573 34 50 1 +1574 34 51 0 +1575 34 51 1 +1576 32 51 1 +1577 32 51 0 +1578 32 50 1 +1579 32 50 0 +1580 33 51 1 +1581 33 51 0 +1582 33 50 1 +1583 33 50 0 +1584 36 50 0 +1585 36 50 1 +1586 36 51 0 +1587 36 51 1 +1588 37 50 0 +1589 37 50 1 +1590 37 51 0 +1591 37 51 1 +1592 39 51 1 +1593 39 51 0 +1594 39 50 1 +1595 39 50 0 +1596 38 51 1 +1597 38 51 0 +1598 38 50 1 +1599 38 50 0 +1600 44 51 0 +1601 44 51 1 +1602 44 50 0 +1603 44 50 1 +1604 45 51 0 +1605 45 51 1 +1606 45 50 0 +1607 45 50 1 +1608 47 50 1 +1609 47 50 0 +1610 47 51 1 +1611 47 51 0 +1612 46 50 1 +1613 46 50 0 +1614 46 51 1 +1615 46 51 0 +1664 43 53 0 +1665 43 53 1 +1666 43 52 0 +1667 43 52 1 +1668 42 53 0 +1669 42 53 1 +1670 42 52 0 +1671 42 52 1 +1672 40 52 1 +1673 40 52 0 +1674 40 53 1 +1675 40 53 0 +1676 41 52 1 +1677 41 52 0 +1678 41 53 1 +1679 41 53 0 +1696 35 52 0 +1697 35 52 1 +1698 35 53 0 +1699 35 53 1 +1700 34 52 0 +1701 34 52 1 +1702 34 53 0 +1703 34 53 1 +1704 32 53 1 +1705 32 53 0 +1706 32 52 1 +1707 32 52 0 +1708 33 53 1 +1709 33 53 0 +1710 33 52 1 +1711 33 52 0 +1712 36 52 0 +1713 36 52 1 +1714 36 53 0 +1715 36 53 1 +1716 37 52 0 +1717 37 52 1 +1718 37 53 0 +1719 37 53 1 +1720 39 53 1 +1721 39 53 0 +1722 39 52 1 +1723 39 52 0 +1724 38 53 1 +1725 38 53 0 +1726 38 52 1 +1727 38 52 0 +1728 44 53 0 +1729 44 53 1 +1730 44 52 0 +1731 44 52 1 +1732 45 53 0 +1733 45 53 1 +1734 45 52 0 +1735 45 52 1 +1736 47 52 1 +1737 47 52 0 +1738 47 53 1 +1739 47 53 0 +1740 46 52 1 +1741 46 52 0 +1742 46 53 1 +1743 46 53 0 +1792 43 55 0 +1793 43 55 1 +1794 43 54 0 +1795 43 54 1 +1796 42 55 0 +1797 42 55 1 +1798 42 54 0 +1799 42 54 1 +1800 40 54 1 +1801 40 54 0 +1802 40 55 1 +1803 40 55 0 +1804 41 54 1 +1805 41 54 0 +1806 41 55 1 +1807 41 55 0 +1824 35 54 0 +1825 35 54 1 +1826 35 55 0 +1827 35 55 1 +1828 34 54 0 +1829 34 54 1 +1830 34 55 0 +1831 34 55 1 +1832 32 55 1 +1833 32 55 0 +1834 32 54 1 +1835 32 54 0 +1836 33 55 1 +1837 33 55 0 +1838 33 54 1 +1839 33 54 0 +1840 36 54 0 +1841 36 54 1 +1842 36 55 0 +1843 36 55 1 +1844 37 54 0 +1845 37 54 1 +1846 37 55 0 +1847 37 55 1 +1848 39 55 1 +1849 39 55 0 +1850 39 54 1 +1851 39 54 0 +1852 38 55 1 +1853 38 55 0 +1854 38 54 1 +1855 38 54 0 +1856 44 55 0 +1857 44 55 1 +1858 44 54 0 +1859 44 54 1 +1860 45 55 0 +1861 45 55 1 +1862 45 54 0 +1863 45 54 1 +1864 47 54 1 +1865 47 54 0 +1866 47 55 1 +1867 47 55 0 +1868 46 54 1 +1869 46 54 0 +1870 46 55 1 +1871 46 55 0 +2176 43 27 0 +2177 43 27 1 +2178 43 26 0 +2179 43 26 1 +2180 42 27 0 +2181 42 27 1 +2182 42 26 0 +2183 42 26 1 +2184 40 26 1 +2185 40 26 0 +2186 40 27 1 +2187 40 27 0 +2188 41 26 1 +2189 41 26 0 +2190 41 27 1 +2191 41 27 0 +2208 35 26 0 +2209 35 26 1 +2210 35 27 0 +2211 35 27 1 +2212 34 26 0 +2213 34 26 1 +2214 34 27 0 +2215 34 27 1 +2216 32 27 1 +2217 32 27 0 +2218 32 26 1 +2219 32 26 0 +2220 33 27 1 +2221 33 27 0 +2222 33 26 1 +2223 33 26 0 +2224 36 26 0 +2225 36 26 1 +2226 36 27 0 +2227 36 27 1 +2228 37 26 0 +2229 37 26 1 +2230 37 27 0 +2231 37 27 1 +2232 39 27 1 +2233 39 27 0 +2234 39 26 1 +2235 39 26 0 +2236 38 27 1 +2237 38 27 0 +2238 38 26 1 +2239 38 26 0 +2240 44 27 0 +2241 44 27 1 +2242 44 26 0 +2243 44 26 1 +2244 45 27 0 +2245 45 27 1 +2246 45 26 0 +2247 45 26 1 +2248 47 26 1 +2249 47 26 0 +2250 47 27 1 +2251 47 27 0 +2252 46 26 1 +2253 46 26 0 +2254 46 27 1 +2255 46 27 0 +2304 43 25 0 +2305 43 25 1 +2306 43 24 0 +2307 43 24 1 +2308 42 25 0 +2309 42 25 1 +2310 42 24 0 +2311 42 24 1 +2312 40 24 1 +2313 40 24 0 +2314 40 25 1 +2315 40 25 0 +2316 41 24 1 +2317 41 24 0 +2318 41 25 1 +2319 41 25 0 +2336 35 24 0 +2337 35 24 1 +2338 35 25 0 +2339 35 25 1 +2340 34 24 0 +2341 34 24 1 +2342 34 25 0 +2343 34 25 1 +2344 32 25 1 +2345 32 25 0 +2346 32 24 1 +2347 32 24 0 +2348 33 25 1 +2349 33 25 0 +2350 33 24 1 +2351 33 24 0 +2352 36 24 0 +2353 36 24 1 +2354 36 25 0 +2355 36 25 1 +2356 37 24 0 +2357 37 24 1 +2358 37 25 0 +2359 37 25 1 +2360 39 25 1 +2361 39 25 0 +2362 39 24 1 +2363 39 24 0 +2364 38 25 1 +2365 38 25 0 +2366 38 24 1 +2367 38 24 0 +2368 44 25 0 +2369 44 25 1 +2370 44 24 0 +2371 44 24 1 +2372 45 25 0 +2373 45 25 1 +2374 45 24 0 +2375 45 24 1 +2376 47 24 1 +2377 47 24 0 +2378 47 25 1 +2379 47 25 0 +2380 46 24 1 +2381 46 24 0 +2382 46 25 1 +2383 46 25 0 +2432 43 23 0 +2433 43 23 1 +2434 43 22 0 +2435 43 22 1 +2436 42 23 0 +2437 42 23 1 +2438 42 22 0 +2439 42 22 1 +2440 40 22 1 +2441 40 22 0 +2442 40 23 1 +2443 40 23 0 +2444 41 22 1 +2445 41 22 0 +2446 41 23 1 +2447 41 23 0 +2464 35 22 0 +2465 35 22 1 +2466 35 23 0 +2467 35 23 1 +2468 34 22 0 +2469 34 22 1 +2470 34 23 0 +2471 34 23 1 +2472 32 23 1 +2473 32 23 0 +2474 32 22 1 +2475 32 22 0 +2476 33 23 1 +2477 33 23 0 +2478 33 22 1 +2479 33 22 0 +2480 36 22 0 +2481 36 22 1 +2482 36 23 0 +2483 36 23 1 +2484 37 22 0 +2485 37 22 1 +2486 37 23 0 +2487 37 23 1 +2488 39 23 1 +2489 39 23 0 +2490 39 22 1 +2491 39 22 0 +2492 38 23 1 +2493 38 23 0 +2494 38 22 1 +2495 38 22 0 +2496 44 23 0 +2497 44 23 1 +2498 44 22 0 +2499 44 22 1 +2500 45 23 0 +2501 45 23 1 +2502 45 22 0 +2503 45 22 1 +2504 47 22 1 +2505 47 22 0 +2506 47 23 1 +2507 47 23 0 +2508 46 22 1 +2509 46 22 0 +2510 46 23 1 +2511 46 23 0 +2560 43 21 0 +2561 43 21 1 +2562 43 20 0 +2563 43 20 1 +2564 42 21 0 +2565 42 21 1 +2566 42 20 0 +2567 42 20 1 +2568 40 20 1 +2569 40 20 0 +2570 40 21 1 +2571 40 21 0 +2572 41 20 1 +2573 41 20 0 +2574 41 21 1 +2575 41 21 0 +2592 35 20 0 +2593 35 20 1 +2594 35 21 0 +2595 35 21 1 +2596 34 20 0 +2597 34 20 1 +2598 34 21 0 +2599 34 21 1 +2600 32 21 1 +2601 32 21 0 +2602 32 20 1 +2603 32 20 0 +2604 33 21 1 +2605 33 21 0 +2606 33 20 1 +2607 33 20 0 +2608 36 20 0 +2609 36 20 1 +2610 36 21 0 +2611 36 21 1 +2612 37 20 0 +2613 37 20 1 +2614 37 21 0 +2615 37 21 1 +2616 39 21 1 +2617 39 21 0 +2618 39 20 1 +2619 39 20 0 +2620 38 21 1 +2621 38 21 0 +2622 38 20 1 +2623 38 20 0 +2624 44 21 0 +2625 44 21 1 +2626 44 20 0 +2627 44 20 1 +2628 45 21 0 +2629 45 21 1 +2630 45 20 0 +2631 45 20 1 +2632 47 20 1 +2633 47 20 0 +2634 47 21 1 +2635 47 21 0 +2636 46 20 1 +2637 46 20 0 +2638 46 21 1 +2639 46 21 0 +2688 43 19 0 +2689 43 19 1 +2690 43 18 0 +2691 43 18 1 +2692 42 19 0 +2693 42 19 1 +2694 42 18 0 +2695 42 18 1 +2696 40 18 1 +2697 40 18 0 +2698 40 19 1 +2699 40 19 0 +2700 41 18 1 +2701 41 18 0 +2702 41 19 1 +2703 41 19 0 +2720 35 18 0 +2721 35 18 1 +2722 35 19 0 +2723 35 19 1 +2724 34 18 0 +2725 34 18 1 +2726 34 19 0 +2727 34 19 1 +2728 32 19 1 +2729 32 19 0 +2730 32 18 1 +2731 32 18 0 +2732 33 19 1 +2733 33 19 0 +2734 33 18 1 +2735 33 18 0 +2736 36 18 0 +2737 36 18 1 +2738 36 19 0 +2739 36 19 1 +2740 37 18 0 +2741 37 18 1 +2742 37 19 0 +2743 37 19 1 +2744 39 19 1 +2745 39 19 0 +2746 39 18 1 +2747 39 18 0 +2748 38 19 1 +2749 38 19 0 +2750 38 18 1 +2751 38 18 0 +2752 44 19 0 +2753 44 19 1 +2754 44 18 0 +2755 44 18 1 +2756 45 19 0 +2757 45 19 1 +2758 45 18 0 +2759 45 18 1 +2760 47 18 1 +2761 47 18 0 +2762 47 19 1 +2763 47 19 0 +2764 46 18 1 +2765 46 18 0 +2766 46 19 1 +2767 46 19 0 +2816 43 17 0 +2817 43 17 1 +2818 43 16 0 +2819 43 16 1 +2820 42 17 0 +2821 42 17 1 +2822 42 16 0 +2823 42 16 1 +2824 40 16 1 +2825 40 16 0 +2826 40 17 1 +2827 40 17 0 +2828 41 16 1 +2829 41 16 0 +2830 41 17 1 +2831 41 17 0 +2848 35 16 0 +2849 35 16 1 +2850 35 17 0 +2851 35 17 1 +2852 34 16 0 +2853 34 16 1 +2854 34 17 0 +2855 34 17 1 +2856 32 17 1 +2857 32 17 0 +2858 32 16 1 +2859 32 16 0 +2860 33 17 1 +2861 33 17 0 +2862 33 16 1 +2863 33 16 0 +2864 36 16 0 +2865 36 16 1 +2866 36 17 0 +2867 36 17 1 +2868 37 16 0 +2869 37 16 1 +2870 37 17 0 +2871 37 17 1 +2872 39 17 1 +2873 39 17 0 +2874 39 16 1 +2875 39 16 0 +2876 38 17 1 +2877 38 17 0 +2878 38 16 1 +2879 38 16 0 +2880 44 17 0 +2881 44 17 1 +2882 44 16 0 +2883 44 16 1 +2884 45 17 0 +2885 45 17 1 +2886 45 16 0 +2887 45 16 1 +2888 47 16 1 +2889 47 16 0 +2890 47 17 1 +2891 47 17 0 +2892 46 16 1 +2893 46 16 0 +2894 46 17 1 +2895 46 17 0 +2944 43 15 0 +2945 43 15 1 +2946 43 14 0 +2947 43 14 1 +2948 42 15 0 +2949 42 15 1 +2950 42 14 0 +2951 42 14 1 +2952 40 14 1 +2953 40 14 0 +2954 40 15 1 +2955 40 15 0 +2956 41 14 1 +2957 41 14 0 +2958 41 15 1 +2959 41 15 0 +2976 35 14 0 +2977 35 14 1 +2978 35 15 0 +2979 35 15 1 +2980 34 14 0 +2981 34 14 1 +2982 34 15 0 +2983 34 15 1 +2984 32 15 1 +2985 32 15 0 +2986 32 14 1 +2987 32 14 0 +2988 33 15 1 +2989 33 15 0 +2990 33 14 1 +2991 33 14 0 +2992 36 14 0 +2993 36 14 1 +2994 36 15 0 +2995 36 15 1 +2996 37 14 0 +2997 37 14 1 +2998 37 15 0 +2999 37 15 1 +3000 39 15 1 +3001 39 15 0 +3002 39 14 1 +3003 39 14 0 +3004 38 15 1 +3005 38 15 0 +3006 38 14 1 +3007 38 14 0 +3008 44 15 0 +3009 44 15 1 +3010 44 14 0 +3011 44 14 1 +3012 45 15 0 +3013 45 15 1 +3014 45 14 0 +3015 45 14 1 +3016 47 14 1 +3017 47 14 0 +3018 47 15 1 +3019 47 15 0 +3020 46 14 1 +3021 46 14 0 +3022 46 15 1 +3023 46 15 0 +3072 43 13 0 +3073 43 13 1 +3074 43 12 0 +3075 43 12 1 +3076 42 13 0 +3077 42 13 1 +3078 42 12 0 +3079 42 12 1 +3080 40 12 1 +3081 40 12 0 +3082 40 13 1 +3083 40 13 0 +3084 41 12 1 +3085 41 12 0 +3086 41 13 1 +3087 41 13 0 +3104 35 12 0 +3105 35 12 1 +3106 35 13 0 +3107 35 13 1 +3108 34 12 0 +3109 34 12 1 +3110 34 13 0 +3111 34 13 1 +3112 32 13 1 +3113 32 13 0 +3114 32 12 1 +3115 32 12 0 +3116 33 13 1 +3117 33 13 0 +3118 33 12 1 +3119 33 12 0 +3120 36 12 0 +3121 36 12 1 +3122 36 13 0 +3123 36 13 1 +3124 37 12 0 +3125 37 12 1 +3126 37 13 0 +3127 37 13 1 +3128 39 13 1 +3129 39 13 0 +3130 39 12 1 +3131 39 12 0 +3132 38 13 1 +3133 38 13 0 +3134 38 12 1 +3135 38 12 0 +3136 44 13 0 +3137 44 13 1 +3138 44 12 0 +3139 44 12 1 +3140 45 13 0 +3141 45 13 1 +3142 45 12 0 +3143 45 12 1 +3144 47 12 1 +3145 47 12 0 +3146 47 13 1 +3147 47 13 0 +3148 46 12 1 +3149 46 12 0 +3150 46 13 1 +3151 46 13 0 +3200 43 11 0 +3201 43 11 1 +3202 43 10 0 +3203 43 10 1 +3204 42 11 0 +3205 42 11 1 +3206 42 10 0 +3207 42 10 1 +3208 40 10 1 +3209 40 10 0 +3210 40 11 1 +3211 40 11 0 +3212 41 10 1 +3213 41 10 0 +3214 41 11 1 +3215 41 11 0 +3232 35 10 0 +3233 35 10 1 +3234 35 11 0 +3235 35 11 1 +3236 34 10 0 +3237 34 10 1 +3238 34 11 0 +3239 34 11 1 +3240 32 11 1 +3241 32 11 0 +3242 32 10 1 +3243 32 10 0 +3244 33 11 1 +3245 33 11 0 +3246 33 10 1 +3247 33 10 0 +3248 36 10 0 +3249 36 10 1 +3250 36 11 0 +3251 36 11 1 +3252 37 10 0 +3253 37 10 1 +3254 37 11 0 +3255 37 11 1 +3256 39 11 1 +3257 39 11 0 +3258 39 10 1 +3259 39 10 0 +3260 38 11 1 +3261 38 11 0 +3262 38 10 1 +3263 38 10 0 +3264 44 11 0 +3265 44 11 1 +3266 44 10 0 +3267 44 10 1 +3268 45 11 0 +3269 45 11 1 +3270 45 10 0 +3271 45 10 1 +3272 47 10 1 +3273 47 10 0 +3274 47 11 1 +3275 47 11 0 +3276 46 10 1 +3277 46 10 0 +3278 46 11 1 +3279 46 11 0 +3328 43 9 0 +3329 43 9 1 +3330 43 8 0 +3331 43 8 1 +3332 42 9 0 +3333 42 9 1 +3334 42 8 0 +3335 42 8 1 +3336 40 8 1 +3337 40 8 0 +3338 40 9 1 +3339 40 9 0 +3340 41 8 1 +3341 41 8 0 +3342 41 9 1 +3343 41 9 0 +3360 35 8 0 +3361 35 8 1 +3362 35 9 0 +3363 35 9 1 +3364 34 8 0 +3365 34 8 1 +3366 34 9 0 +3367 34 9 1 +3368 32 9 1 +3369 32 9 0 +3370 32 8 1 +3371 32 8 0 +3372 33 9 1 +3373 33 9 0 +3374 33 8 1 +3375 33 8 0 +3376 36 8 0 +3377 36 8 1 +3378 36 9 0 +3379 36 9 1 +3380 37 8 0 +3381 37 8 1 +3382 37 9 0 +3383 37 9 1 +3384 39 9 1 +3385 39 9 0 +3386 39 8 1 +3387 39 8 0 +3388 38 9 1 +3389 38 9 0 +3390 38 8 1 +3391 38 8 0 +3392 44 9 0 +3393 44 9 1 +3394 44 8 0 +3395 44 8 1 +3396 45 9 0 +3397 45 9 1 +3398 45 8 0 +3399 45 8 1 +3400 47 8 1 +3401 47 8 0 +3402 47 9 1 +3403 47 9 0 +3404 46 8 1 +3405 46 8 0 +3406 46 9 1 +3407 46 9 0 +3456 43 7 0 +3457 43 7 1 +3458 43 6 0 +3459 43 6 1 +3460 42 7 0 +3461 42 7 1 +3462 42 6 0 +3463 42 6 1 +3464 40 6 1 +3465 40 6 0 +3466 40 7 1 +3467 40 7 0 +3468 41 6 1 +3469 41 6 0 +3470 41 7 1 +3471 41 7 0 +3488 35 6 0 +3489 35 6 1 +3490 35 7 0 +3491 35 7 1 +3492 34 6 0 +3493 34 6 1 +3494 34 7 0 +3495 34 7 1 +3496 32 7 1 +3497 32 7 0 +3498 32 6 1 +3499 32 6 0 +3500 33 7 1 +3501 33 7 0 +3502 33 6 1 +3503 33 6 0 +3504 36 6 0 +3505 36 6 1 +3506 36 7 0 +3507 36 7 1 +3508 37 6 0 +3509 37 6 1 +3510 37 7 0 +3511 37 7 1 +3512 39 7 1 +3513 39 7 0 +3514 39 6 1 +3515 39 6 0 +3516 38 7 1 +3517 38 7 0 +3518 38 6 1 +3519 38 6 0 +3520 44 7 0 +3521 44 7 1 +3522 44 6 0 +3523 44 6 1 +3524 45 7 0 +3525 45 7 1 +3526 45 6 0 +3527 45 6 1 +3528 47 6 1 +3529 47 6 0 +3530 47 7 1 +3531 47 7 0 +3532 46 6 1 +3533 46 6 0 +3534 46 7 1 +3535 46 7 0 +3584 43 5 0 +3585 43 5 1 +3586 43 4 0 +3587 43 4 1 +3588 42 5 0 +3589 42 5 1 +3590 42 4 0 +3591 42 4 1 +3592 40 4 1 +3593 40 4 0 +3594 40 5 1 +3595 40 5 0 +3596 41 4 1 +3597 41 4 0 +3598 41 5 1 +3599 41 5 0 +3616 35 4 0 +3617 35 4 1 +3618 35 5 0 +3619 35 5 1 +3620 34 4 0 +3621 34 4 1 +3622 34 5 0 +3623 34 5 1 +3624 32 5 1 +3625 32 5 0 +3626 32 4 1 +3627 32 4 0 +3628 33 5 1 +3629 33 5 0 +3630 33 4 1 +3631 33 4 0 +3632 36 4 0 +3633 36 4 1 +3634 36 5 0 +3635 36 5 1 +3636 37 4 0 +3637 37 4 1 +3638 37 5 0 +3639 37 5 1 +3640 39 5 1 +3641 39 5 0 +3642 39 4 1 +3643 39 4 0 +3644 38 5 1 +3645 38 5 0 +3646 38 4 1 +3647 38 4 0 +3648 44 5 0 +3649 44 5 1 +3650 44 4 0 +3651 44 4 1 +3652 45 5 0 +3653 45 5 1 +3654 45 4 0 +3655 45 4 1 +3656 47 4 1 +3657 47 4 0 +3658 47 5 1 +3659 47 5 0 +3660 46 4 1 +3661 46 4 0 +3662 46 5 1 +3663 46 5 0 +3712 43 3 0 +3713 43 3 1 +3714 43 2 0 +3715 43 2 1 +3716 42 3 0 +3717 42 3 1 +3718 42 2 0 +3719 42 2 1 +3720 40 2 1 +3721 40 2 0 +3722 40 3 1 +3723 40 3 0 +3724 41 2 1 +3725 41 2 0 +3726 41 3 1 +3727 41 3 0 +3744 35 2 0 +3745 35 2 1 +3746 35 3 0 +3747 35 3 1 +3748 34 2 0 +3749 34 2 1 +3750 34 3 0 +3751 34 3 1 +3752 32 3 1 +3753 32 3 0 +3754 32 2 1 +3755 32 2 0 +3756 33 3 1 +3757 33 3 0 +3758 33 2 1 +3759 33 2 0 +3760 36 2 0 +3761 36 2 1 +3762 36 3 0 +3763 36 3 1 +3764 37 2 0 +3765 37 2 1 +3766 37 3 0 +3767 37 3 1 +3768 39 3 1 +3769 39 3 0 +3770 39 2 1 +3771 39 2 0 +3772 38 3 1 +3773 38 3 0 +3774 38 2 1 +3775 38 2 0 +3776 44 3 0 +3777 44 3 1 +3778 44 2 0 +3779 44 2 1 +3780 45 3 0 +3781 45 3 1 +3782 45 2 0 +3783 45 2 1 +3784 47 2 1 +3785 47 2 0 +3786 47 3 1 +3787 47 3 0 +3788 46 2 1 +3789 46 2 0 +3790 46 3 1 +3791 46 3 0 +3840 43 1 0 +3841 43 1 1 +3842 43 0 0 +3843 43 0 1 +3844 42 1 0 +3845 42 1 1 +3846 42 0 0 +3847 42 0 1 +3848 40 0 1 +3849 40 0 0 +3850 40 1 1 +3851 40 1 0 +3852 41 0 1 +3853 41 0 0 +3854 41 1 1 +3855 41 1 0 +3872 35 0 0 +3873 35 0 1 +3874 35 1 0 +3875 35 1 1 +3876 34 0 0 +3877 34 0 1 +3878 34 1 0 +3879 34 1 1 +3880 32 1 1 +3881 32 1 0 +3882 32 0 1 +3883 32 0 0 +3884 33 1 1 +3885 33 1 0 +3886 33 0 1 +3887 33 0 0 +3888 36 0 0 +3889 36 0 1 +3890 36 1 0 +3891 36 1 1 +3892 37 0 0 +3893 37 0 1 +3894 37 1 0 +3895 37 1 1 +3896 39 1 1 +3897 39 1 0 +3898 39 0 1 +3899 39 0 0 +3900 38 1 1 +3901 38 1 0 +3902 38 0 1 +3903 38 0 0 +3904 44 1 0 +3905 44 1 1 +3906 44 0 0 +3907 44 0 1 +3908 45 1 0 +3909 45 1 1 +3910 45 0 0 +3911 45 0 1 +3912 47 0 1 +3913 47 0 0 +3914 47 1 1 +3915 47 1 0 +3916 46 0 1 +3917 46 0 0 +3918 46 1 1 +3919 46 1 0 diff --git a/Detectors/PHOS/base/files/Mod1RCU3.data b/Detectors/PHOS/base/files/Mod1RCU3.data new file mode 100644 index 0000000000000..43bd76fc42791 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod1RCU3.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 3 0 2 + 1 3 1 2 + 2 3 2 2 + 3 3 3 2 + 4 3 4 2 + 5 3 5 2 + 6 3 6 2 + 7 3 7 2 + 8 3 8 2 + 9 3 9 2 + 10 3 10 2 + 11 3 11 2 + 12 3 12 2 + 13 3 13 2 + 14 3 14 2 + 15 3 15 2 + 16 3 16 2 + 17 3 17 2 + 18 3 18 2 + 19 3 19 2 + 20 3 20 2 + 21 3 21 2 + 22 3 22 2 + 23 3 23 2 + 24 3 24 2 + 25 3 25 2 + 26 3 26 2 + 27 3 27 2 + 28 3 28 2 + 29 3 29 2 + 30 3 30 2 + 31 3 31 2 + 32 3 32 2 + 33 3 33 2 + 34 3 34 2 + 35 3 35 2 + 36 3 36 2 + 37 3 37 2 + 38 3 38 2 + 39 3 39 2 + 40 3 40 2 + 41 3 41 2 + 42 3 42 2 + 43 3 43 2 + 44 3 44 2 + 45 3 45 2 + 46 3 46 2 + 47 3 47 2 + 48 3 48 2 + 49 3 49 2 + 50 3 50 2 + 51 3 51 2 + 52 3 52 2 + 53 3 53 2 + 54 3 54 2 + 55 3 55 2 + 56 3 56 2 + 57 3 57 2 + 58 3 58 2 + 59 3 59 2 + 60 3 60 2 + 61 3 61 2 + 62 3 62 2 + 63 3 63 2 + 64 3 64 2 + 65 3 65 2 + 66 3 66 2 + 67 3 67 2 + 68 3 68 2 + 69 3 69 2 + 70 3 70 2 + 71 3 71 2 + 72 3 72 2 + 73 3 73 2 + 74 3 74 2 + 75 3 75 2 + 76 3 76 2 + 77 3 77 2 + 78 3 78 2 + 79 3 79 2 + 80 3 80 2 + 81 3 81 2 + 82 3 82 2 + 83 3 83 2 + 84 3 84 2 + 85 3 85 2 + 86 3 86 2 + 87 3 87 2 + 88 3 88 2 + 89 3 89 2 + 90 3 90 2 + 91 3 91 2 + 92 3 92 2 + 93 3 93 2 + 94 3 94 2 + 95 3 95 2 + 96 3 96 2 + 97 3 97 2 + 98 3 98 2 + 99 3 99 2 + 100 3 100 2 + 101 3 101 2 + 102 3 102 2 + 103 3 103 2 + 104 3 104 2 + 105 3 105 2 + 106 3 106 2 + 107 3 107 2 + 108 3 108 2 + 109 3 109 2 + 110 3 110 2 + 111 3 111 2 + 112 3 112 2 + 113 3 113 2 + 114 3 114 2 + 115 3 115 2 + 116 3 116 2 + 117 3 117 2 + 118 3 118 2 + 119 3 119 2 + 120 3 120 2 + 121 3 121 2 + 122 3 122 2 + 123 3 123 2 + 124 3 124 2 + 125 3 125 2 + 126 3 126 2 + 127 3 127 2 +2048 3 2048 2 +2049 3 2049 2 +2050 3 2050 2 +2051 3 2051 2 +2052 3 2052 2 +2053 3 2053 2 +2054 3 2054 2 +2055 3 2055 2 +2056 3 2056 2 +2057 3 2057 2 +2058 3 2058 2 +2059 3 2059 2 +2060 3 2060 2 +2061 3 2061 2 +2062 3 2062 2 +2063 3 2063 2 +2064 3 2064 2 +2065 3 2065 2 +2066 3 2066 2 +2067 3 2067 2 +2068 3 2068 2 +2069 3 2069 2 +2070 3 2070 2 +2071 3 2071 2 +2072 3 2072 2 +2073 3 2073 2 +2074 3 2074 2 +2075 3 2075 2 +2076 3 2076 2 +2077 3 2077 2 +2078 3 2078 2 +2079 3 2079 2 +2080 3 2080 2 +2081 3 2081 2 +2082 3 2082 2 +2083 3 2083 2 +2084 3 2084 2 +2085 3 2085 2 +2086 3 2086 2 +2087 3 2087 2 +2088 3 2088 2 +2089 3 2089 2 +2090 3 2090 2 +2091 3 2091 2 +2092 3 2092 2 +2093 3 2093 2 +2094 3 2094 2 +2095 3 2095 2 +2096 3 2096 2 +2097 3 2097 2 +2098 3 2098 2 +2099 3 2099 2 +2100 3 2100 2 +2101 3 2101 2 +2102 3 2102 2 +2103 3 2103 2 +2104 3 2104 2 +2105 3 2105 2 +2106 3 2106 2 +2107 3 2107 2 +2108 3 2108 2 +2109 3 2109 2 +2110 3 2110 2 +2111 3 2111 2 +2112 3 2112 2 +2113 3 2113 2 +2114 3 2114 2 +2115 3 2115 2 +2116 3 2116 2 +2117 3 2117 2 +2118 3 2118 2 +2119 3 2119 2 +2120 3 2120 2 +2121 3 2121 2 +2122 3 2122 2 +2123 3 2123 2 +2124 3 2124 2 +2125 3 2125 2 +2126 3 2126 2 +2127 3 2127 2 +2128 3 2128 2 +2129 3 2129 2 +2130 3 2130 2 +2131 3 2131 2 +2132 3 2132 2 +2133 3 2133 2 +2134 3 2134 2 +2135 3 2135 2 +2136 3 2136 2 +2137 3 2137 2 +2138 3 2138 2 +2139 3 2139 2 +2140 3 2140 2 +2141 3 2141 2 +2142 3 2142 2 +2143 3 2143 2 +2144 3 2144 2 +2145 3 2145 2 +2146 3 2146 2 +2147 3 2147 2 +2148 3 2148 2 +2149 3 2149 2 +2150 3 2150 2 +2151 3 2151 2 +2152 3 2152 2 +2153 3 2153 2 +2154 3 2154 2 +2155 3 2155 2 +2156 3 2156 2 +2157 3 2157 2 +2158 3 2158 2 +2159 3 2159 2 +2160 3 2160 2 +2161 3 2161 2 +2162 3 2162 2 +2163 3 2163 2 +2164 3 2164 2 +2165 3 2165 2 +2166 3 2166 2 +2167 3 2167 2 +2168 3 2168 2 +2169 3 2169 2 +2170 3 2170 2 +2171 3 2171 2 +2172 3 2172 2 +2173 3 2173 2 +2174 3 2174 2 +2175 3 2175 2 + 128 59 29 0 + 129 59 29 1 + 130 59 28 0 + 131 59 28 1 + 132 58 29 0 + 133 58 29 1 + 134 58 28 0 + 135 58 28 1 + 136 56 28 1 + 137 56 28 0 + 138 56 29 1 + 139 56 29 0 + 140 57 28 1 + 141 57 28 0 + 142 57 29 1 + 143 57 29 0 + 160 51 28 0 + 161 51 28 1 + 162 51 29 0 + 163 51 29 1 + 164 50 28 0 + 165 50 28 1 + 166 50 29 0 + 167 50 29 1 + 168 48 29 1 + 169 48 29 0 + 170 48 28 1 + 171 48 28 0 + 172 49 29 1 + 173 49 29 0 + 174 49 28 1 + 175 49 28 0 + 176 52 28 0 + 177 52 28 1 + 178 52 29 0 + 179 52 29 1 + 180 53 28 0 + 181 53 28 1 + 182 53 29 0 + 183 53 29 1 + 184 55 29 1 + 185 55 29 0 + 186 55 28 1 + 187 55 28 0 + 188 54 29 1 + 189 54 29 0 + 190 54 28 1 + 191 54 28 0 + 192 60 29 0 + 193 60 29 1 + 194 60 28 0 + 195 60 28 1 + 196 61 29 0 + 197 61 29 1 + 198 61 28 0 + 199 61 28 1 + 200 63 28 1 + 201 63 28 0 + 202 63 29 1 + 203 63 29 0 + 204 62 28 1 + 205 62 28 0 + 206 62 29 1 + 207 62 29 0 + 256 59 31 0 + 257 59 31 1 + 258 59 30 0 + 259 59 30 1 + 260 58 31 0 + 261 58 31 1 + 262 58 30 0 + 263 58 30 1 + 264 56 30 1 + 265 56 30 0 + 266 56 31 1 + 267 56 31 0 + 268 57 30 1 + 269 57 30 0 + 270 57 31 1 + 271 57 31 0 + 288 51 30 0 + 289 51 30 1 + 290 51 31 0 + 291 51 31 1 + 292 50 30 0 + 293 50 30 1 + 294 50 31 0 + 295 50 31 1 + 296 48 31 1 + 297 48 31 0 + 298 48 30 1 + 299 48 30 0 + 300 49 31 1 + 301 49 31 0 + 302 49 30 1 + 303 49 30 0 + 304 52 30 0 + 305 52 30 1 + 306 52 31 0 + 307 52 31 1 + 308 53 30 0 + 309 53 30 1 + 310 53 31 0 + 311 53 31 1 + 312 55 31 1 + 313 55 31 0 + 314 55 30 1 + 315 55 30 0 + 316 54 31 1 + 317 54 31 0 + 318 54 30 1 + 319 54 30 0 + 320 60 31 0 + 321 60 31 1 + 322 60 30 0 + 323 60 30 1 + 324 61 31 0 + 325 61 31 1 + 326 61 30 0 + 327 61 30 1 + 328 63 30 1 + 329 63 30 0 + 330 63 31 1 + 331 63 31 0 + 332 62 30 1 + 333 62 30 0 + 334 62 31 1 + 335 62 31 0 + 384 59 33 0 + 385 59 33 1 + 386 59 32 0 + 387 59 32 1 + 388 58 33 0 + 389 58 33 1 + 390 58 32 0 + 391 58 32 1 + 392 56 32 1 + 393 56 32 0 + 394 56 33 1 + 395 56 33 0 + 396 57 32 1 + 397 57 32 0 + 398 57 33 1 + 399 57 33 0 + 416 51 32 0 + 417 51 32 1 + 418 51 33 0 + 419 51 33 1 + 420 50 32 0 + 421 50 32 1 + 422 50 33 0 + 423 50 33 1 + 424 48 33 1 + 425 48 33 0 + 426 48 32 1 + 427 48 32 0 + 428 49 33 1 + 429 49 33 0 + 430 49 32 1 + 431 49 32 0 + 432 52 32 0 + 433 52 32 1 + 434 52 33 0 + 435 52 33 1 + 436 53 32 0 + 437 53 32 1 + 438 53 33 0 + 439 53 33 1 + 440 55 33 1 + 441 55 33 0 + 442 55 32 1 + 443 55 32 0 + 444 54 33 1 + 445 54 33 0 + 446 54 32 1 + 447 54 32 0 + 448 60 33 0 + 449 60 33 1 + 450 60 32 0 + 451 60 32 1 + 452 61 33 0 + 453 61 33 1 + 454 61 32 0 + 455 61 32 1 + 456 63 32 1 + 457 63 32 0 + 458 63 33 1 + 459 63 33 0 + 460 62 32 1 + 461 62 32 0 + 462 62 33 1 + 463 62 33 0 + 512 59 35 0 + 513 59 35 1 + 514 59 34 0 + 515 59 34 1 + 516 58 35 0 + 517 58 35 1 + 518 58 34 0 + 519 58 34 1 + 520 56 34 1 + 521 56 34 0 + 522 56 35 1 + 523 56 35 0 + 524 57 34 1 + 525 57 34 0 + 526 57 35 1 + 527 57 35 0 + 544 51 34 0 + 545 51 34 1 + 546 51 35 0 + 547 51 35 1 + 548 50 34 0 + 549 50 34 1 + 550 50 35 0 + 551 50 35 1 + 552 48 35 1 + 553 48 35 0 + 554 48 34 1 + 555 48 34 0 + 556 49 35 1 + 557 49 35 0 + 558 49 34 1 + 559 49 34 0 + 560 52 34 0 + 561 52 34 1 + 562 52 35 0 + 563 52 35 1 + 564 53 34 0 + 565 53 34 1 + 566 53 35 0 + 567 53 35 1 + 568 55 35 1 + 569 55 35 0 + 570 55 34 1 + 571 55 34 0 + 572 54 35 1 + 573 54 35 0 + 574 54 34 1 + 575 54 34 0 + 576 60 35 0 + 577 60 35 1 + 578 60 34 0 + 579 60 34 1 + 580 61 35 0 + 581 61 35 1 + 582 61 34 0 + 583 61 34 1 + 584 63 34 1 + 585 63 34 0 + 586 63 35 1 + 587 63 35 0 + 588 62 34 1 + 589 62 34 0 + 590 62 35 1 + 591 62 35 0 + 640 59 37 0 + 641 59 37 1 + 642 59 36 0 + 643 59 36 1 + 644 58 37 0 + 645 58 37 1 + 646 58 36 0 + 647 58 36 1 + 648 56 36 1 + 649 56 36 0 + 650 56 37 1 + 651 56 37 0 + 652 57 36 1 + 653 57 36 0 + 654 57 37 1 + 655 57 37 0 + 672 51 36 0 + 673 51 36 1 + 674 51 37 0 + 675 51 37 1 + 676 50 36 0 + 677 50 36 1 + 678 50 37 0 + 679 50 37 1 + 680 48 37 1 + 681 48 37 0 + 682 48 36 1 + 683 48 36 0 + 684 49 37 1 + 685 49 37 0 + 686 49 36 1 + 687 49 36 0 + 688 52 36 0 + 689 52 36 1 + 690 52 37 0 + 691 52 37 1 + 692 53 36 0 + 693 53 36 1 + 694 53 37 0 + 695 53 37 1 + 696 55 37 1 + 697 55 37 0 + 698 55 36 1 + 699 55 36 0 + 700 54 37 1 + 701 54 37 0 + 702 54 36 1 + 703 54 36 0 + 704 60 37 0 + 705 60 37 1 + 706 60 36 0 + 707 60 36 1 + 708 61 37 0 + 709 61 37 1 + 710 61 36 0 + 711 61 36 1 + 712 63 36 1 + 713 63 36 0 + 714 63 37 1 + 715 63 37 0 + 716 62 36 1 + 717 62 36 0 + 718 62 37 1 + 719 62 37 0 + 768 59 39 0 + 769 59 39 1 + 770 59 38 0 + 771 59 38 1 + 772 58 39 0 + 773 58 39 1 + 774 58 38 0 + 775 58 38 1 + 776 56 38 1 + 777 56 38 0 + 778 56 39 1 + 779 56 39 0 + 780 57 38 1 + 781 57 38 0 + 782 57 39 1 + 783 57 39 0 + 800 51 38 0 + 801 51 38 1 + 802 51 39 0 + 803 51 39 1 + 804 50 38 0 + 805 50 38 1 + 806 50 39 0 + 807 50 39 1 + 808 48 39 1 + 809 48 39 0 + 810 48 38 1 + 811 48 38 0 + 812 49 39 1 + 813 49 39 0 + 814 49 38 1 + 815 49 38 0 + 816 52 38 0 + 817 52 38 1 + 818 52 39 0 + 819 52 39 1 + 820 53 38 0 + 821 53 38 1 + 822 53 39 0 + 823 53 39 1 + 824 55 39 1 + 825 55 39 0 + 826 55 38 1 + 827 55 38 0 + 828 54 39 1 + 829 54 39 0 + 830 54 38 1 + 831 54 38 0 + 832 60 39 0 + 833 60 39 1 + 834 60 38 0 + 835 60 38 1 + 836 61 39 0 + 837 61 39 1 + 838 61 38 0 + 839 61 38 1 + 840 63 38 1 + 841 63 38 0 + 842 63 39 1 + 843 63 39 0 + 844 62 38 1 + 845 62 38 0 + 846 62 39 1 + 847 62 39 0 + 896 59 41 0 + 897 59 41 1 + 898 59 40 0 + 899 59 40 1 + 900 58 41 0 + 901 58 41 1 + 902 58 40 0 + 903 58 40 1 + 904 56 40 1 + 905 56 40 0 + 906 56 41 1 + 907 56 41 0 + 908 57 40 1 + 909 57 40 0 + 910 57 41 1 + 911 57 41 0 + 928 51 40 0 + 929 51 40 1 + 930 51 41 0 + 931 51 41 1 + 932 50 40 0 + 933 50 40 1 + 934 50 41 0 + 935 50 41 1 + 936 48 41 1 + 937 48 41 0 + 938 48 40 1 + 939 48 40 0 + 940 49 41 1 + 941 49 41 0 + 942 49 40 1 + 943 49 40 0 + 944 52 40 0 + 945 52 40 1 + 946 52 41 0 + 947 52 41 1 + 948 53 40 0 + 949 53 40 1 + 950 53 41 0 + 951 53 41 1 + 952 55 41 1 + 953 55 41 0 + 954 55 40 1 + 955 55 40 0 + 956 54 41 1 + 957 54 41 0 + 958 54 40 1 + 959 54 40 0 + 960 60 41 0 + 961 60 41 1 + 962 60 40 0 + 963 60 40 1 + 964 61 41 0 + 965 61 41 1 + 966 61 40 0 + 967 61 40 1 + 968 63 40 1 + 969 63 40 0 + 970 63 41 1 + 971 63 41 0 + 972 62 40 1 + 973 62 40 0 + 974 62 41 1 + 975 62 41 0 +1024 59 43 0 +1025 59 43 1 +1026 59 42 0 +1027 59 42 1 +1028 58 43 0 +1029 58 43 1 +1030 58 42 0 +1031 58 42 1 +1032 56 42 1 +1033 56 42 0 +1034 56 43 1 +1035 56 43 0 +1036 57 42 1 +1037 57 42 0 +1038 57 43 1 +1039 57 43 0 +1056 51 42 0 +1057 51 42 1 +1058 51 43 0 +1059 51 43 1 +1060 50 42 0 +1061 50 42 1 +1062 50 43 0 +1063 50 43 1 +1064 48 43 1 +1065 48 43 0 +1066 48 42 1 +1067 48 42 0 +1068 49 43 1 +1069 49 43 0 +1070 49 42 1 +1071 49 42 0 +1072 52 42 0 +1073 52 42 1 +1074 52 43 0 +1075 52 43 1 +1076 53 42 0 +1077 53 42 1 +1078 53 43 0 +1079 53 43 1 +1080 55 43 1 +1081 55 43 0 +1082 55 42 1 +1083 55 42 0 +1084 54 43 1 +1085 54 43 0 +1086 54 42 1 +1087 54 42 0 +1088 60 43 0 +1089 60 43 1 +1090 60 42 0 +1091 60 42 1 +1092 61 43 0 +1093 61 43 1 +1094 61 42 0 +1095 61 42 1 +1096 63 42 1 +1097 63 42 0 +1098 63 43 1 +1099 63 43 0 +1100 62 42 1 +1101 62 42 0 +1102 62 43 1 +1103 62 43 0 +1152 59 45 0 +1153 59 45 1 +1154 59 44 0 +1155 59 44 1 +1156 58 45 0 +1157 58 45 1 +1158 58 44 0 +1159 58 44 1 +1160 56 44 1 +1161 56 44 0 +1162 56 45 1 +1163 56 45 0 +1164 57 44 1 +1165 57 44 0 +1166 57 45 1 +1167 57 45 0 +1184 51 44 0 +1185 51 44 1 +1186 51 45 0 +1187 51 45 1 +1188 50 44 0 +1189 50 44 1 +1190 50 45 0 +1191 50 45 1 +1192 48 45 1 +1193 48 45 0 +1194 48 44 1 +1195 48 44 0 +1196 49 45 1 +1197 49 45 0 +1198 49 44 1 +1199 49 44 0 +1200 52 44 0 +1201 52 44 1 +1202 52 45 0 +1203 52 45 1 +1204 53 44 0 +1205 53 44 1 +1206 53 45 0 +1207 53 45 1 +1208 55 45 1 +1209 55 45 0 +1210 55 44 1 +1211 55 44 0 +1212 54 45 1 +1213 54 45 0 +1214 54 44 1 +1215 54 44 0 +1216 60 45 0 +1217 60 45 1 +1218 60 44 0 +1219 60 44 1 +1220 61 45 0 +1221 61 45 1 +1222 61 44 0 +1223 61 44 1 +1224 63 44 1 +1225 63 44 0 +1226 63 45 1 +1227 63 45 0 +1228 62 44 1 +1229 62 44 0 +1230 62 45 1 +1231 62 45 0 +1280 59 47 0 +1281 59 47 1 +1282 59 46 0 +1283 59 46 1 +1284 58 47 0 +1285 58 47 1 +1286 58 46 0 +1287 58 46 1 +1288 56 46 1 +1289 56 46 0 +1290 56 47 1 +1291 56 47 0 +1292 57 46 1 +1293 57 46 0 +1294 57 47 1 +1295 57 47 0 +1312 51 46 0 +1313 51 46 1 +1314 51 47 0 +1315 51 47 1 +1316 50 46 0 +1317 50 46 1 +1318 50 47 0 +1319 50 47 1 +1320 48 47 1 +1321 48 47 0 +1322 48 46 1 +1323 48 46 0 +1324 49 47 1 +1325 49 47 0 +1326 49 46 1 +1327 49 46 0 +1328 52 46 0 +1329 52 46 1 +1330 52 47 0 +1331 52 47 1 +1332 53 46 0 +1333 53 46 1 +1334 53 47 0 +1335 53 47 1 +1336 55 47 1 +1337 55 47 0 +1338 55 46 1 +1339 55 46 0 +1340 54 47 1 +1341 54 47 0 +1342 54 46 1 +1343 54 46 0 +1344 60 47 0 +1345 60 47 1 +1346 60 46 0 +1347 60 46 1 +1348 61 47 0 +1349 61 47 1 +1350 61 46 0 +1351 61 46 1 +1352 63 46 1 +1353 63 46 0 +1354 63 47 1 +1355 63 47 0 +1356 62 46 1 +1357 62 46 0 +1358 62 47 1 +1359 62 47 0 +1408 59 49 0 +1409 59 49 1 +1410 59 48 0 +1411 59 48 1 +1412 58 49 0 +1413 58 49 1 +1414 58 48 0 +1415 58 48 1 +1416 56 48 1 +1417 56 48 0 +1418 56 49 1 +1419 56 49 0 +1420 57 48 1 +1421 57 48 0 +1422 57 49 1 +1423 57 49 0 +1440 51 48 0 +1441 51 48 1 +1442 51 49 0 +1443 51 49 1 +1444 50 48 0 +1445 50 48 1 +1446 50 49 0 +1447 50 49 1 +1448 48 49 1 +1449 48 49 0 +1450 48 48 1 +1451 48 48 0 +1452 49 49 1 +1453 49 49 0 +1454 49 48 1 +1455 49 48 0 +1456 52 48 0 +1457 52 48 1 +1458 52 49 0 +1459 52 49 1 +1460 53 48 0 +1461 53 48 1 +1462 53 49 0 +1463 53 49 1 +1464 55 49 1 +1465 55 49 0 +1466 55 48 1 +1467 55 48 0 +1468 54 49 1 +1469 54 49 0 +1470 54 48 1 +1471 54 48 0 +1472 60 49 0 +1473 60 49 1 +1474 60 48 0 +1475 60 48 1 +1476 61 49 0 +1477 61 49 1 +1478 61 48 0 +1479 61 48 1 +1480 63 48 1 +1481 63 48 0 +1482 63 49 1 +1483 63 49 0 +1484 62 48 1 +1485 62 48 0 +1486 62 49 1 +1487 62 49 0 +1536 59 51 0 +1537 59 51 1 +1538 59 50 0 +1539 59 50 1 +1540 58 51 0 +1541 58 51 1 +1542 58 50 0 +1543 58 50 1 +1544 56 50 1 +1545 56 50 0 +1546 56 51 1 +1547 56 51 0 +1548 57 50 1 +1549 57 50 0 +1550 57 51 1 +1551 57 51 0 +1568 51 50 0 +1569 51 50 1 +1570 51 51 0 +1571 51 51 1 +1572 50 50 0 +1573 50 50 1 +1574 50 51 0 +1575 50 51 1 +1576 48 51 1 +1577 48 51 0 +1578 48 50 1 +1579 48 50 0 +1580 49 51 1 +1581 49 51 0 +1582 49 50 1 +1583 49 50 0 +1584 52 50 0 +1585 52 50 1 +1586 52 51 0 +1587 52 51 1 +1588 53 50 0 +1589 53 50 1 +1590 53 51 0 +1591 53 51 1 +1592 55 51 1 +1593 55 51 0 +1594 55 50 1 +1595 55 50 0 +1596 54 51 1 +1597 54 51 0 +1598 54 50 1 +1599 54 50 0 +1600 60 51 0 +1601 60 51 1 +1602 60 50 0 +1603 60 50 1 +1604 61 51 0 +1605 61 51 1 +1606 61 50 0 +1607 61 50 1 +1608 63 50 1 +1609 63 50 0 +1610 63 51 1 +1611 63 51 0 +1612 62 50 1 +1613 62 50 0 +1614 62 51 1 +1615 62 51 0 +1664 59 53 0 +1665 59 53 1 +1666 59 52 0 +1667 59 52 1 +1668 58 53 0 +1669 58 53 1 +1670 58 52 0 +1671 58 52 1 +1672 56 52 1 +1673 56 52 0 +1674 56 53 1 +1675 56 53 0 +1676 57 52 1 +1677 57 52 0 +1678 57 53 1 +1679 57 53 0 +1696 51 52 0 +1697 51 52 1 +1698 51 53 0 +1699 51 53 1 +1700 50 52 0 +1701 50 52 1 +1702 50 53 0 +1703 50 53 1 +1704 48 53 1 +1705 48 53 0 +1706 48 52 1 +1707 48 52 0 +1708 49 53 1 +1709 49 53 0 +1710 49 52 1 +1711 49 52 0 +1712 52 52 0 +1713 52 52 1 +1714 52 53 0 +1715 52 53 1 +1716 53 52 0 +1717 53 52 1 +1718 53 53 0 +1719 53 53 1 +1720 55 53 1 +1721 55 53 0 +1722 55 52 1 +1723 55 52 0 +1724 54 53 1 +1725 54 53 0 +1726 54 52 1 +1727 54 52 0 +1728 60 53 0 +1729 60 53 1 +1730 60 52 0 +1731 60 52 1 +1732 61 53 0 +1733 61 53 1 +1734 61 52 0 +1735 61 52 1 +1736 63 52 1 +1737 63 52 0 +1738 63 53 1 +1739 63 53 0 +1740 62 52 1 +1741 62 52 0 +1742 62 53 1 +1743 62 53 0 +1792 59 55 0 +1793 59 55 1 +1794 59 54 0 +1795 59 54 1 +1796 58 55 0 +1797 58 55 1 +1798 58 54 0 +1799 58 54 1 +1800 56 54 1 +1801 56 54 0 +1802 56 55 1 +1803 56 55 0 +1804 57 54 1 +1805 57 54 0 +1806 57 55 1 +1807 57 55 0 +1824 51 54 0 +1825 51 54 1 +1826 51 55 0 +1827 51 55 1 +1828 50 54 0 +1829 50 54 1 +1830 50 55 0 +1831 50 55 1 +1832 48 55 1 +1833 48 55 0 +1834 48 54 1 +1835 48 54 0 +1836 49 55 1 +1837 49 55 0 +1838 49 54 1 +1839 49 54 0 +1840 52 54 0 +1841 52 54 1 +1842 52 55 0 +1843 52 55 1 +1844 53 54 0 +1845 53 54 1 +1846 53 55 0 +1847 53 55 1 +1848 55 55 1 +1849 55 55 0 +1850 55 54 1 +1851 55 54 0 +1852 54 55 1 +1853 54 55 0 +1854 54 54 1 +1855 54 54 0 +1856 60 55 0 +1857 60 55 1 +1858 60 54 0 +1859 60 54 1 +1860 61 55 0 +1861 61 55 1 +1862 61 54 0 +1863 61 54 1 +1864 63 54 1 +1865 63 54 0 +1866 63 55 1 +1867 63 55 0 +1868 62 54 1 +1869 62 54 0 +1870 62 55 1 +1871 62 55 0 +2176 59 27 0 +2177 59 27 1 +2178 59 26 0 +2179 59 26 1 +2180 58 27 0 +2181 58 27 1 +2182 58 26 0 +2183 58 26 1 +2184 56 26 1 +2185 56 26 0 +2186 56 27 1 +2187 56 27 0 +2188 57 26 1 +2189 57 26 0 +2190 57 27 1 +2191 57 27 0 +2208 51 26 0 +2209 51 26 1 +2210 51 27 0 +2211 51 27 1 +2212 50 26 0 +2213 50 26 1 +2214 50 27 0 +2215 50 27 1 +2216 48 27 1 +2217 48 27 0 +2218 48 26 1 +2219 48 26 0 +2220 49 27 1 +2221 49 27 0 +2222 49 26 1 +2223 49 26 0 +2224 52 26 0 +2225 52 26 1 +2226 52 27 0 +2227 52 27 1 +2228 53 26 0 +2229 53 26 1 +2230 53 27 0 +2231 53 27 1 +2232 55 27 1 +2233 55 27 0 +2234 55 26 1 +2235 55 26 0 +2236 54 27 1 +2237 54 27 0 +2238 54 26 1 +2239 54 26 0 +2240 60 27 0 +2241 60 27 1 +2242 60 26 0 +2243 60 26 1 +2244 61 27 0 +2245 61 27 1 +2246 61 26 0 +2247 61 26 1 +2248 63 26 1 +2249 63 26 0 +2250 63 27 1 +2251 63 27 0 +2252 62 26 1 +2253 62 26 0 +2254 62 27 1 +2255 62 27 0 +2304 59 25 0 +2305 59 25 1 +2306 59 24 0 +2307 59 24 1 +2308 58 25 0 +2309 58 25 1 +2310 58 24 0 +2311 58 24 1 +2312 56 24 1 +2313 56 24 0 +2314 56 25 1 +2315 56 25 0 +2316 57 24 1 +2317 57 24 0 +2318 57 25 1 +2319 57 25 0 +2336 51 24 0 +2337 51 24 1 +2338 51 25 0 +2339 51 25 1 +2340 50 24 0 +2341 50 24 1 +2342 50 25 0 +2343 50 25 1 +2344 48 25 1 +2345 48 25 0 +2346 48 24 1 +2347 48 24 0 +2348 49 25 1 +2349 49 25 0 +2350 49 24 1 +2351 49 24 0 +2352 52 24 0 +2353 52 24 1 +2354 52 25 0 +2355 52 25 1 +2356 53 24 0 +2357 53 24 1 +2358 53 25 0 +2359 53 25 1 +2360 55 25 1 +2361 55 25 0 +2362 55 24 1 +2363 55 24 0 +2364 54 25 1 +2365 54 25 0 +2366 54 24 1 +2367 54 24 0 +2368 60 25 0 +2369 60 25 1 +2370 60 24 0 +2371 60 24 1 +2372 61 25 0 +2373 61 25 1 +2374 61 24 0 +2375 61 24 1 +2376 63 24 1 +2377 63 24 0 +2378 63 25 1 +2379 63 25 0 +2380 62 24 1 +2381 62 24 0 +2382 62 25 1 +2383 62 25 0 +2432 59 23 0 +2433 59 23 1 +2434 59 22 0 +2435 59 22 1 +2436 58 23 0 +2437 58 23 1 +2438 58 22 0 +2439 58 22 1 +2440 56 22 1 +2441 56 22 0 +2442 56 23 1 +2443 56 23 0 +2444 57 22 1 +2445 57 22 0 +2446 57 23 1 +2447 57 23 0 +2464 51 22 0 +2465 51 22 1 +2466 51 23 0 +2467 51 23 1 +2468 50 22 0 +2469 50 22 1 +2470 50 23 0 +2471 50 23 1 +2472 48 23 1 +2473 48 23 0 +2474 48 22 1 +2475 48 22 0 +2476 49 23 1 +2477 49 23 0 +2478 49 22 1 +2479 49 22 0 +2480 52 22 0 +2481 52 22 1 +2482 52 23 0 +2483 52 23 1 +2484 53 22 0 +2485 53 22 1 +2486 53 23 0 +2487 53 23 1 +2488 55 23 1 +2489 55 23 0 +2490 55 22 1 +2491 55 22 0 +2492 54 23 1 +2493 54 23 0 +2494 54 22 1 +2495 54 22 0 +2496 60 23 0 +2497 60 23 1 +2498 60 22 0 +2499 60 22 1 +2500 61 23 0 +2501 61 23 1 +2502 61 22 0 +2503 61 22 1 +2504 63 22 1 +2505 63 22 0 +2506 63 23 1 +2507 63 23 0 +2508 62 22 1 +2509 62 22 0 +2510 62 23 1 +2511 62 23 0 +2560 59 21 0 +2561 59 21 1 +2562 59 20 0 +2563 59 20 1 +2564 58 21 0 +2565 58 21 1 +2566 58 20 0 +2567 58 20 1 +2568 56 20 1 +2569 56 20 0 +2570 56 21 1 +2571 56 21 0 +2572 57 20 1 +2573 57 20 0 +2574 57 21 1 +2575 57 21 0 +2592 51 20 0 +2593 51 20 1 +2594 51 21 0 +2595 51 21 1 +2596 50 20 0 +2597 50 20 1 +2598 50 21 0 +2599 50 21 1 +2600 48 21 1 +2601 48 21 0 +2602 48 20 1 +2603 48 20 0 +2604 49 21 1 +2605 49 21 0 +2606 49 20 1 +2607 49 20 0 +2608 52 20 0 +2609 52 20 1 +2610 52 21 0 +2611 52 21 1 +2612 53 20 0 +2613 53 20 1 +2614 53 21 0 +2615 53 21 1 +2616 55 21 1 +2617 55 21 0 +2618 55 20 1 +2619 55 20 0 +2620 54 21 1 +2621 54 21 0 +2622 54 20 1 +2623 54 20 0 +2624 60 21 0 +2625 60 21 1 +2626 60 20 0 +2627 60 20 1 +2628 61 21 0 +2629 61 21 1 +2630 61 20 0 +2631 61 20 1 +2632 63 20 1 +2633 63 20 0 +2634 63 21 1 +2635 63 21 0 +2636 62 20 1 +2637 62 20 0 +2638 62 21 1 +2639 62 21 0 +2688 59 19 0 +2689 59 19 1 +2690 59 18 0 +2691 59 18 1 +2692 58 19 0 +2693 58 19 1 +2694 58 18 0 +2695 58 18 1 +2696 56 18 1 +2697 56 18 0 +2698 56 19 1 +2699 56 19 0 +2700 57 18 1 +2701 57 18 0 +2702 57 19 1 +2703 57 19 0 +2720 51 18 0 +2721 51 18 1 +2722 51 19 0 +2723 51 19 1 +2724 50 18 0 +2725 50 18 1 +2726 50 19 0 +2727 50 19 1 +2728 48 19 1 +2729 48 19 0 +2730 48 18 1 +2731 48 18 0 +2732 49 19 1 +2733 49 19 0 +2734 49 18 1 +2735 49 18 0 +2736 52 18 0 +2737 52 18 1 +2738 52 19 0 +2739 52 19 1 +2740 53 18 0 +2741 53 18 1 +2742 53 19 0 +2743 53 19 1 +2744 55 19 1 +2745 55 19 0 +2746 55 18 1 +2747 55 18 0 +2748 54 19 1 +2749 54 19 0 +2750 54 18 1 +2751 54 18 0 +2752 60 19 0 +2753 60 19 1 +2754 60 18 0 +2755 60 18 1 +2756 61 19 0 +2757 61 19 1 +2758 61 18 0 +2759 61 18 1 +2760 63 18 1 +2761 63 18 0 +2762 63 19 1 +2763 63 19 0 +2764 62 18 1 +2765 62 18 0 +2766 62 19 1 +2767 62 19 0 +2816 59 17 0 +2817 59 17 1 +2818 59 16 0 +2819 59 16 1 +2820 58 17 0 +2821 58 17 1 +2822 58 16 0 +2823 58 16 1 +2824 56 16 1 +2825 56 16 0 +2826 56 17 1 +2827 56 17 0 +2828 57 16 1 +2829 57 16 0 +2830 57 17 1 +2831 57 17 0 +2848 51 16 0 +2849 51 16 1 +2850 51 17 0 +2851 51 17 1 +2852 50 16 0 +2853 50 16 1 +2854 50 17 0 +2855 50 17 1 +2856 48 17 1 +2857 48 17 0 +2858 48 16 1 +2859 48 16 0 +2860 49 17 1 +2861 49 17 0 +2862 49 16 1 +2863 49 16 0 +2864 52 16 0 +2865 52 16 1 +2866 52 17 0 +2867 52 17 1 +2868 53 16 0 +2869 53 16 1 +2870 53 17 0 +2871 53 17 1 +2872 55 17 1 +2873 55 17 0 +2874 55 16 1 +2875 55 16 0 +2876 54 17 1 +2877 54 17 0 +2878 54 16 1 +2879 54 16 0 +2880 60 17 0 +2881 60 17 1 +2882 60 16 0 +2883 60 16 1 +2884 61 17 0 +2885 61 17 1 +2886 61 16 0 +2887 61 16 1 +2888 63 16 1 +2889 63 16 0 +2890 63 17 1 +2891 63 17 0 +2892 62 16 1 +2893 62 16 0 +2894 62 17 1 +2895 62 17 0 +2944 59 15 0 +2945 59 15 1 +2946 59 14 0 +2947 59 14 1 +2948 58 15 0 +2949 58 15 1 +2950 58 14 0 +2951 58 14 1 +2952 56 14 1 +2953 56 14 0 +2954 56 15 1 +2955 56 15 0 +2956 57 14 1 +2957 57 14 0 +2958 57 15 1 +2959 57 15 0 +2976 51 14 0 +2977 51 14 1 +2978 51 15 0 +2979 51 15 1 +2980 50 14 0 +2981 50 14 1 +2982 50 15 0 +2983 50 15 1 +2984 48 15 1 +2985 48 15 0 +2986 48 14 1 +2987 48 14 0 +2988 49 15 1 +2989 49 15 0 +2990 49 14 1 +2991 49 14 0 +2992 52 14 0 +2993 52 14 1 +2994 52 15 0 +2995 52 15 1 +2996 53 14 0 +2997 53 14 1 +2998 53 15 0 +2999 53 15 1 +3000 55 15 1 +3001 55 15 0 +3002 55 14 1 +3003 55 14 0 +3004 54 15 1 +3005 54 15 0 +3006 54 14 1 +3007 54 14 0 +3008 60 15 0 +3009 60 15 1 +3010 60 14 0 +3011 60 14 1 +3012 61 15 0 +3013 61 15 1 +3014 61 14 0 +3015 61 14 1 +3016 63 14 1 +3017 63 14 0 +3018 63 15 1 +3019 63 15 0 +3020 62 14 1 +3021 62 14 0 +3022 62 15 1 +3023 62 15 0 +3072 59 13 0 +3073 59 13 1 +3074 59 12 0 +3075 59 12 1 +3076 58 13 0 +3077 58 13 1 +3078 58 12 0 +3079 58 12 1 +3080 56 12 1 +3081 56 12 0 +3082 56 13 1 +3083 56 13 0 +3084 57 12 1 +3085 57 12 0 +3086 57 13 1 +3087 57 13 0 +3104 51 12 0 +3105 51 12 1 +3106 51 13 0 +3107 51 13 1 +3108 50 12 0 +3109 50 12 1 +3110 50 13 0 +3111 50 13 1 +3112 48 13 1 +3113 48 13 0 +3114 48 12 1 +3115 48 12 0 +3116 49 13 1 +3117 49 13 0 +3118 49 12 1 +3119 49 12 0 +3120 52 12 0 +3121 52 12 1 +3122 52 13 0 +3123 52 13 1 +3124 53 12 0 +3125 53 12 1 +3126 53 13 0 +3127 53 13 1 +3128 55 13 1 +3129 55 13 0 +3130 55 12 1 +3131 55 12 0 +3132 54 13 1 +3133 54 13 0 +3134 54 12 1 +3135 54 12 0 +3136 60 13 0 +3137 60 13 1 +3138 60 12 0 +3139 60 12 1 +3140 61 13 0 +3141 61 13 1 +3142 61 12 0 +3143 61 12 1 +3144 63 12 1 +3145 63 12 0 +3146 63 13 1 +3147 63 13 0 +3148 62 12 1 +3149 62 12 0 +3150 62 13 1 +3151 62 13 0 +3200 59 11 0 +3201 59 11 1 +3202 59 10 0 +3203 59 10 1 +3204 58 11 0 +3205 58 11 1 +3206 58 10 0 +3207 58 10 1 +3208 56 10 1 +3209 56 10 0 +3210 56 11 1 +3211 56 11 0 +3212 57 10 1 +3213 57 10 0 +3214 57 11 1 +3215 57 11 0 +3232 51 10 0 +3233 51 10 1 +3234 51 11 0 +3235 51 11 1 +3236 50 10 0 +3237 50 10 1 +3238 50 11 0 +3239 50 11 1 +3240 48 11 1 +3241 48 11 0 +3242 48 10 1 +3243 48 10 0 +3244 49 11 1 +3245 49 11 0 +3246 49 10 1 +3247 49 10 0 +3248 52 10 0 +3249 52 10 1 +3250 52 11 0 +3251 52 11 1 +3252 53 10 0 +3253 53 10 1 +3254 53 11 0 +3255 53 11 1 +3256 55 11 1 +3257 55 11 0 +3258 55 10 1 +3259 55 10 0 +3260 54 11 1 +3261 54 11 0 +3262 54 10 1 +3263 54 10 0 +3264 60 11 0 +3265 60 11 1 +3266 60 10 0 +3267 60 10 1 +3268 61 11 0 +3269 61 11 1 +3270 61 10 0 +3271 61 10 1 +3272 63 10 1 +3273 63 10 0 +3274 63 11 1 +3275 63 11 0 +3276 62 10 1 +3277 62 10 0 +3278 62 11 1 +3279 62 11 0 +3328 59 9 0 +3329 59 9 1 +3330 59 8 0 +3331 59 8 1 +3332 58 9 0 +3333 58 9 1 +3334 58 8 0 +3335 58 8 1 +3336 56 8 1 +3337 56 8 0 +3338 56 9 1 +3339 56 9 0 +3340 57 8 1 +3341 57 8 0 +3342 57 9 1 +3343 57 9 0 +3360 51 8 0 +3361 51 8 1 +3362 51 9 0 +3363 51 9 1 +3364 50 8 0 +3365 50 8 1 +3366 50 9 0 +3367 50 9 1 +3368 48 9 1 +3369 48 9 0 +3370 48 8 1 +3371 48 8 0 +3372 49 9 1 +3373 49 9 0 +3374 49 8 1 +3375 49 8 0 +3376 52 8 0 +3377 52 8 1 +3378 52 9 0 +3379 52 9 1 +3380 53 8 0 +3381 53 8 1 +3382 53 9 0 +3383 53 9 1 +3384 55 9 1 +3385 55 9 0 +3386 55 8 1 +3387 55 8 0 +3388 54 9 1 +3389 54 9 0 +3390 54 8 1 +3391 54 8 0 +3392 60 9 0 +3393 60 9 1 +3394 60 8 0 +3395 60 8 1 +3396 61 9 0 +3397 61 9 1 +3398 61 8 0 +3399 61 8 1 +3400 63 8 1 +3401 63 8 0 +3402 63 9 1 +3403 63 9 0 +3404 62 8 1 +3405 62 8 0 +3406 62 9 1 +3407 62 9 0 +3456 59 7 0 +3457 59 7 1 +3458 59 6 0 +3459 59 6 1 +3460 58 7 0 +3461 58 7 1 +3462 58 6 0 +3463 58 6 1 +3464 56 6 1 +3465 56 6 0 +3466 56 7 1 +3467 56 7 0 +3468 57 6 1 +3469 57 6 0 +3470 57 7 1 +3471 57 7 0 +3488 51 6 0 +3489 51 6 1 +3490 51 7 0 +3491 51 7 1 +3492 50 6 0 +3493 50 6 1 +3494 50 7 0 +3495 50 7 1 +3496 48 7 1 +3497 48 7 0 +3498 48 6 1 +3499 48 6 0 +3500 49 7 1 +3501 49 7 0 +3502 49 6 1 +3503 49 6 0 +3504 52 6 0 +3505 52 6 1 +3506 52 7 0 +3507 52 7 1 +3508 53 6 0 +3509 53 6 1 +3510 53 7 0 +3511 53 7 1 +3512 55 7 1 +3513 55 7 0 +3514 55 6 1 +3515 55 6 0 +3516 54 7 1 +3517 54 7 0 +3518 54 6 1 +3519 54 6 0 +3520 60 7 0 +3521 60 7 1 +3522 60 6 0 +3523 60 6 1 +3524 61 7 0 +3525 61 7 1 +3526 61 6 0 +3527 61 6 1 +3528 63 6 1 +3529 63 6 0 +3530 63 7 1 +3531 63 7 0 +3532 62 6 1 +3533 62 6 0 +3534 62 7 1 +3535 62 7 0 +3584 59 5 0 +3585 59 5 1 +3586 59 4 0 +3587 59 4 1 +3588 58 5 0 +3589 58 5 1 +3590 58 4 0 +3591 58 4 1 +3592 56 4 1 +3593 56 4 0 +3594 56 5 1 +3595 56 5 0 +3596 57 4 1 +3597 57 4 0 +3598 57 5 1 +3599 57 5 0 +3616 51 4 0 +3617 51 4 1 +3618 51 5 0 +3619 51 5 1 +3620 50 4 0 +3621 50 4 1 +3622 50 5 0 +3623 50 5 1 +3624 48 5 1 +3625 48 5 0 +3626 48 4 1 +3627 48 4 0 +3628 49 5 1 +3629 49 5 0 +3630 49 4 1 +3631 49 4 0 +3632 52 4 0 +3633 52 4 1 +3634 52 5 0 +3635 52 5 1 +3636 53 4 0 +3637 53 4 1 +3638 53 5 0 +3639 53 5 1 +3640 55 5 1 +3641 55 5 0 +3642 55 4 1 +3643 55 4 0 +3644 54 5 1 +3645 54 5 0 +3646 54 4 1 +3647 54 4 0 +3648 60 5 0 +3649 60 5 1 +3650 60 4 0 +3651 60 4 1 +3652 61 5 0 +3653 61 5 1 +3654 61 4 0 +3655 61 4 1 +3656 63 4 1 +3657 63 4 0 +3658 63 5 1 +3659 63 5 0 +3660 62 4 1 +3661 62 4 0 +3662 62 5 1 +3663 62 5 0 +3712 59 3 0 +3713 59 3 1 +3714 59 2 0 +3715 59 2 1 +3716 58 3 0 +3717 58 3 1 +3718 58 2 0 +3719 58 2 1 +3720 56 2 1 +3721 56 2 0 +3722 56 3 1 +3723 56 3 0 +3724 57 2 1 +3725 57 2 0 +3726 57 3 1 +3727 57 3 0 +3744 51 2 0 +3745 51 2 1 +3746 51 3 0 +3747 51 3 1 +3748 50 2 0 +3749 50 2 1 +3750 50 3 0 +3751 50 3 1 +3752 48 3 1 +3753 48 3 0 +3754 48 2 1 +3755 48 2 0 +3756 49 3 1 +3757 49 3 0 +3758 49 2 1 +3759 49 2 0 +3760 52 2 0 +3761 52 2 1 +3762 52 3 0 +3763 52 3 1 +3764 53 2 0 +3765 53 2 1 +3766 53 3 0 +3767 53 3 1 +3768 55 3 1 +3769 55 3 0 +3770 55 2 1 +3771 55 2 0 +3772 54 3 1 +3773 54 3 0 +3774 54 2 1 +3775 54 2 0 +3776 60 3 0 +3777 60 3 1 +3778 60 2 0 +3779 60 2 1 +3780 61 3 0 +3781 61 3 1 +3782 61 2 0 +3783 61 2 1 +3784 63 2 1 +3785 63 2 0 +3786 63 3 1 +3787 63 3 0 +3788 62 2 1 +3789 62 2 0 +3790 62 3 1 +3791 62 3 0 +3840 59 1 0 +3841 59 1 1 +3842 59 0 0 +3843 59 0 1 +3844 58 1 0 +3845 58 1 1 +3846 58 0 0 +3847 58 0 1 +3848 56 0 1 +3849 56 0 0 +3850 56 1 1 +3851 56 1 0 +3852 57 0 1 +3853 57 0 0 +3854 57 1 1 +3855 57 1 0 +3872 51 0 0 +3873 51 0 1 +3874 51 1 0 +3875 51 1 1 +3876 50 0 0 +3877 50 0 1 +3878 50 1 0 +3879 50 1 1 +3880 48 1 1 +3881 48 1 0 +3882 48 0 1 +3883 48 0 0 +3884 49 1 1 +3885 49 1 0 +3886 49 0 1 +3887 49 0 0 +3888 52 0 0 +3889 52 0 1 +3890 52 1 0 +3891 52 1 1 +3892 53 0 0 +3893 53 0 1 +3894 53 1 0 +3895 53 1 1 +3896 55 1 1 +3897 55 1 0 +3898 55 0 1 +3899 55 0 0 +3900 54 1 1 +3901 54 1 0 +3902 54 0 1 +3903 54 0 0 +3904 60 1 0 +3905 60 1 1 +3906 60 0 0 +3907 60 0 1 +3908 61 1 0 +3909 61 1 1 +3910 61 0 0 +3911 61 0 1 +3912 63 0 1 +3913 63 0 0 +3914 63 1 1 +3915 63 1 0 +3916 62 0 1 +3917 62 0 0 +3918 62 1 1 +3919 62 1 0 diff --git a/Detectors/PHOS/base/files/Mod2RCU0.data b/Detectors/PHOS/base/files/Mod2RCU0.data new file mode 100644 index 0000000000000..4739df6e3e61e --- /dev/null +++ b/Detectors/PHOS/base/files/Mod2RCU0.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 0 0 2 + 1 0 1 2 + 2 0 2 2 + 3 0 3 2 + 4 0 4 2 + 5 0 5 2 + 6 0 6 2 + 7 0 7 2 + 8 0 8 2 + 9 0 9 2 + 10 0 10 2 + 11 0 11 2 + 12 0 12 2 + 13 0 13 2 + 14 0 14 2 + 15 0 15 2 + 16 0 16 2 + 17 0 17 2 + 18 0 18 2 + 19 0 19 2 + 20 0 20 2 + 21 0 21 2 + 22 0 22 2 + 23 0 23 2 + 24 0 24 2 + 25 0 25 2 + 26 0 26 2 + 27 0 27 2 + 28 0 28 2 + 29 0 29 2 + 30 0 30 2 + 31 0 31 2 + 32 0 32 2 + 33 0 33 2 + 34 0 34 2 + 35 0 35 2 + 36 0 36 2 + 37 0 37 2 + 38 0 38 2 + 39 0 39 2 + 40 0 40 2 + 41 0 41 2 + 42 0 42 2 + 43 0 43 2 + 44 0 44 2 + 45 0 45 2 + 46 0 46 2 + 47 0 47 2 + 48 0 48 2 + 49 0 49 2 + 50 0 50 2 + 51 0 51 2 + 52 0 52 2 + 53 0 53 2 + 54 0 54 2 + 55 0 55 2 + 56 0 56 2 + 57 0 57 2 + 58 0 58 2 + 59 0 59 2 + 60 0 60 2 + 61 0 61 2 + 62 0 62 2 + 63 0 63 2 + 64 0 64 2 + 65 0 65 2 + 66 0 66 2 + 67 0 67 2 + 68 0 68 2 + 69 0 69 2 + 70 0 70 2 + 71 0 71 2 + 72 0 72 2 + 73 0 73 2 + 74 0 74 2 + 75 0 75 2 + 76 0 76 2 + 77 0 77 2 + 78 0 78 2 + 79 0 79 2 + 80 0 80 2 + 81 0 81 2 + 82 0 82 2 + 83 0 83 2 + 84 0 84 2 + 85 0 85 2 + 86 0 86 2 + 87 0 87 2 + 88 0 88 2 + 89 0 89 2 + 90 0 90 2 + 91 0 91 2 + 92 0 92 2 + 93 0 93 2 + 94 0 94 2 + 95 0 95 2 + 96 0 96 2 + 97 0 97 2 + 98 0 98 2 + 99 0 99 2 + 100 0 100 2 + 101 0 101 2 + 102 0 102 2 + 103 0 103 2 + 104 0 104 2 + 105 0 105 2 + 106 0 106 2 + 107 0 107 2 + 108 0 108 2 + 109 0 109 2 + 110 0 110 2 + 111 0 111 2 + 112 0 112 2 + 113 0 113 2 + 114 0 114 2 + 115 0 115 2 + 116 0 116 2 + 117 0 117 2 + 118 0 118 2 + 119 0 119 2 + 120 0 120 2 + 121 0 121 2 + 122 0 122 2 + 123 0 123 2 + 124 0 124 2 + 125 0 125 2 + 126 0 126 2 + 127 0 127 2 +2048 0 2048 2 +2049 0 2049 2 +2050 0 2050 2 +2051 0 2051 2 +2052 0 2052 2 +2053 0 2053 2 +2054 0 2054 2 +2055 0 2055 2 +2056 0 2056 2 +2057 0 2057 2 +2058 0 2058 2 +2059 0 2059 2 +2060 0 2060 2 +2061 0 2061 2 +2062 0 2062 2 +2063 0 2063 2 +2064 0 2064 2 +2065 0 2065 2 +2066 0 2066 2 +2067 0 2067 2 +2068 0 2068 2 +2069 0 2069 2 +2070 0 2070 2 +2071 0 2071 2 +2072 0 2072 2 +2073 0 2073 2 +2074 0 2074 2 +2075 0 2075 2 +2076 0 2076 2 +2077 0 2077 2 +2078 0 2078 2 +2079 0 2079 2 +2080 0 2080 2 +2081 0 2081 2 +2082 0 2082 2 +2083 0 2083 2 +2084 0 2084 2 +2085 0 2085 2 +2086 0 2086 2 +2087 0 2087 2 +2088 0 2088 2 +2089 0 2089 2 +2090 0 2090 2 +2091 0 2091 2 +2092 0 2092 2 +2093 0 2093 2 +2094 0 2094 2 +2095 0 2095 2 +2096 0 2096 2 +2097 0 2097 2 +2098 0 2098 2 +2099 0 2099 2 +2100 0 2100 2 +2101 0 2101 2 +2102 0 2102 2 +2103 0 2103 2 +2104 0 2104 2 +2105 0 2105 2 +2106 0 2106 2 +2107 0 2107 2 +2108 0 2108 2 +2109 0 2109 2 +2110 0 2110 2 +2111 0 2111 2 +2112 0 2112 2 +2113 0 2113 2 +2114 0 2114 2 +2115 0 2115 2 +2116 0 2116 2 +2117 0 2117 2 +2118 0 2118 2 +2119 0 2119 2 +2120 0 2120 2 +2121 0 2121 2 +2122 0 2122 2 +2123 0 2123 2 +2124 0 2124 2 +2125 0 2125 2 +2126 0 2126 2 +2127 0 2127 2 +2128 0 2128 2 +2129 0 2129 2 +2130 0 2130 2 +2131 0 2131 2 +2132 0 2132 2 +2133 0 2133 2 +2134 0 2134 2 +2135 0 2135 2 +2136 0 2136 2 +2137 0 2137 2 +2138 0 2138 2 +2139 0 2139 2 +2140 0 2140 2 +2141 0 2141 2 +2142 0 2142 2 +2143 0 2143 2 +2144 0 2144 2 +2145 0 2145 2 +2146 0 2146 2 +2147 0 2147 2 +2148 0 2148 2 +2149 0 2149 2 +2150 0 2150 2 +2151 0 2151 2 +2152 0 2152 2 +2153 0 2153 2 +2154 0 2154 2 +2155 0 2155 2 +2156 0 2156 2 +2157 0 2157 2 +2158 0 2158 2 +2159 0 2159 2 +2160 0 2160 2 +2161 0 2161 2 +2162 0 2162 2 +2163 0 2163 2 +2164 0 2164 2 +2165 0 2165 2 +2166 0 2166 2 +2167 0 2167 2 +2168 0 2168 2 +2169 0 2169 2 +2170 0 2170 2 +2171 0 2171 2 +2172 0 2172 2 +2173 0 2173 2 +2174 0 2174 2 +2175 0 2175 2 + 128 11 29 0 + 129 11 29 1 + 130 11 28 0 + 131 11 28 1 + 132 10 29 0 + 133 10 29 1 + 134 10 28 0 + 135 10 28 1 + 136 8 28 1 + 137 8 28 0 + 138 8 29 1 + 139 8 29 0 + 140 9 28 1 + 141 9 28 0 + 142 9 29 1 + 143 9 29 0 + 160 3 28 0 + 161 3 28 1 + 162 3 29 0 + 163 3 29 1 + 164 2 28 0 + 165 2 28 1 + 166 2 29 0 + 167 2 29 1 + 168 0 29 1 + 169 0 29 0 + 170 0 28 1 + 171 0 28 0 + 172 1 29 1 + 173 1 29 0 + 174 1 28 1 + 175 1 28 0 + 176 4 28 0 + 177 4 28 1 + 178 4 29 0 + 179 4 29 1 + 180 5 28 0 + 181 5 28 1 + 182 5 29 0 + 183 5 29 1 + 184 7 29 1 + 185 7 29 0 + 186 7 28 1 + 187 7 28 0 + 188 6 29 1 + 189 6 29 0 + 190 6 28 1 + 191 6 28 0 + 192 12 29 0 + 193 12 29 1 + 194 12 28 0 + 195 12 28 1 + 196 13 29 0 + 197 13 29 1 + 198 13 28 0 + 199 13 28 1 + 200 15 28 1 + 201 15 28 0 + 202 15 29 1 + 203 15 29 0 + 204 14 28 1 + 205 14 28 0 + 206 14 29 1 + 207 14 29 0 + 256 11 31 0 + 257 11 31 1 + 258 11 30 0 + 259 11 30 1 + 260 10 31 0 + 261 10 31 1 + 262 10 30 0 + 263 10 30 1 + 264 8 30 1 + 265 8 30 0 + 266 8 31 1 + 267 8 31 0 + 268 9 30 1 + 269 9 30 0 + 270 9 31 1 + 271 9 31 0 + 288 3 30 0 + 289 3 30 1 + 290 3 31 0 + 291 3 31 1 + 292 2 30 0 + 293 2 30 1 + 294 2 31 0 + 295 2 31 1 + 296 0 31 1 + 297 0 31 0 + 298 0 30 1 + 299 0 30 0 + 300 1 31 1 + 301 1 31 0 + 302 1 30 1 + 303 1 30 0 + 304 4 30 0 + 305 4 30 1 + 306 4 31 0 + 307 4 31 1 + 308 5 30 0 + 309 5 30 1 + 310 5 31 0 + 311 5 31 1 + 312 7 31 1 + 313 7 31 0 + 314 7 30 1 + 315 7 30 0 + 316 6 31 1 + 317 6 31 0 + 318 6 30 1 + 319 6 30 0 + 320 12 31 0 + 321 12 31 1 + 322 12 30 0 + 323 12 30 1 + 324 13 31 0 + 325 13 31 1 + 326 13 30 0 + 327 13 30 1 + 328 15 30 1 + 329 15 30 0 + 330 15 31 1 + 331 15 31 0 + 332 14 30 1 + 333 14 30 0 + 334 14 31 1 + 335 14 31 0 + 384 11 33 0 + 385 11 33 1 + 386 11 32 0 + 387 11 32 1 + 388 10 33 0 + 389 10 33 1 + 390 10 32 0 + 391 10 32 1 + 392 8 32 1 + 393 8 32 0 + 394 8 33 1 + 395 8 33 0 + 396 9 32 1 + 397 9 32 0 + 398 9 33 1 + 399 9 33 0 + 416 3 32 0 + 417 3 32 1 + 418 3 33 0 + 419 3 33 1 + 420 2 32 0 + 421 2 32 1 + 422 2 33 0 + 423 2 33 1 + 424 0 33 1 + 425 0 33 0 + 426 0 32 1 + 427 0 32 0 + 428 1 33 1 + 429 1 33 0 + 430 1 32 1 + 431 1 32 0 + 432 4 32 0 + 433 4 32 1 + 434 4 33 0 + 435 4 33 1 + 436 5 32 0 + 437 5 32 1 + 438 5 33 0 + 439 5 33 1 + 440 7 33 1 + 441 7 33 0 + 442 7 32 1 + 443 7 32 0 + 444 6 33 1 + 445 6 33 0 + 446 6 32 1 + 447 6 32 0 + 448 12 33 0 + 449 12 33 1 + 450 12 32 0 + 451 12 32 1 + 452 13 33 0 + 453 13 33 1 + 454 13 32 0 + 455 13 32 1 + 456 15 32 1 + 457 15 32 0 + 458 15 33 1 + 459 15 33 0 + 460 14 32 1 + 461 14 32 0 + 462 14 33 1 + 463 14 33 0 + 512 11 35 0 + 513 11 35 1 + 514 11 34 0 + 515 11 34 1 + 516 10 35 0 + 517 10 35 1 + 518 10 34 0 + 519 10 34 1 + 520 8 34 1 + 521 8 34 0 + 522 8 35 1 + 523 8 35 0 + 524 9 34 1 + 525 9 34 0 + 526 9 35 1 + 527 9 35 0 + 544 3 34 0 + 545 3 34 1 + 546 3 35 0 + 547 3 35 1 + 548 2 34 0 + 549 2 34 1 + 550 2 35 0 + 551 2 35 1 + 552 0 35 1 + 553 0 35 0 + 554 0 34 1 + 555 0 34 0 + 556 1 35 1 + 557 1 35 0 + 558 1 34 1 + 559 1 34 0 + 560 4 34 0 + 561 4 34 1 + 562 4 35 0 + 563 4 35 1 + 564 5 34 0 + 565 5 34 1 + 566 5 35 0 + 567 5 35 1 + 568 7 35 1 + 569 7 35 0 + 570 7 34 1 + 571 7 34 0 + 572 6 35 1 + 573 6 35 0 + 574 6 34 1 + 575 6 34 0 + 576 12 35 0 + 577 12 35 1 + 578 12 34 0 + 579 12 34 1 + 580 13 35 0 + 581 13 35 1 + 582 13 34 0 + 583 13 34 1 + 584 15 34 1 + 585 15 34 0 + 586 15 35 1 + 587 15 35 0 + 588 14 34 1 + 589 14 34 0 + 590 14 35 1 + 591 14 35 0 + 640 11 37 0 + 641 11 37 1 + 642 11 36 0 + 643 11 36 1 + 644 10 37 0 + 645 10 37 1 + 646 10 36 0 + 647 10 36 1 + 648 8 36 1 + 649 8 36 0 + 650 8 37 1 + 651 8 37 0 + 652 9 36 1 + 653 9 36 0 + 654 9 37 1 + 655 9 37 0 + 672 3 36 0 + 673 3 36 1 + 674 3 37 0 + 675 3 37 1 + 676 2 36 0 + 677 2 36 1 + 678 2 37 0 + 679 2 37 1 + 680 0 37 1 + 681 0 37 0 + 682 0 36 1 + 683 0 36 0 + 684 1 37 1 + 685 1 37 0 + 686 1 36 1 + 687 1 36 0 + 688 4 36 0 + 689 4 36 1 + 690 4 37 0 + 691 4 37 1 + 692 5 36 0 + 693 5 36 1 + 694 5 37 0 + 695 5 37 1 + 696 7 37 1 + 697 7 37 0 + 698 7 36 1 + 699 7 36 0 + 700 6 37 1 + 701 6 37 0 + 702 6 36 1 + 703 6 36 0 + 704 12 37 0 + 705 12 37 1 + 706 12 36 0 + 707 12 36 1 + 708 13 37 0 + 709 13 37 1 + 710 13 36 0 + 711 13 36 1 + 712 15 36 1 + 713 15 36 0 + 714 15 37 1 + 715 15 37 0 + 716 14 36 1 + 717 14 36 0 + 718 14 37 1 + 719 14 37 0 + 768 11 39 0 + 769 11 39 1 + 770 11 38 0 + 771 11 38 1 + 772 10 39 0 + 773 10 39 1 + 774 10 38 0 + 775 10 38 1 + 776 8 38 1 + 777 8 38 0 + 778 8 39 1 + 779 8 39 0 + 780 9 38 1 + 781 9 38 0 + 782 9 39 1 + 783 9 39 0 + 800 3 38 0 + 801 3 38 1 + 802 3 39 0 + 803 3 39 1 + 804 2 38 0 + 805 2 38 1 + 806 2 39 0 + 807 2 39 1 + 808 0 39 1 + 809 0 39 0 + 810 0 38 1 + 811 0 38 0 + 812 1 39 1 + 813 1 39 0 + 814 1 38 1 + 815 1 38 0 + 816 4 38 0 + 817 4 38 1 + 818 4 39 0 + 819 4 39 1 + 820 5 38 0 + 821 5 38 1 + 822 5 39 0 + 823 5 39 1 + 824 7 39 1 + 825 7 39 0 + 826 7 38 1 + 827 7 38 0 + 828 6 39 1 + 829 6 39 0 + 830 6 38 1 + 831 6 38 0 + 832 12 39 0 + 833 12 39 1 + 834 12 38 0 + 835 12 38 1 + 836 13 39 0 + 837 13 39 1 + 838 13 38 0 + 839 13 38 1 + 840 15 38 1 + 841 15 38 0 + 842 15 39 1 + 843 15 39 0 + 844 14 38 1 + 845 14 38 0 + 846 14 39 1 + 847 14 39 0 + 896 11 41 0 + 897 11 41 1 + 898 11 40 0 + 899 11 40 1 + 900 10 41 0 + 901 10 41 1 + 902 10 40 0 + 903 10 40 1 + 904 8 40 1 + 905 8 40 0 + 906 8 41 1 + 907 8 41 0 + 908 9 40 1 + 909 9 40 0 + 910 9 41 1 + 911 9 41 0 + 928 3 40 0 + 929 3 40 1 + 930 3 41 0 + 931 3 41 1 + 932 2 40 0 + 933 2 40 1 + 934 2 41 0 + 935 2 41 1 + 936 0 41 1 + 937 0 41 0 + 938 0 40 1 + 939 0 40 0 + 940 1 41 1 + 941 1 41 0 + 942 1 40 1 + 943 1 40 0 + 944 4 40 0 + 945 4 40 1 + 946 4 41 0 + 947 4 41 1 + 948 5 40 0 + 949 5 40 1 + 950 5 41 0 + 951 5 41 1 + 952 7 41 1 + 953 7 41 0 + 954 7 40 1 + 955 7 40 0 + 956 6 41 1 + 957 6 41 0 + 958 6 40 1 + 959 6 40 0 + 960 12 41 0 + 961 12 41 1 + 962 12 40 0 + 963 12 40 1 + 964 13 41 0 + 965 13 41 1 + 966 13 40 0 + 967 13 40 1 + 968 15 40 1 + 969 15 40 0 + 970 15 41 1 + 971 15 41 0 + 972 14 40 1 + 973 14 40 0 + 974 14 41 1 + 975 14 41 0 +1024 11 43 0 +1025 11 43 1 +1026 11 42 0 +1027 11 42 1 +1028 10 43 0 +1029 10 43 1 +1030 10 42 0 +1031 10 42 1 +1032 8 42 1 +1033 8 42 0 +1034 8 43 1 +1035 8 43 0 +1036 9 42 1 +1037 9 42 0 +1038 9 43 1 +1039 9 43 0 +1056 3 42 0 +1057 3 42 1 +1058 3 43 0 +1059 3 43 1 +1060 2 42 0 +1061 2 42 1 +1062 2 43 0 +1063 2 43 1 +1064 0 43 1 +1065 0 43 0 +1066 0 42 1 +1067 0 42 0 +1068 1 43 1 +1069 1 43 0 +1070 1 42 1 +1071 1 42 0 +1072 4 42 0 +1073 4 42 1 +1074 4 43 0 +1075 4 43 1 +1076 5 42 0 +1077 5 42 1 +1078 5 43 0 +1079 5 43 1 +1080 7 43 1 +1081 7 43 0 +1082 7 42 1 +1083 7 42 0 +1084 6 43 1 +1085 6 43 0 +1086 6 42 1 +1087 6 42 0 +1088 12 43 0 +1089 12 43 1 +1090 12 42 0 +1091 12 42 1 +1092 13 43 0 +1093 13 43 1 +1094 13 42 0 +1095 13 42 1 +1096 15 42 1 +1097 15 42 0 +1098 15 43 1 +1099 15 43 0 +1100 14 42 1 +1101 14 42 0 +1102 14 43 1 +1103 14 43 0 +1152 11 45 0 +1153 11 45 1 +1154 11 44 0 +1155 11 44 1 +1156 10 45 0 +1157 10 45 1 +1158 10 44 0 +1159 10 44 1 +1160 8 44 1 +1161 8 44 0 +1162 8 45 1 +1163 8 45 0 +1164 9 44 1 +1165 9 44 0 +1166 9 45 1 +1167 9 45 0 +1184 3 44 0 +1185 3 44 1 +1186 3 45 0 +1187 3 45 1 +1188 2 44 0 +1189 2 44 1 +1190 2 45 0 +1191 2 45 1 +1192 0 45 1 +1193 0 45 0 +1194 0 44 1 +1195 0 44 0 +1196 1 45 1 +1197 1 45 0 +1198 1 44 1 +1199 1 44 0 +1200 4 44 0 +1201 4 44 1 +1202 4 45 0 +1203 4 45 1 +1204 5 44 0 +1205 5 44 1 +1206 5 45 0 +1207 5 45 1 +1208 7 45 1 +1209 7 45 0 +1210 7 44 1 +1211 7 44 0 +1212 6 45 1 +1213 6 45 0 +1214 6 44 1 +1215 6 44 0 +1216 12 45 0 +1217 12 45 1 +1218 12 44 0 +1219 12 44 1 +1220 13 45 0 +1221 13 45 1 +1222 13 44 0 +1223 13 44 1 +1224 15 44 1 +1225 15 44 0 +1226 15 45 1 +1227 15 45 0 +1228 14 44 1 +1229 14 44 0 +1230 14 45 1 +1231 14 45 0 +1280 11 47 0 +1281 11 47 1 +1282 11 46 0 +1283 11 46 1 +1284 10 47 0 +1285 10 47 1 +1286 10 46 0 +1287 10 46 1 +1288 8 46 1 +1289 8 46 0 +1290 8 47 1 +1291 8 47 0 +1292 9 46 1 +1293 9 46 0 +1294 9 47 1 +1295 9 47 0 +1312 3 46 0 +1313 3 46 1 +1314 3 47 0 +1315 3 47 1 +1316 2 46 0 +1317 2 46 1 +1318 2 47 0 +1319 2 47 1 +1320 0 47 1 +1321 0 47 0 +1322 0 46 1 +1323 0 46 0 +1324 1 47 1 +1325 1 47 0 +1326 1 46 1 +1327 1 46 0 +1328 4 46 0 +1329 4 46 1 +1330 4 47 0 +1331 4 47 1 +1332 5 46 0 +1333 5 46 1 +1334 5 47 0 +1335 5 47 1 +1336 7 47 1 +1337 7 47 0 +1338 7 46 1 +1339 7 46 0 +1340 6 47 1 +1341 6 47 0 +1342 6 46 1 +1343 6 46 0 +1344 12 47 0 +1345 12 47 1 +1346 12 46 0 +1347 12 46 1 +1348 13 47 0 +1349 13 47 1 +1350 13 46 0 +1351 13 46 1 +1352 15 46 1 +1353 15 46 0 +1354 15 47 1 +1355 15 47 0 +1356 14 46 1 +1357 14 46 0 +1358 14 47 1 +1359 14 47 0 +1408 11 49 0 +1409 11 49 1 +1410 11 48 0 +1411 11 48 1 +1412 10 49 0 +1413 10 49 1 +1414 10 48 0 +1415 10 48 1 +1416 8 48 1 +1417 8 48 0 +1418 8 49 1 +1419 8 49 0 +1420 9 48 1 +1421 9 48 0 +1422 9 49 1 +1423 9 49 0 +1440 3 48 0 +1441 3 48 1 +1442 3 49 0 +1443 3 49 1 +1444 2 48 0 +1445 2 48 1 +1446 2 49 0 +1447 2 49 1 +1448 0 49 1 +1449 0 49 0 +1450 0 48 1 +1451 0 48 0 +1452 1 49 1 +1453 1 49 0 +1454 1 48 1 +1455 1 48 0 +1456 4 48 0 +1457 4 48 1 +1458 4 49 0 +1459 4 49 1 +1460 5 48 0 +1461 5 48 1 +1462 5 49 0 +1463 5 49 1 +1464 7 49 1 +1465 7 49 0 +1466 7 48 1 +1467 7 48 0 +1468 6 49 1 +1469 6 49 0 +1470 6 48 1 +1471 6 48 0 +1472 12 49 0 +1473 12 49 1 +1474 12 48 0 +1475 12 48 1 +1476 13 49 0 +1477 13 49 1 +1478 13 48 0 +1479 13 48 1 +1480 15 48 1 +1481 15 48 0 +1482 15 49 1 +1483 15 49 0 +1484 14 48 1 +1485 14 48 0 +1486 14 49 1 +1487 14 49 0 +1536 11 51 0 +1537 11 51 1 +1538 11 50 0 +1539 11 50 1 +1540 10 51 0 +1541 10 51 1 +1542 10 50 0 +1543 10 50 1 +1544 8 50 1 +1545 8 50 0 +1546 8 51 1 +1547 8 51 0 +1548 9 50 1 +1549 9 50 0 +1550 9 51 1 +1551 9 51 0 +1568 3 50 0 +1569 3 50 1 +1570 3 51 0 +1571 3 51 1 +1572 2 50 0 +1573 2 50 1 +1574 2 51 0 +1575 2 51 1 +1576 0 51 1 +1577 0 51 0 +1578 0 50 1 +1579 0 50 0 +1580 1 51 1 +1581 1 51 0 +1582 1 50 1 +1583 1 50 0 +1584 4 50 0 +1585 4 50 1 +1586 4 51 0 +1587 4 51 1 +1588 5 50 0 +1589 5 50 1 +1590 5 51 0 +1591 5 51 1 +1592 7 51 1 +1593 7 51 0 +1594 7 50 1 +1595 7 50 0 +1596 6 51 1 +1597 6 51 0 +1598 6 50 1 +1599 6 50 0 +1600 12 51 0 +1601 12 51 1 +1602 12 50 0 +1603 12 50 1 +1604 13 51 0 +1605 13 51 1 +1606 13 50 0 +1607 13 50 1 +1608 15 50 1 +1609 15 50 0 +1610 15 51 1 +1611 15 51 0 +1612 14 50 1 +1613 14 50 0 +1614 14 51 1 +1615 14 51 0 +1664 11 53 0 +1665 11 53 1 +1666 11 52 0 +1667 11 52 1 +1668 10 53 0 +1669 10 53 1 +1670 10 52 0 +1671 10 52 1 +1672 8 52 1 +1673 8 52 0 +1674 8 53 1 +1675 8 53 0 +1676 9 52 1 +1677 9 52 0 +1678 9 53 1 +1679 9 53 0 +1696 3 52 0 +1697 3 52 1 +1698 3 53 0 +1699 3 53 1 +1700 2 52 0 +1701 2 52 1 +1702 2 53 0 +1703 2 53 1 +1704 0 53 1 +1705 0 53 0 +1706 0 52 1 +1707 0 52 0 +1708 1 53 1 +1709 1 53 0 +1710 1 52 1 +1711 1 52 0 +1712 4 52 0 +1713 4 52 1 +1714 4 53 0 +1715 4 53 1 +1716 5 52 0 +1717 5 52 1 +1718 5 53 0 +1719 5 53 1 +1720 7 53 1 +1721 7 53 0 +1722 7 52 1 +1723 7 52 0 +1724 6 53 1 +1725 6 53 0 +1726 6 52 1 +1727 6 52 0 +1728 12 53 0 +1729 12 53 1 +1730 12 52 0 +1731 12 52 1 +1732 13 53 0 +1733 13 53 1 +1734 13 52 0 +1735 13 52 1 +1736 15 52 1 +1737 15 52 0 +1738 15 53 1 +1739 15 53 0 +1740 14 52 1 +1741 14 52 0 +1742 14 53 1 +1743 14 53 0 +1792 11 55 0 +1793 11 55 1 +1794 11 54 0 +1795 11 54 1 +1796 10 55 0 +1797 10 55 1 +1798 10 54 0 +1799 10 54 1 +1800 8 54 1 +1801 8 54 0 +1802 8 55 1 +1803 8 55 0 +1804 9 54 1 +1805 9 54 0 +1806 9 55 1 +1807 9 55 0 +1824 3 54 0 +1825 3 54 1 +1826 3 55 0 +1827 3 55 1 +1828 2 54 0 +1829 2 54 1 +1830 2 55 0 +1831 2 55 1 +1832 0 55 1 +1833 0 55 0 +1834 0 54 1 +1835 0 54 0 +1836 1 55 1 +1837 1 55 0 +1838 1 54 1 +1839 1 54 0 +1840 4 54 0 +1841 4 54 1 +1842 4 55 0 +1843 4 55 1 +1844 5 54 0 +1845 5 54 1 +1846 5 55 0 +1847 5 55 1 +1848 7 55 1 +1849 7 55 0 +1850 7 54 1 +1851 7 54 0 +1852 6 55 1 +1853 6 55 0 +1854 6 54 1 +1855 6 54 0 +1856 12 55 0 +1857 12 55 1 +1858 12 54 0 +1859 12 54 1 +1860 13 55 0 +1861 13 55 1 +1862 13 54 0 +1863 13 54 1 +1864 15 54 1 +1865 15 54 0 +1866 15 55 1 +1867 15 55 0 +1868 14 54 1 +1869 14 54 0 +1870 14 55 1 +1871 14 55 0 +2176 11 27 0 +2177 11 27 1 +2178 11 26 0 +2179 11 26 1 +2180 10 27 0 +2181 10 27 1 +2182 10 26 0 +2183 10 26 1 +2184 8 26 1 +2185 8 26 0 +2186 8 27 1 +2187 8 27 0 +2188 9 26 1 +2189 9 26 0 +2190 9 27 1 +2191 9 27 0 +2208 3 26 0 +2209 3 26 1 +2210 3 27 0 +2211 3 27 1 +2212 2 26 0 +2213 2 26 1 +2214 2 27 0 +2215 2 27 1 +2216 0 27 1 +2217 0 27 0 +2218 0 26 1 +2219 0 26 0 +2220 1 27 1 +2221 1 27 0 +2222 1 26 1 +2223 1 26 0 +2224 4 26 0 +2225 4 26 1 +2226 4 27 0 +2227 4 27 1 +2228 5 26 0 +2229 5 26 1 +2230 5 27 0 +2231 5 27 1 +2232 7 27 1 +2233 7 27 0 +2234 7 26 1 +2235 7 26 0 +2236 6 27 1 +2237 6 27 0 +2238 6 26 1 +2239 6 26 0 +2240 12 27 0 +2241 12 27 1 +2242 12 26 0 +2243 12 26 1 +2244 13 27 0 +2245 13 27 1 +2246 13 26 0 +2247 13 26 1 +2248 15 26 1 +2249 15 26 0 +2250 15 27 1 +2251 15 27 0 +2252 14 26 1 +2253 14 26 0 +2254 14 27 1 +2255 14 27 0 +2304 11 25 0 +2305 11 25 1 +2306 11 24 0 +2307 11 24 1 +2308 10 25 0 +2309 10 25 1 +2310 10 24 0 +2311 10 24 1 +2312 8 24 1 +2313 8 24 0 +2314 8 25 1 +2315 8 25 0 +2316 9 24 1 +2317 9 24 0 +2318 9 25 1 +2319 9 25 0 +2336 3 24 0 +2337 3 24 1 +2338 3 25 0 +2339 3 25 1 +2340 2 24 0 +2341 2 24 1 +2342 2 25 0 +2343 2 25 1 +2344 0 25 1 +2345 0 25 0 +2346 0 24 1 +2347 0 24 0 +2348 1 25 1 +2349 1 25 0 +2350 1 24 1 +2351 1 24 0 +2352 4 24 0 +2353 4 24 1 +2354 4 25 0 +2355 4 25 1 +2356 5 24 0 +2357 5 24 1 +2358 5 25 0 +2359 5 25 1 +2360 7 25 1 +2361 7 25 0 +2362 7 24 1 +2363 7 24 0 +2364 6 25 1 +2365 6 25 0 +2366 6 24 1 +2367 6 24 0 +2368 12 25 0 +2369 12 25 1 +2370 12 24 0 +2371 12 24 1 +2372 13 25 0 +2373 13 25 1 +2374 13 24 0 +2375 13 24 1 +2376 15 24 1 +2377 15 24 0 +2378 15 25 1 +2379 15 25 0 +2380 14 24 1 +2381 14 24 0 +2382 14 25 1 +2383 14 25 0 +2432 11 23 0 +2433 11 23 1 +2434 11 22 0 +2435 11 22 1 +2436 10 23 0 +2437 10 23 1 +2438 10 22 0 +2439 10 22 1 +2440 8 22 1 +2441 8 22 0 +2442 8 23 1 +2443 8 23 0 +2444 9 22 1 +2445 9 22 0 +2446 9 23 1 +2447 9 23 0 +2464 3 22 0 +2465 3 22 1 +2466 3 23 0 +2467 3 23 1 +2468 2 22 0 +2469 2 22 1 +2470 2 23 0 +2471 2 23 1 +2472 0 23 1 +2473 0 23 0 +2474 0 22 1 +2475 0 22 0 +2476 1 23 1 +2477 1 23 0 +2478 1 22 1 +2479 1 22 0 +2480 4 22 0 +2481 4 22 1 +2482 4 23 0 +2483 4 23 1 +2484 5 22 0 +2485 5 22 1 +2486 5 23 0 +2487 5 23 1 +2488 7 23 1 +2489 7 23 0 +2490 7 22 1 +2491 7 22 0 +2492 6 23 1 +2493 6 23 0 +2494 6 22 1 +2495 6 22 0 +2496 12 23 0 +2497 12 23 1 +2498 12 22 0 +2499 12 22 1 +2500 13 23 0 +2501 13 23 1 +2502 13 22 0 +2503 13 22 1 +2504 15 22 1 +2505 15 22 0 +2506 15 23 1 +2507 15 23 0 +2508 14 22 1 +2509 14 22 0 +2510 14 23 1 +2511 14 23 0 +2560 11 21 0 +2561 11 21 1 +2562 11 20 0 +2563 11 20 1 +2564 10 21 0 +2565 10 21 1 +2566 10 20 0 +2567 10 20 1 +2568 8 20 1 +2569 8 20 0 +2570 8 21 1 +2571 8 21 0 +2572 9 20 1 +2573 9 20 0 +2574 9 21 1 +2575 9 21 0 +2592 3 20 0 +2593 3 20 1 +2594 3 21 0 +2595 3 21 1 +2596 2 20 0 +2597 2 20 1 +2598 2 21 0 +2599 2 21 1 +2600 0 21 1 +2601 0 21 0 +2602 0 20 1 +2603 0 20 0 +2604 1 21 1 +2605 1 21 0 +2606 1 20 1 +2607 1 20 0 +2608 4 20 0 +2609 4 20 1 +2610 4 21 0 +2611 4 21 1 +2612 5 20 0 +2613 5 20 1 +2614 5 21 0 +2615 5 21 1 +2616 7 21 1 +2617 7 21 0 +2618 7 20 1 +2619 7 20 0 +2620 6 21 1 +2621 6 21 0 +2622 6 20 1 +2623 6 20 0 +2624 12 21 0 +2625 12 21 1 +2626 12 20 0 +2627 12 20 1 +2628 13 21 0 +2629 13 21 1 +2630 13 20 0 +2631 13 20 1 +2632 15 20 1 +2633 15 20 0 +2634 15 21 1 +2635 15 21 0 +2636 14 20 1 +2637 14 20 0 +2638 14 21 1 +2639 14 21 0 +2688 11 19 0 +2689 11 19 1 +2690 11 18 0 +2691 11 18 1 +2692 10 19 0 +2693 10 19 1 +2694 10 18 0 +2695 10 18 1 +2696 8 18 1 +2697 8 18 0 +2698 8 19 1 +2699 8 19 0 +2700 9 18 1 +2701 9 18 0 +2702 9 19 1 +2703 9 19 0 +2720 3 18 0 +2721 3 18 1 +2722 3 19 0 +2723 3 19 1 +2724 2 18 0 +2725 2 18 1 +2726 2 19 0 +2727 2 19 1 +2728 0 19 1 +2729 0 19 0 +2730 0 18 1 +2731 0 18 0 +2732 1 19 1 +2733 1 19 0 +2734 1 18 1 +2735 1 18 0 +2736 4 18 0 +2737 4 18 1 +2738 4 19 0 +2739 4 19 1 +2740 5 18 0 +2741 5 18 1 +2742 5 19 0 +2743 5 19 1 +2744 7 19 1 +2745 7 19 0 +2746 7 18 1 +2747 7 18 0 +2748 6 19 1 +2749 6 19 0 +2750 6 18 1 +2751 6 18 0 +2752 12 19 0 +2753 12 19 1 +2754 12 18 0 +2755 12 18 1 +2756 13 19 0 +2757 13 19 1 +2758 13 18 0 +2759 13 18 1 +2760 15 18 1 +2761 15 18 0 +2762 15 19 1 +2763 15 19 0 +2764 14 18 1 +2765 14 18 0 +2766 14 19 1 +2767 14 19 0 +2816 11 17 0 +2817 11 17 1 +2818 11 16 0 +2819 11 16 1 +2820 10 17 0 +2821 10 17 1 +2822 10 16 0 +2823 10 16 1 +2824 8 16 1 +2825 8 16 0 +2826 8 17 1 +2827 8 17 0 +2828 9 16 1 +2829 9 16 0 +2830 9 17 1 +2831 9 17 0 +2848 3 16 0 +2849 3 16 1 +2850 3 17 0 +2851 3 17 1 +2852 2 16 0 +2853 2 16 1 +2854 2 17 0 +2855 2 17 1 +2856 0 17 1 +2857 0 17 0 +2858 0 16 1 +2859 0 16 0 +2860 1 17 1 +2861 1 17 0 +2862 1 16 1 +2863 1 16 0 +2864 4 16 0 +2865 4 16 1 +2866 4 17 0 +2867 4 17 1 +2868 5 16 0 +2869 5 16 1 +2870 5 17 0 +2871 5 17 1 +2872 7 17 1 +2873 7 17 0 +2874 7 16 1 +2875 7 16 0 +2876 6 17 1 +2877 6 17 0 +2878 6 16 1 +2879 6 16 0 +2880 12 17 0 +2881 12 17 1 +2882 12 16 0 +2883 12 16 1 +2884 13 17 0 +2885 13 17 1 +2886 13 16 0 +2887 13 16 1 +2888 15 16 1 +2889 15 16 0 +2890 15 17 1 +2891 15 17 0 +2892 14 16 1 +2893 14 16 0 +2894 14 17 1 +2895 14 17 0 +2944 11 15 0 +2945 11 15 1 +2946 11 14 0 +2947 11 14 1 +2948 10 15 0 +2949 10 15 1 +2950 10 14 0 +2951 10 14 1 +2952 8 14 1 +2953 8 14 0 +2954 8 15 1 +2955 8 15 0 +2956 9 14 1 +2957 9 14 0 +2958 9 15 1 +2959 9 15 0 +2976 3 14 0 +2977 3 14 1 +2978 3 15 0 +2979 3 15 1 +2980 2 14 0 +2981 2 14 1 +2982 2 15 0 +2983 2 15 1 +2984 0 15 1 +2985 0 15 0 +2986 0 14 1 +2987 0 14 0 +2988 1 15 1 +2989 1 15 0 +2990 1 14 1 +2991 1 14 0 +2992 4 14 0 +2993 4 14 1 +2994 4 15 0 +2995 4 15 1 +2996 5 14 0 +2997 5 14 1 +2998 5 15 0 +2999 5 15 1 +3000 7 15 1 +3001 7 15 0 +3002 7 14 1 +3003 7 14 0 +3004 6 15 1 +3005 6 15 0 +3006 6 14 1 +3007 6 14 0 +3008 12 15 0 +3009 12 15 1 +3010 12 14 0 +3011 12 14 1 +3012 13 15 0 +3013 13 15 1 +3014 13 14 0 +3015 13 14 1 +3016 15 14 1 +3017 15 14 0 +3018 15 15 1 +3019 15 15 0 +3020 14 14 1 +3021 14 14 0 +3022 14 15 1 +3023 14 15 0 +3072 11 13 0 +3073 11 13 1 +3074 11 12 0 +3075 11 12 1 +3076 10 13 0 +3077 10 13 1 +3078 10 12 0 +3079 10 12 1 +3080 8 12 1 +3081 8 12 0 +3082 8 13 1 +3083 8 13 0 +3084 9 12 1 +3085 9 12 0 +3086 9 13 1 +3087 9 13 0 +3104 3 12 0 +3105 3 12 1 +3106 3 13 0 +3107 3 13 1 +3108 2 12 0 +3109 2 12 1 +3110 2 13 0 +3111 2 13 1 +3112 0 13 1 +3113 0 13 0 +3114 0 12 1 +3115 0 12 0 +3116 1 13 1 +3117 1 13 0 +3118 1 12 1 +3119 1 12 0 +3120 4 12 0 +3121 4 12 1 +3122 4 13 0 +3123 4 13 1 +3124 5 12 0 +3125 5 12 1 +3126 5 13 0 +3127 5 13 1 +3128 7 13 1 +3129 7 13 0 +3130 7 12 1 +3131 7 12 0 +3132 6 13 1 +3133 6 13 0 +3134 6 12 1 +3135 6 12 0 +3136 12 13 0 +3137 12 13 1 +3138 12 12 0 +3139 12 12 1 +3140 13 13 0 +3141 13 13 1 +3142 13 12 0 +3143 13 12 1 +3144 15 12 1 +3145 15 12 0 +3146 15 13 1 +3147 15 13 0 +3148 14 12 1 +3149 14 12 0 +3150 14 13 1 +3151 14 13 0 +3200 11 11 0 +3201 11 11 1 +3202 11 10 0 +3203 11 10 1 +3204 10 11 0 +3205 10 11 1 +3206 10 10 0 +3207 10 10 1 +3208 8 10 1 +3209 8 10 0 +3210 8 11 1 +3211 8 11 0 +3212 9 10 1 +3213 9 10 0 +3214 9 11 1 +3215 9 11 0 +3232 3 10 0 +3233 3 10 1 +3234 3 11 0 +3235 3 11 1 +3236 2 10 0 +3237 2 10 1 +3238 2 11 0 +3239 2 11 1 +3240 0 11 1 +3241 0 11 0 +3242 0 10 1 +3243 0 10 0 +3244 1 11 1 +3245 1 11 0 +3246 1 10 1 +3247 1 10 0 +3248 4 10 0 +3249 4 10 1 +3250 4 11 0 +3251 4 11 1 +3252 5 10 0 +3253 5 10 1 +3254 5 11 0 +3255 5 11 1 +3256 7 11 1 +3257 7 11 0 +3258 7 10 1 +3259 7 10 0 +3260 6 11 1 +3261 6 11 0 +3262 6 10 1 +3263 6 10 0 +3264 12 11 0 +3265 12 11 1 +3266 12 10 0 +3267 12 10 1 +3268 13 11 0 +3269 13 11 1 +3270 13 10 0 +3271 13 10 1 +3272 15 10 1 +3273 15 10 0 +3274 15 11 1 +3275 15 11 0 +3276 14 10 1 +3277 14 10 0 +3278 14 11 1 +3279 14 11 0 +3328 11 9 0 +3329 11 9 1 +3330 11 8 0 +3331 11 8 1 +3332 10 9 0 +3333 10 9 1 +3334 10 8 0 +3335 10 8 1 +3336 8 8 1 +3337 8 8 0 +3338 8 9 1 +3339 8 9 0 +3340 9 8 1 +3341 9 8 0 +3342 9 9 1 +3343 9 9 0 +3360 3 8 0 +3361 3 8 1 +3362 3 9 0 +3363 3 9 1 +3364 2 8 0 +3365 2 8 1 +3366 2 9 0 +3367 2 9 1 +3368 0 9 1 +3369 0 9 0 +3370 0 8 1 +3371 0 8 0 +3372 1 9 1 +3373 1 9 0 +3374 1 8 1 +3375 1 8 0 +3376 4 8 0 +3377 4 8 1 +3378 4 9 0 +3379 4 9 1 +3380 5 8 0 +3381 5 8 1 +3382 5 9 0 +3383 5 9 1 +3384 7 9 1 +3385 7 9 0 +3386 7 8 1 +3387 7 8 0 +3388 6 9 1 +3389 6 9 0 +3390 6 8 1 +3391 6 8 0 +3392 12 9 0 +3393 12 9 1 +3394 12 8 0 +3395 12 8 1 +3396 13 9 0 +3397 13 9 1 +3398 13 8 0 +3399 13 8 1 +3400 15 8 1 +3401 15 8 0 +3402 15 9 1 +3403 15 9 0 +3404 14 8 1 +3405 14 8 0 +3406 14 9 1 +3407 14 9 0 +3456 11 7 0 +3457 11 7 1 +3458 11 6 0 +3459 11 6 1 +3460 10 7 0 +3461 10 7 1 +3462 10 6 0 +3463 10 6 1 +3464 8 6 1 +3465 8 6 0 +3466 8 7 1 +3467 8 7 0 +3468 9 6 1 +3469 9 6 0 +3470 9 7 1 +3471 9 7 0 +3488 3 6 0 +3489 3 6 1 +3490 3 7 0 +3491 3 7 1 +3492 2 6 0 +3493 2 6 1 +3494 2 7 0 +3495 2 7 1 +3496 0 7 1 +3497 0 7 0 +3498 0 6 1 +3499 0 6 0 +3500 1 7 1 +3501 1 7 0 +3502 1 6 1 +3503 1 6 0 +3504 4 6 0 +3505 4 6 1 +3506 4 7 0 +3507 4 7 1 +3508 5 6 0 +3509 5 6 1 +3510 5 7 0 +3511 5 7 1 +3512 7 7 1 +3513 7 7 0 +3514 7 6 1 +3515 7 6 0 +3516 6 7 1 +3517 6 7 0 +3518 6 6 1 +3519 6 6 0 +3520 12 7 0 +3521 12 7 1 +3522 12 6 0 +3523 12 6 1 +3524 13 7 0 +3525 13 7 1 +3526 13 6 0 +3527 13 6 1 +3528 15 6 1 +3529 15 6 0 +3530 15 7 1 +3531 15 7 0 +3532 14 6 1 +3533 14 6 0 +3534 14 7 1 +3535 14 7 0 +3584 11 5 0 +3585 11 5 1 +3586 11 4 0 +3587 11 4 1 +3588 10 5 0 +3589 10 5 1 +3590 10 4 0 +3591 10 4 1 +3592 8 4 1 +3593 8 4 0 +3594 8 5 1 +3595 8 5 0 +3596 9 4 1 +3597 9 4 0 +3598 9 5 1 +3599 9 5 0 +3616 3 4 0 +3617 3 4 1 +3618 3 5 0 +3619 3 5 1 +3620 2 4 0 +3621 2 4 1 +3622 2 5 0 +3623 2 5 1 +3624 0 5 1 +3625 0 5 0 +3626 0 4 1 +3627 0 4 0 +3628 1 5 1 +3629 1 5 0 +3630 1 4 1 +3631 1 4 0 +3632 4 4 0 +3633 4 4 1 +3634 4 5 0 +3635 4 5 1 +3636 5 4 0 +3637 5 4 1 +3638 5 5 0 +3639 5 5 1 +3640 7 5 1 +3641 7 5 0 +3642 7 4 1 +3643 7 4 0 +3644 6 5 1 +3645 6 5 0 +3646 6 4 1 +3647 6 4 0 +3648 12 5 0 +3649 12 5 1 +3650 12 4 0 +3651 12 4 1 +3652 13 5 0 +3653 13 5 1 +3654 13 4 0 +3655 13 4 1 +3656 15 4 1 +3657 15 4 0 +3658 15 5 1 +3659 15 5 0 +3660 14 4 1 +3661 14 4 0 +3662 14 5 1 +3663 14 5 0 +3712 11 3 0 +3713 11 3 1 +3714 11 2 0 +3715 11 2 1 +3716 10 3 0 +3717 10 3 1 +3718 10 2 0 +3719 10 2 1 +3720 8 2 1 +3721 8 2 0 +3722 8 3 1 +3723 8 3 0 +3724 9 2 1 +3725 9 2 0 +3726 9 3 1 +3727 9 3 0 +3744 3 2 0 +3745 3 2 1 +3746 3 3 0 +3747 3 3 1 +3748 2 2 0 +3749 2 2 1 +3750 2 3 0 +3751 2 3 1 +3752 0 3 1 +3753 0 3 0 +3754 0 2 1 +3755 0 2 0 +3756 1 3 1 +3757 1 3 0 +3758 1 2 1 +3759 1 2 0 +3760 4 2 0 +3761 4 2 1 +3762 4 3 0 +3763 4 3 1 +3764 5 2 0 +3765 5 2 1 +3766 5 3 0 +3767 5 3 1 +3768 7 3 1 +3769 7 3 0 +3770 7 2 1 +3771 7 2 0 +3772 6 3 1 +3773 6 3 0 +3774 6 2 1 +3775 6 2 0 +3776 12 3 0 +3777 12 3 1 +3778 12 2 0 +3779 12 2 1 +3780 13 3 0 +3781 13 3 1 +3782 13 2 0 +3783 13 2 1 +3784 15 2 1 +3785 15 2 0 +3786 15 3 1 +3787 15 3 0 +3788 14 2 1 +3789 14 2 0 +3790 14 3 1 +3791 14 3 0 +3840 11 1 0 +3841 11 1 1 +3842 11 0 0 +3843 11 0 1 +3844 10 1 0 +3845 10 1 1 +3846 10 0 0 +3847 10 0 1 +3848 8 0 1 +3849 8 0 0 +3850 8 1 1 +3851 8 1 0 +3852 9 0 1 +3853 9 0 0 +3854 9 1 1 +3855 9 1 0 +3872 3 0 0 +3873 3 0 1 +3874 3 1 0 +3875 3 1 1 +3876 2 0 0 +3877 2 0 1 +3878 2 1 0 +3879 2 1 1 +3880 0 1 1 +3881 0 1 0 +3882 0 0 1 +3883 0 0 0 +3884 1 1 1 +3885 1 1 0 +3886 1 0 1 +3887 1 0 0 +3888 4 0 0 +3889 4 0 1 +3890 4 1 0 +3891 4 1 1 +3892 5 0 0 +3893 5 0 1 +3894 5 1 0 +3895 5 1 1 +3896 7 1 1 +3897 7 1 0 +3898 7 0 1 +3899 7 0 0 +3900 6 1 1 +3901 6 1 0 +3902 6 0 1 +3903 6 0 0 +3904 12 1 0 +3905 12 1 1 +3906 12 0 0 +3907 12 0 1 +3908 13 1 0 +3909 13 1 1 +3910 13 0 0 +3911 13 0 1 +3912 15 0 1 +3913 15 0 0 +3914 15 1 1 +3915 15 1 0 +3916 14 0 1 +3917 14 0 0 +3918 14 1 1 +3919 14 1 0 diff --git a/Detectors/PHOS/base/files/Mod2RCU1.data b/Detectors/PHOS/base/files/Mod2RCU1.data new file mode 100644 index 0000000000000..db125226496cf --- /dev/null +++ b/Detectors/PHOS/base/files/Mod2RCU1.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 1 0 2 + 1 1 1 2 + 2 1 2 2 + 3 1 3 2 + 4 1 4 2 + 5 1 5 2 + 6 1 6 2 + 7 1 7 2 + 8 1 8 2 + 9 1 9 2 + 10 1 10 2 + 11 1 11 2 + 12 1 12 2 + 13 1 13 2 + 14 1 14 2 + 15 1 15 2 + 16 1 16 2 + 17 1 17 2 + 18 1 18 2 + 19 1 19 2 + 20 1 20 2 + 21 1 21 2 + 22 1 22 2 + 23 1 23 2 + 24 1 24 2 + 25 1 25 2 + 26 1 26 2 + 27 1 27 2 + 28 1 28 2 + 29 1 29 2 + 30 1 30 2 + 31 1 31 2 + 32 1 32 2 + 33 1 33 2 + 34 1 34 2 + 35 1 35 2 + 36 1 36 2 + 37 1 37 2 + 38 1 38 2 + 39 1 39 2 + 40 1 40 2 + 41 1 41 2 + 42 1 42 2 + 43 1 43 2 + 44 1 44 2 + 45 1 45 2 + 46 1 46 2 + 47 1 47 2 + 48 1 48 2 + 49 1 49 2 + 50 1 50 2 + 51 1 51 2 + 52 1 52 2 + 53 1 53 2 + 54 1 54 2 + 55 1 55 2 + 56 1 56 2 + 57 1 57 2 + 58 1 58 2 + 59 1 59 2 + 60 1 60 2 + 61 1 61 2 + 62 1 62 2 + 63 1 63 2 + 64 1 64 2 + 65 1 65 2 + 66 1 66 2 + 67 1 67 2 + 68 1 68 2 + 69 1 69 2 + 70 1 70 2 + 71 1 71 2 + 72 1 72 2 + 73 1 73 2 + 74 1 74 2 + 75 1 75 2 + 76 1 76 2 + 77 1 77 2 + 78 1 78 2 + 79 1 79 2 + 80 1 80 2 + 81 1 81 2 + 82 1 82 2 + 83 1 83 2 + 84 1 84 2 + 85 1 85 2 + 86 1 86 2 + 87 1 87 2 + 88 1 88 2 + 89 1 89 2 + 90 1 90 2 + 91 1 91 2 + 92 1 92 2 + 93 1 93 2 + 94 1 94 2 + 95 1 95 2 + 96 1 96 2 + 97 1 97 2 + 98 1 98 2 + 99 1 99 2 + 100 1 100 2 + 101 1 101 2 + 102 1 102 2 + 103 1 103 2 + 104 1 104 2 + 105 1 105 2 + 106 1 106 2 + 107 1 107 2 + 108 1 108 2 + 109 1 109 2 + 110 1 110 2 + 111 1 111 2 + 112 1 112 2 + 113 1 113 2 + 114 1 114 2 + 115 1 115 2 + 116 1 116 2 + 117 1 117 2 + 118 1 118 2 + 119 1 119 2 + 120 1 120 2 + 121 1 121 2 + 122 1 122 2 + 123 1 123 2 + 124 1 124 2 + 125 1 125 2 + 126 1 126 2 + 127 1 127 2 +2048 1 2048 2 +2049 1 2049 2 +2050 1 2050 2 +2051 1 2051 2 +2052 1 2052 2 +2053 1 2053 2 +2054 1 2054 2 +2055 1 2055 2 +2056 1 2056 2 +2057 1 2057 2 +2058 1 2058 2 +2059 1 2059 2 +2060 1 2060 2 +2061 1 2061 2 +2062 1 2062 2 +2063 1 2063 2 +2064 1 2064 2 +2065 1 2065 2 +2066 1 2066 2 +2067 1 2067 2 +2068 1 2068 2 +2069 1 2069 2 +2070 1 2070 2 +2071 1 2071 2 +2072 1 2072 2 +2073 1 2073 2 +2074 1 2074 2 +2075 1 2075 2 +2076 1 2076 2 +2077 1 2077 2 +2078 1 2078 2 +2079 1 2079 2 +2080 1 2080 2 +2081 1 2081 2 +2082 1 2082 2 +2083 1 2083 2 +2084 1 2084 2 +2085 1 2085 2 +2086 1 2086 2 +2087 1 2087 2 +2088 1 2088 2 +2089 1 2089 2 +2090 1 2090 2 +2091 1 2091 2 +2092 1 2092 2 +2093 1 2093 2 +2094 1 2094 2 +2095 1 2095 2 +2096 1 2096 2 +2097 1 2097 2 +2098 1 2098 2 +2099 1 2099 2 +2100 1 2100 2 +2101 1 2101 2 +2102 1 2102 2 +2103 1 2103 2 +2104 1 2104 2 +2105 1 2105 2 +2106 1 2106 2 +2107 1 2107 2 +2108 1 2108 2 +2109 1 2109 2 +2110 1 2110 2 +2111 1 2111 2 +2112 1 2112 2 +2113 1 2113 2 +2114 1 2114 2 +2115 1 2115 2 +2116 1 2116 2 +2117 1 2117 2 +2118 1 2118 2 +2119 1 2119 2 +2120 1 2120 2 +2121 1 2121 2 +2122 1 2122 2 +2123 1 2123 2 +2124 1 2124 2 +2125 1 2125 2 +2126 1 2126 2 +2127 1 2127 2 +2128 1 2128 2 +2129 1 2129 2 +2130 1 2130 2 +2131 1 2131 2 +2132 1 2132 2 +2133 1 2133 2 +2134 1 2134 2 +2135 1 2135 2 +2136 1 2136 2 +2137 1 2137 2 +2138 1 2138 2 +2139 1 2139 2 +2140 1 2140 2 +2141 1 2141 2 +2142 1 2142 2 +2143 1 2143 2 +2144 1 2144 2 +2145 1 2145 2 +2146 1 2146 2 +2147 1 2147 2 +2148 1 2148 2 +2149 1 2149 2 +2150 1 2150 2 +2151 1 2151 2 +2152 1 2152 2 +2153 1 2153 2 +2154 1 2154 2 +2155 1 2155 2 +2156 1 2156 2 +2157 1 2157 2 +2158 1 2158 2 +2159 1 2159 2 +2160 1 2160 2 +2161 1 2161 2 +2162 1 2162 2 +2163 1 2163 2 +2164 1 2164 2 +2165 1 2165 2 +2166 1 2166 2 +2167 1 2167 2 +2168 1 2168 2 +2169 1 2169 2 +2170 1 2170 2 +2171 1 2171 2 +2172 1 2172 2 +2173 1 2173 2 +2174 1 2174 2 +2175 1 2175 2 + 128 27 29 0 + 129 27 29 1 + 130 27 28 0 + 131 27 28 1 + 132 26 29 0 + 133 26 29 1 + 134 26 28 0 + 135 26 28 1 + 136 24 28 1 + 137 24 28 0 + 138 24 29 1 + 139 24 29 0 + 140 25 28 1 + 141 25 28 0 + 142 25 29 1 + 143 25 29 0 + 160 19 28 0 + 161 19 28 1 + 162 19 29 0 + 163 19 29 1 + 164 18 28 0 + 165 18 28 1 + 166 18 29 0 + 167 18 29 1 + 168 16 29 1 + 169 16 29 0 + 170 16 28 1 + 171 16 28 0 + 172 17 29 1 + 173 17 29 0 + 174 17 28 1 + 175 17 28 0 + 176 20 28 0 + 177 20 28 1 + 178 20 29 0 + 179 20 29 1 + 180 21 28 0 + 181 21 28 1 + 182 21 29 0 + 183 21 29 1 + 184 23 29 1 + 185 23 29 0 + 186 23 28 1 + 187 23 28 0 + 188 22 29 1 + 189 22 29 0 + 190 22 28 1 + 191 22 28 0 + 192 28 29 0 + 193 28 29 1 + 194 28 28 0 + 195 28 28 1 + 196 29 29 0 + 197 29 29 1 + 198 29 28 0 + 199 29 28 1 + 200 31 28 1 + 201 31 28 0 + 202 31 29 1 + 203 31 29 0 + 204 30 28 1 + 205 30 28 0 + 206 30 29 1 + 207 30 29 0 + 256 27 31 0 + 257 27 31 1 + 258 27 30 0 + 259 27 30 1 + 260 26 31 0 + 261 26 31 1 + 262 26 30 0 + 263 26 30 1 + 264 24 30 1 + 265 24 30 0 + 266 24 31 1 + 267 24 31 0 + 268 25 30 1 + 269 25 30 0 + 270 25 31 1 + 271 25 31 0 + 288 19 30 0 + 289 19 30 1 + 290 19 31 0 + 291 19 31 1 + 292 18 30 0 + 293 18 30 1 + 294 18 31 0 + 295 18 31 1 + 296 16 31 1 + 297 16 31 0 + 298 16 30 1 + 299 16 30 0 + 300 17 31 1 + 301 17 31 0 + 302 17 30 1 + 303 17 30 0 + 304 20 30 0 + 305 20 30 1 + 306 20 31 0 + 307 20 31 1 + 308 21 30 0 + 309 21 30 1 + 310 21 31 0 + 311 21 31 1 + 312 23 31 1 + 313 23 31 0 + 314 23 30 1 + 315 23 30 0 + 316 22 31 1 + 317 22 31 0 + 318 22 30 1 + 319 22 30 0 + 320 28 31 0 + 321 28 31 1 + 322 28 30 0 + 323 28 30 1 + 324 29 31 0 + 325 29 31 1 + 326 29 30 0 + 327 29 30 1 + 328 31 30 1 + 329 31 30 0 + 330 31 31 1 + 331 31 31 0 + 332 30 30 1 + 333 30 30 0 + 334 30 31 1 + 335 30 31 0 + 384 27 33 0 + 385 27 33 1 + 386 27 32 0 + 387 27 32 1 + 388 26 33 0 + 389 26 33 1 + 390 26 32 0 + 391 26 32 1 + 392 24 32 1 + 393 24 32 0 + 394 24 33 1 + 395 24 33 0 + 396 25 32 1 + 397 25 32 0 + 398 25 33 1 + 399 25 33 0 + 416 19 32 0 + 417 19 32 1 + 418 19 33 0 + 419 19 33 1 + 420 18 32 0 + 421 18 32 1 + 422 18 33 0 + 423 18 33 1 + 424 16 33 1 + 425 16 33 0 + 426 16 32 1 + 427 16 32 0 + 428 17 33 1 + 429 17 33 0 + 430 17 32 1 + 431 17 32 0 + 432 20 32 0 + 433 20 32 1 + 434 20 33 0 + 435 20 33 1 + 436 21 32 0 + 437 21 32 1 + 438 21 33 0 + 439 21 33 1 + 440 23 33 1 + 441 23 33 0 + 442 23 32 1 + 443 23 32 0 + 444 22 33 1 + 445 22 33 0 + 446 22 32 1 + 447 22 32 0 + 448 28 33 0 + 449 28 33 1 + 450 28 32 0 + 451 28 32 1 + 452 29 33 0 + 453 29 33 1 + 454 29 32 0 + 455 29 32 1 + 456 31 32 1 + 457 31 32 0 + 458 31 33 1 + 459 31 33 0 + 460 30 32 1 + 461 30 32 0 + 462 30 33 1 + 463 30 33 0 + 512 27 35 0 + 513 27 35 1 + 514 27 34 0 + 515 27 34 1 + 516 26 35 0 + 517 26 35 1 + 518 26 34 0 + 519 26 34 1 + 520 24 34 1 + 521 24 34 0 + 522 24 35 1 + 523 24 35 0 + 524 25 34 1 + 525 25 34 0 + 526 25 35 1 + 527 25 35 0 + 544 19 34 0 + 545 19 34 1 + 546 19 35 0 + 547 19 35 1 + 548 18 34 0 + 549 18 34 1 + 550 18 35 0 + 551 18 35 1 + 552 16 35 1 + 553 16 35 0 + 554 16 34 1 + 555 16 34 0 + 556 17 35 1 + 557 17 35 0 + 558 17 34 1 + 559 17 34 0 + 560 20 34 0 + 561 20 34 1 + 562 20 35 0 + 563 20 35 1 + 564 21 34 0 + 565 21 34 1 + 566 21 35 0 + 567 21 35 1 + 568 23 35 1 + 569 23 35 0 + 570 23 34 1 + 571 23 34 0 + 572 22 35 1 + 573 22 35 0 + 574 22 34 1 + 575 22 34 0 + 576 28 35 0 + 577 28 35 1 + 578 28 34 0 + 579 28 34 1 + 580 29 35 0 + 581 29 35 1 + 582 29 34 0 + 583 29 34 1 + 584 31 34 1 + 585 31 34 0 + 586 31 35 1 + 587 31 35 0 + 588 30 34 1 + 589 30 34 0 + 590 30 35 1 + 591 30 35 0 + 640 27 37 0 + 641 27 37 1 + 642 27 36 0 + 643 27 36 1 + 644 26 37 0 + 645 26 37 1 + 646 26 36 0 + 647 26 36 1 + 648 24 36 1 + 649 24 36 0 + 650 24 37 1 + 651 24 37 0 + 652 25 36 1 + 653 25 36 0 + 654 25 37 1 + 655 25 37 0 + 672 19 36 0 + 673 19 36 1 + 674 19 37 0 + 675 19 37 1 + 676 18 36 0 + 677 18 36 1 + 678 18 37 0 + 679 18 37 1 + 680 16 37 1 + 681 16 37 0 + 682 16 36 1 + 683 16 36 0 + 684 17 37 1 + 685 17 37 0 + 686 17 36 1 + 687 17 36 0 + 688 20 36 0 + 689 20 36 1 + 690 20 37 0 + 691 20 37 1 + 692 21 36 0 + 693 21 36 1 + 694 21 37 0 + 695 21 37 1 + 696 23 37 1 + 697 23 37 0 + 698 23 36 1 + 699 23 36 0 + 700 22 37 1 + 701 22 37 0 + 702 22 36 1 + 703 22 36 0 + 704 28 37 0 + 705 28 37 1 + 706 28 36 0 + 707 28 36 1 + 708 29 37 0 + 709 29 37 1 + 710 29 36 0 + 711 29 36 1 + 712 31 36 1 + 713 31 36 0 + 714 31 37 1 + 715 31 37 0 + 716 30 36 1 + 717 30 36 0 + 718 30 37 1 + 719 30 37 0 + 768 27 39 0 + 769 27 39 1 + 770 27 38 0 + 771 27 38 1 + 772 26 39 0 + 773 26 39 1 + 774 26 38 0 + 775 26 38 1 + 776 24 38 1 + 777 24 38 0 + 778 24 39 1 + 779 24 39 0 + 780 25 38 1 + 781 25 38 0 + 782 25 39 1 + 783 25 39 0 + 800 19 38 0 + 801 19 38 1 + 802 19 39 0 + 803 19 39 1 + 804 18 38 0 + 805 18 38 1 + 806 18 39 0 + 807 18 39 1 + 808 16 39 1 + 809 16 39 0 + 810 16 38 1 + 811 16 38 0 + 812 17 39 1 + 813 17 39 0 + 814 17 38 1 + 815 17 38 0 + 816 20 38 0 + 817 20 38 1 + 818 20 39 0 + 819 20 39 1 + 820 21 38 0 + 821 21 38 1 + 822 21 39 0 + 823 21 39 1 + 824 23 39 1 + 825 23 39 0 + 826 23 38 1 + 827 23 38 0 + 828 22 39 1 + 829 22 39 0 + 830 22 38 1 + 831 22 38 0 + 832 28 39 0 + 833 28 39 1 + 834 28 38 0 + 835 28 38 1 + 836 29 39 0 + 837 29 39 1 + 838 29 38 0 + 839 29 38 1 + 840 31 38 1 + 841 31 38 0 + 842 31 39 1 + 843 31 39 0 + 844 30 38 1 + 845 30 38 0 + 846 30 39 1 + 847 30 39 0 + 896 27 41 0 + 897 27 41 1 + 898 27 40 0 + 899 27 40 1 + 900 26 41 0 + 901 26 41 1 + 902 26 40 0 + 903 26 40 1 + 904 24 40 1 + 905 24 40 0 + 906 24 41 1 + 907 24 41 0 + 908 25 40 1 + 909 25 40 0 + 910 25 41 1 + 911 25 41 0 + 928 19 40 0 + 929 19 40 1 + 930 19 41 0 + 931 19 41 1 + 932 18 40 0 + 933 18 40 1 + 934 18 41 0 + 935 18 41 1 + 936 16 41 1 + 937 16 41 0 + 938 16 40 1 + 939 16 40 0 + 940 17 41 1 + 941 17 41 0 + 942 17 40 1 + 943 17 40 0 + 944 20 40 0 + 945 20 40 1 + 946 20 41 0 + 947 20 41 1 + 948 21 40 0 + 949 21 40 1 + 950 21 41 0 + 951 21 41 1 + 952 23 41 1 + 953 23 41 0 + 954 23 40 1 + 955 23 40 0 + 956 22 41 1 + 957 22 41 0 + 958 22 40 1 + 959 22 40 0 + 960 28 41 0 + 961 28 41 1 + 962 28 40 0 + 963 28 40 1 + 964 29 41 0 + 965 29 41 1 + 966 29 40 0 + 967 29 40 1 + 968 31 40 1 + 969 31 40 0 + 970 31 41 1 + 971 31 41 0 + 972 30 40 1 + 973 30 40 0 + 974 30 41 1 + 975 30 41 0 +1024 27 43 0 +1025 27 43 1 +1026 27 42 0 +1027 27 42 1 +1028 26 43 0 +1029 26 43 1 +1030 26 42 0 +1031 26 42 1 +1032 24 42 1 +1033 24 42 0 +1034 24 43 1 +1035 24 43 0 +1036 25 42 1 +1037 25 42 0 +1038 25 43 1 +1039 25 43 0 +1056 19 42 0 +1057 19 42 1 +1058 19 43 0 +1059 19 43 1 +1060 18 42 0 +1061 18 42 1 +1062 18 43 0 +1063 18 43 1 +1064 16 43 1 +1065 16 43 0 +1066 16 42 1 +1067 16 42 0 +1068 17 43 1 +1069 17 43 0 +1070 17 42 1 +1071 17 42 0 +1072 20 42 0 +1073 20 42 1 +1074 20 43 0 +1075 20 43 1 +1076 21 42 0 +1077 21 42 1 +1078 21 43 0 +1079 21 43 1 +1080 23 43 1 +1081 23 43 0 +1082 23 42 1 +1083 23 42 0 +1084 22 43 1 +1085 22 43 0 +1086 22 42 1 +1087 22 42 0 +1088 28 43 0 +1089 28 43 1 +1090 28 42 0 +1091 28 42 1 +1092 29 43 0 +1093 29 43 1 +1094 29 42 0 +1095 29 42 1 +1096 31 42 1 +1097 31 42 0 +1098 31 43 1 +1099 31 43 0 +1100 30 42 1 +1101 30 42 0 +1102 30 43 1 +1103 30 43 0 +1152 27 45 0 +1153 27 45 1 +1154 27 44 0 +1155 27 44 1 +1156 26 45 0 +1157 26 45 1 +1158 26 44 0 +1159 26 44 1 +1160 24 44 1 +1161 24 44 0 +1162 24 45 1 +1163 24 45 0 +1164 25 44 1 +1165 25 44 0 +1166 25 45 1 +1167 25 45 0 +1184 19 44 0 +1185 19 44 1 +1186 19 45 0 +1187 19 45 1 +1188 18 44 0 +1189 18 44 1 +1190 18 45 0 +1191 18 45 1 +1192 16 45 1 +1193 16 45 0 +1194 16 44 1 +1195 16 44 0 +1196 17 45 1 +1197 17 45 0 +1198 17 44 1 +1199 17 44 0 +1200 20 44 0 +1201 20 44 1 +1202 20 45 0 +1203 20 45 1 +1204 21 44 0 +1205 21 44 1 +1206 21 45 0 +1207 21 45 1 +1208 23 45 1 +1209 23 45 0 +1210 23 44 1 +1211 23 44 0 +1212 22 45 1 +1213 22 45 0 +1214 22 44 1 +1215 22 44 0 +1216 28 45 0 +1217 28 45 1 +1218 28 44 0 +1219 28 44 1 +1220 29 45 0 +1221 29 45 1 +1222 29 44 0 +1223 29 44 1 +1224 31 44 1 +1225 31 44 0 +1226 31 45 1 +1227 31 45 0 +1228 30 44 1 +1229 30 44 0 +1230 30 45 1 +1231 30 45 0 +1280 27 47 0 +1281 27 47 1 +1282 27 46 0 +1283 27 46 1 +1284 26 47 0 +1285 26 47 1 +1286 26 46 0 +1287 26 46 1 +1288 24 46 1 +1289 24 46 0 +1290 24 47 1 +1291 24 47 0 +1292 25 46 1 +1293 25 46 0 +1294 25 47 1 +1295 25 47 0 +1312 19 46 0 +1313 19 46 1 +1314 19 47 0 +1315 19 47 1 +1316 18 46 0 +1317 18 46 1 +1318 18 47 0 +1319 18 47 1 +1320 16 47 1 +1321 16 47 0 +1322 16 46 1 +1323 16 46 0 +1324 17 47 1 +1325 17 47 0 +1326 17 46 1 +1327 17 46 0 +1328 20 46 0 +1329 20 46 1 +1330 20 47 0 +1331 20 47 1 +1332 21 46 0 +1333 21 46 1 +1334 21 47 0 +1335 21 47 1 +1336 23 47 1 +1337 23 47 0 +1338 23 46 1 +1339 23 46 0 +1340 22 47 1 +1341 22 47 0 +1342 22 46 1 +1343 22 46 0 +1344 28 47 0 +1345 28 47 1 +1346 28 46 0 +1347 28 46 1 +1348 29 47 0 +1349 29 47 1 +1350 29 46 0 +1351 29 46 1 +1352 31 46 1 +1353 31 46 0 +1354 31 47 1 +1355 31 47 0 +1356 30 46 1 +1357 30 46 0 +1358 30 47 1 +1359 30 47 0 +1408 27 49 0 +1409 27 49 1 +1410 27 48 0 +1411 27 48 1 +1412 26 49 0 +1413 26 49 1 +1414 26 48 0 +1415 26 48 1 +1416 24 48 1 +1417 24 48 0 +1418 24 49 1 +1419 24 49 0 +1420 25 48 1 +1421 25 48 0 +1422 25 49 1 +1423 25 49 0 +1440 19 48 0 +1441 19 48 1 +1442 19 49 0 +1443 19 49 1 +1444 18 48 0 +1445 18 48 1 +1446 18 49 0 +1447 18 49 1 +1448 16 49 1 +1449 16 49 0 +1450 16 48 1 +1451 16 48 0 +1452 17 49 1 +1453 17 49 0 +1454 17 48 1 +1455 17 48 0 +1456 20 48 0 +1457 20 48 1 +1458 20 49 0 +1459 20 49 1 +1460 21 48 0 +1461 21 48 1 +1462 21 49 0 +1463 21 49 1 +1464 23 49 1 +1465 23 49 0 +1466 23 48 1 +1467 23 48 0 +1468 22 49 1 +1469 22 49 0 +1470 22 48 1 +1471 22 48 0 +1472 28 49 0 +1473 28 49 1 +1474 28 48 0 +1475 28 48 1 +1476 29 49 0 +1477 29 49 1 +1478 29 48 0 +1479 29 48 1 +1480 31 48 1 +1481 31 48 0 +1482 31 49 1 +1483 31 49 0 +1484 30 48 1 +1485 30 48 0 +1486 30 49 1 +1487 30 49 0 +1536 27 51 0 +1537 27 51 1 +1538 27 50 0 +1539 27 50 1 +1540 26 51 0 +1541 26 51 1 +1542 26 50 0 +1543 26 50 1 +1544 24 50 1 +1545 24 50 0 +1546 24 51 1 +1547 24 51 0 +1548 25 50 1 +1549 25 50 0 +1550 25 51 1 +1551 25 51 0 +1568 19 50 0 +1569 19 50 1 +1570 19 51 0 +1571 19 51 1 +1572 18 50 0 +1573 18 50 1 +1574 18 51 0 +1575 18 51 1 +1576 16 51 1 +1577 16 51 0 +1578 16 50 1 +1579 16 50 0 +1580 17 51 1 +1581 17 51 0 +1582 17 50 1 +1583 17 50 0 +1584 20 50 0 +1585 20 50 1 +1586 20 51 0 +1587 20 51 1 +1588 21 50 0 +1589 21 50 1 +1590 21 51 0 +1591 21 51 1 +1592 23 51 1 +1593 23 51 0 +1594 23 50 1 +1595 23 50 0 +1596 22 51 1 +1597 22 51 0 +1598 22 50 1 +1599 22 50 0 +1600 28 51 0 +1601 28 51 1 +1602 28 50 0 +1603 28 50 1 +1604 29 51 0 +1605 29 51 1 +1606 29 50 0 +1607 29 50 1 +1608 31 50 1 +1609 31 50 0 +1610 31 51 1 +1611 31 51 0 +1612 30 50 1 +1613 30 50 0 +1614 30 51 1 +1615 30 51 0 +1664 27 53 0 +1665 27 53 1 +1666 27 52 0 +1667 27 52 1 +1668 26 53 0 +1669 26 53 1 +1670 26 52 0 +1671 26 52 1 +1672 24 52 1 +1673 24 52 0 +1674 24 53 1 +1675 24 53 0 +1676 25 52 1 +1677 25 52 0 +1678 25 53 1 +1679 25 53 0 +1696 19 52 0 +1697 19 52 1 +1698 19 53 0 +1699 19 53 1 +1700 18 52 0 +1701 18 52 1 +1702 18 53 0 +1703 18 53 1 +1704 16 53 1 +1705 16 53 0 +1706 16 52 1 +1707 16 52 0 +1708 17 53 1 +1709 17 53 0 +1710 17 52 1 +1711 17 52 0 +1712 20 52 0 +1713 20 52 1 +1714 20 53 0 +1715 20 53 1 +1716 21 52 0 +1717 21 52 1 +1718 21 53 0 +1719 21 53 1 +1720 23 53 1 +1721 23 53 0 +1722 23 52 1 +1723 23 52 0 +1724 22 53 1 +1725 22 53 0 +1726 22 52 1 +1727 22 52 0 +1728 28 53 0 +1729 28 53 1 +1730 28 52 0 +1731 28 52 1 +1732 29 53 0 +1733 29 53 1 +1734 29 52 0 +1735 29 52 1 +1736 31 52 1 +1737 31 52 0 +1738 31 53 1 +1739 31 53 0 +1740 30 52 1 +1741 30 52 0 +1742 30 53 1 +1743 30 53 0 +1792 27 55 0 +1793 27 55 1 +1794 27 54 0 +1795 27 54 1 +1796 26 55 0 +1797 26 55 1 +1798 26 54 0 +1799 26 54 1 +1800 24 54 1 +1801 24 54 0 +1802 24 55 1 +1803 24 55 0 +1804 25 54 1 +1805 25 54 0 +1806 25 55 1 +1807 25 55 0 +1824 19 54 0 +1825 19 54 1 +1826 19 55 0 +1827 19 55 1 +1828 18 54 0 +1829 18 54 1 +1830 18 55 0 +1831 18 55 1 +1832 16 55 1 +1833 16 55 0 +1834 16 54 1 +1835 16 54 0 +1836 17 55 1 +1837 17 55 0 +1838 17 54 1 +1839 17 54 0 +1840 20 54 0 +1841 20 54 1 +1842 20 55 0 +1843 20 55 1 +1844 21 54 0 +1845 21 54 1 +1846 21 55 0 +1847 21 55 1 +1848 23 55 1 +1849 23 55 0 +1850 23 54 1 +1851 23 54 0 +1852 22 55 1 +1853 22 55 0 +1854 22 54 1 +1855 22 54 0 +1856 28 55 0 +1857 28 55 1 +1858 28 54 0 +1859 28 54 1 +1860 29 55 0 +1861 29 55 1 +1862 29 54 0 +1863 29 54 1 +1864 31 54 1 +1865 31 54 0 +1866 31 55 1 +1867 31 55 0 +1868 30 54 1 +1869 30 54 0 +1870 30 55 1 +1871 30 55 0 +2176 27 27 0 +2177 27 27 1 +2178 27 26 0 +2179 27 26 1 +2180 26 27 0 +2181 26 27 1 +2182 26 26 0 +2183 26 26 1 +2184 24 26 1 +2185 24 26 0 +2186 24 27 1 +2187 24 27 0 +2188 25 26 1 +2189 25 26 0 +2190 25 27 1 +2191 25 27 0 +2208 19 26 0 +2209 19 26 1 +2210 19 27 0 +2211 19 27 1 +2212 18 26 0 +2213 18 26 1 +2214 18 27 0 +2215 18 27 1 +2216 16 27 1 +2217 16 27 0 +2218 16 26 1 +2219 16 26 0 +2220 17 27 1 +2221 17 27 0 +2222 17 26 1 +2223 17 26 0 +2224 20 26 0 +2225 20 26 1 +2226 20 27 0 +2227 20 27 1 +2228 21 26 0 +2229 21 26 1 +2230 21 27 0 +2231 21 27 1 +2232 23 27 1 +2233 23 27 0 +2234 23 26 1 +2235 23 26 0 +2236 22 27 1 +2237 22 27 0 +2238 22 26 1 +2239 22 26 0 +2240 28 27 0 +2241 28 27 1 +2242 28 26 0 +2243 28 26 1 +2244 29 27 0 +2245 29 27 1 +2246 29 26 0 +2247 29 26 1 +2248 31 26 1 +2249 31 26 0 +2250 31 27 1 +2251 31 27 0 +2252 30 26 1 +2253 30 26 0 +2254 30 27 1 +2255 30 27 0 +2304 27 25 0 +2305 27 25 1 +2306 27 24 0 +2307 27 24 1 +2308 26 25 0 +2309 26 25 1 +2310 26 24 0 +2311 26 24 1 +2312 24 24 1 +2313 24 24 0 +2314 24 25 1 +2315 24 25 0 +2316 25 24 1 +2317 25 24 0 +2318 25 25 1 +2319 25 25 0 +2336 19 24 0 +2337 19 24 1 +2338 19 25 0 +2339 19 25 1 +2340 18 24 0 +2341 18 24 1 +2342 18 25 0 +2343 18 25 1 +2344 16 25 1 +2345 16 25 0 +2346 16 24 1 +2347 16 24 0 +2348 17 25 1 +2349 17 25 0 +2350 17 24 1 +2351 17 24 0 +2352 20 24 0 +2353 20 24 1 +2354 20 25 0 +2355 20 25 1 +2356 21 24 0 +2357 21 24 1 +2358 21 25 0 +2359 21 25 1 +2360 23 25 1 +2361 23 25 0 +2362 23 24 1 +2363 23 24 0 +2364 22 25 1 +2365 22 25 0 +2366 22 24 1 +2367 22 24 0 +2368 28 25 0 +2369 28 25 1 +2370 28 24 0 +2371 28 24 1 +2372 29 25 0 +2373 29 25 1 +2374 29 24 0 +2375 29 24 1 +2376 31 24 1 +2377 31 24 0 +2378 31 25 1 +2379 31 25 0 +2380 30 24 1 +2381 30 24 0 +2382 30 25 1 +2383 30 25 0 +2432 27 23 0 +2433 27 23 1 +2434 27 22 0 +2435 27 22 1 +2436 26 23 0 +2437 26 23 1 +2438 26 22 0 +2439 26 22 1 +2440 24 22 1 +2441 24 22 0 +2442 24 23 1 +2443 24 23 0 +2444 25 22 1 +2445 25 22 0 +2446 25 23 1 +2447 25 23 0 +2464 19 22 0 +2465 19 22 1 +2466 19 23 0 +2467 19 23 1 +2468 18 22 0 +2469 18 22 1 +2470 18 23 0 +2471 18 23 1 +2472 16 23 1 +2473 16 23 0 +2474 16 22 1 +2475 16 22 0 +2476 17 23 1 +2477 17 23 0 +2478 17 22 1 +2479 17 22 0 +2480 20 22 0 +2481 20 22 1 +2482 20 23 0 +2483 20 23 1 +2484 21 22 0 +2485 21 22 1 +2486 21 23 0 +2487 21 23 1 +2488 23 23 1 +2489 23 23 0 +2490 23 22 1 +2491 23 22 0 +2492 22 23 1 +2493 22 23 0 +2494 22 22 1 +2495 22 22 0 +2496 28 23 0 +2497 28 23 1 +2498 28 22 0 +2499 28 22 1 +2500 29 23 0 +2501 29 23 1 +2502 29 22 0 +2503 29 22 1 +2504 31 22 1 +2505 31 22 0 +2506 31 23 1 +2507 31 23 0 +2508 30 22 1 +2509 30 22 0 +2510 30 23 1 +2511 30 23 0 +2560 27 21 0 +2561 27 21 1 +2562 27 20 0 +2563 27 20 1 +2564 26 21 0 +2565 26 21 1 +2566 26 20 0 +2567 26 20 1 +2568 24 20 1 +2569 24 20 0 +2570 24 21 1 +2571 24 21 0 +2572 25 20 1 +2573 25 20 0 +2574 25 21 1 +2575 25 21 0 +2592 19 20 0 +2593 19 20 1 +2594 19 21 0 +2595 19 21 1 +2596 18 20 0 +2597 18 20 1 +2598 18 21 0 +2599 18 21 1 +2600 16 21 1 +2601 16 21 0 +2602 16 20 1 +2603 16 20 0 +2604 17 21 1 +2605 17 21 0 +2606 17 20 1 +2607 17 20 0 +2608 20 20 0 +2609 20 20 1 +2610 20 21 0 +2611 20 21 1 +2612 21 20 0 +2613 21 20 1 +2614 21 21 0 +2615 21 21 1 +2616 23 21 1 +2617 23 21 0 +2618 23 20 1 +2619 23 20 0 +2620 22 21 1 +2621 22 21 0 +2622 22 20 1 +2623 22 20 0 +2624 28 21 0 +2625 28 21 1 +2626 28 20 0 +2627 28 20 1 +2628 29 21 0 +2629 29 21 1 +2630 29 20 0 +2631 29 20 1 +2632 31 20 1 +2633 31 20 0 +2634 31 21 1 +2635 31 21 0 +2636 30 20 1 +2637 30 20 0 +2638 30 21 1 +2639 30 21 0 +2688 27 19 0 +2689 27 19 1 +2690 27 18 0 +2691 27 18 1 +2692 26 19 0 +2693 26 19 1 +2694 26 18 0 +2695 26 18 1 +2696 24 18 1 +2697 24 18 0 +2698 24 19 1 +2699 24 19 0 +2700 25 18 1 +2701 25 18 0 +2702 25 19 1 +2703 25 19 0 +2720 19 18 0 +2721 19 18 1 +2722 19 19 0 +2723 19 19 1 +2724 18 18 0 +2725 18 18 1 +2726 18 19 0 +2727 18 19 1 +2728 16 19 1 +2729 16 19 0 +2730 16 18 1 +2731 16 18 0 +2732 17 19 1 +2733 17 19 0 +2734 17 18 1 +2735 17 18 0 +2736 20 18 0 +2737 20 18 1 +2738 20 19 0 +2739 20 19 1 +2740 21 18 0 +2741 21 18 1 +2742 21 19 0 +2743 21 19 1 +2744 23 19 1 +2745 23 19 0 +2746 23 18 1 +2747 23 18 0 +2748 22 19 1 +2749 22 19 0 +2750 22 18 1 +2751 22 18 0 +2752 28 19 0 +2753 28 19 1 +2754 28 18 0 +2755 28 18 1 +2756 29 19 0 +2757 29 19 1 +2758 29 18 0 +2759 29 18 1 +2760 31 18 1 +2761 31 18 0 +2762 31 19 1 +2763 31 19 0 +2764 30 18 1 +2765 30 18 0 +2766 30 19 1 +2767 30 19 0 +2816 27 17 0 +2817 27 17 1 +2818 27 16 0 +2819 27 16 1 +2820 26 17 0 +2821 26 17 1 +2822 26 16 0 +2823 26 16 1 +2824 24 16 1 +2825 24 16 0 +2826 24 17 1 +2827 24 17 0 +2828 25 16 1 +2829 25 16 0 +2830 25 17 1 +2831 25 17 0 +2848 19 16 0 +2849 19 16 1 +2850 19 17 0 +2851 19 17 1 +2852 18 16 0 +2853 18 16 1 +2854 18 17 0 +2855 18 17 1 +2856 16 17 1 +2857 16 17 0 +2858 16 16 1 +2859 16 16 0 +2860 17 17 1 +2861 17 17 0 +2862 17 16 1 +2863 17 16 0 +2864 20 16 0 +2865 20 16 1 +2866 20 17 0 +2867 20 17 1 +2868 21 16 0 +2869 21 16 1 +2870 21 17 0 +2871 21 17 1 +2872 23 17 1 +2873 23 17 0 +2874 23 16 1 +2875 23 16 0 +2876 22 17 1 +2877 22 17 0 +2878 22 16 1 +2879 22 16 0 +2880 28 17 0 +2881 28 17 1 +2882 28 16 0 +2883 28 16 1 +2884 29 17 0 +2885 29 17 1 +2886 29 16 0 +2887 29 16 1 +2888 31 16 1 +2889 31 16 0 +2890 31 17 1 +2891 31 17 0 +2892 30 16 1 +2893 30 16 0 +2894 30 17 1 +2895 30 17 0 +2944 27 15 0 +2945 27 15 1 +2946 27 14 0 +2947 27 14 1 +2948 26 15 0 +2949 26 15 1 +2950 26 14 0 +2951 26 14 1 +2952 24 14 1 +2953 24 14 0 +2954 24 15 1 +2955 24 15 0 +2956 25 14 1 +2957 25 14 0 +2958 25 15 1 +2959 25 15 0 +2976 19 14 0 +2977 19 14 1 +2978 19 15 0 +2979 19 15 1 +2980 18 14 0 +2981 18 14 1 +2982 18 15 0 +2983 18 15 1 +2984 16 15 1 +2985 16 15 0 +2986 16 14 1 +2987 16 14 0 +2988 17 15 1 +2989 17 15 0 +2990 17 14 1 +2991 17 14 0 +2992 20 14 0 +2993 20 14 1 +2994 20 15 0 +2995 20 15 1 +2996 21 14 0 +2997 21 14 1 +2998 21 15 0 +2999 21 15 1 +3000 23 15 1 +3001 23 15 0 +3002 23 14 1 +3003 23 14 0 +3004 22 15 1 +3005 22 15 0 +3006 22 14 1 +3007 22 14 0 +3008 28 15 0 +3009 28 15 1 +3010 28 14 0 +3011 28 14 1 +3012 29 15 0 +3013 29 15 1 +3014 29 14 0 +3015 29 14 1 +3016 31 14 1 +3017 31 14 0 +3018 31 15 1 +3019 31 15 0 +3020 30 14 1 +3021 30 14 0 +3022 30 15 1 +3023 30 15 0 +3072 27 13 0 +3073 27 13 1 +3074 27 12 0 +3075 27 12 1 +3076 26 13 0 +3077 26 13 1 +3078 26 12 0 +3079 26 12 1 +3080 24 12 1 +3081 24 12 0 +3082 24 13 1 +3083 24 13 0 +3084 25 12 1 +3085 25 12 0 +3086 25 13 1 +3087 25 13 0 +3104 19 12 0 +3105 19 12 1 +3106 19 13 0 +3107 19 13 1 +3108 18 12 0 +3109 18 12 1 +3110 18 13 0 +3111 18 13 1 +3112 16 13 1 +3113 16 13 0 +3114 16 12 1 +3115 16 12 0 +3116 17 13 1 +3117 17 13 0 +3118 17 12 1 +3119 17 12 0 +3120 20 12 0 +3121 20 12 1 +3122 20 13 0 +3123 20 13 1 +3124 21 12 0 +3125 21 12 1 +3126 21 13 0 +3127 21 13 1 +3128 23 13 1 +3129 23 13 0 +3130 23 12 1 +3131 23 12 0 +3132 22 13 1 +3133 22 13 0 +3134 22 12 1 +3135 22 12 0 +3136 28 13 0 +3137 28 13 1 +3138 28 12 0 +3139 28 12 1 +3140 29 13 0 +3141 29 13 1 +3142 29 12 0 +3143 29 12 1 +3144 31 12 1 +3145 31 12 0 +3146 31 13 1 +3147 31 13 0 +3148 30 12 1 +3149 30 12 0 +3150 30 13 1 +3151 30 13 0 +3200 27 11 0 +3201 27 11 1 +3202 27 10 0 +3203 27 10 1 +3204 26 11 0 +3205 26 11 1 +3206 26 10 0 +3207 26 10 1 +3208 24 10 1 +3209 24 10 0 +3210 24 11 1 +3211 24 11 0 +3212 25 10 1 +3213 25 10 0 +3214 25 11 1 +3215 25 11 0 +3232 19 10 0 +3233 19 10 1 +3234 19 11 0 +3235 19 11 1 +3236 18 10 0 +3237 18 10 1 +3238 18 11 0 +3239 18 11 1 +3240 16 11 1 +3241 16 11 0 +3242 16 10 1 +3243 16 10 0 +3244 17 11 1 +3245 17 11 0 +3246 17 10 1 +3247 17 10 0 +3248 20 10 0 +3249 20 10 1 +3250 20 11 0 +3251 20 11 1 +3252 21 10 0 +3253 21 10 1 +3254 21 11 0 +3255 21 11 1 +3256 23 11 1 +3257 23 11 0 +3258 23 10 1 +3259 23 10 0 +3260 22 11 1 +3261 22 11 0 +3262 22 10 1 +3263 22 10 0 +3264 28 11 0 +3265 28 11 1 +3266 28 10 0 +3267 28 10 1 +3268 29 11 0 +3269 29 11 1 +3270 29 10 0 +3271 29 10 1 +3272 31 10 1 +3273 31 10 0 +3274 31 11 1 +3275 31 11 0 +3276 30 10 1 +3277 30 10 0 +3278 30 11 1 +3279 30 11 0 +3328 27 9 0 +3329 27 9 1 +3330 27 8 0 +3331 27 8 1 +3332 26 9 0 +3333 26 9 1 +3334 26 8 0 +3335 26 8 1 +3336 24 8 1 +3337 24 8 0 +3338 24 9 1 +3339 24 9 0 +3340 25 8 1 +3341 25 8 0 +3342 25 9 1 +3343 25 9 0 +3360 19 8 0 +3361 19 8 1 +3362 19 9 0 +3363 19 9 1 +3364 18 8 0 +3365 18 8 1 +3366 18 9 0 +3367 18 9 1 +3368 16 9 1 +3369 16 9 0 +3370 16 8 1 +3371 16 8 0 +3372 17 9 1 +3373 17 9 0 +3374 17 8 1 +3375 17 8 0 +3376 20 8 0 +3377 20 8 1 +3378 20 9 0 +3379 20 9 1 +3380 21 8 0 +3381 21 8 1 +3382 21 9 0 +3383 21 9 1 +3384 23 9 1 +3385 23 9 0 +3386 23 8 1 +3387 23 8 0 +3388 22 9 1 +3389 22 9 0 +3390 22 8 1 +3391 22 8 0 +3392 28 9 0 +3393 28 9 1 +3394 28 8 0 +3395 28 8 1 +3396 29 9 0 +3397 29 9 1 +3398 29 8 0 +3399 29 8 1 +3400 31 8 1 +3401 31 8 0 +3402 31 9 1 +3403 31 9 0 +3404 30 8 1 +3405 30 8 0 +3406 30 9 1 +3407 30 9 0 +3456 27 7 0 +3457 27 7 1 +3458 27 6 0 +3459 27 6 1 +3460 26 7 0 +3461 26 7 1 +3462 26 6 0 +3463 26 6 1 +3464 24 6 1 +3465 24 6 0 +3466 24 7 1 +3467 24 7 0 +3468 25 6 1 +3469 25 6 0 +3470 25 7 1 +3471 25 7 0 +3488 19 6 0 +3489 19 6 1 +3490 19 7 0 +3491 19 7 1 +3492 18 6 0 +3493 18 6 1 +3494 18 7 0 +3495 18 7 1 +3496 16 7 1 +3497 16 7 0 +3498 16 6 1 +3499 16 6 0 +3500 17 7 1 +3501 17 7 0 +3502 17 6 1 +3503 17 6 0 +3504 20 6 0 +3505 20 6 1 +3506 20 7 0 +3507 20 7 1 +3508 21 6 0 +3509 21 6 1 +3510 21 7 0 +3511 21 7 1 +3512 23 7 1 +3513 23 7 0 +3514 23 6 1 +3515 23 6 0 +3516 22 7 1 +3517 22 7 0 +3518 22 6 1 +3519 22 6 0 +3520 28 7 0 +3521 28 7 1 +3522 28 6 0 +3523 28 6 1 +3524 29 7 0 +3525 29 7 1 +3526 29 6 0 +3527 29 6 1 +3528 31 6 1 +3529 31 6 0 +3530 31 7 1 +3531 31 7 0 +3532 30 6 1 +3533 30 6 0 +3534 30 7 1 +3535 30 7 0 +3584 27 5 0 +3585 27 5 1 +3586 27 4 0 +3587 27 4 1 +3588 26 5 0 +3589 26 5 1 +3590 26 4 0 +3591 26 4 1 +3592 24 4 1 +3593 24 4 0 +3594 24 5 1 +3595 24 5 0 +3596 25 4 1 +3597 25 4 0 +3598 25 5 1 +3599 25 5 0 +3616 19 4 0 +3617 19 4 1 +3618 19 5 0 +3619 19 5 1 +3620 18 4 0 +3621 18 4 1 +3622 18 5 0 +3623 18 5 1 +3624 16 5 1 +3625 16 5 0 +3626 16 4 1 +3627 16 4 0 +3628 17 5 1 +3629 17 5 0 +3630 17 4 1 +3631 17 4 0 +3632 20 4 0 +3633 20 4 1 +3634 20 5 0 +3635 20 5 1 +3636 21 4 0 +3637 21 4 1 +3638 21 5 0 +3639 21 5 1 +3640 23 5 1 +3641 23 5 0 +3642 23 4 1 +3643 23 4 0 +3644 22 5 1 +3645 22 5 0 +3646 22 4 1 +3647 22 4 0 +3648 28 5 0 +3649 28 5 1 +3650 28 4 0 +3651 28 4 1 +3652 29 5 0 +3653 29 5 1 +3654 29 4 0 +3655 29 4 1 +3656 31 4 1 +3657 31 4 0 +3658 31 5 1 +3659 31 5 0 +3660 30 4 1 +3661 30 4 0 +3662 30 5 1 +3663 30 5 0 +3712 27 3 0 +3713 27 3 1 +3714 27 2 0 +3715 27 2 1 +3716 26 3 0 +3717 26 3 1 +3718 26 2 0 +3719 26 2 1 +3720 24 2 1 +3721 24 2 0 +3722 24 3 1 +3723 24 3 0 +3724 25 2 1 +3725 25 2 0 +3726 25 3 1 +3727 25 3 0 +3744 19 2 0 +3745 19 2 1 +3746 19 3 0 +3747 19 3 1 +3748 18 2 0 +3749 18 2 1 +3750 18 3 0 +3751 18 3 1 +3752 16 3 1 +3753 16 3 0 +3754 16 2 1 +3755 16 2 0 +3756 17 3 1 +3757 17 3 0 +3758 17 2 1 +3759 17 2 0 +3760 20 2 0 +3761 20 2 1 +3762 20 3 0 +3763 20 3 1 +3764 21 2 0 +3765 21 2 1 +3766 21 3 0 +3767 21 3 1 +3768 23 3 1 +3769 23 3 0 +3770 23 2 1 +3771 23 2 0 +3772 22 3 1 +3773 22 3 0 +3774 22 2 1 +3775 22 2 0 +3776 28 3 0 +3777 28 3 1 +3778 28 2 0 +3779 28 2 1 +3780 29 3 0 +3781 29 3 1 +3782 29 2 0 +3783 29 2 1 +3784 31 2 1 +3785 31 2 0 +3786 31 3 1 +3787 31 3 0 +3788 30 2 1 +3789 30 2 0 +3790 30 3 1 +3791 30 3 0 +3840 27 1 0 +3841 27 1 1 +3842 27 0 0 +3843 27 0 1 +3844 26 1 0 +3845 26 1 1 +3846 26 0 0 +3847 26 0 1 +3848 24 0 1 +3849 24 0 0 +3850 24 1 1 +3851 24 1 0 +3852 25 0 1 +3853 25 0 0 +3854 25 1 1 +3855 25 1 0 +3872 19 0 0 +3873 19 0 1 +3874 19 1 0 +3875 19 1 1 +3876 18 0 0 +3877 18 0 1 +3878 18 1 0 +3879 18 1 1 +3880 16 1 1 +3881 16 1 0 +3882 16 0 1 +3883 16 0 0 +3884 17 1 1 +3885 17 1 0 +3886 17 0 1 +3887 17 0 0 +3888 20 0 0 +3889 20 0 1 +3890 20 1 0 +3891 20 1 1 +3892 21 0 0 +3893 21 0 1 +3894 21 1 0 +3895 21 1 1 +3896 23 1 1 +3897 23 1 0 +3898 23 0 1 +3899 23 0 0 +3900 22 1 1 +3901 22 1 0 +3902 22 0 1 +3903 22 0 0 +3904 28 1 0 +3905 28 1 1 +3906 28 0 0 +3907 28 0 1 +3908 29 1 0 +3909 29 1 1 +3910 29 0 0 +3911 29 0 1 +3912 31 0 1 +3913 31 0 0 +3914 31 1 1 +3915 31 1 0 +3916 30 0 1 +3917 30 0 0 +3918 30 1 1 +3919 30 1 0 diff --git a/Detectors/PHOS/base/files/Mod2RCU2.data b/Detectors/PHOS/base/files/Mod2RCU2.data new file mode 100644 index 0000000000000..21de94d7ace53 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod2RCU2.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 2 0 2 + 1 2 1 2 + 2 2 2 2 + 3 2 3 2 + 4 2 4 2 + 5 2 5 2 + 6 2 6 2 + 7 2 7 2 + 8 2 8 2 + 9 2 9 2 + 10 2 10 2 + 11 2 11 2 + 12 2 12 2 + 13 2 13 2 + 14 2 14 2 + 15 2 15 2 + 16 2 16 2 + 17 2 17 2 + 18 2 18 2 + 19 2 19 2 + 20 2 20 2 + 21 2 21 2 + 22 2 22 2 + 23 2 23 2 + 24 2 24 2 + 25 2 25 2 + 26 2 26 2 + 27 2 27 2 + 28 2 28 2 + 29 2 29 2 + 30 2 30 2 + 31 2 31 2 + 32 2 32 2 + 33 2 33 2 + 34 2 34 2 + 35 2 35 2 + 36 2 36 2 + 37 2 37 2 + 38 2 38 2 + 39 2 39 2 + 40 2 40 2 + 41 2 41 2 + 42 2 42 2 + 43 2 43 2 + 44 2 44 2 + 45 2 45 2 + 46 2 46 2 + 47 2 47 2 + 48 2 48 2 + 49 2 49 2 + 50 2 50 2 + 51 2 51 2 + 52 2 52 2 + 53 2 53 2 + 54 2 54 2 + 55 2 55 2 + 56 2 56 2 + 57 2 57 2 + 58 2 58 2 + 59 2 59 2 + 60 2 60 2 + 61 2 61 2 + 62 2 62 2 + 63 2 63 2 + 64 2 64 2 + 65 2 65 2 + 66 2 66 2 + 67 2 67 2 + 68 2 68 2 + 69 2 69 2 + 70 2 70 2 + 71 2 71 2 + 72 2 72 2 + 73 2 73 2 + 74 2 74 2 + 75 2 75 2 + 76 2 76 2 + 77 2 77 2 + 78 2 78 2 + 79 2 79 2 + 80 2 80 2 + 81 2 81 2 + 82 2 82 2 + 83 2 83 2 + 84 2 84 2 + 85 2 85 2 + 86 2 86 2 + 87 2 87 2 + 88 2 88 2 + 89 2 89 2 + 90 2 90 2 + 91 2 91 2 + 92 2 92 2 + 93 2 93 2 + 94 2 94 2 + 95 2 95 2 + 96 2 96 2 + 97 2 97 2 + 98 2 98 2 + 99 2 99 2 + 100 2 100 2 + 101 2 101 2 + 102 2 102 2 + 103 2 103 2 + 104 2 104 2 + 105 2 105 2 + 106 2 106 2 + 107 2 107 2 + 108 2 108 2 + 109 2 109 2 + 110 2 110 2 + 111 2 111 2 + 112 2 112 2 + 113 2 113 2 + 114 2 114 2 + 115 2 115 2 + 116 2 116 2 + 117 2 117 2 + 118 2 118 2 + 119 2 119 2 + 120 2 120 2 + 121 2 121 2 + 122 2 122 2 + 123 2 123 2 + 124 2 124 2 + 125 2 125 2 + 126 2 126 2 + 127 2 127 2 +2048 2 2048 2 +2049 2 2049 2 +2050 2 2050 2 +2051 2 2051 2 +2052 2 2052 2 +2053 2 2053 2 +2054 2 2054 2 +2055 2 2055 2 +2056 2 2056 2 +2057 2 2057 2 +2058 2 2058 2 +2059 2 2059 2 +2060 2 2060 2 +2061 2 2061 2 +2062 2 2062 2 +2063 2 2063 2 +2064 2 2064 2 +2065 2 2065 2 +2066 2 2066 2 +2067 2 2067 2 +2068 2 2068 2 +2069 2 2069 2 +2070 2 2070 2 +2071 2 2071 2 +2072 2 2072 2 +2073 2 2073 2 +2074 2 2074 2 +2075 2 2075 2 +2076 2 2076 2 +2077 2 2077 2 +2078 2 2078 2 +2079 2 2079 2 +2080 2 2080 2 +2081 2 2081 2 +2082 2 2082 2 +2083 2 2083 2 +2084 2 2084 2 +2085 2 2085 2 +2086 2 2086 2 +2087 2 2087 2 +2088 2 2088 2 +2089 2 2089 2 +2090 2 2090 2 +2091 2 2091 2 +2092 2 2092 2 +2093 2 2093 2 +2094 2 2094 2 +2095 2 2095 2 +2096 2 2096 2 +2097 2 2097 2 +2098 2 2098 2 +2099 2 2099 2 +2100 2 2100 2 +2101 2 2101 2 +2102 2 2102 2 +2103 2 2103 2 +2104 2 2104 2 +2105 2 2105 2 +2106 2 2106 2 +2107 2 2107 2 +2108 2 2108 2 +2109 2 2109 2 +2110 2 2110 2 +2111 2 2111 2 +2112 2 2112 2 +2113 2 2113 2 +2114 2 2114 2 +2115 2 2115 2 +2116 2 2116 2 +2117 2 2117 2 +2118 2 2118 2 +2119 2 2119 2 +2120 2 2120 2 +2121 2 2121 2 +2122 2 2122 2 +2123 2 2123 2 +2124 2 2124 2 +2125 2 2125 2 +2126 2 2126 2 +2127 2 2127 2 +2128 2 2128 2 +2129 2 2129 2 +2130 2 2130 2 +2131 2 2131 2 +2132 2 2132 2 +2133 2 2133 2 +2134 2 2134 2 +2135 2 2135 2 +2136 2 2136 2 +2137 2 2137 2 +2138 2 2138 2 +2139 2 2139 2 +2140 2 2140 2 +2141 2 2141 2 +2142 2 2142 2 +2143 2 2143 2 +2144 2 2144 2 +2145 2 2145 2 +2146 2 2146 2 +2147 2 2147 2 +2148 2 2148 2 +2149 2 2149 2 +2150 2 2150 2 +2151 2 2151 2 +2152 2 2152 2 +2153 2 2153 2 +2154 2 2154 2 +2155 2 2155 2 +2156 2 2156 2 +2157 2 2157 2 +2158 2 2158 2 +2159 2 2159 2 +2160 2 2160 2 +2161 2 2161 2 +2162 2 2162 2 +2163 2 2163 2 +2164 2 2164 2 +2165 2 2165 2 +2166 2 2166 2 +2167 2 2167 2 +2168 2 2168 2 +2169 2 2169 2 +2170 2 2170 2 +2171 2 2171 2 +2172 2 2172 2 +2173 2 2173 2 +2174 2 2174 2 +2175 2 2175 2 + 128 43 29 0 + 129 43 29 1 + 130 43 28 0 + 131 43 28 1 + 132 42 29 0 + 133 42 29 1 + 134 42 28 0 + 135 42 28 1 + 136 40 28 1 + 137 40 28 0 + 138 40 29 1 + 139 40 29 0 + 140 41 28 1 + 141 41 28 0 + 142 41 29 1 + 143 41 29 0 + 160 35 28 0 + 161 35 28 1 + 162 35 29 0 + 163 35 29 1 + 164 34 28 0 + 165 34 28 1 + 166 34 29 0 + 167 34 29 1 + 168 32 29 1 + 169 32 29 0 + 170 32 28 1 + 171 32 28 0 + 172 33 29 1 + 173 33 29 0 + 174 33 28 1 + 175 33 28 0 + 176 36 28 0 + 177 36 28 1 + 178 36 29 0 + 179 36 29 1 + 180 37 28 0 + 181 37 28 1 + 182 37 29 0 + 183 37 29 1 + 184 39 29 1 + 185 39 29 0 + 186 39 28 1 + 187 39 28 0 + 188 38 29 1 + 189 38 29 0 + 190 38 28 1 + 191 38 28 0 + 192 44 29 0 + 193 44 29 1 + 194 44 28 0 + 195 44 28 1 + 196 45 29 0 + 197 45 29 1 + 198 45 28 0 + 199 45 28 1 + 200 47 28 1 + 201 47 28 0 + 202 47 29 1 + 203 47 29 0 + 204 46 28 1 + 205 46 28 0 + 206 46 29 1 + 207 46 29 0 + 256 43 31 0 + 257 43 31 1 + 258 43 30 0 + 259 43 30 1 + 260 42 31 0 + 261 42 31 1 + 262 42 30 0 + 263 42 30 1 + 264 40 30 1 + 265 40 30 0 + 266 40 31 1 + 267 40 31 0 + 268 41 30 1 + 269 41 30 0 + 270 41 31 1 + 271 41 31 0 + 288 35 30 0 + 289 35 30 1 + 290 35 31 0 + 291 35 31 1 + 292 34 30 0 + 293 34 30 1 + 294 34 31 0 + 295 34 31 1 + 296 32 31 1 + 297 32 31 0 + 298 32 30 1 + 299 32 30 0 + 300 33 31 1 + 301 33 31 0 + 302 33 30 1 + 303 33 30 0 + 304 36 30 0 + 305 36 30 1 + 306 36 31 0 + 307 36 31 1 + 308 37 30 0 + 309 37 30 1 + 310 37 31 0 + 311 37 31 1 + 312 39 31 1 + 313 39 31 0 + 314 39 30 1 + 315 39 30 0 + 316 38 31 1 + 317 38 31 0 + 318 38 30 1 + 319 38 30 0 + 320 44 31 0 + 321 44 31 1 + 322 44 30 0 + 323 44 30 1 + 324 45 31 0 + 325 45 31 1 + 326 45 30 0 + 327 45 30 1 + 328 47 30 1 + 329 47 30 0 + 330 47 31 1 + 331 47 31 0 + 332 46 30 1 + 333 46 30 0 + 334 46 31 1 + 335 46 31 0 + 384 43 33 0 + 385 43 33 1 + 386 43 32 0 + 387 43 32 1 + 388 42 33 0 + 389 42 33 1 + 390 42 32 0 + 391 42 32 1 + 392 40 32 1 + 393 40 32 0 + 394 40 33 1 + 395 40 33 0 + 396 41 32 1 + 397 41 32 0 + 398 41 33 1 + 399 41 33 0 + 416 35 32 0 + 417 35 32 1 + 418 35 33 0 + 419 35 33 1 + 420 34 32 0 + 421 34 32 1 + 422 34 33 0 + 423 34 33 1 + 424 32 33 1 + 425 32 33 0 + 426 32 32 1 + 427 32 32 0 + 428 33 33 1 + 429 33 33 0 + 430 33 32 1 + 431 33 32 0 + 432 36 32 0 + 433 36 32 1 + 434 36 33 0 + 435 36 33 1 + 436 37 32 0 + 437 37 32 1 + 438 37 33 0 + 439 37 33 1 + 440 39 33 1 + 441 39 33 0 + 442 39 32 1 + 443 39 32 0 + 444 38 33 1 + 445 38 33 0 + 446 38 32 1 + 447 38 32 0 + 448 44 33 0 + 449 44 33 1 + 450 44 32 0 + 451 44 32 1 + 452 45 33 0 + 453 45 33 1 + 454 45 32 0 + 455 45 32 1 + 456 47 32 1 + 457 47 32 0 + 458 47 33 1 + 459 47 33 0 + 460 46 32 1 + 461 46 32 0 + 462 46 33 1 + 463 46 33 0 + 512 43 35 0 + 513 43 35 1 + 514 43 34 0 + 515 43 34 1 + 516 42 35 0 + 517 42 35 1 + 518 42 34 0 + 519 42 34 1 + 520 40 34 1 + 521 40 34 0 + 522 40 35 1 + 523 40 35 0 + 524 41 34 1 + 525 41 34 0 + 526 41 35 1 + 527 41 35 0 + 544 35 34 0 + 545 35 34 1 + 546 35 35 0 + 547 35 35 1 + 548 34 34 0 + 549 34 34 1 + 550 34 35 0 + 551 34 35 1 + 552 32 35 1 + 553 32 35 0 + 554 32 34 1 + 555 32 34 0 + 556 33 35 1 + 557 33 35 0 + 558 33 34 1 + 559 33 34 0 + 560 36 34 0 + 561 36 34 1 + 562 36 35 0 + 563 36 35 1 + 564 37 34 0 + 565 37 34 1 + 566 37 35 0 + 567 37 35 1 + 568 39 35 1 + 569 39 35 0 + 570 39 34 1 + 571 39 34 0 + 572 38 35 1 + 573 38 35 0 + 574 38 34 1 + 575 38 34 0 + 576 44 35 0 + 577 44 35 1 + 578 44 34 0 + 579 44 34 1 + 580 45 35 0 + 581 45 35 1 + 582 45 34 0 + 583 45 34 1 + 584 47 34 1 + 585 47 34 0 + 586 47 35 1 + 587 47 35 0 + 588 46 34 1 + 589 46 34 0 + 590 46 35 1 + 591 46 35 0 + 640 43 37 0 + 641 43 37 1 + 642 43 36 0 + 643 43 36 1 + 644 42 37 0 + 645 42 37 1 + 646 42 36 0 + 647 42 36 1 + 648 40 36 1 + 649 40 36 0 + 650 40 37 1 + 651 40 37 0 + 652 41 36 1 + 653 41 36 0 + 654 41 37 1 + 655 41 37 0 + 672 35 36 0 + 673 35 36 1 + 674 35 37 0 + 675 35 37 1 + 676 34 36 0 + 677 34 36 1 + 678 34 37 0 + 679 34 37 1 + 680 32 37 1 + 681 32 37 0 + 682 32 36 1 + 683 32 36 0 + 684 33 37 1 + 685 33 37 0 + 686 33 36 1 + 687 33 36 0 + 688 36 36 0 + 689 36 36 1 + 690 36 37 0 + 691 36 37 1 + 692 37 36 0 + 693 37 36 1 + 694 37 37 0 + 695 37 37 1 + 696 39 37 1 + 697 39 37 0 + 698 39 36 1 + 699 39 36 0 + 700 38 37 1 + 701 38 37 0 + 702 38 36 1 + 703 38 36 0 + 704 44 37 0 + 705 44 37 1 + 706 44 36 0 + 707 44 36 1 + 708 45 37 0 + 709 45 37 1 + 710 45 36 0 + 711 45 36 1 + 712 47 36 1 + 713 47 36 0 + 714 47 37 1 + 715 47 37 0 + 716 46 36 1 + 717 46 36 0 + 718 46 37 1 + 719 46 37 0 + 768 43 39 0 + 769 43 39 1 + 770 43 38 0 + 771 43 38 1 + 772 42 39 0 + 773 42 39 1 + 774 42 38 0 + 775 42 38 1 + 776 40 38 1 + 777 40 38 0 + 778 40 39 1 + 779 40 39 0 + 780 41 38 1 + 781 41 38 0 + 782 41 39 1 + 783 41 39 0 + 800 35 38 0 + 801 35 38 1 + 802 35 39 0 + 803 35 39 1 + 804 34 38 0 + 805 34 38 1 + 806 34 39 0 + 807 34 39 1 + 808 32 39 1 + 809 32 39 0 + 810 32 38 1 + 811 32 38 0 + 812 33 39 1 + 813 33 39 0 + 814 33 38 1 + 815 33 38 0 + 816 36 38 0 + 817 36 38 1 + 818 36 39 0 + 819 36 39 1 + 820 37 38 0 + 821 37 38 1 + 822 37 39 0 + 823 37 39 1 + 824 39 39 1 + 825 39 39 0 + 826 39 38 1 + 827 39 38 0 + 828 38 39 1 + 829 38 39 0 + 830 38 38 1 + 831 38 38 0 + 832 44 39 0 + 833 44 39 1 + 834 44 38 0 + 835 44 38 1 + 836 45 39 0 + 837 45 39 1 + 838 45 38 0 + 839 45 38 1 + 840 47 38 1 + 841 47 38 0 + 842 47 39 1 + 843 47 39 0 + 844 46 38 1 + 845 46 38 0 + 846 46 39 1 + 847 46 39 0 + 896 43 41 0 + 897 43 41 1 + 898 43 40 0 + 899 43 40 1 + 900 42 41 0 + 901 42 41 1 + 902 42 40 0 + 903 42 40 1 + 904 40 40 1 + 905 40 40 0 + 906 40 41 1 + 907 40 41 0 + 908 41 40 1 + 909 41 40 0 + 910 41 41 1 + 911 41 41 0 + 928 35 40 0 + 929 35 40 1 + 930 35 41 0 + 931 35 41 1 + 932 34 40 0 + 933 34 40 1 + 934 34 41 0 + 935 34 41 1 + 936 32 41 1 + 937 32 41 0 + 938 32 40 1 + 939 32 40 0 + 940 33 41 1 + 941 33 41 0 + 942 33 40 1 + 943 33 40 0 + 944 36 40 0 + 945 36 40 1 + 946 36 41 0 + 947 36 41 1 + 948 37 40 0 + 949 37 40 1 + 950 37 41 0 + 951 37 41 1 + 952 39 41 1 + 953 39 41 0 + 954 39 40 1 + 955 39 40 0 + 956 38 41 1 + 957 38 41 0 + 958 38 40 1 + 959 38 40 0 + 960 44 41 0 + 961 44 41 1 + 962 44 40 0 + 963 44 40 1 + 964 45 41 0 + 965 45 41 1 + 966 45 40 0 + 967 45 40 1 + 968 47 40 1 + 969 47 40 0 + 970 47 41 1 + 971 47 41 0 + 972 46 40 1 + 973 46 40 0 + 974 46 41 1 + 975 46 41 0 +1024 43 43 0 +1025 43 43 1 +1026 43 42 0 +1027 43 42 1 +1028 42 43 0 +1029 42 43 1 +1030 42 42 0 +1031 42 42 1 +1032 40 42 1 +1033 40 42 0 +1034 40 43 1 +1035 40 43 0 +1036 41 42 1 +1037 41 42 0 +1038 41 43 1 +1039 41 43 0 +1056 35 42 0 +1057 35 42 1 +1058 35 43 0 +1059 35 43 1 +1060 34 42 0 +1061 34 42 1 +1062 34 43 0 +1063 34 43 1 +1064 32 43 1 +1065 32 43 0 +1066 32 42 1 +1067 32 42 0 +1068 33 43 1 +1069 33 43 0 +1070 33 42 1 +1071 33 42 0 +1072 36 42 0 +1073 36 42 1 +1074 36 43 0 +1075 36 43 1 +1076 37 42 0 +1077 37 42 1 +1078 37 43 0 +1079 37 43 1 +1080 39 43 1 +1081 39 43 0 +1082 39 42 1 +1083 39 42 0 +1084 38 43 1 +1085 38 43 0 +1086 38 42 1 +1087 38 42 0 +1088 44 43 0 +1089 44 43 1 +1090 44 42 0 +1091 44 42 1 +1092 45 43 0 +1093 45 43 1 +1094 45 42 0 +1095 45 42 1 +1096 47 42 1 +1097 47 42 0 +1098 47 43 1 +1099 47 43 0 +1100 46 42 1 +1101 46 42 0 +1102 46 43 1 +1103 46 43 0 +1152 43 45 0 +1153 43 45 1 +1154 43 44 0 +1155 43 44 1 +1156 42 45 0 +1157 42 45 1 +1158 42 44 0 +1159 42 44 1 +1160 40 44 1 +1161 40 44 0 +1162 40 45 1 +1163 40 45 0 +1164 41 44 1 +1165 41 44 0 +1166 41 45 1 +1167 41 45 0 +1184 35 44 0 +1185 35 44 1 +1186 35 45 0 +1187 35 45 1 +1188 34 44 0 +1189 34 44 1 +1190 34 45 0 +1191 34 45 1 +1192 32 45 1 +1193 32 45 0 +1194 32 44 1 +1195 32 44 0 +1196 33 45 1 +1197 33 45 0 +1198 33 44 1 +1199 33 44 0 +1200 36 44 0 +1201 36 44 1 +1202 36 45 0 +1203 36 45 1 +1204 37 44 0 +1205 37 44 1 +1206 37 45 0 +1207 37 45 1 +1208 39 45 1 +1209 39 45 0 +1210 39 44 1 +1211 39 44 0 +1212 38 45 1 +1213 38 45 0 +1214 38 44 1 +1215 38 44 0 +1216 44 45 0 +1217 44 45 1 +1218 44 44 0 +1219 44 44 1 +1220 45 45 0 +1221 45 45 1 +1222 45 44 0 +1223 45 44 1 +1224 47 44 1 +1225 47 44 0 +1226 47 45 1 +1227 47 45 0 +1228 46 44 1 +1229 46 44 0 +1230 46 45 1 +1231 46 45 0 +1280 43 47 0 +1281 43 47 1 +1282 43 46 0 +1283 43 46 1 +1284 42 47 0 +1285 42 47 1 +1286 42 46 0 +1287 42 46 1 +1288 40 46 1 +1289 40 46 0 +1290 40 47 1 +1291 40 47 0 +1292 41 46 1 +1293 41 46 0 +1294 41 47 1 +1295 41 47 0 +1312 35 46 0 +1313 35 46 1 +1314 35 47 0 +1315 35 47 1 +1316 34 46 0 +1317 34 46 1 +1318 34 47 0 +1319 34 47 1 +1320 32 47 1 +1321 32 47 0 +1322 32 46 1 +1323 32 46 0 +1324 33 47 1 +1325 33 47 0 +1326 33 46 1 +1327 33 46 0 +1328 36 46 0 +1329 36 46 1 +1330 36 47 0 +1331 36 47 1 +1332 37 46 0 +1333 37 46 1 +1334 37 47 0 +1335 37 47 1 +1336 39 47 1 +1337 39 47 0 +1338 39 46 1 +1339 39 46 0 +1340 38 47 1 +1341 38 47 0 +1342 38 46 1 +1343 38 46 0 +1344 44 47 0 +1345 44 47 1 +1346 44 46 0 +1347 44 46 1 +1348 45 47 0 +1349 45 47 1 +1350 45 46 0 +1351 45 46 1 +1352 47 46 1 +1353 47 46 0 +1354 47 47 1 +1355 47 47 0 +1356 46 46 1 +1357 46 46 0 +1358 46 47 1 +1359 46 47 0 +1408 43 49 0 +1409 43 49 1 +1410 43 48 0 +1411 43 48 1 +1412 42 49 0 +1413 42 49 1 +1414 42 48 0 +1415 42 48 1 +1416 40 48 1 +1417 40 48 0 +1418 40 49 1 +1419 40 49 0 +1420 41 48 1 +1421 41 48 0 +1422 41 49 1 +1423 41 49 0 +1440 35 48 0 +1441 35 48 1 +1442 35 49 0 +1443 35 49 1 +1444 34 48 0 +1445 34 48 1 +1446 34 49 0 +1447 34 49 1 +1448 32 49 1 +1449 32 49 0 +1450 32 48 1 +1451 32 48 0 +1452 33 49 1 +1453 33 49 0 +1454 33 48 1 +1455 33 48 0 +1456 36 48 0 +1457 36 48 1 +1458 36 49 0 +1459 36 49 1 +1460 37 48 0 +1461 37 48 1 +1462 37 49 0 +1463 37 49 1 +1464 39 49 1 +1465 39 49 0 +1466 39 48 1 +1467 39 48 0 +1468 38 49 1 +1469 38 49 0 +1470 38 48 1 +1471 38 48 0 +1472 44 49 0 +1473 44 49 1 +1474 44 48 0 +1475 44 48 1 +1476 45 49 0 +1477 45 49 1 +1478 45 48 0 +1479 45 48 1 +1480 47 48 1 +1481 47 48 0 +1482 47 49 1 +1483 47 49 0 +1484 46 48 1 +1485 46 48 0 +1486 46 49 1 +1487 46 49 0 +1536 43 51 0 +1537 43 51 1 +1538 43 50 0 +1539 43 50 1 +1540 42 51 0 +1541 42 51 1 +1542 42 50 0 +1543 42 50 1 +1544 40 50 1 +1545 40 50 0 +1546 40 51 1 +1547 40 51 0 +1548 41 50 1 +1549 41 50 0 +1550 41 51 1 +1551 41 51 0 +1568 35 50 0 +1569 35 50 1 +1570 35 51 0 +1571 35 51 1 +1572 34 50 0 +1573 34 50 1 +1574 34 51 0 +1575 34 51 1 +1576 32 51 1 +1577 32 51 0 +1578 32 50 1 +1579 32 50 0 +1580 33 51 1 +1581 33 51 0 +1582 33 50 1 +1583 33 50 0 +1584 36 50 0 +1585 36 50 1 +1586 36 51 0 +1587 36 51 1 +1588 37 50 0 +1589 37 50 1 +1590 37 51 0 +1591 37 51 1 +1592 39 51 1 +1593 39 51 0 +1594 39 50 1 +1595 39 50 0 +1596 38 51 1 +1597 38 51 0 +1598 38 50 1 +1599 38 50 0 +1600 44 51 0 +1601 44 51 1 +1602 44 50 0 +1603 44 50 1 +1604 45 51 0 +1605 45 51 1 +1606 45 50 0 +1607 45 50 1 +1608 47 50 1 +1609 47 50 0 +1610 47 51 1 +1611 47 51 0 +1612 46 50 1 +1613 46 50 0 +1614 46 51 1 +1615 46 51 0 +1664 43 53 0 +1665 43 53 1 +1666 43 52 0 +1667 43 52 1 +1668 42 53 0 +1669 42 53 1 +1670 42 52 0 +1671 42 52 1 +1672 40 52 1 +1673 40 52 0 +1674 40 53 1 +1675 40 53 0 +1676 41 52 1 +1677 41 52 0 +1678 41 53 1 +1679 41 53 0 +1696 35 52 0 +1697 35 52 1 +1698 35 53 0 +1699 35 53 1 +1700 34 52 0 +1701 34 52 1 +1702 34 53 0 +1703 34 53 1 +1704 32 53 1 +1705 32 53 0 +1706 32 52 1 +1707 32 52 0 +1708 33 53 1 +1709 33 53 0 +1710 33 52 1 +1711 33 52 0 +1712 36 52 0 +1713 36 52 1 +1714 36 53 0 +1715 36 53 1 +1716 37 52 0 +1717 37 52 1 +1718 37 53 0 +1719 37 53 1 +1720 39 53 1 +1721 39 53 0 +1722 39 52 1 +1723 39 52 0 +1724 38 53 1 +1725 38 53 0 +1726 38 52 1 +1727 38 52 0 +1728 44 53 0 +1729 44 53 1 +1730 44 52 0 +1731 44 52 1 +1732 45 53 0 +1733 45 53 1 +1734 45 52 0 +1735 45 52 1 +1736 47 52 1 +1737 47 52 0 +1738 47 53 1 +1739 47 53 0 +1740 46 52 1 +1741 46 52 0 +1742 46 53 1 +1743 46 53 0 +1792 43 55 0 +1793 43 55 1 +1794 43 54 0 +1795 43 54 1 +1796 42 55 0 +1797 42 55 1 +1798 42 54 0 +1799 42 54 1 +1800 40 54 1 +1801 40 54 0 +1802 40 55 1 +1803 40 55 0 +1804 41 54 1 +1805 41 54 0 +1806 41 55 1 +1807 41 55 0 +1824 35 54 0 +1825 35 54 1 +1826 35 55 0 +1827 35 55 1 +1828 34 54 0 +1829 34 54 1 +1830 34 55 0 +1831 34 55 1 +1832 32 55 1 +1833 32 55 0 +1834 32 54 1 +1835 32 54 0 +1836 33 55 1 +1837 33 55 0 +1838 33 54 1 +1839 33 54 0 +1840 36 54 0 +1841 36 54 1 +1842 36 55 0 +1843 36 55 1 +1844 37 54 0 +1845 37 54 1 +1846 37 55 0 +1847 37 55 1 +1848 39 55 1 +1849 39 55 0 +1850 39 54 1 +1851 39 54 0 +1852 38 55 1 +1853 38 55 0 +1854 38 54 1 +1855 38 54 0 +1856 44 55 0 +1857 44 55 1 +1858 44 54 0 +1859 44 54 1 +1860 45 55 0 +1861 45 55 1 +1862 45 54 0 +1863 45 54 1 +1864 47 54 1 +1865 47 54 0 +1866 47 55 1 +1867 47 55 0 +1868 46 54 1 +1869 46 54 0 +1870 46 55 1 +1871 46 55 0 +2176 43 27 0 +2177 43 27 1 +2178 43 26 0 +2179 43 26 1 +2180 42 27 0 +2181 42 27 1 +2182 42 26 0 +2183 42 26 1 +2184 40 26 1 +2185 40 26 0 +2186 40 27 1 +2187 40 27 0 +2188 41 26 1 +2189 41 26 0 +2190 41 27 1 +2191 41 27 0 +2208 35 26 0 +2209 35 26 1 +2210 35 27 0 +2211 35 27 1 +2212 34 26 0 +2213 34 26 1 +2214 34 27 0 +2215 34 27 1 +2216 32 27 1 +2217 32 27 0 +2218 32 26 1 +2219 32 26 0 +2220 33 27 1 +2221 33 27 0 +2222 33 26 1 +2223 33 26 0 +2224 36 26 0 +2225 36 26 1 +2226 36 27 0 +2227 36 27 1 +2228 37 26 0 +2229 37 26 1 +2230 37 27 0 +2231 37 27 1 +2232 39 27 1 +2233 39 27 0 +2234 39 26 1 +2235 39 26 0 +2236 38 27 1 +2237 38 27 0 +2238 38 26 1 +2239 38 26 0 +2240 44 27 0 +2241 44 27 1 +2242 44 26 0 +2243 44 26 1 +2244 45 27 0 +2245 45 27 1 +2246 45 26 0 +2247 45 26 1 +2248 47 26 1 +2249 47 26 0 +2250 47 27 1 +2251 47 27 0 +2252 46 26 1 +2253 46 26 0 +2254 46 27 1 +2255 46 27 0 +2304 43 25 0 +2305 43 25 1 +2306 43 24 0 +2307 43 24 1 +2308 42 25 0 +2309 42 25 1 +2310 42 24 0 +2311 42 24 1 +2312 40 24 1 +2313 40 24 0 +2314 40 25 1 +2315 40 25 0 +2316 41 24 1 +2317 41 24 0 +2318 41 25 1 +2319 41 25 0 +2336 35 24 0 +2337 35 24 1 +2338 35 25 0 +2339 35 25 1 +2340 34 24 0 +2341 34 24 1 +2342 34 25 0 +2343 34 25 1 +2344 32 25 1 +2345 32 25 0 +2346 32 24 1 +2347 32 24 0 +2348 33 25 1 +2349 33 25 0 +2350 33 24 1 +2351 33 24 0 +2352 36 24 0 +2353 36 24 1 +2354 36 25 0 +2355 36 25 1 +2356 37 24 0 +2357 37 24 1 +2358 37 25 0 +2359 37 25 1 +2360 39 25 1 +2361 39 25 0 +2362 39 24 1 +2363 39 24 0 +2364 38 25 1 +2365 38 25 0 +2366 38 24 1 +2367 38 24 0 +2368 44 25 0 +2369 44 25 1 +2370 44 24 0 +2371 44 24 1 +2372 45 25 0 +2373 45 25 1 +2374 45 24 0 +2375 45 24 1 +2376 47 24 1 +2377 47 24 0 +2378 47 25 1 +2379 47 25 0 +2380 46 24 1 +2381 46 24 0 +2382 46 25 1 +2383 46 25 0 +2432 43 23 0 +2433 43 23 1 +2434 43 22 0 +2435 43 22 1 +2436 42 23 0 +2437 42 23 1 +2438 42 22 0 +2439 42 22 1 +2440 40 22 1 +2441 40 22 0 +2442 40 23 1 +2443 40 23 0 +2444 41 22 1 +2445 41 22 0 +2446 41 23 1 +2447 41 23 0 +2464 35 22 0 +2465 35 22 1 +2466 35 23 0 +2467 35 23 1 +2468 34 22 0 +2469 34 22 1 +2470 34 23 0 +2471 34 23 1 +2472 32 23 1 +2473 32 23 0 +2474 32 22 1 +2475 32 22 0 +2476 33 23 1 +2477 33 23 0 +2478 33 22 1 +2479 33 22 0 +2480 36 22 0 +2481 36 22 1 +2482 36 23 0 +2483 36 23 1 +2484 37 22 0 +2485 37 22 1 +2486 37 23 0 +2487 37 23 1 +2488 39 23 1 +2489 39 23 0 +2490 39 22 1 +2491 39 22 0 +2492 38 23 1 +2493 38 23 0 +2494 38 22 1 +2495 38 22 0 +2496 44 23 0 +2497 44 23 1 +2498 44 22 0 +2499 44 22 1 +2500 45 23 0 +2501 45 23 1 +2502 45 22 0 +2503 45 22 1 +2504 47 22 1 +2505 47 22 0 +2506 47 23 1 +2507 47 23 0 +2508 46 22 1 +2509 46 22 0 +2510 46 23 1 +2511 46 23 0 +2560 43 21 0 +2561 43 21 1 +2562 43 20 0 +2563 43 20 1 +2564 42 21 0 +2565 42 21 1 +2566 42 20 0 +2567 42 20 1 +2568 40 20 1 +2569 40 20 0 +2570 40 21 1 +2571 40 21 0 +2572 41 20 1 +2573 41 20 0 +2574 41 21 1 +2575 41 21 0 +2592 35 20 0 +2593 35 20 1 +2594 35 21 0 +2595 35 21 1 +2596 34 20 0 +2597 34 20 1 +2598 34 21 0 +2599 34 21 1 +2600 32 21 1 +2601 32 21 0 +2602 32 20 1 +2603 32 20 0 +2604 33 21 1 +2605 33 21 0 +2606 33 20 1 +2607 33 20 0 +2608 36 20 0 +2609 36 20 1 +2610 36 21 0 +2611 36 21 1 +2612 37 20 0 +2613 37 20 1 +2614 37 21 0 +2615 37 21 1 +2616 39 21 1 +2617 39 21 0 +2618 39 20 1 +2619 39 20 0 +2620 38 21 1 +2621 38 21 0 +2622 38 20 1 +2623 38 20 0 +2624 44 21 0 +2625 44 21 1 +2626 44 20 0 +2627 44 20 1 +2628 45 21 0 +2629 45 21 1 +2630 45 20 0 +2631 45 20 1 +2632 47 20 1 +2633 47 20 0 +2634 47 21 1 +2635 47 21 0 +2636 46 20 1 +2637 46 20 0 +2638 46 21 1 +2639 46 21 0 +2688 43 19 0 +2689 43 19 1 +2690 43 18 0 +2691 43 18 1 +2692 42 19 0 +2693 42 19 1 +2694 42 18 0 +2695 42 18 1 +2696 40 18 1 +2697 40 18 0 +2698 40 19 1 +2699 40 19 0 +2700 41 18 1 +2701 41 18 0 +2702 41 19 1 +2703 41 19 0 +2720 35 18 0 +2721 35 18 1 +2722 35 19 0 +2723 35 19 1 +2724 34 18 0 +2725 34 18 1 +2726 34 19 0 +2727 34 19 1 +2728 32 19 1 +2729 32 19 0 +2730 32 18 1 +2731 32 18 0 +2732 33 19 1 +2733 33 19 0 +2734 33 18 1 +2735 33 18 0 +2736 36 18 0 +2737 36 18 1 +2738 36 19 0 +2739 36 19 1 +2740 37 18 0 +2741 37 18 1 +2742 37 19 0 +2743 37 19 1 +2744 39 19 1 +2745 39 19 0 +2746 39 18 1 +2747 39 18 0 +2748 38 19 1 +2749 38 19 0 +2750 38 18 1 +2751 38 18 0 +2752 44 19 0 +2753 44 19 1 +2754 44 18 0 +2755 44 18 1 +2756 45 19 0 +2757 45 19 1 +2758 45 18 0 +2759 45 18 1 +2760 47 18 1 +2761 47 18 0 +2762 47 19 1 +2763 47 19 0 +2764 46 18 1 +2765 46 18 0 +2766 46 19 1 +2767 46 19 0 +2816 43 17 0 +2817 43 17 1 +2818 43 16 0 +2819 43 16 1 +2820 42 17 0 +2821 42 17 1 +2822 42 16 0 +2823 42 16 1 +2824 40 16 1 +2825 40 16 0 +2826 40 17 1 +2827 40 17 0 +2828 41 16 1 +2829 41 16 0 +2830 41 17 1 +2831 41 17 0 +2848 35 16 0 +2849 35 16 1 +2850 35 17 0 +2851 35 17 1 +2852 34 16 0 +2853 34 16 1 +2854 34 17 0 +2855 34 17 1 +2856 32 17 1 +2857 32 17 0 +2858 32 16 1 +2859 32 16 0 +2860 33 17 1 +2861 33 17 0 +2862 33 16 1 +2863 33 16 0 +2864 36 16 0 +2865 36 16 1 +2866 36 17 0 +2867 36 17 1 +2868 37 16 0 +2869 37 16 1 +2870 37 17 0 +2871 37 17 1 +2872 39 17 1 +2873 39 17 0 +2874 39 16 1 +2875 39 16 0 +2876 38 17 1 +2877 38 17 0 +2878 38 16 1 +2879 38 16 0 +2880 44 17 0 +2881 44 17 1 +2882 44 16 0 +2883 44 16 1 +2884 45 17 0 +2885 45 17 1 +2886 45 16 0 +2887 45 16 1 +2888 47 16 1 +2889 47 16 0 +2890 47 17 1 +2891 47 17 0 +2892 46 16 1 +2893 46 16 0 +2894 46 17 1 +2895 46 17 0 +2944 43 15 0 +2945 43 15 1 +2946 43 14 0 +2947 43 14 1 +2948 42 15 0 +2949 42 15 1 +2950 42 14 0 +2951 42 14 1 +2952 40 14 1 +2953 40 14 0 +2954 40 15 1 +2955 40 15 0 +2956 41 14 1 +2957 41 14 0 +2958 41 15 1 +2959 41 15 0 +2976 35 14 0 +2977 35 14 1 +2978 35 15 0 +2979 35 15 1 +2980 34 14 0 +2981 34 14 1 +2982 34 15 0 +2983 34 15 1 +2984 32 15 1 +2985 32 15 0 +2986 32 14 1 +2987 32 14 0 +2988 33 15 1 +2989 33 15 0 +2990 33 14 1 +2991 33 14 0 +2992 36 14 0 +2993 36 14 1 +2994 36 15 0 +2995 36 15 1 +2996 37 14 0 +2997 37 14 1 +2998 37 15 0 +2999 37 15 1 +3000 39 15 1 +3001 39 15 0 +3002 39 14 1 +3003 39 14 0 +3004 38 15 1 +3005 38 15 0 +3006 38 14 1 +3007 38 14 0 +3008 44 15 0 +3009 44 15 1 +3010 44 14 0 +3011 44 14 1 +3012 45 15 0 +3013 45 15 1 +3014 45 14 0 +3015 45 14 1 +3016 47 14 1 +3017 47 14 0 +3018 47 15 1 +3019 47 15 0 +3020 46 14 1 +3021 46 14 0 +3022 46 15 1 +3023 46 15 0 +3072 43 13 0 +3073 43 13 1 +3074 43 12 0 +3075 43 12 1 +3076 42 13 0 +3077 42 13 1 +3078 42 12 0 +3079 42 12 1 +3080 40 12 1 +3081 40 12 0 +3082 40 13 1 +3083 40 13 0 +3084 41 12 1 +3085 41 12 0 +3086 41 13 1 +3087 41 13 0 +3104 35 12 0 +3105 35 12 1 +3106 35 13 0 +3107 35 13 1 +3108 34 12 0 +3109 34 12 1 +3110 34 13 0 +3111 34 13 1 +3112 32 13 1 +3113 32 13 0 +3114 32 12 1 +3115 32 12 0 +3116 33 13 1 +3117 33 13 0 +3118 33 12 1 +3119 33 12 0 +3120 36 12 0 +3121 36 12 1 +3122 36 13 0 +3123 36 13 1 +3124 37 12 0 +3125 37 12 1 +3126 37 13 0 +3127 37 13 1 +3128 39 13 1 +3129 39 13 0 +3130 39 12 1 +3131 39 12 0 +3132 38 13 1 +3133 38 13 0 +3134 38 12 1 +3135 38 12 0 +3136 44 13 0 +3137 44 13 1 +3138 44 12 0 +3139 44 12 1 +3140 45 13 0 +3141 45 13 1 +3142 45 12 0 +3143 45 12 1 +3144 47 12 1 +3145 47 12 0 +3146 47 13 1 +3147 47 13 0 +3148 46 12 1 +3149 46 12 0 +3150 46 13 1 +3151 46 13 0 +3200 43 11 0 +3201 43 11 1 +3202 43 10 0 +3203 43 10 1 +3204 42 11 0 +3205 42 11 1 +3206 42 10 0 +3207 42 10 1 +3208 40 10 1 +3209 40 10 0 +3210 40 11 1 +3211 40 11 0 +3212 41 10 1 +3213 41 10 0 +3214 41 11 1 +3215 41 11 0 +3232 35 10 0 +3233 35 10 1 +3234 35 11 0 +3235 35 11 1 +3236 34 10 0 +3237 34 10 1 +3238 34 11 0 +3239 34 11 1 +3240 32 11 1 +3241 32 11 0 +3242 32 10 1 +3243 32 10 0 +3244 33 11 1 +3245 33 11 0 +3246 33 10 1 +3247 33 10 0 +3248 36 10 0 +3249 36 10 1 +3250 36 11 0 +3251 36 11 1 +3252 37 10 0 +3253 37 10 1 +3254 37 11 0 +3255 37 11 1 +3256 39 11 1 +3257 39 11 0 +3258 39 10 1 +3259 39 10 0 +3260 38 11 1 +3261 38 11 0 +3262 38 10 1 +3263 38 10 0 +3264 44 11 0 +3265 44 11 1 +3266 44 10 0 +3267 44 10 1 +3268 45 11 0 +3269 45 11 1 +3270 45 10 0 +3271 45 10 1 +3272 47 10 1 +3273 47 10 0 +3274 47 11 1 +3275 47 11 0 +3276 46 10 1 +3277 46 10 0 +3278 46 11 1 +3279 46 11 0 +3328 43 9 0 +3329 43 9 1 +3330 43 8 0 +3331 43 8 1 +3332 42 9 0 +3333 42 9 1 +3334 42 8 0 +3335 42 8 1 +3336 40 8 1 +3337 40 8 0 +3338 40 9 1 +3339 40 9 0 +3340 41 8 1 +3341 41 8 0 +3342 41 9 1 +3343 41 9 0 +3360 35 8 0 +3361 35 8 1 +3362 35 9 0 +3363 35 9 1 +3364 34 8 0 +3365 34 8 1 +3366 34 9 0 +3367 34 9 1 +3368 32 9 1 +3369 32 9 0 +3370 32 8 1 +3371 32 8 0 +3372 33 9 1 +3373 33 9 0 +3374 33 8 1 +3375 33 8 0 +3376 36 8 0 +3377 36 8 1 +3378 36 9 0 +3379 36 9 1 +3380 37 8 0 +3381 37 8 1 +3382 37 9 0 +3383 37 9 1 +3384 39 9 1 +3385 39 9 0 +3386 39 8 1 +3387 39 8 0 +3388 38 9 1 +3389 38 9 0 +3390 38 8 1 +3391 38 8 0 +3392 44 9 0 +3393 44 9 1 +3394 44 8 0 +3395 44 8 1 +3396 45 9 0 +3397 45 9 1 +3398 45 8 0 +3399 45 8 1 +3400 47 8 1 +3401 47 8 0 +3402 47 9 1 +3403 47 9 0 +3404 46 8 1 +3405 46 8 0 +3406 46 9 1 +3407 46 9 0 +3456 43 7 0 +3457 43 7 1 +3458 43 6 0 +3459 43 6 1 +3460 42 7 0 +3461 42 7 1 +3462 42 6 0 +3463 42 6 1 +3464 40 6 1 +3465 40 6 0 +3466 40 7 1 +3467 40 7 0 +3468 41 6 1 +3469 41 6 0 +3470 41 7 1 +3471 41 7 0 +3488 35 6 0 +3489 35 6 1 +3490 35 7 0 +3491 35 7 1 +3492 34 6 0 +3493 34 6 1 +3494 34 7 0 +3495 34 7 1 +3496 32 7 1 +3497 32 7 0 +3498 32 6 1 +3499 32 6 0 +3500 33 7 1 +3501 33 7 0 +3502 33 6 1 +3503 33 6 0 +3504 36 6 0 +3505 36 6 1 +3506 36 7 0 +3507 36 7 1 +3508 37 6 0 +3509 37 6 1 +3510 37 7 0 +3511 37 7 1 +3512 39 7 1 +3513 39 7 0 +3514 39 6 1 +3515 39 6 0 +3516 38 7 1 +3517 38 7 0 +3518 38 6 1 +3519 38 6 0 +3520 44 7 0 +3521 44 7 1 +3522 44 6 0 +3523 44 6 1 +3524 45 7 0 +3525 45 7 1 +3526 45 6 0 +3527 45 6 1 +3528 47 6 1 +3529 47 6 0 +3530 47 7 1 +3531 47 7 0 +3532 46 6 1 +3533 46 6 0 +3534 46 7 1 +3535 46 7 0 +3584 43 5 0 +3585 43 5 1 +3586 43 4 0 +3587 43 4 1 +3588 42 5 0 +3589 42 5 1 +3590 42 4 0 +3591 42 4 1 +3592 40 4 1 +3593 40 4 0 +3594 40 5 1 +3595 40 5 0 +3596 41 4 1 +3597 41 4 0 +3598 41 5 1 +3599 41 5 0 +3616 35 4 0 +3617 35 4 1 +3618 35 5 0 +3619 35 5 1 +3620 34 4 0 +3621 34 4 1 +3622 34 5 0 +3623 34 5 1 +3624 32 5 1 +3625 32 5 0 +3626 32 4 1 +3627 32 4 0 +3628 33 5 1 +3629 33 5 0 +3630 33 4 1 +3631 33 4 0 +3632 36 4 0 +3633 36 4 1 +3634 36 5 0 +3635 36 5 1 +3636 37 4 0 +3637 37 4 1 +3638 37 5 0 +3639 37 5 1 +3640 39 5 1 +3641 39 5 0 +3642 39 4 1 +3643 39 4 0 +3644 38 5 1 +3645 38 5 0 +3646 38 4 1 +3647 38 4 0 +3648 44 5 0 +3649 44 5 1 +3650 44 4 0 +3651 44 4 1 +3652 45 5 0 +3653 45 5 1 +3654 45 4 0 +3655 45 4 1 +3656 47 4 1 +3657 47 4 0 +3658 47 5 1 +3659 47 5 0 +3660 46 4 1 +3661 46 4 0 +3662 46 5 1 +3663 46 5 0 +3712 43 3 0 +3713 43 3 1 +3714 43 2 0 +3715 43 2 1 +3716 42 3 0 +3717 42 3 1 +3718 42 2 0 +3719 42 2 1 +3720 40 2 1 +3721 40 2 0 +3722 40 3 1 +3723 40 3 0 +3724 41 2 1 +3725 41 2 0 +3726 41 3 1 +3727 41 3 0 +3744 35 2 0 +3745 35 2 1 +3746 35 3 0 +3747 35 3 1 +3748 34 2 0 +3749 34 2 1 +3750 34 3 0 +3751 34 3 1 +3752 32 3 1 +3753 32 3 0 +3754 32 2 1 +3755 32 2 0 +3756 33 3 1 +3757 33 3 0 +3758 33 2 1 +3759 33 2 0 +3760 36 2 0 +3761 36 2 1 +3762 36 3 0 +3763 36 3 1 +3764 37 2 0 +3765 37 2 1 +3766 37 3 0 +3767 37 3 1 +3768 39 3 1 +3769 39 3 0 +3770 39 2 1 +3771 39 2 0 +3772 38 3 1 +3773 38 3 0 +3774 38 2 1 +3775 38 2 0 +3776 44 3 0 +3777 44 3 1 +3778 44 2 0 +3779 44 2 1 +3780 45 3 0 +3781 45 3 1 +3782 45 2 0 +3783 45 2 1 +3784 47 2 1 +3785 47 2 0 +3786 47 3 1 +3787 47 3 0 +3788 46 2 1 +3789 46 2 0 +3790 46 3 1 +3791 46 3 0 +3840 43 1 0 +3841 43 1 1 +3842 43 0 0 +3843 43 0 1 +3844 42 1 0 +3845 42 1 1 +3846 42 0 0 +3847 42 0 1 +3848 40 0 1 +3849 40 0 0 +3850 40 1 1 +3851 40 1 0 +3852 41 0 1 +3853 41 0 0 +3854 41 1 1 +3855 41 1 0 +3872 35 0 0 +3873 35 0 1 +3874 35 1 0 +3875 35 1 1 +3876 34 0 0 +3877 34 0 1 +3878 34 1 0 +3879 34 1 1 +3880 32 1 1 +3881 32 1 0 +3882 32 0 1 +3883 32 0 0 +3884 33 1 1 +3885 33 1 0 +3886 33 0 1 +3887 33 0 0 +3888 36 0 0 +3889 36 0 1 +3890 36 1 0 +3891 36 1 1 +3892 37 0 0 +3893 37 0 1 +3894 37 1 0 +3895 37 1 1 +3896 39 1 1 +3897 39 1 0 +3898 39 0 1 +3899 39 0 0 +3900 38 1 1 +3901 38 1 0 +3902 38 0 1 +3903 38 0 0 +3904 44 1 0 +3905 44 1 1 +3906 44 0 0 +3907 44 0 1 +3908 45 1 0 +3909 45 1 1 +3910 45 0 0 +3911 45 0 1 +3912 47 0 1 +3913 47 0 0 +3914 47 1 1 +3915 47 1 0 +3916 46 0 1 +3917 46 0 0 +3918 46 1 1 +3919 46 1 0 diff --git a/Detectors/PHOS/base/files/Mod2RCU3.data b/Detectors/PHOS/base/files/Mod2RCU3.data new file mode 100644 index 0000000000000..43bd76fc42791 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod2RCU3.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 3 0 2 + 1 3 1 2 + 2 3 2 2 + 3 3 3 2 + 4 3 4 2 + 5 3 5 2 + 6 3 6 2 + 7 3 7 2 + 8 3 8 2 + 9 3 9 2 + 10 3 10 2 + 11 3 11 2 + 12 3 12 2 + 13 3 13 2 + 14 3 14 2 + 15 3 15 2 + 16 3 16 2 + 17 3 17 2 + 18 3 18 2 + 19 3 19 2 + 20 3 20 2 + 21 3 21 2 + 22 3 22 2 + 23 3 23 2 + 24 3 24 2 + 25 3 25 2 + 26 3 26 2 + 27 3 27 2 + 28 3 28 2 + 29 3 29 2 + 30 3 30 2 + 31 3 31 2 + 32 3 32 2 + 33 3 33 2 + 34 3 34 2 + 35 3 35 2 + 36 3 36 2 + 37 3 37 2 + 38 3 38 2 + 39 3 39 2 + 40 3 40 2 + 41 3 41 2 + 42 3 42 2 + 43 3 43 2 + 44 3 44 2 + 45 3 45 2 + 46 3 46 2 + 47 3 47 2 + 48 3 48 2 + 49 3 49 2 + 50 3 50 2 + 51 3 51 2 + 52 3 52 2 + 53 3 53 2 + 54 3 54 2 + 55 3 55 2 + 56 3 56 2 + 57 3 57 2 + 58 3 58 2 + 59 3 59 2 + 60 3 60 2 + 61 3 61 2 + 62 3 62 2 + 63 3 63 2 + 64 3 64 2 + 65 3 65 2 + 66 3 66 2 + 67 3 67 2 + 68 3 68 2 + 69 3 69 2 + 70 3 70 2 + 71 3 71 2 + 72 3 72 2 + 73 3 73 2 + 74 3 74 2 + 75 3 75 2 + 76 3 76 2 + 77 3 77 2 + 78 3 78 2 + 79 3 79 2 + 80 3 80 2 + 81 3 81 2 + 82 3 82 2 + 83 3 83 2 + 84 3 84 2 + 85 3 85 2 + 86 3 86 2 + 87 3 87 2 + 88 3 88 2 + 89 3 89 2 + 90 3 90 2 + 91 3 91 2 + 92 3 92 2 + 93 3 93 2 + 94 3 94 2 + 95 3 95 2 + 96 3 96 2 + 97 3 97 2 + 98 3 98 2 + 99 3 99 2 + 100 3 100 2 + 101 3 101 2 + 102 3 102 2 + 103 3 103 2 + 104 3 104 2 + 105 3 105 2 + 106 3 106 2 + 107 3 107 2 + 108 3 108 2 + 109 3 109 2 + 110 3 110 2 + 111 3 111 2 + 112 3 112 2 + 113 3 113 2 + 114 3 114 2 + 115 3 115 2 + 116 3 116 2 + 117 3 117 2 + 118 3 118 2 + 119 3 119 2 + 120 3 120 2 + 121 3 121 2 + 122 3 122 2 + 123 3 123 2 + 124 3 124 2 + 125 3 125 2 + 126 3 126 2 + 127 3 127 2 +2048 3 2048 2 +2049 3 2049 2 +2050 3 2050 2 +2051 3 2051 2 +2052 3 2052 2 +2053 3 2053 2 +2054 3 2054 2 +2055 3 2055 2 +2056 3 2056 2 +2057 3 2057 2 +2058 3 2058 2 +2059 3 2059 2 +2060 3 2060 2 +2061 3 2061 2 +2062 3 2062 2 +2063 3 2063 2 +2064 3 2064 2 +2065 3 2065 2 +2066 3 2066 2 +2067 3 2067 2 +2068 3 2068 2 +2069 3 2069 2 +2070 3 2070 2 +2071 3 2071 2 +2072 3 2072 2 +2073 3 2073 2 +2074 3 2074 2 +2075 3 2075 2 +2076 3 2076 2 +2077 3 2077 2 +2078 3 2078 2 +2079 3 2079 2 +2080 3 2080 2 +2081 3 2081 2 +2082 3 2082 2 +2083 3 2083 2 +2084 3 2084 2 +2085 3 2085 2 +2086 3 2086 2 +2087 3 2087 2 +2088 3 2088 2 +2089 3 2089 2 +2090 3 2090 2 +2091 3 2091 2 +2092 3 2092 2 +2093 3 2093 2 +2094 3 2094 2 +2095 3 2095 2 +2096 3 2096 2 +2097 3 2097 2 +2098 3 2098 2 +2099 3 2099 2 +2100 3 2100 2 +2101 3 2101 2 +2102 3 2102 2 +2103 3 2103 2 +2104 3 2104 2 +2105 3 2105 2 +2106 3 2106 2 +2107 3 2107 2 +2108 3 2108 2 +2109 3 2109 2 +2110 3 2110 2 +2111 3 2111 2 +2112 3 2112 2 +2113 3 2113 2 +2114 3 2114 2 +2115 3 2115 2 +2116 3 2116 2 +2117 3 2117 2 +2118 3 2118 2 +2119 3 2119 2 +2120 3 2120 2 +2121 3 2121 2 +2122 3 2122 2 +2123 3 2123 2 +2124 3 2124 2 +2125 3 2125 2 +2126 3 2126 2 +2127 3 2127 2 +2128 3 2128 2 +2129 3 2129 2 +2130 3 2130 2 +2131 3 2131 2 +2132 3 2132 2 +2133 3 2133 2 +2134 3 2134 2 +2135 3 2135 2 +2136 3 2136 2 +2137 3 2137 2 +2138 3 2138 2 +2139 3 2139 2 +2140 3 2140 2 +2141 3 2141 2 +2142 3 2142 2 +2143 3 2143 2 +2144 3 2144 2 +2145 3 2145 2 +2146 3 2146 2 +2147 3 2147 2 +2148 3 2148 2 +2149 3 2149 2 +2150 3 2150 2 +2151 3 2151 2 +2152 3 2152 2 +2153 3 2153 2 +2154 3 2154 2 +2155 3 2155 2 +2156 3 2156 2 +2157 3 2157 2 +2158 3 2158 2 +2159 3 2159 2 +2160 3 2160 2 +2161 3 2161 2 +2162 3 2162 2 +2163 3 2163 2 +2164 3 2164 2 +2165 3 2165 2 +2166 3 2166 2 +2167 3 2167 2 +2168 3 2168 2 +2169 3 2169 2 +2170 3 2170 2 +2171 3 2171 2 +2172 3 2172 2 +2173 3 2173 2 +2174 3 2174 2 +2175 3 2175 2 + 128 59 29 0 + 129 59 29 1 + 130 59 28 0 + 131 59 28 1 + 132 58 29 0 + 133 58 29 1 + 134 58 28 0 + 135 58 28 1 + 136 56 28 1 + 137 56 28 0 + 138 56 29 1 + 139 56 29 0 + 140 57 28 1 + 141 57 28 0 + 142 57 29 1 + 143 57 29 0 + 160 51 28 0 + 161 51 28 1 + 162 51 29 0 + 163 51 29 1 + 164 50 28 0 + 165 50 28 1 + 166 50 29 0 + 167 50 29 1 + 168 48 29 1 + 169 48 29 0 + 170 48 28 1 + 171 48 28 0 + 172 49 29 1 + 173 49 29 0 + 174 49 28 1 + 175 49 28 0 + 176 52 28 0 + 177 52 28 1 + 178 52 29 0 + 179 52 29 1 + 180 53 28 0 + 181 53 28 1 + 182 53 29 0 + 183 53 29 1 + 184 55 29 1 + 185 55 29 0 + 186 55 28 1 + 187 55 28 0 + 188 54 29 1 + 189 54 29 0 + 190 54 28 1 + 191 54 28 0 + 192 60 29 0 + 193 60 29 1 + 194 60 28 0 + 195 60 28 1 + 196 61 29 0 + 197 61 29 1 + 198 61 28 0 + 199 61 28 1 + 200 63 28 1 + 201 63 28 0 + 202 63 29 1 + 203 63 29 0 + 204 62 28 1 + 205 62 28 0 + 206 62 29 1 + 207 62 29 0 + 256 59 31 0 + 257 59 31 1 + 258 59 30 0 + 259 59 30 1 + 260 58 31 0 + 261 58 31 1 + 262 58 30 0 + 263 58 30 1 + 264 56 30 1 + 265 56 30 0 + 266 56 31 1 + 267 56 31 0 + 268 57 30 1 + 269 57 30 0 + 270 57 31 1 + 271 57 31 0 + 288 51 30 0 + 289 51 30 1 + 290 51 31 0 + 291 51 31 1 + 292 50 30 0 + 293 50 30 1 + 294 50 31 0 + 295 50 31 1 + 296 48 31 1 + 297 48 31 0 + 298 48 30 1 + 299 48 30 0 + 300 49 31 1 + 301 49 31 0 + 302 49 30 1 + 303 49 30 0 + 304 52 30 0 + 305 52 30 1 + 306 52 31 0 + 307 52 31 1 + 308 53 30 0 + 309 53 30 1 + 310 53 31 0 + 311 53 31 1 + 312 55 31 1 + 313 55 31 0 + 314 55 30 1 + 315 55 30 0 + 316 54 31 1 + 317 54 31 0 + 318 54 30 1 + 319 54 30 0 + 320 60 31 0 + 321 60 31 1 + 322 60 30 0 + 323 60 30 1 + 324 61 31 0 + 325 61 31 1 + 326 61 30 0 + 327 61 30 1 + 328 63 30 1 + 329 63 30 0 + 330 63 31 1 + 331 63 31 0 + 332 62 30 1 + 333 62 30 0 + 334 62 31 1 + 335 62 31 0 + 384 59 33 0 + 385 59 33 1 + 386 59 32 0 + 387 59 32 1 + 388 58 33 0 + 389 58 33 1 + 390 58 32 0 + 391 58 32 1 + 392 56 32 1 + 393 56 32 0 + 394 56 33 1 + 395 56 33 0 + 396 57 32 1 + 397 57 32 0 + 398 57 33 1 + 399 57 33 0 + 416 51 32 0 + 417 51 32 1 + 418 51 33 0 + 419 51 33 1 + 420 50 32 0 + 421 50 32 1 + 422 50 33 0 + 423 50 33 1 + 424 48 33 1 + 425 48 33 0 + 426 48 32 1 + 427 48 32 0 + 428 49 33 1 + 429 49 33 0 + 430 49 32 1 + 431 49 32 0 + 432 52 32 0 + 433 52 32 1 + 434 52 33 0 + 435 52 33 1 + 436 53 32 0 + 437 53 32 1 + 438 53 33 0 + 439 53 33 1 + 440 55 33 1 + 441 55 33 0 + 442 55 32 1 + 443 55 32 0 + 444 54 33 1 + 445 54 33 0 + 446 54 32 1 + 447 54 32 0 + 448 60 33 0 + 449 60 33 1 + 450 60 32 0 + 451 60 32 1 + 452 61 33 0 + 453 61 33 1 + 454 61 32 0 + 455 61 32 1 + 456 63 32 1 + 457 63 32 0 + 458 63 33 1 + 459 63 33 0 + 460 62 32 1 + 461 62 32 0 + 462 62 33 1 + 463 62 33 0 + 512 59 35 0 + 513 59 35 1 + 514 59 34 0 + 515 59 34 1 + 516 58 35 0 + 517 58 35 1 + 518 58 34 0 + 519 58 34 1 + 520 56 34 1 + 521 56 34 0 + 522 56 35 1 + 523 56 35 0 + 524 57 34 1 + 525 57 34 0 + 526 57 35 1 + 527 57 35 0 + 544 51 34 0 + 545 51 34 1 + 546 51 35 0 + 547 51 35 1 + 548 50 34 0 + 549 50 34 1 + 550 50 35 0 + 551 50 35 1 + 552 48 35 1 + 553 48 35 0 + 554 48 34 1 + 555 48 34 0 + 556 49 35 1 + 557 49 35 0 + 558 49 34 1 + 559 49 34 0 + 560 52 34 0 + 561 52 34 1 + 562 52 35 0 + 563 52 35 1 + 564 53 34 0 + 565 53 34 1 + 566 53 35 0 + 567 53 35 1 + 568 55 35 1 + 569 55 35 0 + 570 55 34 1 + 571 55 34 0 + 572 54 35 1 + 573 54 35 0 + 574 54 34 1 + 575 54 34 0 + 576 60 35 0 + 577 60 35 1 + 578 60 34 0 + 579 60 34 1 + 580 61 35 0 + 581 61 35 1 + 582 61 34 0 + 583 61 34 1 + 584 63 34 1 + 585 63 34 0 + 586 63 35 1 + 587 63 35 0 + 588 62 34 1 + 589 62 34 0 + 590 62 35 1 + 591 62 35 0 + 640 59 37 0 + 641 59 37 1 + 642 59 36 0 + 643 59 36 1 + 644 58 37 0 + 645 58 37 1 + 646 58 36 0 + 647 58 36 1 + 648 56 36 1 + 649 56 36 0 + 650 56 37 1 + 651 56 37 0 + 652 57 36 1 + 653 57 36 0 + 654 57 37 1 + 655 57 37 0 + 672 51 36 0 + 673 51 36 1 + 674 51 37 0 + 675 51 37 1 + 676 50 36 0 + 677 50 36 1 + 678 50 37 0 + 679 50 37 1 + 680 48 37 1 + 681 48 37 0 + 682 48 36 1 + 683 48 36 0 + 684 49 37 1 + 685 49 37 0 + 686 49 36 1 + 687 49 36 0 + 688 52 36 0 + 689 52 36 1 + 690 52 37 0 + 691 52 37 1 + 692 53 36 0 + 693 53 36 1 + 694 53 37 0 + 695 53 37 1 + 696 55 37 1 + 697 55 37 0 + 698 55 36 1 + 699 55 36 0 + 700 54 37 1 + 701 54 37 0 + 702 54 36 1 + 703 54 36 0 + 704 60 37 0 + 705 60 37 1 + 706 60 36 0 + 707 60 36 1 + 708 61 37 0 + 709 61 37 1 + 710 61 36 0 + 711 61 36 1 + 712 63 36 1 + 713 63 36 0 + 714 63 37 1 + 715 63 37 0 + 716 62 36 1 + 717 62 36 0 + 718 62 37 1 + 719 62 37 0 + 768 59 39 0 + 769 59 39 1 + 770 59 38 0 + 771 59 38 1 + 772 58 39 0 + 773 58 39 1 + 774 58 38 0 + 775 58 38 1 + 776 56 38 1 + 777 56 38 0 + 778 56 39 1 + 779 56 39 0 + 780 57 38 1 + 781 57 38 0 + 782 57 39 1 + 783 57 39 0 + 800 51 38 0 + 801 51 38 1 + 802 51 39 0 + 803 51 39 1 + 804 50 38 0 + 805 50 38 1 + 806 50 39 0 + 807 50 39 1 + 808 48 39 1 + 809 48 39 0 + 810 48 38 1 + 811 48 38 0 + 812 49 39 1 + 813 49 39 0 + 814 49 38 1 + 815 49 38 0 + 816 52 38 0 + 817 52 38 1 + 818 52 39 0 + 819 52 39 1 + 820 53 38 0 + 821 53 38 1 + 822 53 39 0 + 823 53 39 1 + 824 55 39 1 + 825 55 39 0 + 826 55 38 1 + 827 55 38 0 + 828 54 39 1 + 829 54 39 0 + 830 54 38 1 + 831 54 38 0 + 832 60 39 0 + 833 60 39 1 + 834 60 38 0 + 835 60 38 1 + 836 61 39 0 + 837 61 39 1 + 838 61 38 0 + 839 61 38 1 + 840 63 38 1 + 841 63 38 0 + 842 63 39 1 + 843 63 39 0 + 844 62 38 1 + 845 62 38 0 + 846 62 39 1 + 847 62 39 0 + 896 59 41 0 + 897 59 41 1 + 898 59 40 0 + 899 59 40 1 + 900 58 41 0 + 901 58 41 1 + 902 58 40 0 + 903 58 40 1 + 904 56 40 1 + 905 56 40 0 + 906 56 41 1 + 907 56 41 0 + 908 57 40 1 + 909 57 40 0 + 910 57 41 1 + 911 57 41 0 + 928 51 40 0 + 929 51 40 1 + 930 51 41 0 + 931 51 41 1 + 932 50 40 0 + 933 50 40 1 + 934 50 41 0 + 935 50 41 1 + 936 48 41 1 + 937 48 41 0 + 938 48 40 1 + 939 48 40 0 + 940 49 41 1 + 941 49 41 0 + 942 49 40 1 + 943 49 40 0 + 944 52 40 0 + 945 52 40 1 + 946 52 41 0 + 947 52 41 1 + 948 53 40 0 + 949 53 40 1 + 950 53 41 0 + 951 53 41 1 + 952 55 41 1 + 953 55 41 0 + 954 55 40 1 + 955 55 40 0 + 956 54 41 1 + 957 54 41 0 + 958 54 40 1 + 959 54 40 0 + 960 60 41 0 + 961 60 41 1 + 962 60 40 0 + 963 60 40 1 + 964 61 41 0 + 965 61 41 1 + 966 61 40 0 + 967 61 40 1 + 968 63 40 1 + 969 63 40 0 + 970 63 41 1 + 971 63 41 0 + 972 62 40 1 + 973 62 40 0 + 974 62 41 1 + 975 62 41 0 +1024 59 43 0 +1025 59 43 1 +1026 59 42 0 +1027 59 42 1 +1028 58 43 0 +1029 58 43 1 +1030 58 42 0 +1031 58 42 1 +1032 56 42 1 +1033 56 42 0 +1034 56 43 1 +1035 56 43 0 +1036 57 42 1 +1037 57 42 0 +1038 57 43 1 +1039 57 43 0 +1056 51 42 0 +1057 51 42 1 +1058 51 43 0 +1059 51 43 1 +1060 50 42 0 +1061 50 42 1 +1062 50 43 0 +1063 50 43 1 +1064 48 43 1 +1065 48 43 0 +1066 48 42 1 +1067 48 42 0 +1068 49 43 1 +1069 49 43 0 +1070 49 42 1 +1071 49 42 0 +1072 52 42 0 +1073 52 42 1 +1074 52 43 0 +1075 52 43 1 +1076 53 42 0 +1077 53 42 1 +1078 53 43 0 +1079 53 43 1 +1080 55 43 1 +1081 55 43 0 +1082 55 42 1 +1083 55 42 0 +1084 54 43 1 +1085 54 43 0 +1086 54 42 1 +1087 54 42 0 +1088 60 43 0 +1089 60 43 1 +1090 60 42 0 +1091 60 42 1 +1092 61 43 0 +1093 61 43 1 +1094 61 42 0 +1095 61 42 1 +1096 63 42 1 +1097 63 42 0 +1098 63 43 1 +1099 63 43 0 +1100 62 42 1 +1101 62 42 0 +1102 62 43 1 +1103 62 43 0 +1152 59 45 0 +1153 59 45 1 +1154 59 44 0 +1155 59 44 1 +1156 58 45 0 +1157 58 45 1 +1158 58 44 0 +1159 58 44 1 +1160 56 44 1 +1161 56 44 0 +1162 56 45 1 +1163 56 45 0 +1164 57 44 1 +1165 57 44 0 +1166 57 45 1 +1167 57 45 0 +1184 51 44 0 +1185 51 44 1 +1186 51 45 0 +1187 51 45 1 +1188 50 44 0 +1189 50 44 1 +1190 50 45 0 +1191 50 45 1 +1192 48 45 1 +1193 48 45 0 +1194 48 44 1 +1195 48 44 0 +1196 49 45 1 +1197 49 45 0 +1198 49 44 1 +1199 49 44 0 +1200 52 44 0 +1201 52 44 1 +1202 52 45 0 +1203 52 45 1 +1204 53 44 0 +1205 53 44 1 +1206 53 45 0 +1207 53 45 1 +1208 55 45 1 +1209 55 45 0 +1210 55 44 1 +1211 55 44 0 +1212 54 45 1 +1213 54 45 0 +1214 54 44 1 +1215 54 44 0 +1216 60 45 0 +1217 60 45 1 +1218 60 44 0 +1219 60 44 1 +1220 61 45 0 +1221 61 45 1 +1222 61 44 0 +1223 61 44 1 +1224 63 44 1 +1225 63 44 0 +1226 63 45 1 +1227 63 45 0 +1228 62 44 1 +1229 62 44 0 +1230 62 45 1 +1231 62 45 0 +1280 59 47 0 +1281 59 47 1 +1282 59 46 0 +1283 59 46 1 +1284 58 47 0 +1285 58 47 1 +1286 58 46 0 +1287 58 46 1 +1288 56 46 1 +1289 56 46 0 +1290 56 47 1 +1291 56 47 0 +1292 57 46 1 +1293 57 46 0 +1294 57 47 1 +1295 57 47 0 +1312 51 46 0 +1313 51 46 1 +1314 51 47 0 +1315 51 47 1 +1316 50 46 0 +1317 50 46 1 +1318 50 47 0 +1319 50 47 1 +1320 48 47 1 +1321 48 47 0 +1322 48 46 1 +1323 48 46 0 +1324 49 47 1 +1325 49 47 0 +1326 49 46 1 +1327 49 46 0 +1328 52 46 0 +1329 52 46 1 +1330 52 47 0 +1331 52 47 1 +1332 53 46 0 +1333 53 46 1 +1334 53 47 0 +1335 53 47 1 +1336 55 47 1 +1337 55 47 0 +1338 55 46 1 +1339 55 46 0 +1340 54 47 1 +1341 54 47 0 +1342 54 46 1 +1343 54 46 0 +1344 60 47 0 +1345 60 47 1 +1346 60 46 0 +1347 60 46 1 +1348 61 47 0 +1349 61 47 1 +1350 61 46 0 +1351 61 46 1 +1352 63 46 1 +1353 63 46 0 +1354 63 47 1 +1355 63 47 0 +1356 62 46 1 +1357 62 46 0 +1358 62 47 1 +1359 62 47 0 +1408 59 49 0 +1409 59 49 1 +1410 59 48 0 +1411 59 48 1 +1412 58 49 0 +1413 58 49 1 +1414 58 48 0 +1415 58 48 1 +1416 56 48 1 +1417 56 48 0 +1418 56 49 1 +1419 56 49 0 +1420 57 48 1 +1421 57 48 0 +1422 57 49 1 +1423 57 49 0 +1440 51 48 0 +1441 51 48 1 +1442 51 49 0 +1443 51 49 1 +1444 50 48 0 +1445 50 48 1 +1446 50 49 0 +1447 50 49 1 +1448 48 49 1 +1449 48 49 0 +1450 48 48 1 +1451 48 48 0 +1452 49 49 1 +1453 49 49 0 +1454 49 48 1 +1455 49 48 0 +1456 52 48 0 +1457 52 48 1 +1458 52 49 0 +1459 52 49 1 +1460 53 48 0 +1461 53 48 1 +1462 53 49 0 +1463 53 49 1 +1464 55 49 1 +1465 55 49 0 +1466 55 48 1 +1467 55 48 0 +1468 54 49 1 +1469 54 49 0 +1470 54 48 1 +1471 54 48 0 +1472 60 49 0 +1473 60 49 1 +1474 60 48 0 +1475 60 48 1 +1476 61 49 0 +1477 61 49 1 +1478 61 48 0 +1479 61 48 1 +1480 63 48 1 +1481 63 48 0 +1482 63 49 1 +1483 63 49 0 +1484 62 48 1 +1485 62 48 0 +1486 62 49 1 +1487 62 49 0 +1536 59 51 0 +1537 59 51 1 +1538 59 50 0 +1539 59 50 1 +1540 58 51 0 +1541 58 51 1 +1542 58 50 0 +1543 58 50 1 +1544 56 50 1 +1545 56 50 0 +1546 56 51 1 +1547 56 51 0 +1548 57 50 1 +1549 57 50 0 +1550 57 51 1 +1551 57 51 0 +1568 51 50 0 +1569 51 50 1 +1570 51 51 0 +1571 51 51 1 +1572 50 50 0 +1573 50 50 1 +1574 50 51 0 +1575 50 51 1 +1576 48 51 1 +1577 48 51 0 +1578 48 50 1 +1579 48 50 0 +1580 49 51 1 +1581 49 51 0 +1582 49 50 1 +1583 49 50 0 +1584 52 50 0 +1585 52 50 1 +1586 52 51 0 +1587 52 51 1 +1588 53 50 0 +1589 53 50 1 +1590 53 51 0 +1591 53 51 1 +1592 55 51 1 +1593 55 51 0 +1594 55 50 1 +1595 55 50 0 +1596 54 51 1 +1597 54 51 0 +1598 54 50 1 +1599 54 50 0 +1600 60 51 0 +1601 60 51 1 +1602 60 50 0 +1603 60 50 1 +1604 61 51 0 +1605 61 51 1 +1606 61 50 0 +1607 61 50 1 +1608 63 50 1 +1609 63 50 0 +1610 63 51 1 +1611 63 51 0 +1612 62 50 1 +1613 62 50 0 +1614 62 51 1 +1615 62 51 0 +1664 59 53 0 +1665 59 53 1 +1666 59 52 0 +1667 59 52 1 +1668 58 53 0 +1669 58 53 1 +1670 58 52 0 +1671 58 52 1 +1672 56 52 1 +1673 56 52 0 +1674 56 53 1 +1675 56 53 0 +1676 57 52 1 +1677 57 52 0 +1678 57 53 1 +1679 57 53 0 +1696 51 52 0 +1697 51 52 1 +1698 51 53 0 +1699 51 53 1 +1700 50 52 0 +1701 50 52 1 +1702 50 53 0 +1703 50 53 1 +1704 48 53 1 +1705 48 53 0 +1706 48 52 1 +1707 48 52 0 +1708 49 53 1 +1709 49 53 0 +1710 49 52 1 +1711 49 52 0 +1712 52 52 0 +1713 52 52 1 +1714 52 53 0 +1715 52 53 1 +1716 53 52 0 +1717 53 52 1 +1718 53 53 0 +1719 53 53 1 +1720 55 53 1 +1721 55 53 0 +1722 55 52 1 +1723 55 52 0 +1724 54 53 1 +1725 54 53 0 +1726 54 52 1 +1727 54 52 0 +1728 60 53 0 +1729 60 53 1 +1730 60 52 0 +1731 60 52 1 +1732 61 53 0 +1733 61 53 1 +1734 61 52 0 +1735 61 52 1 +1736 63 52 1 +1737 63 52 0 +1738 63 53 1 +1739 63 53 0 +1740 62 52 1 +1741 62 52 0 +1742 62 53 1 +1743 62 53 0 +1792 59 55 0 +1793 59 55 1 +1794 59 54 0 +1795 59 54 1 +1796 58 55 0 +1797 58 55 1 +1798 58 54 0 +1799 58 54 1 +1800 56 54 1 +1801 56 54 0 +1802 56 55 1 +1803 56 55 0 +1804 57 54 1 +1805 57 54 0 +1806 57 55 1 +1807 57 55 0 +1824 51 54 0 +1825 51 54 1 +1826 51 55 0 +1827 51 55 1 +1828 50 54 0 +1829 50 54 1 +1830 50 55 0 +1831 50 55 1 +1832 48 55 1 +1833 48 55 0 +1834 48 54 1 +1835 48 54 0 +1836 49 55 1 +1837 49 55 0 +1838 49 54 1 +1839 49 54 0 +1840 52 54 0 +1841 52 54 1 +1842 52 55 0 +1843 52 55 1 +1844 53 54 0 +1845 53 54 1 +1846 53 55 0 +1847 53 55 1 +1848 55 55 1 +1849 55 55 0 +1850 55 54 1 +1851 55 54 0 +1852 54 55 1 +1853 54 55 0 +1854 54 54 1 +1855 54 54 0 +1856 60 55 0 +1857 60 55 1 +1858 60 54 0 +1859 60 54 1 +1860 61 55 0 +1861 61 55 1 +1862 61 54 0 +1863 61 54 1 +1864 63 54 1 +1865 63 54 0 +1866 63 55 1 +1867 63 55 0 +1868 62 54 1 +1869 62 54 0 +1870 62 55 1 +1871 62 55 0 +2176 59 27 0 +2177 59 27 1 +2178 59 26 0 +2179 59 26 1 +2180 58 27 0 +2181 58 27 1 +2182 58 26 0 +2183 58 26 1 +2184 56 26 1 +2185 56 26 0 +2186 56 27 1 +2187 56 27 0 +2188 57 26 1 +2189 57 26 0 +2190 57 27 1 +2191 57 27 0 +2208 51 26 0 +2209 51 26 1 +2210 51 27 0 +2211 51 27 1 +2212 50 26 0 +2213 50 26 1 +2214 50 27 0 +2215 50 27 1 +2216 48 27 1 +2217 48 27 0 +2218 48 26 1 +2219 48 26 0 +2220 49 27 1 +2221 49 27 0 +2222 49 26 1 +2223 49 26 0 +2224 52 26 0 +2225 52 26 1 +2226 52 27 0 +2227 52 27 1 +2228 53 26 0 +2229 53 26 1 +2230 53 27 0 +2231 53 27 1 +2232 55 27 1 +2233 55 27 0 +2234 55 26 1 +2235 55 26 0 +2236 54 27 1 +2237 54 27 0 +2238 54 26 1 +2239 54 26 0 +2240 60 27 0 +2241 60 27 1 +2242 60 26 0 +2243 60 26 1 +2244 61 27 0 +2245 61 27 1 +2246 61 26 0 +2247 61 26 1 +2248 63 26 1 +2249 63 26 0 +2250 63 27 1 +2251 63 27 0 +2252 62 26 1 +2253 62 26 0 +2254 62 27 1 +2255 62 27 0 +2304 59 25 0 +2305 59 25 1 +2306 59 24 0 +2307 59 24 1 +2308 58 25 0 +2309 58 25 1 +2310 58 24 0 +2311 58 24 1 +2312 56 24 1 +2313 56 24 0 +2314 56 25 1 +2315 56 25 0 +2316 57 24 1 +2317 57 24 0 +2318 57 25 1 +2319 57 25 0 +2336 51 24 0 +2337 51 24 1 +2338 51 25 0 +2339 51 25 1 +2340 50 24 0 +2341 50 24 1 +2342 50 25 0 +2343 50 25 1 +2344 48 25 1 +2345 48 25 0 +2346 48 24 1 +2347 48 24 0 +2348 49 25 1 +2349 49 25 0 +2350 49 24 1 +2351 49 24 0 +2352 52 24 0 +2353 52 24 1 +2354 52 25 0 +2355 52 25 1 +2356 53 24 0 +2357 53 24 1 +2358 53 25 0 +2359 53 25 1 +2360 55 25 1 +2361 55 25 0 +2362 55 24 1 +2363 55 24 0 +2364 54 25 1 +2365 54 25 0 +2366 54 24 1 +2367 54 24 0 +2368 60 25 0 +2369 60 25 1 +2370 60 24 0 +2371 60 24 1 +2372 61 25 0 +2373 61 25 1 +2374 61 24 0 +2375 61 24 1 +2376 63 24 1 +2377 63 24 0 +2378 63 25 1 +2379 63 25 0 +2380 62 24 1 +2381 62 24 0 +2382 62 25 1 +2383 62 25 0 +2432 59 23 0 +2433 59 23 1 +2434 59 22 0 +2435 59 22 1 +2436 58 23 0 +2437 58 23 1 +2438 58 22 0 +2439 58 22 1 +2440 56 22 1 +2441 56 22 0 +2442 56 23 1 +2443 56 23 0 +2444 57 22 1 +2445 57 22 0 +2446 57 23 1 +2447 57 23 0 +2464 51 22 0 +2465 51 22 1 +2466 51 23 0 +2467 51 23 1 +2468 50 22 0 +2469 50 22 1 +2470 50 23 0 +2471 50 23 1 +2472 48 23 1 +2473 48 23 0 +2474 48 22 1 +2475 48 22 0 +2476 49 23 1 +2477 49 23 0 +2478 49 22 1 +2479 49 22 0 +2480 52 22 0 +2481 52 22 1 +2482 52 23 0 +2483 52 23 1 +2484 53 22 0 +2485 53 22 1 +2486 53 23 0 +2487 53 23 1 +2488 55 23 1 +2489 55 23 0 +2490 55 22 1 +2491 55 22 0 +2492 54 23 1 +2493 54 23 0 +2494 54 22 1 +2495 54 22 0 +2496 60 23 0 +2497 60 23 1 +2498 60 22 0 +2499 60 22 1 +2500 61 23 0 +2501 61 23 1 +2502 61 22 0 +2503 61 22 1 +2504 63 22 1 +2505 63 22 0 +2506 63 23 1 +2507 63 23 0 +2508 62 22 1 +2509 62 22 0 +2510 62 23 1 +2511 62 23 0 +2560 59 21 0 +2561 59 21 1 +2562 59 20 0 +2563 59 20 1 +2564 58 21 0 +2565 58 21 1 +2566 58 20 0 +2567 58 20 1 +2568 56 20 1 +2569 56 20 0 +2570 56 21 1 +2571 56 21 0 +2572 57 20 1 +2573 57 20 0 +2574 57 21 1 +2575 57 21 0 +2592 51 20 0 +2593 51 20 1 +2594 51 21 0 +2595 51 21 1 +2596 50 20 0 +2597 50 20 1 +2598 50 21 0 +2599 50 21 1 +2600 48 21 1 +2601 48 21 0 +2602 48 20 1 +2603 48 20 0 +2604 49 21 1 +2605 49 21 0 +2606 49 20 1 +2607 49 20 0 +2608 52 20 0 +2609 52 20 1 +2610 52 21 0 +2611 52 21 1 +2612 53 20 0 +2613 53 20 1 +2614 53 21 0 +2615 53 21 1 +2616 55 21 1 +2617 55 21 0 +2618 55 20 1 +2619 55 20 0 +2620 54 21 1 +2621 54 21 0 +2622 54 20 1 +2623 54 20 0 +2624 60 21 0 +2625 60 21 1 +2626 60 20 0 +2627 60 20 1 +2628 61 21 0 +2629 61 21 1 +2630 61 20 0 +2631 61 20 1 +2632 63 20 1 +2633 63 20 0 +2634 63 21 1 +2635 63 21 0 +2636 62 20 1 +2637 62 20 0 +2638 62 21 1 +2639 62 21 0 +2688 59 19 0 +2689 59 19 1 +2690 59 18 0 +2691 59 18 1 +2692 58 19 0 +2693 58 19 1 +2694 58 18 0 +2695 58 18 1 +2696 56 18 1 +2697 56 18 0 +2698 56 19 1 +2699 56 19 0 +2700 57 18 1 +2701 57 18 0 +2702 57 19 1 +2703 57 19 0 +2720 51 18 0 +2721 51 18 1 +2722 51 19 0 +2723 51 19 1 +2724 50 18 0 +2725 50 18 1 +2726 50 19 0 +2727 50 19 1 +2728 48 19 1 +2729 48 19 0 +2730 48 18 1 +2731 48 18 0 +2732 49 19 1 +2733 49 19 0 +2734 49 18 1 +2735 49 18 0 +2736 52 18 0 +2737 52 18 1 +2738 52 19 0 +2739 52 19 1 +2740 53 18 0 +2741 53 18 1 +2742 53 19 0 +2743 53 19 1 +2744 55 19 1 +2745 55 19 0 +2746 55 18 1 +2747 55 18 0 +2748 54 19 1 +2749 54 19 0 +2750 54 18 1 +2751 54 18 0 +2752 60 19 0 +2753 60 19 1 +2754 60 18 0 +2755 60 18 1 +2756 61 19 0 +2757 61 19 1 +2758 61 18 0 +2759 61 18 1 +2760 63 18 1 +2761 63 18 0 +2762 63 19 1 +2763 63 19 0 +2764 62 18 1 +2765 62 18 0 +2766 62 19 1 +2767 62 19 0 +2816 59 17 0 +2817 59 17 1 +2818 59 16 0 +2819 59 16 1 +2820 58 17 0 +2821 58 17 1 +2822 58 16 0 +2823 58 16 1 +2824 56 16 1 +2825 56 16 0 +2826 56 17 1 +2827 56 17 0 +2828 57 16 1 +2829 57 16 0 +2830 57 17 1 +2831 57 17 0 +2848 51 16 0 +2849 51 16 1 +2850 51 17 0 +2851 51 17 1 +2852 50 16 0 +2853 50 16 1 +2854 50 17 0 +2855 50 17 1 +2856 48 17 1 +2857 48 17 0 +2858 48 16 1 +2859 48 16 0 +2860 49 17 1 +2861 49 17 0 +2862 49 16 1 +2863 49 16 0 +2864 52 16 0 +2865 52 16 1 +2866 52 17 0 +2867 52 17 1 +2868 53 16 0 +2869 53 16 1 +2870 53 17 0 +2871 53 17 1 +2872 55 17 1 +2873 55 17 0 +2874 55 16 1 +2875 55 16 0 +2876 54 17 1 +2877 54 17 0 +2878 54 16 1 +2879 54 16 0 +2880 60 17 0 +2881 60 17 1 +2882 60 16 0 +2883 60 16 1 +2884 61 17 0 +2885 61 17 1 +2886 61 16 0 +2887 61 16 1 +2888 63 16 1 +2889 63 16 0 +2890 63 17 1 +2891 63 17 0 +2892 62 16 1 +2893 62 16 0 +2894 62 17 1 +2895 62 17 0 +2944 59 15 0 +2945 59 15 1 +2946 59 14 0 +2947 59 14 1 +2948 58 15 0 +2949 58 15 1 +2950 58 14 0 +2951 58 14 1 +2952 56 14 1 +2953 56 14 0 +2954 56 15 1 +2955 56 15 0 +2956 57 14 1 +2957 57 14 0 +2958 57 15 1 +2959 57 15 0 +2976 51 14 0 +2977 51 14 1 +2978 51 15 0 +2979 51 15 1 +2980 50 14 0 +2981 50 14 1 +2982 50 15 0 +2983 50 15 1 +2984 48 15 1 +2985 48 15 0 +2986 48 14 1 +2987 48 14 0 +2988 49 15 1 +2989 49 15 0 +2990 49 14 1 +2991 49 14 0 +2992 52 14 0 +2993 52 14 1 +2994 52 15 0 +2995 52 15 1 +2996 53 14 0 +2997 53 14 1 +2998 53 15 0 +2999 53 15 1 +3000 55 15 1 +3001 55 15 0 +3002 55 14 1 +3003 55 14 0 +3004 54 15 1 +3005 54 15 0 +3006 54 14 1 +3007 54 14 0 +3008 60 15 0 +3009 60 15 1 +3010 60 14 0 +3011 60 14 1 +3012 61 15 0 +3013 61 15 1 +3014 61 14 0 +3015 61 14 1 +3016 63 14 1 +3017 63 14 0 +3018 63 15 1 +3019 63 15 0 +3020 62 14 1 +3021 62 14 0 +3022 62 15 1 +3023 62 15 0 +3072 59 13 0 +3073 59 13 1 +3074 59 12 0 +3075 59 12 1 +3076 58 13 0 +3077 58 13 1 +3078 58 12 0 +3079 58 12 1 +3080 56 12 1 +3081 56 12 0 +3082 56 13 1 +3083 56 13 0 +3084 57 12 1 +3085 57 12 0 +3086 57 13 1 +3087 57 13 0 +3104 51 12 0 +3105 51 12 1 +3106 51 13 0 +3107 51 13 1 +3108 50 12 0 +3109 50 12 1 +3110 50 13 0 +3111 50 13 1 +3112 48 13 1 +3113 48 13 0 +3114 48 12 1 +3115 48 12 0 +3116 49 13 1 +3117 49 13 0 +3118 49 12 1 +3119 49 12 0 +3120 52 12 0 +3121 52 12 1 +3122 52 13 0 +3123 52 13 1 +3124 53 12 0 +3125 53 12 1 +3126 53 13 0 +3127 53 13 1 +3128 55 13 1 +3129 55 13 0 +3130 55 12 1 +3131 55 12 0 +3132 54 13 1 +3133 54 13 0 +3134 54 12 1 +3135 54 12 0 +3136 60 13 0 +3137 60 13 1 +3138 60 12 0 +3139 60 12 1 +3140 61 13 0 +3141 61 13 1 +3142 61 12 0 +3143 61 12 1 +3144 63 12 1 +3145 63 12 0 +3146 63 13 1 +3147 63 13 0 +3148 62 12 1 +3149 62 12 0 +3150 62 13 1 +3151 62 13 0 +3200 59 11 0 +3201 59 11 1 +3202 59 10 0 +3203 59 10 1 +3204 58 11 0 +3205 58 11 1 +3206 58 10 0 +3207 58 10 1 +3208 56 10 1 +3209 56 10 0 +3210 56 11 1 +3211 56 11 0 +3212 57 10 1 +3213 57 10 0 +3214 57 11 1 +3215 57 11 0 +3232 51 10 0 +3233 51 10 1 +3234 51 11 0 +3235 51 11 1 +3236 50 10 0 +3237 50 10 1 +3238 50 11 0 +3239 50 11 1 +3240 48 11 1 +3241 48 11 0 +3242 48 10 1 +3243 48 10 0 +3244 49 11 1 +3245 49 11 0 +3246 49 10 1 +3247 49 10 0 +3248 52 10 0 +3249 52 10 1 +3250 52 11 0 +3251 52 11 1 +3252 53 10 0 +3253 53 10 1 +3254 53 11 0 +3255 53 11 1 +3256 55 11 1 +3257 55 11 0 +3258 55 10 1 +3259 55 10 0 +3260 54 11 1 +3261 54 11 0 +3262 54 10 1 +3263 54 10 0 +3264 60 11 0 +3265 60 11 1 +3266 60 10 0 +3267 60 10 1 +3268 61 11 0 +3269 61 11 1 +3270 61 10 0 +3271 61 10 1 +3272 63 10 1 +3273 63 10 0 +3274 63 11 1 +3275 63 11 0 +3276 62 10 1 +3277 62 10 0 +3278 62 11 1 +3279 62 11 0 +3328 59 9 0 +3329 59 9 1 +3330 59 8 0 +3331 59 8 1 +3332 58 9 0 +3333 58 9 1 +3334 58 8 0 +3335 58 8 1 +3336 56 8 1 +3337 56 8 0 +3338 56 9 1 +3339 56 9 0 +3340 57 8 1 +3341 57 8 0 +3342 57 9 1 +3343 57 9 0 +3360 51 8 0 +3361 51 8 1 +3362 51 9 0 +3363 51 9 1 +3364 50 8 0 +3365 50 8 1 +3366 50 9 0 +3367 50 9 1 +3368 48 9 1 +3369 48 9 0 +3370 48 8 1 +3371 48 8 0 +3372 49 9 1 +3373 49 9 0 +3374 49 8 1 +3375 49 8 0 +3376 52 8 0 +3377 52 8 1 +3378 52 9 0 +3379 52 9 1 +3380 53 8 0 +3381 53 8 1 +3382 53 9 0 +3383 53 9 1 +3384 55 9 1 +3385 55 9 0 +3386 55 8 1 +3387 55 8 0 +3388 54 9 1 +3389 54 9 0 +3390 54 8 1 +3391 54 8 0 +3392 60 9 0 +3393 60 9 1 +3394 60 8 0 +3395 60 8 1 +3396 61 9 0 +3397 61 9 1 +3398 61 8 0 +3399 61 8 1 +3400 63 8 1 +3401 63 8 0 +3402 63 9 1 +3403 63 9 0 +3404 62 8 1 +3405 62 8 0 +3406 62 9 1 +3407 62 9 0 +3456 59 7 0 +3457 59 7 1 +3458 59 6 0 +3459 59 6 1 +3460 58 7 0 +3461 58 7 1 +3462 58 6 0 +3463 58 6 1 +3464 56 6 1 +3465 56 6 0 +3466 56 7 1 +3467 56 7 0 +3468 57 6 1 +3469 57 6 0 +3470 57 7 1 +3471 57 7 0 +3488 51 6 0 +3489 51 6 1 +3490 51 7 0 +3491 51 7 1 +3492 50 6 0 +3493 50 6 1 +3494 50 7 0 +3495 50 7 1 +3496 48 7 1 +3497 48 7 0 +3498 48 6 1 +3499 48 6 0 +3500 49 7 1 +3501 49 7 0 +3502 49 6 1 +3503 49 6 0 +3504 52 6 0 +3505 52 6 1 +3506 52 7 0 +3507 52 7 1 +3508 53 6 0 +3509 53 6 1 +3510 53 7 0 +3511 53 7 1 +3512 55 7 1 +3513 55 7 0 +3514 55 6 1 +3515 55 6 0 +3516 54 7 1 +3517 54 7 0 +3518 54 6 1 +3519 54 6 0 +3520 60 7 0 +3521 60 7 1 +3522 60 6 0 +3523 60 6 1 +3524 61 7 0 +3525 61 7 1 +3526 61 6 0 +3527 61 6 1 +3528 63 6 1 +3529 63 6 0 +3530 63 7 1 +3531 63 7 0 +3532 62 6 1 +3533 62 6 0 +3534 62 7 1 +3535 62 7 0 +3584 59 5 0 +3585 59 5 1 +3586 59 4 0 +3587 59 4 1 +3588 58 5 0 +3589 58 5 1 +3590 58 4 0 +3591 58 4 1 +3592 56 4 1 +3593 56 4 0 +3594 56 5 1 +3595 56 5 0 +3596 57 4 1 +3597 57 4 0 +3598 57 5 1 +3599 57 5 0 +3616 51 4 0 +3617 51 4 1 +3618 51 5 0 +3619 51 5 1 +3620 50 4 0 +3621 50 4 1 +3622 50 5 0 +3623 50 5 1 +3624 48 5 1 +3625 48 5 0 +3626 48 4 1 +3627 48 4 0 +3628 49 5 1 +3629 49 5 0 +3630 49 4 1 +3631 49 4 0 +3632 52 4 0 +3633 52 4 1 +3634 52 5 0 +3635 52 5 1 +3636 53 4 0 +3637 53 4 1 +3638 53 5 0 +3639 53 5 1 +3640 55 5 1 +3641 55 5 0 +3642 55 4 1 +3643 55 4 0 +3644 54 5 1 +3645 54 5 0 +3646 54 4 1 +3647 54 4 0 +3648 60 5 0 +3649 60 5 1 +3650 60 4 0 +3651 60 4 1 +3652 61 5 0 +3653 61 5 1 +3654 61 4 0 +3655 61 4 1 +3656 63 4 1 +3657 63 4 0 +3658 63 5 1 +3659 63 5 0 +3660 62 4 1 +3661 62 4 0 +3662 62 5 1 +3663 62 5 0 +3712 59 3 0 +3713 59 3 1 +3714 59 2 0 +3715 59 2 1 +3716 58 3 0 +3717 58 3 1 +3718 58 2 0 +3719 58 2 1 +3720 56 2 1 +3721 56 2 0 +3722 56 3 1 +3723 56 3 0 +3724 57 2 1 +3725 57 2 0 +3726 57 3 1 +3727 57 3 0 +3744 51 2 0 +3745 51 2 1 +3746 51 3 0 +3747 51 3 1 +3748 50 2 0 +3749 50 2 1 +3750 50 3 0 +3751 50 3 1 +3752 48 3 1 +3753 48 3 0 +3754 48 2 1 +3755 48 2 0 +3756 49 3 1 +3757 49 3 0 +3758 49 2 1 +3759 49 2 0 +3760 52 2 0 +3761 52 2 1 +3762 52 3 0 +3763 52 3 1 +3764 53 2 0 +3765 53 2 1 +3766 53 3 0 +3767 53 3 1 +3768 55 3 1 +3769 55 3 0 +3770 55 2 1 +3771 55 2 0 +3772 54 3 1 +3773 54 3 0 +3774 54 2 1 +3775 54 2 0 +3776 60 3 0 +3777 60 3 1 +3778 60 2 0 +3779 60 2 1 +3780 61 3 0 +3781 61 3 1 +3782 61 2 0 +3783 61 2 1 +3784 63 2 1 +3785 63 2 0 +3786 63 3 1 +3787 63 3 0 +3788 62 2 1 +3789 62 2 0 +3790 62 3 1 +3791 62 3 0 +3840 59 1 0 +3841 59 1 1 +3842 59 0 0 +3843 59 0 1 +3844 58 1 0 +3845 58 1 1 +3846 58 0 0 +3847 58 0 1 +3848 56 0 1 +3849 56 0 0 +3850 56 1 1 +3851 56 1 0 +3852 57 0 1 +3853 57 0 0 +3854 57 1 1 +3855 57 1 0 +3872 51 0 0 +3873 51 0 1 +3874 51 1 0 +3875 51 1 1 +3876 50 0 0 +3877 50 0 1 +3878 50 1 0 +3879 50 1 1 +3880 48 1 1 +3881 48 1 0 +3882 48 0 1 +3883 48 0 0 +3884 49 1 1 +3885 49 1 0 +3886 49 0 1 +3887 49 0 0 +3888 52 0 0 +3889 52 0 1 +3890 52 1 0 +3891 52 1 1 +3892 53 0 0 +3893 53 0 1 +3894 53 1 0 +3895 53 1 1 +3896 55 1 1 +3897 55 1 0 +3898 55 0 1 +3899 55 0 0 +3900 54 1 1 +3901 54 1 0 +3902 54 0 1 +3903 54 0 0 +3904 60 1 0 +3905 60 1 1 +3906 60 0 0 +3907 60 0 1 +3908 61 1 0 +3909 61 1 1 +3910 61 0 0 +3911 61 0 1 +3912 63 0 1 +3913 63 0 0 +3914 63 1 1 +3915 63 1 0 +3916 62 0 1 +3917 62 0 0 +3918 62 1 1 +3919 62 1 0 diff --git a/Detectors/PHOS/base/files/Mod3RCU0.data b/Detectors/PHOS/base/files/Mod3RCU0.data new file mode 100644 index 0000000000000..4739df6e3e61e --- /dev/null +++ b/Detectors/PHOS/base/files/Mod3RCU0.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 0 0 2 + 1 0 1 2 + 2 0 2 2 + 3 0 3 2 + 4 0 4 2 + 5 0 5 2 + 6 0 6 2 + 7 0 7 2 + 8 0 8 2 + 9 0 9 2 + 10 0 10 2 + 11 0 11 2 + 12 0 12 2 + 13 0 13 2 + 14 0 14 2 + 15 0 15 2 + 16 0 16 2 + 17 0 17 2 + 18 0 18 2 + 19 0 19 2 + 20 0 20 2 + 21 0 21 2 + 22 0 22 2 + 23 0 23 2 + 24 0 24 2 + 25 0 25 2 + 26 0 26 2 + 27 0 27 2 + 28 0 28 2 + 29 0 29 2 + 30 0 30 2 + 31 0 31 2 + 32 0 32 2 + 33 0 33 2 + 34 0 34 2 + 35 0 35 2 + 36 0 36 2 + 37 0 37 2 + 38 0 38 2 + 39 0 39 2 + 40 0 40 2 + 41 0 41 2 + 42 0 42 2 + 43 0 43 2 + 44 0 44 2 + 45 0 45 2 + 46 0 46 2 + 47 0 47 2 + 48 0 48 2 + 49 0 49 2 + 50 0 50 2 + 51 0 51 2 + 52 0 52 2 + 53 0 53 2 + 54 0 54 2 + 55 0 55 2 + 56 0 56 2 + 57 0 57 2 + 58 0 58 2 + 59 0 59 2 + 60 0 60 2 + 61 0 61 2 + 62 0 62 2 + 63 0 63 2 + 64 0 64 2 + 65 0 65 2 + 66 0 66 2 + 67 0 67 2 + 68 0 68 2 + 69 0 69 2 + 70 0 70 2 + 71 0 71 2 + 72 0 72 2 + 73 0 73 2 + 74 0 74 2 + 75 0 75 2 + 76 0 76 2 + 77 0 77 2 + 78 0 78 2 + 79 0 79 2 + 80 0 80 2 + 81 0 81 2 + 82 0 82 2 + 83 0 83 2 + 84 0 84 2 + 85 0 85 2 + 86 0 86 2 + 87 0 87 2 + 88 0 88 2 + 89 0 89 2 + 90 0 90 2 + 91 0 91 2 + 92 0 92 2 + 93 0 93 2 + 94 0 94 2 + 95 0 95 2 + 96 0 96 2 + 97 0 97 2 + 98 0 98 2 + 99 0 99 2 + 100 0 100 2 + 101 0 101 2 + 102 0 102 2 + 103 0 103 2 + 104 0 104 2 + 105 0 105 2 + 106 0 106 2 + 107 0 107 2 + 108 0 108 2 + 109 0 109 2 + 110 0 110 2 + 111 0 111 2 + 112 0 112 2 + 113 0 113 2 + 114 0 114 2 + 115 0 115 2 + 116 0 116 2 + 117 0 117 2 + 118 0 118 2 + 119 0 119 2 + 120 0 120 2 + 121 0 121 2 + 122 0 122 2 + 123 0 123 2 + 124 0 124 2 + 125 0 125 2 + 126 0 126 2 + 127 0 127 2 +2048 0 2048 2 +2049 0 2049 2 +2050 0 2050 2 +2051 0 2051 2 +2052 0 2052 2 +2053 0 2053 2 +2054 0 2054 2 +2055 0 2055 2 +2056 0 2056 2 +2057 0 2057 2 +2058 0 2058 2 +2059 0 2059 2 +2060 0 2060 2 +2061 0 2061 2 +2062 0 2062 2 +2063 0 2063 2 +2064 0 2064 2 +2065 0 2065 2 +2066 0 2066 2 +2067 0 2067 2 +2068 0 2068 2 +2069 0 2069 2 +2070 0 2070 2 +2071 0 2071 2 +2072 0 2072 2 +2073 0 2073 2 +2074 0 2074 2 +2075 0 2075 2 +2076 0 2076 2 +2077 0 2077 2 +2078 0 2078 2 +2079 0 2079 2 +2080 0 2080 2 +2081 0 2081 2 +2082 0 2082 2 +2083 0 2083 2 +2084 0 2084 2 +2085 0 2085 2 +2086 0 2086 2 +2087 0 2087 2 +2088 0 2088 2 +2089 0 2089 2 +2090 0 2090 2 +2091 0 2091 2 +2092 0 2092 2 +2093 0 2093 2 +2094 0 2094 2 +2095 0 2095 2 +2096 0 2096 2 +2097 0 2097 2 +2098 0 2098 2 +2099 0 2099 2 +2100 0 2100 2 +2101 0 2101 2 +2102 0 2102 2 +2103 0 2103 2 +2104 0 2104 2 +2105 0 2105 2 +2106 0 2106 2 +2107 0 2107 2 +2108 0 2108 2 +2109 0 2109 2 +2110 0 2110 2 +2111 0 2111 2 +2112 0 2112 2 +2113 0 2113 2 +2114 0 2114 2 +2115 0 2115 2 +2116 0 2116 2 +2117 0 2117 2 +2118 0 2118 2 +2119 0 2119 2 +2120 0 2120 2 +2121 0 2121 2 +2122 0 2122 2 +2123 0 2123 2 +2124 0 2124 2 +2125 0 2125 2 +2126 0 2126 2 +2127 0 2127 2 +2128 0 2128 2 +2129 0 2129 2 +2130 0 2130 2 +2131 0 2131 2 +2132 0 2132 2 +2133 0 2133 2 +2134 0 2134 2 +2135 0 2135 2 +2136 0 2136 2 +2137 0 2137 2 +2138 0 2138 2 +2139 0 2139 2 +2140 0 2140 2 +2141 0 2141 2 +2142 0 2142 2 +2143 0 2143 2 +2144 0 2144 2 +2145 0 2145 2 +2146 0 2146 2 +2147 0 2147 2 +2148 0 2148 2 +2149 0 2149 2 +2150 0 2150 2 +2151 0 2151 2 +2152 0 2152 2 +2153 0 2153 2 +2154 0 2154 2 +2155 0 2155 2 +2156 0 2156 2 +2157 0 2157 2 +2158 0 2158 2 +2159 0 2159 2 +2160 0 2160 2 +2161 0 2161 2 +2162 0 2162 2 +2163 0 2163 2 +2164 0 2164 2 +2165 0 2165 2 +2166 0 2166 2 +2167 0 2167 2 +2168 0 2168 2 +2169 0 2169 2 +2170 0 2170 2 +2171 0 2171 2 +2172 0 2172 2 +2173 0 2173 2 +2174 0 2174 2 +2175 0 2175 2 + 128 11 29 0 + 129 11 29 1 + 130 11 28 0 + 131 11 28 1 + 132 10 29 0 + 133 10 29 1 + 134 10 28 0 + 135 10 28 1 + 136 8 28 1 + 137 8 28 0 + 138 8 29 1 + 139 8 29 0 + 140 9 28 1 + 141 9 28 0 + 142 9 29 1 + 143 9 29 0 + 160 3 28 0 + 161 3 28 1 + 162 3 29 0 + 163 3 29 1 + 164 2 28 0 + 165 2 28 1 + 166 2 29 0 + 167 2 29 1 + 168 0 29 1 + 169 0 29 0 + 170 0 28 1 + 171 0 28 0 + 172 1 29 1 + 173 1 29 0 + 174 1 28 1 + 175 1 28 0 + 176 4 28 0 + 177 4 28 1 + 178 4 29 0 + 179 4 29 1 + 180 5 28 0 + 181 5 28 1 + 182 5 29 0 + 183 5 29 1 + 184 7 29 1 + 185 7 29 0 + 186 7 28 1 + 187 7 28 0 + 188 6 29 1 + 189 6 29 0 + 190 6 28 1 + 191 6 28 0 + 192 12 29 0 + 193 12 29 1 + 194 12 28 0 + 195 12 28 1 + 196 13 29 0 + 197 13 29 1 + 198 13 28 0 + 199 13 28 1 + 200 15 28 1 + 201 15 28 0 + 202 15 29 1 + 203 15 29 0 + 204 14 28 1 + 205 14 28 0 + 206 14 29 1 + 207 14 29 0 + 256 11 31 0 + 257 11 31 1 + 258 11 30 0 + 259 11 30 1 + 260 10 31 0 + 261 10 31 1 + 262 10 30 0 + 263 10 30 1 + 264 8 30 1 + 265 8 30 0 + 266 8 31 1 + 267 8 31 0 + 268 9 30 1 + 269 9 30 0 + 270 9 31 1 + 271 9 31 0 + 288 3 30 0 + 289 3 30 1 + 290 3 31 0 + 291 3 31 1 + 292 2 30 0 + 293 2 30 1 + 294 2 31 0 + 295 2 31 1 + 296 0 31 1 + 297 0 31 0 + 298 0 30 1 + 299 0 30 0 + 300 1 31 1 + 301 1 31 0 + 302 1 30 1 + 303 1 30 0 + 304 4 30 0 + 305 4 30 1 + 306 4 31 0 + 307 4 31 1 + 308 5 30 0 + 309 5 30 1 + 310 5 31 0 + 311 5 31 1 + 312 7 31 1 + 313 7 31 0 + 314 7 30 1 + 315 7 30 0 + 316 6 31 1 + 317 6 31 0 + 318 6 30 1 + 319 6 30 0 + 320 12 31 0 + 321 12 31 1 + 322 12 30 0 + 323 12 30 1 + 324 13 31 0 + 325 13 31 1 + 326 13 30 0 + 327 13 30 1 + 328 15 30 1 + 329 15 30 0 + 330 15 31 1 + 331 15 31 0 + 332 14 30 1 + 333 14 30 0 + 334 14 31 1 + 335 14 31 0 + 384 11 33 0 + 385 11 33 1 + 386 11 32 0 + 387 11 32 1 + 388 10 33 0 + 389 10 33 1 + 390 10 32 0 + 391 10 32 1 + 392 8 32 1 + 393 8 32 0 + 394 8 33 1 + 395 8 33 0 + 396 9 32 1 + 397 9 32 0 + 398 9 33 1 + 399 9 33 0 + 416 3 32 0 + 417 3 32 1 + 418 3 33 0 + 419 3 33 1 + 420 2 32 0 + 421 2 32 1 + 422 2 33 0 + 423 2 33 1 + 424 0 33 1 + 425 0 33 0 + 426 0 32 1 + 427 0 32 0 + 428 1 33 1 + 429 1 33 0 + 430 1 32 1 + 431 1 32 0 + 432 4 32 0 + 433 4 32 1 + 434 4 33 0 + 435 4 33 1 + 436 5 32 0 + 437 5 32 1 + 438 5 33 0 + 439 5 33 1 + 440 7 33 1 + 441 7 33 0 + 442 7 32 1 + 443 7 32 0 + 444 6 33 1 + 445 6 33 0 + 446 6 32 1 + 447 6 32 0 + 448 12 33 0 + 449 12 33 1 + 450 12 32 0 + 451 12 32 1 + 452 13 33 0 + 453 13 33 1 + 454 13 32 0 + 455 13 32 1 + 456 15 32 1 + 457 15 32 0 + 458 15 33 1 + 459 15 33 0 + 460 14 32 1 + 461 14 32 0 + 462 14 33 1 + 463 14 33 0 + 512 11 35 0 + 513 11 35 1 + 514 11 34 0 + 515 11 34 1 + 516 10 35 0 + 517 10 35 1 + 518 10 34 0 + 519 10 34 1 + 520 8 34 1 + 521 8 34 0 + 522 8 35 1 + 523 8 35 0 + 524 9 34 1 + 525 9 34 0 + 526 9 35 1 + 527 9 35 0 + 544 3 34 0 + 545 3 34 1 + 546 3 35 0 + 547 3 35 1 + 548 2 34 0 + 549 2 34 1 + 550 2 35 0 + 551 2 35 1 + 552 0 35 1 + 553 0 35 0 + 554 0 34 1 + 555 0 34 0 + 556 1 35 1 + 557 1 35 0 + 558 1 34 1 + 559 1 34 0 + 560 4 34 0 + 561 4 34 1 + 562 4 35 0 + 563 4 35 1 + 564 5 34 0 + 565 5 34 1 + 566 5 35 0 + 567 5 35 1 + 568 7 35 1 + 569 7 35 0 + 570 7 34 1 + 571 7 34 0 + 572 6 35 1 + 573 6 35 0 + 574 6 34 1 + 575 6 34 0 + 576 12 35 0 + 577 12 35 1 + 578 12 34 0 + 579 12 34 1 + 580 13 35 0 + 581 13 35 1 + 582 13 34 0 + 583 13 34 1 + 584 15 34 1 + 585 15 34 0 + 586 15 35 1 + 587 15 35 0 + 588 14 34 1 + 589 14 34 0 + 590 14 35 1 + 591 14 35 0 + 640 11 37 0 + 641 11 37 1 + 642 11 36 0 + 643 11 36 1 + 644 10 37 0 + 645 10 37 1 + 646 10 36 0 + 647 10 36 1 + 648 8 36 1 + 649 8 36 0 + 650 8 37 1 + 651 8 37 0 + 652 9 36 1 + 653 9 36 0 + 654 9 37 1 + 655 9 37 0 + 672 3 36 0 + 673 3 36 1 + 674 3 37 0 + 675 3 37 1 + 676 2 36 0 + 677 2 36 1 + 678 2 37 0 + 679 2 37 1 + 680 0 37 1 + 681 0 37 0 + 682 0 36 1 + 683 0 36 0 + 684 1 37 1 + 685 1 37 0 + 686 1 36 1 + 687 1 36 0 + 688 4 36 0 + 689 4 36 1 + 690 4 37 0 + 691 4 37 1 + 692 5 36 0 + 693 5 36 1 + 694 5 37 0 + 695 5 37 1 + 696 7 37 1 + 697 7 37 0 + 698 7 36 1 + 699 7 36 0 + 700 6 37 1 + 701 6 37 0 + 702 6 36 1 + 703 6 36 0 + 704 12 37 0 + 705 12 37 1 + 706 12 36 0 + 707 12 36 1 + 708 13 37 0 + 709 13 37 1 + 710 13 36 0 + 711 13 36 1 + 712 15 36 1 + 713 15 36 0 + 714 15 37 1 + 715 15 37 0 + 716 14 36 1 + 717 14 36 0 + 718 14 37 1 + 719 14 37 0 + 768 11 39 0 + 769 11 39 1 + 770 11 38 0 + 771 11 38 1 + 772 10 39 0 + 773 10 39 1 + 774 10 38 0 + 775 10 38 1 + 776 8 38 1 + 777 8 38 0 + 778 8 39 1 + 779 8 39 0 + 780 9 38 1 + 781 9 38 0 + 782 9 39 1 + 783 9 39 0 + 800 3 38 0 + 801 3 38 1 + 802 3 39 0 + 803 3 39 1 + 804 2 38 0 + 805 2 38 1 + 806 2 39 0 + 807 2 39 1 + 808 0 39 1 + 809 0 39 0 + 810 0 38 1 + 811 0 38 0 + 812 1 39 1 + 813 1 39 0 + 814 1 38 1 + 815 1 38 0 + 816 4 38 0 + 817 4 38 1 + 818 4 39 0 + 819 4 39 1 + 820 5 38 0 + 821 5 38 1 + 822 5 39 0 + 823 5 39 1 + 824 7 39 1 + 825 7 39 0 + 826 7 38 1 + 827 7 38 0 + 828 6 39 1 + 829 6 39 0 + 830 6 38 1 + 831 6 38 0 + 832 12 39 0 + 833 12 39 1 + 834 12 38 0 + 835 12 38 1 + 836 13 39 0 + 837 13 39 1 + 838 13 38 0 + 839 13 38 1 + 840 15 38 1 + 841 15 38 0 + 842 15 39 1 + 843 15 39 0 + 844 14 38 1 + 845 14 38 0 + 846 14 39 1 + 847 14 39 0 + 896 11 41 0 + 897 11 41 1 + 898 11 40 0 + 899 11 40 1 + 900 10 41 0 + 901 10 41 1 + 902 10 40 0 + 903 10 40 1 + 904 8 40 1 + 905 8 40 0 + 906 8 41 1 + 907 8 41 0 + 908 9 40 1 + 909 9 40 0 + 910 9 41 1 + 911 9 41 0 + 928 3 40 0 + 929 3 40 1 + 930 3 41 0 + 931 3 41 1 + 932 2 40 0 + 933 2 40 1 + 934 2 41 0 + 935 2 41 1 + 936 0 41 1 + 937 0 41 0 + 938 0 40 1 + 939 0 40 0 + 940 1 41 1 + 941 1 41 0 + 942 1 40 1 + 943 1 40 0 + 944 4 40 0 + 945 4 40 1 + 946 4 41 0 + 947 4 41 1 + 948 5 40 0 + 949 5 40 1 + 950 5 41 0 + 951 5 41 1 + 952 7 41 1 + 953 7 41 0 + 954 7 40 1 + 955 7 40 0 + 956 6 41 1 + 957 6 41 0 + 958 6 40 1 + 959 6 40 0 + 960 12 41 0 + 961 12 41 1 + 962 12 40 0 + 963 12 40 1 + 964 13 41 0 + 965 13 41 1 + 966 13 40 0 + 967 13 40 1 + 968 15 40 1 + 969 15 40 0 + 970 15 41 1 + 971 15 41 0 + 972 14 40 1 + 973 14 40 0 + 974 14 41 1 + 975 14 41 0 +1024 11 43 0 +1025 11 43 1 +1026 11 42 0 +1027 11 42 1 +1028 10 43 0 +1029 10 43 1 +1030 10 42 0 +1031 10 42 1 +1032 8 42 1 +1033 8 42 0 +1034 8 43 1 +1035 8 43 0 +1036 9 42 1 +1037 9 42 0 +1038 9 43 1 +1039 9 43 0 +1056 3 42 0 +1057 3 42 1 +1058 3 43 0 +1059 3 43 1 +1060 2 42 0 +1061 2 42 1 +1062 2 43 0 +1063 2 43 1 +1064 0 43 1 +1065 0 43 0 +1066 0 42 1 +1067 0 42 0 +1068 1 43 1 +1069 1 43 0 +1070 1 42 1 +1071 1 42 0 +1072 4 42 0 +1073 4 42 1 +1074 4 43 0 +1075 4 43 1 +1076 5 42 0 +1077 5 42 1 +1078 5 43 0 +1079 5 43 1 +1080 7 43 1 +1081 7 43 0 +1082 7 42 1 +1083 7 42 0 +1084 6 43 1 +1085 6 43 0 +1086 6 42 1 +1087 6 42 0 +1088 12 43 0 +1089 12 43 1 +1090 12 42 0 +1091 12 42 1 +1092 13 43 0 +1093 13 43 1 +1094 13 42 0 +1095 13 42 1 +1096 15 42 1 +1097 15 42 0 +1098 15 43 1 +1099 15 43 0 +1100 14 42 1 +1101 14 42 0 +1102 14 43 1 +1103 14 43 0 +1152 11 45 0 +1153 11 45 1 +1154 11 44 0 +1155 11 44 1 +1156 10 45 0 +1157 10 45 1 +1158 10 44 0 +1159 10 44 1 +1160 8 44 1 +1161 8 44 0 +1162 8 45 1 +1163 8 45 0 +1164 9 44 1 +1165 9 44 0 +1166 9 45 1 +1167 9 45 0 +1184 3 44 0 +1185 3 44 1 +1186 3 45 0 +1187 3 45 1 +1188 2 44 0 +1189 2 44 1 +1190 2 45 0 +1191 2 45 1 +1192 0 45 1 +1193 0 45 0 +1194 0 44 1 +1195 0 44 0 +1196 1 45 1 +1197 1 45 0 +1198 1 44 1 +1199 1 44 0 +1200 4 44 0 +1201 4 44 1 +1202 4 45 0 +1203 4 45 1 +1204 5 44 0 +1205 5 44 1 +1206 5 45 0 +1207 5 45 1 +1208 7 45 1 +1209 7 45 0 +1210 7 44 1 +1211 7 44 0 +1212 6 45 1 +1213 6 45 0 +1214 6 44 1 +1215 6 44 0 +1216 12 45 0 +1217 12 45 1 +1218 12 44 0 +1219 12 44 1 +1220 13 45 0 +1221 13 45 1 +1222 13 44 0 +1223 13 44 1 +1224 15 44 1 +1225 15 44 0 +1226 15 45 1 +1227 15 45 0 +1228 14 44 1 +1229 14 44 0 +1230 14 45 1 +1231 14 45 0 +1280 11 47 0 +1281 11 47 1 +1282 11 46 0 +1283 11 46 1 +1284 10 47 0 +1285 10 47 1 +1286 10 46 0 +1287 10 46 1 +1288 8 46 1 +1289 8 46 0 +1290 8 47 1 +1291 8 47 0 +1292 9 46 1 +1293 9 46 0 +1294 9 47 1 +1295 9 47 0 +1312 3 46 0 +1313 3 46 1 +1314 3 47 0 +1315 3 47 1 +1316 2 46 0 +1317 2 46 1 +1318 2 47 0 +1319 2 47 1 +1320 0 47 1 +1321 0 47 0 +1322 0 46 1 +1323 0 46 0 +1324 1 47 1 +1325 1 47 0 +1326 1 46 1 +1327 1 46 0 +1328 4 46 0 +1329 4 46 1 +1330 4 47 0 +1331 4 47 1 +1332 5 46 0 +1333 5 46 1 +1334 5 47 0 +1335 5 47 1 +1336 7 47 1 +1337 7 47 0 +1338 7 46 1 +1339 7 46 0 +1340 6 47 1 +1341 6 47 0 +1342 6 46 1 +1343 6 46 0 +1344 12 47 0 +1345 12 47 1 +1346 12 46 0 +1347 12 46 1 +1348 13 47 0 +1349 13 47 1 +1350 13 46 0 +1351 13 46 1 +1352 15 46 1 +1353 15 46 0 +1354 15 47 1 +1355 15 47 0 +1356 14 46 1 +1357 14 46 0 +1358 14 47 1 +1359 14 47 0 +1408 11 49 0 +1409 11 49 1 +1410 11 48 0 +1411 11 48 1 +1412 10 49 0 +1413 10 49 1 +1414 10 48 0 +1415 10 48 1 +1416 8 48 1 +1417 8 48 0 +1418 8 49 1 +1419 8 49 0 +1420 9 48 1 +1421 9 48 0 +1422 9 49 1 +1423 9 49 0 +1440 3 48 0 +1441 3 48 1 +1442 3 49 0 +1443 3 49 1 +1444 2 48 0 +1445 2 48 1 +1446 2 49 0 +1447 2 49 1 +1448 0 49 1 +1449 0 49 0 +1450 0 48 1 +1451 0 48 0 +1452 1 49 1 +1453 1 49 0 +1454 1 48 1 +1455 1 48 0 +1456 4 48 0 +1457 4 48 1 +1458 4 49 0 +1459 4 49 1 +1460 5 48 0 +1461 5 48 1 +1462 5 49 0 +1463 5 49 1 +1464 7 49 1 +1465 7 49 0 +1466 7 48 1 +1467 7 48 0 +1468 6 49 1 +1469 6 49 0 +1470 6 48 1 +1471 6 48 0 +1472 12 49 0 +1473 12 49 1 +1474 12 48 0 +1475 12 48 1 +1476 13 49 0 +1477 13 49 1 +1478 13 48 0 +1479 13 48 1 +1480 15 48 1 +1481 15 48 0 +1482 15 49 1 +1483 15 49 0 +1484 14 48 1 +1485 14 48 0 +1486 14 49 1 +1487 14 49 0 +1536 11 51 0 +1537 11 51 1 +1538 11 50 0 +1539 11 50 1 +1540 10 51 0 +1541 10 51 1 +1542 10 50 0 +1543 10 50 1 +1544 8 50 1 +1545 8 50 0 +1546 8 51 1 +1547 8 51 0 +1548 9 50 1 +1549 9 50 0 +1550 9 51 1 +1551 9 51 0 +1568 3 50 0 +1569 3 50 1 +1570 3 51 0 +1571 3 51 1 +1572 2 50 0 +1573 2 50 1 +1574 2 51 0 +1575 2 51 1 +1576 0 51 1 +1577 0 51 0 +1578 0 50 1 +1579 0 50 0 +1580 1 51 1 +1581 1 51 0 +1582 1 50 1 +1583 1 50 0 +1584 4 50 0 +1585 4 50 1 +1586 4 51 0 +1587 4 51 1 +1588 5 50 0 +1589 5 50 1 +1590 5 51 0 +1591 5 51 1 +1592 7 51 1 +1593 7 51 0 +1594 7 50 1 +1595 7 50 0 +1596 6 51 1 +1597 6 51 0 +1598 6 50 1 +1599 6 50 0 +1600 12 51 0 +1601 12 51 1 +1602 12 50 0 +1603 12 50 1 +1604 13 51 0 +1605 13 51 1 +1606 13 50 0 +1607 13 50 1 +1608 15 50 1 +1609 15 50 0 +1610 15 51 1 +1611 15 51 0 +1612 14 50 1 +1613 14 50 0 +1614 14 51 1 +1615 14 51 0 +1664 11 53 0 +1665 11 53 1 +1666 11 52 0 +1667 11 52 1 +1668 10 53 0 +1669 10 53 1 +1670 10 52 0 +1671 10 52 1 +1672 8 52 1 +1673 8 52 0 +1674 8 53 1 +1675 8 53 0 +1676 9 52 1 +1677 9 52 0 +1678 9 53 1 +1679 9 53 0 +1696 3 52 0 +1697 3 52 1 +1698 3 53 0 +1699 3 53 1 +1700 2 52 0 +1701 2 52 1 +1702 2 53 0 +1703 2 53 1 +1704 0 53 1 +1705 0 53 0 +1706 0 52 1 +1707 0 52 0 +1708 1 53 1 +1709 1 53 0 +1710 1 52 1 +1711 1 52 0 +1712 4 52 0 +1713 4 52 1 +1714 4 53 0 +1715 4 53 1 +1716 5 52 0 +1717 5 52 1 +1718 5 53 0 +1719 5 53 1 +1720 7 53 1 +1721 7 53 0 +1722 7 52 1 +1723 7 52 0 +1724 6 53 1 +1725 6 53 0 +1726 6 52 1 +1727 6 52 0 +1728 12 53 0 +1729 12 53 1 +1730 12 52 0 +1731 12 52 1 +1732 13 53 0 +1733 13 53 1 +1734 13 52 0 +1735 13 52 1 +1736 15 52 1 +1737 15 52 0 +1738 15 53 1 +1739 15 53 0 +1740 14 52 1 +1741 14 52 0 +1742 14 53 1 +1743 14 53 0 +1792 11 55 0 +1793 11 55 1 +1794 11 54 0 +1795 11 54 1 +1796 10 55 0 +1797 10 55 1 +1798 10 54 0 +1799 10 54 1 +1800 8 54 1 +1801 8 54 0 +1802 8 55 1 +1803 8 55 0 +1804 9 54 1 +1805 9 54 0 +1806 9 55 1 +1807 9 55 0 +1824 3 54 0 +1825 3 54 1 +1826 3 55 0 +1827 3 55 1 +1828 2 54 0 +1829 2 54 1 +1830 2 55 0 +1831 2 55 1 +1832 0 55 1 +1833 0 55 0 +1834 0 54 1 +1835 0 54 0 +1836 1 55 1 +1837 1 55 0 +1838 1 54 1 +1839 1 54 0 +1840 4 54 0 +1841 4 54 1 +1842 4 55 0 +1843 4 55 1 +1844 5 54 0 +1845 5 54 1 +1846 5 55 0 +1847 5 55 1 +1848 7 55 1 +1849 7 55 0 +1850 7 54 1 +1851 7 54 0 +1852 6 55 1 +1853 6 55 0 +1854 6 54 1 +1855 6 54 0 +1856 12 55 0 +1857 12 55 1 +1858 12 54 0 +1859 12 54 1 +1860 13 55 0 +1861 13 55 1 +1862 13 54 0 +1863 13 54 1 +1864 15 54 1 +1865 15 54 0 +1866 15 55 1 +1867 15 55 0 +1868 14 54 1 +1869 14 54 0 +1870 14 55 1 +1871 14 55 0 +2176 11 27 0 +2177 11 27 1 +2178 11 26 0 +2179 11 26 1 +2180 10 27 0 +2181 10 27 1 +2182 10 26 0 +2183 10 26 1 +2184 8 26 1 +2185 8 26 0 +2186 8 27 1 +2187 8 27 0 +2188 9 26 1 +2189 9 26 0 +2190 9 27 1 +2191 9 27 0 +2208 3 26 0 +2209 3 26 1 +2210 3 27 0 +2211 3 27 1 +2212 2 26 0 +2213 2 26 1 +2214 2 27 0 +2215 2 27 1 +2216 0 27 1 +2217 0 27 0 +2218 0 26 1 +2219 0 26 0 +2220 1 27 1 +2221 1 27 0 +2222 1 26 1 +2223 1 26 0 +2224 4 26 0 +2225 4 26 1 +2226 4 27 0 +2227 4 27 1 +2228 5 26 0 +2229 5 26 1 +2230 5 27 0 +2231 5 27 1 +2232 7 27 1 +2233 7 27 0 +2234 7 26 1 +2235 7 26 0 +2236 6 27 1 +2237 6 27 0 +2238 6 26 1 +2239 6 26 0 +2240 12 27 0 +2241 12 27 1 +2242 12 26 0 +2243 12 26 1 +2244 13 27 0 +2245 13 27 1 +2246 13 26 0 +2247 13 26 1 +2248 15 26 1 +2249 15 26 0 +2250 15 27 1 +2251 15 27 0 +2252 14 26 1 +2253 14 26 0 +2254 14 27 1 +2255 14 27 0 +2304 11 25 0 +2305 11 25 1 +2306 11 24 0 +2307 11 24 1 +2308 10 25 0 +2309 10 25 1 +2310 10 24 0 +2311 10 24 1 +2312 8 24 1 +2313 8 24 0 +2314 8 25 1 +2315 8 25 0 +2316 9 24 1 +2317 9 24 0 +2318 9 25 1 +2319 9 25 0 +2336 3 24 0 +2337 3 24 1 +2338 3 25 0 +2339 3 25 1 +2340 2 24 0 +2341 2 24 1 +2342 2 25 0 +2343 2 25 1 +2344 0 25 1 +2345 0 25 0 +2346 0 24 1 +2347 0 24 0 +2348 1 25 1 +2349 1 25 0 +2350 1 24 1 +2351 1 24 0 +2352 4 24 0 +2353 4 24 1 +2354 4 25 0 +2355 4 25 1 +2356 5 24 0 +2357 5 24 1 +2358 5 25 0 +2359 5 25 1 +2360 7 25 1 +2361 7 25 0 +2362 7 24 1 +2363 7 24 0 +2364 6 25 1 +2365 6 25 0 +2366 6 24 1 +2367 6 24 0 +2368 12 25 0 +2369 12 25 1 +2370 12 24 0 +2371 12 24 1 +2372 13 25 0 +2373 13 25 1 +2374 13 24 0 +2375 13 24 1 +2376 15 24 1 +2377 15 24 0 +2378 15 25 1 +2379 15 25 0 +2380 14 24 1 +2381 14 24 0 +2382 14 25 1 +2383 14 25 0 +2432 11 23 0 +2433 11 23 1 +2434 11 22 0 +2435 11 22 1 +2436 10 23 0 +2437 10 23 1 +2438 10 22 0 +2439 10 22 1 +2440 8 22 1 +2441 8 22 0 +2442 8 23 1 +2443 8 23 0 +2444 9 22 1 +2445 9 22 0 +2446 9 23 1 +2447 9 23 0 +2464 3 22 0 +2465 3 22 1 +2466 3 23 0 +2467 3 23 1 +2468 2 22 0 +2469 2 22 1 +2470 2 23 0 +2471 2 23 1 +2472 0 23 1 +2473 0 23 0 +2474 0 22 1 +2475 0 22 0 +2476 1 23 1 +2477 1 23 0 +2478 1 22 1 +2479 1 22 0 +2480 4 22 0 +2481 4 22 1 +2482 4 23 0 +2483 4 23 1 +2484 5 22 0 +2485 5 22 1 +2486 5 23 0 +2487 5 23 1 +2488 7 23 1 +2489 7 23 0 +2490 7 22 1 +2491 7 22 0 +2492 6 23 1 +2493 6 23 0 +2494 6 22 1 +2495 6 22 0 +2496 12 23 0 +2497 12 23 1 +2498 12 22 0 +2499 12 22 1 +2500 13 23 0 +2501 13 23 1 +2502 13 22 0 +2503 13 22 1 +2504 15 22 1 +2505 15 22 0 +2506 15 23 1 +2507 15 23 0 +2508 14 22 1 +2509 14 22 0 +2510 14 23 1 +2511 14 23 0 +2560 11 21 0 +2561 11 21 1 +2562 11 20 0 +2563 11 20 1 +2564 10 21 0 +2565 10 21 1 +2566 10 20 0 +2567 10 20 1 +2568 8 20 1 +2569 8 20 0 +2570 8 21 1 +2571 8 21 0 +2572 9 20 1 +2573 9 20 0 +2574 9 21 1 +2575 9 21 0 +2592 3 20 0 +2593 3 20 1 +2594 3 21 0 +2595 3 21 1 +2596 2 20 0 +2597 2 20 1 +2598 2 21 0 +2599 2 21 1 +2600 0 21 1 +2601 0 21 0 +2602 0 20 1 +2603 0 20 0 +2604 1 21 1 +2605 1 21 0 +2606 1 20 1 +2607 1 20 0 +2608 4 20 0 +2609 4 20 1 +2610 4 21 0 +2611 4 21 1 +2612 5 20 0 +2613 5 20 1 +2614 5 21 0 +2615 5 21 1 +2616 7 21 1 +2617 7 21 0 +2618 7 20 1 +2619 7 20 0 +2620 6 21 1 +2621 6 21 0 +2622 6 20 1 +2623 6 20 0 +2624 12 21 0 +2625 12 21 1 +2626 12 20 0 +2627 12 20 1 +2628 13 21 0 +2629 13 21 1 +2630 13 20 0 +2631 13 20 1 +2632 15 20 1 +2633 15 20 0 +2634 15 21 1 +2635 15 21 0 +2636 14 20 1 +2637 14 20 0 +2638 14 21 1 +2639 14 21 0 +2688 11 19 0 +2689 11 19 1 +2690 11 18 0 +2691 11 18 1 +2692 10 19 0 +2693 10 19 1 +2694 10 18 0 +2695 10 18 1 +2696 8 18 1 +2697 8 18 0 +2698 8 19 1 +2699 8 19 0 +2700 9 18 1 +2701 9 18 0 +2702 9 19 1 +2703 9 19 0 +2720 3 18 0 +2721 3 18 1 +2722 3 19 0 +2723 3 19 1 +2724 2 18 0 +2725 2 18 1 +2726 2 19 0 +2727 2 19 1 +2728 0 19 1 +2729 0 19 0 +2730 0 18 1 +2731 0 18 0 +2732 1 19 1 +2733 1 19 0 +2734 1 18 1 +2735 1 18 0 +2736 4 18 0 +2737 4 18 1 +2738 4 19 0 +2739 4 19 1 +2740 5 18 0 +2741 5 18 1 +2742 5 19 0 +2743 5 19 1 +2744 7 19 1 +2745 7 19 0 +2746 7 18 1 +2747 7 18 0 +2748 6 19 1 +2749 6 19 0 +2750 6 18 1 +2751 6 18 0 +2752 12 19 0 +2753 12 19 1 +2754 12 18 0 +2755 12 18 1 +2756 13 19 0 +2757 13 19 1 +2758 13 18 0 +2759 13 18 1 +2760 15 18 1 +2761 15 18 0 +2762 15 19 1 +2763 15 19 0 +2764 14 18 1 +2765 14 18 0 +2766 14 19 1 +2767 14 19 0 +2816 11 17 0 +2817 11 17 1 +2818 11 16 0 +2819 11 16 1 +2820 10 17 0 +2821 10 17 1 +2822 10 16 0 +2823 10 16 1 +2824 8 16 1 +2825 8 16 0 +2826 8 17 1 +2827 8 17 0 +2828 9 16 1 +2829 9 16 0 +2830 9 17 1 +2831 9 17 0 +2848 3 16 0 +2849 3 16 1 +2850 3 17 0 +2851 3 17 1 +2852 2 16 0 +2853 2 16 1 +2854 2 17 0 +2855 2 17 1 +2856 0 17 1 +2857 0 17 0 +2858 0 16 1 +2859 0 16 0 +2860 1 17 1 +2861 1 17 0 +2862 1 16 1 +2863 1 16 0 +2864 4 16 0 +2865 4 16 1 +2866 4 17 0 +2867 4 17 1 +2868 5 16 0 +2869 5 16 1 +2870 5 17 0 +2871 5 17 1 +2872 7 17 1 +2873 7 17 0 +2874 7 16 1 +2875 7 16 0 +2876 6 17 1 +2877 6 17 0 +2878 6 16 1 +2879 6 16 0 +2880 12 17 0 +2881 12 17 1 +2882 12 16 0 +2883 12 16 1 +2884 13 17 0 +2885 13 17 1 +2886 13 16 0 +2887 13 16 1 +2888 15 16 1 +2889 15 16 0 +2890 15 17 1 +2891 15 17 0 +2892 14 16 1 +2893 14 16 0 +2894 14 17 1 +2895 14 17 0 +2944 11 15 0 +2945 11 15 1 +2946 11 14 0 +2947 11 14 1 +2948 10 15 0 +2949 10 15 1 +2950 10 14 0 +2951 10 14 1 +2952 8 14 1 +2953 8 14 0 +2954 8 15 1 +2955 8 15 0 +2956 9 14 1 +2957 9 14 0 +2958 9 15 1 +2959 9 15 0 +2976 3 14 0 +2977 3 14 1 +2978 3 15 0 +2979 3 15 1 +2980 2 14 0 +2981 2 14 1 +2982 2 15 0 +2983 2 15 1 +2984 0 15 1 +2985 0 15 0 +2986 0 14 1 +2987 0 14 0 +2988 1 15 1 +2989 1 15 0 +2990 1 14 1 +2991 1 14 0 +2992 4 14 0 +2993 4 14 1 +2994 4 15 0 +2995 4 15 1 +2996 5 14 0 +2997 5 14 1 +2998 5 15 0 +2999 5 15 1 +3000 7 15 1 +3001 7 15 0 +3002 7 14 1 +3003 7 14 0 +3004 6 15 1 +3005 6 15 0 +3006 6 14 1 +3007 6 14 0 +3008 12 15 0 +3009 12 15 1 +3010 12 14 0 +3011 12 14 1 +3012 13 15 0 +3013 13 15 1 +3014 13 14 0 +3015 13 14 1 +3016 15 14 1 +3017 15 14 0 +3018 15 15 1 +3019 15 15 0 +3020 14 14 1 +3021 14 14 0 +3022 14 15 1 +3023 14 15 0 +3072 11 13 0 +3073 11 13 1 +3074 11 12 0 +3075 11 12 1 +3076 10 13 0 +3077 10 13 1 +3078 10 12 0 +3079 10 12 1 +3080 8 12 1 +3081 8 12 0 +3082 8 13 1 +3083 8 13 0 +3084 9 12 1 +3085 9 12 0 +3086 9 13 1 +3087 9 13 0 +3104 3 12 0 +3105 3 12 1 +3106 3 13 0 +3107 3 13 1 +3108 2 12 0 +3109 2 12 1 +3110 2 13 0 +3111 2 13 1 +3112 0 13 1 +3113 0 13 0 +3114 0 12 1 +3115 0 12 0 +3116 1 13 1 +3117 1 13 0 +3118 1 12 1 +3119 1 12 0 +3120 4 12 0 +3121 4 12 1 +3122 4 13 0 +3123 4 13 1 +3124 5 12 0 +3125 5 12 1 +3126 5 13 0 +3127 5 13 1 +3128 7 13 1 +3129 7 13 0 +3130 7 12 1 +3131 7 12 0 +3132 6 13 1 +3133 6 13 0 +3134 6 12 1 +3135 6 12 0 +3136 12 13 0 +3137 12 13 1 +3138 12 12 0 +3139 12 12 1 +3140 13 13 0 +3141 13 13 1 +3142 13 12 0 +3143 13 12 1 +3144 15 12 1 +3145 15 12 0 +3146 15 13 1 +3147 15 13 0 +3148 14 12 1 +3149 14 12 0 +3150 14 13 1 +3151 14 13 0 +3200 11 11 0 +3201 11 11 1 +3202 11 10 0 +3203 11 10 1 +3204 10 11 0 +3205 10 11 1 +3206 10 10 0 +3207 10 10 1 +3208 8 10 1 +3209 8 10 0 +3210 8 11 1 +3211 8 11 0 +3212 9 10 1 +3213 9 10 0 +3214 9 11 1 +3215 9 11 0 +3232 3 10 0 +3233 3 10 1 +3234 3 11 0 +3235 3 11 1 +3236 2 10 0 +3237 2 10 1 +3238 2 11 0 +3239 2 11 1 +3240 0 11 1 +3241 0 11 0 +3242 0 10 1 +3243 0 10 0 +3244 1 11 1 +3245 1 11 0 +3246 1 10 1 +3247 1 10 0 +3248 4 10 0 +3249 4 10 1 +3250 4 11 0 +3251 4 11 1 +3252 5 10 0 +3253 5 10 1 +3254 5 11 0 +3255 5 11 1 +3256 7 11 1 +3257 7 11 0 +3258 7 10 1 +3259 7 10 0 +3260 6 11 1 +3261 6 11 0 +3262 6 10 1 +3263 6 10 0 +3264 12 11 0 +3265 12 11 1 +3266 12 10 0 +3267 12 10 1 +3268 13 11 0 +3269 13 11 1 +3270 13 10 0 +3271 13 10 1 +3272 15 10 1 +3273 15 10 0 +3274 15 11 1 +3275 15 11 0 +3276 14 10 1 +3277 14 10 0 +3278 14 11 1 +3279 14 11 0 +3328 11 9 0 +3329 11 9 1 +3330 11 8 0 +3331 11 8 1 +3332 10 9 0 +3333 10 9 1 +3334 10 8 0 +3335 10 8 1 +3336 8 8 1 +3337 8 8 0 +3338 8 9 1 +3339 8 9 0 +3340 9 8 1 +3341 9 8 0 +3342 9 9 1 +3343 9 9 0 +3360 3 8 0 +3361 3 8 1 +3362 3 9 0 +3363 3 9 1 +3364 2 8 0 +3365 2 8 1 +3366 2 9 0 +3367 2 9 1 +3368 0 9 1 +3369 0 9 0 +3370 0 8 1 +3371 0 8 0 +3372 1 9 1 +3373 1 9 0 +3374 1 8 1 +3375 1 8 0 +3376 4 8 0 +3377 4 8 1 +3378 4 9 0 +3379 4 9 1 +3380 5 8 0 +3381 5 8 1 +3382 5 9 0 +3383 5 9 1 +3384 7 9 1 +3385 7 9 0 +3386 7 8 1 +3387 7 8 0 +3388 6 9 1 +3389 6 9 0 +3390 6 8 1 +3391 6 8 0 +3392 12 9 0 +3393 12 9 1 +3394 12 8 0 +3395 12 8 1 +3396 13 9 0 +3397 13 9 1 +3398 13 8 0 +3399 13 8 1 +3400 15 8 1 +3401 15 8 0 +3402 15 9 1 +3403 15 9 0 +3404 14 8 1 +3405 14 8 0 +3406 14 9 1 +3407 14 9 0 +3456 11 7 0 +3457 11 7 1 +3458 11 6 0 +3459 11 6 1 +3460 10 7 0 +3461 10 7 1 +3462 10 6 0 +3463 10 6 1 +3464 8 6 1 +3465 8 6 0 +3466 8 7 1 +3467 8 7 0 +3468 9 6 1 +3469 9 6 0 +3470 9 7 1 +3471 9 7 0 +3488 3 6 0 +3489 3 6 1 +3490 3 7 0 +3491 3 7 1 +3492 2 6 0 +3493 2 6 1 +3494 2 7 0 +3495 2 7 1 +3496 0 7 1 +3497 0 7 0 +3498 0 6 1 +3499 0 6 0 +3500 1 7 1 +3501 1 7 0 +3502 1 6 1 +3503 1 6 0 +3504 4 6 0 +3505 4 6 1 +3506 4 7 0 +3507 4 7 1 +3508 5 6 0 +3509 5 6 1 +3510 5 7 0 +3511 5 7 1 +3512 7 7 1 +3513 7 7 0 +3514 7 6 1 +3515 7 6 0 +3516 6 7 1 +3517 6 7 0 +3518 6 6 1 +3519 6 6 0 +3520 12 7 0 +3521 12 7 1 +3522 12 6 0 +3523 12 6 1 +3524 13 7 0 +3525 13 7 1 +3526 13 6 0 +3527 13 6 1 +3528 15 6 1 +3529 15 6 0 +3530 15 7 1 +3531 15 7 0 +3532 14 6 1 +3533 14 6 0 +3534 14 7 1 +3535 14 7 0 +3584 11 5 0 +3585 11 5 1 +3586 11 4 0 +3587 11 4 1 +3588 10 5 0 +3589 10 5 1 +3590 10 4 0 +3591 10 4 1 +3592 8 4 1 +3593 8 4 0 +3594 8 5 1 +3595 8 5 0 +3596 9 4 1 +3597 9 4 0 +3598 9 5 1 +3599 9 5 0 +3616 3 4 0 +3617 3 4 1 +3618 3 5 0 +3619 3 5 1 +3620 2 4 0 +3621 2 4 1 +3622 2 5 0 +3623 2 5 1 +3624 0 5 1 +3625 0 5 0 +3626 0 4 1 +3627 0 4 0 +3628 1 5 1 +3629 1 5 0 +3630 1 4 1 +3631 1 4 0 +3632 4 4 0 +3633 4 4 1 +3634 4 5 0 +3635 4 5 1 +3636 5 4 0 +3637 5 4 1 +3638 5 5 0 +3639 5 5 1 +3640 7 5 1 +3641 7 5 0 +3642 7 4 1 +3643 7 4 0 +3644 6 5 1 +3645 6 5 0 +3646 6 4 1 +3647 6 4 0 +3648 12 5 0 +3649 12 5 1 +3650 12 4 0 +3651 12 4 1 +3652 13 5 0 +3653 13 5 1 +3654 13 4 0 +3655 13 4 1 +3656 15 4 1 +3657 15 4 0 +3658 15 5 1 +3659 15 5 0 +3660 14 4 1 +3661 14 4 0 +3662 14 5 1 +3663 14 5 0 +3712 11 3 0 +3713 11 3 1 +3714 11 2 0 +3715 11 2 1 +3716 10 3 0 +3717 10 3 1 +3718 10 2 0 +3719 10 2 1 +3720 8 2 1 +3721 8 2 0 +3722 8 3 1 +3723 8 3 0 +3724 9 2 1 +3725 9 2 0 +3726 9 3 1 +3727 9 3 0 +3744 3 2 0 +3745 3 2 1 +3746 3 3 0 +3747 3 3 1 +3748 2 2 0 +3749 2 2 1 +3750 2 3 0 +3751 2 3 1 +3752 0 3 1 +3753 0 3 0 +3754 0 2 1 +3755 0 2 0 +3756 1 3 1 +3757 1 3 0 +3758 1 2 1 +3759 1 2 0 +3760 4 2 0 +3761 4 2 1 +3762 4 3 0 +3763 4 3 1 +3764 5 2 0 +3765 5 2 1 +3766 5 3 0 +3767 5 3 1 +3768 7 3 1 +3769 7 3 0 +3770 7 2 1 +3771 7 2 0 +3772 6 3 1 +3773 6 3 0 +3774 6 2 1 +3775 6 2 0 +3776 12 3 0 +3777 12 3 1 +3778 12 2 0 +3779 12 2 1 +3780 13 3 0 +3781 13 3 1 +3782 13 2 0 +3783 13 2 1 +3784 15 2 1 +3785 15 2 0 +3786 15 3 1 +3787 15 3 0 +3788 14 2 1 +3789 14 2 0 +3790 14 3 1 +3791 14 3 0 +3840 11 1 0 +3841 11 1 1 +3842 11 0 0 +3843 11 0 1 +3844 10 1 0 +3845 10 1 1 +3846 10 0 0 +3847 10 0 1 +3848 8 0 1 +3849 8 0 0 +3850 8 1 1 +3851 8 1 0 +3852 9 0 1 +3853 9 0 0 +3854 9 1 1 +3855 9 1 0 +3872 3 0 0 +3873 3 0 1 +3874 3 1 0 +3875 3 1 1 +3876 2 0 0 +3877 2 0 1 +3878 2 1 0 +3879 2 1 1 +3880 0 1 1 +3881 0 1 0 +3882 0 0 1 +3883 0 0 0 +3884 1 1 1 +3885 1 1 0 +3886 1 0 1 +3887 1 0 0 +3888 4 0 0 +3889 4 0 1 +3890 4 1 0 +3891 4 1 1 +3892 5 0 0 +3893 5 0 1 +3894 5 1 0 +3895 5 1 1 +3896 7 1 1 +3897 7 1 0 +3898 7 0 1 +3899 7 0 0 +3900 6 1 1 +3901 6 1 0 +3902 6 0 1 +3903 6 0 0 +3904 12 1 0 +3905 12 1 1 +3906 12 0 0 +3907 12 0 1 +3908 13 1 0 +3909 13 1 1 +3910 13 0 0 +3911 13 0 1 +3912 15 0 1 +3913 15 0 0 +3914 15 1 1 +3915 15 1 0 +3916 14 0 1 +3917 14 0 0 +3918 14 1 1 +3919 14 1 0 diff --git a/Detectors/PHOS/base/files/Mod3RCU1.data b/Detectors/PHOS/base/files/Mod3RCU1.data new file mode 100644 index 0000000000000..db125226496cf --- /dev/null +++ b/Detectors/PHOS/base/files/Mod3RCU1.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 1 0 2 + 1 1 1 2 + 2 1 2 2 + 3 1 3 2 + 4 1 4 2 + 5 1 5 2 + 6 1 6 2 + 7 1 7 2 + 8 1 8 2 + 9 1 9 2 + 10 1 10 2 + 11 1 11 2 + 12 1 12 2 + 13 1 13 2 + 14 1 14 2 + 15 1 15 2 + 16 1 16 2 + 17 1 17 2 + 18 1 18 2 + 19 1 19 2 + 20 1 20 2 + 21 1 21 2 + 22 1 22 2 + 23 1 23 2 + 24 1 24 2 + 25 1 25 2 + 26 1 26 2 + 27 1 27 2 + 28 1 28 2 + 29 1 29 2 + 30 1 30 2 + 31 1 31 2 + 32 1 32 2 + 33 1 33 2 + 34 1 34 2 + 35 1 35 2 + 36 1 36 2 + 37 1 37 2 + 38 1 38 2 + 39 1 39 2 + 40 1 40 2 + 41 1 41 2 + 42 1 42 2 + 43 1 43 2 + 44 1 44 2 + 45 1 45 2 + 46 1 46 2 + 47 1 47 2 + 48 1 48 2 + 49 1 49 2 + 50 1 50 2 + 51 1 51 2 + 52 1 52 2 + 53 1 53 2 + 54 1 54 2 + 55 1 55 2 + 56 1 56 2 + 57 1 57 2 + 58 1 58 2 + 59 1 59 2 + 60 1 60 2 + 61 1 61 2 + 62 1 62 2 + 63 1 63 2 + 64 1 64 2 + 65 1 65 2 + 66 1 66 2 + 67 1 67 2 + 68 1 68 2 + 69 1 69 2 + 70 1 70 2 + 71 1 71 2 + 72 1 72 2 + 73 1 73 2 + 74 1 74 2 + 75 1 75 2 + 76 1 76 2 + 77 1 77 2 + 78 1 78 2 + 79 1 79 2 + 80 1 80 2 + 81 1 81 2 + 82 1 82 2 + 83 1 83 2 + 84 1 84 2 + 85 1 85 2 + 86 1 86 2 + 87 1 87 2 + 88 1 88 2 + 89 1 89 2 + 90 1 90 2 + 91 1 91 2 + 92 1 92 2 + 93 1 93 2 + 94 1 94 2 + 95 1 95 2 + 96 1 96 2 + 97 1 97 2 + 98 1 98 2 + 99 1 99 2 + 100 1 100 2 + 101 1 101 2 + 102 1 102 2 + 103 1 103 2 + 104 1 104 2 + 105 1 105 2 + 106 1 106 2 + 107 1 107 2 + 108 1 108 2 + 109 1 109 2 + 110 1 110 2 + 111 1 111 2 + 112 1 112 2 + 113 1 113 2 + 114 1 114 2 + 115 1 115 2 + 116 1 116 2 + 117 1 117 2 + 118 1 118 2 + 119 1 119 2 + 120 1 120 2 + 121 1 121 2 + 122 1 122 2 + 123 1 123 2 + 124 1 124 2 + 125 1 125 2 + 126 1 126 2 + 127 1 127 2 +2048 1 2048 2 +2049 1 2049 2 +2050 1 2050 2 +2051 1 2051 2 +2052 1 2052 2 +2053 1 2053 2 +2054 1 2054 2 +2055 1 2055 2 +2056 1 2056 2 +2057 1 2057 2 +2058 1 2058 2 +2059 1 2059 2 +2060 1 2060 2 +2061 1 2061 2 +2062 1 2062 2 +2063 1 2063 2 +2064 1 2064 2 +2065 1 2065 2 +2066 1 2066 2 +2067 1 2067 2 +2068 1 2068 2 +2069 1 2069 2 +2070 1 2070 2 +2071 1 2071 2 +2072 1 2072 2 +2073 1 2073 2 +2074 1 2074 2 +2075 1 2075 2 +2076 1 2076 2 +2077 1 2077 2 +2078 1 2078 2 +2079 1 2079 2 +2080 1 2080 2 +2081 1 2081 2 +2082 1 2082 2 +2083 1 2083 2 +2084 1 2084 2 +2085 1 2085 2 +2086 1 2086 2 +2087 1 2087 2 +2088 1 2088 2 +2089 1 2089 2 +2090 1 2090 2 +2091 1 2091 2 +2092 1 2092 2 +2093 1 2093 2 +2094 1 2094 2 +2095 1 2095 2 +2096 1 2096 2 +2097 1 2097 2 +2098 1 2098 2 +2099 1 2099 2 +2100 1 2100 2 +2101 1 2101 2 +2102 1 2102 2 +2103 1 2103 2 +2104 1 2104 2 +2105 1 2105 2 +2106 1 2106 2 +2107 1 2107 2 +2108 1 2108 2 +2109 1 2109 2 +2110 1 2110 2 +2111 1 2111 2 +2112 1 2112 2 +2113 1 2113 2 +2114 1 2114 2 +2115 1 2115 2 +2116 1 2116 2 +2117 1 2117 2 +2118 1 2118 2 +2119 1 2119 2 +2120 1 2120 2 +2121 1 2121 2 +2122 1 2122 2 +2123 1 2123 2 +2124 1 2124 2 +2125 1 2125 2 +2126 1 2126 2 +2127 1 2127 2 +2128 1 2128 2 +2129 1 2129 2 +2130 1 2130 2 +2131 1 2131 2 +2132 1 2132 2 +2133 1 2133 2 +2134 1 2134 2 +2135 1 2135 2 +2136 1 2136 2 +2137 1 2137 2 +2138 1 2138 2 +2139 1 2139 2 +2140 1 2140 2 +2141 1 2141 2 +2142 1 2142 2 +2143 1 2143 2 +2144 1 2144 2 +2145 1 2145 2 +2146 1 2146 2 +2147 1 2147 2 +2148 1 2148 2 +2149 1 2149 2 +2150 1 2150 2 +2151 1 2151 2 +2152 1 2152 2 +2153 1 2153 2 +2154 1 2154 2 +2155 1 2155 2 +2156 1 2156 2 +2157 1 2157 2 +2158 1 2158 2 +2159 1 2159 2 +2160 1 2160 2 +2161 1 2161 2 +2162 1 2162 2 +2163 1 2163 2 +2164 1 2164 2 +2165 1 2165 2 +2166 1 2166 2 +2167 1 2167 2 +2168 1 2168 2 +2169 1 2169 2 +2170 1 2170 2 +2171 1 2171 2 +2172 1 2172 2 +2173 1 2173 2 +2174 1 2174 2 +2175 1 2175 2 + 128 27 29 0 + 129 27 29 1 + 130 27 28 0 + 131 27 28 1 + 132 26 29 0 + 133 26 29 1 + 134 26 28 0 + 135 26 28 1 + 136 24 28 1 + 137 24 28 0 + 138 24 29 1 + 139 24 29 0 + 140 25 28 1 + 141 25 28 0 + 142 25 29 1 + 143 25 29 0 + 160 19 28 0 + 161 19 28 1 + 162 19 29 0 + 163 19 29 1 + 164 18 28 0 + 165 18 28 1 + 166 18 29 0 + 167 18 29 1 + 168 16 29 1 + 169 16 29 0 + 170 16 28 1 + 171 16 28 0 + 172 17 29 1 + 173 17 29 0 + 174 17 28 1 + 175 17 28 0 + 176 20 28 0 + 177 20 28 1 + 178 20 29 0 + 179 20 29 1 + 180 21 28 0 + 181 21 28 1 + 182 21 29 0 + 183 21 29 1 + 184 23 29 1 + 185 23 29 0 + 186 23 28 1 + 187 23 28 0 + 188 22 29 1 + 189 22 29 0 + 190 22 28 1 + 191 22 28 0 + 192 28 29 0 + 193 28 29 1 + 194 28 28 0 + 195 28 28 1 + 196 29 29 0 + 197 29 29 1 + 198 29 28 0 + 199 29 28 1 + 200 31 28 1 + 201 31 28 0 + 202 31 29 1 + 203 31 29 0 + 204 30 28 1 + 205 30 28 0 + 206 30 29 1 + 207 30 29 0 + 256 27 31 0 + 257 27 31 1 + 258 27 30 0 + 259 27 30 1 + 260 26 31 0 + 261 26 31 1 + 262 26 30 0 + 263 26 30 1 + 264 24 30 1 + 265 24 30 0 + 266 24 31 1 + 267 24 31 0 + 268 25 30 1 + 269 25 30 0 + 270 25 31 1 + 271 25 31 0 + 288 19 30 0 + 289 19 30 1 + 290 19 31 0 + 291 19 31 1 + 292 18 30 0 + 293 18 30 1 + 294 18 31 0 + 295 18 31 1 + 296 16 31 1 + 297 16 31 0 + 298 16 30 1 + 299 16 30 0 + 300 17 31 1 + 301 17 31 0 + 302 17 30 1 + 303 17 30 0 + 304 20 30 0 + 305 20 30 1 + 306 20 31 0 + 307 20 31 1 + 308 21 30 0 + 309 21 30 1 + 310 21 31 0 + 311 21 31 1 + 312 23 31 1 + 313 23 31 0 + 314 23 30 1 + 315 23 30 0 + 316 22 31 1 + 317 22 31 0 + 318 22 30 1 + 319 22 30 0 + 320 28 31 0 + 321 28 31 1 + 322 28 30 0 + 323 28 30 1 + 324 29 31 0 + 325 29 31 1 + 326 29 30 0 + 327 29 30 1 + 328 31 30 1 + 329 31 30 0 + 330 31 31 1 + 331 31 31 0 + 332 30 30 1 + 333 30 30 0 + 334 30 31 1 + 335 30 31 0 + 384 27 33 0 + 385 27 33 1 + 386 27 32 0 + 387 27 32 1 + 388 26 33 0 + 389 26 33 1 + 390 26 32 0 + 391 26 32 1 + 392 24 32 1 + 393 24 32 0 + 394 24 33 1 + 395 24 33 0 + 396 25 32 1 + 397 25 32 0 + 398 25 33 1 + 399 25 33 0 + 416 19 32 0 + 417 19 32 1 + 418 19 33 0 + 419 19 33 1 + 420 18 32 0 + 421 18 32 1 + 422 18 33 0 + 423 18 33 1 + 424 16 33 1 + 425 16 33 0 + 426 16 32 1 + 427 16 32 0 + 428 17 33 1 + 429 17 33 0 + 430 17 32 1 + 431 17 32 0 + 432 20 32 0 + 433 20 32 1 + 434 20 33 0 + 435 20 33 1 + 436 21 32 0 + 437 21 32 1 + 438 21 33 0 + 439 21 33 1 + 440 23 33 1 + 441 23 33 0 + 442 23 32 1 + 443 23 32 0 + 444 22 33 1 + 445 22 33 0 + 446 22 32 1 + 447 22 32 0 + 448 28 33 0 + 449 28 33 1 + 450 28 32 0 + 451 28 32 1 + 452 29 33 0 + 453 29 33 1 + 454 29 32 0 + 455 29 32 1 + 456 31 32 1 + 457 31 32 0 + 458 31 33 1 + 459 31 33 0 + 460 30 32 1 + 461 30 32 0 + 462 30 33 1 + 463 30 33 0 + 512 27 35 0 + 513 27 35 1 + 514 27 34 0 + 515 27 34 1 + 516 26 35 0 + 517 26 35 1 + 518 26 34 0 + 519 26 34 1 + 520 24 34 1 + 521 24 34 0 + 522 24 35 1 + 523 24 35 0 + 524 25 34 1 + 525 25 34 0 + 526 25 35 1 + 527 25 35 0 + 544 19 34 0 + 545 19 34 1 + 546 19 35 0 + 547 19 35 1 + 548 18 34 0 + 549 18 34 1 + 550 18 35 0 + 551 18 35 1 + 552 16 35 1 + 553 16 35 0 + 554 16 34 1 + 555 16 34 0 + 556 17 35 1 + 557 17 35 0 + 558 17 34 1 + 559 17 34 0 + 560 20 34 0 + 561 20 34 1 + 562 20 35 0 + 563 20 35 1 + 564 21 34 0 + 565 21 34 1 + 566 21 35 0 + 567 21 35 1 + 568 23 35 1 + 569 23 35 0 + 570 23 34 1 + 571 23 34 0 + 572 22 35 1 + 573 22 35 0 + 574 22 34 1 + 575 22 34 0 + 576 28 35 0 + 577 28 35 1 + 578 28 34 0 + 579 28 34 1 + 580 29 35 0 + 581 29 35 1 + 582 29 34 0 + 583 29 34 1 + 584 31 34 1 + 585 31 34 0 + 586 31 35 1 + 587 31 35 0 + 588 30 34 1 + 589 30 34 0 + 590 30 35 1 + 591 30 35 0 + 640 27 37 0 + 641 27 37 1 + 642 27 36 0 + 643 27 36 1 + 644 26 37 0 + 645 26 37 1 + 646 26 36 0 + 647 26 36 1 + 648 24 36 1 + 649 24 36 0 + 650 24 37 1 + 651 24 37 0 + 652 25 36 1 + 653 25 36 0 + 654 25 37 1 + 655 25 37 0 + 672 19 36 0 + 673 19 36 1 + 674 19 37 0 + 675 19 37 1 + 676 18 36 0 + 677 18 36 1 + 678 18 37 0 + 679 18 37 1 + 680 16 37 1 + 681 16 37 0 + 682 16 36 1 + 683 16 36 0 + 684 17 37 1 + 685 17 37 0 + 686 17 36 1 + 687 17 36 0 + 688 20 36 0 + 689 20 36 1 + 690 20 37 0 + 691 20 37 1 + 692 21 36 0 + 693 21 36 1 + 694 21 37 0 + 695 21 37 1 + 696 23 37 1 + 697 23 37 0 + 698 23 36 1 + 699 23 36 0 + 700 22 37 1 + 701 22 37 0 + 702 22 36 1 + 703 22 36 0 + 704 28 37 0 + 705 28 37 1 + 706 28 36 0 + 707 28 36 1 + 708 29 37 0 + 709 29 37 1 + 710 29 36 0 + 711 29 36 1 + 712 31 36 1 + 713 31 36 0 + 714 31 37 1 + 715 31 37 0 + 716 30 36 1 + 717 30 36 0 + 718 30 37 1 + 719 30 37 0 + 768 27 39 0 + 769 27 39 1 + 770 27 38 0 + 771 27 38 1 + 772 26 39 0 + 773 26 39 1 + 774 26 38 0 + 775 26 38 1 + 776 24 38 1 + 777 24 38 0 + 778 24 39 1 + 779 24 39 0 + 780 25 38 1 + 781 25 38 0 + 782 25 39 1 + 783 25 39 0 + 800 19 38 0 + 801 19 38 1 + 802 19 39 0 + 803 19 39 1 + 804 18 38 0 + 805 18 38 1 + 806 18 39 0 + 807 18 39 1 + 808 16 39 1 + 809 16 39 0 + 810 16 38 1 + 811 16 38 0 + 812 17 39 1 + 813 17 39 0 + 814 17 38 1 + 815 17 38 0 + 816 20 38 0 + 817 20 38 1 + 818 20 39 0 + 819 20 39 1 + 820 21 38 0 + 821 21 38 1 + 822 21 39 0 + 823 21 39 1 + 824 23 39 1 + 825 23 39 0 + 826 23 38 1 + 827 23 38 0 + 828 22 39 1 + 829 22 39 0 + 830 22 38 1 + 831 22 38 0 + 832 28 39 0 + 833 28 39 1 + 834 28 38 0 + 835 28 38 1 + 836 29 39 0 + 837 29 39 1 + 838 29 38 0 + 839 29 38 1 + 840 31 38 1 + 841 31 38 0 + 842 31 39 1 + 843 31 39 0 + 844 30 38 1 + 845 30 38 0 + 846 30 39 1 + 847 30 39 0 + 896 27 41 0 + 897 27 41 1 + 898 27 40 0 + 899 27 40 1 + 900 26 41 0 + 901 26 41 1 + 902 26 40 0 + 903 26 40 1 + 904 24 40 1 + 905 24 40 0 + 906 24 41 1 + 907 24 41 0 + 908 25 40 1 + 909 25 40 0 + 910 25 41 1 + 911 25 41 0 + 928 19 40 0 + 929 19 40 1 + 930 19 41 0 + 931 19 41 1 + 932 18 40 0 + 933 18 40 1 + 934 18 41 0 + 935 18 41 1 + 936 16 41 1 + 937 16 41 0 + 938 16 40 1 + 939 16 40 0 + 940 17 41 1 + 941 17 41 0 + 942 17 40 1 + 943 17 40 0 + 944 20 40 0 + 945 20 40 1 + 946 20 41 0 + 947 20 41 1 + 948 21 40 0 + 949 21 40 1 + 950 21 41 0 + 951 21 41 1 + 952 23 41 1 + 953 23 41 0 + 954 23 40 1 + 955 23 40 0 + 956 22 41 1 + 957 22 41 0 + 958 22 40 1 + 959 22 40 0 + 960 28 41 0 + 961 28 41 1 + 962 28 40 0 + 963 28 40 1 + 964 29 41 0 + 965 29 41 1 + 966 29 40 0 + 967 29 40 1 + 968 31 40 1 + 969 31 40 0 + 970 31 41 1 + 971 31 41 0 + 972 30 40 1 + 973 30 40 0 + 974 30 41 1 + 975 30 41 0 +1024 27 43 0 +1025 27 43 1 +1026 27 42 0 +1027 27 42 1 +1028 26 43 0 +1029 26 43 1 +1030 26 42 0 +1031 26 42 1 +1032 24 42 1 +1033 24 42 0 +1034 24 43 1 +1035 24 43 0 +1036 25 42 1 +1037 25 42 0 +1038 25 43 1 +1039 25 43 0 +1056 19 42 0 +1057 19 42 1 +1058 19 43 0 +1059 19 43 1 +1060 18 42 0 +1061 18 42 1 +1062 18 43 0 +1063 18 43 1 +1064 16 43 1 +1065 16 43 0 +1066 16 42 1 +1067 16 42 0 +1068 17 43 1 +1069 17 43 0 +1070 17 42 1 +1071 17 42 0 +1072 20 42 0 +1073 20 42 1 +1074 20 43 0 +1075 20 43 1 +1076 21 42 0 +1077 21 42 1 +1078 21 43 0 +1079 21 43 1 +1080 23 43 1 +1081 23 43 0 +1082 23 42 1 +1083 23 42 0 +1084 22 43 1 +1085 22 43 0 +1086 22 42 1 +1087 22 42 0 +1088 28 43 0 +1089 28 43 1 +1090 28 42 0 +1091 28 42 1 +1092 29 43 0 +1093 29 43 1 +1094 29 42 0 +1095 29 42 1 +1096 31 42 1 +1097 31 42 0 +1098 31 43 1 +1099 31 43 0 +1100 30 42 1 +1101 30 42 0 +1102 30 43 1 +1103 30 43 0 +1152 27 45 0 +1153 27 45 1 +1154 27 44 0 +1155 27 44 1 +1156 26 45 0 +1157 26 45 1 +1158 26 44 0 +1159 26 44 1 +1160 24 44 1 +1161 24 44 0 +1162 24 45 1 +1163 24 45 0 +1164 25 44 1 +1165 25 44 0 +1166 25 45 1 +1167 25 45 0 +1184 19 44 0 +1185 19 44 1 +1186 19 45 0 +1187 19 45 1 +1188 18 44 0 +1189 18 44 1 +1190 18 45 0 +1191 18 45 1 +1192 16 45 1 +1193 16 45 0 +1194 16 44 1 +1195 16 44 0 +1196 17 45 1 +1197 17 45 0 +1198 17 44 1 +1199 17 44 0 +1200 20 44 0 +1201 20 44 1 +1202 20 45 0 +1203 20 45 1 +1204 21 44 0 +1205 21 44 1 +1206 21 45 0 +1207 21 45 1 +1208 23 45 1 +1209 23 45 0 +1210 23 44 1 +1211 23 44 0 +1212 22 45 1 +1213 22 45 0 +1214 22 44 1 +1215 22 44 0 +1216 28 45 0 +1217 28 45 1 +1218 28 44 0 +1219 28 44 1 +1220 29 45 0 +1221 29 45 1 +1222 29 44 0 +1223 29 44 1 +1224 31 44 1 +1225 31 44 0 +1226 31 45 1 +1227 31 45 0 +1228 30 44 1 +1229 30 44 0 +1230 30 45 1 +1231 30 45 0 +1280 27 47 0 +1281 27 47 1 +1282 27 46 0 +1283 27 46 1 +1284 26 47 0 +1285 26 47 1 +1286 26 46 0 +1287 26 46 1 +1288 24 46 1 +1289 24 46 0 +1290 24 47 1 +1291 24 47 0 +1292 25 46 1 +1293 25 46 0 +1294 25 47 1 +1295 25 47 0 +1312 19 46 0 +1313 19 46 1 +1314 19 47 0 +1315 19 47 1 +1316 18 46 0 +1317 18 46 1 +1318 18 47 0 +1319 18 47 1 +1320 16 47 1 +1321 16 47 0 +1322 16 46 1 +1323 16 46 0 +1324 17 47 1 +1325 17 47 0 +1326 17 46 1 +1327 17 46 0 +1328 20 46 0 +1329 20 46 1 +1330 20 47 0 +1331 20 47 1 +1332 21 46 0 +1333 21 46 1 +1334 21 47 0 +1335 21 47 1 +1336 23 47 1 +1337 23 47 0 +1338 23 46 1 +1339 23 46 0 +1340 22 47 1 +1341 22 47 0 +1342 22 46 1 +1343 22 46 0 +1344 28 47 0 +1345 28 47 1 +1346 28 46 0 +1347 28 46 1 +1348 29 47 0 +1349 29 47 1 +1350 29 46 0 +1351 29 46 1 +1352 31 46 1 +1353 31 46 0 +1354 31 47 1 +1355 31 47 0 +1356 30 46 1 +1357 30 46 0 +1358 30 47 1 +1359 30 47 0 +1408 27 49 0 +1409 27 49 1 +1410 27 48 0 +1411 27 48 1 +1412 26 49 0 +1413 26 49 1 +1414 26 48 0 +1415 26 48 1 +1416 24 48 1 +1417 24 48 0 +1418 24 49 1 +1419 24 49 0 +1420 25 48 1 +1421 25 48 0 +1422 25 49 1 +1423 25 49 0 +1440 19 48 0 +1441 19 48 1 +1442 19 49 0 +1443 19 49 1 +1444 18 48 0 +1445 18 48 1 +1446 18 49 0 +1447 18 49 1 +1448 16 49 1 +1449 16 49 0 +1450 16 48 1 +1451 16 48 0 +1452 17 49 1 +1453 17 49 0 +1454 17 48 1 +1455 17 48 0 +1456 20 48 0 +1457 20 48 1 +1458 20 49 0 +1459 20 49 1 +1460 21 48 0 +1461 21 48 1 +1462 21 49 0 +1463 21 49 1 +1464 23 49 1 +1465 23 49 0 +1466 23 48 1 +1467 23 48 0 +1468 22 49 1 +1469 22 49 0 +1470 22 48 1 +1471 22 48 0 +1472 28 49 0 +1473 28 49 1 +1474 28 48 0 +1475 28 48 1 +1476 29 49 0 +1477 29 49 1 +1478 29 48 0 +1479 29 48 1 +1480 31 48 1 +1481 31 48 0 +1482 31 49 1 +1483 31 49 0 +1484 30 48 1 +1485 30 48 0 +1486 30 49 1 +1487 30 49 0 +1536 27 51 0 +1537 27 51 1 +1538 27 50 0 +1539 27 50 1 +1540 26 51 0 +1541 26 51 1 +1542 26 50 0 +1543 26 50 1 +1544 24 50 1 +1545 24 50 0 +1546 24 51 1 +1547 24 51 0 +1548 25 50 1 +1549 25 50 0 +1550 25 51 1 +1551 25 51 0 +1568 19 50 0 +1569 19 50 1 +1570 19 51 0 +1571 19 51 1 +1572 18 50 0 +1573 18 50 1 +1574 18 51 0 +1575 18 51 1 +1576 16 51 1 +1577 16 51 0 +1578 16 50 1 +1579 16 50 0 +1580 17 51 1 +1581 17 51 0 +1582 17 50 1 +1583 17 50 0 +1584 20 50 0 +1585 20 50 1 +1586 20 51 0 +1587 20 51 1 +1588 21 50 0 +1589 21 50 1 +1590 21 51 0 +1591 21 51 1 +1592 23 51 1 +1593 23 51 0 +1594 23 50 1 +1595 23 50 0 +1596 22 51 1 +1597 22 51 0 +1598 22 50 1 +1599 22 50 0 +1600 28 51 0 +1601 28 51 1 +1602 28 50 0 +1603 28 50 1 +1604 29 51 0 +1605 29 51 1 +1606 29 50 0 +1607 29 50 1 +1608 31 50 1 +1609 31 50 0 +1610 31 51 1 +1611 31 51 0 +1612 30 50 1 +1613 30 50 0 +1614 30 51 1 +1615 30 51 0 +1664 27 53 0 +1665 27 53 1 +1666 27 52 0 +1667 27 52 1 +1668 26 53 0 +1669 26 53 1 +1670 26 52 0 +1671 26 52 1 +1672 24 52 1 +1673 24 52 0 +1674 24 53 1 +1675 24 53 0 +1676 25 52 1 +1677 25 52 0 +1678 25 53 1 +1679 25 53 0 +1696 19 52 0 +1697 19 52 1 +1698 19 53 0 +1699 19 53 1 +1700 18 52 0 +1701 18 52 1 +1702 18 53 0 +1703 18 53 1 +1704 16 53 1 +1705 16 53 0 +1706 16 52 1 +1707 16 52 0 +1708 17 53 1 +1709 17 53 0 +1710 17 52 1 +1711 17 52 0 +1712 20 52 0 +1713 20 52 1 +1714 20 53 0 +1715 20 53 1 +1716 21 52 0 +1717 21 52 1 +1718 21 53 0 +1719 21 53 1 +1720 23 53 1 +1721 23 53 0 +1722 23 52 1 +1723 23 52 0 +1724 22 53 1 +1725 22 53 0 +1726 22 52 1 +1727 22 52 0 +1728 28 53 0 +1729 28 53 1 +1730 28 52 0 +1731 28 52 1 +1732 29 53 0 +1733 29 53 1 +1734 29 52 0 +1735 29 52 1 +1736 31 52 1 +1737 31 52 0 +1738 31 53 1 +1739 31 53 0 +1740 30 52 1 +1741 30 52 0 +1742 30 53 1 +1743 30 53 0 +1792 27 55 0 +1793 27 55 1 +1794 27 54 0 +1795 27 54 1 +1796 26 55 0 +1797 26 55 1 +1798 26 54 0 +1799 26 54 1 +1800 24 54 1 +1801 24 54 0 +1802 24 55 1 +1803 24 55 0 +1804 25 54 1 +1805 25 54 0 +1806 25 55 1 +1807 25 55 0 +1824 19 54 0 +1825 19 54 1 +1826 19 55 0 +1827 19 55 1 +1828 18 54 0 +1829 18 54 1 +1830 18 55 0 +1831 18 55 1 +1832 16 55 1 +1833 16 55 0 +1834 16 54 1 +1835 16 54 0 +1836 17 55 1 +1837 17 55 0 +1838 17 54 1 +1839 17 54 0 +1840 20 54 0 +1841 20 54 1 +1842 20 55 0 +1843 20 55 1 +1844 21 54 0 +1845 21 54 1 +1846 21 55 0 +1847 21 55 1 +1848 23 55 1 +1849 23 55 0 +1850 23 54 1 +1851 23 54 0 +1852 22 55 1 +1853 22 55 0 +1854 22 54 1 +1855 22 54 0 +1856 28 55 0 +1857 28 55 1 +1858 28 54 0 +1859 28 54 1 +1860 29 55 0 +1861 29 55 1 +1862 29 54 0 +1863 29 54 1 +1864 31 54 1 +1865 31 54 0 +1866 31 55 1 +1867 31 55 0 +1868 30 54 1 +1869 30 54 0 +1870 30 55 1 +1871 30 55 0 +2176 27 27 0 +2177 27 27 1 +2178 27 26 0 +2179 27 26 1 +2180 26 27 0 +2181 26 27 1 +2182 26 26 0 +2183 26 26 1 +2184 24 26 1 +2185 24 26 0 +2186 24 27 1 +2187 24 27 0 +2188 25 26 1 +2189 25 26 0 +2190 25 27 1 +2191 25 27 0 +2208 19 26 0 +2209 19 26 1 +2210 19 27 0 +2211 19 27 1 +2212 18 26 0 +2213 18 26 1 +2214 18 27 0 +2215 18 27 1 +2216 16 27 1 +2217 16 27 0 +2218 16 26 1 +2219 16 26 0 +2220 17 27 1 +2221 17 27 0 +2222 17 26 1 +2223 17 26 0 +2224 20 26 0 +2225 20 26 1 +2226 20 27 0 +2227 20 27 1 +2228 21 26 0 +2229 21 26 1 +2230 21 27 0 +2231 21 27 1 +2232 23 27 1 +2233 23 27 0 +2234 23 26 1 +2235 23 26 0 +2236 22 27 1 +2237 22 27 0 +2238 22 26 1 +2239 22 26 0 +2240 28 27 0 +2241 28 27 1 +2242 28 26 0 +2243 28 26 1 +2244 29 27 0 +2245 29 27 1 +2246 29 26 0 +2247 29 26 1 +2248 31 26 1 +2249 31 26 0 +2250 31 27 1 +2251 31 27 0 +2252 30 26 1 +2253 30 26 0 +2254 30 27 1 +2255 30 27 0 +2304 27 25 0 +2305 27 25 1 +2306 27 24 0 +2307 27 24 1 +2308 26 25 0 +2309 26 25 1 +2310 26 24 0 +2311 26 24 1 +2312 24 24 1 +2313 24 24 0 +2314 24 25 1 +2315 24 25 0 +2316 25 24 1 +2317 25 24 0 +2318 25 25 1 +2319 25 25 0 +2336 19 24 0 +2337 19 24 1 +2338 19 25 0 +2339 19 25 1 +2340 18 24 0 +2341 18 24 1 +2342 18 25 0 +2343 18 25 1 +2344 16 25 1 +2345 16 25 0 +2346 16 24 1 +2347 16 24 0 +2348 17 25 1 +2349 17 25 0 +2350 17 24 1 +2351 17 24 0 +2352 20 24 0 +2353 20 24 1 +2354 20 25 0 +2355 20 25 1 +2356 21 24 0 +2357 21 24 1 +2358 21 25 0 +2359 21 25 1 +2360 23 25 1 +2361 23 25 0 +2362 23 24 1 +2363 23 24 0 +2364 22 25 1 +2365 22 25 0 +2366 22 24 1 +2367 22 24 0 +2368 28 25 0 +2369 28 25 1 +2370 28 24 0 +2371 28 24 1 +2372 29 25 0 +2373 29 25 1 +2374 29 24 0 +2375 29 24 1 +2376 31 24 1 +2377 31 24 0 +2378 31 25 1 +2379 31 25 0 +2380 30 24 1 +2381 30 24 0 +2382 30 25 1 +2383 30 25 0 +2432 27 23 0 +2433 27 23 1 +2434 27 22 0 +2435 27 22 1 +2436 26 23 0 +2437 26 23 1 +2438 26 22 0 +2439 26 22 1 +2440 24 22 1 +2441 24 22 0 +2442 24 23 1 +2443 24 23 0 +2444 25 22 1 +2445 25 22 0 +2446 25 23 1 +2447 25 23 0 +2464 19 22 0 +2465 19 22 1 +2466 19 23 0 +2467 19 23 1 +2468 18 22 0 +2469 18 22 1 +2470 18 23 0 +2471 18 23 1 +2472 16 23 1 +2473 16 23 0 +2474 16 22 1 +2475 16 22 0 +2476 17 23 1 +2477 17 23 0 +2478 17 22 1 +2479 17 22 0 +2480 20 22 0 +2481 20 22 1 +2482 20 23 0 +2483 20 23 1 +2484 21 22 0 +2485 21 22 1 +2486 21 23 0 +2487 21 23 1 +2488 23 23 1 +2489 23 23 0 +2490 23 22 1 +2491 23 22 0 +2492 22 23 1 +2493 22 23 0 +2494 22 22 1 +2495 22 22 0 +2496 28 23 0 +2497 28 23 1 +2498 28 22 0 +2499 28 22 1 +2500 29 23 0 +2501 29 23 1 +2502 29 22 0 +2503 29 22 1 +2504 31 22 1 +2505 31 22 0 +2506 31 23 1 +2507 31 23 0 +2508 30 22 1 +2509 30 22 0 +2510 30 23 1 +2511 30 23 0 +2560 27 21 0 +2561 27 21 1 +2562 27 20 0 +2563 27 20 1 +2564 26 21 0 +2565 26 21 1 +2566 26 20 0 +2567 26 20 1 +2568 24 20 1 +2569 24 20 0 +2570 24 21 1 +2571 24 21 0 +2572 25 20 1 +2573 25 20 0 +2574 25 21 1 +2575 25 21 0 +2592 19 20 0 +2593 19 20 1 +2594 19 21 0 +2595 19 21 1 +2596 18 20 0 +2597 18 20 1 +2598 18 21 0 +2599 18 21 1 +2600 16 21 1 +2601 16 21 0 +2602 16 20 1 +2603 16 20 0 +2604 17 21 1 +2605 17 21 0 +2606 17 20 1 +2607 17 20 0 +2608 20 20 0 +2609 20 20 1 +2610 20 21 0 +2611 20 21 1 +2612 21 20 0 +2613 21 20 1 +2614 21 21 0 +2615 21 21 1 +2616 23 21 1 +2617 23 21 0 +2618 23 20 1 +2619 23 20 0 +2620 22 21 1 +2621 22 21 0 +2622 22 20 1 +2623 22 20 0 +2624 28 21 0 +2625 28 21 1 +2626 28 20 0 +2627 28 20 1 +2628 29 21 0 +2629 29 21 1 +2630 29 20 0 +2631 29 20 1 +2632 31 20 1 +2633 31 20 0 +2634 31 21 1 +2635 31 21 0 +2636 30 20 1 +2637 30 20 0 +2638 30 21 1 +2639 30 21 0 +2688 27 19 0 +2689 27 19 1 +2690 27 18 0 +2691 27 18 1 +2692 26 19 0 +2693 26 19 1 +2694 26 18 0 +2695 26 18 1 +2696 24 18 1 +2697 24 18 0 +2698 24 19 1 +2699 24 19 0 +2700 25 18 1 +2701 25 18 0 +2702 25 19 1 +2703 25 19 0 +2720 19 18 0 +2721 19 18 1 +2722 19 19 0 +2723 19 19 1 +2724 18 18 0 +2725 18 18 1 +2726 18 19 0 +2727 18 19 1 +2728 16 19 1 +2729 16 19 0 +2730 16 18 1 +2731 16 18 0 +2732 17 19 1 +2733 17 19 0 +2734 17 18 1 +2735 17 18 0 +2736 20 18 0 +2737 20 18 1 +2738 20 19 0 +2739 20 19 1 +2740 21 18 0 +2741 21 18 1 +2742 21 19 0 +2743 21 19 1 +2744 23 19 1 +2745 23 19 0 +2746 23 18 1 +2747 23 18 0 +2748 22 19 1 +2749 22 19 0 +2750 22 18 1 +2751 22 18 0 +2752 28 19 0 +2753 28 19 1 +2754 28 18 0 +2755 28 18 1 +2756 29 19 0 +2757 29 19 1 +2758 29 18 0 +2759 29 18 1 +2760 31 18 1 +2761 31 18 0 +2762 31 19 1 +2763 31 19 0 +2764 30 18 1 +2765 30 18 0 +2766 30 19 1 +2767 30 19 0 +2816 27 17 0 +2817 27 17 1 +2818 27 16 0 +2819 27 16 1 +2820 26 17 0 +2821 26 17 1 +2822 26 16 0 +2823 26 16 1 +2824 24 16 1 +2825 24 16 0 +2826 24 17 1 +2827 24 17 0 +2828 25 16 1 +2829 25 16 0 +2830 25 17 1 +2831 25 17 0 +2848 19 16 0 +2849 19 16 1 +2850 19 17 0 +2851 19 17 1 +2852 18 16 0 +2853 18 16 1 +2854 18 17 0 +2855 18 17 1 +2856 16 17 1 +2857 16 17 0 +2858 16 16 1 +2859 16 16 0 +2860 17 17 1 +2861 17 17 0 +2862 17 16 1 +2863 17 16 0 +2864 20 16 0 +2865 20 16 1 +2866 20 17 0 +2867 20 17 1 +2868 21 16 0 +2869 21 16 1 +2870 21 17 0 +2871 21 17 1 +2872 23 17 1 +2873 23 17 0 +2874 23 16 1 +2875 23 16 0 +2876 22 17 1 +2877 22 17 0 +2878 22 16 1 +2879 22 16 0 +2880 28 17 0 +2881 28 17 1 +2882 28 16 0 +2883 28 16 1 +2884 29 17 0 +2885 29 17 1 +2886 29 16 0 +2887 29 16 1 +2888 31 16 1 +2889 31 16 0 +2890 31 17 1 +2891 31 17 0 +2892 30 16 1 +2893 30 16 0 +2894 30 17 1 +2895 30 17 0 +2944 27 15 0 +2945 27 15 1 +2946 27 14 0 +2947 27 14 1 +2948 26 15 0 +2949 26 15 1 +2950 26 14 0 +2951 26 14 1 +2952 24 14 1 +2953 24 14 0 +2954 24 15 1 +2955 24 15 0 +2956 25 14 1 +2957 25 14 0 +2958 25 15 1 +2959 25 15 0 +2976 19 14 0 +2977 19 14 1 +2978 19 15 0 +2979 19 15 1 +2980 18 14 0 +2981 18 14 1 +2982 18 15 0 +2983 18 15 1 +2984 16 15 1 +2985 16 15 0 +2986 16 14 1 +2987 16 14 0 +2988 17 15 1 +2989 17 15 0 +2990 17 14 1 +2991 17 14 0 +2992 20 14 0 +2993 20 14 1 +2994 20 15 0 +2995 20 15 1 +2996 21 14 0 +2997 21 14 1 +2998 21 15 0 +2999 21 15 1 +3000 23 15 1 +3001 23 15 0 +3002 23 14 1 +3003 23 14 0 +3004 22 15 1 +3005 22 15 0 +3006 22 14 1 +3007 22 14 0 +3008 28 15 0 +3009 28 15 1 +3010 28 14 0 +3011 28 14 1 +3012 29 15 0 +3013 29 15 1 +3014 29 14 0 +3015 29 14 1 +3016 31 14 1 +3017 31 14 0 +3018 31 15 1 +3019 31 15 0 +3020 30 14 1 +3021 30 14 0 +3022 30 15 1 +3023 30 15 0 +3072 27 13 0 +3073 27 13 1 +3074 27 12 0 +3075 27 12 1 +3076 26 13 0 +3077 26 13 1 +3078 26 12 0 +3079 26 12 1 +3080 24 12 1 +3081 24 12 0 +3082 24 13 1 +3083 24 13 0 +3084 25 12 1 +3085 25 12 0 +3086 25 13 1 +3087 25 13 0 +3104 19 12 0 +3105 19 12 1 +3106 19 13 0 +3107 19 13 1 +3108 18 12 0 +3109 18 12 1 +3110 18 13 0 +3111 18 13 1 +3112 16 13 1 +3113 16 13 0 +3114 16 12 1 +3115 16 12 0 +3116 17 13 1 +3117 17 13 0 +3118 17 12 1 +3119 17 12 0 +3120 20 12 0 +3121 20 12 1 +3122 20 13 0 +3123 20 13 1 +3124 21 12 0 +3125 21 12 1 +3126 21 13 0 +3127 21 13 1 +3128 23 13 1 +3129 23 13 0 +3130 23 12 1 +3131 23 12 0 +3132 22 13 1 +3133 22 13 0 +3134 22 12 1 +3135 22 12 0 +3136 28 13 0 +3137 28 13 1 +3138 28 12 0 +3139 28 12 1 +3140 29 13 0 +3141 29 13 1 +3142 29 12 0 +3143 29 12 1 +3144 31 12 1 +3145 31 12 0 +3146 31 13 1 +3147 31 13 0 +3148 30 12 1 +3149 30 12 0 +3150 30 13 1 +3151 30 13 0 +3200 27 11 0 +3201 27 11 1 +3202 27 10 0 +3203 27 10 1 +3204 26 11 0 +3205 26 11 1 +3206 26 10 0 +3207 26 10 1 +3208 24 10 1 +3209 24 10 0 +3210 24 11 1 +3211 24 11 0 +3212 25 10 1 +3213 25 10 0 +3214 25 11 1 +3215 25 11 0 +3232 19 10 0 +3233 19 10 1 +3234 19 11 0 +3235 19 11 1 +3236 18 10 0 +3237 18 10 1 +3238 18 11 0 +3239 18 11 1 +3240 16 11 1 +3241 16 11 0 +3242 16 10 1 +3243 16 10 0 +3244 17 11 1 +3245 17 11 0 +3246 17 10 1 +3247 17 10 0 +3248 20 10 0 +3249 20 10 1 +3250 20 11 0 +3251 20 11 1 +3252 21 10 0 +3253 21 10 1 +3254 21 11 0 +3255 21 11 1 +3256 23 11 1 +3257 23 11 0 +3258 23 10 1 +3259 23 10 0 +3260 22 11 1 +3261 22 11 0 +3262 22 10 1 +3263 22 10 0 +3264 28 11 0 +3265 28 11 1 +3266 28 10 0 +3267 28 10 1 +3268 29 11 0 +3269 29 11 1 +3270 29 10 0 +3271 29 10 1 +3272 31 10 1 +3273 31 10 0 +3274 31 11 1 +3275 31 11 0 +3276 30 10 1 +3277 30 10 0 +3278 30 11 1 +3279 30 11 0 +3328 27 9 0 +3329 27 9 1 +3330 27 8 0 +3331 27 8 1 +3332 26 9 0 +3333 26 9 1 +3334 26 8 0 +3335 26 8 1 +3336 24 8 1 +3337 24 8 0 +3338 24 9 1 +3339 24 9 0 +3340 25 8 1 +3341 25 8 0 +3342 25 9 1 +3343 25 9 0 +3360 19 8 0 +3361 19 8 1 +3362 19 9 0 +3363 19 9 1 +3364 18 8 0 +3365 18 8 1 +3366 18 9 0 +3367 18 9 1 +3368 16 9 1 +3369 16 9 0 +3370 16 8 1 +3371 16 8 0 +3372 17 9 1 +3373 17 9 0 +3374 17 8 1 +3375 17 8 0 +3376 20 8 0 +3377 20 8 1 +3378 20 9 0 +3379 20 9 1 +3380 21 8 0 +3381 21 8 1 +3382 21 9 0 +3383 21 9 1 +3384 23 9 1 +3385 23 9 0 +3386 23 8 1 +3387 23 8 0 +3388 22 9 1 +3389 22 9 0 +3390 22 8 1 +3391 22 8 0 +3392 28 9 0 +3393 28 9 1 +3394 28 8 0 +3395 28 8 1 +3396 29 9 0 +3397 29 9 1 +3398 29 8 0 +3399 29 8 1 +3400 31 8 1 +3401 31 8 0 +3402 31 9 1 +3403 31 9 0 +3404 30 8 1 +3405 30 8 0 +3406 30 9 1 +3407 30 9 0 +3456 27 7 0 +3457 27 7 1 +3458 27 6 0 +3459 27 6 1 +3460 26 7 0 +3461 26 7 1 +3462 26 6 0 +3463 26 6 1 +3464 24 6 1 +3465 24 6 0 +3466 24 7 1 +3467 24 7 0 +3468 25 6 1 +3469 25 6 0 +3470 25 7 1 +3471 25 7 0 +3488 19 6 0 +3489 19 6 1 +3490 19 7 0 +3491 19 7 1 +3492 18 6 0 +3493 18 6 1 +3494 18 7 0 +3495 18 7 1 +3496 16 7 1 +3497 16 7 0 +3498 16 6 1 +3499 16 6 0 +3500 17 7 1 +3501 17 7 0 +3502 17 6 1 +3503 17 6 0 +3504 20 6 0 +3505 20 6 1 +3506 20 7 0 +3507 20 7 1 +3508 21 6 0 +3509 21 6 1 +3510 21 7 0 +3511 21 7 1 +3512 23 7 1 +3513 23 7 0 +3514 23 6 1 +3515 23 6 0 +3516 22 7 1 +3517 22 7 0 +3518 22 6 1 +3519 22 6 0 +3520 28 7 0 +3521 28 7 1 +3522 28 6 0 +3523 28 6 1 +3524 29 7 0 +3525 29 7 1 +3526 29 6 0 +3527 29 6 1 +3528 31 6 1 +3529 31 6 0 +3530 31 7 1 +3531 31 7 0 +3532 30 6 1 +3533 30 6 0 +3534 30 7 1 +3535 30 7 0 +3584 27 5 0 +3585 27 5 1 +3586 27 4 0 +3587 27 4 1 +3588 26 5 0 +3589 26 5 1 +3590 26 4 0 +3591 26 4 1 +3592 24 4 1 +3593 24 4 0 +3594 24 5 1 +3595 24 5 0 +3596 25 4 1 +3597 25 4 0 +3598 25 5 1 +3599 25 5 0 +3616 19 4 0 +3617 19 4 1 +3618 19 5 0 +3619 19 5 1 +3620 18 4 0 +3621 18 4 1 +3622 18 5 0 +3623 18 5 1 +3624 16 5 1 +3625 16 5 0 +3626 16 4 1 +3627 16 4 0 +3628 17 5 1 +3629 17 5 0 +3630 17 4 1 +3631 17 4 0 +3632 20 4 0 +3633 20 4 1 +3634 20 5 0 +3635 20 5 1 +3636 21 4 0 +3637 21 4 1 +3638 21 5 0 +3639 21 5 1 +3640 23 5 1 +3641 23 5 0 +3642 23 4 1 +3643 23 4 0 +3644 22 5 1 +3645 22 5 0 +3646 22 4 1 +3647 22 4 0 +3648 28 5 0 +3649 28 5 1 +3650 28 4 0 +3651 28 4 1 +3652 29 5 0 +3653 29 5 1 +3654 29 4 0 +3655 29 4 1 +3656 31 4 1 +3657 31 4 0 +3658 31 5 1 +3659 31 5 0 +3660 30 4 1 +3661 30 4 0 +3662 30 5 1 +3663 30 5 0 +3712 27 3 0 +3713 27 3 1 +3714 27 2 0 +3715 27 2 1 +3716 26 3 0 +3717 26 3 1 +3718 26 2 0 +3719 26 2 1 +3720 24 2 1 +3721 24 2 0 +3722 24 3 1 +3723 24 3 0 +3724 25 2 1 +3725 25 2 0 +3726 25 3 1 +3727 25 3 0 +3744 19 2 0 +3745 19 2 1 +3746 19 3 0 +3747 19 3 1 +3748 18 2 0 +3749 18 2 1 +3750 18 3 0 +3751 18 3 1 +3752 16 3 1 +3753 16 3 0 +3754 16 2 1 +3755 16 2 0 +3756 17 3 1 +3757 17 3 0 +3758 17 2 1 +3759 17 2 0 +3760 20 2 0 +3761 20 2 1 +3762 20 3 0 +3763 20 3 1 +3764 21 2 0 +3765 21 2 1 +3766 21 3 0 +3767 21 3 1 +3768 23 3 1 +3769 23 3 0 +3770 23 2 1 +3771 23 2 0 +3772 22 3 1 +3773 22 3 0 +3774 22 2 1 +3775 22 2 0 +3776 28 3 0 +3777 28 3 1 +3778 28 2 0 +3779 28 2 1 +3780 29 3 0 +3781 29 3 1 +3782 29 2 0 +3783 29 2 1 +3784 31 2 1 +3785 31 2 0 +3786 31 3 1 +3787 31 3 0 +3788 30 2 1 +3789 30 2 0 +3790 30 3 1 +3791 30 3 0 +3840 27 1 0 +3841 27 1 1 +3842 27 0 0 +3843 27 0 1 +3844 26 1 0 +3845 26 1 1 +3846 26 0 0 +3847 26 0 1 +3848 24 0 1 +3849 24 0 0 +3850 24 1 1 +3851 24 1 0 +3852 25 0 1 +3853 25 0 0 +3854 25 1 1 +3855 25 1 0 +3872 19 0 0 +3873 19 0 1 +3874 19 1 0 +3875 19 1 1 +3876 18 0 0 +3877 18 0 1 +3878 18 1 0 +3879 18 1 1 +3880 16 1 1 +3881 16 1 0 +3882 16 0 1 +3883 16 0 0 +3884 17 1 1 +3885 17 1 0 +3886 17 0 1 +3887 17 0 0 +3888 20 0 0 +3889 20 0 1 +3890 20 1 0 +3891 20 1 1 +3892 21 0 0 +3893 21 0 1 +3894 21 1 0 +3895 21 1 1 +3896 23 1 1 +3897 23 1 0 +3898 23 0 1 +3899 23 0 0 +3900 22 1 1 +3901 22 1 0 +3902 22 0 1 +3903 22 0 0 +3904 28 1 0 +3905 28 1 1 +3906 28 0 0 +3907 28 0 1 +3908 29 1 0 +3909 29 1 1 +3910 29 0 0 +3911 29 0 1 +3912 31 0 1 +3913 31 0 0 +3914 31 1 1 +3915 31 1 0 +3916 30 0 1 +3917 30 0 0 +3918 30 1 1 +3919 30 1 0 diff --git a/Detectors/PHOS/base/files/Mod3RCU2.data b/Detectors/PHOS/base/files/Mod3RCU2.data new file mode 100644 index 0000000000000..21de94d7ace53 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod3RCU2.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 2 0 2 + 1 2 1 2 + 2 2 2 2 + 3 2 3 2 + 4 2 4 2 + 5 2 5 2 + 6 2 6 2 + 7 2 7 2 + 8 2 8 2 + 9 2 9 2 + 10 2 10 2 + 11 2 11 2 + 12 2 12 2 + 13 2 13 2 + 14 2 14 2 + 15 2 15 2 + 16 2 16 2 + 17 2 17 2 + 18 2 18 2 + 19 2 19 2 + 20 2 20 2 + 21 2 21 2 + 22 2 22 2 + 23 2 23 2 + 24 2 24 2 + 25 2 25 2 + 26 2 26 2 + 27 2 27 2 + 28 2 28 2 + 29 2 29 2 + 30 2 30 2 + 31 2 31 2 + 32 2 32 2 + 33 2 33 2 + 34 2 34 2 + 35 2 35 2 + 36 2 36 2 + 37 2 37 2 + 38 2 38 2 + 39 2 39 2 + 40 2 40 2 + 41 2 41 2 + 42 2 42 2 + 43 2 43 2 + 44 2 44 2 + 45 2 45 2 + 46 2 46 2 + 47 2 47 2 + 48 2 48 2 + 49 2 49 2 + 50 2 50 2 + 51 2 51 2 + 52 2 52 2 + 53 2 53 2 + 54 2 54 2 + 55 2 55 2 + 56 2 56 2 + 57 2 57 2 + 58 2 58 2 + 59 2 59 2 + 60 2 60 2 + 61 2 61 2 + 62 2 62 2 + 63 2 63 2 + 64 2 64 2 + 65 2 65 2 + 66 2 66 2 + 67 2 67 2 + 68 2 68 2 + 69 2 69 2 + 70 2 70 2 + 71 2 71 2 + 72 2 72 2 + 73 2 73 2 + 74 2 74 2 + 75 2 75 2 + 76 2 76 2 + 77 2 77 2 + 78 2 78 2 + 79 2 79 2 + 80 2 80 2 + 81 2 81 2 + 82 2 82 2 + 83 2 83 2 + 84 2 84 2 + 85 2 85 2 + 86 2 86 2 + 87 2 87 2 + 88 2 88 2 + 89 2 89 2 + 90 2 90 2 + 91 2 91 2 + 92 2 92 2 + 93 2 93 2 + 94 2 94 2 + 95 2 95 2 + 96 2 96 2 + 97 2 97 2 + 98 2 98 2 + 99 2 99 2 + 100 2 100 2 + 101 2 101 2 + 102 2 102 2 + 103 2 103 2 + 104 2 104 2 + 105 2 105 2 + 106 2 106 2 + 107 2 107 2 + 108 2 108 2 + 109 2 109 2 + 110 2 110 2 + 111 2 111 2 + 112 2 112 2 + 113 2 113 2 + 114 2 114 2 + 115 2 115 2 + 116 2 116 2 + 117 2 117 2 + 118 2 118 2 + 119 2 119 2 + 120 2 120 2 + 121 2 121 2 + 122 2 122 2 + 123 2 123 2 + 124 2 124 2 + 125 2 125 2 + 126 2 126 2 + 127 2 127 2 +2048 2 2048 2 +2049 2 2049 2 +2050 2 2050 2 +2051 2 2051 2 +2052 2 2052 2 +2053 2 2053 2 +2054 2 2054 2 +2055 2 2055 2 +2056 2 2056 2 +2057 2 2057 2 +2058 2 2058 2 +2059 2 2059 2 +2060 2 2060 2 +2061 2 2061 2 +2062 2 2062 2 +2063 2 2063 2 +2064 2 2064 2 +2065 2 2065 2 +2066 2 2066 2 +2067 2 2067 2 +2068 2 2068 2 +2069 2 2069 2 +2070 2 2070 2 +2071 2 2071 2 +2072 2 2072 2 +2073 2 2073 2 +2074 2 2074 2 +2075 2 2075 2 +2076 2 2076 2 +2077 2 2077 2 +2078 2 2078 2 +2079 2 2079 2 +2080 2 2080 2 +2081 2 2081 2 +2082 2 2082 2 +2083 2 2083 2 +2084 2 2084 2 +2085 2 2085 2 +2086 2 2086 2 +2087 2 2087 2 +2088 2 2088 2 +2089 2 2089 2 +2090 2 2090 2 +2091 2 2091 2 +2092 2 2092 2 +2093 2 2093 2 +2094 2 2094 2 +2095 2 2095 2 +2096 2 2096 2 +2097 2 2097 2 +2098 2 2098 2 +2099 2 2099 2 +2100 2 2100 2 +2101 2 2101 2 +2102 2 2102 2 +2103 2 2103 2 +2104 2 2104 2 +2105 2 2105 2 +2106 2 2106 2 +2107 2 2107 2 +2108 2 2108 2 +2109 2 2109 2 +2110 2 2110 2 +2111 2 2111 2 +2112 2 2112 2 +2113 2 2113 2 +2114 2 2114 2 +2115 2 2115 2 +2116 2 2116 2 +2117 2 2117 2 +2118 2 2118 2 +2119 2 2119 2 +2120 2 2120 2 +2121 2 2121 2 +2122 2 2122 2 +2123 2 2123 2 +2124 2 2124 2 +2125 2 2125 2 +2126 2 2126 2 +2127 2 2127 2 +2128 2 2128 2 +2129 2 2129 2 +2130 2 2130 2 +2131 2 2131 2 +2132 2 2132 2 +2133 2 2133 2 +2134 2 2134 2 +2135 2 2135 2 +2136 2 2136 2 +2137 2 2137 2 +2138 2 2138 2 +2139 2 2139 2 +2140 2 2140 2 +2141 2 2141 2 +2142 2 2142 2 +2143 2 2143 2 +2144 2 2144 2 +2145 2 2145 2 +2146 2 2146 2 +2147 2 2147 2 +2148 2 2148 2 +2149 2 2149 2 +2150 2 2150 2 +2151 2 2151 2 +2152 2 2152 2 +2153 2 2153 2 +2154 2 2154 2 +2155 2 2155 2 +2156 2 2156 2 +2157 2 2157 2 +2158 2 2158 2 +2159 2 2159 2 +2160 2 2160 2 +2161 2 2161 2 +2162 2 2162 2 +2163 2 2163 2 +2164 2 2164 2 +2165 2 2165 2 +2166 2 2166 2 +2167 2 2167 2 +2168 2 2168 2 +2169 2 2169 2 +2170 2 2170 2 +2171 2 2171 2 +2172 2 2172 2 +2173 2 2173 2 +2174 2 2174 2 +2175 2 2175 2 + 128 43 29 0 + 129 43 29 1 + 130 43 28 0 + 131 43 28 1 + 132 42 29 0 + 133 42 29 1 + 134 42 28 0 + 135 42 28 1 + 136 40 28 1 + 137 40 28 0 + 138 40 29 1 + 139 40 29 0 + 140 41 28 1 + 141 41 28 0 + 142 41 29 1 + 143 41 29 0 + 160 35 28 0 + 161 35 28 1 + 162 35 29 0 + 163 35 29 1 + 164 34 28 0 + 165 34 28 1 + 166 34 29 0 + 167 34 29 1 + 168 32 29 1 + 169 32 29 0 + 170 32 28 1 + 171 32 28 0 + 172 33 29 1 + 173 33 29 0 + 174 33 28 1 + 175 33 28 0 + 176 36 28 0 + 177 36 28 1 + 178 36 29 0 + 179 36 29 1 + 180 37 28 0 + 181 37 28 1 + 182 37 29 0 + 183 37 29 1 + 184 39 29 1 + 185 39 29 0 + 186 39 28 1 + 187 39 28 0 + 188 38 29 1 + 189 38 29 0 + 190 38 28 1 + 191 38 28 0 + 192 44 29 0 + 193 44 29 1 + 194 44 28 0 + 195 44 28 1 + 196 45 29 0 + 197 45 29 1 + 198 45 28 0 + 199 45 28 1 + 200 47 28 1 + 201 47 28 0 + 202 47 29 1 + 203 47 29 0 + 204 46 28 1 + 205 46 28 0 + 206 46 29 1 + 207 46 29 0 + 256 43 31 0 + 257 43 31 1 + 258 43 30 0 + 259 43 30 1 + 260 42 31 0 + 261 42 31 1 + 262 42 30 0 + 263 42 30 1 + 264 40 30 1 + 265 40 30 0 + 266 40 31 1 + 267 40 31 0 + 268 41 30 1 + 269 41 30 0 + 270 41 31 1 + 271 41 31 0 + 288 35 30 0 + 289 35 30 1 + 290 35 31 0 + 291 35 31 1 + 292 34 30 0 + 293 34 30 1 + 294 34 31 0 + 295 34 31 1 + 296 32 31 1 + 297 32 31 0 + 298 32 30 1 + 299 32 30 0 + 300 33 31 1 + 301 33 31 0 + 302 33 30 1 + 303 33 30 0 + 304 36 30 0 + 305 36 30 1 + 306 36 31 0 + 307 36 31 1 + 308 37 30 0 + 309 37 30 1 + 310 37 31 0 + 311 37 31 1 + 312 39 31 1 + 313 39 31 0 + 314 39 30 1 + 315 39 30 0 + 316 38 31 1 + 317 38 31 0 + 318 38 30 1 + 319 38 30 0 + 320 44 31 0 + 321 44 31 1 + 322 44 30 0 + 323 44 30 1 + 324 45 31 0 + 325 45 31 1 + 326 45 30 0 + 327 45 30 1 + 328 47 30 1 + 329 47 30 0 + 330 47 31 1 + 331 47 31 0 + 332 46 30 1 + 333 46 30 0 + 334 46 31 1 + 335 46 31 0 + 384 43 33 0 + 385 43 33 1 + 386 43 32 0 + 387 43 32 1 + 388 42 33 0 + 389 42 33 1 + 390 42 32 0 + 391 42 32 1 + 392 40 32 1 + 393 40 32 0 + 394 40 33 1 + 395 40 33 0 + 396 41 32 1 + 397 41 32 0 + 398 41 33 1 + 399 41 33 0 + 416 35 32 0 + 417 35 32 1 + 418 35 33 0 + 419 35 33 1 + 420 34 32 0 + 421 34 32 1 + 422 34 33 0 + 423 34 33 1 + 424 32 33 1 + 425 32 33 0 + 426 32 32 1 + 427 32 32 0 + 428 33 33 1 + 429 33 33 0 + 430 33 32 1 + 431 33 32 0 + 432 36 32 0 + 433 36 32 1 + 434 36 33 0 + 435 36 33 1 + 436 37 32 0 + 437 37 32 1 + 438 37 33 0 + 439 37 33 1 + 440 39 33 1 + 441 39 33 0 + 442 39 32 1 + 443 39 32 0 + 444 38 33 1 + 445 38 33 0 + 446 38 32 1 + 447 38 32 0 + 448 44 33 0 + 449 44 33 1 + 450 44 32 0 + 451 44 32 1 + 452 45 33 0 + 453 45 33 1 + 454 45 32 0 + 455 45 32 1 + 456 47 32 1 + 457 47 32 0 + 458 47 33 1 + 459 47 33 0 + 460 46 32 1 + 461 46 32 0 + 462 46 33 1 + 463 46 33 0 + 512 43 35 0 + 513 43 35 1 + 514 43 34 0 + 515 43 34 1 + 516 42 35 0 + 517 42 35 1 + 518 42 34 0 + 519 42 34 1 + 520 40 34 1 + 521 40 34 0 + 522 40 35 1 + 523 40 35 0 + 524 41 34 1 + 525 41 34 0 + 526 41 35 1 + 527 41 35 0 + 544 35 34 0 + 545 35 34 1 + 546 35 35 0 + 547 35 35 1 + 548 34 34 0 + 549 34 34 1 + 550 34 35 0 + 551 34 35 1 + 552 32 35 1 + 553 32 35 0 + 554 32 34 1 + 555 32 34 0 + 556 33 35 1 + 557 33 35 0 + 558 33 34 1 + 559 33 34 0 + 560 36 34 0 + 561 36 34 1 + 562 36 35 0 + 563 36 35 1 + 564 37 34 0 + 565 37 34 1 + 566 37 35 0 + 567 37 35 1 + 568 39 35 1 + 569 39 35 0 + 570 39 34 1 + 571 39 34 0 + 572 38 35 1 + 573 38 35 0 + 574 38 34 1 + 575 38 34 0 + 576 44 35 0 + 577 44 35 1 + 578 44 34 0 + 579 44 34 1 + 580 45 35 0 + 581 45 35 1 + 582 45 34 0 + 583 45 34 1 + 584 47 34 1 + 585 47 34 0 + 586 47 35 1 + 587 47 35 0 + 588 46 34 1 + 589 46 34 0 + 590 46 35 1 + 591 46 35 0 + 640 43 37 0 + 641 43 37 1 + 642 43 36 0 + 643 43 36 1 + 644 42 37 0 + 645 42 37 1 + 646 42 36 0 + 647 42 36 1 + 648 40 36 1 + 649 40 36 0 + 650 40 37 1 + 651 40 37 0 + 652 41 36 1 + 653 41 36 0 + 654 41 37 1 + 655 41 37 0 + 672 35 36 0 + 673 35 36 1 + 674 35 37 0 + 675 35 37 1 + 676 34 36 0 + 677 34 36 1 + 678 34 37 0 + 679 34 37 1 + 680 32 37 1 + 681 32 37 0 + 682 32 36 1 + 683 32 36 0 + 684 33 37 1 + 685 33 37 0 + 686 33 36 1 + 687 33 36 0 + 688 36 36 0 + 689 36 36 1 + 690 36 37 0 + 691 36 37 1 + 692 37 36 0 + 693 37 36 1 + 694 37 37 0 + 695 37 37 1 + 696 39 37 1 + 697 39 37 0 + 698 39 36 1 + 699 39 36 0 + 700 38 37 1 + 701 38 37 0 + 702 38 36 1 + 703 38 36 0 + 704 44 37 0 + 705 44 37 1 + 706 44 36 0 + 707 44 36 1 + 708 45 37 0 + 709 45 37 1 + 710 45 36 0 + 711 45 36 1 + 712 47 36 1 + 713 47 36 0 + 714 47 37 1 + 715 47 37 0 + 716 46 36 1 + 717 46 36 0 + 718 46 37 1 + 719 46 37 0 + 768 43 39 0 + 769 43 39 1 + 770 43 38 0 + 771 43 38 1 + 772 42 39 0 + 773 42 39 1 + 774 42 38 0 + 775 42 38 1 + 776 40 38 1 + 777 40 38 0 + 778 40 39 1 + 779 40 39 0 + 780 41 38 1 + 781 41 38 0 + 782 41 39 1 + 783 41 39 0 + 800 35 38 0 + 801 35 38 1 + 802 35 39 0 + 803 35 39 1 + 804 34 38 0 + 805 34 38 1 + 806 34 39 0 + 807 34 39 1 + 808 32 39 1 + 809 32 39 0 + 810 32 38 1 + 811 32 38 0 + 812 33 39 1 + 813 33 39 0 + 814 33 38 1 + 815 33 38 0 + 816 36 38 0 + 817 36 38 1 + 818 36 39 0 + 819 36 39 1 + 820 37 38 0 + 821 37 38 1 + 822 37 39 0 + 823 37 39 1 + 824 39 39 1 + 825 39 39 0 + 826 39 38 1 + 827 39 38 0 + 828 38 39 1 + 829 38 39 0 + 830 38 38 1 + 831 38 38 0 + 832 44 39 0 + 833 44 39 1 + 834 44 38 0 + 835 44 38 1 + 836 45 39 0 + 837 45 39 1 + 838 45 38 0 + 839 45 38 1 + 840 47 38 1 + 841 47 38 0 + 842 47 39 1 + 843 47 39 0 + 844 46 38 1 + 845 46 38 0 + 846 46 39 1 + 847 46 39 0 + 896 43 41 0 + 897 43 41 1 + 898 43 40 0 + 899 43 40 1 + 900 42 41 0 + 901 42 41 1 + 902 42 40 0 + 903 42 40 1 + 904 40 40 1 + 905 40 40 0 + 906 40 41 1 + 907 40 41 0 + 908 41 40 1 + 909 41 40 0 + 910 41 41 1 + 911 41 41 0 + 928 35 40 0 + 929 35 40 1 + 930 35 41 0 + 931 35 41 1 + 932 34 40 0 + 933 34 40 1 + 934 34 41 0 + 935 34 41 1 + 936 32 41 1 + 937 32 41 0 + 938 32 40 1 + 939 32 40 0 + 940 33 41 1 + 941 33 41 0 + 942 33 40 1 + 943 33 40 0 + 944 36 40 0 + 945 36 40 1 + 946 36 41 0 + 947 36 41 1 + 948 37 40 0 + 949 37 40 1 + 950 37 41 0 + 951 37 41 1 + 952 39 41 1 + 953 39 41 0 + 954 39 40 1 + 955 39 40 0 + 956 38 41 1 + 957 38 41 0 + 958 38 40 1 + 959 38 40 0 + 960 44 41 0 + 961 44 41 1 + 962 44 40 0 + 963 44 40 1 + 964 45 41 0 + 965 45 41 1 + 966 45 40 0 + 967 45 40 1 + 968 47 40 1 + 969 47 40 0 + 970 47 41 1 + 971 47 41 0 + 972 46 40 1 + 973 46 40 0 + 974 46 41 1 + 975 46 41 0 +1024 43 43 0 +1025 43 43 1 +1026 43 42 0 +1027 43 42 1 +1028 42 43 0 +1029 42 43 1 +1030 42 42 0 +1031 42 42 1 +1032 40 42 1 +1033 40 42 0 +1034 40 43 1 +1035 40 43 0 +1036 41 42 1 +1037 41 42 0 +1038 41 43 1 +1039 41 43 0 +1056 35 42 0 +1057 35 42 1 +1058 35 43 0 +1059 35 43 1 +1060 34 42 0 +1061 34 42 1 +1062 34 43 0 +1063 34 43 1 +1064 32 43 1 +1065 32 43 0 +1066 32 42 1 +1067 32 42 0 +1068 33 43 1 +1069 33 43 0 +1070 33 42 1 +1071 33 42 0 +1072 36 42 0 +1073 36 42 1 +1074 36 43 0 +1075 36 43 1 +1076 37 42 0 +1077 37 42 1 +1078 37 43 0 +1079 37 43 1 +1080 39 43 1 +1081 39 43 0 +1082 39 42 1 +1083 39 42 0 +1084 38 43 1 +1085 38 43 0 +1086 38 42 1 +1087 38 42 0 +1088 44 43 0 +1089 44 43 1 +1090 44 42 0 +1091 44 42 1 +1092 45 43 0 +1093 45 43 1 +1094 45 42 0 +1095 45 42 1 +1096 47 42 1 +1097 47 42 0 +1098 47 43 1 +1099 47 43 0 +1100 46 42 1 +1101 46 42 0 +1102 46 43 1 +1103 46 43 0 +1152 43 45 0 +1153 43 45 1 +1154 43 44 0 +1155 43 44 1 +1156 42 45 0 +1157 42 45 1 +1158 42 44 0 +1159 42 44 1 +1160 40 44 1 +1161 40 44 0 +1162 40 45 1 +1163 40 45 0 +1164 41 44 1 +1165 41 44 0 +1166 41 45 1 +1167 41 45 0 +1184 35 44 0 +1185 35 44 1 +1186 35 45 0 +1187 35 45 1 +1188 34 44 0 +1189 34 44 1 +1190 34 45 0 +1191 34 45 1 +1192 32 45 1 +1193 32 45 0 +1194 32 44 1 +1195 32 44 0 +1196 33 45 1 +1197 33 45 0 +1198 33 44 1 +1199 33 44 0 +1200 36 44 0 +1201 36 44 1 +1202 36 45 0 +1203 36 45 1 +1204 37 44 0 +1205 37 44 1 +1206 37 45 0 +1207 37 45 1 +1208 39 45 1 +1209 39 45 0 +1210 39 44 1 +1211 39 44 0 +1212 38 45 1 +1213 38 45 0 +1214 38 44 1 +1215 38 44 0 +1216 44 45 0 +1217 44 45 1 +1218 44 44 0 +1219 44 44 1 +1220 45 45 0 +1221 45 45 1 +1222 45 44 0 +1223 45 44 1 +1224 47 44 1 +1225 47 44 0 +1226 47 45 1 +1227 47 45 0 +1228 46 44 1 +1229 46 44 0 +1230 46 45 1 +1231 46 45 0 +1280 43 47 0 +1281 43 47 1 +1282 43 46 0 +1283 43 46 1 +1284 42 47 0 +1285 42 47 1 +1286 42 46 0 +1287 42 46 1 +1288 40 46 1 +1289 40 46 0 +1290 40 47 1 +1291 40 47 0 +1292 41 46 1 +1293 41 46 0 +1294 41 47 1 +1295 41 47 0 +1312 35 46 0 +1313 35 46 1 +1314 35 47 0 +1315 35 47 1 +1316 34 46 0 +1317 34 46 1 +1318 34 47 0 +1319 34 47 1 +1320 32 47 1 +1321 32 47 0 +1322 32 46 1 +1323 32 46 0 +1324 33 47 1 +1325 33 47 0 +1326 33 46 1 +1327 33 46 0 +1328 36 46 0 +1329 36 46 1 +1330 36 47 0 +1331 36 47 1 +1332 37 46 0 +1333 37 46 1 +1334 37 47 0 +1335 37 47 1 +1336 39 47 1 +1337 39 47 0 +1338 39 46 1 +1339 39 46 0 +1340 38 47 1 +1341 38 47 0 +1342 38 46 1 +1343 38 46 0 +1344 44 47 0 +1345 44 47 1 +1346 44 46 0 +1347 44 46 1 +1348 45 47 0 +1349 45 47 1 +1350 45 46 0 +1351 45 46 1 +1352 47 46 1 +1353 47 46 0 +1354 47 47 1 +1355 47 47 0 +1356 46 46 1 +1357 46 46 0 +1358 46 47 1 +1359 46 47 0 +1408 43 49 0 +1409 43 49 1 +1410 43 48 0 +1411 43 48 1 +1412 42 49 0 +1413 42 49 1 +1414 42 48 0 +1415 42 48 1 +1416 40 48 1 +1417 40 48 0 +1418 40 49 1 +1419 40 49 0 +1420 41 48 1 +1421 41 48 0 +1422 41 49 1 +1423 41 49 0 +1440 35 48 0 +1441 35 48 1 +1442 35 49 0 +1443 35 49 1 +1444 34 48 0 +1445 34 48 1 +1446 34 49 0 +1447 34 49 1 +1448 32 49 1 +1449 32 49 0 +1450 32 48 1 +1451 32 48 0 +1452 33 49 1 +1453 33 49 0 +1454 33 48 1 +1455 33 48 0 +1456 36 48 0 +1457 36 48 1 +1458 36 49 0 +1459 36 49 1 +1460 37 48 0 +1461 37 48 1 +1462 37 49 0 +1463 37 49 1 +1464 39 49 1 +1465 39 49 0 +1466 39 48 1 +1467 39 48 0 +1468 38 49 1 +1469 38 49 0 +1470 38 48 1 +1471 38 48 0 +1472 44 49 0 +1473 44 49 1 +1474 44 48 0 +1475 44 48 1 +1476 45 49 0 +1477 45 49 1 +1478 45 48 0 +1479 45 48 1 +1480 47 48 1 +1481 47 48 0 +1482 47 49 1 +1483 47 49 0 +1484 46 48 1 +1485 46 48 0 +1486 46 49 1 +1487 46 49 0 +1536 43 51 0 +1537 43 51 1 +1538 43 50 0 +1539 43 50 1 +1540 42 51 0 +1541 42 51 1 +1542 42 50 0 +1543 42 50 1 +1544 40 50 1 +1545 40 50 0 +1546 40 51 1 +1547 40 51 0 +1548 41 50 1 +1549 41 50 0 +1550 41 51 1 +1551 41 51 0 +1568 35 50 0 +1569 35 50 1 +1570 35 51 0 +1571 35 51 1 +1572 34 50 0 +1573 34 50 1 +1574 34 51 0 +1575 34 51 1 +1576 32 51 1 +1577 32 51 0 +1578 32 50 1 +1579 32 50 0 +1580 33 51 1 +1581 33 51 0 +1582 33 50 1 +1583 33 50 0 +1584 36 50 0 +1585 36 50 1 +1586 36 51 0 +1587 36 51 1 +1588 37 50 0 +1589 37 50 1 +1590 37 51 0 +1591 37 51 1 +1592 39 51 1 +1593 39 51 0 +1594 39 50 1 +1595 39 50 0 +1596 38 51 1 +1597 38 51 0 +1598 38 50 1 +1599 38 50 0 +1600 44 51 0 +1601 44 51 1 +1602 44 50 0 +1603 44 50 1 +1604 45 51 0 +1605 45 51 1 +1606 45 50 0 +1607 45 50 1 +1608 47 50 1 +1609 47 50 0 +1610 47 51 1 +1611 47 51 0 +1612 46 50 1 +1613 46 50 0 +1614 46 51 1 +1615 46 51 0 +1664 43 53 0 +1665 43 53 1 +1666 43 52 0 +1667 43 52 1 +1668 42 53 0 +1669 42 53 1 +1670 42 52 0 +1671 42 52 1 +1672 40 52 1 +1673 40 52 0 +1674 40 53 1 +1675 40 53 0 +1676 41 52 1 +1677 41 52 0 +1678 41 53 1 +1679 41 53 0 +1696 35 52 0 +1697 35 52 1 +1698 35 53 0 +1699 35 53 1 +1700 34 52 0 +1701 34 52 1 +1702 34 53 0 +1703 34 53 1 +1704 32 53 1 +1705 32 53 0 +1706 32 52 1 +1707 32 52 0 +1708 33 53 1 +1709 33 53 0 +1710 33 52 1 +1711 33 52 0 +1712 36 52 0 +1713 36 52 1 +1714 36 53 0 +1715 36 53 1 +1716 37 52 0 +1717 37 52 1 +1718 37 53 0 +1719 37 53 1 +1720 39 53 1 +1721 39 53 0 +1722 39 52 1 +1723 39 52 0 +1724 38 53 1 +1725 38 53 0 +1726 38 52 1 +1727 38 52 0 +1728 44 53 0 +1729 44 53 1 +1730 44 52 0 +1731 44 52 1 +1732 45 53 0 +1733 45 53 1 +1734 45 52 0 +1735 45 52 1 +1736 47 52 1 +1737 47 52 0 +1738 47 53 1 +1739 47 53 0 +1740 46 52 1 +1741 46 52 0 +1742 46 53 1 +1743 46 53 0 +1792 43 55 0 +1793 43 55 1 +1794 43 54 0 +1795 43 54 1 +1796 42 55 0 +1797 42 55 1 +1798 42 54 0 +1799 42 54 1 +1800 40 54 1 +1801 40 54 0 +1802 40 55 1 +1803 40 55 0 +1804 41 54 1 +1805 41 54 0 +1806 41 55 1 +1807 41 55 0 +1824 35 54 0 +1825 35 54 1 +1826 35 55 0 +1827 35 55 1 +1828 34 54 0 +1829 34 54 1 +1830 34 55 0 +1831 34 55 1 +1832 32 55 1 +1833 32 55 0 +1834 32 54 1 +1835 32 54 0 +1836 33 55 1 +1837 33 55 0 +1838 33 54 1 +1839 33 54 0 +1840 36 54 0 +1841 36 54 1 +1842 36 55 0 +1843 36 55 1 +1844 37 54 0 +1845 37 54 1 +1846 37 55 0 +1847 37 55 1 +1848 39 55 1 +1849 39 55 0 +1850 39 54 1 +1851 39 54 0 +1852 38 55 1 +1853 38 55 0 +1854 38 54 1 +1855 38 54 0 +1856 44 55 0 +1857 44 55 1 +1858 44 54 0 +1859 44 54 1 +1860 45 55 0 +1861 45 55 1 +1862 45 54 0 +1863 45 54 1 +1864 47 54 1 +1865 47 54 0 +1866 47 55 1 +1867 47 55 0 +1868 46 54 1 +1869 46 54 0 +1870 46 55 1 +1871 46 55 0 +2176 43 27 0 +2177 43 27 1 +2178 43 26 0 +2179 43 26 1 +2180 42 27 0 +2181 42 27 1 +2182 42 26 0 +2183 42 26 1 +2184 40 26 1 +2185 40 26 0 +2186 40 27 1 +2187 40 27 0 +2188 41 26 1 +2189 41 26 0 +2190 41 27 1 +2191 41 27 0 +2208 35 26 0 +2209 35 26 1 +2210 35 27 0 +2211 35 27 1 +2212 34 26 0 +2213 34 26 1 +2214 34 27 0 +2215 34 27 1 +2216 32 27 1 +2217 32 27 0 +2218 32 26 1 +2219 32 26 0 +2220 33 27 1 +2221 33 27 0 +2222 33 26 1 +2223 33 26 0 +2224 36 26 0 +2225 36 26 1 +2226 36 27 0 +2227 36 27 1 +2228 37 26 0 +2229 37 26 1 +2230 37 27 0 +2231 37 27 1 +2232 39 27 1 +2233 39 27 0 +2234 39 26 1 +2235 39 26 0 +2236 38 27 1 +2237 38 27 0 +2238 38 26 1 +2239 38 26 0 +2240 44 27 0 +2241 44 27 1 +2242 44 26 0 +2243 44 26 1 +2244 45 27 0 +2245 45 27 1 +2246 45 26 0 +2247 45 26 1 +2248 47 26 1 +2249 47 26 0 +2250 47 27 1 +2251 47 27 0 +2252 46 26 1 +2253 46 26 0 +2254 46 27 1 +2255 46 27 0 +2304 43 25 0 +2305 43 25 1 +2306 43 24 0 +2307 43 24 1 +2308 42 25 0 +2309 42 25 1 +2310 42 24 0 +2311 42 24 1 +2312 40 24 1 +2313 40 24 0 +2314 40 25 1 +2315 40 25 0 +2316 41 24 1 +2317 41 24 0 +2318 41 25 1 +2319 41 25 0 +2336 35 24 0 +2337 35 24 1 +2338 35 25 0 +2339 35 25 1 +2340 34 24 0 +2341 34 24 1 +2342 34 25 0 +2343 34 25 1 +2344 32 25 1 +2345 32 25 0 +2346 32 24 1 +2347 32 24 0 +2348 33 25 1 +2349 33 25 0 +2350 33 24 1 +2351 33 24 0 +2352 36 24 0 +2353 36 24 1 +2354 36 25 0 +2355 36 25 1 +2356 37 24 0 +2357 37 24 1 +2358 37 25 0 +2359 37 25 1 +2360 39 25 1 +2361 39 25 0 +2362 39 24 1 +2363 39 24 0 +2364 38 25 1 +2365 38 25 0 +2366 38 24 1 +2367 38 24 0 +2368 44 25 0 +2369 44 25 1 +2370 44 24 0 +2371 44 24 1 +2372 45 25 0 +2373 45 25 1 +2374 45 24 0 +2375 45 24 1 +2376 47 24 1 +2377 47 24 0 +2378 47 25 1 +2379 47 25 0 +2380 46 24 1 +2381 46 24 0 +2382 46 25 1 +2383 46 25 0 +2432 43 23 0 +2433 43 23 1 +2434 43 22 0 +2435 43 22 1 +2436 42 23 0 +2437 42 23 1 +2438 42 22 0 +2439 42 22 1 +2440 40 22 1 +2441 40 22 0 +2442 40 23 1 +2443 40 23 0 +2444 41 22 1 +2445 41 22 0 +2446 41 23 1 +2447 41 23 0 +2464 35 22 0 +2465 35 22 1 +2466 35 23 0 +2467 35 23 1 +2468 34 22 0 +2469 34 22 1 +2470 34 23 0 +2471 34 23 1 +2472 32 23 1 +2473 32 23 0 +2474 32 22 1 +2475 32 22 0 +2476 33 23 1 +2477 33 23 0 +2478 33 22 1 +2479 33 22 0 +2480 36 22 0 +2481 36 22 1 +2482 36 23 0 +2483 36 23 1 +2484 37 22 0 +2485 37 22 1 +2486 37 23 0 +2487 37 23 1 +2488 39 23 1 +2489 39 23 0 +2490 39 22 1 +2491 39 22 0 +2492 38 23 1 +2493 38 23 0 +2494 38 22 1 +2495 38 22 0 +2496 44 23 0 +2497 44 23 1 +2498 44 22 0 +2499 44 22 1 +2500 45 23 0 +2501 45 23 1 +2502 45 22 0 +2503 45 22 1 +2504 47 22 1 +2505 47 22 0 +2506 47 23 1 +2507 47 23 0 +2508 46 22 1 +2509 46 22 0 +2510 46 23 1 +2511 46 23 0 +2560 43 21 0 +2561 43 21 1 +2562 43 20 0 +2563 43 20 1 +2564 42 21 0 +2565 42 21 1 +2566 42 20 0 +2567 42 20 1 +2568 40 20 1 +2569 40 20 0 +2570 40 21 1 +2571 40 21 0 +2572 41 20 1 +2573 41 20 0 +2574 41 21 1 +2575 41 21 0 +2592 35 20 0 +2593 35 20 1 +2594 35 21 0 +2595 35 21 1 +2596 34 20 0 +2597 34 20 1 +2598 34 21 0 +2599 34 21 1 +2600 32 21 1 +2601 32 21 0 +2602 32 20 1 +2603 32 20 0 +2604 33 21 1 +2605 33 21 0 +2606 33 20 1 +2607 33 20 0 +2608 36 20 0 +2609 36 20 1 +2610 36 21 0 +2611 36 21 1 +2612 37 20 0 +2613 37 20 1 +2614 37 21 0 +2615 37 21 1 +2616 39 21 1 +2617 39 21 0 +2618 39 20 1 +2619 39 20 0 +2620 38 21 1 +2621 38 21 0 +2622 38 20 1 +2623 38 20 0 +2624 44 21 0 +2625 44 21 1 +2626 44 20 0 +2627 44 20 1 +2628 45 21 0 +2629 45 21 1 +2630 45 20 0 +2631 45 20 1 +2632 47 20 1 +2633 47 20 0 +2634 47 21 1 +2635 47 21 0 +2636 46 20 1 +2637 46 20 0 +2638 46 21 1 +2639 46 21 0 +2688 43 19 0 +2689 43 19 1 +2690 43 18 0 +2691 43 18 1 +2692 42 19 0 +2693 42 19 1 +2694 42 18 0 +2695 42 18 1 +2696 40 18 1 +2697 40 18 0 +2698 40 19 1 +2699 40 19 0 +2700 41 18 1 +2701 41 18 0 +2702 41 19 1 +2703 41 19 0 +2720 35 18 0 +2721 35 18 1 +2722 35 19 0 +2723 35 19 1 +2724 34 18 0 +2725 34 18 1 +2726 34 19 0 +2727 34 19 1 +2728 32 19 1 +2729 32 19 0 +2730 32 18 1 +2731 32 18 0 +2732 33 19 1 +2733 33 19 0 +2734 33 18 1 +2735 33 18 0 +2736 36 18 0 +2737 36 18 1 +2738 36 19 0 +2739 36 19 1 +2740 37 18 0 +2741 37 18 1 +2742 37 19 0 +2743 37 19 1 +2744 39 19 1 +2745 39 19 0 +2746 39 18 1 +2747 39 18 0 +2748 38 19 1 +2749 38 19 0 +2750 38 18 1 +2751 38 18 0 +2752 44 19 0 +2753 44 19 1 +2754 44 18 0 +2755 44 18 1 +2756 45 19 0 +2757 45 19 1 +2758 45 18 0 +2759 45 18 1 +2760 47 18 1 +2761 47 18 0 +2762 47 19 1 +2763 47 19 0 +2764 46 18 1 +2765 46 18 0 +2766 46 19 1 +2767 46 19 0 +2816 43 17 0 +2817 43 17 1 +2818 43 16 0 +2819 43 16 1 +2820 42 17 0 +2821 42 17 1 +2822 42 16 0 +2823 42 16 1 +2824 40 16 1 +2825 40 16 0 +2826 40 17 1 +2827 40 17 0 +2828 41 16 1 +2829 41 16 0 +2830 41 17 1 +2831 41 17 0 +2848 35 16 0 +2849 35 16 1 +2850 35 17 0 +2851 35 17 1 +2852 34 16 0 +2853 34 16 1 +2854 34 17 0 +2855 34 17 1 +2856 32 17 1 +2857 32 17 0 +2858 32 16 1 +2859 32 16 0 +2860 33 17 1 +2861 33 17 0 +2862 33 16 1 +2863 33 16 0 +2864 36 16 0 +2865 36 16 1 +2866 36 17 0 +2867 36 17 1 +2868 37 16 0 +2869 37 16 1 +2870 37 17 0 +2871 37 17 1 +2872 39 17 1 +2873 39 17 0 +2874 39 16 1 +2875 39 16 0 +2876 38 17 1 +2877 38 17 0 +2878 38 16 1 +2879 38 16 0 +2880 44 17 0 +2881 44 17 1 +2882 44 16 0 +2883 44 16 1 +2884 45 17 0 +2885 45 17 1 +2886 45 16 0 +2887 45 16 1 +2888 47 16 1 +2889 47 16 0 +2890 47 17 1 +2891 47 17 0 +2892 46 16 1 +2893 46 16 0 +2894 46 17 1 +2895 46 17 0 +2944 43 15 0 +2945 43 15 1 +2946 43 14 0 +2947 43 14 1 +2948 42 15 0 +2949 42 15 1 +2950 42 14 0 +2951 42 14 1 +2952 40 14 1 +2953 40 14 0 +2954 40 15 1 +2955 40 15 0 +2956 41 14 1 +2957 41 14 0 +2958 41 15 1 +2959 41 15 0 +2976 35 14 0 +2977 35 14 1 +2978 35 15 0 +2979 35 15 1 +2980 34 14 0 +2981 34 14 1 +2982 34 15 0 +2983 34 15 1 +2984 32 15 1 +2985 32 15 0 +2986 32 14 1 +2987 32 14 0 +2988 33 15 1 +2989 33 15 0 +2990 33 14 1 +2991 33 14 0 +2992 36 14 0 +2993 36 14 1 +2994 36 15 0 +2995 36 15 1 +2996 37 14 0 +2997 37 14 1 +2998 37 15 0 +2999 37 15 1 +3000 39 15 1 +3001 39 15 0 +3002 39 14 1 +3003 39 14 0 +3004 38 15 1 +3005 38 15 0 +3006 38 14 1 +3007 38 14 0 +3008 44 15 0 +3009 44 15 1 +3010 44 14 0 +3011 44 14 1 +3012 45 15 0 +3013 45 15 1 +3014 45 14 0 +3015 45 14 1 +3016 47 14 1 +3017 47 14 0 +3018 47 15 1 +3019 47 15 0 +3020 46 14 1 +3021 46 14 0 +3022 46 15 1 +3023 46 15 0 +3072 43 13 0 +3073 43 13 1 +3074 43 12 0 +3075 43 12 1 +3076 42 13 0 +3077 42 13 1 +3078 42 12 0 +3079 42 12 1 +3080 40 12 1 +3081 40 12 0 +3082 40 13 1 +3083 40 13 0 +3084 41 12 1 +3085 41 12 0 +3086 41 13 1 +3087 41 13 0 +3104 35 12 0 +3105 35 12 1 +3106 35 13 0 +3107 35 13 1 +3108 34 12 0 +3109 34 12 1 +3110 34 13 0 +3111 34 13 1 +3112 32 13 1 +3113 32 13 0 +3114 32 12 1 +3115 32 12 0 +3116 33 13 1 +3117 33 13 0 +3118 33 12 1 +3119 33 12 0 +3120 36 12 0 +3121 36 12 1 +3122 36 13 0 +3123 36 13 1 +3124 37 12 0 +3125 37 12 1 +3126 37 13 0 +3127 37 13 1 +3128 39 13 1 +3129 39 13 0 +3130 39 12 1 +3131 39 12 0 +3132 38 13 1 +3133 38 13 0 +3134 38 12 1 +3135 38 12 0 +3136 44 13 0 +3137 44 13 1 +3138 44 12 0 +3139 44 12 1 +3140 45 13 0 +3141 45 13 1 +3142 45 12 0 +3143 45 12 1 +3144 47 12 1 +3145 47 12 0 +3146 47 13 1 +3147 47 13 0 +3148 46 12 1 +3149 46 12 0 +3150 46 13 1 +3151 46 13 0 +3200 43 11 0 +3201 43 11 1 +3202 43 10 0 +3203 43 10 1 +3204 42 11 0 +3205 42 11 1 +3206 42 10 0 +3207 42 10 1 +3208 40 10 1 +3209 40 10 0 +3210 40 11 1 +3211 40 11 0 +3212 41 10 1 +3213 41 10 0 +3214 41 11 1 +3215 41 11 0 +3232 35 10 0 +3233 35 10 1 +3234 35 11 0 +3235 35 11 1 +3236 34 10 0 +3237 34 10 1 +3238 34 11 0 +3239 34 11 1 +3240 32 11 1 +3241 32 11 0 +3242 32 10 1 +3243 32 10 0 +3244 33 11 1 +3245 33 11 0 +3246 33 10 1 +3247 33 10 0 +3248 36 10 0 +3249 36 10 1 +3250 36 11 0 +3251 36 11 1 +3252 37 10 0 +3253 37 10 1 +3254 37 11 0 +3255 37 11 1 +3256 39 11 1 +3257 39 11 0 +3258 39 10 1 +3259 39 10 0 +3260 38 11 1 +3261 38 11 0 +3262 38 10 1 +3263 38 10 0 +3264 44 11 0 +3265 44 11 1 +3266 44 10 0 +3267 44 10 1 +3268 45 11 0 +3269 45 11 1 +3270 45 10 0 +3271 45 10 1 +3272 47 10 1 +3273 47 10 0 +3274 47 11 1 +3275 47 11 0 +3276 46 10 1 +3277 46 10 0 +3278 46 11 1 +3279 46 11 0 +3328 43 9 0 +3329 43 9 1 +3330 43 8 0 +3331 43 8 1 +3332 42 9 0 +3333 42 9 1 +3334 42 8 0 +3335 42 8 1 +3336 40 8 1 +3337 40 8 0 +3338 40 9 1 +3339 40 9 0 +3340 41 8 1 +3341 41 8 0 +3342 41 9 1 +3343 41 9 0 +3360 35 8 0 +3361 35 8 1 +3362 35 9 0 +3363 35 9 1 +3364 34 8 0 +3365 34 8 1 +3366 34 9 0 +3367 34 9 1 +3368 32 9 1 +3369 32 9 0 +3370 32 8 1 +3371 32 8 0 +3372 33 9 1 +3373 33 9 0 +3374 33 8 1 +3375 33 8 0 +3376 36 8 0 +3377 36 8 1 +3378 36 9 0 +3379 36 9 1 +3380 37 8 0 +3381 37 8 1 +3382 37 9 0 +3383 37 9 1 +3384 39 9 1 +3385 39 9 0 +3386 39 8 1 +3387 39 8 0 +3388 38 9 1 +3389 38 9 0 +3390 38 8 1 +3391 38 8 0 +3392 44 9 0 +3393 44 9 1 +3394 44 8 0 +3395 44 8 1 +3396 45 9 0 +3397 45 9 1 +3398 45 8 0 +3399 45 8 1 +3400 47 8 1 +3401 47 8 0 +3402 47 9 1 +3403 47 9 0 +3404 46 8 1 +3405 46 8 0 +3406 46 9 1 +3407 46 9 0 +3456 43 7 0 +3457 43 7 1 +3458 43 6 0 +3459 43 6 1 +3460 42 7 0 +3461 42 7 1 +3462 42 6 0 +3463 42 6 1 +3464 40 6 1 +3465 40 6 0 +3466 40 7 1 +3467 40 7 0 +3468 41 6 1 +3469 41 6 0 +3470 41 7 1 +3471 41 7 0 +3488 35 6 0 +3489 35 6 1 +3490 35 7 0 +3491 35 7 1 +3492 34 6 0 +3493 34 6 1 +3494 34 7 0 +3495 34 7 1 +3496 32 7 1 +3497 32 7 0 +3498 32 6 1 +3499 32 6 0 +3500 33 7 1 +3501 33 7 0 +3502 33 6 1 +3503 33 6 0 +3504 36 6 0 +3505 36 6 1 +3506 36 7 0 +3507 36 7 1 +3508 37 6 0 +3509 37 6 1 +3510 37 7 0 +3511 37 7 1 +3512 39 7 1 +3513 39 7 0 +3514 39 6 1 +3515 39 6 0 +3516 38 7 1 +3517 38 7 0 +3518 38 6 1 +3519 38 6 0 +3520 44 7 0 +3521 44 7 1 +3522 44 6 0 +3523 44 6 1 +3524 45 7 0 +3525 45 7 1 +3526 45 6 0 +3527 45 6 1 +3528 47 6 1 +3529 47 6 0 +3530 47 7 1 +3531 47 7 0 +3532 46 6 1 +3533 46 6 0 +3534 46 7 1 +3535 46 7 0 +3584 43 5 0 +3585 43 5 1 +3586 43 4 0 +3587 43 4 1 +3588 42 5 0 +3589 42 5 1 +3590 42 4 0 +3591 42 4 1 +3592 40 4 1 +3593 40 4 0 +3594 40 5 1 +3595 40 5 0 +3596 41 4 1 +3597 41 4 0 +3598 41 5 1 +3599 41 5 0 +3616 35 4 0 +3617 35 4 1 +3618 35 5 0 +3619 35 5 1 +3620 34 4 0 +3621 34 4 1 +3622 34 5 0 +3623 34 5 1 +3624 32 5 1 +3625 32 5 0 +3626 32 4 1 +3627 32 4 0 +3628 33 5 1 +3629 33 5 0 +3630 33 4 1 +3631 33 4 0 +3632 36 4 0 +3633 36 4 1 +3634 36 5 0 +3635 36 5 1 +3636 37 4 0 +3637 37 4 1 +3638 37 5 0 +3639 37 5 1 +3640 39 5 1 +3641 39 5 0 +3642 39 4 1 +3643 39 4 0 +3644 38 5 1 +3645 38 5 0 +3646 38 4 1 +3647 38 4 0 +3648 44 5 0 +3649 44 5 1 +3650 44 4 0 +3651 44 4 1 +3652 45 5 0 +3653 45 5 1 +3654 45 4 0 +3655 45 4 1 +3656 47 4 1 +3657 47 4 0 +3658 47 5 1 +3659 47 5 0 +3660 46 4 1 +3661 46 4 0 +3662 46 5 1 +3663 46 5 0 +3712 43 3 0 +3713 43 3 1 +3714 43 2 0 +3715 43 2 1 +3716 42 3 0 +3717 42 3 1 +3718 42 2 0 +3719 42 2 1 +3720 40 2 1 +3721 40 2 0 +3722 40 3 1 +3723 40 3 0 +3724 41 2 1 +3725 41 2 0 +3726 41 3 1 +3727 41 3 0 +3744 35 2 0 +3745 35 2 1 +3746 35 3 0 +3747 35 3 1 +3748 34 2 0 +3749 34 2 1 +3750 34 3 0 +3751 34 3 1 +3752 32 3 1 +3753 32 3 0 +3754 32 2 1 +3755 32 2 0 +3756 33 3 1 +3757 33 3 0 +3758 33 2 1 +3759 33 2 0 +3760 36 2 0 +3761 36 2 1 +3762 36 3 0 +3763 36 3 1 +3764 37 2 0 +3765 37 2 1 +3766 37 3 0 +3767 37 3 1 +3768 39 3 1 +3769 39 3 0 +3770 39 2 1 +3771 39 2 0 +3772 38 3 1 +3773 38 3 0 +3774 38 2 1 +3775 38 2 0 +3776 44 3 0 +3777 44 3 1 +3778 44 2 0 +3779 44 2 1 +3780 45 3 0 +3781 45 3 1 +3782 45 2 0 +3783 45 2 1 +3784 47 2 1 +3785 47 2 0 +3786 47 3 1 +3787 47 3 0 +3788 46 2 1 +3789 46 2 0 +3790 46 3 1 +3791 46 3 0 +3840 43 1 0 +3841 43 1 1 +3842 43 0 0 +3843 43 0 1 +3844 42 1 0 +3845 42 1 1 +3846 42 0 0 +3847 42 0 1 +3848 40 0 1 +3849 40 0 0 +3850 40 1 1 +3851 40 1 0 +3852 41 0 1 +3853 41 0 0 +3854 41 1 1 +3855 41 1 0 +3872 35 0 0 +3873 35 0 1 +3874 35 1 0 +3875 35 1 1 +3876 34 0 0 +3877 34 0 1 +3878 34 1 0 +3879 34 1 1 +3880 32 1 1 +3881 32 1 0 +3882 32 0 1 +3883 32 0 0 +3884 33 1 1 +3885 33 1 0 +3886 33 0 1 +3887 33 0 0 +3888 36 0 0 +3889 36 0 1 +3890 36 1 0 +3891 36 1 1 +3892 37 0 0 +3893 37 0 1 +3894 37 1 0 +3895 37 1 1 +3896 39 1 1 +3897 39 1 0 +3898 39 0 1 +3899 39 0 0 +3900 38 1 1 +3901 38 1 0 +3902 38 0 1 +3903 38 0 0 +3904 44 1 0 +3905 44 1 1 +3906 44 0 0 +3907 44 0 1 +3908 45 1 0 +3909 45 1 1 +3910 45 0 0 +3911 45 0 1 +3912 47 0 1 +3913 47 0 0 +3914 47 1 1 +3915 47 1 0 +3916 46 0 1 +3917 46 0 0 +3918 46 1 1 +3919 46 1 0 diff --git a/Detectors/PHOS/base/files/Mod3RCU3.data b/Detectors/PHOS/base/files/Mod3RCU3.data new file mode 100644 index 0000000000000..43bd76fc42791 --- /dev/null +++ b/Detectors/PHOS/base/files/Mod3RCU3.data @@ -0,0 +1,2050 @@ +2048 +3919 + 0 3 0 2 + 1 3 1 2 + 2 3 2 2 + 3 3 3 2 + 4 3 4 2 + 5 3 5 2 + 6 3 6 2 + 7 3 7 2 + 8 3 8 2 + 9 3 9 2 + 10 3 10 2 + 11 3 11 2 + 12 3 12 2 + 13 3 13 2 + 14 3 14 2 + 15 3 15 2 + 16 3 16 2 + 17 3 17 2 + 18 3 18 2 + 19 3 19 2 + 20 3 20 2 + 21 3 21 2 + 22 3 22 2 + 23 3 23 2 + 24 3 24 2 + 25 3 25 2 + 26 3 26 2 + 27 3 27 2 + 28 3 28 2 + 29 3 29 2 + 30 3 30 2 + 31 3 31 2 + 32 3 32 2 + 33 3 33 2 + 34 3 34 2 + 35 3 35 2 + 36 3 36 2 + 37 3 37 2 + 38 3 38 2 + 39 3 39 2 + 40 3 40 2 + 41 3 41 2 + 42 3 42 2 + 43 3 43 2 + 44 3 44 2 + 45 3 45 2 + 46 3 46 2 + 47 3 47 2 + 48 3 48 2 + 49 3 49 2 + 50 3 50 2 + 51 3 51 2 + 52 3 52 2 + 53 3 53 2 + 54 3 54 2 + 55 3 55 2 + 56 3 56 2 + 57 3 57 2 + 58 3 58 2 + 59 3 59 2 + 60 3 60 2 + 61 3 61 2 + 62 3 62 2 + 63 3 63 2 + 64 3 64 2 + 65 3 65 2 + 66 3 66 2 + 67 3 67 2 + 68 3 68 2 + 69 3 69 2 + 70 3 70 2 + 71 3 71 2 + 72 3 72 2 + 73 3 73 2 + 74 3 74 2 + 75 3 75 2 + 76 3 76 2 + 77 3 77 2 + 78 3 78 2 + 79 3 79 2 + 80 3 80 2 + 81 3 81 2 + 82 3 82 2 + 83 3 83 2 + 84 3 84 2 + 85 3 85 2 + 86 3 86 2 + 87 3 87 2 + 88 3 88 2 + 89 3 89 2 + 90 3 90 2 + 91 3 91 2 + 92 3 92 2 + 93 3 93 2 + 94 3 94 2 + 95 3 95 2 + 96 3 96 2 + 97 3 97 2 + 98 3 98 2 + 99 3 99 2 + 100 3 100 2 + 101 3 101 2 + 102 3 102 2 + 103 3 103 2 + 104 3 104 2 + 105 3 105 2 + 106 3 106 2 + 107 3 107 2 + 108 3 108 2 + 109 3 109 2 + 110 3 110 2 + 111 3 111 2 + 112 3 112 2 + 113 3 113 2 + 114 3 114 2 + 115 3 115 2 + 116 3 116 2 + 117 3 117 2 + 118 3 118 2 + 119 3 119 2 + 120 3 120 2 + 121 3 121 2 + 122 3 122 2 + 123 3 123 2 + 124 3 124 2 + 125 3 125 2 + 126 3 126 2 + 127 3 127 2 +2048 3 2048 2 +2049 3 2049 2 +2050 3 2050 2 +2051 3 2051 2 +2052 3 2052 2 +2053 3 2053 2 +2054 3 2054 2 +2055 3 2055 2 +2056 3 2056 2 +2057 3 2057 2 +2058 3 2058 2 +2059 3 2059 2 +2060 3 2060 2 +2061 3 2061 2 +2062 3 2062 2 +2063 3 2063 2 +2064 3 2064 2 +2065 3 2065 2 +2066 3 2066 2 +2067 3 2067 2 +2068 3 2068 2 +2069 3 2069 2 +2070 3 2070 2 +2071 3 2071 2 +2072 3 2072 2 +2073 3 2073 2 +2074 3 2074 2 +2075 3 2075 2 +2076 3 2076 2 +2077 3 2077 2 +2078 3 2078 2 +2079 3 2079 2 +2080 3 2080 2 +2081 3 2081 2 +2082 3 2082 2 +2083 3 2083 2 +2084 3 2084 2 +2085 3 2085 2 +2086 3 2086 2 +2087 3 2087 2 +2088 3 2088 2 +2089 3 2089 2 +2090 3 2090 2 +2091 3 2091 2 +2092 3 2092 2 +2093 3 2093 2 +2094 3 2094 2 +2095 3 2095 2 +2096 3 2096 2 +2097 3 2097 2 +2098 3 2098 2 +2099 3 2099 2 +2100 3 2100 2 +2101 3 2101 2 +2102 3 2102 2 +2103 3 2103 2 +2104 3 2104 2 +2105 3 2105 2 +2106 3 2106 2 +2107 3 2107 2 +2108 3 2108 2 +2109 3 2109 2 +2110 3 2110 2 +2111 3 2111 2 +2112 3 2112 2 +2113 3 2113 2 +2114 3 2114 2 +2115 3 2115 2 +2116 3 2116 2 +2117 3 2117 2 +2118 3 2118 2 +2119 3 2119 2 +2120 3 2120 2 +2121 3 2121 2 +2122 3 2122 2 +2123 3 2123 2 +2124 3 2124 2 +2125 3 2125 2 +2126 3 2126 2 +2127 3 2127 2 +2128 3 2128 2 +2129 3 2129 2 +2130 3 2130 2 +2131 3 2131 2 +2132 3 2132 2 +2133 3 2133 2 +2134 3 2134 2 +2135 3 2135 2 +2136 3 2136 2 +2137 3 2137 2 +2138 3 2138 2 +2139 3 2139 2 +2140 3 2140 2 +2141 3 2141 2 +2142 3 2142 2 +2143 3 2143 2 +2144 3 2144 2 +2145 3 2145 2 +2146 3 2146 2 +2147 3 2147 2 +2148 3 2148 2 +2149 3 2149 2 +2150 3 2150 2 +2151 3 2151 2 +2152 3 2152 2 +2153 3 2153 2 +2154 3 2154 2 +2155 3 2155 2 +2156 3 2156 2 +2157 3 2157 2 +2158 3 2158 2 +2159 3 2159 2 +2160 3 2160 2 +2161 3 2161 2 +2162 3 2162 2 +2163 3 2163 2 +2164 3 2164 2 +2165 3 2165 2 +2166 3 2166 2 +2167 3 2167 2 +2168 3 2168 2 +2169 3 2169 2 +2170 3 2170 2 +2171 3 2171 2 +2172 3 2172 2 +2173 3 2173 2 +2174 3 2174 2 +2175 3 2175 2 + 128 59 29 0 + 129 59 29 1 + 130 59 28 0 + 131 59 28 1 + 132 58 29 0 + 133 58 29 1 + 134 58 28 0 + 135 58 28 1 + 136 56 28 1 + 137 56 28 0 + 138 56 29 1 + 139 56 29 0 + 140 57 28 1 + 141 57 28 0 + 142 57 29 1 + 143 57 29 0 + 160 51 28 0 + 161 51 28 1 + 162 51 29 0 + 163 51 29 1 + 164 50 28 0 + 165 50 28 1 + 166 50 29 0 + 167 50 29 1 + 168 48 29 1 + 169 48 29 0 + 170 48 28 1 + 171 48 28 0 + 172 49 29 1 + 173 49 29 0 + 174 49 28 1 + 175 49 28 0 + 176 52 28 0 + 177 52 28 1 + 178 52 29 0 + 179 52 29 1 + 180 53 28 0 + 181 53 28 1 + 182 53 29 0 + 183 53 29 1 + 184 55 29 1 + 185 55 29 0 + 186 55 28 1 + 187 55 28 0 + 188 54 29 1 + 189 54 29 0 + 190 54 28 1 + 191 54 28 0 + 192 60 29 0 + 193 60 29 1 + 194 60 28 0 + 195 60 28 1 + 196 61 29 0 + 197 61 29 1 + 198 61 28 0 + 199 61 28 1 + 200 63 28 1 + 201 63 28 0 + 202 63 29 1 + 203 63 29 0 + 204 62 28 1 + 205 62 28 0 + 206 62 29 1 + 207 62 29 0 + 256 59 31 0 + 257 59 31 1 + 258 59 30 0 + 259 59 30 1 + 260 58 31 0 + 261 58 31 1 + 262 58 30 0 + 263 58 30 1 + 264 56 30 1 + 265 56 30 0 + 266 56 31 1 + 267 56 31 0 + 268 57 30 1 + 269 57 30 0 + 270 57 31 1 + 271 57 31 0 + 288 51 30 0 + 289 51 30 1 + 290 51 31 0 + 291 51 31 1 + 292 50 30 0 + 293 50 30 1 + 294 50 31 0 + 295 50 31 1 + 296 48 31 1 + 297 48 31 0 + 298 48 30 1 + 299 48 30 0 + 300 49 31 1 + 301 49 31 0 + 302 49 30 1 + 303 49 30 0 + 304 52 30 0 + 305 52 30 1 + 306 52 31 0 + 307 52 31 1 + 308 53 30 0 + 309 53 30 1 + 310 53 31 0 + 311 53 31 1 + 312 55 31 1 + 313 55 31 0 + 314 55 30 1 + 315 55 30 0 + 316 54 31 1 + 317 54 31 0 + 318 54 30 1 + 319 54 30 0 + 320 60 31 0 + 321 60 31 1 + 322 60 30 0 + 323 60 30 1 + 324 61 31 0 + 325 61 31 1 + 326 61 30 0 + 327 61 30 1 + 328 63 30 1 + 329 63 30 0 + 330 63 31 1 + 331 63 31 0 + 332 62 30 1 + 333 62 30 0 + 334 62 31 1 + 335 62 31 0 + 384 59 33 0 + 385 59 33 1 + 386 59 32 0 + 387 59 32 1 + 388 58 33 0 + 389 58 33 1 + 390 58 32 0 + 391 58 32 1 + 392 56 32 1 + 393 56 32 0 + 394 56 33 1 + 395 56 33 0 + 396 57 32 1 + 397 57 32 0 + 398 57 33 1 + 399 57 33 0 + 416 51 32 0 + 417 51 32 1 + 418 51 33 0 + 419 51 33 1 + 420 50 32 0 + 421 50 32 1 + 422 50 33 0 + 423 50 33 1 + 424 48 33 1 + 425 48 33 0 + 426 48 32 1 + 427 48 32 0 + 428 49 33 1 + 429 49 33 0 + 430 49 32 1 + 431 49 32 0 + 432 52 32 0 + 433 52 32 1 + 434 52 33 0 + 435 52 33 1 + 436 53 32 0 + 437 53 32 1 + 438 53 33 0 + 439 53 33 1 + 440 55 33 1 + 441 55 33 0 + 442 55 32 1 + 443 55 32 0 + 444 54 33 1 + 445 54 33 0 + 446 54 32 1 + 447 54 32 0 + 448 60 33 0 + 449 60 33 1 + 450 60 32 0 + 451 60 32 1 + 452 61 33 0 + 453 61 33 1 + 454 61 32 0 + 455 61 32 1 + 456 63 32 1 + 457 63 32 0 + 458 63 33 1 + 459 63 33 0 + 460 62 32 1 + 461 62 32 0 + 462 62 33 1 + 463 62 33 0 + 512 59 35 0 + 513 59 35 1 + 514 59 34 0 + 515 59 34 1 + 516 58 35 0 + 517 58 35 1 + 518 58 34 0 + 519 58 34 1 + 520 56 34 1 + 521 56 34 0 + 522 56 35 1 + 523 56 35 0 + 524 57 34 1 + 525 57 34 0 + 526 57 35 1 + 527 57 35 0 + 544 51 34 0 + 545 51 34 1 + 546 51 35 0 + 547 51 35 1 + 548 50 34 0 + 549 50 34 1 + 550 50 35 0 + 551 50 35 1 + 552 48 35 1 + 553 48 35 0 + 554 48 34 1 + 555 48 34 0 + 556 49 35 1 + 557 49 35 0 + 558 49 34 1 + 559 49 34 0 + 560 52 34 0 + 561 52 34 1 + 562 52 35 0 + 563 52 35 1 + 564 53 34 0 + 565 53 34 1 + 566 53 35 0 + 567 53 35 1 + 568 55 35 1 + 569 55 35 0 + 570 55 34 1 + 571 55 34 0 + 572 54 35 1 + 573 54 35 0 + 574 54 34 1 + 575 54 34 0 + 576 60 35 0 + 577 60 35 1 + 578 60 34 0 + 579 60 34 1 + 580 61 35 0 + 581 61 35 1 + 582 61 34 0 + 583 61 34 1 + 584 63 34 1 + 585 63 34 0 + 586 63 35 1 + 587 63 35 0 + 588 62 34 1 + 589 62 34 0 + 590 62 35 1 + 591 62 35 0 + 640 59 37 0 + 641 59 37 1 + 642 59 36 0 + 643 59 36 1 + 644 58 37 0 + 645 58 37 1 + 646 58 36 0 + 647 58 36 1 + 648 56 36 1 + 649 56 36 0 + 650 56 37 1 + 651 56 37 0 + 652 57 36 1 + 653 57 36 0 + 654 57 37 1 + 655 57 37 0 + 672 51 36 0 + 673 51 36 1 + 674 51 37 0 + 675 51 37 1 + 676 50 36 0 + 677 50 36 1 + 678 50 37 0 + 679 50 37 1 + 680 48 37 1 + 681 48 37 0 + 682 48 36 1 + 683 48 36 0 + 684 49 37 1 + 685 49 37 0 + 686 49 36 1 + 687 49 36 0 + 688 52 36 0 + 689 52 36 1 + 690 52 37 0 + 691 52 37 1 + 692 53 36 0 + 693 53 36 1 + 694 53 37 0 + 695 53 37 1 + 696 55 37 1 + 697 55 37 0 + 698 55 36 1 + 699 55 36 0 + 700 54 37 1 + 701 54 37 0 + 702 54 36 1 + 703 54 36 0 + 704 60 37 0 + 705 60 37 1 + 706 60 36 0 + 707 60 36 1 + 708 61 37 0 + 709 61 37 1 + 710 61 36 0 + 711 61 36 1 + 712 63 36 1 + 713 63 36 0 + 714 63 37 1 + 715 63 37 0 + 716 62 36 1 + 717 62 36 0 + 718 62 37 1 + 719 62 37 0 + 768 59 39 0 + 769 59 39 1 + 770 59 38 0 + 771 59 38 1 + 772 58 39 0 + 773 58 39 1 + 774 58 38 0 + 775 58 38 1 + 776 56 38 1 + 777 56 38 0 + 778 56 39 1 + 779 56 39 0 + 780 57 38 1 + 781 57 38 0 + 782 57 39 1 + 783 57 39 0 + 800 51 38 0 + 801 51 38 1 + 802 51 39 0 + 803 51 39 1 + 804 50 38 0 + 805 50 38 1 + 806 50 39 0 + 807 50 39 1 + 808 48 39 1 + 809 48 39 0 + 810 48 38 1 + 811 48 38 0 + 812 49 39 1 + 813 49 39 0 + 814 49 38 1 + 815 49 38 0 + 816 52 38 0 + 817 52 38 1 + 818 52 39 0 + 819 52 39 1 + 820 53 38 0 + 821 53 38 1 + 822 53 39 0 + 823 53 39 1 + 824 55 39 1 + 825 55 39 0 + 826 55 38 1 + 827 55 38 0 + 828 54 39 1 + 829 54 39 0 + 830 54 38 1 + 831 54 38 0 + 832 60 39 0 + 833 60 39 1 + 834 60 38 0 + 835 60 38 1 + 836 61 39 0 + 837 61 39 1 + 838 61 38 0 + 839 61 38 1 + 840 63 38 1 + 841 63 38 0 + 842 63 39 1 + 843 63 39 0 + 844 62 38 1 + 845 62 38 0 + 846 62 39 1 + 847 62 39 0 + 896 59 41 0 + 897 59 41 1 + 898 59 40 0 + 899 59 40 1 + 900 58 41 0 + 901 58 41 1 + 902 58 40 0 + 903 58 40 1 + 904 56 40 1 + 905 56 40 0 + 906 56 41 1 + 907 56 41 0 + 908 57 40 1 + 909 57 40 0 + 910 57 41 1 + 911 57 41 0 + 928 51 40 0 + 929 51 40 1 + 930 51 41 0 + 931 51 41 1 + 932 50 40 0 + 933 50 40 1 + 934 50 41 0 + 935 50 41 1 + 936 48 41 1 + 937 48 41 0 + 938 48 40 1 + 939 48 40 0 + 940 49 41 1 + 941 49 41 0 + 942 49 40 1 + 943 49 40 0 + 944 52 40 0 + 945 52 40 1 + 946 52 41 0 + 947 52 41 1 + 948 53 40 0 + 949 53 40 1 + 950 53 41 0 + 951 53 41 1 + 952 55 41 1 + 953 55 41 0 + 954 55 40 1 + 955 55 40 0 + 956 54 41 1 + 957 54 41 0 + 958 54 40 1 + 959 54 40 0 + 960 60 41 0 + 961 60 41 1 + 962 60 40 0 + 963 60 40 1 + 964 61 41 0 + 965 61 41 1 + 966 61 40 0 + 967 61 40 1 + 968 63 40 1 + 969 63 40 0 + 970 63 41 1 + 971 63 41 0 + 972 62 40 1 + 973 62 40 0 + 974 62 41 1 + 975 62 41 0 +1024 59 43 0 +1025 59 43 1 +1026 59 42 0 +1027 59 42 1 +1028 58 43 0 +1029 58 43 1 +1030 58 42 0 +1031 58 42 1 +1032 56 42 1 +1033 56 42 0 +1034 56 43 1 +1035 56 43 0 +1036 57 42 1 +1037 57 42 0 +1038 57 43 1 +1039 57 43 0 +1056 51 42 0 +1057 51 42 1 +1058 51 43 0 +1059 51 43 1 +1060 50 42 0 +1061 50 42 1 +1062 50 43 0 +1063 50 43 1 +1064 48 43 1 +1065 48 43 0 +1066 48 42 1 +1067 48 42 0 +1068 49 43 1 +1069 49 43 0 +1070 49 42 1 +1071 49 42 0 +1072 52 42 0 +1073 52 42 1 +1074 52 43 0 +1075 52 43 1 +1076 53 42 0 +1077 53 42 1 +1078 53 43 0 +1079 53 43 1 +1080 55 43 1 +1081 55 43 0 +1082 55 42 1 +1083 55 42 0 +1084 54 43 1 +1085 54 43 0 +1086 54 42 1 +1087 54 42 0 +1088 60 43 0 +1089 60 43 1 +1090 60 42 0 +1091 60 42 1 +1092 61 43 0 +1093 61 43 1 +1094 61 42 0 +1095 61 42 1 +1096 63 42 1 +1097 63 42 0 +1098 63 43 1 +1099 63 43 0 +1100 62 42 1 +1101 62 42 0 +1102 62 43 1 +1103 62 43 0 +1152 59 45 0 +1153 59 45 1 +1154 59 44 0 +1155 59 44 1 +1156 58 45 0 +1157 58 45 1 +1158 58 44 0 +1159 58 44 1 +1160 56 44 1 +1161 56 44 0 +1162 56 45 1 +1163 56 45 0 +1164 57 44 1 +1165 57 44 0 +1166 57 45 1 +1167 57 45 0 +1184 51 44 0 +1185 51 44 1 +1186 51 45 0 +1187 51 45 1 +1188 50 44 0 +1189 50 44 1 +1190 50 45 0 +1191 50 45 1 +1192 48 45 1 +1193 48 45 0 +1194 48 44 1 +1195 48 44 0 +1196 49 45 1 +1197 49 45 0 +1198 49 44 1 +1199 49 44 0 +1200 52 44 0 +1201 52 44 1 +1202 52 45 0 +1203 52 45 1 +1204 53 44 0 +1205 53 44 1 +1206 53 45 0 +1207 53 45 1 +1208 55 45 1 +1209 55 45 0 +1210 55 44 1 +1211 55 44 0 +1212 54 45 1 +1213 54 45 0 +1214 54 44 1 +1215 54 44 0 +1216 60 45 0 +1217 60 45 1 +1218 60 44 0 +1219 60 44 1 +1220 61 45 0 +1221 61 45 1 +1222 61 44 0 +1223 61 44 1 +1224 63 44 1 +1225 63 44 0 +1226 63 45 1 +1227 63 45 0 +1228 62 44 1 +1229 62 44 0 +1230 62 45 1 +1231 62 45 0 +1280 59 47 0 +1281 59 47 1 +1282 59 46 0 +1283 59 46 1 +1284 58 47 0 +1285 58 47 1 +1286 58 46 0 +1287 58 46 1 +1288 56 46 1 +1289 56 46 0 +1290 56 47 1 +1291 56 47 0 +1292 57 46 1 +1293 57 46 0 +1294 57 47 1 +1295 57 47 0 +1312 51 46 0 +1313 51 46 1 +1314 51 47 0 +1315 51 47 1 +1316 50 46 0 +1317 50 46 1 +1318 50 47 0 +1319 50 47 1 +1320 48 47 1 +1321 48 47 0 +1322 48 46 1 +1323 48 46 0 +1324 49 47 1 +1325 49 47 0 +1326 49 46 1 +1327 49 46 0 +1328 52 46 0 +1329 52 46 1 +1330 52 47 0 +1331 52 47 1 +1332 53 46 0 +1333 53 46 1 +1334 53 47 0 +1335 53 47 1 +1336 55 47 1 +1337 55 47 0 +1338 55 46 1 +1339 55 46 0 +1340 54 47 1 +1341 54 47 0 +1342 54 46 1 +1343 54 46 0 +1344 60 47 0 +1345 60 47 1 +1346 60 46 0 +1347 60 46 1 +1348 61 47 0 +1349 61 47 1 +1350 61 46 0 +1351 61 46 1 +1352 63 46 1 +1353 63 46 0 +1354 63 47 1 +1355 63 47 0 +1356 62 46 1 +1357 62 46 0 +1358 62 47 1 +1359 62 47 0 +1408 59 49 0 +1409 59 49 1 +1410 59 48 0 +1411 59 48 1 +1412 58 49 0 +1413 58 49 1 +1414 58 48 0 +1415 58 48 1 +1416 56 48 1 +1417 56 48 0 +1418 56 49 1 +1419 56 49 0 +1420 57 48 1 +1421 57 48 0 +1422 57 49 1 +1423 57 49 0 +1440 51 48 0 +1441 51 48 1 +1442 51 49 0 +1443 51 49 1 +1444 50 48 0 +1445 50 48 1 +1446 50 49 0 +1447 50 49 1 +1448 48 49 1 +1449 48 49 0 +1450 48 48 1 +1451 48 48 0 +1452 49 49 1 +1453 49 49 0 +1454 49 48 1 +1455 49 48 0 +1456 52 48 0 +1457 52 48 1 +1458 52 49 0 +1459 52 49 1 +1460 53 48 0 +1461 53 48 1 +1462 53 49 0 +1463 53 49 1 +1464 55 49 1 +1465 55 49 0 +1466 55 48 1 +1467 55 48 0 +1468 54 49 1 +1469 54 49 0 +1470 54 48 1 +1471 54 48 0 +1472 60 49 0 +1473 60 49 1 +1474 60 48 0 +1475 60 48 1 +1476 61 49 0 +1477 61 49 1 +1478 61 48 0 +1479 61 48 1 +1480 63 48 1 +1481 63 48 0 +1482 63 49 1 +1483 63 49 0 +1484 62 48 1 +1485 62 48 0 +1486 62 49 1 +1487 62 49 0 +1536 59 51 0 +1537 59 51 1 +1538 59 50 0 +1539 59 50 1 +1540 58 51 0 +1541 58 51 1 +1542 58 50 0 +1543 58 50 1 +1544 56 50 1 +1545 56 50 0 +1546 56 51 1 +1547 56 51 0 +1548 57 50 1 +1549 57 50 0 +1550 57 51 1 +1551 57 51 0 +1568 51 50 0 +1569 51 50 1 +1570 51 51 0 +1571 51 51 1 +1572 50 50 0 +1573 50 50 1 +1574 50 51 0 +1575 50 51 1 +1576 48 51 1 +1577 48 51 0 +1578 48 50 1 +1579 48 50 0 +1580 49 51 1 +1581 49 51 0 +1582 49 50 1 +1583 49 50 0 +1584 52 50 0 +1585 52 50 1 +1586 52 51 0 +1587 52 51 1 +1588 53 50 0 +1589 53 50 1 +1590 53 51 0 +1591 53 51 1 +1592 55 51 1 +1593 55 51 0 +1594 55 50 1 +1595 55 50 0 +1596 54 51 1 +1597 54 51 0 +1598 54 50 1 +1599 54 50 0 +1600 60 51 0 +1601 60 51 1 +1602 60 50 0 +1603 60 50 1 +1604 61 51 0 +1605 61 51 1 +1606 61 50 0 +1607 61 50 1 +1608 63 50 1 +1609 63 50 0 +1610 63 51 1 +1611 63 51 0 +1612 62 50 1 +1613 62 50 0 +1614 62 51 1 +1615 62 51 0 +1664 59 53 0 +1665 59 53 1 +1666 59 52 0 +1667 59 52 1 +1668 58 53 0 +1669 58 53 1 +1670 58 52 0 +1671 58 52 1 +1672 56 52 1 +1673 56 52 0 +1674 56 53 1 +1675 56 53 0 +1676 57 52 1 +1677 57 52 0 +1678 57 53 1 +1679 57 53 0 +1696 51 52 0 +1697 51 52 1 +1698 51 53 0 +1699 51 53 1 +1700 50 52 0 +1701 50 52 1 +1702 50 53 0 +1703 50 53 1 +1704 48 53 1 +1705 48 53 0 +1706 48 52 1 +1707 48 52 0 +1708 49 53 1 +1709 49 53 0 +1710 49 52 1 +1711 49 52 0 +1712 52 52 0 +1713 52 52 1 +1714 52 53 0 +1715 52 53 1 +1716 53 52 0 +1717 53 52 1 +1718 53 53 0 +1719 53 53 1 +1720 55 53 1 +1721 55 53 0 +1722 55 52 1 +1723 55 52 0 +1724 54 53 1 +1725 54 53 0 +1726 54 52 1 +1727 54 52 0 +1728 60 53 0 +1729 60 53 1 +1730 60 52 0 +1731 60 52 1 +1732 61 53 0 +1733 61 53 1 +1734 61 52 0 +1735 61 52 1 +1736 63 52 1 +1737 63 52 0 +1738 63 53 1 +1739 63 53 0 +1740 62 52 1 +1741 62 52 0 +1742 62 53 1 +1743 62 53 0 +1792 59 55 0 +1793 59 55 1 +1794 59 54 0 +1795 59 54 1 +1796 58 55 0 +1797 58 55 1 +1798 58 54 0 +1799 58 54 1 +1800 56 54 1 +1801 56 54 0 +1802 56 55 1 +1803 56 55 0 +1804 57 54 1 +1805 57 54 0 +1806 57 55 1 +1807 57 55 0 +1824 51 54 0 +1825 51 54 1 +1826 51 55 0 +1827 51 55 1 +1828 50 54 0 +1829 50 54 1 +1830 50 55 0 +1831 50 55 1 +1832 48 55 1 +1833 48 55 0 +1834 48 54 1 +1835 48 54 0 +1836 49 55 1 +1837 49 55 0 +1838 49 54 1 +1839 49 54 0 +1840 52 54 0 +1841 52 54 1 +1842 52 55 0 +1843 52 55 1 +1844 53 54 0 +1845 53 54 1 +1846 53 55 0 +1847 53 55 1 +1848 55 55 1 +1849 55 55 0 +1850 55 54 1 +1851 55 54 0 +1852 54 55 1 +1853 54 55 0 +1854 54 54 1 +1855 54 54 0 +1856 60 55 0 +1857 60 55 1 +1858 60 54 0 +1859 60 54 1 +1860 61 55 0 +1861 61 55 1 +1862 61 54 0 +1863 61 54 1 +1864 63 54 1 +1865 63 54 0 +1866 63 55 1 +1867 63 55 0 +1868 62 54 1 +1869 62 54 0 +1870 62 55 1 +1871 62 55 0 +2176 59 27 0 +2177 59 27 1 +2178 59 26 0 +2179 59 26 1 +2180 58 27 0 +2181 58 27 1 +2182 58 26 0 +2183 58 26 1 +2184 56 26 1 +2185 56 26 0 +2186 56 27 1 +2187 56 27 0 +2188 57 26 1 +2189 57 26 0 +2190 57 27 1 +2191 57 27 0 +2208 51 26 0 +2209 51 26 1 +2210 51 27 0 +2211 51 27 1 +2212 50 26 0 +2213 50 26 1 +2214 50 27 0 +2215 50 27 1 +2216 48 27 1 +2217 48 27 0 +2218 48 26 1 +2219 48 26 0 +2220 49 27 1 +2221 49 27 0 +2222 49 26 1 +2223 49 26 0 +2224 52 26 0 +2225 52 26 1 +2226 52 27 0 +2227 52 27 1 +2228 53 26 0 +2229 53 26 1 +2230 53 27 0 +2231 53 27 1 +2232 55 27 1 +2233 55 27 0 +2234 55 26 1 +2235 55 26 0 +2236 54 27 1 +2237 54 27 0 +2238 54 26 1 +2239 54 26 0 +2240 60 27 0 +2241 60 27 1 +2242 60 26 0 +2243 60 26 1 +2244 61 27 0 +2245 61 27 1 +2246 61 26 0 +2247 61 26 1 +2248 63 26 1 +2249 63 26 0 +2250 63 27 1 +2251 63 27 0 +2252 62 26 1 +2253 62 26 0 +2254 62 27 1 +2255 62 27 0 +2304 59 25 0 +2305 59 25 1 +2306 59 24 0 +2307 59 24 1 +2308 58 25 0 +2309 58 25 1 +2310 58 24 0 +2311 58 24 1 +2312 56 24 1 +2313 56 24 0 +2314 56 25 1 +2315 56 25 0 +2316 57 24 1 +2317 57 24 0 +2318 57 25 1 +2319 57 25 0 +2336 51 24 0 +2337 51 24 1 +2338 51 25 0 +2339 51 25 1 +2340 50 24 0 +2341 50 24 1 +2342 50 25 0 +2343 50 25 1 +2344 48 25 1 +2345 48 25 0 +2346 48 24 1 +2347 48 24 0 +2348 49 25 1 +2349 49 25 0 +2350 49 24 1 +2351 49 24 0 +2352 52 24 0 +2353 52 24 1 +2354 52 25 0 +2355 52 25 1 +2356 53 24 0 +2357 53 24 1 +2358 53 25 0 +2359 53 25 1 +2360 55 25 1 +2361 55 25 0 +2362 55 24 1 +2363 55 24 0 +2364 54 25 1 +2365 54 25 0 +2366 54 24 1 +2367 54 24 0 +2368 60 25 0 +2369 60 25 1 +2370 60 24 0 +2371 60 24 1 +2372 61 25 0 +2373 61 25 1 +2374 61 24 0 +2375 61 24 1 +2376 63 24 1 +2377 63 24 0 +2378 63 25 1 +2379 63 25 0 +2380 62 24 1 +2381 62 24 0 +2382 62 25 1 +2383 62 25 0 +2432 59 23 0 +2433 59 23 1 +2434 59 22 0 +2435 59 22 1 +2436 58 23 0 +2437 58 23 1 +2438 58 22 0 +2439 58 22 1 +2440 56 22 1 +2441 56 22 0 +2442 56 23 1 +2443 56 23 0 +2444 57 22 1 +2445 57 22 0 +2446 57 23 1 +2447 57 23 0 +2464 51 22 0 +2465 51 22 1 +2466 51 23 0 +2467 51 23 1 +2468 50 22 0 +2469 50 22 1 +2470 50 23 0 +2471 50 23 1 +2472 48 23 1 +2473 48 23 0 +2474 48 22 1 +2475 48 22 0 +2476 49 23 1 +2477 49 23 0 +2478 49 22 1 +2479 49 22 0 +2480 52 22 0 +2481 52 22 1 +2482 52 23 0 +2483 52 23 1 +2484 53 22 0 +2485 53 22 1 +2486 53 23 0 +2487 53 23 1 +2488 55 23 1 +2489 55 23 0 +2490 55 22 1 +2491 55 22 0 +2492 54 23 1 +2493 54 23 0 +2494 54 22 1 +2495 54 22 0 +2496 60 23 0 +2497 60 23 1 +2498 60 22 0 +2499 60 22 1 +2500 61 23 0 +2501 61 23 1 +2502 61 22 0 +2503 61 22 1 +2504 63 22 1 +2505 63 22 0 +2506 63 23 1 +2507 63 23 0 +2508 62 22 1 +2509 62 22 0 +2510 62 23 1 +2511 62 23 0 +2560 59 21 0 +2561 59 21 1 +2562 59 20 0 +2563 59 20 1 +2564 58 21 0 +2565 58 21 1 +2566 58 20 0 +2567 58 20 1 +2568 56 20 1 +2569 56 20 0 +2570 56 21 1 +2571 56 21 0 +2572 57 20 1 +2573 57 20 0 +2574 57 21 1 +2575 57 21 0 +2592 51 20 0 +2593 51 20 1 +2594 51 21 0 +2595 51 21 1 +2596 50 20 0 +2597 50 20 1 +2598 50 21 0 +2599 50 21 1 +2600 48 21 1 +2601 48 21 0 +2602 48 20 1 +2603 48 20 0 +2604 49 21 1 +2605 49 21 0 +2606 49 20 1 +2607 49 20 0 +2608 52 20 0 +2609 52 20 1 +2610 52 21 0 +2611 52 21 1 +2612 53 20 0 +2613 53 20 1 +2614 53 21 0 +2615 53 21 1 +2616 55 21 1 +2617 55 21 0 +2618 55 20 1 +2619 55 20 0 +2620 54 21 1 +2621 54 21 0 +2622 54 20 1 +2623 54 20 0 +2624 60 21 0 +2625 60 21 1 +2626 60 20 0 +2627 60 20 1 +2628 61 21 0 +2629 61 21 1 +2630 61 20 0 +2631 61 20 1 +2632 63 20 1 +2633 63 20 0 +2634 63 21 1 +2635 63 21 0 +2636 62 20 1 +2637 62 20 0 +2638 62 21 1 +2639 62 21 0 +2688 59 19 0 +2689 59 19 1 +2690 59 18 0 +2691 59 18 1 +2692 58 19 0 +2693 58 19 1 +2694 58 18 0 +2695 58 18 1 +2696 56 18 1 +2697 56 18 0 +2698 56 19 1 +2699 56 19 0 +2700 57 18 1 +2701 57 18 0 +2702 57 19 1 +2703 57 19 0 +2720 51 18 0 +2721 51 18 1 +2722 51 19 0 +2723 51 19 1 +2724 50 18 0 +2725 50 18 1 +2726 50 19 0 +2727 50 19 1 +2728 48 19 1 +2729 48 19 0 +2730 48 18 1 +2731 48 18 0 +2732 49 19 1 +2733 49 19 0 +2734 49 18 1 +2735 49 18 0 +2736 52 18 0 +2737 52 18 1 +2738 52 19 0 +2739 52 19 1 +2740 53 18 0 +2741 53 18 1 +2742 53 19 0 +2743 53 19 1 +2744 55 19 1 +2745 55 19 0 +2746 55 18 1 +2747 55 18 0 +2748 54 19 1 +2749 54 19 0 +2750 54 18 1 +2751 54 18 0 +2752 60 19 0 +2753 60 19 1 +2754 60 18 0 +2755 60 18 1 +2756 61 19 0 +2757 61 19 1 +2758 61 18 0 +2759 61 18 1 +2760 63 18 1 +2761 63 18 0 +2762 63 19 1 +2763 63 19 0 +2764 62 18 1 +2765 62 18 0 +2766 62 19 1 +2767 62 19 0 +2816 59 17 0 +2817 59 17 1 +2818 59 16 0 +2819 59 16 1 +2820 58 17 0 +2821 58 17 1 +2822 58 16 0 +2823 58 16 1 +2824 56 16 1 +2825 56 16 0 +2826 56 17 1 +2827 56 17 0 +2828 57 16 1 +2829 57 16 0 +2830 57 17 1 +2831 57 17 0 +2848 51 16 0 +2849 51 16 1 +2850 51 17 0 +2851 51 17 1 +2852 50 16 0 +2853 50 16 1 +2854 50 17 0 +2855 50 17 1 +2856 48 17 1 +2857 48 17 0 +2858 48 16 1 +2859 48 16 0 +2860 49 17 1 +2861 49 17 0 +2862 49 16 1 +2863 49 16 0 +2864 52 16 0 +2865 52 16 1 +2866 52 17 0 +2867 52 17 1 +2868 53 16 0 +2869 53 16 1 +2870 53 17 0 +2871 53 17 1 +2872 55 17 1 +2873 55 17 0 +2874 55 16 1 +2875 55 16 0 +2876 54 17 1 +2877 54 17 0 +2878 54 16 1 +2879 54 16 0 +2880 60 17 0 +2881 60 17 1 +2882 60 16 0 +2883 60 16 1 +2884 61 17 0 +2885 61 17 1 +2886 61 16 0 +2887 61 16 1 +2888 63 16 1 +2889 63 16 0 +2890 63 17 1 +2891 63 17 0 +2892 62 16 1 +2893 62 16 0 +2894 62 17 1 +2895 62 17 0 +2944 59 15 0 +2945 59 15 1 +2946 59 14 0 +2947 59 14 1 +2948 58 15 0 +2949 58 15 1 +2950 58 14 0 +2951 58 14 1 +2952 56 14 1 +2953 56 14 0 +2954 56 15 1 +2955 56 15 0 +2956 57 14 1 +2957 57 14 0 +2958 57 15 1 +2959 57 15 0 +2976 51 14 0 +2977 51 14 1 +2978 51 15 0 +2979 51 15 1 +2980 50 14 0 +2981 50 14 1 +2982 50 15 0 +2983 50 15 1 +2984 48 15 1 +2985 48 15 0 +2986 48 14 1 +2987 48 14 0 +2988 49 15 1 +2989 49 15 0 +2990 49 14 1 +2991 49 14 0 +2992 52 14 0 +2993 52 14 1 +2994 52 15 0 +2995 52 15 1 +2996 53 14 0 +2997 53 14 1 +2998 53 15 0 +2999 53 15 1 +3000 55 15 1 +3001 55 15 0 +3002 55 14 1 +3003 55 14 0 +3004 54 15 1 +3005 54 15 0 +3006 54 14 1 +3007 54 14 0 +3008 60 15 0 +3009 60 15 1 +3010 60 14 0 +3011 60 14 1 +3012 61 15 0 +3013 61 15 1 +3014 61 14 0 +3015 61 14 1 +3016 63 14 1 +3017 63 14 0 +3018 63 15 1 +3019 63 15 0 +3020 62 14 1 +3021 62 14 0 +3022 62 15 1 +3023 62 15 0 +3072 59 13 0 +3073 59 13 1 +3074 59 12 0 +3075 59 12 1 +3076 58 13 0 +3077 58 13 1 +3078 58 12 0 +3079 58 12 1 +3080 56 12 1 +3081 56 12 0 +3082 56 13 1 +3083 56 13 0 +3084 57 12 1 +3085 57 12 0 +3086 57 13 1 +3087 57 13 0 +3104 51 12 0 +3105 51 12 1 +3106 51 13 0 +3107 51 13 1 +3108 50 12 0 +3109 50 12 1 +3110 50 13 0 +3111 50 13 1 +3112 48 13 1 +3113 48 13 0 +3114 48 12 1 +3115 48 12 0 +3116 49 13 1 +3117 49 13 0 +3118 49 12 1 +3119 49 12 0 +3120 52 12 0 +3121 52 12 1 +3122 52 13 0 +3123 52 13 1 +3124 53 12 0 +3125 53 12 1 +3126 53 13 0 +3127 53 13 1 +3128 55 13 1 +3129 55 13 0 +3130 55 12 1 +3131 55 12 0 +3132 54 13 1 +3133 54 13 0 +3134 54 12 1 +3135 54 12 0 +3136 60 13 0 +3137 60 13 1 +3138 60 12 0 +3139 60 12 1 +3140 61 13 0 +3141 61 13 1 +3142 61 12 0 +3143 61 12 1 +3144 63 12 1 +3145 63 12 0 +3146 63 13 1 +3147 63 13 0 +3148 62 12 1 +3149 62 12 0 +3150 62 13 1 +3151 62 13 0 +3200 59 11 0 +3201 59 11 1 +3202 59 10 0 +3203 59 10 1 +3204 58 11 0 +3205 58 11 1 +3206 58 10 0 +3207 58 10 1 +3208 56 10 1 +3209 56 10 0 +3210 56 11 1 +3211 56 11 0 +3212 57 10 1 +3213 57 10 0 +3214 57 11 1 +3215 57 11 0 +3232 51 10 0 +3233 51 10 1 +3234 51 11 0 +3235 51 11 1 +3236 50 10 0 +3237 50 10 1 +3238 50 11 0 +3239 50 11 1 +3240 48 11 1 +3241 48 11 0 +3242 48 10 1 +3243 48 10 0 +3244 49 11 1 +3245 49 11 0 +3246 49 10 1 +3247 49 10 0 +3248 52 10 0 +3249 52 10 1 +3250 52 11 0 +3251 52 11 1 +3252 53 10 0 +3253 53 10 1 +3254 53 11 0 +3255 53 11 1 +3256 55 11 1 +3257 55 11 0 +3258 55 10 1 +3259 55 10 0 +3260 54 11 1 +3261 54 11 0 +3262 54 10 1 +3263 54 10 0 +3264 60 11 0 +3265 60 11 1 +3266 60 10 0 +3267 60 10 1 +3268 61 11 0 +3269 61 11 1 +3270 61 10 0 +3271 61 10 1 +3272 63 10 1 +3273 63 10 0 +3274 63 11 1 +3275 63 11 0 +3276 62 10 1 +3277 62 10 0 +3278 62 11 1 +3279 62 11 0 +3328 59 9 0 +3329 59 9 1 +3330 59 8 0 +3331 59 8 1 +3332 58 9 0 +3333 58 9 1 +3334 58 8 0 +3335 58 8 1 +3336 56 8 1 +3337 56 8 0 +3338 56 9 1 +3339 56 9 0 +3340 57 8 1 +3341 57 8 0 +3342 57 9 1 +3343 57 9 0 +3360 51 8 0 +3361 51 8 1 +3362 51 9 0 +3363 51 9 1 +3364 50 8 0 +3365 50 8 1 +3366 50 9 0 +3367 50 9 1 +3368 48 9 1 +3369 48 9 0 +3370 48 8 1 +3371 48 8 0 +3372 49 9 1 +3373 49 9 0 +3374 49 8 1 +3375 49 8 0 +3376 52 8 0 +3377 52 8 1 +3378 52 9 0 +3379 52 9 1 +3380 53 8 0 +3381 53 8 1 +3382 53 9 0 +3383 53 9 1 +3384 55 9 1 +3385 55 9 0 +3386 55 8 1 +3387 55 8 0 +3388 54 9 1 +3389 54 9 0 +3390 54 8 1 +3391 54 8 0 +3392 60 9 0 +3393 60 9 1 +3394 60 8 0 +3395 60 8 1 +3396 61 9 0 +3397 61 9 1 +3398 61 8 0 +3399 61 8 1 +3400 63 8 1 +3401 63 8 0 +3402 63 9 1 +3403 63 9 0 +3404 62 8 1 +3405 62 8 0 +3406 62 9 1 +3407 62 9 0 +3456 59 7 0 +3457 59 7 1 +3458 59 6 0 +3459 59 6 1 +3460 58 7 0 +3461 58 7 1 +3462 58 6 0 +3463 58 6 1 +3464 56 6 1 +3465 56 6 0 +3466 56 7 1 +3467 56 7 0 +3468 57 6 1 +3469 57 6 0 +3470 57 7 1 +3471 57 7 0 +3488 51 6 0 +3489 51 6 1 +3490 51 7 0 +3491 51 7 1 +3492 50 6 0 +3493 50 6 1 +3494 50 7 0 +3495 50 7 1 +3496 48 7 1 +3497 48 7 0 +3498 48 6 1 +3499 48 6 0 +3500 49 7 1 +3501 49 7 0 +3502 49 6 1 +3503 49 6 0 +3504 52 6 0 +3505 52 6 1 +3506 52 7 0 +3507 52 7 1 +3508 53 6 0 +3509 53 6 1 +3510 53 7 0 +3511 53 7 1 +3512 55 7 1 +3513 55 7 0 +3514 55 6 1 +3515 55 6 0 +3516 54 7 1 +3517 54 7 0 +3518 54 6 1 +3519 54 6 0 +3520 60 7 0 +3521 60 7 1 +3522 60 6 0 +3523 60 6 1 +3524 61 7 0 +3525 61 7 1 +3526 61 6 0 +3527 61 6 1 +3528 63 6 1 +3529 63 6 0 +3530 63 7 1 +3531 63 7 0 +3532 62 6 1 +3533 62 6 0 +3534 62 7 1 +3535 62 7 0 +3584 59 5 0 +3585 59 5 1 +3586 59 4 0 +3587 59 4 1 +3588 58 5 0 +3589 58 5 1 +3590 58 4 0 +3591 58 4 1 +3592 56 4 1 +3593 56 4 0 +3594 56 5 1 +3595 56 5 0 +3596 57 4 1 +3597 57 4 0 +3598 57 5 1 +3599 57 5 0 +3616 51 4 0 +3617 51 4 1 +3618 51 5 0 +3619 51 5 1 +3620 50 4 0 +3621 50 4 1 +3622 50 5 0 +3623 50 5 1 +3624 48 5 1 +3625 48 5 0 +3626 48 4 1 +3627 48 4 0 +3628 49 5 1 +3629 49 5 0 +3630 49 4 1 +3631 49 4 0 +3632 52 4 0 +3633 52 4 1 +3634 52 5 0 +3635 52 5 1 +3636 53 4 0 +3637 53 4 1 +3638 53 5 0 +3639 53 5 1 +3640 55 5 1 +3641 55 5 0 +3642 55 4 1 +3643 55 4 0 +3644 54 5 1 +3645 54 5 0 +3646 54 4 1 +3647 54 4 0 +3648 60 5 0 +3649 60 5 1 +3650 60 4 0 +3651 60 4 1 +3652 61 5 0 +3653 61 5 1 +3654 61 4 0 +3655 61 4 1 +3656 63 4 1 +3657 63 4 0 +3658 63 5 1 +3659 63 5 0 +3660 62 4 1 +3661 62 4 0 +3662 62 5 1 +3663 62 5 0 +3712 59 3 0 +3713 59 3 1 +3714 59 2 0 +3715 59 2 1 +3716 58 3 0 +3717 58 3 1 +3718 58 2 0 +3719 58 2 1 +3720 56 2 1 +3721 56 2 0 +3722 56 3 1 +3723 56 3 0 +3724 57 2 1 +3725 57 2 0 +3726 57 3 1 +3727 57 3 0 +3744 51 2 0 +3745 51 2 1 +3746 51 3 0 +3747 51 3 1 +3748 50 2 0 +3749 50 2 1 +3750 50 3 0 +3751 50 3 1 +3752 48 3 1 +3753 48 3 0 +3754 48 2 1 +3755 48 2 0 +3756 49 3 1 +3757 49 3 0 +3758 49 2 1 +3759 49 2 0 +3760 52 2 0 +3761 52 2 1 +3762 52 3 0 +3763 52 3 1 +3764 53 2 0 +3765 53 2 1 +3766 53 3 0 +3767 53 3 1 +3768 55 3 1 +3769 55 3 0 +3770 55 2 1 +3771 55 2 0 +3772 54 3 1 +3773 54 3 0 +3774 54 2 1 +3775 54 2 0 +3776 60 3 0 +3777 60 3 1 +3778 60 2 0 +3779 60 2 1 +3780 61 3 0 +3781 61 3 1 +3782 61 2 0 +3783 61 2 1 +3784 63 2 1 +3785 63 2 0 +3786 63 3 1 +3787 63 3 0 +3788 62 2 1 +3789 62 2 0 +3790 62 3 1 +3791 62 3 0 +3840 59 1 0 +3841 59 1 1 +3842 59 0 0 +3843 59 0 1 +3844 58 1 0 +3845 58 1 1 +3846 58 0 0 +3847 58 0 1 +3848 56 0 1 +3849 56 0 0 +3850 56 1 1 +3851 56 1 0 +3852 57 0 1 +3853 57 0 0 +3854 57 1 1 +3855 57 1 0 +3872 51 0 0 +3873 51 0 1 +3874 51 1 0 +3875 51 1 1 +3876 50 0 0 +3877 50 0 1 +3878 50 1 0 +3879 50 1 1 +3880 48 1 1 +3881 48 1 0 +3882 48 0 1 +3883 48 0 0 +3884 49 1 1 +3885 49 1 0 +3886 49 0 1 +3887 49 0 0 +3888 52 0 0 +3889 52 0 1 +3890 52 1 0 +3891 52 1 1 +3892 53 0 0 +3893 53 0 1 +3894 53 1 0 +3895 53 1 1 +3896 55 1 1 +3897 55 1 0 +3898 55 0 1 +3899 55 0 0 +3900 54 1 1 +3901 54 1 0 +3902 54 0 1 +3903 54 0 0 +3904 60 1 0 +3905 60 1 1 +3906 60 0 0 +3907 60 0 1 +3908 61 1 0 +3909 61 1 1 +3910 61 0 0 +3911 61 0 1 +3912 63 0 1 +3913 63 0 0 +3914 63 1 1 +3915 63 1 0 +3916 62 0 1 +3917 62 0 0 +3918 62 1 1 +3919 62 1 0 diff --git a/Detectors/PHOS/base/include/PHOSBase/Geometry.h b/Detectors/PHOS/base/include/PHOSBase/Geometry.h index e8be99ff6094b..8a48225c28dc0 100644 --- a/Detectors/PHOS/base/include/PHOSBase/Geometry.h +++ b/Detectors/PHOS/base/include/PHOSBase/Geometry.h @@ -13,6 +13,7 @@ #include <string> +#include <Rtypes.h> #include <RStringView.h> #include <TMath.h> @@ -114,6 +115,8 @@ class Geometry static Geometry* sGeom; // Pointer to the unique instance of the singleton std::string mGeoName; ///< Geometry name string + + ClassDefNV(Geometry, 1); }; } // namespace phos } // namespace o2 diff --git a/Detectors/PHOS/base/include/PHOSBase/Hit.h b/Detectors/PHOS/base/include/PHOSBase/Hit.h index 5214c21861999..f987158686a1f 100644 --- a/Detectors/PHOS/base/include/PHOSBase/Hit.h +++ b/Detectors/PHOS/base/include/PHOSBase/Hit.h @@ -38,7 +38,7 @@ class Hit : public o2::BasicXYZEHit<float> /// \param initialEnergy Energy of the primary particle enering the EMCAL /// \param tof Time of the hit /// \param length Length of the segment - Hit(Int_t trackID, Int_t detID, const Point3D<float>& pos, const Vector3D<float>& mom, Double_t totE, Double_t tof, + Hit(Int_t trackID, Int_t detID, const math_utils::Point3D<float>& pos, const math_utils::Vector3D<float>& mom, Double_t totE, Double_t tof, Double_t eLoss) : o2::BasicXYZEHit<float>(pos.X(), pos.Y(), pos.Z(), tof, eLoss, trackID, detID), mPvector(mom), @@ -84,8 +84,8 @@ class Hit : public o2::BasicXYZEHit<float> void PrintStream(std::ostream& stream) const; private: - Vector3D<float> mPvector; // Momentum Vector - Double32_t mInitialEnergy; // Energy of the parent particle that entered the PHOS front surface + math_utils::Vector3D<float> mPvector; // Momentum Vector + Double32_t mInitialEnergy; // Energy of the parent particle that entered the PHOS front surface ClassDefNV(Hit, 1); }; diff --git a/Detectors/PHOS/base/include/PHOSBase/Mapping.h b/Detectors/PHOS/base/include/PHOSBase/Mapping.h new file mode 100644 index 0000000000000..ea3b5a9080871 --- /dev/null +++ b/Detectors/PHOS/base/include/PHOSBase/Mapping.h @@ -0,0 +1,86 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Mapping +/// \brief Checks validity of hardware address (HW) and transform it to digit AbsId index +/// +/// \author Dmitri Peresunko +/// \since Jan.2020 +/// + +#ifndef PHOSMAPPING_H_ +#define PHOSMAPPING_H_ + +#include <string_view> +#include <vector> +#include <utility> +#include "Rtypes.h" + +namespace o2 +{ + +namespace phos +{ + +class Mapping +{ + public: + enum ErrorStatus { kOK, + kWrongDDL, + kWrongHWAddress, + kWrongAbsId, + kWrongCaloFlag, + kNotInitialized }; + static constexpr short NCHANNELS = 14337; ///< Number of channels starting from 1 + static constexpr short NHWPERDDL = 2048; ///< Number of HW addressed per DDL + static constexpr short NMaxHWAddress = 3929; ///< Maximal HW address (size of array) + static constexpr short NDDL = 14; ///< Total number of DDLs + + enum CaloFlag { kHighGain, + kLowGain, + kTRU }; + + Mapping() = default; + Mapping(std::basic_string_view<char> path); + ~Mapping() = default; + + /// \brief convert hardware address to absId and caloFlag + ErrorStatus hwToAbsId(short ddl, short hw, short& absId, CaloFlag& caloFlag); + /// \brief convert absId and caloflag to hardware address and ddl + ErrorStatus absIdTohw(short absId, short caloFlag, short& ddl, short& hwAddr); + + /// \brief convert ddl number to crorc and link number (TODO!!!) + void ddlToCrorcLink(short iddl, short& crorc, short& link) + { + crorc = iddl / 8; + link = iddl % 8; + } + + ErrorStatus setMapping(); + + protected: + /// \brief Construct vector for conversion only if necessary + ErrorStatus constructAbsToHWMatrix(); + + private: + std::string mPath = ""; ///< path to mapping files + bool mInitialized = false; ///< If conversion tables created + bool mInvInitialized = false; ///< If inverse conversion tables created + short mAbsId[NDDL][NMaxHWAddress] = {0}; ///< Conversion table (ddl,branch,fec,chip,channel) to absId + CaloFlag mCaloFlag[NDDL][NMaxHWAddress] = {kTRU}; ///< Conversion table (ddl,branch,fec,chip,channel) to absId + short mAbsToHW[NCHANNELS][3][2] = {0}; ///< Conversion table (AbsId,caloFlag) to pair (ddl, hw address) + + ClassDefNV(Mapping, 1); +}; // End of Mapping + +} // namespace phos + +} // namespace o2 +#endif diff --git a/Detectors/PHOS/base/include/PHOSBase/PHOSSimParams.h b/Detectors/PHOS/base/include/PHOSBase/PHOSSimParams.h index ceb1c49ed22eb..c777d13bbd8af 100644 --- a/Detectors/PHOS/base/include/PHOSBase/PHOSSimParams.h +++ b/Detectors/PHOS/base/include/PHOSBase/PHOSSimParams.h @@ -35,7 +35,7 @@ struct PHOSSimParams : public o2::conf::ConfigurableParamHelper<PHOSSimParams> { bool mApplyNonLinearity = false; ///< Apply energy non-linearity in digitization bool mApplyDigitization = false; ///< Apply energy digitization in digitization float mAPDNoise = 0.005; ///< RMS of APD noise - float mDigitThreshold = 2.5; ///< minimal energy to keep digit in ADC counts + float mDigitThreshold = 2.; ///< minimal energy to keep digit in ADC counts float mADCwidth = 0.005; ///< width of ADC channel in GeV float mTOFa = 0.5e-9; ///< constant term of TOF resolution float mTOFb = 1.e-9; ///< stohastic term of TOF resolution @@ -43,13 +43,30 @@ struct PHOSSimParams : public o2::conf::ConfigurableParamHelper<PHOSSimParams> { float mCellNonLineaityB = 0.109; ///< Energy scale of cel non-linearity float mCellNonLineaityC = 1.; ///< Overall calibration - float mZSthreshold = 2.5; ///< Zero Suppression threshold + short mZSthreshold = 1; ///< Zero Suppression threshold float mTimeResolutionA = 2.; ///< Time resolution parameter A (in ns) float mTimeResolutionB = 2.; ///< Time resolution parameter B (in ns/GeV) float mTimeResThreshold = 0.5; ///< threshold for time resolution calculation (in GeV) float mMinNoiseTime = -200.; ///< minimum time in noise channels (in ns) float mMaxNoiseTime = 2000.; ///< minimum time in noise channels (in ns) + //Parameters used in Raw simulation + float mSampleDecayTime = 0.091; ///< Time parameter in Gamma2 function (1/tau, 100.e-9/2.1e-6) + + // //Parameters used in raw data reconstruction + short mSpikeThreshold = 100; ///< Single spike >100 ADC channels + short mBaseLine = 0; ///< + short mPreSamples = 2; ///< number of pre-samples readout before sample (if no pedestal subtrauction) + short mMCOverflow = 970; ///< Overflow level for MC simulations: 1023-(pedestal~50) + float mTimeTick = 100.; ///< ns to PHOS digitization step conversion + + // bool mSubtractPedestal = false ; ///< subtract pedestals + // bool mCreateSampleQualityOutput = false ; ///< Create stream of sample quality + // bool mApplyBadMap = false ; ///< Apply bad map in sample fitting + // short mChiMinCut = 0 ; ///< Minimal cut on sample quality + // short mChiMaxCut = 1000; ///< Maximal cut on sample quality + // std::string mFitterVersion = "default"; ///< version of raw fitter to be used + //Parameters used in clusterization float mLogWeight = 4.5; ///< Cutoff used in log. weight calculation float mDigitMinEnergy = 0.010; ///< Minimal energy of digits to be used in cluster (GeV) diff --git a/Detectors/PHOS/base/include/PHOSBase/RCUTrailer.h b/Detectors/PHOS/base/include/PHOSBase/RCUTrailer.h new file mode 100644 index 0000000000000..566ab1141a328 --- /dev/null +++ b/Detectors/PHOS/base/include/PHOSBase/RCUTrailer.h @@ -0,0 +1,208 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RCUTRAILER_H +#define ALICEO2_PHOS_RCUTRAILER_H + +#include <exception> +#include <iosfwd> +#include <string> +#include <cstdint> +#include <gsl/span> +#include "Rtypes.h" + +namespace o2 +{ + +namespace phos +{ + +union ChannelHeader { + uint32_t mDataWord; + struct { + uint32_t mHardwareAddress : 16; ///< Bits 0 - 15: Hardware address + uint32_t mPayloadSize : 10; ///< Bits 16 - 25: Payload size + uint32_t mZero1 : 3; ///< Bits 26 - 28: zeroed + uint32_t mBadChannel : 1; ///< Bit 29: Bad channel status + uint32_t mMark : 1; ///< Bits 30 - 30: Mark header + }; +}; + +union CaloBunchWord { + uint32_t mDataWord; + struct { + uint32_t mWord2 : 10; ///< Bits 0 - 9 : Word 2 + uint32_t mWord1 : 10; ///< Bits 10 - 19 : Word 1 + uint32_t mWord0 : 10; ///< Bits 20 - 29 : Word 0 + uint32_t mZero : 2; ///< Bits 30 - 31 : zeroed + }; +}; + +/// \class RCUTrailer +/// \brief Information stored in the RCU trailer +/// \ingroup PHOSbase +/// +/// The RCU trailer can be found at the end of +/// the payload and contains general information +/// sent by the SRU. +class RCUTrailer +{ + public: + /// \class Error + /// \brief Error handling of the + class Error : public std::exception + { + public: + /// \enum ErrorType_t + /// \brief Error codes for different error types + enum class ErrorType_t { + DECODING_INVALID, ///< Invalid words during decoding + SIZE_INVALID, ///< Invalid trailer size + SAMPLINGFREQ_INVALID, ///< Invalid sampling frequency + L1PHASE_INVALID ///< Invalid L1 phase + }; + + /// \brief Constructor + /// \param errtype Code of the error type + /// \param message corresponding error message + /// + /// Initializing the error with error code and message. + /// To be called when the exception is raised. + Error(ErrorType_t errtype, const char* message) : mErrorType(errtype), mErrorMessage(message) {} + + /// \brief Destructor + ~Error() noexcept override = default; + + /// \brief Access to the error message + /// \return Error message related to the exception type + const char* what() const noexcept override { return mErrorMessage.data(); } + + /// \brief Access to error code + /// \return Error code of the exception type + ErrorType_t getErrorType() const noexcept { return mErrorType; } + + private: + ErrorType_t mErrorType; ///< Type of the error + std::string mErrorMessage; ///< Error Message + }; + + /// \brief Constructor + RCUTrailer() = default; + + RCUTrailer(const gsl::span<const uint32_t> payloadwords); + + /// \brief destructor + ~RCUTrailer() = default; + + /// \brief Reset the RCU trailer + /// + /// Setting all values to 0 + void reset(); + + /// \brief Prints the contents of the RCU trailer data + /// \param stream stream the trailer has to be put on + void printStream(std::ostream& stream) const; + + /// \brief Decode RCU trailer from the 32-bit words in the raw buffer + /// \param buffer Raw buffer from which to read the trailer + /// + /// Read the RCU trailer according to the RCU formware version + /// specified in CDH. + void constructFromRawPayload(const gsl::span<const uint32_t> payloadwords); + + unsigned int getFECErrorsA() const { return mFECERRA; } + unsigned int getFECErrorsB() const { return mFECERRB; } + unsigned short getErrorsG2() const { return mERRREG2; } + unsigned int getErrorsG3() const { return mERRREG3; } + unsigned short getActiveFECsA() const { return mActiveFECsA; } + unsigned short getActiveFECsB() const { return mActiveFECsB; } + unsigned int getAltroCFGReg1() const { return mAltroCFG1; } + unsigned int getAltroCFGReg2() const { return mAltroCFG2; } + int getRCUID() const { return mRCUId; } + unsigned int getTrailerSize() const { return mTrailerSize; } + unsigned int getPayloadSize() const { return mPayloadSize; } + unsigned char getFirmwareVersion() const { return mFirmwareVersion; } + + unsigned short getNumberOfChannelAddressMismatch() const { return (mERRREG3 & 0xFFF); } + unsigned short getNumberOfChannelLengthMismatch() const { return ((mERRREG3 >> 12) & 0x1FFF); } + unsigned char getBaselineCorrection() const { return mAltroCFG1 & 0xF; } + bool getPolarity() const { return (mAltroCFG1 >> 4) & 0x1; } + unsigned char getNumberOfPresamples() const { return (mAltroCFG1 >> 5) & 0x3; } + unsigned char getNumberOfPostsamples() const { return (mAltroCFG1 >> 7) & 0xF; } + bool hasSecondBaselineCorr() const { return (mAltroCFG1 >> 11) & 0x1; } + unsigned char getGlitchFilter() const { return (mAltroCFG1 >> 12) & 0x3; } + unsigned char getNumberOfNonZeroSuppressedPostsamples() const { return (mAltroCFG1 >> 14) & 0x7; } + unsigned char getNumberOfNonZeroSuppressedPresamples() const { return (mAltroCFG1 >> 17) & 0x3; } + bool hasZeroSuppression() const { return (mAltroCFG1 >> 19) & 0x1; } + bool getNumberOfAltroBuffers() const { return (mAltroCFG2 >> 24) & 0x1; } + unsigned char getNumberOfPretriggerSamples() const { return (mAltroCFG2 >> 20) & 0xF; } + unsigned short getNumberOfSamplesPerChannel() const { return (mAltroCFG2 >> 10) & 0x3FF; } + bool isSparseReadout() const { return (mAltroCFG2 >> 9) & 0x1; } + + /// \brief Access to the sampling time + /// \return Sampling time in seconds. + /// \throw Error if the RCU trailer was not properly initializied + double getTimeSample() const; + + /// \brief set time sample + /// \param timesample Time sample (in ns) + void setTimeSample(double timesample); + + /// \brief Access to the L1 phase + /// \return L1 phase w.r.t to the LHC clock + double getL1Phase() const; + + /// \brief Set the L1 phase + /// \param l1phase L1 phase (in ns) + void setL1Phase(double l1phase); + + void setFECErrorsA(unsigned int value) { mFECERRA = value; } + void setFECErrorsB(unsigned int value) { mFECERRB = value; } + void setErrorsG2(unsigned short value) { mERRREG2 = value; } + void setErrorsG3(unsigned int value) { mERRREG3 = value; } + void setActiveFECsA(unsigned short value) { mActiveFECsA = value; } + void setActiveFECsB(unsigned short value) { mActiveFECsB = value; } + void setAltroCFGReg1(unsigned int value) { mAltroCFG1 = value; } + void setAltroCFGReg2(unsigned int value) { mAltroCFG2 = value; } + void setFirmwareVersion(unsigned char version) { mFirmwareVersion = version; } + void setPayloadSize(unsigned int size) { mPayloadSize = size; } + + /// \brief checlks whether the RCU trailer is initialzied + /// \return True if the trailer is initialized, false otherwise + bool isInitialized() const { return mIsInitialized; } + + std::vector<uint32_t> encode() const; + + static RCUTrailer constructFromPayloadWords(const gsl::span<const uint32_t> payloadwords); + + private: + int mRCUId = -1; ///< current RCU identifier + unsigned char mFirmwareVersion = 0; ///< RCU firmware version + unsigned int mTrailerSize = 0; ///< Size of the trailer (in number of 32 bit words) + unsigned int mPayloadSize = 0; ///< Size of the payload (in nunber of 32 bit words) + unsigned int mFECERRA = 0; ///< contains errors related to ALTROBUS transactions + unsigned int mFECERRB = 0; ///< contains errors related to ALTROBUS transactions + unsigned short mERRREG2 = 0; ///< contains errors related to ALTROBUS transactions or trailer of ALTRO channel block + unsigned int mERRREG3 = 0; ///< contains number of altro channels skipped due to an address mismatch + unsigned short mActiveFECsA = 0; ///< bit pattern of active FECs in branch A + unsigned short mActiveFECsB = 0; ///< bit pattern of active FECs in branch B + unsigned int mAltroCFG1 = 0; ///< ALTROCFG1 register + unsigned int mAltroCFG2 = 0; ///< ALTROCFG2 and ALTROIF register + bool mIsInitialized = false; ///< Flag whether RCU trailer is initialized for the given raw event + + ClassDefNV(RCUTrailer, 1); +}; + +std::ostream& operator<<(std::ostream& stream, const RCUTrailer& trailer); + +} // namespace phos + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/base/src/Geometry.cxx b/Detectors/PHOS/base/src/Geometry.cxx index 64666a4c7e3b8..49d4c2cfe4247 100644 --- a/Detectors/PHOS/base/src/Geometry.cxx +++ b/Detectors/PHOS/base/src/Geometry.cxx @@ -19,19 +19,14 @@ Geometry* Geometry::sGeom = nullptr; Geometry::Geometry(const std::string_view name) : mGeoName(name) {} -// static Geometry* Geometry::GetInstance(const std::string_view name) -// { -// if(sGeom){ -// if(sGeom->GetName()==name){ -// return sGeom; -// } -// else{ -// delete sGeom ; -// } -// } -// sGeom = new Geometry(name) ; -// return sGeom; -// } +// module numbering: +// start from module 0 (non-existing), 1 (half-module), 2 (bottom),... 4(highest) +// absId: +// start from 1 till 5*64*56. Numbering in each module starts at bottom left and first go in z direction: +// 56 112 3584 +// ... ... ... +// 1 57 ...3529 +// relid[3]: (module number[0...4], iphi[1...64], iz[1...56]) short Geometry::relToAbsId(char moduleNumber, int strip, int cell) { @@ -45,7 +40,7 @@ short Geometry::relToAbsId(char moduleNumber, int strip, int cell) short row = nStrpZ - (strip - 1) % nStrpZ; short col = (int)std::ceil((float)strip / (nStrpZ)) - 1; - return (moduleNumber - 1) * nCrystalsInModule + row * 2 + (col * nCellsXInStrip + (cell - 1) / 2) * nZ - + return moduleNumber * nCrystalsInModule + row * 2 + (col * nCellsXInStrip + (cell - 1) / 2) * nZ - (cell & 1 ? 1 : 0); } @@ -60,7 +55,7 @@ bool Geometry::absToRelNumbering(short absId, char* relid) short phosmodulenumber = (absId - 1) / (nZ * nPhi); - relid[0] = phosmodulenumber + 1; + relid[0] = phosmodulenumber; absId -= phosmodulenumber * nPhi * nZ; relid[1] = 1 + (absId - 1) / nZ; relid[2] = absId - (relid[1] - 1) * nZ; @@ -72,7 +67,7 @@ char Geometry::absIdToModule(short absId) const short nZ = 56; const short nPhi = 64; - return 1 + (absId - 1) / (nZ * nPhi); + return (absId - 1) / (nZ * nPhi); } int Geometry::areNeighbours(short absId1, short absId2) @@ -100,14 +95,16 @@ int Geometry::areNeighbours(short absId1, short absId2) if ((coldiff <= 1) && (rowdiff <= 1)) { // At least common vertex return 1; } else { - if ((relid2[1] > relid1[1]) && (relid2[2] > relid1[2] + 1)) + if ((relid2[1] > relid1[1]) && (relid2[2] > relid1[2] + 1)) { return 2; // Difference in row numbers is too large to look further + } } return 0; } else { - if (relid1[0] > relid2[0]) // we switched to the next module + if (relid1[0] > relid2[0]) { // we switched to the next module return -1; + } return 2; } return 0; @@ -129,9 +126,9 @@ bool Geometry::relToAbsNumbering(const char* relId, short& absId) const short nPhi = 64; // nStripZ * nCellsZInStrip absId = - (relId[0] - 1) * nPhi * nZ + // the offset of PHOS modules - (relId[1] - 1) * nZ + // the offset along phi - relId[2]; // the offset along z + relId[0] * nPhi * nZ + // the offset of PHOS modules + (relId[1] - 1) * nZ + // the offset along phi + relId[2]; // the offset along z return true; } diff --git a/Detectors/PHOS/base/src/Hit.cxx b/Detectors/PHOS/base/src/Hit.cxx index f27ede8c0930f..9c767ab3db959 100644 --- a/Detectors/PHOS/base/src/Hit.cxx +++ b/Detectors/PHOS/base/src/Hit.cxx @@ -33,8 +33,9 @@ void Hit::PrintStream(std::ostream& stream) const Bool_t Hit::operator<(const Hit& rhs) const { - if (GetDetectorID() == rhs.GetDetectorID()) + if (GetDetectorID() == rhs.GetDetectorID()) { return GetTrackID() < rhs.GetTrackID(); + } return GetDetectorID() < rhs.GetDetectorID(); } @@ -45,8 +46,9 @@ Bool_t Hit::operator==(const Hit& rhs) const Hit& Hit::operator+=(const Hit& rhs) { - if (rhs.GetEnergyLoss() > GetEnergyLoss()) + if (rhs.GetEnergyLoss() > GetEnergyLoss()) { SetTime(rhs.GetTime()); + } SetEnergyLoss(GetEnergyLoss() + rhs.GetEnergyLoss()); return *this; } @@ -54,8 +56,9 @@ Hit& Hit::operator+=(const Hit& rhs) Hit Hit::operator+(const Hit& rhs) const { Hit result(*this); - if (rhs.GetEnergyLoss() > result.GetEnergyLoss()) + if (rhs.GetEnergyLoss() > result.GetEnergyLoss()) { result.SetTime(rhs.GetTime()); + } result.SetEnergyLoss(result.GetEnergyLoss() + rhs.GetEnergyLoss()); return *this; } diff --git a/Detectors/PHOS/base/src/Mapping.cxx b/Detectors/PHOS/base/src/Mapping.cxx new file mode 100644 index 0000000000000..4c2e914431408 --- /dev/null +++ b/Detectors/PHOS/base/src/Mapping.cxx @@ -0,0 +1,175 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Mapping.cxx +/// \author Dmitri Peresunko + +#include <fstream> +#include "TSystem.h" +#include "FairLogger.h" +#include "PHOSBase/Mapping.h" +#include "PHOSBase/Geometry.h" + +using namespace o2::phos; + +Mapping::Mapping(std::basic_string_view<char> path) : mPath(path), + mInitialized(false) +{ +} +//_______________________________________________________ +Mapping::ErrorStatus Mapping::hwToAbsId(short ddl, short hwAddr, short& absId, CaloFlag& caloFlag) +{ + + if (!mInitialized) { + LOG(ERROR) << "Mapping not initialized"; + return kNotInitialized; + } + + if (ddl < 0 || ddl > 14) { + return kWrongDDL; + } + if (hwAddr < 0 || hwAddr >= NMaxHWAddress) { + return kWrongHWAddress; + } + + //transform + absId = mAbsId[ddl][hwAddr]; + caloFlag = mCaloFlag[ddl][hwAddr]; + + if (absId > NCHANNELS) { + absId = 0; + return kWrongHWAddress; + } + return kOK; +} +//_______________________________________________________ +Mapping::ErrorStatus Mapping::absIdTohw(short absId, short caloFlag, short& ddl, short& hwAddr) +{ + + if (absId < 0 || absId > NCHANNELS) { + ddl = 0; + hwAddr = 0; + return kWrongAbsId; + } + if (caloFlag < 0 || caloFlag > 2) { + ddl = 0; + hwAddr = 0; + return kWrongCaloFlag; + } + + if (!mInitialized) { + LOG(ERROR) << "Mapping not initialized"; + return kNotInitialized; + } + + ddl = mAbsToHW[absId][caloFlag][0]; + hwAddr = mAbsToHW[absId][caloFlag][1]; + return kOK; +} +//_______________________________________________________ +Mapping::ErrorStatus Mapping::setMapping() +{ + //Read mapping from data files a-la Run2 + + o2::phos::Geometry* geom = o2::phos::Geometry::GetInstance(); + + std::string p; + if (mPath.empty()) { //use default path + p = gSystem->Getenv("O2_ROOT"); + p += "/share/Detectors/PHOS/files"; + } else { + p = mPath.data(); + } + + for (short m = 0; m < 4; m++) { //modules + for (short i = 0; i < 4; i++) { //RCU + if (m == 0 && (i < 2)) { + continue; //half of module: only RCU 2,3 + } + + short numberOfChannels = 0; + short maxHWAddress = 0; + char fname[255]; + snprintf(fname, 255, "%s/Mod%dRCU%d.data", p.data(), m, i); + std::ifstream* fIn = new std::ifstream(fname); + if (!*fIn) { + LOG(FATAL) << "Missing mapping file " << p << "/Mod" << m << "RCU" << i << ".data"; + return kNotInitialized; + } + if (!(*fIn >> numberOfChannels)) { + LOG(FATAL) << "Syntax of mapping file " << p << "/Mod" << m << "RCU" << i << ".data is wrong: no numberOfChannels"; + return kNotInitialized; + } + if (numberOfChannels != NHWPERDDL) { + LOG(FATAL) << "Unexpected number of channels: " << numberOfChannels << " expecting " << NHWPERDDL << " file " << p << "/Mod" << m << "RCU" << i << ".data is wrong: no numberOfChannels"; + return kNotInitialized; + } + if (!(*fIn >> maxHWAddress)) { + LOG(FATAL) << "Syntax of mapping file " << p << "/Mod" << m << "RCU" << i << ".data is wrong: no maxHWAddress"; + return kNotInitialized; + } + if (maxHWAddress > NMaxHWAddress) { + LOG(FATAL) << "Maximal HW address in file " << maxHWAddress << "larger than array size " << NMaxHWAddress << "for /Mod" << m << "RCU" << i << ".data is wrong: no maxHWAddress"; + return kNotInitialized; + } + + for (short ich = 0; ich < numberOfChannels; ich++) { // 1792 = 2*896 channels connected to each RCU + int hwAddress; + if (!(*fIn >> hwAddress)) { + LOG(FATAL) << "Syntax of mapping file " << p << "/Mod" << m << "RCU" << i << ".data is wrong: no HWadd for ch " << ich; + return kNotInitialized; + } + if (hwAddress > maxHWAddress) { + LOG(FATAL) << "Hardware (ALTRO) adress (" << hwAddress << ") outside the range (0 -> " << maxHWAddress << ") !"; + return kNotInitialized; + } + int row, col, caloFlag; + if (!(*fIn >> row >> col >> caloFlag)) { + LOG(FATAL) << "Syntax of mapping file " << p << "/Mod" << m << "RCU" << i << ".data is wrong: no (raw col caloFlag)"; + return kNotInitialized; + } + + if (caloFlag < 0 || caloFlag > 2) { + LOG(FATAL) << "Wrong CaloFlag value found (" << caloFlag << "). Should be 0, 1, 2 !"; + return kNotInitialized; + } + + if (caloFlag == 2) { //TODO!!!! TRU mapping not known yet + continue; + } + + //convert ddl, col,raw caloFlag to AbsId + // Converts the absolute numbering into the following array + // relid[0] = PHOS Module number + // relid[1] = Row number inside a PHOS module (Phi coordinate) + // relid[2] = Column number inside a PHOS module (Z coordinate) + short ddl = 4 * m + i - 2; + + char relid[3] = {(char)m, (char)row, (char)col}; + short absId; + geom->relToAbsNumbering(relid, absId); + + if (ddl < 0 || ddl >= NDDL) { + LOG(FATAL) << "Wrong ddl address found (" << ddl << "). Module= " << m << " RCU =" << i; + return kNotInitialized; + } + + mAbsId[ddl][hwAddress] = absId; + mCaloFlag[ddl][hwAddress] = (CaloFlag)caloFlag; + + mAbsToHW[absId][caloFlag][0] = ddl; + mAbsToHW[absId][caloFlag][1] = hwAddress; + } + fIn->close(); + } //RCU + } // module + mInitialized = true; + return kOK; +} diff --git a/Detectors/PHOS/base/src/RCUTrailer.cxx b/Detectors/PHOS/base/src/RCUTrailer.cxx new file mode 100644 index 0000000000000..6295023ecbf53 --- /dev/null +++ b/Detectors/PHOS/base/src/RCUTrailer.cxx @@ -0,0 +1,246 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <cfloat> +#include <cmath> +#include <iostream> +#include <fmt/format.h> +#include "FairLogger.h" +#include "CommonConstants/LHCConstants.h" +#include "PHOSBase/RCUTrailer.h" + +using namespace o2::phos; + +RCUTrailer::RCUTrailer(const gsl::span<const uint32_t> payloadwords) +{ + constructFromRawPayload(payloadwords); +} + +void RCUTrailer::reset() +{ + mRCUId = -1; + mFirmwareVersion = 0; + mTrailerSize = 0; + mPayloadSize = 0; + mFECERRA = 0; + mFECERRB = 0; + mERRREG2 = 0; + mERRREG3 = 0; + mActiveFECsA = 0; + mActiveFECsB = 0; + mAltroCFG1 = 0; + mAltroCFG2 = 0; + mIsInitialized = false; +} + +void RCUTrailer::constructFromRawPayload(const gsl::span<const uint32_t> payloadwords) +{ + reset(); + int index = payloadwords.size(); + auto word = payloadwords[--index]; + if ((word >> 30) != 3) { + throw Error(Error::ErrorType_t::DECODING_INVALID, "Last RCU trailer word not found!"); + } + mFirmwareVersion = (word >> 16) & 0xFF; + + mRCUId = (int)((word >> 7) & 0x1FF); + int trailerSize = (word & 0x7F); + + if (trailerSize < 2) { + throw Error(Error::ErrorType_t::SIZE_INVALID, fmt::format("Invalid trailer size found (%d bytes) !", trailerSize * 4).data()); + } + mTrailerSize = trailerSize; + + trailerSize -= 2; // Cut first and last trailer words as they are handled separately + for (; trailerSize > 0; trailerSize--) { + word = payloadwords[--index]; + if ((word >> 30) != 2) { + LOG(ERROR) << "Missing RCU trailer identifier pattern!"; + continue; + } + int parCode = (word >> 26) & 0xF; + int parData = word & 0x3FFFFFF; + switch (parCode) { + case 1: + // ERR_REG1 + mFECERRA = ((parData >> 13) & 0x1FFF) << 7; + mFECERRB = ((parData & 0x1FFF)) << 7; + break; + case 2: + // ERR_REG2 + mERRREG2 = parData & 0x1FF; + break; + case 3: + // ERR_REG3 + mERRREG3 = parData & 0x1FFFFFF; + break; + case 4: + // FEC_RO_A + mActiveFECsA = parData & 0xFFFF; + break; + case 5: + // FEC_RO_B + mActiveFECsB = parData & 0xFFFF; + break; + case 6: + // RDO_CFG1 + mAltroCFG1 = parData & 0xFFFFF; + break; + case 7: + // RDO_CFG2 + mAltroCFG2 = parData & 0x1FFFFFF; + break; + default: + LOG(ERROR) << "Undefined parameter code " << parCode << ", ignore it !"; + break; + } + } + mPayloadSize = payloadwords[--index] & 0x3FFFFFF; + mIsInitialized = true; +} + +double RCUTrailer::getTimeSample() const +{ + unsigned char fq = (mAltroCFG2 >> 5) & 0xF; + double tSample; + switch (fq) { + case 0: + // 20 MHz + tSample = 2.0; + break; + case 1: + // 10 Mhz + tSample = 4.0; + break; + case 2: + // 5 MHz + tSample = 8.; + break; + default: + throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format("Invalid sampling frequency value %d !", int(fq)).data()); + } + + return tSample * o2::constants::lhc::LHCBunchSpacingNS * 1.e-9; +} + +void RCUTrailer::setTimeSample(double timesample) +{ + int fq = 0; + if (std::abs(timesample - 50) < DBL_EPSILON) { + fq = 0; + } else if (std::abs(timesample - 100) < DBL_EPSILON) { + fq = 1; + } else if (std::abs(timesample - 200) < DBL_EPSILON) { + fq = 2; + } else { + throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format("invalid time sample: %f", timesample).data()); + } + mAltroCFG2 = (mAltroCFG2 & 0x1F) | fq << 5; +} + +double RCUTrailer::getL1Phase() const +{ + double tSample = getTimeSample(), + phase = ((double)(mAltroCFG2 & 0x1F)) * o2::constants::lhc::LHCBunchSpacingNS * 1.e-9; + if (phase >= tSample) { + throw Error(Error::ErrorType_t::L1PHASE_INVALID, fmt::format("Invalid L1 trigger phase (%e s (phase) >= %e s (sampling time)) !", phase, tSample).data()); + } + return phase; +} + +void RCUTrailer::setL1Phase(double l1phase) +{ + int phase = l1phase / 25.; + mAltroCFG2 = (mAltroCFG2 & 0x1E0) | phase; +} + +std::vector<uint32_t> RCUTrailer::encode() const +{ + std::vector<uint32_t> encoded; + encoded.emplace_back(mPayloadSize | 2 << 30); + encoded.emplace_back(mAltroCFG2 | 7 << 26 | 2 << 30); + encoded.emplace_back(mAltroCFG1 | 6 << 26 | 2 << 30); + encoded.emplace_back(mActiveFECsB | 5 << 26 | 2 << 30); + encoded.emplace_back(mActiveFECsA | 4 << 26 | 2 << 30); + encoded.emplace_back(mERRREG3 | 3 << 26 | 2 << 30); + encoded.emplace_back(mERRREG2 | 2 << 26 | 2 << 30); + encoded.emplace_back(mFECERRB >> 7 | (mFECERRA >> 7) << 13 | 1 << 26 | 2 << 30); + + uint32_t lasttrailerword = 3 << 30 | mFirmwareVersion << 16 | mRCUId << 7 | (encoded.size() + 1); + encoded.emplace_back(lasttrailerword); + + return encoded; +} + +void RCUTrailer::printStream(std::ostream& stream) const +{ + std::vector<std::string> errors; + double timesample = -1., l1phase = -1.; + try { + timesample = getTimeSample(); + } catch (Error& e) { + errors.push_back(e.what()); + } + try { + l1phase = getL1Phase(); + } catch (Error& e) { + errors.push_back(e.what()); + } + + stream << "RCU trailer (Format version 2):\n" + << "==================================================\n" + << "RCU ID: " << mRCUId << "\n" + << "Firmware version: " << int(mFirmwareVersion) << "\n" + << "Trailer size: " << mTrailerSize << "\n" + << "Payload size: " << mPayloadSize << "\n" + << "FECERRA: 0x" << std::hex << mFECERRA << "\n" + << "FECERRB: 0x" << std::hex << mFECERRB << "\n" + << "ERRREG2: 0x" << std::hex << mERRREG2 << "\n" + << "#channels skipped due to address mismatch: " << std::dec << getNumberOfChannelAddressMismatch() << "\n" + << "#channels skipped due to bad block length: " << std::dec << getNumberOfChannelLengthMismatch() << "\n" + << "Active FECs (branch A): 0x" << std::hex << mActiveFECsA << "\n" + << "Active FECs (branch B): 0x" << std::hex << mActiveFECsB << "\n" + << "Baseline corr: 0x" << std::hex << int(getBaselineCorrection()) << "\n" + << "Number of presamples: " << std::dec << int(getNumberOfPresamples()) << "\n" + << "Number of postsamples: " << std::dec << int(getNumberOfPostsamples()) << "\n" + << "Second baseline corr: " << (hasSecondBaselineCorr() ? "yes" : "no") << "\n" + << "GlitchFilter: " << std::dec << int(getGlitchFilter()) << "\n" + << "Number of non-ZS postsamples: " << std::dec << int(getNumberOfNonZeroSuppressedPostsamples()) << "\n" + << "Number of non-ZS presamples: " << std::dec << int(getNumberOfNonZeroSuppressedPresamples()) << "\n" + << "Number of ALTRO buffers: " << std::dec << getNumberOfAltroBuffers() << "\n" + << "Number of pretrigger samples: " << std::dec << int(getNumberOfPretriggerSamples()) << "\n" + << "Number of samples per channel: " << std::dec << getNumberOfSamplesPerChannel() << "\n" + << "Sparse readout: " << (isSparseReadout() ? "yes" : "no") << "\n" + << "AltroCFG1: 0x" << std::hex << mAltroCFG1 << "\n" + << "AltroCFG2: 0x" << std::hex << mAltroCFG2 << "\n" + << "Sampling time: " << std::scientific << timesample << " s\n" + << "L1 Phase: " << std::scientific << l1phase << " s\n" + << std::dec << std::fixed; + if (errors.size()) { + stream << "Errors: \n" + << "-------------------------------------------------\n"; + for (const auto& e : errors) { + stream << e << "\n"; + } + } + stream << "==================================================\n"; +} + +RCUTrailer RCUTrailer::constructFromPayloadWords(const gsl::span<const uint32_t> payloadwords) +{ + RCUTrailer result; + result.constructFromRawPayload(payloadwords); + return result; +} + +std::ostream& o2::phos::operator<<(std::ostream& stream, const o2::phos::RCUTrailer& trailer) +{ + trailer.printStream(stream); + return stream; +} diff --git a/Detectors/PHOS/calib/include/PHOSCalib/CalibDB.h b/Detectors/PHOS/calib/include/PHOSCalib/CalibDB.h index 3e100b018e7ec..cd1c2e5c6e834 100644 --- a/Detectors/PHOS/calib/include/PHOSCalib/CalibDB.h +++ b/Detectors/PHOS/calib/include/PHOSCalib/CalibDB.h @@ -58,7 +58,7 @@ class CalibDB /// - Incorrect path /// - Wrong timestamp /// - Meta data not set - class ObjectNotFoundException : public std::exception + class ObjectNotFoundException final : public std::exception { public: /// \brief Constructor with query parameters @@ -116,7 +116,7 @@ class CalibDB /// a certain path and with a certain timestamp was valid, the object /// however has a different type than the expected one (something was /// screwed up when writing to the CCDB) - class TypeMismatchException : public std::exception + class TypeMismatchException final : public std::exception { public: /// \brief Constructor diff --git a/Detectors/PHOS/calib/src/BadChannelMap.cxx b/Detectors/PHOS/calib/src/BadChannelMap.cxx index 9e2cc5cdbe021..4e920ef009671 100644 --- a/Detectors/PHOS/calib/src/BadChannelMap.cxx +++ b/Detectors/PHOS/calib/src/BadChannelMap.cxx @@ -84,8 +84,9 @@ void BadChannelMap::PrintStream(std::ostream& stream) const // first sort bad channel IDs stream << "Number of bad cells: " << mBadCells.count() << "\n"; for (int cellID = 0; cellID < mBadCells.size(); cellID++) { - if (mBadCells.test(cellID)) + if (mBadCells.test(cellID)) { stream << cellID << "\n"; + } } } diff --git a/Detectors/PHOS/calib/src/CalibDB.cxx b/Detectors/PHOS/calib/src/CalibDB.cxx index 5c5b0e37655cf..08aa853a24997 100644 --- a/Detectors/PHOS/calib/src/CalibDB.cxx +++ b/Detectors/PHOS/calib/src/CalibDB.cxx @@ -26,42 +26,52 @@ void CalibDB::init() void CalibDB::storeBadChannelMap(BadChannelMap* bcm, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFile(new o2::TObjectWrapper<o2::phos::BadChannelMap>(bcm), "BadChannelMap/PHS", metadata, rangestart, rangeend); } BadChannelMap* CalibDB::readBadChannelMap(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } auto result = mCCDBManager.retrieveFromTFile("BadChannelMap/PHS", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "BadChannelMap/PHS", metadata, timestamp); - if (result->IsA() != TObjectWrapper<o2::phos::BadChannelMap>::Class()) + } + if (result->IsA() != TObjectWrapper<o2::phos::BadChannelMap>::Class()) { throw TypeMismatchException("TObjectWrapper<o2::phos::BadChannelMap>", result->IsA()->GetName()); + } auto wrap = dynamic_cast<TObjectWrapper<o2::phos::BadChannelMap>*>(result); - if (!wrap) + if (!wrap) { throw TypeMismatchException("TObjectWrapper<o2::phos::BadChannelMap>", result->IsA()->GetName()); // type checked before - should not enter here + } return wrap->getObj(); } void CalibDB::storeCalibParams(CalibParams* prms, const std::map<std::string, std::string>& metadata, ULong_t rangestart, ULong_t rangeend) { - if (!mInit) + if (!mInit) { init(); + } mCCDBManager.storeAsTFile(new o2::TObjectWrapper<o2::phos::CalibParams>(prms), "CalibParams/PHS", metadata, rangestart, rangeend); } CalibParams* CalibDB::readCalibParams(ULong_t timestamp, const std::map<std::string, std::string>& metadata) { - if (!mInit) + if (!mInit) { init(); + } auto result = mCCDBManager.retrieveFromTFile("CalibParams/PHS", metadata, timestamp); - if (!result) + if (!result) { throw ObjectNotFoundException(mCCDBServer, "CalibParams/PHS", metadata, timestamp); - if (result->IsA() != TObjectWrapper<o2::phos::CalibParams>::Class()) + } + if (result->IsA() != TObjectWrapper<o2::phos::CalibParams>::Class()) { throw TypeMismatchException("TObjectWrapper<o2::phos::CalibParams>", result->IsA()->GetName()); + } auto wrap = dynamic_cast<TObjectWrapper<o2::phos::CalibParams>*>(result); - if (!wrap) + if (!wrap) { throw TypeMismatchException("TObjectWrapper<o2::phos::CalibParams>", result->IsA()->GetName()); // type checked before - should not enter here + } return wrap->getObj(); } diff --git a/Detectors/PHOS/reconstruction/CMakeLists.txt b/Detectors/PHOS/reconstruction/CMakeLists.txt index bf49c5acffe09..c46283b9d756a 100644 --- a/Detectors/PHOS/reconstruction/CMakeLists.txt +++ b/Detectors/PHOS/reconstruction/CMakeLists.txt @@ -11,11 +11,39 @@ o2_add_library(PHOSReconstruction SOURCES src/Clusterer.cxx src/FullCluster.cxx + src/RawReaderMemory.cxx + src/RawBuffer.cxx + src/RawHeaderStream.cxx + src/RawPayload.cxx + src/AltroDecoder.cxx + src/Bunch.cxx + src/Channel.cxx + src/CaloRawFitter.cxx + src/CTFCoder.cxx + src/CTFHelper.cxx PUBLIC_LINK_LIBRARIES O2::PHOSBase O2::PHOSCalib O2::DataFormatsPHOS - AliceO2::InfoLogger) + O2::DetectorsRaw + AliceO2::InfoLogger + O2::rANS + ms_gsl::ms_gsl) o2_target_root_dictionary(PHOSReconstruction - HEADERS include/PHOSReconstruction/Clusterer.h + HEADERS include/PHOSReconstruction/RawReaderMemory.h + include/PHOSReconstruction/RawBuffer.h + include/PHOSReconstruction/RawHeaderStream.h + include/PHOSReconstruction/RawPayload.h + include/PHOSReconstruction/RawReaderError.h + include/PHOSReconstruction/Bunch.h + include/PHOSReconstruction/RawDecodingError.h + include/PHOSReconstruction/AltroDecoder.h + include/PHOSReconstruction/Channel.h + include/PHOSReconstruction/CaloRawFitter.h + include/PHOSReconstruction/Clusterer.h include/PHOSReconstruction/FullCluster.h) +o2_add_executable(rawreader-file + COMPONENT_NAME phos + PUBLIC_LINK_LIBRARIES O2::PHOSReconstruction + SOURCES run/rawReaderFile.cxx) + diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/AltroDecoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/AltroDecoder.h new file mode 100644 index 0000000000000..42e4f7a2d6cc1 --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/AltroDecoder.h @@ -0,0 +1,137 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_ALTRODECODER_H +#define ALICEO2_PHOS_ALTRODECODER_H + +#include <iosfwd> +#include <gsl/span> +#include <string> +#include "PHOSBase/RCUTrailer.h" +#include "PHOSReconstruction/Bunch.h" +#include "PHOSReconstruction/Channel.h" +#include "PHOSReconstruction/RawReaderMemory.h" + +namespace o2 +{ +namespace phos +{ + +/// \class AltroDecoderError +/// \brief Error handling of the ALTRO Decoder +/// \ingroup EMCALreconstruction +class AltroDecoderError : public std::exception +{ + public: + enum ErrorType_t { + kOK, ///< NoError + RCU_TRAILER_ERROR, ///< RCU trailer cannot be decoded or invalid + RCU_VERSION_ERROR, ///< RCU trailer version not matching with the version in the raw header + RCU_TRAILER_SIZE_ERROR, ///< RCU trailer size length + ALTRO_BUNCH_HEADER_ERROR, ///< ALTRO bunch header cannot be decoded or is invalid + ALTRO_BUNCH_LENGTH_ERROR, ///< ALTRO bunch has incorrect length + ALTRO_PAYLOAD_ERROR, ///< ALTRO payload cannot be decoded + ALTRO_MAPPING_ERROR, ///< Incorrect ALTRO channel mapping + CHANNEL_ERROR ///< Channels not initialized + }; + + /// \brief Constructor + /// + /// Defining error code and error message. To be called when the + /// exception is thrown + AltroDecoderError(ErrorType_t errtype, const char* message) : mErrorType(errtype), mErrorMessage(message) {} + + /// \brief Destructor + ~AltroDecoderError() noexcept override = default; + + /// \brief Access to error message cnnected to the error + /// \return Error message + const char* what() const noexcept override { return mErrorMessage.data(); } + + /// \brief Access to the error type connected to the erro + /// \return Error type + const ErrorType_t getErrorType() const noexcept { return mErrorType; } + + private: + ErrorType_t mErrorType; ///< Code of the decoding error type + std::string mErrorMessage; ///< Message connected to the error type +}; + +/// \class AltroDecoder +/// \brief Decoder of the ALTRO data in the raw page +/// \ingroup PHOSreconstruction +/// \author Dmitri Peresunko aftesr Markus Fasel +/// \since Sept, 2020 +/// +/// This is a base class for reading raw data digits in Altro format. +/// The class is able to read the RCU v3 and above formats. +/// The main difference between the format V3 and older ones is in +/// the coding of the 10-bit Altro payload words. In V3 3 10-bit words +/// are coded in one 32-bit word. The bits 30 and 31 are used to identify +/// the payload, altro header and RCU trailer contents. +/// +/// Based on AliAltroRawStreamV3 and AliCaloRawStreamV3 by C. Cheshkov + +class AltroDecoder +{ + public: + /// \brief Constructor + /// \param reader Raw reader instance to be decoded + AltroDecoder(RawReaderMemory& reader); + + /// \brief Destructor + ~AltroDecoder() = default; + + /// \brief Decode the ALTRO stream + /// \throw AltroDecoderError if the RCUTrailer or ALTRO payload cannot be decoded + /// + /// Decoding and checking the RCUTtrailer and + /// all channels and bunches in the ALTRO stream. + /// After successfull decoding the Decoder can provide + /// a reference to the RCU trailer and a vector + /// with the decoded chanenels, each containing + /// its bunches. + AltroDecoderError::ErrorType_t decode(); + + /// \brief Get reference to the RCU trailer object + /// \return reference to the RCU trailers vector + const RCUTrailer& getRCUTrailer() const { return mRCUTrailer; } + + /// \brief Get the reference to the channel container + /// \return Reference to the channel container + /// \throw AltroDecoderError with CHANNEL_ERROR if the channel container was not initialized for the current event + const std::vector<Channel>& getChannels() const; + + /// \brief Read RCU trailer for the current event in the raw buffer + void readRCUTrailer(); + + /// \brief Read channels for the current event in the raw buffer + void readChannels(); + + private: + /// \brief run checks on the RCU trailer + /// \throw Error if the RCU trailer has inconsistencies + /// + /// Performing various consistency checks on the RCU trailer + /// In case of failure an exception is thrown. + void checkRCUTrailer(); + + RawReaderMemory& mRawReader; ///< underlying raw reader + RCUTrailer mRCUTrailer; ///< RCU trailer + std::vector<Channel> mChannels; ///< vector of channels in the raw stream + bool mChannelsInitialized = false; ///< check whether the channels are initialized + + ClassDefNV(AltroDecoder, 1); +}; + +} // namespace phos + +} // namespace o2 + +#endif diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/Bunch.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/Bunch.h new file mode 100644 index 0000000000000..afda162577293 --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/Bunch.h @@ -0,0 +1,103 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_BUNCH_H +#define ALICEO2_PHOS_BUNCH_H + +#include <cstdint> +#include <vector> +#include <gsl/span> +#include "Rtypes.h" + +namespace o2 +{ + +namespace phos +{ +/// \class Bunch +/// \brief ALTRO bunch information +/// \ingroup PHOSreconstruction +/// +/// The bunch contains the ADC values of a given +/// data bunch for a channel in the ALTRO stream. +/// The ADC values are stored in reversed order in +/// time both in the ALTRO stream and in the bunch +/// object. +/// +/// For iteration one should assume that the end time +/// is 0, however it can also be larger than 0. In this +/// case the first value has to be mapped to the end timebin +/// and the last value to the start timebin, still iterating +/// only over the number of samples. +class Bunch +{ + public: + /// \brief Constructor + Bunch() = default; + + /// \brief Initialize the bunch with start time and bunch length + /// \param length Length of the bunch + /// \param start Start time of the bunch + Bunch(uint8_t length, uint8_t start) : mBunchLength(length), mStartTime(start), mADC() {} + + /// \brief + ~Bunch() = default; + + /// \brief Add ADC value to the bunch + /// \param adc Next ADC value + /// + /// ADC values are stored in reversed order. The next ADC value + /// has to be earlier in time compared to the previous one. + void addADC(uint16_t adc) { mADC.emplace_back(adc); } + + /// \brief Initialize the ADC values in the bunch from a range + /// \param range Range of ADC values + /// + /// The ADC values are stored in reversed order in time. Therefore + /// the last entry is the one earliest in time. + void initFromRange(gsl::span<uint16_t> range); + + /// \brief Get range of ADC values in the bunch + /// \return ADC values in the bunch + /// + /// The ADC values are stored in reversed order in time. Therefore + /// the last entry is the one earliest in time. + const std::vector<uint16_t>& getADC() const { return mADC; } + + /// \brief Get the length of the bunch (number of time bins) + /// \return Length of the bunch + uint8_t getBunchLength() const { return mBunchLength; } + + /// \brief Get the start time bin + /// \return Start timebin + /// + /// The start timebin is the higher of the two, + /// the samples are in reversed order. + uint8_t getStartTime() const { return mStartTime; } + + /// \brief Get the end time bin + /// \return End timebin + /// + /// The end timebin is the lower of the two, + /// the samples are in reversed order. + uint8_t getEndTime() const { return mStartTime - mBunchLength + 1; } + + private: + uint8_t mBunchLength = 0; ///< Number of ADC samples in buffer + uint8_t mStartTime = 0; ///< Start timebin (larger time bin, samples are in reversed order) + std::vector<uint16_t> mADC; ///< ADC samples in bunch + + ClassDefNV(Bunch, 1); +}; + +} // namespace phos + +} // namespace o2 + +#endif diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h new file mode 100644 index 0000000000000..27969fd577b8e --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of PHOS data + +#ifndef O2_PHOS_CTFCODER_H +#define O2_PHOS_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include <array> +#include "DataFormatsPHOS/CTF.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "rANS/rans.h" +#include "PHOSReconstruction/CTFHelper.h" + +class TTree; + +namespace o2 +{ +namespace phos +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::PHS) {} + ~CTFCoder() = default; + + /// entropy-encode data to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const TriggerRecord>& trigData, const gsl::span<const Cell>& cellData); + + /// entropy decode data from buffer with CTF + template <typename VTRG, typename VCELL> + void decode(const CTF::base& ec, VTRG& trigVec, VCELL& cellVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<TriggerRecord>& trigVec, std::vector<Cell>& cellVec); +}; + +/// entropy-encode clusters to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const TriggerRecord>& trigData, const gsl::span<const Cell>& cellData) +{ + 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_bcIncTrig + MD::EENCODE, // BLC_orbitIncTrig + MD::EENCODE, // BLC_entriesTrig + MD::EENCODE, // BLC_packedID + MD::EENCODE, // BLC_time + MD::EENCODE, // BLC_energy + MD::EENCODE // BLC_status + }; + + CTFHelper helper(trigData, cellData); + + // book output size with some margin + auto szIni = sizeof(CTFHeader) + helper.getSize() / 4; // will be autoexpanded if needed + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(helper.createHeader()); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODEPHS(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODEPHS(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); + ENCODEPHS(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); + ENCODEPHS(helper.begin_entriesTrig(), helper.end_entriesTrig(), CTF::BLC_entriesTrig, 0); + + ENCODEPHS(helper.begin_packedID(), helper.end_packedID(), CTF::BLC_packedID, 0); + ENCODEPHS(helper.begin_time(), helper.end_time(), CTF::BLC_time, 0); + ENCODEPHS(helper.begin_energy(), helper.end_energy(), CTF::BLC_energy, 0); + ENCODEPHS(helper.begin_status(), helper.end_status(), CTF::BLC_status, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +/// decode entropy-encoded clusters to standard compact clusters +template <typename VTRG, typename VCELL> +void CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCELL& cellVec) +{ + auto header = ec.getHeader(); + ec.print(getPrefix()); + std::vector<uint16_t> bcInc, entries, energy, cellTime, packedID; + std::vector<uint32_t> orbitInc; + std::vector<uint8_t> status; + +#define DECODEPHOS(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODEPHOS(bcInc, CTF::BLC_bcIncTrig); + DECODEPHOS(orbitInc, CTF::BLC_orbitIncTrig); + DECODEPHOS(entries, CTF::BLC_entriesTrig); + DECODEPHOS(packedID, CTF::BLC_packedID); + + DECODEPHOS(cellTime, CTF::BLC_time); + DECODEPHOS(energy, CTF::BLC_energy); + DECODEPHOS(status, CTF::BLC_status); + // clang-format on + // + trigVec.clear(); + cellVec.clear(); + trigVec.reserve(header.nTriggers); + status.reserve(header.nCells); + + uint32_t firstEntry = 0, cellCount = 0; + o2::InteractionRecord ir(header.firstBC, header.firstOrbit); + + Cell cell; + for (uint32_t itrig = 0; itrig < header.nTriggers; itrig++) { + // restore TrigRecord + if (orbitInc[itrig]) { // non-0 increment => new orbit + ir.bc = bcInc[itrig]; // bcInc has absolute meaning + ir.orbit += orbitInc[itrig]; + } else { + ir.bc += bcInc[itrig]; + } + + firstEntry = cellVec.size(); + for (uint16_t ic = 0; ic < entries[itrig]; ic++) { + cell.setPacked(packedID[cellCount], cellTime[cellCount], energy[cellCount], status[cellCount]); + cellVec.emplace_back(cell); + cellCount++; + } + trigVec.emplace_back(ir, firstEntry, entries[itrig]); + } + assert(cellCount == header.nCells); +} + +} // namespace phos +} // namespace o2 + +#endif // O2_PHOS_CTFCODER_H diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFHelper.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFHelper.h new file mode 100644 index 0000000000000..9a127b4f00b8f --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFHelper.h @@ -0,0 +1,193 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.h +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for PHOS CTF creation + +#ifndef O2_PHOS_CTF_HELPER_H +#define O2_PHOS_CTF_HELPER_H + +#include "DataFormatsPHOS/CTF.h" +#include <gsl/span> + +namespace o2 +{ +namespace phos +{ + +class CTFHelper +{ + + public: + CTFHelper(const gsl::span<const TriggerRecord>& trgData, const gsl::span<const Cell>& cellData) + : mTrigData(trgData), mCellData(cellData) {} + + CTFHeader createHeader() + { + CTFHeader h{uint32_t(mTrigData.size()), uint32_t(mCellData.size()), 0, 0}; + if (mTrigData.size()) { + h.firstOrbit = mTrigData[0].getBCData().orbit; + h.firstBC = mTrigData[0].getBCData().bc; + } + return h; + } + + size_t getSize() const { return mTrigData.size() * sizeof(TriggerRecord) + mCellData.size() * sizeof(Cell); } + + //>>> =========================== ITERATORS ======================================== + + template <typename I, typename D, typename T> + class _Iter + { + public: + using difference_type = int64_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; + using iterator_category = std::random_access_iterator_tag; + + _Iter(const gsl::span<const D>& data, bool end = false) : mData(data), mIndex(end ? data.size() : 0){}; + _Iter() = default; + + const I& operator++() + { + ++mIndex; + return (I&)(*this); + } + + const I& operator--() + { + mIndex--; + return (I&)(*this); + } + + 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); + } + + 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; } + + protected: + gsl::span<const D> mData{}; + size_t 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<Iter_bcIncTrig, TriggerRecord, uint16_t> + { + public: + using _Iter<Iter_bcIncTrig, TriggerRecord, uint16_t>::_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; + } else { + return mData[mIndex].getBCData().bc; + } + } + return 0; + } + }; + + //_______________________________________________ + // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) + class Iter_orbitIncTrig : public _Iter<Iter_orbitIncTrig, TriggerRecord, uint32_t> + { + public: + using _Iter<Iter_orbitIncTrig, TriggerRecord, uint32_t>::_Iter; + value_type operator*() const { return mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0; } + }; + + //_______________________________________________ + // Number of cells for trigger + class Iter_entriesTrig : public _Iter<Iter_entriesTrig, TriggerRecord, uint16_t> + { + public: + using _Iter<Iter_entriesTrig, TriggerRecord, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getNumberOfObjects(); } + }; + + //_______________________________________________ + class Iter_packedID : public _Iter<Iter_packedID, Cell, uint16_t> + { + public: + using _Iter<Iter_packedID, Cell, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedID(); } + }; + + //_______________________________________________ + class Iter_time : public _Iter<Iter_time, Cell, uint16_t> + { + public: + using _Iter<Iter_time, Cell, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedTime(); } + }; + + //_______________________________________________ + class Iter_energy : public _Iter<Iter_energy, Cell, uint16_t> + { + public: + using _Iter<Iter_energy, Cell, uint16_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedEnergy(); } + }; + + //_______________________________________________ + class Iter_status : public _Iter<Iter_status, Cell, uint8_t> + { + public: + using _Iter<Iter_status, Cell, uint8_t>::_Iter; + value_type operator*() const { return mData[mIndex].getPackedCellStatus(); } + }; + + //<<< =========================== ITERATORS ======================================== + + Iter_bcIncTrig begin_bcIncTrig() const { return Iter_bcIncTrig(mTrigData, false); } + Iter_bcIncTrig end_bcIncTrig() const { return Iter_bcIncTrig(mTrigData, true); } + + Iter_orbitIncTrig begin_orbitIncTrig() const { return Iter_orbitIncTrig(mTrigData, false); } + Iter_orbitIncTrig end_orbitIncTrig() const { return Iter_orbitIncTrig(mTrigData, true); } + + Iter_entriesTrig begin_entriesTrig() const { return Iter_entriesTrig(mTrigData, false); } + Iter_entriesTrig end_entriesTrig() const { return Iter_entriesTrig(mTrigData, true); } + + Iter_packedID begin_packedID() const { return Iter_packedID(mCellData, false); } + Iter_packedID end_packedID() const { return Iter_packedID(mCellData, true); } + + Iter_time begin_time() const { return Iter_time(mCellData, false); } + Iter_time end_time() const { return Iter_time(mCellData, true); } + + Iter_energy begin_energy() const { return Iter_energy(mCellData, false); } + Iter_energy end_energy() const { return Iter_energy(mCellData, true); } + + Iter_status begin_status() const { return Iter_status(mCellData, false); } + Iter_status end_status() const { return Iter_status(mCellData, true); } + + private: + const gsl::span<const o2::phos::TriggerRecord> mTrigData; + const gsl::span<const o2::phos::Cell> mCellData; +}; + +} // namespace phos +} // namespace o2 + +#endif diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CaloRawFitter.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CaloRawFitter.h new file mode 100644 index 0000000000000..4e2f9171c0e9d --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CaloRawFitter.h @@ -0,0 +1,113 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CaloRawFitter +/// \brief Raw data fitting: extraction amplitude and time +/// +/// Extraction of amplitude and time +/// from CALO raw data using fast k-level approach or +/// least square fit with Gamma2 function +/// +/// \author Dmitri Peresunko +/// \since Jan.2020 +/// + +#ifndef PHOSRAWFITTER_H_ +#define PHOSRAWFITTER_H_ +class Bunch; + +namespace o2 +{ + +namespace phos +{ + +class CaloRawFitter +{ + + public: + enum FitStatus { kOK, + kNotEvaluated, + kEmptyBunch, + kOverflow, + kSpike, + kNoTime, + kFitFailed, + kBadPedestal, + kManyBunches }; + + public: + /// \brief Constructor + CaloRawFitter() = default; + + /// \brief Destructor + ~CaloRawFitter() = default; + + /// \brief Evaluation Amplitude and TOF + /// return status -1: not evaluated/empty bunch; + /// 0: OK; + /// 1: overflow; + /// 4: single spikes + /// 3: too large RMS; + FitStatus evaluate(const std::vector<Bunch>& bunchvector); + + /// \brief Set HighGain/LowGain channel to performe or not fit of saturated samples + void setLowGain(bool isLow = false) { mLowGain = isLow; } + + /// \brief estimate and subtract pedestals from pre-samples + void setPedSubtract(bool toSubtruct = false) { mPedSubtract = toSubtruct; } + + /// \brief amplitude in last fitted sample + float getAmp(int is) { return mAmp[is]; } + + /// \brief Chi2/NDF of last performed fit + float getChi2(int is) const { return mChi2[is]; } + + /// \brief time in last fitted sample + float getTime(int is) { return mTime[is]; } + + /// \brief is last fitted sample has overflow + bool isOverflow(int is) { return mOverflow[is]; } + + /// \brief Forse perform fitting + /// Make fit for any sample, not only saturated LowGain samples as by default + void forseFitting(bool toRunFit = true) { makeFit = toRunFit; } + + /// \brief Number of fit samples (normaly 1, more in case of pileup, noise etc) + short getNsamples() { return mAmp.size(); } + + /// \brief Set analysis of pedestal run + /// Analyze pedestal run, i.e. calculate mean and RMS of pedestals instead of Amp and Time + void setPedestal() { mPedestalRun = true; } + + protected: + FitStatus evalKLevel(const Bunch& b); + + FitStatus fitGamma2(const Bunch& b); + + private: + bool makeFit = false; ///< run (slow) fit with Gamma2 or use fast evaluation with k-level + bool mLowGain = false; ///< is current bunch from LowGain channel + bool mPedSubtract = false; ///< should one evaluate and subtract pedestals + bool mPedestalRun = false; ///< analyze as pedestal run + std::vector<bool> mOverflow; ///< is last sample saturated + FitStatus mStatus = kNotEvaluated; ///< status of last evaluated sample: -1: not yet evaluated; 0: OK; 1: overflow; 2: too large RMS; 3: single spikes + short mMaxSample = 0; ///< maximal sample + std::vector<float> mAmp; ///< amplitude of last processed sample + std::vector<float> mTime; ///< time of last processed sample + std::vector<float> mChi2; ///< chi2 calculated in last fit + + ClassDefNV(CaloRawFitter, 1); +}; // End of CaloRawFitter + +} // namespace phos + +} // namespace o2 +#endif diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/Channel.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/Channel.h new file mode 100644 index 0000000000000..4ce48c910cf0a --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/Channel.h @@ -0,0 +1,160 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_CHANNEL_H +#define ALICEO2_PHOS_CHANNEL_H + +#include <cstdint> +#include <exception> +#include <vector> +#include "Rtypes.h" +#include "PHOSReconstruction/Bunch.h" + +namespace o2 +{ + +namespace phos +{ + +/// \class Channel +/// \brief ALTRO channel representation +/// \ingroup PHOSreconstruction +/// +/// The channel contains information about +/// a hardware channel in the raw stream. Those +/// information are: +/// - Hardware address +/// - Size of the payload of all bunches in the channel +/// as total number of 10-bit words +/// - Channel status (good or bad) +/// In addition it contains the data of all bunches in the +/// raw stream. +/// +/// The hardware address itself encods +/// - Branch ID (bit 12) +/// - FEC ID (bits 7-10) +/// - ALTRO ID (bits 4-6) +/// - Channel ID (bits 0-3) +class Channel +{ + public: + /// \class HardwareAddressError + /// \brief Handling of uninitialized hardware addresses + class HardwareAddressError : public std::exception + { + public: + /// \brief Constructor + HardwareAddressError() = default; + + /// \brief Destructor + ~HardwareAddressError() noexcept override = default; + + /// \brief Access to error message + /// \return error message + const char* what() const noexcept override + { + return "Hardware address not initialized"; + } + }; + + /// \brief Dummy constructor + Channel() = default; + + /// \brief Constructor initializing hardware address and payload size + /// \param hardwareAddress Harware address + /// \param payloadSize Size of the payload + Channel(int32_t hardwareAddress, uint8_t payloadSize) : mHardwareAddress(hardwareAddress), + mPayloadSize(payloadSize), + mBunches() + { + } + + /// \brief Destructor + ~Channel() = default; + + /// \brief Check whether the channel is bad + /// \return true if the channel is bad, false otherwise + bool isBadChannel() const { return mBadChannel; } + + /// \brief Get the full hardware address + /// \return Hardware address + /// + /// The hardware address contains: + /// - Branch ID (bit 12) + /// - FEC ID (bits 7-10) + /// - ALTRO ID (bits 4-6) + /// - Channel ID (bits 0-3) + uint16_t getHardwareAddress() const { return mHardwareAddress; } + + /// \brief Get the size of the payload + /// \return Size of the payload as number of 10-bit samples (1/3rd words) + uint8_t getPayloadSize() const { return mPayloadSize; } + + /// \brief Get list of bunches in the channel + /// \return List of bunches + const std::vector<Bunch>& getBunches() const { return mBunches; } + + /// \brief Provide the branch index for the current hardware address + /// \return RCU branch index (0 or 1) + /// \throw HadrwareAddressError in case the hardware address is not initialized + int getBranchIndex() const; + + /// \brief Provide the front-end card index for the current hardware address + /// \return Front-end card index for the current hardware address + /// \throw HadrwareAddressError in case the hardware address is not initialized + int getFECIndex() const; + + /// \brief Provide the altro chip index for the current hardware address + /// \return Altro chip index for the current hardware address + /// \throw HadrwareAddressError in case the hardware address is not initialized + int getAltroIndex() const; + + /// \brief Provide the channel index for the current hardware address + /// \return Channel index for the current hardware address + /// \throw HadrwareAddressError in case the hardware address is not initialized + int getChannelIndex() const; + + /// \brief Add bunch to the channel + /// \param bunch Bunch to be added + /// + /// This function will copy the bunch information to the + /// object, which might be expensive. Better use the + /// function createBunch. + void addBunch(const Bunch& bunch) { mBunches.emplace_back(bunch); } + + /// \brief Set the hardware address + /// \param hardwareAddress Hardware address + void setHardwareAddress(uint16_t hardwareAddress) { mHardwareAddress = hardwareAddress; } + + /// \brief Set the size of the payload in number of 10-bit words + /// \param payloadSize Size of the payload + void setPayloadSize(uint8_t payloadSize) { mPayloadSize = payloadSize; } + + /// \brief Mark the channel status + /// \param badchannel Bad channel status (true if bad) + void setBadChannel(bool badchannel) { mBadChannel = badchannel; } + + /// \brief Create and initialize a new bunch and return reference to it + /// \param bunchlength Length of the bunch + /// \param starttime Start time of the bunch + Bunch& createBunch(uint8_t bunchlength, uint8_t starttime); + + private: + int32_t mHardwareAddress = -1; ///< Hardware address + uint8_t mPayloadSize = 0; ///< Payload size + bool mBadChannel; ///< Bad channel status + std::vector<Bunch> mBunches; ///< Bunches in channel; + + ClassDefNV(Channel, 1); +}; + +} // namespace phos +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawBuffer.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawBuffer.h new file mode 100644 index 0000000000000..734632a1e3fee --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawBuffer.h @@ -0,0 +1,89 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RAWBUFFER_H +#define ALICEO2_PHOS_RAWBUFFER_H + +#include <array> +#include <cstdint> +#include <iosfwd> +#include <gsl/span> + +namespace o2 +{ +namespace phos +{ + +/// \class RawBuffer +/// \brief Buffer for PHOS raw pages +/// \ingroup PHOSreconstruction +/// \author Dmitri Peresunko after Markus Fasel +/// \since Sept, 2020 +class RawBuffer +{ + public: + RawBuffer() = default; + ~RawBuffer() = default; + + void reset() { mCurrentDataWord = 0; } + + /// \brief Flush the buffer + /// Does not overwrite the word buffer but just resets the counter and iterator + void flush(); + + /// \brief Read page from stream + /// \param in Input file stream + /// \param payloadsize Number of char words in payload + /// Read a whole superpage from the raw stream + /// and convert the bitwise representation directly + /// into 32 bit words + void readFromStream(std::istream& in, uint32_t payloadsize); + + /// \brief Read page from raw memory buffer + /// \param rawmemory Raw memory buffer (as char words) with size of the payload from the raw data header + /// Converts the char word raw memory buffer of a pages into + /// into the 32 bit word buffer + void readFromMemoryBuffer(const gsl::span<const char> rawmemory); + + /// \brief Get the number of data words read for the superpage + /// \return Number of data words in the superpage + int getNDataWords() const { return mNDataWords; } + + /// \brief Get the next data word in the superpage + /// \return next data word in the superpage + /// \throw std::runtime_error if there exists no next data word + uint32_t getNextDataWord(); + + /// \brief Get the data word at a given index + /// \param index index of the word in the buffer + /// \return word at requested index + /// \throw std::runtime_error if the index is out-of-range + uint32_t getWord(int index) const; + + /// \brief Get all data words from the raw buffer + /// \return Span with data words in the buffer (removing trailing null entries) + const gsl::span<const uint32_t> getDataWords() const { return gsl::span<const uint32_t>(mDataWords.data(), mNDataWords); } + + /// \brief Check whether the next data word exists + /// \return True if more data words exist, false otherwise + /// Check is done starting from the current position + /// of the iterator + bool hasNext() const { return mCurrentDataWord < mNDataWords; } + + private: + std::array<uint32_t, 2048> mDataWords; ///< Data words in one superpage + int mNDataWords = 0; ///< Number of data words read from superpage + int mCurrentDataWord = 0; ///< Iterator over words in superpage +}; + +} // namespace phos + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawDecodingError.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawDecodingError.h new file mode 100644 index 0000000000000..11e53aa22faff --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawDecodingError.h @@ -0,0 +1,85 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RAWDECODINGERROR_H +#define ALICEO2_PHOS_RAWDECODINGERROR_H + +#include <exception> + +namespace o2 +{ + +namespace phos +{ + +/// \class RawDecodingError +/// \brief Error handling of the raw reader +/// \ingroup PHOSreconstruction +/// +/// The following error types are defined: +/// - Page not found +/// - Raw header decoding error +/// - Payload decoding error +class RawDecodingError : public std::exception +{ + public: + /// \enum ErrorType_t + /// \brief Codes for different error types + enum class ErrorType_t { + PAGE_NOTFOUND, ///< Page was not found (page index outside range) + HEADER_DECODING, ///< Header cannot be decoded (format incorrect) + PAYLOAD_DECODING, ///< Payload cannot be decoded (format incorrect) + HEADER_INVALID, ///< Header in memory not belonging to requested superpage + PAYLOAD_INVALID, ///< Payload in memory not belonging to requested superpage + }; + + /// \brief Constructor + /// \param errtype Identifier code of the error type + /// + /// Constructing the error with error code. To be called when the + /// exception is thrown. + RawDecodingError(ErrorType_t errtype) : mErrorType(errtype) + { + } + + /// \brief destructor + ~RawDecodingError() noexcept override = default; + + /// \brief Providing error message of the exception + /// \return Error message of the exception + const char* what() const noexcept override + { + switch (mErrorType) { + case ErrorType_t::PAGE_NOTFOUND: + return "Page with requested index not found"; + case ErrorType_t::HEADER_DECODING: + return "RDH of page cannot be decoded"; + case ErrorType_t::PAYLOAD_DECODING: + return "Payload of page cannot be decoded"; + case ErrorType_t::HEADER_INVALID: + return "Access to header not belonging to requested superpage"; + case ErrorType_t::PAYLOAD_INVALID: + return "Access to payload not belonging to requested superpage"; + }; + return "Undefined error"; + } + + /// \brief Get the type identifier of the error handled with this exception + /// \return Error code of the exception + ErrorType_t getErrorType() const { return mErrorType; } + + private: + ErrorType_t mErrorType; ///< Type of the error +}; + +} // namespace phos + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawHeaderStream.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawHeaderStream.h new file mode 100644 index 0000000000000..f96d478dc84fe --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawHeaderStream.h @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RawHeaderStream.h +/// \brief Input stream operators for raw header 4 and 5 from binary file +/// +/// Helpers to define input stream operator for raw headers v4 and v5 from +/// binary file input stream, used in RawReaderFile + +#ifndef ALICEO2_PHOS_RAWHEADERSTREAM_H +#define ALICEO2_PHOS_RAWHEADERSTREAM_H + +#include <iosfwd> +#include "Headers/RAWDataHeader.h" + +namespace o2 +{ + +namespace phos +{ + +std::istream& operator>>(std::istream& stream, o2::header::RAWDataHeaderV4& header); +std::istream& operator>>(std::istream& stream, o2::header::RAWDataHeaderV5& header); + +std::ostream& operator<<(std::ostream& stream, const o2::header::RAWDataHeaderV4& header); +std::ostream& operator<<(std::ostream& stream, const o2::header::RAWDataHeaderV5& header); + +} // namespace phos + +} // namespace o2 +#endif diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawPayload.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawPayload.h new file mode 100644 index 0000000000000..8340d9dd66bcc --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawPayload.h @@ -0,0 +1,89 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RAWPAYLOAD_H +#define ALICEO2_PHOS_RAWPAYLOAD_H + +#include <cstdint> +#include <vector> +#include <gsl/span> +#include "Rtypes.h" + +namespace o2 +{ + +namespace phos +{ + +/// \class RawPayload +/// \brief Class for raw payload excluding raw data headers from one or multiple DMA pages +/// \ingroup PHOSreconstruction +/// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory +/// \since Nov 14, 2019 +/// +/// Container of 32-bit words in the current payload which can come from a single DMA page +/// or, in case the payload had to be split into multiple pages due to exceeding of the +/// page size, from multiple DMA pages. A page counter provides the amount of DMA pages +/// contributing to the current payload. +class RawPayload +{ + public: + /// \brief Constructor + RawPayload() = default; + + /// \brief Constructor + /// \param payloadwords Payload words of one or multiple pages + /// \param numpages Number of DMA pages contributing to the payload + RawPayload(const gsl::span<const uint32_t> payloadwords, int numpages); + + /// \brief Destructor + ~RawPayload() = default; + + /// \brief Set the number of pages contributing to the current payload + /// \param numpages Number of DMA pages contributing to the payload + void setNumberOfPages(int numpages) { mNumberOfPages = numpages; } + + /// \brief Append many words to the current payload (usually of a given DMA page) + /// \param payloadwords Payload words to be appened to the current payload + void appendPayloadWords(const gsl::span<const uint32_t> payloadwords); + + /// \brief Append single payload word to the current payload + /// \param payloadword Payload word to be appended to the current payload + void appendPayloadWord(uint32_t payloadword) { mPayloadWords.emplace_back(payloadword); }; + + /// \brief Increase the page counter of the current payload + void increasePageCount() { mNumberOfPages++; } + + /// \brief Get the payload words (as 32 bit words) contributing to the current payload + /// \return Words of the current payload + const std::vector<uint32_t>& getPayloadWords() const { return mPayloadWords; } + + /// \brief Get the number of pages contributing to the payload + /// \return Number of pages + int getNumberOfPages() const { return mNumberOfPages; } + + /// \brief Resetting payload words and page counter + void reset(); + + /// \brief Get the size of the payload + /// \return Size of the payload + int getPayloadSize() const { return mPayloadWords.size(); } + + private: + std::vector<uint32_t> mPayloadWords; ///< Payload words (excluding raw header) + int mNumberOfPages; ///< Number of DMA pages + + ClassDefNV(RawPayload, 1); +}; + +} // namespace phos + +} // namespace o2 +#endif diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawReaderError.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawReaderError.h new file mode 100644 index 0000000000000..1663145e510e7 --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawReaderError.h @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RAWREADERERROR_H +#define ALICEO2_PHOS_RAWREADERERROR_H + +namespace o2 +{ + +namespace phos +{ + +/// \class RawReaderError +/// \brief Error occured during reasing raw data +/// \ingroup PHOSReconstruction +/// +/// Error contains DDL number, FEE, chip, channel number if possible and error code + +class RawReaderError +{ + public: + /// \brief Constructor + RawReaderError() = default; + + /// \brief Constructor + RawReaderError(char ddl, char fec, char err) : mDDL(ddl), mFEC(fec), mErr(err) {} + + /// \brief destructor + ~RawReaderError() = default; + + char getDDL() { return mDDL; } + char getFEC() { return mDDL; } + char getError() { return mErr; } + + private: + char mDDL = 0; + char mFEC = 0; + char mErr = 0; + + ClassDefNV(RawReaderError, 1); +}; + +} // namespace phos + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawReaderMemory.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawReaderMemory.h new file mode 100644 index 0000000000000..30f4852603639 --- /dev/null +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/RawReaderMemory.h @@ -0,0 +1,120 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RAWREADERMEMORY_H +#define ALICEO2_PHOS_RAWREADERMEMORY_H + +#include <gsl/span> +#include <Rtypes.h> + +#include "PHOSBase/RCUTrailer.h" +#include "PHOSReconstruction/RawBuffer.h" +#include "PHOSReconstruction/RawPayload.h" +#include "Headers/RAWDataHeader.h" +#include "Headers/RDHAny.h" + +namespace o2 +{ + +namespace phos +{ + +/// \class RawReaderMemory +/// \brief Reader for raw data produced by the Readout application in in-memory format +/// \ingroup PHOSreconstruction +/// \author Dmitri Peresunko after Markus Fasel +/// \since Sept. 25, 2020 +/// +/// +class RawReaderMemory +{ + public: + /// \brief Constructor + RawReaderMemory(const gsl::span<const char> rawmemory); + + /// \brief Destructor + ~RawReaderMemory() = default; + + /// \brief set new raw memory chunk + /// \param rawmemory New raw memory chunk + void setRawMemory(const gsl::span<const char> rawmemory); + + /// \brief Read next payload from the stream + /// + /// Read the next pages until the stop bit is found. + void next(); + + /// \brief Read the next page from the stream (single DMA page) + /// \param resetPayload If true the raw payload is reset + /// \throw Error if the page cannot be read or header or payload cannot be deocded + /// + /// Function reading a single DMA page from the stream. It is called + /// inside the next() function for reading payload from multiple DMA + /// pages. As the function cannot handle payload from multiple pages + /// it should not be called directly by the user. + void nextPage(bool resetPayload = true); + + /// \brief access to the raw header of the current page + /// \return Raw header of the current page + /// \throw RawDecodingError with HEADER_INVALID if the header was not decoded + const o2::header::RDHAny& getRawHeader() const; + + /// \brief access to the raw buffer (single DMA page) + /// \return Raw buffer of the current page + /// \throw Error with PAYLOAD_INCALID if payload was not decoded + const RawBuffer& getRawBuffer() const; + + /// \brief access to the full raw payload (single or multiple DMA pages) + /// \return Raw Payload of the data until the stop bit is received. + const RawPayload& getPayload() const { return mRawPayload; } + + /// \brief Return size of the payload + /// \return size of the payload + int getPayloadSize() const { return mRawPayload.getPayloadSize(); } + + /// \brief get the size of the file in bytes + /// \return size of the file in byte + int getFileSize() const noexcept { return mRawMemoryBuffer.size(); } + + /// \brief get the number of pages in the file + /// \return number of pages in the file + int getNumberOfPages() const noexcept { return mNumData; } + + /// \brief check if more pages are available in the raw file + /// \return true if there is a next page + bool hasNext() const { return mCurrentPosition < mRawMemoryBuffer.size(); } + + protected: + /// \brief Initialize the raw stream + /// + /// Rewind stream to the first entry + void init(); + + o2::header::RDHAny decodeRawHeader(const void* headerwords); + + private: + gsl::span<const char> mRawMemoryBuffer; ///< Memory block with multiple DMA pages + RawBuffer mRawBuffer; ///< Raw buffer + o2::header::RDHAny mRawHeader; ///< Raw header + RawPayload mRawPayload; ///< Raw payload (can consist of multiple pages) + RCUTrailer mCurrentTrailer; ///< RCU trailer + uint64_t mTrailerPayloadWords = 0; ///< Payload words in common trailer + int mCurrentPosition = 0; ///< Current page in file + int mNumData = 0; ///< Number of pages + bool mRawHeaderInitialized = false; ///< RDH for current page initialized + bool mPayloadInitialized = false; ///< Payload for current page initialized + + ClassDefNV(RawReaderMemory, 1); +}; + +} // namespace phos + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/run/rawReaderFile.cxx b/Detectors/PHOS/reconstruction/run/rawReaderFile.cxx new file mode 100644 index 0000000000000..e2a7d78f0207e --- /dev/null +++ b/Detectors/PHOS/reconstruction/run/rawReaderFile.cxx @@ -0,0 +1,122 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 rawReaderFileNew.cxx +/// \author Markus Fasel <markus.fasel@cern.ch>, Oak Ridge National Laboratory + +#include <iostream> +#include <boost/program_options.hpp> + +#include "DetectorsRaw/RawFileReader.h" +#include "PHOSReconstruction/AltroDecoder.h" +#include "PHOSReconstruction/RawReaderMemory.h" +#include "FairLogger.h" + +namespace bpo = boost::program_options; +//using namespace o2::phos; + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " <cmds/options>\n" + " Tool will decode the DDLx data for PHOS 0\n" + "Commands / Options"); + 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("verbose,v", bpo::value<uint32_t>()->default_value(0), "Select verbosity level [0 = no output]"); + add_option("version", "Print version information"); + add_option("input-file,i", bpo::value<std::string>()->required(), "Specifies input file."); + add_option("debug,d", bpo::value<uint32_t>()->default_value(0), "Select debug output level [0 = no debug output]"); + + 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") || argc == 1) { + std::cout << opt_general << std::endl; + exit(0); + } + + if (vm.count("version")) { + //std::cout << GitInfo(); + 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); + } + + auto rawfilename = vm["input-file"].as<std::string>(); + + o2::raw::RawFileReader reader; + reader.setDefaultDataOrigin(o2::header::gDataOriginPHS); + reader.setDefaultDataDescription(o2::header::gDataDescriptionRawData); + reader.setDefaultReadoutCardType(o2::raw::RawFileReader::RORC); + reader.addFile(rawfilename); + reader.init(); + + while (1) { + int tfID = reader.getNextTFToRead(); + if (tfID >= reader.getNTimeFrames()) { + LOG(INFO) << "nothing left to read after " << tfID << " TFs read"; + break; + } + std::vector<char> dataBuffer; // where to put extracted data + for (int il = 0; il < reader.getNLinks(); il++) { + auto& link = reader.getLink(il); + std::cout << "Decoding link " << il << std::endl; + + auto sz = link.getNextTFSize(); // size in bytes needed for the next TF of this link + dataBuffer.resize(sz); + link.readNextTF(dataBuffer.data()); + + // Parse + o2::phos::RawReaderMemory parser(dataBuffer); + while (parser.hasNext()) { + parser.next(); + // Exclude STU DDLs + if (o2::raw::RDHUtils::getFEEID(parser.getRawHeader()) >= 40) { + continue; + } + o2::phos::AltroDecoder decoder(parser); + decoder.decode(); + + auto& rcu = decoder.getRCUTrailer(); + auto& channellist = decoder.getChannels(); + std::cout << rcu << std::endl; + for (auto& chan : channellist) { + std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; + for (auto& bunch : chan.getBunches()) { + std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; + auto adcs = bunch.getADC(); + int time = bunch.getStartTime(); + for (int i = adcs.size() - 1; i >= 0; i--) { + std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; + time--; + } + } + } + } + } + reader.setNextTFToRead(++tfID); + } +} diff --git a/Detectors/PHOS/reconstruction/src/AltroDecoder.cxx b/Detectors/PHOS/reconstruction/src/AltroDecoder.cxx new file mode 100644 index 0000000000000..5547ececcd2f0 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/AltroDecoder.cxx @@ -0,0 +1,120 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <cstring> +#include <boost/format.hpp> +#include "InfoLogger/InfoLogger.hxx" +#include "PHOSReconstruction/AltroDecoder.h" +#include "PHOSReconstruction/RawReaderMemory.h" +#include "PHOSReconstruction/RawDecodingError.h" + +#include "DetectorsRaw/RDHUtils.h" +#include <FairLogger.h> + +using namespace o2::phos; + +AltroDecoder::AltroDecoder(RawReaderMemory& reader) : mRawReader(reader), + mRCUTrailer(), + mChannels(), + mChannelsInitialized(false) +{ +} + +AltroDecoderError::ErrorType_t AltroDecoder::decode() +{ + try { + readRCUTrailer(); + } catch (RCUTrailer::Error& e) { + LOG(ERROR) << "RCU trailer error" << (int)e.getErrorType(); + return AltroDecoderError::RCU_TRAILER_ERROR; + } + //TODO checkRCUTrailer(); + try { + readChannels(); + } catch (AltroDecoderError::ErrorType_t e) { + LOG(ERROR) << "Altro decoding error " << e; + return e; + } + return AltroDecoderError::kOK; +} + +void AltroDecoder::readRCUTrailer() +{ + try { + auto payloadwords = mRawReader.getPayload().getPayloadWords(); + gsl::span<const uint32_t> tmp(payloadwords.data(), payloadwords.size()); + mRCUTrailer.constructFromRawPayload(tmp); + } catch (RCUTrailer::Error& e) { + throw e; + } +} + +void AltroDecoder::checkRCUTrailer() +{ +} + +void AltroDecoder::readChannels() +{ + mChannelsInitialized = false; + mChannels.clear(); + int currentpos = 0; + auto& buffer = mRawReader.getPayload().getPayloadWords(); + + int payloadend = mRCUTrailer.getPayloadSize(); + while (currentpos < payloadend) { + auto currentword = buffer[currentpos++]; + ChannelHeader header = {currentword}; + + if (header.mMark != 1) { + LOG(ERROR) << "Channel header mark not found"; + continue; + } + // starting a new channel + mChannels.emplace_back(int(header.mHardwareAddress), int(header.mPayloadSize)); + auto& currentchannel = mChannels.back(); + /// decode all words for channel + int numberofwords = (currentchannel.getPayloadSize() + 2) / 3; + if (numberofwords > payloadend - currentpos) { + LOG(ERROR) << "Channel payload " << numberofwords << " larger than left in total " << payloadend - currentpos; + continue; + } + std::vector<uint16_t> bunchwords; + for (int iword = 0; iword < numberofwords; iword++) { + currentword = buffer[currentpos++]; + if ((currentword >> 30) != 0) { + LOG(ERROR) << "Unexpected end of payload in altro channel payload! FEE=" << o2::raw::RDHUtils::getFEEID(mRawReader.getRawHeader()) + << ", Address=0x" << std::hex << currentchannel.getHardwareAddress() << ", word=0x" << currentword << std::dec; + currentpos--; + continue; + } + bunchwords.push_back((currentword >> 20) & 0x3FF); + bunchwords.push_back((currentword >> 10) & 0x3FF); + bunchwords.push_back(currentword & 0x3FF); + } + + // decode bunches + int currentsample = 0; + while (currentsample < currentchannel.getPayloadSize()) { + int bunchlength = bunchwords[currentsample] - 2, // remove words for bunchlength and starttime + starttime = bunchwords[currentsample + 1]; + auto& currentbunch = currentchannel.createBunch(bunchlength, starttime); + currentbunch.initFromRange(gsl::span<uint16_t>(&bunchwords[currentsample + 2], std::min((unsigned long)bunchlength, bunchwords.size() - currentsample - 2))); + currentsample += bunchlength + 2; + } + } + mChannelsInitialized = true; +} + +const std::vector<Channel>& AltroDecoder::getChannels() const +{ + if (!mChannelsInitialized) { + throw AltroDecoderError::ErrorType_t::CHANNEL_ERROR; // "Channels not initizalized"); + } + return mChannels; +} diff --git a/Detectors/PHOS/reconstruction/src/Bunch.cxx b/Detectors/PHOS/reconstruction/src/Bunch.cxx new file mode 100644 index 0000000000000..9c0a483f5a0f0 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/Bunch.cxx @@ -0,0 +1,19 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PHOSReconstruction/Bunch.h" + +using namespace o2::phos; + +void Bunch::initFromRange(gsl::span<uint16_t> adcs) +{ + for (auto adcval : adcs) { + mADC.emplace_back(adcval); + } +} \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/src/CTFCoder.cxx b/Detectors/PHOS/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..921dfb1de8f83 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief class for entropy encoding/decoding of PHOS data + +#include "PHOSReconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::phos; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, std::vector<TriggerRecord>& trigVec, std::vector<Cell>& cellVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, trigVec, cellVec); +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + // just to get types + uint16_t bcInc = 0, entries = 0, cellTime = 0, energy = 0, packedid = 0; + uint32_t orbitInc = 0; + uint8_t status = 0; +#define MAKECODER(part, slot) createCoder<decltype(part)>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(bcInc, CTF::BLC_bcIncTrig); + MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); + MAKECODER(entries, CTF::BLC_entriesTrig); + MAKECODER(packedid, CTF::BLC_packedID); + MAKECODER(cellTime, CTF::BLC_time); + MAKECODER(energy, CTF::BLC_energy); + MAKECODER(status, CTF::BLC_status); + // clang-format on +} diff --git a/Detectors/PHOS/reconstruction/src/CTFHelper.cxx b/Detectors/PHOS/reconstruction/src/CTFHelper.cxx new file mode 100644 index 0000000000000..d682ddea97349 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/CTFHelper.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFHelper.cxx +/// \author ruben.shahoyan@cern.ch +/// \brief Helper for PHOS CTF creation + +#include "PHOSReconstruction/CTFHelper.h" diff --git a/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx b/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx new file mode 100644 index 0000000000000..05a418a28ddb3 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/CaloRawFitter.cxx @@ -0,0 +1,327 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CaloRawFitter.cxx +/// \author Dmitri Peresunko + +#include "FairLogger.h" +#include <gsl/span> + +#include "PHOSReconstruction/Bunch.h" +#include "PHOSReconstruction/CaloRawFitter.h" +#include "PHOSBase/PHOSSimParams.h" + +using namespace o2::phos; + +CaloRawFitter::FitStatus CaloRawFitter::evaluate(const std::vector<Bunch>& bunchlist) +{ + + mAmp.clear(); // used as mean in this mode + mTime.clear(); // used as RMS in pedestal mode + mChi2.clear(); // used as mean in this mode + + //Pedestal analysis mode + if (mPedestalRun) { + int nPed = 0; + float mean = 0.; + float rms = 0.; + for (auto b : bunchlist) { + const std::vector<uint16_t>& signal = b.getADC(); + for (std::vector<uint16_t>::const_reverse_iterator it = signal.rbegin(); it != signal.rend(); ++it) { + uint16_t a = *it; + mean += a; + rms += a * a; + ++nPed; + } + if (nPed > 0) { + mean /= nPed; + rms = rms / nPed - mean * mean; + if (rms > 0.) { + rms = sqrt(rms); + } + } + } + mAmp.push_back(mean); + mTime.push_back(rms); + mStatus = kOK; + return kOK; + } + + for (auto b : bunchlist) { + // Extract amplitude and time using maximum and k-level methods + FitStatus s = evalKLevel(b); + mStatus = s; + + // If this is Low Gain channel or explicitely requested, fit sample with Gamma2 function + if (makeFit || (mLowGain && s == kOverflow)) { + FitStatus s2 = fitGamma2(b); + } + //TODO: should we have separate status: overflow & fit OK, and overflow & fit failed, etc? + } + return mStatus; +} + +CaloRawFitter::FitStatus CaloRawFitter::evalKLevel(const Bunch& b) //const ushort *signal, int sigStart, int sigLength) +{ + // Calculate signal parameters (energy, time, quality) from array of samples + // Energy is a maximum sample minus pedestal 9 + // Time is the first time bin + // Signal overflows is there are at least 3 samples of the same amplitude above 900 + + float amp = 0.; + float time = 0.; + bool overflow = false; + + int sigLength = b.getBunchLength(); + if (sigLength == 0) { + return kEmptyBunch; + } + + const short kSpikeThreshold = o2::phos::PHOSSimParams::Instance().mSpikeThreshold; + const short kBaseLine = o2::phos::PHOSSimParams::Instance().mBaseLine; + const short kPreSamples = o2::phos::PHOSSimParams::Instance().mPreSamples; + + float pedMean = 0; + float pedRMS = 0; + int nPed = 0; + mMaxSample = 0; + int nMax = 0; //number of consequitive maximal samples + bool spike = false; + + const std::vector<uint16_t>& signal = b.getADC(); + + int ap = -1, app = -1; //remember previous values to evaluate spikes + + for (std::vector<uint16_t>::const_reverse_iterator it = signal.rbegin(); it != signal.rend(); ++it) { + uint16_t a = *it; + if (mPedSubtract) { + if (nPed < kPreSamples) { //inverse signal time order + nPed++; + pedMean += a; + pedRMS += a * a; + } + } + if (a > mMaxSample) { + mMaxSample = a; + nMax = 0; + } + if (a == mMaxSample) { + nMax++; + } + //check if there is a spike + if (app >= 0 && ap >= 0) { + spike |= (2 * ap - (a + app) > 2 * kSpikeThreshold); + } + app = ap; + ap = a; + } + amp = (float)mMaxSample; + + if (spike) { + mAmp.push_back(amp); + mTime.push_back(b.getStartTime() - 2); + mOverflow.push_back(false); + return kSpike; + } + + if (mMaxSample > 900 && nMax > 2) { + overflow = true; + } + + float pedestal = 0; + if (mPedSubtract) { + if (nPed > 0) { + pedRMS = (pedRMS - pedMean * pedMean / nPed) / nPed; + if (pedRMS > 0.) { + pedRMS = sqrt(pedRMS); + } + pedestal = pedMean / nPed; + } else { + mAmp.push_back(0); + mTime.push_back(b.getStartTime() - 2); + mOverflow.push_back(false); + return kBadPedestal; + } + } + + amp -= pedestal; + if (amp < kBaseLine) { + amp = 0; + } + + //Evaluate time + time = b.getStartTime() - 2; + const int nLine = 6; //Parameters of fitting + const float eMinTOF = 10.; //Choosed from beam-test and cosmic analyis + const float kAmp = 0.35; //Result slightly depends on them, so no getters + // Avoid too low peak: + if (amp < eMinTOF) { + mAmp.push_back(amp); + mTime.push_back(time); + mOverflow.push_back(false); + return kOK; //use estimated time + } + + // Find index posK (kLevel is a level of "timestamp" point Tk): + int posK = sigLength - 1; //last point before crossing k-level + float levelK = pedestal + kAmp * amp; + while (signal[posK] <= levelK && posK >= 0) { + posK--; + } + posK++; + + if (posK == 0 || posK == sigLength - 1) { + mAmp.push_back(amp); + mTime.push_back(time); + mOverflow.push_back(false); + return kNoTime; // + } + + // Find crossing point by solving linear equation (least squares method) + int np = 0; + int iup = posK - 1; + int idn = posK; + Double_t sx = 0., sy = 0., sxx = 0., sxy = 0.; + Double_t x, y; + + while (np < nLine) { + //point above crossing point + if (iup >= 0) { + x = sigLength - iup - 1; + y = signal[iup]; + sx += x; + sy += y; + sxx += (x * x); + sxy += (x * y); + np++; + iup--; + } + //Point below crossing point + if (idn < sigLength) { + if (signal[idn] < pedestal) { + idn = sigLength - 1; //do not scan further + idn++; + continue; + } + x = sigLength - idn - 1; + y = signal[idn]; + sx += x; + sy += y; + sxx += (x * x); + sxy += (x * y); + np++; + idn++; + } + if (idn >= sigLength && iup < 0) { + break; //can not fit futher + } + } + + Double_t det = np * sxx - sx * sx; + if (det == 0) { + mAmp.push_back(amp); + mTime.push_back(time); + mOverflow.push_back(false); + return kNoTime; + } + if (np == 0) { + mAmp.push_back(amp); + mTime.push_back(time); + mOverflow.push_back(false); + return kEmptyBunch; + } + Double_t c1 = (np * sxy - sx * sy) / det; //slope + Double_t c0 = (sy - c1 * sx) / np; //offset + if (c1 == 0) { + mAmp.push_back(amp); + mTime.push_back(time); + mOverflow.push_back(false); + return kNoTime; + } + + // Find where the line cross kLevel: + time += (levelK - c0) / c1 - 5.; //5: mean offset between k-Level and start times + + mAmp.push_back(amp); + mTime.push_back(time); + if (overflow) { + mOverflow.push_back(true); + return kOverflow; + } else { + mOverflow.push_back(false); + return kOK; + } +} + +CaloRawFitter::FitStatus CaloRawFitter::fitGamma2(const Bunch& b) +{ + // Fit bunch with gamma2 function + // TODO!!! validate method + //initial values + float A = mAmp.back(); + float t0 = mTime.back(); + const std::vector<uint16_t>& signal = b.getADC(); + uint16_t tsart = b.getStartTime(); + + const float alpha = 17.; //Decay time in units of 100ns //TODO!!! to be adjusted + const float kEpsilon = 1.e-6; //Accuracy of fit //TODO!!! to be adjusted + const int nIter = 10; //Maximal number of iterations //TODO!!! to be adjusted + + float chi2 = 0; + float derT = 0, derTprev = 0.; + float stepT = 0.1; + int iter = 0, i = 0; + do { + chi2 = 0.; + derTprev = derT; + derT = 0.; + i = 0; + float sA = 0., sB = 0.; + std::vector<uint16_t>::const_reverse_iterator it = signal.rbegin(); + while (it != signal.rend()) { + uint16_t si = *it; + float ti = tsart + i - t0; + it++; + i++; + if (mOverflow.back() && si == mMaxSample) { //do not fit saturated samples + continue; + } + float fi = ti * ti * exp(-ti * alpha); + chi2 += (si - A * fi) * (si - A * fi); + sA += si * fi; + sB += fi * fi; + if (ti != 0.) { + derT += (2. / ti - alpha) * fi * (A * fi - si); + } + } + derT *= A; + //calculate time step and next time + if (derTprev != 0. && derT - derTprev != 0.) { + stepT = derT / (derT - derTprev) * stepT; + } + derTprev = derT; + t0 -= stepT; + if (sB > 0.) { + A = sA / sB; + } + } while (fabs(stepT) > kEpsilon && iter < nIter); // if time step is too small, stop + + if (iter >= nIter) { //Fit did not converge, keep old A and t0. + return kFitFailed; + } + + if (i > 0) { //chi2/NDF + chi2 /= i; + } + + mTime.back() = t0; + mAmp.back() = A; + return kOK; +} diff --git a/Detectors/PHOS/reconstruction/src/Channel.cxx b/Detectors/PHOS/reconstruction/src/Channel.cxx new file mode 100644 index 0000000000000..8bd68a7feff91 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/Channel.cxx @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PHOSReconstruction/Channel.h" + +using namespace o2::phos; + +int Channel::getBranchIndex() const +{ + if (mHardwareAddress == -1) { + throw HardwareAddressError(); + } + return ((mHardwareAddress >> 11) & 0x1); +} + +int Channel::getFECIndex() const +{ + if (mHardwareAddress == -1) { + throw HardwareAddressError(); + } + return ((mHardwareAddress >> 7) & 0xF); +} + +Int_t Channel::getAltroIndex() const +{ + if (mHardwareAddress == -1) { + throw HardwareAddressError(); + } + return ((mHardwareAddress >> 4) & 0x7); +} + +Int_t Channel::getChannelIndex() const +{ + if (mHardwareAddress == -1) { + throw HardwareAddressError(); + } + return (mHardwareAddress & 0xF); +} + +Bunch& Channel::createBunch(uint8_t bunchlength, uint8_t starttime) +{ + mBunches.emplace_back(bunchlength, starttime); + return mBunches.back(); +} \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/src/Clusterer.cxx b/Detectors/PHOS/reconstruction/src/Clusterer.cxx index 9a7d5bffa8616..fc1ae809d9c6b 100644 --- a/Detectors/PHOS/reconstruction/src/Clusterer.cxx +++ b/Detectors/PHOS/reconstruction/src/Clusterer.cxx @@ -175,8 +175,9 @@ void Clusterer::convertCellsToDigits(gsl::span<const Cell> cells, int firstCellI if (mcmap[iLab] == i) { label = iLab; ++iLab; - if (iLab >= nLab) + if (iLab >= nLab) { --iLab; + } } mDigits.emplace_back(c.getAbsId(), c.getEnergy(), c.getTime(), label); mDigits.back().setHighGain(c.getHighGain()); @@ -197,8 +198,9 @@ void Clusterer::makeClusters(gsl::span<const Digit> digits) int iFirst = mFirstDigitInEvent; // first index of digit which potentially can be a part of cluster for (int i = iFirst; i < mLastDigitInEvent; i++) { - if (digitsUsed[i - mFirstDigitInEvent]) + if (digitsUsed[i - mFirstDigitInEvent]) { continue; + } const Digit& digitSeed = digits[i]; float digitSeedEnergy = calibrate(digitSeed.getAmplitude(), digitSeed.getAbsId()); @@ -230,8 +232,9 @@ void Clusterer::makeClusters(gsl::span<const Digit> digits) short digitSeedAbsId = clu->getDigitAbsId(index); index++; for (Int_t j = iFirst; j < mLastDigitInEvent; j++) { - if (digitsUsed[j - mFirstDigitInEvent]) + if (digitsUsed[j - mFirstDigitInEvent]) { continue; // look through remaining digits + } const Digit* digitN = &(digits[j]); float digitNEnergy = calibrate(digitN->getAmplitude(), digitN->getAbsId()); if (isBadChannel(digitN->getAbsId())) { //remove digit @@ -345,8 +348,9 @@ void Clusterer::unfoldOneCluster(FullCluster& iniClu, char nMax, gsl::span<int> a[iclu] += fij[idig][iclu] * fij[idig][iclu]; b[iclu] += it.energy * fij[idig][iclu]; for (int kclu = 0; kclu < nMax; kclu++) { - if (iclu == kclu) + if (iclu == kclu) { continue; + } c[iclu] += eMax[kclu] * fij[idig][iclu] * fij[idig][kclu]; } } diff --git a/Detectors/PHOS/reconstruction/src/FullCluster.cxx b/Detectors/PHOS/reconstruction/src/FullCluster.cxx index 80671129244b4..38d9fc69d3894 100644 --- a/Detectors/PHOS/reconstruction/src/FullCluster.cxx +++ b/Detectors/PHOS/reconstruction/src/FullCluster.cxx @@ -180,10 +180,11 @@ void FullCluster::evalDispersion() mDispersion /= wtot; } - if (mDispersion >= 0) + if (mDispersion >= 0) { mDispersion = std::sqrt(mDispersion); - else + } else { mDispersion = 0.; + } } //____________________________________________________________________________ void FullCluster::evalElipsAxis() @@ -219,14 +220,16 @@ void FullCluster::evalElipsAxis() dxz -= x * z; mLambdaLong = 0.5 * (dxx + dzz) + std::sqrt(0.25 * (dxx - dzz) * (dxx - dzz) + dxz * dxz); - if (mLambdaLong > 0) + if (mLambdaLong > 0) { mLambdaLong = std::sqrt(mLambdaLong); + } mLambdaShort = 0.5 * (dxx + dzz) - std::sqrt(0.25 * (dxx - dzz) * (dxx - dzz) + dxz * dxz); - if (mLambdaShort > 0) // To avoid exception if numerical errors lead to negative lambda. + if (mLambdaShort > 0) { // To avoid exception if numerical errors lead to negative lambda. mLambdaShort = std::sqrt(mLambdaShort); - else + } else { mLambdaShort = 0.; + } } else { mLambdaLong = mLambdaShort = 0.; } diff --git a/Detectors/PHOS/reconstruction/src/RawBuffer.cxx b/Detectors/PHOS/reconstruction/src/RawBuffer.cxx new file mode 100644 index 0000000000000..8df5d66998ad1 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/RawBuffer.cxx @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <fstream> +#include <iostream> +#include <cstring> +#include <FairLogger.h> +#include "PHOSReconstruction/RawBuffer.h" + +using namespace o2::phos; + +void RawBuffer::flush() +{ + mCurrentDataWord = 0; + mNDataWords = 0; + memset(mDataWords.data(), 0, sizeof(uint32_t) * mDataWords.size()); +} + +void RawBuffer::readFromStream(std::istream& in, uint32_t payloadsize) +{ + flush(); + uint32_t word(0); + auto address = reinterpret_cast<char*>(&word); + int nbyte = 0; + while (nbyte < payloadsize) { + in.read(address, sizeof(word)); + nbyte += sizeof(word); + if ((word & 0xFFFFFF) == 0x1d3082) { + LOG(ERROR) << "Found stop word" << std::endl; + // Termination word + // should normally not be decoded in case the payload size + // is determined correctly + break; + } + mDataWords[mNDataWords++] = word; + } +} + +void RawBuffer::readFromMemoryBuffer(const gsl::span<const char> rawmemory) +{ + flush(); + auto address = reinterpret_cast<const uint32_t*>(rawmemory.data()); + for (auto iword = 0; iword < rawmemory.size() / sizeof(uint32_t); iword++) { + // Run2 code, probably not needed for run3 + // if ((address[iword] & 0xFFF) == 0x082) { + // Termination word + // should normally not be decoded in case the payload size + // is determined correctly + //std::cout << "Found termination word" << std::endl; + //break; + // } + mDataWords[mNDataWords++] = address[iword]; + } +} + +uint32_t RawBuffer::getWord(int index) const +{ + if (index >= mNDataWords) { + throw std::runtime_error("Index out of range"); + } + return mDataWords[index]; +} + +uint32_t RawBuffer::getNextDataWord() +{ + if (!hasNext()) { + throw std::runtime_error("No more data words in buffer"); + } + return mDataWords[mCurrentDataWord++]; +} diff --git a/Detectors/PHOS/reconstruction/src/RawHeaderStream.cxx b/Detectors/PHOS/reconstruction/src/RawHeaderStream.cxx new file mode 100644 index 0000000000000..59714a5293d82 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/RawHeaderStream.cxx @@ -0,0 +1,106 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <fstream> +#include <iostream> +#include "PHOSReconstruction/RawHeaderStream.h" + +std::istream& o2::phos::operator>>(std::istream& stream, o2::header::RAWDataHeaderV4& header) +{ + //std::cout << "called, 10 words" << std::endl; + using wordtype = uint64_t; + constexpr int RAWHEADERWORDS = sizeof(header) / sizeof(wordtype); + wordtype message[RAWHEADERWORDS]; + auto address = reinterpret_cast<char*>(message); + for (int i = 0; i < RAWHEADERWORDS; i++) { + stream.read(address + i * sizeof(wordtype) / sizeof(char), sizeof(message[i])); + //std::cout << "Word " << i << ": " << std::hex << message[i] << std::dec << std::endl; + } + header.word0 = message[0]; + header.word1 = message[1]; + header.word2 = message[2]; + header.word3 = message[3]; + header.word4 = message[4]; + header.word5 = message[5]; + header.word6 = message[6]; + header.word7 = message[7]; + return stream; +} + +std::istream& o2::phos::operator>>(std::istream& stream, o2::header::RAWDataHeaderV5& header) +{ + //std::cout << "called, 10 words" << std::endl; + using wordtype = uint64_t; + constexpr int RAWHEADERWORDS = sizeof(header) / sizeof(wordtype); + wordtype message[RAWHEADERWORDS]; + auto address = reinterpret_cast<char*>(message); + for (int i = 0; i < RAWHEADERWORDS; i++) { + stream.read(address + i * sizeof(wordtype) / sizeof(char), sizeof(message[i])); + //std::cout << "Word " << i << ": " << std::hex << message[i] << std::dec << std::endl; + } + header.word0 = message[0]; + header.word1 = message[1]; + header.word2 = message[2]; + header.word3 = message[3]; + header.word4 = message[4]; + header.word5 = message[5]; + header.word6 = message[6]; + header.word7 = message[7]; + return stream; +} + +std::ostream& o2::phos::operator<<(std::ostream& stream, const o2::header::RAWDataHeaderV4& header) +{ + stream << "Raw data header V4:\n" + << " Word0 " << header.word0 << " (0x" << std::hex << header.word0 << std::dec << ")\n" + << " Version: " << header.version << "\n" + << " Header size: " << header.headerSize << "\n" + << " Block length: " << header.blockLength << "\n" + << " FEE ID: " << header.feeId << "\n" + << " Priority: " << header.priority << "\n" + << " Word1 " << header.word1 << " (0x" << std::hex << header.word1 << std::dec << ")\n" + << " Offset to next: " << header.offsetToNext << "\n" + << " Payload size (B):" << header.memorySize << "\n" + << " Packet counter: " << static_cast<int>(header.packetCounter) << "\n" + << " Link ID: " << static_cast<int>(header.linkID) << "\n" + << " Card ID: " << header.cruID << "\n" + << " Endpoint: " << static_cast<int>(header.endPointID) << "\n" + << " Word2 " << header.word2 << " (0x" << std::hex << header.word2 << std::dec << ")\n" + << " Trigger orbit: " << header.triggerOrbit << "\n" + << " Heartbeat orbit: " << header.heartbeatOrbit << "\n" + << " Word3 " << header.word3 << " (0x" << std::hex << header.word3 << std::dec << ")\n" + << " Word4 " << header.word4 << " (0x" << std::hex << header.word4 << std::dec << ")\n" + << " Trigger BC: " << header.triggerBC << "\n" + << " Heartbeat BC: " << header.heartbeatBC << "\n" + << " Trigger Type: " << header.triggerType << "\n" + << " Word5 " << header.word5 << " (0x" << std::hex << header.word5 << std::dec << ")\n" + << " Word6 " << header.word6 << " (0x" << std::hex << header.word6 << std::dec << ")\n" + << " Detector Field: " << header.detectorField << "\n" + << " PAR: " << header.par << "\n" + << " STOP: " << header.stop << "\n" + << " Page count: " << header.pageCnt << "\n" + << " Word7 " << header.word7 << " (0x" << std::hex << header.word7 << std::dec << ")\n" + << "End header\n"; + return stream; +} + +std::ostream& o2::phos::operator<<(std::ostream& stream, const o2::header::RAWDataHeaderV5& header) +{ + stream << "Raw data header V5:\n" + << " Word0 " << header.word0 << " (0x" << std::hex << header.word0 << std::dec << ")\n" + << " Word1 " << header.word1 << " (0x" << std::hex << header.word1 << std::dec << ")\n" + << " Word2 " << header.word2 << " (0x" << std::hex << header.word2 << std::dec << ")\n" + << " Word3 " << header.word3 << " (0x" << std::hex << header.word3 << std::dec << ")\n" + << " Word4 " << header.word4 << " (0x" << std::hex << header.word4 << std::dec << ")\n" + << " Word5 " << header.word5 << " (0x" << std::hex << header.word5 << std::dec << ")\n" + << " Word6 " << header.word6 << " (0x" << std::hex << header.word6 << std::dec << ")\n" + << " Word7 " << header.word7 << " (0x" << std::hex << header.word7 << std::dec << ")\n" + << "End header\n"; + return stream; +} \ No newline at end of file diff --git a/Detectors/PHOS/reconstruction/src/RawPayload.cxx b/Detectors/PHOS/reconstruction/src/RawPayload.cxx new file mode 100644 index 0000000000000..5818b4e124054 --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/RawPayload.cxx @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PHOSReconstruction/RawPayload.h" + +using namespace o2::phos; + +RawPayload::RawPayload(gsl::span<const uint32_t> payloadwords, int numpages) : mPayloadWords(payloadwords.size()), + mNumberOfPages(numpages) +{ + for (auto word : payloadwords) { + mPayloadWords.emplace_back(word); + } +} + +void RawPayload::appendPayloadWords(const gsl::span<const uint32_t> payloadwords) +{ + for (auto word : payloadwords) { + mPayloadWords.emplace_back(word); + } +} + +void RawPayload::reset() +{ + mPayloadWords.clear(); + mNumberOfPages = 0; +} diff --git a/Detectors/PHOS/reconstruction/src/RawReaderMemory.cxx b/Detectors/PHOS/reconstruction/src/RawReaderMemory.cxx new file mode 100644 index 0000000000000..99c48aff52d7c --- /dev/null +++ b/Detectors/PHOS/reconstruction/src/RawReaderMemory.cxx @@ -0,0 +1,151 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <sstream> +#include <string> +#include "PHOSReconstruction/RawReaderMemory.h" +#include "PHOSReconstruction/RawDecodingError.h" +#include "DetectorsRaw/RDHUtils.h" + +using namespace o2::phos; + +using RDHDecoder = o2::raw::RDHUtils; + +RawReaderMemory::RawReaderMemory(gsl::span<const char> rawmemory) : mRawMemoryBuffer(rawmemory) +{ + init(); +} + +void RawReaderMemory::setRawMemory(const gsl::span<const char> rawmemory) +{ + mRawMemoryBuffer = rawmemory; + init(); +} + +o2::header::RDHAny RawReaderMemory::decodeRawHeader(const void* payloadwords) +{ + auto headerversion = RDHDecoder::getVersion(payloadwords); + if (headerversion == 4) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV4*>(payloadwords)); + } else if (headerversion == 5) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV5*>(payloadwords)); + } else if (headerversion == 6) { + return o2::header::RDHAny(*reinterpret_cast<const o2::header::RAWDataHeaderV6*>(payloadwords)); + } + throw RawDecodingError::ErrorType_t::HEADER_DECODING; +} + +void RawReaderMemory::init() +{ + mCurrentPosition = 0; + mRawHeaderInitialized = false; + mPayloadInitialized = false; + mRawBuffer.flush(); + mNumData = mRawMemoryBuffer.size() / 8192; // assume fixed 8 kB pages +} + +void RawReaderMemory::next() +{ + //Reads pages till RCU trailer found + //Several 8kB pages can be concatenated + //RCU trailer expected at the end of payload + //but not at the end of each page + mRawPayload.reset(); + mCurrentTrailer.reset(); + bool isDataTerminated = false; + do { + try { + nextPage(false); + } catch (RawDecodingError::ErrorType_t e) { + throw e; + } + if (hasNext()) { + auto nextheader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + // check continuing payload based on the bc/orbit ID + auto currentbc = RDHDecoder::getTriggerBC(mRawHeader), + nextbc = RDHDecoder::getTriggerBC(nextheader); + auto currentorbit = RDHDecoder::getTriggerOrbit(mRawHeader), + nextorbit = RDHDecoder::getTriggerOrbit(nextheader); + if (currentbc != nextbc || currentorbit != nextorbit) { + isDataTerminated = true; + } else { + auto nextpagecounter = RDHDecoder::getPageCounter(nextheader); + if (nextpagecounter == 0) { + isDataTerminated = true; + } else { + isDataTerminated = false; + } + } + } else { + isDataTerminated = true; + } + // Check if the data continues + } while (!isDataTerminated); + try { + mCurrentTrailer.constructFromPayloadWords(mRawBuffer.getDataWords()); + } catch (...) { + throw RawDecodingError::ErrorType_t::HEADER_DECODING; + } +} + +void RawReaderMemory::nextPage(bool doResetPayload) +{ + if (!hasNext()) { + throw RawDecodingError::ErrorType_t::PAGE_NOTFOUND; + } + if (doResetPayload) { + mRawPayload.reset(); + } + mRawHeaderInitialized = false; + mPayloadInitialized = false; + + // Read RDH header + try { + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + while (RDHDecoder::getOffsetToNext(mRawHeader) == RDHDecoder::getHeaderSize(mRawHeader) && + mCurrentPosition < mRawMemoryBuffer.size()) { + // No Payload - jump to next rawheader + // This will eventually move, depending on whether for events without payload in the SRU we send the RCU trailer + mCurrentPosition += RDHDecoder::getHeaderSize(mRawHeader); + mRawHeader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + } + mRawHeaderInitialized = true; + } catch (...) { + throw RawDecodingError::ErrorType_t::HEADER_DECODING; + } + if (mCurrentPosition + RDHDecoder::getMemorySize(mRawHeader) > mRawMemoryBuffer.size()) { + // Payload incomplete + throw RawDecodingError::ErrorType_t::PAYLOAD_DECODING; + } + + mRawBuffer.readFromMemoryBuffer(gsl::span<const char>(mRawMemoryBuffer.data() + mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader), + RDHDecoder::getMemorySize(mRawHeader) - RDHDecoder::getHeaderSize(mRawHeader))); + gsl::span<const uint32_t> payloadWithoutTrailer(mRawBuffer.getDataWords().data(), mRawBuffer.getNDataWords()); + mRawPayload.appendPayloadWords(payloadWithoutTrailer); + mRawPayload.increasePageCount(); + + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); /// Assume fixed 8 kB page size +} + +const o2::header::RDHAny& RawReaderMemory::getRawHeader() const +{ + if (!mRawHeaderInitialized) { + throw RawDecodingError::ErrorType_t::HEADER_INVALID; + } + return mRawHeader; +} + +const RawBuffer& RawReaderMemory::getRawBuffer() const +{ + if (!mPayloadInitialized) { + throw RawDecodingError::ErrorType_t::PAYLOAD_INVALID; + } + return mRawBuffer; +} diff --git a/Detectors/PHOS/simulation/CMakeLists.txt b/Detectors/PHOS/simulation/CMakeLists.txt index a7dc3a7ef4095..fc4ce4e851b50 100644 --- a/Detectors/PHOS/simulation/CMakeLists.txt +++ b/Detectors/PHOS/simulation/CMakeLists.txt @@ -12,14 +12,25 @@ o2_add_library(PHOSSimulation SOURCES src/Detector.cxx src/GeometryParams.cxx src/Digitizer.cxx + src/RawWriter.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::DataFormatsPHOS O2::PHOSBase - O2::PHOSCalib) + O2::PHOSCalib + O2::SimConfig + O2::SimulationDataFormat + O2::Headers + O2::DetectorsRaw) o2_target_root_dictionary(PHOSSimulation HEADERS include/PHOSSimulation/Detector.h include/PHOSSimulation/GeometryParams.h - include/PHOSSimulation/Digitizer.h) + include/PHOSSimulation/Digitizer.h + include/PHOSSimulation/RawWriter.h) +o2_add_executable(digi2raw + COMPONENT_NAME phos + PUBLIC_LINK_LIBRARIES O2::PHOSSimulation + SOURCES src/RawCreator.cxx) + o2_data_file(COPY data DESTINATION Detectors/PHS/simulation) diff --git a/Detectors/PHOS/simulation/include/PHOSSimulation/Detector.h b/Detectors/PHOS/simulation/include/PHOSSimulation/Detector.h index aa3629d05b684..34d7dfc138a40 100644 --- a/Detectors/PHOS/simulation/include/PHOSSimulation/Detector.h +++ b/Detectors/PHOS/simulation/include/PHOSSimulation/Detector.h @@ -12,7 +12,7 @@ #define ALICEO2_PHOS_DETECTOR_H_ #include "DetectorsBase/Detector.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "PHOSBase/Hit.h" #include "RStringView.h" #include "Rtypes.h" @@ -95,7 +95,7 @@ class Detector : public o2::base::DetImpl<Detector> /// \param[in] time Time of the hit /// \param[in] energyloss Energy deposited in this step /// - Hit* addHit(Int_t trackID, Int_t detID, const Point3D<float>& pos, const Vector3D<float>& mom, Double_t totE, + Hit* addHit(Int_t trackID, Int_t detID, const math_utils::Point3D<float>& pos, const math_utils::Vector3D<float>& mom, Double_t totE, Double_t time, Double_t eLoss); /// @@ -108,10 +108,11 @@ class Detector : public o2::base::DetImpl<Detector> /// std::vector<Hit>* getHits(Int_t iColl) const { - if (iColl == 0) + if (iColl == 0) { return mHits; - else + } else { return nullptr; + } } /// diff --git a/Detectors/PHOS/simulation/include/PHOSSimulation/GeometryParams.h b/Detectors/PHOS/simulation/include/PHOSSimulation/GeometryParams.h index 50e6af8893924..b4f95f91ac1ed 100644 --- a/Detectors/PHOS/simulation/include/PHOSSimulation/GeometryParams.h +++ b/Detectors/PHOS/simulation/include/PHOSSimulation/GeometryParams.h @@ -21,7 +21,7 @@ namespace o2 { namespace phos { -class GeometryParams : public TNamed +class GeometryParams final : public TNamed { public: /// Default constructor @@ -33,8 +33,9 @@ class GeometryParams : public TNamed /// get singleton (create if necessary) static GeometryParams* GetInstance(const std::string_view name = "Run2") { - if (!sGeomParam) + if (!sGeomParam) { sGeomParam = new GeometryParams(name); + } return sGeomParam; } @@ -54,14 +55,17 @@ class GeometryParams : public TNamed void getModuleCenter(int module, float* pos) const { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { pos[i] = mModuleCenter[module][i]; + } } void getModuleAngle(int module, float angle[3][2]) const { - for (int i = 0; i < 3; i++) - for (int ian = 0; ian < 2; ian++) + for (int i = 0; i < 3; i++) { + for (int ian = 0; ian < 2; ian++) { angle[i][ian] = mModuleAngle[module][i][ian]; + } + } } // Return PHOS support geometry parameters float getRailOuterSize(int index) const { return mRailOuterSize[index]; } diff --git a/Detectors/PHOS/simulation/include/PHOSSimulation/RawWriter.h b/Detectors/PHOS/simulation/include/PHOSSimulation/RawWriter.h new file mode 100644 index 0000000000000..2ed3a643599c9 --- /dev/null +++ b/Detectors/PHOS/simulation/include/PHOSSimulation/RawWriter.h @@ -0,0 +1,104 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PHOS_RAWWRITER_H +#define ALICEO2_PHOS_RAWWRITER_H + +#include <gsl/span> + +#include <array> +#include <fstream> +#include <memory> +#include <string> +#include <map> +#include <vector> + +#include "Rtypes.h" + +#include "DetectorsRaw/RawFileWriter.h" +#include "PHOSBase/Mapping.h" +#include "DataFormatsPHOS/Digit.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "PHOSCalib/CalibParams.h" +#include "PHOSBase/RCUTrailer.h" + +namespace o2 +{ + +namespace phos +{ + +static constexpr short kNPHOSSAMPLES = 30; ///< Maximal number of samples in altro +static constexpr short kNPRESAMPLES = 2; ///< Number of pre-samples in altro +static constexpr short kOVERFLOW = 970; ///< Overflow level: 1023-pedestal~50 +static constexpr float kPHOSTIMETICK = 100.; ///< PHOS sampling time step in ns (hits/digits keep time in ns) + +struct AltroBunch { + int mStarttime; + std::vector<int> mADCs; +}; + +struct SRUDigitContainer { + int mSRUid; + std::map<short, std::vector<o2::phos::Digit*>> mChannels; +}; + +class RawWriter +{ + public: + enum class FileFor_t { + kFullDet, + kLink + }; + RawWriter() = default; + RawWriter(const char* outputdir) { setOutputLocation(outputdir); } + ~RawWriter() = default; + + o2::raw::RawFileWriter& getWriter() const { return *mRawWriter; } + + void setOutputLocation(const char* outputdir) { mOutputLocation = outputdir; } + void setFileFor(FileFor_t filefor) { mFileFor = filefor; } + + void init(); + void digitsToRaw(gsl::span<o2::phos::Digit> digits, gsl::span<o2::phos::TriggerRecord> triggers); + bool processTrigger(const gsl::span<o2::phos::Digit> digitsbranch, const o2::phos::TriggerRecord& trg); + + int carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data, + const char* ptr, int maxSize, int splitID, + std::vector<char>& trailer, std::vector<char>& header) const; + + protected: + void createRawBunches(short absId, const std::vector<o2::phos::Digit*>& digits, std::vector<o2::phos::AltroBunch>& bunchHG, + std::vector<o2::phos::AltroBunch>& bunchLG, bool& isLGFilled); + + std::vector<uint32_t> encodeBunchData(const std::vector<uint32_t>& data); + void fillGamma2(float amp, float time, short* samples); + // std::tuple<int, int, int> getOnlineID(int towerID); + // std::tuple<int, int> getLinkAssignment(int ddlID); + + std::vector<char> createRCUTrailer(int payloadsize, int feca, int fecb, double timesample, double l1phase); + + private: + FileFor_t mFileFor = FileFor_t::kFullDet; ///< Granularity of the output files + std::string mOutputLocation = "./"; ///< Rawfile name + std::unique_ptr<Mapping> mMapping; ///< Mapping handler + std::unique_ptr<const CalibParams> mCalibParams; ///< PHOS calibration + gsl::span<o2::phos::Digit> mDigits; ///< Digits input vector - must be in digitized format including the time response + std::vector<SRUDigitContainer> mSRUdata; ///< Internal helper of digits assigned to SRUs + std::unique_ptr<o2::raw::RawFileWriter> mRawWriter; ///< Raw writer + + ClassDefNV(RawWriter, 1); +}; + +} // namespace phos + +} // namespace o2 + +#endif diff --git a/Detectors/PHOS/simulation/src/Detector.cxx b/Detectors/PHOS/simulation/src/Detector.cxx index 1c9fd2f364bf4..a6309ccaf092e 100644 --- a/Detectors/PHOS/simulation/src/Detector.cxx +++ b/Detectors/PHOS/simulation/src/Detector.cxx @@ -161,8 +161,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) } Double_t lostenergy = fMC->Edep(); - if (lostenergy < DBL_EPSILON && !isNewPartile) + if (lostenergy < DBL_EPSILON && !isNewPartile) { return false; // do not create hits with zero energy deposition + } // if(strcmp(mc->CurrentVolName(),"PXTL")!=0) //Non need to check, alwais there... // return false ; // We are not inside a PBWO crystal @@ -186,8 +187,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) if (!isNewPartile) { for (Int_t itr = mHits->size() - 1; itr >= 0; itr--) { Hit* h = &(mHits->at(itr)); - if (h->GetTrackID() != superParent) // switched to another SuperParent, do not search further + if (h->GetTrackID() != superParent) { // switched to another SuperParent, do not search further break; + } if (h->GetDetectorID() == detID) { // found correct hit h->addEnergyLoss(lostenergy); mCurentSuperParent = superParent; @@ -203,9 +205,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) fMC->TrackPosition(posX, posY, posZ); fMC->TrackMomentum(momX, momY, momZ, energy); Double_t estart = fMC->Etot(); - Double_t time = fMC->TrackTime() * 1.e+9; // time in ns?? To be consistent with EMCAL + Double_t time = fMC->TrackTime() * 1.e+9; // time in ns - mCurrentHit = addHit(superParent, detID, Point3D<float>(posX, posY, posZ), Vector3D<float>(momX, momY, momZ), estart, + mCurrentHit = addHit(superParent, detID, math_utils::Point3D<float>(posX, posY, posZ), math_utils::Vector3D<float>(momX, momY, momZ), estart, time, lostenergy); mCurentSuperParent = superParent; mCurrentTrackID = partID; @@ -214,7 +216,7 @@ Bool_t Detector::ProcessHits(FairVolume* v) return true; } -Hit* Detector::addHit(Int_t trackID, Int_t detID, const Point3D<float>& pos, const Vector3D<float>& mom, Double_t totE, +Hit* Detector::addHit(Int_t trackID, Int_t detID, const math_utils::Point3D<float>& pos, const math_utils::Vector3D<float>& mom, Double_t totE, Double_t time, Double_t eLoss) { LOG(DEBUG4) << "Adding hit for track " << trackID << " with position (" << pos.X() << ", " << pos.Y() << ", " @@ -560,8 +562,9 @@ void Detector::ConstructEMCGeometry() Float_t x = (2 * irow + 1 - geom->getNStripX()) * strip[0]; for (icol = 0; icol < geom->getNStripZ(); icol++) { z = (2 * icol + 1 - geom->getNStripZ()) * strip[2]; - if (irow >= geom->getNStripX() / 2) + if (irow >= geom->getNStripX() / 2) { fMC->Gspos("PSTR", nr, "PTIH", x, y, z, 0, "ONLY"); + } nr++; } } diff --git a/Detectors/PHOS/simulation/src/Digitizer.cxx b/Detectors/PHOS/simulation/src/Digitizer.cxx index 630e31b2367c5..d516d7cc80b31 100644 --- a/Detectors/PHOS/simulation/src/Digitizer.cxx +++ b/Detectors/PHOS/simulation/src/Digitizer.cxx @@ -85,7 +85,10 @@ void Digitizer::process(const std::vector<Hit>* hitsBg, const std::vector<Hit>* } Int_t nTotCells = mGeometry->getTotalNCells(); - for (short absId = 1; absId < nTotCells; absId++) { + + short start = 64 * 56 + 32 * 56 + 1; //first digit in half-mod 1 + char relid[3]; + for (short absId = start; absId < nTotCells; absId++) { // If signal exist in this cell, add noise to it, otherwise just create noise digit if (absId == hitAbsId) { @@ -180,14 +183,18 @@ void Digitizer::process(const std::vector<Hit>* hitsBg, const std::vector<Hit>* energy = uncalibrate(energy, absId); - if (energy < o2::phos::PHOSSimParams::Instance().mZSthreshold) { + if (energy <= o2::phos::PHOSSimParams::Instance().mDigitThreshold) { continue; } - digit.setAmplitude(energy); - digit.setHighGain(energy < 1024); //10bit ADC - + digit.setHighGain(energy < o2::phos::PHOSSimParams::Instance().mMCOverflow); //10bit ADC + if (digit.isHighGain()) { + digit.setAmplitude(energy); + } else { + float hglgratio = mCalibParams->getHGLGRatio(absId); + digit.setAmplitude(energy / hglgratio); + } if (o2::phos::PHOSSimParams::Instance().mApplyTimeResolution) { - digit.setTimeStamp(uncalibrateT(timeResolution(digit.getTimeStamp(), energy), absId, digit.isHighGain())); + digit.setTime(uncalibrateT(timeResolution(digit.getTime(), energy), absId, digit.isHighGain())); } digits.push_back(digit); @@ -198,8 +205,8 @@ void Digitizer::process(const std::vector<Hit>* hitsBg, const std::vector<Hit>* // Simulate noise float energy = simulateNoiseEnergy(absId); energy = uncalibrate(energy, absId); - float time = simulateNoiseTime(); - if (energy > o2::phos::PHOSSimParams::Instance().mZSthreshold) { + if (energy > o2::phos::PHOSSimParams::Instance().mDigitThreshold) { + float time = simulateNoiseTime(); digits.emplace_back(absId, energy, time, -1); // current AbsId, energy, random time, no primary } } @@ -222,13 +229,14 @@ float Digitizer::uncalibrate(const float e, const int absId) if (calib > 0) { return floor(e / calib); } else { - return 0; // TODO apply de-calibration from OCDB + return 0; } } //_______________________________________________________________________ float Digitizer::uncalibrateT(const float time, const int absId, bool isHighGain) { // Decalibrate EMC digit, i.e. transform from energy to ADC counts a factor read from CDB + // note time in seconds if (isHighGain) { return time + mCalibParams->getHGTimeCalib(absId); } else { @@ -239,6 +247,7 @@ float Digitizer::uncalibrateT(const float time, const int absId, bool isHighGain float Digitizer::timeResolution(const float time, const float e) { // apply time resolution + // time measured in seconds float timeResolution = o2::phos::PHOSSimParams::Instance().mTimeResolutionA + o2::phos::PHOSSimParams::Instance().mTimeResolutionB / diff --git a/Detectors/PHOS/simulation/src/PHOSSimulationLinkDef.h b/Detectors/PHOS/simulation/src/PHOSSimulationLinkDef.h index c6017f6b1148b..e6f39d31db820 100644 --- a/Detectors/PHOS/simulation/src/PHOSSimulationLinkDef.h +++ b/Detectors/PHOS/simulation/src/PHOSSimulationLinkDef.h @@ -18,5 +18,6 @@ #pragma link C++ class o2::phos::GeometryParams + ; #pragma link C++ class o2::base::DetImpl < o2::phos::Detector> + ; #pragma link C++ class o2::phos::Digitizer + ; +#pragma link C++ class o2::phos::RawWriter + ; #endif diff --git a/Detectors/PHOS/simulation/src/RawCreator.cxx b/Detectors/PHOS/simulation/src/RawCreator.cxx new file mode 100644 index 0000000000000..dedf754d0b26e --- /dev/null +++ b/Detectors/PHOS/simulation/src/RawCreator.cxx @@ -0,0 +1,108 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <memory> +#include <string> +#include <vector> +#include "Framework/Logger.h" + +#include <boost/program_options.hpp> + +#include <TFile.h> +#include <TTree.h> +#include <TTreeReader.h> +#include <TSystem.h> + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/StringUtils.h" +#include "DataFormatsPHOS/Digit.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "PHOSBase/Geometry.h" +#include "PHOSSimulation/RawWriter.h" + +namespace bpo = boost::program_options; + +int main(int argc, const char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " <cmds/options>\n" + " Tool will encode phos raw data from input file\n" + "Commands / Options"); + 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("verbose,v", bpo::value<uint32_t>()->default_value(0), "Select verbosity level [0 = no output]"); + add_option("input-file,i", bpo::value<std::string>()->default_value("phosdigits.root"), "Specifies digit input file."); + add_option("file-for,f", bpo::value<std::string>()->default_value("all"), "single file per: all,link"); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + add_option("debug,d", bpo::value<uint32_t>()->default_value(0), "Select debug output level [0 = no debug output]"); + add_option("configKeyValues", bpo::value<std::string>()->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") || argc == 1) { + std::cout << opt_general << std::endl; + exit(0); + } + + } 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<std::string>()); + + auto digitfilename = vm["input-file"].as<std::string>(), + outputdir = vm["output-dir"].as<std::string>(), + filefor = vm["file-for"].as<std::string>(); + + // if needed, create output directory + if (gSystem->AccessPathName(outputdir.c_str())) { + if (gSystem->mkdir(outputdir.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outputdir; + } else { + LOG(INFO) << "created output directory " << outputdir; + } + } + + std::unique_ptr<TFile> digitfile(TFile::Open(digitfilename.data(), "READ")); + auto treereader = std::make_unique<TTreeReader>(static_cast<TTree*>(digitfile->Get("o2sim"))); + TTreeReaderValue<std::vector<o2::phos::Digit>> digitbranch(*treereader, "PHOSDigit"); + TTreeReaderValue<std::vector<o2::phos::TriggerRecord>> triggerbranch(*treereader, "PHOSDigitTrigRecords"); + + o2::phos::RawWriter::FileFor_t granularity = o2::phos::RawWriter::FileFor_t::kFullDet; + if (filefor == "all") { + granularity = o2::phos::RawWriter::FileFor_t::kFullDet; + } else if (filefor == "link") { + granularity = o2::phos::RawWriter::FileFor_t::kLink; + } + + o2::phos::RawWriter rawwriter; + rawwriter.setOutputLocation(outputdir.data()); + rawwriter.setFileFor(granularity); + rawwriter.init(); + + // Loop over all entries in the tree, where each tree entry corresponds to a time frame + for (auto en : *treereader) { + rawwriter.digitsToRaw(*digitbranch, *triggerbranch); + } + rawwriter.getWriter().writeConfFile("PHS", "RAWDATA", o2::utils::concat_string(outputdir, "/PHSraw.cfg")); +} diff --git a/Detectors/PHOS/simulation/src/RawWriter.cxx b/Detectors/PHOS/simulation/src/RawWriter.cxx new file mode 100644 index 0000000000000..f31c0832b2fdb --- /dev/null +++ b/Detectors/PHOS/simulation/src/RawWriter.cxx @@ -0,0 +1,343 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FairLogger.h" + +#include <fmt/core.h> +#include <gsl/span> +#include <TSystem.h> +#include "PHOSSimulation/RawWriter.h" +#include "PHOSBase/PHOSSimParams.h" +#include "CCDB/CcdbApi.h" + +using namespace o2::phos; + +void RawWriter::init() +{ + mRawWriter = std::make_unique<o2::raw::RawFileWriter>(o2::header::gDataOriginPHS, false); + mRawWriter->setCarryOverCallBack(this); + mRawWriter->setApplyCarryOverToLastPage(true); + + // initialize mapping + if (!mMapping) { + mMapping = std::make_unique<o2::phos::Mapping>(); + if (!mMapping) { + LOG(ERROR) << "Failed to initialize mapping"; + } + if (mMapping->setMapping() != o2::phos::Mapping::kOK) { + LOG(ERROR) << "Failed to construct mapping"; + } + } + + for (auto iddl = 0; iddl < o2::phos::Mapping::NDDL; iddl++) { + // For PHOS set + std::string rawfilename = mOutputLocation; + switch (mFileFor) { + case FileFor_t::kFullDet: + rawfilename += "/phos.raw"; + break; + case FileFor_t::kLink: + rawfilename += fmt::format("/phos_{:d}.raw", iddl); + } + short crorc, link; + mMapping->ddlToCrorcLink(iddl, crorc, link); + mRawWriter->registerLink(iddl, crorc, link, 0, rawfilename.data()); + } + + // initialize containers for SRU + for (auto isru = 0; isru < o2::phos::Mapping::NDDL; isru++) { + SRUDigitContainer srucont; + srucont.mSRUid = isru; + mSRUdata.push_back(srucont); + } +} + +void RawWriter::digitsToRaw(gsl::span<o2::phos::Digit> digitsbranch, gsl::span<o2::phos::TriggerRecord> triggerbranch) +{ + if (!mCalibParams) { + if (o2::phos::PHOSSimParams::Instance().mCCDBPath.compare("localtest") == 0) { + mCalibParams = std::make_unique<CalibParams>(1); // test default calibration + LOG(INFO) << "[RawWriter] No reading calibration from ccdb requested, set default"; + } else { + LOG(INFO) << "[RawWriter] getting calibration object from ccdb"; + o2::ccdb::CcdbApi ccdb; + std::map<std::string, std::string> metadata; + ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation + auto tr = triggerbranch.begin(); + double eventTime = -1; + // if(tr!=triggerbranch.end()){ + // eventTime = (*tr).getBCData().getTimeNS() ; + // } + // mCalibParams = ccdb.retrieveFromTFileAny<o2::phos::CalibParams>("PHOS/Calib", metadata, eventTime); + if (!mCalibParams) { + LOG(FATAL) << "[RawWriter] can not get calibration object from ccdb"; + } + } + } + + for (auto trg : triggerbranch) { + processTrigger(digitsbranch, trg); + } +} + +bool RawWriter::processTrigger(const gsl::span<o2::phos::Digit> digitsbranch, const o2::phos::TriggerRecord& trg) +{ + auto srucont = mSRUdata.begin(); + while (srucont != mSRUdata.end()) { + srucont->mChannels.clear(); + srucont++; + } + std::vector<o2::phos::Digit*>* digitsList; + for (auto& dig : gsl::span(digitsbranch.data() + trg.getFirstEntry(), trg.getNumberOfObjects())) { + short absId = dig.getAbsId(); + short ddl, hwAddrHG; + //get ddl and High Gain hw addresses + if (mMapping->absIdTohw(absId, 0, ddl, hwAddrHG) != o2::phos::Mapping::kOK) { + LOG(ERROR) << "Wrong AbsId" << absId; + } + + //Collect possible several digits (signal+pileup) into one map record + auto celldata = mSRUdata[ddl].mChannels.find(absId); + if (celldata == mSRUdata[ddl].mChannels.end()) { + const auto it = mSRUdata[ddl].mChannels.insert(celldata, {absId, std::vector<o2::phos::Digit*>()}); + it->second.push_back(&dig); + } else { + celldata->second.push_back(&dig); + } + } + + // Create and fill DMA pages for each channel + std::vector<uint32_t> rawbunches; + std::vector<char> payload; + std::vector<AltroBunch> rawbunchesHG, rawbunchesLG; + + for (srucont = mSRUdata.begin(); srucont != mSRUdata.end(); srucont++) { + short ddl = srucont->mSRUid; + payload.clear(); + + for (auto ch = srucont->mChannels.cbegin(); ch != srucont->mChannels.cend(); ch++) { + // Find out hardware address of the channel + bool isLGfilled = 0; + createRawBunches(ch->first, ch->second, rawbunchesHG, rawbunchesLG, isLGfilled); + + short hwAddrHG; //High gain always filled + if (mMapping->absIdTohw(ch->first, 0, ddl, hwAddrHG) != o2::phos::Mapping::kOK) { + LOG(ERROR) << "Wrong AbsId" << ch->first; + } + rawbunches.clear(); + for (auto& bunch : rawbunchesHG) { + rawbunches.push_back(bunch.mADCs.size() + 2); + rawbunches.push_back(bunch.mStarttime); + for (auto adc : bunch.mADCs) { + rawbunches.push_back(adc); + } + } + if (rawbunches.size() == 0) { + continue; + } + auto encodedbunches = encodeBunchData(rawbunches); + ChannelHeader chanhead = {0}; + chanhead.mHardwareAddress = hwAddrHG; + chanhead.mPayloadSize = rawbunches.size(); + chanhead.mMark = 1; //mark channel header + char* chanheadwords = reinterpret_cast<char*>(&chanhead.mDataWord); + for (int iword = 0; iword < sizeof(ChannelHeader) / sizeof(char); iword++) { + payload.emplace_back(chanheadwords[iword]); + } + + char* channelwords = reinterpret_cast<char*>(encodedbunches.data()); + for (auto iword = 0; iword < encodedbunches.size() * sizeof(int) / sizeof(char); iword++) { + payload.emplace_back(channelwords[iword]); + } + + if (isLGfilled) { //fill both HighGain, and LowGain channels in case of saturation + short hwAddrLG; //High gain always filled + if (mMapping->absIdTohw(ch->first, 1, ddl, hwAddrLG) != o2::phos::Mapping::kOK) { + LOG(ERROR) << "Wrong AbsId" << ch->first; + } + + rawbunches.clear(); + for (auto& bunch : rawbunchesLG) { + rawbunches.push_back(bunch.mADCs.size() + 2); + rawbunches.push_back(bunch.mStarttime); + for (auto adc : bunch.mADCs) { + rawbunches.push_back(adc); + } + } + + encodedbunches = encodeBunchData(rawbunches); + ChannelHeader chanheadLG = {0}; + chanheadLG.mHardwareAddress = hwAddrLG; + chanheadLG.mPayloadSize = rawbunches.size(); + chanheadLG.mMark = 1; //mark channel header + + chanheadwords = reinterpret_cast<char*>(&chanheadLG.mDataWord); + for (int iword = 0; iword < sizeof(ChannelHeader) / sizeof(char); iword++) { + payload.emplace_back(chanheadwords[iword]); + } + channelwords = reinterpret_cast<char*>(encodedbunches.data()); + for (auto iword = 0; iword < encodedbunches.size() * sizeof(int) / sizeof(char); iword++) { + payload.emplace_back(channelwords[iword]); + } + } + } + + // Create RCU trailer + auto trailerwords = createRCUTrailer(payload.size() / 4, 16, 16, 100., 0.); + for (auto word : trailerwords) { + payload.emplace_back(word); + } + + // register output data + LOG(DEBUG1) << "Adding payload with size " << payload.size() << " (" << payload.size() / 4 << " ALTRO words)"; + + short crorc, link; + mMapping->ddlToCrorcLink(ddl, crorc, link); + mRawWriter->addData(ddl, crorc, link, 0, trg.getBCData(), payload); + } + return true; +} + +void RawWriter::createRawBunches(short absId, const std::vector<o2::phos::Digit*>& channelDigits, std::vector<o2::phos::AltroBunch>& bunchHG, + std::vector<o2::phos::AltroBunch>& bunchLG, bool& isLGFilled) +{ + + isLGFilled = false; + short samples[kNPHOSSAMPLES] = {0}; + float hglgratio = mCalibParams->getHGLGRatio(absId); + for (auto dig : channelDigits) { + //Convert energy and time to ADC counts and time ticks + float ampADC = dig->getAmplitude(); // Digits amplitude already in ADC channels + if (ampADC > o2::phos::PHOSSimParams::Instance().mMCOverflow) { //High Gain in saturation, fill also Low Gain + isLGFilled = true; + } + float timeTicks = dig->getTime(); //time in ns + timeTicks /= o2::phos::PHOSSimParams::Instance().mTimeTick; //time in PHOS ticks + //Add to current sample contribution from digit + fillGamma2(ampADC, timeTicks, samples); + } + + //reduce samples below ZS and fill output + short zs = (short)o2::phos::PHOSSimParams::Instance().mZSthreshold; + bunchHG.clear(); + AltroBunch currentBunch; + //Note reverse time order + for (int i = kNPHOSSAMPLES; i--;) { + if (samples[i] > zs) { + currentBunch.mADCs.emplace_back(std::min(o2::phos::PHOSSimParams::Instance().mMCOverflow, samples[i])); + } else { //end of sample? + if (currentBunch.mADCs.size()) { + currentBunch.mStarttime = i + 1; + bunchHG.push_back(currentBunch); + currentBunch.mADCs.clear(); + } + } + } + if (currentBunch.mADCs.size()) { + bunchHG.push_back(currentBunch); + currentBunch.mADCs.clear(); + } + if (isLGFilled) { + bunchLG.clear(); + currentBunch.mADCs.clear(); + for (int i = kNPHOSSAMPLES; i--;) { + if (samples[i] > zs * hglgratio) { + currentBunch.mADCs.emplace_back(std::min(o2::phos::PHOSSimParams::Instance().mMCOverflow, short(samples[i] / hglgratio))); + } else { //end of sample? + if (currentBunch.mADCs.size()) { + currentBunch.mStarttime = i + 1; + bunchLG.push_back(currentBunch); + currentBunch.mADCs.clear(); + } + } + } + if (currentBunch.mADCs.size()) { + bunchLG.push_back(currentBunch); + } + } +} + +void RawWriter::fillGamma2(float amp, float time, short* samples) +{ + //Simulate Gamma2 signal added to current sample in PHOS + float alpha = o2::phos::PHOSSimParams::Instance().mSampleDecayTime; + amp += 0.5; //rounding err + for (int i = 0; i < kNPHOSSAMPLES; i++) { + if (i - o2::phos::PHOSSimParams::Instance().mPreSamples < time) { + continue; + } + float x = alpha * (i - o2::phos::PHOSSimParams::Instance().mPreSamples - time); + float y = 0.25 * amp * x * x * std::exp(2. - x); //0.25*exp(-2) normalization to unity + samples[i] += short(y); + } +} + +std::vector<uint32_t> RawWriter::encodeBunchData(const std::vector<uint32_t>& data) +{ + std::vector<uint32_t> encoded; + CaloBunchWord currentword; + currentword.mDataWord = 0; + int wordnumber = 0; + for (auto adc : data) { + switch (wordnumber) { + case 0: + currentword.mWord0 = adc; + break; + case 1: + currentword.mWord1 = adc; + break; + case 2: + currentword.mWord2 = adc; + break; + }; + wordnumber++; + if (wordnumber == 3) { + // start new word; + encoded.push_back(currentword.mDataWord); + currentword.mDataWord = 0; + wordnumber = 0; + } + } + if (wordnumber) { + encoded.push_back(currentword.mDataWord); + } + return encoded; +} + +std::vector<char> RawWriter::createRCUTrailer(int payloadsize, int feca, int fecb, double timesample, double l1phase) +{ + RCUTrailer trailer; + trailer.setActiveFECsA(feca); + trailer.setActiveFECsB(fecb); + trailer.setPayloadSize(payloadsize); + trailer.setL1Phase(l1phase); + trailer.setTimeSample(timesample); + auto trailerwords = trailer.encode(); + std::vector<char> encoded(trailerwords.size() * sizeof(uint32_t)); + memcpy(encoded.data(), trailerwords.data(), trailerwords.size() * sizeof(uint32_t)); + return encoded; +} + +int RawWriter::carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data, + const char* ptr, int maxSize, int splitID, + std::vector<char>& trailer, std::vector<char>& header) const +{ + + constexpr int phosTrailerSize = 36; + int offs = ptr - &data[0]; // offset wrt the head of the payload + assert(offs >= 0 && size_t(offs + maxSize) <= data.size()); // make sure ptr and end of the suggested block are within the payload + int leftBefore = data.size() - offs; // payload left before this splitting + int leftAfter = leftBefore - maxSize; // what would be left after the suggested splitting + int actualSize = maxSize; + if (leftAfter && leftAfter <= phosTrailerSize) { // avoid splitting the trailer or writing only it. + actualSize -= (phosTrailerSize - leftAfter) + 4; // (as we work with int, not char in decoding) + } + return actualSize; +} diff --git a/Detectors/PHOS/workflow/CMakeLists.txt b/Detectors/PHOS/workflow/CMakeLists.txt index 788527d945d91..7d19c6fabaf48 100644 --- a/Detectors/PHOS/workflow/CMakeLists.txt +++ b/Detectors/PHOS/workflow/CMakeLists.txt @@ -12,12 +12,26 @@ o2_add_library(PHOSWorkflow SOURCES src/RecoWorkflow.cxx src/PublisherSpec.cxx src/CellConverterSpec.cxx + src/RawToCellConverterSpec.cxx src/ClusterizerSpec.cxx src/DigitsPrinterSpec.cxx + src/RawWriterSpec.cxx + src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsPHOS - O2::DPLUtils O2::PHOSBase O2::PHOSCalib O2::PHOSSimulation O2::PHOSReconstruction O2::Algorithm) + O2::DPLUtils + O2::PHOSBase + O2::PHOSCalib + O2::PHOSSimulation + O2::PHOSReconstruction + O2::Algorithm) o2_add_executable(reco-workflow COMPONENT_NAME phos SOURCES src/phos-reco-workflow.cxx PUBLIC_LINK_LIBRARIES O2::PHOSWorkflow) + +o2_add_executable(entropy-encoder-workflow + COMPONENT_NAME phos + SOURCES src/entropy-encoder-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::PHOSWorkflow) diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/CellConverterSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/CellConverterSpec.h index b70718f019260..05f3892684125 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/CellConverterSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/CellConverterSpec.h @@ -75,7 +75,7 @@ class CellConverterSpec : public framework::Task std::vector<Cell> mOutputCells; ///< Container with output cells std::vector<TriggerRecord> mOutputCellTrigRecs; ///< Container with trigger records for output cells o2::dataformats::MCTruthContainer<MCLabel> mOutputTruthCont; ///< output MC labels - std::vector<uint> mOutputTruthMap; ///< output MC labels + std::vector<uint32_t> mOutputTruthMap; ///< output MC labels o2::phos::BadChannelMap* mBadMap = nullptr; ///< Bad channels map }; diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/ClusterizerSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/ClusterizerSpec.h index bd9e08a75049d..1268511c9c6e5 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/ClusterizerSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/ClusterizerSpec.h @@ -39,7 +39,7 @@ class ClusterizerSpec : public framework::Task { public: /// \brief Constructor - ClusterizerSpec(bool propagateMC) : framework::Task(), mPropagateMC(propagateMC) {} + ClusterizerSpec(bool propagateMC, bool scanDigits) : framework::Task(), mPropagateMC(propagateMC), mUseDigits(scanDigits) {} /// \brief Destructor ~ClusterizerSpec() override = default; @@ -57,7 +57,8 @@ class ClusterizerSpec : public framework::Task void run(framework::ProcessingContext& ctx) final; private: - bool mPropagateMC = false; ///< Switch whether to process MC true labels + bool mPropagateMC = false; ///< Switch whether to process MC true labels + bool mUseDigits = false; o2::phos::Clusterer mClusterizer; ///< Clusterizer object std::vector<o2::phos::Cluster> mOutputClusters; std::vector<o2::phos::TriggerRecord> mOutputClusterTrigRecs; diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..386a70bf596ce --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to PHOS digit/channels strean + +#ifndef O2_PHOS_ENTROPYDECODER_SPEC +#define O2_PHOS_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "PHOSReconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace phos +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::phos::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace phos +} // namespace o2 + +#endif diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..a61197a8a4ed6 --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert PHOS data to CTF (EncodedBlocks) + +#ifndef O2_PHOS_ENTROPYENCODER_SPEC +#define O2_PHOS_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include <TStopwatch.h> +#include "PHOSReconstruction/CTFCoder.h" + +namespace o2 +{ +namespace phos +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::phos::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace phos +} // namespace o2 + +#endif diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h new file mode 100644 index 0000000000000..9244c4fd339ca --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h @@ -0,0 +1,86 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DataFormatsPHOS/Cell.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "PHOSBase/Mapping.h" +#include "PHOSCalib/CalibParams.h" +#include "PHOSReconstruction/CaloRawFitter.h" +#include "PHOSReconstruction/RawReaderError.h" + +namespace o2 +{ + +namespace phos +{ + +namespace reco_workflow +{ + +/// \class RawToCellConverterSpec +/// \brief Coverter task for Raw data to PHOS cells +/// \author Dmitri Peresunko NRC KI +/// \since Sept., 2020 +/// +class RawToCellConverterSpec : public framework::Task +{ + public: + /// \brief Constructor + /// \param propagateMC If true the MCTruthContainer is propagated to the output + RawToCellConverterSpec() : framework::Task(){}; + + /// \brief Destructor + ~RawToCellConverterSpec() override = default; + + /// \brief Initializing the RawToCellConverterSpec + /// \param ctx Init context + void init(framework::InitContext& ctx) final; + + /// \brief Run conversion of raw data to cells + /// \param ctx Processing context + /// + /// The following branches are linked: + /// Input RawData: {"ROUT", "RAWDATA", 0, Lifetime::Timeframe} + /// Output cells: {"PHS", "CELLS", 0, Lifetime::Timeframe} + /// Output cells trigger record: {"PHS", "CELLSTR", 0, Lifetime::Timeframe} + /// Output HW errors: {"PHS", "RAWHWERRORS", 0, Lifetime::Timeframe} + void run(framework::ProcessingContext& ctx) final; + + protected: + /// \brief simple check of HW address + char CheckHWAddress(short ddl, short hwAddress, short& fee); + + private: + bool mFillChi2 = false; ///< Fill output with quality of samples + bool mCombineGHLG = true; ///< Combine or not HG and LG channels (def: combine, LED runs: not combine) + bool mPedestalRun = false; ///< Analyze pedestal run (calculate pedestal mean and RMS) + std::unique_ptr<Mapping> mMapping; ///< Mapping + std::unique_ptr<CalibParams> mCalibParams; ///!<! PHOS calibration + std::unique_ptr<CaloRawFitter> mRawFitter; ///!<! Raw fitter + std::vector<o2::phos::Cell> mOutputCells; ///< Container with output cells + std::vector<o2::phos::TriggerRecord> mOutputTriggerRecords; ///< Container with output cells + std::vector<o2::phos::RawReaderError> mOutputHWErrors; ///< Errors occured in reading data + std::vector<short> mOutputFitChi; ///< Raw sample fit quality +}; + +/// \brief Creating DataProcessorSpec for the PHOS Cell Converter Spec +/// +/// Refer to RawToCellConverterSpec::run for input and output specs +framework::DataProcessorSpec getRawToCellConverterSpec(); + +} // namespace reco_workflow + +} // namespace phos + +} // namespace o2 diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/RawWriterSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/RawWriterSpec.h new file mode 100644 index 0000000000000..2712177c38600 --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/RawWriterSpec.h @@ -0,0 +1,71 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> + +#include "Framework/DataProcessorSpec.h" +#include "PHOSSimulation/RawWriter.h" +#include "Framework/Task.h" + +namespace o2 +{ + +namespace phos +{ + +namespace reco_workflow +{ + +/// \class RawWriterSpec +/// \brief Coverter task for PHOS digits to raw +/// \author Dmitri Peresunko after Markus Fasel +/// \since Nov.20, 2020 +/// +/// Task converting a vector of PHOS digits to raw file +class RawWriterSpec : public framework::Task +{ + public: + /// \brief Constructor + /// \param propagateMC If true the MCTruthContainer is propagated to the output + RawWriterSpec() : framework::Task(){}; + + /// \brief Destructor + ~RawWriterSpec() override = default; + + /// \brief Initializing the RawWriterSpec + /// \param ctx Init context + void init(framework::InitContext& ctx) final; + + /// \brief Run conversion of digits to cells + /// \param ctx Processing context + /// + /// Converting the input vector of o2::phos::Digit to + /// file with raw data + /// + /// The following branches are linked: + /// Input digits: {"PHS", "DIGITS", 0, Lifetime::Timeframe} + /// Input digit trigger records: {"PHS", "DIGITTRS", 0, Lifetime::Timeframe} + void run(framework::ProcessingContext& ctx) final; + + private: + o2::phos::RawWriter* mRawWriter = nullptr; +}; + +/// \brief Creating DataProcessorSpec for the PHOS Cell Converter Spec +/// \param propagateMC If true the MC truth container is propagated to the output +/// +/// Refer to RawWriterSpec::run for input and output specs +framework::DataProcessorSpec getRawWriterSpec(); + +} // namespace reco_workflow + +} // namespace phos + +} // namespace o2 diff --git a/Detectors/PHOS/workflow/src/CellConverterSpec.cxx b/Detectors/PHOS/workflow/src/CellConverterSpec.cxx index 0eb8c1691771f..224f61b998a68 100644 --- a/Detectors/PHOS/workflow/src/CellConverterSpec.cxx +++ b/Detectors/PHOS/workflow/src/CellConverterSpec.cxx @@ -16,6 +16,7 @@ #include "Framework/ControlService.h" #include "DataFormatsPHOS/MCLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "CommonDataFormat/InteractionRecord.h" #include "PHOSBase/PHOSSimParams.h" #include "CCDB/CcdbApi.h" @@ -24,7 +25,7 @@ using namespace o2::phos::reco_workflow; void CellConverterSpec::init(framework::InitContext& ctx) { - LOG(DEBUG) << "[PHOSCellConverter - init] Initialize converter " << (mPropagateMC ? "with" : "without") << " MC truth container"; + LOG(INFO) << "[PHOSCellConverter - init] Initialize converter " << (mPropagateMC ? "with" : "without") << " MC truth container"; } void CellConverterSpec::run(framework::ProcessingContext& ctx) @@ -38,19 +39,19 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); return; } - LOG(INFO) << "Had Payload"; mOutputCells.clear(); mOutputCellTrigRecs.clear(); auto digits = ctx.inputs().get<std::vector<o2::phos::Digit>>("digits"); auto digitsTR = ctx.inputs().get<std::vector<o2::phos::TriggerRecord>>("digitTriggerRecords"); - auto truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::phos::MCLabel>*>("digitsmctr"); + std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::phos::MCLabel>> truthcont(nullptr); if (mPropagateMC) { + truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::phos::MCLabel>*>("digitsmctr"); mOutputTruthCont.clear(); mOutputTruthMap.clear(); } - LOG(INFO) << "[PHOSCellConverter - run] Received " << digits.size() << " digits and " << digitsTR.size() << " TriggerRecords"; + LOG(INFO) << "[PHOSCellConverter - run] Received " << digits.size() << " digits and " << digitsTR.size() << " TriggerRecords" << truthcont->getNElements() << " MC labels"; //Get TimeStamp from TriggerRecord if (!mBadMap) { @@ -86,17 +87,17 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) } ChannelType_t chantype; - if (dig.isHighGain()) + if (dig.isHighGain()) { chantype = ChannelType_t::HIGH_GAIN; - else + } else { chantype = ChannelType_t::LOW_GAIN; + } // TODO!!! TRU copying... // if (dig.getTRU()) // chantype = ChannelType_t::TRU; mOutputCells.emplace_back(dig.getAbsId(), dig.getAmplitude(), dig.getTime(), chantype); - icell++; if (mPropagateMC) { //copy MC info, int iLab = dig.getLabel(); if (iLab > -1) { @@ -105,18 +106,18 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) labelIndex++; } } + icell++; } mOutputCellTrigRecs.emplace_back(tr.getBCData(), indexStart, mOutputCells.size()); } - LOG(INFO) << "[PHOSCellConverter - run] Writing " << mOutputCells.size() << " cells and " << mOutputCellTrigRecs.size() << " Trig Records "; + LOG(INFO) << "[PHOSCellConverter - run] Writing " << mOutputCells.size() << " cells, " << mOutputCellTrigRecs.size() << " Trig Records " << mOutputTruthCont.getNElements() << " PHOS labels "; + ; ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", 0, o2::framework::Lifetime::Timeframe}, mOutputCells); ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputCellTrigRecs); if (mPropagateMC) { ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLSMCTR", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthCont); ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLSMCMAP", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthMap); } - LOG(INFO) << "Finished "; - ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); } o2::framework::DataProcessorSpec o2::phos::reco_workflow::getCellConverterSpec(bool propagateMC) diff --git a/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx b/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx index 5654e58927bc5..0ce66fc2d9928 100644 --- a/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx @@ -27,7 +27,8 @@ void ClusterizerSpec::init(framework::InitContext& ctx) void ClusterizerSpec::run(framework::ProcessingContext& ctx) { - if (ctx.inputs().isValid("digits")) { + printf("CellCLusterizerSpec: Run \n"); + if (mUseDigits) { LOG(DEBUG) << "PHOSClusterizer - run on digits called"; auto dataref = ctx.inputs().get("digits"); @@ -42,30 +43,46 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) // results in [7968:PHOSClusterizerSpec]: [20:51:44][ERROR] Exception caught: Inconsistent serialization method for extracting span auto digits = ctx.inputs().get<std::vector<o2::phos::Digit>>("digits"); auto digitsTR = ctx.inputs().get<std::vector<o2::phos::TriggerRecord>>("digitTriggerRecords"); - - // auto digitsTR = ctx.inputs().get<std::vector<o2::phos::TriggerRecord>>("digitTriggerRecords"); LOG(DEBUG) << "[PHOSClusterizer - run] Received " << digitsTR.size() << " TR, running clusterizer ..."; - auto truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::phos::MCLabel>*>("digitsmctr"); - mClusterizer.process(digits, digitsTR, truthcont.get(), &mOutputClusters, &mOutputClusterTrigRecs, &mOutputTruthCont); // Find clusters on digits (pass by ref) + // const o2::dataformats::MCTruthContainer<MCLabel>* truthcont=nullptr; + // if(mPropagateMC){ + // truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::phos::MCLabel>*>("digitsmctr"); + // } + // mClusterizer.process(digits, digitsTR, truthcont.get(), &mOutputClusters, &mOutputClusterTrigRecs, &mOutputTruthCont); // Find clusters on digits (pass by ref) } else { LOG(DEBUG) << "PHOSClusterizer - run run on cells called"; - auto dataref = ctx.inputs().get("cells"); - auto const* phosheader = o2::framework::DataRefUtils::getHeader<o2::phos::PHOSBlockHeader*>(dataref); - if (!phosheader->mHasPayload) { - LOG(DEBUG) << "[PHOSClusterizer - run] No more cells" << std::endl; - ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); - return; - } + // auto dataref = ctx.inputs().get("cells"); + // auto const* phosheader = o2::framework::DataRefUtils::getHeader<o2::phos::PHOSBlockHeader*>(dataref); + // if (!phosheader->mHasPayload) { + // LOG(DEBUG) << "[PHOSClusterizer - run] No more cells" << std::endl; + // ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); + // return; + // } auto cells = ctx.inputs().get<gsl::span<o2::phos::Cell>>("cells"); LOG(DEBUG) << "[PHOSClusterizer - run] Received " << cells.size() << " cells, running clusterizer ..."; auto cellsTR = ctx.inputs().get<gsl::span<o2::phos::TriggerRecord>>("cellTriggerRecords"); - auto truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::phos::MCLabel>*>("cellsmctr"); - auto truthmap = ctx.inputs().get<gsl::span<uint>>("cellssmcmap"); - mClusterizer.processCells(cells, cellsTR, truthcont.get(), truthmap, &mOutputClusters, &mOutputClusterTrigRecs, &mOutputTruthCont); // Find clusters on digits (pass by ref) + printf("-------------Cells------------\n"); + for (auto cTR : cellsTR) { + printf("new Event \n"); + int firstCellInEvent = cTR.getFirstEntry(); + int lastCellInEvent = firstCellInEvent + cTR.getNumberOfObjects(); + for (int i = firstCellInEvent; i < lastCellInEvent; i++) { + Cell c = cells[i]; + printf("cell[%d]: absId=%d, amp=%f, Time=%e \n", i, c.getAbsId(), c.getEnergy(), c.getTime()); + } + } + + // const o2::dataformats::MCTruthContainer<MCLabel>* truthcont=nullptr; + // gsl::span<const unsigned int> mcmap ; + // if(mPropagateMC){ + // truthcont = ctx.inputs().get<o2::dataformats::MCTruthContainer<o2::phos::MCLabel>*>("cellsmctr"); + // truthmap = ctx.inputs().get<gsl::span<uint>>("cellssmcmap"); + // } + // mClusterizer.processCells(cells, cellsTR, truthcont.get(), truthmap, &mOutputClusters, &mOutputClusterTrigRecs, &mOutputTruthCont); // Find clusters on digits (pass by ref) } LOG(DEBUG) << "[PHOSClusterizer - run] Writing " << mOutputClusters.size() << " clusters, " << mOutputClusterTrigRecs.size() << "TR and " << mOutputTruthCont.getIndexedSize() << " Labels"; @@ -96,7 +113,7 @@ o2::framework::DataProcessorSpec o2::phos::reco_workflow::getClusterizerSpec(boo return o2::framework::DataProcessorSpec{"PHOSClusterizerSpec", inputs, outputs, - o2::framework::adaptFromTask<o2::phos::reco_workflow::ClusterizerSpec>(propagateMC)}; + o2::framework::adaptFromTask<o2::phos::reco_workflow::ClusterizerSpec>(propagateMC, true)}; } o2::framework::DataProcessorSpec o2::phos::reco_workflow::getCellClusterizerSpec(bool propagateMC) @@ -107,8 +124,8 @@ o2::framework::DataProcessorSpec o2::phos::reco_workflow::getCellClusterizerSpec inputs.emplace_back("cells", o2::header::gDataOriginPHS, "CELLS", 0, o2::framework::Lifetime::Timeframe); inputs.emplace_back("cellTriggerRecords", o2::header::gDataOriginPHS, "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe); if (propagateMC) { - inputs.emplace_back("cellsmctr", "PHS", "CELLSMCTR", 0, o2::framework::Lifetime::Timeframe); - inputs.emplace_back("cellssmcmap", "PHS", "CELLSMCMAP", 0, o2::framework::Lifetime::Timeframe); + //inputs.emplace_back("cellsmctr", "PHS", "CELLSMCTR", 0, o2::framework::Lifetime::Timeframe); + //inputs.emplace_back("cellssmcmap", "PHS", "CELLSMCMAP", 0, o2::framework::Lifetime::Timeframe); } outputs.emplace_back("PHS", "CLUSTERS", 0, o2::framework::Lifetime::Timeframe); outputs.emplace_back("PHS", "CLUSTERTRIGRECS", 0, o2::framework::Lifetime::Timeframe); @@ -119,5 +136,5 @@ o2::framework::DataProcessorSpec o2::phos::reco_workflow::getCellClusterizerSpec return o2::framework::DataProcessorSpec{"PHOSClusterizerSpec", inputs, outputs, - o2::framework::adaptFromTask<o2::phos::reco_workflow::ClusterizerSpec>(propagateMC)}; + o2::framework::adaptFromTask<o2::phos::reco_workflow::ClusterizerSpec>(propagateMC, false)}; } diff --git a/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx b/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..0726b61363ab7 --- /dev/null +++ b/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "PHOSWorkflow/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace phos +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("phos-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& triggers = pc.outputs().make<std::vector<TriggerRecord>>(OutputRef{"triggers"}); + auto& cells = pc.outputs().make<std::vector<Cell>>(OutputRef{"cells"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::phos::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, triggers, cells); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << cells.size() << " PHOS cells in " << triggers.size() << " triggers in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "PHOS Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"triggers"}, "PHS", "CELLTRIGREC", 0, Lifetime::Timeframe}, + OutputSpec{{"cells"}, "PHS", "CELLS", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "phos-entropy-decoder", + Inputs{InputSpec{"ctf", "PHS", "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"phos-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace phos +} // namespace o2 diff --git a/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx b/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..0c5a4b8d6119c --- /dev/null +++ b/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "PHOSWorkflow/EntropyEncoderSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace phos +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("phos-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto triggers = pc.inputs().get<gsl::span<TriggerRecord>>("triggers"); + auto cells = pc.inputs().get<gsl::span<Cell>>("cells"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"PHS", "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, triggers, cells); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + // eeb->print(); + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for PHOS in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "PHOS Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("triggers", "PHS", "CELLTRIGREC", 0, Lifetime::Timeframe); + inputs.emplace_back("cells", "PHS", "CELLS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "phos-entropy-encoder", + inputs, + Outputs{{"PHS", "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"phos-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace phos +} // namespace o2 diff --git a/Detectors/PHOS/workflow/src/PublisherSpec.cxx b/Detectors/PHOS/workflow/src/PublisherSpec.cxx index 574a447552ab0..a170eb8aa73fd 100644 --- a/Detectors/PHOS/workflow/src/PublisherSpec.cxx +++ b/Detectors/PHOS/workflow/src/PublisherSpec.cxx @@ -163,7 +163,7 @@ o2::framework::DataProcessorSpec getPublisherSpec(PublisherConf const& config, b {createOutputSpecs()}, o2::framework::AlgorithmSpec(initFunction), o2::framework::Options{ - {"infile", o2::framework::VariantType::String, "", {"Name of the input file"}}, + {"infile", o2::framework::VariantType::String, "phosdigits.root", {"Name of the input file"}}, {"treename", o2::framework::VariantType::String, config.defaultTreeName.c_str(), {"Name of input tree"}}, {dtb.option.c_str(), o2::framework::VariantType::String, dtb.defval.c_str(), {dtb.help.c_str()}}, {dttrb.option.c_str(), o2::framework::VariantType::String, dttrb.defval.c_str(), {dttrb.help.c_str()}}, diff --git a/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx b/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx new file mode 100644 index 0000000000000..4951c4d8bcd48 --- /dev/null +++ b/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx @@ -0,0 +1,301 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <string> +#include "FairLogger.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "DataFormatsPHOS/PHOSBlockHeader.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "DetectorsRaw/RDHUtils.h" +#include "Framework/InputRecordWalker.h" +#include "CCDB/CcdbApi.h" +#include "PHOSBase/Mapping.h" +#include "PHOSBase/PHOSSimParams.h" +#include "PHOSReconstruction/Bunch.h" +#include "PHOSReconstruction/CaloRawFitter.h" +#include "PHOSReconstruction/AltroDecoder.h" +#include "PHOSReconstruction/RawDecodingError.h" +#include "PHOSWorkflow/RawToCellConverterSpec.h" + +using namespace o2::phos::reco_workflow; + +void RawToCellConverterSpec::init(framework::InitContext& ctx) +{ + LOG(DEBUG) << "Initialize converter "; + + auto path = ctx.options().get<std::string>("mappingpath"); + if (!mMapping) { + mMapping = std::unique_ptr<o2::phos::Mapping>(new o2::phos::Mapping(path)); + if (!mMapping) { + LOG(ERROR) << "Failed to initialize mapping"; + } + if (mMapping->setMapping() != o2::phos::Mapping::kOK) { + LOG(ERROR) << "Failed to construct mapping"; + } + } + + if (!mCalibParams) { + if (o2::phos::PHOSSimParams::Instance().mCCDBPath.compare("localtest") == 0) { + mCalibParams = std::make_unique<CalibParams>(1); // test default calibration + LOG(INFO) << "[RawToCellConverterSpec] No reading calibration from ccdb requested, set default"; + } else { + LOG(INFO) << "[RawToCellConverterSpec] getting calibration object from ccdb"; + o2::ccdb::CcdbApi ccdb; + std::map<std::string, std::string> metadata; + ccdb.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation + // auto tr = triggerbranch.begin(); + double eventTime = -1; + // if(tr!=triggerbranch.end()){ + // eventTime = (*tr).getBCData().getTimeNS() ; + // } + // mCalibParams = ccdb.retrieveFromTFileAny<o2::phos::CalibParams>("PHOS/Calib", metadata, eventTime); + if (!mCalibParams) { + LOG(FATAL) << "[RawToCellConverterSpec] can not get calibration object from ccdb"; + } + } + } + + auto fitmethod = ctx.options().get<std::string>("fitmethod"); + if (fitmethod == "default") { + LOG(INFO) << "Using default raw fitter"; + mRawFitter = std::unique_ptr<o2::phos::CaloRawFitter>(new o2::phos::CaloRawFitter); + //TODO: Configure parameters of fitter from options + // mRawFitter->setAmpCut(mNoiseThreshold); + // mRawFitter->setL1Phase(0.); + } + + mFillChi2 = (ctx.options().get<std::string>("fillchi2").compare("on") == 0); + if (mFillChi2) { + LOG(INFO) << "Fit quality output will be filled"; + } + + mCombineGHLG = (ctx.options().get<std::string>("keepGHLG").compare("on") != 0); + if (!mCombineGHLG) { + LOG(INFO) << "Both HighGain and LowGain will be kept"; + } + + mPedestalRun = (ctx.options().get<std::string>("pedestal").find("on") != std::string::npos); + if (mPedestalRun) { + mRawFitter->setPedestal(); + LOG(INFO) << "Pedestal run will be processed"; + } +} + +void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) +{ + // Cache cells from bunch crossings as the component reads timeframes from many links consecutively + std::map<o2::InteractionRecord, std::shared_ptr<std::vector<o2::phos::Cell>>> cellBuffer; // Internal cell buffer/ + int firstEntry = 0; + mOutputHWErrors.clear(); + if (mFillChi2) { + mOutputFitChi.clear(); + } + + for (const auto& rawData : framework::InputRecordWalker(ctx.inputs())) { + + o2::phos::RawReaderMemory rawreader(o2::framework::DataRefUtils::as<const char>(rawData)); + + // loop over all the DMA pages + while (rawreader.hasNext()) { + try { + rawreader.next(); + } catch (RawDecodingError::ErrorType_t e) { + LOG(ERROR) << "Raw decoding error " << (int)e; + //add error list + mOutputHWErrors.emplace_back(14, (int)e, 1); //Put general errors to non-existing DDL14 + //if problem in header, abandon this page + if (e == RawDecodingError::ErrorType_t::PAGE_NOTFOUND || + e == RawDecodingError::ErrorType_t::HEADER_DECODING || + e == RawDecodingError::ErrorType_t::HEADER_INVALID) { + break; + } + //if problem in payload, try to continue + continue; + } + auto& header = rawreader.getRawHeader(); + auto triggerBC = o2::raw::RDHUtils::getTriggerBC(header); + auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(header); + auto ddl = o2::raw::RDHUtils::getFEEID(header); + + o2::InteractionRecord currentIR(triggerBC, triggerOrbit); + std::shared_ptr<std::vector<o2::phos::Cell>> currentCellContainer; + auto found = cellBuffer.find(currentIR); + if (found == cellBuffer.end()) { + currentCellContainer = std::make_shared<std::vector<o2::phos::Cell>>(); + cellBuffer[currentIR] = currentCellContainer; + } else { + currentCellContainer = found->second; + } + if (ddl > o2::phos::Mapping::NDDL) { //only 14 correct DDLs + LOG(ERROR) << "DDL=" << ddl; + mOutputHWErrors.emplace_back(15, 16, char(ddl)); //Add non-existing DDL as DDL 15 + continue; //skip STU ddl + } + // use the altro decoder to decode the raw data, and extract the RCU trailer + o2::phos::AltroDecoder decoder(rawreader); + AltroDecoderError::ErrorType_t err = decoder.decode(); + + if (err != AltroDecoderError::kOK) { + //TODO handle severe errors + //TODO: probably careful conversion of decoder errors to Fitter errors? + char e = (char)err; + mOutputHWErrors.emplace_back(ddl, 16, e); //assign general header errors to non-existing FEE 16 + } + auto& rcu = decoder.getRCUTrailer(); + auto& channellist = decoder.getChannels(); + // Loop over all the channels for this RCU + for (auto& chan : channellist) { + short absId; + Mapping::CaloFlag caloFlag; + short fee; + char e2 = CheckHWAddress(ddl, chan.getHardwareAddress(), fee); + if (e2) { + mOutputHWErrors.emplace_back(ddl, fee, e2); + continue; + } + Mapping::ErrorStatus s = mMapping->hwToAbsId(ddl, chan.getHardwareAddress(), absId, caloFlag); + if (s != Mapping::ErrorStatus::kOK) { + mOutputHWErrors.emplace_back(ddl, 15, (char)s); //use non-existing FEE 15 for general errors + continue; + } + if (caloFlag != Mapping::kTRU) { //HighGain or LowGain + CaloRawFitter::FitStatus fitResults = mRawFitter->evaluate(chan.getBunches()); + if (fitResults == CaloRawFitter::FitStatus::kNoTime) { + mOutputHWErrors.emplace_back(ddl, fee, (char)5); //Time evaluation error occured + } + if (mFillChi2) { + for (int is = 0; is < mRawFitter->getNsamples(); is++) { + if (!mRawFitter->isOverflow(is)) { //Overflow is will show wrong chi2 + short chiAddr = absId; + chiAddr |= caloFlag << 14; + mOutputFitChi.emplace_back(chiAddr); + mOutputFitChi.emplace_back(short(mRawFitter->getChi2(is))); + } + } + } + if (fitResults == CaloRawFitter::FitStatus::kOK || fitResults == CaloRawFitter::FitStatus::kNoTime) { + //TODO: which results should be accepted? full configurable list + for (int is = 0; is < mRawFitter->getNsamples(); is++) { + if (caloFlag == Mapping::kHighGain && !mRawFitter->isOverflow(is)) { + currentCellContainer->emplace_back(absId, mRawFitter->getAmp(is), + mRawFitter->getTime(is) * o2::phos::PHOSSimParams::Instance().mTimeTick, (ChannelType_t)caloFlag); + } + if (caloFlag == Mapping::kLowGain) { + currentCellContainer->emplace_back(absId, mRawFitter->getAmp(is), + mRawFitter->getTime(is) * o2::phos::PHOSSimParams::Instance().mTimeTick, (ChannelType_t)caloFlag); + } + } + } + } + } + } //RawReader::hasNext + } + + // Loop over BCs, sort cells with increasing cell ID and write to output containers + mOutputCells.clear(); + mOutputTriggerRecords.clear(); + for (auto [bc, cells] : cellBuffer) { + int prevCellSize = mOutputCells.size(); + if (cells->size()) { + // Sort cells according to cell ID + std::sort(cells->begin(), cells->end(), [](o2::phos::Cell& lhs, o2::phos::Cell& rhs) { return lhs.getAbsId() < rhs.getAbsId(); }); + + if (mCombineGHLG && !mPedestalRun) { // combine for normal data, do not combine e.g. for LED run and pedestal + //Combine HG and LG sells + //Should be next to each other after sorting + auto it1 = cells->begin(); + auto it2 = cells->begin(); + it2++; + while (it1 != cells->end()) { + if (it2 != cells->end()) { + if ((*it1).getAbsId() == (*it2).getAbsId()) { //HG and LG channels, if both, copy only HG as more precise + if ((*it1).getType() == o2::phos::HIGH_GAIN) { + mOutputCells.push_back(*it1); + } else { + mOutputCells.push_back(*it2); + } + ++it1; //yes increase twice + ++it2; + } else { //no double cells, copy this one + mOutputCells.push_back(*it1); + } + } else { //just copy last one + mOutputCells.push_back(*it1); + } + ++it1; + ++it2; + } + } else { + for (auto cell : *cells) { + mOutputCells.push_back(cell); + } + } + } + + mOutputTriggerRecords.emplace_back(bc, mOutputCells.size(), mOutputCells.size() - prevCellSize); + } + cellBuffer.clear(); + + LOG(INFO) << "[PHOSRawToCellConverter - run] Writing " << mOutputCells.size() << " cells ..."; + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", 0, o2::framework::Lifetime::Timeframe}, mOutputCells); + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"PHS", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + if (mFillChi2) { + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLFITQA", 0, o2::framework::Lifetime::Timeframe}, mOutputFitChi); + } +} + +char RawToCellConverterSpec::CheckHWAddress(short ddl, short hwAddr, short& fee) +{ + + if (ddl < 0 || ddl > o2::phos::Mapping::NDDL) { + return (char)4; + } + // short chan = hwAddr & 0xf; + short chip = hwAddr >> 4 & 0x7; + short fec = hwAddr >> 7 & 0xf; + short branch = hwAddr >> 11 & 0x1; + + if (branch < 0 || branch > 1) { + return (char)1; + } + if (fec < 0 || fec > 15) { + return (char)2; + } + if (fec != 0 && (chip < 0 || chip > 4 || chip == 1)) { //Do not check for TRU (fec=0) + return (char)3; + } + return (char)0; +} + +o2::framework::DataProcessorSpec o2::phos::reco_workflow::getRawToCellConverterSpec() +{ + std::vector<o2::framework::InputSpec> inputs; + inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"PHS", "RAWDATA"}, o2::framework::Lifetime::Timeframe); + + std::vector<o2::framework::OutputSpec> outputs; + outputs.emplace_back("PHS", "CELLS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("PHS", "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("PHS", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("PHS", "CELLFITQA", 0, o2::framework::Lifetime::Timeframe); + + return o2::framework::DataProcessorSpec{"PHOSRawToCellConverterSpec", + inputs, // o2::framework::select("A:PHS/RAWDATA"), + outputs, + o2::framework::adaptFromTask<o2::phos::reco_workflow::RawToCellConverterSpec>(), + o2::framework::Options{ + {"fitmethod", o2::framework::VariantType::String, "default", {"Fit method (default or fast)"}}, + {"mappingpath", o2::framework::VariantType::String, "", {"Path to mapping files"}}, + {"fillchi2", o2::framework::VariantType::String, "off", {"Fill sample qualities on/off"}}, + {"keepGHLG", o2::framework::VariantType::String, "off", {"keep HighGain and Low Gain signals on/off"}}, + {"pedestal", o2::framework::VariantType::String, "off", {"Analyze as pedestal run on/off"}}}}; +} diff --git a/Detectors/PHOS/workflow/src/RawWriterSpec.cxx b/Detectors/PHOS/workflow/src/RawWriterSpec.cxx new file mode 100644 index 0000000000000..e26bdce955fed --- /dev/null +++ b/Detectors/PHOS/workflow/src/RawWriterSpec.cxx @@ -0,0 +1,70 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FairLogger.h" + +/// \file PHOS/Workflow/src/RawWriterSpec.cxx +/// \brief Digits to raw converter spec for PHOS +/// \author Dmitri Peresunko <Dmitri.Peresunko at cern.ch> +/// \date 20 Nov 2020 + +#include "PHOSWorkflow/RawWriterSpec.h" +#include "Framework/RootSerializationSupport.h" +#include "DataFormatsPHOS/Digit.h" +#include "Framework/ControlService.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "PHOSSimulation/RawWriter.h" +#include "DataFormatsPHOS/PHOSBlockHeader.h" +#include "Framework/ConfigParamRegistry.h" + +using namespace o2::phos::reco_workflow; + +void RawWriterSpec::init(framework::InitContext& ctx) +{ + + auto rawdir = ctx.options().get<std::string>("rawpath"); + + LOG(INFO) << "[PHOSRawWriter - init] Initialize raw writer "; + if (!mRawWriter) { + mRawWriter = new o2::phos::RawWriter(); + mRawWriter->setOutputLocation(rawdir.data()); + mRawWriter->init(); + } +} + +void RawWriterSpec::run(framework::ProcessingContext& ctx) +{ + LOG(DEBUG) << "[PHOSRawWriter - run] called"; + + auto digits = ctx.inputs().get<std::vector<o2::phos::Digit>>("digits"); + auto digitsTR = ctx.inputs().get<std::vector<o2::phos::TriggerRecord>>("digitTriggerRecords"); + LOG(INFO) << "[PHOSRawWriter - run] Received " << digits.size() << " digits and " << digitsTR.size() << " TriggerRecords"; + + mRawWriter->digitsToRaw(digits, digitsTR); + LOG(INFO) << "[PHOSRawWriter - run] Finished "; + + //flash and close output files + mRawWriter->getWriter().close(); + ctx.services().get<o2::framework::ControlService>().readyToQuit(framework::QuitRequest::Me); +} + +o2::framework::DataProcessorSpec o2::phos::reco_workflow::getRawWriterSpec() +{ + std::vector<o2::framework::InputSpec> inputs; + std::vector<o2::framework::OutputSpec> outputs; + inputs.emplace_back("digits", o2::header::gDataOriginPHS, "DIGITS", 0, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("digitTriggerRecords", o2::header::gDataOriginPHS, "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe); + return o2::framework::DataProcessorSpec{"PHOSRawWriterSpec", + inputs, + outputs, + o2::framework::adaptFromTask<o2::phos::reco_workflow::RawWriterSpec>(), + o2::framework::Options{ + {"rawpath", o2::framework::VariantType::String, "./", {"path to write raw"}}, + }}; +} diff --git a/Detectors/PHOS/workflow/src/RecoWorkflow.cxx b/Detectors/PHOS/workflow/src/RecoWorkflow.cxx index 0f337b0d95a93..c2c1cd9c43334 100644 --- a/Detectors/PHOS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/PHOS/workflow/src/RecoWorkflow.cxx @@ -26,7 +26,8 @@ #include "PHOSWorkflow/ClusterizerSpec.h" #include "PHOSWorkflow/DigitsPrinterSpec.h" #include "PHOSWorkflow/PublisherSpec.h" -//#include "PHOSWorkflow/RawWriterSpec.h" +#include "PHOSWorkflow/RawToCellConverterSpec.h" +#include "PHOSWorkflow/RawWriterSpec.h" #include "Framework/DataSpecUtils.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -83,53 +84,72 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, // specs.emplace_back(o2::phos::reco_workflow::getRawWriterSpec()); // } - if (inputType == InputType::Digits) { - specs.emplace_back(o2::phos::getPublisherSpec(PublisherConf{ - "phos-digit-reader", - "o2sim", - {"digitbranch", "PHOSDigit", "Digit branch"}, - {"digittrigger", "PHOSDigitTrigRecords", "TrigRecords branch"}, - {"mcbranch", "PHOSDigitMCTruth", "MC label branch"}, - {"mcmapbranch", "", "Dummy branch"}, - o2::framework::OutputSpec{"PHS", "DIGITS"}, - o2::framework::OutputSpec{"PHS", "DIGITTRIGREC"}, - o2::framework::OutputSpec{"PHS", "DIGITSMCTR"}, - o2::framework::OutputSpec{"PHS", ""}}, // it empty, do not create - propagateMC, false)); + if (inputType == InputType::Raw) { + //no explicit raw reader - if (enableDigitsPrinter) - specs.emplace_back(o2::phos::reco_workflow::getPhosDigitsPrinterSpec()); + if (isEnabled(OutputType::Cells)) { + specs.emplace_back(o2::phos::reco_workflow::getRawToCellConverterSpec()); + } + } + if (inputType == InputType::Digits) { if (isEnabled(OutputType::Cells)) { + specs.emplace_back(o2::phos::getPublisherSpec(PublisherConf{ + "phos-digit-reader", + "o2sim", + {"digitbranch", "PHOSDigit", "Digit branch"}, + {"digittrigger", "PHOSDigitTrigRecords", "TrigRecords branch"}, + {"mcbranch", "PHOSDigitMCTruth", "MC label branch"}, + {"mcmapbranch", "", "Dummy branch"}, + o2::framework::OutputSpec{"PHS", "DIGITS"}, + o2::framework::OutputSpec{"PHS", "DIGITTRIGREC"}, + o2::framework::OutputSpec{"PHS", "DIGITSMCTR"}, + o2::framework::OutputSpec{"PHS", ""}}, // it empty, do not create + propagateMC, false)); // add converter for cells specs.emplace_back(o2::phos::reco_workflow::getCellConverterSpec(propagateMC)); } if (isEnabled(OutputType::Clusters)) { + specs.emplace_back(o2::phos::getPublisherSpec(PublisherConf{ + "phos-digit-reader", + "o2sim", + {"digitbranch", "PHOSDigit", "Digit branch"}, + {"digittrigger", "PHOSDigitTrigRecords", "TrigRecords branch"}, + {"mcbranch", "PHOSDigitMCTruth", "MC label branch"}, + {"mcmapbranch", "", "Dummy branch"}, + o2::framework::OutputSpec{"PHS", "DIGITS"}, + o2::framework::OutputSpec{"PHS", "DIGITTRIGREC"}, + o2::framework::OutputSpec{"PHS", "DIGITSMCTR"}, + o2::framework::OutputSpec{"PHS", ""}}, // it empty, do not create + propagateMC, false)); // add clusterizer specs.emplace_back(o2::phos::reco_workflow::getClusterizerSpec(propagateMC)); } - // if (isEnabled(OutputType::Raw)) { - // // add Raw encoder - // specs.emplace_back(o2::phos::reco_workflow::getRawWriterSpec()); - // } + if (enableDigitsPrinter) { + specs.emplace_back(o2::phos::reco_workflow::getPhosDigitsPrinterSpec()); + } + + if (isEnabled(OutputType::Raw)) { + specs.emplace_back(o2::phos::getPublisherSpec(PublisherConf{ + "phos-digit-reader", + "o2sim", + {"digitbranch", "PHOSDigit", "Digit branch"}, + {"digittrigger", "PHOSDigitTrigRecords", "TrigRecords branch"}, + {"mcbranch", "PHOSDigitMCTruth", "Dummy branch"}, + {"mcmapbranch", "", "Dummy branch"}, + o2::framework::OutputSpec{"PHS", "DIGITS"}, + o2::framework::OutputSpec{"PHS", "DIGITTRIGREC"}, + o2::framework::OutputSpec{"PHS", "DIGITSMCTR"}, + o2::framework::OutputSpec{"PHS", ""}}, // it empty, do not create + false, false)); + // add Raw encoder + specs.emplace_back(o2::phos::reco_workflow::getRawWriterSpec()); + } } if (inputType == InputType::Cells) { - specs.emplace_back(o2::phos::getPublisherSpec(PublisherConf{ - "phos-cell-reader", - "o2sim", - {"cellbranch", "PHSCell", "Cells branch"}, - {"celltrigger", "PHSCellTR", "TrigRecords branch"}, - {"mcbranch", "PHSCellMCTruth", "MC label branch"}, - {"mcmapbranch", "PHSCellMCMap", "MC label map branch"}, - o2::framework::OutputSpec{"PHS", "CELLS"}, - o2::framework::OutputSpec{"PHS", "CELLTRIGREC"}, - o2::framework::OutputSpec{"PHS", "CELLSMCTR"}, - o2::framework::OutputSpec{"PHS", "CELLSMCMAP"}}, - propagateMC, true)); - if (isEnabled(OutputType::Clusters)) { // add clusterizer specs.emplace_back(o2::phos::reco_workflow::getCellClusterizerSpec(propagateMC)); @@ -155,55 +175,6 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, return std::make_tuple(o2::framework::MakeRootTreeWriterSpec::TerminationCondition::Action::DoProcessing, false); }; - // auto makeWriterSpec = [propagateMC, checkReady](const char* processName, - // const char* defaultFileName, - // const char* defaultTreeName, - // bool createMCMap, - // auto&& databranch, - // auto&& datatrbranch, - // auto&& mcbranch=nullptr, - // auto&& mcmapbranch=nullptr) { - // // depending on the MC propagation flag, the RootTreeWriter spec is created with two - // // or one branch definition - // if (propagateMC) { - // if(createMCMap){ - // return std::move(o2::framework::MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, - // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - // std::move(databranch), - // std::move(datatrbranch), - // std::move(mcbranch), - // std::move(mcmapbranch))); - // } - // else{ - // return std::move(o2::framework::MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, - // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - // std::move(databranch), - // std::move(datatrbranch), - // std::move(mcbranch))); - // } - // } - // else{ - // return std::move(o2::framework::MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, - // o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - // std::move(databranch), - // std::move(datatrbranch))); - // } - // }; - - // if (isEnabled(OutputType::Raw)) { - // using RawOutputType = std::vector<o2::phos::Raw>; - // using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::phos::MCLabel>; - // specs.push_back(makeWriterSpec("phos-raw-writer", - // inputType == InputType::Digits ? "phos-raw.root" : "phosrawcells.root", - // "o2sim", - // BranchDefinition<DigitOutputType>{o2::framework::InputSpec{"data", "PHS", "RAW", 0}, - // "PHSRaw", - // "raw-branch-name"}, - // BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "PHS", "RAWMCTR", 0}, - // "PHSRawMCTruth", - // "rawmc-branch-name"})()); - // } - if (isEnabled(OutputType::Digits)) { using DigitOutputType = std::vector<o2::phos::Digit>; using DTROutputType = std::vector<o2::phos::TriggerRecord>; @@ -223,26 +194,6 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, "digitmc-branch-name"})()); } - if (isEnabled(OutputType::Cells)) { - using CellOutputType = std::vector<o2::phos::Cell>; - using CTROutputType = std::vector<o2::phos::TriggerRecord>; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::phos::MCLabel>; - specs.emplace_back(o2::framework::MakeRootTreeWriterSpec("phos-cells-writer", "phoscells.root", "o2sim", -1, - o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, - BranchDefinition<CellOutputType>{o2::framework::InputSpec{"data", "PHS", "CELLS", 0}, - "PHSCell", - "cell-branch-name"}, - BranchDefinition<CTROutputType>{o2::framework::InputSpec{"data", "PHS", "CELLTRIGREC", 0}, - "PHSCellTR", - "celltr-branch-name"}, - BranchDefinition<MCLabelContainer>{o2::framework::InputSpec{"mc", "PHS", "CELLSMCTR", 0}, - "PHSCellMCTruth", - "cellmc-branch-name"}, - BranchDefinition<std::vector<uint>>{o2::framework::InputSpec{"mcmap", "PHS", "CELLSMCMAP", 0}, - "PHSCellMCMAP", - "cellmcmap-branch-name"})()); - } - if (isEnabled(OutputType::Clusters)) { specs.emplace_back(o2::framework::MakeRootTreeWriterSpec("phos-clusters-writer", "phosclusters.root", "o2sim", -1, o2::framework::MakeRootTreeWriterSpec::TerminationCondition{checkReady}, diff --git a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..e59400287359a --- /dev/null +++ b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "PHOSWorkflow/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::phos::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/Passive/src/Absorber.cxx b/Detectors/Passive/src/Absorber.cxx index b325b33b26063..959fea59a8449 100644 --- a/Detectors/Passive/src/Absorber.cxx +++ b/Detectors/Passive/src/Absorber.cxx @@ -40,8 +40,9 @@ Absorber::Absorber(const Absorber& rhs) = default; Absorber& Absorber::operator=(const Absorber& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment FairModule::operator=(rhs); diff --git a/Detectors/Passive/src/Cave.cxx b/Detectors/Passive/src/Cave.cxx index a7265703d2dbc..80f5a2be02cc7 100644 --- a/Detectors/Passive/src/Cave.cxx +++ b/Detectors/Passive/src/Cave.cxx @@ -115,8 +115,9 @@ Cave::Cave(const Cave& rhs) : FairDetector(rhs) {} Cave& Cave::operator=(const Cave& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment FairModule::operator=(rhs); diff --git a/Detectors/Passive/src/Compensator.cxx b/Detectors/Passive/src/Compensator.cxx index b917d6f84ade7..deba512284a5d 100644 --- a/Detectors/Passive/src/Compensator.cxx +++ b/Detectors/Passive/src/Compensator.cxx @@ -36,8 +36,9 @@ Compensator::Compensator(const Compensator& rhs) = default; Compensator& Compensator::operator=(const Compensator& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment PassiveBase::operator=(rhs); diff --git a/Detectors/Passive/src/Dipole.cxx b/Detectors/Passive/src/Dipole.cxx index 535e50ba13b0e..694cf1e1d8844 100644 --- a/Detectors/Passive/src/Dipole.cxx +++ b/Detectors/Passive/src/Dipole.cxx @@ -37,8 +37,9 @@ Dipole::Dipole(const Dipole& rhs) = default; Dipole& Dipole::operator=(const Dipole& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment PassiveBase::operator=(rhs); diff --git a/Detectors/Passive/src/FrameStructure.cxx b/Detectors/Passive/src/FrameStructure.cxx index 5eefcf02b962f..c9290e061b889 100644 --- a/Detectors/Passive/src/FrameStructure.cxx +++ b/Detectors/Passive/src/FrameStructure.cxx @@ -159,8 +159,9 @@ void FrameStructure::createWebFrame(const char* name, float dHz, float theta0, f float theta = TMath::Pi() / 2.; float phi = TMath::ACos(TMath::Cos(theta0) * TMath::Cos(phi0)); - if (phi0 < 0) + if (phi0 < 0) { phi = -phi; + } phi *= krad2deg; theta *= krad2deg; @@ -801,8 +802,9 @@ void FrameStructure::ConstructGeometry() char name[16]; // official module numbering int mod = i + 13; - if (mod > 17) + if (mod > 17) { mod -= 18; + } snprintf(name, 16, "BSEGMO%d", mod); // TGeoVolume* voTRD1 = new TGeoVolume(name, shCS, kMedAir); @@ -810,8 +812,9 @@ void FrameStructure::ConstructGeometry() // Place volume i float phi1 = i * 20.; float phi2 = 270. + phi1; - if (phi2 >= 360.) + if (phi2 >= 360.) { phi2 -= 360.; + } dx = TMath::Sin(phi1 * kdeg2rad) * r; dy = -TMath::Cos(phi1 * kdeg2rad) * r; @@ -1041,28 +1044,36 @@ void FrameStructure::ConstructGeometry() jmod = isec_1[index]; float dz1 = dz + 3. + (zpos - 4.); dx0 = (hR + dz0 + zpos - 4.) * tan10 - (longW / 2. + 0.2) / cos10 - 0.05; - if (jmod != 5) + if (jmod != 5) { vmc->Gspos("BTRDR_10", 2 * jmod + 1, module[jmod], dx0, 0.0, dz1, idrotm[2096], "ONLY"); - if (jmod != 13) + } + if (jmod != 13) { vmc->Gspos("BTRDR_10", 2 * jmod + 2, module[jmod], -dx0, 0.0, dz1, idrotm[2086], "ONLY"); + } dx0 -= 0.5; - if (jmod != 5) + if (jmod != 5) { vmc->Gspos("BTRDR_12", 2 * jmod + 1, module[jmod], dx0, 0.0, dz1, idrotm[2096], "ONLY"); - if (jmod != 13) + } + if (jmod != 13) { vmc->Gspos("BTRDR_12", 2 * jmod + 2, module[jmod], -dx0, 0.0, dz1, idrotm[2087], "ONLY"); + } dz1 += (4 - 0.2); dz1 += dext; dx0 = (hR + dz0 + zpos - 0.2) * tan10 - (longW / 2. + 3. + 0.4) / cos10; - if (jmod != 5) + if (jmod != 5) { vmc->Gspos("BTRDR_11", 2 * jmod + 1, module[jmod], dx0, 0.0, dz1, 0, "ONLY"); - if (jmod != 13) + } + if (jmod != 13) { vmc->Gspos("BTRDR_11", 2 * jmod + 2, module[jmod], -dx0, 0.0, dz1, 0, "ONLY"); + } dz1 -= 0.3; dx0 -= 0.5; - if (jmod != 5) + if (jmod != 5) { vmc->Gspos("BTRDR_14", 2 * jmod + 1, module[jmod], dx0, 0.0, dz1, 0, "ONLY"); - if (jmod != 13) + } + if (jmod != 13) { vmc->Gspos("BTRDR_14", 2 * jmod + 2, module[jmod], -dx0, 0.0, dz1, 0, "ONLY"); + } } // Pos 2 @@ -1116,10 +1127,12 @@ void FrameStructure::ConstructGeometry() for (int index = 0; index < 9; index++) { jmod = isec_3[index]; - if (index > 1) + if (index > 1) { vmc->Gspos("BTRDR_3", 2 * jmod + 1, module[jmod], 50.96 - 5 - 2., 0.0, dz + 3.7, 0, "ONLY"); - if (index < 7) + } + if (index < 7) { vmc->Gspos("BTRDR_3", 2 * jmod + 2, module[jmod], -50.96 + 5 + 2., 0.0, dz + 3.7, 0, "ONLY"); + } } // // Fixation Blocks with tie anchors @@ -1234,29 +1247,37 @@ void FrameStructure::ConstructGeometry() for (int index = 0; index < 11; index++) { int imod = isec_1[index]; - if (imod != 5) + if (imod != 5) { vmc->Gspos("BTRD_FBAS2", index, module[imod], dx, -yFB1, zTA1, idrotm[2097], "ONLY"); + } - if (imod != 13) + if (imod != 13) { vmc->Gspos("BTRD_FBAS1", 11 + index, module[imod], -dx, -yFB1, zTA1, idrotm[2087], "ONLY"); + } - if (imod != 5) + if (imod != 5) { vmc->Gspos("BTRD_FBAS1", 22 + index, module[imod], dx, yFB1, zTA1, idrotm[2096], "ONLY"); + } - if (imod != 13) + if (imod != 13) { vmc->Gspos("BTRD_FBAS2", 33 + index, module[imod], -dx, yFB1, zTA1, idrotm[2086], "ONLY"); + } - if (imod != 5) + if (imod != 5) { vmc->Gspos("BTRD_FBAS4", index, module[imod], dx, -yFB2, zTA1, idrotm[2097], "ONLY"); + } - if (imod != 13) + if (imod != 13) { vmc->Gspos("BTRD_FBAS3", 11 + index, module[imod], -dx, -yFB2, zTA1, idrotm[2087], "ONLY"); + } - if (imod != 5) + if (imod != 5) { vmc->Gspos("BTRD_FBAS3", 22 + index, module[imod], dx, yFB2, zTA1, idrotm[2096], "ONLY"); + } - if (imod != 13) + if (imod != 13) { vmc->Gspos("BTRD_FBAS4", 33 + index, module[imod], -dx, yFB2, zTA1, idrotm[2086], "ONLY"); + } } // @@ -1307,8 +1328,9 @@ void FrameStructure::ConstructGeometry() yxtru1[6] = -6.0; double xxtru2[7]; - for (i = 0; i < 7; i++) + for (i = 0; i < 7; i++) { xxtru2[i] = -xxtru1[i]; + } double xxtru3[5]; double yxtru3[5]; @@ -1319,8 +1341,9 @@ void FrameStructure::ConstructGeometry() } xxtru3[4] = xxtru1[6]; yxtru3[4] = yxtru1[6]; - for (i = 0; i < 5; i++) + for (i = 0; i < 5; i++) { xxtru4[i] = -xxtru3[i]; + } shBTOFS2->DefinePolygon(7, xxtru1, yxtru1); shBTOFS2->DefineSection(0, -4.); @@ -1376,8 +1399,9 @@ void FrameStructure::ConstructGeometry() asTOFS01->SetVisibility(1); for (i = 0; i < 18; i++) { - if (i >= 4 && i <= 8) + if (i >= 4 && i <= 8) { continue; + } float phi1 = i * 20.; float phi2 = 270. + phi1; rot1 = new TGeoRotation(Form("TOFS_R1_%d", i), 90.0, phi1, 90., phi2, 0., 0.); diff --git a/Detectors/Passive/src/Hall.cxx b/Detectors/Passive/src/Hall.cxx index 7980ba2188887..316488a8c6a8a 100644 --- a/Detectors/Passive/src/Hall.cxx +++ b/Detectors/Passive/src/Hall.cxx @@ -32,8 +32,9 @@ Hall::Hall(const Hall& rhs) = default; Hall& Hall::operator=(const Hall& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment PassiveBase::operator=(rhs); diff --git a/Detectors/Passive/src/Magnet.cxx b/Detectors/Passive/src/Magnet.cxx index 6616f9490870b..13faacbcbb08b 100644 --- a/Detectors/Passive/src/Magnet.cxx +++ b/Detectors/Passive/src/Magnet.cxx @@ -46,8 +46,9 @@ Magnet::Magnet(const Magnet& rhs) = default; Magnet& Magnet::operator=(const Magnet& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment PassiveBase::operator=(rhs); diff --git a/Detectors/Passive/src/Pipe.cxx b/Detectors/Passive/src/Pipe.cxx index e637046f008bb..57d4e0678540e 100644 --- a/Detectors/Passive/src/Pipe.cxx +++ b/Detectors/Passive/src/Pipe.cxx @@ -51,8 +51,9 @@ Pipe::Pipe(const Pipe& rhs) = default; Pipe& Pipe::operator=(const Pipe& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment PassiveBase::operator=(rhs); @@ -2730,8 +2731,9 @@ TGeoPcon* Pipe::MakeMotherFromTemplate(const TGeoPcon* shape, Int_t imin, Int_t // Int_t nz0 = shape->GetNz(); // if nz > -1 the number of planes is given by nz - if (nz != -1) + if (nz != -1) { nz0 = nz; + } TGeoPcon* mother = new TGeoPcon(0., 360., nz0); if (imin == -1 || imax == -1) { @@ -2760,8 +2762,9 @@ TGeoPcon* Pipe::MakeMotherFromTemplate(const TGeoPcon* shape, Int_t imin, Int_t double zlast, rminlast, rmaxlast; for (Int_t i = 0; i < shape->GetNz(); i++) { Double_t rmin = shape->GetRmin(i); - if ((i >= imin) && (i <= imax)) + if ((i >= imin) && (i <= imax)) { rmin = r0; + } Double_t rmax = shape->GetRmax(i); Double_t z = shape->GetZ(i); if (i == 0 || (z != zlast || rmin != rminlast || rmax != rmaxlast)) { @@ -2776,9 +2779,11 @@ TGeoPcon* Pipe::MakeMotherFromTemplate(const TGeoPcon* shape, Int_t imin, Int_t pconparams[2] = zplanecounter; // reinit polycon from parameters mother->SetDimensions(pconparams.data()); - } else - for (Int_t i = 0; i < zplanecounter; i++) + } else { + for (Int_t i = 0; i < zplanecounter; i++) { mother->DefineSection(i, pconparams[3 + 3 * i], pconparams[4 + 3 * i], pconparams[5 + 3 * i]); + } + } return mother; } diff --git a/Detectors/Passive/src/Shil.cxx b/Detectors/Passive/src/Shil.cxx index d1bd18505cc28..d8dc7b2401396 100644 --- a/Detectors/Passive/src/Shil.cxx +++ b/Detectors/Passive/src/Shil.cxx @@ -36,8 +36,9 @@ Shil::Shil(const Shil& rhs) = default; Shil& Shil::operator=(const Shil& rhs) { // self assignment - if (this == &rhs) + if (this == &rhs) { return *this; + } // base class assignment PassiveBase::operator=(rhs); @@ -85,8 +86,9 @@ TGeoPcon* MakeShapeFromTemplate(const TGeoPcon* pcon, Float_t drMin, Float_t drM // Int_t nz = pcon->GetNz(); TGeoPcon* cpcon = new TGeoPcon(0., 360., nz); - for (Int_t i = 0; i < nz; i++) + for (Int_t i = 0; i < nz; i++) { cpcon->DefineSection(i, pcon->GetZ(i), pcon->GetRmin(i) + drMin, pcon->GetRmax(i) + drMax); + } return cpcon; } } // namespace @@ -800,10 +802,10 @@ void Shil::ConstructGeometry() dz += saa1Wring2HWidth; dz += saa1DWrings; dz += saa1Wring3HWidth; - asSaa1ExtraShield->AddNode(voSaa1Wring3, 1, new TGeoCombiTrans(0., 0., dz - 0.15, rot090)); - asSaa1ExtraShield->AddNode(voSaa1Wring3, 2, new TGeoCombiTrans(0., 0., dz - 0.15, rot270)); + asSaa1ExtraShield->AddNode(voSaa1Wring3, 1, new TGeoCombiTrans(0., 0., dz, rot090)); + asSaa1ExtraShield->AddNode(voSaa1Wring3, 2, new TGeoCombiTrans(0., 0., dz, rot270)); dz += saa1Wring3HWidth; - asSaa1ExtraShield->AddNode(voSaa1Wring4, 1, new TGeoTranslation(0., 0., dz - 6.)); + asSaa1ExtraShield->AddNode(voSaa1Wring4, 1, new TGeoTranslation(0., 0., dz)); dz += saa1Wring4Width; const Float_t saa1ExtraShieldL = 48; // @@ -1273,23 +1275,26 @@ void Shil::ConstructGeometry() // FA Tail Section for (Int_t iz = 1; iz < 9; iz++) { z = shFaWTail->GetZ(iz - 1); - if (iz == 8) + if (iz == 8) { z -= ozFaSaa1; + } shYOUT1->DefineSection(iz - 1, z + ziFaWTail, shFaWTail->GetRmax(iz - 1) + eps, 150.); } // FA-SAA1 Joint z = shYOUT1->GetZ(7); Int_t izc = 8; for (Int_t iz = 9; iz < 17; iz++) { - if (iz == 11 || iz == 15) + if (iz == 11 || iz == 15) { continue; + } shYOUT1->DefineSection(izc, z + shFaSaa1->GetZ(iz - 9), shFaSaa1->GetRmax(iz - 9) + eps, 150.); izc++; } z = shYOUT1->GetZ(13) - ozSaa1; // SAA1 - Dipole - for (Int_t iz = 15; iz < 22; iz++) + for (Int_t iz = 15; iz < 22; iz++) { shYOUT1->DefineSection(iz - 1, z + shSaa1M->GetZ(iz - 11), shSaa1M->GetRmax(iz - 11) + eps, 150.); + } // Distance between dipole and start of SAA1 2deg opening cone dz = ziDipole - (zSaa1StEnv[0] - dSt + zSaa1StEnvS + ziSaa1); rOut = rOuSaa1StEnv2 + dz * TMath::Tan(2. * kDegRad); diff --git a/Detectors/README.md b/Detectors/README.md index 3be3ed677ae60..b3d4bdbfc8e83 100644 --- a/Detectors/README.md +++ b/Detectors/README.md @@ -18,7 +18,9 @@ This module contains the following submodules: * \subpage refDetectorsCalibration * \subpage refDetectorsCPV * \subpage refDetectorsCTF +* \subpage refDetectorsDCS * \subpage refDetectorsEMCAL +* \subpage refDetectorsDCS * \subpage refDetectorsFIT * \subpage refDetectorsHMPID * \subpage refDetectorsITSMFT @@ -28,5 +30,6 @@ This module contains the following submodules: * \subpage refDetectorsTOF * \subpage refDetectorsTPC * \subpage refDetectorsTRD +* \subpage refDetectorsUpgrades * \subpage refDetectorsZDC /doxy --> diff --git a/Detectors/Raw/CMakeLists.txt b/Detectors/Raw/CMakeLists.txt index cda1bf6fd01a1..2c3e0258c8147 100644 --- a/Detectors/Raw/CMakeLists.txt +++ b/Detectors/Raw/CMakeLists.txt @@ -35,12 +35,18 @@ o2_add_executable(file-check PUBLIC_LINK_LIBRARIES O2::DetectorsRaw Boost::program_options) +o2_add_executable(file-split + COMPONENT_NAME raw + SOURCES src/rawfileSplit.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsRaw) + o2_add_executable(file-reader-workflow COMPONENT_NAME raw SOURCES src/rawfile-reader-workflow.cxx src/RawFileReaderWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsRaw) + o2_add_test(HBFUtils PUBLIC_LINK_LIBRARIES O2::DetectorsRaw O2::Steer diff --git a/Detectors/Raw/README.md b/Detectors/Raw/README.md index dfd756fc048b6..13b744a92dc78 100644 --- a/Detectors/Raw/README.md +++ b/Detectors/Raw/README.md @@ -125,12 +125,18 @@ header : what it wants to add right after the RDH of the new CRU page before th the payload (starting at ptr+actualSize) will be written ``` -The method mast return actual size of the bloc which can be written (`<=maxSize`). +The method must return actual size of the bloc which can be written (`<=maxSize`). If this method populates the trailer, it must ensure that it returns the actual size such that `actualSize + trailer.size() <= maxSize`. -In case returned `actualSize` is 0, current CRU page will be closed w/o adding anything, and new +In case returned `actualSize` is 0, current CRU page will be closed just with user trailer added (if any) and new query of this method will be done on the new CRU page. +By default, the carry-over callback is not called if remaining data fits to the free space of the 8KB page (or the super-page). +In case the splitting affects the information written in the payload trailer, user may set `writer.setApplyCarryOverToLastPage(true)`. +With this flag set to `ON`, if there was at least one splitting for the user payload provided to `addData` method, then the carry-over +method will be called also for the very last chunk (which by definition does not need splitting) and the supplied trailer will overwrite +the tail of the this chunk instead of adding it incrementally. + Additionally, in case detector wants to add some information between `empty` HBF packet's opening and closing RDHs (they will be added automatically using the HBFUtils functionality for all HBFs w/o data between the orbit 0 and last orbit of the TF seen in the simulations), it may implement a callback method @@ -143,10 +149,16 @@ empty HBF the writer will call it with rdh : RDH of the CRU page opening empty RDH toAdd : a vector (supplied empty) to be filled to a size multipe of 16 bytes ``` +The data `toAdd` will be inserted between the star/stop RDHs of the empty HBF. Adding empty HBF pages for HB's w/o data can be avoided by setting `writer.setDontFillEmptyHBF(true)` before starting conversion. Note that the empty HBFs still will be added for HBs which are supposed to open a new TF. -The data `toAdd` will be inserted between the star/stop RDHs of the empty HBF. +Some detectors (ITS/MFT) write a special header word after the RDH of every new CRU page (actually, different GBT words for pages w/o and with ``RDH.stop``) in non-empty HBFs. This can be achieved by +another call back method +```cpp +void newRDHMethod(const RDHAny* rdh, bool prevEmpty, std::vector<char>& toAdd) const; +``` +It proveds the ``RDH`` of the page for which it is called, information if the previous had some payload and buffer to be filled by the used algorithm. The behaviour described above can be modified by providing an extra argument in the `addData` method ```cpp @@ -230,6 +242,8 @@ dataOrigin = ITS #optional, if missing then default is used dataDescription = RAWDATA filePath = path_and_name_of_the_data_file0 +# for CRU detectors the "readoutCard" record below is optional +# readoutCard = CRU [input-1] dataOrigin = TPC @@ -238,6 +252,14 @@ filePath = path_and_name_of_the_data_file1 [input-2] filePath = path_and_name_of_the_data_file2 +[input-1-RORC] +dataOrigin = EMC +filePath = path_and_name_of_the_data_fileX +# for RORC detectors the record below is obligatory +readoutCard = RORC + + + #... # [input-XXX] blocks w/o filePath will be ignoder, XXX is irrelevant @@ -290,9 +312,13 @@ o2-raw-file-reader-workflow --loop arg (=1) loop N times (infinite for N<0) --min-tf arg (=0) min TF ID to process --max-tf arg (=4294967295) max TF ID to process - --message-per-tf send TF of each link as a single FMQ message rather than multipart with message per HB - --output-per-link send message per Link rather than per FMQ output route --delay arg (=0) delay in seconds between consecutive TFs sending + --buffer-size arg (=1048576) buffer size for files preprocessing + --super-page-size arg (=1048576) super-page size for FMQ parts definition + --part-per-hbf FMQ parts per superpage (default) of HBF + --raw-channel-config arg optional raw FMQ channel for non-DPL output + --cache-data cache data at 1st reading, may require excessive memory!!! + --detect-tf0 autodetect HBFUtils start Orbit/BC from 1st TF seen --configKeyValues arg semicolon separated key=value strings # to suppress various error checks / reporting @@ -311,19 +337,26 @@ The workflow takes an input from the configuration file (as described in `RawFil with the `OutputSpec`s indicated in the configuration file (or defaults). Each link data gets `SubSpecification` according to DataDistribution scheme. -If `--loop` argument is provided, data will be re-played in loop. The delay (in seconds) can be added between sensding of consecutive TFs to avoid pile-up of TFs. - -At every invocation of the device `processing` callback a full TimeFrame for every link will be added as N-HBFs parts (one for each HBF in the TF) to the multipart -relayed by the `FairMQ` channel. -In case the `--message-per-tf` option is asked, the whole TF is sent as the only part of the `FairMQPart`. +If `--loop` argument is provided, data will be re-played in loop. The delay (in seconds) can be added between sensding of consecutive TFs to avoid pile-up of TFs. By default at each iteration the data will be again read from the disk. +Using `--cache-data` option one can force caching the data to memory during the 1st reading, this avoiding disk I/O for following iterations, but this option should be used with care as it will eventually create a memory copy of all TFs to read. -Instead of sending a single output (for multiple links) per output route (which means their data will be received together) one can request sending an output per link -by using option `--output-per-link`. +At every invocation of the device `processing` callback a full TimeFrame for every link will be added as a multi-part `FairMQ` message and relayed by the relevant channel. +By default each part will be a single CRU super-page of the link. This behaviour can be changed by providing `part-per-hbf` option, in which case each HBF will be added as a separate HBF. The standard use case of this workflow is to provide the input for other worfklows using the piping, e.g. ```cpp o2-raw-file-reader-workflow --input-conf myConf.cfg | o2-dpl-raw-parser ``` +Option `--raw-channel-config <confstring> forces the reader to send all data (single FairMQParts containing the whole TF) to raw FairMQ channel, emulating the messages from the DataDistribution. +To inject such a data to DPL one should use a parallel process starting with `o2-dpl-raw-proxy`. An example (note `--session default` added to every executable): + +```bash +[Terminal 1]> o2-dpl-raw-proxy --session default -b --dataspec "A:TOF/RAWDATA;B:ITS/RAWDATA;C:MFT/RAWDATA;D:TPC/RAWDATA;E:FT0/RAWDATA" --channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@rr-to-dpl,transport=shmem,rateLogging=1" | o2-dpl-raw-parser --session default --input-spec "A:TOF/RAWDATA;B:ITS/RAWDATA;C:MFT/RAWDATA;D:TPC/RAWDATA;E:FT0/RAWDATA" +``` + +```bash +[Terminal 2]> o2-raw-file-reader-workflow --session default --loop 1000 --delay 3 --input-conf raw/rawAll.cfg --raw-channel-config "name=raw-reader,type=push,method=bind,address=ipc://@rr-to-dpl,transport=shmem,rateLogging=1" --shm-segment-size 16000000000 +``` ## Raw data file checker (standalone executable) @@ -335,7 +368,9 @@ Options: -m [ --max-tf] arg (=0xffffffff) max. TF ID to read (counts from 0) -v [ --verbosity ] arg (=0) 1: long report, 2 or 3: print or dump all RDH -s [ --spsize ] arg (=1048576) nominal super-page size in bytes - -t [ --hbfpertf ] arg (=256) nominal number of HBFs per TF + --detect-tf0 autodetect HBFUtils start Orbit/BC from 1st TF seen + --rorc impose RORC as default detector mode + --configKeyValues arg semicolon separated key=value strings --nocheck-packet-increment ignore /Wrong RDH.packetCounter increment/ --nocheck-page-increment ignore /Wrong RDH.pageCnt increment/ --check-stop-on-page0 check /RDH.stop set of 1st HBF page/ diff --git a/Detectors/Raw/include/DetectorsRaw/HBFUtils.h b/Detectors/Raw/include/DetectorsRaw/HBFUtils.h index a1ca2fa2e5aa0..6bfc6c097509d 100644 --- a/Detectors/Raw/include/DetectorsRaw/HBFUtils.h +++ b/Detectors/Raw/include/DetectorsRaw/HBFUtils.h @@ -54,10 +54,10 @@ struct HBFUtils : public o2::conf::ConfigurableParamHelper<HBFUtils> { IR getIRTF(uint32_t tf) const { return getIRHBF(tf * nHBFPerTF); } ///< get HBF ID corresponding to this IR - int64_t getHBF(const IR& rec) const; + uint32_t getHBF(const IR& rec) const; ///< get TF ID corresponding to this IR - int64_t getTF(const IR& rec) const { return getHBF(rec) / nHBFPerTF; } + uint32_t getTF(const IR& rec) const { return getHBF(rec) / nHBFPerTF; } ///< get TF and HB (within TF) for this IR std::pair<int, int> getTFandHBinTF(const IR& rec) const @@ -66,6 +66,9 @@ struct HBFUtils : public o2::conf::ConfigurableParamHelper<HBFUtils> { return std::pair<int, int>(hbf / nHBFPerTF, hbf % nHBFPerTF); } + ///< get 1st IR of the TF corresponding to provided interaction record + IR getFirstIRofTF(const IR& rec) const { return getIRTF(getTF(rec)); } + ///< get TF and HB (abs) for this IR std::pair<int, int> getTFandHB(const IR& rec) const { diff --git a/Detectors/Raw/include/DetectorsRaw/RDHUtils.h b/Detectors/Raw/include/DetectorsRaw/RDHUtils.h index 1d1a6df6abe40..181ce7c5aca5c 100644 --- a/Detectors/Raw/include/DetectorsRaw/RDHUtils.h +++ b/Detectors/Raw/include/DetectorsRaw/RDHUtils.h @@ -55,7 +55,7 @@ struct RDHUtils { template <typename H> static constexpr int getVersion() { -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE RDHAny::sanityCheckStrict<H>(); if (std::is_same<H, RDHv6>::value) { return 6; @@ -68,7 +68,7 @@ struct RDHUtils { } #else return -1; // dummy value as this method will be used on the CPU only -#endif // __OPENCL__ +#endif // GPUCA_GPUCODE_DEVICE } ///_______________________________ diff --git a/Detectors/Raw/include/DetectorsRaw/RawFileReader.h b/Detectors/Raw/include/DetectorsRaw/RawFileReader.h index 79c2da6c7afa4..90b4e769a8656 100644 --- a/Detectors/Raw/include/DetectorsRaw/RawFileReader.h +++ b/Detectors/Raw/include/DetectorsRaw/RawFileReader.h @@ -54,6 +54,11 @@ class RawFileReader ErrNoSuperPageForTF, NErrorsDefined }; + + enum class FirstTFDetection : int { Disabled, + Pending, + Done }; + static constexpr std::string_view ErrNames[] = { // long names for error codes "Wrong RDH.packetCounter increment", // ErrWrongPacketCounterIncrement @@ -92,31 +97,40 @@ class RawFileReader //================================================================================ using RDHAny = header::RDHAny; - using RDH = o2::header::RAWDataHeaderV4; + using RDH = o2::header::RAWDataHeader; using OrigDescCard = std::tuple<o2::header::DataOrigin, o2::header::DataDescription, ReadoutCardType>; using InputsMap = std::map<OrigDescCard, std::vector<std::string>>; //===================================================================================== + + // reference on blocks making single message part + struct PartStat { + int size; // total size + int nBlocks; // number of consecutive LinkBlock objects + }; + // info on the smallest block of data to be read when fetching the HBF struct LinkBlock { enum { StartTF = 0x1, StartHB = 0x1 << 1, StartSP = 0x1 << 2, EndHB = 0x1 << 3 }; - size_t offset = 0; // where data of the block starts - uint32_t size = 0; // block size - uint32_t tfID = 0; // tf counter (from 0) - IR ir = 0; // ir starting the block - uint16_t fileID = 0; // file id where the block is located - uint8_t flags = 0; // different flags + size_t offset = 0; //! where data of the block starts + uint32_t size = 0; //! block size + uint32_t tfID = 0; //! tf counter (from 0) + IR ir = 0; //! ir starting the block + uint16_t fileID = 0; //! file id where the block is located + uint8_t flags = 0; //! different flags + std::unique_ptr<char[]> dataCache; //! optional cache for fast access LinkBlock() = default; LinkBlock(int fid, size_t offs) : offset(offs), fileID(fid) {} void setFlag(uint8_t fl, bool v = true) { - if (v) + if (v) { flags |= fl; - else + } else { flags &= ~fl; + } } bool testFlag(uint8_t fl) const { return (flags & fl) == fl; } void print(const std::string& pref = "") const; @@ -124,30 +138,31 @@ class RawFileReader //===================================================================================== struct LinkData { - RDHAny rdhl; // RDH with the running info of the last RDH seen - LinkSpec_t spec = 0; // Link subspec augmented by its origin - LinkSubSpec_t subspec = 0; // subspec according to DataDistribution - uint32_t nTimeFrames = 0; - uint32_t nHBFrames = 0; - uint32_t nSPages = 0; - uint64_t nCRUPages = 0; - bool cruDetector = true; // CRU vs RORC detector - bool continuousRO = true; - - o2::header::DataOrigin origin = o2::header::gDataOriginInvalid; - o2::header::DataDescription description = o2::header::gDataDescriptionInvalid; - std::string fairMQChannel{}; // name of the fairMQ channel for the output - int nErrors = 0; - std::vector<LinkBlock> blocks; + RDHAny rdhl; //! RDH with the running info of the last RDH seen + LinkSpec_t spec = 0; //! Link subspec augmented by its origin + LinkSubSpec_t subspec = 0; //! subspec according to DataDistribution + uint32_t nTimeFrames = 0; //! + uint32_t nHBFrames = 0; //! + uint32_t nSPages = 0; //! + uint64_t nCRUPages = 0; //! + bool cruDetector = true; //! CRU vs RORC detector + bool continuousRO = true; //! + + o2::header::DataOrigin origin = o2::header::gDataOriginInvalid; //! + o2::header::DataDescription description = o2::header::gDataDescriptionInvalid; //! + std::string fairMQChannel{}; //! name of the fairMQ channel for the output + int nErrors = 0; //! + std::vector<LinkBlock> blocks; //! + std::vector<std::pair<int, uint32_t>> tfStartBlock; // // transient info during processing - bool openHB = false; - int nHBFinTF = 0; - int nextBlock2Read = 0; // next block which should be read + bool openHB = false; //! + int nHBFinTF = 0; //! + int nextBlock2Read = 0; //! next block which should be read LinkData() = default; template <typename H> - LinkData(const H& rdh, const RawFileReader* r) : rdhl(rdh), reader(r) + LinkData(const H& rdh, RawFileReader* r) : rdhl(rdh), reader(r) { } bool preprocessCRUPage(const RDHAny& rdh, bool newSPage); @@ -155,25 +170,26 @@ class RawFileReader size_t getLargestTF() const; size_t getNextHBFSize() const; size_t getNextTFSize() const; + size_t getNextTFSuperPagesStat(std::vector<PartStat>& parts) const; int getNHBFinTF() const; size_t readNextHBF(char* buff); size_t readNextTF(char* buff); + size_t readNextSuperPage(char* buff, const PartStat* pstat = nullptr); size_t skipNextHBF(); size_t skipNextTF(); - void rewindToTF(uint32_t tf); - + bool rewindToTF(uint32_t tf); void print(bool verbose = false, const std::string& pref = "") const; std::string describe() const; private: - const RawFileReader* reader = nullptr; + RawFileReader* reader = nullptr; //! }; //===================================================================================== - RawFileReader(const std::string& config = "", int verbosity = 0); + RawFileReader(const std::string& config = "", int verbosity = 0, size_t buffsize = 50 * 1024UL); ~RawFileReader() { clear(); } void loadFromInputsMap(const InputsMap& inp); @@ -213,15 +229,23 @@ class RawFileReader size_t getBufferSize() const { return mBufferSize; } void setMaxTFToRead(uint32_t n) { mMaxTFToRead = n; } + bool isEmpty() const { return mEmpty; } uint32_t getMaxTFToRead() const { return mMaxTFToRead; } uint32_t getNTimeFrames() const { return mNTimeFrames; } uint32_t getOrbitMin() const { return mOrbitMin; } uint32_t getOrbitMax() const { return mOrbitMax; } + bool getCacheData() const { return mCacheData; } + void setCacheData(bool v) { mCacheData = v; } + o2::header::DataOrigin getDefaultDataOrigin() const { return mDefDataOrigin; } o2::header::DataDescription getDefaultDataSpecification() const { return mDefDataDescription; } ReadoutCardType getDefaultReadoutCardType() const { return mDefCardType; } + void imposeFirstTF(uint32_t orbit, uint16_t bc); + void setTFAutodetect(FirstTFDetection v) { mFirstTFAutodetect = v; } + FirstTFDetection getTFAutodetect() const { return mFirstTFAutodetect; } + static o2::header::DataOrigin getDataOrigin(const std::string& ors); static o2::header::DataDescription getDataDescription(const std::string& ors); static InputsMap parseInput(const std::string& confUri); @@ -237,30 +261,33 @@ class RawFileReader static constexpr o2::header::DataDescription DEFDataDescription = o2::header::gDataDescriptionRawData; static constexpr ReadoutCardType DEFCardType = CRU; - o2::header::DataOrigin mDefDataOrigin = DEFDataOrigin; - o2::header::DataDescription mDefDataDescription = DEFDataDescription; - ReadoutCardType mDefCardType = CRU; + o2::header::DataOrigin mDefDataOrigin = DEFDataOrigin; //! + o2::header::DataDescription mDefDataDescription = DEFDataDescription; //! + ReadoutCardType mDefCardType = CRU; //! - std::vector<std::string> mFileNames; // input file names - std::vector<FILE*> mFiles; // input file handlers - std::vector<OrigDescCard> mDataSpecs; // data origin and description for every input file + readout card type + std::vector<std::string> mFileNames; //! input file names + std::vector<FILE*> mFiles; //! input file handlers + std::vector<std::unique_ptr<char[]>> mFileBuffers; //! buffers for input files + std::vector<OrigDescCard> mDataSpecs; //! data origin and description for every input file + readout card type bool mInitDone = false; - std::unordered_map<LinkSpec_t, int> mLinkEntries; // mapping between RDH specs and link entry in the mLinksData - std::vector<LinkData> mLinksData; // info on links data in the files - std::vector<int> mOrderedIDs; // links entries ordered in Specs - uint32_t mMaxTFToRead = 0xffffffff; // max TFs to process - uint32_t mNTimeFrames = 0; // total number of time frames - uint32_t mNextTF2Read = 0; // next TF to read - uint32_t mOrbitMin = 0xffffffff; // lowest orbit seen by any link - uint32_t mOrbitMax = 0; // highest orbit seen by any link - size_t mBufferSize = 1024 * 1024; // size of the buffer for files preprocessing - int mNominalSPageSize = 0x1 << 20; // expected super-page size in B - int mCurrentFileID = 0; // current file being processed - long int mPosInFile = 0; // current position in the file - bool mMultiLinkFile = false; // was > than 1 link seen in the file? - uint32_t mCheckErrors = 0; // mask for errors to check - int mVerbosity = 0; - + bool mEmpty = true; + std::unordered_map<LinkSpec_t, int> mLinkEntries; //! mapping between RDH specs and link entry in the mLinksData + std::vector<LinkData> mLinksData; //! info on links data in the files + std::vector<int> mOrderedIDs; //! links entries ordered in Specs + uint32_t mMaxTFToRead = 0xffffffff; //! max TFs to process + uint32_t mNTimeFrames = 0; //! total number of time frames + uint32_t mNextTF2Read = 0; //! next TF to read + uint32_t mOrbitMin = 0xffffffff; //! lowest orbit seen by any link + uint32_t mOrbitMax = 0; //! highest orbit seen by any link + size_t mBufferSize = 5 * 1024UL; //! size of the buffer for files reading + int mNominalSPageSize = 0x1 << 20; //! expected super-page size in B + int mCurrentFileID = 0; //! current file being processed + long int mPosInFile = 0; //! current position in the file + bool mMultiLinkFile = false; //! was > than 1 link seen in the file? + bool mCacheData = false; //! cache data to block after 1st scan (may require excessive memory, use with care) + uint32_t mCheckErrors = 0; //! mask for errors to check + FirstTFDetection mFirstTFAutodetect = FirstTFDetection::Disabled; //! + int mVerbosity = 0; //! ClassDefNV(RawFileReader, 1); }; diff --git a/Detectors/Raw/include/DetectorsRaw/RawFileWriter.h b/Detectors/Raw/include/DetectorsRaw/RawFileWriter.h index d9a59eb8dfb63..8b011feba7435 100644 --- a/Detectors/Raw/include/DetectorsRaw/RawFileWriter.h +++ b/Detectors/Raw/include/DetectorsRaw/RawFileWriter.h @@ -48,6 +48,7 @@ class RawFileWriter const char* ptr, int size, int splitID, std::vector<char>& trailer, std::vector<char>& header)>; using EmptyPageCallBack = std::function<void(const RDHAny* rdh, std::vector<char>& emptyHBF)>; + using NewRDHCallBack = std::function<void(const RDHAny* rdh, bool prevEmpty, std::vector<char>& filler)>; ///===================================================================================== /// output file handler with its own lock @@ -76,7 +77,7 @@ class RawFileWriter ///===================================================================================== /// Single GBT link helper struct LinkData { - static constexpr int MarginToFlush = 2 * sizeof(RDHAny); // flush superpage if free space left <= this margin + static constexpr int MarginToFlush = 10 * sizeof(RDHAny); // flush superpage if free space left <= this margin RDHAny rdhCopy; // RDH with the running info of the last RDH seen IR updateIR; // IR at which new HBF needs to be created int lastRDHoffset = -1; // position of last RDH in the link buffer @@ -107,6 +108,9 @@ class RawFileWriter void print() const; void addData(const IR& ir, const gsl::span<char> data, bool preformatted = false, uint32_t trigger = 0); RDHAny* getLastRDH() { return lastRDHoffset < 0 ? nullptr : reinterpret_cast<RDHAny*>(&buffer[lastRDHoffset]); } + int getCurrentPageSize() const { return lastRDHoffset < 0 ? -1 : int(buffer.size()) - lastRDHoffset; } + // check if we are at the beginning of new page + bool isNewPage() const { return getCurrentPageSize() == sizeof(RDHAny); } std::string describe() const; protected: @@ -236,6 +240,8 @@ class RawFileWriter return it->first; } + OutputFile& getOutputFileForLink(const LinkData& lnk) { return mFName2File[lnk.fileName]; } + int getSuperPageSize() const { return mSuperPageSize; } void setSuperPageSize(int nbytes); @@ -261,6 +267,14 @@ class RawFileWriter }; } + template <class T> + void setNewRDHCallBack(const T* t) + { + newRDHFunc = [=](const RDHAny* rdh, bool prevEmpty, std::vector<char>& toAdd) { + t->newRDHMethod(rdh, prevEmpty, toAdd); + }; + } + // This is a placeholder for the function responsible to split large payload to pieces // fitting 8kB CRU pages. // The RawFileWriter receives from the encoder the payload to format according to the CRU format @@ -313,6 +327,18 @@ class RawFileWriter { } + // This is a placeholder for the optional callback function to provide a detector-specific filler to be added right + // after the page starting by RDH (might be with RDH.stop=1 !) for the normal data filling (!! not automatic open/close RDHs for empty pages) + // + // It provides to the newRDHMethod method the following info: + // rdh : RDH of the CRU page to be opened + // prevEmpty : true is previous RDH page did not receive any data + // toAdd : a vector (supplied empty) to be filled to a size multipe of 16 bytes + // + void newRDHMethod(const RDHAny* rdh, bool prevEmpty, std::vector<char>& toAdd) const + { + } + int getUsedRDHVersion() const { return mUseRDHVersion; } void useRDHVersion(int v) { @@ -341,9 +367,12 @@ class RawFileWriter } } + void setApplyCarryOverToLastPage(bool v) { mApplyCarryOverToLastPage = v; } + bool isRORCDetector() const { return !mCRUDetector; } bool isCRUDetector() const { return mCRUDetector; } bool isRDHStopUsed() const { return mUseRDHStop; } + bool isCarryOverToLastPageApplied() const { return mApplyCarryOverToLastPage; } private: void fillFromCache(); @@ -358,7 +387,7 @@ class RawFileWriter CarryOverCallBack carryOverFunc = nullptr; // default call back for large payload splitting (does nothing) EmptyPageCallBack emptyHBFFunc = nullptr; // default call back for empty HBF (does nothing) - + NewRDHCallBack newRDHFunc = nullptr; // default call back for new page opening (does nothing) // options int mVerbosity = 0; o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; @@ -369,6 +398,7 @@ class RawFileWriter bool mAddSeparateHBFStopPage = true; // HBF stop is added on a separate CRU page bool mUseRDHStop = true; // detector uses STOP in RDH bool mCRUDetector = true; // Detector readout via CRU ( RORC if false) + bool mApplyCarryOverToLastPage = false; // call CarryOver method also for last chunk and overwrite modified trailer //>> caching -------------- bool mCachingStage = false; // signal that current data should be cached diff --git a/Detectors/Raw/src/HBFUtils.cxx b/Detectors/Raw/src/HBFUtils.cxx index d9c5fc008bcf3..e9f8978451570 100644 --- a/Detectors/Raw/src/HBFUtils.cxx +++ b/Detectors/Raw/src/HBFUtils.cxx @@ -20,7 +20,7 @@ using namespace o2::raw; O2ParamImpl(o2::raw::HBFUtils); //_________________________________________________ -int64_t HBFUtils::getHBF(const IR& rec) const +uint32_t HBFUtils::getHBF(const IR& rec) const { ///< get HBF ID corresponding to this IR auto diff = rec.differenceInBC(getFirstIR()); diff --git a/Detectors/Raw/src/RawFileReader.cxx b/Detectors/Raw/src/RawFileReader.cxx index c3f711cb3a7f0..c2d6889771fa2 100644 --- a/Detectors/Raw/src/RawFileReader.cxx +++ b/Detectors/Raw/src/RawFileReader.cxx @@ -71,16 +71,45 @@ void RawFileReader::LinkData::print(bool verbose, const std::string& pref) const } } +//____________________________________________ +size_t RawFileReader::LinkData::getNextTFSuperPagesStat(std::vector<RawFileReader::PartStat>& parts) const +{ + // get stat. of superpages for this link in this TF. We treat as a start of a superpage the discontinuity in the link data, new TF + // or continuous data exceeding a threshold (e.g. 1MB) + if (nextBlock2Read >= 0) { // negative nextBlock2Read signals absence of data + int sz = 0, nSP = 0, ibl = nextBlock2Read, nbl = blocks.size(), nblPart = 0; + parts.clear(); + while (ibl < nbl && (blocks[ibl].tfID == blocks[nextBlock2Read].tfID)) { + if (ibl > nextBlock2Read && (blocks[ibl].testFlag(LinkBlock::StartSP) || + (sz + blocks[ibl].size) > reader->mNominalSPageSize || + (blocks[ibl - 1].offset + blocks[ibl - 1].size) < blocks[ibl].offset)) { // new superpage + parts.emplace_back(RawFileReader::PartStat{sz, nblPart}); + sz = 0; + nblPart = 0; + } + sz += blocks[ibl].size; + nblPart++; + ibl++; + } + if (sz) { + parts.emplace_back(RawFileReader::PartStat{sz, nblPart}); + } + } + return parts.size(); +} + //____________________________________________ size_t RawFileReader::LinkData::getNextHBFSize() const { // estimate the memory size of the next HBF to read // The blocks are guaranteed to not cover more than 1 HB size_t sz = 0; - int ibl = nextBlock2Read, nbl = blocks.size(); - while (ibl < nbl && (blocks[ibl].ir == blocks[nextBlock2Read].ir)) { - sz += blocks[ibl].size; - ibl++; + if (nextBlock2Read >= 0) { // negative nextBlock2Read signals absence of data + int ibl = nextBlock2Read, nbl = blocks.size(); + while (ibl < nbl && (blocks[ibl].ir == blocks[nextBlock2Read].ir)) { + sz += blocks[ibl].size; + ibl++; + } } return sz; } @@ -90,19 +119,29 @@ size_t RawFileReader::LinkData::readNextHBF(char* buff) { // read data of the next complete HB, buffer of getNextHBFSize() must be allocated in advance size_t sz = 0; + if (nextBlock2Read < 0) { // negative nextBlock2Read signals absence of data + return sz; + } int ibl = nextBlock2Read, nbl = blocks.size(); bool error = false; while (ibl < nbl) { - const auto& blc = blocks[ibl]; + auto& blc = blocks[ibl]; if (blc.ir != blocks[nextBlock2Read].ir) { break; } ibl++; - auto fl = reader->mFiles[blc.fileID]; - if (fseek(fl, blc.offset, SEEK_SET) || fread(buff + sz, 1, blc.size, fl) != blc.size) { - LOGF(ERROR, "Failed to read for the %s a bloc:", describe()); - blc.print(); - error = true; + if (blc.dataCache) { + memcpy(buff + sz, blc.dataCache.get(), blc.size); + } else { + auto fl = reader->mFiles[blc.fileID]; + if (fseek(fl, blc.offset, SEEK_SET) || fread(buff + sz, 1, blc.size, fl) != blc.size) { + LOGF(ERROR, "Failed to read for the %s a bloc:", describe()); + blc.print(); + error = true; + } else if (reader->mCacheData) { // need to fill the cache at 1st reading + blc.dataCache = std::make_unique<char[]>(blc.size); + memcpy(blc.dataCache.get(), buff + sz, blc.size); // will be used at next reading + } } sz += blc.size; } @@ -115,6 +154,9 @@ size_t RawFileReader::LinkData::skipNextHBF() { // skip next complete HB size_t sz = 0; + if (nextBlock2Read < 0) { // negative nextBlock2Read signals absence of data + return sz; + } int ibl = nextBlock2Read, nbl = blocks.size(); while (ibl < nbl) { const auto& blc = blocks[ibl]; @@ -134,10 +176,12 @@ size_t RawFileReader::LinkData::getNextTFSize() const // estimate the memory size of the next TF to read // (assuming nextBlock2Read is at the start of the TF) size_t sz = 0; - int ibl = nextBlock2Read, nbl = blocks.size(); - while (ibl < nbl && (blocks[ibl].tfID == blocks[nextBlock2Read].tfID)) { - sz += blocks[ibl].size; - ibl++; + if (nextBlock2Read >= 0) { // negative nextBlock2Read signals absence of data + int ibl = nextBlock2Read, nbl = blocks.size(); + while (ibl < nbl && (blocks[ibl].tfID == blocks[nextBlock2Read].tfID)) { + sz += blocks[ibl].size; + ibl++; + } } return sz; } @@ -147,6 +191,9 @@ size_t RawFileReader::LinkData::readNextTF(char* buff) { // read next complete TF, buffer of getNextTFSize() must be allocated in advance size_t sz = 0; + if (nextBlock2Read < 0) { // negative nextBlock2Read signals absence of data + return sz; + } int ibl0 = nextBlock2Read, nbl = blocks.size(); bool error = false; while (nextBlock2Read < nbl && (blocks[nextBlock2Read].tfID == blocks[ibl0].tfID)) { // nextBlock2Read is incremented by the readNextHBF! @@ -164,6 +211,9 @@ size_t RawFileReader::LinkData::skipNextTF() { // skip next complete TF size_t sz = 0; + if (nextBlock2Read < 0) { // negative nextBlock2Read signals absence of data + return sz; + } int ibl0 = nextBlock2Read, nbl = blocks.size(); bool error = false; while (nextBlock2Read < nbl && (blocks[nextBlock2Read].tfID == blocks[ibl0].tfID)) { // nextBlock2Read is incremented by the readNextHBF! @@ -177,13 +227,17 @@ size_t RawFileReader::LinkData::skipNextTF() } //_____________________________________________________________________ -void RawFileReader::LinkData::rewindToTF(uint32_t tf) +bool RawFileReader::LinkData::rewindToTF(uint32_t tf) { // go to given TF - nextBlock2Read = 0; - for (uint32_t i = 0; i < tf; i++) { - skipNextTF(); + if (tf < tfStartBlock.size()) { + nextBlock2Read = tfStartBlock[tf].first; + } else { + LOG(WARNING) << "No TF " << tf << " for " << describe(); + nextBlock2Read = -1; + return false; } + return true; } //____________________________________________ @@ -191,15 +245,63 @@ int RawFileReader::LinkData::getNHBFinTF() const { // estimate number of HBFs left in the TF int ibl = nextBlock2Read, nbl = blocks.size(), nHB = 0; - while (ibl < nbl && (blocks[ibl].tfID == blocks[nextBlock2Read].tfID)) { - if (blocks[ibl].testFlag(LinkBlock::StartHB)) { - nHB++; + if (nextBlock2Read >= 0) { // negative nextBlock2Read signals absence of data + while (ibl < nbl && (blocks[ibl].tfID == blocks[nextBlock2Read].tfID)) { + if (blocks[ibl].testFlag(LinkBlock::StartHB)) { + nHB++; + } + ibl++; } - ibl++; } return nHB; } +//____________________________________________ +size_t RawFileReader::LinkData::readNextSuperPage(char* buff, const RawFileReader::PartStat* pstat) +{ + // read data of the next complete HB, buffer of getNextHBFSize() must be allocated in advance + size_t sz = 0; + if (nextBlock2Read < 0) { // negative nextBlock2Read signals absence of data + return sz; + } + int ibl = nextBlock2Read, nbl = blocks.size(); + auto tfID = blocks[nextBlock2Read].tfID; + bool error = false; + if (pstat) { // info is provided, use it derictly + sz = pstat->size; + ibl += pstat->nBlocks; + } else { // need to calculate blocks to read + while (ibl < nbl) { + auto& blc = blocks[ibl]; + if (ibl > nextBlock2Read && (blc.tfID != blocks[nextBlock2Read].tfID || + blc.testFlag(LinkBlock::StartSP) || + (sz + blc.size) > reader->mNominalSPageSize || + blocks[ibl - 1].offset + blocks[ibl - 1].size < blc.offset)) { // new superpage or TF + break; + } + ibl++; + sz += blc.size; + } + } + if (sz) { + if (reader->mCacheData && blocks[nextBlock2Read].dataCache) { + memcpy(buff, blocks[nextBlock2Read].dataCache.get(), sz); + } else { + auto fl = reader->mFiles[blocks[nextBlock2Read].fileID]; + if (fseek(fl, blocks[nextBlock2Read].offset, SEEK_SET) || fread(buff, 1, sz, fl) != sz) { + LOGF(ERROR, "Failed to read for the %s a bloc:", describe()); + blocks[nextBlock2Read].print(); + error = true; + } else if (reader->mCacheData) { // cache after 1st reading + blocks[nextBlock2Read].dataCache = std::make_unique<char[]>(sz); + memcpy(blocks[nextBlock2Read].dataCache.get(), buff, sz); + } + } + } + nextBlock2Read = ibl; + return error ? 0 : sz; // in case of the error we ignore the data +} + //____________________________________________ size_t RawFileReader::LinkData::getLargestSuperPage() const { @@ -347,8 +449,20 @@ bool RawFileReader::LinkData::preprocessCRUPage(const RDHAny& rdh, bool newSPage } if (newTF || newSPage || newHB) { + int nbl = blocks.size(); auto& bl = blocks.emplace_back(reader->mCurrentFileID, reader->mPosInFile); + bl.ir = hbIR; + bl.tfID = HBU.getTF(hbIR); // nTimeFrames - 1; if (newTF) { + if (reader->getTFAutodetect() == FirstTFDetection::Pending) { // impose first TF + if (cruDetector) { + reader->imposeFirstTF(hbIR.orbit, hbIR.bc); + bl.tfID = HBU.getTF(hbIR); // update + } else { + throw std::runtime_error("HBFUtil first orbit/bc autodetection cannot be done with first link from CRORC detector"); + } + } + tfStartBlock.emplace_back(nbl, bl.tfID); nTimeFrames++; bl.setFlag(LinkBlock::StartTF); if (reader->mCheckErrors & (0x1 << ErrNoSuperPageForTF) && cruDetector) { @@ -359,8 +473,6 @@ bool RawFileReader::LinkData::preprocessCRUPage(const RDHAny& rdh, bool newSPage } } // end of check errors } - bl.ir = hbIR; - bl.tfID = HBU.getTF(hbIR); // nTimeFrames - 1; if (newSPage) { nSPages++; @@ -391,7 +503,7 @@ bool RawFileReader::LinkData::preprocessCRUPage(const RDHAny& rdh, bool newSPage //====================== methods of RawFileReader ======================== //_____________________________________________________________________ -RawFileReader::RawFileReader(const std::string& config, int verbosity) : mVerbosity(verbosity) +RawFileReader::RawFileReader(const std::string& config, int verbosity, size_t buffSize) : mVerbosity(verbosity), mBufferSize(buffSize) { if (!config.empty()) { auto inp = parseInput(config); @@ -435,7 +547,7 @@ bool RawFileReader::preprocessFile(int ifl) long int nr = 0; mPosInFile = 0; size_t nRDHread = 0, boffs; - bool ok = true, readMore = true; + bool readMore = true; while (readMore && (nr = fread(buffer.get(), 1, mBufferSize, fl))) { boffs = 0; while (1) { @@ -479,7 +591,7 @@ bool RawFileReader::preprocessFile(int ifl) } LOGF(INFO, "File %3d : %9li bytes scanned, %6d RDH read for %4d links from %s", mCurrentFileID, mPosInFile, nRDHread, int(mLinkEntries.size()), mFileNames[mCurrentFileID]); - return ok; + return nRDHread > 0; } //_____________________________________________________________________ @@ -518,12 +630,16 @@ bool RawFileReader::addFile(const std::string& sname, o2::header::DataOrigin ori LOG(ERROR) << "Cannot add new files after initialization"; return false; } - auto inFile = fopen(sname.c_str(), "rb"); bool ok = true; + + mFileBuffers.push_back(std::make_unique<char[]>(mBufferSize)); + auto inFile = fopen(sname.c_str(), "rb"); if (!inFile) { LOG(ERROR) << "Failed to open input file " << sname; - ok = false; + return false; } + setvbuf(inFile, mFileBuffers.back().get(), _IOFBF, mBufferSize); + if (origin == o2h::gDataOriginInvalid) { LOG(ERROR) << "Invalid data origin " << origin.as<std::string>() << " for file " << sname; ok = false; @@ -534,8 +650,8 @@ bool RawFileReader::addFile(const std::string& sname, o2::header::DataOrigin ori } if (!ok) { fclose(inFile); + return false; } - mFileNames.push_back(sname); mFiles.push_back(inFile); mDataSpecs.emplace_back(origin, desc, t); @@ -557,9 +673,11 @@ bool RawFileReader::init() } int nf = mFiles.size(); - bool ok = true; + mEmpty = true; for (int i = 0; i < nf; i++) { - ok &= preprocessFile(i); + if (preprocessFile(i)) { + mEmpty = false; + } } mOrderedIDs.resize(mLinksData.size()); for (int i = mLinksData.size(); i--;) { @@ -575,7 +693,7 @@ bool RawFileReader::init() LOGF(INFO, "Summary of preprocessing:"); for (int i = 0; i < int(mLinksData.size()); i++) { - const auto& link = getLink(i); + auto& link = getLink(i); auto msp = link.getLargestSuperPage(); auto mtf = link.getLargestTF(); if (maxSP < msp) { @@ -598,19 +716,22 @@ bool RawFileReader::init() if (link.blocks.back().ir.orbit > mOrbitMax) { mOrbitMax = link.blocks.back().ir.orbit; } + if (link.tfStartBlock.empty() && !link.blocks.empty()) { + link.tfStartBlock.emplace_back(0, 0); + } if ((mCheckErrors & (0x1 << ErrWrongNumberOfTF)) && (mNTimeFrames != link.nTimeFrames)) { LOGF(ERROR, "%s for %s: %u TFs while %u were seen for other links", ErrNames[ErrWrongNumberOfTF], link.describe(), link.nTimeFrames, mNTimeFrames); } } - LOGF(INFO, "First orbit: %d, Last orbit: %d", mOrbitMin, mOrbitMax); + LOGF(INFO, "First orbit: %u, Last orbit: %u", mOrbitMin, mOrbitMax); LOGF(INFO, "Largest super-page: %zu B, largest TF: %zu B", maxSP, maxTF); if (!mCheckErrors) { LOGF(INFO, "Detailed data format check was disabled"); } mInitDone = true; - return ok; + return !mEmpty; } //_____________________________________________________________________ @@ -662,7 +783,9 @@ void RawFileReader::loadFromInputsMap(const RawFileReader::InputsMap& inp) continue; } for (const auto& fnm : files) { // specific file names - addFile(fnm, std::get<0>(ordesc), std::get<1>(ordesc), std::get<2>(ordesc)); + if (!addFile(fnm, std::get<0>(ordesc), std::get<1>(ordesc), std::get<2>(ordesc))) { + throw std::runtime_error("wrong raw data file path or origin/description"); + } } } } @@ -752,6 +875,19 @@ RawFileReader::InputsMap RawFileReader::parseInput(const std::string& confUri) return entries; } +void RawFileReader::imposeFirstTF(uint32_t orbit, uint16_t bc) +{ + if (mFirstTFAutodetect != FirstTFDetection::Pending) { + throw std::runtime_error("reader was not expecting imposing first TF"); + } + auto& hbu = o2::raw::HBFUtils::Instance(); + o2::raw::HBFUtils::setValue("HBFUtils", "orbitFirst", orbit); + o2::raw::HBFUtils::setValue("HBFUtils", "bcFirst", bc); + LOG(INFO) << "Imposed data-driven TF start"; + mFirstTFAutodetect = FirstTFDetection::Done; + hbu.printKeyValues(); +} + std::string RawFileReader::nochk_opt(RawFileReader::ErrTypes e) { std::string opt = ErrCheckDefaults[e] ? "nocheck-" : "check-"; diff --git a/Detectors/Raw/src/RawFileReaderWorkflow.cxx b/Detectors/Raw/src/RawFileReaderWorkflow.cxx index 50852fd66d8f8..8fc58670ae1a2 100644 --- a/Detectors/Raw/src/RawFileReaderWorkflow.cxx +++ b/Detectors/Raw/src/RawFileReaderWorkflow.cxx @@ -27,7 +27,7 @@ #include "Headers/Stack.h" #include "RawFileReaderWorkflow.h" // not installed - +#include <TStopwatch.h> #include <fairmq/FairMQDevice.h> #include <unistd.h> @@ -42,48 +42,55 @@ using namespace o2::raw; namespace o2f = o2::framework; namespace o2h = o2::header; -class rawReaderSpecs : public o2f::Task +class RawReaderSpecs : public o2f::Task { public: - explicit rawReaderSpecs(const std::string& config, bool tfAsMessage = false, bool outPerRoute = true, int loop = 1, uint32_t delay_us = 0, - uint32_t errmap = 0xffffffff, uint32_t minTF = 0, uint32_t maxTF = 0xffffffff, size_t buffSize = 1024L * 1024L) - : mLoop(loop < 0 ? INT_MAX : (loop < 1 ? 1 : loop)), mHBFPerMessage(!tfAsMessage), mOutPerRoute(outPerRoute), mDelayUSec(delay_us), mMinTFID(minTF), mMaxTFID(maxTF), mReader(std::make_unique<o2::raw::RawFileReader>(config)) + explicit RawReaderSpecs(const std::string& config, int loop = 1, uint32_t delay_us = 0, + uint32_t errmap = 0xffffffff, uint32_t minTF = 0, uint32_t maxTF = 0xffffffff, bool partPerSP = true, bool cache = false, bool autodetectTF0 = false, + size_t spSize = 1024L * 1024L, size_t buffSize = 5 * 1024UL, + const std::string& rawChannelName = "") + : mLoop(loop < 0 ? INT_MAX : (loop < 1 ? 1 : loop)), mDelayUSec(delay_us), mMinTFID(minTF), mMaxTFID(maxTF), mPartPerSP(partPerSP), mReader(std::make_unique<o2::raw::RawFileReader>(config, 0, buffSize)), mRawChannelName(rawChannelName) { mReader->setCheckErrors(errmap); mReader->setMaxTFToRead(maxTF); - mReader->setBufferSize(buffSize); + mReader->setNominalSPageSize(spSize); + mReader->setCacheData(cache); + mReader->setTFAutodetect(autodetectTF0 ? RawFileReader::FirstTFDetection::Pending : RawFileReader::FirstTFDetection::Disabled); LOG(INFO) << "Will preprocess files with buffer size of " << buffSize << " bytes"; LOG(INFO) << "Number of loops over whole data requested: " << mLoop; - if (mHBFPerMessage) { - LOG(INFO) << "Every link TF will be sent as multipart of HBF messages"; - } else { - LOG(INFO) << "HBF of single TF of each link will be sent as a single message"; + for (int i = NTimers; i--;) { + mTimer[i].Stop(); + mTimer[i].Reset(); } - LOG(INFO) << "A message per " << (mOutPerRoute ? "route" : "link") << " will be sent"; - LOG(INFO) << "Delay of " << mDelayUSec << " microseconds will be added between TFs"; } void init(o2f::InitContext& ic) final { assert(mReader); + mTimer[TimerInit].Start(); mReader->init(); + mTimer[TimerInit].Stop(); if (mMaxTFID >= mReader->getNTimeFrames()) { - mMaxTFID = mReader->getNTimeFrames() - 1; + mMaxTFID = mReader->getNTimeFrames() ? mReader->getNTimeFrames() - 1 : 0; } } void run(o2f::ProcessingContext& ctx) final { assert(mReader); - static size_t loopsDone = 0, sentSize = 0, sentMessages = 0; if (mDone) { return; } - int nhbexp = HBFUtils::Instance().getNOrbitsPerTF(); + auto tTotStart = mTimer[TimerTotal].CpuTime(), tIOStart = mTimer[TimerIO].CpuTime(); + mTimer[TimerTotal].Start(false); auto device = ctx.services().get<o2f::RawDeviceService>().device(); assert(device); - auto findOutputChannel = [&ctx](RawFileReader::LinkData& link, size_t timeslice) { + auto findOutputChannel = [&ctx, this](RawFileReader::LinkData& link, size_t timeslice) { + if (!this->mRawChannelName.empty()) { + link.fairMQChannel = this->mRawChannelName; + return true; + } auto outputRoutes = ctx.services().get<o2f::RawDeviceService>().spec().outputs; for (auto& oroute : outputRoutes) { LOG(DEBUG) << "comparing with matcher to route " << oroute.matcher << " TSlice:" << oroute.timeslice; @@ -103,18 +110,18 @@ class rawReaderSpecs : public o2f::Task int nlinks = mReader->getNLinks(); std::unordered_map<std::string, std::unique_ptr<FairMQParts>> messagesPerRoute; - std::vector<std::unique_ptr<FairMQParts>> messagesPerLink; - if (!mOutPerRoute) { - messagesPerLink.resize(nlinks); - } if (tfID > mMaxTFID) { - if (mReader->getNTimeFrames() && --mLoop) { - loopsDone++; + if (!mReader->isEmpty() && --mLoop) { + mLoopsDone++; tfID = 0; - LOG(INFO) << "Starting new loop " << loopsDone << " from the beginning of data"; + LOG(INFO) << "Starting new loop " << mLoopsDone << " from the beginning of data"; } else { - LOGF(INFO, "Finished: payload of %zu bytes in %zu messages sent for %d TFs", sentSize, sentMessages, mTFIDaccum); + mTimer[TimerTotal].Stop(); + LOGF(INFO, "Finished: payload of %zu bytes in %zu messages sent for %d TFs", mSentSize, mSentMessages, mTFCounter); + for (int i = 0; i < NTimers; i++) { + LOGF(INFO, "Timing for %15s: Cpu: %.3e Real: %.3e s in %d slots", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1); + } ctx.services().get<o2f::ControlService>().endOfStream(); ctx.services().get<o2f::ControlService>().readyToQuit(o2f::QuitRequest::Me); mDone = true; @@ -126,104 +133,86 @@ class rawReaderSpecs : public o2f::Task tfID = mMinTFID; } mReader->setNextTFToRead(tfID); - for (int il = 0; il < nlinks; il++) { - mReader->getLink(il).rewindToTF(tfID); - } + std::vector<RawFileReader::PartStat> partsSP; + const auto& hbfU = HBFUtils::Instance(); // read next time frame size_t tfNParts = 0, tfSize = 0; - LOG(INFO) << "Reading TF#" << mTFIDaccum << " (" << tfID << " at iteration " << loopsDone << ')'; + LOG(INFO) << "Reading TF#" << mTFCounter << " (" << tfID << " at iteration " << mLoopsDone << ')'; + o2::header::Stack dummyStack{o2h::DataHeader{}, o2::framework::DataProcessingHeader{0}}; // dummy stack to just to get stack size + auto hstackSize = dummyStack.size(); for (int il = 0; il < nlinks; il++) { auto& link = mReader->getLink(il); - - if (!findOutputChannel(link, mTFIDaccum)) { // no output channel + if (!link.rewindToTF(tfID)) { + continue; // this link has no data for wanted TF + } + if (!findOutputChannel(link, mTFCounter)) { // no output channel continue; } o2h::DataHeader hdrTmpl(link.description, link.origin, link.subspec); // template with 0 size - int nhb = link.getNHBFinTF(); + int nParts = mPartPerSP ? link.getNextTFSuperPagesStat(partsSP) : link.getNHBFinTF(); hdrTmpl.payloadSerializationMethod = o2h::gSerializationMethodNone; - hdrTmpl.splitPayloadParts = mHBFPerMessage ? nhb : 1; + hdrTmpl.splitPayloadParts = nParts; while (hdrTmpl.splitPayloadIndex < hdrTmpl.splitPayloadParts) { - tfSize += hdrTmpl.payloadSize = mHBFPerMessage ? link.getNextHBFSize() : link.getNextTFSize(); - o2::header::Stack headerStack{hdrTmpl, o2::framework::DataProcessingHeader{mTFIDaccum}}; - - auto hdMessage = device->NewMessage(headerStack.size()); - memcpy(hdMessage->GetData(), headerStack.data(), headerStack.size()); - - auto plMessage = device->NewMessage(hdrTmpl.payloadSize); - auto bread = mHBFPerMessage ? link.readNextHBF(reinterpret_cast<char*>(plMessage->GetData())) : link.readNextTF(reinterpret_cast<char*>(plMessage->GetData())); + tfSize += hdrTmpl.payloadSize = mPartPerSP ? partsSP[hdrTmpl.splitPayloadIndex].size : link.getNextHBFSize(); + auto fmqFactory = device->GetChannel(link.fairMQChannel, 0).Transport(); + auto hdMessage = fmqFactory->CreateMessage(hstackSize, fair::mq::Alignment{64}); + auto plMessage = fmqFactory->CreateMessage(hdrTmpl.payloadSize, fair::mq::Alignment{64}); + mTimer[TimerIO].Start(false); + auto bread = mPartPerSP ? link.readNextSuperPage(reinterpret_cast<char*>(plMessage->GetData()), &partsSP[hdrTmpl.splitPayloadIndex]) : link.readNextHBF(reinterpret_cast<char*>(plMessage->GetData())); if (bread != hdrTmpl.payloadSize) { LOG(ERROR) << "Link " << il << " read " << bread << " bytes instead of " << hdrTmpl.payloadSize - << " expected in TF=" << mTFIDaccum << " part=" << hdrTmpl.splitPayloadIndex; + << " expected in TF=" << mTFCounter << " part=" << hdrTmpl.splitPayloadIndex; } + mTimer[TimerIO].Stop(); // check if the RDH to send corresponds to expected orbit if (hdrTmpl.splitPayloadIndex == 0) { - uint32_t hbOrbRead = o2::raw::RDHUtils::getHeartBeatOrbit(plMessage->GetData()); - if (link.cruDetector) { - uint32_t hbOrbExpected = mReader->getOrbitMin() + tfID * nhbexp; - if (hbOrbExpected != hbOrbRead) { - LOGF(ERROR, "Expected orbit=%u but got %u for %d-th HBF in TF#%d of %s/%s/0x%u", - hbOrbExpected, hbOrbRead, hdrTmpl.splitPayloadIndex, tfID, - link.origin.as<std::string>(), link.description.as<std::string>(), link.subspec); - } - } - hdrTmpl.firstTForbit = hbOrbRead + loopsDone * nhbexp; // for next parts - reinterpret_cast<o2::header::DataHeader*>(hdMessage->GetData())->firstTForbit = hdrTmpl.firstTForbit; + auto ir = o2::raw::RDHUtils::getHeartBeatIR(plMessage->GetData()); + auto tfid = hbfU.getTF(ir); + hdrTmpl.firstTForbit = hbfU.getIRTF(tfid).orbit; // will be picked for the + hdrTmpl.tfCounter = mTFCounter; // following parts + // reinterpret_cast<o2::header::DataHeader*>(hdMessage->GetData())->firstTForbit = hdrTmpl.firstTForbit; // hack to fix already filled headers + // reinterpret_cast<o2::header::DataHeader*>(hdMessage->GetData())->tfCounter = mTFCounter; // at the moment don't use it } + o2::header::Stack headerStack{hdrTmpl, o2::framework::DataProcessingHeader{mTFCounter}}; + memcpy(hdMessage->GetData(), headerStack.data(), headerStack.size()); + FairMQParts* parts = nullptr; - if (mOutPerRoute) { - parts = messagesPerRoute[link.fairMQChannel].get(); // FairMQParts* - if (!parts) { - messagesPerRoute[link.fairMQChannel] = std::make_unique<FairMQParts>(); - parts = messagesPerRoute[link.fairMQChannel].get(); - } - } else { // message per link - parts = messagesPerLink[il].get(); // FairMQParts* - if (!parts) { - messagesPerLink[il] = std::make_unique<FairMQParts>(); - parts = messagesPerLink[il].get(); - } + parts = messagesPerRoute[link.fairMQChannel].get(); // FairMQParts* + if (!parts) { + messagesPerRoute[link.fairMQChannel] = std::make_unique<FairMQParts>(); + parts = messagesPerRoute[link.fairMQChannel].get(); } parts->AddPart(std::move(hdMessage)); parts->AddPart(std::move(plMessage)); hdrTmpl.splitPayloadIndex++; // prepare for next tfNParts++; } - LOGF(DEBUG, "Added %d parts for TF#%d(%d in iteration %d) of %s/%s/0x%u", hdrTmpl.splitPayloadParts, mTFIDaccum, tfID, - loopsDone, link.origin.as<std::string>(), link.description.as<std::string>(), link.subspec); + LOGF(DEBUG, "Added %d parts for TF#%d(%d in iteration %d) of %s/%s/0x%u", hdrTmpl.splitPayloadParts, mTFCounter, tfID, + mLoopsDone, link.origin.as<std::string>(), link.description.as<std::string>(), link.subspec); } - if (mTFIDaccum) { // delay sending + if (mTFCounter) { // delay sending usleep(mDelayUSec); } - - if (mOutPerRoute) { - for (auto& msgIt : messagesPerRoute) { - LOG(INFO) << "Sending " << msgIt.second->Size() / 2 << " parts to channel " << msgIt.first; - device->Send(*msgIt.second.get(), msgIt.first); - } - } else { - for (int il = 0; il < nlinks; il++) { - auto& link = mReader->getLink(il); - auto* parts = messagesPerLink[il].get(); // FairMQParts* - if (parts) { - LOG(INFO) << "Sending " << parts->Size() / 2 << " parts to channel " << link.fairMQChannel << " for " << link.describe(); - device->Send(*parts, link.fairMQChannel); - } - } + for (auto& msgIt : messagesPerRoute) { + LOG(INFO) << "Sending " << msgIt.second->Size() / 2 << " parts to channel " << msgIt.first; + device->Send(*msgIt.second.get(), msgIt.first); } + mTimer[TimerTotal].Stop(); + + LOGF(INFO, "Sent payload of %zu bytes in %zu parts in %zu messages for TF %d | Timing (total/IO): %.3e / %.3e", tfSize, tfNParts, + messagesPerRoute.size(), mTFCounter, mTimer[TimerTotal].CpuTime() - tTotStart, mTimer[TimerIO].CpuTime() - tIOStart); - LOGF(INFO, "Sent payload of %zu bytes in %zu parts in %zu messages for TF %d", tfSize, tfNParts, - (mOutPerRoute ? messagesPerRoute.size() : messagesPerLink.size()), mTFIDaccum); - sentSize += tfSize; - sentMessages += tfNParts; + mSentSize += tfSize; + mSentMessages += tfNParts; mReader->setNextTFToRead(++tfID); - ++mTFIDaccum; + ++mTFCounter; } uint32_t getMinTFID() const { return mMinTFID; } @@ -236,42 +225,67 @@ class rawReaderSpecs : public o2f::Task private: int mLoop = 0; // once last TF reached, loop while mLoop>=0 - size_t mTFIDaccum = 0; // TFId accumulator (accounts for looping) + uint32_t mTFCounter = 0; // TFId accumulator (accounts for looping) uint32_t mDelayUSec = 0; // Delay in microseconds between TFs uint32_t mMinTFID = 0; // 1st TF to extract uint32_t mMaxTFID = 0xffffffff; // last TF to extrct - bool mHBFPerMessage = true; // true: send TF as multipart of HBFs, false: single message per TF - bool mOutPerRoute = true; // true: send 1 large output route, otherwise 1 outpur per link + size_t mLoopsDone = 0; + size_t mSentSize = 0; + size_t mSentMessages = 0; + bool mPartPerSP = true; // fill part per superpage bool mDone = false; // processing is over or not + std::string mRawChannelName = ""; // name of optional non-DPL channel std::unique_ptr<o2::raw::RawFileReader> mReader; // matching engine + + enum TimerIDs { TimerInit, + TimerTotal, + TimerIO, + NTimers }; + static constexpr std::string_view TimerName[] = {"Init", "Total", "IO"}; + TStopwatch mTimer[NTimers]; }; -o2f::DataProcessorSpec getReaderSpec(std::string config, bool tfAsMessage, bool outPerRoute, int loop, uint32_t delay_us, uint32_t errmap, - uint32_t minTF, uint32_t maxTF, size_t buffSize) +o2f::DataProcessorSpec getReaderSpec(std::string config, int loop, uint32_t delay_us, uint32_t errmap, + uint32_t minTF, uint32_t maxTF, bool partPerSP, bool cache, bool autodetectTF0, size_t spSize, size_t buffSize, const std::string& rawChannelConfig) { // check which inputs are present in files to read - o2f::Outputs outputs; - if (!config.empty()) { - auto conf = o2::raw::RawFileReader::parseInput(config); - for (const auto& entry : conf) { - const auto& ordescard = entry.first; - if (!entry.second.empty()) { // origin and decription for files to process - outputs.emplace_back(o2f::OutputSpec(o2f::ConcreteDataTypeMatcher{std::get<0>(ordescard), std::get<1>(ordescard)})); + o2f::DataProcessorSpec spec; + spec.name = "raw-file-reader"; + std::string rawChannelName = ""; + if (rawChannelConfig.empty()) { + if (!config.empty()) { + auto conf = o2::raw::RawFileReader::parseInput(config); + for (const auto& entry : conf) { + const auto& ordescard = entry.first; + if (!entry.second.empty()) { // origin and decription for files to process + spec.outputs.emplace_back(o2f::OutputSpec(o2f::ConcreteDataTypeMatcher{std::get<0>(ordescard), std::get<1>(ordescard)})); + } } } + } else { + auto nameStart = rawChannelConfig.find("name="); + if (nameStart == std::string::npos) { + throw std::runtime_error("raw channel name is not provided"); + } + nameStart += strlen("name="); + auto nameEnd = rawChannelConfig.find(",", nameStart + 1); + if (nameEnd == std::string::npos) { + nameEnd = rawChannelConfig.size(); + } + rawChannelName = rawChannelConfig.substr(nameStart, nameEnd - nameStart); + spec.options = {o2f::ConfigParamSpec{"channel-config", o2f::VariantType::String, rawChannelConfig, {"Out-of-band channel config"}}}; + LOG(INFO) << "Will send output to non-DPL channel " << rawChannelConfig; } - return o2f::DataProcessorSpec{ - "raw-file-reader", - o2f::Inputs{}, - outputs, - o2f::AlgorithmSpec{o2f::adaptFromTask<rawReaderSpecs>(config, tfAsMessage, outPerRoute, loop, delay_us, errmap, minTF, maxTF, buffSize)}, - o2f::Options{}}; + + spec.algorithm = o2f::adaptFromTask<RawReaderSpecs>(config, loop, delay_us, errmap, minTF, maxTF, partPerSP, cache, autodetectTF0, spSize, buffSize, rawChannelName); + + return spec; } -o2f::WorkflowSpec o2::raw::getRawFileReaderWorkflow(std::string inifile, bool tfAsMessage, bool outPerRoute, - int loop, uint32_t delay_us, uint32_t errmap, uint32_t minTF, uint32_t maxTF, size_t buffSize) +o2f::WorkflowSpec o2::raw::getRawFileReaderWorkflow(std::string inifile, int loop, uint32_t delay_us, uint32_t errmap, uint32_t minTF, uint32_t maxTF, + bool partPerSP, bool cache, bool autodetectTF0, size_t spSize, size_t buffSize, const std::string& rawChannelConfig) { o2f::WorkflowSpec specs; - specs.emplace_back(getReaderSpec(inifile, tfAsMessage, outPerRoute, loop, delay_us, errmap, minTF, maxTF, buffSize)); + specs.emplace_back(getReaderSpec(inifile, loop, delay_us, errmap, minTF, maxTF, partPerSP, cache, autodetectTF0, spSize, buffSize, rawChannelConfig)); return specs; } diff --git a/Detectors/Raw/src/RawFileReaderWorkflow.h b/Detectors/Raw/src/RawFileReaderWorkflow.h index 7f1b201211a03..c209fd088f573 100644 --- a/Detectors/Raw/src/RawFileReaderWorkflow.h +++ b/Detectors/Raw/src/RawFileReaderWorkflow.h @@ -14,15 +14,17 @@ /// @file RawFileReaderWorkflow.h #include "Framework/WorkflowSpec.h" +class string; namespace o2 { namespace raw { -framework::WorkflowSpec getRawFileReaderWorkflow(std::string inifile, bool tfAsMessage = false, bool outPerRoute = true, - int loop = 1, uint32_t delay_us = 0, uint32_t errMap = 0xffffffff, - uint32_t minTF = 0, uint32_t maxTF = 0xffffffff, size_t bufferSize = 1024L * 1024L); +framework::WorkflowSpec getRawFileReaderWorkflow(std::string inifile, int loop = 1, uint32_t delay_us = 0, uint32_t errMap = 0xffffffff, + uint32_t minTF = 0, uint32_t maxTF = 0xffffffff, bool partPerSP = true, bool cache = false, bool autodetectTF0 = false, + size_t spSize = 1024L * 1024L, size_t bufferSize = 1024L * 1024L, + const std::string& rawChannelConfig = ""); } // namespace raw } // namespace o2 diff --git a/Detectors/Raw/src/RawFileWriter.cxx b/Detectors/Raw/src/RawFileWriter.cxx index 0aaebd76a8330..d11832a52852e 100644 --- a/Detectors/Raw/src/RawFileWriter.cxx +++ b/Detectors/Raw/src/RawFileWriter.cxx @@ -159,7 +159,7 @@ RawFileWriter::LinkData& RawFileWriter::registerLink(uint16_t fee, uint16_t cru, void RawFileWriter::addData(uint16_t feeid, uint16_t cru, uint8_t lnk, uint8_t endpoint, const IR& ir, const gsl::span<char> data, bool preformatted, uint32_t trigger) { // add payload to relevant links - if (data.size() % RDHUtils::GBTWord) { + if (isCRUDetector() && (data.size() % RDHUtils::GBTWord)) { LOG(ERROR) << "provided payload size " << data.size() << " is not multiple of GBT word size"; throw std::runtime_error("payload size is not mutiple of GBT word size"); } @@ -304,16 +304,31 @@ void RawFileWriter::LinkData::addData(const IR& ir, const gsl::span<char> data, addPreformattedCRUPage(data); return; } + + // if we are at the beginning of the page, detector may want to add some header + if (isNewPage() && writer->newRDHFunc) { + std::vector<char> newPageHeader; + writer->newRDHFunc(getLastRDH(), false, newPageHeader); + pushBack(newPageHeader.data(), newPageHeader.size()); + } + const char* ptr = &data[0]; // in case particular detector CRU pages need to be self-consistent, when carrying-over // large payload to new CRU page we may need to write optional trailer and header before // and after the new RDH. - bool carryOver = false; + bool carryOver = false, wasSplit = false, lastSplitPart = false; int splitID = 0; std::vector<char> carryOverHeader; while (dataSize > 0) { + if (carryOver) { // check if there is carry-over header to write in the buffer addHBFPage(); // start new CRU page, if needed, the completed superpage is flushed + if (writer->newRDHFunc) { + std::vector<char> newPageHeader; + writer->newRDHFunc(getLastRDH(), false, newPageHeader); + pushBack(newPageHeader.data(), newPageHeader.size()); + } + // for sure after the carryOver we have space on the CRU page, no need to check LOG(DEBUG) << "Adding carryOverHeader " << carryOverHeader.size() << " bytes in IR " << ir << " to " << describe(); @@ -326,28 +341,53 @@ void RawFileWriter::LinkData::addData(const IR& ir, const gsl::span<char> data, int sizeLeft = sizeLeftCRUPage < sizeLeftSupPage ? sizeLeftCRUPage : sizeLeftSupPage; if (!sizeLeft) { // this page is just over, open a new one addHBFPage(); // start new CRU page, if needed, the completed superpage is flushed + if (writer->newRDHFunc) { + std::vector<char> newPageHeader; + writer->newRDHFunc(getLastRDH(), false, newPageHeader); + pushBack(newPageHeader.data(), newPageHeader.size()); + } continue; } - if (dataSize <= sizeLeft) { // add all remaining data + + if (dataSize <= sizeLeft) { + if (wasSplit && writer->mApplyCarryOverToLastPage) { + lastSplitPart = true; + carryOver = true; + } + } else { + carryOver = true; + wasSplit = true; + } + + if (!carryOver) { // add all remaining data LOG(DEBUG) << "Adding payload " << dataSize << " bytes in IR " << ir << " (carryover=" << carryOver << " ) to " << describe(); pushBack(ptr, dataSize); dataSize = 0; } else { // need to carryOver payload, determine 1st wsize bytes to write starting from ptr - carryOver = true; + if (sizeLeft > dataSize) { + sizeLeft = dataSize; + } int sizeActual = sizeLeft; std::vector<char> carryOverTrailer; if (writer->carryOverFunc) { sizeActual = writer->carryOverFunc(&rdhCopy, data, ptr, sizeLeft, splitID++, carryOverTrailer, carryOverHeader); } LOG(DEBUG) << "Adding carry-over " << splitID - 1 << " fitted payload " << sizeActual << " bytes in IR " << ir << " to " << describe(); - if (sizeActual < 0 || sizeActual + carryOverTrailer.size() > sizeLeft) { + if (sizeActual < 0 || (!lastSplitPart && (sizeActual + carryOverTrailer.size() > sizeLeft))) { throw std::runtime_error(std::string("wrong carry-over data size provided by carryOverMethod") + std::to_string(sizeActual)); } - pushBack(ptr, sizeActual); // write payload fitting to this page + // if there is carry-over trailer at the very last chunk, it must overwrite existing trailer + int trailerOffset = 0; + if (lastSplitPart) { + trailerOffset = carryOverTrailer.size(); + if (sizeActual - trailerOffset < 0) { + throw std::runtime_error("trailer size of last split chunk cannot exceed actual size as it overwrites the existing trailer"); + } + } + pushBack(ptr, sizeActual - trailerOffset); // write payload fitting to this page dataSize -= sizeActual; ptr += sizeActual; - LOG(DEBUG) << "Adding carryOverTrailer " << carryOverTrailer.size() << " bytes in IR " - << ir << " to " << describe(); + LOG(DEBUG) << "Adding carryOverTrailer " << carryOverTrailer.size() << " bytes in IR " << ir << " to " << describe(); pushBack(carryOverTrailer.data(), carryOverTrailer.size()); } } @@ -384,9 +424,10 @@ void RawFileWriter::LinkData::addHBFPage(bool stop) } // finalize last RDH auto& lastRDH = *getLastRDH(); - int psize = buffer.size() - lastRDHoffset; // set the size for the previous header RDH - if (stop && psize == sizeof(RDHAny) && writer->emptyHBFFunc) { // we are closing an empty page, does detector want to add something? - std::vector<char> emtyHBFFiller; // working space for optional empty HBF filler + int psize = getCurrentPageSize(); // set the size for the previous header RDH + bool emptyPage = psize == sizeof(RDHAny); + if (stop && emptyPage && writer->emptyHBFFunc) { // we are closing an empty page, does detector want to add something? + std::vector<char> emtyHBFFiller; // working space for optional empty HBF filler writer->emptyHBFFunc(&lastRDH, emtyHBFFiller); if (emtyHBFFiller.size()) { LOG(DEBUG) << "Adding empty HBF filler of size " << emtyHBFFiller.size() << " for " << describe(); @@ -417,9 +458,18 @@ void RawFileWriter::LinkData::addHBFPage(bool stop) RDHUtils::setPacketCounter(rdhCopy, packetCounter++); RDHUtils::setPageCounter(rdhCopy, pageCnt++); RDHUtils::setStop(rdhCopy, stop); - RDHUtils::setOffsetToNext(rdhCopy, sizeof(RDHAny)); - RDHUtils::setMemorySize(rdhCopy, sizeof(RDHAny)); + std::vector<char> userData; + int sz = sizeof(RDHAny); + if (stop && writer->newRDHFunc) { // detector may want to write something in closing page + writer->newRDHFunc(&rdhCopy, emptyPage, userData); + sz += userData.size(); + } + RDHUtils::setOffsetToNext(rdhCopy, sz); + RDHUtils::setMemorySize(rdhCopy, sz); lastRDHoffset = pushBack(rdhCopy); // entry of the new RDH + if (!userData.empty()) { + pushBack(userData.data(), userData.size()); + } } if (stop) { if (RDHUtils::getTriggerType(rdhCopy) & o2::trigger::TF) { @@ -563,8 +613,7 @@ void RawFileWriter::LinkData::fillEmptyHBHs(const IR& ir, bool dataAdded) std::string RawFileWriter::LinkData::describe() const { std::stringstream ss; - ss << "Link SubSpec=0x" << std::hex << std::setw(8) << std::setfill('0') - << RDHUtils::getSubSpec(rdhCopy) << std::dec + ss << "Link SubSpec=0x" << std::hex << std::setw(8) << std::setfill('0') << subspec << std::dec << '(' << std::setw(3) << int(RDHUtils::getCRUID(rdhCopy)) << ':' << std::setw(2) << int(RDHUtils::getLinkID(rdhCopy)) << ':' << int(RDHUtils::getEndPointID(rdhCopy)) << ") feeID=0x" << std::hex << std::setw(4) << std::setfill('0') << RDHUtils::getFEEID(rdhCopy); return ss.str(); diff --git a/Detectors/Raw/src/rawfile-reader-workflow.cxx b/Detectors/Raw/src/rawfile-reader-workflow.cxx index 956863d1ac46e..7f1062969e6b8 100644 --- a/Detectors/Raw/src/rawfile-reader-workflow.cxx +++ b/Detectors/Raw/src/rawfile-reader-workflow.cxx @@ -27,10 +27,13 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) options.push_back(ConfigParamSpec{"min-tf", VariantType::Int64, 0L, {"min TF ID to process"}}); options.push_back(ConfigParamSpec{"max-tf", VariantType::Int64, 0xffffffffL, {"max TF ID to process"}}); options.push_back(ConfigParamSpec{"loop", VariantType::Int, 1, {"loop N times (infinite for N<0)"}}); - options.push_back(ConfigParamSpec{"message-per-tf", VariantType::Bool, false, {"send TF of each link as a single FMQ message rather than multipart with message per HB"}}); - options.push_back(ConfigParamSpec{"output-per-link", VariantType::Bool, false, {"send message per Link rather than per FMQ output route"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); - options.push_back(ConfigParamSpec{"buffer-size", VariantType::Int64, 1024L * 1024L, {"buffer size for files preprocessing"}}); + options.push_back(ConfigParamSpec{"buffer-size", VariantType::Int64, 5 * 1024L, {"buffer size for files preprocessing"}}); + options.push_back(ConfigParamSpec{"super-page-size", VariantType::Int64, 1024L * 1024L, {"super-page size for FMQ parts definition"}}); + options.push_back(ConfigParamSpec{"part-per-hbf", VariantType::Bool, false, {"FMQ parts per superpage (default) of HBF"}}); + options.push_back(ConfigParamSpec{"raw-channel-config", VariantType::String, "", {"optional raw FMQ channel for non-DPL output"}}); + options.push_back(ConfigParamSpec{"cache-data", VariantType::Bool, false, {"cache data at 1st reading, may require excessive memory!!!"}}); + options.push_back(ConfigParamSpec{"detect-tf0", VariantType::Bool, false, {"autodetect HBFUtils start Orbit/BC from 1st TF seen"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}); // options for error-check suppression @@ -52,9 +55,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) uint32_t maxTF = uint32_t(configcontext.options().get<int64_t>("max-tf")); uint32_t minTF = uint32_t(configcontext.options().get<int64_t>("min-tf")); uint64_t buffSize = uint64_t(configcontext.options().get<int64_t>("buffer-size")); - auto tfAsMessage = configcontext.options().get<bool>("message-per-tf"); - auto outPerRoute = !configcontext.options().get<bool>("output-per-link"); - + uint64_t spSize = uint64_t(configcontext.options().get<int64_t>("super-page-size")); + bool partPerSP = !configcontext.options().get<bool>("part-per-hbf"); + bool cache = configcontext.options().get<bool>("cache-data"); + bool autodetectTF0 = configcontext.options().get<bool>("detect-tf0"); + std::string rawChannelConfig = configcontext.options().get<std::string>("raw-channel-config"); uint32_t errmap = 0; for (int i = RawFileReader::NErrorsDefined; i--;) { auto ei = RawFileReader::ErrTypes(i); @@ -66,5 +71,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get<std::string>("configKeyValues")); uint32_t delay_us = uint32_t(1e6 * configcontext.options().get<float>("delay")); // delay in microseconds - return std::move(o2::raw::getRawFileReaderWorkflow(inifile, tfAsMessage, outPerRoute, loop, delay_us, errmap, minTF, maxTF, buffSize)); + return std::move(o2::raw::getRawFileReaderWorkflow(inifile, loop, delay_us, errmap, minTF, maxTF, partPerSP, cache, autodetectTF0, spSize, buffSize, rawChannelConfig)); } diff --git a/Detectors/Raw/src/rawfileCheck.cxx b/Detectors/Raw/src/rawfileCheck.cxx index 7f2400acec9c9..cb29425cc7250 100644 --- a/Detectors/Raw/src/rawfileCheck.cxx +++ b/Detectors/Raw/src/rawfileCheck.cxx @@ -39,6 +39,7 @@ int main(int argc, char* argv[]) desc_add_option("verbosity,v", bpo::value<int>()->default_value(reader.getVerbosity()), "1: long report, 2 or 3: print or dump all RDH"); desc_add_option("spsize,s", bpo::value<int>()->default_value(reader.getNominalSPageSize()), "nominal super-page size in bytes"); desc_add_option("buffer-size,b", bpo::value<size_t>()->default_value(reader.getNominalSPageSize()), "buffer size for files preprocessing"); + desc_add_option("detect-tf0", "autodetect HBFUtils start Orbit/BC from 1st TF seen"); desc_add_option("rorc", "impose RORC as default detector mode"); desc_add_option("configKeyValues", bpo::value(&configKeyValues)->default_value(""), "semicolon separated key=value strings"); for (int i = 0; i < RawFileReader::NErrorsDefined; i++) { @@ -84,13 +85,14 @@ int main(int argc, char* argv[]) RawFileReader::RDH rdh; LOG(INFO) << "RawDataHeader v" << int(rdh.version) << " is assumed"; - o2::raw::RawFileReader::ReadoutCardType rocard = vm.count("rorc") ? o2::raw::RawFileReader::ReadoutCardType::RORC : o2::raw::RawFileReader::ReadoutCardType::CRU; + RawFileReader::ReadoutCardType rocard = vm.count("rorc") ? o2::raw::RawFileReader::ReadoutCardType::RORC : o2::raw::RawFileReader::ReadoutCardType::CRU; reader.setVerbosity(vm["verbosity"].as<int>()); reader.setNominalSPageSize(vm["spsize"].as<int>()); reader.setMaxTFToRead(vm["max-tf"].as<uint32_t>()); reader.setBufferSize(vm["buffer-size"].as<size_t>()); reader.setDefaultReadoutCardType(rocard); + reader.setTFAutodetect(vm.count("detect-tf0") ? RawFileReader::FirstTFDetection::Pending : RawFileReader::FirstTFDetection::Disabled); uint32_t errmap = 0; for (int i = RawFileReader::NErrorsDefined; i--;) { auto ei = RawFileReader::ErrTypes(i); diff --git a/Detectors/Raw/src/rawfileSplit.cxx b/Detectors/Raw/src/rawfileSplit.cxx new file mode 100644 index 0000000000000..4e5e8f2adfafe --- /dev/null +++ b/Detectors/Raw/src/rawfileSplit.cxx @@ -0,0 +1,211 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 rawfileCheck.h +/// @author ruben.shahoyan@cern.ch +/// @brief Checker for raw data conformity with CRU format + +#include "DetectorsRaw/RawFileReader.h" +#include "DetectorsRaw/RawFileWriter.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/StringUtils.h" +#include "Framework/Logger.h" +#include <TStopwatch.h> +#include <boost/program_options.hpp> +#include <iostream> +#include <string> +#include <vector> +#include <TSystem.h> + +namespace bpo = boost::program_options; + +using namespace o2::raw; + +int main(int argc, char* argv[]) +{ + RawFileReader reader; + std::vector<std::string> fnames; + std::string config, configKeyValues; + bpo::variables_map vm; + bpo::options_description descOpt("Options"); + auto desc_add_option = descOpt.add_options(); + desc_add_option("help,h", "print this help message."); + desc_add_option("input-conf,c", bpo::value(&config)->default_value(""), "read input from configuration file"); + desc_add_option("max-tf,m", bpo::value<uint32_t>()->default_value(0xffffffff), " ID to read (counts from 0)"); + desc_add_option("verbosity,v", bpo::value<int>()->default_value(reader.getVerbosity()), "1: long report, 2 or 3: print or dump all RDH"); + desc_add_option("spsize,s", bpo::value<int>()->default_value(reader.getNominalSPageSize()), "nominal super-page size in bytes"); + desc_add_option("buffer-size,b", bpo::value<size_t>()->default_value(reader.getNominalSPageSize()), "buffer size for files preprocessing"); + desc_add_option("detect-tf0", "autodetect HBFUtils start Orbit/BC from 1st TF seen"); + desc_add_option("rorc", "impose RORC as default detector mode"); + desc_add_option("tfs-per-chunk,n", bpo::value<uint32_t>()->default_value(0xffffffff), " number of output TFs per chunk"); + desc_add_option("output-dir-prefix,o", bpo::value<std::string>()->default_value("./chunk"), "output directory prefix for raw data chunk (chunk ID will be added)"); + desc_add_option("file-for,f", bpo::value<std::string>()->default_value("all"), "single file per: all,cru,link"); + + desc_add_option("configKeyValues", bpo::value(&configKeyValues)->default_value(""), "semicolon separated key=value strings"); + for (int i = 0; i < RawFileReader::NErrorsDefined; i++) { + auto ei = RawFileReader::ErrTypes(i); + desc_add_option(RawFileReader::nochk_opt(ei).c_str(), RawFileReader::nochk_expl(ei).c_str()); + } + + bpo::options_description hiddenOpt("hidden"); + hiddenOpt.add_options()("files", bpo::value(&fnames)->composing(), ""); + + bpo::options_description fullOpt("cmd"); + fullOpt.add(descOpt).add(hiddenOpt); + + bpo::positional_options_description posOpt; + posOpt.add("files", -1); + + auto printHelp = [&](std::ostream& stream) { + stream << "Usage: " << argv[0] << " [options] file0 [... fileN]" << std::endl; + stream << descOpt << std::endl; + stream << " (input files are optional if config file was provided)" << std::endl; + }; + + try { + bpo::store(bpo::command_line_parser(argc, argv) + .options(fullOpt) + .positional(posOpt) + .allow_unregistered() + .run(), + vm); + bpo::notify(vm); + if (argc == 1 || vm.count("help") || (fnames.empty() && config.empty())) { + printHelp(std::cout); + return 0; + } + o2::conf::ConfigurableParam::updateFromString(configKeyValues); + } catch (const bpo::error& e) { + std::cerr << e.what() << "\n\n"; + std::cerr << "Error parsing command line arguments\n"; + printHelp(std::cerr); + return -1; + } + + RawFileReader::RDH rdh; + LOG(INFO) << "RawDataHeader v" << int(rdh.version) << " is assumed"; + + RawFileReader::ReadoutCardType rocard = vm.count("rorc") ? RawFileReader::ReadoutCardType::RORC : RawFileReader::ReadoutCardType::CRU; + + reader.setVerbosity(vm["verbosity"].as<int>()); + reader.setNominalSPageSize(vm["spsize"].as<int>()); + reader.setMaxTFToRead(vm["max-tf"].as<uint32_t>()); + reader.setBufferSize(vm["buffer-size"].as<size_t>()); + reader.setDefaultReadoutCardType(rocard); + reader.setTFAutodetect(vm.count("detect-tf0") ? RawFileReader::FirstTFDetection::Pending : RawFileReader::FirstTFDetection::Disabled); + + std::string_view fileFor = vm["file-for"].as<std::string>(); + + uint32_t errmap = 0; + for (int i = RawFileReader::NErrorsDefined; i--;) { + auto ei = RawFileReader::ErrTypes(i); + if (RawFileReader::ErrCheckDefaults[i]) { + errmap |= 0x1 << i; + } + if (vm.count(RawFileReader::nochk_opt(ei).c_str())) { // toggle + errmap ^= 0x1 << i; + } + LOG(INFO) << ((errmap & (0x1 << i)) ? "apply " : "ignore") << " check for " << RawFileReader::ErrNames[i].data(); + } + + if (!config.empty()) { + auto inp = RawFileReader::parseInput(config); + reader.loadFromInputsMap(inp); + } + + for (int i = 0; i < fnames.size(); i++) { + reader.addFile(fnames[i]); + } + + TStopwatch sw; + sw.Start(); + + reader.setCheckErrors(errmap); + reader.init(); + + sw.Print(); + int maxTFPerChunk = vm["tfs-per-chunk"].as<uint32_t>(); + std::string outDirPrefix = vm["output-dir-prefix"].as<std::string>(), outDir = ""; + int ntf = reader.getNTimeFrames(); + int nlinks = reader.getNLinks(); + std::vector<RawFileReader::PartStat> partsSP; + std::vector<char> buffer; + std::unique_ptr<RawFileWriter> writer; + int chunkID = -1; + + for (int itf = 0; itf < ntf; itf++) { + reader.setNextTFToRead(itf); + bool reinitWriter = false; + if ((itf % maxTFPerChunk) == 0) { + reinitWriter = true; + chunkID++; + } + for (int il = 0; il < nlinks; il++) { + auto& link = reader.getLink(il); + if (!link.rewindToTF(itf)) { + continue; // this link has no data for wanted TF + } + int nParts = link.getNextTFSuperPagesStat(partsSP); + for (int ip = 0; ip < nParts; ip++) { + buffer.resize(partsSP[ip].size); + auto bread = link.readNextSuperPage(buffer.data(), &partsSP[ip]); + if (bread != partsSP[ip].size) { + LOG(ERROR) << "Link " << il << " read " << bread << " bytes instead of " << partsSP[ip].size << " expected in TF=" << itf << " part=" << ip; + } + + if (reinitWriter) { + if (writer) { // generate config for previous chunk + writer->writeConfFile(writer->getOrigin().str, "RAWDATA", o2::utils::concat_string(outDir, '/', writer->getOrigin().str, "raw.cfg")); + } + outDir = o2::utils::concat_string(outDirPrefix, "_", std::to_string(chunkID)); + if (gSystem->AccessPathName(outDir.data())) { + if (gSystem->mkdir(outDir.data(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outDir; + } else { + LOG(INFO) << "created output directory " << outDir; + } + } + writer = std::make_unique<RawFileWriter>(link.origin, link.cruDetector); + writer->useRDHVersion(RDHUtils::getVersion(link.rdhl)); + reinitWriter = false; + } + if (!writer->isLinkRegistered(RDHUtils::getSubSpec(RDHUtils::getCRUID(link.rdhl), RDHUtils::getLinkID(link.rdhl), RDHUtils::getEndPointID(link.rdhl), RDHUtils::getFEEID(link.rdhl)))) { // register the output link + std::string outFileName; + + if (fileFor == "all") { // single file for all links + outFileName = o2::utils::concat_string(outDir, "/", fileFor, ".raw"); + } else if (fileFor == "cru") { + outFileName = o2::utils::concat_string(outDir, "/", fileFor, "_", std::to_string(RDHUtils::getCRUID(link.rdhl)), ".raw"); + } else if (fileFor == "link") { + outFileName = o2::utils::concat_string(outDir, "/", fileFor, + "_", std::to_string(RDHUtils::getLinkID(link.rdhl)), + "_cru", std::to_string(RDHUtils::getCRUID(link.rdhl)), + "_ep", std::to_string(RDHUtils::getEndPointID(link.rdhl)), + "_feeid", std::to_string(RDHUtils::getFEEID(link.rdhl)), ".raw"); + } else { + throw std::runtime_error("invalid option provided for file grouping"); + } + + writer->registerLink(link.rdhl, outFileName); + } + + auto& linkW = writer->getLinkWithSubSpec(link.rdhl); + auto& outF = writer->getOutputFileForLink(linkW); + outF.write(buffer.data(), bread); + } + } + } + if (writer) { // generate config for previous chunk + writer->writeConfFile(writer->getOrigin().str, "RAWDATA", o2::utils::concat_string(outDir, '/', writer->getOrigin().str, "raw.cfg")); + } + writer.reset(); + + return 0; +} diff --git a/Detectors/Raw/test/testRawReaderWriter.cxx b/Detectors/Raw/test/testRawReaderWriter.cxx index c816e59b49704..d11c7aba41e19 100644 --- a/Detectors/Raw/test/testRawReaderWriter.cxx +++ b/Detectors/Raw/test/testRawReaderWriter.cxx @@ -81,6 +81,8 @@ struct TestRawWriter { // simple class to create detector payload for multiple l writer.setEmptyPageCallBack(this); // we want the writer to ask the detector code what to put in empty HBFs } writer.setCarryOverCallBack(this); // we want that writer to ask the detector code how to split large payloads + + writer.setApplyCarryOverToLastPage(true); // call CarryOver method also for the last chunk } //_________________________________________________________________ @@ -155,10 +157,17 @@ struct TestRawWriter { // simple class to create detector payload for multiple l { // how we want to split the large payloads. The data is the full payload which was sent for writing and // it is already equiped with header and trailer + static int verboseCount = 0; + if (maxSize <= RDHUtils::GBTWord) { // do not carry over trailer or header only return 0; } + int bytesLeft = data.size() - (ptr - &data[0]); + bool lastPage = bytesLeft <= maxSize; + if (verboseCount++ < 100) { + LOG(INFO) << "Carry-over method for chunk of size " << bytesLeft << " is called, MaxSize = " << maxSize << (lastPage ? " : last chunk being processed!" : ""); + } // here we simply copy the header/trailer of the payload to every CRU page of this payload header.resize(RDHUtils::GBTWord); std::memcpy(header.data(), &data[0], RDHUtils::GBTWord); @@ -166,7 +175,10 @@ struct TestRawWriter { // simple class to create detector payload for multiple l std::memcpy(trailer.data(), &data[data.size() - RDHUtils::GBTWord], RDHUtils::GBTWord); // since we write an extra GBT word (trailer) in the end of the CRU page, we ask to write // not the block ptr : ptr+maxSize, but ptr : ptr+maxSize - GBTWord; - int sz = maxSize - RDHUtils::GBTWord; + int sz = maxSize; // if the method is called for the last page, then the trailer is overwritten !!! + if (!lastPage) { // otherwise it is added incrementally, so its size must be accounted + sz -= trailer.size(); + } return sz; } }; diff --git a/Detectors/TOF/CMakeLists.txt b/Detectors/TOF/CMakeLists.txt index 7ac94855f7b2d..3afd67c327f02 100644 --- a/Detectors/TOF/CMakeLists.txt +++ b/Detectors/TOF/CMakeLists.txt @@ -15,5 +15,6 @@ add_subdirectory(reconstruction) add_subdirectory(compression) if(BUILD_TESTING) add_subdirectory(prototyping) + add_subdirectory(calibration/macros) endif() add_subdirectory(workflow) diff --git a/Detectors/TOF/base/include/TOFBase/Digit.h b/Detectors/TOF/base/include/TOFBase/Digit.h index 0802800f34735..a777a989995bd 100644 --- a/Detectors/TOF/base/include/TOFBase/Digit.h +++ b/Detectors/TOF/base/include/TOFBase/Digit.h @@ -15,6 +15,7 @@ #include "Rtypes.h" #include "TOFBase/Geo.h" #include "CommonDataFormat/RangeReference.h" +#include "CommonDataFormat/InteractionRecord.h" #include <gsl/span> #include <boost/serialization/base_object.hpp> // for base_object @@ -30,14 +31,19 @@ class Digit public: Digit() = default; - Digit(Int_t channel, Int_t tdc, Int_t tot, Int_t bc, Int_t label = -1, Int_t triggerorbit = 0, Int_t triggerbunch = 0); + Digit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label = -1, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); + Digit(Int_t channel, Int_t tdc, Int_t tot, uint32_t orbit, uint16_t bc, Int_t label = -1, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); ~Digit() = default; /// Get global ordering key made of - static ULong64_t getOrderingKey(Int_t channel, Int_t bc, Int_t /*tdc*/) + static ULong64_t getOrderingKey(Int_t channel, uint64_t bc, Int_t /*tdc*/) { return ((static_cast<ULong64_t>(bc) << 18) + channel); // channel in the least significant bits; then shift by 18 bits (which cover the total number of channels) to write the BC number } + ULong64_t getOrderingKey() + { + return getOrderingKey(mChannel, mIR.toLong(), mTDC); + } Int_t getChannel() const { return mChannel; } void setChannel(Int_t channel) { mChannel = channel; } @@ -48,8 +54,8 @@ class Digit uint16_t getTOT() const { return mTOT; } void setTOT(uint16_t tot) { mTOT = tot; } - Int_t getBC() const { return mBC; } - void setBC(Int_t bc) { mBC = bc; } + uint64_t getBC() const { return mIR.toLong(); } + void setBC(uint64_t bc) { mIR.setFromLong(bc); } Int_t getLabel() const { return mLabel; } void setLabel(Int_t label) { mLabel = label; } @@ -62,7 +68,7 @@ class Digit Bool_t isUsedInCluster() const { return mIsUsedInCluster; } - void setIsUsedInCluster() { mIsUsedInCluster = kTRUE; } + void setIsUsedInCluster(bool val = true) { mIsUsedInCluster = val; } Int_t getElectronicIndex() const { return mElectronIndex; } void setElectronicIndex(Int_t ind) { mElectronIndex = ind; } @@ -78,27 +84,27 @@ class Digit void setIsProblematic(bool flag) { mIsProblematic = flag; } bool isProblematic() const { return mIsProblematic; } - void setTriggerOrbit(int value) { mTriggerOrbit = value; } - int getTriggerOrbit() const { return mTriggerOrbit; } - void setTriggerBunch(int value) { mTriggerBunch = value; } - int getTriggerBunch() const { return mTriggerBunch; } + void setTriggerOrbit(uint32_t value) { mTriggerOrbit = value; } + uint32_t getTriggerOrbit() const { return mTriggerOrbit; } + void setTriggerBunch(uint16_t value) { mTriggerBunch = value; } + uint16_t getTriggerBunch() const { return mTriggerBunch; } private: friend class boost::serialization::access; - Double_t mCalibratedTime; //!< time of the digits after calibration (not persistent; it will be filled during clusterization) Int_t mChannel; ///< TOF channel index uint16_t mTDC; ///< TDC bin number uint16_t mTOT; ///< TOT bin number - Int_t mBC; ///< Bunch Crossing + InteractionRecord mIR{0, 0}; ///< InteractionRecord (orbit and bc) when digit occurs Int_t mLabel; ///< Index of the corresponding entry in the MC label array + Double_t mCalibratedTime; //!< time of the digits after calibration (not persistent; it will be filled during clusterization) Int_t mElectronIndex; //!/< index in electronic format - uint16_t mTriggerOrbit = 0; //!< orbit id of trigger event + uint32_t mTriggerOrbit = 0; //!< orbit id of trigger event // RS: orbit must be 32bits long uint16_t mTriggerBunch = 0; //!< bunch id of trigger event Bool_t mIsUsedInCluster; //!/< flag to declare that the digit was used to build a cluster Bool_t mIsProblematic = false; //!< flag to tell whether the channel of the digit was problemati; not persistent; default = ok - ClassDefNV(Digit, 2); + ClassDefNV(Digit, 4); }; std::ostream& operator<<(std::ostream& stream, const Digit& dig); @@ -107,6 +113,44 @@ struct ReadoutWindowData { // 1st entry and number of entries in the full vector of digits // for given trigger (or BC or RO frame) o2::dataformats::RangeReference<int, int> ref; + o2::dataformats::RangeReference<int, int> refDiagnostic; + + // crate info for diagnostic patterns + int mNdiaCrate[Geo::kNCrate] = {0}; + int mDeltaBCCrate[Geo::kNCrate] = {0}; + int mDeltaEventCounterCrate[Geo::kNCrate] = {0}; + + InteractionRecord mFirstIR{0, 0}; + int mEventCounter = 0; + + const InteractionRecord& getBCData() const { return mFirstIR; } + + void setEmptyCrate(int crate) { mNdiaCrate[crate] = -1; } + bool isEmptyCrate(int crate) const { return (mNdiaCrate[crate] == -1); } + void addedDiagnostic(int crate) { mNdiaCrate[crate]++; } + void setDiagnosticInCrate(int crate, int val) { mNdiaCrate[crate] = val; } + int getDiagnosticInCrate(int crate) const + { + if (isEmptyCrate(crate)) { + return 0; + } else { + return mNdiaCrate[crate]; + } + } + + void setBCData(int orbit, int bc) + { + mFirstIR.orbit = orbit; + mFirstIR.bc = bc; + } + void setBCData(InteractionRecord& src) + { + mFirstIR.orbit = src.orbit; + mFirstIR.bc = src.bc; + } + void SetBC(int bc) { mFirstIR.bc = bc; } + void SetOrbit(int orbit) { mFirstIR.orbit = orbit; } + gsl::span<const Digit> getBunchChannelData(const gsl::span<const Digit> tfdata) const { // extract the span of channel data for this readout window from the whole TF data @@ -118,12 +162,50 @@ struct ReadoutWindowData { { ref.setFirstEntry(first); ref.setEntries(ne); + refDiagnostic.setFirstEntry(0); + refDiagnostic.setEntries(0); } int first() const { return ref.getFirstEntry(); } int size() const { return ref.getEntries(); } + int firstDia() const { return refDiagnostic.getFirstEntry(); } + int sizeDia() const { return refDiagnostic.getEntries(); } + + void setFirstEntry(int first) { ref.setFirstEntry(first); } + void setNEntries(int ne) { ref.setEntries(ne); } + void setFirstEntryDia(int first) { refDiagnostic.setFirstEntry(first); } + void setNEntriesDia(int ne) { refDiagnostic.setEntries(ne); } + + void setEventCounter(int ev) { mEventCounter = ev; } + void setDeltaEventCounterCrate(int crate, int ev) { mDeltaEventCounterCrate[crate] = ev; } + int getEventCounter() const { return mEventCounter; } + int getDeltaEventCounterCrate(int crate) const { return mDeltaEventCounterCrate[crate]; } + void setDeltaBCCrate(int crate, int bc) { mDeltaBCCrate[crate] = bc; } + int getDeltaBCCrate(int crate) const { return mDeltaBCCrate[crate]; } + + ClassDefNV(ReadoutWindowData, 4); +}; + +struct DigitHeader { + int mCountsCrate[Geo::kNCrate] = {0}; + int mNumberOfCrates[Geo::kNCrate + 1] = {0}; + int mCountsRow = 0; - ClassDefNV(ReadoutWindowData, 1); + void clear() + { + memset(mCountsCrate, 0, Geo::kNCrate * 4); + memset(mNumberOfCrates, 0, (Geo::kNCrate + 1) * 4); + mCountsRow = 0; + } + DigitHeader() { clear(); } + void addRow() { mCountsRow++; } + int getNRow() const { return mCountsRow; } + void crateSeen(int crate) { mCountsCrate[crate]++; } + void numCratesSeen(int ncrates) { mNumberOfCrates[ncrates]++; } + int getCrateCounts(int crate) const { return mCountsCrate[crate]; } + int numCratesCounts(int ncrates) const { return mNumberOfCrates[ncrates]; } + + ClassDefNV(DigitHeader, 2); }; } // namespace tof diff --git a/Detectors/TOF/base/include/TOFBase/Geo.h b/Detectors/TOF/base/include/TOFBase/Geo.h index 1b26ca3dc20b3..f98be5d4d012d 100644 --- a/Detectors/TOF/base/include/TOFBase/Geo.h +++ b/Detectors/TOF/base/include/TOFBase/Geo.h @@ -43,6 +43,7 @@ class Geo static void getPos(Int_t* det, Float_t* pos); static void getVolumePath(const Int_t* ind, Char_t* path); static Int_t getStripNumberPerSM(Int_t iplate, Int_t istrip); + static void getStripAndModule(Int_t iStripPerSM, Int_t& iplate, Int_t& istrip); // Return the module and strip per module corresponding to the strip number per SM static Float_t getAngles(Int_t iplate, Int_t istrip) { return ANGLES[iplate][istrip]; } static Float_t getHeights(Int_t iplate, Int_t istrip) { return HEIGHTS[iplate][istrip]; } @@ -62,10 +63,10 @@ class Geo static constexpr Int_t RAW_PAGE_MAX_SIZE = 8192; - static constexpr Float_t BC_TIME = o2::constants::lhc::LHCBunchSpacingNS; // bunch crossing in ns - static constexpr Float_t BC_TIME_INV = 1. / BC_TIME; // inv bunch crossing in ns - static constexpr Float_t BC_TIME_INPS = BC_TIME * 1000; // bunch crossing in ps - static constexpr Float_t BC_TIME_INPS_INV = 1. / BC_TIME_INPS; // inv bunch crossing in ps + 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 static constexpr int BC_IN_ORBIT = o2::constants::lhc::LHCMaxBunches; // N. bunch crossing in 1 orbit static constexpr Int_t NPADX = 48; diff --git a/Detectors/TOF/base/include/TOFBase/Strip.h b/Detectors/TOF/base/include/TOFBase/Strip.h index 015ed765e029e..7b38c757c9e53 100644 --- a/Detectors/TOF/base/include/TOFBase/Strip.h +++ b/Detectors/TOF/base/include/TOFBase/Strip.h @@ -22,7 +22,7 @@ #include <map> #include <sstream> #include <vector> -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" namespace o2 { @@ -62,7 +62,7 @@ class Strip /// @param index New chip index void setStripIndex(Int_t index) { mStripIndex = index; } void init(Int_t index) - //, const o2::Transform3D* mat) + //, const o2::math_utils::Transform3D* mat) { mStripIndex = index; //mMat = mat; @@ -79,7 +79,7 @@ class Strip /// reset points container o2::tof::Digit* findDigit(ULong64_t key); - Int_t addDigit(Int_t channel, Int_t tdc, Int_t tot, Int_t bc, Int_t lbl = 0, int triggerorbit = 0, int triggerbunch = 0); // returns the MC label + Int_t addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lbl = 0, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); // returns the MC label void fillOutputContainer(std::vector<o2::tof::Digit>& digits); diff --git a/Detectors/TOF/base/include/TOFBase/WindowFiller.h b/Detectors/TOF/base/include/TOFBase/WindowFiller.h index aedf8820ebc74..4c2f43764cd9c 100644 --- a/Detectors/TOF/base/include/TOFBase/WindowFiller.h +++ b/Detectors/TOF/base/include/TOFBase/WindowFiller.h @@ -15,6 +15,7 @@ #include "TOFBase/Digit.h" #include "TOFBase/Strip.h" #include "DetectorsRaw/HBFUtils.h" +#include "CommonDataFormat/InteractionRecord.h" namespace o2 { @@ -23,8 +24,22 @@ namespace tof class WindowFiller { - public: + struct PatternData { + uint32_t pattern; + int icrate; + + unsigned long row; + + PatternData(uint32_t patt = 0, int icr = 0, unsigned long rw = 0) : pattern(patt), icrate(icr), row(rw) {} + }; + + struct CrateHeaderData { + int32_t bc[Geo::kNCrate] = {-1}; + uint32_t eventCounter[Geo::kNCrate] = {0}; + CrateHeaderData() { memset(bc, -1, Geo::kNCrate * 4); } + }; + WindowFiller() { initObj(); }; ~WindowFiller() = default; @@ -32,15 +47,17 @@ class WindowFiller void reset(); - Int_t getCurrentReadoutWindow() const { return mReadoutWindowCurrent; } - void setCurrentReadoutWindow(Double_t value) { mReadoutWindowCurrent = value; } - void setEventTime(double value) + uint64_t getCurrentReadoutWindow() const { return mReadoutWindowCurrent; } + void setCurrentReadoutWindow(uint64_t value) { mReadoutWindowCurrent = value; } + void setEventTime(InteractionTimeRecord value) { mEventTime = value; } std::vector<Digit>* getDigitPerTimeFrame() { return &mDigitsPerTimeFrame; } std::vector<ReadoutWindowData>* getReadoutWindowData() { return &mReadoutWindowData; } + std::vector<ReadoutWindowData>* getReadoutWindowDataFiltered() { return &mReadoutWindowDataFiltered; } + DigitHeader& getDigitHeader() { return mDigitHeader; } void fillOutputContainer(std::vector<Digit>& digits); void flushOutputContainer(std::vector<Digit>& digits); // flush all residual buffered data @@ -49,16 +66,32 @@ class WindowFiller void resizeVectorFutureDigit(int size) { mFutureDigits.resize(size); } + void setFirstIR(const o2::InteractionRecord& ir) { mFirstIR = ir; } + + void maskNoiseRate(int val) { mMaskNoiseRate = val; } + + void clearCounts() + { + memset(mChannelCounts, 0, o2::tof::Geo::NCHANNELS * sizeof(mChannelCounts[0])); + } + + std::vector<uint32_t>& getPatterns() { return mPatterns; } + void addPattern(const uint32_t val, int icrate, int orbit, int bc) { mCratePatterns.emplace_back(val, icrate, orbit * 3 + (bc + 100) / Geo::BC_IN_WINDOW); } + void addCrateHeaderData(unsigned long orbit, int crate, int32_t bc, uint32_t eventCounter); + protected: // info TOF timewindow - Int_t mReadoutWindowCurrent = 0; - Int_t mFirstOrbit = 0; - Int_t mFirstBunch = 0; - Double_t mEventTime; + uint64_t mReadoutWindowCurrent = 0; + InteractionRecord mFirstIR{0, 0}; // reference IR (1st IR of the timeframe) + InteractionTimeRecord mEventTime; bool mContinuous = true; bool mFutureToBeSorted = false; + // only needed from Decoder + int mMaskNoiseRate = -1; + int mChannelCounts[o2::tof::Geo::NCHANNELS]; // count of channel hits in the current TF (if MaskNoiseRate enabled) + // digit info //std::vector<Digit>* mDigits; @@ -66,6 +99,7 @@ class WindowFiller std::vector<Digit> mDigitsPerTimeFrame; std::vector<ReadoutWindowData> mReadoutWindowData; + std::vector<ReadoutWindowData> mReadoutWindowDataFiltered; int mIcurrentReadoutWindow = 0; @@ -77,13 +111,21 @@ class WindowFiller // arrays with digit and MCLabels out of the current readout windows (stored to fill future readout window) std::vector<Digit> mFutureDigits; - void fillDigitsInStrip(std::vector<Strip>* strips, int channel, int tdc, int tot, int nbc, UInt_t istrip, Int_t triggerorbit = 0, Int_t triggerbunch = 0); + std::vector<uint32_t> mPatterns; + std::vector<uint64_t> mErrors; + + std::vector<PatternData> mCratePatterns; + std::vector<CrateHeaderData> mCrateHeaderData; + + DigitHeader mDigitHeader; + + void fillDigitsInStrip(std::vector<Strip>* strips, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); // void fillDigitsInStrip(std::vector<Strip>* strips, o2::dataformats::MCTruthContainer<o2::tof::MCLabel>* mcTruthContainer, int channel, int tdc, int tot, int nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID); void checkIfReuseFutureDigits(); void checkIfReuseFutureDigitsRO(); - void insertDigitInFuture(Int_t channel, Int_t tdc, Int_t tot, Int_t bc, Int_t label = 0, Int_t triggerorbit = 0, Int_t triggerbunch = 0) + void insertDigitInFuture(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label = 0, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0) { mFutureDigits.emplace_back(channel, tdc, tot, bc, label, triggerorbit, triggerbunch); mFutureToBeSorted = true; @@ -106,7 +148,7 @@ class WindowFiller return true; } - ClassDefNV(WindowFiller, 1); + ClassDefNV(WindowFiller, 2); }; } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/base/src/Digit.cxx b/Detectors/TOF/base/src/Digit.cxx index 93910bcb70efc..e1de38006e59d 100644 --- a/Detectors/TOF/base/src/Digit.cxx +++ b/Detectors/TOF/base/src/Digit.cxx @@ -16,16 +16,21 @@ using namespace o2::tof; ClassImp(o2::tof::Digit); -Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, Int_t bc, Int_t label, Int_t triggerorbit, Int_t triggerbunch) - : mChannel(channel), mTDC(tdc), mTOT(tot), mBC(bc), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE) +Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label, uint32_t triggerorbit, uint16_t triggerbunch) + : mChannel(channel), mTDC(tdc), mTOT(tot), mIR(0, 0), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE) +{ + mIR.setFromLong(bc); +} +//______________________________________________________________________ +Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, uint32_t orbit, uint16_t bc, Int_t label, uint32_t triggerorbit, uint16_t triggerbunch) + : mChannel(channel), mTDC(tdc), mTOT(tot), mIR(bc, orbit), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE) { } - //______________________________________________________________________ void Digit::printStream(std::ostream& stream) const { - stream << "TOF Digit: Channel " << mChannel << " TDC " << mTDC << " TOT " << mTOT << "Bunch Crossing index" << mBC << " Label " << mLabel << "\n"; + stream << "TOF Digit: Channel " << mChannel << " TDC " << mTDC << " TOT " << mTOT << "Bunch Crossing index" << mIR.toLong() << " Label " << mLabel << "\n"; } //______________________________________________________________________ @@ -63,16 +68,17 @@ void Digit::getPhiAndEtaIndex(int& phi, int& eta) const chan = getChannel(); // note that inside the strip the digits are ordered per channel number Geo::getVolumeIndices(chan, detId); // Get volume index from channel index eta = detId[2] /*strip*/ * 2 + detId[3] /*pad Z*/; - if (detId[1] /*module*/ == 0) + if (detId[1] /*module*/ == 0) { eta += 0; - else if (detId[1] == 1) + } else if (detId[1] == 1) { eta += 38; - else if (detId[1] == 2) + } else if (detId[1] == 2) { eta += 76; - else if (detId[1] == 3) + } else if (detId[1] == 3) { eta += 106; - else if (detId[1] == 4) + } else if (detId[1] == 4) { eta += 144; + } phi = detId[0] /*phi sector*/ * 48 + detId[4] /*pad x*/; return; diff --git a/Detectors/TOF/base/src/Geo.cxx b/Detectors/TOF/base/src/Geo.cxx index ab5cc797b4a2b..6bad7895f0c09 100644 --- a/Detectors/TOF/base/src/Geo.cxx +++ b/Detectors/TOF/base/src/Geo.cxx @@ -45,8 +45,9 @@ void Geo::Init() for (Int_t iplate = 0; iplate < NPLATES; iplate++) { det[1] = iplate; - if (iplate == 2 && (isector == 13 || isector == 14 || isector == 15)) + if (iplate == 2 && (isector == 13 || isector == 14 || isector == 15)) { continue; // PHOS HOLES + } for (Int_t istrip = 0; istrip < NSTRIPC; istrip++) { // maximum number of strip is 19 for plate B and C det[2] = istrip; @@ -72,8 +73,9 @@ void Geo::Init() Double_t rotationAngles[6] = {90., 90. /*+ (isector + 0.5) * PHISEC*/, 0., 0., 90., 0 /* + (isector + 0.5) * PHISEC*/}; - for (Int_t ii = 0; ii < 6; ii++) + for (Int_t ii = 0; ii < 6; ii++) { rotationAngles[ii] *= TMath::DegToRad(); + } for (Int_t isector = 0; isector < NSECTORS; isector++) { rotationAngles[5] = ((isector + 0.5) * PHISEC) * TMath::DegToRad(); @@ -162,23 +164,30 @@ void Geo::getVolumePath(const Int_t* ind, Char_t* path) Int_t iplate = ind[1]; Int_t istrip = ind[2]; - if (iplate == 0) + if (iplate == 0) { icopy = istrip; - if (iplate == 1) + } + if (iplate == 1) { icopy = istrip + NSTRIPC; - if (iplate == 2) + } + if (iplate == 2) { icopy = istrip + NSTRIPC + NSTRIPB; - if (iplate == 3) + } + if (iplate == 3) { icopy = istrip + NSTRIPC + NSTRIPB + NSTRIPA; - if (iplate == 4) + } + if (iplate == 4) { icopy = istrip + NSTRIPC + 2 * NSTRIPB + NSTRIPA; + } icopy++; snprintf(string2, kSize, "FTOA_0/FLTA_0/FSTR_%i", icopy); if (fgHoles && (sector == 13 || sector == 14 || sector == 15)) { - if (iplate < 2) + if (iplate < 2) { snprintf(string2, kSize, "FTOB_0/FLTB_0/FSTR_%i", icopy); - if (iplate > 2) + } + if (iplate > 2) { snprintf(string2, kSize, "FTOC_0/FLTC_0/FSTR_%i", icopy); + } } Int_t padz = ind[3] + 1; @@ -193,8 +202,9 @@ void Geo::getPos(Int_t* det, Float_t* pos) // Returns space point coor (x,y,z) (cm) for Detector // Indices (iSect,iPlate,iStrip,iPadZ,iPadX) // - if (mToBeIntit) + if (mToBeIntit) { Init(); + } // printf("TOFDBG: %d, %d, %d, %d, %d -> %f %f %f\n", det[0], det[1], det[2], det[3], det[4], mPadPosition[det[0]][det[1]][det[2]][det[3]][det[4]][0], mPadPosition[det[0]][det[1]][det[2]][det[3]][det[4]][1], mPadPosition[det[0]][det[1]][det[2]][det[3]][det[4]][2]); pos[0] = mPadPosition[det[0]][det[1]][det[2]][det[3]][det[4]][0]; @@ -208,12 +218,14 @@ void Geo::getDetID(Float_t* pos, Int_t* det) // Returns Detector Indices (iSect,iPlate,iStrip,iPadZ,iPadX) // space point coor (x,y,z) (cm) - if (mToBeIntit) + if (mToBeIntit) { Init(); + } Float_t posLocal[3]; - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { posLocal[ii] = pos[ii]; + } det[0] = getSector(posLocal); @@ -402,8 +414,9 @@ Int_t Geo::fromPlateToStrip(Float_t* pos, Int_t iplate) step[2] = -0.5 * NPADZ * ZPAD; translate(posLoc2, step); - for (Int_t jj = 0; jj < 3; jj++) + for (Int_t jj = 0; jj < 3; jj++) { pos[jj] = posLoc2[jj]; + } return istrip; } @@ -442,11 +455,13 @@ void Geo::getPadDxDyDz(const Float_t* pos, Int_t* det, Float_t* DeltaPos) // // Returns the x coordinate in the Pad reference frame // - if (mToBeIntit) + if (mToBeIntit) { Init(); + } - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { DeltaPos[ii] = pos[ii]; + } det[0] = getSector(DeltaPos); fromGlobalToSector(DeltaPos, det[0]); @@ -488,41 +503,45 @@ Int_t Geo::getPlate(const Float_t* pos) deltaRHOmax = (RMAX - RMIN) * 0.5 - MODULEWALLTHICKNESS - 2. * LENGTHEXINMODBORDER; // old 5.35, new 4.8 if (deltaRhoLoc > deltaZetaLoc * deltaRHOmax / (INTERCENTRMODBORDER2 - INTERCENTRMODBORDER1)) { - if (zLocal < 0) + if (zLocal < 0) { iPlate = 0; - else + } else { iPlate = 4; + } } else { - if (zLocal < 0) + if (zLocal < 0) { iPlate = 1; - else + } else { iPlate = 3; + } } } else if (TMath::Abs(zLocal) >= INTERCENTRMODBORDER1 && TMath::Abs(zLocal) <= INTERCENTRMODBORDER2) { deltaRhoLoc -= LENGTHINCEMODBORDERD; deltaZetaLoc = deltaZetaLoc - INTERCENTRMODBORDER1; deltaRHOmax = (RMAX - RMIN) * 0.5 - MODULEWALLTHICKNESS - 2. * LENGTHINCEMODBORDERD; // old 0.39, new 0.2 - if (deltaRhoLoc > deltaZetaLoc * deltaRHOmax / (INTERCENTRMODBORDER2 - INTERCENTRMODBORDER1)) + if (deltaRhoLoc > deltaZetaLoc * deltaRHOmax / (INTERCENTRMODBORDER2 - INTERCENTRMODBORDER1)) { iPlate = 2; - else { - if (zLocal < 0) + } else { + if (zLocal < 0) { iPlate = 1; - else + } else { iPlate = 3; + } } } - if (zLocal > -ZLENA * 0.5 && zLocal < -EXTERINTERMODBORDER2) + if (zLocal > -ZLENA * 0.5 && zLocal < -EXTERINTERMODBORDER2) { iPlate = 0; - else if (zLocal > -EXTERINTERMODBORDER1 && zLocal < -INTERCENTRMODBORDER2) + } else if (zLocal > -EXTERINTERMODBORDER1 && zLocal < -INTERCENTRMODBORDER2) { iPlate = 1; - else if (zLocal > -INTERCENTRMODBORDER1 && zLocal < INTERCENTRMODBORDER1) + } else if (zLocal > -INTERCENTRMODBORDER1 && zLocal < INTERCENTRMODBORDER1) { iPlate = 2; - else if (zLocal > INTERCENTRMODBORDER2 && zLocal < EXTERINTERMODBORDER1) + } else if (zLocal > INTERCENTRMODBORDER2 && zLocal < EXTERINTERMODBORDER1) { iPlate = 3; - else if (zLocal > EXTERINTERMODBORDER2 && zLocal < ZLENA * 0.5) + } else if (zLocal > EXTERINTERMODBORDER2 && zLocal < ZLENA * 0.5) { iPlate = 4; + } return iPlate; } @@ -534,10 +553,11 @@ Int_t Geo::getPadZ(const Float_t* pos) // Int_t iPadZ = (Int_t)(pos[2] / ZPAD); - if (iPadZ == NPADZ) + if (iPadZ == NPADZ) { iPadZ--; - else if (iPadZ > NPADZ) + } else if (iPadZ > NPADZ) { iPadZ = -1; + } return iPadZ; } @@ -549,10 +569,11 @@ Int_t Geo::getPadX(const Float_t* pos) // Int_t iPadX = (Int_t)(pos[0] / XPAD); - if (iPadX == NPADX) + if (iPadX == NPADX) { iPadX--; - else if (iPadX > NPADX) + } else if (iPadX > NPADX) { iPadX = -1; + } return iPadX; } @@ -563,15 +584,17 @@ void Geo::translate(Float_t* xyz, Float_t translationVector[3]) // Return the vector xyz translated by translationVector vector // - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { xyz[ii] -= translationVector[ii]; + } return; } void Geo::antiRotateToSector(Float_t* xyz, Int_t isector) { - if (mToBeIntit) + if (mToBeIntit) { Init(); + } Float_t xyzDummy[3] = {0., 0., 0.}; @@ -591,16 +614,18 @@ void Geo::antiRotateToSector(Float_t* xyz, Int_t isector) xyz[2] * matAR[ii][2]; } - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { xyz[ii] = xyzDummy[ii]; + } return; } void Geo::rotateToSector(Float_t* xyz, Int_t isector) { - if (mToBeIntit) + if (mToBeIntit) { Init(); + } Float_t xyzDummy[3] = {0., 0., 0.}; @@ -609,8 +634,9 @@ void Geo::rotateToSector(Float_t* xyz, Int_t isector) xyz[2] * mRotationMatrixSector[isector][ii][2]; } - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { xyz[ii] = xyzDummy[ii]; + } return; } @@ -625,8 +651,9 @@ void Geo::rotateToStrip(Float_t* xyz, Int_t iplate, Int_t istrip) xyz[2] * mRotationMatrixPlateStrip[iplate][istrip][ii][2]; } - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { xyz[ii] = xyzDummy[ii]; + } return; } @@ -643,8 +670,9 @@ void Geo::rotate(Float_t* xyz, Double_t rotationAngles[6]) angles[4], angles[5]); */ - for (Int_t ii = 0; ii < 6; ii++) + for (Int_t ii = 0; ii < 6; ii++) { rotationAngles[ii] *= TMath::DegToRad(); + } Float_t xyzDummy[3] = {0., 0., 0.}; @@ -654,8 +682,9 @@ void Geo::rotate(Float_t* xyz, Double_t rotationAngles[6]) xyz[2] * TMath::Cos(rotationAngles[2 * ii]); } - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { xyz[ii] = xyzDummy[ii]; + } return; } @@ -666,8 +695,9 @@ void Geo::antiRotate(Float_t* xyz, Double_t rotationAngles[6]) // Rotates the vector xyz acordint to the rotationAngles // - for (Int_t ii = 0; ii < 6; ii++) + for (Int_t ii = 0; ii < 6; ii++) { rotationAngles[ii] *= TMath::DegToRad(); + } Float_t xyzDummy[3] = {0., 0., 0.}; @@ -682,8 +712,9 @@ void Geo::antiRotate(Float_t* xyz, Double_t rotationAngles[6]) xyzDummy[2] = xyz[0] * TMath::Cos(rotationAngles[0]) + xyz[1] * TMath::Cos(rotationAngles[2]) + xyz[2] * TMath::Cos(rotationAngles[4]); - for (Int_t ii = 0; ii < 3; ii++) + for (Int_t ii = 0; ii < 3; ii++) { xyz[ii] = xyzDummy[ii]; + } return; } @@ -692,3 +723,32 @@ Int_t Geo::getIndexFromEquipment(Int_t icrate, Int_t islot, Int_t ichain, Int_t { return 0; // to be implemented } + +void Geo::getStripAndModule(Int_t iStripPerSM, Int_t& iplate, Int_t& istrip) +{ + // + // Convert the serial number of the TOF strip number iStripPerSM [0,90] + // in module number iplate [0,4] and strip number istrip [0,14/18]. + // Copied from AliRoot TOF::AliTOFGeometry + // + + if (iStripPerSM < 0 || iStripPerSM >= NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB + NSTRIPC) { + iplate = -1; + istrip = -1; + } else if (iStripPerSM < NSTRIPC) { + iplate = 0; + istrip = iStripPerSM; + } else if (iStripPerSM >= NSTRIPC && iStripPerSM < NSTRIPC + NSTRIPB) { + iplate = 1; + istrip = iStripPerSM - NSTRIPC; + } else if (iStripPerSM >= NSTRIPC + NSTRIPB && iStripPerSM < NSTRIPC + NSTRIPB + NSTRIPA) { + iplate = 2; + istrip = iStripPerSM - NSTRIPC - NSTRIPB; + } else if (iStripPerSM >= NSTRIPC + NSTRIPB + NSTRIPA && iStripPerSM < NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB) { + iplate = 3; + istrip = iStripPerSM - NSTRIPC - NSTRIPB - NSTRIPA; + } else if (iStripPerSM >= NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB && iStripPerSM < NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB + NSTRIPC) { + iplate = 4; + istrip = iStripPerSM - NSTRIPC - NSTRIPB - NSTRIPA - NSTRIPB; + } +} diff --git a/Detectors/TOF/base/src/Strip.cxx b/Detectors/TOF/base/src/Strip.cxx index 43b428c4af0d1..82eedd5c561ed 100644 --- a/Detectors/TOF/base/src/Strip.cxx +++ b/Detectors/TOF/base/src/Strip.cxx @@ -33,7 +33,7 @@ Strip::Strip(Int_t index) { } //_______________________________________________________________________ -Int_t Strip::addDigit(Int_t channel, Int_t tdc, Int_t tot, Int_t bc, Int_t lbl, Int_t triggerorbit, Int_t triggerbunch) +Int_t Strip::addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lbl, uint32_t triggerorbit, uint16_t triggerbunch) { // return the MC label. We pass it also as argument, but it can change in @@ -59,8 +59,9 @@ void Strip::fillOutputContainer(std::vector<Digit>& digits) // we assume that the Strip has stored inside only digits from one readout // window --> we flush them all - if (mDigits.empty()) + if (mDigits.empty()) { return; + } auto itBeg = mDigits.begin(); auto iter = itBeg; for (; iter != mDigits.end(); ++iter) { diff --git a/Detectors/TOF/base/src/TOFBaseLinkDef.h b/Detectors/TOF/base/src/TOFBaseLinkDef.h index 75e88f696b0d1..f5cc651c66c47 100644 --- a/Detectors/TOF/base/src/TOFBaseLinkDef.h +++ b/Detectors/TOF/base/src/TOFBaseLinkDef.h @@ -21,5 +21,7 @@ #pragma link C++ class o2::tof::WindowFiller + ; #pragma link C++ class o2::tof::ReadoutWindowData + ; #pragma link C++ class vector < o2::tof::ReadoutWindowData> + ; - +#pragma link C++ class o2::tof::DigitHeader + ; +#pragma link C++ class vector < o2::tof::DigitHeader> + ; +#pragma link C++ class vector < unsigned int> + ; #endif diff --git a/Detectors/TOF/base/src/WindowFiller.cxx b/Detectors/TOF/base/src/WindowFiller.cxx index 15502720bba83..492bf51a59914 100644 --- a/Detectors/TOF/base/src/WindowFiller.cxx +++ b/Detectors/TOF/base/src/WindowFiller.cxx @@ -95,16 +95,38 @@ void WindowFiller::reset() mDigitsPerTimeFrame.clear(); mReadoutWindowData.clear(); + mReadoutWindowDataFiltered.clear(); - mFirstOrbit = 0; - mFirstBunch = 0; + mDigitHeader.clear(); + + mFirstIR.bc = 0; + mFirstIR.orbit = 0; } + //______________________________________________________________________ -void WindowFiller::fillDigitsInStrip(std::vector<Strip>* strips, int channel, int tdc, int tot, int nbc, UInt_t istrip, Int_t triggerorbit, Int_t triggerbunch) +void WindowFiller::fillDigitsInStrip(std::vector<Strip>* strips, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, uint32_t triggerorbit, uint16_t triggerbunch) { (*strips)[istrip].addDigit(channel, tdc, tot, nbc, 0, triggerorbit, triggerbunch); } //______________________________________________________________________ +void WindowFiller::addCrateHeaderData(unsigned long orbit, int crate, int32_t bc, uint32_t eventCounter) +{ + if (orbit < mFirstIR.orbit) { + return; + } + orbit -= mFirstIR.orbit; + + orbit *= Geo::NWINDOW_IN_ORBIT; // move from orbit to N readout window + orbit += (bc + 100) / Geo::BC_IN_WINDOW; // select readout window in the orbit according to the BC (100 shift to avoid border effects) + + if (mCrateHeaderData.size() < orbit + 1) { + mCrateHeaderData.resize(orbit + 1); + } + + mCrateHeaderData[orbit].bc[crate] = bc; + mCrateHeaderData[orbit].eventCounter[crate] = eventCounter; +} +//______________________________________________________________________ void WindowFiller::fillOutputContainer(std::vector<Digit>& digits) { if (mContinuous) { @@ -120,20 +142,90 @@ void WindowFiller::fillOutputContainer(std::vector<Digit>& digits) int first = mDigitsPerTimeFrame.size(); int ne = digits.size(); ReadoutWindowData info(first, ne); - if (digits.size()) + int orbit_shift = mReadoutWindowData.size() / Geo::NWINDOW_IN_ORBIT; + + mDigitHeader.addRow(); + + int bc_shift = -1; + int eventcounter = -1; + int ncratesSeen = 0; + if (mReadoutWindowData.size() >= mCrateHeaderData.size()) { + bc_shift = (mReadoutWindowData.size() % Geo::NWINDOW_IN_ORBIT) * Geo::BC_IN_WINDOW; // insert default value + eventcounter = mReadoutWindowData.size() % 4096; + for (int icrate = 0; icrate < Geo::kNCrate; icrate++) { + info.setEmptyCrate(icrate); + } + } else { + unsigned long irow = mReadoutWindowData.size(); + for (int icrate = 0; icrate < Geo::kNCrate; icrate++) { + if (mCrateHeaderData[irow].bc[icrate] == -1) { // crate not read + info.setEmptyCrate(icrate); + continue; + } else { + mDigitHeader.crateSeen(icrate); + } + ncratesSeen++; + + if (bc_shift == -1 || mCrateHeaderData[irow].bc[icrate] < bc_shift) { + bc_shift = mCrateHeaderData[irow].bc[icrate]; + } + if (eventcounter == -1 || mCrateHeaderData[irow].eventCounter[icrate] < eventcounter) { + eventcounter = mCrateHeaderData[irow].eventCounter[icrate]; + } + } + + mDigitHeader.numCratesSeen(ncratesSeen); + + if (bc_shift == -1) { + bc_shift = (mReadoutWindowData.size() % Geo::NWINDOW_IN_ORBIT) * Geo::BC_IN_WINDOW; // insert default value + } + if (eventcounter == -1) { + eventcounter = mReadoutWindowData.size() % 4096; // insert default value + } + } + + info.setBCData(mFirstIR.orbit + orbit_shift, mFirstIR.bc + bc_shift); + info.setEventCounter(eventcounter); + int firstPattern = mPatterns.size(); + int npatterns = 0; + + // check if patterns are in the current row + for (std::vector<PatternData>::reverse_iterator it = mCratePatterns.rbegin(); it != mCratePatterns.rend(); ++it) { + if (it->row > mReadoutWindowCurrent) { + break; + } + + if (it->row < mReadoutWindowCurrent) { // this should not happen + LOG(ERROR) << "One pattern skipped because appears to occur early of the current row " << it->row << " < " << mReadoutWindowCurrent << " ?!"; + } else { + mPatterns.push_back(it->pattern); + info.addedDiagnostic(it->icrate); + + npatterns++; + } + mCratePatterns.pop_back(); + } + + info.setFirstEntryDia(firstPattern); + info.setNEntriesDia(npatterns); + if (digits.size() || npatterns) { mDigitsPerTimeFrame.insert(mDigitsPerTimeFrame.end(), digits.begin(), digits.end()); + mReadoutWindowDataFiltered.push_back(info); + } mReadoutWindowData.push_back(info); } // switch to next mStrip after flushing current readout window data mIcurrentReadoutWindow++; - if (mIcurrentReadoutWindow >= MAXWINDOWS) + if (mIcurrentReadoutWindow >= MAXWINDOWS) { mIcurrentReadoutWindow = 0; + } mStripsCurrent = &(mStrips[mIcurrentReadoutWindow]); int k = mIcurrentReadoutWindow + 1; for (Int_t i = 0; i < MAXWINDOWS - 1; i++) { - if (k >= MAXWINDOWS) + if (k >= MAXWINDOWS) { k = 0; + } mStripsNext[i] = &(mStrips[k]); k++; } @@ -145,20 +237,22 @@ void WindowFiller::flushOutputContainer(std::vector<Digit>& digits) { // flush all residual buffered data // TO be implemented - printf("flushOutputContainer\n"); + // sort patterns (diagnostic words) in time + std::sort(mCratePatterns.begin(), mCratePatterns.end(), + [](PatternData a, PatternData b) { if(a.row == b.row) { return a.icrate > b.icrate; } else { return a.row > b.row; } }); + for (Int_t i = 0; i < MAXWINDOWS; i++) { int n = 0; - for (int j = 0; j < mStrips[i].size(); j++) + for (int j = 0; j < mStrips[i].size(); j++) { n += ((mStrips[i])[j]).getNumberOfDigits(); - - printf("ro #%d: digits = %d\n", i, n); + } } - printf("Future digits = %lu\n", mFutureDigits.size()); + checkIfReuseFutureDigitsRO(); - if (!mContinuous) + if (!mContinuous) { fillOutputContainer(digits); - else { + } else { for (Int_t i = 0; i < MAXWINDOWS; i++) { fillOutputContainer(digits); // fill all windows which are before (not yet stored) of the new current one checkIfReuseFutureDigitsRO(); @@ -174,13 +268,21 @@ void WindowFiller::flushOutputContainer(std::vector<Digit>& digits) for (Int_t i = 0; i < MAXWINDOWS; i++) { fillOutputContainer(digits); // fill last readout windows } + + int nwindowperTF = o2::raw::HBFUtils::Instance().getNOrbitsPerTF() * Geo::NWINDOW_IN_ORBIT; + + // check that all orbits are complete in terms of number of readout windows + while ((mReadoutWindowData.size() % nwindowperTF)) { + fillOutputContainer(digits); // fill windows without digits to complete all orbits in the last TF + } } } //______________________________________________________________________ void WindowFiller::checkIfReuseFutureDigits() { - if (!mFutureDigits.size()) + if (!mFutureDigits.size()) { return; + } // check if digits stored very far in future match the new readout windows currently available if (mFutureToBeSorted) { @@ -196,8 +298,9 @@ void WindowFiller::checkIfReuseFutureDigits() for (std::vector<Digit>::reverse_iterator digit = mFutureDigits.rbegin(); digit != mFutureDigits.rend(); ++digit) { - if (digit->getBC() > bclimit) + if (digit->getBC() > bclimit) { break; + } double timestamp = digit->getBC() * Geo::BC_TIME + digit->getTDC() * Geo::TDCBIN * 1E-3; // in ns int isnext = Int_t(timestamp * Geo::READOUTWINDOW_INV) - (mReadoutWindowCurrent + 1); // to be replaced with uncalibrated time @@ -235,18 +338,21 @@ void WindowFiller::checkIfReuseFutureDigits() //______________________________________________________________________ void WindowFiller::checkIfReuseFutureDigitsRO() // the same but using readout info information from raw { - if (!mFutureDigits.size()) + if (!mFutureDigits.size()) { return; + } // check if digits stored very far in future match the new readout windows currently available if (mFutureToBeSorted) { // sort digit in descending BC order: kept last as first std::sort(mFutureDigits.begin(), mFutureDigits.end(), [](o2::tof::Digit a, o2::tof::Digit b) { - if (a.getTriggerOrbit() != b.getTriggerOrbit()) + if (a.getTriggerOrbit() != b.getTriggerOrbit()) { return a.getTriggerOrbit() > b.getTriggerOrbit(); - if (a.getTriggerBunch() != b.getTriggerBunch()) + } + if (a.getTriggerBunch() != b.getTriggerBunch()) { return a.getTriggerBunch() > b.getTriggerBunch(); + } return a.getBC() > b.getBC(); }); mFutureToBeSorted = false; @@ -258,12 +364,13 @@ void WindowFiller::checkIfReuseFutureDigitsRO() // the same but using readout in for (std::vector<Digit>::reverse_iterator digit = mFutureDigits.rbegin(); digit != mFutureDigits.rend(); ++digit) { - int row = (digit->getTriggerOrbit() - mFirstOrbit) * Geo::BC_IN_ORBIT + (digit->getTriggerBunch() - mFirstBunch) + 100; // N bunch id of the trigger from timeframe start + 100 bunches + int row = (digit->getTriggerOrbit() - mFirstIR.orbit) * Geo::BC_IN_ORBIT + (digit->getTriggerBunch() - mFirstIR.bc) + 100; // N bunch id of the trigger from timeframe start + 100 bunches row *= Geo::BC_IN_WINDOW_INV; - if (row > rolimit) + if (row > rolimit) { break; + } int isnext = row - mReadoutWindowCurrent; @@ -286,7 +393,9 @@ void WindowFiller::checkIfReuseFutureDigitsRO() // the same but using readout in strips = mStripsNext[isnext - 1]; } - fillDigitsInStrip(strips, digit->getChannel(), digit->getTDC(), digit->getTOT(), digit->getBC(), digit->getChannel() / Geo::NPADS); + if (mMaskNoiseRate < 0 || mChannelCounts[digit->getChannel()] < mMaskNoiseRate) { + fillDigitsInStrip(strips, digit->getChannel(), digit->getTDC(), digit->getTOT(), digit->getBC(), digit->getChannel() / Geo::NPADS); + } // int labelremoved = digit->getLabel(); mFutureDigits.erase(mFutureDigits.begin() + idigit); diff --git a/Detectors/TOF/base/test/testTOFIndex.cxx b/Detectors/TOF/base/test/testTOFIndex.cxx index bd3bd7566673b..beec1680ef462 100644 --- a/Detectors/TOF/base/test/testTOFIndex.cxx +++ b/Detectors/TOF/base/test/testTOFIndex.cxx @@ -42,8 +42,9 @@ BOOST_AUTO_TEST_CASE(testTOFIndex) : j > 2 ? Geo::NSTRIPC : Geo::NSTRIPA; // Define the numer of strips of the plate if (j == 2 && - (i == 15 || i == 14 || i == 13)) // Skip sectors without A plate + (i == 15 || i == 14 || i == 13)) { // Skip sectors without A plate continue; + } // for (Int_t k = 0; k < nStrips; k++) { // Loop on all Strips indextof[2] = k; diff --git a/Detectors/TOF/calibration/CMakeLists.txt b/Detectors/TOF/calibration/CMakeLists.txt index 10921f2d0d3ec..fc969a2108616 100644 --- a/Detectors/TOF/calibration/CMakeLists.txt +++ b/Detectors/TOF/calibration/CMakeLists.txt @@ -14,9 +14,12 @@ o2_add_library(TOFCalibration src/CollectCalibInfoTOF.cxx src/LHCClockCalibrator.cxx src/TOFChannelCalibrator.cxx + src/TOFCalibCollector.cxx + src/TOFDCSProcessor.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF O2::TOFBase O2::CCDB O2::DetectorsCalibration + O2::DetectorsDCS ROOT::Minuit ms_gsl::ms_gsl) @@ -26,7 +29,9 @@ o2_target_root_dictionary(TOFCalibration include/TOFCalibration/CalibTOF.h include/TOFCalibration/LHCClockCalibrator.h include/TOFCalibration/TOFChannelCalibrator.h - include/TOFCalibration/CollectCalibInfoTOF.h) + include/TOFCalibration/TOFCalibCollector.h + include/TOFCalibration/CollectCalibInfoTOF.h + include/TOFCalibration/TOFDCSProcessor.h) o2_add_executable(data-generator-workflow @@ -64,3 +69,17 @@ o2_add_executable(tof-dummy-ccdb-for-calib PUBLIC_LINK_LIBRARIES O2::Framework O2::TOFCalibration O2::DetectorsCalibration) + +o2_add_executable(tof-collect-calib-workflow + COMPONENT_NAME calibration + SOURCES testWorkflow/tof-collect-calib-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::TOFCalibration + O2::DetectorsCalibration) + +o2_add_executable(tof-dcs-workflow + COMPONENT_NAME calibration + SOURCES testWorkflow/tof-dcs-data-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::TOFCalibration + O2::DetectorsDCS) diff --git a/Detectors/TOF/calibration/include/TOFCalibration/CalibTOFapi.h b/Detectors/TOF/calibration/include/TOFCalibration/CalibTOFapi.h index 0165ad4350620..564d7590feb0a 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/CalibTOFapi.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/CalibTOFapi.h @@ -49,8 +49,8 @@ class CalibTOFapi } void readLHCphase(); void readTimeSlewingParam(); - void writeLHCphase(LhcPhase* phase, std::map<std::string, std::string> metadataLHCphase, unsigned long minTimeSTamp, unsigned long maxTimeStamp); - void writeTimeSlewingParam(SlewParam* param, std::map<std::string, std::string> metadataChannelCalib, unsigned long minTimeSTamp, unsigned long maxTimeStamp = 0); + void writeLHCphase(LhcPhase* phase, std::map<std::string, std::string> metadataLHCphase, uint64_t minTimeSTamp, uint64_t maxTimeStamp); + void writeTimeSlewingParam(SlewParam* param, std::map<std::string, std::string> metadataChannelCalib, uint64_t minTimeSTamp, uint64_t maxTimeStamp = 0); float getTimeCalibration(int ich, float tot); float getTimeDecalibration(int ich, float tot); bool isProblematic(int ich); @@ -58,6 +58,7 @@ class CalibTOFapi SlewParam* getSlewParam() { return mSlewParam; } SlewParam& getSlewParamObj() { return *mSlewParam; } + LhcPhase* getLhcPhase() { return mLHCphase; } private: long mTimeStamp; ///< timeStamp for queries diff --git a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h index 08007e27fc95d..09ef16b5dc1b9 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h @@ -51,7 +51,7 @@ struct LHCClockDataHisto { ClassDefNV(LHCClockDataHisto, 1); }; -class LHCClockCalibrator : public o2::calibration::TimeSlotCalibration<o2::dataformats::CalibInfoTOF, o2::tof::LHCClockDataHisto> +class LHCClockCalibrator final : public o2::calibration::TimeSlotCalibration<o2::dataformats::CalibInfoTOF, o2::tof::LHCClockDataHisto> { using TFType = uint64_t; using Slot = o2::calibration::TimeSlot<o2::tof::LHCClockDataHisto>; diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFCalibCollector.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFCalibCollector.h new file mode 100644 index 0000000000000..a5a664b8e9208 --- /dev/null +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFCalibCollector.h @@ -0,0 +1,95 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TOF_CALIB_COLLECTOR_H_ +#define TOF_CALIB_COLLECTOR_H_ + +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "DataFormatsTOF/CalibInfoTOF.h" +#include "TOFBase/Geo.h" +#include "DataFormatsTOF/CalibInfoTOFshort.h" + +#include <array> + +namespace o2 +{ +namespace tof +{ + +class TOFCalibInfoSlot +{ + + using Slot = o2::calibration::TimeSlot<o2::tof::TOFCalibInfoSlot>; + using Geo = o2::tof::Geo; + + public: + static constexpr int NCHANNELSXSECTOR = o2::tof::Geo::NCHANNELS / o2::tof::Geo::NSECTORS; + + TOFCalibInfoSlot() + { + for (int ch = 0; ch < Geo::NCHANNELS; ch++) { + mEntriesSlot[ch] = 0; + } + } + + ~TOFCalibInfoSlot() = default; + + void print() const; + void printEntries() const; + void fill(const gsl::span<const o2::dataformats::CalibInfoTOF> data); + void merge(const TOFCalibInfoSlot* prev); + + auto& getEntriesPerChannel() const { return mEntriesSlot; } + auto& getEntriesPerChannel() { return mEntriesSlot; } + auto& getCollectedCalibInfoSlot() { return mTOFCollectedCalibInfoSlot; } + auto& getCollectedCalibInfoSlot() const { return mTOFCollectedCalibInfoSlot; } + + private: + std::array<int, Geo::NCHANNELS> mEntriesSlot; // vector containing number of entries per channel + std::vector<o2::dataformats::CalibInfoTOFshort> mTOFCollectedCalibInfoSlot; ///< output TOF calibration info + + ClassDefNV(TOFCalibInfoSlot, 1); +}; + +class TOFCalibCollector final : public o2::calibration::TimeSlotCalibration<o2::dataformats::CalibInfoTOF, o2::tof::TOFCalibInfoSlot> +{ + using TFType = uint64_t; + using Slot = o2::calibration::TimeSlot<o2::tof::TOFCalibInfoSlot>; + + public: + TOFCalibCollector(bool TFsendingPolicy, int maxNumOfHits, bool test = false) : mTFsendingPolicy(TFsendingPolicy), mMaxNumOfHits(maxNumOfHits), mTest(test){}; + + ~TOFCalibCollector() 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; + void setIsTest(bool istest) { mTest = istest; } + auto& getCollectedCalibInfo() const { return mTOFCollectedCalibInfo; } + 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 = 500; // maximum number of hits for one single channel to trigger the sending of the information (if mTFsendingPolicy = false) + bool mTest = false; // flag to say whether we are in test mode or not + bool mAbsMaxNumOfHits = true; // to decide if the mMaxNumOfHits should be multiplied by the number of TOF channels + std::array<int, Geo::NCHANNELS> mEntries; // vector containing number of entries per channel + std::vector<o2::dataformats::CalibInfoTOFshort> mTOFCollectedCalibInfo; ///< output TOF calibration info + + ClassDefOverride(TOFCalibCollector, 1); +}; + +} // end namespace tof +} // end namespace o2 + +#endif /* TOF_CHANNEL_CALIBRATOR_H_ */ diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFChannelCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFChannelCalibrator.h index 7fcea42e3e964..9927e1e549354 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/TOFChannelCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFChannelCalibrator.h @@ -95,7 +95,7 @@ class TOFChannelData ClassDefNV(TOFChannelData, 1); }; -class TOFChannelCalibrator : public o2::calibration::TimeSlotCalibration<o2::dataformats::CalibInfoTOF, o2::tof::TOFChannelData> +class TOFChannelCalibrator final : public o2::calibration::TimeSlotCalibration<o2::dataformats::CalibInfoTOF, o2::tof::TOFChannelData> { using TFType = uint64_t; using Slot = o2::calibration::TimeSlot<o2::tof::TOFChannelData>; @@ -120,7 +120,7 @@ class TOFChannelCalibrator : public o2::calibration::TimeSlotCalibration<o2::dat const CcdbObjectInfoVector& getTimeSlewingInfoVector() const { return mInfoVector; } CcdbObjectInfoVector& getTimeSlewingInfoVector() { return mInfoVector; } - void isTest(bool isTest) { mTest = isTest; } + void setIsTest(bool isTest) { mTest = isTest; } bool isTest() const { return mTest; } void setCalibTOFapi(CalibTOFapi* api) { mCalibTOFapi = api; } diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h new file mode 100644 index 0000000000000..120e08a24ab49 --- /dev/null +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h @@ -0,0 +1,160 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DETECTOR_TOFDCSPROCESSOR_H_ +#define DETECTOR_TOFDCSPROCESSOR_H_ + +#include <memory> +#include <Rtypes.h> +#include <unordered_map> +#include <deque> +#include <numeric> +#include "Framework/Logger.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DeliveryType.h" +#include "CCDB/CcdbObjectInfo.h" +#include "CommonUtils/MemFileHelper.h" +#include "CCDB/CcdbApi.h" +#include <gsl/gsl> +#include "TOFBase/Geo.h" + +/// @brief Class to process DCS data points + +namespace o2 +{ +namespace tof +{ + +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; + +struct TOFDCSinfo { + std::pair<uint64_t, double> firstValue; // first value seen by the TOF DCS processor + std::pair<uint64_t, double> lastValue; // last value seen by the TOF DCS processor + std::pair<uint64_t, double> midValue; // mid value seen by the TOF DCS processor + std::pair<uint64_t, double> maxChange; // maximum variation seen by the TOF DCS processor + TOFDCSinfo() + { + firstValue = std::make_pair(0, -999999999); + lastValue = std::make_pair(0, -999999999); + midValue = std::make_pair(0, -999999999); + maxChange = std::make_pair(0, -999999999); + } + void makeEmpty() + { + firstValue.first = lastValue.first = midValue.first = maxChange.first = 0; + firstValue.second = lastValue.second = midValue.second = maxChange.second = -999999999; + } + void print() const; + + ClassDefNV(TOFDCSinfo, 1); +}; + +struct TOFFEACinfo { + std::array<int32_t, 6> stripInSM = {-1, -1, -1, -1, -1, -1}; + int32_t firstPadX = -1; + int32_t lastPadX = -1; +}; + +class TOFDCSProcessor +{ + + public: + using TFType = uint64_t; + using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; + using DQDoubles = std::deque<double>; + + static constexpr int NFEACS = 8; + + TOFDCSProcessor() = default; + ~TOFDCSProcessor() = default; + + void init(const std::vector<DPID>& pids); + + //int process(const std::vector<DPCOM>& dps); + int process(const gsl::span<const DPCOM> dps); + int processDP(const DPCOM& dpcom); + virtual uint64_t processFlags(uint64_t flag, const char* pid); + + void finalize(); + void getStripsConnectedToFEAC(int nDDL, int nFEAC, TOFFEACinfo& info) const; + void updateFEACCCDB(); + void updateHVCCDB(); + + const CcdbObjectInfo& getccdbDPsInfo() const { return mccdbDPsInfo; } + CcdbObjectInfo& getccdbDPsInfo() { return mccdbDPsInfo; } + const std::unordered_map<DPID, TOFDCSinfo>& getTOFDPsInfo() const { return mTOFDCS; } + + const CcdbObjectInfo& getccdbLVInfo() const { return mccdbLVInfo; } + CcdbObjectInfo& getccdbLVInfo() { return mccdbLVInfo; } + const std::bitset<Geo::NCHANNELS>& getLVStatus() const { return mFeac; } + const bool isLVUpdated() const { return mUpdateFeacStatus; } + + const CcdbObjectInfo& getccdbHVInfo() const { return mccdbHVInfo; } + CcdbObjectInfo& getccdbHVInfo() { return mccdbHVInfo; } + const std::bitset<Geo::NCHANNELS>& getHVStatus() const { return mHV; } + const bool isHVUpdated() const { return mUpdateHVStatus; } + + template <typename T> + void prepareCCDBobjectInfo(T& obj, CcdbObjectInfo& info, const std::string& path, TFType tf, + const std::map<std::string, std::string>& md); + + void setTF(TFType tf) { mTF = tf; } + void useVerboseMode() { mVerbose = true; } + + private: + std::unordered_map<DPID, TOFDCSinfo> mTOFDCS; // this is the object that will go to the CCDB + std::unordered_map<DPID, bool> mPids; // contains all PIDs for the processor, the bool + // will be true if the DP was processed at least once + std::unordered_map<DPID, std::vector<DPVAL>> mDpsdoublesmap; // this is the map that will hold the DPs for the + // double type (voltages and currents) + + std::array<std::array<TOFFEACinfo, NFEACS>, Geo::kNDDL> mFeacInfo; // contains the strip/pad info per FEAC + std::array<std::bitset<8>, Geo::kNDDL> mPrevFEACstatus; // previous FEAC status + std::bitset<Geo::NCHANNELS> mFeac; // bitset with feac status per channel + bool mUpdateFeacStatus = false; // whether to update the FEAC status in CCDB or not + std::bitset<Geo::NCHANNELS> mHV; // bitset with HV status per channel + std::array<std::array<std::bitset<19>, Geo::NSECTORS>, Geo::NPLATES> mPrevHVstatus; // previous HV status + bool mUpdateHVStatus = false; // whether to update the HV status in CCDB or not + CcdbObjectInfo mccdbDPsInfo; + CcdbObjectInfo mccdbLVInfo; + CcdbObjectInfo mccdbHVInfo; + TFType mStartTF; // TF index for processing of first processed TF, used to store CCDB object + TFType mTF = 0; // TF index for processing, used to store CCDB object + bool mStartTFset = false; + + bool mVerbose = false; + + ClassDefNV(TOFDCSProcessor, 0); +}; + +template <typename T> +void TOFDCSProcessor::prepareCCDBobjectInfo(T& obj, CcdbObjectInfo& info, const std::string& path, TFType tf, + const std::map<std::string, std::string>& md) +{ + + // prepare all info to be sent to CCDB for object obj + auto clName = o2::utils::MemFileHelper::getClassName(obj); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + info.setPath(path); + info.setObjectType(clName); + info.setFileName(flName); + info.setStartValidityTimestamp(tf); + info.setEndValidityTimestamp(99999999999999); + info.setMetaData(md); +} + +} // namespace tof +} // namespace o2 + +#endif diff --git a/Detectors/TOF/calibration/macros/CMakeLists.txt b/Detectors/TOF/calibration/macros/CMakeLists.txt new file mode 100644 index 0000000000000..47348b684982f --- /dev/null +++ b/Detectors/TOF/calibration/macros/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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( + make-CCDB-Entry-For-DCS + SOURCES makeCCDBEntryForDCS.cxx + COMPONENT_NAME tof + PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB) diff --git a/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.cxx b/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.cxx new file mode 100644 index 0000000000000..e7d07c858dd9e --- /dev/null +++ b/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.cxx @@ -0,0 +1,52 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <vector> +#include <string> +#include "TFile.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsDCS/AliasExpander.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DataPointIdentifier.h" + +#include <unordered_map> +#include <chrono> + +using DPID = o2::dcs::DataPointIdentifier; + +int main(int argc, char* argv[]) //const std::string url = "http://localhost:8080") +{ + + std::string url(argv[0]); + // macro to populate CCDB for TOF with the configuration for DCS + std::unordered_map<DPID, std::string> dpid2DataDesc; + std::vector<std::string> aliases = {"tof_hv_vp_[00..89]", "tof_hv_vn_[00..89]", "tof_hv_ip_[00..89]", "tof_hv_in_[00..89]"}; + std::vector<std::string> aliasesInt = {"TOF_FEACSTATUS_[00..71]", "TOF_HVSTATUS_SM[00..01]MOD[0..1]"}; + std::vector<std::string> expaliases = o2::dcs::expandAliases(aliases); + std::vector<std::string> expaliasesInt = o2::dcs::expandAliases(aliasesInt); + + DPID dpidtmp; + for (size_t i = 0; i < expaliases.size(); ++i) { + DPID::FILL(dpidtmp, expaliases[i], o2::dcs::DeliveryType::RAW_DOUBLE); + dpid2DataDesc[dpidtmp] = "TOFDATAPOINTS"; + } + for (size_t i = 0; i < expaliasesInt.size(); ++i) { + DPID::FILL(dpidtmp, expaliasesInt[i], o2::dcs::DeliveryType::RAW_INT); + dpid2DataDesc[dpidtmp] = "TOFDATAPOINTS"; + } + + o2::ccdb::CcdbApi api; + api.init(url); // or http://localhost:8080 for a local installation + std::map<std::string, std::string> md; + long ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + api.storeAsTFileAny(&dpid2DataDesc, "TOF/DCSconfig", md, ts); + + return 0; +} diff --git a/Detectors/TOF/calibration/src/CalibTOF.cxx b/Detectors/TOF/calibration/src/CalibTOF.cxx index c37f5808c27c6..2a1c5890ab108 100644 --- a/Detectors/TOF/calibration/src/CalibTOF.cxx +++ b/Detectors/TOF/calibration/src/CalibTOF.cxx @@ -44,8 +44,9 @@ CalibTOF::~CalibTOF() // destructor - if (mHistoLHCphase) + if (mHistoLHCphase) { delete mHistoLHCphase; + } delete mHistoChTimeSlewingAll; } //______________________________________________ @@ -108,8 +109,9 @@ void CalibTOF::init() // booking the histogram of the LHCphase int nbinsLHCphase = TMath::Min(1000, int((mMaxTimestamp - mMinTimestamp) / 300) + 1); - if (nbinsLHCphase < 1000) + if (nbinsLHCphase < 1000) { mMaxTimestamp = mMinTimestamp + nbinsLHCphase * 300; // we want that the last bin of the histogram is also large 300s; this we need to do only when we have less than 1000 bins, because in this case we will integrate over intervals that are larger than 300s anyway + } mHistoLHCphase = new TH2F("hLHCphase", ";clock offset (ps); timestamp (s)", 1000, -24400, 24400, nbinsLHCphase, mMinTimestamp, mMaxTimestamp); // setting CCDB for output @@ -155,10 +157,11 @@ void CalibTOF::run(int flag, int sector) std::vector<o2::dataformats::CalibInfoTOFshort>* calibTimePad[NPADSPERSTEP]; for (int ipad = 0; ipad < NPADSPERSTEP; ipad++) { histoChOffsetTemp[ipad] = new TH1F(Form("OffsetTemp_Sec%02d_Pad%04d", sector, ipad), Form("Sector %02d (pad = %04d);channel offset (ps)", sector, ipad), 1000, -24400, 24400); - if (flag & kChannelTimeSlewing) + if (flag & kChannelTimeSlewing) { calibTimePad[ipad] = new std::vector<o2::dataformats::CalibInfoTOFshort>; // temporary array containing [time, tot] for every pad that we process; this will be the input for the 2D histo for timeSlewing calibration (to be filled after we get the channel offset) - else + } else { calibTimePad[ipad] = nullptr; + } } TF1* funcChOffset = new TF1(Form("fTOFchOffset_%02d", sector), "[0]*TMath::Gaus((x-[1])*(x-[1] < 12500 && x-[1] > -12500) + (x-[1]+25000)*(x-[1] < -12500) + (x-[1]-25000)*(x-[1] > 12500),0,[2])*(x > -12500 && x < 12500)", -12500, 12500); @@ -196,8 +199,9 @@ void CalibTOF::run(int flag, int sector) } } TFile* fout = nullptr; - if (flag & kChannelTimeSlewing && mDebugMode) + if (flag & kChannelTimeSlewing && mDebugMode) { fout = new TFile(Form("timeslewingTOF%06i.root", ich / 96), "RECREATE"); + } for (ipad = 0; ipad < NPADSPERSTEP; ipad++) { if (histoChOffsetTemp[ipad]->GetEntries() > 30) { @@ -220,8 +224,9 @@ void CalibTOF::run(int flag, int sector) TGraphErrors* gTimeVsTot = processSlewing(histoChTimeSlewingTemp, 1, funcChOffset); if (gTimeVsTot && gTimeVsTot->GetN()) { - for (int itot = 0; itot < gTimeVsTot->GetN(); itot++) + for (int itot = 0; itot < gTimeVsTot->GetN(); itot++) { mTimeSlewingObj->addTimeSlewingInfo(ich + ipad, gTimeVsTot->GetX()[itot], gTimeVsTot->GetY()[itot] + mCalibChannelOffset[ich + ipad]); + } } else { // just add the channel offset mTimeSlewingObj->addTimeSlewingInfo(ich + ipad, 0, mCalibChannelOffset[ich + ipad]); } @@ -246,8 +251,9 @@ void CalibTOF::run(int flag, int sector) for (int ipad = 0; ipad < NPADSPERSTEP; ipad++) { delete histoChOffsetTemp[ipad]; - if (calibTimePad[ipad]) + if (calibTimePad[ipad]) { delete calibTimePad[ipad]; + } } delete histoChTimeSlewingTemp; delete funcChOffset; @@ -269,11 +275,11 @@ void CalibTOF::fillOutput(int flag) if (mFillCCDB) { if (flag & kLHCphase) { std::map<std::string, std::string> metadataLHCphase; // can be empty - mCalibTOFapi.writeLHCphase(mLHCphaseObj, metadataLHCphase, (unsigned long)mMinTimestamp * 1000, (unsigned long)mMaxTimestamp * 1000); // we use as validity the timestamps that we got from the input for the calibration; but we need to convert to ms for the CCDB (at least for now that we use an integer for the timestamp) + mCalibTOFapi.writeLHCphase(mLHCphaseObj, metadataLHCphase, (uint64_t)mMinTimestamp * 1000, (uint64_t)mMaxTimestamp * 1000); // we use as validity the timestamps that we got from the input for the calibration; but we need to convert to ms for the CCDB (at least for now that we use an integer for the timestamp) } if (flag & kChannelOffset || flag & kChannelTimeSlewing) { std::map<std::string, std::string> metadataChannelCalib; // can be empty - mCalibTOFapi.writeTimeSlewingParam(mTimeSlewingObj, metadataChannelCalib, (unsigned long)mMinTimestamp * 1000); // contains both offset and time slewing; we use as validity the START ONLY timestamp that we got from the input for the calibration; but we need to convert to ms for the CCDB (at least for now that we use an integer for the timestamp), END is default + mCalibTOFapi.writeTimeSlewingParam(mTimeSlewingObj, metadataChannelCalib, (uint64_t)mMinTimestamp * 1000); // contains both offset and time slewing; we use as validity the START ONLY timestamp that we got from the input for the calibration; but we need to convert to ms for the CCDB (at least for now that we use an integer for the timestamp), END is default } } } @@ -350,8 +356,9 @@ void CalibTOF::doLHCPhaseCalib() } int res = FitPeak(mFuncLHCphase, htemp, 500., 3., 2., "LHCphase"); - if (res) + if (res) { continue; + } mLHCphaseObj->addLHCphase(mHistoLHCphase->GetYaxis()->GetBinLowEdge(ifit0), mFuncLHCphase->GetParameter(1)); ifit0 = ifit + 1; // starting point for the next LHC interval @@ -372,8 +379,9 @@ void CalibTOF::fillChannelCalibInput(std::vector<o2::dataformats::CalibInfoTOFsh dtime -= (int(dtime * bc_inv + 5.5) - 5) * bc; // do truncation far (by 5 units) from zero to avoid truncation of negative numbers histo->Fill(dtime); - if (calibTimePad) + if (calibTimePad) { calibTimePad->push_back(*infotof); + } } } //______________________________________________ @@ -402,14 +410,16 @@ float CalibTOF::doChannelCalibration(int ipad, TH1F* histo, TF1* funcChOffset) // calibrate single channel from histos - offsets float integral = histo->Integral(); - if (!integral) + if (!integral) { return -1; // we skip directly the channels that were switched off online, the PHOS holes... + } int resfit = FitPeak(funcChOffset, histo, 500., 3., 2., "ChannelOffset"); // return a number greater than zero to distinguish bad fit from empty channels(fraction=0) - if (resfit) + if (resfit) { return 0.0001; // fit was not good + } float mean = funcChOffset->GetParameter(1); float sigma = funcChOffset->GetParameter(2); @@ -444,10 +454,12 @@ float CalibTOF::doChannelCalibration(int ipad, TH1F* histo, TF1* funcChOffset) int binmin = histo->FindBin(intmin); int binmax = histo->FindBin(intmax); - if (binmin < 1) + if (binmin < 1) { binmin = 1; // avoid to take the underflow bin (can happen in case the sigma is too large) - if (binmax > histo->GetNbinsX()) + } + if (binmax > histo->GetNbinsX()) { binmax = histo->GetNbinsX(); // avoid to take the overflow bin (can happen in case the sigma is too large) + } return (histo->Integral(binmin, binmax) + addduetoperiodicity) / integral; } @@ -460,8 +472,9 @@ void CalibTOF::resetChannelLevelHistos(TH1F* histoOffset[NPADSPERSTEP], TH2F* hi for (int ipad = 0; ipad < NPADSPERSTEP; ipad++) { histoOffset[ipad]->Reset(); - if (calibTimePad[ipad]) + if (calibTimePad[ipad]) { calibTimePad[ipad]->clear(); + } } histoTimeSlewing->Reset(); } @@ -491,17 +504,20 @@ TGraphErrors* CalibTOF::processSlewing(TH2F* histo, Bool_t forceZero, TF1* fitFu Int_t startBin = ibin; Int_t endBin = ibin; while (hpx->Integral(startBin, endBin) < 300) { - if (startBin == 1 && forceZero) + if (startBin == 1 && forceZero) { break; - if (endBin < maxBin) + } + if (endBin < maxBin) { endBin++; - else if (startBin > minBin) + } else if (startBin > minBin) { startBin--; - else + } else { break; + } } - if (hpx->Integral(startBin, endBin) <= 0) + if (hpx->Integral(startBin, endBin) <= 0) { continue; + } // printf("TOT window defined: %f < TOT < %f ns [%d, %d], %d tracks\n", hpx->GetBinLowEdge(startBin), hpx->GetBinLowEdge(endBin + 1), startBin, endBin, (Int_t)hpx->Integral(startBin, endBin)); /* projection-y */ @@ -568,29 +584,35 @@ Int_t CalibTOF::FitPeak(TF1* fitFunc, TH1* h, Float_t startSigma, Float_t nSigma } Double_t fitMin = fitCent - nSigmaMin * startSigma; Double_t fitMax = fitCent + nSigmaMax * startSigma; - if (fitMin < -12500) + if (fitMin < -12500) { fitMin = -12500; - if (fitMax > 12500) + } + if (fitMax > 12500) { fitMax = 12500; + } fitFunc->SetParLimits(1, fitMin, fitMax); fitFunc->SetParameter(0, 100); fitFunc->SetParameter(1, fitCent); fitFunc->SetParameter(2, startSigma); Int_t fitres = h->Fit(fitFunc, "WWq0", "", fitMin, fitMax); //printf("%s) init: %f %f\n ",h->GetName(),fitMin,fitMax); - if (fitres != 0) + if (fitres != 0) { return fitres; + } /* refit with better range */ for (Int_t i = 0; i < 3; i++) { fitCent = fitFunc->GetParameter(1); fitMin = fitCent - nSigmaMin * abs(fitFunc->GetParameter(2)); fitMax = fitCent + nSigmaMax * abs(fitFunc->GetParameter(2)); - if (fitMin < -12500) + if (fitMin < -12500) { fitMin = -12500; - if (fitMax > 12500) + } + if (fitMax > 12500) { fitMax = 12500; - if (fitMin >= fitMax) + } + if (fitMin >= fitMax) { printf("%s) step%i: %f %f\n ", h->GetName(), i, fitMin, fitMax); + } fitFunc->SetParLimits(1, fitMin, fitMax); fitres = h->Fit(fitFunc, "q0", "", fitMin, fitMax); if (fitres != 0) { @@ -598,13 +620,15 @@ Int_t CalibTOF::FitPeak(TF1* fitFunc, TH1* h, Float_t startSigma, Float_t nSigma if (mDebugMode > 1) { char* filename = Form("TOFDBG_%s.root", h->GetName()); - if (hdbg) + if (hdbg) { filename = Form("TOFDBG_%s_%s.root", hdbg->GetName(), debuginfo); + } // printf("write %s\n", filename); TFile ff(filename, "RECREATE"); h->Write(); - if (hdbg) + if (hdbg) { hdbg->Write(); + } ff.Close(); } @@ -614,13 +638,15 @@ Int_t CalibTOF::FitPeak(TF1* fitFunc, TH1* h, Float_t startSigma, Float_t nSigma if (mDebugMode > 1 && fitFunc->GetParError(1) > 100) { char* filename = Form("TOFDBG_%s.root", h->GetName()); - if (hdbg) + if (hdbg) { filename = Form("TOFDBG_%s_%s.root", hdbg->GetName(), debuginfo); + } // printf("write %s\n", filename); TFile ff(filename, "RECREATE"); h->Write(); - if (hdbg) + if (hdbg) { hdbg->Write(); + } ff.Close(); } @@ -694,8 +720,9 @@ void CalibTOF::flagProblematics() for (int i = 0; i < 18; i++) { // exclude channel without entries - if (mTimeSlewingObj->getFractionUnderPeak(i, ipad) < 0) + if (mTimeSlewingObj->getFractionUnderPeak(i, ipad) < 0) { continue; + } nActiveChannels++; @@ -716,16 +743,18 @@ void CalibTOF::flagProblematics() for (int i = 0; i < 18; i++) { // exclude channel without entries - if (mTimeSlewingObj->getFractionUnderPeak(i, ipad) < 0) + if (mTimeSlewingObj->getFractionUnderPeak(i, ipad) < 0) { continue; + } if (mTimeSlewingObj->getSigmaPeak(i, ipad) < sigmaMin || mTimeSlewingObj->getSigmaPeak(i, ipad) > sigmaMax || mTimeSlewingObj->getFractionUnderPeak(i, ipad) < fractionMin) { mTimeSlewingObj->setFractionUnderPeak(i, ipad, -mTimeSlewingObj->getFractionUnderPeak(i, ipad)); mTimeSlewingObj->setSigmaPeak(i, ipad, -mTimeSlewingObj->getSigmaPeak(i, ipad)); - } else + } else { nGoodChannels++; + } } } } diff --git a/Detectors/TOF/calibration/src/CalibTOFapi.cxx b/Detectors/TOF/calibration/src/CalibTOFapi.cxx index 7689412e2428f..2e994bc802675 100644 --- a/Detectors/TOF/calibration/src/CalibTOFapi.cxx +++ b/Detectors/TOF/calibration/src/CalibTOFapi.cxx @@ -48,7 +48,7 @@ void CalibTOFapi::readTimeSlewingParam() //______________________________________________________________________ -void CalibTOFapi::writeLHCphase(LhcPhase* phase, std::map<std::string, std::string> metadataLHCphase, unsigned long minTimeStamp, unsigned long maxTimeStamp) +void CalibTOFapi::writeLHCphase(LhcPhase* phase, std::map<std::string, std::string> metadataLHCphase, uint64_t minTimeStamp, uint64_t maxTimeStamp) { // write LHCphase object to CCDB @@ -61,7 +61,7 @@ void CalibTOFapi::writeLHCphase(LhcPhase* phase, std::map<std::string, std::stri //______________________________________________________________________ -void CalibTOFapi::writeTimeSlewingParam(SlewParam* param, std::map<std::string, std::string> metadataChannelCalib, unsigned long minTimeStamp, unsigned long maxTimeStamp) +void CalibTOFapi::writeTimeSlewingParam(SlewParam* param, std::map<std::string, std::string> metadataChannelCalib, uint64_t minTimeStamp, uint64_t maxTimeStamp) { // write TiemSlewing object to CCDB (it includes offset + problematic) @@ -71,8 +71,9 @@ void CalibTOFapi::writeTimeSlewingParam(SlewParam* param, std::map<std::string, api.init(mgr.getURL()); if (maxTimeStamp == 0) { api.storeAsTFileAny(param, "TOF/ChannelCalib", metadataChannelCalib, minTimeStamp); - } else + } else { api.storeAsTFileAny(param, "TOF/ChannelCalib", metadataChannelCalib, minTimeStamp, maxTimeStamp); + } } //______________________________________________________________________ diff --git a/Detectors/TOF/calibration/src/CollectCalibInfoTOF.cxx b/Detectors/TOF/calibration/src/CollectCalibInfoTOF.cxx index 1129db6910856..3c96a22781f82 100644 --- a/Detectors/TOF/calibration/src/CollectCalibInfoTOF.cxx +++ b/Detectors/TOF/calibration/src/CollectCalibInfoTOF.cxx @@ -144,10 +144,11 @@ void CollectCalibInfoTOF::addHit(o2::dataformats::CalibInfoTOF& calibInfo) if (mTOFCollectedCalibInfo[calibInfo.getTOFChIndex()].size() == MAXNUMBEROFHITS) { // the current channel has arrived to the limit of hits that we can store between two fills --> filling the tree fillTree(); } - if (calibInfo.getTimestamp() < mMinTimestamp.GetVal() || mMinTimestamp.GetVal() == -1) + if (calibInfo.getTimestamp() < mMinTimestamp.GetVal() || mMinTimestamp.GetVal() == -1) { mMinTimestamp.SetVal(calibInfo.getTimestamp()); - else if (calibInfo.getTimestamp() > mMaxTimestamp.GetVal()) + } else if (calibInfo.getTimestamp() > mMaxTimestamp.GetVal()) { mMaxTimestamp.SetVal(calibInfo.getTimestamp()); + } } //______________________________________________ void CollectCalibInfoTOF::fillTree() diff --git a/Detectors/TOF/calibration/src/LHCClockCalibrator.cxx b/Detectors/TOF/calibration/src/LHCClockCalibrator.cxx index a450afd6ce6a1..5391071dd833b 100644 --- a/Detectors/TOF/calibration/src/LHCClockCalibrator.cxx +++ b/Detectors/TOF/calibration/src/LHCClockCalibrator.cxx @@ -10,7 +10,7 @@ #include "TOFCalibration/LHCClockCalibrator.h" #include "Framework/Logger.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "CommonUtils/MemFileHelper.h" #include "CCDB/CcdbApi.h" #include "DetectorsCalibration/Utils.h" @@ -21,7 +21,7 @@ namespace tof { using Slot = o2::calibration::TimeSlot<o2::tof::LHCClockDataHisto>; -using o2::math_utils::math_base::fitGaus; +using o2::math_utils::fitGaus; using LHCphase = o2::dataformats::CalibLHCphaseTOF; using clbUtils = o2::calibration::Utils; @@ -81,7 +81,7 @@ void LHCClockCalibrator::finalizeSlot(Slot& slot) << c->getEntries() << " entries"; std::vector<float> fitValues; float* array = &c->histo[0]; - int fitres = fitGaus(c->nbins, array, -(c->range), c->range, fitValues); + double fitres = fitGaus(c->nbins, array, -(c->range), c->range, fitValues); if (fitres >= 0) { LOG(INFO) << "Fit result " << fitres << " Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; } else { diff --git a/Detectors/TOF/calibration/src/TOFCalibCollector.cxx b/Detectors/TOF/calibration/src/TOFCalibCollector.cxx new file mode 100644 index 0000000000000..eef82b962e436 --- /dev/null +++ b/Detectors/TOF/calibration/src/TOFCalibCollector.cxx @@ -0,0 +1,167 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TOFCalibration/TOFCalibCollector.h" +#include "Framework/Logger.h" +#include <cassert> +#include <iostream> +#include <sstream> +#include <TStopwatch.h> + +namespace o2 +{ +namespace tof +{ + +using Slot = o2::calibration::TimeSlot<o2::tof::TOFCalibInfoSlot>; + +//_____________________________________________ +void TOFCalibInfoSlot::fill(const gsl::span<const o2::dataformats::CalibInfoTOF> data) +{ + // fill container + // we do not apply any calibration at this stage, it will be applied when we + // process the data before filling the CCDB in the separate process + + // we first order the data that arrived, to improve speed when filling + int nd = data.size(); + LOG(DEBUG) << "entries in incoming data = " << nd; + std::vector<int> ord(nd); + std::iota(ord.begin(), ord.end(), 0); + std::sort(ord.begin(), ord.end(), [&data](int i, int j) { return data[i].getTOFChIndex() < data[j].getTOFChIndex(); }); + int chPrev = 0, offsPrev = 0; + for (int i = 0; i < nd; i++) { + const auto& dti = data[ord[i]]; + auto ch = dti.getTOFChIndex(); + auto offset = offsPrev; + if (ch > chPrev) { + offset += std::accumulate(mEntriesSlot.begin() + chPrev, mEntriesSlot.begin() + ch, 0); + } + offsPrev = offset; + chPrev = ch; + mTOFCollectedCalibInfoSlot.emplace(mTOFCollectedCalibInfoSlot.begin() + offset, data[ord[i]].getTimestamp(), data[ord[i]].getDeltaTimePi(), data[ord[i]].getTot(), data[ord[i]].getFlags()); + mEntriesSlot[ch]++; + } +} +//_____________________________________________ +void TOFCalibInfoSlot::merge(const TOFCalibInfoSlot* prev) +{ + // merge data of 2 slots + + LOG(DEBUG) << "Merging two slots with entries: current slot -> " << mTOFCollectedCalibInfoSlot.size() << " , previous slot -> " << prev->mTOFCollectedCalibInfoSlot.size(); + + int offset = 0, offsetPrev = 0; + std::vector<o2::dataformats::CalibInfoTOFshort> tmpVector; + for (int ch = 0; ch < Geo::NCHANNELS; ch++) { + if (mEntriesSlot[ch] != 0) { + for (int i = offset; i < offset + mEntriesSlot[ch]; i++) { + tmpVector.emplace_back(mTOFCollectedCalibInfoSlot[i]); + } + offset += mEntriesSlot[ch]; + } + if (prev->mEntriesSlot[ch] != 0) { + for (int i = offsetPrev; i < offsetPrev + prev->mEntriesSlot[ch]; i++) { + tmpVector.emplace_back(prev->mTOFCollectedCalibInfoSlot[i]); + } + offsetPrev += prev->mEntriesSlot[ch]; + mEntriesSlot[ch] += prev->mEntriesSlot[ch]; + } + } + mTOFCollectedCalibInfoSlot.swap(tmpVector); + LOG(DEBUG) << "After merging the size is " << mTOFCollectedCalibInfoSlot.size(); + return; +} +//_____________________________________________ +void TOFCalibInfoSlot::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 " << mTOFCollectedCalibInfoSlot.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 mTOFCollectedCalibInfo is " << *maxElementIndex << " for channel " << channelIndex; + return; +} + +//_____________________________________________ +void TOFCalibInfoSlot::printEntries() const +{ + // to print number of entries in the tree and per channel + + LOG(INFO) << "Total number of entries " << mTOFCollectedCalibInfoSlot.size(); + for (int i = 0; i < mEntriesSlot.size(); ++i) { + if (mEntriesSlot[i] != 0) { + LOG(INFO) << "channel " << i << " has " << mEntriesSlot[i] << " entries"; + } + } + return; +} + +//=================================================================== + +//_____________________________________________ +void TOFCalibCollector::initOutput() +{ + // emptying the vectors + + mTOFCollectedCalibInfo.clear(); + for (int ch = 0; ch < Geo::NCHANNELS; ch++) { + mEntries[ch] = 0; + } + + return; +} + +//_____________________________________________ +bool TOFCalibCollector::hasEnoughData(const Slot& slot) const +{ + + // We define that we have enough data if the tree is big enough. + // each CalibInfoTOFShort is composed of one int, two floats, one unsigned char --> 13 bytes + // E.g. supposing that we have 256 entries per channel (which is an upper limit ) --> ~523 MB + // we can check if we have at least 1 GB of data --> 500*o2::tof::Geo::NCHANNELS entries in the vector + // (see header file for the fact that mMaxNumOfHits = 500) + // The case in which mScaleMaxNumOfHits = false allows for a fast check + + if (mTest) { + return true; + } + const o2::tof::TOFCalibInfoSlot* c = slot.getContainer(); + LOG(INFO) << "we have " << c->getCollectedCalibInfoSlot().size() << " entries"; + int maxNumberOfHits = mAbsMaxNumOfHits ? mMaxNumOfHits : mMaxNumOfHits * o2::tof::Geo::NCHANNELS; + if (mTFsendingPolicy || c->getCollectedCalibInfoSlot().size() > maxNumberOfHits) { + return true; + } + return false; +} + +//_____________________________________________ +void TOFCalibCollector::finalizeSlot(Slot& slot) +{ + // here we fill the tree with the remaining stuff that was not filled before + + o2::tof::TOFCalibInfoSlot* c = slot.getContainer(); + mTOFCollectedCalibInfo = c->getCollectedCalibInfoSlot(); + LOG(DEBUG) << "vector of CalibTOFInfoShort received with size = " << mTOFCollectedCalibInfo.size(); + mEntries = c->getEntriesPerChannel(); + return; +} + +//_____________________________________________ +Slot& TOFCalibCollector::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<TOFCalibInfoSlot>()); + return slot; +} + +} // end namespace tof +} // end namespace o2 diff --git a/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h b/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h index 004e5f03f4ace..cdfabd09f3ae7 100644 --- a/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h +++ b/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h @@ -28,4 +28,14 @@ #pragma link C++ class o2::calibration::TimeSlot < o2::tof::TOFChannelData> + ; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::dataformats::CalibInfoTOF, o2::tof::TOFChannelData> + ; +#pragma link C++ class o2::tof::TOFCalibInfoSlot + ; +#pragma link C++ class o2::tof::TOFCalibCollector + ; +#pragma link C++ class o2::calibration::TimeSlot < o2::tof::TOFCalibInfoSlot> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::dataformats::CalibInfoTOF, o2::tof::TOFCalibInfoSlot> + ; + +#pragma link C++ class std::bitset < o2::tof::Geo::NCHANNELS> + ; +#pragma link C++ struct std::pair < uint64_t, double> + ; +#pragma link C++ struct o2::tof::TOFDCSinfo + ; +#pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, o2::tof::TOFDCSinfo> + ; + #endif diff --git a/Detectors/TOF/calibration/src/TOFChannelCalibrator.cxx b/Detectors/TOF/calibration/src/TOFChannelCalibrator.cxx index e242d5f441cd3..a2d89aa0ec793 100644 --- a/Detectors/TOF/calibration/src/TOFChannelCalibrator.cxx +++ b/Detectors/TOF/calibration/src/TOFChannelCalibrator.cxx @@ -10,7 +10,7 @@ #include "TOFCalibration/TOFChannelCalibrator.h" #include "Framework/Logger.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "CommonUtils/MemFileHelper.h" #include "CCDB/CcdbApi.h" #include "DetectorsCalibration/Utils.h" @@ -31,7 +31,7 @@ using Slot = o2::calibration::TimeSlot<o2::tof::TOFChannelData>; using TimeSlewing = o2::dataformats::CalibTimeSlewingParamTOF; using clbUtils = o2::calibration::Utils; using boost::histogram::indexed; -using o2::math_utils::math_base::fitGaus; +using o2::math_utils::fitGaus; //using boost::histogram::algorithm; // not sure why it does not work... //_____________________________________________ @@ -43,11 +43,15 @@ void TOFChannelData::fill(const gsl::span<const o2::dataformats::CalibInfoTOF> d int sector = ch / o2::tof::Geo::NPADSXSECTOR; int chInSect = ch % o2::tof::Geo::NPADSXSECTOR; auto dt = data[i].getDeltaTimePi(); + auto tot = data[i].getTot(); // TO BE DISCUSSED: could it be that the LHCphase is too old? If we ar ein sync mode, it could be that it is not yet created for the current run, so the one from the previous run (which could be very old) is used. But maybe it does not matter much, since soon enough a calibrated LHC phase should be produced auto corr = mCalibTOFapi->getTimeCalibration(ch, tot); // we take into account LHCphase, offsets and time slewing LOG(DEBUG) << "inserting in channel " << ch << ": dt = " << dt << ", tot = " << tot << ", corr = " << corr << ", corrected dt = " << dt - corr; - mHisto[sector](dt - corr, chInSect); // we pass the calibrated time + + dt -= corr; + + mHisto[sector](dt, chInSect); // we pass the calibrated time mEntries[ch] += 1; } } @@ -59,6 +63,9 @@ void TOFChannelData::merge(const TOFChannelData* prev) for (int isect = 0; isect < o2::tof::Geo::NSECTORS; isect++) { mHisto[isect] += prev->getHisto(isect); } + for (int ich = 0; ich < o2::tof::Geo::NCHANNELS; ich++) { + mEntries[ich] += prev->mEntries[ich]; + } } //_____________________________________________ @@ -74,7 +81,7 @@ bool TOFChannelData::hasEnoughData(int minEntries) const auto minElementIndex = std::min_element(mEntries.begin(), mEntries.end()); LOG(INFO) << "minElement is at position " << std::distance(mEntries.begin(), minElementIndex) << " and is " << *minElementIndex; bool enough = *minElementIndex < minEntries ? false : true; - LOG(INFO) << "hasEnough = " << (int)enough; + LOG(INFO) << "hasEnough = (minEntry=" << minEntries << ") " << (int)enough; return enough; } @@ -116,7 +123,7 @@ void TOFChannelData::print(int isect) const LOG(INFO) << "Number of entries in histogram: " << boost::histogram::algorithm::sum(mHisto[isect]); for (auto&& x : indexed(mHisto[isect])) { // does not work also when I use indexed(*(mHisto[sector])) cnt++; - //LOG(INFO) << " c " << cnt << " i " << x.index(0) << " j " << x.index(1) << " b0 " << x.bin(0) << " b1 " << x.bin(1) << " val= " << *x << "|" << x.get(); + LOG(DEBUG) << " c " << cnt << " i " << x.index(0) << " j " << x.index(1) << " b0 " << x.bin(0) << " b1 " << x.bin(1) << " val= " << *x << "|" << x.get(); if (x.get() > 0) { LOG(INFO) << "x = " << x.get() << " c " << cnt; } @@ -128,8 +135,9 @@ void TOFChannelData::print(int isect) const void TOFChannelData::printEntries() const { // to print number of entries per channel - for (int i = 0; i < mEntries.size(); ++i) + for (int i = 0; i < mEntries.size(); ++i) { LOG(INFO) << "channel " << i << " has " << mEntries[i] << " entries"; + } } //_____________________________________________ @@ -138,15 +146,15 @@ int TOFChannelData::findBin(float v) const // find the bin along the x-axis (with t-texp) where the value "v" is; this does not depend on the channel // (axis 1), nor on the sector, so we use sector0 - if (v == mRange) + if (v == mRange) { v -= 1.e-1; + } + + LOG(DEBUG) << "In FindBin, v = : " << v; + LOG(DEBUG) << "bin0 limits: lower = " << mHisto[0].axis(0).bin(0).lower() << ", upper = " << mHisto[0].axis(0).bin(0).upper(); + LOG(DEBUG) << "bin1000 limits: lower = " << mHisto[0].axis(0).bin(mNBins - 1).lower() << ", upper = " << mHisto[0].axis(0).bin(mNBins - 1).upper(); + LOG(DEBUG) << "v = " << v << " is in bin " << mHisto[0].axis(0).index(v); - /* - LOG(INFO) << "In FindBin, v = : " << v; - LOG(INFO) << "bin0 limits: lower = " << mHisto[0].axis(0).bin(0).lower() << ", upper = " << mHisto[0].axis(0).bin(0).upper(); - LOG(INFO) << "bin1000 limits: lower = " << mHisto[0].axis(0).bin(mNBins-1).lower() << ", upper = " << mHisto[0].axis(0).bin(mNBins-1).upper(); - LOG(INFO) << "v = " << v << " is in bin " << mHisto[0].axis(0).index(v); - */ return mHisto[0].axis(0).index(v); } @@ -155,49 +163,47 @@ float TOFChannelData::integral(int chmin, int chmax, float binmin, float binmax) { // calculates the integral in [chmin, chmax] and in [binmin, binmax] - if (binmin < -mRange || binmax > mRange || chmin < 0 || chmax >= o2::tof::Geo::NCHANNELS) + if (binmin < -mRange || binmax > mRange || chmin < 0 || chmax >= o2::tof::Geo::NCHANNELS) { throw std::runtime_error("Check your bins, we cannot calculate the integrals in under/overflows bins"); - if (binmax < binmin || chmax < chmin) + } + if (binmax < binmin || chmax < chmin) { throw std::runtime_error("Check your bin limits!"); + } int sector = chmin / o2::tof::Geo::NPADSXSECTOR; - if (sector != chmax / o2::tof::Geo::NPADSXSECTOR) + if (sector != chmax / o2::tof::Geo::NPADSXSECTOR) { throw std::runtime_error("We cannot integrate over channels that belong to different sectors"); + } int chinsectormin = chmin % o2::tof::Geo::NPADSXSECTOR; int chinsectormax = chmax % o2::tof::Geo::NPADSXSECTOR; - //LOG(INFO) << "Calculating integral for channel " << ich << " which is in sector " << sector - // << " (channel in sector is " << chinsector << ")";; - // LOG(INFO) << "Bin min = " << binmin << ", binmax = " << binmax << - // ", chmin = " << chmin << ", chmax = " << chmax << - // ", chinsectormin = " << chinsectormin << ", chinsector max = " << chinsectormax; float res2 = 0; - TStopwatch t3; + //TStopwatch t3; int ind = -1; int binxmin = findBin(binmin); int binxmax = findBin(binmax); LOG(DEBUG) << "binxmin = " << binxmin << ", binxmax = " << binxmax; - t3.Start(); + //t3.Start(); for (unsigned j = chinsectormin; j <= chinsectormax; ++j) { for (unsigned i = binxmin; i <= binxmax; ++i) { const auto& v = mHisto[sector].at(i, j); res2 += v; } } - t3.Stop(); - LOG(INFO) << "Time for integral looping over axis (result = " << res2 << "):"; - t3.Print(); + //t3.Stop(); + LOG(DEBUG) << "Time for integral looping over axis (result = " << res2 << "):"; + //t3.Print(); return res2; /* // what is below is only for alternative methods which all proved to be slower float res = 0, res1 = 0; - TStopwatch t1, t2, + //TStopwatch t1, t2, int startCount = chinsectormin * mNBins + binxmin; int endCount = chinsectormax * mNBins + binxmax; // = startCount + (chinsectormax - chinsectormin) * mNBins + (binxmax - binxmin); LOG(DEBUG) << "startCount = " << startCount << " endCount = " << endCount-1; - t2.Start(); + //t2.Start(); int counts = -1; for (auto&& x : indexed(mHisto[sector])) { counts++; @@ -217,10 +223,10 @@ float TOFChannelData::integral(int chmin, int chmax, float binmin, float binmax) // << x.bin(0).upper() << "], and bin " << x.index(1) << " along y" << " with content " << x.get() << " --> res1 = " << res1; } } - t2.Stop(); - LOG(INFO) << "Time for integral looping over restricted range (result = " << res1 << "):"; - t2.Print(); - t1.Start(); + //t2.Stop(); + //LOG(DEBUG) << "Time for integral looping over restricted range (result = " << res1 << "):"; + //t2.Print(); + //t1.Start(); ind = -1; for (auto&& x : indexed(mHisto[sector])) { ind++; @@ -231,12 +237,12 @@ float TOFChannelData::integral(int chmin, int chmax, float binmin, float binmax) // << x.bin(0).upper() << "], and bin " << x.index(1) << " along y" << " with content " << x.get(); } } - t1.Stop(); - LOG(INFO) << "Time for integral looping (result = " << res << "):"; - t1.Print(); - LOG(INFO) << "Reducing... "; - TStopwatch t; - t.Start(); + //t1.Stop(); + //LOG(DEBUG) << "Time for integral looping (result = " << res << "):"; + //t1.Print(); + LOG(DEBUG) << "Reducing... "; + //TStopwatch t; + //t.Start(); if (binmin == binmax) binmax += 1.e-1; float chinsectorminfl = float(chinsectormin); float chinsectormaxfl = float(chinsectormax); @@ -246,10 +252,10 @@ float TOFChannelData::integral(int chmin, int chmax, float binmin, float binmax) auto hch = boost::histogram::algorithm::reduce(mHisto[sector], boost::histogram::algorithm::shrink(1, chinsectorminfl, chinsectormaxfl), boost::histogram::algorithm::shrink(0, binmin, binmax)); - t.Stop(); - LOG(INFO) << "Time for projection with shrink"; - t.Print(); - LOG(INFO) << "...done."; + //t.Stop(); + //LOG(DEBUG) << "Time for projection with shrink"; + //t.Print(); + //LOG(DEBUG) << "...done."; //int sizeBeforeAxis1 = mHisto[sector].axis(1).size(); //int sizeAfterAxis1 = hch.axis(1).size(); @@ -258,12 +264,12 @@ float TOFChannelData::integral(int chmin, int chmax, float binmin, float binmax) //std::cout << "axis size before reduction: axis 0: " << sizeBeforeAxis0 << ", axis 1: " << sizeBeforeAxis1 << std::endl; //std::cout << "axis size after reduction: axis 0: " << sizeAfterAxis0 << ", axis 1: " << sizeAfterAxis1 << std::endl; - t.Start(); + //t.Start(); auto indhch = indexed(hch); const double enthchInd = std::accumulate(indhch.begin(), indhch.end(), 0.0); - t.Stop(); - LOG(INFO) << "Time for accumulate (result = " << enthchInd << ")"; - t.Print(); + //t.Stop(); + //LOG(DEBUG) << "Time for accumulate (result = " << enthchInd << ")"; + //t.Print(); return enthchInd; */ @@ -282,44 +288,43 @@ float TOFChannelData::integral(int chmin, int chmax, int binxmin, int binxmax) c { // calculates the integral in [chmin, chmax] and in [binmin, binmax] - if (binxmin < 0 || binxmax > mNBins || chmin < 0 || chmax >= o2::tof::Geo::NCHANNELS) + if (binxmin < 0 || binxmax > mNBins || chmin < 0 || chmax >= o2::tof::Geo::NCHANNELS) { throw std::runtime_error("Check your bins, we cannot calculate the integrals in under/overflows bins"); - if (binxmax < binxmin || chmax < chmin) + } + if (binxmax < binxmin || chmax < chmin) { throw std::runtime_error("Check your bin limits!"); + } int sector = chmin / o2::tof::Geo::NPADSXSECTOR; - if (sector != chmax / o2::tof::Geo::NPADSXSECTOR) + if (sector != chmax / o2::tof::Geo::NPADSXSECTOR) { throw std::runtime_error("We cannot integrate over channels that belong to different sectors"); + } int chinsectormin = chmin % o2::tof::Geo::NPADSXSECTOR; int chinsectormax = chmax % o2::tof::Geo::NPADSXSECTOR; - //LOG(INFO) << "Calculating integral for channel " << ich << " which is in sector " << sector - // << " (channel in sector is " << chinsector << ")";; - //LOG(INFO) << "Bin min = " << binmin << ", binmax = " << binmax << - // ", chmin = " << chmin << ", chmax" << chmax << - // ", chinsectormin = " << chinsector min << ", chinsector max = " << chinsectormax; + float res2 = 0; - TStopwatch t3; - t3.Start(); + //TStopwatch t3; + //t3.Start(); for (unsigned j = chinsectormin; j <= chinsectormax; ++j) { for (unsigned i = binxmin; i <= binxmax; ++i) { const auto& v = mHisto[sector].at(i, j); res2 += v; } } - t3.Stop(); - LOG(INFO) << "Time for integral looping over axis (result = " << res2 << "):"; - t3.Print(); + //t3.Stop(); + LOG(DEBUG) << "Time for integral looping over axis (result = " << res2 << "):"; + //t3.Print(); return res2; /* // all that is below is alternative methods, all proved to be slower float res = 0, res1 = 0; - TStopwatch t1, t2; + //TStopwatch t1, t2; int ind = -1; int startCount = chinsectormin * mNBins + binxmin; int endCount = chinsectormax * mNBins + binxmax; // = startCount + (chinsectormax - chinsectormin) * mNBins + (binxmax - binxmin); LOG(DEBUG) << "startCount = " << startCount << " endCount = " << endCount-1; - t2.Start(); + //t2.Start(); int counts = -1; for (auto&& x : indexed(mHisto[sector])) { counts++; @@ -341,10 +346,10 @@ float TOFChannelData::integral(int chmin, int chmax, int binxmin, int binxmax) c // << " --> res1 = " << res1; } } - t2.Stop(); - LOG(INFO) << "Time for integral looping over restricted range (result = " << res1 << "):"; - t2.Print(); - t1.Start(); + //t2.Stop(); + //LOG(DEBUG) << "Time for integral looping over restricted range (result = " << res1 << "):"; + //t2.Print(); + //t1.Start(); for (auto&& x : indexed(mHisto[sector])) { ind++; if ((x.index(0) >= binxmin && x.index(0) <= binxmax) && (x.index(1) >= chinsectormin && x.index(1) <= chinsectormax)) { @@ -352,18 +357,18 @@ float TOFChannelData::integral(int chmin, int chmax, int binxmin, int binxmax) c //LOG(INFO) << "ind = " << ind << " will add bin " << x.index(0) << " along x and bin " << x.index(1) << " along y"; } } - t1.Stop(); - LOG(INFO) << "Time for integral looping (result = " << res << "):"; - t1.Print(); - LOG(INFO) << "Reducing... "; - TStopwatch t; - t.Start(); + //t1.Stop(); + //LOG(DEBUG) << "Time for integral looping (result = " << res << "):"; + //t1.Print(); + //LOG(DEBUG) << "Reducing... "; + //TStopwatch t; + //t.Start(); auto hch = boost::histogram::algorithm::reduce(mHisto[sector], boost::histogram::algorithm::slice(1, chinsectormin, chinsectormax+1), boost::histogram::algorithm::slice(0, binxmin, binxmax+1)); // we need to add "+1" - t.Stop(); - LOG(INFO) << "Time for projection with slice"; - t.Print(); + //t.Stop(); + //LOG(DEBUG) << "Time for projection with slice"; + //t.Print(); //LOG(INFO) << "...done."; //int sizeBeforeAxis1 = mHisto[sector].axis(1).size(); @@ -374,12 +379,12 @@ float TOFChannelData::integral(int chmin, int chmax, int binxmin, int binxmax) c //std::cout << "axis size after reduction: axis 0: " << sizeAfterAxis0 << ", axis 1: " << sizeAfterAxis1 << std::endl; // first way: using indexed (which excludes under/overflow) - t.Start(); + //t.Start(); auto indhch = indexed(hch); const double enthchInd = std::accumulate(indhch.begin(), indhch.end(), 0.0); - t.Stop(); - LOG(INFO) << "Time for accumulate (result = " << enthchInd << ")"; - t.Print(); + //t.Stop(); + //LOG(DEBUG) << "Time for accumulate (result = " << enthchInd << ")"; + //t.Print(); return enthchInd; */ } @@ -455,11 +460,13 @@ void TOFChannelCalibrator::finalizeSlot(Slot& slot) */ std::vector<int> entriesPerChannel = c->getEntriesPerChannel(); if (entriesPerChannel.at(ich) == 0) { + continue; // skip always since a channel with 0 entries is normal, it will be flagged as problematic if (mTest) { LOG(DEBUG) << "Skipping channel " << ich << " because it has zero entries, but it should not be"; // should become error! continue; - } else + } else { throw std::runtime_error("We found one channel with no entries, we cannot calibrate!"); + } } // more efficient way @@ -472,16 +479,34 @@ void TOFChannelCalibrator::finalizeSlot(Slot& slot) } } - int fitres = fitGaus(c->getNbins(), histoValues.data(), -(c->getRange()), c->getRange(), fitValues); + double fitres = fitGaus(c->getNbins(), histoValues.data(), -(c->getRange()), c->getRange(), fitValues); + + if (fitValues[2] < 0) { + fitValues[2] = -fitValues[2]; + } + if (fitres >= 0) { LOG(DEBUG) << "Channel " << ich << " :: Fit result " << fitres << " Mean = " << fitValues[1] << " Sigma = " << fitValues[2]; } else { - LOG(ERROR) << "Channel " << ich << " :: Fit failed with result = " << fitres; + LOG(INFO) << "Channel " << ich << " :: Fit failed with result = " << fitres; } float fractionUnderPeak; float intmin = fitValues[1] - 5 * fitValues[2]; // mean - 5*sigma float intmax = fitValues[1] + 5 * fitValues[2]; // mean + 5*sigma + if (intmin < -mRange) { + intmin = -mRange; + } + if (intmax < -mRange) { + intmax = -mRange; + } + if (intmin > mRange) { + intmin = mRange; + } + if (intmax > mRange) { + intmax = mRange; + } + /* // needed if we calculate the integral using the values int binmin = c->findBin(intmin); diff --git a/Detectors/TOF/calibration/src/TOFDCSProcessor.cxx b/Detectors/TOF/calibration/src/TOFDCSProcessor.cxx new file mode 100644 index 0000000000000..9908076fd1a69 --- /dev/null +++ b/Detectors/TOF/calibration/src/TOFDCSProcessor.cxx @@ -0,0 +1,561 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <TOFCalibration/TOFDCSProcessor.h> +#include "Rtypes.h" +#include <deque> +#include <string> +#include <algorithm> +#include <iterator> +#include <cstring> +#include <bitset> + +using namespace o2::tof; +using namespace o2::dcs; + +using DeliveryType = o2::dcs::DeliveryType; +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; + +ClassImp(o2::tof::TOFDCSinfo); + +void TOFDCSinfo::print() const +{ + LOG(INFO) << "First Value: timestamp = " << firstValue.first << ", value = " << firstValue.second; + LOG(INFO) << "Last Value: timestamp = " << lastValue.first << ", value = " << lastValue.second; + LOG(INFO) << "Mid Value: timestamp = " << midValue.first << ", value = " << midValue.second; + LOG(INFO) << "Max Change: timestamp = " << maxChange.first << ", value = " << maxChange.second; +} + +//__________________________________________________________________ + +void TOFDCSProcessor::init(const std::vector<DPID>& pids) +{ + // fill the array of the DPIDs that will be used by TOF + // pids should be provided by CCDB + + for (const auto& it : pids) { + mPids[it] = false; + mTOFDCS[it].makeEmpty(); + } + + for (int iddl = 0; iddl < Geo::kNDDL; ++iddl) { + for (int ifeac = 0; ifeac < NFEACS; ++ifeac) { + getStripsConnectedToFEAC(iddl, ifeac, mFeacInfo[iddl][ifeac]); + } + } +} + +//__________________________________________________________________ + +int TOFDCSProcessor::process(const gsl::span<const DPCOM> dps) +{ + + // first we check which DPs are missing - if some are, it means that + // the delta map was sent + if (mVerbose) { + LOG(INFO) << "\n\n\nProcessing new TF\n-----------------"; + } + if (!mStartTFset) { + mStartTF = mTF; + mStartTFset = true; + } + + std::unordered_map<DPID, DPVAL> mapin; + for (auto& it : dps) { + mapin[it.id] = it.data; + } + for (auto& it : mPids) { + const auto& el = mapin.find(it.first); + if (el == mapin.end()) { + LOG(DEBUG) << "DP " << it.first << " not found in map"; + } else { + LOG(DEBUG) << "DP " << it.first << " found in map"; + } + } + + mUpdateFeacStatus = false; // by default, we do not foresee a new entry in the CCDB for the FEAC + mUpdateHVStatus = false; // by default, we do not foresee a new entry in the CCDB for the HV + + // now we process all DPs, one by one + for (const auto& it : dps) { + // we process only the DPs defined in the configuration + const auto& el = mPids.find(it.id); + if (el == mPids.end()) { + LOG(INFO) << "DP " << it.id << " not found in TOFDCSProcessor, we will not process it"; + continue; + } + processDP(it); + mPids[it.id] = true; + } + + if (mUpdateFeacStatus) { + updateFEACCCDB(); + } + + if (mUpdateHVStatus) { + updateHVCCDB(); + } + + return 0; +} + +//__________________________________________________________________ + +int TOFDCSProcessor::processDP(const DPCOM& dpcom) +{ + + // processing single DP + + auto& dpid = dpcom.id; + const auto& type = dpid.get_type(); + auto& val = dpcom.data; + if (mVerbose) { + if (type == RAW_DOUBLE) { + LOG(INFO); + LOG(INFO) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue<double>(dpcom); + } else if (type == RAW_INT) { + LOG(INFO); + LOG(INFO) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue<int32_t>(dpcom); + } + } + auto flags = val.get_flags(); + if (processFlags(flags, dpid.get_alias()) == 0) { + // now I need to access the correct element + if (type == RAW_DOUBLE) { + // for these DPs, we will store the first, last, mid value, plus the value where the maximum variation occurred + auto& dvect = mDpsdoublesmap[dpid]; + LOG(DEBUG) << "mDpsdoublesmap[dpid].size() = " << dvect.size(); + auto etime = val.get_epoch_time(); + if (dvect.size() == 0 || + etime != dvect.back().get_epoch_time()) { // we check + // that we did not get the + // same timestamp as the + // latest one + dvect.push_back(val); + } + } + + if (type == RAW_INT) { + // for these DPs, we need some processing + if (std::strstr(dpid.get_alias(), "FEACSTATUS") != nullptr) { // DP is FEACSTATUS + std::string aliasStr(dpid.get_alias()); + // extracting DDL number (regex is quite slow, using this way) + const auto offs = std::strlen("TOF_FEACSTATUS_"); + std::size_t const nn = aliasStr.find_first_of("0123456789", offs); + std::size_t const mm = aliasStr.find_first_not_of("0123456789", nn); + std::string ddlStr = aliasStr.substr(nn, mm != std::string::npos ? mm - nn : mm); + auto iddl = std::stoi(ddlStr); + std::bitset<8> feacstatus(o2::dcs::getValue<int32_t>(dpcom)); + if (mVerbose) { + LOG(INFO) << "DDL: " << iddl << ": Prev FEAC = " << mPrevFEACstatus[iddl] << ", new = " << feacstatus; + } + if (feacstatus == mPrevFEACstatus[iddl]) { + if (mVerbose) { + LOG(INFO) << "Same FEAC status as before, we do nothing"; + } + return 0; + } + if (mVerbose) { + LOG(INFO) << "Something changed in LV for DDL " << iddl << ", we need to check what"; + } + mUpdateFeacStatus = true; + int plate = -1, strip = -1; + int det[5] = {iddl / 4, -1, -1, -1, -1}; + for (auto ifeac = 0; ifeac < NFEACS; ++ifeac) { // we have one bit per FEAC + auto singlefeacstatus = feacstatus[ifeac]; + for (int istrip = 0; istrip < 6; ++istrip) { + if (mFeacInfo[iddl][ifeac].stripInSM[istrip] == -1) { + continue; + } + for (int ipadz = 0; ipadz < Geo::NPADZ; ++ipadz) { + for (int ipadx = mFeacInfo[iddl][ifeac].firstPadX; ipadx <= mFeacInfo[iddl][ifeac].lastPadX; ++ipadx) { + Geo::getStripAndModule(mFeacInfo[iddl][ifeac].stripInSM[istrip], plate, strip); + det[1] = plate; + det[2] = strip; + det[3] = ipadz; + det[4] = ipadx; + int channelIdx = Geo::getIndex(det); + if (mFeac[channelIdx] != singlefeacstatus) { + mFeac[channelIdx] = singlefeacstatus; + } + } + } + } + } // end loop on FEACs + if (mVerbose) { + LOG(INFO) << "Updating previous FEAC status for DDL " << iddl; + } + mPrevFEACstatus[iddl] = feacstatus; + } // end processing current DP, when it is of type FEACSTATUS + + if (std::strstr(dpid.get_alias(), "HVSTATUS") != nullptr) { // DP is HVSTATUS + std::string aliasStr(dpid.get_alias()); + // extracting SECTOR and PLATE number (regex is quite slow, using this way) + const auto offs = std::strlen("TOF_HVSTATUS_SM"); + std::size_t const nn = aliasStr.find_first_of("0123456789", offs); + std::size_t const mm = aliasStr.find_first_not_of("0123456789", nn); + std::size_t const oo = aliasStr.find_first_of("0123456789", mm); + std::size_t const pp = aliasStr.find_first_not_of("0123456789", oo); + std::string sectorStr = aliasStr.substr(nn, mm != std::string::npos ? mm - nn : mm); + auto isect = std::stoi(sectorStr); + std::string plateStr = aliasStr.substr(oo, pp != std::string::npos ? pp - oo : pp); + auto iplat = std::stoi(plateStr); + std::bitset<19> hvstatus(o2::dcs::getValue<int32_t>(dpcom)); + if (mVerbose) { + LOG(INFO) << "Sector: " << isect << ", plate = " << iplat << ": Prev HV = " + << mPrevHVstatus[iplat][isect] << ", new = " << hvstatus; + } + if (hvstatus == mPrevHVstatus[iplat][isect]) { + if (mVerbose) { + LOG(INFO) << "Same HV status as before, we do nothing"; + } + return 0; + } + if (mVerbose) { + LOG(INFO) << "Something changed in HV for Sect " << isect << " and plate " + << iplat << ", we need to check what"; + } + mUpdateHVStatus = true; + int det[5] = {isect, iplat, -1, -1, -1}; + auto nStrips = (iplat == 2 ? Geo::NSTRIPA : (iplat == 0 || iplat == 4) ? Geo::NSTRIPC : Geo::NSTRIPB); + for (auto istrip = 0; istrip < nStrips; ++istrip) { + auto singlestripHV = hvstatus[istrip]; + for (int ipadz = 0; ipadz < Geo::NPADZ; ++ipadz) { + for (int ipadx = 0; ipadx < Geo::NPADX; ++ipadx) { + det[2] = istrip; + det[3] = ipadz; + det[4] = ipadx; + int channelIdx = Geo::getIndex(det); + if (mHV[channelIdx] != singlestripHV) { + mHV[channelIdx] = singlestripHV; + } + } + } + } // end loop on strips + if (mVerbose) { + LOG(INFO) << "Updating previous HV status for Sector: " << isect << ", plate = " << iplat; + } + mPrevHVstatus[iplat][isect] = hvstatus; + } //end processing current DP, when it is of type HVSTATUS + } + } + return 0; +} + +//______________________________________________________________________ + +uint64_t TOFDCSProcessor::processFlags(const uint64_t flags, const char* pid) +{ + + // function to process the flag. the return code zero means that all is fine. + // anything else means that there was an issue + + // for now, I don't know how to use the flags, so I do nothing + + if (flags & DataPointValue::KEEP_ALIVE_FLAG) { + LOG(DEBUG) << "KEEP_ALIVE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::END_FLAG) { + LOG(DEBUG) << "END_FLAG active for DP " << pid; + } + if (flags & DataPointValue::FBI_FLAG) { + LOG(DEBUG) << "FBI_FLAG active for DP " << pid; + } + if (flags & DataPointValue::NEW_FLAG) { + LOG(DEBUG) << "NEW_FLAG active for DP " << pid; + } + if (flags & DataPointValue::DIRTY_FLAG) { + LOG(DEBUG) << "DIRTY_FLAG active for DP " << pid; + } + if (flags & DataPointValue::TURN_FLAG) { + LOG(DEBUG) << "TURN_FLAG active for DP " << pid; + } + if (flags & DataPointValue::WRITE_FLAG) { + LOG(DEBUG) << "WRITE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::READ_FLAG) { + LOG(DEBUG) << "READ_FLAG active for DP " << pid; + } + if (flags & DataPointValue::OVERWRITE_FLAG) { + LOG(DEBUG) << "OVERWRITE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::VICTIM_FLAG) { + LOG(DEBUG) << "VICTIM_FLAG active for DP " << pid; + } + if (flags & DataPointValue::DIM_ERROR_FLAG) { + LOG(DEBUG) << "DIM_ERROR_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_DPID_FLAG) { + LOG(DEBUG) << "BAD_DPID_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_FLAGS_FLAG) { + LOG(DEBUG) << "BAD_FLAGS_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_TIMESTAMP_FLAG) { + LOG(DEBUG) << "BAD_TIMESTAMP_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_PAYLOAD_FLAG) { + LOG(DEBUG) << "BAD_PAYLOAD_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_FBI_FLAG) { + LOG(DEBUG) << "BAD_FBI_FLAG active for DP " << pid; + } + + return 0; +} + +//______________________________________________________________________ + +void TOFDCSProcessor::finalize() +{ + + // here we create the object to then be sent to CCDB + LOG(INFO) << "Finalizing"; + union Converter { + uint64_t raw_data; + double double_value; + } converter0, converter1; + + for (const auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::RAW_DOUBLE) { + auto& tofdcs = mTOFDCS[it.first]; + if (it.second == true) { // we processed the DP at least 1x + auto& dpvect = mDpsdoublesmap[it.first]; + tofdcs.firstValue.first = dpvect[0].get_epoch_time(); + converter0.raw_data = dpvect[0].payload_pt1; + tofdcs.firstValue.second = converter0.double_value; + tofdcs.lastValue.first = dpvect.back().get_epoch_time(); + converter0.raw_data = dpvect.back().payload_pt1; + tofdcs.lastValue.second = converter0.double_value; + // now I will look for the max change + if (dpvect.size() > 1) { + auto deltatime = dpvect.back().get_epoch_time() - dpvect[0].get_epoch_time(); + if (deltatime < 60000) { + // if we did not cover at least 1 minute, + // max variation is defined as the difference between first and last value + converter0.raw_data = dpvect[0].payload_pt1; + converter1.raw_data = dpvect.back().payload_pt1; + double delta = std::abs(converter0.double_value - converter1.double_value); + tofdcs.maxChange.first = deltatime; // is it ok to do like this, as in Run 2? + tofdcs.maxChange.second = delta; + } else { + for (auto i = 0; i < dpvect.size() - 1; ++i) { + for (auto j = i + 1; j < dpvect.size(); ++j) { + auto deltatime = dpvect[j].get_epoch_time() - dpvect[i].get_epoch_time(); + if (deltatime >= 60000) { // we check every min; epoch_time in ms + converter0.raw_data = dpvect[i].payload_pt1; + converter1.raw_data = dpvect[j].payload_pt1; + double delta = std::abs(converter0.double_value - converter1.double_value); + if (delta > tofdcs.maxChange.second) { + tofdcs.maxChange.first = deltatime; // is it ok to do like this, as in Run 2? + tofdcs.maxChange.second = delta; + } + } + } + } + } + // mid point + auto midIdx = dpvect.size() / 2 - 1; + tofdcs.midValue.first = dpvect[midIdx].get_epoch_time(); + converter0.raw_data = dpvect[midIdx].payload_pt1; + tofdcs.midValue.second = converter0.double_value; + } else { + tofdcs.maxChange.first = dpvect[0].get_epoch_time(); + converter0.raw_data = dpvect[0].payload_pt1; + tofdcs.maxChange.second = converter0.double_value; + tofdcs.midValue.first = dpvect[0].get_epoch_time(); + converter0.raw_data = dpvect[0].payload_pt1; + tofdcs.midValue.second = converter0.double_value; + } + } + if (mVerbose) { + LOG(INFO) << "PID = " << it.first.get_alias(); + tofdcs.print(); + } + } + } + std::map<std::string, std::string> md; + prepareCCDBobjectInfo(mTOFDCS, mccdbDPsInfo, "TOF/DCSDPs", mStartTF, md); + + return; +} + +//______________________________________________________________________ + +void TOFDCSProcessor::updateFEACCCDB() +{ + + // we need to update a CCDB for the FEAC status --> let's prepare the CCDBInfo + + if (mVerbose) { + LOG(INFO) << "At least one FEAC changed status --> we will update CCDB"; + } + std::map<std::string, std::string> md; + prepareCCDBobjectInfo(mFeac, mccdbLVInfo, "TOF/LVStatus", mTF, md); + return; +} + +//______________________________________________________________________ + +void TOFDCSProcessor::updateHVCCDB() +{ + + // we need to update a CCDB for the HV status --> let's prepare the CCDBInfo + + if (mVerbose) { + LOG(INFO) << "At least one HV changed status --> we will update CCDB"; + } + std::map<std::string, std::string> md; + prepareCCDBobjectInfo(mHV, mccdbHVInfo, "TOF/HVStatus", mTF, md); + return; +} + +//_______________________________________________________________________ + +void TOFDCSProcessor::getStripsConnectedToFEAC(int nDDL, int nFEAC, TOFFEACinfo& info) const +{ + + // + // Taken from AliRoot/TOF/AliTOFLvHvDataPoints.cxx + // + // FEAC-strip mapping: + // return the strips and first PadX numbers + // connected to the FEAC number nFEAC in the crate number nDDL + // + + switch (nDDL % 4) { + case 0: + info.firstPadX = 0; + info.lastPadX = Geo::NPADX / 2 - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 2; + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 2; + } + } + + break; + case 1: + info.firstPadX = Geo::NPADX / 2; + info.lastPadX = Geo::NPADX - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 2; + } + } + + break; + case 2: + info.firstPadX = Geo::NPADX / 2; + info.lastPadX = Geo::NPADX - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 2); + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 2); + } + } + + break; + case 3: + info.firstPadX = 0; + info.lastPadX = Geo::NPADX / 2 - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 2); + } + } + + break; + } +} diff --git a/Detectors/TOF/calibration/testWorkflow/DataGeneratorSpec.h b/Detectors/TOF/calibration/testWorkflow/DataGeneratorSpec.h index c4e5ae2047eb6..34b56e2ab5d01 100644 --- a/Detectors/TOF/calibration/testWorkflow/DataGeneratorSpec.h +++ b/Detectors/TOF/calibration/testWorkflow/DataGeneratorSpec.h @@ -21,6 +21,7 @@ #include "Framework/ControlService.h" #include "Framework/WorkflowSpec.h" #include "Framework/Task.h" +#include "Framework/Logger.h" #include "DataFormatsTOF/CalibInfoTOF.h" #include "TOFBase/Geo.h" #include "CommonConstants/MathConstants.h" @@ -122,7 +123,7 @@ DataProcessorSpec getTFDispatcherSpec() return DataProcessorSpec{ "calib-tf-dispatcher", Inputs{}, - Outputs{{{"output"}, "DUM", "DATASIZE"}}, + Outputs{{{"output"}, "TOF", "DATASIZE"}}, AlgorithmSpec{adaptFromTask<o2::calibration::TFDispatcher>()}, Options{{"max-timeframes", VariantType::Int64, 99999999999ll, {"max TimeFrames to generate"}}}}; } @@ -131,8 +132,8 @@ DataProcessorSpec getTFProcessorSpec() { return DataProcessorSpec{ "calib-tf-data-processor", - Inputs{{"input", "DUM", "DATASIZE"}}, - Outputs{{{"output"}, "DUM", "CALIBDATA"}}, + Inputs{{"input", "TOF", "DATASIZE"}}, + Outputs{{{"output"}, "TOF", "CALIBDATA"}}, AlgorithmSpec{adaptFromTask<o2::calibration::TFProcessor>()}, Options{ {"mean-latency", VariantType::Int, 1000, {"mean latency of the generator in microseconds"}}, diff --git a/Detectors/TOF/calibration/testWorkflow/LHCClockCalibratorSpec.h b/Detectors/TOF/calibration/testWorkflow/LHCClockCalibratorSpec.h index 45cbd80bf8d91..9999aad1d81c2 100644 --- a/Detectors/TOF/calibration/testWorkflow/LHCClockCalibratorSpec.h +++ b/Detectors/TOF/calibration/testWorkflow/LHCClockCalibratorSpec.h @@ -107,7 +107,7 @@ DataProcessorSpec getLHCClockCalibDeviceSpec() outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); return DataProcessorSpec{ "calib-lhcclock-calibration", - Inputs{{"input", "DUM", "CALIBDATA"}}, + Inputs{{"input", "TOF", "CALIBDATA"}}, outputs, AlgorithmSpec{adaptFromTask<device>()}, Options{ diff --git a/Detectors/TOF/calibration/testWorkflow/README.md b/Detectors/TOF/calibration/testWorkflow/README.md new file mode 100644 index 0000000000000..3b902da31a6da --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/README.md @@ -0,0 +1,94 @@ +<!-- doxy +\page refDetectorsTOFtestWorkflow testWorkflow +/doxy --> + +# TOF calibration workflows + +## DCS DP processing: + +Local example workflow with local CCDB (running on port 8080) : + +This will read the list of DPs to be associated to TOF from CCDB (remove +`--use-ccdb-to-configure` if you don't want this, but use hardcoded +aliases. You can specify the path of CCDB also with `--ccdb-path`. +YOu can also specify to run in verbose mode (`--use-verbose-mode`) + +```shell +o2-dcs-sim-workflow --max-timeframes 3 --delta-fraction 0.5 -b | +o2-calibration-tof-dcs-workflow --use-ccdb-to-configure -b | +o2-calibration-ccdb-populator-workflow --ccdb-path="http://localhost:8080" -b +``` +To populate locally a DCS entry for the configuration, run the: + +`O2/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.C` + +macro. + + +## LHC phase: + +This will process the LHC phase simulated by the Generator workflow + +```shell +LHC phase +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 | +o2-calibration-lhc-clockphase-workflow --tf-per-slot 20 | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 +``` + +## TOF channel calibration: + +To obtain the TOF channel offsets (at end of processing). Input from simulation, but is should work if attached to reco+calib flow + +* simulating reading from ccdb, and using it, with "-b", in "test" mode --> to use this, we need an appropriate CCDB object in the CCDB + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-testworkflows-tof-dummy-ccdb -b | +o2-calibration-tof-channel-calib-workflow --min-entries 50 --do-TOF-channel-calib-in-test-mode --use-ccdb -b | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` + +* simulating reading from ccdb, but not using it, with "-b", in "test" mode --> to use this, we need an appropriate CCDB object in the CCDB + +``` +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-testworkflows-tof-dummy-ccdb -b +| o2-calibration-tof-channel-calib-workflow --min-entries 50 --do-TOF-channel-calib-in-test-mode -b +| o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` + +* Using the workflow that has both LHCclockPhase and TOFChannelCalib; for now I can enable only one, or the CCDB populator will not work + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-tof-dummy-ccdb-for-calib -b | +o2-calibration-tof-calib-workflow --do-channel-offset --min-entries 50 --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` +* same as above, enabling CCDB + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-tof-dummy-ccdb-for-calib -b | +o2-calibration-tof-calib-workflow --do-channel-offset --use-ccdb --min-entries 50 --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` + +## TimeSlewing: + +For Time Slewing. Will save the Time Slewing information in files when a certain condition is reached. A post-processing +should then take care of extracting the CCDB + +* test mode: + +``` shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 10 -b | +o2-calibration-tof-collect-calib-workflow --tf-sending-policy --running-in-test-mode -b + +* non-test but simplified (using option "is-max-number-hits-to-fill-tree-absolute"): + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 10 -b | +o2-calibration-tof-collect-calib-workflow --max-number-hits-to-fill-tree 300 --is-max-number-hits-to-fill-tree-absolute -b +``` \ No newline at end of file diff --git a/Detectors/TOF/calibration/testWorkflow/TOFCalibCollectorSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFCalibCollectorSpec.h new file mode 100644 index 0000000000000..9f9df0953a107 --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/TOFCalibCollectorSpec.h @@ -0,0 +1,125 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TOFCALIB_COLLECTOR_H +#define O2_CALIBRATION_TOFCALIB_COLLECTOR_H + +/// @file TOFCalibCollectorSpec.h +/// @brief Device to collect information for TOF time slewing calibration. + +#include "TOFCalibration/TOFCalibCollector.h" +#include "DetectorsCalibration/Utils.h" +#include "DataFormatsTOF/CalibInfoTOF.h" +#include "Framework/Task.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace calibration +{ + +class TOFCalibCollectorDevice : public o2::framework::Task +{ + + public: + void init(o2::framework::InitContext& ic) final + { + + bool isTFsendingPolicy = ic.options().get<bool>("tf-sending-policy"); + int maxEnt = ic.options().get<int>("max-number-hits-to-fill-tree"); + bool isTest = ic.options().get<bool>("running-in-test-mode"); + bool absMaxEnt = ic.options().get<bool>("is-max-number-hits-to-fill-tree-absolute"); + mCollector = std::make_unique<o2::tof::TOFCalibCollector>(isTFsendingPolicy, maxEnt); + mCollector->setIsTest(isTest); + mCollector->setIsMaxNumberOfHitsAbsolute(absMaxEnt); + mCollector->setSlotLength(1); + mCollector->setMaxSlotsDelay(0); + } + + void run(o2::framework::ProcessingContext& pc) final + { + + auto tfcounter = o2::header::get<o2::framework::DataProcessingHeader*>(pc.inputs().get("input").header)->startTime; // is this the timestamp of the current TF? + auto data = pc.inputs().get<gsl::span<o2::dataformats::CalibInfoTOF>>("input"); + LOG(INFO) << "Processing TF " << tfcounter << " with " << data.size() << " tracks"; + mCollector->process(tfcounter, data); + sendOutput(pc.outputs()); + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + constexpr uint64_t INFINITE_TF = 0xffffffffffffffff; + mCollector->checkSlotsToFinalize(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<o2::tof::TOFCalibCollector> mCollector; + 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::gDataOriginTOF, "COLLECTEDINFO", 0, Lifetime::Timeframe}, collectedInfo); + output.snapshot(Output{o2::header::gDataOriginTOF, "ENTRIESCH", 0, Lifetime::Timeframe}, entriesPerChannel); + mCollector->initOutput(); // reset the output for the next round + } + } +}; + +} // namespace calibration + +namespace framework +{ + +DataProcessorSpec getTOFCalibCollectorDeviceSpec() +{ + using device = o2::calibration::TOFCalibCollectorDevice; + using clbUtils = o2::calibration::Utils; + + std::vector<OutputSpec> outputs; + outputs.emplace_back(o2::header::gDataOriginTOF, "COLLECTEDINFO", 0, Lifetime::Timeframe); + // or should I use the ConcreteDataTypeMatcher? e.g.: outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); + outputs.emplace_back(o2::header::gDataOriginTOF, "ENTRIESCH", 0, Lifetime::Timeframe); + + std::vector<InputSpec> inputs; + inputs.emplace_back("input", "TOF", "CALIBDATA"); + + return DataProcessorSpec{ + "calib-tofcalib-collector", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<device>()}, + Options{ + {"max-number-hits-to-fill-tree", VariantType::Int, 500, {"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"}}, + {"running-in-test-mode", VariantType::Bool, false, {"to run in test mode for simplification"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TOF/calibration/testWorkflow/TOFCalibCollectorWriterSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFCalibCollectorWriterSpec.h new file mode 100644 index 0000000000000..fc126b0fc1670 --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/TOFCalibCollectorWriterSpec.h @@ -0,0 +1,128 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TOFCALIB_COLLECTOR_WRITER_H +#define O2_CALIBRATION_TOFCALIB_COLLECTOR_WRITER_H + +/// @file TOFCalibCollectorWriterSpec.h +/// @brief Device to write to tree the information for TOF time slewing calibration. + +#include "TOFCalibration/TOFCalibCollector.h" +#include "DataFormatsTOF/CalibInfoTOFshort.h" +#include <TTree.h> +#include <gsl/span> + +using namespace o2::framework; + +namespace o2 +{ +namespace calibration +{ + +class TOFCalibCollectorWriter : public o2::framework::Task +{ + + using Geo = o2::tof::Geo; + + public: + void createAndOpenFileAndTree() + { + TString filename = TString::Format("collTOF_%d.root", mCount); + LOG(DEBUG) << "opening file " << filename.Data(); + mfileOut.reset(TFile::Open(TString::Format("%s", filename.Data()), "RECREATE")); + mOutputTree = std::make_unique<TTree>("treeCollectedCalibInfo", "Tree with TOF calib info for Time Slewing"); + mOutputTree->Branch(mOutputBranchName.data(), &mPTOFCalibInfoOut); + } + + void init(o2::framework::InitContext& ic) final + { + mCount = 0; + createAndOpenFileAndTree(); + mTOFCalibInfoOut.reserve(500 * o2::tof::Geo::NCHANNELS); // should be a reasonable number of entries per channel, which allows 1 GB of data + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto collectedInfo = pc.inputs().get<gsl::span<o2::dataformats::CalibInfoTOFshort>>("collectedInfo"); + auto entriesPerChannel = pc.inputs().get<gsl::span<int>>("entriesCh"); + int offsetStart = 0; + for (int ich = 0; ich < o2::tof::Geo::NCHANNELS; ich++) { + mTOFCalibInfoOut.clear(); + if (entriesPerChannel[ich] > 0) { + mTOFCalibInfoOut.resize(entriesPerChannel[ich]); + auto subSpanVect = collectedInfo.subspan(offsetStart, entriesPerChannel[ich]); + memcpy(&mTOFCalibInfoOut[0], subSpanVect.data(), sizeof(o2::dataformats::CalibInfoTOFshort) * subSpanVect.size()); + const o2::dataformats::CalibInfoTOFshort* tmp = subSpanVect.data(); + } + 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<o2::dataformats::CalibInfoTOFshort> mTOFCalibInfoOut, *mPTOFCalibInfoOut = &mTOFCalibInfoOut; ///< these are the object and pointer to the CalibInfo of a specific channel that we need to fill the output tree + std::unique_ptr<TTree> mOutputTree; ///< tree for the collected calib tof info + std::string mTOFCalibInfoBranchName = "TOFCalibInfo"; ///< name of branch containing input TOF calib infos + std::string mOutputBranchName = "TOFCollectedCalibInfo"; ///< name of branch containing output + std::unique_ptr<TFile> 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(); + mfileOut.reset(); + mCount++; + if (!mIsEndOfStream) { + createAndOpenFileAndTree(); + } + } +}; +} // namespace calibration + +namespace framework +{ + +DataProcessorSpec getTOFCalibCollectorWriterSpec() +{ + using device = o2::calibration::TOFCalibCollectorWriter; + std::vector<InputSpec> inputs; + inputs.emplace_back("collectedInfo", o2::header::gDataOriginTOF, "COLLECTEDINFO"); + inputs.emplace_back("entriesCh", o2::header::gDataOriginTOF, "ENTRIESCH"); + + std::vector<OutputSpec> outputs; // empty + + return DataProcessorSpec{ + "calib-tofcalib-collector-writer", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<device>()}, + Options{}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TOF/calibration/testWorkflow/TOFChannelCalibratorSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFChannelCalibratorSpec.h index feebb45599c6f..12847abd5c364 100644 --- a/Detectors/TOF/calibration/testWorkflow/TOFChannelCalibratorSpec.h +++ b/Detectors/TOF/calibration/testWorkflow/TOFChannelCalibratorSpec.h @@ -43,13 +43,13 @@ class TOFChannelCalibDevice : public o2::framework::Task void init(o2::framework::InitContext& ic) final { - int minEnt = std::max(50, ic.options().get<int>("min-entries")); + int minEnt = ic.options().get<int>("min-entries"); //std::max(50, ic.options().get<int>("min-entries")); int nb = std::max(500, ic.options().get<int>("nbins")); float range = ic.options().get<float>("range"); int isTest = ic.options().get<bool>("do-TOF-channel-calib-in-test-mode"); mCalibrator = std::make_unique<o2::tof::TOFChannelCalibrator>(minEnt, nb, range); mCalibrator->setUpdateAtTheEndOfRunOnly(); - mCalibrator->isTest(isTest); + mCalibrator->setIsTest(isTest); // calibration objects set to zero mPhase.addLHCphase(0, 0); @@ -186,7 +186,7 @@ DataProcessorSpec getTOFChannelCalibDeviceSpec(bool useCCDB, bool attachChannelO outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); std::vector<InputSpec> inputs; - inputs.emplace_back("input", "DUM", "CALIBDATA"); + inputs.emplace_back("input", "TOF", "CALIBDATA"); if (useCCDB) { inputs.emplace_back("tofccdbLHCphase", o2::header::gDataOriginTOF, "LHCphase"); inputs.emplace_back("tofccdbChannelCalib", o2::header::gDataOriginTOF, "ChannelCalib"); diff --git a/Detectors/TOF/calibration/testWorkflow/TOFDCSDataProcessorSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFDCSDataProcessorSpec.h new file mode 100644 index 0000000000000..bf31e0397b40c --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/TOFDCSDataProcessorSpec.h @@ -0,0 +1,176 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TOF_DATAPROCESSOR_H +#define O2_TOF_DATAPROCESSOR_H + +/// @file DCSTOFDataProcessorSpec.h +/// @brief TOF Processor for DCS Data Points + +#include <unistd.h> +#include <TRandom.h> +#include <TStopwatch.h> +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/AliasExpander.h" +#include "TOFCalibration/TOFDCSProcessor.h" +#include "DetectorsCalibration/Utils.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; +using namespace o2::ccdb; +using CcdbManager = o2::ccdb::BasicCCDBManager; +using clbUtils = o2::calibration::Utils; + +class TOFDCSDataProcessor : public o2::framework::Task +{ + public: + void init(o2::framework::InitContext& ic) final + { + + std::vector<DPID> vect; + bool useCCDBtoConfigure = ic.options().get<bool>("use-ccdb-to-configure"); + if (useCCDBtoConfigure) { + LOG(INFO) << "Configuring via CCDB"; + std::string ccdbpath = ic.options().get<std::string>("ccdb-path"); + auto& mgr = CcdbManager::instance(); + mgr.setURL(ccdbpath); + CcdbApi api; + api.init(mgr.getURL()); + long ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + std::unordered_map<DPID, std::string>* dpid2DataDesc = mgr.getForTimeStamp<std::unordered_map<DPID, std::string>>("TOF/DCSconfig", ts); + for (auto& i : *dpid2DataDesc) { + vect.push_back(i.first); + } + } else { + LOG(INFO) << "Configuring via hardcoded strings"; + std::vector<std::string> aliases = {"tof_hv_vp_[00..89]", "tof_hv_vn_[00..89]", "tof_hv_ip_[00..89]", "tof_hv_in_[00..89]"}; + std::vector<std::string> aliasesInt = {"TOF_FEACSTATUS_[00..71]"}; + std::vector<std::string> expaliases = o2::dcs::expandAliases(aliases); + std::vector<std::string> expaliasesInt = o2::dcs::expandAliases(aliasesInt); + for (const auto& i : expaliases) { + vect.emplace_back(i, o2::dcs::RAW_DOUBLE); + } + for (const auto& i : expaliasesInt) { + vect.emplace_back(i, o2::dcs::RAW_INT); + } + } + + LOG(INFO) << "Listing Data Points for TOF:"; + for (auto& i : vect) { + LOG(INFO) << i; + } + + mProcessor = std::make_unique<o2::tof::TOFDCSProcessor>(); + bool useVerboseMode = ic.options().get<bool>("use-verbose-mode"); + LOG(INFO) << " ************************* Verbose?" << useVerboseMode; + if (useVerboseMode) { + mProcessor->useVerboseMode(); + } + mProcessor->init(vect); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto tfid = o2::header::get<o2::framework::DataProcessingHeader*>(pc.inputs().get("input").header)->startTime; + auto dps = pc.inputs().get<gsl::span<DPCOM>>("input"); + mProcessor->setTF(tfid); + mProcessor->process(dps); + sendOutput(pc.outputs()); + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + mProcessor->finalize(); + sendOutput(ec.outputs()); + const auto& payload = mProcessor->getTOFDPsInfo(); + auto& info = mProcessor->getccdbDPsInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + ec.outputs().snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + ec.outputs().snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + + private: + std::unique_ptr<TOFDCSProcessor> mProcessor; + + //________________________________________________________________ + void sendOutput(DataAllocator& output) + { + // extract CCDB infos and calibration objects, convert it to TMemFile and send them to the output + + if (mProcessor->isLVUpdated()) { + const auto& payload = mProcessor->getLVStatus(); + auto& info = mProcessor->getccdbLVInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + if (mProcessor->isHVUpdated()) { + const auto& payload = mProcessor->getHVStatus(); + auto& info = mProcessor->getccdbHVInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + } + +}; // end class +} // namespace tof + +namespace framework +{ + +DataProcessorSpec getTOFDCSDataProcessorSpec() +{ + + using clbUtils = o2::calibration::Utils; + + std::vector<OutputSpec> outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload}); + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); + + return DataProcessorSpec{ + "tof-dcs-data-processor", + Inputs{{"input", "DCS", "TOFDATAPOINTS"}}, + outputs, + AlgorithmSpec{adaptFromTask<o2::tof::TOFDCSDataProcessor>()}, + Options{{"ccdb-path", VariantType::String, "http://localhost:8080", {"Path to CCDB"}}, + {"use-ccdb-to-configure", VariantType::Bool, false, {"Use CCDB to configure"}}, + {"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TOF/calibration/testWorkflow/tof-calib-workflow.cxx b/Detectors/TOF/calibration/testWorkflow/tof-calib-workflow.cxx index f5e9a67fefc35..4d14607ff69cd 100644 --- a/Detectors/TOF/calibration/testWorkflow/tof-calib-workflow.cxx +++ b/Detectors/TOF/calibration/testWorkflow/tof-calib-workflow.cxx @@ -45,9 +45,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOG(INFO) << "doChannelOffsetCalib = " << doChannelOffsetCalib; LOG(INFO) << "useCCDB = " << useCCDB; LOG(INFO) << "attachChannelOffsetToLHCphase = " << attachChannelOffsetToLHCphase; - if (doLHCcalib) + if (doLHCcalib) { specs.emplace_back(getLHCClockCalibDeviceSpec()); - if (doChannelOffsetCalib) + } + if (doChannelOffsetCalib) { specs.emplace_back(getTOFChannelCalibDeviceSpec(useCCDB, attachChannelOffsetToLHCphase)); + } return specs; } diff --git a/Detectors/TOF/calibration/testWorkflow/tof-collect-calib-workflow.cxx b/Detectors/TOF/calibration/testWorkflow/tof-collect-calib-workflow.cxx new file mode 100644 index 0000000000000..f31c9ce7984ae --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/tof-collect-calib-workflow.cxx @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TOFCalibCollectorSpec.h" +#include "TOFCalibCollectorWriterSpec.h" +#include "Framework/DataProcessorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getTOFCalibCollectorDeviceSpec()); + specs.emplace_back(getTOFCalibCollectorWriterSpec()); + + return specs; +} diff --git a/Detectors/TOF/calibration/testWorkflow/tof-dcs-data-workflow.cxx b/Detectors/TOF/calibration/testWorkflow/tof-dcs-data-workflow.cxx new file mode 100644 index 0000000000000..29826c1b66505 --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/tof-dcs-data-workflow.cxx @@ -0,0 +1,41 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "Framework/TypeTraits.h" +#include <unordered_map> +namespace o2::framework +{ +template <> +struct has_root_dictionary<std::unordered_map<o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue>, void> : std::true_type { +}; +} // namespace o2::framework +#include "Framework/DataProcessorSpec.h" +#include "TOFDCSDataProcessorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getTOFDCSDataProcessorSpec()); + return specs; +} diff --git a/Detectors/TOF/calibration/testWorkflow/tof-dummy-ccdb-for-calib.cxx b/Detectors/TOF/calibration/testWorkflow/tof-dummy-ccdb-for-calib.cxx index 3bb4c2df855fb..1ad9eb8662b1e 100644 --- a/Detectors/TOF/calibration/testWorkflow/tof-dummy-ccdb-for-calib.cxx +++ b/Detectors/TOF/calibration/testWorkflow/tof-dummy-ccdb-for-calib.cxx @@ -22,7 +22,7 @@ std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext const&) return { DataProcessorSpec{ "simple", - Inputs{{"input", "DUM", "CALIBDATA"}}, + Inputs{{"input", "TOF", "CALIBDATA"}}, Outputs{OutputSpec{{"phase"}, "TOF", "LHCphase"}, OutputSpec{{"timeSlewing"}, "TOF", "ChannelCalib"}, OutputSpec{{"startLHCphase"}, "TOF", "StartLHCphase"}, diff --git a/Detectors/TOF/compression/CMakeLists.txt b/Detectors/TOF/compression/CMakeLists.txt index 9dcbfc25f2c94..adef2471c81e4 100644 --- a/Detectors/TOF/compression/CMakeLists.txt +++ b/Detectors/TOF/compression/CMakeLists.txt @@ -27,4 +27,10 @@ o2_add_executable(compressed-inspector PUBLIC_LINK_LIBRARIES O2::TOFWorkflowUtils ) +o2_add_executable(compressed-analysis + COMPONENT_NAME tof + SOURCES src/tof-compressed-analysis.cxx + PUBLIC_LINK_LIBRARIES O2::TOFWorkflowUtils + ) + diff --git a/Detectors/TOF/compression/include/TOFCompression/Compressor.h b/Detectors/TOF/compression/include/TOFCompression/Compressor.h index cc86d4f12a89f..7d958bb863d05 100644 --- a/Detectors/TOF/compression/include/TOFCompression/Compressor.h +++ b/Detectors/TOF/compression/include/TOFCompression/Compressor.h @@ -33,18 +33,29 @@ class Compressor { public: - Compressor() = default; - ~Compressor() = default; + Compressor() { mDecoderSaveBuffer = new char[mDecoderSaveBufferSize]; }; + ~Compressor() { delete[] mDecoderSaveBuffer; }; inline bool run() { rewind(); if (mDecoderCONET) { mDecoderPointerMax = reinterpret_cast<const uint32_t*>(mDecoderBuffer + mDecoderBufferSize); - return processDRM(); + while (mDecoderPointer < mDecoderPointerMax) { + mEventCounter++; + processDRM(); + if (mDecoderFatal) { + mFatalCounter++; + } + if (mDecoderError) { + mErrorCounter++; + } + } + return false; } - while (!processHBF()) + while (!processHBF()) { ; + } return false; }; @@ -113,7 +124,8 @@ class Compressor bool mDecoderVerbose = false; bool mDecoderError = false; bool mDecoderFatal = false; - char mDecoderSaveBuffer[1048576]; + char* mDecoderSaveBuffer = nullptr; + const int mDecoderSaveBufferSize = 33554432; uint32_t mDecoderSaveBufferDataSize = 0; uint32_t mDecoderSaveBufferDataLeft = 0; @@ -191,7 +203,9 @@ class Compressor uint8_t trmErrors[10][2]; bool hasHits[10][2]; bool hasErrors[10][2]; - bool decodeError; + bool drmDecodeError; + bool ltmDecodeError; + bool trmDecodeError[10]; } mDecoderSummary = {nullptr}; struct SpiderSummary_t { diff --git a/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h b/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h index 84245091dd6ad..0bd9adb6b0e0b 100644 --- a/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h +++ b/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h @@ -32,13 +32,16 @@ template <typename RDH, bool verbose> class CompressorTask : public Task { public: - CompressorTask() = default; - ~CompressorTask() override = default; + CompressorTask() { mBufferOut = new char[mBufferOutSize]; }; + ~CompressorTask() override { delete[] mBufferOut; }; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; private: Compressor<RDH, verbose> mCompressor; + + char* mBufferOut = nullptr; + const int mBufferOutSize = 33554432; }; } // namespace tof diff --git a/Detectors/TOF/compression/src/Compressor.cxx b/Detectors/TOF/compression/src/Compressor.cxx index 4408867c98972..1b31d746f36ed 100644 --- a/Detectors/TOF/compression/src/Compressor.cxx +++ b/Detectors/TOF/compression/src/Compressor.cxx @@ -135,8 +135,9 @@ bool Compressor<RDH, verbose>::processHBF() rdh = reinterpret_cast<const RDH*>(reinterpret_cast<const char*>(rdh) + offsetToNext); /** check next RDH is within buffer **/ - if (reinterpret_cast<const char*>(rdh) < mDecoderBuffer + mDecoderBufferSize) + if (reinterpret_cast<const char*>(rdh) < mDecoderBuffer + mDecoderBufferSize) { continue; + } /** otherwise return **/ return true; @@ -172,8 +173,9 @@ bool Compressor<RDH, verbose>::processHBF() mEncoderPointer = mEncoderPointerStart; } - if (mDecoderError) + if (mDecoderError) { mErrorCounter++; + } /** updated encoder RDH open **/ mEncoderRDH->memorySize = reinterpret_cast<char*>(mEncoderPointer) - reinterpret_cast<char*>(mEncoderRDH); @@ -198,8 +200,9 @@ bool Compressor<RDH, verbose>::processHBF() mDecoderPointer = reinterpret_cast<const uint32_t*>(reinterpret_cast<const char*>(rdh) + rdh->offsetToNext); /** check next RDH is within buffer **/ - if (reinterpret_cast<const char*>(mDecoderPointer) < mDecoderBuffer + mDecoderBufferSize) + if (reinterpret_cast<const char*>(mDecoderPointer) < mDecoderBuffer + mDecoderBufferSize) { return false; + } /** otherwise return **/ return true; @@ -369,14 +372,16 @@ bool Compressor<RDH, verbose>::processDRM() /** LTM global header detected **/ if (IS_LTM_GLOBAL_HEADER(*mDecoderPointer)) { - if (processLTM()) + if (processLTM()) { return true; + } } /** TRM Data Header detected **/ if (IS_TRM_GLOBAL_HEADER(*mDecoderPointer) && GET_TRMDATAHEADER_SLOTID(*mDecoderPointer) > 2) { - if (processTRM()) + if (processTRM()) { return true; + } continue; } @@ -476,14 +481,16 @@ bool Compressor<RDH, verbose>::processDRM() /** decode error **/ mDecoderError = true; + mDecoderSummary.drmDecodeError = true; if (verbose && mDecoderVerbose) { printf("%s %08x [ERROR] trying to recover DRM decode stream %s \n", colorRed, *mDecoderPointer, colorReset); } /** decode error detected, be paranoid **/ - if (decoderParanoid()) + if (decoderParanoid()) { return true; + } decoderNext(); @@ -570,14 +577,16 @@ bool Compressor<RDH, verbose>::processTRM() /** TRM Chain-A Header detected **/ if (IS_TRM_CHAINA_HEADER(*mDecoderPointer) && GET_TRMCHAINHEADER_SLOTID(*mDecoderPointer) == slotId) { - if (processTRMchain(itrm, 0)) + if (processTRMchain(itrm, 0)) { return true; + } } /** TRM Chain-B Header detected **/ if (IS_TRM_CHAINB_HEADER(*mDecoderPointer) && GET_TRMCHAINHEADER_SLOTID(*mDecoderPointer) == slotId) { - if (processTRMchain(itrm, 1)) + if (processTRMchain(itrm, 1)) { return true; + } } /** TRM Data Trailer detected **/ @@ -608,8 +617,9 @@ bool Compressor<RDH, verbose>::processTRM() } /** encoder Spider **/ - if (mDecoderSummary.hasHits[itrm][0] || mDecoderSummary.hasHits[itrm][1]) + if (mDecoderSummary.hasHits[itrm][0] || mDecoderSummary.hasHits[itrm][1]) { encoderSpider(itrm); + } /** success **/ return false; @@ -617,12 +627,14 @@ bool Compressor<RDH, verbose>::processTRM() /** decode error **/ mDecoderError = true; + mDecoderSummary.trmDecodeError[itrm] = true; if (verbose && mDecoderVerbose) { printf("%s %08x [ERROR] breaking TRM decode stream %s \n", colorRed, *mDecoderPointer, colorReset); } /** decode error detected, be paranoid **/ - if (decoderParanoid()) + if (decoderParanoid()) { return true; + } decoderNext(); return false; @@ -714,12 +726,14 @@ bool Compressor<RDH, verbose>::processTRMchain(int itrm, int ichain) /** decode error **/ mDecoderError = true; + mDecoderSummary.trmDecodeError[itrm] = true; if (verbose && mDecoderVerbose) { printf("%s %08x [ERROR] breaking TRM Chain-%c decode stream %s \n", colorRed, *mDecoderPointer, ichain == 0 ? 'A' : 'B', colorReset); } /** decode error detected, be paranoid **/ - if (decoderParanoid()) + if (decoderParanoid()) { return true; + } decoderNext(); break; @@ -757,22 +771,25 @@ void Compressor<RDH, verbose>::encoderSpider(int itrm) /** loop over TRM chains **/ for (int ichain = 0; ichain < 2; ++ichain) { - if (!mDecoderSummary.hasHits[itrm][ichain]) + if (!mDecoderSummary.hasHits[itrm][ichain]) { continue; + } /** loop over TDCs **/ for (int itdc = 0; itdc < 15; ++itdc) { auto nhits = mDecoderSummary.trmDataHits[ichain][itdc]; - if (nhits == 0) + if (nhits == 0) { continue; + } /** loop over hits **/ for (int ihit = 0; ihit < nhits; ++ihit) { auto lhit = *mDecoderSummary.trmDataHit[ichain][itdc][ihit]; - if (!IS_TDC_HIT_LEADING(lhit)) // must be a leading hit + if (!IS_TDC_HIT_LEADING(lhit)) { // must be a leading hit continue; + } auto chan = GET_TRMDATAHIT_CHANID(lhit); auto hitTime = GET_TRMDATAHIT_TIME(lhit); @@ -800,10 +817,12 @@ void Compressor<RDH, verbose>::encoderSpider(int itrm) mSpiderSummary.FramePackedHit[iframe][phit] |= ichain << 31; mSpiderSummary.nFramePackedHits[iframe]++; - if (iframe < firstFilledFrame) + if (iframe < firstFilledFrame) { firstFilledFrame = iframe; - if (iframe > lastFilledFrame) + } + if (iframe > lastFilledFrame) { lastFilledFrame = iframe; + } } mDecoderSummary.trmDataHits[ichain][itdc] = 0; @@ -814,8 +833,9 @@ void Compressor<RDH, verbose>::encoderSpider(int itrm) for (int iframe = firstFilledFrame; iframe < lastFilledFrame + 1; iframe++) { /** check if frame is empty **/ - if (mSpiderSummary.nFramePackedHits[iframe] == 0) + if (mSpiderSummary.nFramePackedHits[iframe] == 0) { continue; + } // encode Frame Header *mEncoderPointer = 0x00000000; @@ -883,9 +903,28 @@ bool Compressor<RDH, verbose>::checkerCheck() } mDecoderSummary = {nullptr}; mCheckerSummary.nDiagnosticWords++; + for (int itrm = 0; itrm < 10; ++itrm) { + mDecoderSummary.trmDataHeader[itrm] = nullptr; + mDecoderSummary.trmDataTrailer[itrm] = nullptr; + for (int ichain = 0; ichain < 2; ++ichain) { + mDecoderSummary.trmChainHeader[itrm][ichain] = nullptr; + mDecoderSummary.trmChainTrailer[itrm][ichain] = nullptr; + mDecoderSummary.trmErrors[itrm][ichain] = 0; + mDecoderSummary.trmErrors[itrm][ichain] = 0; + } + } return true; } + /** check DRM decode error **/ + if (mDecoderSummary.drmDecodeError) { + mCheckerSummary.DiagnosticWord[0] |= diagnostic::DRM_DECODE_ERROR; + if (verbose && mCheckerVerbose) { + printf(" DRM decode error \n"); + } + mDecoderSummary.drmDecodeError = false; + } + /** check DRM Data Trailer **/ if (verbose && mCheckerVerbose) { printf(" --- Checking DRM Data Trailer: %p \n", mDecoderSummary.drmDataTrailer); @@ -897,6 +936,7 @@ bool Compressor<RDH, verbose>::checkerCheck() } mDecoderSummary = {nullptr}; mCheckerSummary.nDiagnosticWords++; + return true; } @@ -907,8 +947,9 @@ bool Compressor<RDH, verbose>::checkerCheck() uint32_t locEvCnt = GET_DRMDATATRAILER_LOCEVCNT(*mDecoderSummary.drmDataTrailer); /** check RDH **/ - if (!mDecoderCONET) + if (!mDecoderCONET) { checkerCheckRDH(); + } /** check enable/participating mask **/ if (verbose && mCheckerVerbose) { @@ -956,8 +997,9 @@ bool Compressor<RDH, verbose>::checkerCheck() /** check DRM event words (careful with pointers because we have 64 bits extra! only for CRU data! **/ auto drmEventWords = mDecoderSummary.drmDataTrailer - mDecoderSummary.drmDataHeader + 1; - if (!mDecoderCONET) + if (!mDecoderCONET) { drmEventWords -= (drmEventWords / 4) * 2; + } drmEventWords -= 6; if (verbose && mCheckerVerbose) { printf(" --- Checking DRM declared/detected event words: %u/%ld \n", GET_DRMDATAHEADER_EVENTWORDS(*mDecoderSummary.drmDataHeader), drmEventWords); @@ -1035,8 +1077,9 @@ bool Compressor<RDH, verbose>::checkerCheck() if (verbose && mCheckerVerbose) { printf(" Non-participating header found (slotId=%u) \n", slotId); } - } else + } else { continue; + } } /** check TRM bit in DRM fault mask **/ @@ -1053,9 +1096,20 @@ bool Compressor<RDH, verbose>::checkerCheck() if (verbose && mCheckerVerbose) { printf(" Missing TRM Data Header (slotId=%u) \n", slotId); } + mDecoderSummary.trmErrors[itrm][0] = 0; + mDecoderSummary.trmErrors[itrm][1] = 0; continue; } + /** check TRM decode error **/ + if (mDecoderSummary.trmDecodeError[itrm]) { + mCheckerSummary.DiagnosticWord[iword] |= diagnostic::TRM_DECODE_ERROR; + if (verbose && mCheckerVerbose) { + printf(" Decode error in TRM (slotId=%u) \n", slotId); + } + mDecoderSummary.trmDecodeError[itrm] = false; + } + /** check TRM Data Trailer **/ if (!mDecoderSummary.trmDataTrailer[itrm]) { mCheckerSummary.DiagnosticWord[iword] |= diagnostic::TRM_TRAILER_MISSING; @@ -1063,6 +1117,8 @@ bool Compressor<RDH, verbose>::checkerCheck() printf(" Missing TRM Trailer (slotId=%u) \n", slotId); } mDecoderSummary.trmDataHeader[itrm] = nullptr; + mDecoderSummary.trmErrors[itrm][0] = 0; + mDecoderSummary.trmErrors[itrm][1] = 0; continue; } @@ -1102,8 +1158,9 @@ bool Compressor<RDH, verbose>::checkerCheck() /** check TRM event words (careful with pointers because we have 64 bits extra! only for CRU data! **/ auto trmEventWords = mDecoderSummary.trmDataTrailer[itrm] - mDecoderSummary.trmDataHeader[itrm] + 1; - if (!mDecoderCONET) + if (!mDecoderCONET) { trmEventWords -= (trmEventWords / 4) * 2; + } if (verbose && mCheckerVerbose) { printf(" --- Checking TRM (slotId=%u) declared/detected event words: %d/%ld \n", slotId, GET_TRMDATAHEADER_EVENTWORDS(*mDecoderSummary.trmDataHeader[itrm]), trmEventWords); } @@ -1123,6 +1180,7 @@ bool Compressor<RDH, verbose>::checkerCheck() if (verbose && mCheckerVerbose) { printf(" Missing TRM Chain Header (slotId=%u, chain=%d) \n", slotId, ichain); } + mDecoderSummary.trmErrors[itrm][ichain] = 0; continue; } @@ -1133,6 +1191,7 @@ bool Compressor<RDH, verbose>::checkerCheck() printf(" Missing TRM Chain Trailer (slotId=%u, chain=%d) \n", slotId, ichain); } mDecoderSummary.trmChainHeader[itrm][ichain] = nullptr; + mDecoderSummary.trmErrors[itrm][ichain] = 0; continue; } @@ -1202,8 +1261,9 @@ bool Compressor<RDH, verbose>::checkerCheck() } /** end of loop over TRMs **/ /** check current diagnostic word **/ - if (mCheckerSummary.DiagnosticWord[iword] & 0xFFFFFFF0) + if (mCheckerSummary.DiagnosticWord[iword] & 0xFFFFFFF0) { mCheckerSummary.nDiagnosticWords++; + } if (verbose && mCheckerVerbose) { std::cout << colorBlue @@ -1348,8 +1408,9 @@ void Compressor<RDH, verbose>::checkSummary() #ifndef CHECKER_COUNTER return; #endif - if (mEventCounter == 0) + if (mEventCounter == 0) { return; + } printf("\n"); printf(" DRM "); float drmheaders = 100. * (float)mDRMCounters.Headers / (float)mEventCounter; diff --git a/Detectors/TOF/compression/src/CompressorTask.cxx b/Detectors/TOF/compression/src/CompressorTask.cxx index 8b9143f45f7a7..7e4d5854daec8 100644 --- a/Detectors/TOF/compression/src/CompressorTask.cxx +++ b/Detectors/TOF/compression/src/CompressorTask.cxx @@ -57,9 +57,8 @@ void CompressorTask<RDH, verbose>::run(ProcessingContext& pc) LOG(DEBUG) << "Compressor run"; /** set encoder output buffer **/ - char bufferOut[1048576]; - mCompressor.setEncoderBuffer(bufferOut); - mCompressor.setEncoderBufferSize(1048576); + mCompressor.setEncoderBuffer(mBufferOut); + mCompressor.setEncoderBufferSize(mBufferOutSize); auto device = pc.services().get<o2::framework::RawDeviceService>().device(); auto outputRoutes = pc.services().get<o2::framework::RawDeviceService>().spec().outputs; @@ -67,8 +66,9 @@ void CompressorTask<RDH, verbose>::run(ProcessingContext& pc) /** loop over inputs routes **/ for (auto iit = pc.inputs().begin(), iend = pc.inputs().end(); iit != iend; ++iit) { - if (!iit.isValid()) + if (!iit.isValid()) { continue; + } /** prepare output parts **/ FairMQParts parts; @@ -87,7 +87,7 @@ void CompressorTask<RDH, verbose>::run(ProcessingContext& pc) mCompressor.run(); auto payloadOutSize = mCompressor.getEncoderByteCounter(); auto payloadMessage = device->NewMessage(payloadOutSize); - std::memcpy(payloadMessage->GetData(), bufferOut, payloadOutSize); + std::memcpy(payloadMessage->GetData(), mBufferOut, payloadOutSize); /** output **/ auto headerOut = *headerIn; diff --git a/Detectors/TOF/compression/src/tof-compressed-analysis.cxx b/Detectors/TOF/compression/src/tof-compressed-analysis.cxx new file mode 100644 index 0000000000000..905dbe1f93e9b --- /dev/null +++ b/Detectors/TOF/compression/src/tof-compressed-analysis.cxx @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 tof-compressor.cxx +/// @author Roberto Preghenella +/// @since 2019-12-18 +/// @brief Basic DPL workflow for TOF raw data compression + +#include "TOFWorkflowUtils/CompressedAnalysisTask.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "FairLogger.h" + +using namespace o2::framework; + +// add workflow options, note that customization needs to be declared before +// including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + auto inputDesc = ConfigParamSpec{"tof-compressed-analysis-input-desc", VariantType::String, "CRAWDATA", {"Input specs description string"}}; + workflowOptions.push_back(inputDesc); +} + +#include "Framework/runDataProcessing.h" // the main driver + +/// This function hooks up the the workflow specifications into the DPL driver. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + auto inputDesc = cfgc.options().get<std::string>("tof-compressed-analysis-input-desc"); + + return WorkflowSpec{ + DataProcessorSpec{"compressed-analysis", + select(std::string("x:TOF/" + inputDesc).c_str()), + Outputs{}, + AlgorithmSpec(adaptFromTask<o2::tof::CompressedAnalysisTask>()), + Options{ + {"tof-compressed-analysis-conet-mode", VariantType::Bool, false, {"CONET mode"}}, + {"tof-compressed-analysis-filename", VariantType::String, "", {"Analysis file name"}}, + {"tof-compressed-analysis-function", VariantType::String, "", {"Analysis function call"}}}}}; +} diff --git a/Detectors/TOF/compression/src/tof-compressed-inspector.cxx b/Detectors/TOF/compression/src/tof-compressed-inspector.cxx index e039cd7e8d335..e32d078c8836e 100644 --- a/Detectors/TOF/compression/src/tof-compressed-inspector.cxx +++ b/Detectors/TOF/compression/src/tof-compressed-inspector.cxx @@ -13,7 +13,7 @@ /// @since 2019-12-18 /// @brief Basic DPL workflow for TOF raw data compression -#include "TOFWorkflow/CompressedInspectorTask.h" +#include "TOFWorkflowUtils/CompressedInspectorTask.h" #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamSpec.h" #include "FairLogger.h" @@ -39,10 +39,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto rdhVersion = cfgc.options().get<int>("tof-compressed-inspector-rdh-version"); AlgorithmSpec algoSpec; - if (rdhVersion == 4) + if (rdhVersion == 4) { algoSpec = AlgorithmSpec{adaptFromTask<o2::tof::CompressedInspectorTask<o2::header::RAWDataHeaderV4>>()}; - else if (rdhVersion == 6) + } else if (rdhVersion == 6) { algoSpec = AlgorithmSpec{adaptFromTask<o2::tof::CompressedInspectorTask<o2::header::RAWDataHeaderV6>>()}; + } WorkflowSpec workflow; workflow.emplace_back(DataProcessorSpec{ @@ -51,7 +52,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) Outputs{}, algoSpec, Options{ - {"tof-compressed-inspector-filename", VariantType::String, "inspector.root", {"Name of the inspector output file"}}}}); + {"tof-compressed-inspector-filename", VariantType::String, "inspector.root", {"Name of the inspector output file"}}, + {"tof-compressed-inspector-decoder-verbose", VariantType::Bool, false, {"Decode in verbose mode"}}}}); return workflow; } diff --git a/Detectors/TOF/reconstruction/CMakeLists.txt b/Detectors/TOF/reconstruction/CMakeLists.txt index 3bdd192d96c42..d577164f5202e 100644 --- a/Detectors/TOF/reconstruction/CMakeLists.txt +++ b/Detectors/TOF/reconstruction/CMakeLists.txt @@ -13,8 +13,12 @@ o2_add_library(TOFReconstruction src/ClustererTask.cxx src/Encoder.cxx src/DecoderBase.cxx src/Decoder.cxx + src/CTFCoder.cxx PUBLIC_LINK_LIBRARIES O2::TOFBase O2::DataFormatsTOF O2::SimulationDataFormat + O2::CommonDataFormat + O2::DataFormatsTOF + O2::rANS O2::DPLUtils O2::TOFCalibration O2::DetectorsRaw) o2_target_root_dictionary(TOFReconstruction @@ -23,4 +27,5 @@ o2_target_root_dictionary(TOFReconstruction include/TOFReconstruction/ClustererTask.h include/TOFReconstruction/Encoder.h include/TOFReconstruction/DecoderBase.h - include/TOFReconstruction/Decoder.h) + include/TOFReconstruction/Decoder.h + include/TOFReconstruction/CTFCoder.h) diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h new file mode 100644 index 0000000000000..edea8157d760e --- /dev/null +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -0,0 +1,236 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.h +/// \author fnoferin@cern.ch +/// \brief class for entropy encoding/decoding of TOF compressed infos data + +#ifndef O2_TOF_CTFCODER_H +#define O2_TOF_CTFCODER_H + +#include <algorithm> +#include <iterator> +#include <string> +#include "DataFormatsTOF/CTF.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "rANS/rans.h" +#include "DetectorsBase/CTFCoderBase.h" +#include "TOFBase/Digit.h" + +class TTree; + +namespace o2 +{ +namespace tof +{ + +class CTFCoder : public o2::ctf::CTFCoderBase +{ + public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::TOF) {} + ~CTFCoder() = default; + + /// entropy-encode clusters to buffer with CTF + template <typename VEC> + void encode(VEC& buff, const gsl::span<const ReadoutWindowData>& rofRecVec, const gsl::span<const Digit>& cdigVec, const gsl::span<const uint32_t>& pattVec); + + /// entropy decode clusters from buffer with CTF + template <typename VROF, typename VDIG, typename VPAT> + void decode(const CTF::base& ec, VROF& rofRecVec, VDIG& cdigVec, VPAT& pattVec); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + + private: + /// compres compact clusters to CompressedInfos + void compress(CompressedInfos& cc, const gsl::span<const ReadoutWindowData>& rofRecVec, const gsl::span<const Digit>& cdigVec, const gsl::span<const uint32_t>& pattVec); + size_t estimateCompressedSize(const CompressedInfos& cc); + /// decompress CompressedInfos to compact clusters + template <typename VROF, typename VDIG, typename VPAT> + void decompress(const CompressedInfos& cc, VROF& rofRecVec, VDIG& cdigVec, VPAT& pattVec); + + void appendToTree(TTree& tree, CTF& ec); + void readFromTree(TTree& tree, int entry, std::vector<ReadoutWindowData>& rofRecVec, std::vector<Digit>& cdigVec, std::vector<uint32_t>& pattVec); + + protected: + ClassDefNV(CTFCoder, 1); +}; + +///___________________________________________________________________________________ +/// entropy-encode digits to buffer with CTF +template <typename VEC> +void CTFCoder::encode(VEC& buff, const gsl::span<const ReadoutWindowData>& rofRecVec, const gsl::span<const Digit>& cdigVec, const gsl::span<const uint32_t>& pattVec) +{ + using MD = o2::ctf::Metadata::OptStore; + // what to do which each field: see o2::ctd::Metadata explanation + constexpr MD optField[CTF::getNBlocks()] = { + MD::EENCODE, //BLCbcIncROF + MD::EENCODE, //BLCorbitIncROF + MD::EENCODE, //BLCndigROF + MD::EENCODE, //BLCndiaROF + MD::EENCODE, //BLCndiaCrate + MD::EENCODE, //BLCtimeFrameInc + MD::EENCODE, //BLCtimeTDCInc + MD::EENCODE, //BLCstripID + MD::EENCODE, //BLCchanInStrip + MD::EENCODE, //BLCtot + MD::EENCODE, //BLCpattMap + }; + CompressedInfos cc; + compress(cc, rofRecVec, cdigVec, pattVec); + // book output size with some margin + auto szIni = estimateCompressedSize(cc); + buff.resize(szIni); + + auto ec = CTF::create(buff); + using ECB = CTF::base; + + ec->setHeader(cc.header); + ec->getANSHeader().majorVersion = 0; + ec->getANSHeader().minorVersion = 1; + // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec +#define ENCODETOF(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); + // clang-format off + ENCODETOF(cc.bcIncROF, CTF::BLCbcIncROF, 0); + ENCODETOF(cc.orbitIncROF, CTF::BLCorbitIncROF, 0); + ENCODETOF(cc.ndigROF, CTF::BLCndigROF, 0); + ENCODETOF(cc.ndiaROF, CTF::BLCndiaROF, 0); + ENCODETOF(cc.ndiaCrate, CTF::BLCndiaCrate, 0); + ENCODETOF(cc.timeFrameInc, CTF::BLCtimeFrameInc, 0); + ENCODETOF(cc.timeTDCInc, CTF::BLCtimeTDCInc, 0); + ENCODETOF(cc.stripID, CTF::BLCstripID, 0); + ENCODETOF(cc.chanInStrip, CTF::BLCchanInStrip, 0); + ENCODETOF(cc.tot, CTF::BLCtot, 0); + ENCODETOF(cc.pattMap, CTF::BLCpattMap, 0); + // clang-format on + CTF::get(buff.data())->print(getPrefix()); +} + +///___________________________________________________________________________________ +/// decode entropy-encoded digits to standard compact digits +template <typename VROF, typename VDIG, typename VPAT> +void CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& cdigVec, VPAT& pattVec) +{ + CompressedInfos cc; + ec.print(getPrefix()); + cc.header = ec.getHeader(); +#define DECODETOF(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) + // clang-format off + DECODETOF(cc.bcIncROF, CTF::BLCbcIncROF); + DECODETOF(cc.orbitIncROF, CTF::BLCorbitIncROF); + DECODETOF(cc.ndigROF, CTF::BLCndigROF); + DECODETOF(cc.ndiaROF, CTF::BLCndiaROF); + DECODETOF(cc.ndiaCrate, CTF::BLCndiaCrate); + + DECODETOF(cc.timeFrameInc, CTF::BLCtimeFrameInc); + DECODETOF(cc.timeTDCInc, CTF::BLCtimeTDCInc); + DECODETOF(cc.stripID, CTF::BLCstripID); + DECODETOF(cc.chanInStrip, CTF::BLCchanInStrip); + DECODETOF(cc.tot, CTF::BLCtot); + DECODETOF(cc.pattMap, CTF::BLCpattMap); + // clang-format on + // + decompress(cc, rofRecVec, cdigVec, pattVec); +} +///___________________________________________________________________________________ +/// decompress compressed infos to standard compact digits +template <typename VROF, typename VDIG, typename VPAT> +void CTFCoder::decompress(const CompressedInfos& cc, VROF& rofRecVec, VDIG& cdigVec, VPAT& pattVec) +{ + rofRecVec.resize(cc.header.nROFs); + cdigVec.resize(cc.header.nDigits); + pattVec.resize(cc.header.nPatternBytes); + std::vector<Digit> digCopy; + + o2::InteractionRecord prevIR(cc.header.firstBC, cc.header.firstOrbit); + uint32_t firstEntry = 0, digCount = 0, stripCount = 0, ndiagnostic = 0; + for (uint32_t irof = 0; irof < cc.header.nROFs; irof++) { + // restore ROFRecord + auto& rofRec = rofRecVec[irof]; + if (cc.orbitIncROF[irof]) { // new orbit + prevIR.bc = cc.bcIncROF[irof]; // bcInc has absolute meaning + prevIR.orbit += cc.orbitIncROF[irof]; + } else { + prevIR.bc += cc.bcIncROF[irof]; + } + rofRec.setBCData(prevIR); + rofRec.setFirstEntry(firstEntry); + rofRec.setNEntries(cc.ndigROF[irof]); + rofRec.setFirstEntryDia(ndiagnostic); + rofRec.setNEntriesDia(cc.ndiaROF[irof]); + for (int icrate = 0; icrate < 72; icrate++) { + rofRec.setDiagnosticInCrate(icrate, cc.ndiaCrate[irof * 72 + icrate] - 1); // -1 because number were traslated since (-1 means crate not available) + } + firstEntry += cc.ndigROF[irof]; + ndiagnostic += cc.ndiaROF[irof]; + + if (!cc.ndigROF[irof]) { + continue; + } + + // restore hit data + uint ctimeframe = 0; + uint ctdc = 0; + + int firstDig = digCount; + + int BCrow = prevIR.orbit * Geo::BC_IN_ORBIT + prevIR.bc; + + digCopy.resize(cc.ndigROF[irof]); + for (uint32_t idig = 0; idig < cc.ndigROF[irof]; idig++) { + auto& digit = digCopy[idig]; //cdigVec[digCount]; + LOGF(DEBUG, "%d) TF=%d, TDC=%d, STRIP=%d, CH=%d", idig, cc.timeFrameInc[digCount], cc.timeTDCInc[digCount], cc.stripID[digCount], cc.chanInStrip[digCount]); + if (cc.timeFrameInc[digCount]) { // new time frame + ctdc = cc.timeTDCInc[digCount]; + ctimeframe += cc.timeFrameInc[digCount]; + } else { + ctdc += cc.timeTDCInc[digCount]; + } + LOGF(DEBUG, "BC=%d, TDC=%d, TOT=%d, CH=%d", uint32_t(ctimeframe) * 64 + ctdc / 1024 + BCrow, ctdc % 1024, cc.tot[digCount], uint32_t(cc.stripID[digCount]) * 96 + cc.chanInStrip[digCount]); + + digit.setBC(uint32_t(ctimeframe) * 64 + ctdc / 1024 + BCrow); + digit.setTDC(ctdc % 1024); + digit.setTOT(cc.tot[digCount]); + digit.setChannel(uint32_t(cc.stripID[digCount]) * 96 + cc.chanInStrip[digCount]); + + digCount++; + } + + // sort digits according to strip number within the ROF + std::sort(digCopy.begin(), digCopy.end(), + [](o2::tof::Digit a, o2::tof::Digit b) { + int str1 = a.getChannel() / Geo::NPADS; + int str2 = b.getChannel() / Geo::NPADS; + if (str1 == str2) { + return (a.getOrderingKey() < b.getOrderingKey()); + } + return (str1 < str2); + }); + + // fill digits, once sorted, of rof in digit vector + for (uint32_t idig = 0; idig < digCopy.size(); idig++) { + cdigVec[firstDig + idig] = digCopy[idig]; + } + + digCopy.clear(); + } + // explicit patterns + memcpy(pattVec.data(), cc.pattMap.data(), cc.header.nPatternBytes); // RSTODO use swap? + assert(digCount == cc.header.nDigits); + + if (digCount != cc.header.nDigits) { + LOG(ERROR) << "expected " << cc.header.nDigits << " but counted " << digCount << " in ROFRecords"; + throw std::runtime_error("mismatch between expected and counter number of digits"); + } +} + +} // namespace tof +} // namespace o2 + +#endif // O2_TOF_CTFCODER_H diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/Clusterer.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/Clusterer.h index 5c0903247eb1e..7bab6b0bef59a 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/Clusterer.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/Clusterer.h @@ -29,7 +29,7 @@ namespace tof { class Clusterer { - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + using MCLabelContainer = o2::dataformats::MCLabelContainer; using Cluster = o2::tof::Cluster; using StripData = o2::tof::DataReader::StripData; using Digit = o2::tof::Digit; @@ -49,9 +49,11 @@ class Clusterer void setCalibApi(CalibApi* calibApi) { mCalibApi = calibApi; - Printf("mCalibApi = %p", mCalibApi); } + void setFirstOrbit(uint64_t orb); + uint64_t getFirstOrbit() const { return mFirstOrbit; } + private: void calibrateStrip(); void processStrip(std::vector<Cluster>& clusters, MCLabelContainer const* digitMCTruth); @@ -66,6 +68,8 @@ class Clusterer void addContributingDigit(Digit* dig); void buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth); CalibApi* mCalibApi = nullptr; //! calib api to handle the TOF calibration + uint64_t mFirstOrbit = 0; //! 1st orbit of the TF + uint64_t mBCOffset = 0; //! 1st orbit of the TF converted to BCs }; } // namespace tof diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/ClustererTask.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/ClustererTask.h index 0e3b2423adeb1..45b0caffa4a24 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/ClustererTask.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/ClustererTask.h @@ -32,9 +32,6 @@ namespace tof class ClustererTask : public FairTask { - - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - public: ClustererTask(Bool_t useMCTruth = kTRUE); ~ClustererTask() override; @@ -46,9 +43,9 @@ class ClustererTask : public FairTask DigitDataReader mReader; ///< Digit reader Clusterer mClusterer; ///< Cluster finder - std::vector<Cluster>* mClustersArray = nullptr; ///< Array of clusters - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mClsLabels = nullptr; ///< MC labels for output - MCLabelContainer const* mDigitMCTruth; ///< Array for MCTruth information associated to digits + std::vector<Cluster>* mClustersArray = nullptr; ///< Array of clusters + o2::dataformats::MCLabelContainer* mClsLabels = nullptr; ///< MC labels for output + o2::dataformats::MCLabelContainer const* mDigitMCTruth; ///< Array for MCTruth information associated to digits ClassDefOverride(ClustererTask, 1); }; diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/Decoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/Decoder.h index eeee1b4838304..735273db631f4 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/Decoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/Decoder.h @@ -44,8 +44,8 @@ class Decoder : public WindowFiller bool open(std::string name); bool decode(); - void readTRM(int icru, int icrate, int orbit, int bunchid); - void InsertDigit(int icrate, int itrm, int itdc, int ichain, int channel, int orbit, int bunchid, int time_ext, int tdc, int tot); + void readTRM(int icru, int icrate, uint32_t orbit, uint16_t bunchid); + void InsertDigit(int icrate, int itrm, int itdc, int ichain, int channel, uint32_t orbit, uint16_t bunchid, int time_ext, int tdc, int tot); void FillWindows(); void clear(); @@ -58,10 +58,22 @@ class Decoder : public WindowFiller void printCrateTrailerInfo(int icru) const; void printHitInfo(int icru) const; - static void fromRawHit2Digit(int icrate, int itrm, int itdc, int ichain, int channel, int orbit, int bunchid, int tdc, int tot, std::array<int, 6>& digitInfo); // convert raw info in digit info (channel, tdc, tot, bc), tdc = packetHit.time + (frameHeader.frameID << 13) + struct DigitInfo { + uint64_t bcAbs; + int channel; + int tdc; + int tot; + uint32_t orbit; + uint16_t bc; + }; + + static void fromRawHit2Digit(int icrate, int itrm, int itdc, int ichain, int channel, uint32_t orbit, uint16_t bunchid, int tdc, int tot, DigitInfo& dinfo); // convert raw info in digit info (channel, tdc, tot, bc), tdc = packetHit.time + (frameHeader.frameID << 13) char* nextPage(void* current, int shift = 8192); + std::vector<uint64_t>& getErrors() { return mErrors; } + void addError(const uint32_t val, int icrate) { mErrors.push_back((uint64_t(icrate) << 32) + val); } + protected: static const int NCRU = 4; diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/DecoderBase.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/DecoderBase.h index a03744814cdc2..a4346afb59298 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/DecoderBase.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/DecoderBase.h @@ -41,8 +41,18 @@ class DecoderBaseT inline bool run() { rewind(); - while (!processHBF()) + if (mDecoderCONET) { + mDecoderPointerMax = reinterpret_cast<const uint32_t*>(mDecoderBuffer + mDecoderBufferSize); + while (mDecoderPointer < mDecoderPointerMax) { + if (processDRM()) { + return false; + } + } + return false; + } + while (!processHBF()) { ; + } return false; }; @@ -54,24 +64,20 @@ class DecoderBaseT void setDecoderVerbose(bool val) { mDecoderVerbose = val; }; void setDecoderBuffer(const char* val) { mDecoderBuffer = val; }; void setDecoderBufferSize(long val) { mDecoderBufferSize = val; }; + void setDecoderCONET(bool val) { mDecoderCONET = val; }; private: /** handlers **/ - virtual void rdhHandler(const RDH* rdh){}; - virtual void headerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit){}; + virtual void rdhHandler(const RDH* rdh) = 0; + virtual void headerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit) = 0; virtual void frameHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, - const FrameHeader_t* frameHeader, const PackedHit_t* packedHits){}; + const FrameHeader_t* frameHeader, const PackedHit_t* packedHits) = 0; virtual void trailerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, const CrateTrailer_t* crateTrailer, const Diagnostic_t* diagnostics, - const Error_t* errors){}; - - /** old API, deprecated **/ - - virtual void trailerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, - const CrateTrailer_t* crateTrailer, const Diagnostic_t* diagnostics){}; + const Error_t* errors) = 0; bool processHBF(); bool processDRM(); @@ -88,6 +94,7 @@ class DecoderBaseT bool mDecoderVerbose = false; bool mDecoderError = false; bool mDecoderFatal = false; + bool mDecoderCONET = false; char mDecoderSaveBuffer[1048576]; uint32_t mDecoderSaveBufferDataSize = 0; uint32_t mDecoderSaveBufferDataLeft = 0; diff --git a/Detectors/TOF/reconstruction/src/CTFCoder.cxx b/Detectors/TOF/reconstruction/src/CTFCoder.cxx new file mode 100644 index 0000000000000..8a72aab4ac933 --- /dev/null +++ b/Detectors/TOF/reconstruction/src/CTFCoder.cxx @@ -0,0 +1,224 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CTFCoder.cxx +/// \author fnoferin@cern.ch +/// \brief class for entropy encoding/decoding of TOF compressed digits data + +#include "TOFReconstruction/CTFCoder.h" +#include "CommonUtils/StringUtils.h" +#include <TTree.h> + +using namespace o2::tof; + +///___________________________________________________________________________________ +// Register encoded data in the tree (Fill is not called, will be done by caller) +void CTFCoder::appendToTree(TTree& tree, CTF& ec) +{ + ec.appendToTree(tree, mDet.getName()); +} + +///___________________________________________________________________________________ +// extract and decode data from the tree +void CTFCoder::readFromTree(TTree& tree, int entry, std::vector<ReadoutWindowData>& rofRecVec, std::vector<Digit>& cdigVec, std::vector<uint32_t>& pattVec) +{ + assert(entry >= 0 && entry < tree.GetEntries()); + CTF ec; + ec.readFromTree(tree, mDet.getName(), entry); + decode(ec, rofRecVec, cdigVec, pattVec); +} + +///________________________________ +void CTFCoder::compress(CompressedInfos& cc, + const gsl::span<const ReadoutWindowData>& rofRecVec, + const gsl::span<const Digit>& cdigVec, + const gsl::span<const uint32_t>& pattVec) +{ + // store in the header the orbit of 1st ROF + cc.clear(); + if (!rofRecVec.size()) { + return; + } + const auto& rofRec0 = rofRecVec[0]; + int nrof = rofRecVec.size(); + + LOGF(INFO, "TOF compress %d ReadoutWindow with %ld digits", nrof, cdigVec.size()); + + cc.header.nROFs = nrof; + cc.header.firstOrbit = rofRec0.getBCData().orbit; + cc.header.firstBC = rofRec0.getBCData().bc; + cc.header.nPatternBytes = pattVec.size(); + cc.header.nDigits = cdigVec.size(); + + cc.bcIncROF.resize(cc.header.nROFs); + cc.orbitIncROF.resize(cc.header.nROFs); + cc.ndigROF.resize(cc.header.nROFs); + cc.ndiaROF.resize(cc.header.nROFs); + cc.ndiaCrate.resize(cc.header.nROFs * 72); + // + cc.timeFrameInc.resize(cc.header.nDigits); + cc.timeTDCInc.resize(cc.header.nDigits); + cc.stripID.resize(cc.header.nDigits); + cc.chanInStrip.resize(cc.header.nDigits); + cc.tot.resize(cc.header.nDigits); + cc.pattMap.resize(cc.header.nPatternBytes); + + uint16_t prevBC = cc.header.firstBC; + uint32_t prevOrbit = cc.header.firstOrbit; + + std::vector<Digit> digCopy; + + for (uint32_t irof = 0; irof < rofRecVec.size(); irof++) { + const auto& rofRec = rofRecVec[irof]; + + const auto& intRec = rofRec.getBCData(); + int rofInBC = intRec.toLong(); + // define interaction record + if (intRec.orbit == prevOrbit) { + cc.orbitIncROF[irof] = 0; + cc.bcIncROF[irof] = intRec.bc - prevBC; // store increment of BC if in the same orbit + } else { + cc.orbitIncROF[irof] = intRec.orbit - prevOrbit; + cc.bcIncROF[irof] = intRec.bc; // otherwise, store absolute bc + prevOrbit = intRec.orbit; + } + prevBC = intRec.bc; + auto ndig = rofRec.size(); + cc.ndigROF[irof] = ndig; + cc.ndiaROF[irof] = rofRec.sizeDia(); + for (int icrate = 0; icrate < 72; icrate++) { + if (rofRec.isEmptyCrate(icrate)) { + cc.ndiaCrate[irof * 72 + icrate] = 0; + } else { + cc.ndiaCrate[irof * 72 + icrate] = rofRec.getDiagnosticInCrate(icrate) + 1; // shifted by one since -1 means crate not available (then to get unsigned int) + } + } + + if (!ndig) { // no hits data for this ROF --> not fill + continue; + } + + int idigMin = rofRec.first(), idigMax = idigMin + ndig; + int idig = idigMin; + + // make a copy of digits + digCopy.clear(); + for (; idig < idigMax; idig++) { + digCopy.emplace_back(cdigVec[idig]); + } + + // sort digits according to time (ascending order) + std::sort(digCopy.begin(), digCopy.end(), + [](o2::tof::Digit a, o2::tof::Digit b) { + if (a.getBC() == b.getBC()) { + return a.getTDC() < b.getTDC(); + } else { + return a.getBC() < b.getBC(); + } + }); + + int timeframe = 0; + int tdc = 0; + idig = idigMin; + for (; idig < idigMax; idig++) { + const auto& dig = digCopy[idig - idigMin]; + int deltaBC = dig.getBC() - rofInBC; + int ctimeframe = deltaBC / 64; + int cTDC = (deltaBC % 64) * 1024 + dig.getTDC(); + if (ctimeframe == timeframe) { + cc.timeFrameInc[idig] = 0; + cc.timeTDCInc[idig] = cTDC - tdc; + } else { + cc.timeFrameInc[idig] = ctimeframe - timeframe; + cc.timeTDCInc[idig] = cTDC; + timeframe = ctimeframe; + } + tdc = cTDC; + + int chan = dig.getChannel(); + cc.stripID[idig] = chan / Geo::NPADS; + cc.chanInStrip[idig] = chan % Geo::NPADS; + cc.tot[idig] = dig.getTOT(); + LOGF(DEBUG, "%d) TOFBC = %d, deltaBC = %d, TDC = %d, CH=%d", irof, rofInBC, deltaBC, cTDC, chan); + LOGF(DEBUG, "%d) TF=%d, TDC=%d, STRIP=%d, CH=%d, TOT=%d", idig, cc.timeFrameInc[idig], cc.timeTDCInc[idig], cc.stripID[idig], cc.chanInStrip[idig], cc.tot[idig]); + } + } + // store explicit patters as they are + memcpy(cc.pattMap.data(), pattVec.data(), cc.header.nPatternBytes); // RSTODO: do we need this? +} + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + CompressedInfos cc; // just to get member types +#define MAKECODER(part, slot) createCoder<decltype(part)::value_type>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + MAKECODER(cc.bcIncROF, CTF::BLCbcIncROF); + MAKECODER(cc.orbitIncROF, CTF::BLCorbitIncROF); + MAKECODER(cc.ndigROF, CTF::BLCndigROF); + MAKECODER(cc.ndiaROF, CTF::BLCndiaROF); + MAKECODER(cc.ndiaCrate, CTF::BLCndiaCrate); + + MAKECODER(cc.timeFrameInc, CTF::BLCtimeFrameInc); + MAKECODER(cc.timeTDCInc, CTF::BLCtimeTDCInc); + MAKECODER(cc.stripID, CTF::BLCstripID); + MAKECODER(cc.chanInStrip, CTF::BLCchanInStrip); + MAKECODER(cc.tot, CTF::BLCtot); + MAKECODER(cc.pattMap, CTF::BLCpattMap); + // clang-format on +} + +///________________________________ +size_t CTFCoder::estimateCompressedSize(const CompressedInfos& cc) +{ + 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<decltype(vec)>::type::value_type +#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ + rans::calculateMaxBufferSize(vec.size(), reinterpret_cast<const o2::rans::LiteralEncoder64<VTP(vec)>*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) + + sz += ESTSIZE(cc.bcIncROF, CTF::BLCbcIncROF); + sz += ESTSIZE(cc.orbitIncROF, CTF::BLCorbitIncROF); + sz += ESTSIZE(cc.ndigROF, CTF::BLCndigROF); + sz += ESTSIZE(cc.ndiaROF, CTF::BLCndiaROF); + sz += ESTSIZE(cc.ndiaCrate, CTF::BLCndiaCrate); + sz += ESTSIZE(cc.timeFrameInc, CTF::BLCtimeFrameInc); + sz += ESTSIZE(cc.timeTDCInc, CTF::BLCtimeTDCInc); + sz += ESTSIZE(cc.stripID, CTF::BLCstripID); + sz += ESTSIZE(cc.chanInStrip, CTF::BLCchanInStrip); + sz += ESTSIZE(cc.tot, CTF::BLCtot); + sz += ESTSIZE(cc.pattMap, CTF::BLCpattMap); + // clang-format on + + LOG(INFO) << "Estimated output size is " << sz << " bytes"; + return sz; +} diff --git a/Detectors/TOF/reconstruction/src/Clusterer.cxx b/Detectors/TOF/reconstruction/src/Clusterer.cxx index 4d4bc96ed5c5d..60fd6594588a8 100644 --- a/Detectors/TOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/TOF/reconstruction/src/Clusterer.cxx @@ -40,9 +40,6 @@ void Clusterer::process(DataReader& reader, std::vector<Cluster>& clusters, MCLa LOG(DEBUG) << "We had " << totNumDigits << " digits in this event"; timerProcess.Stop(); - printf("Timing:\n"); - printf("Clusterer::process: "); - timerProcess.Print(); } //__________________________________________________ @@ -53,6 +50,7 @@ void Clusterer::calibrateStrip() for (int idig = 0; idig < mStripData.digits.size(); idig++) { // LOG(DEBUG) << "Checking digit " << idig; Digit* dig = &mStripData.digits[idig]; + dig->setBC(dig->getBC() - mBCOffset); // RS Don't use raw BC, always start from the beginning of the TF double calib = mCalibApi->getTimeCalibration(dig->getChannel(), dig->getTOT() * Geo::TOTBIN_NS); //printf("channel %d) isProblematic = %d, fractionUnderPeak = %f\n",dig->getChannel(),mCalibApi->isProblematic(dig->getChannel()),mCalibApi->getFractionUnderPeak(dig->getChannel())); // toberem dig->setIsProblematic(mCalibApi->isProblematic(dig->getChannel())); @@ -76,8 +74,9 @@ void Clusterer::processStrip(std::vector<Cluster>& clusters, MCLabelContainer co // LOG(DEBUG) << "Checking digit " << idig; Digit* dig = &mStripData.digits[idig]; //printf("checking digit %d - alreadyUsed=%d - problematic=%d\n",idig,dig->isUsedInCluster(),dig->isProblematic()); // toberem - if (dig->isUsedInCluster() || dig->isProblematic()) + if (dig->isUsedInCluster() || dig->isProblematic()) { continue; // the digit was already used to build a cluster, or it was declared problematic + } mNumberOfContributingDigits = 0; dig->getPhiAndEtaIndex(iphi, ieta); @@ -95,20 +94,23 @@ void Clusterer::processStrip(std::vector<Cluster>& clusters, MCLabelContainer co for (int idigNext = idig + 1; idigNext < mStripData.digits.size(); idigNext++) { Digit* digNext = &mStripData.digits[idigNext]; - if (digNext->isUsedInCluster() || dig->isProblematic()) + if (digNext->isUsedInCluster() || dig->isProblematic()) { continue; // the digit was already used to build a cluster, or was problematic + } // check if the TOF time are close enough to be merged; if not, it means that nothing else will contribute to the cluster (since digits are ordered in time) double timeDigNext = digNext->getCalibratedTime(); // in ps LOG(DEBUG) << "Time difference = " << timeDigNext - timeDig; - if (timeDigNext - timeDig > 500 /*in ps*/) + if (timeDigNext - timeDig > 5000 /*in ps*/) { // to be change to 500 ps break; + } digNext->getPhiAndEtaIndex(iphi2, ieta2); // check if the fired pad are close in space LOG(DEBUG) << "phi difference = " << iphi - iphi2; LOG(DEBUG) << "eta difference = " << ieta - ieta2; - if ((TMath::Abs(iphi - iphi2) > 1) || (TMath::Abs(ieta - ieta2) > 1)) + if ((TMath::Abs(iphi - iphi2) > 1) || (TMath::Abs(ieta - ieta2) > 1)) { continue; + } // if we are here, the digit contributes to the cluster addContributingDigit(digNext); @@ -127,17 +129,17 @@ void Clusterer::addContributingDigit(Digit* dig) // adding a digit to the array that stores the contributing ones if (mNumberOfContributingDigits == 6) { - LOG(WARNING) << "The cluster has already 6 digits associated to it, we cannot add more; returning without doing anything"; + LOG(DEBUG) << "The cluster has already 6 digits associated to it, we cannot add more; returning without doing anything"; int phi, eta; for (int i = 0; i < mNumberOfContributingDigits; i++) { mContributingDigit[i]->getPhiAndEtaIndex(phi, eta); - LOG(WARNING) << "digit already in " << i << ", channel = " << mContributingDigit[i]->getChannel() << ",phi,eta = (" << phi << "," << eta << "), TDC = " << mContributingDigit[i]->getTDC() << ", calibrated time = " << mContributingDigit[i]->getCalibratedTime(); + LOG(DEBUG) << "digit already in " << i << ", channel = " << mContributingDigit[i]->getChannel() << ",phi,eta = (" << phi << "," << eta << "), TDC = " << mContributingDigit[i]->getTDC() << ", calibrated time = " << mContributingDigit[i]->getCalibratedTime(); } dig->getPhiAndEtaIndex(phi, eta); - LOG(WARNING) << "skipped digit" - << ", channel = " << dig->getChannel() << ",phi,eta = (" << phi << "," << eta << "), TDC = " << dig->getTDC() << ", calibrated time = " << dig->getCalibratedTime(); + LOG(DEBUG) << "skipped digit" + << ", channel = " << dig->getChannel() << ",phi,eta = (" << phi << "," << eta << "), TDC = " << dig->getTDC() << ", calibrated time = " << dig->getCalibratedTime(); dig->setIsUsedInCluster(); // flag is at used in any case @@ -171,10 +173,14 @@ void Clusterer::buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth) c.setTime(mContributingDigit[0]->getCalibratedTime()); // time in ps (for now we assume it calibrated) c.setTimeRaw(mContributingDigit[0]->getTDC() * Geo::TDCBIN + mContributingDigit[0]->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3); // time in ps (for now we assume it calibrated) + //printf("timeraw= %lf - time real = %lf (%d, %lu) \n",c.getTimeRaw(),mContributingDigit[0]->getTDC() * Geo::TDCBIN + mContributingDigit[0]->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3,mContributingDigit[0]->getTDC(),mContributingDigit[0]->getBC()); + c.setTot(mContributingDigit[0]->getTOT() * Geo::TOTBIN_NS); // TOT in ns (for now we assume it calibrated) //setL0L1Latency(); // to be filled (maybe) //setDeltaBC(); // to be filled (maybe) + c.setDigitInfo(0, mContributingDigit[0]->getChannel(), mContributingDigit[0]->getCalibratedTime(), mContributingDigit[0]->getTOT() * Geo::TOTBIN_NS); + int chan1, chan2; int phi1, phi2; int eta1, eta2; @@ -187,6 +193,9 @@ void Clusterer::buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth) mContributingDigit[idig]->getPhiAndEtaIndex(phi2, eta2); deltaPhi = phi1 - phi2; deltaEta = eta1 - eta2; + + bool isOk = true; + if (deltaPhi == 1) { // the digit is to the LEFT of the cluster; let's check about UP/DOWN/Same Line if (deltaEta == 1) { // the digit is DOWN LEFT wrt the cluster mask = Cluster::kDownLeft; @@ -208,13 +217,18 @@ void Clusterer::buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth) mask = Cluster::kDown; } else if (deltaEta == -1) { // the digit is UP wrt the cluster mask = Cluster::kUp; - } else { // impossible!! + } else { // same channel! LOG(DEBUG) << " Check what is going on, the digit you are trying to merge to the cluster must be in a different channels... "; } - } else { // impossible!!! We checked above... - LOG(DEBUG) << " Check what is going on, the digit you are trying to merge to the cluster is too far from the cluster, you should have not got here... "; + } else { // |delataphi| > 1 + isOk = false; + mContributingDigit[idig]->setIsUsedInCluster(false); + } + + if (isOk) { + c.setDigitInfo(c.getNumOfContributingChannels(), mContributingDigit[idig]->getChannel(), mContributingDigit[idig]->getCalibratedTime(), mContributingDigit[idig]->getTOT() * Geo::TOTBIN_NS); + c.addBitInContributingChannels(mask); } - c.addBitInContributingChannels(mask); } // filling the MC labels of this cluster; the first will be those of the main digit; then the others @@ -222,6 +236,9 @@ void Clusterer::buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth) int lbl = mClsLabels->getIndexedSize(); // this should correspond to the number of digits also; //printf("lbl = %d\n", lbl); for (int i = 0; i < mNumberOfContributingDigits; i++) { + if (!mContributingDigit[i]->isUsedInCluster()) { + continue; + } //printf("contributing digit = %d\n", i); int digitLabel = mContributingDigit[i]->getLabel(); //printf("digitLabel = %d\n", digitLabel); @@ -258,3 +275,10 @@ void Clusterer::buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth) return; } + +//_____________________________________________________________________ +void Clusterer::setFirstOrbit(uint64_t orb) +{ + mFirstOrbit = orb; + mBCOffset = orb * o2::constants::lhc::LHCMaxBunches; +} diff --git a/Detectors/TOF/reconstruction/src/ClustererTask.cxx b/Detectors/TOF/reconstruction/src/ClustererTask.cxx index 9bb588bef2efe..2410a47b349d1 100644 --- a/Detectors/TOF/reconstruction/src/ClustererTask.cxx +++ b/Detectors/TOF/reconstruction/src/ClustererTask.cxx @@ -12,7 +12,7 @@ /// \brief Implementation of the TOF cluster finder task #include "TOFReconstruction/ClustererTask.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -27,8 +27,9 @@ using namespace o2::tof; //_____________________________________________________________________ ClustererTask::ClustererTask(Bool_t useMCTruth) : FairTask("TOFClustererTask") { - if (useMCTruth) + if (useMCTruth) { mClsLabels = new o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + } } //_____________________________________________________________________ @@ -74,8 +75,9 @@ InitStatus ClustererTask::Init() mgr->RegisterAny("TOFCluster", mClustersArray, kTRUE); // Register new MC Truth container --> here we will now associate to the clusters all labels that belonged to all digits that formed that cluster - if (mClsLabels) + if (mClsLabels) { mgr->RegisterAny("TOFClusterMCTruth", mClsLabels, kTRUE); + } mClusterer.setMCTruthContainer(mClsLabels); @@ -85,10 +87,12 @@ InitStatus ClustererTask::Init() //_____________________________________________________________________ void ClustererTask::Exec(Option_t* option) { - if (mClustersArray) + if (mClustersArray) { mClustersArray->clear(); - if (mClsLabels) + } + if (mClsLabels) { mClsLabels->clear(); + } LOG(DEBUG) << "Running clusterization on new event"; mClusterer.process(mReader, *mClustersArray, mDigitMCTruth); diff --git a/Detectors/TOF/reconstruction/src/DataReader.cxx b/Detectors/TOF/reconstruction/src/DataReader.cxx index 81a53c7c84d42..e8f55bda34e6a 100644 --- a/Detectors/TOF/reconstruction/src/DataReader.cxx +++ b/Detectors/TOF/reconstruction/src/DataReader.cxx @@ -41,15 +41,16 @@ Bool_t DigitDataReader::getNextStripData(StripData& stripData) while (mIdx < mDigitArray->size()) { mLastDigit = &((*mDigitArray)[mIdx++]); - if (stripData.stripID != mLastDigit->getChannel() / Geo::NPADS) + if (stripData.stripID != mLastDigit->getChannel() / Geo::NPADS) { break; + } stripData.digits.emplace_back(*mLastDigit); mLastDigit = nullptr; } // sorting the digits of the current strip according to the TDC std::sort(stripData.digits.begin(), stripData.digits.end(), - [](const Digit& a, const Digit& b) { return a.getTDC() < b.getTDC(); }); + [](const Digit& a, const Digit& b) { if(a.getBC() != b.getBC()){ return a.getBC() < b.getBC();} return a.getTDC() < b.getTDC(); }); return kTRUE; } diff --git a/Detectors/TOF/reconstruction/src/Decoder.cxx b/Detectors/TOF/reconstruction/src/Decoder.cxx index 195da9757df91..46cb81b1ab31f 100644 --- a/Detectors/TOF/reconstruction/src/Decoder.cxx +++ b/Detectors/TOF/reconstruction/src/Decoder.cxx @@ -34,6 +34,7 @@ Decoder::Decoder() mBuffer[i] = nullptr; mCruIn[i] = false; } + clearCounts(); } bool Decoder::open(const std::string name) @@ -78,8 +79,9 @@ bool Decoder::open(const std::string name) char* pos = new char[fullsize]; //mBufferLocal.data(); for (int i = 0; i < NCRU; i++) { - if (!mCruIn[i]) + if (!mCruIn[i]) { continue; + } mBuffer[i] = pos; @@ -101,8 +103,9 @@ bool Decoder::open(const std::string name) bool Decoder::close() { for (int i = 0; i < NCRU; i++) { - if (mFile[i].is_open()) + if (mFile[i].is_open()) { mFile[i].close(); + } } return false; } @@ -110,84 +113,95 @@ bool Decoder::close() void Decoder::clear() { reset(); + if (mMaskNoiseRate > 0) { + clearCounts(); + } + + mPatterns.clear(); + mCratePatterns.clear(); + mCrateHeaderData.clear(); + mErrors.clear(); } -void Decoder::InsertDigit(int icrate, int itrm, int itdc, int ichain, int channel, int orbit, int bunchid, int time_ext, int tdc, int tot) +void Decoder::InsertDigit(int icrate, int itrm, int itdc, int ichain, int channel, uint32_t orbit, uint16_t bunchid, int time_ext, int tdc, int tot) { - std::array<int, 6> digitInfo; + DigitInfo digitInfo; fromRawHit2Digit(icrate, itrm, itdc, ichain, channel, orbit, bunchid, time_ext + tdc, tot, digitInfo); + if (mMaskNoiseRate > 0) { + mChannelCounts[digitInfo.channel]++; + } mHitDecoded++; - int isnext = digitInfo[3] * Geo::BC_IN_WINDOW_INV; - - if (isnext >= MAXWINDOWS) { // accumulate all digits which are not in the first windows + uint64_t isnext = digitInfo.bcAbs * Geo::BC_IN_WINDOW_INV; - insertDigitInFuture(digitInfo[0], digitInfo[1], digitInfo[2], digitInfo[3], 0, digitInfo[4], digitInfo[5]); + if (isnext >= uint64_t(MAXWINDOWS)) { // accumulate all digits which are not in the first windows + insertDigitInFuture(digitInfo.channel, digitInfo.tdc, digitInfo.tot, digitInfo.bcAbs, 0, digitInfo.orbit, digitInfo.bc); } else { std::vector<Strip>* cstrip = mStripsCurrent; // first window - if (isnext) + if (isnext) { cstrip = mStripsNext[isnext - 1]; // next window - - UInt_t istrip = digitInfo[0] / Geo::NPADS; + } + UInt_t istrip = digitInfo.channel / Geo::NPADS; // add digit - fillDigitsInStrip(cstrip, digitInfo[0], digitInfo[1], digitInfo[2], digitInfo[3], istrip); + fillDigitsInStrip(cstrip, digitInfo.channel, digitInfo.tdc, digitInfo.tot, digitInfo.bcAbs, istrip); } } -void Decoder::readTRM(int icru, int icrate, int orbit, int bunchid) +void Decoder::readTRM(int icru, int icrate, uint32_t orbit, uint16_t bunchid) { - if (orbit < mFirstOrbit || (orbit == mFirstOrbit && bunchid < mFirstBunch)) { - mFirstOrbit = orbit; - mFirstBunch = bunchid; + if (orbit < mFirstIR.orbit || (orbit == mFirstIR.orbit && bunchid < mFirstIR.bc)) { + mFirstIR.orbit = orbit; + mFirstIR.bc = bunchid; } - if (mVerbose) + if (mVerbose) { printTRMInfo(icru); + } int nhits = mUnion[icru]->frameHeader.numberOfHits; int time_ext = mUnion[icru]->frameHeader.frameID << 13; int itrm = mUnion[icru]->frameHeader.trmID; int deltaBC = mUnion[icru]->frameHeader.deltaBC; - if (deltaBC != 0) + if (deltaBC != 0) { printf("DeltaBC = %d\n", deltaBC); + } mUnion[icru]++; mIntegratedBytes[icru] += 4; - // read hits - Int_t channel, echannel; - Int_t tdc; - Int_t tot; - Int_t bc; - Int_t time; - - std::array<int, 6> digitInfo; + DigitInfo digitInfo; for (int i = 0; i < nhits; i++) { - fromRawHit2Digit(icrate, itrm, mUnion[icru]->packedHit.tdcID, mUnion[icru]->packedHit.chain, mUnion[icru]->packedHit.channel, orbit, bunchid, time_ext + mUnion[icru]->packedHit.time, mUnion[icru]->packedHit.tot, digitInfo); + fromRawHit2Digit(icrate, itrm, mUnion[icru]->packedHit.tdcID, mUnion[icru]->packedHit.chain, mUnion[icru]->packedHit.channel, orbit, bunchid, + time_ext + mUnion[icru]->packedHit.time, mUnion[icru]->packedHit.tot, digitInfo); + if (mMaskNoiseRate > 0) { + mChannelCounts[digitInfo.channel]++; + } mHitDecoded++; - if (mVerbose) + if (mVerbose) { printHitInfo(icru); + } - int isnext = digitInfo[3] * Geo::BC_IN_WINDOW_INV; + uint64_t isnext = digitInfo.bcAbs * Geo::BC_IN_WINDOW_INV; if (isnext >= MAXWINDOWS) { // accumulate all digits which are not in the first windows - insertDigitInFuture(digitInfo[0], digitInfo[1], digitInfo[2], digitInfo[3], 0, digitInfo[4], digitInfo[5]); + insertDigitInFuture(digitInfo.channel, digitInfo.tdc, digitInfo.tot, digitInfo.bcAbs, 0, digitInfo.orbit, digitInfo.bc); } else { std::vector<Strip>* cstrip = mStripsCurrent; // first window - if (isnext) + if (isnext) { cstrip = mStripsNext[isnext - 1]; // next window + } - UInt_t istrip = digitInfo[0] / Geo::NPADS; + UInt_t istrip = digitInfo.channel / Geo::NPADS; // add digit - fillDigitsInStrip(cstrip, digitInfo[0], digitInfo[1], digitInfo[2], digitInfo[3], istrip); + fillDigitsInStrip(cstrip, digitInfo.channel, digitInfo.tdc, digitInfo.tot, digitInfo.bcAbs, istrip); } mUnion[icru]++; @@ -195,21 +209,17 @@ void Decoder::readTRM(int icru, int icrate, int orbit, int bunchid) } } -void Decoder::fromRawHit2Digit(int icrate, int itrm, int itdc, int ichain, int channel, int orbit, int bunchid, int tdc, int tot, std::array<int, 6>& digitInfo) +void Decoder::fromRawHit2Digit(int icrate, int itrm, int itdc, int ichain, int channel, uint32_t orbit, uint16_t bunchid, int tdc, int tot, Decoder::DigitInfo& dinfo) { // convert raw info in digit info (channel, tdc, tot, bc) // tdc = packetHit.time + (frameHeader.frameID << 13) int echannel = Geo::getECHFromIndexes(icrate, itrm, ichain, itdc, channel); - digitInfo[0] = Geo::getCHFromECH(echannel); - digitInfo[2] = tot; - - digitInfo[3] = int(orbit * o2::tof::Geo::BC_IN_ORBIT); - digitInfo[3] += bunchid; - digitInfo[3] += tdc / 1024; - digitInfo[1] = tdc % 1024; - - digitInfo[4] = orbit; - digitInfo[5] = bunchid; + dinfo.channel = Geo::getCHFromECH(echannel); + dinfo.tot = tot; + dinfo.bcAbs = uint64_t(orbit) * o2::tof::Geo::BC_IN_ORBIT + bunchid + tdc / 1024; + dinfo.tdc = tdc % 1024; + dinfo.orbit = orbit; + dinfo.bc = bunchid; } char* Decoder::nextPage(void* current, int shift) @@ -223,8 +233,8 @@ char* Decoder::nextPage(void* current, int shift) bool Decoder::decode() // return a vector of digits in a TOF readout window { mReadoutWindowCurrent = 0; - mFirstOrbit = 0; - mFirstBunch = 0; + mFirstIR.orbit = 0; + mFirstIR.bc = 0; #ifdef VERBOSE if (mVerbose) @@ -233,20 +243,21 @@ bool Decoder::decode() // return a vector of digits in a TOF readout window auto start = std::chrono::high_resolution_clock::now(); // start from the beginning of the timeframe - mEventTime = 0; // loop over CRUs for (int icru = 0; icru < NCRU; icru++) { - if (!mCruIn[icru]) + if (!mCruIn[icru]) { continue; // no data stream available for this cru + } printf("decoding cru %d\n", icru); while (mUnion[icru] < mUnionEnd[icru]) { // read all the buffer // read open RDH mRDH = reinterpret_cast<o2::header::RAWDataHeader*>(mUnion[icru]); - if (mVerbose) + if (mVerbose) { printRDH(); + } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // note that RDH continue is not yet considered as option (to be added) @@ -258,21 +269,24 @@ bool Decoder::decode() // return a vector of digits in a TOF readout window mUnion[icru] = reinterpret_cast<Union_t*>(shift + rdhsz); mIntegratedBytes[icru] += rdhsz; - if (mUnion[icru] >= mUnionEnd[icru]) + if (mUnion[icru] >= mUnionEnd[icru]) { continue; // end of data stream reac + } for (int window = 0; window < Geo::NWINDOW_IN_ORBIT; window++) { // read Crate Header int bunchid = mUnion[icru]->crateHeader.bunchID; int icrate = mUnion[icru]->crateHeader.drmID; - if (mVerbose) + if (mVerbose) { printCrateInfo(icru); + } mUnion[icru]++; mIntegratedBytes[icru] += 4; //read Orbit int orbit = mUnion[icru]->crateOrbit.orbitID; - if (mVerbose) - printf("orbit ID = %d\n", orbit); + if (mVerbose) { + printf("%d) orbit ID = %d -- bunch ID = %d\n", icrate, orbit, bunchid); + } mUnion[icru]++; mIntegratedBytes[icru] += 4; @@ -281,8 +295,9 @@ bool Decoder::decode() // return a vector of digits in a TOF readout window } // read Crate Trailer - if (mVerbose) + if (mVerbose) { printCrateTrailerInfo(icru); + } auto ndw = mUnion[icru]->crateTrailer.numberOfDiagnostics; mUnion[icru]++; mIntegratedBytes[icru] += 4; @@ -296,8 +311,9 @@ bool Decoder::decode() // return a vector of digits in a TOF readout window // read close RDH mRDH = reinterpret_cast<o2::header::RAWDataHeader*>(nextPage(mRDH, RDHUtils::getMemorySize(*mRDH))); - if (mVerbose) + if (mVerbose) { printRDH(); + } mIntegratedBytes[icru] += RDHUtils::getHeaderSize(*mRDH); // go to next page @@ -316,7 +332,6 @@ void Decoder::FillWindows() { std::vector<Digit> digTemp; flushOutputContainer(digTemp); - printf("hit decoded = %d (digits not filled = %lu)\n", mHitDecoded, mFutureDigits.size()); } void Decoder::printCrateInfo(int icru) const diff --git a/Detectors/TOF/reconstruction/src/DecoderBase.cxx b/Detectors/TOF/reconstruction/src/DecoderBase.cxx index 5e80b7af217fe..8c7fcb4a299fe 100644 --- a/Detectors/TOF/reconstruction/src/DecoderBase.cxx +++ b/Detectors/TOF/reconstruction/src/DecoderBase.cxx @@ -90,8 +90,9 @@ bool DecoderBaseT<RDH>::processHBF() rdh = reinterpret_cast<const RDH*>(reinterpret_cast<const char*>(rdh) + offsetToNext); /** check next RDH is within buffer **/ - if (reinterpret_cast<const char*>(rdh) < mDecoderBuffer + mDecoderBufferSize) + if (reinterpret_cast<const char*>(rdh) < mDecoderBuffer + mDecoderBufferSize) { continue; + } /** otherwise return **/ return true; @@ -111,8 +112,9 @@ bool DecoderBaseT<RDH>::processHBF() mDecoderPointer = reinterpret_cast<const uint32_t*>(mDecoderSaveBuffer); mDecoderPointerMax = reinterpret_cast<const uint32_t*>(mDecoderSaveBuffer + mDecoderSaveBufferDataSize); while (mDecoderPointer < mDecoderPointerMax) { - if (processDRM()) + if (processDRM()) { break; + } } mDecoderSaveBufferDataSize = 0; @@ -132,8 +134,9 @@ bool DecoderBaseT<RDH>::processHBF() mDecoderPointer = reinterpret_cast<const uint32_t*>(reinterpret_cast<const char*>(rdh) + RDHUtils::getOffsetToNext(*rdh)); /** check next RDH is within buffer **/ - if (reinterpret_cast<const char*>(mDecoderPointer) < mDecoderBuffer + mDecoderBufferSize) + if (reinterpret_cast<const char*>(mDecoderPointer) < mDecoderBuffer + mDecoderBufferSize) { return false; + } /** otherwise return **/ return true; @@ -188,7 +191,7 @@ bool DecoderBaseT<RDH>::processDRM() auto crateTrailer = reinterpret_cast<const CrateTrailer_t*>(mDecoderPointer); #ifdef DECODER_VERBOSE if (mDecoderVerbose) { - printf(" %08x CrateTrailer (numberOfDiagnostics=%d, numberOfErrors=%d) \n ", *mDecoderPointer, crateTrailer->numberOfDiagnostics, numberOfErrors); + printf(" %08x CrateTrailer (numberOfDiagnostics=%d, numberOfErrors=%d) \n ", *mDecoderPointer, crateTrailer->numberOfDiagnostics, crateTrailer->numberOfErrors); } #endif mDecoderPointer++; diff --git a/Detectors/TOF/reconstruction/src/Encoder.cxx b/Detectors/TOF/reconstruction/src/Encoder.cxx index 1747315a36ef3..41f0146bc97b5 100644 --- a/Detectors/TOF/reconstruction/src/Encoder.cxx +++ b/Detectors/TOF/reconstruction/src/Encoder.cxx @@ -119,8 +119,9 @@ bool Encoder::close() bool Encoder::alloc(long size) { - if (size < 500000) + if (size < 500000) { size = 500000; + } mSize = size; @@ -143,10 +144,12 @@ void Encoder::encodeTRM(const std::vector<Digit>& summary, Int_t icrate, Int_t i // return next TRM index (-1 if not in the same crate) // start to convert digiti from istart --> then update istart to the starting position of the new TRM { - if (mVerbose) + if (mVerbose) { printf("Crate %d: encode TRM %d \n", icrate, itrm); + } // TRM HEADER + Union_t* trmheader = mUnion[icrate]; mUnion[icrate]->trmDataHeader.slotId = itrm; mUnion[icrate]->trmDataHeader.eventWords = 0; // to be filled at the end mUnion[icrate]->trmDataHeader.eventCnt = mEventCounter; @@ -166,20 +169,23 @@ void Encoder::encodeTRM(const std::vector<Digit>& summary, Int_t icrate, Int_t i while (istart < summary.size()) { // fill hits /** loop over hits **/ int whatChain = summary[istart].getElChainIndex(); - if (whatChain != ichain) + if (whatChain != ichain) { break; + } int whatTRM = summary[istart].getElTRMIndex(); - if (whatTRM != itrm) + if (whatTRM != itrm) { break; + } int whatCrate = summary[istart].getElCrateIndex(); - if (whatCrate != icrate) + if (whatCrate != icrate) { break; + } int hittimeTDC = (summary[istart].getBC() - mEventCounter * Geo::BC_IN_WINDOW) * 1024 + summary[istart].getTDC(); // time in TDC bin within the TOF WINDOW if (hittimeTDC < 0) { LOG(ERROR) << "Negative hit encoded " << hittimeTDC << ", something went wrong in filling readout window"; - printf("%d %d %d\n", summary[istart].getBC(), mEventCounter * Geo::BC_IN_WINDOW, summary[istart].getTDC()); + printf("%llu %d %d\n", (unsigned long long)summary[istart].getBC(), mEventCounter * Geo::BC_IN_WINDOW, summary[istart].getTDC()); } // leading time mUnion[icrate]->trmDataHit.time = hittimeTDC; @@ -206,6 +212,11 @@ void Encoder::encodeTRM(const std::vector<Digit>& summary, Int_t icrate, Int_t i nextWord(icrate); } + // set TRM data size + int neventwords = getSize(trmheader, mUnion[icrate]) / 4 + 1; + neventwords -= neventwords / 4 * 2; + trmheader->trmDataHeader.eventWords = neventwords; + // TRM TRAILER mUnion[icrate]->trmDataTrailer.trailerMark = 3; mUnion[icrate]->trmDataTrailer.eventCRC = 0; // to be implemented @@ -239,16 +250,18 @@ bool Encoder::encode(std::vector<std::vector<o2::tof::Digit>> digitWindow, int t } #ifdef VERBOSE - if (mVerbose) + if (mVerbose) { std::cout << "-------- START ENCODE EVENT ----------------------------------------" << std::endl; + } #endif auto start = std::chrono::high_resolution_clock::now(); mEventCounter = tofwindow; // tof window index mIR.orbit = mEventCounter / Geo::NWINDOW_IN_ORBIT; - for (int i = 0; i < 72; i++) + for (int i = 0; i < 72; i++) { mNextWordStatus[i] = false; + } int bcFirstWin; @@ -260,8 +273,9 @@ bool Encoder::encode(std::vector<std::vector<o2::tof::Digit>> digitWindow, int t mIR.bc = ((mEventCounter % Geo::NWINDOW_IN_ORBIT) * Geo::BC_IN_ORBIT) / Geo::NWINDOW_IN_ORBIT + mFirstBC; // bunch crossing in the current orbit at the beginning of the window. - if (iwin == 0) + if (iwin == 0) { bcFirstWin = mIR.bc; + } int icurrentdigit = 0; // TOF data header @@ -330,12 +344,14 @@ bool Encoder::encode(std::vector<std::vector<o2::tof::Digit>> digitWindow, int t mUnion[i]->drmDataTrailer.locEvCnt = mEventCounter; mUnion[i]->drmDataTrailer.mbz = 0; mUnion[i]->drmDataTrailer.dataId = 5; + int neventwords = getSize(mDRMDataHeader[i], mUnion[i]) / 4 + 1; + neventwords -= neventwords / 4 * 2 + 6; + mDRMDataHeader[i]->eventWords = neventwords; nextWord(i); mUnion[i]->data = 0x70000000; nextWord(i); mTOFDataHeader[i]->bytePayload = getSize(mTOFDataHeader[i], mUnion[i]); - mDRMDataHeader[i]->eventWords = mTOFDataHeader[i]->bytePayload / 4; } // check that all digits were used @@ -346,8 +362,9 @@ bool Encoder::encode(std::vector<std::vector<o2::tof::Digit>> digitWindow, int t mIR.bc = bcFirstWin; - for (int i = 0; i < 72; i++) + for (int i = 0; i < 72; i++) { flush(i); + } mStartRun = false; diff --git a/Detectors/TOF/reconstruction/src/TOFReconstructionLinkDef.h b/Detectors/TOF/reconstruction/src/TOFReconstructionLinkDef.h index e996ffbdd4b9d..a8cd7a6c2c067 100644 --- a/Detectors/TOF/reconstruction/src/TOFReconstructionLinkDef.h +++ b/Detectors/TOF/reconstruction/src/TOFReconstructionLinkDef.h @@ -20,5 +20,6 @@ #pragma link C++ class o2::tof::ClustererTask + ; #pragma link C++ class o2::tof::raw::Encoder + ; #pragma link C++ class o2::tof::compressed::Decoder + ; +#pragma link C++ class o2::tof::CTFCoder + ; #endif diff --git a/Detectors/TOF/simulation/CMakeLists.txt b/Detectors/TOF/simulation/CMakeLists.txt index 2eb23925337d1..0b02ad8df9897 100644 --- a/Detectors/TOF/simulation/CMakeLists.txt +++ b/Detectors/TOF/simulation/CMakeLists.txt @@ -9,7 +9,7 @@ # submit itself to any jurisdiction. o2_add_library(TOFSimulation - SOURCES src/Detector.cxx src/Digitizer.cxx src/DigitizerTask.cxx + SOURCES src/Detector.cxx src/Digitizer.cxx src/DigitizerTask.cxx src/TOFSimParams.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::TOFBase O2::SimulationDataFormat O2::TOFCalibration) @@ -17,7 +17,8 @@ o2_add_library(TOFSimulation o2_target_root_dictionary(TOFSimulation HEADERS include/TOFSimulation/Detector.h include/TOFSimulation/Digitizer.h - include/TOFSimulation/DigitizerTask.h) + include/TOFSimulation/DigitizerTask.h + include/TOFSimulation/TOFSimParams.h) o2_add_executable(digi2raw COMPONENT_NAME tof diff --git a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h index 1dd93d9ee2090..453475bd775ee 100644 --- a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h +++ b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h @@ -114,7 +114,7 @@ class Digitizer : public WindowFiller CalibApi* mCalibApi = nullptr; //! calib api to handle the TOF calibration - void fillDigitsInStrip(std::vector<Strip>* strips, o2::dataformats::MCTruthContainer<o2::tof::MCLabel>* mcTruthContainer, int channel, int tdc, int tot, int nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID); + void fillDigitsInStrip(std::vector<Strip>* strips, o2::dataformats::MCTruthContainer<o2::tof::MCLabel>* mcTruthContainer, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID); Int_t processHit(const HitType& hit, Double_t event_time); void addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, Float_t z, Float_t charge, Int_t iX, Int_t iZ, Int_t padZfired, diff --git a/Detectors/TOF/simulation/include/TOFSimulation/TOFSimParams.h b/Detectors/TOF/simulation/include/TOFSimulation/TOFSimParams.h new file mode 100644 index 0000000000000..9103b3a341f32 --- /dev/null +++ b/Detectors/TOF/simulation/include/TOFSimulation/TOFSimParams.h @@ -0,0 +1,41 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CONF_TOFDIGIPARAMS_H_ +#define O2_CONF_TOFDIGIPARAMS_H_ + +// Global parameters for TOF simulation / digitization + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace tof +{ + +// Global parameters for TOF simulation / digitization +struct TOFSimParams : public o2::conf::ConfigurableParamHelper<TOFSimParams> { + + int time_resolution = 60; // TOF resolution in ps + + // efficiency parameters + float eff_center = 0.995; // efficiency in the center of the fired pad + float eff_boundary1 = 0.94; // efficiency in mBound2 + float eff_boundary2 = 0.833; // efficiency in the pad border + float eff_boundary3 = 0.1; // efficiency in mBound3 + + O2ParamDef(TOFSimParams, "TOFSimParams"); +}; + +} // namespace tof +} // namespace o2 + +#endif diff --git a/Detectors/TOF/simulation/src/Detector.cxx b/Detectors/TOF/simulation/src/Detector.cxx index f93f64bc72058..5001fda818ef6 100644 --- a/Detectors/TOF/simulation/src/Detector.cxx +++ b/Detectors/TOF/simulation/src/Detector.cxx @@ -29,8 +29,9 @@ ClassImp(Detector); Detector::Detector(Bool_t active) : o2::base::DetImpl<Detector>("TOF", active), mEventNr(0), mTOFHoles(kTRUE), mHits(o2::utils::createSimVector<HitType>()) { - for (Int_t i = 0; i < Geo::NSECTORS; i++) + for (Int_t i = 0; i < Geo::NSECTORS; i++) { mTOFSectors[i] = 1; + } } Detector::Detector(const Detector& rhs) @@ -39,8 +40,9 @@ Detector::Detector(const Detector& rhs) mTOFHoles(rhs.mTOFHoles), mHits(o2::utils::createSimVector<HitType>()) { - for (Int_t i = 0; i < Geo::NSECTORS; i++) + for (Int_t i = 0; i < Geo::NSECTORS; i++) { mTOFSectors[i] = rhs.mTOFSectors[i]; + } } Detector::~Detector() @@ -51,9 +53,9 @@ Detector::~Detector() void Detector::InitializeO2Detector() { TGeoVolume* v = gGeoManager->GetVolume("FPAD"); - if (v == nullptr) + if (v == nullptr) { printf("Sensitive volume FSEN not found!!!!!!!!"); - else { + } else { AddSensitiveVolume(v); } } @@ -72,8 +74,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) LOG(DEBUG) << "Process hit in TOF volume ar R=" << radius << " - Z=" << pos2z; Float_t enDep = fMC->Edep(); - if (enDep < 1E-8) + if (enDep < 1E-8) { return kFALSE; // wo se need a threshold? + } // ADD HIT float posx, posy, posz; @@ -193,14 +196,17 @@ void Detector::CreateMaterials() Float_t asc[4] = {12.011, 1.00794, 26.981539, 63.546}; Float_t zsc[4] = {6., 1., 13., 29.}; Float_t wsc[4]; - for (Int_t ii = 0; ii < 4; ii++) + for (Int_t ii = 0; ii < 4; ii++) { wsc[ii] = 0.; + } Float_t wDummy[4], nDummy[4]; - for (Int_t ii = 0; ii < 4; ii++) + for (Int_t ii = 0; ii < 4; ii++) { wDummy[ii] = 0.; - for (Int_t ii = 0; ii < 4; ii++) + } + for (Int_t ii = 0; ii < 4; ii++) { nDummy[ii] = 0.; + } nDummy[0] = 1.; nDummy[1] = 2.; MaterialMixer(wDummy, asc, nDummy, 2); @@ -344,8 +350,9 @@ void Detector::createModules(Float_t xtof, Float_t ytof, Float_t zlenA, Float_t // Int_t idrotm[8]; - for (Int_t ii = 0; ii < 8; ii++) + for (Int_t ii = 0; ii < 8; ii++) { idrotm[ii] = 0; + } // Definition of the of fibre glass modules (FTOA, FTOB and FTOC) Float_t par[3]; @@ -748,23 +755,26 @@ void Detector::makeStripsInModules(Float_t ytof, Float_t zlenA) const Int_t maxStripNumbers[5] = {Geo::NSTRIPC, Geo::NSTRIPB, Geo::NSTRIPA, Geo::NSTRIPB, Geo::NSTRIPC}; Int_t idrotm[Geo::NSTRIPXSECTOR]; - for (Int_t ii = 0; ii < Geo::NSTRIPXSECTOR; ii++) + for (Int_t ii = 0; ii < Geo::NSTRIPXSECTOR; ii++) { idrotm[ii] = 0; + } Int_t totalStrip = 0; Float_t xpos, zpos, ypos, ang; for (Int_t iplate = 0; iplate < Geo::NPLATES; iplate++) { - if (iplate > 0) + if (iplate > 0) { totalStrip += maxStripNumbers[iplate - 1]; + } for (Int_t istrip = 0; istrip < maxStripNumbers[iplate]; istrip++) { ang = Geo::getAngles(iplate, istrip); - if (ang > 0.) + if (ang > 0.) { Matrix(idrotm[istrip + totalStrip], 90., 0., 90. + ang, 90., ang, 90.); - else if (ang == 0.) + } else if (ang == 0.) { Matrix(idrotm[istrip + totalStrip], 90., 0., 90., 90., 0., 0.); - else if (ang < 0.) + } else if (ang < 0.) { Matrix(idrotm[istrip + totalStrip], 90., 0., 90. + ang, 90., -ang, 270.); + } xpos = 0.; ypos = Geo::getHeights(iplate, istrip) + yFLT * 0.5; @@ -773,16 +783,18 @@ void Detector::makeStripsInModules(Float_t ytof, Float_t zlenA) const idrotm[istrip + totalStrip], "ONLY"); if (mTOFHoles) { - if (istrip + totalStrip + 1 > 53) + if (istrip + totalStrip + 1 > 53) { TVirtualMC::GetMC()->Gspos( "FSTR", istrip + totalStrip + 1, "FLTC", xpos, ypos, -zpos - (zlenA * 0.5 - 2. * Geo::MODULEWALLTHICKNESS + Geo::INTERCENTRMODBORDER1) * 0.5, idrotm[istrip + totalStrip], "ONLY"); - if (istrip + totalStrip + 1 < 39) + } + if (istrip + totalStrip + 1 < 39) { TVirtualMC::GetMC()->Gspos( "FSTR", istrip + totalStrip + 1, "FLTB", xpos, ypos, -zpos + (zlenA * 0.5 - 2. * Geo::MODULEWALLTHICKNESS + Geo::INTERCENTRMODBORDER1) * 0.5, idrotm[istrip + totalStrip], "ONLY"); + } } } } @@ -805,8 +817,9 @@ void Detector::createModuleCovers(Float_t xtof, Float_t zlenA) const par[1] = Geo::MODULECOVERTHICKNESS * 0.5; par[2] = zlenA * 0.5 + 2.; TVirtualMC::GetMC()->Gsvolu("FPEA", "BOX ", getMediumID(kAir), par, 3); // Air - if (mTOFHoles) + if (mTOFHoles) { TVirtualMC::GetMC()->Gsvolu("FPEB", "BOX ", getMediumID(kAir), par, 3); // Air + } constexpr Float_t ALCOVERTHICKNESS = 1.5; constexpr Float_t INTERFACECARDTHICKNESS = 0.16; @@ -818,15 +831,17 @@ void Detector::createModuleCovers(Float_t xtof, Float_t zlenA) const par[1] = ALCOVERTHICKNESS * 0.5; // par[2] = zlenA*0.5 + 2.; TVirtualMC::GetMC()->Gsvolu("FALT", "BOX ", getMediumID(kAlFrame), par, 3); // Al - if (mTOFHoles) + if (mTOFHoles) { TVirtualMC::GetMC()->Gsvolu("FALB", "BOX ", getMediumID(kAlFrame), par, 3); // Al + } Float_t xcoor, ycoor, zcoor; xcoor = 0.; ycoor = 0.; zcoor = 0.; TVirtualMC::GetMC()->Gspos("FALT", 0, "FPEA", xcoor, ycoor, zcoor, 0, "ONLY"); - if (mTOFHoles) + if (mTOFHoles) { TVirtualMC::GetMC()->Gspos("FALB", 0, "FPEB", xcoor, ycoor, zcoor, 0, "ONLY"); + } par[0] = xtof * 0.5; // par[1] = ALCOVERTHICKNESS*0.5; @@ -1001,8 +1016,9 @@ void Detector::createBackZone(Float_t xtof, Float_t ytof, Float_t zlenA) const par[1] = (ytof * 0.5 - Geo::MODULECOVERTHICKNESS) * 0.5; par[2] = zlenA * 0.5; TVirtualMC::GetMC()->Gsvolu("FAIA", "BOX ", getMediumID(kAir), par, 3); // Air - if (mTOFHoles) + if (mTOFHoles) { TVirtualMC::GetMC()->Gsvolu("FAIB", "BOX ", getMediumID(kAir), par, 3); // Air + } TVirtualMC::GetMC()->Gsvolu("FAIC", "BOX ", getMediumID(kAir), par, 3); // Air Float_t feaParam[3] = {Geo::FEAPARAMETERS[0], Geo::FEAPARAMETERS[1], Geo::FEAPARAMETERS[2]}; @@ -1676,8 +1692,9 @@ void Detector::makeReadoutCrates(Float_t ytof) const // Int_t idrotm[Geo::NSECTORS]; - for (Int_t ii = 0; ii < Geo::NSECTORS; ii++) + for (Int_t ii = 0; ii < Geo::NSECTORS; ii++) { idrotm[ii] = 0; + } // volume definition Float_t serpar[3] = {29. * 0.5, 121. * 0.5, 90. * 0.5}; @@ -1719,8 +1736,9 @@ void Detector::makeModulesInBTOFvolumes(Float_t ytof, Float_t zlenA) const // Positioning of fibre glass modules (FTOA, FTOB and FTOC) for (Int_t isec = 0; isec < Geo::NSECTORS; isec++) { - if (mTOFSectors[isec] == -1) + if (mTOFSectors[isec] == -1) { continue; + } char name[SIZESTR]; snprintf(name, SIZESTR, "BTOF%d", isec); @@ -1773,13 +1791,15 @@ void Detector::makeCoversInBTOFvolumes() const // Positioning of module covers (FPEA, FPEB) for (Int_t isec = 0; isec < Geo::NSECTORS; isec++) { - if (mTOFSectors[isec] == -1) + if (mTOFSectors[isec] == -1) { continue; + } snprintf(name, SIZESTR, "BTOF%d", isec); - if (mTOFHoles && (isec == 13 || isec == 14 || isec == 15)) + if (mTOFHoles && (isec == 13 || isec == 14 || isec == 15)) { TVirtualMC::GetMC()->Gspos("FPEB", 0, name, xcoor, ycoor, zcoor, idrotm[0], "ONLY"); - else + } else { TVirtualMC::GetMC()->Gspos("FPEA", 0, name, xcoor, ycoor, zcoor, idrotm[0], "ONLY"); + } } } @@ -1809,16 +1829,18 @@ void Detector::makeBackInBTOFvolumes(Float_t ytof) const // Positioning of FEA cards and services containers (FAIA, FAIC and FAIB) for (Int_t isec = 0; isec < Geo::NSECTORS; isec++) { - if (mTOFSectors[isec] == -1) + if (mTOFSectors[isec] == -1) { continue; + } snprintf(name, SIZESTR, "BTOF%d", isec); - if (Geo::FEAWITHMASKS[isec]) + if (Geo::FEAWITHMASKS[isec]) { TVirtualMC::GetMC()->Gspos("FAIA", 0, name, xcoor, ycoor, zcoor, idrotm[0], "ONLY"); - else { - if (mTOFHoles && (isec == 13 || isec == 14 || isec == 15)) + } else { + if (mTOFHoles && (isec == 13 || isec == 14 || isec == 15)) { TVirtualMC::GetMC()->Gspos("FAIB", 0, name, xcoor, ycoor, zcoor, idrotm[0], "ONLY"); - else + } else { TVirtualMC::GetMC()->Gspos("FAIC", 0, name, xcoor, ycoor, zcoor, idrotm[0], "ONLY"); + } } } } @@ -1859,8 +1881,9 @@ void Detector::addAlignableVolumes() const modUID = o2::base::GeometryManager::getSensID(idTOF, modnum++); LOG(DEBUG) << "modUID: " << modUID; - if (mTOFSectors[isect] == -1) + if (mTOFSectors[isect] == -1) { continue; + } if (mTOFHoles && (isect == 13 || isect == 14 || isect == 15)) { if (istr < 39) { @@ -1869,8 +1892,9 @@ void Detector::addAlignableVolumes() const } else if (istr > 53) { vpL3 = "/FTOC_0"; vpL4 = "/FLTC_0/FSTR_"; - } else + } else { continue; + } } else { vpL3 = "/FTOA_0"; vpL4 = "/FLTA_0/FSTR_"; diff --git a/Detectors/TOF/simulation/src/Digitizer.cxx b/Detectors/TOF/simulation/src/Digitizer.cxx index 34f1c58c8d08a..d855866604c2f 100644 --- a/Detectors/TOF/simulation/src/Digitizer.cxx +++ b/Detectors/TOF/simulation/src/Digitizer.cxx @@ -10,6 +10,7 @@ #include "TOFSimulation/Digitizer.h" #include "DetectorsBase/GeometryManager.h" +#include "TOFSimulation/TOFSimParams.h" #include "TCanvas.h" #include "TFile.h" @@ -88,9 +89,9 @@ int Digitizer::process(const std::vector<HitType>* hits, std::vector<Digit>* dig // hits array of TOF hits for a given simulated event // digits passed from external to be filled, in continuous readout mode we will push it on mDigitsPerTimeFrame vector of vectors of digits - // printf("process event time = %f with %ld hits\n",mEventTime,hits->size()); + // printf("process event time = %f with %ld hits\n",mEventTime.getTimeNS(),hits->size()); - Int_t readoutwindow = Int_t((mEventTime - Geo::BC_TIME * (Geo::OVERLAP_IN_BC + 2)) * Geo::READOUTWINDOW_INV); // event time shifted by 2 BC as safe margin before to change current readout window to account for decalibration + Int_t readoutwindow = Int_t((mEventTime.getTimeNS() - Geo::BC_TIME * (Geo::OVERLAP_IN_BC + 2)) * Geo::READOUTWINDOW_INV); // event time shifted by 2 BC as safe margin before to change current readout window to account for decalibration if (mContinuous && readoutwindow > mReadoutWindowCurrent) { // if we are moving in future readout windows flush previous ones (only for continuous readout mode) digits->clear(); @@ -104,7 +105,7 @@ int Digitizer::process(const std::vector<HitType>* hits, std::vector<Digit>* dig for (auto& hit : *hits) { //TODO: put readout window counting/selection - processHit(hit, mEventTime); + processHit(hit, mEventTime.getTimeOffsetWrtBC()); } // end loop over hits if (!mContinuous) { // fill output container per event @@ -130,8 +131,9 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) detIndOtherPad[2] = detInd[2]; // same sector, plate, strip Int_t otherraw = 0; - if (detInd[3] == 0) + if (detInd[3] == 0) { otherraw = 1; + } Int_t iZshift = otherraw ? 1 : -1; @@ -167,10 +169,11 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) detIndOtherPad[4] = detInd[4]; channel = Geo::getIndex(detIndOtherPad); xLocal = deltapos[0]; // recompute local coordinates - if (otherraw) + if (otherraw) { zLocal = deltapos[2] - Geo::ZPAD; // recompute local coordinates - else + } else { zLocal = deltapos[2] + Geo::ZPAD; + } if (isFired(xLocal, zLocal, charge)) { ndigits++; addDigit(channel, istrip, time, xLocal, zLocal, charge, 0, iZshift, detInd[3], trackID); @@ -208,10 +211,11 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) if (detIndOtherPad[4] >= 0) { channel = Geo::getIndex(detIndOtherPad); xLocal = deltapos[0] + Geo::XPAD; // recompute local coordinates - if (otherraw) + if (otherraw) { zLocal = deltapos[2] - Geo::ZPAD; // recompute local coordinates - else + } else { zLocal = deltapos[2] + Geo::ZPAD; + } if (isFired(xLocal, zLocal, charge)) { ndigits++; addDigit(channel, istrip, time, xLocal, zLocal, charge, -1, iZshift, detInd[3], trackID); @@ -224,10 +228,11 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) if (detIndOtherPad[4] < Geo::NPADX) { channel = Geo::getIndex(detIndOtherPad); xLocal = deltapos[0] - Geo::XPAD; // recompute local coordinates - if (otherraw) + if (otherraw) { zLocal = deltapos[2] - Geo::ZPAD; // recompute local coordinates - else + } else { zLocal = deltapos[2] + Geo::ZPAD; + } if (isFired(xLocal, zLocal, charge)) { ndigits++; addDigit(channel, istrip, time, xLocal, zLocal, charge, 1, iZshift, detInd[3], trackID); @@ -245,7 +250,12 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, time = getDigitTimeSmeared(time, x, z, charge); // add time smearing charge *= getFractionOfCharge(x, z); - Float_t tot = 12; // time-over-threshold + + // tot tuned to reproduce 0.8% of orphans tot(=0) + Float_t tot = gRandom->Gaus(12., 1.5); // time-over-threshold + if (tot < 8.4) { + tot = 0; + } Float_t xborder = Geo::XPAD * 0.5 - TMath::Abs(x); Float_t zborder = Geo::ZPAD * 0.5 - TMath::Abs(z); @@ -263,27 +273,26 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, // if(border > 0) printf("deltat =%f\n",mTimeDelay*border*border*border); // else printf("deltat=0\n"); // getchar(); - if (border > 0) + if (border > 0) { time += mTimeDelay * border * border * border; + } } time += TMath::Sqrt(timewalkX * timewalkX + timewalkZ * timewalkZ) - mTimeDelayCorr - mTimeWalkeSlope * 2; // Decalibrate time -= mCalibApi->getTimeDecalibration(channel, tot); //TODO: to be checked that "-" is correct, and we did not need "+" instead :-) - Int_t nbc = Int_t(time * Geo::BC_TIME_INPS_INV); // time elapsed in number of bunch crossing + // let's move from time to bc, tdc + + uint64_t nbc = (uint64_t)(time * Geo::BC_TIME_INPS_INV); // time elapsed in number of bunch crossing //Digit newdigit(time, channel, (time - Geo::BC_TIME_INPS * nbc) * Geo::NTDCBIN_PER_PS, tot * Geo::NTOTBIN_PER_NS, nbc); int tdc = int((time - Geo::BC_TIME_INPS * nbc) * Geo::NTDCBIN_PER_PS); - // additional check to avoid very rare truncation - if (tdc < 0) { - nbc--; - tdc += 1024; - } else if (tdc >= 1024) { - nbc++; - tdc -= 1024; - } + // add orbit and bc + nbc += mEventTime.toLong(); + + // printf("orbit = %d -- bc = %d -- nbc = (%d) %d\n",mEventTime.orbit,mEventTime.bc, mEventTime.toLong(),nbc); // printf("tdc = %d\n",tdc); @@ -295,13 +304,11 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, if (mContinuous) { isnext = nbc / Geo::BC_IN_WINDOW - mReadoutWindowCurrent; - // isnext = Int_t(time * 1E-3 * Geo::READOUTWINDOW_INV) - mReadoutWindowCurrent; // to be replaced with uncalibrated time isIfOverlap = (nbc - Geo::OVERLAP_IN_BC) / Geo::BC_IN_WINDOW - mReadoutWindowCurrent; - // isIfOverlap = Int_t((time - Geo::BC_TIME_INPS * Geo::OVERLAP_IN_BC) * 1E-3 * Geo::READOUTWINDOW_INV) - mReadoutWindowCurrent; // to be replaced with uncalibrated time; - if (isnext == isIfOverlap) + if (isnext == isIfOverlap) { isIfOverlap = -1; - else if (isnext < 0 && isIfOverlap >= 0) { + } else if (isnext < 0 && isIfOverlap >= 0) { isnext = isIfOverlap; isIfOverlap = -1; } else if (isnext >= MAXWINDOWS && isIfOverlap < MAXWINDOWS) { @@ -311,7 +318,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, if (isnext < 0) { LOG(ERROR) << "error: isnext =" << isnext << "(current window = " << mReadoutWindowCurrent << ")" - << " nbc = " << nbc << " -- event time = " << mEventTime << "\n"; + << " nbc = " << nbc << " -- event time = " << mEventTime.getTimeNS() << "\n"; return; } @@ -336,8 +343,9 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, insertDigitInFuture(channel, tdc, tot * Geo::NTOTBIN_PER_NS, nbc, lblCurrent); } - if (isnext) + if (isnext) { iscurrent = false; + } } //printf("add TOF digit c=%i n=%i\n",iscurrent,isnext); @@ -353,12 +361,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, mcTruthContainer = mMCTruthContainerNext[isnext - 1]; } - int eventcounter = mReadoutWindowCurrent + isnext; - int hittimeTDC = (nbc - eventcounter * Geo::BC_IN_WINDOW) * 1024 + tdc; // time in TDC bin within the TOF WINDOW - if (hittimeTDC < 0) - LOG(ERROR) << "1) Negative hit " << hittimeTDC << ", something went wrong in filling readout window: isnext=" << isnext << ", isIfOverlap=" << isIfOverlap; - else - fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID); + fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID); if (isIfOverlap > -1 && isIfOverlap < MAXWINDOWS) { // fill also a second readout window because of the overlap if (!isIfOverlap) { @@ -369,16 +372,11 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, mcTruthContainer = mMCTruthContainerNext[isIfOverlap - 1]; } - int eventcounter = mReadoutWindowCurrent + isIfOverlap; - int hittimeTDC = (nbc - eventcounter * Geo::BC_IN_WINDOW) * 1024 + tdc; // time in TDC bin within the TOF WINDOW - if (hittimeTDC < 0) - LOG(ERROR) << "2) Negative hit " << hittimeTDC << ", something went wrong in filling readout window: isnext=" << isnext << ", isIfOverlap=" << isIfOverlap; - else - fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID); + fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID); } } //______________________________________________________________________ -void Digitizer::fillDigitsInStrip(std::vector<Strip>* strips, o2::dataformats::MCTruthContainer<o2::tof::MCLabel>* mcTruthContainer, int channel, int tdc, int tot, int nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID) +void Digitizer::fillDigitsInStrip(std::vector<Strip>* strips, o2::dataformats::MCTruthContainer<o2::tof::MCLabel>* mcTruthContainer, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID) { int lblCurrent; if (mcTruthContainer) { @@ -430,18 +428,21 @@ Float_t Digitizer::getCharge(Float_t eDep) //______________________________________________________________________ Bool_t Digitizer::isFired(Float_t x, Float_t z, Float_t charge) { - if (TMath::Abs(x) > Geo::XPAD * 0.5 + 0.3) + if (TMath::Abs(x) > Geo::XPAD * 0.5 + 0.3) { return kFALSE; - if (TMath::Abs(z) > Geo::ZPAD * 0.5 + 0.3) + } + if (TMath::Abs(z) > Geo::ZPAD * 0.5 + 0.3) { return kFALSE; + } Float_t effX = getEffX(x); Float_t effZ = getEffZ(z); Float_t efficiency = TMath::Min(effX, effZ); - if (gRandom->Rndm() > efficiency) + if (gRandom->Rndm() > efficiency) { return kFALSE; + } return kTRUE; } @@ -452,20 +453,22 @@ Float_t Digitizer::getEffX(Float_t x) Float_t xborder = Geo::XPAD * 0.5 - TMath::Abs(x); if (xborder > 0) { - if (xborder > mBound1) + if (xborder > mBound1) { return mEffCenter; - else if (xborder > mBound2) + } else if (xborder > mBound2) { return mEffBoundary1 + (mEffCenter - mEffBoundary1) * (xborder - mBound2) / (mBound1 - mBound2); - else + } else { return mEffBoundary2 + (mEffBoundary1 - mEffBoundary2) * xborder / mBound2; + } } else { xborder *= -1; - if (xborder > mBound4) + if (xborder > mBound4) { return 0; - else if (xborder > mBound3) + } else if (xborder > mBound3) { return mEffBoundary3 - mEffBoundary3 * (xborder - mBound3) / (mBound4 - mBound3); - else + } else { return mEffBoundary2 + (mEffBoundary3 - mEffBoundary2) * xborder / mBound3; + } } return 0; @@ -477,20 +480,22 @@ Float_t Digitizer::getEffZ(Float_t z) Float_t zborder = Geo::ZPAD * 0.5 - TMath::Abs(z); if (zborder > 0) { - if (zborder > mBound1) + if (zborder > mBound1) { return mEffCenter; - else if (zborder > mBound2) + } else if (zborder > mBound2) { return mEffBoundary1 + (mEffCenter - mEffBoundary1) * (zborder - mBound2) / (mBound1 - mBound2); - else + } else { return mEffBoundary2 + (mEffBoundary1 - mEffBoundary2) * zborder / mBound2; + } } else { zborder *= -1; - if (zborder > mBound4) + if (zborder > mBound4) { return 0; - else if (zborder > mBound3) + } else if (zborder > mBound3) { return mEffBoundary3 - mEffBoundary3 * (zborder - mBound3) / (mBound4 - mBound3); - else + } else { return mEffBoundary2 + (mEffBoundary3 - mEffBoundary2) * zborder / mBound3; + } } return 0; @@ -508,12 +513,12 @@ void Digitizer::initParameters() mBound4 = 0.9; // distance from border (not fired pad) when efficiency vanishes // resolution parameters - mTOFresolution = 60; // TOF global resolution in ps + mTOFresolution = TOFSimParams::Instance().time_resolution; // TOF global resolution in ps mShowerResolution = 50; // smearing correlated for all digits of the same hit - if (mTOFresolution > mShowerResolution) + if (mTOFresolution > mShowerResolution) { mDigitResolution = TMath::Sqrt(mTOFresolution * mTOFresolution - mShowerResolution * mShowerResolution); // independent smearing for each digit - else { + } else { mShowerResolution = mTOFresolution; mDigitResolution = 0; } @@ -529,21 +534,23 @@ void Digitizer::initParameters() } mTimeDelayCorr = mTimeDelay / 3.5; - if (mShowerResolution > mTimeDelayCorr) + if (mShowerResolution > mTimeDelayCorr) { mShowerResolution = TMath::Sqrt(mShowerResolution * mShowerResolution - mTimeDelayCorr * mTimeDelayCorr); - else + } else { mShowerResolution = 0; + } - if (mShowerResolution > mTimeWalkeSlope * 0.8) + if (mShowerResolution > mTimeWalkeSlope * 0.8) { mShowerResolution = TMath::Sqrt(mShowerResolution * mShowerResolution - mTimeWalkeSlope * mTimeWalkeSlope * 0.64); - else + } else { mShowerResolution = 0; + } // efficiency parameters - mEffCenter = 0.995; // efficiency in the center of the fired pad - mEffBoundary1 = 0.94; // efficiency in mBound2 - mEffBoundary2 = 0.833; // efficiency in the pad border - mEffBoundary3 = 0.1; // efficiency in mBound3 + mEffCenter = TOFSimParams::Instance().eff_center; // efficiency in the center of the fired pad + mEffBoundary1 = TOFSimParams::Instance().eff_boundary1; // efficiency in mBound2 + mEffBoundary2 = TOFSimParams::Instance().eff_boundary2; // efficiency in the pad border + mEffBoundary3 = TOFSimParams::Instance().eff_boundary3; // efficiency in mBound3 } //______________________________________________________________________ @@ -552,12 +559,15 @@ void Digitizer::printParameters() printf("Efficiency in the pad center = %f\n", mEffCenter); printf("Efficiency in the pad border = %f\n", mEffBoundary2); printf("Time resolution = %f ps (shower=%f, digit=%f)\n", mTOFresolution, mShowerResolution, mDigitResolution); - if (mTimeSlope > 0) + if (mTimeSlope > 0) { printf("Degration resolution for pad with signal induced = %f ps/cm x border distance\n", mTimeSlope); - if (mTimeDelay > 0) + } + if (mTimeDelay > 0) { printf("Time delay for pad with signal induced = %f ps\n", mTimeDelay); - if (mTimeWalkeSlope > 0) + } + if (mTimeWalkeSlope > 0) { printf("Time walk ON = %f ps/cm\n", mTimeWalkeSlope); + } } //______________________________________________________________________ @@ -665,17 +675,20 @@ void Digitizer::test(const char* geo) hit->SetEnergyLoss(0.0001); - Int_t ndigits = processHit(*hit, mEventTime); + Int_t ndigits = processHit(*hit, mEventTime.getTimeOffsetWrtBC()); h3->Fill(ndigits); hpadAll->Fill(xlocal, zlocal); for (Int_t k = 0; k < ndigits; k++) { - if (k == 0) + if (k == 0) { h->Fill(getTimeLastHit(k)); - if (k == 0) + } + if (k == 0) { h2->Fill(getTotLastHit(k)); - if (k == 0 && getXshift(k) == 0 && getZshift(k) == 0) + } + if (k == 0 && getXshift(k) == 0 && getZshift(k) == 0) { hTimeWalk->Fill(xlocal, zlocal * (0.5 - detCur[3]) * 2, getTimeLastHit(k)); + } hpad[getXshift(k) + 1][-getZshift(k) + 1]->Fill(getTimeLastHit(k)); hpadHit[getXshift(k) + 1][-getZshift(k) + 1]->Fill(xlocal, zlocal); @@ -714,8 +727,9 @@ void Digitizer::test(const char* geo) for (Int_t j = 0; j < 3; j++) { cpadH->cd(j * 3 + i + 1); hpadHit[i][j]->Draw("colz"); - if (j != 1) + if (j != 1) { hpadHit[i][j]->Scale(2); + } hpadEff[i][j]->Divide(hpadHit[i][j], hpadAll, 1, 1, "B"); hpadEff[i][j]->Draw("surf"); hpadEff[i][j]->SetMaximum(1); @@ -760,7 +774,7 @@ void Digitizer::testFromHits(const char* geo, const char* hits) hit->SetEnergyLoss(t->GetLeaf("o2root.TOF.TOFHit.mELoss")->GetValue(j)); - Int_t ndigits = processHit(*hit, mEventTime); + Int_t ndigits = processHit(*hit, mEventTime.getTimeOffsetWrtBC()); h3->Fill(ndigits); for (Int_t k = 0; k < ndigits; k++) { @@ -798,6 +812,9 @@ void Digitizer::fillOutputContainer(std::vector<Digit>& digits) int first = mDigitsPerTimeFrame.size(); int ne = digits.size(); ReadoutWindowData info(first, ne); + int orbit_shift = mReadoutWindowData.size() / 3; + int bc_shift = (mReadoutWindowData.size() % 3) * Geo::BC_IN_WINDOW; + info.setBCData(mFirstIR.orbit + orbit_shift, mFirstIR.bc + bc_shift); mDigitsPerTimeFrame.insert(mDigitsPerTimeFrame.end(), digits.begin(), digits.end()); mReadoutWindowData.push_back(info); } @@ -813,20 +830,23 @@ void Digitizer::fillOutputContainer(std::vector<Digit>& digits) } } - if (mContinuous) + if (mContinuous) { mMCTruthOutputContainerPerTimeFrame.push_back(*mMCTruthOutputContainer); + } mMCTruthContainerCurrent->clear(); // switch to next mStrip after flushing current readout window data mIcurrentReadoutWindow++; - if (mIcurrentReadoutWindow >= MAXWINDOWS) + if (mIcurrentReadoutWindow >= MAXWINDOWS) { mIcurrentReadoutWindow = 0; + } mStripsCurrent = &(mStrips[mIcurrentReadoutWindow]); mMCTruthContainerCurrent = &(mMCTruthContainer[mIcurrentReadoutWindow]); int k = mIcurrentReadoutWindow + 1; for (Int_t i = 0; i < MAXWINDOWS - 1; i++) { - if (k >= MAXWINDOWS) + if (k >= MAXWINDOWS) { k = 0; + } mMCTruthContainerNext[i] = &(mMCTruthContainer[k]); mStripsNext[i] = &(mStrips[k]); k++; @@ -837,10 +857,9 @@ void Digitizer::fillOutputContainer(std::vector<Digit>& digits) void Digitizer::flushOutputContainer(std::vector<Digit>& digits) { // flush all residual buffered data // TO be implemented - printf("flushOutputContainer\n"); - if (!mContinuous) + if (!mContinuous) { fillOutputContainer(digits); - else { + } else { for (Int_t i = 0; i < MAXWINDOWS; i++) { fillOutputContainer(digits); // fill all windows which are before (not yet stored) of the new current one checkIfReuseFutureDigits(); @@ -864,23 +883,23 @@ void Digitizer::flushOutputContainer(std::vector<Digit>& digits) //______________________________________________________________________ void Digitizer::checkIfReuseFutureDigits() { + uint64_t bclimit = 999999999999999999; + // check if digits stored very far in future match the new readout windows currently available int idigit = mFutureDigits.size() - 1; - int bclimit = 999999; // if bc is larger than this value stop the search in the next loop since bc are ordered in descending order - for (std::vector<Digit>::reverse_iterator digit = mFutureDigits.rbegin(); digit != mFutureDigits.rend(); ++digit) { - - if (digit->getBC() > bclimit) + if (digit->getBC() > bclimit) { break; + } double timestamp = digit->getBC() * Geo::BC_TIME + digit->getTDC() * Geo::TDCBIN * 1E-3; // in ns int isnext = Int_t(timestamp * Geo::READOUTWINDOW_INV) - (mReadoutWindowCurrent + 1); // to be replaced with uncalibrated time int isIfOverlap = Int_t((timestamp - Geo::BC_TIME_INPS * Geo::OVERLAP_IN_BC * 1E-3) * Geo::READOUTWINDOW_INV) - (mReadoutWindowCurrent + 1); // to be replaced with uncalibrated time; - if (isnext == isIfOverlap) + if (isnext == isIfOverlap) { isIfOverlap = -1; - else if (isnext < 0 && isIfOverlap >= 0) { + } else if (isnext < 0 && isIfOverlap >= 0) { isnext = isIfOverlap; isIfOverlap = -1; } else if (isnext >= MAXWINDOWS && isIfOverlap < MAXWINDOWS) { diff --git a/Detectors/TOF/simulation/src/DigitizerTask.cxx b/Detectors/TOF/simulation/src/DigitizerTask.cxx index 3f1463256bb2c..80470b4e6a548 100644 --- a/Detectors/TOF/simulation/src/DigitizerTask.cxx +++ b/Detectors/TOF/simulation/src/DigitizerTask.cxx @@ -95,12 +95,14 @@ void DigitizerTask::Exec(Option_t* option) void DigitizerTask::FinishTask() { // finalize digitization, if needed, flash remaining digits - if (!mContinuous) + if (!mContinuous) { return; + } FairRootManager* mgr = FairRootManager::Instance(); mgr->SetLastFill(kTRUE); /// necessary, otherwise the data is not written out - if (mDigitsArray) + if (mDigitsArray) { mDigitsArray->clear(); + } // TODO: reenable this mMCTruthArray->getIndexedSize(); diff --git a/Detectors/TOF/simulation/src/TOFSimParams.cxx b/Detectors/TOF/simulation/src/TOFSimParams.cxx new file mode 100644 index 0000000000000..b2fd20ceb4e97 --- /dev/null +++ b/Detectors/TOF/simulation/src/TOFSimParams.cxx @@ -0,0 +1,12 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TOFSimulation/TOFSimParams.h" +O2ParamImpl(o2::tof::TOFSimParams); diff --git a/Detectors/TOF/simulation/src/TOFSimulationLinkDef.h b/Detectors/TOF/simulation/src/TOFSimulationLinkDef.h index 9fc14b6a7c0a4..9e656b7eb03ff 100644 --- a/Detectors/TOF/simulation/src/TOFSimulationLinkDef.h +++ b/Detectors/TOF/simulation/src/TOFSimulationLinkDef.h @@ -21,4 +21,7 @@ #pragma link C++ class o2::tof::HitType + ; #pragma link C++ class vector < o2::tof::HitType> + ; +#pragma link C++ class o2::tof::TOFSimParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tof::TOFSimParams> + ; + #endif diff --git a/Detectors/TOF/workflow/CMakeLists.txt b/Detectors/TOF/workflow/CMakeLists.txt index 94d4cfcc7a34e..0c932834b9c4e 100644 --- a/Detectors/TOF/workflow/CMakeLists.txt +++ b/Detectors/TOF/workflow/CMakeLists.txt @@ -17,5 +17,22 @@ o2_add_library(TOFWorkflowUtils src/TOFRawWriterSpec.cxx src/CompressedDecodingTask.cxx src/CompressedInspectorTask.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::TOFBase O2::DataFormatsTOF - O2::TOFReconstruction) + src/CompressedAnalysisTask.cxx + src/EntropyEncoderSpec.cxx + src/EntropyDecoderSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::TOFBase O2::DataFormatsTOF O2::TOFReconstruction) + +o2_add_executable(entropy-encoder-workflow + SOURCES src/entropy-encoder-workflow.cxx + COMPONENT_NAME tof + PUBLIC_LINK_LIBRARIES O2::TOFWorkflowUtils) + +o2_add_executable(digit-writer-workflow + SOURCES src/digit-writer-commissioning.cxx + COMPONENT_NAME tof + PUBLIC_LINK_LIBRARIES O2::TOFWorkflowUtils) + +o2_add_executable(cluster-writer-workflow + SOURCES src/cluster-writer-commissioning.cxx + COMPONENT_NAME tof + PUBLIC_LINK_LIBRARIES O2::TOFWorkflowUtils) diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/DigitReaderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflow/DigitReaderSpec.h deleted file mode 100644 index 79fbd2f450452..0000000000000 --- a/Detectors/TOF/workflow/include/TOFWorkflow/DigitReaderSpec.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TOF_DIGITREADER -#define O2_TOF_DIGITREADER - -#include "TFile.h" - -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "TOFBase/Digit.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace tof -{ - -class DigitReader : public Task -{ - public: - DigitReader(bool useMC) : mUseMC(useMC) {} - ~DigitReader() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - int mState = 0; - bool mUseMC = true; - std::unique_ptr<TFile> mFile = nullptr; - std::vector<o2::tof::Digit> mDigits, *mPdigits = &mDigits; - std::vector<o2::tof::ReadoutWindowData> mRow, *mProw = &mRow; - std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>> mLabels, *mPlabels = &mLabels; -}; - -/// create a processor spec -/// read simulated TOF digits from a root file -framework::DataProcessorSpec getDigitReaderSpec(bool useMC); - -} // namespace tof -} // namespace o2 - -#endif /* O2_TOF_DIGITREADER */ diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/ClusterReaderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/ClusterReaderSpec.h similarity index 100% rename from Detectors/TOF/workflow/include/TOFWorkflow/ClusterReaderSpec.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/ClusterReaderSpec.h diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedAnalysis.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedAnalysis.h new file mode 100644 index 0000000000000..32d5a28f63ff9 --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedAnalysis.h @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CompressedAnalysisTask.h +/// @author Roberto Preghenella +/// @since 2020-09-04 +/// @brief TOF compressed data analysis base class + +#ifndef O2_TOF_COMPRESSEDANALYSIS +#define O2_TOF_COMPRESSEDANALYSIS + +#include "Headers/RAWDataHeader.h" +#include "DataFormatsTOF/CompressedDataFormat.h" +#include "TOFReconstruction/DecoderBase.h" + +using namespace o2::tof::compressed; + +namespace o2 +{ +namespace tof +{ + +class CompressedAnalysis : public DecoderBaseT<o2::header::RAWDataHeaderV6> +{ + + public: + CompressedAnalysis() = default; + ~CompressedAnalysis() override = default; + + virtual bool initialize() = 0; + virtual bool finalize() = 0; + + private: +}; + +} // namespace tof +} // namespace o2 + +#endif /* O2_TOF_COMPRESSEDANALYSIS */ diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedAnalysisTask.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedAnalysisTask.h new file mode 100644 index 0000000000000..367ba19b2aa43 --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedAnalysisTask.h @@ -0,0 +1,94 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CompressedAnalysisTask.h +/// @author Roberto Preghenella +/// @since 2020-09-04 +/// @brief TOF compressed data analysis task + +#ifndef O2_TOF_COMPRESSEDANALYSISTASK +#define O2_TOF_COMPRESSEDANALYSISTASK + +#include "Framework/Task.h" +#include "TOFWorkflowUtils/CompressedAnalysis.h" + +#include "TROOT.h" +#include "TSystem.h" +#include "TGlobal.h" +#include "TFunction.h" +#include <string> +#include <iostream> + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +class CompressedAnalysisTask : public Task +{ + public: + CompressedAnalysisTask() = default; + ~CompressedAnalysisTask() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + + private: + CompressedAnalysis* mAnalysis = nullptr; + bool mStatus = false; + + template <typename T> + T GetFromMacro(const std::string& file, const std::string& funcname, const std::string& type, const std::string& unique) + { + + /** tweak the string to get the required global function **/ + auto func = funcname; + if (func.empty()) { + auto size = file.size(); + auto firstindex = file.find_last_of("/") + 1; + auto lastindex = file.find_last_of("."); + func = file.substr(firstindex < size ? firstindex : 0, + lastindex < size ? lastindex - firstindex : size - firstindex) + + "()"; + } + auto gfunc = func.substr(0, func.find_first_of('(')); + + /** load macro is global function is not already defined **/ + if (!gROOT->GetGlobalFunction(gfunc.c_str())) { + if (gROOT->LoadMacro(file.c_str()) != 0) { + std::cout << "Cannot find " << file << std::endl; + return nullptr; + } + if (!gROOT->GetGlobalFunction(gfunc.c_str())) { + std::cout << "Global function '" << gfunc << "' not defined" << std::endl; + return nullptr; + } + } + + /** check the return type matches the required one **/ + if (strcmp(gROOT->GetGlobalFunction(gfunc.c_str())->GetReturnTypeName(), type.c_str())) { + std::cout << "Global function '" << gfunc << "' does not return a '" << type << "' type" << std::endl; + return nullptr; + } + + /** process function and retrieve pointer to the returned type **/ + gROOT->ProcessLine(Form("%s __%s__ = %s;", type.c_str(), unique.c_str(), func.c_str())); + auto ptr = (T*)gROOT->GetGlobal(Form("__%s__", unique.c_str()))->GetAddress(); + + /** success **/ + return *ptr; + } +}; + +} // namespace tof +} // namespace o2 + +#endif /* O2_TOF_COMPRESSEDANALYSISTASK */ diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/CompressedDecodingTask.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedDecodingTask.h similarity index 76% rename from Detectors/TOF/workflow/include/TOFWorkflow/CompressedDecodingTask.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedDecodingTask.h index 92e3a7c6c66cf..35b67d5224b78 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflow/CompressedDecodingTask.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedDecodingTask.h @@ -36,7 +36,12 @@ using namespace compressed; class CompressedDecodingTask : public DecoderBase, public Task { public: - CompressedDecodingTask() = default; + CompressedDecodingTask(bool conet = false) + { + mConetMode = conet; + setDecoderCONET(conet); + } + ~CompressedDecodingTask() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -46,8 +51,12 @@ class CompressedDecodingTask : public DecoderBase, public Task private: /** decoding handlers **/ void rdhHandler(const o2::header::RAWDataHeader* rdh) override; + void headerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit) override; void frameHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, const FrameHeader_t* frameHeader, const PackedHit_t* packedHits) override; + void trailerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, + const CrateTrailer_t* crateTrailer, const Diagnostic_t* diagnostics, + const Error_t* errors) override; o2::tof::compressed::Decoder mDecoder; std::vector<std::vector<o2::tof::Digit>> mDigits; @@ -55,11 +64,15 @@ class CompressedDecodingTask : public DecoderBase, public Task int mNCrateOpenTF = 0; int mNCrateCloseTF = 0; bool mHasToBePosted = false; - int mInitOrbit = 0; + bool mConetMode = false; + uint32_t mInitOrbit = 0; + bool mRowFilter = false; + bool mMaskNoise = false; + int mNoiseRate = 1000; TStopwatch mTimer; }; -framework::DataProcessorSpec getCompressedDecodingSpec(const std::string& inputDesc); +framework::DataProcessorSpec getCompressedDecodingSpec(const std::string& inputDesc, bool conet = false); } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/CompressedInspectorTask.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedInspectorTask.h similarity index 97% rename from Detectors/TOF/workflow/include/TOFWorkflow/CompressedInspectorTask.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedInspectorTask.h index 18da10a73bcb5..535bdd61f2d47 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflow/CompressedInspectorTask.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/CompressedInspectorTask.h @@ -46,6 +46,8 @@ class CompressedInspectorTask : public DecoderBaseT<RDH>, public Task private: /** decoding handlers **/ + void rdhHandler(const RDH* rdh) override{}; + void headerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit) override; void frameHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/DigitReaderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/DigitReaderSpec.h new file mode 100644 index 0000000000000..e4e3acca37947 --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/DigitReaderSpec.h @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TOF_DIGITREADER +#define O2_TOF_DIGITREADER + +#include "TFile.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TOFBase/Digit.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +class DigitReader : public Task +{ + public: + DigitReader(bool useMC) : mUseMC(useMC) {} + ~DigitReader() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + + private: + int mState = 0; + int mCurrentEntry = 0; + bool mUseMC = true; + std::unique_ptr<TFile> mFile = nullptr; + std::vector<o2::tof::Digit> mDigits, *mPdigits = &mDigits; + std::vector<o2::tof::ReadoutWindowData> mRow, *mProw = &mRow; + std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>> mLabels, *mPlabels = &mLabels; + std::vector<uint32_t> mPatterns, *mPpatterns = &mPatterns; +}; + +/// create a processor spec +/// read simulated TOF digits from a root file +framework::DataProcessorSpec getDigitReaderSpec(bool useMC); + +} // namespace tof +} // namespace o2 + +#endif /* O2_TOF_DIGITREADER */ diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h new file mode 100644 index 0000000000000..e699549ab2147 --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h @@ -0,0 +1,47 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.h +/// @brief Convert CTF (EncodedBlocks) to FT0 digit/channels strean + +#ifndef O2_TOF_ENTROPYDECODER_SPEC +#define O2_TOF_ENTROPYDECODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TOFReconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace tof +{ + +class EntropyDecoderSpec : public o2::framework::Task +{ + public: + EntropyDecoderSpec(); + ~EntropyDecoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::tof::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyDecoderSpec(); + +} // namespace tof +} // namespace o2 + +#endif diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h new file mode 100644 index 0000000000000..2a535abde1eb5 --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h @@ -0,0 +1,48 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.h +/// @brief Convert clusters streams to CTF (EncodedBlocks) + +#ifndef O2_TOF_ENTROPYENCODER_SPEC +#define O2_TOF_ENTROPYENCODER_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "TOFReconstruction/CTFCoder.h" +#include <TStopwatch.h> + +namespace o2 +{ +namespace tof +{ + +class EntropyEncoderSpec : public o2::framework::Task +{ + public: + EntropyEncoderSpec(); + ~EntropyEncoderSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::tof::CTFCoder mCTFCoder; + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getEntropyEncoderSpec(); + +} // namespace tof +} // namespace o2 + +#endif diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/TOFClusterWriterSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterWriterSpec.h similarity index 100% rename from Detectors/TOF/workflow/include/TOFWorkflow/TOFClusterWriterSpec.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterWriterSpec.h diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterWriterSplitterSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterWriterSplitterSpec.h new file mode 100644 index 0000000000000..eecb8c842fc70 --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterWriterSplitterSpec.h @@ -0,0 +1,132 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TOFCLUSTER_SPLITTER_WRITER_H +#define O2_TOFCLUSTER_SPLITTER_WRITER_H + +/// @file TOFClusterWriterSplitterSpec.h +/// @brief Device to write to tree the information for TOF time slewing calibration. + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DataFormatsTOF/Cluster.h" +#include "Framework/Logger.h" +#include <TTree.h> +#include <TFile.h> +#include <gsl/span> + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ +class TOFClusterWriterSplitter : public Task +{ + using OutputType = std::vector<o2::tof::Cluster>; + + std::string mBaseName; + + public: + TOFClusterWriterSplitter(int nTF) : mTFthr(nTF) {} + + void createAndOpenFileAndTree() + { + TString filename = TString::Format("%s_%06d.root", mBaseName.c_str(), mCount); + LOG(DEBUG) << "opening file " << filename.Data(); + mfileOut.reset(TFile::Open(TString::Format("%s", filename.Data()), "RECREATE")); + mOutputTree = std::make_unique<TTree>("o2sim", "Tree with TOF clusters"); + mOutputTree->Branch("TOFCluster", &mPClusters); + + mNTF = 0; + } + + void init(o2::framework::InitContext& ic) final + { + mBaseName = ic.options().get<std::string>("output-base-name"); + + mCount = 0; + createAndOpenFileAndTree(); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto clusters = pc.inputs().get<OutputType>("clusters"); + mPClusters = &clusters; + mOutputTree->Fill(); + + mNTF++; + + if (mNTF >= mTFthr) { + sendOutput(); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + mIsEndOfStream = true; + sendOutput(); + } + + private: + int mCount = 0; // how many times we filled the tree + int mNTF = 0; + int mTFthr = 1; + bool mIsEndOfStream = false; + OutputType mClusters; + const OutputType* mPClusters = &mClusters; + + std::unique_ptr<TTree> mOutputTree; ///< tree for the collected calib tof info + std::unique_ptr<TFile> mfileOut = nullptr; // file in which to write the output + + //________________________________________________________________ + void sendOutput() + { + // 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(); + mfileOut.reset(); + mCount++; + if (!mIsEndOfStream) { + createAndOpenFileAndTree(); + } + } +}; +} // namespace tof + +namespace framework +{ + +DataProcessorSpec getTOFClusterWriterSplitterSpec(int nTF) +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("clusters", o2::header::gDataOriginTOF, "CLUSTERS"); + + std::vector<OutputSpec> outputs; // empty + + return DataProcessorSpec{ + "tof-cluster-splitter-writer", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<o2::tof::TOFClusterWriterSplitter>(nTF)}, + Options{{"output-base-name", VariantType::String, "tofclusters", {"Name of the input file (root extension will be added)"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/TOFClusterizerSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterizerSpec.h similarity index 100% rename from Detectors/TOF/workflow/include/TOFWorkflow/TOFClusterizerSpec.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFClusterizerSpec.h diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/TOFDigitWriterSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFDigitWriterSpec.h similarity index 96% rename from Detectors/TOF/workflow/include/TOFWorkflow/TOFDigitWriterSpec.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFDigitWriterSpec.h index b62ae8272d642..d45d2e4c95509 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflow/TOFDigitWriterSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFDigitWriterSpec.h @@ -18,7 +18,7 @@ namespace o2 namespace tof { -o2::framework::DataProcessorSpec getTOFDigitWriterSpec(bool useMC = 1); +o2::framework::DataProcessorSpec getTOFDigitWriterSpec(bool useMC = 1, bool writeErr = 0); } // end namespace tof } // end namespace o2 diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFDigitWriterSplitterSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFDigitWriterSplitterSpec.h new file mode 100644 index 0000000000000..8311733203f0f --- /dev/null +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFDigitWriterSplitterSpec.h @@ -0,0 +1,168 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TOFDIGIT_SPLITTER_WRITER_H +#define O2_TOFDIGIT_SPLITTER_WRITER_H + +/// @file TOFDigitWriterSplitterSpec.h +/// @brief Device to write to tree the information for TOF time slewing calibration. + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TOFBase/Digit.h" +#include "Framework/Logger.h" +#include <TTree.h> +#include <gsl/span> + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ +class TOFDigitWriterSplitter : public Task +{ + using OutputType = std::vector<o2::tof::Digit>; + using ReadoutWinType = std::vector<o2::tof::ReadoutWindowData>; + using PatternType = std::vector<uint32_t>; + using ErrorType = std::vector<uint64_t>; + using HeaderType = o2::tof::DigitHeader; + + std::string mBaseName; + + public: + TOFDigitWriterSplitter(int nTF, bool storeErr = false) : mTFthr(nTF), mStoreErrors(storeErr) {} + + void createAndOpenFileAndTree() + { + TString filename = TString::Format("%s_%06d.root", mBaseName.c_str(), mCount); + LOG(DEBUG) << "opening file " << filename.Data(); + mfileOut.reset(TFile::Open(TString::Format("%s", filename.Data()), "RECREATE")); + mOutputTree = std::make_unique<TTree>("o2sim", "Tree with TOF digits"); + mOutputTree->Branch("TOFHeader", &mPHeader); + mOutputTree->Branch("TOFDigit", &mPDigits); + mOutputTree->Branch("TOFReadoutWindow", &mPROW); + mOutputTree->Branch("TOFPatterns", &mPDia); + if (mStoreErrors) { + mOutputTree->Branch("TOFErrors", &mPErr); + } + + mNTF = 0; + } + + void init(o2::framework::InitContext& ic) final + { + mBaseName = ic.options().get<std::string>("output-base-name"); + + mCount = 0; + createAndOpenFileAndTree(); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto digits = pc.inputs().get<OutputType>("digits"); + mPDigits = &digits; + auto header = pc.inputs().get<HeaderType>("header"); + mPHeader = &header; + auto row = pc.inputs().get<ReadoutWinType>("rows"); + mPROW = &row; + auto dia = pc.inputs().get<PatternType>("patterns"); + mPDia = &dia; + if (mStoreErrors) { + auto error = pc.inputs().get<ErrorType>("errors"); + mPErr = &error; + + mOutputTree->Fill(); + } else { + mOutputTree->Fill(); + } + mNTF++; + + if (mNTF >= mTFthr) { + sendOutput(); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + mIsEndOfStream = true; + sendOutput(); + } + + private: + int mCount = 0; // how many times we filled the tree + int mNTF = 0; + int mTFthr = 1; + bool mStoreErrors = false; + bool mIsEndOfStream = false; + OutputType mDigits; + const OutputType* mPDigits = &mDigits; + ReadoutWinType mROW; + const ReadoutWinType* mPROW = &mROW; + PatternType mDia; + const PatternType* mPDia = &mDia; + ErrorType mErr; + const ErrorType* mPErr = &mErr; + HeaderType mHeader; + const HeaderType* mPHeader = &mHeader; + std::unique_ptr<TTree> mOutputTree; ///< tree for the collected calib tof info + std::unique_ptr<TFile> mfileOut = nullptr; // file in which to write the output + + //________________________________________________________________ + void sendOutput() + { + // 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(); + mfileOut.reset(); + mCount++; + if (!mIsEndOfStream) { + createAndOpenFileAndTree(); + } + } +}; +} // namespace tof + +namespace framework +{ + +DataProcessorSpec getTOFDigitWriterSplitterSpec(int nTF, bool storeErr = false) +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("header", o2::header::gDataOriginTOF, "DIGITHEADER"); + inputs.emplace_back("digits", o2::header::gDataOriginTOF, "DIGITS"); + inputs.emplace_back("rows", o2::header::gDataOriginTOF, "READOUTWINDOW"); + inputs.emplace_back("patterns", o2::header::gDataOriginTOF, "PATTERNS"); + + if (storeErr) { + inputs.emplace_back("errors", o2::header::gDataOriginTOF, "ERRORS"); + } + + std::vector<OutputSpec> outputs; // empty + + return DataProcessorSpec{ + "tof-digit-splitter-writer", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<o2::tof::TOFDigitWriterSplitter>(nTF, storeErr)}, + Options{{"output-base-name", VariantType::String, "tofdigits", {"Name of the input file (root extension will be added)"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TOF/workflow/include/TOFWorkflow/TOFRawWriterSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFRawWriterSpec.h similarity index 100% rename from Detectors/TOF/workflow/include/TOFWorkflow/TOFRawWriterSpec.h rename to Detectors/TOF/workflow/include/TOFWorkflowUtils/TOFRawWriterSpec.h diff --git a/Detectors/TOF/workflow/src/ClusterReaderSpec.cxx b/Detectors/TOF/workflow/src/ClusterReaderSpec.cxx index 87949525d8146..f9c422307bab3 100644 --- a/Detectors/TOF/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/TOF/workflow/src/ClusterReaderSpec.cxx @@ -14,7 +14,7 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" -#include "TOFWorkflow/ClusterReaderSpec.h" +#include "TOFWorkflowUtils/ClusterReaderSpec.h" #include "DataFormatsParameters/GRPObject.h" using namespace o2::framework; diff --git a/Detectors/TOF/workflow/src/CompressedAnalysisTask.cxx b/Detectors/TOF/workflow/src/CompressedAnalysisTask.cxx new file mode 100644 index 0000000000000..efd9efc9198ef --- /dev/null +++ b/Detectors/TOF/workflow/src/CompressedAnalysisTask.cxx @@ -0,0 +1,99 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CompressedAnalysisTask.cxx +/// @author Roberto Preghenella +/// @since 2020-09-04 +/// @brief TOF compressed data analysis task + +#include "TOFWorkflowUtils/CompressedAnalysisTask.h" +#include "Framework/Task.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/CallbackService.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/RawDeviceService.h" +#include "Framework/DeviceSpec.h" +#include <fairmq/FairMQDevice.h> + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +void CompressedAnalysisTask::init(InitContext& ic) +{ + + auto conetmode = ic.options().get<bool>("tof-compressed-analysis-conet-mode"); + auto filename = ic.options().get<std::string>("tof-compressed-analysis-filename"); + auto function = ic.options().get<std::string>("tof-compressed-analysis-function"); + + if (filename.empty()) { + LOG(ERROR) << "No analysis filename defined"; + mStatus = true; + return; + } + + if (function.empty()) { + LOG(ERROR) << "No analysis function defined"; + mStatus = true; + return; + } + + mAnalysis = GetFromMacro<CompressedAnalysis*>(filename, function, "o2::tof::CompressedAnalysis*", "compressed_analysis"); + if (!mAnalysis) { + LOG(ERROR) << "Could not retrieve analysis from file: " << filename; + mStatus = true; + return; + } + + mAnalysis->setDecoderCONET(conetmode); + mAnalysis->initialize(); + + auto finishFunction = [this]() { + LOG(INFO) << "CompressedBaseTask finish"; + mAnalysis->finalize(); + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, finishFunction); +} + +void CompressedAnalysisTask::run(ProcessingContext& pc) +{ + + /** check status **/ + if (mStatus) { + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + return; + } + + /** loop over inputs routes **/ + for (auto iit = pc.inputs().begin(), iend = pc.inputs().end(); iit != iend; ++iit) { + if (!iit.isValid()) { + continue; + } + + /** loop over input parts **/ + for (auto const& ref : iit) { + + const auto* headerIn = DataRefUtils::getHeader<o2::header::DataHeader*>(ref); + auto payloadIn = ref.payload; + auto payloadInSize = headerIn->payloadSize; + + mAnalysis->setDecoderBuffer(payloadIn); + mAnalysis->setDecoderBufferSize(payloadInSize); + mAnalysis->run(); + } + } +} + +} // namespace tof +} // namespace o2 diff --git a/Detectors/TOF/workflow/src/CompressedDecodingTask.cxx b/Detectors/TOF/workflow/src/CompressedDecodingTask.cxx index 41451891fa9fd..59b2e4d7c139f 100644 --- a/Detectors/TOF/workflow/src/CompressedDecodingTask.cxx +++ b/Detectors/TOF/workflow/src/CompressedDecodingTask.cxx @@ -13,10 +13,11 @@ /// @since 2020-02-25 /// @brief TOF compressed data decoding task -#include "TOFWorkflow/CompressedDecodingTask.h" +#include "TOFWorkflowUtils/CompressedDecodingTask.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "CommonUtils/StringUtils.h" #include "Headers/RAWDataHeader.h" #include "DataFormatsTOF/CompressedDataFormat.h" #include "DetectorsRaw/HBFUtils.h" @@ -38,6 +39,14 @@ void CompressedDecodingTask::init(InitContext& ic) { LOG(INFO) << "CompressedDecoding init"; + mMaskNoise = ic.options().get<bool>("mask-noise"); + mNoiseRate = ic.options().get<int>("noise-counts"); + mRowFilter = ic.options().get<bool>("row-filter"); + + if (mMaskNoise) { + mDecoder.maskNoiseRate(mNoiseRate); + } + auto finishFunction = [this]() { LOG(INFO) << "CompressedDecoding finish"; }; @@ -54,25 +63,64 @@ void CompressedDecodingTask::postData(ProcessingContext& pc) // send output message std::vector<o2::tof::Digit>* alldigits = mDecoder.getDigitPerTimeFrame(); std::vector<o2::tof::ReadoutWindowData>* row = mDecoder.getReadoutWindowData(); + if (mRowFilter) { + row = mDecoder.getReadoutWindowDataFiltered(); + } + + ReadoutWindowData* last = nullptr; + o2::InteractionRecord lastIR; + int lastval = 0; + if (!row->empty()) { + last = &row->back(); + lastval = last->first() + last->size(); + lastIR = last->mFirstIR; + } + + /* + int nwindowperTF = o2::raw::HBFUtils::Instance().getNOrbitsPerTF() * 3; + while (row->size() < nwindowperTF) { + // complete timeframe with empty readout windows + auto& dummy = row->emplace_back(lastval, 0); + dummy.mFirstIR = lastIR; + } + while (row->size() > nwindowperTF) { + // remove extra readout windows after a check they are empty + row->pop_back(); + } +*/ int n_tof_window = row->size(); int n_orbits = n_tof_window / 3; int digit_size = alldigits->size(); - LOG(INFO) << "TOF: N tof window decoded = " << n_tof_window << "(orbits = " << n_orbits << ") with " << digit_size << " digits"; + // LOG(INFO) << "TOF: N tof window decoded = " << n_tof_window << "(orbits = " << n_orbits << ") with " << digit_size << " digits"; // add digits in the output snapshot pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe}, *alldigits); pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe}, *row); - static o2::parameters::GRPObject::ROMode roMode = o2::parameters::GRPObject::CONTINUOUS; + std::vector<uint32_t>& patterns = mDecoder.getPatterns(); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe}, patterns); - LOG(INFO) << "TOF: Sending ROMode= " << roMode << " to GRPUpdater"; - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "ROMode", 0, Lifetime::Timeframe}, roMode); + std::vector<uint64_t>& errors = mDecoder.getErrors(); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "ERRORS", 0, Lifetime::Timeframe}, errors); - mDecoder.clear(); + DigitHeader& digitH = mDecoder.getDigitHeader(); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe}, digitH); - LOG(INFO) << "TOF: TF = " << mNTF << " - Crate in " << mNCrateOpenTF; + // RS this is a hack to be removed once we have correct propagation of the firstTForbit by the framework + auto setFirstTFOrbit = [&](const Output& spec, uint32_t orb) { + auto* hd = pc.outputs().findMessageHeader(spec); + if (!hd) { + throw std::runtime_error(o2::utils::concat_string("failed to find output message header for ", spec.origin.str, "/", spec.description.str, "/", std::to_string(spec.subSpec))); + } + hd->firstTForbit = orb; + }; + + setFirstTFOrbit(Output{o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe}, mInitOrbit); + setFirstTFOrbit(Output{o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe}, mInitOrbit); + + mDecoder.clear(); mNTF++; mNCrateOpenTF = 0; @@ -81,17 +129,24 @@ void CompressedDecodingTask::postData(ProcessingContext& pc) void CompressedDecodingTask::run(ProcessingContext& pc) { - LOG(INFO) << "CompressedDecoding run"; mTimer.Start(false); + if (pc.inputs().getNofParts(0) && !mConetMode) { + //RS set the 1st orbit of the TF from the O2 header, relying on rdhHandler is not good (in fact, the RDH might be eliminated in the derived data) + const auto* dh = o2::header::get<o2::header::DataHeader*>(pc.inputs().getByPos(0).header); + mInitOrbit = dh->firstTForbit; + } + + mDecoder.setFirstIR({0, mInitOrbit}); + /** loop over inputs routes **/ for (auto iit = pc.inputs().begin(), iend = pc.inputs().end(); iit != iend; ++iit) { - if (!iit.isValid()) + if (!iit.isValid()) { continue; + } /** loop over input parts **/ for (auto const& ref : iit) { - const auto* headerIn = DataRefUtils::getHeader<o2::header::DataHeader*>(ref); auto payloadIn = ref.payload; auto payloadInSize = headerIn->payloadSize; @@ -102,8 +157,9 @@ void CompressedDecodingTask::run(ProcessingContext& pc) } } - if (mNCrateOpenTF == 72 && mNCrateOpenTF == mNCrateCloseTF) + if ((mNCrateOpenTF > 0 || mConetMode) && mNCrateOpenTF == mNCrateCloseTF) { mHasToBePosted = true; + } if (mHasToBePosted) { postData(pc); @@ -117,6 +173,162 @@ void CompressedDecodingTask::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +void CompressedDecodingTask::headerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit) +{ + if (mConetMode) { + LOG(DEBUG) << "Crate found" << crateHeader->drmID; + + mInitOrbit = crateOrbit->orbitID; + + mNCrateOpenTF++; + } +} +void CompressedDecodingTask::trailerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit, + const CrateTrailer_t* crateTrailer, const Diagnostic_t* diagnostics, + const Error_t* errors) +{ + if (mConetMode) { + LOG(DEBUG) << "Crate closed" << crateHeader->drmID; + mNCrateCloseTF++; + } + + mDecoder.addCrateHeaderData(crateOrbit->orbitID, crateHeader->drmID, crateHeader->bunchID, crateTrailer->eventCounter); + + // Diagnostics used to fill digit patterns + auto numberOfDiagnostics = crateTrailer->numberOfDiagnostics; + auto numberOfErrors = crateTrailer->numberOfErrors; + for (int i = 0; i < numberOfDiagnostics; i++) { + const uint32_t* val = reinterpret_cast<const uint32_t*>(&(diagnostics[i])); + mDecoder.addPattern(*val, crateHeader->drmID, crateOrbit->orbitID, crateHeader->bunchID); + + /* + int islot = (*val & 15); + printf("DRM = %d (orbit = %d) slot = %d: \n", crateHeader->drmID, crateOrbit->orbitID, islot); + if (islot == 1) { + if (o2::tof::diagnostic::DRM_HEADER_MISSING & *val) { + printf("DRM_HEADER_MISSING\n"); + } + if (o2::tof::diagnostic::DRM_TRAILER_MISSING & *val) { + printf("DRM_TRAILER_MISSING\n"); + } + if (o2::tof::diagnostic::DRM_FEEID_MISMATCH & *val) { + printf("DRM_FEEID_MISMATCH\n"); + } + if (o2::tof::diagnostic::DRM_ORBIT_MISMATCH & *val) { + printf("DRM_ORBIT_MISMATCH\n"); + } + if (o2::tof::diagnostic::DRM_CRC_MISMATCH & *val) { + printf("DRM_CRC_MISMATCH\n"); + } + if (o2::tof::diagnostic::DRM_ENAPARTMASK_DIFFER & *val) { + printf("DRM_ENAPARTMASK_DIFFER\n"); + } + if (o2::tof::diagnostic::DRM_CLOCKSTATUS_WRONG & *val) { + printf("DRM_CLOCKSTATUS_WRONG\n"); + } + if (o2::tof::diagnostic::DRM_FAULTSLOTMASK_NOTZERO & *val) { + printf("DRM_FAULTSLOTMASK_NOTZERO\n"); + } + if (o2::tof::diagnostic::DRM_READOUTTIMEOUT_NOTZERO & *val) { + printf("DRM_READOUTTIMEOUT_NOTZERO\n"); + } + if (o2::tof::diagnostic::DRM_EVENTWORDS_MISMATCH & *val) { + printf("DRM_EVENTWORDS_MISMATCH\n"); + } + if (o2::tof::diagnostic::DRM_MAXDIAGNOSTIC_BIT & *val) { + printf("DRM_MAXDIAGNOSTIC_BIT\n"); + } + } else if (islot == 2) { + if (o2::tof::diagnostic::LTM_HEADER_MISSING & *val) { + printf("LTM_HEADER_MISSING\n"); + } + if (o2::tof::diagnostic::LTM_TRAILER_MISSING & *val) { + printf("LTM_TRAILER_MISSING\n"); + } + if (o2::tof::diagnostic::LTM_HEADER_UNEXPECTED & *val) { + printf("LTM_HEADER_UNEXPECTED\n"); + } + if (o2::tof::diagnostic::LTM_MAXDIAGNOSTIC_BIT & *val) { + printf("LTM_MAXDIAGNOSTIC_BIT\n"); + } + } else if (islot < 13) { + if (o2::tof::diagnostic::TRM_HEADER_MISSING & *val) { + printf("TRM_HEADER_MISSING\n"); + } + if (o2::tof::diagnostic::TRM_TRAILER_MISSING & *val) { + printf("TRM_TRAILER_MISSING\n"); + } + if (o2::tof::diagnostic::TRM_CRC_MISMATCH & *val) { + printf("TRM_CRC_MISMATCH\n"); + } + if (o2::tof::diagnostic::TRM_HEADER_UNEXPECTED & *val) { + printf("TRM_HEADER_UNEXPECTED\n"); + } + if (o2::tof::diagnostic::TRM_EVENTCNT_MISMATCH & *val) { + printf("TRM_EVENTCNT_MISMATCH\n"); + } + if (o2::tof::diagnostic::TRM_EMPTYBIT_NOTZERO & *val) { + printf("TRM_EMPTYBIT_NOTZERO\n"); + } + if (o2::tof::diagnostic::TRM_LBIT_NOTZERO & *val) { + printf("TRM_LBIT_NOTZERO\n"); + } + if (o2::tof::diagnostic::TRM_FAULTSLOTBIT_NOTZERO & *val) { + printf("TRM_FAULTSLOTBIT_NOTZERO\n"); + } + if (o2::tof::diagnostic::TRM_EVENTWORDS_MISMATCH & *val) { + printf("TRM_EVENTWORDS_MISMATCH\n"); + } + if (o2::tof::diagnostic::TRM_DIAGNOSTIC_SPARE1 & *val) { + printf("TRM_DIAGNOSTIC_SPARE1\n"); + } + if (o2::tof::diagnostic::TRM_DIAGNOSTIC_SPARE2 & *val) { + printf("TRM_DIAGNOSTIC_SPARE2\n"); + } + if (o2::tof::diagnostic::TRM_DIAGNOSTIC_SPARE3 & *val) { + printf("TRM_DIAGNOSTIC_SPARE3\n"); + } + if (o2::tof::diagnostic::TRM_MAXDIAGNOSTIC_BIT & *val) { + printf("TRM_MAXDIAGNOSTIC_BIT\n"); + } + + if (o2::tof::diagnostic::TRMCHAIN_HEADER_MISSING & *val) { + printf("TRMCHAIN_HEADER_MISSING\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_TRAILER_MISSING & *val) { + printf("TRMCHAIN_TRAILER_MISSING\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_STATUS_NOTZERO & *val) { + printf("TRMCHAIN_STATUS_NOTZERO\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_EVENTCNT_MISMATCH & *val) { + printf("TRMCHAIN_EVENTCNT_MISMATCH\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_TDCERROR_DETECTED & *val) { + printf("TRMCHAIN_TDCERROR_DETECTED\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_BUNCHCNT_MISMATCH & *val) { + printf("TRMCHAIN_BUNCHCNT_MISMATCH\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_DIAGNOSTIC_SPARE1 & *val) { + printf("TRMCHAIN_DIAGNOSTIC_SPARE1\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_DIAGNOSTIC_SPARE2 & *val) { + printf("TRMCHAIN_DIAGNOSTIC_SPARE2\n"); + } + if (o2::tof::diagnostic::TRMCHAIN_MAXDIAGNOSTIC_BIT & *val) { + printf("TRMCHAIN_MAXDIAGNOSTIC_BIT\n"); + } + } + printf("------\n"); + */ + } + for (int i = 0; i < numberOfErrors; i++) { + const uint32_t* val = reinterpret_cast<const uint32_t*>(&(errors[i])); + mDecoder.addError(*val, crateHeader->drmID); + } +} + void CompressedDecodingTask::rdhHandler(const o2::header::RAWDataHeader* rdh) { @@ -131,7 +343,7 @@ void CompressedDecodingTask::rdhHandler(const o2::header::RAWDataHeader* rdh) // rdh open if ((RDHUtils::getPageCounter(rdhr) == 0) && (RDHUtils::getTriggerType(rdhr) & o2::trigger::TF)) { mNCrateOpenTF++; - mInitOrbit = RDHUtils::getHeartBeatOrbit(rdhr); + mInitOrbit = RDHUtils::getHeartBeatOrbit(rdhr); // RSTODO this may be eliminated once the framework will start to propagated the dh.firstTForbit // printf("New TF open RDH %d\n", int(rdh->feeId)); } }; @@ -145,19 +357,24 @@ void CompressedDecodingTask::frameHandler(const CrateHeader_t* crateHeader, cons } }; -DataProcessorSpec getCompressedDecodingSpec(const std::string& inputDesc) +DataProcessorSpec getCompressedDecodingSpec(const std::string& inputDesc, bool conet) { std::vector<OutputSpec> outputs; + outputs.emplace_back(o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe); - outputs.emplace_back(o2::header::gDataOriginTOF, "ROMode", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTOF, "ERRORS", 0, Lifetime::Timeframe); return DataProcessorSpec{ "tof-compressed-decoder", select(std::string("x:TOF/" + inputDesc).c_str()), outputs, - AlgorithmSpec{adaptFromTask<CompressedDecodingTask>()}, - Options{}}; + AlgorithmSpec{adaptFromTask<CompressedDecodingTask>(conet)}, + Options{ + {"row-filter", VariantType::Bool, false, {"Filter empty row"}}, + {"mask-noise", VariantType::Bool, false, {"Flag to mask noisy digits"}}, + {"noise-counts", VariantType::Int, 1000, {"Counts in a single (TF) payload"}}}}; } } // namespace tof diff --git a/Detectors/TOF/workflow/src/CompressedInspectorTask.cxx b/Detectors/TOF/workflow/src/CompressedInspectorTask.cxx index eb96808b91687..bebb1a8908641 100644 --- a/Detectors/TOF/workflow/src/CompressedInspectorTask.cxx +++ b/Detectors/TOF/workflow/src/CompressedInspectorTask.cxx @@ -13,10 +13,10 @@ /// @since 2020-01-25 /// @brief TOF compressed data inspector task -#include "TOFWorkflow/CompressedInspectorTask.h" +#include "TOFWorkflowUtils/CompressedInspectorTask.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" - +#include "Framework/Logger.h" #include "Headers/RAWDataHeader.h" #include "DataFormatsTOF/CompressedDataFormat.h" @@ -36,6 +36,9 @@ void CompressedInspectorTask<RDH>::init(InitContext& ic) { LOG(INFO) << "CompressedInspector init"; auto filename = ic.options().get<std::string>("tof-compressed-inspector-filename"); + auto verbose = ic.options().get<bool>("tof-compressed-inspector-decoder-verbose"); + + DecoderBaseT<RDH>::setDecoderVerbose(verbose); /** open file **/ if (mFile && mFile->IsOpen()) { @@ -62,13 +65,17 @@ void CompressedInspectorTask<RDH>::init(InitContext& ic) mHistos1D["errorBit"] = new TH1F("hErrorBit", ";TDC error bit", 15, 0., 15.); mHistos2D["error"] = new TH2F("hError", ";slot;TDC", 24, 1., 13., 15, 0., 15.); mHistos2D["test"] = new TH2F("hTest", ";slot;TDC", 24, 1., 13., 15, 0., 15.); + mHistos2D["crateBC"] = new TH2F("hCrateBC", ";crate;BC", 72, 0., 72., 4096, 0., 4096.); + mHistos2D["crateOrbit"] = new TH2F("hCrateOrbit", ";crate;orbit", 72, 0., 72., 4096, 0., 4096.); auto finishFunction = [this]() { LOG(INFO) << "CompressedInspector finish"; - for (auto& histo : mHistos1D) + for (auto& histo : mHistos1D) { histo.second->Write(); - for (auto& histo : mHistos2D) + } + for (auto& histo : mHistos2D) { histo.second->Write(); + } mFile->Close(); }; ic.services().get<CallbackService>().set(CallbackService::Id::Stop, finishFunction); @@ -87,8 +94,9 @@ void CompressedInspectorTask<RDH>::run(ProcessingContext& pc) /** loop over inputs routes **/ for (auto iit = pc.inputs().begin(), iend = pc.inputs().end(); iit != iend; ++iit) { - if (!iit.isValid()) + if (!iit.isValid()) { continue; + } /** loop over input parts **/ for (auto const& ref : iit) { @@ -107,9 +115,14 @@ void CompressedInspectorTask<RDH>::run(ProcessingContext& pc) template <typename RDH> void CompressedInspectorTask<RDH>::headerHandler(const CrateHeader_t* crateHeader, const CrateOrbit_t* crateOrbit) { - for (int ibit = 0; ibit < 11; ++ibit) - if (crateHeader->slotPartMask & (1 << ibit)) + mHistos2D["crateBC"]->Fill(crateHeader->drmID, crateHeader->bunchID); + mHistos2D["crateOrbit"]->Fill(crateHeader->drmID, crateOrbit->orbitID % 4096); + + for (int ibit = 0; ibit < 11; ++ibit) { + if (crateHeader->slotPartMask & (1 << ibit)) { mHistos2D["slotPartMask"]->Fill(crateHeader->drmID, ibit + 2); + } + } }; template <typename RDH> @@ -155,8 +168,9 @@ void CompressedInspectorTask<RDH>::trailerHandler(const CrateHeader_t* crateHead nError++; mHistos2D["error"]->Fill(error->slotID + 0.5 * error->chain, error->tdcID); for (int ibit = 0; ibit < 15; ++ibit) { - if (error->errorFlags & (1 << ibit)) + if (error->errorFlags & (1 << ibit)) { mHistos1D["errorBit"]->Fill(ibit); + } } } } diff --git a/Detectors/TOF/workflow/src/DigitReaderSpec.cxx b/Detectors/TOF/workflow/src/DigitReaderSpec.cxx index c2a3bfb3a9d94..a987aa493515c 100644 --- a/Detectors/TOF/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/TOF/workflow/src/DigitReaderSpec.cxx @@ -16,7 +16,8 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" -#include "TOFWorkflow/DigitReaderSpec.h" +#include "Framework/Logger.h" +#include "TOFWorkflowUtils/DigitReaderSpec.h" #include "DataFormatsParameters/GRPObject.h" using namespace o2::framework; @@ -51,31 +52,38 @@ void DigitReader::run(ProcessingContext& pc) if (treeDig) { treeDig->SetBranchAddress("TOFDigit", &mPdigits); treeDig->SetBranchAddress("TOFReadoutWindow", &mProw); + treeDig->SetBranchAddress("TOFPatterns", &mPpatterns); if (mUseMC) { treeDig->SetBranchAddress("TOFDigitMCTruth", &mPlabels); } - treeDig->GetEntry(0); + treeDig->GetEntry(mCurrentEntry); // add digits loaded in the output snapshot pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe}, mDigits); pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe}, mRow); - if (mUseMC) + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe}, mPatterns); + if (mUseMC) { pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "DIGITSMCTR", 0, Lifetime::Timeframe}, mLabels); + } static o2::parameters::GRPObject::ROMode roMode = o2::parameters::GRPObject::CONTINUOUS; - LOG(INFO) << "TOF: Sending ROMode= " << roMode << " to GRPUpdater"; + LOG(DEBUG) << "TOF: Sending ROMode= " << roMode << " to GRPUpdater"; pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "ROMode", 0, Lifetime::Timeframe}, roMode); } else { LOG(ERROR) << "Cannot read the TOF digits !"; return; } - mState = 2; - //pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); - pc.services().get<ControlService>().endOfStream(); + mCurrentEntry++; + + if (mCurrentEntry >= treeDig->GetEntries()) { + mState = 2; + //pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + pc.services().get<ControlService>().endOfStream(); + } } DataProcessorSpec getDigitReaderSpec(bool useMC) @@ -86,6 +94,7 @@ DataProcessorSpec getDigitReaderSpec(bool useMC) if (useMC) { outputs.emplace_back(o2::header::gDataOriginTOF, "DIGITSMCTR", 0, Lifetime::Timeframe); } + outputs.emplace_back(o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "ROMode", 0, Lifetime::Timeframe); return DataProcessorSpec{ diff --git a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx new file mode 100644 index 0000000000000..ca1fa309a8831 --- /dev/null +++ b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx @@ -0,0 +1,83 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyDecoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "TOFWorkflowUtils/EntropyDecoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +EntropyDecoderSpec::EntropyDecoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("tof-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } +} + +void EntropyDecoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto buff = pc.inputs().get<gsl::span<o2::ctf::BufferType>>("ctf"); + + auto& digitheader = pc.outputs().make<DigitHeader>(OutputRef{"digitheader"}); + auto& digits = pc.outputs().make<std::vector<Digit>>(OutputRef{"digits"}); + auto& row = pc.outputs().make<std::vector<ReadoutWindowData>>(OutputRef{"row"}); + auto& patterns = pc.outputs().make<std::vector<uint32_t>>(OutputRef{"patterns"}); + + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + const auto ctfImage = o2::tof::CTF::getImage(buff.data()); + mCTFCoder.decode(ctfImage, row, digits, patterns); + + mTimer.Stop(); + LOG(INFO) << "Decoded " << digits.size() << " digits in " << row.size() << " ROF in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "TOF Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyDecoderSpec() +{ + std::vector<OutputSpec> outputs{ + OutputSpec{{"digitheader"}, o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe}, + OutputSpec{{"digits"}, o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe}, + OutputSpec{{"row"}, o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe}, + OutputSpec{{"patterns"}, o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe}}; + + return DataProcessorSpec{ + "tof-entropy-decoder", + Inputs{InputSpec{"ctf", o2::header::gDataOriginTOF, "CTFDATA", 0, Lifetime::Timeframe}}, + outputs, + AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, + Options{{"tof-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; +} + +} // namespace tof +} // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx new file mode 100644 index 0000000000000..7af45cc28c8b2 --- /dev/null +++ b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx @@ -0,0 +1,82 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 EntropyEncoderSpec.cxx + +#include <vector> + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "TOFBase/Digit.h" +#include "TOFWorkflowUtils/EntropyEncoderSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +EntropyEncoderSpec::EntropyEncoderSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + std::string dictPath = ic.options().get<std::string>("tof-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + +void EntropyEncoderSpec::run(ProcessingContext& pc) +{ + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + auto compDigits = pc.inputs().get<gsl::span<Digit>>("compDigits"); + auto pspan = pc.inputs().get<gsl::span<uint32_t>>("patterns"); + auto rofs = pc.inputs().get<gsl::span<ReadoutWindowData>>("ROframes"); + + auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{o2::header::gDataOriginTOF, "CTFDATA", 0, Lifetime::Timeframe}); + mCTFCoder.encode(buffer, rofs, compDigits, pspan); + auto eeb = CTF::get(buffer.data()); // cast to container pointer + eeb->compactify(); // eliminate unnecessary padding + buffer.resize(eeb->size()); // shrink buffer to strictly necessary size + // eeb->print(); + mTimer.Stop(); + LOG(INFO) << "Created encoded data of size " << eeb->size() << " for TOF in " << mTimer.CpuTime() - cput << " s"; + // pc.services().get<ControlService>().endOfStream(); +} + +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "TOF Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getEntropyEncoderSpec() +{ + std::vector<InputSpec> inputs; + inputs.emplace_back("compDigits", o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe); + inputs.emplace_back("patterns", o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); + inputs.emplace_back("ROframes", o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "tof-entropy-encoder", + inputs, + Outputs{{o2::header::gDataOriginTOF, "CTFDATA", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>()}, + Options{{"tof-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}}}; +} + +} // namespace tof +} // namespace o2 diff --git a/Detectors/TOF/workflow/src/TOFClusterWriterSpec.cxx b/Detectors/TOF/workflow/src/TOFClusterWriterSpec.cxx index 4d3aa0bacbd9a..9c12d5b16ad78 100644 --- a/Detectors/TOF/workflow/src/TOFClusterWriterSpec.cxx +++ b/Detectors/TOF/workflow/src/TOFClusterWriterSpec.cxx @@ -10,7 +10,7 @@ /// @file TODClusterWriterSpec.cxx -#include "TOFWorkflow/TOFClusterWriterSpec.h" +#include "TOFWorkflowUtils/TOFClusterWriterSpec.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -32,10 +32,10 @@ DataProcessorSpec getTOFClusterWriterSpec(bool useMC) { // Spectators for logging auto logger = [](OutputType const& indata) { - LOG(INFO) << "RECEIVED CLUSTERS SIZE " << indata.size(); + LOG(DEBUG) << "RECEIVED CLUSTERS SIZE " << indata.size(); }; auto loggerMCLabels = [](LabelsType const& labeldata) { - LOG(INFO) << "TOF GOT " << labeldata.getNElements() << " LABELS "; + LOG(DEBUG) << "TOF GOT " << labeldata.getNElements() << " LABELS "; }; return MakeRootTreeWriterSpec("TOFClusterWriter", "tofclusters.root", diff --git a/Detectors/TOF/workflow/src/TOFClusterizerSpec.cxx b/Detectors/TOF/workflow/src/TOFClusterizerSpec.cxx index 61b3796ab1174..aa6c2ef71a633 100644 --- a/Detectors/TOF/workflow/src/TOFClusterizerSpec.cxx +++ b/Detectors/TOF/workflow/src/TOFClusterizerSpec.cxx @@ -8,7 +8,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "TOFWorkflow/TOFClusterizerSpec.h" +#include "TOFWorkflowUtils/TOFClusterizerSpec.h" #include "Framework/ControlService.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataRefUtils.h" @@ -28,7 +28,11 @@ #include <memory> // for make_shared, make_unique, unique_ptr #include <vector> +// RSTODO to remove once the framework will start propagating the header.firstTForbit +#include "DetectorsRaw/HBFUtils.h" + using namespace o2::framework; +using namespace o2::dataformats; namespace o2 { @@ -39,7 +43,6 @@ namespace tof // just need to implement 2 special methods init + run (there is no need to inherit from anything) class TOFDPLClustererTask { - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; bool mUseMC = true; bool mUseCCDB = false; @@ -59,6 +62,20 @@ class TOFDPLClustererTask auto digits = pc.inputs().get<gsl::span<o2::tof::Digit>>("tofdigits"); auto row = pc.inputs().get<std::vector<o2::tof::ReadoutWindowData>*>("readoutwin"); + //auto header = o2::header::get<o2::header::DataHeader*>(pc.inputs().get("tofdigits").header); + + if (row->size() > 0) { + mClusterer.setFirstOrbit(row->at(0).mFirstIR.orbit); + } + + //RSTODO: below is a hack, to remove once the framework will start propagating the header.firstTForbit + //Here I extract the orbit/BC from the abs.BC, since the triggerer orbit/bunch are not set. Then why they are needed? + // if (digits.size()) { + // auto bcabs = digits[0].getBC(); + // auto ir0 = o2::raw::HBFUtils::Instance().getFirstIRofTF({uint16_t(bcabs % Geo::BC_IN_ORBIT), uint32_t(bcabs / Geo::BC_IN_ORBIT)}); + // mClusterer.setFirstOrbit(ir0.orbit); + // } + auto labelvector = std::make_shared<std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>>(); if (mUseMC) { auto digitlabels = pc.inputs().get<std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>*>("tofdigitlabels"); @@ -101,23 +118,25 @@ class TOFDPLClustererTask mClustersArray.clear(); for (int i = 0; i < row->size(); i++) { - printf("# TOF readout window for clusterization = %d/%lu (N digits = %d)\n", i, row->size(), row->at(i).size()); + //printf("# TOF readout window for clusterization = %d/%lu (N digits = %d)\n", i, row->size(), row->at(i).size()); auto digitsRO = row->at(i).getBunchChannelData(digits); mReader.setDigitArray(&digitsRO); if (mUseMC) { mClusterer.process(mReader, mClustersArray, &(labelvector->at(i))); - } else + } else { mClusterer.process(mReader, mClustersArray, nullptr); + } } - LOG(INFO) << "TOF CLUSTERER : TRANSFORMED " << digits.size() - << " DIGITS TO " << mClustersArray.size() << " CLUSTERS"; + LOG(DEBUG) << "TOF CLUSTERER : TRANSFORMED " << digits.size() + << " DIGITS TO " << mClustersArray.size() << " CLUSTERS"; // send clusters pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CLUSTERS", 0, Lifetime::Timeframe}, mClustersArray); // send labels - if (mUseMC) + if (mUseMC) { pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CLUSTERSMCTR", 0, Lifetime::Timeframe}, mClsLabels); + } mTimer.Stop(); } diff --git a/Detectors/TOF/workflow/src/TOFDigitWriterSpec.cxx b/Detectors/TOF/workflow/src/TOFDigitWriterSpec.cxx index efc394950385e..24a81ffaa1793 100644 --- a/Detectors/TOF/workflow/src/TOFDigitWriterSpec.cxx +++ b/Detectors/TOF/workflow/src/TOFDigitWriterSpec.cxx @@ -10,7 +10,7 @@ /// @brief Processor spec for a ROOT file writer for TOF digits -#include "TOFWorkflow/TOFDigitWriterSpec.h" +#include "TOFWorkflowUtils/TOFDigitWriterSpec.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -26,10 +26,13 @@ template <typename T> using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; using OutputType = std::vector<o2::tof::Digit>; using ReadoutWinType = std::vector<o2::tof::ReadoutWindowData>; +using PatternType = std::vector<uint32_t>; +using ErrorType = std::vector<uint64_t>; using LabelsType = std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>; +using HeaderType = o2::tof::DigitHeader; using namespace o2::header; -DataProcessorSpec getTOFDigitWriterSpec(bool useMC) +DataProcessorSpec getTOFDigitWriterSpec(bool useMC, bool writeErr) { auto nCalls = std::make_shared<int>(); *nCalls = 0; @@ -45,12 +48,19 @@ DataProcessorSpec getTOFDigitWriterSpec(bool useMC) auto preprocessor = [nCalls](ProcessingContext&) { (*nCalls)++; }; + auto loggerH = [nCalls](HeaderType const& indata) { + }; auto logger = [nCalls](OutputType const& indata) { - LOG(INFO) << "Call " << *nCalls; - LOG(INFO) << "RECEIVED DIGITS SIZE " << indata.size(); + // LOG(INFO) << "RECEIVED DIGITS SIZE " << indata.size(); }; auto loggerROW = [nCalls](ReadoutWinType const& row) { - LOG(INFO) << "RECEIVED READOUT WINDOWS " << row.size(); + // LOG(INFO) << "RECEIVED READOUT WINDOWS " << row.size(); + }; + auto loggerPatterns = [nCalls](PatternType const& patterns) { + // LOG(INFO) << "RECEIVED PATTERNS " << patterns.size(); + }; + auto loggerErrors = [nCalls](ErrorType const& errors) { + // LOG(INFO) << "RECEIVED PATTERNS " << patterns.size(); }; return MakeRootTreeWriterSpec("TOFDigitWriter", "tofdigits.root", @@ -58,6 +68,11 @@ DataProcessorSpec getTOFDigitWriterSpec(bool useMC) // the preprocessor only increments the call count, we keep this functionality // of the original implementation MakeRootTreeWriterSpec::Preprocessor{preprocessor}, + BranchDefinition<HeaderType>{InputSpec{"digitheader", gDataOriginTOF, "DIGITHEADER", 0}, + "TOFHeader", + "tofdigitheader-branch-name", + 1, + loggerH}, BranchDefinition<OutputType>{InputSpec{"digits", gDataOriginTOF, "DIGITS", 0}, "TOFDigit", "tofdigits-branch-name", @@ -68,6 +83,16 @@ DataProcessorSpec getTOFDigitWriterSpec(bool useMC) "rowindow-branch-name", 1, loggerROW}, + BranchDefinition<PatternType>{InputSpec{"patterns", gDataOriginTOF, "PATTERNS", 0}, + "TOFPatterns", + "patterns-branch-name", + 1, + loggerPatterns}, + BranchDefinition<ErrorType>{InputSpec{"errors", gDataOriginTOF, "ERRORS", 0}, + "TOFErrors", + "errors-branch-name", + (writeErr ? 1 : 0), // one branch if mc labels enabled + loggerErrors}, BranchDefinition<LabelsType>{InputSpec{"labels", gDataOriginTOF, "DIGITSMCTR", 0}, "TOFDigitMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled diff --git a/Detectors/TOF/workflow/src/TOFRawWriterSpec.cxx b/Detectors/TOF/workflow/src/TOFRawWriterSpec.cxx index f98d83ae1b92f..832e18b4995f2 100644 --- a/Detectors/TOF/workflow/src/TOFRawWriterSpec.cxx +++ b/Detectors/TOF/workflow/src/TOFRawWriterSpec.cxx @@ -10,9 +10,10 @@ /// @file TODRawWriterSpec.cxx -#include "TOFWorkflow/TOFRawWriterSpec.h" +#include "TOFWorkflowUtils/TOFRawWriterSpec.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" #include "DetectorsRaw/HBFUtils.h" #include "TOFBase/Geo.h" #include "CommonUtils/StringUtils.h" @@ -75,8 +76,9 @@ void RawWriter::run(ProcessingContext& pc) std::vector<std::vector<o2::tof::Digit>> digitWindows; for (int i = 0; i < nwindow; i += nwindowperorbit) { // encode 3 tof windows (1 orbit) - if (verbosity) + if (verbosity) { printf("----------\nwindow = %d - %d\n----------\n", i, i + nwindowperorbit - 1); + } digitWindows.clear(); @@ -84,8 +86,9 @@ void RawWriter::run(ProcessingContext& pc) for (int j = i; j < i + nwindowperorbit; j++) { if (j < nwindow) { digitRO.clear(); - for (int id = 0; id < row->at(j).size(); id++) + for (int id = 0; id < row->at(j).size(); id++) { digitRO.push_back((*digits)[row->at(j).first() + id]); + } digitWindows.push_back(digitRO); } else { digitWindows.push_back(emptyWindow); diff --git a/Detectors/TOF/workflow/src/cluster-writer-commissioning.cxx b/Detectors/TOF/workflow/src/cluster-writer-commissioning.cxx new file mode 100644 index 0000000000000..b304e2d0003be --- /dev/null +++ b/Detectors/TOF/workflow/src/cluster-writer-commissioning.cxx @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TOFWorkflowUtils/TOFClusterWriterSplitterSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.push_back(ConfigParamSpec{"ntf", o2::framework::VariantType::Int, 1, {"number of timeframe written for output file"}}); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + auto ntf = cfgc.options().get<int>("ntf"); + wf.emplace_back(o2::framework::getTOFClusterWriterSplitterSpec(ntf)); + + return wf; +} diff --git a/Detectors/TOF/workflow/src/digit-writer-commissioning.cxx b/Detectors/TOF/workflow/src/digit-writer-commissioning.cxx new file mode 100644 index 0000000000000..86751ea43fa24 --- /dev/null +++ b/Detectors/TOF/workflow/src/digit-writer-commissioning.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TOFWorkflowUtils/TOFDigitWriterSplitterSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.push_back(ConfigParamSpec{"ntf", o2::framework::VariantType::Int, 1, {"number of timeframe written for output file"}}); + workflowOptions.push_back(ConfigParamSpec{"write-decoding-errors", o2::framework::VariantType::Bool, false, {"trace errors in digits output when decoding"}}); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + auto ntf = cfgc.options().get<int>("ntf"); + auto write_err = cfgc.options().get<bool>("write-decoding-errors"); + wf.emplace_back(o2::framework::getTOFDigitWriterSplitterSpec(ntf, write_err)); + return wf; +} diff --git a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx new file mode 100644 index 0000000000000..aaa303de91e00 --- /dev/null +++ b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TOFWorkflowUtils/EntropyEncoderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + std::vector<ConfigParamSpec> options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get<std::string>("configKeyValues")); + wf.emplace_back(o2::tof::getEntropyEncoderSpec()); + return wf; +} diff --git a/Detectors/TPC/CMakeLists.txt b/Detectors/TPC/CMakeLists.txt index ccc16941d64b3..a07460ecae1c0 100644 --- a/Detectors/TPC/CMakeLists.txt +++ b/Detectors/TPC/CMakeLists.txt @@ -15,3 +15,4 @@ add_subdirectory(simulation) add_subdirectory(monitor) add_subdirectory(workflow) add_subdirectory(qc) +add_subdirectory(spacecharge) diff --git a/Detectors/TPC/base/include/TPCBase/CDBInterface.h b/Detectors/TPC/base/include/TPCBase/CDBInterface.h index 476ca0b78a025..3d186e2eb8730 100644 --- a/Detectors/TPC/base/include/TPCBase/CDBInterface.h +++ b/Detectors/TPC/base/include/TPCBase/CDBInterface.h @@ -260,7 +260,7 @@ class CDBStorage void setJIRA(std::string_view jira) { - mMetaData["Intervention"] = jira; + mMetaData["JIRA"] = jira; } void setComment(std::string_view comment) diff --git a/Detectors/TPC/base/include/TPCBase/CRU.h b/Detectors/TPC/base/include/TPCBase/CRU.h index e92751fb7dcd0..55e6224512a68 100644 --- a/Detectors/TPC/base/include/TPCBase/CRU.h +++ b/Detectors/TPC/base/include/TPCBase/CRU.h @@ -82,14 +82,15 @@ inline GEMstack CRU::gemStack() const { const int reg = int(region()); - if (reg < CRUperIROC) + if (reg < CRUperIROC) { return GEMstack::IROCgem; - else if (reg - CRUperIROC < CRUperPartition) + } else if (reg - CRUperIROC < CRUperPartition) { return GEMstack::OROC1gem; - else if (reg - CRUperIROC - CRUperPartition < CRUperPartition) + } else if (reg - CRUperIROC - CRUperPartition < CRUperPartition) { return GEMstack::OROC2gem; - else + } else { return GEMstack::OROC3gem; + } } } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/base/include/TPCBase/CalArray.h b/Detectors/TPC/base/include/TPCBase/CalArray.h index 1536162b57b12..6cab619159606 100644 --- a/Detectors/TPC/base/include/TPCBase/CalArray.h +++ b/Detectors/TPC/base/include/TPCBase/CalArray.h @@ -17,13 +17,13 @@ #include <string> #include <numeric> #include <type_traits> -#include <boost/format.hpp> - -#include "FairLogger.h" #include "TPCBase/Mapper.h" -using boost::format; +#ifndef GPUCA_ALIGPUCODE +#include "FairLogger.h" +#include <boost/format.hpp> +#endif namespace o2 { @@ -146,6 +146,8 @@ class CalArray void initData(); }; +#ifndef GPUCA_ALIGPUCODE + // ===| pad region etc. initialisation |======================================== template <class T> void CalArray<T>::initData() @@ -156,21 +158,21 @@ void CalArray<T>::initData() case PadSubset::ROC: { mData.resize(ROC(mPadSubsetNumber).rocType() == RocType::IROC ? mapper.getPadsInIROC() : mapper.getPadsInOROC()); if (mName.empty()) { - setName(boost::str(format("ROC_%1$02d") % mPadSubsetNumber)); + setName(boost::str(boost::format("ROC_%1$02d") % mPadSubsetNumber)); } break; } case PadSubset::Partition: { mData.resize(mapper.getPartitionInfo(mPadSubsetNumber % mapper.getNumberOfPartitions()).getNumberOfPads()); if (mName.empty()) { - setName(boost::str(format("Partition_%1$03d") % mPadSubsetNumber)); + setName(boost::str(boost::format("Partition_%1$03d") % mPadSubsetNumber)); } break; } case PadSubset::Region: { mData.resize(mapper.getPadRegionInfo(mPadSubsetNumber % mapper.getNumberOfPadRegions()).getNumberOfPads()); if (mName.empty()) { - setName(boost::str(format("Region_%1$03d") % mPadSubsetNumber)); + setName(boost::str(boost::format("Region_%1$03d") % mPadSubsetNumber)); } break; } @@ -229,7 +231,7 @@ template <class T> inline const CalArray<T>& CalArray<T>::operator*=(const CalArray<T>& other) { if (!((mPadSubset == other.mPadSubset) && (mPadSubsetNumber == other.mPadSubsetNumber))) { - LOG(ERROR) << "pad subste type of the objects it not compatible"; + LOG(ERROR) << "pad subset type of the objects it not compatible"; return *this; } for (size_t i = 0; i < mData.size(); ++i) { @@ -243,11 +245,16 @@ template <class T> inline const CalArray<T>& CalArray<T>::operator/=(const CalArray<T>& other) { if (!((mPadSubset == other.mPadSubset) && (mPadSubsetNumber == other.mPadSubsetNumber))) { - LOG(ERROR) << "pad subste type of the objects it not compatible"; + LOG(ERROR) << "pad subset type of the objects it not compatible"; return *this; } for (size_t i = 0; i < mData.size(); ++i) { - mData[i] /= other.getValue(i); + if (other.getValue(i) != 0) { + mData[i] /= other.getValue(i); + } else { + mData[i] = 0; + LOG(ERROR) << "Division by 0 detected! Value was set to 0."; + } } return *this; } @@ -293,6 +300,9 @@ inline const CalArray<T>& CalArray<T>::operator/=(const T& val) } using CalROC = CalArray<float>; + +#endif // GPUCA_ALIGPUCODE + } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/base/include/TPCBase/CalDet.h b/Detectors/TPC/base/include/TPCBase/CalDet.h index 6e4511d0839c1..f00794beb08a7 100644 --- a/Detectors/TPC/base/include/TPCBase/CalDet.h +++ b/Detectors/TPC/base/include/TPCBase/CalDet.h @@ -14,19 +14,19 @@ #include <memory> #include <vector> #include <string> -#include <boost/format.hpp> -#include <boost/range/combine.hpp> #include <cassert> -#include <FairLogger.h> - #include "DataFormatsTPC/Defs.h" #include "TPCBase/Mapper.h" #include "TPCBase/ROC.h" #include "TPCBase/Sector.h" #include "TPCBase/CalArray.h" -using boost::format; +#ifndef GPUCA_ALIGPUCODE +#include <Framework/Logger.h> +#include <boost/format.hpp> +#include <boost/range/combine.hpp> +#endif namespace o2 { @@ -41,6 +41,8 @@ class CalDet public: CalDet() = default; + CalDet(CalDet const&) = default; + CalDet& operator=(CalDet const&) = default; ~CalDet() = default; CalDet(PadSubset padSusbset) : mName{"PadCalibrationObject"}, mData{}, mPadSubset{padSusbset} { initData(); } @@ -82,6 +84,12 @@ class CalDet const CalDet& operator*=(const T& val); const CalDet& operator/=(const T& val); + template <class U> + friend CalDet<U> operator+(const CalDet<U>&, const CalDet<U>&); + + template <class U> + friend CalDet<U> operator-(const CalDet<U>&, const CalDet<U>&); + private: std::string mName; ///< name of the object std::vector<CalType> mData; ///< internal CalArrays @@ -174,6 +182,8 @@ inline const T CalDet<T>::getValue(const CRU cru, const size_t row, const size_t return T{}; } +#ifndef GPUCA_ALIGPUCODE // hide from GPU standalone compilation + //______________________________________________________________________________ template <class T> inline const CalDet<T>& CalDet<T>::operator+=(const CalDet& other) @@ -282,6 +292,23 @@ inline const CalDet<T>& CalDet<T>::operator/=(const T& val) return *this; } +//______________________________________________________________________________ +template <class T> +CalDet<T> operator+(const CalDet<T>& c1, const CalDet<T>& c2) +{ + CalDet<T> ret(c1); + ret += c2; + return ret; +} + +//______________________________________________________________________________ +template <class T> +CalDet<T> operator-(const CalDet<T>& c1, const CalDet<T>& c2) +{ + CalDet<T> ret(c1); + ret -= c2; + return ret; +} // ===| Full detector initialisation |========================================== template <class T> void CalDet<T>::initData() @@ -311,10 +338,12 @@ void CalDet<T>::initData() for (size_t i = 0; i < size; ++i) { mData.push_back(CalType(mPadSubset, i)); - mData.back().setName(boost::str(format(frmt) % mName % i)); + mData.back().setName(boost::str(boost::format(frmt) % mName % i)); } } +#endif // GPUCA_ALIGPUCODE + using CalPad = CalDet<float>; } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/base/include/TPCBase/FECInfo.h b/Detectors/TPC/base/include/TPCBase/FECInfo.h index b975a49d72c72..72af837b31ca1 100644 --- a/Detectors/TPC/base/include/TPCBase/FECInfo.h +++ b/Detectors/TPC/base/include/TPCBase/FECInfo.h @@ -21,6 +21,11 @@ namespace tpc class FECInfo { public: + static constexpr int ChannelsPerSAMPA = 32; + static constexpr int SAMPAsPerFEC = 5; + static constexpr int ChannelsPerFEC = ChannelsPerSAMPA * SAMPAsPerFEC; + static constexpr int FECsPerSector = 91; + FECInfo() = default; FECInfo(unsigned char index, //unsigned char connector, @@ -33,7 +38,7 @@ class FECInfo unsigned char getIndex() const { return mIndex; } //const unsigned char getConnector() const { return mConnector; } // -> can be calculated from mSampaChannel and mSampaChip - //const unsigned char getChannel() const { return mChannel; } // -> can be calculated from mSampaChannel and mSampaChip + unsigned char getFECChannel() const { return mSampaChip * ChannelsPerSAMPA + mSampaChannel; } // -> can be calculated from mSampaChannel and mSampaChip unsigned char getSampaChip() const { return mSampaChip; } unsigned char getSampaChannel() const { return mSampaChannel; } @@ -58,6 +63,12 @@ class FECInfo static constexpr int sampaOnFEC(const int globalSAMPAId) { return (globalSAMPAId >> 5) & 7; } static constexpr int channelOnSAMPA(const int globalSAMPAId) { return globalSAMPAId & 31; } + /// calculate the sampa number from the channel number on the FEC (0-159) + static constexpr int sampaFromFECChannel(const int fecChannel) { return fecChannel / ChannelsPerSAMPA; } + + /// calculate the sampa channel number from the channel number on the FEC (0-159) + static constexpr int channelFromFECChannel(const int fecChannel) { return fecChannel % ChannelsPerSAMPA; } + static void sampaInfo(const int globalSAMPAId, int& fecInSector, int& sampaOnFEC, int& channelOnSAMPA) { fecInSector = (globalSAMPAId >> 8); diff --git a/Detectors/TPC/base/include/TPCBase/Mapper.h b/Detectors/TPC/base/include/TPCBase/Mapper.h index a51b041d7f1db..c8f5cd7ca8729 100644 --- a/Detectors/TPC/base/include/TPCBase/Mapper.h +++ b/Detectors/TPC/base/include/TPCBase/Mapper.h @@ -27,6 +27,8 @@ #include "TPCBase/PartitionInfo.h" #include "TPCBase/Sector.h" +#include "MathUtils/Cartesian.h" + // using o2::tpc::PadRegionInfo; // using o2::tpc::PartitionInfo; @@ -254,6 +256,21 @@ class Mapper return pos; } + /// sampa on FEC and channel on SAMPA from the cru and the raw FEC channel (0-79) in the half FEC + /// this is required for the link-based zero suppression + static constexpr void getSampaAndChannelOnFEC(const int cruID, const size_t rawFECChannel, int& sampaOnFEC, int& channelOnSAMPA) + { + constexpr int sampaMapping[10] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 2}; + constexpr int channelOffset[10] = {0, 16, 0, 16, 0, 0, 16, 0, 16, 16}; + + const int regionIter = cruID % 2; + const int istreamm = ((rawFECChannel % 10) / 2); + const int partitionStream = istreamm + regionIter * 5; + sampaOnFEC = sampaMapping[partitionStream]; + const int channel = (rawFECChannel % 2) + 2 * (rawFECChannel / 10); + channelOnSAMPA = channel + channelOffset[partitionStream]; + } + const PadCentre& padCentre(const int partition, const int fecInPartition, const int sampaOnFEC, const int channelOnSAMPA) const { @@ -300,6 +317,7 @@ class Mapper } int getNumberOfPadsInRowSector(int row) const { return mMapNumberOfPadsPerRow[row]; } + int getPadOffsetInRowSector(int row) const { return mMapPadOffsetPerRow[row]; } int getNumberOfPadsInRowROC(int roc, int row) const { return mMapNumberOfPadsPerRow[row + (roc % 72 >= getNumberOfIROCs()) * mNumberOfPadRowsIROC]; @@ -563,8 +581,9 @@ inline const DigitPos Mapper::findDigitPosFromLocalPosition(const LocalPosition3 for (const PadRegionInfo& padRegion : mMapPadRegionInfo) { cru = CRU(sec, padRegion.getRegion()); pad = padRegion.findPad(pos); - if (pad.isValid()) + if (pad.isValid()) { break; + } } return DigitPos(cru, pad); @@ -574,10 +593,11 @@ inline const DigitPos Mapper::findDigitPosFromGlobalPosition(const GlobalPositio { // ===| find sector |========================================================= float phi = std::atan2(pos.Y(), pos.X()); - if (phi < 0.) + if (phi < 0.) { phi += TWOPI; + } const unsigned char secNum = std::floor(phi / SECPHIWIDTH); - const float secPhi = secNum * SECPHIWIDTH + SECPHIWIDTH / 2.; + // const float secPhi = secNum * SECPHIWIDTH + SECPHIWIDTH / 2.; Sector sec(secNum + (pos.Z() < 0) * SECTORSPERSIDE); // ===| rotated position |==================================================== diff --git a/Detectors/TPC/base/include/TPCBase/PadROCPos.h b/Detectors/TPC/base/include/TPCBase/PadROCPos.h index f1520ccc374a0..cb76a725591db 100644 --- a/Detectors/TPC/base/include/TPCBase/PadROCPos.h +++ b/Detectors/TPC/base/include/TPCBase/PadROCPos.h @@ -89,10 +89,12 @@ class PadROCPos /// smaller operator bool operator<(const PadROCPos& other) const { - if (mROC < other.mROC) + if (mROC < other.mROC) { return true; - if (mROC == other.mROC && mPadPos < other.mPadPos) + } + if (mROC == other.mROC && mPadPos < other.mPadPos) { return true; + } return false; } diff --git a/Detectors/TPC/base/include/TPCBase/Painter.h b/Detectors/TPC/base/include/TPCBase/Painter.h index 41b1b87f224f1..bab3d989180ee 100644 --- a/Detectors/TPC/base/include/TPCBase/Painter.h +++ b/Detectors/TPC/base/include/TPCBase/Painter.h @@ -57,6 +57,19 @@ TCanvas* draw(const CalDet<T>& calDet, int nbins1D = 300, float xMin1D = 0, floa template <class T> TCanvas* draw(const CalArray<T>& calArray); +/// fill existing 2D histogram for CalDet object +/// \param h2D histogram to fill +/// \param CalDet object with data +/// \param side side which to get the histogram for +template <class T> +void fillHistogram2D(TH2& h2D, const CalDet<T>& calDet, Side side); + +/// fill existing 2D histogram for CalArray object +/// \param h2D histogram to fill +/// \param CalArray object with data +template <class T> +void fillHistogram2D(TH2& h2D, const CalArray<T>& calArray); + /// get 2D histogram for CalDet object /// \param CalDet object with data /// \param side side which to get the histogram for @@ -84,6 +97,20 @@ TH2* getHistogram2D(const CalArray<T>& calArray); template <class T> std::vector<TCanvas*> makeSummaryCanvases(const CalDet<T>& calDet, int nbins1D = 300, float xMin1D = 0, float xMax1D = 0, bool onlyFilled = true); +/// Create summary canvases for a CalDet object +/// +/// 1 Canvas with 2D and 1D distributions for each side +/// 1 Canvas with 2D distributions for all ROCs +/// 1 Canvas with 1D distributions for all ROCs +/// \param CalDet object to draw +/// \param nbins1D number of bins used for the 1D projections +/// \param xMin1D minimum value for 1D distribution (xMin = 0 and xMax = 0 for auto scaling) +/// \param xMax1D maximum value for 1D distribution (xMin = 0 and xMax = 0 for auto scaling) +/// \param fileName input file name +/// \param calPadNames comma separated list of names of the CalPad objects as stored in the file. +/// \return TCanvas containing CalDet content +std::vector<TCanvas*> makeSummaryCanvases(const std::string_view fileName, const std::string_view calPadNames, int nbins1D = 300, float xMin1D = 0, float xMax1D = 0, bool onlyFilled = true); + } // namespace painter } // namespace tpc diff --git a/Detectors/TPC/base/include/TPCBase/ParameterElectronics.h b/Detectors/TPC/base/include/TPCBase/ParameterElectronics.h index 6432b3c617bf4..3946084f72784 100644 --- a/Detectors/TPC/base/include/TPCBase/ParameterElectronics.h +++ b/Detectors/TPC/base/include/TPCBase/ParameterElectronics.h @@ -45,6 +45,13 @@ struct ParameterElectronics : public o2::conf::ConfigurableParamHelper<Parameter float ElectronCharge = 1.602e-19f; ///< Electron charge [C] DigitzationMode DigiMode = DigitzationMode::SubtractPedestal; ///< Digitization mode [full / ... ] + /// Average time from the start of the signal shaping to the COG of the sampled distribution + /// + /// Since the shaping of the electronic starts when the signals arrive, the cluster finder + /// reconstructs signals later in time. Due to the asymmetry of the signal, the average + /// shaping time is somewhat larger then the PeakingTime + float getAverageShapingTime() const { return PeakingTime + 35e-3f; } + O2ParamDef(ParameterElectronics, "TPCEleParam"); }; } // namespace tpc diff --git a/Detectors/TPC/base/include/TPCBase/Sector.h b/Detectors/TPC/base/include/TPCBase/Sector.h index 19a492f4a45b1..4a4fde29ff4ae 100644 --- a/Detectors/TPC/base/include/TPCBase/Sector.h +++ b/Detectors/TPC/base/include/TPCBase/Sector.h @@ -40,7 +40,7 @@ class Sector { public: // the number of sectors - static constexpr int MAXSECTOR = Constants::MAXSECTOR; + static constexpr int MAXSECTOR = constants::MAXSECTOR; /// constructor Sector() = default; diff --git a/Detectors/TPC/base/include/TPCBase/Utils.h b/Detectors/TPC/base/include/TPCBase/Utils.h index d3ff0d5e1e1fd..d2bc09914f25f 100644 --- a/Detectors/TPC/base/include/TPCBase/Utils.h +++ b/Detectors/TPC/base/include/TPCBase/Utils.h @@ -19,21 +19,18 @@ #include <vector> #include <string_view> +#include "TPCBase/CalDet.h" + class TObjArray; class TCanvas; class TH1; +class TChain; namespace o2 { namespace tpc { -template <class T> -class CalDet; - -template <class T> -class CalArray; - /// \namespace utils /// \brief Common utility functions /// @@ -49,7 +46,29 @@ const std::vector<std::string> tokenize(const std::string_view input, const std: TH1* getBinInfoXY(int& binx, int& biny, float& bincx, float& bincy); void addFECInfo(); void saveCanvases(TObjArray& arr, std::string_view outDir, std::string_view types = "png,pdf", std::string_view rootFileName = ""); +void saveCanvases(std::vector<TCanvas*> canvases, std::string_view outDir, std::string_view types = "png,pdf", std::string_view rootFileName = ""); void saveCanvas(TCanvas& c, std::string_view outDir, std::string_view types); +std::vector<CalPad*> readCalPads(const std::string_view fileName, const std::vector<std::string>& calPadNames); +std::vector<CalPad*> readCalPads(const std::string_view fileName, const std::string_view calPadNames); + +/// Merge cal pad objects from different files +/// +/// Requires that all objects have the same name in the differnet files. +/// Objects are simply added. +/// \param outputFileName name of the output file +/// \param inputFileNames input file names. Perforams file system 'ls' in case the string includes '.root'. Otherwise it assumes a text input file with line by line file names. +/// \param calPadNames comma separated list of names of the CalPad objects as stored in the file. +void mergeCalPads(std::string_view outputFileName, std::string_view inputFileNames, std::string_view calPadNames); + +/// Build a chain interpreting a command line argument +/// +/// Comman line argument can e.g. be +/// ls *.root +/// cat fileWithRootFiles.txt +/// \param command command to run +/// \param treeName name of the tree in the chain +/// \param treeTitle title of the tree +TChain* buildChain(std::string_view command, std::string_view treeName, std::string_view treeTitle); } // namespace utils } // namespace tpc diff --git a/Detectors/TPC/base/src/CDBInterface.cxx b/Detectors/TPC/base/src/CDBInterface.cxx index 612f129372dcb..b6fb55d2083ad 100644 --- a/Detectors/TPC/base/src/CDBInterface.cxx +++ b/Detectors/TPC/base/src/CDBInterface.cxx @@ -52,8 +52,9 @@ const CalPad& CDBInterface::getPedestals() loadNoiseAndPedestalFromFile(); } } else if (mUseDefaults) { - if (!mPedestals) + if (!mPedestals) { createDefaultPedestals(); + } } else { // return from CDB, assume that check for object existence are done there return getObjectFromCDB<CalPad>(CDBTypeMap.at(CDBType::CalPedestal)); @@ -75,8 +76,9 @@ const CalPad& CDBInterface::getNoise() loadNoiseAndPedestalFromFile(); } } else if (mUseDefaults) { - if (!mNoise) + if (!mNoise) { createDefaultNoise(); + } } else { // return from CDB, assume that check for object existence are done there return getObjectFromCDB<CalPad>(CDBTypeMap.at(CDBType::CalNoise)); @@ -98,8 +100,9 @@ const CalPad& CDBInterface::getGainMap() loadGainMapFromFile(); } } else if (mUseDefaults) { - if (!mGainMap) + if (!mGainMap) { createDefaultGainMap(); + } } else { // return from CDB, assume that check for object existence are done there return getObjectFromCDB<CalPad>(CDBTypeMap.at(CDBType::CalPadGainFull)); diff --git a/Detectors/TPC/base/src/Mapper.cxx b/Detectors/TPC/base/src/Mapper.cxx index 46abd930a4a39..3acc10d73edd2 100644 --- a/Detectors/TPC/base/src/Mapper.cxx +++ b/Detectors/TPC/base/src/Mapper.cxx @@ -191,8 +191,9 @@ void Mapper::load(const std::string& mappingDir) //readMappingFile(inputDir+"/Detectors/TPC/base/files/TABLE-OROC3.txt"); const char* aliceO2env = std::getenv("O2_ROOT"); - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/TPC/files"; } readMappingFile(inputDir + "/TABLE-IROC.txt"); diff --git a/Detectors/TPC/base/src/PadRegionInfo.cxx b/Detectors/TPC/base/src/PadRegionInfo.cxx index fe80e757125c1..6a3117ad53730 100644 --- a/Detectors/TPC/base/src/PadRegionInfo.cxx +++ b/Detectors/TPC/base/src/PadRegionInfo.cxx @@ -54,25 +54,29 @@ const PadPos PadRegionInfo::findPad(const LocalPosition2D& pos, const Side side const PadPos PadRegionInfo::findPad(const float localX, const float localY, const Side side /*=Side::A*/) const { - if (!isInRegion(localX)) + if (!isInRegion(localX)) { return PadPos(255, 255); + } // the pad coordinate system is for pad-side view. // on the A-Side one looks from the back-side, therefore // the localY-sign must be changed const float localYfactor = (side == Side::A) ? -1.f : 1.f; const unsigned int row = std::floor((localX - mRadiusFirstRow) / mPadHeight); - if (row >= mNumberOfPadRows) + if (row >= mNumberOfPadRows) { return PadPos(255, 255); + } const unsigned int npads = getPadsInRowRegion(row); const float padfloat = (npads / 2 * mPadWidth - localYfactor * localY) / mPadWidth; - if (padfloat < 0) + if (padfloat < 0) { return PadPos(255, 255); + } const unsigned int pad = static_cast<unsigned int>(padfloat); - if (pad >= npads) + if (pad >= npads) { return PadPos(255, 255); + } return PadPos(row, pad); } diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/base/src/Painter.cxx index ede2f413556d3..f818bad0e7add 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/base/src/Painter.cxx @@ -24,6 +24,7 @@ #include "TPCBase/CalDet.h" #include "TPCBase/CalArray.h" #include "TPCBase/Painter.h" +#include "TPCBase/Utils.h" using namespace o2::tpc; @@ -76,8 +77,9 @@ TCanvas* painter::draw(const CalDet<T>& calDet, int nbins1D, float xMin1D, float const auto val = calDet.getValue(roc, irow, ipad); const GlobalPosition2D pos = mapper.getPadCentre(PadROCPos(roc, irow, ipad)); const int bin = hist2D->FindBin(pos.X(), pos.Y()); - if (!hist2D->GetBinContent(bin)) + if (!hist2D->GetBinContent(bin)) { hist2D->SetBinContent(bin, val); + } hist1D->Fill(val); } } @@ -129,34 +131,66 @@ TCanvas* painter::draw(const CalArray<T>& calArray) //______________________________________________________________________________ template <class T> -TH2* painter::getHistogram2D(const CalDet<T>& calDet, Side side) +void painter::fillHistogram2D(TH2& h2D, const CalDet<T>& calDet, Side side) { static const Mapper& mapper = Mapper::instance(); - const auto title = calDet.getName().c_str(); - std::string name = calDet.getName(); - std::replace(name.begin(), name.end(), ' ', '_'); - const char side_name = (side == Side::A) ? 'A' : 'C'; - - auto h2D = new TH2F(Form("h_%cside_2D_%s", side_name, name.c_str()), - Form("%s (%c-Side);x (cm);y (cm)", title, side_name), - 300, -300, 300, 300, -300, 300); - for (ROC roc; !roc.looped(); ++roc) { - if (roc.side() != side) + if (roc.side() != side) { continue; + } + const int nrows = mapper.getNumberOfRowsROC(roc); for (int irow = 0; irow < nrows; ++irow) { const int npads = mapper.getNumberOfPadsInRowROC(roc, irow); for (int ipad = 0; ipad < npads; ++ipad) { const auto val = calDet.getValue(roc, irow, ipad); const GlobalPosition2D pos = mapper.getPadCentre(PadROCPos(roc, irow, ipad)); - const int bin = h2D->FindBin(pos.X(), pos.Y()); - if (!h2D->GetBinContent(bin)) - h2D->SetBinContent(bin, val); + const int bin = h2D.FindBin(pos.X(), pos.Y()); + if (!h2D.GetBinContent(bin)) { + h2D.SetBinContent(bin, val); + } } } } +} + +//______________________________________________________________________________ +template <class T> +void painter::fillHistogram2D(TH2& h2D, const CalArray<T>& calArray) +{ + static const Mapper& mapper = Mapper::instance(); + + const size_t position = calArray.getPadSubsetNumber(); + const PadSubset padSubset = calArray.getPadSubset(); + const int nrows = mapper.getNumberOfPadRows(padSubset, position); + + // ===| fill hist |=========================================================== + for (int irow = 0; irow < nrows; ++irow) { + const int padsInRow = mapper.getNumberOfPadsInRow(padSubset, position, irow); + for (int ipad = 0; ipad < padsInRow; ++ipad) { + const GlobalPadNumber pad = mapper.getPadNumber(padSubset, position, irow, ipad); + const auto val = calArray.getValue(pad); + const int cpad = ipad - padsInRow / 2; + h2D.Fill(irow, cpad, val); + } + } +} + +//______________________________________________________________________________ +template <class T> +TH2* painter::getHistogram2D(const CalDet<T>& calDet, Side side) +{ + const auto title = calDet.getName().c_str(); + std::string name = calDet.getName(); + std::replace(name.begin(), name.end(), ' ', '_'); + const char side_name = (side == Side::A) ? 'A' : 'C'; + + auto h2D = new TH2F(Form("h_%cside_2D_%s", side_name, name.c_str()), + Form("%s (%c-Side);x (cm);y (cm)", title, side_name), + 300, -300, 300, 300, -300, 300); + + fillHistogram2D(*h2D, calDet, side); return h2D; } @@ -168,11 +202,11 @@ TH2* painter::getHistogram2D(const CalArray<T>& calArray) static const Mapper& mapper = Mapper::instance(); const size_t position = calArray.getPadSubsetNumber(); - const PadSubset padSubset = calArray.getPadSubset(); + // ===| maximum number of rows and pads |===================================== const int nrows = mapper.getNumberOfPadRows(padSubset, position); - const int npads = mapper.getNumberOfPadsInRow(padSubset, position, nrows - 1); + const int npads = mapper.getNumberOfPadsInRow(padSubset, position, nrows - 1) + 6; // ===| create histogram |==================================================== const auto title = calArray.getName().c_str(); @@ -183,17 +217,8 @@ TH2* painter::getHistogram2D(const CalArray<T>& calArray) nrows, 0., nrows, npads, -npads / 2, npads / 2); - // ===| fill hist |=========================================================== - for (int irow = 0; irow < nrows; ++irow) { - const int padsInRow = mapper.getNumberOfPadsInRow(padSubset, position, irow); - for (int ipad = 0; ipad < padsInRow; ++ipad) { - const GlobalPadNumber pad = mapper.getPadNumber(padSubset, position, irow, ipad); - const auto val = calArray.getValue(pad); - const int cpad = ipad - padsInRow / 2; - hist->Fill(irow, cpad, val); - //printf("%d %d: %f\n", irow, cpad, (double)val); - } - } + fillHistogram2D(*hist, calArray); + return hist; } @@ -280,12 +305,33 @@ std::vector<TCanvas*> painter::makeSummaryCanvases(const CalDet<T>& calDet, int return vecCanvases; } +//============================================================================== +std::vector<TCanvas*> painter::makeSummaryCanvases(const std::string_view fileName, const std::string_view calPadNames, int nbins1D, float xMin1D, float xMax1D, bool onlyFilled) +{ + using namespace o2::tpc; + + const auto calPads = utils::readCalPads(fileName, calPadNames); + + std::vector<TCanvas*> vecCanvases; + + for (const auto calPad : calPads) { + auto canvases = makeSummaryCanvases(*calPad, nbins1D, xMin1D, xMax1D, onlyFilled); + for (auto c : canvases) { + vecCanvases.emplace_back(c); + } + } + + return vecCanvases; +} + // ===| explicit instantiations |=============================================== // this is required to force the compiler to create instances with the types // we usually would like to deal with template TCanvas* painter::draw<float>(const CalDet<float>& calDet, int, float, float); template std::vector<TCanvas*> painter::makeSummaryCanvases<float>(const CalDet<float>& calDet, int, float, float, bool); template TCanvas* painter::draw<float>(const CalArray<float>& calArray); +template void painter::fillHistogram2D<float>(TH2& h2D, const CalDet<float>& calDet, Side side); +template void painter::fillHistogram2D<float>(TH2& h2D, const CalArray<float>& calArray); template TH2* painter::getHistogram2D<float>(const CalDet<float>& calDet, Side side); template TH2* painter::getHistogram2D<float>(const CalArray<float>& calArray); diff --git a/Detectors/TPC/base/src/Utils.cxx b/Detectors/TPC/base/src/Utils.cxx index 4a2401c3eace2..6469639e34171 100644 --- a/Detectors/TPC/base/src/Utils.cxx +++ b/Detectors/TPC/base/src/Utils.cxx @@ -12,13 +12,17 @@ #include <regex> #include <string> #include <fmt/format.h> +#include <fmt/printf.h> +#include "TSystem.h" #include "TObject.h" #include "TObjArray.h" #include "TCanvas.h" #include "TH1.h" #include "TFile.h" +#include "TChain.h" +#include "Framework/Logger.h" #include "TPCBase/Mapper.h" #include "TPCBase/Utils.h" @@ -87,10 +91,12 @@ void utils::addFECInfo() const auto& mapper = Mapper::instance(); const int roc = h->GetUniqueID(); - if (roc < 0 || roc >= (int)ROC::MaxROC) + if (roc < 0 || roc >= (int)ROC::MaxROC) { return; - if (row < 0 || row >= (int)mapper.getNumberOfRowsROC(roc)) + } + if (row < 0 || row >= (int)mapper.getNumberOfRowsROC(roc)) { return; + } const int nPads = mapper.getNumberOfPadsInRowROC(roc, row); const int pad = cpad + nPads / 2; //printf("row %d, cpad %d, pad %d, nPads %d\n", row, cpad, pad, nPads); @@ -101,10 +107,13 @@ void utils::addFECInfo() const auto& fecInfo = mapper.getFECInfo(PadROCPos(roc, row, pad)); - std::string title("#splitline{#lower[.1]{#scale[.5]{"); - title += (roc / 18 % 2 == 0) ? "A" : "C"; - title += fmt::format("{:02d} ({:02d}) row: {:02d}, pad: {:03d}, globalpad: {:05d} (in roc)}}}{#scale[.5]{FEC: {:02d}, Chip: {:02d}, Chn: {:02d}, Value: {:.3f}}}", - roc % 18, roc, row, pad, channel, fecInfo.getIndex(), fecInfo.getSampaChip(), fecInfo.getSampaChannel(), binValue); + const std::string title = fmt::format( + "#splitline{{#lower[.1]{{#scale[.5]{{" + "{}{:02d} ({:02d}) row: {:02d}, pad: {:03d}, globalpad: {:05d} (in roc)" + "}}}}}}{{#scale[.5]{{FEC: " + "{:02d}, Chip: {:02d}, Chn: {:02d}, Value: {:.3f}" + "}}}}", + (roc / 18 % 2 == 0) ? "A" : "C", roc % 18, roc, row, pad, channel, fecInfo.getIndex(), fecInfo.getSampaChip(), fecInfo.getSampaChannel(), binValue); h->SetTitle(title.data()); } @@ -122,6 +131,16 @@ void utils::saveCanvases(TObjArray& arr, std::string_view outDir, std::string_vi } } +void utils::saveCanvases(std::vector<TCanvas*> canvases, std::string_view outDir, std::string_view types, std::string_view rootFileName) +{ + TObjArray arr; + for (auto c : canvases) { + arr.Add(c); + } + + saveCanvases(arr, outDir, types, rootFileName); +} + void utils::saveCanvas(TCanvas& c, std::string_view outDir, std::string_view types) { const auto typesVec = tokenize(types, ","); @@ -129,3 +148,87 @@ void utils::saveCanvas(TCanvas& c, std::string_view outDir, std::string_view typ c.SaveAs(fmt::format("{}/{}.{}", outDir, c.GetName(), type).data()); } } + +std::vector<CalPad*> utils::readCalPads(const std::string_view fileName, const std::vector<std::string>& calPadNames) +{ + std::vector<CalPad*> calPads(calPadNames.size()); + + std::unique_ptr<TFile> file(TFile::Open(fileName.data())); + if (!file || !file->IsOpen() || file->IsZombie()) { + return calPads; + } + + for (size_t iCalPad = 0; iCalPad < calPadNames.size(); ++iCalPad) { + file->GetObject(calPadNames[iCalPad].data(), calPads[iCalPad]); + } + + return calPads; +} + +std::vector<CalPad*> utils::readCalPads(const std::string_view fileName, const std::string_view calPadNames) +{ + auto calPadNamesVec = tokenize(calPadNames, ","); + return readCalPads(fileName, calPadNamesVec); +} + +//______________________________________________________________________________ +void utils::mergeCalPads(std::string_view outputFileName, std::string_view inputFileNames, std::string_view calPadNames) +{ + using namespace o2::tpc; + + const auto calPadNamesVec = utils::tokenize(calPadNames, ","); + + std::string_view cmd = "ls"; + if (inputFileNames.rfind(".root") == std::string_view::npos) { + cmd = "cat"; + } + auto files = gSystem->GetFromPipe(TString::Format("%s %s", cmd.data(), inputFileNames.data())); + std::unique_ptr<TObjArray> arrFiles(files.Tokenize("\n")); + + std::vector<CalPad*> mergedCalPads; + + for (auto ofile : *arrFiles) { + auto calPads = utils::readCalPads(ofile->GetName(), calPadNamesVec); + if (!calPads.size()) { + continue; + } + if (!mergedCalPads.size()) { + mergedCalPads = calPads; + } else { + for (size_t iCalPad = 0; iCalPad < calPads.size(); ++iCalPad) { + auto calPadName = calPadNamesVec[iCalPad]; + auto calPadMerged = mergedCalPads[iCalPad]; + calPadMerged->setName(calPadName); + auto calPadToMerge = calPads[iCalPad]; + + *calPadMerged += *calPadToMerge; + + delete calPadToMerge; + } + } + } + + std::unique_ptr<TFile> outFile(TFile::Open(outputFileName.data(), "recreate")); + for (auto calPad : mergedCalPads) { + outFile->WriteObject(calPad, calPad->getName().data()); + } +} + +//______________________________________________________________________________ +TChain* utils::buildChain(std::string_view command, std::string_view treeName, std::string_view treeTitle) +{ + const TString files = gSystem->GetFromPipe(command.data()); + std::unique_ptr<TObjArray> arrFiles(files.Tokenize("\n")); + if (!arrFiles->GetEntriesFast()) { + LOGP(error, "command '{}' did not return results", command); + return nullptr; + } + + auto c = new TChain(treeName.data(), treeTitle.data()); + for (const auto o : *arrFiles) { + LOGP(info, "Adding file '{}'", o->GetName()); + c->AddFile(o->GetName()); + } + + return c; +} diff --git a/Detectors/TPC/base/src/ZeroSuppress.cxx b/Detectors/TPC/base/src/ZeroSuppress.cxx index 100a9c5671e14..70cb7ca21b6c2 100644 --- a/Detectors/TPC/base/src/ZeroSuppress.cxx +++ b/Detectors/TPC/base/src/ZeroSuppress.cxx @@ -27,6 +27,7 @@ #include "DataFormatsTPC/Helpers.h" using namespace o2::tpc; +using namespace o2::tpc::constants; void ZeroSuppress::process() { @@ -62,7 +63,7 @@ void ZeroSuppress::DecodeZSPages(gsl::span<const ZeroSuppressedContainer8kb>* z0 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)&inputPage; auto orbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdh); - int timeBin = (_timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / Constants::LHCBCPERTIMEBIN; + int timeBin = (_timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; unsigned int ct = 0; startPtr += (sizeof(z0Container->rdh) + sizeof(z0Container->hdr)); // move to first time bin diff --git a/Detectors/TPC/base/test/testTPCBase.cxx b/Detectors/TPC/base/test/testTPCBase.cxx index dc00b9ecb19bb..37b0bca8129b1 100644 --- a/Detectors/TPC/base/test/testTPCBase.cxx +++ b/Detectors/TPC/base/test/testTPCBase.cxx @@ -23,7 +23,7 @@ namespace tpc BOOST_AUTO_TEST_CASE(Point3D_test) { - Point3D<double> testpoint(2., 3., 4.); + math_utils::Point3D<double> testpoint(2., 3., 4.); BOOST_CHECK_CLOSE(testpoint.X(), 2., 1E-12); BOOST_CHECK_CLOSE(testpoint.Y(), 3., 1E-12); BOOST_CHECK_CLOSE(testpoint.Z(), 4., 1E-12); diff --git a/Detectors/TPC/base/test/testTPCMapper.cxx b/Detectors/TPC/base/test/testTPCMapper.cxx index bfd701298282b..54682abb179ee 100644 --- a/Detectors/TPC/base/test/testTPCMapper.cxx +++ b/Detectors/TPC/base/test/testTPCMapper.cxx @@ -59,8 +59,9 @@ BOOST_AUTO_TEST_CASE(Mapper_complex_test1) std::string line; const char* aliceO2env = std::getenv("O2_ROOT"); std::string inputDir = " "; - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/TPC/files"; std::string file = inputDir + mappingTables[i]; diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 6ecb2524b54cc..2cb2ba2a08347 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -36,11 +36,6 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/CalibPadGainTracks.h include/TPCCalibration/FastHisto.h) -o2_add_executable(calib-pedestal - COMPONENT_NAME tpc - SOURCES run/calib-pedestal.cxx - PUBLIC_LINK_LIBRARIES O2::TPCCalibration O2::Framework O2::DPLUtils) - o2_add_test_root_macro(macro/comparePedestalsAndNoise.C PUBLIC_LINK_LIBRARIES O2::TPCBase LABELS tpc) diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibParam.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibParam.h index 48ad4eb80cb4b..4f1eeb9be16bb 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibParam.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibParam.h @@ -51,7 +51,7 @@ static constexpr float RowX[NPadRows] = { ///< x-posi #else // not defined TPC_RUN2 /// TPC geometric constants for Run 3+ -static constexpr int NPadRows = o2::tpc::Constants::MAXGLOBALPADROW; +static constexpr int NPadRows = o2::tpc::constants::MAXGLOBALPADROW; static constexpr int NROCTypes = 4; static constexpr int NRowsPerROC[NROCTypes] = {63, 34, 30, 25}; static constexpr int NRowsAccumulated[NROCTypes] = {63, 97, 127, 152}; diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h index fed4d99745a02..0ff93475cb93e 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h @@ -197,7 +197,7 @@ class TrackInterpolation std::vector<TPCClusterResiduals> mClRes{}; ///< residuals for each available TPC cluster of all tracks // cache - std::array<CacheStruct, Constants::MAXGLOBALPADROW> mCache{{}}; ///< caching positions, covariances and angles for track extrapolations and interpolation + std::array<CacheStruct, constants::MAXGLOBALPADROW> mCache{{}}; ///< caching positions, covariances and angles for track extrapolations and interpolation // helpers std::unique_ptr<TPCFastTransform> mFastTransform{}; ///< TPC cluster transformation diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index 3f95bafcb01bd..126b03ba589ae 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -17,8 +17,10 @@ #include "SpacePoints/TrackInterpolation.h" #include "TPCBase/ParameterElectronics.h" #include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTPC/Defs.h" #include <fairlogger/Logger.h> +#include <set> using namespace o2::tpc; @@ -48,6 +50,7 @@ void TrackInterpolation::process() LOG(error) << "Initialization not yet done. Aborting..."; return; } + reset(); #ifdef TPC_RUN2 // processing will not work if the run 2 geometry is defined in the parameter class SpacePointsCalibParam.h @@ -122,12 +125,12 @@ bool TrackInterpolation::trackPassesQualityCuts(const o2::dataformats::TrackTPCI // track has a match in TRD or TOF if (trkTPC.getNClusterReferences() < param::MinTPCNCls || trkITS.getNumberOfClusters() < param::MinITSNCls) { - printf("TPC clusters (%i), ITS clusters(%i)\n", trkTPC.getNClusterReferences(), trkITS.getNumberOfClusters()); + LOG(DEBUG) << "TPC clusters (" << trkTPC.getNClusterReferences() << "), ITS clusters(" << trkITS.getNumberOfClusters() << ")"; return false; } if (trkTPC.getChi2() / trkTPC.getNClusterReferences() > param::MaxTPCChi2 || trkITS.getChi2() / trkITS.getNumberOfClusters() > param::MaxITSChi2) { - printf("TPC reduced chi2 (%.2f), ITS reduced chi2 (%.2f)\n", trkTPC.getChi2() / trkTPC.getNClusterReferences(), trkITS.getChi2() / trkITS.getNumberOfClusters()); + LOG(DEBUG) << "TPC reduced chi2 (" << trkTPC.getChi2() / trkTPC.getNClusterReferences() << "), ITS reduced chi2 (" << trkITS.getChi2() / trkITS.getNumberOfClusters() << ")"; return false; } } else { @@ -153,7 +156,7 @@ bool TrackInterpolation::interpolateTrackITSTOF(const o2::dataformats::MatchInfo const auto& clTOF = mTOFClustersArray[matchTOF.getTOFClIndex()]; //const int clTOFSec = (TMath::ATan2(-clTOF.getY(), -clTOF.getX()) + o2::constants::math::PI) * o2::constants::math::Rad2Deg * 0.05; // taken from TOF cluster class as there is no const getter for the sector const int clTOFSec = clTOF.getCount(); - const float clTOFAlpha = o2::utils::Sector2Angle(clTOFSec); + const float clTOFAlpha = o2::math_utils::sector2Angle(clTOFSec); const auto& trkTPC = mTPCTracksArray[matchITSTPC.getRefTPC()]; const auto& trkITS = mITSTracksArray[matchITSTPC.getRefITS()]; auto trkWork = trkITS.getParamOut(); @@ -177,7 +180,7 @@ bool TrackInterpolation::interpolateTrackITSTOF(const o2::dataformats::MatchInfo mCache[row].clAvailable = 1; mCache[row].clY = clTPCYZ[0]; mCache[row].clZ = clTPCYZ[1]; - mCache[row].clAngle = o2::utils::Sector2Angle(sector); + mCache[row].clAngle = o2::math_utils::sector2Angle(sector); } // first extrapolate through TPC and store track position at each pad row @@ -189,7 +192,7 @@ bool TrackInterpolation::interpolateTrackITSTOF(const o2::dataformats::MatchInfo LOG(DEBUG) << "Failed to rotate track during first extrapolation"; return false; } - if (!propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], o2::constants::physics::MassPionCharged, mMaxSnp, mMaxStep, mMatCorr)) { + if (!propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], mMaxSnp, mMaxStep, mMatCorr)) { LOG(DEBUG) << "Failed on first extrapolation"; return false; } @@ -209,13 +212,13 @@ bool TrackInterpolation::interpolateTrackITSTOF(const o2::dataformats::MatchInfo return false; } //float ca, sa; - //o2::utils::sincosf(clTOFAlpha, sa, ca); + //o2::math_utils::sincos(clTOFAlpha, sa, ca); //float clTOFX = clTOF.getX() * ca + clTOF.getY() * sa; // cluster x in sector coordinate frame //std::array<float, 2> clTOFYZ{ -clTOF.getX() * sa + clTOF.getY() * ca, clTOF.getZ() }; // cluster y and z in sector coordinate frame float clTOFX = clTOF.getX(); std::array<float, 2> clTOFYZ{clTOF.getY(), clTOF.getZ()}; std::array<float, 3> clTOFCov{mSigYZ2TOF, 0.f, mSigYZ2TOF}; // assume no correlation between y and z and equal cluster error sigma^2 = (3cm)^2 / 12 - if (!propagator->PropagateToXBxByBz(trkWork, clTOFX, o2::constants::physics::MassPionCharged, mMaxSnp, mMaxStep, mMatCorr)) { + if (!propagator->PropagateToXBxByBz(trkWork, clTOFX, mMaxSnp, mMaxStep, mMatCorr)) { LOG(DEBUG) << "Failed final propagation to TOF radius"; return false; } @@ -233,7 +236,7 @@ bool TrackInterpolation::interpolateTrackITSTOF(const o2::dataformats::MatchInfo LOG(DEBUG) << "Failed to rotate track during back propagation"; return false; } - if (!propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], o2::constants::physics::MassPionCharged, mMaxSnp, mMaxStep, mMatCorr)) { + if (!propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], mMaxSnp, mMaxStep, mMatCorr)) { LOG(DEBUG) << "Failed on back propagation"; //printf("trkX(%.2f), clX(%.2f), clY(%.2f), clZ(%.2f), alphaTOF(%.2f)\n", trkWork.getX(), param::RowX[iRow], clTOFYZ[0], clTOFYZ[1], clTOFAlpha); return false; @@ -271,7 +274,7 @@ bool TrackInterpolation::interpolateTrackITSTOF(const o2::dataformats::MatchInfo res.setZ(mCache[iRow].z[Int]); res.setPhi(mCache[iRow].phi[Int]); res.setTgl(mCache[iRow].tgl[Int]); - res.sec = o2::utils::Angle2Sector(mCache[iRow].clAngle); + res.sec = o2::math_utils::angle2Sector(mCache[iRow].clAngle); res.dRow = deltaRow; res.row = iRow; mClRes.push_back(std::move(res)); @@ -309,10 +312,10 @@ bool TrackInterpolation::extrapolateTrackITS(const o2::its::TrackITS& trkITS, co const auto& cl = trkTPC.getCluster(mTPCTracksClusIdx, iCl, *mTPCClusterIdxStruct, sector, row); float x = 0, y = 0, z = 0; mFastTransform->TransformIdeal(sector, row, cl.getPad(), cl.getTime(), x, y, z, clusterTimeBinOffset); - if (!trk.rotate(o2::utils::Sector2Angle(sector))) { + if (!trk.rotate(o2::math_utils::sector2Angle(sector))) { return false; } - if (!propagator->PropagateToXBxByBz(trk, x, o2::constants::physics::MassPionCharged, mMaxSnp, mMaxStep, mMatCorr)) { + if (!propagator->PropagateToXBxByBz(trk, x, mMaxSnp, mMaxStep, mMatCorr)) { return false; } TPCClusterResiduals res; @@ -322,7 +325,7 @@ bool TrackInterpolation::extrapolateTrackITS(const o2::its::TrackITS& trkITS, co res.setZ(trk.getZ()); res.setPhi(trk.getSnp()); res.setTgl(trk.getTgl()); - res.sec = o2::utils::Angle2Sector(trk.getAlpha()); + res.sec = o2::math_utils::angle2Sector(trk.getAlpha()); res.dRow = row - rowPrev; res.row = row; rowPrev = row; diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx index dc610f2a9efe7..76d23c027831d 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx @@ -18,7 +18,7 @@ #include "SpacePoints/TrackResiduals.h" #include "CommonConstants/MathConstants.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "TMatrixDSym.h" #include "TDecompChol.h" @@ -510,8 +510,8 @@ void TrackResiduals::buildLocalResidualTreesFromRun2Data() fillLocalResidualsTrees(); } - printf("Rejected due to Nclusters(%i), HelixFit(%i), qpt(%i), validation(%i)\n", nRejCl, nRejHelix, nRejQpt, nRejValidation); - printf("validation failed %i times because of fraction of rej. cls and %i times because of rms and %i rest\n", counterTrkValidation[1], counterTrkValidation[2], counterTrkValidation[0]); + //printf("Rejected due to Nclusters(%i), HelixFit(%i), qpt(%i), validation(%i)\n", nRejCl, nRejHelix, nRejQpt, nRejValidation); + //printf("validation failed %i times because of fraction of rej. cls and %i times because of rms and %i rest\n", counterTrkValidation[1], counterTrkValidation[2], counterTrkValidation[0]); LOG(info) << "Accepted " << nTracksSelected << " tracks. With outliers it would be " << nTracksSelectedWithOutliers; dumpTracks(debugOutliers); writeLocalResidualTreesToFile(); @@ -892,7 +892,7 @@ void TrackResiduals::processSectorResiduals(int iSec) #endif // sort in voxel increasing order - o2::math_utils::math_base::SortData(binData, binIndices); + o2::math_utils::SortData(binData, binIndices); if (mPrintMem) { printMem(); } @@ -1007,7 +1007,7 @@ void TrackResiduals::processVoxelResiduals(std::vector<float>& dy, std::vector<f std::array<float, 7> zResults; resVox.flags = 0; std::vector<size_t> indices(dz.size()); - if (!o2::math_utils::math_base::LTMUnbinned(dz, indices, zResults, mLTMCut)) { + if (!o2::math_utils::LTMUnbinned(dz, indices, zResults, mLTMCut)) { LOG(debug) << "failed trimming input array for voxel " << getGlbVoxBin(resVox.bvox); return; } @@ -1713,12 +1713,12 @@ float TrackResiduals::fitPoly1Robust(std::vector<float>& x, std::vector<float>& } std::array<float, 7> yResults; std::vector<size_t> indY(nPoints); - if (!o2::math_utils::math_base::LTMUnbinned(y, indY, yResults, cutLTM)) { + if (!o2::math_utils::LTMUnbinned(y, indY, yResults, cutLTM)) { return -1; } // rearrange used events in increasing order - o2::math_utils::math_base::Reorder(y, indY); - o2::math_utils::math_base::Reorder(x, indY); + o2::math_utils::Reorder(y, indY); + o2::math_utils::Reorder(x, indY); // // 1st fit to get crude slope int nPointsUsed = std::lrint(yResults[0]); @@ -1732,15 +1732,15 @@ float TrackResiduals::fitPoly1Robust(std::vector<float>& x, std::vector<float>& ycm[i] = y[i] - (a + b * x[i]); } std::vector<size_t> indices(nPoints); - o2::math_utils::math_base::SortData(ycm, indices); - o2::math_utils::math_base::Reorder(ycm, indices); - o2::math_utils::math_base::Reorder(y, indices); - o2::math_utils::math_base::Reorder(x, indices); + o2::math_utils::SortData(ycm, indices); + o2::math_utils::Reorder(ycm, indices); + o2::math_utils::Reorder(y, indices); + o2::math_utils::Reorder(x, indices); // // robust estimate of sigma after crude slope correction float sigMAD = getMAD2Sigma({ycm.begin() + vecOffset, ycm.begin() + vecOffset + nPointsUsed}); // find LTM estimate matching to sigMAD, keaping at least given fraction - if (!o2::math_utils::math_base::LTMUnbinnedSig(ycm, indY, yResults, mMinFracLTM, sigMAD, true)) { + if (!o2::math_utils::LTMUnbinnedSig(ycm, indY, yResults, mMinFracLTM, sigMAD, true)) { return -1; } // final fit diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h index 4ea939dafbf97..4928604b3b490 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h @@ -119,6 +119,15 @@ class CalibPulser : public CalibRawBase mXmaxQtot = max; } + /// set minimum Qtot for the signal + void setMinQtot(float minQtot) { mMinimumQtot = minQtot; } + + /// set minimum Qmax for the signal + void setMinQmax(float minQmax) { mMinimumQmax = minQmax; } + + /// set time bin range around the one with the maximum number of entries + void setMaxTimeBinRange(int max) { mMaxTimeBinRange = max; } + /// Analyse the buffered pulser information void analyse(); @@ -155,23 +164,27 @@ class CalibPulser : public CalibRawBase float mXminWidth; ///< xmin of width reference histogram float mXmaxWidth; ///< xmax of width reference histogram - int mFirstTimeBin; ///< first time bin used in analysis - int mLastTimeBin; ///< first time bin used in analysis - int mADCMin; ///< minimum adc value - int mADCMax; ///< maximum adc value - int mNumberOfADCs; ///< number of adc values (mADCMax-mADCMin+1) - int mPeakIntMinus; ///< lower bound from maximum for the peak integration, mean and std dev. calc - int mPeakIntPlus; ///< upper bound from maximum for the peak integration, mean and std dev. calc - int mMinimumQtot; ///< minimal Qtot accepted as pulser signal - CalPad mT0; ///< CalDet object with pulser time information - CalPad mWidth; ///< CalDet object with pulser pulse width information - CalPad mQtot; ///< CalDet object with pulser Qtot information + int mFirstTimeBin; ///< first time bin used in analysis + int mLastTimeBin; ///< first time bin used in analysis + int mADCMin; ///< minimum adc value + int mADCMax; ///< maximum adc value + int mNumberOfADCs; ///< number of adc values (mADCMax-mADCMin+1) + int mPeakIntMinus; ///< lower bound from maximum for the peak integration, mean and std dev. calc + int mPeakIntPlus; ///< upper bound from maximum for the peak integration, mean and std dev. calc + float mMinimumQtot; ///< minimal Qtot accepted as pulser signal + float mMinimumQmax; ///< minimal Qtot accepted as pulser signal + int mMaxTimeBinRange; ///< number of time bins around the one with the maximum number of entries arouch which to analyse + CalPad mT0; ///< CalDet object with pulser time information + CalPad mWidth; ///< CalDet object with pulser pulse width information + CalPad mQtot; ///< CalDet object with pulser Qtot information const CalPad* mPedestal; //!< Pedestal calibration object const CalPad* mNoise; //!< Noise calibration object std::map<PadROCPos, VectorType> mPulserData; //!< ADC data to calculate pulser information + std::array<std::vector<size_t>, ROC::MaxROC> mTimeBinEntries{}; //!< entries per time bin per ROC + PtrVectorType mT0Histograms; //!< T0 histogramgs per ROC and pad PtrVectorType mWidthHistograms; //!< Width histogramgs per ROC and pad PtrVectorType mQtotHistograms; //!< Qtot histogramgs per ROC and pad @@ -181,6 +194,14 @@ class CalibPulser : public CalibRawBase float mT0{0.f}; float mWidth{0.f}; float mQtot{0.f}; + + bool isValid() const { return (mT0 > 0.f) || (mWidth > 0.f) || (mQtot > 0.f); } + }; + + /// element pair + struct ElemPair { + size_t first{0}; + size_t last{0}; }; /// create or return a histogram for a specific ROC @@ -214,7 +235,10 @@ class CalibPulser : public CalibRawBase /// process the adc values of one pad /// extract the T0, width, qmax and qtot /// \param adcData vector with ADC values per pad - PulserData processPadData(const PadROCPos& padROCPos, const VectorType& adcData); + PulserData processPadData(const PadROCPos& padROCPos, const VectorType& adcData, const ElemPair& range); + + /// maximum time bin entries per ROC + std::array<ElemPair, ROC::MaxROC> getTimeRangeROCs(); /// dummy reset void resetEvent() final {} diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibPulserParam.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibPulserParam.h index a928650565188..8aac9fedd51a4 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibPulserParam.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibPulserParam.h @@ -24,22 +24,23 @@ namespace tpc { struct CalibPulserParam : public o2::conf::ConfigurableParamHelper<CalibPulserParam> { - int NbinsT0{200}; ///< Number of bins for T0 reference histogram - float XminT0{-2}; ///< xmin of T0 reference histogram - float XmaxT0{2}; ///< xmax of T0 reference histogram - int NbinsQtot{200}; ///< Number of bins for Qtot reference histogram - float XminQtot{50}; ///< xmin of Qtot reference histogram - float XmaxQtot{700}; ///< xmax of Qtot reference histogram - int NbinsWidth{100}; ///< Number of bins for width reference histogram - float XminWidth{0.1}; ///< xmin of width reference histogram - float XmaxWidth{5.1}; ///< xmax of width reference histogram - int FirstTimeBin{10}; ///< first time bin used in analysis - int LastTimeBin{490}; ///< first time bin used in analysis - int ADCMin{5}; ///< minimum adc value - int ADCMax{1023}; ///< maximum adc value - int PeakIntMinus{2}; ///< lower bound from maximum for the peak integration, mean and std dev. calc - int PeakIntPlus{2}; ///< upper bound from maximum for the peak integration, mean and std dev. calc - int MinimumQtot{20}; ///< minimal Qtot accepted as pulser signal + int NbinsT0{200}; ///< Number of bins for T0 reference histogram + float XminT0{-2}; ///< xmin of T0 reference histogram + float XmaxT0{2}; ///< xmax of T0 reference histogram + int NbinsQtot{200}; ///< Number of bins for Qtot reference histogram + float XminQtot{50}; ///< xmin of Qtot reference histogram + float XmaxQtot{700}; ///< xmax of Qtot reference histogram + int NbinsWidth{100}; ///< Number of bins for width reference histogram + float XminWidth{0.1}; ///< xmin of width reference histogram + float XmaxWidth{5.1}; ///< xmax of width reference histogram + int FirstTimeBin{10}; ///< first time bin used in analysis + int LastTimeBin{490}; ///< first time bin used in analysis + int ADCMin{5}; ///< minimum adc value + int ADCMax{1023}; ///< maximum adc value + int PeakIntMinus{2}; ///< lower bound from maximum for the peak integration, mean and std dev. calc + int PeakIntPlus{2}; ///< upper bound from maximum for the peak integration, mean and std dev. calc + float MinimumQtot{20}; ///< minimal Qtot accepted as pulser signal + float MinimumQmax{10}; ///< minimal Qmax accepted as pulser signal O2ParamDef(CalibPulserParam, "TPCCalibPulser"); }; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h index 8436ee14bbbc3..a1f84dee74fc4 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h @@ -129,6 +129,9 @@ class CalibRawBase /// get present event number size_t getPresentEventNumber() const { return mPresentEventNumber; } + /// return number of events + int getNumberOfEvents() const { return mRawReaderCRUManager.getNumberOfEvents(); } + /// check if present event is complete bool isPresentEventComplete() const { return mRawReaderCRUManager.isEventComplete(mPresentEventNumber); } @@ -204,8 +207,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEvent(int eventNumber) //______________________________________________________________________________ inline CalibRawBase::ProcessStatus CalibRawBase::processEventGBT() { - if (!mGBTFrameContainers.size()) + if (!mGBTFrameContainers.size()) { return ProcessStatus::NoReaders; + } resetEvent(); // loop over raw readers, fill digits for 500 time bins and process @@ -231,8 +235,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventGBT() // row is local in region (CRU) const int row = digi.getRow(); const int pad = digi.getPad(); - if (row == 255 || pad == 255) + if (row == 255 || pad == 255) { continue; + } int rowOffset = 0; switch (mPadSubset) { @@ -285,8 +290,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventGBT() //______________________________________________________________________________ inline CalibRawBase::ProcessStatus CalibRawBase::processEventRawReader(int eventNumber) { - if (!mRawReaders.size()) + if (!mRawReaders.size()) { return ProcessStatus::NoReaders; + } resetEvent(); // loop over raw readers, fill digits for 500 time bins and process @@ -317,8 +323,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventRawReader(int event o2::tpc::PadPos padPos; while (std::shared_ptr<std::vector<uint16_t>> data = reader->getNextData(padPos)) { - if (!data) + if (!data) { continue; + } mProcessedTimeBins = std::max(mProcessedTimeBins, data->size()); @@ -331,8 +338,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventRawReader(int event // row is local in region (CRU) const int row = padPos.getRow(); const int pad = padPos.getPad(); - if (row == 255 || pad == 255) + if (row == 255 || pad == 255) { continue; + } int timeBin = 0; for (const auto& signalI : *data) { @@ -387,8 +395,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventRawReader(int event //______________________________________________________________________________ inline CalibRawBase::ProcessStatus CalibRawBase::processEventRawReaderCRU(int eventNumber) { - if (!mRawReaderCRUManager.getNumberOfReaders()) + if (!mRawReaderCRUManager.getNumberOfReaders()) { return ProcessStatus::NoReaders; + } resetEvent(); mRawReaderCRUManager.init(); @@ -422,94 +431,8 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventRawReaderCRU(int ev const bool skipEvent = mSkipIncomplete && !isPresentEventComplete(); if (!skipEvent) { - for (auto& reader_ptr : mRawReaderCRUManager.getReaders()) { - auto reader = reader_ptr.get(); - - LOG(INFO) << "Processing event number " << eventNumber << " (" << mNevents << ") - RawReader#: " << processedReaders << " ptr: " << reader; - - if (eventNumber >= 0) { - mPresentEventNumber = eventNumber; - } else if (eventNumber == -1) { - if (mPresentEventNumber == std::numeric_limits<size_t>::max()) { - mPresentEventNumber = 0; - } else { - mPresentEventNumber = (reader->getEventNumber() + 1) % reader->getNumberOfEvents(); - } - } else if (eventNumber == -2) { - auto readerNumber = reader->getEventNumber(); - if (readerNumber > 0) { - mPresentEventNumber = readerNumber - 1; - } else { - mPresentEventNumber = reader->getNumberOfEvents() - 1; - } - } - reader->setEventNumber(mPresentEventNumber); - - // process data - reader->processLinks(); - hasData = true; - - const auto& cru = reader->getCRU(); - const int roc = cru.roc(); - - // TODO: OROC case needs subtraction of number of pad rows in IROC - const PadRegionInfo& regionInfo = mMapper.getPadRegionInfo(cru.region()); - const PartitionInfo& partInfo = mMapper.getPartitionInfo(cru.partition()); - - //LOG(INFO) << " Found ADC values: " << reader->getADCMap().size(); - // loop over pads - for (const auto& pair : reader->getADCMap()) { - const auto& padPos = pair.first; - const auto& dataVector = pair.second; - - // TODO: fix this? - mProcessedTimeBins = std::max(mProcessedTimeBins, dataVector.size()); - - // row is local in region (CRU) - const int row = padPos.getRow(); - const int pad = padPos.getPad(); - if (row == 255 || pad == 255) - continue; - - int timeBin = 0; - - int rowOffset = 0; - switch (mPadSubset) { - case PadSubset::ROC: { - rowOffset = regionInfo.getGlobalRowOffset(); - rowOffset -= (cru.isOROC()) * nRowIROC; - break; - } - case PadSubset::Region: { - break; - } - case PadSubset::Partition: { - rowOffset = regionInfo.getGlobalRowOffset(); - rowOffset -= partInfo.getGlobalRowOffset(); - break; - } - } - - //const FECInfo& fecInfo = mMapper.getFECInfo(PadROCPos(roc, row, pad)); - - for (const auto& signalI : dataVector) { - // modify row depending on the calibration type used - const float signal = float(signalI); - //printf("Call update: %d, %d (%d), %d, %d, %.3f -- cru: %03d, reg: %02d -- FEC: %02d, Chip: %02d, Chn: %02d\n", roc, row, rowOffset, pad, timeBin, signal, cru.number(), cru.region(), fecInfo.getIndex(), fecInfo.getSampaChip(), fecInfo.getSampaChannel()); - updateCRU(cru, row, pad, timeBin, signal); - updateROC(roc, row + rowOffset, pad, timeBin, signal); - ++timeBin; - hasData = true; - } - } - LOG(INFO) << "Found time bins: " << mProcessedTimeBins << "\n"; - - reader->clearMap(); - - // notify that one raw reader processing finalized for this event - endReader(); - ++processedReaders; - } + hasData = true; + mRawReaderCRUManager.processEvent(mPresentEventNumber, [this]() { endReader(); }); // set status, don't overwrite decision if (!hasData) { return ProcessStatus::NoMoreData; @@ -600,8 +523,9 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventDigitTree(int event const PadRegionInfo& regionInfo = mMapper.getPadRegionInfo(cru.region()); const PartitionInfo& partInfo = mMapper.getPartitionInfo(cru.partition()); - if (row == 255 || pad == 255) + if (row == 255 || pad == 255) { continue; + } int rowOffset = 0; switch (mPadSubset) { @@ -636,6 +560,7 @@ inline CalibRawBase::ProcessStatus CalibRawBase::processEventDigitTree(int event } endEvent(); + endReader(); ++mNevents; LOG(INFO) << "Present event number : " << mPresentEventNumber; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibTreeDump.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibTreeDump.h index b8dbfdf362369..6bb611bf40ebb 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibTreeDump.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibTreeDump.h @@ -53,28 +53,31 @@ class CalibTreeDump /// Add CalDet object template <typename T> - void add(const CalDet<T>& calDet) + void add(CalDet<T>* calDet) { - mCalDetObjects.push_back(calDet); + mCalDetObjects.emplace_back(calDet); } //void add(const CalDet<DataTypes>& calDet) { mCalDetObjects.push_back(calDet); } /// Add CalArray objects template <typename T> - void add(const CalArray<T>& calArray) + void add(CalArray<T>* calArray) { - mCalArrayObjects.push_back(calArray); + mCalArrayObjects.emplace_back(calArray); } /// Set adding of FEE mapping to the tree void setAddFEEInfo(bool add = true) { mAddFEEInfo = add; } + /// Add CalPad objects from a file + void addCalPads(const std::string_view file, const std::string_view calPadNames); + /// Dump the registered calibration data to file void dumpToFile(const std::string filename = "CalibTree.root"); private: - std::vector<DataTypes> mCalDetObjects{}; ///< array of CalDet objects - std::vector<DataTypes> mCalArrayObjects{}; ///< array of CalArray objects + std::vector<DataTypes*> mCalDetObjects{}; ///< array of CalDet objects + std::vector<DataTypes*> mCalArrayObjects{}; ///< array of CalArray objects bool mAddFEEInfo{false}; ///< add front end electronics mappings std::vector<float> mTraceLengthIROC; ///< trace lengths IROC std::vector<float> mTraceLengthOROC; ///< trace lengths OROC diff --git a/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h b/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h index a2a22b2c84d13..284e0f50c9119 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h @@ -125,6 +125,18 @@ class DigitDump : public CalibRawBase /// return digits for specific sector std::vector<Digit>& getDigits(int sector) { return mDigits[sector]; } + /// directly add a digit + void addDigit(const CRU& cru, const float signal, const int rowInSector, const int padInRow, const int timeBin) + { + mDigits[cru.sector()].emplace_back(cru, signal, rowInSector, padInRow, timeBin); + } + + /// initialize + void initInputOutput(); + + /// End event function + void endEvent() final; + private: std::unique_ptr<CalPad> mPedestal{}; ///< CalDet object with pedestal information std::unique_ptr<CalPad> mNoise{}; ///< CalDet object with noise @@ -152,12 +164,6 @@ class DigitDump : public CalibRawBase /// load noise and pedestal void loadNoiseAndPedestal(); - /// initialize - void initInputOutput(); - - /// End event function - void endEvent() final; - /// dummy reset void resetEvent() final {} }; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/FastHisto.h b/Detectors/TPC/calibration/include/TPCCalibration/FastHisto.h index 6d9b5a857f00c..7318eda963ee5 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/FastHisto.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/FastHisto.h @@ -17,7 +17,7 @@ #define AliceO2_TPC_FastHisto_H //o2 includes -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "Framework/Logger.h" #include <sstream> #include <vector> @@ -94,7 +94,7 @@ class FastHisto /// \return this function returns the truncated mean for the filled histogram /// \param low lower truncation range /// \param high upper truncation range - math_utils::math_base::StatisticsData getStatisticsData(const float low = 0.05f, const float high = 0.6f) const; + math_utils::StatisticsData getStatisticsData(const float low = 0.05f, const float high = 0.6f) const; /// \return this function returns the bin content for given index /// \param index the index (bin) for which the content is returned @@ -186,7 +186,7 @@ inline void FastHisto<T>::fill(T val, T weight) template <class T> inline void FastHisto<T>::print(const int prec) const { - const math_utils::math_base::StatisticsData data = getStatisticsData(); + const math_utils::StatisticsData data = getStatisticsData(); LOGP(info, "\n Entries: {}", std::accumulate(mBinCont.begin(), mBinCont.end(), 0)); LOGP(info, "Truncated Mean: {}", data.mCOG); LOGP(info, "Standard Deviation: {}", data.mStdDev); @@ -243,9 +243,9 @@ inline void FastHisto<T>::print(const int prec) const } template <class T> -inline math_utils::math_base::StatisticsData FastHisto<T>::getStatisticsData(const float low, const float high) const +inline math_utils::StatisticsData FastHisto<T>::getStatisticsData(const float low, const float high) const { - math_utils::math_base::StatisticsData data{}; + math_utils::StatisticsData data{}; // in case something went wrong the COG is the histogram lower limit data.mCOG = mXmin; if (mBinCont.size() == 0) { diff --git a/Detectors/TPC/calibration/macro/drawPulser.C b/Detectors/TPC/calibration/macro/drawPulser.C index d499010138a3d..f4f1d28a87964 100644 --- a/Detectors/TPC/calibration/macro/drawPulser.C +++ b/Detectors/TPC/calibration/macro/drawPulser.C @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #if !defined(__CLING__) || defined(__ROOTCLING__) +#include <array> #include "TROOT.h" #include "TMath.h" #include "TH2.h" @@ -16,21 +17,25 @@ #include "TPCBase/CalDet.h" #include "TPCBase/Painter.h" #include "TPCBase/Utils.h" +#include "TPCBase/Mapper.h" #include "TPad.h" #include "TCanvas.h" #include "TH1F.h" #include "TString.h" +#include "TStyle.h" #endif /// Open pedestalFile and retrieve noise and pedestal values /// Draw then in separate canvases and add an executable to be able to add /// FEC information to the title -TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir = "") +TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir = "", int type = 0, bool normalizeQtot = true) { if ((mode != 0) && (mode != 1)) { return 0x0; } + gStyle->SetNumberContours(100); + TObjArray* arrCanvases = new TObjArray; arrCanvases->SetName("Pulser"); @@ -45,11 +50,60 @@ TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir f.GetObject("Width", calWidth); f.GetObject("Qtot", calQtot); + if (normalizeQtot) { + // normalize Qtot to pad area + const auto& mapper = o2::tpc::Mapper::instance(); + std::array<float, 152> mapRowPadArea; + { + int iregion = 0; + auto padRegion = &mapper.getPadRegionInfo(iregion); + for (size_t i = 0; i < mapRowPadArea.size(); ++i) { + if (i >= padRegion->getGlobalRowOffset() + padRegion->getNumberOfPadRows()) { + padRegion = &mapper.getPadRegionInfo(++iregion); + } + mapRowPadArea[i] = padRegion->getPadWidth() * padRegion->getPadHeight(); + //printf("row: %3i, region: %i, size: %.2f\n", i, iregion, mapRowPadArea[i]); + } + } + + auto& calArraysQtot = calQtot->getData(); + for (size_t iROC = 0; iROC < calArraysQtot.size(); ++iROC) { + auto& calArray = calArraysQtot[iROC]; + auto& qTotROC = calArray.getData(); + int offset = iROC < 36 ? 0 : mapper.getPadsInIROC(); + for (size_t iPad = 0; iPad < qTotROC.size(); ++iPad) { + const auto& padPos = mapper.padPos(iPad + offset); + auto& val = qTotROC[iPad]; + val /= mapRowPadArea[padPos.getRow()]; + } + } + } + // mode 1 handling if (mode == 1) { - auto arrT0 = painter::makeSummaryCanvases(*calT0, 100, 238.f, 240.f); - auto arrWidth = painter::makeSummaryCanvases(*calWidth, 100, 0.38f, 0.57f); - auto arrQtot = painter::makeSummaryCanvases(*calQtot, 100, 20.f, 280.f); + float tMin = 238.f; + float tMax = 240.f; + float wMin = 0.38f; + float wMax = 0.57f; + float qMin = 20.f; + float qMax = 280.f; + if (normalizeQtot) { + qMin = 100.f; + qMax = 350.f; + } + + if (type == 1) { + tMin = 425.f; + tMax = 485.f; + wMin = 0.6; + wMax = 0.8; + qMin = 5.f; + qMax = 500.f; + } + + auto arrT0 = painter::makeSummaryCanvases(*calT0, 100, tMin, tMax); + auto arrWidth = painter::makeSummaryCanvases(*calWidth, 100, wMin, wMax); + auto arrQtot = painter::makeSummaryCanvases(*calQtot, 100, qMin, qMax); for (auto c : arrT0) { arrCanvases->Add(c); diff --git a/Detectors/TPC/calibration/macro/extractGainMap.C b/Detectors/TPC/calibration/macro/extractGainMap.C index 7476bd0f0846d..35ea78192e8df 100644 --- a/Detectors/TPC/calibration/macro/extractGainMap.C +++ b/Detectors/TPC/calibration/macro/extractGainMap.C @@ -107,7 +107,7 @@ void calibGainMacroTrk(const float momMin, const float momMax, const std::string ClusterNativeAccess clusterIndex{}; std::unique_ptr<ClusterNative[]> clusterBuffer{}; - MCLabelContainer clusterMCBuffer; + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clusterMCBuffer; memset(&clusterIndex, 0, sizeof(clusterIndex)); CalibPadGainTracks cGain{}; diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.C b/Detectors/TPC/calibration/macro/preparePedestalFiles.C index d5ace146b26f8..91d2e8a13e962 100644 --- a/Detectors/TPC/calibration/macro/preparePedestalFiles.C +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.C @@ -14,6 +14,7 @@ #include <fstream> #include <iostream> #include <tuple> +#include <numeric> #include "TFile.h" #include "TROOT.h" @@ -45,7 +46,7 @@ struct LinkInfo { using ValueArray = std::array<uint32_t, 80>; using DataMap = std::map<LinkInfo, ValueArray>; -void writeValues(const std::string_view fileName, const DataMap& map); +void writeValues(const std::string_view fileName, const DataMap& map, bool onlyFilled = false); int getHWChannel(int sampa, int channel, int regionIter); /// convert float to integer with fixed precision and max number of digits @@ -72,7 +73,7 @@ constexpr float fixedSizeToFloat(uint32_t value) return float(value) * FloatConversion; } -void preparePedestalFiles(const std::string_view pedestalFileName, const TString outputDir = "./", float sigmaNoise = 3, float minADC = 2, float pedestalOffset = 0) +void preparePedestalFiles(const std::string_view pedestalFileName, const TString outputDir = "./", float sigmaNoise = 3, float minADC = 2, float pedestalOffset = 0, bool onlyFilled = false) { static constexpr float FloatConversion = 1.f / float(1 << 2); @@ -126,13 +127,19 @@ void preparePedestalFiles(const std::string_view pedestalFileName, const TString const int globalLinkID = (fecInPartition % fecOffset) + dataWrapperID * 12; float pedestal = rocPedestal.getValue(ipad); + if ((pedestal > 0) && (pedestalOffset > pedestal)) { + printf("ROC: %2zu, pad: %3zu -- pedestal offset %.2f larger than the pedestal value %.2f. Pedestal and noise will be set to 0\n", iroc, ipad, pedestalOffset, pedestal); + } else { + pedestal -= pedestalOffset; + } + float noise = std::abs(rocNoise.getValue(ipad)); // it seems with the new fitting procedure, the noise can also be negative, since in gaus sigma is quadratic if ((pedestal < 0) || (pedestal > 1023) || (noise < 0) || (noise > 1023)) { printf("Bad pedestal or noise value in ROC %2zu, CRU %3d, fec in CRU: %2d, SAMPA: %d, channel: %2d, pedestal: %.4f, noise %.4f, setting both to 0\n", iroc, cruID, fecInPartition, sampa, sampaChannel, pedestal, noise); pedestal = 0; noise = 0; } - const float threshold = std::max(sigmaNoise * noise, minADC); + const float threshold = (noise > 0) ? std::max(sigmaNoise * noise, minADC) : 0; const int hwChannel = getHWChannel(sampa, sampaChannel, region % 2); // for debugging @@ -149,8 +156,8 @@ void preparePedestalFiles(const std::string_view pedestalFileName, const TString } } - writeValues((outputDir + "/pedestal_values.txt").Data(), pedestalValues); - writeValues((outputDir + "/threshold_values.txt").Data(), thresholdlValues); + writeValues((outputDir + "/pedestal_values.txt").Data(), pedestalValues, onlyFilled); + writeValues((outputDir + "/threshold_values.txt").Data(), thresholdlValues, onlyFilled); } /// return the hardware channel number as mapped in the CRU @@ -167,11 +174,16 @@ int getHWChannel(int sampa, int channel, int regionIter) /// write values of map to fileName /// -void writeValues(const std::string_view fileName, const DataMap& map) +void writeValues(const std::string_view fileName, const DataMap& map, bool onlyFilled) { std::ofstream str(fileName.data(), std::ofstream::out); for (const auto& [linkInfo, data] : map) { + if (onlyFilled) { + if (!std::accumulate(data.begin(), data.end(), uint32_t(0))) { + continue; + } + } std::string values; for (const auto& val : data) { if (values.size()) { diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.sh b/Detectors/TPC/calibration/macro/preparePedestalFiles.sh index d5ea493b26cf7..64e086b803e35 100755 --- a/Detectors/TPC/calibration/macro/preparePedestalFiles.sh +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.sh @@ -8,10 +8,12 @@ required arguments -i, --inputFile= : input file name optional arguments: --o, --outputDir= : set output directory for (default: ./) --m, --minADC= : minimal ADC value accepted for threshold (default: 2) --s, --sigmaNoise= : number of sigmas for the threshold (default: 3) --h, --help : show this help message" +-o, --outputDir= : set output directory for (default: ./) +-m, --minADC= : minimal ADC value accepted for threshold (default: 2) +-s, --sigmaNoise= : number of sigmas for the threshold (default: 3) +-p, --pedestalOffset= : pedestal offset value +-f, --onlyFilled : only write links which have data +-h, --help : show this help message" echo "$usage" } @@ -30,9 +32,11 @@ fileInfo= outputDir="./" minADC=2 sigmaNoise=3 +pedestalOffset=0 +onlyFilled=0 # ===| parse command line options |============================================= -OPTIONS=$(getopt -l "inputFile:,outputDir:,minADC:,sigmaNoise:,help" -o "i:o:t:m:s:h" -n "preparePedestalFiles.sh" -- "$@") +OPTIONS=$(getopt -l "inputFile:,outputDir:,minADC:,sigmaNoise:,pedestalOffset:,onlyFilled,help" -o "i:o:t:m:s:p:fh" -n "preparePedestalFiles.sh" -- "$@") if [ $? != 0 ] ; then usageAndExit @@ -47,6 +51,8 @@ while true; do -o|--outputDir) outputDir=$2; shift 2;; -m|--minADC) minADC=$2; shift 2;; -s|--sigmaNoise) sigmaNoise=$2; shift 2;; + -p|--pedestalOffset) pedestalOffset=$2; shift 2;; + -f|--onlyFilled) onlyFilled=1; shift;; -h|--help) usageAndExit;; *) echo "Internal error!" ; exit 1 ;; esac @@ -58,6 +64,6 @@ if [[ -z "$inputFile" ]]; then fi # ===| command building and execution |========================================= -cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/preparePedestalFiles.C+g'(\"$inputFile\",\"$outputDir\", $sigmaNoise, $minADC)'" +cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/preparePedestalFiles.C+g'(\"$inputFile\",\"$outputDir\", $sigmaNoise, $minADC, $pedestalOffset, $onlyFilled)'" echo "running: $cmd" eval $cmd diff --git a/Detectors/TPC/calibration/macro/runPedestal.C b/Detectors/TPC/calibration/macro/runPedestal.C index 80261d0936d0f..47d131b94c62c 100644 --- a/Detectors/TPC/calibration/macro/runPedestal.C +++ b/Detectors/TPC/calibration/macro/runPedestal.C @@ -19,13 +19,14 @@ #include "TPCCalibration/CalibRawBase.h" #endif -void runPedestal(std::vector<std::string_view> fileInfos, TString outputFileName = "", Int_t nevents = 100, Int_t adcMin = 0, Int_t adcMax = 1100, Int_t firstTimeBin = 0, Int_t lastTimeBin = 450, Int_t statisticsType = 0, uint32_t verbosity = 0, uint32_t debugLevel = 0, Int_t firstEvent = 0, Bool_t debugOutput = false) +void runPedestal(std::vector<std::string_view> fileInfos, TString outputFileName = "", Int_t nevents = 100, Int_t adcMin = 0, Int_t adcMax = 1100, Int_t firstTimeBin = 0, Int_t lastTimeBin = 450, Int_t statisticsType = 0, uint32_t verbosity = 0, uint32_t debugLevel = 0, Int_t firstEvent = 0, Bool_t debugOutput = false, Bool_t skipIncomplete = false) { using namespace o2::tpc; CalibPedestal ped; //(PadSubset::Region); ped.setADCRange(adcMin, adcMax); ped.setStatisticsType(StatisticsType(statisticsType)); ped.setTimeBinRange(firstTimeBin, lastTimeBin); + ped.setSkipIncompleteEvents(skipIncomplete); //ped.processEvent(); //ped.resetData(); @@ -35,14 +36,13 @@ void runPedestal(std::vector<std::string_view> fileInfos, TString outputFileName for (const auto& fileInfo : fileInfos) { ped.setupContainers(fileInfo.data(), verbosity, debugLevel); - for (Int_t i = firstEvent; i < firstEvent + nevents; ++i) { + const int neventsFile = ped.getNumberOfEvents(); + printf("number of events in files: %d\n", neventsFile); + int lastEvent = std::min(neventsFile, firstEvent + nevents); + + for (Int_t i = firstEvent; i < lastEvent; ++i) { status = ped.processEvent(i); std::cout << "Processing event " << i << " with status " << int(status) << '\n'; - if (status == CalibRawBase::ProcessStatus::IncompleteEvent) { - continue; - } else if (status != CalibRawBase::ProcessStatus::Ok) { - break; - } } } ped.analyse(); @@ -61,7 +61,6 @@ void runPedestal(std::vector<std::string_view> fileInfos, TString outputFileName debugFile.Append("/"); debugFile.Append("pedestals_debug.root"); TFile f(debugFile, "recreate"); - TObjArray arr(72); for (int i = 0; i < 72; ++i) { const auto& rocPedestal = calibPedestal.getCalArray(i); @@ -70,9 +69,8 @@ void runPedestal(std::vector<std::string_view> fileInfos, TString outputFileName } auto ch = ped.createControlHistogram(ROC(i)); - arr.Add(static_cast<TObject*>(ch)); + ch->Write(); } - arr.Write("histos", TObject::kSingleKey); f.Write(); } std::cout << "To display the pedestals run: root.exe $calibMacroDir/drawNoiseAndPedestal.C'(\"" << outputFileName << "\")'\n"; diff --git a/Detectors/TPC/calibration/macro/runPedestal.sh b/Detectors/TPC/calibration/macro/runPedestal.sh index e7d645fb23774..a36bb53a60d11 100755 --- a/Detectors/TPC/calibration/macro/runPedestal.sh +++ b/Detectors/TPC/calibration/macro/runPedestal.sh @@ -18,7 +18,7 @@ usage() { echo " -n, --nevents= : number of events to process (default: 1000)" echo " -m, --adcMin= : minimal ADC values accepted (default: 0)" echo " -x, --adcMax= : maximal ADC values accepted (default: 1100)" - echo " -s, --statType= : statistics type - 0: Gaus fit (default), 1: Mean and StdDev" + echo " -s, --statType= : statistics type - 0: Gaus fit (default), 1: Gaus fit fast, 2: Mean and StdDev" echo " -v, --verbosity= : set verbosity level for raw reader" echo " -d, --debugLevel= : set debug level for raw reader" echo " -w, --writeDebug : write debug output histograms" @@ -44,12 +44,13 @@ statisticsType=0 verbosity=0 debugLevel=0 writeDebug=0 +skipIncomplete=0 adcMin=0 adcMax=1100 # ===| parse command line options |============================================= -OPTIONS=$(getopt -l "fileInfo:,outputFile:,firstTimeBin:,lastTimeBin:,nevents:,adcMin:,adcMax:,statType:,verbosity:,debugLevel:,writeDebug,help" -o "i:o:t:f:l:n:m:x:s:v:d:wh" -n "runPedestal.sh" -- "$@") +OPTIONS=$(getopt -l "fileInfo:,outputFile:,firstTimeBin:,lastTimeBin:,nevents:,skipIncomplete,adcMin:,adcMax:,statType:,verbosity:,debugLevel:,writeDebug,help" -o "i:o:t:f:l:n:km:x:s:v:d:wh" -n "runPedestal.sh" -- "$@") if [ $? != 0 ] ; then usageAndExit @@ -65,6 +66,7 @@ while true; do -f|--firstTimeBin) firstTimeBin=$2; shift 2;; -l|--lastTimeBin) lastTimeBin=$2; shift 2;; -n|--nevents) nevents=$2; shift 2;; + -k|--skipIncomplete) skipIncomplete=1; shift;; -m|--adcMin) adcMin=$2; shift 2;; -x|--adcMax) adcMax=$2; shift 2;; -s|--statType) statisticsType=$2; shift 2;; @@ -93,8 +95,9 @@ fi fileInfo=$(echo $fileInfo | sed "s|^|{\"|;s|,|:$lastTimeBin\",\"|g;s|$|\"}|") # ===| command building and execution |========================================= -cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel, 0, $writeDebug)'" -#cmd="perf record -g -o perf.log root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel)'" -#cmd="valgrind --tool=callgrind --dump-instr=yes --dump-instr=yes root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel)'" +cmd="" +#cmd="valgrind --tool=callgrind --dump-instr=yes --dump-instr=yes" +#cmd="perf record -g -o perf.log" +cmd="$cmd root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel, 0, $writeDebug, $skipIncomplete)'" echo "running: $cmd" eval $cmd diff --git a/Detectors/TPC/calibration/macro/runPulser.C b/Detectors/TPC/calibration/macro/runPulser.C index b59d03eb02f99..d1b8b4898a701 100644 --- a/Detectors/TPC/calibration/macro/runPulser.C +++ b/Detectors/TPC/calibration/macro/runPulser.C @@ -21,7 +21,7 @@ void runPulser(std::vector<std::string_view> fileInfos, TString outputFileName = Int_t adcMin = 0, Int_t adcMax = 1100, Int_t firstTimeBin = 0, Int_t lastTimeBin = 500, TString pedestalAndNoiseFile = "", - uint32_t verbosity = 0, uint32_t debugLevel = 0) + uint32_t verbosity = 0, uint32_t debugLevel = 0, int type = 0) { using namespace o2::tpc; // ===| set up calibration class |============================================ @@ -29,6 +29,13 @@ void runPulser(std::vector<std::string_view> fileInfos, TString outputFileName = calib.setADCRange(adcMin, adcMax); calib.setTimeBinRange(firstTimeBin, lastTimeBin); calib.setDebugLevel(); + calib.setQtotBinning(140, 22, 302); + if (type == 1) { + calib.setQtotBinning(150, 2, 302); + calib.setMinQtot(8); + calib.setMinQmax(6); + calib.setMaxTimeBinRange(6); + } //calib.setDebugLevel(debugLevel); // ===| load pedestal if requested |========================================== diff --git a/Detectors/TPC/calibration/macro/runPulser.sh b/Detectors/TPC/calibration/macro/runPulser.sh index 2fe16884a81c6..3a6e637008059 100755 --- a/Detectors/TPC/calibration/macro/runPulser.sh +++ b/Detectors/TPC/calibration/macro/runPulser.sh @@ -21,6 +21,7 @@ usage() { echo " -p, --pedestalFile= : pedestal and noise file" echo " -v, --verbosity= : set verbosity level for raw reader" echo " -d, --debugLevel= : set debug level for raw reader" + echo " -t, --type= : run type (pulser: 0, laser: 1)" echo " -h, --help : show this help message" } @@ -45,8 +46,10 @@ debugLevel=0 adcMin=0 adcMax=1100 +dataType=0 + # ===| parse command line options |============================================= -OPTIONS=$(getopt -l "fileInfo:,outputFile:,firstTimeBin:,lastTimeBin:,nevents:,adcMin:,adcMax:,pedestalFile:,verbosity:,debugLevel:,help" -o "i:o:f:l:n:m:x:p:v:d:h" -n "runPulser.sh" -- "$@") +OPTIONS=$(getopt -l "fileInfo:,outputFile:,firstTimeBin:,lastTimeBin:,nevents:,adcMin:,adcMax:,pedestalFile:,verbosity:,debugLevel:,type:,help" -o "i:o:f:l:n:m:x:p:v:d:t:h" -n "runPulser.sh" -- "$@") if [ $? != 0 ] ; then usageAndExit @@ -67,6 +70,7 @@ while true; do -p|--pedestalFile) pedestalFile=$2; shift 2;; -v|--verbosity) verbosity=$2; shift 2;; -d|--debugLevel) debugLevel=$2; shift 2;; + -t|--type) dataType=$2; shift 2;; -h|--help) usageAndExit;; *) echo "Internal error!" ; exit 1 ;; esac @@ -89,6 +93,6 @@ fi fileInfo=$(echo $fileInfo | sed "s|^|{\"|;s|,|:$lastTimeBin\",\"|g;s|$|\"}|") # ===| command building and execution |========================================= -cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPulser.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, \"$pedestalFile\", $verbosity, $debugLevel)'" +cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPulser.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, \"$pedestalFile\", $verbosity, $debugLevel, $dataType)'" echo "running: $cmd" eval $cmd diff --git a/Detectors/TPC/calibration/run/calib-pedestal.cxx b/Detectors/TPC/calibration/run/calib-pedestal.cxx deleted file mode 100644 index 0bd23979cb80c..0000000000000 --- a/Detectors/TPC/calibration/run/calib-pedestal.cxx +++ /dev/null @@ -1,236 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <fmt/format.h> -#include "Framework/WorkflowSpec.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/DataSpecUtils.h" -#include "Framework/ControlService.h" -#include "Framework/Logger.h" -#include "Framework/ConfigParamSpec.h" -#include "Framework/CompletionPolicy.h" -#include "Framework/CompletionPolicyHelpers.h" -#include "DPLUtils/RawParser.h" -#include "Headers/DataHeader.h" -#include "CommonUtils/ConfigurableParam.h" -#include "TPCCalibration/CalibPedestal.h" -#include "TPCReconstruction/RawReaderCRU.h" -#include <vector> -#include <string> -#include "DetectorsRaw/RDHUtils.h" - -using namespace o2::framework; -using RDHUtils = o2::raw::RDHUtils; - -// customize the completion policy -void customize(std::vector<o2::framework::CompletionPolicy>& policies) -{ - using o2::framework::CompletionPolicy; - policies.push_back(CompletionPolicyHelpers::defineByName("calib-pedestal", CompletionPolicy::CompletionOp::Consume)); -} - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector<ConfigParamSpec>& workflowOptions) -{ - std::vector<ConfigParamSpec> options{ - {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, - {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}}; - - std::swap(workflowOptions, options); -} - -#include "Framework/runDataProcessing.h" - -using RDH = o2::header::RAWDataHeader; - -void printHeader() -{ - LOGP(debug, "{:>5} {:>4} {:>4} {:>4} {:>3} {:>4} {:>10} {:>5} {:>1} {:>10}", "PkC", "pCnt", "fId", "Mem", "CRU", "GLID", "HBOrbit", "HB BC", "s", "Trg"); -} - -void printRDH(const RDH& rdh) -{ - const int globalLinkID = int(RDHUtils::getLinkID(rdh)) + (((rdh.word1 >> 32) >> 28) * 12); - - LOGP(debug, "{:>5} {:>4} {:>4} {:>4} {:>3} {:>4} {:>10} {:>5} {:>1} {:#010X}", (uint64_t)RDHUtils::getPacketCounter(rdh), (uint64_t)RDHUtils::getPageCounter(rdh), - (uint64_t)RDHUtils::getFEEID(rdh), (uint64_t)RDHUtils::getMemorySize(rdh), (uint64_t)RDHUtils::getCRUID(rdh), (uint64_t)globalLinkID, - (uint64_t)RDHUtils::getHeartBeatOrbit(rdh), (uint64_t)RDHUtils::getHeartBeatBC(rdh), (uint64_t)RDHUtils::getStop(rdh), (uint64_t)RDHUtils::getTriggerType(rdh)); -} - -WorkflowSpec defineDataProcessing(ConfigContext const& config) -{ - using namespace o2::tpc; - - // set up configuration - o2::conf::ConfigurableParam::updateFromFile(config.options().get<std::string>("configFile")); - o2::conf::ConfigurableParam::updateFromString(config.options().get<std::string>("configKeyValues")); - o2::conf::ConfigurableParam::writeINI("o2tpccalibration_configuration.ini"); - - struct ProcessAttributes { - CalibPedestal calibPedestal; - rawreader::RawReaderCRUManager rawReader; - uint32_t lastOrbit{0}; - uint64_t lastTFID{0}; - uint32_t maxEvents{100}; - bool quit{false}; - bool dumped{false}; - }; - - auto initFunction = [](InitContext& ic) { - auto processAttributes = std::make_shared<ProcessAttributes>(); - // set up calibration - // TODO: - // it is a bit ugly to use the RawReaderCRUManager for this is. - // At some point the raw reader code should be cleaned up and modularized - { - auto& pedestal = processAttributes->calibPedestal; - pedestal.init(); // initialize configuration via configKeyValues - processAttributes->rawReader.createReader(""); - processAttributes->rawReader.setADCDataCallback([&pedestal](const PadROCPos& padROCPos, const CRU& cru, const gsl::span<const uint32_t> data) -> Int_t { - Int_t timeBins = pedestal.update(padROCPos, cru, data); - pedestal.setNumberOfProcessedTimeBins(std::max(pedestal.getNumberOfProcessedTimeBins(), size_t(timeBins))); - return timeBins; - }); - processAttributes->maxEvents = static_cast<uint32_t>(ic.options().get<int>("max-events")); - } - - auto processingFct = [processAttributes](ProcessingContext& pc) { - // in case the maximum number of events was reached don't do further processing - if (processAttributes->quit) { - return; - } - - if (pc.inputs().isValid("TFID")) { - auto tfid = pc.inputs().get<uint64_t>("TFID"); - LOGP(info, "TFid: {}", tfid); - processAttributes->lastTFID = tfid; - } - - for (auto& input : pc.inputs()) { - const auto* dh = DataRefUtils::getHeader<o2::header::DataHeader*>(input); - - // ---| only process RAWDATA, there might be a nicer way to do this |--- - if (dh == nullptr || dh->dataDescription != o2::header::gDataDescriptionRawData) { - continue; - } - - // ---| extract hardware information to do the processing |--- - const auto subSpecification = dh->subSpecification; - const auto cruID = subSpecification >> 16; - const auto linkID = ((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; - const auto dataWrapperID = ((subSpecification >> 8) & 0xFF) > 0; - const auto globalLinkID = linkID + dataWrapperID * 12; - - // ---| update hardware information in the reader |--- - auto& reader = processAttributes->rawReader.getReaders()[0]; - reader->forceCRU(cruID); - reader->setLink(globalLinkID); - - LOGP(debug, "Specifier: {}/{}/{}", dh->dataOrigin.as<std::string>(), dh->dataDescription.as<std::string>(), dh->subSpecification); - LOGP(debug, "Payload size: {}", dh->payloadSize); - LOGP(debug, "CRU: {}; linkID: {}; dataWrapperID: {}; globalLinkID: {}", cruID, linkID, dataWrapperID, globalLinkID); - - printHeader(); - - // TODO: exception handling needed? - try { - o2::framework::RawParser parser(input.payload, dh->payloadSize); - - // TODO: it would be better to have external event handling and then moving the event processing functionality to CalibRawBase and RawReader to not repeat it in other places - rawreader::ADCRawData rawData; - rawreader::GBTFrame gFrame; - - auto& calibPedestal = processAttributes->calibPedestal; - - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - auto* rdhPtr = it.get_if<o2::header::RAWDataHeader>(); - if (!rdhPtr) { - break; - } - const auto& rdh = *rdhPtr; - printRDH(rdh); - // ===| event handling |=== - // - // really ugly, better treatment required extension in DPL - // events are are detected by close by orbit numbers - // might be possible to change this using the TFID information - // - const auto hbOrbit = RDHUtils::getHeartBeatOrbit(rdhPtr); - const auto lastOrbit = processAttributes->lastOrbit; - - if ((lastOrbit > 0) && (hbOrbit > (lastOrbit + 3))) { - calibPedestal.incrementNEvents(); - LOGP(info, "Number of processed events: {} ({})", calibPedestal.getNumberOfProcessedEvents(), processAttributes->maxEvents); - if (calibPedestal.getNumberOfProcessedEvents() >= processAttributes->maxEvents) { - LOGP(info, "Maximm number of events reached ({}), no more processing will be done", processAttributes->maxEvents); - processAttributes->quit = true; - break; - } - } - - processAttributes->lastOrbit = hbOrbit; - const auto size = it.size(); - auto data = it.data(); - //LOGP(info, "Data size: {}", size); - - int iFrame = 0; - for (int i = 0; i < size; i += 16) { - gFrame.setFrameNumber(iFrame); - gFrame.setPacketNumber(iFrame / 508); - gFrame.readFromMemory(gsl::span<const o2::byte>(data + i, 16)); - - // extract the half words from the 4 32-bit words - gFrame.getFrameHalfWords(); - - // debug output - //if (CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { - //std::cout << gFrame; - //} - - gFrame.getAdcValues(rawData); - gFrame.updateSyncCheck(false); - - ++iFrame; - } - } - - reader->runADCDataCallback(rawData); - } catch (const std::runtime_error& e) { - LOGP(error, "can not create raw parser form input data"); - o2::header::hexDump("payload", input.payload, dh->payloadSize, 64); - LOG(ERROR) << e.what(); - } - } - - // TODO: For the moment simply dump calibration output to file, to check if everything is working as expected - if (processAttributes->quit && !processAttributes->dumped) { - LOGP(info, "Dumping output"); - processAttributes->calibPedestal.analyse(); - processAttributes->calibPedestal.dumpToFile("pedestals.root"); - processAttributes->dumped = true; - //pc.services().get<ControlService>().endOfStream(); - pc.services().get<ControlService>().readyToQuit(QuitRequest::All); - } - }; - - return processingFct; - }; - - WorkflowSpec workflow; - workflow.emplace_back(DataProcessorSpec{ - "calib-pedestal", - select(config.options().get<std::string>("input-spec").c_str()), - Outputs{}, - AlgorithmSpec{initFunction}, - Options{{"max-events", VariantType::Int, 100, {"maximum number of events to process"}}}}); - - return workflow; -} diff --git a/Detectors/TPC/calibration/src/CalibPedestal.cxx b/Detectors/TPC/calibration/src/CalibPedestal.cxx index d30411ed23a32..df8f5496d0b64 100644 --- a/Detectors/TPC/calibration/src/CalibPedestal.cxx +++ b/Detectors/TPC/calibration/src/CalibPedestal.cxx @@ -17,14 +17,14 @@ #include "TFile.h" #include "TPCBase/ROC.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "TPCCalibration/CalibPedestal.h" using namespace o2::tpc; -using o2::math_utils::math_base::fit; -using o2::math_utils::math_base::fitGaus; -using o2::math_utils::math_base::getStatisticsData; -using o2::math_utils::math_base::StatisticsData; +using o2::math_utils::fit; +using o2::math_utils::fitGaus; +using o2::math_utils::getStatisticsData; +using o2::math_utils::StatisticsData; CalibPedestal::CalibPedestal(PadSubset padSubset) : CalibRawBase(padSubset), @@ -59,10 +59,12 @@ Int_t CalibPedestal::updateROC(const Int_t roc, const Int_t row, const Int_t pad const Int_t timeBin, const Float_t signal) { Int_t adcValue = Int_t(signal); - if (timeBin < mFirstTimeBin || timeBin > mLastTimeBin) + if (timeBin < mFirstTimeBin || timeBin > mLastTimeBin) { return 0; - if (adcValue < mADCMin || adcValue > mADCMax) + } + if (adcValue < mADCMin || adcValue > mADCMax) { return 0; + } const GlobalPadNumber padInROC = mMapper.getPadNumberInROC(PadROCPos(roc, row, pad)); Int_t bin = padInROC * mNumberOfADCs + (adcValue - mADCMin); @@ -78,8 +80,9 @@ Int_t CalibPedestal::updateROC(const Int_t roc, const Int_t row, const Int_t pad CalibPedestal::vectorType* CalibPedestal::getVector(ROC roc, bool create /*=kFALSE*/) { vectorType* vec = mADCdata[roc].get(); - if (vec || !create) + if (vec || !create) { return vec; + } const size_t numberOfPads = (roc.rocType() == RocType::IROC) ? mMapper.getPadsInIROC() : mMapper.getPadsInOROC(); diff --git a/Detectors/TPC/calibration/src/CalibPulser.cxx b/Detectors/TPC/calibration/src/CalibPulser.cxx index cb53738591a97..d3e2bbb618067 100644 --- a/Detectors/TPC/calibration/src/CalibPulser.cxx +++ b/Detectors/TPC/calibration/src/CalibPulser.cxx @@ -19,13 +19,13 @@ #include "TFile.h" #include "TPCBase/ROC.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "TPCCalibration/CalibPulser.h" #include "TPCCalibration/CalibPulserParam.h" using namespace o2::tpc; -using o2::math_utils::math_base::getStatisticsData; -using o2::math_utils::math_base::StatisticsData; +using o2::math_utils::getStatisticsData; +using o2::math_utils::StatisticsData; CalibPulser::CalibPulser(PadSubset padSubset) : mNbinsT0{200}, @@ -45,9 +45,11 @@ CalibPulser::CalibPulser(PadSubset padSubset) mPeakIntMinus{2}, mPeakIntPlus{2}, mMinimumQtot{20}, - mT0{"PulserT0"}, - mWidth{"PulserWidth"}, - mQtot{"PulserQtot"}, + mMinimumQmax{10}, + mMaxTimeBinRange{5}, + mT0{"T0"}, + mWidth{"Width"}, + mQtot{"Qtot"}, mPedestal{nullptr}, mNoise{nullptr}, mPulserData{}, @@ -87,6 +89,7 @@ void CalibPulser::init() mPeakIntMinus = param.PeakIntMinus; mPeakIntPlus = param.PeakIntPlus; mMinimumQtot = param.MinimumQtot; + mMinimumQmax = param.MinimumQmax; } //______________________________________________________________________________ @@ -103,8 +106,9 @@ Int_t CalibPulser::updateROC(const Int_t roc, const Int_t row, const Int_t pad, signal -= mPedestal->getValue(ROC(roc), row, pad); } - if (signal < mADCMin || signal > mADCMax) + if (signal < mADCMin || signal > mADCMax) { return 0; + } // ===| temporary calibration data |========================================== const PadROCPos padROCPos(roc, row, pad); @@ -118,6 +122,14 @@ Int_t CalibPulser::updateROC(const Int_t roc, const Int_t row, const Int_t pad, adcData[timeBin - mFirstTimeBin] = signal; //printf("%2d, %3d, %3d, %3d: %.2f\n", roc, row, pad, timeBin, signal); + + // ---| entries per time bin |--- + auto& timeBinEntries = mTimeBinEntries[roc]; + if (!timeBinEntries.size()) { + timeBinEntries.resize(mLastTimeBin - mFirstTimeBin + 1); + } + ++timeBinEntries[timeBin - mFirstTimeBin]; + return 1; } @@ -125,26 +137,36 @@ Int_t CalibPulser::updateROC(const Int_t roc, const Int_t row, const Int_t pad, void CalibPulser::endReader() { // loop over all signals of all pads filled for the present raw reader - for (auto& keyValue : mPulserData) { - const auto& padROCPos = keyValue.first; - const auto& adcData = keyValue.second; + //for (auto& keyValue : mPulserData) { + //const auto& padROCPos = keyValue.first; + //const auto& adcData = keyValue.second; + // + auto timeRangeROCs = getTimeRangeROCs(); + + for (const auto& [padROCPos, adcData] : mPulserData) { const auto currentChannel = mMapper.getPadNumberInROC(padROCPos); //std::cout << (int)padROCPos.getROC().getRoc() << ", " << padROCPos.getRow() << ", " << padROCPos.getPad() << std::endl; //for (auto& val: adcData) std::cout << val << ", "; //std::cout << std::endl; - const auto data = processPadData(padROCPos, adcData); + const auto& elemPair = timeRangeROCs[int(padROCPos.getROC())]; + const auto data = processPadData(padROCPos, adcData, elemPair); //std::cout << data.mT0+mFirstTimeBin << " : " << data.mQtot << " : " << data.mWidth << "\n"; // fill histograms - getHistoT0(padROCPos.getROC(), true)->Fill(data.mT0 + mFirstTimeBin, currentChannel); - getHistoQtot(padROCPos.getROC(), true)->Fill(data.mQtot, currentChannel); - getHistoSigma(padROCPos.getROC(), true)->Fill(data.mWidth, currentChannel); + if (data.isValid()) { + getHistoT0(padROCPos.getROC(), true)->Fill(data.mT0 + mFirstTimeBin, currentChannel); + getHistoQtot(padROCPos.getROC(), true)->Fill(data.mQtot, currentChannel); + getHistoSigma(padROCPos.getROC(), true)->Fill(data.mWidth, currentChannel); + } } // reset the adc data to free space mPulserData.clear(); + for (auto& v : mTimeBinEntries) { + std::fill(v.begin(), v.end(), 0); + } } //______________________________________________________________________________ @@ -153,8 +175,9 @@ TH2S* CalibPulser::getHistogram(ROC roc, CalibPulser::PtrVectorType& rocVector, std::string_view type, bool create /*=kFALSE*/) { TH2S* vec = rocVector[roc].get(); - if (vec || !create) + if (vec || !create) { return vec; + } const size_t nChannels = mMapper.getNumberOfPads(roc); rocVector[roc] = std::make_unique<TH2S>(Form("hCalib%s%02d", type.data(), roc.getRoc()), @@ -166,13 +189,21 @@ TH2S* CalibPulser::getHistogram(ROC roc, CalibPulser::PtrVectorType& rocVector, } //______________________________________________________________________________ -CalibPulser::PulserData CalibPulser::processPadData(const PadROCPos& padROCPos, const CalibPulser::VectorType& adcData) +CalibPulser::PulserData CalibPulser::processPadData(const PadROCPos& padROCPos, const CalibPulser::VectorType& adcData, const CalibPulser::ElemPair& range) { // data to return PulserData data; + // find time bin with maximum number of entreis + const auto vectorSize = adcData.size(); - const auto maxElement = std::max_element(std::begin(adcData), std::end(adcData)); + //const auto maxElement = std::max_element(std::begin(adcData), std::end(adcData)); + const auto maxElement = std::max_element(std::begin(adcData) + range.first, std::begin(adcData) + range.last); + + if (*maxElement < mMinimumQmax) { + return data; + } + const auto maxPosition = std::distance(std::begin(adcData), maxElement); double weightedSum = 0.; @@ -181,8 +212,9 @@ CalibPulser::PulserData CalibPulser::processPadData(const PadROCPos& padROCPos, for (int t = maxPosition - mPeakIntMinus; t <= maxPosition + mPeakIntPlus; ++t) { const auto signal = adcData[t]; // check time bounds - if (t < 0 || t >= vectorSize) + if (t < 0 || t >= vectorSize) { continue; + } weightedSum += signal * (t + 0.5); // +0.5 to get the center of the time bin weightedSum2 += signal * (t + 0.5) * (t + 0.5); chargeSum += signal; @@ -200,17 +232,39 @@ CalibPulser::PulserData CalibPulser::processPadData(const PadROCPos& padROCPos, return data; } +//______________________________________________________________________________ +std::array<CalibPulser::ElemPair, ROC::MaxROC> CalibPulser::getTimeRangeROCs() +{ + std::array<ElemPair, ROC::MaxROC> maxTimeBin; + + for (size_t iROC = 0; iROC < mTimeBinEntries.size(); ++iROC) { + const auto& timeBinEntries = mTimeBinEntries[iROC]; + const auto size = timeBinEntries.size(); + const auto maxElement = std::max_element(std::begin(timeBinEntries), std::end(timeBinEntries)); + const auto maxPosition = std::distance(std::begin(timeBinEntries), maxElement); + + maxTimeBin[iROC].first = (maxPosition > mMaxTimeBinRange) ? maxPosition - mMaxTimeBinRange : 0; + maxTimeBin[iROC].last = std::min(size_t(maxPosition + mMaxTimeBinRange), size - 1); + } + + return maxTimeBin; +} + //______________________________________________________________________________ void CalibPulser::resetData() { mPulserData.clear(); + for (auto& v : mTimeBinEntries) { + std::fill(v.begin(), v.end(), 0); + } std::vector<PtrVectorType*> v{&mT0Histograms, &mWidthHistograms, &mQtotHistograms}; for (auto histArray : v) { for (auto& histPtr : *histArray) { auto ptr = histPtr.get(); - if (ptr) + if (ptr) { ptr->Reset(); + } } } } @@ -223,8 +277,9 @@ void CalibPulser::analyse() auto histWidth = mWidthHistograms.at(roc).get(); auto histQtot = mQtotHistograms.at(roc).get(); - if (!histT0 || !histWidth || !histQtot) + if (!histT0 || !histWidth || !histQtot) { continue; + } // array pointer const auto arrT0 = histT0->GetArray(); @@ -261,14 +316,17 @@ void CalibPulser::dumpToFile(const std::string filename, uint32_t type /* = 0*/) printf("dump debug info\n"); // temporary arrays for writing the objects TObjArray vT0; - for (auto& val : mT0Histograms) + for (auto& val : mT0Histograms) { vT0.Add(val.get()); + } TObjArray vWidth; - for (auto& val : mWidthHistograms) + for (auto& val : mWidthHistograms) { vWidth.Add(val.get()); + } TObjArray vQtot; - for (auto& val : mQtotHistograms) + for (auto& val : mQtotHistograms) { vQtot.Add(val.get()); + } vT0.Write("T0Histograms", TObject::kSingleKey); vWidth.Write("WidthHistograms", TObject::kSingleKey); diff --git a/Detectors/TPC/calibration/src/CalibRawBase.cxx b/Detectors/TPC/calibration/src/CalibRawBase.cxx index a2291159d33fe..79328a2110cfd 100644 --- a/Detectors/TPC/calibration/src/CalibRawBase.cxx +++ b/Detectors/TPC/calibration/src/CalibRawBase.cxx @@ -87,6 +87,7 @@ void CalibRawBase::setupContainers(TString fileInfo, uint32_t verbosity, uint32_ printf("Forcing CRU %03d\n", cru); } } + mRawReaderCRUManager.init(); } else if (rorcType == "digits") { TString files = gSystem->GetFromPipe(TString::Format("ls %s", arrDataInfo->At(0)->GetName())); //const int timeBins = static_cast<TObjString*>(arrDataInfo->At(1))->String().Atoi(); @@ -113,8 +114,9 @@ void CalibRawBase::setupContainers(TString fileInfo, uint32_t verbosity, uint32_ TString& filename = static_cast<TObjString*>(arrDataInfo->At(0))->String(); iCRU = static_cast<TObjString*>(arrDataInfo->At(1))->String().Atoi(); iLink = static_cast<TObjString*>(arrDataInfo->At(2))->String().Atoi(); - if (arrDataInfo->GetEntriesFast() > 3) + if (arrDataInfo->GetEntriesFast() > 3) { iSampaVersion = static_cast<TObjString*>(arrDataInfo->At(3))->String().Atoi(); + } auto cont = new GBTFrameContainer(iSize, iCRU, iLink, iSampaVersion); diff --git a/Detectors/TPC/calibration/src/CalibTreeDump.cxx b/Detectors/TPC/calibration/src/CalibTreeDump.cxx index b9154baf7bbdd..3dc4709f9b7fc 100644 --- a/Detectors/TPC/calibration/src/CalibTreeDump.cxx +++ b/Detectors/TPC/calibration/src/CalibTreeDump.cxx @@ -20,19 +20,28 @@ #include "TTree.h" #include "TMath.h" -#include "MathUtils/MathBase.h" +#include "MathUtils/fit.h" #include "TPCBase/CalArray.h" #include "TPCBase/CalDet.h" #include "TPCBase/CRU.h" #include "TPCBase/Mapper.h" +#include "TPCBase/Utils.h" #include "TPCBase/Sector.h" #include "TPCCalibration/CalibTreeDump.h" -using o2::math_utils::math_base::median; +using o2::math_utils::median; using namespace o2::tpc; +void CalibTreeDump::addCalPads(const std::string_view file, const std::string_view calPadNames) +{ + auto calPads = utils::readCalPads(file, calPadNames); + for (auto calPad : calPads) { + add(calPad); + } +} + //______________________________________________________________________________ void CalibTreeDump::dumpToFile(const std::string filename) { @@ -195,8 +204,12 @@ void CalibTreeDump::addCalDetObjects(TTree* tree) { int iter = 0; - for (auto& calDet : mCalDetObjects) { + for (auto pcalDet : mCalDetObjects) { // ===| branch names |=== + if (!pcalDet) { + continue; + } + auto& calDet = *pcalDet; std::string name = calDet.getName(); if (name == "PadCalibrationObject" || name.size() == 0) { @@ -206,20 +219,27 @@ void CalibTreeDump::addCalDetObjects(TTree* tree) std::string meanName = fmt::format("{}_mean", name); std::string stdDevName = fmt::format("{}_stdDev", name); std::string medianName = fmt::format("{}_median", name); + std::string median1Name = fmt::format("{}_median1", name); + std::string median2Name = fmt::format("{}_median2", name); + std::string median3Name = fmt::format("{}_median3", name); // ===| branch variables |=== std::vector<float>* data = nullptr; float mean{}; float stdDev{}; - float median{}; + float median[4]{}; // ===| branch definitions |=== TBranch* brMean = tree->Branch(meanName.data(), &mean); TBranch* brStdDev = tree->Branch(stdDevName.data(), &stdDev); - TBranch* brMedian = tree->Branch(medianName.data(), &median); + TBranch* brMedian = tree->Branch(medianName.data(), &median[0]); + TBranch* brMedian1 = tree->Branch(median1Name.data(), &median[1]); + TBranch* brMedian2 = tree->Branch(median2Name.data(), &median[2]); + TBranch* brMedian3 = tree->Branch(median3Name.data(), &median[3]); TBranch* brData = tree->Branch(name.data(), &data); // ===| loop over ROCs and fill |=== + int roc = 0; for (auto& calArray : calDet.getData()) { // ---| set data |--- data = &calArray.getData(); @@ -227,13 +247,22 @@ void CalibTreeDump::addCalDetObjects(TTree* tree) // ---| statistics |--- mean = TMath::Mean(data->begin(), data->end()); stdDev = TMath::StdDev(data->begin(), data->end()); - median = TMath::Median(data->size(), data->data()); + median[0] = median[1] = median[2] = median[3] = TMath::Median(data->size(), data->data()); + if (roc > 35) { + median[1] = TMath::Median(Mapper::getPadsInOROC1(), data->data()); + median[2] = TMath::Median(Mapper::getPadsInOROC2(), data->data() + Mapper::getPadsInOROC1()); + median[3] = TMath::Median(Mapper::getPadsInOROC3(), data->data() + Mapper::getPadsInOROC1() + Mapper::getPadsInOROC2()); + } // ---| filling |--- brData->Fill(); brMean->Fill(); brStdDev->Fill(); brMedian->Fill(); + brMedian1->Fill(); + brMedian2->Fill(); + brMedian3->Fill(); + ++roc; } } } @@ -244,8 +273,9 @@ void CalibTreeDump::readTraceLengths(std::string_view mappingDir) std::string inputDir = mappingDir.data(); if (!inputDir.size()) { const char* aliceO2env = std::getenv("O2_ROOT"); - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/TPC/files"; } @@ -302,6 +332,8 @@ void CalibTreeDump::setTraceLengths(std::string_view inputFile, std::vector<floa void CalibTreeDump::setDefaultAliases(TTree* tree) { tree->SetAlias("sector", "roc%36"); + tree->SetAlias("padsPerRow", "2*(pad-cpad)"); + tree->SetAlias("isEdgePad", "(pad==0) || (pad==padsPerRow-1)"); tree->SetAlias("rowInSector", "row + (roc>35)*63"); tree->SetAlias("padWidth", "0.4 + (roc > 35) * 0.2"); tree->SetAlias("padHeight", "0.75 + (rowInSector > 62) * 0.25 + (rowInSector > 96) * 0.2 + (rowInSector > 126) * 0.3"); @@ -312,14 +344,18 @@ void CalibTreeDump::setDefaultAliases(TTree* tree) tree->SetAlias("region", "cruInSector"); tree->SetAlias("partition", "int(cruInSector / 2)"); + tree->SetAlias("padWidth", "(region == 0) * 0.416 + (region == 1) * 0.42 + (region == 2) * 0.42 + (region == 3) * 0.436 + (region == 4) * 0.6 + (region == 5) * 0.6 + (region == 6) * 0.608 + (region == 7) * 0.588 + (region == 8) * 0.604 + (region == 9) * 0.607"); + tree->SetAlias("padHeight", "0.75 + (region>3)*0.25 + (region>5)*0.2 + (region>7)*0.3"); + tree->SetAlias("padArea", "padHeight * padWidth"); + tree->SetAlias("IROC", "roc < 36"); tree->SetAlias("OROC", "roc >= 36"); tree->SetAlias("OROC1", "partition == 2"); tree->SetAlias("OROC2", "partition == 3"); tree->SetAlias("OROC3", "partition == 4"); - tree->SetAlias("A_Side", "sector < 36"); - tree->SetAlias("C_Side", "sector >= 36"); + tree->SetAlias("A_Side", "sector < 18"); + tree->SetAlias("C_Side", "sector >= 18"); if (mAddFEEInfo) { tree->SetAlias("fecID", "fecInSector + sector * 91"); diff --git a/Detectors/TPC/calibration/src/DigitDump.cxx b/Detectors/TPC/calibration/src/DigitDump.cxx index 85dd0a1f239c5..dfbfbe38f3d5e 100644 --- a/Detectors/TPC/calibration/src/DigitDump.cxx +++ b/Detectors/TPC/calibration/src/DigitDump.cxx @@ -89,7 +89,7 @@ Int_t DigitDump::updateCRU(const CRU& cru, const Int_t row, const Int_t pad, //printf("updateCRU: %d, %d (%d, %d), %d, %d, %f, %f\n", int(cru), row, globalRow, sectorRow, pad, timeBin, signal, pedestal); // fill digits - mDigits[cru.sector()].emplace_back(cru, signalCorr, globalRow, pad, timeBin); + addDigit(cru, signalCorr, globalRow, pad, timeBin); return 0; } @@ -100,10 +100,12 @@ void DigitDump::sortDigits() // sort digits for (auto& digits : mDigits) { std::sort(digits.begin(), digits.end(), [](const auto& a, const auto& b) { - if (a.getTimeStamp() < b.getTimeStamp()) + if (a.getTimeStamp() < b.getTimeStamp()) { return true; - if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) + } + if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) { return true; + } return false; }); } diff --git a/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C b/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C index 1a3bf2e49e3bb..7d203bae30596 100644 --- a/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C +++ b/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C @@ -337,7 +337,7 @@ void DrawPadSignal(TString type) //mRawReader->Reset(); TH1D* h2 = mEvDisp.makePadSignals(roc, row, pad); if (h2) { - h2->GetXaxis()->SetRangeUser(0, mEvDisp.getNumberOfProcessedTimeBins() + 5); + //h2->GetXaxis()->SetRangeUser(0, mEvDisp.getNumberOfProcessedTimeBins() + 5); h2->Draw(); h2->SetStats(0); @@ -601,7 +601,7 @@ void CallEventNumber() } //__________________________________________________________________________ -void RunSimpleEventDisplay(TString fileInfo, TString pedestalFile = "", Int_t nTimeBinsPerCall = 500, uint32_t verbosity = 0, uint32_t debugLevel = 0, int selectedSector = 0, bool showSides = 0) +void RunSimpleEventDisplay(TString fileInfo, TString pedestalFile = "", Int_t firstTimeBin = 0, Int_t lastTimeBin = 500, Int_t nTimeBinsPerCall = 500, uint32_t verbosity = 0, uint32_t debugLevel = 0, int selectedSector = 0, bool showSides = 0) { FairLogger* logger = FairLogger::GetLogger(); logger->SetLogVerbosityLevel("LOW"); @@ -623,7 +623,7 @@ void RunSimpleEventDisplay(TString fileInfo, TString pedestalFile = "", Int_t nT mEvDisp.setSelectedSector(mSelectedSector); mEvDisp.setLastSelSector(mSelectedSector); mEvDisp.setTimeBinsPerCall(nTimeBinsPerCall); - mEvDisp.setTimeBinRange(0, nTimeBinsPerCall); + mEvDisp.setTimeBinRange(firstTimeBin, lastTimeBin); InitGUI(); // while (mRawReader->NextEvent() && mRawReader->GetEventFromTag()==0) Next(); diff --git a/Detectors/TPC/monitor/macro/startMonitor b/Detectors/TPC/monitor/macro/startMonitor index c8f93fa684195..a9adecda17365 100755 --- a/Detectors/TPC/monitor/macro/startMonitor +++ b/Detectors/TPC/monitor/macro/startMonitor @@ -13,7 +13,9 @@ usage() { echo echo "optional arguments:" echo " -p, --pedestalFile= : pedestal file" - echo " -t, --timeBins= : number of time bins to process (default: 1000)" + echo " -f, --fistTimeBin= : first time bin for pulser search" + echo " -l, --lastTimeBin= : last time bin for pulser search" + echo " -t, --timeBins= : number of time bins to process (default: $timeBins)" echo " -v, --verbosity= : set verbosity level" echo " -d, --debugLevel= : set debug level" echo " -s, --sector= : select specific sector (default 0)" @@ -33,14 +35,17 @@ usageAndExit() { # ===| default variable values |================================================ fileInfo= pedestalFile= -timeBins=1000 +firstTimeBin=0 +lastTimeBin=512 +timeBins=512 verbosity=0 debugLevel=0 selectedSector=0 showOverview=0 +tool="" # ===| parse command line options |============================================= -OPTIONS=$(getopt -l "fileInfo:,pedestalFile:,timeBins:,verbosity:,debugLevel:,sector:,overview,help" -o "i:p:t:v:d:s:oh" -n "startMonitor" -- "$@") +OPTIONS=$(getopt -l "fileInfo:,pedestalFile:,firstTimeBin:,lastTimeBin:,timeBins:,verbosity:,debugLevel:,sector:,overview,valgrind,callgrind,help" -o "i:p:f:l:t:v:d:s:oh" -n "startMonitor" -- "$@") if [ $? != 0 ] ; then usageAndExit @@ -53,11 +58,15 @@ while true; do --) shift; break;; -i|--fileInfo) fileInfo=$2; shift 2;; -p|--pedestalFile) pedestalFile=$2; shift 2;; + -f|--firstTimeBin) firstTimeBin=$2; shift 2;; + -l|--lastTimeBin) lastTimeBin=$2; shift 2;; -t|--timeBins) timeBins=$2; shift 2;; -v|--verbosity) verbosity=$2; shift 2;; -d|--debugLevel) debugLevel=$2; shift 2;; -s|--sector) selectedSector=$2; shift 2;; -o|--overview) showOverview=1; shift;; + --valgrind) tool="valgrind --log-file=valgrind.log"; shift;; + --callgrind) tool="valgrind --tool=callgrind"; shift;; -h|--help) usageAndExit;; *) echo "Internal error!" ; exit 1 ;; esac @@ -77,8 +86,6 @@ else fi # ===| command building and execution |========================================= - -#cmd="valgrind --log-file=valgrind.log root.exe -l $O2_SRC/Detectors/TPC/reconstruction/macro/addInclude.C $O2_SRC/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C+g'(\"$fileInfo\",\"$pedestalFile\",$timeBins,$verbosity,$debugLevel,$selectedSector,$showOverview)'" -cmd="root.exe -l $O2_SRC/Detectors/TPC/reconstruction/macro/addInclude.C $O2_SRC/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C+g'(\"$fileInfo\",\"$pedestalFile\",$timeBins,$verbosity,$debugLevel,$selectedSector,$showOverview)'" +cmd="$tool root.exe -l $O2_SRC/Detectors/TPC/reconstruction/macro/addInclude.C $O2_SRC/Detectors/TPC/monitor/macro/RunSimpleEventDisplay.C+g'(\"$fileInfo\", \"$pedestalFile\", $firstTimeBin, $lastTimeBin, $timeBins, $verbosity, $debugLevel, $selectedSector, $showOverview)'" echo "running: $cmd" eval $cmd diff --git a/Detectors/TPC/monitor/src/SimpleEventDisplay.cxx b/Detectors/TPC/monitor/src/SimpleEventDisplay.cxx index 8347edf8e02f2..bd85cb9bb057c 100644 --- a/Detectors/TPC/monitor/src/SimpleEventDisplay.cxx +++ b/Detectors/TPC/monitor/src/SimpleEventDisplay.cxx @@ -87,16 +87,21 @@ Int_t SimpleEventDisplay::updateROC(const Int_t roc, // assumes that it is looped over consecutive time bins of one pad // //printf("update called: %d, %d, %d, %d, %.3f\n", roc, row, pad, timeBin, signal); - if (row < 0) + if (row < 0) { return 0; - if (pad < 0) + } + if (pad < 0) { return 0; - if (timeBin < 0) + } + if (timeBin < 0) { return 0; - if ((timeBin > mLastTimeBin) || (timeBin < mFirstTimeBin)) + } + if ((timeBin > mLastTimeBin) || (timeBin < mFirstTimeBin)) { return 0; - if (mSectorLoop && roc % 36 != mSelectedSector % 36) + } + if (mSectorLoop && roc % 36 != mSelectedSector % 36) { return 0; + } if (row < 0 || pad < 0) { printf("Wrong Pad or Row number, skipping!"); @@ -135,7 +140,7 @@ Int_t SimpleEventDisplay::updateROC(const Int_t roc, //fill signals for current pad if (mCurrentROC % 36 == mSelectedSector % 36) { const Int_t nbins = mLastTimeBin - mFirstTimeBin; - const Int_t offset = (nbins + 2) * (iChannel + 1) + timeBin + 1; + const Int_t offset = (nbins + 2) * (iChannel + 1) + (timeBin - mFirstTimeBin) + 1; if ((UInt_t)roc < mTPCmapper.getNumberOfIROCs()) { mHSigIROC->GetArray()[offset] = corrSignal; @@ -194,8 +199,9 @@ TH1D* SimpleEventDisplay::makePadSignals(Int_t roc, Int_t row, Int_t pad) } TH1D* h = nullptr; const Int_t nbins = mLastTimeBin - mFirstTimeBin; - if (nbins <= 0) + if (nbins <= 0) { return nullptr; + } const Int_t offset = (nbins + 2) * (channel + 1); Double_t* arrP = nullptr; @@ -247,8 +253,9 @@ void SimpleEventDisplay::resetEvent() //for (auto reader : mGBTFrameContainers) { //reader->reProcessAllFrames(); //} - if (!mSectorLoop) + if (!mSectorLoop) { mPadMax.multiply(0.); + } mHSigIROC->Reset(); mHSigOROC->Reset(); } diff --git a/Detectors/TPC/qc/CMakeLists.txt b/Detectors/TPC/qc/CMakeLists.txt index 28cd9b510cb84..ee254b20a9315 100644 --- a/Detectors/TPC/qc/CMakeLists.txt +++ b/Detectors/TPC/qc/CMakeLists.txt @@ -10,20 +10,24 @@ o2_add_library(TPCQC SOURCES src/PID.cxx + src/Tracking.cxx src/Helpers.cxx src/TrackCuts.cxx src/Clusters.cxx src/Tracks.cxx PUBLIC_LINK_LIBRARIES O2::TPCBase - O2::DataFormatsTPC) + O2::DataFormatsTPC + O2::GPUTracking) o2_target_root_dictionary(TPCQC HEADERS include/TPCQC/PID.h + include/TPCQC/Tracking.h include/TPCQC/Helpers.h include/TPCQC/TrackCuts.h include/TPCQC/Clusters.h - include/TPCQC/Tracks.h) + include/TPCQC/Tracks.h + include/TPCQC/CalPadWrapper.h) o2_add_test(PID COMPONENT_NAME tpc diff --git a/Detectors/TPC/qc/include/TPCQC/CalPadWrapper.h b/Detectors/TPC/qc/include/TPCQC/CalPadWrapper.h new file mode 100644 index 0000000000000..77afbc8909b4b --- /dev/null +++ b/Detectors/TPC/qc/include/TPCQC/CalPadWrapper.h @@ -0,0 +1,61 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CalPadWrapper_H +#define O2_CalPadWrapper_H + +#include <TClass.h> +#include <TObject.h> + +#include "TPCBase/CalDet.h" + +namespace o2 +{ +namespace tpc +{ +namespace qc +{ +/// Temporary solution until objects not inheriting from TObject can be handled in QualityControl +/// A wrapper class to easily promote CalDet<float> objects to a TObject +/// does not take ownership of wrapped object and should not be used +/// in tight loops since construction expensive +class CalPadWrapper : public TObject +{ + public: + CalPadWrapper(o2::tpc::CalDet<float>* obj) : mObj(obj), TObject() + { + } + + CalPadWrapper() = default; + + void setObj(o2::tpc::CalDet<float>* obj) + { + mObj = obj; + } + + o2::tpc::CalDet<float>* getObj() + { + return mObj; + } + + virtual const char* GetName() const override { return mObj ? mObj->getName().data() : "unset"; } + + virtual ~CalPadWrapper() override = default; + + private: + o2::tpc::CalDet<float>* mObj{}; ///< wrapped CalDet<float> (aka CalPad) + + ClassDefOverride(CalPadWrapper, 1); +}; +} // namespace qc +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/qc/include/TPCQC/Clusters.h b/Detectors/TPC/qc/include/TPCQC/Clusters.h index 9944124c7b61f..9d4668f490887 100644 --- a/Detectors/TPC/qc/include/TPCQC/Clusters.h +++ b/Detectors/TPC/qc/include/TPCQC/Clusters.h @@ -54,6 +54,13 @@ class Clusters const CalPad& getSigmaPad() const { return mSigmaPad; } const CalPad& getTimeBin() const { return mTimeBin; } + CalPad& getNClusters() { return mNClusters; } + CalPad& getQMax() { return mQMax; } + CalPad& getQTot() { return mQTot; } + CalPad& getSigmaTime() { return mSigmaTime; } + CalPad& getSigmaPad() { return mSigmaPad; } + CalPad& getTimeBin() { return mTimeBin; } + private: CalPad mNClusters{"N_Clusters"}; CalPad mQMax{"Q_Max"}; diff --git a/Detectors/TPC/qc/include/TPCQC/Tracking.h b/Detectors/TPC/qc/include/TPCQC/Tracking.h new file mode 100644 index 0000000000000..59b37d5828ba6 --- /dev/null +++ b/Detectors/TPC/qc/include/TPCQC/Tracking.h @@ -0,0 +1,90 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Tracking.h +/// @author David Rohr +/// + +#ifndef AliceO2_TPC_QC_TRACKING_H +#define AliceO2_TPC_QC_TRACKING_H + +#include <vector> +#include <memory> + +class TH1F; +class TH2F; +class TH1D; + +//o2 includes +#include "DataFormatsTPC/Defs.h" + +namespace o2 +{ +class MCCompLabel; +namespace gpu +{ +class GPUO2InterfaceQA; +} // namespace gpu +namespace tpc +{ +class TrackTPC; +struct ClusterNativeAccess; + +namespace qc +{ +// Class for tracking QA (efficiency / resolution) +// Some settings can be steered via --configKeyValues: (See GPUSettingsList.h for actual definitions). Relevant QA parameters are: +// "GPU_QA.strict=[bool]" Strict QA mode: Only consider resolution of tracks where the fit ended within 5 cm of the reference, and remove outliers. (Default: true) +// "GPU_QA.qpt=[float]" Set cut for Q/Pt. (Default: 10.0) +// "GPU_QA.recThreshold=[float]" Compute the efficiency including impure tracks with fake contamination. (Default 0.9) +// "GPU_QA.maxResX=[float]" Maxmimum X (~radius) for reconstructed track position to take into accound for resolution QA in cm (Default: no limit) +// "GPU_QA.nativeFitResolutions=[bool]" Create resolution histograms in the native fit units (sin(phi), tan(lambda), Q/Pt) (Default: false) +// "GPU_QA.filterCharge=[int]" Filter for positive (+1) or negative (-1) charge (Default: no filter) +// "GPU_QA.filterPID=[int]" Filter for Particle Type (0 Electron, 1 Muon, 2 Pion, 3 Kaon, 4 Proton) (Default: no filter) + +class Tracking +{ + public: + /// default constructor + Tracking(); + ~Tracking(); + + enum outputModes { + outputMergeable, // output mergeaable histogrems, which can be merged and then postprocessed + outputPostprocessed, // directly postprocess the histograms before merging + outputLayout // arrange postprocessed histograms in predefined layouts + }; + + // Initiaalize + // postprocessOnly = false: initialize to run the full QA via processTracks function. + // postprocessOnly = true : cannot process tracks but only postprocess mergeeablee histogrems in postprocess function, output type must be outputPostprocessed or outputLayout. + void initialize(outputModes outputMode, bool postprocessOnly = false); + + void processTracks(const std::vector<o2::tpc::TrackTPC>* tracks, const std::vector<o2::MCCompLabel>* tracksMC, const o2::tpc::ClusterNativeAccess* clNative, TObjArray* out = nullptr); + int postprocess(std::vector<TH1F>& in1, std::vector<TH2F>& in2, std::vector<TH1D>& in3, TObjArray& out); // Inputs are modified, thus must not be const + + /// Reset all histograms + void resetHistograms(); + + /// get histograms + void getHists(const std::vector<TH1F>*& h1, const std::vector<TH2F>*& h2, const std::vector<TH1D>*& h3) const; + + private: + std::unique_ptr<o2::gpu::GPUO2InterfaceQA> mQA; //! + outputModes mOutputMode; + + ClassDefNV(Tracking, 1) +}; +} // namespace qc +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/qc/macro/runClusters.C b/Detectors/TPC/qc/macro/runClusters.C index 242c093f7ced8..bed9cbb409d15 100644 --- a/Detectors/TPC/qc/macro/runClusters.C +++ b/Detectors/TPC/qc/macro/runClusters.C @@ -29,7 +29,7 @@ void runClusters(std::string_view outputFile = "ClusterQC.root", std::string_vie ClusterNativeAccess clusterIndex; std::unique_ptr<ClusterNative[]> clusterBuffer; memset(&clusterIndex, 0, sizeof(clusterIndex)); - MCLabelContainer clusterMCBuffer; + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clusterMCBuffer; qc::Clusters clusters; @@ -38,8 +38,8 @@ void runClusters(std::string_view outputFile = "ClusterQC.root", std::string_vie tpcClusterReader.read(i); tpcClusterReader.fillIndex(clusterIndex, clusterBuffer, clusterMCBuffer); size_t iClusters = 0; - for (int isector = 0; isector < Constants::MAXSECTOR; ++isector) { - for (int irow = 0; irow < Constants::MAXGLOBALPADROW; ++irow) { + for (int isector = 0; isector < constants::MAXSECTOR; ++isector) { + for (int irow = 0; irow < constants::MAXGLOBALPADROW; ++irow) { const int nClusters = clusterIndex.nClusters[isector][irow]; if (!nClusters) { continue; @@ -57,4 +57,4 @@ void runClusters(std::string_view outputFile = "ClusterQC.root", std::string_vie } clusters.analyse(); clusters.dumpToFile(outputFile.data()); -} \ No newline at end of file +} diff --git a/Detectors/TPC/qc/src/PID.cxx b/Detectors/TPC/qc/src/PID.cxx index 029280b49bc4f..c5ef72a00a7fa 100644 --- a/Detectors/TPC/qc/src/PID.cxx +++ b/Detectors/TPC/qc/src/PID.cxx @@ -33,20 +33,20 @@ void PID::initializeHistograms() mHist1D.emplace_back("hNClusters", "; # of clusters; counts", 160, 0, 1600); //| mHist1D[0] mHist1D.emplace_back("hdEdxTot", "; dEdxTot (a.u.); counts", 200, 0, 200); //| mHist1D[1] mHist1D.emplace_back("hdEdxMax", "; dEdxMax (a.u.); counts", 200, 0, 200); //| mHist1D[2] - mHist1D.emplace_back("hPhi", "; #phi (rad); counts", 180, -M_PI, M_PI); //| mHist1D[3] + mHist1D.emplace_back("hPhi", "; #phi (rad); counts", 180, 0., 2 * M_PI); //| mHist1D[3] mHist1D.emplace_back("hTgl", "; tan#lambda; counts", 60, -2, 2); //| mHist1D[4] mHist1D.emplace_back("hSnp", "; sin p; counts", 60, -2, 2); //| mHist1D[5] - mHist2D.emplace_back("hdEdxVsPhi", "dEdx (a.u.) vs #phi (rad); #phi (rad); dEdx (a.u.)", 180, -M_PI, M_PI, 300, 0, 300); //| mHist2D[0] - mHist2D.emplace_back("hdEdxVsTgl", "dEdx (a.u.) vs tan#lambda; tan#lambda; dEdx (a.u.)", 60, -2, 2, 300, 0, 300); //| mHist2D[1] - mHist2D.emplace_back("hdEdxVsncls", "dEdx (a.u.) vs ncls; ncls; dEdx (a.u.)", 80, 0, 160, 300, 0, 300); //| mHist2D[2] + mHist2D.emplace_back("hdEdxVsPhi", "dEdx (a.u.) vs #phi (rad); #phi (rad); dEdx (a.u.)", 180, 0., 2 * M_PI, 300, 0, 300); //| mHist2D[0] + mHist2D.emplace_back("hdEdxVsTgl", "dEdx (a.u.) vs tan#lambda; tan#lambda; dEdx (a.u.)", 60, -2, 2, 300, 0, 300); //| mHist2D[1] + mHist2D.emplace_back("hdEdxVsncls", "dEdx (a.u.) vs ncls; ncls; dEdx (a.u.)", 80, 0, 160, 300, 0, 300); //| mHist2D[2] const auto logPtBinning = helpers::makeLogBinning(30, 0.1, 10); if (logPtBinning.size() > 0) { mHist2D.emplace_back("hdEdxVsp", "dEdx (a.u.) vs p (G#it{e}V/#it{c}); p (G#it{e}V/#it{c}); dEdx (a.u.)", logPtBinning.size() - 1, logPtBinning.data(), 300, 0, 300); //| mHist2D[3] } - //mHist2D.emplace_back("hdedxVsphiMIPA","; #phi (rad); dedx (a.u.)", 180,-M_PI,M_PI,25,35,60); //| mHist2D[4] - //mHist2D.emplace_back("hdedxVsphiMIPC","; #phi (rad); dedx (a.u.)", 180,-M_PI,M_PI,25,35,60); //| mHist2D[5] + //mHist2D.emplace_back("hdedxVsphiMIPA","; #phi (rad); dedx (a.u.)", 180,0.,2*M_PI,25,35,60); //| mHist2D[4] + //mHist2D.emplace_back("hdedxVsphiMIPC","; #phi (rad); dedx (a.u.)", 180,0.,2*M_PI,25,35,60); //| mHist2D[5] } //______________________________________________________________________________ @@ -99,4 +99,4 @@ void PID::dumpToFile(const std::string filename) f->WriteObject(&hist, hist.GetName()); } f->Close(); -} \ No newline at end of file +} diff --git a/Detectors/TPC/qc/src/TPCQCLinkDef.h b/Detectors/TPC/qc/src/TPCQCLinkDef.h index 565d9a7a385aa..955563373e617 100644 --- a/Detectors/TPC/qc/src/TPCQCLinkDef.h +++ b/Detectors/TPC/qc/src/TPCQCLinkDef.h @@ -15,9 +15,11 @@ #pragma link off all functions; #pragma link C++ class o2::tpc::qc::PID+; +#pragma link C++ class o2::tpc::qc::Tracking + ; #pragma link C++ class o2::tpc::qc::TrackCuts+; #pragma link C++ class o2::tpc::qc::Clusters+; #pragma link C++ class o2::tpc::qc::Tracks+; +#pragma link C++ class o2::tpc::qc::CalPadWrapper+; #pragma link C++ function o2::tpc::qc::helpers::makeLogBinning+; #pragma link C++ function o2::tpc::qc::helpers::setStyleHistogram1D+; #pragma link C++ function o2::tpc::qc::helpers::setStyleHistogram2D+; diff --git a/Detectors/TPC/qc/src/Tracking.cxx b/Detectors/TPC/qc/src/Tracking.cxx new file mode 100644 index 0000000000000..3efd5e8880de2 --- /dev/null +++ b/Detectors/TPC/qc/src/Tracking.cxx @@ -0,0 +1,77 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Tracking.cxx +/// \author David Rohr + +#define _USE_MATH_DEFINES + +#include <cmath> + +//root includes +#include "TStyle.h" +#include "TFile.h" +#include "TCanvas.h" +#include "TH1F.h" +#include "TH2F.h" +#include "TH1D.h" + +//o2 includes +#include "DataFormatsTPC/TrackTPC.h" +#include "TPCQC/Tracking.h" +#include "GPUO2InterfaceQA.h" +#include "GPUO2InterfaceConfiguration.h" + +ClassImp(o2::tpc::qc::Tracking); + +using namespace o2::tpc::qc; +using namespace o2::gpu; + +Tracking::Tracking() = default; +Tracking::~Tracking() = default; + +static constexpr int QAMODE = 7; + +//______________________________________________________________________________ +void Tracking::initialize(outputModes outputMode, bool postprocessOnly) +{ + mOutputMode = outputMode; + GPUO2InterfaceConfiguration config; + config.configQA.shipToQCAsCanvas = mOutputMode == outputLayout; + mQA = std::make_unique<GPUO2InterfaceQA>(&config.configQA); + if (!postprocessOnly) { + mQA->initializeForProcessing(QAMODE); + } +} + +//______________________________________________________________________________ +void Tracking::resetHistograms() +{ + mQA->resetHists(); +} + +//______________________________________________________________________________ +void Tracking::processTracks(const std::vector<o2::tpc::TrackTPC>* tracks, const std::vector<o2::MCCompLabel>* tracksMC, const o2::tpc::ClusterNativeAccess* clNative, TObjArray* out) +{ + mQA->runQA(tracks, tracksMC, clNative); + if (mOutputMode == outputPostprocessed || mOutputMode == outputLayout) { + mQA->postprocess(*out); + } +} + +int Tracking::postprocess(std::vector<TH1F>& in1, std::vector<TH2F>& in2, std::vector<TH1D>& in3, TObjArray& out) +{ + return mQA->postprocessExternal(in1, in2, in3, out, QAMODE); +} + +void Tracking::getHists(const std::vector<TH1F>*& h1, const std::vector<TH2F>*& h2, const std::vector<TH1D>*& h3) const +{ + mQA->getHists(h1, h2, h3); +} diff --git a/Detectors/TPC/qc/src/Tracks.cxx b/Detectors/TPC/qc/src/Tracks.cxx index 82b7a07f1642a..6b24975cd88a3 100644 --- a/Detectors/TPC/qc/src/Tracks.cxx +++ b/Detectors/TPC/qc/src/Tracks.cxx @@ -30,18 +30,18 @@ void Tracks::initializeHistograms() mHist1D.emplace_back("hNClustersBeforeCuts", "Number of clusters before cuts;# TPC clusters", 160, -0.5, 159.5); mHist1D.emplace_back("hNClustersAfterCuts", "Number of clusters after cuts;# TPC clusters", 160, -0.5, 159.5); mHist1D.emplace_back("hEta", "Pseudorapidity;eta", 400, -2., 2.); - mHist1D.emplace_back("hPhiAside", "Azimuthal angle, A side;phi", 360, -M_PI, M_PI); - mHist1D.emplace_back("hPhiCside", "Azimuthal angle, C side;phi", 360, -M_PI, M_PI); + mHist1D.emplace_back("hPhiAside", "Azimuthal angle, A side;phi", 360, 0., 2 * M_PI); + mHist1D.emplace_back("hPhiCside", "Azimuthal angle, C side;phi", 360, 0., 2 * M_PI); mHist1D.emplace_back("hPt", "Transverse momentum;p_T", 200, 0., 10.); mHist1D.emplace_back("hSign", "Sign of electric charge;charge sign", 3, -1.5, 1.5); mHist2D.emplace_back("h2DNClustersEta", "Number of clusters vs. eta;eta;# TPC clusters", 400, -2., 2., 160, -0.5, 159.5); - mHist2D.emplace_back("h2DNClustersPhiAside", "Number of clusters vs. phi, A side ;phi;# TPC clusters", 360, -M_PI, M_PI, 160, -0.5, 159.5); - mHist2D.emplace_back("h2DNClustersPhiCside", "Number of clusters vs. phi, C side ;phi;# TPC clusters", 360, -M_PI, M_PI, 160, -0.5, 159.5); + mHist2D.emplace_back("h2DNClustersPhiAside", "Number of clusters vs. phi, A side ;phi;# TPC clusters", 360, 0., 2 * M_PI, 160, -0.5, 159.5); + mHist2D.emplace_back("h2DNClustersPhiCside", "Number of clusters vs. phi, C side ;phi;# TPC clusters", 360, 0., 2 * M_PI, 160, -0.5, 159.5); mHist2D.emplace_back("h2DNClustersPt", "Number of clusters vs. p_T;p_T;# TPC clusters", 200, 0., 10., 160, -0.5, 159.5); - mHist2D.emplace_back("h2DEtaPhi", "Tracks in eta vs. phi;phi;eta", 360, -M_PI, M_PI, 400, -2., 2.); - mHist2D.emplace_back("h2DEtaPhiNeg", "Negative tracks in eta vs. phi;phi;eta", 360, -M_PI, M_PI, 400, -2., 2.); - mHist2D.emplace_back("h2DEtaPhiPos", "Positive tracks in eta vs. phi;phi;eta", 360, -M_PI, M_PI, 400, -2., 2.); + mHist2D.emplace_back("h2DEtaPhi", "Tracks in eta vs. phi;phi;eta", 360, 0., 2 * M_PI, 400, -2., 2.); + mHist2D.emplace_back("h2DEtaPhiNeg", "Negative tracks in eta vs. phi;phi;eta", 360, 0., 2 * M_PI, 400, -2., 2.); + mHist2D.emplace_back("h2DEtaPhiPos", "Positive tracks in eta vs. phi;phi;eta", 360, 0., 2 * M_PI, 400, -2., 2.); } //______________________________________________________________________________ @@ -70,9 +70,9 @@ bool Tracks::processTrack(const o2::tpc::TrackTPC& track) // ===| cuts |=== // hard coded cuts. Should be more configural in future - if (nCls < 20) { - return false; - } + //if (nCls < 20) { + // return false; + //} // ===| 1D histogram filling |=== mHist1D[1].Fill(nCls); diff --git a/Detectors/TPC/reconstruction/CMakeLists.txt b/Detectors/TPC/reconstruction/CMakeLists.txt index 05469cde4aac4..c97696459ce53 100644 --- a/Detectors/TPC/reconstruction/CMakeLists.txt +++ b/Detectors/TPC/reconstruction/CMakeLists.txt @@ -9,6 +9,7 @@ # submit itself to any jurisdiction. o2_add_library(TPCReconstruction + TARGETVARNAME targetName SOURCES src/AdcClockMonitor.cxx src/ClustererTask.cxx src/GBTFrame.cxx @@ -27,7 +28,8 @@ o2_add_library(TPCReconstruction src/TPCFastTransformHelperO2.cxx src/CTFCoder.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::SimulationDataFormat - O2::TPCBase O2::GPUTracking) + O2::TPCBase O2::GPUTracking + O2::DetectorsRaw) o2_target_root_dictionary( TPCReconstruction @@ -121,7 +123,7 @@ o2_add_test_root_macro(macro/createTPCSpaceChargeCorrection.C PUBLIC_LINK_LIBRARIES O2::TPCReconstruction O2::CommonConstants O2::CommonUtils - O2::TPCSpaceChargeBase + O2::TPCSpaceCharge LABELS tpc) o2_add_test_root_macro(macro/findKrBoxCluster.C @@ -132,3 +134,10 @@ o2_add_test_root_macro(macro/findKrBoxCluster.C # target which is built after reconstruction # o2_add_test_root_macro(macro/testTracks.C PUBLIC_LINK_LIBRARIES # ReconstructionDataFormats O2::TPCBase O2::DataFormatsTPC O2::TPCSimulation) + +if(OpenMP_CXX_FOUND) + message(STATUS "GPU: Using OpenMP: ${OpenMP_CXX_SPEC_DATE}") + # Must be private, depending libraries might be compiled by compiler not understanding -fopenmp + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index 84b70c69b6b73..6e2bca379cfcd 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -18,9 +18,14 @@ #include <algorithm> #include <iterator> #include <string> +#include <cassert> +#include <type_traits> +#include <typeinfo> +#include <vector> #include "DataFormatsTPC/CTF.h" #include "DataFormatsTPC/CompressedClusters.h" #include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/CTFCoderBase.h" #include "rANS/rans.h" class TTree; @@ -30,15 +35,21 @@ namespace o2 namespace tpc { -class CTFCoder +class CTFCoder : public o2::ctf::CTFCoderBase { public: + CTFCoder() : o2::ctf::CTFCoderBase(CTF::getNBlocks(), o2::detectors::DetID::TPC) {} + ~CTFCoder() = default; + /// entropy-encode compressed clusters to flat buffer template <typename VEC> - static void encode(VEC& buff, const CompressedClusters& ccl); + void encode(VEC& buff, const CompressedClusters& ccl); template <typename VEC> - static void decode(const CTF::base& ec, VEC& buff); + void decode(const CTF::base& ec, VEC& buff); + + void createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op); + size_t estimateCompressedSize(const CompressedClusters& ccl); static size_t constexpr Alignment = 16; static size_t estimateSize(CompressedClusters& c); @@ -62,9 +73,63 @@ class CTFCoder ptrR += res ? sz + (ALG - res) : sz; } + bool getCombineColumns() const { return mCombineColumns; } + void setCombineColumns(bool v) { mCombineColumns = v; } + + private: + void checkDataDictionaryConsistency(const CTFHeader& h); + + template <int NU, int NL> + static constexpr auto MVAR() + { + typename std::conditional<(NU + NL > 16), uint32_t, typename std::conditional<(NU + NL > 8), uint16_t, uint8_t>::type>::type tp = 0; + return tp; + } + template <int NU, int NL> + static constexpr auto MPTR() + { + typename std::conditional<(NU + NL > 16), uint32_t, typename std::conditional<(NU + NL > 8), uint16_t, uint8_t>::type>::type* tp = nullptr; + return tp; + } +#define MTYPE(A, B) decltype(CTFCoder::MVAR<A, B>()) + + template <int NU, int NL, typename CU, typename CL> + static void splitColumns(const std::vector<MTYPE(NU, NL)>& vm, CU*& vu, CL*& vl); + + template <int NU, int NL, typename CU, typename CL> + static auto mergeColumns(const CU* vu, const CL* vl, size_t nelem); + + bool mCombineColumns = false; // combine correlated columns + ClassDefNV(CTFCoder, 1); }; +/// split words of input vector to columns assigned to provided pointers (memory must be allocated in advance) +template <int NU, int NL, typename CU, typename CL> +void CTFCoder::splitColumns(const std::vector<MTYPE(NU, NL)>& vm, CU*& vu, CL*& vl) +{ + static_assert(NU <= sizeof(CU) * 8 && NL <= sizeof(CL) * 8, "output columns bit count is wrong"); + size_t n = vm.size(); + for (size_t i = 0; i < n; i++) { + vu[i] = static_cast<CU>(vm[i] >> NL); + vl[i] = static_cast<CL>(vm[i] & ((0x1 << NL) - 1)); + } +} + +/// merge elements of 2 columns pointed by vu and vl to a single vector with wider field +template <int NU, int NL, typename CU, typename CL> +auto CTFCoder::mergeColumns(const CU* vu, const CL* vl, size_t nelem) +{ + // merge 2 columns to 1 + static_assert(NU <= sizeof(NU) * 8 && NL <= sizeof(NL) * 8, "input columns bit count is wrong"); + std::vector<MTYPE(NU, NL)> outv; + outv.reserve(nelem); + for (size_t i = 0; i < nelem; i++) { + outv.push_back((static_cast<MTYPE(NU, NL)>(vu[i]) << NL) | static_cast<MTYPE(NU, NL)>(vl[i])); + } + return std::move(outv); +} + /// entropy-encode clusters to buffer with CTF template <typename VEC> void CTFCoder::encode(VEC& buff, const CompressedClusters& ccl) @@ -97,37 +162,86 @@ void CTFCoder::encode(VEC& buff, const CompressedClusters& ccl) MD::EENCODE //nSliceRowClusters }; + // book output size with some margin + auto szIni = estimateCompressedSize(ccl); + buff.resize(szIni); + auto ec = CTF::create(buff); - ec->setHeader(reinterpret_cast<const CompressedClustersCounters&>(ccl)); + uint32_t flags = 0; + if (mCombineColumns) { + flags |= CTFHeader::CombinedColumns; + } + ec->setHeader(CTFHeader{reinterpret_cast<const CompressedClustersCounters&>(ccl), flags}); ec->getANSHeader().majorVersion = 0; ec->getANSHeader().minorVersion = 1; // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec -#define ENCODE CTF::get(buff.data())->encode +#define ENCODETPC(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get()); // clang-format off - ENCODE(ccl.qTotA, ccl.qTotA + ccl.nAttachedClusters, CTF::BLCqTotA, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCqTotA], &buff); - ENCODE(ccl.qMaxA, ccl.qMaxA + ccl.nAttachedClusters, CTF::BLCqMaxA, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCqMaxA], &buff); - ENCODE(ccl.flagsA, ccl.flagsA + ccl.nAttachedClusters, CTF::BLCflagsA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCflagsA], &buff); - ENCODE(ccl.rowDiffA, ccl.rowDiffA + ccl.nAttachedClustersReduced, CTF::BLCrowDiffA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCrowDiffA], &buff); - ENCODE(ccl.sliceLegDiffA, ccl.sliceLegDiffA + ccl.nAttachedClustersReduced, CTF::BLCsliceLegDiffA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCsliceLegDiffA], &buff); - ENCODE(ccl.padResA, ccl.padResA + ccl.nAttachedClustersReduced, CTF::BLCpadResA, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCpadResA], &buff); - ENCODE(ccl.timeResA, ccl.timeResA + ccl.nAttachedClustersReduced, CTF::BLCtimeResA, o2::rans::ProbabilityBits25Bit, optField[CTF::BLCtimeResA], &buff); - ENCODE(ccl.sigmaPadA, ccl.sigmaPadA + ccl.nAttachedClusters, CTF::BLCsigmaPadA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCsigmaPadA], &buff); - ENCODE(ccl.sigmaTimeA, ccl.sigmaTimeA + ccl.nAttachedClusters, CTF::BLCsigmaTimeA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCsigmaTimeA], &buff); - ENCODE(ccl.qPtA, ccl.qPtA + ccl.nTracks, CTF::BLCqPtA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCqPtA], &buff); - ENCODE(ccl.rowA, ccl.rowA + ccl.nTracks, CTF::BLCrowA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCrowA], &buff); - ENCODE(ccl.sliceA, ccl.sliceA + ccl.nTracks, CTF::BLCsliceA, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCsliceA], &buff); - ENCODE(ccl.timeA, ccl.timeA + ccl.nTracks, CTF::BLCtimeA, o2::rans::ProbabilityBits25Bit, optField[CTF::BLCtimeA], &buff); - ENCODE(ccl.padA, ccl.padA + ccl.nTracks, CTF::BLCpadA, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCpadA], &buff); - ENCODE(ccl.qTotU, ccl.qTotU + ccl.nUnattachedClusters, CTF::BLCqTotU, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCqTotU], &buff); - ENCODE(ccl.qMaxU, ccl.qMaxU + ccl.nUnattachedClusters, CTF::BLCqMaxU, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCqMaxU], &buff); - ENCODE(ccl.flagsU, ccl.flagsU + ccl.nUnattachedClusters, CTF::BLCflagsU, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCflagsU], &buff); - ENCODE(ccl.padDiffU, ccl.padDiffU + ccl.nUnattachedClusters, CTF::BLCpadDiffU, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCpadDiffU], &buff); - ENCODE(ccl.timeDiffU, ccl.timeDiffU + ccl.nUnattachedClusters, CTF::BLCtimeDiffU, o2::rans::ProbabilityBits25Bit, optField[CTF::BLCtimeDiffU], &buff); - ENCODE(ccl.sigmaPadU, ccl.sigmaPadU + ccl.nUnattachedClusters, CTF::BLCsigmaPadU, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCsigmaPadU], &buff); - ENCODE(ccl.sigmaTimeU, ccl.sigmaTimeU + ccl.nUnattachedClusters, CTF::BLCsigmaTimeU, o2::rans::ProbabilityBits8Bit, optField[CTF::BLCsigmaTimeU], &buff); - ENCODE(ccl.nTrackClusters, ccl.nTrackClusters + ccl.nTracks, CTF::BLCnTrackClusters, o2::rans::ProbabilityBits16Bit, optField[CTF::BLCnTrackClusters], &buff); - ENCODE(ccl.nSliceRowClusters, ccl.nSliceRowClusters + ccl.nSliceRows, CTF::BLCnSliceRowClusters, o2::rans::ProbabilityBits25Bit, optField[CTF::BLCnSliceRowClusters], &buff); + + if (mCombineColumns) { + auto mrg = mergeColumns<CTF::NBitsQTot, CTF::NBitsQMax>(ccl.qTotA, ccl.qMaxA, ccl.nAttachedClusters); + ENCODETPC(&mrg[0], (&mrg[0]) + ccl.nAttachedClusters, CTF::BLCqTotA, 0); + } + else { + ENCODETPC(ccl.qTotA, ccl.qTotA + ccl.nAttachedClusters, CTF::BLCqTotA, 0); + } + ENCODETPC(ccl.qMaxA, ccl.qMaxA + (mCombineColumns ? 0 : ccl.nAttachedClusters), CTF::BLCqMaxA, 0); + + ENCODETPC(ccl.flagsA, ccl.flagsA + ccl.nAttachedClusters, CTF::BLCflagsA, 0); + + if (mCombineColumns) { + auto mrg = mergeColumns<CTF::NBitsRowDiff, CTF::NBitsSliceLegDiff>(ccl.rowDiffA, ccl.sliceLegDiffA, ccl.nAttachedClustersReduced); + ENCODETPC(&mrg[0], (&mrg[0]) + ccl.nAttachedClustersReduced, CTF::BLCrowDiffA, 0); + } + else { + ENCODETPC(ccl.rowDiffA, ccl.rowDiffA + ccl.nAttachedClustersReduced, CTF::BLCrowDiffA, 0); + } + ENCODETPC(ccl.sliceLegDiffA, ccl.sliceLegDiffA + (mCombineColumns ? 0 : ccl.nAttachedClustersReduced), CTF::BLCsliceLegDiffA, 0); + + ENCODETPC(ccl.padResA, ccl.padResA + ccl.nAttachedClustersReduced, CTF::BLCpadResA, 0); + ENCODETPC(ccl.timeResA, ccl.timeResA + ccl.nAttachedClustersReduced, CTF::BLCtimeResA, 0); + + if (mCombineColumns) { + auto mrg = mergeColumns<CTF::NBitsSigmaPad, CTF::NBitsSigmaTime>(ccl.sigmaPadA, ccl.sigmaTimeA, ccl.nAttachedClusters); + ENCODETPC(&mrg[0], &mrg[0] + ccl.nAttachedClusters, CTF::BLCsigmaPadA, 0); + } + else { + ENCODETPC(ccl.sigmaPadA, ccl.sigmaPadA + ccl.nAttachedClusters, CTF::BLCsigmaPadA, 0); + } + ENCODETPC(ccl.sigmaTimeA, ccl.sigmaTimeA + (mCombineColumns ? 0 : ccl.nAttachedClusters), CTF::BLCsigmaTimeA, 0); + + ENCODETPC(ccl.qPtA, ccl.qPtA + ccl.nTracks, CTF::BLCqPtA, 0); + ENCODETPC(ccl.rowA, ccl.rowA + ccl.nTracks, CTF::BLCrowA, 0); + ENCODETPC(ccl.sliceA, ccl.sliceA + ccl.nTracks, CTF::BLCsliceA, 0); + ENCODETPC(ccl.timeA, ccl.timeA + ccl.nTracks, CTF::BLCtimeA, 0); + ENCODETPC(ccl.padA, ccl.padA + ccl.nTracks, CTF::BLCpadA, 0); + + if (mCombineColumns) { + auto mrg = mergeColumns<CTF::NBitsQTot, CTF::NBitsQMax>(ccl.qTotU,ccl.qMaxU,ccl.nUnattachedClusters); + ENCODETPC(&mrg[0], &mrg[0] + ccl.nUnattachedClusters, CTF::BLCqTotU, 0); + } + else { + ENCODETPC(ccl.qTotU, ccl.qTotU + ccl.nUnattachedClusters, CTF::BLCqTotU, 0); + } + ENCODETPC(ccl.qMaxU, ccl.qMaxU + (mCombineColumns ? 0 : ccl.nUnattachedClusters), CTF::BLCqMaxU, 0); + + ENCODETPC(ccl.flagsU, ccl.flagsU + ccl.nUnattachedClusters, CTF::BLCflagsU, 0); + ENCODETPC(ccl.padDiffU, ccl.padDiffU + ccl.nUnattachedClusters, CTF::BLCpadDiffU, 0); + ENCODETPC(ccl.timeDiffU, ccl.timeDiffU + ccl.nUnattachedClusters, CTF::BLCtimeDiffU, 0); + + if (mCombineColumns) { + auto mrg = mergeColumns<CTF::NBitsSigmaPad, CTF::NBitsSigmaTime>(ccl.sigmaPadU, ccl.sigmaTimeU, ccl.nUnattachedClusters); + ENCODETPC(&mrg[0], &mrg[0] + ccl.nUnattachedClusters, CTF::BLCsigmaPadU, 0); + } + else { + ENCODETPC(ccl.sigmaPadU, ccl.sigmaPadU + ccl.nUnattachedClusters, CTF::BLCsigmaPadU, 0); + } + ENCODETPC(ccl.sigmaTimeU, ccl.sigmaTimeU + (mCombineColumns ? 0 : ccl.nUnattachedClusters), CTF::BLCsigmaTimeU, 0); + + ENCODETPC(ccl.nTrackClusters, ccl.nTrackClusters + ccl.nTracks, CTF::BLCnTrackClusters, 0); + ENCODETPC(ccl.nSliceRowClusters, ccl.nSliceRowClusters + ccl.nSliceRows, CTF::BLCnSliceRowClusters, 0); // clang-format on + CTF::get(buff.data())->print(getPrefix()); } /// decode entropy-encoded bloks to TPC CompressedClusters into the externally provided vector (e.g. PMR vector from DPL) @@ -136,7 +250,9 @@ void CTFCoder::decode(const CTF::base& ec, VEC& buffVec) { CompressedClusters cc; CompressedClustersCounters& ccCount = cc; - ccCount = ec.getHeader(); // ec.getHeader is a saved copy of the CompressedClustersCounters + auto& header = ec.getHeader(); + checkDataDictionaryConsistency(header); + ccCount = reinterpret_cast<const CompressedClustersCounters&>(header); CompressedClustersFlat* ccFlat = nullptr; size_t sizeCFlatBody = alignSize(ccFlat); size_t sz = sizeCFlatBody + estimateSize(cc); // total size of the buffVec accounting for the alignment @@ -147,34 +263,81 @@ void CTFCoder::decode(const CTF::base& ec, VEC& buffVec) setCompClusAddresses(cc, buff); ccFlat->set(sz, cc); // set offsets + ec.print(getPrefix()); // decode encoded data directly to destination buff +#define DECODETPC(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) // clang-format off - ec.decode(cc.qTotA, CTF::BLCqTotA); - ec.decode(cc.qMaxA, CTF::BLCqMaxA); - ec.decode(cc.flagsA, CTF::BLCflagsA); - ec.decode(cc.rowDiffA, CTF::BLCrowDiffA); - ec.decode(cc.sliceLegDiffA, CTF::BLCsliceLegDiffA); - ec.decode(cc.padResA, CTF::BLCpadResA); - ec.decode(cc.timeResA, CTF::BLCtimeResA); - ec.decode(cc.sigmaPadA, CTF::BLCsigmaPadA); - ec.decode(cc.sigmaTimeA, CTF::BLCsigmaTimeA); - ec.decode(cc.qPtA, CTF::BLCqPtA); - ec.decode(cc.rowA, CTF::BLCrowA); - ec.decode(cc.sliceA, CTF::BLCsliceA); - ec.decode(cc.timeA, CTF::BLCtimeA); - ec.decode(cc.padA, CTF::BLCpadA); - ec.decode(cc.qTotU, CTF::BLCqTotU); - ec.decode(cc.qMaxU, CTF::BLCqMaxU); - ec.decode(cc.flagsU, CTF::BLCflagsU); - ec.decode(cc.padDiffU, CTF::BLCpadDiffU); - ec.decode(cc.timeDiffU, CTF::BLCtimeDiffU); - ec.decode(cc.sigmaPadU, CTF::BLCsigmaPadU); - ec.decode(cc.sigmaTimeU, CTF::BLCsigmaTimeU); - ec.decode(cc.nTrackClusters, CTF::BLCnTrackClusters); - ec.decode(cc.nSliceRowClusters, CTF::BLCnSliceRowClusters); + if (mCombineColumns) { + std::vector<MTYPE(CTF::NBitsQTot, CTF::NBitsQMax)> mrg; + DECODETPC(mrg, CTF::BLCqTotA); + splitColumns<CTF::NBitsQTot, CTF::NBitsQMax>(mrg, cc.qTotA, cc.qMaxA); + } + else { + DECODETPC(cc.qTotA, CTF::BLCqTotA); + DECODETPC(cc.qMaxA, CTF::BLCqMaxA); + } + + DECODETPC(cc.flagsA, CTF::BLCflagsA); + + if (mCombineColumns) { + std::vector<MTYPE(CTF::NBitsRowDiff, CTF::NBitsSliceLegDiff)> mrg; + DECODETPC(mrg, CTF::BLCrowDiffA); + splitColumns<CTF::NBitsRowDiff, CTF::NBitsSliceLegDiff>(mrg, cc.rowDiffA, cc.sliceLegDiffA); + } + else { + DECODETPC(cc.rowDiffA, CTF::BLCrowDiffA); + DECODETPC(cc.sliceLegDiffA, CTF::BLCsliceLegDiffA); + } + + DECODETPC(cc.padResA, CTF::BLCpadResA); + DECODETPC(cc.timeResA, CTF::BLCtimeResA); + + if (mCombineColumns) { + std::vector<MTYPE(CTF::NBitsSigmaPad, CTF::NBitsSigmaTime)> mrg; + DECODETPC(mrg, CTF::BLCsigmaPadA); + splitColumns<CTF::NBitsSigmaPad, CTF::NBitsSigmaTime>(mrg, cc.sigmaPadA, cc.sigmaTimeA); + } + else { + DECODETPC(cc.sigmaPadA, CTF::BLCsigmaPadA); + DECODETPC(cc.sigmaTimeA, CTF::BLCsigmaTimeA); + } + + DECODETPC(cc.qPtA, CTF::BLCqPtA); + DECODETPC(cc.rowA, CTF::BLCrowA); + DECODETPC(cc.sliceA, CTF::BLCsliceA); + DECODETPC(cc.timeA, CTF::BLCtimeA); + DECODETPC(cc.padA, CTF::BLCpadA); + + if (mCombineColumns) { + std::vector<MTYPE(CTF::NBitsQTot, CTF::NBitsQMax)> mrg; + DECODETPC(mrg, CTF::BLCqTotU); + splitColumns<CTF::NBitsQTot, CTF::NBitsQMax>(mrg, cc.qTotU, cc.qMaxU); + } + else { + DECODETPC(cc.qTotU, CTF::BLCqTotU); + DECODETPC(cc.qMaxU, CTF::BLCqMaxU); + } + + DECODETPC(cc.flagsU, CTF::BLCflagsU); + DECODETPC(cc.padDiffU, CTF::BLCpadDiffU); + DECODETPC(cc.timeDiffU, CTF::BLCtimeDiffU); + + if (mCombineColumns) { + std::vector<MTYPE(CTF::NBitsSigmaPad, CTF::NBitsSigmaTime)> mrg; + DECODETPC(mrg, CTF::BLCsigmaPadU); + splitColumns<CTF::NBitsSigmaPad, CTF::NBitsSigmaTime>(mrg, cc.sigmaPadU, cc.sigmaTimeU); + } + else { + DECODETPC(cc.sigmaPadU, CTF::BLCsigmaPadU); + DECODETPC(cc.sigmaTimeU, CTF::BLCsigmaTimeU); + } + + DECODETPC(cc.nTrackClusters, CTF::BLCnTrackClusters); + DECODETPC(cc.nSliceRowClusters, CTF::BLCnSliceRowClusters); // clang-format on } +#undef MTYPE } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/Clusterer.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/Clusterer.h index 881d71e504080..34405c1de783f 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/Clusterer.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/Clusterer.h @@ -17,7 +17,7 @@ #include <vector> #include <memory> -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "TPCBase/CalDet.h" @@ -33,9 +33,6 @@ class Digit; /// \brief Base Class for TPC clusterer class Clusterer { - protected: - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - public: /// Default Constructor Clusterer() = default; @@ -49,8 +46,8 @@ class Clusterer /// Processing all digits /// \param digits Container with TPC digits /// \param mcDigitTruth MC Digit Truth container - virtual void process(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth) = 0; - virtual void finishProcess(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth) = 0; + virtual void process(gsl::span<o2::tpc::Digit const> const& digits, o2::dataformats::ConstMCLabelContainerView const& mcDigitTruth) = 0; + virtual void finishProcess(gsl::span<o2::tpc::Digit const> const& digits, o2::dataformats::ConstMCLabelContainerView const& mcDigitTruth) = 0; /// Setter for noise object, noise will be added before cluster finding /// \param noiseObject CalDet object, containing noise simulation diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClustererTask.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/ClustererTask.h index f745ae1fedf3d..071ded988bdf7 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClustererTask.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/ClustererTask.h @@ -20,7 +20,7 @@ #include "DataFormatsTPC/Digit.h" #include "TPCReconstruction/HwClusterer.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsTPC/Helpers.h" #include "DataFormatsTPC/ClusterHardware.h" @@ -34,8 +34,6 @@ namespace tpc class ClustererTask : public FairTask { - - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; using OutputType = ClusterHardwareContainer8kb; public: @@ -68,11 +66,11 @@ class ClustererTask : public FairTask // Digit arrays std::unique_ptr<const std::vector<Digit>> mDigitsArray; ///< Array of TPC digits - std::unique_ptr<const MCLabelContainer> mDigitMCTruthArray; ///< Array for MCTruth information associated to digits in mDigitsArrray + std::unique_ptr<const o2::dataformats::ConstMCLabelContainerView> mDigitMCTruthArray; ///< Array for MCTruth information associated to digits in mDigitsArrray // Cluster arrays std::unique_ptr<std::vector<OutputType>> mHwClustersArray; ///< Array of clusters found by Hw Clusterfinder - std::unique_ptr<MCLabelContainer> mHwClustersMCTruthArray; ///< Array for MCTruth information associated to cluster in mHwClustersArrays + std::unique_ptr<o2::dataformats::MCLabelContainer> mHwClustersMCTruthArray; ///< Array for MCTruth information associated to cluster in mHwClustersArrays ClassDefOverride(ClustererTask, 1); }; diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/DigitalCurrentClusterIntegrator.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/DigitalCurrentClusterIntegrator.h index 1efcb10e7a5dd..e30ea4e2ff186 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/DigitalCurrentClusterIntegrator.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/DigitalCurrentClusterIntegrator.h @@ -44,18 +44,20 @@ class DigitalCurrentClusterIntegrator void integrateCluster(int sector, int row, float pad, unsigned int charge) { int ipad = ipad + 0.5; - if (ipad < 0) + if (ipad < 0) { ipad = 0; + } int maxPad = o2::tpc::Mapper::instance().getNumberOfPadsInRowSector(row); - if (ipad >= maxPad) + if (ipad >= maxPad) { ipad = maxPad - 1; + } mIntegratedCurrents[sector][row][ipad] += charge; } void clear(); //Clear all currents to 0 void reset(); //Free all allocated current buffers private: - std::unique_ptr<unsigned long long int[]> mIntegratedCurrents[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW]; + std::unique_ptr<unsigned long long int[]> mIntegratedCurrents[constants::MAXSECTOR][constants::MAXGLOBALPADROW]; }; } // namespace tpc diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/GBTFrameContainer.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/GBTFrameContainer.h index 264c686e2999f..c5f6a7afa26e8 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/GBTFrameContainer.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/GBTFrameContainer.h @@ -147,8 +147,9 @@ class GBTFrameContainer void setEnableStoreGBTFrames(bool val) { mEnableStoreGBTFrames = val; - if (!mEnableStoreGBTFrames) + if (!mEnableStoreGBTFrames) { mGBTFrames.resize(2); + } }; /// Extracts the digits after all 80 channels were transmitted (5*16) diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/GPUCATracking.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/GPUCATracking.h index 9ec0e70bdfca8..7ff7d2d0e8729 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/GPUCATracking.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/GPUCATracking.h @@ -59,7 +59,6 @@ class GPUCATracking int runTracking(o2::gpu::GPUO2InterfaceIOPtrs* data, o2::gpu::GPUInterfaceOutputs* outputs = nullptr); float getPseudoVDrift(); //Return artificial VDrift used to convert time to Z - int getNTracksASide() { return mNTracksASide; } void GetClusterErrors2(int row, float z, float sinPhi, float DzDs, short clusterState, float& ErrY2, float& ErrZ2) const; int registerMemoryForGPU(const void* ptr, size_t size); @@ -71,7 +70,6 @@ class GPUCATracking //The GPUCATracking class interfaces this library via this pointer to GPUTPCO2Interface class. static constexpr float sTrackMCMaxFake = 0.1; - int mNTracksASide = 0; }; } // namespace tpc diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/HardwareClusterDecoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/HardwareClusterDecoder.h index e84532380152d..84d1b2b5ed643 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/HardwareClusterDecoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/HardwareClusterDecoder.h @@ -30,6 +30,8 @@ namespace dataformats { template <typename TruthElement> class MCTruthContainer; +template <typename TruthElement> +class ConstMCTruthContainerView; } /// @class HardwareClusterDecoder @@ -76,7 +78,7 @@ class HardwareClusterDecoder /// @param outMCLabels optional pointer to MC output container int decodeClusters(std::vector<std::pair<const o2::tpc::ClusterHardwareContainer*, std::size_t>>& inputClusters, OutputAllocator outputAllocator, - const std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>* inMCLabels = nullptr, + const std::vector<o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>>* inMCLabels = nullptr, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* outMCLabels = nullptr); /// @brief Sort clusters and MC labels in place diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClusterer.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClusterer.h index b52030da6d50a..3d9fe3c9a2c76 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClusterer.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClusterer.h @@ -20,7 +20,7 @@ #include "TPCReconstruction/Clusterer.h" #include "DataFormatsTPC/Helpers.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include <vector> @@ -39,10 +39,9 @@ class ClusterHardware; /// \brief Class for TPC HW cluster finding class HwClusterer : public Clusterer { - private: - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - + using MCLabelContainer = o2::dataformats::MCLabelContainer; + using ConstMCLabelContainerView = o2::dataformats::ConstMCLabelContainerView; /// Main Constructor HwClusterer( std::vector<ClusterHardwareContainer8kb>* clusterOutputContainer, @@ -71,15 +70,15 @@ class HwClusterer : public Clusterer /// \param digits Container with TPC digits /// \param mcDigitTruth MC Digit Truth container /// \param clearContainerFirst Clears the outpcontainer for clusters and MC labels first, before processing - void process(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth) override; - void process(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth, bool clearContainerFirst); + void process(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth) override; + void process(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth, bool clearContainerFirst); /// Finish processing digits /// \param digits Container with TPC digits /// \param mcDigitTruth MC Digit Truth container /// \param clearContainerFirst Clears the outpcontainer for clusters and MC labels first, before processing - void finishProcess(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth) override; - void finishProcess(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth, bool clearContainerFirst); + void finishProcess(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth) override; + void finishProcess(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth, bool clearContainerFirst); /// Switch for triggered / continuous readout /// \param isContinuous - false for triggered readout, true for continuous readout @@ -189,7 +188,9 @@ class HwClusterer : public Clusterer short mCurrentMcContainerInBuffer; ///< Bit field, where to find the current MC container in buffer short mSplittingMode; ///< Cluster splitting mode, 0 no splitting, 1 for minimum contributes half to both, 2 for miminum corresponds to left/older cluster int mClusterSector; ///< Sector to be processed - int mLastTimebin; ///< Last time bin of previous event + int mPreviousTimebin; ///< Last time bin of previous event + int mFirstTimebin; ///< First time bin to process + int mLastTimebin; ///< Last time bin to process unsigned mLastHB; ///< Last HB bin of previous event unsigned mPeakChargeThreshold; ///< Charge threshold for the central peak in ADC counts unsigned mContributionChargeThreshold; ///< Charge threshold for the contributing pads in ADC counts @@ -216,12 +217,12 @@ class HwClusterer : public Clusterer MCLabelContainer* mClusterMcLabelArray; ///< Pointer to MC Label container }; -inline void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, o2::dataformats::MCTruthContainer<o2::MCCompLabel> const* mcDigitTruth) +inline void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth) { process(digits, mcDigitTruth, true); } -inline void HwClusterer::finishProcess(gsl::span<o2::tpc::Digit const> const& digits, o2::dataformats::MCTruthContainer<o2::MCCompLabel> const* mcDigitTruth) +inline void HwClusterer::finishProcess(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth) { finishProcess(digits, mcDigitTruth, true); } @@ -274,8 +275,9 @@ inline Vc::uint_v HwClusterer::getFpOfADC(const Vc::uint_v value) inline short HwClusterer::getFirstSetBitOfField() { for (short i = 0; i < mTimebinsInBuffer; ++i) { - if ((mCurrentMcContainerInBuffer >> i) & 0x1) + if ((mCurrentMcContainerInBuffer >> i) & 0x1) { return i; + } } return -1; } diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClustererParam.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClustererParam.h index ef1316ce5f982..5a48f31ee4a1b 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClustererParam.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/HwClustererParam.h @@ -26,6 +26,8 @@ namespace tpc struct HwClustererParam : public o2::conf::ConfigurableParamHelper<HwClustererParam> { unsigned peakChargeThreshold = 2; ///< Charge threshold for the central peak in ADC counts unsigned contributionChargeThreshold = 0; ///< Charge threshold for the contributing pads in ADC counts + unsigned firstTimeBin = 0; ///< First time bin to process + unsigned lastTimeBin = 200000; ///< Last time bin to process short splittingMode = 0; ///< cluster splitting mode, 0 no splitting, 1 for minimum contributes half to both, 2 for miminum corresponds to left/older cluster, bool isContinuousReadout = true; ///< Switch for continuous readout bool rejectSinglePadClusters = false; ///< Switch to reject single pad clusters, sigmaPad2Pre == 0 diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/KrBoxClusterFinder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/KrBoxClusterFinder.h index a75765bce08de..6f8e18b135ecd 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/KrBoxClusterFinder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/KrBoxClusterFinder.h @@ -70,6 +70,7 @@ #include "DataFormatsTPC/Digit.h" #include "TPCReconstruction/KrCluster.h" +#include "TPCBase/Mapper.h" #include <tuple> #include <vector> @@ -123,6 +124,9 @@ class KrBoxClusterFinder static constexpr size_t MaxRows = 152; ///< Size of the map in row-direction static constexpr size_t MaxTimes = 550; ///< Size of the map in time-direction + /// Need an instance of Mapper to know position of pads + const Mapper& mMapperInstance = o2::tpc::Mapper::instance(); + KrCluster mTempCluster; ///< Used to save the cluster data /// Here the map is defined where all digits are temporarily stored diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReader.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReader.h index fe22594aee82c..b75c4c0fcbb9b 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReader.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReader.h @@ -247,8 +247,9 @@ inline std::shared_ptr<std::vector<uint16_t>> RawReader::getData(const PadPos& p inline std::shared_ptr<std::vector<uint16_t>> RawReader::getNextData(PadPos& padPos) { - if (mDataIterator == mData.end()) + if (mDataIterator == mData.end()) { return nullptr; + } std::map<PadPos, std::shared_ptr<std::vector<uint16_t>>>::iterator last = mDataIterator; mDataIterator++; padPos = last->first; diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h index 7ee3436a35816..f8ba8d82401ea 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h @@ -58,16 +58,21 @@ enum DebugLevel : uint8_t { /// data type enum class DataType : uint8_t { - TryToDetect = 0, ///< try to auto detect the mode - Continuous = 1, ///< continuous data taking - HBScaling = 2, ///< heart beat sclaing mode - Triggered = 3 ///< triggered data + Continuous = 1, ///< continuous data taking + HBScaling = 2, ///< heart beat sclaing mode + Triggered = 3, ///< triggered data }; /// file type enum class ReaderType : uint8_t { - FLPData = 0, ///< single files from FLP as 8k pages with RAWDataHeader - EPNData = 1 ///< STF builder data merged on EPN + FLP = 0, ///< single files from FLP as 8k pages with RAWDataHeader + EPN = 1, ///< STF builder data merged on EPN +}; + +/// data type +enum class RAWDataType : uint8_t { + GBT = 0, ///< GBT encoded raw data + LinkZS = 1, ///< Link based zero suppression }; using RDH = o2::header::RAWDataHeader; @@ -104,10 +109,11 @@ class ADCRawData /// limit. void setNumTimebins(uint32_t numTB) { - if (numTB >= mADCRaw[mOutputStream].size()) + if (numTB >= mADCRaw[mOutputStream].size()) { mNumTimeBins = mADCRaw[mOutputStream].size(); - else + } else { mNumTimeBins = numTB; + } }; /// number of time bin for selected stream @@ -161,6 +167,10 @@ class SyncPosition /// return half word position uint32_t getHalfWordPosition() const { return mHalfWordPos; }; + uint32_t getFrameNumber() const { return mFrameNum; } + + uint32_t getPacketNumber() const { return mPacketNum; } + /// return if sync was found bool synched() const { return mSyncFound; }; @@ -203,7 +213,7 @@ class GBTFrame /// set syncronisation found for stream /// \param stream stream - bool mSyncFound(int stream) { return mSyncPos[stream].synched(); }; + bool syncFound(int stream) { return mSyncPos[stream].synched(); }; /// get the syncronisation array const SyncArray& getSyncArray() const { return mSyncPos; } @@ -298,6 +308,9 @@ class RawReaderCRUEventSync using RDH = o2::header::RAWDataHeader; + /// default ctor + RawReaderCRUEventSync() { reset(); } + // --------------------------------------------------------------------------- /// \struct LinkInfo /// \brief helper to store link information in an event @@ -386,13 +399,15 @@ class RawReaderCRUEventSync /// get link information for a specific event and cru LinkInfo& getLinkInfo(const RDH& rdh, DataType dataType) { - // check if event is already registered. If not create a new one. - auto& event = createEvent(rdh, dataType); + if (!mLastEvent) { + createEvent(rdh, dataType); + } const auto dataWrapperID = RDHUtils::getEndPointID(rdh); const auto linkID = RDHUtils::getLinkID(rdh); const auto globalLinkID = linkID + dataWrapperID * 12; - return event.CRUInfoArray[RDHUtils::getCRUID(rdh)].LinkInformation[globalLinkID]; + + return mLastEvent->CRUInfoArray[RDHUtils::getCRUID(rdh)].LinkInformation[globalLinkID]; } /// get array with all link informaiton for a specific event number and cru @@ -442,13 +457,19 @@ class RawReaderCRUEventSync void setLinksSeen(const CRU cru, const std::bitset<MaxNumberOfLinks>& links); /// set a cru as seen - void setCRUSeen(const CRU cru) { mCRUSeen[cru] = true; } + void setCRUSeen(const CRU cru, const uint16_t reader = 0) { mCRUSeen[cru] = reader; } + + /// return CRU seen information + const auto& getCRUSeen() const { return mCRUSeen; } + + /// get the reader associated to the CRU + uint32_t getReaderNumber(uint32_t cru) { return mCRUSeen[cru]; } /// reset all information void reset() { mEventInformation.clear(); - mCRUSeen.reset(); + mCRUSeen.fill(-1); } /// overloading output stream operator @@ -459,8 +480,9 @@ class RawReaderCRUEventSync } private: - EventInfoVector mEventInformation{}; ///< event information - std::bitset<CRU::MaxCRU> mCRUSeen{}; ///< if cru was seen + EventInfoVector mEventInformation{}; ///< event information + EventInfo* mLastEvent{nullptr}; ///< Last event that was created + std::array<int16_t, CRU::MaxCRU> mCRUSeen{}; ///< if cru was seen, number is for which decoder ClassDefNV(RawReaderCRUEventSync, 0); // event synchronisation for raw reader instances }; // class RawReaderCRUEventSync @@ -485,12 +507,14 @@ class RawReaderCRU uint32_t stream = 0, uint32_t debugLevel = 0, uint32_t verbosity = 0, - const std::string_view outputFilePrefix = "") + const std::string_view outputFilePrefix = "", + uint32_t readerNumber = 0) : mDebugLevel(debugLevel), mVerbosity(verbosity), mNumTimeBins(numTimeBins), mLink(link), mStream(stream), + mReaderNumber(readerNumber), mCRU(), mFileSize(-1), mPacketsPerLink(), @@ -536,9 +560,15 @@ class RawReaderCRU /// set event number void setEventNumber(uint32_t eventNumber = 0) { mEventNumber = eventNumber; } + /// set the reader number in the manager + void setReaderNumber(uint32_t readerNumber) { mReaderNumber = readerNumber; } + /// set filling of ADC data map void setFillADCdataMap(bool fill) { mFillADCdataMap = fill; } + /// get filling of ADC data map + bool getFillADCdataMap() const { return mFillADCdataMap; } + /// get event number uint32_t getEventNumber() const { return mEventNumber; } @@ -603,9 +633,18 @@ class RawReaderCRU /// copy single events to another file void copyEvents(const std::vector<uint32_t>& eventNumbers, std::string outputDirectory, std::ios_base::openmode mode = std::ios_base::openmode(0)); + /// write GBT data into separate files per link + void writeGBTDataPerLink(std::string_view outputDirectory, int maxEvents = -1); + /// run a data filling callback function void runADCDataCallback(const ADCRawData& rawData); + /// set output file prefix + void setOutputFilePrefix(std::string_view prefix) { mOutputFilePrefix = prefix; } + + /// output file prefix + const std::string& getOutputFilePrefix() const { return mOutputFilePrefix; } + //=========================================================================== //===| Nested helper classes |=============================================== // @@ -642,10 +681,10 @@ class RawReaderCRU class PacketDescriptor { public: - PacketDescriptor(uint32_t headOff, uint32_t cruID, uint32_t linkID, uint32_t dataWrapperID, uint16_t memorySize = 7840, uint16_t packetSize = 8192) : mHeaderOffset(headOff), - mFEEID(cruID + (linkID << 9) + (dataWrapperID << 13)), - mMemorySize(memorySize), - mPacketSize(packetSize) + PacketDescriptor(size_t headOff, uint32_t cruID, uint32_t linkID, uint32_t dataWrapperID, uint16_t memorySize = 7840, uint16_t packetSize = 8192) : mHeaderOffset(headOff), + mFEEID(cruID + (linkID << 9) + (dataWrapperID << 13)), + mMemorySize(memorySize), + mPacketSize(packetSize) { } @@ -673,10 +712,10 @@ class RawReaderCRU } private: - uint32_t mHeaderOffset; ///< header offset - uint16_t mMemorySize; ///< payload size - uint16_t mPacketSize; ///< packet size - uint16_t mFEEID; ///< link ID -- BIT 0-8: CRUid -- BIT 9-12: LinkID -- BIT 13: DataWrapperID -- BIT 14,15: unused + size_t mHeaderOffset; ///< header offset + uint16_t mMemorySize; ///< payload size + uint16_t mPacketSize; ///< packet size + uint16_t mFEEID; ///< link ID -- BIT 0-8: CRUid -- BIT 9-12: LinkID -- BIT 13: DataWrapperID -- BIT 14,15: unused }; // =========================================================================== @@ -691,8 +730,9 @@ class RawReaderCRU uint32_t mLink; ///< present link being processed uint32_t mStream; ///< present stream being processed uint32_t mEventNumber = 0; ///< current event number to process + uint32_t mReaderNumber = 0; ///< raw reader number in manager CRU mCRU; ///< CRU - int mFileSize; ///< size of the input file + size_t mFileSize; ///< size of the input file bool mDumpTextFiles = false; ///< dump debugging text files bool mFillADCdataMap = true; ///< fill the ADC data map bool mForceCRU = false; ///< force CRU: overwrite value from RDH @@ -707,6 +747,8 @@ class RawReaderCRU std::map<PadPos, std::vector<uint16_t>> mADCdata; ///< decoded ADC data RawReaderCRUManager* mManager{nullptr}; ///< event synchronization information + std::ifstream mFileHandle; ///< file handle for input file + /// collect raw GBT data void collectGBTData(std::vector<o2::byte>& data); @@ -729,18 +771,19 @@ inline void GBTFrame::updateSyncCheck(SyncArray& syncArray) { const auto offset = mPrevHWpos ^ 4; - for (int s = 0; s < 5; s++) + for (int s = 0; s < 5; s++) { for (int h = 0; h < 4; h++) { const auto hPos = h + offset; // set position of last filled HW // shift in a 1 if the halfword is 0x15 - if (mFrameHalfWords[s][hPos] == 0x15) + if (mFrameHalfWords[s][hPos] == 0x15) { mSyncCheckRegister[s] = (mSyncCheckRegister[s] << 1) | 1; - // shift in a 0 if the halfword is 0xA - else if (mFrameHalfWords[s][hPos] == 0xA) + // shift in a 0 if the halfword is 0xA + } else if (mFrameHalfWords[s][hPos] == 0xA) { mSyncCheckRegister[s] = (mSyncCheckRegister[s] << 1); - // otherwise reset the register to 0 - else + // otherwise reset the register to 0 + } else { mSyncCheckRegister[s] = 0; + } // std::cout << " SReg : " << s << " : " << std::hex << mSyncCheckRegister[s] << std::endl; // check the register content for the SYNC pattern // Patterh is : 1100.1100.1100.1100.1111.0000.1111.0000 = 0xCCCCF0F0 @@ -748,7 +791,8 @@ inline void GBTFrame::updateSyncCheck(SyncArray& syncArray) if (mSyncCheckRegister[s] == 0xCCCCF0F0 or mSyncCheckRegister[s] == 0x4CCCF0F0 or mSyncCheckRegister[s] == 0x0CCCF0F0) { syncArray[s].setPos(mPacketNum, mFrameNum, mFilePos, h); }; - }; + } + }; } inline void GBTFrame::updateSyncCheck(bool verbose) @@ -759,14 +803,15 @@ inline void GBTFrame::updateSyncCheck(bool verbose) for (int h = 0; h < 4; h++) { const auto hPos = h + offset; // set position of last filled HW // shift in a 1 if the halfword is 0x15 - if (mFrameHalfWords[s][hPos] == 0x15) + if (mFrameHalfWords[s][hPos] == 0x15) { mSyncCheckRegister[s] = (mSyncCheckRegister[s] << 1) | 1; - // shift in a 0 if the halfword is 0xA - else if (mFrameHalfWords[s][hPos] == 0xA) + // shift in a 0 if the halfword is 0xA + } else if (mFrameHalfWords[s][hPos] == 0xA) { mSyncCheckRegister[s] = (mSyncCheckRegister[s] << 1); - // otherwise reset the register to 0 - else + // otherwise reset the register to 0 + } else { mSyncCheckRegister[s] = 0; + } // std::cout << " SReg : " << s << " : " << std::hex << mSyncCheckRegister[s] << std::endl; // check the register content for the SYNC pattern // Patterh is : 1100.1100.1100.1100.1111.0000.1111.0000 = 0xCCCCF0F0 @@ -841,6 +886,7 @@ class RawReaderCRUManager { public: using ADCDataCallback = std::function<Int_t(const PadROCPos&, const CRU&, const gsl::span<const uint32_t>)>; + using EndReaderCallback = std::function<void()>; /// constructor RawReaderCRUManager() = default; @@ -855,7 +901,7 @@ class RawReaderCRUManager const std::string_view outputFilePrefix = "") //RawReaderCRU& createReader(std::string_view fileName, uint32_t numTimeBins) { - mRawReadersCRU.emplace_back(std::make_unique<RawReaderCRU>(inputFileName, numTimeBins, 0, stream, debugLevel, verbosity, outputFilePrefix)); + mRawReadersCRU.emplace_back(std::make_unique<RawReaderCRU>(inputFileName, numTimeBins, 0, stream, debugLevel, verbosity, outputFilePrefix, mRawReadersCRU.size())); mRawReadersCRU.back()->setManager(this); return *mRawReadersCRU.back().get(); } @@ -919,7 +965,12 @@ class RawReaderCRUManager void setDebugLevel(uint32_t debugLevel) { mDebugLevel = debugLevel; } /// set data type - void setDataType(DataType dataType) { mDataType = dataType; } + void setDataType(DataType dataType, RAWDataType rawType) + { + mDataType = dataType; + mRawDataType = rawType; + mDetectDataType = false; + } /// get data type DataType getDataType() const { return mDataType; } @@ -930,14 +981,25 @@ class RawReaderCRUManager /// copy single events from raw input files to another file static void copyEvents(const std::string_view inputFileNames, const std::vector<uint32_t> eventNumbers, std::string_view outputDirectory, std::ios_base::openmode mode = std::ios_base::openmode(0)); + /// copy single events from raw input files to another file + void writeGBTDataPerLink(std::string_view outputDirectory, int maxEvents = -1); + + /// copy single events from raw input files to another file + static void writeGBTDataPerLink(const std::string_view inputFileNames, std::string_view outputDirectory, int maxEvents = -1); + /// set a callback function void setADCDataCallback(ADCDataCallback function) { mADCDataCallback = function; } + /// process event calling mADCDataCallback to process values + void processEvent(uint32_t eventNumber, EndReaderCallback endReader = nullptr); + private: std::vector<std::unique_ptr<RawReaderCRU>> mRawReadersCRU{}; ///< cru type raw readers RawReaderCRUEventSync mEventSync{}; ///< event synchronisation uint32_t mDebugLevel{0}; ///< debug level - DataType mDataType{DataType::TryToDetect}; ///< data type + DataType mDataType{DataType::Continuous}; ///< data type + RAWDataType mRawDataType{RAWDataType::GBT}; ///< raw data type + bool mDetectDataType{true}; ///< try to detect data types bool mIsInitialized{false}; ///< if init was called already ADCDataCallback mADCDataCallback{nullptr}; ///< callback function for filling the ADC data diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/SyncPatternMonitor.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/SyncPatternMonitor.h index 6340c49542cd8..a788d63da9722 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/SyncPatternMonitor.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/SyncPatternMonitor.h @@ -102,10 +102,11 @@ inline short SyncPatternMonitor::addSequence(const short hw0, const short hw1, c inline void SyncPatternMonitor::checkWord(const short hw, const short pos) { ++mCheckedWords; - if (hw == SYNC_PATTERN[mPosition]) + if (hw == SYNC_PATTERN[mPosition]) { ++mPosition; - else if (!(mPosition == SYNC_START + 2 && hw == SYNC_PATTERN[mPosition - 1])) + } else if (!(mPosition == SYNC_START + 2 && hw == SYNC_PATTERN[mPosition - 1])) { mPosition = SYNC_START; + } // Don't reset mPosition at the beginning to avoid missing of start of sync pattern in cases like // // @@ -116,8 +117,9 @@ inline void SyncPatternMonitor::checkWord(const short hw, const short pos) // | // real start - if (mPosition == 32) + if (mPosition == 32) { patternFound(pos); + } }; inline void SyncPatternMonitor::patternFound(const short hw) diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h index c042ac444a65c..1ec1a9c8a155c 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h @@ -74,7 +74,7 @@ class TPCFastTransformHelperO2 /// initialization void init(); /// get space charge correction in internal TPCFastTransform coordinates su,sv->dx,du,dv - int getSpaceChargeCorrection(int slice, int row, float su, float sv, float& dx, float& du, float& dv); + int getSpaceChargeCorrection(int slice, int row, double su, double sv, double& dx, double& du, double& dv); static TPCFastTransformHelperO2* sInstance; ///< singleton instance bool mIsInitialized = 0; ///< initialization flag diff --git a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C index 7c0c46d1e2a65..afd75f772ab10 100644 --- a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C +++ b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C @@ -18,7 +18,7 @@ /eos/user/e/ehellbar/SpaceCharge/data/RUN3/InputSCDensityHistograms Run macro: - root -l -b -q $O2_SRC/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C+\(180,65,65,\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"tpctransformSCcorrection.root\"\) + root -l -b -q $O2_SRC/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C+\(\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"tpctransformSCcorrection.root\"\) Test macro compilation: .L $O2_SRC/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C+ @@ -31,9 +31,8 @@ #include "TFile.h" #include "TH3.h" #include "TLatex.h" -#include "TMatrixD.h" -#include "AliTPCSpaceCharge3DCalc.h" +#include "TPCSpaceCharge/SpaceCharge.h" #include "CommonConstants/MathConstants.h" #include "CommonUtils/TreeStreamRedirector.h" @@ -43,39 +42,36 @@ using namespace o2; using namespace tpc; using namespace gpu; -std::unique_ptr<AliTPCSpaceCharge3DCalc> spaceCharge = nullptr; +/// \param nPhi number of phi bins of original correction lookup table +/// \param nR number of r bins of original correction lookup table, has to be 2^n + 1 +/// \param nZ number of z bins of original correction lookup table, has to be 2^n + 1 +const int nPhi = 180; +const int nR = 129; +const int nZ = 129; +using SC = o2::tpc::SpaceCharge<double, nZ, nR, nPhi>; +std::unique_ptr<SC> spaceCharge; void getSpaceChargeCorrection(const int roc, const double XYZ[3], double dXdYdZ[3]); -void initSpaceCharge(const int nPhi, const int nR, const int nZ, const int interpolationOrder, - const char* histoFileName, const char* histoName); +void initSpaceCharge(const char* histoFileName, const char* histoName); void DumpFlatObjectToFile(const TPCFastTransform* obj, const char* file); std::unique_ptr<TPCFastTransform> ReadFlatObjectFromFile(const char* file); -void debugInterpolation(utils::TreeStreamRedirector& pcstream, - const o2::gpu::TPCFastTransformGeo& geo, - TPCFastTransform* fastTransform); -void debugGridpoints(utils::TreeStreamRedirector& pcstream, - const o2::gpu::TPCFastTransformGeo& geo, - TPCFastTransform* fastTransform); +void debugInterpolation(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform); +void debugGridpoints(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform); -/// Creates TPCFastTransform object for TPC space-charge correction, stores it in a file and provides a deub tree if requested -/// \param nPhi number of phi bins of original correction lookup table -/// \param nR number of r bins of original correction lookup table, has to be 2^n + 1 -/// \param nZ number of z bins of original correction lookup table, has to be 2^n + 1 +/// Creates TPCFastTransform object for TPC space-charge correction, stores it in a file and provides a debug tree if requested /// \param histoFileName path and name to the root file containing input space-charge density histograms /// \param histoName name of the input space-charge density histogram /// \param outputFileName name of the output file to store the TPCFastTransform object in /// \param debug create debug tree comparing original corrections and spline interpolations from TPCFastTransform (1 = on the spline interpolation grid, 2 = on the original lookup table grid) void createTPCSpaceChargeCorrection( - const int nPhi = 180, const int nR = 65, const int nZ = 65, const char* histoFileName = "InputSCDensityHistograms_10000events.root", const char* histoName = "inputSCDensity3D_10000_avg", const char* outputFileName = "tpctransform.root", const int debug = 0) { - const int interpolationOrder = 2.; - initSpaceCharge(nPhi, nR, nZ, interpolationOrder, histoFileName, histoName); + initSpaceCharge(histoFileName, histoName); TPCFastTransformHelperO2::instance()->setSpaceChargeCorrection(getSpaceChargeCorrection); std::unique_ptr<TPCFastTransform> fastTransform(TPCFastTransformHelperO2::instance()->create(0)); @@ -84,7 +80,7 @@ void createTPCSpaceChargeCorrection( if (debug > 0) { const o2::gpu::TPCFastTransformGeo& geo = fastTransform->getGeometry(); - utils::TreeStreamRedirector pcstream(TString::Format("fastTransformUnitTest_debug%d_gridsize%d-%d-%d_order%d.root", debug, nPhi, nR, nZ, interpolationOrder).Data(), "recreate"); + utils::TreeStreamRedirector pcstream(TString::Format("fastTransformUnitTest_debug%d_gridsize%d-%d-%d.root", debug, nPhi, nR, nZ).Data(), "recreate"); switch (debug) { case 1: debugInterpolation(pcstream, geo, fastTransform.get()); @@ -100,54 +96,35 @@ void createTPCSpaceChargeCorrection( } /// Initialize calculation of original correction lookup tables -/// \param nPhi number of phi bins of original correction lookup table -/// \param nR number of r bins of original correction lookup table, has to be 2^n + 1 -/// \param nZ number of z bins of original correction lookup table, has to be 2^n + 1 -/// \param interpolationOrder interpolation method to use for original correction lookup tables (1 = linear interpolation, 2 = polynomial interpolation of 2nd order, >2 = cubic spline interpolation of higher orders) /// \param histoFileName path and name to the root file containing input space-charge density histograms /// \param histoName name of the input space-charge density histogram -void initSpaceCharge(const int nPhi, const int nR, const int nZ, const int interpolationOrder, - const char* histoFileName, const char* histoName) +void initSpaceCharge(const char* histoFileName, const char* histoName) { // get histogram with space-charge density std::unique_ptr<TFile> histoFile = std::unique_ptr<TFile>(TFile::Open(histoFileName)); std::unique_ptr<TH3> scHisto = std::unique_ptr<TH3>((TH3*)histoFile->Get(histoName)); // initialize space-charge object - spaceCharge = std::make_unique<AliTPCSpaceCharge3DCalc>(nR, nZ, nPhi, interpolationOrder, 3, 0); + spaceCharge = std::make_unique<SC>(); // input charge - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixChargeA = std::make_unique<std::unique_ptr<TMatrixD>[]>(nPhi); - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixChargeC = std::make_unique<std::unique_ptr<TMatrixD>[]>(nPhi); - for (int iphi = 0; iphi < nPhi; ++iphi) { - mMatrixChargeA[iphi] = std::make_unique<TMatrixD>(nR, nZ); - mMatrixChargeC[iphi] = std::make_unique<TMatrixD>(nR, nZ); - } - spaceCharge->GetChargeDensity((TMatrixD**)mMatrixChargeA.get(), (TMatrixD**)mMatrixChargeC.get(), (TH3*)scHisto.get(), nR, nZ, nPhi); - spaceCharge->SetInputSpaceChargeA((TMatrixD**)mMatrixChargeA.get()); - spaceCharge->SetInputSpaceChargeC((TMatrixD**)mMatrixChargeC.get()); + spaceCharge->fillChargeDensityFromHisto(*scHisto.get()); // further parameters - spaceCharge->SetOmegaTauT1T2(0.32, 1.f, 1.f); - spaceCharge->SetCorrectionType(0); // 0: use regular spaced LUTs, 1: use irregular LUTs (not recommended at the time as it is slower and results have to be verified) - spaceCharge->SetIntegrationStrategy(0); // 0: use default integration along drift lines, 1: use fast integration (not recommended at the time as results have to be verified) + spaceCharge->setOmegaTauT1T2(0.32, 1.f, 1.f); - // calculate LUTs - spaceCharge->ForceInitSpaceCharge3DPoissonIntegralDz(nR, nZ, nPhi, 300, 1e-8); + // start calculation of lookup tables (takes some time) + spaceCharge->calculateDistortionsCorrections(Side::A); + spaceCharge->calculateDistortionsCorrections(Side::C); } /// Function to get corrections from original lookup tables -/// \param roc readout chamber index (0 - 71) /// \param XYZ array with x, y and z position /// \param dXdYdZ array with correction dx, dy and dz void getSpaceChargeCorrection(const int roc, const double XYZ[3], double dXdYdZ[3]) { - const float xyzf[3] = {static_cast<float>(XYZ[0]), static_cast<float>(XYZ[1]), static_cast<float>(XYZ[2])}; - float dxdydzf[3] = {0.f, 0.f, 0.f}; - spaceCharge->GetCorrection(xyzf, roc, dxdydzf); - dXdYdZ[0] = static_cast<double>(dxdydzf[0]); - dXdYdZ[1] = static_cast<double>(dxdydzf[1]); - dXdYdZ[2] = static_cast<double>(dxdydzf[2]); + Side side = roc < 18 ? Side::A : Side::C; + spaceCharge->getCorrections(XYZ[0], XYZ[1], XYZ[2], side, dXdYdZ[0], dXdYdZ[1], dXdYdZ[2]); } /// Save TPCFastTransform to a file @@ -203,15 +180,12 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, for (int slice = 0; slice < geo.getNumberOfSlices(); slice += 1) { // for (int slice = 21; slice < 22; slice += 1) { std::cout << "debug slice " << slice << " ... " << std::endl; - const o2::gpu::TPCFastTransformGeo::SliceInfo& sliceInfo = geo.getSliceInfo(slice); for (int row = 0; row < geo.getNumberOfRows(); row++) { - int nPads = geo.getRowInfo(row).maxPad + 1; for (int pad = 0; pad < nPads; pad++) { - for (float time = 0; time < 500; time += 10) { // non-corrected point @@ -232,35 +206,32 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, // the original correction double gxyz[3] = {gx, gy, gz}; double gdC[3] = {0, 0, 0}; - getSpaceChargeCorrection(slice, gxyz, gdC); + Side side = slice < geo.getNumberOfSlicesA() ? Side::A : Side::C; + spaceCharge->getCorrections(gxyz[0], gxyz[1], gxyz[2], side, gdC[0], gdC[1], gdC[2]); float ldxC, ldyC, ldzC; - geo.convGlobalToLocal(slice, gdC[0], gdC[1], gdC[2], ldxC, ldyC, - ldzC); + geo.convGlobalToLocal(slice, gdC[0], gdC[1], gdC[2], ldxC, ldyC, ldzC); - float rC = std::sqrt((gx + gdC[0]) * (gx + gdC[0]) + - (gy + gdC[1]) * (gy + gdC[1])); + float rC = std::sqrt((gx + gdC[0]) * (gx + gdC[0]) + (gy + gdC[1]) * (gy + gdC[1])); // calculate distortion for the xyz0 - float ldD[3] = {0.0, 0.0, 0.0}; - float gdD[3] = {0.0, 0.0, 0.0}; + double ldD[3] = {0.0, 0.0, 0.0}; + double gdD[3] = {0.0, 0.0, 0.0}; float gxyzf[3] = {gx, gy, gz}; - float pointCyl[3] = {r, phi, gz}; + float pointCyl[3] = {gz, r, phi}; double efield[3] = {0.0, 0.0, 0.0}; - double charge = spaceCharge->GetChargeCylAC(pointCyl, slice); - double potential = spaceCharge->GetPotentialCylAC(pointCyl, slice); - spaceCharge->GetElectricFieldCyl(pointCyl, slice, efield); - spaceCharge->GetLocalDistortionCyl(pointCyl, slice, ldD); - spaceCharge->GetDistortion(gxyzf, slice, gdD); + double charge = spaceCharge->getChargeCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + double potential = spaceCharge->getPotentialCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + spaceCharge->getElectricFieldsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, efield[0], efield[1], efield[2]); + spaceCharge->getLocalDistortionsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, ldD[0], ldD[1], ldD[2]); + spaceCharge->getDistortions(gxyzf[0], gxyzf[1], gxyzf[2], side, gdD[0], gdD[1], gdD[2]); double gD[3] = {gx + gdD[0], gy + gdD[1], gz + gdD[2]}; - float rD = std::sqrt(gD[0] * gD[0] + gD[1] * gD[1]); // correction for the distorted point - double gdDC[3] = {0, 0, 0}; - getSpaceChargeCorrection(slice, gD, gdDC); + spaceCharge->getCorrections(gD[0], gD[1], gD[2], side, gdDC[0], gdDC[1], gdDC[2]); pcstream << "fastTransform" // internal coordinates @@ -313,9 +284,9 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, // << "charge=" << charge << "potential=" << potential - << "Er=" << efield[0] - << "Ephi=" << efield[1] - << "Ez=" << efield[2] + << "Ez=" << efield[0] + << "Er=" << efield[1] + << "Ephi=" << efield[2] << "\n"; } } @@ -327,51 +298,33 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, /// \param pcstream output stream /// \param geo TPCFastTransformGeo object /// \param fastTransform TPCFastTransform object -void debugGridpoints(utils::TreeStreamRedirector& pcstream, - const o2::gpu::TPCFastTransformGeo& geo, - TPCFastTransform* fastTransform) +void debugGridpoints(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform) { - int nR = spaceCharge->GetNRRows(); - int nZ = spaceCharge->GetNZColumns(); - int nPhi = spaceCharge->GetNPhiSlices(); - - float deltaR = - (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / - (nR - 1); - float deltaZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZ - 1); - float deltaPhi = o2::constants::math::TwoPI / nPhi; - for (int iside = 0; iside < 2; ++iside) { - + const Side side = iside == 0 ? Side::A : Side::C; for (int iphi = 0; iphi < nPhi; ++iphi) { - float phi = deltaPhi * iphi; - int sector = iside == 0 ? phi / o2::constants::math::TwoPI * 18 - : phi / o2::constants::math::TwoPI * 18 + 18; + float phi = spaceCharge->getPhiVertex(iphi, side); + int sector = iside == 0 ? phi / o2::constants::math::TwoPI * 18 : phi / o2::constants::math::TwoPI * 18 + 18; for (int ir = 0; ir < nR; ++ir) { - float radius = AliTPCPoissonSolver::fgkIFCRadius + deltaR * ir; + float radius = spaceCharge->getRVertex(ir, side); float gx0 = radius * std::cos(phi); float gy0 = radius * std::sin(phi); for (int iz = 0; iz < nZ; ++iz) { - float gz0 = iside == 0 ? deltaZ * iz - : -1 * deltaZ * iz; - + float gz0 = spaceCharge->getZVertex(iz, side); float x0 = 0.f, y0 = 0.f, z0 = 0.f; geo.convGlobalToLocal(sector, gx0, gy0, gz0, x0, y0, z0); if (x0 < geo.getRowInfo(0).x - 0.375) { continue; } - if (x0 >= (geo.getRowInfo(62).x + 0.375) && - x0 < (geo.getRowInfo(63).x - 0.5)) { + if (x0 >= (geo.getRowInfo(62).x + 0.375) && x0 < (geo.getRowInfo(63).x - 0.5)) { continue; } - if (x0 >= (geo.getRowInfo(96).x + 0.5) && - x0 < (geo.getRowInfo(97).x - 0.6)) { + if (x0 >= (geo.getRowInfo(96).x + 0.5) && x0 < (geo.getRowInfo(97).x - 0.6)) { continue; } - if (x0 >= (geo.getRowInfo(126).x + 0.6) && - x0 < (geo.getRowInfo(127).x - 0.75)) { + if (x0 >= (geo.getRowInfo(126).x + 0.6) && x0 < (geo.getRowInfo(127).x - 0.75)) { continue; } if (x0 > (geo.getRowInfo(151).x + 0.75)) { @@ -417,19 +370,18 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, double gcorr[3] = {0, 0, 0}; getSpaceChargeCorrection(sector, xyz, gcorr); float lcorr[3]; - geo.convGlobalToLocal(sector, gcorr[0], gcorr[1], gcorr[2], lcorr[0], - lcorr[1], lcorr[2]); + geo.convGlobalToLocal(sector, gcorr[0], gcorr[1], gcorr[2], lcorr[0], lcorr[1], lcorr[2]); float fxyz[3] = {gx0, gy0, gz0}; - float pointCyl[3] = {radius, phi, gz0}; + float pointCyl[3] = {gz0, radius, phi}; double efield[3] = {0.0, 0.0, 0.0}; - float distLocal[3] = {0.0, 0.0, 0.0}; - float dist[3] = {0.0, 0.0, 0.0}; - double charge = spaceCharge->GetChargeCylAC(pointCyl, sector); - double potential = spaceCharge->GetPotentialCylAC(pointCyl, sector); - spaceCharge->GetElectricFieldCyl(pointCyl, sector, efield); - spaceCharge->GetLocalDistortionCyl(pointCyl, sector, distLocal); - spaceCharge->GetDistortion(fxyz, sector, dist); + double distLocal[3] = {0.0, 0.0, 0.0}; + double dist[3] = {0.0, 0.0, 0.0}; + double charge = spaceCharge->getChargeCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + double potential = spaceCharge->getPotentialCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + spaceCharge->getElectricFieldsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, efield[0], efield[1], efield[2]); + spaceCharge->getLocalDistortionsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, distLocal[0], distLocal[1], distLocal[2]); + spaceCharge->getDistortions(fxyz[0], fxyz[1], fxyz[2], side, dist[0], dist[1], dist[2]); pcstream << "fastTransform" // internal coordinates @@ -472,9 +424,9 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, // << "charge=" << charge << "potential=" << potential - << "Er=" << efield[0] - << "Ephi=" << efield[1] - << "Ez=" << efield[2] + << "Ez=" << efield[0] + << "Er=" << efield[1] + << "Ephi=" << efield[2] << "\n"; } } @@ -485,7 +437,7 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, /// Make a simple QA plot of the debug stream void makeQAplot() { - TFile f("fastTransformUnitTest_debug1_gridsize180-65-65_order2.root"); + TFile f("fastTransformUnitTest_debug1_gridsize180-129-129.root"); TTree* tree = (TTree*)f.Get("fastTransform"); tree->SetMarkerSize(0.2); tree->SetMarkerStyle(21); diff --git a/Detectors/TPC/reconstruction/macro/findKrBoxCluster.C b/Detectors/TPC/reconstruction/macro/findKrBoxCluster.C index ec539c48bf7ca..23bac1ed5c25f 100644 --- a/Detectors/TPC/reconstruction/macro/findKrBoxCluster.C +++ b/Detectors/TPC/reconstruction/macro/findKrBoxCluster.C @@ -12,6 +12,7 @@ /// \brief This macro retrieves clusters from Krypton and X-Ray runs, input tpcdigits.root /// \author Philip Hauer <philip.hauer@cern.ch> +#if !defined(__CLING__) || defined(__ROOTCLING__) #include "TCanvas.h" #include "TFile.h" #include "TTree.h" @@ -24,8 +25,9 @@ #include <iostream> #include <tuple> #include <vector> +#endif -void findKrBoxCluster() +void findKrBoxCluster(int lastTimeBin = 1000, int run = -1, int time = -1) { // Read the digits: TFile* file = new TFile("tpcdigits.root"); @@ -34,17 +36,19 @@ void findKrBoxCluster() std::cout << "The Tree has " << nEntries << " Entries." << std::endl; // Initialize File for later writing - TFile* f = new TFile("boxClustersSectors.root", "RECREATE", "Clusters"); - TTree* T = new TTree("T", "Clusters"); + TFile* fOut = new TFile("BoxClusters.root", "RECREATE"); + TTree* tClusters = new TTree("Clusters", "Clusters"); - // Tree will be filled with a vector of clusters - std::vector<o2::tpc::KrCluster> vCluster{}; - T->Branch("cluster", &vCluster); + // Create a Branch for each sector: + std::vector<o2::tpc::KrCluster> clusters; + tClusters->Branch("cls", &clusters); + tClusters->Branch("run", &run); + tClusters->Branch("time", &time); - std::array<std::vector<o2::tpc::Digit>*, 36> DigitizedSignal; - for (int iSec = 0; iSec < DigitizedSignal.size(); ++iSec) { - DigitizedSignal[iSec] = nullptr; - tree->SetBranchAddress(Form("TPCDigit_%d", iSec), &DigitizedSignal[iSec]); + std::array<std::vector<o2::tpc::Digit>*, 36> digitizedSignal; + for (size_t iSec = 0; iSec < digitizedSignal.size(); ++iSec) { + digitizedSignal[iSec] = nullptr; + tree->SetBranchAddress(Form("TPCDigit_%zu", iSec), &digitizedSignal[iSec]); } // Now everything can get processed @@ -54,34 +58,35 @@ void findKrBoxCluster() tree->GetEntry(iEvent); // Each event consists of sectors (atm only two) for (int i = 0; i < 36; i++) { - auto sector = DigitizedSignal[i]; - if (sector->size() != 0) { - // Create ClusterFinder Object on Heap since creation on stack fails - // Probably due to too much memory consumption - o2::tpc::KrBoxClusterFinder* cluster = new o2::tpc::KrBoxClusterFinder(*sector); - std::vector<std::tuple<int, int, int>> localMaxima = cluster->findLocalMaxima(); - // Loop over cluster centers - for (const std::tuple<int, int, int>& coords : localMaxima) { - int padMax = std::get<0>(coords); - int rowMax = std::get<1>(coords); - int timeMax = std::get<2>(coords); - // Build total cluster - o2::tpc::KrCluster tempCluster = cluster->buildCluster(padMax, rowMax, timeMax); - tempCluster.sector = i; - vCluster.emplace_back(tempCluster); + auto sector = digitizedSignal[i]; + if (sector->size() == 0) { + continue; + } + // Create ClusterFinder Object on Heap since creation on stack fails + // Probably due to too much memory consumption + auto clFinder = std::make_unique<o2::tpc::KrBoxClusterFinder>(*sector); + std::vector<std::tuple<int, int, int>> localMaxima = clFinder->findLocalMaxima(); + // Loop over cluster centers + for (const std::tuple<int, int, int>& coords : localMaxima) { + int padMax = std::get<0>(coords); + int rowMax = std::get<1>(coords); + int timeMax = std::get<2>(coords); + + if (timeMax >= lastTimeBin) { + continue; } - // Clean up memory: - delete cluster; - cluster = nullptr; + // Build total cluster + o2::tpc::KrCluster tempCluster = clFinder->buildCluster(padMax, rowMax, timeMax); + tempCluster.sector = i; + clusters.emplace_back(tempCluster); } } // Fill Tree - T->Fill(); - vCluster.clear(); + tClusters->Fill(); + clusters.clear(); } // Write Tree to file - f->cd(); - T->Write(); - f->Close(); + fOut->Write(); + fOut->Close(); return; } diff --git a/Detectors/TPC/reconstruction/macro/readClusters.C b/Detectors/TPC/reconstruction/macro/readClusters.C index a969940fb369f..b92021aba8dc3 100644 --- a/Detectors/TPC/reconstruction/macro/readClusters.C +++ b/Detectors/TPC/reconstruction/macro/readClusters.C @@ -47,7 +47,7 @@ void readClusters(std::string clusterFilename, std::string digitFilename, int se std::vector<o2::tpc::Digit>* digits = nullptr; digitBranch->SetAddress(&digits); - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; + using MCLabelContainer = o2::dataformats::MCLabelContainer; MCLabelContainer* mcClusterTruth = nullptr; clusterTree->SetBranchAddress(Form("TPCClusterHWMCTruth%i", sectorid), &mcClusterTruth); MCLabelContainer* mcDigitTruth = nullptr; diff --git a/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh b/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh index 4ad3028f7ae17..273e6fa4d8923 100755 --- a/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh +++ b/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh @@ -6,6 +6,8 @@ usage() { echo echo "optional arguments:" echo " -i, --inputFile= : set output file name (default: '$inputFile')" + echo " -f, --fistTimeBin= : first time bin for cluster finder" + echo " -l, --lastTimeBin= : last time bin for cluster finder" echo " -o, --outputType= : set output type to be writte (default: '$outputType')" echo " -q, --qMaxThreshold= : set qMax threshold for clusterization (default: '$qMaxThreshold')" echo " -h, --help : show this help message" @@ -24,9 +26,11 @@ usageAndExit() { inputFile=tpcdigits.root outputType=clusters,tracks qMaxThreshold=4 +firstTimeBin=0 +lastTimeBin=500 # ===| parse command line options |============================================= -OPTIONS=$(getopt -l "inputFile:,outputType:,qMaxThreshold:,help" -o "i:o:q:h" -n "runRecoFromDigits.sh" -- "$@") +OPTIONS=$(getopt -l "inputFile:,firstTimeBin:,lastTimeBin:,outputType:,qMaxThreshold:,help" -o "i:f:l:o:q:h" -n "runRecoFromDigits.sh" -- "$@") if [ $? != 0 ] ; then usageAndExit @@ -38,6 +42,8 @@ while true; do case "$1" in --) shift; break;; -i|--inputFile) inputFile=$2; shift 2;; + -f|--firstTimeBin) firstTimeBin=$2; shift 2;; + -l|--lastTimeBin) lastTimeBin=$2; shift 2;; -o|--outputType) outputType=$2; shift 2;; -q|--qMaxThreshold) qMaxThreshold=$2; shift 2;; -h|--help) usageAndExit;; @@ -48,6 +54,6 @@ done # ===| check for required arguments |=========================================== # ===| command building and execution |========================================= -cmd="o2-tpc-reco-workflow -b --infile $inputFile --disable-mc --configKeyValues 'TPCHwClusterer.peakChargeThreshold=$qMaxThreshold;TPCHwClusterer.isContinuousReadout=0' --output-type $outputType" +cmd="o2-tpc-reco-workflow -b --infile $inputFile --disable-mc --configKeyValues 'TPCHwClusterer.peakChargeThreshold=$qMaxThreshold;TPCHwClusterer.isContinuousReadout=0;TPCHwClusterer.firstTimeBin=$firstTimeBin;TPCHwClusterer.lastTimeBin=$lastTimeBin' --output-type $outputType" echo $cmd eval $cmd diff --git a/Detectors/TPC/reconstruction/run/readGBTFrames.cxx b/Detectors/TPC/reconstruction/run/readGBTFrames.cxx index 31356aea130e8..96cac97f2adff 100644 --- a/Detectors/TPC/reconstruction/run/readGBTFrames.cxx +++ b/Detectors/TPC/reconstruction/run/readGBTFrames.cxx @@ -32,8 +32,9 @@ std::mutex mtx; bool isVector1(std::vector<int>& vec) { for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) { - if ((*it) != 1) + if ((*it) != 1) { return false; + } } return true; } @@ -125,18 +126,21 @@ int main(int argc, char* argv[]) std::vector<int> addData_done(infile.size(), 0); std::vector<int> reading_done(infile.size(), 0); for (int i = 0; i < infile.size(); ++i) { - if (size.size() >= infile.size()) + if (size.size() >= infile.size()) { iSize = size[i]; - else + } else { iSize = size[0]; - if (CRU.size() >= infile.size()) + } + if (CRU.size() >= infile.size()) { iCRU = CRU[i]; - else + } else { iCRU = CRU[0]; - if (link.size() >= infile.size()) + } + if (link.size() >= infile.size()) { iLink = link[i]; - else + } else { iLink = link[0]; + } container.push_back(new o2::tpc::GBTFrameContainer(iSize, iCRU, iLink)); container.back()->setEnableAdcClockWarning(checkAdcClock); @@ -149,8 +153,9 @@ int main(int argc, char* argv[]) start = std::chrono::system_clock::now(); for (int i = 0; i < infile.size(); ++i) { - if (infile[i] != "NOFILE") + if (infile[i] != "NOFILE") { threads.emplace_back(addData, std::ref(*container[i]), std::ref(infile[i]), frames, rorcFlavor, std::ref(addData_done[i])); + } } std::vector<std::vector<std::ofstream*>> outfiles(infile.size()); @@ -173,8 +178,9 @@ int main(int argc, char* argv[]) outfiles[i].push_back(out); } - if (j == 2) + if (j == 2) { continue; + } outname = ""; if (i >= outfile.size()) { outfiles[i].push_back(nullptr); diff --git a/Detectors/TPC/reconstruction/run/readRawData.cxx b/Detectors/TPC/reconstruction/run/readRawData.cxx index ac9fd8e1fd55b..137d5d40486c6 100644 --- a/Detectors/TPC/reconstruction/run/readRawData.cxx +++ b/Detectors/TPC/reconstruction/run/readRawData.cxx @@ -65,8 +65,9 @@ int main(int argc, char* argv[]) return EXIT_SUCCESS; } - if (infile[0] == "NOFILE") + if (infile[0] == "NOFILE") { return EXIT_SUCCESS; + } // Initialize logger FairLogger* logger = FairLogger::GetLogger(); @@ -80,12 +81,13 @@ int main(int argc, char* argv[]) for (int i = 0; i < infile.size(); ++i) { readers.emplace_back(); readers[i].addEventSynchronizer(eventSync); - if (region.size() != infile.size() && link.size() == infile.size()) + if (region.size() != infile.size() && link.size() == infile.size()) { readers[i].addInputFile(region[0], link[i], sampaVersion[i], infile[i]); - else if (region.size() == infile.size() && link.size() != infile.size()) + } else if (region.size() == infile.size() && link.size() != infile.size()) { readers[i].addInputFile(region[i], link[0], sampaVersion[i], infile[i]); - else + } else { readers[i].addInputFile(region[i], link[i], sampaVersion[i], infile[i]); + } readers[i].setUseRawInMode3(useRawInMode3); readers[i].setCheckAdcClock(false); } diff --git a/Detectors/TPC/reconstruction/src/AdcClockMonitor.cxx b/Detectors/TPC/reconstruction/src/AdcClockMonitor.cxx index ef33491e5394f..8b0f3be173da4 100644 --- a/Detectors/TPC/reconstruction/src/AdcClockMonitor.cxx +++ b/Detectors/TPC/reconstruction/src/AdcClockMonitor.cxx @@ -82,14 +82,16 @@ int AdcClockMonitor::addSequence(const short seq) mSequencePosition == 0 || mSequencePosition == 1 || mSequencePosition == 2) { - if (seq != 0xF) + if (seq != 0xF) { mState = state::error; + } ++mSequencePosition; break; } if (mSequencePosition == 3) { - if (seq != mTransition0) + if (seq != mTransition0) { mState = state::error; + } ++mSequencePosition; break; } @@ -97,14 +99,16 @@ int AdcClockMonitor::addSequence(const short seq) mSequencePosition == 4 || mSequencePosition == 5 || mSequencePosition == 6) { - if (seq != 0x0) + if (seq != 0x0) { mState = state::error; + } mSequencePosition++; break; } if (mSequencePosition == 7) { - if (seq != mTransition1) + if (seq != mTransition1) { mState = state::error; + } ++mSequencePosition; mSequenceCompleted = true; break; diff --git a/Detectors/TPC/reconstruction/src/CTFCoder.cxx b/Detectors/TPC/reconstruction/src/CTFCoder.cxx index 3de8495c08b10..07a3ce315c8a7 100644 --- a/Detectors/TPC/reconstruction/src/CTFCoder.cxx +++ b/Detectors/TPC/reconstruction/src/CTFCoder.cxx @@ -13,6 +13,7 @@ /// \brief class for entropy encoding/decoding of TPC compressed clusters data #include "TPCReconstruction/CTFCoder.h" +#include <fmt/format.h> using namespace o2::tpc; @@ -86,3 +87,135 @@ void CTFCoder::setCompClusAddresses(CompressedClusters& c, void*& buff) setAlignedPtr(buff, c.nTrackClusters, header.nTracks); } } + +///________________________________ +void CTFCoder::createCoders(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op) +{ + bool mayFail = true; // RS FIXME if the dictionary file is not there, do not produce exception + auto buff = readDictionaryFromFile<CTF>(dictPath, mayFail); + if (!buff.size()) { + if (mayFail) { + return; + } + throw std::runtime_error("Failed to create CTF dictionaty"); + } + const auto* ctf = CTF::get(buff.data()); + mCombineColumns = ctf->getHeader().flags & CTFHeader::CombinedColumns; + LOG(INFO) << "TPC CTF Columns Combining " << (mCombineColumns ? "ON" : "OFF"); + + auto getFreq = [ctf](CTF::Slots slot) -> o2::rans::FrequencyTable { + o2::rans::FrequencyTable ft; + auto bl = ctf->getBlock(slot); + auto md = ctf->getMetadata(slot); + ft.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min, md.max); + return std::move(ft); + }; + auto getProbBits = [ctf](CTF::Slots slot) -> int { + return ctf->getMetadata(slot).probabilityBits; + }; + + CompressedClusters cc; // just to get member types +#define MAKECODER(part, slot) createCoder<std::remove_pointer<decltype(part)>::type>(op, getFreq(slot), getProbBits(slot), int(slot)) + // clang-format off + if (mCombineColumns) { + MAKECODER( (MPTR<CTF::NBitsQTot, CTF::NBitsQMax>()), CTF::BLCqTotA); // merged qTotA and qMaxA + } + else { + MAKECODER(cc.qTotA, CTF::BLCqTotA); + } + MAKECODER(cc.qMaxA, CTF::BLCqMaxA); + MAKECODER(cc.flagsA, CTF::BLCflagsA); + if (mCombineColumns) { + MAKECODER( (MPTR<CTF::NBitsRowDiff, CTF::NBitsSliceLegDiff>()), CTF::BLCrowDiffA); // merged rowDiffA and sliceLegDiffA + } + else { + MAKECODER(cc.rowDiffA, CTF::BLCrowDiffA); + } + MAKECODER(cc.sliceLegDiffA, CTF::BLCsliceLegDiffA); + MAKECODER(cc.padResA, CTF::BLCpadResA); + MAKECODER(cc.timeResA, CTF::BLCtimeResA); + if (mCombineColumns) { + MAKECODER( (MPTR<CTF::NBitsSigmaPad, CTF::NBitsSigmaTime>()), CTF::BLCsigmaPadA); // merged sigmaPadA and sigmaTimeA + } + else { + MAKECODER(cc.sigmaPadA, CTF::BLCsigmaPadA); + } + MAKECODER(cc.sigmaTimeA, CTF::BLCsigmaTimeA); + MAKECODER(cc.qPtA, CTF::BLCqPtA); + MAKECODER(cc.rowA, CTF::BLCrowA); + MAKECODER(cc.sliceA, CTF::BLCsliceA); + MAKECODER(cc.timeA, CTF::BLCtimeA); + MAKECODER(cc.padA, CTF::BLCpadA); + if (mCombineColumns) { + MAKECODER( (MPTR<CTF::NBitsQTot, CTF::NBitsQMax>()), CTF::BLCqTotU); // merged qTotU and qMaxU + } + else { + MAKECODER(cc.qTotU, CTF::BLCqTotU); + } + MAKECODER(cc.qMaxU, CTF::BLCqMaxU); + MAKECODER(cc.flagsU, CTF::BLCflagsU); + MAKECODER(cc.padDiffU, CTF::BLCpadDiffU); + MAKECODER(cc.timeDiffU, CTF::BLCtimeDiffU); + if (mCombineColumns) { + MAKECODER( (MPTR<CTF::NBitsSigmaPad, CTF::NBitsSigmaTime>()), CTF::BLCsigmaPadU); // merged sigmaPadA and sigmaTimeA + } + else { + MAKECODER(cc.sigmaPadU, CTF::BLCsigmaPadU); + } + MAKECODER(cc.sigmaTimeU, CTF::BLCsigmaTimeU); + MAKECODER(cc.nTrackClusters, CTF::BLCnTrackClusters); + MAKECODER(cc.nSliceRowClusters, CTF::BLCnSliceRowClusters); + // clang-format on +} + +/// make sure loaded dictionaries (if any) are consistent with data +void CTFCoder::checkDataDictionaryConsistency(const CTFHeader& h) +{ + if (mCoders[0]) { // if external dictionary is provided (it will set , make sure its columns combining option is the same as + if (mCombineColumns != (h.flags & CTFHeader::CombinedColumns)) { + throw std::runtime_error(fmt::format("Mismatch in columns combining mode, Dictionary:{:s} CTFHeader:{:s}", + mCombineColumns ? "ON" : "OFF", (h.flags & CTFHeader::CombinedColumns) ? "ON" : "OFF")); + } + } else { + setCombineColumns(h.flags & CTFHeader::CombinedColumns); + LOG(INFO) << "CTF with stored dictionaries, columns combining " << (mCombineColumns ? "ON" : "OFF"); + } +} + +///________________________________ +size_t CTFCoder::estimateCompressedSize(const CompressedClusters& ccl) +{ + size_t sz = 0; + // clang-format off + // RS FIXME this is very crude estimate, instead, an empirical values should be used +#define ESTSIZE(slot, ptr, n) mCoders[int(slot)] ? \ + rans::calculateMaxBufferSize(n, reinterpret_cast<const o2::rans::LiteralEncoder64<std::remove_pointer<decltype(ptr)>::type>*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), \ + sizeof(std::remove_pointer<decltype(ptr)>::type)) : n*sizeof(std::remove_pointer<decltype(ptr)>) + sz += ESTSIZE(CTF::BLCqTotA, ccl.qTotA, ccl.nAttachedClusters); + sz += ESTSIZE(CTF::BLCqMaxA, ccl.qMaxA, ccl.nAttachedClusters); + sz += ESTSIZE(CTF::BLCflagsA, ccl.flagsA, ccl.nAttachedClusters); + sz += ESTSIZE(CTF::BLCrowDiffA, ccl.rowDiffA, ccl.nAttachedClustersReduced); + sz += ESTSIZE(CTF::BLCsliceLegDiffA, ccl.sliceLegDiffA, ccl.nAttachedClustersReduced); + sz += ESTSIZE(CTF::BLCpadResA, ccl.padResA, ccl.nAttachedClustersReduced); + sz += ESTSIZE(CTF::BLCtimeResA, ccl.timeResA, ccl.nAttachedClustersReduced); + sz += ESTSIZE(CTF::BLCsigmaPadA, ccl.sigmaPadA, ccl.nAttachedClusters); + sz += ESTSIZE(CTF::BLCsigmaTimeA, ccl.sigmaTimeA, ccl.nAttachedClusters); + sz += ESTSIZE(CTF::BLCqPtA, ccl.qPtA, ccl.nTracks); + sz += ESTSIZE(CTF::BLCrowA, ccl.rowA, ccl.nTracks); + sz += ESTSIZE(CTF::BLCsliceA, ccl.sliceA, ccl.nTracks); + sz += ESTSIZE(CTF::BLCtimeA, ccl.timeA, ccl.nTracks); + sz += ESTSIZE(CTF::BLCpadA, ccl.padA, ccl.nTracks); + sz += ESTSIZE(CTF::BLCqTotU, ccl.qTotU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCqMaxU, ccl.qMaxU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCflagsU, ccl.flagsU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCpadDiffU, ccl.padDiffU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCtimeDiffU, ccl.timeDiffU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCsigmaPadU, ccl.sigmaPadU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCsigmaTimeU, ccl.sigmaTimeU, ccl.nUnattachedClusters); + sz += ESTSIZE(CTF::BLCnTrackClusters, ccl.nTrackClusters, ccl.nTracks); + sz += ESTSIZE(CTF::BLCnSliceRowClusters, ccl.nSliceRowClusters, ccl.nSliceRows); + // clang-format on + + LOG(INFO) << "Estimated output size is " << sz << " bytes"; + return sz; +} diff --git a/Detectors/TPC/reconstruction/src/ClustererTask.cxx b/Detectors/TPC/reconstruction/src/ClustererTask.cxx index 634afbc6a1c65..67bbb1b207dbc 100644 --- a/Detectors/TPC/reconstruction/src/ClustererTask.cxx +++ b/Detectors/TPC/reconstruction/src/ClustererTask.cxx @@ -19,6 +19,7 @@ ClassImp(o2::tpc::ClustererTask); using namespace o2::tpc; +using namespace o2::dataformats; //_____________________________________________________________________ ClustererTask::ClustererTask(int sectorid) @@ -62,8 +63,8 @@ InitStatus ClustererTask::Init() } std::stringstream mcsectornamestr; mcsectornamestr << "TPCDigitMCTruth" << mClusterSector; - mDigitMCTruthArray = std::unique_ptr<const MCLabelContainer>( - mgr->InitObjectAs<const MCLabelContainer*>(mcsectornamestr.str().c_str())); + mDigitMCTruthArray = std::unique_ptr<const ConstMCLabelContainerView>( + mgr->InitObjectAs<const ConstMCLabelContainerView*>(mcsectornamestr.str().c_str())); if (!mDigitMCTruthArray) { LOG(ERROR) << "TPC MC Truth not registered in the FairRootManager. Exiting ..."; return kERROR; @@ -97,12 +98,14 @@ void ClustererTask::Exec(Option_t* option) { LOG(DEBUG) << "Running clusterization on event " << mEventCount << " with " << mDigitsArray->size() << " digits."; - if (mHwClustersArray) + if (mHwClustersArray) { mHwClustersArray->clear(); - if (mHwClustersMCTruthArray) + } + if (mHwClustersMCTruthArray) { mHwClustersMCTruthArray->clear(); + } - mHwClusterer->process(gsl::span<o2::tpc::Digit const>(mDigitsArray->data(), mDigitsArray->size()), mDigitMCTruthArray.get()); + mHwClusterer->process(gsl::span<o2::tpc::Digit const>(mDigitsArray->data(), mDigitsArray->size()), *mDigitMCTruthArray.get()); LOG(DEBUG) << "Hw clusterer delivered " << mHwClustersArray->size() << " cluster container"; ++mEventCount; @@ -113,12 +116,14 @@ void ClustererTask::FinishTask() { LOG(DEBUG) << "Finish clusterization"; - if (mHwClustersArray) + if (mHwClustersArray) { mHwClustersArray->clear(); - if (mHwClustersMCTruthArray) + } + if (mHwClustersMCTruthArray) { mHwClustersMCTruthArray->clear(); + } - mHwClusterer->finishProcess(*mDigitsArray.get(), mDigitMCTruthArray.get()); + mHwClusterer->finishProcess(*mDigitsArray.get(), *mDigitMCTruthArray.get()); LOG(DEBUG) << "Hw clusterer delivered " << mHwClustersArray->size() << " cluster container"; ++mEventCount; diff --git a/Detectors/TPC/reconstruction/src/DigitalCurrentClusterIntegrator.cxx b/Detectors/TPC/reconstruction/src/DigitalCurrentClusterIntegrator.cxx index fff40b59221ce..23e53d0817058 100644 --- a/Detectors/TPC/reconstruction/src/DigitalCurrentClusterIntegrator.cxx +++ b/Detectors/TPC/reconstruction/src/DigitalCurrentClusterIntegrator.cxx @@ -14,11 +14,12 @@ #include "TPCReconstruction/DigitalCurrentClusterIntegrator.h" using namespace o2::tpc; +using namespace o2::tpc::constants; void DigitalCurrentClusterIntegrator::clear() { - for (int i = 0; i < Constants::MAXSECTOR; i++) { - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int i = 0; i < MAXSECTOR; i++) { + for (int j = 0; j < MAXGLOBALPADROW; j++) { if (mIntegratedCurrents[i][j]) { int nPads = Mapper::instance().getNumberOfPadsInRowSector(j); memset(&mIntegratedCurrents[i][j][0], 0, nPads * sizeof(mIntegratedCurrents[i][j][0])); @@ -29,8 +30,8 @@ void DigitalCurrentClusterIntegrator::clear() void DigitalCurrentClusterIntegrator::reset() { - for (int i = 0; i < Constants::MAXSECTOR; i++) { - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int i = 0; i < MAXSECTOR; i++) { + for (int j = 0; j < MAXGLOBALPADROW; j++) { mIntegratedCurrents[i][j].reset(nullptr); } } diff --git a/Detectors/TPC/reconstruction/src/GBTFrameContainer.cxx b/Detectors/TPC/reconstruction/src/GBTFrameContainer.cxx index c855834166b36..cce8657deeb46 100644 --- a/Detectors/TPC/reconstruction/src/GBTFrameContainer.cxx +++ b/Detectors/TPC/reconstruction/src/GBTFrameContainer.cxx @@ -185,8 +185,9 @@ void GBTFrameContainer::addGBTFramesFromBinaryFile(std::string fileName, std::st std::cout << std::endl; for (int j = 0; j < 5; ++j) { - if (ids[j] == 0x8) + if (ids[j] == 0x8) { writeValue[j] = true; + } } for (int j = 0; j < 5; ++j) { if ((writeValue[j] & ids[j]) == 0xF) { @@ -232,8 +233,9 @@ void GBTFrameContainer::addGBTFramesFromBinaryFile(std::string fileName, std::st adcValues[0][((ids[0] & 0x7) * 2)] = (((ids[0] >> 3) & 0x1) == 0) ? 0 : ((words[4] & 0xF) << 6) | ((words[5] >> 26) & 0x3F); for (int j = 0; j < 5; ++j) { - if (ids[j] == 0x8) + if (ids[j] == 0x8) { writeValue[j] = true; + } } for (int j = 0; j < 5; ++j) { if ((writeValue[j] & ids[j]) == 0xF) { @@ -304,13 +306,15 @@ void GBTFrameContainer::processFrame(std::vector<GBTFrame>::iterator iFrame) { ++mGBTFramesAnalyzed; - if (mEnableAdcClockWarning) + if (mEnableAdcClockWarning) { checkAdcClock(iFrame); + } searchSyncPattern(iFrame); - if (mEnableCompileAdcValues) + if (mEnableCompileAdcValues) { compileAdcValues(iFrame); + } } void GBTFrameContainer::compileAdcValues(std::vector<GBTFrame>::iterator iFrame) @@ -319,10 +323,12 @@ void GBTFrameContainer::compileAdcValues(std::vector<GBTFrame>::iterator iFrame) short value2; mAdcMutex.lock(); for (short iHalfSampa = 0; iHalfSampa < 5; ++iHalfSampa) { - if (mPositionForHalfSampa[iHalfSampa] == -1) + if (mPositionForHalfSampa[iHalfSampa] == -1) { continue; - if (mPositionForHalfSampa[iHalfSampa + 5] == -1) + } + if (mPositionForHalfSampa[iHalfSampa + 5] == -1) { continue; + } switch (mPositionForHalfSampa[iHalfSampa]) { case 0: @@ -477,8 +483,9 @@ bool GBTFrameContainer::getData(std::vector<Digit>& container) } mAdcMutex.unlock(); - if (!dataAvailable) + if (!dataAvailable) { return dataAvailable; + } const Mapper& mapper = Mapper::instance(); int iTimeBin = mTimebin; @@ -508,8 +515,9 @@ bool GBTFrameContainer::getData(std::vector<Digit>& container) } } - if (dataAvailable) + if (dataAvailable) { ++mTimebin; + } return dataAvailable; } @@ -531,8 +539,9 @@ bool GBTFrameContainer::getData(std::vector<HalfSAMPAData>& container) } mAdcMutex.unlock(); - if (!dataAvailable) + if (!dataAvailable) { return dataAvailable; + } // if (container.size() != 5) { //// LOG(INFO) << "Container had the wrong size, set it to 5"; diff --git a/Detectors/TPC/reconstruction/src/GPUCATracking.cxx b/Detectors/TPC/reconstruction/src/GPUCATracking.cxx index 814d84cb23732..c764e68674db9 100644 --- a/Detectors/TPC/reconstruction/src/GPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/src/GPUCATracking.cxx @@ -16,7 +16,7 @@ #include "FairLogger.h" #include "ReconstructionDataFormats/Track.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "TChain.h" #include "TClonesArray.h" #include "TPCBase/Mapper.h" @@ -26,22 +26,30 @@ #include "TPCBase/ParameterGas.h" #include "TPCBase/Sector.h" #include "DataFormatsTPC/Digit.h" +#include "DataFormatsTPC/ClusterNativeHelper.h" #include "DetectorsRaw/HBFUtils.h" #include "GPUO2Interface.h" #include "GPUO2InterfaceConfiguration.h" #include "GPUTPCGMMergedTrack.h" #include "GPUTPCGMMergedTrackHit.h" +#include "GPUTPCGMMergerTypes.h" #include "GPUHostDataTypes.h" +#include "GPUQAHelper.h" +#include "TPCFastTransform.h" + +#include <atomic> +#include <optional> +#ifdef WITH_OPENMP +#include <omp.h> +#endif using namespace o2::gpu; using namespace o2::tpc; using namespace o2; using namespace o2::dataformats; -using MCLabelContainer = MCTruthContainer<MCCompLabel>; - -GPUCATracking::GPUCATracking() : mTrackingCAO2Interface() {} +GPUCATracking::GPUCATracking() = default; GPUCATracking::~GPUCATracking() { deinitialize(); } int GPUCATracking::initialize(const GPUO2InterfaceConfiguration& config) @@ -61,13 +69,16 @@ void GPUCATracking::deinitialize() int GPUCATracking::runTracking(GPUO2InterfaceIOPtrs* data, GPUInterfaceOutputs* outputs) { - if ((int)(data->tpcZS != nullptr) + (int)(data->o2Digits != nullptr) + (int)(data->clusters != nullptr) + (int)(data->compressedClusters != nullptr) != 1) { - return 0; + if ((int)(data->tpcZS != nullptr) + (int)(data->o2Digits != nullptr && (data->tpcZS == nullptr || data->o2DigitsMC == nullptr)) + (int)(data->clusters != nullptr) + (int)(data->compressedClusters != nullptr) != 1) { + throw std::runtime_error("Invalid input for gpu tracking"); } + constexpr unsigned char flagsReject = GPUTPCGMMergedTrackHit::flagReject | GPUTPCGMMergedTrackHit::flagNotFit; + const unsigned int flagsRequired = mTrackingCAO2Interface->getConfig().configInterface.dropSecondaryLegs ? gputpcgmmergertypes::attachGoodLeg : 0; + std::vector<TrackTPC>* outputTracks = data->outputTracks; std::vector<uint32_t>* outClusRefs = data->outputClusRefs; - MCLabelContainer* outputTracksMCTruth = data->outputTracksMCTruth; + std::vector<o2::MCCompLabel>* outputTracksMCTruth = data->outputTracksMCTruth; if (!outputTracks || !outClusRefs) { LOG(ERROR) << "Output tracks or clusRefs vectors are not initialized"; @@ -77,67 +88,93 @@ int GPUCATracking::runTracking(GPUO2InterfaceIOPtrs* data, GPUInterfaceOutputs* auto& gasParam = ParameterGas::Instance(); auto& elParam = ParameterElectronics::Instance(); float vzbin = (elParam.ZbinWidth * gasParam.DriftV); - float vzbinInv = 1.f / vzbin; Mapper& mapper = Mapper::instance(); - const ClusterNativeAccess* clusters; std::vector<o2::tpc::Digit> gpuDigits[Sector::MAXSECTOR]; + o2::dataformats::MCTruthContainer<o2::MCCompLabel> gpuDigitsMC[Sector::MAXSECTOR]; + ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer gpuDigitsMCConst[Sector::MAXSECTOR]; + GPUTrackingInOutDigits gpuDigitsMap; - GPUTPCDigitsMCInput gpuDigitsMC; + GPUTPCDigitsMCInput gpuDigitsMapMC; GPUTrackingInOutPointers ptrs; - if (data->compressedClusters) { - ptrs.tpcCompressedClusters = data->compressedClusters; - } else if (data->tpcZS) { - ptrs.tpcZS = data->tpcZS; - } else if (data->o2Digits) { - ptrs.clustersNative = nullptr; + ptrs.tpcCompressedClusters = data->compressedClusters; + ptrs.tpcZS = data->tpcZS; + if (data->o2Digits) { const float zsThreshold = mTrackingCAO2Interface->getConfig().configReconstruction.tpcZSthreshold; const int maxContTimeBin = mTrackingCAO2Interface->getConfig().configEvent.continuousMaxTimeBin; for (int i = 0; i < Sector::MAXSECTOR; i++) { const auto& d = (*(data->o2Digits))[i]; - gpuDigits[i].reserve(d.size()); - gpuDigitsMap.tpcDigits[i] = gpuDigits[i].data(); + if (zsThreshold > 0 && data->tpcZS == nullptr) { + gpuDigits[i].reserve(d.size()); + } for (int j = 0; j < d.size(); j++) { if (maxContTimeBin && d[j].getTimeStamp() >= maxContTimeBin) { throw std::runtime_error("Digit time bin exceeds time frame length"); } - if (d[j].getChargeFloat() >= zsThreshold) { - gpuDigits[i].emplace_back(d[j]); + if (zsThreshold > 0 && data->tpcZS == nullptr) { + if (d[j].getChargeFloat() >= zsThreshold) { + if (data->o2DigitsMC) { + for (const auto& element : (*data->o2DigitsMC)[i]->getLabels(j)) { + gpuDigitsMC[i].addElement(gpuDigits[i].size(), element); + } + } + gpuDigits[i].emplace_back(d[j]); + } + } + } + if (zsThreshold > 0 && data->tpcZS == nullptr) { + gpuDigitsMap.tpcDigits[i] = gpuDigits[i].data(); + gpuDigitsMap.nTPCDigits[i] = gpuDigits[i].size(); + if (data->o2DigitsMC) { + gpuDigitsMC[i].flatten_to(gpuDigitsMCConst[i].first); + gpuDigitsMCConst[i].second = gpuDigitsMCConst[i].first; + gpuDigitsMapMC.v[i] = &gpuDigitsMCConst[i].second; + } + } else { + gpuDigitsMap.tpcDigits[i] = (*(data->o2Digits))[i].data(); + gpuDigitsMap.nTPCDigits[i] = (*(data->o2Digits))[i].size(); + if (data->o2DigitsMC) { + gpuDigitsMapMC.v[i] = (*data->o2DigitsMC)[i]; } } - gpuDigitsMap.nTPCDigits[i] = gpuDigits[i].size(); } if (data->o2DigitsMC) { - for (int i = 0; i < Sector::MAXSECTOR; i++) { - gpuDigitsMC.v[i] = (*data->o2DigitsMC)[i].get(); - } - gpuDigitsMap.tpcDigitsMC = &gpuDigitsMC; + gpuDigitsMap.tpcDigitsMC = &gpuDigitsMapMC; } ptrs.tpcPackedDigits = &gpuDigitsMap; - } else { - clusters = data->clusters; - ptrs.clustersNative = clusters; - ptrs.tpcPackedDigits = nullptr; } + ptrs.clustersNative = data->clusters; int retVal = mTrackingCAO2Interface->RunTracking(&ptrs, outputs); if (data->o2Digits || data->tpcZS || data->compressedClusters) { - clusters = ptrs.clustersNative; + data->clusters = ptrs.clustersNative; } - const GPUTPCGMMergedTrack* tracks = ptrs.mergedTracks; - int nTracks = ptrs.nMergedTracks; - const GPUTPCGMMergedTrackHit* trackClusters = ptrs.mergedTrackHits; + data->compressedClusters = ptrs.tpcCompressedClusters; - if (retVal) { + if (retVal || mTrackingCAO2Interface->getConfig().configInterface.dumpEvents >= 2) { return retVal; } + const GPUTPCGMMergedTrack* tracks = ptrs.mergedTracks; + int nTracks = ptrs.nMergedTracks; + const GPUTPCGMMergedTrackHit* trackClusters = ptrs.mergedTrackHits; + std::vector<std::pair<int, float>> trackSort(nTracks); int tmp = 0, tmp2 = 0; uint32_t clBuff = 0; for (char cside = 0; cside < 2; cside++) { for (int i = 0; i < nTracks; i++) { if (tracks[i].OK() && tracks[i].CSide() == cside) { + bool hasCl = false; + for (int j = 0; j < tracks[i].NClusters(); j++) { + if (!((trackClusters[tracks[i].FirstClusterRef() + j].state & flagsReject) || (ptrs.mergedTrackHitAttachment[trackClusters[tracks[i].FirstClusterRef() + j].num] & flagsRequired) != flagsRequired)) { + hasCl = true; + break; + } + } + if (!hasCl) { + continue; + } trackSort[tmp++] = {i, tracks[i].GetParam().GetTZOffset()}; auto ncl = tracks[i].NClusters(); clBuff += ncl + (ncl + 1) / 2; // actual N clusters to store will be less @@ -146,69 +183,33 @@ int GPUCATracking::runTracking(GPUO2InterfaceIOPtrs* data, GPUInterfaceOutputs* std::sort(trackSort.data() + tmp2, trackSort.data() + tmp, [](const auto& a, const auto& b) { return (a.second > b.second); }); tmp2 = tmp; - if (cside == 0) - mNTracksASide = tmp; } nTracks = tmp; - outputTracks->resize(nTracks); outClusRefs->resize(clBuff); - clBuff = 0; + std::atomic_int clusterOffsetCounter; + clusterOffsetCounter.store(0); + + constexpr float MinDelta = 0.1; + + auto labelAssigner = (outputTracksMCTruth && data->clusters->clustersMCTruth) ? std::make_optional(GPUTPCTrkLbl(data->clusters->clustersMCTruth, sTrackMCMaxFake)) : std::nullopt; + +#ifdef WITH_OPENMP +#pragma omp parallel for if(!outputTracksMCTruth) num_threads(4) +#endif for (int iTmp = 0; iTmp < nTracks; iTmp++) { auto& oTrack = (*outputTracks)[iTmp]; const int i = trackSort[iTmp].first; - float time0 = 0.f, tFwd = 0.f, tBwd = 0.f; - - if (mTrackingCAO2Interface->GetParamContinuous()) { - time0 = tracks[i].GetParam().GetTZOffset(); + labelAssigner->reset(); - if (tracks[i].CCE()) { - bool lastSide = trackClusters[tracks[i].FirstClusterRef()].slice < Sector::MAXSECTOR / 2; - float delta = 0.f; - for (int iCl = 1; iCl < tracks[i].NClusters(); iCl++) { - if (lastSide ^ (trackClusters[tracks[i].FirstClusterRef() + iCl].slice < Sector::MAXSECTOR / 2)) { - auto& cacl1 = trackClusters[tracks[i].FirstClusterRef() + iCl]; - auto& cacl2 = trackClusters[tracks[i].FirstClusterRef() + iCl - 1]; - auto& cl1 = clusters->clustersLinear[cacl1.num]; - auto& cl2 = clusters->clustersLinear[cacl2.num]; - delta = fabs(cl1.getTime() - cl2.getTime()) * 0.5f; - break; - } - } - tFwd = tBwd = delta; - } else { - // estimate max/min time increments which still keep track in the physical limits of the TPC - auto& c1 = trackClusters[tracks[i].FirstClusterRef()]; - auto& c2 = trackClusters[tracks[i].FirstClusterRef() + tracks[i].NClusters() - 1]; - float t1 = clusters->clustersLinear[c1.num].getTime(); - float t2 = clusters->clustersLinear[c2.num].getTime(); - auto times = std::minmax(t1, t2); - tFwd = times.first - time0; - tBwd = time0 - (times.second - detParam.TPClength * vzbinInv); - } - } - - oTrack = - TrackTPC(tracks[i].GetParam().GetX(), tracks[i].GetAlpha(), - {tracks[i].GetParam().GetY(), tracks[i].GetParam().GetZ(), tracks[i].GetParam().GetSinPhi(), - tracks[i].GetParam().GetDzDs(), tracks[i].GetParam().GetQPt()}, - {tracks[i].GetParam().GetCov(0), tracks[i].GetParam().GetCov(1), tracks[i].GetParam().GetCov(2), + oTrack.set(tracks[i].GetParam().GetX(), tracks[i].GetAlpha(), + {tracks[i].GetParam().GetY(), tracks[i].GetParam().GetZ(), tracks[i].GetParam().GetSinPhi(), tracks[i].GetParam().GetDzDs(), tracks[i].GetParam().GetQPt()}, + {tracks[i].GetParam().GetCov(0), + tracks[i].GetParam().GetCov(1), tracks[i].GetParam().GetCov(2), tracks[i].GetParam().GetCov(3), tracks[i].GetParam().GetCov(4), tracks[i].GetParam().GetCov(5), - tracks[i].GetParam().GetCov(6), tracks[i].GetParam().GetCov(7), tracks[i].GetParam().GetCov(8), - tracks[i].GetParam().GetCov(9), tracks[i].GetParam().GetCov(10), tracks[i].GetParam().GetCov(11), - tracks[i].GetParam().GetCov(12), tracks[i].GetParam().GetCov(13), tracks[i].GetParam().GetCov(14)}); - oTrack.setTime0(time0); - oTrack.setDeltaTBwd(tBwd); - oTrack.setDeltaTFwd(tFwd); - if (tracks[i].CCE()) { - oTrack.setHasCSideClusters(); - oTrack.setHasASideClusters(); - } else if (tracks[i].CSide()) { - oTrack.setHasCSideClusters(); - } else { - oTrack.setHasASideClusters(); - } + tracks[i].GetParam().GetCov(6), tracks[i].GetParam().GetCov(7), tracks[i].GetParam().GetCov(8), tracks[i].GetParam().GetCov(9), + tracks[i].GetParam().GetCov(10), tracks[i].GetParam().GetCov(11), tracks[i].GetParam().GetCov(12), tracks[i].GetParam().GetCov(13), tracks[i].GetParam().GetCov(14)}); oTrack.setChi2(tracks[i].GetParam().GetChi2()); auto& outerPar = tracks[i].OuterParam(); @@ -221,74 +222,96 @@ int GPUCATracking::runTracking(GPUO2InterfaceIOPtrs* data, GPUInterfaceOutputs* outerPar.C[12], outerPar.C[13], outerPar.C[14]})); int nOutCl = 0; for (int j = 0; j < tracks[i].NClusters(); j++) { - if (!(trackClusters[tracks[i].FirstClusterRef() + j].state & GPUTPCGMMergedTrackHit::flagReject)) { - nOutCl++; + if ((trackClusters[tracks[i].FirstClusterRef() + j].state & flagsReject) || (ptrs.mergedTrackHitAttachment[trackClusters[tracks[i].FirstClusterRef() + j].num] & flagsRequired) != flagsRequired) { + continue; } + nOutCl++; } + clBuff = clusterOffsetCounter.fetch_add(nOutCl + (nOutCl + 1) / 2); oTrack.setClusterRef(clBuff, nOutCl); // register the references uint32_t* clIndArr = &(*outClusRefs)[clBuff]; // cluster indices start here uint8_t* sectorIndexArr = reinterpret_cast<uint8_t*>(clIndArr + nOutCl); uint8_t* rowIndexArr = sectorIndexArr + nOutCl; - clBuff += nOutCl + (nOutCl + 1) / 2; - std::vector<std::pair<MCCompLabel, unsigned int>> labels; - nOutCl = 0; + int nOutCl2 = 0; + float t1, t2; + int sector1, sector2; for (int j = 0; j < tracks[i].NClusters(); j++) { - if (trackClusters[tracks[i].FirstClusterRef() + j].state & GPUTPCGMMergedTrackHit::flagReject) { + if ((trackClusters[tracks[i].FirstClusterRef() + j].state & flagsReject) || (ptrs.mergedTrackHitAttachment[trackClusters[tracks[i].FirstClusterRef() + j].num] & flagsRequired) != flagsRequired) { continue; } int clusterIdGlobal = trackClusters[tracks[i].FirstClusterRef() + j].num; Sector sector = trackClusters[tracks[i].FirstClusterRef() + j].slice; int globalRow = trackClusters[tracks[i].FirstClusterRef() + j].row; - int clusterIdInRow = clusterIdGlobal - clusters->clusterOffset[sector][globalRow]; + int clusterIdInRow = clusterIdGlobal - data->clusters->clusterOffset[sector][globalRow]; int regionNumber = 0; while (globalRow > mapper.getGlobalRowOffsetRegion(regionNumber) + mapper.getNumberOfRowsRegion(regionNumber)) { regionNumber++; } - clIndArr[nOutCl] = clusterIdInRow; - sectorIndexArr[nOutCl] = sector; - rowIndexArr[nOutCl] = globalRow; - nOutCl++; - if (outputTracksMCTruth && clusters->clustersMCTruth) { - for (const auto& element : clusters->clustersMCTruth->getLabels(clusterIdGlobal)) { - bool found = false; - for (int l = 0; l < labels.size(); l++) { - if (labels[l].first == element) { - labels[l].second++; - found = true; - break; + clIndArr[nOutCl2] = clusterIdInRow; + sectorIndexArr[nOutCl2] = sector; + rowIndexArr[nOutCl2] = globalRow; + if (nOutCl2 == 0) { + t1 = data->clusters->clustersLinear[clusterIdGlobal].getTime(); + sector1 = sector; + } + nOutCl2++; + if (nOutCl2 == nOutCl) { + t2 = data->clusters->clustersLinear[clusterIdGlobal].getTime(); + sector2 = sector; + } + if (outputTracksMCTruth && data->clusters->clustersMCTruth) { + labelAssigner->addLabel(clusterIdGlobal); + } + } + + bool cce = tracks[i].CCE() && ((sector1 < Sector::MAXSECTOR / 2) ^ (sector2 < Sector::MAXSECTOR / 2)); + float time0 = 0.f, tFwd = 0.f, tBwd = 0.f; + if (mTrackingCAO2Interface->GetParamContinuous()) { + time0 = tracks[i].GetParam().GetTZOffset(); + + if (cce) { + bool lastSide = trackClusters[tracks[i].FirstClusterRef()].slice < Sector::MAXSECTOR / 2; + float delta = 0.f; + for (int iCl = 1; iCl < tracks[i].NClusters(); iCl++) { + if (lastSide ^ (trackClusters[tracks[i].FirstClusterRef() + iCl].slice < Sector::MAXSECTOR / 2)) { + auto& cacl1 = trackClusters[tracks[i].FirstClusterRef() + iCl]; + auto& cacl2 = trackClusters[tracks[i].FirstClusterRef() + iCl - 1]; + auto& cl1 = data->clusters->clustersLinear[cacl1.num]; + auto& cl2 = data->clusters->clustersLinear[cacl2.num]; + delta = fabs(cl1.getTime() - cl2.getTime()) * 0.5f; + if (delta < MinDelta) { + delta = MinDelta; } + break; } - if (!found) - labels.emplace_back(element, 1); } + tFwd = tBwd = delta; + } else { + // estimate max/min time increments which still keep track in the physical limits of the TPC + auto times = std::minmax(t1, t2); + tFwd = times.first - time0; + tBwd = time0 - times.second + mTrackingCAO2Interface->getConfig().configCalib.fastTransform->getMaxDriftTime(t1 > t2 ? sector1 : sector2); } } + oTrack.setTime0(time0); + oTrack.setDeltaTBwd(tBwd); + oTrack.setDeltaTFwd(tFwd); + if (cce) { + oTrack.setHasCSideClusters(); + oTrack.setHasASideClusters(); + } else if (tracks[i].CSide()) { + oTrack.setHasCSideClusters(); + } else { + oTrack.setHasASideClusters(); + } + if (outputTracksMCTruth) { - if (labels.size() == 0) { - outputTracksMCTruth->addElement(iTmp, MCCompLabel()); //default constructor creates NotSet label - } else { - int bestLabelNum = 0, bestLabelCount = 0; - for (int j = 0; j < labels.size(); j++) { - if (labels[j].second > bestLabelCount) { - bestLabelNum = j; - bestLabelCount = labels[j].second; - } - } - MCCompLabel& bestLabel = labels[bestLabelNum].first; - if (bestLabelCount < (1.f - sTrackMCMaxFake) * nOutCl) { - bestLabel.setFakeFlag(); - } - outputTracksMCTruth->addElement(iTmp, bestLabel); - } + outputTracksMCTruth->emplace_back(labelAssigner->computeLabel()); } - int lastSector = trackClusters[tracks[i].FirstClusterRef() + tracks[i].NClusters() - 1].slice; - } - outClusRefs->resize(clBuff); // remove overhead - if (data->o2Digits || data->tpcZS || data->compressedClusters) { - data->clusters = ptrs.clustersNative; } - data->compressedClusters = ptrs.tpcCompressedClusters; + outClusRefs->resize(clusterOffsetCounter.load()); // remove overhead + mTrackingCAO2Interface->Clear(false); return (retVal); diff --git a/Detectors/TPC/reconstruction/src/HardwareClusterDecoder.cxx b/Detectors/TPC/reconstruction/src/HardwareClusterDecoder.cxx index bd14d2002e827..a11156a2e0d05 100644 --- a/Detectors/TPC/reconstruction/src/HardwareClusterDecoder.cxx +++ b/Detectors/TPC/reconstruction/src/HardwareClusterDecoder.cxx @@ -34,11 +34,12 @@ using namespace o2::dataformats; int HardwareClusterDecoder::decodeClusters(std::vector<std::pair<const ClusterHardwareContainer*, std::size_t>>& inputClusters, HardwareClusterDecoder::OutputAllocator outputAllocator, - const std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>* inMCLabels, + const std::vector<o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>>* inMCLabels, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* outMCLabels) { - if (mIntegrator == nullptr) + if (mIntegrator == nullptr) { mIntegrator.reset(new DigitalCurrentClusterIntegrator); + } // MCLabelContainer does only allow appending new labels, so we need to write to separate // containers per {sector,padrow} and merge at the end; std::vector<o2::dataformats::MCTruthContainer<o2::MCCompLabel>> outMCLabelContainers; @@ -47,10 +48,10 @@ int HardwareClusterDecoder::decodeClusters(std::vector<std::pair<const ClusterHa } ClusterNative* outputClusterBuffer = nullptr; // the number of clusters in a {sector,row} - int nRowClusters[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW] = {0}; + int nRowClusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW] = {0}; // offset of first cluster of {sector,row} in the output buffer - size_t clusterOffsets[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW] = {0}; - int containerRowCluster[Constants::MAXSECTOR][Constants::MAXGLOBALPADROW] = {0}; + size_t clusterOffsets[constants::MAXSECTOR][constants::MAXGLOBALPADROW] = {0}; + int containerRowCluster[constants::MAXSECTOR][constants::MAXGLOBALPADROW] = {0}; Mapper& mapper = Mapper::instance(); int numberOfOutputContainers = 0; for (int loop = 0; loop < 2; loop++) { @@ -95,8 +96,9 @@ int HardwareClusterDecoder::decodeClusters(std::vector<std::pair<const ClusterHa } } else { //Count how many output buffers we need (and how large they are below) - if (nCls == 0) + if (nCls == 0) { numberOfOutputContainers++; + } } nCls++; nTotalClusters++; @@ -105,8 +107,8 @@ int HardwareClusterDecoder::decodeClusters(std::vector<std::pair<const ClusterHa } if (loop == 1) { //We are done with filling the buffers, sort all output buffers - for (int i = 0; i < Constants::MAXSECTOR; i++) { - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int i = 0; i < constants::MAXSECTOR; i++) { + for (int j = 0; j < constants::MAXGLOBALPADROW; j++) { if (nRowClusters[i][j] == 0) { continue; } @@ -129,8 +131,8 @@ int HardwareClusterDecoder::decodeClusters(std::vector<std::pair<const ClusterHa outputClusterBuffer = reinterpret_cast<ClusterNative*>(rawOutputBuffer + sizeof(ClusterCountIndex)); nTotalClusters = 0; numberOfOutputContainers = 0; - for (int i = 0; i < Constants::MAXSECTOR; i++) { - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int i = 0; i < constants::MAXSECTOR; i++) { + for (int j = 0; j < constants::MAXGLOBALPADROW; j++) { clusterCounts.nClusters[i][j] = nRowClusters[i][j]; if (nRowClusters[i][j] == 0) { continue; @@ -149,8 +151,8 @@ int HardwareClusterDecoder::decodeClusters(std::vector<std::pair<const ClusterHa if (outMCLabels) { auto& labels = *outMCLabels; int nCls = 0; - for (int i = 0; i < Constants::MAXSECTOR; i++) { - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int i = 0; i < constants::MAXSECTOR; i++) { + for (int j = 0; j < constants::MAXGLOBALPADROW; j++) { if (nRowClusters[i][j] == 0) { continue; } @@ -176,7 +178,7 @@ void HardwareClusterDecoder::sortClustersAndMC(ClusterNative* clusters, size_t n return clusters[a] < clusters[b]; }); std::vector<ClusterNative> buffer(clusters, clusters + nClusters); - MCLabelContainer tmpMC = std::move(mcTruth); + ClusterNativeHelper::MCLabelContainer tmpMC = std::move(mcTruth); assert(mcTruth.getIndexedSize() == 0); for (int i = 0; i < nClusters; i++) { clusters[i] = buffer[indizes[i]]; diff --git a/Detectors/TPC/reconstruction/src/HwClusterer.cxx b/Detectors/TPC/reconstruction/src/HwClusterer.cxx index 7563493069368..f7016bf40ea45 100644 --- a/Detectors/TPC/reconstruction/src/HwClusterer.cxx +++ b/Detectors/TPC/reconstruction/src/HwClusterer.cxx @@ -35,7 +35,9 @@ HwClusterer::HwClusterer( mCurrentMcContainerInBuffer(0), mSplittingMode(0), mClusterSector(sectorid), - mLastTimebin(-1), + mPreviousTimebin(-1), + mFirstTimebin(0), + mLastTimebin(200000), mLastHB(0), mPeakChargeThreshold(2), mContributionChargeThreshold(0), @@ -117,6 +119,8 @@ void HwClusterer::init() mPeakChargeThreshold = param.peakChargeThreshold; mContributionChargeThreshold = param.contributionChargeThreshold; + mFirstTimebin = param.firstTimeBin; + mLastTimebin = param.lastTimeBin; mSplittingMode = param.splittingMode; mIsContinuousReadout = param.isContinuousReadout; mRejectSinglePadClusters = param.rejectSinglePadClusters; @@ -125,14 +129,16 @@ void HwClusterer::init() } //______________________________________________________________________________ -void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth, bool clearContainerFirst) +void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth, bool clearContainerFirst) { if (clearContainerFirst) { - if (mClusterArray) + if (mClusterArray) { mClusterArray->clear(); + } - if (mClusterMcLabelArray) + if (mClusterMcLabelArray) { mClusterMcLabelArray->clear(); + } mClusterCounter = 0; } @@ -158,14 +164,22 @@ void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, MCLabel * already done. */ - if (digit.getTimeStamp() != mLastTimebin) { + const auto timeBin = digit.getTimeStamp(); + + if (timeBin < mFirstTimebin) { + continue; + } else if (timeBin > mLastTimebin) { + break; + } + + if (timeBin != mPreviousTimebin) { /* * If the timebin changes, it could change by more then just 1 (not every * timebin has digits). Since the tmp storage covers mTimebinsInBuffer, * at most mTimebinsInBuffer new timebins need to be prepared and checked * for clusters. */ - for (int i = mLastTimebin; (i < digit.getTimeStamp()) && (i - mLastTimebin < mTimebinsInBuffer); ++i) { + for (int i = mPreviousTimebin; (i < timeBin) && (i - mPreviousTimebin < mTimebinsInBuffer); ++i) { /* * If the HB of the cluster which will be found in a few lines, NOT the @@ -190,7 +204,7 @@ void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, MCLabel * doens't matter. * * If mTimebinsInBuffer would be 5 and i 5 is the new digit timebin (4 - * would be mLastTimebin), then 0 is the oldest one timebin and will to + * would be mPreviousTimebin), then 0 is the oldest one timebin and will to * be replaced by the new arriving one. The cluster which could be * found, would then range from timebin 0 to 4 and has its center at * timebin 2. Threrefore we are looking in (i - 2) for clusters and @@ -214,38 +228,42 @@ void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, MCLabel // we have to copy the MC truth container because we need the information // maybe only in the next events (we store permanently 5 timebins), where // the original pointer could already point to the next container. - if (mcDigitTruth) { + if (mcDigitTruth.getBuffer().size()) { if (mCurrentMcContainerInBuffer == 0) { - mMCtruth[mapTimeInRange(digit.getTimeStamp())] = std::make_shared<MCLabelContainer const>(*mcDigitTruth); + auto tmp = std::make_shared<MCLabelContainer>(); + tmp->restore_from(mcDigitTruth.getBuffer().data(), mcDigitTruth.getBuffer().size()); + mMCtruth[mapTimeInRange(timeBin)] = tmp; } else { - mMCtruth[mapTimeInRange(digit.getTimeStamp())] = std::shared_ptr<MCLabelContainer const>(mMCtruth[getFirstSetBitOfField()]); + mMCtruth[mapTimeInRange(timeBin)] = std::shared_ptr<MCLabelContainer const>(mMCtruth[getFirstSetBitOfField()]); } - mCurrentMcContainerInBuffer |= (0x1 << (mapTimeInRange(digit.getTimeStamp()))); + mCurrentMcContainerInBuffer |= (0x1 << (mapTimeInRange(timeBin))); } } /* * add current digit to storage */ - index = mapTimeInRange(digit.getTimeStamp()) * mPadsPerRowSet[mGlobalRowToRowSet[digit.getRow()]] + (digit.getPad() + 2); + const auto row = digit.getRow(); + const auto pad = digit.getPad(); + index = mapTimeInRange(timeBin) * mPadsPerRowSet[mGlobalRowToRowSet[row]] + (pad + 2); // offset of digit pad because of 2 empty pads on both sides float charge = digit.getChargeFloat(); // TODO: fill noise here as well if necessary if (mPedestalObject) { - charge -= mPedestalObject->getValue(CRU(digit.getCRU()), digit.getRow(), digit.getPad()); + charge -= mPedestalObject->getValue(CRU(digit.getCRU()), row, pad); } /* * charge could be smaller than 0 due to pedestal subtraction, if so set it to zero * noise thresholds for zero suppression could also be done here ... */ - mDataBuffer[mGlobalRowToRowSet[digit.getRow()]][index][mGlobalRowToVcIndex[digit.getRow()]] = + mDataBuffer[mGlobalRowToRowSet[row]][index][mGlobalRowToVcIndex[row]] = charge < 0 ? 0 : ((charge < float(0x3FFF) / (1 << 4)) ? (charge * (1 << 4)) : 0x3FFF); - mIndexBuffer[mGlobalRowToRowSet[digit.getRow()]][index][mGlobalRowToVcIndex[digit.getRow()]] = digitIndex++; + mIndexBuffer[mGlobalRowToRowSet[row]][index][mGlobalRowToVcIndex[row]] = digitIndex++; - mLastTimebin = digit.getTimeStamp(); + mPreviousTimebin = timeBin; } if (!mIsContinuousReadout) { @@ -258,7 +276,7 @@ void HwClusterer::process(gsl::span<o2::tpc::Digit const> const& digits, MCLabel } //______________________________________________________________________________ -void HwClusterer::finishProcess(gsl::span<o2::tpc::Digit const> const& digits, MCLabelContainer const* mcDigitTruth, bool clearContainerFirst) +void HwClusterer::finishProcess(gsl::span<o2::tpc::Digit const> const& digits, ConstMCLabelContainerView const& mcDigitTruth, bool clearContainerFirst) { // Process the last digits (if there are any) process(digits, mcDigitTruth, clearContainerFirst); @@ -569,10 +587,12 @@ void HwClusterer::hwClusterProcessor(const Vc::uint_m peakMask, unsigned qMaxInd } selectionMask = peakMask; - if (mRejectSinglePadClusters) + if (mRejectSinglePadClusters) { selectionMask &= !(sigmaPad2 == 0); - if (mRejectSingleTimeClusters) + } + if (mRejectSingleTimeClusters) { selectionMask &= !(sigmaTime2 == 0); + } ClusterHardware tmpCluster; for (int i = 0; i < Vc::uint_v::Size; ++i) { @@ -704,8 +724,9 @@ void HwClusterer::writeOutputWithTimeOffset(int timeOffset) { // Check in which regions cluster were found for (unsigned int region = 0; region < 10; ++region) { - if (mTmpClusterArray[region]->size() == 0) + if (mTmpClusterArray[region]->size() == 0) { continue; + } if (mClusterArray) { // Create new container @@ -746,8 +767,9 @@ void HwClusterer::writeOutputWithTimeOffset(int timeOffset) //______________________________________________________________________________ void HwClusterer::findPeaksForTime(int timebin) { - if (timebin < 0) + if (timebin < 0) { return; + } const unsigned timeBinWrapped = mapTimeInRange(timebin); for (unsigned short row = 0; row < mNumRowSets; ++row) { @@ -764,8 +786,9 @@ void HwClusterer::findPeaksForTime(int timebin) //______________________________________________________________________________ void HwClusterer::computeClusterForTime(int timebin) { - if (timebin < 0) + if (timebin < 0) { return; + } const unsigned timeBinWrapped = mapTimeInRange(timebin); if (mRejectLaterTimebin) { @@ -782,8 +805,9 @@ void HwClusterer::computeClusterForTime(int timebin) const auto peakMask = ((mDataBuffer[row][qMaxIndex] >> 27) == 0x1F) & // True if current pad is peak AND (getFpOfADC(mDataBuffer[row][qMaxIndex]) > getFpOfADC(mDataBuffer[row][qMaxPreviousIndex]) | // previous has smaller charge !((mDataBuffer[row][qMaxPreviousIndex] >> 27) == 0x1F)); // or previous one was not a peak - if (peakMask.isEmpty()) + if (peakMask.isEmpty()) { continue; + } hwClusterProcessor(peakMask, qMaxIndex, pad, timebin, row); } @@ -796,8 +820,9 @@ void HwClusterer::computeClusterForTime(int timebin) const unsigned qMaxIndex = padOffset + pad; const auto peakMask = ((mDataBuffer[row][qMaxIndex] >> 27) == 0x1F); - if (peakMask.isEmpty()) + if (peakMask.isEmpty()) { continue; + } hwClusterProcessor(peakMask, qMaxIndex, pad, timebin, row); } @@ -810,7 +835,7 @@ void HwClusterer::finishFrame(bool clear) { unsigned HB; // Search in last remaining timebins for clusters - for (int i = mLastTimebin; i - mLastTimebin < mTimebinsInBuffer; ++i) { + for (int i = mPreviousTimebin; i - mPreviousTimebin < mTimebinsInBuffer; ++i) { HB = i < 3 ? 0 : (i - 3) / 447; // integer division on purpose if (HB != mLastHB) { writeOutputWithTimeOffset(mLastHB * 447); @@ -824,8 +849,9 @@ void HwClusterer::finishFrame(bool clear) writeOutputWithTimeOffset(mLastHB * 447); if (clear) { - for (int i = 0; i < mTimebinsInBuffer; ++i) + for (int i = 0; i < mTimebinsInBuffer; ++i) { clearBuffer(i); + } } } @@ -850,8 +876,9 @@ void HwClusterer::updateCluster( Vc::uint_v& qTot, Vc::int_v& pad, Vc::int_v& time, Vc::int_v& sigmaPad2, Vc::int_v& sigmaTime2, std::vector<std::unique_ptr<std::vector<std::pair<MCCompLabel, unsigned>>>>& mcLabels, const Vc::uint_m splitMask) { - if (selectionMask.isEmpty()) + if (selectionMask.isEmpty()) { return; + } const int mappedTime = mapTimeInRange(centerTime + dt); const int index = mappedTime * mPadsPerRowSet[row] + centerPad + dp; diff --git a/Detectors/TPC/reconstruction/src/KrBoxClusterFinder.cxx b/Detectors/TPC/reconstruction/src/KrBoxClusterFinder.cxx index 8f03e702bbf81..df12de4060693 100644 --- a/Detectors/TPC/reconstruction/src/KrBoxClusterFinder.cxx +++ b/Detectors/TPC/reconstruction/src/KrBoxClusterFinder.cxx @@ -35,7 +35,14 @@ KrBoxClusterFinder::KrBoxClusterFinder(std::vector<o2::tpc::Digit>& eventSector) // Fill digits map for (const auto& digit : eventSector) { - mMapOfAllDigits[digit.getTimeStamp()][digit.getRow()][digit.getPad()] = digit.getChargeFloat(); + const int time = digit.getTimeStamp(); + const int row = digit.getRow(); + const int pad = digit.getPad(); + + const int pads = mMapperInstance.getNumberOfPadsInRowSector(row); + const int corPad = pad - (pads / 2) + (MaxPads / 2); + + mMapOfAllDigits[time][row][corPad] = digit.getChargeFloat(); } } @@ -54,6 +61,10 @@ void KrBoxClusterFinder::updateTempClusterFinal() mTempCluster.sigmaPad = std::sqrt(std::abs(mTempCluster.sigmaPad - mTempCluster.meanPad * mTempCluster.meanPad)); mTempCluster.sigmaRow = std::sqrt(std::abs(mTempCluster.sigmaRow - mTempCluster.meanRow * mTempCluster.meanRow)); mTempCluster.sigmaTime = std::sqrt(std::abs(mTempCluster.sigmaTime - mTempCluster.meanTime * mTempCluster.meanTime)); + + const int pads = mMapperInstance.getNumberOfPadsInRowSector(int(mTempCluster.meanRow)); + + mTempCluster.meanPad = mTempCluster.meanPad + (pads / 2.0) - (MaxPads / 2.0); } // Function to update the temporal cluster. diff --git a/Detectors/TPC/reconstruction/src/RawReader.cxx b/Detectors/TPC/reconstruction/src/RawReader.cxx index 450739357f499..5e0aeb6994314 100644 --- a/Detectors/TPC/reconstruction/src/RawReader.cxx +++ b/Detectors/TPC/reconstruction/src/RawReader.cxx @@ -105,14 +105,18 @@ bool RawReader::addInputFile(std::string infile) bool RawReader::addInputFile(int region, int link, int sampaVersion, std::string path, int run) { - if (mRun == -1) + if (mRun == -1) { mRun = run; - if (mRegion == -1) + } + if (mRegion == -1) { mRegion = region; - if (mLink == -1) + } + if (mLink == -1) { mLink = link; - if (mSampaVersion == -1) + } + if (mSampaVersion == -1) { mSampaVersion = sampaVersion; + } if (run != mRun) { LOG(DEBUG) << "Run of RawReader is " << mRun << " and not " << run; @@ -198,8 +202,9 @@ bool RawReader::loadEvent(int64_t event) auto ev = mEvents.find(event); - if (ev == mEvents.end()) + if (ev == mEvents.end()) { return false; + } mLastEvent = event; for (auto& eventInfo : *(ev->second)) { @@ -207,27 +212,31 @@ bool RawReader::loadEvent(int64_t event) case 1: // RAW GBT frames { LOG(DEBUG) << "Data of readout mode 1 (RAW GBT frames)"; - if (!decodeRawGBTFrames(eventInfo)) + if (!decodeRawGBTFrames(eventInfo)) { return false; + } break; } case 2: // Decoded data { LOG(DEBUG) << "Data of readout mode 2 (decoded data)"; - if (!decodePreprocessedData(eventInfo)) + if (!decodePreprocessedData(eventInfo)) { return false; + } break; } case 3: // both, RAW GBT frames and decoded data { if (mUseRawInMode3) { LOG(DEBUG) << "Data of readout mode 3 (decoding RAW GBT frames)"; - if (!decodeRawGBTFrames(eventInfo)) + if (!decodeRawGBTFrames(eventInfo)) { return false; + } } else { LOG(DEBUG) << "Data of readout mode 3 (using decoded data)"; - if (!decodePreprocessedData(eventInfo)) + if (!decodePreprocessedData(eventInfo)) { return false; + } } break; } @@ -312,8 +321,9 @@ bool RawReader::decodePreprocessedData(EventInfo eventInfo) if (ids[j] == 0x8) { writeValue[j] = true; // header TS is one before first word -> +1 - if (mTimestampOfFirstData[j] == 0) + if (mTimestampOfFirstData[j] == 0) { mTimestampOfFirstData[j] = eventInfo.header.timeStamp() + 1 + i / indexStep; + } } } @@ -398,8 +408,9 @@ bool RawReader::decodeRawGBTFrames(EventInfo eventInfo) LOG(DEBUG) << "Start index for Event " << eventInfo.header.eventCount() << " is " << i_start; GBTFrame frame; - if (eventInfo.header.eventCount() > 0) + if (eventInfo.header.eventCount() > 0) { frame.setData(words[i_start - indexStep], words[i_start - indexStep + 1], words[i_start - indexStep + 2], words[i_start - indexStep + 3]); + } GBTFrame lastFrame; for (long i = i_start; i < nWords; i = i + indexStep) { @@ -408,16 +419,21 @@ bool RawReader::decodeRawGBTFrames(EventInfo eventInfo) lastFrame = frame; frame.setData(words[i], words[i + 1], words[i + 2], words[i + 3]); - if (syncMon[0].addSequence(frame.getHalfWord(0, 0, 0), frame.getHalfWord(0, 1, 0), frame.getHalfWord(0, 2, 0), frame.getHalfWord(0, 3, 0))) + if (syncMon[0].addSequence(frame.getHalfWord(0, 0, 0), frame.getHalfWord(0, 1, 0), frame.getHalfWord(0, 2, 0), frame.getHalfWord(0, 3, 0))) { mSyncPos[0] = syncMon[0].getPosition(); - if (syncMon[1].addSequence(frame.getHalfWord(0, 0, 1), frame.getHalfWord(0, 1, 1), frame.getHalfWord(0, 2, 1), frame.getHalfWord(0, 3, 1))) + } + if (syncMon[1].addSequence(frame.getHalfWord(0, 0, 1), frame.getHalfWord(0, 1, 1), frame.getHalfWord(0, 2, 1), frame.getHalfWord(0, 3, 1))) { mSyncPos[1] = syncMon[1].getPosition(); - if (syncMon[2].addSequence(frame.getHalfWord(1, 0, 0), frame.getHalfWord(1, 1, 0), frame.getHalfWord(1, 2, 0), frame.getHalfWord(1, 3, 0))) + } + if (syncMon[2].addSequence(frame.getHalfWord(1, 0, 0), frame.getHalfWord(1, 1, 0), frame.getHalfWord(1, 2, 0), frame.getHalfWord(1, 3, 0))) { mSyncPos[2] = syncMon[2].getPosition(); - if (syncMon[3].addSequence(frame.getHalfWord(1, 0, 1), frame.getHalfWord(1, 1, 1), frame.getHalfWord(1, 2, 1), frame.getHalfWord(1, 3, 1))) + } + if (syncMon[3].addSequence(frame.getHalfWord(1, 0, 1), frame.getHalfWord(1, 1, 1), frame.getHalfWord(1, 2, 1), frame.getHalfWord(1, 3, 1))) { mSyncPos[3] = syncMon[3].getPosition(); - if (syncMon[4].addSequence(frame.getHalfWord(2, 0), frame.getHalfWord(2, 1), frame.getHalfWord(2, 2), frame.getHalfWord(2, 3))) + } + if (syncMon[4].addSequence(frame.getHalfWord(2, 0), frame.getHalfWord(2, 1), frame.getHalfWord(2, 2), frame.getHalfWord(2, 3))) { mSyncPos[4] = syncMon[4].getPosition(); + } if (mCheckAdcClock) { @@ -425,12 +441,15 @@ bool RawReader::decodeRawGBTFrames(EventInfo eventInfo) bool adcCheckErr1 = adcClockMon[1].addSequence(frame.getAdcClock(1)); bool adcCheckErr2 = adcClockMon[2].addSequence(frame.getAdcClock(2)); - if (mSyncPos[0] >= 0) + if (mSyncPos[0] >= 0) { adcClockFound[0] = adcClockFound[0] | (!adcCheckErr0); - if (mSyncPos[2] >= 0) + } + if (mSyncPos[2] >= 0) { adcClockFound[1] = adcClockFound[1] | (!adcCheckErr1); - if (mSyncPos[4] >= 0) + } + if (mSyncPos[4] >= 0) { adcClockFound[2] = adcClockFound[2] | (!adcCheckErr2); + } if (adcClockFound[0] & adcCheckErr0) { adcClockFound[0] = false; LOG(DEBUG) << "ADC clock error of SAMPA " << ((mRegion % 2) ? 3 : 0) << " in frame [" << i / indexStep << "]"; diff --git a/Detectors/TPC/reconstruction/src/RawReaderCRU.cxx b/Detectors/TPC/reconstruction/src/RawReaderCRU.cxx index e08eb1b868414..cf40997ba1d7b 100644 --- a/Detectors/TPC/reconstruction/src/RawReaderCRU.cxx +++ b/Detectors/TPC/reconstruction/src/RawReaderCRU.cxx @@ -47,16 +47,25 @@ RawReaderCRUEventSync::EventInfo& RawReaderCRUEventSync::createEvent(const RDH& { const auto heartbeatOrbit = RDHUtils::getHeartBeatOrbit(rdh); + // TODO: might be that reversing the loop below has the same effect as using mLastEvent + if (mLastEvent && mLastEvent->hasHearbeatOrbit(heartbeatOrbit)) { + return *mLastEvent; + } + for (auto& ev : mEventInformation) { const auto hbMatch = ev.hasHearbeatOrbit(heartbeatOrbit); if (hbMatch) { + mLastEvent = &ev; return ev; } else if (ev.HeartbeatOrbits.back() == heartbeatOrbit - 1) { ev.HeartbeatOrbits.emplace_back(heartbeatOrbit); + mLastEvent = &ev; return ev; } } - return mEventInformation.emplace_back(heartbeatOrbit); + auto& ev = mEventInformation.emplace_back(heartbeatOrbit); + mLastEvent = &ev; + return ev; } void RawReaderCRUEventSync::analyse() @@ -71,7 +80,7 @@ void RawReaderCRUEventSync::analyse() for (size_t iCRU = 0; iCRU < event.CRUInfoArray.size(); ++iCRU) { const auto& cruInfo = event.CRUInfoArray[iCRU]; if (!cruInfo.isPresent()) { - if (mCRUSeen[iCRU]) { + if (mCRUSeen[iCRU] >= 0) { event.IsComplete = false; break; } @@ -116,6 +125,14 @@ void RawReaderCRUEventSync::streamTo(std::ostream& output) const const std::string bold("\033[1m"); const std::string clear("\033[0m"); + std::cout << "CRU information"; + for (size_t iCRU = 0; iCRU < mCRUSeen.size(); ++iCRU) { + const auto readerNumber = mCRUSeen[iCRU]; + if (readerNumber >= 0) { + std::cout << fmt::format("CRU {:2} found in reader {}\n", iCRU, readerNumber); + } + } + std::cout << "Detailed event information\n"; // event loop for (int i = 0; i < mEventInformation.size(); ++i) { @@ -182,18 +199,22 @@ void RawReaderCRUEventSync::streamTo(std::ostream& output) const //============================================================================== int RawReaderCRU::scanFile() { - if (mFileIsScanned) + if (mFileIsScanned) { return 0; + } // std::vector<PacketDescriptor> mPacketDescriptorMap; //const uint64_t RDH_HEADERWORD0 = 0x1ea04003; //const uint64_t RDH_HEADERWORD0 = 0x00004003; - const uint64_t RDH_HEADERWORD0 = 0x00004000 + RDHUtils::getVersion<o2::header::RAWDataHeader>(); + const uint64_t RDH_HEADERWORD0 = 0x00004000; // + RDHUtils::getVersion<o2::header::RAWDataHeader>(); - std::ifstream file; - file.open(mInputFileName, std::ifstream::binary); - if (!file.good()) - throw std::runtime_error("Unable to open or access file " + mInputFileName); + std::ifstream& file = mFileHandle; + if (!file.is_open()) { + file.open(mInputFileName, std::ifstream::binary); + if (!file.good()) { + throw std::runtime_error("Unable to open or access file " + mInputFileName); + } + } // get length of file in bytes file.seekg(0, file.end); @@ -209,9 +230,10 @@ int RawReaderCRU::scanFile() // read in the RDH, then jump to the next RDH position RDH rdh; uint32_t currentPacket = 0; + uint32_t lastHeartbeatOrbit = 0; - while (currentPacket < numPackets) { - const uint32_t currentPos = file.tellg(); + while ((currentPacket < numPackets) && !file.eof()) { + const size_t currentPos = file.tellg(); // ===| read in the RawDataHeader at the current position |================= file >> rdh; @@ -227,10 +249,18 @@ int RawReaderCRU::scanFile() // triggerType == 0x10 in the firt packet // if (mManager) { - if (mManager->mDataType == DataType::TryToDetect) { + if (mManager->mDetectDataType) { const uint64_t triggerTypeForTriggeredData = 0x10; const uint64_t triggerType = RDHUtils::getTriggerType(rdh); const uint64_t pageCnt = RDHUtils::getPageCounter(rdh); + const uint64_t linkID = RDHUtils::getLinkID(rdh); + + if (pageCnt == 0) { + if (linkID == 15) { + mManager->mRawDataType = RAWDataType::LinkZS; + mManager->mDetectDataType = false; + } + } if (pageCnt == 1) { if (triggerType == triggerTypeForTriggeredData) { @@ -240,12 +270,13 @@ int RawReaderCRU::scanFile() mManager->mDataType = DataType::HBScaling; O2INFO("Detected HB scaling"); } + mManager->mDetectDataType = false; } } } // ===| get relavant data information |===================================== - //const auto heartbeatOrbit = rdh.heartbeatOrbit; + const auto heartbeatOrbit = RDHUtils::getHeartBeatOrbit(rdh); const auto dataWrapperID = RDHUtils::getEndPointID(rdh); const auto linkID = RDHUtils::getLinkID(rdh); const auto globalLinkID = linkID + dataWrapperID * 12; @@ -265,8 +296,12 @@ int RawReaderCRU::scanFile() RawReaderCRUEventSync::LinkInfo* linkInfo = nullptr; if (mManager) { // in case of triggered mode, we use the first heartbeat orbit as event identifier + if ((lastHeartbeatOrbit == 0) || (heartbeatOrbit != lastHeartbeatOrbit)) { + mManager->mEventSync.createEvent(rdh, mManager->getDataType()); + lastHeartbeatOrbit = heartbeatOrbit; + } linkInfo = &mManager->mEventSync.getLinkInfo(rdh, mManager->getDataType()); - mManager->mEventSync.setCRUSeen(mCRU); + mManager->mEventSync.setCRUSeen(mCRU, mReaderNumber); } //std::cout << "block length: " << blockLength << '\n'; @@ -276,7 +311,7 @@ int RawReaderCRU::scanFile() // * create the packet descriptor // * set the mLinkPresent flag // - if ((rdh.word0 & 0x0000FFFF) == RDH_HEADERWORD0) { + if ((rdh.word0 & 0x0000FFF0) == RDH_HEADERWORD0) { // non 0 stop bit means data with payload if (RDHUtils::getStop(rdh) == 0) { mPacketDescriptorMaps[globalLinkID].emplace_back(currentPos, mCRU, linkID, dataWrapperID, memorySize, packetSize); @@ -302,7 +337,7 @@ int RawReaderCRU::scanFile() }; // debug output - if (CHECK_BIT(mDebugLevel, DebugLevel::RDHDump)) { + if (mVerbosity && CHECK_BIT(mDebugLevel, DebugLevel::RDHDump)) { //std::cout << "Packet " << std::setw(5) << currentPacket << " - Link " << int(linkID) << "\n"; //std::cout << rdh; printHorizontal(rdh); @@ -362,10 +397,13 @@ int RawReaderCRU::scanFile() void RawReaderCRU::findSyncPositions() { - std::ifstream file; - file.open(mInputFileName, std::ifstream::binary); - if (!file.good()) - throw std::runtime_error("Unable to open or access file " + mInputFileName); + std::ifstream& file = mFileHandle; + if (!file.is_open()) { + file.open(mInputFileName, std::ifstream::binary); + if (!file.good()) { + throw std::runtime_error("Unable to open or access file " + mInputFileName); + } + } // loop over the MaxNumberOfLinks potential links in the data // only if data from the link is present and selected @@ -405,7 +443,7 @@ void RawReaderCRU::findSyncPositions() // TODO: In future there might be more then one sync in the stream // this should be takein into account if (syncFoundForLink(link)) { - if (CHECK_BIT(mDebugLevel, DebugLevel::SyncPositions)) { + if (mVerbosity && CHECK_BIT(mDebugLevel, DebugLevel::SyncPositions)) { std::cout << "Sync positions for link " << link << '\n'; const auto& syncs = mSyncPositions[link]; for (int i = 0; i < syncs.size(); ++i) { @@ -422,10 +460,13 @@ void RawReaderCRU::findSyncPositions() int RawReaderCRU::processPacket(GBTFrame& gFrame, uint32_t startPos, uint32_t size, ADCRawData& rawData) { // open the data file - std::ifstream file; - file.open(mInputFileName, std::ifstream::binary); - if (!file.good()) - throw std::runtime_error("Unable to open or access file " + mInputFileName); + std::ifstream& file = mFileHandle; + if (!file.is_open()) { + file.open(mInputFileName, std::ifstream::binary); + if (!file.good()) { + throw std::runtime_error("Unable to open or access file " + mInputFileName); + } + } // jump to the start position of the packet file.seekg(startPos, file.beg); @@ -440,12 +481,12 @@ int RawReaderCRU::processPacket(GBTFrame& gFrame, uint32_t startPos, uint32_t si gFrame.getFrameHalfWords(); // debug output - if (CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { + if (mVerbosity && CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { std::cout << gFrame; } gFrame.getAdcValues(rawData); - gFrame.updateSyncCheck(CHECK_BIT(mDebugLevel, DebugLevel::SyncPositions)); + gFrame.updateSyncCheck(mVerbosity && CHECK_BIT(mDebugLevel, DebugLevel::SyncPositions)); if (!(rawData.getNumTimebins() % 16) && (rawData.getNumTimebins() >= mNumTimeBins * 16)) { return 1; } @@ -457,6 +498,8 @@ int RawReaderCRU::processMemory(const std::vector<o2::byte>& data, ADCRawData& r { GBTFrame gFrame; + const bool dumpSyncPositoins = CHECK_BIT(mDebugLevel, DebugLevel::SyncPositions); + // 16 bytes is the size of a GBT frame for (int iFrame = 0; iFrame < data.size() / 16; ++iFrame) { gFrame.setFrameNumber(iFrame); @@ -473,16 +516,35 @@ int RawReaderCRU::processMemory(const std::vector<o2::byte>& data, ADCRawData& r gFrame.getFrameHalfWords(); // debug output - if (CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { + if (mVerbosity && CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { std::cout << gFrame; } gFrame.getAdcValues(rawData); - gFrame.updateSyncCheck(CHECK_BIT(mDebugLevel, DebugLevel::SyncPositions)); + gFrame.updateSyncCheck(mVerbosity && dumpSyncPositoins); if (!(rawData.getNumTimebins() % 16) && (rawData.getNumTimebins() >= mNumTimeBins * 16)) { - return 1; + break; } }; + + if (mDumpTextFiles && dumpSyncPositoins) { + const auto fileName = mOutputFilePrefix + "/LinkPositions.txt"; + std::ofstream file(fileName, std::ofstream::app); + auto& syncPositions = gFrame.getSyncArray(); + + for (int s = 0; s < 5; ++s) { + auto& syncPos = syncPositions[s]; + if (syncPos.synched()) { + file << mEventNumber << "\t" + << mCRU << "\t" + << mLink << "\t" + << s << "\t" + << syncPos.getPacketNumber() << "\t" + << syncPos.getFrameNumber() << "\t" + << syncPos.getHalfWordPosition() << "\n"; + } + } + } return 0; } @@ -605,13 +667,13 @@ int RawReaderCRU::processDataFile() std::string fileName; for (int s = 0; s < 5; s++) { if (mStream == 0x0 or ((mStream >> s) & 0x1) == 0x1) { - if (gFrame.mSyncFound(s) == false) { + if (gFrame.syncFound(s) == false) { std::cout << "No sync found" << std::endl; } // debug output rawData.setOutputStream(s); rawData.setNumTimebins(mNumTimeBins); - if (CHECK_BIT(mDebugLevel, DebugLevel::ADCValues)) { + if (mVerbosity && CHECK_BIT(mDebugLevel, DebugLevel::ADCValues)) { std::cout << rawData << std::endl; }; // write the data to file @@ -665,10 +727,13 @@ void RawReaderCRU::collectGBTData(std::vector<o2::byte>& data) const auto& mapper = Mapper::instance(); const auto& linkInfoArray = mManager->mEventSync.getLinkInfoArrayForEvent(mEventNumber, mCRU); - std::ifstream file; - file.open(mInputFileName, std::ios::binary); - if (!file.good()) - throw std::runtime_error("Unable to open or access file " + mInputFileName); + std::ifstream& file = mFileHandle; + if (!file.is_open()) { + file.open(mInputFileName, std::ios::binary); + if (!file.good()) { + throw std::runtime_error("Unable to open or access file " + mInputFileName); + } + } size_t presentDataPosition = 0; @@ -743,14 +808,39 @@ void RawReaderCRU::processFile(const std::string_view inputFile, uint32_t timeBi // Instantiate the RawReaderCRU RawReaderCRUManager cruManager; cruManager.setDebugLevel(debugLevel); - RawReaderCRU& rawReaderCRU = cruManager.createReader(inputFile, timeBins, 0, stream, debugLevel, verbosity, outputFilePrefix); + //RawReaderCRU& rawReaderCRU = cruManager.createReader(inputFile, timeBins, 0, stream, debugLevel, verbosity, outputFilePrefix); + cruManager.setupReaders(inputFile, timeBins, debugLevel, verbosity, outputFilePrefix); + for (auto& reader : cruManager.getReaders()) { + reader->mDumpTextFiles = true; + reader->mFillADCdataMap = false; + } cruManager.init(); - rawReaderCRU.mDumpTextFiles = true; - rawReaderCRU.mFillADCdataMap = false; - for (int ievent = 0; ievent < rawReaderCRU.getNumberOfEvents(); ++ievent) { + + if (CHECK_BIT(debugLevel, DebugLevel::SyncPositions)) { + std::string outPrefix = outputFilePrefix.length() ? outputFilePrefix.data() : "./"; + const auto fileName = outPrefix + "/LinkPositions.txt"; + std::ofstream file(fileName, std::ofstream::out); + file << "EventNumber/I" + << ":" + << "CRU" + << ":" + << "Link" + << ":" + << "Stream" + << ":" + << "PacketNumber" + << ":" + << "FrameNumber" + << ":" + << "HalfWordPosition" + << "\n"; + file.close(); + } + + for (int ievent = 0; ievent < cruManager.getNumberOfEvents(); ++ievent) { fmt::print("=============| event {: 5d} |===============\n", ievent); - rawReaderCRU.setEventNumber(ievent); - rawReaderCRU.processLinks(linkMask); + + cruManager.processEvent(ievent); } } @@ -768,10 +858,12 @@ void RawReaderCRU::copyEvents(const std::vector<uint32_t>& eventNumbers, std::st std::ofstream outputFile(outputFileName, std::ios_base::binary | mode); // open the input file - std::ifstream file; - file.open(mInputFileName, std::ifstream::binary); - if (!file.good()) { - throw std::runtime_error("Unable to open or access file " + mInputFileName); + std::ifstream& file = mFileHandle; + if (!file.is_open()) { + file.open(mInputFileName, std::ifstream::binary); + if (!file.good()) { + throw std::runtime_error("Unable to open or access file " + mInputFileName); + } } // data buffer. Maximum size is 8k @@ -796,6 +888,49 @@ void RawReaderCRU::copyEvents(const std::vector<uint32_t>& eventNumbers, std::st } } } + +void RawReaderCRU::writeGBTDataPerLink(std::string_view outputDirectory, int maxEvents) +{ + // open the input file + std::ifstream& file = mFileHandle; + if (!file.is_open()) { + file.open(mInputFileName, std::ifstream::binary); + if (!file.good()) { + throw std::runtime_error("Unable to open or access file " + mInputFileName); + } + } + + // data buffer. Maximum size is 8k + char buffer[8192]; + + // loop over events + for (int eventNumber = 0; eventNumber < getNumberOfEvents(); ++eventNumber) { + if ((maxEvents > -1) && (eventNumber > maxEvents)) { + break; + } + + const auto& linkInfoArray = mManager->mEventSync.getLinkInfoArrayForEvent(eventNumber, mCRU); + + for (int iLink = 0; iLink < MaxNumberOfLinks; ++iLink) { + const auto& linkInfo = linkInfoArray[iLink]; + if (!linkInfo.IsPresent) { + continue; + } + + const int ep = iLink >= 12; + const int link = iLink - (ep)*12; + auto outputFileName = fmt::format("{}/CRU_{:02}_EP_{}_Link_{:02}", outputDirectory.data(), mCRU, ep, link); + std::ofstream outputFile(outputFileName, std::ios_base::binary); + + for (auto packetNumber : linkInfo.PacketPositions) { + const auto& packet = mPacketDescriptorMaps[iLink][packetNumber]; + file.seekg(packet.getPayloadOffset(), file.beg); + file.read(buffer, packet.getPayloadSize()); + outputFile.write(buffer, packet.getPayloadSize()); + } + } + } +} //============================================================================== //===| stream overloads for helper classes |==================================== // @@ -804,8 +939,9 @@ void ADCRawData::streamTo(std::ostream& output) const { const auto numTimeBins = std::min(getNumTimebins(), mNumTimeBins); for (int i = 0; i < numTimeBins * 16; i++) { - if (i % 16 == 0) + if (i % 16 == 0) { output << std::setw(4) << std::to_string(i / 16) << " : "; + } output << std::setw(4) << mADCRaw[(mOutputStream)][i]; output << (((i + 1) % 16 == 0) ? "\n" : " "); }; @@ -988,8 +1124,10 @@ void RawReaderCRUManager::setupReaders(const std::string_view inputFileNames, for (auto file : *arr) { // fix the number of time bins auto& reader = createReader(file->GetName(), numTimeBins); + reader.setReaderNumber(mRawReadersCRU.size() - 1); reader.setVerbosity(verbosity); reader.setDebugLevel(debugLevel); + reader.setOutputFilePrefix(outputFilePrefix); O2INFO("Adding file: %s\n", file->GetName()); } } @@ -1009,3 +1147,51 @@ void RawReaderCRUManager::copyEvents(const std::string_view inputFileNames, cons manager.setupReaders(inputFileNames); manager.copyEvents(eventNumbers, outputDirectory, mode); } + +void RawReaderCRUManager::writeGBTDataPerLink(std::string_view outputDirectory, int maxEvents) +{ + init(); + const auto& cruSeen = mEventSync.getCRUSeen(); + + for (size_t iCRU = 0; iCRU < cruSeen.size(); ++iCRU) { + const auto readerNumber = cruSeen[iCRU]; + if (readerNumber >= 0) { + auto& reader = mRawReadersCRU[readerNumber]; + reader->forceCRU(iCRU); + reader->writeGBTDataPerLink(outputDirectory, maxEvents); + } + } +} + +void RawReaderCRUManager::writeGBTDataPerLink(const std::string_view inputFileNames, std::string_view outputDirectory, int maxEvents) +{ + if (gSystem->AccessPathName(outputDirectory.data())) { + gSystem->mkdir(outputDirectory.data(), kTRUE); + } + + RawReaderCRUManager manager; + manager.setupReaders(inputFileNames); + manager.writeGBTDataPerLink(outputDirectory, maxEvents); +} + +void RawReaderCRUManager::processEvent(uint32_t eventNumber, EndReaderCallback endReader) +{ + const auto& cruSeen = mEventSync.getCRUSeen(); + + for (size_t iCRU = 0; iCRU < cruSeen.size(); ++iCRU) { + const auto readerNumber = cruSeen[iCRU]; + if (readerNumber >= 0) { + auto& reader = mRawReadersCRU[readerNumber]; + if (reader->getFillADCdataMap()) { + LOGF(warning, "Filling of ADC data map not supported in RawReaderCRUManager::processEvent, it is disabled now. use ADCDataCallback"); + reader->setFillADCdataMap(false); + } + reader->setEventNumber(eventNumber); + reader->forceCRU(iCRU); + reader->processLinks(); + if (endReader) { + endReader(); + } + } + } +} diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index 8b7294cbf3198..e8b5979231b8d 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -21,7 +21,7 @@ #include "TPCBase/Sector.h" #include "DataFormatsTPC/Defs.h" #include "TPCFastTransform.h" -#include "SplineHelper2D.h" +#include "Spline2DHelper.h" #include "Riostream.h" #include "FairLogger.h" @@ -65,8 +65,9 @@ void TPCFastTransformHelperO2::init() for (int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { Sector sector = 0; int regionNumber = 0; - while (iRow >= mapper.getGlobalRowOffsetRegion(regionNumber) + mapper.getNumberOfRowsRegion(regionNumber)) + while (iRow >= mapper.getGlobalRowOffsetRegion(regionNumber) + mapper.getNumberOfRowsRegion(regionNumber)) { regionNumber++; + } const PadRegionInfo& region = mapper.getPadRegionInfo(regionNumber); @@ -245,7 +246,7 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, // spline corrections for xyz // Time-of-flight correction: ldrift += dist-to-vtx*tofCorr - const double t0 = elParam.PeakingTime / elParam.ZbinWidth; + const double t0 = elParam.getAverageShapingTime() / elParam.ZbinWidth; const double vdCorrY = 0.; const double ldCorr = 0.; @@ -265,9 +266,9 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, for (int row = 0; row < correction.getGeometry().getNumberOfRows(); row++) { const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(slice, row); float* data = correction.getSplineData(slice, row); - SplineHelper2D<float> helper; + Spline2DHelper<float> helper; helper.setSpline(spline, 3, 3); - auto F = [&](float su, float sv, float dxuv[3]) { + auto F = [&](double su, double sv, double dxuv[3]) { getSpaceChargeCorrection(slice, row, su, sv, dxuv[0], dxuv[1], dxuv[2]); }; helper.approximateFunction(data, 0., 1., 0., 1., F); @@ -283,7 +284,7 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, return 0; } -int TPCFastTransformHelperO2::getSpaceChargeCorrection(int slice, int row, float su, float sv, float& dx, float& du, float& dv) +int TPCFastTransformHelperO2::getSpaceChargeCorrection(int slice, int row, double su, double sv, double& dx, double& du, double& dv) { // get space charge correction in internal TPCFastTransform coordinates su,sv->dx,du,dv diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index adf52848f9357..db914eb39ca69 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -27,6 +27,7 @@ #include "TPCFastTransform.h" #include "TPCdEdxCalibrationSplines.h" #include "GPUO2InterfaceConfiguration.h" +#include "TPCPadGainCalib.h" using namespace o2::gpu; @@ -51,13 +52,13 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) bool continuous = false; //time frame data v.s. triggered events GPUO2InterfaceConfiguration config; - config.configProcessing.deviceType = GPUDataTypes::DeviceType::CPU; - config.configProcessing.forceDeviceType = true; + config.configDeviceBackend.deviceType = GPUDataTypes::DeviceType::CPU; + config.configDeviceBackend.forceDeviceType = true; - config.configDeviceProcessing.nThreads = 4; //4 threads if we run on the CPU, 1 = default, 0 = auto-detect - config.configDeviceProcessing.runQA = false; //Run QA after tracking - config.configDeviceProcessing.eventDisplay = nullptr; //Ptr to event display backend, for running standalone OpenGL event display - //config.configDeviceProcessing.eventDisplay = new GPUDisplayBackendGlfw; + config.configProcessing.ompThreads = 4; //4 threads if we run on the CPU, 1 = default, 0 = auto-detect + config.configProcessing.runQA = false; //Run QA after tracking + config.configProcessing.eventDisplay = nullptr; //Ptr to event display backend, for running standalone OpenGL event display + //config.configProcessing.eventDisplay = new GPUDisplayBackendGlfw; config.configEvent.solenoidBz = solenoidBz; config.configEvent.continuousMaxTimeBin = continuous ? GPUSettings::TPC_MAX_TF_TIME_BIN : 0; //Number of timebins in timeframe if continuous, 0 otherwise @@ -76,11 +77,13 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) config.configCalib.fastTransform = fastTransform.get(); std::unique_ptr<o2::gpu::TPCdEdxCalibrationSplines> dEdxSplines(new TPCdEdxCalibrationSplines); config.configCalib.dEdxSplines = dEdxSplines.get(); + std::unique_ptr<TPCPadGainCalib> gainCalib(new TPCPadGainCalib{}); + config.configCalib.tpcPadGain = gainCalib.get(); tracker.initialize(config); - std::vector<ClusterNativeContainer> cont(Constants::MAXGLOBALPADROW); + std::vector<ClusterNativeContainer> cont(constants::MAXGLOBALPADROW); - for (int i = 0; i < Constants::MAXGLOBALPADROW; i++) { + for (int i = 0; i < constants::MAXGLOBALPADROW; i++) { cont[i].sector = 0; cont[i].globalPadRow = i; cont[i].clusters.resize(1); diff --git a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx index 9c70c60f4557f..ae088c52054b5 100644 --- a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx +++ b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx @@ -180,11 +180,13 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) } } } - if (statN > 0) + if (statN > 0) { statDiff /= statN; + } - if (statNFile > 0) + if (statNFile > 0) { statDiffFile /= statNFile; + } std::cout << "average difference in correction " << statDiff << " cm " << std::endl; BOOST_CHECK_MESSAGE(fabs(statDiff) < 1.e-3, "test of correction map failed, average difference " << statDiff << " cm is too large"); diff --git a/Detectors/TPC/reconstruction/test/testTPCHwClusterer.cxx b/Detectors/TPC/reconstruction/test/testTPCHwClusterer.cxx index d0f7d82b4fc14..d6edfe6507f42 100644 --- a/Detectors/TPC/reconstruction/test/testTPCHwClusterer.cxx +++ b/Detectors/TPC/reconstruction/test/testTPCHwClusterer.cxx @@ -21,13 +21,15 @@ #include "TPCBase/Mapper.h" #include "TPCReconstruction/HwClusterer.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include <vector> #include <memory> #include <iostream> +using MCLabelContainer = o2::dataformats::MCLabelContainer; + namespace o2 { namespace tpc @@ -133,7 +135,6 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test1) { std::cout << "##" << std::endl; std::cout << "## Starting test 1, basic class tests." << std::endl; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; auto clusterArray = std::make_unique<std::vector<ClusterHardwareContainer8kb>>(); auto labelArray = std::make_unique<MCLabelContainer>(); @@ -156,8 +157,10 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test1) auto digits = std::make_unique<const std::vector<Digit>>(digitVec); auto mcDigitTruth = std::make_unique<const MCLabelContainer>(labelContainer); + o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel> flatLabels; + mcDigitTruth->flatten_to(flatLabels); - clusterer.process(*digits.get(), mcDigitTruth.get()); + clusterer.process(*digits.get(), flatLabels); // check if clusters were found BOOST_CHECK_EQUAL(clusterArray->size(), 1); @@ -186,7 +189,6 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test2) { std::cout << "##" << std::endl; std::cout << "## Starting test 2, finding single pad clusters." << std::endl; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; auto clusterArray = std::make_unique<std::vector<o2::tpc::ClusterHardwareContainer8kb>>(); auto labelArray = std::make_unique<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>(); @@ -216,7 +218,8 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test2) std::sort(digits->begin(), digits->end(), sortTime()); // Search clusters - clusterer.process(*digits.get(), nullptr); + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> flatLabelsViewEmpty; + clusterer.process(*digits.get(), flatLabelsViewEmpty); // Check result BOOST_CHECK_EQUAL(clusterArray->size(), 47); @@ -260,7 +263,6 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test3) { std::cout << "##" << std::endl; std::cout << "## Starting test 3, computing cluster properties." << std::endl; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; auto clusterArray = std::make_unique<std::vector<o2::tpc::ClusterHardwareContainer8kb>>(); auto labelArray = std::make_unique<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>(); @@ -314,7 +316,8 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test3) std::sort(digits->begin(), digits->end(), sortTime()); // Search clusters - clusterer.process(*digits.get(), nullptr); + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> flatLabelsViewEmpty; + clusterer.process(*digits.get(), flatLabelsViewEmpty); // Check outcome BOOST_CHECK_EQUAL(clusterArray->size(), 1); @@ -363,7 +366,6 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test4) { std::cout << "##" << std::endl; std::cout << "## Starting test 4, rejecting single pad clusters." << std::endl; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; auto clusterArray = std::make_unique<std::vector<o2::tpc::ClusterHardwareContainer8kb>>(); auto labelArray = std::make_unique<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>(); @@ -416,7 +418,8 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test4) std::cout << "testing without thresholds..." << std::endl; clusterer.setRejectSinglePadClusters(false); clusterer.setRejectSingleTimeClusters(false); - clusterer.process(*digits.get(), nullptr); + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> flatLabelsViewEmpty; + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); auto clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 4); @@ -456,7 +459,7 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test4) std::cout << "testing with pad threshold..." << std::endl; clusterer.setRejectSinglePadClusters(true); clusterer.setRejectSingleTimeClusters(false); - clusterer.process(*digits.get(), nullptr); + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 2); @@ -496,7 +499,7 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test4) std::cout << "testing with time threshold..." << std::endl; clusterer.setRejectSinglePadClusters(false); clusterer.setRejectSingleTimeClusters(true); - clusterer.process(*digits.get(), nullptr); + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 2); @@ -536,7 +539,7 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test4) std::cout << "testing both thresholds..." << std::endl; clusterer.setRejectSinglePadClusters(true); clusterer.setRejectSingleTimeClusters(true); - clusterer.process(*digits.get(), nullptr); + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 1); @@ -582,7 +585,6 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test5) { std::cout << "##" << std::endl; std::cout << "## Starting test 5, rejecting peaks in subsequent." << std::endl; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; auto clusterArray = std::make_unique<std::vector<o2::tpc::ClusterHardwareContainer8kb>>(); auto labelArray = std::make_unique<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>(); @@ -620,7 +622,8 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test5) // Search clusters without rejection, all 4 clusters shoud be found std::cout << "testing without rejection..." << std::endl; clusterer.setRejectLaterTimebin(false); - clusterer.process(*digits.get(), nullptr); + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> flatLabelsViewEmpty; + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); auto clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 4); @@ -632,7 +635,7 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test5) // Search clusters with rejection, cluster with peak charge 13 should be surpressed std::cout << "testing with with rejection..." << std::endl; clusterer.setRejectLaterTimebin(true); - clusterer.process(*digits.get(), nullptr); + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 3); @@ -650,7 +653,6 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test6) { std::cout << "##" << std::endl; std::cout << "## Starting test 6, split charge among nearby clusters." << std::endl; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; auto clusterArray = std::make_unique<std::vector<o2::tpc::ClusterHardwareContainer8kb>>(); auto labelArray = std::make_unique<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>(); @@ -904,7 +906,8 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test6) clustersToCompare.emplace_back(); clustersToCompare.back().setCluster(2, 7 + 100, p_pre_fp(clustersMode0[10]), t_pre_fp(clustersMode0[10]), sigma_p_pre_fp(clustersMode0[10]), sigma_t_pre_fp(clustersMode0[10]), (clustersMode0[10][12] * 2), (std::accumulate(clustersMode0[10].begin(), clustersMode0[10].end(), 0.0) * 16), 0, 0); clusterer.setSplittingMode(0); - clusterer.process(*digits.get(), nullptr); + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> flatLabelsViewEmpty; + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); auto clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 11); @@ -962,7 +965,7 @@ BOOST_AUTO_TEST_CASE(HwClusterer_test6) clustersToCompare.emplace_back(); clustersToCompare.back().setCluster(2, 7 + 100, p_pre_fp(clustersMode1[10]), t_pre_fp(clustersMode1[10]), sigma_p_pre_fp(clustersMode1[10]), sigma_t_pre_fp(clustersMode1[10]), (clustersMode1[10][12] * 2), (std::accumulate(clustersMode1[10].begin(), clustersMode1[10].end(), 0.0) * 16), 0, 0); clusterer.setSplittingMode(1); - clusterer.process(*digits.get(), nullptr); + clusterer.process(*digits.get(), flatLabelsViewEmpty); BOOST_CHECK_EQUAL(clusterArray->size(), 1); clusterContainer = (*clusterArray)[0].getContainer(); BOOST_CHECK_EQUAL(clusterContainer->numberOfClusters, 11); diff --git a/Detectors/TPC/simulation/CMakeLists.txt b/Detectors/TPC/simulation/CMakeLists.txt index 1420c3ce08cce..d4d4af7b46702 100644 --- a/Detectors/TPC/simulation/CMakeLists.txt +++ b/Detectors/TPC/simulation/CMakeLists.txt @@ -21,9 +21,8 @@ o2_add_library(TPCSimulation src/PadResponse.cxx src/Point.cxx src/SAMPAProcessing.cxx - src/SpaceCharge.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::SimulationDataFormat - O2::TPCBase O2::TPCSpaceChargeBase + O2::TPCBase O2::TPCSpaceCharge ROOT::Physics) o2_target_root_dictionary(TPCSimulation @@ -38,8 +37,7 @@ o2_target_root_dictionary(TPCSimulation include/TPCSimulation/GEMAmplification.h include/TPCSimulation/PadResponse.h include/TPCSimulation/Point.h - include/TPCSimulation/SAMPAProcessing.h - include/TPCSimulation/SpaceCharge.h) + include/TPCSimulation/SAMPAProcessing.h) o2_add_executable(digits-to-rawzs COMPONENT_NAME tpc @@ -57,13 +55,6 @@ if(BUILD_TESTING) O2::SimulationDataFormat LABELS tpc) - o2_add_test_root_macro(macro/createResidualDistortionObject.C - PUBLIC_LINK_LIBRARIES O2::TPCSpaceChargeBase - O2::CommonUtils - O2::CommonConstants - O2::TPCSimulation - LABELS tpc) - o2_add_test_root_macro(macro/laserTrackGenerator.C PUBLIC_LINK_LIBRARIES FairRoot::Base O2::DataFormatsTPC diff --git a/Detectors/TPC/simulation/include/TPCSimulation/Detector.h b/Detectors/TPC/simulation/include/TPCSimulation/Detector.h index a18880c9a5ff0..ca94e58779bde 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/Detector.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/Detector.h @@ -114,7 +114,7 @@ class Detector : public o2::base::DetImpl<Detector> /// @param kp* Parameters for the ALICE TPC /// @return Bethe-Bloch value in MIP units template <typename T> - T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5); + static T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5); /// Copied from AliRoot - should go to someplace else /// Function to generate random numbers according to Gamma function diff --git a/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h b/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h index c0a565857eedd..cb6d74c43555a 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h @@ -18,7 +18,7 @@ #include "TPCSimulation/DigitContainer.h" #include "TPCSimulation/PadResponse.h" #include "TPCSimulation/Point.h" -#include "TPCSimulation/SpaceCharge.h" +#include "TPCSpaceCharge/SpaceCharge.h" #include "TPCBase/Mapper.h" @@ -52,6 +52,8 @@ class DigitContainer; class Digitizer { public: + using SC = SpaceCharge<double, 129, 129, 180>; + /// Default constructor Digitizer() = default; @@ -109,20 +111,24 @@ class Digitizer /// \param nZSlices number of grid points in z, must be (2**N)+1 /// \param nPhiBins number of grid points in phi /// \param nRBins number of grid points in r, must be (2**N)+1 - void setUseSCDistortions(SpaceCharge::SCDistortionType distortionType, const TH3* hisInitialSCDensity, int nRBins, int nPhiBins, int nZSlices); + void setUseSCDistortions(SC::SCDistortionType distortionType, const TH3* hisInitialSCDensity); /// Enable the use of space-charge distortions and provide SpaceCharge object as input /// \param spaceCharge unique pointer to spaceCharge object - void setUseSCDistortions(SpaceCharge* spaceCharge); + void setUseSCDistortions(SC* spaceCharge); + + /// Enable the use of space-charge distortions by providing global distortions and global corrections stored in a ROOT file + /// The storage of the values should be done by the methods provided in the SpaceCharge class + /// \param TFile file containing distortions and corrections + void setUseSCDistortions(TFile& finp); private: - DigitContainer mDigitContainer; ///< Container for the Digits - std::unique_ptr<SpaceCharge> mSpaceCharge; ///< Handler of space-charge distortions - Sector mSector = -1; ///< ID of the currently processed sector - float mEventTime = 0.f; ///< Time of the currently processed event + DigitContainer mDigitContainer; ///< Container for the Digits + std::unique_ptr<SC> mSpaceCharge; ///< Handler of space-charge distortions + Sector mSector = -1; ///< ID of the currently processed sector + float mEventTime = 0.f; ///< Time of the currently processed event // FIXME: whats the reason for hving this static? static bool mIsContinuous; ///< Switch for continuous readout bool mUseSCDistortions = false; ///< Flag to switch on the use of space-charge distortions - ClassDefNV(Digitizer, 1); }; } // namespace tpc diff --git a/Detectors/TPC/simulation/include/TPCSimulation/ElectronTransport.h b/Detectors/TPC/simulation/include/TPCSimulation/ElectronTransport.h index 9df17f2db1035..19da546c95264 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/ElectronTransport.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/ElectronTransport.h @@ -87,8 +87,9 @@ inline bool ElectronTransport::isElectronAttachment(float driftTime) { if (mRandomFlat.getNextValue() < mGasParam->AttCoeff * mGasParam->OxygenCont * driftTime) { return true; /// electron is attached and lost - } else + } else { return false; /// not attached + } } inline float ElectronTransport::getDriftTime(float zPos, float signChange) const diff --git a/Detectors/TPC/simulation/include/TPCSimulation/Point.h b/Detectors/TPC/simulation/include/TPCSimulation/Point.h index f7693a8eae19d..baad3a38fede9 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/Point.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/Point.h @@ -27,14 +27,14 @@ class ElementalHit { public: //:: so as to get the right Point3D - ::Point3D<float> mPos; // cartesian position of Hit - float mTime = -1; // time of flight - float mELoss = -2; // energy loss + math_utils::Point3D<float> mPos; // cartesian position of Hit + float mTime = -1; // time of flight + float mELoss = -2; // energy loss float GetX() const { return mPos.X(); } float GetY() const { return mPos.Y(); } float GetZ() const { return mPos.Z(); } - const ::Point3D<float>& getPos() const { return mPos; } + const math_utils::Point3D<float>& getPos() const { return mPos; } float GetEnergyLoss() const { return mELoss; } float GetTime() const { return mTime; } diff --git a/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h b/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h index fc1aaf402695d..1c1bc2c85b0ee 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h @@ -183,8 +183,9 @@ inline float SAMPAProcessing::makeSignal(float ADCcounts, const int sector, cons inline float SAMPAProcessing::getADCSaturation(const float signal) const { const float adcSaturation = mEleParam->ADCsaturation; - if (signal > adcSaturation - 1) + if (signal > adcSaturation - 1) { return adcSaturation - 1; + } return signal; } diff --git a/Detectors/TPC/simulation/include/TPCSimulation/SpaceCharge.h b/Detectors/TPC/simulation/include/TPCSimulation/SpaceCharge.h deleted file mode 100644 index 63dbcef7983db..0000000000000 --- a/Detectors/TPC/simulation/include/TPCSimulation/SpaceCharge.h +++ /dev/null @@ -1,244 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SpaceCharge.h -/// \brief Definition of the handler for the ALICE TPC space-charge distortions calculations -/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch - -/* - * TODO: - * - modifiy TPCSpaceChargeBase classes - * - create light-weight 2D matrix class as matrix containers - * - std::vector<Matrix> instead of TMatrixD** - * - bring to O2 conventions - * - fix constants (more precise values, export into TPCBase/Constants) - * - granularity in r, rphi, z? - * - accumulate and add next slice - * - event based: propagate charge(ievent-1), add charge(ievent) - * - time based: mTime0, mEffectiveTime - * addIons(eventtime, drifttime, r, phi) - * time in us, 50 kHz = <one event / 20 us> - * if (ev.time+dr.time-mTime0 < mLengthTimebin) => add2NextSlice - * if (mLengthTimebin < ev.time+dr.time-mTime0 < mLengthTimebin+100us) add2NextToNextSlice - * - apply updated distortions to ions in NextToNextSlice when space charge is propagated; need to store exact positions (e.g. std::vector<std::vector<float>>)! - * - ion transport along the E field -> Continuity equation - * - Validate results by comparison to single ion transport - * - what about primary ionization? - * - irregular bin sizes in r and rphi - */ - -#ifndef ALICEO2_TPC_SPACECHARGE_H -#define ALICEO2_TPC_SPACECHARGE_H - -#include "AliTPCSpaceCharge3DCalc.h" -#include "DataFormatsTPC/Defs.h" -#include "MathUtils/RandomRing.h" - -class TH3; - -namespace o2 -{ -namespace tpc -{ - -class SpaceCharge -{ - public: - /// Enumerator for setting the space-charge distortion mode - enum class SCDistortionType : int { - SCDistortionsConstant = 0, // space-charge distortions constant over time - SCDistortionsRealistic = 1 // realistic evolution of space-charge distortions over time - }; - - // Constructors - /// Default constructor using a grid size of (129 z bins, 180 phi bins, 129 r bins) - SpaceCharge(); - /// Constructor with grid size specified by user - /// \param nRBins number of grid points in r, must be (2**N)+1 - /// \param nPhiBins number of grid points in phi - /// \param nZSlices number of grid points in z, must be (2**N)+1 - SpaceCharge(int nRBins, int nPhiBins, int nZSlices); - /// Constructor with grid size and interpolation order specified by user - /// \param nRBins number of grid points in r, must be (2**N)+1 - /// \param nPhiBins number of grid points in phi - /// \param nZSlices number of grid points in z, must be (2**N)+1 - /// \param interpolationOrder order used for interpolation of lookup tables - SpaceCharge(int nRBins, int nPhiBins, int nZSlices, int interpolationOrder); - - // Destructor - ~SpaceCharge() = default; - - /// Calculate lookup tables if initial space-charge density is provided - void init(); - - /// Calculate distortion and correction lookup tables using AliTPCSpaceChargeCalc class - /// \return real time for the calculation of the electron lookup tables - float calculateLookupTables(); - /// Update distortion and correction lookup tables by current space-charge density - /// \param eventTime time of current event - /// \return real time for the re-calculation of the electron lookup tables - float updateLookupTables(float eventTime); - - /// Set omega*tau and T1, T2 tensor terms in Langevin-equation solution - /// \param omegaTau omega*tau - /// \param t1 T1 tensor term - /// \param t2 T2 tensor term - void setOmegaTauT1T2(float omegaTau, float t1, float t2); - /// Set an initial space-charge density - /// \param hisSCDensity 3D space-charge density histogram, expected format (phi,r,z) and units C / cm^3 / epsilon0 - void setInitialSpaceChargeDensity(const TH3* hisSCDensity); - /// Add primary ions to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param z z position - /// \param nIons number of ions - void fillPrimaryIons(double r, double phi, double z, int nIons); - /// Add charge to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param z z position - /// \param charge charge in C/cm^3/epsilon0 - void fillPrimaryCharge(double r, double phi, double z, float charge); - /// Add ion backflow to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param side A or C side - /// \param nIons number of ions - void fillIBFIons(double r, double phi, Side side, int nIons); - /// Add ion backflow to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param side A or C side - /// \param charge charge in C/cm^3/epsilon0 - void fillIBFCharge(double r, double phi, Side side, float charge); - /// Get ion drift vector along electric field - /// \param r global radius - /// \param phi global phi position - /// \param z z position - /// \param dr return drift in radial direction - /// \param drphi return drift in azimuthal (rphi) direction - /// \param dz return drift in z direction - void getIonDrift(Side side, double r, double phi, double z, double& dr, double& drphi, double& dz) const; - /// Propagate space-charge density along electric field by one time slice - void propagateSpaceCharge(); - /// Convert space-charge density to distribution of ions, propagate them along the electric field and convert back to space-charge density - void propagateIons(); - - /// Correct electron position using correction lookup tables - /// \param point 3D coordinates of the electron - void correctElectron(GlobalPosition3D& point); - /// Distort electron position using distortion lookup tables - /// \param point 3D coordinates of the electron - void distortElectron(GlobalPosition3D& point) const; - - /// Interpolate the space-charge density from lookup tables in mLookUpTableCalculator - /// \param point Position at which to calculate the space-charge density - /// \return space-charge density at given point in C/cm^3/epsilon0 - double getChargeDensity(Side side, const GlobalPosition3D& point) const; - /// Get the space-charge density stored in the - /// \param iphi phi bin - /// \param ir r bin - /// \param iz z bin - /// \return space-charge density in given bin in C/cm^3/epsilon0 - float getChargeDensity(Side side, int ir, int iphi, int iz) const; - - /// Set the space-charge distortions model - /// \param distortionType distortion type (constant or realistic) - void setSCDistortionType(SCDistortionType distortionType) { mSCDistortionType = distortionType; } - /// Get the space-charge distortions model - SCDistortionType getSCDistortionType() const { return mSCDistortionType; } - - /// Return the ion drift time for one z bin - double getDriftTimeZSlice() const { return mDriftTimeVoxel; } - double getNPhi() const { return mNPhi; } - double getNR() const { return mNR; } - double getNZ() const { return mNZ; } - double getVoxelSizePhi() const { return mVoxelSizePhi; } - double getVoxelSizeR() const { return mVoxelSizeR; } - double getVoxelSizeZ() const { return mVoxelSizeZ; } - std::vector<double> getCoordinatesPhi() const { return mCoordPhi; } - std::vector<double> getCoordinatesR() const { return mCoordR; } - std::vector<double> getCoordinatesZ() const { return mCoordZ; } - - void setUseIrregularLUTs(int useIrrLUTs); - void setUseFastDistIntegration(int useFastInt); - - void setDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC); - - private: - /// Allocate memory for data members - void allocateMemory(); - void setVoxelCoordinates(); - - /// Convert amount of ions into charge density C/cm^3/epsilon0 - /// \param nIons number of ions - /// \return space-charge density (C/cm^3/epsilon0) - float ions2Charge(int rBin, int nIons); - - static constexpr float DvDEoverv0 = 0.0025; //!<! v'(E) / v0 = K / (K*E0) for ions, used in dz calculation - static const float sEzField; //!<! nominal drift field - - static constexpr int MaxNZ = 200; //!<! default number of z slices (1 ms slices) - static constexpr int MaxNPhi = 360; //!<! default number of phi bins - static constexpr float DriftLength = 250.; //!<! drift length of the TPC in (cm) - // ion mobility K = 3.0769231 cm^2/(Vs) in Ne-CO2 90-10 published by A. Deisting - // v_drift = K * E = 3.0769231 cm^2/(Vs) * 400 V/cm = 1230.7692 cm/s - // t_drift = 249.7 cm / v_drift = 203 ms - static constexpr float IonDriftTime = 2.02e5; //!<! drift time of ions for one full drift (us) - - const int mInterpolationOrder; ///< order for interpolation of lookup tables: 2==quadratic, >2==cubic spline - - const int mNZ; ///< number of z slices used in lookup tables - const int mNPhi; ///< number of phi bins used in lookup tables - const int mNR; ///< number of r bins used in lookup tables - const double mVoxelSizeZ; ///< length of one z bin (cm) - const double mDriftTimeVoxel; ///< ion drift time for one z slice (us) - const double mVoxelSizePhi; ///< width of one phi bin (radians) - const double mVoxelSizeR; ///< length of one r bin (cm) - - std::vector<double> mCoordZ; ///< vector with coodinates of the z bins - std::vector<double> mCoordPhi; ///< vector with coodinates of the phi bins - std::vector<double> mCoordR; ///< vector with coodinates of the r bins - - bool mUseInitialSCDensity; ///< Flag for the use of an initial space-charge density at the beginning of the simulation - bool mInitLookUpTables; ///< Flag to indicate if lookup tables have been calculated - float mTimeInit; ///< time of last update of lookup tables - SCDistortionType mSCDistortionType; ///< Type of space-charge distortions - - AliTPCSpaceCharge3DCalc mLookUpTableCalculator; ///< object to calculate and store correction and distortion lookup tables - - /// TODO: What are the coordinates of the bins? They are defined in AliTPCSpaceCharge3DCalc::GetChargeDensity and are different from mCoordZ, mCoordPhi, mCoordR used for local ion drift lookup table! Use consistent convention? Lookup table instead of vector? - std::vector<float> mSpaceChargeDensityA; ///< space-charge density on the A side, stored in C/cm^3/epsilon0, z ordering: z=[0,250], [iphi*mNR*mNZ + ir*mNZ + iz] - std::vector<float> mSpaceChargeDensityC; ///< space-charge density on the C side, stored in C/cm^3/epsilon0, z ordering: z=[0,-250], [iphi*mNR*mNZ + ir*mNZ + iz] - - /// Ion drift vectors after time deltaT = mLengthZSlice / v_driftIon - /// nominal E field only in z direction, distortions in r, phi, z due to space charge - /// d = (dr, drphi, mVoxelSizeZ + dz) - /// TODO: Eliminate the need for these matrices as members, they should be owned by AliTPCLookUpTable3DInterpolatorD. AliTPCLookUpTable3DInterpolatorD needs getters for the matrices and the constructor has to be modified. - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixIonDriftZA; //!<! matrix to store ion drift in z direction along E field on A side in cm - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixIonDriftZC; //!<! matrix to store ion drift in z direction along E field on A side in cm - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixIonDriftRPhiA; //!<! matrix to store ion drift in rphi direction along E field on A side in cm - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixIonDriftRPhiC; //!<! matrix to store ion drift in rphi direction along E field on A side in cm - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixIonDriftRA; //!<! matrix to store ion drift in radial direction along E field on A side in cm - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixIonDriftRC; //!<! matrix to store ion drift in radial direction along E field on C side in cm - /// TODO: these lookup tables should be objects? - std::unique_ptr<AliTPCLookUpTable3DInterpolatorD> mLookUpIonDriftA; ///< lookup table for ion drift along E field on A side in cm - std::unique_ptr<AliTPCLookUpTable3DInterpolatorD> mLookUpIonDriftC; ///< lookup table for ion drift along E field on C side in cm - bool mMemoryAllocated; - - math_utils::RandomRing<> mRandomFlat; //!<! Circular random buffer containing flat random values to convert the charge density to a flat ion distribution inside the voxel - - ClassDefNV(SpaceCharge, 1); -}; - -} // namespace tpc -} // namespace o2 - -#endif // ALICEO2_TPC_SPACECHARGE_H diff --git a/Detectors/TPC/simulation/macro/createResidualDistortionObject.C b/Detectors/TPC/simulation/macro/createResidualDistortionObject.C deleted file mode 100644 index 8660a8dfa25b3..0000000000000 --- a/Detectors/TPC/simulation/macro/createResidualDistortionObject.C +++ /dev/null @@ -1,250 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 createResidualDistortionObject.C -/// \brief This macro creates a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and stores it in a file. -/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch - -#include <cmath> - -#include "TFile.h" -#include "TH3.h" -#include "TMatrixD.h" -#include "TString.h" - -#include "AliTPCSpaceCharge3DCalc.h" -#include "CommonConstants/MathConstants.h" -#include "CommonUtils/TreeStreamRedirector.h" -#include "TPCSimulation/SpaceCharge.h" - -using namespace o2::tpc; - -// function declarations -AliTPCSpaceCharge3DCalc* createSpaceCharge(TH3* hisSCDensity, int bSign, int nR, int nPhi, int nZ); -void fillDistortionLookupMatrices(AliTPCSpaceCharge3DCalc* spaceChargeCalc, AliTPCSpaceCharge3DCalc* spaceChargeCalcAvg, TMatrixD** matrixDistDrA, TMatrixD** matrixDistDrphiA, TMatrixD** matrixDistDzA, TMatrixD** matrixDistDrC, TMatrixD** matrixDistDrphiC, TMatrixD** matrixDistDzC); -void makeDebugTreeResiduals(AliTPCSpaceCharge3DCalc* calcFluc, AliTPCSpaceCharge3DCalc* calcAvg, SpaceCharge* spaceChargeRes); - -/// Create a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and write it to a file. -/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive -/// \param nR number of bins in r -/// \param nPhi number of bins in phi -/// \param nZ number of bins in z -/// \param pathToHistoFile path to a file with the fluctuating and average histograms -/// \param histoFlucName name of the fluctuating histogram -/// \param histoAvgName name of the average histogram -void createResidualDistortionObject(int bSign, int nR = 129, int nPhi = 144, int nZ = 129, const char* pathToHistoFile = "InputSCDensityHistograms_8000events.root", const char* histoFlucName = "inputSCDensity3D_8000_0", const char* histoAvgName = "inputSCDensity3D_8000_avg", bool debug = false) -{ - /* - Usage: - root -l -b -q createResidualDistortionObject.C+\(-1,129,144,129,\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"inputSCDensity3D_8000_avg\",true\) - */ - TFile* fileHistos = TFile::Open(pathToHistoFile); - auto histoFluc = fileHistos->Get<TH3>(histoFlucName); - auto histoAvg = fileHistos->Get<TH3>(histoAvgName); - - TFile* fileOutput = TFile::Open("ResidualDistortions.root", "recreate"); - - // Calculate fluctuating and average distortion and correction lookup tables - AliTPCSpaceCharge3DCalc* scCalcFluc = createSpaceCharge(histoFluc, bSign, nR, nPhi, nZ); - AliTPCSpaceCharge3DCalc* scCalcAvg = createSpaceCharge(histoAvg, bSign, nR, nPhi, nZ); - - // Create matrices and fill them with residual distortions. Create SpaceCharge object, assign residual distortion matrices to it and store it in the output file. - TMatrixD** matrixResDistDrA = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDrphiA = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDzA = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDrC = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDrphiC = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDzC = new TMatrixD*[nPhi]; - for (int iphi = 0; iphi < nPhi; ++iphi) { - matrixResDistDrA[iphi] = new TMatrixD(nR, nZ); - matrixResDistDrphiA[iphi] = new TMatrixD(nR, nZ); - matrixResDistDzA[iphi] = new TMatrixD(nR, nZ); - matrixResDistDrC[iphi] = new TMatrixD(nR, nZ); - matrixResDistDrphiC[iphi] = new TMatrixD(nR, nZ); - matrixResDistDzC[iphi] = new TMatrixD(nR, nZ); - } - fillDistortionLookupMatrices(scCalcFluc, scCalcAvg, matrixResDistDrA, matrixResDistDrphiA, matrixResDistDzA, matrixResDistDrC, matrixResDistDrphiC, matrixResDistDzC); - SpaceCharge spaceChargeRes(nR, nPhi, nZ); - spaceChargeRes.setDistortionLookupTables(matrixResDistDrA, matrixResDistDrphiA, matrixResDistDzA, matrixResDistDrC, matrixResDistDrphiC, matrixResDistDzC); - fileOutput->WriteObject(&spaceChargeRes, "spaceChargeRes"); - - if (debug) { - makeDebugTreeResiduals(scCalcFluc, scCalcAvg, &spaceChargeRes); - } -} - -/// Create AliTPCSpaceCharge3DCalc object from a space-charge density histogram -/// \param hisSCDensity input space-charge density histogram -/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive -/// \param nR number of bins in r -/// \param nPhi number of bins in phi -/// \param nZ number of bins in z -/// \return pointer to AliTPCSpaceCharge3DCalc object -AliTPCSpaceCharge3DCalc* createSpaceCharge(TH3* hisSCDensity, int bSign, int nR, int nPhi, int nZ) -{ - AliTPCSpaceCharge3DCalc* spaceCharge = new AliTPCSpaceCharge3DCalc(nR, nZ, nPhi, 2, 3, 0); - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixChargeA = std::make_unique<std::unique_ptr<TMatrixD>[]>(nPhi); - std::unique_ptr<std::unique_ptr<TMatrixD>[]> mMatrixChargeC = std::make_unique<std::unique_ptr<TMatrixD>[]>(nPhi); - for (int iphi = 0; iphi < nPhi; ++iphi) { - mMatrixChargeA[iphi] = std::make_unique<TMatrixD>(nR, nZ); - mMatrixChargeC[iphi] = std::make_unique<TMatrixD>(nR, nZ); - } - spaceCharge->GetChargeDensity((TMatrixD**)mMatrixChargeA.get(), (TMatrixD**)mMatrixChargeC.get(), hisSCDensity, nR, nZ, nPhi); - spaceCharge->SetInputSpaceChargeA((TMatrixD**)mMatrixChargeA.get()); - spaceCharge->SetInputSpaceChargeC((TMatrixD**)mMatrixChargeC.get()); - float omegaTau = -0.32 * bSign; - spaceCharge->SetOmegaTauT1T2(omegaTau, 1.f, 1.f); - spaceCharge->SetCorrectionType(0); - spaceCharge->SetIntegrationStrategy(0); - spaceCharge->ForceInitSpaceCharge3DPoissonIntegralDz(nR, nZ, nPhi, 300, 1e-8); - return spaceCharge; -} - -/// Store distortions from spaceChargeCalcFluc in the matrices provided. If providing an object with average space-charge distortions spaceChargeCalcAvg, the residual distortions (dist_fluctuation(xyzTrue) + corr_average(xyzDistorted)) will be stored. -/// \param spaceChargeCalcFluc fluctuating distortions object -/// \param spaceChargeCalcAvg average distortions object -/// \param matrixDistDrA matrix to store radial distortions on the A side -/// \param matrixDistDrphiA matrix to store rphi distortions on the A side -/// \param matrixDistDzA matrix to store z distortions on the A side -/// \param matrixDistDrC matrix to store radial distortions on the C side -/// \param matrixDistDrphiC matrix to store rphi distortions on the C side -/// \param matrixDistDzC matrix to store z distortions on the C side -void fillDistortionLookupMatrices(AliTPCSpaceCharge3DCalc* spaceChargeCalcFluc, AliTPCSpaceCharge3DCalc* spaceChargeCalcAvg, TMatrixD** matrixDistDrA, TMatrixD** matrixDistDrphiA, TMatrixD** matrixDistDzA, TMatrixD** matrixDistDrC, TMatrixD** matrixDistDrphiC, TMatrixD** matrixDistDzC) -{ - const int nR = spaceChargeCalcFluc->GetNRRows(); - const int nPhi = spaceChargeCalcFluc->GetNPhiSlices(); - const int nZ = spaceChargeCalcFluc->GetNZColumns(); - const float mVoxelSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nR - 1); - const float mVoxelSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZ - 1); - const float mVoxelSizePhi = o2::constants::math::TwoPI / nPhi; - - for (int iphi = 0; iphi < nPhi; ++iphi) { - float phi = iphi * mVoxelSizePhi; - - for (int ir = 0; ir < nR; ++ir) { - float r = AliTPCPoissonSolver::fgkIFCRadius + ir * mVoxelSizeR; - - for (int iz = 0; iz < nZ; ++iz) { - float absz = iz * mVoxelSizeZ; - - float xCylA[3] = {r, phi, absz}; - float xCylC[3] = {r, phi, -1 * absz}; - int roc = 0; - float distFlucA[3] = {0.f, 0.f, 0.f}; - float distFlucC[3] = {0.f, 0.f, 0.f}; - - // get fluctuating distortions - spaceChargeCalcFluc->GetDistortionCylAC(xCylA, roc, distFlucA); - spaceChargeCalcFluc->GetDistortionCylAC(xCylC, roc + 18, distFlucC); - float distA[3] = {distFlucA[0], distFlucA[1], distFlucA[2]}; - float distC[3] = {distFlucC[0], distFlucC[1], distFlucC[2]}; - - // get average corrections if provided and add them to the fluctuating distortions - if (spaceChargeCalcAvg) { - float xDistCylA[3] = {xCylA[0] + distFlucA[0], xCylA[1] + distFlucA[1] / xCylA[0], xCylA[2] + distFlucA[2]}; - float xDistCylC[3] = {xCylC[0] + distFlucC[0], xCylC[1] + distFlucC[1] / xCylC[0], xCylC[2] + distFlucC[2]}; - float corrAvgA[3] = {0.f, 0.f, 0.f}; - float corrAvgC[3] = {0.f, 0.f, 0.f}; - spaceChargeCalcAvg->GetCorrectionCylAC(xDistCylA, roc, corrAvgA); - spaceChargeCalcAvg->GetCorrectionCylAC(xDistCylC, roc + 18, corrAvgC); - distA[0] += corrAvgA[0]; - distA[1] += (corrAvgA[1] * xCylA[0] / xDistCylA[0]); - distA[2] += corrAvgA[2]; - distC[0] += corrAvgC[0]; - distC[1] += (corrAvgC[1] * xCylC[0] / xDistCylC[0]); - distC[2] += corrAvgC[2]; - } - - // store (residual) distortions in the matrices - (*matrixDistDrA[iphi])(ir, iz) = distA[0]; - (*matrixDistDrphiA[iphi])(ir, iz) = distA[1]; - (*matrixDistDzA[iphi])(ir, iz) = distA[2]; - (*matrixDistDrC[iphi])(ir, iz) = distC[0]; - (*matrixDistDrphiC[iphi])(ir, iz) = distC[1]; - (*matrixDistDzC[iphi])(ir, iz) = -1 * distC[2]; - } - } - } -} - -/// Calculate and stream residual distortions from spaceChargeRes and from calcFluc and calcAvg for comparison. -/// \param calcFluc AliTPCSpaceCharge3DCalc object with fluctuation distortions -/// \param calcAvg AliTPCSpaceCharge3DCalc object with average distortions -/// \param spaceChargeRes SpaceCharge object with residual distortions -void makeDebugTreeResiduals(AliTPCSpaceCharge3DCalc* calcFluc, AliTPCSpaceCharge3DCalc* calcAvg, SpaceCharge* spaceChargeRes) -{ - const int nR = calcFluc->GetNRRows(); - const int nPhi = calcFluc->GetNPhiSlices(); - const int nZ = calcFluc->GetNZColumns(); - const float mVoxelSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nR - 1); - const float mVoxelSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZ - 1); - const float mVoxelSizePhi = o2::constants::math::TwoPI / nPhi; - - o2::utils::TreeStreamRedirector pcstream("debugResidualDistortions.root", "recreate"); - for (int iside = 0; iside < 2; ++iside) { - int roc = iside == 0 ? 0 : 18; - for (int iphi = 0; iphi < nPhi; ++iphi) { - float phi = iphi * mVoxelSizePhi; - for (int ir = 0; ir < nR; ++ir) { - float r = AliTPCPoissonSolver::fgkIFCRadius + ir * mVoxelSizeR; - float x = r * std::cos(phi); - float y = r * std::sin(phi); - for (int iz = 1; iz < nZ; ++iz) { - float z = iside == 0 ? iz * mVoxelSizeZ : -1 * iz * mVoxelSizeZ; - - GlobalPosition3D posDistRes(x, y, z); - spaceChargeRes->distortElectron(posDistRes); - float xyzDistRes[3] = {posDistRes.x(), posDistRes.y(), posDistRes.z()}; - float distRes[3] = {posDistRes.x() - x, posDistRes.y() - y, posDistRes.z() - z}; - - float xyz[3] = {x, y, z}; - float distFluc[3] = {0.f, 0.f, 0.f}; - calcFluc->GetDistortion(xyz, roc, distFluc); - float xyzDist[3] = {xyz[0] + distFluc[0], xyz[1] + distFluc[1], xyz[2] + distFluc[2]}; - float corrAvg[3] = {0.f, 0.f, 0.f}; - calcAvg->GetCorrection(xyzDist, roc, corrAvg); - float xyzDistResTrue[3] = {xyzDist[0] + corrAvg[0], xyzDist[1] + corrAvg[1], xyzDist[2] + corrAvg[2]}; - float distResTrue[3] = {distFluc[0] + corrAvg[0], distFluc[1] + corrAvg[1], distFluc[2] + corrAvg[2]}; - - pcstream << "debug" - << "iside=" << iside - << "iphi=" << iphi - << "ir=" << ir - << "iz=" << iz - // original position - << "phi=" << phi - << "r=" << r - << "x=" << x - << "y=" << y - << "z=" << z - // position of distorted points - << "xRes=" << xyzDistRes[0] - << "yRes=" << xyzDistRes[1] - << "zRes=" << xyzDistRes[2] - // true position of distorted points - << "xResTrue=" << xyzDistResTrue[0] - << "yResTrue=" << xyzDistResTrue[1] - << "zResTrue=" << xyzDistResTrue[2] - // residual distortions - << "distX=" << distRes[0] - << "distY=" << distRes[1] - << "distZ=" << distRes[2] - // true residual distortions - << "distXTrue=" << distResTrue[0] - << "distYTrue=" << distResTrue[1] - << "distZTrue=" << distResTrue[2] - // - << "\n"; - } - } - } - } - pcstream.Close(); -} \ No newline at end of file diff --git a/Detectors/TPC/simulation/run/convertDigitsToRawZS.cxx b/Detectors/TPC/simulation/run/convertDigitsToRawZS.cxx index b8a749b921a8c..fcc06bd746218 100644 --- a/Detectors/TPC/simulation/run/convertDigitsToRawZS.cxx +++ b/Detectors/TPC/simulation/run/convertDigitsToRawZS.cxx @@ -48,12 +48,12 @@ namespace bpo = boost::program_options; using namespace o2::tpc; using namespace o2::gpu; +using namespace o2::dataformats; using o2::MCCompLabel; constexpr static size_t NSectors = o2::tpc::Sector::MAXSECTOR; constexpr static size_t NEndpoints = o2::gpu::GPUTrackingInOutZS::NENDPOINTS; using DigitArray = std::array<gsl::span<const o2::tpc::Digit>, Sector::MAXSECTOR>; -using MCLabelContainer = o2::dataformats::MCTruthContainer<MCCompLabel>; struct ProcessAttributes { std::unique_ptr<unsigned long long int[]> zsoutput; diff --git a/Detectors/TPC/simulation/src/Detector.cxx b/Detectors/TPC/simulation/src/Detector.cxx index cf26551fc756f..7e49887f8ede7 100644 --- a/Detectors/TPC/simulation/src/Detector.cxx +++ b/Detectors/TPC/simulation/src/Detector.cxx @@ -8,6 +8,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "DetectorsBase/MaterialManager.h" #include "TPCSimulation/Detector.h" #include "TPCSimulation/Point.h" #include "TPCBase/ParameterGas.h" @@ -200,8 +201,9 @@ Bool_t Detector::ProcessHits(FairVolume* vol) //<< fMC->Edep() << ", Nelectrons: " //<< numberOfElectrons; - if (numberOfElectrons <= 0) // Could maybe be smaller than 0 due to the Gamma function + if (numberOfElectrons <= 0) { // Could maybe be smaller than 0 due to the Gamma function return kFALSE; + } // ADD HIT static thread_local int oldTrackId = trackID; @@ -418,8 +420,9 @@ void Detector::CreateMaterials() Int_t cnt = 0; for (Int_t i = 0; i < 6; i++) { if (comp[i]) { - if (cnt) + if (cnt) { gname += "-"; + } gname += names[i]; cnt++; } @@ -3076,6 +3079,18 @@ void Detector::defineSensitiveVolumes() // set volume sentive AddSensitiveVolume(v); } + + // Special sensitive volume parameters in case FLUKA is used as transport engine + auto vmc = TVirtualMC::GetMC(); + if (strcmp(vmc->GetName(), "TFluka") == 0) { + LOG(INFO) << "Setting special FLUKA parameters for TPC Driftgas"; + auto& mgr = o2::base::MaterialManager::Instance(); + Int_t index = mgr.getMediumID("TPC", kDriftGas2); + vmc->Gstpar(index, "PRIMIO_E", 20.77); + vmc->Gstpar(index, "PRIMIO_N", 14.35); + vmc->Gstpar(index, "LOSS", 14); + vmc->Gstpar(index, "STRA", 4); + } } Double_t Detector::Gamma(Double_t k) @@ -3086,12 +3101,13 @@ Double_t Detector::Gamma(Double_t k) static thread_local Double_t b1 = 0; static thread_local Double_t b2 = 0; if (k > 0) { - if (k < 0.4) + if (k < 0.4) { n = 1. / k; - else if (k >= 0.4 && k < 4) + } else if (k >= 0.4 && k < 4) { n = 1. / k + (k - 0.4) / k / 3.6; - else if (k >= 4.) + } else if (k >= 4.) { n = 1. / TMath::Sqrt(k); + } b1 = k - 1. / n; b2 = k + 1. / n; c1 = (k < 0.4) ? 0 : b1 * (TMath::Log(b1) - 1.) / 2.; @@ -3105,11 +3121,13 @@ Double_t Detector::Gamma(Double_t k) Double_t w1 = c1 + TMath::Log(nu1); Double_t w2 = c2 + TMath::Log(nu2); y = n * (b1 * w2 - b2 * w1); - if (y < 0) + if (y < 0) { continue; + } x = n * (w2 - w1); - if (TMath::Log(y) >= x) + if (TMath::Log(y) >= x) { break; + } } return TMath::Exp(x); } diff --git a/Detectors/TPC/simulation/src/Digitizer.cxx b/Detectors/TPC/simulation/src/Digitizer.cxx index 241159f14fe88..c12ecdec9be15 100644 --- a/Detectors/TPC/simulation/src/Digitizer.cxx +++ b/Detectors/TPC/simulation/src/Digitizer.cxx @@ -164,20 +164,33 @@ void Digitizer::flush(std::vector<o2::tpc::Digit>& digits, mDigitContainer.fillOutputContainer(digits, labels, commonModeOutput, mSector, sampaProcessing.getTimeBinFromTime(mEventTime), mIsContinuous, finalFlush); } -void Digitizer::setUseSCDistortions(SpaceCharge::SCDistortionType distortionType, const TH3* hisInitialSCDensity, int nRBins, int nPhiBins, int nZSlices) +void Digitizer::setUseSCDistortions(SC::SCDistortionType distortionType, const TH3* hisInitialSCDensity) { mUseSCDistortions = true; if (!mSpaceCharge) { - mSpaceCharge = std::make_unique<SpaceCharge>(nRBins, nPhiBins, nZSlices); + mSpaceCharge = std::make_unique<SC>(); } mSpaceCharge->setSCDistortionType(distortionType); if (hisInitialSCDensity) { - mSpaceCharge->setInitialSpaceChargeDensity(hisInitialSCDensity); + mSpaceCharge->fillChargeDensityFromHisto(*hisInitialSCDensity); + mSpaceCharge->setUseInitialSCDensity(true); } } -void Digitizer::setUseSCDistortions(SpaceCharge* spaceCharge) +void Digitizer::setUseSCDistortions(SC* spaceCharge) { mUseSCDistortions = true; mSpaceCharge.reset(spaceCharge); } + +void Digitizer::setUseSCDistortions(TFile& finp) +{ + mUseSCDistortions = true; + if (!mSpaceCharge) { + mSpaceCharge = std::make_unique<SC>(); + } + mSpaceCharge->setGlobalDistortionsFromFile(finp, Side::A); + mSpaceCharge->setGlobalDistortionsFromFile(finp, Side::C); + mSpaceCharge->setGlobalCorrectionsFromFile(finp, Side::A); + mSpaceCharge->setGlobalCorrectionsFromFile(finp, Side::C); +} diff --git a/Detectors/TPC/simulation/src/GEMAmplification.cxx b/Detectors/TPC/simulation/src/GEMAmplification.cxx index a0965196c4320..78da03d0cf495 100644 --- a/Detectors/TPC/simulation/src/GEMAmplification.cxx +++ b/Detectors/TPC/simulation/src/GEMAmplification.cxx @@ -59,14 +59,14 @@ GEMAmplification::GEMAmplification() float s = mGEMParam->AbsoluteGain[i] / kappa; polya % kappa % s % s % (kappa - 1) % s; std::string name = polya.str(); - o2::base::CachingTF1* polyaDistribution = nullptr; + o2::math_utils::CachingTF1* polyaDistribution = nullptr; if (!cacheexists) { - polyaDistribution = new o2::base::CachingTF1("polya", name.c_str(), 0, 10.f * mGEMParam->AbsoluteGain[i]); + polyaDistribution = new o2::math_utils::CachingTF1("polya", name.c_str(), 0, 10.f * mGEMParam->AbsoluteGain[i]); /// this dramatically alters the speed with which the filling is executed... /// without this, the distribution makes discrete steps at every int polyaDistribution->SetNpx(100000); } else { - polyaDistribution = (o2::base::CachingTF1*)outfile->Get(TString::Format("func%d", i).Data()); + polyaDistribution = (o2::math_utils::CachingTF1*)outfile->Get(TString::Format("func%d", i).Data()); // FIXME: verify that distribution corresponds to the parameters used here } mGain[i].initialize(*polyaDistribution); @@ -84,12 +84,12 @@ GEMAmplification::GEMAmplification() const float sStack = gainStack / (kappaStack * (1.f - effStack)); polya % kappaStack % sStack % sStack % (kappaStack - 1) % sStack; std::string name = polya.str(); - o2::base::CachingTF1* polyaDistribution = nullptr; + o2::math_utils::CachingTF1* polyaDistribution = nullptr; if (!cacheexists) { - polyaDistribution = new o2::base::CachingTF1("polya", name.c_str(), 0, 50000); + polyaDistribution = new o2::math_utils::CachingTF1("polya", name.c_str(), 0, 50000); polyaDistribution->SetNpx(50000); } else { - polyaDistribution = (o2::base::CachingTF1*)outfile->Get("polyaStack"); + polyaDistribution = (o2::math_utils::CachingTF1*)outfile->Get("polyaStack"); } mGainFullStack.initialize(*polyaDistribution); @@ -98,8 +98,9 @@ GEMAmplification::GEMAmplification() } delete polyaDistribution; - if (outfile) + if (outfile) { outfile->Close(); + } watch.Stop(); LOG(INFO) << "TPC: GEM setup (polya) took " << watch.CpuTime(); } diff --git a/Detectors/TPC/simulation/src/PadResponse.cxx b/Detectors/TPC/simulation/src/PadResponse.cxx index 5a24f955356c8..ebcc2b3fb722a 100644 --- a/Detectors/TPC/simulation/src/PadResponse.cxx +++ b/Detectors/TPC/simulation/src/PadResponse.cxx @@ -43,8 +43,9 @@ bool PadResponse::importPRF(std::string file, std::unique_ptr<TGraph2D>& grPRF) { std::string inputDir; const char* aliceO2env = std::getenv("O2_ROOT"); - if (aliceO2env) + if (aliceO2env) { inputDir = aliceO2env; + } inputDir += "/share/Detectors/TPC/files/"; float x, y, normalizedPadResponse; diff --git a/Detectors/TPC/simulation/src/SpaceCharge.cxx b/Detectors/TPC/simulation/src/SpaceCharge.cxx deleted file mode 100644 index 6cc7d8b141f98..0000000000000 --- a/Detectors/TPC/simulation/src/SpaceCharge.cxx +++ /dev/null @@ -1,652 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SpaceCharge.cxx -/// \brief Implementation of the interface for the ALICE TPC space-charge distortions calculations -/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch - -#include "TGeoGlobalMagField.h" -#include "TH3.h" -#include "TMath.h" -#include "TMatrixD.h" -#include "TStopwatch.h" - -#include "FairLogger.h" - -#include "DataFormatsTPC/Constants.h" -#include "DataFormatsTPC/Defs.h" -#include "Field/MagneticField.h" -#include "MathUtils/Utils.h" -#include "TPCBase/ParameterGas.h" -#include "TPCSimulation/SpaceCharge.h" - -using namespace o2::tpc; -using namespace o2::math_utils; - -const float o2::tpc::SpaceCharge::sEzField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; - -SpaceCharge::SpaceCharge() - : mNZ(MaxNZ), - mNPhi(MaxNPhi), - mNR(Constants::MAXGLOBALPADROW), - mVoxelSizeZ(AliTPCPoissonSolver::fgkTPCZ0 / (MaxNZ - 1)), - mDriftTimeVoxel(IonDriftTime / (MaxNZ - 1)), - mVoxelSizePhi(TWOPI / MaxNPhi), - mVoxelSizeR((AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (Constants::MAXGLOBALPADROW - 1)), - mCoordZ(MaxNZ), - mCoordPhi(MaxNPhi), - mCoordR(Constants::MAXGLOBALPADROW), - mInterpolationOrder(2), - mUseInitialSCDensity(false), - mInitLookUpTables(false), - mMemoryAllocated(false), - mTimeInit(-1), - mSCDistortionType(SpaceCharge::SCDistortionType::SCDistortionsRealistic), - mLookUpTableCalculator(Constants::MAXGLOBALPADROW, MaxNZ, MaxNPhi, 2, 3, 0), - mSpaceChargeDensityA(MaxNPhi * Constants::MAXGLOBALPADROW * MaxNZ), - mSpaceChargeDensityC(MaxNPhi * Constants::MAXGLOBALPADROW * MaxNZ), - mRandomFlat(RandomRing<>::RandomType::Flat) -{ - mLookUpTableCalculator.SetCorrectionType(0); - mLookUpTableCalculator.SetIntegrationStrategy(0); - setVoxelCoordinates(); -} - -SpaceCharge::SpaceCharge(int nRBins, int nPhiBins, int nZSlices) - : mNZ(nZSlices), - mNPhi(nPhiBins), - mNR(nRBins), - mVoxelSizeZ(AliTPCPoissonSolver::fgkTPCZ0 / (nZSlices - 1)), - mDriftTimeVoxel(IonDriftTime / (nZSlices - 1)), - mVoxelSizePhi(TWOPI / nPhiBins), - mVoxelSizeR((AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRBins - 1)), - mCoordZ(nZSlices), - mCoordPhi(nPhiBins), - mCoordR(nRBins), - mInterpolationOrder(2), - mUseInitialSCDensity(false), - mInitLookUpTables(false), - mMemoryAllocated(false), - mTimeInit(-1), - mSCDistortionType(SpaceCharge::SCDistortionType::SCDistortionsRealistic), - mLookUpTableCalculator(nRBins, nZSlices, nPhiBins, 2, 3, 0), - mSpaceChargeDensityA(nPhiBins * nRBins * nZSlices), - mSpaceChargeDensityC(nPhiBins * nRBins * nZSlices), - mRandomFlat(RandomRing<>::RandomType::Flat) -{ - mLookUpTableCalculator.SetCorrectionType(0); - mLookUpTableCalculator.SetIntegrationStrategy(0); - setVoxelCoordinates(); -} - -SpaceCharge::SpaceCharge(int nRBins, int nPhiBins, int nZSlices, int interpolationOrder) - : mNZ(nZSlices), - mNPhi(nPhiBins), - mNR(nRBins), - mVoxelSizeZ(AliTPCPoissonSolver::fgkTPCZ0 / (nZSlices - 1)), - mDriftTimeVoxel(IonDriftTime / (nZSlices - 1)), - mVoxelSizePhi(TWOPI / nPhiBins), - mVoxelSizeR((AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRBins - 1)), - mCoordZ(nZSlices), - mCoordPhi(nPhiBins), - mCoordR(nRBins), - mInterpolationOrder(interpolationOrder), - mUseInitialSCDensity(false), - mInitLookUpTables(false), - mMemoryAllocated(false), - mTimeInit(-1), - mSCDistortionType(SpaceCharge::SCDistortionType::SCDistortionsRealistic), - mLookUpTableCalculator(nRBins, nZSlices, nPhiBins, interpolationOrder, 3, 0), - mSpaceChargeDensityA(nPhiBins * nRBins * nZSlices), - mSpaceChargeDensityC(nPhiBins * nRBins * nZSlices), - mRandomFlat(RandomRing<>::RandomType::Flat) -{ - mLookUpTableCalculator.SetCorrectionType(0); - mLookUpTableCalculator.SetIntegrationStrategy(0); - setVoxelCoordinates(); -} - -void SpaceCharge::setVoxelCoordinates() -{ - for (int iz = 0; iz < mNZ; ++iz) { - mCoordZ[iz] = iz * mVoxelSizeZ; - } - for (int iphi = 0; iphi < mNPhi; ++iphi) { - mCoordPhi[iphi] = iphi * mVoxelSizePhi; - } - for (int ir = 0; ir < mNR; ++ir) { - mCoordR[ir] = AliTPCPoissonSolver::fgkIFCRadius + ir * mVoxelSizeR; - } -} - -void SpaceCharge::allocateMemory() -{ - mMatrixIonDriftZA = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - mMatrixIonDriftRPhiA = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - mMatrixIonDriftRA = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - mMatrixIonDriftZC = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - mMatrixIonDriftRPhiC = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - mMatrixIonDriftRC = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - for (int iphi = 0; iphi < mNPhi; ++iphi) { - mMatrixIonDriftZA[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - mMatrixIonDriftRPhiA[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - mMatrixIonDriftRA[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - mMatrixIonDriftZC[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - mMatrixIonDriftRPhiC[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - mMatrixIonDriftRC[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - } - mLookUpIonDriftA = std::make_unique<AliTPCLookUpTable3DInterpolatorD>(mNR, (TMatrixD**)mMatrixIonDriftRA.get(), mCoordR.data(), mNPhi, (TMatrixD**)mMatrixIonDriftRPhiA.get(), mCoordPhi.data(), mNZ, (TMatrixD**)mMatrixIonDriftZA.get(), mCoordZ.data(), mInterpolationOrder); - mLookUpIonDriftC = std::make_unique<AliTPCLookUpTable3DInterpolatorD>(mNR, (TMatrixD**)mMatrixIonDriftRC.get(), mCoordR.data(), mNPhi, (TMatrixD**)mMatrixIonDriftRPhiC.get(), mCoordPhi.data(), mNZ, (TMatrixD**)mMatrixIonDriftZC.get(), mCoordZ.data(), mInterpolationOrder); - mMemoryAllocated = true; -} - -void SpaceCharge::init() -{ - if (!mInitLookUpTables) { - auto o2field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); - const float bzField = o2field->solenoidField(); // magnetic field in kGauss - /// TODO is there a faster way to get the drift velocity - auto& gasParam = ParameterGas::Instance(); - float vDrift = gasParam.DriftV; // drift velocity in cm/us - /// TODO fix hard coded values (ezField, t1, t2): export to Constants.h or get from somewhere? - const float t1 = 1.; - const float t2 = 1.; - /// TODO use this parameterization or fixed value(s) from Magboltz calculations? - const float omegaTau = -10. * bzField * vDrift / std::abs(sEzField); - setOmegaTauT1T2(omegaTau, t1, t2); - if (mUseInitialSCDensity) { - calculateLookupTables(); - } - } -} - -float SpaceCharge::calculateLookupTables() -{ - // Potential, E field and electron distortion and correction lookup tables - TStopwatch timer; - mLookUpTableCalculator.ForceInitSpaceCharge3DPoissonIntegralDz(mNR, mNZ, mNPhi, 300, 1e-8); - float tRealCalc = static_cast<float>(timer.RealTime()); - - // Lookup tables for local ion drift along E field - if (mSCDistortionType == SCDistortionType::SCDistortionsRealistic) { - if (!mMemoryAllocated) { - allocateMemory(); - } - TMatrixD* matrixDriftZ = nullptr; - TMatrixD* matrixDriftRPhi = nullptr; - TMatrixD* matrixDriftR = nullptr; - for (int iside = 0; iside < 2; ++iside) { - const int sign = 1 - iside * 2; - for (int iphi = 0; iphi < mNPhi; ++iphi) { - const float phi = static_cast<float>(mCoordPhi[iphi]); - if (iside == 0) { - matrixDriftZ = mMatrixIonDriftZA[iphi].get(); - matrixDriftRPhi = mMatrixIonDriftRPhiA[iphi].get(); - matrixDriftR = mMatrixIonDriftRA[iphi].get(); - } else { - matrixDriftZ = mMatrixIonDriftZC[iphi].get(); - matrixDriftRPhi = mMatrixIonDriftRPhiC[iphi].get(); - matrixDriftR = mMatrixIonDriftRC[iphi].get(); - } - int roc = iside == 0 ? o2::utils::Angle2Sector(phi) : o2::utils::Angle2Sector(phi) + 18; - for (int ir = 0; ir < mNR; ++ir) { - const float radius = static_cast<float>(mCoordR[ir]); - /// TODO: what is the electric field stored in the LUTs at iz=0 and iz=mNZSlices-1 - for (int iz = 0; iz < mNZ; ++iz) { - const float z = static_cast<float>(mCoordZ[iz]); - float x0[3] = {radius, phi, sign * (z + static_cast<float>(mVoxelSizeZ))}; // iphi, ir, iz+1 - float x1[3] = {radius, phi, sign * z}; // iphi, ir, iz - if (iside == 1) { - x0[2] *= -1; - x1[2] *= -1; - } - double eVector0[3] = {0., 0., 0.}; - double eVector1[3] = {0., 0., 0.}; - mLookUpTableCalculator.GetElectricFieldCyl(x0, roc, eVector0); // returns correct sign for Ez - mLookUpTableCalculator.GetElectricFieldCyl(x1, roc, eVector1); // returns correct sign for Ez - - // drift of ions along E field - (*matrixDriftR)(ir, iz) = -1 * sign * mVoxelSizeZ * 0.5 * (eVector0[0] + eVector1[0]) / (sign * sEzField + eVector0[2]); - (*matrixDriftRPhi)(ir, iz) = -1 * sign * mVoxelSizeZ * 0.5 * (eVector0[1] + eVector1[1]) / (sign * sEzField + eVector0[2]); - (*matrixDriftZ)(ir, iz) = -1 * sign * mVoxelSizeZ + DvDEoverv0 * mVoxelSizeZ * 0.5 * (eVector0[2] + eVector1[2]); - } - } - } - if (iside == 0) { - mLookUpIonDriftA->CopyFromMatricesToInterpolator(); - } else { - mLookUpIonDriftC->CopyFromMatricesToInterpolator(); - } - } - - // TODO: Propagate current SC density along E field by one time bin for next update - // propagateSpaceCharge(); - } - - mInitLookUpTables = true; - return tRealCalc; -} - -float SpaceCharge::updateLookupTables(float eventTime) -{ - // TODO: only update after update time interval - // if (mTimeInit < 0.) { - // mTimeInit = eventTime; // set the time of first initialization - // } - // if (std::abs(eventTime - mTimeInit) < mDriftTimeVoxel) { - // return 0.f; // update only after one time bin has passed - // } - // mTimeInit = eventTime; - - std::unique_ptr<std::unique_ptr<TMatrixD>[]> spaceChargeA = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - std::unique_ptr<std::unique_ptr<TMatrixD>[]> spaceChargeC = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - for (int iphi = 0; iphi < mNPhi; ++iphi) { - spaceChargeA[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - spaceChargeC[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - } - for (int iside = 0; iside < 2; ++iside) { - for (int iphi = 0; iphi < mNPhi; ++iphi) { - TMatrixD& chargeDensity = iside == 0 ? *spaceChargeA[iphi] : *spaceChargeC[iphi]; - for (int ir = 0; ir < mNR; ++ir) { - for (int iz = 0; iz < mNZ; ++iz) { - if (iside == 0) { - chargeDensity(ir, iz) = mSpaceChargeDensityA[iphi * mNR * mNZ + ir * mNZ + iz]; - } else { - chargeDensity(ir, iz) = mSpaceChargeDensityC[iphi * mNR * mNZ + ir * mNZ + iz]; - } - } - } - } - } - mLookUpTableCalculator.SetInputSpaceChargeA((TMatrixD**)spaceChargeA.get()); - mLookUpTableCalculator.SetInputSpaceChargeC((TMatrixD**)spaceChargeC.get()); - float tRealCalc = calculateLookupTables(); - return tRealCalc; -} - -void SpaceCharge::setOmegaTauT1T2(float omegaTau, float t1, float t2) -{ - mLookUpTableCalculator.SetOmegaTauT1T2(omegaTau, t1, t2); -} - -void SpaceCharge::setInitialSpaceChargeDensity(const TH3* hisSCDensity) -{ - std::unique_ptr<std::unique_ptr<TMatrixD>[]> spaceChargeA = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - std::unique_ptr<std::unique_ptr<TMatrixD>[]> spaceChargeC = std::make_unique<std::unique_ptr<TMatrixD>[]>(mNPhi); - for (int iphi = 0; iphi < mNPhi; ++iphi) { - spaceChargeA[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - spaceChargeC[iphi] = std::make_unique<TMatrixD>(mNR, mNZ); - } - mLookUpTableCalculator.GetChargeDensity((TMatrixD**)spaceChargeA.get(), (TMatrixD**)spaceChargeC.get(), hisSCDensity, mNR, mNZ, mNPhi); - for (int iside = 0; iside < 2; ++iside) { - for (int iphi = 0; iphi < mNPhi; ++iphi) { - TMatrixD& chargeDensity = iside == 0 ? *spaceChargeA[iphi] : *spaceChargeC[iphi]; - for (int ir = 0; ir < mNR; ++ir) { - for (int iz = 0; iz < mNZ; ++iz) { - if (iside == 0) { - mSpaceChargeDensityA[iphi * mNR * mNZ + ir * mNZ + iz] = chargeDensity(ir, iz); - } else { - mSpaceChargeDensityC[iphi * mNR * mNZ + ir * mNZ + iz] = chargeDensity(ir, iz); - } - } - } - } - } - mLookUpTableCalculator.SetInputSpaceChargeA((TMatrixD**)spaceChargeA.get()); - mLookUpTableCalculator.SetInputSpaceChargeC((TMatrixD**)spaceChargeC.get()); - mUseInitialSCDensity = true; -} - -void SpaceCharge::fillPrimaryIons(double r, double phi, double z, int nIons) -{ - Side side = z > 0 ? Side::A : Side::C; - double dr = 0.; - double drphi = 0.; - double dz = 0.; - getIonDrift(side, r, phi, z, dr, drphi, dz); - double rdist = r + dr; - double phidist = phi + drphi / r; - double zdist = z + dz; - const int zBin = TMath::BinarySearch(mNZ, mCoordZ.data(), std::abs(zdist)); - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phidist); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), rdist); - /// TODO: protection against ions ending up outside the volume - if (z > 0 && zdist > 0) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } else if (z < 0 && zdist < 0) { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } -} - -void SpaceCharge::fillPrimaryCharge(double r, double phi, double z, float charge) -{ - Side side = z > 0 ? Side::A : Side::C; - double dr = 0.; - double drphi = 0.; - double dz = 0.; - getIonDrift(side, r, phi, z, dr, drphi, dz); - double rdist = r + dr; - double phidist = phi + drphi / r; - double zdist = z + dz; - const int zBin = TMath::BinarySearch(mNZ, mCoordZ.data(), std::abs(zdist)); - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phidist); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), rdist); - /// TODO: protection against ions ending up outside the volume - if (z > 0 && zdist > 0) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } else if (z < 0 && zdist < 0) { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } -} - -void SpaceCharge::fillIBFIons(double r, double phi, Side side, int nIons) -{ - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phi); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), r); - const int zBin = mNZ - 1; - /// TODO: distribution of amplification ions instead of placing all of them in one point - /// TODO: protection against ions ending up outside the volume - if (side == Side::A) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } else { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } -} - -void SpaceCharge::fillIBFCharge(double r, double phi, Side side, float charge) -{ - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phi); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), r); - const int zBin = mNZ - 1; - /// TODO: distribution of amplification ions instead of placing all of them in one point - /// TODO: protection against ions ending up outside the volume - if (side == Side::A) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } else { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } -} - -void SpaceCharge::propagateSpaceCharge() -{ - // - // Continuity equation, assuming that the charge is conserved: - // delta(rho_0) / delta(t) = - div(flux) = - div(rho_0 * u) = - ( rho_0 * div(u) + u * grad(rho_0) ) - // u: velocity vector of space-charge density - // rho_0: space-charge density at time t0 - // - // - // Calculate the change of the space-charge density rho_0 at time t0 after time delta(t) = mVoxelSizeZ / v_driftIon: - // delta(rho_0) = - ( rho_0 * div(u) + u * grad(rho_0) ) * delta(t) - // = - ( rho_0 * div(d) + d * grad(rho_0) ) - // d: drift vector (dr, dphi, mVoxelSizeZ + dz) - // - // div(d) = 1/r del(r*d_r)/del(r) + 1/r del(d_phi)/del(phi) + del(d_z)/del(z) - // = d_r/r + del(d_r)/del(r) + 1/r del(d_phi)/del(phi) + del(d_z)/del(z) - // - // grad(rho_0) = del(rho_0)/del(r) * e_r + 1/r * del(rho_0)/del(phi) * e_phi + del(rho_0)/del(z) * e_z - // e_i: unit vectors in i = {r, phi, z} direction - // - - // Finite difference interpolation coefficients - const float coeffCent40 = 1.f / 12.f; - const float coeffCent41 = -2.f / 3.f; - const float coeffCent42 = -1.f * coeffCent41; - const float coeffCent43 = -1.f * coeffCent40; - const float coeffFwd30 = -11.f / 6.f; - const float coeffFwd31 = 3.f; - const float coeffFwd32 = -1.5; - const float coeffFwd33 = 1.f / 3.f; - const float coeffFwd20 = -1.5; - const float coeffFwd21 = 2.f; - const float coeffFwd22 = -0.5; - - std::vector<float>* scDensity = nullptr; - TMatrixD** matrixDriftZ = nullptr; - TMatrixD** matrixDriftRPhi = nullptr; - TMatrixD** matrixDriftR = nullptr; - for (int iside = 0; iside < 2; ++iside) { - const int signZ = iside == 0 ? 1 : -1; - if (iside == 0) { - scDensity = &mSpaceChargeDensityA; - matrixDriftZ = (TMatrixD**)mMatrixIonDriftZA.get(); - matrixDriftRPhi = (TMatrixD**)mMatrixIonDriftRPhiA.get(); - matrixDriftR = (TMatrixD**)mMatrixIonDriftRA.get(); - } else { - scDensity = &mSpaceChargeDensityC; - matrixDriftZ = (TMatrixD**)mMatrixIonDriftZC.get(); - matrixDriftRPhi = (TMatrixD**)mMatrixIonDriftRPhiC.get(); - matrixDriftR = (TMatrixD**)mMatrixIonDriftRC.get(); - } - /// TODO: is there a better way than to create a copy for the new SC density? - std::vector<float> newSCDensity(mNPhi * mNR * mNZ); - for (int iphi0 = 0; iphi0 < mNPhi; ++iphi0) { - - for (int ir0 = 0; ir0 < mNR; ++ir0) { - const float r0 = static_cast<float>(mCoordR[ir0]); - - for (int iz0 = 0; iz0 < mNZ; ++iz0) { - - // rho_0 * div(d) - float ddrdr = 0.f; - if (ir0 > 1 && ir0 < mNR - 2) { - ddrdr = (coeffCent40 * (*matrixDriftR[iphi0])(ir0 - 2, iz0) + coeffCent41 * (*matrixDriftR[iphi0])(ir0 - 1, iz0) + coeffCent42 * (*matrixDriftR[iphi0])(ir0 + 1, iz0) + coeffCent43 * (*matrixDriftR[iphi0])(ir0 + 2, iz0)) / static_cast<float>(mVoxelSizeR); - } else if (ir0 < 2) { - ddrdr = (coeffFwd30 * (*matrixDriftR[iphi0])(ir0, iz0) + coeffFwd31 * (*matrixDriftR[iphi0])(ir0 + 1, iz0) + coeffFwd32 * (*matrixDriftR[iphi0])(ir0 + 2, iz0) + coeffFwd33 * (*matrixDriftR[iphi0])(ir0 + 3, iz0)) / static_cast<float>(mVoxelSizeR); - } else if (ir0 > (mNR - 3)) { - ddrdr = -1 * (coeffFwd30 * (*matrixDriftR[iphi0])(ir0, iz0) + coeffFwd31 * (*matrixDriftR[iphi0])(ir0 - 1, iz0) + coeffFwd32 * (*matrixDriftR[iphi0])(ir0 - 2, iz0) + coeffFwd33 * (*matrixDriftR[iphi0])(ir0 - 3, iz0)) / static_cast<float>(mVoxelSizeR); - } - - const int iphiCent0 = iphi0 - 2 + (mNPhi) * (iphi0 < 2); - const int iphiCent1 = iphi0 - 1 + (mNPhi) * (iphi0 < 1); - const int iphiCent3 = iphi0 + 1 - (mNPhi) * (iphi0 > (mNPhi - 2)); - const int iphiCent4 = iphi0 + 2 - (mNPhi) * (iphi0 > (mNPhi - 3)); - const float ddphidphi = (coeffCent40 * (*matrixDriftRPhi[iphiCent0])(ir0, iz0) + coeffCent41 * (*matrixDriftRPhi[iphiCent1])(ir0, iz0) + coeffCent42 * (*matrixDriftRPhi[iphiCent3])(ir0, iz0) + coeffCent43 * (*matrixDriftRPhi[iphiCent4])(ir0, iz0)) / static_cast<float>(mVoxelSizePhi); - - float ddzdz = 0.f; - if (iz0 > 1 && iz0 < mNZ - 2) { - ddzdz = signZ * (coeffCent40 * (*matrixDriftZ[iphi0])(ir0, iz0 - 2) + coeffCent41 * (*matrixDriftZ[iphi0])(ir0, iz0 - 1) + coeffCent42 * (*matrixDriftZ[iphi0])(ir0, iz0 + 1) + coeffCent43 * (*matrixDriftZ[iphi0])(ir0, iz0 + 2)) / static_cast<float>(mVoxelSizeR); - } else if (iz0 == 1 || iz0 == (mNZ - 2)) { - ddzdz = signZ * (-0.5 * (*matrixDriftZ[iphi0])(ir0, iz0 - 1) + 0.5 * (*matrixDriftZ[iphi0])(ir0, iz0 + 1)) / static_cast<float>(mVoxelSizeR); - } else if (iz0 == 0) { - ddzdz = signZ * (coeffFwd20 * (*matrixDriftZ[iphi0])(ir0, iz0) + coeffFwd21 * (*matrixDriftZ[iphi0])(ir0, iz0 + 1) + coeffFwd22 * (*matrixDriftZ[iphi0])(ir0, iz0 + 2)) / static_cast<float>(mVoxelSizeR); - } else if (iz0 == (mNZ - 1)) { - ddzdz = -1 * signZ * (coeffFwd20 * (*matrixDriftZ[iphi0])(ir0, iz0) + coeffFwd21 * (*matrixDriftZ[iphi0])(ir0, iz0 - 1) + coeffFwd22 * (*matrixDriftZ[iphi0])(ir0, iz0 - 2)) / static_cast<float>(mVoxelSizeR); - } - - const float qdivd = (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] * (((*matrixDriftR[iphi0])(ir0, iz0) + ddphidphi) / r0 + ddrdr + ddzdz); - - // - d * grad(rho_0) = - d_drift * grad(rho_0(x)) + d_dist * grad(rho_0(x+d_drift)) = (charge0 - charge1) + d_dist * grad(rho_0(x-d_drift)) - if (iz0 < (mNZ - 1)) { - const float dr = (*matrixDriftR[iphi0])(ir0, iz0); - const float drphi = (*matrixDriftRPhi[iphi0])(ir0, iz0); - const float dz = (*matrixDriftZ[iphi0])(ir0, iz0) + mVoxelSizeZ * signZ; - - const int ir1 = dr < 0 ? ir0 - 1 * (ir0 == (mNR - 1)) : ir0 - 1 + 1 * (ir0 == 0); - const int ir2 = dr < 0 ? ir0 + 1 - 1 * (ir0 == (mNR - 1)) : ir0 + 1 * (ir0 == 0); - const int iphi1 = drphi < 0 ? iphi0 : iphi0 - 1 + (mNPhi) * (iphi0 == 0); - const int iphi2 = drphi < 0 ? iphi0 + 1 - (mNPhi) * (iphi0 == (mNPhi - 1)) : iphi0; - const int iz1 = dz < 0 ? iz0 + 1 - 1 * (iside == 0) * (iz0 == (mNZ - 2)) : iz0 + 2 * (iside == 1) - 1 * (iside == 1) * (iz0 == (mNZ - 2)); - const int iz2 = dz < 0 ? iz0 + 2 - 2 * (iside == 1) - 1 * (iside == 0) * (iz0 == (mNZ - 2)) : iz0 + 1 - 1 * (iside == 1) * (iz0 == (mNZ - 2)); - - const float dqdr = ((*scDensity)[iphi0 * mNR * mNZ + ir2 * mNZ + (iz0 + 1)] - (*scDensity)[iphi0 * mNR * mNZ + ir1 * mNZ + (iz0 + 1)]) / static_cast<float>(mVoxelSizeR); - const float dqdphi = ((*scDensity)[iphi2 * mNR * mNZ + ir0 * mNZ + (iz0 + 1)] - (*scDensity)[iphi1 * mNR * mNZ + ir0 * mNZ + (iz0 + 1)]) / static_cast<float>(mVoxelSizePhi); - const float dqdz = ((*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz2] - (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz1]) / static_cast<float>(mVoxelSizeZ); - - const float dgradq = dr * dqdr + drphi / r0 * dqdphi + dz * dqdz; - - newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] = (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + (iz0 + 1)] - (qdivd + dgradq); - } else { - newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] = -qdivd; - } - - if (newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] < 0.f) { - newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] = 0.f; - } - } - } - } - (*scDensity).swap(newSCDensity); - } -} - -void SpaceCharge::propagateIons() -{ - std::vector<float>* scDensity = nullptr; - AliTPCLookUpTable3DInterpolatorD* lookUpIonDrift = nullptr; - for (int iside = 0; iside < 2; ++iside) { - const int signZ = iside == 0 ? 1 : -1; - if (iside == 0) { - scDensity = &mSpaceChargeDensityA; - lookUpIonDrift = mLookUpIonDriftA.get(); - } else { - scDensity = &mSpaceChargeDensityC; - lookUpIonDrift = mLookUpIonDriftC.get(); - } - std::vector<float> newSCDensity(mNPhi * mNR * mNZ); - - for (int iphi0 = 0; iphi0 < mNPhi; ++iphi0) { - const double phi0 = static_cast<float>(mCoordPhi[iphi0]); - - for (int ir0 = 0; ir0 < mNR; ++ir0) { - const double r0 = static_cast<float>(mCoordR[ir0]); - const double r1 = r0 + mVoxelSizeR; - - for (int iz0 = 0; iz0 < mNZ; ++iz0) { - const double z0 = mCoordZ[iz0] * signZ; - - const float ionDensity = (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] * AliTPCPoissonSolver::fgke0 / TMath::Qe(); // #ions / cm^3 - const float nIons = std::round(ionDensity * mVoxelSizeZ * 0.5 * mVoxelSizePhi * (r1 * r1 - r0 * r0)); // absolute #ions in the voxel - - for (int iion = 0; iion < nIons; ++iion) { - double phiIon = phi0 + mRandomFlat.getNextValue() * mVoxelSizePhi; - double rIon = r0 + mRandomFlat.getNextValue() * mVoxelSizeR; - double zIon = z0 + mRandomFlat.getNextValue() * mVoxelSizeZ * signZ; - - double drphi = 0.f; - double dr = 0.f; - double dz = 0.f; - lookUpIonDrift->GetValue(rIon, phiIon, std::abs(zIon), dr, drphi, dz); - float phiIonF = static_cast<float>(phiIon + (drphi / rIon)); - o2::utils::BringTo02PiGen(phiIonF); - rIon += dr; - zIon += dz; - - // continue if ion is outside the TPC boundaries in r or z - if (rIon > (mCoordR[mNR - 1] + mVoxelSizeR) || rIon < mCoordR[0]) { - continue; - } - if ((zIon * signZ) > (mCoordZ[mNZ - 1] + mVoxelSizeZ) || (zIon * signZ) < mCoordZ[0]) { - continue; - } - - const int iphiDrift = TMath::BinarySearch(mNPhi, mCoordPhi.data(), static_cast<double>(phiIonF)); - const int irDrift = TMath::BinarySearch(mNR, mCoordR.data(), rIon); - const int izDrift = TMath::BinarySearch(mNZ, mCoordZ.data(), std::abs(zIon)); - newSCDensity[iphiDrift * mNR * mNZ + irDrift * mNZ + izDrift] += ions2Charge(irDrift, 1); - } - } - } - } - (*scDensity).swap(newSCDensity); - } -} - -void SpaceCharge::getIonDrift(Side side, double r, double phi, double z, double& dr, double& drphi, double& dz) const -{ - if (!mInitLookUpTables) { - return; - } - if (side == Side::A) { - mLookUpIonDriftA->GetValue(r, phi, z, dr, drphi, dz); - } else if (side == Side::C) { - mLookUpIonDriftC->GetValue(r, phi, -1 * z, dr, drphi, dz); - } else { - LOG(INFO) << "TPC side undefined! Cannot calculate local ion drift correction..."; - } -} - -void SpaceCharge::correctElectron(GlobalPosition3D& point) -{ - if (!mInitLookUpTables) { - return; - } - const float x[3] = {point.X(), point.Y(), point.Z()}; - float dx[3] = {0.f, 0.f, 0.f}; - float phi = point.phi(); - o2::utils::BringTo02PiGen(phi); - int roc = o2::utils::Angle2Sector(phi); - /// FIXME: which side when z==0? - if (x[2] < 0) { - roc += 18; - } - mLookUpTableCalculator.GetCorrection(x, roc, dx); - point.SetXYZ(x[0] + dx[0], x[1] + dx[1], x[2] + dx[2]); -} - -void SpaceCharge::distortElectron(GlobalPosition3D& point) const -{ - if (!mInitLookUpTables) { - return; - } - const float x[3] = {point.X(), point.Y(), point.Z()}; - float dx[3] = {0.f, 0.f, 0.f}; - float phi = point.phi(); - o2::utils::BringTo02PiGen(phi); - int roc = o2::utils::Angle2Sector(phi); - /// FIXME: which side when z==0? - if (x[2] < 0) { - roc += 18; - } - mLookUpTableCalculator.GetDistortion(x, roc, dx); - point.SetXYZ(x[0] + dx[0], x[1] + dx[1], x[2] + dx[2]); -} - -double SpaceCharge::getChargeDensity(Side side, const GlobalPosition3D& point) const -{ - Float_t x[3] = {point.rho(), point.phi(), point.z()}; - o2::utils::BringTo02PiGen(x[1]); - const int roc = side == Side::A ? o2::utils::Angle2Sector(x[1]) : o2::utils::Angle2Sector(x[1]) + 18; - return mLookUpTableCalculator.GetChargeCylAC(x, roc); -} - -float SpaceCharge::getChargeDensity(Side side, int ir, int iphi, int iz) const -{ - if (side == Side::A) { - return mSpaceChargeDensityA[iphi * mNR * mNZ + ir * mNZ + iz]; - } else if (side == Side::C) { - return mSpaceChargeDensityC[iphi * mNR * mNZ + ir * mNZ + iz]; - } else { - return -1.f; - } -} - -void SpaceCharge::setUseIrregularLUTs(int useIrrLUTs) -{ - mLookUpTableCalculator.SetCorrectionType(useIrrLUTs); -} - -void SpaceCharge::setUseFastDistIntegration(int useFastInt) -{ - mLookUpTableCalculator.SetIntegrationStrategy(useFastInt); -} - -void SpaceCharge::setDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC) -{ - mLookUpTableCalculator.SetDistortionLookupTables(matrixIntDistDrA, matrixIntDistDrphiA, matrixIntDistDzA, matrixIntDistDrC, matrixIntDistDrphiC, matrixIntDistDzC); - mInitLookUpTables = true; -} - -float SpaceCharge::ions2Charge(int rBin, int nIons) -{ - float rInner = mCoordR[rBin]; - float rOuter = mCoordR[rBin] + mVoxelSizeR; - return nIons * TMath::Qe() / (mVoxelSizeZ * 0.5 * mVoxelSizePhi * (rOuter * rOuter - rInner * rInner)) / AliTPCPoissonSolver::fgke0; -} diff --git a/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h b/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h index 48edbf58b0277..8a727c250f44d 100644 --- a/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h +++ b/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h @@ -32,8 +32,6 @@ #pragma link C++ class std::vector < o2::tpc::ElementalHit> + ; #pragma link C++ class o2::tpc::HitGroup + ; #pragma link C++ class o2::tpc::SAMPAProcessing + ; -#pragma link C++ class o2::tpc::SpaceCharge + ; -#pragma link C++ enum o2::tpc::SpaceCharge::SCDistortionType + ; #pragma link C++ class std::vector < o2::tpc::HitGroup> + ; diff --git a/Detectors/TPC/simulation/test/testTPCSimulation.cxx b/Detectors/TPC/simulation/test/testTPCSimulation.cxx index 34a9bafb2197b..0982ffa7c8c96 100644 --- a/Detectors/TPC/simulation/test/testTPCSimulation.cxx +++ b/Detectors/TPC/simulation/test/testTPCSimulation.cxx @@ -18,9 +18,10 @@ #include <boost/test/unit_test.hpp> #include "TPCSimulation/Point.h" #include "TPCSimulation/DigitMCMetaData.h" +#include "MathUtils/Cartesian.h" template <typename T> -using Point3D = ROOT::Math::PositionVector3D<ROOT::Math::Cartesian3D<T>, ROOT::Math::DefaultCoordinateSystemTag>; +using Point3D = o2::math_utils::Point3D<T>; namespace o2 { diff --git a/Detectors/TPC/spacecharge/CMakeLists.txt b/Detectors/TPC/spacecharge/CMakeLists.txt new file mode 100644 index 0000000000000..a1eb18a71242d --- /dev/null +++ b/Detectors/TPC/spacecharge/CMakeLists.txt @@ -0,0 +1,50 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(TPCSpaceCharge + TARGETVARNAME targetName + SOURCES src/SpaceCharge.cxx + src/PoissonSolver.cxx + PUBLIC_LINK_LIBRARIES O2::TPCBase + Vc::Vc + ROOT::Core) + +o2_target_root_dictionary(TPCSpaceCharge + HEADERS include/TPCSpaceCharge/PoissonSolver.h + include/TPCSpaceCharge/SpaceCharge.h + include/TPCSpaceCharge/RegularGrid3D.h + include/TPCSpaceCharge/DataContainer3D.h + include/TPCSpaceCharge/PoissonSolverHelpers.h + include/TPCSpaceCharge/SpaceChargeHelpers.h + include/TPCSpaceCharge/TriCubic.h + include/TPCSpaceCharge/Vector.h + include/TPCSpaceCharge/Vector3D.h + LINKDEF src/TPCSpacechargeLinkDef.h) + +o2_add_test_root_macro(macro/calculateDistortionsCorrections.C + PUBLIC_LINK_LIBRARIES O2::TPCSpaceCharge + LABELS tpc COMPILE_ONLY) + +o2_add_test_root_macro(macro/createResidualDistortionObject.C + PUBLIC_LINK_LIBRARIES O2::TPCSpaceCharge + O2::CommonUtils + LABELS tpc) + +o2_add_test(PoissonSolver + COMPONENT_NAME spacecharge + PUBLIC_LINK_LIBRARIES O2::TPCSpaceCharge + SOURCES test/testO2TPCPoissonSolver.cxx + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage + LABELS tpc) + +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h new file mode 100644 index 0000000000000..a641a78598336 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h @@ -0,0 +1,204 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DataContainer3D.h +/// \brief This class provides a simple method to store values on a large 3-Dim grid with ROOT io functionality +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_DATACONTAINER3D_H_ +#define ALICEO2_TPC_DATACONTAINER3D_H_ + +#include <memory> +#include "TFile.h" +#include "Rtypes.h" +#include "Framework/Logger.h" +#include <iomanip> + +namespace o2 +{ +namespace tpc +{ + +/// \class DataContainer3D +/// The DataContainer3D class represents a simple method to store values on a large 3-Dim grid with ROOT io functionality. + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nx number of values in x direction +/// \tparam Ny number of values in y direction +/// \tparam Nz number of values in z direction +template <typename DataT = double, unsigned int Nx = 129, unsigned int Ny = 129, unsigned int Nz = 180> +struct DataContainer3D { + + ///< default constructor + DataContainer3D() : mData(FN){}; + + /// operator to directly access the values + const DataT& operator[](size_t i) const { return mData[i]; } + DataT& operator[](size_t i) { return mData[i]; } + + const auto& getData() const { return mData; } + auto& getData() { return mData; } + + /// \param ix index in x dimension + /// \param iy index in y dimension + /// \param iz index in z dimension + /// \return returns the stored value + const DataT& operator()(size_t ix, size_t iy, size_t iz) const + { + const size_t ind = getDataIndex(ix, iy, iz); + return mData[ind]; + } + + /// \param ix index in x dimension + /// \param iy index in y dimension + /// \param iz index in z dimension + /// \return returns the stored value + DataT& operator()(size_t ix, size_t iy, size_t iz) + { + const size_t ind = getDataIndex(ix, iy, iz); + return mData[ind]; + } + + /// \param ix index in x dimension + /// \param iy index in y dimension + /// \param iz index in z dimension + /// \return returns the index to the data + static constexpr size_t getDataIndex(const size_t ix, const size_t iy, const size_t iz) + { + const size_t index = ix + Nx * (iy + iz * Ny); + return index; + } + + /// \return returns the number of values stored + static constexpr size_t getNDataPoints() { return FN; } + + /// \return returns the number of x vertices + static constexpr size_t getNX() { return Nx; } + + /// \return returns the number of y vertices + static constexpr size_t getNY() { return Ny; } + + /// \return returns the number of z vertices + static constexpr size_t getNZ() { return Nz; } + + /// write this object to a file + /// \param outf object is written to this file + /// \param name object is saved with this name + int writeToFile(TFile& outf, const char* name = "data") const; + + /// set values from file + bool initFromFile(TFile& inpf, const char* name = "data"); + + /// get pointer to object from file + inline static DataContainer3D<DataT, Nx, Ny, Nz>* loadFromFile(TFile& inpf, const char* name = "data"); + + /// print the matrix + void print() const; + + private: + static constexpr size_t FN{Nx * Ny * Nz}; ///< number of values stored in the container + std::vector<DataT> mData; ///< storage for the data + + ClassDefNV(DataContainer3D, 1) +}; + +/// +/// ======================================================================================================== +/// Inline implementations of some methods +/// ======================================================================================================== +/// + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +int DataContainer3D<DataT, Nx, Ny, Nz>::writeToFile(TFile& outf, const char* name) const +{ + if (outf.IsZombie()) { + LOGP(ERROR, "Failed to write to file: {}", outf.GetName()); + return -1; + } + outf.WriteObjectAny(this, DataContainer3D<DataT, Nx, Ny, Nz>::Class(), name); + return 0; +} + +/// set values from file +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +bool DataContainer3D<DataT, Nx, Ny, Nz>::initFromFile(TFile& inpf, const char* name) +{ + if (inpf.IsZombie()) { + LOGP(ERROR, "Failed to read from file: {}", inpf.GetName()); + return false; + } + DataContainer3D<DataT, Nx, Ny, Nz>* dataCont{nullptr}; + + dataCont = reinterpret_cast<DataContainer3D<DataT, Nx, Ny, Nz>*>(inpf.GetObjectChecked(name, DataContainer3D<DataT, Nx, Ny, Nz>::Class())); + if (!dataCont) { + LOGP(ERROR, "Failed to load {} from {}", name, inpf.GetName()); + return false; + } + mData = dataCont->mData; + return true; +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +DataContainer3D<DataT, Nx, Ny, Nz>* DataContainer3D<DataT, Nx, Ny, Nz>::loadFromFile(TFile& inpf, const char* name) +{ + if (inpf.IsZombie()) { + LOGP(ERROR, "Failed to read from file {}", inpf.GetName()); + return nullptr; + } + DataContainer3D<DataT, Nx, Ny, Nz>* dataCont{nullptr}; + + dataCont = reinterpret_cast<DataContainer3D<DataT, Nx, Ny, Nz>*>(inpf.GetObjectChecked(name, DataContainer3D<DataT, Nx, Ny, Nz>::Class())); + if (!dataCont) { + LOGP(ERROR, "Failed to load {} from {}", name, inpf.GetName()); + return nullptr; + } + return dataCont; +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +void DataContainer3D<DataT, Nx, Ny, Nz>::print() const +{ + std::stringstream stream; + stream.precision(3); + auto&& w = std::setw(9); + stream << std::endl; + + for (unsigned int iz = 0; iz < Nz; ++iz) { + stream << "z layer: " << iz << "\n"; + // print top x row + stream << "⎡" << w << (*this)(0, 0, iz); + for (unsigned int ix = 1; ix < Nx; ++ix) { + stream << ", " << w << (*this)(ix, 0, iz); + } + stream << " ⎤ \n"; + + for (unsigned int iy = 1; iy < Ny - 1; ++iy) { + stream << "⎢" << w << (*this)(0, iy, iz); + for (unsigned int ix = 1; ix < Nx; ++ix) { + stream << ", " << w << (*this)(ix, iy, iz); + } + stream << " ⎥ \n"; + } + + stream << "⎣" << w << (*this)(0, Ny - 1, iz); + for (unsigned int ix = 1; ix < Nx; ++ix) { + stream << ", " << w << (*this)(ix, Ny - 1, iz); + } + stream << " ⎦ \n \n"; + } + LOGP(info, "{} \n \n", stream.str()); +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolver.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolver.h new file mode 100644 index 0000000000000..c708ea88c1fe2 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolver.h @@ -0,0 +1,488 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PoissonSolver.h +/// \brief This class provides implementation of Poisson equation +/// solver by MultiGrid Method +/// Original version of this class can be found in AliTPCPoissonSolver.h +/// +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_POISSONSOLVER_H_ +#define ALICEO2_TPC_POISSONSOLVER_H_ + +#include "TPCSpaceCharge/DataContainer3D.h" +#include "TPCSpaceCharge/PoissonSolverHelpers.h" +#include "TPCSpaceCharge/RegularGrid3D.h" +#include "TPCSpaceCharge/Vector3D.h" +#include "CommonConstants/MathConstants.h" + +namespace o2 +{ +namespace tpc +{ + +/// \class PoissonSolver +/// The PoissonSolver class represents methods to solve the poisson equation. +/// Original version with more methods can be found in AliTPCPoissonSolver. +/// Following methods are implemented: poissonSolver3D, poissonSolver3D2D, poissonSolver2D + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nr number of vertices in r direction (2^N + 1) +/// \tparam Nz number of vertices in z direction (2^M + 1) +/// \tparam Nphi number of vertices in phi direction +template <typename DataT = double, size_t Nz = 129, size_t Nr = 129, size_t Nphi = 180> +class PoissonSolver +{ + public: + using RegularGrid = RegularGrid3D<DataT, Nz, Nr, Nphi>; + using DataContainer = DataContainer3D<DataT, Nz, Nr, Nphi>; + using Vector = Vector3D<DataT>; + + /// default constructor + PoissonSolver(const RegularGrid& gridProperties) : mGrid3D{gridProperties} {}; + + /// Provides poisson solver in Cylindrical 3D (TPC geometry) + /// + /// Strategy based on parameter settings (mMgParameters)provided + /// * Cascaded multi grid with S.O.R + /// * Geometric MultiGrid + /// * Cycles: V, W, Full + /// * Relaxation: Jacobi, Weighted-Jacobi, Gauss-Seidel + /// * Grid transfer operators: Full, Half + /// * Spectral Methods (TODO) + /// + /// \param matricesV potential in 3D + /// \param matricesCharge charge density in 3D (side effect) + /// \param symmetry symmetry or not + /// + /// \pre Charge density distribution in **matricesCharge** is known and boundary values for **matricesV** are set + /// \post Numerical solution for potential distribution is calculated and stored in each rod at **matricesV** + void poissonSolver3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry); + + /// Provides poisson solver in 2D + /// + /// Based on the strategy (multi grid) + /// + /// \param matricesV potential in matrix + /// \param matricesCharge charge density in matrix (side effect + void poissonSolver2D(DataContainer& matricesV, const DataContainer& matricesCharge); + + DataT getSpacingZ() const { return mGrid3D.getSpacingX(); } + DataT getSpacingR() const { return mGrid3D.getSpacingY(); } + DataT getSpacingPhi() const { return mGrid3D.getSpacingZ(); } + + static void setConvergenceError(const DataT error) { sConvergenceError = error; } + + static DataT getConvergenceError() { return sConvergenceError; } + + /// get the number of threads used for some of the calculations + static int getNThreads() { return sNThreads; } + + /// set the number of threads used for some of the calculations + static void setNThreads(int nThreads) { sNThreads = nThreads; } + + private: + const RegularGrid& mGrid3D{}; ///< grid properties + inline static DataT sConvergenceError{1e-6}; ///< Error tolerated + static constexpr DataT INVTWOPI = 1. / o2::constants::math::TwoPI; ///< inverse of 2*pi + inline static int sNThreads{4}; ///< number of threads which are used during some of the calculations (increasing this number has no big impact) + + /// Relative error calculation: comparison with exact solution + /// + /// \param matricesCurrentV current potential (numerical solution) + /// \param prevArrayV content from matricesCurrentV from previous iteration + DataT getConvergenceError(const Vector& matricesCurrentV, Vector& prevArrayV) const; + + /// 3D - Solve Poisson's Equation in 3D by MultiGrid with constant phi slices + /// + /// NOTE: In order for this algorithm to work, the number of Nr and Nz must be a power of 2 plus one. + /// The number of Nr and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slice == Arbitrary but greater than 3 + /// + /// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ + /// + /// Algorithm for MultiGrid Full Cycle (FMG) + /// - Relax on the coarsest grid + /// - Do from coarsest to finest + /// - Interpolate potential from coarse -> fine + /// - Do V-Cycle to the current coarse level to the coarsest + /// - Stop if converged + /// + /// DeltaPhi in Radians + /// \param matricesV potential in 3D matrix \f$ V(r,\phi,z) \f$ + /// \param matricesCharge charge density in 3D matrix (side effect) \f$ - f(r,\phi,z) \f$ + /// \param symmetry symmetry (TODO for symmetry = 1) + // + /// SYMMETRY = 0 if no phi symmetries, and no phi boundary condition + /// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). + void poissonMultiGrid3D2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry); + + /// 3D - Solve Poisson's Equation in 3D in all direction by MultiGrid + /// + /// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. + /// The number of nRRow and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slices == Arbitrary but greater than 3 + /// + /// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ + /// + /// Algorithm for MultiGrid Full Cycle (FMG) + /// - Relax on the coarsest grid + /// - Do from coarsest to finest + /// - Interpolate potential from coarse -> fine + /// - Do V-Cycle to the current coarse level to the coarsest + /// - Stop if converged + /// + /// \param matricesV potential in 3D matrix + /// \param matricesCharge charge density in 3D matrix (side effect) + /// \param symmetry symmetry or not: symmetry = 0 if no phi symmetries, and no phi boundary condition. + /// symmetry = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). + void poissonMultiGrid3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry); + + /// Solve Poisson's Equation by MultiGrid Technique in 2D (assuming cylindrical symmetry) + /// + /// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. + /// So nRRow == 2**M + 1 and nZColumn == 2**N + 1. The number of nRRow and nZColumn can be different. + /// + /// \param matricesV potential in matrix + /// \param matricesCharge charge density in matrix (side effect + /// \param iPhi phi vertex + void poissonMultiGrid2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int iPhi = 0); + + /// Restrict2D + /// + /// Grid transfer operator, restrict from fine -> coarse grid + /// provide full-half weighting + /// + /// \[ \frac{1}{16}\left( \begin{array}{ccc} + /// 1 & 2 & 1 \\ + /// 2 & 4 & 2 \\ + /// 1 & 2 & 1 \end{array} \right) \] + /// + /// \param matricesCurrentCharge coarse grid (2h) + /// \param residue fine grid (h) + /// \param tnRRow number of vertices in r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param iphi phi vertex + void restrict2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int iphi) const; + + /// Restriction in 3D + /// + /// Restriction is a map from fine grid (h) to coarse grid (2h) + /// + /// In case of 3D + /// Full weighting: + /// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] + /// + /// + /// Restriction in all direction r-phi-z + /// restriction in phi only if oldPhi == 2*newPhi + /// \param matricesCurrentCharge coarser grid 2h + /// \param residue fine grid h + /// \param tnRRow number of grid in Nr (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in Nz (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param newPhiSlice number of Nphi (in phi-direction) for coarser grid + /// \param oldPhiSlice number of Nphi (in phi-direction) for finer grid + void restrict3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Restrict Boundary in 3D + /// + /// Pass boundary information to coarse grid + /// + /// \param matricesCurrentCharge coarser grid 2h + /// \param residue fine grid h + /// \param tnRRow number of grid in Nr (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in Nz (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param newPhiSlice number of Nphi (in phi-direction) for coarser grid + /// \param oldPhiSlice number of Nphi (in phi-direction) for finer grid + void restrictBoundary3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Relaxation operation for multiGrid + /// relaxation used 7 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param matricesCurrentV potential in 3D (matrices of matrix) + /// \param matricesCurrentCharge charge in 3D + /// \param tnRRow number of grid in in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param iPhi phi vertex + /// \param symmetry is the cylinder has symmetry + /// \param h2 \f$ h_{r}^{2} \f$ + /// \param tempRatioZ ration between grid size in z-direction and r-direction + /// \param coefficient1 coefficients for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficients for \f$ V_{x-1,y,z} \f$ + /// \param coefficient3 coefficients for z + /// \param coefficient4 coefficients for f(r,\phi,z) + void relax3D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int iPhi, const int symmetry, const DataT h2, const DataT tempRatioZ, + const std::array<DataT, Nr>& coefficient1, const std::array<DataT, Nr>& coefficient2, const std::array<DataT, Nr>& coefficient3, const std::array<DataT, Nr>& coefficient4) const; + + /// Relax2D + /// + /// Relaxation operation for multiGrid + /// relaxation used 5 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param matricesCurrentV potential in 3D (matrices of matrix) + /// \param matricesCurrentCharge charge in 3D + /// \param tnRRow number of vertices in r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param h2 \f$ h_{r}^{2} \f$ + /// \param tempFourth coefficient for h + /// \param tempRatio ratio between grid size in z-direction and r-direction + /// \param coefficient1 coefficient for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficient for \f$ V_{x-1,y,z} \f$ + void relax2D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT h2, const DataT tempFourth, const DataT tempRatio, + std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2); + + /// Interpolation/Prolongation in 2D + /// + /// Interpolation is a map from coarse grid (h) to fine grid (2h) + /// + /// In case of 2D + /// Full weighting: + /// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] + /// + /// + /// Restriction in all direction r-phi-z + /// \param matricesCurrentV finer grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of grid in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param iphi phi vertex + void interp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int iphi) const; + + /// Interpolation/Prolongation in 3D + /// + /// Interpolation is a map from coarse grid (h) to fine grid (2h) + /// + /// In case of 3D + /// Full weighting: + /// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] + /// + /// + /// Restriction in all direction r-phi-z + /// restriction in phi only if oldPhi == 2*newPhi + /// \param matricesCurrentV finer grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of vertices in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of vertices in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param newPhiSlice number of vertices in phi-direction for coarser grid + /// \param oldPhiSlice number of vertices in phi-direction for finer grid + void interp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Prolongation with Addition for 3D + /// + /// Interpolation with addition from coarse level (2h) --> fine level (h) + /// + /// Interpolation in all direction r-phi-z + /// Interpolation in phi only if oldPhi == 2*newPhi + /// \param matricesCurrentV fine grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of vertices in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of vertices in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a + /// \param newPhiSlice number of vertices in phi-direction for coarser grid + /// \param oldPhiSlice number of vertices in phi-direction for finer grid + void addInterp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Prolongation with Addition for 2D + /// + /// Interpolation with addition from coarse level (2h) --> fine level (h) + /// + /// Interpolation in all direction r-phi-z + /// \param matricesCurrentV fine grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of vertices in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of vertices in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a + /// \param tnPhi phi vertices + void addInterp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int tnPhi) const; + + /// VCycle 3D2D, V Cycle 3D in multiGrid with constant Nphi + /// fine-->coarsest-->fine, propagating the residue to correct initial guess of V + /// + /// Algorithm: + /// + /// NOTE: In order for this algorithm to work, the number of Nr and Nz must be a power of 2 plus one. + /// The number of Nr and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slice == Arbitrary but greater than 3 + /// + /// DeltaPhi in Radians + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param ratioZ ratio between square of grid r and grid z (OPTION, recalculate) + /// \param ratioPhi ratio between square of grid r and grid phi (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + /// \param coefficient1 coefficient for relaxation (r direction) + /// \param coefficient2 coefficient for relaxation (r direction) + /// \param coefficient3 coefficient for relaxation (ratio r/z) + /// \param coefficient4 coefficient for relaxation (ratio for grid_r) + /// \param inverseCoefficient4 coefficient for relaxation (inverse coefficient4) + void vCycle3D2D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, const DataT ratioPhi, std::vector<Vector>& tvArrayV, + std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue, std::array<DataT, Nr>& coefficient1, std::array<DataT, Nr>& coefficient2, std::array<DataT, Nr>& coefficient3, + std::array<DataT, Nr>& coefficient4, std::array<DataT, Nr>& inverseCoefficient4) const; + + /// VCycle 3D, V Cycle in multiGrid, fine-->coarsest-->fine, propagating the residue to correct initial guess of V + /// + /// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. + /// The number of nRRow and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slice == Arbitrary but greater than 3 + /// + /// DeltaPhi in Radians + /// + /// \param symmetry symmetry or not + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param ratioZ ratio between square of grid r and grid z (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + /// \param coefficient1 coefficient for relaxation (r direction) + /// \param coefficient2 coefficient for relaxation (r direction) + /// \param coefficient3 coefficient for relaxation (ratio r/z) + /// \param coefficient4 coefficient for relaxation (ratio for grid_r) + /// \param inverseCoefficient4 coefficient for relaxation (inverse coefficient4) + void vCycle3D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, std::vector<Vector>& tvArrayV, std::vector<Vector>& tvCharge, + std::vector<Vector>& tvResidue, std::array<DataT, Nr>& coefficient1, std::array<DataT, Nr>& coefficient2, std::array<DataT, Nr>& coefficient3, + std::array<DataT, Nr>& coefficient4, std::array<DataT, Nr>& inverseCoefficient4) const; + + /// V-Cycle 2D + /// + /// Implementation non-recursive V-cycle for 2D + /// + /// Algorithms: + /// + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param gridSizeR grid size in r direction (OPTION, recalculate) + /// \param ratio ratio between square of grid r and grid z (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + void vCycle2D(const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, std::vector<Vector>& tvArrayV, + std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue); + + /// W-Cycle 2D + /// + /// Implementation non-recursive W-cycle for 2D + /// + /// Algorithms: + /// + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param gamma number of iterations at coarsest level + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param gridSizeR grid size in r direction (OPTION, recalculate) + /// \param ratio ratio between square of grid r and grid z (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + void wCycle2D(const int gridFrom, const int gridTo, const int gamma, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, + std::vector<Vector>& tvArrayV, std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue); + + /// Residue3D + /// + /// Compute residue from V(.) where V(.) is numerical potential and f(.). + /// residue used 7 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param residue residue in 3D (matrices of matrix) + /// \param matricesCurrentV potential in 3D (matrices of matrix) + /// \param matricesCurrentCharge charge in 3D + /// \param tnRRow number of vertices in the r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param tnPhi number of vertices in phi direction of TPC + /// \param symmetry if the cylinder has symmetry + /// \param ih2 \f$ 1/ h_{r}^{2} \f$ + /// \param tempRatioZ ration between grid size in z-direction and r-direction + /// \param coefficient1 coefficient for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficient for \f$ V_{x-1,y,z} \f$ + /// \param coefficient3 coefficient for z + /// \param inverseCoefficient4 inverse coefficient for f(r,\phi,z) + void residue3D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int tnPhi, const int symmetry, const DataT ih2, const DataT tempRatioZ, + const std::array<DataT, Nr>& coefficient1, const std::array<DataT, Nr>& coefficient2, const std::array<DataT, Nr>& coefficient3, const std::array<DataT, Nr>& inverseCoefficient4) const; + + /// Residue2D + /// + /// Compute residue from V(.) where V(.) is numerical potential and f(.). + /// residue used 5 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param residue potential in 2D + /// \param matricesCurrentV potential in 2D + /// \param matricesCurrentCharge charge in 2D + /// \param nRRow number of nRRow in the r direction of TPC + /// \param nZColumn number of nZColumn in z direction of TPC + /// \param ih2 \f$ h_{r}^{2} \f$ + /// \param iTempFourth coefficient for h + /// \param tempRatio ratio between grid size in z-direction and r-direction + /// \param coefficient1 coefficient for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficient for \f$ V_{x-1,y,z} \f$ + void residue2D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT ih2, const DataT inverseTempFourth, + const DataT tempRatio, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2); + + /// Boundary transfer restrict from fine -> coarse grid + /// + /// \param matricesCurrentCharge coarse grid (2h) + /// \param residue fine grid (h) + /// \param tnRRow number of vertices in the r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param tnPhi phi vertices + void restrictBoundary2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int tnPhi) const; + + // calculate coefficients + void calcCoefficients(unsigned int from, unsigned int to, const DataT h, const DataT tempRatioZ, const DataT tempRatioPhi, std::array<DataT, Nr>& coefficient1, + std::array<DataT, Nr>& coefficient2, std::array<DataT, Nr>& coefficient3, std::array<DataT, Nr>& coefficient4) const; + + // calculate coefficients for 2D poisson solver + void calcCoefficients2D(unsigned int from, unsigned int to, const DataT h, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2) const; + + /// Helper function to check if the integer is equal to a power of two + /// \param i the number + /// \return 1 if it is a power of two, else 0 + bool isPowerOfTwo(const int i) const + { + return ((i > 0) && !(i & (i - 1))); + }; +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h new file mode 100644 index 0000000000000..044a5564e519b --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h @@ -0,0 +1,88 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 O2TPCPoissonSolverHelpers.h +/// \brief This file provides all the necessary structs which are used in the poisson solver +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_POISSONSOLVERHELPERS_H_ +#define ALICEO2_TPC_POISSONSOLVERHELPERS_H_ + +#include "CommonConstants/MathConstants.h" + +namespace o2 +{ +namespace tpc +{ + +///< Enumeration of Cycles Type +enum class CycleType { + VCycle = 0, ///< V Cycle + WCycle = 1, ///< W Cycle (TODO) + FCycle = 2 ///< Full Cycle +}; + +///< Fine -> Coarse Grid transfer operator types +enum class GridTransferType { + Half = 0, ///< Half weighting + Full = 1, ///< Full weighting +}; + +///< Smoothing (Relax) operator types +enum class RelaxType { + Jacobi = 0, ///< Jacobi (5 Stencil 2D, 7 Stencil 3D_ + WeightedJacobi = 1, ///< (TODO) + GaussSeidel = 2 ///< Gauss Seidel 2D (2 Color, 5 Stencil), 3D (7 Stencil) +}; + +struct MGParameters { ///< Parameters choice for MultiGrid algorithm + inline static bool isFull3D = true; ///< TRUE: full coarsening, FALSE: semi coarsening + inline static CycleType cycleType = CycleType::FCycle; ///< cycleType follow CycleType + inline static GridTransferType gtType = GridTransferType::Full; ///< gtType grid transfer type follow GridTransferType + inline static RelaxType relaxType = RelaxType::GaussSeidel; ///< relaxType follow RelaxType + inline static int nPre = 2; ///< number of iteration for pre smoothing + inline static int nPost = 2; ///< number of iteration for post smoothing + inline static int nMGCycle = 200; ///< number of multi grid cycle (V type) + inline static int maxLoop = 7; ///< the number of tree-deep of multi grid + inline static int gamma = 1; ///< number of iteration at coarsest level !TODO SET TO REASONABLE VALUE! +}; + +template <typename DataT = double> +struct TPCParameters { + static constexpr DataT TPCZ0{249.525}; ///< nominal G1T position + static constexpr DataT IFCRADIUS{83.5}; ///< Mean Radius of the Inner Field Cage ( 82.43 min, 83.70 max) (cm) + static constexpr DataT OFCRADIUS{254.5}; ///< Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm) + static constexpr DataT ZOFFSET{0.2}; ///< Offset from CE: calculate all distortions closer to CE as if at this point + static constexpr DataT DVDE{0.0024}; ///< [cm/V] drift velocity dependency on the E field (from Magboltz for NeCO2N2 at standard environment) + static constexpr DataT EM{-1.602176487e-19 / 9.10938215e-31}; ///< charge/mass in [C/kg] + static constexpr DataT E0{8.854187817e-12}; ///< vacuum permittivity [A·s/(V·m)] + inline static DataT cathodev{-103070.0}; ///< Cathode Voltage [V] (for 400 V/cm) + inline static DataT vg1t{-3260}; ///< GEM 1 Top voltage. (setting with reduced ET1,2,4 = 3.5kV/cm) +}; + +template <typename DataT = double, size_t Nr = 129, size_t Nz = 129, size_t Nphi = 180> +struct GridProperties { + static constexpr DataT RMIN{TPCParameters<DataT>::IFCRADIUS}; ///< min radius + static constexpr DataT ZMIN{0}; ///< min z coordinate + static constexpr DataT PHIMIN{0}; ///< min phi coordinate + static constexpr DataT RMAX{TPCParameters<DataT>::OFCRADIUS}; ///< max radius + static constexpr DataT ZMAX{TPCParameters<DataT>::TPCZ0}; ///< max z coordinate + static constexpr DataT PHIMAX{static_cast<DataT>(o2::constants::math::TwoPI)}; ///< max phi coordinate + static constexpr DataT GRIDSPACINGR{(RMAX - RMIN) / (Nr - 1)}; ///< grid spacing in r direction + static constexpr DataT GRIDSPACINGZ{(ZMAX - ZMIN) / (Nz - 1)}; ///< grid spacing in z direction + static constexpr DataT GRIDSPACINGPHI{PHIMAX / Nphi}; ///< grid spacing in phi direction +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/RegularGrid3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/RegularGrid3D.h new file mode 100644 index 0000000000000..f9a5948ece0ad --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/RegularGrid3D.h @@ -0,0 +1,245 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 RegularGrid3D.h +/// \brief Definition of RegularGrid3D class +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> + +#ifndef ALICEO2_TPC_REGULARGRID3D_H_ +#define ALICEO2_TPC_REGULARGRID3D_H_ + +#include "TPCSpaceCharge/Vector.h" +#include "Rtypes.h" // for ClassDefNV + +namespace o2 +{ +namespace tpc +{ + +/// \class RegularGrid3D +/// This class implements basic properties of a regular 3D-Grid like the spacing for each dimension and min and max coordinates. + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nx number of vertices in x direction +/// \tparam Ny number of vertices in y direction +/// \tparam Nz number of vertices in z direction +template <typename DataT = double, unsigned int Nx = 129, unsigned int Ny = 129, unsigned int Nz = 180> +struct RegularGrid3D { + + public: + RegularGrid3D(const DataT xmin, const DataT ymin, const DataT zmin, const DataT spacingX, const DataT spacingY, const DataT spacingZ) : mMin{{xmin, ymin, zmin}}, mMax{{xmin + (Nx - 1) * spacingX, ymin + (Ny - 1) * spacingY, zmin + (Nz - 1) * spacingZ}}, mSpacing{{spacingX, spacingY, spacingZ}}, mInvSpacing{{static_cast<DataT>(1 / spacingX), static_cast<DataT>(1 / spacingY), static_cast<DataT>(1 / spacingZ)}} + { + initLists(); + } + + /// \param deltaX delta x index + /// \return returns the delta index (where the data is stored) for given deltaX + int getDeltaXDataIndex(const int deltaX) const { return deltaX; } + + /// \param deltaY delta y index + /// \return returns the delta index (where the data is stored) for given deltaY + int getDeltaYDataIndex(const int deltaY) const { return Nx * deltaY; } + + /// \param deltaZ delta z index + /// \return returns the delta index (where the data is stored) for given deltaZ + int getDeltaZDataIndex(const int deltaZ) const { return deltaZ * Ny * Nx; } + + // same as above + /// \param delta delta index + /// \param dim dimension of interest + /// \return returns the delta index (where the data is stored) for given delta and dim + int getDeltaDataIndex(const int delta, const int dim) const; + + // check if the specified index for given dimension lies in the grid + /// \param index query index + /// \return returns if the index lies in the grid + bool isIndexInGrid(const int index, const unsigned int dim) const { return index < 0 ? false : (index > (sNdim[dim] - 1) ? false : true); } + + /// \param dim dimension of interest + /// \return returns the number of vertices for given dimension for the grid + static constexpr size_t getN(unsigned int dim) { return sNdim[dim]; } + static constexpr size_t getNX() { return sNdim[FX]; } + static constexpr size_t getNY() { return sNdim[FY]; } + static constexpr size_t getNZ() { return sNdim[FZ]; } + + static constexpr unsigned int getDim() { return FDIM; } /// \return returns number of dimensions of the grid (3) + static constexpr unsigned int getFX() { return FX; } /// \return returns the index for dimension x (0) + static constexpr unsigned int getFY() { return FY; } /// \return returns the index for dimension y (1) + static constexpr unsigned int getFZ() { return FZ; } /// \return returns the index for dimension z (2) + + const Vector<DataT, 3>& getGridMin() const { return mMin; } /// \return returns the minimum coordinates of the grid in all dimensions + DataT getGridMinX() const { return mMin[FX]; } /// \return returns the minimum coordinate of the grid in x dimension + DataT getGridMinY() const { return mMin[FY]; } /// \return returns the minimum coordinate of the grid in y dimension + DataT getGridMinZ() const { return mMin[FZ]; } /// \return returns the minimum coordinate of the grid in z dimension + + DataT getGridMaxX() const { return mMax[FX]; } + DataT getGridMaxY() const { return mMax[FY]; } + DataT getGridMaxZ() const { return mMax[FZ]; } + + /// \return returns the inversed spacing of the grid for all dimensions + const Vector<DataT, 3>& getInvSpacing() const { return mInvSpacing; } + DataT getInvSpacingX() const { return mInvSpacing[FX]; } + DataT getInvSpacingY() const { return mInvSpacing[FY]; } + DataT getInvSpacingZ() const { return mInvSpacing[FZ]; } + + DataT getSpacingX() const { return mSpacing[FX]; } + DataT getSpacingY() const { return mSpacing[FY]; } + DataT getSpacingZ() const { return mSpacing[FZ]; } + + // clamp coordinates to the grid (not circular) + /// \param pos query position which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGrid(const DataT pos, const unsigned int dim) const; + + // clamp coordinates to the grid (not circular) + /// \param pos relative query position in grid which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGridRel(const DataT pos, const unsigned int dim) const; + + // clamp coordinates to the grid circular + /// \param pos query position which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGridCircular(DataT pos, const unsigned int dim) const; + + // clamp coordinates to the grid circular + /// \param pos relative query position in grid which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGridCircularRel(DataT pos, const unsigned int dim) const; + + void checkStability(Vector<DataT, 3>& relPos, const Vector<int, 3>& circular) const; + + /// \param vertexX in x dimension + /// \return returns the x positon for given vertex + DataT getXVertex(const size_t vertexX) const { return mXVertices[vertexX]; } + + /// \param vertexY in y dimension + /// \return returns the y positon for given vertex + DataT getYVertex(const size_t vertexY) const { return mYVertices[vertexY]; } + + /// \param vertexZ in z dimension + /// \return returns the z positon for given vertex + DataT getZVertex(const size_t vertexZ) const { return mZVertices[vertexZ]; } + + const Vector<DataT, 3>& getMaxIndices() const { return sMaxIndex; } /// get max indices for all dimensions + + DataT getMaxIndexX() const { return sMaxIndex[0]; } /// get max index in x direction + DataT getMaxIndexY() const { return sMaxIndex[1]; } /// get max index in y direction + DataT getMaxIndexZ() const { return sMaxIndex[2]; } /// get max index in z direction + + private: + static constexpr unsigned int FDIM = 3; ///< dimensions of the grid (only 3 supported) + static constexpr unsigned int FX = 0; ///< index for x coordinate + static constexpr unsigned int FY = 1; ///< index for y coordinate + static constexpr unsigned int FZ = 2; ///< index for z coordinate + const Vector<DataT, FDIM> mMin{}; ///< min vertices positions of the grid + const Vector<DataT, FDIM> mMax{}; ///< max vertices positions of the grid + const Vector<DataT, FDIM> mSpacing{}; ///< spacing of the grid + const Vector<DataT, FDIM> mInvSpacing{}; ///< inverse spacing of grid + const inline static Vector<DataT, FDIM> sMaxIndex{{Nx - 1, Ny - 1, Nz - 1}}; ///< max index which is on the grid in all dimensions + inline static Vector<int, FDIM> sNdim{{Nx, Ny, Nz}}; ///< number of vertices for each dimension + DataT mXVertices[Nx]{}; ///< positions of vertices in x direction + DataT mYVertices[Ny]{}; ///< positions of vertices in y direction + DataT mZVertices[Nz]{}; ///< positions of vertices in z direction + + void initLists(); + + ClassDefNV(RegularGrid3D, 1) +}; + +/// +/// ======================================================================================================== +/// Inline implementations of some methods +/// ======================================================================================================== +/// + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +DataT RegularGrid3D<DataT, Nx, Ny, Nz>::clampToGrid(const DataT pos, const unsigned int dim) const +{ + if (mMin[dim] < mMax[dim]) { + if (pos < mMin[dim]) { + return mMin[dim]; + } else if (pos > mMax[dim]) { + return mMax[dim]; + } + } else { + if (pos > mMin[dim]) { + return mMin[dim]; + } else if (pos < mMax[dim]) { + return mMax[dim]; + } + } + return pos; +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +DataT RegularGrid3D<DataT, Nx, Ny, Nz>::clampToGridRel(const DataT pos, const unsigned int dim) const +{ + if (pos < 0) { + return 0; + } else if (pos >= sMaxIndex[dim]) { + return sMaxIndex[dim] - 1; // -1 return second last index. otherwise two additional points have to be extrapolated for tricubic interpolation + } + return pos; +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +DataT RegularGrid3D<DataT, Nx, Ny, Nz>::clampToGridCircular(DataT pos, const unsigned int dim) const +{ + while (pos < mMin[dim]) { + pos += mMax[dim] - mMin[dim] + mSpacing[dim]; + } + while (pos >= mMax[dim] + mSpacing[dim]) { + pos -= mMax[dim] + mSpacing[dim] - mMin[dim]; + } + return pos; +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +DataT RegularGrid3D<DataT, Nx, Ny, Nz>::clampToGridCircularRel(DataT pos, const unsigned int dim) const +{ + while (pos < 0) { + pos += sNdim[dim]; + } + while (pos > sNdim[dim]) { + pos -= sNdim[dim]; + } + if (pos == sNdim[dim]) { + pos = 0; + } + return pos; +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +void RegularGrid3D<DataT, Nx, Ny, Nz>::initLists() +{ + for (size_t i = 0; i < Nx; ++i) { + mXVertices[i] = mMin[FX] + i * mSpacing[FX]; + } + for (size_t i = 0; i < Ny; ++i) { + mYVertices[i] = mMin[FY] + i * mSpacing[FY]; + } + for (size_t i = 0; i < Nz; ++i) { + mZVertices[i] = mMin[FZ] + i * mSpacing[FZ]; + } +} + +template <typename DataT, unsigned int Nx, unsigned int Ny, unsigned int Nz> +int RegularGrid3D<DataT, Nx, Ny, Nz>::getDeltaDataIndex(const int delta, const int dim) const +{ + const unsigned int offset[FDIM]{1, Nx, Ny * Nx}; + const int deltaIndex = delta * offset[dim]; + return deltaIndex; +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h new file mode 100644 index 0000000000000..4d4376b758d2b --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h @@ -0,0 +1,901 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SpaceCharge.h +/// \brief This class contains the algorithms for calculation the distortions and corrections +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_SPACECHARGE_H_ +#define ALICEO2_TPC_SPACECHARGE_H_ + +#include "TPCSpaceCharge/TriCubic.h" +#include "TPCSpaceCharge/PoissonSolver.h" +#include "TPCSpaceCharge/SpaceChargeHelpers.h" +#include "TPCSpaceCharge/RegularGrid3D.h" +#include "TPCSpaceCharge/DataContainer3D.h" + +#include "TPCBase/ParameterGas.h" +#include "Field/MagneticField.h" +#include "TGeoGlobalMagField.h" + +#include "DataFormatsTPC/Defs.h" +#include "Framework/Logger.h" + +// Root includes +#include "TF1.h" /// for numerical intergration only +#include "TH3.h" + +namespace o2 +{ +namespace tpc +{ + +/// \class SpaceCharge +/// this class provides the algorithms for calculating the global distortions and corrections from the space charge density. +/// The calculations can be done by a realistic space charge histogram as an input or by an analytical formula. +/// An example of of the usage can be found in 'macro/calculateDistortionsCorrections.C' + +/// \tparam DataT the data type which is used during the calculations +/// \tparam Nz number of vertices in z direction +/// \tparam Nr number of vertices in r direction +/// \tparam Nphi number of vertices in phi direction +template <typename DataT = double, size_t Nz = 129, size_t Nr = 129, size_t Nphi = 180> +class SpaceCharge +{ + using RegularGrid = RegularGrid3D<DataT, Nz, Nr, Nphi>; + using DataContainer = DataContainer3D<DataT, Nz, Nr, Nphi>; + using GridProp = GridProperties<DataT, Nr, Nz, Nphi>; + using TriCubic = TriCubicInterpolator<DataT, Nz, Nr, Nphi>; + + public: + /// default constructor + SpaceCharge() = default; + + /// Enumerator for setting the space-charge distortion mode + enum class SCDistortionType : int { + SCDistortionsConstant = 0, // space-charge distortions constant over time + SCDistortionsRealistic = 1 // realistic evolution of space-charge distortions over time + }; + + /// numerical integration strategys + enum class IntegrationStrategy { Trapezoidal = 0, ///< trapezoidal integration (https://en.wikipedia.org/wiki/Trapezoidal_rule). straight electron drift line assumed: z0->z1, r0->r0, phi0->phi0 + Simpson = 1, ///< simpon integration. see: https://en.wikipedia.org/wiki/Simpson%27s_rule. straight electron drift line assumed: z0->z1, r0->r0, phi0->phi0 + Root = 2, ///< Root integration. straight electron drift line assumed: z0->z1, r0->r0, phi0->phi0 + SimpsonIterative = 3 ///< simpon integration, but using an iterative method to approximate the drift path. No straight electron drift line assumed: z0->z1, r0->r1, phi0->phi1 + }; + + enum class Type { + Distortions = 0, ///< distortions + Corrections = 1 ///< corrections + }; + + enum class GlobalDistType { + Standard = 0, ///< classical method (start calculation of global distortion at each voxel in the tpc and follow electron drift to readout -slow-) + Fast = 1, ///< interpolation of global corrections (use the global corrections to apply an iterative approach to obtain the global distortions -fast-) + None = 2 ///< dont calculate global distortions + }; + + enum class GlobalDistCorrMethod { + LocalDistCorr, ///< using local dis/corr interpolator for calculation of global distortions/corrections + ElectricalField ///< using electric field for calculation of global distortions/corrections + }; + + /// step 0: set the charge density from TH3 histogram containing the space charge density + /// \param hisSCDensity3D histogram for the space charge density + void fillChargeDensityFromHisto(const TH3& hisSCDensity3D); + + /// step 0: set the charge density from TH3 histogram containing the space charge density + /// \param fInp input file containing a histogram for the space charge density + /// \param name the name of the space charge density histogram in the file + void fillChargeDensityFromFile(TFile& fInp, const char* name); + + /// \param side side of the TPC + /// \param globalDistType the algorithm which is used to calculate the global distortions + /// \param globalDistCorrMethod the setting if local distortions/corrections or the electrical field will be used for the calculation of the global distortions/corrections + void calculateDistortionsCorrections(const o2::tpc::Side side); + + /// step 0: this function fills the internal storage for the charge density using an analytical formula + /// \param formulaStruct struct containing a method to evaluate the density + void setChargeDensityFromFormula(const AnalyticalFields<DataT>& formulaStruct); + + /// step 0: this function fills the boundary of the potential using an analytical formula. The boundary is used in the PoissonSolver. + /// \param formulaStruct struct containing a method to evaluate the potential + void setPotentialBoundaryFromFormula(const AnalyticalFields<DataT>& formulaStruct); + + /// step 0: this function fills the potential using an analytical formula + /// \param formulaStruct struct containing a method to evaluate the potential + void setPotentialFromFormula(const AnalyticalFields<DataT>& formulaStruct); + + /// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential + /// \param side side of the TPC + /// \param maxIteration maximum number of iterations used in the poisson solver + /// \param stoppingConvergence stopping criterion used in the poisson solver + /// \param symmetry use symmetry or not in the poisson solver + void poissonSolver(const Side side, const int maxIteration = 300, const DataT stoppingConvergence = 1e-6, const int symmetry = 0); + + /// step 2: calculate numerically the electric field from the potential + /// \param side side of the TPC + void calcEField(const Side side); + + /// step 2a: set the electric field from an analytical formula + /// \param formulaStruct struct containing a method to evaluate the electric fields + void setEFieldFromFormula(const AnalyticalFields<DataT>& formulaStruct); + + /// step 3: calculate the local distortions and corrections with an electric field + /// \param type calculate local corrections or local distortions: type = o2::tpc::SpaceCharge<>::Type::Distortions or o2::tpc::SpaceCharge<>::Type::Corrections + /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi (analytical formula or by TriCubic interpolator) + template <typename ElectricFields = AnalyticalFields<DataT>> + void calcLocalDistortionsCorrections(const Type type, const ElectricFields& formulaStruct); + + /// step 4: calculate global corrections by using the electric field or the local corrections + /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi or the local corrections + template <typename Fields = AnalyticalFields<DataT>> + void calcGlobalCorrections(const Fields& formulaStruct); + + /// step 5: calculate global distortions by using the electric field or the local distortions (SLOW) + /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi or the local distortions + template <typename Fields = AnalyticalFields<DataT>> + void calcGlobalDistortions(const Fields& formulaStruct); + + void init(); + + /// step 5: calculate global distortions using the global corrections (FAST) + /// \param globCorr interpolator for global corrections + /// \param maxIter maximum iterations per global distortion + /// \param approachZ when the difference between the desired z coordinate and the position of the global correction is deltaZ, approach the desired z coordinate by deltaZ * \p approachZ. + /// \param approachR when the difference between the desired r coordinate and the position of the global correction is deltaR, approach the desired r coordinate by deltaR * \p approachR. + /// \param approachPhi when the difference between the desired phi coordinate and the position of the global correction is deltaPhi, approach the desired phi coordinate by deltaPhi * \p approachPhi. + /// \param diffCorr if the absolute differences from the interpolated values for the global corrections from the last iteration compared to the current iteration is smaller than this value, set converged to true for current global distortion + void calcGlobalDistWithGlobalCorrIterative(const DistCorrInterpolator<DataT, Nz, Nr, Nphi>& globCorr, const int maxIter = 100, const DataT approachZ = 0.5, const DataT approachR = 0.5, const DataT approachPhi = 0.5, const DataT diffCorr = 1e-6); + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + DataT getChargeCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + DataT getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param eZ returns correction in z direction + /// \param eR returns correction in r direction + /// \param ePhi returns correction in phi direction + void getElectricFieldsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& eZ, DataT& eR, DataT& ePhi) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param lcorrZ returns local correction in z direction + /// \param lcorrR returns local correction in r direction + /// \param lcorrRPhi returns local correction in rphi direction + void getLocalCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& lcorrZ, DataT& lcorrR, DataT& lcorrRPhi) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param corrZ returns correction in z direction + /// \param corrR returns correction in r direction + /// \param corrRPhi returns correction in rphi direction + void getCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& corrZ, DataT& corrR, DataT& corrRPhi) const; + + /// get the global corrections for given coordinate + /// \param x global x coordinate + /// \param y global y coordinate + /// \param z global z coordinate + /// \param corrX returns corrections in x direction + /// \param corrY returns corrections in y direction + /// \param corrZ returns corrections in z direction + void getCorrections(const DataT x, const DataT y, const DataT z, const Side side, DataT& corrX, DataT& corrY, DataT& corrZ) const; + + /// get the global distortions for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param ldistZ returns local distortion in z direction + /// \param ldistR returns local distortion in r direction + /// \param ldistRPhi returns local distortion in rphi direction + void getLocalDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& ldistZ, DataT& ldistR, DataT& ldistRPhi) const; + + /// get the global distortions for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param distZ returns distortion in z direction + /// \param distR returns distortion in r direction + /// \param distRPhi returns distortion in rphi direction + void getDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& distZ, DataT& distR, DataT& distRPhi) const; + + /// get the global distortions for given coordinate + /// \param x global x coordinate + /// \param y global y coordinate + /// \param z global z coordinate + /// \param distX returns distortion in x direction + /// \param distY returns distortion in y direction + /// \param distZ returns distortion in z direction + void getDistortions(const DataT x, const DataT y, const DataT z, const Side side, DataT& distX, DataT& distY, DataT& distZ) const; + + /// convert x and y coordinates from cartesian to the radius in polar coordinates + static DataT getRadiusFromCartesian(const DataT x, const DataT y) { return std::sqrt(x * x + y * y); } + + /// convert x and y coordinates from cartesian to phi in polar coordinates + static DataT getPhiFromCartesian(const DataT x, const DataT y) { return std::atan2(y, x); } + + /// convert radius and phi coordinates from polar coordinates to x cartesian coordinates + static DataT getXFromPolar(const DataT r, const DataT phi) { return r * std::cos(phi); } + + /// convert radius and phi coordinates from polar coordinates to y cartesian coordinate + static DataT getYFromPolar(const DataT r, const DataT phi) { return r * std::sin(phi); } + + /// Correct electron position using correction lookup tables + /// \param point 3D coordinates of the electron + void correctElectron(GlobalPosition3D& point); + + /// Distort electron position using distortion lookup tables + /// \param point 3D coordinates of the electron + void distortElectron(GlobalPosition3D& point) const; + + /// set the distortions directly from a look up table + /// \param distdZ distortions in z direction + /// \param distdR distortions in r direction + /// \param distdRPhi distortions in rphi direction + /// \param side side of the TPC + void setDistortionLookupTables(const DataContainer& distdZ, const DataContainer& distdR, const DataContainer& distdRPhi, const Side side); + + /// set the density, potential, electric fields, local distortions/corrections, global distortions/corrections from a file. Missing objects in the file are ignored. + /// \file file containing the stored values for the density, potential, electric fields, local distortions/corrections, global distortions/corrections + /// \param side side of the TPC + void setFromFile(TFile& file, const Side side); + + /// Get grid spacing in r direction + DataT getGridSpacingR(const Side side) const { return mGrid3D[side].getSpacingY(); } + + /// Get grid spacing in z direction + DataT getGridSpacingZ(const Side side) const { return mGrid3D[side].getSpacingX(); } + + /// Get grid spacing in phi direction + DataT getGridSpacingPhi(const Side side) const { return mGrid3D[side].getSpacingZ(); } + + /// Get constant electric field + static constexpr DataT getEzField(const Side side) { return getSign(side) * (TPCParameters<DataT>::cathodev - TPCParameters<DataT>::vg1t) / TPCParameters<DataT>::TPCZ0; } + + /// Get inner radius of tpc + DataT getRMin(const Side side) const { return mGrid3D[side].getGridMinY(); } + + /// Get min z position which is used during the calaculations + DataT getZMin(const Side side) const { return mGrid3D[side].getGridMinX(); } + + /// Get min phi + DataT getPhiMin(const Side side) const { return mGrid3D[side].getGridMinZ(); } + + /// Get max r + DataT getRMax(const Side side) const { return mGrid3D[side].getGridMaxY(); }; + + /// Get max z + DataT getZMax(const Side side) const { return mGrid3D[side].getGridMaxX(); } + + /// Get max phi + DataT getPhiMax(const Side side) const { return mGrid3D[side].getGridMaxZ(); } + + // get side of TPC for z coordinate TODO rewrite this + static Side getSide(const DataT z) { return ((z >= 0) ? Side::A : Side::C); } + + /// Get the grid object + const RegularGrid& getGrid3D(const Side side) const { return mGrid3D[side]; } + + /// Get struct containing interpolators for the electrical fields + /// \param side side of the TPC + NumericalFields<DataT, Nz, Nr, Nphi> getElectricFieldsInterpolator(const Side side) const; + + /// Get struct containing interpolators for local distortions dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator<DataT, Nz, Nr, Nphi> getLocalDistInterpolator(const Side side) const; + + /// Get struct containing interpolators for local corrections dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator<DataT, Nz, Nr, Nphi> getLocalCorrInterpolator(const Side side) const; + + /// Get struct containing interpolators for global distortions dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator<DataT, Nz, Nr, Nphi> getGlobalDistInterpolator(const Side side) const; + + /// Get struct containing interpolators for global corrections dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator<DataT, Nz, Nr, Nphi> getGlobalCorrInterpolator(const Side side) const; + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local distortion dR for given vertex + DataT getLocalDistR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalDistdR[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local distortion dZ for given vertex + DataT getLocalDistZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalDistdZ[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local distortion dRPhi for given vertex + DataT getLocalDistRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalDistdRPhi[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local correction dR for given vertex + DataT getLocalCorrR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalCorrdR[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local correction dZ for given vertex + DataT getLocalCorrZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalCorrdZ[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local correction dRPhi for given vertex + DataT getLocalCorrRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalCorrdRPhi[side](iz, ir, iphi); } + + /// Get global distortion dR for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalDistR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalDistdR[side](iz, ir, iphi); } + + /// Get global distortion dZ for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalDistZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalDistdZ[side](iz, ir, iphi); } + + /// Get global distortion dRPhi for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalDistRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalDistdRPhi[side](iz, ir, iphi); } + + /// Get global correction dR for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalCorrR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalCorrdR[side](iz, ir, iphi); } + + /// Get global correction dZ for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalCorrZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalCorrdZ[side](iz, ir, iphi); } + + /// Get global correction dRPhi for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalCorrRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalCorrdRPhi[side](iz, ir, iphi); } + + /// Get global electric Field Er for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getEr(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mElectricFieldEr[side](iz, ir, iphi); } + + /// Get global electric Field Ez for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getEz(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mElectricFieldEz[side](iz, ir, iphi); } + + /// Get global electric Field Ephi for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getEphi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mElectricFieldEphi[side](iz, ir, iphi); } + + /// Get density for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getDensity(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mDensity[side](iz, ir, iphi); } + + /// Get potential for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getPotential(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mPotential[side](iz, ir, iphi); } + + /// Get the step width which is used for the calculation of the correction/distortions in units of the z-bin + static int getStepWidth() { return 1 / sSteps; } + + /// Get phi vertex position for index in phi direction + /// \param indexPhi index in phi direction + DataT getPhiVertex(const size_t indexPhi, const Side side) const { return mGrid3D[side].getZVertex(indexPhi); } + + /// Get r vertex position for index in r direction + /// \param indexR index in r direction + DataT getRVertex(const size_t indexR, const Side side) const { return mGrid3D[side].getYVertex(indexR); } + + /// Get z vertex position for index in z direction + /// \param indexZ index in z direction + DataT getZVertex(const size_t indexZ, const Side side) const { return mGrid3D[side].getXVertex(indexZ); } + + /// \param omegaTau \omega \tau value + /// \param t1 value for t1 see: ??? + /// \param t2 value for t2 see: ??? + void setOmegaTauT1T2(const DataT omegaTau, const DataT t1, const DataT t2) + { + const DataT wt0 = t2 * omegaTau; + mC0 = 1 / (1 + wt0 * wt0); + const DataT wt1 = t1 * omegaTau; + mC1 = wt1 / (1 + wt1 * wt1); + }; + + /// \param c0 coefficient C0 (compare Jim Thomas's notes for definitions) + /// \param c1 coefficient C1 (compare Jim Thomas's notes for definitions) + void setC0C1(const DataT c0, const DataT c1) + { + mC0 = c0; + mC1 = c1; + } + + /// set number of steps used for calculation of distortions/corrections per z bin + /// \param nSteps number of steps per z bin + static void setNStep(const int nSteps) { sSteps = nSteps; } + + static int getNStep() { return sSteps; } + + /// get the number of threads used for some of the calculations + static int getNThreads() { return sNThreads; } + + /// set the number of threads used for some of the calculations + static void setNThreads(const int nThreads) + { + sNThreads = nThreads; + o2::tpc::TriCubicInterpolator<DataT, Nz, Nr, Nphi>::setNThreads(nThreads); + } + + /// set which kind of numerical integration is used for calcution of the integrals int Er/Ez dz, int Ephi/Ez dz, int Ez dz + /// \param strategy numerical integration strategy. see enum IntegrationStrategy for the different types + static void setNumericalIntegrationStrategy(const IntegrationStrategy strategy) { sNumericalIntegrationStrategy = strategy; } + static IntegrationStrategy getNumericalIntegrationStrategy() { return sNumericalIntegrationStrategy; } + + static void setGlobalDistType(const GlobalDistType globalDistType) { sGlobalDistType = globalDistType; } + static GlobalDistType getGlobalDistType() { return sGlobalDistType; } + + static void setGlobalDistCorrMethod(const GlobalDistCorrMethod globalDistCorrMethod) { sGlobalDistCorrCalcMethod = globalDistCorrMethod; } + static GlobalDistCorrMethod getGlobalDistCorrMethod() { return sGlobalDistCorrCalcMethod; } + + static void setSimpsonNIteratives(const int nIter) { sSimpsonNIteratives = nIter; } + static int getSimpsonNIteratives() { return sSimpsonNIteratives; } + + /// Set the space-charge distortions model + /// \param distortionType distortion type (constant or realistic) + static void setSCDistortionType(SCDistortionType distortionType) { sSCDistortionType = distortionType; } + /// Get the space-charge distortions model + static SCDistortionType getSCDistortionType() { return sSCDistortionType; } + + void setUseInitialSCDensity(const bool useInitialSCDensity) { mUseInitialSCDensity = useInitialSCDensity; } + + /// write electric fields to root file + /// \param outf output file where the electrical fields will be written to + /// \side side of the TPC + int dumpElectricFields(TFile& outf, const Side side) const; + + /// set electric field from root file + /// \param inpf input file where the electrical fields are stored + /// \side side of the TPC + void setElectricFieldsFromFile(TFile& inpf, const Side side); + + /// write potential to root file + /// \param outf output file where the potential will be written to + /// \side side of the TPC + int dumpPotential(TFile& outf, const Side side) const { return mPotential[side].writeToFile(outf, Form("potential_side%s", getSideName(side).data())); } + + /// set potential from root file + /// \param inpf input file where the potential is stored + /// \side side of the TPC + void setPotentialFromFile(TFile& inpf, const Side side) { mPotential[side].initFromFile(inpf, Form("potential_side%s", getSideName(side).data())); } + + /// write potential to root file + /// \param outf output file where the charge density will be written to + /// \side side of the TPC + int dumpDensity(TFile& outf, const Side side) const { return mDensity[side].writeToFile(outf, Form("density_side%s", getSideName(side).data())); } + + /// set potential from root file + /// \param inpf input file where the charge density is stored + /// \side side of the TPC + void setDensityFromFile(TFile& inpf, const Side side) { mDensity[side].initFromFile(inpf, Form("density_side%s", getSideName(side).data())); } + + /// write global distortions to root file + /// \param outf output file where the global distortions will be written to + /// \side side of the TPC + int dumpGlobalDistortions(TFile& outf, const Side side) const; + + /// set global distortions from root file + /// \param inpf input file where the global distortions are stored + /// \side side of the TPC + void setGlobalDistortionsFromFile(TFile& inpf, const Side side); + + /// write global corrections to root file + /// \param outf output file where the global corrections will be written to + /// \side side of the TPC + int dumpGlobalCorrections(TFile& outf, const Side side) const; + + /// set global corrections from root file + /// \param inpf input file where the global corrections are stored + /// \side side of the TPC + void setGlobalCorrectionsFromFile(TFile& inpf, const Side side); + + /// write local corrections to root file + /// \param outf output file where the local corrections will be written to + /// \side side of the TPC + int dumpLocalCorrections(TFile& outf, const Side side) const; + + /// set local corrections from root file + /// \param inpf input file where the local corrections are stored + /// \side side of the TPC + void setLocalCorrectionsFromFile(TFile& inpf, const Side side); + + /// write local distortions to root file + /// \param outf output file where the local distortions will be written to + /// \side side of the TPC + int dumpLocalDistortions(TFile& outf, const Side side) const; + + /// set local distortions from root file + /// \param inpf input file where the local distortions are stored + /// \side side of the TPC + void setLocalDistortionsFromFile(TFile& inpf, const Side side); + + /// set z coordinate between min z max z + /// \param posZ z position which will be regulated if needed + DataT regulateZ(const DataT posZ, const Side side) const { return mGrid3D[side].clampToGrid(posZ, 0); } + + /// set r coordinate between 'RMIN - 4 * GRIDSPACINGR' and 'RMAX + 2 * GRIDSPACINGR'. the r coordinate is not clamped to RMIN and RMAX to ensure correct interpolation at the borders of the grid. + DataT regulateR(const DataT posR, const Side side) const; + + /// set phi coordinate between min phi max phi + DataT regulatePhi(const DataT posPhi, const Side side) const { return mGrid3D[side].clampToGridCircular(posPhi, 2); } + + private: + using ASolv = o2::tpc::PoissonSolver<DataT, Nz, Nr, Nphi>; + + inline static int sNThreads{omp_get_max_threads()}; ///< number of threads which are used during the calculations + + inline static IntegrationStrategy sNumericalIntegrationStrategy{IntegrationStrategy::SimpsonIterative}; ///< numerical integration strategy of integration of the E-Field: 0: trapezoidal, 1: Simpson, 2: Root (only for analytical formula case) + inline static int sSimpsonNIteratives{3}; ///< number of iterations which are performed in the iterative simpson calculation of distortions/corrections + inline static int sSteps{1}; ///< during the calculation of the corrections/distortions it is assumed that the electron drifts on a line from deltaZ = z0 -> z1. The value sets the deltaZ width: 1: deltaZ=zBin/1, 5: deltaZ=zBin/5 + inline static GlobalDistType sGlobalDistType{GlobalDistType::Fast}; ///< setting for global distortions: 0: standard method, 1: interpolation of global corrections + inline static GlobalDistCorrMethod sGlobalDistCorrCalcMethod{GlobalDistCorrMethod::LocalDistCorr}; ///< setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator + inline static SCDistortionType sSCDistortionType{SCDistortionType::SCDistortionsConstant}; ///< Type of space-charge distortions + + DataT mC0 = 0; ///< coefficient C0 (compare Jim Thomas's notes for definitions) + DataT mC1 = 0; ///< coefficient C1 (compare Jim Thomas's notes for definitions) + + static constexpr int FNSIDES = SIDES; ///< number of sides of the TPC + bool mIsEfieldSet[FNSIDES]{}; ///< flag if E-fields are set + bool mIsLocalCorrSet[FNSIDES]{}; ///< flag if local corrections are set + bool mIsLocalDistSet[FNSIDES]{}; ///< flag if local distortions are set + bool mIsGlobalCorrSet[FNSIDES]{}; ///< flag if global corrections are set + bool mIsGlobalDistSet[FNSIDES]{}; ///< flag if global distortions are set + bool mIsChargeSet[FNSIDES]{}; ///< flag if the charge + + bool mUseInitialSCDensity{false}; ///< Flag for the use of an initial space-charge density at the beginning of the simulation + bool mInitLookUpTables{false}; ///< Flag to indicate if lookup tables have been calculated + + const RegularGrid mGrid3D[FNSIDES]{ + {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::A) * GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}, + {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::C) * GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}}; ///< grid properties + + DataContainer mLocalDistdR[FNSIDES]{}; ///< data storage for local distortions dR + DataContainer mLocalDistdZ[FNSIDES]{}; ///< data storage for local distortions dZ + DataContainer mLocalDistdRPhi[FNSIDES]{}; ///< data storage for local distortions dRPhi + + DataContainer mLocalCorrdR[FNSIDES]{}; ///< data storage for local corrections dR + DataContainer mLocalCorrdZ[FNSIDES]{}; ///< data storage for local corrections dZ + DataContainer mLocalCorrdRPhi[FNSIDES]{}; ///< data storage for local corrections dRPhi + + DataContainer mGlobalDistdR[FNSIDES]{}; ///< data storage for global distortions dR + DataContainer mGlobalDistdZ[FNSIDES]{}; ///< data storage for global distortions dZ + DataContainer mGlobalDistdRPhi[FNSIDES]{}; ///< data storage for global distortions dRPhi + + DataContainer mGlobalCorrdR[FNSIDES]{}; ///< data storage for global corrections dR + DataContainer mGlobalCorrdZ[FNSIDES]{}; ///< data storage for global corrections dZ + DataContainer mGlobalCorrdRPhi[FNSIDES]{}; ///< data storage for global corrections dRPhi + + DataContainer mDensity[FNSIDES]{}; ///< data storage for space charge density + DataContainer mPotential[FNSIDES]{}; ///< data storage for the potential + + DataContainer mElectricFieldEr[FNSIDES]{}; ///< data storage for the electric field Er + DataContainer mElectricFieldEz[FNSIDES]{}; ///< data storage for the electric field Ez + DataContainer mElectricFieldEphi[FNSIDES]{}; ///< data storage for the electric field Ephi + + TriCubic mInterpolatorPotential[FNSIDES]{ + {mPotential[Side::A], mGrid3D[Side::A]}, + {mPotential[Side::C], mGrid3D[Side::C]}}; ///< interpolator for the potenial + + TriCubic mInterpolatorDensity[FNSIDES]{ + {mDensity[Side::A], mGrid3D[Side::A]}, + {mDensity[Side::C], mGrid3D[Side::C]}}; ///< interpolator for the charge + + DistCorrInterpolator<DataT, Nz, Nr, Nphi> mInterpolatorGlobalCorr[FNSIDES]{ + {mGlobalCorrdR[Side::A], mGlobalCorrdZ[Side::A], mGlobalCorrdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mGlobalCorrdR[Side::C], mGlobalCorrdZ[Side::C], mGlobalCorrdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the global corrections + + DistCorrInterpolator<DataT, Nz, Nr, Nphi> mInterpolatorLocalCorr[FNSIDES]{ + {mLocalCorrdR[Side::A], mLocalCorrdZ[Side::A], mLocalCorrdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mLocalCorrdR[Side::C], mLocalCorrdZ[Side::C], mLocalCorrdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the local corrections + + DistCorrInterpolator<DataT, Nz, Nr, Nphi> mInterpolatorGlobalDist[FNSIDES]{ + {mGlobalDistdR[Side::A], mGlobalDistdZ[Side::A], mGlobalDistdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mGlobalDistdR[Side::C], mGlobalDistdZ[Side::C], mGlobalDistdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the global distortions + + DistCorrInterpolator<DataT, Nz, Nr, Nphi> mInterpolatorLocalDist[FNSIDES]{ + {mLocalDistdR[Side::A], mLocalDistdZ[Side::A], mLocalDistdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mLocalDistdR[Side::C], mLocalDistdZ[Side::C], mLocalDistdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the local distortions + + NumericalFields<DataT, Nz, Nr, Nphi> mInterpolatorEField[FNSIDES]{ + {mElectricFieldEr[Side::A], mElectricFieldEz[Side::A], mElectricFieldEphi[Side::A], mGrid3D[Side::A], Side::A}, + {mElectricFieldEr[Side::C], mElectricFieldEz[Side::C], mElectricFieldEphi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the electric fields + + /// rebin the input space charge density histogram to desired binning + /// \param hOrig original histogram + TH3D rebinDensityHisto(const TH3& hOrig) const; + + static int getSign(const Side side) + { + return side == Side::C ? -1 : 1; + } + + /// get inverse spacing in z direction + DataT getInvSpacingZ(const Side side) const { return mGrid3D[side].getInvSpacingX(); } + + /// get inverse spacing in r direction + DataT getInvSpacingR(const Side side) const { return mGrid3D[side].getInvSpacingY(); } + + /// get inverse spacing in phi direction + DataT getInvSpacingPhi(const Side side) const { return mGrid3D[side].getInvSpacingZ(); } + + std::string getSideName(const Side side) const { return side == Side::A ? "A" : "C"; } + + /// calculate distortions or corrections analytical with electric fields + template <typename Fields = AnalyticalFields<DataT>> + void calcDistCorr(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& ddR, DataT& ddPhi, DataT& ddZ, const Fields& formulaStruct, const bool localDistCorr) const; + + /// calculate distortions/corrections using the formulas proposed in https://edms.cern.ch/ui/file/1108138/1/ALICE-INT-2010-016.pdf page 7 + void langevinCylindrical(DataT& ddR, DataT& ddPhi, DataT& ddZ, const DataT radius, const DataT localIntErOverEz, const DataT localIntEPhiOverEz, const DataT localIntDeltaEz) const; + + /// integrate electrical fields using root integration method + template <typename Fields = AnalyticalFields<DataT>> + void integrateEFieldsRoot(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// integrate electrical fields using trapezoidal integration method + template <typename Fields = AnalyticalFields<DataT>> + void integrateEFieldsTrapezoidal(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// integrate electrical fields using simpson integration method + template <typename Fields = AnalyticalFields<DataT>> + void integrateEFieldsSimpson(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// integrate electrical fields using simpson integration method with non straight drift of electrons + template <typename Fields = AnalyticalFields<DataT>> + void integrateEFieldsSimpsonIterative(const DataT p1r, const DataT p2r, const DataT p1phi, const DataT p2phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// calculate distortions/corrections using analytical electric fields + void processGlobalDistCorr(const DataT radius, const DataT phi, const DataT z0Tmp, const DataT z1Tmp, DataT& ddR, DataT& ddPhi, DataT& ddZ, const AnalyticalFields<DataT>& formulaStruct) const + { + calcDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct, false); + } + + /// calculate distortions/corrections using electric fields from tricubic interpolator + void processGlobalDistCorr(const DataT radius, const DataT phi, const DataT z0Tmp, const DataT z1Tmp, DataT& ddR, DataT& ddPhi, DataT& ddZ, const NumericalFields<DataT, Nz, Nr, Nphi>& formulaStruct) const + { + calcDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct, false); + } + + /// calculate distortions/corrections by interpolation of local distortions/corrections + void processGlobalDistCorr(const DataT radius, const DataT phi, const DataT z0Tmp, [[maybe_unused]] const DataT z1Tmp, DataT& ddR, DataT& ddPhi, DataT& ddZ, const DistCorrInterpolator<DataT, Nz, Nr, Nphi>& localDistCorr) const + { + ddR = localDistCorr.evaldR(z0Tmp, radius, phi); + ddZ = localDistCorr.evaldZ(z0Tmp, radius, phi); + ddPhi = localDistCorr.evaldRPhi(z0Tmp, radius, phi) / radius; + } +}; + +/// +/// ======================================================================================================== +/// Inline implementations of some methods +/// ======================================================================================================== +/// + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Fields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::integrateEFieldsRoot(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + const DataT ezField = getEzField(formulaStruct.getSide()); + TF1 fErOverEz( + "fErOverEz", [&](double* x, double* p) { (void)p; return static_cast<double>(formulaStruct.evalEr(static_cast<DataT>(x[0]), p1r, p1phi) / (formulaStruct.evalEz(static_cast<DataT>(x[0]), p1r, p1phi) + ezField)); }, p1z, p2z, 1); + localIntErOverEz = static_cast<DataT>(fErOverEz.Integral(p1z, p2z)); + + TF1 fEphiOverEz( + "fEPhiOverEz", [&](double* x, double* p) { (void)p; return static_cast<double>(formulaStruct.evalEphi(static_cast<DataT>(x[0]), p1r, p1phi) / (formulaStruct.evalEz(static_cast<DataT>(x[0]), p1r, p1phi) + ezField)); }, p1z, p2z, 1); + localIntEPhiOverEz = static_cast<DataT>(fEphiOverEz.Integral(p1z, p2z)); + + TF1 fEz( + "fEZOverEz", [&](double* x, double* p) { (void)p; return static_cast<double>(formulaStruct.evalEz(static_cast<DataT>(x[0]), p1r, p1phi) - ezField); }, p1z, p2z, 1); + localIntDeltaEz = getSign(formulaStruct.getSide()) * static_cast<DataT>(fEz.Integral(p1z, p2z)); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Fields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::integrateEFieldsTrapezoidal(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + //========trapezoidal rule see: https://en.wikipedia.org/wiki/Trapezoidal_rule ============== + const DataT fielder0 = formulaStruct.evalEr(p1z, p1r, p1phi); + const DataT fieldez0 = formulaStruct.evalEz(p1z, p1r, p1phi); + const DataT fieldephi0 = formulaStruct.evalEphi(p1z, p1r, p1phi); + + const DataT fielder1 = formulaStruct.evalEr(p2z, p1r, p1phi); + const DataT fieldez1 = formulaStruct.evalEz(p2z, p1r, p1phi); + const DataT fieldephi1 = formulaStruct.evalEphi(p2z, p1r, p1phi); + + const DataT ezField = getEzField(formulaStruct.getSide()); + const DataT eZ0 = 1. / (ezField + fieldez0); + const DataT eZ1 = 1. / (ezField + fieldez1); + + const DataT deltaX = 0.5 * (p2z - p1z); + localIntErOverEz = deltaX * (fielder0 * eZ0 + fielder1 * eZ1); + localIntEPhiOverEz = deltaX * (fieldephi0 * eZ0 + fieldephi1 * eZ1); + localIntDeltaEz = getSign(formulaStruct.getSide()) * deltaX * (fieldez0 + fieldez1); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Fields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::integrateEFieldsSimpson(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + //==========simpsons rule see: https://en.wikipedia.org/wiki/Simpson%27s_rule ============================= + const DataT fielder0 = formulaStruct.evalEr(p1z, p1r, p1phi); + const DataT fieldez0 = formulaStruct.evalEz(p1z, p1r, p1phi); + const DataT fieldephi0 = formulaStruct.evalEphi(p1z, p1r, p1phi); + + const DataT fielder1 = formulaStruct.evalEr(p2z, p1r, p1phi); + const DataT fieldez1 = formulaStruct.evalEz(p2z, p1r, p1phi); + const DataT fieldephi1 = formulaStruct.evalEphi(p2z, p1r, p1phi); + + const DataT deltaX = p2z - p1z; + const DataT ezField = getEzField(formulaStruct.getSide()); + const DataT xk2N = (p2z - static_cast<DataT>(0.5) * deltaX); + const DataT ezField2 = formulaStruct.evalEz(xk2N, p1r, p1phi); + const DataT ezField2Denominator = 1. / (ezField + ezField2); + const DataT fieldSum2ErOverEz = formulaStruct.evalEr(xk2N, p1r, p1phi) * ezField2Denominator; + const DataT fieldSum2EphiOverEz = formulaStruct.evalEphi(xk2N, p1r, p1phi) * ezField2Denominator; + + const DataT eZ0 = 1. / (ezField + fieldez0); + const DataT eZ1 = 1. / (ezField + fieldez1); + + const DataT deltaXSimpsonSixth = deltaX / 6.; + localIntErOverEz = deltaXSimpsonSixth * (4. * fieldSum2ErOverEz + fielder0 * eZ0 + fielder1 * eZ1); + localIntEPhiOverEz = deltaXSimpsonSixth * (4. * fieldSum2EphiOverEz + fieldephi0 * eZ0 + fieldephi1 * eZ1); + localIntDeltaEz = getSign(formulaStruct.getSide()) * deltaXSimpsonSixth * (4. * ezField2 + fieldez0 + fieldez1); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Fields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::integrateEFieldsSimpsonIterative(const DataT p1r, const DataT p2r, const DataT p1phi, const DataT p2phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + //==========simpsons rule see: https://en.wikipedia.org/wiki/Simpson%27s_rule ============================= + const Side side = formulaStruct.getSide(); + const DataT ezField = getEzField(side); + const DataT p2phiSave = regulatePhi(p2phi, side); + + const DataT fielder0 = formulaStruct.evalEr(p1z, p1r, p1phi); + const DataT fieldez0 = formulaStruct.evalEz(p1z, p1r, p1phi); + const DataT fieldephi0 = formulaStruct.evalEphi(p1z, p1r, p1phi); + + const DataT fielder1 = formulaStruct.evalEr(p2z, p2r, p2phiSave); + const DataT fieldez1 = formulaStruct.evalEz(p2z, p2r, p2phiSave); + const DataT fieldephi1 = formulaStruct.evalEphi(p2z, p2r, p2phiSave); + + const DataT eZ0Inv = 1. / (ezField + fieldez0); + const DataT eZ1Inv = 1. / (ezField + fieldez1); + + const DataT pHalfZ = 0.5 * (p1z + p2z); // dont needs to be regulated since p1z and p2z are already regulated + const DataT pHalfPhiSave = regulatePhi(0.5 * (p1phi + p2phi), side); // needs to be regulated since p2phi is not regulated + const DataT pHalfR = 0.5 * (p1r + p2r); + + const DataT ezField2 = formulaStruct.evalEz(pHalfZ, pHalfR, pHalfPhiSave); + const DataT eZHalfInv = 1. / (ezField + ezField2); + const DataT fieldSum2ErOverEz = formulaStruct.evalEr(pHalfZ, pHalfR, pHalfPhiSave); + const DataT fieldSum2EphiOverEz = formulaStruct.evalEphi(pHalfZ, pHalfR, pHalfPhiSave); + + const DataT deltaXSimpsonSixth = (p2z - p1z) / 6; + localIntErOverEz = deltaXSimpsonSixth * (4 * fieldSum2ErOverEz * eZHalfInv + fielder0 * eZ0Inv + fielder1 * eZ1Inv); + localIntEPhiOverEz = deltaXSimpsonSixth * (4 * fieldSum2EphiOverEz * eZHalfInv + fieldephi0 * eZ0Inv + fieldephi1 * eZ1Inv); + localIntDeltaEz = getSign(side) * deltaXSimpsonSixth * (4 * ezField2 + fieldez0 + fieldez1); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Fields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calcDistCorr(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& ddR, DataT& ddPhi, DataT& ddZ, const Fields& formulaStruct, const bool localDistCorr) const +{ + // see: https://edms.cern.ch/ui/file/1108138/1/ALICE-INT-2010-016.pdf + // needed for calculation of distortions/corrections + DataT localIntErOverEz = 0; // integral_p1z^p2z Er/Ez dz + DataT localIntEPhiOverEz = 0; // integral_p1z^p2z Ephi/Ez dz + DataT localIntDeltaEz = 0; // integral_p1z^p2z Ez dz + + // there are differentnumerical integration strategys implements. for details see each function. + switch (sNumericalIntegrationStrategy) { + case IntegrationStrategy::SimpsonIterative: // iterative simpson integration (should be more precise at least for the analytical E-Field case but takes alot more time than normal simpson integration) + for (int i = 0; i < sSimpsonNIteratives; ++i) { // TODO define a convergence criterion to abort the algorithm earlier for speed up. + const DataT tmpZ = localDistCorr ? (p2z + ddZ) : regulateZ(p2z + ddZ, formulaStruct.getSide()); // dont regulate for local distortions/corrections! (to get same result as using electric field at last/first bin) + integrateEFieldsSimpsonIterative(p1r, p1r + ddR, p1phi, p1phi + ddPhi, p1z, tmpZ, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, (p1r + 0.5 * ddR), localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); // using the mean radius '(p1r + 0.5 * ddR)' for calculation of distortions/corections + } + break; + case IntegrationStrategy::Simpson: // simpson integration + integrateEFieldsSimpson(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + break; + case IntegrationStrategy::Trapezoidal: // trapezoidal integration (fastest) + integrateEFieldsTrapezoidal(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + break; + case IntegrationStrategy::Root: // using integration implemented in ROOT (slow) + integrateEFieldsRoot(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + break; + default: + LOGP(INFO, "no matching case: Using Simpson"); + integrateEFieldsSimpson(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::langevinCylindrical(DataT& ddR, DataT& ddPhi, DataT& ddZ, const DataT radius, const DataT localIntErOverEz, const DataT localIntEPhiOverEz, const DataT localIntDeltaEz) const +{ + // calculated distortions/correction with the formula described in https://edms.cern.ch/ui/file/1108138/1/ALICE-INT-2010-016.pdf page 7. + ddR = mC0 * localIntErOverEz + mC1 * localIntEPhiOverEz; + ddPhi = (mC0 * localIntEPhiOverEz - mC1 * localIntErOverEz) / radius; + ddZ = -localIntDeltaEz * TPCParameters<DataT>::DVDE; +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceChargeHelpers.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceChargeHelpers.h new file mode 100644 index 0000000000000..6316fd524c283 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceChargeHelpers.h @@ -0,0 +1,269 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SpaceChargeHelpers.h +/// \brief This file provides all necesseray classes which are used during the calcution of the distortions and corrections +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_SPACECHARGEHELPERS_H_ +#define ALICEO2_TPC_SPACECHARGEHELPERS_H_ + +#include <functional> +#include <cmath> +#include "TPCSpaceCharge/TriCubic.h" +#include "DataFormatsTPC/Defs.h" + +namespace o2 +{ +namespace tpc +{ + +/// +/// this class contains an analytical description of the space charge, potential and the electric fields. +/// The analytical functions can be used to test the poisson solver and the caluclation of distortions/corrections. +/// +template <typename DataT = double> +class AnalyticalFields +{ + public: + AnalyticalFields(const o2::tpc::Side side = o2::tpc::Side::A) : mSide{side} {}; + + o2::tpc::Side getSide() const { return mSide; } + + void setSide(const o2::tpc::Side side) { mSide = side; } + + /// sets the parameters + void setParameters(const DataT parA, const DataT parB, const DataT parC) + { + mParA = parA; + mParB = parB; + mParC = parC; + } + + /// return parameter A + DataT getParA() const { return mParA; } + + /// return parameter B + DataT getParB() const { return mParB; } + + /// return parameter C + DataT getParC() const { return mParC; } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Er for given coordinate + DataT evalEr(DataT z, DataT r, DataT phi) const { return mErFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ez for given coordinate + DataT evalEz(DataT z, DataT r, DataT phi) const { return mEzFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ephi for given coordinate + DataT evalEphi(DataT z, DataT r, DataT phi) const { return mEphiFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the potential for given coordinate + DataT evalPotential(DataT z, DataT r, DataT phi) const { return mPotentialFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the space charge density for given coordinate + DataT evalDensity(DataT z, DataT r, DataT phi) const { return mDensityFunc(z, r, phi); } + + /// analytical potential + std::function<DataT(DataT, DataT, DataT)> mPotentialFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return -mParA * (std::pow((-r + 254.5 + 83.5), 4) - 338.0 * std::pow((-r + 254.5 + 83.5), 3) + 21250.75 * std::pow((-r + 254.5 + 83.5), 2)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); + }; + + /// analytical space charge - NOTE: if the space charge density is calculated analytical there would be a - sign in the formula (-mParA) - however since its an e- the sign is flipped (IS THIS CORRECT??? see for minus sign: AliTPCSpaceCharge3DCalc::SetPotentialBoundaryAndChargeFormula)- + std::function<DataT(DataT, DataT, DataT)> mDensityFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * ((1 / r * 16 * (-3311250 + 90995.5 * r - 570.375 * r * r + r * r * r)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) + + (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * std::pow(-r + 254.5 + 83.5, 2)) / (r * r) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) * -2 * mParB * mParB * std::cos(2 * mParB * phi) + + (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * std::pow(-r + 254.5 + 83.5, 2)) * std::cos(mParB * phi) * std::cos(mParB * phi) * 2 * mParC * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) * (2 * mParC * (zz - 125) * (zz - 125) - 1)); + }; + + /// analytical electric field Er + std::function<DataT(DataT, DataT, DataT)> mErFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * 4 * (r * r * r - 760.5 * r * r + 181991 * r - 1.3245 * std::pow(10, 7)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); + }; + + /// analytical electric field Ephi + std::function<DataT(DataT, DataT, DataT)> mEphiFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * (-r + 254.5 + 83.5) * (-r + 254.5 + 83.5)) / r * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) * -mParB * std::sin(2 * mParB * phi); + }; + + /// analytical electric field Ez + std::function<DataT(DataT, DataT, DataT)> mEzFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * (-r + 254.5 + 83.5) * (-r + 254.5 + 83.5)) * std::cos(mParB * phi) * std::cos(mParB * phi) * -2 * mParC * (zz - 125) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); + }; + + static constexpr unsigned int getID() { return ID; } + + private: + static constexpr unsigned int ID = 0; ///< needed to distinguish between the differrent classes + DataT mParA{1e-5}; ///< parameter [0] of functions + DataT mParB{0.5}; ///< parameter [1] of functions + DataT mParC{1e-4}; ///< parameter [2] of functions + o2::tpc::Side mSide{o2::tpc::Side::A}; ///< side of the TPC. Since the absolute value is taken during the calculations the choice of the side is arbitrary. +}; + +/// +/// This class gives tricubic interpolation of the electric fields and can be used to calculate the distortions/corrections. +/// The electric fields have to be calculated by the poisson solver or given by the analytical formula. +/// +template <typename DataT = double, size_t Nr = 129, size_t Nz = 129, size_t Nphi = 180> +class NumericalFields +{ + using RegularGrid = o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>; + using DataContainer = o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>; + using TriCubic = o2::tpc::TriCubicInterpolator<DataT, Nz, Nr, Nphi>; + + public: + /// constructor + /// \param dataEr container for the data of the electrical field Er + /// \param dataEz container for the data of the electrical field Ez + /// \param dataEphi container for the data of the electrical field Ephi + /// \param gridProperties properties of the grid + /// \param side side of the tpc + NumericalFields(const DataContainer& dataEr, const DataContainer& dataEz, const DataContainer& dataEphi, const RegularGrid& gridProperties, const o2::tpc::Side side) : mDataEr{dataEr}, mDataEz{dataEz}, mDataEphi{dataEphi}, mGridProperties{gridProperties}, mSide{side} {}; + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Er for given coordinate + DataT evalEr(DataT z, DataT r, DataT phi) const { return mInterpolatorEr(z, r, phi, mInterpolType); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ez for given coordinate + DataT evalEz(DataT z, DataT r, DataT phi) const { return mInterpolatorEz(z, r, phi, mInterpolType); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ephi for given coordinate + DataT evalEphi(DataT z, DataT r, DataT phi) const { return mInterpolatorEphi(z, r, phi, mInterpolType); } + + o2::tpc::Side getSide() const { return mSide; } + + static constexpr unsigned int getID() { return ID; } + + /// set which kind of TriCubic interpolation algorithm is used + void setInterpolationType(const typename TriCubic::InterpolationType type) { mInterpolType = type; } + + // \return returns which kind of TriCubic interpolation algorithm is used + typename TriCubic::InterpolationType getInterpolationType() const { return mInterpolType; } + + private: + const DataContainer& mDataEr{}; ///< adress to the data container of the grid + const DataContainer& mDataEz{}; ///< adress to the data container of the grid + const DataContainer& mDataEphi{}; ///< adress to the data container of the grid + const RegularGrid& mGridProperties{}; ///< properties of the regular grid + const o2::tpc::Side mSide{}; ///< side of the TPC + + TriCubic mInterpolatorEr{mDataEr, mGridProperties}; ///< TriCubic interpolator of the electric field Er + TriCubic mInterpolatorEz{mDataEz, mGridProperties}; ///< TriCubic interpolator of the electric field Ez + TriCubic mInterpolatorEphi{mDataEphi, mGridProperties}; ///< TriCubic interpolator of the electric field Ephi + typename TriCubic::InterpolationType mInterpolType = TriCubic::InterpolationType::Sparse; ///< type of TriCubic interpolation + static constexpr unsigned int ID = 1; ///< needed to distinguish between the different classes +}; + +/// +/// This class gives tricubic interpolation of the local distortions or corrections. +/// The the local distortions or corrections can be used to calculate the global distortions/corrections. +/// +template <typename DataT = double, size_t Nr = 129, size_t Nz = 129, size_t Nphi = 180> +class DistCorrInterpolator +{ + using RegularGrid = o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>; + using DataContainer = o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>; + using TriCubic = o2::tpc::TriCubicInterpolator<DataT, Nz, Nr, Nphi>; + + public: + /// constructor + /// \param dataDistCorrdR container for the data of the distortions dR + /// \param dataDistCorrdZ container for the data of the distortions dZ + /// \param dataDistCorrdRPhi container for the data of the distortions dPhi + /// \param gridProperties properties of the grid + /// \param side side of the tpc + DistCorrInterpolator(const DataContainer& dataDistCorrdR, const DataContainer& dataDistCorrdZ, const DataContainer& dataDistCorrdRPhi, const RegularGrid& gridProperties, const o2::tpc::Side side) : mDataDistCorrdR{dataDistCorrdR}, mDataDistCorrdZ{dataDistCorrdZ}, mDataDistCorrdRPhi{dataDistCorrdRPhi}, mGridProperties{gridProperties}, mSide{side} {}; + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the local distortion or correction dR for given coordinate + DataT evaldR(const DataT z, const DataT r, const DataT phi) const + { + return interpolatorDistCorrdR(z, r, phi, mInterpolType); + } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the local distortion or correction dZ for given coordinate + DataT evaldZ(const DataT z, const DataT r, const DataT phi) const + { + return interpolatorDistCorrdZ(z, r, phi, mInterpolType); + } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the local distortion or correction dRPhi for given coordinate + DataT evaldRPhi(const DataT z, const DataT r, const DataT phi) const + { + return interpolatorDistCorrdRPhi(z, r, phi, mInterpolType); + } + + o2::tpc::Side getSide() const { return mSide; } + + static constexpr unsigned int getID() { return ID; } + + /// set which kind of TriCubic interpolation algorithm is used + void setInterpolationType(const typename TriCubic::InterpolationType type) { mInterpolType = type; } + + /// \return returns which kind of TriCubic interpolation algorithm is used + typename TriCubic::InterpolationType getInterpolationType() const { return mInterpolType; } + + private: + const DataContainer& mDataDistCorrdR{}; ///< adress to the data container of the grid + const DataContainer& mDataDistCorrdZ{}; ///< adress to the data container of the grid + const DataContainer& mDataDistCorrdRPhi{}; ///< adress to the data container of the grid + const RegularGrid& mGridProperties{}; ///< properties of the regular grid + const o2::tpc::Side mSide{}; ///< side of the TPC. + + TriCubic interpolatorDistCorrdR{mDataDistCorrdR, mGridProperties}; ///< TriCubic interpolator of distortion or correction dR + TriCubic interpolatorDistCorrdZ{mDataDistCorrdZ, mGridProperties}; ///< TriCubic interpolator of distortion or correction dZ + TriCubic interpolatorDistCorrdRPhi{mDataDistCorrdRPhi, mGridProperties}; ///< TriCubic interpolator of distortion or correction dRPhi + typename TriCubic::InterpolationType mInterpolType = TriCubic::InterpolationType::Sparse; ///< type of TriCubic interpolation + static constexpr unsigned int ID = 2; ///< needed to distinguish between the different classes +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/TriCubic.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/TriCubic.h new file mode 100644 index 0000000000000..6cef68f2a1b55 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/TriCubic.h @@ -0,0 +1,1592 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TriCubic.h +/// \brief Definition of TriCubic class +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> + +#ifndef ALICEO2_TPC_TRICUBIC_H_ +#define ALICEO2_TPC_TRICUBIC_H_ + +#include "TPCSpaceCharge/Vector.h" +#include "TPCSpaceCharge/RegularGrid3D.h" +#include "TPCSpaceCharge/DataContainer3D.h" + +#if (defined(WITH_OPENMP) || defined(_OPENMP)) && !defined(__CLING__) +#include <omp.h> +#else +static inline int omp_get_thread_num() { return 0; } +static inline int omp_get_max_threads() { return 1; } +#endif + +namespace o2 +{ +namespace tpc +{ + +/// \class TriCubicInterpolator +/// The TriCubic class represents tricubic interpolation on a regular 3-Dim grid. +/// The algorithm which is used is based on the method developed by F. Lekien and J. Marsden and is described +/// in 'Tricubic Interpolation in Three Dimensions (2005)' http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.89.7835 +/// In this method in a first step 64 coefficients are computed by using a predefined 64*64 matrix. +/// These coefficients have to be computed for each cell in the grid, but are only computed when querying a point in a given cell. +/// The calculated coefficient is then stored for only the last cell and will be reused if the next query point lies in the same cell. +/// +/// Additionally the classical one dimensional approach of interpolating values is implemented. This algorithm is faster when interpolating only a few values inside each cube. +/// +/// periodic boundary conditions are used in phi direction. + +/// void test() +/// { +/// // define the grid: +/// // define number of vertices per dimension +/// const int zvertices = 40; +/// const int rvertices = 40; +/// const int phivertices = 40; +/// +/// // define min range +/// float zmin = 0; +/// float rmin = 0; +/// float phimin = 0; +/// +/// // define spacing between grid vertices +/// float zSpacing = 0.25; +/// float rSpacing = 0.25; +/// float phiSpacing = 2 * M_PI / phivertices; +/// +/// // create grid and datacontainer object +/// o2::tpc::RegularGrid3D<float, zvertices, rvertices, phivertices> grid3D(zmin, rmin, phimin, zSpacing, rSpacing, phiSpacing); +/// o2::tpc::DataContainer3D<float, zvertices, rvertices, phivertices> data3D; +/// +/// // fill the DataContainer3D with some values +/// for (int iz = 0; iz < zvertices; ++iz) { +/// for (int ir = 0; ir < rvertices; ++ir) { +/// for (int iphi = 0; iphi < phivertices; ++iphi) { +/// const float izPos = zSpacing * iz + zmin; +/// const float irPos = rSpacing * ir + rmin; +/// const float iphiPos = phiSpacing * iphi + phimin; +/// data3D(iz, ir, iphi) = std::sin(irPos * izPos / 10.) + std::cos(iphiPos); // some arbitrary function is used here +/// } +/// } +/// } +/// +/// // create tricubic interpolator +/// o2::tpc::TriCubicInterpolator<float, zvertices, rvertices, phivertices> interpolator(data3D, grid3D); +/// +/// // query some values +/// for (float iz = grid3D.getGridMinX(); iz < grid3D.getGridMaxX(); iz += zSpacing / 3.) { +/// for (float ir = grid3D.getGridMinY(); ir < grid3D.getGridMaxY(); ir += rSpacing / 3.) { +/// for (float iphi = grid3D.getGridMinZ() - 2 * phiSpacing; iphi < grid3D.getGridMaxZ() + 2 * phiSpacing; iphi += phiSpacing / 3.) { +/// const float zQuery = iz; +/// const float rQuery = ir; +/// const float phiQuery = iphi; +/// +/// const float interpolatedSparse = interpolator(zQuery, rQuery, phiQuery, o2::tpc::TriCubicInterpolator<float, zvertices, rvertices, phivertices>::InterpolationType::Sparse); +/// const float interpolatedDense = interpolator(zQuery, rQuery, phiQuery, o2::tpc::TriCubicInterpolator<float, zvertices, rvertices, phivertices>::InterpolationType::Dense); +/// const float trueValue = std::sin(rQuery * zQuery / 10.) + std::cos(phiQuery); +/// const float interpolatedDerivative = interpolator(zQuery, rQuery, phiQuery, 1, 1, 0); +/// const float trueDerivative = 1 / 10. * std::cos(rQuery * zQuery / 10.) - rQuery / 10. * std::sin(rQuery * zQuery / 10.) * zQuery / 10.; +/// } +/// } +/// } +/// } + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nz number of vertices in r direction +/// \tparam Nr number of vertices in phi direction +/// \tparam Nphi number of vertices in phi direction +template <typename DataT = double, size_t Nz = 129, size_t Nr = 129, size_t Nphi = 180> +class TriCubicInterpolator +{ + using Grid3D = RegularGrid3D<DataT, Nz, Nr, Nphi>; + using DataContainer = DataContainer3D<DataT, Nz, Nr, Nphi>; + using VDataT = Vc::Vector<DataT>; + + public: + /// Constructor for a tricubic interpolator + /// \param gridData struct containing access to the values of the grid + /// \param gridProperties properties of the 3D grid + TriCubicInterpolator(const DataContainer& gridData, const Grid3D& gridProperties) : mGridData{gridData}, mGridProperties{gridProperties} {}; + + enum class ExtrapolationType { + Linear = 0, ///< assume linear dependency at the boundaries of the grid + Parabola = 1, ///< assume parabolic dependency at the boundaries of the grid + }; + + enum class InterpolationType { + Sparse = 0, ///< using one dimensional method of interpolation (fast when interpolating only a few values in one cube in the grid) + Dense = 1, ///< using three dimensional method of interpolation (fast when interpolating a lot values in one cube in the grid) + }; + + // interpolate value at given coordinate + /// \param z z coordinate + /// \param r r coordinate + /// \param phi phi coordinate + /// \param type interpolation algorithm + /// \return returns the interpolated value at given coordinate + DataT operator()(const DataT z, const DataT r, const DataT phi, const InterpolationType type = InterpolationType::Sparse) const + { + if (type == InterpolationType::Sparse) { + return interpolateSparse(z, r, phi); + } else { + const Vector<DataT, FDim> coordinates{{z, r, phi}}; // vector holding the coordinates + const auto relPos = processInp(coordinates, false); // vector containing the relative position to + return interpolateDense(relPos); + } + } + + /// interpolate derivative at given coordinate + /// \param z z coordinate + /// \param r r coordinate + /// \param phi phi coordinate + /// \param derz order of derivative d/dz: derz=1 -> d/dz f(z,r,phi), derz=2 -> d^2/dz^2 f(z,r,phi), derz=3 -> d^3/dz^3 f(z,r,phi) + /// \param derphi order of derivative d/dr: derr=1 -> d/dr f(z,r,phi), derr=2 -> d^2/dr^2 f(z,r,phi), derr=3 -> d^3/dr^3 f(z,r,phi) + /// \param derphi order of derivative d/dphi: derphi=1 -> d/dphi f(z,r,phi), derphi=2 -> d^2/dphi^2 f(z,r,phi), derphi=3 -> d^3/dphi^3 f(z,r,phi) + /// derz=1 and derr=2 -> d/dz * d^2/dr^2 * f(z,r,phi) + /// \return returns the interpolated derivative at given coordinate + DataT operator()(const DataT z, const DataT r, const DataT phi, const size_t derz, const size_t derr, const size_t derphi) const + { + const Vector<DataT, FDim> coordinates{{z, r, phi}}; // vector holding the coordinates + const auto relPos = processInp(coordinates, false); + return evalDerivative(relPos[0], relPos[1], relPos[2], derz, derr, derphi); + } + + /// set which type of extrapolation is used at the grid boundaries (linear or parabol can be used with periodic phi axis and non periodic z and r axis). + /// \param extrapolationType sets type of extrapolation. See enum ExtrapolationType for different types + void setExtrapolationType(const ExtrapolationType extrapolationType) { mExtrapolationType = extrapolationType; } + + /// \return returns the extrapolation technique for missing boundary values + ExtrapolationType getExtrapolationType() const { return mExtrapolationType; } + + /// get the number of threads used for some of the calculations + static int getNThreads() { return sNThreads; } + + /// set the number of threads used for some of the calculations + static void setNThreads(int nThreads) { sNThreads = nThreads; } + + /// \return returns the number of the thread. Each thread should have an individual thread number + int getThreadNum() const { return sThreadnum; } + + /// \return performs a check if the interpolator can be used with maximum number of threads + bool checkThreadSafety() const { return sNThreads <= omp_get_max_threads(); } + + private: + // matrix containing the 'relationship between the derivatives at the corners of the elements and the coefficients' + inline static Vc::Memory<VDataT, 64> sMat[64]{ + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {9, -9, -9, 9, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 6, -6, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 6, -6, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {4, -4, -4, 4, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}, + {-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {9, -9, 0, 0, -9, 9, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 0, 0, 6, -6, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0}, + {9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0}, + {-27, 27, 27, -27, 27, -27, -27, 27, -18, -9, 18, 9, 18, 9, -18, -9, -18, 18, -9, 9, 18, -18, 9, -9, -18, 18, 18, -18, -9, 9, 9, -9, -12, -6, -6, -3, 12, 6, 6, 3, -12, -6, 12, 6, -6, -3, 6, 3, -12, 12, -6, 6, -6, 6, -3, 3, -8, -4, -4, -2, -4, -2, -2, -1}, + {18, -18, -18, 18, -18, 18, 18, -18, 9, 9, -9, -9, -9, -9, 9, 9, 12, -12, 6, -6, -12, 12, -6, 6, 12, -12, -12, 12, 6, -6, -6, 6, 6, 6, 3, 3, -6, -6, -3, -3, 6, 6, -6, -6, 3, 3, -3, -3, 8, -8, 4, -4, 4, -4, 2, -2, 4, 4, 2, 2, 2, 2, 1, 1}, + {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0}, + {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 9, -9, 9, -9, -9, 9, -9, 9, 12, -12, -12, 12, 6, -6, -6, 6, 6, 3, 6, 3, -6, -3, -6, -3, 8, 4, -8, -4, 4, 2, -4, -2, 6, -6, 6, -6, 3, -3, 3, -3, 4, 2, 4, 2, 2, 1, 2, 1}, + {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -6, 6, -6, 6, 6, -6, 6, -6, -8, 8, 8, -8, -4, 4, 4, -4, -3, -3, -3, -3, 3, 3, 3, 3, -4, -4, 4, 4, -2, -2, 2, 2, -4, 4, -4, 4, -2, 2, -2, 2, -2, -2, -2, -2, -1, -1, -1, -1}, + {2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 0, 0, 6, -6, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {4, -4, 0, 0, -4, 4, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, + {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0}, + {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 12, -12, 6, -6, -12, 12, -6, 6, 9, -9, -9, 9, 9, -9, -9, 9, 8, 4, 4, 2, -8, -4, -4, -2, 6, 3, -6, -3, 6, 3, -6, -3, 6, -6, 3, -3, 6, -6, 3, -3, 4, 2, 2, 1, 4, 2, 2, 1}, + {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -8, 8, -4, 4, 8, -8, 4, -4, -6, 6, 6, -6, -6, 6, 6, -6, -4, -4, -2, -2, 4, 4, 2, 2, -3, -3, 3, 3, -3, -3, 3, 3, -4, 4, -2, 2, -4, 4, -2, 2, -2, -2, -1, -1, -2, -2, -1, -1}, + {4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, + {-12, 12, 12, -12, 12, -12, -12, 12, -8, -4, 8, 4, 8, 4, -8, -4, -6, 6, -6, 6, 6, -6, 6, -6, -6, 6, 6, -6, -6, 6, 6, -6, -4, -2, -4, -2, 4, 2, 4, 2, -4, -2, 4, 2, -4, -2, 4, 2, -3, 3, -3, 3, -3, 3, -3, 3, -2, -1, -2, -1, -2, -1, -2, -1}, + {8, -8, -8, 8, -8, 8, 8, -8, 4, 4, -4, -4, -4, -4, 4, 4, 4, -4, 4, -4, -4, 4, -4, 4, 4, -4, -4, 4, 4, -4, -4, 4, 2, 2, 2, 2, -2, -2, -2, -2, 2, 2, -2, -2, 2, 2, -2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 1}}; ///< matrix containing the 'relationship between the derivatives at the corners of the elements and the coefficients' + + inline static Matrix<DataT, 64> sMatrixA{sMat}; ///< this matrix is used for vectorized operations with the 64*64 matrix + static constexpr unsigned int FDim = Grid3D::getDim(); ///< dimensions of the grid + static constexpr unsigned int FZ = Grid3D::getFX(); ///< index for z coordinate + static constexpr unsigned int FR = Grid3D::getFY(); ///< index for r coordinate + static constexpr unsigned int FPHI = Grid3D::getFZ(); ///< index for phi coordinate + const DataContainer& mGridData{}; ///< adress to the data container of the grid + const Grid3D& mGridProperties{}; ///< adress to the properties of the grid + inline static thread_local const size_t sThreadnum{static_cast<size_t>(omp_get_thread_num())}; ///< save for each thread the thread number to get fast access to the correct array + inline static int sNThreads{omp_get_max_threads()}; ///< number of threads the tricubic interpolator can be used with + std::unique_ptr<Vector<DataT, 64>[]> mCoefficients = std::make_unique<Vector<DataT, 64>[]>(sNThreads); ///< coefficients needed to interpolate a value + std::unique_ptr<Vector<DataT, FDim>[]> mLastInd = std::make_unique<Vector<DataT, FDim>[]>(sNThreads); ///< stores the index for the cell, where the coefficients are already evaluated (only the coefficients for the last cell are stored) + std::unique_ptr<bool[]> mInitialized = std::make_unique<bool[]>(sNThreads); ///< sets the flag if the coefficients are evaluated at least once + ExtrapolationType mExtrapolationType = ExtrapolationType::Parabola; ///< sets which type of extrapolation for missing points at boundary is used. Linear and Parabola is only supported for perdiodic phi axis and non periodic z and r axis + + // DEFINITION OF enum GridPos + //======================================================== + // r + // | 6------F---7 + // | / | / | + // | K G YR L H + // | / | / | + // | 2---B------3 | + // | | | | | + // | | 4---|---E--5 + // | C XL / D XR / + // | | I YL | J + // | | / | / + // | 0---A------1 + // |------------------------------- z + // / + // / + // / + // phi + //======================================================== + + enum class GridPos { + None = 27, + InnerVolume = 26, + Edge0 = 0, + Edge1 = 1, + Edge2 = 2, + Edge3 = 3, + Edge4 = 4, + Edge5 = 5, + Edge6 = 6, + Edge7 = 7, + LineA = 8, + LineB = 9, + LineC = 10, + LineD = 11, + LineE = 12, + LineF = 13, + LineG = 14, + LineH = 15, + LineI = 16, + LineJ = 17, + LineK = 18, + LineL = 19, + SideXRight = 20, + SideXLeft = 21, + SideYRight = 22, + SideYLeft = 23, + SideZRight = 24, + SideZLeft = 25 + }; + + void setValues(const int iz, const int ir, const int iphi, DataT cVals[64]) const; + + const Vector<DataT, 3> processInp(const Vector<DataT, 3>& coordinates, const bool sparse = false) const; + + // calculate the coefficients needed for the interpolation using the 64*64 matrix. + // this is the 'slow' part of the code and might be optimized + void calcCoefficients(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const; + + DataT interpolateDense(const Vector<DataT, 3>& pos) const; + + // interpolate value at given coordinate - this method doesnt compute and stores the coefficients and is faster when quering only a few values per cube + /// \param z z coordinate + /// \param r r coordinate + /// \param phi phi coordinate + /// \return returns the interpolated value at given coordinate + DataT interpolateSparse(const DataT z, const DataT r, const DataT phi) const; + + DataT evalDerivative(const DataT dz, const DataT dr, const DataT dphi, const size_t derz, const size_t derr, const size_t derphi) const; + + // for periodic boundary conditions + void getDataIndexCircularArray(const int index0, const int dim, int arr[]) const; + + // for non periodic boundary conditions + void getDataIndexNonCircularArray(const int index0, const int dim, int arr[]) const; + + // this helps to get circular and non circular padding indices + int getRegulatedDelta(const int index0, const int delta, const unsigned int dim, const int offs) const + { + return mGridProperties.isIndexInGrid(index0 + delta, dim) ? delta : offs; + } + + void initInterpolator(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const; + + DataT extrapolation(const DataT valk, const DataT valk1, const DataT valk2) const; + + DataT linearExtrapolation(const DataT valk, const DataT valk1) const; + + DataT parabolExtrapolation(const DataT valk, const DataT valk1, const DataT valk2) const; + + GridPos findPos(const int iz, const int ir, const int iphi) const; + + bool isInInnerVolume(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool findEdge(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool findLine(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool findSide(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool isSideRight(const int ind, const int dim) const; + + bool isSideLeft(const int ind) const; +}; + +/// +/// ======================================================================================================== +/// Inline implementations +/// ======================================================================================================== +/// + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void TriCubicInterpolator<DataT, Nz, Nr, Nphi>::initInterpolator(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const +{ + calcCoefficients(iz, ir, iphi); + + // store current cell + mInitialized[sThreadnum] = true; + mLastInd[sThreadnum][FZ] = iz; + mLastInd[sThreadnum][FR] = ir; + mLastInd[sThreadnum][FPHI] = iphi; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT TriCubicInterpolator<DataT, Nz, Nr, Nphi>::evalDerivative(const DataT dz, const DataT dr, const DataT dphi, const size_t derz, const size_t derr, const size_t derphi) const +{ + //TODO optimize this + DataT ret{}; + for (size_t i = derz; i < 4; i++) { + for (size_t j = derr; j < 4; j++) { + for (size_t k = derphi; k < 4; k++) { + + const size_t index = i + j * 4 + 16 * k; + DataT cont = mCoefficients[sThreadnum][index] * std::pow(dz, i - derz) * std::pow(dr, j - derr) * std::pow(dphi, k - derphi); + for (size_t w = 0; w < derz; w++) { + cont *= (i - w); + } + for (size_t w = 0; w < derr; w++) { + cont *= (j - w); + } + for (size_t w = 0; w < derphi; w++) { + cont *= (k - w); + } + ret += cont; + } + } + } + const DataT norm = std::pow(mGridProperties.getInvSpacingX(), derz) * std::pow(mGridProperties.getInvSpacingY(), derr) * std::pow(mGridProperties.getInvSpacingZ(), derphi); + return (ret * norm); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT TriCubicInterpolator<DataT, Nz, Nr, Nphi>::extrapolation(const DataT valk, const DataT valk1, const DataT valk2) const +{ + switch (mExtrapolationType) { + case ExtrapolationType::Linear: + default: + return linearExtrapolation(valk, valk1); + break; + case ExtrapolationType::Parabola: + return parabolExtrapolation(valk, valk1, valk2); + break; + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT TriCubicInterpolator<DataT, Nz, Nr, Nphi>::linearExtrapolation(const DataT valk, const DataT valk1) const +{ + const DataT val = 2 * valk - valk1; + return val; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT TriCubicInterpolator<DataT, Nz, Nr, Nphi>::parabolExtrapolation(const DataT valk, const DataT valk1, const DataT valk2) const +{ + const DataT val = 3 * (valk - valk1) + valk2; // legendre polynom with x0=0, x1=1, x2=2 and z=-1 + return val; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void TriCubicInterpolator<DataT, Nz, Nr, Nphi>::calcCoefficients(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const +{ + DataT cVals[64]{}; + setValues(iz, ir, iphi, cVals); + + // needed for first derivative + const Vector<DataT, 24> vecDeriv1A{ + {cVals[22], cVals[23], cVals[26], cVals[27], cVals[38], cVals[39], cVals[42], cVals[43], + cVals[25], cVals[26], cVals[29], cVals[30], cVals[41], cVals[42], cVals[45], cVals[46], + cVals[37], cVals[38], cVals[41], cVals[42], cVals[53], cVals[54], cVals[57], cVals[58]}}; + + const Vector<DataT, 24> vecDeriv1B{ + {cVals[20], cVals[21], cVals[24], cVals[25], cVals[36], cVals[37], cVals[40], cVals[41], + cVals[17], cVals[18], cVals[21], cVals[22], cVals[33], cVals[34], cVals[37], cVals[38], + cVals[5], cVals[6], cVals[9], cVals[10], cVals[21], cVals[22], cVals[25], cVals[26]}}; + + // needed for second derivative + const Vector<DataT, 24> vecDeriv2A{ + {cVals[26], cVals[27], cVals[30], cVals[31], cVals[42], cVals[43], cVals[46], cVals[47], + cVals[38], cVals[39], cVals[42], cVals[43], cVals[54], cVals[55], cVals[58], cVals[59], + cVals[41], cVals[42], cVals[45], cVals[46], cVals[57], cVals[58], cVals[61], cVals[62]}}; + + const Vector<DataT, 24> vecDeriv2B{ + {cVals[24], cVals[25], cVals[28], cVals[29], cVals[40], cVals[41], cVals[44], cVals[45], + cVals[36], cVals[37], cVals[40], cVals[41], cVals[52], cVals[53], cVals[56], cVals[57], + cVals[33], cVals[34], cVals[37], cVals[38], cVals[49], cVals[50], cVals[53], cVals[54]}}; + + const Vector<DataT, 24> vecDeriv2C{ + {cVals[18], cVals[19], cVals[22], cVals[23], cVals[34], cVals[35], cVals[38], cVals[39], + cVals[6], cVals[7], cVals[10], cVals[11], cVals[22], cVals[23], cVals[26], cVals[27], + cVals[9], cVals[10], cVals[13], cVals[14], cVals[25], cVals[26], cVals[29], cVals[30]}}; + + const Vector<DataT, 24> vecDeriv2D{ + {cVals[16], cVals[17], cVals[20], cVals[21], cVals[32], cVals[33], cVals[36], cVals[37], + cVals[4], cVals[5], cVals[8], cVals[9], cVals[20], cVals[21], cVals[24], cVals[25], + cVals[1], cVals[2], cVals[5], cVals[6], cVals[17], cVals[18], cVals[21], cVals[22]}}; + + // needed for third derivative + const Vector<DataT, 8> vecDeriv3A{{cVals[42], cVals[43], cVals[46], cVals[47], cVals[58], cVals[59], cVals[62], cVals[63]}}; + const Vector<DataT, 8> vecDeriv3B{{cVals[40], cVals[41], cVals[44], cVals[45], cVals[56], cVals[57], cVals[60], cVals[61]}}; + const Vector<DataT, 8> vecDeriv3C{{cVals[34], cVals[35], cVals[38], cVals[39], cVals[50], cVals[51], cVals[54], cVals[55]}}; + const Vector<DataT, 8> vecDeriv3D{{cVals[32], cVals[33], cVals[36], cVals[37], cVals[48], cVals[49], cVals[52], cVals[53]}}; + const Vector<DataT, 8> vecDeriv3E{{cVals[10], cVals[11], cVals[14], cVals[15], cVals[26], cVals[27], cVals[30], cVals[31]}}; + const Vector<DataT, 8> vecDeriv3F{{cVals[8], cVals[9], cVals[12], cVals[13], cVals[24], cVals[25], cVals[28], cVals[29]}}; + const Vector<DataT, 8> vecDeriv3G{{cVals[2], cVals[3], cVals[6], cVals[7], cVals[18], cVals[19], cVals[22], cVals[23]}}; + const Vector<DataT, 8> vecDeriv3H{{cVals[0], cVals[1], cVals[4], cVals[5], cVals[16], cVals[17], cVals[20], cVals[21]}}; + + // factor for first derivative + const DataT fac1{0.5}; + const Vector<DataT, 24> vfac1{{fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1}}; + + // factor for second derivative + const DataT fac2{0.25}; + const Vector<DataT, 24> vfac2{{fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2}}; + + // factor for third derivative + const DataT fac3{0.125}; + const Vector<DataT, 8> vfac3{{fac3, fac3, fac3, fac3, fac3, fac3, fac3, fac3}}; + + // compute the derivatives + const Vector<DataT, 24> vecDeriv1Res{vfac1 * (vecDeriv1A - vecDeriv1B)}; + const Vector<DataT, 24> vecDeriv2Res{vfac2 * (vecDeriv2A - vecDeriv2B - vecDeriv2C + vecDeriv2D)}; + const Vector<DataT, 8> vecDeriv3Res{vfac3 * (vecDeriv3A - vecDeriv3B - vecDeriv3C + vecDeriv3D - vecDeriv3E + vecDeriv3F + vecDeriv3G - vecDeriv3H)}; + + const Vector<DataT, 64> matrixPar{ + {cVals[21], cVals[22], cVals[25], cVals[26], cVals[37], cVals[38], cVals[41], cVals[42], + vecDeriv1Res[0], vecDeriv1Res[1], vecDeriv1Res[2], vecDeriv1Res[3], vecDeriv1Res[4], vecDeriv1Res[5], vecDeriv1Res[6], vecDeriv1Res[7], vecDeriv1Res[8], vecDeriv1Res[9], vecDeriv1Res[10], + vecDeriv1Res[11], vecDeriv1Res[12], vecDeriv1Res[13], vecDeriv1Res[14], vecDeriv1Res[15], vecDeriv1Res[16], vecDeriv1Res[17], vecDeriv1Res[18], vecDeriv1Res[19], vecDeriv1Res[20], vecDeriv1Res[21], + vecDeriv1Res[22], vecDeriv1Res[23], vecDeriv2Res[0], vecDeriv2Res[1], vecDeriv2Res[2], vecDeriv2Res[3], vecDeriv2Res[4], vecDeriv2Res[5], vecDeriv2Res[6], vecDeriv2Res[7], vecDeriv2Res[8], vecDeriv2Res[9], + vecDeriv2Res[10], vecDeriv2Res[11], vecDeriv2Res[12], vecDeriv2Res[13], vecDeriv2Res[14], vecDeriv2Res[15], vecDeriv2Res[16], vecDeriv2Res[17], vecDeriv2Res[18], vecDeriv2Res[19], vecDeriv2Res[20], + vecDeriv2Res[21], vecDeriv2Res[22], vecDeriv2Res[23], vecDeriv3Res[0], vecDeriv3Res[1], vecDeriv3Res[2], vecDeriv3Res[3], vecDeriv3Res[4], vecDeriv3Res[5], vecDeriv3Res[6], vecDeriv3Res[7]}}; + + // calc coeffiecients + mCoefficients[sThreadnum] = sMatrixA * matrixPar; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT TriCubicInterpolator<DataT, Nz, Nr, Nphi>::interpolateDense(const Vector<DataT, 3>& pos) const +{ + // the formula for evaluating the interpolation is as follows: + // f(z,r,phi) = \sum_{i,j,k=0}^3 a_{ijk} * z^{i} * r^{j} * phi^{k} + // a_{ijk} is stored in mCoefficients[] and are computed in the function calcCoefficientsX() + + const Vector<DataT, FDim> vals0{{1, 1, 1}}; // z^0, r^0, phi^0 + const Vector<DataT, FDim> vals2{pos * pos}; // z^2, r^2, phi^2 + const Vector<DataT, FDim> vals3{vals2 * pos}; // z^3, r^3, phi^3 + + const DataT valX[4]{vals0[FZ], pos[FZ], vals2[FZ], vals3[FZ]}; + const Vector<DataT, 64> vecValX{ + {valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], + valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], + valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], + valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3]}}; + + const DataT valY[4]{vals0[FR], pos[FR], vals2[FR], vals3[FR]}; + const Vector<DataT, 64> vecValY{ + {valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3], + valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3], + valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3], + valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3]}}; + + const DataT valZ[4]{vals0[FPHI], pos[FPHI], vals2[FPHI], vals3[FPHI]}; + const Vector<DataT, 64> vecValZ{ + {valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], + valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], + valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], + valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3]}}; + + // result = f(z,r,phi) = \sum_{i,j,k=0}^3 a_{ijk} * z^{i} * r^{j} * phi^{k} + const DataT result = sum(mCoefficients[sThreadnum] * vecValX * vecValY * vecValZ); + return result; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT TriCubicInterpolator<DataT, Nz, Nr, Nphi>::interpolateSparse(const DataT z, const DataT r, const DataT phi) const +{ + const Vector<DataT, FDim> coordinates{{z, r, phi}}; // vector holding the coordinates + const auto posRel = processInp(coordinates, true); + + DataT cVals[64]{}; + setValues(mLastInd[sThreadnum][FZ], mLastInd[sThreadnum][FR], mLastInd[sThreadnum][FPHI], cVals); + + const Vector<DataT, FDim> vals0{posRel}; + const Vector<DataT, FDim> vals1{vals0 * vals0}; + const Vector<DataT, FDim> vals2{vals0 * vals1}; + + const int nPoints = 4; + const Vector<DataT, nPoints> vecValX{{1, vals0[FZ], vals1[FZ], vals2[FZ]}}; + const Vector<DataT, nPoints> vecValY{{1, vals0[FR], vals1[FR], vals2[FR]}}; + const Vector<DataT, nPoints> vecValZ{{1, vals0[FPHI], vals1[FPHI], vals2[FPHI]}}; + + const Vc::Memory<VDataT, nPoints> matrA[nPoints]{ + {0, -0.5, 1, -0.5}, + {1, 0, -2.5, 1.5}, + {0, 0.5, 2., -1.5}, + {0, 0, -0.5, 0.5}}; + + const Matrix<DataT, nPoints> matrixA{matrA}; + const Vector<DataT, nPoints> vecValXMult{matrixA * vecValX}; + const Vector<DataT, nPoints> vecValYMult{matrixA * vecValY}; + const Vector<DataT, nPoints> vecValZMult{matrixA * vecValZ}; + + DataT result{}; + int ind = 0; + for (int slice = 0; slice < nPoints; ++slice) { + const Vector<DataT, nPoints> vecA{vecValZMult[slice] * vecValYMult}; + for (int row = 0; row < nPoints; ++row) { + const Vector<DataT, nPoints> vecD{{cVals[ind], cVals[++ind], cVals[++ind], cVals[++ind]}}; + ++ind; + result += sum(vecA[row] * vecValXMult * vecD); + } + } + return result; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +const Vector<DataT, 3> TriCubicInterpolator<DataT, Nz, Nr, Nphi>::processInp(const Vector<DataT, 3>& coordinates, const bool sparse) const +{ + Vector<DataT, FDim> posRel{(coordinates - mGridProperties.getGridMin()) * mGridProperties.getInvSpacing()}; // needed for the grid index + posRel[FPHI] = mGridProperties.clampToGridCircularRel(posRel[FPHI], FPHI); + const Vector<DataT, FDim> posRelN{posRel}; + posRel[FZ] = mGridProperties.clampToGridRel(posRel[FZ], FZ); + posRel[FR] = mGridProperties.clampToGridRel(posRel[FR], FR); + + const Vector<DataT, FDim> index{floor(posRel)}; + + if (!sparse && (!mInitialized[sThreadnum] || !(mLastInd[sThreadnum] == index))) { + initInterpolator(index[FZ], index[FR], index[FPHI]); + } else if (sparse) { + mLastInd[sThreadnum][FZ] = index[FZ]; + mLastInd[sThreadnum][FR] = index[FR]; + mLastInd[sThreadnum][FPHI] = index[FPHI]; + mInitialized[sThreadnum] = false; + } + return posRelN - index; +} + +// for perdiodic boundary condition +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void TriCubicInterpolator<DataT, Nz, Nr, Nphi>::getDataIndexCircularArray(const int index0, const int dim, int arr[]) const +{ + const int delta_min1 = getRegulatedDelta(index0, -1, dim, mGridProperties.getN(dim) - 1); + const int delta_plus1 = getRegulatedDelta(index0, +1, dim, 1 - mGridProperties.getN(dim)); + const int delta_plus2 = getRegulatedDelta(index0, +2, dim, 2 - mGridProperties.getN(dim)); + + arr[0] = mGridProperties.getDeltaDataIndex(delta_min1, dim); + arr[1] = mGridProperties.getDeltaDataIndex(delta_plus1, dim); + arr[2] = mGridProperties.getDeltaDataIndex(delta_plus2, dim); +} + +// for non perdiodic boundary condition +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void TriCubicInterpolator<DataT, Nz, Nr, Nphi>::getDataIndexNonCircularArray(const int index0, const int dim, int arr[]) const +{ + const int delta_min1 = getRegulatedDelta(index0, -1, dim, 0); + const int delta_plus1 = getRegulatedDelta(index0, +1, dim, 0); + const int delta_plus2 = getRegulatedDelta(index0, +2, dim, delta_plus1); + + arr[0] = mGridProperties.getDeltaDataIndex(delta_min1, dim); + arr[1] = mGridProperties.getDeltaDataIndex(delta_plus1, dim); + arr[2] = mGridProperties.getDeltaDataIndex(delta_plus2, dim); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +typename TriCubicInterpolator<DataT, Nz, Nr, Nphi>::GridPos TriCubicInterpolator<DataT, Nz, Nr, Nphi>::findPos(const int iz, const int ir, const int iphi) const +{ + GridPos pos = GridPos::None; + if (isInInnerVolume(iz, ir, iphi, pos)) { + return pos; + } + + if (findEdge(iz, ir, iphi, pos)) { + return pos; + } + + if (findLine(iz, ir, iphi, pos)) { + return pos; + } + + if (findSide(iz, ir, iphi, pos)) { + return pos; + } + return GridPos::None; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +bool TriCubicInterpolator<DataT, Nz, Nr, Nphi>::findEdge(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + const int iR = 2; + if (iz == 0 && ir == 0) { + if (iphi == 0) { + posType = GridPos::Edge0; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge4; + return true; + } + } else if (iz == Nz - iR && ir == 0) { + if (iphi == 0) { + posType = GridPos::Edge1; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge5; + return true; + } + } else if (iz == 0 && ir == Nr - iR) { + if (iphi == 0) { + posType = GridPos::Edge2; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge6; + return true; + } + } else if (iz == Nz - iR && ir == Nr - iR) { + if (iphi == 0) { + posType = GridPos::Edge3; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge7; + return true; + } + } + return false; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +bool TriCubicInterpolator<DataT, Nz, Nr, Nphi>::findLine(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + const int iR = 2; + //check line + if (ir == 0) { + if (iphi == 0) { + posType = GridPos::LineA; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineE; + return true; + } + if (iz == 0) { + posType = GridPos::LineI; + return true; + } else if (iz == Nz - iR) { + posType = GridPos::LineJ; + return true; + } + } else if (ir == Nr - iR) { + if (iphi == 0) { + posType = GridPos::LineB; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineF; + return true; + } + if (iz == 0) { + posType = GridPos::LineK; + return true; + } else if (iz == Nz - iR) { + posType = GridPos::LineL; + return true; + } + } else if (iz == 0) { + if (iphi == 0) { + posType = GridPos::LineC; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineG; + return true; + } + } else if (iz == Nz - iR) { + if (iphi == 0) { + posType = GridPos::LineD; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineH; + return true; + } + } + return false; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +bool TriCubicInterpolator<DataT, Nz, Nr, Nphi>::findSide(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + if (isSideRight(iz, FZ)) { + posType = GridPos::SideXRight; + return true; + } else if (isSideLeft(iz)) { + posType = GridPos::SideXLeft; + return true; + } + if (isSideRight(ir, FR)) { + posType = GridPos::SideYRight; + return true; + } else if (isSideLeft(ir)) { + posType = GridPos::SideYLeft; + return true; + } + if (isSideRight(iphi, FPHI)) { + posType = GridPos::SideZRight; + return true; + } else if (isSideLeft(iphi)) { + posType = GridPos::SideZLeft; + return true; + } + return false; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +bool TriCubicInterpolator<DataT, Nz, Nr, Nphi>::isInInnerVolume(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + if (iz >= 1 && iz < static_cast<int>(Nz - 2) && ir >= 1 && ir < static_cast<int>(Nr - 2) && iphi >= 1 && iphi < static_cast<int>(Nphi - 2)) { + posType = GridPos::InnerVolume; + return true; + } + return false; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +bool TriCubicInterpolator<DataT, Nz, Nr, Nphi>::isSideRight(const int ind, const int dim) const +{ + if (ind == static_cast<int>(mGridProperties.getN(dim) - 2)) { + return true; + } + return false; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +bool TriCubicInterpolator<DataT, Nz, Nr, Nphi>::isSideLeft(const int ind) const +{ + if (ind == 0) { + return true; + } + return false; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void TriCubicInterpolator<DataT, Nz, Nr, Nphi>::setValues(const int iz, const int ir, const int iphi, DataT cVals[64]) const +{ + const GridPos location = findPos(iz, ir, iphi); + const int ii_x_y_z = mGridData.getDataIndex(iz, ir, iphi); + cVals[21] = mGridData[ii_x_y_z]; + + int deltaZ[3]{mGridProperties.getDeltaDataIndex(-1, 0), mGridProperties.getDeltaDataIndex(1, 0), mGridProperties.getDeltaDataIndex(2, 0)}; + int deltaR[3]{mGridProperties.getDeltaDataIndex(-1, 1), mGridProperties.getDeltaDataIndex(1, 1), mGridProperties.getDeltaDataIndex(2, 1)}; + int deltaPhi[3]{}; + getDataIndexCircularArray(iphi, FPHI, deltaPhi); + + const int i0 = 0; + const int i1 = 1; + const int i2 = 2; + + switch (location) { + case GridPos::InnerVolume: + case GridPos::SideZRight: + case GridPos::SideZLeft: + default: { + const int ind[4][4][4]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0], ind[0][0][2] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0], ind[0][1][2] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0], ind[0][2][2] - deltaZ[i0]}, + {ind[0][2][0] - deltaR[i0], ind[0][3][0] - deltaZ[i0], ind[0][3][1] - deltaZ[i0], ind[0][3][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0], ind[1][0][2] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0], ind[1][1][2] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0], ind[1][2][2] - deltaZ[i0]}, + {ind[1][2][0] - deltaR[i0], ind[1][3][0] - deltaZ[i0], ind[1][3][1] - deltaZ[i0], ind[1][3][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0], ind[2][0][2] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0], ind[2][1][2] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0], ind[2][2][2] - deltaZ[i0]}, + {ind[2][2][0] - deltaR[i0], ind[2][3][0] - deltaZ[i0], ind[2][3][1] - deltaZ[i0], ind[2][3][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0], ind[3][0][2] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0], ind[3][1][2] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0], ind[3][2][2] - deltaZ[i0]}, + {ind[3][2][0] - deltaR[i0], ind[3][3][0] - deltaZ[i0], ind[3][3][1] - deltaZ[i0], ind[3][3][2] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = mGridData[ind[0][0][3]]; + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = mGridData[ind[0][1][3]]; + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = mGridData[ind[0][2][3]]; + cVals[12] = mGridData[ind[0][3][0]]; + cVals[13] = mGridData[ind[0][3][1]]; + cVals[14] = mGridData[ind[0][3][2]]; + cVals[15] = mGridData[ind[0][3][3]]; + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = mGridData[ind[1][0][3]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[20] = mGridData[ind[1][1][0]]; + cVals[23] = mGridData[ind[1][1][3]]; + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = mGridData[ind[1][2][3]]; + cVals[28] = mGridData[ind[1][3][0]]; + cVals[29] = mGridData[ind[1][3][1]]; + cVals[30] = mGridData[ind[1][3][2]]; + cVals[31] = mGridData[ind[1][3][3]]; + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = mGridData[ind[2][0][3]]; + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = mGridData[ind[2][1][3]]; + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = mGridData[ind[2][2][3]]; + cVals[44] = mGridData[ind[2][3][0]]; + cVals[45] = mGridData[ind[2][3][1]]; + cVals[46] = mGridData[ind[2][3][2]]; + cVals[47] = mGridData[ind[2][3][3]]; + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[51] = mGridData[ind[3][0][3]]; + cVals[52] = mGridData[ind[3][1][0]]; + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = mGridData[ind[3][1][3]]; + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = mGridData[ind[3][2][3]]; + cVals[60] = mGridData[ind[3][3][0]]; + cVals[61] = mGridData[ind[3][3][1]]; + cVals[62] = mGridData[ind[3][3][2]]; + cVals[63] = mGridData[ind[3][3][3]]; + } break; + + case GridPos::SideXRight: + case GridPos::LineD: + case GridPos::LineH: { + const int ind[4][4][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}, + {ind[0][2][0] - deltaR[i0], ind[0][3][0] - deltaZ[i0], ind[0][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}, + {ind[1][2][0] - deltaR[i0], ind[1][3][0] - deltaZ[i0], ind[1][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}, + {ind[2][2][0] - deltaR[i0], ind[2][3][0] - deltaZ[i0], ind[2][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}, + {ind[3][2][0] - deltaR[i0], ind[3][3][0] - deltaZ[i0], ind[3][3][1] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][0][1]], mGridData[ind[0][0][0]]); + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = extrapolation(mGridData[ind[0][1][2]], mGridData[ind[0][1][1]], mGridData[ind[0][1][0]]); + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][2][1]], mGridData[ind[0][2][0]]); + cVals[12] = mGridData[ind[0][3][0]]; + cVals[13] = mGridData[ind[0][3][1]]; + cVals[14] = mGridData[ind[0][3][2]]; + cVals[15] = extrapolation(mGridData[ind[0][3][2]], mGridData[ind[0][3][1]], mGridData[ind[0][3][0]]); + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][0][1]], mGridData[ind[1][0][0]]); + cVals[20] = mGridData[ind[1][1][0]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[23] = extrapolation(mGridData[ind[1][1][2]], mGridData[ii_x_y_z], mGridData[ind[1][1][0]]); + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][2][1]], mGridData[ind[1][2][0]]); + cVals[28] = mGridData[ind[1][3][0]]; + cVals[29] = mGridData[ind[1][3][1]]; + cVals[30] = mGridData[ind[1][3][2]]; + cVals[31] = extrapolation(mGridData[ind[1][3][2]], mGridData[ind[1][3][1]], mGridData[ind[1][3][0]]); + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][0][1]], mGridData[ind[2][0][0]]); + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = extrapolation(mGridData[ind[2][1][2]], mGridData[ind[2][1][1]], mGridData[ind[2][1][0]]); + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][2][1]], mGridData[ind[2][2][0]]); + cVals[44] = mGridData[ind[2][3][0]]; + cVals[45] = mGridData[ind[2][3][1]]; + cVals[46] = mGridData[ind[2][3][2]]; + cVals[47] = extrapolation(mGridData[ind[2][3][2]], mGridData[ind[2][3][1]], mGridData[ind[2][3][0]]); + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[52] = mGridData[ind[3][1][0]]; + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][0][1]], mGridData[ind[3][0][0]]); + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = extrapolation(mGridData[ind[3][1][2]], mGridData[ind[3][1][1]], mGridData[ind[3][1][0]]); + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][2][1]], mGridData[ind[3][2][0]]); + cVals[60] = mGridData[ind[3][3][0]]; + cVals[61] = mGridData[ind[3][3][1]]; + cVals[62] = mGridData[ind[3][3][2]]; + cVals[63] = extrapolation(mGridData[ind[3][3][2]], mGridData[ind[3][3][1]], mGridData[ind[3][3][0]]); + } break; + + case GridPos::SideYRight: + case GridPos::LineB: + case GridPos::LineF: { + const int ind[4][3][4]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0], ind[0][0][2] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0], ind[0][1][2] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0], ind[0][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0], ind[1][0][2] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0], ind[1][1][2] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0], ind[1][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0], ind[2][0][2] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0], ind[2][1][2] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0], ind[2][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0], ind[3][0][2] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0], ind[3][1][2] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0], ind[3][2][2] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = mGridData[ind[0][0][3]]; + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = mGridData[ind[0][1][3]]; + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = mGridData[ind[0][2][3]]; + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][0]], mGridData[ind[0][0][0]]); + cVals[13] = extrapolation(mGridData[ind[0][2][1]], mGridData[ind[0][1][1]], mGridData[ind[0][0][1]]); + cVals[14] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][2]], mGridData[ind[0][0][2]]); + cVals[15] = extrapolation(mGridData[ind[0][2][3]], mGridData[ind[0][1][3]], mGridData[ind[0][0][3]]); + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = mGridData[ind[1][0][3]]; + cVals[20] = mGridData[ind[1][1][0]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[23] = mGridData[ind[1][1][3]]; + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = mGridData[ind[1][2][3]]; + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][1][0]], mGridData[ind[1][0][0]]); + cVals[29] = extrapolation(mGridData[ind[1][2][1]], mGridData[ii_x_y_z], mGridData[ind[1][0][1]]); + cVals[30] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][2]], mGridData[ind[1][0][2]]); + cVals[31] = extrapolation(mGridData[ind[1][2][3]], mGridData[ind[1][1][3]], mGridData[ind[1][0][3]]); + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = mGridData[ind[2][0][3]]; + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = mGridData[ind[2][1][3]]; + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = mGridData[ind[2][2][3]]; + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][0]], mGridData[ind[2][0][0]]); + cVals[45] = extrapolation(mGridData[ind[2][2][1]], mGridData[ind[2][1][1]], mGridData[ind[2][0][1]]); + cVals[46] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][2]], mGridData[ind[2][0][2]]); + cVals[47] = extrapolation(mGridData[ind[2][2][3]], mGridData[ind[2][1][3]], mGridData[ind[2][0][3]]); + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[51] = mGridData[ind[3][0][3]]; + cVals[52] = mGridData[ind[3][1][0]]; + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = mGridData[ind[3][1][3]]; + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = mGridData[ind[3][2][3]]; + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][0]], mGridData[ind[3][0][0]]); + cVals[61] = extrapolation(mGridData[ind[3][2][1]], mGridData[ind[3][1][1]], mGridData[ind[3][0][1]]); + cVals[62] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][2]], mGridData[ind[3][0][2]]); + cVals[63] = extrapolation(mGridData[ind[3][2][3]], mGridData[ind[3][1][3]], mGridData[ind[3][0][3]]); + } break; + + case GridPos::SideYLeft: + case GridPos::LineA: + case GridPos::LineE: { + const int ind[4][3][4]{ + {{ii_x_y_z + deltaPhi[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0], ind[0][0][2] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0], ind[0][1][2] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0], ind[0][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0], ind[1][0][2] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0], ind[1][1][2] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0], ind[1][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0], ind[2][0][2] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0], ind[2][1][2] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0], ind[2][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0], ind[3][0][2] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0], ind[3][1][2] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0], ind[3][2][2] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0]]); + cVals[1] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][1]], mGridData[ind[0][2][1]]); + cVals[2] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][1][2]], mGridData[ind[0][2][2]]); + cVals[3] = extrapolation(mGridData[ind[0][0][3]], mGridData[ind[0][1][3]], mGridData[ind[0][2][3]]); + cVals[4] = mGridData[ind[0][0][0]]; + cVals[5] = mGridData[ind[0][0][1]]; + cVals[6] = mGridData[ind[0][0][2]]; + cVals[7] = mGridData[ind[0][0][3]]; + cVals[8] = mGridData[ind[0][1][0]]; + cVals[9] = mGridData[ind[0][1][1]]; + cVals[10] = mGridData[ind[0][1][2]]; + cVals[11] = mGridData[ind[0][1][3]]; + cVals[12] = mGridData[ind[0][2][0]]; + cVals[13] = mGridData[ind[0][2][1]]; + cVals[14] = mGridData[ind[0][2][2]]; + cVals[15] = mGridData[ind[0][2][3]]; + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][1][0]], mGridData[ind[1][2][0]]); + cVals[17] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][2][1]]); + cVals[18] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][1][2]], mGridData[ind[1][2][2]]); + cVals[19] = extrapolation(mGridData[ind[1][0][3]], mGridData[ind[1][1][3]], mGridData[ind[1][2][3]]); + cVals[20] = mGridData[ind[1][0][0]]; + cVals[22] = mGridData[ind[1][0][2]]; + cVals[23] = mGridData[ind[1][0][3]]; + cVals[24] = mGridData[ind[1][1][0]]; + cVals[25] = mGridData[ind[1][1][1]]; + cVals[26] = mGridData[ind[1][1][2]]; + cVals[27] = mGridData[ind[1][1][3]]; + cVals[28] = mGridData[ind[1][2][0]]; + cVals[29] = mGridData[ind[1][2][1]]; + cVals[30] = mGridData[ind[1][2][2]]; + cVals[31] = mGridData[ind[1][2][3]]; + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0]]); + cVals[33] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][1]], mGridData[ind[2][2][1]]); + cVals[34] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][1][2]], mGridData[ind[2][2][2]]); + cVals[35] = extrapolation(mGridData[ind[2][0][3]], mGridData[ind[2][1][3]], mGridData[ind[2][2][3]]); + cVals[36] = mGridData[ind[2][0][0]]; + cVals[37] = mGridData[ind[2][0][1]]; + cVals[38] = mGridData[ind[2][0][2]]; + cVals[39] = mGridData[ind[2][0][3]]; + cVals[40] = mGridData[ind[2][1][0]]; + cVals[41] = mGridData[ind[2][1][1]]; + cVals[42] = mGridData[ind[2][1][2]]; + cVals[43] = mGridData[ind[2][1][3]]; + cVals[44] = mGridData[ind[2][2][0]]; + cVals[45] = mGridData[ind[2][2][1]]; + cVals[46] = mGridData[ind[2][2][2]]; + cVals[47] = mGridData[ind[2][2][3]]; + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0]]); + cVals[49] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][1]], mGridData[ind[3][2][1]]); + cVals[50] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][1][2]], mGridData[ind[3][2][2]]); + cVals[51] = extrapolation(mGridData[ind[3][0][3]], mGridData[ind[3][1][3]], mGridData[ind[3][2][3]]); + cVals[52] = mGridData[ind[3][0][0]]; + cVals[53] = mGridData[ind[3][0][1]]; + cVals[54] = mGridData[ind[3][0][2]]; + cVals[55] = mGridData[ind[3][0][3]]; + cVals[56] = mGridData[ind[3][1][0]]; + cVals[57] = mGridData[ind[3][1][1]]; + cVals[58] = mGridData[ind[3][1][2]]; + cVals[59] = mGridData[ind[3][1][3]]; + cVals[60] = mGridData[ind[3][2][0]]; + cVals[61] = mGridData[ind[3][2][1]]; + cVals[62] = mGridData[ind[3][2][2]]; + cVals[63] = mGridData[ind[3][2][3]]; + } break; + + case GridPos::SideXLeft: + case GridPos::LineC: + case GridPos::LineG: { + const int ind[4][4][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}, + {ind[0][2][0] - deltaR[i0], ind[0][3][0] - deltaZ[i0], ind[0][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}, + {ind[1][2][0] - deltaR[i0], ind[1][3][0] - deltaZ[i0], ind[1][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}, + {ind[2][2][0] - deltaR[i0], ind[2][3][0] - deltaZ[i0], ind[2][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}, + {ind[3][2][0] - deltaR[i0], ind[3][3][0] - deltaZ[i0], ind[3][3][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2]]); + cVals[1] = mGridData[ind[0][0][0]]; + cVals[2] = mGridData[ind[0][0][1]]; + cVals[3] = mGridData[ind[0][0][2]]; + cVals[4] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][1][1]], mGridData[ind[0][1][2]]); + cVals[5] = mGridData[ind[0][1][0]]; + cVals[6] = mGridData[ind[0][1][1]]; + cVals[7] = mGridData[ind[0][1][2]]; + cVals[8] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][2][1]], mGridData[ind[0][2][2]]); + cVals[9] = mGridData[ind[0][2][0]]; + cVals[10] = mGridData[ind[0][2][1]]; + cVals[11] = mGridData[ind[0][2][2]]; + cVals[12] = extrapolation(mGridData[ind[0][3][0]], mGridData[ind[0][3][1]], mGridData[ind[0][3][2]]); + cVals[13] = mGridData[ind[0][3][0]]; + cVals[14] = mGridData[ind[0][3][1]]; + cVals[15] = mGridData[ind[0][3][2]]; + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][0][1]], mGridData[ind[1][0][2]]); + cVals[17] = mGridData[ind[1][0][0]]; + cVals[18] = mGridData[ind[1][0][1]]; + cVals[19] = mGridData[ind[1][0][2]]; + cVals[20] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][1][2]]); + cVals[22] = mGridData[ind[1][1][1]]; + cVals[23] = mGridData[ind[1][1][2]]; + cVals[24] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][2][1]], mGridData[ind[1][2][2]]); + cVals[25] = mGridData[ind[1][2][0]]; + cVals[26] = mGridData[ind[1][2][1]]; + cVals[27] = mGridData[ind[1][2][2]]; + cVals[28] = extrapolation(mGridData[ind[1][3][0]], mGridData[ind[1][3][1]], mGridData[ind[1][3][2]]); + cVals[29] = mGridData[ind[1][3][0]]; + cVals[30] = mGridData[ind[1][3][1]]; + cVals[31] = mGridData[ind[1][3][2]]; + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2]]); + cVals[33] = mGridData[ind[2][0][0]]; + cVals[34] = mGridData[ind[2][0][1]]; + cVals[35] = mGridData[ind[2][0][2]]; + cVals[36] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][1][1]], mGridData[ind[2][1][2]]); + cVals[37] = mGridData[ind[2][1][0]]; + cVals[38] = mGridData[ind[2][1][1]]; + cVals[39] = mGridData[ind[2][1][2]]; + cVals[40] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][2][1]], mGridData[ind[2][2][2]]); + cVals[41] = mGridData[ind[2][2][0]]; + cVals[42] = mGridData[ind[2][2][1]]; + cVals[43] = mGridData[ind[2][2][2]]; + cVals[44] = extrapolation(mGridData[ind[2][3][0]], mGridData[ind[2][3][1]], mGridData[ind[2][3][2]]); + cVals[45] = mGridData[ind[2][3][0]]; + cVals[46] = mGridData[ind[2][3][1]]; + cVals[47] = mGridData[ind[2][3][2]]; + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2]]); + cVals[49] = mGridData[ind[3][0][0]]; + cVals[50] = mGridData[ind[3][0][1]]; + cVals[51] = mGridData[ind[3][0][2]]; + cVals[52] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][1][1]], mGridData[ind[3][1][2]]); + cVals[53] = mGridData[ind[3][1][0]]; + cVals[54] = mGridData[ind[3][1][1]]; + cVals[55] = mGridData[ind[3][1][2]]; + cVals[56] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][2][1]], mGridData[ind[3][2][2]]); + cVals[57] = mGridData[ind[3][2][0]]; + cVals[58] = mGridData[ind[3][2][1]]; + cVals[59] = mGridData[ind[3][2][2]]; + cVals[60] = extrapolation(mGridData[ind[3][3][0]], mGridData[ind[3][3][1]], mGridData[ind[3][3][2]]); + cVals[61] = mGridData[ind[3][3][0]]; + cVals[62] = mGridData[ind[3][3][1]]; + cVals[63] = mGridData[ind[3][3][2]]; + } break; + + case GridPos::Edge0: + case GridPos::Edge4: + case GridPos::LineI: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z, ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][1]], mGridData[ind[0][2][2]]); + cVals[1] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0]]); + cVals[2] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][1]], mGridData[ind[0][2][1]]); + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][1][2]], mGridData[ind[0][2][2]]); + cVals[4] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2]]); + cVals[5] = mGridData[ind[0][0][0]]; + cVals[6] = mGridData[ind[0][0][1]]; + cVals[7] = mGridData[ind[0][0][2]]; + cVals[8] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][1][1]], mGridData[ind[0][1][2]]); + cVals[9] = mGridData[ind[0][1][0]]; + cVals[10] = mGridData[ind[0][1][1]]; + cVals[11] = mGridData[ind[0][1][2]]; + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][2][1]], mGridData[ind[0][2][2]]); + cVals[13] = mGridData[ind[0][2][0]]; + cVals[14] = mGridData[ind[0][2][1]]; + cVals[15] = mGridData[ind[0][2][2]]; + cVals[16] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][2][2]]); + cVals[17] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][0]], mGridData[ind[1][2][0]]); + cVals[18] = extrapolation(mGridData[ind[1][0][1]], mGridData[ind[1][1][1]], mGridData[ind[1][2][1]]); + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][1][2]], mGridData[ind[1][2][2]]); + cVals[20] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][0][1]], mGridData[ind[1][0][2]]); + cVals[22] = mGridData[ind[1][0][1]]; + cVals[23] = mGridData[ind[1][0][2]]; + cVals[24] = extrapolation(mGridData[ind[1][1][0]], mGridData[ind[1][1][1]], mGridData[ind[1][1][2]]); + cVals[25] = mGridData[ind[1][1][0]]; + cVals[26] = mGridData[ind[1][1][1]]; + cVals[27] = mGridData[ind[1][1][2]]; + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][2][1]], mGridData[ind[1][2][2]]); + cVals[29] = mGridData[ind[1][2][0]]; + cVals[30] = mGridData[ind[1][2][1]]; + cVals[31] = mGridData[ind[1][2][2]]; + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][1]], mGridData[ind[2][2][2]]); + cVals[33] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0]]); + cVals[34] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][1]], mGridData[ind[2][2][1]]); + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][1][2]], mGridData[ind[2][2][2]]); + cVals[36] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2]]); + cVals[37] = mGridData[ind[2][0][0]]; + cVals[38] = mGridData[ind[2][0][1]]; + cVals[39] = mGridData[ind[2][0][2]]; + cVals[40] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][1][1]], mGridData[ind[2][1][2]]); + cVals[41] = mGridData[ind[2][1][0]]; + cVals[42] = mGridData[ind[2][1][1]]; + cVals[43] = mGridData[ind[2][1][2]]; + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][2][1]], mGridData[ind[2][2][2]]); + cVals[45] = mGridData[ind[2][2][0]]; + cVals[46] = mGridData[ind[2][2][1]]; + cVals[47] = mGridData[ind[2][2][2]]; + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][1]], mGridData[ind[3][2][2]]); + cVals[49] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0]]); + cVals[50] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][1]], mGridData[ind[3][2][1]]); + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][1][2]], mGridData[ind[3][2][2]]); + cVals[52] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2]]); + cVals[53] = mGridData[ind[3][0][0]]; + cVals[54] = mGridData[ind[3][0][1]]; + cVals[55] = mGridData[ind[3][0][2]]; + cVals[56] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][1][1]], mGridData[ind[3][1][2]]); + cVals[57] = mGridData[ind[3][1][0]]; + cVals[58] = mGridData[ind[3][1][1]]; + cVals[59] = mGridData[ind[3][1][2]]; + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][2][1]], mGridData[ind[3][2][2]]); + cVals[61] = mGridData[ind[3][2][0]]; + cVals[62] = mGridData[ind[3][2][1]]; + cVals[63] = mGridData[ind[3][2][2]]; + } break; + + case GridPos::Edge1: + case GridPos::Edge5: + case GridPos::LineJ: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0]]); + cVals[1] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][1]], mGridData[ind[0][2][1]]); + cVals[2] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0] + deltaZ[i0]]); + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][1][1]], mGridData[ind[0][2][0]]); + cVals[4] = mGridData[ind[0][0][0]]; + cVals[5] = mGridData[ind[0][0][1]]; + cVals[6] = mGridData[ind[0][0][2]]; + cVals[7] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][0][1]], mGridData[ind[0][0][0]]); + cVals[8] = mGridData[ind[0][1][0]]; + cVals[9] = mGridData[ind[0][1][1]]; + cVals[10] = mGridData[ind[0][1][2]]; + cVals[11] = extrapolation(mGridData[ind[0][1][2]], mGridData[ind[0][1][1]], mGridData[ind[0][1][0]]); + cVals[12] = mGridData[ind[0][2][0]]; + cVals[13] = mGridData[ind[0][2][1]]; + cVals[14] = mGridData[ind[0][2][2]]; + cVals[15] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][2][1]], mGridData[ind[0][2][0]]); + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][1][0]], mGridData[ind[1][2][0]]); + cVals[17] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][2][1]]); + cVals[18] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][0]], mGridData[ind[1][2][0] + deltaZ[i0]]); + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][1][1]], mGridData[ind[1][2][0]]); + cVals[20] = mGridData[ind[1][0][0]]; + cVals[22] = mGridData[ind[1][0][2]]; + cVals[23] = extrapolation(mGridData[ind[1][0][2]], mGridData[ii_x_y_z], mGridData[ind[1][0][0]]); + cVals[24] = mGridData[ind[1][1][0]]; + cVals[25] = mGridData[ind[1][1][1]]; + cVals[26] = mGridData[ind[1][1][2]]; + cVals[27] = extrapolation(mGridData[ind[1][1][2]], mGridData[ind[1][1][1]], mGridData[ind[1][1][0]]); + cVals[28] = mGridData[ind[1][2][0]]; + cVals[29] = mGridData[ind[1][2][1]]; + cVals[30] = mGridData[ind[1][2][2]]; + cVals[31] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][2][1]], mGridData[ind[1][2][0]]); + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0]]); + cVals[33] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][1]], mGridData[ind[2][2][1]]); + cVals[34] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0] + deltaZ[i0]]); + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][1][1]], mGridData[ind[2][2][0]]); + cVals[36] = mGridData[ind[2][0][0]]; + cVals[37] = mGridData[ind[2][0][1]]; + cVals[38] = mGridData[ind[2][0][2]]; + cVals[39] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][0][1]], mGridData[ind[2][0][0]]); + cVals[40] = mGridData[ind[2][1][0]]; + cVals[41] = mGridData[ind[2][1][1]]; + cVals[42] = mGridData[ind[2][1][2]]; + cVals[43] = extrapolation(mGridData[ind[2][1][2]], mGridData[ind[2][1][1]], mGridData[ind[2][1][0]]); + cVals[44] = mGridData[ind[2][2][0]]; + cVals[45] = mGridData[ind[2][2][1]]; + cVals[46] = mGridData[ind[2][2][2]]; + cVals[47] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][2][1]], mGridData[ind[2][2][0]]); + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0]]); + cVals[49] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][1]], mGridData[ind[3][2][1]]); + cVals[50] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0] + deltaZ[i0]]); + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][1][1]], mGridData[ind[3][2][0]]); + cVals[52] = mGridData[ind[3][0][0]]; + cVals[53] = mGridData[ind[3][0][1]]; + cVals[54] = mGridData[ind[3][0][2]]; + cVals[55] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][0][1]], mGridData[ind[3][0][0]]); + cVals[56] = mGridData[ind[3][1][0]]; + cVals[57] = mGridData[ind[3][1][1]]; + cVals[58] = mGridData[ind[3][1][2]]; + cVals[59] = extrapolation(mGridData[ind[3][1][2]], mGridData[ind[3][1][1]], mGridData[ind[3][1][0]]); + cVals[60] = mGridData[ind[3][2][0]]; + cVals[61] = mGridData[ind[3][2][1]]; + cVals[62] = mGridData[ind[3][2][2]]; + cVals[63] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][2][1]], mGridData[ind[3][2][0]]); + } break; + + case GridPos::Edge2: + case GridPos::Edge6: + case GridPos::LineK: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2]]); + cVals[1] = mGridData[ind[0][0][0]]; + cVals[2] = mGridData[ind[0][0][1]]; + cVals[3] = mGridData[ind[0][0][2]]; + cVals[4] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][1][1]], mGridData[ind[0][1][2]]); + cVals[5] = mGridData[ind[0][1][0]]; + cVals[6] = mGridData[ind[0][1][1]]; + cVals[7] = mGridData[ind[0][1][2]]; + cVals[8] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2] + deltaR[i0]]); + cVals[9] = mGridData[ind[0][2][0]]; + cVals[10] = mGridData[ind[0][2][1]]; + cVals[11] = mGridData[ind[0][2][2]]; + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][1]], mGridData[ind[0][0][2]]); + cVals[13] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][0]], mGridData[ind[0][0][0]]); + cVals[14] = extrapolation(mGridData[ind[0][2][1]], mGridData[ind[0][1][1]], mGridData[ind[0][0][1]]); + cVals[15] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][2]], mGridData[ind[0][0][2]]); + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][0][1]], mGridData[ind[1][0][2]]); + cVals[17] = mGridData[ind[1][0][0]]; + cVals[18] = mGridData[ind[1][0][1]]; + cVals[19] = mGridData[ind[1][0][2]]; + cVals[20] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][1][2]]); + cVals[22] = mGridData[ind[1][1][1]]; + cVals[23] = mGridData[ind[1][1][2]]; + cVals[24] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][0][1]], mGridData[ind[1][0][2] + deltaR[i0]]); + cVals[25] = mGridData[ind[1][2][0]]; + cVals[26] = mGridData[ind[1][2][1]]; + cVals[27] = mGridData[ind[1][2][2]]; + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][1][1]], mGridData[ind[1][0][2]]); + cVals[29] = extrapolation(mGridData[ind[1][2][0]], mGridData[ii_x_y_z], mGridData[ind[1][0][0]]); + cVals[30] = extrapolation(mGridData[ind[1][2][1]], mGridData[ind[1][1][1]], mGridData[ind[1][0][1]]); + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2]]); + cVals[31] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][2]], mGridData[ind[1][0][2]]); + cVals[33] = mGridData[ind[2][0][0]]; + cVals[34] = mGridData[ind[2][0][1]]; + cVals[35] = mGridData[ind[2][0][2]]; + cVals[36] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][1][1]], mGridData[ind[2][1][2]]); + cVals[37] = mGridData[ind[2][1][0]]; + cVals[38] = mGridData[ind[2][1][1]]; + cVals[39] = mGridData[ind[2][1][2]]; + cVals[40] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2] + deltaR[i0]]); + cVals[41] = mGridData[ind[2][2][0]]; + cVals[42] = mGridData[ind[2][2][1]]; + cVals[43] = mGridData[ind[2][2][2]]; + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][1]], mGridData[ind[2][0][2]]); + cVals[45] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][0]], mGridData[ind[2][0][0]]); + cVals[46] = extrapolation(mGridData[ind[2][2][1]], mGridData[ind[2][1][1]], mGridData[ind[2][0][1]]); + cVals[47] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][2]], mGridData[ind[2][0][2]]); + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2]]); + cVals[49] = mGridData[ind[3][0][0]]; + cVals[50] = mGridData[ind[3][0][1]]; + cVals[51] = mGridData[ind[3][0][2]]; + cVals[52] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][1][1]], mGridData[ind[3][1][2]]); + cVals[53] = mGridData[ind[3][1][0]]; + cVals[54] = mGridData[ind[3][1][1]]; + cVals[55] = mGridData[ind[3][1][2]]; + cVals[56] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2] + deltaR[i0]]); + cVals[57] = mGridData[ind[3][2][0]]; + cVals[58] = mGridData[ind[3][2][1]]; + cVals[59] = mGridData[ind[3][2][2]]; + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][1]], mGridData[ind[3][0][2]]); + cVals[61] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][0]], mGridData[ind[3][0][0]]); + cVals[62] = extrapolation(mGridData[ind[3][2][1]], mGridData[ind[3][1][1]], mGridData[ind[3][0][1]]); + cVals[63] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][2]], mGridData[ind[3][0][2]]); + } break; + + case GridPos::Edge3: + case GridPos::Edge7: + case GridPos::LineL: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][0][1]], mGridData[ind[0][0][0]]); + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = extrapolation(mGridData[ind[0][1][2]], mGridData[ind[0][1][1]], mGridData[ind[0][1][0]]); + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][2][1]], mGridData[ind[0][2][0]]); + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][0]], mGridData[ind[0][0][0]]); + cVals[13] = extrapolation(mGridData[ind[0][2][1]], mGridData[ind[0][1][1]], mGridData[ind[0][0][1]]); + cVals[14] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][2]], mGridData[ind[0][0][2]]); + cVals[15] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][1]], mGridData[ind[0][0][0]]); + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][0][1]], mGridData[ind[1][0][0]]); + cVals[20] = mGridData[ind[1][1][0]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[23] = extrapolation(mGridData[ind[1][1][2]], mGridData[ii_x_y_z], mGridData[ind[1][1][0]]); + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][2][1]], mGridData[ind[1][2][0]]); + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][1][0]], mGridData[ind[1][0][0]]); + cVals[29] = extrapolation(mGridData[ind[1][2][1]], mGridData[ii_x_y_z], mGridData[ind[1][0][1]]); + cVals[30] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][2]], mGridData[ind[1][0][2]]); + cVals[31] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][1]], mGridData[ind[1][0][0]]); + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][0][1]], mGridData[ind[2][0][0]]); + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = extrapolation(mGridData[ind[2][1][2]], mGridData[ind[2][1][1]], mGridData[ind[2][1][0]]); + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][2][1]], mGridData[ind[2][2][0]]); + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][0]], mGridData[ind[2][0][0]]); + cVals[45] = extrapolation(mGridData[ind[2][2][1]], mGridData[ind[2][1][1]], mGridData[ind[2][0][1]]); + cVals[46] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][2]], mGridData[ind[2][0][2]]); + cVals[47] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][1]], mGridData[ind[2][0][0]]); + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][0][1]], mGridData[ind[3][0][0]]); + cVals[52] = mGridData[ind[3][1][0]]; + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = extrapolation(mGridData[ind[3][1][2]], mGridData[ind[3][1][1]], mGridData[ind[3][1][0]]); + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][2][1]], mGridData[ind[3][2][0]]); + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][0]], mGridData[ind[3][0][0]]); + cVals[61] = extrapolation(mGridData[ind[3][2][1]], mGridData[ind[3][1][1]], mGridData[ind[3][0][1]]); + cVals[62] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][2]], mGridData[ind[3][0][2]]); + cVals[63] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][1]], mGridData[ind[3][0][0]]); + } break; + } +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector.h new file mode 100644 index 0000000000000..a6862291d84dc --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector.h @@ -0,0 +1,173 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 matrix.h +/// \brief Definition of Vector and Matrix class +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> + +#ifndef ALICEO2_TPC_VECTOR_H_ +#define ALICEO2_TPC_VECTOR_H_ + +#include <Vc/Vc> + +namespace o2 +{ +namespace tpc +{ +template <typename DataT = float, size_t N = 64> +class Matrix +{ + using VDataT = Vc::Vector<DataT>; + + public: + /// constructor + /// \param dataMatrix pointer to the data + Matrix(const Vc::Memory<VDataT, N>* dataMatrix) : mDataMatrix(dataMatrix) {} + + const Vc::Memory<VDataT, N>& operator[](size_t i) const { return mDataMatrix[i]; } + + private: + const Vc::Memory<VDataT, N>* mDataMatrix{}; +}; + +template <typename DataT = float, size_t N = 64> +class Vector +{ + using VDataT = Vc::Vector<DataT>; + + public: + /// default constructor + Vector() = default; + + /// constructor + /// \param dataVector data which is assigned to the vector + Vector(const Vc::Memory<VDataT, N>& dataVector) : mDataVector(dataVector) {} + + /// operator access + const DataT operator[](size_t i) const { return mDataVector.scalar(i); } + DataT& operator[](size_t i) { return mDataVector.scalar(i); } + + /// sets the vector with index j + void setVector(const size_t j, const VDataT& vector) { mDataVector.vector(j) = vector; } + + /// \return returns the vector with index j + const VDataT getVector(const size_t j) const { return mDataVector.vector(j); } + + /// \return returns the number of Vc::Vector<DataT> stored in the Vector + size_t getvectorsCount() const { return mDataVector.vectorsCount(); } + + /// \return returns the number of entries stored in the Vector + size_t getentriesCount() const { return mDataVector.entriesCount(); } + + private: + // storage for the data + Vc::Memory<VDataT, N> mDataVector{}; +}; + +template <typename DataT, size_t N> +inline Vector<DataT, N> operator*(const Matrix<DataT, N>& a, const Vector<DataT, N>& b) +{ + using V = Vc::Vector<DataT>; + // resulting vector c + Vector<DataT, N> c; + for (size_t i = 0; i < N; ++i) { + V c_ij{}; + for (size_t j = 0; j < a[i].vectorsCount(); ++j) { + c_ij += a[i].vector(j) * b.getVector(j); + } + c[i] = c_ij.sum(); + } + return c; +} + +template <typename DataT, size_t N> +inline Vector<DataT, N> floor(const Vector<DataT, N>& a) +{ + Vector<DataT, N> c; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, Vc::floor(a.getVector(j))); + } + return c; +} + +template <typename DataT, size_t N> +inline Vector<DataT, N> operator-(const Vector<DataT, N>& a, const Vector<DataT, N>& b) +{ + // resulting matrix c + Vector<DataT, N> c; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, a.getVector(j) - b.getVector(j)); + } + return c; +} + +template <typename DataT, size_t N> +inline Vector<DataT, N> operator+(const Vector<DataT, N>& a, const Vector<DataT, N>& b) +{ + // resulting matrix c + Vector<DataT, N> c; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, a.getVector(j) + b.getVector(j)); + } + return c; +} + +template <typename DataT, size_t N> +inline Vector<DataT, N> operator*(const DataT a, const Vector<DataT, N>& b) +{ + // resulting matrix c + Vector<DataT, N> c; + for (size_t j = 0; j < b.getvectorsCount(); ++j) { + c.setVector(j, a * b.getVector(j)); + } + return c; +} + +// compute the sum of one Vector +template <typename DataT, size_t N> +inline DataT sum(const Vector<DataT, N>& a) +{ + // resulting matrix c + Vc::Vector<DataT> b = a.getVector(0); + for (size_t j = 1; j < a.getvectorsCount(); ++j) { + b += a.getVector(j); + } + return b.sum(); +} + +// multiply each row from a vector with the row from a second vector +template <typename DataT, size_t N> +inline Vector<DataT, N> operator*(const Vector<DataT, N>& a, const Vector<DataT, N>& b) +{ + // resulting matrix c + Vector<DataT, N> c{}; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, a.getVector(j) * b.getVector(j)); + } + return c; +} + +// check if all elements are equal +template <typename DataT, size_t N> +inline bool operator==(const Vector<DataT, N>& a, const Vector<DataT, N>& b) +{ + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + if (any_of(a.getVector(j) != b.getVector(j))) { + return false; + } + } + return true; +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector3D.h new file mode 100644 index 0000000000000..bd00405f1c9a7 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector3D.h @@ -0,0 +1,109 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Vector3D.h +/// \brief this is a simple 3D-matrix class with the possibility to resize the dimensions +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Oct 23, 2020 + +#ifndef ALICEO2_TPC_VECTOR3D_H_ +#define ALICEO2_TPC_VECTOR3D_H_ + +namespace o2 +{ +namespace tpc +{ + +/// this is a simple vector class which is used in the poisson solver class + +/// \tparam DataT the data type of the mStorage which is used during the calculations +template <typename DataT = double> +class Vector3D +{ + public: + /// Constructor for a tricubic interpolator + /// \param nr number of data points in r directions + /// \param nz number of data points in r directions + /// \param nphi number of data points in r directions + Vector3D(const unsigned int nr, const unsigned int nz, const unsigned int nphi) : mNr{nr}, mNz{nz}, mNphi{nphi}, mStorage{nr * nz * nphi} {}; + + /// default constructor + Vector3D() = default; + + /// operator to set the values + DataT& operator()(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) + { + return mStorage[getIndex(iR, iZ, iPhi)]; + } + + /// operator to read the values + const DataT& operator()(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) const + { + return mStorage[getIndex(iR, iZ, iPhi)]; + } + + /// operator to directly access the values + DataT& operator[](const unsigned int index) + { + return mStorage[index]; + } + + const DataT& operator[](const unsigned int index) const + { + return mStorage[index]; + } + + /// \param iR index in r direction + /// \param iZ index in z direction + /// \param iPhi index in phi direction + /// \return returns the index for given indices + int getIndex(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) const + { + return iR + mNr * (iZ + mNz * iPhi); + } + + /// resize the vector + /// \param nr number of data points in r directions + /// \param nz number of data points in r directions + /// \param nphi number of data points in r directions + void resize(const unsigned int nr, const unsigned int nz, const unsigned int nphi) + { + mNr = nr; + mNz = nz; + mNphi = nphi; + mStorage.resize(nr * nz * nphi); + } + + const auto& data() const { return mStorage; } + auto& data() { return mStorage; } + + unsigned int getNr() const { return mNr; } ///< get number of data points in r direction + unsigned int getNz() const { return mNz; } ///< get number of data points in z direction + unsigned int getNphi() const { return mNphi; } ///< get number of data points in phi direction + unsigned int size() const { return mStorage.size; } ///< get number of data points + + auto begin() const { return mStorage.begin(); } + auto begin() { return mStorage.begin(); } + + auto end() const { return mStorage.end(); } + auto end() { return mStorage.end(); } + + private: + unsigned int mNr{}; ///< number of data points in r direction + unsigned int mNz{}; ///< number of data points in z direction + unsigned int mNphi{}; ///< number of data points in phi direction + std::vector<DataT> mStorage{}; ///< vector containing the data +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C b/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C new file mode 100644 index 0000000000000..05a6050a79dda --- /dev/null +++ b/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C @@ -0,0 +1,365 @@ +// g++ -o spacecharge ~/alice/O2/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C -I ~/alice/sw/osx_x86-64/FairLogger/latest/include -L ~/alice/sw/osx_x86-64/FairLogger/latest/lib -I$O2_ROOT/include -L$O2_ROOT/lib -lO2TPCSpacecharge -lO2CommonUtils -std=c++17 -I$ROOTSYS/include -L$ROOTSYS/lib -lCore -L$VC_ROOT/lib -lVc -I$VC_ROOT/include -Xpreprocessor -fopenmp -I/usr/local/include -L/usr/local/lib -lomp -O3 -ffast-math -lFairLogger -lRIO +#include "TPCSpaceCharge/SpaceCharge.h" +#include <iostream> +#include <chrono> +#include <array> +#include "CommonUtils/TreeStreamRedirector.h" + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void calculateDistortionsAnalytical(const int globalEFieldTypeAna = 1, const int globalDistTypeAna = 1, const int eFieldTypeAna = 1, const int usePoissonSolverAna = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1); + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void calculateDistortionsFromHist(const char* path, const char* histoName, const int sides, const int globalEFieldType = 1, const int globalDistType = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1); + +/// \param gridType granularity of the grid +/// \param globalEFieldTypeAna setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistTypeAna setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param eFieldTypeAna setting for electrc field: 0: analytical formula, 1: tricubic interpolator +/// \param usePoissonSolverAna setting for use poisson solver or analytical formula for potential: 0: analytical formula, 1: poisson solver +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +/// \param nThreads number of threads which are used (if the value is -1 all threads should be used) +void calcDistAna(const int gridType = 0, const int globalEFieldTypeAna = 1, const int globalDistTypeAna = 1, const int eFieldTypeAna = 1, const int usePoissonSolverAna = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1) +{ + if (gridType == 0) { + calculateDistortionsAnalytical<double, 129, 129, 180>(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 1) { + calculateDistortionsAnalytical<double, 65, 65, 180>(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 2) { + calculateDistortionsAnalytical<double, 33, 33, 180>(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 3) { + calculateDistortionsAnalytical<double, 17, 17, 90>(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 4) { + calculateDistortionsAnalytical<double, 257, 257, 180>(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } +} + +/// \param path path to the root file containing the 3D density histogram +/// \param histoName name of the histogram in the root file +/// \param gridType granularity of the grid +/// \param sides setting which sides will be processed: 0: A- and C-Side, 1: A-Side, 2: C-Side +/// \param globalEFieldType setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistType setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +/// \param nThreads number of threads which are used (if the value is -1 all threads should be used) +void calcDistFromHist(const char* path, const char* histoName, const int gridType = 0, const int sides = 0, const int globalEFieldType = 1, const int globalDistType = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1) +{ + if (gridType == 0) { + calculateDistortionsFromHist<double, 129, 129, 180>(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 1) { + calculateDistortionsFromHist<double, 65, 65, 180>(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 2) { + calculateDistortionsFromHist<double, 33, 33, 180>(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 3) { + calculateDistortionsFromHist<double, 17, 17, 90>(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 4) { + calculateDistortionsFromHist<double, 257, 257, 180>(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 5) { + calculateDistortionsFromHist<double, 257, 257, 360>(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } +} + +/// \param spaceChargeCalc SpaceCharge object in which the calculations are performed +/// \param anaFields struct containing the analytical electric fields potential, space charge +/// \param fileOut output file where the calculated values are stored +/// \param side side of the TPC +/// \param globalEFieldType settings for global distortions/corrections: 0: using electric field for calculation of global distortions/corrections, 1: using local dis/corr interpolator for calculation of global distortions/corrections +/// \param globalDistType settings for global distortions: 0: standard method (start calculation of global distortion at each voxel in the tpc and follow electron drift to readout -slow-), 1: interpolation of global corrections (use the global corrections to apply an iterative approach to obtain the global distortions -fast-) +/// \param eFieldType setting for the electric field: 0: use analytical formula for the eletrical field for all calculations, 1: use the tricubic interpolator for the electric field +/// \param usePoissonSolver use poisson solver to calculate the potential or get the potential from the analytical formula 0: use analytical formula, 1: use poisson solver to calculate the potential (also calculates Efields using the obtained potential) +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void calculateDistortionsCorrectionsAnalytical(o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>& spaceChargeCalc, o2::tpc::AnalyticalFields<DataT> anaFields, const int globalEFieldType, const int eFieldType, const int globalDistType, const int usePoissonSolver) +{ + using timer = std::chrono::high_resolution_clock; + + std::cout << "====== STARTING CALCULATIION OF DISTORTIONS AND CORRECTIONS BY USING A ANALYTICAL FORMULA AS INPUT ======" << std::endl; + std::cout << "bins in z: " << Nz << std::endl; + std::cout << "bins in r: " << Nr << std::endl; + std::cout << "bins in phi: " << Nphi << std::endl; + + const std::array<std::string, 2> sGlobalType{"Electric fields", "local distortion/correction interpolator"}; + std::cout << "calculation of global distortions and corrections are performed by using: " << sGlobalType[globalEFieldType] << std::endl; + + const std::array<std::string, 2> sGlobalDistType{"Standard method", "interpolation of global corrections"}; + std::cout << "calculation of global distortions performed by following method: " << sGlobalDistType[globalDistType] << std::endl; + + const std::array<std::string, 2> sEfieldType{"analytical formula", "tricubic interpolator"}; + std::cout << "Using the E-fields from: " << sEfieldType[eFieldType] << std::endl; + + const std::array<std::string, 2> sUsePoissonSolver{"analytical formula", "poisson solver"}; + std::cout << "Using the Potential from: " << sUsePoissonSolver[usePoissonSolver] << std::endl; + std::cout << std::endl; + + const o2::tpc::Side side = anaFields.getSide(); + spaceChargeCalc.setChargeDensityFromFormula(anaFields); + + auto startTotal = timer::now(); + + if (usePoissonSolver == 1) { + spaceChargeCalc.setPotentialBoundaryFromFormula(anaFields); + auto start = timer::now(); + spaceChargeCalc.poissonSolver(side); + auto stop = timer::now(); + std::chrono::duration<float> time = stop - start; + std::cout << "poissonSolver: " << time.count() << std::endl; + } else { + spaceChargeCalc.setPotentialFromFormula(anaFields); + } + + if (usePoissonSolver == 1) { + auto start = timer::now(); + spaceChargeCalc.calcEField(side); + auto stop = timer::now(); + std::chrono::duration<float> time = stop - start; + std::cout << "electric field calculation: " << time.count() << std::endl; + } else { + spaceChargeCalc.setEFieldFromFormula(anaFields); + } + + const auto numEFields = spaceChargeCalc.getElectricFieldsInterpolator(side); + auto start = timer::now(); + const auto dist = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>::Type::Distortions; + (eFieldType == 1) ? spaceChargeCalc.calcLocalDistortionsCorrections(dist, numEFields) : spaceChargeCalc.calcLocalDistortionsCorrections(dist, anaFields); // local distortion calculation + auto stop = timer::now(); + std::chrono::duration<float> time = stop - start; + std::cout << "local distortions analytical: " << time.count() << std::endl; + + start = timer::now(); + const auto corr = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>::Type::Corrections; + (eFieldType == 1) ? spaceChargeCalc.calcLocalDistortionsCorrections(corr, numEFields) : spaceChargeCalc.calcLocalDistortionsCorrections(corr, anaFields); // local correction calculation + stop = timer::now(); + time = stop - start; + std::cout << "local corrections analytical: " << time.count() << std::endl; + + start = timer::now(); + const auto lCorrInterpolator = spaceChargeCalc.getLocalCorrInterpolator(side); + if (globalEFieldType == 1) { + spaceChargeCalc.calcGlobalCorrections(lCorrInterpolator); + } else if (eFieldType == 1) { + spaceChargeCalc.calcGlobalCorrections(numEFields); + } else { + spaceChargeCalc.calcGlobalCorrections(anaFields); + } + + stop = timer::now(); + time = stop - start; + std::cout << "global corrections analytical: " << time.count() << std::endl; + + start = timer::now(); + const auto lDistInterpolator = spaceChargeCalc.getLocalDistInterpolator(side); + if (globalDistType == 0) { + if (globalEFieldType == 1) { + spaceChargeCalc.calcGlobalDistortions(lDistInterpolator); + } else if (eFieldType == 1) { + spaceChargeCalc.calcGlobalDistortions(numEFields); + } else { + spaceChargeCalc.calcGlobalDistortions(anaFields); + } + } else { + const auto globalCorrInterpolator = spaceChargeCalc.getGlobalCorrInterpolator(side); + spaceChargeCalc.calcGlobalDistWithGlobalCorrIterative(globalCorrInterpolator); + } + stop = timer::now(); + time = stop - start; + std::cout << "global distortions analytical: " << time.count() << std::endl; + + auto stopTotal = timer::now(); + time = stopTotal - startTotal; + std::cout << "====== everything is done. Total Time: " << time.count() << std::endl; + std::cout << std::endl; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void writeToTree(o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>& spaceCharge3D, o2::utils::TreeStreamRedirector& pcstream, const o2::tpc::Side side) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + auto ldistR = spaceCharge3D.getLocalDistR(iZ, iR, iPhi, side); + auto ldistZ = spaceCharge3D.getLocalDistZ(iZ, iR, iPhi, side); + auto ldistRPhi = spaceCharge3D.getLocalDistRPhi(iZ, iR, iPhi, side); + auto lcorrR = spaceCharge3D.getLocalCorrR(iZ, iR, iPhi, side); + auto lcorrZ = spaceCharge3D.getLocalCorrZ(iZ, iR, iPhi, side); + auto lcorrRPhi = spaceCharge3D.getLocalCorrRPhi(iZ, iR, iPhi, side); + + auto distR = spaceCharge3D.getGlobalDistR(iZ, iR, iPhi, side); + auto distZ = spaceCharge3D.getGlobalDistZ(iZ, iR, iPhi, side); + auto distRPhi = spaceCharge3D.getGlobalDistRPhi(iZ, iR, iPhi, side); + auto corrR = spaceCharge3D.getGlobalCorrR(iZ, iR, iPhi, side); + auto corrZ = spaceCharge3D.getGlobalCorrZ(iZ, iR, iPhi, side); + auto corrRPhi = spaceCharge3D.getGlobalCorrRPhi(iZ, iR, iPhi, side); + + // distort then correct + auto radius = spaceCharge3D.getRVertex(iR, side); + auto z = spaceCharge3D.getZVertex(iZ, side); + auto phi = spaceCharge3D.getPhiVertex(iPhi, side); + DataT corrRDistPoint{}; + DataT corrZDistPoint{}; + DataT corrRPhiDistPoint{}; + + const DataT zDistorted = z + distZ; + const DataT radiusDistorted = radius + distR; + const DataT phiDistorted = spaceCharge3D.regulatePhi(phi + distRPhi / radius, side); + spaceCharge3D.getCorrectionsCyl(zDistorted, radiusDistorted, phiDistorted, side, corrZDistPoint, corrRDistPoint, corrRPhiDistPoint); + corrRPhiDistPoint *= radius / radiusDistorted; + + auto eZ = spaceCharge3D.getEz(iZ, iR, iPhi, side); + auto eR = spaceCharge3D.getEr(iZ, iR, iPhi, side); + auto ePhi = spaceCharge3D.getEphi(iZ, iR, iPhi, side); + auto pot = spaceCharge3D.getPotential(iZ, iR, iPhi, side); + auto charge = spaceCharge3D.getDensity(iZ, iR, iPhi, side); + + auto xPos = spaceCharge3D.getXFromPolar(radius, phi); + auto yPos = spaceCharge3D.getYFromPolar(radius, phi); + + int nr = Nr; + int nz = Nz; + int nphi = Nphi; + int iSide = side; + + pcstream << "distortions" + /// numer of bins + << "nR=" << nr + << "nPhi=" << nphi + << "nZ=" << nz + // bin indices + << "ir=" << iR + << "iz=" << iZ + << "iphi=" << iPhi + // coordinates + << "r=" << radius + << "z=" << z + << "x=" << xPos + << "y=" << yPos + << "phi=" << phi + // local distortions + << "ldistR=" << ldistR + << "ldistZ=" << ldistZ + << "ldistRPhi=" << ldistRPhi + // local corrections + << "lcorrR=" << lcorrR + << "lcorrZ=" << lcorrZ + << "lcorrRPhi=" << lcorrRPhi + // global distortions + << "distR=" << distR + << "distZ=" << distZ + << "distRPhi=" << distRPhi + // global corrections + << "corrR=" << corrR + << "corrZ=" << corrZ + << "corrRPhi=" << corrRPhi + // correction after distortion applied (test for consistency) + << "corrRDistortedPoint=" << corrRDistPoint + << "corrRPhiDistortedPoint=" << corrRPhiDistPoint + << "corrZDistortedPoint=" << corrZDistPoint + // electric fields etc. + << "Er=" << eR + << "Ez=" << eZ + << "Ephi=" << ePhi + << "potential=" << pot + << "charge=" << charge + << "side=" << iSide + << "\n"; + } + } + } +} + +/// \param globalEFieldTypeAna setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistTypeAna setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param eFieldTypeAna setting for electrc field: 0: analytical formula, 1: tricubic interpolator +/// \param usePoissonSolverAna setting for use poisson solver or analytical formula for potential: 0: analytical formula, 1: poisson solver +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +template <typename DataT = double, size_t Nz = 17, size_t Nr = 17, size_t Nphi = 90> +void calculateDistortionsAnalytical(const int globalEFieldTypeAna, const int globalDistTypeAna, const int eFieldTypeAna, const int usePoissonSolverAna, const int nSteps, const int simpsonIterations, const int nThreads) +{ + const auto integrationStrategy = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>::IntegrationStrategy::SimpsonIterative; + o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi> spaceCharge3D; + spaceCharge3D.setOmegaTauT1T2(0.32f, 1, 1); + spaceCharge3D.setNStep(nSteps); + spaceCharge3D.setSimpsonNIteratives(simpsonIterations); + spaceCharge3D.setNumericalIntegrationStrategy(integrationStrategy); + if (nThreads != -1) { + spaceCharge3D.setNThreads(nThreads); + } + + // write to root file + o2::utils::TreeStreamRedirector pcstream(TString::Format("distortions_ana_nR%lu_nZ%lu_nPhi%lu_SimpsonsIter%i.root", Nr, Nz, Nphi, simpsonIterations).Data(), "RECREATE"); + for (int iside = 0; iside < 2; ++iside) { + std::cout << "side: " << iside << std::endl; + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + o2::tpc::AnalyticalFields<DataT> anaFields(side); + calculateDistortionsCorrectionsAnalytical(spaceCharge3D, anaFields, globalEFieldTypeAna, eFieldTypeAna, globalDistTypeAna, usePoissonSolverAna); + pcstream.GetFile()->cd(); + writeToTree(spaceCharge3D, pcstream, side); + } + + pcstream.GetFile()->cd(); + pcstream.Close(); +} + +/// \param path path to the root file containing the 3D density histogram +/// \param histoName name of the histogram in the root file +/// \param sides setting which sides will be processed: 0: A- and C-Side, 1: A-Side, 2: C-Side +/// \param globalEFieldType setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistType setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +template <typename DataT = double, size_t Nz = 17, size_t Nr = 17, size_t Nphi = 90> +void calculateDistortionsFromHist(const char* path, const char* histoName, const int sides, const int globalEFieldType, const int globalDistType, const int nSteps, const int simpsonIterations, const int nThreads) +{ + using SC = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>; + const auto integrationStrategy = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>::IntegrationStrategy::SimpsonIterative; + SC spaceCharge3D; + spaceCharge3D.setOmegaTauT1T2(0.32f, 1, 1); + spaceCharge3D.setNStep(nSteps); + spaceCharge3D.setSimpsonNIteratives(simpsonIterations); + spaceCharge3D.setNumericalIntegrationStrategy(integrationStrategy); + if (nThreads != -1) { + spaceCharge3D.setNThreads(nThreads); + } + + // set density from input file + TFile fileOutSCDensity(path, "READ"); + spaceCharge3D.fillChargeDensityFromFile(fileOutSCDensity, histoName); + fileOutSCDensity.Close(); + + o2::utils::TreeStreamRedirector pcstream(TString::Format("distortions_real_nR%lu_nZ%lu_nPhi%lu_SimpsonsIter%i.root", Nr, Nz, Nphi, simpsonIterations).Data(), "RECREATE"); + int iSideStart = 0; + int iSideEnd = 2; + if (sides == 1) { + // a side only + iSideEnd = 1; + } else if (sides == 2) { + // c side only + iSideStart = 1; + } + for (int iside = iSideStart; iside < iSideEnd; ++iside) { + std::cout << "side: " << iside << std::endl; + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + const auto distType = globalDistType == 0 ? SC::GlobalDistType::Standard : SC ::GlobalDistType::Fast; + spaceCharge3D.setGlobalDistType(distType); + const auto eType = globalEFieldType == 1 ? SC::GlobalDistCorrMethod::LocalDistCorr : SC::GlobalDistCorrMethod::ElectricalField; + spaceCharge3D.setGlobalDistCorrMethod(eType); + spaceCharge3D.calculateDistortionsCorrections(side); + // write to root file + pcstream.GetFile()->cd(); + writeToTree(spaceCharge3D, pcstream, side); + } + pcstream.GetFile()->cd(); + pcstream.Close(); + + // write global corrections and distortions to file + TFile fOut("spacecharge.root", "RECREATE"); + if (sides != 2) { + spaceCharge3D.dumpGlobalDistortions(fOut, o2::tpc::Side::A); + spaceCharge3D.dumpGlobalCorrections(fOut, o2::tpc::Side::A); + } + if (sides != 1) { + spaceCharge3D.dumpGlobalDistortions(fOut, o2::tpc::Side::C); + spaceCharge3D.dumpGlobalCorrections(fOut, o2::tpc::Side::C); + } + fOut.Close(); +} diff --git a/Detectors/TPC/spacecharge/macro/createResidualDistortionObject.C b/Detectors/TPC/spacecharge/macro/createResidualDistortionObject.C new file mode 100644 index 0000000000000..f3b7d02f0c750 --- /dev/null +++ b/Detectors/TPC/spacecharge/macro/createResidualDistortionObject.C @@ -0,0 +1,215 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 createResidualDistortionObject.C +/// \brief This macro creates a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and stores it in a file. +/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch + +#include <cmath> + +#include "TFile.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "TPCSpaceCharge/SpaceCharge.h" +#include "MathUtils/Cartesian.h" + +using namespace o2::tpc; +using DataT = double; +constexpr int NZ = 129; +constexpr int NR = 129; +constexpr int NPHI = 180; +using DataContainer = DataContainer3D<DataT, NZ, NR, NPHI>; + +// function declarations +void createSpaceCharge(o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& spaceCharge, const char* path, const char* histoName, const int bSign, const int nThreads = -1); +void fillDistortionLookupMatrices(o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& spaceChargeCalcFluc, o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>* spaceChargeCalcAvg, DataContainer matrixDistDr[2], DataContainer matrixDistDrphi[2], DataContainer matrixDistDz[2]); +void makeDebugTreeResiduals(o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& calcFluc, o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& calcAvg, o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& spaceChargeRes); +/// Create a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and write it to a file. +/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive +/// \param pathToHistoFile path to a file with the fluctuating and average histograms +/// \param histoFlucName name of the fluctuating histogram +/// \param histoAvgName name of the average histogram +void createResidualDistortionObject(int bSign, const char* pathToHistoFile = "InputSCDensityHistograms_8000events.root", const char* histoFlucName = "inputSCDensity3D_8000_0", const char* histoAvgName = "inputSCDensity3D_8000_avg", const int nThreads = -1, bool debug = false) +{ + /* + Usage: + root -l -b -q createResidualDistortionObject.C+\(-1,\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"inputSCDensity3D_8000_avg\",true\) + */ + + // Calculate fluctuating and average distortion and correction lookup tables + o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI> scCalcFluc; + o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI> scCalcAvg; + createSpaceCharge(scCalcFluc, pathToHistoFile, histoFlucName, bSign, nThreads); + createSpaceCharge(scCalcAvg, pathToHistoFile, histoAvgName, bSign, nThreads); + + // Create matrices and fill them with residual distortions. Create SpaceCharge object, assign residual distortion matrices to it and store it in the output file. + DataContainer matrixResDistDr[2]; + DataContainer matrixResDistDrphi[2]; + DataContainer matrixResDistDz[2]; + + fillDistortionLookupMatrices(scCalcFluc, &scCalcAvg, matrixResDistDr, matrixResDistDrphi, matrixResDistDz); + o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI> spaceChargeRes; + spaceChargeRes.setDistortionLookupTables(matrixResDistDz[0], matrixResDistDr[0], matrixResDistDrphi[0], o2::tpc::Side::A); + spaceChargeRes.setDistortionLookupTables(matrixResDistDz[1], matrixResDistDr[1], matrixResDistDrphi[1], o2::tpc::Side::C); + + TFile fileOutput("ResidualDistortions.root", "recreate"); + spaceChargeRes.dumpGlobalDistortions(fileOutput, o2::tpc::Side::A); + spaceChargeRes.dumpGlobalDistortions(fileOutput, o2::tpc::Side::C); + + if (debug) { + makeDebugTreeResiduals(scCalcFluc, scCalcAvg, spaceChargeRes); + } +} + +/// calculate the distortions and corrections from a space-charge density histogram +/// \param spaceCharge input space-charge density object which will be filled +/// \param path path to a file with the histograms +/// \param histoName name of the histogram +/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive +/// \param nThreads number of threads which are used +void createSpaceCharge(o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& spaceCharge, const char* path, const char* histoName, const int bSign, const int nThreads) +{ + const float omegaTau = -0.32 * bSign; + spaceCharge.setOmegaTauT1T2(omegaTau, 1, 1); + if (nThreads != -1) { + spaceCharge.setNThreads(nThreads); + } + // set density from input file + TFile fileOutSCDensity(path, "READ"); + spaceCharge.fillChargeDensityFromFile(fileOutSCDensity, histoName); + fileOutSCDensity.Close(); + + // calculate the distortions + spaceCharge.calculateDistortionsCorrections(o2::tpc::Side::A); + spaceCharge.calculateDistortionsCorrections(o2::tpc::Side::C); +} + +/// Store distortions from spaceChargeCalcFluc in the matrices provided. If providing an object with average space-charge distortions spaceChargeCalcAvg, the residual distortions (dist_fluctuation(xyzTrue) + corr_average(xyzDistorted)) will be stored. +/// \param spaceChargeCalcFluc fluctuating distortions object +/// \param spaceChargeCalcAvg average distortions object +/// \param matrixDistDr matrix to store radial distortions on the A and C side +/// \param matrixDistDrphi matrix to store rphi distortions on the A and C side +/// \param matrixDistDz matrix to store z distortions on the A and C side +void fillDistortionLookupMatrices(o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& spaceChargeCalcFluc, o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>* spaceChargeCalcAvg, DataContainer matrixDistDr[2], DataContainer matrixDistDrphi[2], DataContainer matrixDistDz[2]) +{ + for (int iside = 0; iside < 2; ++iside) { + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + for (int iphi = 0; iphi < NPHI; ++iphi) { + const auto phi = spaceChargeCalcFluc.getPhiVertex(iphi, side); + + for (int ir = 0; ir < NR; ++ir) { + const auto r = spaceChargeCalcFluc.getRVertex(ir, side); + + for (int iz = 0; iz < NZ; ++iz) { + const auto z = spaceChargeCalcFluc.getZVertex(iz, side); + + // get fluctuating distortions + DataT distFlucdZ = 0; + DataT distFlucdR = 0; + DataT distFlucdRPhi = 0; + spaceChargeCalcFluc.getDistortionsCyl(z, r, phi, side, distFlucdZ, distFlucdR, distFlucdRPhi); + + // get average corrections if provided and add them to the fluctuating distortions + if (spaceChargeCalcAvg) { + const float zDistorted = z + distFlucdZ; + const float rDistorted = r + distFlucdR; + const float phiDistorted = phi + distFlucdRPhi / r; + DataT corrZDistPoint = 0; + DataT corrRDistPoint = 0; + DataT corrRPhiDistPoint = 0; + spaceChargeCalcAvg->getCorrectionsCyl(zDistorted, rDistorted, phiDistorted, side, corrZDistPoint, corrRDistPoint, corrRPhiDistPoint); + distFlucdZ += corrZDistPoint; + distFlucdR += corrRDistPoint; + distFlucdRPhi += corrRPhiDistPoint * r / rDistorted; + } + + // store (residual) distortions in the matrices + matrixDistDr[iside](ir, iz, iphi) = distFlucdR; + matrixDistDrphi[iside](ir, iz, iphi) = distFlucdRPhi; + matrixDistDz[iside](ir, iz, iphi) = distFlucdZ; + } + } + } + } +} + +/// Calculate and stream residual distortions from spaceChargeRes and from calcFluc and calcAvg for comparison. +/// \param calcFluc space charge object with fluctuation distortions +/// \param calcAvg space charge object with average distortions +/// \param spaceChargeRes space charge object with residual distortions +void makeDebugTreeResiduals(o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& calcFluc, o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& calcAvg, o2::tpc::SpaceCharge<DataT, NZ, NR, NPHI>& spaceChargeRes) +{ + o2::utils::TreeStreamRedirector pcstream("debugResidualDistortions.root", "recreate"); + for (int iside = 0; iside < 2; ++iside) { + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + for (int iphi = 0; iphi < NPHI; ++iphi) { + auto phi = calcFluc.getPhiVertex(iphi, side); + for (int ir = 0; ir < NR; ++ir) { + auto r = calcFluc.getRVertex(ir, side); + DataT x = r * std::cos(phi); + DataT y = r * std::sin(phi); + for (int iz = 1; iz < NZ; ++iz) { + DataT z = calcFluc.getZVertex(iz, side); + + GlobalPosition3D posDistRes(x, y, z); + spaceChargeRes.distortElectron(posDistRes); + DataT xyzDistRes[3] = {posDistRes.x(), posDistRes.y(), posDistRes.z()}; + DataT distRes[3] = {posDistRes.x() - x, posDistRes.y() - y, posDistRes.z() - z}; + + DataT distFlucX = 0; + DataT distFlucY = 0; + DataT distFlucZ = 0; + calcFluc.getDistortions(x, y, z, side, distFlucX, distFlucY, distFlucZ); + + DataT xDistorted = x + distFlucX; + DataT yDistorted = y + distFlucY; + DataT zDistorted = z + distFlucZ; + + DataT corrAvgX = 0; + DataT corrAvgY = 0; + DataT corrAvgZ = 0; + calcAvg.getCorrections(xDistorted, yDistorted, zDistorted, side, corrAvgX, corrAvgY, corrAvgZ); + DataT xyzDistResTrue[3] = {xDistorted + corrAvgX, yDistorted + corrAvgY, zDistorted + corrAvgZ}; + DataT distResTrue[3] = {distFlucX + corrAvgX, distFlucY + corrAvgY, distFlucZ + corrAvgZ}; + + pcstream << "debug" + << "iside=" << iside + << "iphi=" << iphi + << "ir=" << ir + << "iz=" << iz + // original position + << "phi=" << phi + << "r=" << r + << "x=" << x + << "y=" << y + << "z=" << z + // position of distorted points + << "xRes=" << xyzDistRes[0] + << "yRes=" << xyzDistRes[1] + << "zRes=" << xyzDistRes[2] + // true position of distorted points + << "xResTrue=" << xyzDistResTrue[0] + << "yResTrue=" << xyzDistResTrue[1] + << "zResTrue=" << xyzDistResTrue[2] + // residual distortions + << "distX=" << distRes[0] + << "distY=" << distRes[1] + << "distZ=" << distRes[2] + // true residual distortions + << "distXTrue=" << distResTrue[0] + << "distYTrue=" << distResTrue[1] + << "distZTrue=" << distResTrue[2] + // + << "\n"; + } + } + } + } + pcstream.Close(); +} diff --git a/Detectors/TPC/spacecharge/src/PoissonSolver.cxx b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx new file mode 100644 index 0000000000000..1a52719225b88 --- /dev/null +++ b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx @@ -0,0 +1,1497 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PoissonSolver.cxx +/// \brief This class provides implementation of Poisson Eq +/// solver by MultiGrid Method +/// +/// +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#include "TPCSpaceCharge/PoissonSolver.h" +#include "Framework/Logger.h" +#include <numeric> +#include <fmt/core.h> + +#ifdef WITH_OPENMP +#include <omp.h> +#endif + +using namespace o2::tpc; + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::poissonSolver3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry) +{ + if (MGParameters::isFull3D) { + poissonMultiGrid3D(matricesV, matricesCharge, symmetry); + } else { + poissonMultiGrid3D2D(matricesV, matricesCharge, symmetry); + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::poissonSolver2D(DataContainer& matricesV, const DataContainer& matricesCharge) +{ + poissonMultiGrid2D(matricesV, matricesCharge); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::poissonMultiGrid2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int iPhi) +{ + /// Geometry of TPC -- should be use AliTPCParams instead + const DataT gridSpacingR = getSpacingR(); + const DataT gridSpacingZ = getSpacingZ(); + const DataT ratioZ = gridSpacingR * gridSpacingR / (gridSpacingZ * gridSpacingZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} + + int nGridRow = 0; // number grid + int nGridCol = 0; // number grid + + int nnRow = Nr; + while (nnRow >>= 1) { + ++nGridRow; + } + + int nnCol = Nz; + while (nnCol >>= 1) { + ++nGridCol; + } + + //Check that number of Nr and Nz is suitable for multi grid + if (!isPowerOfTwo(Nr - 1)) { + LOGP(ERROR, "PoissonMultiGrid2D: PoissonMultiGrid - Error in the number of Nr. Must be 2**M + 1"); + return; + } + if (!isPowerOfTwo(Nz - 1)) { + LOGP(ERROR, "PoissonMultiGrid2D: PoissonMultiGrid - Error in the number of Nz. Must be 2**N - 1"); + return; + } + + const int nLoop = std::max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion + + LOGP(info, "{}", fmt::format("PoissonMultiGrid2D: nGridRow={}, nGridCol={}, nLoop={}, nMGCycle={}", nGridRow, nGridCol, nLoop, MGParameters::nMGCycle)); + + int iOne = 1; // in/dex + int jOne = 1; // index + int tnRRow = Nr; + int tnZColumn = Nz; + + // Vector for storing multi grid array + std::vector<Vector> tvArrayV(nLoop); // potential <--> error + std::vector<Vector> tvChargeFMG(nLoop); // charge is restricted in full multiGrid + std::vector<Vector> tvCharge(nLoop); // charge <--> residue + std::vector<Vector> tvResidue(nLoop); // residue calculation + + // Allocate memory for temporary grid + for (int count = 1; count <= nLoop; ++count) { + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + // if one just address to matrixV + tvResidue[count - 1].resize(tnRRow, tnZColumn, 1); + tvChargeFMG[count - 1].resize(tnRRow, tnZColumn, 1); + tvArrayV[count - 1].resize(tnRRow, tnZColumn, 1); + tvCharge[count - 1].resize(tnRRow, tnZColumn, 1); + + if (count == 1) { + for (int iphi = iPhi; iphi <= iPhi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + tvChargeFMG[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvCharge[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvArrayV[count - 1](ir, iz, iphi) = matricesV(iz, ir, iphi); + } + } + } + } else { + restrict2D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, 0); + } + iOne = 2 * iOne; + jOne = 2 * jOne; + } + + /// full multi grid + if (MGParameters::cycleType == CycleType::FCycle) { + + LOGP(info, "PoissonMultiGrid2D: Do full cycle"); + // FMG + // 1) Relax on the coarsest grid + iOne = iOne * 0.5; + jOne = jOne * 0.5; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + const DataT h = gridSpacingR * nLoop; + const DataT h2 = h * h; + const DataT tempRatio = ratioZ * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + + std::vector<DataT> coefficient1(tnRRow); + std::vector<DataT> coefficient2(tnRRow); + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + relax2D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + + // Do VCycle from nLoop H to h + for (int count = nLoop - 2; count >= 0; --count) { + + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + interp2D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, iPhi); + + // Copy the relax charge to the tvCharge + tvCharge[count] = tvChargeFMG[count]; //copy + + // Do V cycle + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + vCycle2D(count + 1, nLoop, MGParameters::nPre, MGParameters::nPost, gridSpacingR, ratioZ, tvArrayV, tvCharge, tvResidue); + } + } + } else if (MGParameters::cycleType == CycleType::VCycle) { + // 2. VCycle + LOGP(info, "PoissonMultiGrid2D: Do V cycle"); + + int gridFrom = 1; + int gridTo = nLoop; + + // Do MGCycle + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + vCycle2D(gridFrom, gridTo, MGParameters::nPre, MGParameters::nPost, gridSpacingR, ratioZ, tvArrayV, tvCharge, tvResidue); + } + } else if (MGParameters::cycleType == CycleType::WCycle) { + // 3. W Cycle (TODO:) + int gridFrom = 1; + int gridTo = nLoop; + // Do MGCycle + for (Int_t mgCycle = 0; mgCycle < MGParameters::nMGCycle; mgCycle++) { + wCycle2D(gridFrom, gridTo, MGParameters::gamma, MGParameters::nPre, MGParameters::nPost, gridSpacingR, ratioZ, tvArrayV, tvCharge, tvResidue); + } + } + + // fill output + for (int iphi = iPhi; iphi <= iPhi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + matricesV(iz, ir, iphi) = tvArrayV[0](ir, iz, iphi); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::poissonMultiGrid3D2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry) +{ + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D2D: in Poisson Solver 3D multiGrid semi coarsening Nr={}, cols={}, Nphi={}", Nz, Nr, Nphi)); + + // Check that the number of Nr and Nz is suitable for a binary expansion + if (!isPowerOfTwo((Nr - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3DMultiGrid - Error in the number of Nr. Must be 2**M + 1"); + return; + } + if (!isPowerOfTwo((Nz - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3DMultiGrid - Error in the number of Nz. Must be 2**N - 1"); + return; + } + if (Nphi <= 3) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3DMultiGrid - Error in the number of Nphi. Must be larger than 3"); + return; + } + if (Nphi > 1000) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3D Nphi > 1000 is not allowed (nor wise)"); + return; + } + + const DataT gridSpacingR = getSpacingR(); + const DataT gridSpacingZ = getSpacingZ(); + const DataT gridSpacingPhi = getSpacingPhi(); + const DataT ratioPhi = gridSpacingR * gridSpacingR / (gridSpacingPhi * gridSpacingPhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT ratioZ = gridSpacingR * gridSpacingR / (gridSpacingZ * gridSpacingZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} + + // Solve Poisson's equation in cylindrical coordinates by multiGrid technique + // Allow for different size grid spacing in R and Z directions + int nGridRow = 0; // number grid + int nGridCol = 0; // number grid + int nnRow = Nr; + int nnCol = Nz; + + while (nnRow >>= 1) { + ++nGridRow; + } + while (nnCol >>= 1) { + ++nGridCol; + } + + const int maxVal = std::max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion + const size_t nLoop = (maxVal > MGParameters::maxLoop) ? MGParameters::maxLoop : maxVal; + unsigned int iOne = 1; // index i in gridSize r (original) + unsigned int jOne = 1; // index j in gridSize z (original) + + std::vector<Vector> tvArrayV(nLoop); // potential <--> error + std::vector<Vector> tvChargeFMG(nLoop); // charge is restricted in full multiGrid + std::vector<Vector> tvCharge(nLoop); // charge <--> residue + std::vector<Vector> tvPrevArrayV(nLoop); // error calculation + std::vector<Vector> tvResidue(nLoop); // residue calculation + + for (unsigned int count = 1; count <= nLoop; count++) { + const unsigned int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + const unsigned int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tvResidue[count - 1].resize(tnRRow, tnZColumn, Nphi); + tvPrevArrayV[count - 1].resize(tnRRow, tnZColumn, Nphi); + + // memory for the finest grid is from parameters + tvChargeFMG[count - 1].resize(tnRRow, tnZColumn, Nphi); + tvArrayV[count - 1].resize(tnRRow, tnZColumn, Nphi); + tvCharge[count - 1].resize(tnRRow, tnZColumn, Nphi); + + if (count == 1) { + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + tvChargeFMG[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvArrayV[count - 1](ir, iz, iphi) = matricesV(iz, ir, iphi); + } + } + } + } else { + restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, Nphi, Nphi); + restrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, Nphi, Nphi); + } + iOne = 2 * iOne; // doubling + jOne = 2 * jOne; // doubling + } + + std::array<DataT, Nr> coefficient1{}; // coefficient1(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array<DataT, Nr> coefficient2{}; // coefficient2(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array<DataT, Nr> coefficient3{}; // coefficient3(Nr) for storing (1/r_{i}^2) from central differences in phi direction + std::array<DataT, Nr> coefficient4{}; // coefficient4(Nr) for storing 1/2 + std::array<DataT, Nr> inverseCoefficient4{}; // inverse of coefficient4(Nr) + + // Case full multi grid (FMG) + if (MGParameters::cycleType == CycleType::FCycle) { + // 1) Relax on the coarsest grid + iOne = iOne * 0.5; + jOne = jOne * 0.5; + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + const DataT iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // relax on the coarsest level + relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + + // 2) Do multiGrid v-cycle from coarsest to finest + for (int count = nLoop - 2; count >= 0; --count) { + // move to finer grid + iOne = iOne * 0.5; + jOne = jOne * 0.5; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + // 2) a) Interpolate potential for h -> 2h (coarse -> fine) + interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, Nphi, Nphi); + + // 2) c) Copy the restricted charge to charge for calculation + tvCharge[count] = tvChargeFMG[count]; //copy + + // 2) c) Do V cycle MGParameters::nMGCycle times at most + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + // Copy the potential to temp array for convergence calculation + tvPrevArrayV[count] = tvArrayV[count]; + + // 2) c) i) Call V cycle from grid count+1 (current fine level) to nLoop (coarsest) + vCycle3D2D(symmetry, count + 1, nLoop, MGParameters::nPre, MGParameters::nPost, ratioZ, ratioPhi, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); + + const DataT convergenceError = getConvergenceError(tvArrayV[count], tvPrevArrayV[count]); + + /// if already converge just break move to finer grid + if (convergenceError <= sConvergenceError) { + break; + } + } + } + } + + // fill output + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + matricesV(iz, ir, iphi) = tvArrayV[0](ir, iz, iphi); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::poissonMultiGrid3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry) +{ + const DataT gridSpacingR = getSpacingR(); + const DataT gridSpacingZ = getSpacingZ(); + const DataT ratioZ = gridSpacingR * gridSpacingR / (gridSpacingZ * gridSpacingZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} + + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D: in Poisson Solver 3D multi grid full coarsening Nr={}, cols={}, Nphi={}", Nr, Nz, Nphi)); + + // Check that the number of Nr and Nz is suitable for a binary expansion + if (!isPowerOfTwo((Nr - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3DMultiGrid - Error in the number of Nr. Must be 2**M + 1"); + return; + } + if (!isPowerOfTwo((Nz - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3DMultiGrid - Error in the number of Nz. Must be 2**N - 1"); + return; + } + if (Nphi <= 3) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3DMultiGrid - Error in the number of Nphi. Must be larger than 3"); + return; + } + if (Nphi > 1000) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3D Nphi > 1000 is not allowed (nor wise)"); + return; + } + + // Solve Poisson's equation in cylindrical coordinates by multi grid technique + // Allow for different size grid spacing in R and Z directions + int nGridRow = 0; // number grid + int nGridCol = 0; // number grid + int nGridPhi = 0; + + int nnRow = Nr; + while (nnRow >>= 1) { + ++nGridRow; + } + + int nnCol = Nz; + while (nnCol >>= 1) { + ++nGridCol; + } + + int nnPhi = Nphi; + while (nnPhi % 2 == 0) { + ++nGridPhi; + nnPhi *= 0.5; + } + + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D: nGridRow={}, nGridCol={}, nGridPhi={}", nGridRow, nGridCol, nGridPhi)); + const int nLoop = std::max({nGridRow, nGridCol, nGridPhi}); // Calculate the number of nLoop for the binary expansion + + // Vector for storing multi grid array + int iOne = 1; // index i in gridSize r (original) + int jOne = 1; // index j in gridSize z (original) + int kOne = 1; // index k in gridSize phi + int tnRRow = Nr; + int tnZColumn = Nz; + int tPhiSlice = Nphi; + + // 1) Memory allocation for multi grid + std::vector<Vector> tvArrayV(nLoop); // potential <--> error + std::vector<Vector> tvChargeFMG(nLoop); // charge is restricted in full multiGrid + std::vector<Vector> tvCharge(nLoop); // charge <--> residue + std::vector<Vector> tvPrevArrayV(nLoop); // error calculation + std::vector<Vector> tvResidue(nLoop); // residue calculation + + std::array<DataT, Nr> coefficient1{}; // coefficient1(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array<DataT, Nr> coefficient2{}; // coefficient2(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array<DataT, Nr> coefficient3{}; // coefficient3(Nr) for storing (1/r_{i}^2) from central differences in phi direction + std::array<DataT, Nr> coefficient4{}; // coefficient4(Nr) for storing 1/2 + std::array<DataT, Nr> inverseCoefficient4{}; // inverse of coefficient4(Nr) + + for (int count = 1; count <= nLoop; ++count) { + // tnRRow,tnZColumn in new grid + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + // allocate memory for residue + tvResidue[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvPrevArrayV[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvChargeFMG[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvArrayV[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvCharge[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + + // memory for the finest grid is from parameters + if (count == 1) { + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + tvChargeFMG[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvArrayV[count - 1](ir, iz, iphi) = matricesV(iz, ir, iphi); + } + } + } + tvCharge[count - 1] = tvChargeFMG[count - 1]; + } + iOne = 2 * iOne; // doubling + jOne = 2 * jOne; // doubling + kOne = 2 * kOne; + } + + // Case full multi grid (FMG) + if (MGParameters::cycleType == CycleType::FCycle) { + // Restrict the charge to coarser grid + iOne = 2; + jOne = 2; + kOne = 2; + int otPhiSlice = Nphi; + + // 1) Restrict Charge and Boundary to coarser grid + for (int count = 2; count <= nLoop; ++count) { + // tnRRow,tnZColumn in new grid + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D: Restrict3D, tnRRow={}, tnZColumn={}, newPhiSlice={}, oldPhiSlice={}", tnRRow, tnZColumn, tPhiSlice, otPhiSlice)); + restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + // copy boundary values of V + restrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + otPhiSlice = tPhiSlice; + + iOne = 2 * iOne; // doubling + jOne = 2 * jOne; // doubling + kOne = 2 * kOne; + } + + // Relax on the coarsest grid + // FMG + // 2) Relax on the coarsest grid + // move to the coarsest + 1 + iOne = iOne * 0.5; + jOne = jOne * 0.5; + kOne = kOne * 0.5; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + otPhiSlice = tPhiSlice; + + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT gridSizePhiInv = tPhiSlice * INVTWOPI; // h_{phi} + const DataT tempRatioPhi = h2 * gridSizePhiInv * gridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 3) Relax on the coarsest grid + relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + + // 4) V Cycle from coarsest to finest + for (int count = nLoop - 2; count >= 0; --count) { + // move to finer grid + std::fill(std::begin(coefficient1), std::end(coefficient1), 0); + std::fill(std::begin(coefficient2), std::end(coefficient2), 0); + std::fill(std::begin(coefficient3), std::end(coefficient3), 0); + std::fill(std::begin(coefficient4), std::end(coefficient4), 0); + std::fill(std::begin(inverseCoefficient4), std::end(inverseCoefficient4), 0); + + iOne = iOne * 0.5; + jOne = jOne * 0.5; + kOne = kOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + // 4) a) interpolate from 2h --> h grid + interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + + // Copy the relax charge to the tvCharge + if (count > 0) { + tvCharge[count] = tvChargeFMG[count]; + } + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + // copy to store previous potential + tvPrevArrayV[count] = tvArrayV[count]; + + vCycle3D(symmetry, count + 1, nLoop, MGParameters::nPre, MGParameters::nPost, ratioZ, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); + + // converge error + const DataT convergenceError = getConvergenceError(tvArrayV[count], tvPrevArrayV[count]); + // if already converge just break move to finer grid + if (convergenceError <= sConvergenceError) { + break; + } + } + // keep old slice information + otPhiSlice = tPhiSlice; + } + } else if (MGParameters::cycleType == CycleType::VCycle) { + // V-cycle + int gridFrom = 1; + int gridTo = nLoop; + + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + // copy to store previous potential + tvPrevArrayV[0] = tvArrayV[0]; + + // Do V Cycle from the coarsest to finest grid + vCycle3D(symmetry, gridFrom, gridTo, MGParameters::nPre, MGParameters::nPost, ratioZ, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); + + // convergence error + const DataT convergenceError = getConvergenceError(tvArrayV[0], tvPrevArrayV[0]); + + // if error already achieved then stop mg iteration + if (convergenceError <= sConvergenceError) { + break; + } + } + } + + // fill output + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + matricesV(iz, ir, iphi) = tvArrayV[0](ir, iz, iphi); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::wCycle2D(const int gridFrom, const int gridTo, const int gamma, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, + std::vector<Vector>& tvArrayV, std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue) +{ + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + std::vector<DataT> coefficient1(Nr); + std::vector<DataT> coefficient2(Nz); + + // 1) Go to coarsest level + for (int count = gridFrom; count <= gridTo - 2; ++count) { + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + const DataT inverseTempFourth = 1.0 / tempFourth; + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + Vector matricesCurrentV = tvArrayV[count - 1]; + Vector matricesCurrentCharge = tvCharge[count - 1]; + Vector residue = tvResidue[count - 1]; + + // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax2D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } + + // 2) Residue calculation + residue2D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, coefficient1, coefficient2); + + iOne = 2 * iOne; + jOne = 2 * jOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + matricesCurrentCharge = tvCharge[count]; + matricesCurrentV = tvArrayV[count]; + + //3) Restriction + restrict2D(matricesCurrentCharge, residue, tnRRow, tnZColumn, 0); + } + + // Do V cycle from: gridTo-1 to gridTo gamma times + for (int iGamma = 0; iGamma < gamma; ++iGamma) { + vCycle2D(gridTo - 1, gridTo, nPre, nPost, gridSizeR, ratio, tvArrayV, tvCharge, tvResidue); + } + + // Go to finest grid + for (int count = gridTo - 2; count >= gridFrom; --count) { + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + const Vector matricesCurrentCharge = tvCharge[count - 1]; + Vector matricesCurrentV = tvArrayV[count - 1]; + const Vector matricesCurrentVC = tvArrayV[count]; + + // 6) Interpolation/Prolongation + addInterp2D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, 0); + + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + // 7) Post-Smoothing: Gauss-Seidel Relaxation + for (Int_t jPost = 1; jPost <= nPost; ++jPost) { + relax2D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } // end post smoothing + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::vCycle2D(const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, std::vector<Vector>& tvArrayV, + std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue) +{ + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + std::vector<DataT> coefficient1(Nr); + std::vector<DataT> coefficient2(Nz); + + // 1) Go to coarsest level + for (int count = gridFrom; count <= gridTo - 1; ++count) { + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + const DataT inverseTempFourth = 1.0 / tempFourth; + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax2D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } + + // 2) Residue calculation + residue2D(tvResidue[count - 1], tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, coefficient1, coefficient2); + + iOne = 2 * iOne; + jOne = 2 * jOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + //3) Restriction + restrict2D(tvCharge[count], tvResidue[count - 1], tnRRow, tnZColumn, 0); + + //4) Zeroing coarser V + std::fill(tvArrayV[count].begin(), tvArrayV[count].end(), 0); // is this necessary??? + } + + // 5) coarsest grid + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + relax2D(tvArrayV[gridTo - 1], tvCharge[gridTo - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + + // Go to finest grid + for (int count = gridTo - 1; count >= gridFrom; count--) { + + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + // 6) Interpolation/Prolongation + addInterp2D(tvArrayV[count - 1], tvArrayV[count], tnRRow, tnZColumn, 0); + + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + // 7) Post-Smoothing: Gauss-Seidel Relaxation + for (int jPost = 1; jPost <= nPost; ++jPost) { + relax2D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } // end post smoothing + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::vCycle3D2D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, const DataT ratioPhi, + std::vector<Vector>& tvArrayV, std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue, std::array<DataT, Nr>& coefficient1, + std::array<DataT, Nr>& coefficient2, std::array<DataT, Nr>& coefficient3, std::array<DataT, Nr>& coefficient4, std::array<DataT, Nr>& inverseCoefficient4) const +{ + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + for (int count = gridFrom; count <= gridTo - 1; ++count) { + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const int iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + for (unsigned int i = 1; i < tnRRow - 1; ++i) { + inverseCoefficient4[i] = 1.0 / coefficient4[i]; + } + + //Info("VCycle3D2D","Before Pre-smoothing"); + // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } // end pre smoothing + + // 2) Residue calculation + residue3D(tvResidue[count - 1], tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, Nphi, symmetry, ih2, tempRatioZ, coefficient1, coefficient2, coefficient3, inverseCoefficient4); + + iOne = 2 * iOne; + jOne = 2 * jOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + //3) Restriction + restrict3D(tvCharge[count], tvResidue[count - 1], tnRRow, tnZColumn, Nphi, Nphi); + + //4) Zeroing coarser V + std::fill(tvArrayV[count].begin(), tvArrayV[count].end(), 0); + } + + // coarsest grid + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + + const int iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 3) Relax on the coarsest grid + relax3D(tvArrayV[gridTo - 1], tvCharge[gridTo - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + + // back to fine + for (int count = gridTo - 1; count >= gridFrom; --count) { + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + const int iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + // 4) Interpolation/Prolongation + addInterp3D(tvArrayV[count - 1], tvArrayV[count], tnRRow, tnZColumn, Nphi, Nphi); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 5) Post-Smoothing: Gauss-Seidel Relaxation + for (int jPost = 1; jPost <= nPost; ++jPost) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } // end post smoothing + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::vCycle3D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, std::vector<Vector>& tvArrayV, + std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue, std::array<DataT, Nr>& coefficient1, std::array<DataT, Nr>& coefficient2, std::array<DataT, Nr>& coefficient3, + std::array<DataT, Nr>& coefficient4, std::array<DataT, Nr>& inverseCoefficient4) const +{ + const DataT gridSpacingR = getSpacingR(); + + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + int kOne = 1 << (gridFrom - 1); + + int nnPhi = Nphi; + while (nnPhi % 2 == 0) { + nnPhi *= 0.5; + } + + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + int tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + for (int count = gridFrom; count <= gridTo - 1; ++count) { + const int otPhiSlice = tPhiSlice; + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const DataT tempGridSizePhiInv = tPhiSlice * INVTWOPI; // phi now is multiGrid + const DataT tempRatioPhi = h2 * tempGridSizePhiInv * tempGridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + for (int i = 1; i < tnRRow - 1; ++i) { + inverseCoefficient4[i] = 1.0 / coefficient4[i]; + } + + // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } // end pre smoothing + + // 2) Residue calculation + residue3D(tvResidue[count - 1], tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, ih2, tempRatioZ, coefficient1, coefficient2, coefficient3, inverseCoefficient4); + + iOne = 2 * iOne; + jOne = 2 * jOne; + kOne = 2 * kOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + //3) Restriction + restrict3D(tvCharge[count], tvResidue[count - 1], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + + //4) Zeroing coarser V + std::fill(tvArrayV[count].begin(), tvArrayV[count].end(), 0); + } + + // coarsest grid + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT tempGridSizePhiInv = tPhiSlice * INVTWOPI; // phi now is multiGrid + const DataT tempRatioPhi = h2 * tempGridSizePhiInv * tempGridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 3) Relax on the coarsest grid + relax3D(tvArrayV[gridTo - 1], tvCharge[gridTo - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + // back to fine + for (int count = gridTo - 1; count >= gridFrom; --count) { + const int otPhiSlice = tPhiSlice; + iOne = iOne * 0.5; + jOne = jOne * 0.5; + kOne = kOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT tempGridSizePhiInv = tPhiSlice * INVTWOPI; + const DataT tempRatioPhi = h2 * tempGridSizePhiInv * tempGridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + // 4) Interpolation/Prolongation + addInterp3D(tvArrayV[count - 1], tvArrayV[count], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 5) Post-Smoothing: Gauss-Seidel Relaxation + for (int jPost = 1; jPost <= nPost; ++jPost) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::residue2D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT ih2, const DataT inverseTempFourth, + const DataT tempRatio, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2) +{ + const int iPhi = 0; +#pragma omp parallel for num_threads(sNThreads) + for (int i = 1; i < tnRRow - 1; ++i) { + for (int j = 1; j < tnZColumn - 1; ++j) { + residue(i, j, iPhi) = ih2 * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) - inverseTempFourth * matricesCurrentV(i, j, iPhi)) + matricesCurrentCharge(i, j, iPhi); + } // end cols + } // end nRRow + + //Boundary points. + for (int i = 0; i < tnRRow; ++i) { + residue(i, 0, iPhi) = residue(i, tnZColumn - 1, iPhi) = 0.0; + } + + for (int j = 0; j < tnZColumn; ++j) { + residue(0, j, iPhi) = residue(tnRRow - 1, j, iPhi) = 0.0; + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::residue3D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int tnPhi, const int symmetry, + const DataT ih2, const DataT tempRatioZ, const std::array<DataT, Nr>& coefficient1, const std::array<DataT, Nr>& coefficient2, const std::array<DataT, Nr>& coefficient3, const std::array<DataT, Nr>& inverseCoefficient4) const +{ +#pragma omp parallel for num_threads(sNThreads) // parallising this loop is possible - but using more than 2 cores makes it slower - + for (int m = 0; m < tnPhi; ++m) { + int mp1 = m + 1; + int signPlus = 1; + int mm1 = m - 1; + int signMinus = 1; + + // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (symmetry == 1) { + if (mp1 > tnPhi - 1) { + mp1 = tnPhi - 2; + } + if (mm1 < 0) { + mm1 = 1; + } + } + // Anti-symmetry in phi + else if (symmetry == -1) { + if (mp1 > tnPhi - 1) { + mp1 = tnPhi - 2; + signPlus = -1; + } + if (mm1 < 0) { + mm1 = 1; + signMinus = -1; + } + } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi + if (mp1 > tnPhi - 1) { + mp1 = m + 1 - tnPhi; + } + if (mm1 < 0) { + mm1 = m - 1 + tnPhi; + } + } + + for (int j = 1; j < tnZColumn - 1; ++j) { + for (int i = 1; i < tnRRow - 1; ++i) { + residue(i, j, m) = ih2 * (coefficient2[i] * matricesCurrentV(i - 1, j, m) + tempRatioZ * (matricesCurrentV(i, j - 1, m) + matricesCurrentV(i, j + 1, m)) + coefficient1[i] * matricesCurrentV(i + 1, j, m) + + coefficient3[i] * (signPlus * matricesCurrentV(i, j, mp1) + signMinus * matricesCurrentV(i, j, mm1)) - inverseCoefficient4[i] * matricesCurrentV(i, j, m)) + + matricesCurrentCharge(i, j, m); + } // end cols + } // end Nr + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::interp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + // Do restrict 2 D for each slice + if (newPhiSlice == 2 * oldPhiSlice) { + for (int m = 0; m < newPhiSlice; m += 2) { + // assuming no symmetry + int mm = m * 0.5; + int mmPlus = mm + 1; + int mp1 = m + 1; + + // round + if (mmPlus > oldPhiSlice - 1) { + mmPlus = mm + 1 - oldPhiSlice; + } + if (mp1 > newPhiSlice - 1) { + mp1 = m + 1 - newPhiSlice; + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = matricesCurrentVC(iHalf, jHalf, mm); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) = 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) = 0.25 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf, mm)); + // point on line at phi direction + matricesCurrentV(i, j, mp1) = 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)) + (matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf, mm))); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)) + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm))); + // point at the center at phi direction + matricesCurrentV(i, j, mp1) = 0.125 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)) + + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm) + matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf + 1, mmPlus))); + } + } + } + + } else { +#pragma omp parallel for num_threads(sNThreads) // no change + for (int m = 0; m < newPhiSlice; ++m) { + interp2D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, m); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::interp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int iphi) const +{ + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + matricesCurrentV(i, j, iphi) = matricesCurrentVC(i * 0.5, j * 0.5, iphi); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, iphi) = 0.5 * (matricesCurrentVC(iHalf, jHalf, iphi) + matricesCurrentVC(iHalf, jHalf + 1, iphi)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, iphi) = 0.5 * (matricesCurrentVC(iHalf, jHalf, iphi) + matricesCurrentVC(iHalf + 1, jHalf, iphi)); + } + } + + // only if full + if (MGParameters::gtType == GridTransferType::Full) { + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, iphi) = 0.25 * (matricesCurrentVC(iHalf, jHalf, iphi) + matricesCurrentVC(iHalf, jHalf + 1, iphi) + matricesCurrentVC(iHalf + 1, jHalf, iphi) + matricesCurrentVC(iHalf + 1, jHalf + 1, iphi)); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::addInterp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + // Do restrict 2 D for each slice + if (newPhiSlice == 2 * oldPhiSlice) { + for (int m = 0; m < newPhiSlice; m += 2) { + // assuming no symmetry + int mm = m * 0.5; + int mmPlus = mm + 1; + int mp1 = m + 1; + + // round + if (mmPlus > (oldPhiSlice)-1) { + mmPlus = mm + 1 - (oldPhiSlice); + } + if (mp1 > (newPhiSlice)-1) { + mp1 = m + 1 - (newPhiSlice); + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += matricesCurrentVC(iHalf, jHalf, mm); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) += 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) += 0.25 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf, mm)); + // point on line at phi direction + matricesCurrentV(i, j, mp1) += 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)) + (matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf, mm))); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)) + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm))); + // point at the center at phi direction + matricesCurrentV(i, j, mp1) += 0.125 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)) + + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm) + matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf + 1, mmPlus))); + } + } + } + + } else { +#pragma omp parallel for num_threads(sNThreads) // no change + for (int m = 0; m < newPhiSlice; m++) { + addInterp2D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, m); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::addInterp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int tnPhi) const +{ + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + matricesCurrentVC(i * 0.5, j * 0.5, tnPhi); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = 0.5 * i; + const int jHalf = 0.5 * j; + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + 0.5 * (matricesCurrentVC(iHalf, jHalf, tnPhi) + matricesCurrentVC(iHalf, jHalf + 1, tnPhi)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = 0.5 * i; + const int jHalf = 0.5 * j; + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + 0.5 * (matricesCurrentVC(iHalf, jHalf, tnPhi) + matricesCurrentVC(iHalf + 1, jHalf, tnPhi)); + } + } + + // only if full + if (MGParameters::gtType == GridTransferType::Full) { + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = 0.5 * i; + const int jHalf = 0.5 * j; + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + 0.25 * (matricesCurrentVC(iHalf, jHalf, tnPhi) + matricesCurrentVC(iHalf, jHalf + 1, tnPhi) + matricesCurrentVC(iHalf + 1, jHalf, tnPhi) + matricesCurrentVC(iHalf + 1, jHalf + 1, tnPhi)); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::relax3D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int iPhi, const int symmetry, const DataT h2, + const DataT tempRatioZ, const std::array<DataT, Nr>& coefficient1, const std::array<DataT, Nr>& coefficient2, const std::array<DataT, Nr>& coefficient3, const std::array<DataT, Nr>& coefficient4) const +{ + // Gauss-Seidel (Read Black} + if (MGParameters::relaxType == RelaxType::GaussSeidel) { + // for each slice + for (int iPass = 1; iPass <= 2; ++iPass) { + const int msw = (iPass % 2) ? 1 : 2; + for (int m = 0; m < iPhi; ++m) { + const int jsw = ((msw + m) % 2) ? 1 : 2; + int mp1 = m + 1; + int signPlus = 1; + int mm1 = m - 1; + int signMinus = 1; + // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (symmetry == 1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + } + if (mm1 < 0) { + mm1 = 1; + } + } + // Anti-symmetry in phi + else if (symmetry == -1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + signPlus = -1; + } + if (mm1 < 0) { + mm1 = 1; + signMinus = -1; + } + } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi + if (mp1 > iPhi - 1) { + mp1 = m + 1 - iPhi; + } + if (mm1 < 0) { + mm1 = m - 1 + iPhi; + } + } + int isw = jsw; + for (int j = 1; j < tnZColumn - 1; ++j, isw = 3 - isw) { + for (int i = isw; i < tnRRow - 1; i += 2) { + (matricesCurrentV)(i, j, m) = (coefficient2[i] * (matricesCurrentV)(i - 1, j, m) + tempRatioZ * ((matricesCurrentV)(i, j - 1, m) + (matricesCurrentV)(i, j + 1, m)) + coefficient1[i] * (matricesCurrentV)(i + 1, j, m) + coefficient3[i] * (signPlus * (matricesCurrentV)(i, j, mp1) + signMinus * (matricesCurrentV)(i, j, mm1)) + (h2 * (matricesCurrentCharge)(i, j, m))) * coefficient4[i]; + } // end cols + } // end Nr + } // end phi + } // end sweep + } else if (MGParameters::relaxType == RelaxType::Jacobi) { + // for each slice + for (int m = 0; m < iPhi; ++m) { + int mp1 = m + 1; + int signPlus = 1; + int mm1 = m - 1; + int signMinus = 1; + + // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (symmetry == 1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + } + if (mm1 < 0) { + mm1 = 1; + } + } + // Anti-symmetry in phi + else if (symmetry == -1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + signPlus = -1; + } + if (mm1 < 0) { + mm1 = 1; + signMinus = -1; + } + } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi + if (mp1 > iPhi - 1) { + mp1 = m + 1 - iPhi; + } + if (mm1 < 0) { + mm1 = m - 1 + iPhi; + } + } + // Jacobian + for (int j = 1; j < tnZColumn - 1; ++j) { + for (int i = 1; i < tnRRow - 1; ++i) { + (matricesCurrentV)(i, j, m) = (coefficient2[i] * (matricesCurrentV)(i - 1, j, m) + tempRatioZ * ((matricesCurrentV)(i, j - 1, m) + (matricesCurrentV)(i, j + 1, m)) + coefficient1[i] * (matricesCurrentV)(i + 1, j, m) + coefficient3[i] * (signPlus * (matricesCurrentV)(i, j, mp1) + signMinus * (matricesCurrentV)(i, j, mm1)) + (h2 * (matricesCurrentCharge)(i, j, m))) * coefficient4[i]; + } // end cols + } // end Nr + } // end phi + } else { + // Case weighted Jacobi + // TODO + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::relax2D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT h2, const DataT tempFourth, const DataT tempRatio, + std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2) +{ + // Gauss-Seidel + const int iPhi = 0; + if (MGParameters::relaxType == RelaxType::GaussSeidel) { + int jsw = 1; + for (int iPass = 1; iPass <= 2; ++iPass, jsw = 3 - jsw) { + int isw = jsw; + for (int j = 1; j < tnZColumn - 1; ++j, isw = 3 - isw) { + for (int i = isw; i < tnRRow - 1; i += 2) { + matricesCurrentV(i, j, iPhi) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) + (h2 * matricesCurrentCharge(i, j, iPhi))); + } // end cols + } // end Nr + } // end pass red-black + } else if (MGParameters::relaxType == RelaxType::Jacobi) { + for (int j = 1; j < tnZColumn - 1; ++j) { + for (int i = 1; i < tnRRow - 1; ++i) { + matricesCurrentV(i, j, iPhi) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) + (h2 * matricesCurrentCharge(i, j, iPhi))); + } // end cols + } // end Nr + } else if (MGParameters::relaxType == RelaxType::WeightedJacobi) { + // Weighted Jacobi + // TODO + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::restrictBoundary3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + // in case of full 3d and the Nphi is also coarsening + if (2 * newPhiSlice == oldPhiSlice) { + for (int m = 0, mm = 0; m < newPhiSlice; ++m, mm += 2) { + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, m) = residue(0, jj, mm); + matricesCurrentCharge(tnRRow - 1, j, m) = residue((tnRRow - 1) * 2, jj, mm); + } + + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, m) = residue(ii, 0, mm); + matricesCurrentCharge(i, tnZColumn - 1, m) = residue(ii, (tnZColumn - 1) * 2, mm); + } + } // end phis + } else { + for (int m = 0; m < newPhiSlice; ++m) { + restrictBoundary2D(matricesCurrentCharge, residue, tnRRow, tnZColumn, m); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::restrictBoundary2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int tnPhi) const +{ + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, tnPhi) = residue(0, jj, tnPhi); + matricesCurrentCharge(tnRRow - 1, j, tnPhi) = residue((tnRRow - 1) * 2, jj, tnPhi); + } + + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, tnPhi) = residue(ii, 0, tnPhi); + matricesCurrentCharge(i, tnZColumn - 1, tnPhi) = residue(ii, (tnZColumn - 1) * 2, tnPhi); + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::restrict3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + if (2 * newPhiSlice == oldPhiSlice) { + int mm = 0; + for (int m = 0; m < newPhiSlice; m++, mm += 2) { + // assuming no symmetry + int mp1 = mm + 1; + int mm1 = mm - 1; + + if (mp1 > (oldPhiSlice)-1) { + mp1 = mm + 1 - (oldPhiSlice); + } + if (mm1 < 0) { + mm1 = mm - 1 + (oldPhiSlice); + } + + for (int i = 1, ii = 2; i < tnRRow - 1; ++i, ii += 2) { + for (int j = 1, jj = 2; j < tnZColumn - 1; ++j, jj += 2) { + + // at the same plane + const int iip1 = ii + 1; + const int iim1 = ii - 1; + const int jjp1 = jj + 1; + const int jjm1 = jj - 1; + const DataT s1 = residue(iip1, jj, mm) + residue(iim1, jj, mm) + residue(ii, jjp1, mm) + residue(ii, jjm1, mm) + residue(ii, jj, mp1) + residue(ii, jj, mm1); + + const DataT s2 = (residue(iip1, jjp1, mm) + residue(iip1, jjm1, mm) + residue(iip1, jj, mp1) + residue(iip1, jj, mm1)) + + (residue(iim1, jjm1, mm) + residue(iim1, jjp1, mm) + residue(iim1, jj, mp1) + residue(iim1, jj, mm1)) + + residue(ii, jjm1, mp1) + residue(ii, jjp1, mm1) + residue(ii, jjm1, mm1) + residue(ii, jjp1, mp1); + + const DataT s3 = (residue(iip1, jjp1, mp1) + residue(iip1, jjm1, mp1) + residue(iip1, jjp1, mm1) + residue(iip1, jjm1, mm1)) + + (residue(iim1, jjm1, mm1) + residue(iim1, jjp1, mm1) + residue(iim1, jjm1, mp1) + residue(iim1, jjp1, mp1)); + + matricesCurrentCharge(i, j, m) = 0.125 * residue(ii, jj, mm) + 0.0625 * s1 + 0.03125 * s2 + 0.015625 * s3; + } // end cols + } // end Nr + + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, m) = residue(0, jj, mm); + matricesCurrentCharge(tnRRow - 1, j, m) = residue((tnRRow - 1) * 2, jj, mm); + } + + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, m) = residue(ii, 0, mm); + matricesCurrentCharge(i, tnZColumn - 1, m) = residue(ii, (tnZColumn - 1) * 2, mm); + } + } // end phis + + } else { + for (int m = 0; m < newPhiSlice; ++m) { + restrict2D(matricesCurrentCharge, residue, tnRRow, tnZColumn, m); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::restrict2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int iphi) const +{ + for (int i = 1, ii = 2; i < tnRRow - 1; ++i, ii += 2) { + for (int j = 1, jj = 2; j < tnZColumn - 1; ++j, jj += 2) { + const int iip1 = ii + 1; + const int iim1 = ii - 1; + const int jjp1 = jj + 1; + const int jjm1 = jj - 1; + if (MGParameters::gtType == GridTransferType::Half) { + // half + matricesCurrentCharge(i, j, iphi) = 0.5 * residue(ii, jj, iphi) + 0.125 * (residue(iip1, jj, iphi) + residue(iim1, jj, iphi) + residue(ii, jjp1, iphi) + residue(ii, jjm1, iphi)); + } else if (MGParameters::gtType == GridTransferType::Full) { + matricesCurrentCharge(i, j, iphi) = 0.25 * residue(ii, jj, iphi) + 0.125 * (residue(iip1, jj, iphi) + residue(iim1, jj, iphi) + residue(ii, jjp1, iphi) + residue(ii, jjm1, iphi)) + + 0.0625 * (residue(iip1, jjp1, iphi) + residue(iim1, jjp1, iphi) + residue(iip1, jjm1, iphi) + residue(iim1, jjm1, iphi)); + } + } // end cols + } // end Nr + // boundary + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, iphi) = residue(0, jj, iphi); + matricesCurrentCharge(tnRRow - 1, j, iphi) = residue((tnRRow - 1) * 2, jj, iphi); + } + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, iphi) = residue(ii, 0, iphi); + matricesCurrentCharge(i, tnZColumn - 1, iphi) = residue(ii, (tnZColumn - 1) * 2, iphi); + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT PoissonSolver<DataT, Nz, Nr, Nphi>::getConvergenceError(const Vector& matricesCurrentV, Vector& prevArrayV) const +{ + std::vector<DataT> errorArr(prevArrayV.getNphi()); + + // subtract the two matrices + std::transform(prevArrayV.begin(), prevArrayV.end(), matricesCurrentV.begin(), prevArrayV.begin(), std::minus<DataT>()); + +#pragma omp parallel for num_threads(sNThreads) // parallising this loop is possible - but using more than 2 cores makes it slower - + for (unsigned int m = 0; m < prevArrayV.getNphi(); ++m) { + // square each entry in the vector and sum them up + const auto phiStep = prevArrayV.getNr() * prevArrayV.getNz(); // number of points in one phi slice + const auto start = prevArrayV.begin() + m * phiStep; + const auto end = start + phiStep; + errorArr[m] = std::inner_product(start, end, start, 0.); // inner product "Sum (matrix[a]*matrix[a])" + } + // return largest error + return *std::max_element(std::begin(errorArr), std::end(errorArr)); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::calcCoefficients(unsigned int from, unsigned int to, const DataT h, const DataT tempRatioZ, const DataT tempRatioPhi, std::array<DataT, Nr>& coefficient1, std::array<DataT, Nr>& coefficient2, std::array<DataT, Nr>& coefficient3, std::array<DataT, Nr>& coefficient4) const +{ + for (unsigned int i = from; i < to; ++i) { + const DataT radiusInv = 1. / (TPCParameters<DataT>::IFCRADIUS + i * h); + const DataT hRadiusTmp = h * 0.5 * radiusInv; + coefficient1[i] = 1.0 + hRadiusTmp; + coefficient2[i] = 1.0 - hRadiusTmp; + coefficient3[i] = tempRatioPhi * radiusInv * radiusInv; + coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void PoissonSolver<DataT, Nz, Nr, Nphi>::calcCoefficients2D(unsigned int from, unsigned int to, const DataT h, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2) const +{ + for (int i = from; i < to; ++i) { + DataT radiusInvHalf = h * 0.5 / (TPCParameters<DataT>::IFCRADIUS + i * h); + coefficient1[i] = 1.0 + radiusInvHalf; + coefficient2[i] = 1.0 - radiusInvHalf; + } +} + +template class o2::tpc::PoissonSolver<double, 17, 17, 90>; +template class o2::tpc::PoissonSolver<double, 33, 33, 180>; +template class o2::tpc::PoissonSolver<double, 65, 65, 180>; +template class o2::tpc::PoissonSolver<double, 129, 129, 180>; +template class o2::tpc::PoissonSolver<double, 257, 257, 180>; +template class o2::tpc::PoissonSolver<double, 257, 257, 360>; +template class o2::tpc::PoissonSolver<double, 129, 129, 1>; // for 2D Poisson Solver diff --git a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx new file mode 100644 index 0000000000000..9c881d28d327f --- /dev/null +++ b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx @@ -0,0 +1,1251 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SpaceCharge.cxx +/// \brief Definition of SpaceCharge class +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> +/// \date Aug 21, 2020 + +#include "TPCSpaceCharge/SpaceCharge.h" +#include "fmt/core.h" +#include "Framework/Logger.h" +#include <chrono> + +#ifdef WITH_OPENMP +#include <omp.h> +#endif + +templateClassImp(o2::tpc::SpaceCharge); + +using namespace o2::tpc; + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calculateDistortionsCorrections(const o2::tpc::Side side) +{ + using timer = std::chrono::high_resolution_clock; + using SC = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>; + if (!mIsChargeSet[side]) { + LOGP(ERROR, "the charge is not set!"); + } + + const std::array<std::string, 2> sglobalType{"local distortion/correction interpolator", "Electric fields"}; + const std::array<std::string, 2> sglobalDistType{"Standard method", "interpolation of global corrections"}; + const std::array<std::string, 2> sideName{"A", "C"}; + + LOGP(info, "====== starting calculation of distortions and corrections for Side {} ======", sideName[side]); + LOGP(info, "Using {} threads", getNThreads()); + + if (getGlobalDistCorrMethod() == SC::GlobalDistCorrMethod::LocalDistCorr) { + LOGP(info, "calculation of global distortions and corrections are performed by using: {}", sglobalType[0]); + } else { + LOGP(info, "calculation of global distortions and corrections are performed by using: {}", sglobalType[1]); + } + + if (getGlobalDistType() == SC::GlobalDistType::Fast) { + LOGP(info, "calculation of global distortions performed by following method: {}", sglobalDistType[1]); + } else if (getGlobalDistType() == SC::GlobalDistType::Standard) { + LOGP(info, "calculation of global distortions performed by following method: {}", sglobalDistType[0]); + } else { + LOGP(info, "skipping calculation of global distortions"); + } + + auto startTotal = timer::now(); + + auto start = timer::now(); + poissonSolver(side); + auto stop = timer::now(); + std::chrono::duration<float> time = stop - start; + LOGP(info, "Poisson Solver time: {}", time.count()); + + start = timer::now(); + calcEField(side); + stop = timer::now(); + time = stop - start; + LOGP(info, "electric field calculation time: {}", time.count()); + + const auto numEFields = getElectricFieldsInterpolator(side); + if (getGlobalDistType() == SC::GlobalDistType::Standard) { + start = timer::now(); + const auto dist = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>::Type::Distortions; + calcLocalDistortionsCorrections(dist, numEFields); // local distortion calculation + stop = timer::now(); + time = stop - start; + LOGP(info, "local distortions time: {}", time.count()); + } else { + LOGP(info, "skipping local distortions (not needed)"); + } + + start = timer::now(); + const auto corr = o2::tpc::SpaceCharge<DataT, Nz, Nr, Nphi>::Type::Corrections; + calcLocalDistortionsCorrections(corr, numEFields); // local correction calculation + stop = timer::now(); + time = stop - start; + LOGP(info, "local corrections time: {}", time.count()); + + start = timer::now(); + const auto lCorrInterpolator = getLocalCorrInterpolator(side); + (getGlobalDistCorrMethod() == SC::GlobalDistCorrMethod::LocalDistCorr) ? calcGlobalCorrections(lCorrInterpolator) : calcGlobalCorrections(numEFields); + stop = timer::now(); + time = stop - start; + LOGP(info, "global corrections time: {}", time.count()); + start = timer::now(); + if (getGlobalDistType() == SC::GlobalDistType::Fast) { + const auto globalCorrInterpolator = getGlobalCorrInterpolator(side); + calcGlobalDistWithGlobalCorrIterative(globalCorrInterpolator); + } else if (getGlobalDistType() == SC::GlobalDistType::Standard) { + const auto lDistInterpolator = getLocalDistInterpolator(side); + (getGlobalDistCorrMethod() == SC::GlobalDistCorrMethod::LocalDistCorr) ? calcGlobalDistortions(lDistInterpolator) : calcGlobalDistortions(numEFields); + } else { + } + + stop = timer::now(); + time = stop - start; + LOGP(info, "global distortions time: {}", time.count()); + + stop = timer::now(); + time = stop - startTotal; + LOGP(info, "everything is done. Total Time: {}", time.count()); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT SpaceCharge<DataT, Nz, Nr, Nphi>::regulateR(const DataT posR, const Side side) const +{ + const DataT minR = getRMin(side) - 4 * getGridSpacingR(side); + if (posR < minR) { + return minR; + } + const DataT maxR = getRMax(side) + 2 * getGridSpacingR(side); + if (posR > maxR) { + return maxR; + } + return posR; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setFromFile(TFile& file, const Side side) +{ + setDensityFromFile(file, side); + setPotentialFromFile(file, side); + setElectricFieldsFromFile(file, side); + setLocalDistortionsFromFile(file, side); + setLocalCorrectionsFromFile(file, side); + setGlobalDistortionsFromFile(file, side); + setGlobalCorrectionsFromFile(file, side); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setChargeDensityFromFormula(const AnalyticalFields<DataT>& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + mDensity[side](iZ, iR, iPhi) = formulaStruct.evalDensity(z, radius, phi); + } + } + } + mIsChargeSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setPotentialFromFormula(const AnalyticalFields<DataT>& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setPotentialBoundaryFromFormula(const AnalyticalFields<DataT>& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + const size_t iR = 0; + const DataT radius = getRVertex(iR, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + const size_t iR = Nr - 1; + const DataT radius = getRVertex(iR, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + const size_t iZ = 0; + const DataT z = getZVertex(iZ, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + const size_t iZ = Nz - 1; + const DataT z = getZVertex(iZ, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::poissonSolver(const Side side, const int maxIteration, const DataT stoppingConvergence, const int symmetry) +{ + ASolv::setConvergenceError(stoppingConvergence); + ASolv poissonSolver(mGrid3D[0]); + poissonSolver.poissonSolver3D(mPotential[side], mDensity[side], symmetry); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setEFieldFromFormula(const AnalyticalFields<DataT>& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT radius = getRVertex(iR, side); + const DataT z = getZVertex(iZ, side); + const DataT phi = getPhiVertex(iPhi, side); + mElectricFieldEr[side](iZ, iR, iPhi) = formulaStruct.evalEr(z, radius, phi); + mElectricFieldEz[side](iZ, iR, iPhi) = formulaStruct.evalEz(z, radius, phi); + mElectricFieldEphi[side](iZ, iR, iPhi) = formulaStruct.evalEphi(z, radius, phi); + } + } + } + mIsEfieldSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calcEField(const Side side) +{ +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const int symmetry = 0; + size_t tmpPlus = iPhi + 1; + int signPlus = 1; + int tmpMinus = static_cast<int>(iPhi - 1); + int signMinus = 1; + if (symmetry == 1 || symmetry == -1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (tmpPlus > Nphi - 1) { + if (symmetry == -1) { + signPlus = -1; + } + tmpPlus = Nphi - 2; + } + if (tmpMinus < 0) { + tmpMinus = 1; // SHOULD IT BE =0? + if (symmetry == -1) { + signMinus = -1; + } + } + } else { // No Symmetries in phi, no boundaries, the calculations is continuous across all phi + if (tmpPlus > Nphi - 1) { + tmpPlus = iPhi + 1 - Nphi; + } + if (tmpMinus < 0) { + tmpMinus = static_cast<int>(iPhi - 1 + Nphi); + } + } + + // for non-boundary V + for (size_t iR = 1; iR < Nr - 1; iR++) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 1; iZ < Nz - 1; iZ++) { + mElectricFieldEr[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ, iR + 1, iPhi) - mPotential[side](iZ, iR - 1, iPhi)) * static_cast<DataT>(0.5) * getInvSpacingR(side); // r direction + mElectricFieldEz[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ + 1, iR, iPhi) - mPotential[side](iZ - 1, iR, iPhi)) * static_cast<DataT>(0.5) * getInvSpacingZ(side); // z direction + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast<DataT>(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + + // for boundary-r + for (size_t iZ = 0; iZ < Nz; iZ++) { + mElectricFieldEr[side](iZ, 0, iPhi) = -1 * (-static_cast<DataT>(0.5) * mPotential[side](iZ, 2, iPhi) + 2 * mPotential[side](iZ, 1, iPhi) - static_cast<DataT>(1.5) * mPotential[side](iZ, 0, iPhi)) * getInvSpacingR(side); // forward difference + mElectricFieldEr[side](iZ, Nr - 1, iPhi) = -1 * (static_cast<DataT>(1.5) * mPotential[side](iZ, Nr - 1, iPhi) - 2 * mPotential[side](iZ, Nr - 2, iPhi) + static_cast<DataT>(0.5) * mPotential[side](iZ, Nr - 3, iPhi)) * getInvSpacingR(side); // backward difference + } + + for (size_t iR = 0; iR < Nr; iR += Nr - 1) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 1; iZ < Nz - 1; iZ++) { + mElectricFieldEz[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ + 1, iR, iPhi) - mPotential[side](iZ - 1, iR, iPhi)) * static_cast<DataT>(0.5) * getInvSpacingZ(side); // z direction + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast<DataT>(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + + // for boundary-z + for (size_t iR = 0; iR < Nr; ++iR) { + mElectricFieldEz[side](0, iR, iPhi) = -1 * (-static_cast<DataT>(0.5) * mPotential[side](2, iR, iPhi) + 2 * mPotential[side](1, iR, iPhi) - static_cast<DataT>(1.5) * mPotential[side](0, iR, iPhi)) * getInvSpacingZ(side); + mElectricFieldEz[side](Nz - 1, iR, iPhi) = -1 * (static_cast<DataT>(1.5) * mPotential[side](Nz - 1, iR, iPhi) - 2 * mPotential[side](Nz - 2, iR, iPhi) + static_cast<DataT>(0.5) * mPotential[side](Nz - 3, iR, iPhi)) * getInvSpacingZ(side); + } + + for (size_t iR = 1; iR < Nr - 1; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; iZ += Nz - 1) { + mElectricFieldEr[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ, iR + 1, iPhi) - mPotential[side](iZ, iR - 1, iPhi)) * static_cast<DataT>(0.5) * getInvSpacingR(side); // r direction + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast<DataT>(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + + // corner points for EPhi + for (size_t iR = 0; iR < Nr; iR += Nr - 1) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; iZ += Nz - 1) { + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast<DataT>(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + } + mIsEfieldSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calcGlobalDistWithGlobalCorrIterative(const DistCorrInterpolator<DataT, Nz, Nr, Nphi>& globCorr, const int maxIter, const DataT approachZ, const DataT approachR, const DataT approachPhi, const DataT diffCorr) +{ + const Side side = globCorr.getSide(); + +#pragma omp parallel for num_threads(sNThreads) + for (unsigned int iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (unsigned int iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (unsigned int iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + + unsigned int nearestiZ = iZ; + unsigned int nearestiR = iR; + unsigned int nearestiPhi = iPhi; + + DataT nearestZ = getZVertex(nearestiZ, side); + DataT nearestR = getRVertex(nearestiR, side); + DataT nearestPhi = getPhiVertex(nearestiPhi, side); + + // + //========================================================================================== + //==== start algorithm: use tricubic upsampling to numerically approach the query point ==== + //========================================================================================== + // + // 1. calculate difference from nearest point to query point with stepwidth factor x + // and approach the new point + // + DataT stepR = (radius - nearestR) * approachR; + DataT stepZ = (z - nearestZ) * approachZ; + DataT stepPhi = (phi - nearestPhi) * approachPhi; + + // needed to check for convergence + DataT lastCorrdR = std::numeric_limits<DataT>::max(); + DataT lastCorrdZ = std::numeric_limits<DataT>::max(); + DataT lastCorrdRPhi = std::numeric_limits<DataT>::max(); + + // interpolated global correction + DataT corrdR = 0; + DataT corrdRPhi = 0; + DataT corrdZ = 0; + + for (int iter = 0; iter < maxIter; ++iter) { + // 2. get new point coordinates + const DataT rCurrPos = getRVertex(nearestiR, side) + stepR; + const DataT zCurrPos = getZVertex(nearestiZ, side) + stepZ; + const DataT phiCurrPos = getPhiVertex(nearestiPhi, side) + stepPhi; + + // interpolate global correction at new point and calculate position of global correction + // corrdR = globCorr.evalSparsedR(zCurrPos, rCurrPos, phiCurrPos); + corrdR = globCorr.evaldR(zCurrPos, rCurrPos, phiCurrPos); + const DataT rNewPos = rCurrPos + corrdR; + + // const DataT corrPhi = globCorr.evalSparsedRPhi(zCurrPos, rCurrPos, phiCurrPos) / rCurrPos; + const DataT corrPhi = globCorr.evaldRPhi(zCurrPos, rCurrPos, phiCurrPos) / rCurrPos; + corrdRPhi = corrPhi * rNewPos; // normalize to new r coordinate + const DataT phiNewPos = phiCurrPos + corrPhi; + + // corrdZ = globCorr.evalSparsedZ(zCurrPos, rCurrPos, phiCurrPos); + corrdZ = globCorr.evaldZ(zCurrPos, rCurrPos, phiCurrPos); + const DataT zNewPos = zCurrPos + corrdZ; + + // approach desired coordinate + stepR += (radius - rNewPos) * approachR; + stepZ += (z - zNewPos) * approachZ; + stepPhi += (phi - phiNewPos) * approachPhi; + + // check for convergence + const DataT diffCorrdR = std::abs(corrdR - lastCorrdR); + const DataT diffCorrdRZ = std::abs(corrdZ - lastCorrdZ); + const DataT diffCorrdRPhi = std::abs(corrdRPhi - lastCorrdRPhi); + + // stop algorithm if converged + if (diffCorrdR < diffCorr && diffCorrdRZ < diffCorr && diffCorrdRPhi < diffCorr) { + break; + } + + lastCorrdR = corrdR; + lastCorrdZ = corrdZ; + lastCorrdRPhi = corrdRPhi; + } + // set global distortions if algorithm converged or iterations exceed max numbers of iterations + mGlobalDistdR[side](iZ, iR, iPhi) = -corrdR; + mGlobalDistdRPhi[side](iZ, iR, iPhi) = -corrdRPhi; + mGlobalDistdZ[side](iZ, iR, iPhi) = -corrdZ; + } + } + } + // set flag that global distortions are set to true + mIsGlobalDistSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +NumericalFields<DataT, Nz, Nr, Nphi> SpaceCharge<DataT, Nz, Nr, Nphi>::getElectricFieldsInterpolator(const Side side) const +{ + if (!mIsEfieldSet[side]) { + LOGP(warning, "============== E-Fields are not set! ==============\n"); + } + NumericalFields<DataT, Nz, Nr, Nphi> numFields(mElectricFieldEr[side], mElectricFieldEz[side], mElectricFieldEphi[side], mGrid3D[side], side); + return numFields; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DistCorrInterpolator<DataT, Nz, Nr, Nphi> SpaceCharge<DataT, Nz, Nr, Nphi>::getLocalDistInterpolator(const Side side) const +{ + if (!mIsLocalDistSet[side]) { + LOGP(warning, "============== local distortions not set! ==============\n"); + } + DistCorrInterpolator<DataT, Nz, Nr, Nphi> numFields(mLocalDistdR[side], mLocalDistdZ[side], mLocalDistdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DistCorrInterpolator<DataT, Nz, Nr, Nphi> SpaceCharge<DataT, Nz, Nr, Nphi>::getLocalCorrInterpolator(const Side side) const +{ + if (!mIsLocalCorrSet[side]) { + LOGP(warning, "============== local corrections not set! ==============\n"); + } + DistCorrInterpolator<DataT, Nz, Nr, Nphi> numFields(mLocalCorrdR[side], mLocalCorrdZ[side], mLocalCorrdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DistCorrInterpolator<DataT, Nz, Nr, Nphi> SpaceCharge<DataT, Nz, Nr, Nphi>::getGlobalDistInterpolator(const Side side) const +{ + if (!mIsGlobalDistSet[side]) { + LOGP(warning, "============== global distortions not set ==============\n"); + } + DistCorrInterpolator<DataT, Nz, Nr, Nphi> numFields(mGlobalDistdR[side], mGlobalDistdZ[side], mGlobalDistdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DistCorrInterpolator<DataT, Nz, Nr, Nphi> SpaceCharge<DataT, Nz, Nr, Nphi>::getGlobalCorrInterpolator(const Side side) const +{ + if (!mIsGlobalCorrSet[side]) { + LOGP(warning, "============== global corrections not set ==============\n"); + } + DistCorrInterpolator<DataT, Nz, Nr, Nphi> numFields(mGlobalCorrdR[side], mGlobalCorrdZ[side], mGlobalCorrdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +int SpaceCharge<DataT, Nz, Nr, Nphi>::dumpElectricFields(TFile& outf, const Side side) const +{ + if (!mIsEfieldSet[side]) { + LOGP(warning, "============== E-Fields are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int er = mElectricFieldEr[side].writeToFile(outf, fmt::format("fieldEr_side{}", sideName).data()); + const int ez = mElectricFieldEz[side].writeToFile(outf, fmt::format("fieldEz_side{}", sideName).data()); + const int ephi = mElectricFieldEphi[side].writeToFile(outf, fmt::format("fieldEphi_side{}", sideName).data()); + return er + ez + ephi; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setElectricFieldsFromFile(TFile& inpf, const Side side) +{ + const std::string sideName = getSideName(side); + mElectricFieldEr[side].initFromFile(inpf, fmt::format("fieldEr_side{}", sideName).data()); + mElectricFieldEz[side].initFromFile(inpf, fmt::format("fieldEz_side{}", sideName).data()); + mElectricFieldEphi[side].initFromFile(inpf, fmt::format("fieldEphi_side{}", sideName).data()); + mIsEfieldSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +int SpaceCharge<DataT, Nz, Nr, Nphi>::dumpGlobalDistortions(TFile& outf, const Side side) const +{ + if (!mIsGlobalDistSet[side]) { + LOGP(warning, "============== global distortions are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int er = mGlobalDistdR[side].writeToFile(outf, fmt::format("distR_side{}", sideName).data()); + const int ez = mGlobalDistdZ[side].writeToFile(outf, fmt::format("distZ_side{}", sideName).data()); + const int ephi = mGlobalDistdRPhi[side].writeToFile(outf, fmt::format("distRphi_side{}", sideName).data()); + return er + ez + ephi; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setGlobalDistortionsFromFile(TFile& inpf, const Side side) +{ + mIsGlobalDistSet[side] = true; + const std::string sideName = getSideName(side); + mGlobalDistdR[side].initFromFile(inpf, fmt::format("distR_side{}", sideName).data()); + mGlobalDistdZ[side].initFromFile(inpf, fmt::format("distZ_side{}", sideName).data()); + mGlobalDistdRPhi[side].initFromFile(inpf, fmt::format("distRphi_side{}", sideName).data()); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +int SpaceCharge<DataT, Nz, Nr, Nphi>::dumpGlobalCorrections(TFile& outf, const Side side) const +{ + if (!mIsGlobalCorrSet[side]) { + LOGP(warning, "============== global corrections are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int er = mGlobalCorrdR[side].writeToFile(outf, fmt::format("corrR_side{}", sideName).data()); + const int ez = mGlobalCorrdZ[side].writeToFile(outf, fmt::format("corrZ_side{}", sideName).data()); + const int ephi = mGlobalCorrdRPhi[side].writeToFile(outf, fmt::format("corrRPhi_side{}", sideName).data()); + return er + ez + ephi; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setGlobalCorrectionsFromFile(TFile& inpf, const Side side) +{ + mIsGlobalCorrSet[side] = true; + const std::string sideName = getSideName(side); + mGlobalCorrdR[side].initFromFile(inpf, fmt::format("corrR_side{}", sideName).data()); + mGlobalCorrdZ[side].initFromFile(inpf, fmt::format("corrZ_side{}", sideName).data()); + mGlobalCorrdRPhi[side].initFromFile(inpf, fmt::format("corrRPhi_side{}", sideName).data()); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +int SpaceCharge<DataT, Nz, Nr, Nphi>::dumpLocalCorrections(TFile& outf, const Side side) const +{ + if (!mIsLocalCorrSet[side]) { + LOGP(warning, "============== local corrections are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int lCorrdR = mLocalCorrdR[side].writeToFile(outf, fmt::format("lcorrR_side{}", sideName).data()); + const int lCorrdZ = mLocalCorrdZ[side].writeToFile(outf, fmt::format("lcorrZ_side{}", sideName).data()); + const int lCorrdRPhi = mLocalCorrdRPhi[side].writeToFile(outf, fmt::format("lcorrRPhi_side{}", sideName).data()); + return lCorrdR + lCorrdZ + lCorrdRPhi; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setLocalCorrectionsFromFile(TFile& inpf, const Side side) +{ + const std::string sideName = getSideName(side); + const bool lCorrdR = mLocalCorrdR[side].initFromFile(inpf, fmt::format("lcorrR_side{}", sideName).data()); + const bool lCorrdZ = mLocalCorrdZ[side].initFromFile(inpf, fmt::format("lcorrZ_side{}", sideName).data()); + const bool lCorrdRPhi = mLocalCorrdRPhi[side].initFromFile(inpf, fmt::format("lcorrRPhi_side{}", sideName).data()); + if (lCorrdR && lCorrdZ && lCorrdRPhi) { + mIsLocalCorrSet[side] = true; + } else { + mIsLocalCorrSet[side] = false; + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +int SpaceCharge<DataT, Nz, Nr, Nphi>::dumpLocalDistortions(TFile& outf, const Side side) const +{ + if (!mIsLocalDistSet[side]) { + LOGP(warning, "============== local distortions are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int lDistdR = mLocalDistdR[side].writeToFile(outf, fmt::format("ldistR_side{}", sideName).data()); + const int lDistdZ = mLocalDistdZ[side].writeToFile(outf, fmt::format("ldistZ_side{}", sideName).data()); + const int lDistdRPhi = mLocalDistdRPhi[side].writeToFile(outf, fmt::format("ldistRPhi_side{}", sideName).data()); + return lDistdR + lDistdZ + lDistdRPhi; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setLocalDistortionsFromFile(TFile& inpf, const Side side) +{ + const std::string sideName = getSideName(side); + const bool lDistdR = mLocalDistdR[side].initFromFile(inpf, fmt::format("ldistR_side{}", sideName).data()); + const bool lDistdZ = mLocalDistdZ[side].initFromFile(inpf, fmt::format("ldistZ_side{}", sideName).data()); + const bool lDistdRPhi = mLocalDistdRPhi[side].initFromFile(inpf, fmt::format("ldistRPhi_side{}", sideName).data()); + + if (lDistdR && lDistdZ && lDistdRPhi) { + mIsLocalDistSet[side] = true; + } else { + mIsLocalDistSet[side] = false; + } +} +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::fillChargeDensityFromFile(TFile& fInp, const char* name) +{ + const TH3* hisSCDensity3D = (TH3*)fInp.Get(name); + fillChargeDensityFromHisto(*hisSCDensity3D); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::fillChargeDensityFromHisto(const TH3& hisSCDensity3D) +{ + TH3D hRebin = rebinDensityHisto(hisSCDensity3D); + for (int side = Side::A; side < SIDES; ++side) { + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const size_t zBin = side == Side::A ? Nz + iZ + 1 : Nz - iZ; + mDensity[side](iZ, iR, iPhi) = hRebin.GetBinContent(iPhi + 1, iR + 1, zBin); + } + } + } + mIsChargeSet[side] = true; + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +TH3D SpaceCharge<DataT, Nz, Nr, Nphi>::rebinDensityHisto(const TH3& hOrig) const +{ + TH3D hRebin{}; + + const int nBinsPhiNew = Nphi; + const int nBinsRNew = Nr; + const int nBinsZNew = 2 * Nz; + + const auto phiLow = hOrig.GetXaxis()->GetBinLowEdge(1); + const auto phiUp = hOrig.GetXaxis()->GetBinUpEdge(hOrig.GetNbinsX()); + const auto rLow = hOrig.GetYaxis()->GetBinLowEdge(1); + const auto rUp = hOrig.GetYaxis()->GetBinUpEdge(hOrig.GetNbinsY()); + const auto zLow = hOrig.GetZaxis()->GetBinLowEdge(1); + const auto zUp = hOrig.GetZaxis()->GetBinUpEdge(hOrig.GetNbinsZ()); + hRebin.SetBins(nBinsPhiNew, phiLow, phiUp, nBinsRNew, rLow, rUp, nBinsZNew, zLow, zUp); + + for (int iBinPhi = 1; iBinPhi <= nBinsPhiNew; ++iBinPhi) { + const auto phiLowEdge = hRebin.GetXaxis()->GetBinLowEdge(iBinPhi); + const auto phiUpEdge = hRebin.GetXaxis()->GetBinUpEdge(iBinPhi); + + const int phiLowBinOrig = hOrig.GetXaxis()->FindBin(phiLowEdge); + const int phiUpBinOrig = hOrig.GetXaxis()->FindBin(phiUpEdge); + + // calculate the weights (area of original bin lies in the new bin / binwidthOrig) of the first and last bins + const auto binWidthPhiOrig = hOrig.GetXaxis()->GetBinWidth(phiLowBinOrig); + const auto lowerBinWeightPhi = std::abs(phiLowEdge - hOrig.GetXaxis()->GetBinUpEdge(phiLowBinOrig)) / binWidthPhiOrig; + const auto upperBinWeightPhi = std::abs(phiUpEdge - hOrig.GetXaxis()->GetBinLowEdge(phiUpBinOrig)) / binWidthPhiOrig; + + for (int iBinR = 1; iBinR <= nBinsRNew; ++iBinR) { + const auto rLowEdge = hRebin.GetYaxis()->GetBinLowEdge(iBinR); + const auto rUpEdge = hRebin.GetYaxis()->GetBinUpEdge(iBinR); + + const int rLowBinOrig = hOrig.GetYaxis()->FindBin(rLowEdge); + const int rUpBinOrig = hOrig.GetYaxis()->FindBin(rUpEdge); + + // calculate the weights (area of original bin lies in the new bin / binwidthOrig) of the first and last bins + const auto binWidthROrig = hOrig.GetYaxis()->GetBinWidth(rLowBinOrig); + const auto lowerBinWeightR = std::abs(rLowEdge - hOrig.GetYaxis()->GetBinUpEdge(rLowBinOrig)) / binWidthROrig; + const auto upperBinWeightR = std::abs(rUpEdge - hOrig.GetYaxis()->GetBinLowEdge(rUpBinOrig)) / binWidthROrig; + + for (int iBinZ = 1; iBinZ <= nBinsZNew; ++iBinZ) { + const auto zLowEdge = hRebin.GetZaxis()->GetBinLowEdge(iBinZ); + const auto zUpEdge = hRebin.GetZaxis()->GetBinUpEdge(iBinZ); + const auto zCenter = hRebin.GetZaxis()->GetBinCenter(iBinZ); + + int zLowBinOrig = hOrig.GetZaxis()->FindBin(zLowEdge); + int zUpBinOrig = hOrig.GetZaxis()->FindBin(zUpEdge); + const int currside = getSide(zCenter); // set the side of the current z-bin + // get the side of the lowest and uppest bin from the orig histo + const int sideLowOrig = getSide(hOrig.GetZaxis()->GetBinCenter(zLowBinOrig)); + const int sideUpOrig = getSide(hOrig.GetZaxis()->GetBinCenter(zUpBinOrig)); + + // make bounds/side check of the zLowBinOrig and zUpBinOrig bins. They must be on the same side as the currside!!! + if (currside != sideLowOrig && zLowBinOrig != zUpBinOrig) { + // if the lower bins from the orig histo are not on the same side as the rebinned increase the binnumber until they are on the same side + bool notequal = true; + do { + zLowBinOrig += 1; + if (zLowBinOrig > zUpBinOrig) { + LOGP(WARNING, "SOMETHING WENT WRONG: SETTING BINS TO: {}", zUpBinOrig); + zLowBinOrig = zUpBinOrig; + notequal = false; + } + const int sideTmp = getSide(hOrig.GetZaxis()->GetBinCenter(zLowBinOrig)); + if (sideTmp == currside) { + notequal = false; + } + } while (notequal); + } + + if (currside != sideUpOrig && zLowBinOrig != zUpBinOrig) { + // if the upper bins from the orig histo are not on the same side as the rebinned increase the binnumber until they are on the same side + bool notequal = true; + do { + zUpBinOrig -= 1; + if (zUpBinOrig < zLowBinOrig) { + LOGP(WARNING, "SOMETHING WENT WRONG: SETTING BINS TO: {}", zLowBinOrig); + zUpBinOrig = zLowBinOrig; + notequal = false; + } + const int sideTmp = getSide(hOrig.GetZaxis()->GetBinCenter(zUpBinOrig)); + if (sideTmp == currside) { + notequal = false; + } + } while (notequal); + } + + const auto binWidthZOrig = hOrig.GetZaxis()->GetBinWidth(zLowBinOrig); + const auto lowerBinWeightZ = std::abs(zLowEdge - hOrig.GetZaxis()->GetBinUpEdge(zLowBinOrig)) / binWidthZOrig; + const auto upperBinWeightZ = std::abs(zUpEdge - hOrig.GetZaxis()->GetBinLowEdge(zUpBinOrig)) / binWidthZOrig; + + // get the mean value of the original histogram of the found bin range + DataT sum = 0; + DataT sumW = 0; + for (int iPhi = phiLowBinOrig; iPhi <= phiUpBinOrig; ++iPhi) { + DataT weightPhi = 1; + if (iPhi == phiLowBinOrig) { + weightPhi = lowerBinWeightPhi; + } else if (iPhi == phiUpBinOrig) { + weightPhi = upperBinWeightPhi; + } + + for (int iR = rLowBinOrig; iR <= rUpBinOrig; ++iR) { + DataT weightR = 1; + if (iR == rLowBinOrig) { + weightR = lowerBinWeightR; + } else if (iR == rUpBinOrig) { + weightR = upperBinWeightR; + } + + for (int iZ = zLowBinOrig; iZ <= zUpBinOrig; ++iZ) { + DataT weightZ = 1; + if (iZ == zLowBinOrig) { + weightZ = lowerBinWeightZ; + } else if (iZ == zUpBinOrig) { + weightZ = upperBinWeightZ; + } + const auto val = hOrig.GetBinContent(iPhi, iR, iZ); + // if(val==0){ + // what to do now??? + // } + const auto totalWeight = weightPhi * weightR * weightZ; + sum += val * totalWeight; + sumW += totalWeight; + } + } + } + sum /= sumW; + hRebin.SetBinContent(iBinPhi, iBinR, iBinZ, sum); + } + } + } + return hRebin; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename ElectricFields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calcLocalDistortionsCorrections(const SpaceCharge<DataT, Nz, Nr, Nphi>::Type type, const ElectricFields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + // calculate local distortions/corrections for each vertex in the tpc +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz - 1; ++iZ) { + // set z coordinate depending on distortions or correction calculation + const DataT z0 = type == Type::Corrections ? getZVertex(iZ + 1, side) : getZVertex(iZ, side); + const DataT z1 = type == Type::Corrections ? getZVertex(iZ, side) : getZVertex(iZ + 1, side); + + DataT drTmp = 0; // local distortion dR + DataT dPhiTmp = 0; // local distortion dPhi (multiplication with R has to be done at the end) + DataT dzTmp = 0; // local distortion dZ + + const DataT stepSize = (z1 - z0) / sSteps; // the distortions are calculated by leting the elctron drift this distance in z direction + for (int iter = 0; iter < sSteps; ++iter) { + const DataT z0Tmp = (z0 + iter * stepSize + dzTmp); // starting z position + const DataT z1Tmp = (z0Tmp + stepSize); // electron drifts from z0Tmp to z1Tmp + + DataT ddR = 0; // distortion dR for drift from z0Tmp to z1Tmp + DataT ddPhi = 0; // distortion dPhi for drift from z0Tmp to z1Tmp + DataT ddZ = 0; // distortion dZ for drift from z0Tmp to z1Tmp + + const DataT radiusTmp = regulateR(radius + drTmp, side); // current radial position + const DataT phiTmp = regulatePhi(phi + dPhiTmp, side); // current phi position + + // calculate distortions/corrections + calcDistCorr(radiusTmp, phiTmp, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct, true); + + // add temp distortions to local distortions + drTmp += ddR; + dPhiTmp += ddPhi; + dzTmp += ddZ; + } + + // store local distortions/corrections + switch (type) { + case Type::Corrections: + mLocalCorrdR[side](iZ + 1, iR, iPhi) = drTmp; + mLocalCorrdRPhi[side](iZ + 1, iR, iPhi) = dPhiTmp * radius; + mLocalCorrdZ[side](iZ + 1, iR, iPhi) = dzTmp; + break; + + case Type::Distortions: + mLocalDistdR[side](iZ, iR, iPhi) = drTmp; + mLocalDistdRPhi[side](iZ, iR, iPhi) = dPhiTmp * radius; + mLocalDistdZ[side](iZ, iR, iPhi) = dzTmp; + break; + } + } + //extrapolate local distortion/correction to last/first bin using legendre polynoms with x0=0, x1=1, x2=2 and x=-1. This has to be done to ensure correct interpolation in the last,second last/first,second bin! + switch (type) { + case Type::Corrections: + mLocalCorrdR[side](0, iR, iPhi) = 3 * (mLocalCorrdR[side](1, iR, iPhi) - mLocalCorrdR[side](2, iR, iPhi)) + mLocalCorrdR[side](3, iR, iPhi); + mLocalCorrdRPhi[side](0, iR, iPhi) = 3 * (mLocalCorrdRPhi[side](1, iR, iPhi) - mLocalCorrdRPhi[side](2, iR, iPhi)) + mLocalCorrdRPhi[side](3, iR, iPhi); + mLocalCorrdZ[side](0, iR, iPhi) = 3 * (mLocalCorrdZ[side](1, iR, iPhi) - mLocalCorrdZ[side](2, iR, iPhi)) + mLocalCorrdZ[side](3, iR, iPhi); + break; + + case Type::Distortions: + mLocalDistdR[side](Nz - 1, iR, iPhi) = 3 * (mLocalDistdR[side](Nz - 2, iR, iPhi) - mLocalDistdR[side](Nz - 3, iR, iPhi)) + mLocalDistdR[side](Nz - 4, iR, iPhi); + mLocalDistdRPhi[side](Nz - 1, iR, iPhi) = 3 * (mLocalDistdRPhi[side](Nz - 2, iR, iPhi) - mLocalDistdRPhi[side](Nz - 3, iR, iPhi)) + mLocalDistdRPhi[side](Nz - 4, iR, iPhi); + mLocalDistdZ[side](Nz - 1, iR, iPhi) = 3 * (mLocalDistdZ[side](Nz - 2, iR, iPhi) - mLocalDistdZ[side](Nz - 3, iR, iPhi)) + mLocalDistdZ[side](Nz - 4, iR, iPhi); + break; + } + } + } + switch (type) { + case Type::Corrections: + mIsLocalCorrSet[side] = true; + break; + case Type::Distortions: + mIsLocalDistSet[side] = true; + break; + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Fields> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calcGlobalDistortions(const Fields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + const DataT stepSize = formulaStruct.getID() == 2 ? getGridSpacingZ(side) : getGridSpacingZ(side) / sSteps; // if one used local distortions then no smaller stepsize is needed. if electric fields are used then smaller stepsize can be used + // loop over tpc volume and let the electron drift from each vertex to the readout of the tpc +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi0 = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT r0 = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz - 1; ++iZ) { + const DataT z0 = getZVertex(iZ, side); // the electron starts at z0, r0, phi0 + DataT drDist = 0.0; // global distortion dR + DataT dPhiDist = 0.0; // global distortion dPhi (multiplication with R has to be done at the end) + DataT dzDist = 0.0; // global distortion dZ + int iter = 0; + + for (;;) { + const DataT z0Tmp = z0 + dzDist + iter * stepSize; // starting z position + const DataT z1Tmp = regulateZ(z0Tmp + stepSize, side); // electron drifts from z0Tmp to z1Tmp + const DataT radius = regulateR(r0 + drDist, side); // current radial position of the electron + const DataT phi = regulatePhi(phi0 + dPhiDist, side); // current phi position of the electron + + DataT ddR = 0; // distortion dR for drift from z0Tmp to z1Tmp + DataT ddPhi = 0; // distortion dPhi for drift from z0Tmp to z1Tmp + DataT ddZ = 0; // distortion dZ for drift from z0Tmp to z1Tmp + + // get the distortion from interpolation of local distortions or calculate distortions with the electric field + processGlobalDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct); + + // if one uses local distortions the interpolated value for the last bin has to be scaled. + // This has to be done because of the interpolated value is defined for a drift length of one z bin, but in the last bin the distance to the readout can be smaller than one z bin. + const bool checkReached = side == Side::A ? z1Tmp >= getZMax(side) : z1Tmp <= getZMax(side); + if (formulaStruct.getID() == 2 && checkReached) { + const DataT fac = std::abs((getZMax(side) - z0Tmp) * getInvSpacingZ(side)); + ddR *= fac; + ddZ *= fac; + ddPhi *= fac; + } + + // add local distortions to global distortions + drDist += ddR; + dPhiDist += ddPhi; + dzDist += ddZ; + + // set loop to exit if the readout is reached and approximate distortion of 'missing' (one never ends exactly on the readout: z1Tmp + ddZ != ZMAX) drift distance. + // approximation is done by the current calculated values of the distortions and scaled linear to the 'missing' distance. + if (checkReached) { + const DataT endPoint = z1Tmp + ddZ; + const DataT deltaZ = getZMax(side) - endPoint; // distance from last point to read out + const DataT diff = endPoint - z0Tmp; + const DataT fac = diff != 0 ? std::abs(deltaZ / diff) : 0; // approximate the distortions for the 'missing' distance deltaZ + drDist += ddR * fac; + dPhiDist += ddPhi * fac; + dzDist += ddZ * fac; + break; + } + ++iter; + } + // store global distortions + mGlobalDistdR[side](iZ, iR, iPhi) = drDist; + mGlobalDistdRPhi[side](iZ, iR, iPhi) = dPhiDist * r0; + mGlobalDistdZ[side](iZ, iR, iPhi) = dzDist; + } + } + } + // set flag that global distortions are set to true + mIsGlobalDistSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +template <typename Formulas> +void SpaceCharge<DataT, Nz, Nr, Nphi>::calcGlobalCorrections(const Formulas& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + const int iSteps = formulaStruct.getID() == 2 ? 1 : sSteps; // if one used local corrections no step width is needed. since it is already used for calculation of the local corrections + const DataT stepSize = -getGridSpacingZ(side) / iSteps; + // loop over tpc volume and let the electron drift from each vertex to the readout of the tpc +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi0 = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + + const DataT r0 = getRVertex(iR, side); + DataT drCorr = 0; + DataT dPhiCorr = 0; + DataT dzCorr = 0; + + // start at the readout and follow electron towards central electrode + for (size_t iZ = Nz - 1; iZ >= 1; --iZ) { + const DataT z0 = getZVertex(iZ, side); // the electron starts at z0, r0, phi0 + // flag which is set when the central electrode is reached. if the central electrode is reached the calculation of the global corrections is aborted and the value set is the last calculated value. + bool centralElectrodeReached = false; + for (int iter = 0; iter < iSteps; ++iter) { + if (centralElectrodeReached) { + break; + } + const DataT radius = regulateR(r0 + drCorr, side); // current radial position of the electron + const DataT phi = regulatePhi(phi0 + dPhiCorr, side); // current phi position of the electron + const DataT z0Tmp = z0 + dzCorr + iter * stepSize; // starting z position + const DataT z1Tmp = regulateZ(z0Tmp + stepSize, side); // follow electron from z0Tmp to z1Tmp + DataT ddR = 0; // distortion dR for z0Tmp to z1Tmp + DataT ddPhi = 0; // distortion dPhi for z0Tmp to z1Tmp + DataT ddZ = 0; // distortion dZ for z0Tmp to z1Tmp + + // get the distortion from interpolation of local distortions or calculate distortions with the electric field + processGlobalDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct); + + // if one uses local corrections the interpolated value for the first bin has to be scaled. + // This has to be done because of the interpolated value is defined for a drift length of one z bin, but in the first bin the distance to the readout can be smaller than one z bin. + centralElectrodeReached = getSign(side) * z1Tmp <= getZMin(side); + if (formulaStruct.getID() == 2 && centralElectrodeReached) { + const DataT fac = (z0Tmp - getZMin(side)) * getInvSpacingZ(side); + ddR *= fac; + ddZ *= fac; + ddPhi *= fac; + } + + // add local corrections to global corrections + drCorr += ddR; + dPhiCorr += ddPhi; + dzCorr += ddZ; + + // set loop to exit if the central electrode is reached and approximate correction of 'missing' (one never ends exactly on the central electrode: z1Tmp + ddZ != ZMIN) distance. + // approximation is done by the current calculated values of the corrections and scaled linear to the 'missing' distance deltaZ. (NOT TESTED) + if (centralElectrodeReached) { + const DataT endPoint = z1Tmp + ddZ; + const DataT deltaZ = endPoint - getZMin(side); + const DataT diff = z0Tmp - endPoint; + const DataT fac = diff != 0 ? deltaZ / diff : 0; // approximate the distortions for the 'missing' distance deltaZ + drCorr += ddR * fac; + dPhiCorr += ddPhi * fac; + dzCorr += ddZ * fac; + break; + } + } + // store global corrections + mGlobalCorrdR[side](iZ - 1, iR, iPhi) = drCorr; + mGlobalCorrdRPhi[side](iZ - 1, iR, iPhi) = dPhiCorr * r0; + mGlobalCorrdZ[side](iZ - 1, iR, iPhi) = dzCorr; + } + } + } + // set flag that global corrections are set to true + mIsGlobalCorrSet[side] = true; +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::correctElectron(GlobalPosition3D& point) +{ + DataT corrX{}; + DataT corrY{}; + DataT corrZ{}; + const Side side = getSide(point.Z()); + + // get the distortions for input coordinate + getCorrections(point.X(), point.Y(), point.Z(), side, corrX, corrY, corrZ); + + // set distorted coordinates + point.SetXYZ(point.X() + corrX, point.Y() + corrY, point.Y() + corrY); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::distortElectron(GlobalPosition3D& point) const +{ + DataT distX{}; + DataT distY{}; + DataT distZ{}; + const Side side = getSide(point.Z()); + // get the distortions for input coordinate + getDistortions(point.X(), point.Y(), point.Z(), side, distX, distY, distZ); + + // set distorted coordinates + point.SetXYZ(point.X() + distX, point.Y() + distY, point.Z() + distZ); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT SpaceCharge<DataT, Nz, Nr, Nphi>::getChargeCyl(const DataT z, const DataT r, const DataT phi, const Side side) const +{ + return mInterpolatorDensity[side](z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT SpaceCharge<DataT, Nz, Nr, Nphi>::getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const +{ + return mInterpolatorPotential[side](z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getElectricFieldsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& eZ, DataT& eR, DataT& ePhi) const +{ + eZ = mInterpolatorEField[side].evalEz(z, r, phi); + eR = mInterpolatorEField[side].evalEr(z, r, phi); + ePhi = mInterpolatorEField[side].evalEphi(z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getLocalCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& lcorrZ, DataT& lcorrR, DataT& lcorrRPhi) const +{ + lcorrZ = mInterpolatorLocalCorr[side].evaldZ(z, r, phi); + lcorrR = mInterpolatorLocalCorr[side].evaldR(z, r, phi); + lcorrRPhi = mInterpolatorLocalCorr[side].evaldRPhi(z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& corrZ, DataT& corrR, DataT& corrRPhi) const +{ + corrZ = mInterpolatorGlobalCorr[side].evaldZ(z, r, phi); + corrR = mInterpolatorGlobalCorr[side].evaldR(z, r, phi); + corrRPhi = mInterpolatorGlobalCorr[side].evaldRPhi(z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getCorrections(const DataT x, const DataT y, const DataT z, const Side side, DataT& corrX, DataT& corrY, DataT& corrZ) const +{ + // convert cartesian to polar + const DataT radius = getRadiusFromCartesian(x, y); + const DataT phi = getPhiFromCartesian(x, y); + + DataT corrR{}; + DataT corrRPhi{}; + getCorrectionsCyl(z, radius, phi, side, corrZ, corrR, corrRPhi); + + // Calculate corrected position + const DataT radiusCorr = radius + corrR; + const DataT phiCorr = phi + corrRPhi / radius; + + corrX = getXFromPolar(radiusCorr, phiCorr) - x; // difference between corrected and original x coordinate + corrY = getYFromPolar(radiusCorr, phiCorr) - y; // difference between corrected and original y coordinate +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getLocalDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& ldistZ, DataT& ldistR, DataT& ldistRPhi) const +{ + ldistZ = mInterpolatorLocalDist[side].evaldZ(z, r, phi); + ldistR = mInterpolatorLocalDist[side].evaldR(z, r, phi); + ldistRPhi = mInterpolatorLocalDist[side].evaldRPhi(z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& distZ, DataT& distR, DataT& distRPhi) const +{ + distZ = mInterpolatorGlobalDist[side].evaldZ(z, r, phi); + distR = mInterpolatorGlobalDist[side].evaldR(z, r, phi); + distRPhi = mInterpolatorGlobalDist[side].evaldRPhi(z, r, phi); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::getDistortions(const DataT x, const DataT y, const DataT z, const Side side, DataT& distX, DataT& distY, DataT& distZ) const +{ + // convert cartesian to polar + const DataT radius = getRadiusFromCartesian(x, y); + const DataT phi = getPhiFromCartesian(x, y); + + DataT distR{}; + DataT distRPhi{}; + getDistortionsCyl(z, radius, phi, side, distZ, distR, distRPhi); + + // Calculate distorted position + const DataT radiusDist = radius + distR; + const DataT phiDist = phi + distRPhi / radius; + + distX = getXFromPolar(radiusDist, phiDist) - x; // difference between distorted and original x coordinate + distY = getYFromPolar(radiusDist, phiDist) - y; // difference between distorted and original y coordinate +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::init() +{ + using timer = std::chrono::high_resolution_clock; + if (!mInitLookUpTables) { + auto start = timer::now(); + auto o2field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); + const float bzField = o2field->solenoidField(); // magnetic field in kGauss + /// TODO is there a faster way to get the drift velocity + auto& gasParam = ParameterGas::Instance(); + float vDrift = gasParam.DriftV; // drift velocity in cm/us + /// TODO fix hard coded values (ezField, t1, t2): export to Constants.h or get from somewhere? + const float t1 = 1.; + const float t2 = 1.; + /// TODO use this parameterization or fixed value(s) from Magboltz calculations? + const float omegaTau = -10. * bzField * vDrift / std::abs(getEzField(Side::A)); + setOmegaTauT1T2(omegaTau, t1, t2); + if (mUseInitialSCDensity) { + LOG(WARNING) << "mUseInitialSCDensity" << mUseInitialSCDensity; + calculateDistortionsCorrections(Side::A); + calculateDistortionsCorrections(Side::C); + mInitLookUpTables = true; + } + auto stop = timer::now(); + std::chrono::duration<float> time = stop - start; + LOGP(info, "Total Time Distortions and Corrections for A and C Side: {}", time.count()); + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void SpaceCharge<DataT, Nz, Nr, Nphi>::setDistortionLookupTables(const DataContainer& distdZ, const DataContainer& distdR, const DataContainer& distdRPhi, const Side side) +{ + mGlobalDistdR[side] = distdR; + mGlobalDistdZ[side] = distdZ; + mGlobalDistdRPhi[side] = distdRPhi; + mIsGlobalDistSet[side] = true; +} + +using DataTD = double; +template class o2::tpc::SpaceCharge<DataTD, 17, 17, 90>; +template class o2::tpc::SpaceCharge<DataTD, 33, 33, 180>; +template class o2::tpc::SpaceCharge<DataTD, 65, 65, 180>; +template class o2::tpc::SpaceCharge<DataTD, 129, 129, 180>; +template class o2::tpc::SpaceCharge<DataTD, 257, 257, 180>; +template class o2::tpc::SpaceCharge<DataTD, 257, 257, 360>; + +// 129*129*180 +using NumFields129D = NumericalFields<DataTD, 129, 129, 180>; +using AnaFields129D = AnalyticalFields<DataTD>; +using DistCorrInterp129D = DistCorrInterpolator<DataTD, 129, 129, 180>; +using O2TPCSpaceCharge3DCalc129D = SpaceCharge<DataTD, 129, 129, 180>; + +template void O2TPCSpaceCharge3DCalc129D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc129D::Type, const NumFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc129D::Type, const AnaFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalCorrections(const NumFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalCorrections(const AnaFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalCorrections(const DistCorrInterp129D&); + +template void O2TPCSpaceCharge3DCalc129D::calcGlobalDistortions(const NumFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalDistortions(const AnaFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalDistortions(const DistCorrInterp129D&); + +// 33*33*180 +using NumFields33D = NumericalFields<DataTD, 33, 33, 180>; +using AnaFields33D = AnalyticalFields<DataTD>; +using DistCorrInterp33D = DistCorrInterpolator<DataTD, 33, 33, 180>; +using O2TPCSpaceCharge3DCalc33D = SpaceCharge<DataTD, 33, 33, 180>; + +template void O2TPCSpaceCharge3DCalc33D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc33D::Type, const NumFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc33D::Type, const AnaFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalCorrections(const NumFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalCorrections(const AnaFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalCorrections(const DistCorrInterp33D&); + +template void O2TPCSpaceCharge3DCalc33D::calcGlobalDistortions(const NumFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalDistortions(const AnaFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalDistortions(const DistCorrInterp33D&); + +// 65*65*180 +using NumFields65D = NumericalFields<DataTD, 65, 65, 180>; +using AnaFields65D = AnalyticalFields<DataTD>; +using DistCorrInterp65D = DistCorrInterpolator<DataTD, 65, 65, 180>; +using O2TPCSpaceCharge3DCalc65D = SpaceCharge<DataTD, 65, 65, 180>; + +template void O2TPCSpaceCharge3DCalc65D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc65D::Type, const NumFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc65D::Type, const AnaFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalCorrections(const NumFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalCorrections(const AnaFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalCorrections(const DistCorrInterp65D&); + +template void O2TPCSpaceCharge3DCalc65D::calcGlobalDistortions(const NumFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalDistortions(const AnaFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalDistortions(const DistCorrInterp65D&); + +// 17*17*90 +using NumFields17D = NumericalFields<DataTD, 17, 17, 90>; +using AnaFields17D = AnalyticalFields<DataTD>; +using DistCorrInterp17D = DistCorrInterpolator<DataTD, 17, 17, 90>; +using O2TPCSpaceCharge3DCalc17D = SpaceCharge<DataTD, 17, 17, 90>; + +template void O2TPCSpaceCharge3DCalc17D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc17D::Type, const NumFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc17D::Type, const AnaFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalCorrections(const NumFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalCorrections(const AnaFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalCorrections(const DistCorrInterp17D&); + +template void O2TPCSpaceCharge3DCalc17D::calcGlobalDistortions(const NumFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalDistortions(const AnaFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalDistortions(const DistCorrInterp17D&); + +// 257*257*180 +using NumFields257D = NumericalFields<DataTD, 257, 257, 180>; +using AnaFields257D = AnalyticalFields<DataTD>; +using DistCorrInterp257D = DistCorrInterpolator<DataTD, 257, 257, 180>; +using O2TPCSpaceCharge3DCalc257D = SpaceCharge<DataTD, 257, 257, 180>; + +template void O2TPCSpaceCharge3DCalc257D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257D::Type, const NumFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257D::Type, const AnaFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalCorrections(const NumFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalCorrections(const AnaFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalCorrections(const DistCorrInterp257D&); + +template void O2TPCSpaceCharge3DCalc257D::calcGlobalDistortions(const NumFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalDistortions(const AnaFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalDistortions(const DistCorrInterp257D&); + +// 257*257*360 +using NumFields257360D = NumericalFields<DataTD, 257, 257, 360>; +using AnaFields257360D = AnalyticalFields<DataTD>; +using DistCorrInterp257360D = DistCorrInterpolator<DataTD, 257, 257, 360>; +using O2TPCSpaceCharge3DCalc257360D = SpaceCharge<DataTD, 257, 257, 360>; + +template void O2TPCSpaceCharge3DCalc257360D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257360D::Type, const NumFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257360D::Type, const AnaFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalCorrections(const NumFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalCorrections(const AnaFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalCorrections(const DistCorrInterp257360D&); + +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalDistortions(const NumFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalDistortions(const AnaFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalDistortions(const DistCorrInterp257360D&); diff --git a/Detectors/TPC/spacecharge/src/TPCSpacechargeLinkDef.h b/Detectors/TPC/spacecharge/src/TPCSpacechargeLinkDef.h new file mode 100644 index 0000000000000..1dd77a5bcafd9 --- /dev/null +++ b/Detectors/TPC/spacecharge/src/TPCSpacechargeLinkDef.h @@ -0,0 +1,81 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TPCSpacechargeLinkDef.h +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ nestedclasses; +#pragma link C++ nestedtypedef; + +#pragma link C++ class o2::tpc::MGParameters + ; +#pragma link C++ class o2::tpc::TPCParameters < double> + ; +#pragma link C++ class o2::tpc::AnalyticalFields < double> + ; + +// 257*257*360 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 257, 257, 360> + ; + +// 257*257*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 257, 257, 180> + ; + +// 129*129*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 129, 129, 180> + ; + +// 65*65*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 65, 65, 180> + ; + +// 33*33*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 33, 33, 180> + ; + +// 17*17*90 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 17, 17, 90> + ; + +#endif diff --git a/Detectors/TPC/spacecharge/test/testO2TPCPoissonSolver.cxx b/Detectors/TPC/spacecharge/test/testO2TPCPoissonSolver.cxx new file mode 100644 index 0000000000000..61d615b252d99 --- /dev/null +++ b/Detectors/TPC/spacecharge/test/testO2TPCPoissonSolver.cxx @@ -0,0 +1,240 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 testPoissonSolver.cxx +/// \brief this task tests the poisson solver +/// +/// \author Matthias Kleiner <mkleiner@ikf.uni-frankfurt.de> + +#define BOOST_TEST_MODULE Test TPC O2TPCSpaceCharge3DCalc class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include "TPCSpaceCharge/PoissonSolver.h" +#include "TPCSpaceCharge/SpaceChargeHelpers.h" + +namespace o2 +{ +namespace tpc +{ + +using DataT = double; // using float actually takes alot longer than double (doesnt converge when using float) +static constexpr DataT TOLERANCE = 3; // relative tolerance for 3D (maximum large error is at phi=90 since there the potential is 0!) +static constexpr DataT TOLERANCE2D = 8.5; // relative tolerance for 2D TODO check why the difference between numerical and analyticial is larger than for 3D! +static constexpr DataT ABSTOLERANCE = 0.01; // absolute tolerance is taken at small values near 0 +static constexpr int NR = 129; // grid in r +static constexpr int NZ = 129; // grid in z +static constexpr int NPHI = 180; // grid in phi + +/// Get phi vertex position for index in phi direction +/// \param indexPhi index in phi direction +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT getPhiVertex(const size_t indexPhi, const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>& grid) +{ + return grid.getZVertex(indexPhi); +} + +/// Get r vertex position for index in r direction +/// \param indexR index in r direction +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT getRVertex(const size_t indexR, const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>& grid) +{ + return grid.getYVertex(indexR); +} + +/// Get z vertex position for index in z direction +/// \param indexZ index in z direction +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +DataT getZVertex(const size_t indexZ, const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>& grid) +{ + return grid.getXVertex(indexZ); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void setChargeDensityFromFormula(const AnalyticalFields<DataT>& formulas, const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>& grid, o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& density) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex<DataT, Nz, Nr, Nphi>(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex<DataT, Nz, Nr, Nphi>(iR, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex<DataT, Nz, Nr, Nphi>(iZ, grid); + density(iZ, iR, iPhi) = formulas.evalDensity(z, radius, phi); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void setPotentialFromFormula(const AnalyticalFields<DataT>& formulas, const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>& grid, o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& potential) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex<DataT, Nz, Nr, Nphi>(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex<DataT, Nz, Nr, Nphi>(iR, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex<DataT, Nz, Nr, Nphi>(iZ, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void setPotentialBoundaryFromFormula(const AnalyticalFields<DataT>& formulas, const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi>& grid, o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& potential) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex<DataT, Nz, Nr, Nphi>(iPhi, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex<DataT, Nz, Nr, Nphi>(iZ, grid); + const size_t iR = 0; + const DataT radius = getRVertex<DataT, Nz, Nr, Nphi>(iR, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex<DataT, Nz, Nr, Nphi>(iPhi, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex<DataT, Nz, Nr, Nphi>(iZ, grid); + const size_t iR = Nr - 1; + const DataT radius = getRVertex<DataT, Nz, Nr, Nphi>(iR, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex<DataT, Nz, Nr, Nphi>(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex<DataT, Nz, Nr, Nphi>(iR, grid); + const size_t iZ = 0; + const DataT z = getZVertex<DataT, Nz, Nr, Nphi>(iZ, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex<DataT, Nz, Nr, Nphi>(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex<DataT, Nz, Nr, Nphi>(iR, grid); + const size_t iZ = Nz - 1; + const DataT z = getZVertex<DataT, Nz, Nr, Nphi>(iZ, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void testAlmostEqualArray(o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& analytical, o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& numerical) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + if (std::fabs(analytical(iZ, iR, iPhi)) < ABSTOLERANCE) { + BOOST_CHECK_SMALL(numerical(iZ, iR, iPhi) - analytical(iZ, iR, iPhi), ABSTOLERANCE); + } else { + BOOST_CHECK_CLOSE(numerical(iZ, iR, iPhi), analytical(iZ, iR, iPhi), TOLERANCE); + } + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void testAlmostEqualArray2D(o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& analytical, o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>& numerical) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + if (std::fabs(analytical(iZ, iR, iPhi)) < ABSTOLERANCE) { + BOOST_CHECK_SMALL(numerical(iZ, iR, iPhi) - analytical(iZ, iR, iPhi), ABSTOLERANCE); + } else { + BOOST_CHECK_CLOSE(numerical(iZ, iR, iPhi), analytical(iZ, iR, iPhi), TOLERANCE2D); + } + } + } + } +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void poissonSolver3D() +{ + using GridProp = GridProperties<DataT, Nr, Nz, Nphi>; + const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi> grid3D{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}; + + using DataContainer = o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>; + DataContainer potentialNumerical{}; + DataContainer potentialAnalytical{}; + DataContainer charge{}; + + const o2::tpc::AnalyticalFields<DataT> analyticalFields; + // set the boudnary and charge for numerical poisson solver + setChargeDensityFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, charge); + setPotentialBoundaryFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, potentialNumerical); + + // set analytical potential + setPotentialFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, potentialAnalytical); + + //calculate numerical potential + PoissonSolver<DataT, Nz, Nr, Nphi> poissonSolver(grid3D); + const int symmetry = 0; + poissonSolver.poissonSolver3D(potentialNumerical, charge, symmetry); + + // compare numerical with analytical solution of the potential + testAlmostEqualArray<DataT, Nz, Nr, Nphi>(potentialAnalytical, potentialNumerical); +} + +template <typename DataT, size_t Nz, size_t Nr, size_t Nphi> +void poissonSolver2D() +{ + using GridProp = GridProperties<DataT, Nr, Nz, Nphi>; + const o2::tpc::RegularGrid3D<DataT, Nz, Nr, Nphi> grid3D{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}; + + using DataContainer = o2::tpc::DataContainer3D<DataT, Nz, Nr, Nphi>; + DataContainer potentialNumerical{}; + DataContainer potentialAnalytical{}; + DataContainer charge{}; + + // set the boudnary and charge for numerical poisson solver + const o2::tpc::AnalyticalFields<DataT> analyticalFields; + setChargeDensityFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, charge); + setPotentialBoundaryFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, potentialNumerical); + + // set analytical potential + setPotentialFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, potentialAnalytical); + + //calculate numerical potential + PoissonSolver<DataT, Nz, Nr, Nphi> poissonSolver(grid3D); + poissonSolver.poissonSolver2D(potentialNumerical, charge); + + // compare numerical with analytical solution of the potential + testAlmostEqualArray2D<DataT, Nz, Nr, Nphi>(potentialAnalytical, potentialNumerical); +} + +BOOST_AUTO_TEST_CASE(PoissonSolver3D_test) +{ + o2::tpc::MGParameters::isFull3D = true; //3D + poissonSolver3D<DataT, NZ, NR, NPHI>(); +} + +BOOST_AUTO_TEST_CASE(PoissonSolver3D2D_test) +{ + o2::tpc::MGParameters::isFull3D = false; // 3D2D + poissonSolver3D<DataT, NZ, NR, NPHI>(); +} + +BOOST_AUTO_TEST_CASE(PoissonSolver2D_test) +{ + const int Nphi = 1; + poissonSolver2D<DataT, NZ, NR, Nphi>(); +} + +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 735b3c9fee684..720c31d542017 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -23,10 +23,12 @@ o2_add_library(TPCWorkflow src/LinkZSToDigitsSpec.cxx src/TPCSectorCompletionPolicy.cxx src/ZSSpec.cxx + src/CalibProcessingHelper.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC O2::DPLUtils O2::TPCReconstruction - O2::TPCCalibration O2::TPCSimulation) + O2::TPCCalibration O2::TPCSimulation + O2::DetectorsCalibration) o2_add_executable(reco-workflow COMPONENT_NAME tpc @@ -38,6 +40,11 @@ o2_add_executable(raw-to-digits-workflow SOURCES src/tpc-raw-to-digits-workflow.cxx PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) +o2_add_executable(calib-pedestal + COMPONENT_NAME tpc + SOURCES src/tpc-calib-pedestal.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + o2_add_test(workflow COMPONENT_NAME tpc LABELS tpc workflow @@ -46,7 +53,7 @@ o2_add_test(workflow ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) if(GPUCA_EVENT_DISPLAY OR - (OPENGL_FOUND AND GLFW_FOUND AND GLEW_FOUND AND OPENGL_GLU_FOUND + (OPENGL_FOUND AND GLFW_FOUND AND TARGET AliceO2::DebugGUI AND OPENGL_GLU_FOUND AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")) target_compile_definitions(${targetName} PRIVATE GPUCA_BUILD_EVENT_DISPLAY) endif() diff --git a/Detectors/TPC/workflow/README.md b/Detectors/TPC/workflow/README.md index a7210e008e46c..85d3fdd4e3c60 100644 --- a/Detectors/TPC/workflow/README.md +++ b/Detectors/TPC/workflow/README.md @@ -146,3 +146,58 @@ bz= magnetic field * implement configuration from the GRP ## Open questions + +## Calibration workflows + +### Pedestal calibration +#### Options +```bash +--direct-file-dump write final calibration to local root file +--max-events maximum number of events to process +--no-calib-output don't send the calibration data via DPL (required in case the calibration write is not attached) +--use-old-subspec Subspecification is built from RDH like +``` + +#### Running with data distribution +In one shell start the data distribution playback, e.g. +```bash +StfBuilder --id builder --data-source-enable --data-source-dir=2020-11-11T14_18_25Z --data-source-rate=100 --dpl-channel-name=dpl-chan --channel-config "name=dpl-chan,type=pair,method=connect,address=ipc:///tmp/stf-builder-dpl-pipe-0,transport=zeromq,rateLogging=1" +``` + +In another shell start the pedestal calibration +```bash +o2-dpl-raw-proxy -b --dataspec "A:TPC/RAWDATA" --channel-config "name=readout-proxy,type=pair,method=bind,address=ipc:///tmp/stf-builder-dpl-pipe-0,transport=zeromq,rateLogging=1" \ +| o2-tpc-calib-pedestal --max-events 5 --direct-file-dump --shm-segment-size $((8<<30)) --no-calib-output --use-old-subspec +``` + +#### Running with raw file playback +Create a raw-reader.cfg e.g. +```bash +i=0; echo -e "[defaults]\ndataOrigin = TPC\ndataDescription = RAWDATA\n" > raw-reader.cfg; echo; for file in *.raw; do echo "[input-$i]"; echo "dataOrigin = TPC"; echo "dataDescription = RAWDATA"; echo "filePath=$file"; echo; i=$((i+1)); done >> raw-reader.cfg +``` + +Then run +```bash +o2-raw-file-reader-workflow --input-conf raw-reader.cfg --nocheck-hbf-per-tf --nocheck-hbf-jump --shm-segment-size $((8<<30)) \ +| o2-tpc-calib-pedestal --max-events 5 --direct-file-dump --shm-segment-size $((8<<30)) --no-calib-output +``` + +#### Send data to CCDB +Remove the `--no-calib-output` option and add +```bash +| o2-calibration-ccdb-populator-workflow +``` + +## Running the recontruction on GBT raw data +This requires to do zero suppression in the first stage. For this the `DigiDump` class is used, wrapped in an o2 workflow. + +Use either the [DD](#running-with-data-distribution) part or [raw file playback](#running-with-raw-file-playback) from above and add as processor +```bash +| o2-tpc-raw-to-digits-workflow --pedestal-file pedestals.root --configKeyValues "TPCDigitDump.ADCMin=3;TPCDigitDump.NoiseThreshold=3" \ +| o2-tpc-reco-workflow --input-type digitizer --output-type tracks --disable-mc +``` + +To directly dump the digits to file for inspection use for the reco workflow +```bash +| o2-tpc-reco-workflow --input-type digitizer --output-type digits --disable-mc +``` diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CATrackerSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CATrackerSpec.h index b382f71d41a6f..8f2195dc9eecc 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CATrackerSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CATrackerSpec.h @@ -14,6 +14,7 @@ /// @brief Processor spec for running TPC CA tracking #include "Framework/DataProcessorSpec.h" +#include "RecoWorkflow.h" #include <utility> // std::forward namespace o2 @@ -40,7 +41,9 @@ enum struct Operation { OutputCAClusters, // publish the clusters produced by CA clusterer OutputCompClusters, // publish CompClusters container OutputCompClustersFlat, // publish CompClusters container + OutputQA, // Ship QA histograms to QC ProcessMC, // process MC labels + SendClustersPerSector, // Send clusters and clusters mc labels per sector Noop, // skip argument on the constructor }; @@ -86,9 +89,15 @@ struct Config { case Operation::OutputCAClusters: outputCAClusters = true; break; + case Operation::OutputQA: + outputQA = true; + break; case Operation::ProcessMC: processMC = true; break; + case Operation::SendClustersPerSector: + sendClustersPerSector = true; + break; case Operation::Noop: break; default: @@ -111,7 +120,9 @@ struct Config { bool outputCompClusters = false; bool outputCompClustersFlat = false; bool outputCAClusters = false; + bool outputQA = false; bool processMC = false; + bool sendClustersPerSector = false; }; } // namespace ca @@ -132,7 +143,7 @@ struct Config { /// /// @param specconfig configuration options for the processor spec /// @param tpcsectors list of sector numbers -framework::DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int> const& tpcsectors); +framework::DataProcessorSpec getCATrackerSpec(o2::tpc::reco_workflow::CompletionPolicyData* policyData, ca::Config const& specconfig, std::vector<int> const& tpcsectors); o2::framework::CompletionPolicy getCATrackerCompletionPolicy(); } // end namespace tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h new file mode 100644 index 0000000000000..74158f92ee406 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TPC_CalibProcessingHelper_H +#define O2_TPC_CalibProcessingHelper_H + +#include <memory> + +#include "Framework/InputRecord.h" + +using o2::tpc::rawreader::RawReaderCRU; + +namespace o2 +{ +namespace tpc +{ +namespace rawreader +{ +class RawReaderCRU; +} +namespace calib_processing_helper +{ + +uint64_t processRawData(o2::framework::InputRecord& inputs, std::unique_ptr<RawReaderCRU>& reader, bool useOldSubspec = false); +} // namespace calib_processing_helper +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h index d85a8d530a3db..f4f7c2d146ebb 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h @@ -16,6 +16,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Headers/DataHeader.h" +#include "TPCReconstruction/CTFCoder.h" #include <TStopwatch.h> namespace o2 @@ -34,8 +35,10 @@ class EntropyDecoderSpec : public o2::framework::Task ~EntropyDecoderSpec() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; private: + o2::tpc::CTFCoder mCTFCoder; TStopwatch mTimer; }; diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h index 3f91c7049d4b7..50da4378372ca 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h @@ -17,6 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "TPCReconstruction/CTFCoder.h" #include <TStopwatch.h> namespace o2 @@ -34,9 +35,11 @@ class EntropyEncoderSpec : public o2::framework::Task } ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; private: + o2::tpc::CTFCoder mCTFCoder; bool mFromFile = false; TStopwatch mTimer; }; diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/PublisherSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/PublisherSpec.h index e2385e4f1c726..8a1673977d7f9 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/PublisherSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/PublisherSpec.h @@ -28,6 +28,7 @@ namespace tpc { using OutputSpec = framework::OutputSpec; +using Reader = o2::framework::RootTreeReader; struct PublisherConf { struct BranchOptionConfig { @@ -44,6 +45,7 @@ struct PublisherConf { OutputSpec mcoutput; std::vector<int> tpcSectors; std::vector<int> outputIds; + Reader::SpecialPublishHook* hook = nullptr; }; /// create a processor spec @@ -57,7 +59,7 @@ framework::DataProcessorSpec getPublisherSpec(PublisherConf const& config, bool auto mco = o2::framework::DataSpecUtils::asConcreteDataTypeMatcher(config.mcoutput); // a creator callback for the actual reader instance - auto creator = [dto, mco, propagateMC](const char* treename, const char* filename, int nofEvents, Reader::PublishingMode publishingMode, o2::header::DataHeader::SubSpecificationType subSpec, const char* branchname, const char* mcbranchname) { + auto creator = [dto, mco, propagateMC](const char* treename, const char* filename, int nofEvents, Reader::PublishingMode publishingMode, o2::header::DataHeader::SubSpecificationType subSpec, const char* branchname, const char* mcbranchname, Reader::SpecialPublishHook* publishhook = nullptr) { constexpr auto persistency = o2::framework::Lifetime::Timeframe; if (propagateMC) { return std::make_shared<Reader>(treename, @@ -66,13 +68,15 @@ framework::DataProcessorSpec getPublisherSpec(PublisherConf const& config, bool publishingMode, Output{mco.origin, mco.description, subSpec, persistency}, mcbranchname, - Reader::BranchDefinition<T>{Output{dto.origin, dto.description, subSpec, persistency}, branchname}); + Reader::BranchDefinition<T>{Output{dto.origin, dto.description, subSpec, persistency}, branchname}, + publishhook); } else { return std::make_shared<Reader>(treename, filename, nofEvents, publishingMode, - Reader::BranchDefinition<T>{Output{dto.origin, dto.description, subSpec, persistency}, branchname}); + Reader::BranchDefinition<T>{Output{dto.origin, dto.description, subSpec, persistency}, branchname}, + publishhook); } }; @@ -82,7 +86,7 @@ framework::DataProcessorSpec getPublisherSpec(PublisherConf const& config, bool namespace workflow_reader { using Reader = o2::framework::RootTreeReader; -using Creator = std::function<std::shared_ptr<Reader>(const char*, const char*, int, Reader::PublishingMode, o2::header::DataHeader::SubSpecificationType, const char*, const char*)>; +using Creator = std::function<std::shared_ptr<Reader>(const char*, const char*, int, Reader::PublishingMode, o2::header::DataHeader::SubSpecificationType, const char*, const char*, Reader::SpecialPublishHook*)>; } // namespace workflow_reader framework::DataProcessorSpec createPublisherSpec(PublisherConf const& config, bool propagateMC, workflow_reader::Creator creator); diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h index c0e1703ee632f..ffacb3a870af2 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h @@ -26,7 +26,7 @@ namespace tpc /// create a processor spec /// read simulated TPC clusters from file and publish -o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef, std::vector<int> const& tpcSectors); +o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string inputSpec, std::vector<int> const& tpcSectors); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h index 2173e6d3ace15..d4140736b0985 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h @@ -17,11 +17,16 @@ #include "Framework/WorkflowSpec.h" #include <vector> +#include <array> #include <string> #include <numeric> // std::iota namespace o2 { +namespace framework +{ +struct InputSpec; +} namespace tpc { @@ -53,11 +58,16 @@ enum struct OutputType { Digits, CompClusters, EncodedClusters, DisableWriter, + SendClustersPerSector, ZSRaw, + QA, }; +using CompletionPolicyData = std::vector<framework::InputSpec>; + /// create the workflow for TPC reconstruction -framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, // +framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, // + std::vector<int> const& tpcSectors, // std::vector<int> const& laneConfiguration, // bool propagateMC = true, unsigned nLanes = 1, // std::string const& cfgInput = "digitizer", // @@ -67,7 +77,8 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, int zs10bit = 0, float zsThreshold = 2.0f); -static inline framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, // +static inline framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, // + std::vector<int> const& tpcSectors, // bool propagateMC = true, unsigned nLanes = 1, // std::string const& cfgInput = "digitizer", // std::string const& cfgOutput = "tracks", // @@ -79,10 +90,11 @@ static inline framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSec // create a default lane configuration with ids [0, nLanes-1] std::vector<int> laneConfiguration(nLanes); std::iota(laneConfiguration.begin(), laneConfiguration.end(), 0); - return getWorkflow(tpcSectors, laneConfiguration, propagateMC, nLanes, cfgInput, cfgOutput, caClusterer, zsOnTheFly, zs10bit, zsThreshold); + return getWorkflow(policyData, tpcSectors, laneConfiguration, propagateMC, nLanes, cfgInput, cfgOutput, caClusterer, zsOnTheFly, zs10bit, zsThreshold); } -static inline framework::WorkflowSpec getWorkflow(bool propagateMC = true, unsigned nLanes = 1, // +static inline framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, // + bool propagateMC = true, unsigned nLanes = 1, // std::string const& cfgInput = "digitizer", // std::string const& cfgOutput = "tracks", // int caClusterer = 0, // @@ -93,7 +105,7 @@ static inline framework::WorkflowSpec getWorkflow(bool propagateMC = true, unsig // create a default lane configuration with ids [0, nLanes-1] std::vector<int> laneConfiguration(nLanes); std::iota(laneConfiguration.begin(), laneConfiguration.end(), 0); - return getWorkflow({}, laneConfiguration, propagateMC, nLanes, cfgInput, cfgOutput, caClusterer, zsOnTheFly, zs10bit, zsThreshold); + return getWorkflow(policyData, {}, laneConfiguration, propagateMC, nLanes, cfgInput, cfgOutput, caClusterer, zsOnTheFly, zs10bit, zsThreshold); } } // end namespace reco_workflow diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPedestalSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPedestalSpec.h new file mode 100644 index 0000000000000..d73fe24fc0dab --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPedestalSpec.h @@ -0,0 +1,191 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TPCCALIBPEDESTALSPEC_H +#define O2_CALIBRATION_TPCCALIBPEDESTALSPEC_H + +/// @file TPCCalibPedestalSpec.h +/// @brief TPC Pedestal calibration processor + +#include <vector> +#include <string> +#include <chrono> + +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/ConfigParamRegistry.h" + +#include "Headers/DataHeader.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsCalibration/Utils.h" + +#include "TPCBase/CDBInterface.h" +#include "TPCCalibration/CalibPedestal.h" +#include "TPCReconstruction/RawReaderCRU.h" +#include "TPCWorkflow/CalibProcessingHelper.h" + +using namespace o2::framework; +using namespace o2::tpc; +using clbUtils = o2::calibration::Utils; + +namespace o2 +{ +namespace calibration +{ + +class TPCCalibPedestalDevice : public o2::framework::Task +{ + public: + TPCCalibPedestalDevice(bool skipCalib) : mSkipCalib(skipCalib) {} + + void init(o2::framework::InitContext& ic) final + { + // set up ADC value filling + mCalibPedestal.init(); // initialize configuration via configKeyValues + mRawReader.createReader(""); + mRawReader.setADCDataCallback([this](const PadROCPos& padROCPos, const CRU& cru, const gsl::span<const uint32_t> data) -> int { + const int timeBins = mCalibPedestal.update(padROCPos, cru, data); + mCalibPedestal.setNumberOfProcessedTimeBins(std::max(mCalibPedestal.getNumberOfProcessedTimeBins(), size_t(timeBins))); + return timeBins; + }); + + mMaxEvents = static_cast<uint32_t>(ic.options().get<int>("max-events")); + mUseOldSubspec = ic.options().get<bool>("use-old-subspec"); + mForceQuit = ic.options().get<bool>("force-quit"); + mDirectFileDump = ic.options().get<bool>("direct-file-dump"); + if (mUseOldSubspec) { + LOGP(info, "Using old subspecification (CruId << 16) | ((LinkId + 1) << (CruEndPoint == 1 ? 8 : 0))"); + } + } + + void run(o2::framework::ProcessingContext& pc) final + { + // in case the maximum number of events was reached don't do further processing + if (mReadyToQuit) { + return; + } + + auto& reader = mRawReader.getReaders()[0]; + calib_processing_helper::processRawData(pc.inputs(), reader, mUseOldSubspec); + + mCalibPedestal.incrementNEvents(); + LOGP(info, "Number of processed events: {} ({})", mCalibPedestal.getNumberOfProcessedEvents(), mMaxEvents); + + if ((mCalibPedestal.getNumberOfProcessedEvents() >= mMaxEvents) && !mCalibDumped) { + LOGP(info, "Maximm number of events reached ({}), no more processing will be done", mMaxEvents); + mReadyToQuit = true; + dumpCalibData(); + if (mForceQuit) { + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::All); + } else { + //pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOGP(info, "endOfStream"); + dumpCalibData(); + if (!mSkipCalib) { + sendOutput(ec.outputs()); + } + ec.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } + + private: + CalibPedestal mCalibPedestal; + rawreader::RawReaderCRUManager mRawReader; + uint32_t mMaxEvents{100}; + bool mReadyToQuit{false}; + bool mCalibDumped{false}; + bool mUseOldSubspec{false}; + bool mForceQuit{false}; + bool mDirectFileDump{false}; + bool mSkipCalib{false}; + + //____________________________________________________________________________ + void sendOutput(DataAllocator& output) + { + CDBStorage::MetaData_t md; + + // perhaps should be changed to time of the run + const auto now = std::chrono::system_clock::now(); + long timeStart = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count(); + long timeEnd = 99999999999999; + + std::array<const CalDet<float>*, 2> data = {&mCalibPedestal.getPedestal(), &mCalibPedestal.getNoise()}; + std::array<CDBType, 2> dataType = {CDBType::CalPedestal, CDBType::CalNoise}; + + for (size_t i = 0; i < data.size(); ++i) { + auto cal = data[i]; + o2::ccdb::CcdbObjectInfo w; + auto image = o2::ccdb::CcdbApi::createObjectImage(cal, &w); + + w.setPath(CDBTypeMap.at(dataType[i])); + w.setStartValidityTimestamp(timeStart); + w.setEndValidityTimestamp(timeEnd); + + LOG(INFO) << "Sending object " << w.getPath() << "/" << w.getFileName() << " of size " << image->size() + << " bytes, valid for " << w.getStartValidityTimestamp() << " : " << w.getEndValidityTimestamp(); + + header::DataHeader::SubSpecificationType subSpec{(header::DataHeader::SubSpecificationType)i}; + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, subSpec}, *image.get()); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, subSpec}, w); + } + } + + //____________________________________________________________________________ + void dumpCalibData() + { + if (mDirectFileDump && !mCalibDumped) { + LOGP(info, "Dumping output"); + mCalibPedestal.analyse(); + mCalibPedestal.dumpToFile("pedestals.root"); + mCalibDumped = true; + } + } +}; + +} // namespace calibration + +namespace framework +{ + +DataProcessorSpec getTPCCalibPedestalSpec(const std::string inputSpec, bool skipCalib) +{ + using device = o2::calibration::TPCCalibPedestalDevice; + + std::vector<OutputSpec> outputs; + if (!skipCalib) { + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload}); + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); + } + + return DataProcessorSpec{ + "calib-tpc-pedestal", + select(inputSpec.data()), + outputs, + AlgorithmSpec{adaptFromTask<device>(skipCalib)}, + Options{ + {"max-events", VariantType::Int, 100, {"maximum number of events to process"}}, + {"use-old-subspec", VariantType::Bool, false, {"use old subsecifiation definition"}}, + {"force-quit", VariantType::Bool, false, {"force quit after max-events have been reached"}}, + {"direct-file-dump", VariantType::Bool, false, {"directly dump calibration to file"}}, + } // end Options + }; // end DataProcessorSpec +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCSectorCompletionPolicy.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCSectorCompletionPolicy.h index ae394304dd9df..52a38fb2cb2c3 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCSectorCompletionPolicy.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCSectorCompletionPolicy.h @@ -28,7 +28,6 @@ namespace o2 { -using namespace framework; namespace tpc { @@ -64,6 +63,8 @@ namespace tpc class TPCSectorCompletionPolicy { public: + using CompletionPolicyData = std::vector<framework::InputSpec>; + enum struct Config { // require data on all other inputs in addition to the ones checked for the sector completion RequireAll, @@ -80,15 +81,15 @@ class TPCSectorCompletionPolicy { constexpr static size_t NSectors = o2::tpc::Sector::MAXSECTOR; - auto matcher = [expression = mProcessorName](DeviceSpec const& device) -> bool { + auto matcher = [expression = mProcessorName](framework::DeviceSpec const& device) -> bool { return std::regex_match(device.name.begin(), device.name.end(), std::regex(expression.c_str())); }; - auto callback = [inputMatchers = mInputMatchers, bRequireAll = this->mRequireAll](CompletionPolicy::InputSet inputs) -> CompletionPolicy::CompletionOp { - auto op = CompletionPolicy::CompletionOp::Wait; + auto callback = [bRequireAll = mRequireAll, inputMatchers = mInputMatchers, externalInputMatchers = mExternalInputMatchers](framework::CompletionPolicy::InputSet inputs) -> framework::CompletionPolicy::CompletionOp { std::bitset<NSectors> validSectors = 0; bool haveMatchedInput = false; uint64_t activeSectors = 0; + std::vector<uint64_t> validSectorsExternal(externalInputMatchers ? externalInputMatchers->size() : 0); size_t nActiveInputRoutes = 0; size_t nMaxPartsPerRoute = 0; int inputType = -1; @@ -96,17 +97,17 @@ class TPCSectorCompletionPolicy nMaxPartsPerRoute = it.size() > nMaxPartsPerRoute ? it.size() : nMaxPartsPerRoute; bool haveActivePart = false; for (auto const& ref : it) { - if (!DataRefUtils::isValid(ref)) { + if (!framework::DataRefUtils::isValid(ref)) { continue; } haveActivePart = true; - auto const* dh = DataRefUtils::getHeader<o2::header::DataHeader*>(ref); + auto const* dh = framework::DataRefUtils::getHeader<o2::header::DataHeader*>(ref); // check if the O2 message matches on of the input specs to be matched and // if it matches, check for the sector header, retrieve the active sector information - // and mark the sector as valid + // and mark the sector as valid, we require to match exactly one of the inputs in the list for (size_t idx = 0, end = inputMatchers.size(); idx < end; idx++) { auto const& spec = inputMatchers[idx]; - if (DataRefUtils::match(ref, spec)) { + if (framework::DataRefUtils::match(ref, spec)) { haveMatchedInput = true; if (inputType == -1) { // we bind to the index of the first match and require all other inputs to match the same spec @@ -119,34 +120,57 @@ class TPCSectorCompletionPolicy << dh->dataDescription.as<std::string>() + "/" + dh->subSpecification; throw std::runtime_error(error.str()); } - auto const* sectorHeader = DataRefUtils::getHeader<o2::tpc::TPCSectorHeader*>(ref); + auto const* sectorHeader = framework::DataRefUtils::getHeader<o2::tpc::TPCSectorHeader*>(ref); if (sectorHeader == nullptr) { - // FIXME: think about error policy throw std::runtime_error("TPC sector header missing on header stack"); } activeSectors |= sectorHeader->activeSectors; - std::bitset<NSectors> sectorMask(sectorHeader->sectorBits); - validSectors |= sectorMask; + validSectors |= sectorHeader->sectorBits; break; } } + + // We require to match all inputs in the external list + if (externalInputMatchers) { + for (size_t idx = 0, end = externalInputMatchers->size(); idx < end; idx++) { + auto const& spec = (*externalInputMatchers)[idx]; + if (framework::DataRefUtils::match(ref, spec)) { + auto const* sectorHeader = framework::DataRefUtils::getHeader<o2::tpc::TPCSectorHeader*>(ref); + if (sectorHeader == nullptr) { + throw std::runtime_error("TPC sector header missing on header stack"); + } + activeSectors |= sectorHeader->activeSectors; + validSectorsExternal[idx] |= sectorHeader->sectorBits; + break; + } + } + } } if (haveActivePart) { ++nActiveInputRoutes; } } + if (externalInputMatchers) { + // We require all external matchers to have all sectors present, if not we wait + for (size_t idx = 0, end = externalInputMatchers->size(); idx < end; idx++) { + if (validSectorsExternal[idx] == 0 || validSectorsExternal[idx] != activeSectors) { + return framework::CompletionPolicy::CompletionOp::Wait; + } + } + } + // If the flag Config::RequireAll is set in the constructor arguments we require // data from all inputs in addition to the sector matching condition // To be fully correct we would need to require data from all inputs not going // into the TPC policy, but that is not possible for the moment. That's why there is a possibly // unhandled case if multiple TPC input routes are defined but a complete data set is coming over // one of them. Not likely to be a use case, though. - if (haveMatchedInput && activeSectors == validSectors.to_ulong() && + if ((inputMatchers.size() == 0 || (haveMatchedInput && activeSectors == validSectors.to_ulong())) && (!bRequireAll || nActiveInputRoutes == inputs.size())) { // we can process if there is input for all sectors, the required sectors are // transported as part of the sector header - op = CompletionPolicy::CompletionOp::Consume; + return framework::CompletionPolicy::CompletionOp::Consume; } else if (activeSectors == 0 && nActiveInputRoutes == inputs.size()) { // no sector header is transmitted, this is the case for e.g. the ZS raw data // we simply require input on all routes, this is also the default of DPL DataRelayer @@ -158,12 +182,12 @@ class TPCSectorCompletionPolicy //if (nMaxPartsPerRoute > 1) { // LOG(WARNING) << "No sector information is provided with the data, data set is complete with data on all input routes. But there are multiple parts on at least one route and this policy might not be complete, no check possible if other parts on some routes are still missing. It is adviced to add a custom policy."; //} - op = CompletionPolicy::CompletionOp::Consume; + return framework::CompletionPolicy::CompletionOp::Consume; } - return op; + return framework::CompletionPolicy::CompletionOp::Wait; }; - return CompletionPolicy{"TPCSectorCompletionPolicy", matcher, callback}; + return framework::CompletionPolicy{"TPCSectorCompletionPolicy", matcher, callback}; } private: @@ -172,7 +196,7 @@ class TPCSectorCompletionPolicy void init(Arg&& arg, Args&&... args) { using Type = std::decay_t<Arg>; - if constexpr (std::is_same<Type, InputSpec>::value) { + if constexpr (std::is_same<Type, framework::InputSpec>::value) { mInputMatchers.emplace_back(std::move(arg)); } else if constexpr (std::is_same<Type, TPCSectorCompletionPolicy::Config>::value) { switch (arg) { @@ -180,8 +204,10 @@ class TPCSectorCompletionPolicy mRequireAll = true; break; } + } else if constexpr (std::is_same<Type, std::vector<o2::framework::InputSpec>*>::value) { + mExternalInputMatchers = arg; } else { - static_assert(always_static_assert_v<Type>); + static_assert(framework::always_static_assert_v<Type>); } if constexpr (sizeof...(args) > 0) { init(std::forward<Args>(args)...); @@ -189,7 +215,11 @@ class TPCSectorCompletionPolicy } std::string mProcessorName; - std::vector<InputSpec> mInputMatchers; + std::vector<framework::InputSpec> mInputMatchers; + // The external input matchers behave as the internal ones with the following differences: + // - They are controlled externally and the external entity can modify them, e.g. after parsing command line arguments. + // - They are all matched independently, it is not sufficient that one of them is present for all sectors + const std::vector<framework::InputSpec>* mExternalInputMatchers = nullptr; bool mRequireAll = false; }; } // namespace tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TrackReaderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TrackReaderSpec.h index 61a75e795a064..f9899344a4213 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TrackReaderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TrackReaderSpec.h @@ -45,7 +45,7 @@ class TrackReader : public Task std::vector<o2::tpc::TrackTPC>*mTracksInp = nullptr, mTracksOut; std::vector<o2::tpc::TPCClRefElem>*mCluRefVecInp = nullptr, mCluRefVecOut; - o2::dataformats::MCTruthContainer<o2::MCCompLabel>*mMCTruthInp = nullptr, mMCTruthOut; + std::vector<o2::MCCompLabel>*mMCTruthInp = nullptr, mMCTruthOut; std::unique_ptr<TFile> mFile; std::unique_ptr<TTree> mTree; diff --git a/Detectors/TPC/workflow/src/CATrackerSpec.cxx b/Detectors/TPC/workflow/src/CATrackerSpec.cxx index 1801bf7485a1c..e3d7795fc6b0e 100644 --- a/Detectors/TPC/workflow/src/CATrackerSpec.cxx +++ b/Detectors/TPC/workflow/src/CATrackerSpec.cxx @@ -39,242 +39,159 @@ #include "TPCdEdxCalibrationSplines.h" #include "DPLUtils/DPLRawParser.h" #include "DetectorsBase/MatLayerCylSet.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsCommonDataFormats/NameConf.h" #include "DetectorsRaw/HBFUtils.h" #include "TPCBase/RDHUtils.h" #include "GPUO2InterfaceConfiguration.h" +#include "GPUO2InterfaceQA.h" +#include "TPCPadGainCalib.h" #include "GPUDisplayBackend.h" #ifdef GPUCA_BUILD_EVENT_DISPLAY #include "GPUDisplayBackendGlfw.h" #endif #include "DataFormatsParameters/GRPObject.h" #include "TPCBase/Sector.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "TPCBase/Utils.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "Algorithm/Parser.h" +#include <boost/filesystem.hpp> #include <memory> // for make_shared #include <vector> #include <iomanip> #include <stdexcept> #include <regex> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include "GPUReconstructionConvert.h" #include "DetectorsRaw/RDHUtils.h" +#include <TStopwatch.h> +#include <TObjArray.h> +#include <TH1F.h> +#include <TH2F.h> +#include <TH1D.h> using namespace o2::framework; using namespace o2::header; using namespace o2::gpu; using namespace o2::base; +using namespace o2::dataformats; +using namespace o2::tpc::reco_workflow; namespace o2 { namespace tpc { -DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int> const& tpcsectors) +DataProcessorSpec getCATrackerSpec(CompletionPolicyData* policyData, ca::Config const& specconfig, std::vector<int> const& tpcsectors) { if (specconfig.outputCAClusters && !specconfig.caClusterer && !specconfig.decompressTPC) { throw std::runtime_error("inconsistent configuration: cluster output is only possible if CA clusterer is activated"); } + static TStopwatch timer; + constexpr static size_t NSectors = Sector::MAXSECTOR; constexpr static size_t NEndpoints = 20; //TODO: get from mapper? using ClusterGroupParser = o2::algorithm::ForwardParser<ClusterGroupHeader>; struct ProcessAttributes { - std::bitset<NSectors> validInputs = 0; - std::bitset<NSectors> validMcInputs = 0; std::unique_ptr<ClusterGroupParser> parser; std::unique_ptr<GPUCATracking> tracker; std::unique_ptr<GPUDisplayBackend> displayBackend; std::unique_ptr<TPCFastTransform> fastTransform; std::unique_ptr<TPCdEdxCalibrationSplines> dEdxSplines; - int verbosity = 1; + std::unique_ptr<TPCPadGainCalib> tpcPadGainCalib; + std::unique_ptr<GPUSettingsQA> qaConfig; + int qaTaskMask = 0; + std::unique_ptr<GPUO2InterfaceQA> qa; std::vector<int> clusterOutputIds; + unsigned long outputBufferSize = 0; + unsigned long tpcSectorMask = 0; + int verbosity = 1; bool readyToQuit = false; bool allocateOutputOnTheFly = false; bool suppressOutput = false; }; auto processAttributes = std::make_shared<ProcessAttributes>(); + for (auto s : tpcsectors) { + processAttributes->tpcSectorMask |= (1ul << s); + } auto initFunction = [processAttributes, specconfig](InitContext& ic) { - auto options = ic.options().get<std::string>("tracker-options"); + GPUO2InterfaceConfiguration config; + GPUSettingsO2 confParam; { auto& parser = processAttributes->parser; auto& tracker = processAttributes->tracker; parser = std::make_unique<ClusterGroupParser>(); tracker = std::make_unique<GPUCATracking>(); - // Prepare initialization of CATracker - we parse the deprecated option string here, - // and create the proper configuration objects for compatibility. - // This should go away eventually. - - // Default Settings - float solenoidBz = 5.00668; // B-field - float refX = 83.; // transport tracks to this x after tracking, >500 for disabling - bool continuous = false; // time frame data v.s. triggered events - int nThreads = 1; // number of threads if we run on the CPU, 1 = default, 0 = auto-detect - bool useGPU = false; // use a GPU for processing, if false uses GPU - int debugLevel = 0; // Enable additional debug output - int dump = 0; // create memory dump of processed events for standalone runs, 2 to dump only and skip processing - char gpuType[1024] = "CUDA"; // Type of GPU device, if useGPU is set to true - int gpuDevice = -1; // Select GPU device id (-1 = auto-detect fastest, -2 = use pipeline-slice) - GPUDisplayBackend* display = nullptr; // Ptr to display backend (enables event display) - bool qa = false; // Run the QA after tracking - bool readTransformationFromFile = false; // Read the TPC transformation from the file - bool allocateOutputOnTheFly = true; // Provide a callback to allocate output buffer on the fly instead of preallocating - char tpcTransformationFileName[1024] = ""; // A file with the TPC transformation - char matBudFileName[1024] = ""; // Material budget file name - char dEdxSplinesFile[1024] = ""; // File containing dEdx splines - int tpcRejectionMode = GPUSettings::RejectionStrategyA; - size_t memoryPoolSize = 1; - size_t hostMemoryPoolSize = 0; - - const auto grp = o2::parameters::GRPObject::loadFrom("o2sim_grp.root"); + // Create configuration object and fill settings + const auto grp = o2::parameters::GRPObject::loadFrom(o2::base::NameConf::getGRPFileName()); + o2::base::GeometryManager::loadGeometry(); + o2::base::Propagator::initFieldFromGRP(o2::base::NameConf::getGRPFileName()); if (grp) { - solenoidBz *= grp->getL3Current() / 30000.; - continuous = grp->isDetContinuousReadOut(o2::detectors::DetID::TPC); - LOG(INFO) << "Initializing run paramerers from GRP bz=" << solenoidBz << " cont=" << continuous; + config.configEvent.solenoidBz = 5.00668f * grp->getL3Current() / 30000.; + config.configEvent.continuousMaxTimeBin = grp->isDetContinuousReadOut(o2::detectors::DetID::TPC) ? -1 : 0; // Number of timebins in timeframe if continuous, 0 otherwise + LOG(INFO) << "Initializing run paramerers from GRP bz=" << config.configEvent.solenoidBz << " cont=" << grp->isDetContinuousReadOut(o2::detectors::DetID::TPC); } else { throw std::runtime_error("Failed to initialize run parameters from GRP"); } - - // Parse the config string - const char* opt = options.c_str(); - if (opt && *opt) { - printf("Received options %s\n", opt); - const char* optPtr = opt; - while (optPtr && *optPtr) { - while (*optPtr == ' ') { - optPtr++; - } - const char* nextPtr = strstr(optPtr, " "); - const int optLen = nextPtr ? nextPtr - optPtr : strlen(optPtr); - if (strncmp(optPtr, "cont", optLen) == 0) { - continuous = true; - printf("Continuous tracking mode enabled\n"); - } else if (strncmp(optPtr, "dump", optLen) == 0) { - dump = 1; - printf("Dumping of input events enabled\n"); - } else if (strncmp(optPtr, "dumponly", optLen) == 0) { - dump = 2; - printf("Dumping of input events enabled, processing disabled\n"); - } else if (strncmp(optPtr, "display", optLen) == 0) { + confParam = config.ReadConfigurableParam(); + processAttributes->allocateOutputOnTheFly = confParam.allocateOutputOnTheFly; + processAttributes->outputBufferSize = confParam.outputBufferSize; + processAttributes->suppressOutput = (confParam.dump == 2); + config.configInterface.dumpEvents = confParam.dump; + config.configInterface.dropSecondaryLegs = confParam.dropSecondaryLegs; + config.configInterface.memoryBufferScaleFactor = confParam.memoryBufferScaleFactor; + if (confParam.display) { #ifdef GPUCA_BUILD_EVENT_DISPLAY - processAttributes->displayBackend.reset(new GPUDisplayBackendGlfw); - display = processAttributes->displayBackend.get(); - printf("Event display enabled\n"); + processAttributes->displayBackend.reset(new GPUDisplayBackendGlfw); + config.configProcessing.eventDisplay = processAttributes->displayBackend.get(); + LOG(INFO) << "Event display enabled"; #else - printf("Standalone Event Display not enabled at build time!\n"); + throw std::runtime_error("Standalone Event Display not enabled at build time!"); #endif - } else if (strncmp(optPtr, "qa", optLen) == 0) { - qa = true; - printf("Enabling TPC Standalone QA\n"); - } else if (optLen > 3 && strncmp(optPtr, "bz=", 3) == 0) { - sscanf(optPtr + 3, "%f", &solenoidBz); - printf("Using solenoid field %f\n", solenoidBz); - } else if (optLen > 5 && strncmp(optPtr, "refX=", 5) == 0) { - sscanf(optPtr + 5, "%f", &refX); - printf("Propagating to reference X %f\n", refX); - } else if (optLen > 5 && strncmp(optPtr, "debug=", 6) == 0) { - sscanf(optPtr + 6, "%d", &debugLevel); - printf("Debug level set to %d\n", debugLevel); - } else if (optLen > 8 && strncmp(optPtr, "threads=", 8) == 0) { - sscanf(optPtr + 8, "%d", &nThreads); - printf("Using %d threads\n", nThreads); - } else if (optLen > 21 && strncmp(optPtr, "tpcRejectionStrategy=", 21) == 0) { - sscanf(optPtr + 21, "%d", &tpcRejectionMode); - tpcRejectionMode = tpcRejectionMode == 0 ? GPUSettings::RejectionNone : tpcRejectionMode == 1 ? GPUSettings::RejectionStrategyA : GPUSettings::RejectionStrategyB; - printf("TPC Rejection Mode: %d\n", tpcRejectionMode); - } else if (optLen > 8 && strncmp(optPtr, "gpuType=", 8) == 0) { - int len = std::min(optLen - 8, 1023); - memcpy(gpuType, optPtr + 8, len); - gpuType[len] = 0; - useGPU = true; - printf("Using GPU Type %s\n", gpuType); - } else if (optLen > 8 && strncmp(optPtr, "matBudFile=", 8) == 0) { - int len = std::min(optLen - 11, 1023); - memcpy(matBudFileName, optPtr + 11, len); - matBudFileName[len] = 0; - } else if (optLen > 8 && strncmp(optPtr, "gpuNum=", 7) == 0) { - sscanf(optPtr + 7, "%d", &gpuDevice); - printf("Using GPU device %d\n", gpuDevice); - } else if (optLen > 8 && strncmp(optPtr, "gpuMemorySize=", 14) == 0) { - sscanf(optPtr + 14, "%llu", (unsigned long long int*)&memoryPoolSize); - printf("GPU memory pool size set to %llu\n", (unsigned long long int)memoryPoolSize); - } else if (optLen > 8 && strncmp(optPtr, "hostMemorySize=", 15) == 0) { - sscanf(optPtr + 15, "%llu", (unsigned long long int*)&hostMemoryPoolSize); - printf("Host memory pool size set to %llu\n", (unsigned long long int)hostMemoryPoolSize); - } else if (optLen > 8 && strncmp(optPtr, "dEdxFile=", 9) == 0) { - int len = std::min(optLen - 9, 1023); - memcpy(dEdxSplinesFile, optPtr + 9, len); - dEdxSplinesFile[len] = 0; - } else if (optLen > 15 && strncmp(optPtr, "transformation=", 15) == 0) { - int len = std::min(optLen - 15, 1023); - memcpy(tpcTransformationFileName, optPtr + 15, len); - tpcTransformationFileName[len] = 0; - readTransformationFromFile = true; - printf("Read TPC transformation from the file \"%s\"\n", tpcTransformationFileName); - } else { - printf("Unknown option: %s\n", optPtr); - throw std::invalid_argument("Unknown config string option"); - } - optPtr = nextPtr; - } } - // Create configuration object and fill settings - processAttributes->allocateOutputOnTheFly = allocateOutputOnTheFly; - processAttributes->suppressOutput = (dump == 2); - GPUO2InterfaceConfiguration config; - if (useGPU) { - config.configProcessing.deviceType = GPUDataTypes::GetDeviceType(gpuType); - } else { - config.configProcessing.deviceType = GPUDataTypes::DeviceType::CPU; + if (config.configEvent.continuousMaxTimeBin == -1) { + config.configEvent.continuousMaxTimeBin = (o2::raw::HBFUtils::Instance().getNOrbitsPerTF() * o2::constants::lhc::LHCMaxBunches + 2 * constants::LHCBCPERTIMEBIN - 2) / constants::LHCBCPERTIMEBIN; } - config.configProcessing.forceDeviceType = true; // If we request a GPU, we force that it is available - no CPU fallback - - if (gpuDevice == -2) { + if (config.configProcessing.deviceNum == -2) { int myId = ic.services().get<const o2::framework::DeviceSpec>().inputTimesliceId; int idMax = ic.services().get<const o2::framework::DeviceSpec>().maxInputTimeslices; - gpuDevice = myId; + config.configProcessing.deviceNum = myId; LOG(INFO) << "GPU device number selected from pipeline id: " << myId << " / " << idMax; } - config.configDeviceProcessing.deviceNum = gpuDevice; - config.configDeviceProcessing.nThreads = nThreads; - config.configDeviceProcessing.runQA = qa; // Run QA after tracking - config.configDeviceProcessing.runMC = specconfig.processMC; // Propagate MC labels - config.configDeviceProcessing.eventDisplay = display; // Ptr to event display backend, for running standalone OpenGL event display - config.configDeviceProcessing.debugLevel = debugLevel; // Debug verbosity - config.configDeviceProcessing.forceMemoryPoolSize = memoryPoolSize; // GPU / Host Memory pool size, default = 1 = auto-detect - config.configDeviceProcessing.forceHostMemoryPoolSize = hostMemoryPoolSize; // Same for host, overrides the avove value for the host if set - if (memoryPoolSize || hostMemoryPoolSize) { - config.configDeviceProcessing.memoryAllocationStrategy = 2; - } - - config.configEvent.solenoidBz = solenoidBz; - int maxContTimeBin = (o2::raw::HBFUtils::Instance().getNOrbitsPerTF() * o2::constants::lhc::LHCMaxBunches + 2 * Constants::LHCBCPERTIMEBIN - 2) / Constants::LHCBCPERTIMEBIN; - config.configEvent.continuousMaxTimeBin = continuous ? maxContTimeBin : 0; // Number of timebins in timeframe if continuous, 0 otherwise - - config.configReconstruction.NWays = 3; // Should always be 3! - config.configReconstruction.NWaysOuter = true; // Will create outer param for TRD - config.configReconstruction.SearchWindowDZDR = 2.5f; // Should always be 2.5 for looper-finding and/or continuous tracking - config.configReconstruction.TrackReferenceX = refX; - - // Settings for TPC Compression: - config.configReconstruction.tpcRejectionMode = tpcRejectionMode; // Implement TPC Strategy A - config.configReconstruction.tpcRejectQPt = 1.f / 0.05f; // Reject clusters of tracks < 50 MeV - config.configReconstruction.tpcCompressionModes = GPUSettings::CompressionFull; // Activate all compression steps - config.configReconstruction.tpcCompressionSortOrder = GPUSettings::SortPad; // Sort order for differences compression - config.configReconstruction.tpcSigBitsCharge = 4; // Number of significant bits in TPC cluster chargs - config.configReconstruction.tpcSigBitsWidth = 3; // Number of significant bits in TPC cluster width - - config.configInterface.dumpEvents = dump; + config.configProcessing.runMC = specconfig.processMC; + if (specconfig.outputQA) { + if (!specconfig.processMC) { + throw std::runtime_error("Need MC information to create QA plots"); + } + config.configQA.shipToQC = true; + if (!config.configProcessing.runQA) { + config.configQA.enableLocalOutput = false; + processAttributes->qaTaskMask = 15; + config.configProcessing.runQA = -processAttributes->qaTaskMask; + } + } + config.configReconstruction.NWaysOuter = true; config.configInterface.outputToExternalBuffers = true; // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) with this instance of GPUCATracking config.configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, GPUDataTypes::RecoStep::TPCSliceTracking, GPUDataTypes::RecoStep::TPCMerging, - GPUDataTypes::RecoStep::TPCCompression, - GPUDataTypes::RecoStep::TPCdEdx); + GPUDataTypes::RecoStep::TPCCompression); + + config.configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, !confParam.synchronousProcessing); + if (confParam.synchronousProcessing) { + config.configReconstruction.useMatLUT = false; + } + // Alternative steps: TRDTracking | ITSTracking config.configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCClusters); // Alternative inputs: GPUDataTypes::InOutType::TRDTracklets @@ -294,9 +211,9 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int } // Create and forward data objects for TPC transformation, material LUT, ... - if (readTransformationFromFile) { + if (confParam.transformationFile.size()) { processAttributes->fastTransform = nullptr; - config.configCalib.fastTransform = TPCFastTransform::loadFromFile(tpcTransformationFileName); + config.configCalib.fastTransform = TPCFastTransform::loadFromFile(confParam.transformationFile.c_str()); } else { processAttributes->fastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0)); config.configCalib.fastTransform = processAttributes->fastTransform.get(); @@ -304,54 +221,95 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int if (config.configCalib.fastTransform == nullptr) { throw std::invalid_argument("GPUCATracking: initialization of the TPC transformation failed"); } - if (strlen(matBudFileName)) { - config.configCalib.matLUT = o2::base::MatLayerCylSet::loadFromFile(matBudFileName, "MatBud"); + + if (confParam.matLUTFile.size()) { + config.configCalib.matLUT = o2::base::MatLayerCylSet::loadFromFile(confParam.matLUTFile.c_str(), "MatBud"); } - processAttributes->dEdxSplines.reset(new TPCdEdxCalibrationSplines); - if (strlen(dEdxSplinesFile)) { - TFile dEdxFile(dEdxSplinesFile); - processAttributes->dEdxSplines->setSplinesFromFile(dEdxFile); + + if (confParam.dEdxFile.size()) { + processAttributes->dEdxSplines.reset(new TPCdEdxCalibrationSplines(confParam.dEdxFile.c_str())); + } else { + processAttributes->dEdxSplines.reset(new TPCdEdxCalibrationSplines); } config.configCalib.dEdxSplines = processAttributes->dEdxSplines.get(); + if (boost::filesystem::exists(confParam.gainCalibFile)) { + LOG(INFO) << "Loading tpc gain correction from file " << confParam.gainCalibFile; + const auto* gainMap = o2::tpc::utils::readCalPads(confParam.gainCalibFile, "GainMap")[0]; + processAttributes->tpcPadGainCalib.reset(new TPCPadGainCalib{*gainMap}); + } else { + if (not confParam.gainCalibFile.empty()) { + LOG(WARN) << "Couldn't find tpc gain correction file " << confParam.gainCalibFile << ". Not applying any gain correction."; + } + processAttributes->tpcPadGainCalib.reset(new TPCPadGainCalib{}); + } + config.configCalib.tpcPadGain = processAttributes->tpcPadGainCalib.get(); + + config.configCalib.o2Propagator = Propagator::Instance(); + // Sample code what needs to be done for the TRD Geometry, when we extend this to TRD tracking. - /*o2::base::GeometryManager::loadGeometry(); - o2::trd::TRDGeometry gm; + /* o2::trd::Geometry gm; gm.createPadPlaneArray(); gm.createClusterMatrixArray(); - std::unique_ptr<o2::trd::TRDGeometryFlat> gf(gm); + std::unique_ptr<o2::trd::GeometryFlat> gf(gm); config.trdGeometry = gf.get();*/ // Configuration is prepared, initialize the tracker. if (tracker->initialize(config) != 0) { throw std::invalid_argument("GPUCATracking initialization failed"); } - processAttributes->validInputs.reset(); - processAttributes->validMcInputs.reset(); + if (specconfig.outputQA) { + processAttributes->qaConfig.reset(new GPUSettingsQA(config.configQA)); + processAttributes->qa = std::make_unique<GPUO2InterfaceQA>(processAttributes->qaConfig.get()); + } + timer.Stop(); + timer.Reset(); } auto& callbacks = ic.services().get<CallbackService>(); - callbacks.set(CallbackService::Id::RegionInfoCallback, [processAttributes](FairMQRegionInfo const& info) { + callbacks.set(CallbackService::Id::RegionInfoCallback, [&processAttributes, confParam](FairMQRegionInfo const& info) { if (info.size) { + int fd = 0; + if (confParam.mutexMemReg) { + fd = open("/tmp/o2_gpu_memlock_mutex.lock", O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd == -1) { + throw std::runtime_error("Error opening lock file"); + } + if (lockf(fd, F_LOCK, 0)) { + throw std::runtime_error("Error locking file"); + } + } auto& tracker = processAttributes->tracker; if (tracker->registerMemoryForGPU(info.ptr, info.size)) { throw std::runtime_error("Error registering memory for GPU"); } + if (confParam.mutexMemReg) { + if (lockf(fd, F_ULOCK, 0)) { + throw std::runtime_error("Error unlocking file"); + } + close(fd); + } } }); + // the callback to be set as hook at stop of processing for the framework + auto printTiming = []() { + LOGF(INFO, "TPC CATracker total timing: Cpu: %.3e Real: %.3e s in %d slots", timer.CpuTime(), timer.RealTime(), timer.Counter() - 1); + }; + ic.services().get<CallbackService>().set(CallbackService::Id::Stop, printTiming); + auto processingFct = [processAttributes, specconfig](ProcessingContext& pc) { if (processAttributes->readyToQuit) { return; } + printf("RUN PROCESSING\n"); + auto cput = timer.CpuTime(); + timer.Start(false); auto& parser = processAttributes->parser; auto& tracker = processAttributes->tracker; - uint64_t activeSectors = 0; auto& verbosity = processAttributes->verbosity; // FIXME cleanup almost duplicated code - auto& validMcInputs = processAttributes->validMcInputs; - using CachedMCLabelContainer = decltype(std::declval<InputRecord>().get<MCLabelContainer*>(DataRef{nullptr, nullptr, nullptr})); - std::vector<CachedMCLabelContainer> mcInputs; + std::vector<ConstMCLabelContainerView> mcInputs; std::vector<gsl::span<const char>> inputs; struct InputRef { DataRef data; @@ -368,7 +326,9 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int const void** tpcZSmetaPointers2[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; const unsigned int* tpcZSmetaSizes2[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; std::array<gsl::span<const o2::tpc::Digit>, NSectors> inputDigits; - std::array<std::unique_ptr<const MCLabelContainer>, NSectors> inputDigitsMC; + std::vector<ConstMCLabelContainerView> inputDigitsMC; + std::array<int, constants::MAXSECTOR> inputDigitsMCIndex; + std::array<const ConstMCLabelContainerView*, constants::MAXSECTOR> inputDigitsMCPtrs; std::array<unsigned int, NEndpoints * NSectors> tpcZSonTheFlySizes; gsl::span<const ZeroSuppressedContainer8kb> inputZS; @@ -378,6 +338,7 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int {"check", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITSMCTR"}, Lifetime::Timeframe}, {"check", ConcreteDataTypeMatcher{gDataOriginTPC, "CLNATIVEMCLBL"}, Lifetime::Timeframe}, }; + unsigned long recvMask = 0; for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { auto const* sectorHeader = DataRefUtils::getHeader<TPCSectorHeader*>(ref); if (sectorHeader == nullptr) { @@ -389,78 +350,80 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int if (sector < 0) { continue; } - std::bitset<NSectors> sectorMask(sectorHeader->sectorBits); - if ((validMcInputs & sectorMask).any()) { - // have already data for this sector, this should not happen in the current - // sequential implementation, for parallel path merged at the tracker stage - // multiple buffers need to be handled + if (recvMask & sectorHeader->sectorBits) { throw std::runtime_error("can only have one MC data set per sector"); } + recvMask |= sectorHeader->sectorBits; inputrefs[sector].labels = ref; if (specconfig.caClusterer) { - inputDigitsMC[sector] = std::move(pc.inputs().get<const MCLabelContainer*>(ref)); - } else { + inputDigitsMCIndex[sector] = inputDigitsMC.size(); + inputDigitsMC.emplace_back(ConstMCLabelContainerView(pc.inputs().get<gsl::span<char>>(ref))); } - validMcInputs |= sectorMask; - activeSectors |= sectorHeader->activeSectors; - if (verbosity > 1) { - LOG(INFO) << "received " << *(ref.spec) << " MC label containers" - << " for sectors " << sectorMask // - << std::endl // - << " mc input status: " << validMcInputs // - << std::endl // - << " active sectors: " << std::bitset<NSectors>(activeSectors); // + } + if (recvMask != processAttributes->tpcSectorMask) { + throw std::runtime_error("Incomplete set of MC labels received"); + } + if (specconfig.caClusterer) { + for (unsigned int i = 0; i < NSectors; i++) { + LOG(INFO) << "GOT MC LABELS FOR SECTOR " << i << " -> " << inputDigitsMC[inputDigitsMCIndex[i]].getNElements(); + inputDigitsMCPtrs[i] = &inputDigitsMC[inputDigitsMCIndex[i]]; } } } - auto& validInputs = processAttributes->validInputs; - int operation = 0; - std::vector<InputSpec> filter = { - {"check", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITS"}, Lifetime::Timeframe}, - {"check", ConcreteDataTypeMatcher{gDataOriginTPC, "CLUSTERNATIVE"}, Lifetime::Timeframe}, - }; - - for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { - auto const* sectorHeader = DataRefUtils::getHeader<TPCSectorHeader*>(ref); - if (sectorHeader == nullptr) { - throw std::runtime_error("sector header missing on header stack"); - } - const int sector = sectorHeader->sector(); - if (sector < 0) { - continue; - } - std::bitset<NSectors> sectorMask(sectorHeader->sectorBits); - if ((validInputs & sectorMask).any()) { - // have already data for this sector, this should not happen in the current - // sequential implementation, for parallel path merged at the tracker stage - // multiple buffers need to be handled - throw std::runtime_error("can only have one cluster data set per sector"); + if (!specconfig.decompressTPC && (!specconfig.caClusterer || ((!specconfig.zsOnTheFly || specconfig.processMC) && !specconfig.zsDecoder))) { + std::vector<InputSpec> filter = { + {"check", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITS"}, Lifetime::Timeframe}, + {"check", ConcreteDataTypeMatcher{gDataOriginTPC, "CLUSTERNATIVE"}, Lifetime::Timeframe}, + }; + unsigned long recvMask = 0; + for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { + auto const* sectorHeader = DataRefUtils::getHeader<TPCSectorHeader*>(ref); + if (sectorHeader == nullptr) { + throw std::runtime_error("sector header missing on header stack"); + } + const int sector = sectorHeader->sector(); + if (sector < 0) { + continue; + } + if (recvMask & sectorHeader->sectorBits) { + throw std::runtime_error("can only have one cluster data set per sector"); + } + recvMask |= sectorHeader->sectorBits; + inputrefs[sector].data = ref; + if (specconfig.caClusterer && (!specconfig.zsOnTheFly || specconfig.processMC)) { + inputDigits[sector] = pc.inputs().get<gsl::span<o2::tpc::Digit>>(ref); + LOG(INFO) << "GOT DIGITS SPAN FOR SECTOR " << sector << " -> " << inputDigits[sector].size(); + } } - activeSectors |= sectorHeader->activeSectors; - validInputs |= sectorMask; - inputrefs[sector].data = ref; - if (specconfig.caClusterer && !specconfig.zsOnTheFly) { - inputDigits[sector] = pc.inputs().get<gsl::span<o2::tpc::Digit>>(ref); - LOG(INFO) << "GOT SPAN FOR SECTOR " << sector << " -> " << inputDigits[sector].size(); + if (recvMask != processAttributes->tpcSectorMask) { + throw std::runtime_error("Incomplete set of clusters/digits received"); } } + if (specconfig.zsOnTheFly) { tpcZSonTheFlySizes = {0}; // tpcZSonTheFlySizes: #zs pages per endpoint: std::vector<InputSpec> filter = {{"check", ConcreteDataTypeMatcher{gDataOriginTPC, "ZSSIZES"}, Lifetime::Timeframe}}; + bool recv = false, recvsizes = false; for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { + if (recvsizes) { + throw std::runtime_error("Received multiple ZSSIZES data"); + } tpcZSonTheFlySizes = pc.inputs().get<std::array<unsigned int, NEndpoints * NSectors>>(ref); + recvsizes = true; } // zs pages std::vector<InputSpec> filter2 = {{"check", ConcreteDataTypeMatcher{gDataOriginTPC, "TPCZS"}, Lifetime::Timeframe}}; for (auto const& ref : InputRecordWalker(pc.inputs(), filter2)) { + if (recv) { + throw std::runtime_error("Received multiple TPCZS data"); + } inputZS = pc.inputs().get<gsl::span<ZeroSuppressedContainer8kb>>(ref); + recv = true; } - //set all sectors as active and as valid inputs - for (int s = 0; s < NSectors; s++) { - activeSectors |= 1 << s; - validInputs.set(s); + if (!recv || !recvsizes) { + throw std::runtime_error("TPC ZS data not received"); } for (unsigned int i = 0; i < GPUTrackingInOutZS::NSLICES; i++) { for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { @@ -518,6 +481,8 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int continue; } ptr = current; + } else if (ptr == nullptr) { + ptr = current; } count++; } @@ -562,171 +527,114 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int pCompClustersFlat = pc.inputs().get<CompressedClustersFlat*>("input").get(); } } else if (!specconfig.zsOnTheFly) { - // FIXME: We can have digits input in zs decoder mode for MC labels - // This code path should run optionally also for the zs decoder version - auto printInputLog = [&verbosity, &validInputs, &activeSectors](auto& r, const char* comment, auto& s) { - if (verbosity > 1) { - LOG(INFO) << comment << " " << *(r.spec) << ", size " << DataRefUtils::getPayloadSize(r) // - << " for sector " << s // - << std::endl // - << " input status: " << validInputs // - << std::endl // - << " active sectors: " << std::bitset<NSectors>(activeSectors); // - } - }; - // for digits and clusters we always have the sector information, activeSectors being zero - // is thus an error condition. The completion policy makes sure that the data set is complete - if (activeSectors == 0 || (activeSectors & validInputs.to_ulong()) != activeSectors) { - throw std::runtime_error("Incomplete input data, expecting complete data set, buffering has been removed "); - } - // MC label blocks must be in the same multimessage with the corresponding data, the completion - // policy does not check for the MC labels and expects them to be present and thus complete if - // the data is complete - if (specconfig.processMC && (activeSectors & validMcInputs.to_ulong()) != activeSectors) { - throw std::runtime_error("Incomplete mc label input, expecting complete data set, buffering has been removed"); - } - assert(specconfig.processMC == false || validMcInputs == validInputs); for (auto const& refentry : inputrefs) { auto& sector = refentry.first; auto& ref = refentry.second.data; - if (ref.payload == nullptr) { - // skip zero-length message - continue; - } - if (refentry.second.labels.header != nullptr && refentry.second.labels.payload != nullptr) { - mcInputs.emplace_back(std::move(pc.inputs().get<const MCLabelContainer*>(refentry.second.labels))); - } - inputs.emplace_back(gsl::span(ref.payload, DataRefUtils::getPayloadSize(ref))); - printInputLog(ref, "received", sector); - } - assert(mcInputs.size() == 0 || mcInputs.size() == inputs.size()); - if (verbosity > 0) { - // make human readable information from the bitfield - std::string bitInfo; - auto nActiveBits = validInputs.count(); - if (((uint64_t)0x1 << nActiveBits) == validInputs.to_ulong() + 1) { - // sectors 0 to some upper bound are active - bitInfo = "0-" + std::to_string(nActiveBits - 1); - } else { - int rangeStart = -1; - int rangeEnd = -1; - for (size_t sector = 0; sector < validInputs.size(); sector++) { - if (validInputs.test(sector)) { - if (rangeStart < 0) { - if (rangeEnd >= 0) { - bitInfo += ","; - } - bitInfo += std::to_string(sector); - if (nActiveBits == 1) { - break; - } - rangeStart = sector; - } - rangeEnd = sector; - } else { - if (rangeStart >= 0 && rangeEnd > rangeStart) { - bitInfo += "-" + std::to_string(rangeEnd); - } - rangeStart = -1; - } + if (!specconfig.caClusterer) { + if (ref.payload == nullptr) { + // skip zero-length message + continue; } - if (rangeStart >= 0 && rangeEnd > rangeStart) { - bitInfo += "-" + std::to_string(rangeEnd); + if (refentry.second.labels.header != nullptr && refentry.second.labels.payload != nullptr) { + mcInputs.emplace_back(ConstMCLabelContainerView(pc.inputs().get<gsl::span<char>>(refentry.second.labels))); } + inputs.emplace_back(gsl::span(ref.payload, DataRefUtils::getPayloadSize(ref))); } - LOG(INFO) << "running tracking for sector(s) " << bitInfo; + if (verbosity > 1) { + LOG(INFO) << "received " << *(ref.spec) << ", size " << DataRefUtils::getPayloadSize(ref) << " for sector " << sector; + } + } + if (verbosity) { + LOGF(INFO, "running tracking for sector(s) 0x%09x", processAttributes->tpcSectorMask); } } std::vector<TrackTPC> tracks; std::vector<uint32_t> clusRefs; - MCLabelContainer tracksMCTruth; + std::vector<o2::MCCompLabel> tracksMCTruth; GPUO2InterfaceIOPtrs ptrs; ClusterNativeAccess clusterIndex; std::unique_ptr<ClusterNative[]> clusterBuffer; - MCLabelContainer clustersMCBuffer; + ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clustersMCBuffer; void* ptrEp[NSectors * NEndpoints] = {}; ptrs.outputTracks = &tracks; ptrs.outputClusRefs = &clusRefs; ptrs.outputTracksMCTruth = (specconfig.processMC ? &tracksMCTruth : nullptr); if (specconfig.caClusterer) { if (specconfig.zsOnTheFly) { - std::cout << "inputZS size: " << inputZS.size() << std::endl; const unsigned long long int* buffer = reinterpret_cast<const unsigned long long int*>(&inputZS[0]); o2::gpu::GPUReconstructionConvert::RunZSEncoderCreateMeta(buffer, tpcZSonTheFlySizes.data(), *&ptrEp, &tpcZS); ptrs.tpcZS = &tpcZS; if (specconfig.processMC) { - throw std::runtime_error("Currently unable to process MC information, tpc-tracker crashing when MC propagated"); - ptrs.o2DigitsMC = &inputDigitsMC; + ptrs.o2Digits = &inputDigits; + ptrs.o2DigitsMC = &inputDigitsMCPtrs; } } else if (specconfig.zsDecoder) { ptrs.tpcZS = &tpcZS; if (specconfig.processMC) { - throw std::runtime_error("Cannot process MC information, none available"); // In fact, passing in MC data with ZS TPC Raw is not yet available + throw std::runtime_error("Cannot process MC information, none available"); } } else { - ptrs.o2Digits = &inputDigits; // TODO: We will also create ClusterNative as output stored in ptrs. Should be added to the output + ptrs.o2Digits = &inputDigits; if (specconfig.processMC) { - ptrs.o2DigitsMC = &inputDigitsMC; + ptrs.o2DigitsMC = &inputDigitsMCPtrs; } } } else if (specconfig.decompressTPC) { ptrs.compressedClusters = pCompClustersFlat; } else { memset(&clusterIndex, 0, sizeof(clusterIndex)); - ClusterNativeHelper::Reader::fillIndex(clusterIndex, clusterBuffer, clustersMCBuffer, inputs, mcInputs, [&validInputs](auto& index) { return validInputs.test(index); }); + ClusterNativeHelper::Reader::fillIndex(clusterIndex, clusterBuffer, clustersMCBuffer, inputs, mcInputs, [&processAttributes](auto& index) { return processAttributes->tpcSectorMask & (1ul << index); }); ptrs.clusters = &clusterIndex; } // a byte size resizable vector object, the DataAllocator returns reference to internal object // initialize optional pointer to the vector object - using ClusterOutputChunkType = std::decay_t<decltype(pc.outputs().make<std::vector<char>>(Output{"", "", 0}))>; - ClusterOutputChunkType* clusterOutput = nullptr; + using O2CharVectorOutputType = std::decay_t<decltype(pc.outputs().make<std::vector<char>>(Output{"", "", 0}))>; TPCSectorHeader clusterOutputSectorHeader{0}; if (processAttributes->clusterOutputIds.size() > 0) { - if (activeSectors == 0) { - // there is no sector header shipped with the ZS raw data and thus we do not have - // a valid activeSector variable, though it will be needed downstream - // FIXME: check if this can be provided upstream - for (auto const& sector : processAttributes->clusterOutputIds) { - activeSectors |= 0x1 << sector; - } - } - clusterOutputSectorHeader.sectorBits = activeSectors; + clusterOutputSectorHeader.sectorBits = processAttributes->tpcSectorMask; // subspecs [0, NSectors - 1] are used to identify sector data, we use NSectors // to indicate the full TPC - o2::header::DataHeader::SubSpecificationType subspec = NSectors; - clusterOutputSectorHeader.activeSectors = activeSectors; - clusterOutput = &pc.outputs().make<std::vector<char>>({gDataOriginTPC, "CLUSTERNATIVE", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}); + clusterOutputSectorHeader.activeSectors = processAttributes->tpcSectorMask; } GPUInterfaceOutputs outputRegions; - // TODO: For output to preallocated buffer, just allocated some large buffer for now. - // This should be estimated correctly, but it is not the default for now, so it doesn't matter much. - size_t bufferSize = 256ul * 1024 * 1024; - auto* bufferCompressedClusters = !processAttributes->allocateOutputOnTheFly && specconfig.outputCompClustersFlat ? &pc.outputs().make<std::vector<char>>(Output{gDataOriginTPC, "COMPCLUSTERSFLAT", 0}, bufferSize) : nullptr; - if (processAttributes->allocateOutputOnTheFly && specconfig.outputCompClustersFlat) { - outputRegions.compressedClusters.allocator = [&bufferCompressedClusters, &pc](size_t size) -> void* {bufferCompressedClusters = &pc.outputs().make<std::vector<char>>(Output{gDataOriginTPC, "COMPCLUSTERSFLAT", 0}, size); return bufferCompressedClusters->data(); }; - } else if (specconfig.outputCompClustersFlat) { - outputRegions.compressedClusters.ptr = bufferCompressedClusters->data(); - outputRegions.compressedClusters.size = bufferCompressedClusters->size(); - } - if (clusterOutput != nullptr) { + std::optional<std::reference_wrapper<O2CharVectorOutputType>> clusterOutput = std::nullopt, bufferCompressedClusters = std::nullopt, bufferTPCTracks = std::nullopt; + char *clusterOutputChar = nullptr, *bufferCompressedClustersChar = nullptr, *bufferTPCTracksChar = nullptr; + if (specconfig.outputCompClustersFlat) { if (processAttributes->allocateOutputOnTheFly) { - outputRegions.clustersNative.allocator = [&clusterOutput, &pc](size_t size) -> void* {clusterOutput->resize(size + sizeof(ClusterCountIndex)); return (char*)clusterOutput->data() + sizeof(ClusterCountIndex); }; + outputRegions.compressedClusters.allocator = [&bufferCompressedClustersChar, &pc](size_t size) -> void* {bufferCompressedClustersChar = pc.outputs().make<char>(Output{gDataOriginTPC, "COMPCLUSTERSFLAT", 0}, size).data(); return bufferCompressedClustersChar; }; } else { - clusterOutput->resize(bufferSize); - outputRegions.clustersNative.ptr = (char*)clusterOutput->data() + sizeof(ClusterCountIndex); - outputRegions.clustersNative.size = clusterOutput->size() * sizeof(*clusterOutput->data()) - sizeof(ClusterCountIndex); + bufferCompressedClusters.emplace(pc.outputs().make<std::vector<char>>(Output{gDataOriginTPC, "COMPCLUSTERSFLAT", 0}, processAttributes->outputBufferSize)); + outputRegions.compressedClusters.ptr = bufferCompressedClustersChar = bufferCompressedClusters->get().data(); + outputRegions.compressedClusters.size = bufferCompressedClusters->get().size(); } } - auto* bufferTPCTracks = !processAttributes->allocateOutputOnTheFly ? &pc.outputs().make<std::vector<char>>(Output{gDataOriginTPC, "TRACKSGPU", 0}, bufferSize) : nullptr; - if (processAttributes->allocateOutputOnTheFly) { - outputRegions.tpcTracks.allocator = [&bufferTPCTracks, &pc](size_t size) -> void* {bufferTPCTracks = &pc.outputs().make<std::vector<char>>(Output{gDataOriginTPC, "TRACKSGPU", 0}, size); return bufferTPCTracks->data(); }; - } else { - outputRegions.tpcTracks.ptr = bufferTPCTracks->data(); - outputRegions.tpcTracks.size = bufferTPCTracks->size(); + if (processAttributes->clusterOutputIds.size() > 0) { + const o2::header::DataDescription outputLabel = specconfig.sendClustersPerSector ? (o2::header::DataDescription) "CLUSTERNATIVETMP" : (o2::header::DataDescription) "CLUSTERNATIVE"; + if (processAttributes->allocateOutputOnTheFly) { + outputRegions.clustersNative.allocator = [&clusterOutputChar, &pc, clusterOutputSectorHeader, outputLabel](size_t size) -> void* {clusterOutputChar = pc.outputs().make<char>({gDataOriginTPC, outputLabel, NSectors, Lifetime::Timeframe, {clusterOutputSectorHeader}}, size + sizeof(ClusterCountIndex)).data(); return clusterOutputChar + sizeof(ClusterCountIndex); }; + } else { + clusterOutput.emplace(pc.outputs().make<std::vector<char>>({gDataOriginTPC, outputLabel, NSectors, Lifetime::Timeframe, {clusterOutputSectorHeader}}, processAttributes->outputBufferSize)); + clusterOutputChar = clusterOutput->get().data(); + outputRegions.clustersNative.ptr = clusterOutputChar + sizeof(ClusterCountIndex); + outputRegions.clustersNative.size = clusterOutput->get().size() - sizeof(ClusterCountIndex); + } + } + if (specconfig.outputTracks) { + if (processAttributes->allocateOutputOnTheFly) { + outputRegions.tpcTracks.allocator = [&bufferTPCTracksChar, &pc](size_t size) -> void* {bufferTPCTracksChar = pc.outputs().make<char>(Output{gDataOriginTPC, "TRACKSGPU", 0}, size).data(); return bufferTPCTracksChar; }; + } else { + bufferTPCTracks.emplace(pc.outputs().make<std::vector<char>>(Output{gDataOriginTPC, "TRACKSGPU", 0}, processAttributes->outputBufferSize)); + outputRegions.tpcTracks.ptr = bufferTPCTracksChar = bufferTPCTracks->get().data(); + outputRegions.tpcTracks.size = bufferTPCTracks->get().size(); + } + } + if (specconfig.processMC) { + outputRegions.clusterLabels.allocator = [&clustersMCBuffer](size_t size) -> void* { return &clustersMCBuffer; }; } + printf("RUN TRACKING\n"); int retVal = tracker->runTracking(&ptrs, &outputRegions); if (processAttributes->suppressOutput) { return; @@ -740,15 +648,17 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int pc.outputs().snapshot(OutputRef{"outTracks"}, tracks); pc.outputs().snapshot(OutputRef{"outClusRefs"}, clusRefs); if (specconfig.processMC) { - LOG(INFO) << "sending " << tracksMCTruth.getIndexedSize() << " track label(s)"; + LOG(INFO) << "sending " << tracksMCTruth.size() << " track label(s)"; pc.outputs().snapshot(OutputRef{"mclblout"}, tracksMCTruth); } } if (ptrs.compressedClusters != nullptr) { if (specconfig.outputCompClustersFlat) { - bufferCompressedClusters->resize(outputRegions.compressedClusters.size); - if ((void*)ptrs.compressedClusters != (void*)bufferCompressedClusters->data()) { + if (!processAttributes->allocateOutputOnTheFly) { + bufferCompressedClusters->get().resize(outputRegions.compressedClusters.size); + } + if ((void*)ptrs.compressedClusters != (void*)bufferCompressedClustersChar) { throw std::runtime_error("compressed cluster output ptrs out of sync"); // sanity check } } @@ -766,31 +676,62 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int } // previously, clusters have been published individually for the enabled sectors // clusters are now published as one block, subspec is NSectors - if (clusterOutput != nullptr) { - clusterOutput->resize(sizeof(ClusterCountIndex) + outputRegions.clustersNative.size); - if ((void*)ptrs.clusters->clustersLinear != (void*)((char*)clusterOutput->data() + sizeof(ClusterCountIndex))) { + if (processAttributes->clusterOutputIds.size() > 0) { + if (!processAttributes->allocateOutputOnTheFly) { + clusterOutput->get().resize(sizeof(ClusterCountIndex) + outputRegions.clustersNative.size); + } + if ((void*)ptrs.clusters->clustersLinear != (void*)(clusterOutputChar + sizeof(ClusterCountIndex))) { throw std::runtime_error("cluster native output ptrs out of sync"); // sanity check } - o2::header::DataHeader::SubSpecificationType subspec = NSectors; - // doing a copy for now, in the future the tracker uses the output buffer directly - auto& target = *clusterOutput; ClusterNativeAccess const& accessIndex = *ptrs.clusters; - ClusterCountIndex* outIndex = reinterpret_cast<ClusterCountIndex*>(target.data()); - static_assert(sizeof(ClusterCountIndex) == sizeof(accessIndex.nClusters)); - memcpy(outIndex, &accessIndex.nClusters[0][0], sizeof(ClusterCountIndex)); - if (specconfig.processMC && accessIndex.clustersMCTruth) { - pc.outputs().snapshot({gDataOriginTPC, "CLNATIVEMCLBL", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}, *accessIndex.clustersMCTruth); - } - } - - validInputs.reset(); - if (specconfig.processMC) { - validMcInputs.reset(); - for (auto& mcInput : mcInputs) { - mcInput.reset(); + if (specconfig.sendClustersPerSector) { + for (int i = 0; i < NSectors; i++) { + if (processAttributes->tpcSectorMask & (1ul << i)) { + o2::header::DataHeader::SubSpecificationType subspec = i; + clusterOutputSectorHeader.sectorBits = (1ul << i); + char* buffer = pc.outputs().make<char>({gDataOriginTPC, "CLUSTERNATIVE", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}, accessIndex.nClustersSector[i] * sizeof(*accessIndex.clustersLinear) + sizeof(ClusterCountIndex)).data(); + ClusterCountIndex* outIndex = reinterpret_cast<ClusterCountIndex*>(buffer); + memset(outIndex, 0, sizeof(*outIndex)); + for (int j = 0; j < constants::MAXGLOBALPADROW; j++) { + outIndex->nClusters[i][j] = accessIndex.nClusters[i][j]; + } + memcpy(buffer + sizeof(*outIndex), accessIndex.clusters[i][0], accessIndex.nClustersSector[i] * sizeof(*accessIndex.clustersLinear)); + if (specconfig.processMC && accessIndex.clustersMCTruth) { + MCLabelContainer cont; + for (int j = 0; j < accessIndex.nClustersSector[i]; j++) { + const auto& labels = accessIndex.clustersMCTruth->getLabels(accessIndex.clusterOffset[i][0] + j); + for (const auto& label : labels) { + cont.addElement(j, label); + } + } + ConstMCLabelContainer contflat; + cont.flatten_to(contflat); + pc.outputs().snapshot({gDataOriginTPC, "CLNATIVEMCLBL", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}, contflat); + } + } + } + } else { + o2::header::DataHeader::SubSpecificationType subspec = NSectors; + ClusterCountIndex* outIndex = reinterpret_cast<ClusterCountIndex*>(clusterOutputChar); + static_assert(sizeof(ClusterCountIndex) == sizeof(accessIndex.nClusters)); + memcpy(outIndex, &accessIndex.nClusters[0][0], sizeof(ClusterCountIndex)); + if (specconfig.processMC && accessIndex.clustersMCTruth) { + pc.outputs().snapshot({gDataOriginTPC, "CLNATIVEMCLBL", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}, clustersMCBuffer.first); + } } } + if (specconfig.outputQA) { + TObjArray out; + std::vector<TH1F> copy1 = *outputRegions.qa.hist1; // Internally, this will also be used as output, so we need a non-const copy + std::vector<TH2F> copy2 = *outputRegions.qa.hist2; + std::vector<TH1D> copy3 = *outputRegions.qa.hist3; + processAttributes->qa->postprocessExternal(copy1, copy2, copy3, out, processAttributes->qaTaskMask ? processAttributes->qaTaskMask : -1); + pc.outputs().snapshot({gDataOriginTPC, "TRACKINGQA", 0, Lifetime::Timeframe}, out); + processAttributes->qa->cleanup(); + } + timer.Stop(); + LOG(INFO) << "TPC CATracker time for this TF " << timer.CpuTime() - cput << " s"; }; return processingFct; @@ -800,26 +741,29 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int // changing the binding name of the input in order to identify inputs by unique labels // in the processing. Think about how the processing can be made agnostic of input size, // e.g. by providing a span of inputs under a certain label - auto createInputSpecs = [&tpcsectors, &specconfig]() { + auto createInputSpecs = [&tpcsectors, &specconfig, policyData]() { Inputs inputs; if (specconfig.decompressTPC) { inputs.emplace_back(InputSpec{"input", ConcreteDataTypeMatcher{gDataOriginTPC, specconfig.decompressTPCFromROOT ? header::DataDescription("COMPCLUSTERS") : header::DataDescription("COMPCLUSTERSFLAT")}, Lifetime::Timeframe}); } else if (specconfig.caClusterer) { // We accept digits and MC labels also if we run on ZS Raw data, since they are needed for MC label propagation - if (!specconfig.zsOnTheFly && !specconfig.zsDecoder) { // FIXME: We can have digits input in zs decoder mode for MC labels, to be made optional + if ((!specconfig.zsOnTheFly || specconfig.processMC) && !specconfig.zsDecoder) { inputs.emplace_back(InputSpec{"input", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITS"}, Lifetime::Timeframe}); + policyData->emplace_back(o2::framework::InputSpec{"digits", o2::framework::ConcreteDataTypeMatcher{"TPC", "DIGITS"}}); } } else { inputs.emplace_back(InputSpec{"input", ConcreteDataTypeMatcher{gDataOriginTPC, "CLUSTERNATIVE"}, Lifetime::Timeframe}); + policyData->emplace_back(o2::framework::InputSpec{"clusters", o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}}); } if (specconfig.processMC) { - if (!specconfig.zsOnTheFly && specconfig.caClusterer) { - constexpr o2::header::DataDescription datadesc("DIGITSMCTR"); - if (!specconfig.zsDecoder) { // FIXME: We can have digits input in zs decoder mode for MC labels, to be made optional - inputs.emplace_back(InputSpec{"mclblin", ConcreteDataTypeMatcher{gDataOriginTPC, datadesc}, Lifetime::Timeframe}); + if (specconfig.caClusterer) { + if (!specconfig.zsDecoder) { + inputs.emplace_back(InputSpec{"mclblin", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITSMCTR"}, Lifetime::Timeframe}); + policyData->emplace_back(o2::framework::InputSpec{"digitsmc", o2::framework::ConcreteDataTypeMatcher{"TPC", "DIGITSMCTR"}}); } } else { inputs.emplace_back(InputSpec{"mclblin", ConcreteDataTypeMatcher{gDataOriginTPC, "CLNATIVEMCLBL"}, Lifetime::Timeframe}); + policyData->emplace_back(o2::framework::InputSpec{"clustersmc", o2::framework::ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}}); } } @@ -835,6 +779,9 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int return inputs; }; + //o2::framework::InputSpec{"cluster", o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}}, + // o2::framework::InputSpec{"digits", o2::framework::ConcreteDataTypeMatcher{"TPC", "DIGITS"}})()); + auto createOutputSpecs = [&specconfig, &tpcsectors, &processAttributes]() { std::vector<OutputSpec> outputSpecs{ OutputSpec{{"outTracks"}, gDataOriginTPC, "TRACKS", 0, Lifetime::Timeframe}, @@ -864,21 +811,35 @@ DataProcessorSpec getCATrackerSpec(ca::Config const& specconfig, std::vector<int for (auto const& sector : tpcsectors) { processAttributes->clusterOutputIds.emplace_back(sector); } - outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVE", NSectors, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVE", specconfig.sendClustersPerSector ? 0 : NSectors, Lifetime::Timeframe); + if (specconfig.sendClustersPerSector) { + outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVETMP", NSectors, Lifetime::Timeframe); // Dummy buffer the TPC tracker writes the inital linear clusters to + for (const auto sector : tpcsectors) { + outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVE", sector, Lifetime::Timeframe); + } + } else { + outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVE", NSectors, Lifetime::Timeframe); + } if (specconfig.processMC) { - outputSpecs.emplace_back(OutputSpec{gDataOriginTPC, "CLNATIVEMCLBL", NSectors, Lifetime::Timeframe}); + if (specconfig.sendClustersPerSector) { + for (const auto sector : tpcsectors) { + outputSpecs.emplace_back(gDataOriginTPC, "CLNATIVEMCLBL", sector, Lifetime::Timeframe); + } + } else { + outputSpecs.emplace_back(gDataOriginTPC, "CLNATIVEMCLBL", NSectors, Lifetime::Timeframe); + } } } + if (specconfig.outputQA) { + outputSpecs.emplace_back(gDataOriginTPC, "TRACKINGQA", 0, Lifetime::Timeframe); + } return std::move(outputSpecs); }; return DataProcessorSpec{"tpc-tracker", // process id {createInputSpecs()}, {createOutputSpecs()}, - AlgorithmSpec(initFunction), - Options{ - {"tracker-options", VariantType::String, "", {"Option string passed to tracker"}}, - }}; + AlgorithmSpec(initFunction)}; } } // namespace tpc diff --git a/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx b/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx new file mode 100644 index 0000000000000..6f42bfa033dcf --- /dev/null +++ b/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx @@ -0,0 +1,95 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/ConcreteDataMatcher.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" + +#include "DPLUtils/RawParser.h" +#include "TPCBase/RDHUtils.h" +#include "TPCReconstruction/RawReaderCRU.h" + +#include "TPCWorkflow/CalibProcessingHelper.h" + +using namespace o2::tpc; +using namespace o2::framework; + +uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inputs, std::unique_ptr<RawReaderCRU>& reader, bool useOldSubspec) +{ + std::vector<InputSpec> filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; + + uint64_t activeSectors = 0; + + for (auto const& ref : InputRecordWalker(inputs, filter)) { + const auto* dh = DataRefUtils::getHeader<o2::header::DataHeader*>(ref); + + // ---| extract hardware information to do the processing |--- + const auto subSpecification = dh->subSpecification; + rdh_utils::FEEIDType feeID = (rdh_utils::FEEIDType)dh->subSpecification; + rdh_utils::FEEIDType cruID, linkID, endPoint; + + if (useOldSubspec) { + //---| old definition by Gvozden |--- + cruID = (rdh_utils::FEEIDType)(subSpecification >> 16); + linkID = (rdh_utils::FEEIDType)((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; + endPoint = (rdh_utils::FEEIDType)((subSpecification >> 8) & 0xFF) > 0; + } else { + //---| new definition by David |--- + rdh_utils::getMapping(feeID, cruID, endPoint, linkID); + } + + uint64_t sector = cruID / 10; + activeSectors |= (0x1 << sector); + + const auto globalLinkID = linkID + endPoint * 12; + + // ---| update hardware information in the reader |--- + reader->forceCRU(cruID); + reader->setLink(globalLinkID); + + LOGP(info, "Specifier: {}/{}/{}", dh->dataOrigin.as<std::string>(), dh->dataDescription.as<std::string>(), subSpecification); + LOGP(info, "Payload size: {}", dh->payloadSize); + LOGP(info, "CRU: {}; linkID: {}; endPoint: {}; globalLinkID: {}", cruID, linkID, endPoint, globalLinkID); + + // TODO: exception handling needed? + const gsl::span<const char> raw = inputs.get<gsl::span<char>>(ref); + o2::framework::RawParser parser(raw.data(), raw.size()); + + // TODO: it would be better to have external event handling and then moving the event processing functionality to CalibRawBase and RawReader to not repeat it in other places + rawreader::ADCRawData rawData; + rawreader::GBTFrame gFrame; + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + + const auto size = it.size(); + auto data = it.data(); + //LOGP(info, "Data size: {}", size); + + int iFrame = 0; + for (int i = 0; i < size; i += 16) { + gFrame.setFrameNumber(iFrame); + gFrame.setPacketNumber(iFrame / 508); + gFrame.readFromMemory(gsl::span<const o2::byte>(data + i, 16)); + + // extract the half words from the 4 32-bit words + gFrame.getFrameHalfWords(); + + gFrame.getAdcValues(rawData); + gFrame.updateSyncCheck(false); + + ++iFrame; + } + } + + reader->runADCDataCallback(rawData); + } + + return activeSectors; +} diff --git a/Detectors/TPC/workflow/src/ClusterDecoderRawSpec.cxx b/Detectors/TPC/workflow/src/ClusterDecoderRawSpec.cxx index f43fad7a07bcf..66d9b25966598 100644 --- a/Detectors/TPC/workflow/src/ClusterDecoderRawSpec.cxx +++ b/Detectors/TPC/workflow/src/ClusterDecoderRawSpec.cxx @@ -22,7 +22,7 @@ #include "DataFormatsTPC/ClusterHardware.h" #include "DataFormatsTPC/Helpers.h" #include "TPCReconstruction/HardwareClusterDecoder.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include <FairMQLogger.h> #include <memory> // for make_shared @@ -35,14 +35,12 @@ using namespace o2::framework; using namespace o2::header; +using namespace o2::dataformats; namespace o2 { namespace tpc { - -using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - /// create the processor spec for TPC raw cluster decoder converting TPC raw to native clusters /// Input: raw pages of TPC raw clusters /// Output: vector of containers with clusters in ClusterNative format, one container per @@ -115,13 +113,15 @@ DataProcessorSpec getClusterDecoderRawSpec(bool sendMC) // MC labels are received as one container of labels in the sequence matching clusters // in the raw pages - std::vector<MCLabelContainer> mcinCopies; - std::unique_ptr<const MCLabelContainer> mcin; + std::vector<ConstMCLabelContainer> mcinCopiesFlat; + std::vector<ConstMCLabelContainerView> mcinCopiesFlatView; + ConstMCLabelContainerView mcin; if (DataRefUtils::isValid(mclabelref)) { - mcin = std::move(pc.inputs().get<MCLabelContainer*>(mclabelref)); - mcinCopies.resize(nPages); + mcin = pc.inputs().get<gsl::span<char>>(mclabelref); + mcinCopiesFlat.resize(nPages); + mcinCopiesFlatView.reserve(nPages); if (verbosity > 0) { - LOG(INFO) << "Decoder input: " << size << ", " << nPages << " pages, " << mcin->getIndexedSize() << " MC label sets for sector " << sectorHeader->sector(); + LOG(INFO) << "Decoder input: " << size << ", " << nPages << " pages, " << mcin.getIndexedSize() << " MC label sets for sector " << sectorHeader->sector(); } } @@ -133,6 +133,7 @@ DataProcessorSpec getClusterDecoderRawSpec(bool sendMC) size_t mcinPos = 0; size_t totalNumberOfClusters = 0; for (size_t page = 0; page < nPages; page++) { + MCLabelContainer mcinCopy; inputList.emplace_back(reinterpret_cast<const ClusterHardwareContainer*>(ref.payload + page * 8192), 1); const ClusterHardwareContainer& container = *(inputList.back().first); if (verbosity > 1) { @@ -141,22 +142,24 @@ DataProcessorSpec getClusterDecoderRawSpec(bool sendMC) << std::setw(3) << container.numberOfClusters << " cluster(s)"; // } totalNumberOfClusters += container.numberOfClusters; - if (mcin) { + if (mcin.getBuffer().size()) { for (size_t mccopyPos = 0; - mccopyPos < container.numberOfClusters && mcinPos < mcin->getIndexedSize(); + mccopyPos < container.numberOfClusters && mcinPos < mcin.getIndexedSize(); mccopyPos++, mcinPos++) { - for (auto const& label : mcin->getLabels(mcinPos)) { - mcinCopies[page].addElement(mccopyPos, label); + for (auto const& label : mcin.getLabels(mcinPos)) { + mcinCopy.addElement(mccopyPos, label); } } } + mcinCopy.flatten_to(mcinCopiesFlat[page]); + mcinCopiesFlatView.emplace_back(mcinCopiesFlat[page]); } // FIXME: introduce error handling policy: throw, ignore, warn //assert(!mcin || mcinPos == mcin->getIndexedSize()); - if (mcin && mcinPos != totalNumberOfClusters) { + if (mcin.getBuffer().size() && mcinPos != totalNumberOfClusters) { LOG(ERROR) << "inconsistent number of MC label objects processed" << ", expecting MC label objects for " << totalNumberOfClusters << " cluster(s)" - << ", got " << mcin->getIndexedSize(); + << ", got " << mcin.getIndexedSize(); } // output of the decoder is sorted in (sector,globalPadRow) coordinates, individual // containers are created for clusters and MC labels per (sector,globalPadRow) address @@ -166,7 +169,7 @@ DataProcessorSpec getClusterDecoderRawSpec(bool sendMC) return outputBuffer; }; MCLabelContainer mcout; - decoder->decodeClusters(inputList, outputAllocator, (mcin ? &mcinCopies : nullptr), &mcout); + decoder->decodeClusters(inputList, outputAllocator, (mcin.getBuffer().size() ? &mcinCopiesFlatView : nullptr), &mcout); // TODO: reestablish the logging messages on the raw buffer // if (verbosity > 1) { @@ -181,7 +184,9 @@ DataProcessorSpec getClusterDecoderRawSpec(bool sendMC) << " label object(s)" << std::endl; } // serialize the complete list of MC label containers - pc.outputs().snapshot(Output{gDataOriginTPC, DataDescription("CLNATIVEMCLBL"), fanSpec, Lifetime::Timeframe, std::move(mcHeaderStack)}, mcout); + ConstMCLabelContainer labelsFlat; + mcout.flatten_to(labelsFlat); + pc.outputs().snapshot(Output{gDataOriginTPC, DataDescription("CLNATIVEMCLBL"), fanSpec, Lifetime::Timeframe, std::move(mcHeaderStack)}, labelsFlat); } }; diff --git a/Detectors/TPC/workflow/src/ClustererSpec.cxx b/Detectors/TPC/workflow/src/ClustererSpec.cxx index ee0220cafe689..59b3336a840cf 100644 --- a/Detectors/TPC/workflow/src/ClustererSpec.cxx +++ b/Detectors/TPC/workflow/src/ClustererSpec.cxx @@ -32,14 +32,13 @@ using namespace o2::framework; using namespace o2::header; +using namespace o2::dataformats; namespace o2 { namespace tpc { -using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - /// create a processor spec /// runs the TPC HwClusterer in a DPL process with digits and mc as input DataProcessorSpec getClustererSpec(bool sendMC) @@ -87,14 +86,14 @@ DataProcessorSpec getClustererSpec(bool sendMC) } return; } - std::unique_ptr<const MCLabelContainer> inMCLabels; + ConstMCLabelContainerView inMCLabels; if (DataRefUtils::isValid(mclabelref)) { - inMCLabels = std::move(pc.inputs().get<const MCLabelContainer*>(mclabelref)); + inMCLabels = pc.inputs().get<gsl::span<char>>(mclabelref); } auto inDigits = pc.inputs().get<gsl::span<o2::tpc::Digit>>(dataref); - if (verbosity > 0 && inMCLabels) { + if (verbosity > 0 && inMCLabels.getBuffer().size()) { LOG(INFO) << "received " << inDigits.size() << " digits, " - << inMCLabels->getIndexedSize() << " MC label objects" + << inMCLabels.getIndexedSize() << " MC label objects" << " input MC label size " << DataRefUtils::getPayloadSize(mclabelref); } if (!clusterers[sector]) { @@ -115,9 +114,10 @@ DataProcessorSpec getClustererSpec(bool sendMC) // are cleared but also the cluster counter. Clearing the containers externally leaves the // cluster counter unchanged and leads to an inconsistency between cluster container and // MC label container (the latter just grows with every call). - clusterer->process(inDigits, inMCLabels.get(), true /* clear output containers and cluster counter */); + clusterer->process(inDigits, inMCLabels, true /* clear output containers and cluster counter */); const std::vector<o2::tpc::Digit> emptyDigits; - clusterer->finishProcess(emptyDigits, nullptr, false); // keep here the false, otherwise the clusters are lost of they are not stored in the meantime + ConstMCLabelContainerView emptyLabels; + clusterer->finishProcess(emptyDigits, emptyLabels, false); // keep here the false, otherwise the clusters are lost of they are not stored in the meantime if (verbosity > 0) { LOG(INFO) << "clusterer produced " << std::accumulate(clusterArray.begin(), clusterArray.end(), size_t(0), [](size_t l, auto const& r) { return l + r.getContainer()->numberOfClusters; }) @@ -133,7 +133,9 @@ DataProcessorSpec getClustererSpec(bool sendMC) auto outputPages = pc.outputs().make<ClusterHardwareContainer8kb>(Output{gDataOriginTPC, "CLUSTERHW", fanSpec, Lifetime::Timeframe, {*sectorHeader}}, clusterArray.size()); std::copy(clusterArray.begin(), clusterArray.end(), outputPages.begin()); if (DataRefUtils::isValid(mclabelref)) { - pc.outputs().snapshot(Output{gDataOriginTPC, "CLUSTERHWMCLBL", fanSpec, Lifetime::Timeframe, {*sectorHeader}}, mctruthArray); + ConstMCLabelContainer mcflat; + mctruthArray.flatten_to(mcflat); + pc.outputs().snapshot(Output{gDataOriginTPC, "CLUSTERHWMCLBL", fanSpec, Lifetime::Timeframe, {*sectorHeader}}, mcflat); } }; diff --git a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx index d4ad917b4d972..b5d97bb2931ff 100644 --- a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx @@ -16,7 +16,6 @@ #include "Framework/ConfigParamRegistry.h" #include "DataFormatsTPC/CompressedClusters.h" #include "TPCWorkflow/EntropyDecoderSpec.h" -#include "TPCReconstruction/CTFCoder.h" using namespace o2::framework; @@ -25,9 +24,12 @@ namespace o2 namespace tpc { -void EntropyDecoderSpec::init(InitContext& ic) +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) { - // at the moment do nothing + std::string dictPath = ic.options().get<std::string>("tpc-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Decoder); + } } void EntropyDecoderSpec::run(ProcessingContext& pc) @@ -39,21 +41,27 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) auto& compclusters = pc.outputs().make<std::vector<char>>(OutputRef{"output"}); const auto ctfImage = o2::tpc::CTF::getImage(buff.data()); - CTFCoder::decode(ctfImage, compclusters); + mCTFCoder.decode(ctfImage, compclusters); mTimer.Stop(); LOG(INFO) << "Decoded " << buff.size() * sizeof(o2::ctf::BufferType) << " encoded bytes to " - << compclusters.size() << " bytes in" << mTimer.CpuTime() - cput << "\n"; + << compclusters.size() << " bytes in " << mTimer.CpuTime() - cput << " s"; +} + +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "TPC Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } DataProcessorSpec getEntropyDecoderSpec() { return DataProcessorSpec{ - "TPC", + "tpc-entropy-decoder", Inputs{InputSpec{"ctf", "TPC", "CTFDATA", 0, Lifetime::Timeframe}}, Outputs{OutputSpec{{"output"}, "TPC", "COMPCLUSTERSFLAT", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask<EntropyDecoderSpec>()}, - Options{}}; + Options{{"tpc-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF decoding dictionary"}}}}; } } // namespace tpc diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index ba71acba4fe94..e89992a9cf963 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -15,7 +15,6 @@ #include "TPCWorkflow/EntropyEncoderSpec.h" #include "DataFormatsTPC/CompressedClusters.h" -#include "TPCReconstruction/CTFCoder.h" #include "Framework/ConfigParamRegistry.h" #include "Headers/DataHeader.h" @@ -27,6 +26,15 @@ namespace o2 namespace tpc { +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +{ + mCTFCoder.setCombineColumns(!ic.options().get<bool>("no-ctf-columns-combining")); + std::string dictPath = ic.options().get<std::string>("tpc-ctf-dictionary"); + if (!dictPath.empty() && dictPath != "none") { + mCTFCoder.createCoders(dictPath, o2::ctf::CTFCoderBase::OpType::Encoder); + } +} + void EntropyEncoderSpec::run(ProcessingContext& pc) { CompressedClusters clusters; @@ -50,7 +58,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); auto& buffer = pc.outputs().make<std::vector<o2::ctf::BufferType>>(Output{"TPC", "CTFDATA", 0, Lifetime::Timeframe}); - CTFCoder::encode(buffer, clusters); + mCTFCoder.encode(buffer, clusters); auto encodedBlocks = CTF::get(buffer.data()); // cast to container pointer encodedBlocks->compactify(); // eliminate unnecessary padding buffer.resize(encodedBlocks->size()); // shrink buffer to strictly necessary size @@ -71,9 +79,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile) header::DataDescription inputType = inputFromFile ? header::DataDescription("COMPCLUSTERS") : header::DataDescription("COMPCLUSTERSFLAT"); return DataProcessorSpec{ "tpc-entropy-encoder", // process id - {{"input", "TPC", inputType, 0, Lifetime::Timeframe}}, + Inputs{{"input", "TPC", inputType, 0, Lifetime::Timeframe}}, Outputs{{"TPC", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec(adaptFromTask<EntropyEncoderSpec>(inputFromFile))}; + AlgorithmSpec{adaptFromTask<EntropyEncoderSpec>(inputFromFile)}, + Options{{"tpc-ctf-dictionary", VariantType::String, "ctf_dictionary.root", {"File of CTF encoding dictionary"}}, + {"no-ctf-columns-combining", VariantType::Bool, false, {"Do not combine correlated columns in CTF"}}}}; } } // namespace tpc diff --git a/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx b/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx index 4125822558658..cfb3905b3b3c1 100644 --- a/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx +++ b/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx @@ -69,10 +69,12 @@ o2::framework::DataProcessorSpec getLinkZSToDigitsSpec(int channel, const std::s // sort digits for (auto& digits : digitsAll) { std::sort(digits.begin(), digits.end(), [](const auto& a, const auto& b) { - if (a.getTimeStamp() < b.getTimeStamp()) + if (a.getTimeStamp() < b.getTimeStamp()) { return true; - if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) + } + if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) { return true; + } return false; }); } diff --git a/Detectors/TPC/workflow/src/PublisherSpec.cxx b/Detectors/TPC/workflow/src/PublisherSpec.cxx index 962e5ad028589..d6c787a37f024 100644 --- a/Detectors/TPC/workflow/src/PublisherSpec.cxx +++ b/Detectors/TPC/workflow/src/PublisherSpec.cxx @@ -138,8 +138,8 @@ DataProcessorSpec createPublisherSpec(PublisherConf const& config, bool propagat publishingMode, subSpec, clusterbranchname.c_str(), // name of data branch - mcbranchname.c_str() // name of mc label branch - ); + mcbranchname.c_str(), // name of mc label branch + config.hook); if (sectorMode == SectorMode::Full) { break; } diff --git a/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx b/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx index 69eae838dd275..bc36ab063c59a 100644 --- a/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx +++ b/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx @@ -8,23 +8,25 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Framework/WorkflowSpec.h" +#include <vector> +#include <string> +#include "fmt/format.h" + +#include "Framework/Task.h" #include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRefUtils.h" -#include "Framework/Lifetime.h" -#include "DPLUtils/RawParser.h" -#include "Headers/DataHeader.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/WorkflowSpec.h" + #include "DataFormatsTPC/TPCSectorHeader.h" -#include "DataFormatsTPC/Digit.h" +#include "Headers/DataHeader.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsCalibration/Utils.h" + #include "TPCCalibration/DigitDump.h" #include "TPCReconstruction/RawReaderCRU.h" -#include "TPCWorkflow/RawToDigitsSpec.h" -#include "Framework/Logger.h" -#include <vector> -#include <string> - +#include "TPCWorkflow/CalibProcessingHelper.h" using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; @@ -33,192 +35,119 @@ namespace o2 namespace tpc { -o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef, std::vector<int> const& tpcSectors) +class TPCDigitDumpDevice : public o2::framework::Task { + public: + TPCDigitDumpDevice(const std::vector<int>& sectors) : mSectors(sectors) {} + + void init(o2::framework::InitContext& ic) final + { + // set up ADC value filling + mRawReader.createReader(""); + mDigitDump.init(); + mDigitDump.setInMemoryOnly(); + const auto pedestalFile = ic.options().get<std::string>("pedestal-file"); + LOGP(info, "Setting pedestal file: {}", pedestalFile); + mDigitDump.setPedestalAndNoiseFile(pedestalFile); + + mRawReader.setADCDataCallback([this](const PadROCPos& padROCPos, const CRU& cru, const gsl::span<const uint32_t> data) -> int { + const int timeBins = mDigitDump.update(padROCPos, cru, data); + mDigitDump.setNumberOfProcessedTimeBins(std::max(mDigitDump.getNumberOfProcessedTimeBins(), size_t(timeBins))); + return timeBins; + }); + + mMaxEvents = static_cast<uint32_t>(ic.options().get<int>("max-events")); + mUseOldSubspec = ic.options().get<bool>("use-old-subspec"); + mForceQuit = ic.options().get<bool>("force-quit"); + if (mUseOldSubspec) { + LOGP(info, "Using old subspecification (CruId << 16) | ((LinkId + 1) << (CruEndPoint == 1 ? 8 : 0))"); + } + } - struct ProcessAttributes { - DigitDump digitDump; ///< digits creation class - rawreader::RawReaderCRUManager rawReader; ///< GBT frame decoder - uint32_t lastOrbit{0}; ///< last processed orbit number - uint32_t maxEvents{100}; ///< maximum number of events to process - uint64_t activeSectors{0}; ///< bit mask of active sectors - bool quit{false}; ///< if workflow is ready to quit - std::vector<int> tpcSectors{}; ///< tpc sector configuration - }; - - // ===| stateful initialization |============================================= - // - auto initFunction = [channel, tpcSectors](InitContext& ic) { - // ===| create and set up processing attributes |=== - auto processAttributes = std::make_shared<ProcessAttributes>(); - // set up calibration - { - auto& digitDump = processAttributes->digitDump; - digitDump.init(); - digitDump.setInMemoryOnly(); - const auto pedestalFile = ic.options().get<std::string>("pedestal-file"); - LOGP(info, "Setting pedestal file: {}", pedestalFile); - digitDump.setPedestalAndNoiseFile(pedestalFile); - - processAttributes->rawReader.createReader(""); - processAttributes->rawReader.setADCDataCallback([&digitDump](const PadROCPos& padROCPos, const CRU& cru, const gsl::span<const uint32_t> data) -> Int_t { - Int_t timeBins = digitDump.update(padROCPos, cru, data); - digitDump.setNumberOfProcessedTimeBins(std::max(digitDump.getNumberOfProcessedTimeBins(), size_t(timeBins))); - return timeBins; - }); - processAttributes->maxEvents = static_cast<uint32_t>(ic.options().get<int>("max-events")); - processAttributes->tpcSectors = tpcSectors; + void run(o2::framework::ProcessingContext& pc) final + { + // in case the maximum number of events was reached don't do further processing + if (mReadyToQuit) { + return; } - // ===| data processor |==================================================== - // - auto processingFct = [processAttributes, channel](ProcessingContext& pc) { - if (processAttributes->quit) { - return; - } + auto& reader = mRawReader.getReaders()[0]; + mActiveSectors = calib_processing_helper::processRawData(pc.inputs(), reader, mUseOldSubspec); - // ===| digit snapshot |=== - // - // lambda that snapshots digits to be sent out; - // prepares and attaches header with sector information - // - auto snapshotDigits = [&pc, processAttributes, channel](std::vector<o2::tpc::Digit> const& digits, int sector) { - o2::tpc::TPCSectorHeader header{sector}; - header.activeSectors = processAttributes->activeSectors; - // digit for now are transported per sector, not per lane - // pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast<SubSpecificationType>(channel), Lifetime::Timeframe, header}, - pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast<SubSpecificationType>(sector), Lifetime::Timeframe, header}, - const_cast<std::vector<o2::tpc::Digit>&>(digits)); - }; - - // loop over all inputs - for (auto& input : pc.inputs()) { - const auto* dh = DataRefUtils::getHeader<o2::header::DataHeader*>(input); - - // select only RAW data - if (dh->dataDescription != o2::header::gDataDescriptionRawData) { - continue; - } - - // ===| extract electronics mapping information |=== - const auto subSpecification = dh->subSpecification; - const auto cruID = subSpecification >> 16; - const auto linkID = ((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; - const auto dataWrapperID = ((subSpecification >> 8) & 0xFF) > 0; - const auto globalLinkID = linkID + dataWrapperID * 12; - const auto sector = cruID / 10; - - // update active sectors - processAttributes->activeSectors |= (0x1 << sector); - - // set up mapping information for raw reader - auto& reader = processAttributes->rawReader.getReaders()[0]; - reader->forceCRU(cruID); - reader->setLink(globalLinkID); - - LOGP(debug, "Specifier: {}/{}/{}", dh->dataOrigin.as<std::string>(), dh->dataDescription.as<std::string>(), dh->subSpecification); - LOGP(debug, "Payload size: {}", dh->payloadSize); - LOGP(debug, "CRU: {}; linkID: {}; dataWrapperID: {}; globalLinkID: {}", cruID, linkID, dataWrapperID, globalLinkID); - - try { - o2::framework::RawParser parser(input.payload, dh->payloadSize); - - rawreader::ADCRawData rawData; - rawreader::GBTFrame gFrame; - - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - auto* rdhPtr = it.get_if<o2::header::RAWDataHeaderV4>(); - if (!rdhPtr) { - break; - } - const auto& rdh = *rdhPtr; - //printRDH(rdh); - - // ===| event handling |=== - // - // really ugly, better treatment requires extension in DPL - // events are are detected by close by orbit numbers - // - const auto hbOrbit = rdh.heartbeatOrbit; - const auto lastOrbit = processAttributes->lastOrbit; - - if ((lastOrbit > 0) && (hbOrbit > (lastOrbit + 3))) { - auto& digitDump = processAttributes->digitDump; - digitDump.incrementNEvents(); - LOGP(info, "Number of processed events: {} ({})", digitDump.getNumberOfProcessedEvents(), processAttributes->maxEvents); - digitDump.sortDigits(); - - // publish digits of all configured sectors - for (auto isector : processAttributes->tpcSectors) { - snapshotDigits(digitDump.getDigits(isector), isector); - } - digitDump.clearDigits(); - - processAttributes->activeSectors = 0; - if (digitDump.getNumberOfProcessedEvents() >= processAttributes->maxEvents) { - LOGP(info, "Maximum number of events reached ({}), no more processing will be done", processAttributes->maxEvents); - processAttributes->quit = true; - pc.services().get<ControlService>().endOfStream(); - //pc.services().get<ControlService>().readyToQuit(QuitRequest::All); - pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); - break; - } - } - - processAttributes->lastOrbit = hbOrbit; - const auto size = it.size(); - auto data = it.data(); - LOGP(debug, "Raw data block payload size: {}", size); - - int iFrame = 0; - for (int i = 0; i < size; i += 16) { - gFrame.setFrameNumber(iFrame); - gFrame.setPacketNumber(iFrame / 508); - gFrame.readFromMemory(gsl::span<const o2::byte>(data + i, 16)); - - // extract the half words from the 4 32-bit words - gFrame.getFrameHalfWords(); - - // debug output - //if (CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { - //std::cout << gFrame; - //} - - gFrame.getAdcValues(rawData); - gFrame.updateSyncCheck(false); - - ++iFrame; - } - } - - reader->runADCDataCallback(rawData); - } catch (const std::runtime_error& e) { - LOG(ERROR) << "can not create raw parser form input data"; - o2::header::hexDump("payload", input.payload, dh->payloadSize, 64); - LOG(ERROR) << e.what(); - } + mDigitDump.incrementNEvents(); + LOGP(info, "Number of processed events: {} ({})", mDigitDump.getNumberOfProcessedEvents(), mMaxEvents); + + snapshotDigits(pc.outputs()); + + if ((mDigitDump.getNumberOfProcessedEvents() >= mMaxEvents)) { + LOGP(info, "Maximm number of events reached ({}), no more processing will be done", mMaxEvents); + mReadyToQuit = true; + if (mForceQuit) { + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::All); + } else { + //pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); } - }; + } + } - return processingFct; - }; + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOGP(info, "endOfStream"); + if (mActiveSectors) { + snapshotDigits(ec.outputs()); + } + ec.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } - std::stringstream id; - id << "TPCDigitizer" << channel; + private: + DigitDump mDigitDump; + rawreader::RawReaderCRUManager mRawReader; + uint32_t mMaxEvents{100}; + bool mReadyToQuit{false}; + bool mCalibDumped{false}; + bool mUseOldSubspec{false}; + bool mForceQuit{false}; + uint64_t mActiveSectors{0}; ///< bit mask of active sectors + std::vector<int> mSectors{}; ///< tpc sector configuration + + //____________________________________________________________________________ + void snapshotDigits(DataAllocator& output) + { + mDigitDump.sortDigits(); + for (auto isector : mSectors) { + o2::tpc::TPCSectorHeader header{isector}; + header.activeSectors = mActiveSectors; + // digit for now are transported per sector, not per lane + output.snapshot(Output{"TPC", "DIGITS", static_cast<SubSpecificationType>(isector), Lifetime::Timeframe, header}, + mDigitDump.getDigits(isector)); + } + mDigitDump.clearDigits(); + mActiveSectors = 0; + } +}; + +DataProcessorSpec getRawToDigitsSpec(int channel, const std::string inputSpec, std::vector<int> const& tpcSectors) +{ + using device = o2::tpc::TPCDigitDumpDevice; - std::vector<OutputSpec> outputs; // define channel by triple of (origin, type id of data to be sent on this channel, subspecification) + std::vector<OutputSpec> outputs; for (auto isector : tpcSectors) { outputs.emplace_back("TPC", "DIGITS", static_cast<SubSpecificationType>(isector), Lifetime::Timeframe); } return DataProcessorSpec{ - id.str().c_str(), - select(inputDef.data()), + fmt::format("TPCDigitizer{}", channel), + select(inputSpec.data()), outputs, - AlgorithmSpec{initFunction}, + AlgorithmSpec{adaptFromTask<device>(tpcSectors)}, Options{ {"max-events", VariantType::Int, 100, {"maximum number of events to process"}}, - {"pedestal-file", VariantType::String, "", {"file with pedestals and noise for zero suppression"}}}}; + {"use-old-subspec", VariantType::Bool, false, {"use old subsecifiation definition"}}, + {"force-quit", VariantType::Bool, false, {"force quit after max-events have been reached"}}, + {"pedestal-file", VariantType::String, "", {"file with pedestals and noise for zero suppression"}}, + } // end Options + }; // end DataProcessorSpec } } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 901590b220d82..43116540ffed7 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -32,7 +32,8 @@ #include "DataFormatsTPC/TPCSectorHeader.h" #include "DataFormatsTPC/CompressedClusters.h" #include "DataFormatsTPC/ZeroSuppression.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsTPC/Helpers.h" #include "DataFormatsTPC/ZeroSuppression.h" @@ -49,6 +50,8 @@ #include <array> #include <gsl/span> +using namespace o2::dataformats; + namespace o2 { namespace tpc @@ -80,15 +83,15 @@ const std::unordered_map<std::string, OutputType> OutputMap{ {"compressed-clusters", OutputType::CompClusters}, {"encoded-clusters", OutputType::EncodedClusters}, {"disable-writer", OutputType::DisableWriter}, + {"send-clusters-per-sector", OutputType::SendClustersPerSector}, {"zsraw", OutputType::ZSRaw}, -}; + {"qa", OutputType::QA}}; -framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vector<int> const& laneConfiguration, +framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vector<int> const& tpcSectors, std::vector<int> const& laneConfiguration, bool propagateMC, unsigned nLanes, std::string const& cfgInput, std::string const& cfgOutput, int caClusterer, int zsOnTheFly, int zs10bit, float zsThreshold) { InputType inputType; - try { inputType = InputMap.at(cfgInput); } catch (std::out_of_range&) { @@ -104,6 +107,25 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec return std::find(outputTypes.begin(), outputTypes.end(), type) != outputTypes.end(); }; + bool decompressTPC = inputType == InputType::CompClustersCTF || inputType == InputType::CompClusters; + // Disable not applicable settings depending on TPC input, no need to disable manually + if (decompressTPC && (isEnabled(OutputType::Clusters) || isEnabled(OutputType::Tracks))) { + caClusterer = false; + zsOnTheFly = false; + propagateMC = false; + } + if (inputType == InputType::ZSRaw) { + caClusterer = true; + zsOnTheFly = false; + propagateMC = false; + } + if (inputType == InputType::ClustersHardware || inputType == InputType::Clusters) { + caClusterer = false; + } + if (!caClusterer) { + zsOnTheFly = false; + } + if (inputType == InputType::ClustersHardware && isEnabled(OutputType::Digits)) { throw std::invalid_argument("input/output type mismatch, can not produce 'digits' from 'clustershardware'"); } @@ -113,10 +135,6 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec if (inputType == InputType::ZSRaw && isEnabled(OutputType::ClustersHardware)) { throw std::invalid_argument("input/output type mismatch, can not produce 'clustershardware' from 'zsraw'"); } - - if (inputType == InputType::ZSRaw && !caClusterer) { - throw std::invalid_argument("zsraw input needs caclusterer"); - } if (caClusterer && (inputType == InputType::Clusters || inputType == InputType::ClustersHardware)) { throw std::invalid_argument("ca-clusterer requires digits as input"); } @@ -126,12 +144,28 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec WorkflowSpec specs; + // We provide a special publishing method for labels which have been stored in a split format and need + // to be transformed into a contiguous shareable container before publishing. For other branches/types this returns + // false and the generic RootTreeWriter publishing proceeds + static Reader::SpecialPublishHook hook{[](std::string_view name, ProcessingContext& context, o2::framework::Output const& output, char* data) -> bool { + if (TString(name.data()).Contains("TPCDigitMCTruth") || TString(name.data()).Contains("TPCClusterHwMCTruth") || TString(name.data()).Contains("TPCClusterNativeMCTruth")) { + auto storedlabels = reinterpret_cast<o2::dataformats::IOMCTruthContainerView const*>(data); + o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel> flatlabels; + storedlabels->copyandflatten(flatlabels); + LOG(INFO) << "PUBLISHING CONST LABELS " << flatlabels.getNElements(); + context.outputs().snapshot(output, flatlabels); + return true; + } + return false; + }}; + // The OutputSpec of the PublisherSpec is configured depending on the input // type. Note that the configuration of the dispatch trigger in the main file // needs to be done in accordance. This means, if a new input option is added // also the dispatch trigger needs to be updated. if (inputType == InputType::Digits) { using Type = std::vector<o2::tpc::Digit>; + specs.emplace_back(o2::tpc::getPublisherSpec<Type>(PublisherConf{ "tpc-digit-reader", "o2sim", @@ -141,7 +175,7 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec OutputSpec{"TPC", "DIGITSMCTR"}, tpcSectors, laneConfiguration, - }, + &hook}, propagateMC)); } else if (inputType == InputType::ClustersHardware) { specs.emplace_back(o2::tpc::getPublisherSpec(PublisherConf{ @@ -153,7 +187,7 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec OutputSpec{"TPC", "CLUSTERHWMCLBL"}, tpcSectors, laneConfiguration, - }, + &hook}, propagateMC)); } else if (inputType == InputType::Clusters) { specs.emplace_back(o2::tpc::getPublisherSpec(PublisherConf{ @@ -165,7 +199,7 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec OutputSpec{"TPC", "CLNATIVEMCLBL"}, tpcSectors, laneConfiguration, - }, + &hook}, propagateMC)); } else if (inputType == InputType::CompClusters) { // TODO: need to check if we want to store the MC labels alongside with compressed clusters @@ -178,10 +212,10 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec {"clusterbranch", "TPCCompClusters", "Branch with TPC compressed clusters"}, {"clustermcbranch", "TPCClusterNativeMCTruth", "MC label branch"}, OutputSpec{"TPC", "COMPCLUSTERS"}, - OutputSpec{"TPC", "CLNATIVEMCLBL"}, + OutputSpec{"TPC", "CLNATIVEMCLBL"}, // This does not work with labels! std::vector<int>(1, 0), std::vector<int>(1, 0), - }, + &hook}, false)); } else if (inputType == InputType::EncodedClusters) { // TODO: need to check if we want to store the MC labels alongside with encoded clusters @@ -192,10 +226,10 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec {"clusterbranch", "TPCEncodedClusters", "Branch with TPC encoded clusters"}, {"clustermcbranch", "TPCClusterNativeMCTruth", "MC label branch"}, OutputSpec{"TPC", "ENCCLUSTERS"}, - OutputSpec{"TPC", "CLNATIVEMCLBL"}, + OutputSpec{"TPC", "CLNATIVEMCLBL"}, // This does not work with labels! std::vector<int>(1, 0), std::vector<int>(1, 0), - }, + &hook}, false)); } @@ -204,21 +238,17 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec // ClusterDecoderRawSpec bool produceCompClusters = isEnabled(OutputType::CompClusters); bool produceTracks = isEnabled(OutputType::Tracks); - bool runTracker = produceTracks || produceCompClusters; + bool runTracker = produceTracks || produceCompClusters || (isEnabled(OutputType::Clusters) && caClusterer); bool runHWDecoder = !caClusterer && (runTracker || isEnabled(OutputType::Clusters)); bool runClusterer = !caClusterer && (runHWDecoder || isEnabled(OutputType::ClustersHardware)); bool zsDecoder = inputType == InputType::ZSRaw; bool runClusterEncoder = isEnabled(OutputType::EncodedClusters); - bool decompressTPC = inputType == InputType::CompClustersCTF || inputType == InputType::CompClusters; + // input matrix runClusterer &= inputType == InputType::Digitizer || inputType == InputType::Digits; runHWDecoder &= runClusterer || inputType == InputType::ClustersHardware; runTracker &= caClusterer || runHWDecoder || inputType == InputType::Clusters || decompressTPC; - if (decompressTPC && (isEnabled(OutputType::Clusters) || isEnabled(OutputType::Tracks)) && (caClusterer || zsOnTheFly || propagateMC)) { - throw std::invalid_argument("Compressed clusters as input are incompatible to ca-clusterer, zs-on-the-fly, propagate-mc"); - } - bool outRaw = inputType == InputType::Digits && isEnabled(OutputType::ZSRaw); //bool runZSDecode = inputType == InputType::ZSRaw; bool zsToDigit = inputType == InputType::ZSRaw && isEnabled(OutputType::Digits); @@ -290,6 +320,15 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec // ------------------------------------------------------------------------------------------- // helper to create writer specs for different types of output + auto fillLabels = [](TBranch& branch, std::vector<char> const& labelbuffer, DataRef const& /*ref*/) { + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> labels(labelbuffer); + o2::dataformats::IOMCTruthContainerView outputcontainer; + auto br = framework::RootTreeWriter::remapBranch(branch, &outputcontainer); + outputcontainer.adopt(labelbuffer); + br->Fill(); + br->ResetAddress(); + }; + auto makeWriterSpec = [tpcSectors, laneConfiguration, propagateMC, getIndex, getName](const char* processName, const char* defaultFileName, const char* defaultTreeName, @@ -330,7 +369,6 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec // selected by output type 'difits' if (isEnabled(OutputType::Digits) && !isEnabled(OutputType::DisableWriter)) { using DigitOutputType = std::vector<o2::tpc::Digit>; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; specs.push_back(makeWriterSpec("tpc-digits-writer", inputType == InputType::ZSRaw ? "tpc-zs-digits.root" : inputType == InputType::Digits ? "tpc-filtered-digits.root" : "tpcdigits.root", "o2sim", @@ -348,7 +386,6 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec // // selected by output type 'clustershardware' if (isEnabled(OutputType::ClustersHardware) && !isEnabled(OutputType::DisableWriter)) { - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; specs.push_back(makeWriterSpec("tpc-clusterhardware-writer", inputType == InputType::ClustersHardware ? "tpc-filtered-clustershardware.root" : "tpc-clustershardware.root", "tpcclustershardware", @@ -366,7 +403,6 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec // // selected by output type 'clusters' if (isEnabled(OutputType::Clusters) && !isEnabled(OutputType::DisableWriter)) { - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; // if the caClusterer is enabled, only one data set with the full TPC is produced, and the writer // is configured to write one single branch specs.push_back(makeWriterSpec("tpc-native-cluster-writer", @@ -375,18 +411,18 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec BranchDefinition<const char*>{InputSpec{"data", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}}, "TPCClusterNative", "databranch"}, - BranchDefinition<MCLabelContainer>{InputSpec{"mc", ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}}, - "TPCClusterNativeMCTruth", - "mcbranch"}, - caClusterer || decompressTPC)); + BranchDefinition<std::vector<char>>{InputSpec{"mc", ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}}, + "TPCClusterNativeMCTruth", + "mcbranch", fillLabels}, + (caClusterer || decompressTPC) && !isEnabled(OutputType::SendClustersPerSector))); } if (zsOnTheFly) { - specs.emplace_back(o2::tpc::getZSEncoderSpec(laneConfiguration, zs10bit, zsThreshold, outRaw)); + specs.emplace_back(o2::tpc::getZSEncoderSpec(tpcSectors, zs10bit, zsThreshold, outRaw)); } if (zsToDigit) { - specs.emplace_back(o2::tpc::getZStoDigitsSpec(laneConfiguration)); + specs.emplace_back(o2::tpc::getZStoDigitsSpec(tpcSectors)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -395,19 +431,21 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec // // selected by output type 'tracks' if (runTracker) { - specs.emplace_back(o2::tpc::getCATrackerSpec(ca::Config{ - propagateMC ? ca::Operation::ProcessMC : ca::Operation::Noop, - decompressTPC ? ca::Operation::DecompressTPC : ca::Operation::Noop, - decompressTPC && inputType == InputType::CompClusters ? ca::Operation::DecompressTPCFromROOT : ca::Operation::Noop, - caClusterer ? ca::Operation::CAClusterer : ca::Operation::Noop, - zsDecoder ? ca::Operation::ZSDecoder : ca::Operation::Noop, - zsOnTheFly ? ca::Operation::ZSOnTheFly : ca::Operation::Noop, - produceTracks ? ca::Operation::OutputTracks : ca::Operation::Noop, - produceCompClusters ? ca::Operation::OutputCompClusters : ca::Operation::Noop, - runClusterEncoder ? ca::Operation::OutputCompClustersFlat : ca::Operation::Noop, - isEnabled(OutputType::Clusters) && (caClusterer || decompressTPC) ? ca::Operation::OutputCAClusters : ca::Operation::Noop, - }, - laneConfiguration)); + specs.emplace_back(o2::tpc::getCATrackerSpec(policyData, ca::Config{ + propagateMC ? ca::Operation::ProcessMC : ca::Operation::Noop, + decompressTPC ? ca::Operation::DecompressTPC : ca::Operation::Noop, + decompressTPC && inputType == InputType::CompClusters ? ca::Operation::DecompressTPCFromROOT : ca::Operation::Noop, + caClusterer ? ca::Operation::CAClusterer : ca::Operation::Noop, + zsDecoder ? ca::Operation::ZSDecoder : ca::Operation::Noop, + zsOnTheFly ? ca::Operation::ZSOnTheFly : ca::Operation::Noop, + produceTracks ? ca::Operation::OutputTracks : ca::Operation::Noop, + produceCompClusters ? ca::Operation::OutputCompClusters : ca::Operation::Noop, + runClusterEncoder ? ca::Operation::OutputCompClustersFlat : ca::Operation::Noop, + isEnabled(OutputType::SendClustersPerSector) ? ca::Operation::SendClustersPerSector : ca::Operation::Noop, + isEnabled(OutputType::QA) ? ca::Operation::OutputQA : ca::Operation::Noop, + isEnabled(OutputType::Clusters) && (caClusterer || decompressTPC) ? ca::Operation::OutputCAClusters : ca::Operation::Noop, + }, + tpcSectors)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -436,23 +474,21 @@ framework::WorkflowSpec getWorkflow(std::vector<int> const& tpcSectors, std::vec using TrackOutputType = std::vector<o2::tpc::TrackTPC>; using ClusRefsOutputType = std::vector<o2::tpc::TPCClRefElem>; - - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; // a spectator callback which will be invoked by the tree writer with the extracted object // we are using it for printing a log message auto logger = BranchDefinition<TrackOutputType>::Spectator([](TrackOutputType const& tracks) { LOG(INFO) << "writing " << tracks.size() << " track(s)"; }); - auto tracksdef = BranchDefinition<TrackOutputType>{InputSpec{"inputTracks", "TPC", "TRACKS", 0}, // - "TPCTracks", "track-branch-name", // - 1, // - logger}; // - auto clrefdef = BranchDefinition<ClusRefsOutputType>{InputSpec{"inputClusRef", "TPC", "CLUSREFS", 0}, // - "ClusRefs", "trackclusref-branch-name"}; // - auto mcdef = BranchDefinition<MCLabelContainer>{InputSpec{"mcinput", "TPC", "TRACKSMCLBL", 0}, // - "TPCTracksMCTruth", // - (propagateMC ? 1 : 0), // - "trackmc-branch-name"}; // + auto tracksdef = BranchDefinition<TrackOutputType>{InputSpec{"inputTracks", "TPC", "TRACKS", 0}, // + "TPCTracks", "track-branch-name", // + 1, // + logger}; // + auto clrefdef = BranchDefinition<ClusRefsOutputType>{InputSpec{"inputClusRef", "TPC", "CLUSREFS", 0}, // + "ClusRefs", "trackclusref-branch-name"}; // + auto mcdef = BranchDefinition<std::vector<o2::MCCompLabel>>{InputSpec{"mcinput", "TPC", "TRACKSMCLBL", 0}, // + "TPCTracksMCTruth", // + (propagateMC ? 1 : 0), // + "trackmc-branch-name"}; // // depending on the MC propagation flag, branch definition for MC labels is disabled specs.push_back(MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, diff --git a/Detectors/TPC/workflow/src/TrackReaderSpec.cxx b/Detectors/TPC/workflow/src/TrackReaderSpec.cxx index 69069c3078221..7f1c12b54c8ff 100644 --- a/Detectors/TPC/workflow/src/TrackReaderSpec.cxx +++ b/Detectors/TPC/workflow/src/TrackReaderSpec.cxx @@ -60,7 +60,7 @@ void TrackReader::accumulate(int from, int n) mTracksOut.swap(*mTracksInp); mCluRefVecOut.swap(*mCluRefVecInp); if (mUseMC) { - mMCTruthOut.mergeAtBack(*mMCTruthInp); + std::copy(mMCTruthInp->begin(), mMCTruthInp->end(), std::back_inserter(mMCTruthOut)); } } else { for (int iev = 0; iev < n; iev++) { @@ -83,7 +83,7 @@ void TrackReader::accumulate(int from, int n) std::copy(tr0, tr1, std::back_inserter(mTracksOut)); // MC if (mUseMC) { - mMCTruthOut.mergeAtBack(*mMCTruthInp); + std::copy(mMCTruthInp->begin(), mMCTruthInp->end(), std::back_inserter(mMCTruthOut)); } } } diff --git a/Detectors/TPC/workflow/src/ZSSpec.cxx b/Detectors/TPC/workflow/src/ZSSpec.cxx index f2caffca49296..91db1b93f5dbd 100644 --- a/Detectors/TPC/workflow/src/ZSSpec.cxx +++ b/Detectors/TPC/workflow/src/ZSSpec.cxx @@ -65,7 +65,6 @@ DataProcessorSpec getZSEncoderSpec(std::vector<int> const& inputIds, bool zs10bi struct ProcessAttributes { std::unique_ptr<unsigned long long int[]> zsoutput; std::vector<unsigned int> sizes; - std::unique_ptr<o2::gpu::GPUReconstructionConvert> zsEncoder; std::vector<int> inputIds; bool verify = false; int verbosity = 1; @@ -74,7 +73,6 @@ DataProcessorSpec getZSEncoderSpec(std::vector<int> const& inputIds, bool zs10bi auto initFunction = [inputIds, zs10bit, threshold, outRaw](InitContext& ic) { auto processAttributes = std::make_shared<ProcessAttributes>(); - auto& zsEncoder = processAttributes->zsEncoder; auto& zsoutput = processAttributes->zsoutput; processAttributes->inputIds = inputIds; auto& verify = processAttributes->verify; @@ -87,7 +85,6 @@ DataProcessorSpec getZSEncoderSpec(std::vector<int> const& inputIds, bool zs10bi return; } - auto& zsEncoder = processAttributes->zsEncoder; auto& zsoutput = processAttributes->zsoutput; auto& verify = processAttributes->verify; auto& sizes = processAttributes->sizes; @@ -97,8 +94,10 @@ DataProcessorSpec getZSEncoderSpec(std::vector<int> const& inputIds, bool zs10bi GPUTrackingInOutDigits inDigitsGPU; GPUParam _GPUParam; - _GPUParam.SetDefaults(5.00668); - const GPUParam mGPUParam = _GPUParam; + GPUO2InterfaceConfiguration config; + config.configEvent.solenoidBz = 5.00668; + config.ReadConfigurableParam(); + _GPUParam.SetDefaults(&config.configEvent, &config.configReconstruction, &config.configProcessing, nullptr); int operation = 0; std::vector<InputSpec> filter = {{"check", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITS"}, Lifetime::Timeframe}}; @@ -128,7 +127,7 @@ DataProcessorSpec getZSEncoderSpec(std::vector<int> const& inputIds, bool zs10bi sizes.resize(NSectors * NEndpoints); bool zs12bit = !zs10bit; o2::InteractionRecord ir = o2::raw::HBFUtils::Instance().getFirstIR(); - zsEncoder->RunZSEncoder<o2::tpc::Digit, DigitArray>(inputDigits, &zsoutput, sizes.data(), nullptr, &ir, mGPUParam, zs12bit, verify, threshold); + o2::gpu::GPUReconstructionConvert::RunZSEncoder<o2::tpc::Digit, DigitArray>(inputDigits, &zsoutput, sizes.data(), nullptr, &ir, _GPUParam, zs12bit, verify, threshold); ZeroSuppressedContainer8kb* page = reinterpret_cast<ZeroSuppressedContainer8kb*>(zsoutput.get()); unsigned int offset = 0; for (unsigned int i = 0; i < NSectors; i++) { @@ -179,7 +178,7 @@ DataProcessorSpec getZSEncoderSpec(std::vector<int> const& inputIds, bool zs10bi if (useGrouping != LinksGrouping::Link) { writer.useCaching(); } - zsEncoder->RunZSEncoder<o2::tpc::Digit>(inputDigits, nullptr, nullptr, &writer, &ir, mGPUParam, zs12bit, false, threshold); + o2::gpu::GPUReconstructionConvert::RunZSEncoder<o2::tpc::Digit>(inputDigits, nullptr, nullptr, &writer, &ir, _GPUParam, zs12bit, false, threshold); writer.writeConfFile("TPC", "RAWDATA", fmt::format("{}tpcraw.cfg", outDir)); } zsoutput.reset(nullptr); @@ -242,10 +241,12 @@ DataProcessorSpec getZStoDigitsSpec(std::vector<int> const& inputIds) // sort digits for (auto& digits : outDigits) { std::sort(digits.begin(), digits.end(), [](const auto& a, const auto& b) { - if (a.getTimeStamp() < b.getTimeStamp()) + if (a.getTimeStamp() < b.getTimeStamp()) { return true; - if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) + } + if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) { return true; + } return false; }); } diff --git a/Detectors/TPC/workflow/src/tpc-calib-pedestal.cxx b/Detectors/TPC/workflow/src/tpc-calib-pedestal.cxx new file mode 100644 index 0000000000000..ba58c351beaab --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-calib-pedestal.cxx @@ -0,0 +1,75 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <fmt/format.h> +#include "Framework/WorkflowSpec.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/ControlService.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "DPLUtils/RawParser.h" +#include "Headers/DataHeader.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCCalibration/CalibPedestal.h" +#include "TPCReconstruction/RawReaderCRU.h" +#include <vector> +#include <string> +#include "DetectorsRaw/RDHUtils.h" +#include "TPCBase/RDHUtils.h" +#include "TPCWorkflow/TPCCalibPedestalSpec.h" + +using namespace o2::framework; +using RDHUtils = o2::raw::RDHUtils; + +// customize the completion policy +void customize(std::vector<o2::framework::CompletionPolicy>& policies) +{ + using o2::framework::CompletionPolicy; + policies.push_back(CompletionPolicyHelpers::defineByName("calib-tpc-pedestal", CompletionPolicy::CompletionOp::Consume)); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + std::vector<ConfigParamSpec> options{ + {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"no-calib-output", VariantType::Bool, false, {"skip sending the calibration output"}}, + }; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +using RDH = o2::header::RAWDataHeader; + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + using namespace o2::tpc; + + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(config.options().get<std::string>("configFile")); + o2::conf::ConfigurableParam::updateFromString(config.options().get<std::string>("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpccalibration_configuration.ini"); + + const std::string inputSpec = config.options().get<std::string>("input-spec"); + const auto skipCalib = config.options().get<bool>("no-calib-output"); + + WorkflowSpec workflow; + workflow.emplace_back(getTPCCalibPedestalSpec(inputSpec, skipCalib)); + + return workflow; +} diff --git a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx index 8c245bec30d0c..b1ebf0fc1e8ea 100644 --- a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx @@ -12,8 +12,6 @@ #include "Framework/WorkflowSpec.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ControlService.h" -#include "Framework/CompletionPolicy.h" -#include "Framework/CompletionPolicyHelpers.h" #include "Framework/Logger.h" #include "Framework/ConfigParamSpec.h" #include "CommonUtils/ConfigurableParam.h" @@ -29,17 +27,6 @@ using namespace o2::framework; -// customize the completion policy -void customize(std::vector<o2::framework::CompletionPolicy>& policies) -{ - // we customize the completion policy for the writer since it should stream immediately - using CompletionPolicy = o2::framework::CompletionPolicy; - using CompletionPolicyHelpers = o2::framework::CompletionPolicyHelpers; - policies.push_back(CompletionPolicyHelpers::defineByName("tpc-cluster-decoder.*", CompletionPolicy::CompletionOp::Consume)); - policies.push_back(CompletionPolicyHelpers::defineByName("tpc-clusterer.*", CompletionPolicy::CompletionOp::Consume)); - policies.push_back(CompletionPolicyHelpers::defineByName("tpc-tracker.*", CompletionPolicy::CompletionOp::Consume)); -} - enum class DecoderType { GBT, ///< GBT frame raw decoding LinkZS ///< Link based zero suppression @@ -126,8 +113,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) recoOuput = tpcRecoOutputType.c_str(); } - auto tpcRecoWorkflow = o2::tpc::reco_workflow::getWorkflow(tpcSectors, tpcSectors, false, lanes, "digitizer", recoOuput.data()); - specs.insert(specs.end(), tpcRecoWorkflow.begin(), tpcRecoWorkflow.end()); - return specs; } diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 3f2635ea5a0f0..b10b2c08f5293 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -35,6 +35,9 @@ // publisher will trigger on. This is dependent on the input type o2::framework::Output gDispatchTrigger{"", ""}; +// Global variable used to transport data to the completion policy +o2::tpc::reco_workflow::CompletionPolicyData gPolicyData; + // add workflow options, note that customization needs to be declared before // including Framework/runDataProcessing void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) @@ -43,15 +46,15 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) std::vector<ConfigParamSpec> options{ {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clustersnative, compressed-clusters, compressed-clusters-ctf"}}, - {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clustersnative, tracks, compressed-clusters, encoded-clusters, disable-writer"}}, - {"ca-clusterer", VariantType::Bool, false, {"Use clusterer of GPUCATracking"}}, + {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clustersnative, tracks, compressed-clusters, encoded-clusters, disable-writer, send-clusters-per-sector, qa"}}, + {"no-ca-clusterer", VariantType::Bool, false, {"Use HardwareClusterer instead of clusterer of GPUCATracking"}}, {"disable-mc", VariantType::Bool, false, {"disable sending of MC information"}}, - {"tpc-sectors", VariantType::String, "0-35", {"TPC sector range, e.g. 5-7,8,9"}}, + //{"tpc-sectors", VariantType::String, "0-35", {"TPC sector range, e.g. 5-7,8,9"}}, {"tpc-lanes", VariantType::Int, 1, {"number of parallel lanes up to the tracker"}}, {"dispatching-mode", VariantType::String, "prompt", {"determines when to dispatch: prompt, complete"}}, - {"tpc-zs", VariantType::Bool, false, {"use TPC zero suppression, true/false"}}, + {"no-tpc-zs-on-the-fly", VariantType::Bool, false, {"Do not use TPC zero suppression on the fly"}}, {"zs-threshold", VariantType::Float, 2.0f, {"zero suppression threshold"}}, - {"zs-10bit", VariantType::Bool, false, {"use 10 bit ADCs for TPC zero suppression, true/false, default/false = 12 bit ADC"}}, + {"zs-10bit", VariantType::Bool, false, {"use 10 bit ADCs for TPC zero suppression, default = 12 bit ADC"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCHwClusterer.peakChargeThreshold=4;...')"}}, {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}}; @@ -85,9 +88,7 @@ void customize(std::vector<o2::framework::CompletionPolicy>& policies) policies.push_back(CompletionPolicyHelpers::defineByName("tpc-cluster-decoder.*", CompletionPolicy::CompletionOp::Consume)); policies.push_back(CompletionPolicyHelpers::defineByName("tpc-clusterer.*", CompletionPolicy::CompletionOp::Consume)); // the custom completion policy for the tracker - policies.push_back(o2::tpc::TPCSectorCompletionPolicy("tpc-tracker.*", - o2::framework::InputSpec{"cluster", o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}}, - o2::framework::InputSpec{"digits", o2::framework::ConcreteDataTypeMatcher{"TPC", "DIGITS"}})()); + policies.push_back(o2::tpc::TPCSectorCompletionPolicy("tpc-tracker.*", o2::tpc::TPCSectorCompletionPolicy::Config::RequireAll, &gPolicyData)()); } #include "Framework/runDataProcessing.h" // the main driver @@ -110,12 +111,13 @@ using namespace o2::framework; /// This function hooks up the the workflow specifications into the DPL driver. WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - auto tpcSectors = o2::RangeTokenizer::tokenize<int>(cfgc.options().get<std::string>("tpc-sectors")); + //auto tpcSectors = o2::RangeTokenizer::tokenize<int>(cfgc.options().get<std::string>("tpc-sectors")); + std::vector<int> tpcSectors(36); + std::iota(tpcSectors.begin(), tpcSectors.end(), 0); // the lane configuration defines the subspecification ids to be distributed among the lanes. - std::vector<int> laneConfiguration; + std::vector<int> laneConfiguration = tpcSectors; // Currently just a copy of the tpcSectors, why? auto nLanes = cfgc.options().get<int>("tpc-lanes"); auto inputType = cfgc.options().get<std::string>("input-type"); - laneConfiguration = tpcSectors; // depending on whether to dispatch early (prompt) and on the input type, we // set the matcher. Note that this has to be in accordance with the OutputSpecs @@ -139,15 +141,16 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::writeINI("o2tpcrecoworkflow_configuration.ini"); bool doMC = not cfgc.options().get<bool>("disable-mc"); - return o2::tpc::reco_workflow::getWorkflow(tpcSectors, // sector configuration - laneConfiguration, // lane configuration - doMC, // - nLanes, // - inputType, // - cfgc.options().get<std::string>("output-type"), // - cfgc.options().get<bool>("ca-clusterer"), // - cfgc.options().get<bool>("tpc-zs"), // - cfgc.options().get<bool>("zs-10bit"), // - cfgc.options().get<float>("zs-threshold") // + return o2::tpc::reco_workflow::getWorkflow(&gPolicyData, // + tpcSectors, // sector configuration + laneConfiguration, // lane configuration + doMC, // + nLanes, // + inputType, // + cfgc.options().get<std::string>("output-type"), // + !cfgc.options().get<bool>("no-ca-clusterer"), // + !cfgc.options().get<bool>("no-tpc-zs-on-the-fly"), // + cfgc.options().get<bool>("zs-10bit"), // + cfgc.options().get<float>("zs-threshold") // ); } diff --git a/Detectors/TPC/workflow/test/test_TPCWorkflow.cxx b/Detectors/TPC/workflow/test/test_TPCWorkflow.cxx index e5d3154bfa69f..83c156851f91b 100644 --- a/Detectors/TPC/workflow/test/test_TPCWorkflow.cxx +++ b/Detectors/TPC/workflow/test/test_TPCWorkflow.cxx @@ -21,6 +21,7 @@ #include <TTree.h> using namespace o2; +using namespace o2::dataformats; BOOST_AUTO_TEST_CASE(TPCWorkflow_types) { @@ -30,8 +31,7 @@ BOOST_AUTO_TEST_CASE(TPCWorkflow_types) // even though std::vector < o2::dataformats::MCTruthContainer < o2::MCCompLabel >> // has not been specified in the LinkDef file. But it can only e serialized to a // tree branch if it has been defined. - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - MCLabelContainer labels; + dataformats::MCLabelContainer labels; std::vector<MCLabelContainer> containers; const char* filename = "testTPCWorkflowTypes.root"; const char* treename = "testtree"; diff --git a/Detectors/TRD/base/CMakeLists.txt b/Detectors/TRD/base/CMakeLists.txt index bb97e82247ef9..cf970c5282758 100644 --- a/Detectors/TRD/base/CMakeLists.txt +++ b/Detectors/TRD/base/CMakeLists.txt @@ -8,13 +8,13 @@ # submit itself to any jurisdiction. o2_add_library(TRDBase - SOURCES src/TRDPadPlane.cxx - src/TRDGeometryBase.cxx - src/TRDGeometry.cxx - src/TRDGeometryFlat.cxx - src/TRDCommonParam.cxx - src/TRDDiffAndTimeStructEstimator.cxx - src/TRDSimParam.cxx + SOURCES src/PadPlane.cxx + src/GeometryBase.cxx + src/Geometry.cxx + src/GeometryFlat.cxx + src/CommonParam.cxx + src/DiffAndTimeStructEstimator.cxx + src/SimParam.cxx src/PadResponse.cxx src/Digit.cxx src/CalDet.cxx @@ -29,6 +29,7 @@ o2_add_library(TRDBase src/ChamberNoise.cxx src/CalOnlineGainTables.cxx src/Tracklet.cxx + src/TrackletTransformer.cxx PUBLIC_LINK_LIBRARIES O2::GPUCommon O2::GPUUtils O2::DetectorsCommonDataFormats @@ -36,15 +37,16 @@ o2_add_library(TRDBase O2::DetectorsBase ROOT::Physics O2::SimulationDataFormat + O2::DataFormatsTRD O2::CCDB) o2_target_root_dictionary(TRDBase - HEADERS include/TRDBase/TRDPadPlane.h - include/TRDBase/TRDGeometryBase.h - include/TRDBase/TRDGeometry.h - include/TRDBase/TRDGeometryFlat.h - include/TRDBase/TRDSimParam.h - include/TRDBase/TRDCommonParam.h + HEADERS include/TRDBase/PadPlane.h + include/TRDBase/GeometryBase.h + include/TRDBase/Geometry.h + include/TRDBase/GeometryFlat.h + include/TRDBase/SimParam.h + include/TRDBase/CommonParam.h include/TRDBase/PadResponse.h include/TRDBase/Digit.h include/TRDBase/MCLabel.h @@ -62,7 +64,8 @@ o2_target_root_dictionary(TRDBase include/TRDBase/Calibrations.h include/TRDBase/ChamberNoise.h include/TRDBase/CalOnlineGainTables.h - include/TRDBase/Tracklet.h) + include/TRDBase/Tracklet.h + include/TRDBase/TrackletTransformer.h) o2_add_test(DiffusionCoefficient SOURCES test/testTRDDiffusionCoefficient.cxx @@ -78,3 +81,10 @@ o2_add_test(Geometry ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage LABELS trd ) +o2_add_test(RawData + COMPONENT_NAME trd + PUBLIC_LINK_LIBRARIES O2::TRDBase O2::DataFormatsTRD + SOURCES test/testRawData.cxx + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage + LABELS trd + ) diff --git a/Detectors/TRD/base/include/TRDBase/CalDet.h b/Detectors/TRD/base/include/TRDBase/CalDet.h index 5216976a3b3d9..9affc767fdcdc 100644 --- a/Detectors/TRD/base/include/TRDBase/CalDet.h +++ b/Detectors/TRD/base/include/TRDBase/CalDet.h @@ -18,8 +18,10 @@ // // /////////////////////////////////////////////////////////////////////////////// -class TRDGeometry; -class TRDPadPlane; +#include "DataFormatsTRD/Constants.h" + +class Geometry; +class PadPlane; class TH1F; class TH2F; @@ -31,17 +33,13 @@ namespace trd class CalDet { public: - enum { kNplan = 6, - kNcham = 5, - kNsect = 18, - kNdet = 540 }; CalDet(std::string name = "CalDet", std::string title = "CalDet") : mName(name), mTitle(title){}; ~CalDet() = default; // float getValue(int d) const { return mData[d]; }; - float getValue(int p, int c, int s) const { return mData[TRDGeometry::getDetector(p, c, s)]; }; + float getValue(int p, int c, int s) const { return mData[Geometry::getDetector(p, c, s)]; }; void setValue(int d, float value) { mData[d] = value; }; - void setValue(int p, int c, int s, float value) { mData[TRDGeometry::getDetector(p, c, s)] = value; }; + void setValue(int p, int c, int s, float value) { mData[Geometry::getDetector(p, c, s)] = value; }; void setName(std::string name) { mName = name; }; // these 4 get and set methods are probably not needed (were not in the old code) but are here for completeness void setTitle(std::string title) { mTitle = title; }; std::string& getName() { return mName; }; @@ -71,7 +69,7 @@ class CalDet void divide(const CalDet* calDet); protected: - std::array<float, kNdet> mData{}; // Data + std::array<float, constants::NSECTOR> mData{}; // Data std::string mName; // name for spectra, carried over originally from inheritence from TNamed std::string mTitle; // title prepend for spectra, carried over originally from inheritence from TNamed }; diff --git a/Detectors/TRD/base/include/TRDBase/CalOnlineGainTables.h b/Detectors/TRD/base/include/TRDBase/CalOnlineGainTables.h index c44b70ab06f8f..2537830bac80c 100644 --- a/Detectors/TRD/base/include/TRDBase/CalOnlineGainTables.h +++ b/Detectors/TRD/base/include/TRDBase/CalOnlineGainTables.h @@ -28,7 +28,7 @@ /////////////////////////////////////////////////////////////////////////////// #include <array> -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" class FeeParam; namespace o2 @@ -44,34 +44,34 @@ class CalOnlineGainTables // get and set the various values stored internally in the gain tables. float getGainCorrectionFactorrm(int det, int rob, int mcm) const; float getGainCorrectionFactor(int det, int row, int col) const; - float getGainCorrectionFactor(int sector, int stack, int layer, int row, int col) const { return getGainCorrectionFactor(TRDGeometry::getDetector(sector, stack, layer), row, col); }; + float getGainCorrectionFactor(int sector, int stack, int layer, int row, int col) const { return getGainCorrectionFactor(Geometry::getDetector(sector, stack, layer), row, col); }; short getAdcdacrm(int det, int rob, int mcm) const; short getAdcdac(int det, int row, int col) const; - short getAdcdac(int sector, int stack, int layer, int row, int col) const { return getAdcdac(TRDGeometry::getDetector(sector, stack, layer), row, col); }; + short getAdcdac(int sector, int stack, int layer, int row, int col) const { return getAdcdac(Geometry::getDetector(sector, stack, layer), row, col); }; float getMCMGainrm(int det, int rob, int mcm) const; float getMCMGain(int det, int row, int col) const; - float getMCMGain(int sector, int stack, int layer, int row, int col) const { return getMCMGain(TRDGeometry::getDetector(sector, stack, layer), row, col); }; + float getMCMGain(int sector, int stack, int layer, int row, int col) const { return getMCMGain(Geometry::getDetector(sector, stack, layer), row, col); }; short getFGANrm(int det, int rob, int mcm, int channel) const; short getFGAN(int det, int row, int col) const; - short getFGAN(int sector, int stack, int layer, int row, int col) const { return getFGAN(TRDGeometry::getDetector(sector, stack, layer), row, col); }; + short getFGAN(int sector, int stack, int layer, int row, int col) const { return getFGAN(Geometry::getDetector(sector, stack, layer), row, col); }; short getFGFNrm(int det, int rob, int mcm, int channel) const; short getFGFN(int det, int row, int col) const; - short getFGFN(int sector, int stack, int layer, int row, int col) const { return getFGFN(TRDGeometry::getDetector(sector, stack, layer), row, col); }; + short getFGFN(int sector, int stack, int layer, int row, int col) const { return getFGFN(Geometry::getDetector(sector, stack, layer), row, col); }; void setGainCorrectionFactorrm(int det, int rob, int mcm, float gain); void setGainCorrectionFactor(int det, int row, int col, float gain); - void setGainCorrectionFactor(int sector, int stack, int layer, int row, int col, float gain) { setGainCorrectionFactor(TRDGeometry::getDetector(sector, stack, layer), row, col, gain); }; + void setGainCorrectionFactor(int sector, int stack, int layer, int row, int col, float gain) { setGainCorrectionFactor(Geometry::getDetector(sector, stack, layer), row, col, gain); }; void setAdcdacrm(int det, int rob, int mcm, short gain); void setAdcdac(int det, int row, int col, short gain); - void setAdcdac(int sector, int stack, int layer, int row, int col, short gain) { setAdcdac(TRDGeometry::getDetector(sector, stack, layer), row, col, gain); }; + void setAdcdac(int sector, int stack, int layer, int row, int col, short gain) { setAdcdac(Geometry::getDetector(sector, stack, layer), row, col, gain); }; void setMCMGainrm(int det, int rob, int mcm, float gain); void setMCMGain(int det, int row, int col, float gain); - void setMCMGain(int sector, int stack, int layer, int row, int col, float gain) { setMCMGain(TRDGeometry::getDetector(sector, stack, layer), row, col, gain); }; + void setMCMGain(int sector, int stack, int layer, int row, int col, float gain) { setMCMGain(Geometry::getDetector(sector, stack, layer), row, col, gain); }; void setFGANrm(int det, int rob, int mcm, int channel, short gain); void setFGAN(int det, int row, int col, short gain); - void setFGAN(int sector, int stack, int layer, int row, int col, short gain) { setFGAN(TRDGeometry::getDetector(sector, stack, layer), row, col, gain); }; + void setFGAN(int sector, int stack, int layer, int row, int col, short gain) { setFGAN(Geometry::getDetector(sector, stack, layer), row, col, gain); }; void setFGFNrm(int det, int rob, int mcm, int channel, short gain); void setFGFN(int det, int row, int col, short gain); - void setFGFN(int sector, int stack, int layer, int row, int col, short gain) { setFGFN(TRDGeometry::getDetector(sector, stack, layer), row, col, gain); }; + void setFGFN(int sector, int stack, int layer, int row, int col, short gain) { setFGFN(Geometry::getDetector(sector, stack, layer), row, col, gain); }; // these 4 are used primarily to reading in from run2 ocdb, might have wider uses. void setAdcdac(int arrayoffset, short adc) { mGainTable[arrayoffset].mAdcdac = adc; }; diff --git a/Detectors/TRD/base/include/TRDBase/CalPad.h b/Detectors/TRD/base/include/TRDBase/CalPad.h index 9795dcff40052..a9708af8d1f49 100644 --- a/Detectors/TRD/base/include/TRDBase/CalPad.h +++ b/Detectors/TRD/base/include/TRDBase/CalPad.h @@ -17,6 +17,8 @@ // // /////////////////////////////////////////////////////////////////////////////// +#include "DataFormatsTRD/Constants.h" + class CalROC; class CalDet; class TH2F; @@ -26,10 +28,6 @@ class CalPad { public: - enum { kNplan = 6, - kNcham = 5, - kNsect = 18, - kNdet = 540 }; CalPad(); CalPad(const std::string& name, const std::String& title); @@ -37,7 +35,7 @@ class CalPad ~CalPad(); CalPad& operator=(const CalPad& c); - static int getDet(int p, int c, int s) { return p + c * kNplan + s * kNplan * kNcham; }; + static int getDet(int p, int c, int s) { return p + c * Constants::NLAYER + s * Constants::NLAYER * Constants::NSTACK; }; CalROC* getCalROC(int d) const { return mROC[d]; }; CalROC* getCalROC(int p, int c, int s) const @@ -69,7 +67,7 @@ class CalPad bool divide(const CalPad* pad, const CalDet* calDet1 = 0, const CalDet* calDet2 = 0, int type = 0); protected: - std::vector<CalROC> mROC(kNdet); // Array of ROC objects which contain the values per pad + std::vector<CalROC> mROC(Constants::MAXCHAMBER); // Array of ROC objects which contain the values per pad ClassDef(CalPad, 1) // TRD calibration class for parameters which are saved per pad }; diff --git a/Detectors/TRD/base/include/TRDBase/CalPadStatus.h b/Detectors/TRD/base/include/TRDBase/CalPadStatus.h new file mode 100644 index 0000000000000..32a61b5285a61 --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/CalPadStatus.h @@ -0,0 +1,96 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_CALPADSTATUS_H +#define O2_TRD_CALPADSTATUS_H + +#include "DataFormatsTRD/Constants.h" + +#include "TH1F.h" +#include "TH2F.h" +#include <string> + +namespace o2 +{ +namespace trd +{ + +/////////////////////////////////////////////////////////////////////////////// +// // +// TRD calibration class for the single pad status // +// // +/////////////////////////////////////////////////////////////////////////////// + +class CalSingleChamberStatus; + +class CalPadStatus +{ + + public: + enum { kMasked = 2, + kPadBridgedLeft = 4, + kPadBridgedRight = 8, + kReadSecond = 16, + kNotConnected = 32 }; + + CalPadStatus(); + CalPadStatus(const Text_t* name, const Text_t* title); + CalPadStatus(const CalPadStatus& c); + ~CalPadStatus(); + CalPadStatus& operator=(const CalPadStatus& c); + + void Copy(CalPadStatus& c) const; + + Bool_t isMasked(Int_t d, Int_t col, Int_t row) const + { + return checkStatus(d, col, row, kMasked); + }; + Bool_t isBridgedLeft(Int_t d, Int_t col, Int_t row) const + { + return checkStatus(d, col, row, kPadBridgedLeft); + }; + Bool_t isBridgedRight(Int_t d, Int_t col, Int_t row) const + { + return checkStatus(d, col, row, kPadBridgedRight); + }; + Bool_t isReadSecond(Int_t d, Int_t col, Int_t row) const + { + return checkStatus(d, col, row, kReadSecond); + }; + Bool_t isNotConnected(Int_t d, Int_t col, Int_t row) const + { + return checkStatus(d, col, row, kNotConnected); + }; + Bool_t checkStatus(Int_t d, Int_t col, Int_t row, Int_t bitMask) const; + + CalSingleChamberStatus* getCalROC(Int_t d) const { return mROC[d]; }; + CalSingleChamberStatus* getCalROC(Int_t p, Int_t c, Int_t s) const; + + // Plot functions + TH1F* makeHisto1D(); + TH2F* makeHisto2DSmPl(Int_t sm, Int_t pl); + void plotHistos2DSm(Int_t sm, const Char_t* name); + + std::string getTitle() { return mTitle; }; + std::string getName() { return mName; }; + void setTitle(const std::string newTitle) { mTitle = newTitle; }; + void setName(const std::string newName) { mName = newName; }; + + protected: + CalSingleChamberStatus* mROC[constants::MAXCHAMBER]; // Array of ROC objects which contain the values per pad + + private: + std::string mName; + std::string mTitle; + ClassDefNV(CalPadStatus, 1); // TRD calibration class for the single pad status +}; +} //namespace trd +} //namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/CalSingleChamberStatus.h b/Detectors/TRD/base/include/TRDBase/CalSingleChamberStatus.h new file mode 100644 index 0000000000000..d1566ab1b0266 --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/CalSingleChamberStatus.h @@ -0,0 +1,76 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_CALSINGLECHAMBERSTATUS_H +#define O2_TRD_CALSINGLECHAMBERSTATUS_H + +#include <Rtypes.h> +namespace o2 +{ +namespace trd +{ + +//////////////////////////////////////////////////////////////////////////// +// // +// TRD calibration base class containing status values for one ROC // +// // +//////////////////////////////////////////////////////////////////////////// + +//_____________________________________________________________________________ +class CalSingleChamberStatus +{ + + public: + enum { kMasked = 2, + kPadBridgedLeft = 4, + kPadBridgedRight = 8, + kReadSecond = 16, + kNotConnected = 32 }; + + CalSingleChamberStatus(); + CalSingleChamberStatus(Int_t p, Int_t c, Int_t cols); + CalSingleChamberStatus(const CalSingleChamberStatus& c); + ~CalSingleChamberStatus(); + CalSingleChamberStatus& operator=(const CalSingleChamberStatus& c); + void Copy(CalSingleChamberStatus& c) const; + + Bool_t isMasked(Int_t col, Int_t row) const { return ((getStatus(col, row) & kMasked) + ? kTRUE + : kFALSE); }; + Bool_t isBridgedLeft(Int_t col, Int_t row) const { return ((getStatus(col, row) & kPadBridgedLeft) ? kTRUE : kFALSE); }; + Bool_t isBridgedRight(Int_t col, Int_t row) const { return ((getStatus(col, row) & kPadBridgedRight) ? kTRUE : kFALSE); }; + Bool_t isNotConnected(Int_t col, Int_t row) const { return ((getStatus(col, row) & kNotConnected) ? kTRUE : kFALSE); }; + Int_t getNrows() const { return mNrows; }; + Int_t getNcols() const { return mNcols; }; + + Int_t getChannel(Int_t col, Int_t row) const { return row + col * mNrows; }; + Int_t getNchannels() const { return mNchannels; }; + Char_t getStatus(Int_t ich) const { return mData[ich]; }; + Char_t getStatus(Int_t col, Int_t row) const { return mData[getChannel(col, row)]; }; + + void setStatus(Int_t ich, Char_t vd) { mData[ich] = vd; }; + void setStatus(Int_t col, Int_t row, Char_t vd) { mData[getChannel(col, row)] = vd; }; + + protected: + Int_t mPla{0}; // Plane number + Int_t mCha{0}; // Chamber number + + Int_t mNrows{0}; // Number of rows + Int_t mNcols{0}; // Number of columns + + Int_t mNchannels{0}; // Number of channels + std::vector<char> mData; //[fNchannels] Data + + ClassDefNV(CalSingleChamberStatus, 1); // TRD ROC calibration class +}; + +} // namespace trd +} // namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/Calibrations.h b/Detectors/TRD/base/include/TRDBase/Calibrations.h index 6dbb386a5c3c5..3a513b399aa3c 100644 --- a/Detectors/TRD/base/include/TRDBase/Calibrations.h +++ b/Detectors/TRD/base/include/TRDBase/Calibrations.h @@ -35,7 +35,7 @@ #include <memory> #include <string> -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/ChamberCalibrations.h" #include "TRDBase/LocalVDrift.h" #include "TRDBase/LocalT0.h" @@ -46,7 +46,7 @@ #include "TRDBase/PadStatus.h" #include "TRDBase/CalOnlineGainTables.h" -class TRDGeometry; +class Geometry; namespace o2 { namespace trd diff --git a/Detectors/TRD/base/include/TRDBase/ChamberCalibrations.h b/Detectors/TRD/base/include/TRDBase/ChamberCalibrations.h index da27f66b7b856..9c433deb76c2e 100644 --- a/Detectors/TRD/base/include/TRDBase/ChamberCalibrations.h +++ b/Detectors/TRD/base/include/TRDBase/ChamberCalibrations.h @@ -18,9 +18,10 @@ // Most things were stored in AliTRDcalROC,AliTRDcalPad, AliTRDcalDet // /////////////////////////////////////////////////////////////////////////////// -#include "TRDBase/TRDSimParam.h" +#include "TRDBase/SimParam.h" +#include "DataFormatsTRD/Constants.h" // -class TRDGeometry; +class Geometry; namespace o2 { @@ -32,30 +33,30 @@ class ChamberCalibrations ChamberCalibrations() = default; ~ChamberCalibrations() = default; // - float getVDrift(int p, int c, int s) const { return mVDrift[o2::trd::TRDGeometry::getDetector(p, c, s)]; }; + float getVDrift(int p, int c, int s) const { return mVDrift[o2::trd::Geometry::getDetector(p, c, s)]; }; float getVDrift(int roc) const { return mVDrift[roc]; }; - float getGainFactor(int p, int c, int s) const { return mGainFactor[TRDGeometry::getDetector(p, c, s)]; }; + float getGainFactor(int p, int c, int s) const { return mGainFactor[Geometry::getDetector(p, c, s)]; }; float getGainFactor(int roc) const { return mGainFactor[roc]; }; - float getT0(int p, int c, int s) const { return mT0[TRDGeometry::getDetector(p, c, s)]; }; + float getT0(int p, int c, int s) const { return mT0[Geometry::getDetector(p, c, s)]; }; float getT0(int roc) const { return mT0[roc]; }; - float getExB(int p, int c, int s) const { return mExB[TRDGeometry::getDetector(p, c, s)]; }; + float getExB(int p, int c, int s) const { return mExB[Geometry::getDetector(p, c, s)]; }; float getExB(int roc) const { return mExB[roc]; }; - void setVDrift(int p, int c, int s, float vdrift) { mVDrift[o2::trd::TRDGeometry::getDetector(p, c, s)] = vdrift; }; + void setVDrift(int p, int c, int s, float vdrift) { mVDrift[o2::trd::Geometry::getDetector(p, c, s)] = vdrift; }; void setVDrift(int roc, float vdrift) { mVDrift[roc] = vdrift; }; - void setGainFactor(int p, int c, int s, float gainfactor) { mGainFactor[TRDGeometry::getDetector(p, c, s)] = gainfactor; }; + void setGainFactor(int p, int c, int s, float gainfactor) { mGainFactor[Geometry::getDetector(p, c, s)] = gainfactor; }; void setGainFactor(int roc, float gainfactor) { mGainFactor[roc] = gainfactor; }; - void setT0(int p, int c, int s, float t0) { mT0[TRDGeometry::getDetector(p, c, s)] = t0; }; + void setT0(int p, int c, int s, float t0) { mT0[Geometry::getDetector(p, c, s)] = t0; }; void setT0(int roc, float t0) { mT0[roc] = t0; }; - void setExB(int p, int c, int s, float exb) { mExB[TRDGeometry::getDetector(p, c, s)] = exb; }; + void setExB(int p, int c, int s, float exb) { mExB[Geometry::getDetector(p, c, s)] = exb; }; void setExB(int roc, float exb) { mExB[roc] = exb; }; //bulk gets ? bool init(int run2run = 0); protected: - std::array<float, TRDSimParam::kNdet> mVDrift{}; // mean drift velocity per chamber. - std::array<float, TRDSimParam::kNdet> mGainFactor{}; // mean gas gain per chamber - std::array<float, TRDSimParam::kNdet> mT0{}; // Min timeoffset in the chamber - std::array<float, TRDSimParam::kNdet> mExB{}; // + std::array<float, constants::MAXCHAMBER> mVDrift{}; // mean drift velocity per chamber. + std::array<float, constants::MAXCHAMBER> mGainFactor{}; // mean gas gain per chamber + std::array<float, constants::MAXCHAMBER> mT0{}; // Min timeoffset in the chamber + std::array<float, constants::MAXCHAMBER> mExB{}; // }; } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/base/include/TRDBase/ChamberNoise.h b/Detectors/TRD/base/include/TRDBase/ChamberNoise.h index dfa477457d7cc..a6f4df8785fb6 100644 --- a/Detectors/TRD/base/include/TRDBase/ChamberNoise.h +++ b/Detectors/TRD/base/include/TRDBase/ChamberNoise.h @@ -18,9 +18,10 @@ // Originally stored in AliTRDCalDet and instantiated at DetNoise in ocdb // /////////////////////////////////////////////////////////////////////////////// -#include "TRDBase/TRDSimParam.h" +#include "TRDBase/SimParam.h" +#include "DataFormatsTRD/Constants.h" // -class TRDGeometry; +class Geometry; namespace o2 { @@ -32,13 +33,13 @@ class ChamberNoise ChamberNoise() = default; ~ChamberNoise() = default; // - float getNoise(int p, int c, int s) const { return mNoise[o2::trd::TRDGeometry::getDetector(p, c, s)]; }; + float getNoise(int p, int c, int s) const { return mNoise[o2::trd::Geometry::getDetector(p, c, s)]; }; float getNoise(int det) const { return mNoise[det]; }; - void setNoise(int p, int c, int s, float noise) { mNoise[o2::trd::TRDGeometry::getDetector(p, c, s)] = noise; }; + void setNoise(int p, int c, int s, float noise) { mNoise[o2::trd::Geometry::getDetector(p, c, s)] = noise; }; void setNoise(int det, float noise) { mNoise[det] = noise; }; //bulk gets ? protected: - std::array<float, TRDSimParam::kNdet> mNoise{}; + std::array<float, constants::MAXCHAMBER> mNoise{}; }; } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/base/include/TRDBase/ChamberStatus.h b/Detectors/TRD/base/include/TRDBase/ChamberStatus.h index afd6951a0d0cf..642a1a56995c4 100644 --- a/Detectors/TRD/base/include/TRDBase/ChamberStatus.h +++ b/Detectors/TRD/base/include/TRDBase/ChamberStatus.h @@ -21,8 +21,9 @@ /////////////////////////////////////////////////////////////////////////////// #include <array> -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDSimParam.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/SimParam.h" +#include "DataFormatsTRD/Constants.h" class TH2D; namespace o2 @@ -49,9 +50,9 @@ class ChamberStatus ChamberStatus() = default; ~ChamberStatus() = default; // - // char getStatus(int p, int c, int s) const { int roc=TRDGeometry::getDetector(p,c,s); return mStatus[roc];} + // char getStatus(int p, int c, int s) const { int roc=Geometry::getDetector(p,c,s); return mStatus[roc];} char getStatus(int det) const { return mStatus[det]; } - // void setStatus(int p, int c, int s, char status) { int roc=TRDGeometry::getDetector(p,c,s); setStatus(roc,status);} + // void setStatus(int p, int c, int s, char status) { int roc=Geometry::getDetector(p,c,s); setStatus(roc,status);} void setStatus(int det, char status); void setRawStatus(int det, char status) { mStatus[det] = status; }; void unsetStatusBit(int det, char status); @@ -67,7 +68,7 @@ class ChamberStatus TH2D* plotBadCalibrated(int sm, int rphi); // Plot calibration status for sm and halfchamberside TH2D* plot(int sm); // Plot mStatus for sm protected: - std::array<char, TRDSimParam::kNdet> mStatus{}; + std::array<char, constants::MAXCHAMBER> mStatus{}; }; } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/base/include/TRDBase/CommonParam.h b/Detectors/TRD/base/include/TRDBase/CommonParam.h new file mode 100644 index 0000000000000..5c1f6e079a9ef --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/CommonParam.h @@ -0,0 +1,86 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_COMMONPARAM_H +#define O2_TRD_COMMONPARAM_H + +#include "GPUCommonRtypes.h" + +namespace o2 +{ +namespace trd +{ + +class PadPlane; + +class CommonParam +{ + public: + enum { kXenon = 0, + kArgon = 1 }; + + CommonParam(const CommonParam& p); + CommonParam& operator=(const CommonParam& p); + ~CommonParam(); + + static CommonParam* Instance(); + static void Terminate(); + + void SetExB(int exbOn = 1) { mExBOn = exbOn; } + void SetSamplingFrequency(float freq) { mSamplingFrequency = freq; } + void SetXenon(); + void SetArgon(); + + bool ExBOn() const { return mExBOn; } + bool IsXenon() const { return (mGasMixture == kXenon); } + bool IsArgon() const { return (mGasMixture == kArgon); } + int GetGasMixture() const { return mGasMixture; } + float GetSamplingFrequency() const { return mSamplingFrequency; } + float GetCachedField() const { return mField; } + + // Cached magnetic field, to be called by the user before using GetDiffCoeff or GetOmegaTau + bool cacheMagField(); + float GetOmegaTau(float vdrift); + bool GetDiffCoeff(float& dl, float& dt, float vdrift); + + double TimeStruct(float vdrift, double xd, double z); + + protected: + void SampleTimeStruct(float vdrift); + +#ifndef GPUCA_GPUCODE_DEVICE + static CommonParam* fgInstance; // Instance of this class (singleton implementation) + static bool fgTerminated; // Defines if this class has already been terminated +#endif + int mExBOn; // Switch for the ExB effects + double mField; // cached magnetic field + float mDiffusionT; // Transverse drift coefficient + float mDiffusionL; // Longitudinal drift coefficient + float mDiffLastVdrift; // The structures are valid for fLastVdrift (caching) + + float* mTimeStruct1; //! Time Structure of Drift Cells + float* mTimeStruct2; //! Time Structure of Drift Cells + float mVDlo; // Lower drift velocity, for interpolation + float mVDhi; // Higher drift velocity, for interpolation + float mTimeLastVdrift; // The structures are valid for fLastVdrift (caching) + + float mSamplingFrequency; // Sampling Frequency in MHz + + int mGasMixture; // Gas mixture: 0-Xe/C02 1-Ar/CO2. + + private: + // This is a singleton, constructor is private! + CommonParam(); + + ClassDef(CommonParam, 1); // The constant parameters common to simulation and reconstruction +}; +} // namespace trd +} // namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/DiffAndTimeStructEstimator.h b/Detectors/TRD/base/include/TRDBase/DiffAndTimeStructEstimator.h new file mode 100644 index 0000000000000..33f339ec6e3e1 --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/DiffAndTimeStructEstimator.h @@ -0,0 +1,58 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_DIFFANDTIMESTRUCTESTIMATOR_H +#define O2_TRD_DIFFANDTIMESTRUCTESTIMATOR_H + +#include <array> + +namespace o2 +{ +namespace trd +{ + +// CONTANT TIME STRUCTURE DATA FROM GARFIELD +constexpr int ktimebin = 38; +constexpr int kZbin = 11; + +/// A class to calculate diffusion and time structure values (GARFIELD model) +/// (used in digitization). Class was split off trom CommonParam +/// and is no longer a singleton so that we can use it in a multithreaded context. +class DiffusionAndTimeStructEstimator +{ + public: + DiffusionAndTimeStructEstimator() = default; + + /// determines the diffusion coefficients as a function of drift velocity + bool GetDiffCoeff(float& dl, float& dt, float vdrift); + + /// determines drift time as function of drift velocity and coordinates + float TimeStruct(float vdrift, float xd, float z); + + private: + void SampleTimeStruct(float vdrift); + + std::array<float, ktimebin * kZbin> mTimeStruct1; //! cached Time Structure of Drift Cells (for last vdrift value) + std::array<float, ktimebin * kZbin> mTimeStruct2; //! cached Time Structure of Drift Cells (for last vdrift value) + float mVDlo; //! Lower drift velocity, for interpolation + float mVDhi; //! Higher drift velocity, for interpolation + float mInvBinWidth; //! caching 1/(mVDhi - mVDlo) + float mTimeLastVdrift = -1.f; //! The structures are valid for this mLastVdrift (caching) + + // for the diffusion part + float mDiffLastVdrift = -1.f; + float mDiffusionL = -1.f; + float mDiffusionT = -1.f; +}; + +} // namespace trd +} // namespace o2 + +#endif //O2_TRD_DIFFANDTIMESTRUCTESTIMATOR_H diff --git a/Detectors/TRD/base/include/TRDBase/Digit.h b/Detectors/TRD/base/include/TRDBase/Digit.h index 31436dcd4d283..d76ed2445139b 100644 --- a/Detectors/TRD/base/include/TRDBase/Digit.h +++ b/Detectors/TRD/base/include/TRDBase/Digit.h @@ -15,10 +15,12 @@ #include <vector> #include <array> #include <unordered_map> +#include <numeric> #include "Rtypes.h" // for ClassDef #include "CommonDataFormat/TimeStamp.h" -#include "TRDBase/TRDCommonParam.h" +#include "TRDBase/CommonParam.h" +#include "DataFormatsTRD/Constants.h" namespace o2 { @@ -26,7 +28,7 @@ namespace trd { using ADC_t = std::uint16_t; -using ArrayADC = std::array<ADC_t, kTimeBins>; +using ArrayADC = std::array<ADC_t, constants::TIMEBINS>; using TimeStamp = o2::dataformats::TimeStamp<double>; @@ -51,6 +53,7 @@ class Digit : public TimeStamp int getRow() const { return mRow; } int getPad() const { return mPad; } ArrayADC const& getADC() const { return mADC; } + ADC_t getADCsum() const { return std::accumulate(mADC.begin(), mADC.end(), (ADC_t)0); } private: std::uint16_t mDetector{0}; // TRD detector number, 0-539 diff --git a/Detectors/TRD/base/include/TRDBase/FeeParam.h b/Detectors/TRD/base/include/TRDBase/FeeParam.h index f514f0cf6f6c2..5e16d67b1aa7e 100644 --- a/Detectors/TRD/base/include/TRDBase/FeeParam.h +++ b/Detectors/TRD/base/include/TRDBase/FeeParam.h @@ -26,12 +26,14 @@ // // //////////////////////////////////////////////////////////////////////////// +#include "DataFormatsTRD/Constants.h" + #include <array> #include <vector> -class TRDCommonParam; -class TRDPadPlane; -class TRDGeometry; +class CommonParam; +class PadPlane; +class Geometry; namespace o2 { @@ -67,30 +69,12 @@ class FeeParam static short chipmaskToMCMlist(unsigned int cmA, unsigned int cmB, unsigned short linkpair, int* mcmList, int listSize); static short getRobAB(unsigned short robsel, unsigned short linkpair); // Returns the chamber side (A=0, B=0) of a ROB - // geometry - static float getSamplingFrequency() { return (float)mgkLHCfrequency / 4000000.0; } //TODO put the 40MHz into a static variable somewhere. - static int getNmcmRob() { return mgkNmcmRob; } - static int getNmcmRobInRow() { return mgkNmcmRobInRow; } - static int getNmcmRobInCol() { return mgkNmcmRobInCol; } - static int getNrobC0() { return mgkNrobC0; } - static int getNrobC1() { return mgkNrobC1; } - static int getNadcMcm() { return mgkNadcMcm; } - static int getNcol() { return mgkNcol; } - static int getNcolMcm() { return mgkNcolMcm; } - static int getNrowC0() { return mgkNrowC0; } - static int getNrowC1() { return mgkNrowC1; } - // Basic Geometrical numbers - static const int mgkLHCfrequency = 40079000; // [Hz] LHC clock - static const int mgkNmcmRob = 16; // Number of MCMs per ROB - static const int mgkNmcmRobInRow = 4; // Number of MCMs per ROB in row dir. - static const int mgkNmcmRobInCol = 4; // Number of MCMs per ROB in col dir. - static const int mgkNrobC0 = 6; // Number of ROBs per C0 chamber - static const int mgkNrobC1 = 8; // Number of ROBs per C1 chamber - static const int mgkNadcMcm = 21; // Number of ADC channels per MCM - static const int mgkNcol = 144; // Number of pads per padplane row - static const int mgkNcolMcm = 18; // Number of pads per MCM - static const int mgkNrowC0 = 12; // Number of Rows per C0 chamber - static const int mgkNrowC1 = 16; // Number of Rows per C1 chamber + // wiring + virtual int getORI(int detector, int readoutboard) const; + virtual int getORIinSM(int detector, int readoutboard) const; + // virtual void createORILookUpTable(); + virtual int getORIfromHCID(int hcid) const; + virtual int getHCIDfromORI(int ori, int readoutboard) const; // TODO we need more info than just ori, for now readoutboard is there ... might change // tracklet simulation bool getTracklet() const { return mgTracklet; } @@ -156,7 +140,7 @@ class FeeParam static FeeParam* mgInstance; // Singleton instance static bool mgTerminated; // Defines if this class has already been terminated - TRDCommonParam* mCP = nullptr; // TRD common parameters class + CommonParam* mCP = nullptr; // TRD common parameters class static std::vector<short> mgLUTPadNumbering; // Lookup table mapping Pad to MCM static bool mgLUTPadNumberingFilled; // Lookup table mapping Pad to MCM @@ -174,22 +158,24 @@ class FeeParam static const int mgkMaxRAWversion = 3; // Maximum raw version number supported // geometry constants - static std::array<float, 30> mgZrow; // z-position of pad row edge 6x5 - static std::array<float, 6> mgX; // x-position for all layers - static std::array<float, 6> mgInvX; // inverse x-position for all layers (to remove divisions) - static std::array<float, 6> mgTiltingAngle; // tilting angle for every layer - static std::array<float, 6> mgTiltingAngleTan; // tan of tilting angle for every layer (look up table to avoid tan calculations) - static std::array<float, 6> mgWidthPad; // pad width for all layers - static std::array<float, 6> mgInvWidthPad; // inverse pad width for all layers (to remove divisions) + static std::array<float, constants::NCHAMBERPERSEC> mgZrow; // z-position of pad row edge 6x5 + static std::array<float, constants::NLAYER> mgX; // x-position for all layers + static std::array<float, constants::NLAYER> mgInvX; // inverse x-position for all layers (to remove divisions) + static std::array<float, constants::NLAYER> mgTiltingAngle; // tilting angle for every layer + static std::array<float, constants::NLAYER> mgTiltingAngleTan; // tan of tilting angle for every layer (look up table to avoid tan calculations) + static std::array<float, constants::NLAYER> mgWidthPad; // pad width for all layers + static std::array<float, constants::NLAYER> mgInvWidthPad; // inverse pad width for all layers (to remove divisions) static float mgLengthInnerPadC0; // inner pad length C0 chamber static float mgLengthOuterPadC0; // outer pad length C0 chamber - static std::array<float, 6> mgLengthInnerPadC1; // inner pad length C1 chambers - static std::array<float, 6> mgLengthOuterPadC1; // outer pad length C1 chambers + static std::array<float, constants::NLAYER> mgLengthInnerPadC1; // inner pad length C1 chambers + static std::array<float, constants::NLAYER> mgLengthOuterPadC1; // outer pad length C1 chambers static float mgScalePad; // scaling factor for pad width static float mgDriftLength; // length of the parse gaintbl Krypton_2009-01 drift region static float mgBinDy; // bin in dy (140 um) static int mgDyMax; // max dy for a tracklet (hard limit) static int mgDyMin; // min dy for a tracklet (hard limit) + //std::array<int,30> mgAsideLUT; // A side LUT to map ORI to stack/layer/side + //std::array<int,30> mgCsideLUT; // C side LUT to map ORI to stack/layer/side // settings float mMagField; // magnetic field diff --git a/Detectors/TRD/base/include/TRDBase/Geometry.h b/Detectors/TRD/base/include/TRDBase/Geometry.h new file mode 100644 index 0000000000000..eb9c6573424cf --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/Geometry.h @@ -0,0 +1,69 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_GEOMETRY_H +#define O2_TRD_GEOMETRY_H + +#include "TRDBase/GeometryBase.h" +#include "DetectorsCommonDataFormats/DetMatrixCache.h" +#include "DetectorsCommonDataFormats/DetID.h" + +#include <string> +#include <vector> +#include <memory> + +namespace o2 +{ +namespace trd +{ + +class Geometry : public GeometryBase, public o2::detectors::DetMatrixCacheIndirect +{ + public: + ~Geometry() override = default; + + static Geometry* instance() + { + static Geometry mGeom; + return &mGeom; + } + + void createGeometry(std::vector<int> const& idtmed); + void addAlignableVolumes() const; + bool createClusterMatrixArray(); + void createPadPlaneArray(); + + bool rotateBack(int det, const float* const loc, float* glb) const; + bool chamberInGeometry(int det) const; + std::vector<std::string> const& getSensitiveTRDVolumes() const { return mSensitiveVolumeNames; } + + protected: + void fillMatrixCache(int mask) override; + + private: + void createVolumes(std::vector<int> const& idtmed); + void assembleChamber(int ilayer, int istack); + void createFrame(std::vector<int> const& idtmed); + void createServices(std::vector<int> const& idtmed); + void createPadPlane(int ilayer, int istack); + + std::vector<std::string> mSensitiveVolumeNames; //!< vector keeping track of sensitive TRD volumes + static const o2::detectors::DetID sDetID; + + // helper function to create volumes and registering them automatically + void createVolume(const char* name, const char* shape, int nmed, float* upar, int np); + + Geometry(); + + ClassDefOverride(Geometry, 2); // TRD geometry class +}; +} // end namespace trd +} // end namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/GeometryBase.h b/Detectors/TRD/base/include/TRDBase/GeometryBase.h new file mode 100644 index 0000000000000..7bb5fec24b65d --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/GeometryBase.h @@ -0,0 +1,221 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_GEOMETRYBASE_H +#define O2_TRD_GEOMETRYBASE_H + +#include "GPUCommonDef.h" +#include "TRDBase/CommonParam.h" +#include "DataFormatsTRD/Constants.h" +#include "TRDBase/PadPlane.h" + +namespace o2 +{ +namespace trd +{ +class GeometryBase +{ + public: + ~GeometryBase() = default; + + GPUd() int isVersion() { return 1; } + GPUd() bool isHole(int la, int st, int se) const { return (((se == 13) || (se == 14) || (se == 15)) && (st == 2)); } + GPUd() bool isOnBoundary(int det, float y, float z, float eps = 0.5) const; + + GPUd() void setSMstatus(int sm, bool status) + { + if (status) { + mSMStatus |= 0x3ffff & (0x1 << sm); + } else { + mSMStatus &= ~(0x3ffff & (0x1 << sm)); + } + } + GPUd() bool getSMstatus(int sm) const { return (mSMStatus & (0x1 << sm)) != 0; } + GPUd() static int getDetectorSec(int det) { return (det % (constants::NLAYER * constants::NSTACK)); } + GPUd() static int getDetectorSec(int layer, int stack) { return (layer + stack * constants::NLAYER); } + GPUd() static int getDetector(int layer, int stack, int sector) { return (layer + stack * constants::NLAYER + sector * constants::NLAYER * constants::NSTACK); } + GPUd() static int getLayer(int det) { return (det % constants::NLAYER); } + GPUd() static int getStack(int det) { return ((det % (constants::NLAYER * constants::NSTACK)) / constants::NLAYER); } + GPUd() int getStack(float z, int layer) const; + + GPUd() const PadPlane* getPadPlane(int layer, int stack) const { return &mPadPlanes[getDetectorSec(layer, stack)]; } + GPUd() const PadPlane* getPadPlane(int det) const { return &mPadPlanes[getDetectorSec(det)]; } + + GPUd() int getRowMax(int layer, int stack, int /*sector*/) const { return getPadPlane(layer, stack)->getNrows(); } + GPUd() int getColMax(int layer) const { return getPadPlane(layer, 0)->getNcols(); } + GPUd() float getRow0(int layer, int stack, int /*sector*/) const { return getPadPlane(layer, stack)->getRow0(); } + GPUd() float getCol0(int layer) const { return getPadPlane(layer, 0)->getCol0(); } + + GPUd() float getRowPos(int layer, int stack, int row) { return mPadPlanes[getDetectorSec(layer, stack)].getRowPos(row); } + GPUd() float getRowSize(int layer, int stack, int row) { return mPadPlanes[getDetectorSec(layer, stack)].getRowSize(row); } + GPUd() float getRow0(int layer, int stack) { return mPadPlanes[getDetectorSec(layer, stack)].getRow0(); } + GPUd() float getRowEnd(int layer, int stack) { return mPadPlanes[getDetectorSec(layer, stack)].getRowEnd(); } + + static constexpr GPUd() int getSector(int det) { return (det / (constants::NLAYER * constants::NSTACK)); } + static constexpr GPUd() float getTime0(int layer) { return TIME0[layer]; } + static constexpr GPUd() float getXtrdBeg() { return XTRDBEG; } + static constexpr GPUd() float getXtrdEnd() { return XTRDEND; } + static constexpr GPUd() float getChamberWidth(int layer) { return CWIDTH[layer]; } + static constexpr GPUd() float getChamberLength(int layer, int stack) { return CLENGTH[layer][stack]; } + static constexpr GPUd() float getAlpha() { return 2.0 * 3.14159265358979324 / constants::NSECTOR; } + static constexpr GPUd() float cheight() { return CH; } + static constexpr GPUd() float cheightSV() { return CHSV; } + static constexpr GPUd() float cspace() { return VSPACE; } + static constexpr GPUd() float craHght() { return CRAH; } + static constexpr GPUd() float cdrHght() { return CDRH; } + static constexpr GPUd() float camHght() { return CAMH; } + static constexpr GPUd() float croHght() { return CROH; } + static constexpr GPUd() float csvHght() { return CSVH; } + static constexpr GPUd() float croWid() { return CROW; } + static constexpr GPUd() float anodePos() { return ANODEPOS; } + static constexpr GPUd() float myThick() { return RMYTHICK; } + static constexpr GPUd() float drThick() { return DRTHICK; } + static constexpr GPUd() float amThick() { return AMTHICK; } + static constexpr GPUd() float drZpos() { return DRZPOS; } + static constexpr GPUd() float rpadW() { return RPADW; } + static constexpr GPUd() float cpadW() { return CPADW; } + static constexpr GPUd() float cwidcha() { return (SWIDTH2 - SWIDTH1) / SHEIGHT * (CH + VSPACE); } + static constexpr GPUd() int MCMmax() { return MCMMAX; } + static constexpr GPUd() int MCMrow() { return MCMROW; } + static constexpr GPUd() int ROBmaxC0() { return ROBMAXC0; } + static constexpr GPUd() int ROBmaxC1() { return ROBMAXC1; } + static constexpr GPUd() int ADCmax() { return ADCMAX; } + static constexpr GPUd() int TBmax() { return TBMAX; } + static constexpr GPUd() int padmax() { return PADMAX; } + static constexpr GPUd() int colmax() { return COLMAX; } + static constexpr GPUd() int rowmaxC0() { return ROWMAXC0; } + static constexpr GPUd() int rowmaxC1() { return ROWMAXC1; } + + protected: + GeometryBase() = default; + + static constexpr float TLENGTH = 751.0; ///< Total length of the TRD mother volume + + // Parameter of the super module mother volumes + static constexpr float SHEIGHT = 77.9; ///< Height of the supermodule + static constexpr float SWIDTH1 = 94.881; ///< Lower width of the supermodule + static constexpr float SWIDTH2 = 122.353; ///< Upper width of the supermodule + static constexpr float SLENGTH = 702.0; ///< Length of the supermodule + + // Length of the additional space in front of the supermodule used for services + static constexpr float FLENGTH = (TLENGTH - SLENGTH) / 2.0; + + static constexpr float SMPLTT = 0.2; ///< Thickness of the super module side plates + + static constexpr float VSPACE = 1.784; ///< Vertical spacing of the chambers + static constexpr float HSPACE = 2.0; ///< Horizontal spacing of the chambers + static constexpr float VROCSM = 1.2; ///< Radial distance of the first ROC to the outer plates of the SM + + static constexpr float CRAH = 4.8; ///< Height of the radiator part of the chambers + static constexpr float CDRH = 3.0; ///< Height of the drift region of the chambers + static constexpr float CAMH = 0.7; ///< Height of the amplification region of the chambers + static constexpr float CROH = 2.316; ///< Height of the readout of the chambers + static constexpr float CROW = 0.9; ///< Additional width of the readout chamber frames + static constexpr float CSVH = VSPACE - 0.742; ///< Height of the services on top of the chambers + static constexpr float CH = CRAH + CDRH + CAMH + CROH; ///< Total height of the chambers (w/o services) + static constexpr float CHSV = CH + CSVH; ///< Total height of the chambers (with services) + + // Distance of anode wire plane relative to middle of alignable volume + static constexpr float ANODEPOS = CRAH + CDRH + CAMH / 2.0 - CHSV / 2.0; + + static constexpr float CALT = 0.4; ///< Thicknesses of different parts of the chamber frame Lower aluminum frame + static constexpr float CCLST = 0.21; ///< Thickness of the lower Wacosit frame sides + static constexpr float CCLFT = 1.0; ///< Thickness of the lower Wacosit frame front + static constexpr float CGLT = 0.25; ///< Thichness of the glue around the radiator + static constexpr float CCUTA = 1.0; ///< Upper Wacosit frame around amplification region + static constexpr float CCUTB = 0.8; ///< Thickness of the upper Wacosit frame around amp. region + static constexpr float CAUT = 1.5; ///< Al frame of back panel + static constexpr float CALW = 2.5; ///< Width of additional aluminum ledge on lower frame + static constexpr float CALH = 0.4; ///< Height of additional aluminum ledge on lower frame + static constexpr float CALWMOD = 0.4; ///< Width of additional aluminum ledge on lower frame + static constexpr float CALHMOD = 2.5; ///< Height of additional aluminum ledge on lower frame + static constexpr float CWSW = 1.2; ///< Width of additional wacosit ledge on lower frame + static constexpr float CWSH = 0.3; ///< Height of additional wacosit ledge on lower frame + + static constexpr float CPADW = 0.0; ///>Difference of outer chamber width and pad plane width + static constexpr float RPADW = 1.0; ///<Difference of outer chamber width and pad plane width + + // + // Thickness of the the material layers + // + static constexpr float DRTHICK = CDRH; ///< Thickness of the drift region + static constexpr float AMTHICK = CAMH; ///< Thickness of the amplification region + static constexpr float XETHICK = DRTHICK + AMTHICK; ///< Thickness of the gas volume + static constexpr float WRTHICK = 0.00011; ///< Thickness of the wire planes + + static constexpr float RMYTHICK = 0.0015; ///< Thickness of the mylar layers in the radiator + static constexpr float RCBTHICK = 0.0055; ///< Thickness of the carbon layers in the radiator + static constexpr float RGLTHICK = 0.0065; ///< Thickness of the glue layers in the radiator + static constexpr float RRHTHICK = 0.8; ///< Thickness of the rohacell layers in the radiator + static constexpr float RFBTHICK = CRAH - 2.0 * (RMYTHICK + RCBTHICK + RRHTHICK); ///< Thickness of the fiber layers in the radiator + + static constexpr float PPDTHICK = 0.0025; ///< Thickness of copper of the pad plane + static constexpr float PPPTHICK = 0.0356; ///< Thickness of PCB board of the pad plane + static constexpr float PGLTHICK = 0.1428; ///< Thickness of the glue layer + static constexpr float PCBTHICK = 0.019; ///< Thickness of the carbon layers + static constexpr float PPCTHICK = 0.0486; ///< Thickness of the PCB readout boards + static constexpr float PRBTHICK = 0.0057; ///< Thickness of the PCB copper layers + static constexpr float PELTHICK = 0.0029; ///< Thickness of all other electronics components (caps, etc.) + static constexpr float PHCTHICK = CROH - PPDTHICK - PPPTHICK - PGLTHICK - PCBTHICK * 2.0 - PPCTHICK - PRBTHICK - PELTHICK; ///< Thickness of the honeycomb support structure + + // + // Position of the material layers + // + static constexpr float DRZPOS = 2.4; ///< Position of the drift region + static constexpr float AMZPOS = 0.0; ///< Position of the amplification region + static constexpr float WRZPOSA = 0.0; ///< Position of the wire planes + static constexpr float WRZPOSB = -AMTHICK / 2.0 + 0.001; ///< Position of the wire planes + static constexpr float CALZPOS = 0.3; ///< Position of the additional aluminum ledges + + static constexpr int MCMMAX = 16; ///< Maximum number of MCMs per ROB + static constexpr int MCMROW = 4; ///< Maximum number of MCMs per ROB Row + static constexpr int ROBMAXC0 = 6; ///< Maximum number of ROBs per C0 chamber + static constexpr int ROBMAXC1 = 8; ///< Maximum number of ROBs per C1 chamber + static constexpr int ADCMAX = 21; ///< Maximum number of ADC channels per MCM + static constexpr int TBMAX = 60; ///< Maximum number of Time bins + static constexpr int PADMAX = 18; ///< Maximum number of pads per MCM + static constexpr int COLMAX = 144; ///< Maximum number of pads per padplane row + static constexpr int ROWMAXC0 = 12; ///< Maximum number of Rows per C0 chamber + static constexpr int ROWMAXC1 = 16; ///< Maximum number of Rows per C1 chamber + + static constexpr float TIME0BASE = 300.65; ///< Base value for calculation of Time-position of pad 0 + // Time-position of pad 0 + static constexpr float TIME0[6] = {TIME0BASE + 0 * (CH + VSPACE), + TIME0BASE + 1 * (CH + VSPACE), + TIME0BASE + 2 * (CH + VSPACE), + TIME0BASE + 3 * (CH + VSPACE), + TIME0BASE + 4 * (CH + VSPACE), + TIME0BASE + 5 * (CH + VSPACE)}; + + static constexpr float XTRDBEG = 288.43; ///< X-coordinate in tracking system of begin of TRD mother volume + static constexpr float XTRDEND = 366.33; ///< X-coordinate in tracking system of end of TRD mother volume + + // The outer width of the chambers + static constexpr float CWIDTH[constants::NLAYER] = {90.4, 94.8, 99.3, 103.7, 108.1, 112.6}; + + // The outer lengths of the chambers + // Includes the spacings between the chambers! + static constexpr float CLENGTH[constants::NLAYER][constants::NSTACK] = { + {124.0, 124.0, 110.0, 124.0, 124.0}, + {124.0, 124.0, 110.0, 124.0, 124.0}, + {131.0, 131.0, 110.0, 131.0, 131.0}, + {138.0, 138.0, 110.0, 138.0, 138.0}, + {145.0, 145.0, 110.0, 145.0, 145.0}, + {147.0, 147.0, 110.0, 147.0, 147.0}}; + + PadPlane mPadPlanes[constants::NLAYER * constants::NSTACK]; + + int mSMStatus = 0x3ffff; + + ClassDefNV(GeometryBase, 1); +}; +} // end namespace trd +} // end namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/GeometryFlat.h b/Detectors/TRD/base/include/TRDBase/GeometryFlat.h new file mode 100644 index 0000000000000..394c4be5595af --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/GeometryFlat.h @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_GEOMETRYFLAT_H +#define O2_TRD_GEOMETRYFLAT_H + +#ifndef GPUCA_GPUCODE_DEVICE +#include <cstring> +#endif +#include "FlatObject.h" +#include "GPUCommonDef.h" +#include "GPUCommonTransform3D.h" +#include "TRDBase/GeometryBase.h" +#include "TRDBase/PadPlane.h" +#include "DataFormatsTRD/Constants.h" + +namespace o2 +{ +namespace trd +{ + +class Geometry; + +//Reduced flat version of TRD geometry class. +//Contains all entries required for tracking on GPUs. +class GeometryFlat : public o2::gpu::FlatObject, public GeometryBase +{ + public: +#ifndef GPUCA_GPUCODE_DEVICE + GeometryFlat() = default; + GeometryFlat(const GeometryFlat& v) : FlatObject(), GeometryBase() + { + memcpy((void*)this, (void*)&v, sizeof(*this)); + } + GeometryFlat(const Geometry& geo); + ~GeometryFlat() = default; +#endif + GPUd() const o2::gpu::Transform3D* getMatrixT2L(int det) const + { + if (mMatrixIndirection[det] == -1) { + return nullptr; + } + return &mMatrixCache[mMatrixIndirection[det]]; + ; + } + + GPUd() bool chamberInGeometry(int det) const + { + return (mMatrixIndirection[det] >= 0); + } + + private: + o2::gpu::Transform3D mMatrixCache[constants::NCHAMBER]; + short mMatrixIndirection[constants::MAXCHAMBER]; +}; + +} // namespace trd +} // namespace o2 + +#endif diff --git a/Detectors/TRD/base/include/TRDBase/PadCalibrations.h b/Detectors/TRD/base/include/TRDBase/PadCalibrations.h index d6391b3c36431..f0523a6e9d53f 100644 --- a/Detectors/TRD/base/include/TRDBase/PadCalibrations.h +++ b/Detectors/TRD/base/include/TRDBase/PadCalibrations.h @@ -21,9 +21,10 @@ #include <array> #include "TRDBase/PadParameters.h" -#include "TRDBase/TRDSimParam.h" +#include "TRDBase/SimParam.h" +#include "DataFormatsTRD/Constants.h" -class TRDGeometry; +class Geometry; namespace o2 { @@ -46,16 +47,16 @@ class PadCalibrations void reset(int roc, int col, int row, std::vector<T>& data); void init(); protected: - std::array<PadParameters<T>, TRDSimParam::kNdet> mreadOutChamber; + std::array<PadParameters<T>, constants::MAXCHAMBER> mreadOutChamber; }; template <class T> PadCalibrations<T>::PadCalibrations() { // - // TRDCalPadStatus constructor + // CalPadStatus constructor // - //TRDGeometry fgeom; + //Geometry fgeom; int chamberindex = 0; for (auto& roc : mreadOutChamber) { // Range-for! roc.init(chamberindex++); @@ -66,7 +67,7 @@ template <class T> void PadCalibrations<T>::init() { // - // TRDCalPadStatus constructor + // CalPadStatus constructor // int chamberindex = 0; for (auto& roc : mreadOutChamber) { // Range-for! diff --git a/Detectors/TRD/base/include/TRDBase/PadParameters.h b/Detectors/TRD/base/include/TRDBase/PadParameters.h index 4c99bf020d6e6..a147d21f19c99 100644 --- a/Detectors/TRD/base/include/TRDBase/PadParameters.h +++ b/Detectors/TRD/base/include/TRDBase/PadParameters.h @@ -22,9 +22,10 @@ // #include <vector> -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDSimParam.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/SimParam.h" #include "TRDBase/FeeParam.h" +#include "DataFormatsTRD/Constants.h" namespace o2 { @@ -52,12 +53,12 @@ class PadParameters int reset(int chamberindex, int col, int row, std::vector<T>& data); protected: - int mPlane{0}; // Plane number - int mChamber{0}; // Chamber number - int mNrows{0}; // Number of rows - int mNcols{FeeParam::mgkNcol}; // Number of columns - int mNchannels; // Number of channels = rows*columns - std::vector<T> mData; // Size is mNchannels + int mPlane{0}; // Plane number + int mChamber{0}; // Chamber number + int mNrows{0}; // Number of rows + int mNcols{constants::NCOLUMN}; // Number of columns + int mNchannels; // Number of channels = rows*columns + std::vector<T> mData; // Size is mNchannels }; template <class T> @@ -69,34 +70,38 @@ PadParameters<T>::PadParameters(int chamberindex) template <class T> int PadParameters<T>::init(int chamberindex) { - mPlane = TRDGeometry::getLayer(chamberindex); - mChamber = TRDGeometry::getStack(chamberindex); - if (mChamber == 2) - mNrows = FeeParam::mgkNrowC0; - else - mNrows = FeeParam::mgkNrowC1; + mPlane = Geometry::getLayer(chamberindex); + mChamber = Geometry::getStack(chamberindex); + if (mChamber == 2) { + mNrows = constants::NROWC0; + } else { + mNrows = constants::NROWC1; + } // the FeeParam variables need to be unprotected, and dont want to change FeeParam in this PR. mNchannels = mNrows * mNcols; mData.resize(mNchannels); - if (mData.size() != mNchannels || mData.size() == 0) + if (mData.size() != mNchannels || mData.size() == 0) { return -1; + } return 0; } template <class T> int PadParameters<T>::reset(int chamberindex, int cols, int rows, std::vector<T>& data) { - mPlane = TRDGeometry::getLayer(chamberindex); - mChamber = TRDGeometry::getStack(chamberindex); + mPlane = Geometry::getLayer(chamberindex); + mChamber = Geometry::getStack(chamberindex); mNrows = rows; mNcols = cols; // the FeeParam variables need to be unprotected, and dont want to change FeeParam in this PR. mNchannels = mNrows * mNcols; - if (mData.size() != mNchannels) + if (mData.size() != mNchannels) { return -2; + } mData.resize(mNchannels); - if (mData.size() != mNchannels || mData.size() == 0) + if (mData.size() != mNchannels || mData.size() == 0) { return -1; + } // now reset the data of the pads. int counter = 0; diff --git a/Detectors/TRD/base/include/TRDBase/PadPlane.h b/Detectors/TRD/base/include/TRDBase/PadPlane.h new file mode 100644 index 0000000000000..7e72d508c0e03 --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/PadPlane.h @@ -0,0 +1,182 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_PADPLANE_H +#define O2_TRD_PADPLANE_H + +//Forwards to standard header with protection for GPU compilation +#include "GPUCommonRtypes.h" // for ClassDef + +#include "GPUCommonDef.h" + +//////////////////////////////////////////////////////////////////////////// +// // +// TRD pad plane class // +// // +// Contains the information on ideal pad positions, pad dimensions, // +// tilting angle, etc. // +// It also provides methods to identify the current pad number from // +// local tracking coordinates. // +// // +//////////////////////////////////////////////////////////////////////////// +namespace o2 +{ +namespace trd +{ +class PadPlane +{ + public: + PadPlane() = default; + PadPlane(int layer, int stack) : mLayer(layer), mStack(stack){}; + PadPlane(const PadPlane& p) = delete; + PadPlane& operator=(const PadPlane& p) = delete; + ~PadPlane() = default; + + void setLayer(int l) { mLayer = l; }; + void setStack(int s) { mStack = s; }; + void setRowSpacing(double s) { mRowSpacing = s; }; + void setColSpacing(double s) { mColSpacing = s; }; + void setLengthRim(double l) { mLengthRim = l; }; + void setWidthRim(double w) { mWidthRim = w; }; + void setNcols(int n); + void setNrows(int n); + void setPadCol(int ic, double c) + { + if (ic < mNcols) { + mPadCol[ic] = c; + } + }; + void setPadRow(int ir, double r) + { + if (ir < mNrows) { + mPadRow[ir] = r; + } + }; + void setLength(double l) { mLength = l; }; + void setWidth(double w) { mWidth = w; }; + void setLengthOPad(double l) { mLengthOPad = l; }; + void setWidthOPad(double w) { mWidthOPad = w; }; + void setLengthIPad(double l) { mLengthIPad = l; }; + void setWidthIPad(double w) { mWidthIPad = w; }; + void setPadRowSMOffset(double o) { mPadRowSMOffset = o; }; + void setAnodeWireOffset(float o) { mAnodeWireOffset = o; }; + void setTiltingAngle(double t); + + GPUd() int getPadRowNumber(double z) const; + GPUd() int getPadRowNumberROC(double z) const; + GPUd() int getPadColNumber(double rphi) const; + + GPUd() double getTiltOffset(double rowOffset) const { return mTiltingTan * (rowOffset - 0.5 * mLengthIPad); }; + GPUd() double getPadRowOffset(int row, double z) const + { + if ((row < 0) || (row >= mNrows)) { + return -1.0; + } else { + return mPadRow[row] + mPadRowSMOffset - z; + } + }; + GPUd() double getPadRowOffsetROC(int row, double z) const + { + if ((row < 0) || (row >= mNrows)) { + return -1.0; + } else { + return mPadRow[row] - z; + } + }; + + GPUd() double getPadColOffset(int col, double rphi) const + { + if ((col < 0) || (col >= mNcols)) { + return -1.0; + } else { + return rphi - mPadCol[col]; + } + }; + + GPUd() double getTiltingAngle() const { return mTiltingAngle; }; + GPUd() int getNrows() const { return mNrows; }; + GPUd() int getNcols() const { return mNcols; }; + GPUd() double getRow0() const { return mPadRow[0] + mPadRowSMOffset; }; + GPUd() double getRow0ROC() const { return mPadRow[0]; }; + GPUd() double getCol0() const { return mPadCol[0]; }; + GPUd() double getRowEnd() const { return mPadRow[mNrows - 1] - mLengthOPad + mPadRowSMOffset; }; + GPUd() double getRowEndROC() const { return mPadRow[mNrows - 1] - mLengthOPad; }; + GPUd() double getColEnd() const { return mPadCol[mNcols - 1] + mWidthOPad; }; + GPUd() double getRowPos(int row) const { return mPadRow[row] + mPadRowSMOffset; }; + GPUd() double getRowPosROC(int row) const { return mPadRow[row]; }; + GPUd() double getColPos(int col) const { return mPadCol[col]; }; + GPUd() double getRowSize(int row) const + { + if ((row == 0) || (row == mNrows - 1)) { + return mLengthOPad; + } else { + return mLengthIPad; + } + }; + GPUd() double getColSize(int col) const + { + if ((col == 0) || (col == mNcols - 1)) { + return mWidthOPad; + } else { + return mWidthIPad; + } + }; + + GPUd() double getLengthRim() const { return mLengthRim; }; + GPUd() double getWidthRim() const { return mWidthRim; }; + GPUd() double getRowSpacing() const { return mRowSpacing; }; + GPUd() double getColSpacing() const { return mColSpacing; }; + GPUd() double getLengthOPad() const { return mLengthOPad; }; + GPUd() double getLengthIPad() const { return mLengthIPad; }; + GPUd() double getWidthOPad() const { return mWidthOPad; }; + GPUd() double getWidthIPad() const { return mWidthIPad; }; + GPUd() double getAnodeWireOffset() const { return mAnodeWireOffset; }; + + protected: + static constexpr int MAXCOLS = 144; + static constexpr int MAXROWS = 16; + + int mLayer; // Layer number + int mStack; // Stack number + + double mLength; // Length of pad plane in z-direction (row) + double mWidth; // Width of pad plane in rphi-direction (col) + + double mLengthRim; // Length of the rim in z-direction (row) + double mWidthRim; // Width of the rim in rphi-direction (col) + + double mLengthOPad; // Length of an outer pad in z-direction (row) + double mWidthOPad; // Width of an outer pad in rphi-direction (col) + + double mLengthIPad; // Length of an inner pad in z-direction (row) + double mWidthIPad; // Width of an inner pad in rphi-direction (col) + + double mRowSpacing; // Spacing between the pad rows + double mColSpacing; // Spacing between the pad columns + + int mNrows; // Number of rows + int mNcols; // Number of columns + + double mTiltingAngle; // Pad tilting angle + double mTiltingTan; // Tangens of pad tilting angle + + double mPadRow[MAXROWS]; // Pad border positions in row direction + double mPadCol[MAXCOLS]; // Pad border positions in column direction + + double mPadRowSMOffset; // To be added to translate local ROC system to local SM system + + double mAnodeWireOffset; // Distance of first anode wire from pad edge + + private: + ClassDefNV(PadPlane, 1); // TRD ROC pad plane +}; +} // namespace trd +} // namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/PadResponse.h b/Detectors/TRD/base/include/TRDBase/PadResponse.h index 30dac688942ab..b655ba847b119 100644 --- a/Detectors/TRD/base/include/TRDBase/PadResponse.h +++ b/Detectors/TRD/base/include/TRDBase/PadResponse.h @@ -12,7 +12,7 @@ #define ALICEO2_TRD_PADRESPONSE_H_ #include <array> -#include "TRDBase/TRDCommonParam.h" // For kNLayer +#include "DataFormatsTRD/Constants.h" namespace o2 { @@ -32,7 +32,7 @@ class PadResponse static constexpr float mPRFhi{1.5}; // Higher boundary of the PRF float mPRFwid; // Bin width of the sampled PRF int mPRFpad; // Distance to next pad in PRF - std::array<float, kNlayer * mPRFbin> mPRFsmp{}; // Sampled pad response + std::array<float, constants::NLAYER * mPRFbin> mPRFsmp{}; // Sampled pad response }; } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/base/include/TRDBase/SimParam.h b/Detectors/TRD/base/include/TRDBase/SimParam.h new file mode 100644 index 0000000000000..e6de326bc8650 --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/SimParam.h @@ -0,0 +1,169 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_SIMPARAM_H +#define O2_TRD_SIMPARAM_H + +//Forwards to standard header with protection for GPU compilation +#include "GPUCommonRtypes.h" // for ClassDef + +namespace o2 +{ +namespace trd +{ +//////////////////////////////////////////////////////////////////////////// +// // +// Class containing constant simulation parameters // +// // +//////////////////////////////////////////////////////////////////////////// +class SimParam +{ + public: + enum { + kNPadsInPadResponse = 3 // Number of pads included in the pad response + }; + + static SimParam* Instance(); + static void Terminate(); + + void SetGasGain(float gasgain) { mGasGain = gasgain; } + void SetNoise(float noise) { mNoise = noise; } + void SetChipGain(float chipgain) { mChipGain = chipgain; } + void SetADCoutRange(float range) { mADCoutRange = range; } + void SetADCinRange(float range) { mADCinRange = range; } + void SetADCbaseline(int basel) { mADCbaseline = basel; } + void SetDiffusion(int diffOn = 1) { mDiffusionOn = diffOn; } + void SetElAttach(int elOn = 1) { mElAttachOn = elOn; } + void SetElAttachProp(float prop) { mElAttachProp = prop; } + void SetTimeResponse(int trfOn = 1) + { + mTRFOn = trfOn; + ReInit(); + } + void SetCrossTalk(int ctOn = 1) + { + mCTOn = ctOn; + ReInit(); + } + void SetPadCoupling(float v) { mPadCoupling = v; } + void SetTimeCoupling(float v) { mTimeCoupling = v; } + void SetTimeStruct(bool tsOn = 1) { mTimeStructOn = tsOn; } + void SetPadResponse(int prfOn = 1) { mPRFOn = prfOn; } + void SetNTimeBins(int ntb) { mNTimeBins = ntb; } + void SetNTBoverwriteOCDB(bool over = true) { mNTBoverwriteOCDB = over; } + float GetGasGain() const { return mGasGain; } + float GetNoise() const { return mNoise; } + float GetChipGain() const { return mChipGain; } + float GetADCoutRange() const { return mADCoutRange; } + float GetADCinRange() const { return mADCinRange; } + int GetADCbaseline() const { return mADCbaseline; } + float GetTRFlo() const { return mTRFlo; } + float GetTRFhi() const { return mTRFhi; } + float GetPadCoupling() const { return mPadCoupling; } + float GetTimeCoupling() const { return mTimeCoupling; } + int GetNTimeBins() const { return mNTimeBins; } + bool GetNTBoverwriteOCDB() const { return mNTBoverwriteOCDB; } + bool DiffusionOn() const { return mDiffusionOn; } + bool ElAttachOn() const { return mElAttachOn; } + float GetElAttachProp() const { return mElAttachProp; } + bool TRFOn() const { return mTRFOn; } + bool CTOn() const { return mCTOn; } + bool TimeStructOn() const { return mTimeStructOn; } + bool PRFOn() const { return mPRFOn; } + const int getNumberOfPadsInPadResponse() const { return kNPadsInPadResponse; } + inline double TimeResponse(double) const; + inline double CrossTalk(double) const; + void ReInit(); + + protected: + static SimParam* fgInstance; // Instance of this class (singleton implementation) + static bool fgTerminated; // Defines if this class has already been terminated and + // therefore does not return instances in GetInstance anymore + + // Digitization parameter + float mGasGain; // Gas gain + float mNoise; // Electronics noise + float mChipGain; // Electronics gain + + float mADCoutRange; // ADC output range (number of channels) + float mADCinRange; // ADC input range (input charge) + int mADCbaseline; // ADC intrinsic baseline in ADC channel + + int mDiffusionOn; // Switch for the diffusion + + int mElAttachOn; // Switch for the electron attachment + float mElAttachProp; // Propability for electron attachment (for 1m) + + int mTRFOn; // Switch for the time response + float* mTRFsmp; //! Integrated time response + int mTRFbin; // Number of bins for the TRF + float mTRFlo; // Lower boundary of the TRF + float mTRFhi; // Higher boundary of the TRF + float mInvTRFwid; // Inverse of the bin width of the integrated TRF + + int mCTOn; // Switch for cross talk + float* mCTsmp; //! Integrated cross talk + + float mPadCoupling; // Pad coupling factor + float mTimeCoupling; // Time coupling factor (image charge of moving ions) + int mTimeStructOn; // Switch for cell time structure + + int mPRFOn; // Switch for the pad response + + int mNTimeBins; // Number of time bins (only used it fNTBoverwriteOCDB = true) + bool mNTBoverwriteOCDB; // Switch to overwrite number of time bins from PCDB + + private: + // This is a singleton, constructor is private! + SimParam(); + ~SimParam(); + + void Init(); + void SampleTRF(); + + ClassDefNV(SimParam, 1); // The TRD simulation parameters +}; + +inline double SimParam::TimeResponse(double time) const +{ + // + // Applies the preamp shaper time response + // (We assume a signal rise time of 0.2us = fTRFlo/2. + // + + double rt = (time - .5 * mTRFlo) * mInvTRFwid; + int iBin = (int)rt; + double dt = rt - iBin; + if ((iBin >= 0) && (iBin + 1 < mTRFbin)) { + return mTRFsmp[iBin] + (mTRFsmp[iBin + 1] - mTRFsmp[iBin]) * dt; + } else { + return 0.0; + } +} + +inline double SimParam::CrossTalk(double time) const +{ + // + // Applies the pad-pad capacitive cross talk + // + + double rt = (time - mTRFlo) * mInvTRFwid; + int iBin = (int)rt; + double dt = rt - iBin; + if ((iBin >= 0) && (iBin + 1 < mTRFbin)) { + return mCTsmp[iBin] + (mCTsmp[iBin + 1] - mCTsmp[iBin]) * dt; + } else { + return 0.0; + } +} + +} // namespace trd +} // namespace o2 +#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDCalPadStatus.h b/Detectors/TRD/base/include/TRDBase/TRDCalPadStatus.h deleted file mode 100644 index a5edb6f325c59..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDCalPadStatus.h +++ /dev/null @@ -1,98 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDCALPADSTATUS_H -#define O2_TRDCALPADSTATUS_H - -#include "TH1F.h" -#include "TH2F.h" -#include <string> - -namespace o2 -{ -namespace trd -{ - -/////////////////////////////////////////////////////////////////////////////// -// // -// TRD calibration class for the single pad status // -// // -/////////////////////////////////////////////////////////////////////////////// - -class TRDCalSingleChamberStatus; - -class TRDCalPadStatus -{ - - public: - enum { kNplan = 6, - kNcham = 5, - kNsect = 18, - kNdet = 540 }; - enum { kMasked = 2, - kPadBridgedLeft = 4, - kPadBridgedRight = 8, - kReadSecond = 16, - kNotConnected = 32 }; - - TRDCalPadStatus(); - TRDCalPadStatus(const Text_t* name, const Text_t* title); - TRDCalPadStatus(const TRDCalPadStatus& c); - ~TRDCalPadStatus(); - TRDCalPadStatus& operator=(const TRDCalPadStatus& c); - - void Copy(TRDCalPadStatus& c) const; - - Bool_t isMasked(Int_t d, Int_t col, Int_t row) const - { - return checkStatus(d, col, row, kMasked); - }; - Bool_t isBridgedLeft(Int_t d, Int_t col, Int_t row) const - { - return checkStatus(d, col, row, kPadBridgedLeft); - }; - Bool_t isBridgedRight(Int_t d, Int_t col, Int_t row) const - { - return checkStatus(d, col, row, kPadBridgedRight); - }; - Bool_t isReadSecond(Int_t d, Int_t col, Int_t row) const - { - return checkStatus(d, col, row, kReadSecond); - }; - Bool_t isNotConnected(Int_t d, Int_t col, Int_t row) const - { - return checkStatus(d, col, row, kNotConnected); - }; - Bool_t checkStatus(Int_t d, Int_t col, Int_t row, Int_t bitMask) const; - - TRDCalSingleChamberStatus* getCalROC(Int_t d) const { return mROC[d]; }; - TRDCalSingleChamberStatus* getCalROC(Int_t p, Int_t c, Int_t s) const; - - // Plot functions - TH1F* makeHisto1D(); - TH2F* makeHisto2DSmPl(Int_t sm, Int_t pl); - void plotHistos2DSm(Int_t sm, const Char_t* name); - - std::string getTitle() { return mTitle; }; - std::string getName() { return mName; }; - void setTitle(const std::string newTitle) { mTitle = newTitle; }; - void setName(const std::string newName) { mName = newName; }; - - protected: - TRDCalSingleChamberStatus* mROC[kNdet]; // Array of ROC objects which contain the values per pad - - private: - std::string mName; - std::string mTitle; - ClassDefNV(TRDCalPadStatus, 1); // TRD calibration class for the single pad status -}; -} //namespace trd -} //namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDCalSingleChamberStatus.h b/Detectors/TRD/base/include/TRDBase/TRDCalSingleChamberStatus.h deleted file mode 100644 index b0b0a4189ae7d..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDCalSingleChamberStatus.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDCALSINGLECHAMBERSTATUS_H -#define O2_TRDCALSINGLECHAMBERSTATUS_H - -#include <Rtypes.h> -namespace o2 -{ -namespace trd -{ - -//////////////////////////////////////////////////////////////////////////// -// // -// TRD calibration base class containing status values for one ROC // -// // -//////////////////////////////////////////////////////////////////////////// - -//_____________________________________________________________________________ -class TRDCalSingleChamberStatus -{ - - public: - enum { kMasked = 2, - kPadBridgedLeft = 4, - kPadBridgedRight = 8, - kReadSecond = 16, - kNotConnected = 32 }; - - TRDCalSingleChamberStatus(); - TRDCalSingleChamberStatus(Int_t p, Int_t c, Int_t cols); - TRDCalSingleChamberStatus(const TRDCalSingleChamberStatus& c); - ~TRDCalSingleChamberStatus(); - TRDCalSingleChamberStatus& operator=(const TRDCalSingleChamberStatus& c); - void Copy(TRDCalSingleChamberStatus& c) const; - - Bool_t isMasked(Int_t col, Int_t row) const { return ((getStatus(col, row) & kMasked) - ? kTRUE - : kFALSE); }; - Bool_t isBridgedLeft(Int_t col, Int_t row) const { return ((getStatus(col, row) & kPadBridgedLeft) ? kTRUE : kFALSE); }; - Bool_t isBridgedRight(Int_t col, Int_t row) const { return ((getStatus(col, row) & kPadBridgedRight) ? kTRUE : kFALSE); }; - Bool_t isNotConnected(Int_t col, Int_t row) const { return ((getStatus(col, row) & kNotConnected) ? kTRUE : kFALSE); }; - Int_t getNrows() const { return mNrows; }; - Int_t getNcols() const { return mNcols; }; - - Int_t getChannel(Int_t col, Int_t row) const { return row + col * mNrows; }; - Int_t getNchannels() const { return mNchannels; }; - Char_t getStatus(Int_t ich) const { return mData[ich]; }; - Char_t getStatus(Int_t col, Int_t row) const { return mData[getChannel(col, row)]; }; - - void setStatus(Int_t ich, Char_t vd) { mData[ich] = vd; }; - void setStatus(Int_t col, Int_t row, Char_t vd) { mData[getChannel(col, row)] = vd; }; - - protected: - Int_t mPla{0}; // Plane number - Int_t mCha{0}; // Chamber number - - Int_t mNrows{0}; // Number of rows - Int_t mNcols{0}; // Number of columns - - Int_t mNchannels{0}; // Number of channels - std::vector<char> mData; //[fNchannels] Data - - ClassDefNV(TRDCalSingleChamberStatus, 1); // TRD ROC calibration class -}; - -} // namespace trd -} // namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDCommonParam.h b/Detectors/TRD/base/include/TRDBase/TRDCommonParam.h deleted file mode 100644 index 173a7eeac151b..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDCommonParam.h +++ /dev/null @@ -1,88 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDCOMMONPARAM_H -#define O2_TRDCOMMONPARAM_H - -#include "GPUCommonRtypes.h" - -namespace o2 -{ -namespace trd -{ - -class TRDPadPlane; -constexpr int kNlayer = 6, kNstack = 5, kNsector = 18, kNdet = 540; -constexpr int kTimeBins = 30; - -class TRDCommonParam -{ - public: - enum { kXenon = 0, - kArgon = 1 }; - - TRDCommonParam(const TRDCommonParam& p); - TRDCommonParam& operator=(const TRDCommonParam& p); - ~TRDCommonParam(); - - static TRDCommonParam* Instance(); - static void Terminate(); - - void SetExB(int exbOn = 1) { mExBOn = exbOn; } - void SetSamplingFrequency(float freq) { mSamplingFrequency = freq; } - void SetXenon(); - void SetArgon(); - - bool ExBOn() const { return mExBOn; } - bool IsXenon() const { return (mGasMixture == kXenon); } - bool IsArgon() const { return (mGasMixture == kArgon); } - int GetGasMixture() const { return mGasMixture; } - float GetSamplingFrequency() const { return mSamplingFrequency; } - float GetCachedField() const { return mField; } - - // Cached magnetic field, to be called by the user before using GetDiffCoeff or GetOmegaTau - bool cacheMagField(); - float GetOmegaTau(float vdrift); - bool GetDiffCoeff(float& dl, float& dt, float vdrift); - - double TimeStruct(float vdrift, double xd, double z); - - protected: - void SampleTimeStruct(float vdrift); - -#ifndef GPUCA_GPUCODE_DEVICE - static TRDCommonParam* fgInstance; // Instance of this class (singleton implementation) - static bool fgTerminated; // Defines if this class has already been terminated -#endif - int mExBOn; // Switch for the ExB effects - double mField; // cached magnetic field - float mDiffusionT; // Transverse drift coefficient - float mDiffusionL; // Longitudinal drift coefficient - float mDiffLastVdrift; // The structures are valid for fLastVdrift (caching) - - float* mTimeStruct1; //! Time Structure of Drift Cells - float* mTimeStruct2; //! Time Structure of Drift Cells - float mVDlo; // Lower drift velocity, for interpolation - float mVDhi; // Higher drift velocity, for interpolation - float mTimeLastVdrift; // The structures are valid for fLastVdrift (caching) - - float mSamplingFrequency; // Sampling Frequency in MHz - - int mGasMixture; // Gas mixture: 0-Xe/C02 1-Ar/CO2. - - private: - // This is a singleton, constructor is private! - TRDCommonParam(); - - ClassDef(TRDCommonParam, 1); // The constant parameters common to simulation and reconstruction -}; -} // namespace trd -} // namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDDiffAndTimeStructEstimator.h b/Detectors/TRD/base/include/TRDBase/TRDDiffAndTimeStructEstimator.h deleted file mode 100644 index 3e968847e0e73..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDDiffAndTimeStructEstimator.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDDIFFANDTIMESTRUCTESTIMATOR_H -#define O2_TRDDIFFANDTIMESTRUCTESTIMATOR_H - -#include <array> - -namespace o2 -{ -namespace trd -{ - -// CONTANT TIME STRUCTURE DATA FROM GARFIELD -constexpr int ktimebin = 38; -constexpr int kZbin = 11; - -/// A class to calculate diffusion and time structure values (GARFIELD model) -/// (used in digitization). Class was split off trom TRDCommonParam -/// and is no longer a singleton so that we can use it in a multithreaded context. -class TRDDiffusionAndTimeStructEstimator -{ - public: - TRDDiffusionAndTimeStructEstimator() = default; - - /// determines the diffusion coefficients as a function of drift velocity - bool GetDiffCoeff(float& dl, float& dt, float vdrift); - - /// determines drift time as function of drift velocity and coordinates - float TimeStruct(float vdrift, float xd, float z); - - private: - void SampleTimeStruct(float vdrift); - - std::array<float, ktimebin * kZbin> mTimeStruct1; //! cached Time Structure of Drift Cells (for last vdrift value) - std::array<float, ktimebin * kZbin> mTimeStruct2; //! cached Time Structure of Drift Cells (for last vdrift value) - float mVDlo; //! Lower drift velocity, for interpolation - float mVDhi; //! Higher drift velocity, for interpolation - float mInvBinWidth; //! caching 1/(mVDhi - mVDlo) - float mTimeLastVdrift = -1.f; //! The structures are valid for this mLastVdrift (caching) - - // for the diffusion part - float mDiffLastVdrift = -1.f; - float mDiffusionL = -1.f; - float mDiffusionT = -1.f; -}; - -} // namespace trd -} // namespace o2 - -#endif //O2_TRDDIFFANDTIMESTRUCTESTIMATOR_H diff --git a/Detectors/TRD/base/include/TRDBase/TRDGeometry.h b/Detectors/TRD/base/include/TRDBase/TRDGeometry.h deleted file mode 100644 index 1e9ec5a5beefa..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDGeometry.h +++ /dev/null @@ -1,74 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDGEOMETRY_H -#define O2_TRDGEOMETRY_H - -#include "TRDBase/TRDGeometryBase.h" -#include "DetectorsCommonDataFormats/DetMatrixCache.h" -#include "DetectorsCommonDataFormats/DetID.h" - -#include <string> -#include <vector> -#include <memory> - -using namespace o2::trd; -class TRootIOCtor; - -namespace o2 -{ -namespace trd -{ - -class TRDGeometry : public TRDGeometryBase, public o2::detectors::DetMatrixCacheIndirect -{ - public: - ~TRDGeometry() override = default; - // A ROOT IO constructor (to silence warnings about missing default constructor) - TRDGeometry(TRootIOCtor*) {} - - static TRDGeometry* instance() - { - static TRDGeometry mGeom; - return &mGeom; - } - - void createGeometry(std::vector<int> const& idtmed); - void addAlignableVolumes() const; - bool createClusterMatrixArray(); - void createPadPlaneArray(); - - bool rotateBack(int det, const float* const loc, float* glb) const; - bool chamberInGeometry(int det) const; - std::vector<std::string> const& getSensitiveTRDVolumes() const { return mSensitiveVolumeNames; } - - protected: - void fillMatrixCache(int mask) override; - - private: - void createVolumes(std::vector<int> const& idtmed); - void assembleChamber(int ilayer, int istack); - void createFrame(std::vector<int> const& idtmed); - void createServices(std::vector<int> const& idtmed); - void createPadPlane(int ilayer, int istack); - - std::vector<std::string> mSensitiveVolumeNames; //!< vector keeping track of sensitive TRD volumes - static const o2::detectors::DetID sDetID; - - // helper function to create volumes and registering them automatically - void createVolume(const char* name, const char* shape, int nmed, float* upar, int np); - - TRDGeometry(); - - ClassDefOverride(TRDGeometry, 2); // TRD geometry class -}; -} // end namespace trd -} // end namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDGeometryBase.h b/Detectors/TRD/base/include/TRDBase/TRDGeometryBase.h deleted file mode 100644 index e26333d245ce9..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDGeometryBase.h +++ /dev/null @@ -1,222 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDGEOMETRYBASE_H -#define O2_TRDGEOMETRYBASE_H - -#include "GPUCommonDef.h" -#include "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDPadPlane.h" - -namespace o2 -{ -namespace trd -{ -class TRDGeometryBase -{ - public: - ~TRDGeometryBase() = default; - - static constexpr int MAXMATRICES = 521; - - GPUd() int isVersion() { return 1; } - GPUd() bool isHole(int la, int st, int se) const { return (((se == 13) || (se == 14) || (se == 15)) && (st == 2)); } - GPUd() bool isOnBoundary(int det, float y, float z, float eps = 0.5) const; - - GPUd() void setSMstatus(int sm, bool status) - { - if (status) { - mSMStatus |= 0x3ffff & (0x1 << sm); - } else { - mSMStatus &= ~(0x3ffff & (0x1 << sm)); - } - } - GPUd() bool getSMstatus(int sm) const { return (mSMStatus & (0x1 << sm)) != 0; } - GPUd() static int getDetectorSec(int det) { return (det % (kNlayer * kNstack)); } - GPUd() static int getDetectorSec(int layer, int stack) { return (layer + stack * kNlayer); } - GPUd() static int getDetector(int layer, int stack, int sector) { return (layer + stack * kNlayer + sector * kNlayer * kNstack); } - GPUd() static int getLayer(int det) { return (det % kNlayer); } - GPUd() static int getStack(int det) { return ((det % (kNlayer * kNstack)) / kNlayer); } - GPUd() int getStack(float z, int layer) const; - - GPUd() const TRDPadPlane* getPadPlane(int layer, int stack) const { return &mPadPlanes[getDetectorSec(layer, stack)]; } - GPUd() const TRDPadPlane* getPadPlane(int det) const { return &mPadPlanes[getDetectorSec(det)]; } - - GPUd() int getRowMax(int layer, int stack, int /*sector*/) const { return getPadPlane(layer, stack)->getNrows(); } - GPUd() int getColMax(int layer) const { return getPadPlane(layer, 0)->getNcols(); } - GPUd() float getRow0(int layer, int stack, int /*sector*/) const { return getPadPlane(layer, stack)->getRow0(); } - GPUd() float getCol0(int layer) const { return getPadPlane(layer, 0)->getCol0(); } - - GPUd() float getRowPos(int layer, int stack, int row) { return mPadPlanes[getDetectorSec(layer, stack)].getRowPos(row); } - GPUd() float getRowSize(int layer, int stack, int row) { return mPadPlanes[getDetectorSec(layer, stack)].getRowSize(row); } - GPUd() float getRow0(int layer, int stack) { return mPadPlanes[getDetectorSec(layer, stack)].getRow0(); } - GPUd() float getRowEnd(int layer, int stack) { return mPadPlanes[getDetectorSec(layer, stack)].getRowEnd(); } - - static constexpr int getSector(int det) { return (det / (kNlayer * kNstack)); } - static constexpr float getTime0(int layer) { return TIME0[layer]; } - static constexpr float getXtrdBeg() { return XTRDBEG; } - static constexpr float getXtrdEnd() { return XTRDEND; } - static constexpr float getChamberWidth(int layer) { return CWIDTH[layer]; } - static constexpr float getChamberLength(int layer, int stack) { return CLENGTH[layer][stack]; } - static constexpr float getAlpha() { return 2.0 * 3.14159265358979324 / kNsector; } - static constexpr float cheight() { return CH; } - static constexpr float cheightSV() { return CHSV; } - static constexpr float cspace() { return VSPACE; } - static constexpr float craHght() { return CRAH; } - static constexpr float cdrHght() { return CDRH; } - static constexpr float camHght() { return CAMH; } - static constexpr float croHght() { return CROH; } - static constexpr float csvHght() { return CSVH; } - static constexpr float croWid() { return CROW; } - static constexpr float anodePos() { return ANODEPOS; } - static constexpr float myThick() { return RMYTHICK; } - static constexpr float drThick() { return DRTHICK; } - static constexpr float amThick() { return AMTHICK; } - static constexpr float drZpos() { return DRZPOS; } - static constexpr float rpadW() { return RPADW; } - static constexpr float cpadW() { return CPADW; } - static constexpr float cwidcha() { return (SWIDTH2 - SWIDTH1) / SHEIGHT * (CH + VSPACE); } - static constexpr int MCMmax() { return MCMMAX; } - static constexpr int MCMrow() { return MCMROW; } - static constexpr int ROBmaxC0() { return ROBMAXC0; } - static constexpr int ROBmaxC1() { return ROBMAXC1; } - static constexpr int ADCmax() { return ADCMAX; } - static constexpr int TBmax() { return TBMAX; } - static constexpr int padmax() { return PADMAX; } - static constexpr int colmax() { return COLMAX; } - static constexpr int rowmaxC0() { return ROWMAXC0; } - static constexpr int rowmaxC1() { return ROWMAXC1; } - - protected: - TRDGeometryBase() = default; - - static constexpr float TLENGTH = 751.0; ///< Total length of the TRD mother volume - - // Parameter of the super module mother volumes - static constexpr float SHEIGHT = 77.9; ///< Height of the supermodule - static constexpr float SWIDTH1 = 94.881; ///< Lower width of the supermodule - static constexpr float SWIDTH2 = 122.353; ///< Upper width of the supermodule - static constexpr float SLENGTH = 702.0; ///< Length of the supermodule - - // Length of the additional space in front of the supermodule used for services - static constexpr float FLENGTH = (TLENGTH - SLENGTH) / 2.0; - - static constexpr float SMPLTT = 0.2; ///< Thickness of the super module side plates - - static constexpr float VSPACE = 1.784; ///< Vertical spacing of the chambers - static constexpr float HSPACE = 2.0; ///< Horizontal spacing of the chambers - static constexpr float VROCSM = 1.2; ///< Radial distance of the first ROC to the outer plates of the SM - - static constexpr float CRAH = 4.8; ///< Height of the radiator part of the chambers - static constexpr float CDRH = 3.0; ///< Height of the drift region of the chambers - static constexpr float CAMH = 0.7; ///< Height of the amplification region of the chambers - static constexpr float CROH = 2.316; ///< Height of the readout of the chambers - static constexpr float CROW = 0.9; ///< Additional width of the readout chamber frames - static constexpr float CSVH = VSPACE - 0.742; ///< Height of the services on top of the chambers - static constexpr float CH = CRAH + CDRH + CAMH + CROH; ///< Total height of the chambers (w/o services) - static constexpr float CHSV = CH + CSVH; ///< Total height of the chambers (with services) - - // Distance of anode wire plane relative to middle of alignable volume - static constexpr float ANODEPOS = CRAH + CDRH + CAMH / 2.0 - CHSV / 2.0; - - static constexpr float CALT = 0.4; ///< Thicknesses of different parts of the chamber frame Lower aluminum frame - static constexpr float CCLST = 0.21; ///< Thickness of the lower Wacosit frame sides - static constexpr float CCLFT = 1.0; ///< Thickness of the lower Wacosit frame front - static constexpr float CGLT = 0.25; ///< Thichness of the glue around the radiator - static constexpr float CCUTA = 1.0; ///< Upper Wacosit frame around amplification region - static constexpr float CCUTB = 0.8; ///< Thickness of the upper Wacosit frame around amp. region - static constexpr float CAUT = 1.5; ///< Al frame of back panel - static constexpr float CALW = 2.5; ///< Width of additional aluminum ledge on lower frame - static constexpr float CALH = 0.4; ///< Height of additional aluminum ledge on lower frame - static constexpr float CALWMOD = 0.4; ///< Width of additional aluminum ledge on lower frame - static constexpr float CALHMOD = 2.5; ///< Height of additional aluminum ledge on lower frame - static constexpr float CWSW = 1.2; ///< Width of additional wacosit ledge on lower frame - static constexpr float CWSH = 0.3; ///< Height of additional wacosit ledge on lower frame - - static constexpr float CPADW = 0.0; ///>Difference of outer chamber width and pad plane width - static constexpr float RPADW = 1.0; ///<Difference of outer chamber width and pad plane width - - // - // Thickness of the the material layers - // - static constexpr float DRTHICK = CDRH; ///< Thickness of the drift region - static constexpr float AMTHICK = CAMH; ///< Thickness of the amplification region - static constexpr float XETHICK = DRTHICK + AMTHICK; ///< Thickness of the gas volume - static constexpr float WRTHICK = 0.00011; ///< Thickness of the wire planes - - static constexpr float RMYTHICK = 0.0015; ///< Thickness of the mylar layers in the radiator - static constexpr float RCBTHICK = 0.0055; ///< Thickness of the carbon layers in the radiator - static constexpr float RGLTHICK = 0.0065; ///< Thickness of the glue layers in the radiator - static constexpr float RRHTHICK = 0.8; ///< Thickness of the rohacell layers in the radiator - static constexpr float RFBTHICK = CRAH - 2.0 * (RMYTHICK + RCBTHICK + RRHTHICK); ///< Thickness of the fiber layers in the radiator - - static constexpr float PPDTHICK = 0.0025; ///< Thickness of copper of the pad plane - static constexpr float PPPTHICK = 0.0356; ///< Thickness of PCB board of the pad plane - static constexpr float PGLTHICK = 0.1428; ///< Thickness of the glue layer - static constexpr float PCBTHICK = 0.019; ///< Thickness of the carbon layers - static constexpr float PPCTHICK = 0.0486; ///< Thickness of the PCB readout boards - static constexpr float PRBTHICK = 0.0057; ///< Thickness of the PCB copper layers - static constexpr float PELTHICK = 0.0029; ///< Thickness of all other electronics components (caps, etc.) - static constexpr float PHCTHICK = CROH - PPDTHICK - PPPTHICK - PGLTHICK - PCBTHICK * 2.0 - PPCTHICK - PRBTHICK - PELTHICK; ///< Thickness of the honeycomb support structure - - // - // Position of the material layers - // - static constexpr float DRZPOS = 2.4; ///< Position of the drift region - static constexpr float AMZPOS = 0.0; ///< Position of the amplification region - static constexpr float WRZPOSA = 0.0; ///< Position of the wire planes - static constexpr float WRZPOSB = -AMTHICK / 2.0 + 0.001; ///< Position of the wire planes - static constexpr float CALZPOS = 0.3; ///< Position of the additional aluminum ledges - - static constexpr int MCMMAX = 16; ///< Maximum number of MCMs per ROB - static constexpr int MCMROW = 4; ///< Maximum number of MCMs per ROB Row - static constexpr int ROBMAXC0 = 6; ///< Maximum number of ROBs per C0 chamber - static constexpr int ROBMAXC1 = 8; ///< Maximum number of ROBs per C1 chamber - static constexpr int ADCMAX = 21; ///< Maximum number of ADC channels per MCM - static constexpr int TBMAX = 60; ///< Maximum number of Time bins - static constexpr int PADMAX = 18; ///< Maximum number of pads per MCM - static constexpr int COLMAX = 144; ///< Maximum number of pads per padplane row - static constexpr int ROWMAXC0 = 12; ///< Maximum number of Rows per C0 chamber - static constexpr int ROWMAXC1 = 16; ///< Maximum number of Rows per C1 chamber - - static constexpr float TIME0BASE = 300.65; ///< Base value for calculation of Time-position of pad 0 - // Time-position of pad 0 - static constexpr float TIME0[6] = {TIME0BASE + 0 * (CH + VSPACE), - TIME0BASE + 1 * (CH + VSPACE), - TIME0BASE + 2 * (CH + VSPACE), - TIME0BASE + 3 * (CH + VSPACE), - TIME0BASE + 4 * (CH + VSPACE), - TIME0BASE + 5 * (CH + VSPACE)}; - - static constexpr float XTRDBEG = 288.43; ///< X-coordinate in tracking system of begin of TRD mother volume - static constexpr float XTRDEND = 366.33; ///< X-coordinate in tracking system of end of TRD mother volume - - // The outer width of the chambers - static constexpr float CWIDTH[kNlayer] = {90.4, 94.8, 99.3, 103.7, 108.1, 112.6}; - - // The outer lengths of the chambers - // Includes the spacings between the chambers! - static constexpr float CLENGTH[kNlayer][kNstack] = { - {124.0, 124.0, 110.0, 124.0, 124.0}, - {124.0, 124.0, 110.0, 124.0, 124.0}, - {131.0, 131.0, 110.0, 131.0, 131.0}, - {138.0, 138.0, 110.0, 138.0, 138.0}, - {145.0, 145.0, 110.0, 145.0, 145.0}, - {147.0, 147.0, 110.0, 147.0, 147.0}}; - - TRDPadPlane mPadPlanes[kNlayer * kNstack]; - - int mSMStatus = 0x3ffff; - - ClassDefNV(TRDGeometryBase, 1); -}; -} // end namespace trd -} // end namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDGeometryFlat.h b/Detectors/TRD/base/include/TRDBase/TRDGeometryFlat.h deleted file mode 100644 index 3cf643d84b6df..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDGeometryFlat.h +++ /dev/null @@ -1,67 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDGEOMETRYFLAT_H -#define O2_TRDGEOMETRYFLAT_H - -#ifndef __OPENCL__ -#include <cstring> -#endif -#include "FlatObject.h" -#include "GPUCommonDef.h" -#include "GPUCommonTransform3D.h" -#include "TRDBase/TRDGeometryBase.h" -#include "TRDBase/TRDPadPlane.h" - -using namespace o2::trd; - -namespace o2 -{ -namespace trd -{ - -class TRDGeometry; - -//Reduced flat version of TRD geometry class. -//Contains all entries required for tracking on GPUs. -class TRDGeometryFlat : public o2::gpu::FlatObject, public TRDGeometryBase -{ - public: -#ifndef GPUCA_GPUCODE_DEVICE - TRDGeometryFlat() = default; - TRDGeometryFlat(const TRDGeometryFlat& v) : FlatObject(), TRDGeometryBase() - { - memcpy((void*)this, (void*)&v, sizeof(*this)); - } - TRDGeometryFlat(const TRDGeometry& geo); - ~TRDGeometryFlat() = default; -#endif - GPUd() const o2::gpu::Transform3D* getMatrixT2L(int det) const - { - if (mMatrixIndirection[det] == -1) - return nullptr; - return &mMatrixCache[mMatrixIndirection[det]]; - ; - } - - GPUd() bool chamberInGeometry(int det) const - { - return (mMatrixIndirection[det] >= 0); - } - - private: - o2::gpu::Transform3D mMatrixCache[MAXMATRICES]; - short mMatrixIndirection[kNdet]; -}; - -} // namespace trd -} // namespace o2 - -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDPadPlane.h b/Detectors/TRD/base/include/TRDBase/TRDPadPlane.h deleted file mode 100644 index 130483dae6f16..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDPadPlane.h +++ /dev/null @@ -1,175 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDPADPLANE_H -#define O2_TRDPADPLANE_H - -//Forwards to standard header with protection for GPU compilation -#include "GPUCommonRtypes.h" // for ClassDef - -#include "GPUCommonDef.h" - -//////////////////////////////////////////////////////////////////////////// -// // -// TRD pad plane class // -// // -// Contains the information on ideal pad positions, pad dimensions, // -// tilting angle, etc. // -// It also provides methods to identify the current pad number from // -// local tracking coordinates. // -// // -//////////////////////////////////////////////////////////////////////////// -namespace o2 -{ -namespace trd -{ -class TRDPadPlane -{ - public: - TRDPadPlane() = default; - TRDPadPlane(int layer, int stack) : mLayer(layer), mStack(stack){}; - TRDPadPlane(const TRDPadPlane& p) = delete; - TRDPadPlane& operator=(const TRDPadPlane& p) = delete; - ~TRDPadPlane() = default; - - void setLayer(int l) { mLayer = l; }; - void setStack(int s) { mStack = s; }; - void setRowSpacing(double s) { mRowSpacing = s; }; - void setColSpacing(double s) { mColSpacing = s; }; - void setLengthRim(double l) { mLengthRim = l; }; - void setWidthRim(double w) { mWidthRim = w; }; - void setNcols(int n); - void setNrows(int n); - void setPadCol(int ic, double c) - { - if (ic < mNcols) - mPadCol[ic] = c; - }; - void setPadRow(int ir, double r) - { - if (ir < mNrows) - mPadRow[ir] = r; - }; - void setLength(double l) { mLength = l; }; - void setWidth(double w) { mWidth = w; }; - void setLengthOPad(double l) { mLengthOPad = l; }; - void setWidthOPad(double w) { mWidthOPad = w; }; - void setLengthIPad(double l) { mLengthIPad = l; }; - void setWidthIPad(double w) { mWidthIPad = w; }; - void setPadRowSMOffset(double o) { mPadRowSMOffset = o; }; - void setAnodeWireOffset(float o) { mAnodeWireOffset = o; }; - void setTiltingAngle(double t); - - GPUd() int getPadRowNumber(double z) const; - GPUd() int getPadRowNumberROC(double z) const; - GPUd() int getPadColNumber(double rphi) const; - - GPUd() double getTiltOffset(double rowOffset) const { return mTiltingTan * (rowOffset - 0.5 * mLengthIPad); }; - GPUd() double getPadRowOffset(int row, double z) const - { - if ((row < 0) || (row >= mNrows)) - return -1.0; - else - return mPadRow[row] + mPadRowSMOffset - z; - }; - GPUd() double getPadRowOffsetROC(int row, double z) const - { - if ((row < 0) || (row >= mNrows)) - return -1.0; - else - return mPadRow[row] - z; - }; - - GPUd() double getPadColOffset(int col, double rphi) const - { - if ((col < 0) || (col >= mNcols)) - return -1.0; - else - return rphi - mPadCol[col]; - }; - - GPUd() double getTiltingAngle() const { return mTiltingAngle; }; - GPUd() int getNrows() const { return mNrows; }; - GPUd() int getNcols() const { return mNcols; }; - GPUd() double getRow0() const { return mPadRow[0] + mPadRowSMOffset; }; - GPUd() double getRow0ROC() const { return mPadRow[0]; }; - GPUd() double getCol0() const { return mPadCol[0]; }; - GPUd() double getRowEnd() const { return mPadRow[mNrows - 1] - mLengthOPad + mPadRowSMOffset; }; - GPUd() double getRowEndROC() const { return mPadRow[mNrows - 1] - mLengthOPad; }; - GPUd() double getColEnd() const { return mPadCol[mNcols - 1] + mWidthOPad; }; - GPUd() double getRowPos(int row) const { return mPadRow[row] + mPadRowSMOffset; }; - GPUd() double getRowPosROC(int row) const { return mPadRow[row]; }; - GPUd() double getColPos(int col) const { return mPadCol[col]; }; - GPUd() double getRowSize(int row) const - { - if ((row == 0) || (row == mNrows - 1)) - return mLengthOPad; - else - return mLengthIPad; - }; - GPUd() double getColSize(int col) const - { - if ((col == 0) || (col == mNcols - 1)) - return mWidthOPad; - else - return mWidthIPad; - }; - - GPUd() double getLengthRim() const { return mLengthRim; }; - GPUd() double getWidthRim() const { return mWidthRim; }; - GPUd() double getRowSpacing() const { return mRowSpacing; }; - GPUd() double getColSpacing() const { return mColSpacing; }; - GPUd() double getLengthOPad() const { return mLengthOPad; }; - GPUd() double getLengthIPad() const { return mLengthIPad; }; - GPUd() double getWidthOPad() const { return mWidthOPad; }; - GPUd() double getWidthIPad() const { return mWidthIPad; }; - GPUd() double getAnodeWireOffset() const { return mAnodeWireOffset; }; - - protected: - static constexpr int MAXCOLS = 144; - static constexpr int MAXROWS = 16; - - int mLayer; // Layer number - int mStack; // Stack number - - double mLength; // Length of pad plane in z-direction (row) - double mWidth; // Width of pad plane in rphi-direction (col) - - double mLengthRim; // Length of the rim in z-direction (row) - double mWidthRim; // Width of the rim in rphi-direction (col) - - double mLengthOPad; // Length of an outer pad in z-direction (row) - double mWidthOPad; // Width of an outer pad in rphi-direction (col) - - double mLengthIPad; // Length of an inner pad in z-direction (row) - double mWidthIPad; // Width of an inner pad in rphi-direction (col) - - double mRowSpacing; // Spacing between the pad rows - double mColSpacing; // Spacing between the pad columns - - int mNrows; // Number of rows - int mNcols; // Number of columns - - double mTiltingAngle; // Pad tilting angle - double mTiltingTan; // Tangens of pad tilting angle - - double mPadRow[MAXROWS]; // Pad border positions in row direction - double mPadCol[MAXCOLS]; // Pad border positions in column direction - - double mPadRowSMOffset; // To be added to translate local ROC system to local SM system - - double mAnodeWireOffset; // Distance of first anode wire from pad edge - - private: - ClassDefNV(TRDPadPlane, 1); // TRD ROC pad plane -}; -} // namespace trd -} // namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/TRDSimParam.h b/Detectors/TRD/base/include/TRDBase/TRDSimParam.h deleted file mode 100644 index 8e555527f0380..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/TRDSimParam.h +++ /dev/null @@ -1,172 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TRDSIMPARAM_H -#define O2_TRDSIMPARAM_H - -//Forwards to standard header with protection for GPU compilation -#include "GPUCommonRtypes.h" // for ClassDef - -namespace o2 -{ -namespace trd -{ -//////////////////////////////////////////////////////////////////////////// -// // -// Class containing constant simulation parameters // -// // -//////////////////////////////////////////////////////////////////////////// -class TRDSimParam -{ - public: - enum { kNplan = 6, - kNcham = 5, - kNsect = 18, - kNdet = 540, - kNPadsInPadResponse = 3 // Number of pads included in the pad response - }; - - static TRDSimParam* Instance(); - static void Terminate(); - - void SetGasGain(float gasgain) { mGasGain = gasgain; } - void SetNoise(float noise) { mNoise = noise; } - void SetChipGain(float chipgain) { mChipGain = chipgain; } - void SetADCoutRange(float range) { mADCoutRange = range; } - void SetADCinRange(float range) { mADCinRange = range; } - void SetADCbaseline(int basel) { mADCbaseline = basel; } - void SetDiffusion(int diffOn = 1) { mDiffusionOn = diffOn; } - void SetElAttach(int elOn = 1) { mElAttachOn = elOn; } - void SetElAttachProp(float prop) { mElAttachProp = prop; } - void SetTimeResponse(int trfOn = 1) - { - mTRFOn = trfOn; - ReInit(); - } - void SetCrossTalk(int ctOn = 1) - { - mCTOn = ctOn; - ReInit(); - } - void SetPadCoupling(float v) { mPadCoupling = v; } - void SetTimeCoupling(float v) { mTimeCoupling = v; } - void SetTimeStruct(bool tsOn = 1) { mTimeStructOn = tsOn; } - void SetPadResponse(int prfOn = 1) { mPRFOn = prfOn; } - void SetNTimeBins(int ntb) { mNTimeBins = ntb; } - void SetNTBoverwriteOCDB(bool over = true) { mNTBoverwriteOCDB = over; } - float GetGasGain() const { return mGasGain; } - float GetNoise() const { return mNoise; } - float GetChipGain() const { return mChipGain; } - float GetADCoutRange() const { return mADCoutRange; } - float GetADCinRange() const { return mADCinRange; } - int GetADCbaseline() const { return mADCbaseline; } - float GetTRFlo() const { return mTRFlo; } - float GetTRFhi() const { return mTRFhi; } - float GetPadCoupling() const { return mPadCoupling; } - float GetTimeCoupling() const { return mTimeCoupling; } - int GetNTimeBins() const { return mNTimeBins; } - bool GetNTBoverwriteOCDB() const { return mNTBoverwriteOCDB; } - bool DiffusionOn() const { return mDiffusionOn; } - bool ElAttachOn() const { return mElAttachOn; } - float GetElAttachProp() const { return mElAttachProp; } - bool TRFOn() const { return mTRFOn; } - bool CTOn() const { return mCTOn; } - bool TimeStructOn() const { return mTimeStructOn; } - bool PRFOn() const { return mPRFOn; } - const int getNumberOfPadsInPadResponse() const { return kNPadsInPadResponse; } - inline double TimeResponse(double) const; - inline double CrossTalk(double) const; - void ReInit(); - - protected: - static TRDSimParam* fgInstance; // Instance of this class (singleton implementation) - static bool fgTerminated; // Defines if this class has already been terminated and - // therefore does not return instances in GetInstance anymore - - // Digitization parameter - float mGasGain; // Gas gain - float mNoise; // Electronics noise - float mChipGain; // Electronics gain - - float mADCoutRange; // ADC output range (number of channels) - float mADCinRange; // ADC input range (input charge) - int mADCbaseline; // ADC intrinsic baseline in ADC channel - - int mDiffusionOn; // Switch for the diffusion - - int mElAttachOn; // Switch for the electron attachment - float mElAttachProp; // Propability for electron attachment (for 1m) - - int mTRFOn; // Switch for the time response - float* mTRFsmp; //! Integrated time response - int mTRFbin; // Number of bins for the TRF - float mTRFlo; // Lower boundary of the TRF - float mTRFhi; // Higher boundary of the TRF - float mInvTRFwid; // Inverse of the bin width of the integrated TRF - - int mCTOn; // Switch for cross talk - float* mCTsmp; //! Integrated cross talk - - float mPadCoupling; // Pad coupling factor - float mTimeCoupling; // Time coupling factor (image charge of moving ions) - int mTimeStructOn; // Switch for cell time structure - - int mPRFOn; // Switch for the pad response - - int mNTimeBins; // Number of time bins (only used it fNTBoverwriteOCDB = true) - bool mNTBoverwriteOCDB; // Switch to overwrite number of time bins from PCDB - - private: - // This is a singleton, constructor is private! - TRDSimParam(); - ~TRDSimParam(); - - void Init(); - void SampleTRF(); - - ClassDefNV(TRDSimParam, 1); // The TRD simulation parameters -}; - -inline double TRDSimParam::TimeResponse(double time) const -{ - // - // Applies the preamp shaper time response - // (We assume a signal rise time of 0.2us = fTRFlo/2. - // - - double rt = (time - .5 * mTRFlo) * mInvTRFwid; - int iBin = (int)rt; - double dt = rt - iBin; - if ((iBin >= 0) && (iBin + 1 < mTRFbin)) { - return mTRFsmp[iBin] + (mTRFsmp[iBin + 1] - mTRFsmp[iBin]) * dt; - } else { - return 0.0; - } -} - -inline double TRDSimParam::CrossTalk(double time) const -{ - // - // Applies the pad-pad capacitive cross talk - // - - double rt = (time - mTRFlo) * mInvTRFwid; - int iBin = (int)rt; - double dt = rt - iBin; - if ((iBin >= 0) && (iBin + 1 < mTRFbin)) { - return mCTsmp[iBin] + (mCTsmp[iBin + 1] - mCTsmp[iBin]) * dt; - } else { - return 0.0; - } -} - -} // namespace trd -} // namespace o2 -#endif diff --git a/Detectors/TRD/base/include/TRDBase/Tracklet.h b/Detectors/TRD/base/include/TRDBase/Tracklet.h index f417dd9bce22c..88d6d042ff35f 100644 --- a/Detectors/TRD/base/include/TRDBase/Tracklet.h +++ b/Detectors/TRD/base/include/TRDBase/Tracklet.h @@ -8,7 +8,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//#include "TRDBase/TRDGeometryBase.h" +//#include "TRDBase/GeometryBase.h" //#include "DetectorsCommonDataFormats/DetMatrixCache.h" //#include "DetectorsCommonDataFormats/DetID.h" @@ -32,7 +32,7 @@ #include <memory> // for std::unique_ptr #include "Rtypes.h" // for ClassDef -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include <SimulationDataFormat/MCCompLabel.h> namespace o2 @@ -69,7 +69,7 @@ class Tracklet // ----- Getters for contents of tracklet word ----- int getYbin() const; // in units of 160 um - int getdY() const; // in units of 140 um + float getdY() const; // in units of 140 um int getZbin() const { return ((mROB >> 1) << 2) | (mMCM >> 2); } int getPID() const { return mPID; } @@ -125,7 +125,7 @@ class Tracklet std::vector<float> getClsCharges() const { return mClsCharges; } //TODO this is a problem, giving a pointer out to an internal class member protected: - TRDGeometry* mGeo; //! TRD geometry + Geometry* mGeo; //! TRD geometry int mHCId; // half-chamber ID (only transient) // unsigned int mTrackletWord; // tracklet word: PID | Z | deflection length | Y diff --git a/Detectors/TRD/base/include/TRDBase/TrackletTransformer.h b/Detectors/TRD/base/include/TRDBase/TrackletTransformer.h new file mode 100644 index 0000000000000..80614fb2df6d8 --- /dev/null +++ b/Detectors/TRD/base/include/TRDBase/TrackletTransformer.h @@ -0,0 +1,71 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_TRACKLETTRANSFORMER_H +#define O2_TRD_TRACKLETTRANSFORMER_H + +#include "TRDBase/Geometry.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/CalibratedTracklet.h" + +namespace o2 +{ +namespace trd +{ + +class TrackletTransformer +{ + public: + TrackletTransformer(); + ~TrackletTransformer() = default; + + float getXCathode() { return mXCathode; } + float getXAnode() { return mXAnode; } + float getXDrift() { return mXDrift; } + float getXtb0() { return mXtb0; } + + void setXCathode(float x) { mXCathode = x; } + void setXAnode(float x) { mXAnode = x; } + void setXDrift(float x) { mXDrift = x; } + void setXtb0(float x) { mXtb0 = x; } + + void loadPadPlane(int hcid); + + float calculateY(int hcid, int column, int position); + + float calculateZ(int padrow); + + float calculateDy(int slope, double oldLorentzAngle, double lorentzAngle, double driftVRatio); + + float calibrateX(double x, double t0Correction); + + std::array<float, 3> transformL2T(int hcid, std::array<double, 3> spacePoint); + + CalibratedTracklet transformTracklet(Tracklet64 tracklet); + + private: + o2::trd::Geometry* mGeo; + const o2::trd::PadPlane* mPadPlane; + + float mXCathode; + float mXAnode; + float mXDrift; + float mXtb0; + + float mt0Correction; + float mOldLorentzAngle; + float mLorentzAngle; + float mDriftVRatio; +}; + +} // namespace trd +} // namespace o2 + +#endif diff --git a/Detectors/TRD/base/macros/OCDB2CCDB.C b/Detectors/TRD/base/macros/OCDB2CCDB.C index 132546513eecf..3f2b691370e6c 100644 --- a/Detectors/TRD/base/macros/OCDB2CCDB.C +++ b/Detectors/TRD/base/macros/OCDB2CCDB.C @@ -25,7 +25,7 @@ // alienv enter VO_ALICE@O2::latest-O2-o2,VO_ALICE@AliRoot::latest-O2-o2 // according to my configs, modify as required of course. // - +#if !defined(__CINT__) || defined(__MAKECINT__) #include <iostream> #include <fstream> #include <string> @@ -77,12 +77,13 @@ #include "TRDBase/ChamberNoise.h" #include "TRDBase/CalOnlineGainTables.h" #include "TRDBase/FeeParam.h" -#include "TRDBase/TrapConfig.h" +#include "TRDSimulation/TrapConfig.h" +#include "DataFormatsTRD/Constants.h" +#endif using namespace std; using namespace o2::ccdb; using namespace o2::trd; - // global variables // histograms used for extracting the mean and RMS of calibration parameters @@ -453,38 +454,38 @@ void OCDB2CCDB(Int_t run, const Char_t* storageURI = "alien://folder=/alice/data /* THESE ARE THE ONES NOT CURRENTLY INCLUDED. -trd_ddchamberStatus -trd_gaschromatographXe -trd_gasOverpressure +trd_ddchamberStatus +trd_gaschromatographXe +trd_gasOverpressure trd_hvDriftImon -MonitoringData -PIDLQ -trd_envTemp -trd_gasCO2 +MonitoringData +PIDLQ +trd_envTemp +trd_gasCO2 trd_hvDriftUmon -PIDLQ1D -trd_gaschromatographCO2 -trd_gasH2O -trd_hvAnodeImon +PIDLQ1D +trd_gaschromatographCO2 +trd_gasH2O +trd_hvAnodeImon TrkAttach -PIDNN -PHQ PIDThresholds +PIDNN +PHQ PIDThresholds This pulls stuff from DCS I should hopefully not need this stuff for simulation. -DCS +DCS AliTRDSensorArray descends from AliTRDDCSSensorArray -trd_gaschromatographN2 -trd_gasO2 -trd_goofieHv -trd_goofiePressure +trd_gaschromatographN2 +trd_gasO2 +trd_goofieHv +trd_goofiePressure trd_hvAnodeUmon -trd_goofieN2 -trd_goofieTemp -trd_goofieCO2 -trd_goofiePeakArea -trd_goofieVelocity -trd_goofieGain -trd_goofiePeakPos +trd_goofieN2 +trd_goofieTemp +trd_goofieCO2 +trd_goofiePeakArea +trd_goofieVelocity +trd_goofieGain +trd_goofiePeakPos */ return; } diff --git a/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C b/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C index 0121e3d1c4ed2..0b6a5f0bcfe61 100644 --- a/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C +++ b/Detectors/TRD/base/macros/OCDB2CCDBTrapConfig.C @@ -207,7 +207,7 @@ its all going here unfortunately .... */ vector<std::string> run2confignames = { - /* "cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5505", + "cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5505", "cf_pg-fpnp32_zs-s16-deh_tb24_trkl-b2p-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5585", "cf_pg-fpnp32_zs-s16-deh_tb24_trkl-b5p-fs1e24-ht200-qs0e23s23e22-pidlhc11dv3en-pt100_ptrg.r5766", "cf_pg-fpnp32_zs-s16-deh_tb24_trkl-b0-fs1e24-ht200-qs0e23s23e22-pidlhc11dv3en_ptrg.r5767", @@ -261,7 +261,7 @@ its all going here unfortunately .... "cf_pg-fpnp32_zs-s16-deh_tb26_trkl-b2n-fs1e24-ht200-qs0e23s23e22-pidlhc11dv3en-pt100_ptrg.r5771", "cf_pg-fpnp32_zs-s16-deh_tb24_trkl-b5n-fs1e24-ht200-qs0e23s23e22-pidlhc11dv1-pt100_ptrg.r5762", "cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b0-fs1e24-ht200-qs0e24s24e23-pidlinear_ptrg.r5505", - "cf_pg-fpnp32_zs-s16-deh_tb24_trkl-b0-fs1e24-ht200-qs0e23s23e22-pidlhc11dv1_ptrg.r5762",*/ + "cf_pg-fpnp32_zs-s16-deh_tb24_trkl-b0-fs1e24-ht200-qs0e23s23e22-pidlhc11dv1_ptrg.r5762", "cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5549"}; // now we loop over these extracting the trapconfing and dumping it into the ccdb. @@ -270,10 +270,10 @@ its all going here unfortunately .... if ((entry = GetCDBentry("TRD/Calib/TrapConfig", 0))) { if ((run2caltrapconfig = (AliTRDCalTrapConfig*)entry->GetObject())) { for (auto const& run2trapconfigname : run2confignames) { - auto o2trapconfig = new TrapConfig(run2trapconfigname); + auto o2trapconfig = new TrapConfig(); run2trapconfig = run2caltrapconfig->Get(run2trapconfigname.c_str()); ParseTrapConfigs(o2trapconfig, run2trapconfig); - // ccdb.storeAsTFileAny(o2trapconfig, "TRD_test/TrapConfig2020/c", metadata, 1, 1670700184549); //upper time chosen into the future else the server simply adds a year + ccdb.storeAsTFileAny(o2trapconfig, "TRD_test/TrapConfig", metadata, 1, 1670700184549); //upper time chosen into the future else the server simply adds a year // cout << "ccdb.storeAsTFileAny(o2trapconfig, Form(\"" << TRDCalBase.c_str() << "/TrapConfig/" << run2trapconfigname.c_str()<< endl; //upper time chosen into the future else the server simply adds a year //AliTRDcalibDB *calibdb=AliTRDcalibDB::Instance(); } diff --git a/Detectors/TRD/base/src/CalDet.cxx b/Detectors/TRD/base/src/CalDet.cxx index b429399eaccc4..7fdf6ecd2a6fb 100644 --- a/Detectors/TRD/base/src/CalDet.cxx +++ b/Detectors/TRD/base/src/CalDet.cxx @@ -20,12 +20,13 @@ #include <TH2F.h> #include <TRobustEstimator.h> -#include "TRDBase/TRDPadPlane.h" -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/PadPlane.h" +#include "TRDBase/Geometry.h" #include "TRDBase/CalDet.h" // #include "AliMathBase.h" using namespace o2::trd; +using namespace o2::trd::constants; //___________________________________________________________________________________ double CalDet::getMean(CalDet* const outlierDet) const @@ -35,11 +36,11 @@ double CalDet::getMean(CalDet* const outlierDet) const // if (!outlierDet) { - return TMath::Mean(kNdet, mData.data()); + return TMath::Mean(MAXCHAMBER, mData.data()); } - std::array<double, kNdet> ddata; + std::array<double, MAXCHAMBER> ddata; int nPoints = 0; - for (int i = 0; i < kNdet; i++) { + for (int i = 0; i < MAXCHAMBER; i++) { if (!(outlierDet->getValue(i))) { ddata[nPoints] = mData[nPoints]; nPoints++; @@ -56,11 +57,11 @@ double CalDet::getMedian(CalDet* const outlierDet) const // if (!outlierDet) { - return (double)TMath::Median(kNdet, mData.data()); + return (double)TMath::Median(MAXCHAMBER, mData.data()); } - std::array<double, kNdet> ddata; + std::array<double, MAXCHAMBER> ddata; int nPoints = 0; - for (int i = 0; i < kNdet; i++) { + for (int i = 0; i < MAXCHAMBER; i++) { if (!(outlierDet->getValue(i))) { ddata[nPoints] = mData[nPoints]; nPoints++; @@ -77,11 +78,11 @@ double CalDet::getRMS(CalDet* const outlierDet) const // if (!outlierDet) { - return TMath::RMS(kNdet, mData.data()); + return TMath::RMS(MAXCHAMBER, mData.data()); } - std::array<double, kNdet> ddata; + std::array<double, MAXCHAMBER> ddata; int nPoints = 0; - for (int i = 0; i < kNdet; i++) { + for (int i = 0; i < MAXCHAMBER; i++) { if (!(outlierDet->getValue(i))) { ddata[nPoints] = mData[nPoints]; nPoints++; @@ -98,27 +99,29 @@ double CalDet::getRMSRobust(double robust) const // // sorted - std::array<int, kNdet> index; - TMath::Sort((int)kNdet, mData.data(), index.data()); + std::array<int, MAXCHAMBER> index; + TMath::Sort((int)MAXCHAMBER, mData.data(), index.data()); // reject - double reject = (int)(kNdet * (1 - robust) / 2.0); + double reject = (int)(MAXCHAMBER * (1 - robust) / 2.0); if (reject <= 0) { reject = 0; } - if (reject >= kNdet) { + if (reject >= MAXCHAMBER) { reject = 0; } - std::array<double, kNdet> ddata; + std::array<double, MAXCHAMBER> ddata; int nPoints = 0; - for (int i = 0; i < kNdet; i++) { + for (int i = 0; i < MAXCHAMBER; i++) { bool rej = kFALSE; for (int k = 0; k < reject; k++) { - if (i == index[k]) + if (i == index[k]) { rej = kTRUE; - if (i == index[kNdet - (k + 1)]) + } + if (i == index[MAXCHAMBER - (k + 1)]) { rej = kTRUE; + } } if (!rej) { ddata[nPoints] = mData[i]; @@ -136,25 +139,29 @@ double CalDet::getMeanRobust(double robust) const // // sorted - std::array<int, kNdet> index; - TMath::Sort((int)kNdet, mData.data(), index.data()); + std::array<int, MAXCHAMBER> index; + TMath::Sort((int)MAXCHAMBER, mData.data(), index.data()); // reject - double reject = (int)(kNdet * (1 - robust) / 2.0); - if (reject <= 0) + double reject = (int)(MAXCHAMBER * (1 - robust) / 2.0); + if (reject <= 0) { reject = 0; - if (reject >= kNdet) + } + if (reject >= MAXCHAMBER) { reject = 0; + } - std::array<double, kNdet> ddata; + std::array<double, MAXCHAMBER> ddata; int nPoints = 0; - for (int i = 0; i < kNdet; i++) { + for (int i = 0; i < MAXCHAMBER; i++) { bool rej = kFALSE; for (int k = 0; k < reject; k++) { - if (i == index[k]) + if (i == index[k]) { rej = kTRUE; - if (i == index[kNdet - (k + 1)]) + } + if (i == index[MAXCHAMBER - (k + 1)]) { rej = kTRUE; + } } if (!rej) { ddata[nPoints] = mData[i]; @@ -171,10 +178,10 @@ double CalDet::getLTM(double* sigma, double fraction, CalDet* const outlierDet) // Calculate LTM mean and sigma // - std::array<double, kNdet> ddata; + std::array<double, MAXCHAMBER> ddata; double mean = 0, lsigma = 0; unsigned int nPoints = 0; - for (int i = 0; i < kNdet; i++) { + for (int i = 0; i < MAXCHAMBER; i++) { if (!outlierDet || !(outlierDet->getValue(i))) { ddata[nPoints] = mData[nPoints]; nPoints++; @@ -232,7 +239,7 @@ TH1F* CalDet::makeHisto1Distribution(float min, float max, int type) title << mTitle.c_str() << " CalDet 1Distribution"; std::string titlestr = title.str(); TH1F* his = new TH1F(mName.c_str(), titlestr.c_str(), 100, min, max); - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { his->Fill(getValue(idet)); } return his; @@ -276,8 +283,8 @@ TH1F* CalDet::makeHisto1DAsFunctionOfDet(float min, float max, int type) std::stringstream title; title << mTitle.c_str() << " CalDet as function of det"; std::string titlestr = title.str(); - TH1F* his = new TH1F(mName.c_str(), titlestr.c_str(), kNdet, 0, kNdet); - for (int det = 0; det < kNdet; det++) { + TH1F* his = new TH1F(mName.c_str(), titlestr.c_str(), MAXCHAMBER, 0, MAXCHAMBER); + for (int det = 0; det < MAXCHAMBER; det++) { his->Fill(det + 0.5, getValue(det)); } his->SetMaximum(max); @@ -321,7 +328,7 @@ TH2F* CalDet::makeHisto2DCh(int ch, float min, float max, int type) } } - TRDGeometry* trdGeo = TRDGeometry::instance(); + Geometry* trdGeo = Geometry::instance(); float poslocal[3] = {0, 0, 0}; float posglobal[3] = {0, 0, 0}; @@ -334,10 +341,10 @@ TH2F* CalDet::makeHisto2DCh(int ch, float min, float max, int type) // Where we begin int offsetch = 6 * ch; - for (int isec = 0; isec < kNsect; isec++) { - for (int ipl = 0; ipl < kNplan; ipl++) { + for (int isec = 0; isec < NSECTOR; isec++) { + for (int ipl = 0; ipl < NLAYER; ipl++) { int det = offsetch + isec * 30 + ipl; - const TRDPadPlane* padPlane = trdGeo->getPadPlane(ipl, ch); + const PadPlane* padPlane = trdGeo->getPadPlane(ipl, ch); for (int icol = 0; icol < padPlane->getNcols(); icol++) { poslocal[0] = trdGeo->getTime0(ipl); poslocal[2] = padPlane->getRowPos(0); @@ -394,8 +401,8 @@ TH2F* CalDet::makeHisto2DSmPl(int sm, int pl, float min, float max, int type) } } - TRDGeometry* trdGeo = TRDGeometry::instance(); - const TRDPadPlane* padPlane0 = trdGeo->getPadPlane(pl, 0); + Geometry* trdGeo = Geometry::instance(); + const PadPlane* padPlane0 = trdGeo->getPadPlane(pl, 0); double row0 = padPlane0->getRow0(); double col0 = padPlane0->getCol0(); @@ -406,9 +413,9 @@ TH2F* CalDet::makeHisto2DSmPl(int sm, int pl, float min, float max, int type) // Where we begin int offsetsmpl = 30 * sm + pl; - for (int k = 0; k < kNcham; k++) { + for (int k = 0; k < NSTACK; k++) { int det = offsetsmpl + k * 6; - int kb = kNcham - 1 - k; + int kb = NSTACK - 1 - k; his->SetBinContent(kb + 1, 2, mData[det]); his->SetBinContent(kb + 1, 3, mData[det]); } @@ -427,7 +434,7 @@ void CalDet::add(float c1) // Add constant for all detectors // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { mData[idet] += c1; } } @@ -439,7 +446,7 @@ void CalDet::multiply(float c1) // multiply constant for all detectors // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { mData[idet] *= c1; } } @@ -451,7 +458,7 @@ void CalDet::add(const CalDet* calDet, double c1) // add caldet channel by channel multiplied by c1 // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { mData[idet] += calDet->getValue(idet) * c1; } } @@ -463,7 +470,7 @@ void CalDet::multiply(const CalDet* calDet) // multiply caldet channel by channel // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { mData[idet] *= calDet->getValue(idet); } } @@ -476,7 +483,7 @@ void CalDet::divide(const CalDet* calDet) // float eps = 0.00000000000000001; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (TMath::Abs(calDet->getValue(idet)) > eps) { mData[idet] /= calDet->getValue(idet); } diff --git a/Detectors/TRD/base/src/CalOnlineGainTables.cxx b/Detectors/TRD/base/src/CalOnlineGainTables.cxx index 3e5311c6b24a2..6164137870c2f 100644 --- a/Detectors/TRD/base/src/CalOnlineGainTables.cxx +++ b/Detectors/TRD/base/src/CalOnlineGainTables.cxx @@ -16,10 +16,12 @@ /////////////////////////////////////////////////////////////////////////////// #include "TRDBase/CalOnlineGainTables.h" -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/FeeParam.h" +#include "DataFormatsTRD/Constants.h" using namespace o2::trd; +using namespace o2::trd::constants; using namespace std; float o2::trd::CalOnlineGainTables::UnDef = -999.0; @@ -30,7 +32,7 @@ int CalOnlineGainTables::getArrayOffset(int det, int row, int col) const int rob = mFeeParam->getROBfromPad(row, col); int mcm = mFeeParam->getMCMfromPad(row, col); int detoffset = det * 128; //TODO find this constant from somewhere else max rob=8, max mcm=16, so 7x16+15=127 - int mcmoffset = rob * (mFeeParam->mgkNmcmRob) + mcm; + int mcmoffset = rob * NMCMROB + mcm; return detoffset + mcmoffset; } @@ -41,7 +43,7 @@ int CalOnlineGainTables::getChannel(int col) const int CalOnlineGainTables::getArrayOffsetrm(int det, int rob, int mcm) const { - return det * 128 + rob * FeeParam::instance()->mgkNmcmRob + mcm; + return det * 128 + rob * NMCMROB + mcm; } float CalOnlineGainTables::getGainCorrectionFactor(int det, int row, int col) const @@ -57,8 +59,9 @@ float CalOnlineGainTables::getGainCorrectionFactor(int det, int row, int col) co GainCorrectionFactor = -1.0; } else if (mGainTable[arrayoffset].mFGFN[channel] > 511) { GainCorrectionFactor = CalOnlineGainTables::UnDef; - } else + } else { GainCorrectionFactor = (mGainTable[arrayoffset].mFGFN[channel] / 2048.) + 0.875; + } } else { float ADCCorrection = (1. / (1. + ((float)mGainTable[arrayoffset].mAdcdac / 31.) * 0.4 / 1.05)); GainCorrectionFactor = ADCCorrection * (((mGainTable[arrayoffset].mFGFN[channel]) / 2048.) + 0.875); diff --git a/Detectors/TRD/base/src/CalPad.cxx b/Detectors/TRD/base/src/CalPad.cxx index f27dfd8efaf5d..418e36c51c247 100644 --- a/Detectors/TRD/base/src/CalPad.cxx +++ b/Detectors/TRD/base/src/CalPad.cxx @@ -14,13 +14,14 @@ #include <TStyle.h> #include "TRDBase/CalPad.h" -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDPadPlane.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/PadPlane.h" #include "CalROC.h" #include "CalDet.h" using namespace o2::trd; +using namespace o2::trd::constants; /////////////////////////////////////////////////////////////////////////////// // // @@ -35,7 +36,7 @@ CalPad::CalPad() // CalPad default constructor // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { fROC[idet] = nullptr; } } @@ -47,9 +48,9 @@ CalPad::CalPad(const Text_t* name, const Text_t* title) // CalPad constructor // - for (int isec = 0; isec < kNsect; isec++) { - for (int ipla = 0; ipla < kNplan; ipla++) { - for (int icha = 0; icha < kNcham; icha++) { + for (int isec = 0; isec < NSECTOR; isec++) { + for (int ipla = 0; ipla < NLAYER; ipla++) { + for (int icha = 0; icha < NSTACK; icha++) { int idet = getDet(ipla, icha, isec); fROC[idet] = new CalROC(ipla, icha); } @@ -66,7 +67,7 @@ CalPad::CalPad(const CalPad& c) // CalPad copy constructor // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { fROC[idet] = new CalROC(*((CalPad&)c).fROC[idet]); } mName = c.mName; @@ -80,7 +81,7 @@ CalPad::~CalPad() // CalPad destructor // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (fROC[idet]) { delete fROC[idet]; fROC[idet] = 0; @@ -107,7 +108,7 @@ void CalPad::Copy(TObject& c) const // Copy function // - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (((CalPad&)c).fROC[idet]) { delete ((CalPad&)c).fROC[idet]; } @@ -133,7 +134,7 @@ bool CalPad::scaleROCs(const CalDet* values) if (!values) return kFALSE; bool result = kTRUE; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (fROC[idet]) { if (!fROC[idet]->multiply(values->getValue(idet))) result = kFALSE; @@ -172,7 +173,7 @@ double CalPad::getMeanRMS(double& rms, const CalDet* calDet, int type) if (type == 0) factor = 1.0; double sum = 0, sum2 = 0, n = 0, val; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet) factor = calDet->getValue(idet); CalROC* calRoc = fROC[idet]; @@ -208,9 +209,9 @@ double CalPad::getMean(const CalDet* calDet, int type, CalPad* const outlierPad) double factor = 0.0; if (type == 0) factor = 1.0; - double arr[kNdet]; + double arr[MAXCHAMBER]; int n = 0; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet) factor = calDet->getValue(idet); CalROC* calRoc = fROC[idet]; @@ -240,9 +241,9 @@ double CalPad::getRMS(const CalDet* calDet, int type, CalPad* const outlierPad) double factor = 0.0; if (type == 0) factor = 1.0; - double arr[kNdet]; + double arr[MAXCHAMBER]; int n = 0; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet) factor = calDet->getValue(idet); CalROC* calRoc = fROC[idet]; @@ -272,9 +273,9 @@ double CalPad::getMedian(const CalDet* calDet, int type, CalPad* const outlierPa double factor = 0.0; if (type == 0) factor = 1.0; - double arr[kNdet]; + double arr[MAXCHAMBER]; int n = 0; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet) factor = calDet->getValue(idet); CalROC* calRoc = fROC[idet]; @@ -304,12 +305,12 @@ double CalPad::getLTM(double* sigma, double fraction, const CalDet* calDet, int double factor = 0.0; if (type == 0) factor = 1.0; - double arrm[kNdet]; - double arrs[kNdet]; + double arrm[MAXCHAMBER]; + double arrs[MAXCHAMBER]; double* sTemp = 0x0; int n = 0; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet) factor = calDet->getValue(idet); CalROC* calRoc = fROC[idet]; @@ -390,7 +391,7 @@ TH1F* CalPad::makeHisto1D(const CalDet* calDet, int typedet, float min, float ma char name[1000]; snprintf(name, 1000, "%s Pad 1D", getTitle()); TH1F* his = new TH1F(name, name, 100, min, max); - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet) factor = calDet->getValue(idet); if (fROC[idet]) { @@ -425,7 +426,7 @@ TH2F* CalPad::makeHisto2DSmPl(int sm, int pl, const CalDet* calDet, int typedet, float kEpsilon = 0.000000000001; - TRDGeometry* trdGeo = new TRDGeometry(); + Geometry* trdGeo = new Geometry(); if (type >= 0) { float kEpsilonr = 0.005; @@ -478,7 +479,7 @@ TH2F* CalPad::makeHisto2DSmPl(int sm, int pl, const CalDet* calDet, int typedet, // Where we begin int offsetsmpl = 30 * sm + pl; - for (int k = 0; k < kNcham; k++) { + for (int k = 0; k < NSTACK; k++) { int det = offsetsmpl + k * 6; if (calDet) factor = calDet->getValue(det); @@ -488,7 +489,7 @@ TH2F* CalPad::makeHisto2DSmPl(int sm, int pl, const CalDet* calDet, int typedet, for (int icol = 0; icol < calRoc->getNcols(); icol++) { if (TMath::Abs(calRoc->getValue(icol, irow)) > kEpsilon) { int binz = 0; - int kb = kNcham - 1 - k; + int kb = NSTACK - 1 - k; int krow = calRoc->getNrows() - 1 - irow; int kcol = calRoc->getNcols() - 1 - icol; if (kb > 2) @@ -570,7 +571,7 @@ TH2F* CalPad::makeHisto2DCh(int ch, const CalDet* calDet, int typedet, float min } } - TRDGeometry* trdGeo = new TRDGeometry(); + Geometry* trdGeo = new Geometry(); float kEpsilon = 0.000000000001; @@ -584,8 +585,8 @@ TH2F* CalPad::makeHisto2DCh(int ch, const CalDet* calDet, int typedet, float min // Where we begin int offsetch = 6 * ch; - for (int isec = 0; isec < kNsect; isec++) { - for (int ipl = 0; ipl < kNplan; ipl++) { + for (int isec = 0; isec < NSECTOR; isec++) { + for (int ipl = 0; ipl < NLAYER; ipl++) { int det = offsetch + isec * 30 + ipl; if (calDet) factor = calDet->getValue(det); @@ -635,7 +636,7 @@ bool CalPad::add(float c1) // bool result = kTRUE; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (fROC[idet]) { if (!fROC[idet]->add(c1)) result = kFALSE; @@ -651,7 +652,7 @@ bool CalPad::multiply(float c1) // multiply constant for all channels of all ROCs // bool result = kTRUE; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (fROC[idet]) { if (!fROC[idet]->multiply(c1)) result = kFALSE; @@ -680,7 +681,7 @@ bool CalPad::add(const CalPad* pad, double c1, const CalDet* calDet1, const CalD factor2 = 1.0; } bool result = kTRUE; - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet1) factor1 = calDet1->getValue(idet); if (calDet2) @@ -723,7 +724,7 @@ bool CalPad::multiply(const CalPad* pad, const CalDet* calDet1, const CalDet* ca factor1 = 1.0; factor2 = 1.0; } - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet1) factor1 = calDet1->getValue(idet); if (calDet2) @@ -773,7 +774,7 @@ bool CalPad::divide(const CalPad* pad, const CalDet* calDet1, const CalDet* calD factor1 = 1.0; factor2 = 1.0; } - for (int idet = 0; idet < kNdet; idet++) { + for (int idet = 0; idet < MAXCHAMBER; idet++) { if (calDet1) factor1 = calDet1->getValue(idet); if (calDet2) diff --git a/Detectors/TRD/base/src/CalPadStatus.cxx b/Detectors/TRD/base/src/CalPadStatus.cxx new file mode 100644 index 0000000000000..de82b5155ba33 --- /dev/null +++ b/Detectors/TRD/base/src/CalPadStatus.cxx @@ -0,0 +1,276 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/////////////////////////////////////////////////////////////////////////////// +// // +// TRD calibration class for the single pad status // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <TH1F.h> +#include <TH2F.h> +#include <TStyle.h> +#include <TCanvas.h> + +#include "TRDBase/GeometryBase.h" +#include "DetectorsCommonDataFormats/DetMatrixCache.h" +#include "DetectorsCommonDataFormats/DetID.h" + +#include "TRDBase/Geometry.h" +#include "TRDBase/PadPlane.h" +#include "TRDBase/CalSingleChamberStatus.h" // test +#include "TRDBase/CalPadStatus.h" + +using namespace o2::trd; +using namespace o2::trd::constants; + +//_____________________________________________________________________________ +CalPadStatus::CalPadStatus() +{ + // + // CalPadStatus default constructor + // + + for (int idet = 0; idet < MAXCHAMBER; idet++) { + mROC[idet] = nullptr; + } +} + +//_____________________________________________________________________________ +CalPadStatus::CalPadStatus(const Text_t* name, const Text_t* title) +{ + // + // CalPadStatus constructor + // + //Geometry fgeom; + for (int isec = 0; isec < NSECTOR; isec++) { + for (int ipla = 0; ipla < NLAYER; ipla++) { + for (int icha = 0; icha < NSTACK; icha++) { + int idet = o2::trd::Geometry::getDetector(ipla, icha, isec); + // int idet = fgeom.getDetector(ipla,icha,isec);//GeometryBase::getDetector(ipla,icha,isec); + mROC[idet] = new CalSingleChamberStatus(ipla, icha, 144); + } + } + } + mName = name; + mTitle = title; +} + +//_____________________________________________________________________________ +CalPadStatus::CalPadStatus(const CalPadStatus& c) +{ + // + // CalPadStatus copy constructor + // + + ((CalPadStatus&)c).Copy(*this); +} + +//_____________________________________________________________________________ +CalPadStatus::~CalPadStatus() +{ + // + // CalPadStatus destructor + // + + for (int idet = 0; idet < MAXCHAMBER; idet++) { + if (mROC[idet]) { + delete mROC[idet]; + mROC[idet] = nullptr; + } + } +} + +//_____________________________________________________________________________ +CalPadStatus& CalPadStatus::operator=(const CalPadStatus& c) +{ + // + // Assignment operator + // + + if (this != &c) + ((CalPadStatus&)c).Copy(*this); + return *this; +} + +//_____________________________________________________________________________ +void CalPadStatus::Copy(CalPadStatus& c) const +{ + // + // Copy function + // + + for (int idet = 0; idet < MAXCHAMBER; idet++) { + if (mROC[idet]) { + mROC[idet]->Copy(*((CalPadStatus&)c).mROC[idet]); + } + } +} + +//_____________________________________________________________________________ +Bool_t CalPadStatus::checkStatus(int d, int col, int row, int bitMask) const +{ + // + // Checks the pad status + // + + CalSingleChamberStatus* roc = getCalROC(d); + if (!roc) { + return kFALSE; + } else { + return (roc->getStatus(col, row) & bitMask) ? kTRUE : kFALSE; + } +} + +//_____________________________________________________________________________ +CalSingleChamberStatus* CalPadStatus::getCalROC(int p, int c, int s) const +{ + // + // Returns the readout chamber of this pad + // + //Geometry fgeom; + //return mROC[fgeom.getDetector(p,c,s)]; + return mROC[o2::trd::Geometry::getDetector(p, c, s)]; +} + +//_____________________________________________________________________________ +TH1F* CalPadStatus::makeHisto1D() +{ + // + // Make 1D histo + // + + char name[1000]; + snprintf(name, 1000, "%s Pad 1D", getTitle().c_str()); + TH1F* his = new TH1F(name, name, 6, -0.5, 5.5); + his->GetXaxis()->SetBinLabel(1, "Good"); + his->GetXaxis()->SetBinLabel(2, "Masked"); + his->GetXaxis()->SetBinLabel(3, "PadBridgedLeft"); + his->GetXaxis()->SetBinLabel(4, "PadBridgedRight"); + his->GetXaxis()->SetBinLabel(5, "ReadSecond"); + his->GetXaxis()->SetBinLabel(6, "NotConnected"); + + for (int idet = 0; idet < MAXCHAMBER; idet++) { + if (mROC[idet]) { + for (int ichannel = 0; ichannel < mROC[idet]->getNchannels(); ichannel++) { + int status = (int)mROC[idet]->getStatus(ichannel); + if (status == 2) + status = 1; + if (status == 4) + status = 2; + if (status == 8) + status = 3; + if (status == 16) + status = 4; + if (status == 32) + status = 5; + his->Fill(status); + } + } + } + + return his; +} + +//_____________________________________________________________________________ +TH2F* CalPadStatus::makeHisto2DSmPl(int sm, int pl) +{ + // + // Make 2D graph + // + + gStyle->SetPalette(1); + Geometry* trdGeo = new Geometry(); + const PadPlane* padPlane0 = trdGeo->getPadPlane(pl, 0); + Double_t row0 = padPlane0->getRow0(); + Double_t col0 = padPlane0->getCol0(); + + char name[1000]; + snprintf(name, 1000, "%s Pad 2D sm %d pl %d", getTitle().c_str(), sm, pl); + TH2F* his = new TH2F(name, name, 88, -TMath::Abs(row0), TMath::Abs(row0), 148, -TMath::Abs(col0), TMath::Abs(col0)); + + // Where we begin + int offsetsmpl = 30 * sm + pl; + + for (int k = 0; k < NSTACK; k++) { + int det = offsetsmpl + k * 6; + if (mROC[det]) { + CalSingleChamberStatus* calRoc = mROC[det]; + for (int icol = 0; icol < calRoc->getNcols(); icol++) { + for (int irow = 0; irow < calRoc->getNrows(); irow++) { + int binz = 0; + int kb = NSTACK - 1 - k; + int krow = calRoc->getNrows() - 1 - irow; + int kcol = calRoc->getNcols() - 1 - icol; + if (kb > 2) + binz = 16 * (kb - 1) + 12 + krow + 1 + 2 * (kb + 1); + else + binz = 16 * kb + krow + 1 + 2 * (kb + 1); + int biny = kcol + 1 + 2; + Float_t value = calRoc->getStatus(icol, irow); + his->SetBinContent(binz, biny, value); + } + } + for (int icol = 1; icol < 147; icol++) { + for (int l = 0; l < 2; l++) { + int binz = 0; + int kb = NSTACK - 1 - k; + if (kb > 2) + binz = 16 * (kb - 1) + 12 + 1 + 2 * (kb + 1) - (l + 1); + else + binz = 16 * kb + 1 + 2 * (kb + 1) - (l + 1); + his->SetBinContent(binz, icol, 50.0); + } + } + } + } + for (int icol = 1; icol < 147; icol++) { + his->SetBinContent(88, icol, 50.0); + his->SetBinContent(87, icol, 50.0); + } + for (int irow = 1; irow < 89; irow++) { + his->SetBinContent(irow, 1, 50.0); + his->SetBinContent(irow, 2, 50.0); + his->SetBinContent(irow, 147, 50.0); + his->SetBinContent(irow, 148, 50.0); + } + + his->SetXTitle("z (cm)"); + his->SetYTitle("y (cm)"); + his->SetMaximum(50); + his->SetMinimum(0.0); + his->SetStats(0); + + return his; +} + +//_____________________________________________________________________________ +void CalPadStatus::plotHistos2DSm(int sm, const char* name) +{ + // + // Make 2D graph + // + + gStyle->SetPalette(1); + TCanvas* c1 = new TCanvas(name, name, 50, 50, 600, 800); + c1->Divide(3, 2); + c1->cd(1); + makeHisto2DSmPl(sm, 0)->Draw("colz"); + c1->cd(2); + makeHisto2DSmPl(sm, 1)->Draw("colz"); + c1->cd(3); + makeHisto2DSmPl(sm, 2)->Draw("colz"); + c1->cd(4); + makeHisto2DSmPl(sm, 3)->Draw("colz"); + c1->cd(5); + makeHisto2DSmPl(sm, 4)->Draw("colz"); + c1->cd(6); + makeHisto2DSmPl(sm, 5)->Draw("colz"); +} diff --git a/Detectors/TRD/base/src/CalROC.cxx b/Detectors/TRD/base/src/CalROC.cxx index 4e9f0924e0d4d..7c52cba96daa3 100644 --- a/Detectors/TRD/base/src/CalROC.cxx +++ b/Detectors/TRD/base/src/CalROC.cxx @@ -247,9 +247,9 @@ bool CalROC::add(float c1) bool result = true; for (int idata = 0; idata < mNchannels; idata++) { - if (((getValue(idata) + c1) <= 6.5535) && ((getValue(idata) + c1) >= 0.0)) + if (((getValue(idata) + c1) <= 6.5535) && ((getValue(idata) + c1) >= 0.0)) { setValue(idata, getValue(idata) + c1); - else { + } else { setValue(idata, 0.0); result = false; } @@ -265,12 +265,13 @@ bool CalROC::multiply(float c1) // bool result = true; - if (c1 < 0) + if (c1 < 0) { return false; + } for (int idata = 0; idata < mNchannels; idata++) { - if ((getValue(idata) * c1) <= 6.5535) + if ((getValue(idata) * c1) <= 6.5535) { setValue(idata, getValue(idata) * c1); - else { + } else { setValue(idata, 0.0); result = false; } @@ -288,9 +289,9 @@ bool CalROC::add(const CalROC* roc, double c1) bool result = true; for (int idata = 0; idata < mNchannels; idata++) { if (((getValue(idata) + roc->getValue(idata) * c1) <= 6.5535) && - ((getValue(idata) + roc->getValue(idata) * c1) >= 0.0)) + ((getValue(idata) + roc->getValue(idata) * c1) >= 0.0)) { setValue(idata, getValue(idata) + roc->getValue(idata) * c1); - else { + } else { setValue(idata, 0.0); result = false; } @@ -307,9 +308,9 @@ bool CalROC::multiply(const CalROC* roc) bool result = true; for (int idata = 0; idata < mNchannels; idata++) { - if ((getValue(idata) * roc->getValue(idata)) <= 6.5535) + if ((getValue(idata) * roc->getValue(idata)) <= 6.5535) { setValue(idata, getValue(idata) * roc->getValue(idata)); - else { + } else { setValue(idata, 0.0); result = false; } @@ -328,9 +329,9 @@ bool CalROC::divide(const CalROC* roc) float eps = 0.00000000000000001; for (int idata = 0; idata < mNchannels; idata++) { if (TMath::Abs(roc->getValue(idata)) > eps) { - if ((getValue(idata) / roc->getValue(idata)) <= 6.5535) + if ((getValue(idata) / roc->getValue(idata)) <= 6.5535) { setValue(idata, getValue(idata) / roc->getValue(idata)); - else { + } else { setValue(idata, 0.0); result = false; } @@ -401,8 +402,9 @@ TH2F* CalROC::makeHisto2D(float min, float max, int type, float mu) float sigma = getRMS(); float nsigma = TMath::Abs(min); sigma *= nsigma; - if (sigma < epsr) + if (sigma < epsr) { sigma = epsr; + } min = mean - sigma; max = mean + sigma; } @@ -417,8 +419,9 @@ TH2F* CalROC::makeHisto2D(float min, float max, int type, float mu) double sigma; float mean = getLTM(&sigma, max); sigma *= min; - if (sigma < epsr) + if (sigma < epsr) { sigma = epsr; + } min = mean - sigma; max = mean + sigma; } @@ -455,8 +458,9 @@ TH1F* CalROC::makeHisto1D(float min, float max, int type, float mu) float sigma = getRMS(); float nsigma = TMath::Abs(min); sigma *= nsigma; - if (sigma < epsr) + if (sigma < epsr) { sigma = epsr; + } min = mean - sigma; max = mean + sigma; } @@ -474,8 +478,9 @@ TH1F* CalROC::makeHisto1D(float min, float max, int type, float mu) double sigma; float mean = getLTM(&sigma, max); sigma *= min; - if (sigma < epsr) + if (sigma < epsr) { sigma = epsr; + } min = mean - sigma; max = mean + sigma; } diff --git a/Detectors/TRD/base/src/CalSingleChamberStatus.cxx b/Detectors/TRD/base/src/CalSingleChamberStatus.cxx new file mode 100644 index 0000000000000..f74a3817df95f --- /dev/null +++ b/Detectors/TRD/base/src/CalSingleChamberStatus.cxx @@ -0,0 +1,156 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/////////////////////////////////////////////////////////////////////////////// +// // +// Calibration base class for a single ROC // +// Contains one char value per pad // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "TRDBase/CalSingleChamberStatus.h" +#include <TMath.h> +#include <Rtypes.h> +#include "TRDBase/CommonParam.h" + +using namespace o2::trd; + +//_____________________________________________________________________________ +CalSingleChamberStatus::CalSingleChamberStatus() = default; + +//_____________________________________________________________________________ +CalSingleChamberStatus::CalSingleChamberStatus(Int_t p, Int_t c, Int_t cols) + : mPla(p), mCha(c), mNcols(cols) +{ + // + // Constructor that initializes a given pad plane type + // + + // + // The pad plane parameter + // + switch (p) { + case 0: + if (c == 2) { + // L0C0 type + mNrows = 12; + } else { + // L0C1 type + mNrows = 16; + } + break; + case 1: + if (c == 2) { + // L1C0 type + mNrows = 12; + } else { + // L1C1 type + mNrows = 16; + } + break; + case 2: + if (c == 2) { + // L2C0 type + mNrows = 12; + } else { + // L2C1 type + mNrows = 16; + } + break; + case 3: + if (c == 2) { + // L3C0 type + mNrows = 12; + } else { + // L3C1 type + mNrows = 16; + } + break; + case 4: + if (c == 2) { + // L4C0 type + mNrows = 12; + } else { + // L4C1 type + mNrows = 16; + } + break; + case 5: + if (c == 2) { + // L5C0 type + mNrows = 12; + } else { + // L5C1 type + mNrows = 16; + } + break; + }; + + mNchannels = mNrows * mNcols; + if (mNchannels != 0) { + mData.resize(mNchannels); + } + memset(&mData[0], 0, sizeof(mData[0]) * mData.size()); +} + +//_____________________________________________________________________________ +CalSingleChamberStatus::CalSingleChamberStatus(const CalSingleChamberStatus& c) + : mPla(c.mPla), mCha(c.mCha), mNrows(c.mNrows), mNcols(c.mNcols), mNchannels(c.mNchannels) +{ + // + // CalSingleChamberStatus copy constructor + // + + mData = c.mData; +} + +//_____________________________________________________________________________ +CalSingleChamberStatus::~CalSingleChamberStatus() = default; + +//_____________________________________________________________________________ +CalSingleChamberStatus& CalSingleChamberStatus::operator=(const CalSingleChamberStatus& c) +{ + // + // Assignment operator + // + + if (this == &c) { + return *this; + } + + mPla = c.mPla; + mCha = c.mCha; + mNrows = c.mNrows; + mNcols = c.mNcols; + mNchannels = c.mNchannels; + mData = c.mData; + + return *this; +} + +//_____________________________________________________________________________ +void CalSingleChamberStatus::Copy(CalSingleChamberStatus& c) const +{ + // + // Copy function + // + + Int_t iBin = 0; + + c.mPla = mPla; + c.mCha = mCha; + + c.mNrows = mNrows; + c.mNcols = mNcols; + + c.mNchannels = mNchannels; + + c.mData = mData; +} diff --git a/Detectors/TRD/base/src/Calibrations.cxx b/Detectors/TRD/base/src/Calibrations.cxx index 0ef78cd985e6d..fe251323adfc6 100644 --- a/Detectors/TRD/base/src/Calibrations.cxx +++ b/Detectors/TRD/base/src/Calibrations.cxx @@ -18,7 +18,7 @@ #include <sstream> #include <string> -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/Calibrations.h" #include "TRDBase/ChamberCalibrations.h" #include "TRDBase/ChamberNoise.h" @@ -48,17 +48,42 @@ void Calibrations::setCCDB(int calibrationobjecttype, long timestamp) switch (calibrationobjecttype) { case 1: //simulation mChamberCalibrations = ccdbmgr.get<o2::trd::ChamberCalibrations>("TRD_test/ChamberCalibrations"); + if (!mChamberCalibrations) { + LOG(fatal) << "No chamber calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mLocalVDrift = ccdbmgr.get<o2::trd::LocalVDrift>("TRD_test/LocalVDrift"); + if (!mLocalVDrift) { + LOG(fatal) << "No Local V Drift calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mLocalT0 = ccdbmgr.get<o2::trd::LocalT0>("TRD_test/LocalT0"); + if (!mLocalT0) { + LOG(fatal) << "No Local T0 calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mLocalGainFactor = ccdbmgr.get<o2::trd::LocalGainFactor>("TRD_test/LocalGainFactor"); + if (!mLocalT0) { + LOG(fatal) << "No Local T0 calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mPadNoise = ccdbmgr.get<o2::trd::PadNoise>("TRD_test/PadNoise"); + if (!mPadNoise) { + LOG(fatal) << "No Padnoise calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mChamberStatus = ccdbmgr.get<o2::trd::ChamberStatus>("TRD_test/ChamberStatus"); + if (!mChamberStatus) { + LOG(fatal) << "No ChamberStatus calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mPadStatus = ccdbmgr.get<o2::trd::PadStatus>("TRD_test/PadStatus"); + if (!mPadStatus) { + LOG(fatal) << "No Pad Status calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } mChamberNoise = ccdbmgr.get<o2::trd::ChamberNoise>("TRD_test/ChamberNoise"); + if (!mChamberNoise) { + LOG(fatal) << "No ChamberNoise calibrations returned from CCDB for TRD calibrations, or TRD from what ever you are running."; + } //mCalOnlineGainTables= ccdbmgr.get<o2::trd::CalOnlineGainTables>("TRD_test/OnlineGainTables"); //std::shared_ptr<TrapConfig> mTrapConfig; //std::shared_ptr<PRFWidth> mPRDWidth //std::shared_ptr<CalOnlineGainTables> mCalOnlineGainTables; + break; case 2: //reconstruction LOG(fatal) << "Reconstruction calibration not implemented yet "; @@ -84,62 +109,69 @@ void Calibrations::setOnlineGainTables(std::string& tablename) double Calibrations::getVDrift(int det, int col, int row) const { - if (mChamberCalibrations && mLocalVDrift) + if (mChamberCalibrations && mLocalVDrift) { return (double)mChamberCalibrations->getVDrift(det) * (double)mLocalVDrift->getValue(det, col, row); - else + } else { return -1; + } } double Calibrations::getT0(int det, int col, int row) const { - if (mChamberCalibrations && mLocalT0) + if (mChamberCalibrations && mLocalT0) { return (double)mChamberCalibrations->getT0(det) + (double)mLocalT0->getValue(det, col, row); - else { + } else { return -1; } } double Calibrations::getExB(int det) const { - if (mChamberCalibrations) + if (mChamberCalibrations) { return (double)mChamberCalibrations->getExB(det); - else + } else { return -1; + } } double Calibrations::getGainFactor(int det, int col, int row) const { - if (mChamberCalibrations && mLocalGainFactor) + if (mChamberCalibrations && mLocalGainFactor) { return (double)mChamberCalibrations->getGainFactor(det) * (double)mLocalGainFactor->getValue(det, col, row); - else + } else { return -1; + } } double Calibrations::getPadGainFactor(int det, int col, int row) const { - if (mLocalGainFactor) + if (mLocalGainFactor) { return (double)mLocalGainFactor->getValue(det, col, row); - else + } else { return -1; + } } double Calibrations::getOnlineGainAdcdac(int det, int row, int mcm) const { - if (mCalOnlineGainTables) + if (mCalOnlineGainTables) { return mCalOnlineGainTables->getAdcdacrm(det, row, mcm); - else + } else { return -1; + } } double Calibrations::getOnlineGainFGAN(int det, int rob, int mcm, int adc) const { - if (mCalOnlineGainTables) + if (mCalOnlineGainTables) { return mCalOnlineGainTables->getFGANrm(det, rob, mcm, adc); - else + } else { return -1; + } } double Calibrations::getOnlineGainFGFN(int det, int rob, int mcm, int adc) const { - if (mCalOnlineGainTables) + if (mCalOnlineGainTables) { return mCalOnlineGainTables->getFGFNrm(det, rob, mcm, adc); - else + } else { return -1; + } } diff --git a/Detectors/TRD/base/src/ChamberNoise.cxx b/Detectors/TRD/base/src/ChamberNoise.cxx index 9145783b0ca7e..f1e48b53fd5db 100644 --- a/Detectors/TRD/base/src/ChamberNoise.cxx +++ b/Detectors/TRD/base/src/ChamberNoise.cxx @@ -16,7 +16,7 @@ #include <array> -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/ChamberNoise.h" #include "TH2D.h" diff --git a/Detectors/TRD/base/src/ChamberStatus.cxx b/Detectors/TRD/base/src/ChamberStatus.cxx index f7694bb8c1c6b..46e0a1a83ced0 100644 --- a/Detectors/TRD/base/src/ChamberStatus.cxx +++ b/Detectors/TRD/base/src/ChamberStatus.cxx @@ -130,11 +130,13 @@ TH2D* ChamberStatus::plot(int sm, int rphi) int status = getStatus(i); h2->Fill(stackn, layer, status); if (rphi == 0) { - if (!(mStatus[i] & kNoDataHalfChamberSideBpat)) + if (!(mStatus[i] & kNoDataHalfChamberSideBpat)) { h2->Fill(stackn, layer, status); + } } else if (rphi == 1) { - if (!(mStatus[i] & kNoDataHalfChamberSideApat)) + if (!(mStatus[i] & kNoDataHalfChamberSideApat)) { h2->Fill(stackn, layer, status); + } } } @@ -161,15 +163,19 @@ TH2D* ChamberStatus::plotNoData(int sm, int rphi) int layer = i % 6; int stackn = static_cast<int>((i - start) / 6.); if (rphi == 0) { - if (mStatus[i] & kNoDataHalfChamberSideBpat) + if (mStatus[i] & kNoDataHalfChamberSideBpat) { h2->Fill(stackn, layer, 1); - if (mStatus[i] & kNoDatapat) + } + if (mStatus[i] & kNoDatapat) { h2->Fill(stackn, layer, 1); + } } else if (rphi == 1) { - if (!(mStatus[i] & kNoDataHalfChamberSideApat)) + if (!(mStatus[i] & kNoDataHalfChamberSideApat)) { h2->Fill(stackn, layer, 1); - if (!(mStatus[i] & kNoDatapat)) + } + if (!(mStatus[i] & kNoDatapat)) { h2->Fill(stackn, layer, 1); + } } } @@ -196,11 +202,13 @@ TH2D* ChamberStatus::plotBadCalibrated(int sm, int rphi) int layer = i % 6; int stackn = static_cast<int>((i - start) / 6.); if (rphi == 0) { - if (mStatus[i] & kBadCalibratedpat) + if (mStatus[i] & kBadCalibratedpat) { h2->Fill(stackn, layer, 1); + } } else if (rphi == 1) { - if (mStatus[i] & kBadCalibratedpat) + if (mStatus[i] & kBadCalibratedpat) { h2->Fill(stackn, layer, 1); + } } } diff --git a/Detectors/TRD/base/src/CommonParam.cxx b/Detectors/TRD/base/src/CommonParam.cxx new file mode 100644 index 0000000000000..c8fe591d0536d --- /dev/null +++ b/Detectors/TRD/base/src/CommonParam.cxx @@ -0,0 +1,745 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 containing parameters common to simulation and reconstruction // +// // +// Request an instance with AliTRDCommonParam::Instance() // +// Then request the needed values // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <TGeoGlobalMagField.h> +#include <TMath.h> + +#include <FairLogger.h> +#include "TRDBase/CommonParam.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/SimParam.h" + +#include "Field/MagneticField.h" + +using namespace o2::trd; +ClassImp(CommonParam); + +CommonParam* CommonParam::fgInstance = nullptr; +bool CommonParam::fgTerminated = false; + +//_ singleton implementation __________________________________________________ +CommonParam* CommonParam::Instance() +{ + // + // Singleton implementation + // Returns an instance of this class, it is created if neccessary + // + + if (fgTerminated != false) { + return nullptr; + } + + if (fgInstance == nullptr) { + fgInstance = new CommonParam(); + } + + return fgInstance; +} + +//_____________________________________________________________________________ +void CommonParam::Terminate() +{ + // + // Singleton implementation + // Deletes the instance of this class and sets the terminated flag, + // instances cannot be requested anymore + // This function can be called several times. + // + + fgTerminated = true; + + if (fgInstance != nullptr) { + delete fgInstance; + fgInstance = nullptr; + } +} + +//_____________________________________________________________________________ +CommonParam::CommonParam() + : mExBOn(true), + mDiffusionT(0.0), + mDiffusionL(0.0), + mDiffLastVdrift(-1.0), + mTimeStruct1(nullptr), + mTimeStruct2(nullptr), + mVDlo(0.0), + mVDhi(0.0), + mTimeLastVdrift(-1.0), + mSamplingFrequency(10.0), + mGasMixture(kXenon), + mField(-0.5) +{ + // + // Default constructor + // +} + +//_____________________________________________________________________________ +CommonParam::~CommonParam() +{ + // + // Destructor + // + if (mTimeStruct1) { + delete[] mTimeStruct1; + mTimeStruct1 = nullptr; + } + if (mTimeStruct2) { + delete[] mTimeStruct2; + mTimeStruct2 = nullptr; + } +} + +//_____________________________________________________________________________ +bool CommonParam::cacheMagField() +{ + // The magnetic field strength + const o2::field::MagneticField* fld = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); + if (!fld) { + LOG(FATAL) << "Magnetic field is not initialized!"; + return false; + } + mField = 0.1 * fld->solenoidField(); // kGauss -> Tesla + return true; +} + +//_____________________________________________________________________________ +float CommonParam::GetOmegaTau(float vdrift) +{ + /* + // + // Returns omega*tau (tan(Lorentz-angle)) for a given drift + // velocity <vdrift> for Xe/CO2 (15%). + // The values are according to a GARFIELD simulation. + // + + AliMagF* fld = (AliMagF *) TGeoGlobalMagField::Instance()->GetField(); + if (!fld) { + return 0.0; + } + double bz = 0.1 * fld->SolenoidField(); // kGauss -> Tesla + double fieldAbs = TMath::Abs(bz); + double fieldSgn = (bz > 0.0) ? 1.0 : -1.0; + + const int kNb = 5; + float p0[kNb] = { 0.004810, 0.007412, 0.010252, 0.013409, 0.016888 }; + float p1[kNb] = { 0.054875, 0.081534, 0.107333, 0.131983, 0.155455 }; + float p2[kNb] = { -0.008682, -0.012896, -0.016987, -0.020880, -0.024623 }; + float p3[kNb] = { 0.000155, 0.000238, 0.000330, 0.000428, 0.000541 }; + + // No ExB if field is too small (or zero) + if (fieldAbs < 0.01) { + + return 0.0; + + } + // Calculate ExB from parametrization + else if (IsXenon()) { + + int ib = ((int) (10 * (fieldAbs - 0.15))); + ib = TMath::Max( 0,ib); + ib = TMath::Min(kNb-1,ib); + + float alphaL = p0[ib] + + p1[ib] * vdrift + + p2[ib] * vdrift*vdrift + + p3[ib] * vdrift*vdrift*vdrift; + + return TMath::Tan(fieldSgn * alphaL); + + } + else if (IsArgon()) { + + return 0.0219769; + + } + + return 0.0; + */ + return 0.; // fix compiler warning +} + +//_____________________________________________________________________________ +bool CommonParam::GetDiffCoeff(float& dl, float& dt, float vdrift) +{ + // + // Calculates the diffusion coefficients in longitudinal <dl> and + // transverse <dt> direction for a given drift velocity <vdrift> + // + + // Nothing to do + if (TMath::Abs(mDiffLastVdrift - vdrift) < 0.001) { + dl = mDiffusionL; + dt = mDiffusionT; + return true; + } + mDiffLastVdrift = vdrift; + + if (IsXenon()) { + // + // Vd and B-field dependent diffusion and Lorentz angle + // + + // kNb = kNb = kNb + constexpr int kNb = 5; + + // If looking at compatibility with AliRoot: + // ibL and ibT are calculated the same way so, just define ib = ibL = ibT + int ib = ((int)(10 * (mField - 0.15))); + ib = TMath::Max(0, ib); + ib = TMath::Min(kNb - 1, ib); + + // DiffusionT + constexpr float p0T[kNb] = {0.009550, 0.009599, 0.009674, 0.009757, 0.009850}; + constexpr float p1T[kNb] = {0.006667, 0.006539, 0.006359, 0.006153, 0.005925}; + constexpr float p2T[kNb] = {-0.000853, -0.000798, -0.000721, -0.000635, -0.000541}; + constexpr float p3T[kNb] = {0.000131, 0.000122, 0.000111, 0.000098, 0.000085}; + // DiffusionL + constexpr float p0L[kNb] = {0.007440, 0.007493, 0.007513, 0.007672, 0.007831}; + constexpr float p1L[kNb] = {0.019252, 0.018912, 0.018636, 0.018012, 0.017343}; + constexpr float p2L[kNb] = {-0.005042, -0.004926, -0.004867, -0.004650, -0.004424}; + constexpr float p3L[kNb] = {0.000195, 0.000189, 0.000195, 0.000182, 0.000169}; + + float v2 = vdrift * vdrift; + float v3 = v2 * vdrift; + mDiffusionL = p0L[ib] + p1L[ib] * vdrift + p2L[ib] * v2 + p3L[ib] * v3; + mDiffusionT = p0T[ib] + p1T[ib] * vdrift + p2T[ib] * v2 + p3T[ib] * v3; + + dl = mDiffusionL; + dt = mDiffusionT; + return true; + } else if (IsArgon()) { + // + // Diffusion constants and Lorentz angle only for B = 0.5T + // + mDiffusionL = 0.0182; + mDiffusionT = 0.0159; + dl = mDiffusionL; + dt = mDiffusionT; + return true; + } else { + return false; + } +} + +//_____________________________________________________________________________ +double CommonParam::TimeStruct(float vdrift, double dist, double z) +{ + // + // Applies the time structure of the drift cells (by C.Lippmann). + // The drift time of electrons to the anode wires depends on the + // distance to the wire (z) and on the position in the drift region. + // + // input : + // dist = radial distance from (cathode) pad plane [cm] + // z = distance from anode wire (parallel to cathode planes) [cm] + // + // output : + // tdrift = the drift time of an electron at the given position + // + // We interpolate between the drift time values at the two drift + // velocities fVDlo and fVDhi, being smaller and larger than + // fDriftVelocity. We use the two stored drift time maps fTimeStruct1 + // and fTimeStruct2, calculated for the two mentioned drift velocities. + // + + SampleTimeStruct(vdrift); + + // Indices: + int r1 = (int)(10 * dist); + if (r1 < 0) { + r1 = 0; + } + if (r1 > 37) { + r1 = 37; + } + int r2 = r1 + 1; + if (r2 > 37) { + r2 = 37; + } + const int kz1 = ((int)(100 * z / 2.5)); + const int kz2 = kz1 + 1; + + if ((r1 < 0) || (r1 > 37) || (kz1 < 0) || (kz1 > 10)) { + LOG(INFO) << Form("Indices out of range: dist=%.2f, z=%.2f, r1=%d, kz1=%d", dist, z, r1, kz1); + } + + const float ky111 = mTimeStruct1[r1 + 38 * kz1]; + const float ky221 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct1[r2 + 38 * kz2] : mTimeStruct1[37 + 38 * 10]; + const float ky121 = (kz2 <= 10) ? mTimeStruct1[r1 + 38 * kz2] : mTimeStruct1[r1 + 38 * 10]; + const float ky211 = mTimeStruct1[r2 + 38 * kz1]; + + const float ky112 = mTimeStruct2[r1 + 38 * kz1]; + const float ky222 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct2[r2 + 38 * kz2] : mTimeStruct2[37 + 38 * 10]; + const float ky122 = (kz2 <= 10) ? mTimeStruct2[r1 + 38 * kz2] : mTimeStruct2[r1 + 38 * 10]; + const float ky212 = mTimeStruct2[r2 + 38 * kz1]; + + // Interpolation in dist-directions, lower drift time map + const float ky11 = (ky211 - ky111) * 10 * dist + ky111 - (ky211 - ky111) * r1; + const float ky21 = (ky221 - ky121) * 10 * dist + ky121 - (ky221 - ky121) * r1; + + // Interpolation in dist-direction, larger drift time map + const float ky12 = (ky212 - ky112) * 10 * dist + ky112 - (ky212 - ky112) * r1; + const float ky22 = (ky222 - ky122) * 10 * dist + ky122 - (ky222 - ky122) * r1; + + // Dist now is the drift distance to the anode wires (negative if electrons are + // between anode wire plane and cathode pad plane) + dist -= Geometry::amThick() / 2.0; + + // Interpolation in z-directions, lower drift time map + const float ktdrift1 = + ((TMath::Abs(dist) > 0.005) || (z > 0.005)) ? (ky21 - ky11) * 100 * z / 2.5 + ky11 - (ky21 - ky11) * kz1 : 0.0; + // Interpolation in z-directions, larger drift time map + const float ktdrift2 = + ((TMath::Abs(dist) > 0.005) || (z > 0.005)) ? (ky22 - ky12) * 100 * z / 2.5 + ky12 - (ky22 - ky12) * kz1 : 0.0; + + // Interpolation between the values at fVDlo and fVDhi + float a = (ktdrift2 - ktdrift1) / (mVDhi - mVDlo); + float b = ktdrift2 - a * mVDhi; + float t = a * vdrift + b; + + return t; +} + +//_____________________________________________________________________________ +void CommonParam::SampleTimeStruct(float vdrift) +{ + // + // Samples the timing structure of a drift cell + // Drift Time data calculated with Garfield (by C.Lippmann) + // + + // Nothing to do + if (TMath::Abs(mTimeLastVdrift - vdrift) < 1.e-3) { + return; + } + mTimeLastVdrift = vdrift; + + // Drift time maps are saved for some drift velocity values (in drift region): + float fVDsmp[8]; + fVDsmp[0] = 1.032; + fVDsmp[1] = 1.158; + fVDsmp[2] = 1.299; + fVDsmp[3] = 1.450; + fVDsmp[4] = 1.610; + fVDsmp[5] = 1.783; + fVDsmp[6] = 1.959; + fVDsmp[7] = 2.134; + + if (vdrift < fVDsmp[0]) { + LOG(INFO) << Form("Drift Velocity too small (%.3f<%.3f)", vdrift, fVDsmp[0]); + vdrift = fVDsmp[0]; + } else if (vdrift > fVDsmp[7]) { + LOG(INFO) << Form("Drift Velocity too large (%.3f>%.3f)", vdrift, fVDsmp[6]); + vdrift = fVDsmp[7]; + } + + const int ktimebin = 38; + const int kZbin = 11; + + // Garfield simulation at UD = -1500V; vd = 1.032cm/microsec, <driftfield> = 525V/cm + float time1500[ktimebin][kZbin] = { + {0.09170, 0.09205, 0.09306, 0.09475, 0.09716, 0.10035, 0.10445, 0.10993, 0.11838, 0.13986, 0.37858}, + {0.06588, 0.06626, 0.06739, 0.06926, 0.07186, 0.07524, 0.07951, 0.08515, 0.09381, 0.11601, 0.35673}, + {0.03946, 0.04003, 0.04171, 0.04435, 0.04780, 0.05193, 0.05680, 0.06306, 0.07290, 0.09873, 0.34748}, + {0.01151, 0.01283, 0.01718, 0.02282, 0.02880, 0.03479, 0.04098, 0.04910, 0.06413, 0.10567, 0.36897}, + {0.01116, 0.01290, 0.01721, 0.02299, 0.02863, 0.03447, 0.04074, 0.04984, 0.06839, 0.11625, 0.37277}, + {0.03919, 0.03974, 0.04131, 0.04380, 0.04703, 0.05102, 0.05602, 0.06309, 0.07651, 0.10938, 0.36838}, + {0.06493, 0.06560, 0.06640, 0.06802, 0.07051, 0.07392, 0.07853, 0.08510, 0.09690, 0.12621, 0.38058}, + {0.09174, 0.09186, 0.09225, 0.09303, 0.09477, 0.00000, 0.11205, 0.11952, 0.13461, 0.16984, 0.43017}, + {0.14356, 0.14494, 0.14959, 0.16002, 0.18328, 0.27981, 0.22785, 0.21240, 0.21948, 0.25965, 0.52392}, + {0.23120, 0.23366, 0.24046, 0.25422, 0.28071, 0.36914, 0.32999, 0.31208, 0.31772, 0.35804, 0.62249}, + {0.32686, 0.32916, 0.33646, 0.35053, 0.37710, 0.46292, 0.42773, 0.40948, 0.41497, 0.45527, 0.71955}, + {0.42353, 0.42583, 0.43317, 0.44727, 0.47380, 0.55884, 0.52479, 0.50650, 0.51194, 0.55225, 0.81658}, + {0.52038, 0.52271, 0.53000, 0.54415, 0.57064, 0.65545, 0.62172, 0.60341, 0.60885, 0.64915, 0.91339}, + {0.61724, 0.61953, 0.62694, 0.64098, 0.66756, 0.75226, 0.71862, 0.70030, 0.70575, 0.74604, 1.01035}, + {0.71460, 0.71678, 0.72376, 0.73786, 0.76447, 0.84913, 0.81551, 0.79720, 0.80264, 0.84292, 1.10723}, + {0.81101, 0.81334, 0.82066, 0.83475, 0.86127, 0.94599, 0.91240, 0.89408, 0.89952, 0.93981, 1.20409}, + {0.90788, 0.91023, 0.91752, 0.93163, 0.95815, 1.04293, 1.00929, 0.99096, 0.99640, 1.03669, 1.30106}, + {1.00477, 1.00707, 1.01449, 1.02852, 1.05504, 1.13976, 1.10617, 1.08784, 1.09329, 1.13358, 1.39796}, + {1.10166, 1.10397, 1.11130, 1.12541, 1.15257, 1.23672, 1.20307, 1.18472, 1.19018, 1.23046, 1.49486}, + {1.19854, 1.20084, 1.20818, 1.22235, 1.24885, 1.33355, 1.29992, 1.28156, 1.28707, 1.32735, 1.59177}, + {1.29544, 1.29780, 1.30507, 1.31917, 1.34575, 1.43073, 1.39681, 1.37851, 1.38396, 1.42377, 1.68854}, + {1.39236, 1.39462, 1.40205, 1.41607, 1.44259, 1.52745, 1.49368, 1.47541, 1.48083, 1.52112, 1.78546}, + {1.49314, 1.49149, 1.49885, 1.51297, 1.53949, 1.62420, 1.59016, 1.57231, 1.57772, 1.61800, 1.88048}, + {1.58610, 1.58839, 1.59572, 1.60983, 1.63635, 1.72109, 1.68651, 1.66921, 1.67463, 1.71489, 1.97918}, + {1.68400, 1.68529, 1.69261, 1.70671, 1.73331, 1.81830, 1.78341, 1.76605, 1.77150, 1.81179, 2.07608}, + {1.77991, 1.78215, 1.78952, 1.80385, 1.83014, 1.91486, 1.88128, 1.86215, 1.86837, 1.90865, 2.17304}, + {1.87674, 1.87904, 1.88647, 1.90052, 1.92712, 2.01173, 1.97812, 1.95905, 1.96527, 2.00710, 2.26979}, + {1.97369, 1.97594, 1.98326, 1.99869, 2.02442, 2.10865, 2.07501, 2.05666, 2.06214, 2.10243, 2.36669}, + {2.07052, 2.07281, 2.08016, 2.09425, 2.12132, 2.20555, 2.17182, 2.15341, 2.15904, 2.19933, 2.46363}, + {2.16742, 2.16971, 2.17707, 2.19114, 2.21766, 2.30240, 2.26877, 2.25015, 2.25573, 2.29586, 2.56060}, + {2.26423, 2.26659, 2.27396, 2.28803, 2.31456, 2.40828, 2.36567, 2.34705, 2.35282, 2.39765, 2.65744}, + {2.36153, 2.36349, 2.37330, 2.38501, 2.41159, 2.49940, 2.46257, 2.44420, 2.44843, 2.48987, 2.75431}, + {2.46558, 2.46035, 2.46822, 2.48181, 2.50849, 2.59630, 2.55947, 2.54112, 2.54513, 2.58677, 2.85094}, + {2.56248, 2.55723, 2.56486, 2.57871, 2.60520, 2.68998, 2.65626, 2.63790, 2.64316, 2.68360, 2.94813}, + {2.65178, 2.65441, 2.66153, 2.67556, 2.70210, 2.78687, 2.75319, 2.73463, 2.74032, 2.78060, 3.04503}, + {2.74868, 2.75131, 2.75870, 2.77245, 2.79385, 2.88700, 2.85009, 2.83177, 2.83723, 2.87750, 3.14193}, + {2.84574, 2.84789, 2.85560, 2.86935, 2.89075, 2.98060, 2.94576, 2.92868, 2.93356, 2.97436, 3.23868}, + {2.94239, 2.94469, 2.95221, 2.96625, 2.99345, 3.07747, 3.04266, 3.02545, 3.03051, 3.07118, 3.33555}}; + + // Garfield simulation at UD = -1600V; vd = 1.158cm/microsec, <driftfield> = 558V/cm + float time1600[ktimebin][kZbin] = { + {0.09169, 0.09203, 0.09305, 0.09473, 0.09714, 0.10032, 0.10441, 0.10990, 0.11835, 0.13986, 0.37845}, + {0.06589, 0.06626, 0.06738, 0.06924, 0.07184, 0.07521, 0.07947, 0.08512, 0.09379, 0.11603, 0.35648}, + {0.03947, 0.04003, 0.04171, 0.04434, 0.04778, 0.05190, 0.05678, 0.06304, 0.07292, 0.09876, 0.34759}, + {0.01111, 0.01281, 0.01718, 0.02281, 0.02879, 0.03477, 0.04097, 0.04910, 0.06415, 0.10573, 0.36896}, + {0.01120, 0.01311, 0.01721, 0.02279, 0.02862, 0.03446, 0.04074, 0.04981, 0.06825, 0.11595, 0.37255}, + {0.03919, 0.03980, 0.04132, 0.04380, 0.04704, 0.05102, 0.05602, 0.06302, 0.07633, 0.10896, 0.36743}, + {0.06531, 0.06528, 0.06631, 0.06805, 0.07053, 0.07392, 0.07853, 0.08505, 0.09669, 0.12578, 0.37967}, + {0.09157, 0.09171, 0.09216, 0.09301, 0.09475, 0.00000, 0.11152, 0.11879, 0.13352, 0.16802, 0.42750}, + {0.13977, 0.14095, 0.14509, 0.15433, 0.17534, 0.26406, 0.21660, 0.20345, 0.21113, 0.25067, 0.51434}, + {0.21816, 0.22041, 0.22631, 0.23850, 0.26208, 0.34340, 0.30755, 0.29237, 0.29878, 0.33863, 0.60258}, + {0.30344, 0.30547, 0.31241, 0.32444, 0.34809, 0.42696, 0.39464, 0.37919, 0.38546, 0.42530, 0.68926}, + {0.38969, 0.39164, 0.39810, 0.41059, 0.43441, 0.51246, 0.48112, 0.46562, 0.47191, 0.51172, 0.77558}, + {0.47592, 0.47799, 0.48442, 0.49689, 0.52061, 0.59855, 0.56752, 0.55201, 0.55826, 0.59808, 0.86202}, + {0.56226, 0.56428, 0.57074, 0.58324, 0.60696, 0.68483, 0.65388, 0.63837, 0.64461, 0.68445, 0.94830}, + {0.64881, 0.65063, 0.65709, 0.66958, 0.69331, 0.77117, 0.74023, 0.72472, 0.73098, 0.77079, 1.03486}, + {0.73506, 0.73698, 0.74344, 0.75596, 0.77964, 0.85751, 0.82658, 0.81107, 0.81731, 0.85712, 1.12106}, + {0.82132, 0.82333, 0.82979, 0.84228, 0.86608, 0.94386, 0.91293, 0.89742, 0.90367, 0.94335, 1.20737}, + {0.90767, 0.90968, 0.91614, 0.92863, 0.95236, 1.03021, 0.99928, 0.98377, 0.99001, 1.02984, 1.29371}, + {0.99410, 0.99602, 1.00257, 1.01498, 1.03869, 1.11720, 1.08563, 1.07011, 1.07637, 1.11621, 1.37873}, + {1.08036, 1.08240, 1.08884, 1.10138, 1.12504, 1.20301, 1.17198, 1.15647, 1.16272, 1.20255, 1.46651}, + {1.16670, 1.16872, 1.17525, 1.18783, 1.21139, 1.28934, 1.25833, 1.24281, 1.24909, 1.28889, 1.55275}, + {1.25307, 1.25510, 1.26153, 1.27404, 1.29773, 1.37584, 1.34469, 1.32916, 1.33536, 1.37524, 1.63915}, + {1.33942, 1.34146, 1.34788, 1.36040, 1.38410, 1.46438, 1.43105, 1.41537, 1.42176, 1.46158, 1.72538}, + {1.42579, 1.42782, 1.43458, 1.44674, 1.47043, 1.55085, 1.51675, 1.50168, 1.50810, 1.54793, 1.81174}, + {1.51207, 1.51454, 1.52060, 1.53307, 1.55684, 1.63478, 1.60336, 1.58820, 1.59446, 1.63414, 1.89814}, + {1.59856, 1.60047, 1.60693, 1.61942, 1.64317, 1.72257, 1.69008, 1.67454, 1.68080, 1.72063, 1.98433}, + {1.68481, 1.68682, 1.69330, 1.70584, 1.72949, 1.80752, 1.77643, 1.76089, 1.76716, 1.80692, 2.07069}, + {1.77117, 1.77319, 1.77969, 1.79260, 1.81583, 1.89376, 1.86226, 1.84720, 1.85355, 1.89256, 2.15343}, + {1.85748, 1.85967, 1.86605, 1.87848, 1.90222, 1.98010, 1.94913, 1.93271, 1.93981, 1.97968, 2.24355}, + {1.94386, 1.94587, 1.95233, 1.96484, 1.98854, 2.06646, 2.03542, 2.01755, 2.02617, 2.06604, 2.32993}, + {2.03022, 2.03230, 2.03868, 2.05134, 2.07488, 2.15367, 2.12178, 2.10391, 2.11252, 2.15432, 2.41623}, + {2.11656, 2.11857, 2.12505, 2.13772, 2.16147, 2.23919, 2.20817, 2.19265, 2.20744, 2.23872, 2.49996}, + {2.20291, 2.20611, 2.21137, 2.22387, 2.24758, 2.32563, 2.29450, 2.27901, 2.28525, 2.32507, 2.58897}, + {2.28922, 2.29172, 2.29774, 2.31345, 2.33400, 2.41287, 2.38086, 2.36535, 2.37160, 2.40869, 2.67113}, + {2.37572, 2.37764, 2.38410, 2.39803, 2.42046, 2.49817, 2.46721, 2.45171, 2.45794, 2.49505, 2.76061}, + {2.46190, 2.46396, 2.47043, 2.48340, 2.50665, 2.58453, 2.55357, 2.53728, 2.54430, 2.58407, 2.84816}, + {2.54833, 2.55032, 2.55679, 2.56976, 2.59312, 2.67103, 2.63993, 2.62364, 2.63062, 2.67040, 2.93444}, + {2.63456, 2.63660, 2.64304, 2.65555, 2.67938, 2.75739, 2.72629, 2.71064, 2.71688, 2.75671, 3.01886}}; + + // Garfield simulation at UD = -1700V; vd = 1.299cm/microsec, <driftfield> = 590V/cm + float time1700[ktimebin][kZbin] = { + {0.09167, 0.09201, 0.09302, 0.09471, 0.09712, 0.10029, 0.10438, 0.10986, 0.11832, 0.13986, 0.37824}, + {0.06591, 0.06626, 0.06736, 0.06923, 0.07183, 0.07519, 0.07944, 0.08511, 0.09378, 0.11603, 0.35625}, + {0.03946, 0.04003, 0.04170, 0.04433, 0.04777, 0.05189, 0.05676, 0.06301, 0.07291, 0.09880, 0.34724}, + {0.01110, 0.01281, 0.01718, 0.02280, 0.02879, 0.03476, 0.04096, 0.04910, 0.06417, 0.10582, 0.36861}, + {0.01115, 0.01294, 0.01721, 0.02276, 0.02862, 0.03447, 0.04074, 0.04980, 0.06812, 0.11565, 0.37231}, + {0.03920, 0.03974, 0.04133, 0.04381, 0.04706, 0.05105, 0.05603, 0.06299, 0.07618, 0.10860, 0.36646}, + {0.06498, 0.06529, 0.06634, 0.06808, 0.07055, 0.07395, 0.07852, 0.08500, 0.09650, 0.12532, 0.37850}, + {0.09143, 0.09159, 0.09207, 0.09297, 0.09473, 0.00000, 0.11102, 0.11809, 0.13245, 0.16627, 0.42496}, + {0.13646, 0.13750, 0.14112, 0.14926, 0.16806, 0.24960, 0.20627, 0.19536, 0.20366, 0.24256, 0.50557}, + {0.20678, 0.20848, 0.21384, 0.22450, 0.24552, 0.32018, 0.28740, 0.27477, 0.28196, 0.32128, 0.58475}, + {0.28287, 0.28461, 0.29020, 0.30108, 0.32224, 0.39467, 0.36500, 0.35217, 0.35926, 0.39860, 0.66194}, + {0.35972, 0.36145, 0.36713, 0.37797, 0.39912, 0.47091, 0.44212, 0.42925, 0.43632, 0.47563, 0.73892}, + {0.43667, 0.43841, 0.44413, 0.45494, 0.47607, 0.54780, 0.51912, 0.50627, 0.51334, 0.55254, 0.81595}, + {0.51365, 0.51540, 0.52101, 0.53193, 0.55305, 0.62463, 0.59617, 0.58328, 0.59035, 0.62965, 0.89303}, + {0.59064, 0.59240, 0.59801, 0.60893, 0.63009, 0.70169, 0.67317, 0.66028, 0.66735, 0.70666, 0.96995}, + {0.66765, 0.66939, 0.67501, 0.68592, 0.70724, 0.77863, 0.75016, 0.73728, 0.74435, 0.78366, 1.04696}, + {0.74464, 0.74636, 0.75200, 0.76293, 0.78405, 0.85561, 0.82716, 0.81427, 0.82133, 0.86064, 1.12396}, + {0.82165, 0.82340, 0.82902, 0.83991, 0.86104, 0.93266, 0.90414, 0.89128, 0.89834, 0.93763, 1.20100}, + {0.89863, 0.90042, 0.90659, 0.91705, 0.93805, 1.00960, 0.98115, 0.96825, 0.97533, 1.01462, 1.27801}, + {0.97563, 0.97740, 0.98310, 0.99391, 1.01504, 1.08659, 1.05814, 1.04526, 1.05233, 1.09163, 1.35503}, + {1.05276, 1.05451, 1.06002, 1.07090, 1.09099, 1.16357, 1.13516, 1.12225, 1.12933, 1.16863, 1.43195}, + {1.12977, 1.13138, 1.13700, 1.14792, 1.16797, 1.24061, 1.21212, 1.19926, 1.20626, 1.24554, 1.50900}, + {1.20664, 1.20839, 1.21400, 1.22490, 1.24606, 1.31772, 1.28914, 1.27382, 1.28329, 1.32262, 1.58550}, + {1.28367, 1.28541, 1.29099, 1.30189, 1.32312, 1.39460, 1.36612, 1.34924, 1.36030, 1.39961, 1.66310}, + {1.36064, 1.36249, 1.36799, 1.37896, 1.40004, 1.48030, 1.44314, 1.43032, 1.43731, 1.47659, 1.73442}, + {1.43762, 1.43937, 1.44497, 1.45618, 1.47704, 1.54932, 1.52012, 1.50725, 1.51430, 1.55357, 1.81708}, + {1.51462, 1.51937, 1.52203, 1.53316, 1.55403, 1.62572, 1.59713, 1.58424, 1.59128, 1.63061, 1.89406}, + {1.59162, 1.59338, 1.59947, 1.60989, 1.63103, 1.70270, 1.67411, 1.66124, 1.66799, 1.70759, 1.97103}, + {1.66874, 1.67037, 1.67597, 1.68687, 1.70814, 1.77969, 1.75112, 1.73806, 1.74530, 1.78457, 2.04794}, + {1.74693, 1.74749, 1.75297, 1.76476, 1.78500, 1.85667, 1.82811, 1.81504, 1.82101, 1.86161, 2.12492}, + {1.82260, 1.82437, 1.82995, 1.84174, 1.86202, 1.93372, 1.90509, 1.89202, 1.89930, 1.93859, 2.20189}, + {1.89964, 1.90135, 1.90693, 1.91789, 1.93952, 2.01080, 1.98207, 1.96921, 1.97628, 2.01384, 2.27887}, + {1.97662, 1.97917, 1.98611, 1.99487, 2.01601, 2.08778, 2.05846, 2.04623, 2.05330, 2.09244, 2.35585}, + {2.05359, 2.05615, 2.06309, 2.07187, 2.09867, 2.16459, 2.13610, 2.12322, 2.13029, 2.16942, 2.43199}, + {2.13063, 2.13233, 2.13795, 2.14886, 2.17008, 2.24199, 2.21310, 2.20020, 2.20727, 2.24659, 2.50983}, + {2.20761, 2.20931, 2.21955, 2.22624, 2.24708, 2.32147, 2.29009, 2.27725, 2.28276, 2.32359, 2.58680}, + {2.28459, 2.29108, 2.29202, 2.30286, 2.32007, 2.39559, 2.36683, 2.35422, 2.36119, 2.40058, 2.66081}, + {2.36153, 2.36806, 2.36889, 2.37985, 2.40092, 2.47828, 2.44381, 2.43099, 2.43819, 2.47750, 2.73779}}; + + // Garfield simulation at UD = -1800V; vd = 1.450cm/microsec, <driftfield> = 623V/cm + float time1800[ktimebin][kZbin] = { + {0.09166, 0.09199, 0.09300, 0.09470, 0.09709, 0.10026, 0.10434, 0.10983, 0.11831, 0.13987, 0.37802}, + {0.06585, 0.06623, 0.06735, 0.06921, 0.07180, 0.07520, 0.07941, 0.08507, 0.09376, 0.11604, 0.35624}, + {0.03945, 0.04004, 0.04169, 0.04432, 0.04776, 0.05187, 0.05674, 0.06300, 0.07290, 0.09884, 0.34704}, + {0.01108, 0.01287, 0.01717, 0.02280, 0.02880, 0.03476, 0.04095, 0.04909, 0.06419, 0.10589, 0.36843}, + {0.01115, 0.01287, 0.01720, 0.02276, 0.02862, 0.03448, 0.04073, 0.04973, 0.06799, 0.11535, 0.37224}, + {0.03918, 0.03975, 0.04134, 0.04382, 0.04707, 0.05105, 0.05603, 0.06296, 0.07605, 0.10822, 0.36560}, + {0.06498, 0.06532, 0.06635, 0.06809, 0.07058, 0.07395, 0.07855, 0.08495, 0.09632, 0.12488, 0.37730}, + {0.09130, 0.09160, 0.09199, 0.09300, 0.09472, 0.00000, 0.11059, 0.11747, 0.13146, 0.16462, 0.42233}, + {0.13364, 0.13449, 0.13767, 0.14481, 0.16147, 0.23635, 0.19706, 0.18812, 0.19704, 0.23520, 0.49749}, + {0.19698, 0.19844, 0.20311, 0.21236, 0.23082, 0.29920, 0.26936, 0.25927, 0.26732, 0.30601, 0.56871}, + {0.26518, 0.26670, 0.27160, 0.28099, 0.29955, 0.36597, 0.33885, 0.32858, 0.33653, 0.37524, 0.63801}, + {0.33441, 0.33553, 0.34040, 0.34987, 0.36841, 0.43428, 0.40797, 0.39763, 0.40556, 0.44425, 0.70698}, + {0.40296, 0.40447, 0.40934, 0.41881, 0.43737, 0.50306, 0.47695, 0.46662, 0.47455, 0.51329, 0.77600}, + {0.47296, 0.47344, 0.47830, 0.48779, 0.50632, 0.57200, 0.54593, 0.53559, 0.54351, 0.58222, 0.84489}, + {0.54089, 0.54264, 0.54727, 0.55673, 0.57529, 0.64094, 0.61490, 0.60457, 0.61249, 0.65118, 0.91394}, + {0.60987, 0.61138, 0.61624, 0.62573, 0.64428, 0.70989, 0.68397, 0.67354, 0.68147, 0.72015, 0.98291}, + {0.67883, 0.68035, 0.68521, 0.69469, 0.71324, 0.77896, 0.75287, 0.74251, 0.75043, 0.78912, 1.04458}, + {0.74780, 0.74932, 0.75421, 0.76367, 0.78221, 0.84785, 0.82185, 0.81148, 0.81941, 0.85811, 1.12085}, + {0.81690, 0.81830, 0.82316, 0.83263, 0.85120, 0.91683, 0.89077, 0.88045, 0.88837, 0.92707, 1.18976}, + {0.88574, 0.88726, 0.89228, 0.90198, 0.92017, 0.98578, 0.95974, 0.94947, 0.95734, 0.99604, 1.25873}, + {0.95493, 0.95624, 0.96110, 0.97058, 0.98913, 1.05481, 1.02873, 1.01839, 1.02631, 1.06503, 1.32772}, + {1.02392, 1.02524, 1.03008, 1.03955, 1.05810, 1.12378, 1.09757, 1.08605, 1.09530, 1.13399, 1.39669}, + {1.09270, 1.09418, 1.09911, 1.10854, 1.12714, 1.19281, 1.16502, 1.15633, 1.16427, 1.20271, 1.46574}, + {1.16168, 1.16323, 1.16801, 1.17772, 1.19604, 1.26190, 1.23399, 1.22531, 1.23323, 1.27194, 1.53475}, + {1.23061, 1.23214, 1.23698, 1.24669, 1.26503, 1.33073, 1.30461, 1.29428, 1.30220, 1.34091, 1.60372}, + {1.29960, 1.30110, 1.30596, 1.31544, 1.33398, 1.39962, 1.37228, 1.36323, 1.37121, 1.40988, 1.67273}, + {1.36851, 1.37007, 1.37512, 1.38441, 1.40297, 1.46865, 1.44256, 1.43222, 1.44017, 1.47878, 1.74155}, + {1.43752, 1.43904, 1.44773, 1.45338, 1.47220, 1.53759, 1.51136, 1.50119, 1.50914, 1.54775, 1.81050}, + {1.50646, 1.50802, 1.51288, 1.52237, 1.54097, 1.60697, 1.58049, 1.57018, 1.57811, 1.61678, 1.87947}, + {1.57545, 1.57720, 1.58185, 1.59134, 1.60996, 1.67787, 1.64929, 1.63914, 1.64707, 1.68570, 1.94851}, + {1.64442, 1.64617, 1.65081, 1.66035, 1.67893, 1.74684, 1.71826, 1.70745, 1.71604, 1.75310, 2.01748}, + {1.71337, 1.71513, 1.71978, 1.72932, 1.74645, 1.81346, 1.78739, 1.77642, 1.78501, 1.82151, 2.08644}, + {1.78238, 1.78410, 1.78876, 1.79824, 1.81678, 1.88332, 1.85639, 1.84262, 1.85397, 1.89270, 2.15533}, + {1.85135, 1.85306, 1.85778, 1.86728, 1.88580, 1.95615, 1.92536, 1.91171, 1.92283, 1.96165, 2.22428}, + {1.92774, 1.92184, 1.92672, 1.93618, 1.95477, 2.02048, 1.99427, 1.98068, 1.99192, 2.03062, 2.29338}, + {1.98929, 1.99081, 1.99567, 2.00515, 2.02373, 2.08987, 2.06332, 2.05249, 2.05939, 2.09928, 2.36227}, + {2.05829, 2.05978, 2.06464, 2.07414, 2.09272, 2.15850, 2.12928, 2.12194, 2.12987, 2.16825, 2.43083}, + {2.12726, 2.12869, 2.13360, 2.14425, 2.16160, 2.22872, 2.20118, 2.19078, 2.19876, 2.23416, 2.50007}}; + + // Garfield simulation at UD = -1900V; vd = 1.610cm/microsec, <driftfield> = 655V/cm + float time1900[ktimebin][kZbin] = { + {0.09166, 0.09198, 0.09298, 0.09467, 0.09707, 0.10023, 0.10431, 0.10980, 0.11828, 0.13988, 0.37789}, + {0.06584, 0.06622, 0.06735, 0.06920, 0.07179, 0.07514, 0.07938, 0.08505, 0.09374, 0.11606, 0.35599}, + {0.03945, 0.04002, 0.04169, 0.04432, 0.04775, 0.05185, 0.05672, 0.06298, 0.07290, 0.09888, 0.34695}, + {0.01109, 0.01281, 0.01717, 0.02279, 0.02878, 0.03476, 0.04094, 0.04909, 0.06421, 0.10597, 0.36823}, + {0.01115, 0.01287, 0.01720, 0.02294, 0.02862, 0.03448, 0.04074, 0.04973, 0.06783, 0.11506, 0.37206}, + {0.03940, 0.03975, 0.04135, 0.04386, 0.04708, 0.05106, 0.05604, 0.06293, 0.07592, 0.10787, 0.36484}, + {0.06500, 0.06534, 0.06638, 0.06811, 0.07060, 0.07413, 0.07852, 0.08491, 0.09614, 0.12446, 0.37626}, + {0.09119, 0.09140, 0.09194, 0.09293, 0.09471, 0.00000, 0.11013, 0.11685, 0.13050, 0.16302, 0.41991}, + {0.13111, 0.13190, 0.13466, 0.14091, 0.15554, 0.22409, 0.18846, 0.18167, 0.19113, 0.22854, 0.48995}, + {0.18849, 0.18975, 0.19380, 0.20185, 0.21797, 0.28050, 0.25368, 0.24575, 0.25446, 0.29249, 0.55442}, + {0.24995, 0.25125, 0.25563, 0.26366, 0.27986, 0.34065, 0.31605, 0.30815, 0.31680, 0.35482, 0.61697}, + {0.31187, 0.31324, 0.31745, 0.32580, 0.34205, 0.40217, 0.37825, 0.37031, 0.37897, 0.41696, 0.67890}, + {0.37401, 0.37531, 0.37955, 0.38777, 0.40395, 0.46408, 0.44037, 0.43242, 0.44108, 0.47906, 0.74122}, + {0.43610, 0.43741, 0.44161, 0.44986, 0.46604, 0.52614, 0.50248, 0.49452, 0.50316, 0.54116, 0.80326}, + {0.49820, 0.49988, 0.50372, 0.51196, 0.52814, 0.58822, 0.56459, 0.55661, 0.56527, 0.60326, 0.86526}, + {0.56032, 0.56161, 0.56582, 0.57408, 0.59024, 0.65032, 0.62670, 0.61872, 0.62737, 0.66537, 0.92749}, + {0.62240, 0.62371, 0.62792, 0.63624, 0.65236, 0.71241, 0.68881, 0.68081, 0.68947, 0.72750, 0.98941}, + {0.68449, 0.68581, 0.69002, 0.69828, 0.71444, 0.77452, 0.75089, 0.74295, 0.75157, 0.78957, 1.05157}, + {0.74660, 0.74790, 0.75212, 0.76036, 0.77654, 0.83748, 0.81299, 0.80501, 0.81193, 0.85168, 1.11375}, + {0.80870, 0.81017, 0.81423, 0.82246, 0.83867, 0.89908, 0.87509, 0.86660, 0.87577, 0.91376, 1.17586}, + {0.87080, 0.87233, 0.87632, 0.88458, 0.90074, 0.96083, 0.93718, 0.92922, 0.93787, 0.97588, 1.23794}, + {0.93291, 0.93422, 0.93844, 0.94667, 0.96293, 1.02295, 0.99929, 0.99127, 0.99997, 1.03797, 1.29995}, + {0.99500, 0.99645, 1.00308, 1.00877, 1.02497, 1.08504, 1.06140, 1.05343, 1.06203, 1.10006, 1.36216}, + {1.05712, 1.05926, 1.06262, 1.07092, 1.08706, 1.14754, 1.12350, 1.11550, 1.12417, 1.16218, 1.42427}, + {1.11921, 1.12059, 1.12473, 1.13297, 1.14916, 1.21140, 1.18560, 1.17284, 1.18625, 1.22414, 1.48629}, + {1.18140, 1.18262, 1.18690, 1.19508, 1.21125, 1.27139, 1.24164, 1.23495, 1.24838, 1.28634, 1.54852}, + {1.24340, 1.24473, 1.24901, 1.25732, 1.27336, 1.33358, 1.30793, 1.30179, 1.31047, 1.34848, 1.61066}, + {1.30551, 1.30684, 1.31104, 1.32056, 1.33553, 1.39609, 1.37004, 1.36392, 1.37045, 1.41057, 1.67259}, + {1.36755, 1.36892, 1.37315, 1.39148, 1.39755, 1.45820, 1.43215, 1.42602, 1.43467, 1.47268, 1.73477}, + {1.42966, 1.43101, 1.43549, 1.45359, 1.45976, 1.52031, 1.49601, 1.48811, 1.49677, 1.53477, 1.79691}, + {1.49180, 1.49321, 1.49760, 1.51570, 1.52175, 1.58185, 1.55771, 1.55023, 1.55888, 1.59672, 1.85501}, + {1.55391, 1.55527, 1.55943, 1.57782, 1.58391, 1.64395, 1.62008, 1.61233, 1.62085, 1.65883, 1.92091}, + {1.61599, 1.61732, 1.62154, 1.63993, 1.64612, 1.70608, 1.68237, 1.67108, 1.68301, 1.72110, 1.97931}, + {1.67808, 1.67948, 1.68364, 1.70204, 1.70823, 1.76858, 1.74404, 1.73539, 1.74512, 1.78321, 2.04522}, + {1.74019, 1.74152, 1.74573, 1.76415, 1.77015, 1.83040, 1.80615, 1.79366, 1.80723, 1.84509, 2.10742}, + {1.80235, 1.80362, 1.80783, 1.82626, 1.83227, 1.89246, 1.86795, 1.85405, 1.86938, 1.90720, 2.16953}, + {1.86442, 1.86572, 1.86994, 1.88837, 1.89438, 1.95445, 1.93006, 1.92283, 1.93148, 1.96931, 2.23147}, + {1.92700, 1.92783, 1.93197, 1.95049, 1.95649, 2.01668, 1.99217, 1.98486, 1.99352, 2.03143, 2.29358}}; + + // Garfield simulation at UD = -2000V; vd = 1.783cm/microsec, <driftfield> = 688V/cm + float time2000[ktimebin][kZbin] = { + {0.09176, 0.09196, 0.09296, 0.09465, 0.09704, 0.10020, 0.10427, 0.10977, 0.11825, 0.13988, 0.37774}, + {0.06583, 0.06620, 0.06735, 0.06918, 0.07177, 0.07513, 0.07936, 0.08503, 0.09372, 0.11606, 0.35586}, + {0.03944, 0.04001, 0.04170, 0.04431, 0.04774, 0.05184, 0.05670, 0.06296, 0.07291, 0.09893, 0.34680}, + {0.01108, 0.01281, 0.01719, 0.02279, 0.02879, 0.03474, 0.04093, 0.04908, 0.06422, 0.10605, 0.36800}, + {0.01114, 0.01287, 0.01720, 0.02276, 0.02863, 0.03449, 0.04073, 0.04970, 0.06774, 0.11478, 0.37179}, + {0.03925, 0.03977, 0.04135, 0.04386, 0.04711, 0.05108, 0.05604, 0.06290, 0.07580, 0.10748, 0.36386}, + {0.06501, 0.06536, 0.06640, 0.06814, 0.07062, 0.07398, 0.07852, 0.08487, 0.09598, 0.12405, 0.37519}, + {0.09109, 0.09127, 0.09188, 0.09292, 0.09472, 0.00000, 0.10964, 0.11630, 0.12960, 0.16150, 0.41765}, + {0.12898, 0.12968, 0.13209, 0.13749, 0.15034, 0.21286, 0.18088, 0.17590, 0.18591, 0.22254, 0.48315}, + {0.18122, 0.18227, 0.18574, 0.19263, 0.20674, 0.26376, 0.23960, 0.23375, 0.24316, 0.28047, 0.54179}, + {0.23674, 0.23784, 0.24142, 0.24847, 0.26264, 0.31810, 0.29602, 0.29008, 0.29944, 0.33675, 0.59795}, + {0.29279, 0.29382, 0.29742, 0.30448, 0.31865, 0.37364, 0.35215, 0.34629, 0.35555, 0.39286, 0.65411}, + {0.34875, 0.34987, 0.35346, 0.36054, 0.37472, 0.42956, 0.40825, 0.40229, 0.41167, 0.44894, 0.71033}, + {0.40484, 0.40594, 0.40954, 0.41660, 0.43077, 0.48560, 0.46433, 0.45840, 0.46772, 0.50500, 0.76632}, + {0.46090, 0.46201, 0.46560, 0.47267, 0.48684, 0.54167, 0.52041, 0.51449, 0.52382, 0.56108, 0.82227}, + {0.51698, 0.51809, 0.52173, 0.52874, 0.54291, 0.59776, 0.57646, 0.57052, 0.57986, 0.61717, 0.87836}, + {0.57306, 0.57418, 0.57782, 0.58513, 0.59899, 0.65380, 0.63255, 0.62661, 0.63594, 0.67325, 0.93460}, + {0.62912, 0.63024, 0.63383, 0.64103, 0.65506, 0.70988, 0.68484, 0.68267, 0.69202, 0.72878, 0.99046}, + {0.68521, 0.68633, 0.68990, 0.69699, 0.71115, 0.76595, 0.74468, 0.73872, 0.74814, 0.78538, 1.04674}, + {0.74127, 0.74239, 0.74605, 0.75303, 0.77022, 0.82204, 0.80078, 0.79484, 0.80416, 0.84147, 1.10261}, + {0.79736, 0.79846, 0.80206, 0.80947, 0.82330, 0.87813, 0.85688, 0.85087, 0.86023, 0.89752, 1.15874}, + {0.85342, 0.85454, 0.85815, 0.86519, 0.87936, 0.93417, 0.91293, 0.90428, 0.91631, 0.95360, 1.20760}, + {0.90949, 0.91061, 0.91423, 0.92128, 0.93544, 0.99026, 0.96807, 0.96305, 0.97239, 1.00967, 1.27078}, + {0.96556, 0.96669, 0.97111, 0.97734, 0.99151, 1.04664, 1.02508, 1.01879, 1.02846, 1.06167, 1.32695}, + {1.02167, 1.02279, 1.02656, 1.03341, 1.04759, 1.10242, 1.08115, 1.07003, 1.08453, 1.12184, 1.38304}, + {1.07776, 1.07883, 1.08242, 1.08950, 1.10384, 1.16422, 1.13725, 1.13133, 1.14061, 1.17793, 1.43910}, + {1.13379, 1.13492, 1.13864, 1.14567, 1.15973, 1.21455, 1.19323, 1.18734, 1.19668, 1.23401, 1.49528}, + {1.18988, 1.19098, 1.19457, 1.20164, 1.21582, 1.27064, 1.24937, 1.24044, 1.25275, 1.29004, 1.55137}, + {1.24592, 1.24706, 1.25087, 1.25773, 1.27188, 1.32670, 1.30544, 1.29953, 1.30883, 1.34613, 1.60743}, + {1.30202, 1.30313, 1.30673, 1.31381, 1.32797, 1.38278, 1.36151, 1.35167, 1.36490, 1.40221, 1.66306}, + {1.35809, 1.35921, 1.36282, 1.36986, 1.38403, 1.43888, 1.41760, 1.41174, 1.42083, 1.45830, 1.71915}, + {1.41419, 1.41528, 1.41890, 1.42595, 1.44011, 1.49496, 1.47368, 1.46769, 1.47706, 1.51436, 1.77523}, + {1.47131, 1.47141, 1.47494, 1.48850, 1.49620, 1.55137, 1.52977, 1.51820, 1.53315, 1.57042, 1.83158}, + {1.52635, 1.52750, 1.53103, 1.53814, 1.55228, 1.60736, 1.58503, 1.57986, 1.58920, 1.62649, 1.88767}, + {1.58418, 1.58355, 1.58711, 1.59526, 1.60833, 1.66316, 1.63345, 1.63261, 1.64556, 1.68204, 1.94359}, + {1.64027, 1.63958, 1.64489, 1.65024, 1.66443, 1.71925, 1.69794, 1.69201, 1.70143, 1.73865, 1.99968}, + {1.69450, 1.69566, 1.69940, 1.70697, 1.71841, 1.77819, 1.75396, 1.74814, 1.75743, 1.79083, 2.05427}, + {1.75054, 1.75221, 1.75527, 1.76306, 1.77662, 1.83428, 1.81006, 1.81173, 1.81345, 1.85076, 2.10289}}; + + // Garfield simulation at UD = -2100V; vd = 1.959cm/microsec, <driftfield> = 720V/cm + float time2100[ktimebin][kZbin] = { + {0.09160, 0.09194, 0.09294, 0.09462, 0.09701, 0.10017, 0.10424, 0.10974, 0.11823, 0.13988, 0.37762}, + {0.06585, 0.06619, 0.06731, 0.06916, 0.07174, 0.07509, 0.07933, 0.08500, 0.09370, 0.11609, 0.35565}, + {0.03960, 0.04001, 0.04171, 0.04430, 0.04774, 0.05182, 0.05668, 0.06294, 0.07291, 0.09896, 0.34676}, + {0.01109, 0.01280, 0.01716, 0.02279, 0.02876, 0.03474, 0.04096, 0.04908, 0.06424, 0.10612, 0.36790}, + {0.01114, 0.01285, 0.01719, 0.02287, 0.02863, 0.03449, 0.04073, 0.04964, 0.06759, 0.11446, 0.37162}, + {0.03922, 0.03977, 0.04146, 0.04386, 0.04711, 0.05109, 0.05605, 0.06287, 0.07575, 0.10713, 0.36298}, + {0.06504, 0.06538, 0.06641, 0.06818, 0.07064, 0.07426, 0.07852, 0.08483, 0.09581, 0.12363, 0.37424}, + {0.09103, 0.09129, 0.09186, 0.09291, 0.09476, 0.00000, 0.10923, 0.11578, 0.12873, 0.16005, 0.41525}, + {0.12723, 0.12777, 0.12988, 0.13458, 0.14579, 0.20264, 0.17421, 0.17078, 0.18132, 0.21708, 0.47699}, + {0.17508, 0.17601, 0.17897, 0.18487, 0.19698, 0.24881, 0.22737, 0.22337, 0.23348, 0.27000, 0.53032}, + {0.22571, 0.22663, 0.22969, 0.23570, 0.24787, 0.29826, 0.27871, 0.27462, 0.28471, 0.32122, 0.58166}, + {0.27664, 0.27759, 0.28067, 0.28669, 0.29891, 0.34898, 0.32982, 0.32570, 0.33576, 0.37229, 0.63268}, + {0.32766, 0.32862, 0.33170, 0.33778, 0.34988, 0.39973, 0.38088, 0.37675, 0.38680, 0.42333, 0.68159}, + {0.37872, 0.37966, 0.38275, 0.38875, 0.40093, 0.45073, 0.43192, 0.42780, 0.43786, 0.47438, 0.73480}, + {0.42974, 0.43070, 0.43378, 0.43982, 0.45196, 0.50177, 0.48297, 0.47884, 0.48889, 0.52544, 0.78581}, + {0.48081, 0.48175, 0.48482, 0.49084, 0.50302, 0.55290, 0.53398, 0.52988, 0.53994, 0.57647, 0.83687}, + {0.53645, 0.53295, 0.53586, 0.54188, 0.55408, 0.60398, 0.58504, 0.58092, 0.59100, 0.62768, 0.88773}, + {0.58345, 0.58409, 0.58690, 0.59292, 0.60510, 0.65562, 0.63609, 0.63197, 0.64203, 0.67856, 0.93907}, + {0.63397, 0.63490, 0.63795, 0.64403, 0.65613, 0.70612, 0.68714, 0.68301, 0.69294, 0.72955, 0.99000}, + {0.68496, 0.68592, 0.68899, 0.69504, 0.70733, 0.75708, 0.73818, 0.73405, 0.74412, 0.78064, 1.04100}, + {0.73600, 0.73696, 0.74003, 0.74624, 0.75828, 0.80805, 0.78904, 0.78512, 0.79517, 0.83152, 1.09205}, + {0.78709, 0.78801, 0.79108, 0.79709, 0.80931, 0.85906, 0.84027, 0.83614, 0.84621, 0.88269, 1.14058}, + {0.83808, 0.83905, 0.84215, 0.84816, 0.86031, 0.91011, 0.89139, 0.88718, 0.89725, 0.93377, 1.19413}, + {0.88916, 0.89010, 0.89320, 0.89920, 0.91136, 0.96117, 0.94235, 0.93822, 0.94828, 0.98480, 1.24538}, + {0.94036, 0.94113, 0.94422, 0.95023, 0.96241, 1.01220, 0.99310, 0.98927, 0.99933, 1.03585, 1.29629}, + {0.99139, 0.99220, 0.99525, 1.00127, 1.01344, 1.06324, 1.04451, 1.04033, 1.04836, 1.08690, 1.34727}, + {1.04261, 1.04325, 1.04628, 1.05232, 1.06448, 1.12090, 1.09546, 1.09136, 1.10142, 1.13795, 1.39831}, + {1.09331, 1.09429, 1.09742, 1.10336, 1.11557, 1.16547, 1.14658, 1.13642, 1.15247, 1.18898, 1.44936}, + {1.14436, 1.14539, 1.14847, 1.15443, 1.16662, 1.21794, 1.19763, 1.19329, 1.20349, 1.23956, 1.50043}, + {1.19533, 1.19651, 1.19943, 1.20548, 1.21666, 1.26753, 1.24862, 1.24434, 1.25455, 1.29106, 1.55142}, + {1.24638, 1.24756, 1.25046, 1.25648, 1.26764, 1.31858, 1.29967, 1.29538, 1.30499, 1.34211, 1.60250}, + {1.29747, 1.29847, 1.30175, 1.30753, 1.31869, 1.36969, 1.35069, 1.34656, 1.35663, 1.39316, 1.64644}, + {1.35537, 1.34952, 1.35255, 1.35869, 1.36973, 1.41387, 1.40173, 1.39761, 1.40768, 1.44396, 1.70238}, + {1.39956, 1.40056, 1.40380, 1.40961, 1.42178, 1.46492, 1.45278, 1.45423, 1.45872, 1.49522, 1.75557}, + {1.45080, 1.45159, 1.45463, 1.46109, 1.47287, 1.52263, 1.50382, 1.50050, 1.50977, 1.54502, 1.80670}, + {1.50165, 1.50264, 1.50570, 1.51214, 1.52233, 1.57370, 1.55484, 1.55155, 1.56080, 1.59731, 1.85778}, + {1.55269, 1.55364, 1.55675, 1.56274, 1.57491, 1.62598, 1.60590, 1.60259, 1.61185, 1.64836, 1.90883}, + {1.60368, 1.60469, 1.60779, 1.61373, 1.62596, 1.67738, 1.65651, 1.65249, 1.66290, 1.69936, 1.95959}}; + + // Garfield simulation at UD = -2200V; vd = 2.134cm/microsec, <driftfield> = 753V/cm + float time2200[ktimebin][kZbin] = { + {0.09162, 0.09194, 0.09292, 0.09460, 0.09702, 0.10014, 0.10421, 0.10971, 0.11820, 0.13990, 0.37745}, + {0.06581, 0.06618, 0.06730, 0.06915, 0.07173, 0.07507, 0.07931, 0.08497, 0.09368, 0.11609, 0.35560}, + {0.03947, 0.04001, 0.04167, 0.04429, 0.04772, 0.05183, 0.05667, 0.06293, 0.07292, 0.09900, 0.34673}, + {0.01111, 0.01280, 0.01716, 0.02279, 0.02876, 0.03473, 0.04091, 0.04907, 0.06426, 0.10620, 0.36766}, + {0.01113, 0.01285, 0.01719, 0.02276, 0.02863, 0.03452, 0.04076, 0.04960, 0.06745, 0.11419, 0.37139}, + {0.03923, 0.03978, 0.04137, 0.04387, 0.04713, 0.05110, 0.05605, 0.06284, 0.07551, 0.10677, 0.36210}, + {0.06505, 0.06540, 0.06644, 0.06820, 0.07069, 0.07401, 0.07852, 0.08479, 0.09565, 0.12325, 0.37313}, + {0.09107, 0.09127, 0.09181, 0.09291, 0.09474, 0.00000, 0.10883, 0.11528, 0.12789, 0.15865, 0.41313}, + {0.12559, 0.12622, 0.12800, 0.13206, 0.14166, 0.19331, 0.16832, 0.16632, 0.17724, 0.21218, 0.47098}, + {0.16992, 0.17070, 0.17325, 0.17831, 0.18871, 0.23557, 0.21690, 0.21451, 0.22514, 0.26082, 0.52034}, + {0.21640, 0.21722, 0.21987, 0.22500, 0.23540, 0.28097, 0.26399, 0.26154, 0.27214, 0.30784, 0.56734}, + {0.26318, 0.26400, 0.26679, 0.27181, 0.28220, 0.32739, 0.31090, 0.30842, 0.31902, 0.35474, 0.61415}, + {0.31001, 0.31085, 0.31348, 0.31866, 0.32903, 0.37412, 0.35777, 0.35546, 0.36588, 0.40159, 0.66103}, + {0.35687, 0.35769, 0.36033, 0.36556, 0.37588, 0.42094, 0.40523, 0.40214, 0.41273, 0.44841, 0.70785}, + {0.40372, 0.40457, 0.40723, 0.41234, 0.42273, 0.46778, 0.45148, 0.44903, 0.45961, 0.49526, 0.75486}, + {0.45062, 0.45139, 0.45404, 0.45966, 0.46958, 0.51470, 0.49833, 0.49584, 0.50644, 0.54211, 0.80160}, + {0.49742, 0.49825, 0.50088, 0.50605, 0.51644, 0.56148, 0.54518, 0.54270, 0.55330, 0.58897, 0.84854}, + {0.54427, 0.54510, 0.54774, 0.55290, 0.56329, 0.60846, 0.59203, 0.58955, 0.60014, 0.63578, 0.89528}, + {0.59119, 0.59199, 0.59471, 0.59975, 0.61014, 0.65533, 0.63889, 0.63636, 0.64699, 0.68269, 0.94197}, + {0.63866, 0.63880, 0.64145, 0.64664, 0.65701, 0.70639, 0.68574, 0.68325, 0.69385, 0.72949, 0.98900}, + {0.68483, 0.68566, 0.68831, 0.69347, 0.70386, 0.74890, 0.73260, 0.73010, 0.74069, 0.77638, 1.03320}, + {0.73168, 0.73255, 0.73515, 0.74031, 0.75072, 0.79576, 0.77117, 0.77501, 0.78755, 0.82318, 1.08006}, + {0.77854, 0.78310, 0.78200, 0.79525, 0.79756, 0.84281, 0.81803, 0.82393, 0.83441, 0.87008, 1.12692}, + {0.82541, 0.82642, 0.82916, 0.83528, 0.84442, 0.89086, 0.87315, 0.87079, 0.88125, 0.91694, 1.17648}, + {0.87226, 0.87308, 0.87602, 0.88086, 0.89128, 0.93772, 0.92001, 0.91751, 0.92811, 0.95587, 1.22328}, + {0.91921, 0.91994, 0.92256, 0.92772, 0.94713, 0.98566, 0.96690, 0.96436, 0.97495, 1.01064, 1.26882}, + {0.96790, 0.96679, 0.96941, 0.97463, 0.99399, 1.03001, 1.01376, 1.01112, 1.02181, 1.05749, 1.31568}, + {1.01278, 1.01390, 1.01674, 1.02147, 1.03182, 1.07820, 1.06056, 1.05798, 1.06867, 1.10433, 1.36390}, + {1.05964, 1.06076, 1.06331, 1.06833, 1.07870, 1.13297, 1.10742, 1.10520, 1.11543, 1.15120, 1.41069}, + {1.10664, 1.10762, 1.10997, 1.11519, 1.12556, 1.17531, 1.15427, 1.14620, 1.16229, 1.19805, 1.45758}, + {1.15352, 1.15421, 1.15683, 1.16218, 1.17242, 1.21910, 1.20035, 1.19863, 1.20579, 1.24473, 1.50412}, + {1.20019, 1.20115, 1.20369, 1.20892, 1.21928, 1.26913, 1.24721, 1.24549, 1.25605, 1.29159, 1.54920}, + {1.24707, 1.24846, 1.25052, 1.25602, 1.26608, 1.31558, 1.29448, 1.29232, 1.30293, 1.33675, 1.59798}, + {1.29391, 1.29475, 1.29738, 1.30255, 1.31294, 1.36244, 1.34167, 1.33918, 1.34979, 1.38229, 1.64496}, + {1.34078, 1.34304, 1.34424, 1.35565, 1.35980, 1.40930, 1.38853, 1.38229, 1.39664, 1.42863, 1.69162}, + {1.38762, 1.38847, 1.39110, 1.39627, 1.40666, 1.45183, 1.43539, 1.43289, 1.44348, 1.47549, 1.73876}, + {1.43524, 1.43533, 1.43796, 1.44310, 1.45371, 1.49305, 1.48224, 1.47941, 1.49034, 1.52601, 1.78552}, + {1.48122, 1.48219, 1.48482, 1.48991, 1.50030, 1.53991, 1.52898, 1.52653, 1.53653, 1.57282, 1.82386}}; + + if (!mTimeStruct1) { + mTimeStruct1 = new float[ktimebin * kZbin]; + } + if (!mTimeStruct2) { + mTimeStruct2 = new float[ktimebin * kZbin]; + } + memset(mTimeStruct1, 0, ktimebin * kZbin * sizeof(float)); + memset(mTimeStruct2, 0, ktimebin * kZbin * sizeof(float)); + + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + if (vdrift > fVDsmp[6]) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time2200[ctrt][ctrz]; + mVDlo = fVDsmp[6]; + mVDhi = fVDsmp[7]; + } else if (vdrift > fVDsmp[5]) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; + mVDlo = fVDsmp[5]; + mVDhi = fVDsmp[6]; + } else if (vdrift > fVDsmp[4]) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; + mVDlo = fVDsmp[4]; + mVDhi = fVDsmp[5]; + } else if (vdrift > fVDsmp[3]) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; + mVDlo = fVDsmp[3]; + mVDhi = fVDsmp[4]; + } else if (vdrift > fVDsmp[2]) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; + mVDlo = fVDsmp[2]; + mVDhi = fVDsmp[3]; + } else if (vdrift > fVDsmp[1]) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; + mVDlo = fVDsmp[1]; + mVDhi = fVDsmp[2]; + } else if (vdrift > (fVDsmp[0] - 1.0e-5)) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1500[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; + mVDlo = fVDsmp[0]; + mVDhi = fVDsmp[1]; + } + } + } +} + +void CommonParam::SetXenon() +{ + mGasMixture = kXenon; + SimParam::Instance()->ReInit(); +} + +void CommonParam::SetArgon() +{ + mGasMixture = kArgon; + SimParam::Instance()->ReInit(); +} diff --git a/Detectors/TRD/base/src/DiffAndTimeStructEstimator.cxx b/Detectors/TRD/base/src/DiffAndTimeStructEstimator.cxx new file mode 100644 index 0000000000000..5c9a131050a22 --- /dev/null +++ b/Detectors/TRD/base/src/DiffAndTimeStructEstimator.cxx @@ -0,0 +1,581 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDBase/CommonParam.h" +#include "TRDBase/DiffAndTimeStructEstimator.h" +#include "TRDBase/Geometry.h" +#include <cmath> + +namespace o2::trd +{ + +// Garfield simulation at UD = -1500V; vd = 1.032cm/microsec, <driftfield> = 525V/cm +constexpr float time1500[ktimebin][kZbin] = { + {0.09170, 0.09205, 0.09306, 0.09475, 0.09716, 0.10035, 0.10445, 0.10993, 0.11838, 0.13986, 0.37858}, + {0.06588, 0.06626, 0.06739, 0.06926, 0.07186, 0.07524, 0.07951, 0.08515, 0.09381, 0.11601, 0.35673}, + {0.03946, 0.04003, 0.04171, 0.04435, 0.04780, 0.05193, 0.05680, 0.06306, 0.07290, 0.09873, 0.34748}, + {0.01151, 0.01283, 0.01718, 0.02282, 0.02880, 0.03479, 0.04098, 0.04910, 0.06413, 0.10567, 0.36897}, + {0.01116, 0.01290, 0.01721, 0.02299, 0.02863, 0.03447, 0.04074, 0.04984, 0.06839, 0.11625, 0.37277}, + {0.03919, 0.03974, 0.04131, 0.04380, 0.04703, 0.05102, 0.05602, 0.06309, 0.07651, 0.10938, 0.36838}, + {0.06493, 0.06560, 0.06640, 0.06802, 0.07051, 0.07392, 0.07853, 0.08510, 0.09690, 0.12621, 0.38058}, + {0.09174, 0.09186, 0.09225, 0.09303, 0.09477, 0.00000, 0.11205, 0.11952, 0.13461, 0.16984, 0.43017}, + {0.14356, 0.14494, 0.14959, 0.16002, 0.18328, 0.27981, 0.22785, 0.21240, 0.21948, 0.25965, 0.52392}, + {0.23120, 0.23366, 0.24046, 0.25422, 0.28071, 0.36914, 0.32999, 0.31208, 0.31772, 0.35804, 0.62249}, + {0.32686, 0.32916, 0.33646, 0.35053, 0.37710, 0.46292, 0.42773, 0.40948, 0.41497, 0.45527, 0.71955}, + {0.42353, 0.42583, 0.43317, 0.44727, 0.47380, 0.55884, 0.52479, 0.50650, 0.51194, 0.55225, 0.81658}, + {0.52038, 0.52271, 0.53000, 0.54415, 0.57064, 0.65545, 0.62172, 0.60341, 0.60885, 0.64915, 0.91339}, + {0.61724, 0.61953, 0.62694, 0.64098, 0.66756, 0.75226, 0.71862, 0.70030, 0.70575, 0.74604, 1.01035}, + {0.71460, 0.71678, 0.72376, 0.73786, 0.76447, 0.84913, 0.81551, 0.79720, 0.80264, 0.84292, 1.10723}, + {0.81101, 0.81334, 0.82066, 0.83475, 0.86127, 0.94599, 0.91240, 0.89408, 0.89952, 0.93981, 1.20409}, + {0.90788, 0.91023, 0.91752, 0.93163, 0.95815, 1.04293, 1.00929, 0.99096, 0.99640, 1.03669, 1.30106}, + {1.00477, 1.00707, 1.01449, 1.02852, 1.05504, 1.13976, 1.10617, 1.08784, 1.09329, 1.13358, 1.39796}, + {1.10166, 1.10397, 1.11130, 1.12541, 1.15257, 1.23672, 1.20307, 1.18472, 1.19018, 1.23046, 1.49486}, + {1.19854, 1.20084, 1.20818, 1.22235, 1.24885, 1.33355, 1.29992, 1.28156, 1.28707, 1.32735, 1.59177}, + {1.29544, 1.29780, 1.30507, 1.31917, 1.34575, 1.43073, 1.39681, 1.37851, 1.38396, 1.42377, 1.68854}, + {1.39236, 1.39462, 1.40205, 1.41607, 1.44259, 1.52745, 1.49368, 1.47541, 1.48083, 1.52112, 1.78546}, + {1.49314, 1.49149, 1.49885, 1.51297, 1.53949, 1.62420, 1.59016, 1.57231, 1.57772, 1.61800, 1.88048}, + {1.58610, 1.58839, 1.59572, 1.60983, 1.63635, 1.72109, 1.68651, 1.66921, 1.67463, 1.71489, 1.97918}, + {1.68400, 1.68529, 1.69261, 1.70671, 1.73331, 1.81830, 1.78341, 1.76605, 1.77150, 1.81179, 2.07608}, + {1.77991, 1.78215, 1.78952, 1.80385, 1.83014, 1.91486, 1.88128, 1.86215, 1.86837, 1.90865, 2.17304}, + {1.87674, 1.87904, 1.88647, 1.90052, 1.92712, 2.01173, 1.97812, 1.95905, 1.96527, 2.00710, 2.26979}, + {1.97369, 1.97594, 1.98326, 1.99869, 2.02442, 2.10865, 2.07501, 2.05666, 2.06214, 2.10243, 2.36669}, + {2.07052, 2.07281, 2.08016, 2.09425, 2.12132, 2.20555, 2.17182, 2.15341, 2.15904, 2.19933, 2.46363}, + {2.16742, 2.16971, 2.17707, 2.19114, 2.21766, 2.30240, 2.26877, 2.25015, 2.25573, 2.29586, 2.56060}, + {2.26423, 2.26659, 2.27396, 2.28803, 2.31456, 2.40828, 2.36567, 2.34705, 2.35282, 2.39765, 2.65744}, + {2.36153, 2.36349, 2.37330, 2.38501, 2.41159, 2.49940, 2.46257, 2.44420, 2.44843, 2.48987, 2.75431}, + {2.46558, 2.46035, 2.46822, 2.48181, 2.50849, 2.59630, 2.55947, 2.54112, 2.54513, 2.58677, 2.85094}, + {2.56248, 2.55723, 2.56486, 2.57871, 2.60520, 2.68998, 2.65626, 2.63790, 2.64316, 2.68360, 2.94813}, + {2.65178, 2.65441, 2.66153, 2.67556, 2.70210, 2.78687, 2.75319, 2.73463, 2.74032, 2.78060, 3.04503}, + {2.74868, 2.75131, 2.75870, 2.77245, 2.79385, 2.88700, 2.85009, 2.83177, 2.83723, 2.87750, 3.14193}, + {2.84574, 2.84789, 2.85560, 2.86935, 2.89075, 2.98060, 2.94576, 2.92868, 2.93356, 2.97436, 3.23868}, + {2.94239, 2.94469, 2.95221, 2.96625, 2.99345, 3.07747, 3.04266, 3.02545, 3.03051, 3.07118, 3.33555}}; + +// Garfield simulation at UD = -1600V; vd = 1.158cm/microsec, <driftfield> = 558V/cm +constexpr float time1600[ktimebin][kZbin] = { + {0.09169, 0.09203, 0.09305, 0.09473, 0.09714, 0.10032, 0.10441, 0.10990, 0.11835, 0.13986, 0.37845}, + {0.06589, 0.06626, 0.06738, 0.06924, 0.07184, 0.07521, 0.07947, 0.08512, 0.09379, 0.11603, 0.35648}, + {0.03947, 0.04003, 0.04171, 0.04434, 0.04778, 0.05190, 0.05678, 0.06304, 0.07292, 0.09876, 0.34759}, + {0.01111, 0.01281, 0.01718, 0.02281, 0.02879, 0.03477, 0.04097, 0.04910, 0.06415, 0.10573, 0.36896}, + {0.01120, 0.01311, 0.01721, 0.02279, 0.02862, 0.03446, 0.04074, 0.04981, 0.06825, 0.11595, 0.37255}, + {0.03919, 0.03980, 0.04132, 0.04380, 0.04704, 0.05102, 0.05602, 0.06302, 0.07633, 0.10896, 0.36743}, + {0.06531, 0.06528, 0.06631, 0.06805, 0.07053, 0.07392, 0.07853, 0.08505, 0.09669, 0.12578, 0.37967}, + {0.09157, 0.09171, 0.09216, 0.09301, 0.09475, 0.00000, 0.11152, 0.11879, 0.13352, 0.16802, 0.42750}, + {0.13977, 0.14095, 0.14509, 0.15433, 0.17534, 0.26406, 0.21660, 0.20345, 0.21113, 0.25067, 0.51434}, + {0.21816, 0.22041, 0.22631, 0.23850, 0.26208, 0.34340, 0.30755, 0.29237, 0.29878, 0.33863, 0.60258}, + {0.30344, 0.30547, 0.31241, 0.32444, 0.34809, 0.42696, 0.39464, 0.37919, 0.38546, 0.42530, 0.68926}, + {0.38969, 0.39164, 0.39810, 0.41059, 0.43441, 0.51246, 0.48112, 0.46562, 0.47191, 0.51172, 0.77558}, + {0.47592, 0.47799, 0.48442, 0.49689, 0.52061, 0.59855, 0.56752, 0.55201, 0.55826, 0.59808, 0.86202}, + {0.56226, 0.56428, 0.57074, 0.58324, 0.60696, 0.68483, 0.65388, 0.63837, 0.64461, 0.68445, 0.94830}, + {0.64881, 0.65063, 0.65709, 0.66958, 0.69331, 0.77117, 0.74023, 0.72472, 0.73098, 0.77079, 1.03486}, + {0.73506, 0.73698, 0.74344, 0.75596, 0.77964, 0.85751, 0.82658, 0.81107, 0.81731, 0.85712, 1.12106}, + {0.82132, 0.82333, 0.82979, 0.84228, 0.86608, 0.94386, 0.91293, 0.89742, 0.90367, 0.94335, 1.20737}, + {0.90767, 0.90968, 0.91614, 0.92863, 0.95236, 1.03021, 0.99928, 0.98377, 0.99001, 1.02984, 1.29371}, + {0.99410, 0.99602, 1.00257, 1.01498, 1.03869, 1.11720, 1.08563, 1.07011, 1.07637, 1.11621, 1.37873}, + {1.08036, 1.08240, 1.08884, 1.10138, 1.12504, 1.20301, 1.17198, 1.15647, 1.16272, 1.20255, 1.46651}, + {1.16670, 1.16872, 1.17525, 1.18783, 1.21139, 1.28934, 1.25833, 1.24281, 1.24909, 1.28889, 1.55275}, + {1.25307, 1.25510, 1.26153, 1.27404, 1.29773, 1.37584, 1.34469, 1.32916, 1.33536, 1.37524, 1.63915}, + {1.33942, 1.34146, 1.34788, 1.36040, 1.38410, 1.46438, 1.43105, 1.41537, 1.42176, 1.46158, 1.72538}, + {1.42579, 1.42782, 1.43458, 1.44674, 1.47043, 1.55085, 1.51675, 1.50168, 1.50810, 1.54793, 1.81174}, + {1.51207, 1.51454, 1.52060, 1.53307, 1.55684, 1.63478, 1.60336, 1.58820, 1.59446, 1.63414, 1.89814}, + {1.59856, 1.60047, 1.60693, 1.61942, 1.64317, 1.72257, 1.69008, 1.67454, 1.68080, 1.72063, 1.98433}, + {1.68481, 1.68682, 1.69330, 1.70584, 1.72949, 1.80752, 1.77643, 1.76089, 1.76716, 1.80692, 2.07069}, + {1.77117, 1.77319, 1.77969, 1.79260, 1.81583, 1.89376, 1.86226, 1.84720, 1.85355, 1.89256, 2.15343}, + {1.85748, 1.85967, 1.86605, 1.87848, 1.90222, 1.98010, 1.94913, 1.93271, 1.93981, 1.97968, 2.24355}, + {1.94386, 1.94587, 1.95233, 1.96484, 1.98854, 2.06646, 2.03542, 2.01755, 2.02617, 2.06604, 2.32993}, + {2.03022, 2.03230, 2.03868, 2.05134, 2.07488, 2.15367, 2.12178, 2.10391, 2.11252, 2.15432, 2.41623}, + {2.11656, 2.11857, 2.12505, 2.13772, 2.16147, 2.23919, 2.20817, 2.19265, 2.20744, 2.23872, 2.49996}, + {2.20291, 2.20611, 2.21137, 2.22387, 2.24758, 2.32563, 2.29450, 2.27901, 2.28525, 2.32507, 2.58897}, + {2.28922, 2.29172, 2.29774, 2.31345, 2.33400, 2.41287, 2.38086, 2.36535, 2.37160, 2.40869, 2.67113}, + {2.37572, 2.37764, 2.38410, 2.39803, 2.42046, 2.49817, 2.46721, 2.45171, 2.45794, 2.49505, 2.76061}, + {2.46190, 2.46396, 2.47043, 2.48340, 2.50665, 2.58453, 2.55357, 2.53728, 2.54430, 2.58407, 2.84816}, + {2.54833, 2.55032, 2.55679, 2.56976, 2.59312, 2.67103, 2.63993, 2.62364, 2.63062, 2.67040, 2.93444}, + {2.63456, 2.63660, 2.64304, 2.65555, 2.67938, 2.75739, 2.72629, 2.71064, 2.71688, 2.75671, 3.01886}}; + +// Garfield simulation at UD = -1700V; vd = 1.299cm/microsec, <driftfield> = 590V/cm +constexpr float time1700[ktimebin][kZbin] = { + {0.09167, 0.09201, 0.09302, 0.09471, 0.09712, 0.10029, 0.10438, 0.10986, 0.11832, 0.13986, 0.37824}, + {0.06591, 0.06626, 0.06736, 0.06923, 0.07183, 0.07519, 0.07944, 0.08511, 0.09378, 0.11603, 0.35625}, + {0.03946, 0.04003, 0.04170, 0.04433, 0.04777, 0.05189, 0.05676, 0.06301, 0.07291, 0.09880, 0.34724}, + {0.01110, 0.01281, 0.01718, 0.02280, 0.02879, 0.03476, 0.04096, 0.04910, 0.06417, 0.10582, 0.36861}, + {0.01115, 0.01294, 0.01721, 0.02276, 0.02862, 0.03447, 0.04074, 0.04980, 0.06812, 0.11565, 0.37231}, + {0.03920, 0.03974, 0.04133, 0.04381, 0.04706, 0.05105, 0.05603, 0.06299, 0.07618, 0.10860, 0.36646}, + {0.06498, 0.06529, 0.06634, 0.06808, 0.07055, 0.07395, 0.07852, 0.08500, 0.09650, 0.12532, 0.37850}, + {0.09143, 0.09159, 0.09207, 0.09297, 0.09473, 0.00000, 0.11102, 0.11809, 0.13245, 0.16627, 0.42496}, + {0.13646, 0.13750, 0.14112, 0.14926, 0.16806, 0.24960, 0.20627, 0.19536, 0.20366, 0.24256, 0.50557}, + {0.20678, 0.20848, 0.21384, 0.22450, 0.24552, 0.32018, 0.28740, 0.27477, 0.28196, 0.32128, 0.58475}, + {0.28287, 0.28461, 0.29020, 0.30108, 0.32224, 0.39467, 0.36500, 0.35217, 0.35926, 0.39860, 0.66194}, + {0.35972, 0.36145, 0.36713, 0.37797, 0.39912, 0.47091, 0.44212, 0.42925, 0.43632, 0.47563, 0.73892}, + {0.43667, 0.43841, 0.44413, 0.45494, 0.47607, 0.54780, 0.51912, 0.50627, 0.51334, 0.55254, 0.81595}, + {0.51365, 0.51540, 0.52101, 0.53193, 0.55305, 0.62463, 0.59617, 0.58328, 0.59035, 0.62965, 0.89303}, + {0.59064, 0.59240, 0.59801, 0.60893, 0.63009, 0.70169, 0.67317, 0.66028, 0.66735, 0.70666, 0.96995}, + {0.66765, 0.66939, 0.67501, 0.68592, 0.70724, 0.77863, 0.75016, 0.73728, 0.74435, 0.78366, 1.04696}, + {0.74464, 0.74636, 0.75200, 0.76293, 0.78405, 0.85561, 0.82716, 0.81427, 0.82133, 0.86064, 1.12396}, + {0.82165, 0.82340, 0.82902, 0.83991, 0.86104, 0.93266, 0.90414, 0.89128, 0.89834, 0.93763, 1.20100}, + {0.89863, 0.90042, 0.90659, 0.91705, 0.93805, 1.00960, 0.98115, 0.96825, 0.97533, 1.01462, 1.27801}, + {0.97563, 0.97740, 0.98310, 0.99391, 1.01504, 1.08659, 1.05814, 1.04526, 1.05233, 1.09163, 1.35503}, + {1.05276, 1.05451, 1.06002, 1.07090, 1.09099, 1.16357, 1.13516, 1.12225, 1.12933, 1.16863, 1.43195}, + {1.12977, 1.13138, 1.13700, 1.14792, 1.16797, 1.24061, 1.21212, 1.19926, 1.20626, 1.24554, 1.50900}, + {1.20664, 1.20839, 1.21400, 1.22490, 1.24606, 1.31772, 1.28914, 1.27382, 1.28329, 1.32262, 1.58550}, + {1.28367, 1.28541, 1.29099, 1.30189, 1.32312, 1.39460, 1.36612, 1.34924, 1.36030, 1.39961, 1.66310}, + {1.36064, 1.36249, 1.36799, 1.37896, 1.40004, 1.48030, 1.44314, 1.43032, 1.43731, 1.47659, 1.73442}, + {1.43762, 1.43937, 1.44497, 1.45618, 1.47704, 1.54932, 1.52012, 1.50725, 1.51430, 1.55357, 1.81708}, + {1.51462, 1.51937, 1.52203, 1.53316, 1.55403, 1.62572, 1.59713, 1.58424, 1.59128, 1.63061, 1.89406}, + {1.59162, 1.59338, 1.59947, 1.60989, 1.63103, 1.70270, 1.67411, 1.66124, 1.66799, 1.70759, 1.97103}, + {1.66874, 1.67037, 1.67597, 1.68687, 1.70814, 1.77969, 1.75112, 1.73806, 1.74530, 1.78457, 2.04794}, + {1.74693, 1.74749, 1.75297, 1.76476, 1.78500, 1.85667, 1.82811, 1.81504, 1.82101, 1.86161, 2.12492}, + {1.82260, 1.82437, 1.82995, 1.84174, 1.86202, 1.93372, 1.90509, 1.89202, 1.89930, 1.93859, 2.20189}, + {1.89964, 1.90135, 1.90693, 1.91789, 1.93952, 2.01080, 1.98207, 1.96921, 1.97628, 2.01384, 2.27887}, + {1.97662, 1.97917, 1.98611, 1.99487, 2.01601, 2.08778, 2.05846, 2.04623, 2.05330, 2.09244, 2.35585}, + {2.05359, 2.05615, 2.06309, 2.07187, 2.09867, 2.16459, 2.13610, 2.12322, 2.13029, 2.16942, 2.43199}, + {2.13063, 2.13233, 2.13795, 2.14886, 2.17008, 2.24199, 2.21310, 2.20020, 2.20727, 2.24659, 2.50983}, + {2.20761, 2.20931, 2.21955, 2.22624, 2.24708, 2.32147, 2.29009, 2.27725, 2.28276, 2.32359, 2.58680}, + {2.28459, 2.29108, 2.29202, 2.30286, 2.32007, 2.39559, 2.36683, 2.35422, 2.36119, 2.40058, 2.66081}, + {2.36153, 2.36806, 2.36889, 2.37985, 2.40092, 2.47828, 2.44381, 2.43099, 2.43819, 2.47750, 2.73779}}; + +// Garfield simulation at UD = -1800V; vd = 1.450cm/microsec, <driftfield> = 623V/cm +constexpr float time1800[ktimebin][kZbin] = { + {0.09166, 0.09199, 0.09300, 0.09470, 0.09709, 0.10026, 0.10434, 0.10983, 0.11831, 0.13987, 0.37802}, + {0.06585, 0.06623, 0.06735, 0.06921, 0.07180, 0.07520, 0.07941, 0.08507, 0.09376, 0.11604, 0.35624}, + {0.03945, 0.04004, 0.04169, 0.04432, 0.04776, 0.05187, 0.05674, 0.06300, 0.07290, 0.09884, 0.34704}, + {0.01108, 0.01287, 0.01717, 0.02280, 0.02880, 0.03476, 0.04095, 0.04909, 0.06419, 0.10589, 0.36843}, + {0.01115, 0.01287, 0.01720, 0.02276, 0.02862, 0.03448, 0.04073, 0.04973, 0.06799, 0.11535, 0.37224}, + {0.03918, 0.03975, 0.04134, 0.04382, 0.04707, 0.05105, 0.05603, 0.06296, 0.07605, 0.10822, 0.36560}, + {0.06498, 0.06532, 0.06635, 0.06809, 0.07058, 0.07395, 0.07855, 0.08495, 0.09632, 0.12488, 0.37730}, + {0.09130, 0.09160, 0.09199, 0.09300, 0.09472, 0.00000, 0.11059, 0.11747, 0.13146, 0.16462, 0.42233}, + {0.13364, 0.13449, 0.13767, 0.14481, 0.16147, 0.23635, 0.19706, 0.18812, 0.19704, 0.23520, 0.49749}, + {0.19698, 0.19844, 0.20311, 0.21236, 0.23082, 0.29920, 0.26936, 0.25927, 0.26732, 0.30601, 0.56871}, + {0.26518, 0.26670, 0.27160, 0.28099, 0.29955, 0.36597, 0.33885, 0.32858, 0.33653, 0.37524, 0.63801}, + {0.33441, 0.33553, 0.34040, 0.34987, 0.36841, 0.43428, 0.40797, 0.39763, 0.40556, 0.44425, 0.70698}, + {0.40296, 0.40447, 0.40934, 0.41881, 0.43737, 0.50306, 0.47695, 0.46662, 0.47455, 0.51329, 0.77600}, + {0.47296, 0.47344, 0.47830, 0.48779, 0.50632, 0.57200, 0.54593, 0.53559, 0.54351, 0.58222, 0.84489}, + {0.54089, 0.54264, 0.54727, 0.55673, 0.57529, 0.64094, 0.61490, 0.60457, 0.61249, 0.65118, 0.91394}, + {0.60987, 0.61138, 0.61624, 0.62573, 0.64428, 0.70989, 0.68397, 0.67354, 0.68147, 0.72015, 0.98291}, + {0.67883, 0.68035, 0.68521, 0.69469, 0.71324, 0.77896, 0.75287, 0.74251, 0.75043, 0.78912, 1.04458}, + {0.74780, 0.74932, 0.75421, 0.76367, 0.78221, 0.84785, 0.82185, 0.81148, 0.81941, 0.85811, 1.12085}, + {0.81690, 0.81830, 0.82316, 0.83263, 0.85120, 0.91683, 0.89077, 0.88045, 0.88837, 0.92707, 1.18976}, + {0.88574, 0.88726, 0.89228, 0.90198, 0.92017, 0.98578, 0.95974, 0.94947, 0.95734, 0.99604, 1.25873}, + {0.95493, 0.95624, 0.96110, 0.97058, 0.98913, 1.05481, 1.02873, 1.01839, 1.02631, 1.06503, 1.32772}, + {1.02392, 1.02524, 1.03008, 1.03955, 1.05810, 1.12378, 1.09757, 1.08605, 1.09530, 1.13399, 1.39669}, + {1.09270, 1.09418, 1.09911, 1.10854, 1.12714, 1.19281, 1.16502, 1.15633, 1.16427, 1.20271, 1.46574}, + {1.16168, 1.16323, 1.16801, 1.17772, 1.19604, 1.26190, 1.23399, 1.22531, 1.23323, 1.27194, 1.53475}, + {1.23061, 1.23214, 1.23698, 1.24669, 1.26503, 1.33073, 1.30461, 1.29428, 1.30220, 1.34091, 1.60372}, + {1.29960, 1.30110, 1.30596, 1.31544, 1.33398, 1.39962, 1.37228, 1.36323, 1.37121, 1.40988, 1.67273}, + {1.36851, 1.37007, 1.37512, 1.38441, 1.40297, 1.46865, 1.44256, 1.43222, 1.44017, 1.47878, 1.74155}, + {1.43752, 1.43904, 1.44773, 1.45338, 1.47220, 1.53759, 1.51136, 1.50119, 1.50914, 1.54775, 1.81050}, + {1.50646, 1.50802, 1.51288, 1.52237, 1.54097, 1.60697, 1.58049, 1.57018, 1.57811, 1.61678, 1.87947}, + {1.57545, 1.57720, 1.58185, 1.59134, 1.60996, 1.67787, 1.64929, 1.63914, 1.64707, 1.68570, 1.94851}, + {1.64442, 1.64617, 1.65081, 1.66035, 1.67893, 1.74684, 1.71826, 1.70745, 1.71604, 1.75310, 2.01748}, + {1.71337, 1.71513, 1.71978, 1.72932, 1.74645, 1.81346, 1.78739, 1.77642, 1.78501, 1.82151, 2.08644}, + {1.78238, 1.78410, 1.78876, 1.79824, 1.81678, 1.88332, 1.85639, 1.84262, 1.85397, 1.89270, 2.15533}, + {1.85135, 1.85306, 1.85778, 1.86728, 1.88580, 1.95615, 1.92536, 1.91171, 1.92283, 1.96165, 2.22428}, + {1.92774, 1.92184, 1.92672, 1.93618, 1.95477, 2.02048, 1.99427, 1.98068, 1.99192, 2.03062, 2.29338}, + {1.98929, 1.99081, 1.99567, 2.00515, 2.02373, 2.08987, 2.06332, 2.05249, 2.05939, 2.09928, 2.36227}, + {2.05829, 2.05978, 2.06464, 2.07414, 2.09272, 2.15850, 2.12928, 2.12194, 2.12987, 2.16825, 2.43083}, + {2.12726, 2.12869, 2.13360, 2.14425, 2.16160, 2.22872, 2.20118, 2.19078, 2.19876, 2.23416, 2.50007}}; + +// Garfield simulation at UD = -1900V; vd = 1.610cm/microsec, <driftfield> = 655V/cm +constexpr float time1900[ktimebin][kZbin] = { + {0.09166, 0.09198, 0.09298, 0.09467, 0.09707, 0.10023, 0.10431, 0.10980, 0.11828, 0.13988, 0.37789}, + {0.06584, 0.06622, 0.06735, 0.06920, 0.07179, 0.07514, 0.07938, 0.08505, 0.09374, 0.11606, 0.35599}, + {0.03945, 0.04002, 0.04169, 0.04432, 0.04775, 0.05185, 0.05672, 0.06298, 0.07290, 0.09888, 0.34695}, + {0.01109, 0.01281, 0.01717, 0.02279, 0.02878, 0.03476, 0.04094, 0.04909, 0.06421, 0.10597, 0.36823}, + {0.01115, 0.01287, 0.01720, 0.02294, 0.02862, 0.03448, 0.04074, 0.04973, 0.06783, 0.11506, 0.37206}, + {0.03940, 0.03975, 0.04135, 0.04386, 0.04708, 0.05106, 0.05604, 0.06293, 0.07592, 0.10787, 0.36484}, + {0.06500, 0.06534, 0.06638, 0.06811, 0.07060, 0.07413, 0.07852, 0.08491, 0.09614, 0.12446, 0.37626}, + {0.09119, 0.09140, 0.09194, 0.09293, 0.09471, 0.00000, 0.11013, 0.11685, 0.13050, 0.16302, 0.41991}, + {0.13111, 0.13190, 0.13466, 0.14091, 0.15554, 0.22409, 0.18846, 0.18167, 0.19113, 0.22854, 0.48995}, + {0.18849, 0.18975, 0.19380, 0.20185, 0.21797, 0.28050, 0.25368, 0.24575, 0.25446, 0.29249, 0.55442}, + {0.24995, 0.25125, 0.25563, 0.26366, 0.27986, 0.34065, 0.31605, 0.30815, 0.31680, 0.35482, 0.61697}, + {0.31187, 0.31324, 0.31745, 0.32580, 0.34205, 0.40217, 0.37825, 0.37031, 0.37897, 0.41696, 0.67890}, + {0.37401, 0.37531, 0.37955, 0.38777, 0.40395, 0.46408, 0.44037, 0.43242, 0.44108, 0.47906, 0.74122}, + {0.43610, 0.43741, 0.44161, 0.44986, 0.46604, 0.52614, 0.50248, 0.49452, 0.50316, 0.54116, 0.80326}, + {0.49820, 0.49988, 0.50372, 0.51196, 0.52814, 0.58822, 0.56459, 0.55661, 0.56527, 0.60326, 0.86526}, + {0.56032, 0.56161, 0.56582, 0.57408, 0.59024, 0.65032, 0.62670, 0.61872, 0.62737, 0.66537, 0.92749}, + {0.62240, 0.62371, 0.62792, 0.63624, 0.65236, 0.71241, 0.68881, 0.68081, 0.68947, 0.72750, 0.98941}, + {0.68449, 0.68581, 0.69002, 0.69828, 0.71444, 0.77452, 0.75089, 0.74295, 0.75157, 0.78957, 1.05157}, + {0.74660, 0.74790, 0.75212, 0.76036, 0.77654, 0.83748, 0.81299, 0.80501, 0.81193, 0.85168, 1.11375}, + {0.80870, 0.81017, 0.81423, 0.82246, 0.83867, 0.89908, 0.87509, 0.86660, 0.87577, 0.91376, 1.17586}, + {0.87080, 0.87233, 0.87632, 0.88458, 0.90074, 0.96083, 0.93718, 0.92922, 0.93787, 0.97588, 1.23794}, + {0.93291, 0.93422, 0.93844, 0.94667, 0.96293, 1.02295, 0.99929, 0.99127, 0.99997, 1.03797, 1.29995}, + {0.99500, 0.99645, 1.00308, 1.00877, 1.02497, 1.08504, 1.06140, 1.05343, 1.06203, 1.10006, 1.36216}, + {1.05712, 1.05926, 1.06262, 1.07092, 1.08706, 1.14754, 1.12350, 1.11550, 1.12417, 1.16218, 1.42427}, + {1.11921, 1.12059, 1.12473, 1.13297, 1.14916, 1.21140, 1.18560, 1.17284, 1.18625, 1.22414, 1.48629}, + {1.18140, 1.18262, 1.18690, 1.19508, 1.21125, 1.27139, 1.24164, 1.23495, 1.24838, 1.28634, 1.54852}, + {1.24340, 1.24473, 1.24901, 1.25732, 1.27336, 1.33358, 1.30793, 1.30179, 1.31047, 1.34848, 1.61066}, + {1.30551, 1.30684, 1.31104, 1.32056, 1.33553, 1.39609, 1.37004, 1.36392, 1.37045, 1.41057, 1.67259}, + {1.36755, 1.36892, 1.37315, 1.39148, 1.39755, 1.45820, 1.43215, 1.42602, 1.43467, 1.47268, 1.73477}, + {1.42966, 1.43101, 1.43549, 1.45359, 1.45976, 1.52031, 1.49601, 1.48811, 1.49677, 1.53477, 1.79691}, + {1.49180, 1.49321, 1.49760, 1.51570, 1.52175, 1.58185, 1.55771, 1.55023, 1.55888, 1.59672, 1.85501}, + {1.55391, 1.55527, 1.55943, 1.57782, 1.58391, 1.64395, 1.62008, 1.61233, 1.62085, 1.65883, 1.92091}, + {1.61599, 1.61732, 1.62154, 1.63993, 1.64612, 1.70608, 1.68237, 1.67108, 1.68301, 1.72110, 1.97931}, + {1.67808, 1.67948, 1.68364, 1.70204, 1.70823, 1.76858, 1.74404, 1.73539, 1.74512, 1.78321, 2.04522}, + {1.74019, 1.74152, 1.74573, 1.76415, 1.77015, 1.83040, 1.80615, 1.79366, 1.80723, 1.84509, 2.10742}, + {1.80235, 1.80362, 1.80783, 1.82626, 1.83227, 1.89246, 1.86795, 1.85405, 1.86938, 1.90720, 2.16953}, + {1.86442, 1.86572, 1.86994, 1.88837, 1.89438, 1.95445, 1.93006, 1.92283, 1.93148, 1.96931, 2.23147}, + {1.92700, 1.92783, 1.93197, 1.95049, 1.95649, 2.01668, 1.99217, 1.98486, 1.99352, 2.03143, 2.29358}}; + +// Garfield simulation at UD = -2000V; vd = 1.783cm/microsec, <driftfield> = 688V/cm +constexpr float time2000[ktimebin][kZbin] = { + {0.09176, 0.09196, 0.09296, 0.09465, 0.09704, 0.10020, 0.10427, 0.10977, 0.11825, 0.13988, 0.37774}, + {0.06583, 0.06620, 0.06735, 0.06918, 0.07177, 0.07513, 0.07936, 0.08503, 0.09372, 0.11606, 0.35586}, + {0.03944, 0.04001, 0.04170, 0.04431, 0.04774, 0.05184, 0.05670, 0.06296, 0.07291, 0.09893, 0.34680}, + {0.01108, 0.01281, 0.01719, 0.02279, 0.02879, 0.03474, 0.04093, 0.04908, 0.06422, 0.10605, 0.36800}, + {0.01114, 0.01287, 0.01720, 0.02276, 0.02863, 0.03449, 0.04073, 0.04970, 0.06774, 0.11478, 0.37179}, + {0.03925, 0.03977, 0.04135, 0.04386, 0.04711, 0.05108, 0.05604, 0.06290, 0.07580, 0.10748, 0.36386}, + {0.06501, 0.06536, 0.06640, 0.06814, 0.07062, 0.07398, 0.07852, 0.08487, 0.09598, 0.12405, 0.37519}, + {0.09109, 0.09127, 0.09188, 0.09292, 0.09472, 0.00000, 0.10964, 0.11630, 0.12960, 0.16150, 0.41765}, + {0.12898, 0.12968, 0.13209, 0.13749, 0.15034, 0.21286, 0.18088, 0.17590, 0.18591, 0.22254, 0.48315}, + {0.18122, 0.18227, 0.18574, 0.19263, 0.20674, 0.26376, 0.23960, 0.23375, 0.24316, 0.28047, 0.54179}, + {0.23674, 0.23784, 0.24142, 0.24847, 0.26264, 0.31810, 0.29602, 0.29008, 0.29944, 0.33675, 0.59795}, + {0.29279, 0.29382, 0.29742, 0.30448, 0.31865, 0.37364, 0.35215, 0.34629, 0.35555, 0.39286, 0.65411}, + {0.34875, 0.34987, 0.35346, 0.36054, 0.37472, 0.42956, 0.40825, 0.40229, 0.41167, 0.44894, 0.71033}, + {0.40484, 0.40594, 0.40954, 0.41660, 0.43077, 0.48560, 0.46433, 0.45840, 0.46772, 0.50500, 0.76632}, + {0.46090, 0.46201, 0.46560, 0.47267, 0.48684, 0.54167, 0.52041, 0.51449, 0.52382, 0.56108, 0.82227}, + {0.51698, 0.51809, 0.52173, 0.52874, 0.54291, 0.59776, 0.57646, 0.57052, 0.57986, 0.61717, 0.87836}, + {0.57306, 0.57418, 0.57782, 0.58513, 0.59899, 0.65380, 0.63255, 0.62661, 0.63594, 0.67325, 0.93460}, + {0.62912, 0.63024, 0.63383, 0.64103, 0.65506, 0.70988, 0.68484, 0.68267, 0.69202, 0.72878, 0.99046}, + {0.68521, 0.68633, 0.68990, 0.69699, 0.71115, 0.76595, 0.74468, 0.73872, 0.74814, 0.78538, 1.04674}, + {0.74127, 0.74239, 0.74605, 0.75303, 0.77022, 0.82204, 0.80078, 0.79484, 0.80416, 0.84147, 1.10261}, + {0.79736, 0.79846, 0.80206, 0.80947, 0.82330, 0.87813, 0.85688, 0.85087, 0.86023, 0.89752, 1.15874}, + {0.85342, 0.85454, 0.85815, 0.86519, 0.87936, 0.93417, 0.91293, 0.90428, 0.91631, 0.95360, 1.20760}, + {0.90949, 0.91061, 0.91423, 0.92128, 0.93544, 0.99026, 0.96807, 0.96305, 0.97239, 1.00967, 1.27078}, + {0.96556, 0.96669, 0.97111, 0.97734, 0.99151, 1.04664, 1.02508, 1.01879, 1.02846, 1.06167, 1.32695}, + {1.02167, 1.02279, 1.02656, 1.03341, 1.04759, 1.10242, 1.08115, 1.07003, 1.08453, 1.12184, 1.38304}, + {1.07776, 1.07883, 1.08242, 1.08950, 1.10384, 1.16422, 1.13725, 1.13133, 1.14061, 1.17793, 1.43910}, + {1.13379, 1.13492, 1.13864, 1.14567, 1.15973, 1.21455, 1.19323, 1.18734, 1.19668, 1.23401, 1.49528}, + {1.18988, 1.19098, 1.19457, 1.20164, 1.21582, 1.27064, 1.24937, 1.24044, 1.25275, 1.29004, 1.55137}, + {1.24592, 1.24706, 1.25087, 1.25773, 1.27188, 1.32670, 1.30544, 1.29953, 1.30883, 1.34613, 1.60743}, + {1.30202, 1.30313, 1.30673, 1.31381, 1.32797, 1.38278, 1.36151, 1.35167, 1.36490, 1.40221, 1.66306}, + {1.35809, 1.35921, 1.36282, 1.36986, 1.38403, 1.43888, 1.41760, 1.41174, 1.42083, 1.45830, 1.71915}, + {1.41419, 1.41528, 1.41890, 1.42595, 1.44011, 1.49496, 1.47368, 1.46769, 1.47706, 1.51436, 1.77523}, + {1.47131, 1.47141, 1.47494, 1.48850, 1.49620, 1.55137, 1.52977, 1.51820, 1.53315, 1.57042, 1.83158}, + {1.52635, 1.52750, 1.53103, 1.53814, 1.55228, 1.60736, 1.58503, 1.57986, 1.58920, 1.62649, 1.88767}, + {1.58418, 1.58355, 1.58711, 1.59526, 1.60833, 1.66316, 1.63345, 1.63261, 1.64556, 1.68204, 1.94359}, + {1.64027, 1.63958, 1.64489, 1.65024, 1.66443, 1.71925, 1.69794, 1.69201, 1.70143, 1.73865, 1.99968}, + {1.69450, 1.69566, 1.69940, 1.70697, 1.71841, 1.77819, 1.75396, 1.74814, 1.75743, 1.79083, 2.05427}, + {1.75054, 1.75221, 1.75527, 1.76306, 1.77662, 1.83428, 1.81006, 1.81173, 1.81345, 1.85076, 2.10289}}; + +// Garfield simulation at UD = -2100V; vd = 1.959cm/microsec, <driftfield> = 720V/cm +constexpr float time2100[ktimebin][kZbin] = { + {0.09160, 0.09194, 0.09294, 0.09462, 0.09701, 0.10017, 0.10424, 0.10974, 0.11823, 0.13988, 0.37762}, + {0.06585, 0.06619, 0.06731, 0.06916, 0.07174, 0.07509, 0.07933, 0.08500, 0.09370, 0.11609, 0.35565}, + {0.03960, 0.04001, 0.04171, 0.04430, 0.04774, 0.05182, 0.05668, 0.06294, 0.07291, 0.09896, 0.34676}, + {0.01109, 0.01280, 0.01716, 0.02279, 0.02876, 0.03474, 0.04096, 0.04908, 0.06424, 0.10612, 0.36790}, + {0.01114, 0.01285, 0.01719, 0.02287, 0.02863, 0.03449, 0.04073, 0.04964, 0.06759, 0.11446, 0.37162}, + {0.03922, 0.03977, 0.04146, 0.04386, 0.04711, 0.05109, 0.05605, 0.06287, 0.07575, 0.10713, 0.36298}, + {0.06504, 0.06538, 0.06641, 0.06818, 0.07064, 0.07426, 0.07852, 0.08483, 0.09581, 0.12363, 0.37424}, + {0.09103, 0.09129, 0.09186, 0.09291, 0.09476, 0.00000, 0.10923, 0.11578, 0.12873, 0.16005, 0.41525}, + {0.12723, 0.12777, 0.12988, 0.13458, 0.14579, 0.20264, 0.17421, 0.17078, 0.18132, 0.21708, 0.47699}, + {0.17508, 0.17601, 0.17897, 0.18487, 0.19698, 0.24881, 0.22737, 0.22337, 0.23348, 0.27000, 0.53032}, + {0.22571, 0.22663, 0.22969, 0.23570, 0.24787, 0.29826, 0.27871, 0.27462, 0.28471, 0.32122, 0.58166}, + {0.27664, 0.27759, 0.28067, 0.28669, 0.29891, 0.34898, 0.32982, 0.32570, 0.33576, 0.37229, 0.63268}, + {0.32766, 0.32862, 0.33170, 0.33778, 0.34988, 0.39973, 0.38088, 0.37675, 0.38680, 0.42333, 0.68159}, + {0.37872, 0.37966, 0.38275, 0.38875, 0.40093, 0.45073, 0.43192, 0.42780, 0.43786, 0.47438, 0.73480}, + {0.42974, 0.43070, 0.43378, 0.43982, 0.45196, 0.50177, 0.48297, 0.47884, 0.48889, 0.52544, 0.78581}, + {0.48081, 0.48175, 0.48482, 0.49084, 0.50302, 0.55290, 0.53398, 0.52988, 0.53994, 0.57647, 0.83687}, + {0.53645, 0.53295, 0.53586, 0.54188, 0.55408, 0.60398, 0.58504, 0.58092, 0.59100, 0.62768, 0.88773}, + {0.58345, 0.58409, 0.58690, 0.59292, 0.60510, 0.65562, 0.63609, 0.63197, 0.64203, 0.67856, 0.93907}, + {0.63397, 0.63490, 0.63795, 0.64403, 0.65613, 0.70612, 0.68714, 0.68301, 0.69294, 0.72955, 0.99000}, + {0.68496, 0.68592, 0.68899, 0.69504, 0.70733, 0.75708, 0.73818, 0.73405, 0.74412, 0.78064, 1.04100}, + {0.73600, 0.73696, 0.74003, 0.74624, 0.75828, 0.80805, 0.78904, 0.78512, 0.79517, 0.83152, 1.09205}, + {0.78709, 0.78801, 0.79108, 0.79709, 0.80931, 0.85906, 0.84027, 0.83614, 0.84621, 0.88269, 1.14058}, + {0.83808, 0.83905, 0.84215, 0.84816, 0.86031, 0.91011, 0.89139, 0.88718, 0.89725, 0.93377, 1.19413}, + {0.88916, 0.89010, 0.89320, 0.89920, 0.91136, 0.96117, 0.94235, 0.93822, 0.94828, 0.98480, 1.24538}, + {0.94036, 0.94113, 0.94422, 0.95023, 0.96241, 1.01220, 0.99310, 0.98927, 0.99933, 1.03585, 1.29629}, + {0.99139, 0.99220, 0.99525, 1.00127, 1.01344, 1.06324, 1.04451, 1.04033, 1.04836, 1.08690, 1.34727}, + {1.04261, 1.04325, 1.04628, 1.05232, 1.06448, 1.12090, 1.09546, 1.09136, 1.10142, 1.13795, 1.39831}, + {1.09331, 1.09429, 1.09742, 1.10336, 1.11557, 1.16547, 1.14658, 1.13642, 1.15247, 1.18898, 1.44936}, + {1.14436, 1.14539, 1.14847, 1.15443, 1.16662, 1.21794, 1.19763, 1.19329, 1.20349, 1.23956, 1.50043}, + {1.19533, 1.19651, 1.19943, 1.20548, 1.21666, 1.26753, 1.24862, 1.24434, 1.25455, 1.29106, 1.55142}, + {1.24638, 1.24756, 1.25046, 1.25648, 1.26764, 1.31858, 1.29967, 1.29538, 1.30499, 1.34211, 1.60250}, + {1.29747, 1.29847, 1.30175, 1.30753, 1.31869, 1.36969, 1.35069, 1.34656, 1.35663, 1.39316, 1.64644}, + {1.35537, 1.34952, 1.35255, 1.35869, 1.36973, 1.41387, 1.40173, 1.39761, 1.40768, 1.44396, 1.70238}, + {1.39956, 1.40056, 1.40380, 1.40961, 1.42178, 1.46492, 1.45278, 1.45423, 1.45872, 1.49522, 1.75557}, + {1.45080, 1.45159, 1.45463, 1.46109, 1.47287, 1.52263, 1.50382, 1.50050, 1.50977, 1.54502, 1.80670}, + {1.50165, 1.50264, 1.50570, 1.51214, 1.52233, 1.57370, 1.55484, 1.55155, 1.56080, 1.59731, 1.85778}, + {1.55269, 1.55364, 1.55675, 1.56274, 1.57491, 1.62598, 1.60590, 1.60259, 1.61185, 1.64836, 1.90883}, + {1.60368, 1.60469, 1.60779, 1.61373, 1.62596, 1.67738, 1.65651, 1.65249, 1.66290, 1.69936, 1.95959}}; + +// Garfield simulation at UD = -2200V; vd = 2.134cm/microsec, <driftfield> = 753V/cm +constexpr float time2200[ktimebin][kZbin] = { + {0.09162, 0.09194, 0.09292, 0.09460, 0.09702, 0.10014, 0.10421, 0.10971, 0.11820, 0.13990, 0.37745}, + {0.06581, 0.06618, 0.06730, 0.06915, 0.07173, 0.07507, 0.07931, 0.08497, 0.09368, 0.11609, 0.35560}, + {0.03947, 0.04001, 0.04167, 0.04429, 0.04772, 0.05183, 0.05667, 0.06293, 0.07292, 0.09900, 0.34673}, + {0.01111, 0.01280, 0.01716, 0.02279, 0.02876, 0.03473, 0.04091, 0.04907, 0.06426, 0.10620, 0.36766}, + {0.01113, 0.01285, 0.01719, 0.02276, 0.02863, 0.03452, 0.04076, 0.04960, 0.06745, 0.11419, 0.37139}, + {0.03923, 0.03978, 0.04137, 0.04387, 0.04713, 0.05110, 0.05605, 0.06284, 0.07551, 0.10677, 0.36210}, + {0.06505, 0.06540, 0.06644, 0.06820, 0.07069, 0.07401, 0.07852, 0.08479, 0.09565, 0.12325, 0.37313}, + {0.09107, 0.09127, 0.09181, 0.09291, 0.09474, 0.00000, 0.10883, 0.11528, 0.12789, 0.15865, 0.41313}, + {0.12559, 0.12622, 0.12800, 0.13206, 0.14166, 0.19331, 0.16832, 0.16632, 0.17724, 0.21218, 0.47098}, + {0.16992, 0.17070, 0.17325, 0.17831, 0.18871, 0.23557, 0.21690, 0.21451, 0.22514, 0.26082, 0.52034}, + {0.21640, 0.21722, 0.21987, 0.22500, 0.23540, 0.28097, 0.26399, 0.26154, 0.27214, 0.30784, 0.56734}, + {0.26318, 0.26400, 0.26679, 0.27181, 0.28220, 0.32739, 0.31090, 0.30842, 0.31902, 0.35474, 0.61415}, + {0.31001, 0.31085, 0.31348, 0.31866, 0.32903, 0.37412, 0.35777, 0.35546, 0.36588, 0.40159, 0.66103}, + {0.35687, 0.35769, 0.36033, 0.36556, 0.37588, 0.42094, 0.40523, 0.40214, 0.41273, 0.44841, 0.70785}, + {0.40372, 0.40457, 0.40723, 0.41234, 0.42273, 0.46778, 0.45148, 0.44903, 0.45961, 0.49526, 0.75486}, + {0.45062, 0.45139, 0.45404, 0.45966, 0.46958, 0.51470, 0.49833, 0.49584, 0.50644, 0.54211, 0.80160}, + {0.49742, 0.49825, 0.50088, 0.50605, 0.51644, 0.56148, 0.54518, 0.54270, 0.55330, 0.58897, 0.84854}, + {0.54427, 0.54510, 0.54774, 0.55290, 0.56329, 0.60846, 0.59203, 0.58955, 0.60014, 0.63578, 0.89528}, + {0.59119, 0.59199, 0.59471, 0.59975, 0.61014, 0.65533, 0.63889, 0.63636, 0.64699, 0.68269, 0.94197}, + {0.63866, 0.63880, 0.64145, 0.64664, 0.65701, 0.70639, 0.68574, 0.68325, 0.69385, 0.72949, 0.98900}, + {0.68483, 0.68566, 0.68831, 0.69347, 0.70386, 0.74890, 0.73260, 0.73010, 0.74069, 0.77638, 1.03320}, + {0.73168, 0.73255, 0.73515, 0.74031, 0.75072, 0.79576, 0.77117, 0.77501, 0.78755, 0.82318, 1.08006}, + {0.77854, 0.78310, 0.78200, 0.79525, 0.79756, 0.84281, 0.81803, 0.82393, 0.83441, 0.87008, 1.12692}, + {0.82541, 0.82642, 0.82916, 0.83528, 0.84442, 0.89086, 0.87315, 0.87079, 0.88125, 0.91694, 1.17648}, + {0.87226, 0.87308, 0.87602, 0.88086, 0.89128, 0.93772, 0.92001, 0.91751, 0.92811, 0.95587, 1.22328}, + {0.91921, 0.91994, 0.92256, 0.92772, 0.94713, 0.98566, 0.96690, 0.96436, 0.97495, 1.01064, 1.26882}, + {0.96790, 0.96679, 0.96941, 0.97463, 0.99399, 1.03001, 1.01376, 1.01112, 1.02181, 1.05749, 1.31568}, + {1.01278, 1.01390, 1.01674, 1.02147, 1.03182, 1.07820, 1.06056, 1.05798, 1.06867, 1.10433, 1.36390}, + {1.05964, 1.06076, 1.06331, 1.06833, 1.07870, 1.13297, 1.10742, 1.10520, 1.11543, 1.15120, 1.41069}, + {1.10664, 1.10762, 1.10997, 1.11519, 1.12556, 1.17531, 1.15427, 1.14620, 1.16229, 1.19805, 1.45758}, + {1.15352, 1.15421, 1.15683, 1.16218, 1.17242, 1.21910, 1.20035, 1.19863, 1.20579, 1.24473, 1.50412}, + {1.20019, 1.20115, 1.20369, 1.20892, 1.21928, 1.26913, 1.24721, 1.24549, 1.25605, 1.29159, 1.54920}, + {1.24707, 1.24846, 1.25052, 1.25602, 1.26608, 1.31558, 1.29448, 1.29232, 1.30293, 1.33675, 1.59798}, + {1.29391, 1.29475, 1.29738, 1.30255, 1.31294, 1.36244, 1.34167, 1.33918, 1.34979, 1.38229, 1.64496}, + {1.34078, 1.34304, 1.34424, 1.35565, 1.35980, 1.40930, 1.38853, 1.38229, 1.39664, 1.42863, 1.69162}, + {1.38762, 1.38847, 1.39110, 1.39627, 1.40666, 1.45183, 1.43539, 1.43289, 1.44348, 1.47549, 1.73876}, + {1.43524, 1.43533, 1.43796, 1.44310, 1.45371, 1.49305, 1.48224, 1.47941, 1.49034, 1.52601, 1.78552}, + {1.48122, 1.48219, 1.48482, 1.48991, 1.50030, 1.53991, 1.52898, 1.52653, 1.53653, 1.57282, 1.82386}}; + +//_____________________________________________________________________________ +void DiffusionAndTimeStructEstimator::SampleTimeStruct(float vdrift) +{ + // + // Samples the timing structure of a drift cell + // Drift Time data calculated with Garfield (by C.Lippmann) + // + + // Nothing to do + if (std::abs(mTimeLastVdrift - vdrift) < 1.e-3) { + return; + } + mTimeLastVdrift = vdrift; + + // Drift time maps are saved for some drift velocity values (in drift region): + constexpr float fVDsmp[8] = {1.032, 1.158, 1.299, 1.450, 1.610, 1.783, 1.959, 2.134}; + + if (vdrift < fVDsmp[0]) { + LOG(WARN) << "TRD: Drift Velocity too small " << vdrift << " < " << fVDsmp[0]; + vdrift = fVDsmp[0]; + } else if (vdrift > fVDsmp[7]) { + LOG(WARN) << "TRD: Drift Velocity too large " << vdrift << " > " << fVDsmp[6]; + vdrift = fVDsmp[7]; + } + if (vdrift > fVDsmp[6]) { + mVDlo = fVDsmp[6]; + mVDhi = fVDsmp[7]; + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time2200[ctrt][ctrz]; + } + } + } else if (vdrift > fVDsmp[5]) { + mVDlo = fVDsmp[5]; + mVDhi = fVDsmp[6]; + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; + } + } + } else if (vdrift > fVDsmp[4]) { + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; + } + } + mVDlo = fVDsmp[4]; + mVDhi = fVDsmp[5]; + } else if (vdrift > fVDsmp[3]) { + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; + } + } + mVDlo = fVDsmp[3]; + mVDhi = fVDsmp[4]; + } else if (vdrift > fVDsmp[2]) { + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; + } + } + mVDlo = fVDsmp[2]; + mVDhi = fVDsmp[3]; + } else if (vdrift > fVDsmp[1]) { + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; + } + } + mVDlo = fVDsmp[1]; + mVDhi = fVDsmp[2]; + } else if (vdrift > (fVDsmp[0] - 1.0e-5)) { + for (int ctrt = 0; ctrt < ktimebin; ctrt++) { + for (int ctrz = 0; ctrz < kZbin; ctrz++) { + mTimeStruct1[ctrt + ctrz * ktimebin] = time1500[ctrt][ctrz]; + mTimeStruct2[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; + } + } + mVDlo = fVDsmp[0]; + mVDhi = fVDsmp[1]; + } + mInvBinWidth = 1. / (mVDhi - mVDlo); +} + +//_____________________________________________________________________________ +float DiffusionAndTimeStructEstimator::TimeStruct(float vdrift, float dist, float z) +{ + // + // Applies the time structure of the drift cells (by C.Lippmann). + // The drift time of electrons to the anode wires depends on the + // distance to the wire (z) and on the position in the drift region. + // + // input : + // dist = radial distance from (cathode) pad plane [cm] + // z = distance from anode wire (parallel to cathode planes) [cm] + // + // output : + // tdrift = the drift time of an electron at the given position + // + // We interpolate between the drift time values at the two drift + // velocities fVDlo and fVDhi, being smaller and larger than + // fDriftVelocity. We use the two stored drift time maps fTimeStruct1 + // and fTimeStruct2, calculated for the two mentioned drift velocities. + // + + SampleTimeStruct(vdrift); + + // Indices: + int r1 = (int)(10 * dist); + if (r1 < 0) { + r1 = 0; + } + if (r1 > 37) { + r1 = 37; + } + int r2 = r1 + 1; + if (r2 > 37) { + r2 = 37; + } + const int kz1 = ((int)(100 * z / 2.5)); + const int kz2 = kz1 + 1; + + if ((r1 < 0) || (r1 > 37) || (kz1 < 0) || (kz1 > 10)) { + LOG(WARN) << Form("TRD: Time struct indices out of range: dist=%.2f, z=%.2f, r1=%d, kz1=%d", dist, z, r1, kz1); + } + + const float ky111 = mTimeStruct1[r1 + 38 * kz1]; + const float ky221 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct1[r2 + 38 * kz2] : mTimeStruct1[37 + 38 * 10]; + const float ky121 = (kz2 <= 10) ? mTimeStruct1[r1 + 38 * kz2] : mTimeStruct1[r1 + 38 * 10]; + const float ky211 = mTimeStruct1[r2 + 38 * kz1]; + + const float ky112 = mTimeStruct2[r1 + 38 * kz1]; + const float ky222 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct2[r2 + 38 * kz2] : mTimeStruct2[37 + 38 * 10]; + const float ky122 = (kz2 <= 10) ? mTimeStruct2[r1 + 38 * kz2] : mTimeStruct2[r1 + 38 * 10]; + const float ky212 = mTimeStruct2[r2 + 38 * kz1]; + + // Interpolation in dist-directions, lower drift time map + const float ky11 = (ky211 - ky111) * 10 * dist + ky111 - (ky211 - ky111) * r1; + const float ky21 = (ky221 - ky121) * 10 * dist + ky121 - (ky221 - ky121) * r1; + + // Interpolation in dist-direction, larger drift time map + const float ky12 = (ky212 - ky112) * 10 * dist + ky112 - (ky212 - ky112) * r1; + const float ky22 = (ky222 - ky122) * 10 * dist + ky122 - (ky222 - ky122) * r1; + + // Dist now is the drift distance to the anode wires (negative if electrons are + // between anode wire plane and cathode pad plane) + dist -= Geometry::amThick() * 0.5; + + // Interpolation in z-directions, lower drift time map + const bool condition = ((std::abs(dist) > 0.005) || (z > 0.005)); + const float ktdrift1 = + condition ? (ky21 - ky11) * 100 * z / 2.5 + ky11 - (ky21 - ky11) * kz1 : 0.0; + // Interpolation in z-directions, larger drift time map + const float ktdrift2 = + condition ? (ky22 - ky12) * 100 * z / 2.5 + ky12 - (ky22 - ky12) * kz1 : 0.0; + + // Interpolation between the values at fVDlo and fVDhi + const float a = (ktdrift2 - ktdrift1) * mInvBinWidth; // 1./(mVDhi - mVDlo); + const float b = ktdrift2 - a * mVDhi; + const float t = a * vdrift + b; + + return t; +} + +//_____________________________________________________________________________ +bool DiffusionAndTimeStructEstimator::GetDiffCoeff(float& dl, float& dt, float vdrift) +{ + // + // Calculates the diffusion coefficients in longitudinal <dl> and + // transverse <dt> direction for a given drift velocity <vdrift> + // + + // Nothing to do + if (std::abs(mDiffLastVdrift - vdrift) < 0.001) { + dl = mDiffusionL; + dt = mDiffusionT; + return true; + } + mDiffLastVdrift = vdrift; + + if (CommonParam::Instance()->IsXenon()) { + // + // Vd and B-field dependent diffusion and Lorentz angle + // + + // kNb = kNb = kNb + constexpr int kNb = 5; + + // If looking at compatibility with AliRoot: + // ibL and ibT are calculated the same way so, just define ib = ibL = ibT + int ib = ((int)(10 * (CommonParam::Instance()->GetCachedField() - 0.15))); + ib = std::max(0, ib); + ib = std::min(kNb - 1, ib); + + // DiffusionT + constexpr float p0T[kNb] = {0.009550, 0.009599, 0.009674, 0.009757, 0.009850}; + constexpr float p1T[kNb] = {0.006667, 0.006539, 0.006359, 0.006153, 0.005925}; + constexpr float p2T[kNb] = {-0.000853, -0.000798, -0.000721, -0.000635, -0.000541}; + constexpr float p3T[kNb] = {0.000131, 0.000122, 0.000111, 0.000098, 0.000085}; + // DiffusionL + constexpr float p0L[kNb] = {0.007440, 0.007493, 0.007513, 0.007672, 0.007831}; + constexpr float p1L[kNb] = {0.019252, 0.018912, 0.018636, 0.018012, 0.017343}; + constexpr float p2L[kNb] = {-0.005042, -0.004926, -0.004867, -0.004650, -0.004424}; + constexpr float p3L[kNb] = {0.000195, 0.000189, 0.000195, 0.000182, 0.000169}; + + const float v2 = vdrift * vdrift; + const float v3 = v2 * vdrift; + mDiffusionL = p0L[ib] + p1L[ib] * vdrift + p2L[ib] * v2 + p3L[ib] * v3; + mDiffusionT = p0T[ib] + p1T[ib] * vdrift + p2T[ib] * v2 + p3T[ib] * v3; + + dl = mDiffusionL; + dt = mDiffusionT; + return true; + } else if (CommonParam::Instance()->IsArgon()) { + // + // Diffusion constants and Lorentz angle only for B = 0.5T + // + mDiffusionL = 0.0182; + mDiffusionT = 0.0159; + dl = mDiffusionL; + dt = mDiffusionT; + return true; + } else { + return false; + } +} + +} // namespace o2::trd diff --git a/Detectors/TRD/base/src/FeeParam.cxx b/Detectors/TRD/base/src/FeeParam.cxx index add0a182a0886..38df171513dbf 100644 --- a/Detectors/TRD/base/src/FeeParam.cxx +++ b/Detectors/TRD/base/src/FeeParam.cxx @@ -33,15 +33,18 @@ #include <TMath.h> #include <TVirtualMC.h> #include <fairlogger/Logger.h> +#include <array> #include "DetectorsBase/GeometryManager.h" -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDPadPlane.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/PadPlane.h" #include "TRDBase/FeeParam.h" -#include "TRDBase/TRDCommonParam.h" +#include "TRDBase/CommonParam.h" +//#include "DataFormatsTRD/Constants.h" using namespace std; using namespace o2::trd; +using namespace o2::trd::constants; //_____________________________________________________________________________ @@ -55,24 +58,24 @@ bool FeeParam::mgLUTPadNumberingFilled = false; std::vector<short> FeeParam::mgLUTPadNumbering; // definition of geometry constants -std::array<float, 30> FeeParam::mgZrow = { +std::array<float, NCHAMBERPERSEC> FeeParam::mgZrow = { 301, 177, 53, -57, -181, 301, 177, 53, -57, -181, 315, 184, 53, -57, -188, 329, 191, 53, -57, -195, 343, 198, 53, -57, -202, 347, 200, 53, -57, -204}; -std::array<float, 6> FeeParam::mgX = {300.65, 313.25, 325.85, 338.45, 351.05, 363.65}; -std::array<float, 6> FeeParam::mgTiltingAngle = {-2., 2., -2., 2., -2., 2.}; +std::array<float, NLAYER> FeeParam::mgX = {300.65, 313.25, 325.85, 338.45, 351.05, 363.65}; +std::array<float, NLAYER> FeeParam::mgTiltingAngle = {-2., 2., -2., 2., -2., 2.}; int FeeParam::mgDyMax = 63; int FeeParam::mgDyMin = -64; float FeeParam::mgBinDy = 140e-4; -std::array<float, 6> FeeParam::mgWidthPad = {0.635, 0.665, 0.695, 0.725, 0.755, 0.785}; -std::array<float, 6> FeeParam::mgLengthInnerPadC1 = {7.5, 7.5, 8.0, 8.5, 9.0, 9.0}; -std::array<float, 6> FeeParam::mgLengthOuterPadC1 = {7.5, 7.5, 7.5, 7.5, 7.5, 8.5}; -std::array<float, 6> FeeParam::mgInvX; -std::array<float, 6> FeeParam::mgTiltingAngleTan; -std::array<float, 6> FeeParam::mgInvWidthPad; +std::array<float, NLAYER> FeeParam::mgWidthPad = {0.635, 0.665, 0.695, 0.725, 0.755, 0.785}; +std::array<float, NLAYER> FeeParam::mgLengthInnerPadC1 = {7.5, 7.5, 8.0, 8.5, 9.0, 9.0}; +std::array<float, NLAYER> FeeParam::mgLengthOuterPadC1 = {7.5, 7.5, 7.5, 7.5, 7.5, 8.5}; +std::array<float, NLAYER> FeeParam::mgInvX; +std::array<float, NLAYER> FeeParam::mgTiltingAngleTan; +std::array<float, NLAYER> FeeParam::mgInvWidthPad; float FeeParam::mgLengthInnerPadC0 = 9.0; float FeeParam::mgLengthOuterPadC0 = 8.0; @@ -93,8 +96,9 @@ FeeParam* FeeParam::instance() mgInstance = new FeeParam(); } // this is moved here to remove recursive calls induced by the line 2 above this one. - if (!mgLUTPadNumberingFilled) + if (!mgLUTPadNumberingFilled) { mgInstance->createPad2MCMLookUpTable(); + } return mgInstance; } @@ -127,7 +131,7 @@ FeeParam::FeeParam() : mMagField(0.), // // Default constructor // - mCP = TRDCommonParam::Instance(); + mCP = CommonParam::Instance(); // These variables are used internally in the class to elliminate divisions. // putting them at the top was messy. @@ -157,8 +161,9 @@ FeeParam::FeeParam(const FeeParam& p) // mRAWversion = p.mRAWversion; mCP = p.mCP; - if (!mgLUTPadNumberingFilled) + if (!mgLUTPadNumberingFilled) { mgInstance->createPad2MCMLookUpTable(); + } } //_____________________________________________________________________________ @@ -196,7 +201,7 @@ int FeeParam::getPadRowFromMCM(int irob, int imcm) const // Return on which pad row this mcm sits // - return mgkNmcmRobInRow * (irob / 2) + imcm / mgkNmcmRobInCol; + return NMCMROBINROW * (irob / 2) + imcm / NMCMROBINCOL; } //_____________________________________________________________________________ @@ -215,11 +220,12 @@ int FeeParam::getPadColFromADC(int irob, int imcm, int iadc) const // http://wiki.kip.uni-heidelberg.de/ti/TRD/index.php/Image:ROB_MCM_numbering.pdf // - if (iadc < 0 || iadc > mgkNadcMcm) + if (iadc < 0 || iadc > NADCMCM) { return -100; - int mcmcol = imcm % mgkNmcmRobInCol + getRobSide(irob) * mgkNmcmRobInCol; // MCM column number on ROC [0..7] - int padcol = mcmcol * mgkNcolMcm + mgkNcolMcm + 1 - iadc; - if (padcol < 0 || padcol >= mgkNcol) { + } + int mcmcol = imcm % NMCMROBINCOL + getRobSide(irob) * NMCMROBINCOL; // MCM column number on ROC [0..7] + int padcol = mcmcol * NCOLMCM + NCOLMCM + 1 - iadc; + if (padcol < 0 || padcol >= NCOLUMN) { return -1; // this is commented because of reason above OK } return padcol; @@ -234,10 +240,11 @@ int FeeParam::getExtendedPadColFromADC(int irob, int imcm, int iadc) const // so we have to introduce new virtual pad numbering scheme for this purpose. // - if (iadc < 0 || iadc > mgkNadcMcm) + if (iadc < 0 || iadc > NADCMCM) { return -100; - int mcmcol = imcm % mgkNmcmRobInCol + getRobSide(irob) * mgkNmcmRobInCol; // MCM column number on ROC [0..7] - int padcol = mcmcol * mgkNadcMcm + mgkNcolMcm + 2 - iadc; + } + int mcmcol = imcm % NMCMROBINCOL + getRobSide(irob) * NMCMROBINCOL; // MCM column number on ROC [0..7] + int padcol = mcmcol * NADCMCM + NCOLMCM + 2 - iadc; return padcol; } @@ -250,9 +257,10 @@ int FeeParam::getMCMfromPad(int irow, int icol) const // Return -1 for error. // - if (irow < 0 || icol < 0 || irow > mgkNrowC1 || icol > mgkNcol) + if (irow < 0 || icol < 0 || irow > NROWC1 || icol > NCOLUMN) { return -1; - return (icol % (mgkNcol / 2)) / mgkNcolMcm + mgkNmcmRobInCol * (irow % mgkNmcmRobInRow); + } + return (icol % (NCOLUMN / 2)) / NCOLMCM + NMCMROBINCOL * (irow % NMCMROBINROW); } //_____________________________________________________________________________ @@ -263,8 +271,9 @@ int FeeParam::getMCMfromSharedPad(int irow, int icol) const // Return -1 for error. // - if (irow < 0 || icol < 0 || irow > mgkNrowC1 || icol > mgkNcol + 8 * 3) + if (irow < 0 || icol < 0 || irow > NROWC1 || icol > NCOLUMN + 8 * 3) { return -1; + } int adc = 20 - (icol % 18) - 1; switch (adc) { @@ -282,7 +291,7 @@ int FeeParam::getMCMfromSharedPad(int irow, int icol) const break; } - return (icol % (mgkNcol / 2)) / mgkNcolMcm + mgkNmcmRobInCol * (irow % mgkNmcmRobInRow); + return (icol % (NCOLUMN / 2)) / NCOLMCM + NMCMROBINCOL * (irow % NMCMROBINROW); } //_____________________________________________________________________________ @@ -291,7 +300,7 @@ int FeeParam::getROBfromPad(int irow, int icol) const // // Return on which rob this pad is // - return (int)((int)irow / (int)mgkNmcmRobInRow) * 2 + getColSide(icol); + return (irow / NMCMROBINROW) * 2 + getColSide(icol); } //_____________________________________________________________________________ @@ -302,9 +311,9 @@ int FeeParam::getROBfromSharedPad(int irow, int icol) const // if (icol < 72) { - return (irow / mgkNmcmRobInRow) * 2 + getColSide(icol + 5); + return (irow / NMCMROBINROW) * 2 + getColSide(icol + 5); } else { - return (irow / mgkNmcmRobInRow) * 2 + getColSide(icol - 5); + return (irow / NMCMROBINROW) * 2 + getColSide(icol - 5); } } @@ -315,8 +324,9 @@ int FeeParam::getRobSide(int irob) const // Return on which side this rob sits (A side = 0, B side = 1) // - if (irob < 0 || irob >= mgkNrobC1) + if (irob < 0 || irob >= NROBC1) { return -1; + } return irob % 2; } @@ -328,16 +338,18 @@ int FeeParam::getColSide(int icol) const // Return on which side this column sits (A side = 0, B side = 1) // - if (icol < 0 || icol >= mgkNcol) + if (icol < 0 || icol >= NCOLUMN) { return -1; + } - return icol / (mgkNcol / 2); + return icol / (NCOLUMN / 2); } unsigned int FeeParam::aliToExtAli(int rob, int aliid) { - if (aliid != 127) + if (aliid != 127) { return ((1 << 10) | (rob << 7) | aliid); + } return 127; } @@ -408,12 +420,15 @@ int FeeParam::extAliToAli(unsigned int dest, unsigned short linkpair, unsigned s cmA &= gkChipmaskDefLp[linkpair]; cmB &= gkChipmaskDefLp[linkpair]; // Remove if only one side is selected - if (robAB == 1) + if (robAB == 1) { cmB = 0; - if (robAB == 2) + } + if (robAB == 2) { cmA = 0; - if (robAB == 4 && linkpair != 2) + } + if (robAB == 4 && linkpair != 2) { cmA = cmB = 0; // Restrict to only T3A and T3B + } // Finally convert chipmask to list of slaves nmcm = chipmaskToMCMlist(cmA, cmB, linkpair, mcmList, listSize); @@ -427,10 +442,12 @@ short FeeParam::getRobAB(unsigned short robsel, unsigned short linkpair) if ((robsel & 0x8) != 0) { // 1000 .. direct ROB selection. Only one of the 8 ROBs are used. robsel = robsel & 7; - if ((robsel % 2) == 0 && (robsel / 2) == linkpair) + if ((robsel % 2) == 0 && (robsel / 2) == linkpair) { return 1; // Even means A side (position 0,2,4,6) - if ((robsel % 2) == 1 && (robsel / 2) == linkpair) + } + if ((robsel % 2) == 1 && (robsel / 2) == linkpair) { return 2; // Odd means B side (position 1,3,5,7) + } return 0; } @@ -454,6 +471,100 @@ short FeeParam::getRobAB(unsigned short robsel, unsigned short linkpair) return 0; } +/* +void FeeParam::createORILookUpTable() +{ + int ori; + for(int trdstack=0;trdstack<3;trdstack++) + { + for(int side=0;side<2;side++) + { + + for(int trdlayer=5;trdlayer>=0;trdlayer++) + { + ori=trdstack*12 + (5-trdlayer + side*6) +trdlayer/6 + side; + mgAsideLUT[ori]= (trdstack<<8) + (trdlayer<<4) + side; // A side LUT to map ORI to stack/layer/side + if(ori==29) break; + + } + if(ori==29) break; + } + if(ori==29) break; + } + for(int trdstack=4;trdstack>1;trdstack--) + { + for(int side=0;side<2;side++) + { + + for(int trdlayer=5;trdlayer>=0;trdlayer++) + { + ori = (4-trdstack)*12 + (5-trdlayer + side*5) +trdlayer/6 + side; + int newside; + if(ori >=24) newside=1; else newside=side; // a hack as I am not typing this all out. + mgCsideLUT[ori]= (trdstack<<8) + (trdlayer<<4) + newside; // A side LUT to map ORI to stack/layer/side + if(ori==29) break; + } + if(ori==29) break; + } + if(ori==29) break; + } +} +*/ + +int FeeParam::getORI(int detector, int readoutboard) const +{ + int supermodule = detector / 30; + LOG(debug3) << "getORI : " << detector << " :: " << readoutboard << getORIinSM(detector, readoutboard) + 60 * detector; + return getORIinSM(detector, readoutboard) + 2 * detector; // 2 ORI per detector +} + +int FeeParam::getORIinSM(int detector, int readoutboard) const +{ + int ori = -1; + int chamberside = 0; + int trdstack = Geometry::getStack(detector); + int trdlayer = Geometry::getLayer(detector); + int side = getRobSide(readoutboard); + //see TDP for explanation of mapping TODO should probably come from CCDB for the instances where the mapping of ori fibers is misconfigured (accidental fibre swaps). + if (trdstack < 2 || (trdstack == 2 && side == 0)) // A Side + { + ori = trdstack * 12 + (5 - trdlayer + side * 5) + trdlayer / 6 + side; // <- that is correct for A side at least for now, probably not for very long LUT as that will come form CCDB ni anycase. + } else { + if (trdstack > 2 || (trdstack == 2 && side == 1)) // CSide + { + int newside = side; + if (trdstack == 2) { + newside = 0; // the last part of C side CRU is a special case. + } + ori = (4 - trdstack) * 12 + (5 - trdlayer + newside * 5) + trdlayer / 6 + newside; + } else { + LOG(warn) << " something wrong with calculation of ORI for detector " << detector << " and readoutboard" << readoutboard; + } + } + // now offset for supermodule (+60*supermodule); + + return ori; +} + +int FeeParam::getORIfromHCID(int hcid) const +{ + int detector = hcid / 2; + int side = hcid % 2; // 0 for side 0, 1 for side 1; + int ori = -1; + int chamberside = 0; + int trdstack = Geometry::getStack(detector); + int trdlayer = Geometry::getLayer(detector); + return getORIinSM(detector, side); // it takes readoutboard but only cares if its odd or even hence side here. + return 1; +} +int FeeParam::getHCIDfromORI(int ori, int readoutboard) const +{ + // ori = 60*SM+offset[0-29 A, 30-59 C] + // from the offset, we can derive the stack/layer/side combination giving the decector. + //TODO do we need this, currently I dont, others might? come back if need be. + // + return 1; +} short FeeParam::chipmaskToMCMlist(unsigned int cmA, unsigned int cmB, unsigned short linkpair, int* mcmList, int listSize) { @@ -491,22 +602,21 @@ void FeeParam::setRAWversion(int rawver) } } -/* +/* * This was originally moved here from arrayADC, signalADC etc. We now longer use those classes * so removing this for now as its crashing. -*/ + */ void FeeParam::createPad2MCMLookUpTable() { - // // Initializes the Look Up Table to relate // pad numbering and mcm channel numbering // if (!mgLUTPadNumberingFilled) { - LOG(debug) << " resizing lookup array to : " << getNcol() << " elements previously : " << mgLUTPadNumbering.size(); - mgLUTPadNumbering.resize(FeeParam::getNcol()); - memset(&mgLUTPadNumbering[0], 0, sizeof(mgLUTPadNumbering[0]) * getNcol()); + LOG(debug) << " resizing lookup array to : " << NCOLUMN << " elements previously : " << mgLUTPadNumbering.size(); + mgLUTPadNumbering.resize(NCOLUMN); + memset(&mgLUTPadNumbering[0], 0, sizeof(mgLUTPadNumbering[0]) * NCOLUMN); for (int mcm = 0; mcm < 8; mcm++) { int lowerlimit = 0 + mcm * 18; int upperlimit = 18 + mcm * 18; @@ -515,7 +625,6 @@ void FeeParam::createPad2MCMLookUpTable() mgLUTPadNumbering[index] = index + shiftposition; } } - mgLUTPadNumberingFilled = true; } } @@ -524,7 +633,7 @@ int FeeParam::getDyCorrection(int det, int rob, int mcm) const // calculate the correction of the deflection // i.e. Lorentz angle and tilt correction (if active) - int layer = det % 6; + int layer = det % NLAYER; float dyTilt = (mgDriftLength * std::tan(mgTiltingAngle[layer] * M_PI / 180.) * getLocalZ(det, rob, mcm) * mgInvX[layer]); @@ -532,8 +641,9 @@ int FeeParam::getDyCorrection(int det, int rob, int mcm) const // calculate Lorentz correction float dyCorr = -mOmegaTau * mgDriftLength; - if (mTiltCorr) + if (mTiltCorr) { dyCorr += dyTilt; // add tilt correction + } return (int)TMath::Nint(dyCorr * mgScalePad * mgInvWidthPad[layer]); } @@ -547,8 +657,9 @@ void FeeParam::getDyRange(int det, int rob, int mcm, int ch, dyMaxInt = mgDyMax; // deflection cut is considered for |B| > 0.1 T only - if (std::abs(mMagField) < 0.1) + if (std::abs(mMagField) < 0.1) { return; + } float e = 0.30; @@ -564,20 +675,22 @@ void FeeParam::getDyRange(int det, int rob, int mcm, int ch, dyMinInt = int(dyMin / mgBinDy); // clipping to allowed range - if (dyMinInt < mgDyMin) + if (dyMinInt < mgDyMin) { dyMinInt = mgDyMin; - else if (dyMinInt > mgDyMax) + } else if (dyMinInt > mgDyMax) { dyMinInt = mgDyMax; + } float dyMax = (mgDriftLength * std::tan(phi + maxDeflAngle)); dyMaxInt = int(dyMax / mgBinDy); // clipping to allowed range - if (dyMaxInt > mgDyMax) + if (dyMaxInt > mgDyMax) { dyMaxInt = mgDyMax; - else if (dyMaxInt < mgDyMin) + } else if (dyMaxInt < mgDyMin) { dyMaxInt = mgDyMin; + } } else if (maxDeflTemp < 0.) { // this must not happen printf("Inconsistent calculation of sin(alpha): %f\n", maxDeflTemp); @@ -598,7 +711,7 @@ float FeeParam::getElongation(int det, int rob, int mcm, int ch) const // calculate the ratio of the distance to the primary vertex and the // distance in x-direction for the given ADC channel - int layer = det % 6; + int layer = det % NLAYER; float elongation = std::abs(getDist(det, rob, mcm, ch) * mgInvX[layer]); @@ -614,8 +727,9 @@ void FeeParam::getCorrectionFactors(int det, int rob, int mcm, int ch, { // calculate the gain correction factors for the given ADC channel float Invgain = 1.0; - if (mPidGainCorr == true) + if (mPidGainCorr == true) { Invgain = 1 / gain; + } if (mPidTracklengthCorr == true) { float InvElongationOverGain = 1 / getElongation(det, rob, mcm, ch) * Invgain; @@ -638,7 +752,7 @@ float FeeParam::getX(int det, int /* rob */, int /* mcm */) const { // return the distance to the beam axis in x-direction - int layer = det % 6; + int layer = det % NLAYER; return mgX[layer]; } @@ -646,7 +760,7 @@ float FeeParam::getLocalY(int det, int rob, int mcm, int ch) const { // get local y-position (r-phi) w.r.t. the chamber centre - int layer = det % 6; + int layer = det % NLAYER; // calculate the pad position as in the TRAP float ypos = (-4 + 1 + (rob & 0x1) * 4 + (mcm & 0x3)) * 18 - ch - 0.5; // y position in bins of pad widths return ypos * mgWidthPad[layer]; @@ -656,24 +770,26 @@ float FeeParam::getLocalZ(int det, int rob, int mcm) const { // get local z-position w.r.t. to the chamber boundary - int stack = (det % 30) / 6; - int layer = det % 6; + int stack = (det % NCHAMBERPERSEC) / NLAYER; + int layer = det % NLAYER; int row = (rob / 2) * 4 + mcm / 4; if (stack == 2) { - if (row == 0) - return (mgZrow[layer * 6 + stack] - 0.5 * mgLengthOuterPadC0); - else if (row == 11) - return (mgZrow[layer * 6 + stack] - 1.5 * mgLengthOuterPadC0 - (row - 1) * mgLengthInnerPadC0); - else - return (mgZrow[layer * 6 + stack] - mgLengthOuterPadC0 - (row - 0.5) * mgLengthInnerPadC0); + if (row == 0) { + return (mgZrow[layer * NLAYER + stack] - 0.5 * mgLengthOuterPadC0); + } else if (row == 11) { + return (mgZrow[layer * NLAYER + stack] - 1.5 * mgLengthOuterPadC0 - (row - 1) * mgLengthInnerPadC0); + } else { + return (mgZrow[layer * NLAYER + stack] - mgLengthOuterPadC0 - (row - 0.5) * mgLengthInnerPadC0); + } } else { - if (row == 0) - return (mgZrow[layer * 6 + stack] - 0.5 * mgLengthOuterPadC1[layer]); - else if (row == 15) - return (mgZrow[layer * 6 + stack] - 1.5 * mgLengthOuterPadC1[layer] - (row - 1) * mgLengthInnerPadC1[layer]); - else - return (mgZrow[layer * 6 + stack] - mgLengthOuterPadC1[layer] - (row - 0.5) * mgLengthInnerPadC1[layer]); + if (row == 0) { + return (mgZrow[layer * NLAYER + stack] - 0.5 * mgLengthOuterPadC1[layer]); + } else if (row == 15) { + return (mgZrow[layer * NLAYER + stack] - 1.5 * mgLengthOuterPadC1[layer] - (row - 1) * mgLengthInnerPadC1[layer]); + } else { + return (mgZrow[layer * NLAYER + stack] - mgLengthOuterPadC1[layer] - (row - 0.5) * mgLengthInnerPadC1[layer]); + } } } diff --git a/Detectors/TRD/base/src/Geometry.cxx b/Detectors/TRD/base/src/Geometry.cxx new file mode 100644 index 0000000000000..361644709bf73 --- /dev/null +++ b/Detectors/TRD/base/src/Geometry.cxx @@ -0,0 +1,2773 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <TGeoManager.h> +#include <TGeoPhysicalNode.h> +#include <TMath.h> +#include <TVirtualMC.h> +#include <FairLogger.h> + +#include "DetectorsBase/GeometryManager.h" +#include "MathUtils/Utils.h" // math_utils::bit2Mask +#include "TRDBase/Geometry.h" +#include "TRDBase/PadPlane.h" + +using namespace o2::trd; +using namespace o2::trd::constants; + +//_____________________________________________________________________________ + +const o2::detectors::DetID Geometry::sDetID(o2::detectors::DetID::TRD); + +//_____________________________________________________________________________ +Geometry::Geometry() : GeometryBase(), o2::detectors::DetMatrixCacheIndirect(sDetID) +{ + createPadPlaneArray(); +} + +//_____________________________________________________________________________ +bool Geometry::rotateBack(int det, const float* const loc, float* glb) const +{ + // + // Rotates a chambers to transform the corresponding local frame + // coordinates <loc> into the coordinates of the ALICE restframe <glb>. + // + + int sector = getSector(det); + float phi = 2.0 * TMath::Pi() / (float)NSECTOR * ((float)sector + 0.5); + + glb[0] = loc[0] * TMath::Cos(phi) - loc[1] * TMath::Sin(phi); + glb[1] = loc[0] * TMath::Sin(phi) + loc[1] * TMath::Cos(phi); + glb[2] = loc[2]; + + return true; +} + +//_____________________________________________________________________________ +void Geometry::createPadPlaneArray() +{ + // + // Creates the array of PadPlane objects + // + + for (int ilayer = 0; ilayer < NLAYER; ilayer++) { + for (int istack = 0; istack < NSTACK; istack++) { + createPadPlane(ilayer, istack); + } + } +} + +//_____________________________________________________________________________ +void Geometry::createPadPlane(int ilayer, int istack) +{ + // + // Creates an PadPlane object + // + int ipp = getDetectorSec(ilayer, istack); + auto& padPlane = mPadPlanes[ipp]; + + padPlane.setLayer(ilayer); + padPlane.setStack(istack); + + padPlane.setRowSpacing(0.0); + padPlane.setColSpacing(0.0); + + padPlane.setLengthRim(1.0); + padPlane.setWidthRim(0.5); + + padPlane.setNcols(144); + + padPlane.setAnodeWireOffset(0.25); + + // + // The pad plane parameter + // + const float kTiltAngle = 2.0; + switch (ilayer) { + case 0: + if (istack == 2) { + // L0C0 type + padPlane.setNrows(12); + padPlane.setLength(108.0); + padPlane.setLengthOPad(8.0); + padPlane.setLengthIPad(9.0); + } else { + // L0C1 type + padPlane.setNrows(16); + padPlane.setLength(122.0); + padPlane.setLengthOPad(7.5); + padPlane.setLengthIPad(7.5); + } + padPlane.setWidth(92.2); + padPlane.setWidthOPad(0.515); + padPlane.setWidthIPad(0.635); + padPlane.setTiltingAngle(-kTiltAngle); + break; + case 1: + if (istack == 2) { + // L1C0 type + padPlane.setNrows(12); + padPlane.setLength(108.0); + padPlane.setLengthOPad(8.0); + padPlane.setLengthIPad(9.0); + } else { + // L1C1 type + padPlane.setNrows(16); + padPlane.setLength(122.0); + padPlane.setLengthOPad(7.5); + padPlane.setLengthIPad(7.5); + } + padPlane.setWidth(96.6); + padPlane.setWidthOPad(0.585); + padPlane.setWidthIPad(0.665); + padPlane.setTiltingAngle(kTiltAngle); + break; + case 2: + if (istack == 2) { + // L2C0 type + padPlane.setNrows(12); + padPlane.setLength(108.0); + padPlane.setLengthOPad(8.0); + padPlane.setLengthIPad(9.0); + } else { + // L2C1 type + padPlane.setNrows(16); + padPlane.setLength(129.0); + padPlane.setLengthOPad(7.5); + padPlane.setLengthIPad(8.0); + } + padPlane.setWidth(101.1); + padPlane.setWidthOPad(0.705); + padPlane.setWidthIPad(0.695); + padPlane.setTiltingAngle(-kTiltAngle); + break; + case 3: + if (istack == 2) { + // L3C0 type + padPlane.setNrows(12); + padPlane.setLength(108.0); + padPlane.setLengthOPad(8.0); + padPlane.setLengthIPad(9.0); + } else { + // L3C1 type + padPlane.setNrows(16); + padPlane.setLength(136.0); + padPlane.setLengthOPad(7.5); + padPlane.setLengthIPad(8.5); + } + padPlane.setWidth(105.5); + padPlane.setWidthOPad(0.775); + padPlane.setWidthIPad(0.725); + padPlane.setTiltingAngle(kTiltAngle); + break; + case 4: + if (istack == 2) { + // L4C0 type + padPlane.setNrows(12); + padPlane.setLength(108.0); + padPlane.setLengthOPad(8.0); + } else { + // L4C1 type + padPlane.setNrows(16); + padPlane.setLength(143.0); + padPlane.setLengthOPad(7.5); + } + padPlane.setWidth(109.9); + padPlane.setWidthOPad(0.845); + padPlane.setLengthIPad(9.0); + padPlane.setWidthIPad(0.755); + padPlane.setTiltingAngle(-kTiltAngle); + break; + case 5: + if (istack == 2) { + // L5C0 type + padPlane.setNrows(12); + padPlane.setLength(108.0); + padPlane.setLengthOPad(8.0); + } else { + // L5C1 type + padPlane.setNrows(16); + padPlane.setLength(145.0); + padPlane.setLengthOPad(8.5); + } + padPlane.setWidth(114.4); + padPlane.setWidthOPad(0.965); + padPlane.setLengthIPad(9.0); + padPlane.setWidthIPad(0.785); + padPlane.setTiltingAngle(kTiltAngle); + break; + }; + + // + // The positions of the borders of the pads + // + // Row direction + // + float row = CLENGTH[ilayer][istack] / 2.0 - RPADW - padPlane.getLengthRim(); + for (int ir = 0; ir < padPlane.getNrows(); ir++) { + padPlane.setPadRow(ir, row); + row -= padPlane.getRowSpacing(); + if (ir == 0) { + row -= padPlane.getLengthOPad(); + } else { + row -= padPlane.getLengthIPad(); + } + } + // + // Column direction + // + float col = -CWIDTH[ilayer] / 2.0 - CROW + padPlane.getWidthRim(); + for (int ic = 0; ic < padPlane.getNcols(); ic++) { + padPlane.setPadCol(ic, col); + col += padPlane.getColSpacing(); + if (ic == 0) { + col += padPlane.getWidthOPad(); + } else { + col += padPlane.getWidthIPad(); + } + } + // Calculate the offset to translate from the local ROC system into + // the local supermodule system, which is used for clusters + float rowTmp = CLENGTH[ilayer][0] + CLENGTH[ilayer][1] + CLENGTH[ilayer][2] / 2.0; + for (int jstack = 0; jstack < istack; jstack++) { + rowTmp -= CLENGTH[ilayer][jstack]; + } + padPlane.setPadRowSMOffset(rowTmp - CLENGTH[ilayer][istack] / 2.0); +} + +void Geometry::createVolume(const char* name, const char* shape, int nmed, float* upar, int np) +{ + TVirtualMC::GetMC()->Gsvolu(name, shape, nmed, upar, np); + + // add to sensitive volumes for TRD if matching criterion + // these are coded with J+K in the second character (according to AliTRDv1.cxx of AliROOT) + if (name[1] == 'J' || name[1] == 'K') { + mSensitiveVolumeNames.emplace_back(name); + } +} + +//_____________________________________________________________________________ +void Geometry::createGeometry(std::vector<int> const& idtmed) +{ + // + // Create the TRD geometry + + if (!gGeoManager) { + // RSTODO: in future there will be a method to load matrices from the CDB + LOG(FATAL) << "Geometry is not loaded"; + } + + createVolumes(idtmed); +} + +void Geometry::createVolumes(std::vector<int> const& idtmed) +{ + // + // Create the TRD geometry volumes + // + // + // Names of the TRD volumina (xx = detector number): + // + // Volume (Air) wrapping the readout chamber components + // UTxx includes: UAxx, UDxx, UFxx, UUxx + // + // Lower part of the readout chambers (drift volume + radiator) + // UAxx Aluminum frames (Al) + // + // Upper part of the readout chambers (readout plane + fee) + // UDxx Wacosit frames of amp. region (Wacosit) + // UFxx Aluminum frame of back panel (Al) + // + // Services on chambers (cooling, cables, MCMs, DCS boards, ...) + // UUxx Volume containing the services (Air) + // + // Material layers inside sensitive area: + // Name Description Mat. Thick. Dens. Radl. X/X_0 + // + // URMYxx Mylar layers (x2) Mylar 0.0015 1.39 28.5464 0.005% + // URCBxx Carbon layer (x2) Carbon 0.0055 1.75 24.2824 0.023% + // URGLxx Glue on the carbon layers (x2) Araldite 0.0065 1.12 37.0664 0.018% + // URRHxx Rohacell layer (x2) Rohacell 0.8 0.075 536.005 0.149% + // URFBxx Fiber mat layer PP 3.186 0.068 649.727 0.490% + // + // UJxx Drift region Xe/CO2 3.0 0.00495 1792.37 0.167% + // UKxx Amplification region Xe/CO2 0.7 0.00495 1792.37 0.039% + // UWxx Wire planes (x2) Copper 0.00011 8.96 1.43503 0.008% + // + // UPPDxx Copper of pad plane Copper 0.0025 8.96 1.43503 0.174% + // UPPPxx PCB of pad plane G10 0.0356 2.0 14.9013 0.239% + // UPGLxx Glue on pad planes Araldite 0.0923 1.12 37.0664 0.249% + // + add. glue (ca. 600g) Araldite 0.0505 1.12 37.0663 0.107% + // UPCBxx Carbon fiber mats (x2) Carbon 0.019 1.75 24.2824 0.078% + // UPHCxx Honeycomb structure Aramide 2.0299 0.032 1198.84 0.169% + // UPPCxx PCB of readout board G10 0.0486 2.0 14.9013 0.326% + // UPRDxx Copper of readout board Copper 0.0057 8.96 1.43503 0.404% + // UPELxx Electronics + cables Copper 0.0029 8.96 1.43503 0.202% + // + + const int kNparTrd = 4; + const int kNparCha = 3; + + float xpos; + float ypos; + float zpos; + + float parTrd[kNparTrd]; + float parCha[kNparCha]; + + const int kTag = 100; + char cTagV[kTag]; + char cTagM[kTag]; + + // There are three TRD volumes for the supermodules in order to accomodate + // the different arrangements in front of PHOS + // UTR1: Default supermodule + // UTR2: Supermodule in front of PHOS with double carbon cover + // UTR3: As UTR2, but w/o middle stack + // UTR4: Sector 17 with missing chamber L4S4 + // + // The mother volume for one sector (Air), full length in z-direction + // Provides material for side plates of super module + parTrd[0] = SWIDTH1 / 2.0; + parTrd[1] = SWIDTH2 / 2.0; + parTrd[2] = SLENGTH / 2.0; + parTrd[3] = SHEIGHT / 2.0; + createVolume("UTR1", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTR2", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTR3", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTR4", "TRD1", idtmed[2], parTrd, kNparTrd); + // The outer aluminum plates of the super module (Al) + parTrd[0] = SWIDTH1 / 2.0; + parTrd[1] = SWIDTH2 / 2.0; + parTrd[2] = SLENGTH / 2.0; + parTrd[3] = SHEIGHT / 2.0; + createVolume("UTS1", "TRD1", idtmed[1], parTrd, kNparTrd); + createVolume("UTS2", "TRD1", idtmed[1], parTrd, kNparTrd); + createVolume("UTS3", "TRD1", idtmed[1], parTrd, kNparTrd); + createVolume("UTS4", "TRD1", idtmed[1], parTrd, kNparTrd); + // The inner part of the TRD mother volume for one sector (Air), + // full length in z-direction + parTrd[0] = SWIDTH1 / 2.0 - SMPLTT; + parTrd[1] = SWIDTH2 / 2.0 - SMPLTT; + parTrd[2] = SLENGTH / 2.0; + parTrd[3] = SHEIGHT / 2.0 - SMPLTT; + createVolume("UTI1", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTI2", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTI3", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTI4", "TRD1", idtmed[2], parTrd, kNparTrd); + + // The inner part of the TRD mother volume for services in front + // of the supermodules (Air), + parTrd[0] = SWIDTH1 / 2.0; + parTrd[1] = SWIDTH2 / 2.0; + parTrd[2] = FLENGTH / 2.0; + parTrd[3] = SHEIGHT / 2.0; + createVolume("UTF1", "TRD1", idtmed[2], parTrd, kNparTrd); + createVolume("UTF2", "TRD1", idtmed[2], parTrd, kNparTrd); + + for (int istack = 0; istack < NSTACK; istack++) { + for (int ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + + // The lower part of the readout chambers (drift volume + radiator) + // The aluminum frames + snprintf(cTagV, kTag, "UA%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; + parCha[2] = CRAH / 2.0 + CDRH / 2.0; + createVolume(cTagV, "BOX ", idtmed[1], parCha, kNparCha); + // The additional aluminum on the frames + // This part has not the correct shape but is just supposed to + // represent the missing material. The correct form of the L-shaped + // profile would not fit into the alignable volume. + snprintf(cTagV, kTag, "UZ%02d", iDet); + parCha[0] = CALWMOD / 2.0; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; + parCha[2] = CALHMOD / 2.0; + createVolume(cTagV, "BOX ", idtmed[1], parCha, kNparCha); + // The additional Wacosit on the frames + snprintf(cTagV, kTag, "UP%02d", iDet); + parCha[0] = CWSW / 2.0; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; + parCha[2] = CWSH / 2.0; + createVolume(cTagV, "BOX ", idtmed[7], parCha, kNparCha); + // The Wacosit frames + snprintf(cTagV, kTag, "UB%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 - CALT; + parCha[1] = -1.0; + parCha[2] = -1.0; + createVolume(cTagV, "BOX ", idtmed[7], parCha, kNparCha); + // The glue around the radiator + snprintf(cTagV, kTag, "UX%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 - CALT - CCLST; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCLFT; + parCha[2] = CRAH / 2.0; + createVolume(cTagV, "BOX ", idtmed[11], parCha, kNparCha); + // The inner part of radiator (air) + snprintf(cTagV, kTag, "UC%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 - CALT - CCLST - CGLT; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCLFT - CGLT; + parCha[2] = -1.0; + createVolume(cTagV, "BOX ", idtmed[2], parCha, kNparCha); + + // The upper part of the readout chambers (amplification volume) + // The Wacosit frames + snprintf(cTagV, kTag, "UD%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 + CROW; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; + parCha[2] = CAMH / 2.0; + createVolume(cTagV, "BOX ", idtmed[7], parCha, kNparCha); + // The inner part of the Wacosit frame (air) + snprintf(cTagV, kTag, "UE%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 + CROW - CCUTB; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCUTA; + parCha[2] = -1.; + createVolume(cTagV, "BOX ", idtmed[2], parCha, kNparCha); + + // The back panel, including pad plane and readout boards + // The aluminum frames + snprintf(cTagV, kTag, "UF%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 + CROW; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; + parCha[2] = CROH / 2.0; + createVolume(cTagV, "BOX ", idtmed[1], parCha, kNparCha); + // The inner part of the aluminum frames + snprintf(cTagV, kTag, "UG%02d", iDet); + parCha[0] = CWIDTH[ilayer] / 2.0 + CROW - CAUT; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CAUT; + parCha[2] = -1.0; + createVolume(cTagV, "BOX ", idtmed[2], parCha, kNparCha); + + // + // The material layers inside the chambers + // + + // Mylar layer (radiator) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = RMYTHICK / 2.0; + snprintf(cTagV, kTag, "URMY%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[27], parCha, kNparCha); + // Carbon layer (radiator) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = RCBTHICK / 2.0; + snprintf(cTagV, kTag, "URCB%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[26], parCha, kNparCha); + // Araldite layer (radiator) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = RGLTHICK / 2.0; + snprintf(cTagV, kTag, "URGL%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[11], parCha, kNparCha); + // Rohacell layer (radiator) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = RRHTHICK / 2.0; + snprintf(cTagV, kTag, "URRH%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[15], parCha, kNparCha); + // Fiber layer (radiator) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = RFBTHICK / 2.0; + snprintf(cTagV, kTag, "URFB%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[28], parCha, kNparCha); + + // Xe/Isobutane layer (drift volume) + parCha[0] = CWIDTH[ilayer] / 2.0 - CALT - CCLST; + parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCLFT; + parCha[2] = DRTHICK / 2.0; + snprintf(cTagV, kTag, "UJ%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[9], parCha, kNparCha); + + // Xe/Isobutane layer (amplification volume) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = AMTHICK / 2.0; + snprintf(cTagV, kTag, "UK%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[9], parCha, kNparCha); + // Cu layer (wire plane) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = WRTHICK / 2.0; + snprintf(cTagV, kTag, "UW%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[3], parCha, kNparCha); + + // Cu layer (pad plane) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PPDTHICK / 2.0; + snprintf(cTagV, kTag, "UPPD%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[5], parCha, kNparCha); + // G10 layer (pad plane) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PPPTHICK / 2.0; + snprintf(cTagV, kTag, "UPPP%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[13], parCha, kNparCha); + // Araldite layer (glue) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PGLTHICK / 2.0; + snprintf(cTagV, kTag, "UPGL%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[11], parCha, kNparCha); + // Carbon layer (carbon fiber mats) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PCBTHICK / 2.0; + snprintf(cTagV, kTag, "UPCB%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[26], parCha, kNparCha); + // Aramide layer (honeycomb) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PHCTHICK / 2.0; + snprintf(cTagV, kTag, "UPHC%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[10], parCha, kNparCha); + // G10 layer (PCB readout board) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PPCTHICK / 2; + snprintf(cTagV, kTag, "UPPC%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[13], parCha, kNparCha); + // Cu layer (traces in readout board) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PRBTHICK / 2.0; + snprintf(cTagV, kTag, "UPRB%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[6], parCha, kNparCha); + // Cu layer (other material on in readout board, incl. screws) + parCha[0] = -1.0; + parCha[1] = -1.0; + parCha[2] = PELTHICK / 2.0; + snprintf(cTagV, kTag, "UPEL%02d", iDet); + createVolume(cTagV, "BOX ", idtmed[4], parCha, kNparCha); + + // + // Position the layers in the chambers + // + xpos = 0.0; + ypos = 0.0; + + // Lower part + // Mylar layers (radiator) + zpos = RMYTHICK / 2.0 - CRAH / 2.0; + snprintf(cTagV, kTag, "URMY%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + zpos = -RMYTHICK / 2.0 + CRAH / 2.0; + snprintf(cTagV, kTag, "URMY%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Carbon layers (radiator) + zpos = RCBTHICK / 2.0 + RMYTHICK - CRAH / 2.0; + snprintf(cTagV, kTag, "URCB%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + zpos = -RCBTHICK / 2.0 - RMYTHICK + CRAH / 2.0; + snprintf(cTagV, kTag, "URCB%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Carbon layers (radiator) + zpos = RGLTHICK / 2.0 + RCBTHICK + RMYTHICK - CRAH / 2.0; + snprintf(cTagV, kTag, "URGL%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + zpos = -RGLTHICK / 2.0 - RCBTHICK - RMYTHICK + CRAH / 2.0; + snprintf(cTagV, kTag, "URGL%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Rohacell layers (radiator) + zpos = RRHTHICK / 2.0 + RGLTHICK + RCBTHICK + RMYTHICK - CRAH / 2.0; + snprintf(cTagV, kTag, "URRH%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + zpos = -RRHTHICK / 2.0 - RGLTHICK - RCBTHICK - RMYTHICK + CRAH / 2.0; + snprintf(cTagV, kTag, "URRH%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Fiber layers (radiator) + zpos = 0.0; + snprintf(cTagV, kTag, "URFB%02d", iDet); + snprintf(cTagM, kTag, "UC%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + // Xe/Isobutane layer (drift volume) + zpos = DRZPOS; + snprintf(cTagV, kTag, "UJ%02d", iDet); + snprintf(cTagM, kTag, "UB%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + // Upper part + // Xe/Isobutane layer (amplification volume) + zpos = AMZPOS; + snprintf(cTagV, kTag, "UK%02d", iDet); + snprintf(cTagM, kTag, "UE%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Cu layer (wire planes inside amplification volume) + zpos = WRZPOSA; + snprintf(cTagV, kTag, "UW%02d", iDet); + snprintf(cTagM, kTag, "UK%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + zpos = WRZPOSB; + snprintf(cTagV, kTag, "UW%02d", iDet); + snprintf(cTagM, kTag, "UK%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + // Back panel + pad plane + readout part + // Cu layer (pad plane) + zpos = PPDTHICK / 2.0 - CROH / 2.0; + snprintf(cTagV, kTag, "UPPD%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // G10 layer (pad plane) + zpos = PPPTHICK / 2.0 + PPDTHICK - CROH / 2.0; + snprintf(cTagV, kTag, "UPPP%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Araldite layer (glue) + zpos = PGLTHICK / 2.0 + PPPTHICK + PPDTHICK - CROH / 2.0; + snprintf(cTagV, kTag, "UPGL%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Carbon layers (carbon fiber mats) + zpos = PCBTHICK / 2.0 + PGLTHICK + PPPTHICK + PPDTHICK - CROH / 2.0; + snprintf(cTagV, kTag, "UPCB%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + zpos = -PCBTHICK / 2.0 - PPCTHICK - PRBTHICK - PELTHICK + CROH / 2.0; + snprintf(cTagV, kTag, "UPCB%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Aramide layer (honeycomb) + zpos = PHCTHICK / 2.0 + PCBTHICK + PGLTHICK + PPPTHICK + PPDTHICK - CROH / 2.0; + snprintf(cTagV, kTag, "UPHC%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // G10 layer (PCB readout board) + zpos = -PPCTHICK / 2.0 - PRBTHICK - PELTHICK + CROH / 2.0; + snprintf(cTagV, kTag, "UPPC%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Cu layer (traces in readout board) + zpos = -PRBTHICK / 2.0 - PELTHICK + CROH / 2.0; + snprintf(cTagV, kTag, "UPRB%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // Cu layer (other materials on readout board, incl. screws) + zpos = -PELTHICK / 2.0 + CROH / 2.0; + snprintf(cTagV, kTag, "UPEL%02d", iDet); + snprintf(cTagM, kTag, "UG%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + // Position the inner volumes of the chambers in the frames + xpos = 0.0; + ypos = 0.0; + + // The inner part of the radiator (air) + zpos = 0.0; + snprintf(cTagV, kTag, "UC%02d", iDet); + snprintf(cTagM, kTag, "UX%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // The glue around the radiator + zpos = CRAH / 2.0 - CDRH / 2.0 - CRAH / 2.0; + snprintf(cTagV, kTag, "UX%02d", iDet); + snprintf(cTagM, kTag, "UB%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + // The lower Wacosit frame inside the aluminum frame + zpos = 0.0; + snprintf(cTagV, kTag, "UB%02d", iDet); + snprintf(cTagM, kTag, "UA%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + // The inside of the upper Wacosit frame + zpos = 0.0; + snprintf(cTagV, kTag, "UE%02d", iDet); + snprintf(cTagM, kTag, "UD%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + // The inside of the upper aluminum frame + zpos = 0.0; + snprintf(cTagV, kTag, "UG%02d", iDet); + snprintf(cTagM, kTag, "UF%02d", iDet); + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + } + } + + // Create the volumes of the super module frame + createFrame(idtmed); + + // Create the volumes of the services + createServices(idtmed); + + for (int istack = 0; istack < NSTACK; istack++) { + for (int ilayer = 0; ilayer < NLAYER; ilayer++) { + assembleChamber(ilayer, istack); + } + } + + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTI1", 1, "UTS1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTI2", 1, "UTS2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTI3", 1, "UTS3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTI4", 1, "UTS4", xpos, ypos, zpos, 0, "ONLY"); + + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTS1", 1, "UTR1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTS2", 1, "UTR2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTS3", 1, "UTR3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTS4", 1, "UTR4", xpos, ypos, zpos, 0, "ONLY"); + + // Put the TRD volumes into the space frame mother volumes + // if enabled via status flag + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + for (int isector = 0; isector < NSECTOR; isector++) { + if (getSMstatus(isector)) { + snprintf(cTagV, kTag, "BTRD%d", isector); + switch (isector) { + case 17: + // Missing L4S4 chamber + TVirtualMC::GetMC()->Gspos("UTR4", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); + break; + case 13: + case 14: + case 15: + // Double carbon, w/o middle stack + TVirtualMC::GetMC()->Gspos("UTR3", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); + break; + case 11: + case 12: + // Double carbon, all stacks + TVirtualMC::GetMC()->Gspos("UTR2", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); + break; + default: + // Standard supermodule + TVirtualMC::GetMC()->Gspos("UTR1", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); + }; + } + } + + // Put the TRD volumes into the space frame mother volumes + // if enabled via status flag + xpos = 0.0; + ypos = 0.5 * SLENGTH + 0.5 * FLENGTH; + zpos = 0.0; + for (int isector = 0; isector < NSECTOR; isector++) { + if (getSMstatus(isector)) { + snprintf(cTagV, kTag, "BTRD%d", isector); + TVirtualMC::GetMC()->Gspos("UTF1", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTF2", 1, cTagV, xpos, -ypos, zpos, 0, "ONLY"); + } + } + + // Resolve runtime shapes (which is done as part of TGeoManager::CheckGeometry) NOW. + // This is otherwise done when saying gGeoManager->CloseGeometry(). + // However, we need to make sure all the TGeoVolumes are correctly available even before this + // stage because FairMCApplication initializes the sensisitive volumes before closing the geometry. + // The true origin of the "problem" comes from the fact, that the TRD construction above uses + // Geant3-like construction routines that allow giving negative parameters, indicating dimensions to be + // fixed later. This prevents immediate construction of the TGeoVolume. + gGeoManager->CheckGeometry(); +} + +//_____________________________________________________________________________ +void Geometry::createFrame(std::vector<int> const& idtmed) +{ + // + // Create the geometry of the frame of the supermodule + // + // Names of the TRD services volumina + // + // USRL Support rails for the chambers (Al) + // USxx Support cross bars between the chambers (Al) + // USHx Horizontal connection between the cross bars (Al) + // USLx Long corner ledges (Al) + // + + int ilayer = 0; + + float xpos = 0.0; + float ypos = 0.0; + float zpos = 0.0; + + const int kTag = 100; + char cTagV[kTag]; + char cTagM[kTag]; + + const int kNparTRD = 4; + float parTRD[kNparTRD]; + const int kNparBOX = 3; + float parBOX[kNparBOX]; + const int kNparTRP = 11; + float parTRP[kNparTRP]; + + // The rotation matrices + const int kNmatrix = 7; + int matrix[kNmatrix]; + TVirtualMC::GetMC()->Matrix(matrix[0], 100.0, 0.0, 90.0, 90.0, 10.0, 0.0); + TVirtualMC::GetMC()->Matrix(matrix[1], 80.0, 0.0, 90.0, 90.0, 10.0, 180.0); + TVirtualMC::GetMC()->Matrix(matrix[2], 90.0, 0.0, 0.0, 0.0, 90.0, 90.0); + TVirtualMC::GetMC()->Matrix(matrix[3], 90.0, 180.0, 0.0, 180.0, 90.0, 90.0); + TVirtualMC::GetMC()->Matrix(matrix[4], 170.0, 0.0, 80.0, 0.0, 90.0, 90.0); + TVirtualMC::GetMC()->Matrix(matrix[5], 170.0, 180.0, 80.0, 180.0, 90.0, 90.0); + TVirtualMC::GetMC()->Matrix(matrix[6], 180.0, 180.0, 90.0, 180.0, 90.0, 90.0); + + // + // The carbon inserts in the top/bottom aluminum plates + // + + const int kNparCrb = 3; + float parCrb[kNparCrb]; + parCrb[0] = 0.0; + parCrb[1] = 0.0; + parCrb[2] = 0.0; + createVolume("USCR", "BOX ", idtmed[26], parCrb, 0); + // Bottom 1 (all sectors) + parCrb[0] = 77.49 / 2.0; + parCrb[1] = 104.60 / 2.0; + parCrb[2] = SMPLTT / 2.0; + xpos = 0.0; + ypos = 0.0; + zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("USCR", 1, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 2, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 3, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 4, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + // Bottom 2 (all sectors) + parCrb[0] = 77.49 / 2.0; + parCrb[1] = 55.80 / 2.0; + parCrb[2] = SMPLTT / 2.0; + xpos = 0.0; + ypos = 85.6; + zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("USCR", 5, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 6, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 7, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 8, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 9, "UTS1", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 10, "UTS2", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 11, "UTS3", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 12, "UTS4", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + // Bottom 3 (all sectors) + parCrb[0] = 77.49 / 2.0; + parCrb[1] = 56.00 / 2.0; + parCrb[2] = SMPLTT / 2.0; + xpos = 0.0; + ypos = 148.5; + zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("USCR", 13, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 14, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 15, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 16, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 17, "UTS1", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 18, "UTS2", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 19, "UTS3", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 20, "UTS4", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + // Bottom 4 (all sectors) + parCrb[0] = 77.49 / 2.0; + parCrb[1] = 118.00 / 2.0; + parCrb[2] = SMPLTT / 2.0; + xpos = 0.0; + ypos = 240.5; + zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("USCR", 21, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 22, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 23, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 24, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 25, "UTS1", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 26, "UTS2", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 27, "UTS3", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 28, "UTS4", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); + // Top 1 (only in front of PHOS) + parCrb[0] = 111.48 / 2.0; + parCrb[1] = 105.00 / 2.0; + parCrb[2] = SMPLTT / 2.0; + xpos = 0.0; + ypos = 0.0; + zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("USCR", 29, "UTS2", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 30, "UTS3", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); + // Top 2 (only in front of PHOS) + parCrb[0] = 111.48 / 2.0; + parCrb[1] = 56.00 / 2.0; + parCrb[2] = SMPLTT / 2.0; + xpos = 0.0; + ypos = 85.5; + zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("USCR", 31, "UTS2", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 32, "UTS3", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 33, "UTS2", xpos, -ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); + TVirtualMC::GetMC()->Gsposp("USCR", 34, "UTS3", xpos, -ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); + + // + // The chamber support rails + // + + const float kSRLhgt = 2.00; + const float kSRLwidA = 2.3; + const float kSRLwidB = 1.947; + const float kSRLdst = 1.135; + const int kNparSRL = 11; + float parSRL[kNparSRL]; + // Trapezoidal shape + parSRL[0] = SLENGTH / 2.0; + parSRL[1] = 0.0; + parSRL[2] = 0.0; + parSRL[3] = kSRLhgt / 2.0; + parSRL[4] = kSRLwidB / 2.0; + parSRL[5] = kSRLwidA / 2.0; + parSRL[6] = 5.0; + parSRL[7] = kSRLhgt / 2.0; + parSRL[8] = kSRLwidB / 2.0; + parSRL[9] = kSRLwidA / 2.0; + parSRL[10] = 5.0; + createVolume("USRL", "TRAP", idtmed[1], parSRL, kNparSRL); + + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + xpos = CWIDTH[ilayer] / 2.0 + kSRLwidA / 2.0 + kSRLdst; + ypos = 0.0; + zpos = VROCSM + SMPLTT - CALZPOS - SHEIGHT / 2.0 + CRAH + CDRH - CALH - kSRLhgt / 2.0 + + ilayer * (CH + VSPACE); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + NLAYER, "UTI1", -xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 2 * NLAYER, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 3 * NLAYER, "UTI2", -xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 4 * NLAYER, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 5 * NLAYER, "UTI3", -xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 6 * NLAYER, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 7 * NLAYER, "UTI4", -xpos, ypos, zpos, matrix[3], "ONLY"); + } + + // + // The cross bars between the chambers + // + + const float kSCBwid = 1.0; + const float kSCBthk = 2.0; + const float kSCHhgt = 0.3; + + const int kNparSCB = 3; + float parSCB[kNparSCB]; + parSCB[1] = kSCBwid / 2.0; + parSCB[2] = CH / 2.0 + VSPACE / 2.0 - kSCHhgt; + + const int kNparSCI = 3; + float parSCI[kNparSCI]; + parSCI[1] = -1; + + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + // The aluminum of the cross bars + parSCB[0] = CWIDTH[ilayer] / 2.0 + kSRLdst / 2.0; + snprintf(cTagV, kTag, "USF%01d", ilayer); + createVolume(cTagV, "BOX ", idtmed[1], parSCB, kNparSCB); + + // The empty regions in the cross bars + float thkSCB = kSCBthk; + if (ilayer < 2) { + thkSCB *= 1.5; + } + parSCI[2] = parSCB[2] - thkSCB; + parSCI[0] = parSCB[0] / 4.0 - kSCBthk; + snprintf(cTagV, kTag, "USI%01d", ilayer); + createVolume(cTagV, "BOX ", idtmed[2], parSCI, kNparSCI); + + snprintf(cTagV, kTag, "USI%01d", ilayer); + snprintf(cTagM, kTag, "USF%01d", ilayer); + ypos = 0.0; + zpos = 0.0; + xpos = parSCI[0] + thkSCB / 2.0; + TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); + xpos = -parSCI[0] - thkSCB / 2.0; + TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); + xpos = 3.0 * parSCI[0] + 1.5 * thkSCB; + TVirtualMC::GetMC()->Gspos(cTagV, 3, cTagM, xpos, ypos, zpos, 0, "ONLY"); + xpos = -3.0 * parSCI[0] - 1.5 * thkSCB; + TVirtualMC::GetMC()->Gspos(cTagV, 4, cTagM, xpos, ypos, zpos, 0, "ONLY"); + + snprintf(cTagV, kTag, "USF%01d", ilayer); + xpos = 0.0; + zpos = VROCSM + SMPLTT + parSCB[2] - SHEIGHT / 2.0 + ilayer * (CH + VSPACE); + + ypos = CLENGTH[ilayer][2] / 2.0 + CLENGTH[ilayer][1]; + TVirtualMC::GetMC()->Gspos(cTagV, 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + + ypos = -CLENGTH[ilayer][2] / 2.0 - CLENGTH[ilayer][1]; + TVirtualMC::GetMC()->Gspos(cTagV, 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + } + + // + // The horizontal connections between the cross bars + // + + const int kNparSCH = 3; + float parSCH[kNparSCH]; + + for (ilayer = 1; ilayer < NLAYER - 1; ilayer++) { + parSCH[0] = CWIDTH[ilayer] / 2.0; + parSCH[1] = (CLENGTH[ilayer + 1][2] / 2.0 + CLENGTH[ilayer + 1][1] - CLENGTH[ilayer][2] / 2.0 - + CLENGTH[ilayer][1]) / + 2.0; + parSCH[2] = kSCHhgt / 2.0; + + snprintf(cTagV, kTag, "USH%01d", ilayer); + createVolume(cTagV, "BOX ", idtmed[1], parSCH, kNparSCH); + xpos = 0.0; + ypos = CLENGTH[ilayer][2] / 2.0 + CLENGTH[ilayer][1] + parSCH[1]; + zpos = VROCSM + SMPLTT - kSCHhgt / 2.0 - SHEIGHT / 2.0 + (ilayer + 1) * (CH + VSPACE); + TVirtualMC::GetMC()->Gspos(cTagV, 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + ypos = -ypos; + TVirtualMC::GetMC()->Gspos(cTagV, 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos(cTagV, 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + } + + // + // The asymmetric flat frame in the middle + // + + // The envelope volume (aluminum) + parTRD[0] = 87.60 / 2.0; + parTRD[1] = 114.00 / 2.0; + parTRD[2] = 1.20 / 2.0; + parTRD[3] = 71.30 / 2.0; + createVolume("USDB", "TRD1", idtmed[1], parTRD, kNparTRD); + // Empty spaces (air) + parTRP[0] = 1.20 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 27.00 / 2.0; + parTRP[4] = 50.60 / 2.0; + parTRP[5] = 5.00 / 2.0; + parTRP[6] = 3.5; + parTRP[7] = 27.00 / 2.0; + parTRP[8] = 50.60 / 2.0; + parTRP[9] = 5.00 / 2.0; + parTRP[10] = 3.5; + createVolume("USD1", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = 18.0; + ypos = 0.0; + zpos = 27.00 / 2.0 - 71.3 / 2.0; + TVirtualMC::GetMC()->Gspos("USD1", 1, "USDB", xpos, ypos, zpos, matrix[2], "ONLY"); + // Empty spaces (air) + parTRP[0] = 1.20 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 33.00 / 2.0; + parTRP[4] = 5.00 / 2.0; + parTRP[5] = 62.10 / 2.0; + parTRP[6] = 3.5; + parTRP[7] = 33.00 / 2.0; + parTRP[8] = 5.00 / 2.0; + parTRP[9] = 62.10 / 2.0; + parTRP[10] = 3.5; + createVolume("USD2", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = 21.0; + ypos = 0.0; + zpos = 71.3 / 2.0 - 33.0 / 2.0; + TVirtualMC::GetMC()->Gspos("USD2", 1, "USDB", xpos, ypos, zpos, matrix[2], "ONLY"); + // Empty spaces (air) + parBOX[0] = 22.50 / 2.0; + parBOX[1] = 1.20 / 2.0; + parBOX[2] = 70.50 / 2.0; + createVolume("USD3", "BOX ", idtmed[2], parBOX, kNparBOX); + xpos = -25.75; + ypos = 0.0; + zpos = 0.4; + TVirtualMC::GetMC()->Gspos("USD3", 1, "USDB", xpos, ypos, zpos, 0, "ONLY"); + // Empty spaces (air) + parTRP[0] = 1.20 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 25.50 / 2.0; + parTRP[4] = 5.00 / 2.0; + parTRP[5] = 65.00 / 2.0; + parTRP[6] = -1.0; + parTRP[7] = 25.50 / 2.0; + parTRP[8] = 5.00 / 2.0; + parTRP[9] = 65.00 / 2.0; + parTRP[10] = -1.0; + createVolume("USD4", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = 2.0; + ypos = 0.0; + zpos = -1.6; + TVirtualMC::GetMC()->Gspos("USD4", 1, "USDB", xpos, ypos, zpos, matrix[6], "ONLY"); + // Empty spaces (air) + parTRP[0] = 1.20 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 23.50 / 2.0; + parTRP[4] = 63.50 / 2.0; + parTRP[5] = 5.00 / 2.0; + parTRP[6] = 16.0; + parTRP[7] = 23.50 / 2.0; + parTRP[8] = 63.50 / 2.0; + parTRP[9] = 5.00 / 2.0; + parTRP[10] = 16.0; + createVolume("USD5", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = 36.5; + ypos = 0.0; + zpos = -1.5; + TVirtualMC::GetMC()->Gspos("USD5", 1, "USDB", xpos, ypos, zpos, matrix[5], "ONLY"); + // Empty spaces (air) + parTRP[0] = 1.20 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 70.50 / 2.0; + parTRP[4] = 4.50 / 2.0; + parTRP[5] = 16.50 / 2.0; + parTRP[6] = -5.0; + parTRP[7] = 70.50 / 2.0; + parTRP[8] = 4.50 / 2.0; + parTRP[9] = 16.50 / 2.0; + parTRP[10] = -5.0; + createVolume("USD6", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = -43.7; + ypos = 0.0; + zpos = 0.4; + TVirtualMC::GetMC()->Gspos("USD6", 1, "USDB", xpos, ypos, zpos, matrix[2], "ONLY"); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0; + zpos = 0.04; + TVirtualMC::GetMC()->Gspos("USDB", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USDB", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Upper bar (aluminum) + parBOX[0] = 95.00 / 2.0; + parBOX[1] = 1.20 / 2.0; + parBOX[2] = 3.00 / 2.0; + createVolume("USD7", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 3.00 / 2.0; + TVirtualMC::GetMC()->Gspos("USD7", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD7", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Lower bar (aluminum) + parBOX[0] = 90.22 / 2.0; + parBOX[1] = 1.20 / 2.0; + parBOX[2] = 1.74 / 2.0; + createVolume("USD8", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0 - 0.1; + zpos = -SHEIGHT / 2.0 + SMPLTT + 2.27; + TVirtualMC::GetMC()->Gspos("USD8", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD8", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Lower bar (aluminum) + parBOX[0] = 82.60 / 2.0; + parBOX[1] = 1.20 / 2.0; + parBOX[2] = 1.40 / 2.0; + createVolume("USD9", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0; + zpos = -SHEIGHT / 2.0 + SMPLTT + 1.40 / 2.0; + TVirtualMC::GetMC()->Gspos("USD9", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USD9", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Front sheet (aluminum) + parTRP[0] = 0.10 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 74.50 / 2.0; + parTRP[4] = 31.70 / 2.0; + parTRP[5] = 44.00 / 2.0; + parTRP[6] = -5.0; + parTRP[7] = 74.50 / 2.0; + parTRP[8] = 31.70 / 2.0; + parTRP[9] = 44.00 / 2.0; + parTRP[10] = -5.0; + createVolume("USDF", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = -32.0; + ypos = CLENGTH[5][2] / 2.0 + 1.20 / 2.0 + 0.10 / 2.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("USDF", 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 2, "UTI1", xpos, -ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 3, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 4, "UTI2", xpos, -ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 5, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 6, "UTI3", xpos, -ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 7, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USDF", 8, "UTI4", xpos, -ypos, zpos, matrix[2], "ONLY"); + + // + // The flat frame in front of the chambers + // + + // The envelope volume (aluminum) + parTRD[0] = 90.00 / 2.0 - 0.1; + parTRD[1] = 114.00 / 2.0 - 0.1; + parTRD[2] = 1.50 / 2.0; + parTRD[3] = 70.30 / 2.0; + createVolume("USCB", "TRD1", idtmed[1], parTRD, kNparTRD); + // Empty spaces (air) + parTRD[0] = 87.00 / 2.0; + parTRD[1] = 10.00 / 2.0; + parTRD[2] = 1.50 / 2.0; + parTRD[3] = 26.35 / 2.0; + createVolume("USC1", "TRD1", idtmed[2], parTRD, kNparTRD); + xpos = 0.0; + ypos = 0.0; + zpos = 26.35 / 2.0 - 70.3 / 2.0; + TVirtualMC::GetMC()->Gspos("USC1", 1, "USCB", xpos, ypos, zpos, 0, "ONLY"); + // Empty spaces (air) + parTRD[0] = 10.00 / 2.0; + parTRD[1] = 111.00 / 2.0; + parTRD[2] = 1.50 / 2.0; + parTRD[3] = 35.05 / 2.0; + createVolume("USC2", "TRD1", idtmed[2], parTRD, kNparTRD); + xpos = 0.0; + ypos = 0.0; + zpos = 70.3 / 2.0 - 35.05 / 2.0; + TVirtualMC::GetMC()->Gspos("USC2", 1, "USCB", xpos, ypos, zpos, 0, "ONLY"); + // Empty spaces (air) + parTRP[0] = 1.50 / 2.0; + parTRP[1] = 0.0; + parTRP[2] = 0.0; + parTRP[3] = 37.60 / 2.0; + parTRP[4] = 63.90 / 2.0; + parTRP[5] = 8.86 / 2.0; + parTRP[6] = 16.0; + parTRP[7] = 37.60 / 2.0; + parTRP[8] = 63.90 / 2.0; + parTRP[9] = 8.86 / 2.0; + parTRP[10] = 16.0; + createVolume("USC3", "TRAP", idtmed[2], parTRP, kNparTRP); + xpos = -30.5; + ypos = 0.0; + zpos = -2.0; + TVirtualMC::GetMC()->Gspos("USC3", 1, "USCB", xpos, ypos, zpos, matrix[4], "ONLY"); + TVirtualMC::GetMC()->Gspos("USC3", 2, "USCB", -xpos, ypos, zpos, matrix[5], "ONLY"); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("USCB", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USCB", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Upper bar (aluminum) + parBOX[0] = 95.00 / 2.0; + parBOX[1] = 1.50 / 2.0; + parBOX[2] = 3.00 / 2.0; + createVolume("USC4", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; + zpos = SHEIGHT / 2.0 - SMPLTT - 3.00 / 2.0; + TVirtualMC::GetMC()->Gspos("USC4", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC4", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Lower bar (aluminum) + parBOX[0] = 90.22 / 2.0; + parBOX[1] = 1.50 / 2.0; + parBOX[2] = 2.00 / 2.0; + createVolume("USC5", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; + zpos = -SHEIGHT / 2.0 + SMPLTT + 2.60; + TVirtualMC::GetMC()->Gspos("USC5", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC5", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + // Lower bar (aluminum) + parBOX[0] = 82.60 / 2.0; + parBOX[1] = 1.50 / 2.0; + parBOX[2] = 1.60 / 2.0; + createVolume("USC6", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; + zpos = -SHEIGHT / 2.0 + SMPLTT + 1.60 / 2.0; + TVirtualMC::GetMC()->Gspos("USC6", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USC6", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); + + // + // The long corner ledges + // + + const int kNparSCL = 3; + float parSCL[kNparSCL]; + const int kNparSCLb = 11; + float parSCLb[kNparSCLb]; + + // Upper ledges + // Thickness of the corner ledges + const float kSCLthkUa = 0.6; + const float kSCLthkUb = 0.6; + // Width of the corner ledges + const float kSCLwidUa = 3.2; + const float kSCLwidUb = 4.8; + // Position of the corner ledges + const float kSCLposxUa = 0.7; + const float kSCLposxUb = 3.3; + const float kSCLposzUa = 1.65; + const float kSCLposzUb = 0.3; + // Vertical + parSCL[0] = kSCLthkUa / 2.0; + parSCL[1] = SLENGTH / 2.0; + parSCL[2] = kSCLwidUa / 2.0; + createVolume("USL1", "BOX ", idtmed[1], parSCL, kNparSCL); + xpos = SWIDTH2 / 2.0 - SMPLTT - kSCLposxUa; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - kSCLposzUa; + TVirtualMC::GetMC()->Gspos("USL1", 1, "UTI1", xpos, ypos, zpos, matrix[0], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL1", 3, "UTI4", xpos, ypos, zpos, matrix[0], "ONLY"); + xpos = -xpos; + TVirtualMC::GetMC()->Gspos("USL1", 2, "UTI1", xpos, ypos, zpos, matrix[1], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL1", 4, "UTI4", xpos, ypos, zpos, matrix[1], "ONLY"); + // Horizontal + parSCL[0] = kSCLwidUb / 2.0; + parSCL[1] = SLENGTH / 2.0; + parSCL[2] = kSCLthkUb / 2.0; + createVolume("USL2", "BOX ", idtmed[1], parSCL, kNparSCL); + xpos = SWIDTH2 / 2.0 - SMPLTT - kSCLposxUb; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - kSCLposzUb; + TVirtualMC::GetMC()->Gspos("USL2", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL2", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL2", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL2", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + xpos = -xpos; + TVirtualMC::GetMC()->Gspos("USL2", 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL2", 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL2", 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL2", 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + + // Lower ledges + // Thickness of the corner ledges + const float kSCLthkLa = 2.464; + const float kSCLthkLb = 1.0; + // Width of the corner ledges + const float kSCLwidLa = 8.3; + const float kSCLwidLb = 4.0; + // Position of the corner ledges + const float kSCLposxLa = (3.0 * kSCLthkLb - kSCLthkLa) / 4.0 + 0.05; + const float kSCLposxLb = kSCLthkLb + kSCLwidLb / 2.0 + 0.05; + const float kSCLposzLa = kSCLwidLa / 2.0; + const float kSCLposzLb = kSCLthkLb / 2.0; + // Vertical + // Trapezoidal shape + parSCLb[0] = SLENGTH / 2.0; + parSCLb[1] = 0.0; + parSCLb[2] = 0.0; + parSCLb[3] = kSCLwidLa / 2.0; + parSCLb[4] = kSCLthkLb / 2.0; + parSCLb[5] = kSCLthkLa / 2.0; + parSCLb[6] = 5.0; + parSCLb[7] = kSCLwidLa / 2.0; + parSCLb[8] = kSCLthkLb / 2.0; + parSCLb[9] = kSCLthkLa / 2.0; + parSCLb[10] = 5.0; + createVolume("USL3", "TRAP", idtmed[1], parSCLb, kNparSCLb); + xpos = SWIDTH1 / 2.0 - SMPLTT - kSCLposxLa; + ypos = 0.0; + zpos = -SHEIGHT / 2.0 + SMPLTT + kSCLposzLa; + TVirtualMC::GetMC()->Gspos("USL3", 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL3", 3, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL3", 5, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL3", 7, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY"); + xpos = -xpos; + TVirtualMC::GetMC()->Gspos("USL3", 2, "UTI1", xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL3", 4, "UTI2", xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL3", 6, "UTI3", xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("USL3", 8, "UTI4", xpos, ypos, zpos, matrix[3], "ONLY"); + // Horizontal part + parSCL[0] = kSCLwidLb / 2.0; + parSCL[1] = SLENGTH / 2.0; + parSCL[2] = kSCLthkLb / 2.0; + createVolume("USL4", "BOX ", idtmed[1], parSCL, kNparSCL); + xpos = SWIDTH1 / 2.0 - SMPLTT - kSCLposxLb; + ypos = 0.0; + zpos = -SHEIGHT / 2.0 + SMPLTT + kSCLposzLb; + TVirtualMC::GetMC()->Gspos("USL4", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL4", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL4", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL4", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + xpos = -xpos; + TVirtualMC::GetMC()->Gspos("USL4", 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL4", 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL4", 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("USL4", 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + + // + // Aluminum plates in the front part of the super modules + // + + const int kNparTrd = 4; + float parTrd[kNparTrd]; + parTrd[0] = SWIDTH1 / 2.0 - 2.5; + parTrd[1] = SWIDTH2 / 2.0 - 2.5; + parTrd[2] = SMPLTT / 2.0; + parTrd[3] = SHEIGHT / 2.0 - 1.0; + createVolume("UTA1", "TRD1", idtmed[1], parTrd, kNparTrd); + xpos = 0.0; + ypos = SMPLTT / 2.0 - FLENGTH / 2.0; + zpos = -0.5; + TVirtualMC::GetMC()->Gspos("UTA1", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTA1", 2, "UTF2", xpos, -ypos, zpos, 0, "ONLY"); + + const int kNparPlt = 3; + float parPlt[kNparPlt]; + parPlt[0] = 0.0; + parPlt[1] = 0.0; + parPlt[2] = 0.0; + createVolume("UTA2", "BOX ", idtmed[1], parPlt, 0); + xpos = 0.0; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT / 2.0; + parPlt[0] = SWIDTH2 / 2.0 - 0.2; + parPlt[1] = FLENGTH / 2.0; + parPlt[2] = SMPLTT / 2.0; + TVirtualMC::GetMC()->Gsposp("UTA2", 1, "UTF2", xpos, ypos, zpos, 0, "ONLY", parPlt, kNparPlt); + xpos = (SWIDTH1 + SWIDTH2) / 4.0 - SMPLTT / 2.0 - 0.0016; + ypos = 0.0; + zpos = 0.0; + parPlt[0] = SMPLTT / 2.0; + parPlt[1] = FLENGTH / 2.0; + parPlt[2] = SHEIGHT / 2.0; + TVirtualMC::GetMC()->Gsposp("UTA2", 2, "UTF2", xpos, ypos, zpos, matrix[0], "ONLY", parPlt, kNparPlt); + TVirtualMC::GetMC()->Gsposp("UTA2", 3, "UTF2", -xpos, ypos, zpos, matrix[1], "ONLY", parPlt, kNparPlt); + + // Additional aluminum bar + parBOX[0] = 80.0 / 2.0; + parBOX[1] = 1.0 / 2.0; + parBOX[2] = 10.0 / 2.0; + createVolume("UTA3", "BOX ", idtmed[1], parBOX, kNparBOX); + xpos = 0.0; + ypos = 1.0 / 2.0 + SMPLTT - FLENGTH / 2.0; + zpos = SHEIGHT / 2.0 - 1.5 - 10.0 / 2.0; + TVirtualMC::GetMC()->Gspos("UTA3", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTA3", 2, "UTF2", xpos, -ypos, zpos, 0, "ONLY"); +} + +//_____________________________________________________________________________ +void Geometry::createServices(std::vector<int> const& idtmed) +{ + // + // Create the geometry of the services + // + // Names of the TRD services volumina + // + // UTC1 Cooling arterias (Al) + // UTC2 Cooling arterias (Water) + // UUxx Volumes for the services at the chambers (Air) + // UMCM Readout MCMs (G10/Cu/Si) + // UDCS DCSs boards (G10/Cu) + // UTP1 Power bars (Cu) + // UTCP Cooling pipes (Fe) + // UTCH Cooling pipes (Water) + // UTPL Power lines (Cu) + // UTGD Gas distribution box (V2A) + // + + int ilayer = 0; + int istack = 0; + + float xpos = 0.0; + float ypos = 0.0; + float zpos = 0.0; + + const int kTag = 100; + char cTagV[kTag]; + + const int kNparBox = 3; + float parBox[kNparBox]; + + const int kNparTube = 3; + float parTube[kNparTube]; + + // Services inside the baby frame + const float kBBMdz = 223.0; + const float kBBSdz = 8.5; + + // Services inside the back frame + const float kBFMdz = 118.0; + const float kBFSdz = 8.5; + + // The rotation matrices + const int kNmatrix = 10; + int matrix[kNmatrix]; + TVirtualMC::GetMC()->Matrix(matrix[0], 100.0, 0.0, 90.0, 90.0, 10.0, 0.0); // rotation around y-axis + TVirtualMC::GetMC()->Matrix(matrix[1], 80.0, 0.0, 90.0, 90.0, 10.0, 180.0); // rotation around y-axis + TVirtualMC::GetMC()->Matrix(matrix[2], 0.0, 0.0, 90.0, 90.0, 90.0, 0.0); + TVirtualMC::GetMC()->Matrix(matrix[3], 180.0, 0.0, 90.0, 90.0, 90.0, 180.0); + TVirtualMC::GetMC()->Matrix(matrix[4], 90.0, 0.0, 0.0, 0.0, 90.0, 90.0); + TVirtualMC::GetMC()->Matrix(matrix[5], 100.0, 0.0, 90.0, 270.0, 10.0, 0.0); + TVirtualMC::GetMC()->Matrix(matrix[6], 80.0, 0.0, 90.0, 270.0, 10.0, 180.0); + TVirtualMC::GetMC()->Matrix(matrix[7], 90.0, 10.0, 90.0, 100.0, 0.0, 0.0); // rotation around z-axis + TVirtualMC::GetMC()->Matrix(matrix[8], 90.0, 350.0, 90.0, 80.0, 0.0, 0.0); // rotation around z-axis + TVirtualMC::GetMC()->Matrix(matrix[9], 90.0, 90.0, 90.0, 180.0, 0.0, 0.0); // rotation around z-axis + + // + // The cooling arterias + // + + // Width of the cooling arterias + const float kCOLwid = 0.8; + // Height of the cooling arterias + const float kCOLhgt = 6.5; + // Positioning of the cooling + const float kCOLposx = 1.0; + const float kCOLposz = -1.2; + // Thickness of the walls of the cooling arterias + const float kCOLthk = 0.1; + const int kNparCOL = 3; + float parCOL[kNparCOL]; + parCOL[0] = 0.0; + parCOL[1] = 0.0; + parCOL[2] = 0.0; + createVolume("UTC1", "BOX ", idtmed[8], parCOL, 0); + createVolume("UTC3", "BOX ", idtmed[8], parCOL, 0); + parCOL[0] = kCOLwid / 2.0 - kCOLthk; + parCOL[1] = -1.0; + parCOL[2] = kCOLhgt / 2.0 - kCOLthk; + createVolume("UTC2", "BOX ", idtmed[14], parCOL, kNparCOL); + createVolume("UTC4", "BOX ", idtmed[14], parCOL, kNparCOL); + + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTC2", 1, "UTC1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTC4", 1, "UTC3", xpos, ypos, zpos, 0, "ONLY"); + + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + // Along the chambers + xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx; + ypos = 0.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = SLENGTH / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer, "UTI1", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + NLAYER, "UTI1", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 6 * NLAYER, "UTI2", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 7 * NLAYER, "UTI2", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 8 * NLAYER, "UTI3", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 9 * NLAYER, "UTI3", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 10 * NLAYER, "UTI4", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 11 * NLAYER, "UTI4", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + + // Front of supermodules + xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx; + ypos = 0.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = FLENGTH / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 2 * NLAYER, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 3 * NLAYER, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 4 * NLAYER, "UTF2", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 5 * NLAYER, "UTF2", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + } + + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + // In baby frame + xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx - 2.5; + ypos = kBBSdz / 2.0 - kBBMdz / 2.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = kBBSdz / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 6 * NLAYER, "BBTRD", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 7 * NLAYER, "BBTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + } + + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + // In back frame + xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx - 0.3; + ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = kBFSdz / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 6 * NLAYER, "BFTRD", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, + kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 7 * NLAYER, "BFTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, + kNparCOL); + } + + // The upper most layer + // Along the chambers + xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 1.3; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = SLENGTH / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC1", 6, "UTI1", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + NLAYER, "UTI1", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 6 * NLAYER, "UTI2", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 7 * NLAYER, "UTI2", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 8 * NLAYER, "UTI3", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 9 * NLAYER, "UTI3", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 10 * NLAYER, "UTI4", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 11 * NLAYER, "UTI4", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + // Front of supermodules + xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 1.3; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = FLENGTH / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 2 * NLAYER, "UTF1", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 3 * NLAYER, "UTF1", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 4 * NLAYER, "UTF2", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 5 * NLAYER, "UTF2", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + // In baby frame + xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 3.1; + ypos = kBBSdz / 2.0 - kBBMdz / 2.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = kBBSdz / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 6 * NLAYER, "BBTRD", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 7 * NLAYER, "BBTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + // In back frame + xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 1.3; + ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; + parCOL[0] = kCOLwid / 2.0; + parCOL[1] = kBFSdz / 2.0; + parCOL[2] = kCOLhgt / 2.0; + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 6 * NLAYER, "BFTRD", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 7 * NLAYER, "BFTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); + + // + // The power bus bars + // + + const float kPWRwid = 0.6; + // Increase the height of the power bus bars to take into + // account the material of additional cables, etc. + const float kPWRhgtA = 5.0 + 0.2; + const float kPWRhgtB = 5.0; + const float kPWRposx = 2.0; + const float kPWRposz = 0.1; + const int kNparPWR = 3; + float parPWR[kNparPWR]; + parPWR[0] = 0.0; + parPWR[1] = 0.0; + parPWR[2] = 0.0; + createVolume("UTP1", "BOX ", idtmed[25], parPWR, 0); + createVolume("UTP3", "BOX ", idtmed[25], parPWR, 0); + + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + // Along the chambers + xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx; + ypos = 0.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kPWRhgtA / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = SLENGTH / 2.0; + parPWR[2] = kPWRhgtA / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer, "UTI1", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + NLAYER, "UTI1", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 6 * NLAYER, "UTI2", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 7 * NLAYER, "UTI2", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 8 * NLAYER, "UTI3", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 9 * NLAYER, "UTI3", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 10 * NLAYER, "UTI4", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 11 * NLAYER, "UTI4", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + + // Front of supermodule + xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx; + ypos = 0.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kPWRhgtA / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = FLENGTH / 2.0; + parPWR[2] = kPWRhgtA / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 2 * NLAYER, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 3 * NLAYER, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 4 * NLAYER, "UTF2", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 5 * NLAYER, "UTF2", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + } + + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + // In baby frame + xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx - 2.5; + ypos = kBBSdz / 2.0 - kBBMdz / 2.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kPWRhgtB / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = kBBSdz / 2.0; + parPWR[2] = kPWRhgtB / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 6 * NLAYER, "BBTRD", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 7 * NLAYER, "BBTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + } + + for (ilayer = 1; ilayer < NLAYER; ilayer++) { + // In back frame + xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx - 0.3; + ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; + zpos = + VROCSM + SMPLTT - CALZPOS + kPWRhgtB / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = kBFSdz / 2.0; + parPWR[2] = kPWRhgtB / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 8 * NLAYER, "BFTRD", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, + kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 9 * NLAYER, "BFTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, + kNparPWR); + } + + // The upper most layer + // Along the chambers + xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 1.3; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = SLENGTH / 2.0; + parPWR[2] = kPWRhgtB / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP1", 6, "UTI1", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + NLAYER, "UTI1", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 6 * NLAYER, "UTI2", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 7 * NLAYER, "UTI2", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 8 * NLAYER, "UTI3", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 9 * NLAYER, "UTI3", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 10 * NLAYER, "UTI4", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 11 * NLAYER, "UTI4", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + // Front of supermodules + xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 1.3; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = FLENGTH / 2.0; + parPWR[2] = kPWRhgtB / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 2 * NLAYER, "UTF1", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 3 * NLAYER, "UTF1", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 4 * NLAYER, "UTF2", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 5 * NLAYER, "UTF2", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + // In baby frame + xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 3.0; + ypos = kBBSdz / 2.0 - kBBMdz / 2.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = kBBSdz / 2.0; + parPWR[2] = kPWRhgtB / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 6 * NLAYER, "BBTRD", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 7 * NLAYER, "BBTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + // In back frame + xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 1.3; + ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; + parPWR[0] = kPWRwid / 2.0; + parPWR[1] = kBFSdz / 2.0; + parPWR[2] = kPWRhgtB / 2.0; + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 8 * NLAYER, "BFTRD", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 9 * NLAYER, "BFTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); + + // + // The gas tubes connecting the chambers in the super modules with holes + // Material: Stainless steel + // + + // PHOS holes + parTube[0] = 0.0; + parTube[1] = 2.2 / 2.0; + parTube[2] = CLENGTH[5][2] / 2.0 - HSPACE / 2.0; + createVolume("UTG1", "TUBE", idtmed[8], parTube, kNparTube); + parTube[0] = 0.0; + parTube[1] = 2.1 / 2.0; + parTube[2] = CLENGTH[5][2] / 2.0 - HSPACE / 2.0; + createVolume("UTG2", "TUBE", idtmed[9], parTube, kNparTube); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTG2", 1, "UTG1", xpos, ypos, zpos, 0, "ONLY"); + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 - 1.5; + ypos = 0.0; + zpos = VROCSM + SMPLTT + kCOLhgt / 2.0 - SHEIGHT / 2.0 + 5.0 + ilayer * (CH + VSPACE); + TVirtualMC::GetMC()->Gspos("UTG1", 1 + ilayer, "UTI3", xpos, ypos, zpos, matrix[4], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTG1", 7 + ilayer, "UTI3", -xpos, ypos, zpos, matrix[4], "ONLY"); + } + // Missing L4S4 chamber in sector 17 + parTube[0] = 0.0; + parTube[1] = 2.2 / 2.0; + parTube[2] = CLENGTH[4][4] / 2.0 - HSPACE / 2.0; + createVolume("UTG3", "TUBE", idtmed[8], parTube, kNparTube); + parTube[0] = 0.0; + parTube[1] = 2.1 / 2.0; + parTube[2] = CLENGTH[4][4] / 2.0 - HSPACE / 2.0; + createVolume("UTG4", "TUBE", idtmed[9], parTube, kNparTube); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTG4", 1, "UTG3", xpos, ypos, zpos, 0, "ONLY"); + xpos = CWIDTH[4] / 2.0 + kCOLwid / 2.0 - 1.5; + ypos = -CLENGTH[4][0] / 2.0 - CLENGTH[4][1] - CLENGTH[4][2] / 2.0; + zpos = VROCSM + SMPLTT + kCOLhgt / 2.0 - SHEIGHT / 2.0 + 5.0 + 4 * (CH + VSPACE); + TVirtualMC::GetMC()->Gspos("UTG3", 1, "UTI4", xpos, ypos, zpos, matrix[4], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTG4", 2, "UTI4", -xpos, ypos, zpos, matrix[4], "ONLY"); + + // + // The volumes for the services at the chambers + // + + const int kNparServ = 3; + float parServ[kNparServ]; + + for (istack = 0; istack < NSTACK; istack++) { + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + + snprintf(cTagV, kTag, "UU%02d", iDet); + parServ[0] = CWIDTH[ilayer] / 2.0; + parServ[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; + parServ[2] = CSVH / 2.0; + createVolume(cTagV, "BOX", idtmed[2], parServ, kNparServ); + } + } + + // + // The cooling pipes inside the service volumes + // + + // The cooling pipes + parTube[0] = 0.0; + parTube[1] = 0.0; + parTube[2] = 0.0; + createVolume("UTCP", "TUBE", idtmed[24], parTube, 0); + // The cooling water + parTube[0] = 0.0; + parTube[1] = 0.2 / 2.0; + parTube[2] = -1.0; + createVolume("UTCH", "TUBE", idtmed[14], parTube, kNparTube); + // Water inside the cooling pipe + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTCH", 1, "UTCP", xpos, ypos, zpos, 0, "ONLY"); + + // Position the cooling pipes in the mother volume + for (istack = 0; istack < NSTACK; istack++) { + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + int iCopy = getDetector(ilayer, istack, 0) * 100; + int nMCMrow = getRowMax(ilayer, istack, 0); + float ySize = (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)nMCMrow); + snprintf(cTagV, kTag, "UU%02d", iDet); + for (int iMCMrow = 0; iMCMrow < nMCMrow; iMCMrow++) { + xpos = 0.0; + ypos = (0.5 + iMCMrow) * ySize - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; + zpos = 0.0 + 0.742 / 2.0; + // The cooling pipes + parTube[0] = 0.0; + parTube[1] = 0.3 / 2.0; // Thickness of the cooling pipes + parTube[2] = CWIDTH[ilayer] / 2.0; + TVirtualMC::GetMC()->Gsposp("UTCP", iCopy + iMCMrow, cTagV, xpos, ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + } + } + } + + // + // The power lines + // + + // The copper power lines + parTube[0] = 0.0; + parTube[1] = 0.0; + parTube[2] = 0.0; + createVolume("UTPL", "TUBE", idtmed[5], parTube, 0); + + // Position the power lines in the mother volume + for (istack = 0; istack < NSTACK; istack++) { + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + int iCopy = getDetector(ilayer, istack, 0) * 100; + int nMCMrow = getRowMax(ilayer, istack, 0); + float ySize = (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)nMCMrow); + snprintf(cTagV, kTag, "UU%02d", iDet); + for (int iMCMrow = 0; iMCMrow < nMCMrow; iMCMrow++) { + xpos = 0.0; + ypos = (0.5 + iMCMrow) * ySize - 1.0 - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; + zpos = -0.4 + 0.742 / 2.0; + parTube[0] = 0.0; + parTube[1] = 0.2 / 2.0; // Thickness of the power lines + parTube[2] = CWIDTH[ilayer] / 2.0; + TVirtualMC::GetMC()->Gsposp("UTPL", iCopy + iMCMrow, cTagV, xpos, ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + } + } + } + + // + // The MCMs + // + + const float kMCMx = 3.0; + const float kMCMy = 3.0; + const float kMCMz = 0.3; + + const float kMCMpcTh = 0.1; + const float kMCMcuTh = 0.0025; + const float kMCMsiTh = 0.03; + const float kMCMcoTh = 0.04; + + // The mother volume for the MCMs (air) + const int kNparMCM = 3; + float parMCM[kNparMCM]; + parMCM[0] = kMCMx / 2.0; + parMCM[1] = kMCMy / 2.0; + parMCM[2] = kMCMz / 2.0; + createVolume("UMCM", "BOX", idtmed[2], parMCM, kNparMCM); + + // The MCM carrier G10 layer + parMCM[0] = kMCMx / 2.0; + parMCM[1] = kMCMy / 2.0; + parMCM[2] = kMCMpcTh / 2.0; + createVolume("UMC1", "BOX", idtmed[19], parMCM, kNparMCM); + // The MCM carrier Cu layer + parMCM[0] = kMCMx / 2.0; + parMCM[1] = kMCMy / 2.0; + parMCM[2] = kMCMcuTh / 2.0; + createVolume("UMC2", "BOX", idtmed[18], parMCM, kNparMCM); + // The silicon of the chips + parMCM[0] = kMCMx / 2.0; + parMCM[1] = kMCMy / 2.0; + parMCM[2] = kMCMsiTh / 2.0; + createVolume("UMC3", "BOX", idtmed[20], parMCM, kNparMCM); + // The aluminum of the cooling plates + parMCM[0] = kMCMx / 2.0; + parMCM[1] = kMCMy / 2.0; + parMCM[2] = kMCMcoTh / 2.0; + createVolume("UMC4", "BOX", idtmed[24], parMCM, kNparMCM); + + // Put the MCM material inside the MCM mother volume + xpos = 0.0; + ypos = 0.0; + zpos = -kMCMz / 2.0 + kMCMpcTh / 2.0; + TVirtualMC::GetMC()->Gspos("UMC1", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); + zpos += kMCMpcTh / 2.0 + kMCMcuTh / 2.0; + TVirtualMC::GetMC()->Gspos("UMC2", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); + zpos += kMCMcuTh / 2.0 + kMCMsiTh / 2.0; + TVirtualMC::GetMC()->Gspos("UMC3", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); + zpos += kMCMsiTh / 2.0 + kMCMcoTh / 2.0; + TVirtualMC::GetMC()->Gspos("UMC4", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); + + // Position the MCMs in the mother volume + for (istack = 0; istack < NSTACK; istack++) { + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + int iCopy = getDetector(ilayer, istack, 0) * 1000; + int nMCMrow = getRowMax(ilayer, istack, 0); + float ySize = (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)nMCMrow); + int nMCMcol = 8; + float xSize = (getChamberWidth(ilayer) - 2.0 * CPADW) / ((float)nMCMcol + 6); // Introduce 6 gaps + int iMCM[8] = {1, 2, 3, 5, 8, 9, 10, 12}; // 0..7 MCM + 6 gap structure + snprintf(cTagV, kTag, "UU%02d", iDet); + for (int iMCMrow = 0; iMCMrow < nMCMrow; iMCMrow++) { + for (int iMCMcol = 0; iMCMcol < nMCMcol; iMCMcol++) { + xpos = (0.5 + iMCM[iMCMcol]) * xSize + 1.0 - CWIDTH[ilayer] / 2.0; + ypos = (0.5 + iMCMrow) * ySize + 1.0 - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; + zpos = -0.4 + 0.742 / 2.0; + TVirtualMC::GetMC()->Gspos("UMCM", iCopy + iMCMrow * 10 + iMCMcol, cTagV, xpos, ypos, zpos, 0, "ONLY"); + // Add two additional smaller cooling pipes on top of the MCMs + // to mimic the meandering structure + xpos = (0.5 + iMCM[iMCMcol]) * xSize + 1.0 - CWIDTH[ilayer] / 2.0; + ypos = (0.5 + iMCMrow) * ySize - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; + zpos = 0.0 + 0.742 / 2.0; + parTube[0] = 0.0; + parTube[1] = 0.3 / 2.0; // Thickness of the cooling pipes + parTube[2] = kMCMx / 2.0; + TVirtualMC::GetMC()->Gsposp("UTCP", iCopy + iMCMrow * 10 + iMCMcol + 50, cTagV, xpos, ypos + 1.0, zpos, + matrix[2], "ONLY", parTube, kNparTube); + TVirtualMC::GetMC()->Gsposp("UTCP", iCopy + iMCMrow * 10 + iMCMcol + 500, cTagV, xpos, ypos + 2.0, zpos, + matrix[2], "ONLY", parTube, kNparTube); + } + } + } + } + + // + // The DCS boards + // + + const float kDCSx = 9.0; + const float kDCSy = 14.5; + const float kDCSz = 0.3; + + const float kDCSpcTh = 0.15; + const float kDCScuTh = 0.01; + const float kDCScoTh = 0.04; + + // The mother volume for the DCSs (air) + const int kNparDCS = 3; + float parDCS[kNparDCS]; + parDCS[0] = kDCSx / 2.0; + parDCS[1] = kDCSy / 2.0; + parDCS[2] = kDCSz / 2.0; + createVolume("UDCS", "BOX", idtmed[2], parDCS, kNparDCS); + + // The DCS carrier G10 layer + parDCS[0] = kDCSx / 2.0; + parDCS[1] = kDCSy / 2.0; + parDCS[2] = kDCSpcTh / 2.0; + createVolume("UDC1", "BOX", idtmed[19], parDCS, kNparDCS); + // The DCS carrier Cu layer + parDCS[0] = kDCSx / 2.0; + parDCS[1] = kDCSy / 2.0; + parDCS[2] = kDCScuTh / 2.0; + createVolume("UDC2", "BOX", idtmed[18], parDCS, kNparDCS); + // The aluminum of the cooling plates + parDCS[0] = 5.0 / 2.0; + parDCS[1] = 5.0 / 2.0; + parDCS[2] = kDCScoTh / 2.0; + createVolume("UDC3", "BOX", idtmed[24], parDCS, kNparDCS); + + // Put the DCS material inside the DCS mother volume + xpos = 0.0; + ypos = 0.0; + zpos = -kDCSz / 2.0 + kDCSpcTh / 2.0; + TVirtualMC::GetMC()->Gspos("UDC1", 1, "UDCS", xpos, ypos, zpos, 0, "ONLY"); + zpos += kDCSpcTh / 2.0 + kDCScuTh / 2.0; + TVirtualMC::GetMC()->Gspos("UDC2", 1, "UDCS", xpos, ypos, zpos, 0, "ONLY"); + zpos += kDCScuTh / 2.0 + kDCScoTh / 2.0; + TVirtualMC::GetMC()->Gspos("UDC3", 1, "UDCS", xpos, ypos, zpos, 0, "ONLY"); + + // Put the DCS board in the chamber services mother volume + for (istack = 0; istack < NSTACK; istack++) { + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + int iCopy = iDet + 1; + xpos = CWIDTH[ilayer] / 2.0 - + 1.9 * (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)getRowMax(ilayer, istack, 0)); + ypos = 0.05 * CLENGTH[ilayer][istack]; + zpos = kDCSz / 2.0 - CSVH / 2.0; + snprintf(cTagV, kTag, "UU%02d", iDet); + TVirtualMC::GetMC()->Gspos("UDCS", iCopy, cTagV, xpos, ypos, zpos, 0, "ONLY"); + } + } + + // + // The ORI boards + // + + const float kORIx = 4.2; + const float kORIy = 13.5; + const float kORIz = 0.3; + + const float kORIpcTh = 0.15; + const float kORIcuTh = 0.01; + const float kORIcoTh = 0.04; + + // The mother volume for the ORIs (air) + const int kNparORI = 3; + float parORI[kNparORI]; + parORI[0] = kORIx / 2.0; + parORI[1] = kORIy / 2.0; + parORI[2] = kORIz / 2.0; + createVolume("UORI", "BOX", idtmed[2], parORI, kNparORI); + + // The ORI carrier G10 layer + parORI[0] = kORIx / 2.0; + parORI[1] = kORIy / 2.0; + parORI[2] = kORIpcTh / 2.0; + createVolume("UOR1", "BOX", idtmed[19], parORI, kNparORI); + // The ORI carrier Cu layer + parORI[0] = kORIx / 2.0; + parORI[1] = kORIy / 2.0; + parORI[2] = kORIcuTh / 2.0; + createVolume("UOR2", "BOX", idtmed[18], parORI, kNparORI); + // The aluminum of the cooling plates + parORI[0] = kORIx / 2.0; + parORI[1] = kORIy / 2.0; + parORI[2] = kORIcoTh / 2.0; + createVolume("UOR3", "BOX", idtmed[24], parORI, kNparORI); + + // Put the ORI material inside the ORI mother volume + xpos = 0.0; + ypos = 0.0; + zpos = -kORIz / 2.0 + kORIpcTh / 2.0; + TVirtualMC::GetMC()->Gspos("UOR1", 1, "UORI", xpos, ypos, zpos, 0, "ONLY"); + zpos += kORIpcTh / 2.0 + kORIcuTh / 2.0; + TVirtualMC::GetMC()->Gspos("UOR2", 1, "UORI", xpos, ypos, zpos, 0, "ONLY"); + zpos += kORIcuTh / 2.0 + kORIcoTh / 2.0; + TVirtualMC::GetMC()->Gspos("UOR3", 1, "UORI", xpos, ypos, zpos, 0, "ONLY"); + + // Put the ORI board in the chamber services mother volume + for (istack = 0; istack < NSTACK; istack++) { + for (ilayer = 0; ilayer < NLAYER; ilayer++) { + int iDet = getDetectorSec(ilayer, istack); + int iCopy = iDet + 1; + xpos = CWIDTH[ilayer] / 2.0 - + 1.92 * (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)getRowMax(ilayer, istack, 0)); + ypos = -16.0; + zpos = kORIz / 2.0 - CSVH / 2.0; + snprintf(cTagV, kTag, "UU%02d", iDet); + TVirtualMC::GetMC()->Gspos("UORI", iCopy, cTagV, xpos, ypos, zpos, 0, "ONLY"); + xpos = -CWIDTH[ilayer] / 2.0 + + 3.8 * (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)getRowMax(ilayer, istack, 0)); + ypos = -16.0; + zpos = kORIz / 2.0 - CSVH / 2.0; + snprintf(cTagV, kTag, "UU%02d", iDet); + TVirtualMC::GetMC()->Gspos("UORI", iCopy + MAXCHAMBER, cTagV, xpos, ypos, zpos, 0, "ONLY"); + } + } + + // + // Services in front of the super module + // + + // Gas in-/outlet pipes (INOX) + parTube[0] = 0.0; + parTube[1] = 0.0; + parTube[2] = 0.0; + createVolume("UTG3", "TUBE", idtmed[8], parTube, 0); + // The gas inside the in-/outlet pipes (Xe) + parTube[0] = 0.0; + parTube[1] = 1.2 / 2.0; + parTube[2] = -1.0; + createVolume("UTG4", "TUBE", idtmed[9], parTube, kNparTube); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTG4", 1, "UTG3", xpos, ypos, zpos, 0, "ONLY"); + for (ilayer = 0; ilayer < NLAYER - 1; ilayer++) { + xpos = 0.0; + ypos = CLENGTH[ilayer][2] / 2.0 + CLENGTH[ilayer][1] + CLENGTH[ilayer][0]; + zpos = 9.0 - SHEIGHT / 2.0 + ilayer * (CH + VSPACE); + parTube[0] = 0.0; + parTube[1] = 1.5 / 2.0; + parTube[2] = CWIDTH[ilayer] / 2.0 - 2.5; + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY", parTube, kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 1 * NLAYER, "UTI1", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 2 * NLAYER, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 3 * NLAYER, "UTI2", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 4 * NLAYER, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 5 * NLAYER, "UTI3", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 6 * NLAYER, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 7 * NLAYER, "UTI4", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, + kNparTube); + } + + // Gas distribution box + parBox[0] = 14.50 / 2.0; + parBox[1] = 4.52 / 2.0; + parBox[2] = 5.00 / 2.0; + createVolume("UTGD", "BOX ", idtmed[8], parBox, kNparBox); + parBox[0] = 14.50 / 2.0; + parBox[1] = 4.00 / 2.0; + parBox[2] = 4.40 / 2.0; + createVolume("UTGI", "BOX ", idtmed[9], parBox, kNparBox); + parTube[0] = 0.0; + parTube[1] = 4.0 / 2.0; + parTube[2] = 8.0 / 2.0; + createVolume("UTGT", "TUBE", idtmed[8], parTube, kNparTube); + parTube[0] = 0.0; + parTube[1] = 3.4 / 2.0; + parTube[2] = 8.0 / 2.0; + createVolume("UTGG", "TUBE", idtmed[9], parTube, kNparTube); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTGI", 1, "UTGD", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTGG", 1, "UTGT", xpos, ypos, zpos, 0, "ONLY"); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTGD", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); + xpos = -3.0; + ypos = 0.0; + zpos = 6.5; + TVirtualMC::GetMC()->Gspos("UTGT", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); + xpos = -11.25; + ypos = 0.0; + zpos = 0.5; + TVirtualMC::GetMC()->Gspos("UTGT", 3, "UTF1", xpos, ypos, zpos, matrix[2], "ONLY"); + xpos = 11.25; + ypos = 0.0; + zpos = 0.5; + TVirtualMC::GetMC()->Gspos("UTGT", 5, "UTF1", xpos, ypos, zpos, matrix[2], "ONLY"); + + // Cooling manifolds + parBox[0] = 5.0 / 2.0; + parBox[1] = 23.0 / 2.0; + parBox[2] = 70.0 / 2.0; + createVolume("UTCM", "BOX ", idtmed[2], parBox, kNparBox); + parBox[0] = 5.0 / 2.0; + parBox[1] = 5.0 / 2.0; + parBox[2] = 70.0 / 2.0; + createVolume("UTCA", "BOX ", idtmed[8], parBox, kNparBox); + parBox[0] = 5.0 / 2.0 - 0.3; + parBox[1] = 5.0 / 2.0 - 0.3; + parBox[2] = 70.0 / 2.0 - 0.3; + createVolume("UTCW", "BOX ", idtmed[14], parBox, kNparBox); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTCW", 1, "UTCA", xpos, ypos, zpos, 0, "ONLY"); + xpos = 0.0; + ypos = 5.0 / 2.0 - 23.0 / 2.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTCA", 1, "UTCM", xpos, ypos, zpos, 0, "ONLY"); + parTube[0] = 0.0; + parTube[1] = 3.0 / 2.0; + parTube[2] = 18.0 / 2.0; + createVolume("UTCO", "TUBE", idtmed[8], parTube, kNparTube); + parTube[0] = 0.0; + parTube[1] = 3.0 / 2.0 - 0.3; + parTube[2] = 18.0 / 2.0; + createVolume("UTCL", "TUBE", idtmed[14], parTube, kNparTube); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTCL", 1, "UTCO", xpos, ypos, zpos, 0, "ONLY"); + xpos = 0.0; + ypos = 2.5; + zpos = -70.0 / 2.0 + 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 1, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 2, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 3, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 4, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 5, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 6, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 7, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + zpos += 7.0; + TVirtualMC::GetMC()->Gspos("UTCO", 8, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); + + xpos = 40.0; + ypos = FLENGTH / 2.0 - 23.0 / 2.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTCM", 1, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTCM", 2, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTCM", 3, "UTF2", xpos, -ypos, zpos, matrix[5], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTCM", 4, "UTF2", -xpos, -ypos, zpos, matrix[6], "ONLY"); + + // Power connection boards (Cu) + parBox[0] = 0.5 / 2.0; + parBox[1] = 15.0 / 2.0; + parBox[2] = 7.0 / 2.0; + createVolume("UTPC", "BOX ", idtmed[25], parBox, kNparBox); + for (ilayer = 0; ilayer < NLAYER - 1; ilayer++) { + xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0; + ypos = 0.0; + zpos = VROCSM + SMPLTT + kPWRhgtA / 2.0 - SHEIGHT / 2.0 + kPWRposz + (ilayer + 1) * (CH + VSPACE); + TVirtualMC::GetMC()->Gspos("UTPC", ilayer, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTPC", ilayer + NLAYER, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY"); + } + xpos = CWIDTH[5] / 2.0 + kPWRhgtA / 2.0 - 2.0; + ypos = 0.0; + zpos = SHEIGHT / 2.0 - SMPLTT - 2.0; + TVirtualMC::GetMC()->Gspos("UTPC", 5, "UTF1", xpos, ypos, zpos, matrix[3], "ONLY"); + TVirtualMC::GetMC()->Gspos("UTPC", 5 + NLAYER, "UTF1", -xpos, ypos, zpos, matrix[3], "ONLY"); + + // Power connection panel (Al) + parBox[0] = 60.0 / 2.0; + parBox[1] = 10.0 / 2.0; + parBox[2] = 3.0 / 2.0; + createVolume("UTPP", "BOX ", idtmed[1], parBox, kNparBox); + xpos = 0.0; + ypos = 0.0; + zpos = 18.0; + TVirtualMC::GetMC()->Gspos("UTPP", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); + + // + // Electronics boxes + // + + // Casing (INOX) + parBox[0] = 60.0 / 2.0; + parBox[1] = 10.0 / 2.0; + parBox[2] = 6.0 / 2.0; + createVolume("UTE1", "BOX ", idtmed[8], parBox, kNparBox); + // Interior (air) + parBox[0] = parBox[0] - 0.5; + parBox[1] = parBox[1] - 0.5; + parBox[2] = parBox[2] - 0.5; + createVolume("UTE2", "BOX ", idtmed[2], parBox, kNparBox); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTE2", 1, "UTE1", xpos, ypos, zpos, 0, "ONLY"); + xpos = 0.0; + ypos = SLENGTH / 2.0 - 10.0 / 2.0 - 3.0; + zpos = -SHEIGHT / 2.0 + 6.0 / 2.0 + 1.0; + TVirtualMC::GetMC()->Gspos("UTE1", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE1", 2, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE1", 3, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE1", 4, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + + // Casing (INOX) + parBox[0] = 50.0 / 2.0; + parBox[1] = 15.0 / 2.0; + parBox[2] = 20.0 / 2.0; + createVolume("UTE3", "BOX ", idtmed[8], parBox, kNparBox); + // Interior (air) + parBox[0] = parBox[0] - 0.5; + parBox[1] = parBox[1] - 0.5; + parBox[2] = parBox[2] - 0.5; + createVolume("UTE4", "BOX ", idtmed[2], parBox, kNparBox); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTE4", 1, "UTE3", xpos, ypos, zpos, 0, "ONLY"); + xpos = 0.0; + ypos = -SLENGTH / 2.0 + 15.0 / 2.0 + 3.0; + zpos = -SHEIGHT / 2.0 + 20.0 / 2.0 + 1.0; + TVirtualMC::GetMC()->Gspos("UTE3", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE3", 2, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE3", 3, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE3", 4, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + + // Casing (INOX) + parBox[0] = 20.0 / 2.0; + parBox[1] = 7.0 / 2.0; + parBox[2] = 20.0 / 2.0; + createVolume("UTE5", "BOX ", idtmed[8], parBox, kNparBox); + // Interior (air) + parBox[0] = parBox[0] - 0.5; + parBox[1] = parBox[1] - 0.5; + parBox[2] = parBox[2] - 0.5; + createVolume("UTE6", "BOX ", idtmed[2], parBox, kNparBox); + xpos = 0.0; + ypos = 0.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTE6", 1, "UTE5", xpos, ypos, zpos, 0, "ONLY"); + xpos = 20.0; + ypos = -SLENGTH / 2.0 + 7.0 / 2.0 + 3.0; + zpos = 0.0; + TVirtualMC::GetMC()->Gspos("UTE5", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE5", 2, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE5", 3, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE5", 4, "UTI4", xpos, ypos, zpos, 0, "ONLY"); + xpos = -xpos; + TVirtualMC::GetMC()->Gspos("UTE5", 5, "UTI1", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE5", 6, "UTI2", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE5", 7, "UTI3", xpos, ypos, zpos, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("UTE5", 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); +} + +//_____________________________________________________________________________ +void Geometry::assembleChamber(int ilayer, int istack) +{ + // + // Group volumes UA, UD, UF, UU into an assembly that defines the + // alignable volume of a single readout chamber + // + + const int kTag = 100; + char cTagV[kTag]; + char cTagM[kTag]; + + double xpos = 0.0; + double ypos = 0.0; + double zpos = 0.0; + + int idet = getDetectorSec(ilayer, istack); + + // Create the assembly for a given ROC + snprintf(cTagM, kTag, "UT%02d", idet); + TGeoVolume* roc = new TGeoVolumeAssembly(cTagM); + + // Add the lower part of the chamber (aluminum frame), + // including radiator and drift region + xpos = 0.0; + ypos = 0.0; + zpos = CRAH / 2.0 + CDRH / 2.0 - CHSV / 2.0; + snprintf(cTagV, kTag, "UA%02d", idet); + TGeoVolume* rocA = gGeoManager->GetVolume(cTagV); + roc->AddNode(rocA, 1, new TGeoTranslation(xpos, ypos, zpos)); + + // Add the additional aluminum ledges + xpos = CWIDTH[ilayer] / 2.0 + CALWMOD / 2.0; + ypos = 0.0; + zpos = CRAH + CDRH - CALZPOS - CALHMOD / 2.0 - CHSV / 2.0; + snprintf(cTagV, kTag, "UZ%02d", idet); + TGeoVolume* rocZ = gGeoManager->GetVolume(cTagV); + roc->AddNode(rocZ, 1, new TGeoTranslation(xpos, ypos, zpos)); + roc->AddNode(rocZ, 2, new TGeoTranslation(-xpos, ypos, zpos)); + + // Add the additional wacosit ledges + xpos = CWIDTH[ilayer] / 2.0 + CWSW / 2.0; + ypos = 0.0; + zpos = CRAH + CDRH - CWSH / 2.0 - CHSV / 2.0; + snprintf(cTagV, kTag, "UP%02d", idet); + TGeoVolume* rocP = gGeoManager->GetVolume(cTagV); + roc->AddNode(rocP, 1, new TGeoTranslation(xpos, ypos, zpos)); + roc->AddNode(rocP, 2, new TGeoTranslation(-xpos, ypos, zpos)); + + // Add the middle part of the chamber (G10 frame), + // including amplification region + xpos = 0.0; + ypos = 0.0; + zpos = CAMH / 2.0 + CRAH + CDRH - CHSV / 2.0; + snprintf(cTagV, kTag, "UD%02d", idet); + TGeoVolume* rocD = gGeoManager->GetVolume(cTagV); + roc->AddNode(rocD, 1, new TGeoTranslation(xpos, ypos, zpos)); + + // Add the upper part of the chamber (aluminum frame), + // including back panel and FEE + xpos = 0.0; + ypos = 0.0; + zpos = CROH / 2.0 + CAMH + CRAH + CDRH - CHSV / 2.0; + snprintf(cTagV, kTag, "UF%02d", idet); + TGeoVolume* rocF = gGeoManager->GetVolume(cTagV); + roc->AddNode(rocF, 1, new TGeoTranslation(xpos, ypos, zpos)); + + // Add the volume with services on top of the back panel + xpos = 0.0; + ypos = 0.0; + zpos = CSVH / 2.0 + CROH + CAMH + CRAH + CDRH - CHSV / 2.0; + snprintf(cTagV, kTag, "UU%02d", idet); + TGeoVolume* rocU = gGeoManager->GetVolume(cTagV); + roc->AddNode(rocU, 1, new TGeoTranslation(xpos, ypos, zpos)); + + // Place the ROC assembly into the super modules + xpos = 0.0; + ypos = 0.0; + ypos = CLENGTH[ilayer][0] + CLENGTH[ilayer][1] + CLENGTH[ilayer][2] / 2.0; + for (int ic = 0; ic < istack; ic++) { + ypos -= CLENGTH[ilayer][ic]; + } + ypos -= CLENGTH[ilayer][istack] / 2.0; + zpos = VROCSM + SMPLTT + CHSV / 2.0 - SHEIGHT / 2.0 + ilayer * (CH + VSPACE); + TGeoVolume* sm1 = gGeoManager->GetVolume("UTI1"); + TGeoVolume* sm2 = gGeoManager->GetVolume("UTI2"); + TGeoVolume* sm3 = gGeoManager->GetVolume("UTI3"); + TGeoVolume* sm4 = gGeoManager->GetVolume("UTI4"); + sm1->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); + sm2->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); + if (istack != 2) { + // w/o middle stack + sm3->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); + } + if (!((ilayer == 4) && (istack == 4))) { + // Sector 17 w/o L4S4 chamber + sm4->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); + } +} + +void Geometry::fillMatrixCache(int mask) +{ + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) { + useT2LCache(); + } + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) { + useL2GCache(); + } + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) { + useT2GCache(); + } + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)) { + useT2GRotCache(); + } + + std::string volPath; + // keep in mind that changing this path can affect behaviour in Detector.cxx + // see: https://github.com/AliceO2Group/AliceO2/pull/3890 + const std::string vpStr{"ALIC_1/barrel_1/B077_1/BSEGMO"}; + const std::string vpApp1{"_1/BTRD"}; + const std::string vpApp2{"_1"}; + const std::string vpApp3a{"/UTR1_1/UTS1_1/UTI1_1"}; + const std::string vpApp3b{"/UTR2_1/UTS2_1/UTI2_1"}; + const std::string vpApp3c{"/UTR3_1/UTS3_1/UTI3_1"}; + const std::string vpApp3d{"/UTR4_1/UTS4_1/UTI4_1"}; + + for (int ilayer = 0; ilayer < NLAYER; ilayer++) { + for (int isector = 0; isector < NSECTOR; isector++) { + for (int istack = 0; istack < NSTACK; istack++) { + Int_t lid = getDetector(ilayer, istack, isector); + // Check for disabled supermodules + volPath = vpStr; + volPath += std::to_string(isector); + volPath += vpApp1; + volPath += std::to_string(isector); + volPath += vpApp2; + switch (isector) { + case 17: + if ((istack == 4) && (ilayer == 4)) { + continue; + } + volPath += vpApp3d; + break; + case 13: + case 14: + case 15: + // Check for holes in from of PHOS + if (istack == 2) { + continue; + } + volPath += vpApp3c; + break; + case 11: + case 12: + volPath += vpApp3b; + break; + default: + volPath += vpApp3a; + }; + if (!gGeoManager->CheckPath(volPath.c_str())) { + continue; + } + const auto m = o2::base::GeometryManager::getMatrix(o2::detectors::DetID::TRD, lid); + TGeoHMatrix rotMatrix; + rotMatrix.RotateX(-90); + rotMatrix.RotateY(-90); + rotMatrix.MultiplyLeft(m); + const TGeoHMatrix& t2l = rotMatrix.Inverse(); + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) { + setMatrixL2G(Mat3D(t2l), lid); + } + + Double_t sectorAngle = 20.0 * (isector % 18) + 10.0; + TGeoHMatrix rotSector; + rotSector.RotateZ(sectorAngle); + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) { + setMatrixT2G(Mat3D(rotSector), lid); + } + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)) { + setMatrixT2GRot(Rot2D(sectorAngle), lid); + } + if (mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) { + const TGeoMatrix& inv = rotSector.Inverse(); + rotMatrix.MultiplyLeft(&inv); + setMatrixT2L(Mat3D(rotMatrix.Inverse()), lid); + } + } + } + } +} + +//_____________________________________________________________________________ +void Geometry::addAlignableVolumes() const +{ + // + // define alignable volumes of the TRD + // + if (!gGeoManager) { + LOG(FATAL) << "Geometry is not loaded"; + } + + std::string volPath; + std::string vpStr{"ALIC_1/barrel_1/B077_1/BSEGMO"}; + const std::string vpApp1{"_1/BTRD"}; + const std::string vpApp2{"_1"}; + const std::string vpApp3a{"/UTR1_1/UTS1_1/UTI1_1"}; + const std::string vpApp3b{"/UTR2_1/UTS2_1/UTI2_1"}; + const std::string vpApp3c{"/UTR3_1/UTS3_1/UTI3_1"}; + const std::string vpApp3d{"/UTR4_1/UTS4_1/UTI4_1"}; + std::string symName; + + // in opposite to AliGeomManager, we use consecutive numbering of modules through whole TRD + int volid = -1; + + // The super modules + // The symbolic names are: TRD/sm00 ... TRD/sm17 + for (int isector = 0; isector < NSECTOR; isector++) { + volPath = vpStr; + volPath += std::to_string(isector); + volPath += vpApp1; + volPath += std::to_string(isector); + volPath += vpApp2; + symName = Form("TRD/sm%02d", isector); + gGeoManager->SetAlignableEntry(symName.c_str(), volPath.c_str()); + } + + // The readout chambers + // The symbolic names are: TRD/sm00/st0/pl0 ... TRD/sm17/st4/pl5 + + for (int isector = 0; isector < NSECTOR; isector++) { + if (!getSMstatus(isector)) { + continue; + } + for (int ilayer = 0; ilayer < NLAYER; ilayer++) { + for (int istack = 0; istack < NSTACK; istack++) { + Int_t lid = getDetector(ilayer, istack, isector); + int idet = getDetectorSec(ilayer, istack); + + // Check for disabled supermodules + volPath = vpStr; + volPath += std::to_string(isector); + volPath += vpApp1; + volPath += std::to_string(isector); + volPath += vpApp2; + switch (isector) { + case 17: + if ((istack == 4) && (ilayer == 4)) { + continue; + } + volPath += vpApp3d; + break; + case 13: + case 14: + case 15: + // Check for holes in from of PHOS + if (istack == 2) { + continue; + } + volPath += vpApp3c; + break; + case 11: + case 12: + volPath += vpApp3b; + break; + default: + volPath += vpApp3a; + }; + volPath += Form("/UT%02d_1", idet); + + symName = Form("TRD/sm%02d/st%d/pl%d", isector, istack, ilayer); + int modID = o2::base::GeometryManager::getSensID(o2::detectors::DetID::TRD, lid); + + TGeoPNEntry* alignableEntry = gGeoManager->SetAlignableEntry(symName.c_str(), volPath.c_str(), modID); + + // Add the tracking to local matrix + if (alignableEntry) { + TGeoHMatrix* globMatrix = alignableEntry->GetGlobalOrig(); + Double_t sectorAngle = 20.0 * (isector % 18) + 10.0; + TGeoHMatrix* t2lMatrix = new TGeoHMatrix(); + t2lMatrix->RotateZ(sectorAngle); + const TGeoHMatrix& globmatrixi = globMatrix->Inverse(); + t2lMatrix->MultiplyLeft(&globmatrixi); + alignableEntry->SetMatrix(t2lMatrix); + } else { + LOG(ERROR) << "Alignable entry is not valid: ModID:" << modID << " Sector:" << isector << " Lr:" << ilayer + << " Stack: " << istack << " name: " << symName.c_str() << " vol: " << volPath.c_str(); + } + } + } + } +} + +//_____________________________________________________________________________ +bool Geometry::createClusterMatrixArray() +{ + + if (!gGeoManager) { + LOG(ERROR) << "Geometry is not loaded yet"; + return false; + } + + if (isBuilt()) { + LOG(WARNING) << "Already built"; + return true; // already initialized + } + + setSize(NCHAMBER, MAXCHAMBER); //Only NCHAMBER=521 of MAXCHAMBER matrices are filled + fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G) | + o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)); + return true; +} + +//_____________________________________________________________________________ +bool Geometry::chamberInGeometry(int det) const +{ + // + // Checks whether the given detector is part of the current geometry + // + + if (!isMatrixAvailable(det)) { + return false; + } else { + return true; + } +} diff --git a/Detectors/TRD/base/src/GeometryBase.cxx b/Detectors/TRD/base/src/GeometryBase.cxx new file mode 100644 index 0000000000000..167c9f73899c5 --- /dev/null +++ b/Detectors/TRD/base/src/GeometryBase.cxx @@ -0,0 +1,85 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDBase/GeometryBase.h" + +using namespace o2::trd; +using namespace o2::trd::constants; + +//_____________________________________________________________________________ +GPUd() int GeometryBase::getStack(float z, int layer) const +{ + // + // Reconstruct the chamber number from the z position and layer number + // + // The return function has to be protected for positiveness !! + // + + if ((layer < 0) || (layer >= NLAYER)) { + return -1; + } + + int istck = NSTACK; + float zmin = 0.0; + float zmax = 0.0; + + do { + istck--; + if (istck < 0) { + break; + } + const PadPlane* pp = getPadPlane(layer, istck); + zmax = pp->getRow0(); + int nrows = pp->getNrows(); + zmin = zmax - 2 * pp->getLengthOPad() - (nrows - 2) * pp->getLengthIPad() - (nrows - 1) * pp->getRowSpacing(); + } while ((z < zmin) || (z > zmax)); + + return istck; +} + +//_____________________________________________________________________________ +GPUd() bool GeometryBase::isOnBoundary(int det, float y, float z, float eps) const +{ + // + // Checks whether position is at the boundary of the sensitive volume + // + + int ly = getLayer(det); + if ((ly < 0) || (ly >= NLAYER)) { + return true; + } + + int stk = getStack(det); + if ((stk < 0) || (stk >= NSTACK)) { + return true; + } + + const PadPlane* pp = &mPadPlanes[getDetectorSec(ly, stk)]; + if (!pp) { + return true; + } + + float max = pp->getRow0(); + int n = pp->getNrows(); + float min = max - 2 * pp->getLengthOPad() - (n - 2) * pp->getLengthIPad() - (n - 1) * pp->getRowSpacing(); + if (z < min + eps || z > max - eps) { + // printf("z : min[%7.2f (%7.2f)] %7.2f max[(%7.2f) %7.2f]\n", min, min+eps, z, max-eps, max); + return true; + } + min = pp->getCol0(); + n = pp->getNcols(); + max = min + 2 * pp->getWidthOPad() + (n - 2) * pp->getWidthIPad() + (n - 1) * pp->getColSpacing(); + if (y < min + eps || y > max - eps) { + // printf("y : min[%7.2f (%7.2f)] %7.2f max[(%7.2f) %7.2f]\n", min, min+eps, y, max-eps, max); + return true; + } + + return false; +} diff --git a/Detectors/TRD/base/src/GeometryFlat.cxx b/Detectors/TRD/base/src/GeometryFlat.cxx new file mode 100644 index 0000000000000..34ca9ee05db9f --- /dev/null +++ b/Detectors/TRD/base/src/GeometryFlat.cxx @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDBase/GeometryFlat.h" +#include "TRDBase/Geometry.h" +#include "DataFormatsTRD/Constants.h" + +using namespace o2::trd; +using namespace o2::trd::constants; + +GeometryFlat::GeometryFlat(const Geometry& geo) +{ + GeometryBase& b1 = *this; + const GeometryBase& b2 = geo; + memcpy((void*)&b1, (void*)&b2, sizeof(b1)); + int matrixCount = 0; + for (int i = 0; i < MAXCHAMBER; i++) { + if (geo.chamberInGeometry(i)) { + double v[12]; + geo.getMatrixT2L(i).GetComponents(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11]); + float f[12]; + for (int k = 0; k < 12; k++) { + f[k] = v[k]; + } + mMatrixCache[matrixCount] = o2::gpu::Transform3D(f); + mMatrixIndirection[i] = matrixCount++; + } else { + mMatrixIndirection[i] = -1; + } + } +} diff --git a/Detectors/TRD/base/src/PadPlane.cxx b/Detectors/TRD/base/src/PadPlane.cxx new file mode 100644 index 0000000000000..bc8a65c485869 --- /dev/null +++ b/Detectors/TRD/base/src/PadPlane.cxx @@ -0,0 +1,160 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/////////////////////////////////////////////////////////////////////////////// +// // +// Describes a pad plane of a TRD ROC // +// // +// Contains the information on pad postions, pad dimensions, // +// tilting angle, etc. // +// It also provides methods to identify the current pad number from // +// global coordinates. // +// The numbering and coordinates should follow the official convention // +// (see David Emschermanns note on TRD convention) // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "TRDBase/PadPlane.h" +#include <TMath.h> +#include <FairLogger.h> + +using namespace o2::trd; + +//_____________________________________________________________________________ +void PadPlane::setTiltingAngle(double t) +{ + // + // Set the tilting angle of the pads + // + + mTiltingAngle = t; + mTiltingTan = TMath::Tan(TMath::Pi() / 180.0 * mTiltingAngle); +} + +//_____________________________________________________________________________ +int PadPlane::getPadRowNumber(double z) const +{ + // + // Finds the pad row number for a given z-position in local supermodule system + // + + int row = 0; + int nabove = 0; + int nbelow = 0; + int middle = 0; + + if ((z > getRow0()) || (z < getRowEnd())) { + row = -1; + + } else { + nabove = mNrows + 1; + nbelow = 0; + while (nabove - nbelow > 1) { + middle = (nabove + nbelow) / 2; + if (z == (mPadRow[middle - 1] + mPadRowSMOffset)) { + row = middle; + } + if (z > (mPadRow[middle - 1] + mPadRowSMOffset)) { + nabove = middle; + } else { + nbelow = middle; + } + } + row = nbelow - 1; + } + + return row; +} + +//_____________________________________________________________________________ +int PadPlane::getPadRowNumberROC(double z) const +{ + // + // Finds the pad row number for a given z-position in local ROC system + // + + int row = 0; + int nabove = 0; + int nbelow = 0; + int middle = 0; + + if ((z > getRow0ROC()) || (z < getRowEndROC())) { + row = -1; + + } else { + nabove = mNrows + 1; + nbelow = 0; + while (nabove - nbelow > 1) { + middle = (nabove + nbelow) / 2; + if (z == mPadRow[middle - 1]) { + row = middle; + } + if (z > mPadRow[middle - 1]) { + nabove = middle; + } else { + nbelow = middle; + } + } + row = nbelow - 1; + } + + return row; +} + +//_____________________________________________________________________________ +int PadPlane::getPadColNumber(double rphi) const +{ + // + // Finds the pad column number for a given rphi-position + // + + int col = 0; + int nabove = 0; + int nbelow = 0; + int middle = 0; + + if ((rphi < getCol0()) || (rphi > getColEnd())) { + col = -1; + + } else { + nabove = mNcols; + nbelow = 0; + while (nabove - nbelow > 1) { + middle = (nabove + nbelow) / 2; + if (rphi == mPadCol[middle]) { + col = middle; + } + if (rphi > mPadCol[middle]) { + nbelow = middle; + } else { + nabove = middle; + } + } + col = nbelow; + } + + return col; +} + +void PadPlane::setNcols(int n) +{ + if (n > MAXCOLS) { + LOG(FATAL) << "MAXCOLS exceeded " << n << " > " << MAXCOLS; + } + mNcols = n; +}; + +void PadPlane::setNrows(int n) +{ + if (n > MAXROWS) { + LOG(FATAL) << "MAXROWS exceeded " << n << " > " << MAXROWS; + } + mNrows = n; +}; diff --git a/Detectors/TRD/base/src/PadResponse.cxx b/Detectors/TRD/base/src/PadResponse.cxx index ce533a1d8a7df..c011add451b66 100644 --- a/Detectors/TRD/base/src/PadResponse.cxx +++ b/Detectors/TRD/base/src/PadResponse.cxx @@ -11,6 +11,7 @@ #include "TRDBase/PadResponse.h" using namespace o2::trd; +using namespace o2::trd::constants; void PadResponse::samplePRF() { @@ -19,7 +20,7 @@ void PadResponse::samplePRF() // constexpr int kPRFbin = 61; // arbitraty value - need documentation/ref. - constexpr float prf[kNlayer][kPRFbin] = { + constexpr float prf[NLAYER][kPRFbin] = { {2.9037e-02, 3.3608e-02, 3.9020e-02, 4.5292e-02, 5.2694e-02, 6.1362e-02, 7.1461e-02, 8.3362e-02, 9.7063e-02, 1.1307e-01, 1.3140e-01, 1.5235e-01, @@ -132,7 +133,7 @@ void PadResponse::samplePRF() int ipos2; float diff; - for (int iLayer = 0; iLayer < kNlayer; ++iLayer) { + for (int iLayer = 0; iLayer < NLAYER; ++iLayer) { for (int iBin = 0; iBin < mPRFbin; ++iBin) { float bin = (((float)iBin) + 0.5) * mPRFwid + mPRFlo; ipos1 = ipos2 = 0; @@ -146,8 +147,9 @@ void PadResponse::samplePRF() mPRFsmp[iLayer * mPRFbin + iBin] = prf[iLayer][ipos2 - 1]; } else { --ipos2; - if (ipos2 >= kPRFbin) + if (ipos2 >= kPRFbin) { ipos2 = kPRFbin - 1; + } ipos1 = ipos2 - 1; mPRFsmp[iLayer * mPRFbin + iBin] = prf[iLayer][ipos2] + diff * (prf[iLayer][ipos2] - prf[iLayer][ipos1]) / sPRFwid; } @@ -171,16 +173,16 @@ int PadResponse::getPRF(double signal, double dist, int layer, double* pad) cons pad[1] = 0; pad[2] = 0; - if ((iBin1 >= 0) && (iBin1 < (mPRFbin * kNlayer))) { + if ((iBin1 >= 0) && (iBin1 < (mPRFbin * NLAYER))) { if (iBin0 >= 0) { pad[0] = signal * mPRFsmp[iBin0]; } pad[1] = signal * mPRFsmp[iBin1]; - if (iBin2 < (mPRFbin * kNlayer)) { + if (iBin2 < (mPRFbin * NLAYER)) { pad[2] = signal * mPRFsmp[iBin2]; } return 1; } else { return 0; } -} \ No newline at end of file +} diff --git a/Detectors/TRD/base/src/SimParam.cxx b/Detectors/TRD/base/src/SimParam.cxx new file mode 100644 index 0000000000000..83223d8af1b23 --- /dev/null +++ b/Detectors/TRD/base/src/SimParam.cxx @@ -0,0 +1,295 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 containing constant simulation parameters // +// // +// Request an instance with SimParam::Instance() // +// Then request the needed values // +// // +//////////////////////////////////////////////////////////////////////////// + +#include "TRDBase/SimParam.h" +#include <TMath.h> +#include "TRDBase/CommonParam.h" +#include <FairLogger.h> + +using namespace o2::trd; +ClassImp(SimParam); + +SimParam* SimParam::fgInstance = nullptr; +bool SimParam::fgTerminated = false; + +//_ singleton implementation __________________________________________________ +SimParam* SimParam::Instance() +{ + // + // Singleton implementation + // Returns an instance of this class, it is created if neccessary + // + + if (fgTerminated != false) { + return nullptr; + } + + if (fgInstance == nullptr) { + fgInstance = new SimParam(); + } + + return fgInstance; +} + +//_ singleton implementation __________________________________________________ +void SimParam::Terminate() +{ + // + // Singleton implementation + // Deletes the instance of this class and sets the terminated flag, + // instances cannot be requested anymore + // This function can be called several times. + // + + fgTerminated = true; + + if (fgInstance != nullptr) { + delete fgInstance; + fgInstance = nullptr; + } +} + +//_____________________________________________________________________________ +SimParam::SimParam() + : mGasGain(0.0), + mNoise(0.0), + mChipGain(0.0), + mADCoutRange(0.0), + mADCinRange(0.0), + mADCbaseline(0), + mDiffusionOn(false), + mElAttachOn(false), + mElAttachProp(0.0), + mTRFOn(false), + mTRFsmp(nullptr), + mTRFbin(0), + mTRFlo(0.0), + mTRFhi(0.0), + mInvTRFwid(0.0), + mCTOn(false), + mCTsmp(nullptr), + mPadCoupling(0.0), + mTimeCoupling(0.0), + mTimeStructOn(false), + mPRFOn(false), + mNTimeBins(0), + mNTBoverwriteOCDB(false) +{ + // + // Default constructor + // + + Init(); +} + +//_____________________________________________________________________________ +void SimParam::Init() +{ + // + // Default initializiation + // + + // The default parameter for the digitization + mGasGain = 4000.0; + mChipGain = 12.4; + mNoise = 1250.0; + mADCoutRange = 1023.0; // 10-bit ADC + mADCinRange = 2000.0; // 2V input range + mADCbaseline = 10; + + // Diffusion on + mDiffusionOn = true; + + // Propability for electron attachment + mElAttachOn = false; + mElAttachProp = 0.0; + + // The time response function + mTRFOn = true; + + // The cross talk + mCTOn = true; + + // The pad coupling factor + // Use 0.46, instead of the theroetical value 0.3, since it reproduces better + // the test beam data, even tough it is not understood why. + mPadCoupling = 0.46; + + // The time coupling factor (same number as for the TPC) + mTimeCoupling = 0.4; + + // Use drift time maps + mTimeStructOn = true; + + // The pad response function + mPRFOn = true; + + // The number of time bins + mNTimeBins = 22; + mNTBoverwriteOCDB = false; + + ReInit(); +} + +//_____________________________________________________________________________ +SimParam::~SimParam() +{ + // + // Destructor + // + if (mTRFsmp) { + delete[] mTRFsmp; + mTRFsmp = nullptr; + } + + if (mCTsmp) { + delete[] mCTsmp; + mCTsmp = nullptr; + } +} + +//_____________________________________________________________________________ +void SimParam::ReInit() +{ + // + // Reinitializes the parameter class after a change + // + + if (CommonParam::Instance()->IsXenon()) { + // The range and the binwidth for the sampled TRF + mTRFbin = 200; + // Start 0.2 mus before the signal + mTRFlo = -0.4; + // End the maximum drift time after the signal + mTRFhi = 3.58; + // Standard gas gain + mGasGain = 4000.0; + } else if (CommonParam::Instance()->IsArgon()) { + // The range and the binwidth for the sampled TRF + mTRFbin = 50; + // Start 0.2 mus before the signal + mTRFlo = 0.02; + // End the maximum drift time after the signal + mTRFhi = 1.98; + // Higher gas gain + mGasGain = 8000.0; + } else { + LOG(FATAL) << "Not a valid gas mixture!\n"; + } + mInvTRFwid = ((float)mTRFbin) / (mTRFhi - mTRFlo); // Inverse of the bin width of the integrated TRF + + // Create the sampled TRF + SampleTRF(); +} + +//_____________________________________________________________________________ +void SimParam::SampleTRF() +{ + // + // Samples the new time response function. + // + + int ipasa = 0; + + // Xenon + // From Antons measurements with Fe55 source, adjusted by C. Lippmann. + // time bins are -0.4, -0.38, -0.36, ...., 3.54, 3.56, 3.58 microseconds + const int kNpasa = 200; // kNpasa should be equal to fTRFbin! + float xtalk[kNpasa]; + float signal[kNpasa] = { + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0002, 0.0007, 0.0026, 0.0089, 0.0253, 0.0612, 0.1319, + 0.2416, 0.3913, 0.5609, 0.7295, 0.8662, 0.9581, 1.0000, 0.9990, 0.9611, 0.8995, 0.8269, 0.7495, 0.6714, 0.5987, + 0.5334, 0.4756, 0.4249, 0.3811, 0.3433, 0.3110, 0.2837, 0.2607, 0.2409, 0.2243, 0.2099, 0.1974, 0.1868, 0.1776, + 0.1695, 0.1627, 0.1566, 0.1509, 0.1457, 0.1407, 0.1362, 0.1317, 0.1274, 0.1233, 0.1196, 0.1162, 0.1131, 0.1102, + 0.1075, 0.1051, 0.1026, 0.1004, 0.0979, 0.0956, 0.0934, 0.0912, 0.0892, 0.0875, 0.0858, 0.0843, 0.0829, 0.0815, + 0.0799, 0.0786, 0.0772, 0.0757, 0.0741, 0.0729, 0.0718, 0.0706, 0.0692, 0.0680, 0.0669, 0.0655, 0.0643, 0.0630, + 0.0618, 0.0607, 0.0596, 0.0587, 0.0576, 0.0568, 0.0558, 0.0550, 0.0541, 0.0531, 0.0522, 0.0513, 0.0505, 0.0497, + 0.0490, 0.0484, 0.0474, 0.0465, 0.0457, 0.0449, 0.0441, 0.0433, 0.0425, 0.0417, 0.0410, 0.0402, 0.0395, 0.0388, + 0.0381, 0.0374, 0.0368, 0.0361, 0.0354, 0.0348, 0.0342, 0.0336, 0.0330, 0.0324, 0.0318, 0.0312, 0.0306, 0.0301, + 0.0296, 0.0290, 0.0285, 0.0280, 0.0275, 0.0270, 0.0265, 0.0260, 0.0256, 0.0251, 0.0246, 0.0242, 0.0238, 0.0233, + 0.0229, 0.0225, 0.0221, 0.0217, 0.0213, 0.0209, 0.0206, 0.0202, 0.0198, 0.0195, 0.0191, 0.0188, 0.0184, 0.0181, + 0.0178, 0.0175, 0.0171, 0.0168, 0.0165, 0.0162, 0.0159, 0.0157, 0.0154, 0.0151, 0.0148, 0.0146, 0.0143, 0.0140, + 0.0138, 0.0135, 0.0133, 0.0131, 0.0128, 0.0126, 0.0124, 0.0121, 0.0119, 0.0120, 0.0115, 0.0113, 0.0111, 0.0109, + 0.0107, 0.0105, 0.0103, 0.0101, 0.0100, 0.0098, 0.0096, 0.0094, 0.0092, 0.0091, 0.0089, 0.0088, 0.0086, 0.0084, + 0.0083, 0.0081, 0.0080, 0.0078}; + signal[0] = 0.0; + signal[1] = 0.0; + signal[2] = 0.0; + // With undershoot, positive peak corresponds to ~3% of the main signal: + for (ipasa = 3; ipasa < kNpasa; ipasa++) { + xtalk[ipasa] = 0.2 * (signal[ipasa - 2] - signal[ipasa - 3]); + } + xtalk[0] = 0.0; + xtalk[1] = 0.0; + xtalk[2] = 0.0; + + // Argon + // Ar measurement with Fe55 source by Anton + // time bins are 0.02, 0.06, 0.10, ...., 1.90, 1.94, 1.98 microseconds + const int kNpasaAr = 50; + float xtalkAr[kNpasaAr]; + float signalAr[kNpasaAr] = {-0.01, 0.01, 0.00, 0.00, 0.01, -0.01, 0.01, 2.15, 22.28, 55.53, 68.52, 58.21, 40.92, + 27.12, 18.49, 13.42, 10.48, 8.67, 7.49, 6.55, 5.71, 5.12, 4.63, 4.22, 3.81, 3.48, + 3.20, 2.94, 2.77, 2.63, 2.50, 2.37, 2.23, 2.13, 2.03, 1.91, 1.83, 1.75, 1.68, + 1.63, 1.56, 1.49, 1.50, 1.49, 1.29, 1.19, 1.21, 1.21, 1.20, 1.10}; + // Normalization to maximum + for (ipasa = 0; ipasa < kNpasaAr; ipasa++) { + signalAr[ipasa] /= 68.52; + } + signalAr[0] = 0.0; + signalAr[1] = 0.0; + signalAr[2] = 0.0; + // With undershoot, positive peak corresponds to ~3% of the main signal: + for (ipasa = 3; ipasa < kNpasaAr; ipasa++) { + xtalkAr[ipasa] = 0.2 * (signalAr[ipasa - 2] - signalAr[ipasa - 3]); + } + xtalkAr[0] = 0.0; + xtalkAr[1] = 0.0; + xtalkAr[2] = 0.0; + + if (mTRFsmp) { + delete[] mTRFsmp; + } + mTRFsmp = new float[mTRFbin]; + + if (mCTsmp) { + delete[] mCTsmp; + } + mCTsmp = new float[mTRFbin]; + + if (CommonParam::Instance()->IsXenon()) { + if (mTRFbin != kNpasa) { + LOG(ERROR) << "Array mismatch (xenon)\n\n"; + } + } else if (CommonParam::Instance()->IsArgon()) { + if (mTRFbin != kNpasaAr) { + LOG(ERROR) << "Array mismatch (argon)\n\n"; + } + } + + for (int iBin = 0; iBin < mTRFbin; iBin++) { + if (CommonParam::Instance()->IsXenon()) { + mTRFsmp[iBin] = signal[iBin]; + mCTsmp[iBin] = xtalk[iBin]; + } else if (CommonParam::Instance()->IsArgon()) { + mTRFsmp[iBin] = signalAr[iBin]; + mCTsmp[iBin] = xtalkAr[iBin]; + } + } +} diff --git a/Detectors/TRD/base/src/TRDBaseLinkDef.h b/Detectors/TRD/base/src/TRDBaseLinkDef.h index fc6ab2dbf8ac9..7b9247326057c 100644 --- a/Detectors/TRD/base/src/TRDBaseLinkDef.h +++ b/Detectors/TRD/base/src/TRDBaseLinkDef.h @@ -14,11 +14,11 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::trd::TRDPadPlane + ; -#pragma link C++ class o2::trd::TRDGeometry + ; -#pragma link C++ class o2::trd::TRDGeometryBase + ; -#pragma link C++ class o2::trd::TRDCommonParam + ; -#pragma link C++ class o2::trd::TRDSimParam + ; +#pragma link C++ class o2::trd::PadPlane + ; +#pragma link C++ class o2::trd::Geometry + ; +#pragma link C++ class o2::trd::GeometryBase + ; +#pragma link C++ class o2::trd::CommonParam + ; +#pragma link C++ class o2::trd::SimParam + ; #pragma link C++ class o2::trd::Digit + ; #pragma link C++ class std::vector < o2::trd::Digit> + ; #pragma link C++ class o2::trd::FeeParam + ; @@ -46,5 +46,9 @@ #pragma link C++ class std::vector < o2::trd::Tracklet > +; #include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" + +// this is just needed to please the DPL ROOTTreeWriter facility +#pragma link C++ class o2::dataformats::ConstMCTruthContainer < o2::trd::MCLabel> + ; #endif diff --git a/Detectors/TRD/base/src/TRDCalPadStatus.cxx b/Detectors/TRD/base/src/TRDCalPadStatus.cxx deleted file mode 100644 index 6a91bf074a941..0000000000000 --- a/Detectors/TRD/base/src/TRDCalPadStatus.cxx +++ /dev/null @@ -1,275 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/////////////////////////////////////////////////////////////////////////////// -// // -// TRD calibration class for the single pad status // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include <TH1F.h> -#include <TH2F.h> -#include <TStyle.h> -#include <TCanvas.h> - -#include "TRDBase/TRDGeometryBase.h" -#include "DetectorsCommonDataFormats/DetMatrixCache.h" -#include "DetectorsCommonDataFormats/DetID.h" - -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDPadPlane.h" -#include "TRDBase/TRDCalSingleChamberStatus.h" // test -#include "TRDBase/TRDCalPadStatus.h" - -using namespace o2::trd; - -//_____________________________________________________________________________ -TRDCalPadStatus::TRDCalPadStatus() -{ - // - // TRDCalPadStatus default constructor - // - - for (int idet = 0; idet < kNdet; idet++) { - mROC[idet] = nullptr; - } -} - -//_____________________________________________________________________________ -TRDCalPadStatus::TRDCalPadStatus(const Text_t* name, const Text_t* title) -{ - // - // TRDCalPadStatus constructor - // - //TRDGeometry fgeom; - for (int isec = 0; isec < kNsect; isec++) { - for (int ipla = 0; ipla < kNplan; ipla++) { - for (int icha = 0; icha < kNcham; icha++) { - int idet = o2::trd::TRDGeometry::getDetector(ipla, icha, isec); - // int idet = fgeom.getDetector(ipla,icha,isec);//TRDGeometryBase::getDetector(ipla,icha,isec); - mROC[idet] = new TRDCalSingleChamberStatus(ipla, icha, 144); - } - } - } - mName = name; - mTitle = title; -} - -//_____________________________________________________________________________ -TRDCalPadStatus::TRDCalPadStatus(const TRDCalPadStatus& c) -{ - // - // TRDCalPadStatus copy constructor - // - - ((TRDCalPadStatus&)c).Copy(*this); -} - -//_____________________________________________________________________________ -TRDCalPadStatus::~TRDCalPadStatus() -{ - // - // TRDCalPadStatus destructor - // - - for (int idet = 0; idet < kNdet; idet++) { - if (mROC[idet]) { - delete mROC[idet]; - mROC[idet] = nullptr; - } - } -} - -//_____________________________________________________________________________ -TRDCalPadStatus& TRDCalPadStatus::operator=(const TRDCalPadStatus& c) -{ - // - // Assignment operator - // - - if (this != &c) - ((TRDCalPadStatus&)c).Copy(*this); - return *this; -} - -//_____________________________________________________________________________ -void TRDCalPadStatus::Copy(TRDCalPadStatus& c) const -{ - // - // Copy function - // - - for (int idet = 0; idet < kNdet; idet++) { - if (mROC[idet]) { - mROC[idet]->Copy(*((TRDCalPadStatus&)c).mROC[idet]); - } - } -} - -//_____________________________________________________________________________ -Bool_t TRDCalPadStatus::checkStatus(int d, int col, int row, int bitMask) const -{ - // - // Checks the pad status - // - - TRDCalSingleChamberStatus* roc = getCalROC(d); - if (!roc) { - return kFALSE; - } else { - return (roc->getStatus(col, row) & bitMask) ? kTRUE : kFALSE; - } -} - -//_____________________________________________________________________________ -TRDCalSingleChamberStatus* TRDCalPadStatus::getCalROC(int p, int c, int s) const -{ - // - // Returns the readout chamber of this pad - // - //TRDGeometry fgeom; - //return mROC[fgeom.getDetector(p,c,s)]; - return mROC[o2::trd::TRDGeometry::getDetector(p, c, s)]; -} - -//_____________________________________________________________________________ -TH1F* TRDCalPadStatus::makeHisto1D() -{ - // - // Make 1D histo - // - - char name[1000]; - snprintf(name, 1000, "%s Pad 1D", getTitle().c_str()); - TH1F* his = new TH1F(name, name, 6, -0.5, 5.5); - his->GetXaxis()->SetBinLabel(1, "Good"); - his->GetXaxis()->SetBinLabel(2, "Masked"); - his->GetXaxis()->SetBinLabel(3, "PadBridgedLeft"); - his->GetXaxis()->SetBinLabel(4, "PadBridgedRight"); - his->GetXaxis()->SetBinLabel(5, "ReadSecond"); - his->GetXaxis()->SetBinLabel(6, "NotConnected"); - - for (int idet = 0; idet < kNdet; idet++) { - if (mROC[idet]) { - for (int ichannel = 0; ichannel < mROC[idet]->getNchannels(); ichannel++) { - int status = (int)mROC[idet]->getStatus(ichannel); - if (status == 2) - status = 1; - if (status == 4) - status = 2; - if (status == 8) - status = 3; - if (status == 16) - status = 4; - if (status == 32) - status = 5; - his->Fill(status); - } - } - } - - return his; -} - -//_____________________________________________________________________________ -TH2F* TRDCalPadStatus::makeHisto2DSmPl(int sm, int pl) -{ - // - // Make 2D graph - // - - gStyle->SetPalette(1); - TRDGeometry* trdGeo = new TRDGeometry(); - const TRDPadPlane* padPlane0 = trdGeo->getPadPlane(pl, 0); - Double_t row0 = padPlane0->getRow0(); - Double_t col0 = padPlane0->getCol0(); - - char name[1000]; - snprintf(name, 1000, "%s Pad 2D sm %d pl %d", getTitle().c_str(), sm, pl); - TH2F* his = new TH2F(name, name, 88, -TMath::Abs(row0), TMath::Abs(row0), 148, -TMath::Abs(col0), TMath::Abs(col0)); - - // Where we begin - int offsetsmpl = 30 * sm + pl; - - for (int k = 0; k < kNcham; k++) { - int det = offsetsmpl + k * 6; - if (mROC[det]) { - TRDCalSingleChamberStatus* calRoc = mROC[det]; - for (int icol = 0; icol < calRoc->getNcols(); icol++) { - for (int irow = 0; irow < calRoc->getNrows(); irow++) { - int binz = 0; - int kb = kNcham - 1 - k; - int krow = calRoc->getNrows() - 1 - irow; - int kcol = calRoc->getNcols() - 1 - icol; - if (kb > 2) - binz = 16 * (kb - 1) + 12 + krow + 1 + 2 * (kb + 1); - else - binz = 16 * kb + krow + 1 + 2 * (kb + 1); - int biny = kcol + 1 + 2; - Float_t value = calRoc->getStatus(icol, irow); - his->SetBinContent(binz, biny, value); - } - } - for (int icol = 1; icol < 147; icol++) { - for (int l = 0; l < 2; l++) { - int binz = 0; - int kb = kNcham - 1 - k; - if (kb > 2) - binz = 16 * (kb - 1) + 12 + 1 + 2 * (kb + 1) - (l + 1); - else - binz = 16 * kb + 1 + 2 * (kb + 1) - (l + 1); - his->SetBinContent(binz, icol, 50.0); - } - } - } - } - for (int icol = 1; icol < 147; icol++) { - his->SetBinContent(88, icol, 50.0); - his->SetBinContent(87, icol, 50.0); - } - for (int irow = 1; irow < 89; irow++) { - his->SetBinContent(irow, 1, 50.0); - his->SetBinContent(irow, 2, 50.0); - his->SetBinContent(irow, 147, 50.0); - his->SetBinContent(irow, 148, 50.0); - } - - his->SetXTitle("z (cm)"); - his->SetYTitle("y (cm)"); - his->SetMaximum(50); - his->SetMinimum(0.0); - his->SetStats(0); - - return his; -} - -//_____________________________________________________________________________ -void TRDCalPadStatus::plotHistos2DSm(int sm, const char* name) -{ - // - // Make 2D graph - // - - gStyle->SetPalette(1); - TCanvas* c1 = new TCanvas(name, name, 50, 50, 600, 800); - c1->Divide(3, 2); - c1->cd(1); - makeHisto2DSmPl(sm, 0)->Draw("colz"); - c1->cd(2); - makeHisto2DSmPl(sm, 1)->Draw("colz"); - c1->cd(3); - makeHisto2DSmPl(sm, 2)->Draw("colz"); - c1->cd(4); - makeHisto2DSmPl(sm, 3)->Draw("colz"); - c1->cd(5); - makeHisto2DSmPl(sm, 4)->Draw("colz"); - c1->cd(6); - makeHisto2DSmPl(sm, 5)->Draw("colz"); -} diff --git a/Detectors/TRD/base/src/TRDCalSingleChamberStatus.cxx b/Detectors/TRD/base/src/TRDCalSingleChamberStatus.cxx deleted file mode 100644 index bc44f6a19dbf7..0000000000000 --- a/Detectors/TRD/base/src/TRDCalSingleChamberStatus.cxx +++ /dev/null @@ -1,156 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/////////////////////////////////////////////////////////////////////////////// -// // -// Calibration base class for a single ROC // -// Contains one char value per pad // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include "TRDBase/TRDCalSingleChamberStatus.h" -#include <TMath.h> -#include <Rtypes.h> -#include "TRDBase/TRDCommonParam.h" - -using namespace o2::trd; - -//_____________________________________________________________________________ -TRDCalSingleChamberStatus::TRDCalSingleChamberStatus() = default; - -//_____________________________________________________________________________ -TRDCalSingleChamberStatus::TRDCalSingleChamberStatus(Int_t p, Int_t c, Int_t cols) - : mPla(p), mCha(c), mNcols(cols) -{ - // - // Constructor that initializes a given pad plane type - // - - // - // The pad plane parameter - // - switch (p) { - case 0: - if (c == 2) { - // L0C0 type - mNrows = 12; - } else { - // L0C1 type - mNrows = 16; - } - break; - case 1: - if (c == 2) { - // L1C0 type - mNrows = 12; - } else { - // L1C1 type - mNrows = 16; - } - break; - case 2: - if (c == 2) { - // L2C0 type - mNrows = 12; - } else { - // L2C1 type - mNrows = 16; - } - break; - case 3: - if (c == 2) { - // L3C0 type - mNrows = 12; - } else { - // L3C1 type - mNrows = 16; - } - break; - case 4: - if (c == 2) { - // L4C0 type - mNrows = 12; - } else { - // L4C1 type - mNrows = 16; - } - break; - case 5: - if (c == 2) { - // L5C0 type - mNrows = 12; - } else { - // L5C1 type - mNrows = 16; - } - break; - }; - - mNchannels = mNrows * mNcols; - if (mNchannels != 0) { - mData.resize(mNchannels); - } - memset(&mData[0], 0, sizeof(mData[0]) * mData.size()); -} - -//_____________________________________________________________________________ -TRDCalSingleChamberStatus::TRDCalSingleChamberStatus(const TRDCalSingleChamberStatus& c) - : mPla(c.mPla), mCha(c.mCha), mNrows(c.mNrows), mNcols(c.mNcols), mNchannels(c.mNchannels) -{ - // - // TRDCalSingleChamberStatus copy constructor - // - - mData = c.mData; -} - -//_____________________________________________________________________________ -TRDCalSingleChamberStatus::~TRDCalSingleChamberStatus() = default; - -//_____________________________________________________________________________ -TRDCalSingleChamberStatus& TRDCalSingleChamberStatus::operator=(const TRDCalSingleChamberStatus& c) -{ - // - // Assignment operator - // - - if (this == &c) { - return *this; - } - - mPla = c.mPla; - mCha = c.mCha; - mNrows = c.mNrows; - mNcols = c.mNcols; - mNchannels = c.mNchannels; - mData = c.mData; - - return *this; -} - -//_____________________________________________________________________________ -void TRDCalSingleChamberStatus::Copy(TRDCalSingleChamberStatus& c) const -{ - // - // Copy function - // - - Int_t iBin = 0; - - c.mPla = mPla; - c.mCha = mCha; - - c.mNrows = mNrows; - c.mNcols = mNcols; - - c.mNchannels = mNchannels; - - c.mData = mData; -} diff --git a/Detectors/TRD/base/src/TRDCommonParam.cxx b/Detectors/TRD/base/src/TRDCommonParam.cxx deleted file mode 100644 index 41dc82e340602..0000000000000 --- a/Detectors/TRD/base/src/TRDCommonParam.cxx +++ /dev/null @@ -1,740 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 containing parameters common to simulation and reconstruction // -// // -// Request an instance with AliTRDCommonParam::Instance() // -// Then request the needed values // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include <TGeoGlobalMagField.h> -#include <TMath.h> - -#include <FairLogger.h> -#include "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDSimParam.h" - -#include "Field/MagneticField.h" - -using namespace o2::trd; -ClassImp(TRDCommonParam); - -TRDCommonParam* TRDCommonParam::fgInstance = nullptr; -bool TRDCommonParam::fgTerminated = false; - -//_ singleton implementation __________________________________________________ -TRDCommonParam* TRDCommonParam::Instance() -{ - // - // Singleton implementation - // Returns an instance of this class, it is created if neccessary - // - - if (fgTerminated != false) { - return nullptr; - } - - if (fgInstance == nullptr) { - fgInstance = new TRDCommonParam(); - } - - return fgInstance; -} - -//_____________________________________________________________________________ -void TRDCommonParam::Terminate() -{ - // - // Singleton implementation - // Deletes the instance of this class and sets the terminated flag, - // instances cannot be requested anymore - // This function can be called several times. - // - - fgTerminated = true; - - if (fgInstance != nullptr) { - delete fgInstance; - fgInstance = nullptr; - } -} - -//_____________________________________________________________________________ -TRDCommonParam::TRDCommonParam() - : mExBOn(true), - mDiffusionT(0.0), - mDiffusionL(0.0), - mDiffLastVdrift(-1.0), - mTimeStruct1(nullptr), - mTimeStruct2(nullptr), - mVDlo(0.0), - mVDhi(0.0), - mTimeLastVdrift(-1.0), - mSamplingFrequency(10.0), - mGasMixture(kXenon), - mField(-0.5) -{ - // - // Default constructor - // -} - -//_____________________________________________________________________________ -TRDCommonParam::~TRDCommonParam() -{ - // - // Destructor - // - if (mTimeStruct1) { - delete[] mTimeStruct1; - mTimeStruct1 = nullptr; - } - if (mTimeStruct2) { - delete[] mTimeStruct2; - mTimeStruct2 = nullptr; - } -} - -//_____________________________________________________________________________ -bool TRDCommonParam::cacheMagField() -{ - // The magnetic field strength - const o2::field::MagneticField* fld = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); - if (!fld) { - LOG(FATAL) << "Magnetic field is not initialized!"; - return false; - } - mField = 0.1 * fld->solenoidField(); // kGauss -> Tesla - return true; -} - -//_____________________________________________________________________________ -float TRDCommonParam::GetOmegaTau(float vdrift) -{ - /* - // - // Returns omega*tau (tan(Lorentz-angle)) for a given drift - // velocity <vdrift> for Xe/CO2 (15%). - // The values are according to a GARFIELD simulation. - // - - AliMagF* fld = (AliMagF *) TGeoGlobalMagField::Instance()->GetField(); - if (!fld) { - return 0.0; - } - double bz = 0.1 * fld->SolenoidField(); // kGauss -> Tesla - double fieldAbs = TMath::Abs(bz); - double fieldSgn = (bz > 0.0) ? 1.0 : -1.0; - - const int kNb = 5; - float p0[kNb] = { 0.004810, 0.007412, 0.010252, 0.013409, 0.016888 }; - float p1[kNb] = { 0.054875, 0.081534, 0.107333, 0.131983, 0.155455 }; - float p2[kNb] = { -0.008682, -0.012896, -0.016987, -0.020880, -0.024623 }; - float p3[kNb] = { 0.000155, 0.000238, 0.000330, 0.000428, 0.000541 }; - - // No ExB if field is too small (or zero) - if (fieldAbs < 0.01) { - - return 0.0; - - } - // Calculate ExB from parametrization - else if (IsXenon()) { - - int ib = ((int) (10 * (fieldAbs - 0.15))); - ib = TMath::Max( 0,ib); - ib = TMath::Min(kNb-1,ib); - - float alphaL = p0[ib] - + p1[ib] * vdrift - + p2[ib] * vdrift*vdrift - + p3[ib] * vdrift*vdrift*vdrift; - - return TMath::Tan(fieldSgn * alphaL); - - } - else if (IsArgon()) { - - return 0.0219769; - - } - - return 0.0; - */ - return 0.; // fix compiler warning -} - -//_____________________________________________________________________________ -bool TRDCommonParam::GetDiffCoeff(float& dl, float& dt, float vdrift) -{ - // - // Calculates the diffusion coefficients in longitudinal <dl> and - // transverse <dt> direction for a given drift velocity <vdrift> - // - - // Nothing to do - if (TMath::Abs(mDiffLastVdrift - vdrift) < 0.001) { - dl = mDiffusionL; - dt = mDiffusionT; - return true; - } - mDiffLastVdrift = vdrift; - - if (IsXenon()) { - // - // Vd and B-field dependent diffusion and Lorentz angle - // - - // kNb = kNb = kNb - constexpr int kNb = 5; - - // If looking at compatibility with AliRoot: - // ibL and ibT are calculated the same way so, just define ib = ibL = ibT - int ib = ((int)(10 * (mField - 0.15))); - ib = TMath::Max(0, ib); - ib = TMath::Min(kNb - 1, ib); - - // DiffusionT - constexpr float p0T[kNb] = {0.009550, 0.009599, 0.009674, 0.009757, 0.009850}; - constexpr float p1T[kNb] = {0.006667, 0.006539, 0.006359, 0.006153, 0.005925}; - constexpr float p2T[kNb] = {-0.000853, -0.000798, -0.000721, -0.000635, -0.000541}; - constexpr float p3T[kNb] = {0.000131, 0.000122, 0.000111, 0.000098, 0.000085}; - // DiffusionL - constexpr float p0L[kNb] = {0.007440, 0.007493, 0.007513, 0.007672, 0.007831}; - constexpr float p1L[kNb] = {0.019252, 0.018912, 0.018636, 0.018012, 0.017343}; - constexpr float p2L[kNb] = {-0.005042, -0.004926, -0.004867, -0.004650, -0.004424}; - constexpr float p3L[kNb] = {0.000195, 0.000189, 0.000195, 0.000182, 0.000169}; - - float v2 = vdrift * vdrift; - float v3 = v2 * vdrift; - mDiffusionL = p0L[ib] + p1L[ib] * vdrift + p2L[ib] * v2 + p3L[ib] * v3; - mDiffusionT = p0T[ib] + p1T[ib] * vdrift + p2T[ib] * v2 + p3T[ib] * v3; - - dl = mDiffusionL; - dt = mDiffusionT; - return true; - } else if (IsArgon()) { - // - // Diffusion constants and Lorentz angle only for B = 0.5T - // - mDiffusionL = 0.0182; - mDiffusionT = 0.0159; - dl = mDiffusionL; - dt = mDiffusionT; - return true; - } else { - return false; - } -} - -//_____________________________________________________________________________ -double TRDCommonParam::TimeStruct(float vdrift, double dist, double z) -{ - // - // Applies the time structure of the drift cells (by C.Lippmann). - // The drift time of electrons to the anode wires depends on the - // distance to the wire (z) and on the position in the drift region. - // - // input : - // dist = radial distance from (cathode) pad plane [cm] - // z = distance from anode wire (parallel to cathode planes) [cm] - // - // output : - // tdrift = the drift time of an electron at the given position - // - // We interpolate between the drift time values at the two drift - // velocities fVDlo and fVDhi, being smaller and larger than - // fDriftVelocity. We use the two stored drift time maps fTimeStruct1 - // and fTimeStruct2, calculated for the two mentioned drift velocities. - // - - SampleTimeStruct(vdrift); - - // Indices: - int r1 = (int)(10 * dist); - if (r1 < 0) - r1 = 0; - if (r1 > 37) - r1 = 37; - int r2 = r1 + 1; - if (r2 > 37) - r2 = 37; - const int kz1 = ((int)(100 * z / 2.5)); - const int kz2 = kz1 + 1; - - if ((r1 < 0) || (r1 > 37) || (kz1 < 0) || (kz1 > 10)) { - LOG(INFO) << Form("Indices out of range: dist=%.2f, z=%.2f, r1=%d, kz1=%d", dist, z, r1, kz1); - } - - const float ky111 = mTimeStruct1[r1 + 38 * kz1]; - const float ky221 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct1[r2 + 38 * kz2] : mTimeStruct1[37 + 38 * 10]; - const float ky121 = (kz2 <= 10) ? mTimeStruct1[r1 + 38 * kz2] : mTimeStruct1[r1 + 38 * 10]; - const float ky211 = mTimeStruct1[r2 + 38 * kz1]; - - const float ky112 = mTimeStruct2[r1 + 38 * kz1]; - const float ky222 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct2[r2 + 38 * kz2] : mTimeStruct2[37 + 38 * 10]; - const float ky122 = (kz2 <= 10) ? mTimeStruct2[r1 + 38 * kz2] : mTimeStruct2[r1 + 38 * 10]; - const float ky212 = mTimeStruct2[r2 + 38 * kz1]; - - // Interpolation in dist-directions, lower drift time map - const float ky11 = (ky211 - ky111) * 10 * dist + ky111 - (ky211 - ky111) * r1; - const float ky21 = (ky221 - ky121) * 10 * dist + ky121 - (ky221 - ky121) * r1; - - // Interpolation in dist-direction, larger drift time map - const float ky12 = (ky212 - ky112) * 10 * dist + ky112 - (ky212 - ky112) * r1; - const float ky22 = (ky222 - ky122) * 10 * dist + ky122 - (ky222 - ky122) * r1; - - // Dist now is the drift distance to the anode wires (negative if electrons are - // between anode wire plane and cathode pad plane) - dist -= TRDGeometry::amThick() / 2.0; - - // Interpolation in z-directions, lower drift time map - const float ktdrift1 = - ((TMath::Abs(dist) > 0.005) || (z > 0.005)) ? (ky21 - ky11) * 100 * z / 2.5 + ky11 - (ky21 - ky11) * kz1 : 0.0; - // Interpolation in z-directions, larger drift time map - const float ktdrift2 = - ((TMath::Abs(dist) > 0.005) || (z > 0.005)) ? (ky22 - ky12) * 100 * z / 2.5 + ky12 - (ky22 - ky12) * kz1 : 0.0; - - // Interpolation between the values at fVDlo and fVDhi - float a = (ktdrift2 - ktdrift1) / (mVDhi - mVDlo); - float b = ktdrift2 - a * mVDhi; - float t = a * vdrift + b; - - return t; -} - -//_____________________________________________________________________________ -void TRDCommonParam::SampleTimeStruct(float vdrift) -{ - // - // Samples the timing structure of a drift cell - // Drift Time data calculated with Garfield (by C.Lippmann) - // - - // Nothing to do - if (TMath::Abs(mTimeLastVdrift - vdrift) < 1.e-3) { - return; - } - mTimeLastVdrift = vdrift; - - // Drift time maps are saved for some drift velocity values (in drift region): - float fVDsmp[8]; - fVDsmp[0] = 1.032; - fVDsmp[1] = 1.158; - fVDsmp[2] = 1.299; - fVDsmp[3] = 1.450; - fVDsmp[4] = 1.610; - fVDsmp[5] = 1.783; - fVDsmp[6] = 1.959; - fVDsmp[7] = 2.134; - - if (vdrift < fVDsmp[0]) { - LOG(INFO) << Form("Drift Velocity too small (%.3f<%.3f)", vdrift, fVDsmp[0]); - vdrift = fVDsmp[0]; - } else if (vdrift > fVDsmp[7]) { - LOG(INFO) << Form("Drift Velocity too large (%.3f>%.3f)", vdrift, fVDsmp[6]); - vdrift = fVDsmp[7]; - } - - const int ktimebin = 38; - const int kZbin = 11; - - // Garfield simulation at UD = -1500V; vd = 1.032cm/microsec, <driftfield> = 525V/cm - float time1500[ktimebin][kZbin] = { - {0.09170, 0.09205, 0.09306, 0.09475, 0.09716, 0.10035, 0.10445, 0.10993, 0.11838, 0.13986, 0.37858}, - {0.06588, 0.06626, 0.06739, 0.06926, 0.07186, 0.07524, 0.07951, 0.08515, 0.09381, 0.11601, 0.35673}, - {0.03946, 0.04003, 0.04171, 0.04435, 0.04780, 0.05193, 0.05680, 0.06306, 0.07290, 0.09873, 0.34748}, - {0.01151, 0.01283, 0.01718, 0.02282, 0.02880, 0.03479, 0.04098, 0.04910, 0.06413, 0.10567, 0.36897}, - {0.01116, 0.01290, 0.01721, 0.02299, 0.02863, 0.03447, 0.04074, 0.04984, 0.06839, 0.11625, 0.37277}, - {0.03919, 0.03974, 0.04131, 0.04380, 0.04703, 0.05102, 0.05602, 0.06309, 0.07651, 0.10938, 0.36838}, - {0.06493, 0.06560, 0.06640, 0.06802, 0.07051, 0.07392, 0.07853, 0.08510, 0.09690, 0.12621, 0.38058}, - {0.09174, 0.09186, 0.09225, 0.09303, 0.09477, 0.00000, 0.11205, 0.11952, 0.13461, 0.16984, 0.43017}, - {0.14356, 0.14494, 0.14959, 0.16002, 0.18328, 0.27981, 0.22785, 0.21240, 0.21948, 0.25965, 0.52392}, - {0.23120, 0.23366, 0.24046, 0.25422, 0.28071, 0.36914, 0.32999, 0.31208, 0.31772, 0.35804, 0.62249}, - {0.32686, 0.32916, 0.33646, 0.35053, 0.37710, 0.46292, 0.42773, 0.40948, 0.41497, 0.45527, 0.71955}, - {0.42353, 0.42583, 0.43317, 0.44727, 0.47380, 0.55884, 0.52479, 0.50650, 0.51194, 0.55225, 0.81658}, - {0.52038, 0.52271, 0.53000, 0.54415, 0.57064, 0.65545, 0.62172, 0.60341, 0.60885, 0.64915, 0.91339}, - {0.61724, 0.61953, 0.62694, 0.64098, 0.66756, 0.75226, 0.71862, 0.70030, 0.70575, 0.74604, 1.01035}, - {0.71460, 0.71678, 0.72376, 0.73786, 0.76447, 0.84913, 0.81551, 0.79720, 0.80264, 0.84292, 1.10723}, - {0.81101, 0.81334, 0.82066, 0.83475, 0.86127, 0.94599, 0.91240, 0.89408, 0.89952, 0.93981, 1.20409}, - {0.90788, 0.91023, 0.91752, 0.93163, 0.95815, 1.04293, 1.00929, 0.99096, 0.99640, 1.03669, 1.30106}, - {1.00477, 1.00707, 1.01449, 1.02852, 1.05504, 1.13976, 1.10617, 1.08784, 1.09329, 1.13358, 1.39796}, - {1.10166, 1.10397, 1.11130, 1.12541, 1.15257, 1.23672, 1.20307, 1.18472, 1.19018, 1.23046, 1.49486}, - {1.19854, 1.20084, 1.20818, 1.22235, 1.24885, 1.33355, 1.29992, 1.28156, 1.28707, 1.32735, 1.59177}, - {1.29544, 1.29780, 1.30507, 1.31917, 1.34575, 1.43073, 1.39681, 1.37851, 1.38396, 1.42377, 1.68854}, - {1.39236, 1.39462, 1.40205, 1.41607, 1.44259, 1.52745, 1.49368, 1.47541, 1.48083, 1.52112, 1.78546}, - {1.49314, 1.49149, 1.49885, 1.51297, 1.53949, 1.62420, 1.59016, 1.57231, 1.57772, 1.61800, 1.88048}, - {1.58610, 1.58839, 1.59572, 1.60983, 1.63635, 1.72109, 1.68651, 1.66921, 1.67463, 1.71489, 1.97918}, - {1.68400, 1.68529, 1.69261, 1.70671, 1.73331, 1.81830, 1.78341, 1.76605, 1.77150, 1.81179, 2.07608}, - {1.77991, 1.78215, 1.78952, 1.80385, 1.83014, 1.91486, 1.88128, 1.86215, 1.86837, 1.90865, 2.17304}, - {1.87674, 1.87904, 1.88647, 1.90052, 1.92712, 2.01173, 1.97812, 1.95905, 1.96527, 2.00710, 2.26979}, - {1.97369, 1.97594, 1.98326, 1.99869, 2.02442, 2.10865, 2.07501, 2.05666, 2.06214, 2.10243, 2.36669}, - {2.07052, 2.07281, 2.08016, 2.09425, 2.12132, 2.20555, 2.17182, 2.15341, 2.15904, 2.19933, 2.46363}, - {2.16742, 2.16971, 2.17707, 2.19114, 2.21766, 2.30240, 2.26877, 2.25015, 2.25573, 2.29586, 2.56060}, - {2.26423, 2.26659, 2.27396, 2.28803, 2.31456, 2.40828, 2.36567, 2.34705, 2.35282, 2.39765, 2.65744}, - {2.36153, 2.36349, 2.37330, 2.38501, 2.41159, 2.49940, 2.46257, 2.44420, 2.44843, 2.48987, 2.75431}, - {2.46558, 2.46035, 2.46822, 2.48181, 2.50849, 2.59630, 2.55947, 2.54112, 2.54513, 2.58677, 2.85094}, - {2.56248, 2.55723, 2.56486, 2.57871, 2.60520, 2.68998, 2.65626, 2.63790, 2.64316, 2.68360, 2.94813}, - {2.65178, 2.65441, 2.66153, 2.67556, 2.70210, 2.78687, 2.75319, 2.73463, 2.74032, 2.78060, 3.04503}, - {2.74868, 2.75131, 2.75870, 2.77245, 2.79385, 2.88700, 2.85009, 2.83177, 2.83723, 2.87750, 3.14193}, - {2.84574, 2.84789, 2.85560, 2.86935, 2.89075, 2.98060, 2.94576, 2.92868, 2.93356, 2.97436, 3.23868}, - {2.94239, 2.94469, 2.95221, 2.96625, 2.99345, 3.07747, 3.04266, 3.02545, 3.03051, 3.07118, 3.33555}}; - - // Garfield simulation at UD = -1600V; vd = 1.158cm/microsec, <driftfield> = 558V/cm - float time1600[ktimebin][kZbin] = { - {0.09169, 0.09203, 0.09305, 0.09473, 0.09714, 0.10032, 0.10441, 0.10990, 0.11835, 0.13986, 0.37845}, - {0.06589, 0.06626, 0.06738, 0.06924, 0.07184, 0.07521, 0.07947, 0.08512, 0.09379, 0.11603, 0.35648}, - {0.03947, 0.04003, 0.04171, 0.04434, 0.04778, 0.05190, 0.05678, 0.06304, 0.07292, 0.09876, 0.34759}, - {0.01111, 0.01281, 0.01718, 0.02281, 0.02879, 0.03477, 0.04097, 0.04910, 0.06415, 0.10573, 0.36896}, - {0.01120, 0.01311, 0.01721, 0.02279, 0.02862, 0.03446, 0.04074, 0.04981, 0.06825, 0.11595, 0.37255}, - {0.03919, 0.03980, 0.04132, 0.04380, 0.04704, 0.05102, 0.05602, 0.06302, 0.07633, 0.10896, 0.36743}, - {0.06531, 0.06528, 0.06631, 0.06805, 0.07053, 0.07392, 0.07853, 0.08505, 0.09669, 0.12578, 0.37967}, - {0.09157, 0.09171, 0.09216, 0.09301, 0.09475, 0.00000, 0.11152, 0.11879, 0.13352, 0.16802, 0.42750}, - {0.13977, 0.14095, 0.14509, 0.15433, 0.17534, 0.26406, 0.21660, 0.20345, 0.21113, 0.25067, 0.51434}, - {0.21816, 0.22041, 0.22631, 0.23850, 0.26208, 0.34340, 0.30755, 0.29237, 0.29878, 0.33863, 0.60258}, - {0.30344, 0.30547, 0.31241, 0.32444, 0.34809, 0.42696, 0.39464, 0.37919, 0.38546, 0.42530, 0.68926}, - {0.38969, 0.39164, 0.39810, 0.41059, 0.43441, 0.51246, 0.48112, 0.46562, 0.47191, 0.51172, 0.77558}, - {0.47592, 0.47799, 0.48442, 0.49689, 0.52061, 0.59855, 0.56752, 0.55201, 0.55826, 0.59808, 0.86202}, - {0.56226, 0.56428, 0.57074, 0.58324, 0.60696, 0.68483, 0.65388, 0.63837, 0.64461, 0.68445, 0.94830}, - {0.64881, 0.65063, 0.65709, 0.66958, 0.69331, 0.77117, 0.74023, 0.72472, 0.73098, 0.77079, 1.03486}, - {0.73506, 0.73698, 0.74344, 0.75596, 0.77964, 0.85751, 0.82658, 0.81107, 0.81731, 0.85712, 1.12106}, - {0.82132, 0.82333, 0.82979, 0.84228, 0.86608, 0.94386, 0.91293, 0.89742, 0.90367, 0.94335, 1.20737}, - {0.90767, 0.90968, 0.91614, 0.92863, 0.95236, 1.03021, 0.99928, 0.98377, 0.99001, 1.02984, 1.29371}, - {0.99410, 0.99602, 1.00257, 1.01498, 1.03869, 1.11720, 1.08563, 1.07011, 1.07637, 1.11621, 1.37873}, - {1.08036, 1.08240, 1.08884, 1.10138, 1.12504, 1.20301, 1.17198, 1.15647, 1.16272, 1.20255, 1.46651}, - {1.16670, 1.16872, 1.17525, 1.18783, 1.21139, 1.28934, 1.25833, 1.24281, 1.24909, 1.28889, 1.55275}, - {1.25307, 1.25510, 1.26153, 1.27404, 1.29773, 1.37584, 1.34469, 1.32916, 1.33536, 1.37524, 1.63915}, - {1.33942, 1.34146, 1.34788, 1.36040, 1.38410, 1.46438, 1.43105, 1.41537, 1.42176, 1.46158, 1.72538}, - {1.42579, 1.42782, 1.43458, 1.44674, 1.47043, 1.55085, 1.51675, 1.50168, 1.50810, 1.54793, 1.81174}, - {1.51207, 1.51454, 1.52060, 1.53307, 1.55684, 1.63478, 1.60336, 1.58820, 1.59446, 1.63414, 1.89814}, - {1.59856, 1.60047, 1.60693, 1.61942, 1.64317, 1.72257, 1.69008, 1.67454, 1.68080, 1.72063, 1.98433}, - {1.68481, 1.68682, 1.69330, 1.70584, 1.72949, 1.80752, 1.77643, 1.76089, 1.76716, 1.80692, 2.07069}, - {1.77117, 1.77319, 1.77969, 1.79260, 1.81583, 1.89376, 1.86226, 1.84720, 1.85355, 1.89256, 2.15343}, - {1.85748, 1.85967, 1.86605, 1.87848, 1.90222, 1.98010, 1.94913, 1.93271, 1.93981, 1.97968, 2.24355}, - {1.94386, 1.94587, 1.95233, 1.96484, 1.98854, 2.06646, 2.03542, 2.01755, 2.02617, 2.06604, 2.32993}, - {2.03022, 2.03230, 2.03868, 2.05134, 2.07488, 2.15367, 2.12178, 2.10391, 2.11252, 2.15432, 2.41623}, - {2.11656, 2.11857, 2.12505, 2.13772, 2.16147, 2.23919, 2.20817, 2.19265, 2.20744, 2.23872, 2.49996}, - {2.20291, 2.20611, 2.21137, 2.22387, 2.24758, 2.32563, 2.29450, 2.27901, 2.28525, 2.32507, 2.58897}, - {2.28922, 2.29172, 2.29774, 2.31345, 2.33400, 2.41287, 2.38086, 2.36535, 2.37160, 2.40869, 2.67113}, - {2.37572, 2.37764, 2.38410, 2.39803, 2.42046, 2.49817, 2.46721, 2.45171, 2.45794, 2.49505, 2.76061}, - {2.46190, 2.46396, 2.47043, 2.48340, 2.50665, 2.58453, 2.55357, 2.53728, 2.54430, 2.58407, 2.84816}, - {2.54833, 2.55032, 2.55679, 2.56976, 2.59312, 2.67103, 2.63993, 2.62364, 2.63062, 2.67040, 2.93444}, - {2.63456, 2.63660, 2.64304, 2.65555, 2.67938, 2.75739, 2.72629, 2.71064, 2.71688, 2.75671, 3.01886}}; - - // Garfield simulation at UD = -1700V; vd = 1.299cm/microsec, <driftfield> = 590V/cm - float time1700[ktimebin][kZbin] = { - {0.09167, 0.09201, 0.09302, 0.09471, 0.09712, 0.10029, 0.10438, 0.10986, 0.11832, 0.13986, 0.37824}, - {0.06591, 0.06626, 0.06736, 0.06923, 0.07183, 0.07519, 0.07944, 0.08511, 0.09378, 0.11603, 0.35625}, - {0.03946, 0.04003, 0.04170, 0.04433, 0.04777, 0.05189, 0.05676, 0.06301, 0.07291, 0.09880, 0.34724}, - {0.01110, 0.01281, 0.01718, 0.02280, 0.02879, 0.03476, 0.04096, 0.04910, 0.06417, 0.10582, 0.36861}, - {0.01115, 0.01294, 0.01721, 0.02276, 0.02862, 0.03447, 0.04074, 0.04980, 0.06812, 0.11565, 0.37231}, - {0.03920, 0.03974, 0.04133, 0.04381, 0.04706, 0.05105, 0.05603, 0.06299, 0.07618, 0.10860, 0.36646}, - {0.06498, 0.06529, 0.06634, 0.06808, 0.07055, 0.07395, 0.07852, 0.08500, 0.09650, 0.12532, 0.37850}, - {0.09143, 0.09159, 0.09207, 0.09297, 0.09473, 0.00000, 0.11102, 0.11809, 0.13245, 0.16627, 0.42496}, - {0.13646, 0.13750, 0.14112, 0.14926, 0.16806, 0.24960, 0.20627, 0.19536, 0.20366, 0.24256, 0.50557}, - {0.20678, 0.20848, 0.21384, 0.22450, 0.24552, 0.32018, 0.28740, 0.27477, 0.28196, 0.32128, 0.58475}, - {0.28287, 0.28461, 0.29020, 0.30108, 0.32224, 0.39467, 0.36500, 0.35217, 0.35926, 0.39860, 0.66194}, - {0.35972, 0.36145, 0.36713, 0.37797, 0.39912, 0.47091, 0.44212, 0.42925, 0.43632, 0.47563, 0.73892}, - {0.43667, 0.43841, 0.44413, 0.45494, 0.47607, 0.54780, 0.51912, 0.50627, 0.51334, 0.55254, 0.81595}, - {0.51365, 0.51540, 0.52101, 0.53193, 0.55305, 0.62463, 0.59617, 0.58328, 0.59035, 0.62965, 0.89303}, - {0.59064, 0.59240, 0.59801, 0.60893, 0.63009, 0.70169, 0.67317, 0.66028, 0.66735, 0.70666, 0.96995}, - {0.66765, 0.66939, 0.67501, 0.68592, 0.70724, 0.77863, 0.75016, 0.73728, 0.74435, 0.78366, 1.04696}, - {0.74464, 0.74636, 0.75200, 0.76293, 0.78405, 0.85561, 0.82716, 0.81427, 0.82133, 0.86064, 1.12396}, - {0.82165, 0.82340, 0.82902, 0.83991, 0.86104, 0.93266, 0.90414, 0.89128, 0.89834, 0.93763, 1.20100}, - {0.89863, 0.90042, 0.90659, 0.91705, 0.93805, 1.00960, 0.98115, 0.96825, 0.97533, 1.01462, 1.27801}, - {0.97563, 0.97740, 0.98310, 0.99391, 1.01504, 1.08659, 1.05814, 1.04526, 1.05233, 1.09163, 1.35503}, - {1.05276, 1.05451, 1.06002, 1.07090, 1.09099, 1.16357, 1.13516, 1.12225, 1.12933, 1.16863, 1.43195}, - {1.12977, 1.13138, 1.13700, 1.14792, 1.16797, 1.24061, 1.21212, 1.19926, 1.20626, 1.24554, 1.50900}, - {1.20664, 1.20839, 1.21400, 1.22490, 1.24606, 1.31772, 1.28914, 1.27382, 1.28329, 1.32262, 1.58550}, - {1.28367, 1.28541, 1.29099, 1.30189, 1.32312, 1.39460, 1.36612, 1.34924, 1.36030, 1.39961, 1.66310}, - {1.36064, 1.36249, 1.36799, 1.37896, 1.40004, 1.48030, 1.44314, 1.43032, 1.43731, 1.47659, 1.73442}, - {1.43762, 1.43937, 1.44497, 1.45618, 1.47704, 1.54932, 1.52012, 1.50725, 1.51430, 1.55357, 1.81708}, - {1.51462, 1.51937, 1.52203, 1.53316, 1.55403, 1.62572, 1.59713, 1.58424, 1.59128, 1.63061, 1.89406}, - {1.59162, 1.59338, 1.59947, 1.60989, 1.63103, 1.70270, 1.67411, 1.66124, 1.66799, 1.70759, 1.97103}, - {1.66874, 1.67037, 1.67597, 1.68687, 1.70814, 1.77969, 1.75112, 1.73806, 1.74530, 1.78457, 2.04794}, - {1.74693, 1.74749, 1.75297, 1.76476, 1.78500, 1.85667, 1.82811, 1.81504, 1.82101, 1.86161, 2.12492}, - {1.82260, 1.82437, 1.82995, 1.84174, 1.86202, 1.93372, 1.90509, 1.89202, 1.89930, 1.93859, 2.20189}, - {1.89964, 1.90135, 1.90693, 1.91789, 1.93952, 2.01080, 1.98207, 1.96921, 1.97628, 2.01384, 2.27887}, - {1.97662, 1.97917, 1.98611, 1.99487, 2.01601, 2.08778, 2.05846, 2.04623, 2.05330, 2.09244, 2.35585}, - {2.05359, 2.05615, 2.06309, 2.07187, 2.09867, 2.16459, 2.13610, 2.12322, 2.13029, 2.16942, 2.43199}, - {2.13063, 2.13233, 2.13795, 2.14886, 2.17008, 2.24199, 2.21310, 2.20020, 2.20727, 2.24659, 2.50983}, - {2.20761, 2.20931, 2.21955, 2.22624, 2.24708, 2.32147, 2.29009, 2.27725, 2.28276, 2.32359, 2.58680}, - {2.28459, 2.29108, 2.29202, 2.30286, 2.32007, 2.39559, 2.36683, 2.35422, 2.36119, 2.40058, 2.66081}, - {2.36153, 2.36806, 2.36889, 2.37985, 2.40092, 2.47828, 2.44381, 2.43099, 2.43819, 2.47750, 2.73779}}; - - // Garfield simulation at UD = -1800V; vd = 1.450cm/microsec, <driftfield> = 623V/cm - float time1800[ktimebin][kZbin] = { - {0.09166, 0.09199, 0.09300, 0.09470, 0.09709, 0.10026, 0.10434, 0.10983, 0.11831, 0.13987, 0.37802}, - {0.06585, 0.06623, 0.06735, 0.06921, 0.07180, 0.07520, 0.07941, 0.08507, 0.09376, 0.11604, 0.35624}, - {0.03945, 0.04004, 0.04169, 0.04432, 0.04776, 0.05187, 0.05674, 0.06300, 0.07290, 0.09884, 0.34704}, - {0.01108, 0.01287, 0.01717, 0.02280, 0.02880, 0.03476, 0.04095, 0.04909, 0.06419, 0.10589, 0.36843}, - {0.01115, 0.01287, 0.01720, 0.02276, 0.02862, 0.03448, 0.04073, 0.04973, 0.06799, 0.11535, 0.37224}, - {0.03918, 0.03975, 0.04134, 0.04382, 0.04707, 0.05105, 0.05603, 0.06296, 0.07605, 0.10822, 0.36560}, - {0.06498, 0.06532, 0.06635, 0.06809, 0.07058, 0.07395, 0.07855, 0.08495, 0.09632, 0.12488, 0.37730}, - {0.09130, 0.09160, 0.09199, 0.09300, 0.09472, 0.00000, 0.11059, 0.11747, 0.13146, 0.16462, 0.42233}, - {0.13364, 0.13449, 0.13767, 0.14481, 0.16147, 0.23635, 0.19706, 0.18812, 0.19704, 0.23520, 0.49749}, - {0.19698, 0.19844, 0.20311, 0.21236, 0.23082, 0.29920, 0.26936, 0.25927, 0.26732, 0.30601, 0.56871}, - {0.26518, 0.26670, 0.27160, 0.28099, 0.29955, 0.36597, 0.33885, 0.32858, 0.33653, 0.37524, 0.63801}, - {0.33441, 0.33553, 0.34040, 0.34987, 0.36841, 0.43428, 0.40797, 0.39763, 0.40556, 0.44425, 0.70698}, - {0.40296, 0.40447, 0.40934, 0.41881, 0.43737, 0.50306, 0.47695, 0.46662, 0.47455, 0.51329, 0.77600}, - {0.47296, 0.47344, 0.47830, 0.48779, 0.50632, 0.57200, 0.54593, 0.53559, 0.54351, 0.58222, 0.84489}, - {0.54089, 0.54264, 0.54727, 0.55673, 0.57529, 0.64094, 0.61490, 0.60457, 0.61249, 0.65118, 0.91394}, - {0.60987, 0.61138, 0.61624, 0.62573, 0.64428, 0.70989, 0.68397, 0.67354, 0.68147, 0.72015, 0.98291}, - {0.67883, 0.68035, 0.68521, 0.69469, 0.71324, 0.77896, 0.75287, 0.74251, 0.75043, 0.78912, 1.04458}, - {0.74780, 0.74932, 0.75421, 0.76367, 0.78221, 0.84785, 0.82185, 0.81148, 0.81941, 0.85811, 1.12085}, - {0.81690, 0.81830, 0.82316, 0.83263, 0.85120, 0.91683, 0.89077, 0.88045, 0.88837, 0.92707, 1.18976}, - {0.88574, 0.88726, 0.89228, 0.90198, 0.92017, 0.98578, 0.95974, 0.94947, 0.95734, 0.99604, 1.25873}, - {0.95493, 0.95624, 0.96110, 0.97058, 0.98913, 1.05481, 1.02873, 1.01839, 1.02631, 1.06503, 1.32772}, - {1.02392, 1.02524, 1.03008, 1.03955, 1.05810, 1.12378, 1.09757, 1.08605, 1.09530, 1.13399, 1.39669}, - {1.09270, 1.09418, 1.09911, 1.10854, 1.12714, 1.19281, 1.16502, 1.15633, 1.16427, 1.20271, 1.46574}, - {1.16168, 1.16323, 1.16801, 1.17772, 1.19604, 1.26190, 1.23399, 1.22531, 1.23323, 1.27194, 1.53475}, - {1.23061, 1.23214, 1.23698, 1.24669, 1.26503, 1.33073, 1.30461, 1.29428, 1.30220, 1.34091, 1.60372}, - {1.29960, 1.30110, 1.30596, 1.31544, 1.33398, 1.39962, 1.37228, 1.36323, 1.37121, 1.40988, 1.67273}, - {1.36851, 1.37007, 1.37512, 1.38441, 1.40297, 1.46865, 1.44256, 1.43222, 1.44017, 1.47878, 1.74155}, - {1.43752, 1.43904, 1.44773, 1.45338, 1.47220, 1.53759, 1.51136, 1.50119, 1.50914, 1.54775, 1.81050}, - {1.50646, 1.50802, 1.51288, 1.52237, 1.54097, 1.60697, 1.58049, 1.57018, 1.57811, 1.61678, 1.87947}, - {1.57545, 1.57720, 1.58185, 1.59134, 1.60996, 1.67787, 1.64929, 1.63914, 1.64707, 1.68570, 1.94851}, - {1.64442, 1.64617, 1.65081, 1.66035, 1.67893, 1.74684, 1.71826, 1.70745, 1.71604, 1.75310, 2.01748}, - {1.71337, 1.71513, 1.71978, 1.72932, 1.74645, 1.81346, 1.78739, 1.77642, 1.78501, 1.82151, 2.08644}, - {1.78238, 1.78410, 1.78876, 1.79824, 1.81678, 1.88332, 1.85639, 1.84262, 1.85397, 1.89270, 2.15533}, - {1.85135, 1.85306, 1.85778, 1.86728, 1.88580, 1.95615, 1.92536, 1.91171, 1.92283, 1.96165, 2.22428}, - {1.92774, 1.92184, 1.92672, 1.93618, 1.95477, 2.02048, 1.99427, 1.98068, 1.99192, 2.03062, 2.29338}, - {1.98929, 1.99081, 1.99567, 2.00515, 2.02373, 2.08987, 2.06332, 2.05249, 2.05939, 2.09928, 2.36227}, - {2.05829, 2.05978, 2.06464, 2.07414, 2.09272, 2.15850, 2.12928, 2.12194, 2.12987, 2.16825, 2.43083}, - {2.12726, 2.12869, 2.13360, 2.14425, 2.16160, 2.22872, 2.20118, 2.19078, 2.19876, 2.23416, 2.50007}}; - - // Garfield simulation at UD = -1900V; vd = 1.610cm/microsec, <driftfield> = 655V/cm - float time1900[ktimebin][kZbin] = { - {0.09166, 0.09198, 0.09298, 0.09467, 0.09707, 0.10023, 0.10431, 0.10980, 0.11828, 0.13988, 0.37789}, - {0.06584, 0.06622, 0.06735, 0.06920, 0.07179, 0.07514, 0.07938, 0.08505, 0.09374, 0.11606, 0.35599}, - {0.03945, 0.04002, 0.04169, 0.04432, 0.04775, 0.05185, 0.05672, 0.06298, 0.07290, 0.09888, 0.34695}, - {0.01109, 0.01281, 0.01717, 0.02279, 0.02878, 0.03476, 0.04094, 0.04909, 0.06421, 0.10597, 0.36823}, - {0.01115, 0.01287, 0.01720, 0.02294, 0.02862, 0.03448, 0.04074, 0.04973, 0.06783, 0.11506, 0.37206}, - {0.03940, 0.03975, 0.04135, 0.04386, 0.04708, 0.05106, 0.05604, 0.06293, 0.07592, 0.10787, 0.36484}, - {0.06500, 0.06534, 0.06638, 0.06811, 0.07060, 0.07413, 0.07852, 0.08491, 0.09614, 0.12446, 0.37626}, - {0.09119, 0.09140, 0.09194, 0.09293, 0.09471, 0.00000, 0.11013, 0.11685, 0.13050, 0.16302, 0.41991}, - {0.13111, 0.13190, 0.13466, 0.14091, 0.15554, 0.22409, 0.18846, 0.18167, 0.19113, 0.22854, 0.48995}, - {0.18849, 0.18975, 0.19380, 0.20185, 0.21797, 0.28050, 0.25368, 0.24575, 0.25446, 0.29249, 0.55442}, - {0.24995, 0.25125, 0.25563, 0.26366, 0.27986, 0.34065, 0.31605, 0.30815, 0.31680, 0.35482, 0.61697}, - {0.31187, 0.31324, 0.31745, 0.32580, 0.34205, 0.40217, 0.37825, 0.37031, 0.37897, 0.41696, 0.67890}, - {0.37401, 0.37531, 0.37955, 0.38777, 0.40395, 0.46408, 0.44037, 0.43242, 0.44108, 0.47906, 0.74122}, - {0.43610, 0.43741, 0.44161, 0.44986, 0.46604, 0.52614, 0.50248, 0.49452, 0.50316, 0.54116, 0.80326}, - {0.49820, 0.49988, 0.50372, 0.51196, 0.52814, 0.58822, 0.56459, 0.55661, 0.56527, 0.60326, 0.86526}, - {0.56032, 0.56161, 0.56582, 0.57408, 0.59024, 0.65032, 0.62670, 0.61872, 0.62737, 0.66537, 0.92749}, - {0.62240, 0.62371, 0.62792, 0.63624, 0.65236, 0.71241, 0.68881, 0.68081, 0.68947, 0.72750, 0.98941}, - {0.68449, 0.68581, 0.69002, 0.69828, 0.71444, 0.77452, 0.75089, 0.74295, 0.75157, 0.78957, 1.05157}, - {0.74660, 0.74790, 0.75212, 0.76036, 0.77654, 0.83748, 0.81299, 0.80501, 0.81193, 0.85168, 1.11375}, - {0.80870, 0.81017, 0.81423, 0.82246, 0.83867, 0.89908, 0.87509, 0.86660, 0.87577, 0.91376, 1.17586}, - {0.87080, 0.87233, 0.87632, 0.88458, 0.90074, 0.96083, 0.93718, 0.92922, 0.93787, 0.97588, 1.23794}, - {0.93291, 0.93422, 0.93844, 0.94667, 0.96293, 1.02295, 0.99929, 0.99127, 0.99997, 1.03797, 1.29995}, - {0.99500, 0.99645, 1.00308, 1.00877, 1.02497, 1.08504, 1.06140, 1.05343, 1.06203, 1.10006, 1.36216}, - {1.05712, 1.05926, 1.06262, 1.07092, 1.08706, 1.14754, 1.12350, 1.11550, 1.12417, 1.16218, 1.42427}, - {1.11921, 1.12059, 1.12473, 1.13297, 1.14916, 1.21140, 1.18560, 1.17284, 1.18625, 1.22414, 1.48629}, - {1.18140, 1.18262, 1.18690, 1.19508, 1.21125, 1.27139, 1.24164, 1.23495, 1.24838, 1.28634, 1.54852}, - {1.24340, 1.24473, 1.24901, 1.25732, 1.27336, 1.33358, 1.30793, 1.30179, 1.31047, 1.34848, 1.61066}, - {1.30551, 1.30684, 1.31104, 1.32056, 1.33553, 1.39609, 1.37004, 1.36392, 1.37045, 1.41057, 1.67259}, - {1.36755, 1.36892, 1.37315, 1.39148, 1.39755, 1.45820, 1.43215, 1.42602, 1.43467, 1.47268, 1.73477}, - {1.42966, 1.43101, 1.43549, 1.45359, 1.45976, 1.52031, 1.49601, 1.48811, 1.49677, 1.53477, 1.79691}, - {1.49180, 1.49321, 1.49760, 1.51570, 1.52175, 1.58185, 1.55771, 1.55023, 1.55888, 1.59672, 1.85501}, - {1.55391, 1.55527, 1.55943, 1.57782, 1.58391, 1.64395, 1.62008, 1.61233, 1.62085, 1.65883, 1.92091}, - {1.61599, 1.61732, 1.62154, 1.63993, 1.64612, 1.70608, 1.68237, 1.67108, 1.68301, 1.72110, 1.97931}, - {1.67808, 1.67948, 1.68364, 1.70204, 1.70823, 1.76858, 1.74404, 1.73539, 1.74512, 1.78321, 2.04522}, - {1.74019, 1.74152, 1.74573, 1.76415, 1.77015, 1.83040, 1.80615, 1.79366, 1.80723, 1.84509, 2.10742}, - {1.80235, 1.80362, 1.80783, 1.82626, 1.83227, 1.89246, 1.86795, 1.85405, 1.86938, 1.90720, 2.16953}, - {1.86442, 1.86572, 1.86994, 1.88837, 1.89438, 1.95445, 1.93006, 1.92283, 1.93148, 1.96931, 2.23147}, - {1.92700, 1.92783, 1.93197, 1.95049, 1.95649, 2.01668, 1.99217, 1.98486, 1.99352, 2.03143, 2.29358}}; - - // Garfield simulation at UD = -2000V; vd = 1.783cm/microsec, <driftfield> = 688V/cm - float time2000[ktimebin][kZbin] = { - {0.09176, 0.09196, 0.09296, 0.09465, 0.09704, 0.10020, 0.10427, 0.10977, 0.11825, 0.13988, 0.37774}, - {0.06583, 0.06620, 0.06735, 0.06918, 0.07177, 0.07513, 0.07936, 0.08503, 0.09372, 0.11606, 0.35586}, - {0.03944, 0.04001, 0.04170, 0.04431, 0.04774, 0.05184, 0.05670, 0.06296, 0.07291, 0.09893, 0.34680}, - {0.01108, 0.01281, 0.01719, 0.02279, 0.02879, 0.03474, 0.04093, 0.04908, 0.06422, 0.10605, 0.36800}, - {0.01114, 0.01287, 0.01720, 0.02276, 0.02863, 0.03449, 0.04073, 0.04970, 0.06774, 0.11478, 0.37179}, - {0.03925, 0.03977, 0.04135, 0.04386, 0.04711, 0.05108, 0.05604, 0.06290, 0.07580, 0.10748, 0.36386}, - {0.06501, 0.06536, 0.06640, 0.06814, 0.07062, 0.07398, 0.07852, 0.08487, 0.09598, 0.12405, 0.37519}, - {0.09109, 0.09127, 0.09188, 0.09292, 0.09472, 0.00000, 0.10964, 0.11630, 0.12960, 0.16150, 0.41765}, - {0.12898, 0.12968, 0.13209, 0.13749, 0.15034, 0.21286, 0.18088, 0.17590, 0.18591, 0.22254, 0.48315}, - {0.18122, 0.18227, 0.18574, 0.19263, 0.20674, 0.26376, 0.23960, 0.23375, 0.24316, 0.28047, 0.54179}, - {0.23674, 0.23784, 0.24142, 0.24847, 0.26264, 0.31810, 0.29602, 0.29008, 0.29944, 0.33675, 0.59795}, - {0.29279, 0.29382, 0.29742, 0.30448, 0.31865, 0.37364, 0.35215, 0.34629, 0.35555, 0.39286, 0.65411}, - {0.34875, 0.34987, 0.35346, 0.36054, 0.37472, 0.42956, 0.40825, 0.40229, 0.41167, 0.44894, 0.71033}, - {0.40484, 0.40594, 0.40954, 0.41660, 0.43077, 0.48560, 0.46433, 0.45840, 0.46772, 0.50500, 0.76632}, - {0.46090, 0.46201, 0.46560, 0.47267, 0.48684, 0.54167, 0.52041, 0.51449, 0.52382, 0.56108, 0.82227}, - {0.51698, 0.51809, 0.52173, 0.52874, 0.54291, 0.59776, 0.57646, 0.57052, 0.57986, 0.61717, 0.87836}, - {0.57306, 0.57418, 0.57782, 0.58513, 0.59899, 0.65380, 0.63255, 0.62661, 0.63594, 0.67325, 0.93460}, - {0.62912, 0.63024, 0.63383, 0.64103, 0.65506, 0.70988, 0.68484, 0.68267, 0.69202, 0.72878, 0.99046}, - {0.68521, 0.68633, 0.68990, 0.69699, 0.71115, 0.76595, 0.74468, 0.73872, 0.74814, 0.78538, 1.04674}, - {0.74127, 0.74239, 0.74605, 0.75303, 0.77022, 0.82204, 0.80078, 0.79484, 0.80416, 0.84147, 1.10261}, - {0.79736, 0.79846, 0.80206, 0.80947, 0.82330, 0.87813, 0.85688, 0.85087, 0.86023, 0.89752, 1.15874}, - {0.85342, 0.85454, 0.85815, 0.86519, 0.87936, 0.93417, 0.91293, 0.90428, 0.91631, 0.95360, 1.20760}, - {0.90949, 0.91061, 0.91423, 0.92128, 0.93544, 0.99026, 0.96807, 0.96305, 0.97239, 1.00967, 1.27078}, - {0.96556, 0.96669, 0.97111, 0.97734, 0.99151, 1.04664, 1.02508, 1.01879, 1.02846, 1.06167, 1.32695}, - {1.02167, 1.02279, 1.02656, 1.03341, 1.04759, 1.10242, 1.08115, 1.07003, 1.08453, 1.12184, 1.38304}, - {1.07776, 1.07883, 1.08242, 1.08950, 1.10384, 1.16422, 1.13725, 1.13133, 1.14061, 1.17793, 1.43910}, - {1.13379, 1.13492, 1.13864, 1.14567, 1.15973, 1.21455, 1.19323, 1.18734, 1.19668, 1.23401, 1.49528}, - {1.18988, 1.19098, 1.19457, 1.20164, 1.21582, 1.27064, 1.24937, 1.24044, 1.25275, 1.29004, 1.55137}, - {1.24592, 1.24706, 1.25087, 1.25773, 1.27188, 1.32670, 1.30544, 1.29953, 1.30883, 1.34613, 1.60743}, - {1.30202, 1.30313, 1.30673, 1.31381, 1.32797, 1.38278, 1.36151, 1.35167, 1.36490, 1.40221, 1.66306}, - {1.35809, 1.35921, 1.36282, 1.36986, 1.38403, 1.43888, 1.41760, 1.41174, 1.42083, 1.45830, 1.71915}, - {1.41419, 1.41528, 1.41890, 1.42595, 1.44011, 1.49496, 1.47368, 1.46769, 1.47706, 1.51436, 1.77523}, - {1.47131, 1.47141, 1.47494, 1.48850, 1.49620, 1.55137, 1.52977, 1.51820, 1.53315, 1.57042, 1.83158}, - {1.52635, 1.52750, 1.53103, 1.53814, 1.55228, 1.60736, 1.58503, 1.57986, 1.58920, 1.62649, 1.88767}, - {1.58418, 1.58355, 1.58711, 1.59526, 1.60833, 1.66316, 1.63345, 1.63261, 1.64556, 1.68204, 1.94359}, - {1.64027, 1.63958, 1.64489, 1.65024, 1.66443, 1.71925, 1.69794, 1.69201, 1.70143, 1.73865, 1.99968}, - {1.69450, 1.69566, 1.69940, 1.70697, 1.71841, 1.77819, 1.75396, 1.74814, 1.75743, 1.79083, 2.05427}, - {1.75054, 1.75221, 1.75527, 1.76306, 1.77662, 1.83428, 1.81006, 1.81173, 1.81345, 1.85076, 2.10289}}; - - // Garfield simulation at UD = -2100V; vd = 1.959cm/microsec, <driftfield> = 720V/cm - float time2100[ktimebin][kZbin] = { - {0.09160, 0.09194, 0.09294, 0.09462, 0.09701, 0.10017, 0.10424, 0.10974, 0.11823, 0.13988, 0.37762}, - {0.06585, 0.06619, 0.06731, 0.06916, 0.07174, 0.07509, 0.07933, 0.08500, 0.09370, 0.11609, 0.35565}, - {0.03960, 0.04001, 0.04171, 0.04430, 0.04774, 0.05182, 0.05668, 0.06294, 0.07291, 0.09896, 0.34676}, - {0.01109, 0.01280, 0.01716, 0.02279, 0.02876, 0.03474, 0.04096, 0.04908, 0.06424, 0.10612, 0.36790}, - {0.01114, 0.01285, 0.01719, 0.02287, 0.02863, 0.03449, 0.04073, 0.04964, 0.06759, 0.11446, 0.37162}, - {0.03922, 0.03977, 0.04146, 0.04386, 0.04711, 0.05109, 0.05605, 0.06287, 0.07575, 0.10713, 0.36298}, - {0.06504, 0.06538, 0.06641, 0.06818, 0.07064, 0.07426, 0.07852, 0.08483, 0.09581, 0.12363, 0.37424}, - {0.09103, 0.09129, 0.09186, 0.09291, 0.09476, 0.00000, 0.10923, 0.11578, 0.12873, 0.16005, 0.41525}, - {0.12723, 0.12777, 0.12988, 0.13458, 0.14579, 0.20264, 0.17421, 0.17078, 0.18132, 0.21708, 0.47699}, - {0.17508, 0.17601, 0.17897, 0.18487, 0.19698, 0.24881, 0.22737, 0.22337, 0.23348, 0.27000, 0.53032}, - {0.22571, 0.22663, 0.22969, 0.23570, 0.24787, 0.29826, 0.27871, 0.27462, 0.28471, 0.32122, 0.58166}, - {0.27664, 0.27759, 0.28067, 0.28669, 0.29891, 0.34898, 0.32982, 0.32570, 0.33576, 0.37229, 0.63268}, - {0.32766, 0.32862, 0.33170, 0.33778, 0.34988, 0.39973, 0.38088, 0.37675, 0.38680, 0.42333, 0.68159}, - {0.37872, 0.37966, 0.38275, 0.38875, 0.40093, 0.45073, 0.43192, 0.42780, 0.43786, 0.47438, 0.73480}, - {0.42974, 0.43070, 0.43378, 0.43982, 0.45196, 0.50177, 0.48297, 0.47884, 0.48889, 0.52544, 0.78581}, - {0.48081, 0.48175, 0.48482, 0.49084, 0.50302, 0.55290, 0.53398, 0.52988, 0.53994, 0.57647, 0.83687}, - {0.53645, 0.53295, 0.53586, 0.54188, 0.55408, 0.60398, 0.58504, 0.58092, 0.59100, 0.62768, 0.88773}, - {0.58345, 0.58409, 0.58690, 0.59292, 0.60510, 0.65562, 0.63609, 0.63197, 0.64203, 0.67856, 0.93907}, - {0.63397, 0.63490, 0.63795, 0.64403, 0.65613, 0.70612, 0.68714, 0.68301, 0.69294, 0.72955, 0.99000}, - {0.68496, 0.68592, 0.68899, 0.69504, 0.70733, 0.75708, 0.73818, 0.73405, 0.74412, 0.78064, 1.04100}, - {0.73600, 0.73696, 0.74003, 0.74624, 0.75828, 0.80805, 0.78904, 0.78512, 0.79517, 0.83152, 1.09205}, - {0.78709, 0.78801, 0.79108, 0.79709, 0.80931, 0.85906, 0.84027, 0.83614, 0.84621, 0.88269, 1.14058}, - {0.83808, 0.83905, 0.84215, 0.84816, 0.86031, 0.91011, 0.89139, 0.88718, 0.89725, 0.93377, 1.19413}, - {0.88916, 0.89010, 0.89320, 0.89920, 0.91136, 0.96117, 0.94235, 0.93822, 0.94828, 0.98480, 1.24538}, - {0.94036, 0.94113, 0.94422, 0.95023, 0.96241, 1.01220, 0.99310, 0.98927, 0.99933, 1.03585, 1.29629}, - {0.99139, 0.99220, 0.99525, 1.00127, 1.01344, 1.06324, 1.04451, 1.04033, 1.04836, 1.08690, 1.34727}, - {1.04261, 1.04325, 1.04628, 1.05232, 1.06448, 1.12090, 1.09546, 1.09136, 1.10142, 1.13795, 1.39831}, - {1.09331, 1.09429, 1.09742, 1.10336, 1.11557, 1.16547, 1.14658, 1.13642, 1.15247, 1.18898, 1.44936}, - {1.14436, 1.14539, 1.14847, 1.15443, 1.16662, 1.21794, 1.19763, 1.19329, 1.20349, 1.23956, 1.50043}, - {1.19533, 1.19651, 1.19943, 1.20548, 1.21666, 1.26753, 1.24862, 1.24434, 1.25455, 1.29106, 1.55142}, - {1.24638, 1.24756, 1.25046, 1.25648, 1.26764, 1.31858, 1.29967, 1.29538, 1.30499, 1.34211, 1.60250}, - {1.29747, 1.29847, 1.30175, 1.30753, 1.31869, 1.36969, 1.35069, 1.34656, 1.35663, 1.39316, 1.64644}, - {1.35537, 1.34952, 1.35255, 1.35869, 1.36973, 1.41387, 1.40173, 1.39761, 1.40768, 1.44396, 1.70238}, - {1.39956, 1.40056, 1.40380, 1.40961, 1.42178, 1.46492, 1.45278, 1.45423, 1.45872, 1.49522, 1.75557}, - {1.45080, 1.45159, 1.45463, 1.46109, 1.47287, 1.52263, 1.50382, 1.50050, 1.50977, 1.54502, 1.80670}, - {1.50165, 1.50264, 1.50570, 1.51214, 1.52233, 1.57370, 1.55484, 1.55155, 1.56080, 1.59731, 1.85778}, - {1.55269, 1.55364, 1.55675, 1.56274, 1.57491, 1.62598, 1.60590, 1.60259, 1.61185, 1.64836, 1.90883}, - {1.60368, 1.60469, 1.60779, 1.61373, 1.62596, 1.67738, 1.65651, 1.65249, 1.66290, 1.69936, 1.95959}}; - - // Garfield simulation at UD = -2200V; vd = 2.134cm/microsec, <driftfield> = 753V/cm - float time2200[ktimebin][kZbin] = { - {0.09162, 0.09194, 0.09292, 0.09460, 0.09702, 0.10014, 0.10421, 0.10971, 0.11820, 0.13990, 0.37745}, - {0.06581, 0.06618, 0.06730, 0.06915, 0.07173, 0.07507, 0.07931, 0.08497, 0.09368, 0.11609, 0.35560}, - {0.03947, 0.04001, 0.04167, 0.04429, 0.04772, 0.05183, 0.05667, 0.06293, 0.07292, 0.09900, 0.34673}, - {0.01111, 0.01280, 0.01716, 0.02279, 0.02876, 0.03473, 0.04091, 0.04907, 0.06426, 0.10620, 0.36766}, - {0.01113, 0.01285, 0.01719, 0.02276, 0.02863, 0.03452, 0.04076, 0.04960, 0.06745, 0.11419, 0.37139}, - {0.03923, 0.03978, 0.04137, 0.04387, 0.04713, 0.05110, 0.05605, 0.06284, 0.07551, 0.10677, 0.36210}, - {0.06505, 0.06540, 0.06644, 0.06820, 0.07069, 0.07401, 0.07852, 0.08479, 0.09565, 0.12325, 0.37313}, - {0.09107, 0.09127, 0.09181, 0.09291, 0.09474, 0.00000, 0.10883, 0.11528, 0.12789, 0.15865, 0.41313}, - {0.12559, 0.12622, 0.12800, 0.13206, 0.14166, 0.19331, 0.16832, 0.16632, 0.17724, 0.21218, 0.47098}, - {0.16992, 0.17070, 0.17325, 0.17831, 0.18871, 0.23557, 0.21690, 0.21451, 0.22514, 0.26082, 0.52034}, - {0.21640, 0.21722, 0.21987, 0.22500, 0.23540, 0.28097, 0.26399, 0.26154, 0.27214, 0.30784, 0.56734}, - {0.26318, 0.26400, 0.26679, 0.27181, 0.28220, 0.32739, 0.31090, 0.30842, 0.31902, 0.35474, 0.61415}, - {0.31001, 0.31085, 0.31348, 0.31866, 0.32903, 0.37412, 0.35777, 0.35546, 0.36588, 0.40159, 0.66103}, - {0.35687, 0.35769, 0.36033, 0.36556, 0.37588, 0.42094, 0.40523, 0.40214, 0.41273, 0.44841, 0.70785}, - {0.40372, 0.40457, 0.40723, 0.41234, 0.42273, 0.46778, 0.45148, 0.44903, 0.45961, 0.49526, 0.75486}, - {0.45062, 0.45139, 0.45404, 0.45966, 0.46958, 0.51470, 0.49833, 0.49584, 0.50644, 0.54211, 0.80160}, - {0.49742, 0.49825, 0.50088, 0.50605, 0.51644, 0.56148, 0.54518, 0.54270, 0.55330, 0.58897, 0.84854}, - {0.54427, 0.54510, 0.54774, 0.55290, 0.56329, 0.60846, 0.59203, 0.58955, 0.60014, 0.63578, 0.89528}, - {0.59119, 0.59199, 0.59471, 0.59975, 0.61014, 0.65533, 0.63889, 0.63636, 0.64699, 0.68269, 0.94197}, - {0.63866, 0.63880, 0.64145, 0.64664, 0.65701, 0.70639, 0.68574, 0.68325, 0.69385, 0.72949, 0.98900}, - {0.68483, 0.68566, 0.68831, 0.69347, 0.70386, 0.74890, 0.73260, 0.73010, 0.74069, 0.77638, 1.03320}, - {0.73168, 0.73255, 0.73515, 0.74031, 0.75072, 0.79576, 0.77117, 0.77501, 0.78755, 0.82318, 1.08006}, - {0.77854, 0.78310, 0.78200, 0.79525, 0.79756, 0.84281, 0.81803, 0.82393, 0.83441, 0.87008, 1.12692}, - {0.82541, 0.82642, 0.82916, 0.83528, 0.84442, 0.89086, 0.87315, 0.87079, 0.88125, 0.91694, 1.17648}, - {0.87226, 0.87308, 0.87602, 0.88086, 0.89128, 0.93772, 0.92001, 0.91751, 0.92811, 0.95587, 1.22328}, - {0.91921, 0.91994, 0.92256, 0.92772, 0.94713, 0.98566, 0.96690, 0.96436, 0.97495, 1.01064, 1.26882}, - {0.96790, 0.96679, 0.96941, 0.97463, 0.99399, 1.03001, 1.01376, 1.01112, 1.02181, 1.05749, 1.31568}, - {1.01278, 1.01390, 1.01674, 1.02147, 1.03182, 1.07820, 1.06056, 1.05798, 1.06867, 1.10433, 1.36390}, - {1.05964, 1.06076, 1.06331, 1.06833, 1.07870, 1.13297, 1.10742, 1.10520, 1.11543, 1.15120, 1.41069}, - {1.10664, 1.10762, 1.10997, 1.11519, 1.12556, 1.17531, 1.15427, 1.14620, 1.16229, 1.19805, 1.45758}, - {1.15352, 1.15421, 1.15683, 1.16218, 1.17242, 1.21910, 1.20035, 1.19863, 1.20579, 1.24473, 1.50412}, - {1.20019, 1.20115, 1.20369, 1.20892, 1.21928, 1.26913, 1.24721, 1.24549, 1.25605, 1.29159, 1.54920}, - {1.24707, 1.24846, 1.25052, 1.25602, 1.26608, 1.31558, 1.29448, 1.29232, 1.30293, 1.33675, 1.59798}, - {1.29391, 1.29475, 1.29738, 1.30255, 1.31294, 1.36244, 1.34167, 1.33918, 1.34979, 1.38229, 1.64496}, - {1.34078, 1.34304, 1.34424, 1.35565, 1.35980, 1.40930, 1.38853, 1.38229, 1.39664, 1.42863, 1.69162}, - {1.38762, 1.38847, 1.39110, 1.39627, 1.40666, 1.45183, 1.43539, 1.43289, 1.44348, 1.47549, 1.73876}, - {1.43524, 1.43533, 1.43796, 1.44310, 1.45371, 1.49305, 1.48224, 1.47941, 1.49034, 1.52601, 1.78552}, - {1.48122, 1.48219, 1.48482, 1.48991, 1.50030, 1.53991, 1.52898, 1.52653, 1.53653, 1.57282, 1.82386}}; - - if (!mTimeStruct1) - mTimeStruct1 = new float[ktimebin * kZbin]; - if (!mTimeStruct2) - mTimeStruct2 = new float[ktimebin * kZbin]; - memset(mTimeStruct1, 0, ktimebin * kZbin * sizeof(float)); - memset(mTimeStruct2, 0, ktimebin * kZbin * sizeof(float)); - - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - if (vdrift > fVDsmp[6]) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time2200[ctrt][ctrz]; - mVDlo = fVDsmp[6]; - mVDhi = fVDsmp[7]; - } else if (vdrift > fVDsmp[5]) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; - mVDlo = fVDsmp[5]; - mVDhi = fVDsmp[6]; - } else if (vdrift > fVDsmp[4]) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; - mVDlo = fVDsmp[4]; - mVDhi = fVDsmp[5]; - } else if (vdrift > fVDsmp[3]) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; - mVDlo = fVDsmp[3]; - mVDhi = fVDsmp[4]; - } else if (vdrift > fVDsmp[2]) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; - mVDlo = fVDsmp[2]; - mVDhi = fVDsmp[3]; - } else if (vdrift > fVDsmp[1]) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; - mVDlo = fVDsmp[1]; - mVDhi = fVDsmp[2]; - } else if (vdrift > (fVDsmp[0] - 1.0e-5)) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1500[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; - mVDlo = fVDsmp[0]; - mVDhi = fVDsmp[1]; - } - } - } -} - -void TRDCommonParam::SetXenon() -{ - mGasMixture = kXenon; - TRDSimParam::Instance()->ReInit(); -} - -void TRDCommonParam::SetArgon() -{ - mGasMixture = kArgon; - TRDSimParam::Instance()->ReInit(); -} diff --git a/Detectors/TRD/base/src/TRDDiffAndTimeStructEstimator.cxx b/Detectors/TRD/base/src/TRDDiffAndTimeStructEstimator.cxx deleted file mode 100644 index d7ef65c81349b..0000000000000 --- a/Detectors/TRD/base/src/TRDDiffAndTimeStructEstimator.cxx +++ /dev/null @@ -1,578 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDDiffAndTimeStructEstimator.h" -#include "TRDBase/TRDGeometry.h" -#include <cmath> - -namespace o2::trd -{ - -// Garfield simulation at UD = -1500V; vd = 1.032cm/microsec, <driftfield> = 525V/cm -constexpr float time1500[ktimebin][kZbin] = { - {0.09170, 0.09205, 0.09306, 0.09475, 0.09716, 0.10035, 0.10445, 0.10993, 0.11838, 0.13986, 0.37858}, - {0.06588, 0.06626, 0.06739, 0.06926, 0.07186, 0.07524, 0.07951, 0.08515, 0.09381, 0.11601, 0.35673}, - {0.03946, 0.04003, 0.04171, 0.04435, 0.04780, 0.05193, 0.05680, 0.06306, 0.07290, 0.09873, 0.34748}, - {0.01151, 0.01283, 0.01718, 0.02282, 0.02880, 0.03479, 0.04098, 0.04910, 0.06413, 0.10567, 0.36897}, - {0.01116, 0.01290, 0.01721, 0.02299, 0.02863, 0.03447, 0.04074, 0.04984, 0.06839, 0.11625, 0.37277}, - {0.03919, 0.03974, 0.04131, 0.04380, 0.04703, 0.05102, 0.05602, 0.06309, 0.07651, 0.10938, 0.36838}, - {0.06493, 0.06560, 0.06640, 0.06802, 0.07051, 0.07392, 0.07853, 0.08510, 0.09690, 0.12621, 0.38058}, - {0.09174, 0.09186, 0.09225, 0.09303, 0.09477, 0.00000, 0.11205, 0.11952, 0.13461, 0.16984, 0.43017}, - {0.14356, 0.14494, 0.14959, 0.16002, 0.18328, 0.27981, 0.22785, 0.21240, 0.21948, 0.25965, 0.52392}, - {0.23120, 0.23366, 0.24046, 0.25422, 0.28071, 0.36914, 0.32999, 0.31208, 0.31772, 0.35804, 0.62249}, - {0.32686, 0.32916, 0.33646, 0.35053, 0.37710, 0.46292, 0.42773, 0.40948, 0.41497, 0.45527, 0.71955}, - {0.42353, 0.42583, 0.43317, 0.44727, 0.47380, 0.55884, 0.52479, 0.50650, 0.51194, 0.55225, 0.81658}, - {0.52038, 0.52271, 0.53000, 0.54415, 0.57064, 0.65545, 0.62172, 0.60341, 0.60885, 0.64915, 0.91339}, - {0.61724, 0.61953, 0.62694, 0.64098, 0.66756, 0.75226, 0.71862, 0.70030, 0.70575, 0.74604, 1.01035}, - {0.71460, 0.71678, 0.72376, 0.73786, 0.76447, 0.84913, 0.81551, 0.79720, 0.80264, 0.84292, 1.10723}, - {0.81101, 0.81334, 0.82066, 0.83475, 0.86127, 0.94599, 0.91240, 0.89408, 0.89952, 0.93981, 1.20409}, - {0.90788, 0.91023, 0.91752, 0.93163, 0.95815, 1.04293, 1.00929, 0.99096, 0.99640, 1.03669, 1.30106}, - {1.00477, 1.00707, 1.01449, 1.02852, 1.05504, 1.13976, 1.10617, 1.08784, 1.09329, 1.13358, 1.39796}, - {1.10166, 1.10397, 1.11130, 1.12541, 1.15257, 1.23672, 1.20307, 1.18472, 1.19018, 1.23046, 1.49486}, - {1.19854, 1.20084, 1.20818, 1.22235, 1.24885, 1.33355, 1.29992, 1.28156, 1.28707, 1.32735, 1.59177}, - {1.29544, 1.29780, 1.30507, 1.31917, 1.34575, 1.43073, 1.39681, 1.37851, 1.38396, 1.42377, 1.68854}, - {1.39236, 1.39462, 1.40205, 1.41607, 1.44259, 1.52745, 1.49368, 1.47541, 1.48083, 1.52112, 1.78546}, - {1.49314, 1.49149, 1.49885, 1.51297, 1.53949, 1.62420, 1.59016, 1.57231, 1.57772, 1.61800, 1.88048}, - {1.58610, 1.58839, 1.59572, 1.60983, 1.63635, 1.72109, 1.68651, 1.66921, 1.67463, 1.71489, 1.97918}, - {1.68400, 1.68529, 1.69261, 1.70671, 1.73331, 1.81830, 1.78341, 1.76605, 1.77150, 1.81179, 2.07608}, - {1.77991, 1.78215, 1.78952, 1.80385, 1.83014, 1.91486, 1.88128, 1.86215, 1.86837, 1.90865, 2.17304}, - {1.87674, 1.87904, 1.88647, 1.90052, 1.92712, 2.01173, 1.97812, 1.95905, 1.96527, 2.00710, 2.26979}, - {1.97369, 1.97594, 1.98326, 1.99869, 2.02442, 2.10865, 2.07501, 2.05666, 2.06214, 2.10243, 2.36669}, - {2.07052, 2.07281, 2.08016, 2.09425, 2.12132, 2.20555, 2.17182, 2.15341, 2.15904, 2.19933, 2.46363}, - {2.16742, 2.16971, 2.17707, 2.19114, 2.21766, 2.30240, 2.26877, 2.25015, 2.25573, 2.29586, 2.56060}, - {2.26423, 2.26659, 2.27396, 2.28803, 2.31456, 2.40828, 2.36567, 2.34705, 2.35282, 2.39765, 2.65744}, - {2.36153, 2.36349, 2.37330, 2.38501, 2.41159, 2.49940, 2.46257, 2.44420, 2.44843, 2.48987, 2.75431}, - {2.46558, 2.46035, 2.46822, 2.48181, 2.50849, 2.59630, 2.55947, 2.54112, 2.54513, 2.58677, 2.85094}, - {2.56248, 2.55723, 2.56486, 2.57871, 2.60520, 2.68998, 2.65626, 2.63790, 2.64316, 2.68360, 2.94813}, - {2.65178, 2.65441, 2.66153, 2.67556, 2.70210, 2.78687, 2.75319, 2.73463, 2.74032, 2.78060, 3.04503}, - {2.74868, 2.75131, 2.75870, 2.77245, 2.79385, 2.88700, 2.85009, 2.83177, 2.83723, 2.87750, 3.14193}, - {2.84574, 2.84789, 2.85560, 2.86935, 2.89075, 2.98060, 2.94576, 2.92868, 2.93356, 2.97436, 3.23868}, - {2.94239, 2.94469, 2.95221, 2.96625, 2.99345, 3.07747, 3.04266, 3.02545, 3.03051, 3.07118, 3.33555}}; - -// Garfield simulation at UD = -1600V; vd = 1.158cm/microsec, <driftfield> = 558V/cm -constexpr float time1600[ktimebin][kZbin] = { - {0.09169, 0.09203, 0.09305, 0.09473, 0.09714, 0.10032, 0.10441, 0.10990, 0.11835, 0.13986, 0.37845}, - {0.06589, 0.06626, 0.06738, 0.06924, 0.07184, 0.07521, 0.07947, 0.08512, 0.09379, 0.11603, 0.35648}, - {0.03947, 0.04003, 0.04171, 0.04434, 0.04778, 0.05190, 0.05678, 0.06304, 0.07292, 0.09876, 0.34759}, - {0.01111, 0.01281, 0.01718, 0.02281, 0.02879, 0.03477, 0.04097, 0.04910, 0.06415, 0.10573, 0.36896}, - {0.01120, 0.01311, 0.01721, 0.02279, 0.02862, 0.03446, 0.04074, 0.04981, 0.06825, 0.11595, 0.37255}, - {0.03919, 0.03980, 0.04132, 0.04380, 0.04704, 0.05102, 0.05602, 0.06302, 0.07633, 0.10896, 0.36743}, - {0.06531, 0.06528, 0.06631, 0.06805, 0.07053, 0.07392, 0.07853, 0.08505, 0.09669, 0.12578, 0.37967}, - {0.09157, 0.09171, 0.09216, 0.09301, 0.09475, 0.00000, 0.11152, 0.11879, 0.13352, 0.16802, 0.42750}, - {0.13977, 0.14095, 0.14509, 0.15433, 0.17534, 0.26406, 0.21660, 0.20345, 0.21113, 0.25067, 0.51434}, - {0.21816, 0.22041, 0.22631, 0.23850, 0.26208, 0.34340, 0.30755, 0.29237, 0.29878, 0.33863, 0.60258}, - {0.30344, 0.30547, 0.31241, 0.32444, 0.34809, 0.42696, 0.39464, 0.37919, 0.38546, 0.42530, 0.68926}, - {0.38969, 0.39164, 0.39810, 0.41059, 0.43441, 0.51246, 0.48112, 0.46562, 0.47191, 0.51172, 0.77558}, - {0.47592, 0.47799, 0.48442, 0.49689, 0.52061, 0.59855, 0.56752, 0.55201, 0.55826, 0.59808, 0.86202}, - {0.56226, 0.56428, 0.57074, 0.58324, 0.60696, 0.68483, 0.65388, 0.63837, 0.64461, 0.68445, 0.94830}, - {0.64881, 0.65063, 0.65709, 0.66958, 0.69331, 0.77117, 0.74023, 0.72472, 0.73098, 0.77079, 1.03486}, - {0.73506, 0.73698, 0.74344, 0.75596, 0.77964, 0.85751, 0.82658, 0.81107, 0.81731, 0.85712, 1.12106}, - {0.82132, 0.82333, 0.82979, 0.84228, 0.86608, 0.94386, 0.91293, 0.89742, 0.90367, 0.94335, 1.20737}, - {0.90767, 0.90968, 0.91614, 0.92863, 0.95236, 1.03021, 0.99928, 0.98377, 0.99001, 1.02984, 1.29371}, - {0.99410, 0.99602, 1.00257, 1.01498, 1.03869, 1.11720, 1.08563, 1.07011, 1.07637, 1.11621, 1.37873}, - {1.08036, 1.08240, 1.08884, 1.10138, 1.12504, 1.20301, 1.17198, 1.15647, 1.16272, 1.20255, 1.46651}, - {1.16670, 1.16872, 1.17525, 1.18783, 1.21139, 1.28934, 1.25833, 1.24281, 1.24909, 1.28889, 1.55275}, - {1.25307, 1.25510, 1.26153, 1.27404, 1.29773, 1.37584, 1.34469, 1.32916, 1.33536, 1.37524, 1.63915}, - {1.33942, 1.34146, 1.34788, 1.36040, 1.38410, 1.46438, 1.43105, 1.41537, 1.42176, 1.46158, 1.72538}, - {1.42579, 1.42782, 1.43458, 1.44674, 1.47043, 1.55085, 1.51675, 1.50168, 1.50810, 1.54793, 1.81174}, - {1.51207, 1.51454, 1.52060, 1.53307, 1.55684, 1.63478, 1.60336, 1.58820, 1.59446, 1.63414, 1.89814}, - {1.59856, 1.60047, 1.60693, 1.61942, 1.64317, 1.72257, 1.69008, 1.67454, 1.68080, 1.72063, 1.98433}, - {1.68481, 1.68682, 1.69330, 1.70584, 1.72949, 1.80752, 1.77643, 1.76089, 1.76716, 1.80692, 2.07069}, - {1.77117, 1.77319, 1.77969, 1.79260, 1.81583, 1.89376, 1.86226, 1.84720, 1.85355, 1.89256, 2.15343}, - {1.85748, 1.85967, 1.86605, 1.87848, 1.90222, 1.98010, 1.94913, 1.93271, 1.93981, 1.97968, 2.24355}, - {1.94386, 1.94587, 1.95233, 1.96484, 1.98854, 2.06646, 2.03542, 2.01755, 2.02617, 2.06604, 2.32993}, - {2.03022, 2.03230, 2.03868, 2.05134, 2.07488, 2.15367, 2.12178, 2.10391, 2.11252, 2.15432, 2.41623}, - {2.11656, 2.11857, 2.12505, 2.13772, 2.16147, 2.23919, 2.20817, 2.19265, 2.20744, 2.23872, 2.49996}, - {2.20291, 2.20611, 2.21137, 2.22387, 2.24758, 2.32563, 2.29450, 2.27901, 2.28525, 2.32507, 2.58897}, - {2.28922, 2.29172, 2.29774, 2.31345, 2.33400, 2.41287, 2.38086, 2.36535, 2.37160, 2.40869, 2.67113}, - {2.37572, 2.37764, 2.38410, 2.39803, 2.42046, 2.49817, 2.46721, 2.45171, 2.45794, 2.49505, 2.76061}, - {2.46190, 2.46396, 2.47043, 2.48340, 2.50665, 2.58453, 2.55357, 2.53728, 2.54430, 2.58407, 2.84816}, - {2.54833, 2.55032, 2.55679, 2.56976, 2.59312, 2.67103, 2.63993, 2.62364, 2.63062, 2.67040, 2.93444}, - {2.63456, 2.63660, 2.64304, 2.65555, 2.67938, 2.75739, 2.72629, 2.71064, 2.71688, 2.75671, 3.01886}}; - -// Garfield simulation at UD = -1700V; vd = 1.299cm/microsec, <driftfield> = 590V/cm -constexpr float time1700[ktimebin][kZbin] = { - {0.09167, 0.09201, 0.09302, 0.09471, 0.09712, 0.10029, 0.10438, 0.10986, 0.11832, 0.13986, 0.37824}, - {0.06591, 0.06626, 0.06736, 0.06923, 0.07183, 0.07519, 0.07944, 0.08511, 0.09378, 0.11603, 0.35625}, - {0.03946, 0.04003, 0.04170, 0.04433, 0.04777, 0.05189, 0.05676, 0.06301, 0.07291, 0.09880, 0.34724}, - {0.01110, 0.01281, 0.01718, 0.02280, 0.02879, 0.03476, 0.04096, 0.04910, 0.06417, 0.10582, 0.36861}, - {0.01115, 0.01294, 0.01721, 0.02276, 0.02862, 0.03447, 0.04074, 0.04980, 0.06812, 0.11565, 0.37231}, - {0.03920, 0.03974, 0.04133, 0.04381, 0.04706, 0.05105, 0.05603, 0.06299, 0.07618, 0.10860, 0.36646}, - {0.06498, 0.06529, 0.06634, 0.06808, 0.07055, 0.07395, 0.07852, 0.08500, 0.09650, 0.12532, 0.37850}, - {0.09143, 0.09159, 0.09207, 0.09297, 0.09473, 0.00000, 0.11102, 0.11809, 0.13245, 0.16627, 0.42496}, - {0.13646, 0.13750, 0.14112, 0.14926, 0.16806, 0.24960, 0.20627, 0.19536, 0.20366, 0.24256, 0.50557}, - {0.20678, 0.20848, 0.21384, 0.22450, 0.24552, 0.32018, 0.28740, 0.27477, 0.28196, 0.32128, 0.58475}, - {0.28287, 0.28461, 0.29020, 0.30108, 0.32224, 0.39467, 0.36500, 0.35217, 0.35926, 0.39860, 0.66194}, - {0.35972, 0.36145, 0.36713, 0.37797, 0.39912, 0.47091, 0.44212, 0.42925, 0.43632, 0.47563, 0.73892}, - {0.43667, 0.43841, 0.44413, 0.45494, 0.47607, 0.54780, 0.51912, 0.50627, 0.51334, 0.55254, 0.81595}, - {0.51365, 0.51540, 0.52101, 0.53193, 0.55305, 0.62463, 0.59617, 0.58328, 0.59035, 0.62965, 0.89303}, - {0.59064, 0.59240, 0.59801, 0.60893, 0.63009, 0.70169, 0.67317, 0.66028, 0.66735, 0.70666, 0.96995}, - {0.66765, 0.66939, 0.67501, 0.68592, 0.70724, 0.77863, 0.75016, 0.73728, 0.74435, 0.78366, 1.04696}, - {0.74464, 0.74636, 0.75200, 0.76293, 0.78405, 0.85561, 0.82716, 0.81427, 0.82133, 0.86064, 1.12396}, - {0.82165, 0.82340, 0.82902, 0.83991, 0.86104, 0.93266, 0.90414, 0.89128, 0.89834, 0.93763, 1.20100}, - {0.89863, 0.90042, 0.90659, 0.91705, 0.93805, 1.00960, 0.98115, 0.96825, 0.97533, 1.01462, 1.27801}, - {0.97563, 0.97740, 0.98310, 0.99391, 1.01504, 1.08659, 1.05814, 1.04526, 1.05233, 1.09163, 1.35503}, - {1.05276, 1.05451, 1.06002, 1.07090, 1.09099, 1.16357, 1.13516, 1.12225, 1.12933, 1.16863, 1.43195}, - {1.12977, 1.13138, 1.13700, 1.14792, 1.16797, 1.24061, 1.21212, 1.19926, 1.20626, 1.24554, 1.50900}, - {1.20664, 1.20839, 1.21400, 1.22490, 1.24606, 1.31772, 1.28914, 1.27382, 1.28329, 1.32262, 1.58550}, - {1.28367, 1.28541, 1.29099, 1.30189, 1.32312, 1.39460, 1.36612, 1.34924, 1.36030, 1.39961, 1.66310}, - {1.36064, 1.36249, 1.36799, 1.37896, 1.40004, 1.48030, 1.44314, 1.43032, 1.43731, 1.47659, 1.73442}, - {1.43762, 1.43937, 1.44497, 1.45618, 1.47704, 1.54932, 1.52012, 1.50725, 1.51430, 1.55357, 1.81708}, - {1.51462, 1.51937, 1.52203, 1.53316, 1.55403, 1.62572, 1.59713, 1.58424, 1.59128, 1.63061, 1.89406}, - {1.59162, 1.59338, 1.59947, 1.60989, 1.63103, 1.70270, 1.67411, 1.66124, 1.66799, 1.70759, 1.97103}, - {1.66874, 1.67037, 1.67597, 1.68687, 1.70814, 1.77969, 1.75112, 1.73806, 1.74530, 1.78457, 2.04794}, - {1.74693, 1.74749, 1.75297, 1.76476, 1.78500, 1.85667, 1.82811, 1.81504, 1.82101, 1.86161, 2.12492}, - {1.82260, 1.82437, 1.82995, 1.84174, 1.86202, 1.93372, 1.90509, 1.89202, 1.89930, 1.93859, 2.20189}, - {1.89964, 1.90135, 1.90693, 1.91789, 1.93952, 2.01080, 1.98207, 1.96921, 1.97628, 2.01384, 2.27887}, - {1.97662, 1.97917, 1.98611, 1.99487, 2.01601, 2.08778, 2.05846, 2.04623, 2.05330, 2.09244, 2.35585}, - {2.05359, 2.05615, 2.06309, 2.07187, 2.09867, 2.16459, 2.13610, 2.12322, 2.13029, 2.16942, 2.43199}, - {2.13063, 2.13233, 2.13795, 2.14886, 2.17008, 2.24199, 2.21310, 2.20020, 2.20727, 2.24659, 2.50983}, - {2.20761, 2.20931, 2.21955, 2.22624, 2.24708, 2.32147, 2.29009, 2.27725, 2.28276, 2.32359, 2.58680}, - {2.28459, 2.29108, 2.29202, 2.30286, 2.32007, 2.39559, 2.36683, 2.35422, 2.36119, 2.40058, 2.66081}, - {2.36153, 2.36806, 2.36889, 2.37985, 2.40092, 2.47828, 2.44381, 2.43099, 2.43819, 2.47750, 2.73779}}; - -// Garfield simulation at UD = -1800V; vd = 1.450cm/microsec, <driftfield> = 623V/cm -constexpr float time1800[ktimebin][kZbin] = { - {0.09166, 0.09199, 0.09300, 0.09470, 0.09709, 0.10026, 0.10434, 0.10983, 0.11831, 0.13987, 0.37802}, - {0.06585, 0.06623, 0.06735, 0.06921, 0.07180, 0.07520, 0.07941, 0.08507, 0.09376, 0.11604, 0.35624}, - {0.03945, 0.04004, 0.04169, 0.04432, 0.04776, 0.05187, 0.05674, 0.06300, 0.07290, 0.09884, 0.34704}, - {0.01108, 0.01287, 0.01717, 0.02280, 0.02880, 0.03476, 0.04095, 0.04909, 0.06419, 0.10589, 0.36843}, - {0.01115, 0.01287, 0.01720, 0.02276, 0.02862, 0.03448, 0.04073, 0.04973, 0.06799, 0.11535, 0.37224}, - {0.03918, 0.03975, 0.04134, 0.04382, 0.04707, 0.05105, 0.05603, 0.06296, 0.07605, 0.10822, 0.36560}, - {0.06498, 0.06532, 0.06635, 0.06809, 0.07058, 0.07395, 0.07855, 0.08495, 0.09632, 0.12488, 0.37730}, - {0.09130, 0.09160, 0.09199, 0.09300, 0.09472, 0.00000, 0.11059, 0.11747, 0.13146, 0.16462, 0.42233}, - {0.13364, 0.13449, 0.13767, 0.14481, 0.16147, 0.23635, 0.19706, 0.18812, 0.19704, 0.23520, 0.49749}, - {0.19698, 0.19844, 0.20311, 0.21236, 0.23082, 0.29920, 0.26936, 0.25927, 0.26732, 0.30601, 0.56871}, - {0.26518, 0.26670, 0.27160, 0.28099, 0.29955, 0.36597, 0.33885, 0.32858, 0.33653, 0.37524, 0.63801}, - {0.33441, 0.33553, 0.34040, 0.34987, 0.36841, 0.43428, 0.40797, 0.39763, 0.40556, 0.44425, 0.70698}, - {0.40296, 0.40447, 0.40934, 0.41881, 0.43737, 0.50306, 0.47695, 0.46662, 0.47455, 0.51329, 0.77600}, - {0.47296, 0.47344, 0.47830, 0.48779, 0.50632, 0.57200, 0.54593, 0.53559, 0.54351, 0.58222, 0.84489}, - {0.54089, 0.54264, 0.54727, 0.55673, 0.57529, 0.64094, 0.61490, 0.60457, 0.61249, 0.65118, 0.91394}, - {0.60987, 0.61138, 0.61624, 0.62573, 0.64428, 0.70989, 0.68397, 0.67354, 0.68147, 0.72015, 0.98291}, - {0.67883, 0.68035, 0.68521, 0.69469, 0.71324, 0.77896, 0.75287, 0.74251, 0.75043, 0.78912, 1.04458}, - {0.74780, 0.74932, 0.75421, 0.76367, 0.78221, 0.84785, 0.82185, 0.81148, 0.81941, 0.85811, 1.12085}, - {0.81690, 0.81830, 0.82316, 0.83263, 0.85120, 0.91683, 0.89077, 0.88045, 0.88837, 0.92707, 1.18976}, - {0.88574, 0.88726, 0.89228, 0.90198, 0.92017, 0.98578, 0.95974, 0.94947, 0.95734, 0.99604, 1.25873}, - {0.95493, 0.95624, 0.96110, 0.97058, 0.98913, 1.05481, 1.02873, 1.01839, 1.02631, 1.06503, 1.32772}, - {1.02392, 1.02524, 1.03008, 1.03955, 1.05810, 1.12378, 1.09757, 1.08605, 1.09530, 1.13399, 1.39669}, - {1.09270, 1.09418, 1.09911, 1.10854, 1.12714, 1.19281, 1.16502, 1.15633, 1.16427, 1.20271, 1.46574}, - {1.16168, 1.16323, 1.16801, 1.17772, 1.19604, 1.26190, 1.23399, 1.22531, 1.23323, 1.27194, 1.53475}, - {1.23061, 1.23214, 1.23698, 1.24669, 1.26503, 1.33073, 1.30461, 1.29428, 1.30220, 1.34091, 1.60372}, - {1.29960, 1.30110, 1.30596, 1.31544, 1.33398, 1.39962, 1.37228, 1.36323, 1.37121, 1.40988, 1.67273}, - {1.36851, 1.37007, 1.37512, 1.38441, 1.40297, 1.46865, 1.44256, 1.43222, 1.44017, 1.47878, 1.74155}, - {1.43752, 1.43904, 1.44773, 1.45338, 1.47220, 1.53759, 1.51136, 1.50119, 1.50914, 1.54775, 1.81050}, - {1.50646, 1.50802, 1.51288, 1.52237, 1.54097, 1.60697, 1.58049, 1.57018, 1.57811, 1.61678, 1.87947}, - {1.57545, 1.57720, 1.58185, 1.59134, 1.60996, 1.67787, 1.64929, 1.63914, 1.64707, 1.68570, 1.94851}, - {1.64442, 1.64617, 1.65081, 1.66035, 1.67893, 1.74684, 1.71826, 1.70745, 1.71604, 1.75310, 2.01748}, - {1.71337, 1.71513, 1.71978, 1.72932, 1.74645, 1.81346, 1.78739, 1.77642, 1.78501, 1.82151, 2.08644}, - {1.78238, 1.78410, 1.78876, 1.79824, 1.81678, 1.88332, 1.85639, 1.84262, 1.85397, 1.89270, 2.15533}, - {1.85135, 1.85306, 1.85778, 1.86728, 1.88580, 1.95615, 1.92536, 1.91171, 1.92283, 1.96165, 2.22428}, - {1.92774, 1.92184, 1.92672, 1.93618, 1.95477, 2.02048, 1.99427, 1.98068, 1.99192, 2.03062, 2.29338}, - {1.98929, 1.99081, 1.99567, 2.00515, 2.02373, 2.08987, 2.06332, 2.05249, 2.05939, 2.09928, 2.36227}, - {2.05829, 2.05978, 2.06464, 2.07414, 2.09272, 2.15850, 2.12928, 2.12194, 2.12987, 2.16825, 2.43083}, - {2.12726, 2.12869, 2.13360, 2.14425, 2.16160, 2.22872, 2.20118, 2.19078, 2.19876, 2.23416, 2.50007}}; - -// Garfield simulation at UD = -1900V; vd = 1.610cm/microsec, <driftfield> = 655V/cm -constexpr float time1900[ktimebin][kZbin] = { - {0.09166, 0.09198, 0.09298, 0.09467, 0.09707, 0.10023, 0.10431, 0.10980, 0.11828, 0.13988, 0.37789}, - {0.06584, 0.06622, 0.06735, 0.06920, 0.07179, 0.07514, 0.07938, 0.08505, 0.09374, 0.11606, 0.35599}, - {0.03945, 0.04002, 0.04169, 0.04432, 0.04775, 0.05185, 0.05672, 0.06298, 0.07290, 0.09888, 0.34695}, - {0.01109, 0.01281, 0.01717, 0.02279, 0.02878, 0.03476, 0.04094, 0.04909, 0.06421, 0.10597, 0.36823}, - {0.01115, 0.01287, 0.01720, 0.02294, 0.02862, 0.03448, 0.04074, 0.04973, 0.06783, 0.11506, 0.37206}, - {0.03940, 0.03975, 0.04135, 0.04386, 0.04708, 0.05106, 0.05604, 0.06293, 0.07592, 0.10787, 0.36484}, - {0.06500, 0.06534, 0.06638, 0.06811, 0.07060, 0.07413, 0.07852, 0.08491, 0.09614, 0.12446, 0.37626}, - {0.09119, 0.09140, 0.09194, 0.09293, 0.09471, 0.00000, 0.11013, 0.11685, 0.13050, 0.16302, 0.41991}, - {0.13111, 0.13190, 0.13466, 0.14091, 0.15554, 0.22409, 0.18846, 0.18167, 0.19113, 0.22854, 0.48995}, - {0.18849, 0.18975, 0.19380, 0.20185, 0.21797, 0.28050, 0.25368, 0.24575, 0.25446, 0.29249, 0.55442}, - {0.24995, 0.25125, 0.25563, 0.26366, 0.27986, 0.34065, 0.31605, 0.30815, 0.31680, 0.35482, 0.61697}, - {0.31187, 0.31324, 0.31745, 0.32580, 0.34205, 0.40217, 0.37825, 0.37031, 0.37897, 0.41696, 0.67890}, - {0.37401, 0.37531, 0.37955, 0.38777, 0.40395, 0.46408, 0.44037, 0.43242, 0.44108, 0.47906, 0.74122}, - {0.43610, 0.43741, 0.44161, 0.44986, 0.46604, 0.52614, 0.50248, 0.49452, 0.50316, 0.54116, 0.80326}, - {0.49820, 0.49988, 0.50372, 0.51196, 0.52814, 0.58822, 0.56459, 0.55661, 0.56527, 0.60326, 0.86526}, - {0.56032, 0.56161, 0.56582, 0.57408, 0.59024, 0.65032, 0.62670, 0.61872, 0.62737, 0.66537, 0.92749}, - {0.62240, 0.62371, 0.62792, 0.63624, 0.65236, 0.71241, 0.68881, 0.68081, 0.68947, 0.72750, 0.98941}, - {0.68449, 0.68581, 0.69002, 0.69828, 0.71444, 0.77452, 0.75089, 0.74295, 0.75157, 0.78957, 1.05157}, - {0.74660, 0.74790, 0.75212, 0.76036, 0.77654, 0.83748, 0.81299, 0.80501, 0.81193, 0.85168, 1.11375}, - {0.80870, 0.81017, 0.81423, 0.82246, 0.83867, 0.89908, 0.87509, 0.86660, 0.87577, 0.91376, 1.17586}, - {0.87080, 0.87233, 0.87632, 0.88458, 0.90074, 0.96083, 0.93718, 0.92922, 0.93787, 0.97588, 1.23794}, - {0.93291, 0.93422, 0.93844, 0.94667, 0.96293, 1.02295, 0.99929, 0.99127, 0.99997, 1.03797, 1.29995}, - {0.99500, 0.99645, 1.00308, 1.00877, 1.02497, 1.08504, 1.06140, 1.05343, 1.06203, 1.10006, 1.36216}, - {1.05712, 1.05926, 1.06262, 1.07092, 1.08706, 1.14754, 1.12350, 1.11550, 1.12417, 1.16218, 1.42427}, - {1.11921, 1.12059, 1.12473, 1.13297, 1.14916, 1.21140, 1.18560, 1.17284, 1.18625, 1.22414, 1.48629}, - {1.18140, 1.18262, 1.18690, 1.19508, 1.21125, 1.27139, 1.24164, 1.23495, 1.24838, 1.28634, 1.54852}, - {1.24340, 1.24473, 1.24901, 1.25732, 1.27336, 1.33358, 1.30793, 1.30179, 1.31047, 1.34848, 1.61066}, - {1.30551, 1.30684, 1.31104, 1.32056, 1.33553, 1.39609, 1.37004, 1.36392, 1.37045, 1.41057, 1.67259}, - {1.36755, 1.36892, 1.37315, 1.39148, 1.39755, 1.45820, 1.43215, 1.42602, 1.43467, 1.47268, 1.73477}, - {1.42966, 1.43101, 1.43549, 1.45359, 1.45976, 1.52031, 1.49601, 1.48811, 1.49677, 1.53477, 1.79691}, - {1.49180, 1.49321, 1.49760, 1.51570, 1.52175, 1.58185, 1.55771, 1.55023, 1.55888, 1.59672, 1.85501}, - {1.55391, 1.55527, 1.55943, 1.57782, 1.58391, 1.64395, 1.62008, 1.61233, 1.62085, 1.65883, 1.92091}, - {1.61599, 1.61732, 1.62154, 1.63993, 1.64612, 1.70608, 1.68237, 1.67108, 1.68301, 1.72110, 1.97931}, - {1.67808, 1.67948, 1.68364, 1.70204, 1.70823, 1.76858, 1.74404, 1.73539, 1.74512, 1.78321, 2.04522}, - {1.74019, 1.74152, 1.74573, 1.76415, 1.77015, 1.83040, 1.80615, 1.79366, 1.80723, 1.84509, 2.10742}, - {1.80235, 1.80362, 1.80783, 1.82626, 1.83227, 1.89246, 1.86795, 1.85405, 1.86938, 1.90720, 2.16953}, - {1.86442, 1.86572, 1.86994, 1.88837, 1.89438, 1.95445, 1.93006, 1.92283, 1.93148, 1.96931, 2.23147}, - {1.92700, 1.92783, 1.93197, 1.95049, 1.95649, 2.01668, 1.99217, 1.98486, 1.99352, 2.03143, 2.29358}}; - -// Garfield simulation at UD = -2000V; vd = 1.783cm/microsec, <driftfield> = 688V/cm -constexpr float time2000[ktimebin][kZbin] = { - {0.09176, 0.09196, 0.09296, 0.09465, 0.09704, 0.10020, 0.10427, 0.10977, 0.11825, 0.13988, 0.37774}, - {0.06583, 0.06620, 0.06735, 0.06918, 0.07177, 0.07513, 0.07936, 0.08503, 0.09372, 0.11606, 0.35586}, - {0.03944, 0.04001, 0.04170, 0.04431, 0.04774, 0.05184, 0.05670, 0.06296, 0.07291, 0.09893, 0.34680}, - {0.01108, 0.01281, 0.01719, 0.02279, 0.02879, 0.03474, 0.04093, 0.04908, 0.06422, 0.10605, 0.36800}, - {0.01114, 0.01287, 0.01720, 0.02276, 0.02863, 0.03449, 0.04073, 0.04970, 0.06774, 0.11478, 0.37179}, - {0.03925, 0.03977, 0.04135, 0.04386, 0.04711, 0.05108, 0.05604, 0.06290, 0.07580, 0.10748, 0.36386}, - {0.06501, 0.06536, 0.06640, 0.06814, 0.07062, 0.07398, 0.07852, 0.08487, 0.09598, 0.12405, 0.37519}, - {0.09109, 0.09127, 0.09188, 0.09292, 0.09472, 0.00000, 0.10964, 0.11630, 0.12960, 0.16150, 0.41765}, - {0.12898, 0.12968, 0.13209, 0.13749, 0.15034, 0.21286, 0.18088, 0.17590, 0.18591, 0.22254, 0.48315}, - {0.18122, 0.18227, 0.18574, 0.19263, 0.20674, 0.26376, 0.23960, 0.23375, 0.24316, 0.28047, 0.54179}, - {0.23674, 0.23784, 0.24142, 0.24847, 0.26264, 0.31810, 0.29602, 0.29008, 0.29944, 0.33675, 0.59795}, - {0.29279, 0.29382, 0.29742, 0.30448, 0.31865, 0.37364, 0.35215, 0.34629, 0.35555, 0.39286, 0.65411}, - {0.34875, 0.34987, 0.35346, 0.36054, 0.37472, 0.42956, 0.40825, 0.40229, 0.41167, 0.44894, 0.71033}, - {0.40484, 0.40594, 0.40954, 0.41660, 0.43077, 0.48560, 0.46433, 0.45840, 0.46772, 0.50500, 0.76632}, - {0.46090, 0.46201, 0.46560, 0.47267, 0.48684, 0.54167, 0.52041, 0.51449, 0.52382, 0.56108, 0.82227}, - {0.51698, 0.51809, 0.52173, 0.52874, 0.54291, 0.59776, 0.57646, 0.57052, 0.57986, 0.61717, 0.87836}, - {0.57306, 0.57418, 0.57782, 0.58513, 0.59899, 0.65380, 0.63255, 0.62661, 0.63594, 0.67325, 0.93460}, - {0.62912, 0.63024, 0.63383, 0.64103, 0.65506, 0.70988, 0.68484, 0.68267, 0.69202, 0.72878, 0.99046}, - {0.68521, 0.68633, 0.68990, 0.69699, 0.71115, 0.76595, 0.74468, 0.73872, 0.74814, 0.78538, 1.04674}, - {0.74127, 0.74239, 0.74605, 0.75303, 0.77022, 0.82204, 0.80078, 0.79484, 0.80416, 0.84147, 1.10261}, - {0.79736, 0.79846, 0.80206, 0.80947, 0.82330, 0.87813, 0.85688, 0.85087, 0.86023, 0.89752, 1.15874}, - {0.85342, 0.85454, 0.85815, 0.86519, 0.87936, 0.93417, 0.91293, 0.90428, 0.91631, 0.95360, 1.20760}, - {0.90949, 0.91061, 0.91423, 0.92128, 0.93544, 0.99026, 0.96807, 0.96305, 0.97239, 1.00967, 1.27078}, - {0.96556, 0.96669, 0.97111, 0.97734, 0.99151, 1.04664, 1.02508, 1.01879, 1.02846, 1.06167, 1.32695}, - {1.02167, 1.02279, 1.02656, 1.03341, 1.04759, 1.10242, 1.08115, 1.07003, 1.08453, 1.12184, 1.38304}, - {1.07776, 1.07883, 1.08242, 1.08950, 1.10384, 1.16422, 1.13725, 1.13133, 1.14061, 1.17793, 1.43910}, - {1.13379, 1.13492, 1.13864, 1.14567, 1.15973, 1.21455, 1.19323, 1.18734, 1.19668, 1.23401, 1.49528}, - {1.18988, 1.19098, 1.19457, 1.20164, 1.21582, 1.27064, 1.24937, 1.24044, 1.25275, 1.29004, 1.55137}, - {1.24592, 1.24706, 1.25087, 1.25773, 1.27188, 1.32670, 1.30544, 1.29953, 1.30883, 1.34613, 1.60743}, - {1.30202, 1.30313, 1.30673, 1.31381, 1.32797, 1.38278, 1.36151, 1.35167, 1.36490, 1.40221, 1.66306}, - {1.35809, 1.35921, 1.36282, 1.36986, 1.38403, 1.43888, 1.41760, 1.41174, 1.42083, 1.45830, 1.71915}, - {1.41419, 1.41528, 1.41890, 1.42595, 1.44011, 1.49496, 1.47368, 1.46769, 1.47706, 1.51436, 1.77523}, - {1.47131, 1.47141, 1.47494, 1.48850, 1.49620, 1.55137, 1.52977, 1.51820, 1.53315, 1.57042, 1.83158}, - {1.52635, 1.52750, 1.53103, 1.53814, 1.55228, 1.60736, 1.58503, 1.57986, 1.58920, 1.62649, 1.88767}, - {1.58418, 1.58355, 1.58711, 1.59526, 1.60833, 1.66316, 1.63345, 1.63261, 1.64556, 1.68204, 1.94359}, - {1.64027, 1.63958, 1.64489, 1.65024, 1.66443, 1.71925, 1.69794, 1.69201, 1.70143, 1.73865, 1.99968}, - {1.69450, 1.69566, 1.69940, 1.70697, 1.71841, 1.77819, 1.75396, 1.74814, 1.75743, 1.79083, 2.05427}, - {1.75054, 1.75221, 1.75527, 1.76306, 1.77662, 1.83428, 1.81006, 1.81173, 1.81345, 1.85076, 2.10289}}; - -// Garfield simulation at UD = -2100V; vd = 1.959cm/microsec, <driftfield> = 720V/cm -constexpr float time2100[ktimebin][kZbin] = { - {0.09160, 0.09194, 0.09294, 0.09462, 0.09701, 0.10017, 0.10424, 0.10974, 0.11823, 0.13988, 0.37762}, - {0.06585, 0.06619, 0.06731, 0.06916, 0.07174, 0.07509, 0.07933, 0.08500, 0.09370, 0.11609, 0.35565}, - {0.03960, 0.04001, 0.04171, 0.04430, 0.04774, 0.05182, 0.05668, 0.06294, 0.07291, 0.09896, 0.34676}, - {0.01109, 0.01280, 0.01716, 0.02279, 0.02876, 0.03474, 0.04096, 0.04908, 0.06424, 0.10612, 0.36790}, - {0.01114, 0.01285, 0.01719, 0.02287, 0.02863, 0.03449, 0.04073, 0.04964, 0.06759, 0.11446, 0.37162}, - {0.03922, 0.03977, 0.04146, 0.04386, 0.04711, 0.05109, 0.05605, 0.06287, 0.07575, 0.10713, 0.36298}, - {0.06504, 0.06538, 0.06641, 0.06818, 0.07064, 0.07426, 0.07852, 0.08483, 0.09581, 0.12363, 0.37424}, - {0.09103, 0.09129, 0.09186, 0.09291, 0.09476, 0.00000, 0.10923, 0.11578, 0.12873, 0.16005, 0.41525}, - {0.12723, 0.12777, 0.12988, 0.13458, 0.14579, 0.20264, 0.17421, 0.17078, 0.18132, 0.21708, 0.47699}, - {0.17508, 0.17601, 0.17897, 0.18487, 0.19698, 0.24881, 0.22737, 0.22337, 0.23348, 0.27000, 0.53032}, - {0.22571, 0.22663, 0.22969, 0.23570, 0.24787, 0.29826, 0.27871, 0.27462, 0.28471, 0.32122, 0.58166}, - {0.27664, 0.27759, 0.28067, 0.28669, 0.29891, 0.34898, 0.32982, 0.32570, 0.33576, 0.37229, 0.63268}, - {0.32766, 0.32862, 0.33170, 0.33778, 0.34988, 0.39973, 0.38088, 0.37675, 0.38680, 0.42333, 0.68159}, - {0.37872, 0.37966, 0.38275, 0.38875, 0.40093, 0.45073, 0.43192, 0.42780, 0.43786, 0.47438, 0.73480}, - {0.42974, 0.43070, 0.43378, 0.43982, 0.45196, 0.50177, 0.48297, 0.47884, 0.48889, 0.52544, 0.78581}, - {0.48081, 0.48175, 0.48482, 0.49084, 0.50302, 0.55290, 0.53398, 0.52988, 0.53994, 0.57647, 0.83687}, - {0.53645, 0.53295, 0.53586, 0.54188, 0.55408, 0.60398, 0.58504, 0.58092, 0.59100, 0.62768, 0.88773}, - {0.58345, 0.58409, 0.58690, 0.59292, 0.60510, 0.65562, 0.63609, 0.63197, 0.64203, 0.67856, 0.93907}, - {0.63397, 0.63490, 0.63795, 0.64403, 0.65613, 0.70612, 0.68714, 0.68301, 0.69294, 0.72955, 0.99000}, - {0.68496, 0.68592, 0.68899, 0.69504, 0.70733, 0.75708, 0.73818, 0.73405, 0.74412, 0.78064, 1.04100}, - {0.73600, 0.73696, 0.74003, 0.74624, 0.75828, 0.80805, 0.78904, 0.78512, 0.79517, 0.83152, 1.09205}, - {0.78709, 0.78801, 0.79108, 0.79709, 0.80931, 0.85906, 0.84027, 0.83614, 0.84621, 0.88269, 1.14058}, - {0.83808, 0.83905, 0.84215, 0.84816, 0.86031, 0.91011, 0.89139, 0.88718, 0.89725, 0.93377, 1.19413}, - {0.88916, 0.89010, 0.89320, 0.89920, 0.91136, 0.96117, 0.94235, 0.93822, 0.94828, 0.98480, 1.24538}, - {0.94036, 0.94113, 0.94422, 0.95023, 0.96241, 1.01220, 0.99310, 0.98927, 0.99933, 1.03585, 1.29629}, - {0.99139, 0.99220, 0.99525, 1.00127, 1.01344, 1.06324, 1.04451, 1.04033, 1.04836, 1.08690, 1.34727}, - {1.04261, 1.04325, 1.04628, 1.05232, 1.06448, 1.12090, 1.09546, 1.09136, 1.10142, 1.13795, 1.39831}, - {1.09331, 1.09429, 1.09742, 1.10336, 1.11557, 1.16547, 1.14658, 1.13642, 1.15247, 1.18898, 1.44936}, - {1.14436, 1.14539, 1.14847, 1.15443, 1.16662, 1.21794, 1.19763, 1.19329, 1.20349, 1.23956, 1.50043}, - {1.19533, 1.19651, 1.19943, 1.20548, 1.21666, 1.26753, 1.24862, 1.24434, 1.25455, 1.29106, 1.55142}, - {1.24638, 1.24756, 1.25046, 1.25648, 1.26764, 1.31858, 1.29967, 1.29538, 1.30499, 1.34211, 1.60250}, - {1.29747, 1.29847, 1.30175, 1.30753, 1.31869, 1.36969, 1.35069, 1.34656, 1.35663, 1.39316, 1.64644}, - {1.35537, 1.34952, 1.35255, 1.35869, 1.36973, 1.41387, 1.40173, 1.39761, 1.40768, 1.44396, 1.70238}, - {1.39956, 1.40056, 1.40380, 1.40961, 1.42178, 1.46492, 1.45278, 1.45423, 1.45872, 1.49522, 1.75557}, - {1.45080, 1.45159, 1.45463, 1.46109, 1.47287, 1.52263, 1.50382, 1.50050, 1.50977, 1.54502, 1.80670}, - {1.50165, 1.50264, 1.50570, 1.51214, 1.52233, 1.57370, 1.55484, 1.55155, 1.56080, 1.59731, 1.85778}, - {1.55269, 1.55364, 1.55675, 1.56274, 1.57491, 1.62598, 1.60590, 1.60259, 1.61185, 1.64836, 1.90883}, - {1.60368, 1.60469, 1.60779, 1.61373, 1.62596, 1.67738, 1.65651, 1.65249, 1.66290, 1.69936, 1.95959}}; - -// Garfield simulation at UD = -2200V; vd = 2.134cm/microsec, <driftfield> = 753V/cm -constexpr float time2200[ktimebin][kZbin] = { - {0.09162, 0.09194, 0.09292, 0.09460, 0.09702, 0.10014, 0.10421, 0.10971, 0.11820, 0.13990, 0.37745}, - {0.06581, 0.06618, 0.06730, 0.06915, 0.07173, 0.07507, 0.07931, 0.08497, 0.09368, 0.11609, 0.35560}, - {0.03947, 0.04001, 0.04167, 0.04429, 0.04772, 0.05183, 0.05667, 0.06293, 0.07292, 0.09900, 0.34673}, - {0.01111, 0.01280, 0.01716, 0.02279, 0.02876, 0.03473, 0.04091, 0.04907, 0.06426, 0.10620, 0.36766}, - {0.01113, 0.01285, 0.01719, 0.02276, 0.02863, 0.03452, 0.04076, 0.04960, 0.06745, 0.11419, 0.37139}, - {0.03923, 0.03978, 0.04137, 0.04387, 0.04713, 0.05110, 0.05605, 0.06284, 0.07551, 0.10677, 0.36210}, - {0.06505, 0.06540, 0.06644, 0.06820, 0.07069, 0.07401, 0.07852, 0.08479, 0.09565, 0.12325, 0.37313}, - {0.09107, 0.09127, 0.09181, 0.09291, 0.09474, 0.00000, 0.10883, 0.11528, 0.12789, 0.15865, 0.41313}, - {0.12559, 0.12622, 0.12800, 0.13206, 0.14166, 0.19331, 0.16832, 0.16632, 0.17724, 0.21218, 0.47098}, - {0.16992, 0.17070, 0.17325, 0.17831, 0.18871, 0.23557, 0.21690, 0.21451, 0.22514, 0.26082, 0.52034}, - {0.21640, 0.21722, 0.21987, 0.22500, 0.23540, 0.28097, 0.26399, 0.26154, 0.27214, 0.30784, 0.56734}, - {0.26318, 0.26400, 0.26679, 0.27181, 0.28220, 0.32739, 0.31090, 0.30842, 0.31902, 0.35474, 0.61415}, - {0.31001, 0.31085, 0.31348, 0.31866, 0.32903, 0.37412, 0.35777, 0.35546, 0.36588, 0.40159, 0.66103}, - {0.35687, 0.35769, 0.36033, 0.36556, 0.37588, 0.42094, 0.40523, 0.40214, 0.41273, 0.44841, 0.70785}, - {0.40372, 0.40457, 0.40723, 0.41234, 0.42273, 0.46778, 0.45148, 0.44903, 0.45961, 0.49526, 0.75486}, - {0.45062, 0.45139, 0.45404, 0.45966, 0.46958, 0.51470, 0.49833, 0.49584, 0.50644, 0.54211, 0.80160}, - {0.49742, 0.49825, 0.50088, 0.50605, 0.51644, 0.56148, 0.54518, 0.54270, 0.55330, 0.58897, 0.84854}, - {0.54427, 0.54510, 0.54774, 0.55290, 0.56329, 0.60846, 0.59203, 0.58955, 0.60014, 0.63578, 0.89528}, - {0.59119, 0.59199, 0.59471, 0.59975, 0.61014, 0.65533, 0.63889, 0.63636, 0.64699, 0.68269, 0.94197}, - {0.63866, 0.63880, 0.64145, 0.64664, 0.65701, 0.70639, 0.68574, 0.68325, 0.69385, 0.72949, 0.98900}, - {0.68483, 0.68566, 0.68831, 0.69347, 0.70386, 0.74890, 0.73260, 0.73010, 0.74069, 0.77638, 1.03320}, - {0.73168, 0.73255, 0.73515, 0.74031, 0.75072, 0.79576, 0.77117, 0.77501, 0.78755, 0.82318, 1.08006}, - {0.77854, 0.78310, 0.78200, 0.79525, 0.79756, 0.84281, 0.81803, 0.82393, 0.83441, 0.87008, 1.12692}, - {0.82541, 0.82642, 0.82916, 0.83528, 0.84442, 0.89086, 0.87315, 0.87079, 0.88125, 0.91694, 1.17648}, - {0.87226, 0.87308, 0.87602, 0.88086, 0.89128, 0.93772, 0.92001, 0.91751, 0.92811, 0.95587, 1.22328}, - {0.91921, 0.91994, 0.92256, 0.92772, 0.94713, 0.98566, 0.96690, 0.96436, 0.97495, 1.01064, 1.26882}, - {0.96790, 0.96679, 0.96941, 0.97463, 0.99399, 1.03001, 1.01376, 1.01112, 1.02181, 1.05749, 1.31568}, - {1.01278, 1.01390, 1.01674, 1.02147, 1.03182, 1.07820, 1.06056, 1.05798, 1.06867, 1.10433, 1.36390}, - {1.05964, 1.06076, 1.06331, 1.06833, 1.07870, 1.13297, 1.10742, 1.10520, 1.11543, 1.15120, 1.41069}, - {1.10664, 1.10762, 1.10997, 1.11519, 1.12556, 1.17531, 1.15427, 1.14620, 1.16229, 1.19805, 1.45758}, - {1.15352, 1.15421, 1.15683, 1.16218, 1.17242, 1.21910, 1.20035, 1.19863, 1.20579, 1.24473, 1.50412}, - {1.20019, 1.20115, 1.20369, 1.20892, 1.21928, 1.26913, 1.24721, 1.24549, 1.25605, 1.29159, 1.54920}, - {1.24707, 1.24846, 1.25052, 1.25602, 1.26608, 1.31558, 1.29448, 1.29232, 1.30293, 1.33675, 1.59798}, - {1.29391, 1.29475, 1.29738, 1.30255, 1.31294, 1.36244, 1.34167, 1.33918, 1.34979, 1.38229, 1.64496}, - {1.34078, 1.34304, 1.34424, 1.35565, 1.35980, 1.40930, 1.38853, 1.38229, 1.39664, 1.42863, 1.69162}, - {1.38762, 1.38847, 1.39110, 1.39627, 1.40666, 1.45183, 1.43539, 1.43289, 1.44348, 1.47549, 1.73876}, - {1.43524, 1.43533, 1.43796, 1.44310, 1.45371, 1.49305, 1.48224, 1.47941, 1.49034, 1.52601, 1.78552}, - {1.48122, 1.48219, 1.48482, 1.48991, 1.50030, 1.53991, 1.52898, 1.52653, 1.53653, 1.57282, 1.82386}}; - -//_____________________________________________________________________________ -void TRDDiffusionAndTimeStructEstimator::SampleTimeStruct(float vdrift) -{ - // - // Samples the timing structure of a drift cell - // Drift Time data calculated with Garfield (by C.Lippmann) - // - - // Nothing to do - if (std::abs(mTimeLastVdrift - vdrift) < 1.e-3) { - return; - } - mTimeLastVdrift = vdrift; - - // Drift time maps are saved for some drift velocity values (in drift region): - constexpr float fVDsmp[8] = {1.032, 1.158, 1.299, 1.450, 1.610, 1.783, 1.959, 2.134}; - - if (vdrift < fVDsmp[0]) { - LOG(WARN) << "TRD: Drift Velocity too small " << vdrift << " < " << fVDsmp[0]; - vdrift = fVDsmp[0]; - } else if (vdrift > fVDsmp[7]) { - LOG(WARN) << "TRD: Drift Velocity too large " << vdrift << " > " << fVDsmp[6]; - vdrift = fVDsmp[7]; - } - if (vdrift > fVDsmp[6]) { - mVDlo = fVDsmp[6]; - mVDhi = fVDsmp[7]; - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time2200[ctrt][ctrz]; - } - } - } else if (vdrift > fVDsmp[5]) { - mVDlo = fVDsmp[5]; - mVDhi = fVDsmp[6]; - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time2100[ctrt][ctrz]; - } - } - } else if (vdrift > fVDsmp[4]) { - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time2000[ctrt][ctrz]; - } - } - mVDlo = fVDsmp[4]; - mVDhi = fVDsmp[5]; - } else if (vdrift > fVDsmp[3]) { - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1900[ctrt][ctrz]; - } - } - mVDlo = fVDsmp[3]; - mVDhi = fVDsmp[4]; - } else if (vdrift > fVDsmp[2]) { - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1800[ctrt][ctrz]; - } - } - mVDlo = fVDsmp[2]; - mVDhi = fVDsmp[3]; - } else if (vdrift > fVDsmp[1]) { - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1700[ctrt][ctrz]; - } - } - mVDlo = fVDsmp[1]; - mVDhi = fVDsmp[2]; - } else if (vdrift > (fVDsmp[0] - 1.0e-5)) { - for (int ctrt = 0; ctrt < ktimebin; ctrt++) { - for (int ctrz = 0; ctrz < kZbin; ctrz++) { - mTimeStruct1[ctrt + ctrz * ktimebin] = time1500[ctrt][ctrz]; - mTimeStruct2[ctrt + ctrz * ktimebin] = time1600[ctrt][ctrz]; - } - } - mVDlo = fVDsmp[0]; - mVDhi = fVDsmp[1]; - } - mInvBinWidth = 1. / (mVDhi - mVDlo); -} - -//_____________________________________________________________________________ -float TRDDiffusionAndTimeStructEstimator::TimeStruct(float vdrift, float dist, float z) -{ - // - // Applies the time structure of the drift cells (by C.Lippmann). - // The drift time of electrons to the anode wires depends on the - // distance to the wire (z) and on the position in the drift region. - // - // input : - // dist = radial distance from (cathode) pad plane [cm] - // z = distance from anode wire (parallel to cathode planes) [cm] - // - // output : - // tdrift = the drift time of an electron at the given position - // - // We interpolate between the drift time values at the two drift - // velocities fVDlo and fVDhi, being smaller and larger than - // fDriftVelocity. We use the two stored drift time maps fTimeStruct1 - // and fTimeStruct2, calculated for the two mentioned drift velocities. - // - - SampleTimeStruct(vdrift); - - // Indices: - int r1 = (int)(10 * dist); - if (r1 < 0) - r1 = 0; - if (r1 > 37) - r1 = 37; - int r2 = r1 + 1; - if (r2 > 37) - r2 = 37; - const int kz1 = ((int)(100 * z / 2.5)); - const int kz2 = kz1 + 1; - - if ((r1 < 0) || (r1 > 37) || (kz1 < 0) || (kz1 > 10)) { - LOG(WARN) << Form("TRD: Time struct indices out of range: dist=%.2f, z=%.2f, r1=%d, kz1=%d", dist, z, r1, kz1); - } - - const float ky111 = mTimeStruct1[r1 + 38 * kz1]; - const float ky221 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct1[r2 + 38 * kz2] : mTimeStruct1[37 + 38 * 10]; - const float ky121 = (kz2 <= 10) ? mTimeStruct1[r1 + 38 * kz2] : mTimeStruct1[r1 + 38 * 10]; - const float ky211 = mTimeStruct1[r2 + 38 * kz1]; - - const float ky112 = mTimeStruct2[r1 + 38 * kz1]; - const float ky222 = ((r2 <= 37) && (kz2 <= 10)) ? mTimeStruct2[r2 + 38 * kz2] : mTimeStruct2[37 + 38 * 10]; - const float ky122 = (kz2 <= 10) ? mTimeStruct2[r1 + 38 * kz2] : mTimeStruct2[r1 + 38 * 10]; - const float ky212 = mTimeStruct2[r2 + 38 * kz1]; - - // Interpolation in dist-directions, lower drift time map - const float ky11 = (ky211 - ky111) * 10 * dist + ky111 - (ky211 - ky111) * r1; - const float ky21 = (ky221 - ky121) * 10 * dist + ky121 - (ky221 - ky121) * r1; - - // Interpolation in dist-direction, larger drift time map - const float ky12 = (ky212 - ky112) * 10 * dist + ky112 - (ky212 - ky112) * r1; - const float ky22 = (ky222 - ky122) * 10 * dist + ky122 - (ky222 - ky122) * r1; - - // Dist now is the drift distance to the anode wires (negative if electrons are - // between anode wire plane and cathode pad plane) - dist -= TRDGeometry::amThick() * 0.5; - - // Interpolation in z-directions, lower drift time map - const bool condition = ((std::abs(dist) > 0.005) || (z > 0.005)); - const float ktdrift1 = - condition ? (ky21 - ky11) * 100 * z / 2.5 + ky11 - (ky21 - ky11) * kz1 : 0.0; - // Interpolation in z-directions, larger drift time map - const float ktdrift2 = - condition ? (ky22 - ky12) * 100 * z / 2.5 + ky12 - (ky22 - ky12) * kz1 : 0.0; - - // Interpolation between the values at fVDlo and fVDhi - const float a = (ktdrift2 - ktdrift1) * mInvBinWidth; // 1./(mVDhi - mVDlo); - const float b = ktdrift2 - a * mVDhi; - const float t = a * vdrift + b; - - return t; -} - -//_____________________________________________________________________________ -bool TRDDiffusionAndTimeStructEstimator::GetDiffCoeff(float& dl, float& dt, float vdrift) -{ - // - // Calculates the diffusion coefficients in longitudinal <dl> and - // transverse <dt> direction for a given drift velocity <vdrift> - // - - // Nothing to do - if (std::abs(mDiffLastVdrift - vdrift) < 0.001) { - dl = mDiffusionL; - dt = mDiffusionT; - return true; - } - mDiffLastVdrift = vdrift; - - if (TRDCommonParam::Instance()->IsXenon()) { - // - // Vd and B-field dependent diffusion and Lorentz angle - // - - // kNb = kNb = kNb - constexpr int kNb = 5; - - // If looking at compatibility with AliRoot: - // ibL and ibT are calculated the same way so, just define ib = ibL = ibT - int ib = ((int)(10 * (TRDCommonParam::Instance()->GetCachedField() - 0.15))); - ib = std::max(0, ib); - ib = std::min(kNb - 1, ib); - - // DiffusionT - constexpr float p0T[kNb] = {0.009550, 0.009599, 0.009674, 0.009757, 0.009850}; - constexpr float p1T[kNb] = {0.006667, 0.006539, 0.006359, 0.006153, 0.005925}; - constexpr float p2T[kNb] = {-0.000853, -0.000798, -0.000721, -0.000635, -0.000541}; - constexpr float p3T[kNb] = {0.000131, 0.000122, 0.000111, 0.000098, 0.000085}; - // DiffusionL - constexpr float p0L[kNb] = {0.007440, 0.007493, 0.007513, 0.007672, 0.007831}; - constexpr float p1L[kNb] = {0.019252, 0.018912, 0.018636, 0.018012, 0.017343}; - constexpr float p2L[kNb] = {-0.005042, -0.004926, -0.004867, -0.004650, -0.004424}; - constexpr float p3L[kNb] = {0.000195, 0.000189, 0.000195, 0.000182, 0.000169}; - - const float v2 = vdrift * vdrift; - const float v3 = v2 * vdrift; - mDiffusionL = p0L[ib] + p1L[ib] * vdrift + p2L[ib] * v2 + p3L[ib] * v3; - mDiffusionT = p0T[ib] + p1T[ib] * vdrift + p2T[ib] * v2 + p3T[ib] * v3; - - dl = mDiffusionL; - dt = mDiffusionT; - return true; - } else if (TRDCommonParam::Instance()->IsArgon()) { - // - // Diffusion constants and Lorentz angle only for B = 0.5T - // - mDiffusionL = 0.0182; - mDiffusionT = 0.0159; - dl = mDiffusionL; - dt = mDiffusionT; - return true; - } else { - return false; - } -} - -} // namespace o2::trd \ No newline at end of file diff --git a/Detectors/TRD/base/src/TRDGeometry.cxx b/Detectors/TRD/base/src/TRDGeometry.cxx deleted file mode 100644 index 017b720437362..0000000000000 --- a/Detectors/TRD/base/src/TRDGeometry.cxx +++ /dev/null @@ -1,2769 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <TGeoManager.h> -#include <TGeoPhysicalNode.h> -#include <TMath.h> -#include <TVirtualMC.h> -#include <FairLogger.h> - -#include "DetectorsBase/GeometryManager.h" -#include "MathUtils/Utils.h" // utils::bit2Mask -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDPadPlane.h" - -using namespace o2::trd; - -//_____________________________________________________________________________ - -const o2::detectors::DetID TRDGeometry::sDetID(o2::detectors::DetID::TRD); - -//_____________________________________________________________________________ -TRDGeometry::TRDGeometry() : TRDGeometryBase(), o2::detectors::DetMatrixCacheIndirect(sDetID) -{ - createPadPlaneArray(); -} - -//_____________________________________________________________________________ -bool TRDGeometry::rotateBack(int det, const float* const loc, float* glb) const -{ - // - // Rotates a chambers to transform the corresponding local frame - // coordinates <loc> into the coordinates of the ALICE restframe <glb>. - // - - int sector = getSector(det); - float phi = 2.0 * TMath::Pi() / (float)kNsector * ((float)sector + 0.5); - - glb[0] = loc[0] * TMath::Cos(phi) - loc[1] * TMath::Sin(phi); - glb[1] = loc[0] * TMath::Sin(phi) + loc[1] * TMath::Cos(phi); - glb[2] = loc[2]; - - return true; -} - -//_____________________________________________________________________________ -void TRDGeometry::createPadPlaneArray() -{ - // - // Creates the array of TRDPadPlane objects - // - - for (int ilayer = 0; ilayer < kNlayer; ilayer++) { - for (int istack = 0; istack < kNstack; istack++) { - createPadPlane(ilayer, istack); - } - } -} - -//_____________________________________________________________________________ -void TRDGeometry::createPadPlane(int ilayer, int istack) -{ - // - // Creates an TRDPadPlane object - // - int ipp = getDetectorSec(ilayer, istack); - auto& padPlane = mPadPlanes[ipp]; - - padPlane.setLayer(ilayer); - padPlane.setStack(istack); - - padPlane.setRowSpacing(0.0); - padPlane.setColSpacing(0.0); - - padPlane.setLengthRim(1.0); - padPlane.setWidthRim(0.5); - - padPlane.setNcols(144); - - padPlane.setAnodeWireOffset(0.25); - - // - // The pad plane parameter - // - const float kTiltAngle = 2.0; - switch (ilayer) { - case 0: - if (istack == 2) { - // L0C0 type - padPlane.setNrows(12); - padPlane.setLength(108.0); - padPlane.setLengthOPad(8.0); - padPlane.setLengthIPad(9.0); - } else { - // L0C1 type - padPlane.setNrows(16); - padPlane.setLength(122.0); - padPlane.setLengthOPad(7.5); - padPlane.setLengthIPad(7.5); - } - padPlane.setWidth(92.2); - padPlane.setWidthOPad(0.515); - padPlane.setWidthIPad(0.635); - padPlane.setTiltingAngle(-kTiltAngle); - break; - case 1: - if (istack == 2) { - // L1C0 type - padPlane.setNrows(12); - padPlane.setLength(108.0); - padPlane.setLengthOPad(8.0); - padPlane.setLengthIPad(9.0); - } else { - // L1C1 type - padPlane.setNrows(16); - padPlane.setLength(122.0); - padPlane.setLengthOPad(7.5); - padPlane.setLengthIPad(7.5); - } - padPlane.setWidth(96.6); - padPlane.setWidthOPad(0.585); - padPlane.setWidthIPad(0.665); - padPlane.setTiltingAngle(kTiltAngle); - break; - case 2: - if (istack == 2) { - // L2C0 type - padPlane.setNrows(12); - padPlane.setLength(108.0); - padPlane.setLengthOPad(8.0); - padPlane.setLengthIPad(9.0); - } else { - // L2C1 type - padPlane.setNrows(16); - padPlane.setLength(129.0); - padPlane.setLengthOPad(7.5); - padPlane.setLengthIPad(8.0); - } - padPlane.setWidth(101.1); - padPlane.setWidthOPad(0.705); - padPlane.setWidthIPad(0.695); - padPlane.setTiltingAngle(-kTiltAngle); - break; - case 3: - if (istack == 2) { - // L3C0 type - padPlane.setNrows(12); - padPlane.setLength(108.0); - padPlane.setLengthOPad(8.0); - padPlane.setLengthIPad(9.0); - } else { - // L3C1 type - padPlane.setNrows(16); - padPlane.setLength(136.0); - padPlane.setLengthOPad(7.5); - padPlane.setLengthIPad(8.5); - } - padPlane.setWidth(105.5); - padPlane.setWidthOPad(0.775); - padPlane.setWidthIPad(0.725); - padPlane.setTiltingAngle(kTiltAngle); - break; - case 4: - if (istack == 2) { - // L4C0 type - padPlane.setNrows(12); - padPlane.setLength(108.0); - padPlane.setLengthOPad(8.0); - } else { - // L4C1 type - padPlane.setNrows(16); - padPlane.setLength(143.0); - padPlane.setLengthOPad(7.5); - } - padPlane.setWidth(109.9); - padPlane.setWidthOPad(0.845); - padPlane.setLengthIPad(9.0); - padPlane.setWidthIPad(0.755); - padPlane.setTiltingAngle(-kTiltAngle); - break; - case 5: - if (istack == 2) { - // L5C0 type - padPlane.setNrows(12); - padPlane.setLength(108.0); - padPlane.setLengthOPad(8.0); - } else { - // L5C1 type - padPlane.setNrows(16); - padPlane.setLength(145.0); - padPlane.setLengthOPad(8.5); - } - padPlane.setWidth(114.4); - padPlane.setWidthOPad(0.965); - padPlane.setLengthIPad(9.0); - padPlane.setWidthIPad(0.785); - padPlane.setTiltingAngle(kTiltAngle); - break; - }; - - // - // The positions of the borders of the pads - // - // Row direction - // - float row = CLENGTH[ilayer][istack] / 2.0 - RPADW - padPlane.getLengthRim(); - for (int ir = 0; ir < padPlane.getNrows(); ir++) { - padPlane.setPadRow(ir, row); - row -= padPlane.getRowSpacing(); - if (ir == 0) { - row -= padPlane.getLengthOPad(); - } else { - row -= padPlane.getLengthIPad(); - } - } - // - // Column direction - // - float col = -CWIDTH[ilayer] / 2.0 - CROW + padPlane.getWidthRim(); - for (int ic = 0; ic < padPlane.getNcols(); ic++) { - padPlane.setPadCol(ic, col); - col += padPlane.getColSpacing(); - if (ic == 0) { - col += padPlane.getWidthOPad(); - } else { - col += padPlane.getWidthIPad(); - } - } - // Calculate the offset to translate from the local ROC system into - // the local supermodule system, which is used for clusters - float rowTmp = CLENGTH[ilayer][0] + CLENGTH[ilayer][1] + CLENGTH[ilayer][2] / 2.0; - for (int jstack = 0; jstack < istack; jstack++) { - rowTmp -= CLENGTH[ilayer][jstack]; - } - padPlane.setPadRowSMOffset(rowTmp - CLENGTH[ilayer][istack] / 2.0); -} - -void TRDGeometry::createVolume(const char* name, const char* shape, int nmed, float* upar, int np) -{ - TVirtualMC::GetMC()->Gsvolu(name, shape, nmed, upar, np); - - // add to sensitive volumes for TRD if matching criterion - // these are coded with J+K in the second character (according to AliTRDv1.cxx of AliROOT) - if (name[1] == 'J' || name[1] == 'K') { - mSensitiveVolumeNames.emplace_back(name); - } -} - -//_____________________________________________________________________________ -void TRDGeometry::createGeometry(std::vector<int> const& idtmed) -{ - // - // Create the TRD geometry - - if (!gGeoManager) { - // RSTODO: in future there will be a method to load matrices from the CDB - LOG(FATAL) << "Geometry is not loaded"; - } - - createVolumes(idtmed); -} - -void TRDGeometry::createVolumes(std::vector<int> const& idtmed) -{ - // - // Create the TRD geometry volumes - // - // - // Names of the TRD volumina (xx = detector number): - // - // Volume (Air) wrapping the readout chamber components - // UTxx includes: UAxx, UDxx, UFxx, UUxx - // - // Lower part of the readout chambers (drift volume + radiator) - // UAxx Aluminum frames (Al) - // - // Upper part of the readout chambers (readout plane + fee) - // UDxx Wacosit frames of amp. region (Wacosit) - // UFxx Aluminum frame of back panel (Al) - // - // Services on chambers (cooling, cables, MCMs, DCS boards, ...) - // UUxx Volume containing the services (Air) - // - // Material layers inside sensitive area: - // Name Description Mat. Thick. Dens. Radl. X/X_0 - // - // URMYxx Mylar layers (x2) Mylar 0.0015 1.39 28.5464 0.005% - // URCBxx Carbon layer (x2) Carbon 0.0055 1.75 24.2824 0.023% - // URGLxx Glue on the carbon layers (x2) Araldite 0.0065 1.12 37.0664 0.018% - // URRHxx Rohacell layer (x2) Rohacell 0.8 0.075 536.005 0.149% - // URFBxx Fiber mat layer PP 3.186 0.068 649.727 0.490% - // - // UJxx Drift region Xe/CO2 3.0 0.00495 1792.37 0.167% - // UKxx Amplification region Xe/CO2 0.7 0.00495 1792.37 0.039% - // UWxx Wire planes (x2) Copper 0.00011 8.96 1.43503 0.008% - // - // UPPDxx Copper of pad plane Copper 0.0025 8.96 1.43503 0.174% - // UPPPxx PCB of pad plane G10 0.0356 2.0 14.9013 0.239% - // UPGLxx Glue on pad planes Araldite 0.0923 1.12 37.0664 0.249% - // + add. glue (ca. 600g) Araldite 0.0505 1.12 37.0663 0.107% - // UPCBxx Carbon fiber mats (x2) Carbon 0.019 1.75 24.2824 0.078% - // UPHCxx Honeycomb structure Aramide 2.0299 0.032 1198.84 0.169% - // UPPCxx PCB of readout board G10 0.0486 2.0 14.9013 0.326% - // UPRDxx Copper of readout board Copper 0.0057 8.96 1.43503 0.404% - // UPELxx Electronics + cables Copper 0.0029 8.96 1.43503 0.202% - // - - const int kNparTrd = 4; - const int kNparCha = 3; - - float xpos; - float ypos; - float zpos; - - float parTrd[kNparTrd]; - float parCha[kNparCha]; - - const int kTag = 100; - char cTagV[kTag]; - char cTagM[kTag]; - - // There are three TRD volumes for the supermodules in order to accomodate - // the different arrangements in front of PHOS - // UTR1: Default supermodule - // UTR2: Supermodule in front of PHOS with double carbon cover - // UTR3: As UTR2, but w/o middle stack - // UTR4: Sector 17 with missing chamber L4S4 - // - // The mother volume for one sector (Air), full length in z-direction - // Provides material for side plates of super module - parTrd[0] = SWIDTH1 / 2.0; - parTrd[1] = SWIDTH2 / 2.0; - parTrd[2] = SLENGTH / 2.0; - parTrd[3] = SHEIGHT / 2.0; - createVolume("UTR1", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTR2", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTR3", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTR4", "TRD1", idtmed[2], parTrd, kNparTrd); - // The outer aluminum plates of the super module (Al) - parTrd[0] = SWIDTH1 / 2.0; - parTrd[1] = SWIDTH2 / 2.0; - parTrd[2] = SLENGTH / 2.0; - parTrd[3] = SHEIGHT / 2.0; - createVolume("UTS1", "TRD1", idtmed[1], parTrd, kNparTrd); - createVolume("UTS2", "TRD1", idtmed[1], parTrd, kNparTrd); - createVolume("UTS3", "TRD1", idtmed[1], parTrd, kNparTrd); - createVolume("UTS4", "TRD1", idtmed[1], parTrd, kNparTrd); - // The inner part of the TRD mother volume for one sector (Air), - // full length in z-direction - parTrd[0] = SWIDTH1 / 2.0 - SMPLTT; - parTrd[1] = SWIDTH2 / 2.0 - SMPLTT; - parTrd[2] = SLENGTH / 2.0; - parTrd[3] = SHEIGHT / 2.0 - SMPLTT; - createVolume("UTI1", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTI2", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTI3", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTI4", "TRD1", idtmed[2], parTrd, kNparTrd); - - // The inner part of the TRD mother volume for services in front - // of the supermodules (Air), - parTrd[0] = SWIDTH1 / 2.0; - parTrd[1] = SWIDTH2 / 2.0; - parTrd[2] = FLENGTH / 2.0; - parTrd[3] = SHEIGHT / 2.0; - createVolume("UTF1", "TRD1", idtmed[2], parTrd, kNparTrd); - createVolume("UTF2", "TRD1", idtmed[2], parTrd, kNparTrd); - - for (int istack = 0; istack < kNstack; istack++) { - for (int ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - - // The lower part of the readout chambers (drift volume + radiator) - // The aluminum frames - snprintf(cTagV, kTag, "UA%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; - parCha[2] = CRAH / 2.0 + CDRH / 2.0; - createVolume(cTagV, "BOX ", idtmed[1], parCha, kNparCha); - // The additional aluminum on the frames - // This part has not the correct shape but is just supposed to - // represent the missing material. The correct form of the L-shaped - // profile would not fit into the alignable volume. - snprintf(cTagV, kTag, "UZ%02d", iDet); - parCha[0] = CALWMOD / 2.0; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; - parCha[2] = CALHMOD / 2.0; - createVolume(cTagV, "BOX ", idtmed[1], parCha, kNparCha); - // The additional Wacosit on the frames - snprintf(cTagV, kTag, "UP%02d", iDet); - parCha[0] = CWSW / 2.0; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; - parCha[2] = CWSH / 2.0; - createVolume(cTagV, "BOX ", idtmed[7], parCha, kNparCha); - // The Wacosit frames - snprintf(cTagV, kTag, "UB%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 - CALT; - parCha[1] = -1.0; - parCha[2] = -1.0; - createVolume(cTagV, "BOX ", idtmed[7], parCha, kNparCha); - // The glue around the radiator - snprintf(cTagV, kTag, "UX%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 - CALT - CCLST; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCLFT; - parCha[2] = CRAH / 2.0; - createVolume(cTagV, "BOX ", idtmed[11], parCha, kNparCha); - // The inner part of radiator (air) - snprintf(cTagV, kTag, "UC%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 - CALT - CCLST - CGLT; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCLFT - CGLT; - parCha[2] = -1.0; - createVolume(cTagV, "BOX ", idtmed[2], parCha, kNparCha); - - // The upper part of the readout chambers (amplification volume) - // The Wacosit frames - snprintf(cTagV, kTag, "UD%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 + CROW; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; - parCha[2] = CAMH / 2.0; - createVolume(cTagV, "BOX ", idtmed[7], parCha, kNparCha); - // The inner part of the Wacosit frame (air) - snprintf(cTagV, kTag, "UE%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 + CROW - CCUTB; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCUTA; - parCha[2] = -1.; - createVolume(cTagV, "BOX ", idtmed[2], parCha, kNparCha); - - // The back panel, including pad plane and readout boards - // The aluminum frames - snprintf(cTagV, kTag, "UF%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 + CROW; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; - parCha[2] = CROH / 2.0; - createVolume(cTagV, "BOX ", idtmed[1], parCha, kNparCha); - // The inner part of the aluminum frames - snprintf(cTagV, kTag, "UG%02d", iDet); - parCha[0] = CWIDTH[ilayer] / 2.0 + CROW - CAUT; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CAUT; - parCha[2] = -1.0; - createVolume(cTagV, "BOX ", idtmed[2], parCha, kNparCha); - - // - // The material layers inside the chambers - // - - // Mylar layer (radiator) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = RMYTHICK / 2.0; - snprintf(cTagV, kTag, "URMY%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[27], parCha, kNparCha); - // Carbon layer (radiator) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = RCBTHICK / 2.0; - snprintf(cTagV, kTag, "URCB%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[26], parCha, kNparCha); - // Araldite layer (radiator) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = RGLTHICK / 2.0; - snprintf(cTagV, kTag, "URGL%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[11], parCha, kNparCha); - // Rohacell layer (radiator) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = RRHTHICK / 2.0; - snprintf(cTagV, kTag, "URRH%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[15], parCha, kNparCha); - // Fiber layer (radiator) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = RFBTHICK / 2.0; - snprintf(cTagV, kTag, "URFB%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[28], parCha, kNparCha); - - // Xe/Isobutane layer (drift volume) - parCha[0] = CWIDTH[ilayer] / 2.0 - CALT - CCLST; - parCha[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0 - CCLFT; - parCha[2] = DRTHICK / 2.0; - snprintf(cTagV, kTag, "UJ%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[9], parCha, kNparCha); - - // Xe/Isobutane layer (amplification volume) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = AMTHICK / 2.0; - snprintf(cTagV, kTag, "UK%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[9], parCha, kNparCha); - // Cu layer (wire plane) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = WRTHICK / 2.0; - snprintf(cTagV, kTag, "UW%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[3], parCha, kNparCha); - - // Cu layer (pad plane) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PPDTHICK / 2.0; - snprintf(cTagV, kTag, "UPPD%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[5], parCha, kNparCha); - // G10 layer (pad plane) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PPPTHICK / 2.0; - snprintf(cTagV, kTag, "UPPP%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[13], parCha, kNparCha); - // Araldite layer (glue) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PGLTHICK / 2.0; - snprintf(cTagV, kTag, "UPGL%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[11], parCha, kNparCha); - // Carbon layer (carbon fiber mats) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PCBTHICK / 2.0; - snprintf(cTagV, kTag, "UPCB%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[26], parCha, kNparCha); - // Aramide layer (honeycomb) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PHCTHICK / 2.0; - snprintf(cTagV, kTag, "UPHC%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[10], parCha, kNparCha); - // G10 layer (PCB readout board) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PPCTHICK / 2; - snprintf(cTagV, kTag, "UPPC%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[13], parCha, kNparCha); - // Cu layer (traces in readout board) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PRBTHICK / 2.0; - snprintf(cTagV, kTag, "UPRB%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[6], parCha, kNparCha); - // Cu layer (other material on in readout board, incl. screws) - parCha[0] = -1.0; - parCha[1] = -1.0; - parCha[2] = PELTHICK / 2.0; - snprintf(cTagV, kTag, "UPEL%02d", iDet); - createVolume(cTagV, "BOX ", idtmed[4], parCha, kNparCha); - - // - // Position the layers in the chambers - // - xpos = 0.0; - ypos = 0.0; - - // Lower part - // Mylar layers (radiator) - zpos = RMYTHICK / 2.0 - CRAH / 2.0; - snprintf(cTagV, kTag, "URMY%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - zpos = -RMYTHICK / 2.0 + CRAH / 2.0; - snprintf(cTagV, kTag, "URMY%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Carbon layers (radiator) - zpos = RCBTHICK / 2.0 + RMYTHICK - CRAH / 2.0; - snprintf(cTagV, kTag, "URCB%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - zpos = -RCBTHICK / 2.0 - RMYTHICK + CRAH / 2.0; - snprintf(cTagV, kTag, "URCB%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Carbon layers (radiator) - zpos = RGLTHICK / 2.0 + RCBTHICK + RMYTHICK - CRAH / 2.0; - snprintf(cTagV, kTag, "URGL%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - zpos = -RGLTHICK / 2.0 - RCBTHICK - RMYTHICK + CRAH / 2.0; - snprintf(cTagV, kTag, "URGL%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Rohacell layers (radiator) - zpos = RRHTHICK / 2.0 + RGLTHICK + RCBTHICK + RMYTHICK - CRAH / 2.0; - snprintf(cTagV, kTag, "URRH%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - zpos = -RRHTHICK / 2.0 - RGLTHICK - RCBTHICK - RMYTHICK + CRAH / 2.0; - snprintf(cTagV, kTag, "URRH%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Fiber layers (radiator) - zpos = 0.0; - snprintf(cTagV, kTag, "URFB%02d", iDet); - snprintf(cTagM, kTag, "UC%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - // Xe/Isobutane layer (drift volume) - zpos = DRZPOS; - snprintf(cTagV, kTag, "UJ%02d", iDet); - snprintf(cTagM, kTag, "UB%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - // Upper part - // Xe/Isobutane layer (amplification volume) - zpos = AMZPOS; - snprintf(cTagV, kTag, "UK%02d", iDet); - snprintf(cTagM, kTag, "UE%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Cu layer (wire planes inside amplification volume) - zpos = WRZPOSA; - snprintf(cTagV, kTag, "UW%02d", iDet); - snprintf(cTagM, kTag, "UK%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - zpos = WRZPOSB; - snprintf(cTagV, kTag, "UW%02d", iDet); - snprintf(cTagM, kTag, "UK%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - // Back panel + pad plane + readout part - // Cu layer (pad plane) - zpos = PPDTHICK / 2.0 - CROH / 2.0; - snprintf(cTagV, kTag, "UPPD%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // G10 layer (pad plane) - zpos = PPPTHICK / 2.0 + PPDTHICK - CROH / 2.0; - snprintf(cTagV, kTag, "UPPP%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Araldite layer (glue) - zpos = PGLTHICK / 2.0 + PPPTHICK + PPDTHICK - CROH / 2.0; - snprintf(cTagV, kTag, "UPGL%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Carbon layers (carbon fiber mats) - zpos = PCBTHICK / 2.0 + PGLTHICK + PPPTHICK + PPDTHICK - CROH / 2.0; - snprintf(cTagV, kTag, "UPCB%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - zpos = -PCBTHICK / 2.0 - PPCTHICK - PRBTHICK - PELTHICK + CROH / 2.0; - snprintf(cTagV, kTag, "UPCB%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Aramide layer (honeycomb) - zpos = PHCTHICK / 2.0 + PCBTHICK + PGLTHICK + PPPTHICK + PPDTHICK - CROH / 2.0; - snprintf(cTagV, kTag, "UPHC%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // G10 layer (PCB readout board) - zpos = -PPCTHICK / 2.0 - PRBTHICK - PELTHICK + CROH / 2.0; - snprintf(cTagV, kTag, "UPPC%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Cu layer (traces in readout board) - zpos = -PRBTHICK / 2.0 - PELTHICK + CROH / 2.0; - snprintf(cTagV, kTag, "UPRB%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // Cu layer (other materials on readout board, incl. screws) - zpos = -PELTHICK / 2.0 + CROH / 2.0; - snprintf(cTagV, kTag, "UPEL%02d", iDet); - snprintf(cTagM, kTag, "UG%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - // Position the inner volumes of the chambers in the frames - xpos = 0.0; - ypos = 0.0; - - // The inner part of the radiator (air) - zpos = 0.0; - snprintf(cTagV, kTag, "UC%02d", iDet); - snprintf(cTagM, kTag, "UX%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // The glue around the radiator - zpos = CRAH / 2.0 - CDRH / 2.0 - CRAH / 2.0; - snprintf(cTagV, kTag, "UX%02d", iDet); - snprintf(cTagM, kTag, "UB%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - // The lower Wacosit frame inside the aluminum frame - zpos = 0.0; - snprintf(cTagV, kTag, "UB%02d", iDet); - snprintf(cTagM, kTag, "UA%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - // The inside of the upper Wacosit frame - zpos = 0.0; - snprintf(cTagV, kTag, "UE%02d", iDet); - snprintf(cTagM, kTag, "UD%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - // The inside of the upper aluminum frame - zpos = 0.0; - snprintf(cTagV, kTag, "UG%02d", iDet); - snprintf(cTagM, kTag, "UF%02d", iDet); - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - } - } - - // Create the volumes of the super module frame - createFrame(idtmed); - - // Create the volumes of the services - createServices(idtmed); - - for (int istack = 0; istack < kNstack; istack++) { - for (int ilayer = 0; ilayer < kNlayer; ilayer++) { - assembleChamber(ilayer, istack); - } - } - - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTI1", 1, "UTS1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTI2", 1, "UTS2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTI3", 1, "UTS3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTI4", 1, "UTS4", xpos, ypos, zpos, 0, "ONLY"); - - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTS1", 1, "UTR1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTS2", 1, "UTR2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTS3", 1, "UTR3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTS4", 1, "UTR4", xpos, ypos, zpos, 0, "ONLY"); - - // Put the TRD volumes into the space frame mother volumes - // if enabled via status flag - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - for (int isector = 0; isector < kNsector; isector++) { - if (getSMstatus(isector)) { - snprintf(cTagV, kTag, "BTRD%d", isector); - switch (isector) { - case 17: - // Missing L4S4 chamber - TVirtualMC::GetMC()->Gspos("UTR4", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); - break; - case 13: - case 14: - case 15: - // Double carbon, w/o middle stack - TVirtualMC::GetMC()->Gspos("UTR3", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); - break; - case 11: - case 12: - // Double carbon, all stacks - TVirtualMC::GetMC()->Gspos("UTR2", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); - break; - default: - // Standard supermodule - TVirtualMC::GetMC()->Gspos("UTR1", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); - }; - } - } - - // Put the TRD volumes into the space frame mother volumes - // if enabled via status flag - xpos = 0.0; - ypos = 0.5 * SLENGTH + 0.5 * FLENGTH; - zpos = 0.0; - for (int isector = 0; isector < kNsector; isector++) { - if (getSMstatus(isector)) { - snprintf(cTagV, kTag, "BTRD%d", isector); - TVirtualMC::GetMC()->Gspos("UTF1", 1, cTagV, xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTF2", 1, cTagV, xpos, -ypos, zpos, 0, "ONLY"); - } - } - - // Resolve runtime shapes (which is done as part of TGeoManager::CheckGeometry) NOW. - // This is otherwise done when saying gGeoManager->CloseGeometry(). - // However, we need to make sure all the TGeoVolumes are correctly available even before this - // stage because FairMCApplication initializes the sensisitive volumes before closing the geometry. - // The true origin of the "problem" comes from the fact, that the TRD construction above uses - // Geant3-like construction routines that allow giving negative parameters, indicating dimensions to be - // fixed later. This prevents immediate construction of the TGeoVolume. - gGeoManager->CheckGeometry(); -} - -//_____________________________________________________________________________ -void TRDGeometry::createFrame(std::vector<int> const& idtmed) -{ - // - // Create the geometry of the frame of the supermodule - // - // Names of the TRD services volumina - // - // USRL Support rails for the chambers (Al) - // USxx Support cross bars between the chambers (Al) - // USHx Horizontal connection between the cross bars (Al) - // USLx Long corner ledges (Al) - // - - int ilayer = 0; - - float xpos = 0.0; - float ypos = 0.0; - float zpos = 0.0; - - const int kTag = 100; - char cTagV[kTag]; - char cTagM[kTag]; - - const int kNparTRD = 4; - float parTRD[kNparTRD]; - const int kNparBOX = 3; - float parBOX[kNparBOX]; - const int kNparTRP = 11; - float parTRP[kNparTRP]; - - // The rotation matrices - const int kNmatrix = 7; - int matrix[kNmatrix]; - TVirtualMC::GetMC()->Matrix(matrix[0], 100.0, 0.0, 90.0, 90.0, 10.0, 0.0); - TVirtualMC::GetMC()->Matrix(matrix[1], 80.0, 0.0, 90.0, 90.0, 10.0, 180.0); - TVirtualMC::GetMC()->Matrix(matrix[2], 90.0, 0.0, 0.0, 0.0, 90.0, 90.0); - TVirtualMC::GetMC()->Matrix(matrix[3], 90.0, 180.0, 0.0, 180.0, 90.0, 90.0); - TVirtualMC::GetMC()->Matrix(matrix[4], 170.0, 0.0, 80.0, 0.0, 90.0, 90.0); - TVirtualMC::GetMC()->Matrix(matrix[5], 170.0, 180.0, 80.0, 180.0, 90.0, 90.0); - TVirtualMC::GetMC()->Matrix(matrix[6], 180.0, 180.0, 90.0, 180.0, 90.0, 90.0); - - // - // The carbon inserts in the top/bottom aluminum plates - // - - const int kNparCrb = 3; - float parCrb[kNparCrb]; - parCrb[0] = 0.0; - parCrb[1] = 0.0; - parCrb[2] = 0.0; - createVolume("USCR", "BOX ", idtmed[26], parCrb, 0); - // Bottom 1 (all sectors) - parCrb[0] = 77.49 / 2.0; - parCrb[1] = 104.60 / 2.0; - parCrb[2] = SMPLTT / 2.0; - xpos = 0.0; - ypos = 0.0; - zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("USCR", 1, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 2, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 3, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 4, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - // Bottom 2 (all sectors) - parCrb[0] = 77.49 / 2.0; - parCrb[1] = 55.80 / 2.0; - parCrb[2] = SMPLTT / 2.0; - xpos = 0.0; - ypos = 85.6; - zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("USCR", 5, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 6, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 7, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 8, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 9, "UTS1", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 10, "UTS2", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 11, "UTS3", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 12, "UTS4", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - // Bottom 3 (all sectors) - parCrb[0] = 77.49 / 2.0; - parCrb[1] = 56.00 / 2.0; - parCrb[2] = SMPLTT / 2.0; - xpos = 0.0; - ypos = 148.5; - zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("USCR", 13, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 14, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 15, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 16, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 17, "UTS1", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 18, "UTS2", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 19, "UTS3", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 20, "UTS4", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - // Bottom 4 (all sectors) - parCrb[0] = 77.49 / 2.0; - parCrb[1] = 118.00 / 2.0; - parCrb[2] = SMPLTT / 2.0; - xpos = 0.0; - ypos = 240.5; - zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("USCR", 21, "UTS1", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 22, "UTS2", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 23, "UTS3", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 24, "UTS4", xpos, ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 25, "UTS1", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 26, "UTS2", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 27, "UTS3", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 28, "UTS4", xpos, -ypos, zpos, 0, "ONLY", parCrb, kNparCrb); - // Top 1 (only in front of PHOS) - parCrb[0] = 111.48 / 2.0; - parCrb[1] = 105.00 / 2.0; - parCrb[2] = SMPLTT / 2.0; - xpos = 0.0; - ypos = 0.0; - zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("USCR", 29, "UTS2", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 30, "UTS3", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); - // Top 2 (only in front of PHOS) - parCrb[0] = 111.48 / 2.0; - parCrb[1] = 56.00 / 2.0; - parCrb[2] = SMPLTT / 2.0; - xpos = 0.0; - ypos = 85.5; - zpos = SMPLTT / 2.0 - SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("USCR", 31, "UTS2", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 32, "UTS3", xpos, ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 33, "UTS2", xpos, -ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); - TVirtualMC::GetMC()->Gsposp("USCR", 34, "UTS3", xpos, -ypos, -zpos, 0, "ONLY", parCrb, kNparCrb); - - // - // The chamber support rails - // - - const float kSRLhgt = 2.00; - const float kSRLwidA = 2.3; - const float kSRLwidB = 1.947; - const float kSRLdst = 1.135; - const int kNparSRL = 11; - float parSRL[kNparSRL]; - // Trapezoidal shape - parSRL[0] = SLENGTH / 2.0; - parSRL[1] = 0.0; - parSRL[2] = 0.0; - parSRL[3] = kSRLhgt / 2.0; - parSRL[4] = kSRLwidB / 2.0; - parSRL[5] = kSRLwidA / 2.0; - parSRL[6] = 5.0; - parSRL[7] = kSRLhgt / 2.0; - parSRL[8] = kSRLwidB / 2.0; - parSRL[9] = kSRLwidA / 2.0; - parSRL[10] = 5.0; - createVolume("USRL", "TRAP", idtmed[1], parSRL, kNparSRL); - - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - xpos = CWIDTH[ilayer] / 2.0 + kSRLwidA / 2.0 + kSRLdst; - ypos = 0.0; - zpos = VROCSM + SMPLTT - CALZPOS - SHEIGHT / 2.0 + CRAH + CDRH - CALH - kSRLhgt / 2.0 + - ilayer * (CH + VSPACE); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + kNlayer, "UTI1", -xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 2 * kNlayer, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 3 * kNlayer, "UTI2", -xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 4 * kNlayer, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 5 * kNlayer, "UTI3", -xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 6 * kNlayer, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USRL", ilayer + 1 + 7 * kNlayer, "UTI4", -xpos, ypos, zpos, matrix[3], "ONLY"); - } - - // - // The cross bars between the chambers - // - - const float kSCBwid = 1.0; - const float kSCBthk = 2.0; - const float kSCHhgt = 0.3; - - const int kNparSCB = 3; - float parSCB[kNparSCB]; - parSCB[1] = kSCBwid / 2.0; - parSCB[2] = CH / 2.0 + VSPACE / 2.0 - kSCHhgt; - - const int kNparSCI = 3; - float parSCI[kNparSCI]; - parSCI[1] = -1; - - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - // The aluminum of the cross bars - parSCB[0] = CWIDTH[ilayer] / 2.0 + kSRLdst / 2.0; - snprintf(cTagV, kTag, "USF%01d", ilayer); - createVolume(cTagV, "BOX ", idtmed[1], parSCB, kNparSCB); - - // The empty regions in the cross bars - float thkSCB = kSCBthk; - if (ilayer < 2) { - thkSCB *= 1.5; - } - parSCI[2] = parSCB[2] - thkSCB; - parSCI[0] = parSCB[0] / 4.0 - kSCBthk; - snprintf(cTagV, kTag, "USI%01d", ilayer); - createVolume(cTagV, "BOX ", idtmed[2], parSCI, kNparSCI); - - snprintf(cTagV, kTag, "USI%01d", ilayer); - snprintf(cTagM, kTag, "USF%01d", ilayer); - ypos = 0.0; - zpos = 0.0; - xpos = parSCI[0] + thkSCB / 2.0; - TVirtualMC::GetMC()->Gspos(cTagV, 1, cTagM, xpos, ypos, zpos, 0, "ONLY"); - xpos = -parSCI[0] - thkSCB / 2.0; - TVirtualMC::GetMC()->Gspos(cTagV, 2, cTagM, xpos, ypos, zpos, 0, "ONLY"); - xpos = 3.0 * parSCI[0] + 1.5 * thkSCB; - TVirtualMC::GetMC()->Gspos(cTagV, 3, cTagM, xpos, ypos, zpos, 0, "ONLY"); - xpos = -3.0 * parSCI[0] - 1.5 * thkSCB; - TVirtualMC::GetMC()->Gspos(cTagV, 4, cTagM, xpos, ypos, zpos, 0, "ONLY"); - - snprintf(cTagV, kTag, "USF%01d", ilayer); - xpos = 0.0; - zpos = VROCSM + SMPLTT + parSCB[2] - SHEIGHT / 2.0 + ilayer * (CH + VSPACE); - - ypos = CLENGTH[ilayer][2] / 2.0 + CLENGTH[ilayer][1]; - TVirtualMC::GetMC()->Gspos(cTagV, 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - - ypos = -CLENGTH[ilayer][2] / 2.0 - CLENGTH[ilayer][1]; - TVirtualMC::GetMC()->Gspos(cTagV, 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - } - - // - // The horizontal connections between the cross bars - // - - const int kNparSCH = 3; - float parSCH[kNparSCH]; - - for (ilayer = 1; ilayer < kNlayer - 1; ilayer++) { - parSCH[0] = CWIDTH[ilayer] / 2.0; - parSCH[1] = (CLENGTH[ilayer + 1][2] / 2.0 + CLENGTH[ilayer + 1][1] - CLENGTH[ilayer][2] / 2.0 - - CLENGTH[ilayer][1]) / - 2.0; - parSCH[2] = kSCHhgt / 2.0; - - snprintf(cTagV, kTag, "USH%01d", ilayer); - createVolume(cTagV, "BOX ", idtmed[1], parSCH, kNparSCH); - xpos = 0.0; - ypos = CLENGTH[ilayer][2] / 2.0 + CLENGTH[ilayer][1] + parSCH[1]; - zpos = VROCSM + SMPLTT - kSCHhgt / 2.0 - SHEIGHT / 2.0 + (ilayer + 1) * (CH + VSPACE); - TVirtualMC::GetMC()->Gspos(cTagV, 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - ypos = -ypos; - TVirtualMC::GetMC()->Gspos(cTagV, 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos(cTagV, 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - } - - // - // The asymmetric flat frame in the middle - // - - // The envelope volume (aluminum) - parTRD[0] = 87.60 / 2.0; - parTRD[1] = 114.00 / 2.0; - parTRD[2] = 1.20 / 2.0; - parTRD[3] = 71.30 / 2.0; - createVolume("USDB", "TRD1", idtmed[1], parTRD, kNparTRD); - // Empty spaces (air) - parTRP[0] = 1.20 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 27.00 / 2.0; - parTRP[4] = 50.60 / 2.0; - parTRP[5] = 5.00 / 2.0; - parTRP[6] = 3.5; - parTRP[7] = 27.00 / 2.0; - parTRP[8] = 50.60 / 2.0; - parTRP[9] = 5.00 / 2.0; - parTRP[10] = 3.5; - createVolume("USD1", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = 18.0; - ypos = 0.0; - zpos = 27.00 / 2.0 - 71.3 / 2.0; - TVirtualMC::GetMC()->Gspos("USD1", 1, "USDB", xpos, ypos, zpos, matrix[2], "ONLY"); - // Empty spaces (air) - parTRP[0] = 1.20 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 33.00 / 2.0; - parTRP[4] = 5.00 / 2.0; - parTRP[5] = 62.10 / 2.0; - parTRP[6] = 3.5; - parTRP[7] = 33.00 / 2.0; - parTRP[8] = 5.00 / 2.0; - parTRP[9] = 62.10 / 2.0; - parTRP[10] = 3.5; - createVolume("USD2", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = 21.0; - ypos = 0.0; - zpos = 71.3 / 2.0 - 33.0 / 2.0; - TVirtualMC::GetMC()->Gspos("USD2", 1, "USDB", xpos, ypos, zpos, matrix[2], "ONLY"); - // Empty spaces (air) - parBOX[0] = 22.50 / 2.0; - parBOX[1] = 1.20 / 2.0; - parBOX[2] = 70.50 / 2.0; - createVolume("USD3", "BOX ", idtmed[2], parBOX, kNparBOX); - xpos = -25.75; - ypos = 0.0; - zpos = 0.4; - TVirtualMC::GetMC()->Gspos("USD3", 1, "USDB", xpos, ypos, zpos, 0, "ONLY"); - // Empty spaces (air) - parTRP[0] = 1.20 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 25.50 / 2.0; - parTRP[4] = 5.00 / 2.0; - parTRP[5] = 65.00 / 2.0; - parTRP[6] = -1.0; - parTRP[7] = 25.50 / 2.0; - parTRP[8] = 5.00 / 2.0; - parTRP[9] = 65.00 / 2.0; - parTRP[10] = -1.0; - createVolume("USD4", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = 2.0; - ypos = 0.0; - zpos = -1.6; - TVirtualMC::GetMC()->Gspos("USD4", 1, "USDB", xpos, ypos, zpos, matrix[6], "ONLY"); - // Empty spaces (air) - parTRP[0] = 1.20 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 23.50 / 2.0; - parTRP[4] = 63.50 / 2.0; - parTRP[5] = 5.00 / 2.0; - parTRP[6] = 16.0; - parTRP[7] = 23.50 / 2.0; - parTRP[8] = 63.50 / 2.0; - parTRP[9] = 5.00 / 2.0; - parTRP[10] = 16.0; - createVolume("USD5", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = 36.5; - ypos = 0.0; - zpos = -1.5; - TVirtualMC::GetMC()->Gspos("USD5", 1, "USDB", xpos, ypos, zpos, matrix[5], "ONLY"); - // Empty spaces (air) - parTRP[0] = 1.20 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 70.50 / 2.0; - parTRP[4] = 4.50 / 2.0; - parTRP[5] = 16.50 / 2.0; - parTRP[6] = -5.0; - parTRP[7] = 70.50 / 2.0; - parTRP[8] = 4.50 / 2.0; - parTRP[9] = 16.50 / 2.0; - parTRP[10] = -5.0; - createVolume("USD6", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = -43.7; - ypos = 0.0; - zpos = 0.4; - TVirtualMC::GetMC()->Gspos("USD6", 1, "USDB", xpos, ypos, zpos, matrix[2], "ONLY"); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0; - zpos = 0.04; - TVirtualMC::GetMC()->Gspos("USDB", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USDB", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Upper bar (aluminum) - parBOX[0] = 95.00 / 2.0; - parBOX[1] = 1.20 / 2.0; - parBOX[2] = 3.00 / 2.0; - createVolume("USD7", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 3.00 / 2.0; - TVirtualMC::GetMC()->Gspos("USD7", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD7", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Lower bar (aluminum) - parBOX[0] = 90.22 / 2.0; - parBOX[1] = 1.20 / 2.0; - parBOX[2] = 1.74 / 2.0; - createVolume("USD8", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0 - 0.1; - zpos = -SHEIGHT / 2.0 + SMPLTT + 2.27; - TVirtualMC::GetMC()->Gspos("USD8", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD8", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Lower bar (aluminum) - parBOX[0] = 82.60 / 2.0; - parBOX[1] = 1.20 / 2.0; - parBOX[2] = 1.40 / 2.0; - createVolume("USD9", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0; - zpos = -SHEIGHT / 2.0 + SMPLTT + 1.40 / 2.0; - TVirtualMC::GetMC()->Gspos("USD9", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USD9", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Front sheet (aluminum) - parTRP[0] = 0.10 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 74.50 / 2.0; - parTRP[4] = 31.70 / 2.0; - parTRP[5] = 44.00 / 2.0; - parTRP[6] = -5.0; - parTRP[7] = 74.50 / 2.0; - parTRP[8] = 31.70 / 2.0; - parTRP[9] = 44.00 / 2.0; - parTRP[10] = -5.0; - createVolume("USDF", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = -32.0; - ypos = CLENGTH[5][2] / 2.0 + 1.20 / 2.0 + 0.10 / 2.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("USDF", 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 2, "UTI1", xpos, -ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 3, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 4, "UTI2", xpos, -ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 5, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 6, "UTI3", xpos, -ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 7, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USDF", 8, "UTI4", xpos, -ypos, zpos, matrix[2], "ONLY"); - - // - // The flat frame in front of the chambers - // - - // The envelope volume (aluminum) - parTRD[0] = 90.00 / 2.0 - 0.1; - parTRD[1] = 114.00 / 2.0 - 0.1; - parTRD[2] = 1.50 / 2.0; - parTRD[3] = 70.30 / 2.0; - createVolume("USCB", "TRD1", idtmed[1], parTRD, kNparTRD); - // Empty spaces (air) - parTRD[0] = 87.00 / 2.0; - parTRD[1] = 10.00 / 2.0; - parTRD[2] = 1.50 / 2.0; - parTRD[3] = 26.35 / 2.0; - createVolume("USC1", "TRD1", idtmed[2], parTRD, kNparTRD); - xpos = 0.0; - ypos = 0.0; - zpos = 26.35 / 2.0 - 70.3 / 2.0; - TVirtualMC::GetMC()->Gspos("USC1", 1, "USCB", xpos, ypos, zpos, 0, "ONLY"); - // Empty spaces (air) - parTRD[0] = 10.00 / 2.0; - parTRD[1] = 111.00 / 2.0; - parTRD[2] = 1.50 / 2.0; - parTRD[3] = 35.05 / 2.0; - createVolume("USC2", "TRD1", idtmed[2], parTRD, kNparTRD); - xpos = 0.0; - ypos = 0.0; - zpos = 70.3 / 2.0 - 35.05 / 2.0; - TVirtualMC::GetMC()->Gspos("USC2", 1, "USCB", xpos, ypos, zpos, 0, "ONLY"); - // Empty spaces (air) - parTRP[0] = 1.50 / 2.0; - parTRP[1] = 0.0; - parTRP[2] = 0.0; - parTRP[3] = 37.60 / 2.0; - parTRP[4] = 63.90 / 2.0; - parTRP[5] = 8.86 / 2.0; - parTRP[6] = 16.0; - parTRP[7] = 37.60 / 2.0; - parTRP[8] = 63.90 / 2.0; - parTRP[9] = 8.86 / 2.0; - parTRP[10] = 16.0; - createVolume("USC3", "TRAP", idtmed[2], parTRP, kNparTRP); - xpos = -30.5; - ypos = 0.0; - zpos = -2.0; - TVirtualMC::GetMC()->Gspos("USC3", 1, "USCB", xpos, ypos, zpos, matrix[4], "ONLY"); - TVirtualMC::GetMC()->Gspos("USC3", 2, "USCB", -xpos, ypos, zpos, matrix[5], "ONLY"); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("USCB", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USCB", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Upper bar (aluminum) - parBOX[0] = 95.00 / 2.0; - parBOX[1] = 1.50 / 2.0; - parBOX[2] = 3.00 / 2.0; - createVolume("USC4", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; - zpos = SHEIGHT / 2.0 - SMPLTT - 3.00 / 2.0; - TVirtualMC::GetMC()->Gspos("USC4", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC4", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Lower bar (aluminum) - parBOX[0] = 90.22 / 2.0; - parBOX[1] = 1.50 / 2.0; - parBOX[2] = 2.00 / 2.0; - createVolume("USC5", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; - zpos = -SHEIGHT / 2.0 + SMPLTT + 2.60; - TVirtualMC::GetMC()->Gspos("USC5", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC5", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - // Lower bar (aluminum) - parBOX[0] = 82.60 / 2.0; - parBOX[1] = 1.50 / 2.0; - parBOX[2] = 1.60 / 2.0; - createVolume("USC6", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = CLENGTH[5][2] / 2.0 + CLENGTH[5][1] + CLENGTH[5][0]; - zpos = -SHEIGHT / 2.0 + SMPLTT + 1.60 / 2.0; - TVirtualMC::GetMC()->Gspos("USC6", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 2, "UTI1", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 4, "UTI2", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 6, "UTI3", xpos, -ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USC6", 8, "UTI4", xpos, -ypos, zpos, 0, "ONLY"); - - // - // The long corner ledges - // - - const int kNparSCL = 3; - float parSCL[kNparSCL]; - const int kNparSCLb = 11; - float parSCLb[kNparSCLb]; - - // Upper ledges - // Thickness of the corner ledges - const float kSCLthkUa = 0.6; - const float kSCLthkUb = 0.6; - // Width of the corner ledges - const float kSCLwidUa = 3.2; - const float kSCLwidUb = 4.8; - // Position of the corner ledges - const float kSCLposxUa = 0.7; - const float kSCLposxUb = 3.3; - const float kSCLposzUa = 1.65; - const float kSCLposzUb = 0.3; - // Vertical - parSCL[0] = kSCLthkUa / 2.0; - parSCL[1] = SLENGTH / 2.0; - parSCL[2] = kSCLwidUa / 2.0; - createVolume("USL1", "BOX ", idtmed[1], parSCL, kNparSCL); - xpos = SWIDTH2 / 2.0 - SMPLTT - kSCLposxUa; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - kSCLposzUa; - TVirtualMC::GetMC()->Gspos("USL1", 1, "UTI1", xpos, ypos, zpos, matrix[0], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL1", 3, "UTI4", xpos, ypos, zpos, matrix[0], "ONLY"); - xpos = -xpos; - TVirtualMC::GetMC()->Gspos("USL1", 2, "UTI1", xpos, ypos, zpos, matrix[1], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL1", 4, "UTI4", xpos, ypos, zpos, matrix[1], "ONLY"); - // Horizontal - parSCL[0] = kSCLwidUb / 2.0; - parSCL[1] = SLENGTH / 2.0; - parSCL[2] = kSCLthkUb / 2.0; - createVolume("USL2", "BOX ", idtmed[1], parSCL, kNparSCL); - xpos = SWIDTH2 / 2.0 - SMPLTT - kSCLposxUb; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - kSCLposzUb; - TVirtualMC::GetMC()->Gspos("USL2", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL2", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL2", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL2", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - xpos = -xpos; - TVirtualMC::GetMC()->Gspos("USL2", 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL2", 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL2", 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL2", 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - - // Lower ledges - // Thickness of the corner ledges - const float kSCLthkLa = 2.464; - const float kSCLthkLb = 1.0; - // Width of the corner ledges - const float kSCLwidLa = 8.3; - const float kSCLwidLb = 4.0; - // Position of the corner ledges - const float kSCLposxLa = (3.0 * kSCLthkLb - kSCLthkLa) / 4.0 + 0.05; - const float kSCLposxLb = kSCLthkLb + kSCLwidLb / 2.0 + 0.05; - const float kSCLposzLa = kSCLwidLa / 2.0; - const float kSCLposzLb = kSCLthkLb / 2.0; - // Vertical - // Trapezoidal shape - parSCLb[0] = SLENGTH / 2.0; - parSCLb[1] = 0.0; - parSCLb[2] = 0.0; - parSCLb[3] = kSCLwidLa / 2.0; - parSCLb[4] = kSCLthkLb / 2.0; - parSCLb[5] = kSCLthkLa / 2.0; - parSCLb[6] = 5.0; - parSCLb[7] = kSCLwidLa / 2.0; - parSCLb[8] = kSCLthkLb / 2.0; - parSCLb[9] = kSCLthkLa / 2.0; - parSCLb[10] = 5.0; - createVolume("USL3", "TRAP", idtmed[1], parSCLb, kNparSCLb); - xpos = SWIDTH1 / 2.0 - SMPLTT - kSCLposxLa; - ypos = 0.0; - zpos = -SHEIGHT / 2.0 + SMPLTT + kSCLposzLa; - TVirtualMC::GetMC()->Gspos("USL3", 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL3", 3, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL3", 5, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL3", 7, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY"); - xpos = -xpos; - TVirtualMC::GetMC()->Gspos("USL3", 2, "UTI1", xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL3", 4, "UTI2", xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL3", 6, "UTI3", xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("USL3", 8, "UTI4", xpos, ypos, zpos, matrix[3], "ONLY"); - // Horizontal part - parSCL[0] = kSCLwidLb / 2.0; - parSCL[1] = SLENGTH / 2.0; - parSCL[2] = kSCLthkLb / 2.0; - createVolume("USL4", "BOX ", idtmed[1], parSCL, kNparSCL); - xpos = SWIDTH1 / 2.0 - SMPLTT - kSCLposxLb; - ypos = 0.0; - zpos = -SHEIGHT / 2.0 + SMPLTT + kSCLposzLb; - TVirtualMC::GetMC()->Gspos("USL4", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL4", 3, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL4", 5, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL4", 7, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - xpos = -xpos; - TVirtualMC::GetMC()->Gspos("USL4", 2, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL4", 4, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL4", 6, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("USL4", 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - - // - // Aluminum plates in the front part of the super modules - // - - const int kNparTrd = 4; - float parTrd[kNparTrd]; - parTrd[0] = SWIDTH1 / 2.0 - 2.5; - parTrd[1] = SWIDTH2 / 2.0 - 2.5; - parTrd[2] = SMPLTT / 2.0; - parTrd[3] = SHEIGHT / 2.0 - 1.0; - createVolume("UTA1", "TRD1", idtmed[1], parTrd, kNparTrd); - xpos = 0.0; - ypos = SMPLTT / 2.0 - FLENGTH / 2.0; - zpos = -0.5; - TVirtualMC::GetMC()->Gspos("UTA1", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTA1", 2, "UTF2", xpos, -ypos, zpos, 0, "ONLY"); - - const int kNparPlt = 3; - float parPlt[kNparPlt]; - parPlt[0] = 0.0; - parPlt[1] = 0.0; - parPlt[2] = 0.0; - createVolume("UTA2", "BOX ", idtmed[1], parPlt, 0); - xpos = 0.0; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT / 2.0; - parPlt[0] = SWIDTH2 / 2.0 - 0.2; - parPlt[1] = FLENGTH / 2.0; - parPlt[2] = SMPLTT / 2.0; - TVirtualMC::GetMC()->Gsposp("UTA2", 1, "UTF2", xpos, ypos, zpos, 0, "ONLY", parPlt, kNparPlt); - xpos = (SWIDTH1 + SWIDTH2) / 4.0 - SMPLTT / 2.0 - 0.0016; - ypos = 0.0; - zpos = 0.0; - parPlt[0] = SMPLTT / 2.0; - parPlt[1] = FLENGTH / 2.0; - parPlt[2] = SHEIGHT / 2.0; - TVirtualMC::GetMC()->Gsposp("UTA2", 2, "UTF2", xpos, ypos, zpos, matrix[0], "ONLY", parPlt, kNparPlt); - TVirtualMC::GetMC()->Gsposp("UTA2", 3, "UTF2", -xpos, ypos, zpos, matrix[1], "ONLY", parPlt, kNparPlt); - - // Additional aluminum bar - parBOX[0] = 80.0 / 2.0; - parBOX[1] = 1.0 / 2.0; - parBOX[2] = 10.0 / 2.0; - createVolume("UTA3", "BOX ", idtmed[1], parBOX, kNparBOX); - xpos = 0.0; - ypos = 1.0 / 2.0 + SMPLTT - FLENGTH / 2.0; - zpos = SHEIGHT / 2.0 - 1.5 - 10.0 / 2.0; - TVirtualMC::GetMC()->Gspos("UTA3", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTA3", 2, "UTF2", xpos, -ypos, zpos, 0, "ONLY"); -} - -//_____________________________________________________________________________ -void TRDGeometry::createServices(std::vector<int> const& idtmed) -{ - // - // Create the geometry of the services - // - // Names of the TRD services volumina - // - // UTC1 Cooling arterias (Al) - // UTC2 Cooling arterias (Water) - // UUxx Volumes for the services at the chambers (Air) - // UMCM Readout MCMs (G10/Cu/Si) - // UDCS DCSs boards (G10/Cu) - // UTP1 Power bars (Cu) - // UTCP Cooling pipes (Fe) - // UTCH Cooling pipes (Water) - // UTPL Power lines (Cu) - // UTGD Gas distribution box (V2A) - // - - int ilayer = 0; - int istack = 0; - - float xpos = 0.0; - float ypos = 0.0; - float zpos = 0.0; - - const int kTag = 100; - char cTagV[kTag]; - - const int kNparBox = 3; - float parBox[kNparBox]; - - const int kNparTube = 3; - float parTube[kNparTube]; - - // Services inside the baby frame - const float kBBMdz = 223.0; - const float kBBSdz = 8.5; - - // Services inside the back frame - const float kBFMdz = 118.0; - const float kBFSdz = 8.5; - - // The rotation matrices - const int kNmatrix = 10; - int matrix[kNmatrix]; - TVirtualMC::GetMC()->Matrix(matrix[0], 100.0, 0.0, 90.0, 90.0, 10.0, 0.0); // rotation around y-axis - TVirtualMC::GetMC()->Matrix(matrix[1], 80.0, 0.0, 90.0, 90.0, 10.0, 180.0); // rotation around y-axis - TVirtualMC::GetMC()->Matrix(matrix[2], 0.0, 0.0, 90.0, 90.0, 90.0, 0.0); - TVirtualMC::GetMC()->Matrix(matrix[3], 180.0, 0.0, 90.0, 90.0, 90.0, 180.0); - TVirtualMC::GetMC()->Matrix(matrix[4], 90.0, 0.0, 0.0, 0.0, 90.0, 90.0); - TVirtualMC::GetMC()->Matrix(matrix[5], 100.0, 0.0, 90.0, 270.0, 10.0, 0.0); - TVirtualMC::GetMC()->Matrix(matrix[6], 80.0, 0.0, 90.0, 270.0, 10.0, 180.0); - TVirtualMC::GetMC()->Matrix(matrix[7], 90.0, 10.0, 90.0, 100.0, 0.0, 0.0); // rotation around z-axis - TVirtualMC::GetMC()->Matrix(matrix[8], 90.0, 350.0, 90.0, 80.0, 0.0, 0.0); // rotation around z-axis - TVirtualMC::GetMC()->Matrix(matrix[9], 90.0, 90.0, 90.0, 180.0, 0.0, 0.0); // rotation around z-axis - - // - // The cooling arterias - // - - // Width of the cooling arterias - const float kCOLwid = 0.8; - // Height of the cooling arterias - const float kCOLhgt = 6.5; - // Positioning of the cooling - const float kCOLposx = 1.0; - const float kCOLposz = -1.2; - // Thickness of the walls of the cooling arterias - const float kCOLthk = 0.1; - const int kNparCOL = 3; - float parCOL[kNparCOL]; - parCOL[0] = 0.0; - parCOL[1] = 0.0; - parCOL[2] = 0.0; - createVolume("UTC1", "BOX ", idtmed[8], parCOL, 0); - createVolume("UTC3", "BOX ", idtmed[8], parCOL, 0); - parCOL[0] = kCOLwid / 2.0 - kCOLthk; - parCOL[1] = -1.0; - parCOL[2] = kCOLhgt / 2.0 - kCOLthk; - createVolume("UTC2", "BOX ", idtmed[14], parCOL, kNparCOL); - createVolume("UTC4", "BOX ", idtmed[14], parCOL, kNparCOL); - - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTC2", 1, "UTC1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTC4", 1, "UTC3", xpos, ypos, zpos, 0, "ONLY"); - - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - // Along the chambers - xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx; - ypos = 0.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = SLENGTH / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer, "UTI1", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + kNlayer, "UTI1", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 6 * kNlayer, "UTI2", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 7 * kNlayer, "UTI2", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 8 * kNlayer, "UTI3", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 9 * kNlayer, "UTI3", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 10 * kNlayer, "UTI4", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", ilayer + 11 * kNlayer, "UTI4", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - - // Front of supermodules - xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx; - ypos = 0.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = FLENGTH / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 2 * kNlayer, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 3 * kNlayer, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 4 * kNlayer, "UTF2", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 5 * kNlayer, "UTF2", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - } - - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - // In baby frame - xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx - 2.5; - ypos = kBBSdz / 2.0 - kBBMdz / 2.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = kBBSdz / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 6 * kNlayer, "BBTRD", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 7 * kNlayer, "BBTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - } - - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - // In back frame - xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 + kCOLposx - 0.3; - ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kCOLhgt / 2.0 - SHEIGHT / 2.0 + kCOLposz + ilayer * (CH + VSPACE); - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = kBFSdz / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 6 * kNlayer, "BFTRD", xpos, ypos, zpos, matrix[0], "ONLY", parCOL, - kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", ilayer + 7 * kNlayer, "BFTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parCOL, - kNparCOL); - } - - // The upper most layer - // Along the chambers - xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 1.3; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = SLENGTH / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC1", 6, "UTI1", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + kNlayer, "UTI1", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 6 * kNlayer, "UTI2", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 7 * kNlayer, "UTI2", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 8 * kNlayer, "UTI3", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 9 * kNlayer, "UTI3", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 10 * kNlayer, "UTI4", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC1", 6 + 11 * kNlayer, "UTI4", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - // Front of supermodules - xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 1.3; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = FLENGTH / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 2 * kNlayer, "UTF1", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 3 * kNlayer, "UTF1", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 4 * kNlayer, "UTF2", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 5 * kNlayer, "UTF2", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - // In baby frame - xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 3.1; - ypos = kBBSdz / 2.0 - kBBMdz / 2.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = kBBSdz / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 6 * kNlayer, "BBTRD", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 7 * kNlayer, "BBTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - // In back frame - xpos = CWIDTH[5] / 2.0 - kCOLhgt / 2.0 - 1.3; - ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.4 - kCOLwid / 2.0; - parCOL[0] = kCOLwid / 2.0; - parCOL[1] = kBFSdz / 2.0; - parCOL[2] = kCOLhgt / 2.0; - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 6 * kNlayer, "BFTRD", xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - TVirtualMC::GetMC()->Gsposp("UTC3", 6 + 7 * kNlayer, "BFTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parCOL, kNparCOL); - - // - // The power bus bars - // - - const float kPWRwid = 0.6; - // Increase the height of the power bus bars to take into - // account the material of additional cables, etc. - const float kPWRhgtA = 5.0 + 0.2; - const float kPWRhgtB = 5.0; - const float kPWRposx = 2.0; - const float kPWRposz = 0.1; - const int kNparPWR = 3; - float parPWR[kNparPWR]; - parPWR[0] = 0.0; - parPWR[1] = 0.0; - parPWR[2] = 0.0; - createVolume("UTP1", "BOX ", idtmed[25], parPWR, 0); - createVolume("UTP3", "BOX ", idtmed[25], parPWR, 0); - - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - // Along the chambers - xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx; - ypos = 0.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kPWRhgtA / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = SLENGTH / 2.0; - parPWR[2] = kPWRhgtA / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer, "UTI1", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + kNlayer, "UTI1", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 6 * kNlayer, "UTI2", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 7 * kNlayer, "UTI2", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 8 * kNlayer, "UTI3", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 9 * kNlayer, "UTI3", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 10 * kNlayer, "UTI4", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", ilayer + 11 * kNlayer, "UTI4", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - - // Front of supermodule - xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx; - ypos = 0.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kPWRhgtA / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = FLENGTH / 2.0; - parPWR[2] = kPWRhgtA / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 2 * kNlayer, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 3 * kNlayer, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 4 * kNlayer, "UTF2", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 5 * kNlayer, "UTF2", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - } - - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - // In baby frame - xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx - 2.5; - ypos = kBBSdz / 2.0 - kBBMdz / 2.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kPWRhgtB / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = kBBSdz / 2.0; - parPWR[2] = kPWRhgtB / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 6 * kNlayer, "BBTRD", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 7 * kNlayer, "BBTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - } - - for (ilayer = 1; ilayer < kNlayer; ilayer++) { - // In back frame - xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0 + kPWRposx - 0.3; - ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; - zpos = - VROCSM + SMPLTT - CALZPOS + kPWRhgtB / 2.0 - SHEIGHT / 2.0 + kPWRposz + ilayer * (CH + VSPACE); - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = kBFSdz / 2.0; - parPWR[2] = kPWRhgtB / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 8 * kNlayer, "BFTRD", xpos, ypos, zpos, matrix[0], "ONLY", parPWR, - kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", ilayer + 9 * kNlayer, "BFTRD", -xpos, ypos, zpos, matrix[1], "ONLY", parPWR, - kNparPWR); - } - - // The upper most layer - // Along the chambers - xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 1.3; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = SLENGTH / 2.0; - parPWR[2] = kPWRhgtB / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP1", 6, "UTI1", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + kNlayer, "UTI1", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 6 * kNlayer, "UTI2", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 7 * kNlayer, "UTI2", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 8 * kNlayer, "UTI3", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 9 * kNlayer, "UTI3", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 10 * kNlayer, "UTI4", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP1", 6 + 11 * kNlayer, "UTI4", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - // Front of supermodules - xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 1.3; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = FLENGTH / 2.0; - parPWR[2] = kPWRhgtB / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 2 * kNlayer, "UTF1", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 3 * kNlayer, "UTF1", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 4 * kNlayer, "UTF2", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 5 * kNlayer, "UTF2", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - // In baby frame - xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 3.0; - ypos = kBBSdz / 2.0 - kBBMdz / 2.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = kBBSdz / 2.0; - parPWR[2] = kPWRhgtB / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 6 * kNlayer, "BBTRD", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 7 * kNlayer, "BBTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - // In back frame - xpos = CWIDTH[5] / 2.0 + kPWRhgtB / 2.0 - 1.3; - ypos = -kBFSdz / 2.0 + kBFMdz / 2.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 0.6 - kPWRwid / 2.0; - parPWR[0] = kPWRwid / 2.0; - parPWR[1] = kBFSdz / 2.0; - parPWR[2] = kPWRhgtB / 2.0; - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 8 * kNlayer, "BFTRD", xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - TVirtualMC::GetMC()->Gsposp("UTP3", 6 + 9 * kNlayer, "BFTRD", -xpos, ypos, zpos, matrix[3], "ONLY", parPWR, kNparPWR); - - // - // The gas tubes connecting the chambers in the super modules with holes - // Material: Stainless steel - // - - // PHOS holes - parTube[0] = 0.0; - parTube[1] = 2.2 / 2.0; - parTube[2] = CLENGTH[5][2] / 2.0 - HSPACE / 2.0; - createVolume("UTG1", "TUBE", idtmed[8], parTube, kNparTube); - parTube[0] = 0.0; - parTube[1] = 2.1 / 2.0; - parTube[2] = CLENGTH[5][2] / 2.0 - HSPACE / 2.0; - createVolume("UTG2", "TUBE", idtmed[9], parTube, kNparTube); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTG2", 1, "UTG1", xpos, ypos, zpos, 0, "ONLY"); - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - xpos = CWIDTH[ilayer] / 2.0 + kCOLwid / 2.0 - 1.5; - ypos = 0.0; - zpos = VROCSM + SMPLTT + kCOLhgt / 2.0 - SHEIGHT / 2.0 + 5.0 + ilayer * (CH + VSPACE); - TVirtualMC::GetMC()->Gspos("UTG1", 1 + ilayer, "UTI3", xpos, ypos, zpos, matrix[4], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTG1", 7 + ilayer, "UTI3", -xpos, ypos, zpos, matrix[4], "ONLY"); - } - // Missing L4S4 chamber in sector 17 - parTube[0] = 0.0; - parTube[1] = 2.2 / 2.0; - parTube[2] = CLENGTH[4][4] / 2.0 - HSPACE / 2.0; - createVolume("UTG3", "TUBE", idtmed[8], parTube, kNparTube); - parTube[0] = 0.0; - parTube[1] = 2.1 / 2.0; - parTube[2] = CLENGTH[4][4] / 2.0 - HSPACE / 2.0; - createVolume("UTG4", "TUBE", idtmed[9], parTube, kNparTube); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTG4", 1, "UTG3", xpos, ypos, zpos, 0, "ONLY"); - xpos = CWIDTH[4] / 2.0 + kCOLwid / 2.0 - 1.5; - ypos = -CLENGTH[4][0] / 2.0 - CLENGTH[4][1] - CLENGTH[4][2] / 2.0; - zpos = VROCSM + SMPLTT + kCOLhgt / 2.0 - SHEIGHT / 2.0 + 5.0 + 4 * (CH + VSPACE); - TVirtualMC::GetMC()->Gspos("UTG3", 1, "UTI4", xpos, ypos, zpos, matrix[4], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTG4", 2, "UTI4", -xpos, ypos, zpos, matrix[4], "ONLY"); - - // - // The volumes for the services at the chambers - // - - const int kNparServ = 3; - float parServ[kNparServ]; - - for (istack = 0; istack < kNstack; istack++) { - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - - snprintf(cTagV, kTag, "UU%02d", iDet); - parServ[0] = CWIDTH[ilayer] / 2.0; - parServ[1] = CLENGTH[ilayer][istack] / 2.0 - HSPACE / 2.0; - parServ[2] = CSVH / 2.0; - createVolume(cTagV, "BOX", idtmed[2], parServ, kNparServ); - } - } - - // - // The cooling pipes inside the service volumes - // - - // The cooling pipes - parTube[0] = 0.0; - parTube[1] = 0.0; - parTube[2] = 0.0; - createVolume("UTCP", "TUBE", idtmed[24], parTube, 0); - // The cooling water - parTube[0] = 0.0; - parTube[1] = 0.2 / 2.0; - parTube[2] = -1.0; - createVolume("UTCH", "TUBE", idtmed[14], parTube, kNparTube); - // Water inside the cooling pipe - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTCH", 1, "UTCP", xpos, ypos, zpos, 0, "ONLY"); - - // Position the cooling pipes in the mother volume - for (istack = 0; istack < kNstack; istack++) { - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - int iCopy = getDetector(ilayer, istack, 0) * 100; - int nMCMrow = getRowMax(ilayer, istack, 0); - float ySize = (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)nMCMrow); - snprintf(cTagV, kTag, "UU%02d", iDet); - for (int iMCMrow = 0; iMCMrow < nMCMrow; iMCMrow++) { - xpos = 0.0; - ypos = (0.5 + iMCMrow) * ySize - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; - zpos = 0.0 + 0.742 / 2.0; - // The cooling pipes - parTube[0] = 0.0; - parTube[1] = 0.3 / 2.0; // Thickness of the cooling pipes - parTube[2] = CWIDTH[ilayer] / 2.0; - TVirtualMC::GetMC()->Gsposp("UTCP", iCopy + iMCMrow, cTagV, xpos, ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - } - } - } - - // - // The power lines - // - - // The copper power lines - parTube[0] = 0.0; - parTube[1] = 0.0; - parTube[2] = 0.0; - createVolume("UTPL", "TUBE", idtmed[5], parTube, 0); - - // Position the power lines in the mother volume - for (istack = 0; istack < kNstack; istack++) { - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - int iCopy = getDetector(ilayer, istack, 0) * 100; - int nMCMrow = getRowMax(ilayer, istack, 0); - float ySize = (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)nMCMrow); - snprintf(cTagV, kTag, "UU%02d", iDet); - for (int iMCMrow = 0; iMCMrow < nMCMrow; iMCMrow++) { - xpos = 0.0; - ypos = (0.5 + iMCMrow) * ySize - 1.0 - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; - zpos = -0.4 + 0.742 / 2.0; - parTube[0] = 0.0; - parTube[1] = 0.2 / 2.0; // Thickness of the power lines - parTube[2] = CWIDTH[ilayer] / 2.0; - TVirtualMC::GetMC()->Gsposp("UTPL", iCopy + iMCMrow, cTagV, xpos, ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - } - } - } - - // - // The MCMs - // - - const float kMCMx = 3.0; - const float kMCMy = 3.0; - const float kMCMz = 0.3; - - const float kMCMpcTh = 0.1; - const float kMCMcuTh = 0.0025; - const float kMCMsiTh = 0.03; - const float kMCMcoTh = 0.04; - - // The mother volume for the MCMs (air) - const int kNparMCM = 3; - float parMCM[kNparMCM]; - parMCM[0] = kMCMx / 2.0; - parMCM[1] = kMCMy / 2.0; - parMCM[2] = kMCMz / 2.0; - createVolume("UMCM", "BOX", idtmed[2], parMCM, kNparMCM); - - // The MCM carrier G10 layer - parMCM[0] = kMCMx / 2.0; - parMCM[1] = kMCMy / 2.0; - parMCM[2] = kMCMpcTh / 2.0; - createVolume("UMC1", "BOX", idtmed[19], parMCM, kNparMCM); - // The MCM carrier Cu layer - parMCM[0] = kMCMx / 2.0; - parMCM[1] = kMCMy / 2.0; - parMCM[2] = kMCMcuTh / 2.0; - createVolume("UMC2", "BOX", idtmed[18], parMCM, kNparMCM); - // The silicon of the chips - parMCM[0] = kMCMx / 2.0; - parMCM[1] = kMCMy / 2.0; - parMCM[2] = kMCMsiTh / 2.0; - createVolume("UMC3", "BOX", idtmed[20], parMCM, kNparMCM); - // The aluminum of the cooling plates - parMCM[0] = kMCMx / 2.0; - parMCM[1] = kMCMy / 2.0; - parMCM[2] = kMCMcoTh / 2.0; - createVolume("UMC4", "BOX", idtmed[24], parMCM, kNparMCM); - - // Put the MCM material inside the MCM mother volume - xpos = 0.0; - ypos = 0.0; - zpos = -kMCMz / 2.0 + kMCMpcTh / 2.0; - TVirtualMC::GetMC()->Gspos("UMC1", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); - zpos += kMCMpcTh / 2.0 + kMCMcuTh / 2.0; - TVirtualMC::GetMC()->Gspos("UMC2", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); - zpos += kMCMcuTh / 2.0 + kMCMsiTh / 2.0; - TVirtualMC::GetMC()->Gspos("UMC3", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); - zpos += kMCMsiTh / 2.0 + kMCMcoTh / 2.0; - TVirtualMC::GetMC()->Gspos("UMC4", 1, "UMCM", xpos, ypos, zpos, 0, "ONLY"); - - // Position the MCMs in the mother volume - for (istack = 0; istack < kNstack; istack++) { - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - int iCopy = getDetector(ilayer, istack, 0) * 1000; - int nMCMrow = getRowMax(ilayer, istack, 0); - float ySize = (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)nMCMrow); - int nMCMcol = 8; - float xSize = (getChamberWidth(ilayer) - 2.0 * CPADW) / ((float)nMCMcol + 6); // Introduce 6 gaps - int iMCM[8] = {1, 2, 3, 5, 8, 9, 10, 12}; // 0..7 MCM + 6 gap structure - snprintf(cTagV, kTag, "UU%02d", iDet); - for (int iMCMrow = 0; iMCMrow < nMCMrow; iMCMrow++) { - for (int iMCMcol = 0; iMCMcol < nMCMcol; iMCMcol++) { - xpos = (0.5 + iMCM[iMCMcol]) * xSize + 1.0 - CWIDTH[ilayer] / 2.0; - ypos = (0.5 + iMCMrow) * ySize + 1.0 - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; - zpos = -0.4 + 0.742 / 2.0; - TVirtualMC::GetMC()->Gspos("UMCM", iCopy + iMCMrow * 10 + iMCMcol, cTagV, xpos, ypos, zpos, 0, "ONLY"); - // Add two additional smaller cooling pipes on top of the MCMs - // to mimic the meandering structure - xpos = (0.5 + iMCM[iMCMcol]) * xSize + 1.0 - CWIDTH[ilayer] / 2.0; - ypos = (0.5 + iMCMrow) * ySize - CLENGTH[ilayer][istack] / 2.0 + HSPACE / 2.0; - zpos = 0.0 + 0.742 / 2.0; - parTube[0] = 0.0; - parTube[1] = 0.3 / 2.0; // Thickness of the cooling pipes - parTube[2] = kMCMx / 2.0; - TVirtualMC::GetMC()->Gsposp("UTCP", iCopy + iMCMrow * 10 + iMCMcol + 50, cTagV, xpos, ypos + 1.0, zpos, - matrix[2], "ONLY", parTube, kNparTube); - TVirtualMC::GetMC()->Gsposp("UTCP", iCopy + iMCMrow * 10 + iMCMcol + 500, cTagV, xpos, ypos + 2.0, zpos, - matrix[2], "ONLY", parTube, kNparTube); - } - } - } - } - - // - // The DCS boards - // - - const float kDCSx = 9.0; - const float kDCSy = 14.5; - const float kDCSz = 0.3; - - const float kDCSpcTh = 0.15; - const float kDCScuTh = 0.01; - const float kDCScoTh = 0.04; - - // The mother volume for the DCSs (air) - const int kNparDCS = 3; - float parDCS[kNparDCS]; - parDCS[0] = kDCSx / 2.0; - parDCS[1] = kDCSy / 2.0; - parDCS[2] = kDCSz / 2.0; - createVolume("UDCS", "BOX", idtmed[2], parDCS, kNparDCS); - - // The DCS carrier G10 layer - parDCS[0] = kDCSx / 2.0; - parDCS[1] = kDCSy / 2.0; - parDCS[2] = kDCSpcTh / 2.0; - createVolume("UDC1", "BOX", idtmed[19], parDCS, kNparDCS); - // The DCS carrier Cu layer - parDCS[0] = kDCSx / 2.0; - parDCS[1] = kDCSy / 2.0; - parDCS[2] = kDCScuTh / 2.0; - createVolume("UDC2", "BOX", idtmed[18], parDCS, kNparDCS); - // The aluminum of the cooling plates - parDCS[0] = 5.0 / 2.0; - parDCS[1] = 5.0 / 2.0; - parDCS[2] = kDCScoTh / 2.0; - createVolume("UDC3", "BOX", idtmed[24], parDCS, kNparDCS); - - // Put the DCS material inside the DCS mother volume - xpos = 0.0; - ypos = 0.0; - zpos = -kDCSz / 2.0 + kDCSpcTh / 2.0; - TVirtualMC::GetMC()->Gspos("UDC1", 1, "UDCS", xpos, ypos, zpos, 0, "ONLY"); - zpos += kDCSpcTh / 2.0 + kDCScuTh / 2.0; - TVirtualMC::GetMC()->Gspos("UDC2", 1, "UDCS", xpos, ypos, zpos, 0, "ONLY"); - zpos += kDCScuTh / 2.0 + kDCScoTh / 2.0; - TVirtualMC::GetMC()->Gspos("UDC3", 1, "UDCS", xpos, ypos, zpos, 0, "ONLY"); - - // Put the DCS board in the chamber services mother volume - for (istack = 0; istack < kNstack; istack++) { - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - int iCopy = iDet + 1; - xpos = CWIDTH[ilayer] / 2.0 - - 1.9 * (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)getRowMax(ilayer, istack, 0)); - ypos = 0.05 * CLENGTH[ilayer][istack]; - zpos = kDCSz / 2.0 - CSVH / 2.0; - snprintf(cTagV, kTag, "UU%02d", iDet); - TVirtualMC::GetMC()->Gspos("UDCS", iCopy, cTagV, xpos, ypos, zpos, 0, "ONLY"); - } - } - - // - // The ORI boards - // - - const float kORIx = 4.2; - const float kORIy = 13.5; - const float kORIz = 0.3; - - const float kORIpcTh = 0.15; - const float kORIcuTh = 0.01; - const float kORIcoTh = 0.04; - - // The mother volume for the ORIs (air) - const int kNparORI = 3; - float parORI[kNparORI]; - parORI[0] = kORIx / 2.0; - parORI[1] = kORIy / 2.0; - parORI[2] = kORIz / 2.0; - createVolume("UORI", "BOX", idtmed[2], parORI, kNparORI); - - // The ORI carrier G10 layer - parORI[0] = kORIx / 2.0; - parORI[1] = kORIy / 2.0; - parORI[2] = kORIpcTh / 2.0; - createVolume("UOR1", "BOX", idtmed[19], parORI, kNparORI); - // The ORI carrier Cu layer - parORI[0] = kORIx / 2.0; - parORI[1] = kORIy / 2.0; - parORI[2] = kORIcuTh / 2.0; - createVolume("UOR2", "BOX", idtmed[18], parORI, kNparORI); - // The aluminum of the cooling plates - parORI[0] = kORIx / 2.0; - parORI[1] = kORIy / 2.0; - parORI[2] = kORIcoTh / 2.0; - createVolume("UOR3", "BOX", idtmed[24], parORI, kNparORI); - - // Put the ORI material inside the ORI mother volume - xpos = 0.0; - ypos = 0.0; - zpos = -kORIz / 2.0 + kORIpcTh / 2.0; - TVirtualMC::GetMC()->Gspos("UOR1", 1, "UORI", xpos, ypos, zpos, 0, "ONLY"); - zpos += kORIpcTh / 2.0 + kORIcuTh / 2.0; - TVirtualMC::GetMC()->Gspos("UOR2", 1, "UORI", xpos, ypos, zpos, 0, "ONLY"); - zpos += kORIcuTh / 2.0 + kORIcoTh / 2.0; - TVirtualMC::GetMC()->Gspos("UOR3", 1, "UORI", xpos, ypos, zpos, 0, "ONLY"); - - // Put the ORI board in the chamber services mother volume - for (istack = 0; istack < kNstack; istack++) { - for (ilayer = 0; ilayer < kNlayer; ilayer++) { - int iDet = getDetectorSec(ilayer, istack); - int iCopy = iDet + 1; - xpos = CWIDTH[ilayer] / 2.0 - - 1.92 * (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)getRowMax(ilayer, istack, 0)); - ypos = -16.0; - zpos = kORIz / 2.0 - CSVH / 2.0; - snprintf(cTagV, kTag, "UU%02d", iDet); - TVirtualMC::GetMC()->Gspos("UORI", iCopy, cTagV, xpos, ypos, zpos, 0, "ONLY"); - xpos = -CWIDTH[ilayer] / 2.0 + - 3.8 * (getChamberLength(ilayer, istack) - 2.0 * RPADW) / ((float)getRowMax(ilayer, istack, 0)); - ypos = -16.0; - zpos = kORIz / 2.0 - CSVH / 2.0; - snprintf(cTagV, kTag, "UU%02d", iDet); - TVirtualMC::GetMC()->Gspos("UORI", iCopy + kNdet, cTagV, xpos, ypos, zpos, 0, "ONLY"); - } - } - - // - // Services in front of the super module - // - - // Gas in-/outlet pipes (INOX) - parTube[0] = 0.0; - parTube[1] = 0.0; - parTube[2] = 0.0; - createVolume("UTG3", "TUBE", idtmed[8], parTube, 0); - // The gas inside the in-/outlet pipes (Xe) - parTube[0] = 0.0; - parTube[1] = 1.2 / 2.0; - parTube[2] = -1.0; - createVolume("UTG4", "TUBE", idtmed[9], parTube, kNparTube); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTG4", 1, "UTG3", xpos, ypos, zpos, 0, "ONLY"); - for (ilayer = 0; ilayer < kNlayer - 1; ilayer++) { - xpos = 0.0; - ypos = CLENGTH[ilayer][2] / 2.0 + CLENGTH[ilayer][1] + CLENGTH[ilayer][0]; - zpos = 9.0 - SHEIGHT / 2.0 + ilayer * (CH + VSPACE); - parTube[0] = 0.0; - parTube[1] = 1.5 / 2.0; - parTube[2] = CWIDTH[ilayer] / 2.0 - 2.5; - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1, "UTI1", xpos, ypos, zpos, matrix[2], "ONLY", parTube, kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 1 * kNlayer, "UTI1", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 2 * kNlayer, "UTI2", xpos, ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 3 * kNlayer, "UTI2", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 4 * kNlayer, "UTI3", xpos, ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 5 * kNlayer, "UTI3", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 6 * kNlayer, "UTI4", xpos, ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - TVirtualMC::GetMC()->Gsposp("UTG3", ilayer + 1 + 7 * kNlayer, "UTI4", xpos, -ypos, zpos, matrix[2], "ONLY", parTube, - kNparTube); - } - - // Gas distribution box - parBox[0] = 14.50 / 2.0; - parBox[1] = 4.52 / 2.0; - parBox[2] = 5.00 / 2.0; - createVolume("UTGD", "BOX ", idtmed[8], parBox, kNparBox); - parBox[0] = 14.50 / 2.0; - parBox[1] = 4.00 / 2.0; - parBox[2] = 4.40 / 2.0; - createVolume("UTGI", "BOX ", idtmed[9], parBox, kNparBox); - parTube[0] = 0.0; - parTube[1] = 4.0 / 2.0; - parTube[2] = 8.0 / 2.0; - createVolume("UTGT", "TUBE", idtmed[8], parTube, kNparTube); - parTube[0] = 0.0; - parTube[1] = 3.4 / 2.0; - parTube[2] = 8.0 / 2.0; - createVolume("UTGG", "TUBE", idtmed[9], parTube, kNparTube); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTGI", 1, "UTGD", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTGG", 1, "UTGT", xpos, ypos, zpos, 0, "ONLY"); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTGD", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); - xpos = -3.0; - ypos = 0.0; - zpos = 6.5; - TVirtualMC::GetMC()->Gspos("UTGT", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); - xpos = -11.25; - ypos = 0.0; - zpos = 0.5; - TVirtualMC::GetMC()->Gspos("UTGT", 3, "UTF1", xpos, ypos, zpos, matrix[2], "ONLY"); - xpos = 11.25; - ypos = 0.0; - zpos = 0.5; - TVirtualMC::GetMC()->Gspos("UTGT", 5, "UTF1", xpos, ypos, zpos, matrix[2], "ONLY"); - - // Cooling manifolds - parBox[0] = 5.0 / 2.0; - parBox[1] = 23.0 / 2.0; - parBox[2] = 70.0 / 2.0; - createVolume("UTCM", "BOX ", idtmed[2], parBox, kNparBox); - parBox[0] = 5.0 / 2.0; - parBox[1] = 5.0 / 2.0; - parBox[2] = 70.0 / 2.0; - createVolume("UTCA", "BOX ", idtmed[8], parBox, kNparBox); - parBox[0] = 5.0 / 2.0 - 0.3; - parBox[1] = 5.0 / 2.0 - 0.3; - parBox[2] = 70.0 / 2.0 - 0.3; - createVolume("UTCW", "BOX ", idtmed[14], parBox, kNparBox); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTCW", 1, "UTCA", xpos, ypos, zpos, 0, "ONLY"); - xpos = 0.0; - ypos = 5.0 / 2.0 - 23.0 / 2.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTCA", 1, "UTCM", xpos, ypos, zpos, 0, "ONLY"); - parTube[0] = 0.0; - parTube[1] = 3.0 / 2.0; - parTube[2] = 18.0 / 2.0; - createVolume("UTCO", "TUBE", idtmed[8], parTube, kNparTube); - parTube[0] = 0.0; - parTube[1] = 3.0 / 2.0 - 0.3; - parTube[2] = 18.0 / 2.0; - createVolume("UTCL", "TUBE", idtmed[14], parTube, kNparTube); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTCL", 1, "UTCO", xpos, ypos, zpos, 0, "ONLY"); - xpos = 0.0; - ypos = 2.5; - zpos = -70.0 / 2.0 + 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 1, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 2, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 3, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 4, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 5, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 6, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 7, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - zpos += 7.0; - TVirtualMC::GetMC()->Gspos("UTCO", 8, "UTCM", xpos, ypos, zpos, matrix[4], "ONLY"); - - xpos = 40.0; - ypos = FLENGTH / 2.0 - 23.0 / 2.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTCM", 1, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTCM", 2, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTCM", 3, "UTF2", xpos, -ypos, zpos, matrix[5], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTCM", 4, "UTF2", -xpos, -ypos, zpos, matrix[6], "ONLY"); - - // Power connection boards (Cu) - parBox[0] = 0.5 / 2.0; - parBox[1] = 15.0 / 2.0; - parBox[2] = 7.0 / 2.0; - createVolume("UTPC", "BOX ", idtmed[25], parBox, kNparBox); - for (ilayer = 0; ilayer < kNlayer - 1; ilayer++) { - xpos = CWIDTH[ilayer] / 2.0 + kPWRwid / 2.0; - ypos = 0.0; - zpos = VROCSM + SMPLTT + kPWRhgtA / 2.0 - SHEIGHT / 2.0 + kPWRposz + (ilayer + 1) * (CH + VSPACE); - TVirtualMC::GetMC()->Gspos("UTPC", ilayer, "UTF1", xpos, ypos, zpos, matrix[0], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTPC", ilayer + kNlayer, "UTF1", -xpos, ypos, zpos, matrix[1], "ONLY"); - } - xpos = CWIDTH[5] / 2.0 + kPWRhgtA / 2.0 - 2.0; - ypos = 0.0; - zpos = SHEIGHT / 2.0 - SMPLTT - 2.0; - TVirtualMC::GetMC()->Gspos("UTPC", 5, "UTF1", xpos, ypos, zpos, matrix[3], "ONLY"); - TVirtualMC::GetMC()->Gspos("UTPC", 5 + kNlayer, "UTF1", -xpos, ypos, zpos, matrix[3], "ONLY"); - - // Power connection panel (Al) - parBox[0] = 60.0 / 2.0; - parBox[1] = 10.0 / 2.0; - parBox[2] = 3.0 / 2.0; - createVolume("UTPP", "BOX ", idtmed[1], parBox, kNparBox); - xpos = 0.0; - ypos = 0.0; - zpos = 18.0; - TVirtualMC::GetMC()->Gspos("UTPP", 1, "UTF1", xpos, ypos, zpos, 0, "ONLY"); - - // - // Electronics boxes - // - - // Casing (INOX) - parBox[0] = 60.0 / 2.0; - parBox[1] = 10.0 / 2.0; - parBox[2] = 6.0 / 2.0; - createVolume("UTE1", "BOX ", idtmed[8], parBox, kNparBox); - // Interior (air) - parBox[0] = parBox[0] - 0.5; - parBox[1] = parBox[1] - 0.5; - parBox[2] = parBox[2] - 0.5; - createVolume("UTE2", "BOX ", idtmed[2], parBox, kNparBox); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTE2", 1, "UTE1", xpos, ypos, zpos, 0, "ONLY"); - xpos = 0.0; - ypos = SLENGTH / 2.0 - 10.0 / 2.0 - 3.0; - zpos = -SHEIGHT / 2.0 + 6.0 / 2.0 + 1.0; - TVirtualMC::GetMC()->Gspos("UTE1", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE1", 2, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE1", 3, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE1", 4, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - - // Casing (INOX) - parBox[0] = 50.0 / 2.0; - parBox[1] = 15.0 / 2.0; - parBox[2] = 20.0 / 2.0; - createVolume("UTE3", "BOX ", idtmed[8], parBox, kNparBox); - // Interior (air) - parBox[0] = parBox[0] - 0.5; - parBox[1] = parBox[1] - 0.5; - parBox[2] = parBox[2] - 0.5; - createVolume("UTE4", "BOX ", idtmed[2], parBox, kNparBox); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTE4", 1, "UTE3", xpos, ypos, zpos, 0, "ONLY"); - xpos = 0.0; - ypos = -SLENGTH / 2.0 + 15.0 / 2.0 + 3.0; - zpos = -SHEIGHT / 2.0 + 20.0 / 2.0 + 1.0; - TVirtualMC::GetMC()->Gspos("UTE3", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE3", 2, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE3", 3, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE3", 4, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - - // Casing (INOX) - parBox[0] = 20.0 / 2.0; - parBox[1] = 7.0 / 2.0; - parBox[2] = 20.0 / 2.0; - createVolume("UTE5", "BOX ", idtmed[8], parBox, kNparBox); - // Interior (air) - parBox[0] = parBox[0] - 0.5; - parBox[1] = parBox[1] - 0.5; - parBox[2] = parBox[2] - 0.5; - createVolume("UTE6", "BOX ", idtmed[2], parBox, kNparBox); - xpos = 0.0; - ypos = 0.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTE6", 1, "UTE5", xpos, ypos, zpos, 0, "ONLY"); - xpos = 20.0; - ypos = -SLENGTH / 2.0 + 7.0 / 2.0 + 3.0; - zpos = 0.0; - TVirtualMC::GetMC()->Gspos("UTE5", 1, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE5", 2, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE5", 3, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE5", 4, "UTI4", xpos, ypos, zpos, 0, "ONLY"); - xpos = -xpos; - TVirtualMC::GetMC()->Gspos("UTE5", 5, "UTI1", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE5", 6, "UTI2", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE5", 7, "UTI3", xpos, ypos, zpos, 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("UTE5", 8, "UTI4", xpos, ypos, zpos, 0, "ONLY"); -} - -//_____________________________________________________________________________ -void TRDGeometry::assembleChamber(int ilayer, int istack) -{ - // - // Group volumes UA, UD, UF, UU into an assembly that defines the - // alignable volume of a single readout chamber - // - - const int kTag = 100; - char cTagV[kTag]; - char cTagM[kTag]; - - double xpos = 0.0; - double ypos = 0.0; - double zpos = 0.0; - - int idet = getDetectorSec(ilayer, istack); - - // Create the assembly for a given ROC - snprintf(cTagM, kTag, "UT%02d", idet); - TGeoVolume* roc = new TGeoVolumeAssembly(cTagM); - - // Add the lower part of the chamber (aluminum frame), - // including radiator and drift region - xpos = 0.0; - ypos = 0.0; - zpos = CRAH / 2.0 + CDRH / 2.0 - CHSV / 2.0; - snprintf(cTagV, kTag, "UA%02d", idet); - TGeoVolume* rocA = gGeoManager->GetVolume(cTagV); - roc->AddNode(rocA, 1, new TGeoTranslation(xpos, ypos, zpos)); - - // Add the additional aluminum ledges - xpos = CWIDTH[ilayer] / 2.0 + CALWMOD / 2.0; - ypos = 0.0; - zpos = CRAH + CDRH - CALZPOS - CALHMOD / 2.0 - CHSV / 2.0; - snprintf(cTagV, kTag, "UZ%02d", idet); - TGeoVolume* rocZ = gGeoManager->GetVolume(cTagV); - roc->AddNode(rocZ, 1, new TGeoTranslation(xpos, ypos, zpos)); - roc->AddNode(rocZ, 2, new TGeoTranslation(-xpos, ypos, zpos)); - - // Add the additional wacosit ledges - xpos = CWIDTH[ilayer] / 2.0 + CWSW / 2.0; - ypos = 0.0; - zpos = CRAH + CDRH - CWSH / 2.0 - CHSV / 2.0; - snprintf(cTagV, kTag, "UP%02d", idet); - TGeoVolume* rocP = gGeoManager->GetVolume(cTagV); - roc->AddNode(rocP, 1, new TGeoTranslation(xpos, ypos, zpos)); - roc->AddNode(rocP, 2, new TGeoTranslation(-xpos, ypos, zpos)); - - // Add the middle part of the chamber (G10 frame), - // including amplification region - xpos = 0.0; - ypos = 0.0; - zpos = CAMH / 2.0 + CRAH + CDRH - CHSV / 2.0; - snprintf(cTagV, kTag, "UD%02d", idet); - TGeoVolume* rocD = gGeoManager->GetVolume(cTagV); - roc->AddNode(rocD, 1, new TGeoTranslation(xpos, ypos, zpos)); - - // Add the upper part of the chamber (aluminum frame), - // including back panel and FEE - xpos = 0.0; - ypos = 0.0; - zpos = CROH / 2.0 + CAMH + CRAH + CDRH - CHSV / 2.0; - snprintf(cTagV, kTag, "UF%02d", idet); - TGeoVolume* rocF = gGeoManager->GetVolume(cTagV); - roc->AddNode(rocF, 1, new TGeoTranslation(xpos, ypos, zpos)); - - // Add the volume with services on top of the back panel - xpos = 0.0; - ypos = 0.0; - zpos = CSVH / 2.0 + CROH + CAMH + CRAH + CDRH - CHSV / 2.0; - snprintf(cTagV, kTag, "UU%02d", idet); - TGeoVolume* rocU = gGeoManager->GetVolume(cTagV); - roc->AddNode(rocU, 1, new TGeoTranslation(xpos, ypos, zpos)); - - // Place the ROC assembly into the super modules - xpos = 0.0; - ypos = 0.0; - ypos = CLENGTH[ilayer][0] + CLENGTH[ilayer][1] + CLENGTH[ilayer][2] / 2.0; - for (int ic = 0; ic < istack; ic++) { - ypos -= CLENGTH[ilayer][ic]; - } - ypos -= CLENGTH[ilayer][istack] / 2.0; - zpos = VROCSM + SMPLTT + CHSV / 2.0 - SHEIGHT / 2.0 + ilayer * (CH + VSPACE); - TGeoVolume* sm1 = gGeoManager->GetVolume("UTI1"); - TGeoVolume* sm2 = gGeoManager->GetVolume("UTI2"); - TGeoVolume* sm3 = gGeoManager->GetVolume("UTI3"); - TGeoVolume* sm4 = gGeoManager->GetVolume("UTI4"); - sm1->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); - sm2->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); - if (istack != 2) { - // w/o middle stack - sm3->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); - } - if (!((ilayer == 4) && (istack == 4))) { - // Sector 17 w/o L4S4 chamber - sm4->AddNode(roc, 1, new TGeoTranslation(xpos, ypos, zpos)); - } -} - -void TRDGeometry::fillMatrixCache(int mask) -{ - if (mask & o2::utils::bit2Mask(o2::TransformType::T2L)) { - useT2LCache(); - } - if (mask & o2::utils::bit2Mask(o2::TransformType::L2G)) { - useL2GCache(); - } - if (mask & o2::utils::bit2Mask(o2::TransformType::T2G)) { - useT2GCache(); - } - if (mask & o2::utils::bit2Mask(o2::TransformType::T2GRot)) { - useT2GRotCache(); - } - - std::string volPath; - const std::string vpStr{"ALIC_1/barrel_1/B077_1/BSEGMO"}; - const std::string vpApp1{"_1/BTRD"}; - const std::string vpApp2{"_1"}; - const std::string vpApp3a{"/UTR1_1/UTS1_1/UTI1_1"}; - const std::string vpApp3b{"/UTR2_1/UTS2_1/UTI2_1"}; - const std::string vpApp3c{"/UTR3_1/UTS3_1/UTI3_1"}; - const std::string vpApp3d{"/UTR4_1/UTS4_1/UTI4_1"}; - - for (int ilayer = 0; ilayer < kNlayer; ilayer++) { - for (int isector = 0; isector < kNsector; isector++) { - for (int istack = 0; istack < kNstack; istack++) { - Int_t lid = getDetector(ilayer, istack, isector); - // Check for disabled supermodules - volPath = vpStr; - volPath += std::to_string(isector); - volPath += vpApp1; - volPath += std::to_string(isector); - volPath += vpApp2; - switch (isector) { - case 17: - if ((istack == 4) && (ilayer == 4)) { - continue; - } - volPath += vpApp3d; - break; - case 13: - case 14: - case 15: - // Check for holes in from of PHOS - if (istack == 2) { - continue; - } - volPath += vpApp3c; - break; - case 11: - case 12: - volPath += vpApp3b; - break; - default: - volPath += vpApp3a; - }; - if (!gGeoManager->CheckPath(volPath.c_str())) { - continue; - } - const auto m = o2::base::GeometryManager::getMatrix(o2::detectors::DetID::TRD, lid); - TGeoHMatrix rotMatrix; - rotMatrix.RotateX(-90); - rotMatrix.RotateY(-90); - rotMatrix.MultiplyLeft(m); - const TGeoHMatrix& t2l = rotMatrix.Inverse(); - if (mask & o2::utils::bit2Mask(o2::TransformType::L2G)) { - setMatrixL2G(Mat3D(t2l), lid); - } - - Double_t sectorAngle = 20.0 * (isector % 18) + 10.0; - TGeoHMatrix rotSector; - rotSector.RotateZ(sectorAngle); - if (mask & o2::utils::bit2Mask(o2::TransformType::T2G)) { - setMatrixT2G(Mat3D(rotSector), lid); - } - if (mask & o2::utils::bit2Mask(o2::TransformType::T2GRot)) { - setMatrixT2GRot(Rot2D(sectorAngle), lid); - } - if (mask & o2::utils::bit2Mask(o2::TransformType::T2L)) { - const TGeoMatrix& inv = rotSector.Inverse(); - rotMatrix.MultiplyLeft(&inv); - setMatrixT2L(Mat3D(rotMatrix.Inverse()), lid); - } - } - } - } -} - -//_____________________________________________________________________________ -void TRDGeometry::addAlignableVolumes() const -{ - // - // define alignable volumes of the TRD - // - if (!gGeoManager) { - LOG(FATAL) << "Geometry is not loaded"; - } - - std::string volPath; - std::string vpStr{"ALIC_1/barrel_1/B077_1/BSEGMO"}; - const std::string vpApp1{"_1/BTRD"}; - const std::string vpApp2{"_1"}; - const std::string vpApp3a{"/UTR1_1/UTS1_1/UTI1_1"}; - const std::string vpApp3b{"/UTR2_1/UTS2_1/UTI2_1"}; - const std::string vpApp3c{"/UTR3_1/UTS3_1/UTI3_1"}; - const std::string vpApp3d{"/UTR4_1/UTS4_1/UTI4_1"}; - std::string symName; - - // in opposite to AliGeomManager, we use consecutive numbering of modules through whole TRD - int volid = -1; - - // The super modules - // The symbolic names are: TRD/sm00 ... TRD/sm17 - for (int isector = 0; isector < kNsector; isector++) { - volPath = vpStr; - volPath += std::to_string(isector); - volPath += vpApp1; - volPath += std::to_string(isector); - volPath += vpApp2; - symName = Form("TRD/sm%02d", isector); - gGeoManager->SetAlignableEntry(symName.c_str(), volPath.c_str()); - } - - // The readout chambers - // The symbolic names are: TRD/sm00/st0/pl0 ... TRD/sm17/st4/pl5 - - for (int isector = 0; isector < kNsector; isector++) { - if (!getSMstatus(isector)) - continue; - for (int ilayer = 0; ilayer < kNlayer; ilayer++) { - for (int istack = 0; istack < kNstack; istack++) { - Int_t lid = getDetector(ilayer, istack, isector); - int idet = getDetectorSec(ilayer, istack); - - // Check for disabled supermodules - volPath = vpStr; - volPath += std::to_string(isector); - volPath += vpApp1; - volPath += std::to_string(isector); - volPath += vpApp2; - switch (isector) { - case 17: - if ((istack == 4) && (ilayer == 4)) { - continue; - } - volPath += vpApp3d; - break; - case 13: - case 14: - case 15: - // Check for holes in from of PHOS - if (istack == 2) { - continue; - } - volPath += vpApp3c; - break; - case 11: - case 12: - volPath += vpApp3b; - break; - default: - volPath += vpApp3a; - }; - volPath += Form("/UT%02d_1", idet); - - symName = Form("TRD/sm%02d/st%d/pl%d", isector, istack, ilayer); - int modID = o2::base::GeometryManager::getSensID(o2::detectors::DetID::TRD, lid); - - TGeoPNEntry* alignableEntry = gGeoManager->SetAlignableEntry(symName.c_str(), volPath.c_str(), modID); - - // Add the tracking to local matrix - if (alignableEntry) { - TGeoHMatrix* globMatrix = alignableEntry->GetGlobalOrig(); - Double_t sectorAngle = 20.0 * (isector % 18) + 10.0; - TGeoHMatrix* t2lMatrix = new TGeoHMatrix(); - t2lMatrix->RotateZ(sectorAngle); - const TGeoHMatrix& globmatrixi = globMatrix->Inverse(); - t2lMatrix->MultiplyLeft(&globmatrixi); - alignableEntry->SetMatrix(t2lMatrix); - } else { - LOG(ERROR) << "Alignable entry is not valid: ModID:" << modID << " Sector:" << isector << " Lr:" << ilayer - << " Stack: " << istack << " name: " << symName.c_str() << " vol: " << volPath.c_str(); - } - } - } - } -} - -//_____________________________________________________________________________ -bool TRDGeometry::createClusterMatrixArray() -{ - - if (!gGeoManager) { - LOG(ERROR) << "Geometry is not loaded yet"; - return false; - } - - if (isBuilt()) { - LOG(WARNING) << "Already built"; - return true; // already initialized - } - - setSize(MAXMATRICES, kNdet); //Only MAXMATRICES=521 of kNdet matrices are filled - fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L) | o2::utils::bit2Mask(o2::TransformType::L2G) | - o2::utils::bit2Mask(o2::TransformType::T2G) | o2::utils::bit2Mask(o2::TransformType::T2GRot)); - return true; -} - -//_____________________________________________________________________________ -bool TRDGeometry::chamberInGeometry(int det) const -{ - // - // Checks whether the given detector is part of the current geometry - // - - if (!isMatrixAvailable(det)) { - return false; - } else { - return true; - } -} diff --git a/Detectors/TRD/base/src/TRDGeometryBase.cxx b/Detectors/TRD/base/src/TRDGeometryBase.cxx deleted file mode 100644 index 85fc87c7af839..0000000000000 --- a/Detectors/TRD/base/src/TRDGeometryBase.cxx +++ /dev/null @@ -1,79 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "TRDBase/TRDGeometryBase.h" - -using namespace o2::trd; - -//_____________________________________________________________________________ -GPUd() int TRDGeometryBase::getStack(float z, int layer) const -{ - // - // Reconstruct the chamber number from the z position and layer number - // - // The return function has to be protected for positiveness !! - // - - if ((layer < 0) || (layer >= kNlayer)) - return -1; - - int istck = kNstack; - float zmin = 0.0; - float zmax = 0.0; - - do { - istck--; - if (istck < 0) - break; - const TRDPadPlane* pp = getPadPlane(layer, istck); - zmax = pp->getRow0(); - int nrows = pp->getNrows(); - zmin = zmax - 2 * pp->getLengthOPad() - (nrows - 2) * pp->getLengthIPad() - (nrows - 1) * pp->getRowSpacing(); - } while ((z < zmin) || (z > zmax)); - - return istck; -} - -//_____________________________________________________________________________ -GPUd() bool TRDGeometryBase::isOnBoundary(int det, float y, float z, float eps) const -{ - // - // Checks whether position is at the boundary of the sensitive volume - // - - int ly = getLayer(det); - if ((ly < 0) || (ly >= kNlayer)) - return true; - - int stk = getStack(det); - if ((stk < 0) || (stk >= kNstack)) - return true; - - const TRDPadPlane* pp = &mPadPlanes[getDetectorSec(ly, stk)]; - if (!pp) - return true; - - float max = pp->getRow0(); - int n = pp->getNrows(); - float min = max - 2 * pp->getLengthOPad() - (n - 2) * pp->getLengthIPad() - (n - 1) * pp->getRowSpacing(); - if (z < min + eps || z > max - eps) { - // printf("z : min[%7.2f (%7.2f)] %7.2f max[(%7.2f) %7.2f]\n", min, min+eps, z, max-eps, max); - return true; - } - min = pp->getCol0(); - n = pp->getNcols(); - max = min + 2 * pp->getWidthOPad() + (n - 2) * pp->getWidthIPad() + (n - 1) * pp->getColSpacing(); - if (y < min + eps || y > max - eps) { - // printf("y : min[%7.2f (%7.2f)] %7.2f max[(%7.2f) %7.2f]\n", min, min+eps, y, max-eps, max); - return true; - } - - return false; -} diff --git a/Detectors/TRD/base/src/TRDGeometryFlat.cxx b/Detectors/TRD/base/src/TRDGeometryFlat.cxx deleted file mode 100644 index 2f1dc0e72358c..0000000000000 --- a/Detectors/TRD/base/src/TRDGeometryFlat.cxx +++ /dev/null @@ -1,34 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "TRDBase/TRDGeometryFlat.h" -#include "TRDBase/TRDGeometry.h" - -TRDGeometryFlat::TRDGeometryFlat(const TRDGeometry& geo) -{ - TRDGeometryBase& b1 = *this; - const TRDGeometryBase& b2 = geo; - memcpy((void*)&b1, (void*)&b2, sizeof(b1)); - int matrixCount = 0; - for (int i = 0; i < kNdet; i++) { - if (geo.chamberInGeometry(i)) { - double v[12]; - geo.getMatrixT2L(i).GetComponents(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11]); - float f[12]; - for (int k = 0; k < 12; k++) { - f[k] = v[k]; - } - mMatrixCache[matrixCount] = o2::gpu::Transform3D(f); - mMatrixIndirection[i] = matrixCount++; - } else { - mMatrixIndirection[i] = -1; - } - } -} diff --git a/Detectors/TRD/base/src/TRDPadPlane.cxx b/Detectors/TRD/base/src/TRDPadPlane.cxx deleted file mode 100644 index 62c94ebff3650..0000000000000 --- a/Detectors/TRD/base/src/TRDPadPlane.cxx +++ /dev/null @@ -1,160 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/////////////////////////////////////////////////////////////////////////////// -// // -// Describes a pad plane of a TRD ROC // -// // -// Contains the information on pad postions, pad dimensions, // -// tilting angle, etc. // -// It also provides methods to identify the current pad number from // -// global coordinates. // -// The numbering and coordinates should follow the official convention // -// (see David Emschermanns note on TRD convention) // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include "TRDBase/TRDPadPlane.h" -#include <TMath.h> -#include <FairLogger.h> - -using namespace o2::trd; - -//_____________________________________________________________________________ -void TRDPadPlane::setTiltingAngle(double t) -{ - // - // Set the tilting angle of the pads - // - - mTiltingAngle = t; - mTiltingTan = TMath::Tan(TMath::Pi() / 180.0 * mTiltingAngle); -} - -//_____________________________________________________________________________ -int TRDPadPlane::getPadRowNumber(double z) const -{ - // - // Finds the pad row number for a given z-position in local supermodule system - // - - int row = 0; - int nabove = 0; - int nbelow = 0; - int middle = 0; - - if ((z > getRow0()) || (z < getRowEnd())) { - row = -1; - - } else { - nabove = mNrows + 1; - nbelow = 0; - while (nabove - nbelow > 1) { - middle = (nabove + nbelow) / 2; - if (z == (mPadRow[middle - 1] + mPadRowSMOffset)) { - row = middle; - } - if (z > (mPadRow[middle - 1] + mPadRowSMOffset)) { - nabove = middle; - } else { - nbelow = middle; - } - } - row = nbelow - 1; - } - - return row; -} - -//_____________________________________________________________________________ -int TRDPadPlane::getPadRowNumberROC(double z) const -{ - // - // Finds the pad row number for a given z-position in local ROC system - // - - int row = 0; - int nabove = 0; - int nbelow = 0; - int middle = 0; - - if ((z > getRow0ROC()) || (z < getRowEndROC())) { - row = -1; - - } else { - nabove = mNrows + 1; - nbelow = 0; - while (nabove - nbelow > 1) { - middle = (nabove + nbelow) / 2; - if (z == mPadRow[middle - 1]) { - row = middle; - } - if (z > mPadRow[middle - 1]) { - nabove = middle; - } else { - nbelow = middle; - } - } - row = nbelow - 1; - } - - return row; -} - -//_____________________________________________________________________________ -int TRDPadPlane::getPadColNumber(double rphi) const -{ - // - // Finds the pad column number for a given rphi-position - // - - int col = 0; - int nabove = 0; - int nbelow = 0; - int middle = 0; - - if ((rphi < getCol0()) || (rphi > getColEnd())) { - col = -1; - - } else { - nabove = mNcols; - nbelow = 0; - while (nabove - nbelow > 1) { - middle = (nabove + nbelow) / 2; - if (rphi == mPadCol[middle]) { - col = middle; - } - if (rphi > mPadCol[middle]) { - nbelow = middle; - } else { - nabove = middle; - } - } - col = nbelow; - } - - return col; -} - -void TRDPadPlane::setNcols(int n) -{ - if (n > MAXCOLS) { - LOG(FATAL) << "MAXCOLS exceeded " << n << " > " << MAXCOLS; - } - mNcols = n; -}; - -void TRDPadPlane::setNrows(int n) -{ - if (n > MAXROWS) { - LOG(FATAL) << "MAXROWS exceeded " << n << " > " << MAXROWS; - } - mNrows = n; -}; diff --git a/Detectors/TRD/base/src/TRDSimParam.cxx b/Detectors/TRD/base/src/TRDSimParam.cxx deleted file mode 100644 index cc71d7629ced6..0000000000000 --- a/Detectors/TRD/base/src/TRDSimParam.cxx +++ /dev/null @@ -1,296 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 containing constant simulation parameters // -// // -// Request an instance with TRDSimParam::Instance() // -// Then request the needed values // -// // -//////////////////////////////////////////////////////////////////////////// - -#include "TRDBase/TRDSimParam.h" -#include <TMath.h> -#include "TRDBase/TRDCommonParam.h" -//#include "AliLog.h" -#include <FairLogger.h> - -using namespace o2::trd; -ClassImp(TRDSimParam); - -TRDSimParam* TRDSimParam::fgInstance = nullptr; -bool TRDSimParam::fgTerminated = false; - -//_ singleton implementation __________________________________________________ -TRDSimParam* TRDSimParam::Instance() -{ - // - // Singleton implementation - // Returns an instance of this class, it is created if neccessary - // - - if (fgTerminated != false) { - return nullptr; - } - - if (fgInstance == nullptr) { - fgInstance = new TRDSimParam(); - } - - return fgInstance; -} - -//_ singleton implementation __________________________________________________ -void TRDSimParam::Terminate() -{ - // - // Singleton implementation - // Deletes the instance of this class and sets the terminated flag, - // instances cannot be requested anymore - // This function can be called several times. - // - - fgTerminated = true; - - if (fgInstance != nullptr) { - delete fgInstance; - fgInstance = nullptr; - } -} - -//_____________________________________________________________________________ -TRDSimParam::TRDSimParam() - : mGasGain(0.0), - mNoise(0.0), - mChipGain(0.0), - mADCoutRange(0.0), - mADCinRange(0.0), - mADCbaseline(0), - mDiffusionOn(false), - mElAttachOn(false), - mElAttachProp(0.0), - mTRFOn(false), - mTRFsmp(nullptr), - mTRFbin(0), - mTRFlo(0.0), - mTRFhi(0.0), - mInvTRFwid(0.0), - mCTOn(false), - mCTsmp(nullptr), - mPadCoupling(0.0), - mTimeCoupling(0.0), - mTimeStructOn(false), - mPRFOn(false), - mNTimeBins(0), - mNTBoverwriteOCDB(false) -{ - // - // Default constructor - // - - Init(); -} - -//_____________________________________________________________________________ -void TRDSimParam::Init() -{ - // - // Default initializiation - // - - // The default parameter for the digitization - mGasGain = 4000.0; - mChipGain = 12.4; - mNoise = 1250.0; - mADCoutRange = 1023.0; // 10-bit ADC - mADCinRange = 2000.0; // 2V input range - mADCbaseline = 10; - - // Diffusion on - mDiffusionOn = true; - - // Propability for electron attachment - mElAttachOn = false; - mElAttachProp = 0.0; - - // The time response function - mTRFOn = true; - - // The cross talk - mCTOn = true; - - // The pad coupling factor - // Use 0.46, instead of the theroetical value 0.3, since it reproduces better - // the test beam data, even tough it is not understood why. - mPadCoupling = 0.46; - - // The time coupling factor (same number as for the TPC) - mTimeCoupling = 0.4; - - // Use drift time maps - mTimeStructOn = true; - - // The pad response function - mPRFOn = true; - - // The number of time bins - mNTimeBins = 22; - mNTBoverwriteOCDB = false; - - ReInit(); -} - -//_____________________________________________________________________________ -TRDSimParam::~TRDSimParam() -{ - // - // Destructor - // - if (mTRFsmp) { - delete[] mTRFsmp; - mTRFsmp = nullptr; - } - - if (mCTsmp) { - delete[] mCTsmp; - mCTsmp = nullptr; - } -} - -//_____________________________________________________________________________ -void TRDSimParam::ReInit() -{ - // - // Reinitializes the parameter class after a change - // - - if (TRDCommonParam::Instance()->IsXenon()) { - // The range and the binwidth for the sampled TRF - mTRFbin = 200; - // Start 0.2 mus before the signal - mTRFlo = -0.4; - // End the maximum drift time after the signal - mTRFhi = 3.58; - // Standard gas gain - mGasGain = 4000.0; - } else if (TRDCommonParam::Instance()->IsArgon()) { - // The range and the binwidth for the sampled TRF - mTRFbin = 50; - // Start 0.2 mus before the signal - mTRFlo = 0.02; - // End the maximum drift time after the signal - mTRFhi = 1.98; - // Higher gas gain - mGasGain = 8000.0; - } else { - LOG(FATAL) << "Not a valid gas mixture!\n"; - } - mInvTRFwid = ((float)mTRFbin) / (mTRFhi - mTRFlo); // Inverse of the bin width of the integrated TRF - - // Create the sampled TRF - SampleTRF(); -} - -//_____________________________________________________________________________ -void TRDSimParam::SampleTRF() -{ - // - // Samples the new time response function. - // - - int ipasa = 0; - - // Xenon - // From Antons measurements with Fe55 source, adjusted by C. Lippmann. - // time bins are -0.4, -0.38, -0.36, ...., 3.54, 3.56, 3.58 microseconds - const int kNpasa = 200; // kNpasa should be equal to fTRFbin! - float xtalk[kNpasa]; - float signal[kNpasa] = { - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0002, 0.0007, 0.0026, 0.0089, 0.0253, 0.0612, 0.1319, - 0.2416, 0.3913, 0.5609, 0.7295, 0.8662, 0.9581, 1.0000, 0.9990, 0.9611, 0.8995, 0.8269, 0.7495, 0.6714, 0.5987, - 0.5334, 0.4756, 0.4249, 0.3811, 0.3433, 0.3110, 0.2837, 0.2607, 0.2409, 0.2243, 0.2099, 0.1974, 0.1868, 0.1776, - 0.1695, 0.1627, 0.1566, 0.1509, 0.1457, 0.1407, 0.1362, 0.1317, 0.1274, 0.1233, 0.1196, 0.1162, 0.1131, 0.1102, - 0.1075, 0.1051, 0.1026, 0.1004, 0.0979, 0.0956, 0.0934, 0.0912, 0.0892, 0.0875, 0.0858, 0.0843, 0.0829, 0.0815, - 0.0799, 0.0786, 0.0772, 0.0757, 0.0741, 0.0729, 0.0718, 0.0706, 0.0692, 0.0680, 0.0669, 0.0655, 0.0643, 0.0630, - 0.0618, 0.0607, 0.0596, 0.0587, 0.0576, 0.0568, 0.0558, 0.0550, 0.0541, 0.0531, 0.0522, 0.0513, 0.0505, 0.0497, - 0.0490, 0.0484, 0.0474, 0.0465, 0.0457, 0.0449, 0.0441, 0.0433, 0.0425, 0.0417, 0.0410, 0.0402, 0.0395, 0.0388, - 0.0381, 0.0374, 0.0368, 0.0361, 0.0354, 0.0348, 0.0342, 0.0336, 0.0330, 0.0324, 0.0318, 0.0312, 0.0306, 0.0301, - 0.0296, 0.0290, 0.0285, 0.0280, 0.0275, 0.0270, 0.0265, 0.0260, 0.0256, 0.0251, 0.0246, 0.0242, 0.0238, 0.0233, - 0.0229, 0.0225, 0.0221, 0.0217, 0.0213, 0.0209, 0.0206, 0.0202, 0.0198, 0.0195, 0.0191, 0.0188, 0.0184, 0.0181, - 0.0178, 0.0175, 0.0171, 0.0168, 0.0165, 0.0162, 0.0159, 0.0157, 0.0154, 0.0151, 0.0148, 0.0146, 0.0143, 0.0140, - 0.0138, 0.0135, 0.0133, 0.0131, 0.0128, 0.0126, 0.0124, 0.0121, 0.0119, 0.0120, 0.0115, 0.0113, 0.0111, 0.0109, - 0.0107, 0.0105, 0.0103, 0.0101, 0.0100, 0.0098, 0.0096, 0.0094, 0.0092, 0.0091, 0.0089, 0.0088, 0.0086, 0.0084, - 0.0083, 0.0081, 0.0080, 0.0078}; - signal[0] = 0.0; - signal[1] = 0.0; - signal[2] = 0.0; - // With undershoot, positive peak corresponds to ~3% of the main signal: - for (ipasa = 3; ipasa < kNpasa; ipasa++) { - xtalk[ipasa] = 0.2 * (signal[ipasa - 2] - signal[ipasa - 3]); - } - xtalk[0] = 0.0; - xtalk[1] = 0.0; - xtalk[2] = 0.0; - - // Argon - // Ar measurement with Fe55 source by Anton - // time bins are 0.02, 0.06, 0.10, ...., 1.90, 1.94, 1.98 microseconds - const int kNpasaAr = 50; - float xtalkAr[kNpasaAr]; - float signalAr[kNpasaAr] = {-0.01, 0.01, 0.00, 0.00, 0.01, -0.01, 0.01, 2.15, 22.28, 55.53, 68.52, 58.21, 40.92, - 27.12, 18.49, 13.42, 10.48, 8.67, 7.49, 6.55, 5.71, 5.12, 4.63, 4.22, 3.81, 3.48, - 3.20, 2.94, 2.77, 2.63, 2.50, 2.37, 2.23, 2.13, 2.03, 1.91, 1.83, 1.75, 1.68, - 1.63, 1.56, 1.49, 1.50, 1.49, 1.29, 1.19, 1.21, 1.21, 1.20, 1.10}; - // Normalization to maximum - for (ipasa = 0; ipasa < kNpasaAr; ipasa++) { - signalAr[ipasa] /= 68.52; - } - signalAr[0] = 0.0; - signalAr[1] = 0.0; - signalAr[2] = 0.0; - // With undershoot, positive peak corresponds to ~3% of the main signal: - for (ipasa = 3; ipasa < kNpasaAr; ipasa++) { - xtalkAr[ipasa] = 0.2 * (signalAr[ipasa - 2] - signalAr[ipasa - 3]); - } - xtalkAr[0] = 0.0; - xtalkAr[1] = 0.0; - xtalkAr[2] = 0.0; - - if (mTRFsmp) { - delete[] mTRFsmp; - } - mTRFsmp = new float[mTRFbin]; - - if (mCTsmp) { - delete[] mCTsmp; - } - mCTsmp = new float[mTRFbin]; - - if (TRDCommonParam::Instance()->IsXenon()) { - if (mTRFbin != kNpasa) { - LOG(ERROR) << "Array mismatch (xenon)\n\n"; - } - } else if (TRDCommonParam::Instance()->IsArgon()) { - if (mTRFbin != kNpasaAr) { - LOG(ERROR) << "Array mismatch (argon)\n\n"; - } - } - - for (int iBin = 0; iBin < mTRFbin; iBin++) { - if (TRDCommonParam::Instance()->IsXenon()) { - mTRFsmp[iBin] = signal[iBin]; - mCTsmp[iBin] = xtalk[iBin]; - } else if (TRDCommonParam::Instance()->IsArgon()) { - mTRFsmp[iBin] = signalAr[iBin]; - mCTsmp[iBin] = xtalkAr[iBin]; - } - } -} diff --git a/Detectors/TRD/base/src/Tracklet.cxx b/Detectors/TRD/base/src/Tracklet.cxx index 110c2566d9123..723cdffcae627 100644 --- a/Detectors/TRD/base/src/Tracklet.cxx +++ b/Detectors/TRD/base/src/Tracklet.cxx @@ -22,7 +22,7 @@ #include "TRDBase/Tracklet.h" #include <fairlogger/Logger.h> -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include <SimulationDataFormat/MCCompLabel.h> #include <SimulationDataFormat/MCTruthContainer.h> //_____________________________________________________________________________ @@ -32,25 +32,25 @@ using namespace o2::trd; Tracklet::Tracklet() { // constructor - mGeo = TRDGeometry::instance(); + mGeo = Geometry::instance(); } Tracklet::Tracklet(int hcid) : mHCId(hcid) { // constructor - mGeo = TRDGeometry::instance(); + mGeo = Geometry::instance(); } Tracklet::Tracklet(int hcid, int rob, int mcm, float pid, float rawslope, float rawoffset, float rawslope4trackletword, float rawoffset4trackletword) : mHCId(hcid), mMCM(mcm), mROB(rob), mPID(pid), mdY(rawslope), mY(rawoffset), mdY4tw(rawslope4trackletword), mY4tw(rawoffset4trackletword) { // constructor - mGeo = TRDGeometry::instance(); + mGeo = Geometry::instance(); } Tracklet::Tracklet(const Tracklet& rhs) : mHCId(rhs.mHCId), mMCM(rhs.mMCM), mROB(rhs.mROB), mQ0(rhs.mQ0), mQ1(rhs.mQ1), mPID(rhs.mPID), mdY(rhs.mdY), mY(rhs.mY), mdY4tw(rhs.mdY4tw), mY4tw(rhs.mY4tw), mNHits(rhs.mNHits), mNHits0(rhs.mNHits0), mNHits1(rhs.mNHits1), mSlope(rhs.mSlope), mOffset(rhs.mOffset), mError(rhs.mError), mNClusters(rhs.mNClusters) { // copy constructor - mGeo = TRDGeometry::instance(); + mGeo = Geometry::instance(); mResiduals = rhs.mResiduals; mClsCharges = rhs.mClsCharges; @@ -81,7 +81,7 @@ int Tracklet::getYbin() const return mY; } -int Tracklet::getdY() const +float Tracklet::getdY() const { return mdY; } diff --git a/Detectors/TRD/base/src/TrackletTransformer.cxx b/Detectors/TRD/base/src/TrackletTransformer.cxx new file mode 100644 index 0000000000000..986d57c2426e9 --- /dev/null +++ b/Detectors/TRD/base/src/TrackletTransformer.cxx @@ -0,0 +1,158 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/GeometryManager.h" +#include "TRDBase/TrackletTransformer.h" +#include "TMath.h" + +using namespace o2::trd; +using namespace o2::trd::constants; + +TrackletTransformer::TrackletTransformer() +{ + o2::base::GeometryManager::loadGeometry(); + mGeo = Geometry::instance(); + mGeo->createPadPlaneArray(); + mGeo->createClusterMatrixArray(); + + // 3 cm + mXCathode = mGeo->cdrHght(); + // 2.221 + // mXAnode = mGeo->anodePos(); + // 3.35 + mXAnode = mGeo->cdrHght() + mGeo->camHght() / 2; + // 2.5 + mXDrift = mGeo->cdrHght() - 0.5; + mXtb0 = -100; + + // dummy values for testing. This will change in the future when values are pulled from CCDB + mt0Correction = -0.1; + mOldLorentzAngle = 0.16; + mLorentzAngle = -0.14; + mDriftVRatio = 1.1; +} + +void TrackletTransformer::loadPadPlane(int hcid) +{ + int detector = hcid / 2; + int stack = mGeo->getStack(detector); + int layer = mGeo->getLayer(detector); + mPadPlane = mGeo->getPadPlane(layer, stack); +} + +float TrackletTransformer::calculateY(int hcid, int column, int position) +{ + double padWidth = mPadPlane->getWidthIPad(); + int side = hcid % 2; + + // slightly modified TDP eq 16.1 (appended -1 to the end to account for MCM shared pads) + double pad = float(position - (1 << (NBITSTRKLPOS - 1))) * GRANULARITYTRKLPOS + NCOLMCM * (4 * side + column) + 10.5 - 1; + float y = padWidth * (pad - 72); + + return y; +} + +float TrackletTransformer::calculateZ(int padrow) +{ + double rowPos = mPadPlane->getRowPos(padrow); + double rowWidth = mPadPlane->getRowSize(padrow); + double middleRowPos = mPadPlane->getRowPos(mPadPlane->getNrows() / 2); + + float z = rowPos - rowWidth / 2. - middleRowPos; + + return z; +} + +float TrackletTransformer::calculateDy(int slope, double oldLorentzAngle, double lorentzAngle, double driftVRatio) +{ + double padWidth = mPadPlane->getWidthIPad(); + + // temporary dummy value in cm/microsecond + float vDrift = 1.56; + float driftHeight = mGeo->cdrHght(); + + // dy = slope * nTimeBins * padWidth * GRANULARITYTRKLSLOPE; + // nTimeBins should be number of timebins in drift region. 1 timebin is 100 nanosecond + double rawDy = slope * ((driftHeight / vDrift) / 0.1) * padWidth * GRANULARITYTRKLSLOPE; + + // driftDistance = 3.35 + float driftDistance = mGeo->cdrHght() + mGeo->camHght(); + + float cmSlope = rawDy / driftDistance; + + double calibratedDy = rawDy - (TMath::Tan(lorentzAngle) * driftDistance); + calibratedDy += (TMath::Tan(oldLorentzAngle) * driftDistance * driftVRatio) + cmSlope * (driftDistance * (1 - driftVRatio)); + + // ALTERNATIVE METHOD + + // double x_anode_hit = driftDistance*driftVRatio/cmSlope; + // double y_anode_hit = driftDistance*driftVRatio; + + // double x_Lorentz_drift_hit = TMath::Tan(oldLorentzAngle)*driftDistance*driftVRatio - TMath::Tan(lorentzAngle)*driftDistance; + // double y_Lorentz_drift_hit = driftDistance*driftVRatio - driftDistance; + + // double Delta_x_Lorentz_drift_hit = x_anode_hit - x_Lorentz_drift_hit; + // double Delta_y_Lorentz_drift_hit = y_anode_hit - y_Lorentz_drift_hit; + // double impact_angle_rec = TMath::ATan2(Delta_y_Lorentz_drift_hit,Delta_x_Lorentz_drift_hit); + + // float calibrationShift = TMath::Tan(impact_angle_rec) * driftDistance; + + // LOG(info) << "ORIGINAL: " << calibratedDy; + // LOG(info) << "ALTERNATIVE: " << rawDy + calibrationShift; + + return calibratedDy; +} + +float TrackletTransformer::calibrateX(double x, double t0Correction) +{ + return x += t0Correction; +} + +std::array<float, 3> TrackletTransformer::transformL2T(int hcid, std::array<double, 3> point) +{ + int detector = hcid / 2; + auto transformationMatrix = mGeo->getMatrixT2L(detector); + + ROOT::Math::Impl::Transform3D<double>::Point localPoint(point[0], point[1], point[2]); + auto gobalPoint = transformationMatrix ^ localPoint; + + return {(float)gobalPoint.x(), (float)gobalPoint.y(), (float)gobalPoint.z()}; +} + +CalibratedTracklet TrackletTransformer::transformTracklet(Tracklet64 tracklet) +{ + uint64_t trackletWord = tracklet.getTrackletWord(); + uint64_t hcid = tracklet.getHCID(); + uint64_t padrow = tracklet.getPadRow(); + uint64_t column = tracklet.getColumn(); + // 0-2048 | units:pad-widths | granularity=1/75 (measured from center pad 10) 1024 is 0/center of pad 10 + uint64_t position = tracklet.getPosition(); + // 0-127 | units:pads/timebin | granularity=1/1000 + uint64_t slope = tracklet.getSlope(); + + // calculate raw local chamber space point + loadPadPlane(hcid); + float x = getXDrift(); + float y = calculateY(hcid, column, position); + float z = calculateZ(padrow); + + float dy = calculateDy(slope, mOldLorentzAngle, mLorentzAngle, mDriftVRatio); + float calibratedX = calibrateX(x, mt0Correction); + + std::array<float, 3> sectorSpacePoint = transformL2T(hcid, std::array<double, 3>{x, y, z}); + + LOG(debug) << "x: " << sectorSpacePoint[0] << " | " + << "y: " << sectorSpacePoint[1] << " | " + << "z: " << sectorSpacePoint[2]; + + CalibratedTracklet calibratedTracklet = CalibratedTracklet(trackletWord, sectorSpacePoint[0], sectorSpacePoint[1], sectorSpacePoint[2], dy); + + return calibratedTracklet; +} diff --git a/Detectors/TRD/base/test/testArrayADC.cxx b/Detectors/TRD/base/test/testArrayADC.cxx deleted file mode 100644 index 4cc1da4e09c02..0000000000000 --- a/Detectors/TRD/base/test/testArrayADC.cxx +++ /dev/null @@ -1,41 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 testTPCMapper.cxx -/// \brief This task tests the mapper function -/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de - -#define BOOST_TEST_MODULE Test TRD_ArrayADC -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include <boost/test/unit_test.hpp> -#include <cstdlib> -#include <fstream> -#include <iostream> -#include <sstream> -#include <string> -#include "TRDBase/TRDArrayADC.h" - -namespace o2 -{ -namespace tcp -{ - -/// \brief Test the arrayadc class operation -// -/// check the bit manipulations -BOOST_AUTO_TEST_CASE(ArrayADCtest1) -{ - TRDArrayADC data = new TRDArrayADC(); - //set bits as corrupted and see if they are detected correctly -} - -} // namespace tcp -} // namespace o2 diff --git a/Detectors/TRD/base/test/testRawData.cxx b/Detectors/TRD/base/test/testRawData.cxx new file mode 100644 index 0000000000000..f9b88a690be6c --- /dev/null +++ b/Detectors/TRD/base/test/testRawData.cxx @@ -0,0 +1,89 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 testTRDDataFormats.cxx +/// \brief This task tests the data format structs +/// \author Sean Murray, murrays@cern.ch + +#define BOOST_TEST_MODULE Test TRD_RawDataHeader +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/Tracklet64.h" + +namespace o2 +{ +namespace trd +{ + +/// \brief Test the data header struct sizes +// +/// check the bit manipulations +BOOST_AUTO_TEST_CASE(TRDRawDataHeaderSizes) +{ + //check the sizes of header structs due to packing + BOOST_CHECK_EQUAL(sizeof(o2::trd::TrackletMCMData), 4); + BOOST_CHECK_EQUAL(sizeof(o2::trd::TrackletHCHeader), 4); + BOOST_CHECK_EQUAL(sizeof(o2::trd::TrackletMCMHeader), 4); + BOOST_CHECK_EQUAL(sizeof(o2::trd::HalfCRUHeader), 64); +} + +BOOST_AUTO_TEST_CASE(TRDRawDataHeaderInternals) +{ + o2::trd::TrackletMCMData tracklet; + o2::trd::TrackletHCHeader halfchamberheader; + o2::trd::HalfCRUHeader halfcruheader; + // changed as now nothing spans a 32 or 16 bit boundary + halfcruheader.word0 = 0x22200; //bc is at 0x0000000fff00 + BOOST_CHECK_EQUAL(halfcruheader.BunchCrossing, 0x222); + halfcruheader.word0 = 0x00000077; //headerversion is at 0x000000000 + BOOST_CHECK_EQUAL(halfcruheader.HeaderVersion, 119); + //error flags + halfcruheader.word12[0] = 0xa02; + halfcruheader.word12[1] = 0xab0000; + BOOST_CHECK_EQUAL(halfcruheader.errorflags[0].errorflag, 2); + BOOST_CHECK_EQUAL(halfcruheader.errorflags[1].errorflag, 0xa); + halfcruheader.word12[1] = 0x00ed000000000000; // should link 14 error flags. + BOOST_CHECK_EQUAL(halfcruheader.errorflags[14].errorflag, 0xed); + BOOST_CHECK_EQUAL(halfcruheader.errorflags[14].errorflag, o2::trd::getlinkerrorflag(halfcruheader, 14)); + //datasizes + halfcruheader.word47[0] = 0xbdbd; + BOOST_CHECK_EQUAL(halfcruheader.datasizes[0].size, 0xbdbd); + BOOST_CHECK_EQUAL(halfcruheader.datasizes[0].size, o2::trd::getlinkdatasize(halfcruheader, 0)); + halfcruheader.word47[1] = 0xabcd; + BOOST_CHECK_EQUAL(halfcruheader.datasizes[4].size, 0xabcd); + BOOST_CHECK_EQUAL(halfcruheader.datasizes[4].size, o2::trd::getlinkdatasize(halfcruheader, 4)); + halfcruheader.word47[2] = 0xaaade127; + BOOST_CHECK_EQUAL(halfcruheader.datasizes[8].size, 0xe127); + BOOST_CHECK_EQUAL(halfcruheader.datasizes[8].size, o2::trd::getlinkdatasize(halfcruheader, 8)); + halfcruheader.word47[3] = 0xefaadebc0000; + BOOST_CHECK_EQUAL(halfcruheader.datasizes[14].size, 0xefaa); + BOOST_CHECK_EQUAL(halfcruheader.datasizes[14].size, o2::trd::getlinkdatasize(halfcruheader, 14)); + o2::trd::TrackletMCMHeader mcmrawdataheader; + mcmrawdataheader.word = 0x78000000; + BOOST_CHECK_EQUAL(mcmrawdataheader.padrow, 15); + mcmrawdataheader.word = 0x06000000; + BOOST_CHECK_EQUAL(mcmrawdataheader.col, 3); + mcmrawdataheader.word = 0x01fe0000; + BOOST_CHECK_EQUAL(mcmrawdataheader.pid2, 0xff); // 8 bits + mcmrawdataheader.word = 0x01fe; + BOOST_CHECK_EQUAL(mcmrawdataheader.pid0, 0xff); // 8 bits + //check tracklet + tracklet.word = 0xffc00000; + BOOST_CHECK_EQUAL((uint32_t)tracklet.pos, 0x3ff); +} +} // namespace trd +} // namespace o2 diff --git a/Detectors/TRD/base/test/testTRDDiffusionCoefficient.cxx b/Detectors/TRD/base/test/testTRDDiffusionCoefficient.cxx index 1d21e2d5a5e8b..55355fc7ef386 100644 --- a/Detectors/TRD/base/test/testTRDDiffusionCoefficient.cxx +++ b/Detectors/TRD/base/test/testTRDDiffusionCoefficient.cxx @@ -16,8 +16,8 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> -#include "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDDiffAndTimeStructEstimator.h" +#include "TRDBase/CommonParam.h" +#include "TRDBase/DiffAndTimeStructEstimator.h" #include "Field/MagneticField.h" #include <TGeoGlobalMagField.h> @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(TRDDiffusionCoefficient_test1) TGeoGlobalMagField::Instance()->SetField(fld); TGeoGlobalMagField::Instance()->Lock(); - auto commonParam = TRDCommonParam::Instance(); + auto commonParam = CommonParam::Instance(); float dl = 0; float dt = 0; float vd = 1.48; @@ -47,8 +47,8 @@ BOOST_AUTO_TEST_CASE(TRDDiffusionCoefficient_test1) /// \brief Test time structure BOOST_AUTO_TEST_CASE(TRDTimeStructure_test) { - auto commonParam = TRDCommonParam::Instance(); - TRDDiffusionAndTimeStructEstimator estimator; + auto commonParam = CommonParam::Instance(); + DiffusionAndTimeStructEstimator estimator; BOOST_CHECK_CLOSE(estimator.TimeStruct(1.48, 1., 0.1), commonParam->TimeStruct(1.48, 1., 0.1), 0.001); BOOST_CHECK_CLOSE(estimator.TimeStruct(1.1, 1., 0.1), commonParam->TimeStruct(1.1, 1., 0.1), 0.001); BOOST_CHECK_CLOSE(estimator.TimeStruct(2, 1., 0.1), commonParam->TimeStruct(2, 1., 0.1), 0.001); @@ -58,8 +58,8 @@ BOOST_AUTO_TEST_CASE(TRDTimeStructure_test) /// \brief compare diffusion coeff BOOST_AUTO_TEST_CASE(TRDDiffusion_test) { - auto commonParam = TRDCommonParam::Instance(); - TRDDiffusionAndTimeStructEstimator estimator; + auto commonParam = CommonParam::Instance(); + DiffusionAndTimeStructEstimator estimator; float dl1 = 0.; float dl2 = 0.; float dt1 = 0.; diff --git a/Detectors/TRD/base/test/testTRDGeometry.cxx b/Detectors/TRD/base/test/testTRDGeometry.cxx index e99d46fc1ffab..976f20b9a2699 100644 --- a/Detectors/TRD/base/test/testTRDGeometry.cxx +++ b/Detectors/TRD/base/test/testTRDGeometry.cxx @@ -9,7 +9,7 @@ // or submit itself to any jurisdiction. /// \file testTRDGeometry.cxx -/// \brief This task tests the TRDGeometry +/// \brief This task tests the Geometry /// \author Sean Murray, murrays@cern.ch #define BOOST_TEST_MODULE Test TRD_Geometry @@ -21,7 +21,7 @@ #include <iostream> #include <sstream> #include <string> -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include <iostream> namespace o2 @@ -29,26 +29,26 @@ namespace o2 namespace trd { -/// \brief Test the TRDGeometry class +/// \brief Test the Geometry class // /// BOOST_AUTO_TEST_CASE(TRDGeometry_test1) { //arbitrary chosen - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowPos(1, 1, 3), 154.5, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowSize(1, 1, 3), 7.5, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRow0(1, 1), 177, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowEnd(1, 1), 57, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowPos(1, 1, 3), 154.5, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowSize(1, 1, 3), 7.5, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRow0(1, 1), 177, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowEnd(1, 1), 57, 1e-3); //start - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowPos(0, 0, 3), 278.5, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowSize(0, 0, 3), 7.5, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRow0(0, 0), 301, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowEnd(0, 0), 181, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowPos(0, 0, 3), 278.5, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowSize(0, 0, 3), 7.5, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRow0(0, 0), 301, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowEnd(0, 0), 181, 1e-3); //end of trd. - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowPos(5, 4, 0), -204, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowSize(5, 4, 3), 9, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRow0(5, 4), -204, 1e-3); - BOOST_CHECK_CLOSE(TRDGeometry::instance()->getRowEnd(5, 4), -347, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowPos(5, 4, 0), -204, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowSize(5, 4, 3), 9, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRow0(5, 4), -204, 1e-3); + BOOST_CHECK_CLOSE(Geometry::instance()->getRowEnd(5, 4), -347, 1e-3); } } // namespace trd diff --git a/Detectors/TRD/macros/CMakeLists.txt b/Detectors/TRD/macros/CMakeLists.txt index 610afda87fdb3..405687d0f308f 100644 --- a/Detectors/TRD/macros/CMakeLists.txt +++ b/Detectors/TRD/macros/CMakeLists.txt @@ -14,8 +14,9 @@ o2_add_test_root_macro(CheckCCDBvalues.C o2_add_test_root_macro(CheckDigits.C PUBLIC_LINK_LIBRARIES O2::TRDBase + O2::DataFormatsTRD LABELS trd) o2_add_test_root_macro(CheckHits.C PUBLIC_LINK_LIBRARIES O2::TRDBase O2::TRDSimulation - LABELS trd) \ No newline at end of file + LABELS trd) diff --git a/Detectors/TRD/macros/CheckDigits.C b/Detectors/TRD/macros/CheckDigits.C index 181bd2211af98..f887fe44d6d3f 100644 --- a/Detectors/TRD/macros/CheckDigits.C +++ b/Detectors/TRD/macros/CheckDigits.C @@ -22,8 +22,9 @@ #include "FairLogger.h" #include "TRDBase/Digit.h" -#include "TRDBase/TRDSimParam.h" -#include "TRDBase/TRDCommonParam.h" +#include "TRDBase/SimParam.h" +#include "TRDBase/CommonParam.h" +#include "DataFormatsTRD/Constants.h" #endif using namespace o2::trd; @@ -74,9 +75,9 @@ void CheckDigits(std::string digifile = "trddigits.root", hDet->Fill(det); hRow->Fill(row); hPad->Fill(pad); - for (int tb = 0; tb < kTimeBins; ++tb) { + for (int tb = 0; tb < o2::trd::constants::TIMEBINS; ++tb) { ADC_t adc = adcs[tb]; - if (adc == (ADC_t)TRDSimParam::Instance()->GetADCoutRange()) { + if (adc == (ADC_t)SimParam::Instance()->GetADCoutRange()) { // LOG(INFO) << "Out of range ADC " << adc; continue; } diff --git a/Detectors/TRD/macros/CheckHits.C b/Detectors/TRD/macros/CheckHits.C index a861b49da4262..5f06c5456d92c 100644 --- a/Detectors/TRD/macros/CheckHits.C +++ b/Detectors/TRD/macros/CheckHits.C @@ -20,7 +20,7 @@ #include "FairLogger.h" #include "TRDSimulation/Detector.h" -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/Calibrations.h" #include "DetectorsCommonDataFormats/NameConf.h" #include "DetectorsCommonDataFormats/DetID.h" @@ -57,15 +57,15 @@ void CheckHits(const int detector = 50, // 354, 14, 242, 50 hitTree->GetEvent(iev); for (const auto& hit : *hits) { int det = hit.GetDetectorID(); - // if (calib.isChamberNoData(det) || o2::trd::TRDGeometry::getStack(det)!=2 || o2::trd::TRDGeometry::getSector(det) != 4) { + // if (calib.isChamberNoData(det) || o2::trd::Geometry::getStack(det)!=2 || o2::trd::Geometry::getSector(det) != 4) { // if (calib.isChamberNoData(det)) { // continue; // } if (det != detector) { - // LOG(INFO) << "REJECTED Detector = " << det <<"\t Stack = " << o2::trd::TRDGeometry::getStack(det) << "\t Sector = " << o2::trd::TRDGeometry::getSector(det); + // LOG(INFO) << "REJECTED Detector = " << det <<"\t Stack = " << o2::trd::Geometry::getStack(det) << "\t Sector = " << o2::trd::Geometry::getSector(det); continue; } - LOG(INFO) << "ACCEPTED Detector = " << det << "\t Stack = " << o2::trd::TRDGeometry::getStack(det) << "\t Sector = " << o2::trd::TRDGeometry::getSector(det); + LOG(INFO) << "ACCEPTED Detector = " << det << "\t Stack = " << o2::trd::Geometry::getStack(det) << "\t Sector = " << o2::trd::Geometry::getSector(det); // loop over det, pad, row? double locC = hit.getLocalC(); // col direction in amplification or drift volume double locR = hit.getLocalR(); // row direction in amplification or drift volume diff --git a/Detectors/TRD/macros/ParseTrapRawOutput.C b/Detectors/TRD/macros/ParseTrapRawOutput.C new file mode 100644 index 0000000000000..c1d212463b5ce --- /dev/null +++ b/Detectors/TRD/macros/ParseTrapRawOutput.C @@ -0,0 +1,64 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include <TChain.h> +#include <TFile.h> +#include <TH1F.h> +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/LinkData.h" +#include <memory> +#endif + +//Parse the trackletraw output of a trap simulation +//Display on the screen and indent for half chamber, mcmheader and mcmtracklet. +//Create a raw dump as well of the incoming data. + +using namespace o2::trd; + +void ParseTrapRawOutput(std::string path = "./", std::string inputTracklets = "trdtrapraw.root") +{ + TChain trdtrapraw("o2sim"); + trdtrapraw.AddFile((path + inputTracklets).c_str()); + + std::vector<o2::trd::LinkRecord> links; + std::vector<o2::trd::LinkRecord>* linksptr = &links; + + std::vector<uint32_t> trapraw; + std::vector<uint32_t>* traprawptr = &trapraw; + trdtrapraw.SetBranchAddress("TrapRaw", &traprawptr); + trdtrapraw.SetBranchAddress("TrapLinkRecord", &linksptr); + trdtrapraw.GetEntry(0); + + ofstream outfile("rawdata", std::ios::out | std::ofstream::binary); + outfile.write((char*)(&trapraw[0]), sizeof(trapraw[0]) * trapraw.size()); + outfile.close(); + uint64_t mcmheadcount = 0; + uint64_t halfchamberheadcount = 0; + uint64_t traprawtrackletount = 0; + //at each linkrecord data we should have a halfchamberheader; + // with in the range specified by the linkrecord we have a structure of : + // mcmheader, traprawtracklet[1-3], mcmheader, traprawtracklet[1-3], etc. etc. + for (auto& link : links) { + o2::trd::TrackletHCHeader halfchamber; + halfchamber.word = link.getLinkId(); + std::cout << "in link with HCID of " << halfchamber; + for (int i = link.getFirstEntry(); i < link.getFirstEntry() + link.getNumberOfObjects(); i++) { + //read TrackletMCMHeader + //read 1 to 3 TrackletMCMDatas + if (((trapraw[i]) & 0x1) == 1 && (trapraw[i] & 0x80000000) != 0) { + TrackletMCMHeader mcm; + mcm.word = trapraw[i]; + std::cout << "\t\t" << mcm; + mcmheadcount++; + } else if (((trapraw[i]) & 0x1) == 0) { + //tracklet word + // + TrackletMCMData tracklet; + tracklet.word = trapraw[i]; + std::cout << "\t\t\t\tTracklet : 0x" << std::hex << tracklet.word << std::dec << std::endl; + traprawtrackletount++; + } else + std::cout << "most sig bit is not binary ?? 0x" << std::hex << trapraw[i] << std::dec << std::endl; + } + } + std::cout << "counts " << halfchamberheadcount << "::" << mcmheadcount << "::" << traprawtrackletount << std::endl; +} diff --git a/Detectors/TRD/macros/convertRun2ToRun3Digits.C b/Detectors/TRD/macros/convertRun2ToRun3Digits.C new file mode 100644 index 0000000000000..f67bc7a45bfd7 --- /dev/null +++ b/Detectors/TRD/macros/convertRun2ToRun3Digits.C @@ -0,0 +1,241 @@ +#if !defined(__CINT__) || defined(__MAKECINT__) +#include <TClonesArray.h> + +#include <AliRunLoader.h> +#include <AliLoader.h> +#include <AliDataLoader.h> +#include <AliTreeLoader.h> +#include <AliTRDarrayADC.h> + +#include <iostream> + +#include "TH1F.h" +#include "TRDBase/Digit.h" +#include "TRDBase/Tracklet.h" + +#include <AliRawReaderRoot.h> +#include <AliRawReaderDateOnline.h> +#include <AliTRDrawStream.h> +#include <AliRawReader.h> +#endif + +using namespace o2; +using namespace trd; +using namespace std; + +// qa.root +// 18000283989033.808.root +// TRD.Digits.root +void convertRun2ToRun3Digits(TString qaOutPath = "", + TString rawDataInPath = "", + TString run2DigitsInPath = "", + TString run3DigitsOutPath = "trddigits.root", + int nRawEvents = 1000) +{ + vector<o2::trd::Digit> run3Digits; + vector<o2::trd::TriggerRecord> triggerRecords; + + TH1F* hAdc = new TH1F("hADC", "ADC spectrum", 1024, -0.5, 1023.5); + TH1F* hTBsum = new TH1F("hTBsum", "TBsum", 3000, -0.5, 2999.5); + + // convert raw data if path set + if (rawDataInPath != "") { + cout << "Converting RAW data..." << endl; + AliRawReader* reader; + if (rawDataInPath.Contains(".root")) { + cout << "[I] Reading with ROOT" << endl; + AliRawReaderRoot* readerDate = new AliRawReaderRoot(rawDataInPath); + readerDate->SelectEquipment(0, 1024, 1024); + readerDate->Select("TRD"); + //readerDate->SelectEvents(7); + reader = (AliRawReader*)readerDate; + + } else if (rawDataInPath.Contains(":")) { + cout << "[I] Reading DATE monitoring events" << endl; + AliRawReaderDateOnline* readerRoot = new AliRawReaderDateOnline(rawDataInPath); + readerRoot->SelectEquipment(0, 1024, 1041); + readerRoot->Select("TRD"); + //readerRoot->SelectEvents(7); + reader = (AliRawReader*)readerRoot; + } + + AliTRDdigitsManager* digitMan = new AliTRDdigitsManager; + digitMan->CreateArrays(); + + AliTRDrawStream* rawStream = new AliTRDrawStream(reader); + + TClonesArray trkl("AliTRDtrackletMCM"); + rawStream->SetTrackletArray(&trkl); + + int ievent = 0; + while (reader->NextEvent()) { + ievent++; + int eventtime = ievent * 12; + + if (ievent >= nRawEvents) + break; + + //digitMan->ResetArrays(); + + if (ievent % 10 == 0) { + cout << "Event " << ievent << endl; + } + + // hntrkl->Fill(trkl.GetEntries()); + while (rawStream->NextChamber(digitMan) >= 0) { + //hptphase->Fill(digMan->GetDigitsParam()->GetPretriggerPhase()); + } + + for (int det = 0; det < AliTRDCommonParam::kNdet; det++) { + AliTRDSignalIndex* idx = digitMan->GetIndexes(det); + + if (!idx) + continue; + if (!idx->HasEntry()) + continue; + + int row, col; + while (idx->NextRCIndex(row, col)) { + int tbsum = 0; + ArrayADC adctimes; + for (int timebin = 0; timebin < digitMan->GetDigits(det)->GetNtime(); timebin++) { + int adc = digitMan->GetDigits(det)->GetData(row, col, timebin); + hAdc->Fill(adc); + tbsum += adc; + + adctimes[timebin] = adc; + } + + if (tbsum > 0) { + run3Digits.push_back(o2::trd::Digit(det, row, col, adctimes, eventtime)); + } + + hTBsum->Fill(tbsum); + } + } + trkl.Clear(); + } + + delete rawStream; + if (reader) + delete reader; + } + + // convert run2 digits if path set + if (run2DigitsInPath != "") { + cout << "Converting Run2 digits..." << endl; + + TFile run2DigitsFile(run2DigitsInPath); + AliTRDdigitsManager* digitMan = new AliTRDdigitsManager; + digitMan->CreateArrays(); + + TIter next(run2DigitsFile.GetListOfKeys()); + + uint64_t triggerRecordsStart = 0; + int recordSize = 0; + int ievent = 0; + while (TObject* obj = next()) { + cout << "Processing " << obj->GetName() << endl; + + // eventTime needs to be some increasing integer + string eventNumber(obj->GetName(), 5, 3); + int eventTime = stoi(eventNumber) * 12; + + TTree* tr = (TTree*)run2DigitsFile.Get(Form("%s/TreeD", obj->GetName())); + + for (int det = 0; det < AliTRDCommonParam::kNdet; det++) { + digitMan->ClearArrays(det); + digitMan->ClearIndexes(det); + } + + digitMan->ReadDigits(tr); + + for (int det = 0; det < AliTRDCommonParam::kNdet; det++) { + if (!digitMan->GetDigits(det)) + continue; + + int sector = det / 30; + int stack = (det - sector * 30) / 6; + + digitMan->GetDigits(det)->Expand(); + + int nrows = AliTRDfeeParam::GetNrowC1(); + if (stack == 2) { + nrows = AliTRDfeeParam::GetNrowC0(); + } + + // cout << "det: " << det << " | " << "sector: " << sector << " | " << "stack: " << stack << " | " << "rows: " << nrows << endl; + + for (int row = 0; row < nrows; row++) { + for (int col = 0; col < AliTRDfeeParam::GetNcol(); col++) { + int tbsum = 0; + ArrayADC adctimes; + + for (int timebin = 0; timebin < digitMan->GetDigits(det)->GetNtime(); timebin++) { + + if (digitMan->GetDigits(det)->GetNtime() > 30) { + cout << "----!!! --- number of times is greater than 30" << endl; + } + + int adc = digitMan->GetDigitAmp(row, col, timebin, det); + adctimes[timebin] = adc; + + // this value seems to indicate no digit -> skip + if (adc == -7169) + continue; + + hAdc->Fill(adc); + tbsum += adc; + } + + if (tbsum > 0) { + run3Digits.push_back(o2::trd::Digit(det, row, col, adctimes, eventTime)); + } + + if (tbsum > 0) { + hTBsum->Fill(tbsum); + } + } + } + recordSize = run3Digits.size() - triggerRecordsStart; + triggerRecords.emplace_back(ievent, triggerRecordsStart, recordSize); + triggerRecordsStart = run3Digits.size(); + ievent++; + } + } + } + + // show and write QA + if (qaOutPath != "") { + hAdc->SetXTitle("ADC value"); + hAdc->SetYTitle("number of entries"); + + TCanvas* cnv_adc = new TCanvas("cnv_adc", "cnv_adc"); + cnv_adc->SetLogy(); + hAdc->Draw(); + + TCanvas* cnv_tbsum = new TCanvas("cnv_tbsum", "cnv_tbsum"); + cnv_adc->SetLogy(); + hTBsum->Draw(); + + TFile* outFile = new TFile(qaOutPath, "RECREATE"); + hAdc->Write(); + hTBsum->Write(); + + cout << "QA output written to: " << qaOutPath << endl; + } + + // write run3 digits + if (run3Digits.size() != 0) { + TFile* digitsFile = new TFile(run3DigitsOutPath, "RECREATE"); + TTree* digitTree = new TTree("o2sim", "run2 digits"); + std::vector<o2::trd::Digit>* run3pdigits = &run3Digits; + digitTree->Branch("TRDDigit", &run3pdigits); + digitTree->Branch("TriggerRecord", &triggerRecords); + digitTree->Fill(); + cout << run3Digits.size() << " run3 digits written to: " << run3DigitsOutPath << endl; + digitTree->Write(); + delete digitTree; + delete digitsFile; + } +} diff --git a/Detectors/TRD/simulation/CMakeLists.txt b/Detectors/TRD/simulation/CMakeLists.txt index 08ebaa02c5424..0292114818995 100644 --- a/Detectors/TRD/simulation/CMakeLists.txt +++ b/Detectors/TRD/simulation/CMakeLists.txt @@ -20,7 +20,8 @@ o2_add_library(TRDSimulation src/TrapConfig.cxx src/TrapConfigHandler.cxx src/TrapSimulator.cxx - PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::SimulationDataFormat O2::TRDBase) + src/Trap2CRU.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::SimulationDataFormat O2::TRDBase O2::DataFormatsTRD O2::DetectorsRaw) o2_target_root_dictionary(TRDSimulation HEADERS include/TRDSimulation/Detector.h @@ -29,6 +30,7 @@ o2_target_root_dictionary(TRDSimulation include/TRDSimulation/TRDSimParams.h include/TRDSimulation/TrapConfig.h include/TRDSimulation/TrapConfigHandler.h + include/TRDSimulation/Trap2CRU.h include/TRDSimulation/TrapSimulator.h) if (OpenMP_CXX_FOUND) @@ -37,3 +39,13 @@ if (OpenMP_CXX_FOUND) endif() o2_data_file(COPY data DESTINATION Detectors/TRD/simulation) + +o2_add_executable(trap2raw + COMPONENT_NAME trd + SOURCES src/trap2raw.cxx + PUBLIC_LINK_LIBRARIES O2::TRDSimulation + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils + O2::DataFormatsTRD + Boost::program_options) diff --git a/Detectors/TRD/simulation/include/TRDSimulation/Detector.h b/Detectors/TRD/simulation/include/TRDSimulation/Detector.h index b7fe798677867..a943d6758155b 100644 --- a/Detectors/TRD/simulation/include/TRDSimulation/Detector.h +++ b/Detectors/TRD/simulation/include/TRDSimulation/Detector.h @@ -60,7 +60,7 @@ namespace o2 namespace trd { -class TRDGeometry; +class Geometry; class TRsim; class Detector : public o2::base::DetImpl<Detector> @@ -111,7 +111,7 @@ class Detector : public o2::base::DetImpl<Detector> float mWion; // Ionization potential - TRDGeometry* mGeom = nullptr; + Geometry* mGeom = nullptr; template <typename Det> friend class o2::base::DetImpl; diff --git a/Detectors/TRD/simulation/include/TRDSimulation/Digitizer.h b/Detectors/TRD/simulation/include/TRDSimulation/Digitizer.h index 38555f1adb6c4..03b0203e438e7 100644 --- a/Detectors/TRD/simulation/include/TRDSimulation/Digitizer.h +++ b/Detectors/TRD/simulation/include/TRDSimulation/Digitizer.h @@ -16,8 +16,9 @@ #include "TRDBase/Calibrations.h" #include "TRDBase/Digit.h" #include "TRDBase/MCLabel.h" -#include "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDDiffAndTimeStructEstimator.h" +#include "TRDBase/CommonParam.h" +#include "TRDBase/DiffAndTimeStructEstimator.h" +#include "DataFormatsTRD/Constants.h" #include "MathUtils/RandomRing.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -32,17 +33,18 @@ namespace o2 namespace trd { -class TRDGeometry; -class TRDSimParam; -class TRDPadPlane; +class Geometry; +class SimParam; +class PadPlane; class TRDArraySignal; class PadResponse; struct SignalArray { - double firstTBtime; // first TB time - std::array<float, kTimeBins> signals{}; // signals - std::unordered_map<int, int> trackIds; // tracks Ids associated to the signal - std::vector<MCLabel> labels; // labels associated to the signal + double firstTBtime; // first TB time + std::array<float, constants::TIMEBINS> signals{}; // signals + std::unordered_map<int, int> trackIds; // tracks Ids associated to the signal + std::vector<MCLabel> labels; // labels associated to the signal + bool isDigit = false; // flag a signal converted to a digit }; using DigitContainer = std::vector<Digit>; @@ -57,6 +59,7 @@ class Digitizer void process(std::vector<HitType> const&, DigitContainer&, o2::dataformats::MCTruthContainer<MCLabel>&); void flush(DigitContainer&, o2::dataformats::MCTruthContainer<MCLabel>&); + void pileup(); void setEventTime(double timeNS) { mTime = timeNS; } void setEventID(int entryID) { mEventID = entryID; } void setSrcID(int sourceID) { mSrcID = sourceID; } @@ -65,11 +68,16 @@ class Digitizer int getEventID() const { return mEventID; } int getSrcID() const { return mSrcID; } + // Trigger parameters + static constexpr double READOUT_TIME = 3000; // the time the readout takes, as 30 TB = 3 micro-s. + static constexpr double DEAD_TIME = 200; // trigger deadtime, 2 micro-s + static constexpr double BUSY_TIME = READOUT_TIME + DEAD_TIME; // the time for which no new trigger can be received in nanoseconds + private: - TRDGeometry* mGeo = nullptr; // access to TRDGeometry + Geometry* mGeo = nullptr; // access to Geometry PadResponse* mPRF = nullptr; // access to PadResponse - TRDSimParam* mSimParam = nullptr; // access to TRDSimParam instance - TRDCommonParam* mCommonParam = nullptr; // access to TRDCommonParam instance + SimParam* mSimParam = nullptr; // access to SimParam instance + CommonParam* mCommonParam = nullptr; // access to CommonParam instance Calibrations* mCalib = nullptr; // access to Calibrations in CCDB // number of digitizer threads @@ -79,7 +87,7 @@ class Digitizer std::vector<math_utils::RandomRing<>> mGausRandomRings; // pre-generated normal distributed random numbers std::vector<math_utils::RandomRing<>> mFlatRandomRings; // pre-generated flat distributed random numbers std::vector<math_utils::RandomRing<>> mLogRandomRings; // pre-generated exp distributed random number - std::vector<TRDDiffusionAndTimeStructEstimator> mDriftEstimators; + std::vector<DiffusionAndTimeStructEstimator> mDriftEstimators; double mTime = 0.; // time in nanoseconds double mLastTime = -1; // negative, by default to flag the first event @@ -94,15 +102,9 @@ class Digitizer kEmbeddingEvent }; - // Trigger parameters - bool mTriggeredEvent = false; - static constexpr double READOUT_TIME = 3000; // the time the readout takes, as 30 TB = 3 micro-s. - static constexpr double DEAD_TIME = 200; // trigger deadtime, 2 micro-s - static constexpr double BUSY_TIME = READOUT_TIME + DEAD_TIME; // the time for which no new trigger can be received in nanoseconds - // Digitization parameters - static constexpr float AmWidth = TRDGeometry::amThick(); // Width of the amplification region - static constexpr float DrWidth = TRDGeometry::drThick(); // Width of the drift retion + static constexpr float AmWidth = Geometry::amThick(); // Width of the amplification region + static constexpr float DrWidth = Geometry::drThick(); // Width of the drift retion static constexpr float DrMin = -0.5 * AmWidth; // Drift + Amplification region static constexpr float DrMax = DrWidth + 0.5 * AmWidth; // Drift + Amplification region float mSamplingRate = 0; // The sampling rate @@ -113,21 +115,20 @@ class Digitizer int mMaxTimeBinsTRAP = 30; // Maximum number of time bins for processing adcs; should be read from the CCDB or the TRAP config // Digitization containers - std::vector<HitType> mHitContainer; // the container of hits in a given detector - std::vector<MCLabel> mMergedLabels; // temporary label container - std::array<SignalContainer, kNdet> mSignalsMapCollection; // container for caching signals over a timeframe - std::deque<std::array<SignalContainer, kNdet>> mPileupSignals; // container for piled up signals + std::vector<HitType> mHitContainer; // the container of hits in a given detector + std::vector<MCLabel> mMergedLabels; // temporary label container + std::array<SignalContainer, constants::MAXCHAMBER> mSignalsMapCollection; // container for caching signals over a timeframe + std::deque<std::array<SignalContainer, constants::MAXCHAMBER>> mPileupSignals; // container for piled up signals - void getHitContainerPerDetector(const std::vector<HitType>&, std::array<std::vector<HitType>, kNdet>&); + void getHitContainerPerDetector(const std::vector<HitType>&, std::array<std::vector<HitType>, constants::MAXCHAMBER>&); void setSimulationParameters(); // Digitization chain methods int triggerEventProcessing(DigitContainer&, o2::dataformats::MCTruthContainer<MCLabel>&); - void pileup(); SignalContainer addSignalsFromPileup(); void clearContainers(); bool convertHits(const int, const std::vector<HitType>&, SignalContainer&, int thread = 0); // True if hit-to-signal conversion is successful - bool convertSignalsToADC(const SignalContainer&, DigitContainer&, int thread = 0); // True if signal-to-ADC conversion is successful + bool convertSignalsToADC(SignalContainer&, DigitContainer&, int thread = 0); // True if signal-to-ADC conversion is successful void addLabel(const o2::trd::HitType& hit, std::vector<o2::trd::MCLabel>&, std::unordered_map<int, int>&); bool diffusion(float, float, float, float, float, float, double&, double&, double&, int thread = 0); // True if diffusion is applied successfully diff --git a/Detectors/TRD/simulation/include/TRDSimulation/Trap2CRU.h b/Detectors/TRD/simulation/include/TRDSimulation/Trap2CRU.h new file mode 100644 index 0000000000000..c1e672262b1bd --- /dev/null +++ b/Detectors/TRD/simulation/include/TRDSimulation/Trap2CRU.h @@ -0,0 +1,97 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/////////////////////////////////////////////////////////////////////////////// +// // +// TRD Trap2CRU class // +// Class to take the trap output that arrives at the cru and produce // +// the cru output. I suppose a cru simulator // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef ALICE_O2_TRD_TRAP2CRU_H +#define ALICE_O2_TRD_TRAP2CRU_H + +#include <string> +#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/LinkRecord.h" +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/Tracklet64.h" +//#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsRaw/RawFileWriter.h" + +namespace o2 +{ +namespace trd +{ + +class Trap2CRU +{ + static constexpr int NumberOfCRU = 36; + static constexpr int NumberOfHalfCRU = 72; + static constexpr int NumberOfFLP = 12; + static constexpr int CRUperFLP = 3; + static constexpr int WordSizeInBytes = 256; // word size in bits, everything is done in 256 bit blocks. + static constexpr int WordSize = 8; // 8 standard 32bit words. + static constexpr int NLinksPerHalfCRU = 15; // number of fibers per each half cru + static constexpr int TRDLinkID = 15; // hard coded link id. TODO give reason? + static constexpr uint32_t PaddWord = 0xeeee; // pad word to fill 256bit blocks or entire block for no data case. + static constexpr bool DebugDataWriting = false; //dump put very first vector of data to see if the thing going into the rawwriter is correct. + //TODO come back and change the mapping of 1077 channels to a lut addnd probably configurable. + // + public: + Trap2CRU() = default; + Trap2CRU(const std::string& outputDir, const std::string& inputFilename); + void readTrapData(const std::string& otuputDir, const std::string& inputFilename, int superPageSizeInB); + void convertTrapData(o2::trd::TriggerRecord const& TrigRecord); + // default for now will be file per half cru as per the files Guido did for us. + // TODO come back and give users a choice. + // void setFilePerLink(){mfileGranularity = mgkFilesPerLink;}; + // bool getFilePerLink(){return (mfileGranularity==mgkFilesPerLink);}; + // void setFilePerHalfCRU(){mfileGranularity = mgkFilesPerHalfCRU;}; + // bool getFilePerHalfCRU(){return (mfileGranularity==mgkFilesPerHalfCRU);}; // + int getVerbosity() { return mVerbosity; } + void setVerbosity(int verbosity) { mVerbosity = verbosity; } + void buildCRUPayLoad(); + int sortByORI(); + o2::raw::RawFileWriter& getWriter() { return mWriter; } + uint32_t buildCRUHeader(HalfCRUHeader& header, uint32_t bc, uint32_t halfcru, int startlinkrecord); + void linkSizePadding(uint32_t linksize, uint32_t& crudatasize, uint32_t& padding); + + private: + int mfileGranularity; /// per link or per half cru for each file + uint32_t mLinkID; + uint16_t mCruID; + uint64_t mFeeID; + uint32_t mEndPointID; + std::string mInputFilename; + std::string mOutputFilename; + int mVerbosity = 0; + HalfCRUHeader mHalfCRUHeader; + TrackletMCMHeader mTrackletMCMHeader; + TrackletMCMData mTrackletMCMData; + + std::vector<char> mRawData; // store for building data event for a single half cru + uint32_t mRawDataPos = 0; + TFile* mTrapRawFile; + TTree* mTrapRawTree; // incoming data tree + // locations to store the incoming data branches + std::vector<o2::trd::LinkRecord> mLinkRecords, *mLinkRecordsPtr = &mLinkRecords; + std::vector<o2::trd::TriggerRecord> mTriggerRecords, *mTriggerRecordsPtr = &mTriggerRecords; + std::vector<uint32_t> mTrapRawData, *mTrapRawDataPtr = &mTrapRawData; + + const o2::raw::HBFUtils& mSampler = o2::raw::HBFUtils::Instance(); + o2::raw::RawFileWriter mWriter{"TRD"}; + + ClassDefNV(Trap2CRU, 1); +}; + +} // end namespace trd +} // end namespace o2 +#endif diff --git a/Detectors/TRD/simulation/include/TRDSimulation/TrapConfig.h b/Detectors/TRD/simulation/include/TRDSimulation/TrapConfig.h index c4ea97ce69fbb..8f527eb3063d3 100644 --- a/Detectors/TRD/simulation/include/TRDSimulation/TrapConfig.h +++ b/Detectors/TRD/simulation/include/TRDSimulation/TrapConfig.h @@ -565,8 +565,9 @@ class TrapConfig if (index < mData.size()) { mData[index] = value; mValid[index] = valid; - } else + } else { LOG(debug) << "attempt to write data outside array with size : " << mData.size() << "and index of :" << index; + } } //next 3 functions are putrely for back ref cross checks to run2. int getAllocMode() { return (int)mAllocMode; } @@ -675,6 +676,9 @@ class TrapConfig const std::array<int, 3> mgkRegisterAddressBlockSize = {0x0400, 0x0200, 0x0004}; std::string mTrapConfigName; std::string mTrapConfigVersion; + void PrintDmemValue3(TrapConfig::TrapDmemWord* trapval, std::ofstream& output); + void PrintRegisterValue3(TrapConfig::TrapRegister* trapval, std::ofstream& output); + void DumpTrapConfig2File(std::string filename); private: // TrapConfig& operator=(const TrapConfig& rhs); // not implemented diff --git a/Detectors/TRD/simulation/include/TRDSimulation/TrapConfigHandler.h b/Detectors/TRD/simulation/include/TRDSimulation/TrapConfigHandler.h index fa23021d32a58..bd3738fbc1529 100644 --- a/Detectors/TRD/simulation/include/TRDSimulation/TrapConfigHandler.h +++ b/Detectors/TRD/simulation/include/TRDSimulation/TrapConfigHandler.h @@ -77,10 +77,6 @@ class TrapConfigHandler static const unsigned int mgkScsnCmdOri = 26; // SCSN command for ORI configuration static const unsigned int mgkScsnLTUparam = 27; // extended SCSN command for the LTU configuration - static const int mgkMCMperROBCol = 4; // MCMs per ROB column - static const int mgkPadsPerMCM = 18; // readout pads per MCM - static const int mgkMCMperROBRow = 4; // MCMs per ROB row - static const int mgkMaxLinkPairs = 4; // number of linkpairs used during configuration static const int mgkMcmlistSize = 256; // list of MCMs to which a value has to be written diff --git a/Detectors/TRD/simulation/include/TRDSimulation/TrapSimulator.h b/Detectors/TRD/simulation/include/TRDSimulation/TrapSimulator.h index 7d9fa7153d534..82ae9bde7795d 100644 --- a/Detectors/TRD/simulation/include/TRDSimulation/TrapSimulator.h +++ b/Detectors/TRD/simulation/include/TRDSimulation/TrapSimulator.h @@ -21,15 +21,17 @@ #include <iostream> #include <ostream> #include <fstream> +#include <gsl/span> -#include "TRDBase/Tracklet.h" #include "TRDBase/FeeParam.h" #include "TRDBase/Digit.h" #include "TRDSimulation/Digitizer.h" -#include "TRDSimulation/TrapConfigHandler.h" //TODO I think i can dump this. #include "TRDSimulation/TrapConfig.h" #include "TRDBase/MCLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/Constants.h" class TH2F; @@ -75,17 +77,25 @@ class TrapSimulator void unsetData() { mDataIsSet = false; - for (auto& fitreg : mFitReg) + for (auto& fitreg : mFitReg) { fitreg.ClearReg(); + } mNHits = 0; mADCFilled = 0; - for (auto& tmplabel : mADCLabels) + for (auto& tmplabel : mADCLabels) { tmplabel.clear(); // clear MC Labels sent in from the digits coming in. + } mTrackletLabels.clear(); // clear the stored labels. + mTrackletArray64.clear(); + for (auto& trackletdetail : mTrackletDetails) { + trackletdetail.clear(); + } }; - // void setData(int iadc, const std::vector<int>& adc); // set ADC data with array - void setData(int iadc, const ArrayADC& adc, std::vector<o2::MCCompLabel>& labels); // set ADC data with array - //void setData(int iadc, const ArrayADC& adc, gsl::span<o2::MCCompLabel,-1>& labels); // set ADC data with array + // set ADC data with array + // void setData(int iadc, const std::vector<int>& adc); + void setData(int iadc, const ArrayADC& adc, std::vector<o2::MCCompLabel>& labels); + // set ADC data with array + //void setData(int iadc, const ArrayADC& adc, gsl::span<o2::MCCompLabel,-1>& labels); void setBaselines(); // set the baselines as done in setDataByPad which is bypassed due to using setData in line above. void setData(int iadc, int it, int adc); // set ADC data void setDataFromDigitizerAndRun(std::vector<o2::trd::Digit>& data, o2::dataformats::MCTruthContainer<MCLabel>&); // data coming in manually from the the digitizer. @@ -117,9 +127,9 @@ class TrapSimulator std::string getTrklBranchName() const { return mTrklBranchName; } void setTrklBranchName(std::string name) { mTrklBranchName = name; } - int packData(std::vector<uint32_t>& buf); - int produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv = 0) const; // Produce raw data stream - Real data format - int produceTrackletStream(std::vector<uint32_t>& buf); // produce the tracklet stream for this MCM + int packData(std::vector<uint32_t>& rawdata, uint32_t offset); + int getRawStream(std::vector<uint32_t>& buf, uint32_t offset, unsigned int iEv = 0) const; // Produce raw data stream - Real data format + int getTrackletStream(std::vector<uint32_t>& buf, uint32_t offset); // produce the tracklet stream for this MCM // different stages of processing in the TRAP void filter(); // Apply digital filters for existing data (according to configuration) @@ -195,9 +205,11 @@ class TrapSimulator static const int mgkDmemAddrTimeOffset = 0xc3fe; // DMEM address of time offset t0 static const int mgkDmemAddrYcorr = 0xc3ff; // DMEM address of y correction (mis-alignment) static const int mgkMaxTracklets = 4; // maximum number of tracklet-words submitted per MCM (one per CPU) - //std::array<TrackletMCM> getTrackletArray() const { return mTrackletArray; } - std::vector<Tracklet>& getTrackletArray() { return mTrackletArray; } - void getTracklets(std::vector<Tracklet>& TrackletStore); // place the trapsim tracklets nto the incoming vector + static constexpr int mQ2Startbin = 3; // Start range of Q2, for now here. TODO pull from a revised TrapConfig? + static constexpr int mQ2Endbin = 5; // End range of Q2, also pull from a revised trapconfig at some point. + + std::vector<Tracklet64>& getTrackletArray64() { return mTrackletArray64; } + void getTracklet64s(std::vector<Tracklet64>& TrackletStore); // place the trapsim 64 bit tracklets nto the incoming vector o2::dataformats::MCTruthContainer<o2::MCCompLabel>& getTrackletLabels() { return mTrackletLabels; } bool checkInitialized() const; // Check whether the class is initialized @@ -228,9 +240,10 @@ class TrapSimulator mQtot = 0; mYpos = 0; } - }; //mHits[mgkNHitsMC]; - //std::array<Hit, 50> mHits; + }; + std::array<Hit, mgkNHitsMC> mHits{}; // was 100 in the run2 via fgkNHitsMC; + class FilterReg { public: @@ -259,6 +272,7 @@ class TrapSimulator int mNhits; // number of hits unsigned int mQ0; // charge accumulated in first window unsigned int mQ1; // charge accumulated in second window + unsigned int mQ2; // charge accumulated in third windows currently timebin 3 to 5 unsigned int mSumX; // sum x int mSumY; // sum y unsigned int mSumX2; // sum x**2 @@ -269,14 +283,70 @@ class TrapSimulator mNhits = 0; mQ0 = 0; mQ1 = 0; + mQ2 = 0; //TODO should this go here as its calculated differeintly in softwaren not hardware like the other 2? mSumX = 0; mSumY = 0; mSumX2 = 0; mSumY2 = 0; mSumXY = 0; } + void Print() + { + LOG(info) << "FitReg : "; + LOG(info) << "\t Q 0:1:2 : " << mQ0 << ":" << mQ1 << ":" << mQ2; + LOG(info) << "\t SumX:SumY:SumX2:SumY2:SumXY : " << mSumX << ":" << mSumY << ":" << mSumX2 << ":" << mSumY2 << ":" << mSumXY; + } + }; + std::array<FitReg, constants::NADCMCM> mFitReg{}; + //class to store the tracklet details that are not stored in tracklet64. + //used for later debugging purposes or in depth analysis of some part of tracklet creation or properties. + class TrackletDetail + { + public: + TrackletDetail() = default; + TrackletDetail(float slope, float position, int q0, int q1, int q2, std::array<int, 3> hits, float error) + { + mSlope = slope; + mPosition = position; + mError = error; + mCharges[0] = q0; + mCharges[1] = q1; + mCharges[2] = q2; + mPidHits = hits; + }; + ~TrackletDetail() = default; + std::array<int, 3> mPidHits; // no. of contributing clusters in each pid window. + std::array<int, 3> mCharges; // Q0,Q1,Q2, charges in each pid window. + float mSlope; // tracklet slope + float mPosition; // tracklet offset + float mError; // tracklet error + int mNClusters; // no. of clusters + std::vector<float> mResiduals; //[mNClusters] cluster to tracklet residuals + std::vector<float> mClsCharges; //[mNClusters] cluster charge + void clear() + { + mPidHits[0] = 0; + mPidHits[1] = 0; + mPidHits[2] = 0; + mCharges[0] = 0; + mCharges[1] = 0; + mCharges[2] = 0; + mSlope = 0; + mPosition = 0; + mError = 0; + mNClusters = 0; + mResiduals.clear(); + mClsCharges.clear(); + } + void setHits(std::array<int, 3> hits) { mPidHits = hits; } + + void setClusters(std::vector<float> res, std::vector<float> charges, int nclusters) + { + mResiduals = res; + mClsCharges = charges; + mNClusters = nclusters; + } }; - std::array<FitReg, FeeParam::mgkNadcMcm> mFitReg{}; protected: void setNTimebins(int ntimebins); // allocate data arrays corr. to the no. of timebins @@ -291,10 +361,11 @@ class TrapSimulator //TODO adcr adcf labels zerosupressionmap can all go into their own class. Refactor when stable. std::vector<int> mADCR; // Array with MCM ADC values (Raw, 12 bit) 2d with dimension mNTimeBin std::vector<int> mADCF; // Array with MCM ADC values (Filtered, 12 bit) 2d with dimension mNTimeBin - std::array<std::vector<o2::MCCompLabel>, FeeParam::mgkNadcMcm> mADCLabels{}; // MC Labels sent in from the digits coming in. + std::array<std::vector<o2::MCCompLabel>, constants::NADCMCM> mADCLabels{}; // MC Labels sent in from the digits coming in. std::vector<unsigned int> mMCMT; // tracklet word for one mcm/trap-chip - std::vector<Tracklet> mTrackletArray; // Array of TRDtrackletMCM which contains MC information in addition to the tracklet word + std::vector<Tracklet64> mTrackletArray64; // Array of 64 bit tracklets o2::dataformats::MCTruthContainer<o2::MCCompLabel> mTrackletLabels; + std::vector<TrackletDetail> mTrackletDetails; // store additional tracklet information for eventual debug output. std::vector<int> mZSMap; // Zero suppression map (1 dimensional projection) std::array<int, mgkNCPU> mFitPtr{}; // pointer to the tracklet to be calculated by CPU i @@ -302,14 +373,13 @@ class TrapSimulator std::string mTrklBranchName; // name of the tracklet branch to write to // Parameter classes - FeeParam* mFeeParam; // FEE parameters + FeeParam* mFeeParam; // FEE parameters, a singleton TrapConfig* mTrapConfig; // TRAP config - TrapConfigHandler mTrapConfigHandler; // CalOnlineGainTables mGainTable; - static const int NOfAdcPerMcm = FeeParam::mgkNadcMcm; + static const int NOfAdcPerMcm = constants::NADCMCM; - std::array<FilterReg, FeeParam::mgkNadcMcm> mInternalFilterRegisters; + std::array<FilterReg, constants::NADCMCM> mInternalFilterRegisters; int mADCFilled = 0; // stores bitpattern of fillted adc, for know when to fill with pure baseline, for use with setData(int iadc, const ArrayADC& adc); int mNHits; // Number of detected hits @@ -339,11 +409,20 @@ class TrapSimulator static int mgAddBaseline; // add baseline to the ADC values static bool mgStoreClusters; // whether to store all clusters in the tracklets + + bool mdebugStream = false; // whether or not to keep all the additional info for eventual dumping to a tree. + bool mDataIsSet = false; - Calibrations* mCalib; + Calibrations* mCalib; // connection to caiibrations class to pull in info from ccdb. + + std::array<TrackletMCMData, 3> mTracklets; + TrackletMCMHeader mMCMHeader; + static constexpr bool debugheaders = false; }; std::ostream& operator<<(std::ostream& os, const TrapSimulator& mcm); +std::ostream& operator<<(std::ostream& os, TrapSimulator::FitReg& fitreg); + } //namespace trd } //namespace o2 #endif diff --git a/Detectors/TRD/simulation/src/Detector.cxx b/Detectors/TRD/simulation/src/Detector.cxx index cc203d6842a6e..dfed6e56c5c12 100644 --- a/Detectors/TRD/simulation/src/Detector.cxx +++ b/Detectors/TRD/simulation/src/Detector.cxx @@ -8,28 +8,35 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "TRDSimulation/Detector.h" + +#include "TRDBase/CommonParam.h" +#include "TRDBase/Geometry.h" +#include "TRDSimulation/TRsim.h" +#include "DataFormatsTRD/Constants.h" + +#include "CommonUtils/ShmAllocator.h" +#include "SimulationDataFormat/Stack.h" +#include "FairVolume.h" +#include "FairRootManager.h" + #include <TGeoManager.h> #include <TVirtualMC.h> + #include <vector> #include <stdexcept> -#include "FairVolume.h" -#include "FairRootManager.h" -#include "SimulationDataFormat/Stack.h" -#include "CommonUtils/ShmAllocator.h" -#include "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDGeometry.h" -#include "TRDSimulation/Detector.h" -#include "TRDSimulation/TRsim.h" +#include <string> using namespace o2::trd; +using namespace o2::trd::constants; Detector::Detector(Bool_t active) : o2::base::DetImpl<Detector>("TRD", active) { mHits = o2::utils::createSimVector<HitType>(); - if (TRDCommonParam::Instance()->IsXenon()) { + if (CommonParam::Instance()->IsXenon()) { mWion = 23.53; // Ionization energy XeCO2 (85/15) - } else if (TRDCommonParam::Instance()->IsArgon()) { + } else if (CommonParam::Instance()->IsArgon()) { mWion = 27.21; // Ionization energy ArCO2 (82/18) } else { LOG(FATAL) << "Wrong gas mixture"; @@ -51,9 +58,9 @@ Detector::Detector(const Detector& rhs) mGasDensity(rhs.mGasDensity), mGeom(rhs.mGeom) { - if (TRDCommonParam::Instance()->IsXenon()) { + if (CommonParam::Instance()->IsXenon()) { mWion = 23.53; // Ionization energy XeCO2 (85/15) - } else if (TRDCommonParam::Instance()->IsArgon()) { + } else if (CommonParam::Instance()->IsArgon()) { mWion = 27.21; // Ionization energy ArCO2 (82/18) } else { LOG(FATAL) << "Wrong gas mixture"; @@ -85,41 +92,43 @@ bool Detector::ProcessHits(FairVolume* v) if ((!fMC->TrackCharge()) || fMC->IsTrackDisappeared()) { return false; } + fMC->SetMaxStep(0.1); // Should we optimize this value? // Inside sensitive volume ? bool drRegion = false; bool amRegion = false; - const TString cIdSensDr = "J"; - const TString cIdSensAm = "K"; - const TString cIdCurrent = fMC->CurrentVolName(); - if (cIdCurrent[1] == cIdSensDr) { - drRegion = true; + char idRegion; + int cIdChamber; + int r1 = std::sscanf(fMC->CurrentVolName(), "U%c%d", &idRegion, &cIdChamber); + if (r1 != 2) { + LOG(FATAL) << "Something went wrong with the geometry volume name " << fMC->CurrentVolName(); } - if (cIdCurrent[1] == cIdSensAm) { + if (idRegion == 'J') { + drRegion = true; + } else if (idRegion == 'K') { amRegion = true; - } - if (!drRegion && !amRegion) { + } else { return false; } - // Determine the dectector number - int sector, det; - // The plane number and chamber number - char cIdChamber[3]; - cIdChamber[0] = cIdCurrent[2]; - cIdChamber[1] = cIdCurrent[3]; - cIdChamber[2] = 0; - // The det-sec number (0 – 29) - const int idChamber = mGeom->getDetectorSec(atoi(cIdChamber)); - // The sector number (0 - 17), according to the standard coordinate system - TString cIdPath = fMC->CurrentVolPath(); - char cIdSector[3]; - cIdSector[0] = cIdPath[21]; - cIdSector[1] = cIdPath[22]; - cIdSector[2] = 0; - sector = atoi(cIdSector); - // The detector number (0 – 539) - det = mGeom->getDetector(mGeom->getLayer(idChamber), mGeom->getStack(idChamber), sector); + const int idChamber = mGeom->getDetectorSec(cIdChamber); + if (idChamber < 0 || idChamber > 29) { + LOG(FATAL) << "Chamber ID out of bounds"; + } + + int sector; + int r2 = std::sscanf(fMC->CurrentVolOffName(7), "BTRD%d", §or); + if (r2 != 1) { + LOG(FATAL) << "Something went wrong with the geometry volume name " << fMC->CurrentVolOffName(7); + } + if (sector < 0 || sector >= NSECTOR) { + LOG(FATAL) << "Sector out of bounds"; + } + // The detector number (0 - 539) + int det = mGeom->getDetector(mGeom->getLayer(idChamber), mGeom->getStack(idChamber), sector); + if (det < 0 || det >= MAXCHAMBER) { + LOG(FATAL) << "Detector number out of bounds"; + } // 0: InFlight 1: Entering 2: Exiting int trkStat = 0; @@ -178,7 +187,7 @@ bool Detector::ProcessHits(FairVolume* v) gGeoManager->MasterToLocal(pos, loc); // Go to the local coordinate system (locR, locC, locT) float locC = loc[0], locR = loc[1], locT = loc[2]; if (drRegion) { - locT = locT - 0.5 * (TRDGeometry::drThick() + TRDGeometry::amThick()); // Relative to middle of amplification region + locT = locT - 0.5 * (Geometry::drThick() + Geometry::amThick()); // Relative to middle of amplification region } addHit(xp, yp, zp, locC, locR, locT, tof, totalChargeDep, trackID, det, drRegion); stack->addHit(GetDetId()); @@ -220,7 +229,7 @@ void Detector::createTRhit(int det) sigma = muMy * mFoilDensity; if (sigma > 0.0) { absLength = gRandom->Exp(1.0 / sigma); - if (absLength < TRDGeometry::myThick()) { + if (absLength < Geometry::myThick()) { continue; } } else { @@ -229,9 +238,9 @@ void Detector::createTRhit(int det) // The absorbtion cross sections in the drift gas // Gas-mixture (Xe/CO2) double muNo = 0.0; - if (TRDCommonParam::Instance()->IsXenon()) { + if (CommonParam::Instance()->IsXenon()) { muNo = mTR->getMuXe(energyMeV); - } else if (TRDCommonParam::Instance()->IsArgon()) { + } else if (CommonParam::Instance()->IsArgon()) { muNo = mTR->getMuAr(energyMeV); } double muCO = mTR->getMuCO(energyMeV); @@ -243,7 +252,7 @@ void Detector::createTRhit(int det) // is deposited. if (sigma > 0.0) { absLength = gRandom->Exp(1.0 / sigma); - if (absLength > (TRDGeometry::drThick() + TRDGeometry::amThick())) { + if (absLength > (Geometry::drThick() + Geometry::amThick())) { continue; } } else { @@ -268,7 +277,7 @@ void Detector::createTRhit(int det) double loc[3] = {-99, -99, -99}; gGeoManager->MasterToLocal(pos, loc); // Go to the local coordinate system (locR, locC, locT) float locC = loc[0], locR = loc[1], locT = loc[2]; - locT = locT - 0.5 * (TRDGeometry::drThick() + TRDGeometry::amThick()); // Relative to middle of amplification region + locT = locT - 0.5 * (Geometry::drThick() + Geometry::amThick()); // Relative to middle of amplification region addHit(x, y, z, locC, locR, locT, tof, totalChargeDep, trackID, det, true); // All TR hits are in drift region stack->addHit(GetDetId()); } @@ -356,9 +365,9 @@ void Detector::createMaterials() float fac = 0.82; float dar = 0.00166; // at 20C float dgmAr = fac * dar + (1.0 - fac) * dco; - if (TRDCommonParam::Instance()->IsXenon()) { + if (CommonParam::Instance()->IsXenon()) { Mixture(53, "XeCO2", aXeCO2, zXeCO2, dgmXe, -3, wXeCO2); - } else if (TRDCommonParam::Instance()->IsArgon()) { + } else if (CommonParam::Instance()->IsArgon()) { LOG(INFO) << "Gas mixture: Ar C02 (80/20)"; Mixture(53, "ArCO2", aArCO2, zArCO2, dgmAr, -3, wArCO2); } else { @@ -489,10 +498,10 @@ void Detector::createMaterials() // Save the density values for the TRD absorbtion float dmy = 1.39; mFoilDensity = dmy; - if (TRDCommonParam::Instance()->IsXenon()) { + if (CommonParam::Instance()->IsXenon()) { mGasDensity = dgmXe; mGasNobleFraction = fxc; - } else if (TRDCommonParam::Instance()->IsArgon()) { + } else if (CommonParam::Instance()->IsArgon()) { mGasDensity = dgmAr; mGasNobleFraction = fac; } @@ -508,7 +517,7 @@ void Detector::ConstructGeometry() // to the geometry creation getMediumIDMappingAsVector(medmapping); - mGeom = TRDGeometry::instance(); + mGeom = Geometry::instance(); mGeom->createGeometry(medmapping); } diff --git a/Detectors/TRD/simulation/src/Digitizer.cxx b/Detectors/TRD/simulation/src/Digitizer.cxx index 6b9dfecc48675..3647c95a99bc2 100644 --- a/Detectors/TRD/simulation/src/Digitizer.cxx +++ b/Detectors/TRD/simulation/src/Digitizer.cxx @@ -14,9 +14,9 @@ #include "FairLogger.h" #include "DetectorsBase/GeometryManager.h" -#include "TRDBase/TRDGeometry.h" -#include "TRDBase/TRDSimParam.h" -#include "TRDBase/TRDPadPlane.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/SimParam.h" +#include "TRDBase/PadPlane.h" #include "TRDBase/PadResponse.h" #include "TRDSimulation/Digitizer.h" @@ -28,16 +28,17 @@ #endif using namespace o2::trd; +using namespace o2::trd::constants; using namespace o2::math_utils; // init method for late initialization void Digitizer::init() { - mGeo = TRDGeometry::instance(); + mGeo = Geometry::instance(); mGeo->createClusterMatrixArray(); // Requiered for chamberInGeometry() mPRF = new PadResponse(); // Pad response function initialization - mSimParam = TRDSimParam::Instance(); // Instance for simulation parameters - mCommonParam = TRDCommonParam::Instance(); // Instance for common parameters + mSimParam = SimParam::Instance(); // Instance for simulation parameters + mCommonParam = CommonParam::Instance(); // Instance for common parameters if (!mSimParam) { } if (!mCommonParam) { @@ -78,8 +79,8 @@ void Digitizer::setSimulationParameters() if (mSimParam->TRFOn()) { mTimeBinTRFend = ((int)(mSimParam->GetTRFhi() * mCommonParam->GetSamplingFrequency())) - 1; } - mMaxTimeBins = kTimeBins; // for signals, usually set at 30 tb = 3 microseconds - mMaxTimeBinsTRAP = kTimeBins; // for adcs; should be read from the CCDB or the TRAP config + mMaxTimeBins = TIMEBINS; // for signals, usually set at 30 tb = 3 microseconds + mMaxTimeBinsTRAP = TIMEBINS; // for adcs; should be read from the CCDB or the TRAP config mSamplingRate = mCommonParam->GetSamplingFrequency(); mElAttachProp = mSimParam->GetElAttachProp() / 100; } @@ -95,19 +96,21 @@ void Digitizer::flush(DigitContainer& digits, o2::dataformats::MCTruthContainer< LOG(WARN) << "TRD conversion of signals to digits failed"; } for (const auto& iter : smc) { - labels.addElements(labels.getIndexedSize(), iter.second.labels); + if (iter.second.isDigit) + labels.addElements(labels.getIndexedSize(), iter.second.labels); } } } else { // since we don't have any pileup signals just flush the signals for each chamber // we avoid flattening the array<map, ndets> to a single map - for (const auto& smc : mSignalsMapCollection) { + for (auto& smc : mSignalsMapCollection) { bool status = convertSignalsToADC(smc, digits); if (!status) { LOG(WARN) << "TRD conversion of signals to digits failed"; } for (const auto& iter : smc) { - labels.addElements(labels.getIndexedSize(), iter.second.labels); + if (iter.second.isDigit) + labels.addElements(labels.getIndexedSize(), iter.second.labels); } } } @@ -119,7 +122,7 @@ SignalContainer Digitizer::addSignalsFromPileup() int count = 0; SignalContainer addedSignalsMap; for (const auto& collection : mPileupSignals) { - for (int det = 0; det < kNdet; ++det) { + for (int det = 0; det < MAXCHAMBER; ++det) { const auto& signalMap = collection[det]; //--> a map with active pads only for this chamber for (const auto& signal : signalMap) { // loop over active pads only, if there is any const int& key = signal.first; @@ -185,33 +188,6 @@ void Digitizer::clearContainers() } } -int Digitizer::triggerEventProcessing(DigitContainer& digits, o2::dataformats::MCTruthContainer<MCLabel>& labels) -{ - if (mCurrentTriggerTime < 0 && mLastTime < 0) { - // very first event - mCurrentTriggerTime = mTime; - mLastTime = mTime; - return EventType::kFirstEvent; - } - if (mTime > mLastTime) { - if ((mTime - mCurrentTriggerTime) < BUSY_TIME) { - // send the signal containers to the pileup container, and do not change the current trigger time. - pileup(); - mLastTime = mTime; - return EventType::kPileupEvent; - } else { - // flush the digits: signals from the pileup container are converted to adcs - // digits and labels are produced, and the current trigger time is changed after the flush is completed - flush(digits, labels); - mCurrentTriggerTime = mTime; - mLastTime = mTime; - return EventType::kTriggerFired; - } - } else { - return EventType::kEmbeddingEvent; - } -} - void Digitizer::process(std::vector<HitType> const& hits, DigitContainer& digits, o2::dataformats::MCTruthContainer<MCLabel>& labels) { @@ -219,10 +195,8 @@ void Digitizer::process(std::vector<HitType> const& hits, DigitContainer& digits LOG(FATAL) << "TRD Calibration database not available"; } - int status = triggerEventProcessing(digits, labels); - // Get the a hit container for all the hits in a given detector then call convertHits for a given detector (0 - 539) - std::array<std::vector<HitType>, kNdet> hitsPerDetector; + std::array<std::vector<HitType>, MAXCHAMBER> hitsPerDetector; getHitContainerPerDetector(hits, hitsPerDetector); #ifdef WITH_OPENMP @@ -230,7 +204,7 @@ void Digitizer::process(std::vector<HitType> const& hits, DigitContainer& digits // Loop over all TRD detectors (in a parallel fashion) #pragma omp parallel for schedule(dynamic) #endif - for (int det = 0; det < kNdet; ++det) { + for (int det = 0; det < MAXCHAMBER; ++det) { #ifdef WITH_OPENMP const int threadid = omp_get_thread_num(); #else @@ -258,10 +232,10 @@ void Digitizer::process(std::vector<HitType> const& hits, DigitContainer& digits } } -void Digitizer::getHitContainerPerDetector(const std::vector<HitType>& hits, std::array<std::vector<HitType>, kNdet>& hitsPerDetector) +void Digitizer::getHitContainerPerDetector(const std::vector<HitType>& hits, std::array<std::vector<HitType>, MAXCHAMBER>& hitsPerDetector) { // - // Fill an array of size kNdet (540) + // Fill an array of size MAXCHAMBER (540) // The i-element of the array contains the hit collection for the i-detector // To be called once, before doing the loop over all detectors and process the hits // @@ -279,7 +253,7 @@ bool Digitizer::convertHits(const int det, const std::vector<HitType>& hits, Sig double padSignal[mNpad]; const double calExBDetValue = mCalib->getExB(det); // T * V/cm (check units) - const TRDPadPlane* padPlane = mGeo->getPadPlane(det); + const PadPlane* padPlane = mGeo->getPadPlane(det); const int layer = mGeo->getLayer(det); const float rowEndROC = padPlane->getRowEndROC(); const float row0 = padPlane->getRow0ROC(); @@ -481,7 +455,7 @@ float drawGaus(o2::math_utils::RandomRing<>& normaldistRing, float mu, float sig return mu + sigma * normaldistRing.getNextValue(); } -bool Digitizer::convertSignalsToADC(const SignalContainer& signalMapCont, DigitContainer& digits, int thread) +bool Digitizer::convertSignalsToADC(SignalContainer& signalMapCont, DigitContainer& digits, int thread) { // // Converts the sampled electron signals to ADC values for a given chamber @@ -494,7 +468,7 @@ bool Digitizer::convertSignalsToADC(const SignalContainer& signalMapCont, DigitC double baseline = mSimParam->GetADCbaseline() / adcConvert; // The electronics baseline in mV double baselineEl = baseline / convert; // The electronics baseline in electrons - for (const auto& signalMapIter : signalMapCont) { + for (auto& signalMapIter : signalMapCont) { const auto key = signalMapIter.first; const int det = getDetectorFromKey(key); const int row = getRowFromKey(key); @@ -521,6 +495,7 @@ bool Digitizer::convertSignalsToADC(const SignalContainer& signalMapCont, DigitC LOG(FATAL) << "Not a valid gain " << padgain << ", " << det << ", " << col << ", " << row; } + signalMapIter.second.isDigit = true; // flag the signal as digit // Loop over the all timebins in the ADC array const auto& signalArray = signalMapIter.second.signals; ArrayADC adcs{}; diff --git a/Detectors/TRD/simulation/src/TRDSimulationLinkDef.h b/Detectors/TRD/simulation/src/TRDSimulationLinkDef.h index b7c96d2753619..c95cf85cc2465 100644 --- a/Detectors/TRD/simulation/src/TRDSimulationLinkDef.h +++ b/Detectors/TRD/simulation/src/TRDSimulationLinkDef.h @@ -25,6 +25,12 @@ #pragma link C++ class o2::trd::TrapConfig::TrapDmemWord + ; #pragma link C++ class o2::trd::TrapConfig::TrapRegister + ; #pragma link C++ class o2::trd::TrapSimulator + ; +// dictionaries for the internal classes of TrapSimulator are needed for the debug output +#pragma link C++ class o2::trd::TrapSimulator::TrackletDetail + ; +#pragma link C++ class o2::trd::TrapSimulator::Hit + ; +#pragma link C++ class o2::trd::TrapSimulator::FitReg + ; +#pragma link C++ class o2::trd::TrapSimulator::FilterReg + ; +#pragma link C++ class o2::trd::Trap2CRU + ; #pragma link C++ class o2::trd::TRDSimParams + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trd::TRDSimParams> + ; diff --git a/Detectors/TRD/simulation/src/Trap2CRU.cxx b/Detectors/TRD/simulation/src/Trap2CRU.cxx new file mode 100644 index 0000000000000..ff2f3f6514d02 --- /dev/null +++ b/Detectors/TRD/simulation/src/Trap2CRU.cxx @@ -0,0 +1,311 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/////////////////////////////////////////////////////////////////////////////// +// // +// TRD Trap2CRU class // +// Class to take the trap output that arrives at the cru and produce // +// the cru output. A data mapping more than a cru simulator // +/////////////////////////////////////////////////////////////////////////////// + +#include <string> + +#include "Headers/RAWDataHeader.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/LinkRecord.h" +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/Constants.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsRaw/RawFileWriter.h" +#include "TRDSimulation/Trap2CRU.h" +#include "CommonUtils/StringUtils.h" +#include "TRDBase/CommonParam.h" +#include "TFile.h" +#include "TTree.h" +#include <TStopwatch.h> +#include <cassert> +#include <fstream> +#include <iostream> +#include <array> +#include <string> +#include <bitset> +#include <vector> +#include <gsl/span> + +using namespace o2::raw; + +namespace o2 +{ +namespace trd +{ + +Trap2CRU::Trap2CRU(const std::string& outputDir, const std::string& inputFilename) +{ + readTrapData(outputDir, inputFilename, 1024 * 1024); +} + +void Trap2CRU::readTrapData(const std::string& outputDir, const std::string& inputFilename, int superPageSizeInB) +{ + //set things up, read the file and then deligate to convertTrapdata to do the conversion. + // + mRawData.reserve(1024 * 1024); //TODO take out the hardcoded 1MB its supposed to come in from the options + LOG(debug) << "Trap2CRU::readTrapData"; + // data comes in index by event (triggerrecord) and link (linke record) both sequentially. + // first 15 links go to cru0a, second 15 links go to cru0b, 3rd 15 links go to cru1a ... first 90 links to flp0 and then repate for 12 flp + // then do next event + + // lets register our links + std::string prefix = outputDir; + if (!prefix.empty() && prefix.back() != '/') { + prefix += '/'; + } + + for (int link = 0; link < NumberOfHalfCRU; link++) { + // FeeID *was* 0xFEED, now is indicates the cru Supermodule, side (A/C) and endpoint. See RawData.cxx for details. + int supermodule = link / 4; + int endpoint = link / 2; + int side = link % 2 ? 1 : 0; + mFeeID = buildTRDFeeID(supermodule, side, endpoint); + mCruID = link / 2; + mEndPointID = link % 2 ? 1 : 0; //TODO figure out a value ... endpoint needs a rebase to PR4106 + mLinkID = TRDLinkID; + std::string trdside = link % 2 ? "c" : "a"; // the side of supermodule readout A or C, odd numbered CRU are A, even numbered CRU are C. + // filenmae structure of trd_cru_[CRU#]_[upper/lower].raw + std::string outputFilelink = o2::utils::concat_string(prefix, "trd_cru_", std::to_string(mCruID), "_", trdside, ".raw"); + mWriter.registerLink(mFeeID, mCruID, mLinkID, mEndPointID, outputFilelink); + } + mTrapRawFile = TFile::Open(inputFilename.data()); + assert(mTrapRawFile != nullptr); + LOG(debug) << "Trap Raw file open " << inputFilename; + mTrapRawTree = (TTree*)mTrapRawFile->Get("o2sim"); + + mTrapRawTree->SetBranchAddress("TrapLinkRecord", &mLinkRecordsPtr); // branch with the link records + mTrapRawTree->SetBranchAddress("RawTriggerRecord", &mTriggerRecordsPtr); // branch with the trigger records + mTrapRawTree->SetBranchAddress("TrapRaw", &mTrapRawDataPtr); // branch with the actual incoming data. + + for (int entry = 0; entry < mTrapRawTree->GetEntries(); entry++) { + mTrapRawTree->GetEntry(entry); + LOG(debug) << "Before Trigger Reord loop Event starts at:" << mTriggerRecords[0].getFirstEntry() << " and has " << mTriggerRecords[0].getNumberOfObjects() << " entries"; + uint32_t linkcount = 0; + for (auto trigger : mTriggerRecords) { + //get the event limits from TriggerRecord; + uint32_t eventstart = trigger.getFirstEntry(); + uint32_t eventend = trigger.getFirstEntry() + trigger.getNumberOfObjects(); + LOG(debug) << "Event starts at:" << eventstart << " and ends at :" << eventend; + convertTrapData(trigger); + } + } +} + +int Trap2CRU::sortByORI() +{ + // data comes in sorted by padcolum, a row of 8 trap chips. + // this is sadly not how the electronics is actually connected. + // we therefore need to resort the data according to the ORI link. + // TODO consider unpacking an entire event into memory into a per ori vector, then dump it all out. + // this is not for production running so the performance hit of sortin is probably ok ?? TODO ask someone to verify that. + return 1; +} + +void Trap2CRU::buildCRUPayLoad() +{ + // go through the data for the event in question, sort via above method, produce the raw stream for each cru. + // i.e. 30 link per cru, 3cru per flp. + // 30x [HalfCRUHeader, TrackletHCHeader0, [MCMHeader TrackletMCMData. .....] TrackletHCHeader1 ..... TrackletHCHeader30 ...] + // + // data must be padded into blocks of 256bit so on average 4 padding 32 bit words. +} + +void Trap2CRU::linkSizePadding(uint32_t linksize, uint32_t& crudatasize, uint32_t& padding) +{ + // all data must be 256 bit aligned (8x64bit). + // if zero the whole 256 bit must be padded (empty link) + // crudatasize is the size to be stored in the cruheader, i.e. units of 256bits. + // linksize is the incoming link size from the linkrecord, + // padding is of course the amount of padding in 32bit words. + uint32_t rem = 0; + if (linksize != 0) { + //data, so figure out padding cru word, the other case is simple, full padding if size=0 + rem = linksize % 8; + if (rem != 0) { + crudatasize = linksize / 8 + 1; + padding = 8 - rem; + } else { + + crudatasize = linksize / 8; // 32 bit word to 256 bit word. + padding = 0; + } + LOG(debug) << "We have data with linkdatasize=" << linksize << " with size number in header of:" << crudatasize << " padded with " << padding << " 32bit words"; + } else { + //linksize is zero so no data, pad fully. + crudatasize = 1; + padding = 8; + LOG(debug) << "We have data with linkdatasize=" << linksize << " with size number in header of:" << crudatasize << " padded with " << padding << " 32bit words"; + } + LOG(debug) << "linkSizePadding : CRUDATASIZE : " << crudatasize; +} + +uint32_t Trap2CRU::buildCRUHeader(HalfCRUHeader& header, uint32_t bc, uint32_t halfcru, int startlinkrecord) +{ + int bunchcrossing = bc; + int stopbits = 0x01; // do we care about this and eventtype in simulations? + int eventtype = 0x01; + int crurdhversion = 6; + int feeid = 0; //TODO must be in cruheader down the road, inside the reserved somewhere ... + int cruid = 0; //TODO """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + uint32_t crudatasize = 0; //link size in units of 256 bits. + int endpoint = halfcru % 2 ? 1 : 0; //TODO figure out a value ... endpoint needs a rebase to PR4106 + uint32_t padding = 0; + //setHalfCRUHeader(halfcruheader, rdhversion, bunchcrossing, stopbits, endpoint, eventtype); //TODO come back and pull this from somewhere. + setHalfCRUHeader(header, crurdhversion, bunchcrossing, stopbits, endpoint, eventtype, feeid, cruid); //TODO come back and pull this from somewhere. + // memset(&tmpLinkInfo[0],0,sizeof(tmpLinkInfo[0])*tmpLinkInfo.size()); + + // halfcruheader from the relevant mLinkRecords. + int linkrecord = startlinkrecord; + int totallinkdatasize = 0; //in units of 256bits + for (int link = 0; link < NLinksPerHalfCRU; link++) { + int linkid = link + halfcru * NLinksPerHalfCRU; // TODO this might have to change to a lut I dont think the mapping is linear. + int errors = 0; + int linksize = 0; // linkSizePadding will convert it to 1 for the no data case. + if (mLinkRecords[linkrecord].getLinkId() == linkid) { + linksize = mLinkRecords[linkrecord].getNumberOfObjects(); + // this can be done differently by keeping a pointer to halfcruheader and setting it after reading it all in and going back per link to set the size. + LOG(debug3) << "setting CRU HEADER for halfcru : " << halfcru << "and link : " << link << " contents" << header << ":" << link << ":" << linksize << ":" << errors; + linkrecord++; // increment locally for walking through linkrecords. + } + linkSizePadding(linksize, crudatasize, padding); + setHalfCRUHeaderLinkData(header, link, crudatasize, errors); // write one padding block for empty links. + totallinkdatasize += crudatasize; + } + return totallinkdatasize; +} + +void Trap2CRU::convertTrapData(o2::trd::TriggerRecord const& TrigRecord) +{ + + //build a HalfCRUHeader for this event/cru/endpoint + //loop over cru's + // loop over all half chambers, thankfully they data is sorted. + // check if current chamber has a link + // if not blank, else fill in data from link records + // dump data to rawwriter + //finished for event. this method is only called per event. + int currentlinkrecord = 0; + char* traprawdataptr = (char*)&mTrapRawData[0]; + for (int halfcru = 0; halfcru < NumberOfHalfCRU; halfcru++) { + int supermodule = halfcru / 4; + int endpoint = halfcru / 2; + int side = halfcru % 2 ? 1 : 0; + mFeeID = buildTRDFeeID(supermodule, side, endpoint); + mCruID = halfcru / 2; + mLinkID = TRDLinkID; + mEndPointID = halfcru % 2 ? 1 : 0; + + memset(&mRawData[0], 0, sizeof(mRawData[0]) * mRawData.size()); // zero the rawdata storage + int numberofdetectors = o2::trd::constants::MAXCHAMBER; + HalfCRUHeader halfcruheader; + //now write the cruheader at the head of all the data for this halfcru. + LOG(debug) << "cru before building cruheader for halfcru index : " << halfcru << " with contents \n" + << halfcruheader; + uint32_t totalhalfcrudatasize = buildCRUHeader(halfcruheader, TrigRecord.getBCData().bc, halfcru, currentlinkrecord); + + std::vector<char> rawdatavector(totalhalfcrudatasize * 32 + sizeof(halfcruheader)); // sum of link sizes + padding in units of bytes and some space for the header (512 bytes). + char* rawdataptr = rawdatavector.data(); + + //dumpHalfCRUHeader(halfcruheader); + memcpy(rawdataptr, (char*)&halfcruheader, sizeof(halfcruheader)); + std::array<uint64_t, 8> raw{}; + memcpy((char*)&raw[0], rawdataptr, sizeof(halfcruheader)); + for (int i = 0; i < 2; i++) { + int index = 4 * i; + LOG(debug) << "[1/2rawdaptr " << i << " " << std::hex << raw[index + 3] << " " << raw[index + 2] << " " << raw[index + 1] << " " << raw[index + 0]; + } + rawdataptr += sizeof(halfcruheader); + + int linkdatasize = 0; // in 32 bit words + int link = halfcru / 2; + for (int halfcrulink = 0; halfcrulink < NLinksPerHalfCRU; halfcrulink++) { + //links run from 0 to 14, so linkid offset is halfcru*15; + int linkid = halfcrulink + halfcru * NLinksPerHalfCRU; + LOG(debug) << "Currently checking for data on linkid : " << linkid << " from halfcru=" << halfcru << " and halfcrulink:" << halfcrulink << " ?? " << linkid << "==" << mLinkRecords[currentlinkrecord].getLinkId(); + int errors = 0; // put no errors in for now. + int size = 0; // in 32 bit words + int datastart = 0; // in 32 bit words + int dataend = 0; // in 32 bit words + uint32_t paddingsize = 0; // in 32 bit words + uint32_t crudatasize = 0; // in 256 bit words. + if (mLinkRecords[currentlinkrecord].getLinkId() == linkid) { + //this link has data in the stream. + LOG(debug) << "+++ We have data on linkid = " << linkid << " halfcrulink : " << halfcrulink; + linkdatasize = mLinkRecords[currentlinkrecord].getNumberOfObjects(); + datastart = mLinkRecords[currentlinkrecord].getFirstEntry(); + dataend = datastart + size; + LOG(debug) << "We have data on linkid = " << linkid << " and linksize : " << linkdatasize << " so :" << linkdatasize / 8 << " 256 bit words"; + currentlinkrecord++; + } else { + assert(mLinkRecords[currentlinkrecord].getLinkId() < linkid); + LOG(debug) << "---We do not have data on linkid = " << linkid << " halfcrulink : " << halfcrulink; + //blank data for this link + // put in a 1 256 bit word of data for the link and padd with 0xeeee x 8 + linkdatasize = 0; + paddingsize = 8; + } + // now copy data to rawdata, padding as and where needed. + // + linkSizePadding(linkdatasize, crudatasize, paddingsize); //TODO this can come out as we have already called it, but previously we have lost the #padding words, solve to remove. + + LOG(debug) << "WRITING " << crudatasize << " 256 bit data words to output stream"; + LOG(debug) << "setting CRY HEADER for " << halfcruheader << ":" << halfcrulink << ":" << crudatasize << ":" << errors; + // now pad .... + LOG(debug) << " now to pump data into the stream with : " << linkdatasize << " crudatasize:" << crudatasize << " paddingsize: " << paddingsize << " and rem:" << linkdatasize % 8; + char* olddataptr = rawdataptr; // store the old pointer so we can do some sanity checks for how far we advance. + //linkdatasize is the #of 32 bit words coming from the incoming tree. + //paddingsize is the number of padding words to add 0xeeee + uint32_t bytestocopy = linkdatasize * (sizeof(uint32_t)); + LOG(debug) << "copying " << bytestocopy << " bytes of link tracklet data at pos:" << std::hex << static_cast<void*>(rawdataptr); + memcpy(rawdataptr, traprawdataptr, bytestocopy); + //increment pointer + rawdataptr += bytestocopy; + traprawdataptr += bytestocopy; + //now for padding + uint16_t padbytes = paddingsize * sizeof(uint32_t); + LOG(debug) << "writing " << padbytes << " bytes of padding data at pos:" << std::hex << static_cast<void*>(rawdataptr); + memset(rawdataptr, 0xee, padbytes); + //increment pointer. + rawdataptr += padbytes; + if (padbytes + bytestocopy != crudatasize * 32) { + LOG(debug) << "something wrong with data size writing padbytes:" << padbytes << " bytestocopy : " << bytestocopy << " crudatasize:" << crudatasize; + } + LOG(debug3) << std::hex << " rawdataptr:" << static_cast<void*>(rawdataptr) << " traprawdataptr " << static_cast<void*>(traprawdataptr); + //sanity check for now: + if (((char*)rawdataptr - (char*)olddataptr) != crudatasize * 32) { // cru words are 8 uint32 and comparison is in bytes. + LOG(debug) << "according to pointer arithmatic we have added " << rawdataptr - olddataptr << "bytes from " << static_cast<void*>(rawdataptr) << "-" << static_cast<void*>(olddataptr) << " when we should have added " << crudatasize * 8 * 4 << " because crudatasize=" << crudatasize; + } + if (crudatasize != o2::trd::getlinkdatasize(halfcruheader, halfcrulink)) { + // we have written the wrong amount of data .... + LOG(debug) << "crudata is ! = get link data size " << crudatasize << "!=" << o2::trd::getlinkdatasize(halfcruheader, halfcrulink); + } + LOG(debug) << "copied " << crudatasize * 32 << "bytes to halfcrurawdata which now has size of " << rawdatavector.size() << " for " << link << ":" << endpoint; + } + LOG(debug) << "writing to " << std::hex << mFeeID << std::dec << " : " << mCruID << " : " << mLinkID << " : " << mEndPointID; + mWriter.addData(mFeeID, mCruID, mLinkID, mEndPointID, TrigRecord.getBCData(), rawdatavector); + if (DebugDataWriting) { + std::ofstream out2("crutestdumprawdatavector"); + out2.write(rawdatavector.data(), rawdatavector.size()); + out2.flush(); + halfcru = NumberOfHalfCRU; // exit loop after 1 half cru for now. + } + } +} + +} // end namespace trd +} // end namespace o2 diff --git a/Detectors/TRD/simulation/src/TrapConfig.cxx b/Detectors/TRD/simulation/src/TrapConfig.cxx index 1333e8c59f1e3..3090036aa2c41 100644 --- a/Detectors/TRD/simulation/src/TrapConfig.cxx +++ b/Detectors/TRD/simulation/src/TrapConfig.cxx @@ -12,13 +12,14 @@ // // // TRAP config // // // -// Author: J. Klein (Jochen.Klein@cern.ch) // -// Lots of mods by S. Murray (murrays@cern.ch) // +// Author: J. Klein (Jochen.Klein@cern.ch) (run2 version // +// S. Murray (murrays@cern.ch) // //////////////////////////////////////////////////////////////////////////// -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/FeeParam.h" #include "TRDSimulation/TrapConfig.h" +#include "DataFormatsTRD/Constants.h" #include <fairlogger/Logger.h> #include <fstream> @@ -28,6 +29,7 @@ using namespace std; using namespace o2::trd; +using namespace o2::trd::constants; bool TrapConfig::mgRegAddressMapInitialized = false; @@ -526,8 +528,9 @@ void TrapConfig::resetDmem() { // reset the data memory - for (int iAddr = 0; iAddr < mgkDmemWords; iAddr++) + for (int iAddr = 0; iAddr < mgkDmemWords; iAddr++) { mDmem[iAddr].reset(); + } } int TrapConfig::getTrapReg(TrapReg_t reg, int det, int rob, int mcm) @@ -622,8 +625,9 @@ bool TrapConfig::setDmem(int addr, unsigned int value, int det) if (!mDmem[addr].setValue(value, det)) { LOG(error) << "Problem writing to DMEM address 0x" << hex << std::setw(4) << addr; return false; - } else + } else { return true; + } } bool TrapConfig::setDmem(int addr, unsigned int value, int det, int rob, int mcm) @@ -639,8 +643,9 @@ bool TrapConfig::setDmem(int addr, unsigned int value, int det, int rob, int mcm if (!mDmem[addr].setValue(value, det, rob, mcm)) { LOG(error) << "Problem writing to DMEM address 0x" << hex << std::setw(4) << addr; return false; - } else + } else { return true; + } } unsigned int TrapConfig::getDmemUnsigned(int addr, int det, int rob, int mcm) @@ -663,9 +668,9 @@ bool TrapConfig::printTrapReg(TrapReg_t reg, int det, int rob, int mcm) // print the value stored in the given register // if it is individual a valid MCM has to be specified - if ((det >= 0 && det < kNdet) && - (rob >= 0 && rob < FeeParam::getNrobC1()) && - (mcm >= 0 && mcm < FeeParam::getNmcmRob() + 2)) { + if ((det >= 0 && det < MAXCHAMBER) && + (rob >= 0 && rob < NROBC1) && + (mcm >= 0 && mcm < NMCMROB + 2)) { LOG(info) << getRegName((TrapReg_t)reg) << "(" << std::setw(2) << getRegNBits((TrapReg_t)reg) << " bits) at 0x" << hex << std::setw(4) << getRegAddress((TrapReg_t)reg) << " is 0x" << hex << std::setw(8) << mRegisterValue[reg].getValue(det, rob, mcm) @@ -696,20 +701,21 @@ TrapConfig::TrapReg_t TrapConfig::getRegByAddress(int address) // get register by its address // used for reading of configuration data as sent to real FEE - if (address < mgkRegisterAddressBlockStart[0]) + if (address < mgkRegisterAddressBlockStart[0]) { return kLastReg; - else if (address < mgkRegisterAddressBlockStart[0] + mgkRegisterAddressBlockSize[0]) + } else if (address < mgkRegisterAddressBlockStart[0] + mgkRegisterAddressBlockSize[0]) { return mgRegAddressMap[address - mgkRegisterAddressBlockStart[0]]; - else if (address < mgkRegisterAddressBlockStart[1]) + } else if (address < mgkRegisterAddressBlockStart[1]) { return kLastReg; - else if (address < mgkRegisterAddressBlockStart[1] + mgkRegisterAddressBlockSize[1]) + } else if (address < mgkRegisterAddressBlockStart[1] + mgkRegisterAddressBlockSize[1]) { return mgRegAddressMap[address - mgkRegisterAddressBlockStart[1] + mgkRegisterAddressBlockSize[0]]; - else if (address < mgkRegisterAddressBlockStart[2]) + } else if (address < mgkRegisterAddressBlockStart[2]) { return kLastReg; - else if (address < mgkRegisterAddressBlockStart[2] + mgkRegisterAddressBlockSize[2]) + } else if (address < mgkRegisterAddressBlockStart[2] + mgkRegisterAddressBlockSize[2]) { return mgRegAddressMap[address - mgkRegisterAddressBlockStart[2] + mgkRegisterAddressBlockSize[1] + mgkRegisterAddressBlockSize[0]]; - else + } else { return kLastReg; + } } void TrapConfig::printMemDatx(ostream& os, int addr) @@ -755,10 +761,11 @@ void TrapConfig::printDatx(ostream& os, unsigned int addr, unsigned int data, in os << std::setw(5) << 10 << std::setw(8) << addr << std::setw(12) << data; - if (mcm == 127) + if (mcm == 127) { os << std::setw(8) << 127; - else + } else { os << std::setw(8) << FeeParam::aliToExtAli(rob, mcm); + } os << std::endl; } @@ -776,8 +783,9 @@ void TrapConfig::printVerify(ostream& os, int det, int rob, int mcm) } for (int iWord = 0; iWord < mgkDmemWords; ++iWord) { - if (getDmemUnsigned(mgkDmemStartAddress + iWord, det, rob, mcm) == 0) + if (getDmemUnsigned(mgkDmemStartAddress + iWord, det, rob, mcm) == 0) { continue; + } os << std::setw(5) << 9 << std::setw(8) << mgkDmemStartAddress + iWord << std::setw(12) << getDmemUnsigned(mgkDmemStartAddress + iWord, det, rob, mcm) @@ -979,8 +987,9 @@ void TrapConfig::TrapRegister::init(const char* name, int addr, int nBits, int r mAddr = addr; mNbits = nBits; mResetValue = resetValue; - } else + } else { LOG(fatal) << "Re-initialising an existing TRAP register "; + } } void TrapConfig::TrapRegister::initfromrun2(const char* name, int addr, int nBits, int resetValue) @@ -995,13 +1004,55 @@ void TrapConfig::TrapRegister::initfromrun2(const char* name, int addr, int nBit //LOG(fatal) << "Re-initialising an existing TRAP register"; } +void TrapConfig::PrintDmemValue3(TrapConfig::TrapDmemWord* trapval, std::ofstream& output) +{ + output << "\t AllocationMode : " << trapval->getAllocMode() << std::endl; + output << "\t Array size : " << trapval->getDataSize() << std::endl; + for (int dataarray = 0; dataarray < trapval->getDataSize(); dataarray++) { + output << "\t " << trapval->getDataRaw(dataarray) << " : valid : " << trapval->getValidRaw(dataarray) << std::endl; + } +} + +void TrapConfig::PrintRegisterValue3(TrapConfig::TrapRegister* trapval, std::ofstream& output) +{ + output << "\t AllocationMode : " << trapval->getAllocMode() << std::endl; + output << "\t Array size : " << trapval->getDataSize() << std::endl; + for (int dataarray = 0; dataarray < trapval->getDataSize(); dataarray++) { + output << "\t " << trapval->getDataRaw(dataarray) << " : valid : " << trapval->getValidRaw(dataarray) << std::endl; + } +} + +void TrapConfig::DumpTrapConfig2File(std::string filename) +{ + std::ofstream outfile(filename); + outfile << "Trap Registers : " << std::endl; + for (int regvalue = 0; regvalue < TrapConfig::kLastReg; regvalue++) { + outfile << " Trap : " << mRegisterValue[regvalue].getName() + << " at : 0x " << std::hex << mRegisterValue[regvalue].getAddr() << std::dec + << " with nbits : " << mRegisterValue[regvalue].getNbits() + << " and reset value of : " << mRegisterValue[regvalue].getResetValue() << std::endl; + // now for the inherited AliTRDtrapValue members; + PrintRegisterValue3(&mRegisterValue[regvalue], outfile); + } + + // outfile << "done with regiser values now for dmemwords" << std::endl; + outfile << "DMEM Words : " << std::endl; + for (int dmemwords = 0; dmemwords < TrapConfig::mgkDmemWords; dmemwords++) { + // copy fName, fAddr + // inherited from trapvalue : fAllocMode, fSize fData and fValid + // trapconfig->mDmem[dmemwords].mName= run2config->fDmem[dmemwords].fName; // this gets set on setting the address + outfile << "Name : " << mDmem[dmemwords].getName() << " :address : " << mDmem[dmemwords].getAddress() << std::endl; + PrintDmemValue3(&mDmem[dmemwords], outfile); + } +} + void TrapConfig::configureOnlineGains() { // we dont want to do this anymore .... but here for future reference. /* if (hasOnlineFilterGain()) { - const int nDets = kNdet; - const int nMcms = TRDGeometry::MCMmax(); - const int nChs = TRDGeometry::ADCmax(); + const int nDets = MAXCHAMBER; + const int nMcms = Geometry::MCMmax(); + const int nChs = Geometry::ADCmax(); for (int ch = 0; ch < nChs; ++ch) { TrapConfig::TrapReg_t regFGAN = (TrapConfig::TrapReg_t)(TrapConfig::kFGA0 + ch); @@ -1011,10 +1062,10 @@ void TrapConfig::configureOnlineGains() } for (int iDet = 0; iDet < nDets; ++iDet) { - //const int MaxRows = TRDGeometry::getStack(iDet) == 2 ? FeeParam::mgkNrowC0 : FeeParam::mgkNrowC1; - int MaxCols = FeeParam::mgkNcol; + //const int MaxRows = Geometry::getStack(iDet) == 2 ? NROWC0 : NROWC1; + int MaxCols = NCOLUMN; // CalOnlineGainTableROC gainTbl = mGainTable.getGainTableROC(iDet); - const int nRobs = TRDGeometry::getStack(iDet) == 2 ? TRDGeometry::ROBmaxC0() : TRDGeometry::ROBmaxC1(); + const int nRobs = Geometry::getStack(iDet) == 2 ? Geometry::ROBmaxC0() : Geometry::ROBmaxC1(); for (int rob = 0; rob < nRobs; ++rob) { for (int mcm = 0; mcm < nMcms; ++mcm) { diff --git a/Detectors/TRD/simulation/src/TrapConfigHandler.cxx b/Detectors/TRD/simulation/src/TrapConfigHandler.cxx index 3aa22a9b9fefa..e0c144dd11ec3 100644 --- a/Detectors/TRD/simulation/src/TrapConfigHandler.cxx +++ b/Detectors/TRD/simulation/src/TrapConfigHandler.cxx @@ -22,18 +22,20 @@ #include "TRDBase/FeeParam.h" #include "TRDBase/Tracklet.h" -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "TRDBase/CalOnlineGainTables.h" #include "TRDBase/PadResponse.h" #include "TRDSimulation/TrapConfigHandler.h" #include "TRDSimulation/TrapConfig.h" #include "TRDSimulation/TrapSimulator.h" +#include "DataFormatsTRD/Constants.h" #include "TMath.h" #include "TGeoMatrix.h" #include "TGraph.h" using namespace std; using namespace o2::trd; +using namespace o2::trd::constants; TrapConfigHandler::TrapConfigHandler(TrapConfig* cfg) : mFeeParam(), mRestrictiveMask((0x3ffff << 11) | (0x1f << 6) | 0x3f), mTrapConfig(cfg), mGtbl() { @@ -52,8 +54,9 @@ void TrapConfigHandler::init() // I/O configuration which we don't care about mTrapConfig->setTrapRegAlloc(TrapConfig::kSEBDOU, TrapConfig::kAllocNone); // position look-up table by layer - for (int iBin = 0; iBin < 128; iBin++) + for (int iBin = 0; iBin < 128; iBin++) { mTrapConfig->setTrapRegAlloc((TrapConfig::TrapReg_t)(TrapConfig::kTPL00 + iBin), TrapConfig::kAllocByLayer); + } // ... individual mTrapConfig->setTrapRegAlloc(TrapConfig::kC14CPUA, TrapConfig::kAllocByMCM); mTrapConfig->setTrapRegAlloc(TrapConfig::kC15CPUA, TrapConfig::kAllocByMCM); @@ -62,35 +65,36 @@ void TrapConfigHandler::init() for (int iAddr = TrapConfig::mgkDmemStartAddress; iAddr < (TrapConfig::mgkDmemWords + TrapConfig::mgkDmemStartAddress); iAddr++) { - if (iAddr == TrapSimulator::mgkDmemAddrDeflCorr) + if (iAddr == TrapSimulator::mgkDmemAddrDeflCorr) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocByMCMinSM); - else if (iAddr == TrapSimulator::mgkDmemAddrNdrift) + } else if (iAddr == TrapSimulator::mgkDmemAddrNdrift) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocByDetector); - else if ((iAddr >= TrapSimulator::mgkDmemAddrDeflCutStart) && (iAddr <= TrapSimulator::mgkDmemAddrDeflCutEnd)) + } else if ((iAddr >= TrapSimulator::mgkDmemAddrDeflCutStart) && (iAddr <= TrapSimulator::mgkDmemAddrDeflCutEnd)) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocByMCMinSM); - else if ((iAddr >= TrapSimulator::mgkDmemAddrTrackletStart) && (iAddr <= TrapSimulator::mgkDmemAddrTrackletEnd)) + } else if ((iAddr >= TrapSimulator::mgkDmemAddrTrackletStart) && (iAddr <= TrapSimulator::mgkDmemAddrTrackletEnd)) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocByMCM); - else if ((iAddr >= TrapSimulator::mgkDmemAddrLUTStart) && (iAddr <= TrapSimulator::mgkDmemAddrLUTEnd)) + } else if ((iAddr >= TrapSimulator::mgkDmemAddrLUTStart) && (iAddr <= TrapSimulator::mgkDmemAddrLUTEnd)) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocGlobal); - else if (iAddr == TrapSimulator::mgkDmemAddrLUTcor0) + } else if (iAddr == TrapSimulator::mgkDmemAddrLUTcor0) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocByMCMinSM); - else if (iAddr == TrapSimulator::mgkDmemAddrLUTcor1) + } else if (iAddr == TrapSimulator::mgkDmemAddrLUTcor1) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocByMCMinSM); - else if (iAddr == TrapSimulator::mgkDmemAddrLUTnbins) + } else if (iAddr == TrapSimulator::mgkDmemAddrLUTnbins) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocGlobal); - else if (iAddr == TrapSimulator::mgkDmemAddrLUTLength) + } else if (iAddr == TrapSimulator::mgkDmemAddrLUTLength) { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocGlobal); - else + } else { mTrapConfig->setDmemAlloc(iAddr, TrapConfig::kAllocGlobal); + } } mFeeParam = FeeParam::instance(); } @@ -215,10 +219,11 @@ int TrapConfigHandler::loadConfig() } for (int iBin = 0; iBin < 128; iBin++) { int corr = (int)(gr.Eval(iBin)) - iBin; - if (corr < 0) + if (corr < 0) { corr = 0; - else if (corr > 31) + } else if (corr > 31) { corr = 31; + } for (int iStack = 0; iStack < 540 / 6; iStack++) { mTrapConfig->setTrapReg((TrapConfig::TrapReg_t)(TrapConfig::kTPL00 + iBin), corr, 6 * iStack + iLayer); } @@ -276,8 +281,8 @@ int TrapConfigHandler::loadConfig(std::string filename) if (cmd != 999 && addr != -1 && extali != -1) { if (cmd == mgkScsnCmdWrite) { - for (int det = 0; det < kNdet; det++) { - unsigned int rocpos = (1 << (TRDGeometry::getSector(det) + 11)) | (1 << (TRDGeometry::getStack(det) + 6)) | (1 << TRDGeometry::getLayer(det)); + for (int det = 0; det < MAXCHAMBER; det++) { + unsigned int rocpos = (1 << (Geometry::getSector(det) + 11)) | (1 << (Geometry::getStack(det) + 6)) | (1 << Geometry::getLayer(det)); LOG(debug) << "checking restriction: mask=0x" << hex << std::setw(8) << mRestrictiveMask << " rocpos=0x" << hex << std::setw(8) << rocpos; if ((mRestrictiveMask & rocpos) == rocpos) { LOG(debug) << "match:" @@ -304,8 +309,8 @@ int TrapConfigHandler::loadConfig(std::string filename) else if (cmd == mgkScsnCmdSetHC) { int fullVersion = ((data & 0x7F00) >> 1) | (data & 0x7f); - for (int iDet = 0; iDet < kNdet; iDet++) { - int smls = (TRDGeometry::getSector(iDet) << 6) | (TRDGeometry::getLayer(iDet) << 3) | TRDGeometry::getStack(iDet); + for (int iDet = 0; iDet < MAXCHAMBER; iDet++) { + int smls = (Geometry::getSector(iDet) << 6) | (Geometry::getLayer(iDet) << 3) | Geometry::getStack(iDet); for (int iRob = 0; iRob < 8; iRob++) { // HC mergers @@ -383,7 +388,7 @@ void TrapConfigHandler::processLTUparam(int dest, int addr, unsigned int data) switch (dest) { case 0: // set the parameters in TrapConfig - for (int det = 0; det < kNdet; det++) { + for (int det = 0; det < MAXCHAMBER; det++) { configureDyCorr(det); configureDRange(det); // deflection range configureNTimebins(det); // timebins in the drift region @@ -454,7 +459,7 @@ void TrapConfigHandler::configureDyCorr(int det) return; } - int nRobs = TRDGeometry::getStack(det) == 2 ? 6 : 8; + int nRobs = Geometry::getStack(det) == 2 ? 6 : 8; for (int r = 0; r < nRobs; r++) { for (int m = 0; m < 16; m++) { @@ -479,7 +484,7 @@ void TrapConfigHandler::configureDRange(int det) return; } - int nRobs = TRDGeometry::getStack(det) == 2 ? 6 : 8; + int nRobs = Geometry::getStack(det) == 2 ? 6 : 8; int dyMinInt; int dyMaxInt; @@ -547,24 +552,26 @@ void TrapConfigHandler::configurePIDcorr(int det) unsigned int cor1; int readoutboard; int mcm; - // int nRobs = TRDGeometry::getStack(det) == 2 ? 6 : 8; + // int nRobs = Geometry::getStack(det) == 2 ? 6 : 8; int MaxRows; FeeParam* feeparam = FeeParam::instance(); - if (TRDGeometry::getStack(det) == 2) - MaxRows = FeeParam::mgkNrowC0; - else - MaxRows = FeeParam::mgkNrowC1; - int MaxCols = FeeParam::mgkNcol; + if (Geometry::getStack(det) == 2) { + MaxRows = NROWC0; + } else { + MaxRows = NROWC1; + } + int MaxCols = NCOLUMN; for (int row = 0; row < MaxRows; row++) { //TODO put this back to rob/mcm and not row/col as done in TrapSimulator for (int col = 0; col < MaxCols; col++) { readoutboard = feeparam->getROBfromPad(row, col); mcm = feeparam->getMCMfromPad(row, col); int dest = 1 << 10 | readoutboard << 7 | mcm; //TODO impelment a method for determining if gaintables are valid, used to be if pointer was valid. - if (mGtbl.getMCMGain(det, row, col) != 0.0) //TODO check this logic there might be problems here. + if (mGtbl.getMCMGain(det, row, col) != 0.0) { //TODO check this logic there might be problems here. mFeeParam->getCorrectionFactors(det, readoutboard, mcm, 9, cor0, cor1, mGtbl.getMCMGain(det, row, col)); - else + } else { mFeeParam->getCorrectionFactors(det, readoutboard, mcm, 9, cor0, cor1); + } addValues(det, mgkScsnCmdWrite, dest, addrLUTcor0, cor0); addValues(det, mgkScsnCmdWrite, dest, addrLUTcor1, cor1); } @@ -586,7 +593,7 @@ bool TrapConfigHandler::addValues(unsigned int det, unsigned int cmd, unsigned i } TrapConfig::TrapReg_t mcmReg = mTrapConfig->getRegByAddress(addr); - int rocType = TRDGeometry::getStack(det) == 2 ? 0 : 1; + int rocType = Geometry::getStack(det) == 2 ? 0 : 1; static const int mcmListSize = 40; // 40 is more or less arbitrary int mcmList[mcmListSize]; @@ -618,10 +625,11 @@ bool TrapConfigHandler::addValues(unsigned int det, unsigned int cmd, unsigned i if (FeeParam::extAliToAli(extali, linkPair, rocType, mcmList, mcmListSize) != 0) { int i = 0; while (i < mcmListSize && mcmList[i] != -1) { - if (mcmList[i] == 127) + if (mcmList[i] == 127) { mTrapConfig->setDmem(addr, data, det, 0, 127); - else + } else { mTrapConfig->setDmem(addr, data, det, mcmList[i] >> 7, mcmList[i] & 0x7f); + } i++; } } diff --git a/Detectors/TRD/simulation/src/TrapSimulator.cxx b/Detectors/TRD/simulation/src/TrapSimulator.cxx index b166661faadd2..11d1a0f51b9bc 100644 --- a/Detectors/TRD/simulation/src/TrapSimulator.cxx +++ b/Detectors/TRD/simulation/src/TrapSimulator.cxx @@ -17,13 +17,10 @@ // // /////////////////////////////////////////////////////////////////////////////// -//#include "TTreeStream.h" - -#include "TRDBase/TRDSimParam.h" -#include "TRDBase/TRDCommonParam.h" -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/SimParam.h" +#include "TRDBase/CommonParam.h" +#include "TRDBase/Geometry.h" #include "TRDBase/FeeParam.h" -#include "TRDBase/Tracklet.h" #include "TRDBase/CalOnlineGainTables.h" #include "TRDSimulation/TrapConfigHandler.h" #include "TRDSimulation/TrapConfig.h" @@ -35,6 +32,8 @@ #include "TRDSimulation/Digitizer.h" #include <SimulationDataFormat/MCCompLabel.h> #include <SimulationDataFormat/MCTruthContainer.h> +#include <DataFormatsTRD/RawData.h> +#include <DataFormatsTRD/Tracklet64.h> #include <iostream> #include <iomanip> @@ -50,11 +49,13 @@ #include "TRandom3.h" #include <ostream> #include <fstream> +#include <numeric> using namespace o2::trd; using namespace std; +using namespace o2::trd::constants; -#define DEBUGTRAP 1 +#define infoTRAP 1 bool TrapSimulator::mgApplyCut = true; int TrapSimulator::mgAddBaseline = 0; @@ -67,7 +68,6 @@ TrapSimulator::TrapSimulator() { // // TrapSimulator default constructor - // By default, nothing is initialized. // It is necessary to issue init before use. mFitPtr[0] = 0; @@ -85,7 +85,7 @@ void TrapSimulator::init(TrapConfig* trapconfig, int det, int robPos, int mcmPos // memory is allocated in the first initialization // // - LOG(debug4) << " : trap sim is at : 0x" << hex << trapconfig; + LOG(debug) << " : trap sim is at : 0x" << hex << trapconfig; if (!mInitialized) { mFeeParam = FeeParam::instance(); mTrapConfig = trapconfig; @@ -98,16 +98,16 @@ void TrapSimulator::init(TrapConfig* trapconfig, int det, int robPos, int mcmPos if (!mInitialized) { mNTimeBin = mTrapConfig->getTrapReg(TrapConfig::kC13CPUA, mDetector, mRobPos, mMcmPos); - mZSMap.resize(FeeParam::getNadcMcm()); + mZSMap.resize(NADCMCM); // tracklet calculation - //now a 25 slot array mFitReg.resize(FeeParam::getNadcMcm()); //TODO for now this is constant size in an array not a vector + //now a 25 slot array mFitReg.resize(NADCMCM); //TODO for now this is constant size in an array not a vector // mTrackletArray-.resize(mgkMaxTracklets); //now a 100 slot array as per run2 mHits.resize(50); mMCMT.resize(mgkMaxTracklets); - mADCR.resize(mNTimeBin * FeeParam::getNadcMcm()); - mADCF.resize(mNTimeBin * FeeParam::getNadcMcm()); + mADCR.resize(mNTimeBin * NADCMCM); + mADCF.resize(mNTimeBin * NADCMCM); } mInitialized = true; @@ -122,8 +122,9 @@ void TrapSimulator::reset() // Resets the data values and internal filter registers // by re-initialising them - if (!checkInitialized()) + if (!checkInitialized()) { return; + } //clear the adc data memset(&mADCR[0], 0, sizeof(mADCR[0]) * mADCR.size()); @@ -136,10 +137,16 @@ void TrapSimulator::reset() mTrackletLabels.clear(); // as the name implies clear the stored labels mNHits = 0; - for (auto filterreg : mInternalFilterRegisters) + for (auto filterreg : mInternalFilterRegisters) { filterreg.ClearReg(); + } + // clear the tracklet detail information. + for (auto trackletdetail : mTrackletDetails) { + trackletdetail.clear(); + } + // Default unread, low active bit mask // Default unread, low active bit mask - memset(&mZSMap[0], 0, sizeof(mZSMap[0]) * FeeParam::getNadcMcm()); + memset(&mZSMap[0], 0, sizeof(mZSMap[0]) * NADCMCM); memset(&mMCMT[0], 0, sizeof(mMCMT[0]) * mgkMaxTracklets); //mDict1.clear(); //mDict2.clear(); @@ -183,8 +190,9 @@ std::ostream& o2::trd::operator<<(std::ostream& os, const TrapSimulator& mcm) // output implementation // no output for non-initialized MCM - if (!mcm.checkInitialized()) + if (!mcm.checkInitialized()) { return os; + } // ----- human-readable output ----- if (os.iword(TrapSimulator::mgkFormatIndex) == 0) { @@ -193,25 +201,27 @@ std::ostream& o2::trd::operator<<(std::ostream& os, const TrapSimulator& mcm) os << "----- Unfiltered ADC data (10 bit) -----" << std::endl; os << "ch "; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(5) << iChannel; + } os << std::endl; for (int iTimeBin = 0; iTimeBin < mcm.getNumberOfTimeBins(); iTimeBin++) { os << "tb " << std::setw(2) << iTimeBin << ":"; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(5) << (mcm.getDataRaw(iChannel, iTimeBin) >> mcm.mgkAddDigits); } os << std::endl; } os << "----- Filtered ADC data (10+2 bit) -----" << std::endl; os << "ch "; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(4) << iChannel << ((~mcm.getZeroSupressionMap(iChannel) != 0) ? "!" : " "); + } os << std::endl; for (int iTimeBin = 0; iTimeBin < mcm.getNumberOfTimeBins(); iTimeBin++) { os << "tb " << std::setw(2) << iTimeBin << ":"; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(4) << (mcm.getDataFiltered(iChannel, iTimeBin)) << (((mcm.getZeroSupressionMap(iChannel) & (1 << iTimeBin)) == 0) ? "!" : " "); } @@ -226,7 +236,7 @@ std::ostream& o2::trd::operator<<(std::ostream& os, const TrapSimulator& mcm) int addrStep = 0x80; for (int iTimeBin = 0; iTimeBin < mcm.getNumberOfTimeBins(); iTimeBin++) { - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(5) << 10 << std::setw(5) << addrOffset + iChannel * addrStep + iTimeBin << std::setw(5) << (mcm.getDataFiltered(iChannel, iTimeBin)) @@ -240,12 +250,12 @@ std::ostream& o2::trd::operator<<(std::ostream& os, const TrapSimulator& mcm) else if (os.iword(TrapSimulator::mgkFormatIndex) == 2) { int bufSize = 300; std::vector<uint32_t> buf; - buf.resize(bufSize); - - int bufLength = mcm.produceRawStream(buf); + buf.reserve(bufSize); + int bufLength = mcm.getRawStream(buf, 0); - for (int i = 0; i < bufLength; i++) + for (int i = 0; i < bufLength; i++) { std::cout << "0x" << std::hex << buf[i] << std::dec << std::endl; + } } @@ -263,8 +273,9 @@ void TrapSimulator::printFitRegXml(ostream& os) const bool tracklet = false; for (int cpu = 0; cpu < 4; cpu++) { - if (mFitPtr[cpu] != 31) + if (mFitPtr[cpu] != 31) { tracklet = true; + } } const char* const aquote{R"(")"}; const char* const cmdid{R"(" cmdid="0">)"}; @@ -309,7 +320,7 @@ void TrapSimulator::printFitRegXml(ostream& os) const void TrapSimulator::printTrackletsXml(ostream& os) const { // print tracklets in XML format - + //TODO FIX this to new format const char* const cmdid{R"(" cmdid="0">)"}; os << "<nginject>" << std::endl; os << "<ack roc=\"" << mDetector << cmdid << std::endl; @@ -319,6 +330,7 @@ void TrapSimulator::printTrackletsXml(ostream& os) const os << " <m mcm=\"" << mMcmPos << "\">" << std::endl; int pid, padrow, slope, offset; + //TODO take care of the 0th being the header, and cpu1,2,3 being 1 to 3 tracklets. for (int cpu = 0; cpu < 4; cpu++) { if (mMCMT[cpu] == 0x10001000) { pid = -1; @@ -353,7 +365,7 @@ void TrapSimulator::printAdcDatTxt(ostream& os) const os << "# MCM " << mMcmPos << " on ROB " << mRobPos << " in detector " << mDetector << std::endl; for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); ++iChannel) { + for (int iChannel = 0; iChannel < NADCMCM; ++iChannel) { os << std::setw(5) << (getDataRaw(iChannel, iTimeBin) >> mgkAddDigits); } os << std::endl; @@ -368,12 +380,13 @@ void TrapSimulator::printAdcDatHuman(ostream& os) const os << "----- Unfiltered ADC data (10 bit) -----" << std::endl; os << "ch "; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(5) << iChannel; + } os << std::endl; for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { os << "tb " << std::setw(2) << iTimeBin << ":"; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(5) << (getDataRaw(iChannel, iTimeBin) >> mgkAddDigits); } os << std::endl; @@ -381,13 +394,14 @@ void TrapSimulator::printAdcDatHuman(ostream& os) const os << "----- Filtered ADC data (10+2 bit) -----" << std::endl; os << "ch "; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(4) << iChannel << ((~mZSMap[iChannel] != 0) ? "!" : " "); + } os << std::endl; for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { os << "tb " << std::setw(2) << iTimeBin << ":"; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << std::setw(4) << (getDataFiltered(iChannel, iTimeBin)) << (((mZSMap[iChannel] & (1 << iTimeBin)) == 0) ? "!" : " "); } @@ -407,7 +421,7 @@ void TrapSimulator::printAdcDatXml(ostream& os) const os << " <ro-board rob=\"" << mRobPos << "\">" << std::endl; os << " <m mcm=\"" << mMcmPos << "\">" << std::endl; - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { os << " <ch chnr=\"" << iChannel << "\">" << std::endl; for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { os << "<tb>" << mADCF[iChannel * mNTimeBin + iTimeBin] / 4 << "</tb>"; @@ -435,17 +449,19 @@ void TrapSimulator::printAdcDatDatx(ostream& os, bool broadcast, int timeBinOffs int addrOffsetEBSIA = 0x20; for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { - for (int iChannel = 0; iChannel < FeeParam::getNadcMcm(); iChannel++) { + for (int iChannel = 0; iChannel < NADCMCM; iChannel++) { if ((iTimeBin < timeBinOffset) || (iTimeBin >= mNTimeBin + timeBinOffset)) { - if (broadcast == false) + if (broadcast == false) { mTrapConfig->printDatx(os, addrOffset + iChannel * addrStep + addrOffsetEBSIA + iTimeBin, 10, getRobPos(), getMcmPos()); - else + } else { mTrapConfig->printDatx(os, addrOffset + iChannel * addrStep + addrOffsetEBSIA + iTimeBin, 10, 0, 127); + } } else { - if (broadcast == false) + if (broadcast == false) { mTrapConfig->printDatx(os, addrOffset + iChannel * addrStep + addrOffsetEBSIA + iTimeBin, (getDataFiltered(iChannel, iTimeBin - timeBinOffset) / 4), getRobPos(), getMcmPos()); - else + } else { mTrapConfig->printDatx(os, addrOffset + iChannel * addrStep + addrOffsetEBSIA + iTimeBin, (getDataFiltered(iChannel, iTimeBin - timeBinOffset) / 4), 0, 127); + } } } os << std::endl; @@ -481,11 +497,12 @@ void TrapSimulator::setNTimebins(int ntimebins) // is needed (should not be the case for real data) LOG(fatal) << "setNTimebins(" << ntimebins << ") not implemented as we can no longer change the size of the ADC array"; - if (!checkInitialized()) + if (!checkInitialized()) { return; + } mNTimeBin = ntimebins; - // for( int iAdc = 0 ; iAdc < FeeParam::getNadcMcm(); iAdc++ ) { + // for( int iAdc = 0 ; iAdc < NADCMCM; iAdc++ ) { // delete [] mADCR[iAdc]; // delete [] mADCF[iAdc]; // mADCR[iAdc] = new int[mNTimeBin]; @@ -519,8 +536,9 @@ void TrapSimulator::noiseTest(int nsamples, int mean, int sigma, int inputGain, // same way as in normal simulation. // The functions produces four histograms with the values at the different stages. - if (!checkInitialized()) + if (!checkInitialized()) { return; + } std::string nameInputGain; std::string nameInputTail; @@ -581,17 +599,19 @@ void TrapSimulator::noiseTest(int nsamples, int mean, int sigma, int inputGain, valuep = filterPedestalNextSample(1, 0, ((int)value) << 2); - if (inputGain == 0) + if (inputGain == 0) { valueg = filterGainNextSample(1, ((int)value) << 2); - else + } else { valueg = filterGainNextSample(1, valuep); + } - if (inputTail == 0) + if (inputTail == 0) { valuet = filterTailNextSample(1, ((int)value) << 2); - else if (inputTail == 1) + } else if (inputTail == 1) { valuet = filterTailNextSample(1, valuep); - else + } else { valuet = filterTailNextSample(1, valueg); + } hfp->SetBinContent(i, valuep >> 2); hfg->SetBinContent(i, valueg >> 2); @@ -617,7 +637,7 @@ bool TrapSimulator::checkInitialized() const // // if (!mInitialized) - // LOG(debug4) << "TrapSimulator is not initialized but function other than Init() is called."; + // LOG(debug) << "TrapSimulator is not initialized but function other than Init() is called."; return mInitialized; } @@ -634,10 +654,11 @@ void TrapSimulator::print(int choice) const // have been performed. // Codacy wont let us use string choices, as it says we must use string::starts_with() instead of string::find() - if (!checkInitialized()) + if (!checkInitialized()) { return; + } - LOG(info) << "MCM " << mMcmPos << " on ROB " << mRobPos << " in detector " << mDetector; + LOG(debug) << "MCM " << mMcmPos << " on ROB " << mRobPos << " in detector " << mDetector; //std::string opt = option; if ((choice & PRINTRAW) != 0 || (choice & PRINTFILTERED) != 0) { @@ -645,16 +666,17 @@ void TrapSimulator::print(int choice) const } if ((choice & PRINTDETECTED) != 0) { - LOG(info) << "Found " << mNHits << " hits:"; + LOG(debug) << "Found " << mNHits << " hits:"; for (int iHit = 0; iHit < mNHits; iHit++) { - LOG(info) << "Hit " << std::setw(3) << iHit << " in timebin " << std::setw(2) << mHits[iHit].mTimebin << ", ADC " << std::setw(2) << mHits[iHit].mChannel << " has charge " << std::setw(3) << mHits[iHit].mQtot << " and position " << mHits[iHit].mYpos; + LOG(debug) << "Hit " << std::setw(3) << iHit << " in timebin " << std::setw(2) << mHits[iHit].mTimebin << ", ADC " << std::setw(2) << mHits[iHit].mChannel << " has charge " << std::setw(3) << mHits[iHit].mQtot << " and position " << mHits[iHit].mYpos; } } if ((choice & PRINTFOUND) != 0) { - LOG(info) << "Found Tracklets:"; - for (int iTrkl = 0; iTrkl < mTrackletArray.size(); iTrkl++) { - LOG(info) << "tracklet " << iTrkl << ": 0x" << hex << std::setw(8) << mTrackletArray[iTrkl].getTrackletWord(); + LOG(debug) << "Found Tracklets:"; + for (int iTrkl = 0; iTrkl < mTrackletArray64.size(); iTrkl++) { + LOG(debug) << "tracklet " << iTrkl << ": 0x" << hex << std::setw(32) << mTrackletArray64[iTrkl].getTrackletWord(); + LOG(debug) << mTrackletArray64[iTrkl]; } } } @@ -668,15 +690,16 @@ void TrapSimulator::draw(int choice, int index) // In addition to the ADC values: // PLOTHITS - plot hits // PLOTTRACKLETS - plot tracklets - if (!checkInitialized()) + if (!checkInitialized()) { return; - TFile* rootfile = new TFile(Form("Spectra_%i.root", index), "RECREATE"); - TCanvas* c1 = new TCanvas("c1"); + } + TFile* rootfile = new TFile("trdtrackletplots.root", "UPDATE"); + TCanvas* c1 = new TCanvas(Form("canvas_%i_%i:%i:%i_%i", index, mDetector, mRobPos, mMcmPos, (int)mTrackletArray64.size())); TH2F* hist = new TH2F(Form("mcmdata_%i", index), Form("Data of MCM %i on ROB %i in detector %i ", mMcmPos, mRobPos, mDetector), - FeeParam::getNadcMcm(), + NADCMCM, -0.5, - FeeParam::getNadcMcm() - 0.5, + NADCMCM - 0.5, getNumberOfTimeBins(), -0.5, getNumberOfTimeBins() - 0.5); @@ -685,9 +708,9 @@ void TrapSimulator::draw(int choice, int index) hist->SetStats(false); TH2F* histfiltered = new TH2F(Form("mcmdataf_%i", index), Form("Data of MCM %i on ROB %i in detector %i filtered", mMcmPos, mRobPos, mDetector), - FeeParam::getNadcMcm(), + NADCMCM, -0.5, - FeeParam::getNadcMcm() - 0.5, + NADCMCM - 0.5, getNumberOfTimeBins(), -0.5, getNumberOfTimeBins() - 0.5); @@ -696,19 +719,19 @@ void TrapSimulator::draw(int choice, int index) if ((choice & PLOTRAW) != 0) { for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) { + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { hist->SetBinContent(iAdc + 1, iTimeBin + 1, mADCR[iAdc * mNTimeBin + iTimeBin] >> mgkAddDigits); } } + hist->Draw("COLZ"); } else { for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) { + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { histfiltered->SetBinContent(iAdc + 1, iTimeBin + 1, mADCF[iAdc * mNTimeBin + iTimeBin] >> mgkAddDigits); } } + histfiltered->Draw("COLZ"); } - hist->Draw("CONT4Z"); - histfiltered->Draw("CONT4Z"); if ((choice & PLOTHITS) != 0) { TGraph* grHits = new TGraph(); @@ -722,31 +745,33 @@ void TrapSimulator::draw(int choice, int index) if ((choice & PLOTTRACKLETS) != 0) { TLine* trklLines = new TLine[4]; - for (int iTrkl = 0; iTrkl < mTrackletArray.size(); iTrkl++) { - Tracklet trkl = mTrackletArray[iTrkl]; - float padWidth = 0.635 + 0.03 * (mDetector % 6); - float offset = padWidth / 256. * ((((((mRobPos & 0x1) << 2) + (mMcmPos & 0x3)) * 18) << 8) - ((18 * 4 * 2 - 18 * 2 - 3) << 7)); // revert adding offset in FitTracklet - //TODO replace the 18, 4 3 and 7 with constants for readability + LOG(debug) << "Tracklet start for index : " << index; + if (mTrackletArray64.size() > 0) { + LOG(debug) << "Tracklet : for " << mTrackletArray64[0].getDetector() << "::" << mTrackletArray64[0].getROB() << " : " << mTrackletArray64[0].getMCM(); + } else { + LOG(debug) << "Tracklet : for trackletarray size of zero "; + } + for (int iTrkl = 0; iTrkl < mTrackletArray64.size(); iTrkl++) { + Tracklet64 trkl = mTrackletArray64[iTrkl]; + float position = trkl.getPosition(); int ndrift = mTrapConfig->getDmemUnsigned(mgkDmemAddrNdrift, mDetector, mRobPos, mMcmPos) >> 5; - float slope = 0; - if (ndrift) - slope = trkl.getdY() * 140e-4 / ndrift; + float slope = trkl.getSlope(); int t0 = mTrapConfig->getTrapReg(TrapConfig::kTPFS, mDetector, mRobPos, mMcmPos); int t1 = mTrapConfig->getTrapReg(TrapConfig::kTPFE, mDetector, mRobPos, mMcmPos); - trklLines[iTrkl].SetX1((offset - (trkl.getY() - slope * t0)) / padWidth); // ??? sign? + trklLines[iTrkl].SetX1(position - slope * t0); trklLines[iTrkl].SetY1(t0); - trklLines[iTrkl].SetX2((offset - (trkl.getY() - slope * t1)) / padWidth); // ??? sign? + trklLines[iTrkl].SetX2(position - slope * t1); trklLines[iTrkl].SetY2(t1); trklLines[iTrkl].SetLineColor(2); trklLines[iTrkl].SetLineWidth(2); - LOG(info) << "Tracklet " << iTrkl << ": y = " << trkl.getY() << ", dy = " << (trkl.getdY() * 140e-4) << " offset : " << offset; - LOG(info) << "Tracklet " << iTrkl << ": x1,y1,x2,y2 :: " << trklLines[iTrkl].GetX1() << "," << trklLines[iTrkl].GetY1() << "," << trklLines[iTrkl].GetX2() << "," << trklLines[iTrkl].GetY2(); - LOG(info) << "Tracklet " << iTrkl << ": t0 : " << t0 << ", t1 " << t1 << ", padwidth:" << padWidth << ", slope:" << slope; - + LOG(debug) << "Tracklet " << iTrkl << ": y = " << trkl.getPosition() << ", slope = " << (float)trkl.getSlope() << "for a det:rob:mcm combo of : " << mDetector << ":" << mRobPos << ":" << mMcmPos; + LOG(debug) << "Tracklet " << iTrkl << ": x1,y1,x2,y2 :: " << trklLines[iTrkl].GetX1() << "," << trklLines[iTrkl].GetY1() << "," << trklLines[iTrkl].GetX2() << "," << trklLines[iTrkl].GetY2(); + LOG(debug) << "Tracklet " << iTrkl << ": t0 : " << t0 << ", t1 " << t1 << ", slope:" << slope << ", which comes from : " << mTrapConfig->getDmemUnsigned(mgkDmemAddrNdrift, mDetector, mRobPos, mMcmPos) << " shifted 5 to the right "; trklLines[iTrkl].Draw(); } + LOG(debug) << "Tracklet end ..."; } c1->Write(); rootfile->Close(); @@ -758,28 +783,29 @@ void TrapSimulator::setData(int adc, const ArrayADC& data, std::vector<o2::MCCom // Store ADC data into array of raw data // - if (!checkInitialized()) + if (!checkInitialized()) { return; + } - if (adc < 0 || adc >= FeeParam::getNadcMcm()) { - // LOG(error) << "Error: ADC " << adc << " is out of range (0 .. " << FeeParam::getNadcMcm() - 1 << ")"; + if (adc < 0 || adc >= NADCMCM) { + // LOG(error) << "Error: ADC " << adc << " is out of range (0 .. " << NADCMCM - 1 << ")"; return; } - // LOG(info) << "Set Data : Det:Rob:MCM::"<< getDetector() <<":" <<getRobPos()<<":"<<getMcmPos() << " t:"<< mNTimeBin; + // LOG(debug) << "Set Data : Det:Rob:MCM::"<< getDetector() <<":" <<getRobPos()<<":"<<getMcmPos() << " t:"<< mNTimeBin; for (int it = 0; it < mNTimeBin; it++) { mADCR[adc * mNTimeBin + it] = ((int)(data[it]) << mgkAddDigits) + (mgAddBaseline << mgkAddDigits); mADCF[adc * mNTimeBin + it] = ((int)(data[it]) << mgkAddDigits) + (mgAddBaseline << mgkAddDigits); // mADCR[adc * mNTimeBin + it] = (unsigned int)(data[it]); // mADCF[adc * mNTimeBin + it] = (unsigned int)(data[it]); - // LOG(info) << data[it] <<" at "<< adc << "*" << mNTimeBin <<"+"<<it <<"="<< adc*mNTimeBin+it << " with data :"<<mADCR[adc*mNTimeBin+it] << ":" << mADCF[adc*mNTimeBin+it]; + // LOG(debug) << data[it] <<" at "<< adc << "*" << mNTimeBin <<"+"<<it <<"="<< adc*mNTimeBin+it << " with data :"<<mADCR[adc*mNTimeBin+it] << ":" << mADCF[adc*mNTimeBin+it]; } mDataIsSet = true; mADCFilled |= (1 << adc); //for (auto& tmplabel : labels) mADCLabels[adc].push_back(tmplabel); - LOG(debug) << "setting data labels incoming of : " << labels.size() << " with adc of " << adc; + // LOG(debug) << "setting data labels incoming of : " << labels.size() << " with adc of " << adc; mADCLabels[adc] = labels; - LOG(debug) << "setting data labels incoming of : " << labels.size() << " with adc of " << adc << " now mADCLabels[adc] size is : " << mADCLabels[adc].size(); + // LOG(debug) << "setting data labels incoming of : " << labels.size() << " with adc of " << adc << " now mADCLabels[adc] size is : " << mADCLabels[adc].size(); } void TrapSimulator::setData(int adc, int it, int data) @@ -789,11 +815,12 @@ void TrapSimulator::setData(int adc, int it, int data) // This time enter it element by element. // - if (!checkInitialized()) + if (!checkInitialized()) { return; + } - if (adc < 0 || adc >= FeeParam::getNadcMcm()) { - LOG(error) << "Error: ADC " << adc << " is out of range (0 .. " << FeeParam::getNadcMcm() - 1 << ")"; + if (adc < 0 || adc >= NADCMCM) { + LOG(error) << "Error: ADC " << adc << " is out of range (0 .. " << NADCMCM - 1 << ")"; return; } @@ -809,12 +836,12 @@ void TrapSimulator::setBaselines() //we now add it by singular ADC, so there are adc channels that never get touched. //this fixes that. - LOG(info) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " for " << mDetector << ":" << mRobPos << ":" << mMcmPos; + LOG(debug) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " for " << mDetector << ":" << mRobPos << ":" << mMcmPos; //loop over all adcs. - for (int adc = 0; adc < FeeParam::getNadcMcm(); adc++) { - LOG(info) << "Setting baselines for adc: " << adc << " of " << mDetector << ":" << mRobPos << ":" << mMcmPos << hex << mADCFilled << " if of : " << (mADCFilled & (1 << adc)); + for (int adc = 0; adc < NADCMCM; adc++) { + LOG(debug) << "Setting baselines for adc: " << adc << " of " << mDetector << ":" << mRobPos << ":" << mMcmPos << hex << mADCFilled << " if of : " << (mADCFilled & (1 << adc)); if ((mADCFilled & (1 << adc)) == 0) { // adc is empty by construction of mADCFilled. - LOG(info) << "past if Setting baselines for adc: " << adc << " of " << mDetector << ":" << mRobPos << ":" << mMcmPos; + LOG(debug) << "past if Setting baselines for adc: " << adc << " of " << mDetector << ":" << mRobPos << ":" << mMcmPos; for (int timebin = 0; timebin < mNTimeBin; timebin++) { mADCR[adc * mNTimeBin + timebin] = mTrapConfig->getTrapReg(TrapConfig::kFPNP, mDetector, mRobPos, mMcmPos) + (mgAddBaseline << mgkAddDigits); mADCF[adc * mNTimeBin + timebin] = mTrapConfig->getTrapReg(TrapConfig::kTPFP, mDetector, mRobPos, mMcmPos) + (mgAddBaseline << mgkAddDigits); @@ -829,10 +856,11 @@ void TrapSimulator::setDataPedestal(int adc) // Store ADC data into array of raw data // - if (!checkInitialized()) + if (!checkInitialized()) { return; + } - if (adc < 0 || adc >= FeeParam::getNadcMcm()) { + if (adc < 0 || adc >= NADCMCM) { return; } @@ -846,8 +874,9 @@ bool TrapSimulator::getHit(int index, int& channel, int& timebin, int& qtot, int { // retrieve the MC hit information (not available in TRAP hardware) - if (index < 0 || index >= mNHits) + if (index < 0 || index >= mNHits) { return false; + } channel = mHits[index].mChannel; timebin = mHits[index].mTimebin; @@ -871,36 +900,117 @@ int TrapSimulator::getCol(int adc) // Return column id of the pad for the given ADC channel // - if (!checkInitialized()) + if (!checkInitialized()) { return -1; + } int col = mFeeParam->getPadColFromADC(mRobPos, mMcmPos, adc); - if (col < 0 || col >= mFeeParam->getNcol()) + if (col < 0 || col >= NCOLUMN) { return -1; - else + } else { return col; + } } -int TrapSimulator::packData(std::vector<uint32_t>& buf) +//TODO figure why I could not get span to work here +int TrapSimulator::packData(std::vector<uint32_t>& rawdata, uint32_t offset) { + // return # of 32 bit words. // //given the, up to 3 tracklets, pack them according to the define data format. // + // std::cout << "span size in packData is : " << rawdata.size() << std::endl; //TODO this is left blank so that the dataformats etc. can come in a seperate PR //to keep different work seperate. - LOG(fatal) << "Called packData of TrapSimualtor ... why? I am going to die, till next pr."; - return 1; + uint32_t wordswritten = 0; // count the 32 bit words written; + // std::cout << &raw[offset] << std::endl; + // std::cout << raw.data() << std::endl;; + // //TODO query? the mCPU have the packed data, rather use them? + TrackletMCMHeader mcmhead; + int trackletcount = 0; + // TrackletMCMHeader mcmhead; + offset++; + std::array<TrackletMCMData, 3> tracklets{}; + mcmhead.oneb = 1; + mcmhead.onea = 1; + mcmhead.padrow = ((mRobPos >> 1) << 2) | (mMcmPos >> 2); + int mcmcol = mMcmPos % NMCMROBINCOL + (mRobPos % 2) * NMCMROBINCOL; + int padcol = mcmcol * NCOLMCM + NCOLMCM + 1; + mcmhead.col = 1; //TODO check this, cant call FeeParam due to virtual function + LOG(debug) << "packing data with trackletarry64 size of : " << mTrackletArray64.size(); + for (int i = 0; i < 3; i++) { + if (i < mTrackletArray64.size()) { // we have a tracklet + LOG(debug) << "we have a tracklet at i=" << i << " with trackletword 0x" << mTrackletArray64[i].getTrackletWord(); + switch (i) { + case 0: + mcmhead.pid0 = ((mTrackletArray64[0].getQ2()) << 2) + ((mTrackletArray64[0].getQ1()) >> 5); + break; // all of Q2 and upper 2 bits of Q1. + case 1: + mcmhead.pid1 = ((mTrackletArray64[1].getQ2()) << 2) + ((mTrackletArray64[1].getQ1()) >> 5); + break; // all of Q2 and upper 2 bits of Q1. + case 2: + mcmhead.pid2 = ((mTrackletArray64[2].getQ2()) << 2) + ((mTrackletArray64[2].getQ1()) >> 5); + break; // all of Q2 and upper 2 bits of Q1. + } + tracklets[i].checkbit = 1; + LOG(debug) << mTrackletArray64[i]; + tracklets[i].pid = mTrackletArray64[i].getQ0() & (mTrackletArray64[i].getQ1() << 8); + tracklets[i].slope = mTrackletArray64[i].getSlope(); + tracklets[i].pos = mTrackletArray64[i].getPosition(); + tracklets[i].checkbit = 0; + trackletcount++; + } else { // else we dont have a tracklet so mark it off in the header. + switch (i) { + case 1: + mcmhead.pid1 = 0xff; + LOG(debug) << "setting mcmhead pid1 to 0xff with tracklet array size of " << mTrackletArray64[i]; + break; // set the pid to ff to signify not there + case 2: + mcmhead.pid2 = 0xff; + break; // set the pid to maximal to signify not there (6bits). + } + } + } + // raw.push_back((uint32_t)mcmhead) + LOG(debug) << "pushing back mcm head of 0x" << std::hex << mcmhead.word << " with trackletcount of : " << std::dec << trackletcount << ":-:" << wordswritten; + rawdata.push_back(mcmhead.word); //memcpy(&rawdata[wordswritten++], &mcmhead, sizeof(mcmhead)); + wordswritten++; + for (int i = 0; i < trackletcount; i++) { + LOG(debug) << "pushing back mcmtrackletword of 0x" << std::hex << tracklets[i].word; + rawdata.push_back(tracklets[i].word); //memcpy(&rawdata[wordswritten++], &tracklets[i], sizeof(TrackletMCMData)); + wordswritten++; + } + + //display the headers written + if (debugheaders) { + LOG(debug) << ">>>>> START info OUTPUT OF packData trackletcount:-:wordcount" << trackletcount << ":-:" << wordswritten; + o2::trd::printTrackletMCMHeader(mcmhead); + o2::trd::printTrackletMCMData(tracklets[0]); + if (trackletcount > 1) { + o2::trd::printTrackletMCMData(tracklets[1]); + } + if (trackletcount > 2) { + o2::trd::printTrackletMCMData(tracklets[2]); + } + LOG(debug) << "<<<<< END info OUTPUT OF packData"; + } + //must produce between 2 and 4 words ... 1 and 3 tracklets. + // assert(wordswritten<5); + // assert(wordswritten>1); + LOG(debug) << "now to leave pack data after passing asserts with wordswritten = " << wordswritten; + return wordswritten; // in units of 32 bits. } -int TrapSimulator::produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv) const +int TrapSimulator::getRawStream(std::vector<uint32_t>& buf, uint32_t offset, unsigned int iEv) const { // // Produce raw data stream from this MCM and put in buf // Returns number of words filled, or negative value // with -1 * number of overflowed words // - if (!checkInitialized()) + if (!checkInitialized()) { return 0; + } unsigned int x; unsigned int mcmHeader = 0; @@ -910,20 +1020,22 @@ int TrapSimulator::produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv int rawVer = mFeeParam->getRAWversion(); std::vector<int> adc; //TODO come back an avoid a copy. - if (!checkInitialized()) + if (!checkInitialized()) { return 0; + } - if (mTrapConfig->getTrapReg(TrapConfig::kEBSF, mDetector, mRobPos, mMcmPos) != 0) // store unfiltered data + if (mTrapConfig->getTrapReg(TrapConfig::kEBSF, mDetector, mRobPos, mMcmPos) != 0) { // store unfiltered data adc = mADCR; - else + } else { adc = mADCF; + } // Produce ADC mask : nncc cccm mmmm mmmm mmmm mmmm mmmm 1100 // n : unused , c : ADC count, m : selected ADCs if (rawVer >= 3 && (mTrapConfig->getTrapReg(TrapConfig::kC15CPUA, mDetector, mRobPos, mMcmPos) & (1 << 13))) { // check for zs flag in TRAP configuration int nActiveADC = 0; // number numberOverFlowWordsWritten activated ADC bits in a word - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) { + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { if (~mZSMap[iAdc] != 0) { // 0 means not suppressed adcMask |= (1 << (iAdc + 4)); // last 4 digit reserved for 1100=0xc nActiveADC++; // number numberOverFlowWordsWritten 1 in mmm....m @@ -931,8 +1043,9 @@ int TrapSimulator::produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv } if ((nActiveADC == 0) && - (mTrapConfig->getTrapReg(TrapConfig::kC15CPUA, mDetector, mRobPos, mMcmPos) & (1 << 8))) // check for DEH flag in TRAP configuration + (mTrapConfig->getTrapReg(TrapConfig::kC15CPUA, mDetector, mRobPos, mMcmPos) & (1 << 8))) { // check for DEH flag in TRAP configuration return 0; + } // assemble adc mask word adcMask |= (1 << 30) | ((0x3FFFFFFC) & (~(nActiveADC) << 25)) | 0xC; // nn = 01, ccccc are inverted, 0xc=1100 @@ -940,17 +1053,19 @@ int TrapSimulator::produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv // MCM header mcmHeader = (1 << 31) | (mRobPos << 28) | (mMcmPos << 24) | ((iEv % 0x100000) << 4) | 0xC; - if (numberWordsWritten < buf.size()) + if (numberWordsWritten < buf.size()) { buf[numberWordsWritten++] = mcmHeader; - else + } else { numberOverFlowWordsWritten++; + } // ADC mask if (adcMask != 0) { - if (numberWordsWritten < buf.size()) + if (numberWordsWritten < buf.size()) { buf[numberWordsWritten++] = adcMask; - else + } else { numberOverFlowWordsWritten++; + } } // Produce ADC data. 3 timebins are packed into one 32 bits word @@ -959,8 +1074,9 @@ int TrapSimulator::produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv unsigned int aa = 0, a1 = 0, a2 = 0, a3 = 0; for (int iAdc = 0; iAdc < 21; iAdc++) { - if (rawVer >= 3 && ~mZSMap[iAdc] == 0) + if (rawVer >= 3 && ~mZSMap[iAdc] == 0) { continue; // Zero Suppression, 0 means not suppressed + } aa = !(iAdc & 1) + 2; for (int iT = 0; iT < mNTimeBin; iT += 3) { a1 = ((iT) < mNTimeBin) ? adc[iAdc * mNTimeBin + iT] >> mgkAddDigits : 0; @@ -975,13 +1091,14 @@ int TrapSimulator::produceRawStream(std::vector<uint32_t>& buf, unsigned int iEv } } - if (numberOverFlowWordsWritten != 0) + if (numberOverFlowWordsWritten != 0) { return -numberOverFlowWordsWritten; - else + } else { return numberWordsWritten; + } } -int TrapSimulator::produceTrackletStream(std::vector<uint32_t>& buf) +int TrapSimulator::getTrackletStream(std::vector<uint32_t>& buf, uint32_t offset) { // // Produce tracklet raw data stream from this MCM and put in buf @@ -990,16 +1107,18 @@ int TrapSimulator::produceTrackletStream(std::vector<uint32_t>& buf) // with -1 * number of overflowed words // - if (!checkInitialized()) + if (!checkInitialized()) { return 0; + } int numberWordsWritten = 0; // Number of written words // Produce tracklet data. // A MCM Header followed by at most 3 tracklet words. // Although a little confusingly some of the tracklet data is in the header. - - numberWordsWritten = packData(buf); + if (mTrackletArray64.size() != 0) { // meaningless if there are no tracklets + numberWordsWritten = packData(buf, offset); + } return numberWordsWritten; } @@ -1014,8 +1133,9 @@ void TrapSimulator::filter() // sequentially for parameter tuning. // - if (!checkInitialized()) + if (!checkInitialized()) { return; + } // Apply filters sequentially. Bypass is handled by filters // since counters and internal registers may be updated even @@ -1023,26 +1143,27 @@ void TrapSimulator::filter() // The first filter takes the data from mADCR and // outputs to mADCF. - //LOG(info) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + LOG(debug) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__; // Non-linearity filter not implemented. filterPedestal(); filterGain(); filterTail(); // Crosstalk filter not implemented. - // LOG(info) << "LEAVE: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + LOG(debug) << "LEAVE: " << __FILE__ << ":" << __func__ << ":" << __LINE__; } void TrapSimulator::filterPedestalInit(int baseline) { // Initializes the pedestal filter assuming that the input has // been constant for a long time (compared to the time constant). - // LOG(info) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + // LOG(debug) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; unsigned short fptc = mTrapConfig->getTrapReg(TrapConfig::kFPTC, mDetector, mRobPos, mMcmPos); // 0..3, 0 - fastest, 3 - slowest - for (int adc = 0; adc < FeeParam::getNadcMcm(); adc++) + for (int adc = 0; adc < NADCMCM; adc++) { mInternalFilterRegisters[adc].mPedAcc = (baseline << 2) * (1 << mgkFPshifts[fptc]); - // LOG(info) << "LEAVE: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + } + // LOG(debug) << "LEAVE: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; } unsigned short TrapSimulator::filterPedestalNextSample(int adc, int timebin, unsigned short value) @@ -1050,7 +1171,7 @@ unsigned short TrapSimulator::filterPedestalNextSample(int adc, int timebin, uns // Returns the output of the pedestal filter given the input value. // The output depends on the internal registers and, thus, the // history of the filter. - /// LOG(info) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + LOG(debug) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__; unsigned short fpnp = mTrapConfig->getTrapReg(TrapConfig::kFPNP, mDetector, mRobPos, mMcmPos); // 0..511 -> 0..127.75, pedestal at the output unsigned short fptc = mTrapConfig->getTrapReg(TrapConfig::kFPTC, mDetector, mRobPos, mMcmPos); // 0..3, 0 - fastest, 3 - slowest @@ -1068,18 +1189,23 @@ unsigned short TrapSimulator::filterPedestalNextSample(int adc, int timebin, uns mInternalFilterRegisters[adc].mPedAcc = (mInternalFilterRegisters[adc].mPedAcc + correction) & 0x7FFFFFFF; // 31 bits } - // LOG(info) << "LEAVE: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; - if (fpby == 0) - return value; + // LOG(debug) << "LEAVE: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + if (fpby == 0) { + LOG(debug) << "LEAVE: (about to)" << __FILE__ << ":" << __func__ << ":" << __LINE__; + if (fpby == 0) { + return value; + } + } - if (inpAdd <= accumulatorShifted) + if (inpAdd <= accumulatorShifted) { return 0; - else { + } else { inpAdd = inpAdd - accumulatorShifted; - if (inpAdd > 0xFFF) + if (inpAdd > 0xFFF) { return 0xFFF; - else + } else { return inpAdd; + } } } @@ -1093,16 +1219,16 @@ void TrapSimulator::filterPedestal() // It has only an effect if previous samples have been fed to // find the pedestal. Currently, the simulation assumes that // the input has been stable for a sufficiently long time. - // LOG(info) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + // LOG(debug) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) { + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { int oldadc = mADCF[iAdc * mNTimeBin + iTimeBin]; mADCF[iAdc * mNTimeBin + iTimeBin] = filterPedestalNextSample(iAdc, iTimeBin, mADCR[iAdc * mNTimeBin + iTimeBin]); - // LOG(info) << "mADCF : time : " << iTimeBin << " adc : " << iAdc << " change : " << oldadc << " -> " << mADCF[iAdc * mNTimeBin + iTimeBin]; + // LOG(debug) << "mADCF : time : " << iTimeBin << " adc : " << iAdc << " change : " << oldadc << " -> " << mADCF[iAdc * mNTimeBin + iTimeBin]; } } - // LOG(info) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; + // LOG(debug) << "BEGIN: " << __FILE__ << ":" << __func__ << ":" << __LINE__ ; } void TrapSimulator::filterGainInit() @@ -1110,7 +1236,7 @@ void TrapSimulator::filterGainInit() // Initializes the gain filter. In this case, only threshold // counters are reset. - for (int adc = 0; adc < FeeParam::getNadcMcm(); adc++) { + for (int adc = 0; adc < NADCMCM; adc++) { // these are counters which in hardware continue // until maximum or reset mInternalFilterRegisters[adc].mGainCounterA = 0; @@ -1124,45 +1250,46 @@ unsigned short TrapSimulator::filterGainNextSample(int adc, unsigned short value // BEGIN_LATEX O_{i}(t) = #gamma_{i} * I_{i}(t) + a_{i} END_LATEX // The output depends on the internal registers and, thus, the // history of the filter. - // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " with adc = " << adc << " value = " << value; + // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " with adc = " << adc << " value = " << value; unsigned short mgby = mTrapConfig->getTrapReg(TrapConfig::kFGBY, mDetector, mRobPos, mMcmPos); // bypass, active low unsigned short mgf = mTrapConfig->getTrapReg(TrapConfig::TrapReg_t(TrapConfig::kFGF0 + adc), mDetector, mRobPos, mMcmPos); // 0x700 + (0 & 0x1ff); unsigned short mga = mTrapConfig->getTrapReg(TrapConfig::TrapReg_t(TrapConfig::kFGA0 + adc), mDetector, mRobPos, mMcmPos); // 40; unsigned short mgta = mTrapConfig->getTrapReg(TrapConfig::kFGTA, mDetector, mRobPos, mMcmPos); // 20; unsigned short mgtb = mTrapConfig->getTrapReg(TrapConfig::kFGTB, mDetector, mRobPos, mMcmPos); // 2060; - // mgf=256; - // mga=8; - // mgta=20; - // mgtb=2060; + // mgf=256; + // mga=8; + // mgta=20; + // mgtb=2060; unsigned int mgfExtended = 0x700 + mgf; // The corr factor which is finally applied has to be extended by 0x700 (hex) or 0.875 (dec) - // because fgf=0 correspons to 0.875 and fgf=511 correspons to 1.125 - 2^(-11) - // (see TRAP User Manual for details) - //if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " with adc = " << adc << " value = " << value << " Trapconfig values :" << mgby <<":"<<mgf<<":"<<mga<<":"<<mgta<<":"<<mgtb << ":"<< mgfExtended; + // because fgf=0 correspons to 0.875 and fgf=511 correspons to 1.125 - 2^(-11) + // (see TRAP User Manual for details) + //if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) << "ENTER: " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " with adc = " << adc << " value = " << value << " Trapconfig values :" << mgby <<":"<<mgf<<":"<<mga<<":"<<mgta<<":"<<mgtb << ":"<< mgfExtended; unsigned int corr; // corrected value - // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) << "after declaring corr adc = " << adc << " value = " << value; + // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) << "after declaring corr adc = " << adc << " value = " << value; value &= 0xFFF; corr = (value * mgfExtended) >> 11; corr = corr > 0xfff ? 0xfff : corr; - // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr; + // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr; corr = addUintClipping(corr, mga, 12); - // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr; + // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr; // Update threshold counters // not really useful as they are cleared with every new event if (!((mInternalFilterRegisters[adc].mGainCounterA == 0x3FFFFFF) || (mInternalFilterRegisters[adc].mGainCounterB == 0x3FFFFFF))) // stop when full { - // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr << " mgtb : " << mgtb; - if (corr >= mgtb) + // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr << " mgtb : " << mgtb; + if (corr >= mgtb) { mInternalFilterRegisters[adc].mGainCounterB++; - else if (corr >= mgta) + } else if (corr >= mgta) { mInternalFilterRegisters[adc].mGainCounterA++; + } } - // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(info) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr << " mgby : " << mgby; + // if(mDetector==75&& mRobPos==5 && mMcmPos==15) LOG(debug) <<__LINE__ << " adc = " << adc << " value = " << value << " corr : " << corr << " mgby : " << mgby; // if (mgby == 1) // return corr; // else @@ -1173,7 +1300,7 @@ void TrapSimulator::filterGain() { // Read data from mADCF and apply gain filter. - for (int adc = 0; adc < FeeParam::getNadcMcm(); adc++) { + for (int adc = 0; adc < NADCMCM; adc++) { for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { mADCF[adc * mNTimeBin + iTimeBin] = filterGainNextSample(adc, mADCF[adc * mNTimeBin + iTimeBin]); } @@ -1201,13 +1328,14 @@ void TrapSimulator::filterTailInit(int baseline) float ql, qs; - if (baseline < 0) + if (baseline < 0) { baseline = mTrapConfig->getTrapReg(TrapConfig::kFPNP, mDetector, mRobPos, mMcmPos); + } ql = lambdaL * (1 - lambdaS) * alphaL; qs = lambdaS * (1 - lambdaL) * (1 - alphaL); - for (int adc = 0; adc < FeeParam::getNadcMcm(); adc++) { + for (int adc = 0; adc < NADCMCM; adc++) { int value = baseline & 0xFFF; int corr = (value * mTrapConfig->getTrapReg(TrapConfig::TrapReg_t(TrapConfig::kFGF0 + adc), mDetector, mRobPos, mMcmPos)) >> 11; corr = corr > 0xfff ? 0xfff : corr; @@ -1244,10 +1372,11 @@ unsigned short TrapSimulator::filterTailNextSample(int adc, unsigned short value aQ = addUintClipping(mInternalFilterRegisters[adc].mTailAmplLong, mInternalFilterRegisters[adc].mTailAmplShort, 12); // calculate the difference between the input and the generated signal - if (inpVolt > aQ) + if (inpVolt > aQ) { aDiff = inpVolt - aQ; - else + } else { aDiff = 0; + } // the inputs to the two generators, weighted alInpv = (aDiff * alphaLong) >> 11; @@ -1263,10 +1392,11 @@ unsigned short TrapSimulator::filterTailNextSample(int adc, unsigned short value mInternalFilterRegisters[adc].mTailAmplShort = tmp & 0xFFF; // the output of the filter - if (mTrapConfig->getTrapReg(TrapConfig::kFTBY, mDetector, mRobPos, mMcmPos) == 0) // bypass mode, active low + if (mTrapConfig->getTrapReg(TrapConfig::kFTBY, mDetector, mRobPos, mMcmPos) == 0) { // bypass mode, active low return value; - else + } else { return aDiff; + } } void TrapSimulator::filterTail() @@ -1274,7 +1404,7 @@ void TrapSimulator::filterTail() // Apply tail cancellation filter to all data. for (int iTimeBin = 0; iTimeBin < mNTimeBin; iTimeBin++) { - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) { + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { mADCF[iAdc * mNTimeBin + iTimeBin] = filterTailNextSample(iAdc, mADCF[iAdc * mNTimeBin + iTimeBin]); } } @@ -1290,16 +1420,18 @@ void TrapSimulator::zeroSupressionMapping() // http://www.kip.uni-heidelberg.de/ti/TRD/doc/trap/TRAP-UserManual.pdf // - if (!checkInitialized()) + if (!checkInitialized()) { return; + } int eBIS = mTrapConfig->getTrapReg(TrapConfig::kEBIS, mDetector, mRobPos, mMcmPos); int eBIT = mTrapConfig->getTrapReg(TrapConfig::kEBIT, mDetector, mRobPos, mMcmPos); int eBIL = mTrapConfig->getTrapReg(TrapConfig::kEBIL, mDetector, mRobPos, mMcmPos); int eBIN = mTrapConfig->getTrapReg(TrapConfig::kEBIN, mDetector, mRobPos, mMcmPos); - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { mZSMap[iAdc] = -1; + } for (int it = 0; it < mNTimeBin; it++) { int iAdc; // current ADC channel @@ -1328,7 +1460,7 @@ void TrapSimulator::zeroSupressionMapping() } // ----- last channel ----- - iAdc = FeeParam::getNadcMcm() - 1; + iAdc = NADCMCM - 1; ap = mADCF[(iAdc - 1) * mNTimeBin + it] >> mgkAddDigits; // previous ac = mADCF[iAdc * mNTimeBin + it] >> mgkAddDigits; // current @@ -1346,7 +1478,7 @@ void TrapSimulator::zeroSupressionMapping() } // ----- middle channels ----- - for (iAdc = 1; iAdc < FeeParam::getNadcMcm() - 1; iAdc++) { + for (iAdc = 1; iAdc < NADCMCM - 1; iAdc++) { ap = mADCF[(iAdc - 1) * mNTimeBin + it] >> mgkAddDigits; // previous ac = mADCF[iAdc * mNTimeBin + it] >> mgkAddDigits; // current an = mADCF[(iAdc + 1) * mNTimeBin + it] >> mgkAddDigits; // next @@ -1372,7 +1504,7 @@ void TrapSimulator::addHitToFitreg(int adc, unsigned short timebin, unsigned sho // the tracklet calculation. // In addition to the fit sums in the fit register // - + LOG(debug) << __func__ << " adc : " << adc << " timebin: " << timebin << " qtot: " << qtot << " ypos:" << ypos; if (adc > 24) { LOG(error) << " adc channel into addHitToFitReg is out of bounds for mFitReg : " << adc; } @@ -1386,6 +1518,10 @@ void TrapSimulator::addHitToFitreg(int adc, unsigned short timebin, unsigned sho (timebin < mTrapConfig->getTrapReg(TrapConfig::kTPQE1, mDetector, mRobPos, mMcmPos))) { mFitReg[adc].mQ1 += qtot; } + // Q2 is simply the addition of times from 3 to 5, for now consts in the header file till they come from a config. + if (timebin > mQ2Startbin && timebin < mQ2Endbin) { + mFitReg[adc].mQ2 += qtot; + } if ((timebin >= mTrapConfig->getTrapReg(TrapConfig::kTPFS, mDetector, mRobPos, mMcmPos)) && (timebin < mTrapConfig->getTrapReg(TrapConfig::kTPFE, mDetector, mRobPos, mMcmPos))) { @@ -1395,10 +1531,10 @@ void TrapSimulator::addHitToFitreg(int adc, unsigned short timebin, unsigned sho mFitReg[adc].mSumY += ypos; mFitReg[adc].mSumY2 += ypos * ypos; mFitReg[adc].mSumXY += timebin * ypos; - // LOG(debug) << "fitreg[" << adc << "] in timebin " << timebin << ": X=" << mFitReg[adc].mSumX - // << ", X2=" << mFitReg[adc].mSumX2 << ", N=" << mFitReg[adc].mNhits << ", Y=" - // << mFitReg[adc].mSumY << ", Y2=" << mFitReg[adc].mSumY2 << ", XY=" << mFitReg[adc].mSumXY - // << ", Q0=" << mFitReg[adc].mQ0 << ", Q1=" << mFitReg[adc].mQ1; + LOG(debug) << "fitreg[" << adc << "] in timebin " << timebin << ": X=" << mFitReg[adc].mSumX + << ", X2=" << mFitReg[adc].mSumX2 << ", N=" << mFitReg[adc].mNhits << ", Y=" + << mFitReg[adc].mSumY << ", Y2=" << mFitReg[adc].mSumY2 << ", XY=" << mFitReg[adc].mSumXY + << ", Q0=" << mFitReg[adc].mQ0 << ", Q1=" << mFitReg[adc].mQ1; } // register hits (MC info) @@ -1408,9 +1544,9 @@ void TrapSimulator::addHitToFitreg(int adc, unsigned short timebin, unsigned sho mHits[mNHits].mQtot = qtot; mHits[mNHits].mYpos = ypos; mHits[mNHits].mTimebin = timebin; - // LOG(info) << "in addhit to fit reg labels coming in are of " << labels.size() << " and labels assigned from [mNHits = "<<mNHits<<"] a size of " << mHits[mNHits].mLabels.size(); + //LOG(debug) << "in addhit to fit reg labels coming in are of " << labels.size() << " and labels assigned from [mNHits = "<<mNHits<<"] a size of " << mHits[mNHits].mLabels.size(); // mHits[mNHits].mLabels = labels; - // LOG(info) << "in addhit to fit reg labels coming in are of " << labels.size() << " and labels assigned from [mNHits = "<<mNHits<<"] a size of " << mHits[mNHits].mLabels.size(); + //LOG(debug) << "in addhit to fit reg labels coming in are of " << labels.size() << " and labels assigned from [mNHits = "<<mNHits<<"] a size of " << mHits[mNHits].mLabels.size(); //TODO link to the labels. mNHits++; //.emplace_back(adc, timebin, qtot, ypos); // TODO add label indexes into the labels container for all those labels pertaining to this hit. @@ -1418,7 +1554,7 @@ void TrapSimulator::addHitToFitreg(int adc, unsigned short timebin, unsigned sho LOG(warn) << "no space left to store the MC information for the hit >100 mNHits : " << mNHits; //print( PRINTRAW| PRINTFILTERED); } - // LOG(info) << "added hit of : "<< adc<<":"<< qtot<<":"<< ypos<<":"<< timebin; // TODO add label indexes into the labels container for all those labels pertaining to this hit. + // LOG(debug) << "added hit of : "<< adc<<":"<< qtot<<":"<< ypos<<":"<< timebin; // TODO add label indexes into the labels container for all those labels pertaining to this hit. } void TrapSimulator::calcFitreg() @@ -1438,7 +1574,6 @@ void TrapSimulator::calcFitreg() std::array<unsigned short, 20> qTotal{}; //[19 + 1]; // the last is dummy std::array<unsigned short, 6> marked{}, qMarked{}; unsigned short worse1, worse2; - int debugstop = 1; if (mgStoreClusters) { timebin1 = 0; @@ -1470,13 +1605,13 @@ void TrapSimulator::calcFitreg() // mFitReg.clear(); for (int i = 0; i < mNHits; i++) { - mHits[i].ClearHits(); // do it this way as we dont want to 100 members if we only used 3 .... + mHits[i].ClearHits(); // do it this way as we dont want to zero 150 members if we only used 3 .... } for (timebin = timebin1; timebin < timebin2; timebin++) { // first find the hit candidates and store the total cluster charge in qTotal array // in case of not hit store 0 there. - for (adcch = 0; adcch < FeeParam::getNadcMcm() - 2; adcch++) { + for (adcch = 0; adcch < NADCMCM - 2; adcch++) { if (((adcMask >> adcch) & 7) == 7) //??? all 3 channels are present in case of ZS { adcLeft = mADCF[adcch * mNTimeBin + timebin]; @@ -1498,15 +1633,15 @@ void TrapSimulator::calcFitreg() // The accumulated charge is with the pedestal!!! qtotTemp = adcLeft + adcCentral + adcRight; - /* if ((qtotTemp > 130)) { - LOG(info) << "testtree " + if ((qtotTemp > 130)) { + LOG(debug) << "qtotTemp>130 " << "qtot=" << qtotTemp << " qleft=" << adcLeft << " qcent=" << adcCentral << " qright=" << adcRight - << " for " << mDetector <<":"<<mRobPos<<":"<<mMcmPos << ":adcleft:"<<adcch<<":t"<<timebin; + << " for " << mDetector << ":" << mRobPos << ":" << mMcmPos << ":adcleft:" << adcch << ":t" << timebin; } -*/ + if ((hitQual) && (qtotTemp >= mTrapConfig->getTrapReg(TrapConfig::kTPHT, mDetector, mRobPos, mMcmPos)) && (adcLeft <= adcCentral) && @@ -1519,7 +1654,7 @@ void TrapSimulator::calcFitreg() qTotal[adcch] = 0; //jkl } // if (qTotal[adcch] != 0) - // LOG(info) << "ch " << setw(2) << adcch << " qTotal " << qTotal[adcch]; + // LOG(debug) << "ch " << setw(2) << adcch << " qTotal " << qTotal[adcch]; } fromLeft = -1; @@ -1567,8 +1702,9 @@ void TrapSimulator::calcFitreg() } if (found > 4) // sorting like in the TRAP in case of 5 or 6 candidates! { - if (marked[4] == marked[5]) + if (marked[4] == marked[5]) { marked[5] = 19; + } for (found = 0; found < 6; found++) { qMarked[found] = qTotal[marked[found]] >> 4; } @@ -1584,11 +1720,11 @@ void TrapSimulator::calcFitreg() // Now mask the two channels with the smallest charge if (worse1 < 19) { qTotal[worse1] = 0; - //LOG(debug3) << "Kill ch " << worse1; + //LOG(debug) << "Kill ch " << worse1; } if (worse2 < 19) { qTotal[worse2] = 0; - // LOG(debug3) << "Kill ch " << worse2; + // LOG(debug) << "Kill ch " << worse2; } } @@ -1602,61 +1738,75 @@ void TrapSimulator::calcFitreg() // subtract the pedestal TPFP, clipping instead of wrapping int regTPFP = mTrapConfig->getTrapReg(TrapConfig::kTPFP, mDetector, mRobPos, mMcmPos); //TODO put this together with the others as members of trapsim, which is initiliased by det,rob,mcm. - // LOG(info) << "Hit found, time=" << timebin << ", adcch=" << adcch << "/" << adcch + 1 << "/" - // << adcch + 2 << ", adc values=" << adcLeft << "/" << adcCentral << "/" - // << adcRight << ", regTPFP=" << regTPFP << ", TPHT=" << mTrapConfig->getTrapReg(TrapConfig::kTPHT, mDetector, mRobPos, mMcmPos); - if (adcLeft < regTPFP) + LOG(debug) << "Hit found, time=" << timebin << ", adcch=" << adcch << "/" << adcch + 1 << "/" + << adcch + 2 << ", adc values=" << adcLeft << "/" << adcCentral << "/" + << adcRight << ", regTPFP=" << regTPFP << ", TPHT=" << mTrapConfig->getTrapReg(TrapConfig::kTPHT, mDetector, mRobPos, mMcmPos); + if (adcLeft < regTPFP) { adcLeft = 0; - else + } else { adcLeft -= regTPFP; - if (adcCentral < regTPFP) + } + if (adcCentral < regTPFP) { adcCentral = 0; - else + } else { adcCentral -= regTPFP; - if (adcRight < regTPFP) + } + if (adcRight < regTPFP) { adcRight = 0; - else + } else { adcRight -= regTPFP; + } // Calculate the center of gravity // checking for adcCentral != 0 (in case of "bad" configuration) - if (adcCentral == 0) + if (adcCentral == 0) { continue; + } ypos = 128 * (adcRight - adcLeft) / adcCentral; - if (ypos < 0) + if (ypos < 0) { ypos = -ypos; + } + //ypos element of [0:128] // make the correction using the position LUT + LOG(debug) << "ypos raw is " << ypos << " adcrigh-adcleft/adccentral " << adcRight << "-" << adcLeft << "/" << adcCentral << "==" << (adcRight - adcLeft) / adcCentral << " 128 * numerator : " << 128 * (adcRight - adcLeft) / adcCentral; + LOG(debug) << "ypos before lut correction : " << ypos; ypos = ypos + mTrapConfig->getTrapReg((TrapConfig::TrapReg_t)(TrapConfig::kTPL00 + (ypos & 0x7F)), mDetector, mRobPos, mMcmPos); - if (adcLeft > adcRight) - ypos = -ypos; + if (adcLeft > adcRight) { + LOG(debug) << "ypos after lut correction : " << ypos; + if (adcLeft > adcRight) { + ypos = -ypos; + } + } /* TODO this is left in here as a ref for what was done with labels, its stored externally now figure something out. -*/ + */ // add the hit to the fitregister // int a=qTotal[adcch] >> mgkAddDigits; - // LOG(info) << "calling addHitToFitreg with :" << adcch << " :: " << timebin << " :: " << hex << qTotal[adcch] << dec << " :: shifted bits :" << 2 << " :: " << ypos; + // LOG(debug) << "calling addHitToFitreg with :" << adcch << " :: " << timebin << " :: " << hex << qTotal[adcch] << dec << " :: shifted bits :" << 2 << " :: " << ypos; // addHitToFitreg(adcch, timebin, qTotal[adcch] >> 2, ypos); - LOG(debug) << __func__ << "ADDING HIT FOR in adclabels : " << mADCLabels[adcch].size() << " with adcch of " << adcch << " qtot : " << qTotal[adcch] << " timebin :" << timebin; + LOG(debug) << __func__ << "ADDING HIT FOR in adclabels : " << mADCLabels[adcch].size() << " with adcch of " << adcch << " qtot : " << qTotal[adcch] << " timebin :" << timebin << " and ypos:" << ypos; addHitToFitreg(adcch, timebin, qTotal[adcch] >> mgkAddDigits, ypos); //, mADCLabels[adcch]); - // LOG(debug) << __FILE__ << ":" << __LINE__ << " :: added hit to fit re "; + // LOG(debug) << __FILE__ << ":" << __LINE__ << " :: added hit to fit re "; } } } - /* - for (int iAdc = 0; iAdc < FeeParam::getNadcMcm(); iAdc++) { + + LOG(debug) << "***** Fit Registers begin"; + for (int iAdc = 0; iAdc < NADCMCM; iAdc++) { if (mFitReg[iAdc].mNhits != 0) { LOG(debug) << "fitreg[" << iAdc << "]: nHits = " << mFitReg[iAdc].mNhits << "]: sumX = " << mFitReg[iAdc].mSumX << ", sumY = " << mFitReg[iAdc].mSumY << ", sumX2 = " << mFitReg[iAdc].mSumX2 << ", sumY2 = " << mFitReg[iAdc].mSumY2 << ", sumXY = " << mFitReg[iAdc].mSumXY; } - }*/ + } + LOG(debug) << "***** Fit Registers end"; //print(PRINTRAW); LOG(debug) << "LEAVING : " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " :: " << getDetector() << ":" << getRobPos() << ":" << getMcmPos() << " and mNHits : " << mNHits; } void TrapSimulator::trackletSelection() { - // Select up to 4 tracklet candidates from the fit registers + // Select up to 3 tracklet candidates from the fit registers // and assign them to the CPUs. - // LOG(info) << "ENTERING : " << __FILE__ << ":" << __func__ << ":" << __LINE__<<" :: "<< getDetector()<<":"<< getRobPos()<<":"<< getMcmPos(); + LOG(debug) << "ENTERING : " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " :: " << getDetector() << ":" << getRobPos() << ":" << getMcmPos(); unsigned short adcIdx, i, j, ntracks, tmp; std::array<unsigned short, 18> trackletCandch{}; // store the adcch[0] and number of hits[1] for all tracklet candidates std::array<unsigned short, 18> trackletCandhits{}; // store the adcch[0] and number of hits[1] for all tracklet candidates @@ -1667,15 +1817,15 @@ void TrapSimulator::trackletSelection() (mFitReg[adcIdx].mNhits + mFitReg[adcIdx + 1].mNhits >= mTrapConfig->getTrapReg(TrapConfig::kTPCT, mDetector, mRobPos, mMcmPos))) { trackletCandch[ntracks] = adcIdx; trackletCandhits[ntracks] = mFitReg[adcIdx].mNhits + mFitReg[adcIdx + 1].mNhits; - // LOG(info) << ntracks << " " << trackletCandch[ntracks] << " " << trackletCandhits[ntracks]; + // LOG(debug) << ntracks << " " << trackletCandch[ntracks] << " " << trackletCandhits[ntracks]; ntracks++; }; } - // LOG(info) << "Number of track candidates:"<< ntracks; - // for (i = 0; i < ntracks; i++) - // LOG(info) << "TRACKS: " << i << " " << trackletCandch[i] << " " << trackletCandhits[i]; - - if (ntracks > 4) { + LOG(debug) << "Number of track candidates:" << ntracks; + for (i = 0; i < ntracks; i++) { + LOG(debug) << "TRACKS: " << i << " " << trackletCandch[i] << " " << trackletCandhits[i]; + } + if (ntracks > 3) { // primitive sorting according to the number of hits for (j = 0; j < (ntracks - 1); j++) { for (i = j + 1; i < ntracks; i++) { @@ -1691,7 +1841,7 @@ void TrapSimulator::trackletSelection() } } } - ntracks = 4; // cut the rest, 4 is the max + ntracks = 3; // cut the rest, 3 is the max } // else is not necessary to sort @@ -1709,25 +1859,29 @@ void TrapSimulator::trackletSelection() } } } - for (i = 0; i < ntracks; i++) // CPUs with tracklets. + for (i = 0; i < ntracks; i++) { // CPUs with tracklets. mFitPtr[i] = trackletCandch[i]; // pointer to the left channel with tracklet for CPU[i] - for (i = ntracks; i < 4; i++) // CPUs without tracklets - mFitPtr[i] = 31; // pointer to the left channel with tracklet for CPU[i] = 31 (invalid) - // LOG(info) << "-------------------------------------------- found " << ntracks << " tracklet candidates"; - // for (i = 0; i < 4; i++) - // LOG(info) << "fitPtr[" << i << "]: " << mFitPtr[i]; + } + for (i = ntracks; i < 4; i++) { // CPUs without tracklets + mFitPtr[i] = 31; // pointer to the left channel with tracklet for CPU[i] = 31 (invalid) + } + LOG(debug) << __func__ << " found " << ntracks << " tracklet candidates"; + // for (i = 0; i < 4; i++) + // LOG(debug) << "fitPtr[" << i << "]: " << mFitPtr[i]; // reject multiple tracklets if (FeeParam::instance()->getRejectMultipleTracklets()) { unsigned short counts = 0; for (j = 0; j < (ntracks - 1); j++) { - if (mFitPtr[j] == 31) + if (mFitPtr[j] == 31) { continue; + } for (i = j + 1; i < ntracks; i++) { // check if tracklets are from neighbouring ADC channels - if (TMath::Abs(mFitPtr[j] - mFitPtr[i]) > 1.) + if (TMath::Abs(mFitPtr[j] - mFitPtr[i]) > 1.) { continue; + } // check which tracklet candidate has higher amount of hits if ((mFitReg[mFitPtr[j]].mNhits + mFitReg[mFitPtr[j] + 1].mNhits) >= @@ -1747,17 +1901,18 @@ void TrapSimulator::trackletSelection() // for (i = 0; i < 4; i++) // LOG(debug) << "fitPtr[" << i << "]: " << mFitPtr[i]; } + LOG(debug) << "LEAVING : " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " :: " << getDetector() << ":" << getRobPos() << ":" << getMcmPos() << " with ntracks:" << ntracks; } void TrapSimulator::fitTracklet() { // Perform the actual tracklet fit based on the fit sums // which have been filled in the fit registers. - // LOG(debug) << "ENTERING : " << __FILE__ << ":" << __func__ << ":" << __LINE__<<" :: "<< getDetector()<<":"<< getRobPos()<<":"<< getMcmPos() << " and Tracklet array size is : " << mTrackletArray.size(); + LOG(debug) << "ENTERING : " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " :: " << getDetector() << ":" << getRobPos() << ":" << getMcmPos() << " and Tracklet array size is : " << mTrackletArray64.size(); // parameters in fitred.asm (fit program) int rndAdd = 0; int decPlaces = 5; // must be larger than 1 or change the following code - // if (decPlaces > 1) + // if (decPlaces > 1) rndAdd = (1 << (decPlaces - 1)) + 1; // else if (decPlaces == 1) // rndAdd = 1; @@ -1768,30 +1923,31 @@ void TrapSimulator::fitTracklet() int padrow = ((mRobPos >> 1) << 2) | (mMcmPos >> 2); int yoffs = (((((mRobPos & 0x1) << 2) + (mMcmPos & 0x3)) * 18) << 8) - ((18 * 4 * 2 - 18 * 2 - 1) << 7); + // TODO we dont need the stuff calculated in fitred.asm because we are now all relative to the mcm ?? check this statement + LOG(debug) << "padrow:" << padrow << " yoffs:" << yoffs << " and rndAdd:" << rndAdd; // add corrections for mis-alignment if (FeeParam::instance()->getUseMisalignCorr()) { - LOG(debug3) << "using mis-alignment correction"; + LOG(debug) << "using mis-alignment correction"; yoffs += (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrYcorr, mDetector, mRobPos, mMcmPos); } yoffs = yoffs << decPlaces; // holds position of ADC channel 1 int layer = mDetector % 6; - unsigned int scaleY = (unsigned int)((0.635 + 0.03 * layer) / (256.0 * 160.0e-4) * shift); - unsigned int scaleD = (unsigned int)((0.635 + 0.03 * layer) / (256.0 * 140.0e-4) * shift); - + unsigned int scaleY = 1; //(unsigned int)((0.635 + 0.03 * layer) / (256.0 * 160.0e-4) * shift); + unsigned int scaleD = 1; //(unsigned int)((0.635 + 0.03 * layer) / (256.0 * 140.0e-4) * shift); + LOG(debug) << "scaleY : " << scaleY << " scaleD=" << scaleD << " shift:" << std::hex << shift << std::dec; int deflCorr = (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCorr, mDetector, mRobPos, mMcmPos); int ndrift = (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrNdrift, mDetector, mRobPos, mMcmPos); // local variables for calculation - long mult, temp, denom; //??? - unsigned int q0, q1, pid; // charges in the two windows and total charge - float rawpid, rawz, rawy, rawslope, rawoffset; - float rawslope4trackletword, rawoffset4trackletword; + long mult, temp, denom; + unsigned int q0, q1, q2 = 23, pid; // charges in the two windows and total charge + float rawpid, rawz, rawy, rawslope, rawposition; unsigned short nHits; // number of hits - int slope, offset; // slope and offset of the tracklet + int slope, position; // slope and position of the tracklet int sumX, sumY, sumXY, sumX2; // fit sums from fit registers - int sumY2; // not used in the current TRAP program, now used for error calculation (simulation only) + int sumY2; // not used in the current TRAP program, used for error calculation (simulation only) float fitError, fitSlope, fitOffset; FitReg *fit0, *fit1; // pointers to relevant fit registers @@ -1807,69 +1963,87 @@ void TrapSimulator::fitTracklet() } else { fit0 = &mFitReg[mFitPtr[cpu]]; fit1 = &mFitReg[mFitPtr[cpu] + 1]; // next channel - + // fit0->Print(); + // fit1->Print(); + LOG(debug) << "mult is : " << mult << " and in hex : 0x" << std::hex << mult << std::dec; mult = 1; mult = mult << (32 + decPlaces); mult = -mult; + LOG(debug) << "after mult is : " << mult << " and in hex : 0x" << std::hex << mult << std::dec; // time offset for fit sums const int t0 = FeeParam::instance()->getUseTimeOffset() ? (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrTimeOffset, mDetector, mRobPos, mMcmPos) : 0; - // LOG(info) << "using time offset t0 = " << t0; + LOG(debug) << "using time offset of t0 = " << t0; // Merging nHits = fit0->mNhits + fit1->mNhits; // number of hits sumX = fit0->mSumX + fit1->mSumX; sumX2 = fit0->mSumX2 + fit1->mSumX2; denom = ((long)nHits) * ((long)sumX2) - ((long)sumX) * ((long)sumX); + // set ADC data with array mult = mult / denom; // exactly like in the TRAP program q0 = fit0->mQ0 + fit1->mQ0; q1 = fit0->mQ1 + fit1->mQ1; + q2 = fit0->mQ2 + fit1->mQ2; sumY = fit0->mSumY + fit1->mSumY + 256 * fit1->mNhits; sumXY = fit0->mSumXY + fit1->mSumXY + 256 * fit1->mSumX; sumY2 = fit0->mSumY2 + fit1->mSumY2 + 512 * fit1->mSumY + 256 * 256 * fit1->mNhits; + LOG(debug) << "sumY2 = " << sumY2 << " "; + ; slope = nHits * sumXY - sumX * sumY; + LOG(debug) << "slope = " << slope; //offset = sumX2*sumY - sumX*sumXY - t0 * sumX*sumY + t0 * nHits*sumXY; - offset = sumX2 * sumY - sumX * sumXY; - offset = offset << 5; - offset += t0 * nHits * sumXY - t0 * sumX * sumY; - offset = offset >> 5; + position = sumX2 * sumY - sumX * sumXY; + position = position << 5; + position += t0 * nHits * sumXY - t0 * sumX * sumY; + position = position >> 5; temp = mult * slope; slope = temp >> 32; // take the upper 32 bits slope = -slope; - temp = mult * offset; - offset = temp >> 32; // take the upper 32 bits - - offset = offset + yoffs; - // LOG(info) << "slope = " << slope << ", slope * ndrift = " << slope * ndrift << ", deflCorr: " << deflCorr; - slope = ((slope * ndrift) >> ndriftDp) + deflCorr; - offset = offset - (mFitPtr[cpu] << (8 + decPlaces)); - + temp = mult * position; + position = temp >> 32; // take the upper 32 bits + LOG(debug) << "slope = " << slope; + //slope = slope;//this is here as a reference for what was done in run2 //* ndrift) >> ndriftDp) + deflCorr; + auto oldpos = position; + LOG(debug) << "position = position - (mFitPtr[cpu] << (8 + decPlaces));"; + position = position - (mFitPtr[cpu] << (8 + decPlaces)); + LOG(debug) << "position = " << position << " - " << mFitPtr[cpu] << " << (8+" << decPlaces << ")"; + + LOG(debug) << "position = " << position << " : " << oldpos; + LOG(debug) << "slope before scale of " << scaleD << " is = " << slope; temp = slope; temp = temp * scaleD; slope = (temp >> 32); - rawslope4trackletword = temp; - temp = offset; + LOG(debug) << "slope after scale and shift is = " << slope; + LOG(debug) << "slope = " << slope; + LOG(debug) << "position before scale and shift = " << position; + temp = position; temp = temp * scaleY; - offset = (temp >> 32); - rawoffset4trackletword = temp; + position = (temp >> 32); + LOG(debug) << "position after scale and shift is = " << position; // rounding, like in the TRAP slope = (slope + rndAdd) >> decPlaces; - offset = (offset + rndAdd) >> decPlaces; + position = (position + rndAdd) >> decPlaces; + LOG(debug) << "position = " << position; + LOG(debug) << "slope = " << slope; - // LOG(info) << "Det: " << setw(3) << mDetector << ", ROB: " << mRobPos << ", MCM: " << setw(2) << mMcmPos << setw(-1) << ": deflection: " << slope << ", min: " << (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos) << " max : " << (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 1 + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos); + LOG(debug) << "Det: " << setw(3) << mDetector << ", ROB: " << mRobPos << ", MCM: " << setw(2) << mMcmPos << setw(-1) << ": deflection: " << slope << ", min: " << (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos) << " max : " << (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 1 + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos); - // LOG(info) << "Fit sums: x = " << sumX << ", X = " << sumX2 << ", y = " << sumY << ", Y = " << sumY2 << ", Z = " << sumXY << ", q0 = " << q0 << ", q1 = " << q1; + LOG(debug) << "Fit sums: x = " << sumX << ", X = " << sumX2 << ", y = " << sumY << ", Y = " << sumY2 << ", Z = " << sumXY << ", q0 = " << q0 << ", q1 = " << q1; fitSlope = (float)(nHits * sumXY - sumX * sumY) / (nHits * sumX2 - sumX * sumX); fitOffset = (float)(sumX2 * sumY - sumX * sumXY) / (nHits * sumX2 - sumX * sumX); + LOG(debug) << "FIT : a:" << fitSlope << " b:" << fitOffset; + LOG(debug) << "FIT for trackletword : a:" << slope << " b:" << position; + float sx = (float)sumX; float sx2 = (float)sumX2; float sy = (float)sumY; @@ -1885,42 +2059,61 @@ void TrapSimulator::fitTracklet() rejected = true; } - // LOG(info) << "slope : " << slope << " getDmemUnsigned " << mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos); + // LOG(debug) << "slope : " << slope << " getDmemUnsigned " << mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos); if (rejected && getApplyCut()) { mMCMT[cpu] = 0x10001000; //??? FeeParam::getTrackletEndmarker(); } else { - if (slope > 63 || slope < -64) { // wrapping in TRAP! - LOG(info) << "Overflow in slope: " << slope << ", tracklet discarded!"; + if (slope > 127 || slope < -127) { // wrapping in TRAP! + LOG(debug) << "Overflow in slope: " << slope << ", tracklet discarded!"; mMCMT[cpu] = 0x10001000; continue; } - slope = slope & 0x7F; // 7 bit - - if (offset > 0xfff || offset < -0xfff) { - LOG(warning) << "Overflow in offset"; + slope = slope & 0xff; // 8 bit + + yoffs = yoffs << decPlaces; // holds position of ADC channel 1 + int layer = mDetector % 6; + unsigned int scaleY = (unsigned int)((0.635 + 0.03 * layer) / (256.0 * 160.0e-4) * shift); + unsigned int scaleD = (unsigned int)((0.635 + 0.03 * layer) / (256.0 * 140.0e-4) * shift); + LOG(debug) << "scaleY : " << scaleY << " scaleD=" << scaleD << " shift:" << std::hex << shift << std::dec; + int deflCorr = (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCorr, mDetector, mRobPos, mMcmPos); + int ndrift = (int)mTrapConfig->getDmemUnsigned(mgkDmemAddrNdrift, mDetector, mRobPos, mMcmPos); + + // local variables for calculation + long mult, temp, denom; + unsigned int q0, q1, q2 = 23, pid; // charges in the two windows and total charge + float rawpid, rawz, rawy, rawslope, rawposition; + float rawslope4trackletword, rawoffset4trackletword; + // unsigned short nHits; // number of hits + int slope, position; // slope and position of the tracklet + int sumX, sumY, sumXY, sumX2; // fit sums from fit registers + int sumY2; // not used in the current TRAP program, used for error calculation (simulation only) + float fitError, fitSlope, fitOffset; + FitReg *fit0, *fit1; // pointers to relevant fit registers + + // const uint32_t OneDivN[32] = { // 2**31/N : exactly like in the TRAP, the simple division here gives the same result! + // 0x00000000, 0x80000000, 0x40000000, 0x2AAAAAA0, 0x20000000, 0x19999990, 0x15555550, 0x12492490, + // 0x10000000, 0x0E38E380, 0x0CCCCCC0, 0x0BA2E8B0, 0x0AAAAAA0, 0x09D89D80, 0x09249240, 0x08888880, + // 0x08000000, 0x07878780, 0x071C71C0, 0x06BCA1A0, 0x06666660, 0x06186180, 0x05D17450, 0x0590B210, + // 0x05555550, 0x051EB850, 0x04EC4EC0, 0x04BDA120, 0x04924920, 0x0469EE50, 0x04444440, 0x04210840}; + + if (position > 0x7ff || position < -0x7ff) { // 11 bits. + LOG(warning) << "Overflow in position with position of " << position << " in hex 0x" << std::hex << position; } - offset = offset & 0x1FFF; // 13 bit - - pid = getPID(q0, q1); - rawpid = pid; - - if (pid > 0xff) { - LOG(warn) << "Overflow in PID"; // was warn - } - pid = pid & 0xFF; // 8 bit, exactly like in the TRAP program + position = position & 0x7ff; // 11 bits // assemble and store the tracklet word - mMCMT[cpu] = (pid << 24) | (padrow << 20) | (slope << 13) | offset; - // pid Z deflection lenght Y + TrackletMCMData trackletword; + buildTrackletMCMData(trackletword, slope, position, q0, q1, q2); + mMCMT[cpu] = trackletword.word; + rawz = padrow; - rawoffset = offset; + rawposition = position; // calculate number of hits and MC label // std::array<int, 3> mcLabel = {-1, -1, -1}; - int nHits0 = 0; - int nHits1 = 0; + std::array<int, 3> nHits{}; // const int maxLabels = 30; // std::array<int, 30> label{}; // up to 30 different labels possible @@ -1938,19 +2131,25 @@ void TrapSimulator::fitTracklet() // counting contributing hits if (mHits[iHit].mTimebin >= mTrapConfig->getTrapReg(TrapConfig::kTPQS0, mDetector, mRobPos, mMcmPos) && mHits[iHit].mTimebin < mTrapConfig->getTrapReg(TrapConfig::kTPQE0, mDetector, mRobPos, mMcmPos)) { - nHits0++; + nHits[0]++; } if (mHits[iHit].mTimebin >= mTrapConfig->getTrapReg(TrapConfig::kTPQS1, mDetector, mRobPos, mMcmPos) && mHits[iHit].mTimebin < mTrapConfig->getTrapReg(TrapConfig::kTPQE1, mDetector, mRobPos, mMcmPos)) { - nHits1++; + nHits[1]++; } + if (mHits[iHit].mTimebin >= 3 && //TODO this needs to come from trapconfig, its not there yet. + mHits[iHit].mTimebin < 5) { + nHits[2]++; + } + //TODO nHits2 ???? to correspond to Q2 ??? + // LOG(debug) << "setting bit pattern for chanel : " << mHits[iHit].mChannel << " of hit : " << iHit << std::hex << " bitp before : " << adchitbp << std::dec; adchitbp |= (1 << mHits[iHit].mChannel); LOG(debug) << "after setting bit pattern for chanel : " << mHits[iHit].mChannel << " of hit : " << iHit << std::hex << " bitp after : " << adchitbp << std::dec; // TODO label calculation only if there is a digitsmanager to get the labels froAm } bool printoutadcs = false; - //TODO these debug statemetns are left in to debug the labels later. + //TODO these info statemetns are left in to info the labels later. LOG(debug) << "adc bitpatterh is " << std::hex << adchitbp << std::dec; for (int i = 0; i < 21; i++) { if (adchitbp & (1 << i)) { @@ -1969,55 +2168,176 @@ void TrapSimulator::fitTracklet() //if(printoutadcs) print(PRINTRAW|PRINTFILTERED); //labels come in via adc, now loop over ADC and see which contribute to the hits. - LOG(debug) << "TrapSim Trackletarray size is : " << mTrackletArray.size() << " :: adding a track at " << mMCMT[cpu] << ":" << mDetector * 2 + mRobPos % 2 << ":" << mRobPos << ":" << mMcmPos << " LABELS size: " << localTrackletLabels.size(); - mTrackletArray.emplace_back(mDetector * 2 + mRobPos % 2, mRobPos, mMcmPos, rawpid, rawslope, rawoffset, rawslope4trackletword, rawoffset4trackletword); - int newtrackposition = mTrackletArray.size() - 1; - // mTrackletArray[newtrackposition].setLabel(mcLabel); - mTrackletArray[newtrackposition].setNHits(fit0->mNhits + fit1->mNhits); - mTrackletArray[newtrackposition].setNHits0(nHits0); - mTrackletArray[newtrackposition].setNHits1(nHits1); - mTrackletArray[newtrackposition].setQ0(q0); - mTrackletArray[newtrackposition].setQ1(q1); - mTrackletArray[newtrackposition].setSlope(fitSlope); - mTrackletArray[newtrackposition].setOffset(fitOffset); - mTrackletArray[newtrackposition].setError(TMath::Sqrt(TMath::Abs(fitError) / nHits)); + LOG(debug) << "TrapSim Trackletarray size is : " << mTrackletArray64.size() << " :: adding a track at " << mMCMT[cpu] << ":" << mDetector * 2 + mRobPos % 2 << ":" << mRobPos << ":" << mMcmPos << " LABELS size: " << localTrackletLabels.size(); + uint32_t format = 0; + uint32_t hcid = mDetector * 2 + mRobPos % 2; + uint32_t padrow = ((mRobPos >> 1) << 2) | (mMcmPos >> 2); + uint32_t col = mFeeParam->getPadColFromADC(mRobPos, mMcmPos, 1); + //defined above uint32_t position = ; + //uint32_t s + mTrackletArray64.emplace_back(format, hcid, padrow, col, position, slope, q0, q1, q2); + if (mdebugStream) { + mTrackletDetails.emplace_back(position, slope, q0, q1, q2, nHits, fitError); + } + //mTrackletArray[newtrackposition].setLabel(mcLabel); LOG(debug) << "adding elements to mTrackletLabels of size " << mTrackletLabels.getIndexedSize() << "::" << mTrackletLabels.getNElements() << " labels for additional labels vector of :" << localTrackletLabels.size() << " labels"; mTrackletLabels.addElements(mTrackletLabels.getIndexedSize(), localTrackletLabels); LOG(debug) << "elements in mTrackletLabels is size " << mTrackletLabels.getIndexedSize() << "::" << mTrackletLabels.getNElements() << " labels for additional labels vector of :" << localTrackletLabels.size() << " labels"; // store cluster information (if requested) - if (mgStoreClusters) { + if (mgStoreClusters && mdebugStream) { std::vector<float> res(getNumberOfTimeBins()); std::vector<float> qtot(getNumberOfTimeBins()); for (int iTimebin = 0; iTimebin < getNumberOfTimeBins(); ++iTimebin) { res[iTimebin] = 0; qtot[iTimebin] = 0; } - for (int iHit = 0; iHit < mNHits; iHit++) { int timebin = mHits[iHit].mTimebin; // check if hit contributes if (mHits[iHit].mChannel == mFitPtr[cpu]) { - // for (int i=0;i<mNHits;i++) LOG(info) << "mHits.["<<i << "].mTimebin="<< mHits[i].mTimebin; + // for (int i=0;i<mNHits;i++) LOG(debug) << "mHits.["<<i << "].mTimebin="<< mHits[i].mTimebin; res[timebin] = mHits[iHit].mYpos - (fitSlope * timebin + fitOffset); qtot[timebin] = mHits[iHit].mQtot; } else if (mHits[iHit].mChannel == mFitPtr[cpu] + 1) { res[timebin] = mHits[iHit].mYpos + 256 - (fitSlope * timebin + fitOffset); qtot[timebin] = mHits[iHit].mQtot; } - } - mTrackletArray[newtrackposition].setClusters(res, qtot, getNumberOfTimeBins()); - } - //TODO dont leave this commented out ! - if (fitError < 0) { - LOG(warn) << "fit slope: " << fitSlope << ", offset: " << fitOffset << ", error: " << TMath::Sqrt(TMath::Abs(fitError) / nHits); - LOG(info) << "Strange fit error: " << fitError << " from Sx: " << sumX << ", Sy: " << sumY << ", Sxy: " << sumXY << ", Sx2: " << sumX2 << ", Sy2: " << sumY2 << ", nHits: " << nHits; + // LOG(debug) << "slope : " << slope << " getDmemUnsigned " << mTrapConfig->getDmemUnsigned(mgkDmemAddrDeflCutStart + 2 * mFitPtr[cpu], mDetector, mRobPos, mMcmPos); + + if (rejected && getApplyCut()) { + mMCMT[cpu] = 0x10001000; //??? FeeParam::getTrackletEndmarker(); + } else { + if (slope > 127 || slope < -127) { // wrapping in TRAP! + LOG(debug) << "Overflow in slope: " << slope << ", tracklet discarded!"; + mMCMT[cpu] = 0x10001000; + continue; + } + + slope = slope & 0xff; // 8 bit + + if (position > 0x7ff || position < -0x7ff) { // 11 bits. + LOG(warning) << "Overflow in position with position of " << position << " in hex 0x" << std::hex << position; + } + position = position & 0x7ff; // 13 bit + + // assemble and store the tracklet word + TrackletMCMData trackletword; + buildTrackletMCMData(trackletword, slope, position, q0, q1, q2); + mMCMT[cpu] = trackletword.word; + + rawz = padrow; + rawposition = position; + + // calculate number of hits and MC label + // std::array<int, 3> mcLabel = {-1, -1, -1}; + std::array<int, 3> nHits{}; + + // const int maxLabels = 30; + // std::array<int, 30> label{}; // up to 30 different labels possible + // std::array<int, 30> count{}; + // int nLabels = 0; + std::vector<o2::MCCompLabel> localTrackletLabels; + //we have 21 adc so can fit into a 32bit bit pattern. + uint32_t adchitbp = 0; + for (int iHit = 0; iHit < mNHits; iHit++) { + if ((mHits[iHit].mChannel - mFitPtr[cpu] < 0) || + (mHits[iHit].mChannel - mFitPtr[cpu] > 1)) { + continue; + } + + // counting contributing hits + if (mHits[iHit].mTimebin >= mTrapConfig->getTrapReg(TrapConfig::kTPQS0, mDetector, mRobPos, mMcmPos) && + mHits[iHit].mTimebin < mTrapConfig->getTrapReg(TrapConfig::kTPQE0, mDetector, mRobPos, mMcmPos)) { + nHits[0]++; + } + if (mHits[iHit].mTimebin >= mTrapConfig->getTrapReg(TrapConfig::kTPQS1, mDetector, mRobPos, mMcmPos) && + mHits[iHit].mTimebin < mTrapConfig->getTrapReg(TrapConfig::kTPQE1, mDetector, mRobPos, mMcmPos)) { + nHits[1]++; + } + if (mHits[iHit].mTimebin >= 3 && mHits[iHit].mTimebin < 5) { + nHits[2]++; + } + //TODO nHits2 ???? to correspond to Q2 ??? + // + LOG(debug) << "setting bit pattern for chanel : " << mHits[iHit].mChannel << " of hit : " << iHit << std::hex << " bitp before : " << adchitbp << std::dec; + adchitbp |= (1 << mHits[iHit].mChannel); + LOG(debug) << "after setting bit pattern for chanel : " << mHits[iHit].mChannel << " of hit : " << iHit << std::hex << " bitp after : " << adchitbp << std::dec; + // TODO label calculation only if there is a digitsmanager to get the labels froAm + } + bool printoutadcs = false; + //TODO these info statemetns are left in to info the labels later. + LOG(debug) << "adc bitpatterh is " << std::hex << adchitbp << std::dec; + for (int i = 0; i < 21; i++) { + if (adchitbp & (1 << i)) { + LOG(debug) << "adding labels for adc " << i << "adc bitpatterh is " << std::hex << adchitbp << std::dec << " there are : " << mADCLabels[i].size() << " labels"; + + localTrackletLabels.insert(localTrackletLabels.end(), mADCLabels[i].begin(), mADCLabels[i].end()); // append the hit labels to this tracklets temp labels store. + if (mADCLabels[i].size() == 0) { + LOG(debug) << "BECAUSE THERE ARE NO LABELS LETS SEE WHICH ADC's DO HAVE LABELS"; + for (int j = 0; j < 21; j++) { + LOG(debug) << "ADC : " << j << " has " << mADCLabels[j].size() << " labels"; + } + printoutadcs = true; + } + } + } + //if(printoutadcs) print(PRINTRAW|PRINTFILTERED); + //labels come in via adc, now loop over ADC and see which contribute to the hits. + + LOG(debug) << "TrapSim Trackletarray size is : " << mTrackletArray64.size() << " :: adding a track at " << mMCMT[cpu] << ":" << mDetector * 2 + mRobPos % 2 << ":" << mRobPos << ":" << mMcmPos << " LABELS size: " << localTrackletLabels.size(); + uint32_t format = 0; + uint32_t hcid = mDetector * 2 + mRobPos % 2; + uint32_t padrow = ((mRobPos >> 1) << 2) | (mMcmPos >> 2); + uint32_t col = mFeeParam->getPadColFromADC(mRobPos, mMcmPos, 1); + //uint32_t position = rawoffset; + //uint32_t s + mTrackletArray64.emplace_back(format, hcid, padrow, col, position, slope, q0, q1, q2); + if (mdebugStream) { + mTrackletDetails.emplace_back(position, slope, q0, q1, q2, nHits, fitError); + } + //mTrackletArray[newtrackposition].setLabel(mcLabel); + LOG(debug) << "adding elements to mTrackletLabels of size " << mTrackletLabels.getIndexedSize() << "::" << mTrackletLabels.getNElements() << " labels for additional labels vector of :" << localTrackletLabels.size() << " labels"; + mTrackletLabels.addElements(mTrackletLabels.getIndexedSize(), localTrackletLabels); + LOG(debug) << "elements in mTrackletLabels is size " << mTrackletLabels.getIndexedSize() << "::" << mTrackletLabels.getNElements() << " labels for additional labels vector of :" << localTrackletLabels.size() << " labels"; + // store cluster information (if requested) + if (mgStoreClusters && mdebugStream) { + std::vector<float> res(getNumberOfTimeBins()); + std::vector<float> qtot(getNumberOfTimeBins()); + for (int iTimebin = 0; iTimebin < getNumberOfTimeBins(); ++iTimebin) { + res[iTimebin] = 0; + qtot[iTimebin] = 0; + } + for (int iHit = 0; iHit < mNHits; iHit++) { + int timebin = mHits[iHit].mTimebin; + + // check if hit contributes + if (mHits[iHit].mChannel == mFitPtr[cpu]) { + // for (int i=0;i<mNHits;i++) LOG(debug) << "mHits.["<<i << "].mTimebin="<< mHits[i].mTimebin; + res[timebin] = mHits[iHit].mYpos - (fitSlope * timebin + fitOffset); + qtot[timebin] = mHits[iHit].mQtot; + } else if (mHits[iHit].mChannel == mFitPtr[cpu] + 1) { + res[timebin] = mHits[iHit].mYpos + 256 - (fitSlope * timebin + fitOffset); + qtot[timebin] = mHits[iHit].mQtot; + } + } + mTrackletDetails[mTrackletDetails.size() - 1].setClusters(res, qtot, getNumberOfTimeBins()); + // TODO we no longer store clusters in 64bit tracklets, put where? + } + + if (fitError < 0) { + LOG(debug) << "fit slope: " << fitSlope << ", offset: " << fitOffset << ", error: " << TMath::Sqrt(TMath::Abs(fitError) / getNHits()); + + LOG(debug) << "Strange fit error: " << fitError << " from Sx: " << sumX << ", Sy: " << sumY << ", Sxy: " << sumXY << ", Sx2: " << sumX2 << ", Sy2: " << sumY2 << ", nHitssize: " << nHits.size(); + } + } + } } + LOG(debug) << "LEAVING : " << __FILE__ << ":" << __func__ << ":" << __LINE__ << " :: " << getDetector() << ":" << getRobPos() << ":" << getMcmPos() << " and Tracklet array size is : " << mTrackletArray64.size(); } } } - // LOG(info) << "LEAVING : " << __FILE__ << ":" << __func__ << ":" << __LINE__<<" :: "<< getDetector()<<":"<< getRobPos()<<":"<< getMcmPos() << " and Tracklet array size is : " << mTrackletArray.size(); } void TrapSimulator::tracklet() @@ -2029,21 +2349,24 @@ void TrapSimulator::tracklet() if (!mInitialized) { return; } - mTrackletArray.clear(); - + LOG(debug) << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"; + mTrackletArray64.clear(); calcFitreg(); if (mNHits == 0) { return; } trackletSelection(); fitTracklet(); + //print(15); + LOG(debug) << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"; + LOG(debug) << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"; } -void TrapSimulator::getTracklets(std::vector<Tracklet>& TrackletStore) +void TrapSimulator::getTracklet64s(std::vector<Tracklet64>& TrackletStore) { // simply returns the found tracklets for the O2 dpl to then do its thing. // - TrackletStore.insert(std::end(TrackletStore), std::begin(mTrackletArray), std::end(mTrackletArray)); + TrackletStore.insert(std::end(TrackletStore), std::begin(mTrackletArray64), std::end(mTrackletArray64)); //std::copy(mTrackletArray.begin(),mTrackletArray.end(),std::back_inserter(TrackletStore)); } @@ -2051,7 +2374,7 @@ void TrapSimulator::getTracklets(std::vector<Tracklet>& TrackletStore) // PID section // // Memory area for the LUT: 0xC100 to 0xC3FF -// +// ??? TODO Q2 ??? Aso do we still need this function? We are sending back the charge sumations now. // The addresses for the parameters (the order is optimized for maximum calculation speed in the MCMs): // 0xC028: cor1 // 0xC029: nBins(sF) @@ -2078,6 +2401,7 @@ void TrapSimulator::getTracklets(std::vector<Tracklet>& TrackletStore) // 16) if >=, clip to TableLength-1 // 17) read from the LUT 8 bits // TODO the new program from Venelin will be different. +// // TODO ask someone, we are taking q0,q1,q2 and sending it back, no pid calculation? int TrapSimulator::getPID(int q0, int q1) { @@ -2088,19 +2412,21 @@ int TrapSimulator::getPID(int q0, int q1) unsigned int nBinsQ0 = mTrapConfig->getDmemUnsigned(mgkDmemAddrLUTnbins, mDetector, mRobPos, mMcmPos); // number of bins in q0 / 4 !! unsigned int pidTotalSize = mTrapConfig->getDmemUnsigned(mgkDmemAddrLUTLength, mDetector, mRobPos, mMcmPos); - if (nBinsQ0 == 0 || pidTotalSize == 0) // make sure we don't run into trouble if the value for Q0 is not configured - return 0; // Q1 not configured is ok for 1D LUT + if (nBinsQ0 == 0 || pidTotalSize == 0) { // make sure we don't run into trouble if the value for Q0 is not configured + return 0; // Q1 not configured is ok for 1D LUT + } unsigned long corrQ0 = mTrapConfig->getDmemUnsigned(mgkDmemAddrLUTcor0, mDetector, mRobPos, mMcmPos); unsigned long corrQ1 = mTrapConfig->getDmemUnsigned(mgkDmemAddrLUTcor1, mDetector, mRobPos, mMcmPos); - if (corrQ0 == 0) // make sure we don't run into trouble if one of the values is not configured + if (corrQ0 == 0) { // make sure we don't run into trouble if one of the values is not configured return 0; + } addrQ0 = corrQ0; addrQ0 = (((addrQ0 * q0) >> 16) >> 16); // because addrQ0 = (q0 * corrQ0) >> 32; does not work for unknown reasons if (addrQ0 >= nBinsQ0) { // check for overflow - // LOG(debug3) << "Overflow in q0: " << addrQ0 << "/4 is bigger then " << nBinsQ0; + // LOG(debug) << "Overflow in q0: " << addrQ0 << "/4 is bigger then " << nBinsQ0; addrQ0 = nBinsQ0 - 1; } @@ -2109,7 +2435,7 @@ int TrapSimulator::getPID(int q0, int q1) addr = addrQ0 + nBinsQ0 * addr; // because addr = addrQ0 + nBinsQ0* (((corrQ1*q1)>>32); does not work if (addr >= pidTotalSize) { - // LOG(debug3) << "Overflow in q1. Address " << addr << "/4 is bigger then " << pidTotalSize; + // LOG(debug) << "Overflow in q1. Address " << addr << "/4 is bigger then " << pidTotalSize; addr = pidTotalSize - 1; } @@ -2131,11 +2457,13 @@ unsigned int TrapSimulator::addUintClipping(unsigned int a, unsigned int b, unsi unsigned int sum = a + b; if (nbits < 32) { unsigned int maxv = (1 << nbits) - 1; - if (sum > maxv) + if (sum > maxv) { sum = maxv; + } } else { - if ((sum < a) || (sum < b)) + if ((sum < a) || (sum < b)) { sum = 0xFFFFFFFF; + } } return sum; } @@ -2169,14 +2497,17 @@ void TrapSimulator::sort3(unsigned short idx1i, unsigned short idx2i, unsigned s int sel; - if (val1i > val2i) + if (val1i > val2i) { sel = 4; - else + } else { sel = 0; - if (val2i > val3i) + } + if (val2i > val3i) { sel = sel + 2; - if (val3i > val1i) + } + if (val3i > val1i) { sel = sel + 1; + } switch (sel) { case 6: // 1 > 2 > 3 => 1 2 3 case 0: // 1 = 2 = 3 => 1 2 3 : in this case doesn't matter, but so is in hardware! @@ -2324,7 +2655,7 @@ bool TrapSimulator::readPackedConfig(TrapConfig* cfg, int hc, unsigned int* data data++; idx++; - LOG(debug3) << "read: 0x" << hex << header; + LOG(debug) << "read: 0x" << hex << header; if (header & 0x01) // single data { @@ -2336,22 +2667,22 @@ bool TrapSimulator::readPackedConfig(TrapConfig* cfg, int hc, unsigned int* data if (header & 0x02) // check if > 16 bits { dataHi = *data; - LOG(debug3) << "read: 0x" << hex << dataHi; + LOG(debug) << "read: 0x" << hex << dataHi; data++; idx++; err += ((dataHi ^ (dat | 1)) & 0xFFFF) != 0; dat = (dataHi & 0xFFFF0000) | dat; } - LOG(debug3) << "addr=0x" << hex << caddr << "(" << cfg->getRegName(cfg->getRegByAddress(caddr)) << ") data=0x" << hex << dat; + LOG(debug) << "addr=0x" << hex << caddr << "(" << cfg->getRegName(cfg->getRegByAddress(caddr)) << ") data=0x" << hex << dat; if (!cfg->poke(caddr, dat, det, rob, mcm)) { - LOG(debug3) << "(single-write): non-existing address 0x" << std::hex << caddr << " containing 0x" << std::hex << header; + LOG(debug) << "(single-write): non-existing address 0x" << std::hex << caddr << " containing 0x" << std::hex << header; } if (idx > size) { - LOG(debug3) << "(single-write): no more data, missing end marker"; + LOG(debug) << "(single-write): no more data, missing end marker"; return -err; } } else { - LOG(debug3) << "(single-write): address 0x" << setw(4) << std::hex << caddr << " => old endmarker?" << std::dec; + LOG(debug) << "(single-write): address 0x" << setw(4) << std::hex << caddr << " => old endmarker?" << std::dec; return err; } } @@ -2381,22 +2712,22 @@ bool TrapSimulator::readPackedConfig(TrapConfig* cfg, int hc, unsigned int* data bitcnt -= bwidth; if (bitcnt < 0) { header = *data; - LOG(debug3) << "read 0x" << setw(8) << std::hex << header << std::dec; + LOG(debug) << "read 0x" << setw(8) << std::hex << header << std::dec; data++; idx++; err += (header & 1); header = header >> 1; bitcnt = 31 - bwidth; } - LOG(debug3) << "addr=0x" << setw(4) << std::hex << caddr << "(" << cfg->getRegName(cfg->getRegByAddress(caddr)) << ") data=0x" << setw(8) << std::hex << (header & msk); + LOG(debug) << "addr=0x" << setw(4) << std::hex << caddr << "(" << cfg->getRegName(cfg->getRegByAddress(caddr)) << ") data=0x" << setw(8) << std::hex << (header & msk); if (!cfg->poke(caddr, header & msk, det, rob, mcm)) { - LOG(debug3) << "(single-write): non-existing address 0x" << setw(4) << std::hex << caddr << " containing 0x" << setw(8) << std::hex << header << std::dec; + LOG(debug) << "(single-write): non-existing address 0x" << setw(4) << std::hex << caddr << " containing 0x" << setw(8) << std::hex << header << std::dec; } caddr += step; header = header >> bwidth; if (idx >= size) { - LOG(debug3) << "(block-write): no end marker! " << idx << " words read"; + LOG(debug) << "(block-write): no end marker! " << idx << " words read"; return -err; } } @@ -2405,20 +2736,20 @@ bool TrapSimulator::readPackedConfig(TrapConfig* cfg, int hc, unsigned int* data case 31: { while (nwords > 0) { header = *data; - LOG(debug3) << "read 0x" << setw(8) << std::hex << header; + LOG(debug) << "read 0x" << setw(8) << std::hex << header; data++; idx++; nwords--; err += (header & 1); - LOG(debug3) << "addr=0x" << hex << setw(4) << caddr << " (" << cfg->getRegName(cfg->getRegByAddress(caddr)) << ") data=0x" << hex << setw(8) << (header >> 1); + LOG(debug) << "addr=0x" << hex << setw(4) << caddr << " (" << cfg->getRegName(cfg->getRegByAddress(caddr)) << ") data=0x" << hex << setw(8) << (header >> 1); if (!cfg->poke(caddr, header >> 1, det, rob, mcm)) { - LOG(debug3) << "(single-write): non-existing address 0x" << setw(4) << std::hex << " containing 0x" << setw(8) << std::hex << header << std::dec; + LOG(debug) << "(single-write): non-existing address 0x" << setw(4) << std::hex << " containing 0x" << setw(8) << std::hex << header << std::dec; } caddr += step; if (idx >= size) { - LOG(debug3) << "no end marker! " << idx << " words read"; + LOG(debug) << "no end marker! " << idx << " words read"; return -err; } } @@ -2433,4 +2764,3 @@ bool TrapSimulator::readPackedConfig(TrapConfig* cfg, int hc, unsigned int* data LOG(debug) << "no end marker! " << idx << " words read"; return -err; // only if the max length of the block reached! } - diff --git a/Detectors/TRD/simulation/src/trap2raw.cxx b/Detectors/TRD/simulation/src/trap2raw.cxx new file mode 100644 index 0000000000000..bcba5a7c959dd --- /dev/null +++ b/Detectors/TRD/simulation/src/trap2raw.cxx @@ -0,0 +1,140 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/////////////////////////////////////////////////////////////////////////////// +// // +// TRD Raw data format generation // +// Take the output of the trapsimulator the [2-4]x32 bit words and // +// associated headers and links etc. and produce the output of the cru. // +// Hence the incredibly original name. // +// // +// Some parts bare a striking resemblence to (at least when written) // +// FIT/FTO/simulation/src/digit2raw.cxx // +/////////////////////////////////////////////////////////////////////////////// + +#include "fairlogger/Logger.h" + +#include <DataFormatsTRD/RawData.h> +#include <DataFormatsTRD/Tracklet64.h> +#include <DataFormatsTRD/LinkRecord.h> +#include <DataFormatsTRD/TriggerRecord.h> + +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "DetectorsRaw/HBFUtils.h" +#include "TRDSimulation/Trap2CRU.h" +#include "DataFormatsParameters/GRPObject.h" + +#include <boost/program_options.hpp> +#include <TSystem.h> +#include <TFile.h> +#include <TStopwatch.h> +#include <string> +#include <iomanip> +#include <iostream> +#include <iomanip> +#include "TCanvas.h" +#include <TTree.h> +#include <TFile.h> +#include <ostream> +#include <fstream> + +namespace bpo = boost::program_options; + +void trap2raw(const std::string& inpName, const std::string& outDir, int verbosity, + bool filePerLink, uint32_t rdhV = 4, bool noEmptyHBF = false, + int superPageSizeInB = 1024 * 1024); + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + "Convert TRD sim otuput to CRU raw 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<int>()->default_value(0), "verbosity level"); + add_option("input-file,i", bpo::value<std::string>()->default_value("trdtrapraw.root"), "input Trapsim raw file"); + add_option("file-per-halfcru,l", bpo::value<bool>()->default_value(false)->implicit_value(true), "create output file per half cru, 2 files per cru 15 links each."); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + uint32_t defRDH = o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>(); + add_option("rdh-version,r", bpo::value<uint32_t>()->default_value(defRDH), "RDH version to use"); + add_option("no-empty-hbf,e", bpo::value<bool>()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + // add_option("data-format,d", bpo::value<uint32_t>()->default_value(1)->implicit_value(true), "format of data, default 1, see documentation."); + add_option("configKeyValues", bpo::value<std::string>()->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<std::string>()); + + std::cout << "yay it ran" << std::endl; + trap2raw(vm["input-file"].as<std::string>(), vm["output-dir"].as<std::string>(), vm["verbosity"].as<int>(), + vm["file-per-halfcru"].as<bool>(), vm["rdh-version"].as<uint32_t>(), vm["no-empty-hbf"].as<bool>()); + + return 0; +} + +void trap2raw(const std::string& inpName, const std::string& outDir, int verbosity, bool filePerLink, uint32_t rdhV, bool noEmptyHBF, int superPageSizeInB) +{ + + TStopwatch swTot; + swTot.Start(); + o2::trd::Trap2CRU mc2raw; + //mc2raw.setFilePerLink(filePerLink); TODO come back to this, lets get it working first. + mc2raw.setVerbosity(verbosity); + auto& wr = mc2raw.getWriter(); + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + // wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::TRD)); // must be set explicitly + wr.setContinuousReadout(false); // above should work, but I know this is correct, come back TODO + wr.setSuperPageSize(superPageSizeInB); + wr.useRDHVersion(rdhV); + wr.setDontFillEmptyHBF(noEmptyHBF); + + std::string outDirName(outDir); + if (outDirName.back() != '/') { + outDirName += '/'; + } + // if needed, create output directory + if (gSystem->AccessPathName(outDirName.c_str())) { + if (gSystem->mkdir(outDirName.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outDirName; + } else { + LOG(INFO) << "created output directory " << outDirName; + } + } + + mc2raw.readTrapData(outDirName, inpName, superPageSizeInB); + wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); + // + swTot.Stop(); + swTot.Print(); +} diff --git a/Detectors/TRD/workflow/CMakeLists.txt b/Detectors/TRD/workflow/CMakeLists.txt index 078158c38810c..512ab65af4640 100644 --- a/Detectors/TRD/workflow/CMakeLists.txt +++ b/Detectors/TRD/workflow/CMakeLists.txt @@ -8,7 +8,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -#add_compile_options(-O0 -pg -fPIC) +#add_compile_options(-O0 -pg -fPIC) o2_add_library(TRDWorkflow TARGETVARNAME targetName @@ -16,8 +16,15 @@ o2_add_library(TRDWorkflow src/TRDDigitWriterSpec.cxx src/TRDDigitReaderSpec.cxx src/TRDTrackletWriterSpec.cxx + src/TRDCalibratedTrackletWriterSpec.cxx + src/TRDTrackletReaderSpec.cxx + src/TRDTrapRawWriterSpec.cxx src/TRDTrapSimulatorSpec.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::Steer O2::Algorithm O2::DataFormatsTRD O2::TRDSimulation O2::DetectorsBase O2::SimulationDataFormat O2::TRDBase) + src/TRDTrackletTransformerSpec.cxx + src/TRDGlobalTrackingSpec.cxx + src/TRDTrackWriterSpec.cxx + src/TRDTrackingWorkflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::Steer O2::Algorithm O2::DataFormatsTRD O2::TRDSimulation O2::DetectorsBase O2::SimulationDataFormat O2::TRDBase O2::GPUTracking O2::GlobalTrackingWorkflow) #o2_target_root_dictionary(TRDWorkflow # HEADERS include/TRDWorkflow/TRDTrapSimulatorSpec.h) @@ -32,8 +39,18 @@ o2_add_executable(trap-sim O2::DataFormatsTRD O2::TRDWorkflow) +o2_add_executable(global-tracking + COMPONENT_NAME trd + SOURCES src/trd-tracking-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::TRDWorkflow) + +o2_add_executable(tracklet-transformer + COMPONENT_NAME trd + SOURCES src/TRDTrackletTransformerWorkflow.cxx + PUBLIC_LINK_LIBRARIES O2::TRDWorkflow) + + if (OpenMP_CXX_FOUND) target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) endif() - diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDCalibratedTrackletWriterSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDCalibratedTrackletWriterSpec.h new file mode 100644 index 0000000000000..bf62beed66acf --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDCalibratedTrackletWriterSpec.h @@ -0,0 +1,26 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRDCALIBRATEDTRACKLETWRITER_H +#define O2_TRDCALIBRATEDTRACKLETWRITER_H + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace trd +{ + +framework::DataProcessorSpec getTRDCalibratedTrackletWriterSpec(); + +} // end namespace trd +} // end namespace o2 + +#endif diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDDigitReaderSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDDigitReaderSpec.h index 427a67af6249e..2837bd004e363 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDDigitReaderSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDDigitReaderSpec.h @@ -11,16 +11,13 @@ #ifndef O2_TRDTRAPSIMULATORRAWREADERSPEC_H #define O2_TRDTRAPSIMULATORRAWREADERSPEC_H -#include "TRDBase/Digit.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 <SimulationDataFormat/MCTruthContainer.h> #include "TRDBase/MCLabel.h" -#include "DataFormatsTRD/TriggerRecord.h" #include "TFile.h" #include "TTree.h" @@ -44,9 +41,6 @@ class TRDDigitReaderSpec : public o2::framework::Task int mChannels; std::unique_ptr<TFile> mFile = nullptr; //std::unique_ptr<TTree> DPLTree; - std::vector<o2::trd::Digit> mDigits, *mPDigits = &mDigits; - o2::dataformats::MCTruthContainer<o2::trd::MCLabel> mMCLabels, *mPMCLabels = &mMCLabels; - std::vector<o2::trd::TriggerRecord> mTriggerRecords, *mPTriggerRecords = &mTriggerRecords; std::string mInputFileName = ""; std::string mDigitTreeName = "o2sim"; std::string mDigitBranchName = "TRDDigit"; diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h new file mode 100644 index 0000000000000..12c21c6ab4792 --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h @@ -0,0 +1,52 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TRDGlobalTrackingSpec.h + +#ifndef O2_TRD_GLOBALTRACKING +#define O2_TRD_GLOBALTRACKING + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TStopwatch.h" +#include "TRDBase/GeometryFlat.h" +#include "GPUO2Interface.h" +#include "GPUTRDTracker.h" + +namespace o2 +{ +namespace trd +{ + +class TRDGlobalTracking : public o2::framework::Task +{ + public: + TRDGlobalTracking(bool useMC) : mUseMC(useMC) {} + ~TRDGlobalTracking() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + o2::gpu::GPUTRDTracker* mTracker{nullptr}; ///< TRD tracking engine + o2::gpu::GPUReconstruction* mRec{nullptr}; ///< GPU reconstruction pointer, handles memory for the tracker + o2::gpu::GPUChainTracking* mChainTracking{nullptr}; ///< TRD tracker is run in the tracking chain + std::unique_ptr<GeometryFlat> mFlatGeo{nullptr}; ///< flat TRD geometry + bool mUseMC{false}; ///< MC flag + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC); + +} // namespace trd +} // namespace o2 + +#endif /* O2_TRD_TRACKLETREADER */ diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackWriterSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackWriterSpec.h new file mode 100644 index 0000000000000..4bacd79c5a014 --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackWriterSpec.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_TRACK_WRITER_H +#define O2_TRD_TRACK_WRITER_H + +/// @file TRDTrackWriterSpec.h + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace trd +{ + +/// create a processor spec +framework::DataProcessorSpec getTRDTrackWriterSpec(bool useMC); + +} // namespace trd +} // namespace o2 + +#endif diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackingWorkflow.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackingWorkflow.h new file mode 100644 index 0000000000000..1bd9e6e33bd81 --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackingWorkflow.h @@ -0,0 +1,28 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRD_TRACKING_WORKFLOW_H +#define O2_TRD_TRACKING_WORKFLOW_H + +/// @file TRDTrackingWorkflow.h + +#include "Framework/WorkflowSpec.h" + +namespace o2 +{ +namespace trd +{ + +framework::WorkflowSpec getTRDTrackingWorkflow(bool disableRootInp, bool disableRootOut); + +} // namespace trd +} // namespace o2 + +#endif diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackletReaderSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackletReaderSpec.h new file mode 100644 index 0000000000000..cdc839a2f9e4c --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackletReaderSpec.h @@ -0,0 +1,57 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TRDTrackletReaderSpec.h + +#ifndef O2_TRD_TRACKLETREADER +#define O2_TRD_TRACKLETREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2 +{ +namespace trd +{ + +class TRDTrackletReader : public o2::framework::Task +{ + public: + TRDTrackletReader(bool useMC) : mUseMC(useMC) {} + ~TRDTrackletReader() 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}; + std::unique_ptr<TFile> mFile; + std::unique_ptr<TTree> mTree; + std::string mInFileName{"trdtracklets.root"}; + std::string mInTreeName{"o2sim"}; + std::vector<o2::trd::Tracklet64> mTracklets, *mTrackletsPtr = &mTracklets; + std::vector<o2::trd::TriggerRecord> mTriggerRecords, *mTriggerRecordsPtr = &mTriggerRecords; + std::vector<o2::MCCompLabel> mLabels, *mLabelsPtr = &mLabels; +}; + +/// create a processor spec +/// read TRD tracklets from a root file +framework::DataProcessorSpec getTRDTrackletReaderSpec(bool useMC); + +} // namespace trd +} // namespace o2 + +#endif /* O2_TRD_TRACKLETREADER */ diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackletTransformerSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackletTransformerSpec.h new file mode 100644 index 0000000000000..ffbc41f026a37 --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrackletTransformerSpec.h @@ -0,0 +1,36 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "Framework/Task.h" + +#include "TRDBase/TrackletTransformer.h" + +namespace o2 +{ +namespace trd +{ + +class TRDTrackletTransformerSpec : public o2::framework::Task +{ + public: + // TRDTrackletTransformerSpec(); + // ~TRDTrackletTransformerSpec() override = default; + void init(o2::framework::InitContext& ic) override; + void run(o2::framework::ProcessingContext& pc) override; + + private: + o2::trd::TrackletTransformer mTransformer; +}; + +o2::framework::DataProcessorSpec getTRDTrackletTransformerSpec(); + +} // end namespace trd +} // end namespace o2 diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapRawWriterSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapRawWriterSpec.h new file mode 100644 index 0000000000000..82189f7affe7f --- /dev/null +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapRawWriterSpec.h @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRDTRAPSIMULATORRAWWRITER_H +#define O2_TRDTRAPSIMULATORRAWWRITER_H + +#include <fstream> +#include <iostream> + +// This is the raw data that is output by the trap trap chip. +// mcmheader word followed by 1 or more traprawtracklet words. +// and a halfchamber header at the beginning of a half chamber. +// halfchamber header + mcmheader uniquely identifies the chip producing the data. + +namespace o2 +{ +namespace framework +{ +struct DataProcessorSpec; +} +} // namespace o2 + +namespace o2 +{ +namespace trd +{ + +o2::framework::DataProcessorSpec getTRDTrapRawWriterSpec(); + +} // end namespace trd +} // end namespace o2 + +#endif // O2_TRDTRAPSIMULATORRAWWRITER_H diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapSimulatorSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapSimulatorSpec.h index dec702aa3d185..34d77dc76372a 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapSimulatorSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDTrapSimulatorSpec.h @@ -18,9 +18,11 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "TRDBase/FeeParam.h" -#include "TRDBase/Tracklet.h" #include "TRDSimulation/TrapSimulator.h" #include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/LinkRecord.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/RawData.h" #include "TRDSimulation/TrapConfig.h" #include "CCDB/BasicCCDBManager.h" @@ -39,13 +41,18 @@ class TRDDPLTrapSimulatorTask : public o2::framework::Task void init(o2::framework::InitContext& ic) override; void run(o2::framework::ProcessingContext& pc) override; - void fixTriggerRecords(std::vector<o2::trd::TriggerRecord>& trigRecord); + + protected: + void fixTriggerRecords(std::vector<o2::trd::TriggerRecord>& trigRecord); // should be temporary. + void setTriggerRecord(std::vector<o2::trd::TriggerRecord>& triggerrecord, uint32_t currentrecord, uint64_t recordsize); + void setTrapSimulatorData(int adc, std::vector<o2::trd::Digit>& digits, int digitposition); + // TODO LABELS, o2::dataformats::MCTruthContainer<o2::MCCompLabel>* labels) private: std::array<TrapSimulator, 8> mTrapSimulator; //the 8 trap simulators for a given padrow. FeeParam* mFeeParam = nullptr; TrapConfig* mTrapConfig = nullptr; - std::unique_ptr<TRDGeometry> mGeo; + std::unique_ptr<Geometry> mGeo; // std::unique_ptr<TrapConfigHandler> mTrapConfigHandler; int mNumThreads = 8; unsigned long mRunNumber = 297595; //run number to anchor simulation to. @@ -57,14 +64,14 @@ class TRDDPLTrapSimulatorTask : public o2::framework::Task bool mDebugRejectedTracklets{false}; bool mEnableOnlineGainCorrection{false}; bool mEnableTrapConfigDump{false}; - bool mFixTriggerRecords{false}; // shift the trigger record due to its being corrupt on coming in. - std::vector<Tracklet> mTracklets; // store of found tracklets + std::vector<Tracklet64> mTracklets; // store of found tracklets std::string mTrapConfigName; // the name of the config to be used. std::string mTrapConfigBaseName = "TRD_test/TrapConfig/"; std::unique_ptr<CalOnlineGainTables> mGainTable; //this will probably not be used in run3. std::string mOnlineGainTableName; std::unique_ptr<Calibrations> mCalib; // store the calibrations connection to CCDB. Used primarily for the gaintables in line above. + std::vector<o2::trd::LinkRecord> mLinkRecords; //arrays to keep some stats during processing std::array<unsigned int, 8> mTrapUsedCounter{0}; std::array<unsigned int, 8> mTrapUsedFrequency{0}; @@ -77,8 +84,13 @@ class TRDDPLTrapSimulatorTask : public o2::framework::Task std::chrono::duration<double> mTrackletTime{0}; ///< full timer std::chrono::duration<double> mSortingTime{0}; ///< full timer + uint64_t mTotalRawWordsWritten = 0; // words written for the raw format of 4x32bits, where 4 can be 2 to 4 depending on # of tracklets in the block. + int32_t mOldHalfChamberLinkId = 0; + bool mNewTrackletHCHeaderHasBeenWritten{false}; + TrackletHCHeader mTrackletHCHeader; // the current half chamber header, that will be written if a first tracklet is found for this halfchamber. TrapConfig* getTrapConfig(); void loadTrapConfig(); + void loadDefaultTrapConfig(); void setOnlineGainTables(); }; diff --git a/Detectors/TRD/workflow/src/TRDCalibratedTrackletWriterSpec.cxx b/Detectors/TRD/workflow/src/TRDCalibratedTrackletWriterSpec.cxx new file mode 100644 index 0000000000000..84cfa12dcb34f --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDCalibratedTrackletWriterSpec.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDWorkflow/TRDCalibratedTrackletWriterSpec.h" +#include "DataFormatsTRD/CalibratedTracklet.h" + +#include "DPLUtils/MakeRootTreeWriterSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace trd +{ + +template <typename T> +using BranchDefinition = framework::MakeRootTreeWriterSpec::BranchDefinition<T>; + +o2::framework::DataProcessorSpec getTRDCalibratedTrackletWriterSpec() +{ + using MakeRootTreeWriterSpec = framework::MakeRootTreeWriterSpec; + + return MakeRootTreeWriterSpec("calibrated-tracklet-writer", + "trdcalibratedtracklets.root", + "ctracklets", + 1, + BranchDefinition<std::vector<CalibratedTracklet>>{InputSpec{"ctracklets", "TRD", "CTRACKLETS"}, "CTracklets"})(); + // BranchDefinition<std::vector<o2::trd::TriggerRecord>>{InputSpec{"tracklettrigs", "TRD", "TRKTRGRD"}, "TrackTrg"})(); +}; + +} // end namespace trd +} // end namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDDigitReaderSpec.cxx b/Detectors/TRD/workflow/src/TRDDigitReaderSpec.cxx index 521ce7a4f1731..9931ad0269fa2 100644 --- a/Detectors/TRD/workflow/src/TRDDigitReaderSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDDigitReaderSpec.cxx @@ -24,7 +24,8 @@ #include "Steer/HitProcessingManager.h" // for DigitizationContext #include "TChain.h" #include <SimulationDataFormat/MCCompLabel.h> -#include <SimulationDataFormat/MCTruthContainer.h> +#include <SimulationDataFormat/ConstMCTruthContainer.h> +#include <SimulationDataFormat/IOMCTruthContainerView.h> #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" #include "TRDBase/Digit.h" // for the Digit type @@ -82,16 +83,28 @@ void TRDDigitReaderSpec::run(ProcessingContext& pc) LOG(info) << "mState is not 1"; return; } - TTree* DPLTree = ((TTree*)mFile->Get(mDigitTreeName.c_str())); + auto DPLTree = ((TTree*)mFile->Get(mDigitTreeName.c_str())); if (DPLTree) { - DPLTree->SetBranchAddress(mDigitBranchName.c_str(), &mPDigits); - DPLTree->SetBranchAddress(mTriggerRecordBranchName.c_str(), &mPTriggerRecords); - DPLTree->SetBranchAddress(mMCLabelsBranchName.c_str(), &mPMCLabels); - DPLTree->GetEntry(0); - pc.outputs().snapshot(Output{"TRD", "DIGITS", 0, Lifetime::Timeframe}, mDigits); - pc.outputs().snapshot(Output{"TRD", "TRGRDIG", 0, Lifetime::Timeframe}, mTriggerRecords); - pc.outputs().snapshot(Output{"TRD", "LABELS", 0, Lifetime::Timeframe}, mMCLabels); - LOG(info) << "TRDDigitReader digits size=" << mDigits.size() << " triggerrecords size=" << mTriggerRecords.size() << " mc labels size=" << mMCLabels.getNElements(); + std::vector<o2::trd::Digit>* digits = nullptr; + o2::dataformats::IOMCTruthContainerView* ioLabels = nullptr; + std::vector<o2::trd::TriggerRecord>* triggerRecords = nullptr; + + auto getFromBranch = [DPLTree](const char* name, void** ptr) { + auto br = DPLTree->GetBranch(name); + br->SetAddress(ptr); + br->GetEntry(0); + br->ResetAddress(); + }; + getFromBranch(mDigitBranchName.c_str(), (void**)&digits); + getFromBranch(mTriggerRecordBranchName.c_str(), (void**)&triggerRecords); + getFromBranch(mMCLabelsBranchName.c_str(), (void**)&ioLabels); + + // publish labels in shared memory + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::trd::MCLabel>>(Output{"TRD", "LABELS", 0, Lifetime::Timeframe}); + ioLabels->copyandflatten(sharedlabels); + pc.outputs().snapshot(Output{"TRD", "DIGITS", 0, Lifetime::Timeframe}, *digits); + pc.outputs().snapshot(Output{"TRD", "TRGRDIG", 0, Lifetime::Timeframe}, *triggerRecords); + LOG(info) << "TRDDigitReader digits size=" << digits->size() << " triggerrecords size=" << triggerRecords->size() << " mc labels size (in bytes) = " << sharedlabels.size(); } //delete DPLTree; // next line will delete the pointer as well. mFile->Close(); diff --git a/Detectors/TRD/workflow/src/TRDDigitWriterSpec.cxx b/Detectors/TRD/workflow/src/TRDDigitWriterSpec.cxx index 568a781715a2b..b71d52b5b916e 100644 --- a/Detectors/TRD/workflow/src/TRDDigitWriterSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDDigitWriterSpec.cxx @@ -16,7 +16,8 @@ #include "Framework/InputSpec.h" #include "TRDBase/Digit.h" #include "DataFormatsTRD/TriggerRecord.h" -#include <SimulationDataFormat/MCTruthContainer.h> +#include <SimulationDataFormat/ConstMCTruthContainer.h> +#include <SimulationDataFormat/IOMCTruthContainerView.h> #include "TRDBase/MCLabel.h" #include "TRDWorkflow/TRDDigitWriterSpec.h" @@ -32,13 +33,50 @@ o2::framework::DataProcessorSpec getTRDDigitWriterSpec(bool mctruth) { using InputSpec = framework::InputSpec; using MakeRootTreeWriterSpec = framework::MakeRootTreeWriterSpec; + using DataRef = framework::DataRef; + + // the callback to be set as hook for custom action when the writer is closed + auto finishWriting = [](TFile* outputfile, TTree* outputtree) { + const auto* brArr = outputtree->GetListOfBranches(); + int64_t nent = 0; + for (const auto* brc : *brArr) { + int64_t n = ((const TBranch*)brc)->GetEntries(); + if (nent && (nent != n)) { + LOG(ERROR) << "Branches have different number of entries"; + } + nent = n; + } + outputtree->SetEntries(nent); + outputtree->Write(); + outputfile->Close(); + }; + + // custom handler for labels: + // essentially transform the input container (as registered in the original branch definition) to the special output format for labels + auto customlabelhandler = [](TBranch& branch, std::vector<char> const& labeldata, DataRef const& ref) { + // make the actual output object by adopting/casting the buffer + // into a split format + o2::dataformats::IOMCTruthContainerView outputcontainer(labeldata); + auto br = framework::RootTreeWriter::remapBranch(branch, &outputcontainer); + br->Fill(); + br->ResetAddress(); + }; + + auto labelsdef = BranchDefinition<std::vector<char>>{InputSpec{"labelinput", "TRD", "LABELS"}, + "TRDMCLabels", "labels-branch-name", + // this branch definition is disabled if MC labels are not processed + (mctruth ? 1 : 0), + customlabelhandler}; + return MakeRootTreeWriterSpec("TRDDigitWriter", "trddigits.root", "o2sim", 1, + // setting a custom callback for closing the writer + MakeRootTreeWriterSpec::CustomClose(finishWriting), BranchDefinition<std::vector<o2::trd::Digit>>{InputSpec{"input", "TRD", "DIGITS"}, "TRDDigit"}, BranchDefinition<std::vector<o2::trd::TriggerRecord>>{InputSpec{"trinput", "TRD", "TRGRDIG"}, "TriggerRecord"}, - BranchDefinition<o2::dataformats::MCTruthContainer<o2::trd::MCLabel>>{InputSpec{"labelinput", "TRD", "LABELS"}, "TRDMCLabels", mctruth ? 1 : 0})(); + std::move(labelsdef))(); } } // end namespace trd diff --git a/Detectors/TRD/workflow/src/TRDDigitizerSpec.cxx b/Detectors/TRD/workflow/src/TRDDigitizerSpec.cxx index bcb69e2094181..5d8ca8b7b5257 100644 --- a/Detectors/TRD/workflow/src/TRDDigitizerSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDDigitizerSpec.cxx @@ -19,7 +19,7 @@ #include "Steer/HitProcessingManager.h" // for DigitizationContext #include "TChain.h" #include <SimulationDataFormat/MCCompLabel.h> -#include <SimulationDataFormat/MCTruthContainer.h> +#include <SimulationDataFormat/ConstMCTruthContainer.h> #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" #include "TRDBase/Digit.h" // for the Digit type @@ -79,41 +79,61 @@ class TRDDPLDigitizerTask : public o2::base::BaseDPLDigitizer std::vector<o2::trd::Digit> digits; // digits which get filled o2::dataformats::MCTruthContainer<o2::trd::MCLabel> labels; // labels which get filled + o2::InteractionTimeRecord currentTime; // the current time + o2::InteractionTimeRecord triggerTime; // the time at which the TRD start reading out a signal + bool firstEvent = true; // Flag for the first event processed + TStopwatch timer; timer.Start(); - // loop over all composite collisions given from context // (aka loop over all the interaction records) for (int collID = 0; collID < irecords.size(); ++collID) { - mDigitizer.setEventTime(irecords[collID].getTimeNS()); + currentTime = irecords[collID]; + // Trigger logic implemented here + if (firstEvent) { + triggerTime = currentTime; + firstEvent = false; + } else { + double dT = currentTime.getTimeNS() - triggerTime.getTimeNS(); + if (dT < o2::trd::Digitizer::BUSY_TIME) { + // BUSY_TIME = READOUT_TIME + DEAD_TIME, if less than that, pile up the signals and update the last time + mDigitizer.pileup(); + } else { + // A new signal can be received, and the detector read it out: + // flush previous stored digits, labels and keep a trigger record + // then update the trigger time to the new one + mDigitizer.flush(digits, labels); + assert(digits.size() == labels.getIndexedSize()); + // Add trigger record, and send digits to the accumulator + triggers.emplace_back(triggerTime, digitsAccum.size(), digits.size()); + std::copy(digits.begin(), digits.end(), std::back_inserter(digitsAccum)); + if (mctruth) { + labelsAccum.mergeAtBack(labels); + } + triggerTime = currentTime; + digits.clear(); + labels.clear(); + } + } + + mDigitizer.setEventTime(triggerTime.getTimeNS()); // do we need this? // for each collision, loop over the constituents event and source IDs // (background signal merging is basically taking place here) for (auto& part : eventParts[collID]) { mDigitizer.setEventID(part.entryID); mDigitizer.setSrcID(part.sourceID); - - // get the hits for this event and this source + // get the hits for this event and this source and process them std::vector<o2::trd::HitType> hits; context->retrieveHits(mSimChains, "TRDHit", part.sourceID, part.entryID, &hits); - LOG(INFO) << "For collision " << collID << " eventID " << part.entryID << " found TRD " << hits.size() << " hits "; - mDigitizer.process(hits, digits, labels); - - // Add trigger record - triggers.emplace_back(irecords[collID], digitsAccum.size(), digits.size()); - - std::copy(digits.begin(), digits.end(), std::back_inserter(digitsAccum)); - if (mctruth) { - labelsAccum.mergeAtBack(labels); - } - digits.clear(); - labels.clear(); } } + // Force flush of the digits that remain in the digitizer cache mDigitizer.flush(digits, labels); - triggers.emplace_back(irecords[irecords.size() - 1], digitsAccum.size(), digits.size()); + assert(digits.size() == labels.getIndexedSize()); + triggers.emplace_back(triggerTime, digitsAccum.size(), digits.size()); std::copy(digits.begin(), digits.end(), std::back_inserter(digitsAccum)); if (mctruth) { labelsAccum.mergeAtBack(labels); @@ -125,7 +145,9 @@ class TRDDPLDigitizerTask : public o2::base::BaseDPLDigitizer pc.outputs().snapshot(Output{"TRD", "DIGITS", 0, Lifetime::Timeframe}, digitsAccum); if (mctruth) { LOG(INFO) << "TRD: Sending " << labelsAccum.getNElements() << " labels"; - pc.outputs().snapshot(Output{"TRD", "LABELS", 0, Lifetime::Timeframe}, labelsAccum); + // we are flattening the labels and write to managed shared memory container for further communication + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::trd::MCLabel>>(Output{"TRD", "LABELS", 0, Lifetime::Timeframe}); + labelsAccum.flatten_to(sharedlabels); } LOG(INFO) << "TRD: Sending ROMode= " << mROMode << " to GRPUpdater"; pc.outputs().snapshot(Output{"TRD", "ROMode", 0, Lifetime::Timeframe}, mROMode); @@ -140,8 +162,8 @@ class TRDDPLDigitizerTask : public o2::base::BaseDPLDigitizer Digitizer mDigitizer; std::vector<TChain*> mSimChains; // RS: at the moment using hardcoded flag for continuos readout - o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::CONTINUOUS; // readout mode -}; + o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::PRESENT; // readout mode +}; // namespace trd o2::framework::DataProcessorSpec getTRDDigitizerSpec(int channel, bool mctruth) { @@ -168,5 +190,5 @@ o2::framework::DataProcessorSpec getTRDDigitizerSpec(int channel, bool mctruth) Options{}}; } -} // end namespace trd +} // namespace trd } // end namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx new file mode 100644 index 0000000000000..6081c105da61b --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -0,0 +1,193 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TRDGlobalTrackingSpec.cxx + +#include "TRDWorkflow/TRDGlobalTrackingSpec.h" + +#include "TRDBase/Geometry.h" + +#include "DetectorsCommonDataFormats/NameConf.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/TriggerRecord.h" + +// GPU header +#include "GPUReconstruction.h" +#include "GPUChainTracking.h" +#include "GPUSettings.h" +#include "GPUDataTypes.h" +#include "GPUTRDDef.h" +#include "GPUTRDTrack.h" +#include "GPUTRDTrackletWord.h" +#include "GPUTRDInterfaces.h" +#include "GPUTRDGeometry.h" + +using namespace o2::framework; +using namespace o2::gpu; + +namespace o2 +{ +namespace trd +{ + +void TRDGlobalTracking::init(InitContext& ic) +{ + //-------- init geometry and field --------// + o2::base::GeometryManager::loadGeometry(); + o2::base::Propagator::initFieldFromGRP(o2::base::NameConf::getGRPFileName()); + auto geo = Geometry::instance(); + geo->createPadPlaneArray(); + geo->createClusterMatrixArray(); + mFlatGeo = std::make_unique<GeometryFlat>(*geo); + + //-------- init GPU reconstruction --------// + GPURecoStepConfiguration cfgRecoStep; + cfgRecoStep.steps = GPUDataTypes::RecoStep::NoRecoStep; + cfgRecoStep.inputs.clear(); + cfgRecoStep.outputs.clear(); + mRec = GPUReconstruction::CreateInstance("CPU", true); + mRec->SetSettings(o2::base::Propagator::Instance()->getNominalBz(), &cfgRecoStep); + + mChainTracking = mRec->AddChain<GPUChainTracking>(); + + mTracker = new GPUTRDTracker(); + mTracker->SetNCandidates(mRec->GetProcessingSettings().trdNCandidates); // must be set before initialization + mTracker->SetProcessPerTimeFrame(); + mTracker->SetNMaxCollisions(mRec->GetProcessingSettings().trdNMaxCollisions); + + mRec->RegisterGPUProcessor(mTracker, false); + mChainTracking->SetTRDGeometry(std::move(mFlatGeo)); + if (mRec->Init()) { + LOG(FATAL) << "GPUReconstruction could not be initialized"; + } + + mTracker->PrintSettings(); + + mTimer.Stop(); + mTimer.Reset(); +} + +void TRDGlobalTracking::run(ProcessingContext& pc) +{ + mTimer.Start(false); + const auto tracksITSTPC = pc.inputs().get<gsl::span<o2::dataformats::TrackTPCITS>>("tpcitstrack"); + const auto trackletsTRD = pc.inputs().get<gsl::span<o2::trd::Tracklet64>>("trdtracklets"); + const auto triggerRecords = pc.inputs().get<gsl::span<o2::trd::TriggerRecord>>("trdtriggerrec"); + + int nTracks = tracksITSTPC.size(); + int nCollisions = triggerRecords.size(); + int nTracklets = trackletsTRD.size(); + LOGF(INFO, "There are %i tracklets in total from %i trigger records", nTracklets, nCollisions); + + std::vector<float> trdTriggerTimes; + std::vector<int> trdTriggerIndices; + + for (int iEv = 0; iEv < nCollisions; ++iEv) { + const auto& trg = triggerRecords.at(iEv); + int nTrackletsCurrent = trg.getNumberOfObjects(); + int iFirstTracklet = trg.getFirstEntry(); + int64_t evTime = trg.getBCData().toLong() * o2::constants::lhc::LHCBunchSpacingNS; // event time in ns + trdTriggerTimes.push_back(evTime / 1000.); + trdTriggerIndices.push_back(iFirstTracklet); + LOGF(DEBUG, "Event %i: Occured at %li us after SOR, contains %i tracklets, index of first tracklet is %i", iEv, evTime / 1000, nTrackletsCurrent, iFirstTracklet); + } + + mTracker->Reset(); + + mChainTracking->mIOPtrs.nMergedTracks = nTracks; + mChainTracking->mIOPtrs.nTRDTracklets = nTracklets; + mChainTracking->AllocateIOMemory(); + mRec->PrepareEvent(); + mRec->SetupGPUProcessor(mTracker, true); + + LOG(DEBUG) << "Start loading input into TRD tracker"; + // load everything into the tracker + int nTracksLoaded = 0; + for (int iTrk = 0; iTrk < nTracks; ++iTrk) { + const auto& match = tracksITSTPC[iTrk]; + const auto& trk = match.getParamOut(); + GPUTRDTrack trkLoad; + trkLoad.setX(trk.getX()); + trkLoad.setAlpha(trk.getAlpha()); + for (int i = 0; i < 5; ++i) { + trkLoad.setParam(trk.getParam(i), i); + } + for (int i = 0; i < 15; ++i) { + trkLoad.setCov(trk.getCov()[i], i); + } + trkLoad.setTime(match.getTimeMUS().getTimeStamp()); + if (mTracker->LoadTrack(trkLoad)) { + continue; + } + ++nTracksLoaded; + LOGF(DEBUG, "Loaded track %i with time %f", nTracksLoaded, trkLoad.getTime()); + } + + for (int iTrklt = 0; iTrklt < nTracklets; ++iTrklt) { + auto trklt = trackletsTRD[iTrklt]; + unsigned int trkltWord = 0; // DUMMY + GPUTRDTrackletWord trkltLoad; + trkltLoad.SetId(iTrklt); + trkltLoad.SetHCId(trklt.getHCID()); + trkltLoad.SetTrackletWord(trkltWord); + if (mTracker->LoadTracklet(trkltLoad) > 0) { + LOG(WARNING) << "Could not load tracklet " << iTrklt; + } + } + mTracker->SetTriggerRecordTimes(&(trdTriggerTimes[0])); + mTracker->SetTriggerRecordIndices(&(trdTriggerIndices[0])); + mTracker->SetNCollisions(nCollisions); + //mTracker->DumpTracks(); + mTracker->DoTracking(mChainTracking); + //mTracker->DumpTracks(); + + std::vector<GPUTRDTrack> tracksOut(mTracker->NTracks()); + std::copy(mTracker->Tracks(), mTracker->Tracks() + mTracker->NTracks(), tracksOut.begin()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "MATCHTRD", 0, Lifetime::Timeframe}, tracksOut); + + mTimer.Stop(); +} + +void TRDGlobalTracking::endOfStream(EndOfStreamContext& ec) +{ + LOGF(INFO, "TRD global tracking total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC) +{ + std::vector<InputSpec> inputs; + std::vector<OutputSpec> outputs; + inputs.emplace_back("tpcitstrack", "GLO", "TPCITS", 0, Lifetime::Timeframe); + inputs.emplace_back("trdtracklets", o2::header::gDataOriginTRD, "TRACKLETS", 0, Lifetime::Timeframe); + inputs.emplace_back("trdtriggerrec", o2::header::gDataOriginTRD, "TRKTRGRD", 0, Lifetime::Timeframe); + + if (useMC) { + LOG(FATAL) << "MC usage must be disabled for this workflow, since it is not yet implemented"; + //inputs.emplace_back("itstracklabel", "GLO", "TPCITS_ITSMC", 0, Lifetime::Timeframe); + //inputs.emplace_back("tpctracklabel", "GLO", "TPCITS_TPCMC", 0, Lifetime::Timeframe); + } + + outputs.emplace_back(o2::header::gDataOriginTRD, "MATCHTRD", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "trd-globaltracking", + inputs, + outputs, + AlgorithmSpec{adaptFromTask<TRDGlobalTracking>(useMC)}, + Options{}}; +} + +} // namespace trd +} // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDTrackWriterSpec.cxx b/Detectors/TRD/workflow/src/TRDTrackWriterSpec.cxx new file mode 100644 index 0000000000000..f42af52207f57 --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDTrackWriterSpec.cxx @@ -0,0 +1,62 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TRDTrackWriterSpec.cxx + +#include <vector> +#include "GPUO2Interface.h" +#include "GPUTRDDef.h" +#include "GPUTRDTrack.h" + +#include "DPLUtils/MakeRootTreeWriterSpec.h" + +using namespace o2::framework; +using namespace o2::gpu; + +namespace o2 +{ +namespace trd +{ +template <typename T> +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition<T>; + +DataProcessorSpec getTRDTrackWriterSpec(bool useMC) +{ + // TODO: not clear if the writer is supposed to write MC labels at some point + // this is just a dummy definition for the template branch definition below + // define the correct type and the input specs + using LabelsType = std::vector<int>; + // force, this will disable the branch for now, can be adjusted in the future + useMC = false; + + // A spectator to store the size of the data array for the logger below + auto tracksSize = std::make_shared<int>(); + auto tracksLogger = [tracksSize](std::vector<GPUTRDTrack> const& tracks) { + *tracksSize = tracks.size(); + }; + + return MakeRootTreeWriterSpec("trd-track-writer", + "trdtracks.root", + "tracksTRD", + BranchDefinition<std::vector<GPUTRDTrack>>{InputSpec{"tracks", o2::header::gDataOriginTRD, "MATCHTRD", 0}, + "tracks", + "tracks-branch-name", + 1, + tracksLogger}, + // NOTE: this branch template is to show how the conditional MC labels can + // be defined, the '0' disables the branch for the moment + BranchDefinition<LabelsType>{InputSpec{"matchtpclabels", "GLO", "SOME_LABELS", 0}, + "labels", + (useMC ? 1 : 0), // one branch if mc labels enabled + "labels-branch-name"})(); +} + +} // namespace trd +} // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDTrackingWorkflow.cxx b/Detectors/TRD/workflow/src/TRDTrackingWorkflow.cxx new file mode 100644 index 0000000000000..4b60307680434 --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDTrackingWorkflow.cxx @@ -0,0 +1,44 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TRDTrackingWorkflow.cxx + +#include <vector> + +#include "Framework/WorkflowSpec.h" +#include "GlobalTrackingWorkflow/TrackTPCITSReaderSpec.h" +#include "TRDWorkflow/TRDTrackletReaderSpec.h" +#include "TRDWorkflow/TRDGlobalTrackingSpec.h" +#include "TRDWorkflow/TRDTrackWriterSpec.h" + +namespace o2 +{ +namespace trd +{ + +framework::WorkflowSpec getTRDTrackingWorkflow(bool disableRootInp, bool disableRootOut) +{ + framework::WorkflowSpec specs; + bool useMC = false; + if (!disableRootInp) { + specs.emplace_back(o2::globaltracking::getTrackTPCITSReaderSpec(useMC)); + specs.emplace_back(o2::trd::getTRDTrackletReaderSpec(useMC)); + } + + specs.emplace_back(o2::trd::getTRDGlobalTrackingSpec(useMC)); + + if (!disableRootOut) { + specs.emplace_back(o2::trd::getTRDTrackWriterSpec(useMC)); + } + return specs; +} + +} // namespace trd +} // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDTrackletReaderSpec.cxx b/Detectors/TRD/workflow/src/TRDTrackletReaderSpec.cxx new file mode 100644 index 0000000000000..2f29405c61564 --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDTrackletReaderSpec.cxx @@ -0,0 +1,91 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TRDTrackletReaderSpec.cxx + +#include "TRDWorkflow/TRDTrackletReaderSpec.h" + +#include "Headers/DataHeader.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace trd +{ + +void TRDTrackletReader::init(InitContext& ic) +{ + // get the option from the init context + LOG(INFO) << "Init TRD tracklet reader!"; + mInFileName = ic.options().get<std::string>("trd-tracklet-infile"); + mInTreeName = ic.options().get<std::string>("treename"); + connectTree(mInFileName); +} + +void TRDTrackletReader::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("Tracklet", &mTrackletsPtr); + mTree->SetBranchAddress("TrackTrg", &mTriggerRecordsPtr); + if (mUseMC) { + LOG(FATAL) << "MC information not yet included for TRD tracklets"; + } + LOG(INFO) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +void TRDTrackletReader::run(ProcessingContext& pc) +{ + auto currEntry = mTree->GetReadEntry() + 1; + assert(currEntry < mTree->GetEntries()); // this should not happen + mTree->GetEntry(currEntry); + LOG(INFO) << "Pushing " << mTriggerRecords.size() << " TRD trigger records at entry " << currEntry; + LOG(INFO) << "Pushing " << mTracklets.size() << " TRD tracklets for these trigger records"; + + pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "TRACKLETS", 0, Lifetime::Timeframe}, mTracklets); + pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "TRKTRGRD", 0, Lifetime::Timeframe}, mTriggerRecords); + if (mUseMC) { + LOG(FATAL) << "MC information not yet included for TRD tracklets"; + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get<ControlService>().endOfStream(); + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + } +} + +DataProcessorSpec getTRDTrackletReaderSpec(bool useMC) +{ + std::vector<OutputSpec> outputs; + outputs.emplace_back(o2::header::gDataOriginTRD, "TRACKLETS", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTRD, "TRKTRGRD", 0, Lifetime::Timeframe); + if (useMC) { + LOG(FATAL) << "MC information not yet included for TRD tracklets"; + } + + return DataProcessorSpec{ + "TRDTrackletReader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<TRDTrackletReader>(useMC)}, + Options{ + {"trd-tracklet-infile", VariantType::String, "trdtracklets.root", {"Name of the input file"}}, + {"treename", VariantType::String, "o2sim", {"Name of top-level TTree"}}, + }}; +} + +} // namespace trd +} // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDTrackletTransformerSpec.cxx b/Detectors/TRD/workflow/src/TRDTrackletTransformerSpec.cxx new file mode 100644 index 0000000000000..acb441fcd54bf --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDTrackletTransformerSpec.cxx @@ -0,0 +1,73 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <gsl/span> + +#include "TRDWorkflow/TRDTrackletTransformerSpec.h" + +#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/CalibratedTracklet.h" + +using namespace o2::framework; +using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; + +namespace o2 +{ +namespace trd +{ + +void TRDTrackletTransformerSpec::init(o2::framework::InitContext& ic) +{ + LOG(info) << "initializing tracklet transformer"; +} + +void TRDTrackletTransformerSpec::run(o2::framework::ProcessingContext& pc) +{ + LOG(info) << "running tracklet transformer"; + + auto tracklets = pc.inputs().get<gsl::span<Tracklet64>>("inTracklets"); + // std::vector<TriggerRecord> triggerRec = pc.inputs().get<std::vector<TriggerRecord>>("triggerRecord"); + + std::vector<CalibratedTracklet> calibratedTracklets; + calibratedTracklets.reserve(tracklets.size()); + + // temporary. For testing + // for (int reci=0; reci < triggerRec.size(); reci++) + // { + // LOG(info) << triggerRec[reci].getFirstEntry() << " | " << triggerRec[reci].getNumberOfObjects(); + // } + + LOG(info) << tracklets.size() << " tracklets found!"; + + for (const auto& tracklet : tracklets) { + CalibratedTracklet calibratedTracklet = mTransformer.transformTracklet(tracklet); + calibratedTracklets.push_back(calibratedTracklet); + } + + pc.outputs().snapshot(Output{"TRD", "CTRACKLETS", 0, Lifetime::Timeframe}, calibratedTracklets); +} + +o2::framework::DataProcessorSpec getTRDTrackletTransformerSpec() +{ + LOG(info) << "getting TRDTrackletTransformerSpec"; + return DataProcessorSpec{ + "TRDTRACKLETTRANSFORMER", + Inputs{ + InputSpec{"inTracklets", "TRD", "TRACKLETS", 0}, + // InputSpec{"triggerRecord", "TRD", "TRKTRGRD", 0} + }, + Outputs{OutputSpec{"TRD", "CTRACKLETS", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<TRDTrackletTransformerSpec>()}, + Options{}}; +} + +} //end namespace trd +} //end namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDTrackletTransformerWorkflow.cxx b/Detectors/TRD/workflow/src/TRDTrackletTransformerWorkflow.cxx new file mode 100644 index 0000000000000..fca31bcd222ee --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDTrackletTransformerWorkflow.cxx @@ -0,0 +1,48 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDWorkflow/TRDTrackletTransformerSpec.h" +#include "TRDWorkflow/TRDCalibratedTrackletWriterSpec.h" +#include "TRDWorkflow/TRDTrackletReaderSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" + +using namespace o2::framework; + +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{ + "root-in", VariantType::Int, 1, {"enable (1) or disable (0) input from ROOT file"}}); + workflowOptions.push_back(ConfigParamSpec{ + "root-out", VariantType::Int, 1, {"enable (1) or disable (0) output to ROOT file"}}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + int rootIn = configcontext.options().get<int>("root-in"); + int rootOut = configcontext.options().get<int>("root-out"); + + WorkflowSpec spec; + + if (rootIn) { + spec.emplace_back(o2::trd::getTRDTrackletReaderSpec(0)); + } + + spec.emplace_back(o2::trd::getTRDTrackletTransformerSpec()); + + if (rootOut) { + spec.emplace_back(o2::trd::getTRDCalibratedTrackletWriterSpec()); + } + + return spec; +} diff --git a/Detectors/TRD/workflow/src/TRDTrackletWriterSpec.cxx b/Detectors/TRD/workflow/src/TRDTrackletWriterSpec.cxx index 709402e5c463a..ee8d4ff93ef7b 100644 --- a/Detectors/TRD/workflow/src/TRDTrackletWriterSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDTrackletWriterSpec.cxx @@ -18,8 +18,8 @@ #include "TRDBase/Digit.h" #include <SimulationDataFormat/MCTruthContainer.h> #include "TRDBase/MCLabel.h" -#include "TRDBase/Tracklet.h" #include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/Tracklet64.h" #include <fstream> #include <iostream> @@ -52,7 +52,7 @@ o2::framework::DataProcessorSpec getTRDTrackletWriterSpec() "trdtracklets.root", "o2sim", 1, - BranchDefinition<std::vector<o2::trd::Tracklet>>{InputSpec{"tracklets", "TRD", "TRACKLETS"}, "Tracklet"}, + BranchDefinition<std::vector<o2::trd::Tracklet64>>{InputSpec{"tracklets", "TRD", "TRACKLETS"}, "Tracklet"}, //BranchDefinition<o2::dataformats::MCTruthContainer<o2::trd::MCLabel>>{InputSpec{"trklabels", "TRD", "TRKLABELS"}, "TRKLabels"}, BranchDefinition<std::vector<o2::trd::TriggerRecord>>{InputSpec{"tracklettrigs", "TRD", "TRKTRGRD"}, "TrackTrg"})(); }; diff --git a/Detectors/TRD/workflow/src/TRDTrapRawWriterSpec.cxx b/Detectors/TRD/workflow/src/TRDTrapRawWriterSpec.cxx new file mode 100644 index 0000000000000..e05161a9bcec9 --- /dev/null +++ b/Detectors/TRD/workflow/src/TRDTrapRawWriterSpec.cxx @@ -0,0 +1,51 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRDTRAPSIMULATORTRAPRAWWRITER_H +#define O2_TRDTRAPSIMULATORTRAPRAWWRITER_H + +#include "Framework/DataProcessorSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "Framework/InputSpec.h" +#include "TRDWorkflow/TRDTrapRawWriterSpec.h" +#include "DataFormatsTRD/RawData.h" +#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/LinkRecord.h" + +#include <fstream> +#include <iostream> + +using namespace o2::framework; + +namespace o2 +{ +namespace trd +{ + +template <typename T> +using BranchDefinition = framework::MakeRootTreeWriterSpec::BranchDefinition<T>; + +o2::framework::DataProcessorSpec getTRDTrapRawWriterSpec() +{ + // using InputSpec = framework::InputSpec; + using MakeRootTreeWriterSpec = framework::MakeRootTreeWriterSpec; + return MakeRootTreeWriterSpec("TRDTrkltRawWrt", + "trdtrapraw.root", + "o2sim", + 1, + BranchDefinition<std::vector<uint32_t>>{InputSpec{"trapraw", "TRD", "RAWDATA"}, "TrapRaw"}, + BranchDefinition<std::vector<o2::trd::LinkRecord>>{InputSpec{"traplinks", "TRD", "RAWLNKRD"}, "TrapLinkRecord"}, + BranchDefinition<std::vector<o2::trd::TriggerRecord>>{InputSpec{"traprawtrigrec", "TRD", "RAWTRGRD"}, "RawTriggerRecord"})(); +}; + +} // end namespace trd +} // end namespace o2 + +#endif // O2_TRDTRAPSIMULATORTRACKLETWRITER_H diff --git a/Detectors/TRD/workflow/src/TRDTrapSimulatorSpec.cxx b/Detectors/TRD/workflow/src/TRDTrapSimulatorSpec.cxx index 1e4876f90f600..beee2b7c22c33 100644 --- a/Detectors/TRD/workflow/src/TRDTrapSimulatorSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDTrapSimulatorSpec.cxx @@ -20,7 +20,12 @@ #include <cmath> #include <unistd.h> // for getppid #include <chrono> +#include <gsl/span> +#include <iostream> +#include <fstream> + #include "TChain.h" +#include "TFile.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" @@ -33,7 +38,7 @@ #include "Steer/HitProcessingManager.h" // for DigitizationContext #include "TChain.h" #include <SimulationDataFormat/MCCompLabel.h> -#include <SimulationDataFormat/MCTruthContainer.h> +#include <SimulationDataFormat/ConstMCTruthContainer.h> #include "fairlogger/Logger.h" #include "CCDB/BasicCCDBManager.h" @@ -46,61 +51,22 @@ #include "TRDBase/Calibrations.h" #include "TRDSimulation/TrapSimulator.h" #include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/LinkRecord.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/RawData.h" //#ifdef WITH_OPENMP //#include <omp.h> //#endif ci is failing on this, sort out another time. using namespace o2::framework; +using namespace std::placeholders; // this is for std::bind to build the comparator for the indexed sort of digits. namespace o2 { namespace trd { -bool msgDigitSortComparator(o2::trd::Digit const& a, o2::trd::Digit const& b) -{ - FeeParam* fee = FeeParam::instance(); - int rowa = a.getRow(); - int rowb = b.getRow(); - int pada = a.getPad(); - int padb = b.getPad(); - double timea = a.getTimeStamp(); - double timeb = b.getTimeStamp(); - int roba = fee->getROBfromPad(rowa, pada); - int robb = fee->getROBfromPad(rowb, padb); - int mcma = fee->getMCMfromPad(rowa, pada); - int mcmb = fee->getMCMfromPad(rowb, padb); - //LOG(info) << "comparing " << rowa << ":" << pada <<":" << roba <<" "<< mcma << " with " << rowb << ":" << padb <<":" << robb <<" "<< mcmb; - if (timea < timeb) { - // LOG(info) << "yip timea < timeb " << timea <<"<" << timeb; - return 1; - } else if (timea == timeb) { - - if (a.getDetector() < b.getDetector()) - return 1; - else { - if (a.getDetector() == b.getDetector()) { - if (roba < robb) - return 1; - else { - if (roba == robb) { - if (mcma < mcmb) - return 1; - else - return 0; - } else - return 0; - } - return 0; - } - return 0; - } - return 0; - } - return 0; -} - TrapConfig* TRDDPLTrapSimulatorTask::getTrapConfig() { // return an existing TRAPconfig or load it from the CCDB @@ -116,82 +82,58 @@ TrapConfig* TRDDPLTrapSimulatorTask::getTrapConfig() // try to load the requested configuration loadTrapConfig(); //calib. - LOG(debug) << "using TRAPconfig :" << mTrapConfig->getConfigName().c_str() << "." << mTrapConfig->getConfigVersion().c_str(); - + if (mTrapConfig->getConfigName() == "" && mTrapConfig->getConfigVersion() == "") { + //some trap configs dont have config name and version set, in those cases, just show the file name used. + LOG(info) << "using TRAPconfig :\"" << mTrapConfigName; + } else { + LOG(info) << "using TRAPconfig :\"" << mTrapConfig->getConfigName().c_str() << "\".\"" << mTrapConfig->getConfigVersion().c_str() << "\""; + } // we still have to load the gain tables // if the gain filter is active return mTrapConfig; } // end of else from if mTrapConfig } -void PrintDmemValue3(TrapConfig::TrapDmemWord* trapval, std::ofstream& output) -{ - output << "\t AllocationMode : " << trapval->getAllocMode() << std::endl; - output << "\t Array size : " << trapval->getDataSize() << std::endl; - for (int dataarray = 0; dataarray < trapval->getDataSize(); dataarray++) { - output << "\t " << trapval->getDataRaw(dataarray) << " : valid : " << trapval->getValidRaw(dataarray) << std::endl; - } -} -void PrintRegisterValue3(TrapConfig::TrapRegister* trapval, std::ofstream& output) -{ - output << "\t AllocationMode : " << trapval->getAllocMode() << std::endl; - output << "\t Array size : " << trapval->getDataSize() << std::endl; - for (int dataarray = 0; dataarray < trapval->getDataSize(); dataarray++) { - output << "\t " << trapval->getDataRaw(dataarray) << " : valid : " << trapval->getValidRaw(dataarray) << std::endl; - } -} -void PrintTrapConfigAsStored3(TrapConfig* trapconfig) +void TRDDPLTrapSimulatorTask::loadDefaultTrapConfig() { - std::ofstream run3config("run3trapconfig-AsStored-insidedpl.txt"); - run3config << "Trap Registers : " << std::endl; - for (int regvalue = 0; regvalue < TrapConfig::kLastReg; regvalue++) { - run3config << " Trap : " << trapconfig->mRegisterValue[regvalue].getName() - << " at : 0x " << std::hex << trapconfig->mRegisterValue[regvalue].getAddr() << std::dec - << " with nbits : " << trapconfig->mRegisterValue[regvalue].getNbits() - << " and reset value of : " << trapconfig->mRegisterValue[regvalue].getResetValue() << std::endl; - // now for the inherited AliTRDtrapValue members; - PrintRegisterValue3(&trapconfig->mRegisterValue[regvalue], run3config); - } - - // run3config << "done with regiser values now for dmemwords" << std::endl; - run3config << "DMEM Words : " << std::endl; - for (int dmemwords = 0; dmemwords < TrapConfig::mgkDmemWords; dmemwords++) { - // copy fName, fAddr - // inherited from trapvalue : fAllocMode, fSize fData and fValid - // trapconfig->mDmem[dmemwords].mName= run2config->fDmem[dmemwords].fName; // this gets set on setting the address - run3config << "Name : " << trapconfig->mDmem[dmemwords].getName() << " :address : " << trapconfig->mDmem[dmemwords].getAddress() << std::endl; - PrintDmemValue3(&trapconfig->mDmem[dmemwords], run3config); + //this loads a trap config from a root file for those times when the ccdb is not around and you want to keep working. + TFile* f; + f = new TFile("DefaultTrapConfig.root"); + mTrapConfig = (o2::trd::TrapConfig*)f->Get("ccdb_object"); + if (mTrapConfig == nullptr) { + LOG(fatal) << "failed to load from ccdb, and attempted to load from disk, you seem to be really out of luck."; } + // else we have loaded the trap config successfully. } - void TRDDPLTrapSimulatorTask::loadTrapConfig() { // try to load the specified configuration from the CCDB - LOG(debug) << "looking for TRAPconfig " << mTrapConfigName; + LOG(info) << "looking for TRAPconfig " << mTrapConfigName; auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); - ccdbmgr.setTimestamp(297595); + ccdbmgr.setTimestamp(mRunNumber); //default is : mTrapConfigName="cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5549"; - mTrapConfigName = "c"; - mTrapConfig = ccdbmgr.get<o2::trd::TrapConfig>("TRD_test/TrapConfig2020/c"); + mTrapConfigName = "c"; //cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5549"; + mTrapConfig = ccdbmgr.get<o2::trd::TrapConfig>("TRD_test/TrapConfig2020/" + mTrapConfigName); if (mTrapConfig == nullptr) { //failed to find or open or connect or something to get the trapconfig from the ccdb. //first check the directory listing. LOG(warn) << " failed to get trapconfig from ccdb with name : " << mTrapConfigName; - LOG(info) << "Retrieving trapconfig failed"; + loadDefaultTrapConfig(); } else { //TODO figure out how to get the debug level from logger and only do this for debug option to --severity debug (or what ever the command actualy is) - if (mEnableTrapConfigDump) - PrintTrapConfigAsStored3(mTrapConfig); + if (mEnableTrapConfigDump) { + mTrapConfig->DumpTrapConfig2File("run3trapconfig_dump"); + } } } void TRDDPLTrapSimulatorTask::setOnlineGainTables() { - const int nDets = 540; //TRDGeometry::Ndet(); - const int nMcms = TRDGeometry::MCMmax(); - const int nChs = TRDGeometry::ADCmax(); + const int nDets = 540; //Geometry::Ndet(); + const int nMcms = Geometry::MCMmax(); + const int nChs = Geometry::ADCmax(); //check FGBY from trapconfig. //check the input parameter of trd-onlinegaincorrection. //warn if you have chosen a trapconfig with gaincorrections but chosen not to use them. @@ -214,7 +156,7 @@ void TRDDPLTrapSimulatorTask::setOnlineGainTables() } for (int iDet = 0; iDet < nDets; ++iDet) { - const int nRobs = TRDGeometry::getStack(iDet) == 2 ? TRDGeometry::ROBmaxC0() : TRDGeometry::ROBmaxC1(); + const int nRobs = Geometry::getStack(iDet) == 2 ? Geometry::ROBmaxC0() : Geometry::ROBmaxC1(); for (int rob = 0; rob < nRobs; ++rob) { for (int mcm = 0; mcm < nMcms; ++mcm) { // set ADC reference voltage @@ -236,19 +178,18 @@ void TRDDPLTrapSimulatorTask::setOnlineGainTables() void TRDDPLTrapSimulatorTask::init(o2::framework::InitContext& ic) { + LOG(debug) << "entering init"; mFeeParam = FeeParam::instance(); mPrintTrackletOptions = ic.options().get<int>("trd-printtracklets"); mDrawTrackletOptions = ic.options().get<int>("trd-drawtracklets"); mShowTrackletStats = ic.options().get<int>("show-trd-trackletstats"); mTrapConfigName = ic.options().get<std::string>("trd-trapconfig"); - mPrintOutTrapConfig = ic.options().get<bool>("trd-printtrapconfig"); mDebugRejectedTracklets = ic.options().get<bool>("trd-debugrejectedtracklets"); mEnableOnlineGainCorrection = ic.options().get<bool>("trd-onlinegaincorrection"); mOnlineGainTableName = ic.options().get<std::string>("trd-onlinegaintable"); mRunNumber = ic.options().get<int>("trd-runnum"); mEnableTrapConfigDump = ic.options().get<bool>("trd-dumptrapconfig"); - mFixTriggerRecords = ic.options().get<bool>("trd-fixtriggerrecord"); - //Connect to CCDB for all things needing access to ccdb. + //Connect to CCDB for all things needing access to ccdb, trapconfig and online gains auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); mCalib = std::make_unique<Calibrations>(); mCalib->setCCDBForSimulation(mRunNumber); @@ -266,40 +207,43 @@ std::string printDigit(o2::trd::Digit& a) bool digitindexcompare(unsigned int A, unsigned int B, const std::vector<o2::trd::Digit>& originalDigits) { - + // sort into ROC:padrow:padcolum const o2::trd::Digit *a, *b; a = &originalDigits[A]; b = &originalDigits[B]; - FeeParam* fee = FeeParam::instance(); - int rowa = a->getRow(); - int rowb = b->getRow(); - int pada = a->getPad(); - int padb = b->getPad(); - double timea = a->getTimeStamp(); - double timeb = b->getTimeStamp(); - int roba = fee->getROBfromPad(rowa, pada); - int robb = fee->getROBfromPad(rowb, padb); - int mcma = fee->getMCMfromPad(rowa, pada); - int mcmb = fee->getMCMfromPad(rowb, padb); - if (a->getTimeStamp() < b->getTimeStamp()) - return 1; - if (a->getTimeStamp() > b->getTimeStamp()) - return 0; // timestamps are equal - if (a->getDetector() < b->getDetector()) + if (a->getDetector() < b->getDetector()) { return 1; - if (a->getDetector() > b->getDetector()) + } + if (a->getDetector() > b->getDetector()) { return 0; - //detectors are equal - if (roba < robb) + } + if (a->getRow() < b->getRow()) { return 1; - if (roba > robb) + } + if (a->getRow() > b->getRow()) { return 0; - //rob is equal - if (mcma < mcmb) - return 1; - else + } + if (a->getPad() < b->getPad()) { return 0; + } + if (a->getPad() > b->getPad()) { + return 1; + } + return 0; +} + +void TRDDPLTrapSimulatorTask::setTriggerRecord(std::vector<o2::trd::TriggerRecord>& triggerrecord, uint32_t currentrecord, uint64_t recordsize) +{ + // so increment the tracklet trigger records and fill accordingly for the now completed prior triggerrecord. + uint64_t triggerrecordstart = 0; + if (currentrecord == 0) { // for not the first one we can simply look back to the previous one to get the start. + triggerrecordstart = 0; + triggerrecord[currentrecord].setDataRange(triggerrecordstart, recordsize); + } else { + triggerrecordstart = triggerrecord[currentrecord - 1].getFirstEntry() + triggerrecord[currentrecord - 1].getNumberOfObjects(); + triggerrecord[currentrecord].setDataRange(triggerrecordstart, recordsize - triggerrecordstart); + } } void TRDDPLTrapSimulatorTask::fixTriggerRecords(std::vector<o2::trd::TriggerRecord>& trigRecord) @@ -332,75 +276,108 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) // mNumThreads = std::min(maxthreads, 8); // LOG(INFO) << "TRD: Trapping with " << mNumThreads << " threads "; //#endif - //TODO need to change to gsl::span. // get inputs for the TrapSimulator // the digits are going to be sorted, we therefore need a copy of the vector rather than an object created // directly on the input data, the output vector however is created directly inside the message // memory thus avoiding copy by snapshot - // TODO: rather than sorting the digits, an index can be sorted and the read-only input data accessed - // via the sorted index + + /********* + * iNPUTS + ********/ + auto inputDigits = pc.inputs().get<gsl::span<o2::trd::Digit>>("digitinput"); std::vector<o2::trd::Digit> msgDigits(inputDigits.begin(), inputDigits.end()); // auto digits pc.outputs().make<std::vector<o2::trd::Digit>>(Output{"TRD", "TRKDIGITS", 0, Lifetime::Timeframe}, msgDigits.begin(), msgDigits.end()); - auto digitMCLabels = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("labelinput"); - - // auto rawDataOut = pc.outputs().make<char>(Output{"TRD", "RAWDATA", 0, Lifetime::Timeframe}, 1000); //TODO number is just a place holder until we start using it. - o2::dataformats::MCTruthContainer<o2::MCCompLabel> trackletMCLabels; - + auto digitMCLabels = pc.inputs().get<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>("labelinput"); // the returned object is read-only as it refers directly to the underlying raw input data // need to make a copy because the object might be changed in fixTriggerRecords auto inputTriggerRecords = pc.inputs().get<gsl::span<o2::trd::TriggerRecord>>("triggerrecords"); - std::vector<o2::trd::TriggerRecord> triggerRecords(inputTriggerRecords.begin(), inputTriggerRecords.end()); - uint64_t currentTriggerRecord = 0; - for (auto& trig : triggerRecords) { - LOG(debug) << "Trigger Record ; " << trig.getFirstEntry() << " --> " << trig.getNumberOfObjects(); - } - // fix incoming trigger records if requested. - if (mFixTriggerRecords) - fixTriggerRecords(triggerRecords); + /* ***** + * setup data objects + * *****/ + // trigger records to index the 64bit tracklets.yy + std::vector<o2::trd::TriggerRecord> triggerRecords(inputTriggerRecords.begin(), inputTriggerRecords.end()); std::vector<o2::trd::TriggerRecord> trackletTriggerRecords = triggerRecords; // copy over the whole thing but we only really want the bunch crossing info. + std::vector<o2::trd::TriggerRecord> rawTriggerRecords = triggerRecords; // as we have the option of having tracklets and/or raw data, we need both triggerrecords. + // of course we dont *actually* need it we could simply walk through all the raw data header to header. + // auto rawDataOut = pc.outputs().make<char>(Output{"TRD", "RAWDATA", 0, Lifetime::Timeframe}, 1000); //TODO number is just a place holder until we start using it. + o2::dataformats::MCTruthContainer<o2::MCCompLabel> trackletMCLabels; + //index of digits, TODO refactor to a digitindex class. + std::vector<unsigned int> msgDigitsIndex(msgDigits.size()); + //set up structures to hold the returning tracklets. + std::vector<Tracklet64> trapTracklets; //vector to store the retrieved tracklets from an trapsim object + std::vector<Tracklet64> trapTrackletsAccum; + std::vector<uint32_t> rawdata; + // trigger records to index the "raw" data + uint64_t currentTriggerRecord = 0; + /* ******* + * reserve sizes + * *******/ + mLinkRecords.reserve(1080 * triggerRecords.size()); // worse case scenario is all links for all events. TODO get 1080 from somewhere. //TODO these must be created directly in the output as done at the top of this run method - std::vector<unsigned int> msgDigitsIndex; - msgDigitsIndex.reserve(msgDigits.size()); - - LOG(debug) << "Read in msgDigits with size of : " << msgDigits.size() << " labels contain : " << digitMCLabels->getNElements() << " with and index size of : " << digitMCLabels->getIndexedSize(); - - if (digitMCLabels->getIndexedSize() != msgDigits.size()) { - LOG(debug) << "Read in msgDigits with size of : " << msgDigits.size() << " labels contain : " << digitMCLabels->getNElements() << " with and index size of : " << digitMCLabels->getIndexedSize(); - LOG(warn) << "Digits and Labels coming into TrapSimulator are of differing sizes, labels will be jibberish. "; + //msgDigitsIndex.reserve(msgDigits.size()); + LOG(debug) << "Read in msgDigits with size of : " << msgDigits.size() << " labels contain : " << digitMCLabels.getNElements() << " with and index size of : " << digitMCLabels.getIndexedSize() << " and triggerrecord count of :" << triggerRecords.size(); + if (digitMCLabels.getIndexedSize() != msgDigits.size()) { + LOG(warn) << "Digits and Labels coming into TrapSimulator are of differing sizes, labels will be jibberish. " << digitMCLabels.getIndexedSize() << "!=" << msgDigits.size(); } - //set up structures to hold the returning tracklets. - std::vector<Tracklet> trapTracklets; //vector to store the retrieved tracklets from an trapsim object - std::vector<Tracklet> trapTrackletsAccum; trapTracklets.reserve(30); trapTrackletsAccum.reserve(msgDigits.size() / 3); - msgDigitsIndex.reserve(msgDigits.size()); - - int count = 0; - //make msgDigitsIndex a simple vector of ascending numbers mapping trivially into the msgDigits vector. - for (int i = 0; i < msgDigits.size(); i++) { - msgDigitsIndex.push_back(i); + //msgDigitsIndex.reserve(msgDigits.size()); + // worse case scenario is header and single tracklet word, hence 2, for higher tracklet count the factors reduces relative to tracklet count. Remember 3 digits per tracklet. + rawdata.reserve(msgDigits.size() * 2); + + //Build the digits index. + // std::iota(msgDigitsIndex.begin(), msgDigitsIndex.end(), static_cast<unsigned int>(0)); + std::generate(msgDigitsIndex.begin(), msgDigitsIndex.end(), [n = 0]() mutable { return n++; }); + int indexcount = 0; + for (auto index : msgDigitsIndex) { + LOG(debug) << indexcount << ":" << index; } - LOG(debug) << "msgdigitsindex is " << msgDigitsIndex.size(); - + if (msgDigitsIndex.size() != msgDigits.size()) { + //error condition for sort. + LOG(fatal) << "Cant index digits as index and digits differ in size, this is not permitted. Digits size=" << msgDigits.size() << " and index size=" << msgDigitsIndex.size(); + } + //sort the digits array TODO refactor this intoa vector index sort and possibly generalise past merely digits. auto sortstart = std::chrono::high_resolution_clock::now(); - //sort the digits array - std::stable_sort(msgDigits.begin(), msgDigits.end(), msgDigitSortComparator); + for (auto& trig : triggerRecords) { + std::stable_sort(std::begin(msgDigitsIndex) + trig.getFirstEntry(), std::begin(msgDigitsIndex) + trig.getNumberOfObjects() + trig.getFirstEntry(), + [&msgDigits](auto&& PH1, auto&& PH2) { return digitindexcompare(PH1, PH2, msgDigits); }); + } + mSortingTime = std::chrono::high_resolution_clock::now() - sortstart; - LOG(info) << "TRD Digit Sorting took " << mSortingTime.count(); + LOG(debug) << "TRD Digit Sorting took " << mSortingTime.count(); // sort from triggerRecords.getFirstEntry() to triggerRecords.getFirstEntry()+triggerRecords.getNumberOfObjects(); //check the incoming triggerrecords: + int triggerrecordcount = 0; for (auto& trig : triggerRecords) { - LOG(debug) << "Trigger Record ; " << trig.getFirstEntry() << " --> " << trig.getNumberOfObjects(); + LOG(debug) << "Trigger Record ; " << triggerrecordcount << " = " << trig.getFirstEntry() << " --> " << trig.getNumberOfObjects(); + triggerrecordcount++; } for (auto& trig : trackletTriggerRecords) { LOG(debug) << "Trigger Tracklet Record ; " << trig.getFirstEntry() << " --> " << trig.getNumberOfObjects(); } + //print digits to check the sorting. + LOG(debug) << " Digits : "; + //for (auto& digit : msgDigits) { + for (auto& digitindex : msgDigitsIndex) { + Digit digit = msgDigits[digitindex]; + LOG(debug) << "sorted digit time:" << digit.getTimeStamp() << " detector:row:pad:rob:mcm ::" + << digit.getDetector() << ":" << digit.getRow() << ":" << digit.getPad() << ":" + << mFeeParam->getROBfromPad(digit.getRow(), digit.getPad()) << ":" + << mFeeParam->getMCMfromPad(digit.getRow(), digit.getPad()) + << " LinkId:" << LinkRecord::getHalfChamberLinkId(digit.getDetector(), mFeeParam->getROBfromPad(digit.getRow(), digit.getPad())) << "\t\t SM:stack:layer:side " + << digit.getDetector() / 30 << ":" << Geometry::getStack(digit.getDetector()) + << ":" << Geometry::getLayer(digit.getDetector()) << ":" << FeeParam::instance()->getRobSide(mFeeParam->getROBfromPad(digit.getRow(), digit.getPad())) + << " with ORI# : " << mFeeParam->getORI(digit.getDetector(), mFeeParam->getROBfromPad(digit.getRow(), digit.getPad())) + << " within SM ori#:" << mFeeParam->getORIinSM(digit.getDetector(), mFeeParam->getROBfromPad(digit.getRow(), digit.getPad())); + } + //accounting variables for various things. + //TODO make them class members int olddetector = -1; int oldrow = -1; int oldpad = -1; @@ -409,40 +386,59 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) int oldsize = 0; double trackletrate; unsigned long oldtrackletcount = 0; + mTotalRawWordsWritten = 0; // words written for the raw format of 4x32bits, where 4 can be 2 to 4 depending on # of tracklets in the block. + mOldHalfChamberLinkId = 0; + mNewTrackletHCHeaderHasBeenWritten = false; // now to loop over the incoming digits. auto digitloopstart = std::chrono::high_resolution_clock::now(); uint64_t digitcounter = 0; - for (auto digititerator = msgDigits.begin(); digititerator != msgDigits.end() /* && loopindex<300*/; ++digititerator) { + double b = 0; + LOG(debug4) << "now for digit loop "; + for (auto digititerator = msgDigitsIndex.begin(); digititerator != msgDigitsIndex.end() /* && std::distance(msgDigits.begin(),digititerator)<7*/; ++digititerator) { //in here we have an entire padrow which corresponds to 8 TRAPs. //while on a single padrow, populate data structures in the 8 trapsimulator. //on change of padrow // fireup trapsim, do its thing with each 18 sequence of pads data that already exists inside the class from previous iterations of the loop - double digittime = digititerator->getTimeStamp(); - int pad = digititerator->getPad(); - int row = digititerator->getRow(); - int detector = digititerator->getDetector(); + LOG(debug) << "Digit iterator is : " << *digititerator; + Digit* digit = &msgDigits[*digititerator]; + double digittime = digit->getTimeStamp(); + int pad = digit->getPad(); + int row = digit->getRow(); + int detector = digit->getDetector(); int rob = mFeeParam->getROBfromPad(row, pad); int mcm = mFeeParam->getMCMfromPad(row, pad); - LOG(debug3) << "calculated rob and mcm at top of loop with detector:row:pad:rob:mcm" << detector << ":" << row << ":" << pad << ":" << rob << ":" << mcm; - if (digititerator == msgDigits.begin()) { // first time in loop + int trdstack = Geometry::getStack(detector); + int trdlayer = Geometry::getLayer(detector); + int fibreside = FeeParam::instance()->getRobSide(rob); + + LOG(debug) << "calculated rob and mcm at top of loop with detector:row:pad:rob:mcm ::" + << detector << ":" << row << ":" << pad << ":" << rob << ":" << mcm + << " LinkId:" << LinkRecord::getHalfChamberLinkId(detector, rob) << "\t\t SM:stack:layer:side " << detector / 30 << ":" << trdstack << ":" << trdlayer << ":" << fibreside + << " with ORI : " << mFeeParam->getORI(detector, rob) << " and within supermodule ori index:" << mFeeParam->getORIinSM(detector, rob); + LOG(debug) << "digit time : " << digittime; + if (digititerator == msgDigitsIndex.begin()) { // first time in loop oldrow = row; olddetector = detector; } - + //Are we on a new half chamber ? + if (mOldHalfChamberLinkId != LinkRecord::getHalfChamberLinkId(detector, rob)) { + // hcid= detector*2 + robpos%2; + // new half chamber so add the header to the raw data stream. + buildTrackletHCHeaderd(mTrackletHCHeader, detector, rob, currentTriggerRecord * 42, 4); + //buildTrackletHCHeader(mTrackletHCHeader,sector,stack,layer,side,currentTriggerRecord*42,4); + mOldHalfChamberLinkId = LinkRecord::getHalfChamberLinkId(detector, rob); + // now we have a problem. We must only write the halfchamberheader if a tracklet is written i.e. if the digits for this half chamber actually produce 1 or more tracklets! + mNewTrackletHCHeaderHasBeenWritten = false; + } //figure out which trigger record from digits we are on if (digitcounter >= triggerRecords[currentTriggerRecord].getFirstEntry() + triggerRecords[currentTriggerRecord].getNumberOfObjects()) { //trigger record changed. - // so increment the tracklet trigger records and fill accordingly for the now completed prior triggerrecord. - uint64_t triggerrecordstart = 0; - if (currentTriggerRecord == 0) { // for not the first one we can simply look back to the previous one to get the start. - triggerrecordstart = 0; - trackletTriggerRecords[currentTriggerRecord].setDataRange(triggerrecordstart, trapTrackletsAccum.size()); - } else { - triggerrecordstart = trackletTriggerRecords[currentTriggerRecord - 1].getFirstEntry() + trackletTriggerRecords[currentTriggerRecord - 1].getNumberOfObjects(); - trackletTriggerRecords[currentTriggerRecord].setDataRange(triggerrecordstart, trapTrackletsAccum.size() - triggerrecordstart); - } - currentTriggerRecord++; //move to next trigger record. + //Now we know the ranges so populate the triggerrecord related to the previously block of data. + setTriggerRecord(trackletTriggerRecords, currentTriggerRecord, trapTrackletsAccum.size()); + setTriggerRecord(rawTriggerRecords, currentTriggerRecord, mTotalRawWordsWritten); + currentTriggerRecord++; + LOG(debug) << "changing trigger records : " << currentTriggerRecord; } if (olddetector != detector || oldrow != row) { @@ -454,7 +450,6 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) unsigned long numberofusedtraps = 0; for (int trapcounter = 0; trapcounter < 8; trapcounter++) { unsigned int isinit = mTrapSimulator[trapcounter].checkInitialized(); - // LOG(debug3) << "Start of trap : " << trapcounter; if (mTrapSimulator[trapcounter].isDataSet()) { //firedtraps //this one has been filled with data for the now previous pad row. auto trapsimtimerstart = std::chrono::high_resolution_clock::now(); @@ -463,9 +458,26 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) mTrapSimulator[trapcounter].filter(); mTrapSimulator[trapcounter].tracklet(); - trapTracklets = mTrapSimulator[trapcounter].getTrackletArray(); //TODO remove the copy and send the Accumulated array into the Trapsimulator + trapTracklets = mTrapSimulator[trapcounter].getTrackletArray64(); //TODO remove the copy and send the Accumulated array into the Trapsimulator auto trapLabels = mTrapSimulator[trapcounter].getTrackletLabels(); - + if (!mNewTrackletHCHeaderHasBeenWritten && trapTracklets.size() != 0) { // take account of the case where we have data in the trapchip adc but no tracklets + //we have a tracklet for said half chamber, but the halfchamber ID has not been written yet + // .. fix the previous linkrecord to note its end of range. + if (mLinkRecords.size() == 0) { // special case for the first entry into the linkrecords vector. + mLinkRecords.emplace_back(mTrackletHCHeader.word, 0, -1); + // LOG(debug) << " added HCID :[record.size==0] " << mTrackletHCHeader.HCID << " with number of bytes : " << mTotalRawWordsWritten << "-" << mLinkRecords.back().getFirstEntry(); + } else { + mLinkRecords.back().setNumberOfObjects(mTotalRawWordsWritten - mLinkRecords.back().getFirstEntry()); // current number of words written - the start of this index record. + // LOG(debug) << " added HCID : " << mTrackletHCHeader.HCID << " with number of bytes : " << mTotalRawWordsWritten << "-" << mLinkRecords.back().getFirstEntry(); + //..... so write the new one thing + mLinkRecords.emplace_back(mTrackletHCHeader.word, mTotalRawWordsWritten, -1); // set the number of elements to -1 for an error condition + } + mNewTrackletHCHeaderHasBeenWritten = true; + LOG(debug) << mTrackletHCHeader; + } + LOG(debug) << "getting trackletsteram for trapcounter = " << trapcounter; + auto wordswritten = mTrapSimulator[trapcounter].getTrackletStream(rawdata, mTotalRawWordsWritten); // view of data from current marker and only 5 words long (can only have 4 words at most in the trackletstream for 1 MCM) + mTotalRawWordsWritten += wordswritten; LOG(debug) << "Tracklets accumulated before addition of new ones :" << trapTrackletsAccum.size() << " :: about to add " << trapTracklets.size() << " count tracklets, labels coming in index of: " << trapLabels.getIndexedSize() << " and elements of : " @@ -483,16 +495,17 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) // mTrapSimulator[trapcounter].zeroSupressionMapping(); - // if (mDrawTrackletOptions != 0) - // mTrapSimulator[trapcounter].draw(mDrawTrackletOptions, loopindex); + if (mDrawTrackletOptions != 0) { + mTrapSimulator[trapcounter].draw(mDrawTrackletOptions, loopindex); + } if (mDebugRejectedTracklets) { //&& trapTracklets.size()==0) { mTrapSimulator[trapcounter].draw(7, loopindex); //draw adc when no tracklets are found.A - LOG(info) << "loop index : " << loopindex; - mTrapSimulator[trapcounter].print(1); - // if(loopindex==320) LOG(fatal) <<"exiting at trap loop count 320"; + LOG(debug) << "loop index : " << loopindex; + //mTrapSimulator[trapcounter].print(1); + } + if (mPrintTrackletOptions != 0) { + mTrapSimulator[trapcounter].print(mPrintTrackletOptions); } - // if (mPrintTrackletOptions != 0) - // mTrapSimulator[trapcounter].print(mPrintTrackletOptions); loopindex++; //set this trap sim object to have not data (effectively) reset. @@ -500,8 +513,9 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) } else { LOG(debug) << "if statement is init failed [" << trapcounter << "] PROCESSING TRAP !"; } - // LOG(info) << "Finishe MCM : : " << trapcounter; + // LOG(debug) << "Finishe MCM : : " << trapcounter; } //end of loop over trap chips + //timing info mTrapLoopTime += std::chrono::high_resolution_clock::now() - traploopstart; mTrapUsedFrequency[numberofusedtraps]++; @@ -527,31 +541,35 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) int trapindex = pad / 18; //check trap is initialised. if (!mTrapSimulator[trapindex].isDataSet()) { - // LOG(debug) << "Initialising trapsimulator for triplet (" << detector << "," << rob << "," - // << mcm << ") as its not initialized and we need to send it some adc data."; + LOG(debug) << "Initialising trapsimulator for triplet (" << detector << "," << rob << "," + << mcm << ") as its not initialized and we need to send it some adc data."; mTrapSimulator[trapindex].init(mTrapConfig, detector, rob, mcm); } - int adc = 0; - adc = 20 - (pad % 18) - 1; + int adc = 20 - (pad % 18) - 1; std::vector<o2::MCCompLabel> tmplabels; - auto digitslabels = digitMCLabels->getLabels(digitcounter); + auto digitslabels = digitMCLabels.getLabels(digitcounter); for (auto& tmplabel : digitslabels) { tmplabels.push_back(tmplabel); } LOG(debug) << "tmplabels for set data : " << tmplabels.size() << " and gslspan digitlabels size of : " << digitslabels.size(); - mTrapSimulator[trapindex].setData(adc, digititerator->getADC(), tmplabels); + LOG(debug) << " setting data with pad=" << pad << " ti=" << trapindex + 1; + mTrapSimulator[trapindex].setData(adc, digit->getADC(), tmplabels); // now take care of the case of shared pads (the whole reason for doing this pad row wise). if (pad % 18 == 0 || (pad + 1) % 18 == 0) { //case of pad 18 and 19 must be shared to preceding trap chip adc 1 and 0 respectively. adc = 20 - (pad % 18) - 1; - mTrapSimulator[trapindex - 1].setData(adc, digititerator->getADC(), tmplabels); + if (trapindex != 0) { // avoid the case of the first trap chip + LOG(debug) << " setting data preceding with pad=" << pad << " ti=" << trapindex - 1; + mTrapSimulator[trapindex - 1].setData(adc, digit->getADC(), tmplabels); + } } if ((pad - 1) % 18 == 0) { // case of pad 17 must shared to next trap chip as adc 20 //check trap is initialised. adc = 20 - (pad % 18) - 1; if (trapindex + 1 != 8) { // avoid the case of the last trap chip. - mTrapSimulator[trapindex + 1].setData(adc, digititerator->getADC(), tmplabels); + LOG(debug) << " setting data proceeding with pad=" << pad << " ti=" << trapindex + 1; + mTrapSimulator[trapindex + 1].setData(adc, digit->getADC(), tmplabels); } } @@ -564,29 +582,38 @@ void TRDDPLTrapSimulatorTask::run(o2::framework::ProcessingContext& pc) // now finalise auto triggerrecordstart = trackletTriggerRecords[currentTriggerRecord - 1].getFirstEntry() + trackletTriggerRecords[currentTriggerRecord - 1].getNumberOfObjects(); trackletTriggerRecords[currentTriggerRecord].setDataRange(triggerrecordstart, trapTrackletsAccum.size() - triggerrecordstart); + mLinkRecords.back().setNumberOfObjects(mTotalRawWordsWritten - mLinkRecords.back().getFirstEntry()); // set the final link entry LOG(info) << "Trap simulator found " << trapTrackletsAccum.size() << " tracklets from " << msgDigits.size() << " Digits and " << trackletMCLabels.getIndexedSize() << " associated MC Label indexes and " << trackletMCLabels.getNElements() << " associated MC Labels"; if (mShowTrackletStats > 0) { mDigitLoopTime = std::chrono::high_resolution_clock::now() - digitloopstart; - LOG(info) << "Trap Simulator done \\o/ "; + LOG(info) << "Trap Simulator done "; #ifdef TRDTIMESORT - LOG(info) << "Sorting took " << mSortingTime.count(); + LOG(info) << "Sorting took " << mSortingTime.count() << "s"; #endif - LOG(info) << "Digit loop took : " << mDigitLoopTime.count(); - LOG(info) << "Trapsim took : " << mTrapSimAccumulatedTime.count(); - LOG(info) << "Traploop took : " << mTrapLoopTime.count(); + LOG(info) << "Digit loop took : " << mDigitLoopTime.count() << "s"; + LOG(info) << "Trapsim took : " << mTrapSimAccumulatedTime.count() << "s"; + LOG(info) << "Traploop took : " << mTrapLoopTime.count() << "s"; for (auto trapcount : mTrapUsedFrequency) { LOG(info) << "# traps fired Traploop are : " << trapcount; } for (auto trapcount : mTrapUsedCounter) { LOG(info) << "each trap position fired : " << trapcount; } + LOG(info) << "Raw data words written = " << mTotalRawWordsWritten << " with a vector size = " << rawdata.size(); + LOG(info) << "Raw data words written = " << mTotalRawWordsWritten << " with a vector size = " << rawdata.size(); } - + LOG(debug) << "END OF RUN ............."; + //TODO does anyone care to have the digits to tracklet mapping. Do we then presort the digits inline with the index or send both digits and sorted index. + //TODO is is available for post processing via the debug stream output. pc.outputs().snapshot(Output{"TRD", "TRACKLETS", 0, Lifetime::Timeframe}, trapTrackletsAccum); pc.outputs().snapshot(Output{"TRD", "TRKTRGRD", 0, Lifetime::Timeframe}, trackletTriggerRecords); /*pc.outputs().snapshot(Output{"TRD", "TRKLABELS", 0, Lifetime::Timeframe}, trackletMCLabels); */ // LOG(info) << "digit MCLabels is of type : " << type_id_with_cvr<decltype(digitMCLabels)>().pretty_name(); - LOG(info) << "exiting the trap sim run method "; + pc.outputs().snapshot(Output{"TRD", "RAWDATA", 0, Lifetime::Timeframe}, rawdata); + pc.outputs().snapshot(Output{"TRD", "RAWTRGRD", 0, Lifetime::Timeframe}, rawTriggerRecords); + pc.outputs().snapshot(Output{"TRD", "RAWLNKRD", 0, Lifetime::Timeframe}, mLinkRecords); + + LOG(debug) << "exiting the trap sim run method "; pc.services().get<ControlService>().endOfStream(); pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); } @@ -595,23 +622,24 @@ o2::framework::DataProcessorSpec getTRDTrapSimulatorSpec() { return DataProcessorSpec{"TRAP", Inputs{InputSpec{"digitinput", "TRD", "DIGITS", 0}, InputSpec{"triggerrecords", "TRD", "TRGRDIG", 0}, InputSpec{"labelinput", "TRD", "LABELS", 0}}, - Outputs{OutputSpec{"TRD", "TRACKLETS", 0, Lifetime::Timeframe}, - OutputSpec{"TRD", "TRKTRGRD", 0, Lifetime::Timeframe} + Outputs{OutputSpec{"TRD", "TRACKLETS", 0, Lifetime::Timeframe}, // this is the 64 tracklet words + OutputSpec{"TRD", "TRKTRGRD", 0, Lifetime::Timeframe}, /*OutputSpec{"TRD", "TRKDIGITS", 0, Lifetime::Timeframe},*/ - /*OutputSpec{"TRD", "TRKLABELS", 0, Lifetime::Timeframe},*/ - /*OutputSpec{"TRD", "RAWDATA", 0, Lifetime::Timeframe}*/}, + OutputSpec{"TRD", "TRKLABELS", 0, Lifetime::Timeframe}, + /*OutputSpec{"TRD", "TRAPRAWDUMP", 0, Lifetime::Timeframe},*/ + OutputSpec{"TRD", "RAWTRGRD", 0, Lifetime::Timeframe}, // offsets for each event in the rawdata + OutputSpec{"TRD", "RAWLNKRD", 0, Lifetime::Timeframe}, // offsets for each link/halfchamberheader in the rawdata, halfchamberheader sitting in here. + OutputSpec{"TRD", "RAWDATA", 0, Lifetime::Timeframe}}, // this is the mcmheader,traprawtracklet, repeat in varying quantities. AlgorithmSpec{adaptFromTask<TRDDPLTrapSimulatorTask>()}, Options{ {"show-trd-trackletstats", VariantType::Int, 25000, {"Display the accumulated size and capacity at number of track intervals"}}, - {"trd-trapconfig", VariantType::String, "default", {"Name of the trap config from the CCDB"}}, - {"trd-printtrapconfig", VariantType::Bool, false, {"Name of the trap config from the CCDB"}}, - {"trd-drawtracklets", VariantType::Int, 0, {"Bitpattern of input to TrapSimulator Draw method (be very careful) one file per track"}}, - {"trd-printtracklets", VariantType::Int, 0, {"Bitpattern of input to TrapSimulator print method"}}, - {"trd-fixtriggerrecord", VariantType::Bool, false, {"Fix trigger record alignment, temporary, hence false by default"}}, + {"trd-trapconfig", VariantType::String, "cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5549", {"Name of the trap config from the CCDB default:cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5549"}}, + {"trd-drawtracklets", VariantType::Int, 0, {"Bitpattern of input to TrapSimulator Draw method one histogram per chip not per tracklet, 1=raw,2=hits,4=tracklets, 7 for all"}}, + {"trd-printtracklets", VariantType::Int, 0, {"Bitpattern of input to TrapSimulator print method, "}}, {"trd-onlinegaincorrection", VariantType::Bool, false, {"Apply online gain calibrations, mostly for back checking to run2 by setting FGBY to 0"}}, {"trd-onlinegaintable", VariantType::String, "Krypton_2015-02", {"Online gain table to be use, names found in CCDB, obviously trd-onlinegaincorrection must be set as well."}}, {"trd-debugrejectedtracklets", VariantType::Bool, false, {"Output all MCM where tracklets were not identified"}}, - {"trd-dumptrapconfig", VariantType::Bool, false, {"Dump the trapconfig at loading"}}, + {"trd-dumptrapconfig", VariantType::Bool, false, {"Dump the selected trap configuration at loading time, to text file"}}, {"trd-runnum", VariantType::Int, 297595, {"Run number to use to anchor simulation to, defaults to 297595"}}}}; }; diff --git a/Detectors/TRD/workflow/src/TRDTrapSimulatorWorkFlow.cxx b/Detectors/TRD/workflow/src/TRDTrapSimulatorWorkFlow.cxx index 84d0516ecaea5..7ee787c381be3 100644 --- a/Detectors/TRD/workflow/src/TRDTrapSimulatorWorkFlow.cxx +++ b/Detectors/TRD/workflow/src/TRDTrapSimulatorWorkFlow.cxx @@ -20,6 +20,7 @@ // for TRD #include "TRDWorkflow/TRDTrapSimulatorSpec.h" #include "TRDWorkflow/TRDTrackletWriterSpec.h" +#include "TRDWorkflow/TRDTrapRawWriterSpec.h" #include "TRDWorkflow/TRDDigitReaderSpec.h" #include "DataFormatsParameters/GRPObject.h" @@ -85,5 +86,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // connect the TRD digitization o2::trd::getTRDTrapSimulatorSpec(), // connect the TRD digit writer - o2::trd::getTRDTrackletWriterSpec()}; + o2::trd::getTRDTrackletWriterSpec(), + // connect the TRD digit writer + o2::trd::getTRDTrapRawWriterSpec()}; } diff --git a/Detectors/TRD/workflow/src/TRDWorkflowLinkDef.h b/Detectors/TRD/workflow/src/TRDWorkflowLinkDef.h index 51b41d4d5fa62..e109530d22899 100644 --- a/Detectors/TRD/workflow/src/TRDWorkflowLinkDef.h +++ b/Detectors/TRD/workflow/src/TRDWorkflowLinkDef.h @@ -15,6 +15,6 @@ #pragma link off all functions; //#pragma link C++ class o2::trd::TRDDPLDigitizerTask + ; - +#pragma link C++ class gsl::span < uint32_t > +; #endif diff --git a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx new file mode 100644 index 0000000000000..4a15ec72aee6f --- /dev/null +++ b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx @@ -0,0 +1,44 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "TRDWorkflow/TRDTrackingWorkflow.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.push_back(ConfigParamSpec{ + "disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}); + workflowOptions.push_back(ConfigParamSpec{ + "disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}); + std::string keyvaluehelp("Semicolon separated key=value strings ..."); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); +} + +// ------------------------------------------------------------------ + +#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<std::string>("configKeyValues")); + // write the configuration used for the workflow + o2::conf::ConfigurableParam::writeINI("o2trdtracking-workflow_configuration.ini"); + auto disableRootInp = configcontext.options().get<bool>("disable-root-input"); + auto disableRootOut = configcontext.options().get<bool>("disable-root-output"); + return std::move(o2::trd::getTRDTrackingWorkflow(disableRootInp, disableRootOut)); +} diff --git a/Detectors/Upgrades/ALICE3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/CMakeLists.txt new file mode 100644 index 0000000000000..c8f381fd9f8ab --- /dev/null +++ b/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -0,0 +1,11 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(TRK) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt new file mode 100644 index 0000000000000..4d58e2495d96f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -0,0 +1,13 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(simulation) +add_subdirectory(base) +add_subdirectory(macros) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/README.md b/Detectors/Upgrades/ALICE3/TRK/README.md new file mode 100644 index 0000000000000..f35f9d4fe20dc --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/README.md @@ -0,0 +1,21 @@ +<!-- doxy +\page refDetectorsUpgradesPostLS4TRK UpgradesTRK +/doxy --> + +# TRK +At the moment the TRK name is a placeholder for silicon barrel detector. + +# Run the full simulation +Provided O2 has been compiled with upgrades enabled, it is possible to simulate TRK geometry using the `o2-sim` executable. + +## Simulation +TRK module is enabled via the `-m TRK` parameter. +In case of `PIPE` to be enabled, the size of the beam pipe is automatically scaled to what is foreseen for the upgrades. + +Typical command to generate MC data: +```bash +o2-sim -m PIPE TRK [...] +``` + +## Reconstruction +Currently, the reconstruction is driven the `macro/run_trac_alice3.C`, which takes as input the hits generated by simulation. Hits can be smeared upon request (see `kUseSmearing`). \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt new file mode 100644 index 0000000000000..fdd2f81a51017 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt @@ -0,0 +1,18 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(TRKBase + SOURCES src/MisalignmentParameter.cxx + src/GeometryTGeo.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::ITSMFTBase) + +o2_target_root_dictionary(TRKBase + HEADERS include/TRKBase/GeometryTGeo.h + include/TRKBase/MisalignmentParameter.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h new file mode 100644 index 0000000000000..9c289f5243a54 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -0,0 +1,355 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GeometryTGeo.h +/// \brief Definition of the GeometryTGeo class +/// \author cvetan.cheshkov@cern.ch - 15/02/2007 +/// \author ruben.shahoyan@cern.ch - adapted to ITSupg 18/07/2012 + +#ifndef ALICEO2_TRK_GEOMETRYTGEO_H_ +#define ALICEO2_TRK_GEOMETRYTGEO_H_ + +#include <TGeoMatrix.h> // for TGeoHMatrix +#include <TObject.h> // for TObject +#include <array> +#include <string> +#include <vector> +#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 + +class TGeoPNEntry; + +namespace o2 +{ +namespace trk +{ +/// GeometryTGeo is a simple interface class to TGeoManager. It is used in the simulation +/// and reconstruction in order to query the TGeo ITS geometry. +/// RS: In order to preserve the static character of the class but make it dynamically access +/// geometry, we need to check in every method if the structures are initialized. To be converted +/// to singleton at later stage. + +class GeometryTGeo : public o2::itsmft::GeometryTGeo +{ + public: + typedef o2::math_utils::Transform3D Mat3D; + using DetMatrixCache::getMatrixL2G; + using DetMatrixCache::getMatrixT2GRot; + using DetMatrixCache::getMatrixT2L; + // this method is not advised for ITS: for barrel detectors whose tracking frame is just a rotation + // it is cheaper to use T2GRot + using DetMatrixCache::getMatrixT2G; + + static GeometryTGeo* Instance() + { + // get (create if needed) a unique instance of the object + if (!sInstance) { + sInstance = std::unique_ptr<GeometryTGeo>(new GeometryTGeo(true, 0)); + } + return sInstance.get(); + } + + // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) + static void adopt(GeometryTGeo* raw); + + // 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)*/ + ); + + /// Default destructor + ~GeometryTGeo() override = default; + + GeometryTGeo(const GeometryTGeo& src) = delete; + GeometryTGeo& operator=(const GeometryTGeo& geom) = delete; + + // implement filling of the matrix cache + using o2::itsmft::GeometryTGeo::fillMatrixCache; + void fillMatrixCache(int mask) override; + + // cache parameters of sensors tracking frames + void fillTrackingFramesCache(); + + /// Exract ITS parameters from TGeo + void Build(int loadTrans = 0) override; + + int getNumberOfChipRowsPerModule(int lay) const { return mNumberOfChipRowsPerModule[lay]; } + int getNumberOfChipColsPerModule(int lay) const + { + return mNumberOfChipRowsPerModule[lay] ? mNumberOfChipsPerModule[lay] / mNumberOfChipRowsPerModule[lay] : -1; + } + + int getNumberOfChipsPerModule(int lay) const { return mNumberOfChipsPerModule[lay]; } + int getNumberOfChipsPerHalfStave(int lay) const { return mNumberOfChipsPerHalfStave[lay]; } + int getNumberOfChipsPerStave(int lay) const { return mNumberOfChipsPerStave[lay]; } + int getNumberOfChipsPerLayer(int lay) const { return mNumberOfChipsPerLayer[lay]; } + int getNumberOfModules(int lay) const { return mNumberOfModules[lay]; } + int getNumberOfHalfStaves(int lay) const { return mNumberOfHalfStaves[lay]; } + int getNumberOfStaves(int lay) const { return mNumberOfStaves[lay]; } + int getNumberOfLayers() const { return mNumberOfLayers; } + int getChipIndex(int lay, int detInLay) const { return getFirstChipIndex(lay) + detInLay; } + /// This routine computes the chip index number from the layer, stave, and chip number in stave + /// \param int lay The layer number. Starting from 0. + /// \param int sta The stave number. Starting from 0 + /// \param int chipInStave The chip number in the stave. Starting from 0 + int getChipIndex(int lay, int sta, int detInSta) const; + + /// This routine computes the chip index number from the layer, stave, substave and chip number + /// in substave + /// \param int lay The layer number. Starting from 0. + /// \param int sta The stave number. Starting from 0 + /// \param int substa The substave number. Starting from 0 + /// \param int chipInSStave The chip number in the sub stave. Starting from 0 + int getChipIndex(int lay, int sta, int subSta, int detInSubSta) const; + + /// This routine computes the chip index number from the layer,stave, substave module and + /// chip number in module. + /// \param int lay The layer number. Starting from 0. + /// \param int sta The stave number. Starting from 0 + /// \param int substa The substave number. Starting from 0 + /// \param int module The module number ... + /// \param int chipInSStave The chip number in the module. Starting from 0 + int getChipIndex(int lay, int sta, int subSta, int md, int detInMod) const; + + /// This routine computes the layer, stave, substave, module and chip number + /// given the chip index number + /// \param int index The chip index number, starting from zero. + /// \param int lay The layer number. Starting from 0 + /// \param int sta The stave number. Starting from 0 + /// \param int ssta The halfstave number. Starting from 0 + /// \param int mod The module number. Starting from 0 + /// \param int chip The detector number. Starting from 0 + bool getChipId(int index, int& lay, int& sta, int& ssta, int& mod, int& chip) const; + + /// Get chip layer, from 0 + int getLayer(int index) const; + + /// Get chip stave, from 0 + int getStave(int index) const; + + /// Get chip substave id in stave, from 0 + int getHalfStave(int index) const; + + /// Get chip module id in substave, from 0 + int getModule(int index) const; + + /// Get chip number within layer, from 0 + int getChipIdInLayer(int index) const; + + /// Get chip number within stave, from 0 + int getChipIdInStave(int index) const; + + /// Get chip number within stave, from 0 + int getChipIdInHalfStave(int index) const; + + /// Get chip number within module, from 0 + int getChipIdInModule(int index) const; + + int getLastChipIndex(int lay) const { return mLastChipIndex[lay]; } + int getFirstChipIndex(int lay) const { return (lay == 0) ? 0 : mLastChipIndex[lay - 1] + 1; } + const char* getSymbolicName(int index) const + { + /// return symbolic name of sensor + return o2::base::GeometryManager::getSymbolicName(getDetID(), index); + } + + const char* getSymbolicName(int lay, int sta, int det) const + { + /// return symbolic name of sensor + return getSymbolicName(getChipIndex(lay, sta, det)); + } + + /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by quering the TGeoManager + TGeoHMatrix* getMatrix(int index) const { return o2::base::GeometryManager::getMatrix(getDetID(), index); } + TGeoHMatrix* getMatrix(int lay, int sta, int sens) const { return getMatrix(getChipIndex(lay, sta, sens)); } + bool getOriginalMatrix(int index, TGeoHMatrix& m) const + { + /// Get the original (ideal geometry) TGeo matrix for a given chip identified by 'index' + /// The method is slow, so it should be used with great care (for caching only) + return o2::base::GeometryManager::getOriginalMatrix(getDetID(), index, m); + } + + bool getOriginalMatrix(int lay, int sta, int det, TGeoHMatrix& m) const + { + /// Get the original (ideal geometry) TGeo matrix for a given chip identified by 'index' + /// The method is slow, so it should be used with great care (for caching only) + return getOriginalMatrix(getChipIndex(lay, sta, det), m); + } + + const Mat3D& getMatrixT2L(int lay, int sta, int det) const { return getMatrixT2L(getChipIndex(lay, sta, det)); } + const Mat3D& getMatrixSensor(int index) const { return getMatrixL2G(index); } + const Mat3D& getMatrixSensor(int lay, int sta, int det) + { + // get positioning matrix of the sensor, alias to getMatrixL2G + return getMatrixSensor(getChipIndex(lay, sta, det)); + } + + const Rot2D& getMatrixT2GRot(int lay, int sta, int sens) + { + /// get matrix for tracking to global frame transformation + return getMatrixT2GRot(getChipIndex(lay, sta, sens)); + } + + bool isTrackingFrameCached() const { return !mCacheRefX.empty(); } + void getSensorXAlphaRefPlane(int index, float& x, float& alpha) const + { + x = getSensorRefX(index); + alpha = getSensorRefAlpha(index); + } + + float getSensorRefX(int isn) const { return mCacheRefX[isn]; } + float getSensorRefAlpha(int isn) const { return mCacheRefAlpha[isn]; } + // Attention: these are transformations wrt sensitive volume! + void localToGlobal(int index, const double* loc, double* glob); + + void localToGlobal(int lay, int sta, int det, const double* loc, double* glob); + + void globalToLocal(int index, const double* glob, double* loc); + + void globalToLocal(int lay, int sta, int det, const double* glob, double* loc); + + void localToGlobalVector(int index, const double* loc, double* glob); + + void globalToLocalVector(int index, const double* glob, double* loc); + + void Print(Option_t* opt = "") const; + + static const char* getITSVolPattern() { return sVolumeName.c_str(); } + static const char* getITSLayerPattern() { return sLayerName.c_str(); } + static const char* getITSWrapVolPattern() { return sWrapperVolumeName.c_str(); } + static const char* getITSStavePattern() { return sStaveName.c_str(); } + static const char* getITSHalfStavePattern() { return sHalfStaveName.c_str(); } + static const char* getITSModulePattern() { return sModuleName.c_str(); } + static const char* getITSChipPattern() { return sChipName.c_str(); } + static const char* getITSSensorPattern() { return sSensorName.c_str(); } + static void setITSVolPattern(const char* nm) { sVolumeName = nm; } + static void setITSLayerPattern(const char* nm) { sLayerName = nm; } + static void setITSWrapVolPattern(const char* nm) { sWrapperVolumeName = nm; } + static void setITSStavePattern(const char* nm) { sStaveName = nm; } + static void setITSHalfStavePattern(const char* nm) { sHalfStaveName = nm; } + static void setITSModulePattern(const char* nm) { sModuleName = nm; } + static void setITSChipPattern(const char* nm) { sChipName = nm; } + static void setITSSensorPattern(const char* nm) { sSensorName = nm; } + /// sym name of the layer + static const char* composeSymNameTRK() { return o2::detectors::DetID(o2::detectors::DetID::IT3).getName(); } + /// sym name of the layer + static const char* composeSymNameLayer(int lr); + + /// Sym name of the stave at given layer + static const char* composeSymNameStave(int lr, int sta); + + /// Sym name of the stave at given layer + static const char* composeSymNameHalfStave(int lr, int sta, int ssta); + + /// Sym name of the substave at given layer/stave + static const char* composeSymNameModule(int lr, int sta, int ssta, int mod); + + /// Sym name of the chip in the given layer/stave/substave/module + static const char* composeSymNameChip(int lr, int sta, int ssta, int mod, int chip); + + protected: + /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) + /// for a given chip 'index' by quering the TGeoManager + TGeoHMatrix* extractMatrixSensor(int index) const; + + // create matrix for transformation from sensor local frame to global one + TGeoHMatrix& createT2LMatrix(int isn); + + // get sensor tracking frame alpha and + void extractSensorXAlpha(int isn, float& x, float& alp); + + /// 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. + /// \param int lay The layer number. Starting from 0. + bool getLayer(int index, int& lay, int& index2) const; + + /// Determines the number of chips per module on the (sub)stave in the Geometry + /// Also extract the layout: span of module centers in Z and X + /// \param lay: layer number from 0 + int extractNumberOfChipsPerModule(int lay, int& nrow) const; + + /// Determines the number of layers in the Geometry + /// \param lay: layer number, starting from 0 + int extractNumberOfStaves(int lay) const; + + /// Determines the number of substaves in the stave of the layer + /// \param lay: layer number, starting from 0 + int extractNumberOfHalfStaves(int lay) const; + + /// Determines the number of modules in substave in the stave of the layer + /// \param lay: layer number, starting from 0 + /// For the setup w/o modules defined the module and the stave or the substave is the same thing + /// Legacy method, keep it just in case... + int extractNumberOfModules(int lay) const; + + /// Determines the layer detector type the Geometry and + /// returns the detector type id for the layer + /// \param lay: layer number from 0 + int extractLayerChipType(int lay) const; + + /// Determines the number of layers in the Geometry + int extractNumberOfLayers(); + + /// Extract number following the prefix in the name string + int extractVolumeCopy(const char* name, const char* prefix) const; + + TGeoPNEntry* getPNEntry(int index) const + { + /// Get a pointer to the TGeoPNEntry of a chip identified by 'index' + /// Returns NULL in case of invalid index, missing TGeoManager or invalid symbolic name + return o2::base::GeometryManager::getPNEntry(getDetID(), index); + } + + protected: + static constexpr int MAXLAYERS = 15; ///< max number of active layers + + Int_t mNumberOfLayers; ///< number of layers + std::vector<int> mNumberOfStaves; ///< number of staves/layer(layer) + std::vector<int> mNumberOfHalfStaves; ///< the number of substaves/stave(layer) + std::vector<int> mNumberOfModules; ///< number of modules/substave(layer) + std::vector<int> mNumberOfChipsPerModule; ///< number of chips per module (group of chips on substaves) + std::vector<int> mNumberOfChipRowsPerModule; ///< number of chips rows per module (relevant for OB modules) + std::vector<int> mNumberOfChipsPerHalfStave; ///< number of chips per substave + std::vector<int> mNumberOfChipsPerStave; ///< number of chips per stave + std::vector<int> mNumberOfChipsPerLayer; ///< number of chips per stave + std::vector<int> mLastChipIndex; ///< max ID of the detctor in the layer + std::array<char, MAXLAYERS> mLayerToWrapper; ///< Layer to wrapper correspondence + + std::vector<float> mCacheRefX; ///< sensors tracking plane reference X + std::vector<float> mCacheRefAlpha; ///< sensors tracking plane reference alpha + + static std::string sVolumeName; ///< Mother volume name + static std::string sLayerName; ///< Layer name + static std::string sStaveName; ///< Stave name + static std::string sHalfStaveName; ///< HalfStave name + static std::string sModuleName; ///< Module name + static std::string sChipName; ///< Chip name + static std::string sSensorName; ///< Sensor name + static std::string sWrapperVolumeName; ///< Wrapper volume name + + private: + static std::unique_ptr<o2::trk::GeometryTGeo> sInstance; ///< singletone instance + + ClassDefOverride(GeometryTGeo, 1); // ITS geometry based on TGeo +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/MisalignmentParameter.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/MisalignmentParameter.h new file mode 100644 index 0000000000000..9c6c5c1f5bfab --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/MisalignmentParameter.h @@ -0,0 +1,70 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MisalignmentParameter.h +/// \brief Definition of the MisalignmentParameter class + +#ifndef ALICEO2_TRK_MISALIGNMENTPARAMETER_H_ +#define ALICEO2_TRK_MISALIGNMENTPARAMETER_H_ + +#include "FairParGenericSet.h" // for FairParGenericSet + +#include "Rtypes.h" // for ClassDef + +#include "TArrayD.h" // for TArrayD + +class FairParamList; + +namespace o2 +{ +namespace trk +{ +class MisalignmentParameter : public FairParGenericSet +{ + public: + MisalignmentParameter(const char* name = "MisalignmentParameter", + const char* title = "Misalignment parameter for AliceO2ITSHitProducerIdealMisallign Parameters", + const char* context = "TestDefaultContext"); + + ~MisalignmentParameter() override; + + void Clear(); + + void putParams(FairParamList*) override; + + Bool_t getParams(FairParamList*) override; + + TArrayD getShiftX() { return mShiftX; } + TArrayD getShiftY() { return mShiftY; } + TArrayD getShiftZ() { return mShiftZ; } + TArrayD getRotX() { return mRotX; } + TArrayD getRotY() { return mRotY; } + TArrayD getRotZ() { return mRotZ; } + Int_t getNumberOfDetectors() { return mNumberOfDetectors; } + + private: + TArrayD mShiftX; ///< Array to hold the misalignment in x-direction + TArrayD mShiftY; ///< Array to hold the misalignment in y-direction + TArrayD mShiftZ; ///< Array to hold the misalignment in z-direction + TArrayD mRotX; ///< Array to hold the rotation in x-direction + TArrayD mRotY; ///< Array to hold the rotation in y-direction + TArrayD mRotZ; ///< Array to hold the rotation in z-direction + Int_t mNumberOfDetectors; ///< Total number of detectors + + MisalignmentParameter(const MisalignmentParameter&); + + MisalignmentParameter& operator=(const MisalignmentParameter&); + + ClassDefOverride(MisalignmentParameter, 1); +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx new file mode 100644 index 0000000000000..e4c7c5447df2d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -0,0 +1,743 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GeometryTGeo.cxx +/// \brief Implementation of the GeometryTGeo class +/// \author cvetan.cheshkov@cern.ch - 15/02/2007 +/// \author ruben.shahoyan@cern.ch - adapted to ITSupg 18/07/2012 + +// ATTENTION: In opposite to old AliITSgeomTGeo, all indices start from 0, not from 1!!! + +#include "TRKBase/GeometryTGeo.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSMFTBase/SegmentationAlpide.h" +#include "MathUtils/Cartesian.h" + +#include "FairLogger.h" // for LOG + +#include <TGeoBBox.h> // for TGeoBBox +#include <TGeoManager.h> // for gGeoManager, TGeoManager +#include <TGeoPhysicalNode.h> // for TGeoPNEntry, TGeoPhysicalNode +#include <TGeoShape.h> // for TGeoShape +#include <TMath.h> // for Nint, ATan2, RadToDeg +#include <TString.h> // for TString, Form +#include "TClass.h" // for TClass +#include "TGeoMatrix.h" // for TGeoHMatrix +#include "TGeoNode.h" // for TGeoNode, TGeoNodeMatrix +#include "TGeoVolume.h" // for TGeoVolume +#include "TMathBase.h" // for Max +#include "TObjArray.h" // for TObjArray +#include "TObject.h" // for TObject + +#include <cctype> // for isdigit +#include <cstdio> // for snprintf, NULL, printf +#include <cstring> // for strstr, strlen + +using namespace TMath; +using namespace o2::trk; +using namespace o2::detectors; + +using Segmentation = o2::itsmft::SegmentationAlpide; + +ClassImp(o2::trk::GeometryTGeo); + +std::unique_ptr<o2::trk::GeometryTGeo> GeometryTGeo::sInstance; + +std::string GeometryTGeo::sVolumeName = "ITSV"; ///< Mother volume name +std::string GeometryTGeo::sLayerName = "ITSULayer"; ///< Layer name +std::string GeometryTGeo::sStaveName = "ITSUStave"; ///< Stave name +std::string GeometryTGeo::sHalfStaveName = "ITSUHalfStave"; ///< HalfStave name +std::string GeometryTGeo::sModuleName = "ITSUModule"; ///< Module name +std::string GeometryTGeo::sChipName = "ITSUChip"; ///< Chip name +std::string GeometryTGeo::sSensorName = "ITSUSensor"; ///< Sensor name +std::string GeometryTGeo::sWrapperVolumeName = "ITSUWrapVol"; ///< Wrapper volume name + +//__________________________________________________________________________ +GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::itsmft::GeometryTGeo(DetID::TRK) +{ + // default c-tor, if build is true, the structures will be filled and the transform matrices + // will be cached + if (sInstance) { + LOG(FATAL) << "Invalid use of public constructor: o2::trk::GeometryTGeo instance exists"; + // throw std::runtime_error("Invalid use of public constructor: o2::trk::GeometryTGeo instance exists"); + } + + for (int i = MAXLAYERS; i--;) { + mLayerToWrapper[i] = -1; + } + if (build) { + Build(loadTrans); + } +} + +//__________________________________________________________________________ +void GeometryTGeo::adopt(GeometryTGeo* raw) +{ + // 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::trk::GeometryTGeo instance exists"; + } + sInstance = std::unique_ptr<o2::trk::GeometryTGeo>(raw); +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIndex(int lay, int sta, int chipInStave) const +{ + return getFirstChipIndex(lay) + mNumberOfChipsPerStave[lay] * sta + chipInStave; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIndex(int lay, int sta, int substa, int chipInSStave) const +{ + int n = getFirstChipIndex(lay) + mNumberOfChipsPerStave[lay] * sta + chipInSStave; + if (mNumberOfHalfStaves[lay] && substa > 0) { + n += mNumberOfChipsPerHalfStave[lay] * substa; + } + return n; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIndex(int lay, int sta, int substa, int md, int chipInMod) const +{ + int n = getFirstChipIndex(lay) + mNumberOfChipsPerStave[lay] * sta + chipInMod; + if (mNumberOfHalfStaves[lay] && substa > 0) { + n += mNumberOfChipsPerHalfStave[lay] * substa; + } + if (mNumberOfModules[lay] && md > 0) { + n += mNumberOfChipsPerModule[lay] * md; + } + return n; +} + +//__________________________________________________________________________ +bool GeometryTGeo::getLayer(int index, int& lay, int& indexInLr) const +{ + lay = getLayer(index); + indexInLr = index - getFirstChipIndex(lay); + return kTRUE; +} + +//__________________________________________________________________________ +int GeometryTGeo::getLayer(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + return lay; +} + +//__________________________________________________________________________ +int GeometryTGeo::getStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index / mNumberOfChipsPerStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getHalfStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + if (mNumberOfHalfStaves[lay] < 0) { + return -1; + } + index -= getFirstChipIndex(lay); + index %= mNumberOfChipsPerStave[lay]; + return index / mNumberOfChipsPerHalfStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getModule(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + if (mNumberOfModules[lay] < 0) { + return 0; + } + index -= getFirstChipIndex(lay); + index %= mNumberOfChipsPerStave[lay]; + if (mNumberOfHalfStaves[lay]) { + index %= mNumberOfChipsPerHalfStave[lay]; + } + return index / mNumberOfChipsPerModule[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInLayer(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index % mNumberOfChipsPerStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInHalfStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index % mNumberOfChipsPerHalfStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInModule(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index % mNumberOfChipsPerModule[lay]; +} + +//__________________________________________________________________________ +bool GeometryTGeo::getChipId(int index, int& lay, int& sta, int& hsta, int& mod, int& chip) const +{ + lay = getLayer(index); + index -= getFirstChipIndex(lay); + sta = index / mNumberOfChipsPerStave[lay]; + index %= mNumberOfChipsPerStave[lay]; + hsta = mNumberOfHalfStaves[lay] > 0 ? index / mNumberOfChipsPerHalfStave[lay] : -1; + index %= mNumberOfChipsPerHalfStave[lay]; + mod = mNumberOfModules[lay] > 0 ? index / mNumberOfChipsPerModule[lay] : -1; + chip = index % mNumberOfChipsPerModule[lay]; + + return kTRUE; +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameLayer(int lr) +{ + return Form("%s/%s%d", composeSymNameTRK(), getITSLayerPattern(), lr); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameStave(int lr, int stave) +{ + return Form("%s/%s%d", composeSymNameLayer(lr), getITSStavePattern(), stave); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameHalfStave(int lr, int stave, int substave) +{ + return substave >= 0 ? Form("%s/%s%d", composeSymNameStave(lr, stave), getITSHalfStavePattern(), substave) + : composeSymNameStave(lr, stave); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameModule(int lr, int stave, int substave, int mod) +{ + return mod >= 0 ? Form("%s/%s%d", composeSymNameHalfStave(lr, stave, substave), getITSModulePattern(), mod) + : composeSymNameHalfStave(lr, stave, substave); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameChip(int lr, int sta, int substave, int mod, int chip) +{ + return Form("%s/%s%d", composeSymNameModule(lr, sta, substave, mod), getITSChipPattern(), chip); +} + +//__________________________________________________________________________ +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 + + int lay, stav, sstav, mod, chipInMod; + getChipId(index, lay, stav, sstav, mod, chipInMod); + + int wrID = mLayerToWrapper[lay]; + + TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getITSVolPattern()); + + if (wrID >= 0) { + path += Form("%s%d_1/", getITSWrapVolPattern(), wrID); + } + + path += + Form("%s%d_1/%s%d_%d/", GeometryTGeo::getITSLayerPattern(), lay, GeometryTGeo::getITSStavePattern(), lay, stav); + + if (mNumberOfHalfStaves[lay] > 0) { + path += Form("%s%d_%d/", GeometryTGeo::getITSHalfStavePattern(), lay, sstav); + } + if (mNumberOfModules[lay] > 0) { + path += Form("%s%d_%d/", GeometryTGeo::getITSModulePattern(), lay, mod); + } + path += + Form("%s%d_%d/%s%d_1", GeometryTGeo::getITSChipPattern(), lay, chipInMod, GeometryTGeo::getITSSensorPattern(), lay); + + static TGeoHMatrix matTmp; + gGeoManager->PushPath(); + + if (!gGeoManager->cd(path.Data())) { + gGeoManager->PopPath(); + LOG(ERROR) << "Error in cd-ing to " << path.Data(); + return nullptr; + } // end if !gGeoManager + + matTmp = *gGeoManager->GetCurrentMatrix(); // matrix may change after cd + // RSS + // printf("%d/%d/%d %s\n",lay,stav,detInSta,path.Data()); + // mat->Print(); + // Restore the modeler state. + gGeoManager->PopPath(); + + // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor ticknesses + static TGeoTranslation tra(0., 0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff), 0.); + + matTmp *= tra; + + return &matTmp; +} + +//__________________________________________________________________________ +void GeometryTGeo::Build(int loadTrans) +{ + if (isBuilt()) { + LOG(WARNING) << "Already built"; + return; // already initialized + } + + if (!gGeoManager) { + // RSTODO: in future there will be a method to load matrices from the CDB + LOG(FATAL) << "Geometry is not loaded"; + } + + mNumberOfLayers = extractNumberOfLayers(); + if (!mNumberOfLayers) { + return; + } + + mNumberOfStaves.resize(mNumberOfLayers); + mNumberOfHalfStaves.resize(mNumberOfLayers); + mNumberOfModules.resize(mNumberOfLayers); + mNumberOfChipsPerModule.resize(mNumberOfLayers); + mNumberOfChipRowsPerModule.resize(mNumberOfLayers); + mNumberOfChipsPerHalfStave.resize(mNumberOfLayers); + mNumberOfChipsPerStave.resize(mNumberOfLayers); + mNumberOfChipsPerLayer.resize(mNumberOfLayers); + mLastChipIndex.resize(mNumberOfLayers); + int numberOfChips = 0; + + for (int i = 0; i < mNumberOfLayers; i++) { + mNumberOfStaves[i] = extractNumberOfStaves(i); + mNumberOfHalfStaves[i] = extractNumberOfHalfStaves(i); + mNumberOfModules[i] = extractNumberOfModules(i); + mNumberOfChipsPerModule[i] = extractNumberOfChipsPerModule(i, mNumberOfChipRowsPerModule[i]); + mNumberOfChipsPerHalfStave[i] = mNumberOfChipsPerModule[i] * Max(1, mNumberOfModules[i]); + mNumberOfChipsPerStave[i] = mNumberOfChipsPerHalfStave[i] * Max(1, mNumberOfHalfStaves[i]); + mNumberOfChipsPerLayer[i] = mNumberOfChipsPerStave[i] * mNumberOfStaves[i]; + numberOfChips += mNumberOfChipsPerLayer[i]; + mLastChipIndex[i] = numberOfChips - 1; + } + setSize(numberOfChips); + fillTrackingFramesCache(); + // + fillMatrixCache(loadTrans); +} + +//__________________________________________________________________________ +void GeometryTGeo::fillMatrixCache(int mask) +{ + // populate matrix cache for requested transformations + // + if (mSize < 1) { + LOG(WARNING) << "The method Build was not called yet"; + Build(mask); + return; + } + + // 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"; + auto& cacheL2G = getCacheL2G(); + cacheL2G.setSize(mSize); + + for (int i = 0; i < mSize; i++) { + TGeoHMatrix* hm = extractMatrixSensor(i); + cacheL2G.setMatrix(Mat3D(*hm), i); + } + } + + 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"; + auto& cacheT2L = getCacheT2L(); + cacheT2L.setSize(mSize); + for (int i = 0; i < mSize; i++) { + TGeoHMatrix& hm = createT2LMatrix(i); + cacheT2L.setMatrix(Mat3D(hm), i); + } + } + + 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"; + auto& cacheT2Gr = getCacheT2GRot(); + cacheT2Gr.setSize(mSize); + for (int i = 0; i < mSize; i++) { + cacheT2Gr.setMatrix(Rot2D(getSensorRefAlpha(i)), i); + } + } +} + +//__________________________________________________________________________ +void GeometryTGeo::fillTrackingFramesCache() +{ + // fill for every sensor its tracking frame parameteres + if (!isTrackingFrameCached()) { + // special cache for sensors tracking frame X and alpha params + mCacheRefX.resize(mSize); + mCacheRefAlpha.resize(mSize); + for (int i = 0; i < mSize; i++) { + extractSensorXAlpha(i, mCacheRefX[i], mCacheRefAlpha[i]); + } + } +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfLayers() +{ + // int numberOfLayers = 0; + + // TGeoVolume* itsV = gGeoManager->GetVolume(getITSVolPattern()); + // if (!itsV) { + // LOG(FATAL) << "ITS 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); + // const char* name = nd->GetName(); + + // if (strstr(name, getITSLayerPattern())) { + // numberOfLayers++; + // if ((lrID = extractVolumeCopy(name, GeometryTGeo::getITSLayerPattern())) < 0) { + // LOG(FATAL) << "Failed to extract layer ID from the " << name; + // exit(1); + // } + + // mLayerToWrapper[lrID] = -1; // not wrapped + // } else if (strstr(name, getITSWrapVolPattern())) { // 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())) { + // if ((lrID = extractVolumeCopy(ndW->GetName(), GeometryTGeo::getITSLayerPattern())) < 0) { + // LOG(FATAL) << "Failed to extract layer ID from the " << name; + // exit(1); + // } + // numberOfLayers++; + // mLayerToWrapper[lrID] = wrID; + // } + // } + // } + // } + // return numberOfLayers; + return 10; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfStaves(int lay) const +{ + int numberOfStaves = 0; + char laynam[30]; + snprintf(laynam, 30, "%s%d", getITSLayerPattern(), lay); + TGeoVolume* volLr = gGeoManager->GetVolume(laynam); + if (!volLr) { + LOG(FATAL) << "can't find " << laynam << " volume"; + return -1; + } + + // Loop on all layer nodes, count Stave volumes by checking names + int nNodes = volLr->GetNodes()->GetEntries(); + for (int j = 0; j < nNodes; j++) { + // LOG(INFO) << "L" << lay << " " << j << " of " << nNodes << " " + // << volLr->GetNodes()->At(j)->GetName() << " " + // << getITSStavePattern() << " -> " << numberOfStaves; + if (strstr(volLr->GetNodes()->At(j)->GetName(), getITSStavePattern())) { + numberOfStaves++; + } + } + return numberOfStaves; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfHalfStaves(int lay) const +{ + if (sHalfStaveName.empty()) { + return 0; // for the setup w/o substave defined the stave and the substave is the same thing + } + int nSS = 0; + char stavnam[30]; + snprintf(stavnam, 30, "%s%d", getITSStavePattern(), lay); + TGeoVolume* volLd = gGeoManager->GetVolume(stavnam); + if (!volLd) { + 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(), getITSHalfStavePattern())) { + nSS++; + } + } + return nSS; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfModules(int lay) const +{ + if (sModuleName.empty()) { + return 0; + } + + char stavnam[30]; + TGeoVolume* volLd = nullptr; + + if (!sHalfStaveName.empty()) { + snprintf(stavnam, 30, "%s%d", getITSHalfStavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { // no substaves, check staves + snprintf(stavnam, 30, "%s%d", getITSStavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { + return 0; + } + + int nMod = 0; + + // Loop on all substave nodes, count module volumes by checking names + int nNodes = volLd->GetNodes()->GetEntries(); + + for (int j = 0; j < nNodes; j++) { + if (strstr(volLd->GetNodes()->At(j)->GetName(), getITSModulePattern())) { + nMod++; + } + } + return nMod; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const +{ + int numberOfChips = 0; + char stavnam[30]; + TGeoVolume* volLd = nullptr; + + if (!sModuleName.empty()) { + 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", getITSHalfStavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + } + if (!volLd) { // no substaves on this layer, check staves + snprintf(stavnam, 30, "%s%d", getITSStavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { + LOG(FATAL) << "can't find volume containing chips on layer " << lay; + } + + // Loop on all stave nodes, count Chip volumes by checking names + int nNodes = volLd->GetNodes()->GetEntries(); + + double xmin = 1e9, xmax = -1e9, zmin = 1e9, zmax = -1e9; + double lab[3], loc[3] = {0, 0, 0}; + double dx = -1, dz = -1; + + for (int j = 0; j < nNodes; j++) { + // AliInfo(Form("L%d %d of %d %s %s -> + // %d",lay,j,nNodes,volLd->GetNodes()->At(j)->GetName(),GetITSChipPattern(),numberOfChips)); + TGeoNodeMatrix* node = (TGeoNodeMatrix*)volLd->GetNodes()->At(j); + if (!strstr(node->GetName(), getITSChipPattern())) { + continue; + } + node->LocalToMaster(loc, lab); + if (lab[0] > xmax) { + xmax = lab[0]; + } + if (lab[0] < xmin) { + xmin = lab[0]; + } + if (lab[2] > zmax) { + zmax = lab[2]; + } + if (lab[2] < zmin) { + zmin = lab[2]; + } + + numberOfChips++; + + if (dx < 0) { + TGeoShape* chShape = node->GetVolume()->GetShape(); + TGeoBBox* bbox = dynamic_cast<TGeoBBox*>(chShape); + if (!bbox) { + LOG(FATAL) << "Chip " << node->GetName() << " volume is of unprocessed shape " << chShape->IsA()->GetName(); + } else { + dx = 2 * bbox->GetDX(); + dz = 2 * bbox->GetDZ(); + } + } + } + + double spanX = xmax - xmin; + double spanZ = zmax - zmin; + nrow = TMath::Nint(spanX / dx + 1); + int ncol = TMath::Nint(spanZ / dz + 1); + 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; + } + return numberOfChips; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractLayerChipType(int lay) const +{ + char stavnam[30]; + snprintf(stavnam, 30, "%s%d", getITSLayerPattern(), lay); + TGeoVolume* volLd = gGeoManager->GetVolume(stavnam); + if (!volLd) { + LOG(FATAL) << "can't find volume " << stavnam; + return -1; + } + return volLd->GetUniqueID(); +} + +//__________________________________________________________________________ +void GeometryTGeo::Print(Option_t*) const +{ + printf("NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); + if (!isBuilt()) { + return; + } + + 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]); + } +} + +//__________________________________________________________________________ +void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) +{ + // calculate r and phi of the impact of the normal on the sensor + // (i.e. phi of the tracking frame alpha and X of the sensor in this frame) + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + const TGeoHMatrix* matL2G = extractMatrixSensor(isn); + + 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); + o2::math_utils::bringTo02Pi(alp); +} + +//__________________________________________________________________________ +TGeoHMatrix& GeometryTGeo::createT2LMatrix(int isn) +{ + // create for sensor isn the TGeo matrix for Tracking to Local frame transformations + static TGeoHMatrix t2l; + float x = 0.f, alp = 0.f; + extractSensorXAlpha(isn, x, alp); + t2l.Clear(); + t2l.RotateZ(alp * RadToDeg()); // rotate in direction of normal to the sensor plane + const TGeoHMatrix* matL2G = extractMatrixSensor(isn); + const TGeoHMatrix& matL2Gi = matL2G->Inverse(); + t2l.MultiplyLeft(&matL2Gi); + return t2l; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractVolumeCopy(const char* name, const char* prefix) const +{ + TString nms = name; + if (!nms.BeginsWith(prefix)) { + return -1; + } + nms.Remove(0, strlen(prefix)); + if (!isdigit(nms.Data()[0])) { + return -1; + } + return nms.Atoi(); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/MisalignmentParameter.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/MisalignmentParameter.cxx new file mode 100644 index 0000000000000..ba16a754938c9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/MisalignmentParameter.cxx @@ -0,0 +1,92 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 MisalignmentParameter.cxx +/// \brief Implementation of the MisalignmentParameter class + +#include "TRKBase/MisalignmentParameter.h" + +#include "FairParamList.h" + +using namespace o2::trk; + +ClassImp(o2::trk::MisalignmentParameter); + +MisalignmentParameter::MisalignmentParameter(const char* name, const char* title, const char* context) + : FairParGenericSet(name, title, context), + mShiftX(), + mShiftY(), + mShiftZ(), + mRotX(), + mRotY(), + mRotZ(), + mNumberOfDetectors(0) +{ +} + +MisalignmentParameter::~MisalignmentParameter() = default; +void MisalignmentParameter::Clear() {} +void MisalignmentParameter::putParams(FairParamList* list) +{ + if (!list) { + return; + } + + list->add("NumberOfDetectors", mNumberOfDetectors); + list->add("ShiftX", mShiftX); + list->add("ShiftY", mShiftY); + list->add("ShiftZ", mShiftZ); + list->add("RotationX", mRotX); + list->add("RotationY", mRotY); + list->add("RotationZ", mRotZ); +} + +Bool_t MisalignmentParameter::getParams(FairParamList* list) +{ + if (!list) { + return kFALSE; + } + + if (!list->fill("NumberOfDetectors", &mNumberOfDetectors)) { + return kFALSE; + } + + mShiftX.Set(mNumberOfDetectors); + if (!list->fill("ShiftX", &mShiftX)) { + return kFALSE; + } + + mShiftY.Set(mNumberOfDetectors); + if (!list->fill("ShiftY", &mShiftY)) { + return kFALSE; + } + + mShiftZ.Set(mNumberOfDetectors); + if (!list->fill("ShiftZ", &mShiftZ)) { + return kFALSE; + } + + mRotX.Set(mNumberOfDetectors); + if (!list->fill("RotationX", &mRotX)) { + return kFALSE; + } + + mRotY.Set(mNumberOfDetectors); + if (!list->fill("RotationY", &mRotY)) { + return kFALSE; + } + + mRotZ.Set(mNumberOfDetectors); + if (!list->fill("RotationZ", &mRotZ)) { + return kFALSE; + } + + return kTRUE; +} diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h new file mode 100644 index 0000000000000..c773fbe3e13bb --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h @@ -0,0 +1,20 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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::trk::GeometryTGeo; +#pragma link C++ class o2::trk::MisalignmentParameter + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt new file mode 100644 index 0000000000000..e2058e7d098e1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt @@ -0,0 +1,11 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(test) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt new file mode 100644 index 0000000000000..10bb6dd71f256 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -0,0 +1,9 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt new file mode 100644 index 0000000000000..9b99ee26b445c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -0,0 +1,25 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(TRKSimulation + SOURCES src/V11Geometry.cxx src/V1Layer.cxx src/V3Layer.cxx + src/Detector.cxx src/V3Services.cxx + PUBLIC_LINK_LIBRARIES O2::TRKBase O2::ITSMFTSimulation + ROOT::Physics) + +o2_target_root_dictionary(TRKSimulation + HEADERS include/TRKSimulation/Detector.h + include/TRKSimulation/V1Layer.h + include/TRKSimulation/V3Layer.h + include/TRKSimulation/V11Geometry.h + include/TRKSimulation/V3Services.h + ) + +o2_data_file(COPY data DESTINATION Detectors/TRK/simulation) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/data/simcuts.dat b/Detectors/Upgrades/ALICE3/TRK/simulation/data/simcuts.dat new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h new file mode 100644 index 0000000000000..e7398594b1ed8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -0,0 +1,382 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Detector.h +/// \brief Definition of the Detector class + +#ifndef ALICEO2_TRK_DETECTOR_H_ +#define ALICEO2_TRK_DETECTOR_H_ + +#include <vector> // for vector +#include "DetectorsBase/GeometryManager.h" // for getSensID +#include "DetectorsBase/Detector.h" // for Detector +#include "DetectorsCommonDataFormats/DetID.h" // for Detector +#include "ITSMFTSimulation/Hit.h" // for Hit +#include "Rtypes.h" // for Int_t, Double_t, Float_t, Bool_t, etc +#include "TArrayD.h" // for TArrayD +#include "TGeoManager.h" // for gGeoManager, TGeoManager (ptr only) +#include "TLorentzVector.h" // for TLorentzVector +#include "TVector3.h" // for TVector3 + +class FairVolume; +class TGeoVolume; + +class TParticle; + +class TString; + +namespace o2 +{ +namespace itsmft +{ +class Hit; +} +} // namespace o2 + +namespace o2 +{ +namespace trk +{ +class GeometryTGeo; +} +} // namespace o2 +namespace o2 +{ +namespace trk +{ +class V3Layer; +} +} // namespace o2 + +namespace o2 +{ +namespace trk +{ +class V3Layer; +class V3Services; + +class Detector : public o2::base::DetImpl<Detector> +{ + public: + enum Model { + kIBModelDummy = 0, + kIBModel0 = 1, + kIBModel1 = 2, + kIBModel21 = 3, + kIBModel22 = 4, + kIBModel3 = 5, + kIBModel4 = 10, + kOBModelDummy = 6, + kOBModel0 = 7, + kOBModel1 = 8, + kOBModel2 = 9 + }; + + static constexpr Int_t sNumberLayers = 10; ///< Number of layers in ITSU + static constexpr Int_t sNumberInnerLayers = 10; ///< Number of inner layers in ITSU + static constexpr Int_t sNumberOfWrapperVolumes = 3; ///< Number of wrapper volumes + + /// Name : Detector Name + /// Active: kTRUE for active detectors (ProcessHits() will be called) + /// kFALSE for inactive detectors + Detector(Bool_t active); + + /// Special version for TRK: add number on Inner Layers + Detector(Bool_t active, Int_t nlay); + + /// Default constructor + Detector(); + + /// Default destructor + ~Detector() override; + + /// Initialization of the detector is done here + void InitializeO2Detector() override; + + /// This method is called for each step during simulation (see FairMCApplication::Stepping()) + Bool_t ProcessHits(FairVolume* v = nullptr) override; + + /// Registers the produced collections in FAIRRootManager + void Register() override; + + /// Gets the produced collections + std::vector<o2::itsmft::Hit>* getHits(Int_t iColl) const + { + if (iColl == 0) { + return mHits; + } + return nullptr; + } + + public: + /// Has to be called after each event to reset the containers + void Reset() override; + + /// Base class to create the detector geometry + void ConstructGeometry() override; + + /// Creates the Service Barrel (as a simple cylinder) for IB and OB + /// \param innerBarrel if true, build IB service barrel, otherwise for OB + /// \param dest the mother volume holding the service barrel + /// \param mgr the gGeoManager pointer (used to get the material) + void createServiceBarrel(const Bool_t innerBarrel, TGeoVolume* dest, const TGeoManager* mgr = gGeoManager); + + /// Sets the layer parameters + /// \param nlay layer number + /// \param phi0 layer phi0 + /// \param r layer radius + /// \param nstav number of staves + /// \param nunit IB: number of chips per stave + /// \param OB: number of modules per half stave + /// \param lthick stave thickness (if omitted, defaults to 0) + /// \param dthick detector thickness (if omitted, defaults to 0) + /// \param dettypeID ?? + /// \param buildLevel (if 0, all geometry is build, used for material budget studies) + void defineLayer(Int_t nlay, Double_t phi0, Double_t r, Int_t nladd, Int_t nmod, Double_t lthick = 0., + Double_t dthick = 0., UInt_t detType = 0, Int_t buildFlag = 0) override; + + /// Sets the layer parameters for a "turbo" layer + /// (i.e. a layer whose staves overlap in phi) + /// \param nlay layer number + /// \param phi0 phi of 1st stave + /// \param r layer radius + /// \param nstav number of staves + /// \param nunit IB: number of chips per stave + /// \param OB: number of modules per half stave + /// \param width stave width + /// \param tilt layer tilt angle (degrees) + /// \param lthick stave thickness (if omitted, defaults to 0) + /// \param dthick detector thickness (if omitted, defaults to 0) + /// \param dettypeID ?? + /// \param buildLevel (if 0, all geometry is build, used for material budget studies) + // void defineLayerTurbo(Int_t nlay, Double_t phi0, Double_t r, Int_t nladd, Int_t nmod, Double_t width, Double_t tilt, + // Double_t lthick = 0., Double_t dthick = 0., UInt_t detType = 0, Int_t buildFlag = 0) override; + + /// Sets the layer parameters for new TRK geo + /// \param nlay layer number + /// \param r layer radius + /// \param zlen layer length + /// \param dthick detector thickness (if omitted, defaults to 0) + /// \param dettypeID ?? + /// \param buildLevel (if 0, all geometry is build, used for material budget studies) + void defineInnerLayerTRK(Int_t nlay, Double_t r, Double_t zlen, + Double_t dthick = 0., UInt_t detType = 0, Int_t buildFlag = 0); + + /// Gets the layer parameters + /// \param nlay layer number + /// \param phi0 phi of 1st stave + /// \param r layer radius + /// \param nstav number of staves + /// \param nmod IB: number of chips per stave + /// \param OB: number of modules per half stave + /// \param width stave width + /// \param tilt stave tilt angle + /// \param lthick stave thickness + /// \param dthick detector thickness + /// \param dettype detector type + virtual void getLayerParameters(Int_t nlay, Double_t& phi0, Double_t& r, Int_t& nladd, Int_t& nmod, Double_t& width, + Double_t& tilt, Double_t& lthick, Double_t& mthick, UInt_t& dettype) const; + + /// This method is an example of how to add your own point of type Hit to the clones array + o2::itsmft::Hit* 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); + + /// Set per wrapper volume parameters + void defineWrapperVolume(Int_t id, Double_t rmin, Double_t rmax, Double_t zspan) override; + + /// Add alignable top volumes + void addAlignableVolumes() const override; + + /// Add alignable Layer volumes + /// \param lr layer number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesLayer(Int_t lr, TString& parent, Int_t& lastUID) const; + + /// Add alignable Stave volumes + /// \param lr layer number + /// \param st stave number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesStave(Int_t lr, Int_t st, TString& parent, Int_t& lastUID) const; + + /// Add alignable HalfStave volumes + /// \param lr layer number + /// \param st stave number + /// \param hst half stave number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesHalfStave(Int_t lr, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const; + + /// Add alignable Module volumes + /// \param lr layer number + /// \param st stave number + /// \param hst half stave number + /// \param md module number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesModule(Int_t lr, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const; + + /// Add alignable Chip volumes + /// \param lr layer number + /// \param st stave number + /// \param hst half stave number + /// \param md module number + /// \param ch chip number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesChip(Int_t lr, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, + Int_t& lastUID) const; + + /// Return Chip Volume UID + /// \param id volume id + Int_t chipVolUID(Int_t id) const { return o2::base::GeometryManager::getSensID(o2::detectors::DetID::TRK, id); } + + void EndOfEvent() override; + + void FinishPrimary() override { ; } + virtual void finishRun() { ; } + void BeginPrimary() override { ; } + void PostTrack() override { ; } + void PreTrack() override { ; } + /// Prints out the content of this class in ASCII format + /// \param ostream *os The output stream + void Print(std::ostream* os) const; + + /// Reads in the content of this class in the format of Print + /// \param istream *is The input stream + void Read(std::istream* is); + + /// Returns the number of layers + Int_t getNumberOfLayers() const { return sNumberLayers; } + virtual void setStaveModelIB(Model model) { mStaveModelInnerBarrel = model; } + virtual void setStaveModelOB(Model model) { mStaveModelOuterBarrel = model; } + virtual Model getStaveModelIB() const { return mStaveModelInnerBarrel; } + virtual Model getStaveModelOB() const { return mStaveModelOuterBarrel; } + + void createOuterBarrel(const Bool_t ob) { mCreateOuterBarrel = ob; }; + Bool_t isCreateOuterBarrel() { return mCreateOuterBarrel; }; + + o2::trk::GeometryTGeo* mGeometryTGeo; //! access to geometry details + + protected: + Int_t* mLayerID; //! [sNumberLayers] layer identifier + TString* mLayerName; //! [sNumberLayers] layer identifier + + private: + /// this is transient data about track passing the sensor + struct TrackData { // this is transient + bool mHitStarted; //! hit creation started + unsigned char mTrkStatusStart; //! track status flag + TLorentzVector mPositionStart; //! position at entrance + TLorentzVector mMomentumStart; //! momentum + double mEnergyLoss; //! energy loss + } mTrackData; //! + + Int_t mNumberOfInnerLayers; //! number of TRK inner layers + Int_t mTotalNumberOfLayers; //! total number of TRK layers (IB+OB) + Bool_t mCreateOuterBarrel; //! true to create the Outer Barrel + + Int_t mNumberOfDetectors; + + Bool_t mModifyGeometry; + + Double_t mWrapperMinRadius[sNumberOfWrapperVolumes]; //! Min radius of wrapper volume + Double_t mWrapperMaxRadius[sNumberOfWrapperVolumes]; //! Max radius of wrapper volume + Double_t mWrapperZSpan[sNumberOfWrapperVolumes]; //! Z span of wrapper volume + Int_t* mWrapperLayerId; //! Id of wrapper layer to which layer belongs (-1 if not wrapped) + + Bool_t* mTurboLayer; //! True for "turbo" layers + Bool_t* mTRKLayer; //! True for new TRK layers + Double_t* mLayerPhi0; //! Vector of layer's 1st stave phi in lab + Double_t* mLayerRadii; //! Vector of layer radii + Double_t* mLayerZLen; //! Vector of layer lengths + Int_t* mStavePerLayer; //! Vector of number of staves per layer + Int_t* mUnitPerStave; //! Vector of number of "units" per stave + Double_t* mChipThickness; //! Vector of chip thicknesses + Double_t* mStaveWidth; //! Vector of stave width (only used for turbo) + Double_t* mStaveTilt; //! Vector of stave tilt (only used for turbo) + Double_t* mDetectorThickness; //! Vector of detector thicknesses + UInt_t* mChipTypeID; //! Vector of detector type id + Int_t* mBuildLevel; //! Vector of Material Budget Studies + + /// Container for hit data + std::vector<o2::itsmft::Hit>* mHits; + + /// We need this as a method to access members + void configITS(Detector* its); + + /// Creates all needed arrays + void createAllArrays(); + + /// Creates an air-filled wrapper cylindrical volume + TGeoVolume* createWrapperVolume(const Int_t nLay); + + /// Create the detector materials + virtual void createMaterials(); + + /// Construct the detector geometry + void constructDetectorGeometry(); + + /// Define the sensitive volumes of the geometry + void defineSensitiveVolumes(); + + /// Creates the Inner Barrel Services + /// \param motherVolume the TGeoVolume owing the volume structure + void createInnerBarrelServices(TGeoVolume* motherVolume); + + /// Creates the Middle Barrel Services + /// \param motherVolume the TGeoVolume owing the volume structure + void createMiddlBarrelServices(TGeoVolume* motherVolume); + + /// Creates the Outer Barrel Services + /// \param motherVolume the TGeoVolume owing the volume structure + void createOuterBarrelServices(TGeoVolume* motherVolume); + + /// Creates the Outer Barrel Supports + /// \param motherVolume the TGeoVolume owing the volume supports + void createOuterBarrelSupports(TGeoVolume* motherVolume); + + Detector(const Detector&); + + Detector& operator=(const Detector&); + + Model mStaveModelInnerBarrel; //! The stave model for the Inner Barrel + Model mStaveModelOuterBarrel; //! The stave model for the Outer Barrel + V3Layer** mGeometry; //! Geometry + V3Services* mServicesGeometry; //! Services Geometry + + template <typename Det> + friend class o2::base::DetImpl; + ClassDefOverride(Detector, 1); +}; + +// Input and output function for standard C++ input/output. +std::ostream& operator<<(std::ostream& os, Detector& source); + +std::istream& operator>>(std::istream& os, Detector& source); +} // namespace trk +} // namespace o2 + +#ifdef USESHM +namespace o2 +{ +namespace base +{ +template <> +struct UseShm<o2::trk::Detector> { + static constexpr bool value = true; +}; +} // namespace base +} // namespace o2 +#endif + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V11Geometry.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V11Geometry.h new file mode 100644 index 0000000000000..3e73c519cd9c8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V11Geometry.h @@ -0,0 +1,458 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V11Geometry.h +/// \brief Definition of the V11Geometry class + +#ifndef ALICEO2_TRK_V11GEOMETRY_H_ +#define ALICEO2_TRK_V11GEOMETRY_H_ + +#include <TMath.h> // for DegToRad, Cos, Sin, Tan +#include <TObject.h> // for TObject +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, V11Geometry::Class, etc + +class TGeoArb8; // lines 11-11 +class TGeoBBox; // lines 16-16 +class TGeoConeSeg; // lines 15-15 +class TGeoPcon; // lines 12-12 +class TGeoTube; // lines 13-13 +class TGeoTubeSeg; // lines 14-14 + +namespace o2 +{ +namespace trk +{ + +/// This class is a base class for the ITS geometry version 11. It contains common/standard +/// functions used in many places in defining the ITS geometry, version 11. Large posions of +/// the ITS geometry, version 11, should be derived from this class so as to make maximum +/// use of these common functions. This class also defines the proper conversion values such, +/// to cm and degrees, such that the most useful units, those used in the Engineering drawings, +/// can be used. +class V11Geometry : public TObject +{ + + public: + V11Geometry() : mDebug(){}; + + V11Geometry(Int_t debug) : mDebug(debug){}; + + ~V11Geometry() + override = default; + + /// Sets the debug flag for debugging output + void setDebug(Int_t level = 5) + { + mDebug = level; + } + + /// Clears the debug flag so no debugging output will be generated + void setNoDebug() + { + mDebug = 0; + } + + /// Returns the debug flag value + Bool_t getDebug(Int_t level = 1) const + { + return mDebug >= level; + } + + // Static functions + + /// Define Trig functions for use with degrees (standerd TGeo angles). + /// Sine function + Double_t sinD(Double_t deg) const + { + return TMath::Sin(deg * TMath::DegToRad()); + } + + /// Cosine function + Double_t cosD(Double_t deg) const + { + return TMath::Cos(deg * TMath::DegToRad()); + } + + /// Tangent function + Double_t tanD(Double_t deg) const + { + return TMath::Tan(deg * TMath::DegToRad()); + } + + /// Determine the intersection of two lines + /// Given the two lines, one passing by (x0,y0) with slope m and + /// the other passing by (x1,y1) with slope n, returns the coordinates + /// of the intersecting point (xi,yi) + /// \param Double_t m The slope of the first line + /// \param Double_t x0,y0 The x and y coord. of the first point + /// \param Double_t n The slope of the second line + /// \param Double_t x1,y1 The x and y coord. of the second point + /// As an output it gives the coordinates xi and yi of the intersection point + void intersectLines(Double_t m, Double_t x0, Double_t y0, Double_t n, Double_t x1, Double_t y1, Double_t& xi, + Double_t& yi) const; + + /// Determine the intersection of a line and a circle + /// Given a line passing by (x0,y0) with slope m and a circle with + /// radius rr and center (xc,yc), returns the coordinates of the + /// intersecting points (xi1,yi1) and (xi2,yi2) (xi1 > xi2) + /// \param Double_t m The slope of the line + /// \param Double_t x0,y0 The x and y coord. of the point + /// \param Double_t rr The radius of the circle + /// \param Double_t xc,yc The x and y coord. of the center of circle + /// As an output it gives the coordinates xi and yi of the intersection points + /// Returns kFALSE if the line does not intercept the circle, otherwise kTRUE + static Bool_t intersectCircle(Double_t m, Double_t x0, Double_t y0, Double_t rr, Double_t xc, Double_t yc, + Double_t& xi1, Double_t& yi1, Double_t& xi2, Double_t& yi2); + + /// Given the two points (x0,y0) and (x1,y1) and the location x, returns + /// the value y corresponding to that point x on the line defined by the + /// two points. Returns the value y corresponding to the point x on the line defined by + /// the two points (x0,y0) and (x1,y1). + /// \param Double_t x0 The first x value defining the line + /// \param Double_t y0 The first y value defining the line + /// \param Double_t x1 The second x value defining the line + /// \param Double_t y1 The second y value defining the line + /// \param Double_t x The x value for which the y value is wanted. + Double_t yFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x) const; + + /// Given the two points (x0,y0) and (x1,y1) and the location y, returns + /// the value x corresponding to that point y on the line defined by the + /// two points. Returns the value x corresponding to the point y on the line defined by + /// the two points (x0,y0) and (x1,y1). + /// \param Double_t x0 The first x value defining the line + /// \param Double_t y0 The first y value defining the line + /// \param Double_t x1 The second x value defining the line + /// \param Double_t y1 The second y value defining the line + /// \param Double_t y The y value for which the x value is wanted. + Double_t xFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t y) const; + + /// Functions require at parts of Volume A to be already defined. + /// Returns the value of Rmax corresponding to point z alone the line + /// defined by the two points p.Rmax(i1),p-GetZ(i1) and p->GetRmax(i2), + /// p->GetZ(i2). + /// \param TGeoPcon *p The Polycone where the two points come from + /// \param Int_t i1 Point 1 + /// \param Int_t i2 Point 2 + /// \param Double_t z The value of z for which Rmax is to be found + /// \param Double_t Rmx the value corresponding to z + Double_t rMaxFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const; + + /// Returns the value of Rmin corresponding to point z alone the line + /// defined by the two points p->GetRmin(i1),p->GetZ(i1) and + /// p->GetRmin(i2), p->GetZ(i2). + /// \param TGeoPcon *p The Polycone where the two points come from + /// \param Int_t i1 Point 1 + /// \param Int_t i2 Point 2 + /// \param Double_t z The value of z for which Rmax is to be found + /// \param Double_t Rmx the value corresponding to z + Double_t rMinFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const; + + /// Returns the value of Rmin corresponding to point z alone the line + /// defined by the two points p->GetRmin(i1),p->GetZ(i1) and + /// p->GetRmin(i2), p->GetZ(i2). Returns the value r corresponding to z and the + /// line defined by the two points + /// \param Double_t az Array of z values + /// \param Double_t r Array of r values + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t z Value z at which r is to be found + Double_t rFrom2Points(const Double_t* ar, const Double_t* az, Int_t i1, Int_t i2, Double_t z) const; + + /// Returns the value of Z corresponding to point R alone the line + /// defined by the two points p->GetRmin(i1),p->GetZ(i1) and + /// p->GetRmin(i2),p->GetZ(i2). Returns the value z corresponding to r min + /// and the line defined by the two points + /// \param TGeoPcon *p The Poly cone where the two points come from. + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t r Value r min at which z is to be found + Double_t zFrom2MinPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const; + + /// Returns the value of Z corresponding to point R alone the line + /// defined by the two points p->GetRmax(i1),p->GetZ(i1) and + /// p->GetRmax(i2),p->GetZ(i2). Returns the value z corresponding to + /// r max and the line defined by the two points + /// \param TGeoPcon *p The Poly cone where the two points come from. + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t r Value r max at which z is to be found + Double_t zFrom2MaxPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const; + + /// Returns the value of z corresponding to point R alone the line + /// defined by the two points p->GetRmax(i1),p->GetZ(i1) and + /// p->GetRmax(i2),p->GetZ(i2). Returns the value z corresponding to r + /// and the line defined by the two points + /// \param Double_t z Array of z values + /// \param Double_t ar Array of r values + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t r Value r at which z is to be found + Double_t zFrom2Points(const Double_t* az, const Double_t* ar, Int_t i1, Int_t i2, Double_t r) const; + + /// General Outer Cone surface equation Rmax + /// Given 1 point from a TGeoPcon(z and Rmax) the angle tc returns r for + /// a given z, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns the value Rmax corresponding to the line at angle th, offset by + /// th, and the point p->GetZ/Rmin[ip] at the location z. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t z The value of z to compute Rmax from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t rMaxFromZpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t z, Double_t th = 0.0) const; + + // General Cone surface equation R(z). Returns the value R correstponding to the line at + // angle th, offset by th, and the point p->GetZ/Rmax[ip] at the location z. + // \param Double_t ar The array of R values + // \param Double_t az The array of Z values + // \param Int_t ip The index in p to get the point location + // \param Double_t tc The angle of that part of the cone is at + // \param Double_t z The value of z to compute R from + // \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t rFromZpCone(const Double_t* ar, const Double_t* az, int ip, Double_t tc, Double_t z, + Double_t th = 0.0) const; + + /// General Inner Cone surface equation Rmin. + /// Given 1 point from a TGeoPcon(z and Rmin) the angle tc returns r for + /// a given z, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns the value Rmin correstponding to the line at angle th, + /// offset by th, and the point p->GetZ/Rmin[ip] at the location z. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t z The value of z to compute Rmin from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t rMinFromZpCone(const TGeoPcon* p, Int_t ip, Double_t tc, Double_t z, Double_t th = 0.0) const; + + /// General Outer cone Surface equation for z. + /// Given 1 point from a TGeoPcon(z and Rmax) the angle tc returns z for + /// a given Rmax, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns thevalue Z correstponding to the line at angle th, + /// offset by th, and the point p->GetZ/Rmax[ip] at the location r. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t r The value of Rmax to compute z from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t zFromRMaxpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, Double_t th = 0.0) const; + + /// General Outer cone Surface equation for z. + /// Returns the value Z correstponding to the line at angle th, offeset by + /// th, and the point p->GetZ/Rmax[ip] at the locatin r. + /// \param Double_t ar The array of R values + /// \param Double_t az The array of Z values + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t r The value of Rmax to compute z from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t zFromRMaxpCone(const Double_t* ar, const Double_t* az, Int_t ip, Double_t tc, Double_t r, + Double_t th = 0.0) const; + + /// General Inner cone Surface equation for z. + /// Given 1 point from a TGeoPcon(z and Rmin) the angle tc returns z for + /// a given Rmin, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns the value Z correstponding to the line at angle th, offeset by + /// th, and the point p->GetZ/Rmin[ip] at the location r. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t r The value of Rmin to compute z from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t zFromRMinpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, Double_t th = 0.0) const; + + /// Given two lines defined by the points i1, i2,i3 in the TGeoPcon + /// class p that intersect at point p->GetZ(i2) return the point z,r + /// that is Cthick away in the TGeoPcon class q. If points i1=i2 + /// and max == kTRUE, then p->GetRmin(i1) and p->GetRmax(i2) are used. + /// if points i2=i3 and max=kTRUE then points p->GetRmax(i2) and + /// p->GetRmin(i3) are used. If i2=i3 and max=kFALSE, then p->GetRmin(i2) + /// and p->GetRmax(i3) are used. + /// \param TGeoPcon *p Class where points i1, i2, and i3 are taken from + /// \param Int_t i1 First point in class p + /// \param Int_t i2 Second point in class p + /// \param Int_t i3 Third point in class p + /// \param Double_t c Distance inside the outer/inner surface that the point j1 + /// is to be computed for + /// \param TGeoPcon *q Pointer to class for results to be put into. + /// \param Int_t j1 Point in class q where data is to be stored. + /// \param Bool_t ma if kTRUE, then a Rmax value is computed, else a Rmin valule is computed + /// \param TGeoPcon *q Pointer to class for results to be put into. + void insidePoint(const TGeoPcon* p, Int_t i1, Int_t i2, Int_t i3, Double_t Cthick, TGeoPcon* q, Int_t j1, + Bool_t max) const; + + /// Given two intersecting lines defined by the points (x0,y0), (x1,y1) and + /// (x1,y1), (x2,y2) {intersecting at (x1,y1)} the point (x,y) a distance + /// c away is returned such that two lines a distance c away from the + /// lines defined above intersect at (x,y). + /// \param Double_t x0 X point on the first intersecting sets of lines + /// \param Double_t y0 Y point on the first intersecting sets of lines + /// \param Double_t x1 X point on the first/second intersecting sets of lines + /// \param Double_t y1 Y point on the first/second intersecting sets of lines + /// \param Double_t x2 X point on the second intersecting sets of lines + /// \param Double_t y2 Y point on the second intersecting sets of lines + /// \param Double_t c Distance the two sets of lines are from each other + /// \param Double_t x X point for the intersecting sets of parellel lines + /// \param Double_t y Y point for the intersecting sets of parellel lines + void insidePoint(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t c, + Double_t& x, Double_t& y) const; + + /// Given an initial point z0,r0, the initial angle theta0, and the radius + /// of curvature, returns the point z1, r1 at the angle theta1. Theta + /// measured from the r axis in the clock wise direction [degrees]. + /// \param Double_t rc The radius of curvature + /// \param Double_t theta0 The starting angle (degrees) + /// \param Double_t z0 The value of z at theta0 + /// \param Double_t r0 The value of r at theta0 + /// \param Double_t theta1 The ending angle (degrees) + /// \param Double_t &z1 The value of z at theta1 + /// \param Double_t &r1 The value of r at theta1 + void radiusOfCurvature(Double_t rc, Double_t theta0, Double_t z0, Double_t r0, Double_t theta1, Double_t& z1, + Double_t& r1) const; + + // Output functions for debugging + + /// Prints out the content of the TGeoArb8 + /// \param TGeoArb8 *a + void printArb8(const TGeoArb8* a) const; + + /// Prints out the contents of the TGeoPcon + /// \param TGeoPcon *a + void printPcon(const TGeoPcon* a) const; + + /// Prints out the contents of the TGeoTube + /// \param TGeoTube *a + void printTube(const TGeoTube* a) const; + + /// Prints out the contents of the TGeoTubeSeg + /// \param TGeoTubeSeg *a + void printTubeSeg(const TGeoTubeSeg* a) const; + + /// Prints out the contents of the TGeoConeSeg + /// \param TGeoConeSeg *a + void printConeSeg(const TGeoConeSeg* a) const; + + /// Prints out the contents of the TGeoBBox + /// \param TGeoBBox *a + void printBBox(const TGeoBBox* a) const; + + /// Draws a cross sectional view of the TGeoPcon, Primarily for debugging. + /// A TCanvas should exist first. + /// \param TGeoPcon *p The TGeoPcon to be "drawn" + /// \param Int_t fillc The fill color to be used + /// \param Int_t fills The fill style to be used + /// \param Int_t linec The line color to be used + /// \param Int_t lines The line style to be used + /// \param Int_t linew The line width to be used + /// \param Int_t markc The markder color to be used + /// \param Int_t marks The markder style to be used + /// \param Float_t marksize The marker size + void drawCrossSection(const TGeoPcon* p, Int_t fillc = 7, Int_t fills = 4050, Int_t linec = 3, Int_t lines = 1, + Int_t linew = 4, Int_t markc = 2, Int_t marks = 4, Float_t marksize = 1.0) const; + + /// Computes the angles, t0 and t1 corresponding to the intersection of + /// the line, defined by {x0,y0} {x1,y1}, and the circle, defined by + /// its center {xc,yc} and radius r. If the line does not intersect the + /// line, function returns kFALSE, otherwise it returns kTRUE. If the + /// line is tangent to the circle, the angles t0 and t1 will be the same. + /// Returns kTRUE if line intersects circle or kFALSE if line does not intersect circle + /// or the line is not properly defined point {x0,y0} and {x1,y1} are the same point. + /// \param Double_t x0 X of first point defining the line + /// \param Double_t y0 Y of first point defining the line + /// \param Double_t x1 X of Second point defining the line + /// \param Double_t y1 Y of Second point defining the line + /// \param Double_t xc X of Circle center point defining the line + /// \param Double_t yc Y of Circle center point defining the line + /// \param Double_t r radius of circle + /// \param Double_t &t0 First angle where line intersects circle + /// \param Double_t &t1 Second angle where line intersects circle + Bool_t angleOfIntersectionWithLine(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t xc, Double_t yc, + Double_t rc, Double_t& t0, Double_t& t1) const; + + /// Function to compute the ending angle, for arc 0, and starting angle, + /// for arc 1, such that a straight line will connect them with no discontinuities. + /// Begin_Html + /* + <img src="picts/ITS/V11Geometry_AnglesForRoundedCorners.gif"> + */ + // End_Html + /// \param Double_t x0 X Coordinate of arc 0 center. + /// \param Double_t y0 Y Coordinate of arc 0 center. + /// \param Double_t r0 Radius of curvature of arc 0. For signe see figure. + /// \param Double_t x1 X Coordinate of arc 1 center. + /// \param Double_t y1 Y Coordinate of arc 1 center. + /// \param Double_t r1 Radius of curvature of arc 1. For signe see figure. + /// \param Double_t t0 Ending angle of arch 0, with respect to x axis, Degrees. + /// \param Double_t t1 Starting angle of arch 1, with respect to x axis, Degrees. + void anglesForRoundedCorners(Double_t x0, Double_t y0, Double_t r0, Double_t x1, Double_t y1, Double_t r1, + Double_t& t0, Double_t& t1) const; + + /// Define a general createMaterials function here so that if + /// any specific subdetector does not define it this null function + /// will due. This function is not declaired const so that a sub- + /// detector's version may use class variables if they wish. + /// Defined media here should correspond to the one defined in galice.cuts + /// File which is red in (AliMC*) fMCApp::Init() { ReadTransPar(); } + void createDefaultMaterials(); + + virtual void createMaterials(){}; + + /// Function to create the figure describing how the function + /// anglesForRoundedCorners works. + /// \param Double_t x0 X Coordinate of arc 0 center. + /// \param Double_t y0 Y Coordinate of arc 0 center. + /// \param Double_t r0 Radius of curvature of arc 0. For signe see figure. + /// \param Double_t x1 X Coordinate of arc 1 center. + /// \param Double_t y1 Y Coordinate of arc 1 center. + /// \param Double_t r1 Radius of curvature of arc 1. For signe see figure. + void makeFigure1(Double_t x0 = 0.0, Double_t y0 = 0.0, Double_t r0 = 2.0, Double_t x1 = -4.0, Double_t y1 = -2.0, + Double_t r1 = 1.0); + + protected: + // Units, Convert from k?? to cm,degree,GeV,seconds, + static const Double_t sMicron; ///< Convert micron to TGeom's cm. + static const Double_t sMm; ///< Convert mm to TGeom's cm. + static const Double_t sCm; ///< Convert cm to TGeom's cm. + static const Double_t sDegree; ///< Convert degrees to TGeom's degrees + static const Double_t sRadian; ///< To Radians + static const Double_t sGCm3; ///< Density in g/cm^3 + static const Double_t sKgm3; ///< Density in kg/m^3 + static const Double_t sKgdm3; ///< Density in kg/dm^3 + static const Double_t sCelsius; ///< Temperature in degrees Celcius + static const Double_t sPascal; ///< Preasure in Pascal + static const Double_t sKPascal; ///< Preasure in KPascal + static const Double_t sEV; ///< Energy in eV + static const Double_t sKEV; ///< Energy in KeV + static const Double_t sMEV; ///< Energy in MeV + static const Double_t sGEV; ///< Energy in GeV + + private: + /// Basic function used to determine the ending angle and starting angles + /// for rounded corners given the relative distance between the centers + /// of the circles and the difference/sum of their radii. Case 0. Returns the angle in Degrees + /// \param Double_t dx difference in x locations of the circle centers + /// \param Double_t dy difference in y locations of the circle centers + /// \param Double_t sdr difference or sum of the circle radii + Double_t angleForRoundedCorners0(Double_t dx, Double_t dy, Double_t sdr) const; + + /// Basic function used to determine the ending angle and starting angles + /// for rounded corners given the relative distance between the centers + /// of the circles and the difference/sum of their radii. Case 0. Returns the angle in Degrees + /// \param Double_t dx difference in x locations of the circle centers + /// \param Double_t dy difference in y locations of the circle centers + /// \param Double_t sdr difference or sum of the circle radii + Double_t angleForRoundedCorners1(Double_t dx, Double_t dy, Double_t sdr) const; + + Int_t mDebug; //! Debug flag/level + ClassDefOverride(V11Geometry, 1); // Base class for ITS v11 geometry +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V1Layer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V1Layer.h new file mode 100644 index 0000000000000..e338cb3a3a837 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V1Layer.h @@ -0,0 +1,415 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRK_UPGRADEV1LAYER_H_ +#define ALICEO2_TRK_UPGRADEV1LAYER_H_ + +#include <TGeoManager.h> // for gGeoManager +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc +#include "TRKSimulation/V11Geometry.h" // for V11Geometry +#include "TRKSimulation/Detector.h" // for Detector, Detector::Model + +class TGeoArb8; + +class TGeoCombiTrans; + +class TGeoVolume; // lines 15-15 + +namespace o2 +{ +namespace trk +{ + +/// This class defines the Geometry for the ITS using TGeo. This is a work class used +/// to study different configurations during the development of the new ITS structure +class V1Layer : public V11Geometry +{ + + public: + enum { + kStave, + kHalfStave, + kModule, + kChip, + kNHLevels + }; + + // Default constructor + V1Layer(); + + // Constructor setting debugging level + V1Layer(Int_t debug); + + // Constructor setting layer number and debugging level + V1Layer(Int_t lay, Int_t debug); + + /// Constructor setting layer number and debugging level + /// for a "turbo" layer (i.e. where staves overlap in phi) + V1Layer(Int_t lay, Bool_t turbo, Int_t debug); + + /// Copy constructor + V1Layer(const V1Layer& source); + + /// Assignment operator + V1Layer& operator=(const V1Layer& source); + + /// Default destructor + ~V1Layer() override; + + Bool_t isTurbo() const + { + return mIsTurbo; + }; + + Double_t getStaveThick() const + { + return mStaveThickness; + }; + + Double_t getStaveTilt() const + { + return mStaveTilt; + }; + + Double_t getStaveWidth() const + { + return mStaveWidth; + }; + + Double_t getSensorThick() const + { + return mSensorThickness; + }; + + Double_t getNumberOfStaves() const + { + return mNumberOfStaves; + }; + + Double_t getNumberOfChips() const + { + return mNumberOfChips; + }; + + Double_t getRadius() const + { + return mLayerRadius; + }; + + Double_t getPhi0() const + { + return mPhi0; + }; + + Double_t getZLength() const + { + return mZLength; + }; + + Int_t getChipType() const + { + return mChipTypeID; + } + + Int_t getNumberOfStavesPerParent() const + { + return mHierarchy[kStave]; + } + + Int_t getNumberOfHalfStavesPerParent() const + { + return mHierarchy[kHalfStave]; + } + + Int_t getNumberOfModulesPerParent() const + { + return mHierarchy[kModule]; + } + + Int_t getNumberOfChipsPerParent() const + { + return mHierarchy[kChip]; + } + + Detector::Model getStaveModel() const + { + return mStaveModel; + } + + void setStaveThick(Double_t t) + { + mStaveThickness = t; + }; + + /// Sets the Stave tilt angle (for turbo layers only) + /// \param t The stave tilt angle + void setStaveTilt(Double_t t); + + /// Sets the Stave width (for turbo layers only) + /// \param w The stave width + void setStaveWidth(Double_t w); + + void setSensorThick(Double_t t) + { + mSensorThickness = t; + }; + + void setNumberOfStaves(Int_t n) + { + mHierarchy[kStave] = mNumberOfStaves = n; + }; + + /// Sets the number of units in a stave: + /// for the Inner Barrel: the number of chips per stave + /// for the Outer Barrel: the number of modules per half stave + /// \param u the number of units + void setNumberOfUnits(Int_t u); + + void setRadius(Double_t r) + { + mLayerRadius = r; + }; + + void setPhi0(Double_t phi) + { + mPhi0 = phi; + } + + void setZLength(Double_t z) + { + mZLength = z; + }; + + void setChipType(Int_t tp) + { + mChipTypeID = tp; + } + + void setBuildLevel(Int_t buildLevel) + { + mBuildLevel = buildLevel; + } + + void setStaveModel(o2::trk::Detector::Model model) + { + mStaveModel = model; + } + + /// Creates the actual Layer and places inside its mother volume + /// \param motherVolume the TGeoVolume owing the volume structure + virtual void createLayer(TGeoVolume* motherVolume); + + private: + /// Creates the actual Layer and places inside its mother volume + /// A so-called "turbo" layer is a layer where staves overlap in phi + /// User can set width and tilt angle, no check is performed here + /// to avoid volume overlaps + /// \param motherVolume The TGeoVolume owing the volume structure + void createLayerTurbo(TGeoVolume* motherVolume); + + /// Computes the inner radius of the air container for the Turbo configuration + /// as the radius of either the circle tangent to the stave or the circle + /// passing for the stave's lower vertex. Returns the radius of the container + /// if >0, else flag to use the lower vertex + Double_t radiusOmTurboContainer(); + + /// Creates the actual Stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStave(const TGeoManager* mgr = gGeoManager); + + // TGeoVolume* createChip(Double_t x, Double_t z, const TGeoManager *mgr=gGeoManager); + + /// Creates the IB Module: (only the chips for the time being) + /// Returns the module as a TGeoVolume + /// \param xmod, ymod, zmod X, Y, Z module half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleInnerB(Double_t x, Double_t y, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the actual Chip + /// \param xchip,ychip,zchip The chip dimensions + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createChipInnerB(Double_t x, Double_t y, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the OB Module: HIC + FPC + Carbon plate + /// Returns the module as a TGeoVolume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Inner Barrel(Here we fake the halfstave volume to have the + /// same formal geometry hierarchy as for the Outer Barrel) + /// \param xsta, ysta, zsta X, Y, Z stave half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveInnerB(Double_t x, Double_t y, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveStructInnerB(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create a dummy stave + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerBDummy(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager) const; + + /// Create the mechanical stave structure for Model 0 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB0(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 1 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB1(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 2.1 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB21(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 2.2 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB22(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 3 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB3(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Creation of the mechanical stave structure for the Outer Barrel as in v0 + /// (we fake the module and halfstave volumes to have always + /// the same formal geometry hierarchy) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterB0(const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical half stave structure or the Outer Barrel as in TDR + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterB1(const TGeoManager* mgr = gGeoManager); + + /// Create the space frame for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the space frame for the Outer Barrel (Model 1) + /// Returns a TGeoVolume with the Space Frame of a stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB1(const TGeoManager* mgr = gGeoManager); + + /// Creates the V-shaped sides of the OB space frame (from a similar method with same + /// name and function in V11GeometrySDD class by L.Gaudichet) + TGeoArb8* createStaveSide(const char* name, Double_t dz, Double_t angle, Double_t xSign, Double_t L, Double_t H, + Double_t l); + + /// Help method to create a TGeoCombiTrans matrix from a similar method with same name and + /// function in V11GeometrySDD class by L.Gaudichet) + /// Returns the TGeoCombiTrans which make a translation in y and z and a rotation in phi + /// in the global coord system. If planeSym = true, the rotation places the object + /// symetrically (with respect to the transverse plane) to its position in the + /// case planeSym = false + TGeoCombiTrans* createCombiTrans(const char* name, Double_t dy, Double_t dz, Double_t dphi, + Bool_t planeSym = kFALSE); + + /// Help method to add a translation to a TGeoCombiTrans matrix (from a similar method + /// with same name and function in V11GeometrySDD class by L.Gaudichet) + void addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx = 0, Double_t dy = 0, Double_t dz = 0) const; + + Int_t mLayerNumber; ///< Current layer number + Double_t mPhi0; ///< lab phi of 1st stave, in degrees!!! + Double_t mLayerRadius; ///< Inner radius of this layer + Double_t mZLength; ///< Z length of this layer + Double_t mSensorThickness; ///< Sensor thickness + Double_t mStaveThickness; ///< Stave thickness + Double_t mStaveWidth; ///< Stave width (for turbo layers only) + Double_t mStaveTilt; ///< Stave tilt angle (for turbo layers only) in degrees + Int_t mNumberOfStaves; ///< Number of staves in this layer + Int_t mNumberOfModules; ///< Number of modules per container if defined (HalfStave, Stave, whatever is + ///< container) + Int_t mNumberOfChips; ///< Number chips per container (module, HalfStave, Stave, whatever is + /// container) + Int_t mHierarchy[kNHLevels]; ///< array to query number of staves, hstaves, modules, chips per its parent volume + + UInt_t mChipTypeID; ///< detector type id + Bool_t mIsTurbo; ///< True if this layer is a "turbo" layer + Int_t mBuildLevel; ///< Used for material studies + + Detector::Model mStaveModel; ///< The stave model + + // Parameters for the geometry + + // General Parameters + static const Int_t sNumberOmInnerLayers; ///< Number of IB Layers + + static const Double_t sDefaultSensorThick; ///< Default sensor thickness + static const Double_t sDefaultStaveThick; ///< Default stave thickness + + // Inner Barrel Parameters + static const Int_t sIBChipsPerRow; ///< IB chips per row in module + static const Int_t sIBNChipRows; ///< IB chip rows in module + + // Outer Barrel Parameters + static const Int_t sOBChipsPerRow; ///< OB chips per row in module + static const Int_t sOBNChipRows; ///< OB chip rows in module + + static const Double_t sOBHalfStaveWidth; ///< OB Half Stave Width + static const Double_t sOBModuleWidth; ///< OB Module Width + static const Double_t sOBModuleGap; ///< Gap between OB modules + static const Double_t sOBChipXGap; ///< Gap between OB chips on X + static const Double_t sOBChipZGap; ///< Gap between OB chips on Z + static const Double_t sOBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sOBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sOBBusCableAlThick; ///< Thickness of Bus Aluminum + static const Double_t sOBBusCableKapThick; ///< Thickness of Bus Kapton + static const Double_t sOBCarbonPlateThick; ///< OB Carbon Plate Thickness + static const Double_t sOBColdPlateThick; ///< OB Cold Plate Thickness + static const Double_t sOBGlueThick; ///< OB Glue total Thickness + static const Double_t sOBModuleZLength; ///< OB Chip Length along Z + static const Double_t sOBHalfStaveYTrans; ///< OB half staves Y transl. + static const Double_t sOBHalfStaveXOverlap; ///< OB half staves X overlap + static const Double_t sOBGraphiteFoilThick; ///< OB graphite foil thickness + static const Double_t sOBCoolTubeInnerD; ///< OB cooling inner diameter + static const Double_t sOBCoolTubeThick; ///< OB cooling tube thickness + static const Double_t sOBCoolTubeXDist; ///< OB cooling tube separation + + static const Double_t sOBSpaceFrameWidth; ///< OB Space Frame Width + static const Double_t sOBSpaceFrameTotHigh; ///< OB Total Y Height + static const Double_t sOBSFrameBeamRadius; ///< OB Space Frame Beam Radius + static const Double_t sOBSpaceFrameLa; ///< Parameters defining... + static const Double_t sOBSpaceFrameHa; ///< ...the V side shape... + static const Double_t sOBSpaceFrameLb; ///< ...of the carbon... + static const Double_t sOBSpaceFrameHb; ///< ...OB Space Frame + static const Double_t sOBSpaceFrameL; ///< OB SF + static const Double_t sOBSFBotBeamAngle; ///< OB SF bottom beam angle + static const Double_t sOBSFrameBeamSidePhi; ///< OB SF side beam angle + + ClassDefOverride(V1Layer, 0); // ITS v1 geometry +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V3Layer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V3Layer.h new file mode 100644 index 0000000000000..2180ebb487b84 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V3Layer.h @@ -0,0 +1,570 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V3Layer.h +/// \brief Definition of the V3Layer class +/// \author Mario Sitta <sitta@to.infn.it> +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#ifndef ALICEO2_TRK_UPGRADEV3LAYER_H_ +#define ALICEO2_TRK_UPGRADEV3LAYER_H_ + +#include <TGeoManager.h> // for gGeoManager +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc +#include "TRKSimulation/V11Geometry.h" // for V11Geometry +#include "TRKSimulation/Detector.h" // for Detector, Detector::Model + +class TGeoXtru; + +class TGeoCombiTrans; + +class TGeoVolume; // lines 15-15 + +namespace o2 +{ +namespace trk +{ + +/// This class defines the Geometry for the ITS using TGeo. This is a work class used +/// to study different configurations during the development of the new ITS structure +class V3Layer : public V11Geometry +{ + + public: + enum { kStave, + kHalfStave, + kModule, + kChip, + kNHLevels }; + + // Default constructor + V3Layer(); + + /// Constructor setting layer number and debugging level + /// for a "turbo" layer (i.e. where staves overlap in phi) + V3Layer(Int_t lay, Bool_t turbo = kFALSE, Int_t debug = 0); + + /// Copy constructor + V3Layer(const V3Layer&) = default; + + /// Assignment operator + V3Layer& operator=(const V3Layer&) = default; + + /// Default destructor + ~V3Layer() override; + + Bool_t hasGammaConversionRods() const { return mAddGammaConv; }; + + Bool_t isTurbo() const { return mIsTurbo; }; + + Bool_t isTRK() const { return mIsTRK; }; + + Double_t getChipThick() const { return mChipThickness; }; + + Double_t getStaveTilt() const { return mStaveTilt; }; + + Double_t getStaveWidth() const { return mStaveWidth; }; + + Double_t getSensorThick() const { return mSensorThickness; }; + + Double_t getNumberOfStaves() const { return mNumberOfStaves; }; + + Double_t getNumberOfChips() const { return mNumberOfChips; }; + + Double_t getRadius() const { return mLayerRadius; }; + + Double_t getPhi0() const { return mPhi0; }; + + Double_t getIBModuleZLength() const { return mIBModuleZLength; }; + + Double_t getOBModuleZLength() const { return mOBModuleZLength; }; + + Int_t getChipType() const { return mChipTypeID; } + + Int_t getNumberOfStavesPerParent() const { return mHierarchy[kStave]; } + + Int_t getNumberOfHalfStavesPerParent() const { return mHierarchy[kHalfStave]; } + + Int_t getNumberOfModulesPerParent() const { return mHierarchy[kModule]; } + + Int_t getNumberOfChipsPerParent() const { return mHierarchy[kChip]; } + + Int_t getBuildLevel() const { return mBuildLevel; } + + Int_t getNumberOfInnerlayers() const { return mNumberOfInnerLayers; }; + + Detector::Model getStaveModel() const { return mStaveModel; } + + void setChipThick(Double_t t) { mChipThickness = t; }; + + /// Gets the Gamma Conversion Rod diameter + Double_t getGammaConversionRodDiam(); + + /// Gets the Gamma Conversion Rod X position + Double_t getGammaConversionRodXPos(); + + /// Sets whether this is a new TRK layer + void setIsTRK(const Bool_t trk) { mIsTRK = trk; }; + + /// Sets Module Z length (valid only for new TRK layer) + void setIBModuleZLength(const Double_t z) { mIBModuleZLength = z; }; + + /// Sets the Stave tilt angle (for turbo layers only) + /// \param t The stave tilt angle + void setStaveTilt(Double_t t); + + /// Sets the Stave width (for turbo layers only) + /// \param w The stave width + void setStaveWidth(Double_t w); + + void setSensorThick(Double_t t) { mSensorThickness = t; }; + + void setNumberOfStaves(Int_t n) { mHierarchy[kStave] = mNumberOfStaves = n; }; + + /// Sets the number of units in a stave: + /// for the Inner Barrel: the number of chips per stave + /// for the Outer Barrel: the number of modules per half stave + /// \param u the number of units + void setNumberOfUnits(Int_t u); + + void setRadius(Double_t r) { mLayerRadius = r; }; + + void setPhi0(Double_t phi) { mPhi0 = phi; } + + void setChipType(Int_t tp) { mChipTypeID = tp; } + + void setBuildLevel(Int_t buildLevel) { mBuildLevel = buildLevel; } + + void setStaveModel(o2::trk::Detector::Model model) { mStaveModel = model; } + + void setNumberOfInnerLayers(const Int_t n) { mNumberOfInnerLayers = n; }; + + /// Adds the Gamma Conversion Rods to the geometry + /// \param diam the diameter of each rod + /// \param xPos the X position of each rod + void addGammaConversionRods(const Double_t diam, const Double_t xPos); + + /// Creates the actual Layer and places inside its mother volume + /// \param motherVolume the TGeoVolume owing the volume structure + virtual void createLayer(TGeoVolume* motherVolume); + + private: + /// Creates the actual Layer and places inside its mother volume + /// A so-called "turbo" layer is a layer where staves overlap in phi + /// User can set width and tilt angle, no check is performed here + /// to avoid volume overlaps + /// \param motherVolume The TGeoVolume owing the volume structure + void createLayerTurbo(TGeoVolume* motherVolume); + + /// Creates a new TRK Layer and places inside its mother volume + /// \param motherVolume The TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createTRKLayer(TGeoVolume* motherVolume, const TGeoManager* mgr = gGeoManager); + + /// Computes the inner radius of the air container for the Turbo configuration + /// as the radius of either the circle tangent to the stave or the circle + /// passing for the stave's lower vertex. Returns the radius of the container + /// if >0, else flag to use the lower vertex + Double_t radiusOmTurboContainer(); + + /// Creates the actual Stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStave(const TGeoManager* mgr = gGeoManager); + + /// Creates the IB Module: (only the chips for the time being) + /// Returns the module as a TGeoVolume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleInnerB(const TGeoManager* mgr = gGeoManager); + + /// Creates the OB Module: HIC + FPC + Carbon plate + /// Returns the module as a TGeoVolume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleOuterB(const TGeoManager* mgr = gGeoManager); + + /// Creates the IB FPC Aluminum Ground layer + /// Returns the layer as a TGeoVolume + /// \param x, z X, Z layer half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBFPCAlGnd(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the IB FPC Aluminum Anode layer + /// Returns the layer as a TGeoVolume + /// \param x, z X, Z layer half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBFPCAlAnode(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the IB FPC capacitors + /// \param modvol The IB module mother volume + /// \param zchip The chip half Z length + /// \param yzero The Y base position of capacitors + /// \param mgr The GeoManager (used only to get the proper material) + void createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yzero, const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Inner Barrel(Here we fake the halfstave volume to have the + /// same formal geometry hierarchy as for the Outer Barrel) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveInnerB(const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveStructInnerB(const TGeoManager* mgr = gGeoManager); + + /// Create a dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the mechanical stave structure for Model 4 of TDR + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB4(const TGeoManager* mgr = gGeoManager); + + /// Create the Inner Barrel End Stave connectors + /// \param mgr The GeoManager (used only to get the proper material) + void createIBConnectors(const TGeoManager* mgr = gGeoManager); + + /// Create the Inner Barrel End Stave connectors on Side A + /// \param mgr The GeoManager (used only to get the proper material) + void createIBConnectorsASide(const TGeoManager* mgr = gGeoManager); + + /// Create the Inner Barrel End Stave connectors on Side C + /// \param mgr The GeoManager (used only to get the proper material) + void createIBConnectorsCSide(const TGeoManager* mgr = gGeoManager); + + /// Creates the OB FPC Copper Ground layer + /// Returns the FPC as a TGeoVolume + /// \param z Z module half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createOBFPCCuGnd(Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the OB FPC Copper Signal layer + /// Returns the FPC as a TGeoVolume + /// \param z Z module half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createOBFPCCuSig(Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the OB Power and Bias Buses + /// Returns a TGeoVolume with both buses + /// \param z Z stave half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createOBPowerBiasBuses(Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the mechanical half stave structure or the Outer Barrel as in TDR + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterB2(const TGeoManager* mgr = gGeoManager); + + /// Create the Cold Plate connectors + void createOBColdPlateConnectors(); + + /// Create the A-Side end-stave connectors for OB staves + void createOBColdPlateConnectorsASide(); + + /// Create the C-Side end-stave connectors for OB staves + void createOBColdPlateConnectorsCSide(); + + /// Create the space frame for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the space frame for the Outer Barrel (Model 2) + /// Returns a TGeoVolume with the Space Frame of a stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB2(const TGeoManager* mgr = gGeoManager); + + /// Create the space frame building blocks for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + void createOBSpaceFrameObjects(const TGeoManager* mgr = gGeoManager); + + /// Create the space frame connectors for the Outer Barrel + /// \param mother the mother volume where to put the connector + /// \param ymot,zmot the Y and Z position in the mother volume + /// \param sideA true for side A, false for side C + /// \param mgr The GeoManager (used only to get the proper material) + void createOBSpaceFrameConnector(TGeoVolume* mother, const Double_t ymot, const Double_t zmot, const Bool_t sideA, const TGeoManager* mgr = gGeoManager); + + /// Creates the V-shaped sides of the OB space frame (from a similar method with same + /// name and function in V11GeometrySDD class by L.Gaudichet) + /// \param name The volume name + /// \param dz The half Z length + /// \param alpha The first rotation angle + /// \param beta The second rotation angle + /// \param L The stave length + /// \param H The stave height + /// \param top True to create the top corner, False to create the side one + TGeoXtru* createStaveSide(const char* name, Double_t dz, Double_t alpha, Double_t beta, Double_t L, Double_t H, + Bool_t top); + + /// Help method to create a TGeoCombiTrans matrix from a similar method with same name and + /// function in V11GeometrySDD class by L.Gaudichet) + /// Returns the TGeoCombiTrans which make a translation in y and z and a rotation in phi + /// in the global coord system. If planeSym = true, the rotation places the object + /// symetrically (with respect to the transverse plane) to its position in the + /// case planeSym = false + TGeoCombiTrans* createCombiTrans(const char* name, Double_t dy, Double_t dz, Double_t dphi, Bool_t planeSym = kFALSE); + + /// Help method to add a translation to a TGeoCombiTrans matrix (from a similar method + /// with same name and function in V11GeometrySDD class by L.Gaudichet) + void addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx = 0, Double_t dy = 0, Double_t dz = 0) const; + + Int_t mLayerNumber; ///< Current layer number + Int_t mNumberOfInnerLayers; ///< Actual number of inner layers + Double_t mPhi0; ///< lab phi of 1st stave, in degrees!!! + Double_t mLayerRadius; ///< Inner radius of this layer + Double_t mSensorThickness; ///< Sensor thickness + Double_t mChipThickness; ///< Chip thickness + Double_t mStaveWidth; ///< Stave width (for turbo layers only) + Double_t mStaveTilt; ///< Stave tilt angle (for turbo layers only) in degrees + Int_t mNumberOfStaves; ///< Number of staves in this layer + Int_t mNumberOfModules; ///< Number of modules per container if defined (HalfStave, Stave, whatever is + ///< container) + Int_t mNumberOfChips; ///< Number chips per container (module, HalfStave, Stave, whatever is + /// container) + Int_t mHierarchy[kNHLevels]; ///< array to query number of staves, hstaves, modules, chips per its parent volume + + UInt_t mChipTypeID; ///< detector type id + Bool_t mIsTurbo; ///< True if this layer is a "turbo" layer + Bool_t mIsTRK; ///< True if this layer is a new TRK layer + Int_t mBuildLevel; ///< Used for material studies + + Detector::Model mStaveModel; ///< The stave model + + Bool_t mAddGammaConv; ///< True to add Gamma Conversion Rods + Double_t mGammaConvDiam; ///< Gamma Conversion Rod Diameter + Double_t mGammaConvXPos; ///< Gamma Conversion Rod X Position + + // Dimensions computed during geometry build-up + Double_t mIBModuleZLength; ///< IB Module Length along Z + Double_t mOBModuleZLength; ///< OB Module Length along Z + + // Parameters for the geometry + + // General Parameters + static const Int_t sNumberOfInnerLayers; ///< Number of IB Layers + + static const Double_t sDefaultSensorThick; ///< Default sensor thickness + static const Double_t sMetalLayerThick; ///< Metal layer thickness + + // Inner Barrel Parameters + static const Int_t sIBChipsPerRow; ///< IB chips per row in module + 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 sIBConnectorXWidth; ///< IB Connectors Width + static const Double_t sIBConnectorYTot; ///< IB Connectors total height + static const Double_t sIBConnectBlockZLen; ///< IB Connector Block Z length + static const Double_t sIBConnBodyYHeight; ///< IB Connector Body Y height + static const Double_t sIBConnTailYMid; ///< IB Connector Tail Y mid pt + static const Double_t sIBConnTailYShift; ///< IB Connector Tail Y shift + static const Double_t sIBConnTailZLen; ///< IB Connector Tail Z length + static const Double_t sIBConnTailOpenPhi; ///< IB Connector Tail Angle + static const Double_t sIBConnRoundHoleD; ///< IB Connector Hole diameter + static const Double_t sIBConnRoundHoleZ; ///< IB Connector Hole Z pos + static const Double_t sIBConnSquareHoleX; ///< IB Connector Hole X len + static const Double_t sIBConnSquareHoleZ; ///< IB Connector Hole Z len + static const Double_t sIBConnSquareHoleZPos; ///< IB Connector Hole Z pos + static const Double_t sIBConnInsertHoleD; ///< IB Connector Insert diam + static const Double_t sIBConnInsertHoleZPos; ///< IB Connector Insert Z pos + static const Double_t sIBConnTubeHole1D; ///< IB Connector Tube1 diam + static const Double_t sIBConnTubeHole1ZLen; ///< IB Connector Tube1 Z len + static const Double_t sIBConnTubeHole1ZLen2; ///< IB Conn Tube1 Z len 2'side + static const Double_t sIBConnTubeHole2D; ///< IB Connector Tube2 diam + static const Double_t sIBConnTubeHole3XPos; ///< IB Connector Tube3 X pos + static const Double_t sIBConnTubeHole3ZPos; ///< IB Connector Tube3 Z pos + static const Double_t sIBConnTubesXDist; ///< IB Connector Tubes X dist + static const Double_t sIBConnTubesYPos; ///< IB Connector Tubes Y pos + static const Double_t sIBConnInsertD; ///< IB Connector Insert diam + static const Double_t sIBConnInsertHeight; ///< IB Connector Insert height + static const Double_t sIBConnSideHole1D; ///< IB Conn Side 1st hole D + static const Double_t sIBConnSideHole1YPos; ///< IB Conn Side 1st hole Y pos + static const Double_t sIBConnSideHole1ZPos; ///< IB Conn Side 1st hole Z pos + static const Double_t sIBConnSideHole1XWid; ///< IB Conn Side 1st hole X wid + static const Double_t sIBConnSideHole2YPos; ///< IB Conn Side 2nd hole Y pos + static const Double_t sIBConnSideHole2ZPos; ///< IB Conn Side 2nd hole Z pos + static const Double_t sIBConnSideHole2XWid; ///< IB Conn Side 2nd hole X wid + static const Double_t sIBConnSideHole2YWid; ///< IB Conn Side 2nd hole Y wid + static const Double_t sIBConnSideHole2ZWid; ///< IB Conn Side 2nd hole Z wid + static const Double_t sIBConnectAFitExtD; ///< IB ConnectorA Fitting ext D + static const Double_t sIBConnectAFitIntD; ///< IB ConnectorA Fitting int D + static const Double_t sIBConnectAFitZLen; ///< IB ConnectorA Fitting Z len + static const Double_t sIBConnectAFitZOut; ///< IB ConnectorA Fitting Z Out + static const Double_t sIBConnPlugInnerD; ///< IB Connector Plug int diam + static const Double_t sIBConnPlugTotLen; ///< IB Connector Plug tot len + static const Double_t sIBConnPlugInnerLen; ///< IB Connector Plug int len + + static const Double_t sIBStaveHeight; ///< IB Stave Total Y Height + + // Outer Barrel Parameters + static const Int_t sOBChipsPerRow; ///< OB chips per row in module + static const Int_t sOBNChipRows; ///< OB chip rows in module + + static const Double_t sOBChipThickness; ///< Default OB chip thickness + + static const Double_t sOBHalfStaveWidth; ///< OB Half Stave Width + static const Double_t sOBModuleGap; ///< Gap between OB modules + static const Double_t sOBChipXGap; ///< Gap between OB chips on X + static const Double_t sOBChipZGap; ///< Gap between OB chips on Z + static const Double_t sOBFlexCableXWidth; ///< OB FPC X width + static const Double_t sOBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sOBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sOBFPCSoldMaskThick; ///< Thickness of FPC Solder Mask + static const Double_t sOBFPCCopperThick; ///< Thickness of FPC Copper + static const Double_t sOBFPCCuAreaFracGnd; ///< Fraction of Cu on Gnd FPC + static const Double_t sOBFPCCuAreaFracSig; ///< Fraction of Cu on Sig FPC + static const Double_t sOBGlueFPCThick; ///< Thickness of Glue to FPC + static const Double_t sOBGlueColdPlThick; ///< Thickness of Glue to Cold Pl + static const Double_t sOBPowerBusXWidth; ///< OB Power Bus X width + static const Double_t sOBPowerBusAlThick; ///< OB Power Bus Al thickness + static const Double_t sOBPowerBusAlFrac; ///< Fraction of Al on OB PB + static const Double_t sOBPowerBusDielThick; ///< OB Power Bus Dielectric thick + static const Double_t sOBPowerBusKapThick; ///< OB Power Bus Kapton thick + static const Double_t sOBBiasBusXWidth; ///< OB Bias Bus X width + static const Double_t sOBBiasBusAlThick; ///< OB Bias Bus Al thickness + static const Double_t sOBBiasBusAlFrac; ///< Fraction of Al on OB BB + static const Double_t sOBBiasBusDielThick; ///< OB Bias Bus Dielectric thick + static const Double_t sOBBiasBusKapThick; ///< OB Bias Bus Kapton thick + static const Double_t sOBColdPlateXWidth; ///< OB Cold Plate X width + static const Double_t sOBColdPlateZLenML; ///< OB ML Cold Plate Z length + static const Double_t sOBColdPlateZLenOL; ///< OB OL Cold Plate Z length + static const Double_t sOBColdPlateThick; ///< OB Cold Plate Thickness + static const Double_t sOBHalfStaveYPos; ///< OB half staves Y position + static const Double_t sOBHalfStaveYTrans; ///< OB half staves Y transl. + static const Double_t sOBHalfStaveXOverlap; ///< OB half staves X overlap + static const Double_t sOBGraphiteFoilThick; ///< OB graphite foil thickness + static const Double_t sOBCarbonFleeceThick; ///< OB carbon fleece thickness + static const Double_t sOBCoolTubeInnerD; ///< OB cooling inner diameter + static const Double_t sOBCoolTubeThick; ///< OB cooling tube thickness + static const Double_t sOBCoolTubeXDist; ///< OB cooling tube separation + + static const Double_t sOBCPConnectorXWidth; ///< OB Cold Plate Connect Width + static const Double_t sOBCPConnBlockZLen; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnBlockYHei; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnHollowZLen; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnHollowYHei; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnSquareHoleX; ///< OB Conn Square Hole X len + static const Double_t sOBCPConnSquareHoleZ; ///< OB Conn Square Hole Z len + static const Double_t sOBCPConnSqrHoleZPos; ///< OB Conn Square Hole Z pos + static const Double_t sOBCPConnSqrInsertRZ; ///< OB Conn Square Insert RZpos + static const Double_t sOBCPConnRoundHoleD; ///< OB Conn Round Hole diam + static const Double_t sOBCPConnRndHoleZPos; ///< OB Conn Round Hole Z pos + static const Double_t sOBCPConnTubesXDist; ///< OB Connector Tubes X dist + static const Double_t sOBCPConnTubesYPos; ///< OB Connector Tubes Y pos + static const Double_t sOBCPConnTubeHole1D; ///< OB Connector Tube1 diam + static const Double_t sOBCPConnTubeHole1Z; ///< OB Connector Tube1 Z len + static const Double_t sOBCPConnTubeHole2D; ///< OB Connector Tube2 diam + static const Double_t sOBCPConnFitHoleD; ///< OB Connector Fit Hole diam + static const Double_t sOBCPConnTubeHole3XP; ///< OB Connector Tube3 X pos + static const Double_t sOBCPConnTubeHole3ZP; ///< OB Connector Tube3 Z pos + static const Double_t sOBCPConnInstZThick; ///< OB Connector Insert height + static const Double_t sOBCPConnInsertYHei; ///< OB Connector Insert height + static const Double_t sOBCPConnAFitExtD; ///< OB ConnectorA Fitting ext D + static const Double_t sOBCPConnAFitThick; ///< OB ConnectorA Fitting thick + static const Double_t sOBCPConnAFitZLen; ///< OB ConnectorA Fitting Z len + static const Double_t sOBCPConnAFitZIn; ///< OB ConnectorA Fitting Z ins + static const Double_t sOBCPConnPlugInnerD; ///< OB Connector Plug int diam + static const Double_t sOBCPConnPlugTotLen; ///< OB Connector Plug tot le + static const Double_t sOBCPConnPlugThick; ///< OB Connector Plug thickness + + static const Double_t sOBSpaceFrameZLen[2]; ///< OB Space Frame Length + static const Int_t sOBSpaceFrameNUnits[2]; ///< OB Number of SF Units + static const Double_t sOBSpaceFrameUnitLen; ///< OB Space Frame Unit length + static const Double_t sOBSpaceFrameWidth; ///< OB Space Frame Width + static const Double_t sOBSpaceFrameHeight; ///< OB Space Frame Height + static const Double_t sOBSpaceFrameTopVL; ///< Parameters defining... + static const Double_t sOBSpaceFrameTopVH; ///< ...the Top V shape + static const Double_t sOBSpaceFrameSideVL; ///< Parameters defining... + static const Double_t sOBSpaceFrameSideVH; ///< ...the Side V shape + static const Double_t sOBSpaceFrameVAlpha; ///< Angles of aperture... + static const Double_t sOBSpaceFrameVBeta; ///< ...of the V shapes + static const Double_t sOBSFrameBaseRibDiam; ///< OB SFrame Base Rib Diam + static const Double_t sOBSFrameBaseRibPhi; ///< OB SF base beam angle + static const Double_t sOBSFrameSideRibDiam; ///< OB SFrame Side Rib Diam + static const Double_t sOBSFrameSideRibPhi; ///< OB SF side beam angle + static const Double_t sOBSFrameULegLen; ///< OB SF U-Leg length + static const Double_t sOBSFrameULegWidth; ///< OB SF U-Leg width + static const Double_t sOBSFrameULegHeight1; ///< OB SF U-Leg height + static const Double_t sOBSFrameULegHeight2; ///< OB SF U-Leg height + static const Double_t sOBSFrameULegThick; ///< OB SF U-Leg thickness + static const Double_t sOBSFrameULegXPos; ///< OB SF U-Leg X position + static const Double_t sOBSFrameConnWidth; ///< OB SF Connector width + static const Double_t sOBSFrameConnTotLen; ///< OB SF Connector total length + static const Double_t sOBSFrameConnTotHei; ///< OB SF Connector total height + static const Double_t sOBSFrameConnTopLen; ///< OB SF Connector top length + static const Double_t sOBSFrameConnInsWide; ///< OB SF Connector insert width + static const Double_t sOBSFrameConnInsBase; ///< OB SF Connector insert base + static const Double_t sOBSFrameConnInsHei; ///< OB SF Connector insert height + static const Double_t sOBSFrameConnHoleZPos; ///< OB SF Connector holes Z pos + static const Double_t sOBSFrameConnHoleZDist; ///< OB SF Connector holes Zdist + static const Double_t sOBSFrameConnTopHoleD; ///< OB SF Connec top hole diam + static const Double_t sOBSFrConnTopHoleXDist; ///< OB SF Connec top hole Xdist + static const Double_t sOBSFrameConnAHoleWid; ///< OB SF Connect A hole width + static const Double_t sOBSFrameConnAHoleLen; ///< OB SF Connect A hole length + static const Double_t sOBSFrConnASideHoleD; ///< OB SF Conn A side hole dia + static const Double_t sOBSFrConnASideHoleL; ///< OB SF Conn A side hole len + static const Double_t sOBSFrConnASideHoleY; ///< OB SF Conn A side hole Y pos + static const Double_t sOBSFrameConnCHoleZPos; ///< OB SF Connect C hole Z pos + static const Double_t sOBSFrConnCHoleXDist; ///< OB SF Conn C top hole Xdist + static const Double_t sOBSFrConnCTopHoleD; ///< OB SF Conn C top hole dia + static const Double_t sOBSFrameConnInsHoleD; ///< OB SF Connec insert hole dia + static const Double_t sOBSFrameConnInsHoleX; ///< OB SF Connec insert hole X + + ClassDefOverride(V3Layer, 0); // ITS v3 geometry +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V3Services.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V3Services.h new file mode 100644 index 0000000000000..58e2c918f7ace --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/V3Services.h @@ -0,0 +1,184 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V3Services.h +/// \brief Definition of the V3Services class +/// \author Mario Sitta <sitta@to.infn.it> +/// \author Parinya Namwongsa <parinya.namwongsa@cern.ch> + +#ifndef ALICEO2_TRK_UPGRADEV3SERVICES_H_ +#define ALICEO2_TRK_UPGRADEV3SERVICES_H_ + +#include <TGeoManager.h> // for gGeoManager +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc +#include "TRKSimulation/V11Geometry.h" // for V11Geometry +#include "TRKSimulation/Detector.h" // for Detector, Detector::Model + +class TGeoXtru; + +class TGeoCombiTrans; + +class TGeoVolume; + +namespace o2 +{ +namespace trk +{ + +/// This class defines the Geometry for the Services of the ITS Upgrade using TGeo. +class V3Services : public V11Geometry +{ + + public: + // Default constructor + V3Services(); + + /// Copy constructor + V3Services(const V3Services&) = default; + + /// Assignment operator + V3Services& operator=(const V3Services&) = default; + + /// Default destructor + ~V3Services() override; + + /// Creates the Inner Barrel End Wheels on Side A + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBEndWheelsSideA(const TGeoManager* mgr = gGeoManager); + + /// Creates the Inner Barrel End Wheels on Side C + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBEndWheelsSideC(const TGeoManager* mgr = gGeoManager); + + /// Creates the CYSS Assembly (i.e. the supporting half cylinder and cone) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createCYSSAssembly(const TGeoManager* mgr = gGeoManager); + + /// Creates the Middle Barrel End Wheels on Side A + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createMBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Middle Barrel End Wheels on Side C + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel End Wheels on Side A + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel End Wheels on Side C + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side A + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBConeSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side C + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBConeSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + private: + /// Creates a single Inner Barrel End Wheel on Side A + /// \param iLay the layer number + /// \param endWheel the End Wheel volume assembly + /// \param mgr The GeoManager (used only to get the proper material) + void ibEndWheelSideA(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr = gGeoManager); + + /// Creates a single Inner Barrel End Wheel on Side C + /// \param iLay the layer number + /// \param endWheel the End Wheel volume assembly + /// \param mgr The GeoManager (used only to get the proper material) + void ibEndWheelSideC(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr = gGeoManager); + + /// Creates the shape of a Rib on Side A + /// \param iLay the layer number + TGeoXtru* ibEndWheelARibShape(const Int_t iLay); + + /// Creates the CYSS cylinder of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssCylinder(const TGeoManager* mgr = gGeoManager); + + /// Creates the CYSS cone of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssCone(const TGeoManager* mgr = gGeoManager); + + /// Creates the CYSS Flange on Side A of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssFlangeSideA(const TGeoManager* mgr = gGeoManager); + + /// Creates the hollows in the CYSS Flange on Side A of the Inner Barrel + /// \param zlen the thickness of the ring where the hollows are + TString ibCreateHollowsCyssFlangeSideA(const Double_t zlen); + + /// Creates the CYSS Flange on Side C of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssFlangeSideC(const TGeoManager* mgr = gGeoManager); + + /// Creates a single Middle/Outer Barrel End Wheel on Side A + /// \param iLay the layer number + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obEndWheelSideA(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates a single Middle Barrel End Wheel on Side C + /// \param iLay the layer number + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates a single Outer Barrel End Wheel on Side C + /// \param iLay the layer number + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side A + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obConeSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side C + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obConeSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone Trays on Side A + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obConeTraysSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + // Parameters + static constexpr Int_t sNumberInnerLayers = 3; ///< Number of inner layers in ITSU + static constexpr Int_t sNumberMiddlLayers = 2; ///< Number of middle layers in ITSU + static constexpr Int_t sNumberOuterLayers = 2; ///< Number of outer layers in ITSU + + // Common parameters for IB services + static const Double_t sIBWheelACZdist; ///< IB Z distance between wheels + static const Double_t sIBCYSSFlangeCZPos; ///< IB Z position of CYSS C Flange + + // Common parameters for OB services + static const Double_t sOBWheelThickness; ///< MB/OB Wheels Thickness + static const Double_t sMBWheelsZpos; ///< MB Wheels Z position + static const Double_t sOBWheelsZpos; ///< OB Wheels Z position + static const Double_t sOBConesZpos; ///< OB Cones A & C Z position + + ClassDefOverride(V3Services, 0); // ITS v3 support geometry +}; +} // namespace trk +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx new file mode 100644 index 0000000000000..06c40781b5696 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -0,0 +1,1374 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Detector.cxx +/// \brief Implementation of the Detector class + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSMFTSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/Detector.h" +#include "TRKSimulation/V3Layer.h" +#include "TRKSimulation/V3Services.h" + +#include "SimulationDataFormat/Stack.h" +#include "SimulationDataFormat/TrackReference.h" + +// FairRoot includes +#include "FairDetector.h" // for FairDetector +#include "FairLogger.h" // 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 "FairRootManager.h" + +#include "TGeoManager.h" // for TGeoManager, gGeoManager +#include "TGeoTube.h" // for TGeoTube +#include "TGeoPcon.h" // for TGeoPcon +#include "TGeoVolume.h" // for TGeoVolume, TGeoVolumeAssembly +#include "TString.h" // for TString, operator+ +#include "TVirtualMC.h" // for gMC, TVirtualMC +#include "TVirtualMCStack.h" // for TVirtualMCStack + +#include <cstdio> // for NULL, snprintf + +class FairModule; + +class TGeoMedium; + +class TParticle; + +using std::cout; +using std::endl; + +using o2::itsmft::Hit; +using Segmentation = o2::itsmft::SegmentationAlpide; +using namespace o2::trk; + +float getDetLengthFromEta(const float eta, const float radius) +{ + return 2. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta)))); +} + +Detector::Detector() + : o2::base::DetImpl<Detector>("TRK", kTRUE), + mTrackData(), + /* + mHitStarted(false), + mTrkStatusStart(), + mPositionStart(), + mMomentumStart(), + mEnergyLoss(), + */ + mNumberOfDetectors(-1), + mModifyGeometry(kFALSE), + mHits(o2::utils::createSimVector<o2::itsmft::Hit>()), + mStaveModelInnerBarrel(kIBModel0), + mStaveModelOuterBarrel(kOBModel0) +{ +} + +static double radii2Turbo(double rMin, double rMid, double rMax, double sensW) +{ + // compute turbo angle from radii and sensor width + return TMath::ASin((rMax * rMax - rMin * rMin) / (2 * rMid * sensW)) * TMath::RadToDeg(); +} + +// We need this as a method to access members +void Detector::configITS(Detector* its) +{ + // build ITS upgrade detector + const int kNLr = 7; + const int kNLrInner = 3; + const int kBuildLevel = 0; + const int kSensTypeID = 0; // dummy id for Alpide sensor + + const float ChipThicknessIB = 50.e-4; + const float ChipThicknessOB = 100.e-4; + + enum { kRmn, + kRmd, + kRmx, + kNModPerStave, + kPhi0, + kNStave, + kNPar }; + // Radii are from last TDR (ALICE-TDR-017.pdf Tab. 1.1, rMid is mean value) + // const double tdr5dat[kNLr][kNPar] = { + // {2.24, 2.34, 2.67, 9., 16.42, 12}, // for each inner layer: rMin,rMid,rMax,NChip/Stave, phi0, nStaves + // {3.01, 3.15, 3.46, 9., 12.18, 16}, + // {3.78, 3.93, 4.21, 9., 9.55, 20}, + // {-1, 19.6, -1, 4., 0., 24}, // for others: -, rMid, -, NMod/HStave, phi0, nStaves // 24 was 49 + // {-1, 24.55, -1, 4., 0., 30}, // 30 was 61 + // {-1, 34.39, -1, 7., 0., 42}, // 42 was 88 + // {-1, 39.34, -1, 7., 0., 48} // 48 was 100 + // }; + // const int nChipsPerModule = 7; // For OB: how many chips in a row + // const double zChipGap = 0.01; // For OB: gap in Z between chips + // const double zModuleGap = 0.01; // For OB: gap in Z between modules + + // double dzLr, rLr, phi0, turbo; + // int nStaveLr, nModPerStaveLr; + + // its->setStaveModelIB(o2::trk::Detector::kIBModel4); + its->setStaveModelOB(o2::trk::Detector::kOBModel2); + + const int kNWrapVol = 3; + const double wrpRMin[kNWrapVol] = {2.1, 19.3, 33.32}; + const double wrpRMax[kNWrapVol] = {15.4, 29.14, 46.0}; + const double wrpZSpan[kNWrapVol] = {70., 93., 163.6}; + + for (int iw = 0; iw < kNWrapVol; iw++) { + its->defineWrapperVolume(iw, wrpRMin[iw], wrpRMax[iw], wrpZSpan[iw]); + } + + // Build OB only (4 layers) + // for (int idLr = mNumberOfInnerLayers; idLr < mTotalNumberOfLayers; idLr++) { + // int im = idLr - mNumberOfInnerLayers + kNLrInner; + // rLr = tdr5dat[im][kRmd]; + // phi0 = tdr5dat[im][kPhi0]; + + // nStaveLr = TMath::Nint(tdr5dat[im][kNStave]); + // nModPerStaveLr = TMath::Nint(tdr5dat[im][kNModPerStave]); + // int nChipsPerStaveLr = nModPerStaveLr; + // its->defineLayer(idLr, phi0, rLr, nStaveLr, nModPerStaveLr, ChipThicknessOB, Segmentation::SensorLayerThickness, + // kSensTypeID, kBuildLevel); + // } + + // From Mario Sitta's hack + std::vector<std::array<double, 2>> tdr5data; + tdr5data.emplace_back(std::array<double, 2>{1.8f, getDetLengthFromEta(1.44f, 1.8f)}); + tdr5data.emplace_back(std::array<double, 2>{2.8f, getDetLengthFromEta(1.44f, 2.8f)}); + tdr5data.emplace_back(std::array<double, 2>{3.8f, getDetLengthFromEta(1.44f, 3.8f)}); + tdr5data.emplace_back(std::array<double, 2>{8.0f, getDetLengthFromEta(1.44f, 8.0f)}); + tdr5data.emplace_back(std::array<double, 2>{20.0f, getDetLengthFromEta(1.44f, 20.0f)}); + tdr5data.emplace_back(std::array<double, 2>{25.0f, getDetLengthFromEta(1.44f, 25.0f)}); + tdr5data.emplace_back(std::array<double, 2>{40.0f, getDetLengthFromEta(1.44f, 40.0f)}); + tdr5data.emplace_back(std::array<double, 2>{55.f, getDetLengthFromEta(1.44f, 55.f)}); + tdr5data.emplace_back(std::array<double, 2>{80.0f, getDetLengthFromEta(1.44f, 80.0f)}); + tdr5data.emplace_back(std::array<double, 2>{100.f, getDetLengthFromEta(1.44f, 100.f)}); + + std::array<float, 10> sensorThicknesses = {50.e-4, 50.e-4, 50.e-4, 50.e-3, 50.e-3, 50.e-3, 50.e-3, 50.e-3, 50.e-3, 50.e-3}; + its->setStaveModelOB(o2::trk::Detector::kOBModel2); + // its->createOuterBarrel(false); + + auto idLayer{0}; + for (auto& layerData : tdr5data) { + its->defineInnerLayerTRK(idLayer, layerData[0], layerData[1], sensorThicknesses[idLayer], 0, 0); + ++idLayer; + } + its->createOuterBarrel(false); +} + +Detector::Detector(Bool_t active) + : o2::base::DetImpl<Detector>("TRK", active), + mTrackData(), + /* + mHitStarted(false), + mTrkStatusStart(), + mPositionStart(), + mMomentumStart(), + mEnergyLoss(), + */ + mNumberOfInnerLayers(10), + mNumberOfDetectors(-1), + mModifyGeometry(kFALSE), + mHits(o2::utils::createSimVector<o2::itsmft::Hit>()), + mStaveModelInnerBarrel(kIBModel0), + mStaveModelOuterBarrel(kOBModel0) +{ + + mTotalNumberOfLayers = mNumberOfInnerLayers + (sNumberLayers - sNumberInnerLayers); + + createAllArrays(); + + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer + } + + if (mTotalNumberOfLayers > 0) { // if not, we'll Fatal-ize in CreateGeometry + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + mTRKLayer[j] = kFALSE; + mLayerPhi0[j] = 0; + mLayerRadii[j] = 0.; + mLayerZLen[j] = 0.; + mStavePerLayer[j] = 0; + mUnitPerStave[j] = 0; + mChipThickness[j] = 0.; + mStaveWidth[j] = 0.; + mStaveTilt[j] = 0.; + mDetectorThickness[j] = 0.; + mChipTypeID[j] = 0; + mBuildLevel[j] = 0; + mGeometry[j] = nullptr; + } + } + mServicesGeometry = nullptr; + + for (int i = sNumberOfWrapperVolumes; i--;) { + mWrapperMinRadius[i] = mWrapperMaxRadius[i] = mWrapperZSpan[i] = -1; + } + + configITS(this); +} + +Detector::Detector(const Detector& rhs) + : o2::base::DetImpl<Detector>(rhs), + mTrackData(), + /* + mHitStarted(false), + mTrkStatusStart(), + mPositionStart(), + mMomentumStart(), + mEnergyLoss(), + */ + mNumberOfInnerLayers(rhs.mNumberOfInnerLayers), + mNumberOfDetectors(rhs.mNumberOfDetectors), + mModifyGeometry(rhs.mModifyGeometry), + + /// Container for data points + mHits(o2::utils::createSimVector<o2::itsmft::Hit>()), + mStaveModelInnerBarrel(rhs.mStaveModelInnerBarrel), + mStaveModelOuterBarrel(rhs.mStaveModelOuterBarrel) +{ + + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer + } +} + +Detector::~Detector() +{ + + if (mHits) { + // delete mHits; + o2::utils::freeSimVector(mHits); + } +} + +Detector& Detector::operator=(const Detector& rhs) +{ + // The standard = operator + // Inputs: + // Detector &h the sourse of this copy + // Outputs: + // none. + // Return: + // A copy of the sourse hit h + + if (this == &rhs) { + return *this; + } + + // base class assignment + base::Detector::operator=(rhs); + + mNumberOfDetectors = rhs.mNumberOfDetectors; + + mModifyGeometry = rhs.mModifyGeometry; + + /// Container for data points + mHits = nullptr; + + mStaveModelInnerBarrel = rhs.mStaveModelInnerBarrel; + mStaveModelOuterBarrel = rhs.mStaveModelOuterBarrel; + + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer + } + + return *this; +} + +void Detector::createAllArrays() +{ + // Create all arrays + // We have to do it here because now the number of Inner Layers is + // not fixed, so we don't know in advance how many to create: they + // cannot be anymore static arrays as in the default version + // M.S. 21 mar 2020 (13th day of Italian Coronavirus lockdown) + + mLayerID = new Int_t[mTotalNumberOfLayers]; + mLayerName = new TString[mTotalNumberOfLayers]; + + mWrapperLayerId = new Int_t[mTotalNumberOfLayers]; + mTurboLayer = new Bool_t[mTotalNumberOfLayers]; + mTRKLayer = new Bool_t[mTotalNumberOfLayers]; + + mLayerPhi0 = new Double_t[mTotalNumberOfLayers]; + mLayerRadii = new Double_t[mTotalNumberOfLayers]; + mLayerZLen = new Double_t[mTotalNumberOfLayers]; + mStavePerLayer = new Int_t[mTotalNumberOfLayers]; + mUnitPerStave = new Int_t[mTotalNumberOfLayers]; + mChipThickness = new Double_t[mTotalNumberOfLayers]; + mStaveWidth = new Double_t[mTotalNumberOfLayers]; + mStaveTilt = new Double_t[mTotalNumberOfLayers]; + mDetectorThickness = new Double_t[mTotalNumberOfLayers]; + mChipTypeID = new UInt_t[mTotalNumberOfLayers]; + mBuildLevel = new Int_t[mTotalNumberOfLayers]; + + mGeometry = new V3Layer*[mTotalNumberOfLayers]; +} + +void Detector::InitializeO2Detector() +{ + // Define the list of sensitive volumes + defineSensitiveVolumes(); + + for (int i = 0; i < mTotalNumberOfLayers; i++) { + mLayerID[i] = gMC ? TVirtualMC::GetMC()->VolId(mLayerName[i]) : 0; + } + + mGeometryTGeo = GeometryTGeo::Instance(); + // FairRuntimeDb* rtdb= FairRun::Instance()->GetRuntimeDb(); + // O2itsGeoPar* par=(O2itsGeoPar*)(rtdb->getContainer("O2itsGeoPar")); +} + +Bool_t Detector::ProcessHits(FairVolume* vol) +{ + // This method is called from the MC stepping + if (!(fMC->TrackCharge())) { + return kFALSE; + } + + Int_t lay = 0, volID = vol->getMCid(); + + // FIXME: Determine the layer number. Is this information available directly from the FairVolume? + bool notSens = false; + while ((lay < mTotalNumberOfLayers) && (notSens = (volID != mLayerID[lay]))) { + ++lay; + } + if (notSens) { + return kFALSE; // RS: can this happen? This method must be called for sensors only? + } + + // Is it needed to keep a track reference when the outer ITS volume is encountered? + auto stack = (o2::data::Stack*)fMC->GetStack(); + if (fMC->IsTrackExiting() && (lay == 0 || lay == 6)) { + // Keep the track refs for the innermost and outermost layers only + o2::TrackReference tr(*fMC, GetDetId()); + tr.setTrackID(stack->GetCurrentTrackNumber()); + tr.setUserId(lay); + stack->addTrackReference(tr); + } + bool startHit = false, stopHit = false; + unsigned char status = 0; + if (fMC->IsTrackEntering()) { + status |= Hit::kTrackEntering; + } + if (fMC->IsTrackInside()) { + status |= Hit::kTrackInside; + } + if (fMC->IsTrackExiting()) { + status |= Hit::kTrackExiting; + } + if (fMC->IsTrackOut()) { + status |= Hit::kTrackOut; + } + if (fMC->IsTrackStop()) { + status |= Hit::kTrackStopped; + } + if (fMC->IsTrackAlive()) { + status |= Hit::kTrackAlive; + } + + // track is entering or created in the volume + if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) { + startHit = true; + } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) { + stopHit = true; + } + + // increment energy loss at all steps except entrance + if (!startHit) { + mTrackData.mEnergyLoss += fMC->Edep(); + } + if (!(startHit | stopHit)) { + return kFALSE; // do noting + } + + if (startHit) { + mTrackData.mEnergyLoss = 0.; + fMC->TrackMomentum(mTrackData.mMomentumStart); + fMC->TrackPosition(mTrackData.mPositionStart); + mTrackData.mTrkStatusStart = status; + mTrackData.mHitStarted = true; + } + if (stopHit) { + TLorentzVector positionStop; + fMC->TrackPosition(positionStop); + // Retrieve the indices with the volume path + int stave(0), halfstave(0), chipinmodule(0), module; + fMC->CurrentVolOffID(1, chipinmodule); + fMC->CurrentVolOffID(2, module); + fMC->CurrentVolOffID(3, halfstave); + fMC->CurrentVolOffID(4, stave); + int chipindex = mGeometryTGeo->getChipIndex(lay, stave, halfstave, module, chipinmodule); + + Hit* p = addHit(stack->GetCurrentTrackNumber(), chipindex, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), + mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); + // p->SetTotalEnergy(vmc->Etot()); + + // RS: not sure this is needed + // Increment number of Detector det points in TParticle + stack->addHit(GetDetId()); + } + + return kTRUE; +} + +void Detector::createMaterials() +{ + Int_t ifield = 2; + Float_t fieldm = 10.0; + o2::base::Detector::initFieldTrackingParams(ifield, fieldm); + //////////// + + Float_t tmaxfd = 0.1; // 1.0; // Degree + Float_t stemax = 1.0; // cm + Float_t deemax = 0.1; // 30.0; // Fraction of particle's energy 0<deemax<=1 + Float_t epsil = 1.0E-4; // 1.0; // cm + Float_t stmin = 0.0; // cm "Default value used" + + Float_t tmaxfdSi = 0.1; // .10000E+01; // Degree + Float_t stemaxSi = 0.0075; // .10000E+01; // cm + Float_t deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1 + Float_t epsilSi = 1.0E-4; // .10000E+01; + Float_t stminSi = 0.0; // cm "Default value used" + + Float_t tmaxfdAir = 0.1; // .10000E+01; // Degree + Float_t stemaxAir = .10000E+01; // cm + Float_t deemaxAir = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1 + Float_t epsilAir = 1.0E-4; // .10000E+01; + Float_t stminAir = 0.0; // cm "Default value used" + + // AIR + Float_t aAir[4] = {12.0107, 14.0067, 15.9994, 39.948}; + Float_t zAir[4] = {6., 7., 8., 18.}; + Float_t wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827}; + Float_t dAir = 1.20479E-3; + + // Water + Float_t aWater[2] = {1.00794, 15.9994}; + Float_t zWater[2] = {1., 8.}; + Float_t wWater[2] = {0.111894, 0.888106}; + Float_t dWater = 1.0; + + // PEEK CF30 + Float_t aPEEK[3] = {12.0107, 1.00794, 15.9994}; + Float_t zPEEK[3] = {6., 1., 8.}; + Float_t wPEEK[3] = {19., 12., 3}; + Float_t dPEEK = 1.32; + + // Kapton + Float_t aKapton[4] = {1.00794, 12.0107, 14.010, 15.9994}; + Float_t zKapton[4] = {1., 6., 7., 8.}; + Float_t wKapton[4] = {0.026362, 0.69113, 0.07327, 0.209235}; + Float_t dKapton = 1.42; + + // Tungsten Carbide + Float_t aWC[2] = {183.84, 12.0107}; + Float_t zWC[2] = {74, 6}; + Float_t wWC[2] = {0.5, 0.5}; + Float_t dWC = 15.63; + + // BEOL (Metal interconnection stack in Si sensors) + Float_t aBEOL[3] = {26.982, 28.086, 15.999}; + Float_t zBEOL[3] = {13, 14, 8}; // Al, Si, O + Float_t wBEOL[3] = {0.170, 0.388, 0.442}; + Float_t dBEOL = 2.28; + + // Inox 304 + Float_t aInox304[4] = {12.0107, 51.9961, 58.6928, 55.845}; + Float_t zInox304[4] = {6., 24., 28, 26}; // C, Cr, Ni, Fe + Float_t wInox304[4] = {0.0003, 0.18, 0.10, 0}; // [3] will be computed + Float_t dInox304 = 7.85; + + // Ceramic (for IB capacitors) (BaTiO3) + 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; + + // Rohacell (C9 H13 N1 O2) + Float_t aRohac[4] = {12.01, 1.01, 14.010, 16.}; + Float_t zRohac[4] = {6., 1., 7., 8.}; + Float_t wRohac[4] = {9., 13., 1., 2.}; + Float_t dRohac = 0.05; + + 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); + + o2::base::Detector::Mixture(2, "WATER$", aWater, zWater, dWater, 2, wWater); + o2::base::Detector::Medium(2, "WATER$", 2, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + o2::base::Detector::Material(3, "SI$", 0.28086E+02, 0.14000E+02, 0.23300E+01, 0.93600E+01, 0.99900E+03); + o2::base::Detector::Medium(3, "SI$", 3, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + o2::base::Detector::Material(4, "BERILLIUM$", 9.01, 4., 1.848, 35.3, 36.7); // From AliPIPEv3 + o2::base::Detector::Medium(4, "BERILLIUM$", 4, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + o2::base::Detector::Material(5, "COPPER$", 0.63546E+02, 0.29000E+02, 0.89600E+01, 0.14300E+01, 0.99900E+03); + o2::base::Detector::Medium(5, "COPPER$", 5, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // needed for STAVE , Carbon, kapton, Epoxy, flexcable + + // AliceO2::Base::Detector::Material(6,"CARBON$",12.0107,6,2.210,999,999); + o2::base::Detector::Material(6, "CARBON$", 12.0107, 6, 2.210 / 1.3, 999, 999); + o2::base::Detector::Medium(6, "CARBON$", 6, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + o2::base::Detector::Mixture(7, "KAPTON(POLYCH2)$", aKapton, zKapton, dKapton, 4, wKapton); + o2::base::Detector::Medium(7, "KAPTON(POLYCH2)$", 7, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // values below modified as compared to source AliITSv11 ! + + // BEOL (Metal interconnection stack in Si sensors) + o2::base::Detector::Mixture(29, "METALSTACK$", aBEOL, zBEOL, dBEOL, 3, wBEOL); + o2::base::Detector::Medium(29, "METALSTACK$", 29, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // Glue between IB chip and FPC: density reduced to take into account + // empty spaces (160 glue spots/chip , diam. 1 spot = 1 mm) + o2::base::Detector::Material(30, "GLUE_IBFPC$", 12.011, 6, 1.05 * 0.3, 999, 999); + o2::base::Detector::Medium(30, "GLUE_IBFPC$", 30, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // Ceramic for IB capacitors (nmat < 0 => wmat contains number of atoms) + o2::base::Detector::Mixture(31, "CERAMIC$", aCeramic, zCeramic, dCeramic, -3, wCeramic); + o2::base::Detector::Medium(31, "CERAMIC$", 31, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // All types of carbon + // Unidirectional prepreg + o2::base::Detector::Material(8, "K13D2U2k$", 12.0107, 6, 1.643, 999, 999); + o2::base::Detector::Medium(8, "K13D2U2k$", 8, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Material(17, "K13D2U120$", 12.0107, 6, 1.583, 999, 999); + o2::base::Detector::Medium(17, "K13D2U120$", 17, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Carbon prepreg woven + o2::base::Detector::Material(18, "F6151B05M$", 12.0107, 6, 2.133, 999, 999); + o2::base::Detector::Medium(18, "F6151B05M$", 18, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Impregnated thread + o2::base::Detector::Material(9, "M60J3K$", 12.0107, 6, 2.21, 999, 999); + o2::base::Detector::Medium(9, "M60J3K$", 9, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Impregnated thread + o2::base::Detector::Material(10, "M55J6K$", 12.0107, 6, 1.63, 999, 999); + o2::base::Detector::Medium(10, "M55J6K$", 10, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Fabric(0/90) + o2::base::Detector::Material(11, "T300$", 12.0107, 6, 1.725, 999, 999); + o2::base::Detector::Medium(11, "T300$", 11, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // AMEC Thermasol + o2::base::Detector::Material(12, "FGS003$", 12.0107, 6, 1.6, 999, 999); + o2::base::Detector::Medium(12, "FGS003$", 12, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Carbon fleece + o2::base::Detector::Material(13, "CarbonFleece$", 12.0107, 6, 0.4, 999, 999); + o2::base::Detector::Medium(13, "CarbonFleece$", 13, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, + stminSi); + // Rohacell + o2::base::Detector::Mixture(32, "ROHACELL$", aRohac, zRohac, dRohac, -4, wRohac); + o2::base::Detector::Medium(32, "ROHACELL$", 32, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // PEEK CF30 + o2::base::Detector::Mixture(19, "PEEKCF30$", aPEEK, zPEEK, dPEEK, -3, wPEEK); + o2::base::Detector::Medium(19, "PEEKCF30$", 19, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Flex cable + Float_t aFCm[5] = {12.0107, 1.00794, 14.0067, 15.9994, 26.981538}; + Float_t zFCm[5] = {6., 1., 7., 8., 13.}; + Float_t wFCm[5] = {0.520088819984, 0.01983871336, 0.0551367996, 0.157399667056, 0.247536}; + // Float_t dFCm = 1.6087; // original + // Float_t dFCm = 2.55; // conform with STAR + Float_t dFCm = 2.595; // conform with Corrado + + o2::base::Detector::Mixture(14, "FLEXCABLE$", aFCm, zFCm, dFCm, 5, wFCm); + o2::base::Detector::Medium(14, "FLEXCABLE$", 14, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // AliceO2::Base::Detector::Material(7,"GLUE$",0.12011E+02,0.60000E+01,0.1930E+01/2.015,999,999); + // // original + o2::base::Detector::Material(15, "GLUE$", 12.011, 6, 1.93 / 2.015, 999, 999); // conform with ATLAS, Corrado, Stefan + o2::base::Detector::Medium(15, "GLUE$", 15, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + o2::base::Detector::Material(16, "ALUMINUM$", 0.26982E+02, 0.13000E+02, 0.26989E+01, 0.89000E+01, 0.99900E+03); + o2::base::Detector::Medium(16, "ALUMINUM$", 16, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + o2::base::Detector::Mixture(20, "TUNGCARB$", aWC, zWC, dWC, 2, wWC); + o2::base::Detector::Medium(20, "TUNGCARB$", 20, 0, ifield, fieldm, tmaxfd, stemax, deemaxSi, epsilSi, stminSi); + + wInox304[3] = 1. - wInox304[0] - wInox304[1] - wInox304[2]; + o2::base::Detector::Mixture(21, "INOX304$", aInox304, zInox304, dInox304, 4, wInox304); + o2::base::Detector::Medium(21, "INOX304$", 21, 0, ifield, fieldm, tmaxfd, stemax, deemaxSi, epsilSi, stminSi); + + // Tungsten (for gamma converter rods) + o2::base::Detector::Material(28, "TUNGSTEN$", 183.84, 74, 19.25, 999, 999); + o2::base::Detector::Medium(28, "TUNGSTEN$", 28, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); +} + +void Detector::EndOfEvent() { Reset(); } + +void Detector::Register() +{ + // This will create a branch in the output tree called Hit, setting the last + // parameter to kFALSE means that this collection will not be written to the file, + // it will exist only during the simulation + + if (FairRootManager::Instance()) { + FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, kTRUE); + } +} + +void Detector::Reset() +{ + if (!o2::utils::ShmManager::Instance().isOperational()) { + mHits->clear(); + } +} + +void Detector::defineWrapperVolume(Int_t id, Double_t rmin, Double_t rmax, Double_t zspan) +{ + // set parameters of id-th wrapper volume + if (id >= sNumberOfWrapperVolumes || id < 0) { + LOG(FATAL) << "id " << id << " of wrapper volume is not in 0-" << sNumberOfWrapperVolumes - 1 << " range"; + } + + mWrapperMinRadius[id] = rmin; + mWrapperMaxRadius[id] = rmax; + mWrapperZSpan[id] = zspan; +} + +void Detector::defineLayer(Int_t nlay, double phi0, Double_t r, Int_t nstav, Int_t nunit, Double_t lthick, + Double_t dthick, UInt_t dettypeID, Int_t buildLevel) +{ + // Sets the layer parameters + // Inputs: + // nlay layer number + // phi0 layer phi0 + // r layer radius + // nstav number of staves + // nunit IB: number of chips per stave + // OB: number of modules per half stave + // lthick stave thickness (if omitted, defaults to 0) + // dthick detector thickness (if omitted, defaults to 0) + // dettypeID ?? + // buildLevel (if 0, all geometry is build, used for material budget studies) + // Outputs: + // none. + // Return: + // none. + + LOG(INFO) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit + << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; + + if (nlay >= mTotalNumberOfLayers || nlay < 0) { + LOG(ERROR) << "Wrong layer number " << nlay; + return; + } + + mTurboLayer[nlay] = kFALSE; + mLayerPhi0[nlay] = phi0; + mLayerRadii[nlay] = r; + mStavePerLayer[nlay] = nstav; + mUnitPerStave[nlay] = nunit; + mChipThickness[nlay] = lthick; + mDetectorThickness[nlay] = dthick; + mChipTypeID[nlay] = dettypeID; + mBuildLevel[nlay] = buildLevel; +} + +void Detector::defineInnerLayerTRK(Int_t nlay, Double_t r, Double_t zlen, + Double_t dthick, UInt_t dettypeID, Int_t buildLevel) +{ + // Sets the layer parameters + // Inputs: + // nlay layer number + // r layer radius + // zlen layer length + // dthick detector thickness (if omitted, defaults to 0) + // dettypeID ?? + // buildLevel (if 0, all geometry is build, used for material budget studies) + // Outputs: + // none. + // Return: + // none. + + LOG(INFO) << "L# " << nlay << " with TRK geo R:" << r + << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; + + if (nlay >= mTotalNumberOfLayers || nlay < 0) { + LOG(ERROR) << "Wrong layer number " << nlay; + return; + } + + mTurboLayer[nlay] = kFALSE; + mTRKLayer[nlay] = kTRUE; + mLayerRadii[nlay] = r; + mLayerZLen[nlay] = zlen; + mDetectorThickness[nlay] = dthick; + mChipTypeID[nlay] = dettypeID; + mBuildLevel[nlay] = buildLevel; + LOG(INFO) << "Added layer: " << nlay << std::endl; +} + +void Detector::getLayerParameters(Int_t nlay, Double_t& phi0, Double_t& r, Int_t& nstav, Int_t& nmod, Double_t& width, + Double_t& tilt, Double_t& lthick, Double_t& dthick, UInt_t& dettype) const +{ + // Gets the layer parameters + // Inputs: + // nlay layer number + // Outputs: + // phi0 phi of 1st stave + // r layer radius + // nstav number of staves + // nmod IB: number of chips per stave + // OB: number of modules per half stave + // width stave width + // tilt stave tilt angle + // lthick stave thickness + // dthick detector thickness + // dettype detector type + // Return: + // none. + + if (nlay >= mTotalNumberOfLayers || nlay < 0) { + LOG(ERROR) << "Wrong layer number " << nlay; + return; + } + + phi0 = mLayerPhi0[nlay]; + r = mLayerRadii[nlay]; + nstav = mStavePerLayer[nlay]; + nmod = mUnitPerStave[nlay]; + width = mStaveWidth[nlay]; + tilt = mStaveTilt[nlay]; + lthick = mChipThickness[nlay]; + dthick = mDetectorThickness[nlay]; + dettype = mChipTypeID[nlay]; +} + +TGeoVolume* Detector::createWrapperVolume(Int_t id) +{ + // Creates an air-filled wrapper cylindrical volume + // For OB a Pcon is needed to host the support rings + // while avoiding overlaps with MFT structures and OB cones + + const Double_t suppRingAZlen = 4.; + const Double_t coneRingARmax = 33.96; + const Double_t coneRingAZlen = 5.6; + const Double_t suppRingCZlen[3] = {4.8, 4.0, 2.4}; + const Double_t suppRingsRmin[3] = {23.35, 20.05, 35.4}; + + if (mWrapperMinRadius[id] < 0 || mWrapperMaxRadius[id] < 0 || mWrapperZSpan[id] < 0) { + LOG(FATAL) << "Wrapper volume " << id << " was requested but not defined"; + } + + // Now create the actual shape and volume + TGeoShape* tube; + Double_t zlen; + switch (id) { + case 0: // IB Layer 0,1,2: simple cylinder + { + TGeoTube* wrap = new TGeoTube(mWrapperMinRadius[id], mWrapperMaxRadius[id], mWrapperZSpan[id] / 2.); + tube = (TGeoShape*)wrap; + } break; + case 1: // MB Layer 3,4: complex Pcon to avoid MFT overlaps + { + TGeoPcon* wrap = new TGeoPcon(0, 360, 6); + zlen = mWrapperZSpan[id] / 2 + suppRingCZlen[0]; + wrap->DefineSection(0, -zlen, suppRingsRmin[0], mWrapperMaxRadius[id]); + zlen = mWrapperZSpan[id] / 2 + suppRingCZlen[1]; + wrap->DefineSection(1, -zlen, suppRingsRmin[0], mWrapperMaxRadius[id]); + wrap->DefineSection(2, -zlen, suppRingsRmin[1], mWrapperMaxRadius[id]); + wrap->DefineSection(3, -mWrapperZSpan[id] / 2., suppRingsRmin[1], mWrapperMaxRadius[id]); + wrap->DefineSection(4, -mWrapperZSpan[id] / 2., mWrapperMinRadius[id], mWrapperMaxRadius[id]); + zlen = mWrapperZSpan[id] / 2 + suppRingAZlen; + wrap->DefineSection(5, zlen, mWrapperMinRadius[id], mWrapperMaxRadius[id]); + tube = (TGeoShape*)wrap; + } break; + case 2: // OB Layer 5,6: simpler Pcon to avoid OB cones overlaps + { + TGeoPcon* wrap = new TGeoPcon(0, 360, 6); + zlen = mWrapperZSpan[id] / 2; + wrap->DefineSection(0, -zlen, suppRingsRmin[2], mWrapperMaxRadius[id]); + zlen -= suppRingCZlen[2]; + wrap->DefineSection(1, -zlen, suppRingsRmin[2], mWrapperMaxRadius[id]); + wrap->DefineSection(2, -zlen, mWrapperMinRadius[id], mWrapperMaxRadius[id]); + zlen = mWrapperZSpan[id] / 2 - coneRingAZlen; + wrap->DefineSection(3, zlen, mWrapperMinRadius[id], mWrapperMaxRadius[id]); + wrap->DefineSection(4, zlen, coneRingARmax, mWrapperMaxRadius[id]); + wrap->DefineSection(5, mWrapperZSpan[id] / 2, coneRingARmax, mWrapperMaxRadius[id]); + tube = (TGeoShape*)wrap; + } break; + default: // Can never happen, keeps gcc quiet + break; + } + + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + + char volnam[30]; + snprintf(volnam, 29, "%s%d", GeometryTGeo::getITSWrapVolPattern(), id); + + auto* wrapper = new TGeoVolume(volnam, tube, medAir); + + return wrapper; +} + +void Detector::ConstructGeometry() +{ + // Create the detector materials + createMaterials(); + // Construct the detector geometry + constructDetectorGeometry(); +} + +void Detector::constructDetectorGeometry() +{ + // Create the geometry and insert it in the mother volume ITSV + TGeoManager* geoManager = gGeoManager; + + TGeoVolume* vALIC = geoManager->GetVolume("barrel"); + + if (!vALIC) { + LOG(FATAL) << "Could not find the top volume"; + } + new TGeoVolumeAssembly(GeometryTGeo::getITSVolPattern()); + TGeoVolume* vITSV = geoManager->GetVolume(GeometryTGeo::getITSVolPattern()); + vALIC->AddNode(vITSV, 2, new TGeoTranslation(0, 30., 0)); // Copy number is 2 to cheat AliGeoManager::CheckSymNamesLUT + + const Int_t kLength = 100; + Char_t vstrng[kLength] = "xxxRS"; //? + vITSV->SetTitle(vstrng); + + // Check that we have all needed parameters + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + if (mLayerRadii[j] <= 0) { + LOG(FATAL) << "Wrong layer radius for layer " << j << "(" << mLayerRadii[j] << ")"; + } + if (mStavePerLayer[j] <= 0 && !mTRKLayer[j]) { + LOG(FATAL) << "Wrong number of staves for layer " << j << "(" << mStavePerLayer[j] << ")"; + } + if (mUnitPerStave[j] <= 0 && !mTRKLayer[j]) { + LOG(FATAL) << "Wrong number of chips for layer " << j << "(" << mUnitPerStave[j] << ")"; + } + if (mChipThickness[j] < 0 && !mTRKLayer[j]) { + LOG(FATAL) << "Wrong chip thickness for layer " << j << "(" << mChipThickness[j] << ")"; + } + if (mTurboLayer[j] && mStaveWidth[j] <= 0) { + LOG(FATAL) << "Wrong stave width for layer " << j << "(" << mStaveWidth[j] << ")"; + } + if (mDetectorThickness[j] < 0) { + LOG(FATAL) << "Wrong Sensor thickness for layer " << j << "(" << mDetectorThickness[j] << ")"; + } + + if (j > 0 && // Always check IB, check OB only if present + ((j < mNumberOfInnerLayers) || mCreateOuterBarrel)) { + if (mLayerRadii[j] <= mLayerRadii[j - 1]) { + LOG(FATAL) << "Layer " << j << " radius (" << mLayerRadii[j] << ") is smaller than layer " << j - 1 + << " radius (" << mLayerRadii[j - 1] << ")"; + } + } + + if (mChipThickness[j] == 0 && !mTRKLayer[j]) { + LOG(INFO) << "Chip thickness for layer " << j << " not set, using default"; + } + } + + // Create the wrapper volumes + TGeoVolume** wrapVols = nullptr; + + if (sNumberOfWrapperVolumes && mCreateOuterBarrel) { + wrapVols = new TGeoVolume*[sNumberOfWrapperVolumes]; + for (int id = 1; id < sNumberOfWrapperVolumes; id++) { + wrapVols[id] = createWrapperVolume(id); + vITSV->AddNode(wrapVols[id], 1, nullptr); + } + } + if (!mCreateOuterBarrel) { + mTotalNumberOfLayers = mNumberOfInnerLayers; + } + + // Now create the actual geometry + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + TGeoVolume* dest = vITSV; + mWrapperLayerId[j] = -1; + + if (mTurboLayer[j]) { + mGeometry[j] = new V3Layer(j, kTRUE, kFALSE); + mGeometry[j]->setStaveWidth(mStaveWidth[j]); + mGeometry[j]->setStaveTilt(mStaveTilt[j]); + } else { + mGeometry[j] = new V3Layer(j, kFALSE); + } + + mGeometry[j]->setNumberOfInnerLayers(mNumberOfInnerLayers); + + if (mTRKLayer[j]) { + mGeometry[j]->setIsTRK(kTRUE); + mGeometry[j]->setIBModuleZLength(mLayerZLen[j]); + } + + mGeometry[j]->setPhi0(mLayerPhi0[j]); + mGeometry[j]->setRadius(mLayerRadii[j]); + mGeometry[j]->setNumberOfStaves(mStavePerLayer[j]); + mGeometry[j]->setNumberOfUnits(mUnitPerStave[j]); + mGeometry[j]->setChipType(mChipTypeID[j]); + mGeometry[j]->setBuildLevel(mBuildLevel[j]); + + if (j < mNumberOfInnerLayers) { + mGeometry[j]->setStaveModel(mStaveModelInnerBarrel); + } else { + mGeometry[j]->setStaveModel(mStaveModelOuterBarrel); + } + + LOG(DEBUG1) << "mBuildLevel: " << mBuildLevel[j]; + + if (mChipThickness[j] != 0) { + mGeometry[j]->setChipThick(mChipThickness[j]); + } + if (mDetectorThickness[j] != 0) { + mGeometry[j]->setSensorThick(mDetectorThickness[j]); + } + + if (mCreateOuterBarrel && j >= mNumberOfInnerLayers) { + for (int iw = 0; iw < sNumberOfWrapperVolumes; iw++) { + if (mLayerRadii[j] > mWrapperMinRadius[iw] && mLayerRadii[j] < mWrapperMaxRadius[iw]) { + LOG(DEBUG) << "Will embed layer " << j << " in wrapper volume " << iw; + + dest = wrapVols[iw]; + mWrapperLayerId[j] = iw; + break; + } + } + } + mGeometry[j]->createLayer(dest); + } + + // Finally create the services + mServicesGeometry = new V3Services(); + + // createInnerBarrelServices(wrapVols[0]); + // createMiddlBarrelServices(wrapVols[1]); + // createOuterBarrelServices(wrapVols[2]); + // createOuterBarrelSupports(vITSV); + + // TEMPORARY - These routines will be obsoleted once the new services are completed - TEMPORARY + // createServiceBarrel(kTRUE, wrapVols[0]); + // createServiceBarrel(kFALSE, wrapVols[2]); + delete[] wrapVols; // delete pointer only, not the volumes +} + +void Detector::createInnerBarrelServices(TGeoVolume* motherVolume) +{ + // + // Creates the Inner Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the services + // + // Output: + // + // Return: + // + // Created: 15 May 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // Updated: 19 Jun 2019 Mario Sitta IB Side A added + // Updated: 21 Oct 2019 Mario Sitta CYSS added + // + + // Create the End Wheels on Side A + TGeoVolume* endWheelsA = mServicesGeometry->createIBEndWheelsSideA(); + + motherVolume->AddNode(endWheelsA, 1, nullptr); + + // Create the End Wheels on Side C + TGeoVolume* endWheelsC = mServicesGeometry->createIBEndWheelsSideC(); + + motherVolume->AddNode(endWheelsC, 1, nullptr); + + // Create the CYSS Assembly (i.e. the supporting half cylinder and cone) + TGeoVolume* cyss = mServicesGeometry->createCYSSAssembly(); + + motherVolume->AddNode(cyss, 1, nullptr); +} + +void Detector::createMiddlBarrelServices(TGeoVolume* motherVolume) +{ + // + // Creates the Middle Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the services + // + // Output: + // + // Return: + // + // Created: 24 Sep 2019 Mario Sitta + // + + // Create the End Wheels on Side A + mServicesGeometry->createMBEndWheelsSideA(motherVolume); + + // Create the End Wheels on Side C + mServicesGeometry->createMBEndWheelsSideC(motherVolume); +} + +void Detector::createOuterBarrelServices(TGeoVolume* motherVolume) +{ + // + // Creates the Outer Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the services + // + // Output: + // + // Return: + // + // Created: 27 Sep 2019 Mario Sitta + // + + // Create the End Wheels on Side A + mServicesGeometry->createOBEndWheelsSideA(motherVolume); + + // Create the End Wheels on Side C + mServicesGeometry->createOBEndWheelsSideC(motherVolume); +} + +void Detector::createOuterBarrelSupports(TGeoVolume* motherVolume) +{ + // + // Creates the Outer Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the supports + // + // Output: + // + // Return: + // + // Created: 26 Jan 2020 Mario Sitta + // + + // Create the Cone on Side A + mServicesGeometry->createOBConeSideA(motherVolume); + + // Create the Cone on Side C + mServicesGeometry->createOBConeSideC(motherVolume); +} + +// Service Barrel +void Detector::createServiceBarrel(const Bool_t innerBarrel, TGeoVolume* dest, const TGeoManager* mgr) +{ + // Creates the Service Barrel (as a simple cylinder) for IB and OB + // Inputs: + // innerBarrel : if true, build IB service barrel, otherwise for OB + // dest : the mother volume holding the service barrel + // mgr : the gGeoManager pointer (used to get the material) + // + + Double_t rminIB = 4.7; + Double_t rminOB = 43.9; + Double_t zLenOB; + Double_t cInt = 0.22; // dimensioni cilindro di supporto interno + Double_t cExt = 1.00; // dimensioni cilindro di supporto esterno + // Double_t phi1 = 180; + // Double_t phi2 = 360; + + TGeoMedium* medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + + if (innerBarrel) { + zLenOB = ((TGeoTube*)(dest->GetShape()))->GetDz(); + // TGeoTube*ibSuppSh = new TGeoTubeSeg(rminIB,rminIB+cInt,zLenOB,phi1,phi2); + auto* ibSuppSh = new TGeoTube(rminIB, rminIB + cInt, zLenOB); + auto* ibSupp = new TGeoVolume("ibSuppCyl", ibSuppSh, medCarbonFleece); + dest->AddNode(ibSupp, 1); + } else { + zLenOB = ((TGeoTube*)(dest->GetShape()))->GetDz(); + auto* obSuppSh = new TGeoTube(rminOB, rminOB + cExt, zLenOB); + auto* obSupp = new TGeoVolume("obSuppCyl", obSuppSh, medCarbonFleece); + dest->AddNode(obSupp, 1); + } + + return; +} + +void Detector::addAlignableVolumes() const +{ + // + // Creates entries for alignable volumes associating the symbolic volume + // name with the corresponding volume path. + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + LOG(INFO) << "Add ITS alignable volumes"; + + if (!gGeoManager) { + LOG(FATAL) << "TGeoManager doesn't exist !"; + return; + } + + TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getITSVolPattern()); + TString sname = GeometryTGeo::composeSymNameTRK(); + + LOG(DEBUG) << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + Int_t lastUID = 0; + for (Int_t lr = 0; lr < mTotalNumberOfLayers; lr++) { + addAlignableVolumesLayer(lr, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a Layer and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString wrpV = + mWrapperLayerId[lr] != -1 ? Form("%s%d_1", GeometryTGeo::getITSWrapVolPattern(), mWrapperLayerId[lr]) : ""; + TString path; + if (mCreateOuterBarrel && (lr >= mNumberOfInnerLayers)) { + path = Form("%s/%s/%s%d_1", parent.Data(), wrpV.Data(), GeometryTGeo::getITSLayerPattern(), lr); + } else { + path = Form("%s/%s%d_1", parent.Data(), GeometryTGeo::getITSLayerPattern(), lr); + } + TString sname = GeometryTGeo::composeSymNameLayer(lr); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nstaves = lrobj->getNumberOfStavesPerParent(); + for (int st = 0; st < nstaves; st++) { + addAlignableVolumesStave(lr, st, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesStave(Int_t lr, Int_t st, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a Stave and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + TString path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getITSStavePattern(), lr, st); + TString sname = GeometryTGeo::composeSymNameStave(lr, st); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nhstave = lrobj->getNumberOfHalfStavesPerParent(); + Int_t start = nhstave > 0 ? 0 : -1; + for (Int_t sst = start; sst < nhstave; sst++) { + addAlignableVolumesHalfStave(lr, st, sst, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a HalfStave (if any) and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = parent; + if (hst >= 0) { + path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getITSHalfStavePattern(), lr, hst); + TString sname = GeometryTGeo::composeSymNameHalfStave(lr, st, hst); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nmodules = lrobj->getNumberOfModulesPerParent(); + Int_t start = nmodules > 0 ? 0 : -1; + for (Int_t md = start; md < nmodules; md++) { + addAlignableVolumesModule(lr, st, hst, md, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesModule(Int_t lr, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a Module (if any) and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = parent; + if (md >= 0) { + path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getITSModulePattern(), lr, md); + TString sname = GeometryTGeo::composeSymNameModule(lr, st, hst, md); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nchips = lrobj->getNumberOfChipsPerParent(); + for (Int_t ic = 0; ic < nchips; ic++) { + addAlignableVolumesChip(lr, st, hst, md, ic, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesChip(Int_t lr, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, + Int_t& lastUID) const +{ + // + // Add alignable volumes for a Chip + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getITSChipPattern(), lr, ch); + TString sname = GeometryTGeo::composeSymNameChip(lr, st, hst, md, ch); + Int_t modUID = chipVolUID(lastUID++); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname, path.Data(), modUID)) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + return; +} + +void Detector::defineSensitiveVolumes() +{ + TGeoManager* geoManager = gGeoManager; + TGeoVolume* v; + + TString volumeName; + + // The names of the ITS sensitive volumes have the format: ITSUSensor(0...sNumberLayers-1) + for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { + volumeName = GeometryTGeo::getITSSensorPattern() + TString::Itoa(j, 10); + v = geoManager->GetVolume(volumeName.Data()); + AddSensitiveVolume(v); + } +} + +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) +{ + mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus); + return &(mHits->back()); +} + +void Detector::Print(std::ostream* os) const +{ + // Standard output format for this class. + // Inputs: + // ostream *os The output stream + // Outputs: + // none. + // Return: + // none. + +#if defined __GNUC__ +#if __GNUC__ > 2 + std::ios::fmtflags fmt; +#else + Int_t fmt; +#endif +#else +#if defined __ICC || defined __ECC || defined __xlC__ + ios::fmtflags fmt; +#else + Int_t fmt; +#endif +#endif + // RS: why do we need to pring this garbage? + + // fmt = os->setf(std::ios::scientific); // set scientific floating point output + // fmt = os->setf(std::ios::hex); // set hex for mStatus only. + // fmt = os->setf(std::ios::dec); // every thing else decimel. + // *os << mModule << " "; + // *os << mEnergyDepositionStep << " " << mTof; + // *os << " " << mStartingStepX << " " << mStartingStepY << " " << mStartingStepZ; + // *os << " " << endl; + // os->flags(fmt); // reset back to old formating. + return; +} + +void Detector::Read(std::istream* is) +{ + // Standard input format for this class. + // Inputs: + // istream *is the input stream + // Outputs: + // none. + // Return: + // none. + // RS no need to read garbage + return; +} + +std::ostream& operator<<(std::ostream& os, Detector& p) +{ + // Standard output streaming function. + // Inputs: + // ostream os The output stream + // Detector p The his to be printed out + // Outputs: + // none. + // Return: + // The input stream + + p.Print(&os); + return os; +} + +std::istream& operator>>(std::istream& is, Detector& r) +{ + // Standard input streaming function. + // Inputs: + // istream is The input stream + // Detector p The Detector class to be filled from this input stream + // Outputs: + // none. + // Return: + // The input stream + + r.Read(&is); + return is; +} + +ClassImp(o2::trk::Detector); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h new file mode 100644 index 0000000000000..959b89136db28 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -0,0 +1,24 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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::trk::V11Geometry + ; +#pragma link C++ class o2::trk::V1Layer + ; +#pragma link C++ class o2::trk::V3Layer + ; +#pragma link C++ class o2::trk::V3Services + ; +#pragma link C++ class o2::trk::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::trk::Detector> + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/V11Geometry.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V11Geometry.cxx new file mode 100644 index 0000000000000..a341a322ff539 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V11Geometry.cxx @@ -0,0 +1,1229 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V11Geometry.cxx +/// \brief Implementation of the V11Geometry class + +#include "TRKSimulation/V11Geometry.h" + +#include "FairLogger.h" // for LOG + +#include <TArc.h> // for TArc +#include <TArrow.h> // for TArrow +#include <TCanvas.h> // for TCanvas +#include <TGeoArb8.h> // for TGeoArb8 +#include <TGeoElement.h> // for TGeoElement +#include <TGeoMaterial.h> // for TGeoMixture, TGeoMaterial, etc +#include <TGeoPcon.h> // for TGeoPcon +#include <TGeoCone.h> // for TGeoConSeg +#include <TLine.h> // for TLine +#include <TPolyLine.h> // for TPolyLine +#include <TPolyMarker.h> // for TPolyMarker +#include <TText.h> // for TText +#include "TMath.h" // for DegToRad, Cos, Sqrt, ATan2, Sin, Tan, Pi, etc +#include "TMathBase.h" // for Max, Min, Abs +#include <TGeoTube.h> // for TGeoTubeSeg + +#include <cstdio> // for printf, snprintf +#include <Riostream.h> + +using std::cin; +using std::cout; +using std::endl; + +using namespace o2::trk; + +ClassImp(o2::trk::V11Geometry); + +const Double_t V11Geometry::sMicron = 1.0E-4; +const Double_t V11Geometry::sMm = 0.10; +const Double_t V11Geometry::sCm = 1.00; +const Double_t V11Geometry::sDegree = 1.0; +const Double_t V11Geometry::sRadian = 180. / 3.14159265358979323846; +const Double_t V11Geometry::sGCm3 = 1.0; // assume default is g/cm^3 +const Double_t V11Geometry::sKgm3 = 1.0E+3; // assume Kg/m^3 +const Double_t V11Geometry::sKgdm3 = 1.0; // assume Kg/dm^3 +const Double_t V11Geometry::sCelsius = 1.0; // Assume default is C +const Double_t V11Geometry::sPascal = 1.0E-3; // Assume kPascal +const Double_t V11Geometry::sKPascal = 1.0; // Asume kPascal +const Double_t V11Geometry::sEV = 1.0E-9; // GeV default +const Double_t V11Geometry::sKEV = 1.0e-6; // GeV default +const Double_t V11Geometry::sMEV = 1.0e-3; // GeV default +const Double_t V11Geometry::sGEV = 1.0; // GeV default + +void V11Geometry::intersectLines(Double_t m, Double_t x0, Double_t y0, Double_t n, Double_t x1, + Double_t y1, Double_t& xi, Double_t& yi) const +{ + if (TMath::Abs(m - n) < 0.000001) { + LOG(ERROR) << "Lines are parallel: m = " << m << " n = " << n; + return; + } + + xi = (y1 - n * x1 - y0 + m * x0) / (m - n); + yi = y0 + m * (xi - x0); + + return; +} + +Bool_t V11Geometry::intersectCircle(Double_t m, Double_t x0, Double_t y0, Double_t rr, Double_t xc, + Double_t yc, Double_t& xi1, Double_t& yi1, Double_t& xi2, + Double_t& yi2) +{ + Double_t p = m * x0 - y0; + Double_t q = m * m + 1; + + p = p - m * xc + yc; + + Double_t delta = m * m * p * p - q * (p * p - rr * rr); + + if (delta < 0) { + return kFALSE; + } else { + Double_t root = TMath::Sqrt(delta); + xi1 = (m * p + root) / q + xc; + xi2 = (m * p - root) / q + xc; + yi1 = m * (xi1 - x0) + y0; + yi2 = m * (xi2 - x0) + y0; + return kTRUE; + } +} + +Double_t V11Geometry::yFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x) + const +{ + if (x0 == x1 && y0 == y1) { + printf( + "Error: V11Geometry::Yfrom2Ponts The two points are " + "the same (%e,%e) and (%e,%e)", + x0, y0, x1, y1); + return 0.0; + } // end if + if (x0 == x1) { + printf( + "Warning: V11Geometry::yFrom2Points x0=%e == x1=%e. " + "line vertical " + "returning mean y", + x0, x1); + return 0.5 * (y0 + y1); + } // end if x0==x1 + Double_t m = (y0 - y1) / (x0 - x1); + return m * (x - x0) + y0; +} + +Double_t V11Geometry::xFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t y) + const +{ + if (x0 == x1 && y0 == y1) { + printf( + "Error: V11Geometry::Yfrom2Ponts The two points are " + "the same (%e,%e) and (%e,%e)", + x0, y0, x1, y1); + return 0.0; + } // end if + if (y0 == y1) { + printf( + "Warrning: V11Geometry::yFrom2Points y0=%e == y1=%e. " + "line horizontal returning mean x", + y0, y1); + return 0.5 * (x0 + x1); + } // end if y0==y1 + Double_t m = (x0 - x1) / (y0 - y1); + return m * (y - y0) + x0; +} + +Double_t V11Geometry::rMaxFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const +{ + Double_t d0, d1, d2, r; + + d0 = p->GetRmax(i1) - p->GetRmax(i2); // cout <<"L263: d0="<<d0<<endl; + d1 = z - p->GetZ(i2); // cout <<"L264: d1="<<d1<<endl; + d2 = p->GetZ(i1) - p->GetZ(i2); // cout <<"L265: d2="<<d2<<endl; + r = p->GetRmax(i2) + d1 * d0 / d2; // cout <<"L266: r="<<r<<endl; + return r; +} + +Double_t V11Geometry::rMinFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const +{ + return p->GetRmin(i2) + + (p->GetRmin(i1) - p->GetRmin(i2)) * (z - p->GetZ(i2)) / (p->GetZ(i1) - p->GetZ(i2)); +} + +Double_t V11Geometry::rFrom2Points(const Double_t* p, const Double_t* az, Int_t i1, Int_t i2, + Double_t z) const +{ + return p[i2] + (p[i1] - p[i2]) * (z - az[i2]) / (az[i1] - az[i2]); +} + +Double_t V11Geometry::zFrom2MinPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const +{ + return p->GetZ(i2) + + (p->GetZ(i1) - p->GetZ(i2)) * (r - p->GetRmin(i2)) / (p->GetRmin(i1) - p->GetRmin(i2)); +} + +Double_t V11Geometry::zFrom2MaxPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const +{ + return p->GetZ(i2) + + (p->GetZ(i1) - p->GetZ(i2)) * (r - p->GetRmax(i2)) / (p->GetRmax(i1) - p->GetRmax(i2)); +} + +Double_t V11Geometry::zFrom2Points(const Double_t* z, const Double_t* ar, Int_t i1, Int_t i2, + Double_t r) const +{ + return z[i2] + (z[i1] - z[i2]) * (r - ar[i2]) / (ar[i1] - ar[i2]); +} + +Double_t V11Geometry::rMaxFromZpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t z, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return -tantc * (z - p->GetZ(ip)) + p->GetRmax(ip) + th / costc; +} + +Double_t V11Geometry::rFromZpCone(const Double_t* ar, const Double_t* az, int ip, Double_t tc, + Double_t z, Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return -tantc * (z - az[ip]) + ar[ip] + th / costc; +} + +Double_t V11Geometry::rMinFromZpCone(const TGeoPcon* p, Int_t ip, Double_t tc, Double_t z, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return -tantc * (z - p->GetZ(ip)) + p->GetRmin(ip) + th / costc; +} + +Double_t V11Geometry::zFromRMaxpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return p->GetZ(ip) + (p->GetRmax(ip) + th / costc - r) / tantc; +} + +Double_t V11Geometry::zFromRMaxpCone(const Double_t* ar, const Double_t* az, int ip, Double_t tc, + Double_t r, Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return az[ip] + (ar[ip] + th / costc - r) / tantc; +} + +Double_t V11Geometry::zFromRMinpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return p->GetZ(ip) + (p->GetRmin(ip) + th / costc - r) / tantc; +} + +void V11Geometry::radiusOfCurvature(Double_t rc, Double_t theta0, Double_t z0, Double_t r0, + Double_t theta1, Double_t& z1, Double_t& r1) const +{ + z1 = rc * (TMath::Sin(theta1 * TMath::DegToRad()) - TMath::Sin(theta0 * TMath::DegToRad())) + z0; + r1 = rc * (TMath::Cos(theta1 * TMath::DegToRad()) - TMath::Cos(theta0 * TMath::DegToRad())) + r0; + return; +} + +void V11Geometry::insidePoint(const TGeoPcon* p, Int_t i1, Int_t i2, Int_t i3, Double_t c, + TGeoPcon* q, Int_t j1, Bool_t max) const +{ + Double_t x0, y0, x1, y1, x2, y2, x, y; + + if (max) { + c = -c; // cout <<"L394 c="<<c<<endl; + y0 = p->GetRmax(i1); + if (i1 == i2) { + y0 = p->GetRmin(i1); // cout <<"L396 y0="<<y0<<endl; + } + y1 = p->GetRmax(i2); // cout <<"L397 y1="<<y1<<endl; + y2 = p->GetRmax(i3); // cout <<"L398 y2="<<y2<<endl; + if (i2 == i3) { + y2 = p->GetRmin(i3); // cout <<"L399 y2="<<y2<<endl; + } + } else { // min + y0 = p->GetRmin(i1); // cout <<"L401 y0="<<y0<<endl; + y1 = p->GetRmin(i2); // cout <<"L402 y1="<<y1<<endl; + y2 = p->GetRmin(i3); + + if (i2 == i3) { + y2 = p->GetRmax(i3); // cout <<"L404 y2="<<y2<<endl; + } + } // end if + x0 = p->GetZ(i1); // cout <<"L406 x0="<<x0<<endl; + x1 = p->GetZ(i2); // cout <<"L407 x1="<<x1<<endl; + x2 = p->GetZ(i3); // cout <<"L408 x2="<<x2<<endl; + + insidePoint(x0, y0, x1, y1, x2, y2, c, x, y); + q->Z(j1) = x; + + if (max) { + q->Rmax(j1) = y; + } else { + q->Rmin(j1) = y; + } + return; +} + +void V11Geometry::insidePoint(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x2, + Double_t y2, Double_t c, Double_t& x, Double_t& y) const +{ + Double_t dx01, dx12, dy01, dy12, r01, r12, m; + + // printf("InsidePoint: x0=% #12.7g y0=% #12.7g x1=% #12.7g y1=% #12.7g " + // "x2=% #12.7g y2=% #12.7g c=% #12.7g ",x0,y0,x1,y2,x2,y2,c); + dx01 = x0 - x1; // cout <<"L410 dx01="<<dx01<<endl; + dx12 = x1 - x2; // cout <<"L411 dx12="<<dx12<<endl; + dy01 = y0 - y1; // cout <<"L412 dy01="<<dy01<<endl; + dy12 = y1 - y2; // cout <<"L413 dy12="<<dy12<<endl; + r01 = TMath::Sqrt(dy01 * dy01 + dx01 * dx01); // cout <<"L414 r01="<<r01<<endl; + r12 = TMath::Sqrt(dy12 * dy12 + dx12 * dx12); // cout <<"L415 r12="<<r12<<endl; + m = dx12 * dy01 - dy12 * dx01; + if (m * m < DBL_EPSILON) { // m == n + if (dy01 == 0.0) { // line are = + x = x1 + c; // cout <<"L419 x="<<x<<endl; + y = y1; // cout <<"L420 y="<<y<<endl; + // printf("dy01==0.0 x=% #12.7g y=% #12.7g\n",x,y); + return; + } else if (dx01 == 0.0) { + x = x1; + y = y1 + c; + // printf("dx01==0.0 x=% #12.7g y=% #12.7g\n",x,y); + return; + } else { // dx01!=0 and dy01 !=0. + x = x1 - 0.5 * c * r01 / dy01; // cout <<"L434 x="<<x<<endl; + y = y1 + 0.5 * c * r01 / dx01; // cout <<"L435 y="<<y<<endl; + // printf("m*m<DBL_E x=% #12.7g y=% #12.7g\n",x,y); + } // end if + return; + } + x = x1 + c * (dx12 * r01 - dx01 * r12) / m; // cout <<"L442 x="<<x<<endl; + y = y1 + c * (dy12 * r01 - dy01 * r12) / m; // cout <<"L443 y="<<y<<endl; + // printf(" x=% #12.7g y=% #12.7g\n",x,y); + // cout <<"=============================================="<<endl; + return; +} + +void V11Geometry::printArb8(const TGeoArb8* a) const +{ + if (!getDebug()) { + return; + } + printf("%s", a->GetName()); + a->InspectShape(); + return; +} + +void V11Geometry::printPcon(const TGeoPcon* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": N=" << a->GetNz() << " Phi1=" << a->GetPhi1() + << ", Dphi=" << a->GetDphi() << endl; + cout << "i\t Z \t Rmin \t Rmax" << endl; + for (Int_t iii = 0; iii < a->GetNz(); iii++) { + cout << iii << "\t" << a->GetZ(iii) << "\t" << a->GetRmin(iii) << "\t" << a->GetRmax(iii) + << endl; + } // end for iii + return; +} + +void V11Geometry::printTube(const TGeoTube* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Rmin=" << a->GetRmin() << " Rmax=" << a->GetRmax() + << " Dz=" << a->GetDz() << endl; + return; +} + +void V11Geometry::printTubeSeg(const TGeoTubeSeg* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Phi1=" << a->GetPhi1() << " Phi2=" << a->GetPhi2() + << " Rmin=" << a->GetRmin() << " Rmax=" << a->GetRmax() << " Dz=" << a->GetDz() << endl; + return; +} + +void V11Geometry::printConeSeg(const TGeoConeSeg* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Phi1=" << a->GetPhi1() << " Phi2=" << a->GetPhi2() + << " Rmin1=" << a->GetRmin1() << " Rmax1=" << a->GetRmax1() << " Rmin2=" << a->GetRmin2() + << " Rmax2=" << a->GetRmax2() << " Dz=" << a->GetDz() << endl; + return; +} + +void V11Geometry::printBBox(const TGeoBBox* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Dx=" << a->GetDX() << " Dy=" << a->GetDY() << " Dz=" << a->GetDZ() + << endl; + return; +} + +void V11Geometry::createDefaultMaterials() +{ + Int_t i; + Double_t w; + + // Define some elements + auto* itsH = new TGeoElement("TRK_H", "Hydrogen", 1, 1.00794); + auto* itsHe = new TGeoElement("TRK_He", "Helium", 2, 4.002602); + auto* itsC = new TGeoElement("TRK_C", "Carbon", 6, 12.0107); + auto* itsN = new TGeoElement("TRK_N", "Nitrogen", 7, 14.0067); + auto* itsO = new TGeoElement("TRK_O", "Oxygen", 8, 15.994); + auto* itsF = new TGeoElement("TRK_F", "Florine", 9, 18.9984032); + auto* itsNe = new TGeoElement("TRK_Ne", "Neon", 10, 20.1797); + auto* itsMg = new TGeoElement("TRK_Mg", "Magnesium", 12, 24.3050); + auto* itsAl = new TGeoElement("TRK_Al", "Aluminum", 13, 26981538); + auto* itsSi = new TGeoElement("TRK_Si", "Silicon", 14, 28.0855); + auto* itsP = new TGeoElement("TRK_P", "Phosphorous", 15, 30.973761); + auto* itsS = new TGeoElement("TRK_S", "Sulfur", 16, 32.065); + auto* itsAr = new TGeoElement("TRK_Ar", "Argon", 18, 39.948); + auto* itsTi = new TGeoElement("TRK_Ti", "Titanium", 22, 47.867); + auto* itsCr = new TGeoElement("TRK_Cr", "Chromium", 24, 51.9961); + auto* itsMn = new TGeoElement("TRK_Mn", "Manganese", 25, 54.938049); + auto* itsFe = new TGeoElement("TRK_Fe", "Iron", 26, 55.845); + auto* itsCo = new TGeoElement("TRK_Co", "Cobalt", 27, 58.933200); + auto* itsNi = new TGeoElement("TRK_Ni", "Nickrl", 28, 56.6930); + auto* itsCu = new TGeoElement("TRK_Cu", "Copper", 29, 63.546); + auto* itsZn = new TGeoElement("TRK_Zn", "Zinc", 30, 65.39); + auto* itsKr = new TGeoElement("TRK_Kr", "Krypton", 36, 83.80); + auto* itsMo = new TGeoElement("TRK_Mo", "Molylibdium", 42, 95.94); + auto* itsXe = new TGeoElement("TRK_Xe", "Zeon", 54, 131.293); + + // Start with the Materials since for any one material there + // can be defined more than one Medium. + // Air, dry. at 15degree C, 101325Pa at sea-level, % by volume + // (% by weight). Density is 351 Kg/m^3 + // N2 78.084% (75.47%), O2 20.9476% (23.20%), Ar 0.934 (1.28%)%, + // C02 0.0314% (0.0590%), Ne 0.001818% (0.0012%, CH4 0.002% (), + // He 0.000524% (0.00007%), Kr 0.000114% (0.0003%), H2 0.00005% (3.5E-6%), + // Xe 0.0000087% (0.00004 %), H2O 0.0% (dry) + trace amounts at the ppm + // levels. + auto* itsAir = new TGeoMixture("TRK_Air", 9); + w = 75.47E-2; + itsAir->AddElement(itsN, w); // Nitorgen, atomic + w = 23.29E-2 + // O2 + 5.90E-4 * 2. * 15.994 / (12.0107 + 2. * 15.994); // CO2. + itsAir->AddElement(itsO, w); // Oxygen, atomic + w = 1.28E-2; + itsAir->AddElement(itsAr, w); // Argon, atomic + w = 5.90E-4 * 12.0107 / (12.0107 + 2. * 15.994) + // CO2 + 2.0E-5 * 12.0107 / (12.0107 + 4. * 1.00794); // CH4 + itsAir->AddElement(itsC, w); // Carbon, atomic + w = 1.818E-5; + itsAir->AddElement(itsNe, w); // Ne, atomic + w = 3.5E-8; + itsAir->AddElement(itsHe, w); // Helium, atomic + w = 7.0E-7; + itsAir->AddElement(itsKr, w); // Krypton, atomic + w = 3.0E-6; + itsAir->AddElement(itsH, w); // Hydrogen, atomic + w = 4.0E-7; + itsAir->AddElement(itsXe, w); // Xenon, atomic + itsAir->SetDensity(351.0 * sKgm3); + itsAir->SetPressure(101325 * sPascal); + itsAir->SetTemperature(15.0 * sCelsius); + itsAir->SetState(TGeoMaterial::kMatStateGas); + + // Silicone + auto* itsSiDet = new TGeoMaterial("TRK_Si", itsSi, 2.33 * sGCm3); + itsSiDet->SetTemperature(15.0 * sCelsius); + itsSiDet->SetState(TGeoMaterial::kMatStateSolid); + + // Epoxy C18 H19 O3 + auto* itsEpoxy = new TGeoMixture("TRK_Epoxy", 3); + itsEpoxy->AddElement(itsC, 18); + itsEpoxy->AddElement(itsH, 19); + itsEpoxy->AddElement(itsO, 3); + itsEpoxy->SetDensity(1.8 * sGCm3); + itsEpoxy->SetTemperature(15.0 * sCelsius); + itsEpoxy->SetState(TGeoMaterial::kMatStateSolid); + + // Carbon Fiber, M55J, 60% fiber by volume. Fiber density + // 1.91 g/cm^3. See ToryaCA M55J data sheet. + // Begin_Html + /* + <A HREF="http://torayusa.com/cfa/pdfs/M55JDataSheet.pdf"> Data Sheet + </A> + */ + // End_Html + auto* itsCarbonFiber = new TGeoMixture("TRK_CarbonFiber-M55J", 4); + // Assume that the epoxy fill in the space between the fibers and so + // no change in the total volume. To compute w, assume 1cm^3 total + // volume. + w = 1.91 / (1.91 + (1. - .60) * itsEpoxy->GetDensity()); + itsCarbonFiber->AddElement(itsC, w); + w = (1. - .60) * itsEpoxy->GetDensity() / (1.91 + (1. - .06) * itsEpoxy->GetDensity()); + + for (i = 0; i < itsEpoxy->GetNelements(); i++) { + itsCarbonFiber->AddElement(itsEpoxy->GetElement(i), itsEpoxy->GetWmixt()[i] * w); + } + + itsCarbonFiber->SetDensity((1.91 + (1. - .60) * itsEpoxy->GetDensity()) * sGCm3); + itsCarbonFiber->SetTemperature(22.0 * sCelsius); + itsCarbonFiber->SetState(TGeoMaterial::kMatStateSolid); + + // Rohacell 51A millable foam product. + // C9 H13 N1 O2 52Kg/m^3 + // Elemental composition, Private comunications with + // Bjorn S. Nilsen + // Begin_Html + /* + <A HREF="http://www.rohacell.com/en/performanceplastics8344.html"> + Rohacell-A see Properties + </A> + */ + // End_Html + auto* itsFoam = new TGeoMixture("TRK_Foam", 4); + itsFoam->AddElement(itsC, 9); + itsFoam->AddElement(itsH, 13); + itsFoam->AddElement(itsN, 1); + itsFoam->AddElement(itsO, 2); + itsFoam->SetTitle("Rohacell 51 A"); + itsFoam->SetDensity(52. * sKgm3); + itsFoam->SetTemperature(22.0 * sCelsius); + itsFoam->SetState(TGeoMaterial::kMatStateSolid); + + // Kapton % by weight, H 2.6362, C69.1133, N 7.3270, O 20.0235 + // Density 1.42 g/cm^3 + // Begin_Html + /* + <A HREF="http://www2.dupont.com/Kapton/en_US/assets/downloads/pdf/summaryofprop.pdf"> + Kapton. also see </A> + <A HREF="http://physics.nist.gov/cgi-bin/Star/compos.pl?matno=179"> + </A> + */ + // End_Html + auto* itsKapton = new TGeoMixture("TRK_Kapton", 4); + itsKapton->AddElement(itsH, 0.026362); + itsKapton->AddElement(itsC, 0.691133); + itsKapton->AddElement(itsN, 0.073270); + itsKapton->AddElement(itsO, 0.200235); + itsKapton->SetTitle("Kapton ribon and cable base"); + itsKapton->SetDensity(1.42 * sGCm3); + itsKapton->SetTemperature(22.0 * sCelsius); + itsKapton->SetState(TGeoMaterial::kMatStateSolid); + + // UPILEX-S C16 H6 O4 N2 polymer (a Kapton like material) + // Density 1.47 g/cm^3 + // Begin_Html + /* + <A HREF="http://northamerica.ube.com/page.php?pageid=9"> + UPILEX-S. also see </A> + <A HREF="http://northamerica.ube.com/page.php?pageid=81"> + </A> + */ + // End_Html + auto* itsUpilex = new TGeoMixture("TRK_Upilex", 4); + itsUpilex->AddElement(itsC, 16); + itsUpilex->AddElement(itsH, 6); + itsUpilex->AddElement(itsN, 2); + itsUpilex->AddElement(itsO, 4); + itsUpilex->SetTitle("Upilex ribon, cable, and pcb base"); + itsUpilex->SetDensity(1.47 * sGCm3); + itsUpilex->SetTemperature(22.0 * sCelsius); + itsUpilex->SetState(TGeoMaterial::kMatStateSolid); + + // Aluminum 6061 (Al used by US groups) + // % by weight, Cr 0.04-0.35 range [0.0375 nominal value used] + // Cu 0.15-0.4 [0.275], Fe Max 0.7 [0.35], Mg 0.8-1.2 [1.0], + // Mn Max 0.15 [0.075] Si 0.4-0.8 [0.6], Ti Max 0.15 [0.075], + // Zn Max 0.25 [0.125], Rest Al [97.4625]. Density 2.7 g/cm^3 + // Begin_Html + /* + <A HREG="http://www.matweb.com/SpecificMaterial.asp?bassnum=MA6016&group=General"> + Aluminum 6061 specifications + </A> + */ + // End_Html + auto* itsAl6061 = new TGeoMixture("TRK_Al6061", 9); + itsAl6061->AddElement(itsCr, 0.000375); + itsAl6061->AddElement(itsCu, 0.00275); + itsAl6061->AddElement(itsFe, 0.0035); + itsAl6061->AddElement(itsMg, 0.01); + itsAl6061->AddElement(itsMn, 0.00075); + itsAl6061->AddElement(itsSi, 0.006); + itsAl6061->AddElement(itsTi, 0.00075); + itsAl6061->AddElement(itsZn, 0.00125); + itsAl6061->AddElement(itsAl, 0.974625); + itsAl6061->SetTitle("Aluminum Alloy 6061"); + itsAl6061->SetDensity(2.7 * sGCm3); + itsAl6061->SetTemperature(22.0 * sCelsius); + itsAl6061->SetState(TGeoMaterial::kMatStateSolid); + + // Aluminum 7075 (Al used by Italian groups) + // % by weight, Cr 0.18-0.28 range [0.23 nominal value used] + // Cu 1.2-2.0 [1.6], Fe Max 0.5 [0.25], Mg 2.1-2.9 [2.5], + // Mn Max 0.3 [0.125] Si Max 0.4 [0.2], Ti Max 0.2 [0.1], + // Zn 5.1-6.1 [5.6], Rest Al [89.395]. Density 2.81 g/cm^3 + // Begin_Html + /* + <A HREG="http://asm.matweb.com/search/SpecificMaterial.asp?bassnum=MA7075T6"> + Aluminum 7075 specifications + </A> + */ + // End_Html + auto* itsAl7075 = new TGeoMixture("TRK_Al7075", 9); + itsAl7075->AddElement(itsCr, 0.0023); + itsAl7075->AddElement(itsCu, 0.016); + itsAl7075->AddElement(itsFe, 0.0025); + itsAl7075->AddElement(itsMg, 0.025); + itsAl7075->AddElement(itsMn, 0.00125); + itsAl7075->AddElement(itsSi, 0.002); + itsAl7075->AddElement(itsTi, 0.001); + itsAl7075->AddElement(itsZn, 0.056); + itsAl7075->AddElement(itsAl, 0.89395); + itsAl7075->SetTitle("Aluminum Alloy 7075"); + itsAl7075->SetDensity(2.81 * sGCm3); + itsAl7075->SetTemperature(22.0 * sCelsius); + itsAl7075->SetState(TGeoMaterial::kMatStateSolid); + + // "Ruby" spheres, Al2 O3 + // "Ruby" Sphere posts, Ryton R-4 04 + // Begin_Html + /* + <A HREF=""> + Ruby Sphere Posts + </A> + */ + // End_Html + auto* itsRuby = new TGeoMixture("TRK_RubySphere", 2); + itsRuby->AddElement(itsAl, 2); + itsRuby->AddElement(itsO, 3); + itsRuby->SetTitle("Ruby reference sphere"); + itsRuby->SetDensity(2.81 * sGCm3); + itsRuby->SetTemperature(22.0 * sCelsius); + itsRuby->SetState(TGeoMaterial::kMatStateSolid); + + // Inox, AISI 304L, compoistion % by weight (assumed) + // C Max 0.03 [0.015], Mn Max 2.00 [1.00], Si Max 1.00 [0.50] + // P Max 0.045 [0.0225], S Max 0.03 [0.015], Ni 8.0-10.5 [9.25] + // Cr 18-20 [19.], Mo 2.-2.5 [2.25], rest Fe: density 7.93 Kg/dm^3 + // Begin_Html + /* + <A HREF="http://www.cimap.fr/caracter.pdf"> + Stainless steal (INOX) AISI 304L composition + </A> + */ + // End_Html + auto* itsInox304L = new TGeoMixture("TRK_Inox304L", 9); + itsInox304L->AddElement(itsC, 0.00015); + itsInox304L->AddElement(itsMn, 0.010); + itsInox304L->AddElement(itsSi, 0.005); + itsInox304L->AddElement(itsP, 0.000225); + itsInox304L->AddElement(itsS, 0.00015); + itsInox304L->AddElement(itsNi, 0.0925); + itsInox304L->AddElement(itsCr, 0.1900); + itsInox304L->AddElement(itsMo, 0.0225); + itsInox304L->AddElement(itsFe, 0.679475); // Rest Fe + itsInox304L->SetTitle("ITS Stainless Steal (Inox) type AISI 304L"); + itsInox304L->SetDensity(7.93 * sKgdm3); + itsInox304L->SetTemperature(22.0 * sCelsius); + itsInox304L->SetState(TGeoMaterial::kMatStateSolid); + + // Inox, AISI 316L, composition % by weight (assumed) + // C Max 0.03 [0.015], Mn Max 2.00 [1.00], Si Max 1.00 [0.50] + // P Max 0.045 [0.0225], S Max 0.03 [0.015], Ni 10.0-14. [12.] + // Cr 16-18 [17.], Mo 2-3 [2.5]: density 7.97 Kg/dm^3 + // Begin_Html + /* + <A HREF="http://www.cimap.fr/caracter.pdf"> + Stainless steal (INOX) AISI 316L composition + </A> + */ + // End_Html + auto* itsInox316L = new TGeoMixture("TRK_Inox316L", 9); + itsInox316L->AddElement(itsC, 0.00015); + itsInox316L->AddElement(itsMn, 0.010); + itsInox316L->AddElement(itsSi, 0.005); + itsInox316L->AddElement(itsP, 0.000225); + itsInox316L->AddElement(itsS, 0.00015); + itsInox316L->AddElement(itsNi, 0.12); + itsInox316L->AddElement(itsCr, 0.17); + itsInox316L->AddElement(itsMo, 0.025); + itsInox316L->AddElement(itsFe, 0.66945); // Rest Fe + itsInox316L->SetTitle("ITS Stainless Steal (Inox) type AISI 316L"); + itsInox316L->SetDensity(7.97 * sKgdm3); + itsInox316L->SetTemperature(22.0 * sCelsius); + itsInox316L->SetState(TGeoMaterial::kMatStateSolid); + + // Inox, Phynox or Elgiloy AMS 5833, composition % by weight + // C Max 0.15 [0.15], Mn Max 2.00 [2.00], Be max 0.0001 [none] + // Ni 18. [18.], Cr 21.5 [21.5], Mo 7.5 [7.5], Co 42 [42.]: + // density 8.3 Kg/dm^3 + // Begin_Html + /* + <A HREF="http://www.freepatentsonline.com/20070032816.html"> + Compostion of Phynox or Elgiloy AMS 5833, also see + </A> + <A HREF="http://www.alloywire.com/phynox_alloy.html"> + under corss reference number [0024]. + </A> + */ + // End_Html + auto* itsPhynox = new TGeoMixture("TRK_Phynox", 7); + itsPhynox->AddElement(itsC, 0.0015); + itsPhynox->AddElement(itsMn, 0.020); + itsPhynox->AddElement(itsNi, 0.18); + itsPhynox->AddElement(itsCr, 0.215); + itsPhynox->AddElement(itsMo, 0.075); + itsPhynox->AddElement(itsCo, 0.42); + itsPhynox->AddElement(itsFe, 0.885); + itsPhynox->SetTitle("ITS Cooling tube alloy"); + itsPhynox->SetDensity(8.3 * sGCm3); + itsPhynox->SetTemperature(22.0 * sCelsius); + itsPhynox->SetState(TGeoMaterial::kMatStateSolid); + + // G10FR4 + + // Demineralized Water H2O SDD & SSD Cooling liquid + auto* itsWater = new TGeoMixture("TRK_Water", 2); + itsWater->AddElement(itsH, 2); + itsWater->AddElement(itsO, 1); + itsWater->SetTitle("ITS Cooling Water"); + itsWater->SetDensity(1.0 * sGCm3); + itsWater->SetTemperature(22.0 * sCelsius); + itsWater->SetState(TGeoMaterial::kMatStateLiquid); + + // Freon SPD Cooling liquid PerFluorobuthane C4F10 + // Begin_Html + /* + <A HREF=" + http://st-support-cooling-electronics.web.cern.ch/st-support-cooling-electronics/default.htm"> + SPD 2 phase cooling using PerFluorobuthane + </A> + */ + // End_Html + auto* itsFreon = new TGeoMixture("TRK_SPD_Freon", 2); + itsFreon->AddElement(itsC, 4); + itsFreon->AddElement(itsF, 10); + itsFreon->SetTitle("ITS SPD 2 phase Cooling freon"); + itsFreon->SetDensity(1.52 * sGCm3); + itsFreon->SetTemperature(22.0 * sCelsius); + itsFreon->SetState(TGeoMaterial::kMatStateLiquid); + + // Int_t ifield = ((AliMagF*)TGeoGlobalMagField::Instance()->GetField())->Integ(); + // Float_t fieldm = ((AliMagF*)TGeoGlobalMagField::Instance()->GetField())->Max(); + + // Float_t tmaxfd = 0.1;// 1.0;// Degree + // Float_t stemax = 1.0;// cm + // Float_t deemax = 0.1;// 30.0;// Fraction of particle's energy 0<deemax<=1 + // Float_t epsil = 1.0E-4;// 1.0; cm + // Float_t stmin = 0.0; // cm "Default value used" + + // Float_t tmaxfdSi = 0.1; // .10000E+01; // Degree + // Float_t stemaxSi = 0.0075; // .10000E+01; // cm + // Float_t deemaxSi = 0.1; // Fraction of particle's energy 0<deemax<=1 + // Float_t epsilSi = 1.0E-4;// .10000E+01; + /* + Float_t stminSi = 0.0; // cm "Default value used" + + Float_t tmaxfdAir = 0.1; // .10000E+01; // Degree + Float_t stemaxAir = .10000E+01; // cm + Float_t deemaxAir = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1 + Float_t epsilAir = 1.0E-4;// .10000E+01; + Float_t stminAir = 0.0; // cm "Default value used" + + Float_t tmaxfdServ = 1.0; // 10.0; // Degree + Float_t stemaxServ = 1.0; // 0.01; // cm + Float_t deemaxServ = 0.5; // 0.1; // Fraction of particle's energy 0<deemax<=1 + Float_t epsilServ = 1.0E-3; // 0.003; // cm + Float_t stminServ = 0.0; //0.003; // cm "Default value used" + + // Freon PerFluorobuthane C4F10 see + // http://st-support-cooling-electronics.web.cern.ch/ + // st-support-cooling-electronics/default.htm + Float_t afre[2] = { 12.011,18.9984032 }; + Float_t zfre[2] = { 6., 9. }; + Float_t wfre[2] = { 4.,10. }; + Float_t densfre = 1.52; + + //CM55J + Float_t aCM55J[4]={12.0107,14.0067,15.9994,1.00794}; + Float_t zCM55J[4]={6.,7.,8.,1.}; + Float_t wCM55J[4]={0.908508078,0.010387573,0.055957585,0.025146765}; + Float_t dCM55J = 1.63; + + //ALCM55J + Float_t aALCM55J[5]={12.0107,14.0067,15.9994,1.00794,26.981538}; + Float_t zALCM55J[5]={6.,7.,8.,1.,13.}; + Float_t wALCM55J[5]={0.817657902,0.0093488157,0.0503618265,0.0226320885,0.1}; + Float_t dALCM55J = 1.9866; + + //Si Chips + Float_t aSICHIP[6]={12.0107,14.0067,15.9994,1.00794,28.0855,107.8682}; + Float_t zSICHIP[6]={6.,7.,8.,1.,14., 47.}; + Float_t wSICHIP[6]={0.039730642,0.001396798,0.01169634, + 0.004367771,0.844665,0.09814344903}; + Float_t dSICHIP = 2.36436; + + //Inox + Float_t aINOX[9]={12.0107,54.9380, 28.0855,30.9738,32.066, + 58.6928,55.9961,95.94,55.845}; + Float_t zINOX[9]={6.,25.,14.,15.,16., 28.,24.,42.,26.}; + Float_t wINOX[9]={0.0003,0.02,0.01,0.00045,0.0003,0.12,0.17,0.025,0.654}; + Float_t dINOX = 8.03; + + //SDD HV microcable + Float_t aHVm[5]={12.0107,1.00794,14.0067,15.9994,26.981538}; + Float_t zHVm[5]={6.,1.,7.,8.,13.}; + Float_t wHVm[5]={0.520088819984,0.01983871336,0.0551367996,0.157399667056, 0.247536}; + Float_t dHVm = 1.6087; + + //SDD LV+signal cable + Float_t aLVm[5]={12.0107,1.00794,14.0067,15.9994,26.981538}; + Float_t zLVm[5]={6.,1.,7.,8.,13.}; + Float_t wLVm[5]={0.21722436468,0.0082859922,0.023028867,0.06574077612, 0.68572}; + Float_t dLVm = 2.1035; + + //SDD hybrid microcab + Float_t aHLVm[5]={12.0107,1.00794,14.0067,15.9994,26.981538}; + Float_t zHLVm[5]={6.,1.,7.,8.,13.}; + Float_t wHLVm[5]={0.24281879711,0.00926228815,0.02574224025,0.07348667449, 0.64869}; + Float_t dHLVm = 2.0502; + + //SDD anode microcab + Float_t aALVm[5]={12.0107,1.00794,14.0067,15.9994,26.981538}; + Float_t zALVm[5]={6.,1.,7.,8.,13.}; + Float_t wALVm[5]={0.392653705471,0.0128595919215, + 0.041626868025,0.118832707289, 0.431909}; + Float_t dALVm = 2.0502; + + //X7R capacitors + Float_t aX7R[7]={137.327,47.867,15.9994,58.6928,63.5460,118.710,207.2}; + Float_t zX7R[7]={56.,22.,8.,28.,29.,50.,82.}; + Float_t wX7R[7]={0.251639432,0.084755042,0.085975822, + 0.038244751,0.009471271,0.321736471,0.2081768}; + Float_t dX7R = 7.14567; + + // AIR + Float_t aAir[4]={12.0107,14.0067,15.9994,39.948}; + Float_t zAir[4]={6.,7.,8.,18.}; + Float_t wAir[4]={0.000124,0.755267,0.231781,0.012827}; + Float_t dAir = 1.20479E-3; + + // Water + Float_t aWater[2]={1.00794,15.9994}; + Float_t zWater[2]={1.,8.}; + Float_t wWater[2]={0.111894,0.888106}; + Float_t dWater = 1.0; + + // CERAMICS + // 94.4% Al2O3 , 2.8% SiO2 , 2.3% MnO , 0.5% Cr2O3 + Float_t acer[5] = { 26.981539,15.9994,28.0855,54.93805,51.9961 }; + Float_t zcer[5] = { 13., 8., 14., 25., 24. }; + Float_t wcer[5] = {.4443408,.5213375,.0130872,.0178135,.003421}; + Float_t denscer = 3.6; + + // G10FR4 + Float_t zG10FR4[14] = {14.00, 20.00, 13.00, 12.00, 5.00, + 22.00, 11.00, 19.00, 26.00, 9.00, + 8.00, 6.00, 7.00, 1.00}; + Float_t aG10FR4[14] = {28.0855000,40.0780000,26.9815380,24.3050000, + 10.8110000,47.8670000,22.9897700,39.0983000, + 55.8450000,18.9984000,15.9994000,12.0107000, + 14.0067000,1.0079400}; + Float_t wG10FR4[14] = {0.15144894,0.08147477,0.04128158,0.00904554, + 0.01397570,0.00287685,0.00445114,0.00498089, + 0.00209828,0.00420000,0.36043788,0.27529426, + 0.01415852,0.03427566}; + Float_t densG10FR4= 1.8; + + //--- EPOXY --- C18 H19 O3 + Float_t aEpoxy[3] = {15.9994, 1.00794, 12.0107} ; + Float_t zEpoxy[3] = { 8., 1., 6.} ; + Float_t wEpoxy[3] = { 3., 19., 18.} ; + Float_t dEpoxy = 1.8 ; + + // rohacell: C9 H13 N1 O2 + Float_t arohac[4] = {12.01, 1.01, 14.010, 16.}; + Float_t zrohac[4] = { 6., 1., 7., 8.}; + Float_t wrohac[4] = { 9., 13., 1., 2.}; + Float_t drohac = 0.05; + + // If he/she means stainless steel (inox) + Aluminium and Zeff=15.3383 then + // %Al=81.6164 %inox=100-%Al + Float_t aInAl[5] = {27., 55.847,51.9961,58.6934,28.0855 }; + Float_t zInAl[5] = {13., 26.,24.,28.,14. }; + Float_t wInAl[5] = {.816164, .131443,.0330906,.0183836,.000919182}; + Float_t dInAl = 3.075; + + // Kapton + Float_t aKapton[4]={1.00794,12.0107, 14.010,15.9994}; + Float_t zKapton[4]={1.,6.,7.,8.}; + Float_t wKapton[4]={0.026362,0.69113,0.07327,0.209235}; + Float_t dKapton = 1.42; + + //SDD ruby sph. + Float_t aAlOxide[2] = { 26.981539,15.9994}; + Float_t zAlOxide[2] = { 13., 8.}; + Float_t wAlOxide[2] = {0.4707, 0.5293}; + Float_t dAlOxide = 3.97; + */ +} + +void V11Geometry::drawCrossSection(const TGeoPcon* p, Int_t fillc, Int_t fills, Int_t linec, + Int_t lines, Int_t linew, Int_t markc, Int_t marks, + Float_t marksize) const +{ + Int_t n = 0, m = 0, i = 0; + Double_t *z = nullptr, *r = nullptr; + TPolyMarker* pts = nullptr; + TPolyLine* line = nullptr; + + n = p->GetNz(); + if (n <= 0) { + return; + } + m = 2 * n + 1; + z = new Double_t[m]; + r = new Double_t[m]; + + for (i = 0; i < n; i++) { + z[i] = p->GetZ(i); + r[i] = p->GetRmax(i); + z[i + n] = p->GetZ(n - 1 - i); + r[i + n] = p->GetRmin(n - 1 - i); + } // end for i + z[n - 1] = z[0]; + r[n - 1] = r[0]; + + line = new TPolyLine(n, z, r); + pts = new TPolyMarker(n, z, r); + + line->SetFillColor(fillc); + line->SetFillStyle(fills); + line->SetLineColor(linec); + line->SetLineStyle(lines); + line->SetLineWidth(linew); + pts->SetMarkerColor(markc); + pts->SetMarkerStyle(marks); + pts->SetMarkerSize(marksize); + + line->Draw("f"); + line->Draw(); + pts->Draw(); + + delete[] z; + delete[] r; + + cout << "Hit Return to continue" << endl; + cin >> n; + delete line; + delete pts; + return; +} + +Bool_t V11Geometry::angleOfIntersectionWithLine(Double_t x0, Double_t y0, Double_t x1, Double_t y1, + Double_t xc, Double_t yc, Double_t rc, Double_t& t0, + Double_t& t1) const +{ + Double_t dx, dy, cx, cy, s2, t[4]; + Double_t a0, b0, c0, a1, b1, c1, sinthp, sinthm, costhp, costhm; + Int_t i, j; + + t0 = 400.0; + t1 = 400.0; + dx = x1 - x0; + dy = y1 - y0; + cx = xc - x0; + cy = yc - y0; + s2 = dx * dx + dy * dy; + if (s2 == 0.0) { + return kFALSE; + } + + a0 = rc * rc * s2; + if (a0 == 0.0) { + return kFALSE; + } + b0 = 2.0 * rc * dx * (dx * cy - cx * dy); + c0 = dx * dx * cy * cy - 2.0 * dy * dx * cy * cx + cx * cx * dy * dy - rc * rc * dy * dy; + c0 = 0.25 * b0 * b0 / (a0 * a0) - c0 / a0; + if (c0 < 0.0) { + return kFALSE; + } + sinthp = -0.5 * b0 / a0 + TMath::Sqrt(c0); + sinthm = -0.5 * b0 / a0 - TMath::Sqrt(c0); + + a1 = rc * rc * s2; + if (a1 == 0.0) { + return kFALSE; + } + b1 = 2.0 * rc * dy * (dy * cx - dx * cy); + c1 = dy * dy * cx * cx - 2.0 * dy * dx * cy * cx + dx * dx * cy * cy - rc * rc * dx * dx; + c1 = 0.25 * b1 * b1 / (a1 * a1) - c1 / a1; + if (c1 < 0.0) { + return kFALSE; + } + costhp = -0.5 * b1 / a1 + TMath::Sqrt(c1); + costhm = -0.5 * b1 / a1 - TMath::Sqrt(c1); + + t[0] = t[1] = t[2] = t[3] = 400.; + a0 = TMath::ATan2(sinthp, costhp); + if (a0 < 0.0) { + a0 += 2.0 * TMath::Pi(); + } + a1 = TMath::ATan2(sinthp, costhm); + if (a1 < 0.0) { + a1 += 2.0 * TMath::Pi(); + } + b0 = TMath::ATan2(sinthm, costhp); + if (b0 < 0.0) { + b0 += 2.0 * TMath::Pi(); + } + b1 = TMath::ATan2(sinthm, costhm); + if (b1 < 0.0) { + b1 += 2.0 * TMath::Pi(); + } + x1 = xc + rc * TMath::Cos(a0); + y1 = yc + rc * TMath::Sin(a0); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[0] = a0 * TMath::RadToDeg(); + } + x1 = xc + rc * TMath::Cos(a1); + y1 = yc + rc * TMath::Sin(a1); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[1] = a1 * TMath::RadToDeg(); + } + x1 = xc + rc * TMath::Cos(b0); + y1 = yc + rc * TMath::Sin(b0); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[2] = b0 * TMath::RadToDeg(); + } + x1 = xc + rc * TMath::Cos(b1); + y1 = yc + rc * TMath::Sin(b1); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[3] = b1 * TMath::RadToDeg(); + } + for (i = 0; i < 4; i++) { + for (j = i + 1; j < 4; j++) { + if (t[i] > t[j]) { + t0 = t[i]; + t[i] = t[j]; + t[j] = t0; + } + } // end for i,j + } + t0 = t[0]; + t1 = t[1]; + + return kTRUE; +} + +Double_t V11Geometry::angleForRoundedCorners0(Double_t dx, Double_t dy, Double_t sdr) const +{ + Double_t a, b; + + b = dy * dy + dx * dx - sdr * sdr; + if (b < 0.0) { + Error("AngleForRoundedCorners0", "dx^2(%e)+dy^2(%e)-sdr^2(%e)=b=%e<0", dx, dy, sdr, b); + } + b = TMath::Sqrt(b); + a = -sdr * dy + dx * b; + b = -sdr * dx - dy * b; + return TMath::ATan2(a, b) * TMath::RadToDeg(); +} + +Double_t V11Geometry::angleForRoundedCorners1(Double_t dx, Double_t dy, Double_t sdr) const +{ + Double_t a, b; + + b = dy * dy + dx * dx - sdr * sdr; + if (b < 0.0) { + Error("AngleForRoundedCorners1", "dx^2(%e)+dy^2(%e)-sdr^2(%e)=b=%e<0", dx, dy, sdr, b); + } + b = TMath::Sqrt(b); + a = -sdr * dy - dx * b; + b = -sdr * dx + dy * b; + return TMath::ATan2(a, b) * TMath::RadToDeg(); +} + +void V11Geometry::anglesForRoundedCorners(Double_t x0, Double_t y0, Double_t r0, Double_t x1, + Double_t y1, Double_t r1, Double_t& t0, Double_t& t1) + const +{ + Double_t t; + + if (r0 >= 0.0 && r1 >= 0.0) { // Inside to inside ++ + t = angleForRoundedCorners1(x1 - x0, y1 - y0, r1 - r0); + t0 = t1 = t; + return; + } else if (r0 >= 0.0 && r1 <= 0.0) { // Inside to Outside +- + r1 = -r1; // make positive + t = angleForRoundedCorners0(x1 - x0, y1 - y0, r1 + r0); + t0 = 180.0 + t; + if (t0 < 0.0) { + t += 360.; + } + if (t < 0.0) { + t += 360.; + } + t1 = t; + return; + } else if (r0 <= 0.0 && r1 >= 0.0) { // Outside to Inside -+ + r0 = -r0; // make positive + t = angleForRoundedCorners1(x1 - x0, y1 - y0, r1 + r0); + t0 = 180.0 + t; + if (t0 > 180.) { + t0 -= 360.; + } + if (t > 180.) { + t -= 360.; + } + t1 = t; + return; + } else if (r0 <= 0.0 && r1 <= 0.0) { // Outside to outside -- + r0 = -r0; // make positive + r1 = -r1; // make positive + t = angleForRoundedCorners0(x1 - x0, y1 - y0, r1 - r0); + t0 = t1 = t; + return; + } + return; +} + +void V11Geometry::makeFigure1(Double_t x0, Double_t y0, Double_t r0, Double_t x1, Double_t y1, + Double_t r1) +{ + Double_t t0[4], t1[4], xa0[4], ya0[4], xa1[4], ya1[4], ra0[4], ra1[4]; + Double_t xmin, ymin, xmax, ymax, h; + Int_t j; + + for (j = 0; j < 4; j++) { + ra0[j] = r0; + if (j % 2) { + ra0[j] = -r0; + } + ra1[j] = r1; + if (j > 1) { + ra1[j] = -r1; + } + anglesForRoundedCorners(x0, y0, ra0[j], x1, y1, ra1[j], t0[j], t1[j]); + xa0[j] = TMath::Abs(r0) * cosD(t0[j]) + x0; + ya0[j] = TMath::Abs(r0) * sinD(t0[j]) + y0; + xa1[j] = TMath::Abs(r1) * cosD(t1[j]) + x1; + ya1[j] = TMath::Abs(r1) * sinD(t1[j]) + y1; + } + if (r0 < 0.0) { + r0 = -r0; + } + if (r1 < 0.0) { + r1 = -r1; + } + xmin = TMath::Min(x0 - r0, x1 - r1); + ymin = TMath::Min(y0 - r0, y1 - r1); + xmax = TMath::Max(x0 + r0, x1 + r1); + ymax = TMath::Max(y0 + r0, y1 + r1); + + for (j = 1; j < 4; j++) { + xmin = TMath::Min(xmin, xa0[j]); + xmin = TMath::Min(xmin, xa1[j]); + ymin = TMath::Min(ymin, ya0[j]); + ymin = TMath::Min(ymin, ya1[j]); + + xmax = TMath::Max(xmax, xa0[j]); + xmax = TMath::Max(xmax, xa1[j]); + ymax = TMath::Max(ymax, ya0[j]); + ymax = TMath::Max(ymax, ya1[j]); + } + if (xmin < 0.0) { + xmin *= 1.1; + } else { + xmin *= 0.9; + } + if (ymin < 0.0) { + ymin *= 1.1; + } else { + ymin *= 0.9; + } + if (xmax < 0.0) { + xmax *= 0.9; + } else { + xmax *= 1.1; + } + if (ymax < 0.0) { + ymax *= 0.9; + } else { + ymax *= 1.1; + } + j = (Int_t)(500.0 * (ymax - ymin) / (xmax - xmin)); + auto* can = + new TCanvas("V11Geometry_AnglesForRoundedCorners", "Figure for V11Geometry", 500, j); + h = ymax - ymin; + if (h < 0) { + h = -h; + } + can->Range(xmin, ymin, xmax, ymax); + auto* c0 = new TArc(x0, y0, r0); + auto* c1 = new TArc(x1, y1, r1); + TLine* line[4]; + TArrow* ar0[4]; + TArrow* ar1[4]; + + for (j = 0; j < 4; j++) { + ar0[j] = new TArrow(x0, y0, xa0[j], ya0[j]); + ar1[j] = new TArrow(x1, y1, xa1[j], ya1[j]); + line[j] = new TLine(xa0[j], ya0[j], xa1[j], ya1[j]); + ar0[j]->SetLineColor(j + 1); + ar0[j]->SetArrowSize(0.1 * r0 / h); + ar1[j]->SetLineColor(j + 1); + ar1[j]->SetArrowSize(0.1 * r1 / h); + line[j]->SetLineColor(j + 1); + } + c0->Draw(); + c1->Draw(); + + for (j = 0; j < 4; j++) { + ar0[j]->Draw(); + ar1[j]->Draw(); + line[j]->Draw(); + } + + auto* t = new TText(); + t->SetTextSize(0.02); + Char_t txt[100]; + snprintf(txt, 99, "(x0=%5.2f,y0=%5.2f)", x0, y0); + t->DrawText(x0, y0, txt); + snprintf(txt, 99, "(x1=%5.2f,y1=%5.2f)", x1, y1); + + for (j = 0; j < 4; j++) { + t->SetTextColor(j + 1); + t->DrawText(x1, y1, txt); + snprintf(txt, 99, "r0=%5.2f", ra0[j]); + t->DrawText(0.5 * (x0 + xa0[j]), 0.5 * (y0 + ya0[j]), txt); + snprintf(txt, 99, "r1=%5.2f", ra1[j]); + t->DrawText(0.5 * (x1 + xa1[j]), 0.5 * (y1 + ya1[j]), txt); + } +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/V1Layer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V1Layer.cxx new file mode 100644 index 0000000000000..e190a5390a31f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V1Layer.cxx @@ -0,0 +1,2762 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V1Layer.cxx +/// \brief Implementation of the V1Layer class +/// \author Mario Sitta <sitta@to.infn.it> +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#include "TRKSimulation/V1Layer.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/Detector.h" + +#include "FairLogger.h" // for LOG + +#include <TGeoArb8.h> // for TGeoArb8 +#include <TGeoBBox.h> // for TGeoBBox +#include <TGeoCone.h> // for TGeoConeSeg, TGeoCone +#include <TGeoManager.h> // for TGeoManager, gGeoManager +#include <TGeoMatrix.h> // for TGeoCombiTrans, TGeoRotation, etc +#include <TGeoTrd1.h> // for TGeoTrd1 +#include <TGeoTube.h> // for TGeoTube, TGeoTubeSeg +#include <TGeoVolume.h> // for TGeoVolume, TGeoVolumeAssembly +#include <TGeoXtru.h> // for TGeoXtru +#include "TMathBase.h" // for Abs +#include <TMath.h> // for Sin, RadToDeg, DegToRad, Cos, Tan, etc + +#include <cstdio> // for snprintf + +class TGeoMedium; + +using namespace TMath; +using namespace o2::trk; + +// General Parameters +const Int_t V1Layer::sNumberOmInnerLayers = 3; + +const Double_t V1Layer::sDefaultSensorThick = 300 * sMicron; +const Double_t V1Layer::sDefaultStaveThick = 1 * sCm; + +// Inner Barrel Parameters +const Int_t V1Layer::sIBChipsPerRow = 9; +const Int_t V1Layer::sIBNChipRows = 1; + +// Outer Barrel Parameters +const Int_t V1Layer::sOBChipsPerRow = 7; +const Int_t V1Layer::sOBNChipRows = 2; + +const Double_t V1Layer::sOBHalfStaveWidth = 3.01 * sCm; +const Double_t V1Layer::sOBModuleWidth = sOBHalfStaveWidth; +const Double_t V1Layer::sOBModuleGap = 0.01 * sCm; +const Double_t V1Layer::sOBChipXGap = 0.01 * sCm; +const Double_t V1Layer::sOBChipZGap = 0.01 * sCm; +const Double_t V1Layer::sOBFlexCableAlThick = 0.005 * sCm; +const Double_t V1Layer::sOBFlexCableKapThick = 0.01 * sCm; +const Double_t V1Layer::sOBBusCableAlThick = 0.02 * sCm; +const Double_t V1Layer::sOBBusCableKapThick = 0.02 * sCm; +const Double_t V1Layer::sOBColdPlateThick = 0.012 * sCm; +const Double_t V1Layer::sOBCarbonPlateThick = 0.012 * sCm; +const Double_t V1Layer::sOBGlueThick = 0.03 * sCm; +const Double_t V1Layer::sOBModuleZLength = 21.06 * sCm; +const Double_t V1Layer::sOBHalfStaveYTrans = 1.76 * sMm; +const Double_t V1Layer::sOBHalfStaveXOverlap = 4.3 * sMm; +const Double_t V1Layer::sOBGraphiteFoilThick = 30.0 * sMicron; +const Double_t V1Layer::sOBCoolTubeInnerD = 2.052 * sMm; +const Double_t V1Layer::sOBCoolTubeThick = 32.0 * sMicron; +const Double_t V1Layer::sOBCoolTubeXDist = 11.1 * sMm; + +const Double_t V1Layer::sOBSpaceFrameWidth = 42.0 * sMm; +const Double_t V1Layer::sOBSpaceFrameTotHigh = 43.1 * sMm; +const Double_t V1Layer::sOBSFrameBeamRadius = 0.6 * sMm; +const Double_t V1Layer::sOBSpaceFrameLa = 3.0 * sMm; +const Double_t V1Layer::sOBSpaceFrameHa = 0.721979 * sMm; +const Double_t V1Layer::sOBSpaceFrameLb = 3.7 * sMm; +const Double_t V1Layer::sOBSpaceFrameHb = 0.890428 * sMm; +const Double_t V1Layer::sOBSpaceFrameL = 0.25 * sMm; +const Double_t V1Layer::sOBSFBotBeamAngle = 56.5; +const Double_t V1Layer::sOBSFrameBeamSidePhi = 65.0; + +ClassImp(V1Layer); + +#define SQ(A) (A) * (A) + +V1Layer::V1Layer() + : V11Geometry(), + mLayerNumber(0), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(false), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(Int_t debug) + : V11Geometry(debug), + mLayerNumber(0), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(false), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(Int_t lay, Int_t debug) + : V11Geometry(debug), + mLayerNumber(lay), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(false), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(Int_t lay, Bool_t turbo, Int_t debug) + : V11Geometry(debug), + mLayerNumber(lay), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(turbo), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(const V1Layer& s) + : V11Geometry(s.getDebug()), + mLayerNumber(s.mLayerNumber), + mPhi0(s.mPhi0), + mLayerRadius(s.mLayerRadius), + mZLength(s.mZLength), + mSensorThickness(s.mSensorThickness), + mStaveThickness(s.mStaveThickness), + mStaveWidth(s.mStaveWidth), + mStaveTilt(s.mStaveTilt), + mNumberOfStaves(s.mNumberOfStaves), + mNumberOfModules(s.mNumberOfModules), + mNumberOfChips(s.mNumberOfChips), + mChipTypeID(s.mChipTypeID), + mIsTurbo(s.mIsTurbo), + mBuildLevel(s.mBuildLevel), + mStaveModel(s.mStaveModel) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = s.mHierarchy[i]; + } +} + +V1Layer& V1Layer::operator=(const V1Layer& s) +{ + if (&s == this) { + return *this; + } + + mLayerNumber = s.mLayerNumber; + mPhi0 = s.mPhi0; + mLayerRadius = s.mLayerRadius; + mZLength = s.mZLength; + mSensorThickness = s.mSensorThickness; + mStaveThickness = s.mStaveThickness; + mStaveWidth = s.mStaveWidth; + mStaveTilt = s.mStaveTilt; + mNumberOfStaves = s.mNumberOfStaves; + mNumberOfModules = s.mNumberOfModules; + mNumberOfChips = s.mNumberOfChips; + mIsTurbo = s.mIsTurbo; + mChipTypeID = s.mChipTypeID; + mBuildLevel = s.mBuildLevel; + mStaveModel = s.mStaveModel; + for (int i = kNHLevels; i--;) { + mHierarchy[i] = s.mHierarchy[i]; + } + + return *this; +} + +V1Layer::~V1Layer() = default; + +void V1Layer::createLayer(TGeoVolume* motherVolume) +{ + char volumeName[30]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper parameters + if (mLayerRadius <= 0) { + LOG(FATAL) << "Wrong layer radius " << mLayerRadius; + } + + if (mZLength <= 0) { + LOG(FATAL) << "Wrong layer length " << mZLength; + } + + if (mNumberOfStaves <= 0) { + LOG(FATAL) << "Wrong number of staves " << mNumberOfStaves; + } + + if (mNumberOfChips <= 0) { + LOG(FATAL) << "Wrong number of chips " << mNumberOfChips; + } + + if (mLayerNumber >= sNumberOmInnerLayers && mNumberOfModules <= 0) { + LOG(FATAL) << "Wrong number of modules " << mNumberOfModules; + } + + if (mStaveThickness <= 0) { + LOG(INFO) << "Stave thickness wrong or not set " << mStaveThickness << " using default " + << sDefaultStaveThick; + mStaveThickness = sDefaultStaveThick; + } + + if (mSensorThickness <= 0) { + LOG(INFO) << "Sensor thickness wrong or not set " << mSensorThickness << " using default " + << sDefaultSensorThick; + mSensorThickness = sDefaultSensorThick; + } + + if (mSensorThickness > mStaveThickness) { + LOG(WARNING) << "Sensor thickness " << mSensorThickness << " is greater than stave thickness " + << mStaveThickness << " fixing"; + mSensorThickness = mStaveThickness; + } + + // If a Turbo layer is requested, do it and exit + if (mIsTurbo) { + createLayerTurbo(motherVolume); + return; + } + + // First create the stave container + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // mStaveWidth = mLayerRadius*Tan(alpha); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSLayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + + // layerVolume->SetVisibility(kFALSE); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius + ((TGeoBBox*)stavVol->GetShape())->GetDY(); + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode(stavVol, j, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + // geometry is served + return; +} + +void V1Layer::createLayerTurbo(TGeoVolume* motherVolume) +{ + char volumeName[30]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper (remaining) parameters + if (mStaveWidth <= 0) { + LOG(FATAL) << "Wrong stave width " << mStaveWidth; + } + + if (Abs(mStaveTilt) > 45) { + LOG(WARNING) << "Stave tilt angle (" << mStaveTilt << ") greater than 45deg"; + } + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSLayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius /* +chip thick ?! */; + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode( + stavVol, j, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi - mStaveTilt, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + return; +} + +TGeoVolume* V1Layer::createStave(const TGeoManager* /*mgr*/) +{ + char volumeName[30]; + + Double_t xlen, ylen, zlen; + Double_t xpos, ypos; + Double_t alpha; + + // First create all needed shapes + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // The stave + xlen = mLayerRadius * Tan(alpha); + if (mIsTurbo) { + xlen = 0.5 * mStaveWidth; + } + ylen = 0.5 * mStaveThickness; + zlen = 0.5 * mZLength; + + Double_t yplus = 0.46; + auto* stave = new TGeoXtru(2); // z sections + Double_t xv[5] = {xlen, xlen, 0, -xlen, -xlen}; + Double_t yv[5] = {ylen + 0.09, -0.15, -yplus - mSensorThickness, -0.15, ylen + 0.09}; + stave->DefinePolygon(5, xv, yv); + stave->DefineSection(0, -zlen, 0, 0, 1.); + stave->DefineSection(1, +zlen, 0, 0, 1.); + + // We have all shapes: now create the real volumes + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSStavePattern(), mLayerNumber); + // TGeoVolume *staveVol = new TGeoVolume(volumeName, stave, medAir); + TGeoVolume* staveVol = new TGeoVolumeAssembly(volumeName); + + // staveVol->SetVisibility(kFALSE); + staveVol->SetVisibility(kTRUE); + staveVol->SetLineColor(2); + TGeoVolume* mechStaveVol = nullptr; + + // Now build up the stave + if (mLayerNumber < sNumberOmInnerLayers) { + TGeoVolume* modVol = createStaveInnerB(xlen, ylen, zlen); + staveVol->AddNode(modVol, 0); + mHierarchy[kHalfStave] = 1; + + // Mechanical stave structure + mechStaveVol = createStaveStructInnerB(xlen, zlen); + if (mechStaveVol) { + ypos = ((TGeoBBox*)(modVol->GetShape()))->GetDY() + + ((TGeoBBox*)(mechStaveVol->GetShape()))->GetDY(); + staveVol->AddNode(mechStaveVol, 1, + new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", 0, 0, 180))); + } + } else { + TGeoVolume* hstaveVol = createStaveOuterB(); + if (mStaveModel == Detector::kOBModel0) { // Create simplified stave struct as in v0 + staveVol->AddNode(hstaveVol, 0); + mHierarchy[kHalfStave] = 1; + } else { // (if mStaveModel) Create new stave struct as in TDR + xpos = ((TGeoBBox*)(hstaveVol->GetShape()))->GetDX() - sOBHalfStaveXOverlap / 2; + // ypos is CF height as computed in createSpaceFrameOuterB1 + ypos = (sOBSpaceFrameTotHigh - sOBHalfStaveYTrans) / 2; + staveVol->AddNode(hstaveVol, 0, new TGeoTranslation(-xpos, ypos, 0)); + staveVol->AddNode(hstaveVol, 1, new TGeoTranslation(xpos, ypos + sOBHalfStaveYTrans, 0)); + mHierarchy[kHalfStave] = 2; // RS + mechStaveVol = createSpaceFrameOuterB(); + + if (mechStaveVol) { + staveVol->AddNode(mechStaveVol, 1, + new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", 180, 0, 0))); + } + } + } + // Done, return the stave + return staveVol; +} + +TGeoVolume* V1Layer::createStaveInnerB(const Double_t xsta, const Double_t ysta, + const Double_t zsta, const TGeoManager* mgr) +{ + Double_t xmod, ymod, zmod; + char volumeName[30]; + + // First we create the module (i.e. the HIC with 9 chips) + TGeoVolume* moduleVol = createModuleInnerB(xsta, ysta, zsta); + + // Then we create the fake halfstave and the actual stave + xmod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDX(); + ymod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDY(); + zmod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDZ(); + + auto* hstave = new TGeoBBox(xmod, ymod, zmod); + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + auto* hstaveVol = new TGeoVolume(volumeName, hstave, medAir); + + // Finally build it up + hstaveVol->AddNode(moduleVol, 0); + mHierarchy[kModule] = 1; + + // Done, return the stave structure + return hstaveVol; +} + +TGeoVolume* V1Layer::createModuleInnerB(Double_t xmod, Double_t ymod, Double_t zmod, + const TGeoManager* mgr) +{ + Double_t zchip; + Double_t zpos; + char volumeName[30]; + + // First create the single chip + zchip = zmod / sIBChipsPerRow; + TGeoVolume* chipVol = createChipInnerB(xmod, ymod, zchip); + + // Then create the module and populate it with the chips + auto* module = new TGeoBBox(xmod, ymod, zmod); + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSModulePattern(), mLayerNumber); + auto* modVol = new TGeoVolume(volumeName, module, medAir); + + // mm (not used) zlen = ((TGeoBBox*)chipVol->GetShape())->GetDZ(); + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + zpos = -zmod + j * 2 * zchip + zchip; + modVol->AddNode(chipVol, j, new TGeoTranslation(0, 0, zpos)); + mHierarchy[kChip]++; + } + // Done, return the module + return modVol; +} + +TGeoVolume* V1Layer::createStaveStructInnerB(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kIBModelDummy: + mechStavVol = createStaveModelInnerBDummy(xsta, zsta, mgr); + break; + case Detector::kIBModel0: + mechStavVol = createStaveModelInnerB0(xsta, zsta, mgr); + break; + case Detector::kIBModel1: + mechStavVol = createStaveModelInnerB1(xsta, zsta, mgr); + break; + case Detector::kIBModel21: + mechStavVol = createStaveModelInnerB21(xsta, zsta, mgr); + break; + case Detector::kIBModel22: + mechStavVol = createStaveModelInnerB22(xsta, zsta, mgr); + break; + case Detector::kIBModel3: + mechStavVol = createStaveModelInnerB3(xsta, zsta, mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelInnerBDummy(const Double_t, const Double_t, + const TGeoManager*) const +{ + // Done, return the stave structur + return nullptr; +} + +TGeoVolume* V1Layer::createStaveModelInnerB0(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + LOG(INFO) << "################################# pointer :" << medWater; + TGeoMedium* medM60J3K = mgr->GetMedium("TRK_M60J3K$"); + LOG(INFO) << "################################# pointer :" << medM60J3K; + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("TRK_FLEXCABLE$"); + + // Local parameters + Double_t kConeOutRadius = 0.15 / 2; + Double_t kConeInRadius = 0.1430 / 2; + Double_t kStaveLength = zsta * 2; + Double_t kStaveWidth = xsta * 2 - kConeOutRadius * 2; + Double_t kWidth = kStaveWidth / 4; // 1/2 of kWidth + Double_t kStaveHeight = 0.3; + Double_t kHeight = kStaveHeight / 2; + Double_t kAlpha = 90 - 67; // 90-33.69; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = kWidth / TMath::Sin(kTheta); + Double_t kL1 = kWidth / TMath::Tan(kTheta); + Double_t kS2 = TMath::Sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(the2); + Double_t kThe2 = TMath::ATan(kHeight / kS1); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + // Int_t loop = kStaveLength/(kL1); + // Double_t s3 = kWidth/(2*TMath::Sin(kTheta)); + // Double_t s4 = 3*kWidth/(2*TMath::Sin(kTheta)); + + LOG(DEBUG1) << "BuildLevel " << mBuildLevel; + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", o2::trk::GeometryTGeo::getITSStavePattern(), + mLayerNumber); + + Double_t z = 0, y = -0.011 + 0.0150, x = 0; + + TGeoVolume* mechStavVol = nullptr; + + if (mBuildLevel < 5) { + + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, kStaveHeight, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // detailed structure ++++++++++++++ + // Pipe Kapton grey-35 + auto* coolTube = new TGeoTube(kConeInRadius, kConeOutRadius, kStaveLength / 2); + auto* volCoolTube = new TGeoVolume("pipe", coolTube, medKapton); + volCoolTube->SetFillColor(35); + volCoolTube->SetLineColor(35); + mechStavVol->AddNode(volCoolTube, 0, new TGeoTranslation(x + (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + mechStavVol->AddNode(volCoolTube, 1, new TGeoTranslation(x - (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + } + + if (mBuildLevel < 4) { + auto* coolTubeW = new TGeoTube(0., kConeInRadius, kStaveLength / 2); + auto* volCoolTubeW = new TGeoVolume("pipeWater", coolTubeW, medWater); + volCoolTubeW->SetFillColor(4); + volCoolTubeW->SetLineColor(4); + mechStavVol->AddNode(volCoolTubeW, 0, new TGeoTranslation(x + (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + mechStavVol->AddNode(volCoolTubeW, 1, new TGeoTranslation(x - (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + } + + // frequency of filament + // n = 4 means very dense(4 filaments per interval) + // n = 2 means dense(2 filaments per interval) + Int_t n = 4; + Int_t loop = (Int_t)(kStaveLength / (4 * kL1 / n) + 2 / n) - 1; + if (mBuildLevel < 3) { + // Top CFRP Filament black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox(kS2, 0.007 / 2, 0.15 / 2); //(kS2,0.002,0.02); + auto* volT2 = new TGeoVolume("TopFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + + for (int i = 1; i < loop; i++) { // i<60;i++){ + mechStavVol->AddNode( + volT2, 4 * i + 0, + new TGeoCombiTrans( + x + kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 1, + new TGeoCombiTrans( + x - kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 2, + new TGeoCombiTrans( + x + kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 3, + new TGeoCombiTrans( + x - kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + } + + // Bottom CFRP Filament black-12 Carbon structure TGeoBBox (thickness,width,length) + auto* t1 = new TGeoBBox(0.007 / 2, 0.15 / 2, kS1); //(0.002,0.02,kS1); + auto* volT1 = new TGeoVolume("CFRPBottom", t1, medM60J3K); + volT1->SetLineColor(12); + volT1->SetFillColor(12); + + for (int i = 1; i < loop; i++) { + mechStavVol->AddNode( + volT1, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - kHeight, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", -90, kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - kHeight, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", 90, kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - kHeight, z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - kHeight, z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", -90, +kAlpha, 0))); + } + } + + if (mBuildLevel < 2) { + // Glue CFRP-Silicon layers TGeoBBox(thickness,width,kS1); + auto* tG = new TGeoBBox(0.0075 / 2, 0.18 / 2, kS1); + auto* volTG = new TGeoVolume("Glue1", tG, medGlue); + volTG->SetLineColor(5); + volTG->SetFillColor(5); + + for (int i = 1; i < loop; i++) { // i<60;i++){ + mechStavVol->AddNode( + volTG, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - 0.16, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(2*kL1*i), + new TGeoRotation("volTG", -90, kAlpha, 0))); + mechStavVol->AddNode( + volTG, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - 0.16, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(2*kL1*i), + new TGeoRotation("volTG", 90, kAlpha, 0))); + mechStavVol->AddNode( + volTG, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - 0.16, z - kStaveLength / 2 + ((4 / n) * i * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volTG", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volTG, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - 0.16, z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volTG", -90, +kAlpha, 0))); + } + + auto* glue = new TGeoBBox(xsta, 0.005 / 2, zsta); + auto* volGlue = new TGeoVolume("Glue2", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + // mechStavVol->AddNode(volGlue, 0, new TGeoCombiTrans(x, y-0.16, z, new TGeoRotation("",0, 0, + // 0))); + mechStavVol->AddNode(volGlue, 1, new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005, z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + // Flex cable brown-28 TGeoBBox(width,thickness,length); + auto* kapCable = new TGeoBBox(xsta, 0.01 / 2, zsta); + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005 - 0.01, z, + new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structur + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelInnerB1(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("TRK_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("TRK_FLEXCABLE$"); + + // Local parameters + Double_t kConeOutRadius = 0.15 / 2; + // Double_t kConeInRadius = 0.1430/2; + Double_t kStaveLength = zsta * 2; + // Double_t kStaveWidth = xsta*2-kConeOutRadius*2; + Double_t kStaveWidth = xsta * 2; + Double_t kWidth = kStaveWidth / 4; // 1/2 of kWidth + Double_t kStaveHeight = 0.3; + Double_t kHeight = kStaveHeight / 2; + Double_t kAlpha = 90 - 33.; // 90-30; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = kWidth / TMath::Sin(kTheta); + Double_t kL1 = kWidth / TMath::Tan(kTheta); + Double_t kS2 = TMath::Sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(the2); + Double_t kThe2 = TMath::ATan(kHeight / kS1); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + Int_t loop = (Int_t)((kStaveLength / (2 * kL1)) / 2); + + TGeoVolume* mechStavVol = nullptr; + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", o2::trk::GeometryTGeo::getITSStavePattern(), + mLayerNumber); + + // detailed structure ++++++++++++++ + Double_t z = 0, y = -0.011 + 0.0150, x = 0; + + // Polimide micro channels numbers + Double_t yMC = y - kHeight + 0.01; + Int_t nb = (Int_t)(kStaveWidth / 0.1) + 1; + Double_t xstaMC = (nb * 0.1 - 0.08) / 2; + + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, kStaveHeight, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Polimide micro channels numbers + auto* tM0 = new TGeoBBox(xstaMC, 0.005 / 2, zsta); + auto* volTM0 = new TGeoVolume("MicroChanCover", tM0, medKapton); + volTM0->SetLineColor(35); + volTM0->SetFillColor(35); + mechStavVol->AddNode(volTM0, 0, + new TGeoCombiTrans(x, -0.0125 + yMC, z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volTM0, 1, + new TGeoCombiTrans(x, +0.0125 + yMC, z, new TGeoRotation("", 0, 0, 0))); + + auto* tM0b = new TGeoBBox(0.02 / 2, 0.02 / 2, zsta); + auto* volTM0b = new TGeoVolume("MicroChanWalls", tM0b, medKapton); + volTM0b->SetLineColor(35); + volTM0b->SetFillColor(35); + for (Int_t ib = 0; ib < nb; ib++) { + mechStavVol->AddNode(volTM0b, ib, new TGeoCombiTrans(x + ib * 0.1 - xstaMC + 0.01, yMC, z, new TGeoRotation("", 0, 0, 0))); + } + } + + if (mBuildLevel < 4) { + // Water in Polimide micro channels + auto* water = new TGeoBBox(0.08 / 2, 0.02 / 2, zsta + 0.1); + auto* volWater = new TGeoVolume("Water", water, medWater); + volWater->SetLineColor(4); + volWater->SetFillColor(4); + for (Int_t ib = 0; ib < (nb - 1); ib++) { + mechStavVol->AddNode(volWater, ib, new TGeoCombiTrans(x + ib * 0.1 - xstaMC + 0.06, yMC, z, new TGeoRotation("", 0, 0, 0))); + } + } + + if (mBuildLevel < 3) { + // Bottom filament CFRP black-12 Carbon structure TGeoBBox (thickness,width,length) + Double_t filWidth = 0.04; + Double_t filHeight = 0.02; + auto* t1 = new TGeoBBox(filHeight / 2, filWidth / 2, kS1); + auto* volT1 = new TGeoVolume("CFRPBottom", t1, medM60J3K); + volT1->SetLineColor(12); + volT1->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode(volT1, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, kAlpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * kL1 * i) + kS1 / 2, + new TGeoRotation("volT1", 90, kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, +kAlpha, 0))); + } + + // Top filament CFRP black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox(kS2, filHeight / 2, filWidth / 2); + auto* volT2 = new TGeoVolume("CFRPTop", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode( + volT2, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + } + } + + if (mBuildLevel < 2) { + // Glue between filament and polimide micro channel + auto* t3 = new TGeoBBox(0.01 / 2, 0.04, kS1); + auto* volT3 = new TGeoVolume("FilamentGlue", t3, medGlue); + volT3->SetLineColor(5); + volT3->SetFillColor(5); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode(volT3, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + (4 * kL1 * i) + kS1 / 2, + new TGeoRotation("volT1", -90, kAlpha, 0))); + mechStavVol->AddNode(volT3, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + (4 * kL1 * i) + kS1 / 2, + new TGeoRotation("volT1", 90, kAlpha, 0))); + mechStavVol->AddNode( + volT3, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volT3, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, +kAlpha, 0))); + } + + // Glue microchannel and sensor + auto* glueM = new TGeoBBox(xsta, 0.01 / 2, zsta); + auto* volGlueM = new TGeoVolume("MicroChanGlue", glueM, medGlue); + volGlueM->SetLineColor(5); + volGlueM->SetFillColor(5); + mechStavVol->AddNode(volGlueM, 0, + new TGeoCombiTrans(x, y - 0.16, z, new TGeoRotation("", 0, 0, 0))); + + // Glue sensor and kapton + auto* glue = new TGeoBBox(xsta, 0.005 / 2, zsta); + auto* volGlue = new TGeoVolume("SensorGlue", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + mechStavVol->AddNode(volGlue, 1, new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005, z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + auto* kapCable = new TGeoBBox(xsta, 0.01 / 2, zsta); + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005 - 0.01, z, + new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structur + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelInnerB21(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("TRK_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("TRK_FLEXCABLE$"); + TGeoMedium* medK13D2U2k = mgr->GetMedium("TRK_K13D2U2k$"); + TGeoMedium* medFGS003 = mgr->GetMedium("TRK_FGS003$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + + // Local parameters + Double_t kConeOutRadius = 0.151384 / 2; + Double_t kConeInRadius = 0.145034 / 2; + Double_t kStaveLength = zsta; + Double_t kStaveWidth = xsta * 2; + Double_t kWidth = (kStaveWidth + 0.005) / 4; + Double_t kStaveHeigth = 0.33; // 0.33; + Double_t kHeight = (kStaveHeigth + 0.025) / 2; + Double_t kAlpha = 57; // 56.31; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = (kStaveWidth / 4) / TMath::Sin(kTheta); + Double_t kL1 = (kStaveWidth / 4) / TMath::Tan(kTheta); + Double_t kS2 = sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(the2); + Double_t kThe2 = TMath::ATan(kHeight / kS1); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + // Double_t lay1 = 0.003157; + Double_t kLay1 = 0.003; // Amec carbon + // Double_t lay2 = 0.0043215;//C Fleece carbon + Double_t kLay2 = 0.002; // C Fleece carbon + Double_t kLay3 = 0.007; // K13D2U carbon + Int_t loop = (Int_t)(kStaveLength / (2 * kL1)); + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", o2::trk::GeometryTGeo::getITSStavePattern(), + mLayerNumber); + + Double_t z = 0, y = -(kConeOutRadius + 0.03) + 0.0385, x = 0; + + TGeoVolume* mechStavVol = nullptr; + + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, kStaveHeigth, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Pipe Kapton grey-35 + auto* cone1 = + new TGeoCone(kStaveLength, kConeInRadius, kConeOutRadius, kConeInRadius, kConeOutRadius); + auto* volCone1 = new TGeoVolume("PolyimidePipe", cone1, medKapton); + volCone1->SetFillColor(35); + volCone1->SetLineColor(35); + mechStavVol->AddNode(volCone1, 1, new TGeoTranslation(x + 0.25, y, z)); + mechStavVol->AddNode(volCone1, 2, new TGeoTranslation(x - 0.25, y, z)); + } + + if (mBuildLevel < 4) { + auto* coolTubeW = new TGeoTube(0., kConeInRadius, kStaveLength); + auto* volCoolTubeW = new TGeoVolume("Water", coolTubeW, medWater); + volCoolTubeW->SetFillColor(4); + volCoolTubeW->SetLineColor(4); + mechStavVol->AddNode(volCoolTubeW, 0, new TGeoTranslation(x - 0.25, y, z)); + mechStavVol->AddNode(volCoolTubeW, 1, new TGeoTranslation(x + 0.25, y, z)); + } + + if (mBuildLevel < 3) { + // top fillament + // Top filament M60J black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = + new TGeoBBox(kS2, 0.02 / 2, 0.04 / 2); // TGeoBBox *t2=new TGeoBBox(kS2,0.01,0.02); + auto* volT2 = new TGeoVolume("TopFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + + for (int i = 0; i < loop; i++) { // i<28;i++){ + mechStavVol->AddNode( + volT2, i * 4 + 1, + new TGeoCombiTrans(x + kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, i * 4 + 2, + new TGeoCombiTrans(x - kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + mechStavVol->AddNode( + volT2, i * 4 + 3, + new TGeoCombiTrans(x + kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, i * 4 + 4, + new TGeoCombiTrans(x - kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + // mechStavVol->AddNode(volT2,i*4+1,new + // TGeoCombiTrans(x+kWidth+0.0036,y+kHeight-(0.12/2)+0.072,z+kStaveLength+(i*4*kL1)+kS1/2, new + // TGeoRotation("volT2",90,90-kAlpha,90-kBeta))); + } + + // wall side structure out + auto* box4 = new TGeoBBox(0.03 / 2, 0.12 / 2, kStaveLength - 0.50); + auto* plate4 = new TGeoVolume("WallOut", box4, medM60J3K); + plate4->SetFillColor(35); + plate4->SetLineColor(35); + mechStavVol->AddNode(plate4, 1, + new TGeoCombiTrans(x + (2 * kStaveWidth / 4) - (0.03 / 2), + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate4", 0, 0, 0))); + mechStavVol->AddNode(plate4, 2, + new TGeoCombiTrans(x - (2 * kStaveWidth / 4) + (0.03 / 2), + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate4", 0, 0, 0))); + // wall side in + auto* box5 = new TGeoBBox(0.015 / 2, 0.12 / 2, kStaveLength - 0.50); + auto* plate5 = new TGeoVolume("WallIn", box5, medM60J3K); + plate5->SetFillColor(12); + plate5->SetLineColor(12); + mechStavVol->AddNode(plate5, 1, + new TGeoCombiTrans(x + (2 * kStaveWidth / 4) - 0.03 - 0.015 / 2, + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate5", 0, 0, 0))); + mechStavVol->AddNode(plate5, 2, + new TGeoCombiTrans(x - (2 * kStaveWidth / 4) + 0.03 + 0.015 / 2, + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate5", 0, 0, 0))); + + // Amec Thermasol red-2 cover tube FGS300 + auto* cons1 = + new TGeoConeSeg(kStaveLength - 0.50, kConeOutRadius, kConeOutRadius + kLay1, kConeOutRadius, + kConeOutRadius + kLay1, 0, 180); + auto* cone11 = new TGeoVolume("ThermasolPipeCover", cons1, medFGS003); + cone11->SetFillColor(2); + cone11->SetLineColor(2); + mechStavVol->AddNode(cone11, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("Cone11", 0, 0, 0))); + mechStavVol->AddNode(cone11, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("Cone11", 0, 0, 0))); + + auto* box2 = + new TGeoBBox((0.50 - (2 * kConeOutRadius)) / 2, kLay1 / 2, kStaveLength - 0.50); + auto* plate2 = new TGeoVolume("ThermasolMiddle", box2, medFGS003); + plate2->SetFillColor(2); + plate2->SetLineColor(2); + mechStavVol->AddNode(plate2, 1, new TGeoCombiTrans(x, y - kConeOutRadius + (kLay1 / 2), z, new TGeoRotation("plate2", 0, 0, 0))); + + auto* box21 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - kLay1) / 2, kLay1 / 2, kStaveLength - 0.50); + auto* plate21 = new TGeoVolume("ThermasolLeftRight", box21, medFGS003); + plate21->SetFillColor(2); + plate21->SetLineColor(2); + mechStavVol->AddNode( + plate21, 1, new TGeoCombiTrans(x + 0.25 + kConeOutRadius + (0.75 - 0.25 - kConeOutRadius) / 2 - (kLay1 / 2), y - kConeOutRadius + (kLay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + mechStavVol->AddNode( + plate21, 2, new TGeoCombiTrans(x - 0.25 - kConeOutRadius - (0.75 - 0.25 - kConeOutRadius) / 2 + (kLay1 / 2), y - kConeOutRadius + (kLay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + + auto* box22 = new TGeoBBox((kLay1 / 2), kConeOutRadius / 2, kStaveLength - 0.50); + auto* plate22 = new TGeoVolume("ThermasolVertical", box22, medFGS003); + plate22->SetFillColor(2); + plate22->SetLineColor(2); + mechStavVol->AddNode(plate22, 1, new TGeoCombiTrans(x + 0.25 + kConeOutRadius + (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 2, new TGeoCombiTrans(x + 0.25 - kConeOutRadius - (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 3, new TGeoCombiTrans(x - 0.25 + kConeOutRadius + (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 4, new TGeoCombiTrans(x - 0.25 - kConeOutRadius - (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + + // C Fleece + auto* cons2 = + new TGeoConeSeg(kStaveLength - 0.50, kConeOutRadius + kLay1, kConeOutRadius + kLay1 + kLay2, + kConeOutRadius + kLay1, kConeOutRadius + kLay1 + kLay2, 0, 180); + auto* cone12 = new TGeoVolume("CFleecePipeCover", cons2, medCarbonFleece); + cone12->SetFillColor(28); + cone12->SetLineColor(28); + mechStavVol->AddNode(cone12, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("Cone12", 0, 0, 0))); + mechStavVol->AddNode(cone12, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("Cone12", 0, 0, 0))); + + auto* box3 = + new TGeoBBox((0.50 - (2 * (kConeOutRadius + kLay1))) / 2, kLay2 / 2, kStaveLength - 0.50); + auto* plate3 = new TGeoVolume("CFleeceMiddle", box3, medCarbonFleece); + plate3->SetFillColor(28); + plate3->SetLineColor(28); + mechStavVol->AddNode(plate3, 1, new TGeoCombiTrans(x, y - kConeOutRadius + kLay1 + (kLay2 / 2), z, new TGeoRotation("plate3", 0, 0, 0))); + + auto* box31 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - kLay1) / 2, kLay2 / 2, kStaveLength - 0.50); + auto* plate31 = new TGeoVolume("CFleeceLeftRight", box31, medCarbonFleece); + plate31->SetFillColor(28); + plate31->SetLineColor(28); + mechStavVol->AddNode( + plate31, 1, + new TGeoCombiTrans( + x + 0.25 + kConeOutRadius + kLay1 + (0.75 - 0.25 - kConeOutRadius - kLay1) / 2, + y - kConeOutRadius + kLay1 + (kLay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + mechStavVol->AddNode( + plate31, 2, + new TGeoCombiTrans( + x - 0.25 - kConeOutRadius - kLay1 - (0.75 - 0.25 - kConeOutRadius - kLay1) / 2, + y - kConeOutRadius + kLay1 + (kLay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + + auto* box32 = new TGeoBBox((kLay2 / 2), (kConeOutRadius - kLay1) / 2, kStaveLength - 0.50); + auto* plate32 = new TGeoVolume("CFleeceVertical", box32, medCarbonFleece); + plate32->SetFillColor(28); + plate32->SetLineColor(28); + mechStavVol->AddNode(plate32, 1, + new TGeoCombiTrans(x + 0.25 + kConeOutRadius + kLay1 + (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 2, + new TGeoCombiTrans(x + 0.25 - kConeOutRadius - kLay1 - (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 3, + new TGeoCombiTrans(x - 0.25 + kConeOutRadius + kLay1 + (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 4, + new TGeoCombiTrans(x - 0.25 - kConeOutRadius - kLay1 - (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + + // K13D2U carbon plate + auto* box1 = new TGeoBBox(2 * kWidth, kLay3 / 2, kStaveLength - 0.50); + auto* plate1 = new TGeoVolume("CarbonPlate", box1, medK13D2U2k); + plate1->SetFillColor(5); + plate1->SetLineColor(5); + mechStavVol->AddNode(plate1, 1, new TGeoCombiTrans(x, y - (kConeOutRadius + (kLay3 / 2)), z, new TGeoRotation("plate1", 0, 0, 0))); + + // C Fleece bottom plate + auto* box6 = new TGeoBBox(2 * kWidth, kLay2 / 2, kStaveLength - 0.50); + auto* plate6 = new TGeoVolume("CFleeceBottom", box6, medCarbonFleece); + plate6->SetFillColor(2); + plate6->SetLineColor(2); + mechStavVol->AddNode(plate6, 1, + new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2)), z, + new TGeoRotation("plate1", 0, 0, 0))); + } + + if (mBuildLevel < 2) { + // Glue layers and kapton + auto* glue = new TGeoBBox(kStaveWidth / 2, 0.005 / 2, zsta); + auto* volGlue = new TGeoVolume("Glue", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + mechStavVol->AddNode( + volGlue, 0, new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2) + (0.01 / 2)), z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volGlue, 1, + new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2) + 0.01 + mSensorThickness + (0.01 / 2)), + z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + auto* kapCable = new TGeoBBox(kStaveWidth / 2, 0.01 / 2, zsta); + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2) + 0.01 + mSensorThickness + 0.01 + (0.01 / 2)), + z, new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structure + return mechStavVol; +} + +// new model22 +TGeoVolume* V1Layer::createStaveModelInnerB22(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("TRK_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("TRK_FLEXCABLE$"); + TGeoMedium* medK13D2U2k = mgr->GetMedium("TRK_K13D2U2k$"); + TGeoMedium* medFGS003 = mgr->GetMedium("TRK_FGS003$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + + // Local parameters + Double_t kConeOutRadius = (0.1024 + 0.0025) / 2; // 0.107/2; + Double_t kConeInRadius = 0.1024 / 2; // 0.10105/2 + Double_t kStaveLength = zsta; + Double_t kStaveWidth = xsta * 2; + Double_t kWidth = (kStaveWidth) / 4; + Double_t kStaveHeight = 0.283; // 0.33; + Double_t kHeight = (kStaveHeight) / 2; + Double_t kAlpha = 57; // 56.31; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = ((kStaveWidth) / 4) / TMath::Sin(kTheta); + Double_t kL1 = (kStaveWidth / 4) / TMath::Tan(kTheta); + Double_t kS2 = sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(kThe2); + Double_t kThe2 = TMath::ATan(kHeight / (0.375 - 0.036)); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + Double_t klay1 = 0.003; // Amec carbon + Double_t klay2 = 0.002; // C Fleece carbon + Double_t klay3 = 0.007; // CFplate K13D2U carbon + Double_t klay4 = 0.007; // GluekStaveLength/2 + Double_t klay5 = 0.01; // Flex cable + Double_t kTopVertexMaxWidth = 0.072; + Double_t kTopVertexHeight = 0.04; + Double_t kSideVertexMWidth = 0.052; + Double_t kSideVertexHeight = 0.11; + + Int_t loop = (Int_t)(kStaveLength / (2 * kL1)); + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", o2::trk::GeometryTGeo::getITSStavePattern(), + mLayerNumber); + + Double_t z = 0, y = -(2 * kConeOutRadius) + klay1 + klay2 + mSensorThickness / 2 - 0.0004, x = 0; + + TGeoVolume* mechStavVol = nullptr; + + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[6] = { + kStaveWidth / 2, kStaveWidth / 2, 0.012, + -0.012, -kStaveWidth / 2, -kStaveWidth / 2}; + // Double_t yv[6] = {-2*(kConeOutRadius+klay1+1.5*klay2+klay3+klay4+mSensorThickness+klay5), + // 0-0.02,kStaveHeight+0.01,kStaveHeight+0.01,0-0.02, + // -2*(kConeOutRadius+klay1+1.5*klay2+klay3+klay4+mSensorThickness+klay5)}; + // (kConeOutRadius*2)-0.0635 + Double_t yv[6] = { + -(kConeOutRadius * 2) - 0.06395, 0 - 0.02, kStaveHeight + 0.01, + kStaveHeight + 0.01, 0 - 0.02, -(kConeOutRadius * 2) - 0.06395}; // (kConeOutRadius*2)-0.064 + mechStruct->DefinePolygon(6, xv, yv); + mechStruct->DefineSection(0, -kStaveLength, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Polyimide Pipe Kapton grey-35 + auto* cone1 = new TGeoCone(kStaveLength, kConeInRadius, kConeOutRadius - 0.0001, + kConeInRadius, kConeOutRadius - 0.0001); + auto* volCone1 = new TGeoVolume("PolyimidePipe", cone1, medKapton); + volCone1->SetFillColor(35); + volCone1->SetLineColor(35); + mechStavVol->AddNode(volCone1, 1, new TGeoTranslation(x + 0.25, y, z)); + mechStavVol->AddNode(volCone1, 2, new TGeoTranslation(x - 0.25, y, z)); + } + + if (mBuildLevel < 4) { + auto* coolTubeW = new TGeoTube(0., kConeInRadius - 0.0001, kStaveLength); + auto* volCoolTubeW = new TGeoVolume("Water", coolTubeW, medWater); + volCoolTubeW->SetFillColor(4); + volCoolTubeW->SetLineColor(4); + mechStavVol->AddNode(volCoolTubeW, 0, new TGeoTranslation(x - 0.25, y, z)); + mechStavVol->AddNode(volCoolTubeW, 1, new TGeoTranslation(x + 0.25, y, z)); + } + + if (mBuildLevel < 3) { + // top fillament + // Top filament M60J black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox( + kS2 - 0.028, 0.02 / 2, + 0.02 / 2); // 0.04/2//TGeoBBox *t2=new TGeoBBox(kS2,0.01,0.02);//kS2-0.03 old Config.C + auto* volT2 = new TGeoVolume("TopFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<28;i++){ + // 1) Front Left Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 1, + new TGeoCombiTrans(x + kWidth + 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + // 2) Front Right Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 2, + new TGeoCombiTrans(x - kWidth - 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + // 3) Back Left Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 3, + new TGeoCombiTrans(x + kWidth + 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + // 4) Back Right Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 4, + new TGeoCombiTrans(x - kWidth - 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + } + + // Vertex structure + // top ver trd1 + auto* trd1 = new TGeoTrd1(0, kTopVertexMaxWidth / 2, kStaveLength, kTopVertexHeight / 2); + auto* ibdv = new TGeoVolume("TopVertex", trd1, medM60J3K); + ibdv->SetFillColor(12); + ibdv->SetLineColor(12); + mechStavVol->AddNode( + ibdv, 1, new TGeoCombiTrans(x, y + kStaveHeight + 0.03, z, new TGeoRotation("ibdv", 0., -90, 0))); // y+kStaveHeight+0.056 + + // left trd2 + auto* trd2 = new TGeoTrd1(0, kSideVertexMWidth / 2, kStaveLength, kSideVertexHeight / 2); + auto* ibdv2 = new TGeoVolume("LeftVertex", trd2, medM60J3K); + ibdv2->SetFillColor(12); + ibdv2->SetLineColor(12); + mechStavVol->AddNode( + ibdv2, 1, + new TGeoCombiTrans( + x + kStaveWidth / 2 - 0.06, y - 0.0355, z, + new TGeoRotation("ibdv2", -103.3, 90, 0))); // x-kStaveWidth/2-0.09 old Config.C y-0.0355, + + // right trd3 + auto* trd3 = new TGeoTrd1(0, kSideVertexMWidth / 2, kStaveLength, kSideVertexHeight / 2); + auto* ibdv3 = new TGeoVolume("RightVertex", trd3, medM60J3K); + ibdv3->SetFillColor(12); + ibdv3->SetLineColor(12); + mechStavVol->AddNode( + ibdv3, 1, new TGeoCombiTrans(x - kStaveWidth / 2 + 0.06, y - 0.0355, z, new TGeoRotation("ibdv3", 103.3, 90, 0))); // x-kStaveWidth/2+0.09 old Config.C + + // Carbon Fleece + auto* cons2 = + new TGeoConeSeg(zsta, kConeOutRadius + klay1, kConeOutRadius + klay1 + klay2, + kConeOutRadius + klay1, kConeOutRadius + klay1 + klay2, 0, 180); + auto* cone12 = new TGeoVolume("CarbonFleecePipeCover", cons2, medCarbonFleece); + cone12->SetFillColor(28); + cone12->SetLineColor(28); + mechStavVol->AddNode(cone12, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("cone12", 0, 0, 0))); + mechStavVol->AddNode(cone12, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("cone12", 0, 0, 0))); + + auto* box3 = new TGeoBBox((0.50 - (2 * (kConeOutRadius + klay1))) / 2, klay2 / 2, + zsta); // kStaveLength-0.50); + auto* plate3 = new TGeoVolume("CarbonFleeceMiddle", box3, medCarbonFleece); + plate3->SetFillColor(28); + plate3->SetLineColor(28); + mechStavVol->AddNode(plate3, 1, new TGeoCombiTrans(x, y - kConeOutRadius + klay1 + (klay2 / 2), z, new TGeoRotation("plate3", 0, 0, 0))); + + auto* box31 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - klay1) / 2 + 0.0025, klay2 / 2, zsta); + auto* plate31 = new TGeoVolume("CarbonFleeceLeftRight", box31, medCarbonFleece); + plate31->SetFillColor(28); + plate31->SetLineColor(28); + mechStavVol->AddNode( + plate31, 1, + new TGeoCombiTrans( + x + 0.25 + kConeOutRadius + klay1 + (0.75 - 0.25 - kConeOutRadius - klay1) / 2, + y - kConeOutRadius + klay1 + (klay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + mechStavVol->AddNode( + plate31, 2, + new TGeoCombiTrans( + x - 0.25 - kConeOutRadius - klay1 - (0.75 - 0.25 - kConeOutRadius - klay1) / 2, + y - kConeOutRadius + klay1 + (klay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + + auto* box32 = new TGeoBBox((klay2 / 2), (kConeOutRadius - klay1) / 2, zsta); + auto* plate32 = new TGeoVolume("CarbonFleeceVertical", box32, medCarbonFleece); + plate32->SetFillColor(28); + plate32->SetLineColor(28); + mechStavVol->AddNode(plate32, 1, + new TGeoCombiTrans(x + 0.25 + kConeOutRadius + klay1 + (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 2, + new TGeoCombiTrans(x + 0.25 - kConeOutRadius - klay1 - (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 3, + new TGeoCombiTrans(x - 0.25 + kConeOutRadius + klay1 + (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 4, + new TGeoCombiTrans(x - 0.25 - kConeOutRadius - klay1 - (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + + // Amec Thermasol red-2 cover tube FGS300 or Carbon Paper + auto* cons1 = + new TGeoConeSeg(zsta, kConeOutRadius, kConeOutRadius + klay1 - 0.0001, kConeOutRadius, + kConeOutRadius + klay1 - 0.0001, 0, 180); // kConeOutRadius+klay1-0.0001 + auto* cone11 = new TGeoVolume("ThermasolPipeCover", cons1, medFGS003); + cone11->SetFillColor(2); + cone11->SetLineColor(2); + mechStavVol->AddNode(cone11, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("cone11", 0, 0, 0))); + mechStavVol->AddNode(cone11, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("cone11", 0, 0, 0))); + + auto* box2 = + new TGeoBBox((0.50 - (2 * kConeOutRadius)) / 2, (klay1 / 2), zsta); // kStaveLength-0.50); + auto* plate2 = new TGeoVolume("ThermasolMiddle", box2, medFGS003); + plate2->SetFillColor(2); + plate2->SetLineColor(2); + mechStavVol->AddNode(plate2, 1, new TGeoCombiTrans(x, y - kConeOutRadius + (klay1 / 2), z, new TGeoRotation("plate2", 0, 0, 0))); + + auto* box21 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - klay1) / 2 + 0.0025, (klay1 / 2), zsta); + auto* plate21 = new TGeoVolume("ThermasolLeftRight", box21, medFGS003); + plate21->SetFillColor(2); + plate21->SetLineColor(2); + mechStavVol->AddNode( + plate21, 1, + new TGeoCombiTrans( + x + 0.25 + kConeOutRadius + (0.75 - 0.25 - kConeOutRadius) / 2 - (klay1 / 2) + 0.0025, + y - kConeOutRadius + (klay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + mechStavVol->AddNode( + plate21, 2, + new TGeoCombiTrans( + x - 0.25 - kConeOutRadius - (0.75 - 0.25 - kConeOutRadius) / 2 + (klay1 / 2) - 0.0025, + y - kConeOutRadius + (klay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + + auto* box22 = new TGeoBBox((klay1 / 2), kConeOutRadius / 2, zsta); + auto* plate22 = new TGeoVolume("ThermasolVertical", box22, medFGS003); + plate22->SetFillColor(2); + plate22->SetLineColor(2); + mechStavVol->AddNode(plate22, 1, new TGeoCombiTrans(x + 0.25 + kConeOutRadius + (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 2, new TGeoCombiTrans(x + 0.25 - kConeOutRadius - (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 3, new TGeoCombiTrans(x - 0.25 + kConeOutRadius + (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 4, new TGeoCombiTrans(x - 0.25 - kConeOutRadius - (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + + // K13D2U CF plate + auto* box1 = new TGeoBBox(2 * kWidth, (klay3) / 2, zsta); + auto* plate1 = new TGeoVolume("CFPlate", box1, medK13D2U2k); + plate1->SetFillColor(5); + plate1->SetLineColor(5); + mechStavVol->AddNode(plate1, 1, new TGeoCombiTrans(x, y - (kConeOutRadius + (klay3 / 2)), z, new TGeoRotation("plate1", 0, 0, 0))); + + // C Fleece bottom plate + auto* box6 = new TGeoBBox(2 * kWidth, (klay2) / 2, zsta); + auto* plate6 = new TGeoVolume("CarbonFleeceBottom", box6, medCarbonFleece); + plate6->SetFillColor(2); + plate6->SetLineColor(2); + mechStavVol->AddNode(plate6, 1, + new TGeoCombiTrans(x, y - (kConeOutRadius + klay3 + (klay2 / 2)), z, + new TGeoRotation("plate6", 0, 0, 0))); + } + if (mBuildLevel < 2) { + // Glue klayers and kapton + auto* glue = new TGeoBBox(kStaveWidth / 2, (klay4) / 2, zsta); + auto* volGlue = new TGeoVolume("Glue", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + // mechStavVol->AddNode(volGlue, 0, new + // TGeoCombiTrans(x,y-(kConeOutRadius+klay3+klay2+(klay4/2)), z, new TGeoRotation("",0, 0, 0))); + mechStavVol->AddNode( + volGlue, 0, + new TGeoCombiTrans(x, y - (kConeOutRadius + klay3 + klay2 + (klay4) / 2) + 0.00005, z, + new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + // Flex Cable or Bus + auto* kapCable = new TGeoBBox(kStaveWidth / 2, klay5 / 2, zsta); // klay5/2 + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + // mechStavVol->AddNode(volCable, 0, new TGeoCombiTrans(x, + // y-(kConeOutRadius+klay3+klay2+klay4+mSensorThickness+(klay5)/2)+0.0002, z, new + // TGeoRotation("",0, + // 0, 0))); + mechStavVol->AddNode( + volCable, 0, + new TGeoCombiTrans( + x, y - (kConeOutRadius + klay3 + klay2 + klay4 + mSensorThickness + (klay5) / 2) + 0.01185, + z, new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structe + return mechStavVol; +} + +// model3 +TGeoVolume* V1Layer::createStaveModelInnerB3(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("TRK_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("TRK_FLEXCABLE$"); + // TGeoMedium *medK13D2U2k = mgr->GetMedium("TRK_K13D2U2k$"); + // TGeoMedium *medFGS003 = mgr->GetMedium("TRK_FGS003$"); + // TGeoMedium *medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + + // Local parameters + Double_t kConeOutRadius = 0.15 / 2; + Double_t kStaveLength = zsta * 2; + Double_t kStaveWidth = xsta * 2; + Double_t w = kStaveWidth / 4; // 1/2 of W + Double_t staveHeight = 0.3; + Double_t h = staveHeight / 2; + Double_t alpha = 90 - 33.; // 90-30; + Double_t the1 = alpha * TMath::DegToRad(); + Double_t s1 = w / TMath::Sin(the1); + Double_t l = w / TMath::Tan(the1); + Double_t s2 = TMath::Sqrt(h * h + s1 * s1); // TMath::Sin(the2); + Double_t the2 = TMath::ATan(h / s1); + Double_t beta = the2 * TMath::RadToDeg(); + Double_t klay4 = 0.007; // Glue + Double_t klay5 = 0.01; // Flexcable + Int_t loop = (Int_t)((kStaveLength / (2 * l)) / 2); + Double_t hh = 0.01; + Double_t ang1 = 0 * TMath::DegToRad(); + Double_t ang2 = 0 * TMath::DegToRad(); + Double_t ang3 = 0 * TMath::DegToRad(); + Int_t chips = 4; + Double_t headWidth = 0.25; + Double_t smcLength = kStaveLength / chips - 2 * headWidth; // 6.25; + Double_t smcWidth = kStaveWidth; + Double_t smcSide1Thick = 0.03; + Double_t vaporThick = 0.032; + Double_t liquidThick = 0.028; + Double_t smcSide2Thick = 0.01; + Double_t smcSide3Thick = 0.0055; + Double_t smcSide4Thick = 0.0095; + Double_t smcSide5Thick = 0.0075; + Double_t smcSpace = 0.01; + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", o2::trk::GeometryTGeo::getITSStavePattern(), + mLayerNumber); + + // detailed structure ++++++++++++++ + Double_t z = 0, y = 0 - 0.007, x = 0; + + // Polimide micro channels numbers + Double_t yMC = y - h + 0.01; + Int_t nb = (Int_t)(kStaveWidth / 0.1) + 1; + Double_t xstaMC = (nb * 0.1 - 0.08) / 2; + + TGeoVolume* mechStavVol = nullptr; + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, staveHeight, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Silicon micro channels numbers + + auto* tM0a = new TGeoBBox(smcWidth / 2, 0.003 / 2, headWidth / 2); + auto* volTM0a = new TGeoVolume("microChanTop1", tM0a, medKapton); + volTM0a->SetLineColor(35); + volTM0a->SetFillColor(35); + + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0a, 0, + new TGeoCombiTrans(x, yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + (headWidth / 2), + new TGeoRotation("", ang1, ang2, ang3))); + mechStavVol->AddNode( + volTM0a, 1, + new TGeoCombiTrans(x, yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - (headWidth / 2), + new TGeoRotation("", ang1, ang2, ang3))); + } + auto* tM0c = new TGeoBBox(0.3 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c = new TGeoVolume("microChanTop2", tM0c, medKapton); + volTM0c->SetLineColor(35); + volTM0c->SetFillColor(35); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c, 0, new TGeoCombiTrans(x + (smcWidth / 2) - (0.3 / 2), yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); + mechStavVol->AddNode( + volTM0c, 1, new TGeoCombiTrans(x - (smcWidth / 2) + (0.3 / 2), yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0c1 = new TGeoBBox(0.2225 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c1 = new TGeoVolume("microChanBot1", tM0c1, medKapton); + volTM0c1->SetLineColor(6); + volTM0c1->SetFillColor(6); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c1, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick) - (0.2225 / 2), yMC + 0.03 - hh - (0.003), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0c1, 1, new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick) + (smcSide2Thick) + (smcSide4Thick) + (0.2225 / 2), yMC + 0.03 - hh - (0.003), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0c2 = new TGeoBBox(0.072 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c2 = new TGeoVolume("microChanBot2", tM0c2, medKapton); + volTM0c2->SetLineColor(35); + volTM0c2->SetFillColor(35); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c2, 0, new TGeoCombiTrans(x + smcWidth / 2 - (0.072 / 2), yMC + 0.03 - (0.035 + 0.0015) - (0.003) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0c2r = new TGeoBBox(0.068 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c2r = new TGeoVolume("microChanBot3", tM0c2r, medKapton); + volTM0c2r->SetLineColor(35); + volTM0c2r->SetFillColor(35); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c2r, 0, new TGeoCombiTrans(x - smcWidth / 2 + (0.068 / 2), yMC + 0.03 - (0.035 + 0.0015) - (0.003) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0d = new TGeoBBox(smcSide1Thick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0d = new TGeoVolume("microChanSide1", tM0d, medKapton); + volTM0d->SetLineColor(12); + volTM0d->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0d, 1, new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + + auto* tM0d1 = new TGeoBBox(smcSide2Thick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0d1 = new TGeoVolume("microChanSide2", tM0d1, medKapton); + volTM0d1->SetLineColor(12); + volTM0d1->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d1, 0, + new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick / 2), + yMC + 0.03 - (0.003 + 0.035) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0d1, 1, + new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick) + (smcSide2Thick / 2), + yMC + 0.03 - (0.003 + 0.035) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0d2 = new TGeoBBox(smcSide3Thick / 2, (hh + 0.003) / 2, smcLength / 2); + auto* volTM0d2 = new TGeoVolume("microChanSide3", tM0d2, medKapton); + volTM0d2->SetLineColor(12); + volTM0d2->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d2, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick / 2), yMC + 0.03 - (0.003 + hh + 0.003) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0d2r = new TGeoBBox(smcSide4Thick / 2, (hh + 0.003) / 2, smcLength / 2); + auto* volTM0d2r = new TGeoVolume("microChanSide4", tM0d2r, medKapton); + volTM0d2r->SetLineColor(12); + volTM0d2r->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d2r, 0, + new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick) + (smcSide2Thick) + + (smcSide4Thick / 2), + yMC + 0.03 - (0.003 + hh + 0.003) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0e = new TGeoBBox(smcSide5Thick / 2, hh / 2, smcLength / 2); + auto* volTM0e = new TGeoVolume("microChanSide5", tM0e, medKapton); + volTM0e->SetLineColor(12); + volTM0e->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + for (Int_t ie = 0; ie < 11; ie++) { + mechStavVol->AddNode( + volTM0e, 0, + new TGeoCombiTrans(x - (ie * (smcSpace + smcSide5Thick)) + smcWidth / 2 - + (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick)-smcSpace - (smcSide5Thick / 2), + yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0e, 1, + new TGeoCombiTrans(x + (ie * (smcSpace + smcSide5Thick)) - smcWidth / 2 + + (smcSide1Thick) + (liquidThick) + (smcSide2Thick) + (smcSide4Thick) + + smcSpace + (smcSide5Thick / 2), + yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + } + + auto* tM0f = new TGeoBBox(0.02 / 2, hh / 2, smcLength / 2); + auto* volTM0f = new TGeoVolume("microChanTop3", tM0f, medKapton); + // Double_t smcChannels=12; + Double_t smcCloseWallvapor = smcWidth / 2 - smcSide1Thick - vaporThick - smcSide2Thick - + smcSide3Thick - 12 * smcSpace - 11 * smcSide5Thick; + Double_t smcCloseWallliquid = smcWidth / 2 - smcSide1Thick - liquidThick - smcSide2Thick - + smcSide4Thick - 12 * smcSpace - 11 * smcSide5Thick; + volTM0f->SetLineColor(12); + volTM0f->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0f, 0, + new TGeoCombiTrans(x + smcCloseWallvapor - (0.02) / 2, yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0f, 1, + new TGeoCombiTrans(x - smcCloseWallliquid + (0.02) / 2, yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + // Head(back) microchannel + + auto* tM0hb = new TGeoBBox(smcWidth / 2, 0.025 / 2, headWidth / 2); + auto* volTM0hb = new TGeoVolume("microChanHeadBackBottom1", tM0hb, medKapton); + volTM0hb->SetLineColor(4); + volTM0hb->SetFillColor(4); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0hb, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0145 - (0.025 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + (headWidth / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0hb, 1, new TGeoCombiTrans(x, yMC + 0.03 - 0.0145 - (0.025) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - (headWidth / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0h1 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.05 / 2); + auto* volTM0h1 = new TGeoVolume("microChanHeadBackBottom2", tM0h1, medKapton); + volTM0h1->SetLineColor(5); + volTM0h1->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0h1, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - headWidth + (0.05 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0h2 = new TGeoBBox(smcWidth / 2, 0.003 / 2, 0.18 / 2); + auto* volTM0h2 = new TGeoVolume("microChanHeadBackBottom7", tM0h2, medKapton); + volTM0h2->SetLineColor(6); + volTM0h2->SetFillColor(6); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0h2, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - 0.01 - (0.003 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - 0.02 - (0.18 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0h3 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.02 / 2); + auto* volTM0h3 = new TGeoVolume("microChanHeadBackBottom3", tM0h3, medKapton); + volTM0h3->SetLineColor(5); + volTM0h3->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0h3, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - (0.02 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0b1 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.03 / 2); + auto* volTM0b1 = new TGeoVolume("microChanHeadBackBottom4", tM0b1, medKapton); + volTM0b1->SetLineColor(5); + volTM0b1->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0b1, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + headWidth - (0.03 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0b2 = new TGeoBBox(smcWidth / 2, 0.003 / 2, 0.2 / 2); + auto* volTM0b2 = new TGeoVolume("microChanHeadBackBottom5", tM0b2, medKapton); + volTM0b2->SetLineColor(6); + volTM0b2->SetFillColor(6); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0b2, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - 0.01 - (0.003 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + 0.02 + (0.2 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0b3 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.02 / 2); + auto* volTM0b3 = new TGeoVolume("microChanHeadBackBottom6", tM0b3, medKapton); + volTM0b3->SetLineColor(5); + volTM0b3->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0b3, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + (0.02 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + + auto* tM0b = new TGeoBBox(0.02 / 2, 0.02 / 2, zsta); + auto* volTM0b = new TGeoVolume("microChanWalls", tM0b, medKapton); + volTM0b->SetLineColor(35); + volTM0b->SetFillColor(35); + for (Int_t ib = 0; ib < nb; ib++) { + // mechStavVol->AddNode(volTM0b, ib, new TGeoCombiTrans(x+ib*0.1-xstaMC+0.01,yMC, z, new + // TGeoRotation("",0, 0, 0))); + } + } + + if (mBuildLevel < 4) { + // cooling inlet outlet + auto* tM0dv = new TGeoBBox(vaporThick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0dv = new TGeoVolume("microChanVapor", tM0dv, medWater); + volTM0dv->SetLineColor(2); + volTM0dv->SetFillColor(2); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0dv, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0dl = new TGeoBBox(liquidThick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0dl = new TGeoVolume("microChanLiquid", tM0dl, medWater); + volTM0dl->SetLineColor(3); + volTM0dl->SetFillColor(3); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0dl, 0, new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + // small cooling fluid now using water wait for freeon value + auto* tM0dlq = new TGeoBBox(smcSpace / 2, hh / 2, smcLength / 2); + auto* volTM0dlq = new TGeoVolume("smallLiquid", tM0dlq, medWater); + volTM0dlq->SetLineColor(3); + volTM0dlq->SetFillColor(3); + auto* tM0dvp = new TGeoBBox(smcSpace / 2, hh / 2, smcLength / 2); + auto* volTM0dvp = new TGeoVolume("microChanVapor", tM0dvp, medWater); + volTM0dvp->SetLineColor(2); + volTM0dvp->SetFillColor(2); + for (Int_t mo = 1; mo <= chips; mo++) { + for (Int_t is = 0; is < 12; is++) { + mechStavVol->AddNode( + volTM0dlq, 0, new TGeoCombiTrans(x + (is * (smcSpace + smcSide5Thick)) - smcWidth / 2 + (smcSide1Thick) + (vaporThick) + (smcSide2Thick) + (smcSide3Thick) + smcSpace / 2, yMC + 0.03 - (0.003 + hh) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0dvp, 1, new TGeoCombiTrans(x - (is * (smcSpace + smcSide5Thick)) + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick)-smcSpace / 2, yMC + 0.03 - (0.003 + hh) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + } + } + + if (mBuildLevel < 3) { + // Bottom filament CFRP black-12 Carbon structure TGeoBBox (thickness,width,length) + Double_t filWidth = 0.04; + Double_t filHeight = 0.02; + auto* t1 = new TGeoBBox(filHeight / 2, filWidth / 2, s1); + auto* volT1 = new TGeoVolume("bottomFilament", t1, medM60J3K); + volT1->SetLineColor(12); + volT1->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode(volT1, 4 * i + 0, + new TGeoCombiTrans(x + w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * l * i) + s1 / 2, + new TGeoRotation("volT1", -90, alpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 1, + new TGeoCombiTrans(x - w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * l * i) + s1 / 2, + new TGeoRotation("volT1", 90, alpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 2, + new TGeoCombiTrans(x + w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT1", -90, -alpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 3, + new TGeoCombiTrans(x - w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT1", -90, +alpha, 0))); + } + + // Top filament CERP black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox(s2, filHeight / 2, filWidth / 2); + auto* volT2 = new TGeoVolume("topFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode( + volT2, 4 * i + 0, new TGeoCombiTrans(x + w, y + 0.04 + filHeight / 2, z - kStaveLength / 2 + (i * 4 * l) + s1 / 2, new TGeoRotation("volT2", 90, 90 - alpha, 90 - beta))); + mechStavVol->AddNode( + volT2, 4 * i + 1, + new TGeoCombiTrans(x - w, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT2", 90, -90 + alpha, -90 + beta))); + mechStavVol->AddNode( + volT2, 4 * i + 2, + new TGeoCombiTrans(x + w, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT2", 90, -90 + alpha, 90 - beta))); + mechStavVol->AddNode( + volT2, 4 * i + 3, + new TGeoCombiTrans(x - w, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT2", 90, 90 - alpha, -90 + beta))); + } + } + + if (mBuildLevel < 2) { + // Glue Filament and Silicon MicroChannel + auto* tM0 = new TGeoBBox(xstaMC / 5, klay4 / 2, zsta); + auto* volTM0 = new TGeoVolume("glueFM", tM0, medGlue); + volTM0->SetLineColor(5); + volTM0->SetFillColor(5); + mechStavVol->AddNode(volTM0, 0, new TGeoCombiTrans(x - xsta / 2 - 0.25, 0.03 + yMC, z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volTM0, 1, new TGeoCombiTrans(x + xsta / 2 + 0.25, 0.03 + yMC, z, new TGeoRotation("", 0, 0, 0))); + + // Glue microchannel and sensor + auto* glueM = new TGeoBBox(xstaMC / 5, klay4 / 2, zsta); + auto* volGlueM = new TGeoVolume("glueMS", glueM, medGlue); + volGlueM->SetLineColor(5); + volGlueM->SetFillColor(5); + mechStavVol->AddNode(volGlueM, 0, new TGeoCombiTrans(x - xsta / 2 - 0.25, yMC - 0.01, z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volGlueM, 1, new TGeoCombiTrans(x + xsta / 2 + 0.25, yMC - 0.01, z, new TGeoRotation("", 0, 0, 0))); + + // Glue sensor and kapton + auto* glue = new TGeoBBox(xsta, klay4 / 2, zsta); + auto* volGlue = new TGeoVolume("glueSensorBus", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + mechStavVol->AddNode(volGlue, 1, new TGeoCombiTrans(x, y - 0.154 - mSensorThickness - klay4 / 2, z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + auto* kapCable = new TGeoBBox(xsta, klay5 / 2, zsta); + auto* volCable = new TGeoVolume("Flexcable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - 0.154 - mSensorThickness - klay4 - klay5 / 2, z, + new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structure + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveOuterB(const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + mechStavVol = createStaveModelOuterBDummy(mgr); + break; + case Detector::kOBModel0: + mechStavVol = createStaveModelOuterB0(mgr); + break; + case Detector::kOBModel1: + mechStavVol = createStaveModelOuterB1(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelOuterBDummy(const TGeoManager*) const +{ + // Done, return the stave structure + return nullptr; +} + +TGeoVolume* V1Layer::createStaveModelOuterB0(const TGeoManager* mgr) +{ + Double_t xmod, ymod, zmod; + Double_t xlen, ylen, zlen; + Double_t ypos, zpos; + char volumeName[30]; + + // First create all needed shapes + // The chip + xlen = sOBHalfStaveWidth; + ylen = 0.5 * mStaveThickness; // TO BE CHECKED + zlen = sOBModuleZLength / 2; + + TGeoVolume* chipVol = createChipInnerB(xlen, ylen, zlen); + + xmod = ((TGeoBBox*)chipVol->GetShape())->GetDX(); + ymod = ((TGeoBBox*)chipVol->GetShape())->GetDY(); + zmod = ((TGeoBBox*)chipVol->GetShape())->GetDZ(); + + auto* module = new TGeoBBox(xmod, ymod, zmod); + + zlen = sOBModuleZLength * mNumberOfModules; + auto* hstave = new TGeoBBox(xlen, ylen, zlen / 2); + + // We have all shapes: now create the real volumes + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSModulePattern(), mLayerNumber); + auto* modVol = new TGeoVolume(volumeName, module, medAir); + modVol->SetVisibility(kTRUE); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + auto* hstaveVol = new TGeoVolume(volumeName, hstave, medAir); + + // Finally build it up + modVol->AddNode(chipVol, 0); + mHierarchy[kChip] = 1; + + for (Int_t j = 0; j < mNumberOfModules; j++) { + ypos = 0.021; // Remove small overlap - M.S: 21may13 + zpos = -hstave->GetDZ() + j * 2 * zmod + zmod; + hstaveVol->AddNode(modVol, j, new TGeoTranslation(0, ypos, zpos)); + mHierarchy[kModule]++; + } + // Done, return the stave structure + return hstaveVol; +} + +TGeoVolume* V1Layer::createStaveModelOuterB1(const TGeoManager* mgr) +{ + Double_t yFlex1 = sOBFlexCableAlThick; + Double_t yFlex2 = sOBFlexCableKapThick; + Double_t flexOverlap = 5; // to be checked + Double_t xHalmSt = sOBHalfStaveWidth / 2; + Double_t rCoolMin = sOBCoolTubeInnerD / 2; + Double_t rCoolMax = rCoolMin + sOBCoolTubeThick; + Double_t kLay1 = 0.004; // to be checked + Double_t kLay2 = sOBGraphiteFoilThick; + + Double_t xlen, ylen; + Double_t ymod, zmod; + Double_t xtru[12], ytru[12]; + Double_t xpos, ypos, ypos1, zpos /*, zpos5cm*/; + Double_t zlen; + char volumeName[30]; + + zlen = (mNumberOfModules * sOBModuleZLength + (mNumberOfModules - 1) * sOBModuleGap) / 2; + + // First create all needed shapes + TGeoVolume* moduleVol = createModuleOuterB(); + moduleVol->SetVisibility(kTRUE); + ymod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDY(); + zmod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDZ(); + + auto* busAl = new TGeoBBox("BusAl", xHalmSt, sOBBusCableAlThick / 2, zlen); + auto* busKap = new TGeoBBox("BusKap", xHalmSt, sOBBusCableKapThick / 2, zlen); + + auto* coldPlate = + new TGeoBBox("ColdPlate", sOBHalfStaveWidth / 2, sOBColdPlateThick / 2, zlen); + + auto* coolTube = new TGeoTube("CoolingTube", rCoolMin, rCoolMax, zlen); + auto* coolWater = new TGeoTube("CoolingWater", 0., rCoolMin, zlen); + + xlen = xHalmSt - sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + auto* graphlat = new TGeoBBox("GraphLateral", xlen / 2, kLay2 / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + auto* graphmid = new TGeoBBox("GraphMiddle", xlen, kLay2 / 2, zlen); + + ylen = coolTube->GetRmax() - kLay2; + auto* graphvert = new TGeoBBox("GraphVertical", kLay2 / 2, ylen / 2, zlen); + + auto* graphtub = + new TGeoTubeSeg("GraphTube", rCoolMax, rCoolMax + kLay2, zlen, 180., 360.); + + xlen = xHalmSt - sOBCoolTubeXDist / 2 - coolTube->GetRmax() - kLay2; + auto* fleeclat = new TGeoBBox("FleecLateral", xlen / 2, kLay1 / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax() - kLay2; + auto* fleecmid = new TGeoBBox("FleecMiddle", xlen, kLay1 / 2, zlen); + + ylen = coolTube->GetRmax() - kLay2 - kLay1; + auto* fleecvert = new TGeoBBox("FleecVertical", kLay1 / 2, ylen / 2, zlen); + + auto* fleectub = + new TGeoTubeSeg("FleecTube", rCoolMax + kLay2, rCoolMax + kLay1 + kLay2, zlen, 180., 360.); + + auto* flex1_5cm = new TGeoBBox("Flex1MV_5cm", xHalmSt, yFlex1 / 2, flexOverlap / 2); + auto* flex2_5cm = new TGeoBBox("Flex2MV_5cm", xHalmSt, yFlex2 / 2, flexOverlap / 2); + + // The half stave container (an XTru to avoid overlaps between neightbours) + xtru[0] = xHalmSt; + ytru[0] = 0; + xtru[1] = xtru[0]; + ytru[1] = -2 * (ymod + busAl->GetDY() + busKap->GetDY() + coldPlate->GetDY() + graphlat->GetDY() + + fleeclat->GetDY()); + xtru[2] = sOBCoolTubeXDist / 2 + fleectub->GetRmax(); + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[2] - (coolTube->GetRmax() + fleectub->GetRmax()); + xtru[4] = sOBCoolTubeXDist / 2 - fleectub->GetRmax(); + ytru[4] = ytru[3]; + xtru[5] = xtru[4]; + ytru[5] = ytru[2]; + for (Int_t i = 0; i < 6; i++) { + xtru[6 + i] = -xtru[5 - i]; + ytru[6 + i] = ytru[5 - i]; + } + auto* halmStave = new TGeoXtru(2); + halmStave->DefinePolygon(12, xtru, ytru); + halmStave->DefineSection(0, -mZLength / 2); + halmStave->DefineSection(1, mZLength / 2); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAluminum = mgr->GetMedium("TRK_ALUMINUM$"); + TGeoMedium* medCarbon = mgr->GetMedium("TRK_CARBON$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + TGeoMedium* medFGS003 = mgr->GetMedium("TRK_FGS003$"); // amec thermasol + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + auto* busAlVol = new TGeoVolume("BusAlVol", busAl, medAluminum); + busAlVol->SetLineColor(kCyan); + busAlVol->SetFillColor(busAlVol->GetLineColor()); + busAlVol->SetFillStyle(4000); // 0% transparent + + auto* busKapVol = new TGeoVolume("BusKapVol", busKap, medKapton); + busKapVol->SetLineColor(kBlue); + busKapVol->SetFillColor(busKapVol->GetLineColor()); + busKapVol->SetFillStyle(4000); // 0% transparent + + auto* coldPlateVol = new TGeoVolume("ColdPlateVol", coldPlate, medCarbon); + coldPlateVol->SetLineColor(kYellow - 3); + coldPlateVol->SetFillColor(coldPlateVol->GetLineColor()); + coldPlateVol->SetFillStyle(4000); // 0% transparent + + auto* coolTubeVol = new TGeoVolume("CoolingTubeVol", coolTube, medKapton); + coolTubeVol->SetLineColor(kGray); + coolTubeVol->SetFillColor(coolTubeVol->GetLineColor()); + coolTubeVol->SetFillStyle(4000); // 0% transparent + + auto* coolWaterVol = new TGeoVolume("CoolingWaterVol", coolWater, medWater); + coolWaterVol->SetLineColor(kBlue); + coolWaterVol->SetFillColor(coolWaterVol->GetLineColor()); + coolWaterVol->SetFillStyle(4000); // 0% transparent + + auto* graphlatVol = new TGeoVolume("GraphiteFoilLateral", graphlat, medFGS003); + graphlatVol->SetLineColor(kGreen); + graphlatVol->SetFillColor(graphlatVol->GetLineColor()); + graphlatVol->SetFillStyle(4000); // 0% transparent + + auto* graphmidVol = new TGeoVolume("GraphiteFoilMiddle", graphmid, medFGS003); + graphmidVol->SetLineColor(kGreen); + graphmidVol->SetFillColor(graphmidVol->GetLineColor()); + graphmidVol->SetFillStyle(4000); // 0% transparent + + auto* graphvertVol = new TGeoVolume("GraphiteFoilVertical", graphvert, medFGS003); + graphvertVol->SetLineColor(kGreen); + graphvertVol->SetFillColor(graphvertVol->GetLineColor()); + graphvertVol->SetFillStyle(4000); // 0% transparent + + auto* graphtubVol = new TGeoVolume("GraphiteFoilPipeCover", graphtub, medFGS003); + graphtubVol->SetLineColor(kGreen); + graphtubVol->SetFillColor(graphtubVol->GetLineColor()); + graphtubVol->SetFillStyle(4000); // 0% transparent + + auto* fleeclatVol = new TGeoVolume("CarbonFleeceLateral", fleeclat, medCarbonFleece); + fleeclatVol->SetLineColor(kViolet); + fleeclatVol->SetFillColor(fleeclatVol->GetLineColor()); + fleeclatVol->SetFillStyle(4000); // 0% transparent + + auto* fleecmidVol = new TGeoVolume("CarbonFleeceMiddle", fleecmid, medCarbonFleece); + fleecmidVol->SetLineColor(kViolet); + fleecmidVol->SetFillColor(fleecmidVol->GetLineColor()); + fleecmidVol->SetFillStyle(4000); // 0% transparent + + auto* fleecvertVol = new TGeoVolume("CarbonFleeceVertical", fleecvert, medCarbonFleece); + fleecvertVol->SetLineColor(kViolet); + fleecvertVol->SetFillColor(fleecvertVol->GetLineColor()); + fleecvertVol->SetFillStyle(4000); // 0% transparent + + auto* fleectubVol = new TGeoVolume("CarbonFleecePipeCover", fleectub, medCarbonFleece); + fleectubVol->SetLineColor(kViolet); + fleectubVol->SetFillColor(fleectubVol->GetLineColor()); + fleectubVol->SetFillStyle(4000); // 0% transparent + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + auto* halmStaveVol = new TGeoVolume(volumeName, halmStave, medAir); + // halmStaveVol->SetLineColor(12); + // halmStaveVol->SetFillColor(12); + // halmStaveVol->SetVisibility(kTRUE); + + auto* flex1_5cmVol = new TGeoVolume("Flex1Vol5cm", flex1_5cm, medAluminum); + auto* flex2_5cmVol = new TGeoVolume("Flex2Vol5cm", flex2_5cm, medKapton); + + flex1_5cmVol->SetLineColor(kRed); + flex2_5cmVol->SetLineColor(kGreen); + + // Now build up the half stave + ypos = -busKap->GetDY(); + halmStaveVol->AddNode(busKapVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos -= (busKap->GetDY() + busAl->GetDY()); + halmStaveVol->AddNode(busAlVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos -= (busAl->GetDY() + ymod); + for (Int_t j = 0; j < mNumberOfModules; j++) { + zpos = -zlen + j * (2 * zmod + sOBModuleGap) + zmod; + halmStaveVol->AddNode(moduleVol, j, new TGeoTranslation(0, ypos, zpos)); + mHierarchy[kModule]++; + } + + ypos -= (ymod + coldPlate->GetDY()); + halmStaveVol->AddNode(coldPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + + coolTubeVol->AddNode(coolWaterVol, 1, nullptr); + + xpos = sOBCoolTubeXDist / 2; + ypos1 = ypos - (coldPlate->GetDY() + coolTube->GetRmax()); + halmStaveVol->AddNode(coolTubeVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(coolTubeVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(graphtubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphtubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(fleectubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleectubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + xpos = xHalmSt - graphlat->GetDX(); + ypos1 = ypos - (coldPlate->GetDY() + graphlat->GetDY()); + halmStaveVol->AddNode(graphlatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphlatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(graphmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = xHalmSt - 2 * graphlat->GetDX() + graphvert->GetDX(); + ypos1 = ypos - (coldPlate->GetDY() + 2 * graphlat->GetDY() + graphvert->GetDY()); + halmStaveVol->AddNode(graphvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = graphmid->GetDX() - graphvert->GetDX(); + halmStaveVol->AddNode(graphvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + + xpos = xHalmSt - fleeclat->GetDX(); + ypos1 = ypos - (coldPlate->GetDY() + 2 * graphlat->GetDY() + fleeclat->GetDY()); + halmStaveVol->AddNode(fleeclatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleeclatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(fleecmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = xHalmSt - 2 * fleeclat->GetDX() + fleecvert->GetDX(); + ypos1 = ypos - + (coldPlate->GetDY() + 2 * graphlat->GetDY() + 2 * fleeclat->GetDY() + fleecvert->GetDY()); + halmStaveVol->AddNode(fleecvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleecvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = fleecmid->GetDX() - fleecvert->GetDX(); + halmStaveVol->AddNode(fleecvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleecvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + + // THE FOLLOWING IS ONLY A REMINDER FOR WHAT IS STILL MISSING + + // for (Int_t j=0; j<mNumberOfChips; j++) { + + // zpos = -(zact + (mNumberOfChips-1)*modGap)/2 + j*(zMod + modGap) + zMod/2; + // zpos5cm = -(zact + (mNumberOfChips-1)*modGap)/2 + (j+1)*(zMod + modGap) + flexOverlap/2 ; + + // halmStaveVol->AddNode(moduleVol, j, new TGeoTranslation(xPos, -ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + ymod, zpos)); + // halmStaveVol->AddNode(moduleVol, mNumberOfChips+j, new TGeoTranslation(-xPos, -ylen + yPos + // + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + ymod +deltaY, zpos)); + + // if((j+1)!=mNumberOfChips){ + // halmStaveVol->AddNode(flex1_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + 2*ymod + yFlex1 + yFlex2 + yFlex1/2,zpos5cm)); + // halmStaveVol->AddNode(flex1_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + yFlex1 + yFlex2 + yFlex1/2 + // +deltaY,zpos5cm)); + // halmStaveVol->AddNode(flex2_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + 2*ymod + 2*yFlex1 + 3*yFlex2/2,zpos5cm)); + // halmStaveVol->AddNode(flex2_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + 2*yFlex1 + 3*yFlex2/2 +deltaY,zpos5cm)); + // } + // else { + // halmStaveVol->AddNode(flex1_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + 2*ymod + yFlex1/2,zpos5cm-modGap)); + // halmStaveVol->AddNode(flex1_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + yFlex1/2 +deltaY,zpos5cm-modGap)); + // halmStaveVol->AddNode(flex2_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate +2*ymod + yFlex1 + yFlex2/2,zpos5cm-modGap)); + // halmStaveVol->AddNode(flex2_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + yFlex1 + yFlex2/2 +deltaY,zpos5cm-modGap)); + + // } + // } + // Done, return the half stave structure + return halmStaveVol; +} + +TGeoVolume* V1Layer::createSpaceFrameOuterB(const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + case Detector::kOBModel0: + mechStavVol = createSpaceFrameOuterBDummy(mgr); + break; + case Detector::kOBModel1: + mechStavVol = createSpaceFrameOuterB1(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + + return mechStavVol; +} + +TGeoVolume* V1Layer::createSpaceFrameOuterBDummy(const TGeoManager*) const +{ + // Done, return the stave structur + return nullptr; +} + +TGeoVolume* V1Layer::createSpaceFrameOuterB1(const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medCarbon = mgr->GetMedium("TRK_CARBON$"); + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + // Local parameters + Double_t sframeWidth = sOBSpaceFrameWidth; + Double_t sframeHeight = sOBSpaceFrameTotHigh - sOBHalfStaveYTrans; + Double_t staveBeamRadius = sOBSFrameBeamRadius; + Double_t staveLa = sOBSpaceFrameLa; + Double_t staveHa = sOBSpaceFrameHa; + Double_t staveLb = sOBSpaceFrameLb; + Double_t staveHb = sOBSpaceFrameHb; + Double_t stavel = sOBSpaceFrameL; + Double_t bottomBeamAngle = sOBSFBotBeamAngle; + Double_t triangleHeight = sframeHeight - staveBeamRadius; + Double_t halmTheta = TMath::ATan(0.5 * sframeWidth / triangleHeight); + // Double_t alpha = TMath::Pi()*3./4. - halmTheta/2.; + Double_t beta = (TMath::Pi() - 2. * halmTheta) / 4.; + // Double_t distCenterSideDown = 0.5*sframeWidth/TMath::Cos(beta); + + Double_t zlen; + Double_t xpos, ypos, zpos; + Double_t seglen; + char volumeName[30]; + + zlen = mNumberOfModules * sOBModuleZLength + (mNumberOfModules - 1) * sOBModuleGap; + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + if (gGeoManager->GetVolume(volumeName)) { // Should always be so + sframeHeight -= ((TGeoBBox*)gGeoManager->GetVolume(volumeName)->GetShape())->GetDY() * 2; + zlen = ((TGeoBBox*)gGeoManager->GetVolume(volumeName)->GetShape())->GetDZ() * 2; + } + seglen = zlen / mNumberOfModules; + + // First create all needed shapes and volumes + auto* spaceFrame = new TGeoBBox(sframeWidth / 2, sframeHeight / 2, zlen / 2); + auto* segment = new TGeoBBox(sframeWidth / 2, sframeHeight / 2, seglen / 2); + + auto* spaceFrameVol = new TGeoVolume("CarbonFrameVolume", spaceFrame, medAir); + spaceFrameVol->SetVisibility(kFALSE); + + auto* segmentVol = new TGeoVolume("segmentVol", segment, medAir); + + // SpaceFrame + + //--- the top V of the Carbon Fiber Stave (segment) + TGeoArb8* cmStavTop1 = createStaveSide("CFstavTopCornerVol1shape", seglen / 2., halmTheta, -1, + staveLa, staveHa, stavel); + auto* cmStavTopVol1 = new TGeoVolume("CFstavTopCornerVol1", cmStavTop1, medCarbon); + cmStavTopVol1->SetLineColor(35); + + TGeoArb8* cmStavTop2 = createStaveSide("CFstavTopCornerVol2shape", seglen / 2., halmTheta, 1, + staveLa, staveHa, stavel); + auto* cmStavTopVol2 = new TGeoVolume("CFstavTopCornerVol2", cmStavTop2, medCarbon); + cmStavTopVol2->SetLineColor(35); + + auto* trTop1 = new TGeoTranslation(0, sframeHeight / 2, 0); + + //--- the 2 side V + TGeoArb8* cmStavSide1 = + createStaveSide("CFstavSideCornerVol1shape", seglen / 2., beta, -1, staveLb, staveHb, stavel); + auto* cmStavSideVol1 = new TGeoVolume("CFstavSideCornerVol1", cmStavSide1, medCarbon); + cmStavSideVol1->SetLineColor(35); + + TGeoArb8* cmStavSide2 = + createStaveSide("CFstavSideCornerVol2shape", seglen / 2., beta, 1, staveLb, staveHb, stavel); + auto* cmStavSideVol2 = new TGeoVolume("CFstavSideCornerVol2", cmStavSide2, medCarbon); + cmStavSideVol2->SetLineColor(35); + + xpos = -sframeWidth / 2; + ypos = -sframeHeight / 2 + staveBeamRadius + staveHb * TMath::Sin(beta); + auto* ctSideR = new TGeoCombiTrans( + xpos, ypos, 0, new TGeoRotation("", 180 - 2 * beta * TMath::RadToDeg(), 0, 0)); + auto* ctSideL = new TGeoCombiTrans( + -xpos, ypos, 0, new TGeoRotation("", -180 + 2 * beta * TMath::RadToDeg(), 0, 0)); + + segmentVol->AddNode(cmStavTopVol1, 1, trTop1); + segmentVol->AddNode(cmStavTopVol2, 1, trTop1); + segmentVol->AddNode(cmStavSideVol1, 1, ctSideR); + segmentVol->AddNode(cmStavSideVol1, 2, ctSideL); + segmentVol->AddNode(cmStavSideVol2, 1, ctSideR); + segmentVol->AddNode(cmStavSideVol2, 2, ctSideL); + + //--- The beams + // Beams on the sides + Double_t beamPhiPrime = TMath::ASin( + 1. / TMath::Sqrt((1 + TMath::Sin(2 * beta) * TMath::Sin(2 * beta) / + (tanD(sOBSFrameBeamSidePhi) * tanD(sOBSFrameBeamSidePhi))))); + Double_t beamLength = TMath::Sqrt(sframeHeight * sframeHeight / + (TMath::Sin(beamPhiPrime) * TMath::Sin(beamPhiPrime)) + + sframeWidth * sframeWidth / 4.) - + staveLa / 2 - staveLb / 2; + auto* sideBeam = new TGeoTubeSeg(0, staveBeamRadius, beamLength / 2, 0, 180); + auto* sideBeamVol = new TGeoVolume("CFstavSideBeamVol", sideBeam, medCarbon); + sideBeamVol->SetLineColor(35); + + auto* beamRot1 = new TGeoRotation("", /*90-2*beta*/ halmTheta * TMath::RadToDeg(), + -beamPhiPrime * TMath::RadToDeg(), -90); + auto* beamRot2 = + new TGeoRotation("", 90 - 2. * beta * TMath::RadToDeg(), beamPhiPrime * TMath::RadToDeg(), -90); + auto* beamRot3 = + new TGeoRotation("", 90 + 2. * beta * TMath::RadToDeg(), beamPhiPrime * TMath::RadToDeg(), -90); + auto* beamRot4 = new TGeoRotation("", 90 + 2. * beta * TMath::RadToDeg(), + -beamPhiPrime * TMath::RadToDeg(), -90); + + TGeoCombiTrans* beamTransf[8]; + xpos = 0.49 * triangleHeight * TMath::Tan(halmTheta); // was 0.5, fix small overlap + ypos = staveBeamRadius / 2; + zpos = seglen / 8; + beamTransf[0] = new TGeoCombiTrans(xpos, ypos, -3 * zpos, beamRot1); + + beamTransf[1] = new TGeoCombiTrans(xpos, ypos, -3 * zpos, beamRot1); + addTranslationToCombiTrans(beamTransf[1], 0, 0, seglen / 2); + + beamTransf[2] = new TGeoCombiTrans(xpos, ypos, -zpos, beamRot2); + + beamTransf[3] = new TGeoCombiTrans(xpos, ypos, -zpos, beamRot2); + addTranslationToCombiTrans(beamTransf[3], 0, 0, seglen / 2); + + beamTransf[4] = new TGeoCombiTrans(-xpos, ypos, -3 * zpos, beamRot3); + + beamTransf[5] = new TGeoCombiTrans(-xpos, ypos, -3 * zpos, beamRot3); + addTranslationToCombiTrans(beamTransf[5], 0, 0, seglen / 2); + + beamTransf[6] = new TGeoCombiTrans(-xpos, ypos, -zpos, beamRot4); + beamTransf[7] = new TGeoCombiTrans(-xpos, ypos, 3 * zpos, beamRot4); + + //--- Beams of the bottom + auto* bottomBeam1 = + new TGeoTubeSeg(0, staveBeamRadius, sframeWidth / 2. - staveLb / 3, 0, 180); + auto* bottomBeam1Vol = new TGeoVolume("CFstavBottomBeam1Vol", bottomBeam1, medCarbon); + bottomBeam1Vol->SetLineColor(35); + + auto* bottomBeam2 = + new TGeoTubeSeg(0, staveBeamRadius, sframeWidth / 2. - staveLb / 3, 0, 90); + auto* bottomBeam2Vol = new TGeoVolume("CFstavBottomBeam2Vol", bottomBeam2, medCarbon); + bottomBeam2Vol->SetLineColor(35); + + auto* bottomBeam3 = new TGeoTubeSeg( + 0, staveBeamRadius, 0.5 * sframeWidth / sinD(bottomBeamAngle) - staveLb / 3, 0, 180); + auto* bottomBeam3Vol = new TGeoVolume("CFstavBottomBeam3Vol", bottomBeam3, medCarbon); + bottomBeam3Vol->SetLineColor(35); + + auto* bottomBeamRot1 = new TGeoRotation("", 90, 90, 90); + auto* bottomBeamRot2 = new TGeoRotation("", -90, 90, -90); + + auto* bottomBeamTransf1 = + new TGeoCombiTrans("", 0, -(sframeHeight / 2 - staveBeamRadius), 0, bottomBeamRot1); + auto* bottomBeamTransf2 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), -seglen / 2, bottomBeamRot1); + auto* bottomBeamTransf3 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), seglen / 2, bottomBeamRot2); + // be careful for beams #3: when "reading" from -z to +z and + // from the bottom of the stave, it should draw a Lambda, and not a V + auto* bottomBeamRot4 = new TGeoRotation("", -90, bottomBeamAngle, -90); + auto* bottomBeamRot5 = new TGeoRotation("", -90, -bottomBeamAngle, -90); + + auto* bottomBeamTransf4 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), -seglen / 4, bottomBeamRot4); + auto* bottomBeamTransf5 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), seglen / 4, bottomBeamRot5); + + segmentVol->AddNode(sideBeamVol, 1, beamTransf[0]); + segmentVol->AddNode(sideBeamVol, 2, beamTransf[1]); + segmentVol->AddNode(sideBeamVol, 3, beamTransf[2]); + segmentVol->AddNode(sideBeamVol, 4, beamTransf[3]); + segmentVol->AddNode(sideBeamVol, 5, beamTransf[4]); + segmentVol->AddNode(sideBeamVol, 6, beamTransf[5]); + segmentVol->AddNode(sideBeamVol, 7, beamTransf[6]); + segmentVol->AddNode(sideBeamVol, 8, beamTransf[7]); + segmentVol->AddNode(bottomBeam1Vol, 1, bottomBeamTransf1); + segmentVol->AddNode(bottomBeam2Vol, 1, bottomBeamTransf2); + segmentVol->AddNode(bottomBeam2Vol, 2, bottomBeamTransf3); + segmentVol->AddNode(bottomBeam3Vol, 1, bottomBeamTransf4); + segmentVol->AddNode(bottomBeam3Vol, 2, bottomBeamTransf5); + + // Then build up the space frame + for (Int_t i = 0; i < mNumberOfModules; i++) { + zpos = -spaceFrame->GetDZ() + (1 + 2 * i) * segment->GetDZ(); + spaceFrameVol->AddNode(segmentVol, i, new TGeoTranslation(0, 0, zpos)); + } + + // Done, return the space frame structure + return spaceFrameVol; +} + +TGeoVolume* V1Layer::createChipInnerB(const Double_t xchip, const Double_t ychip, + const Double_t zchip, const TGeoManager* mgr) +{ + char volumeName[30]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // First create all needed shapes + + // The chip + auto* chip = new TGeoBBox(xchip, ychip, zchip); + + // The sensor + xlen = chip->GetDX(); + ylen = 0.5 * mSensorThickness; + zlen = chip->GetDZ(); + auto* sensor = new TGeoBBox(xlen, ylen, zlen); + + // We have all shapes: now create the real volumes + TGeoMedium* medSi = mgr->GetMedium("TRK_SI$"); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSChipPattern(), mLayerNumber); + auto* chipVol = new TGeoVolume(volumeName, chip, medSi); + chipVol->SetVisibility(kTRUE); + chipVol->SetLineColor(1); + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSSensorPattern(), mLayerNumber); + auto* sensVol = new TGeoVolume(volumeName, sensor, medSi); + sensVol->SetVisibility(kTRUE); + sensVol->SetLineColor(8); + sensVol->SetLineWidth(1); + sensVol->SetFillColor(sensVol->GetLineColor()); + sensVol->SetFillStyle(4000); // 0% transparent + + // Now build up the chip + xpos = 0.; + ypos = -chip->GetDY() + sensor->GetDY(); + zpos = 0.; + + chipVol->AddNode(sensVol, 1, new TGeoTranslation(xpos, ypos, zpos)); + + // Done, return the chip + return chipVol; +} + +TGeoVolume* V1Layer::createModuleOuterB(const TGeoManager* mgr) +{ + char volumeName[30]; + + Double_t xGap = sOBChipXGap; + Double_t zGap = sOBChipZGap; + + Double_t xchip, ychip, zchip; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // First create all needed shapes + + // The chip (the same as for IB) + xlen = (sOBHalfStaveWidth / 2 - xGap / 2) / sOBNChipRows; + ylen = 0.5 * mStaveThickness; // TO BE CHECKED + zlen = (sOBModuleZLength - (sOBChipsPerRow - 1) * zGap) / (2 * sOBChipsPerRow); + + TGeoVolume* chipVol = createChipInnerB(xlen, ylen, zlen); + + xchip = ((TGeoBBox*)chipVol->GetShape())->GetDX(); + ychip = ((TGeoBBox*)chipVol->GetShape())->GetDY(); + zchip = ((TGeoBBox*)chipVol->GetShape())->GetDZ(); + + // The module carbon plate + xlen = sOBHalfStaveWidth / 2; + ylen = sOBCarbonPlateThick / 2; + zlen = sOBModuleZLength / 2; + auto* modPlate = new TGeoBBox("CarbonPlate", xlen, ylen, zlen); + + // The glue + ylen = sOBGlueThick / 2; + auto* glue = new TGeoBBox("Glue", xlen, ylen, zlen); + + // The flex cables + ylen = sOBFlexCableAlThick / 2; + auto* flexAl = new TGeoBBox("FlexAl", xlen, ylen, zlen); + + ylen = sOBFlexCableKapThick / 2; + auto* flexKap = new TGeoBBox("FlexKap", xlen, ylen, zlen); + + // The module + xlen = sOBHalfStaveWidth / 2; + ylen = ychip + modPlate->GetDY() + glue->GetDY() + flexAl->GetDY() + flexKap->GetDY(); + zlen = sOBModuleZLength / 2; + auto* module = new TGeoBBox("OBModule", xlen, ylen, zlen); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medCarbon = mgr->GetMedium("TRK_CARBON$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medAluminum = mgr->GetMedium("TRK_ALUMINUM$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + + auto* modPlateVol = new TGeoVolume("CarbonPlateVol", modPlate, medCarbon); + modPlateVol->SetLineColor(kMagenta - 8); + modPlateVol->SetFillColor(modPlateVol->GetLineColor()); + modPlateVol->SetFillStyle(4000); // 0% transparent + + auto* glueVol = new TGeoVolume("GlueVol", glue, medGlue); + glueVol->SetLineColor(kBlack); + glueVol->SetFillColor(glueVol->GetLineColor()); + glueVol->SetFillStyle(4000); // 0% transparent + + auto* flexAlVol = new TGeoVolume("FlexAlVol", flexAl, medAluminum); + flexAlVol->SetLineColor(kRed); + flexAlVol->SetFillColor(flexAlVol->GetLineColor()); + flexAlVol->SetFillStyle(4000); // 0% transparent + + auto* flexKapVol = new TGeoVolume("FlexKapVol", flexKap, medKapton); + flexKapVol->SetLineColor(kGreen); + flexKapVol->SetFillColor(flexKapVol->GetLineColor()); + flexKapVol->SetFillStyle(4000); // 0% transparent + + snprintf(volumeName, 30, "%s%d", o2::trk::GeometryTGeo::getITSModulePattern(), mLayerNumber); + auto* modVol = new TGeoVolume(volumeName, module, medAir); + modVol->SetVisibility(kTRUE); + + // Now build up the module + ypos = -module->GetDY() + modPlate->GetDY(); + modVol->AddNode(modPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (modPlate->GetDY() + glue->GetDY()); + modVol->AddNode(glueVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = -module->GetDX() + xchip; + ypos += (glue->GetDY() + ychip); + for (Int_t k = 0; k < sOBChipsPerRow; k++) { // put 7x2 chip into one module + zpos = -module->GetDZ() + zchip + k * (2 * zchip + zGap); + modVol->AddNode(chipVol, 2 * k, new TGeoTranslation(xpos, ypos, zpos)); + modVol->AddNode(chipVol, 2 * k + 1, + new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, 180, 180))); + mHierarchy[kChip] += 2; + } + + ypos += (ychip + flexAl->GetDY()); + modVol->AddNode(flexAlVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (flexAl->GetDY() + flexKap->GetDY()); + modVol->AddNode(flexKapVol, 1, new TGeoTranslation(0, ypos, 0)); + + // Done, return the module + return modVol; +} + +Double_t V1Layer::radiusOmTurboContainer() +{ + Double_t rr, delta, z, lstav, rstav; + + if (mStaveThickness > 89.) { // Very big angle: avoid overflows since surely + return -1; // the radius from lower vertex is the right value + } + + rstav = mLayerRadius + 0.5 * mStaveThickness; + delta = (0.5 * mStaveThickness) / cosD(mStaveTilt); + z = (0.5 * mStaveThickness) * tanD(mStaveTilt); + + rr = rstav - delta; + lstav = (0.5 * mStaveWidth) - z; + + if ((rr * sinD(mStaveTilt) < lstav)) { + return (rr * cosD(mStaveTilt)); + } else { + return -1; + } +} + +void V1Layer::setNumberOfUnits(Int_t u) +{ + if (mLayerNumber < sNumberOmInnerLayers) { + mNumberOfChips = u; + } else { + mNumberOfModules = u; + mNumberOfChips = sOBChipsPerRow; + } +} + +void V1Layer::setStaveTilt(const Double_t t) +{ + if (mIsTurbo) { + mStaveTilt = t; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +void V1Layer::setStaveWidth(const Double_t w) +{ + if (mIsTurbo) { + mStaveWidth = w; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +TGeoArb8* V1Layer::createStaveSide(const char* name, Double_t dz, Double_t angle, + Double_t xSign, Double_t L, Double_t H, Double_t l) +{ + // Create one half of the V shape corner of CF stave + + auto* cmStavSide = new TGeoArb8(dz); + cmStavSide->SetName(name); + + // Points must be in clockwise order + cmStavSide->SetVertex(0, 0, 0); + cmStavSide->SetVertex(2, xSign * (L * TMath::Sin(angle) - l * TMath::Cos(angle)), + -L * TMath::Cos(angle) - l * TMath::Sin(angle)); + cmStavSide->SetVertex(4, 0, 0); + cmStavSide->SetVertex(6, xSign * (L * TMath::Sin(angle) - l * TMath::Cos(angle)), + -L * TMath::Cos(angle) - l * TMath::Sin(angle)); + if (xSign < 0) { + cmStavSide->SetVertex(1, 0, -H); + cmStavSide->SetVertex(3, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + cmStavSide->SetVertex(5, 0, -H); + cmStavSide->SetVertex(7, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + } else { + cmStavSide->SetVertex(1, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + cmStavSide->SetVertex(3, 0, -H); + cmStavSide->SetVertex(5, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + cmStavSide->SetVertex(7, 0, -H); + } + return cmStavSide; +} + +TGeoCombiTrans* V1Layer::createCombiTrans(const char* name, Double_t dy, Double_t dz, + Double_t dphi, Bool_t planeSym) +{ + TGeoTranslation t1(dy * cosD(90. + dphi), dy * sinD(90. + dphi), dz); + TGeoRotation r1("", 0., 0., dphi); + TGeoRotation r2("", 90, 180, -90 - dphi); + + auto* combiTrans1 = new TGeoCombiTrans(name); + combiTrans1->SetTranslation(t1); + if (planeSym) { + combiTrans1->SetRotation(r1); + } else { + combiTrans1->SetRotation(r2); + } + return combiTrans1; +} + +void V1Layer::addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx, Double_t dy, + Double_t dz) const +{ + // Add a dx,dy,dz translation to the initial TGeoCombiTrans + const Double_t* vect = ct->GetTranslation(); + Double_t newVect[3] = {vect[0] + dx, vect[1] + dy, vect[2] + dz}; + ct->SetTranslation(newVect); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/V3Layer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V3Layer.cxx new file mode 100644 index 0000000000000..062dd419f26c1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V3Layer.cxx @@ -0,0 +1,3875 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V3Layer.cxx +/// \brief Implementation of the V3Layer class +/// \author Mario Sitta <sitta@to.infn.it> +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#include "TRKSimulation/V3Layer.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/Detector.h" +#include "ITSMFTSimulation/AlpideChip.h" +#include "ITSMFTBase/SegmentationAlpide.h" + +#include "FairLogger.h" // for LOG + +#include <TGeoArb8.h> // for TGeoArb8 +#include <TGeoBBox.h> // for TGeoBBox +#include <TGeoCone.h> // for TGeoConeSeg, TGeoCone +#include <TGeoPcon.h> // for TGeoPcon +#include <TGeoManager.h> // for TGeoManager, gGeoManager +#include <TGeoMatrix.h> // for TGeoCombiTrans, TGeoRotation, etc +#include <TGeoTrd1.h> // for TGeoTrd1 +#include <TGeoTube.h> // for TGeoTube, TGeoTubeSeg +#include <TGeoVolume.h> // for TGeoVolume, TGeoVolumeAssembly +#include <TGeoXtru.h> // for TGeoXtru +#include <TGeoCompositeShape.h> // for TGeoCompositeShape +#include "TMathBase.h" // for Abs +#include <TMath.h> // for Sin, RadToDeg, DegToRad, Cos, Tan, etc + +#include <cstdio> // for snprintf + +class TGeoMedium; + +using namespace TMath; +using namespace o2::trk; +using namespace o2::itsmft; +using AlpideChip = o2::itsmft::AlpideChip; + +// General Parameters +const Int_t V3Layer::sNumberOfInnerLayers = 3; + +// Inner Barrel Parameters +const Int_t V3Layer::sIBChipsPerRow = 9; +const Int_t V3Layer::sIBNChipRows = 1; +const Double_t V3Layer::sIBChipZGap = 150.0 * sMicron; + +const Double_t V3Layer::sIBModuleZLength = 27.12 * sCm; +const Double_t V3Layer::sIBFPCWiderXPlus = 850.0 * sMicron; +const Double_t V3Layer::sIBFPCWiderXNeg = 300.0 * sMicron; +const Double_t V3Layer::sIBFlexCableAlThick = 25.0 * sMicron; +const Double_t V3Layer::sIBFPCAlGNDWidth = (4.1 + 11.15) * sMm; +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::sIBColdPlateWidth = 15.4 * sMm; +const Double_t V3Layer::sIBColdPlateZLen = 290.0 * sMm; +const Double_t V3Layer::sIBGlueThick = 50.0 * sMicron; +const Double_t V3Layer::sIBCarbonFleeceThick = 20.0 * sMicron; +const Double_t V3Layer::sIBCarbonPaperThick = 30.0 * sMicron; +const Double_t V3Layer::sIBCarbonPaperWidth = 12.5 * sMm; +const Double_t V3Layer::sIBCarbonPaperZLen = 280.0 * sMm; +const Double_t V3Layer::sIBK13D2UThick = 70.0 * sMicron; +const Double_t V3Layer::sIBCoolPipeInnerD = 1.024 * sMm; +const Double_t V3Layer::sIBCoolPipeThick = 25.4 * sMicron; +const Double_t V3Layer::sIBCoolPipeXDist = 5.0 * sMm; +const Double_t V3Layer::sIBCoolPipeZLen = 302.0 * sMm; +const Double_t V3Layer::sIBTopVertexWidth1 = 0.258 * sMm; +const Double_t V3Layer::sIBTopVertexWidth2 = 0.072 * sCm; +const Double_t V3Layer::sIBTopVertexHeight = 0.04 * sCm; +const Double_t V3Layer::sIBTopVertexAngle = 60.0; // Deg +const Double_t V3Layer::sIBSideVertexWidth = 0.05 * sCm; +const Double_t V3Layer::sIBSideVertexHeight = 0.074 * sCm; +const Double_t V3Layer::sIBTopFilamentSide = 0.04 * sCm; +const Double_t V3Layer::sIBTopFilamentAlpha = 109.8; // Deg +const Double_t V3Layer::sIBTopFilamentInterZ = 15.0 * sMm; +const Double_t V3Layer::sIBEndSupportThick = 0.149 * sMm; +const Double_t V3Layer::sIBEndSupportZLen = 2.5 * sMm; +const Double_t V3Layer::sIBEndSupportXUp = 1.0 * sMm; +const Double_t V3Layer::sIBEndSupportOpenPhi = 120.0; // Deg + +const Double_t V3Layer::sIBConnectorXWidth = 10.0 * sMm; +const Double_t V3Layer::sIBConnectorYTot = 4.7 * sMm; +const Double_t V3Layer::sIBConnectBlockZLen = 16.5 * sMm; +const Double_t V3Layer::sIBConnBodyYHeight = 2.5 * sMm; +const Double_t V3Layer::sIBConnTailYShift = 0.9 * sMm; +const Double_t V3Layer::sIBConnTailYMid = 2.5 * sMm; +const Double_t V3Layer::sIBConnTailZLen = 2.5 * sMm; +const Double_t V3Layer::sIBConnTailOpenPhi = 120.0; // Deg +const Double_t V3Layer::sIBConnRoundHoleD = 2.0 * sMm; +const Double_t V3Layer::sIBConnRoundHoleZ = (9.0 - 4.0) * sMm; +const Double_t V3Layer::sIBConnSquareHoleX = 2.0 * sMm; +const Double_t V3Layer::sIBConnSquareHoleZ = 2.8 * sMm; +const Double_t V3Layer::sIBConnSquareHoleZPos = 9.0 * sMm; +const Double_t V3Layer::sIBConnInsertHoleD = 2.0 * sMm; +const Double_t V3Layer::sIBConnInsertHoleZPos = 9.0 * sMm; +const Double_t V3Layer::sIBConnTubeHole1D = 1.6 * sMm; +const Double_t V3Layer::sIBConnTubeHole1ZLen = 3.0 * sMm; +const Double_t V3Layer::sIBConnTubeHole1ZLen2 = 2.7 * sMm; +const Double_t V3Layer::sIBConnTubeHole2D = 1.2 * sMm; +const Double_t V3Layer::sIBConnTubeHole3XPos = 1.0 * sMm; +const Double_t V3Layer::sIBConnTubeHole3ZPos = 14.5 * sMm; +const Double_t V3Layer::sIBConnTubesXDist = 5.0 * sMm; +const Double_t V3Layer::sIBConnTubesYPos = 1.25 * sMm; +const Double_t V3Layer::sIBConnInsertD = 2.0 * sMm; +const Double_t V3Layer::sIBConnInsertHeight = 2.3 * sMm; +const Double_t V3Layer::sIBConnSideHole1D = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole1YPos = 1.25 * sMm; +const Double_t V3Layer::sIBConnSideHole1ZPos = 11.5 * sMm; +const Double_t V3Layer::sIBConnSideHole1XWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2YPos = 1.25 * sMm; +const Double_t V3Layer::sIBConnSideHole2ZPos = 11.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2XWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2YWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2ZWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnectAFitExtD = 1.65 * sMm; +const Double_t V3Layer::sIBConnectAFitIntD = 1.19 * sMm; +const Double_t V3Layer::sIBConnectAFitZLen = 12.5 * sMm; +const Double_t V3Layer::sIBConnectAFitZOut = 10.0 * sMm; +const Double_t V3Layer::sIBConnPlugInnerD = 0.8 * sMm; +const Double_t V3Layer::sIBConnPlugTotLen = 1.7 * sMm; +const Double_t V3Layer::sIBConnPlugInnerLen = 1.0 * sMm; + +const Double_t V3Layer::sIBStaveHeight = 0.5 * sCm; + +// Outer Barrel Parameters +const Int_t V3Layer::sOBChipsPerRow = 7; +const Int_t V3Layer::sOBNChipRows = 2; + +const Double_t V3Layer::sOBChipThickness = 100.0 * sMicron; + +const Double_t V3Layer::sOBHalfStaveWidth = 3.01 * sCm; +const Double_t V3Layer::sOBModuleGap = 200.0 * sMicron; +const Double_t V3Layer::sOBChipXGap = 150.0 * sMicron; +const Double_t V3Layer::sOBChipZGap = 150.0 * sMicron; +const Double_t V3Layer::sOBFlexCableXWidth = 3.3 * sCm; +const Double_t V3Layer::sOBFlexCableAlThick = 0.005 * sCm; +const Double_t V3Layer::sOBFlexCableKapThick = 75.0 * sMicron; +const Double_t V3Layer::sOBFPCSoldMaskThick = 30.0 * sMicron; +const Double_t V3Layer::sOBFPCCopperThick = 18.0 * sMicron; +const Double_t V3Layer::sOBFPCCuAreaFracGnd = 0.954; // F.Benotto +const Double_t V3Layer::sOBFPCCuAreaFracSig = 0.617; // F.Benotto +const Double_t V3Layer::sOBGlueFPCThick = 50 * sMicron; +const Double_t V3Layer::sOBGlueColdPlThick = 80 * sMicron; +const Double_t V3Layer::sOBPowerBusXWidth = 3.04 * sCm; +const Double_t V3Layer::sOBPowerBusAlThick = 100.0 * sMicron; +const Double_t V3Layer::sOBPowerBusAlFrac = 0.90; // L.Greiner +const Double_t V3Layer::sOBPowerBusDielThick = 50.0 * sMicron; +const Double_t V3Layer::sOBPowerBusKapThick = 27.5 * sMicron; +const Double_t V3Layer::sOBBiasBusXWidth = 7.7 * sMm; +const Double_t V3Layer::sOBBiasBusAlThick = 25.0 * sMicron; +const Double_t V3Layer::sOBBiasBusAlFrac = 0.90; // L.Greiner +const Double_t V3Layer::sOBBiasBusDielThick = 50.0 * sMicron; +const Double_t V3Layer::sOBBiasBusKapThick = 25.0 * sMicron; +const Double_t V3Layer::sOBColdPlateXWidth = 3.04 * sCm; +const Double_t V3Layer::sOBColdPlateZLenML = 87.55 * sCm; +const Double_t V3Layer::sOBColdPlateZLenOL = 150.15 * sCm; +const Double_t V3Layer::sOBColdPlateThick = 0.012 * sCm; +const Double_t V3Layer::sOBHalfStaveYPos = 2.067 * sCm; +const Double_t V3Layer::sOBHalfStaveYTrans = 1.76 * sMm; +const Double_t V3Layer::sOBHalfStaveXOverlap = 7.2 * sMm; +const Double_t V3Layer::sOBGraphiteFoilThick = 30.0 * sMicron; +const Double_t V3Layer::sOBCarbonFleeceThick = 20.0 * sMicron; +const Double_t V3Layer::sOBCoolTubeInnerD = 2.05 * sMm; +const Double_t V3Layer::sOBCoolTubeThick = 32.0 * sMicron; +const Double_t V3Layer::sOBCoolTubeXDist = 10.0 * sMm; + +const Double_t V3Layer::sOBCPConnectorXWidth = 16.0 * sMm; +const Double_t V3Layer::sOBCPConnBlockZLen = 15.0 * sMm; +const Double_t V3Layer::sOBCPConnBlockYHei = 3.6 * sMm; +const Double_t V3Layer::sOBCPConnHollowZLen = 3.0 * sMm; +const Double_t V3Layer::sOBCPConnHollowYHei = 0.9 * sMm; +const Double_t V3Layer::sOBCPConnSquareHoleX = 4.0 * sMm; +const Double_t V3Layer::sOBCPConnSquareHoleZ = 5.0 * sMm; +const Double_t V3Layer::sOBCPConnSqrHoleZPos = 4.0 * sMm; +const Double_t V3Layer::sOBCPConnSqrInsertRZ = 3.5 * sMm; +const Double_t V3Layer::sOBCPConnRoundHoleD = 4.0 * sMm; +const Double_t V3Layer::sOBCPConnRndHoleZPos = 7.0 * sMm; +const Double_t V3Layer::sOBCPConnTubesXDist = 10.0 * sMm; +const Double_t V3Layer::sOBCPConnTubesYPos = 1.8 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole1D = 2.6 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole1Z = 3.5 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole2D = 2.2 * sMm; +const Double_t V3Layer::sOBCPConnFitHoleD = 2.8 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole3XP = 1.0 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole3ZP = 2.0 * sMm; +const Double_t V3Layer::sOBCPConnInstZThick = 1.0 * sMm; +const Double_t V3Layer::sOBCPConnInsertYHei = 3.4 * sMm; +const Double_t V3Layer::sOBCPConnAFitExtD = 2.8 * sMm; +const Double_t V3Layer::sOBCPConnAFitThick = 0.3 * sMm; +const Double_t V3Layer::sOBCPConnAFitZLen = 17.0 * sMm; +const Double_t V3Layer::sOBCPConnAFitZIn = 3.0 * sMm; +const Double_t V3Layer::sOBCPConnPlugInnerD = 0.8 * sMm; +const Double_t V3Layer::sOBCPConnPlugTotLen = 1.7 * sMm; +const Double_t V3Layer::sOBCPConnPlugThick = 0.5 * sMm; + +const Double_t V3Layer::sOBSpaceFrameZLen[2] = {900.0 * sMm, 1526.0 * sMm}; +const Int_t V3Layer::sOBSpaceFrameNUnits[2] = {23, 39}; +const Double_t V3Layer::sOBSpaceFrameUnitLen = 39.1 * sMm; +const Double_t V3Layer::sOBSpaceFrameWidth = 42.44 * sMm; +const Double_t V3Layer::sOBSpaceFrameHeight = 36.45 * sMm; +const Double_t V3Layer::sOBSpaceFrameTopVL = 4.0 * sMm; +const Double_t V3Layer::sOBSpaceFrameTopVH = 0.35 * sMm; +const Double_t V3Layer::sOBSpaceFrameSideVL = 4.5 * sMm; +const Double_t V3Layer::sOBSpaceFrameSideVH = 0.35 * sMm; +const Double_t V3Layer::sOBSpaceFrameVAlpha = 60.0; // deg +const Double_t V3Layer::sOBSpaceFrameVBeta = 68.0; // deg +const Double_t V3Layer::sOBSFrameBaseRibDiam = 1.33 * sMm; +const Double_t V3Layer::sOBSFrameBaseRibPhi = 54.0; // deg +const Double_t V3Layer::sOBSFrameSideRibDiam = 1.25 * sMm; +const Double_t V3Layer::sOBSFrameSideRibPhi = 70.0; // deg +const Double_t V3Layer::sOBSFrameULegLen = 14.2 * sMm; +const Double_t V3Layer::sOBSFrameULegWidth = 1.5 * sMm; +const Double_t V3Layer::sOBSFrameULegHeight1 = 2.7 * sMm; +const Double_t V3Layer::sOBSFrameULegHeight2 = 5.0 * sMm; +const Double_t V3Layer::sOBSFrameULegThick = 0.3 * sMm; +const Double_t V3Layer::sOBSFrameULegXPos = 12.9 * sMm; +const Double_t V3Layer::sOBSFrameConnWidth = 42.0 * sMm; +const Double_t V3Layer::sOBSFrameConnTotLen = 29.0 * sMm; +const Double_t V3Layer::sOBSFrameConnTotHei = 4.8 * sMm; +const Double_t V3Layer::sOBSFrameConnTopLen = 14.0 * sMm; +const Double_t V3Layer::sOBSFrameConnInsWide = 36.869 * sMm; +const Double_t V3Layer::sOBSFrameConnInsBase = 39.6 * sMm; +const Double_t V3Layer::sOBSFrameConnInsHei = 2.8 * sMm; +const Double_t V3Layer::sOBSFrameConnHoleZPos = 7.0 * sMm; +const Double_t V3Layer::sOBSFrameConnHoleZDist = 15.0 * sMm; +const Double_t V3Layer::sOBSFrameConnTopHoleD = 3.0 * sMm; +const Double_t V3Layer::sOBSFrConnTopHoleXDist = 24.0 * sMm; +const Double_t V3Layer::sOBSFrameConnAHoleWid = 4.0 * sMm; +const Double_t V3Layer::sOBSFrameConnAHoleLen = 5.0 * sMm; +const Double_t V3Layer::sOBSFrConnASideHoleD = 3.0 * sMm; +const Double_t V3Layer::sOBSFrConnASideHoleL = 2.5 * sMm; +const Double_t V3Layer::sOBSFrConnASideHoleY = 2.3 * sMm; +const Double_t V3Layer::sOBSFrameConnCHoleZPos = 3.0 * sMm; +const Double_t V3Layer::sOBSFrConnCHoleXDist = 32.0 * sMm; +const Double_t V3Layer::sOBSFrConnCTopHoleD = 4.0 * sMm; +const Double_t V3Layer::sOBSFrameConnInsHoleD = 5.0 * sMm; +const Double_t V3Layer::sOBSFrameConnInsHoleX = 25.8 * sMm; + +ClassImp(V3Layer); + +#define SQ(A) (A) * (A) + +V3Layer::V3Layer() + : V11Geometry(), + mLayerNumber(0), + mNumberOfInnerLayers(0), + mPhi0(0), + mLayerRadius(0), + mSensorThickness(0), + mChipThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(0), + mIsTRK(0), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy), + mAddGammaConv(kFALSE), + mGammaConvDiam(0), + mGammaConvXPos(0), + mIBModuleZLength(0), + mOBModuleZLength(0) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V3Layer::V3Layer(Int_t lay, Bool_t turbo, Int_t debug) + : V11Geometry(debug), + mLayerNumber(lay), + mNumberOfInnerLayers(sNumberOfInnerLayers), + mPhi0(0), + mLayerRadius(0), + mSensorThickness(0), + mChipThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(turbo), + mIsTRK(0), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy), + mAddGammaConv(kFALSE), + mGammaConvDiam(0), + mGammaConvXPos(0), + mIBModuleZLength(0), + mOBModuleZLength(0) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V3Layer::~V3Layer() = default; + +void V3Layer::createLayer(TGeoVolume* motherVolume) +{ + const Int_t nameLen = 30; + char volumeName[nameLen]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper parameters + if (mLayerRadius <= 0) { + LOG(FATAL) << "Wrong layer radius " << mLayerRadius; + } + /* // These checks would fail with new TRK geo, let's trust the user here :) + if (mNumberOfStaves <= 0) { + LOG(FATAL) << "Wrong number of staves " << mNumberOfStaves; + } + + if (mNumberOfChips <= 0) { + LOG(FATAL) << "Wrong number of chips " << mNumberOfChips; + } + + if (mLayerNumber >= sNumberOfInnerLayers && mNumberOfModules <= 0) { + LOG(FATAL) << "Wrong number of modules " << mNumberOfModules; + } + + if (mChipThickness <= 0) { + LOG(FATAL) << "Chip thickness wrong or not set " << mChipThickness; + } + + if (mSensorThickness <= 0) { + LOG(FATAL) << "Sensor thickness wrong or not set " << mSensorThickness; + } + + if (mSensorThickness > mChipThickness) { + LOG(FATAL) << "Sensor thickness " << mSensorThickness << " is greater than chip thickness " << mChipThickness; + } +*/ + // If a Turbo layer is requested, do it and exit + if (mIsTurbo) { + createLayerTurbo(motherVolume); + return; + } + + // If a new TRK layer is requested, do it and exit + if (mIsTRK) { + createTRKLayer(motherVolume); + return; + } + + // First create the stave container + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // mStaveWidth = mLayerRadius*Tan(alpha); + + snprintf(volumeName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSLayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + + // layerVolume->SetVisibility(kFALSE); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius + (static_cast<TGeoBBox*>(stavVol->GetShape()))->GetDY(); + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode(stavVol, j, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + // geometry is served + return; +} + +void V3Layer::createLayerTurbo(TGeoVolume* motherVolume) +{ + const Int_t nameLen = 30; + char volumeName[nameLen]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper (remaining) parameters + if (mStaveWidth <= 0) { + LOG(FATAL) << "Wrong stave width " << mStaveWidth; + } + + if (Abs(mStaveTilt) > 45) { + LOG(WARNING) << "Stave tilt angle (" << mStaveTilt << ") greater than 45deg"; + } + + snprintf(volumeName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSLayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius /* +chip thick ?! */; + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode(stavVol, j, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi - mStaveTilt, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + return; +} + +void V3Layer::createTRKLayer(TGeoVolume* motherVolume, const TGeoManager* mgr) +{ + + Double_t rmin = mLayerRadius; + Double_t rmax = rmin + mSensorThickness; + + const Int_t nameLen = 30; + char chipName[nameLen], sensName[nameLen], moduleName[nameLen], + hsName[nameLen], staveName[nameLen], layerName[nameLen]; + + snprintf(sensName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSSensorPattern(), mLayerNumber); + snprintf(chipName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSChipPattern(), mLayerNumber); + snprintf(moduleName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSModulePattern(), mLayerNumber); + snprintf(hsName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + snprintf(staveName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSStavePattern(), mLayerNumber); + snprintf(layerName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSLayerPattern(), mLayerNumber); + + TGeoTube* sensor = new TGeoTube(rmin, rmax, mIBModuleZLength / 2); + TGeoTube* chip = new TGeoTube(rmin, rmax, mIBModuleZLength / 2); + TGeoTube* module = new TGeoTube(rmin, rmax, mIBModuleZLength / 2); + TGeoTube* hstave = new TGeoTube(rmin, rmax, mIBModuleZLength / 2); + TGeoTube* stave = new TGeoTube(rmin, rmax, mIBModuleZLength / 2); + TGeoTube* layer = new TGeoTube(rmin, rmax, mIBModuleZLength / 2); + + TGeoMedium* medSi = mgr->GetMedium("TRK_SI$"); + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medAir); + TGeoVolume* modVol = new TGeoVolume(moduleName, hstave, medAir); + TGeoVolume* hstaveVol = new TGeoVolume(hsName, hstave, medAir); + TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); + TGeoVolume* layerVol = new TGeoVolume(layerName, layer, medAir); + + LOG(DEBUG) << "Inserting " << sensVol->GetName() << " inside " << chipVol->GetName(); + chipVol->AddNode(sensVol, 1, nullptr); + + LOG(DEBUG) << "Inserting " << chipVol->GetName() << " inside " << modVol->GetName(); + modVol->AddNode(chipVol, 0, nullptr); + mHierarchy[kChip] = 1; + + LOG(DEBUG) << "Inserting " << modVol->GetName() << " inside " << hstaveVol->GetName(); + hstaveVol->AddNode(modVol, 0, nullptr); + mHierarchy[kModule] = 1; + + LOG(DEBUG) << "Inserting " << hstaveVol->GetName() << " inside " << staveVol->GetName(); + staveVol->AddNode(hstaveVol, 0, nullptr); + mHierarchy[kHalfStave] = 1; + + LOG(DEBUG) << "Inserting " << staveVol->GetName() << " inside " << layerVol->GetName(); + layerVol->AddNode(staveVol, 0, nullptr); + mHierarchy[kStave] = 1; + + // Finally put everything in the mother volume + LOG(DEBUG) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); + motherVolume->AddNode(layerVol, 1, nullptr); + + return; +} + +TGeoVolume* V3Layer::createStave(const TGeoManager* /*mgr*/) +{ + // + // Creates the actual Stave + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 22 Jun 2011 Mario Sitta + // Updated: 18 Dec 2013 Mario Sitta Handle IB and OB + // Updated: 12 Jan 2015 Mario Sitta Fix overlap with new OB space frame + // (by moving the latter, not the sensors to avoid + // spoiling their position in space) + // Updated: 03 Mar 2015 Mario Sitta Fix chip position + // Updated: 16 Mar 2017 Mario Sitta AliceO2 version + // Updated: 10 Jan 2018 Mario Sitta Compute all dimensions using + // AlpideChip as basis + // + + const Int_t nameLen = 30; + char volumeName[nameLen]; + + Double_t xpos, ypos; + Double_t alpha; + + // First create all needed shapes + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // The stave + snprintf(volumeName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSStavePattern(), mLayerNumber); + TGeoVolume* staveVol = new TGeoVolumeAssembly(volumeName); + staveVol->SetVisibility(kTRUE); + staveVol->SetLineColor(2); + + TGeoVolume* mechStaveVol = nullptr; + + // Now build up the stave + if (mLayerNumber < mNumberOfInnerLayers) { + TGeoVolume* modVol = createStaveInnerB(); + ypos = (static_cast<TGeoBBox*>(modVol->GetShape()))->GetDY() - mChipThickness; // = 0 if not kIBModel4 + staveVol->AddNode(modVol, 0, new TGeoTranslation(0, ypos, 0)); + mHierarchy[kHalfStave] = 1; + + // Mechanical stave structure + // mechStaveVol = createStaveStructInnerB(); + if (mechStaveVol) { + ypos = (static_cast<TGeoBBox*>(modVol->GetShape()))->GetDY() - ypos; + if (mStaveModel != Detector::kIBModel4) { + ypos += (static_cast<TGeoBBox*>(mechStaveVol->GetShape()))->GetDY(); + } + staveVol->AddNode(mechStaveVol, 1, new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", 0, 0, 180))); + } + } else { + TGeoVolume* hstaveVol = createStaveOuterB(); + if (mStaveModel == Detector::kOBModel0) { // Create simplified stave struct as in v0 + staveVol->AddNode(hstaveVol, 0); + mHierarchy[kHalfStave] = 1; + } else { // (if mStaveModel) Create new stave struct as in TDR + xpos = (static_cast<TGeoBBox*>(hstaveVol->GetShape()))->GetDX() - sOBHalfStaveXOverlap / 2; + // ypos is now a parameter to avoid HS displacement wrt nominal radii + ypos = sOBHalfStaveYPos; + staveVol->AddNode(hstaveVol, 0, new TGeoTranslation(-xpos, ypos, 0)); + staveVol->AddNode(hstaveVol, 1, new TGeoTranslation(xpos, ypos + sOBHalfStaveYTrans, 0)); + mHierarchy[kHalfStave] = 2; // RS + mechStaveVol = createSpaceFrameOuterB(); + + if (mechStaveVol) { + if (mBuildLevel < 6) { // Carbon + staveVol->AddNode(mechStaveVol, 1, + new TGeoCombiTrans(0, -sOBSFrameULegHeight1, 0, new TGeoRotation("", 180, 0, 0))); + } + } + } + } + + staveVol->GetShape()->ComputeBBox(); // RS: enfore recompting of BBox + + // Done, return the stave + return staveVol; +} + +TGeoVolume* V3Layer::createStaveInnerB(const TGeoManager* mgr) +{ + Double_t xmod, ymod, zmod; + const Int_t nameLen = 30; + char volumeName[nameLen]; + + // First we create the module (i.e. the HIC with 9 chips) + TGeoVolume* moduleVol = createModuleInnerB(); + + // Then we create the fake halfstave and the actual stave + xmod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDX(); + ymod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDY(); + zmod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDZ(); + + TGeoBBox* hstave = new TGeoBBox(xmod, ymod, zmod); + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + snprintf(volumeName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + TGeoVolume* hstaveVol = new TGeoVolume(volumeName, hstave, medAir); + + // Finally build it up + hstaveVol->AddNode(moduleVol, 0); + mHierarchy[kModule] = 1; + + // Done, return the stave structure + return hstaveVol; +} + +TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) +{ + Double_t xtot, ytot, ztot, xchip, zchip, ymod; + Double_t xpos, ypos, zpos; + Bool_t dummyChip; + const Int_t nameLen = 30; + char chipName[nameLen], sensName[nameLen], volumeName[nameLen]; + + // For material budget studies + if (mBuildLevel < 6) { + dummyChip = kFALSE; // will be made of Si + } else { + dummyChip = kTRUE; // will be made of Air + } + + // First create the single chip + snprintf(chipName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSChipPattern(), mLayerNumber); + snprintf(sensName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSSensorPattern(), mLayerNumber); + + ymod = 0.5 * mChipThickness; + + TGeoVolume* chipVol = AlpideChip::createChip(ymod, mSensorThickness / 2, chipName, sensName, dummyChip); + + xchip = (static_cast<TGeoBBox*>(chipVol->GetShape()))->GetDX(); + zchip = (static_cast<TGeoBBox*>(chipVol->GetShape()))->GetDZ(); + + mIBModuleZLength = 2 * zchip * sIBChipsPerRow + (sIBChipsPerRow - 1) * sIBChipZGap; + + // Then create the Glue, the Kapton and the two Aluminum cables + xtot = xchip + (sIBFPCWiderXPlus + sIBFPCWiderXNeg) / 2; + ztot = mIBModuleZLength / 2; + + TGeoBBox* glue = new TGeoBBox(xchip, sIBGlueThick / 2, ztot); + TGeoBBox* kapCable = new TGeoBBox(xtot, sIBFlexCableKapThick / 2, ztot); + + TGeoVolume* aluGndCableVol = createIBFPCAlGnd(xtot, ztot); + TGeoVolume* aluAnodeCableVol = createIBFPCAlAnode(xtot, ztot); + + // Finally create the module and populate it with the chips + // (and the FPC Kapton and Aluminum in the most recent IB model) + Double_t ygnd = (static_cast<TGeoBBox*>(aluGndCableVol->GetShape()))->GetDY(); + Double_t yano = (static_cast<TGeoBBox*>(aluAnodeCableVol->GetShape()))->GetDY(); + + ytot = ymod; + if (mStaveModel == Detector::kIBModel4) { + ytot += (sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitorYHi / 2); + } + + TGeoBBox* module = new TGeoBBox(xtot, ytot, ztot); + + // Now the volumes + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE_IBFPC$"); + + snprintf(volumeName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSModulePattern(), mLayerNumber); + TGeoVolume* modVol = new TGeoVolume(volumeName, module, medAir); + + TGeoVolume* glueVol = new TGeoVolume("FPCGlue", glue, medGlue); + glueVol->SetLineColor(kBlack); + glueVol->SetFillColor(kBlack); + + TGeoVolume* kapCableVol = new TGeoVolume("FPCKapton", kapCable, medKapton); + kapCableVol->SetLineColor(kBlue); + kapCableVol->SetFillColor(kBlue); + + // Build up the module + // Chips are rotated by 180deg around Y axis + // in order to have the correct X and Z axis orientation + xpos = -xtot + (static_cast<TGeoBBox*>(chipVol->GetShape()))->GetDX() + sIBFPCWiderXNeg; + ypos = -ytot + ymod; // = 0 if not kIBModel4 + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + zpos = ztot - j * (2 * zchip + sIBChipZGap) - zchip; + modVol->AddNode(chipVol, j, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 180, 180))); + mHierarchy[kChip]++; + } + + if (mStaveModel == Detector::kIBModel4) { + ypos += (ymod + glue->GetDY()); + if (mBuildLevel < 2) { // Glue + modVol->AddNode(glueVol, 1, new TGeoTranslation(xpos, ypos, 0)); + } + ypos += glue->GetDY(); + + if (mBuildLevel < 4) { // Kapton + ypos += ygnd; + modVol->AddNode(aluGndCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (ygnd + kapCable->GetDY()); + modVol->AddNode(kapCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (kapCable->GetDY() + yano); + modVol->AddNode(aluAnodeCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += yano; + } + } + + // Add the capacitors + createIBCapacitors(modVol, zchip, ypos); + + // Done, return the module + return modVol; +} + +void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yzero, const TGeoManager* mgr) +{ + // + // Adds the capacitors to the IB FPC + // + // Created: 13 Feb 2018 Mario Sitta + // Updated: 03 Apr 2019 Mario Sitta Fix positions (180' rotation) + // + + // Position of the various capacitors (A.Junique private communication + // where: X_capacitor = Z_module , Y_capacitor = X_module) + // Capacitors (different groups) + const Double_t xGroup1A = 4265.9 * sMicron; + const Double_t zGroup1A[2] = {-7142.9 * sMicron, 7594.1 * sMicron}; + const Double_t xGroup1B = 690.9 * sMicron; + const Double_t zGroup1B = -7142.9 * sMicron; + const Double_t xGroup2 = 6300.0 * sMicron; + const Double_t zGroup2 = 15075.0 * sMicron; + const Double_t xGroup3 = 5575.0 * sMicron; + const Double_t zGroup3 = 131900.0 * sMicron; + const Double_t xGroup4[2] = {5600.0 * sMicron, 5575.0 * sMicron}; + const Double_t zGroup4[sIBChipsPerRow] = {275.0 * sMicron, 250.0 * sMicron, 275.0 * sMicron, + 250.0 * sMicron, 250.0 * sMicron, 300.0 * sMicron, + 250.0 * sMicron, 300.0 * sMicron, 250.0 * sMicron}; + const Int_t nGroup5A = 5, nGroup5B = 4; + const Double_t xGroup5A[2] = {1400.0 * sMicron, 1350.0 * sMicron}; + const Double_t zGroup5A[nGroup5A] = {-112957.5 * sMicron, -82854.5 * sMicron, 7595.5 * sMicron, 37745.5 * sMicron, + 128194.1 * sMicron}; + const Double_t xGroup5B = 1100.0 * sMicron; + const Double_t zGroup5B[nGroup5B] = {-51525.0 * sMicron, -21375.0 * sMicron, 69075.0 * sMicron, 99225.0 * sMicron}; + // Resistors + const Int_t nResist = 2; + const Double_t xResist = -7975.0 * sMicron; + const Double_t zResist[nResist] = {114403.0 * sMicron, 119222.0 * sMicron}; + + Double_t xpos, ypos, zpos; + Int_t nCapacitors; + + TGeoVolume *capacitor, *resistor; + + // Check whether we already have the volume, otherwise create it + // (so as to avoid creating multiple copies of the very same volume + // for each layer) + capacitor = mgr->GetVolume("IBFPCCapacitor"); + + if (!capacitor) { + TGeoBBox* capsh = new TGeoBBox(sIBFlexCapacitorXWid / 2, sIBFlexCapacitorYHi / 2, sIBFlexCapacitorZLen / 2); + + TGeoMedium* medCeramic = mgr->GetMedium("TRK_CERAMIC$"); + + capacitor = new TGeoVolume("IBFPCCapacitor", capsh, medCeramic); + capacitor->SetLineColor(kBlack); + capacitor->SetFillColor(kBlack); + + TGeoBBox* ressh = new TGeoBBox(sIBFlexCapacitorXWid / 2, // Resistors have + sIBFlexCapacitorYHi / 2, // the same dim's + sIBFlexCapacitorZLen / 2); // as capacitors + + resistor = new TGeoVolume("IBFPCResistor", ressh, medCeramic); + resistor->SetLineColor(kBlack); + resistor->SetFillColor(kBlack); + } else { // Volumes already defined, get them + resistor = mgr->GetVolume("IBFPCResistor"); + } + + // Place all the capacitors (they are really a lot...) + ypos = yzero + sIBFlexCapacitorYHi / 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)); + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[1]; + modvol->AddNode(capacitor, 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)); + } + + nCapacitors += sIBChipsPerRow; + 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)); + } + + nCapacitors += (sIBChipsPerRow - 1); + xpos = xGroup3; + zpos = zGroup3; + modvol->AddNode(capacitor, 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + + nCapacitors++; + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + if (j == (sIBChipsPerRow - 1)) { + xpos = xGroup4[1]; + } else { + xpos = xGroup4[0]; + } + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup4[j]; + modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + nCapacitors += sIBChipsPerRow; + for (Int_t j = 0; j < nGroup5A; j++) { + if (j == 0) { + xpos = xGroup5A[0]; + } else { + xpos = xGroup5A[1]; + } + zpos = zGroup5A[j]; + modvol->AddNode(capacitor, 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)); + } + + // Place the resistors + xpos = xResist; + for (Int_t j = 0; j < nResist; j++) { + zpos = zResist[j]; + modvol->AddNode(resistor, j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); + } +} + +TGeoVolume* V3Layer::createIBFPCAlGnd(const Double_t xcable, const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the IB FPC Aluminum Ground cable + // + // Created: 20 Oct 2017 Mario Sitta + // + + Double_t ytot, ypos; + + // First create all needed shapes + ytot = sIBFlexCablePolyThick + sIBFlexCableAlThick; + TGeoBBox* coverlay = new TGeoBBox(xcable, ytot / 2, zcable); + TGeoBBox* aluminum = new TGeoBBox(xcable, sIBFlexCableAlThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medAluminum = mgr->GetMedium("TRK_ALUMINUM$"); + + TGeoVolume* coverlayVol = new TGeoVolume("FPCCoverlayGround", coverlay, medKapton); + coverlayVol->SetLineColor(kBlue); + coverlayVol->SetFillColor(kBlue); + + TGeoVolume* aluminumVol = new TGeoVolume("FPCAluminumGround", aluminum, medAluminum); + aluminumVol->SetLineColor(kCyan); + aluminumVol->SetFillColor(kCyan); + + ypos = coverlay->GetDY() - aluminum->GetDY(); + if (mBuildLevel < 1) { // Aluminum + coverlayVol->AddNode(aluminumVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + return coverlayVol; +} + +TGeoVolume* V3Layer::createIBFPCAlAnode(const Double_t xcable, const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the IB FPC Aluminum Anode cable + // + // + // Created: 20 Oct 2017 Mario Sitta + // Updated: 03 Apr 2019 Mario Sitta Fix Al position (180' rotation) + // + + Double_t ytot, ypos; + Double_t xtru[4], ytru[4]; + + // First create all needed shapes + ytot = sIBFlexCablePolyThick + sIBFlexCableAlThick; + TGeoBBox* coverlay = new TGeoBBox(xcable, ytot / 2, zcable); + + // A trapezoid + xtru[0] = -sIBFPCAlAnodeWidth2 / 2; + ytru[0] = -zcable; + xtru[1] = sIBFPCAlAnodeWidth2 / 2; + ytru[1] = ytru[0]; + xtru[2] = xtru[0] + sIBFPCAlAnodeWidth1; + ytru[2] = zcable; + xtru[3] = xtru[0]; + ytru[3] = ytru[2]; + + TGeoXtru* aluminum = new TGeoXtru(2); + aluminum->DefinePolygon(4, xtru, ytru); + aluminum->DefineSection(0, -sIBFlexCableAlThick / 2); + aluminum->DefineSection(1, sIBFlexCableAlThick / 2); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medAluminum = mgr->GetMedium("TRK_ALUMINUM$"); + + TGeoVolume* coverlayVol = new TGeoVolume("FPCCoverlayAnode", coverlay, medKapton); + coverlayVol->SetLineColor(kBlue); + coverlayVol->SetFillColor(kBlue); + + TGeoVolume* aluminumVol = new TGeoVolume("FPCAluminumAnode", aluminum, medAluminum); + aluminumVol->SetLineColor(kCyan); + aluminumVol->SetFillColor(kCyan); + + ypos = -coverlay->GetDY() + aluminum->GetZ(1); + if (mBuildLevel < 1) { // Aluminum + coverlayVol->AddNode(aluminumVol, 1, new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", 0, -90, 0))); + } + + return coverlayVol; +} + +TGeoVolume* V3Layer::createStaveStructInnerB(const TGeoManager* mgr) +{ + // + // Create the mechanical stave structure + // + // Created: 22 Mar 2013 Chinorat Kobdaj + // Updated: 26 Apr 2013 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version - All models obsolete except last one + // Updated: 25 Jan 2018 Mario Sitta Stave width is now a constant + // + + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kIBModelDummy: + mechStavVol = createStaveModelInnerBDummy(mgr); + break; + case Detector::kIBModel0: + case Detector::kIBModel1: + case Detector::kIBModel21: + case Detector::kIBModel22: + case Detector::kIBModel3: + LOG(FATAL) << "Stave model " << mStaveModel << " obsolete and no longer supported"; + break; + case Detector::kIBModel4: + mechStavVol = createStaveModelInnerB4(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V3Layer::createStaveModelInnerBDummy(const TGeoManager*) const +{ + // + // Create dummy stave + // + // Created: 22 Mar 2013 Chinorat Kobdaj + // Updated: 26 Apr 2013 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version + // + + // Done, return the stave structur + return nullptr; +} + +// model4 +//________________________________________________________________________ +TGeoVolume* V3Layer::createStaveModelInnerB4(const TGeoManager* mgr) +{ + // + // Create the mechanical stave structure for Model 4 of TDR + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 04 Dec 2014 Mario Sitta + // Updated: 03 Mar 2015 Mario Sitta FPC in right position (beyond chip) + // Updated: 06 Mar 2015 Mario Sitta Space Frame corrected (C.G. data) + // Updated: 30 Apr 2015 Mario Sitta End-stave connectors added + // Updated: 04 Apr 2017 Mario Sitta O2 version + // Updated: 25 Jan 2018 Mario Sitta Stave width is now a constant + // Updated: 03 Feb 2018 Mario Sitta To last drawings (ALIITSUP0051) + // + + // Local parameters + const Double_t xstave = sIBColdPlateWidth / 2; + + Double_t layerHeight = 0.; + + Double_t rPipeMin = sIBCoolPipeInnerD / 2; + Double_t rPipeMax = rPipeMin + sIBCoolPipeThick; + + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; // The stave container Xtru + Double_t xlen, ylen, zlen, ztot; + Double_t xpos, ypos, zpos, ylay, yposPipe; + Double_t beta, gamma, theta; + + // First create all needed shapes + ztot = sIBColdPlateZLen / 2; + + TGeoBBox* glue = new TGeoBBox(xstave, sIBGlueThick / 2, ztot); + + TGeoBBox* fleecbot = new TGeoBBox(xstave, sIBCarbonFleeceThick / 2, ztot); + + TGeoBBox* cfplate = new TGeoBBox(xstave, sIBK13D2UThick / 2, ztot); + + TGeoTube* pipe = new TGeoTube(rPipeMin, rPipeMax, sIBCoolPipeZLen / 2); + + TGeoTube* water = new TGeoTube(0., rPipeMin, sIBCoolPipeZLen / 2); + + TGeoTubeSeg* cpaptub = new TGeoTubeSeg(rPipeMax, rPipeMax + sIBCarbonPaperThick, sIBCarbonPaperZLen / 2, 0, 180); + + TGeoBBox* cpapvert = new TGeoBBox(sIBCarbonPaperThick / 2, pipe->GetRmax() / 2, sIBCarbonPaperZLen / 2); + + xlen = sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick; + TGeoBBox* cpapmid = new TGeoBBox(xlen, sIBCarbonPaperThick / 2, sIBCarbonPaperZLen / 2); + + xlen = sIBCarbonPaperWidth / 2 - sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick; + TGeoBBox* cpaplr = new TGeoBBox(xlen / 2, sIBCarbonPaperThick / 2, sIBCarbonPaperZLen / 2); + + TGeoTubeSeg* fleecpipe = new TGeoTubeSeg(cpaptub->GetRmax(), cpaptub->GetRmax() + sIBCarbonFleeceThick, ztot, 0, 180); + + TGeoBBox* fleecvert = new TGeoBBox(sIBCarbonFleeceThick / 2, (pipe->GetRmax() - sIBCarbonPaperThick) / 2, ztot); + + xlen = sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick - sIBCarbonFleeceThick; + TGeoBBox* fleecmid = new TGeoBBox(xlen, sIBCarbonFleeceThick / 2, ztot); + + xlen = xstave - sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick - sIBCarbonFleeceThick; + TGeoBBox* fleeclr = new TGeoBBox(xlen / 2, sIBCarbonFleeceThick / 2, ztot); + + // The total height of the layer can now be computed + layerHeight = 2 * (glue->GetDY() + fleecbot->GetDY() + cfplate->GetDY() + cpaplr->GetDY() + fleeclr->GetDY()); + + // The spaceframe structure + TGeoTrd1* topv = new TGeoTrd1(sIBTopVertexWidth1 / 2, sIBTopVertexWidth2 / 2, ztot, sIBTopVertexHeight / 2); + + xv[0] = 0; + yv[0] = 0; + xv[1] = sIBSideVertexWidth; + yv[1] = yv[0]; + xv[2] = xv[0]; + yv[2] = sIBSideVertexHeight; + + TGeoXtru* sidev = new TGeoXtru(2); + sidev->DefinePolygon(3, xv, yv); + sidev->DefineSection(0, -ztot); + sidev->DefineSection(1, ztot); + + xv[0] = sIBEndSupportXUp / 2; + yv[0] = sIBStaveHeight - sIBEndSupportThick; + xv[1] = xstave - sIBSideVertexWidth; + yv[1] = layerHeight + sIBSideVertexHeight; + xv[2] = xstave; + yv[2] = layerHeight; + xv[3] = xv[2]; + yv[3] = 0; + xv[4] = xstave + sIBEndSupportThick; + yv[4] = yv[3]; + xv[5] = xv[4]; + yv[5] = yv[2]; + xv[6] = xv[1] + sIBEndSupportThick * sinD(sIBEndSupportOpenPhi / 2); + yv[6] = yv[1] + sIBEndSupportThick * cosD(sIBEndSupportOpenPhi / 2); + xv[7] = xv[0]; + yv[7] = sIBStaveHeight; + for (Int_t i = 0; i < nv / 2; i++) { + xv[8 + i] = -xv[7 - i]; + yv[8 + i] = yv[7 - i]; + } + + TGeoXtru* endsupp = new TGeoXtru(2); + endsupp->DefinePolygon(16, xv, yv); + endsupp->DefineSection(0, -sIBEndSupportZLen / 2); + endsupp->DefineSection(1, sIBEndSupportZLen / 2); + + xlen = TMath::Sqrt((yv[7] - yv[6]) * (yv[7] - yv[6]) + (xv[7] - xv[6]) * (xv[7] - xv[6]) + + sIBTopFilamentInterZ * sIBTopFilamentInterZ / 4); + theta = TMath::ATan((yv[7] - yv[6]) / (xv[7] - xv[6])) * TMath::RadToDeg(); + TGeoBBox* topfil = new TGeoBBox(xlen / 2, sIBTopFilamentSide / 2, sIBTopFilamentSide / 2); + + // The half stave container (an XTru to avoid overlaps between neighbours) + xv[0] = xstave + sIBTopFilamentSide; + yv[0] = 0; + xv[1] = xv[0]; + yv[1] = layerHeight + sIBSideVertexHeight + topfil->GetDZ(); + ; + xv[2] = sIBEndSupportXUp / 2; + yv[2] = sIBStaveHeight + sIBTopFilamentSide / sinD(-theta); // theta is neg + for (Int_t i = 0; i < 3; i++) { + xv[3 + i] = -xv[2 - i]; + yv[3 + i] = yv[2 - i]; + } + + TGeoXtru* mechStruct = new TGeoXtru(2); + mechStruct->DefinePolygon(6, xv, yv); + mechStruct->SetName("mechStruct"); + mechStruct->DefineSection(0, -ztot); + mechStruct->DefineSection(1, ztot); + + // The connectors' containers + zlen = sIBConnectBlockZLen - sIBConnTailZLen + sIBConnectAFitZOut; + TGeoBBox* connAside = new TGeoBBox("connAsideIB", sIBConnectorXWidth / 2, sIBConnBodyYHeight / 2, zlen / 2); + + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + TGeoBBox* connCside = new TGeoBBox("connCsideIB", sIBConnectorXWidth / 2, sIBConnBodyYHeight / 2, zlen / 2); + + // The StaveStruct container, a Composite Shape + yposPipe = 2 * glue->GetDY() + 2 * fleecbot->GetDY() + 2 * cfplate->GetDY() + pipe->GetRmax(); + ypos = connAside->GetDY() - sIBConnTubesYPos + yposPipe; + zpos = ztot + connAside->GetDZ(); + TGeoTranslation* transAside = new TGeoTranslation("transAsideIB", 0, ypos, zpos); + transAside->RegisterYourself(); + + ypos = connCside->GetDY() - sIBConnTubesYPos + yposPipe; + zpos = ztot + connCside->GetDZ(); + TGeoTranslation* transCside = new TGeoTranslation("transCsideIB", 0, ypos, -zpos); + transCside->RegisterYourself(); + + TGeoCompositeShape* mechStavSh = + new TGeoCompositeShape("mechStruct+connAsideIB:transAsideIB+connCsideIB:transCsideIB"); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + TGeoMedium* medM55J6K = mgr->GetMedium("TRK_M55J6K$"); + TGeoMedium* medM60J3K = mgr->GetMedium("TRK_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medK13D2U2k = mgr->GetMedium("TRK_K13D2U2k$"); + TGeoMedium* medFGS003 = mgr->GetMedium("TRK_FGS003$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + + const Int_t nameLen = 30; + char volname[nameLen]; + snprintf(volname, nameLen, "%s%d_StaveStruct", o2::trk::GeometryTGeo::getITSStavePattern(), mLayerNumber); + TGeoVolume* mechStavVol = new TGeoVolume(volname, mechStavSh, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kFALSE); + + TGeoVolume* glueVol = new TGeoVolume("Glue", glue, medGlue); + glueVol->SetLineColor(kBlack); + glueVol->SetFillColor(kBlack); + + TGeoVolume* fleecbotVol = new TGeoVolume("CarbonFleeceBottom", fleecbot, medCarbonFleece); + fleecbotVol->SetFillColor(kViolet); + fleecbotVol->SetLineColor(kViolet); + + TGeoVolume* cfplateVol = new TGeoVolume("CFPlate", cfplate, medK13D2U2k); + cfplateVol->SetFillColor(5); // Yellow + cfplateVol->SetLineColor(5); + + TGeoVolume* pipeVol = new TGeoVolume("PolyimidePipe", pipe, medKapton); + pipeVol->SetFillColor(35); // Blue shade + pipeVol->SetLineColor(35); + + TGeoVolume* waterVol = new TGeoVolume("Water", water, medWater); + waterVol->SetFillColor(4); // Bright blue + waterVol->SetLineColor(4); + + TGeoVolume* cpaptubVol = new TGeoVolume("ThermasolPipeCover", cpaptub, medFGS003); + cpaptubVol->SetFillColor(2); // Red + cpaptubVol->SetLineColor(2); + + TGeoVolume* cpapvertVol = new TGeoVolume("ThermasolVertical", cpapvert, medFGS003); + cpapvertVol->SetFillColor(2); // Red + cpapvertVol->SetLineColor(2); + + TGeoVolume* cpapmidVol = new TGeoVolume("ThermasolMiddle", cpapmid, medFGS003); + cpapmidVol->SetFillColor(2); // Red + cpapmidVol->SetLineColor(2); + + TGeoVolume* cpaplrVol = new TGeoVolume("ThermasolLeftRight", cpaplr, medFGS003); + cpaplrVol->SetFillColor(2); // Red + cpaplrVol->SetLineColor(2); + + TGeoVolume* fleecpipeVol = new TGeoVolume("CarbonFleecePipeCover", fleecpipe, medCarbonFleece); + fleecpipeVol->SetFillColor(28); // Brown shade + fleecpipeVol->SetLineColor(28); + + TGeoVolume* fleecvertVol = new TGeoVolume("CarbonFleeceVertical", fleecvert, medCarbonFleece); + fleecvertVol->SetFillColor(28); // Brown shade + fleecvertVol->SetLineColor(28); + + TGeoVolume* fleecmidVol = new TGeoVolume("CarbonFleeceMiddle", fleecmid, medCarbonFleece); + fleecmidVol->SetFillColor(28); // Brown shade + fleecmidVol->SetLineColor(28); + + TGeoVolume* fleeclrVol = new TGeoVolume("CarbonFleeceLeftRight", fleeclr, medCarbonFleece); + fleeclrVol->SetFillColor(28); // Brown shade + fleeclrVol->SetLineColor(28); + + TGeoVolume* topvVol = new TGeoVolume("TopVertex", topv, medM55J6K); + topvVol->SetFillColor(12); // Gray shade + topvVol->SetLineColor(12); + + TGeoVolume* sidevVol = new TGeoVolume("SideVertex", sidev, medM55J6K); + sidevVol->SetFillColor(12); // Gray shade + sidevVol->SetLineColor(12); + + TGeoVolume* topfilVol = new TGeoVolume("TopFilament", topfil, medM60J3K); + topfilVol->SetFillColor(12); // Gray shade + topfilVol->SetLineColor(12); + + TGeoVolume* endsuppVol = new TGeoVolume("EndSupport", endsupp, medM55J6K); + endsuppVol->SetFillColor(12); // Gray shade + endsuppVol->SetLineColor(12); + + // Now build up the half stave + ypos = glue->GetDY(); + if (mBuildLevel < 2) { // Glue + mechStavVol->AddNode(glueVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (glue->GetDY() + fleecbot->GetDY()); + if (mBuildLevel < 5) { // Carbon + mechStavVol->AddNode(fleecbotVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (fleecbot->GetDY() + cfplate->GetDY()); + if (mBuildLevel < 5) { // Carbon + mechStavVol->AddNode(cfplateVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ylay = ypos + cfplate->GetDY(); // The level where tubes etc. lay + + xpos = sIBCoolPipeXDist / 2; + ypos = ylay + pipe->GetRmax(); + yposPipe = ypos; // Save for later use + if (mBuildLevel < 4) { // Kapton + mechStavVol->AddNode(pipeVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(pipeVol, 2, new TGeoTranslation(xpos, ypos, 0)); + } + + if (mBuildLevel < 3) { // Water + mechStavVol->AddNode(waterVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(waterVol, 2, new TGeoTranslation(xpos, ypos, 0)); + } + + if (mBuildLevel < 5) { // Carbon (stave components) + mechStavVol->AddNode(cpaptubVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpaptubVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + mechStavVol->AddNode(fleecpipeVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleecpipeVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 - pipe->GetRmax() - cpapvert->GetDX(); + ypos = ylay + cpapvert->GetDY(); + mechStavVol->AddNode(cpapvertVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpapvertVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 + pipe->GetRmax() + cpapvert->GetDX(); + mechStavVol->AddNode(cpapvertVol, 3, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpapvertVol, 4, new TGeoTranslation(xpos, ypos, 0)); + + ypos = ylay + sIBCarbonPaperThick / 2; + mechStavVol->AddNode(cpapmidVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = xstave - cpaplr->GetDX(); + mechStavVol->AddNode(cpaplrVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpaplrVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 - pipe->GetRmax() - 2 * cpapvert->GetDX() - fleecvert->GetDX(); + ypos = ylay + sIBCarbonPaperThick + fleecvert->GetDY(); + mechStavVol->AddNode(fleecvertVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleecvertVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 + pipe->GetRmax() + 2 * cpapvert->GetDX() + fleecvert->GetDX(); + mechStavVol->AddNode(fleecvertVol, 3, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleecvertVol, 4, new TGeoTranslation(xpos, ypos, 0)); + + ypos = ylay + sIBCarbonPaperThick + sIBCarbonFleeceThick / 2; + mechStavVol->AddNode(fleecmidVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = xstave - fleeclr->GetDX(); + mechStavVol->AddNode(fleeclrVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleeclrVol, 2, new TGeoTranslation(xpos, ypos, 0)); + } + + ylay += (sIBCarbonPaperThick + sIBCarbonFleeceThick); + + if (mBuildLevel < 5) { // Carbon (spaceframe) + ypos = sIBStaveHeight - sIBEndSupportThick - topv->GetDz(); // Due to rotation, z is on Y + mechStavVol->AddNode(topvVol, 1, new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", 0, -90, 0))); + + xpos = xstave - sidev->GetX(1); + ypos = ylay; + mechStavVol->AddNode(sidevVol, 1, new TGeoTranslation(xpos, ypos, 0)); + mechStavVol->AddNode(sidevVol, 2, new TGeoCombiTrans(-xpos, ypos, 0, new TGeoRotation("", 90, 180, -90))); + + zpos = ztot - endsupp->GetZ(1); + mechStavVol->AddNode(endsuppVol, 1, new TGeoTranslation(0, 0, zpos)); + mechStavVol->AddNode(endsuppVol, 2, new TGeoTranslation(0, 0, -zpos)); + + gamma = 180. - sIBTopFilamentAlpha; + xpos = xstave / 2 + topfil->GetDZ(); + ypos = (endsupp->GetY(7) + endsupp->GetY(6)) / 2; + Int_t nFilamentGroups = (Int_t)(2 * (ztot - sIBEndSupportZLen) / sIBTopFilamentInterZ); + // theta was computed when filament volume was created + for (int i = 0; i < nFilamentGroups; i++) { // i<19 + // 1) Front Left Top Filament + zpos = -(ztot - sIBEndSupportZLen) + i * sIBTopFilamentInterZ + sIBTopFilamentInterZ / 4; + mechStavVol->AddNode(topfilVol, i * 4 + 1, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90 + theta, gamma / 2, -90))); + // 2) Front Right Top Filament + mechStavVol->AddNode(topfilVol, i * 4 + 2, + new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90 - theta, -gamma / 2, -90))); + // 3) Back Left Top Filament + zpos += sIBTopFilamentInterZ / 2; + mechStavVol->AddNode(topfilVol, i * 4 + 3, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90 + theta, -gamma / 2, -90))); + // 4) Back Right Top Filament + mechStavVol->AddNode(topfilVol, i * 4 + 4, + new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90 - theta, gamma / 2, -90))); + } + } + + // Add the end-stave connectors + TGeoVolume *connectorASide, *connectorCSide; + + // Check whether we have already all pieces + // Otherwise create them + connectorASide = mgr->GetVolume("IBConnectorASide"); + + if (!connectorASide) { + createIBConnectors(mgr); + connectorASide = mgr->GetVolume("IBConnectorASide"); + } + connectorCSide = mgr->GetVolume("IBConnectorCSide"); + + ypos = (static_cast<TGeoBBox*>(connectorASide->GetShape()))->GetDY() - sIBConnTubesYPos + + yposPipe; // We center the pipe and hole axes + zpos = ztot + (sIBConnectBlockZLen - sIBConnTailZLen + sIBConnectAFitZOut) / 2; + mechStavVol->AddNode(connectorASide, 1, new TGeoTranslation(0, ypos, zpos)); + + zpos = ztot + (sIBConnectBlockZLen - sIBConnTailZLen) / 2; + mechStavVol->AddNode(connectorCSide, 1, new TGeoCombiTrans(0, ypos, -zpos, new TGeoRotation("", 90, 180, -90))); + + // Done, return the stave structure + return mechStavVol; +} + +void V3Layer::createIBConnectors(const TGeoManager* mgr) +{ + // + // Create the end-stave connectors for IB staves + // (simply call the actual creator methods) + // + // Created: 20 Apr 2015 Mario Sitta + // + + createIBConnectorsASide(mgr); + createIBConnectorsCSide(mgr); +} + +void V3Layer::createIBConnectorsASide(const TGeoManager* mgr) +{ + // + // Create the A-Side end-stave connectors for IB staves + // + // Created: 22 Apr 2015 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version + // Updated: 28 Jan 2018 Mario Sitta To last drawings (ALIITSUP0051) + // Updated: 19 Jun 2019 Mario Sitta Avoid fake overlaps with EndWheels + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + TGeoMedium* medInox304 = mgr->GetMedium("TRK_INOX304$"); + + // First create all elements + // (All measures refer to the blueprint ALIITSUP0051) + + // The connector block, two Composite Shapes: + // the body... + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + TGeoBBox* connBody = new TGeoBBox("connBodyA", xlen / 2, ylen / 2, zlen / 2); + + TGeoTube* connRoundHole = new TGeoTube("connRoundHoleA", 0., sIBConnRoundHoleD / 2, sIBConnBodyYHeight / 1.5); + + zpos = -connBody->GetDZ() + sIBConnRoundHoleZ; + TGeoCombiTrans* connRoundHoleTrans = + new TGeoCombiTrans("roundHoleTransA", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + connRoundHoleTrans->RegisterYourself(); + + xlen = sIBConnSquareHoleX / 2; + ylen = sIBConnBodyYHeight / 1.5; + zlen = sIBConnSquareHoleZ / 2; + TGeoBBox* connSquareHole = new TGeoBBox("connSquareHoleA", xlen, ylen, zlen); + + zpos = -connBody->GetDZ() + sIBConnSquareHoleZPos; + TGeoTranslation* connSquareHoleTrans = new TGeoTranslation("squareHoleTransA", 0, 0, zpos); + connSquareHoleTrans->RegisterYourself(); + + TGeoTube* connTubeHole2 = new TGeoTube("tube2HoleA", 0, sIBConnTubeHole2D / 2, connBody->GetDZ()); + + xpos = sIBConnTubesXDist / 2; + ypos = -connBody->GetDY() + sIBConnTubesYPos; + + TGeoTranslation* connTubes2Trans1 = new TGeoTranslation("tubes2Trans1A", -xpos, ypos, 0); + connTubes2Trans1->RegisterYourself(); + + TGeoTranslation* connTubes2Trans2 = new TGeoTranslation("tubes2Trans2A", xpos, ypos, 0); + connTubes2Trans2->RegisterYourself(); + + zlen = sIBConnTubeHole1ZLen - sIBConnTailZLen; + TGeoTube* connTubeHole3 = new TGeoTube("tube3HoleA", 0, sIBConnTubeHole1D / 2, zlen); + + zpos = connBody->GetDZ(); + TGeoTranslation* connTubes3Trans1 = new TGeoTranslation("tubes3Trans1A", -xpos, ypos, -zpos); + connTubes3Trans1->RegisterYourself(); + TGeoTranslation* connTubes3Trans2 = new TGeoTranslation("tubes3Trans2A", xpos, ypos, -zpos); + connTubes3Trans2->RegisterYourself(); + + zlen = sIBConnTubeHole1ZLen2; + TGeoTube* connFitHole = new TGeoTube("fitHoleA", 0, sIBConnectAFitExtD / 2, zlen); + + TGeoTranslation* connFitHoleTrans1 = new TGeoTranslation("fitTrans1A", -xpos, ypos, zpos); + connFitHoleTrans1->RegisterYourself(); + TGeoTranslation* connFitHoleTrans2 = new TGeoTranslation("fitTrans2A", xpos, ypos, zpos); + connFitHoleTrans2->RegisterYourself(); + + zlen = sIBConnSideHole1XWid / 1.5; + TGeoTube* sideHole1 = new TGeoTube("sideHole1A", 0, sIBConnSideHole1D / 2, zlen); + + xpos = connBody->GetDX() - sIBConnSideHole1XWid + sideHole1->GetDz(); + ypos = connBody->GetDY() - sIBConnSideHole1YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole1ZPos - sIBConnTailZLen); + TGeoCombiTrans* connSideHole1Trans = + new TGeoCombiTrans("sideHole1TransA", xpos, ypos, zpos, new TGeoRotation("", 90, 90, 0)); + connSideHole1Trans->RegisterYourself(); + + TGeoBBox* sideHole2Box = + new TGeoBBox("sideHole2AB", sIBConnSideHole2XWid, sIBConnSideHole2YWid / 2, sIBConnSideHole2ZWid / 2); + + xpos = -connBody->GetDX(); + ypos = connBody->GetDY() - sIBConnSideHole2YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + sideHole2Box->GetDZ(); + TGeoTranslation* sideHole2BTrans = new TGeoTranslation("sideHole2TransBA", xpos, ypos, zpos); + sideHole2BTrans->RegisterYourself(); + + TGeoTubeSeg* sideHole2TubeSeg = + new TGeoTubeSeg("sideHole2ATS", 0, sIBConnSideHole2YWid / 2, sIBConnSideHole2XWid, 0, 180); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen); + TGeoCombiTrans* sideHole2TSTrans1 = + new TGeoCombiTrans("sideHole2TSTrans1A", xpos, ypos, zpos, new TGeoRotation("", 90, -90, 0)); + sideHole2TSTrans1->RegisterYourself(); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + 2 * sideHole2Box->GetDZ(); + TGeoCombiTrans* sideHole2TSTrans2 = + new TGeoCombiTrans("sideHole2TSTrans2A", xpos, ypos, zpos, new TGeoRotation("", 90, 90, 0)); + sideHole2TSTrans2->RegisterYourself(); + + TGeoCompositeShape* connBodySh = new TGeoCompositeShape( + "connBodyA-connRoundHoleA:roundHoleTransA-connSquareHoleA:squareHoleTransA-tube2HoleA:tubes2Trans1A-tube2HoleA:" + "tubes2Trans2A-fitHoleA:fitTrans1A-fitHoleA:fitTrans2A-tube3HoleA:tubes3Trans1A-tube3HoleA:tubes3Trans2A-" + "sideHole1A:sideHole1TransA-sideHole2AB:sideHole2TransBA-sideHole2ATS:sideHole2TSTrans1A-sideHole2ATS:" + "sideHole2TSTrans2A"); + + TGeoVolume* connBlockBody = new TGeoVolume("IBConnectorBlockBodyASide", connBodySh, medPEEK); + connBlockBody->SetFillColor(42); // Brownish shade + connBlockBody->SetLineColor(42); + + // ...and the tail + xv[0] = sIBConnectorXWidth / 2; + yv[0] = sIBConnTailYShift; + xv[1] = xv[0]; + yv[1] = sIBConnTailYMid; + xv[2] = xv[1] - (sIBConnectorYTot - sIBConnTailYMid) / tanD(90 - sIBConnTailOpenPhi / 2); + yv[2] = sIBConnectorYTot; + + for (Int_t i = 0; i < 3; i++) { + xv[3 + i] = -xv[2 - i]; + yv[3 + i] = yv[2 - i]; + } + + TGeoXtru* connTail = new TGeoXtru(2); + connTail->SetName("connTailA"); + connTail->DefinePolygon(6, xv, yv); + connTail->DefineSection(0, 0); + connTail->DefineSection(1, sIBConnTailZLen); + + TGeoTube* connTubeHole1 = new TGeoTube("tube1HoleA", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 1.5); + + xpos = sIBConnTubesXDist / 2; + ypos = sIBConnTubesYPos; + zpos = connTail->GetZ(1) / 2; + TGeoTranslation* connTubes1Trans1 = new TGeoTranslation("tubes1Trans1A", -xpos, ypos, zpos); + connTubes1Trans1->RegisterYourself(); + TGeoTranslation* connTubes1Trans2 = new TGeoTranslation("tubes1Trans2A", xpos, ypos, zpos); + connTubes1Trans2->RegisterYourself(); + + TGeoCompositeShape* connTailSh = + new TGeoCompositeShape("connTailA-tube1HoleA:tubes1Trans1A-tube1HoleA:tubes1Trans2A"); + + TGeoVolume* connBlockTail = new TGeoVolume("IBConnectorBlockTailASide", connTailSh, medPEEK); + connBlockTail->SetFillColor(42); // Brownish shade + connBlockTail->SetLineColor(42); + + // The fitting tubes, a Tube + TGeoTube* connFitSh = new TGeoTube(sIBConnectAFitIntD / 2, sIBConnectAFitExtD / 2, sIBConnectAFitZLen / 2); + + TGeoVolume* connFit = new TGeoVolume("IBConnectorFitting", connFitSh, medInox304); + connFit->SetFillColor(kGray); + connFit->SetLineColor(kGray); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen + sIBConnectAFitZOut; + + TGeoBBox* connBox = new TGeoBBox("connBoxA", xlen / 2, ylen / 2, zlen / 2); + + ypos = -sIBConnectorYTot / 2 + connBox->GetDY(); + TGeoTranslation* transBodyA = new TGeoTranslation("transBodyA", 0, ypos, 0); + transBodyA->RegisterYourself(); + + ypos = -sIBConnectorYTot / 2; + zpos = -connBox->GetDZ() - connTail->GetZ(1); + TGeoTranslation* transTailA = new TGeoTranslation("transTailA", 0, ypos, zpos); + transTailA->RegisterYourself(); + + TGeoTube* connTubeHollow = new TGeoTube("tubeHollowA", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 2); + + xpos = sIBConnTubesXDist / 2; + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = -connBox->GetDZ() - connTail->GetZ(1) + sIBConnTubeHole1ZLen / 2; + TGeoTranslation* connTubeHollTrans1 = new TGeoTranslation("tubeHollTrans1A", -xpos, ypos, zpos); + connTubeHollTrans1->RegisterYourself(); + TGeoTranslation* connTubeHollTrans2 = new TGeoTranslation("tubeHollTrans2A", xpos, ypos, zpos); + connTubeHollTrans2->RegisterYourself(); + + zpos = -connBox->GetDZ() + connTubeHole2->GetDz() - 2 * connFitHole->GetDz(); + TGeoTranslation* connTubes2Trans1Body = new TGeoTranslation("tubes2Trans1BA", -xpos, ypos, zpos); + connTubes2Trans1Body->RegisterYourself(); + TGeoTranslation* connTubes2Trans2Body = new TGeoTranslation("tubes2Trans2BA", xpos, ypos, zpos); + connTubes2Trans2Body->RegisterYourself(); + + TGeoCompositeShape* connBoxSh = new TGeoCompositeShape( + "connBoxA:transBodyA-tube2HoleA:tubes2Trans1BA-tube2HoleA:tubes2Trans2BA+connTailA:transTailA-tubeHollowA:tubeHollTrans1A-" + "tubeHollowA:tubeHollTrans2A"); + + TGeoVolume* connBoxASide = new TGeoVolume("IBConnectorASide", connBoxSh, medAir); + + // Finally build up the connector + // (NB: the origin is in the connBox, i.e. w/o the tail in Z) + ypos = -sIBConnectorYTot / 2; + zpos = -connBox->GetDZ() - connTail->GetZ(1); + connBoxASide->AddNode(connBlockTail, 1, new TGeoTranslation(0, ypos, zpos)); + + ypos = -sIBConnectorYTot / 2 + connBody->GetDY(); + zpos = -connBox->GetDZ() + connBody->GetDZ(); + connBoxASide->AddNode(connBlockBody, 1, new TGeoTranslation(0, ypos, zpos)); + + xpos = sIBConnTubesXDist / 2; + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = connBox->GetDZ() - connFitSh->GetDz(); + connBoxASide->AddNode(connFit, 1, new TGeoTranslation(xpos, ypos, zpos)); + connBoxASide->AddNode(connFit, 2, new TGeoTranslation(-xpos, ypos, zpos)); +} + +void V3Layer::createIBConnectorsCSide(const TGeoManager* mgr) +{ + // + // Create the C-Side end-stave connectors for IB staves + // + // Created: 05 May 2015 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version + // Updated: 28 Jan 2018 Mario Sitta To last drawings (ALIITSUP0051) + // Updated: 15 May 2019 Mario Sitta Avoid fake overlaps with EndWheels + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + + // First create all elements + // (All measures refer to the blueprint ALIITSUP0051) + + // The connector block, two Composite Shapes: + // the body... + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + TGeoBBox* connBody = new TGeoBBox("connBodyC", xlen / 2, ylen / 2, zlen / 2); + + TGeoTube* connRoundHole = new TGeoTube("connRoundHoleC", 0., sIBConnRoundHoleD / 2, sIBConnBodyYHeight / 1.5); + + zpos = -connBody->GetDZ() + sIBConnRoundHoleZ; + TGeoCombiTrans* connRoundHoleTrans = + new TGeoCombiTrans("roundHoleTransC", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + connRoundHoleTrans->RegisterYourself(); + + TGeoTube* connInsertHole = new TGeoTube("connInsertHoleC", 0, sIBConnInsertHoleD / 2, sIBConnBodyYHeight / 1.5); + + zpos = -connBody->GetDZ() + sIBConnInsertHoleZPos; + TGeoCombiTrans* connInsertHoleTrans = + new TGeoCombiTrans("insertHoleTransC", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + connInsertHoleTrans->RegisterYourself(); + + TGeoTube* connTubeHole2 = new TGeoTube("tube2HoleC", 0, sIBConnTubeHole2D / 2, connBody->GetDZ()); + + xpos = sIBConnTubesXDist / 2; + ypos = -connBody->GetDY() + sIBConnTubesYPos; + zpos = sIBConnectBlockZLen - sIBConnTubeHole3ZPos; + TGeoTranslation* connTubes2Trans1 = new TGeoTranslation("tubes2Trans1C", -xpos, ypos, -zpos); + connTubes2Trans1->RegisterYourself(); + TGeoTranslation* connTubes2Trans2 = new TGeoTranslation("tubes2Trans2C", xpos, ypos, -zpos); + connTubes2Trans2->RegisterYourself(); + + zlen = sIBConnectorXWidth; + TGeoTube* connTubeHole3 = new TGeoTube("tube3HoleC", 0, sIBConnTubeHole2D / 2, zlen / 2); + + xpos = sIBConnTubeHole3XPos; + zpos = connBody->GetDZ() - (sIBConnectBlockZLen - sIBConnTubeHole3ZPos); + TGeoCombiTrans* connTubes3Trans = + new TGeoCombiTrans("tubes3TransC", xpos, ypos, zpos, new TGeoRotation("", 90, -90, 90)); + connTubes3Trans->RegisterYourself(); + + zlen = sIBConnTubeHole1ZLen - sIBConnTailZLen; + TGeoTube* connTubeHole4 = new TGeoTube("tube4HoleC", 0, sIBConnTubeHole1D / 2, zlen); + + xpos = sIBConnTubesXDist / 2; + zpos = connBody->GetDZ(); + TGeoTranslation* connTubes4Trans1 = new TGeoTranslation("tubes4Trans1C", -xpos, ypos, -zpos); + connTubes4Trans1->RegisterYourself(); + TGeoTranslation* connTubes4Trans2 = new TGeoTranslation("tubes4Trans2C", xpos, ypos, -zpos); + connTubes4Trans2->RegisterYourself(); + + zlen = sIBConnSideHole1XWid / 1.5; + TGeoTube* sideHole1 = new TGeoTube("sideHole1C", 0, sIBConnSideHole1D / 2, zlen); + + xpos = -connBody->GetDX() + sIBConnSideHole1XWid - sideHole1->GetDz(); + ypos = connBody->GetDY() - sIBConnSideHole1YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole1ZPos - sIBConnTailZLen); + TGeoCombiTrans* connSideHole1Trans = + new TGeoCombiTrans("sideHole1TransC", xpos, ypos, zpos, new TGeoRotation("", 90, 90, 0)); + connSideHole1Trans->RegisterYourself(); + + TGeoBBox* sideHole2Box = + new TGeoBBox("sideHole2CB", sIBConnSideHole2XWid, sIBConnSideHole2YWid / 2, sIBConnSideHole2ZWid / 2); + + xpos = connBody->GetDX(); + ypos = connBody->GetDY() - sIBConnSideHole2YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + sideHole2Box->GetDZ(); + TGeoTranslation* sideHole2BTrans = new TGeoTranslation("sideHole2TransBC", xpos, ypos, zpos); + sideHole2BTrans->RegisterYourself(); + + TGeoTubeSeg* sideHole2TubeSeg = + new TGeoTubeSeg("sideHole2CTS", 0, sIBConnSideHole2YWid / 2, sIBConnSideHole2XWid, 180, 360); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen); + TGeoCombiTrans* sideHole2TSTrans1 = + new TGeoCombiTrans("sideHole2TSTrans1C", xpos, ypos, zpos, new TGeoRotation("", -90, 90, 0)); + sideHole2TSTrans1->RegisterYourself(); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + 2 * sideHole2Box->GetDZ(); + TGeoCombiTrans* sideHole2TSTrans2 = + new TGeoCombiTrans("sideHole2TSTrans2C", xpos, ypos, zpos, new TGeoRotation("", -90, -90, 0)); + sideHole2TSTrans2->RegisterYourself(); + + TGeoCompositeShape* connBodySh = new TGeoCompositeShape( + "connBodyC-tube2HoleC:tubes2Trans1C-tube2HoleC:tubes2Trans2C-tube3HoleC:tubes3TransC-tube4HoleC:tubes4Trans1C-" + "tube4HoleC:tubes4Trans2C-sideHole1C:sideHole1TransC-sideHole2CTS:sideHole2TSTrans1C-sideHole2CTS:" + "sideHole2TSTrans2C-sideHole2CB:sideHole2TransBC-connRoundHoleC:roundHoleTransC-connInsertHoleC:insertHoleTransC"); + + TGeoVolume* connBlockBody = new TGeoVolume("IBConnectorBlockBodyCSide", connBodySh, medPEEK); + connBlockBody->SetFillColor(42); // Brownish shade + connBlockBody->SetLineColor(42); + + // ...and the tail + xv[0] = sIBConnectorXWidth / 2; + yv[0] = sIBConnTailYShift; + xv[1] = xv[0]; + yv[1] = sIBConnTailYMid; + xv[2] = xv[1] - (sIBConnectorYTot - sIBConnTailYMid) / tanD(90 - sIBConnTailOpenPhi / 2); + yv[2] = sIBConnectorYTot; + + for (Int_t i = 0; i < 3; i++) { + xv[3 + i] = -xv[2 - i]; + yv[3 + i] = yv[2 - i]; + } + + TGeoXtru* connTail = new TGeoXtru(2); + connTail->SetName("connTailC"); + connTail->DefinePolygon(6, xv, yv); + connTail->DefineSection(0, 0); + connTail->DefineSection(1, sIBConnTailZLen); + + TGeoTube* connTubeHole1 = new TGeoTube("tube1HoleC", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 1.5); + + xpos = sIBConnTubesXDist / 2; + ypos = sIBConnTubesYPos; + zpos = connTail->GetZ(1) / 2; + TGeoTranslation* connTubes1Trans1 = new TGeoTranslation("tubes1Trans1C", -xpos, ypos, zpos); + connTubes1Trans1->RegisterYourself(); + TGeoTranslation* connTubes1Trans2 = new TGeoTranslation("tubes1Trans2C", xpos, ypos, zpos); + connTubes1Trans2->RegisterYourself(); + + TGeoCompositeShape* connTailSh = + new TGeoCompositeShape("connTailC-tube1HoleC:tubes1Trans1C-tube1HoleC:tubes1Trans2C"); + + TGeoVolume* connBlockTail = new TGeoVolume("IBConnectorBlockTailCSide", connTailSh, medPEEK); + connBlockTail->SetFillColor(42); // Brownish shade + connBlockTail->SetLineColor(42); + + // The plug, a Pcon + zlen = sIBConnPlugTotLen - sIBConnPlugInnerLen; + TGeoPcon* connPlugSh = new TGeoPcon(0, 360, 4); + connPlugSh->DefineSection(0, 0., 0., sIBConnTubeHole2D / 2); + connPlugSh->DefineSection(1, zlen, 0., sIBConnTubeHole2D / 2); + connPlugSh->DefineSection(2, zlen, sIBConnPlugInnerD / 2, sIBConnTubeHole2D / 2); + connPlugSh->DefineSection(3, sIBConnPlugTotLen, sIBConnPlugInnerD / 2, sIBConnTubeHole2D / 2); + + TGeoVolume* connPlug = new TGeoVolume("IBConnectorPlugC", connPlugSh, medPEEK); + connPlug->SetFillColor(44); // Brownish shade (a bit darker to spot it) + connPlug->SetLineColor(44); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + + TGeoBBox* connBox = new TGeoBBox("connBoxC", xlen / 2, ylen / 2, zlen / 2); + + ypos = -sIBConnectorYTot / 2 + connBox->GetDY(); + TGeoTranslation* transBodyC = new TGeoTranslation("transBodyC", 0, ypos, 0); + transBodyC->RegisterYourself(); + + ypos = -sIBConnectorYTot / 2; + zpos = -connBox->GetDZ() - connTail->GetZ(1); + TGeoTranslation* transTailC = new TGeoTranslation("transTailC", 0, ypos, zpos); + transTailC->RegisterYourself(); + + TGeoTube* connTubeHollow = new TGeoTube("tubeHollowC", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 2); + + xpos = sIBConnTubesXDist / 2; + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = -connBox->GetDZ() - connTail->GetZ(1) + sIBConnTubeHole1ZLen / 2; + TGeoTranslation* connTubeHollTrans1 = new TGeoTranslation("tubeHollTrans1C", -xpos, ypos, zpos); + connTubeHollTrans1->RegisterYourself(); + TGeoTranslation* connTubeHollTrans2 = new TGeoTranslation("tubeHollTrans2C", xpos, ypos, zpos); + connTubeHollTrans2->RegisterYourself(); + + zpos = connBody->GetDZ() - (sIBConnectBlockZLen - sIBConnTubeHole3ZPos); + TGeoTranslation* connTubes2Trans1Body = new TGeoTranslation("tubes2Trans1BC", -xpos, ypos, -zpos); + connTubes2Trans1Body->RegisterYourself(); + TGeoTranslation* connTubes2Trans2Body = new TGeoTranslation("tubes2Trans2BC", xpos, ypos, -zpos); + connTubes2Trans2Body->RegisterYourself(); + + TGeoCompositeShape* connBoxSh = new TGeoCompositeShape( + "connBoxC:transBodyC-tube2HoleC:tubes2Trans1BC-tube2HoleC:tubes2Trans2BC+connTailC:transTailC-tubeHollowC:tubeHollTrans1C-" + "tubeHollowC:tubeHollTrans2C"); + + TGeoVolume* connBoxCSide = new TGeoVolume("IBConnectorCSide", connBoxSh, medAir); + + // Finally build up the connector + // (NB: the origin is in the connBox, i.e. w/o the tail in Z) + ypos = -connBoxSh->GetDY(); + zpos = -connBodySh->GetDZ() - connTail->GetZ(1); + connBoxCSide->AddNode(connBlockTail, 1, new TGeoTranslation(0, ypos, zpos)); + + ypos = -connBoxSh->GetDY() + connBody->GetDY(); + connBoxCSide->AddNode(connBlockBody, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = connBox->GetDX(); + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = connBody->GetDZ() - (sIBConnectBlockZLen - sIBConnTubeHole3ZPos); + connBoxCSide->AddNode(connPlug, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90, -90, 90))); +} + +TGeoVolume* V3Layer::createStaveOuterB(const TGeoManager* mgr) +{ + // Create the chip stave for the Outer Barrel + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 20 Dec 2013 Mario Sitta + // Updated: 12 Mar 2014 Mario Sitta + // Updated: 19 Jul 2017 Mario Sitta O2 version + // + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + mechStavVol = createStaveModelOuterBDummy(mgr); + break; + case Detector::kOBModel0: + case Detector::kOBModel1: + LOG(FATAL) << "Stave model " << mStaveModel << " obsolete and no longer supported"; + break; + case Detector::kOBModel2: + mechStavVol = createStaveModelOuterB2(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V3Layer::createStaveModelOuterBDummy(const TGeoManager*) const +{ + // + // Create dummy stave + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 20 Dec 2013 Mario Sitta + // + + // Done, return the stave structure + return nullptr; +} + +TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) +{ + // + // Create the mechanical half stave structure + // for the Outer Barrel as in TDR + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 20 Nov 2013 Anastasia Barbano + // Updated: 16 Jan 2014 Mario Sitta + // Updated: 24 Feb 2014 Mario Sitta + // Updated: 11 Nov 2014 Mario Sitta Model2 + // Updated: 03 Dec 2014 Mario Sitta Revised with C.Gargiulo latest infos + // Updated: 19 Jul 2017 Mario Sitta O2 version + // Updated: 04 Aug 2018 Mario Sitta Updated geometry + // Updated: 25 Aug 2018 Mario Sitta To latest blueprints + // + + // Local parameters + Double_t yFlex1 = sOBFlexCableAlThick; + Double_t yFlex2 = sOBFlexCableKapThick; + Double_t flexOverlap = 5; // to be checked - unused for the time being + Double_t yCFleece = sOBCarbonFleeceThick; + Double_t yGraph = sOBGraphiteFoilThick; + Double_t xHalfSt, yHalfSt; + + Double_t xmod, ymod, zmod, ypowbus; + Double_t xtru[12], ytru[12]; + Double_t xpos, ypos, ypos1, zpos /*, zpos5cm*/; + Double_t xlen, ylen, zlen; + const Int_t nameLen = 30; + char volname[nameLen]; + + Double_t rCoolMin, rCoolMax; + rCoolMin = sOBCoolTubeInnerD / 2; + + rCoolMax = rCoolMin + sOBCoolTubeThick; + + // First create all needed shapes + + TGeoVolume* moduleVol = createModuleOuterB(); + moduleVol->SetVisibility(kTRUE); + xmod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDX(); + ymod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDY(); + zmod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDZ(); + + if (mLayerNumber == mNumberOfInnerLayers || + mLayerNumber == mNumberOfInnerLayers + 1) { + zlen = sOBColdPlateZLenML / 2; // Middle Layer + } else { + zlen = sOBColdPlateZLenOL / 2; // Outer Layer + } + + xlen = sOBColdPlateXWidth / 2; + + TGeoBBox* coldPlate = new TGeoBBox("ColdPlate", xlen, sOBColdPlateThick / 2, zlen); + + TGeoBBox* fleeccent = new TGeoBBox("FleeceCent", xlen, yCFleece / 2, zlen); + + TGeoTube* coolTube = new TGeoTube("CoolingTube", rCoolMin, rCoolMax, zlen); + TGeoTube* coolWater = new TGeoTube("CoolingWater", 0., rCoolMin, zlen); + + xlen = sOBColdPlateXWidth / 2 - sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + TGeoBBox* graphlat = new TGeoBBox("GraphLateral", xlen / 2, yGraph / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + TGeoBBox* graphmid = new TGeoBBox("GraphMiddle", xlen, yGraph / 2, zlen); + + ylen = coolTube->GetRmax() - yGraph; + TGeoBBox* graphvert = new TGeoBBox("GraphVertical", yGraph / 2, ylen / 2, zlen); + + TGeoTubeSeg* graphtub = new TGeoTubeSeg("GraphTube", rCoolMax, rCoolMax + yGraph, zlen, 180., 360.); + + xlen = sOBColdPlateXWidth / 2 - sOBCoolTubeXDist / 2 - coolTube->GetRmax() - yGraph; + TGeoBBox* fleeclat = new TGeoBBox("FleecLateral", xlen / 2, yCFleece / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax() - yGraph; + TGeoBBox* fleecmid = new TGeoBBox("FleecMiddle", xlen, yCFleece / 2, zlen); + + ylen = coolTube->GetRmax() - yGraph - yCFleece; + TGeoBBox* fleecvert = new TGeoBBox("FleecVertical", yCFleece / 2, ylen / 2, zlen); + + TGeoTubeSeg* fleectub = + new TGeoTubeSeg("FleecTube", rCoolMax + yGraph, rCoolMax + yCFleece + yGraph, zlen, 180., 360.); + + TGeoTube* gammaConvRod; + if (mAddGammaConv) { + gammaConvRod = new TGeoTube("GammaConver", 0, 0.5 * mGammaConvDiam, zlen - sOBCPConnHollowZLen); + } + + // TGeoBBox* flex1_5cm = new TGeoBBox("Flex1MV_5cm", xHalfSt, yFlex1 / 2, flexOverlap / 2); + // TGeoBBox* flex2_5cm = new TGeoBBox("Flex2MV_5cm", xHalfSt, yFlex2 / 2, flexOverlap / 2); + + // The power bus + TGeoVolume* powerBusVol = createOBPowerBiasBuses(zlen); + powerBusVol->SetVisibility(kTRUE); + ypowbus = (static_cast<TGeoBBox*>(powerBusVol->GetShape()))->GetDY(); + + // The half stave container (an XTru to avoid overlaps between neightbours) + xHalfSt = xmod; // add the cross cables when done! + yHalfSt = ypowbus + ymod + coldPlate->GetDY() + 2 * fleeccent->GetDY() + graphlat->GetDY() + fleeclat->GetDY(); + if (mAddGammaConv) { + yHalfSt += mGammaConvDiam; + } + + xtru[0] = xHalfSt; + ytru[0] = 0; + xtru[1] = xtru[0]; + ytru[1] = -2 * yHalfSt; + xtru[2] = sOBCoolTubeXDist / 2 + fleectub->GetRmax(); + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[2] - (coolTube->GetRmax() + fleectub->GetRmax()); + if (mAddGammaConv) { + ytru[3] -= mGammaConvDiam; + } + xtru[4] = sOBCoolTubeXDist / 2 - fleectub->GetRmax(); + ytru[4] = ytru[3]; + xtru[5] = xtru[4]; + ytru[5] = ytru[2]; + for (Int_t i = 0; i < 6; i++) { + xtru[6 + i] = -xtru[5 - i]; + ytru[6 + i] = ytru[5 - i]; + } + TGeoXtru* halfStaveCent = new TGeoXtru(2); + halfStaveCent->DefinePolygon(12, xtru, ytru); + halfStaveCent->DefineSection(0, -zlen); + halfStaveCent->DefineSection(1, zlen); + snprintf(volname, nameLen, "staveCentral%d", mLayerNumber); + halfStaveCent->SetName(volname); + + // The connectors' containers + TGeoBBox* connAside = new TGeoBBox("connAsideOB", sOBCPConnectorXWidth / 2, sOBCPConnBlockYHei / 2, + (sOBCPConnBlockZLen + sOBCPConnAFitZLen - sOBCPConnAFitZIn) / 2); + + TGeoBBox* connCside = + new TGeoBBox("connCsideOB", sOBCPConnectorXWidth / 2, sOBCPConnBlockYHei / 2, sOBCPConnBlockZLen / 2); + + // The StaveStruct container, a Composite Shape + if (mAddGammaConv) { + yHalfSt -= mGammaConvDiam; + } + ypos = 2 * yHalfSt + connAside->GetDY() - sOBCPConnHollowYHei; + zpos = zlen + connAside->GetDZ() - sOBCPConnHollowZLen; + snprintf(volname, nameLen, "transAsideOB%d", mLayerNumber); + TGeoTranslation* transAside = new TGeoTranslation(volname, 0, -ypos, zpos); + transAside->RegisterYourself(); + + zpos = zlen + connCside->GetDZ() - sOBCPConnHollowZLen; + snprintf(volname, nameLen, "transCsideOB%d", mLayerNumber); + TGeoTranslation* transCside = new TGeoTranslation(volname, 0, -ypos, -zpos); + transCside->RegisterYourself(); + + char componame[70]; + snprintf(componame, 70, "staveCentral%d+connAsideOB:transAsideOB%d+connCsideOB:transCsideOB%d", mLayerNumber, + mLayerNumber, mLayerNumber); + + TGeoCompositeShape* halfStave = new TGeoCompositeShape(componame); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAluminum = mgr->GetMedium("TRK_ALUMINUM$"); + TGeoMedium* medK13D2U120 = mgr->GetMedium("TRK_K13D2U120$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medWater = mgr->GetMedium("TRK_WATER$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("TRK_CarbonFleece$"); + TGeoMedium* medFGS003 = mgr->GetMedium("TRK_FGS003$"); // amec thermasol + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medTungsten = mgr->GetMedium("TRK_TUNGSTEN$"); + + TGeoVolume* coldPlateVol = new TGeoVolume("ColdPlateVol", coldPlate, medK13D2U120); + coldPlateVol->SetLineColor(kYellow - 3); + coldPlateVol->SetFillColor(coldPlateVol->GetLineColor()); + coldPlateVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleeccentVol = new TGeoVolume("CarbonFleeceCentral", fleeccent, medCarbonFleece); + fleeccentVol->SetLineColor(kViolet); + fleeccentVol->SetFillColor(fleeccentVol->GetLineColor()); + fleeccentVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* coolTubeVol = new TGeoVolume("CoolingTubeVol", coolTube, medKapton); + coolTubeVol->SetLineColor(kGray); + coolTubeVol->SetFillColor(coolTubeVol->GetLineColor()); + coolTubeVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* coolWaterVol; + coolWaterVol = new TGeoVolume("CoolingWaterVol", coolWater, medWater); + coolWaterVol->SetLineColor(kBlue); + coolWaterVol->SetFillColor(coolWaterVol->GetLineColor()); + coolWaterVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphlatVol = new TGeoVolume("GraphiteFoilLateral", graphlat, medFGS003); + graphlatVol->SetLineColor(kGreen); + graphlatVol->SetFillColor(graphlatVol->GetLineColor()); + graphlatVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphmidVol = new TGeoVolume("GraphiteFoilMiddle", graphmid, medFGS003); + graphmidVol->SetLineColor(kGreen); + graphmidVol->SetFillColor(graphmidVol->GetLineColor()); + graphmidVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphvertVol = new TGeoVolume("GraphiteFoilVertical", graphvert, medFGS003); + graphvertVol->SetLineColor(kGreen); + graphvertVol->SetFillColor(graphvertVol->GetLineColor()); + graphvertVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphtubVol = new TGeoVolume("GraphiteFoilPipeCover", graphtub, medFGS003); + graphtubVol->SetLineColor(kGreen); + graphtubVol->SetFillColor(graphtubVol->GetLineColor()); + graphtubVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleeclatVol = new TGeoVolume("CarbonFleeceLateral", fleeclat, medCarbonFleece); + fleeclatVol->SetLineColor(kViolet); + fleeclatVol->SetFillColor(fleeclatVol->GetLineColor()); + fleeclatVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleecmidVol = new TGeoVolume("CarbonFleeceMiddle", fleecmid, medCarbonFleece); + fleecmidVol->SetLineColor(kViolet); + fleecmidVol->SetFillColor(fleecmidVol->GetLineColor()); + fleecmidVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleecvertVol = new TGeoVolume("CarbonFleeceVertical", fleecvert, medCarbonFleece); + fleecvertVol->SetLineColor(kViolet); + fleecvertVol->SetFillColor(fleecvertVol->GetLineColor()); + fleecvertVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleectubVol = new TGeoVolume("CarbonFleecePipeCover", fleectub, medCarbonFleece); + fleectubVol->SetLineColor(kViolet); + fleectubVol->SetFillColor(fleectubVol->GetLineColor()); + fleectubVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* gammaConvRodVol; + if (mAddGammaConv) { + gammaConvRodVol = new TGeoVolume("GammaConversionRod", gammaConvRod, medTungsten); + gammaConvRodVol->SetLineColor(kBlack); + gammaConvRodVol->SetFillColor(gammaConvRodVol->GetLineColor()); + gammaConvRodVol->SetFillStyle(4000); // 0% transparent + } + + snprintf(volname, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSHalfStavePattern(), mLayerNumber); + TGeoVolume* halfStaveVol = new TGeoVolume(volname, halfStave, medAir); + // halfStaveVol->SetLineColor(12); + // halfStaveVol->SetFillColor(12); + // halfStaveVol->SetVisibility(kTRUE); + + // TGeoVolume* flex1_5cmVol = new TGeoVolume("Flex1Vol5cm", flex1_5cm, medAluminum); + // TGeoVolume* flex2_5cmVol = new TGeoVolume("Flex2Vol5cm", flex2_5cm, medKapton); + + // flex1_5cmVol->SetLineColor(kRed); + // flex2_5cmVol->SetLineColor(kGreen); + + // Now build up the half stave + ypos = -ypowbus; + halfStaveVol->AddNode(powerBusVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos -= (ypowbus + ymod); + for (Int_t j = 0; j < mNumberOfModules; j++) { + zpos = zlen - j * (2 * zmod + sOBModuleGap) - zmod; + halfStaveVol->AddNode(moduleVol, j, new TGeoTranslation(0, ypos, zpos)); + mHierarchy[kModule]++; + } + + ypos -= ymod; + + ypos -= fleeccent->GetDY(); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(fleeccentVol, 1, new TGeoTranslation(0, ypos, 0)); + } + ypos -= fleeccent->GetDY(); + + ypos -= coldPlate->GetDY(); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(coldPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + } + ypos -= coldPlate->GetDY(); + + ypos -= fleeccent->GetDY(); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(fleeccentVol, 2, new TGeoTranslation(0, ypos, 0)); + } + + xpos = sOBCoolTubeXDist / 2; + ypos1 = ypos - (fleeccent->GetDY() + coolTube->GetRmax()); + if (mBuildLevel < 4) { // Water + halfStaveVol->AddNode(coolWaterVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(coolWaterVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + } + + if (mBuildLevel < 5) { // Kapton + halfStaveVol->AddNode(coolTubeVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(coolTubeVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + } + + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(graphtubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphtubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halfStaveVol->AddNode(fleectubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleectubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + } + + xpos = sOBColdPlateXWidth / 2 - graphlat->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + graphlat->GetDY()); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(graphlatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphlatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halfStaveVol->AddNode(graphmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = sOBColdPlateXWidth / 2 - 2 * graphlat->GetDX() + graphvert->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + graphvert->GetDY()); + halfStaveVol->AddNode(graphvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = graphmid->GetDX() - graphvert->GetDX(); + halfStaveVol->AddNode(graphvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + } + + xpos = sOBColdPlateXWidth / 2 - fleeclat->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + fleeclat->GetDY()); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(fleeclatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleeclatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halfStaveVol->AddNode(fleecmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = sOBColdPlateXWidth / 2 - 2 * fleeclat->GetDX() + fleecvert->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + 2 * fleeclat->GetDY() + fleecvert->GetDY()); + halfStaveVol->AddNode(fleecvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleecvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = fleecmid->GetDX() - fleecvert->GetDX(); + halfStaveVol->AddNode(fleecvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleecvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + } + + // Add the Gamma Converter Rod (only on Layer 3) - M.S. 17 Oct 2016 + if (mAddGammaConv) { + xpos = mGammaConvXPos; + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + 2 * fleeclat->GetDY() + gammaConvRod->GetRmax()); + halfStaveVol->AddNode(gammaConvRodVol, 1, new TGeoTranslation(xpos, ypos1, 0)); + } + + // Add the end-stave connectors + TGeoVolume *connectorASide, *connectorCSide; + + // Check whether we have already all pieces + // Otherwise create them + connectorASide = mgr->GetVolume("OBColdPlateConnectorASide"); + + if (!connectorASide) { + createOBColdPlateConnectors(); + connectorASide = mgr->GetVolume("OBColdPlateConnectorASide"); + } + connectorCSide = mgr->GetVolume("OBColdPlateConnectorCSide"); + + ypos = 2 * yHalfSt + (static_cast<TGeoBBox*>(connectorASide->GetShape()))->GetDY() - sOBCPConnHollowYHei; + zpos = zlen + (static_cast<TGeoBBox*>(connectorASide->GetShape()))->GetDZ() - sOBCPConnHollowZLen; + halfStaveVol->AddNode(connectorASide, 1, new TGeoCombiTrans(0, -ypos, zpos, new TGeoRotation("", 180, 0, 0))); + + zpos = zlen + (static_cast<TGeoBBox*>(connectorCSide->GetShape()))->GetDZ() - sOBCPConnHollowZLen; + halfStaveVol->AddNode(connectorCSide, 1, new TGeoCombiTrans(0, -ypos, -zpos, new TGeoRotation("", 180, 0, 0))); + + // Done, return the half stave structure + return halfStaveVol; +} + +TGeoVolume* V3Layer::createOBPowerBiasBuses(const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the OB Power Bus and Bias Bus cables + // + // Input: + // zcable : the cable half Z length + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume with both the Power and the Bias Buses + // + // Created: 05 Aug 2018 Mario Sitta + // Updated: 06 Sep 2018 Mario Sitta + // + + Double_t xcable, ytot, ypos; + + // First create all needed shapes + xcable = sOBPowerBusXWidth / 2; + TGeoBBox* gndPB = new TGeoBBox(xcable, sOBPowerBusAlThick / 2, zcable); + TGeoBBox* dielPB = new TGeoBBox(xcable, sOBPowerBusDielThick / 2, zcable); + TGeoBBox* kapPB = new TGeoBBox(xcable, sOBPowerBusKapThick / 2, zcable); + xcable *= sOBPowerBusAlFrac; + TGeoBBox* topPB = new TGeoBBox(xcable, sOBPowerBusAlThick / 2, zcable); + + xcable = sOBBiasBusXWidth / 2; + TGeoBBox* botBB = new TGeoBBox(xcable, sOBBiasBusAlThick / 2, zcable); + TGeoBBox* dielBB = new TGeoBBox(xcable, sOBBiasBusDielThick / 2, zcable); + TGeoBBox* kapBB = new TGeoBBox(xcable, sOBBiasBusKapThick / 2, zcable); + xcable *= sOBBiasBusAlFrac; + TGeoBBox* topBB = new TGeoBBox(xcable, sOBBiasBusAlThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medAluminum = mgr->GetMedium("TRK_ALUMINUM$"); + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + TGeoVolume* gndPBVol = new TGeoVolume("PowerBusGround", gndPB, medAluminum); + gndPBVol->SetLineColor(kCyan); + gndPBVol->SetFillColor(gndPBVol->GetLineColor()); + gndPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* dielPBVol = new TGeoVolume("PowerBusDielectric", dielPB, medKapton); + dielPBVol->SetLineColor(kBlue); + dielPBVol->SetFillColor(dielPBVol->GetLineColor()); + dielPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* kapPBVol = new TGeoVolume("PowerBusKapton", kapPB, medKapton); + kapPBVol->SetLineColor(kBlue); + kapPBVol->SetFillColor(kapPBVol->GetLineColor()); + kapPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* topPBVol = new TGeoVolume("PowerBusTop", topPB, medAluminum); + topPBVol->SetLineColor(kCyan); + topPBVol->SetFillColor(topPBVol->GetLineColor()); + topPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* botBBVol = new TGeoVolume("BiasBusBottom", botBB, medAluminum); + botBBVol->SetLineColor(kCyan); + botBBVol->SetFillColor(botBBVol->GetLineColor()); + botBBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* dielBBVol = new TGeoVolume("BiasBusDielectric", dielBB, medKapton); + dielBBVol->SetLineColor(kBlue); + dielBBVol->SetFillColor(dielBBVol->GetLineColor()); + dielBBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* kapBBVol = new TGeoVolume("BiasBusKapton", kapBB, medKapton); + kapBBVol->SetLineColor(kBlue); + kapBBVol->SetFillColor(kapBBVol->GetLineColor()); + kapBBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* topBBVol = new TGeoVolume("BiasBusTop", topBB, medAluminum); + topBBVol->SetLineColor(kCyan); + topBBVol->SetFillColor(topBBVol->GetLineColor()); + topBBVol->SetFillStyle(4000); // 0% transparent + + // Finally the volume containing both the Power Bus and the Bias Bus + xcable = sOBPowerBusXWidth / 2; + ytot = 2 * kapPB->GetDY() + topPB->GetDY() + dielPB->GetDY() + gndPB->GetDY() + 2 * kapBB->GetDY() + topBB->GetDY() + dielBB->GetDY() + botBB->GetDY(); + + TGeoBBox* pnbBus = new TGeoBBox(xcable, ytot, zcable); + + TGeoVolume* pnbBusVol = new TGeoVolume("OBPowerBiasBus", pnbBus, medAir); + + // Volumes are piled up from bottom to top + ypos = -pnbBus->GetDY() + kapPB->GetDY(); + if (mBuildLevel < 5) { // Kapton + pnbBusVol->AddNode(kapPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (kapPB->GetDY() + gndPB->GetDY()); + if (mBuildLevel < 2) { // Aluminum + pnbBusVol->AddNode(gndPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (gndPB->GetDY() + dielPB->GetDY()); + if (mBuildLevel < 5) { // Kapton + pnbBusVol->AddNode(dielPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (dielPB->GetDY() + topPB->GetDY()); + if (mBuildLevel < 2) { // Aluminum + pnbBusVol->AddNode(topPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (topPB->GetDY() + kapPB->GetDY()); + if (mBuildLevel < 5) { // Kapton + pnbBusVol->AddNode(kapPBVol, 2, new TGeoTranslation(0, ypos, 0)); + } + + // + ypos += (kapPB->GetDY() + kapBB->GetDY()); + if (mBuildLevel < 5) { // Kapton + pnbBusVol->AddNode(kapBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (kapBB->GetDY() + botBB->GetDY()); + if (mBuildLevel < 2) { // Aluminum + pnbBusVol->AddNode(botBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (botBB->GetDY() + dielBB->GetDY()); + if (mBuildLevel < 5) { // Kapton + pnbBusVol->AddNode(dielBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (dielBB->GetDY() + topBB->GetDY()); + if (mBuildLevel < 2) { // Aluminum + pnbBusVol->AddNode(topBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + ypos += (topBB->GetDY() + kapBB->GetDY()); + if (mBuildLevel < 5) { // Kapton + pnbBusVol->AddNode(kapBBVol, 2, new TGeoTranslation(0, ypos, 0)); + } + + // + return pnbBusVol; +} + +void V3Layer::createOBColdPlateConnectors() +{ + // + // Create the Cold Plate connectors for OB half staves + // (simply call the actual creator methods) + // + // Input: + // + // Output: + // + // Return: + // + // Created: 26 May 2015 Mario Sitta + // + + createOBColdPlateConnectorsASide(); + createOBColdPlateConnectorsCSide(); +} + +void V3Layer::createOBColdPlateConnectorsASide() +{ + // + // Create the A-Side end-stave connectors for IB staves + // + // Input: + // + // Output: + // + // Return: + // + // Created: 26 May 2015 Mario Sitta + // Updated: 20 Jul 2017 Mario Sitta O2 version + // Updated: 15 Oct 2018 Mario Sitta To latest blueprints + // + + // The geoManager + const TGeoManager* mgr = gGeoManager; + + // Local variables + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + TGeoMedium* medInox304 = mgr->GetMedium("TRK_INOX304$"); + + // First create all elements + + // The connector block, a Composite Shape + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen; + TGeoBBox* connBlock = new TGeoBBox("connBlockA", xlen / 2, ylen / 2, zlen / 2); + + xv[0] = sOBCPConnectorXWidth * 0.6; + yv[0] = -sOBCPConnHollowYHei; + xv[1] = xv[0]; + yv[1] = sOBCPConnHollowYHei; + xv[2] = sOBCPConnTubesXDist / 2 + sOBCPConnTubeHole1D / 2; + yv[2] = yv[1]; + xv[3] = xv[2]; + yv[3] = sOBCPConnTubesYPos; + xv[4] = sOBCPConnTubesXDist / 2 - sOBCPConnTubeHole1D / 2; + yv[4] = yv[3]; + xv[5] = xv[4]; + yv[5] = yv[2]; + + for (Int_t i = 0; i < 6; i++) { + xv[6 + i] = -xv[5 - i]; + yv[6 + i] = yv[5 - i]; + } + + TGeoXtru* connBlockHoll = new TGeoXtru(2); + connBlockHoll->SetName("connBlockHollA"); + connBlockHoll->DefinePolygon(12, xv, yv); + connBlockHoll->DefineSection(0, -sOBCPConnHollowZLen); + connBlockHoll->DefineSection(1, sOBCPConnHollowZLen); + + ypos = -connBlock->GetDY(); + zpos = -connBlock->GetDZ(); + TGeoTranslation* transBlockHoll = new TGeoTranslation("transBlockHollA", 0, ypos, zpos); + transBlockHoll->RegisterYourself(); + + xlen = sOBCPConnSquareHoleX / 2; + ylen = sOBCPConnBlockYHei / 1.5; + zlen = sOBCPConnSquareHoleZ / 2; + TGeoBBox* connSquareHole = new TGeoBBox("connASquareHole", xlen, ylen, zlen); + + zpos = + -connBlock->GetDZ() + (sOBCPConnSqrHoleZPos + connSquareHole->GetDZ()); + TGeoTranslation* transSquareHole = new TGeoTranslation("transASquareHole", 0, 0, zpos); + transSquareHole->RegisterYourself(); + + zlen = sOBCPConnTubeHole1Z; + TGeoTube* connTubeHole1 = new TGeoTube("tube1AHole", 0, sOBCPConnTubeHole1D / 2, zlen); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = connBlock->GetDZ(); + TGeoTranslation* trans1Tube1AHole = new TGeoTranslation("trans1Tube1AHole", -xpos, ypos, -zpos); + trans1Tube1AHole->RegisterYourself(); + TGeoTranslation* trans2Tube1AHole = new TGeoTranslation("trans2Tube1AHole", xpos, ypos, -zpos); + trans2Tube1AHole->RegisterYourself(); + + zlen = sOBCPConnBlockZLen; + TGeoTube* connTubeHole2 = new TGeoTube("tube2AHole", 0, sOBCPConnTubeHole2D / 2, zlen); + + TGeoTranslation* trans1Tube2AHole = new TGeoTranslation("trans1Tube2AHole", -xpos, ypos, 0); + trans1Tube2AHole->RegisterYourself(); + TGeoTranslation* trans2Tube2AHole = new TGeoTranslation("trans2Tube2AHole", xpos, ypos, 0); + trans2Tube2AHole->RegisterYourself(); + + zlen = sOBCPConnAFitZIn; + TGeoTube* connFitHole = new TGeoTube("fitAHole", 0, sOBCPConnFitHoleD / 2, zlen); + + TGeoTranslation* trans1FitAHole = new TGeoTranslation("trans1FitAHole", -xpos, ypos, zpos); + trans1FitAHole->RegisterYourself(); + TGeoTranslation* trans2FitAHole = new TGeoTranslation("trans2FitAHole", xpos, ypos, zpos); + trans2FitAHole->RegisterYourself(); + + TGeoCompositeShape* connBlockSh = new TGeoCompositeShape( + "connBlockA-connBlockHollA:transBlockHollA-connASquareHole:transASquareHole-tube1AHole:trans1Tube1AHole-tube1AHole:" + "trans2Tube1AHole-tube2AHole:trans1Tube2AHole-tube2AHole:trans2Tube2AHole-fitAHole:trans1FitAHole-fitAHole:" + "trans2FitAHole"); + + TGeoVolume* connBlockA = new TGeoVolume("OBColdPlateConnectorBlockASide", connBlockSh, medPEEK); + connBlockA->SetFillColor(42); // Brownish shade + connBlockA->SetLineColor(42); + + // The fitting tubes, a Tube + Double_t rmin = sOBCPConnAFitExtD / 2 - sOBCPConnAFitThick; + TGeoTube* connFitSh = new TGeoTube(rmin, sOBCPConnAFitExtD / 2, sOBCPConnAFitZLen / 2); + + TGeoVolume* connFit = new TGeoVolume("OBColdPlateConnectorFitting", connFitSh, medInox304); + connFit->SetFillColor(kGray); + connFit->SetLineColor(kGray); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen + (sOBCPConnAFitZLen - sOBCPConnAFitZIn); + TGeoBBox* connBox = new TGeoBBox("connectorOBCPA", xlen / 2, ylen / 2, zlen / 2); + + ypos = -connBox->GetDY(); + zpos = -connBox->GetDZ(); + TGeoTranslation* transBoxHoll = new TGeoTranslation("transBoxHollA", 0, ypos, zpos); + transBoxHoll->RegisterYourself(); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBox->GetDY() + sOBCPConnTubesYPos; + zpos = connBox->GetDZ(); + TGeoTranslation* trans1BoxHole = new TGeoTranslation("trans1BoxAHole", -xpos, ypos, -zpos); + trans1BoxHole->RegisterYourself(); + TGeoTranslation* trans2BoxHole = new TGeoTranslation("trans2BoxAHole", xpos, ypos, -zpos); + trans2BoxHole->RegisterYourself(); + + TGeoCompositeShape* connectSh = new TGeoCompositeShape( + "connectorOBCPA-connBlockHollA:transBoxHollA-tube1AHole:trans1BoxAHole-tube1AHole:trans2BoxAHole"); + + TGeoVolume* connectorASide = new TGeoVolume("OBColdPlateConnectorASide", connectSh, medAir); + + // Finally build up the connector + zpos = -connectSh->GetDZ() + connBlock->GetDZ(); + connectorASide->AddNode(connBlockA, 1, new TGeoTranslation(0, 0, zpos)); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = connectSh->GetDZ() - connFitSh->GetDz(); + connectorASide->AddNode(connFit, 1, new TGeoTranslation(-xpos, ypos, zpos)); + connectorASide->AddNode(connFit, 2, new TGeoTranslation(xpos, ypos, zpos)); +} + +void V3Layer::createOBColdPlateConnectorsCSide() +{ + // + // Create the C-Side end-stave connectors for IB staves + // + // Input: + // + // Output: + // + // Return: + // + // Created: 29 May 2015 Mario Sitta + // Updated: 20 Jul 2017 Mario Sitta O2 version + // Updated: 15 Oct 2018 Mario Sitta To latest blueprints + // + + // The geoManager + const TGeoManager* mgr = gGeoManager; + + // Local variables + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + + // First create all elements + + // The connector block, a Composite Shape + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen; + TGeoBBox* connBlock = new TGeoBBox("connBlockC", xlen / 2, ylen / 2, zlen / 2); + + xv[0] = sOBCPConnectorXWidth * 0.6; + yv[0] = -sOBCPConnHollowYHei; + xv[1] = xv[0]; + yv[1] = sOBCPConnHollowYHei; + xv[2] = sOBCPConnTubesXDist / 2 + sOBCPConnTubeHole1D / 2; + yv[2] = yv[1]; + xv[3] = xv[2]; + yv[3] = sOBCPConnTubesYPos; + xv[4] = sOBCPConnTubesXDist / 2 - sOBCPConnTubeHole1D / 2; + yv[4] = yv[3]; + xv[5] = xv[4]; + yv[5] = yv[2]; + + for (Int_t i = 0; i < 6; i++) { + xv[6 + i] = -xv[5 - i]; + yv[6 + i] = yv[5 - i]; + } + + TGeoXtru* connBlockHoll = new TGeoXtru(2); + connBlockHoll->SetName("connBlockHollC"); + connBlockHoll->DefinePolygon(12, xv, yv); + connBlockHoll->DefineSection(0, -sOBCPConnHollowZLen); + connBlockHoll->DefineSection(1, sOBCPConnHollowZLen); + + ypos = -connBlock->GetDY(); + zpos = connBlock->GetDZ(); + TGeoTranslation* transBlockHoll = new TGeoTranslation("transBlockHollC", 0, ypos, zpos); + transBlockHoll->RegisterYourself(); + + TGeoTube* connRoundHole = new TGeoTube("connCRoundHole", 0, sOBCPConnRoundHoleD / 2, sOBCPConnBlockYHei / 1.5); + + zpos = connBlock->GetDZ() - sOBCPConnRndHoleZPos; + TGeoCombiTrans* transRoundHole = new TGeoCombiTrans("transCRoundHole", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + transRoundHole->RegisterYourself(); + + zlen = sOBCPConnTubeHole1Z; + TGeoTube* connTubeHole1 = new TGeoTube("tube1CHole", 0, sOBCPConnTubeHole1D / 2, zlen); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = connBlock->GetDZ(); + TGeoTranslation* trans1Tube1AHole = new TGeoTranslation("trans1Tube1CHole", -xpos, ypos, zpos); + trans1Tube1AHole->RegisterYourself(); + TGeoTranslation* trans2Tube1AHole = new TGeoTranslation("trans2Tube1CHole", xpos, ypos, zpos); + trans2Tube1AHole->RegisterYourself(); + + TGeoTube* connTubeHole2 = new TGeoTube("tube2CHole", 0, sOBCPConnTubeHole2D / 2, connBlock->GetDZ()); + + zpos = sOBCPConnTubeHole3ZP; + TGeoTranslation* connTubes2Trans1 = new TGeoTranslation("trans1Tube2CHole", -xpos, ypos, zpos); + connTubes2Trans1->RegisterYourself(); + TGeoTranslation* connTubes2Trans2 = new TGeoTranslation("trans2Tube2CHole", xpos, ypos, zpos); + connTubes2Trans2->RegisterYourself(); + + TGeoTube* connTubeHole3 = new TGeoTube("tube3CHole", 0, sOBCPConnTubeHole2D / 2, connBlock->GetDX()); + + xpos = -sOBCPConnTubeHole3XP; + zpos = -connBlock->GetDZ() + sOBCPConnTubeHole3ZP; + TGeoCombiTrans* connTubes3Trans = + new TGeoCombiTrans("transTube3CHole", xpos, ypos, zpos, new TGeoRotation("", 90, -90, 90)); + connTubes3Trans->RegisterYourself(); + + TGeoCompositeShape* connBlockSh = new TGeoCompositeShape( + "connBlockC-connBlockHollC:transBlockHollC-connCRoundHole:transCRoundHole-tube1CHole:trans1Tube1CHole-tube1CHole:" + "trans2Tube1CHole-tube2CHole:trans1Tube2CHole-tube2CHole:trans2Tube2CHole-tube3CHole:transTube3CHole"); + + TGeoVolume* connBlockC = new TGeoVolume("OBColdPlateConnectorBlockCSide", connBlockSh, medPEEK); + connBlockC->SetFillColor(42); // Brownish shade + connBlockC->SetLineColor(42); + + // The plug, a Pcon + TGeoPcon* connPlugSh = new TGeoPcon(0, 360, 4); + connPlugSh->DefineSection(0, 0., 0., sOBCPConnTubeHole2D / 2); + connPlugSh->DefineSection(1, sOBCPConnPlugThick, 0., sOBCPConnTubeHole2D / 2); + connPlugSh->DefineSection(2, sOBCPConnPlugThick, sOBCPConnPlugInnerD / 2, sOBCPConnTubeHole2D / 2); + connPlugSh->DefineSection(3, sOBCPConnPlugTotLen, sOBCPConnPlugInnerD / 2, sOBCPConnTubeHole2D / 2); + + TGeoVolume* connPlug = new TGeoVolume("OBCPConnectorPlugC", connPlugSh, medPEEK); + connPlug->SetFillColor(44); // Brownish shade (a bit darker to spot it) + connPlug->SetLineColor(44); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen; + TGeoBBox* connBox = new TGeoBBox("connectorOBCPC", xlen / 2, ylen / 2, zlen / 2); + + ypos = -connBox->GetDY(); + zpos = connBox->GetDZ(); + TGeoTranslation* transBoxHoll = new TGeoTranslation("transBoxHollC", 0, ypos, zpos); + transBoxHoll->RegisterYourself(); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBox->GetDY() + sOBCPConnTubesYPos; + zpos = connBox->GetDZ(); + TGeoTranslation* trans1BoxHole = new TGeoTranslation("trans1BoxCHole", -xpos, ypos, zpos); + trans1BoxHole->RegisterYourself(); + TGeoTranslation* trans2BoxHole = new TGeoTranslation("trans2BoxCHole", xpos, ypos, zpos); + trans2BoxHole->RegisterYourself(); + + TGeoCompositeShape* connectSh = new TGeoCompositeShape( + "connectorOBCPC-connBlockHollC:transBoxHollC-tube1CHole:trans1BoxCHole-tube1CHole:trans2BoxCHole"); + + TGeoVolume* connectorCSide = new TGeoVolume("OBColdPlateConnectorCSide", connectSh, medAir); + + // Finally build up the connector + connectorCSide->AddNode(connBlockC, 1); + + xpos = -connBlock->GetDX(); + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = -connBlock->GetDZ() + sOBCPConnTubeHole3ZP; + connectorCSide->AddNode(connPlug, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90, 90, 90))); +} + +TGeoVolume* V3Layer::createSpaceFrameOuterB(const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + case Detector::kOBModel0: + mechStavVol = createSpaceFrameOuterBDummy(mgr); + break; + case Detector::kOBModel1: + case Detector::kOBModel2: + mechStavVol = createSpaceFrameOuterB2(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + + return mechStavVol; +} + +TGeoVolume* V3Layer::createSpaceFrameOuterBDummy(const TGeoManager*) const +{ + // + // Create dummy stave + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + + // Done, return the stave structur + return nullptr; +} + +TGeoVolume* V3Layer::createSpaceFrameOuterB2(const TGeoManager* mgr) +{ + // + // Create the space frame for the Outer Barrel (Model 2) + // The building blocks are created in another method to avoid + // replicating the same volumes for all OB staves + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume with the Space Frame of a stave + // + // Created: 03 Feb 2015 Mario Sitta + // Updated: 04 Jun 2015 Mario Sitta Change container to avoid overlaps + // Updated: 20 Jul 2017 Mario Sitta O2 version + // + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + TGeoVolume *unitVol[2], *next2EndVol[2], *endVol[2]; + Double_t *xtru, *ytru; + Double_t zlen, zpos; + Int_t nPoints; + const Int_t nameLen = 30; + char volname[nameLen]; + + // Check whether we have already all pieces + // Otherwise create them + unitVol[0] = mgr->GetVolume("SpaceFrameUnit0"); + + if (!unitVol[0]) { + createOBSpaceFrameObjects(mgr); + unitVol[0] = mgr->GetVolume("SpaceFrameUnit0"); + } + + unitVol[1] = mgr->GetVolume("SpaceFrameUnit1"); + + next2EndVol[0] = mgr->GetVolume("SpaceFrameNext2EndUnit0"); + next2EndVol[1] = mgr->GetVolume("SpaceFrameNext2EndUnit1"); + + endVol[0] = mgr->GetVolume("SpaceFrameEndUnit0"); + endVol[1] = mgr->GetVolume("SpaceFrameEndUnit1"); + + // Get the shape of the units + // and create a similar shape for the Space Frame container + TGeoXtru* volShape = static_cast<TGeoXtru*>(unitVol[0]->GetShape()); + + nPoints = volShape->GetNvert(); + xtru = new Double_t[nPoints]; + ytru = new Double_t[nPoints]; + + for (Int_t i = 0; i < nPoints; i++) { + xtru[i] = volShape->GetX(i); + ytru[i] = volShape->GetY(i); + } + + Int_t iOBLayer = mLayerNumber - mNumberOfInnerLayers + 3; + Int_t nUnits = sOBSpaceFrameNUnits[iOBLayer / 5]; // 3,4 -> 0 - 5,6 -> 1 + zlen = (nUnits - 2) * sOBSpaceFrameUnitLen; // Take end units out + + TGeoXtru* spaceFrameCentral = new TGeoXtru(2); + spaceFrameCentral->DefinePolygon(nPoints, xtru, ytru); + spaceFrameCentral->DefineSection(0, -zlen / 2); + spaceFrameCentral->DefineSection(1, zlen / 2); + snprintf(volname, nameLen, "sframecentral%d", mLayerNumber); + spaceFrameCentral->SetName(volname); + + zpos = zlen / 2 + sOBSpaceFrameUnitLen / 2; + snprintf(volname, nameLen, "endUnit0Trans%d", mLayerNumber); + TGeoCombiTrans* endUnit0Trans = new TGeoCombiTrans(volname, 0, 0, -zpos, new TGeoRotation("", 90, 180, -90)); + endUnit0Trans->RegisterYourself(); + snprintf(volname, nameLen, "endUnit1Trans%d", mLayerNumber); + TGeoTranslation* endUnit1Trans = new TGeoTranslation(volname, 0, 0, zpos); + endUnit1Trans->RegisterYourself(); + + // The Space Frame container: a Composite Shape to avoid overlaps + // between the U-legs space and the end-stave connectors + // ("endunitcontainer" is defined in CreateOBSpaceFrameObjects) + char componame[100]; + snprintf(componame, 100, "sframecentral%d+endunitcontainer:endUnit0Trans%d+endunitcontainer:endUnit1Trans%d", + mLayerNumber, mLayerNumber, mLayerNumber); + + TGeoCompositeShape* spaceFrame = new TGeoCompositeShape(componame); + + snprintf(volname, nameLen, "SpaceFrameVolumeLay%d", mLayerNumber); + TGeoVolume* spaceFrameVol = new TGeoVolume(volname, spaceFrame, medAir); + spaceFrameVol->SetVisibility(kFALSE); + + // Finally build up the space frame + TGeoXtru* frameUnit = static_cast<TGeoXtru*>(unitVol[0]->GetShape()); + + zpos = -spaceFrame->GetDZ() + frameUnit->GetDZ() + sOBSFrameConnTopLen; + spaceFrameVol->AddNode(endVol[0], 1, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 90, 180, -90))); + + zpos += (2 * frameUnit->GetDZ()); + spaceFrameVol->AddNode(next2EndVol[0], 1, new TGeoTranslation(0, 0, zpos)); + + for (Int_t i = 2; i < nUnits - 2; i++) { + zpos += (2 * frameUnit->GetDZ()); + Int_t j = i / 2; + Int_t k = i - j * 2; // alternatively 0 or 1 + spaceFrameVol->AddNode(unitVol[k], j, new TGeoTranslation(0, 0, zpos)); + } + + zpos += (2 * frameUnit->GetDZ()); + spaceFrameVol->AddNode(next2EndVol[1], 1, new TGeoTranslation(0, 0, zpos)); + + zpos += (2 * frameUnit->GetDZ()); + spaceFrameVol->AddNode(endVol[1], 1, new TGeoTranslation(0, 0, zpos)); + + // Done, clean up and return the space frame structure + delete[] xtru; + delete[] ytru; + + return spaceFrameVol; +} + +void V3Layer::createOBSpaceFrameObjects(const TGeoManager* mgr) +{ + // + // Create the space frame building blocks for the Outer Barrel + // This method is practically identical to previous versions of + // CreateSpaceFrameOuterB1 + // NB: it is pretty cumbersome, because we don't want to use assemblies + // so we are forced to have well-crafted containers to avoid fake overlaps + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume with the Space Frame of a stave + // + // Created: 03 Feb 2015 Mario Sitta + // Updated: 03 Jun 2015 Mario Sitta End units w/o U-legs + // Updated: 20 Jul 2017 Mario Sitta O2 version + // Updated: 09 Sep 2019 Mario Sitta Connectors added + // Updated: 27 Sep 2019 Mario Sitta New TopV for End Units + // + + // Materials defined in AliITSUv2 + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); + TGeoMedium* medF6151B05M = mgr->GetMedium("TRK_F6151B05M$"); + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + + // Local parameters + Double_t halfFrameWidth = sOBSpaceFrameWidth / 2; + Double_t triangleHeight = sOBSpaceFrameHeight; + Double_t sframeHeight = triangleHeight + sOBSFrameBaseRibDiam + sOBSFrameULegHeight2 * 2; + Double_t staveLa = sOBSpaceFrameTopVL; + Double_t staveHa = sOBSpaceFrameTopVH; + Double_t staveLb = sOBSpaceFrameSideVL; + Double_t staveHb = sOBSpaceFrameSideVH; + Double_t alphaDeg = sOBSpaceFrameVAlpha; + Double_t alphaRad = alphaDeg * TMath::DegToRad() / 2; + Double_t beta = sOBSpaceFrameVBeta * TMath::DegToRad() / 2; + Double_t sideRibRadius = sOBSFrameSideRibDiam / 2; + Double_t sidePhiDeg = sOBSFrameSideRibPhi; + Double_t sidePhiRad = sidePhiDeg * TMath::DegToRad(); + Double_t baseRibRadius = sOBSFrameBaseRibDiam / 2; + Double_t basePhiDeg = sOBSFrameBaseRibPhi; + Double_t basePhiRad = basePhiDeg * TMath::DegToRad(); + Double_t ulegHalfLen = sOBSFrameULegLen / 2; + Double_t ulegHalfWidth = sOBSFrameULegWidth / 2; + Double_t ulegHigh1 = sOBSFrameULegHeight1; + Double_t ulegHigh2 = sOBSFrameULegHeight2; + Double_t ulegThick = sOBSFrameULegThick; + Double_t topVFactorEU = 0.60; // Fraction of TopV total length for End Units + + Double_t xlen, zlen; + Double_t xpos, ypos, zpos; + Double_t unitlen; + Double_t xtru[22], ytru[22]; + + unitlen = sOBSpaceFrameUnitLen; + + xlen = halfFrameWidth + sideRibRadius; + + // We need a properly shaped Xtru to accomodate the ribs avoiding + // overlaps with the HalfStave cooling tubes + xtru[0] = sOBSFrameULegXPos - ulegHalfLen; + ytru[0] = -(triangleHeight / 2 + baseRibRadius); + xtru[1] = xtru[0]; + ytru[1] = ytru[0] - ulegHigh1; + xtru[2] = xtru[1] + ulegThick; + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[0] - ulegThick; + xtru[7] = sOBSFrameULegXPos + ulegHalfLen; + ytru[7] = ytru[0]; + xtru[6] = xtru[7]; + ytru[6] = ytru[1]; + xtru[5] = xtru[6] - ulegThick; + ytru[5] = ytru[6]; + xtru[4] = xtru[5]; + ytru[4] = ytru[3]; + xtru[8] = xlen; + ytru[8] = ytru[7]; + xtru[9] = xtru[8]; + ytru[9] = 0.9 * ytru[8]; + xtru[10] = 0.3 * xtru[8]; + ytru[10] = triangleHeight / 2; + for (Int_t i = 0; i < 11; i++) { // Reflect on the X negative side + xtru[i + 11] = -xtru[10 - i]; + ytru[i + 11] = ytru[10 - i]; + } + ytru[15] = ytru[0] - ulegHigh2; // U-legs on negative X are longer + ytru[16] = ytru[15]; + ytru[19] = ytru[15]; + ytru[20] = ytru[15]; + + // The space frame single units + // We need two units because the base ribs are alternately oriented + // The next-to-end units are slightly different + TGeoXtru* frameUnit = new TGeoXtru(2); + frameUnit->DefinePolygon(22, xtru, ytru); + frameUnit->DefineSection(0, -unitlen / 2); + frameUnit->DefineSection(1, unitlen / 2); + + TGeoXtru* next2EndUnit = new TGeoXtru(2); + next2EndUnit->DefinePolygon(22, xtru, ytru); + next2EndUnit->DefineSection(0, -unitlen / 2); + next2EndUnit->DefineSection(1, unitlen / 2); + + // The end units have no U-legs, but they contain the end-stave connectors + // so we build a CompositeShape using two Xtru's + xtru[0] = xlen; + ytru[0] = -(triangleHeight / 2 + baseRibRadius); + xtru[1] = xtru[0]; + ytru[1] = 0.9 * ytru[0]; + xtru[2] = 0.3 * xtru[0]; + ytru[2] = triangleHeight / 2; + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* endUnitBody = new TGeoXtru(2); + endUnitBody->SetName("endunitbody"); + endUnitBody->DefinePolygon(6, xtru, ytru); + endUnitBody->DefineSection(0, -unitlen / 2); + endUnitBody->DefineSection(1, 0.8 * unitlen / 2); + + xtru[2] = 0.25 * (3 * xtru[1] + xtru[2]); + ytru[2] = 0.25 * (3 * ytru[1] + ytru[2]); + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* endUnitBodyLow = new TGeoXtru(2); + endUnitBodyLow->SetName("endunitbodylow"); + endUnitBodyLow->DefinePolygon(6, xtru, ytru); + endUnitBodyLow->DefineSection(0, 0.8 * unitlen / 2); + endUnitBodyLow->DefineSection(1, unitlen / 2); + + // (See createOBSpaceFrameConnector lower down for details) + xtru[0] = sOBSFrameConnWidth / 2.; + ytru[0] = 0.; + xtru[1] = xtru[0]; + ytru[1] = sOBSFrameConnInsHei; + xtru[2] = xtru[1] - sOBSFrameConnTotHei + sOBSFrameConnInsHei; + ytru[2] = sOBSFrameConnTotHei; + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* endUnitConn = new TGeoXtru(2); + endUnitConn->SetName("endunitconn"); + endUnitConn->DefinePolygon(6, xtru, ytru); + endUnitConn->DefineSection(0, 0.); + endUnitConn->DefineSection(1, sOBSFrameConnTopLen); + + // We create a fake side V to have its dimensions, needed for + // the creation of the end unit container + TGeoXtru* vside = + createStaveSide("fakeCornerSide", unitlen / 2., alphaRad, beta, staveLb, staveHb, kFALSE); + + ypos = -triangleHeight / 2 + vside->GetY(3); + TGeoTranslation* endUnitConnTrans = new TGeoTranslation("endunitconntrans", 0, ypos, unitlen / 2); + endUnitConnTrans->RegisterYourself(); + + TGeoCompositeShape* endUnit = new TGeoCompositeShape("endunitbody+endunitbodylow+endunitconn:endunitconntrans"); + endUnit->SetName("endunitcontainer"); // Will be used when create spaceframe + + // The air containers + TGeoVolume* unitVol[2]; + unitVol[0] = new TGeoVolume("SpaceFrameUnit0", frameUnit, medAir); + unitVol[1] = new TGeoVolume("SpaceFrameUnit1", frameUnit, medAir); + unitVol[0]->SetVisibility(kFALSE); + unitVol[1]->SetVisibility(kFALSE); + + TGeoVolume* next2EndVol[2]; + next2EndVol[0] = new TGeoVolume("SpaceFrameNext2EndUnit0", next2EndUnit, medAir); + next2EndVol[1] = new TGeoVolume("SpaceFrameNext2EndUnit1", next2EndUnit, medAir); + next2EndVol[0]->SetVisibility(kFALSE); + next2EndVol[1]->SetVisibility(kFALSE); + + TGeoVolume* endVol[2]; + endVol[0] = new TGeoVolume("SpaceFrameEndUnit0", endUnit, medAir); + endVol[1] = new TGeoVolume("SpaceFrameEndUnit1", endUnit, medAir); + endVol[0]->SetVisibility(kFALSE); + endVol[1]->SetVisibility(kFALSE); + + // The actual volumes + + //--- The top V of the Carbon Fiber Stave (segment) + TGeoXtru* cfStavTop = + createStaveSide("CFstavTopCornerVolshape", unitlen / 2., alphaRad, beta, staveLa, staveHa, kTRUE); + + TGeoVolume* cfStavTopVol = new TGeoVolume("CFstavTopCornerVol", cfStavTop, medCarbon); + cfStavTopVol->SetLineColor(35); + + unitVol[0]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + unitVol[1]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + next2EndVol[0]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + next2EndVol[1]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + zlen = topVFactorEU * unitlen; + TGeoXtru* cfStavTopEU = + createStaveSide("CFstavTopCornerEUVolshape", zlen / 2., alphaRad, beta, staveLa, staveHa, kTRUE); + + TGeoVolume* cfStavTopVolEU = new TGeoVolume("CFstavTopCornerEUVol", cfStavTopEU, medCarbon); + cfStavTopVol->SetLineColor(35); + + zpos = endUnitBody->GetDZ() - zlen / 2.; + + endVol[0]->AddNode(cfStavTopVolEU, 1, new TGeoTranslation(0, triangleHeight / 2, -zpos)); + + endVol[1]->AddNode(cfStavTopVolEU, 1, new TGeoTranslation(0, triangleHeight / 2, -zpos)); + + //--- The two side V's + TGeoXtru* cfStavSide = + createStaveSide("CFstavSideCornerVolshape", unitlen / 2., alphaRad, beta, staveLb, staveHb, kFALSE); + + TGeoVolume* cfStavSideVol = new TGeoVolume("CFstavSideCornerVol", cfStavSide, medCarbon); + cfStavSideVol->SetLineColor(35); + + unitVol[0]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + unitVol[0]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + unitVol[1]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + unitVol[1]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + next2EndVol[0]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + next2EndVol[0]->AddNode( + cfStavSideVol, 2, new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + next2EndVol[1]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + next2EndVol[1]->AddNode( + cfStavSideVol, 2, new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + endVol[0]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + endVol[0]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + endVol[1]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + endVol[1]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + //--- The beams + // Ribs on the sides + Double_t ribZProj = triangleHeight / TMath::Tan(sidePhiRad); + Double_t sideRibLen = + TMath::Sqrt(ribZProj * ribZProj + triangleHeight * triangleHeight + halfFrameWidth * halfFrameWidth); + + TGeoTubeSeg* sideRib = new TGeoTubeSeg(0, sideRibRadius, sideRibLen / 2, 0, 180); + TGeoVolume* sideRibVol = new TGeoVolume("CFstavSideBeamVol", sideRib, medCarbon); + sideRibVol->SetLineColor(35); + + TGeoCombiTrans* sideTransf[4]; + xpos = halfFrameWidth / 2 + 0.8 * staveHa * TMath::Cos(alphaRad / 2); + ypos = -sideRibRadius / 2; + zpos = unitlen / 4; + + sideTransf[0] = new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 90 - alphaDeg, -sidePhiDeg, -90)); + sideTransf[1] = new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90 - alphaDeg, sidePhiDeg, -90)); + sideTransf[2] = new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 90 + alphaDeg, sidePhiDeg, -90)); + sideTransf[3] = new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90 + alphaDeg, -sidePhiDeg, -90)); + + unitVol[0]->AddNode(sideRibVol, 1, sideTransf[0]); + unitVol[0]->AddNode(sideRibVol, 2, sideTransf[1]); + unitVol[0]->AddNode(sideRibVol, 3, sideTransf[2]); + unitVol[0]->AddNode(sideRibVol, 4, sideTransf[3]); + + unitVol[1]->AddNode(sideRibVol, 1, sideTransf[0]); + unitVol[1]->AddNode(sideRibVol, 2, sideTransf[1]); + unitVol[1]->AddNode(sideRibVol, 3, sideTransf[2]); + unitVol[1]->AddNode(sideRibVol, 4, sideTransf[3]); + + next2EndVol[0]->AddNode(sideRibVol, 1, sideTransf[0]); + next2EndVol[0]->AddNode(sideRibVol, 2, sideTransf[1]); + next2EndVol[0]->AddNode(sideRibVol, 3, sideTransf[2]); + next2EndVol[0]->AddNode(sideRibVol, 4, sideTransf[3]); + + next2EndVol[1]->AddNode(sideRibVol, 1, sideTransf[0]); + next2EndVol[1]->AddNode(sideRibVol, 2, sideTransf[1]); + next2EndVol[1]->AddNode(sideRibVol, 3, sideTransf[2]); + next2EndVol[1]->AddNode(sideRibVol, 4, sideTransf[3]); + + endVol[0]->AddNode(sideRibVol, 1, sideTransf[0]); + endVol[0]->AddNode(sideRibVol, 2, sideTransf[1]); + endVol[0]->AddNode(sideRibVol, 3, sideTransf[2]); + endVol[0]->AddNode(sideRibVol, 4, sideTransf[3]); + + endVol[1]->AddNode(sideRibVol, 1, sideTransf[0]); + endVol[1]->AddNode(sideRibVol, 2, sideTransf[1]); + endVol[1]->AddNode(sideRibVol, 3, sideTransf[2]); + endVol[1]->AddNode(sideRibVol, 4, sideTransf[3]); + + // Ribs on the bottom + // Rib1 are the inclined ones, Rib2 the straight ones + Double_t baseRibLen = 0.98 * 2 * halfFrameWidth / TMath::Sin(basePhiRad); + + TGeoTubeSeg* baseRib1 = new TGeoTubeSeg(0, baseRibRadius, baseRibLen / 2, 0, 180); + TGeoVolume* baseRib1Vol = new TGeoVolume("CFstavBaseBeam1Vol", baseRib1, medCarbon); + baseRib1Vol->SetLineColor(35); + + TGeoTubeSeg* baseRib2 = new TGeoTubeSeg(0, baseRibRadius, halfFrameWidth, 0, 90); + TGeoVolume* baseRib2Vol = new TGeoVolume("CFstavBaseBeam2Vol", baseRib2, medCarbon); + baseRib2Vol->SetLineColor(35); + + TGeoTubeSeg* baseEndRib = new TGeoTubeSeg(0, baseRibRadius, halfFrameWidth, 0, 180); + TGeoVolume* baseEndRibVol = new TGeoVolume("CFstavBaseEndBeamVol", baseEndRib, medCarbon); + baseEndRibVol->SetLineColor(35); + + TGeoCombiTrans* baseTransf[6]; + ypos = triangleHeight / 2; + zpos = unitlen / 2; + + baseTransf[0] = new TGeoCombiTrans("", 0, -ypos, -zpos, new TGeoRotation("", 90, 90, 90)); + baseTransf[1] = new TGeoCombiTrans("", 0, -ypos, zpos, new TGeoRotation("", -90, 90, -90)); + baseTransf[2] = new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", -90, basePhiDeg, -90)); + baseTransf[3] = new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", -90, -basePhiDeg, -90)); + zpos -= baseEndRib->GetRmax(); + baseTransf[4] = new TGeoCombiTrans("", 0, -ypos, -zpos, new TGeoRotation("", 90, 90, 90)); + baseTransf[5] = new TGeoCombiTrans("", 0, -ypos, zpos, new TGeoRotation("", 90, 90, 90)); + + unitVol[0]->AddNode(baseRib2Vol, 1, baseTransf[0]); + unitVol[0]->AddNode(baseRib2Vol, 2, baseTransf[1]); + unitVol[0]->AddNode(baseRib1Vol, 1, baseTransf[2]); + + unitVol[1]->AddNode(baseRib2Vol, 1, baseTransf[0]); + unitVol[1]->AddNode(baseRib2Vol, 2, baseTransf[1]); + unitVol[1]->AddNode(baseRib1Vol, 1, baseTransf[3]); + + next2EndVol[0]->AddNode(baseRib2Vol, 1, baseTransf[0]); + next2EndVol[0]->AddNode(baseRib2Vol, 2, baseTransf[1]); + next2EndVol[0]->AddNode(baseRib1Vol, 1, baseTransf[3]); + + next2EndVol[1]->AddNode(baseRib2Vol, 1, baseTransf[0]); + next2EndVol[1]->AddNode(baseRib2Vol, 2, baseTransf[1]); + next2EndVol[1]->AddNode(baseRib1Vol, 1, baseTransf[3]); + + endVol[0]->AddNode(baseEndRibVol, 1, baseTransf[4]); + endVol[0]->AddNode(baseRib2Vol, 1, baseTransf[1]); + endVol[0]->AddNode(baseRib1Vol, 1, baseTransf[2]); + + endVol[1]->AddNode(baseEndRibVol, 1, baseTransf[5]); + endVol[1]->AddNode(baseRib2Vol, 1, baseTransf[0]); + endVol[1]->AddNode(baseRib1Vol, 1, baseTransf[2]); + + // The Space Frame connectors + ypos = -triangleHeight / 2 + cfStavSide->GetY(3); + zpos = unitlen / 2; + createOBSpaceFrameConnector(endVol[0], ypos, zpos, kFALSE); // Side C + createOBSpaceFrameConnector(endVol[1], ypos, zpos, kTRUE); // Side A + + // U-Legs + // The shorter + xtru[0] = ulegHalfLen; + ytru[0] = 0; + xtru[1] = xtru[0]; + ytru[1] = -ulegHigh1; + xtru[2] = xtru[1] - ulegThick; + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[0] - ulegThick; + for (Int_t i = 0; i < 4; i++) { // Reflect on the X negative side + xtru[i + 4] = -xtru[3 - i]; + ytru[i + 4] = ytru[3 - i]; + } + + TGeoXtru* uleg1full = new TGeoXtru(2); // This will go in the next end units + uleg1full->DefinePolygon(8, xtru, ytru); + uleg1full->DefineSection(0, -ulegHalfWidth); + uleg1full->DefineSection(1, ulegHalfWidth); + + TGeoXtru* uleg1half = new TGeoXtru(2); // This will go in the middle unitys + uleg1half->DefinePolygon(8, xtru, ytru); + uleg1half->DefineSection(0, -ulegHalfWidth / 2); + uleg1half->DefineSection(1, ulegHalfWidth / 2); + + TGeoVolume* uleg1fullVol = new TGeoVolume("CFstavULeg1FullVol", uleg1full, medF6151B05M); + uleg1fullVol->SetLineColor(35); + + TGeoVolume* uleg1halfVol = new TGeoVolume("CFstavULeg1HalfVol", uleg1half, medF6151B05M); + uleg1halfVol->SetLineColor(35); + + // The longer + ytru[1] = -ulegHigh2; + ytru[2] = -ulegHigh2; + ytru[5] = -ulegHigh2; + ytru[6] = -ulegHigh2; + + TGeoXtru* uleg2full = new TGeoXtru(2); // This will go in the next end units + uleg2full->DefinePolygon(8, xtru, ytru); + uleg2full->DefineSection(0, -ulegHalfWidth); + uleg2full->DefineSection(1, ulegHalfWidth); + + TGeoXtru* uleg2half = new TGeoXtru(2); // This will go in the middle unitys + uleg2half->DefinePolygon(8, xtru, ytru); + uleg2half->DefineSection(0, -ulegHalfWidth / 2); + uleg2half->DefineSection(1, ulegHalfWidth / 2); + + TGeoVolume* uleg2fullVol = new TGeoVolume("CFstavULeg2FullVol", uleg2full, medF6151B05M); + uleg2fullVol->SetLineColor(35); + + TGeoVolume* uleg2halfVol = new TGeoVolume("CFstavULeg2HalfVol", uleg2half, medF6151B05M); + uleg2halfVol->SetLineColor(35); + + xpos = sOBSFrameULegXPos; + ypos = triangleHeight / 2 + baseRibRadius; + zpos = unitlen / 2 - uleg1half->GetZ(1); + + unitVol[0]->AddNode(uleg1halfVol, 1, // Shorter on +X + new TGeoTranslation(xpos, -ypos, -zpos)); + unitVol[0]->AddNode(uleg1halfVol, 2, new TGeoTranslation(xpos, -ypos, zpos)); + + unitVol[1]->AddNode(uleg1halfVol, 1, new TGeoTranslation(xpos, -ypos, -zpos)); + unitVol[1]->AddNode(uleg1halfVol, 2, new TGeoTranslation(xpos, -ypos, zpos)); + + unitVol[0]->AddNode(uleg2halfVol, 1, // Longer on -X + new TGeoTranslation(-xpos, -ypos, -zpos)); + unitVol[0]->AddNode(uleg2halfVol, 2, new TGeoTranslation(-xpos, -ypos, zpos)); + + unitVol[1]->AddNode(uleg2halfVol, 1, new TGeoTranslation(-xpos, -ypos, -zpos)); + unitVol[1]->AddNode(uleg2halfVol, 2, new TGeoTranslation(-xpos, -ypos, zpos)); + + next2EndVol[0]->AddNode(uleg1halfVol, 1, new TGeoTranslation(xpos, -ypos, zpos)); + next2EndVol[0]->AddNode(uleg2halfVol, 1, new TGeoTranslation(-xpos, -ypos, zpos)); + + next2EndVol[1]->AddNode(uleg1halfVol, 1, new TGeoTranslation(xpos, -ypos, -zpos)); + next2EndVol[1]->AddNode(uleg2halfVol, 1, new TGeoTranslation(-xpos, -ypos, -zpos)); + + zpos = unitlen / 2 - uleg1full->GetZ(1); + next2EndVol[0]->AddNode(uleg1fullVol, 1, new TGeoTranslation(xpos, -ypos, -zpos)); + next2EndVol[0]->AddNode(uleg2fullVol, 1, new TGeoTranslation(-xpos, -ypos, -zpos)); + + next2EndVol[1]->AddNode(uleg1fullVol, 1, new TGeoTranslation(xpos, -ypos, zpos)); + next2EndVol[1]->AddNode(uleg2fullVol, 1, new TGeoTranslation(-xpos, -ypos, zpos)); + + // Done + return; +} + +void V3Layer::createOBSpaceFrameConnector(TGeoVolume* mother, const Double_t ymot, const Double_t zmot, const Bool_t sideA, const TGeoManager* mgr) +{ + // + // Creates the OB Space Frame Connectors + // (ALIITSUP0070+ALIITSUP0069) + // + // Input: + // mother : the SF unit volume to contain the connector + // ymot : the Y position of the connector in the mother volume + // zmot : the Z position of the connector in the mother volume + // sideA : true for Side A, false for Side C + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 09 Sep 2019 M. Sitta + + // Materials defined in AliITSUv2 + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + + // Local parameters + TString connName, compoShape; + + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + Double_t xtru[6], ytru[6]; + + // The external (higher) part: a Xtru + ylen = sOBSFrameConnTotHei - sOBSFrameConnInsHei; + + xtru[0] = sOBSFrameConnWidth / 2.; + ytru[0] = 0.; + xtru[1] = xtru[0]; + ytru[1] = sOBSFrameConnInsHei; + xtru[2] = xtru[1] - ylen; // Because side is at 45' so dx = dy + ytru[2] = sOBSFrameConnTotHei; + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* topConn = new TGeoXtru(2); + topConn->SetName("connectorTop"); + topConn->DefinePolygon(6, xtru, ytru); + topConn->DefineSection(0, 0.); + topConn->DefineSection(1, sOBSFrameConnTopLen); + + // The insert: a Xtru + zlen = sOBSFrameConnTotLen - sOBSFrameConnTopLen; + + xtru[0] = sOBSFrameConnInsBase / 2.; + ytru[0] = 0.; + xtru[1] = sOBSFrameConnInsWide / 2.; + ytru[1] = sOBSFrameConnInsHei; + xtru[2] = -xtru[1]; + ytru[2] = ytru[1]; + xtru[3] = -xtru[0]; + ytru[3] = ytru[0]; + + TGeoXtru* insConn = new TGeoXtru(2); + insConn->SetName("connectorIns"); + insConn->DefinePolygon(4, xtru, ytru); + insConn->DefineSection(0, -zlen); + insConn->DefineSection(1, 0.); + + // The holes in the external (higher) part: Tube's and a BBox + TGeoTube* topHoleR = new TGeoTube("topholer", 0., sOBSFrameConnTopHoleD / 2., 1.1 * sOBSFrameConnTotHei); + + xpos = sOBSFrConnTopHoleXDist / 2.; + ypos = sOBSFrameConnTotHei / 2.; + zpos = sOBSFrameConnTopLen - sOBSFrameConnHoleZPos; + TGeoCombiTrans* topHoleR1Trans = new TGeoCombiTrans("topholer1tr", xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topHoleR1Trans->RegisterYourself(); + + TGeoCombiTrans* topHoleR2Trans = new TGeoCombiTrans("topholer2tr", -xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topHoleR2Trans->RegisterYourself(); + + xpos = sOBSFrConnCHoleXDist / 2.; + zpos = sOBSFrameConnTopLen - sOBSFrameConnCHoleZPos; + TGeoCombiTrans* topCHoleR1Trans = new TGeoCombiTrans("topcholer1tr", xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topCHoleR1Trans->RegisterYourself(); + + TGeoCombiTrans* topCHoleR2Trans = new TGeoCombiTrans("topcholer2tr", -xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topCHoleR2Trans->RegisterYourself(); + + TGeoBBox* topAHole = new TGeoBBox("topahole", sOBSFrameConnAHoleWid / 2., sOBSFrameConnTotHei, sOBSFrameConnAHoleLen / 2.); + + zpos = sOBSFrameConnTopLen - sOBSFrameConnHoleZPos; + TGeoTranslation* topAHoleTrans = new TGeoTranslation("topaholetr", 0, ypos, zpos); + topAHoleTrans->RegisterYourself(); + + TGeoTube* topCHole = new TGeoTube("topchole", 0., sOBSFrConnCTopHoleD / 2., sOBSFrameConnTotHei); + + TGeoCombiTrans* topCHoleTrans = new TGeoCombiTrans("topcholetr", 0, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topCHoleTrans->RegisterYourself(); + + TGeoTube* topASide = new TGeoTube("topaside", 0., sOBSFrConnASideHoleD / 2., 1.1 * sOBSFrConnASideHoleL); + + zpos = sOBSFrameConnTopLen + topASide->GetDz() - sOBSFrConnASideHoleL; + TGeoTranslation* topASideTrans = new TGeoTranslation("topasidetr", 0, sOBSFrConnASideHoleY, zpos); + topASideTrans->RegisterYourself(); + + // The holes in the insert: a Tube + TGeoTube* insHole = new TGeoTube("inshole", 0., sOBSFrameConnInsHoleD / 2., sOBSFrameConnInsHei); + + xpos = sOBSFrameConnInsHoleX / 2.; + ypos = sOBSFrameConnInsHei / 2.; + zpos = sOBSFrameConnTopLen - sOBSFrameConnHoleZPos - sOBSFrameConnHoleZDist; + TGeoCombiTrans* insHole1Trans = new TGeoCombiTrans("inshole1tr", xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + insHole1Trans->RegisterYourself(); + + TGeoCombiTrans* insHole2Trans = new TGeoCombiTrans("inshole2tr", -xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + insHole2Trans->RegisterYourself(); + + // The connector: a CompositeShape + if (sideA) { + connName = "OBSFConnectorA"; + compoShape = "(connectorTop-topholer:topholer2tr-topholer:topholer1tr-topahole:topaholetr-topaside:topasidetr)+(connectorIns-inshole:inshole1tr-inshole:inshole2tr)"; + } else { + connName = "OBSFConnectorC"; + compoShape = "(connectorTop-topholer:topholer2tr-topholer:topholer1tr-topholer:topcholer1tr-topholer:topcholer2tr-topchole:topcholetr)+(connectorIns-inshole:inshole1tr-inshole:inshole2tr)"; + } + + TGeoCompositeShape* obsfConnSh = new TGeoCompositeShape(compoShape.Data()); + + TGeoVolume* obsfConnVol = new TGeoVolume(connName, obsfConnSh, medPEEK); + + // Finally put the connector into its mother volume + mother->AddNode(obsfConnVol, 1, new TGeoTranslation(0, ymot, zmot)); +} + +TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) +{ + // + // Creates the OB Module: HIC + FPC + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the module as a TGeoVolume + // + // Created: 18 Dec 2013 M. Sitta, A. Barbano + // Updated: 26 Feb 2014 M. Sitta + // Updated: 12 Nov 2014 M. Sitta Model2 is w/o Carbon Plate and Glue + // and Cu instead of Al + // Updated: 20 Jul 2017 M. Sitta O2 version + // Updated: 30 Jul 2018 M. Sitta Updated geometry + // + + const Int_t nameLen = 30; + char chipName[nameLen], sensName[nameLen], volName[nameLen]; + + Double_t xGap = sOBChipXGap; + Double_t zGap = sOBChipZGap; + + Double_t xchip, ychip, zchip; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + Bool_t dummyChip; + + // First create all needed shapes + + // For material budget studies + if (mBuildLevel < 7) { + dummyChip = kFALSE; // will be made of Si + } else { + dummyChip = kTRUE; // will be made of Air + } + + // The chip (the same as for IB) + snprintf(chipName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSChipPattern(), mLayerNumber); + snprintf(sensName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSSensorPattern(), mLayerNumber); + + ylen = 0.5 * sOBChipThickness; + + TGeoVolume* chipVol = AlpideChip::createChip(ylen, mSensorThickness / 2, chipName, sensName, dummyChip); + + xchip = (static_cast<TGeoBBox*>(chipVol->GetShape()))->GetDX(); + ychip = (static_cast<TGeoBBox*>(chipVol->GetShape()))->GetDY(); + zchip = (static_cast<TGeoBBox*>(chipVol->GetShape()))->GetDZ(); + + mOBModuleZLength = 2 * zchip * sOBChipsPerRow + (sOBChipsPerRow - 1) * sOBChipZGap; + + zlen = mOBModuleZLength / 2; + + // The glue + xlen = (4 * xchip + xGap) / 2; + ylen = sOBGlueFPCThick / 2; + TGeoBBox* glueFPC = new TGeoBBox("GlueFPC", xlen, ylen, zlen); + + ylen = sOBGlueColdPlThick / 2; + TGeoBBox* glueCP = new TGeoBBox("GlueCP", xlen, ylen, zlen); + + // The FPC cables + xlen = sOBFlexCableXWidth / 2; + ylen = sOBFlexCableKapThick / 2; + TGeoBBox* flexKap = new TGeoBBox("MidFlexKap", xlen, ylen, zlen); + + TGeoVolume* cuGndCableVol = createOBFPCCuGnd(zlen); + TGeoVolume* cuSignalCableVol = createOBFPCCuSig(zlen); + + // The module + Double_t ygnd = (static_cast<TGeoBBox*>(cuGndCableVol->GetShape()))->GetDY(); + Double_t ysig = (static_cast<TGeoBBox*>(cuSignalCableVol->GetShape()))->GetDY(); + + xlen = (static_cast<TGeoBBox*>(cuGndCableVol->GetShape()))->GetDX(); + ylen = glueCP->GetDY() + ychip + glueFPC->GetDY() + ysig + flexKap->GetDY() + ygnd; + TGeoBBox* module = new TGeoBBox("OBModule", xlen, ylen, zlen); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAir = mgr->GetMedium("TRK_AIR$"); + TGeoMedium* medGlue = mgr->GetMedium("TRK_GLUE$"); + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + + TGeoVolume* glueFPCVol = new TGeoVolume("GlueFPCVol", glueFPC, medGlue); + glueFPCVol->SetLineColor(kBlack); + glueFPCVol->SetFillColor(glueFPCVol->GetLineColor()); + glueFPCVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* glueCPVol = new TGeoVolume("GlueColdPlVol", glueCP, medGlue); + glueCPVol->SetLineColor(kBlack); + glueCPVol->SetFillColor(glueCPVol->GetLineColor()); + glueCPVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* flexKapVol = new TGeoVolume("FPCMidKapVol", flexKap, medKapton); + flexKapVol->SetLineColor(kGreen); + flexKapVol->SetFillColor(flexKapVol->GetLineColor()); + flexKapVol->SetFillStyle(4000); // 0% transparent + + snprintf(volName, nameLen, "%s%d", o2::trk::GeometryTGeo::getITSModulePattern(), mLayerNumber); + TGeoVolume* modVol = new TGeoVolume(volName, module, medAir); + modVol->SetVisibility(kTRUE); + + // Now build up the module + ypos = -module->GetDY() + glueCP->GetDY(); + + if (mBuildLevel < 3) { // Glue + modVol->AddNode(glueCPVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + xpos = xchip + xGap / 2; + ypos += (ychip + glueCP->GetDY()); + // We use two loops here to have the same chip numbering as in HW + // X ^ | 6| 5| 4| 3| 2| 1| 0| + // ----|--------------------------> Z + // | | 7| 8| 9|10|11|12|13| + // + for (Int_t k = 0; k < sOBChipsPerRow; k++) // put first 7 chip row + { + zpos = module->GetDZ() - zchip - k * (2 * zchip + zGap); + modVol->AddNode(chipVol, k, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 180, 180))); + mHierarchy[kChip] += 1; + } + + for (Int_t k = 0; k < sOBChipsPerRow; k++) // put second 7 chip row + { + zpos = -module->GetDZ() + zchip + k * (2 * zchip + zGap); + modVol->AddNode(chipVol, k + sOBChipsPerRow, new TGeoTranslation(-xpos, ypos, zpos)); + mHierarchy[kChip] += 1; + } + + ypos += (ychip + glueFPC->GetDY()); + if (mBuildLevel < 3) { // Glue + modVol->AddNode(glueFPCVol, 1, new TGeoTranslation(0, ypos, 0)); + } + ypos += glueFPC->GetDY(); + + if (mBuildLevel < 5) { // Kapton + ypos += ysig; + modVol->AddNode(cuSignalCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (ysig + flexKap->GetDY()); + modVol->AddNode(flexKapVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (flexKap->GetDY() + ygnd); + modVol->AddNode(cuGndCableVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + // Done, return the module + return modVol; +} + +TGeoVolume* V3Layer::createOBFPCCuGnd(const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the OB FPC Copper Ground cable + // + // Input: + // zcable : the cable half Z length + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the FPC cable as a TGeoVolume + // + // Created: 30 Jul 2018 Mario Sitta + // + + Double_t xcable, ytot, ypos; + + // First create all needed shapes + xcable = sOBFlexCableXWidth / 2; + ytot = sOBFPCSoldMaskThick + sOBFPCCopperThick; + TGeoBBox* soldmask = new TGeoBBox(xcable, ytot / 2, zcable); + xcable *= sOBFPCCuAreaFracGnd; + TGeoBBox* copper = new TGeoBBox(xcable, sOBFPCCopperThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medCopper = mgr->GetMedium("TRK_COPPER$"); + + TGeoVolume* soldmaskVol = new TGeoVolume("FPCGndSolderMask", soldmask, medKapton); + soldmaskVol->SetLineColor(kBlue); + soldmaskVol->SetFillColor(kBlue); + + TGeoVolume* copperVol = new TGeoVolume("FPCCopperGround", copper, medCopper); + copperVol->SetLineColor(kCyan); + copperVol->SetFillColor(kCyan); + + ypos = -soldmask->GetDY() + copper->GetDY(); + if (mBuildLevel < 1) { // Copper + soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + return soldmaskVol; +} + +TGeoVolume* V3Layer::createOBFPCCuSig(const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the OB FPC Copper Signal cable + // + // Input: + // zcable : the cable half Z length + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the FPC cable as a TGeoVolume + // + // Created: 30 Jul 2018 Mario Sitta + // + + Double_t xcable, ytot, ypos; + + // First create all needed shapes + xcable = sOBFlexCableXWidth / 2; + ytot = sOBFPCSoldMaskThick + sOBFPCCopperThick; + TGeoBBox* soldmask = new TGeoBBox(xcable, ytot / 2, zcable); + xcable *= sOBFPCCuAreaFracSig; + TGeoBBox* copper = new TGeoBBox(xcable, sOBFPCCopperThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("TRK_KAPTON(POLYCH2)$"); + TGeoMedium* medCopper = mgr->GetMedium("TRK_COPPER$"); + + TGeoVolume* soldmaskVol = new TGeoVolume("FPCSigSolderMask", soldmask, medKapton); + soldmaskVol->SetLineColor(kBlue); + soldmaskVol->SetFillColor(kBlue); + + TGeoVolume* copperVol = new TGeoVolume("FPCCopperSignal", copper, medCopper); + copperVol->SetLineColor(kCyan); + copperVol->SetFillColor(kCyan); + + ypos = soldmask->GetDY() - copper->GetDY(); + if (mBuildLevel < 1) { // Copper + soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + return soldmaskVol; +} + +Double_t V3Layer::getGammaConversionRodDiam() +{ + // + // Gets the diameter of the gamma conversion rods, if defined + // + // + // Input: + // + // Output: + // + // Return: + // the diameter of the gamma conversion rods for this layer + // + // Created: 26 Oct 2016 Mario Sitta + // + + if (!mAddGammaConv) { + LOG(WARNING) << "Gamma Conversion rods not defined for this layer"; + } + return mGammaConvDiam; +} + +Double_t V3Layer::getGammaConversionRodXPos() +{ + // + // Gets the X position of the gamma conversion rods, if defined + // + // + // Input: + // + // Output: + // + // Return: + // the X position of the gamma conversion rods for this layer + // in the Half Stave reference system + // + // Created: 26 Oct 2016 Mario Sitta + // + + if (!mAddGammaConv) { + LOG(WARNING) << "Gamma Conversion rods not defined for this layer"; + } + return mGammaConvXPos; +} + +Double_t V3Layer::radiusOmTurboContainer() +{ + Double_t rr, delta, z, lstav, rstav; + + if (mChipThickness > 89.) { // Very big angle: avoid overflows since surely + return -1; // the radius from lower vertex is the right value + } + + rstav = mLayerRadius + 0.5 * mChipThickness; + delta = (0.5 * mChipThickness) / cosD(mStaveTilt); + z = (0.5 * mChipThickness) * tanD(mStaveTilt); + + rr = rstav - delta; + lstav = (0.5 * mStaveWidth) - z; + + if ((rr * sinD(mStaveTilt) < lstav)) { + return (rr * cosD(mStaveTilt)); + } else { + return -1; + } +} + +void V3Layer::setNumberOfUnits(Int_t u) +{ + if (mLayerNumber < mNumberOfInnerLayers) { + mNumberOfChips = u; + } else { + mNumberOfModules = u; + mNumberOfChips = sOBChipsPerRow; + } +} + +void V3Layer::setStaveTilt(const Double_t t) +{ + if (mIsTurbo) { + mStaveTilt = t; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +void V3Layer::setStaveWidth(const Double_t w) +{ + if (mIsTurbo) { + mStaveWidth = w; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +TGeoXtru* V3Layer::createStaveSide(const char* name, Double_t dz, Double_t alpha, Double_t beta, Double_t L, Double_t H, + Bool_t top) +{ + // + // Creates the V-shaped sides of the OB space frame + // (from a similar method with same name and function + // in AliITSv11GeometrySDD class by L.Gaudichet) + // + // Updated: 15 Dec 2014 Mario Sitta Rewritten using Xtru + // Updated: 09 Jan 2015 Mario Sitta Rewritten again using different + // aperture angles (info by C.Gargiulo) + // Updated: 21 Jul 2017 Mario Sitta O2 version + // + + // Create the V shape corner of CF stave + + const Int_t nv = 6; + Double_t xv[nv], yv[nv]; + + TGeoXtru* cfStavSide = new TGeoXtru(2); + cfStavSide->SetName(name); + + Double_t theta = TMath::PiOver2() - beta; + Double_t gamma = beta - alpha; + // Points must be in clockwise order + if (top) { // TOP - vertices not in order + xv[3] = 0; + yv[3] = 0; + xv[2] = L * TMath::Sin(alpha); + yv[2] = -L * TMath::Cos(alpha); + xv[1] = xv[2] - H * TMath::Cos(alpha); + yv[1] = yv[2] - H * TMath::Sin(alpha); + xv[0] = 0; + yv[0] = yv[1] + TMath::Tan(theta) * xv[1]; + xv[4] = -xv[2]; // Reflect + yv[4] = yv[2]; + xv[5] = -xv[1]; + yv[5] = yv[1]; + } else { // SIDE + Double_t m = -TMath::Tan(alpha), n = TMath::Tan(gamma); + xv[0] = 0; + yv[0] = 0; + xv[1] = -L * TMath::Cos(2 * alpha); + yv[1] = L * TMath::Sin(2 * alpha); + xv[2] = xv[1] - H * TMath::Sin(2 * alpha); + yv[2] = yv[1] - H * TMath::Cos(2 * alpha); + xv[4] = -L; + yv[4] = H; + xv[5] = xv[4]; + yv[5] = 0; + xv[3] = (yv[4] - n * xv[4]) / (m - n); + yv[3] = m * xv[3]; + } + + cfStavSide->DefinePolygon(nv, xv, yv); + cfStavSide->DefineSection(0, -dz); + cfStavSide->DefineSection(1, dz); + + return cfStavSide; +} + +TGeoCombiTrans* V3Layer::createCombiTrans(const char* name, Double_t dy, Double_t dz, Double_t dphi, Bool_t planeSym) +{ + TGeoTranslation t1(dy * cosD(90. + dphi), dy * sinD(90. + dphi), dz); + TGeoRotation r1("", 0., 0., dphi); + TGeoRotation r2("", 90, 180, -90 - dphi); + + TGeoCombiTrans* combiTrans1 = new TGeoCombiTrans(name); + combiTrans1->SetTranslation(t1); + if (planeSym) { + combiTrans1->SetRotation(r1); + } else { + combiTrans1->SetRotation(r2); + } + return combiTrans1; +} + +void V3Layer::addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx, Double_t dy, Double_t dz) const +{ + // Add a dx,dy,dz translation to the initial TGeoCombiTrans + const Double_t* vect = ct->GetTranslation(); + Double_t newVect[3] = {vect[0] + dx, vect[1] + dy, vect[2] + dz}; + ct->SetTranslation(newVect); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/V3Services.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V3Services.cxx new file mode 100644 index 0000000000000..f7e3362298098 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/V3Services.cxx @@ -0,0 +1,2354 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V3Services.cxx +/// \brief Implementation of the V3Services class +/// \author Mario Sitta <sitta@to.infn.it> +/// \author Parinya Namwongsa <parinya.namwongsa@cern.ch> + +#include "TRKSimulation/V3Services.h" +#include "TRKSimulation/V11Geometry.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/Detector.h" +#include "ITSMFTSimulation/AlpideChip.h" + +#include "FairLogger.h" // for LOG + +//#include <TGeoArb8.h> // for TGeoArb8 +#include <TGeoBBox.h> // for TGeoBBox +#include <TGeoCone.h> // for TGeoConeSeg, TGeoCone +#include <TGeoPcon.h> // for TGeoPcon +#include <TGeoManager.h> // for TGeoManager, gGeoManager +#include <TGeoMatrix.h> // for TGeoCombiTrans, TGeoRotation, etc +//#include <TGeoTrd1.h> // for TGeoTrd1 +#include <TGeoTube.h> // for TGeoTube, TGeoTubeSeg +#include <TGeoVolume.h> // for TGeoVolume, TGeoVolumeAssembly +#include <TGeoXtru.h> // for TGeoXtru +#include <TGeoCompositeShape.h> // for TGeoCompositeShape +#include "TMathBase.h" // for Abs +#include <TMath.h> // for Sin, RadToDeg, DegToRad, Cos, Tan, etc + +#include <cstdio> // for snprintf + +class TGeoMedium; + +using namespace TMath; +using namespace o2::trk; + +// Parameters +const Double_t V3Services::sIBWheelACZdist = 306.0 * sMm; +const Double_t V3Services::sIBCYSSFlangeCZPos = 171.5 * sMm; // Computed from different drawings +const Double_t V3Services::sOBWheelThickness = 2.0 * sMm; +const Double_t V3Services::sMBWheelsZpos = 457.0 * sMm; +const Double_t V3Services::sOBWheelsZpos = 770.0 * sMm; +const Double_t V3Services::sOBConesZpos = 798.0 * sMm; + +ClassImp(V3Services); + +#define SQ(A) (A) * (A) + +V3Services::V3Services() + : V11Geometry() +{ +} + +V3Services::~V3Services() = default; + +TGeoVolume* V3Services::createIBEndWheelsSideA(const TGeoManager* mgr) +{ + // + // Creates the Inner Barrel End Wheels on Side A + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume(Assembly) with all the wheels + // + // Created: 19 Jun 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideA"); + endWheelsVol->SetVisibility(kTRUE); + + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) { + ibEndWheelSideA(jLay, endWheelsVol, mgr); + } + + // Return the wheels + return endWheelsVol; +} + +TGeoVolume* V3Services::createIBEndWheelsSideC(const TGeoManager* mgr) +{ + // + // Creates the Inner Barrel End Wheels on Side C + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume(Assembly) with all the wheels + // + // Created: 15 May 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideC"); + endWheelsVol->SetVisibility(kTRUE); + + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) { + ibEndWheelSideC(jLay, endWheelsVol, mgr); + } + + // Return the wheels + return endWheelsVol; +} + +TGeoVolume* V3Services::createCYSSAssembly(const TGeoManager* mgr) +{ + // + // Creates the CYSS Assembly (i.e. the supporting cylinder and cone) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume(Assembly) with all the elements + // + // Created: 21 Oct 2019 Mario Sitta + // Updated: 02 Dec 2019 Mario Sitta Full cylinder implemented + // + + static const Double_t sCyssFlangeAZpos = 9.0 * sMm; + static const Double_t sCyssFlangeCZpos = 1.0 * sMm; + + Double_t zlen, zpos; + + TGeoVolume* cyssVol = new TGeoVolumeAssembly("IBCYSSAssembly"); + cyssVol->SetVisibility(kTRUE); + + TGeoVolume* cyssCylinder = ibCyssCylinder(mgr); + zlen = (static_cast<TGeoTubeSeg*>(cyssCylinder->GetShape()))->GetDz(); + zpos = sIBCYSSFlangeCZPos - sCyssFlangeCZpos - zlen; + cyssVol->AddNode(cyssCylinder, 1, new TGeoTranslation(0, 0, -zpos)); + cyssVol->AddNode(cyssCylinder, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + TGeoVolume* cyssCone = ibCyssCone(mgr); + zpos = -zpos + zlen - (static_cast<TGeoPcon*>(cyssCone->GetShape()))->GetZ(2); + cyssVol->AddNode(cyssCone, 1, new TGeoTranslation(0, 0, zpos)); + cyssVol->AddNode(cyssCone, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + TGeoVolume* cyssFlangeA = ibCyssFlangeSideA(mgr); + Int_t nZPlanes = (static_cast<TGeoPcon*>(cyssCone->GetShape()))->GetNz(); + zpos = zpos + (static_cast<TGeoPcon*>(cyssCone->GetShape()))->GetZ(nZPlanes - 1) + sCyssFlangeAZpos; + cyssVol->AddNode(cyssFlangeA, 1, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 180, 0))); + cyssVol->AddNode(cyssFlangeA, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 0, 180, 0))); + + TGeoVolume* cyssFlangeC = ibCyssFlangeSideC(mgr); + zpos = sIBCYSSFlangeCZPos; + cyssVol->AddNode(cyssFlangeC, 1, new TGeoTranslation(0, 0, -zpos)); + cyssVol->AddNode(cyssFlangeC, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + // Return the whole assembly + return cyssVol; +} + +void V3Services::createMBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Middle Barrel End Wheels on Side A + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 24 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) { + obEndWheelSideA(jLay, mother, mgr); + } +} + +void V3Services::createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Middle Barrel End Wheels on Side C + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) { + mbEndWheelSideC(jLay, mother, mgr); + } +} + +void V3Services::createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel End Wheels on Side A + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 27 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) { + obEndWheelSideA(jLay + sNumberMiddlLayers, mother, mgr); + } +} + +void V3Services::createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel End Wheels on Side C + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 27 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) { + obEndWheelSideC(jLay, mother, mgr); + } +} + +void V3Services::createOBConeSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel Cone on Side A + // + // Input: + // mother : the volume hosting the cones + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 03 Feb 2020 Mario Sitta + // + + obConeSideA(mother, mgr); + obConeTraysSideA(mother, mgr); +} + +void V3Services::createOBConeSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel Cone on Side C + // + // Input: + // mother : the volume hosting the cones + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Jan 2020 Mario Sitta + // + + obConeSideC(mother, mgr); +} + +void V3Services::ibEndWheelSideA(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side A + // for a given layer of the Inner Barrel + // (Layer 0: ALIITSSUP0183+ALIITSUP0127) + // (Layer 1: ALIITSSUP0173+ALIITSUP0124) + // (Layer 2: ALIITSSUP0139+ALIITSUP0125) + // + // Input: + // iLay : the layer number + // endWheel : the whole end wheel volume + // where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 19 Jun 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + // The Basis Cone A Side and the Reinforcement C Side are physically two + // different pieces put together. For sake of simplicity here they are + // made out of the same TGeoPcon volume. Moreover they are two halves, + // so here they are made as a single cone. + static const Double_t sConeATotalLength[3] = {191.0 * sMm, 184.0 * sMm, 177 * sMm}; + static const Double_t sConeAIntSectZlen1[3] = {40.35 * sMm, 39.0 * sMm, 36.0 * sMm}; + static const Double_t sConeAIntSectZlen2[3] = {47.0 * sMm, 44.0 * sMm, 41.0 * sMm}; + static const Double_t sConeAIntSectDmin[3] = {55.8 * sMm, 71.8 * sMm, 87.8 * sMm}; + static const Double_t sConeAIntSectDmax[3] = {57.0 * sMm, 73.0 * sMm, 89.0 * sMm}; + static const Double_t sConeAExtSectZlen1[3] = {60.0 * sMm, 47.0 * sMm, 44.0 * sMm}; + static const Double_t sConeAExtSectZlen2[3] = {66.0 * sMm, 52.0 * sMm, 50.0 * sMm}; + static const Double_t sConeAExtSectDmin[3] = {114.0 * sMm, 174.0 * sMm, 234.0 * sMm}; + static const Double_t sConeAExtSectDmax[3] = {116.0 * sMm, 176.0 * sMm, 236.0 * sMm}; + static const Double_t sConeASectThicker = 0.8 * sMm; + static const Double_t sConeAOpeningAngle[3] = {20.0, 30.0, 40.2}; // Deg + + static const Int_t sConeAWallNHoles[3] = {6, 8, 10}; + static const Double_t sConeAWallHoleD = 4.5 * sMm; + static const Double_t sConeAWallHoleZpos = 4.0 * sMm; + + static const Double_t sConeACentralHole1D = 3.0 * sMm; + static const Double_t sConeACentralHole2D = 3.4 * sMm; + static const Double_t sConeACentralHole3D = 3.0 * sMm; + static const Double_t sConeACentralHole1Z = 20.0 * sMm; + static const Double_t sConeACentralHole2Z = 30.0 * sMm; + static const Double_t sConeACentralHole3Z[3] = {177.0 * sMm, 170.0 * sMm, 163.0 * sMm}; + + // The Cone Reinforcement + static const Double_t sConeARenfDmin[3] = {54.3 * sMm, 69.85 * sMm, 85.0 * sMm}; + static const Double_t sConeARenfZlen = 2.5 * sMm; + static const Double_t sConeARenfZpos = 14.5 * sMm; + + // The Middle Ring + static const Double_t sConeAMidRingDmin[3] = {56.0 * sMm, 116.0 * sMm, 176.0 * sMm}; + static const Double_t sConeAMidRingDmax[3] = {58.0 * sMm, 118.0 * sMm, 178.0 * sMm}; + static const Double_t sConeAMidRingZlen = 42.0 * sMm; + + static const Double_t sConeAMidRingZpos[3] = {5.0 * sMm, 0.0 * sMm, 0.0 * sMm}; + + // The Ribs + static const Int_t sConeANRibs[3] = {6, 8, 10}; + static const Double_t sConeARibsZpos = 17.0 * sMm; + + // The End Wheel Steps + static const Double_t sConeAStepXdispl[3] = {4.0 * sMm, 6.5 * sMm, 8.5 * sMm}; + static const Double_t sConeAStepYdispl[3] = {24.4 * sMm, 32.1 * sMm, 39.6 * sMm}; + static const Double_t sConeAStepR[3] = {27.8 * sMm, 35.8 * sMm, 43.8 * sMm}; + + static const Double_t sConeAStepZlen = 14.0 * sMm; + + static const Double_t sConeAStepHoleXpos = 3.0 * sMm; + static const Double_t sConeAStepHoleZpos = 4.0 * sMm; + static const Double_t sConeAStepHoleZdist = 4.0 * sMm; + + static const Double_t sConeAStepHolePhi[3] = {30.0, 22.5, 18.0}; // Deg + static const Double_t sConeAStepHolePhi0[3] = {0.7, -16.2, -10.5}; // Deg + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, thick, phimin, dphi; + Double_t xpos, ypos, zpos, zref; + + // Create the whole cone (Basic + Reinforcement) as a CompositeShape + // (a single Pcon minus the holes) + TGeoPcon* coneabasis = new TGeoPcon(Form("coneabasis%d", iLay), 0, 360, 15); + + rmin = sConeAIntSectDmin[iLay] / 2; + rmax = sConeAIntSectDmax[iLay] / 2; + coneabasis->DefineSection(0, 0., rmin, rmax); + zpos = sConeARenfZpos; + coneabasis->DefineSection(1, zpos, rmin, rmax); + rmin = sConeARenfDmin[iLay] / 2; + coneabasis->DefineSection(2, zpos, rmin, rmax); + zpos += sConeARenfZlen; + coneabasis->DefineSection(3, zpos, rmin, rmax); + rmin = coneabasis->GetRmin(0); + coneabasis->DefineSection(4, zpos, rmin, rmax); + coneabasis->DefineSection(5, sConeAIntSectZlen1[iLay], rmin, rmax); + rmax += sConeASectThicker; + coneabasis->DefineSection(6, sConeAIntSectZlen1[iLay], rmin, rmax); + coneabasis->DefineSection(7, sConeAIntSectZlen2[iLay], rmin, rmax); + rmin = coneabasis->GetRmax(1); + coneabasis->DefineSection(8, sConeAIntSectZlen2[iLay], rmin, rmax); + rmin = sConeAExtSectDmin[iLay] / 2 - sConeASectThicker; + rmax = sConeAExtSectDmin[iLay] / 2; + zlen = sConeAIntSectZlen2[iLay] + (rmin - coneabasis->GetRmin(4)) / TMath::Tan(sConeAOpeningAngle[iLay] * TMath::DegToRad()); + coneabasis->DefineSection(9, zlen, rmin, rmax); + zlen = sConeATotalLength[iLay] - sConeAExtSectZlen2[iLay]; + coneabasis->DefineSection(10, zlen, rmin, rmax); + rmax = sConeAExtSectDmax[iLay] / 2; + coneabasis->DefineSection(11, zlen, rmin, rmax); + zlen = sConeATotalLength[iLay] - sConeAExtSectZlen1[iLay]; + coneabasis->DefineSection(12, zlen, rmin, rmax); + rmin = sConeAExtSectDmin[iLay] / 2; + coneabasis->DefineSection(13, zlen, rmin, rmax); + coneabasis->DefineSection(14, sConeATotalLength[iLay], rmin, rmax); + + TString coneAComposite = Form("coneabasis%d", iLay); + + // The holes in the vertical wall + thick = coneabasis->GetRmax(0) - coneabasis->GetRmin(0); + TGeoTube* coneawallhole = new TGeoTube(Form("coneawallhole%d", iLay), 0, sConeAWallHoleD / 2, 4 * thick); + + rmin = sConeAIntSectDmax[iLay] / 2 - thick / 2; + zpos = sConeAWallHoleZpos; + dphi = 180. / sConeAWallNHoles[iLay]; + phimin = dphi / 2.; + for (Int_t ihole = 0; ihole < 2 * sConeAWallNHoles[iLay]; ihole++) { + Double_t phi = phimin + ihole * dphi; + xpos = rmin * TMath::Sin(phi * TMath::DegToRad()); + ypos = rmin * TMath::Cos(phi * TMath::DegToRad()); + TGeoCombiTrans* coneawhmat = new TGeoCombiTrans(Form("coneawhmat%dl%d", ihole, iLay), xpos, ypos, zpos, new TGeoRotation("", -phi, 90, 0)); + coneawhmat->RegisterYourself(); + coneAComposite += Form("-coneawallhole%d:coneawhmat%dl%d", iLay, ihole, iLay); + } + + // The central holes + TGeoTube* coneacenthole1 = new TGeoTube(Form("coneacenthole1l%d", iLay), 0, sConeACentralHole1D / 2, 4 * thick); + + TGeoCombiTrans* coneach1mat1 = new TGeoCombiTrans(Form("coneach1mat1l%d", iLay), 0, rmin, sConeACentralHole1Z, new TGeoRotation("", 0, 90, 0)); + coneach1mat1->RegisterYourself(); + TGeoCombiTrans* coneach1mat2 = new TGeoCombiTrans(Form("coneach1mat2l%d", iLay), 0, -rmin, sConeACentralHole1Z, new TGeoRotation("", 0, 90, 0)); + coneach1mat2->RegisterYourself(); + + coneAComposite += Form("-coneacenthole1l%d:coneach1mat1l%d-coneacenthole1l%d:coneach1mat2l%d", iLay, iLay, iLay, iLay); + + TGeoTube* coneacenthole2 = new TGeoTube(Form("coneacenthole2l%d", iLay), 0, sConeACentralHole2D / 2, 4 * thick); + + TGeoCombiTrans* coneach2mat1 = new TGeoCombiTrans(Form("coneach2mat1l%d", iLay), 0, rmin, sConeACentralHole2Z, new TGeoRotation("", 0, 90, 0)); + coneach2mat1->RegisterYourself(); + TGeoCombiTrans* coneach2mat2 = new TGeoCombiTrans(Form("coneach2mat2l%d", iLay), 0, -rmin, sConeACentralHole2Z, new TGeoRotation("", 0, 90, 0)); + coneach2mat2->RegisterYourself(); + + coneAComposite += Form("-coneacenthole2l%d:coneach2mat1l%d-coneacenthole2l%d:coneach2mat2l%d", iLay, iLay, iLay, iLay); + + TGeoTube* coneacenthole3 = new TGeoTube(Form("coneacenthole3l%d", iLay), 0, sConeACentralHole3D / 2, 4 * thick); + + rmin = sConeAExtSectDmax[iLay] / 2 - thick / 2; + TGeoCombiTrans* coneach3mat1 = new TGeoCombiTrans(Form("coneach3mat1l%d", iLay), 0, rmin, sConeACentralHole3Z[iLay], new TGeoRotation("", 0, 90, 0)); + coneach3mat1->RegisterYourself(); + TGeoCombiTrans* coneach3mat2 = new TGeoCombiTrans(Form("coneach3mat2l%d", iLay), 0, -rmin, sConeACentralHole3Z[iLay], new TGeoRotation("", 0, 90, 0)); + coneach3mat2->RegisterYourself(); + + coneAComposite += Form("-coneacenthole3l%d:coneach3mat1l%d-coneacenthole3l%d:coneach3mat2l%d", iLay, iLay, iLay, iLay); + + TGeoCompositeShape* coneABasisSh = new TGeoCompositeShape(coneAComposite.Data()); + + // The Middle Ring (a Tube) + rmin = sConeAMidRingDmin[iLay] / 2; + rmax = sConeAMidRingDmax[iLay] / 2; + zlen = sConeAMidRingZlen / 2; + TGeoTube* midRingSh = new TGeoTube(Form("midRingSh%d", iLay), rmin, rmax, zlen); + + // A Rib (a TGeoXtru) + TGeoXtru* coneARibSh = ibEndWheelARibShape(iLay); + + // Now the Step as a Composite Shape (subtraction of a Pcon from a BBox) + // (cutting volume should be slightly larger than desired region) + rmin = sConeAStepR[iLay]; + + xlen = TMath::Sqrt(rmin * rmin - sConeAStepYdispl[iLay] * sConeAStepYdispl[iLay]) - sConeAStepXdispl[iLay]; + ylen = TMath::Sqrt(rmin * rmin - sConeAStepXdispl[iLay] * sConeAStepXdispl[iLay]) - sConeAStepYdispl[iLay]; + TGeoBBox* stepBoxSh = new TGeoBBox(Form("stepBoxASh%d", iLay), xlen / 2, ylen / 2, sConeAStepZlen / 2); + + xpos = sConeAStepXdispl[iLay] + stepBoxSh->GetDX(); + ypos = sConeAStepYdispl[iLay] + stepBoxSh->GetDY(); + TGeoTranslation* stepBoxTr = new TGeoTranslation(Form("stepBoxATr%d", iLay), xpos, ypos, 0); + stepBoxTr->RegisterYourself(); + + phimin = 90. - TMath::ACos(sConeAStepYdispl[iLay] / rmin) * TMath::RadToDeg() - 5; + dphi = 90. - TMath::ASin(sConeAStepXdispl[iLay] / rmin) * TMath::RadToDeg() - phimin + 10; + rmax = rmin + 2 * stepBoxSh->GetDY(); + + TGeoPcon* stepPconSh = new TGeoPcon(Form("stepPconASh%d", iLay), phimin, dphi, 2); + stepPconSh->DefineSection(0, -1.05 * sConeAStepZlen / 2, rmin, rmax); + stepPconSh->DefineSection(1, 1.05 * sConeAStepZlen / 2, rmin, rmax); + + TGeoCompositeShape* stepASh = new TGeoCompositeShape(Form("stepBoxASh%d:stepBoxATr%d-stepPconASh%d", iLay, iLay, iLay)); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + + TGeoVolume* coneABasisVol = new TGeoVolume(Form("ConeABasis%d", iLay), coneABasisSh, medCarbon); + coneABasisVol->SetFillColor(kBlue); + coneABasisVol->SetLineColor(kBlue); + + TGeoVolume* midRingVol = new TGeoVolume(Form("ConeAMidRing%d", iLay), midRingSh, medCarbon); + coneABasisVol->SetFillColor(kBlue); + coneABasisVol->SetLineColor(kBlue); + + TGeoVolume* coneARibVol = new TGeoVolume(Form("ConeARibVol%d", iLay), coneARibSh, medCarbon); + coneARibVol->SetFillColor(kBlue); + coneARibVol->SetLineColor(kBlue); + + TGeoVolume* stepAVol = new TGeoVolume(Form("ConeAStep%d", iLay), stepASh, medPEEK); + stepAVol->SetFillColor(kBlue); + stepAVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // (origin of local coordinates is at smaller end of Cone Basis) + zref = sIBWheelACZdist / 2 - (sConeAStepHoleZpos + sConeAStepHoleZdist); + + zpos = zref; + endWheel->AddNode(coneABasisVol, 1, new TGeoTranslation(0, 0, zpos)); + + zpos = zref + sConeATotalLength[iLay] - sConeAMidRingZpos[iLay] - midRingSh->GetDz(); + endWheel->AddNode(midRingVol, 1, new TGeoTranslation(0, 0, zpos)); + + rmin = sConeAExtSectDmin[iLay] / 2 - 0.035; + zpos = zref + sConeATotalLength[iLay] - sConeARibsZpos; + dphi = 180. / sConeANRibs[iLay]; + for (Int_t irib = 0; irib < 2 * sConeANRibs[iLay]; irib++) { + Double_t phi = irib * dphi; + xpos = rmin * TMath::Sin(phi * TMath::DegToRad()); + ypos = rmin * TMath::Cos(phi * TMath::DegToRad()); + endWheel->AddNode(coneARibVol, 1, new TGeoCombiTrans(xpos, -ypos, zpos, new TGeoRotation("", 90 + phi, 90, -90))); + } + + // The position of the Steps is given wrt the holes (see eg. ALIITSUP0187) + dphi = 180. - sConeAStepHolePhi0[iLay]; + + Int_t numberOfStaves = o2::trk::GeometryTGeo::Instance()->getNumberOfStaves(iLay); + zpos = zref + (static_cast<TGeoBBox*>(stepAVol->GetShape()))->GetDZ(); + for (Int_t j = 0; j < numberOfStaves; j++) { + Double_t phi = dphi + j * sConeAStepHolePhi[iLay]; + endWheel->AddNode(stepAVol, j + 1, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 180, -90 - phi))); + } +} + +void V3Services::ibEndWheelSideC(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side C + // for a given layer of the Inner Barrel + // (Layer 0: ALIITSSUP0186+ALIITSUP0126) + // (Layer 1: ALIITSSUP0176+ALIITSUP0123) + // (Layer 2: ALIITSSUP0143+ALIITSUP0121) + // + // Input: + // iLay : the layer number + // endWheel : the whole end wheel volume + // where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 15 May 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + // The Basis C Side and the Reinforcement C Side are physically two + // different pieces put together. For sake of simplicity here they are + // made out of the same TGeoPcon volume. Moreover they are two halves, + // so here they are made as a single cylinder. + // The End Wheel Basis + static const Double_t sEndWheelCDmax[3] = {57.0 * sMm, 73.0 * sMm, 89.0 * sMm}; + static const Double_t sEndWheelCDmin[3] = {44.5 * sMm, 58.0 * sMm, 74.0 * sMm}; + static const Double_t sEndWheelCHeigh[3] = {25.0 * sMm, 22.5 * sMm, 20.0 * sMm}; + static const Double_t sEndWheelCThick = 0.6 * sMm; + + static const Int_t sEndWCWallNHoles[3] = {6, 8, 10}; + static const Double_t sEndWCWallHoleD = 4.5 * sMm; + static const Double_t sEndWCWallHoleZpos = 4.0 * sMm; + + static const Int_t sEndWCBaseNBigHoles = 5; + static const Int_t sEndWCBaseNSmalHoles = 6; + static const Double_t sEndWCBaseBigHoleD = 3.6 * sMm; + static const Double_t sEndWCBaseSmalHoleD = 2.5 * sMm; + static const Double_t sEndWCBaseHolesDpos[3] = {50.0 * sMm, 64.0 * sMm, 80.0 * sMm}; + static const Double_t sEndWCBaseHolesPhi = 15.0; // Deg + + // The End Wheel Reinforcement + static const Double_t sEndWCRenfDmin[3] = {44.0 * sMm, 58.0 * sMm, 74.0 * sMm}; + static const Double_t sEndWCRenfDint[3] = {55.0 * sMm, 71.0 * sMm, 87.0 * sMm}; + static const Double_t sEndWCRenfHeigh[3] = {4.0 * sMm, 3.0 * sMm, 3.0 * sMm}; + static const Double_t sEndWCRenfThick = 0.6 * sMm; + + static const Double_t sEndWCRenfZpos = 14.2 * sMm; + + static const Int_t sEndWCRenfNSmalHoles[3] = {5, 7, 9}; + + // The End Wheel Steps + static const Double_t sEndWCStepXdispl[3] = {4.0 * sMm, 6.5 * sMm, 8.5 * sMm}; + static const Double_t sEndWCStepYdispl[3] = {24.4 * sMm, 32.1 * sMm, 39.6 * sMm}; + static const Double_t sEndWCStepR[3] = {27.8 * sMm, 35.8 * sMm, 43.8 * sMm}; + + static const Double_t sEndWCStepZlen = 14.0 * sMm; + + static const Double_t sEndWCStepHoleXpos = 3.0 * sMm; + static const Double_t sEndWCStepHoleZpos = 4.0 * sMm; + static const Double_t sEndWCStepHoleZdist = 4.0 * sMm; + + static const Double_t sEndWCStepHolePhi[3] = {30.0, 22.5, 18.0}; // Deg + static const Double_t sEndWCStepHolePhi0[2] = {9.5, 10.5}; // Deg + static const Double_t sEndWCStepYlow = 7.0 * sMm; + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + + // Create the whole wheel (Basic + Reinforcement) as a CompositeShape + // (a single Pcon minus the (copious!) holes) + TGeoPcon* endwcbasis = new TGeoPcon(Form("endwcbasis%d", iLay), 0, 360, 10); + + rmin = sEndWheelCDmax[iLay] / 2 - sEndWheelCThick; + endwcbasis->DefineSection(0, 0., rmin, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(1, sEndWCRenfZpos, rmin, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(2, sEndWCRenfZpos, sEndWCRenfDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + zlen = sEndWCRenfZpos + sEndWCRenfThick; + endwcbasis->DefineSection(3, zlen, sEndWCRenfDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(4, zlen, sEndWCRenfDint[iLay] / 2, sEndWheelCDmax[iLay] / 2); + zlen = sEndWCRenfZpos + sEndWCRenfHeigh[iLay]; + endwcbasis->DefineSection(5, zlen, sEndWCRenfDint[iLay] / 2, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(6, zlen, rmin, sEndWheelCDmax[iLay] / 2); + zlen = sEndWheelCHeigh[iLay] - sEndWheelCThick; + endwcbasis->DefineSection(7, zlen, rmin, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(8, zlen, sEndWheelCDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(9, sEndWheelCHeigh[iLay], sEndWheelCDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + + TString endWheelComposite = Form("endwcbasis%d", iLay); + + // The holes in the vertical wall + TGeoTube* endwcwalhol = new TGeoTube(Form("endwcwalhol%d", iLay), 0, sEndWCWallHoleD / 2, 4 * sEndWheelCThick); + + rmin = sEndWheelCDmax[iLay] / 2 - sEndWheelCThick / 2; + zpos = sEndWCWallHoleZpos; + dphi = 180. / sEndWCWallNHoles[iLay]; + phimin = dphi / 2.; + for (Int_t ihole = 0; ihole < 2 * sEndWCWallNHoles[iLay]; ihole++) { + Double_t phi = phimin + ihole * dphi; + xpos = rmin * TMath::Sin(phi * TMath::DegToRad()); + ypos = rmin * TMath::Cos(phi * TMath::DegToRad()); + TGeoCombiTrans* endwcwhmat = new TGeoCombiTrans(Form("endwcwhmat%dl%d", ihole, iLay), xpos, ypos, zpos, new TGeoRotation("", -phi, 90, 0)); + endwcwhmat->RegisterYourself(); + endWheelComposite += Form("-endwcwalhol%d:endwcwhmat%dl%d", iLay, ihole, iLay); + } + + // The holes in the base + TGeoTube* endwcbasBhol = new TGeoTube(Form("endwcbasBhol%d", iLay), 0, sEndWCBaseBigHoleD / 2, 1.5 * sEndWheelCThick); + + TGeoTube* endwcbasShol = new TGeoTube(Form("endwcbasShol%d", iLay), 0, sEndWCBaseSmalHoleD / 2, 1.5 * sEndWheelCThick); + + rmin = sEndWCBaseHolesDpos[iLay] / 2; + zpos = (endwcbasis->GetZ(8) + endwcbasis->GetZ(9)) / 2; + + char holename[strlen(endwcbasBhol->GetName()) + 1]; + + phimin = 0.; + for (Int_t ihole = 0; ihole < (sEndWCBaseNBigHoles + sEndWCBaseNSmalHoles); ihole++) { + phimin += sEndWCBaseHolesPhi; + xpos = rmin * TMath::Cos(phimin * TMath::DegToRad()); + ypos = rmin * TMath::Sin(phimin * TMath::DegToRad()); + TGeoTranslation* endwcbshmata = new TGeoTranslation(Form("endwcbshmat%dl%da", ihole, iLay), xpos, ypos, zpos); + endwcbshmata->RegisterYourself(); + TGeoTranslation* endwcbshmatb = new TGeoTranslation(Form("endwcbshmat%dl%db", ihole, iLay), -xpos, -ypos, zpos); + endwcbshmatb->RegisterYourself(); + if ((ihole > 1 && ihole < 5) || (ihole > 5 && ihole < 9)) { // Small holes + strcpy(holename, endwcbasShol->GetName()); + } else { + strcpy(holename, endwcbasBhol->GetName()); + } + endWheelComposite += Form("-%s:endwcbshmat%dl%da-%s:endwcbshmat%dl%db", holename, ihole, iLay, holename, ihole, iLay); + } + + // The holes in the reinforcement + zpos = (endwcbasis->GetZ(2) + endwcbasis->GetZ(3)) / 2; + + phimin = 0.; + dphi = 180. / (sEndWCRenfNSmalHoles[iLay] + 1); + for (Int_t ihole = 0; ihole < sEndWCRenfNSmalHoles[iLay]; ihole++) { + phimin += dphi; + xpos = rmin * TMath::Cos(phimin * TMath::DegToRad()); + ypos = rmin * TMath::Sin(phimin * TMath::DegToRad()); + TGeoTranslation* endwcrshmata = new TGeoTranslation(Form("endwcrshmat%dl%da", ihole, iLay), xpos, ypos, zpos); + endwcrshmata->RegisterYourself(); + TGeoTranslation* endwcrshmatb = new TGeoTranslation(Form("endwcrshmat%dl%db", ihole, iLay), -xpos, -ypos, zpos); + endwcrshmatb->RegisterYourself(); + endWheelComposite += Form("-endwcbasShol%d:endwcrshmat%dl%da-endwcbasShol%d:endwcrshmat%dl%db", iLay, ihole, iLay, iLay, ihole, iLay); + } + + TGeoCompositeShape* endWheelCSh = new TGeoCompositeShape(endWheelComposite.Data()); + + // Now the Step as a Composite Shape (subtraction of a Pcon from a BBox) + // (cutting volume should be slightly larger than desired region) + rmin = sEndWCStepR[iLay]; + + xlen = TMath::Sqrt(rmin * rmin - sEndWCStepYdispl[iLay] * sEndWCStepYdispl[iLay]) - sEndWCStepXdispl[iLay]; + ylen = TMath::Sqrt(rmin * rmin - sEndWCStepXdispl[iLay] * sEndWCStepXdispl[iLay]) - sEndWCStepYdispl[iLay]; + TGeoBBox* stepBoxSh = new TGeoBBox(Form("stepBoxCSh%d", iLay), xlen / 2, ylen / 2, sEndWCStepZlen / 2); + + xpos = sEndWCStepXdispl[iLay] + stepBoxSh->GetDX(); + ypos = sEndWCStepYdispl[iLay] + stepBoxSh->GetDY(); + TGeoTranslation* stepBoxTr = new TGeoTranslation(Form("stepBoxCTr%d", iLay), xpos, ypos, 0); + stepBoxTr->RegisterYourself(); + + phimin = 90. - TMath::ACos(sEndWCStepYdispl[iLay] / rmin) * TMath::RadToDeg() - 5; + dphi = 90. - TMath::ASin(sEndWCStepXdispl[iLay] / rmin) * TMath::RadToDeg() - phimin + 10; + rmax = rmin + 2 * stepBoxSh->GetDY(); + + TGeoPcon* stepPconSh = new TGeoPcon(Form("stepPconCSh%d", iLay), phimin, dphi, 2); + stepPconSh->DefineSection(0, -1.05 * sEndWCStepZlen / 2, rmin, rmax); + stepPconSh->DefineSection(1, 1.05 * sEndWCStepZlen / 2, rmin, rmax); + + TGeoCompositeShape* stepCSh = new TGeoCompositeShape(Form("stepBoxCSh%d:stepBoxCTr%d-stepPconCSh%d", iLay, iLay, iLay)); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + TGeoMedium* medPEEK = mgr->GetMedium("TRK_PEEKCF30$"); + + TGeoVolume* endWheelCVol = new TGeoVolume(Form("EndWheelCBasis%d", iLay), endWheelCSh, medCarbon); + endWheelCVol->SetFillColor(kBlue); + endWheelCVol->SetLineColor(kBlue); + + TGeoVolume* stepCVol = new TGeoVolume(Form("EndWheelCStep%d", iLay), stepCSh, medPEEK); + stepCVol->SetFillColor(kBlue); + stepCVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + zpos = sIBWheelACZdist / 2 - (sEndWCStepHoleZpos + sEndWCStepHoleZdist); + endWheel->AddNode(endWheelCVol, 1, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 0, 180, 0))); + + // The position of the Steps is given wrt the holes (see eg. ALIITSUP0187) + dphi = sEndWCStepHolePhi0[iLay]; + + Int_t numberOfStaves = o2::trk::GeometryTGeo::Instance()->getNumberOfStaves(iLay); + zpos += (static_cast<TGeoBBox*>(stepCVol->GetShape()))->GetDZ(); + for (Int_t j = 0; j < numberOfStaves; j++) { + Double_t phi = dphi + j * sEndWCStepHolePhi[iLay]; + endWheel->AddNode(stepCVol, j + 1, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 180, -90 - phi))); + } +} + +TGeoXtru* V3Services::ibEndWheelARibShape(const Int_t iLay) +{ + // + // Creates the shape of a Rib on Side A cone + // (Layer 0: ALIITSSUP0182) + // (Layer 1: ALIITSSUP0172) + // (Layer 2: ALIITSSUP0136) + // + // Input: + // iLay : the layer number + // + // Output: + // + // Return: + // the Rib shape as a TGeoXtru + // + // Created: 23 Aug 2019 Mario Sitta + // + + static const Int_t sConeARibNVert = 8; + + static const Double_t sConeARibWidth = 27.3 * sMm; + static const Double_t sConeARibTotalLen[3] = {98.03 * sMm, 104.65 * sMm, 101.43 * sMm}; + static const Double_t sConeARibIntLen[3] = {50.0 * sMm, 40.0 * sMm, 28.5 * sMm}; + static const Double_t sConeARibStep[3] = {1.0 * sMm, 1.1 * sMm, 1.0 * sMm}; + static const Double_t sConeARibLenToStep[3] = {42.0 * sMm, 29.0 * sMm, 26.0 * sMm}; + static const Double_t sConeARibLenAfterStep[3] = {50.5 * sMm, 37.0 * sMm, 33.5 * sMm}; + static const Double_t sConeARibVertexPos[3] = {9.0 * sMm, 40.0 * sMm, 58.0 * sMm}; + static const Double_t sConeARibIntAngle = 45.0; // Deg + static const Double_t sConeARibVertexAngle[2] = {20.0, 30.0}; // Deg + + static const Double_t sConeARibThick = 0.9 * sMm; + + // Local variables + Double_t xtru[sConeARibNVert], ytru[sConeARibNVert]; + + // Rib shapes for Layer 0 and Layers 1,2 are different + xtru[0] = 0.; + ytru[0] = 0.; + xtru[1] = sConeARibLenToStep[iLay]; + ytru[1] = 0.; + xtru[2] = xtru[1]; + ytru[2] = sConeARibStep[iLay]; + xtru[3] = sConeARibLenAfterStep[iLay]; + ytru[3] = ytru[2]; + xtru[4] = sConeARibTotalLen[iLay]; + if (iLay == 0) { + ytru[4] = sConeARibWidth - sConeARibVertexPos[iLay]; + xtru[5] = sConeARibIntLen[iLay] + sConeARibVertexPos[iLay] / TMath::Tan(sConeARibIntAngle * TMath::DegToRad()); + } else { + ytru[4] = sConeARibVertexPos[iLay]; + xtru[5] = sConeARibIntLen[iLay] + (sConeARibVertexPos[iLay] - sConeARibWidth) / TMath::Tan(sConeARibVertexAngle[iLay - 1] * TMath::DegToRad()); + } + ytru[5] = ytru[4]; + xtru[6] = sConeARibIntLen[iLay]; + ytru[6] = sConeARibWidth; + xtru[7] = 0.; + ytru[7] = ytru[6]; + + // The actual Xtru + TGeoXtru* ribShape = new TGeoXtru(2); + ribShape->DefinePolygon(sConeARibNVert, xtru, ytru); + ribShape->DefineSection(0, -sConeARibThick / 2); + ribShape->DefineSection(1, sConeARibThick / 2); + + return ribShape; +} + +TGeoVolume* V3Services::ibCyssCylinder(const TGeoManager* mgr) +{ + // + // Creates the cylinder of the Inner Barrel CYSS + // (ALIITSUP0191) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the cylinder as a TGeoVolume + // + // Created: 21 Oct 2019 Mario Sitta + // + + static const Double_t sCyssCylInnerD = 95.6 * sMm; + static const Double_t sCyssCylOuterD = 100.0 * sMm; + static const Double_t sCyssCylZLength = 353.0 * sMm; + static const Double_t sCyssCylFabricThick = 0.1 * sMm; + + // Local variables + Double_t rmin, rmax, zlen, phimin, phimax, dphi; + + // First create the shapes + rmin = sCyssCylInnerD / 2; + rmax = sCyssCylOuterD / 2; + zlen = sCyssCylZLength / 2; + TGeoTubeSeg* cyssOuterCylSh = new TGeoTubeSeg(rmin, rmax, zlen, 180, 360); + + rmin += sCyssCylFabricThick; + rmax -= sCyssCylFabricThick; + zlen -= sCyssCylFabricThick; + + dphi = TMath::ASin(sCyssCylFabricThick / rmax); + phimin = 180 + dphi * TMath::RadToDeg(); + phimax = 360 - dphi * TMath::RadToDeg(); + + TGeoTubeSeg* cyssInnerCylSh = new TGeoTubeSeg(rmin, rmax, zlen, phimin, phimax); + + // We have all shapes: now create the real volumes + TGeoMedium* medPrepreg = mgr->GetMedium("TRK_F6151B05M$"); + TGeoMedium* medRohacell = mgr->GetMedium("TRK_ROHACELL$"); + + TGeoVolume* cyssOuterCylVol = new TGeoVolume("IBCYSSCylinder", cyssOuterCylSh, medPrepreg); + cyssOuterCylVol->SetLineColor(35); + + TGeoVolume* cyssInnerCylVol = new TGeoVolume("IBCYSSCylinderFoam", cyssInnerCylSh, medRohacell); + cyssInnerCylVol->SetLineColor(kGreen); + + cyssOuterCylVol->AddNode(cyssInnerCylVol, 1, nullptr); + + // Finally return the cylinder volume + return cyssOuterCylVol; +} + +TGeoVolume* V3Services::ibCyssCone(const TGeoManager* mgr) +{ + // + // Creates the cone of the Inner Barrel CYSS + // (ALIITSUP0190) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the cone as a TGeoVolume + // + // Created: 24 Oct 2019 Mario Sitta + // + + static const Double_t sCyssConeTotalLength = 150.0 * sMm; + + static const Double_t sCyssConeIntSectDmin = 100.0 * sMm; + static const Double_t sCyssConeIntSectDmax = 101.2 * sMm; + static const Double_t sCyssConeIntSectZlen = 23.0 * sMm; + static const Double_t sCyssConeIntCylZlen = 15.0 * sMm; + + static const Double_t sCyssConeExtSectDmin = 246.0 * sMm; + static const Double_t sCyssConeExtSectDmax = 257.2 * sMm; + static const Double_t sCyssConeExtSectZlen = 42.0 * sMm; + static const Double_t sCyssConeExtCylZlen = 40.0 * sMm; + + static const Double_t sCyssConeOpeningAngle = 40.0; // Deg + + static const Double_t sCyssConeFabricThick = 0.3 * sMm; + + // Local variables + Double_t rmin, rmax, zlen1, zlen2, phimin, phirot, dphi; + Double_t x1, y1, x2, y2, x3, y3, m, xin, yin; + + // The CYSS Cone is physically a single piece made by a cylindrical + // section, a conical section, and a second cylindrical section + // The cone and the second cylinder have a foam core + // Both are implemented as two Pcon's + + TGeoPcon* cyssConeSh = new TGeoPcon(180, 180, 6); + + rmin = sCyssConeIntSectDmin / 2; + rmax = sCyssConeIntSectDmax / 2; + cyssConeSh->DefineSection(0, 0, rmin, rmax); + cyssConeSh->DefineSection(1, sCyssConeIntCylZlen, rmin, rmax); + zlen1 = sCyssConeTotalLength - sCyssConeExtSectZlen; + rmax = yFrom2Points(sCyssConeIntCylZlen, sCyssConeIntSectDmax / 2, zlen1, sCyssConeExtSectDmax / 2, sCyssConeIntSectZlen); + cyssConeSh->DefineSection(2, sCyssConeIntSectZlen, rmin, rmax); + zlen2 = sCyssConeTotalLength - sCyssConeExtCylZlen; + rmin = yFrom2Points(sCyssConeIntSectZlen, sCyssConeIntSectDmin / 2, zlen2, sCyssConeExtSectDmin / 2, zlen1); + rmax = sCyssConeExtSectDmax / 2; + cyssConeSh->DefineSection(3, zlen1, rmin, rmax); + rmin = sCyssConeExtSectDmin / 2; + cyssConeSh->DefineSection(4, zlen2, rmin, rmax); + cyssConeSh->DefineSection(5, sCyssConeTotalLength, rmin, rmax); + + dphi = TMath::ASin(sCyssConeFabricThick / (0.5 * sCyssConeIntSectDmax)); + phimin = 180 + dphi * TMath::RadToDeg(); + phirot = 180 - 2 * dphi * TMath::RadToDeg(); + + // The foam cone is built from the points of the outer cone + TGeoPcon* cyssConeFoamSh = new TGeoPcon(phimin, phirot, 5); + + m = TMath::Tan(sCyssConeOpeningAngle * TMath::DegToRad()); + x1 = cyssConeSh->GetZ(2); + y1 = cyssConeSh->GetRmin(2); + x2 = cyssConeSh->GetZ(1); + y2 = cyssConeSh->GetRmin(1); + x3 = x1; + y3 = y2 + m * (x3 - x2); + + insidePoint(x1, y1, x2, y2, x3, y3, -sCyssConeFabricThick, xin, yin); + cyssConeFoamSh->DefineSection(0, xin, yin, yin); + + x3 = cyssConeSh->GetZ(3); + y3 = cyssConeSh->GetRmin(3); + + insidePoint(x3, y3, x1, y1, x2, y2, -sCyssConeFabricThick, xin, yin); + zlen1 = xin; + rmin = yin; + rmax = y2 + m * (zlen1 - x2); + cyssConeFoamSh->DefineSection(1, zlen1, rmin, rmax); + + x1 = cyssConeSh->GetZ(5); + y1 = cyssConeSh->GetRmax(5); + x2 = cyssConeSh->GetZ(3); + y2 = cyssConeSh->GetRmax(3); + x3 = cyssConeSh->GetZ(2); + y3 = cyssConeSh->GetRmax(2); + + insidePoint(x1, y1, x2, y2, x3, y3, -sCyssConeFabricThick, xin, yin); + zlen1 = xin; + rmin = cyssConeFoamSh->GetRmin(1) + m * (zlen1 - cyssConeFoamSh->GetZ(1)); + rmax = sCyssConeExtSectDmax / 2 - sCyssConeFabricThick; + cyssConeFoamSh->DefineSection(2, zlen1, rmin, rmax); + + rmin = sCyssConeExtSectDmin / 2 + sCyssConeFabricThick; + zlen1 = cyssConeSh->GetZ(4); + cyssConeFoamSh->DefineSection(3, zlen1, rmin, rmax); + + zlen1 = sCyssConeTotalLength - sCyssConeFabricThick; + cyssConeFoamSh->DefineSection(4, zlen1, rmin, rmax); + + // We have all shapes: now create the real volumes + TGeoMedium* medPrepreg = mgr->GetMedium("TRK_F6151B05M$"); + TGeoMedium* medRohacell = mgr->GetMedium("TRK_ROHACELL$"); + + TGeoVolume* cyssConeVol = new TGeoVolume("IBCYSSCone", cyssConeSh, medPrepreg); + cyssConeVol->SetLineColor(35); + + TGeoVolume* cyssConeFoamVol = new TGeoVolume("IBCYSSConeFoam", cyssConeFoamSh, medRohacell); + cyssConeFoamVol->SetLineColor(kGreen); + + cyssConeVol->AddNode(cyssConeFoamVol, 1, nullptr); + + // Finally return the cone volume + return cyssConeVol; +} + +TGeoVolume* V3Services::ibCyssFlangeSideA(const TGeoManager* mgr) +{ + // + // Creates the Flange on Side A for the Inner Barrel CYSS + // (ALIITSUP0189) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the flange as a TGeoVolume + // + // Created: 28 Oct 2019 Mario Sitta + // + + // Radii of the steps + static const Double_t sCyssFlangeAStep1Dmin = 254.1 * sMm; + static const Double_t sCyssFlangeAStep1Dmax = 287.0 * sMm; + static const Double_t sCyssFlangeAStep2Dmax = 259.0 * sMm; + static const Double_t sCyssFlangeAStep3Dmin = 243.0 * sMm; + static const Double_t sCyssFlangeAStep3Dmax = 245.5 * sMm; + static const Double_t sCyssFlangeAStep4Dmax = 239.0 * sMm; + static const Double_t sCyssFlangeAInnerD = 236.0 * sMm; + static const Double_t sCyssFlangeAInRingD = 238.0 * sMm; + + // Heights of the steps + static const Double_t sCyssFlangeATotHei = 39.0 * sMm; + static const Double_t sCyssFlangeAStep1H = 5.5 * sMm; + static const Double_t sCyssFlangeAInRingH = 7.0 * sMm; + static const Double_t sCyssFlangeAInRingUp = 1.0 * sMm; + static const Double_t sCyssFlangeAStep2H = 9.0 * sMm; + static const Double_t sCyssFlangeAStep3H = 10.0 * sMm; + static const Double_t sCyssFlangeAStep4H = 8.5 * sMm; + + // The wings + static const Double_t sCyssFlangeAWingD = 307.0 * sMm; + static const Double_t sCyssFlangeAWingW = 16.0 * sMm; + + // Holes + static const Double_t sCyssFlangeANotchW = 3.0 * sMm; + + static const Double_t sCyssFlangeAHolesDpos = 274.0 * sMm; + + static const Double_t sCyssFlangeAHole1Num = 8; + static const Double_t sCyssFlangeAHole1D = 5.5 * sMm; + static const Double_t sCyssFlangeAHole1Phi0 = 10; // Deg + static const Double_t sCyssFlangeAHole1PhiStep = 20; // Deg + + static const Double_t sCyssFlangeAHole2D = 4.0 * sMm; + static const Double_t sCyssFlangeAHole2Phi = 20; // Deg + + static const Double_t sCyssFlangeAHole3D = 7.0 * sMm; + static const Double_t sCyssFlangeAHole3Phi = 6; // Deg + + static const Double_t sCyssFlangeAWingHoleD = 8.1 * sMm; + static const Double_t sCyssFlangeAWingHoleYpos = 9.0 * sMm; + static const Double_t sCyssFlangeAWingHoleRpos = 146.0 * sMm; + + // Local variables + Double_t rmin, rmax, zlen, phi, dphi; + Double_t xpos, ypos; + + // The CYSS Flange on Side A is physically a single piece. + // It is implemented as a CompositeShape of two Pcon's and one TubeSeg + // minus a huge number of holes + + // The flange body + TGeoPcon* cyssFlangeABody = new TGeoPcon("cyssflangeabody", 180, 180, 12); + + rmin = sCyssFlangeAStep1Dmin / 2; + rmax = sCyssFlangeAStep1Dmax / 2; + cyssFlangeABody->DefineSection(0, 0, rmin, rmax); + cyssFlangeABody->DefineSection(1, sCyssFlangeAStep1H, rmin, rmax); + rmax = sCyssFlangeAStep2Dmax / 2; + cyssFlangeABody->DefineSection(2, sCyssFlangeAStep1H, rmin, rmax); + cyssFlangeABody->DefineSection(3, sCyssFlangeAInRingH, rmin, rmax); + rmin = sCyssFlangeAStep3Dmin / 2; + cyssFlangeABody->DefineSection(4, sCyssFlangeAInRingH, rmin, rmax); + cyssFlangeABody->DefineSection(5, sCyssFlangeAStep2H, rmin, rmax); + rmax = sCyssFlangeAStep3Dmax / 2; + cyssFlangeABody->DefineSection(6, sCyssFlangeAStep2H, rmin, rmax); + zlen = sCyssFlangeATotHei - sCyssFlangeAStep3H; + cyssFlangeABody->DefineSection(7, zlen, rmin, rmax); + rmin = sCyssFlangeAInnerD / 2; + cyssFlangeABody->DefineSection(8, zlen, rmin, rmax); + zlen = sCyssFlangeATotHei - sCyssFlangeAStep4H; + cyssFlangeABody->DefineSection(9, zlen, rmin, rmax); + rmax = sCyssFlangeAStep4Dmax / 2; + cyssFlangeABody->DefineSection(10, zlen, rmin, rmax); + cyssFlangeABody->DefineSection(11, sCyssFlangeATotHei, rmin, rmax); + + // The inner ring + // We define half of it and put two copies to leave the notch space + rmin = sCyssFlangeAStep3Dmin / 2; + phi = 0.5 * (sCyssFlangeANotchW / rmin) * TMath::RadToDeg(); + + TGeoPcon* cyssFlangeAInRing = new TGeoPcon("cflangearing", 180, 90 - phi, 4); + + rmin = sCyssFlangeAInnerD / 2; + rmax = sCyssFlangeAInRingD / 2; + cyssFlangeAInRing->DefineSection(0, sCyssFlangeAInRingUp, rmin, rmax); + cyssFlangeAInRing->DefineSection(1, sCyssFlangeAInRingH, rmin, rmax); + rmax = sCyssFlangeAStep3Dmin / 2; + cyssFlangeAInRing->DefineSection(2, sCyssFlangeAInRingH, rmin, rmax); + cyssFlangeAInRing->DefineSection(3, sCyssFlangeAStep2H, rmin, rmax); + + TGeoRotation* flangeARingRot = new TGeoRotation("cringrot", 90 + phi, 0, 0); + flangeARingRot->RegisterYourself(); + + TString cyssFlangeAComposite = Form("cyssflangeabody+cflangearing+cflangearing:cringrot"); + + // The wings + rmin = sCyssFlangeAStep1Dmax / 2; + rmax = sCyssFlangeAWingD / 2; + zlen = sCyssFlangeAStep1H / 2; + phi = 0.5 * (sCyssFlangeAWingW / rmin) * TMath::RadToDeg(); + + TGeoTubeSeg* cyssFlangeAWing = new TGeoTubeSeg("cflangeawing", rmin, rmax, zlen, 270 - phi, 270 + phi); + + TGeoTranslation* cwingTR1 = new TGeoTranslation("cwingtr1", 0, 0, zlen); + cwingTR1->RegisterYourself(); + + TGeoCombiTrans* cwingCT2 = new TGeoCombiTrans("cwingct2", 0, 0, zlen, new TGeoRotation("", 90 - phi, 0, 0)); + cwingCT2->RegisterYourself(); + + TGeoCombiTrans* cwingCT3 = new TGeoCombiTrans("cwingct3", 0, 0, zlen, new TGeoRotation("", -90 + phi, 0, 0)); + cwingCT3->RegisterYourself(); + + cyssFlangeAComposite += "+cflangeawing:cwingtr1+cflangeawing:cwingct2+cflangeawing:cwingct3"; + + // The (many) holes + zlen = cyssFlangeAWing->GetDz(); + + // The 8 round holes (4 on each side) + rmax = sCyssFlangeAHole1D / 2; + TGeoTube* hole1 = new TGeoTube("hole1", 0, rmax, 2 * zlen); + + for (Int_t i = 0; i < sCyssFlangeAHole1Num / 2; i++) { + Double_t phi = sCyssFlangeAHole1Phi0 + i * sCyssFlangeAHole1PhiStep; + xpos = 0.5 * sCyssFlangeAHolesDpos * TMath::Sin(phi * TMath::DegToRad()); + ypos = 0.5 * sCyssFlangeAHolesDpos * TMath::Cos(phi * TMath::DegToRad()); + TGeoTranslation* hole1Tr1 = new TGeoTranslation(Form("hole1Tr1%d", i), xpos, -ypos, zlen); + hole1Tr1->RegisterYourself(); + TGeoTranslation* hole1Tr2 = new TGeoTranslation(Form("hole1Tr2%d", i), -xpos, -ypos, zlen); + hole1Tr2->RegisterYourself(); + cyssFlangeAComposite += Form("-hole1:hole1Tr1%d-hole1:hole1Tr2%d", i, i); + } + + // The 2 smaller round holes (1 on each side) + rmax = sCyssFlangeAHole2D / 2; + TGeoTube* hole2 = new TGeoTube("hole2", 0, rmax, 2 * zlen); + + xpos = 0.5 * sCyssFlangeAHolesDpos * TMath::Sin(sCyssFlangeAHole2Phi * TMath::DegToRad()); + ypos = 0.5 * sCyssFlangeAHolesDpos * TMath::Cos(sCyssFlangeAHole2Phi * TMath::DegToRad()); + TGeoTranslation* hole2Tr1 = new TGeoTranslation("hole2Tr1", xpos, -ypos, zlen); + hole2Tr1->RegisterYourself(); + TGeoTranslation* hole2Tr2 = new TGeoTranslation("hole2Tr2", -xpos, -ypos, zlen); + hole2Tr2->RegisterYourself(); + + cyssFlangeAComposite += "-hole2:hole2Tr1-hole2:hole2Tr2"; + + // The 2 bigger round holes (1 on each side) + rmax = sCyssFlangeAHole3D / 2; + TGeoTube* hole3 = new TGeoTube("hole3", 0, rmax, 2 * zlen); + + xpos = 0.5 * sCyssFlangeAHolesDpos * TMath::Sin(sCyssFlangeAHole3Phi * TMath::DegToRad()); + ypos = 0.5 * sCyssFlangeAHolesDpos * TMath::Cos(sCyssFlangeAHole3Phi * TMath::DegToRad()); + TGeoTranslation* hole3Tr1 = new TGeoTranslation("hole3Tr1", xpos, -ypos, zlen); + hole3Tr1->RegisterYourself(); + TGeoTranslation* hole3Tr2 = new TGeoTranslation("hole3Tr2", -xpos, -ypos, zlen); + hole3Tr2->RegisterYourself(); + + cyssFlangeAComposite += "-hole3:hole3Tr1-hole3:hole3Tr2"; + + // The holes in the wings + rmax = sCyssFlangeAWingHoleD / 2; + TGeoTube* wingHole = new TGeoTube("wingHole", 0, rmax, 2 * zlen); + + TGeoTranslation* wingHoleTr1 = new TGeoTranslation("wingHoleTr1", 0, -sCyssFlangeAWingHoleRpos, zlen); + wingHoleTr1->RegisterYourself(); + + TGeoTranslation* wingHoleTr2 = new TGeoTranslation("wingHoleTr2", sCyssFlangeAWingHoleRpos, -sCyssFlangeAWingHoleYpos, zlen); + wingHoleTr2->RegisterYourself(); + + TGeoTranslation* wingHoleTr3 = new TGeoTranslation("wingHoleTr3", -sCyssFlangeAWingHoleRpos, -sCyssFlangeAWingHoleYpos, zlen); + wingHoleTr3->RegisterYourself(); + + cyssFlangeAComposite += "-wingHole:wingHoleTr1-wingHole:wingHoleTr2-wingHole:wingHoleTr3"; + + // Lastly the hollows (évidements): a nightmare deserving its own method + TString cyssFlangeAHollows = ibCreateHollowsCyssFlangeSideA(zlen); + + cyssFlangeAComposite += cyssFlangeAHollows.Data(); + + // The final flange shape + TGeoCompositeShape* cyssFlangeASh = new TGeoCompositeShape(cyssFlangeAComposite.Data()); + + // We have all shapes: now create the real volumes + TGeoMedium* medAlu = mgr->GetMedium("TRK_ALUMINUM$"); + + TGeoVolume* cyssFlangeAVol = new TGeoVolume("IBCYSSFlangeA", cyssFlangeASh, medAlu); + cyssFlangeAVol->SetLineColor(kCyan); + cyssFlangeAVol->SetFillColor(kCyan); + + // Finally return the flange volume + return cyssFlangeAVol; +} + +TString V3Services::ibCreateHollowsCyssFlangeSideA(const Double_t zlen) +{ + // + // Creates the very complicate hollow holes in the Flange + // on Side A for the Inner Barrel CYSS + // (ALIITSUP0189) + // + // Input: + // zlen : the thickness of the ring where the hollows are located + // + // Output: + // + // Return: + // the string describing the holes and their positions + // + // Created: 04 Nov 2019 Mario Sitta + // + + static const Double_t sCyssFlangeAHolesDpos = 274.0 * sMm; + + static const Double_t sCyssFlangeAHole1Phi0 = 10; // Deg + static const Double_t sCyssFlangeAHole1PhiStep = 20; // Deg + + static const Double_t sCyssFlangeAHole2Phi = 20; // Deg + + static const Double_t sCyssFlangeAHollowD = 7.0 * sMm; + static const Double_t sCyssFlangeAHollowPhi0 = 13; // Deg + static const Double_t sCyssFlangeAHollowPhi1 = 8; // Deg + + // Local variables + Double_t rmin, rmax, phi, dphi; + Double_t xpos, ypos; + + TString cyssFlangeAHollows; + + // + rmax = sCyssFlangeAHollowD / 2; + TGeoTubeSeg* roundHalf = new TGeoTubeSeg("roundhalf", 0, rmax, 2 * zlen, 0, 180); + + Double_t rHoles = sCyssFlangeAHolesDpos / 2; + + xpos = rHoles * TMath::Cos(sCyssFlangeAHollowPhi0 * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(sCyssFlangeAHollowPhi0 * TMath::DegToRad()); + TGeoCombiTrans* roundTr1 = new TGeoCombiTrans("roundtr1", xpos, -ypos, zlen, new TGeoRotation("", -sCyssFlangeAHollowPhi0, 0, 0)); + roundTr1->RegisterYourself(); + TGeoCombiTrans* roundTr2 = new TGeoCombiTrans("roundtr2", -xpos, -ypos, zlen, new TGeoRotation("", sCyssFlangeAHollowPhi0, 0, 0)); + roundTr2->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr1-roundhalf:roundtr2"; + + TGeoTranslation* noRot = new TGeoTranslation("norot", 0, 0, zlen); + noRot->RegisterYourself(); + TGeoCombiTrans* yRot180 = new TGeoCombiTrans("yrot180", 0, 0, zlen, new TGeoRotation("", 0, 180, 180)); + yRot180->RegisterYourself(); + + rmin = sCyssFlangeAHolesDpos / 2 - sCyssFlangeAHollowD / 2; + rmax = sCyssFlangeAHolesDpos / 2 + sCyssFlangeAHollowD / 2; + + for (Int_t j = 1; j < 4; j++) { + phi = 90 - (sCyssFlangeAHole1Phi0 + j * sCyssFlangeAHole1PhiStep + 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr3 = new TGeoCombiTrans(Form("roundtr%d", j + 2), xpos, -ypos, zlen, new TGeoRotation("", 180 - phi, 0, 0)); + roundTr3->RegisterYourself(); + TGeoCombiTrans* roundTr4 = new TGeoCombiTrans(Form("roundtr%d", j + 5), -xpos, -ypos, zlen, new TGeoRotation("", phi - 180, 0, 0)); + roundTr4->RegisterYourself(); + + cyssFlangeAHollows += Form("-roundhalf:roundtr%d-roundhalf:roundtr%d", j + 2, j + 5); + + phi = 360 - phi - 0.05; + if (j == 3) { + dphi = 360 - sCyssFlangeAHollowPhi0 + 0.05; + } else { + dphi = phi + (sCyssFlangeAHole1PhiStep - sCyssFlangeAHollowPhi1) + 0.1; + } + + TGeoTubeSeg* hollow1 = new TGeoTubeSeg(Form("hollow%d", j), rmin, rmax, 2 * zlen, phi, dphi); + + cyssFlangeAHollows += Form("-hollow%d:norot-hollow%d:yrot180", j, j); + + phi = 90 - (sCyssFlangeAHole1Phi0 + j * sCyssFlangeAHole1PhiStep - 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr5 = new TGeoCombiTrans(Form("roundtr%d", j + 8), xpos, -ypos, zlen, new TGeoRotation("", -phi, 0, 0)); + roundTr5->RegisterYourself(); + TGeoCombiTrans* roundTr6 = new TGeoCombiTrans(Form("roundtr%d", j + 11), -xpos, -ypos, zlen, new TGeoRotation("", phi, 0, 0)); + roundTr6->RegisterYourself(); + + cyssFlangeAHollows += Form("-roundhalf:roundtr%d-roundhalf:roundtr%d", j + 8, j + 11); + } + + // + phi = 90 - (sCyssFlangeAHole2Phi + 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr15 = new TGeoCombiTrans("roundtr15", xpos, -ypos, zlen, new TGeoRotation("", 180 - phi, 0, 0)); + roundTr15->RegisterYourself(); + TGeoCombiTrans* roundTr16 = new TGeoCombiTrans("roundtr16", -xpos, -ypos, zlen, new TGeoRotation("", phi - 180, 0, 0)); + roundTr16->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr15-roundhalf:roundtr16"; + + phi = 360 - phi - 0.5; + dphi = phi + (sCyssFlangeAHole1Phi0 + sCyssFlangeAHole1PhiStep - sCyssFlangeAHole2Phi - sCyssFlangeAHollowPhi1) + 0.5; + TGeoTubeSeg* hollow4 = new TGeoTubeSeg("hollow4", rmin, rmax, 2 * zlen, phi, dphi); + + cyssFlangeAHollows += "-hollow4:norot-hollow4:yrot180"; + + // + phi = 90 - (sCyssFlangeAHole2Phi - 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr17 = new TGeoCombiTrans("roundtr17", xpos, -ypos, zlen, new TGeoRotation("", -phi, 0, 0)); + roundTr17->RegisterYourself(); + TGeoCombiTrans* roundTr18 = new TGeoCombiTrans("roundtr18", -xpos, -ypos, zlen, new TGeoRotation("", phi, 0, 0)); + roundTr18->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr17-roundhalf:roundtr18"; + + phi = 90 - (sCyssFlangeAHole1Phi0 + 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr19 = new TGeoCombiTrans("roundtr19", xpos, -ypos, zlen, new TGeoRotation("", 180 - phi, 0, 0)); + roundTr19->RegisterYourself(); + TGeoCombiTrans* roundTr20 = new TGeoCombiTrans("roundtr20", -xpos, -ypos, zlen, new TGeoRotation("", phi - 180, 0, 0)); + roundTr20->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr19-roundhalf:roundtr20"; + + TGeoCombiTrans* zRotPhi = new TGeoCombiTrans("zrotphi", 0, 0, zlen, new TGeoRotation("", -sCyssFlangeAHole1Phi0, 0, 0)); + zRotPhi->RegisterYourself(); + TGeoCombiTrans* yzRot180Phi = new TGeoCombiTrans("yzrot180phi", 0, 0, zlen, new TGeoRotation("", 0, 180, 180 - sCyssFlangeAHole1Phi0)); + yzRot180Phi->RegisterYourself(); + + cyssFlangeAHollows += "-hollow4:zrotphi-hollow4:yzrot180phi"; + + // Finally we return the string + return cyssFlangeAHollows; +} + +TGeoVolume* V3Services::ibCyssFlangeSideC(const TGeoManager* mgr) +{ + // + // Creates the Flange on Side C for the Inner Barrel CYSS + // (ALIITSUP0098) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the flange as a TGeoVolume + // + // Created: 23 Oct 2019 Mario Sitta + // + + // Radii of the rings + static const Double_t sCyssFlangeCDmin1 = 44.0 * sMm; + static const Double_t sCyssFlangeCDmin2 = 57.0 * sMm; + static const Double_t sCyssFlangeCDmin3 = 73.0 * sMm; + + static const Double_t sCyssFlangeCDmax1 = 58.8 * sMm; + static const Double_t sCyssFlangeCDmax2 = 74.8 * sMm; + static const Double_t sCyssFlangeCDmax3 = 94.0 * sMm; + + static const Double_t sCyssFlangeCDWallIn = 89.0 * sMm; + static const Double_t sCyssFlangeCDWallOut = 95.6 * sMm; + + static const Double_t sCyssFlangeCDExt = 100.0 * sMm; + + // Thicknesses and heights + static const Double_t sCyssFlangeCTotH = 10.0 * sMm; + static const Double_t sCyssFlangeCExtThick = 1.0 * sMm; + + static const Double_t sCyssFlangeCHmax1 = 1.5 * sMm; + static const Double_t sCyssFlangeCHmax2 = 4.0 * sMm; + static const Double_t sCyssFlangeCHmax3 = 6.5 * sMm; + + static const Double_t sCyssFlangeCHmin2 = 2.5 * sMm; + static const Double_t sCyssFlangeCHmin3 = 5.0 * sMm; + + // Holes + static const Double_t sHoles22Dia = 2.2 * sMm; + static const Double_t sHoles22Phi = 60; // Deg + + static const Double_t sHoles30Dia = 3.0 * sMm; + static const Double_t sHoles30Phi = 15; // Deg + + static const Double_t sHoles12Dia = 1.2 * sMm; + static const Double_t sHoles12Phi = 75; // Deg + + static const Double_t sHolesDdist[3] = {50.0 * sMm, 64.0 * sMm, 80.0 * sMm}; + + static const Double_t sCyssFlangeCNotchH = 3.2 * sMm; + static const Double_t sCyssFlangeCNotchW = 3.0 * sMm; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos; + + // The CYSS Flange on Side C is physically a single piece. + // It is implemented as a CompositeShape of two Pcon's minus the holes + + // The flange body + TGeoPcon* cyssFlangeCDisks = new TGeoPcon("cyssflangecdisks", 180, 180, 12); + + rmin = sCyssFlangeCDmin1 / 2; + rmax = sCyssFlangeCDmax1 / 2; + cyssFlangeCDisks->DefineSection(0, 0, rmin, rmax); + cyssFlangeCDisks->DefineSection(1, sCyssFlangeCHmax1, rmin, rmax); + rmin = sCyssFlangeCDmin2 / 2; + cyssFlangeCDisks->DefineSection(2, sCyssFlangeCHmax1, rmin, rmax); + cyssFlangeCDisks->DefineSection(3, sCyssFlangeCHmin2, rmin, rmax); + rmax = sCyssFlangeCDmax2 / 2; + cyssFlangeCDisks->DefineSection(4, sCyssFlangeCHmin2, rmin, rmax); + cyssFlangeCDisks->DefineSection(5, sCyssFlangeCHmax2, rmin, rmax); + rmin = sCyssFlangeCDmin3 / 2; + cyssFlangeCDisks->DefineSection(6, sCyssFlangeCHmax2, rmin, rmax); + cyssFlangeCDisks->DefineSection(7, sCyssFlangeCHmin3, rmin, rmax); + rmax = sCyssFlangeCDWallOut / 2; + cyssFlangeCDisks->DefineSection(8, sCyssFlangeCHmin3, rmin, rmax); + cyssFlangeCDisks->DefineSection(9, sCyssFlangeCHmax3, rmin, rmax); + rmin = sCyssFlangeCDWallIn / 2; + cyssFlangeCDisks->DefineSection(10, sCyssFlangeCHmax3, rmin, rmax); + cyssFlangeCDisks->DefineSection(11, sCyssFlangeCTotH, rmin, rmax); + + TGeoPcon* cyssFlangeCExt = new TGeoPcon("cflangecext", 180, 180, 4); + + rmin = sCyssFlangeCDmax3 / 2; + rmax = sCyssFlangeCDExt / 2; + cyssFlangeCExt->DefineSection(0, 0, rmin, rmax); + cyssFlangeCExt->DefineSection(1, sCyssFlangeCExtThick, rmin, rmax); + rmax = sCyssFlangeCDWallOut / 2; + cyssFlangeCExt->DefineSection(2, sCyssFlangeCExtThick, rmin, rmax); + cyssFlangeCExt->DefineSection(3, sCyssFlangeCHmin3, rmin, rmax); + + TString cyssFlangeCComposite = Form("cyssflangecdisks+cflangecext"); + + // The flange holes + rmax = sHoles22Dia / 2; + zlen = sCyssFlangeCTotH / 2; + TGeoTube* hole22 = new TGeoTube("hole22", 0, rmax, 1.1 * zlen); + + for (Int_t j = 0; j < 3; j++) { + ypos = sHolesDdist[j] / 2; + TGeoTranslation* holeCTr = new TGeoTranslation(Form("holeCTr%d", j), 0, -ypos, zlen); + holeCTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole22:holeCTr%d", j); + + xpos = TMath::Sin(sHoles22Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + ypos = TMath::Cos(sHoles22Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + TGeoTranslation* holeLTr = new TGeoTranslation(Form("holeLTr%d", j), xpos, -ypos, zlen); + holeLTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole22:holeLTr%d", j); + + TGeoTranslation* holeRTr = new TGeoTranslation(Form("holeRTr%d", j), -xpos, -ypos, zlen); + holeRTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole22:holeRTr%d", j); + } + + rmax = sHoles30Dia / 2; + TGeoTube* hole30 = new TGeoTube("hole30", 0, rmax, zlen); + + for (Int_t k = 0; k < 3; k++) { + Double_t phi = (k + 1) * sHoles30Phi; + for (Int_t j = 0; j < 3; j++) { + xpos = TMath::Sin(phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + ypos = TMath::Cos(phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + + TGeoTranslation* holeLTr = new TGeoTranslation(Form("holeLTr%d%d", k, j), xpos, -ypos, zlen); + holeLTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole30:holeLTr%d%d", k, j); + + TGeoTranslation* holeRTr = new TGeoTranslation(Form("holeRTr%d%d", k, j), -xpos, -ypos, zlen); + holeRTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole30:holeRTr%d%d", k, j); + } + } + + rmax = sHoles12Dia / 2; + TGeoTube* hole12 = new TGeoTube("hole12", 0, rmax, 1.1 * zlen); + + for (Int_t j = 0; j < 3; j++) { + xpos = TMath::Sin(sHoles12Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + ypos = TMath::Cos(sHoles12Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + TGeoTranslation* holeLTr = new TGeoTranslation(Form("holeLTrM%d", j), xpos, -ypos, zlen); + holeLTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole12:holeLTrM%d", j); + + TGeoTranslation* holeRTr = new TGeoTranslation(Form("holeRTrM%d", j), -xpos, -ypos, zlen); + holeRTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole12:holeRTrM%d", j); + } + + TGeoBBox* notch = new TGeoBBox("notch", sCyssFlangeCNotchW / 2, (sCyssFlangeCDWallOut - sCyssFlangeCDWallIn), sCyssFlangeCNotchH); + + ypos = (sCyssFlangeCDWallIn + sCyssFlangeCDWallOut) / 4; + TGeoTranslation* notchTr = new TGeoTranslation("notchTr", 0, -ypos, sCyssFlangeCTotH); + notchTr->RegisterYourself(); + + cyssFlangeCComposite += "-notch:notchTr"; + + // The final flange shape + TGeoCompositeShape* cyssFlangeCSh = new TGeoCompositeShape(cyssFlangeCComposite.Data()); + + // We have all shapes: now create the real volumes + TGeoMedium* medAlu = mgr->GetMedium("TRK_ALUMINUM$"); + + TGeoVolume* cyssFlangeCVol = new TGeoVolume("IBCYSSFlangeC", cyssFlangeCSh, medAlu); + cyssFlangeCVol->SetLineColor(kCyan); + cyssFlangeCVol->SetFillColor(kCyan); + + // Finally return the flange volume + return cyssFlangeCVol; +} + +void V3Services::obEndWheelSideA(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side A + // for a given layer of the Middle and Outer Barrels + // (Layer 3: ALICE-W3-01-Side_A, Layer 4: ALICE-W4-01-Side_A, + // Layer 5: ALICE-W5-01-Side_A Layer 6: ALICE-W6-01-Side_A) + // + // Input: + // iLay : the layer number (0,1: Middle, 2,3: Outer) + // endWheel : the volume where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 24 Sep 2019 Mario Sitta + // Updated: 27 Sep 2019 Mario Sitta + // + + // The support ring + static const Double_t sOBWheelTotZlen = 55.0 * sMm; + static const Double_t sOBWheelSuppZlen = 35.0 * sMm; + + static const Double_t sOBWheelSuppRmin[4] = {204.0 * sMm, 254.0 * sMm, 352.5 * sMm, 402.0 * sMm}; + static const Double_t sOBWheelSuppRmax[4] = {241.0 * sMm, 291.0 * sMm, 389.0 * sMm, 448.5 * sMm}; + + // The support blocks + static const Double_t sOBWheelShelfWide[4] = {56.05 * sMm, 55.15 * sMm, 54.10 * sMm, 53.81 * sMm}; // TO BE CHECKED + static const Double_t sOBWheelShelfHoleZpos = 48.0 * sMm; + + static const Double_t sOBWheelShelfRpos[4] = {213.0 * sMm, 262.5 * sMm, 361.0 * sMm, 410.5 * sMm}; + static const Double_t sOBWheelShelfPhi0[4] = {0.0, 0.0, 0.0, 0.0}; // Deg + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + + // The Support Wheel is physically a single piece, a hollow ring + // plus the stave support shelves + // For the sake of simplicity we build it up with four TGeoTube's + // one per each wall of the ring (inner, outer, lower, upper) plus + // as many TGeoBBox's as needed for the shelves + + // The inner ring + TGeoTube* innerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); + + // The outer ring + TGeoTube* outerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); + + // The lower ring + rmax = sOBWheelSuppRmin[iLay] + sOBWheelThickness; + zlen = sOBWheelSuppZlen - 2 * sOBWheelThickness; + TGeoTube* lowerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], rmax, zlen / 2); + + // The upper ring + rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; + TGeoTube* upperRingSh = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); + + // The shelf support + xlen = sOBWheelShelfWide[iLay]; + ylen = 2 * sOBWheelThickness; + zlen = sOBWheelTotZlen - sOBWheelSuppZlen; + TGeoBBox* shelfSh = new TGeoBBox(xlen / 2, ylen / 2, zlen / 2); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + + Int_t nLay = iLay + sNumberInnerLayers; + + TGeoVolume* ringInnerVol = new TGeoVolume(Form("OBEndWheelAInnerRing%d", nLay), innerRingSh, medCarbon); + ringInnerVol->SetFillColor(kBlue); + ringInnerVol->SetLineColor(kBlue); + + TGeoVolume* ringOuterVol = new TGeoVolume(Form("OBEndWheelAOuterRing%d", nLay), outerRingSh, medCarbon); + ringOuterVol->SetFillColor(kBlue); + ringOuterVol->SetLineColor(kBlue); + + TGeoVolume* ringLowerVol = new TGeoVolume(Form("OBEndWheelALowerRing%d", nLay), lowerRingSh, medCarbon); + ringLowerVol->SetFillColor(kBlue); + ringLowerVol->SetLineColor(kBlue); + + TGeoVolume* ringUpperVol = new TGeoVolume(Form("OBEndWheelAUpperRing%d", nLay), upperRingSh, medCarbon); + ringUpperVol->SetFillColor(kBlue); + ringUpperVol->SetLineColor(kBlue); + + TGeoVolume* shelfVol = new TGeoVolume(Form("OBEndWheelAShelf%d", nLay), shelfSh, medCarbon); + shelfVol->SetFillColor(kBlue); + shelfVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // In blueprints the Z position is given wrt the shelf holes + // First the ring + if (iLay < sNumberMiddlLayers) { + zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos; + } else { + zpos = sOBWheelsZpos + sOBWheelShelfHoleZpos; + } + + zpos -= outerRingSh->GetDz(); + mother->AddNode(ringOuterVol, 1, new TGeoTranslation(0, 0, zpos)); + + zpos -= (outerRingSh->GetDz() + lowerRingSh->GetDz()); + mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, zpos)); + + zpos -= (lowerRingSh->GetDz() + innerRingSh->GetDz()); + mother->AddNode(ringInnerVol, 1, new TGeoTranslation(0, 0, zpos)); + + // Then the support blocks + Int_t numberOfStaves = o2::trk::GeometryTGeo::Instance()->getNumberOfStaves(nLay); + Double_t alpha = 360. / numberOfStaves; + + rmin = sOBWheelShelfRpos[iLay] + shelfSh->GetDY(); + zpos -= (innerRingSh->GetDz() + shelfSh->GetDZ()); + + for (Int_t j = 0; j < numberOfStaves; j++) { // As in V3Layer::createLayer + Double_t phi = j * alpha + sOBWheelShelfPhi0[iLay]; + xpos = rmin * cosD(phi); + ypos = rmin * sinD(phi); + phi += 90; + mother->AddNode(shelfVol, j, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi, 0, 0))); + } +} + +void V3Services::mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side C + // for a given layer of the Middle Barrel + // (wheels on Side C are very different for Middle and Outer Barrels, + // so we cannot use a single method for both as for Side A) + // (Layer 3: ALICE-W3-04-Side_C, Layer 4: ALICE-W4-05-Side_C) + // + // Input: + // iLay : the layer number (0,1: Middle, 2,3: Outer) + // endWheel : the volume where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Sep 2019 Mario Sitta + // + + // The support ring + static const Double_t sOBWheelTotZlen[2] = {63.0 * sMm, 55.0 * sMm}; + static const Double_t sOBWheelSuppZlen[2] = {43.0 * sMm, 35.0 * sMm}; + + static const Double_t sOBWheelSuppRmin[2] = {200.5 * sMm, 254.0 * sMm}; + static const Double_t sOBWheelSuppRmax[2] = {237.5 * sMm, 291.0 * sMm}; + static const Double_t sOBWheelFlangeR[2] = {255.0 * sMm, 239.5 * sMm}; + static const Double_t sOBWheelFlangeZlen = 8.0 * sMm; + + // The support blocks + static const Double_t sOBWheelShelfWide[2] = {56.05 * sMm, 55.15 * sMm}; // TO BE CHECKED + static const Double_t sOBWheelShelfHoleZpos[2] = {56.0 * sMm, 48.0 * sMm}; + + static const Double_t sOBWheelShelfRpos[2] = {213.0 * sMm, 262.5 * sMm}; + static const Double_t sOBWheelShelfPhi0[2] = {0.0, 0.0}; // Deg + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + Int_t nsect; + + // The Support Wheel is physically a single piece, a hollow ring + // with a flange plus the stave support shelves + // Unfortunately the flange is on opposite sides on the two layers + // (externally to the ring for layer 3, internally for layer 4) + // For the sake of simplicity we build it up with three TGeoTube's and + // one TGeoPcon for each wall of the ring (inner, outer, lower, upper) + // plus as many TGeoBBox's as needed for the shelves + + // The inner ring + TGeoTube* innerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); + + // The outer ring with the flange + if (iLay == 0) { + nsect = 6; + } else { + nsect = 4; + } + + TGeoPcon* outerRingSh = new TGeoPcon(0, 360, nsect); + + if (iLay == 0) { + rmin = sOBWheelSuppRmax[0] - 2 * sOBWheelThickness; + outerRingSh->DefineSection(0, 0., rmin, sOBWheelFlangeR[0]); + outerRingSh->DefineSection(1, 2 * sOBWheelThickness, rmin, sOBWheelFlangeR[0]); + outerRingSh->DefineSection(2, 2 * sOBWheelThickness, rmin, sOBWheelSuppRmax[0]); + outerRingSh->DefineSection(3, sOBWheelFlangeZlen, rmin, sOBWheelSuppRmax[0]); + outerRingSh->DefineSection(4, sOBWheelFlangeZlen, sOBWheelSuppRmin[0], sOBWheelSuppRmax[0]); + zlen = sOBWheelFlangeZlen + sOBWheelThickness; + outerRingSh->DefineSection(5, zlen, sOBWheelSuppRmin[0], sOBWheelSuppRmax[0]); + } else { + outerRingSh->DefineSection(0, 0., sOBWheelFlangeR[1], sOBWheelSuppRmax[1]); + outerRingSh->DefineSection(1, sOBWheelThickness, sOBWheelFlangeR[1], sOBWheelSuppRmax[1]); + rmax = sOBWheelSuppRmin[1] + sOBWheelThickness; + outerRingSh->DefineSection(2, sOBWheelThickness, sOBWheelFlangeR[1], rmax); + outerRingSh->DefineSection(3, 2 * sOBWheelThickness, sOBWheelFlangeR[1], rmax); + } + + // The lower ring + if (iLay == 0) { + zlen = sOBWheelSuppZlen[iLay] - sOBWheelFlangeZlen - 2 * sOBWheelThickness; + } else { + zlen = sOBWheelSuppZlen[iLay] - sOBWheelThickness - outerRingSh->GetZ(nsect - 1); + } + + rmax = sOBWheelSuppRmin[iLay] + sOBWheelThickness; + TGeoTube* lowerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], rmax, zlen / 2); + + // The upper ring + if (iLay == 1) { // For odd layers the upper and lower rings length is the same + zlen = sOBWheelSuppZlen[iLay] - 2 * sOBWheelThickness; + } + + rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; + TGeoTube* upperRingSh = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); + + // The shelf support + xlen = sOBWheelShelfWide[iLay]; + ylen = 2 * sOBWheelThickness; + zlen = sOBWheelTotZlen[iLay] - sOBWheelSuppZlen[iLay]; + TGeoBBox* shelfSh = new TGeoBBox(xlen / 2, ylen / 2, zlen / 2); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + + Int_t nLay = iLay + sNumberInnerLayers; + + TGeoVolume* ringInnerVol = new TGeoVolume(Form("OBEndWheelCInnerRing%d", nLay), innerRingSh, medCarbon); + ringInnerVol->SetFillColor(kBlue); + ringInnerVol->SetLineColor(kBlue); + + TGeoVolume* ringOuterVol = new TGeoVolume(Form("OBEndWheelCOuterRing%d", nLay), outerRingSh, medCarbon); + ringOuterVol->SetFillColor(kBlue); + ringOuterVol->SetLineColor(kBlue); + + TGeoVolume* ringLowerVol = new TGeoVolume(Form("OBEndWheelCLowerRing%d", nLay), lowerRingSh, medCarbon); + ringLowerVol->SetFillColor(kBlue); + ringLowerVol->SetLineColor(kBlue); + + TGeoVolume* ringUpperVol = new TGeoVolume(Form("OBEndWheelCUpperRing%d", nLay), upperRingSh, medCarbon); + ringUpperVol->SetFillColor(kBlue); + ringUpperVol->SetLineColor(kBlue); + + TGeoVolume* shelfVol = new TGeoVolume(Form("OBEndWheelAShelf%d", nLay), shelfSh, medCarbon); + shelfVol->SetFillColor(kBlue); + shelfVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // In blueprints the Z position is given wrt the shelf holes + // First the ring + zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos[iLay] - sOBWheelSuppZlen[iLay]; + + zpos += innerRingSh->GetDz(); + mother->AddNode(ringInnerVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos += (innerRingSh->GetDz() + upperRingSh->GetDz()); + mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos += (-upperRingSh->GetDz() + lowerRingSh->GetDz()); + mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos += (lowerRingSh->GetDz() + outerRingSh->GetZ(nsect - 1)); + mother->AddNode(ringOuterVol, 1, new TGeoTranslation(0, 0, -zpos)); + + // Then the support blocks + Int_t numberOfStaves = o2::trk::GeometryTGeo::Instance()->getNumberOfStaves(nLay); + Double_t alpha = 360. / numberOfStaves; + + rmin = sOBWheelShelfRpos[iLay] + shelfSh->GetDY(); + zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos[iLay] - sOBWheelSuppZlen[iLay]; + zpos -= shelfSh->GetDZ(); + + for (Int_t j = 0; j < numberOfStaves; j++) { // As in V3Layer::createLayer + Double_t phi = j * alpha + sOBWheelShelfPhi0[iLay]; + xpos = rmin * cosD(phi); + ypos = rmin * sinD(phi); + phi += 90; + mother->AddNode(shelfVol, j, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", phi, 0, 0))); + } +} + +void V3Services::obEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side C + // for a given layer of the Outer Barrel + // (wheels on Side C are very different for Middle and Outer Barrels, + // so we cannot use a single method for both as for Side A) + // (Layer 5: ALICE-W5-04-Side_C, Layer 6: ALICE-W6-04-Side_C) + // + // Input: + // iLay : the layer number (0,1: Middle, 2,3: Outer) + // endWheel : the volume where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 07 Oct 2019 Mario Sitta + // + + // The support ring + static const Double_t sOBWheelTotZlen[2] = {37.0 * sMm, 35.0 * sMm}; + + static const Double_t sOBWheelSuppRmin = 354.0 * sMm; + static const Double_t sOBWheelSuppRmax[2] = {389.5 * sMm, 448.5 * sMm}; + static const Double_t sOBWheelIntFlangeR[2] = {335.0 * sMm, 393.0 * sMm}; + static const Double_t sOBWheelExtFlangeR = 409.0 * sMm; + static const Double_t sOBWheelIntFlangeZ = 4.0 * sMm; // TO BE CHECKED! + + static const Double_t sOBWheelShelfRpos[2] = {361.0 * sMm, 410.5 * sMm}; + static const Double_t sOBWheelShelfHoleZpos[2] = {28.0 * sMm, 26.0 * sMm}; + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + Int_t nsect; + + // The Support Wheels are physically a single piece, a hollow ring + // with one or two flanges + // For the sake of simplicity we build it up with a TGeoTube for the + // external wall and a TGeoPcon for the remaining of the ring for layer 6 + // and with two TGeoPcon's for layer 5 + + // The upper ring: a Pcon for Layer 5, a Tube for Layer 6 + TGeoShape* upperRingSh; + + rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; + if (iLay == 0) { + nsect = 4; + TGeoPcon* ring = new TGeoPcon(0, 360, nsect); + ring->DefineSection(0, sOBWheelThickness, rmin, sOBWheelExtFlangeR); + ring->DefineSection(1, 2 * sOBWheelThickness, rmin, sOBWheelExtFlangeR); + ring->DefineSection(2, 2 * sOBWheelThickness, rmin, sOBWheelSuppRmax[iLay]); + zlen = sOBWheelTotZlen[iLay] - sOBWheelThickness; + ring->DefineSection(3, zlen, rmin, sOBWheelSuppRmax[iLay]); + upperRingSh = (TGeoShape*)ring; + } else { + zlen = sOBWheelTotZlen[iLay] - 2 * sOBWheelThickness; + TGeoTube* ring = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); + upperRingSh = (TGeoShape*)ring; + } + + // The lower ring: a Pcon + TGeoPcon* lowerRingSh; + + if (iLay == 0) { + nsect = 14; + lowerRingSh = new TGeoPcon(0, 360, nsect); + lowerRingSh->DefineSection(0, 0., sOBWheelSuppRmin, sOBWheelExtFlangeR); + lowerRingSh->DefineSection(1, sOBWheelThickness, sOBWheelSuppRmin, sOBWheelExtFlangeR); + rmax = sOBWheelSuppRmin + sOBWheelThickness; + lowerRingSh->DefineSection(2, sOBWheelThickness, sOBWheelSuppRmin, rmax); + lowerRingSh->DefineSection(3, sOBWheelIntFlangeZ, sOBWheelSuppRmin, rmax); + lowerRingSh->DefineSection(4, sOBWheelIntFlangeZ, sOBWheelIntFlangeR[iLay], rmax); + zpos = sOBWheelIntFlangeZ + 2 * sOBWheelThickness; + lowerRingSh->DefineSection(5, zpos, sOBWheelIntFlangeR[iLay], rmax); + lowerRingSh->DefineSection(6, zpos, sOBWheelSuppRmin, rmax); + zpos += sOBWheelIntFlangeZ; + lowerRingSh->DefineSection(7, zpos, sOBWheelSuppRmin, rmax); + rmax = sOBWheelShelfRpos[iLay] + sOBWheelThickness; + lowerRingSh->DefineSection(8, zpos, sOBWheelSuppRmin, rmax); + zpos += sOBWheelThickness; + lowerRingSh->DefineSection(9, zpos, sOBWheelSuppRmin, rmax); + lowerRingSh->DefineSection(10, zpos, sOBWheelShelfRpos[iLay], rmax); + zpos = sOBWheelTotZlen[iLay] - sOBWheelThickness; + lowerRingSh->DefineSection(11, zpos, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(12, zpos, sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + lowerRingSh->DefineSection(13, sOBWheelTotZlen[iLay], sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + } else { + nsect = 10; + lowerRingSh = new TGeoPcon(0, 360, nsect); + lowerRingSh->DefineSection(0, 0., sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + lowerRingSh->DefineSection(1, sOBWheelThickness, sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + rmax = sOBWheelShelfRpos[iLay] + sOBWheelThickness; + lowerRingSh->DefineSection(2, sOBWheelThickness, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(3, sOBWheelIntFlangeZ, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(4, sOBWheelIntFlangeZ, sOBWheelIntFlangeR[iLay], rmax); + zpos = sOBWheelIntFlangeZ + 2 * sOBWheelThickness; + lowerRingSh->DefineSection(5, zpos, sOBWheelIntFlangeR[iLay], rmax); + lowerRingSh->DefineSection(6, zpos, sOBWheelShelfRpos[iLay], rmax); + zpos = sOBWheelTotZlen[iLay] - sOBWheelThickness; + lowerRingSh->DefineSection(7, zpos, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(8, zpos, sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + lowerRingSh->DefineSection(9, sOBWheelTotZlen[iLay], sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + } + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + + Int_t nLay = iLay + sNumberInnerLayers + sNumberMiddlLayers; + + TGeoVolume* ringUpperVol = new TGeoVolume(Form("OBEndWheelCUpperRing%d", nLay), upperRingSh, medCarbon); + ringUpperVol->SetFillColor(kBlue); + ringUpperVol->SetLineColor(kBlue); + + TGeoVolume* ringLowerVol = new TGeoVolume(Form("OBEndWheelCLowerRing%d", nLay), lowerRingSh, medCarbon); + ringLowerVol->SetFillColor(kBlue); + ringLowerVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // In blueprints the Z position is given wrt the shelf holes + zpos = sOBWheelsZpos + sOBWheelShelfHoleZpos[iLay]; + + mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, -zpos)); + + if (iLay == 1) { + zpos -= (sOBWheelThickness + (static_cast<TGeoTube*>(upperRingSh))->GetDz()); + } + mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, -zpos)); +} + +void V3Services::obConeSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Cone structure on Side A of the Outer Barrel + // (ALICE-W4-04-Cone_4A) + // + // Input: + // mother : the volume where to place the current created cone + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 03 Feb 2020 Mario Sitta + // + + static const Double_t sOBConeATotZlen = 350.0 * sMm; + static const Double_t sOBConeAStartCyl2 = 170.0 * sMm; + static const Double_t sOBConeAEndCyl1 = 160.8 * sMm; + static const Double_t sOBConeAThinCylZ = 36.0 * sMm; + + static const Double_t sOBConeAIntR = 291.5 * sMm; + static const Double_t sOBConeAExtR = 302.5 * sMm; + + static const Double_t sOBConeARingExtR = 339.5 * sMm; + static const Double_t sOBConeARingZlen = 55.0 * sMm; + static const Double_t sOBConeARingZout = 35.0 * sMm; + + static const Double_t sOBConeAThickAll = 2.0 * sMm; + static const Double_t sOBConeAThickThin = 1.0 * sMm; + + static const Double_t sOBConeAReinfZIn = 1.0 * sMm; + static const Double_t sOBConeAReinfRIn = 301.6 * sMm; + + static const Double_t sOBConeAReinfThick = 6.5 * sMm; + + static const Int_t sOBConeARibNVert = 8; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos, zpos; + + // The OB Cone on Side A is physically a single piece. + // It is implemented using two Pcon plus a Xtru for the reinforcements + + Double_t phi = sOBConeAReinfThick / sOBConeAIntR; + phi *= TMath::RadToDeg(); + + // The main cone: a Pcon + TGeoPcon* obConeSh = new TGeoPcon(phi, 180 - 2 * phi, 10); + + rmin = sOBConeAIntR; + rmax = sOBConeAReinfRIn; + obConeSh->DefineSection(0, 0., rmin, rmax); + obConeSh->DefineSection(1, sOBConeAReinfZIn, rmin, rmax); + rmax = rmin + sOBConeAThickThin; + obConeSh->DefineSection(2, sOBConeAReinfZIn, rmin, rmax); + obConeSh->DefineSection(3, sOBConeAThinCylZ, rmin, rmax); + rmax = rmin + sOBConeAThickAll; + obConeSh->DefineSection(4, sOBConeAThinCylZ, rmin, rmax); + zlen = sOBConeATotZlen - sOBConeAStartCyl2; + obConeSh->DefineSection(5, zlen, rmin, rmax); + rmin = sOBConeAExtR; + rmax = rmin + sOBConeAThickAll; + zlen = sOBConeATotZlen - sOBConeAEndCyl1; + obConeSh->DefineSection(6, zlen, rmin, rmax); + zlen = sOBConeATotZlen - sOBConeAThickAll; + obConeSh->DefineSection(7, zlen, rmin, rmax); + rmax = sOBConeARingExtR; + obConeSh->DefineSection(8, zlen, rmin, rmax); + obConeSh->DefineSection(9, sOBConeATotZlen, rmin, rmax); + + // The external ring: a Pcon + TGeoPcon* obConeRingSh = new TGeoPcon(phi, 180 - 2 * phi, 6); + + rmin = obConeSh->GetRmax(7); + rmax = rmin + sOBConeAThickAll; + obConeRingSh->DefineSection(0, 0., rmin, rmax); + zlen = sOBConeARingZlen - sOBConeARingZout; + obConeRingSh->DefineSection(1, zlen, rmin, rmax); + rmax = sOBConeARingExtR; + obConeRingSh->DefineSection(2, zlen, rmin, rmax); + zlen += sOBConeAThickAll; + obConeRingSh->DefineSection(3, zlen, rmin, rmax); + rmin = rmax - sOBConeAThickAll; + obConeRingSh->DefineSection(4, zlen, rmin, rmax); + zlen = sOBConeARingZlen - sOBConeAThickAll; + obConeRingSh->DefineSection(5, zlen, rmin, rmax); + + // The reinforcement rib: a Xtru + Double_t xr[sOBConeARibNVert], yr[sOBConeARibNVert]; + + xr[0] = 0; + yr[0] = 0; + xr[1] = obConeSh->GetRmax(0) - obConeSh->GetRmin(0); + yr[1] = yr[0]; + xr[2] = xr[1]; + yr[2] = obConeSh->GetZ(5); + xr[7] = xr[0]; + yr[7] = yr[2]; + xr[6] = obConeSh->GetRmin(6) - obConeSh->GetRmin(5); + yr[6] = obConeSh->GetZ(6); + xr[3] = xr[6] + (xr[1] - xr[0]); + yr[3] = yr[6]; + xr[5] = xr[6]; + yr[5] = sOBConeATotZlen - sOBConeARingZout + sOBConeAThickAll; + xr[4] = xr[3]; + yr[4] = yr[5]; + + TGeoXtru* obConeRibSh = new TGeoXtru(2); + obConeRibSh->DefinePolygon(sOBConeARibNVert, xr, yr); + obConeRibSh->DefineSection(0, 0); + obConeRibSh->DefineSection(1, sOBConeAThickAll); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + + TGeoVolume* obConeVol = new TGeoVolume("OBConeSideA", obConeSh, medCarbon); + obConeVol->SetFillColor(kBlue); + obConeVol->SetLineColor(kBlue); + + TGeoVolume* obConeRingVol = new TGeoVolume("OBConeRingSideA", obConeRingSh, medCarbon); + obConeRingVol->SetFillColor(kBlue); + obConeRingVol->SetLineColor(kBlue); + + TGeoVolume* obConeRibVol = new TGeoVolume("OBConeRibSideA", obConeRibSh, medCarbon); + obConeRibVol->SetFillColor(kBlue); + obConeRibVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + zpos = sOBConesZpos - sOBConeATotZlen; + + mother->AddNode(obConeVol, 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(obConeVol, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + zpos = sOBConesZpos - sOBConeARingZlen; + + mother->AddNode(obConeRingVol, 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(obConeRingVol, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + xpos = obConeSh->GetRmin(0); + ypos = sOBConeAReinfThick; + zpos = sOBConesZpos - sOBConeATotZlen; + + mother->AddNode(obConeRibVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + mother->AddNode(obConeRibVol, 4, new TGeoCombiTrans(-xpos, -ypos, zpos, new TGeoRotation("", 0, -90, 180))); + + ypos = sOBConeAReinfThick - sOBConeAThickAll; + + mother->AddNode(obConeRibVol, 3, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, -90, -180))); + mother->AddNode(obConeRibVol, 4, new TGeoCombiTrans(xpos, -ypos, zpos, new TGeoRotation("", 0, 90, 0))); +} + +void V3Services::obConeTraysSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Cone Trays on Side A of the Outer Barrel + // (ALICE-W3-08-vassoio+ALICE-W5-08_vassoio) + // + // Input: + // mother : the volume where to place the current created cone + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 05 Feb 2020 Mario Sitta + // + + static const Double_t sOBTrayZlen[2] = {112.0 * sMm, 115.0 * sMm}; + static const Double_t sOBTrayRmin[2] = {222.0 * sMm, 370.0 * sMm}; + static const Double_t sOBTrayRmax[2] = {240.0 * sMm, 386.0 * sMm}; + + static const Double_t sOBTrayZpos[2] = {181.0 * sMm, 20.0 * sMm}; + + static const Double_t sOBTrayThick = 2.0 * sMm; + + static const Double_t sOBTrayReinfWide[2] = {27.0 * sMm, 24.0 * sMm}; + static const Double_t sOBTrayReinfYpos = 6.0 * sMm; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos, zpos; + + // Each OB Tray on Side A is physically a single piece. + // It is implemented using a Pcon plus a BBox for the reinforcements + + TGeoPcon* obTraySh[2]; + TGeoBBox* obTrayRibSh[2]; + + for (Int_t j = 0; j < 2; j++) { + Double_t phi = (sOBTrayReinfYpos + sOBTrayThick) / sOBTrayRmin[j]; + phi *= TMath::RadToDeg(); + + // The main body: a Pcon + obTraySh[j] = new TGeoPcon(180 + phi, 180 - 2 * phi, 4); + + rmin = sOBTrayRmin[j]; + rmax = sOBTrayRmax[j]; + obTraySh[j]->DefineSection(0, 0., rmin, rmax); + obTraySh[j]->DefineSection(1, sOBTrayThick, rmin, rmax); + rmin = rmax - sOBTrayThick; + obTraySh[j]->DefineSection(2, sOBTrayThick, rmin, rmax); + obTraySh[j]->DefineSection(3, sOBTrayZlen[j], rmin, rmax); + + // The reinforcement rib: a BBox + obTrayRibSh[j] = new TGeoBBox(sOBTrayReinfWide[j] / 2, + sOBTrayThick / 2, + sOBTrayZlen[j] / 2); + } // for (j = 0,1) + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + + TGeoVolume *obTrayVol[2], *obTrayRibVol[2]; + + for (Int_t j = 0; j < 2; j++) { + obTrayVol[j] = new TGeoVolume(Form("OBConeTray%d", j), obTraySh[j], medCarbon); + obTrayVol[j]->SetFillColor(kBlue); + obTrayVol[j]->SetLineColor(kBlue); + + obTrayRibVol[j] = new TGeoVolume(Form("OBConeTrayRib%d", j), obTrayRibSh[j], medCarbon); + obTrayRibVol[j]->SetFillColor(kBlue); + obTrayRibVol[j]->SetLineColor(kBlue); + } + + // Finally put everything in the mother volume + + for (Int_t j = 0; j < 2; j++) { + if (j == 0) { + zpos = sOBConesZpos - sOBTrayZpos[j] - sOBTrayZlen[j]; + } else { + zpos = sOBConesZpos + sOBTrayZpos[j]; + } + + mother->AddNode(obTrayVol[j], 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(obTrayVol[j], 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + xpos = obTraySh[j]->GetRmin(0) + obTrayRibSh[j]->GetDX(); + ypos = sOBTrayReinfYpos + obTrayRibSh[j]->GetDY(); + zpos += obTrayRibSh[j]->GetDZ(); + + mother->AddNode(obTrayRibVol[j], 1, new TGeoTranslation(xpos, -ypos, zpos)); + mother->AddNode(obTrayRibVol[j], 2, new TGeoTranslation(-xpos, -ypos, zpos)); + mother->AddNode(obTrayRibVol[j], 3, new TGeoTranslation(xpos, ypos, zpos)); + mother->AddNode(obTrayRibVol[j], 4, new TGeoTranslation(-xpos, ypos, zpos)); + } +} + +void V3Services::obConeSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Cone structure on Side C of the Outer Barrel + // (ALICE-W4-06-Cone_4C) + // + // Input: + // mother : the volume where to place the current created cone + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Jan 2020 Mario Sitta + // + + static const Double_t sOBConeCTotZlen = 332.5 * sMm; + static const Double_t sOBConeCStartCyl2 = 132.8 * sMm; + static const Double_t sOBConeCEndCyl1 = 82.4 * sMm; + static const Double_t sOBConeCThinCylZ = 36.0 * sMm; + + static const Double_t sOBConeCIntR = 291.5 * sMm; + static const Double_t sOBConeCExtR = 315.0 * sMm; + + static const Double_t sOBConeCRingExtR = 333.0 * sMm; + static const Double_t sOBConeCRingZlen = 61.0 * sMm; + static const Double_t sOBConeCRingZout = 42.0 * sMm; + + static const Double_t sOBConeCThickAll = 2.0 * sMm; + static const Double_t sOBConeCThickThin = 1.0 * sMm; + + static const Double_t sOBConeCReinfZIn = 2.0 * sMm; + static const Double_t sOBConeCReinfRIn = 301.6 * sMm; + static const Double_t sOBConeCReinfROut = 351.5 * sMm; + + static const Double_t sOBConeCReinfThick = 6.5 * sMm; + + static const Int_t sOBConeCRibNVert = 8; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos, zpos; + + // The OB Cone on Side C is physically a single piece. + // It is implemented using two Pcon plus a Xtru for the reinforcements + + Double_t phi = sOBConeCReinfThick / sOBConeCIntR; + phi *= TMath::RadToDeg(); + + // The main cone: a Pcon + TGeoPcon* obConeSh = new TGeoPcon(phi, 180 - 2 * phi, 10); + + rmin = sOBConeCExtR; + rmax = sOBConeCReinfROut; + obConeSh->DefineSection(0, 0., rmin, rmax); + obConeSh->DefineSection(1, sOBConeCThickAll, rmin, rmax); + rmax = rmin + sOBConeCThickAll; + obConeSh->DefineSection(2, sOBConeCThickAll, rmin, rmax); + obConeSh->DefineSection(3, sOBConeCEndCyl1, rmin, rmax); + rmin = sOBConeCIntR; + rmax = rmin + sOBConeCThickAll; + obConeSh->DefineSection(4, sOBConeCStartCyl2, rmin, rmax); + zlen = sOBConeCTotZlen - sOBConeCThinCylZ; + obConeSh->DefineSection(5, zlen, rmin, rmax); + rmax = rmin + sOBConeCThickThin; + obConeSh->DefineSection(6, zlen, rmin, rmax); + zlen = sOBConeCTotZlen - sOBConeCReinfZIn; + obConeSh->DefineSection(7, zlen, rmin, rmax); + rmax = sOBConeCReinfRIn; + obConeSh->DefineSection(8, zlen, rmin, rmax); + obConeSh->DefineSection(9, sOBConeCTotZlen, rmin, rmax); + + // The external ring: a Pcon + TGeoPcon* obConeRingSh = new TGeoPcon(phi, 180 - 2 * phi, 8); + + rmin = sOBConeCRingExtR - sOBConeCThickAll; + rmax = sOBConeCReinfROut; + obConeRingSh->DefineSection(0, 0., rmin, rmax); + obConeRingSh->DefineSection(1, sOBConeCThickAll, rmin, rmax); + rmax = sOBConeCRingExtR; + obConeRingSh->DefineSection(2, sOBConeCThickAll, rmin, rmax); + zlen = sOBConeCRingZout - sOBConeCThickAll; + obConeRingSh->DefineSection(3, zlen, rmin, rmax); + rmin = sOBConeCExtR + sOBConeCThickAll; + obConeRingSh->DefineSection(4, zlen, rmin, rmax); + obConeRingSh->DefineSection(5, sOBConeCRingZout, rmin, rmax); + rmax = rmin + sOBConeCThickAll; + obConeRingSh->DefineSection(6, sOBConeCRingZout, rmin, rmax); + obConeRingSh->DefineSection(7, sOBConeCRingZlen, rmin, rmax); + + // The reinforcement rib: a Xtru + Double_t xr[sOBConeCRibNVert], yr[sOBConeCRibNVert]; + + xr[0] = 0; + yr[0] = 0; + xr[1] = obConeSh->GetRmax(9) - obConeSh->GetRmin(9); + yr[1] = yr[0]; + xr[2] = xr[1]; + yr[2] = obConeSh->GetZ(9) - obConeSh->GetZ(4); + xr[7] = xr[0]; + yr[7] = yr[2]; + xr[6] = obConeSh->GetRmin(3) - obConeSh->GetRmin(4); + yr[6] = obConeSh->GetZ(9) - obConeSh->GetZ(3); + xr[3] = xr[6] + (xr[1] - xr[0]); + yr[3] = yr[6]; + xr[5] = xr[6]; + yr[5] = sOBConeCTotZlen - sOBConeCRingZout; + xr[4] = xr[3]; + yr[4] = yr[5]; + + TGeoXtru* obConeRibSh = new TGeoXtru(2); + obConeRibSh->DefinePolygon(sOBConeCRibNVert, xr, yr); + obConeRibSh->DefineSection(0, 0); + obConeRibSh->DefineSection(1, sOBConeCThickAll); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("TRK_M55J6K$"); // TO BE CHECKED + + TGeoVolume* obConeVol = new TGeoVolume("OBConeSideC", obConeSh, medCarbon); + obConeVol->SetFillColor(kBlue); + obConeVol->SetLineColor(kBlue); + + TGeoVolume* obConeRingVol = new TGeoVolume("OBConeRingSideC", obConeRingSh, medCarbon); + obConeRingVol->SetFillColor(kBlue); + obConeRingVol->SetLineColor(kBlue); + + TGeoVolume* obConeRibVol = new TGeoVolume("OBConeRibSideC", obConeRibSh, medCarbon); + obConeRibVol->SetFillColor(kBlue); + obConeRibVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + zpos = sOBConesZpos; + + mother->AddNode(obConeVol, 1, new TGeoTranslation(0, 0, -zpos)); + mother->AddNode(obConeVol, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + zpos -= sOBConeCThickAll; + + mother->AddNode(obConeRingVol, 1, new TGeoTranslation(0, 0, -zpos)); + mother->AddNode(obConeRingVol, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + xpos = obConeSh->GetRmin(9); + ypos = sOBConeCReinfThick; + zpos = sOBConesZpos - obConeSh->GetZ(9); + + mother->AddNode(obConeRibVol, 1, new TGeoCombiTrans(xpos, -ypos, -zpos, new TGeoRotation("", 0, -90, 0))); + mother->AddNode(obConeRibVol, 2, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 180))); + + ypos = sOBConeCReinfThick - sOBConeCThickAll; + + mother->AddNode(obConeRibVol, 3, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 0, -90, 0))); + mother->AddNode(obConeRibVol, 4, new TGeoCombiTrans(-xpos, -ypos, -zpos, new TGeoRotation("", 0, 90, 180))); +} diff --git a/Detectors/Upgrades/CMakeLists.txt b/Detectors/Upgrades/CMakeLists.txt index dd42de06dfdbf..c87ff88e6997f 100644 --- a/Detectors/Upgrades/CMakeLists.txt +++ b/Detectors/Upgrades/CMakeLists.txt @@ -10,4 +10,4 @@ message(STATUS "Building detectors for upgrades") add_subdirectory(IT3) -add_subdirectory(PostLS4) \ No newline at end of file +add_subdirectory(ALICE3) \ No newline at end of file diff --git a/Detectors/Upgrades/IT3/README.md b/Detectors/Upgrades/IT3/README.md new file mode 100644 index 0000000000000..9a4d82a533edb --- /dev/null +++ b/Detectors/Upgrades/IT3/README.md @@ -0,0 +1,21 @@ +<!-- doxy +\page refDetectorsUpgradesIT3 UpgradesIT3 +/doxy --> + +# IT3 +Upgraded version of the ITS that includes upgraded truly-cylindrical inner barrel. + +# Run the full simulation +Provided O2 has been compiled with upgrades enabled, it is possible to simulate ITS 3 geometry within the `o2-sim` executable. + +## Simulation +IT3 module is enabled via the `-m IT3` parameter. +In case of `PIPE` to be enabled, the size of the beam pipe is automatically scaled to what is foreseen for the upgrades. + +Typical command to generate MC data: +```bash +o2-sim -m PIPE IT3 [...] +``` + +## Reconstruction +Currently, the reconstruction is driven the `macro/run_trac_alice3.C`, which takes as input the hits generated by simulation. Hits can be smeared upon request (see `kUseSmearing`). \ No newline at end of file diff --git a/Detectors/Upgrades/IT3/base/include/ITS3Base/GeometryTGeo.h b/Detectors/Upgrades/IT3/base/include/ITS3Base/GeometryTGeo.h index 8934bab5e60bf..2857cf19c86f4 100644 --- a/Detectors/Upgrades/IT3/base/include/ITS3Base/GeometryTGeo.h +++ b/Detectors/Upgrades/IT3/base/include/ITS3Base/GeometryTGeo.h @@ -42,7 +42,7 @@ namespace its3 class GeometryTGeo : public o2::itsmft::GeometryTGeo { public: - typedef o2::Transform3D Mat3D; + typedef o2::math_utils::Transform3D Mat3D; using DetMatrixCache::getMatrixL2G; using DetMatrixCache::getMatrixT2GRot; using DetMatrixCache::getMatrixT2L; @@ -53,8 +53,9 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static GeometryTGeo* Instance() { // get (create if needed) a unique instance of the object - if (!sInstance) + if (!sInstance) { sInstance = std::unique_ptr<GeometryTGeo>(new GeometryTGeo(true, 0)); + } return sInstance.get(); } @@ -67,9 +68,9 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo // 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::utils::bit2Mask(o2::TransformType::T2L, // default transformations to load - o2::TransformType::T2G, - o2::TransformType::L2G)*/ + /*o2::base::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, // default transformations to load + o2::math_utils::TransformType::T2G, + o2::math_utils::TransformType::L2G)*/ ); /// Default destructor diff --git a/Detectors/Upgrades/IT3/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/IT3/base/src/GeometryTGeo.cxx index a873385bbc7ec..210e677910de5 100644 --- a/Detectors/Upgrades/IT3/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/IT3/base/src/GeometryTGeo.cxx @@ -18,7 +18,7 @@ #include "ITS3Base/GeometryTGeo.h" #include "DetectorsBase/GeometryManager.h" #include "ITSMFTBase/SegmentationAlpide.h" -#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Cartesian.h" #include "FairLogger.h" // for LOG @@ -43,7 +43,6 @@ using namespace TMath; using namespace o2::its3; using namespace o2::detectors; -using namespace o2::utils; using Segmentation = o2::itsmft::SegmentationAlpide; @@ -285,7 +284,7 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const int wrID = mLayerToWrapper[lay]; - TString path = Form("/cave_1/%s_2/", GeometryTGeo::getITSVolPattern()); + TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getITSVolPattern()); if (wrID >= 0) { path += Form("%s%d_1/", getITSWrapVolPattern(), wrID); @@ -385,7 +384,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } // build matrices - if ((mask & o2::utils::bit2Mask(o2::TransformType::L2G)) && !getCacheL2G().isFilled()) { + 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"; auto& cacheL2G = getCacheL2G(); @@ -397,7 +396,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2L)) && !getCacheT2L().isFilled()) { + 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"; auto& cacheT2L = getCacheT2L(); @@ -408,7 +407,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2G)) && !getCacheT2G().isFilled()) { + 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"; @@ -422,7 +421,7 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::utils::bit2Mask(o2::TransformType::T2GRot)) && !getCacheT2GRot().isFilled()) { + 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"; auto& cacheT2Gr = getCacheT2GRot(); @@ -680,8 +679,9 @@ int GeometryTGeo::extractLayerChipType(int lay) const void GeometryTGeo::Print(Option_t*) const { printf("NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); - if (!isBuilt()) + if (!isBuilt()) { return; + } for (int i = 0; i < mNumberOfLayers; i++) { printf( @@ -709,7 +709,7 @@ void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) double xp = gloB[0] - dx * t, yp = gloB[1] - dy * t; x = Sqrt(xp * xp + yp * yp); alp = ATan2(yp, xp); - BringTo02Pi(alp); + o2::math_utils::bringTo02Pi(alp); } //__________________________________________________________________________ diff --git a/Detectors/Upgrades/IT3/simulation/src/Detector.cxx b/Detectors/Upgrades/IT3/simulation/src/Detector.cxx index 674a52efd1ed6..760fecf285dc2 100644 --- a/Detectors/Upgrades/IT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/IT3/simulation/src/Detector.cxx @@ -394,8 +394,9 @@ Bool_t Detector::ProcessHits(FairVolume* vol) while ((lay < mTotalNumberOfLayers) && (notSens = (volID != mLayerID[lay]))) { ++lay; } - if (notSens) + if (notSens) { return kFALSE; // RS: can this happen? This method must be called for sensors only? + } // Is it needed to keep a track reference when the outer ITS volume is encountered? auto stack = (o2::data::Stack*)fMC->GetStack(); @@ -435,10 +436,12 @@ Bool_t Detector::ProcessHits(FairVolume* vol) } // increment energy loss at all steps except entrance - if (!startHit) + if (!startHit) { mTrackData.mEnergyLoss += fMC->Edep(); - if (!(startHit | stopHit)) + } + if (!(startHit | stopHit)) { return kFALSE; // do noting + } if (startHit) { mTrackData.mEnergyLoss = 0.; @@ -919,14 +922,14 @@ void Detector::constructDetectorGeometry() // Create the geometry and insert it in the mother volume ITSV TGeoManager* geoManager = gGeoManager; - TGeoVolume* vALIC = geoManager->GetVolume("cave"); + TGeoVolume* vALIC = geoManager->GetVolume("barrel"); if (!vALIC) { LOG(FATAL) << "Could not find the top volume"; } new TGeoVolumeAssembly(GeometryTGeo::getITSVolPattern()); TGeoVolume* vITSV = geoManager->GetVolume(GeometryTGeo::getITSVolPattern()); - vALIC->AddNode(vITSV, 2, nullptr); // Copy number is 2 to cheat AliGeoManager::CheckSymNamesLUT + vALIC->AddNode(vITSV, 2, new TGeoTranslation(0, 30., 0)); // Copy number is 2 to cheat AliGeoManager::CheckSymNamesLUT const Int_t kLength = 100; Char_t vstrng[kLength] = "xxxRS"; //? @@ -976,8 +979,9 @@ void Detector::constructDetectorGeometry() vITSV->AddNode(wrapVols[id], 1, nullptr); } } - if (!mCreateOuterBarrel) + if (!mCreateOuterBarrel) { mTotalNumberOfLayers = mNumberOfInnerLayers; + } // Now create the actual geometry for (Int_t j = 0; j < mTotalNumberOfLayers; j++) { @@ -1021,7 +1025,7 @@ void Detector::constructDetectorGeometry() mGeometry[j]->setSensorThick(mDetectorThickness[j]); } - if (mCreateOuterBarrel && j >= mNumberOfInnerLayers) + if (mCreateOuterBarrel && j >= mNumberOfInnerLayers) { for (int iw = 0; iw < sNumberOfWrapperVolumes; iw++) { if (mLayerRadii[j] > mWrapperMinRadius[iw] && mLayerRadii[j] < mWrapperMaxRadius[iw]) { LOG(DEBUG) << "Will embed layer " << j << " in wrapper volume " << iw; @@ -1031,6 +1035,7 @@ void Detector::constructDetectorGeometry() break; } } + } mGeometry[j]->createLayer(dest); } @@ -1200,17 +1205,19 @@ void Detector::addAlignableVolumes() const return; } - TString path = Form("/cave_1/%s_2", GeometryTGeo::getITSVolPattern()); + TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getITSVolPattern()); TString sname = GeometryTGeo::composeSymNameITS3(); LOG(DEBUG) << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } Int_t lastUID = 0; - for (Int_t lr = 0; lr < mTotalNumberOfLayers; lr++) + for (Int_t lr = 0; lr < mTotalNumberOfLayers; lr++) { addAlignableVolumesLayer(lr, path, lastUID); + } return; } @@ -1235,13 +1242,15 @@ void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) LOG(DEBUG) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } const V3Layer* lrobj = mGeometry[lr]; Int_t nstaves = lrobj->getNumberOfStavesPerParent(); - for (int st = 0; st < nstaves; st++) + for (int st = 0; st < nstaves; st++) { addAlignableVolumesStave(lr, st, path, lastUID); + } return; } @@ -1258,14 +1267,16 @@ void Detector::addAlignableVolumesStave(Int_t lr, Int_t st, TString& parent, Int LOG(DEBUG) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } const V3Layer* lrobj = mGeometry[lr]; Int_t nhstave = lrobj->getNumberOfHalfStavesPerParent(); Int_t start = nhstave > 0 ? 0 : -1; - for (Int_t sst = start; sst < nhstave; sst++) + for (Int_t sst = start; sst < nhstave; sst++) { addAlignableVolumesHalfStave(lr, st, sst, path, lastUID); + } return; } @@ -1285,15 +1296,17 @@ void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t st, Int_t hst, TStri LOG(DEBUG) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } } const V3Layer* lrobj = mGeometry[lr]; Int_t nmodules = lrobj->getNumberOfModulesPerParent(); Int_t start = nmodules > 0 ? 0 : -1; - for (Int_t md = start; md < nmodules; md++) + for (Int_t md = start; md < nmodules; md++) { addAlignableVolumesModule(lr, st, hst, md, path, lastUID); + } return; } @@ -1313,14 +1326,16 @@ void Detector::addAlignableVolumesModule(Int_t lr, Int_t st, Int_t hst, Int_t md LOG(DEBUG) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } } const V3Layer* lrobj = mGeometry[lr]; Int_t nchips = lrobj->getNumberOfChipsPerParent(); - for (Int_t ic = 0; ic < nchips; ic++) + for (Int_t ic = 0; ic < nchips; ic++) { addAlignableVolumesChip(lr, st, hst, md, ic, path, lastUID); + } return; } @@ -1340,8 +1355,9 @@ void Detector::addAlignableVolumesChip(Int_t lr, Int_t st, Int_t hst, Int_t md, LOG(DEBUG) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname, path.Data(), modUID)) + if (!gGeoManager->SetAlignableEntry(sname, path.Data(), modUID)) { LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } return; } diff --git a/Detectors/Upgrades/IT3/simulation/src/V3Layer.cxx b/Detectors/Upgrades/IT3/simulation/src/V3Layer.cxx index f4e6815106a47..cbc2914b68a74 100644 --- a/Detectors/Upgrades/IT3/simulation/src/V3Layer.cxx +++ b/Detectors/Upgrades/IT3/simulation/src/V3Layer.cxx @@ -547,8 +547,9 @@ TGeoVolume* V3Layer::createStave(const TGeoManager* /*mgr*/) // mechStaveVol = createStaveStructInnerB(); if (mechStaveVol) { ypos = (static_cast<TGeoBBox*>(modVol->GetShape()))->GetDY() - ypos; - if (mStaveModel != Detector::kIBModel4) + if (mStaveModel != Detector::kIBModel4) { ypos += (static_cast<TGeoBBox*>(mechStaveVol->GetShape()))->GetDY(); + } staveVol->AddNode(mechStaveVol, 1, new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", 0, 0, 180))); } } else { @@ -566,9 +567,10 @@ TGeoVolume* V3Layer::createStave(const TGeoManager* /*mgr*/) mechStaveVol = createSpaceFrameOuterB(); if (mechStaveVol) { - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon staveVol->AddNode(mechStaveVol, 1, new TGeoCombiTrans(0, -sOBSFrameULegHeight1, 0, new TGeoRotation("", 180, 0, 0))); + } } } } @@ -617,10 +619,11 @@ TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) char chipName[nameLen], sensName[nameLen], volumeName[nameLen]; // For material budget studies - if (mBuildLevel < 6) + if (mBuildLevel < 6) { dummyChip = kFALSE; // will be made of Si - else + } else { dummyChip = kTRUE; // will be made of Air + } // First create the single chip snprintf(chipName, nameLen, "%s%d", o2::its3::GeometryTGeo::getITSChipPattern(), mLayerNumber); @@ -651,8 +654,9 @@ TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) Double_t yano = (static_cast<TGeoBBox*>(aluAnodeCableVol->GetShape()))->GetDY(); ytot = ymod; - if (mStaveModel == Detector::kIBModel4) + if (mStaveModel == Detector::kIBModel4) { ytot += (sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitorYHi / 2); + } TGeoBBox* module = new TGeoBBox(xtot, ytot, ztot); @@ -685,8 +689,9 @@ TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) if (mStaveModel == Detector::kIBModel4) { ypos += (ymod + glue->GetDY()); - if (mBuildLevel < 2) // Glue + if (mBuildLevel < 2) { // Glue modVol->AddNode(glueVol, 1, new TGeoTranslation(xpos, ypos, 0)); + } ypos += glue->GetDY(); if (mBuildLevel < 4) { // Kapton @@ -808,20 +813,22 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz nCapacitors++; for (Int_t j = 0; j < sIBChipsPerRow; j++) { - if (j == (sIBChipsPerRow - 1)) + if (j == (sIBChipsPerRow - 1)) { xpos = xGroup4[1]; - else + } else { xpos = xGroup4[0]; + } zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup4[j]; modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += sIBChipsPerRow; for (Int_t j = 0; j < nGroup5A; j++) { - if (j == 0) + if (j == 0) { xpos = xGroup5A[0]; - else + } else { xpos = xGroup5A[1]; + } zpos = zGroup5A[j]; modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } @@ -869,8 +876,9 @@ TGeoVolume* V3Layer::createIBFPCAlGnd(const Double_t xcable, const Double_t zcab aluminumVol->SetFillColor(kCyan); ypos = coverlay->GetDY() - aluminum->GetDY(); - if (mBuildLevel < 1) // Aluminum + if (mBuildLevel < 1) { // Aluminum coverlayVol->AddNode(aluminumVol, 1, new TGeoTranslation(0, ypos, 0)); + } return coverlayVol; } @@ -920,8 +928,9 @@ TGeoVolume* V3Layer::createIBFPCAlAnode(const Double_t xcable, const Double_t zc aluminumVol->SetFillColor(kCyan); ypos = -coverlay->GetDY() + aluminum->GetZ(1); - if (mBuildLevel < 1) // Aluminum + if (mBuildLevel < 1) { // Aluminum coverlayVol->AddNode(aluminumVol, 1, new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", 0, -90, 0))); + } return coverlayVol; } @@ -1224,16 +1233,19 @@ TGeoVolume* V3Layer::createStaveModelInnerB4(const TGeoManager* mgr) // Now build up the half stave ypos = glue->GetDY(); - if (mBuildLevel < 2) // Glue + if (mBuildLevel < 2) { // Glue mechStavVol->AddNode(glueVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (glue->GetDY() + fleecbot->GetDY()); - if (mBuildLevel < 5) // Carbon + if (mBuildLevel < 5) { // Carbon mechStavVol->AddNode(fleecbotVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (fleecbot->GetDY() + cfplate->GetDY()); - if (mBuildLevel < 5) // Carbon + if (mBuildLevel < 5) { // Carbon mechStavVol->AddNode(cfplateVol, 1, new TGeoTranslation(0, ypos, 0)); + } ylay = ypos + cfplate->GetDY(); // The level where tubes etc. lay @@ -1906,10 +1918,11 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) zmod = (static_cast<TGeoBBox*>(moduleVol->GetShape()))->GetDZ(); if (mLayerNumber == mNumberOfInnerLayers || - mLayerNumber == mNumberOfInnerLayers + 1) + mLayerNumber == mNumberOfInnerLayers + 1) { zlen = sOBColdPlateZLenML / 2; // Middle Layer - else + } else { zlen = sOBColdPlateZLenOL / 2; // Outer Layer + } xlen = sOBColdPlateXWidth / 2; @@ -1944,8 +1957,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) new TGeoTubeSeg("FleecTube", rCoolMax + yGraph, rCoolMax + yCFleece + yGraph, zlen, 180., 360.); TGeoTube* gammaConvRod; - if (mAddGammaConv) + if (mAddGammaConv) { gammaConvRod = new TGeoTube("GammaConver", 0, 0.5 * mGammaConvDiam, zlen - sOBCPConnHollowZLen); + } // TGeoBBox* flex1_5cm = new TGeoBBox("Flex1MV_5cm", xHalfSt, yFlex1 / 2, flexOverlap / 2); // TGeoBBox* flex2_5cm = new TGeoBBox("Flex2MV_5cm", xHalfSt, yFlex2 / 2, flexOverlap / 2); @@ -1958,8 +1972,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) // The half stave container (an XTru to avoid overlaps between neightbours) xHalfSt = xmod; // add the cross cables when done! yHalfSt = ypowbus + ymod + coldPlate->GetDY() + 2 * fleeccent->GetDY() + graphlat->GetDY() + fleeclat->GetDY(); - if (mAddGammaConv) + if (mAddGammaConv) { yHalfSt += mGammaConvDiam; + } xtru[0] = xHalfSt; ytru[0] = 0; @@ -1969,8 +1984,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) ytru[2] = ytru[1]; xtru[3] = xtru[2]; ytru[3] = ytru[2] - (coolTube->GetRmax() + fleectub->GetRmax()); - if (mAddGammaConv) + if (mAddGammaConv) { ytru[3] -= mGammaConvDiam; + } xtru[4] = sOBCoolTubeXDist / 2 - fleectub->GetRmax(); ytru[4] = ytru[3]; xtru[5] = xtru[4]; @@ -1994,8 +2010,9 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) new TGeoBBox("connCsideOB", sOBCPConnectorXWidth / 2, sOBCPConnBlockYHei / 2, sOBCPConnBlockZLen / 2); // The StaveStruct container, a Composite Shape - if (mAddGammaConv) + if (mAddGammaConv) { yHalfSt -= mGammaConvDiam; + } ypos = 2 * yHalfSt + connAside->GetDY() - sOBCPConnHollowYHei; zpos = zlen + connAside->GetDZ() - sOBCPConnHollowZLen; snprintf(volname, nameLen, "transAsideOB%d", mLayerNumber); @@ -2119,18 +2136,21 @@ TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) ypos -= ymod; ypos -= fleeccent->GetDY(); - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon halfStaveVol->AddNode(fleeccentVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos -= fleeccent->GetDY(); ypos -= coldPlate->GetDY(); - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon halfStaveVol->AddNode(coldPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos -= coldPlate->GetDY(); ypos -= fleeccent->GetDY(); - if (mBuildLevel < 6) // Carbon + if (mBuildLevel < 6) { // Carbon halfStaveVol->AddNode(fleeccentVol, 2, new TGeoTranslation(0, ypos, 0)); + } xpos = sOBCoolTubeXDist / 2; ypos1 = ypos - (fleeccent->GetDY() + coolTube->GetRmax()); @@ -2307,45 +2327,55 @@ TGeoVolume* V3Layer::createOBPowerBiasBuses(const Double_t zcable, const TGeoMan // Volumes are piled up from bottom to top ypos = -pnbBus->GetDY() + kapPB->GetDY(); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (kapPB->GetDY() + gndPB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(gndPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (gndPB->GetDY() + dielPB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(dielPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (dielPB->GetDY() + topPB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(topPBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (topPB->GetDY() + kapPB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapPBVol, 2, new TGeoTranslation(0, ypos, 0)); + } // ypos += (kapPB->GetDY() + kapBB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (kapBB->GetDY() + botBB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(botBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (botBB->GetDY() + dielBB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(dielBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (dielBB->GetDY() + topBB->GetDY()); - if (mBuildLevel < 2) // Aluminum + if (mBuildLevel < 2) { // Aluminum pnbBusVol->AddNode(topBBVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += (topBB->GetDY() + kapBB->GetDY()); - if (mBuildLevel < 5) // Kapton + if (mBuildLevel < 5) { // Kapton pnbBusVol->AddNode(kapBBVol, 2, new TGeoTranslation(0, ypos, 0)); + } // return pnbBusVol; @@ -3452,10 +3482,11 @@ TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) // First create all needed shapes // For material budget studies - if (mBuildLevel < 7) + if (mBuildLevel < 7) { dummyChip = kFALSE; // will be made of Si - else + } else { dummyChip = kTRUE; // will be made of Air + } // The chip (the same as for IB) snprintf(chipName, nameLen, "%s%d", o2::its3::GeometryTGeo::getITSChipPattern(), mLayerNumber); @@ -3525,8 +3556,9 @@ TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) // Now build up the module ypos = -module->GetDY() + glueCP->GetDY(); - if (mBuildLevel < 3) // Glue + if (mBuildLevel < 3) { // Glue modVol->AddNode(glueCPVol, 1, new TGeoTranslation(0, ypos, 0)); + } xpos = xchip + xGap / 2; ypos += (ychip + glueCP->GetDY()); @@ -3550,8 +3582,9 @@ TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) } ypos += (ychip + glueFPC->GetDY()); - if (mBuildLevel < 3) // Glue + if (mBuildLevel < 3) { // Glue modVol->AddNode(glueFPCVol, 1, new TGeoTranslation(0, ypos, 0)); + } ypos += glueFPC->GetDY(); if (mBuildLevel < 5) { // Kapton @@ -3608,8 +3641,9 @@ TGeoVolume* V3Layer::createOBFPCCuGnd(const Double_t zcable, const TGeoManager* copperVol->SetFillColor(kCyan); ypos = -soldmask->GetDY() + copper->GetDY(); - if (mBuildLevel < 1) // Copper + if (mBuildLevel < 1) { // Copper soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + } return soldmaskVol; } @@ -3653,8 +3687,9 @@ TGeoVolume* V3Layer::createOBFPCCuSig(const Double_t zcable, const TGeoManager* copperVol->SetFillColor(kCyan); ypos = soldmask->GetDY() - copper->GetDY(); - if (mBuildLevel < 1) // Copper + if (mBuildLevel < 1) { // Copper soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + } return soldmaskVol; } diff --git a/Detectors/Upgrades/IT3/simulation/src/V3Services.cxx b/Detectors/Upgrades/IT3/simulation/src/V3Services.cxx index aa89b344a34ed..0f99e37b716cc 100644 --- a/Detectors/Upgrades/IT3/simulation/src/V3Services.cxx +++ b/Detectors/Upgrades/IT3/simulation/src/V3Services.cxx @@ -81,8 +81,9 @@ TGeoVolume* V3Services::createIBEndWheelsSideA(const TGeoManager* mgr) TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideA"); endWheelsVol->SetVisibility(kTRUE); - for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) { ibEndWheelSideA(jLay, endWheelsVol, mgr); + } // Return the wheels return endWheelsVol; @@ -108,8 +109,9 @@ TGeoVolume* V3Services::createIBEndWheelsSideC(const TGeoManager* mgr) TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideC"); endWheelsVol->SetVisibility(kTRUE); - for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) { ibEndWheelSideC(jLay, endWheelsVol, mgr); + } // Return the wheels return endWheelsVol; @@ -182,8 +184,9 @@ void V3Services::createMBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* m // Created: 24 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) { obEndWheelSideA(jLay, mother, mgr); + } } void V3Services::createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) @@ -202,8 +205,9 @@ void V3Services::createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* m // Created: 26 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) { mbEndWheelSideC(jLay, mother, mgr); + } } void V3Services::createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr) @@ -222,8 +226,9 @@ void V3Services::createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* m // Created: 27 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) { obEndWheelSideA(jLay + sNumberMiddlLayers, mother, mgr); + } } void V3Services::createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) @@ -242,8 +247,9 @@ void V3Services::createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* m // Created: 27 Sep 2019 Mario Sitta // - for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) { obEndWheelSideC(jLay, mother, mgr); + } } void V3Services::createOBConeSideA(TGeoVolume* mother, const TGeoManager* mgr) @@ -668,10 +674,11 @@ void V3Services::ibEndWheelSideC(const Int_t iLay, TGeoVolume* endWheel, const T endwcbshmata->RegisterYourself(); TGeoTranslation* endwcbshmatb = new TGeoTranslation(Form("endwcbshmat%dl%db", ihole, iLay), -xpos, -ypos, zpos); endwcbshmatb->RegisterYourself(); - if ((ihole > 1 && ihole < 5) || (ihole > 5 && ihole < 9)) // Small holes + if ((ihole > 1 && ihole < 5) || (ihole > 5 && ihole < 9)) { // Small holes strcpy(holename, endwcbasShol->GetName()); - else + } else { strcpy(holename, endwcbasBhol->GetName()); + } endWheelComposite += Form("-%s:endwcbshmat%dl%da-%s:endwcbshmat%dl%db", holename, ihole, iLay, holename, ihole, iLay); } @@ -1270,10 +1277,11 @@ TString V3Services::ibCreateHollowsCyssFlangeSideA(const Double_t zlen) cyssFlangeAHollows += Form("-roundhalf:roundtr%d-roundhalf:roundtr%d", j + 2, j + 5); phi = 360 - phi - 0.05; - if (j == 3) + if (j == 3) { dphi = 360 - sCyssFlangeAHollowPhi0 + 0.05; - else + } else { dphi = phi + (sCyssFlangeAHole1PhiStep - sCyssFlangeAHollowPhi1) + 0.1; + } TGeoTubeSeg* hollow1 = new TGeoTubeSeg(Form("hollow%d", j), rmin, rmax, 2 * zlen, phi, dphi); @@ -1611,10 +1619,11 @@ void V3Services::obEndWheelSideA(const Int_t iLay, TGeoVolume* mother, const TGe // Finally put everything in the mother volume // In blueprints the Z position is given wrt the shelf holes // First the ring - if (iLay < sNumberMiddlLayers) + if (iLay < sNumberMiddlLayers) { zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos; - else + } else { zpos = sOBWheelsZpos + sOBWheelShelfHoleZpos; + } zpos -= outerRingSh->GetDz(); mother->AddNode(ringOuterVol, 1, new TGeoTranslation(0, 0, zpos)); @@ -1697,10 +1706,11 @@ void V3Services::mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGe TGeoTube* innerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); // The outer ring with the flange - if (iLay == 0) + if (iLay == 0) { nsect = 6; - else + } else { nsect = 4; + } TGeoPcon* outerRingSh = new TGeoPcon(0, 360, nsect); @@ -1722,17 +1732,19 @@ void V3Services::mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGe } // The lower ring - if (iLay == 0) + if (iLay == 0) { zlen = sOBWheelSuppZlen[iLay] - sOBWheelFlangeZlen - 2 * sOBWheelThickness; - else + } else { zlen = sOBWheelSuppZlen[iLay] - sOBWheelThickness - outerRingSh->GetZ(nsect - 1); + } rmax = sOBWheelSuppRmin[iLay] + sOBWheelThickness; TGeoTube* lowerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], rmax, zlen / 2); // The upper ring - if (iLay == 1) // For odd layers the upper and lower rings length is the same + if (iLay == 1) { // For odd layers the upper and lower rings length is the same zlen = sOBWheelSuppZlen[iLay] - 2 * sOBWheelThickness; + } rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; TGeoTube* upperRingSh = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); @@ -1929,8 +1941,9 @@ void V3Services::obEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGe mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, -zpos)); - if (iLay == 1) + if (iLay == 1) { zpos -= (sOBWheelThickness + (static_cast<TGeoTube*>(upperRingSh))->GetDz()); + } mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, -zpos)); } @@ -2165,10 +2178,11 @@ void V3Services::obConeTraysSideA(TGeoVolume* mother, const TGeoManager* mgr) // Finally put everything in the mother volume for (Int_t j = 0; j < 2; j++) { - if (j == 0) + if (j == 0) { zpos = sOBConesZpos - sOBTrayZpos[j] - sOBTrayZlen[j]; - else + } else { zpos = sOBConesZpos + sOBTrayZpos[j]; + } mother->AddNode(obTrayVol[j], 1, new TGeoTranslation(0, 0, zpos)); mother->AddNode(obTrayVol[j], 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); diff --git a/Detectors/Upgrades/PostLS4/CMakeLists.txt b/Detectors/Upgrades/PostLS4/CMakeLists.txt deleted file mode 100644 index 73aa18e55d56b..0000000000000 --- a/Detectors/Upgrades/PostLS4/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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". -# -# See http://alice-o2.web.cern.ch/license for full licensing information. -# -# In applying this license CERN does 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/Detectors/Upgrades/README.md b/Detectors/Upgrades/README.md new file mode 100644 index 0000000000000..7e31340e67998 --- /dev/null +++ b/Detectors/Upgrades/README.md @@ -0,0 +1,21 @@ +<!-- doxy +\page refDetectorsUpgrades Upgrades +/doxy --> + +# Upgrades +This is a top page for detectors not included in Run 3 layout. + + +# Installation +To enable the compilation of this section you need to have the environment variable `ENABLE_UPGRADES=ON` set. +An example for the installation command is: + +```bash +ENABLE_UPGRADES=ON aliBuild build O2 --defaults o2 +``` + +Currently two sections are included: +<!-- doxy +* \subpage refDetectorsUpgradesIT3 +* \subpage refDetectorsUpgradesALICE3TRK +/doxy --> diff --git a/Detectors/Vertexing/CMakeLists.txt b/Detectors/Vertexing/CMakeLists.txt index 85103ac22c8d0..b36d310a214f4 100644 --- a/Detectors/Vertexing/CMakeLists.txt +++ b/Detectors/Vertexing/CMakeLists.txt @@ -10,13 +10,34 @@ o2_add_library(DetectorsVertexing SOURCES src/DCAFitterN.cxx + src/PVertexer.cxx + src/PVertexerHelpers.cxx + src/PVertexerParams.cxx + src/VertexTrackMatcher.cxx + src/SVertexer.cxx + src/SVertexerParams.cxx + src/V0Hypothesis.cxx PUBLIC_LINK_LIBRARIES ROOT::Core O2::CommonUtils - O2::ReconstructionDataFormats) + O2::ReconstructionDataFormats + O2::DataFormatsTPC + O2::DataFormatsITS + O2::TPCBase + O2::SimulationDataFormat + O2::FT0Reconstruction + O2::DataFormatsFT0 + ms_gsl::ms_gsl) o2_target_root_dictionary(DetectorsVertexing HEADERS include/DetectorsVertexing/HelixHelper.h - include/DetectorsVertexing/DCAFitterN.h) + include/DetectorsVertexing/PVertexer.h + include/DetectorsVertexing/PVertexerHelpers.h + include/DetectorsVertexing/PVertexerParams.h + include/DetectorsVertexing/VertexTrackMatcher.h + include/DetectorsVertexing/DCAFitterN.h + include/DetectorsVertexing/SVertexer.h + include/DetectorsVertexing/SVertexerParams.h + include/DetectorsVertexing/V0Hypothesis.h) o2_add_test( DCAFitterN diff --git a/Detectors/Vertexing/include/DetectorsVertexing/DCAFitterN.h b/Detectors/Vertexing/include/DetectorsVertexing/DCAFitterN.h index f5000b7ccbd5b..bfc6f7978ed7f 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/DCAFitterN.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/DCAFitterN.h @@ -30,15 +30,15 @@ namespace vertexing struct TrackCovI { float sxx, syy, syz, szz; - TrackCovI(const o2::track::TrackParCov& trc) { set(trc); } + TrackCovI(const o2::track::TrackParCov& trc, float xerrFactor = 1.) { set(trc, xerrFactor); } TrackCovI() = default; - void set(const o2::track::TrackParCov& trc) + void set(const o2::track::TrackParCov& trc, float xerrFactor = 1) { // 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; + float cyy = trc.getSigmaY2(), czz = trc.getSigmaZ2(), cyz = trc.getSigmaZY(), cxx = cyy * xerrFactor; float detYZ = cyy * czz - cyz * cyz; if (detYZ > 0.) { auto detYZI = 1. / detYZ; @@ -75,7 +75,7 @@ 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; @@ -109,15 +109,12 @@ class DCAFitterN ///< return Chi2 at PCA candidate (no check for its validity) float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; } - ///< 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]; } - - ///< prapare copies of tracks at the V0 candidate (no check for the candidate validity) + ///< 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); - ///< 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]; } + ///< check if propagation of tracks to candidate vertex was done + bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } ///< track param propagated to V0 candidate (no check for the candidate validity) /// propagateTracksToVertex must be called in advance @@ -129,6 +126,17 @@ class DCAFitterN 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; + + ///< create parent track param w/o errors for decay vertex + 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; + + MatSym3D calcPCACovMatrix(int cand = 0) const; + const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; } ///< return number of iterations during minimization (no check for its validity) @@ -180,6 +188,33 @@ class DCAFitterN bool closerToAlternative() const; 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]; } + + ///< 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]; } + + MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame + { + MatStd3D mat; + mat(2, 2) = 1; + mat(0, 0) = mat(1, 1) = mTrAux[i].c; + mat(0, 1) = -mTrAux[i].s; + mat(1, 0) = mTrAux[i].s; + return std::move(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 std::move(mat); + } + void assign(int) {} template <class T, class... Tr> void assign(int i, const T& t, const Tr&... args) @@ -231,16 +266,16 @@ class DCAFitterN int mCurHyp = 0; int mCrossIDCur = 0; int mCrossIDAlt = -1; - 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 - int mMaxIter = 20; // max number of iterations - float mBz = 0; // bz field, to be set by user - float mMaxR2 = 200. * 200.; // reject PCA's above this radius - float mMaxDZIni = 4.; // reject (if>0) PCA candidate if tracks DZ exceeds threshold - float mMinParamChange = 1e-3; // stop iterations if largest change of any X is smaller than this - float mMinRelChi2Change = 0.9; // stop iterations is chi2/chi2old > this - float mMaxChi2 = 100; // abs cut on chi2 or abs distance + 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 + int mMaxIter = 20; // max number of iterations + float mBz = 0; // bz field, to be set by user + float mMaxR2 = 200. * 200.; // reject PCA's above this radius + float mMaxDZIni = 4.; // reject (if>0) PCA candidate if tracks DZ exceeds threshold + float mMinParamChange = 1e-3; // stop iterations if largest change of any X is smaller than this + float mMinRelChi2Change = 0.9; // 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 ClassDefNV(DCAFitterN, 1); @@ -259,7 +294,7 @@ int DCAFitterN<N, Args...>::process(const Tr&... args) mTrAux[i].set(*mOrigTrPtr[i], mBz); } if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1])) { // even for N>2 it should be enough to test just 1 loop - return 0; // no crossing + return 0; // no crossing } if (mUseAbsDCA) { calcRMatrices(); // needed for fast residuals derivatives calculation in case of abs. distance minimization @@ -290,7 +325,7 @@ int DCAFitterN<N, Args...>::process(const Tr&... args) if (mUseAbsDCA ? minimizeChi2NoErr() : minimizeChi2()) { mOrder[mCurHyp] = mCurHyp; if (mPropagateToPCA && !propagateTracksToVertex(mCurHyp)) { - return false; + continue; // discard candidate if failed to propagate to it } mCurHyp++; } @@ -554,12 +589,12 @@ void DCAFitterN<N, Args...>::calcPCANoErr() { // calculate point of closest approach for N prongs w/o errors auto& pca = mPCA[mCurHyp]; - o2::utils::rotateZ(mTrPos[mCurHyp][N - 1][0], mTrPos[mCurHyp][N - 1][1], pca[0], pca[1], mTrAux[N - 1].s, mTrAux[N - 1].c); + o2::math_utils::rotateZd(mTrPos[mCurHyp][N - 1][0], mTrPos[mCurHyp][N - 1][1], pca[0], pca[1], mTrAux[N - 1].s, mTrAux[N - 1].c); //RRRR mTrAux[N-1].loc2glo(mTrPos[mCurHyp][N-1][0], mTrPos[mCurHyp][N-1][1], pca[0], pca[1] ); pca[2] = mTrPos[mCurHyp][N - 1][2]; for (int i = N - 1; i--;) { double x, y; - o2::utils::rotateZ(mTrPos[mCurHyp][i][0], mTrPos[mCurHyp][i][1], x, y, mTrAux[i].s, mTrAux[i].c); + o2::math_utils::rotateZd(mTrPos[mCurHyp][i][0], mTrPos[mCurHyp][i][1], x, y, mTrAux[i].s, mTrAux[i].c); //RRRR mTrAux[i].loc2glo(mTrPos[mCurHyp][i][0], mTrPos[mCurHyp][i][1], x, y ); pca[0] += x; pca[1] += y; @@ -570,6 +605,18 @@ void DCAFitterN<N, Args...>::calcPCANoErr() pca[2] *= NInv; } +//___________________________________________________________________ +template <int N, typename... Args> +ROOT::Math::SMatrix<double, 3, 3, ROOT::Math::MatRepSym<double, 3>> DCAFitterN<N, Args...>::calcPCACovMatrix(int cand) const +{ + // calculate covariance matrix for the point of closest approach + MatSym3D covm; + for (int i = N; i--;) { + covm += ROOT::Math::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); + } + return std::move(covm); +} + //___________________________________________________________________ template <int N, typename... Args> void DCAFitterN<N, Args...>::calcTrackResiduals() @@ -579,7 +626,7 @@ void DCAFitterN<N, Args...>::calcTrackResiduals() for (int i = N; i--;) { mTrRes[mCurHyp][i] = mTrPos[mCurHyp][i]; vtxLoc = mPCA[mCurHyp]; - o2::utils::rotateZInv(vtxLoc[0], vtxLoc[1], vtxLoc[0], vtxLoc[1], mTrAux[i].s, mTrAux[i].c); // glo->loc + o2::math_utils::rotateZInvd(vtxLoc[0], vtxLoc[1], vtxLoc[0], vtxLoc[1], mTrAux[i].s, mTrAux[i].c); // glo->loc mTrRes[mCurHyp][i] -= vtxLoc; } } @@ -660,6 +707,22 @@ bool DCAFitterN<N, Args...>::propagateTracksToVertex(int icand) return true; } +//___________________________________________________________________ +template <int N, typename... Args> +inline o2::track::TrackPar DCAFitterN<N, Args...>::getTrackParamAtPCA(int i, int icand) const +{ + // propagate tracks param only to current vertex (if not already done) + int ord = mOrder[icand]; + o2::track::TrackPar trc(mCandTr[ord][i]); + if (!mTrPropDone[ord]) { + auto x = mTrAux[i].c * mPCA[ord][0] + mTrAux[i].s * mPCA[ord][1]; // X of PCA in the track frame + if (!trc.propagateParamTo(x, mBz)) { + trc.invalidate(); + } + } + return std::move(trc); +} + //___________________________________________________________________ template <int N, typename... Args> inline double DCAFitterN<N, Args...>::getAbsMax(const VecND& v) @@ -685,8 +748,8 @@ bool DCAFitterN<N, Args...>::minimizeChi2() if (!mCandTr[mCurHyp][i].propagateTo(x, mBz)) { return false; } - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i]); // prepare inverse cov.matrices at starting point + setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions + mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point } if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference @@ -821,6 +884,64 @@ void DCAFitterN<N, Args...>::print() const LOG(INFO) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDZIni; } +//___________________________________________________________________ +template <int N, typename... Args> +o2::track::TrackParCov DCAFitterN<N, Args...>::createParentTrackParCov(int cand, bool sectorAlpha) const +{ + const auto& trP = getTrack(0, cand); + const auto& trN = getTrack(1, cand); + const auto& wvtx = getPCACandidate(cand); + std::array<float, 21> covV = {0.}; + std::array<float, 3> pvecV = {0.}; + int q = 0; + for (int it = 0; it < N; it++) { + const auto& trc = getTrack(it, cand); + std::array<float, 3> pvecT = {0.}; + std::array<float, 21> 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]]; + } + for (int i = 0; i < 3; i++) { + pvecV[i] += pvecT[i]; + } + q += trc.getCharge(); + } + auto covVtxV = calcPCACovMatrix(cand); + covV[0] = covVtxV(0, 0); + covV[1] = covVtxV(1, 0); + covV[2] = covVtxV(1, 1); + covV[3] = covVtxV(2, 0); + covV[4] = covVtxV(2, 1); + covV[5] = covVtxV(2, 2); + const std::array<float, 3> vertex = {(float)wvtx[0], (float)wvtx[1], (float)wvtx[2]}; + return std::move(o2::track::TrackParCov(vertex, pvecV, covV, q, sectorAlpha)); +} + +//___________________________________________________________________ +template <int N, typename... Args> +o2::track::TrackPar DCAFitterN<N, Args...>::createParentTrackPar(int cand, bool sectorAlpha) const +{ + const auto& trP = getTrack(0, cand); + const auto& trN = getTrack(1, cand); + const auto& wvtx = getPCACandidate(cand); + std::array<float, 3> pvecV = {0.}; + int q = 0; + for (int it = 0; it < N; it++) { + const auto& trc = getTrack(it, cand); + std::array<float, 3> pvecT = {0.}; + trc.getPxPyPzGlo(pvecT); + for (int i = 0; i < 3; i++) { + pvecV[i] += pvecT[i]; + } + q += trc.getCharge(); + } + const std::array<float, 3> vertex = {(float)wvtx[0], (float)wvtx[1], (float)wvtx[2]}; + return std::move(o2::track::TrackPar(vertex, pvecV, q, sectorAlpha)); +} + using DCAFitter2 = DCAFitterN<2, o2::track::TrackParCov>; using DCAFitter3 = DCAFitterN<3, o2::track::TrackParCov>; diff --git a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h b/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h index 2514830c9769c..97c7abca67de5 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h @@ -25,7 +25,7 @@ namespace track ///__________________________________________________________________________ //< precalculated track radius, center, alpha sin,cos and their combinations -struct TrackAuxPar : public o2::utils::CircleXY { +struct TrackAuxPar : public o2::math_utils::CircleXYf_t { using Track = o2::track::TrackPar; float c, s, cc, ss, cs; // cos ans sin of track alpha and their products diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h new file mode 100644 index 0000000000000..971614f518081 --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h @@ -0,0 +1,171 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexer.h +/// \brief Primary vertex finder +/// \author ruben.shahoyan@cern.ch + +#ifndef O2_PVERTEXER_H +#define O2_PVERTEXER_H + +#include <array> +#include <utility> +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/TimeStamp.h" +#include "CommonDataFormat/BunchFilling.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "MathUtils/Utils.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsFT0/RecPoints.h" +#include "DetectorsVertexing/PVertexerHelpers.h" +#include "DetectorsVertexing/PVertexerParams.h" +#include "FT0Reconstruction/InteractionTag.h" +#include "gsl/span" + +namespace o2 +{ +namespace vertexing +{ + +namespace o2d = o2::dataformats; + +class PVertexer +{ + + public: + enum class FitStatus : int { Failure, + PoolEmpty, + NotEnoughTracks, + IterateFurther, + OK }; + + void init(); + int process(gsl::span<const o2d::TrackTPCITS> tracksITSTPC, gsl::span<const o2::ft0::RecPoints> ft0Data, + std::vector<PVertex>& vertices, std::vector<o2d::VtxTrackIndex>& vertexTrackIDs, std::vector<V2TRef>& v2tRefs, + gsl::span<const o2::MCCompLabel> lblITS, gsl::span<const o2::MCCompLabel> lblTPC, std::vector<o2::MCEventLabel>& lblVtx); + + int process(gsl::span<const o2d::TrackTPCITS> tracksITSTPC, gsl::span<const o2::ft0::RecPoints> ft0Data, + std::vector<PVertex>& vertices, std::vector<o2d::VtxTrackIndex>& vertexTrackIDs, std::vector<V2TRef>& v2tRefs); + + static void createMCLabels(gsl::span<const o2::MCCompLabel> lblITS, gsl::span<const o2::MCCompLabel> lblTPC, + const std::vector<PVertex> vertices, const std::vector<o2d::VtxTrackIndex> vertexTrackIDs, const std::vector<V2TRef> v2tRefs, + std::vector<o2::MCEventLabel>& lblVtx); + bool findVertex(const VertexingInput& input, PVertex& vtx); + + void setStartIR(const o2::InteractionRecord& ir) { mStartIR = ir; } ///< set InteractionRecods for the beginning of the TF + + void setTukey(float t) + { + mTukey2I = t > 0.f ? 1.f / (t * t) : 1.f / (PVertexerParams::kDefTukey * PVertexerParams::kDefTukey); + } + float getTukey() const; + + void finalizeVertex(const VertexingInput& input, const PVertex& vtx, std::vector<PVertex>& vertices, std::vector<V2TRef>& v2tRefs, std::vector<int>& vertexTrackIDs, SeedHisto& histo); + bool setCompatibleIR(PVertex& vtx); + + void setBunchFilling(const o2::BunchFilling& bf); + + void setBz(float bz) { mBz = bz; } + void setValidateWithFT0(bool v) { mValidateWithFT0 = v; } + bool getValidateWithFT0() const { return mValidateWithFT0; } + + auto& getTracksPool() const { return mTracksPool; } + auto& getTimeZClusters() const { return mTimeZClusters; } + auto& getSortedTrackIndices() const { return mSortedTrackID; } + + auto& getMeanVertex() const { return mMeanVertex; } + void setMeanVertex(const o2d::VertexBase& v) + { + mMeanVertex = v; + initMeanVertexConstraint(); + } + + float estimateScale2() + { + auto sc = mPVParams->zHistoBinSize * mPVParams->zHistoBinSize * mTukey2I / (mStatZErr.getMean() * mStatZErr.getMean()); + return sc; + } + + private: + FitStatus fitIteration(const VertexingInput& input, VertexSeed& vtxSeed); + void accountTrack(TrackVF& trc, VertexSeed& vtxSeed) const; + bool solveVertex(VertexSeed& vtxSeed) const; + FitStatus evalIterations(VertexSeed& vtxSeed, PVertex& vtx) const; + TimeEst timeEstimate(const VertexingInput& input) const; + float findZSeedHistoPeak() const; + void initMeanVertexConstraint(); + void applyConstraint(VertexSeed& vtxSeed) const; + bool upscaleSigma(VertexSeed& vtxSeed) const; + void createTracksPool(gsl::span<const o2d::TrackTPCITS> tracksITSTPC); + int findVertices(const VertexingInput& input, std::vector<PVertex>& vertices, std::vector<int>& vertexTrackIDs, std::vector<V2TRef>& v2tRefs); + std::pair<int, int> getBestFT0Trigger(const PVertex& vtx, gsl::span<const o2::ft0::RecPoints> ft0Data, int& currEntry) const; + + int dbscan_RangeQuery(int idxs, std::vector<int>& cand, const std::vector<int>& status); + void dbscan_clusterize(); + + o2::BunchFilling mBunchFilling; + std::array<int16_t, o2::constants::lhc::LHCMaxBunches> mClosestBunchAbove; // closest filled bunch from above + std::array<int16_t, o2::constants::lhc::LHCMaxBunches> mClosestBunchBelow; // closest filled bunch from below + o2d::VertexBase mMeanVertex{{0., 0., 0.}, {0.1 * 0.1, 0., 0.1 * 0.1, 0., 0., 6. * 6.}}; + std::array<float, 3> mXYConstraintInvErr = {1.0f, 0.f, 1.0f}; ///< nominal vertex constraint inverted errors^2 + // + o2::math_utils::StatAccumulator mStatZErr; + o2::math_utils::StatAccumulator mStatTErr; + std::vector<TrackVF> mTracksPool; ///< tracks in internal representation used for vertexing + std::vector<int> mSortedTrackID; ///< indices of tracks sorted in time + std::vector<TimeZCluster> mTimeZClusters; ///< set of time clusters + std::vector<int> mClusterTrackIDs; ///< IDs of tracks making the clusters + + float mBz = 0.; ///< mag.field at beam line + bool mValidateWithFT0 = false; ///< require vertex validation with FT0 (if available) + + o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF + + ///========== Parameters to be set externally, e.g. from CCDB ==================== + const PVertexerParams* mPVParams = nullptr; + const o2::ft0::InteractionTag* mFT0Params = nullptr; + float mTukey2I = 0; ///< 1./[Tukey parameter]^2 + static constexpr float kDefTukey = 5.0f; ///< def.value for tukey constant + static constexpr float kHugeF = 1.e12; ///< very large float + static constexpr float kAlmost0F = 1e-12; ///< tiny float + static constexpr double kAlmost0D = 1e-16; ///< tiny double + + ClassDefNV(PVertexer, 1); +}; + +//___________________________________________________________________ +inline void PVertexer::applyConstraint(VertexSeed& vtxSeed) const +{ + // impose meanVertex constraint, i.e. account terms + // (V_i-Constrain_i)^2/sig2constr_i for i=X,Y in the fit chi2 definition + vtxSeed.cxx += mXYConstraintInvErr[0]; + vtxSeed.cyy += mXYConstraintInvErr[1]; + vtxSeed.cx0 += mXYConstraintInvErr[0] * mMeanVertex.getX(); + vtxSeed.cy0 += mXYConstraintInvErr[1] * mMeanVertex.getY(); +} + +//___________________________________________________________________ +inline bool PVertexer::upscaleSigma(VertexSeed& vtxSeed) const +{ + // scale upward the scaleSigma2 if needes + if (vtxSeed.scaleSigma2 < mPVParams->maxScale2) { + auto s = vtxSeed.scaleSigma2 * mPVParams->upscaleFactor; + vtxSeed.setScale(s > mPVParams->maxScale2 ? mPVParams->maxScale2 : s, mTukey2I); + return true; + } + return false; +} + +} // namespace vertexing +} // namespace o2 +#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexerHelpers.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexerHelpers.h new file mode 100644 index 0000000000000..a38bf5ff37531 --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexerHelpers.h @@ -0,0 +1,322 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexerHelpers.h +/// \brief Primary vertex finder helper classes +/// \author ruben.shahoyan@cern.ch + +#ifndef O2_PVERTEXER_HELPERS_H +#define O2_PVERTEXER_HELPERS_H + +#include "gsl/span" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "CommonDataFormat/TimeStamp.h" + +namespace o2 +{ +namespace vertexing +{ + +using PVertex = o2::dataformats::PrimaryVertex; +using TimeEst = o2::dataformats::TimeStampWithError<float, float>; +using V2TRef = o2::dataformats::VtxTrackRef; +using GIndex = o2::dataformats::VtxTrackIndex; + +///< weights and scaling params for current vertex +struct VertexSeed : public PVertex { + double wghSum = 0.; // sum of tracks weights + double wghChi2 = 0.; // sum of tracks weighted chi2's + double tMeanAcc = 0.; // sum of track times * inv.err^2 + double tMeanAccErr = 0.; // some of tracks times inv.err^2 + double cxx = 0., cyy = 0., czz = 0., cxy = 0., cxz = 0., cyz = 0., cx0 = 0., cy0 = 0., cz0 = 0.; // elements of lin.equation matrix + float scaleSigma2 = 1.; // scaling parameter on top of Tukey param + float scaleSigma2Prev = 1.; + float maxScaleSigma2Tested = 0.; + float scaleSig2ITuk2I = 0; // inverse squared Tukey parameter scaled by scaleSigma2 + int nScaleSlowConvergence = 0; + int nScaleIncrease = 0; + int nIterations = 0; + bool useConstraint = true; + bool fillErrors = true; + + void setScale(float scale2, float tukey2I) + { + scaleSigma2Prev = scaleSigma2; + scaleSigma2 = scale2; + scaleSig2ITuk2I = tukey2I / scale2; + } + + void resetForNewIteration() + { + setNContributors(0); + //setTimeStamp({0., 0.}); + wghSum = 0; + wghChi2 = 0; + tMeanAcc = 0; + tMeanAccErr = 0; + cxx = cyy = czz = cxy = cxz = cyz = cx0 = cy0 = cz0 = 0.; + } + + VertexSeed() = default; + VertexSeed(const PVertex& vtx, bool _constraint, bool _errors) + : PVertex(vtx), useConstraint(_constraint), fillErrors(_errors) {} + + void print() const; +}; + +struct TrackVF { + /** Straight track parameterization in the frame defined by alpha angle. + Assumed to be defined in the proximity to vertex, so that the + straight-line extrapolation Y=mY+mTgP*(x-mX) and Z=mZ+mTgL*(x-mX) is + precise + */ + enum { kUsed, + kNoVtx = -1, + kDiscarded = kNoVtx - 1 }; + float x; ///< reference X + float y; ///< Y at X + float z; ///< Z at X + float sig2YI; ///< YY component of inverse cov.matrix + float sig2ZI; ///< ZZ component of inverse cov.matrix + float sigYZI; ///< YZ component of inverse cov.matrix + float tgP; ///< tangent(phi) in tracking frame + float tgL; ///< tangent(lambda) + float cosAlp; ///< cos of alpha frame + float sinAlp; ///< sin of alpha frame + + TimeEst timeEst; + float wgh = 0.; ///< track weight wrt current vertex seed + uint32_t entry = 0; + int16_t bin = -1; // seeds histo bin + uint8_t srcID = 0; + uint8_t flags = 0; + int vtxID = kNoVtx; ///< assigned vertex + + // + bool canAssign() const { return wgh > 0. && vtxID == kNoVtx; } + bool canUse() const { return vtxID == kNoVtx; } + bool canUse(float zmin, float zmax) const + { + return canUse() && (z > zmin && z < zmax); + } + bool operator<(const TrackVF& trc) const { return z < trc.z; } + + float getZForXY(float vx, float vy) const + { + return z + tgL * (vx * cosAlp + vy * sinAlp - x); + } + + // weighted distance^2 to other track (accounting for own errors only) + float getDist2(const TrackVF& o) const + { + auto dtnorm2 = (timeEst.getTimeStamp() - o.timeEst.getTimeStamp()) / timeEst.getTimeStampError(); + auto dz = z - o.z; + return dtnorm2 + dz * dz * sig2ZI; + } + + // weighted distance^2 to other track (accounting for both track errors errors only) + float getDist2Sym(const TrackVF& o) const + { + auto dt = timeEst.getTimeStamp() - o.timeEst.getTimeStamp(); + auto dz = z - o.z; + float dte2 = o.timeEst.getTimeStampError() * o.timeEst.getTimeStampError() + timeEst.getTimeStampError() * timeEst.getTimeStampError(); + return dt / dte2 + dz * dz / (1. / sig2ZI + 1. / o.sig2ZI); + } + + float getResiduals(const PVertex& vtx, float& dy, float& dz) const + { + // get residuals (Y and Z DCA in track frame) and calculate chi2 + float dx = vtx.getX() * cosAlp + vtx.getY() * sinAlp - x; // VX rotated to track frame - trackX + dy = y + tgP * dx - (-vtx.getX() * sinAlp + vtx.getY() * cosAlp); + dz = z + tgL * dx - vtx.getZ(); + return (dy * dy * sig2YI + dz * dz * sig2ZI) + 2. * dy * dz * sigYZI; + } + + TrackVF() = default; + TrackVF(const o2::track::TrackParCov& src, const TimeEst& t_est, uint32_t _entry, uint8_t _srcID) + : x(src.getX()), y(src.getY()), z(src.getZ()), tgL(src.getTgl()), tgP(src.getSnp() / std::sqrt(1. - src.getSnp()) * (1. + src.getSnp())), timeEst(t_est), entry(_entry), srcID(_srcID) + { + o2::math_utils::sincos(src.getAlpha(), sinAlp, cosAlp); + auto det = src.getSigmaY2() * src.getSigmaZ2() - src.getSigmaZY() * src.getSigmaZY(); + auto detI = 1. / det; + sig2YI = src.getSigmaZ2() * detI; + sig2ZI = src.getSigmaY2() * detI; + sigYZI = -src.getSigmaZY() * detI; + } +}; + +struct VertexingInput { + gsl::span<int> idRange; + TimeEst timeEst{0, -1.}; // negative error means don't use time info + float scaleSigma2 = 10; + bool useConstraint = false; + bool fillErrors = true; +}; + +struct SeedHisto { + float range = 20; + float binSize = 0.5; + float binSizeInv = 0.; + int nFilled = 0; + std::vector<int> data; + + SeedHisto() = delete; + SeedHisto(float _range = 20., float _binsize = 0.5) : range(_range), binSize(_binsize) + { + auto zr = 2 * range; + int nzb = zr / binSize; + if (nzb * binSize < zr - 1e-9) { + nzb++; + } + binSizeInv = 1. / binSize; + range = nzb * binSize / 2.; + data.resize(nzb); + } + + int size() const { return data.size(); } + + void fill(float z) + { + incrementBin(findBin(z)); + } + + void incrementBin(int bin) + { + data[bin]++; + nFilled++; + } + + void decrementBin(int bin) + { + data[bin]--; + nFilled--; + } + + int findBin(float z) + { + auto d = z + range; + if (d < 0.) { + return 0; + } + uint32_t n = d * binSizeInv; + return n < data.size() ? n : data.size() - 1; + } + + int findHighestPeakBin() const + { + if (nFilled < 2) { + return -1; + } + int n = data.size(), maxBin = -1, maxv = 0; + for (int i = 0; i < n; i++) { + if (data[i] > maxv) { + maxv = data[(maxBin = i)]; + } + } + return maxBin; + } + + bool isValidBin(int ib) const + { + return static_cast<uint32_t>(ib) < data.size(); + } + + float getBinCenter(int ib) const + { + return (ib + 0.5) * binSize - range; // no check for being in the range!!! + } + + void discardBin(int ib) + { // no check for being in the range!!! + nFilled -= data[ib]; + data[ib] = 0; + } +}; + +struct TimeZCluster { + TimeEst timeEst; + int first = -1; + int last = -1; + int count = 0; + + void clear() + { + first = last = -1; + count = 0; + } + + void addTrack(int i, const TimeEst& trcT) + { + auto trcTErr2 = trcT.getTimeStampError() * trcT.getTimeStampError(); + auto trcTErr2Inv = 1. / trcTErr2; + if (first < 0) { + first = last = i; + timeEst.setTimeStamp(trcT.getTimeStamp()); + timeEst.setTimeStampError(trcT.getTimeStampError()); + } else { + auto vtxTErr2Inv = 1. / (timeEst.getTimeStampError() * timeEst.getTimeStampError()); + auto vtxTErr2UpdInv = trcTErr2Inv + vtxTErr2Inv; + auto vtxTErr2Upd = 1. / vtxTErr2UpdInv; + timeEst.setTimeStamp((timeEst.getTimeStamp() * vtxTErr2Inv + trcT.getTimeStamp() * trcTErr2Inv) * vtxTErr2Upd); + timeEst.setTimeStampError(std::sqrt(vtxTErr2Upd)); + if (i > last) { + last = i; + } + } + count++; + } + + bool isCompatible(const TimeEst& c, float margin, float cut) const + { + if (first < 0) { + return true; + } + float dt = timeEst.getTimeStamp() - c.getTimeStamp(); + if (c.getTimeStampError() && timeEst.getTimeStampError()) { + float trcTErr2 = c.getTimeStampError() * c.getTimeStampError(); + float err = trcTErr2 + timeEst.getTimeStampError() + margin; + return dt * dt / err < cut; + } else { + return std::abs(dt) < cut; + } + } + + void merge(TimeZCluster& c) + { + if (c.first < last) { + first = c.first; + } else { + last = c.last; + } + if (timeEst.getTimeStampError() && c.timeEst.getTimeStampError()) { // weighted average + auto cTErr2 = c.timeEst.getTimeStampError() * c.timeEst.getTimeStampError(); + auto cTErr2Inv = 1. / cTErr2; + + auto tErr2 = timeEst.getTimeStampError(); + auto tErr2Inv = 1. / (tErr2 * tErr2); + auto tErr2UpdInv = cTErr2Inv + tErr2Inv; + auto tErr2Upd = 1. / tErr2UpdInv; + timeEst.setTimeStamp((timeEst.getTimeStamp() * tErr2Inv + c.timeEst.getTimeStamp() * cTErr2Inv) * tErr2Upd); + timeEst.setTimeStampError(std::sqrt(tErr2Upd)); + } else { + timeEst.setTimeStamp((timeEst.getTimeStamp() * count + c.timeEst.getTimeStamp() * c.count) / (count + c.count)); + count += c.count; + c.count = 0; + } + } +}; + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexerParams.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexerParams.h new file mode 100644 index 0000000000000..22e6d18a5bc43 --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexerParams.h @@ -0,0 +1,73 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 + +#ifndef ALICEO2_PVERTEXER_PARAMS_H +#define ALICEO2_PVERTEXER_PARAMS_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace vertexing +{ + +// These are configurable params for Primary Vertexer +struct PVertexerParams : public o2::conf::ConfigurableParamHelper<PVertexerParams> { + static constexpr float kDefTukey = 5.0f; ///< def.value for tukey constant + + // DBSCAN clustering settings + float dbscanMaxDist2 = 9.; ///< distance^2 cut (eps^2). + float dbscanDeltaT = 10.; ///< abs. time difference cut + float dbscanAdaptCoef = 0.1; ///< adapt dbscan minPts for each cluster as minPts=max(minPts, currentSize*dbscanAdaptCoef). + + int maxVerticesPerCluster = 1; ///< max vertices per time-z cluster to look for + int maxTrialsPerCluster = 3; ///< max unsucessful trials for vertex search per vertex + + // track selection + float dcaTolerance = 1.3; ///< consider tracks within this abs DCA to mean vertex + float pullIniCut = 9; ///< cut on pull (n^2 sigma) on dca to mean vertex + + // parameters + float zHistoRange = 20.; ///< +-range of the Zseed histo + float zHistoBinSize = 0.5; ///< size of the Zseed histo bin + float tukey = kDefTukey; ///< 1./[Tukey parameter]^2 + float minScale2 = 1.; ///< min slaling factor^2 + float acceptableScale2 = 4.; ///< if below this factor, try to refit with minScale2 + float maxScale2 = 1.e6; ///< max slaling factor^2 + float upscaleFactor = 9.; ///< factor for upscaling if not candidate is found + float slowConvergenceFactor = 0.5; ///< consider convergence as slow if ratio new/old scale2 exceeds it + // + // validation with FT0 + bool requireFT0ValidTimeMean = false; //true;///< require both FT0A/C + int minNContributorsForFT0cut = 4; ///< do not apply FT0 cut to vertice below FT0 efficiency threshold + float maxTError = 0.2; ///< use min of vertex time error or this for nsigma evaluation + float minTError = 0.003; ///< don't use error smaller than that (~BC/2/minNContributorsForFT0cut) + float nSigmaTimeCut = 4.; ///< eliminate vertex if there is no FT0 signal within this cut + float timeBiasMS = 0; ///< relative bias in ms to add to TPCITS-based time stamp + + // + // stopping condition params + float maxChi2Mean = 10.; ///< max mean chi2 of vertex to accept + int minTracksPerVtx = 2; ///< min N tracks per vertex + int maxIterations = 20; ///< max iterations per vertex fit + int maxNScaleIncreased = 2; ///< max number of scaling-non-decreasing iterations + int maxNScaleSlowConvergence = 3; ///< max number of weak scaling decrease iterations + bool useTimeInChi2 = true; ///< use track-vertex time difference in chi2 calculation + + O2ParamDef(PVertexerParams, "pvertexer"); +}; + +} // namespace vertexing +} // end namespace o2 + +#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h new file mode 100644 index 0000000000000..db00b27d0c735 --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h @@ -0,0 +1,120 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SVertexer.h +/// \brief Secondary vertex finder +/// \author ruben.shahoyan@cern.ch +#ifndef O2_S_VERTEXER_H +#define O2_S_VERTEXER_H + +#include "gsl/span" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsITS/TrackITS.h" +#include "CommonDataFormat/RangeReference.h" +#include "DetectorsVertexing/DCAFitterN.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "DetectorsVertexing/V0Hypothesis.h" + +namespace o2 +{ +namespace vertexing +{ + +namespace o2d = o2::dataformats; + +class SVertexer +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + using VRef = o2::dataformats::VtxTrackRef; + using PVertex = const o2::dataformats::PrimaryVertex; + using V0 = o2::dataformats::V0; + using TrackTPCITS = o2::dataformats::TrackTPCITS; + using TrackITS = o2::its::TrackITS; + using TrackTPC = o2::tpc::TrackTPC; + using RRef = o2::dataformats::RangeReference<int, int>; + + // struct to access tracks and extra info from different sources + struct TrackAccessor { + static constexpr std::array<size_t, GIndex::NSources> sizes{sizeof(TrackTPCITS), sizeof(TrackITS), sizeof(TrackTPC)}; + std::array<const char*, GIndex::NSources> startOfSource{}; + std::array<std::vector<char>, GIndex::NSources> charges; + + TrackAccessor(const gsl::span<const TrackTPCITS>& tpcits, const gsl::span<const TrackITS>& its, const gsl::span<const TrackTPC>& tpc) + { + if (tpcits.size()) { + startOfSource[GIndex::TPCITS] = reinterpret_cast<const char*>(tpcits.data()); + auto& ch = charges[GIndex::TPCITS]; + ch.resize(tpcits.size()); + for (uint32_t ic = 0; ic < tpcits.size(); ic++) { + ch[ic] = tpcits[ic].getCharge(); + } + } + if (its.size()) { + startOfSource[GIndex::ITS] = reinterpret_cast<const char*>(its.data()); + auto& ch = charges[GIndex::ITS]; + ch.resize(its.size()); + for (uint32_t ic = 0; ic < its.size(); ic++) { + ch[ic] = its[ic].getCharge(); + } + } + if (tpc.size()) { + startOfSource[GIndex::TPC] = reinterpret_cast<const char*>(tpc.data()); + auto& ch = charges[GIndex::TPC]; + ch.resize(tpc.size()); + for (uint32_t ic = 0; ic < tpc.size(); ic++) { + ch[ic] = tpc[ic].getCharge(); + } + } + } + char getCharge(GIndex id) const { return getCharge(id.getSource(), id.getIndex()); } + char getCharge(int src, int idx) const { return charges[src][idx]; } + const o2::track::TrackParCov& getTrack(GIndex id) const { return getTrack(id.getSource(), id.getIndex()); } + const o2::track::TrackParCov& getTrack(int src, int idx) const { return *reinterpret_cast<const o2::track::TrackParCov*>(startOfSource[src] + sizes[src] * idx); } + }; + + void init(); + void process(const gsl::span<const PVertex>& vertices, // primary vertices + const gsl::span<const GIndex>& trackIndex, // Global ID's for associated tracks + const gsl::span<const VRef>& vtxRefs, // references from vertex to these track IDs + const gsl::span<const TrackTPCITS>& tpcits, // global tracks + const gsl::span<const TrackITS>& its, // ITS tracks + const gsl::span<const TrackTPC>& tpc, // TPC tracks + std::vector<V0>& v0s, // found V0s + std::vector<RRef>& vtx2V0refs // references from PVertex to V0 + ); + + auto& getMeanVertex() const { return mMeanVertex; } + void setMeanVertex(const o2d::VertexBase& v) { mMeanVertex = v; } + + private: + uint64_t getPairIdx(GIndex id1, GIndex id2) const + { + return (uint64_t(id1) << 32) | id2; + } + o2d::VertexBase mMeanVertex{{0., 0., 0.}, {0.1 * 0.1, 0., 0.1 * 0.1, 0., 0., 6. * 6.}}; + const SVertexerParams* mSVParams = nullptr; + std::array<V0Hypothesis, SVertexerParams::NPIDV0> mV0Hyps; + DCAFitterN<2> mFitter2Prong; + + float mMinR2ToMeanVertex = 0; + float mMaxDCAXY2ToMeanVertex = 0; + float mMinCosPointingAngle = 0; +}; + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/SVertexerParams.h b/Detectors/Vertexing/include/DetectorsVertexing/SVertexerParams.h new file mode 100644 index 0000000000000..32cd94034b994 --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/SVertexerParams.h @@ -0,0 +1,66 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SVertexerParams.h +/// \brief Configurable params for secondary vertexer +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_SVERTEXER_PARAMS_H +#define ALICEO2_SVERTEXER_PARAMS_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace vertexing +{ + +// These are configurable params for Primary Vertexer +struct SVertexerParams : public o2::conf::ConfigurableParamHelper<SVertexerParams> { + + enum PIDV0 { Photon, + K0, + Lambda, + AntiLambda, + HyperTriton, + AntiHyperTriton, + NPIDV0 }; + enum PIDParams { SigmaMV0, + NSigmaMV0, + Margin, + CPt, + NPIDParams }; + // parameters + float maxChi2 = 2.; ///< max dca from prongs to vertex + float minParamChange = 1e-3; ///< stop when tracks X-params being minimized change by less that this value + float minRelChi2Change = 0.9; ///< stop when chi2 changes by less than this value + float maxDZIni = 5.; ///< don't consider as a seed (circles intersection) if Z distance exceeds this + float maxRIni = 150; ///< don't consider as a seed (circles intersection) if its R exceeds this + + bool useAbsDCA = true; ///< use abs dca minimization + // + float minRfromMeanVertex = 0.5; ///< min radial distance of V0 from beam line (mean vertex) + float maxDCAXYfromMeanVertex = 0.2; ///< min DCA of V0 from beam line (mean vertex) + float minCosPointingAngle = 0.8; + + // cuts on different PID params + float pidCutsPhoton[NPIDParams] = {0.001, 20, 0.60, 0.0}; // Photon + float pidCutsK0[NPIDParams] = {0.003, 20, 0.07, 0.5}; // K0 + float pidCutsLambda[NPIDParams] = {0.001, 20, 0.07, 0.5}; // Lambda + float pidCutsHTriton[NPIDParams] = {0.0025, 14, 0.07, 0.5}; // HyperTriton + + O2ParamDef(SVertexerParams, "svertexer"); +}; + +} // namespace vertexing +} // end namespace o2 + +#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/V0Hypothesis.h b/Detectors/Vertexing/include/DetectorsVertexing/V0Hypothesis.h new file mode 100644 index 0000000000000..8a95d9d6a6d9e --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/V0Hypothesis.h @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V0Hypothesis.h +/// \brief V0 hypothesis checker +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_V0_HYPOTHESIS_H +#define ALICEO2_V0_HYPOTHESIS_H + +#include "ReconstructionDataFormats/PID.h" +#include "DetectorsVertexing/SVertexerParams.h" + +namespace o2 +{ +namespace vertexing +{ + +class V0Hypothesis +{ + + public: + using PID = o2::track::PID; + + void set(PID v0, PID ppos, PID pneg, float sig, float nSig, float margin, float cpt, float bz = 0.f); + void set(PID v0, PID ppos, PID pneg, const float pars[SVertexerParams::NPIDParams], float bz = 0.f); + + float getMassV0Hyp() const { return PID::getMass(mPIDV0); } + float getMassPosProng() const { return PID::getMass(mPIDPosProng); } + float getMassNegProng() const { return PID::getMass(mPIDNegProng); } + + float calcMass2(float p2Pos, float p2Neg, float p2V0) const + { + // calculate v0 mass from squared momentum of its prongs and total momentum + float ePos = std::sqrt(p2Pos + getMass2PosProng()), eNeg = std::sqrt(p2Neg + getMass2NegProng()), eV0 = ePos + eNeg; + return eV0 * eV0 - p2V0; + } + + float calcMass(float p2Pos, float p2Neg, float p2V0) const { return std::sqrt(calcMass2(p2Pos, p2Neg, p2V0)); } + + bool check(float p2Pos, float p2Neg, float p2V0, float ptV0) const + { // check if given mass and pt is matching to hypothesis + return check(calcMass(p2Pos, p2Neg, p2V0), ptV0); + } + + bool check(float mass, float pt) const + { // check if given mass and pt is matching to hypothesis + return std::abs(mass - getMassV0Hyp()) < getMargin(pt); + } + + float getSigma(float pt) const { return 1.f + mCPt * pt; } + float getMargin(float pt) const { return mNSigma * getSigma(pt) + mMargin; } + + private: + float getMass2PosProng() const { return PID::getMass2(mPIDPosProng); } + float getMass2NegProng() const { return PID::getMass2(mPIDNegProng); } + + PID mPIDV0 = PID::K0; + PID mPIDPosProng = PID::Pion; + PID mPIDNegProng = PID::Pion; + + float mNSigma = 0.; // number of sigmas of mass res + float mSigma = 0.; // sigma of mass res at 0 pt + float mMargin = 0.; // additive safety margin in mass cut + float mCPt = 0.; // pT dependence of mass resolution parameterized as mSigma*(1+mC1*pt); + + ClassDefNV(V0Hypothesis, 1); +}; + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/VertexTrackMatcher.h b/Detectors/Vertexing/include/DetectorsVertexing/VertexTrackMatcher.h new file mode 100644 index 0000000000000..958ca4735a0bd --- /dev/null +++ b/Detectors/Vertexing/include/DetectorsVertexing/VertexTrackMatcher.h @@ -0,0 +1,89 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VertexTrackMatcher.h +/// \brief Class for vertex track association +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_VERTEX_TRACK_MATCHER_ +#define ALICEO2_VERTEX_TRACK_MATCHER_ + +#include "gsl/span" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DetectorsVertexing/PVertexerParams.h" +#include "MathUtils/Primitive2D.h" + +namespace o2 +{ +namespace vertexing +{ + +class VertexTrackMatcher +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + using VRef = o2::dataformats::VtxTrackRef; + using PVertex = const o2::dataformats::PrimaryVertex; + using TrackTPCITS = o2::dataformats::TrackTPCITS; + using TrackITS = o2::its::TrackITS; + using ITSROFR = o2::itsmft::ROFRecord; + using TrackTPC = o2::tpc::TrackTPC; + using TmpMap = std::unordered_map<int, std::vector<GIndex>>; + using TimeEst = o2::dataformats::TimeStampWithError<float, float>; + using TBracket = o2::math_utils::Bracketf_t; + + void init(); + void process(const gsl::span<const PVertex>& vertices, // vertices + const gsl::span<const GIndex>& v2tfitIDs, // IDs of contributor tracks used in fit + const gsl::span<const VRef>& v2tfitRefs, // references on these tracks (we used special reference with multiple sources, but currently only TPCITS used) + const gsl::span<const TrackTPCITS>& tpcits, // global tracks + const gsl::span<const TrackITS>& its, // ITS tracks + const gsl::span<const ITSROFR>& itsROFR, // ITS tracks ROFRecords + const gsl::span<const TrackTPC>& tpc, // TPC tracks + std::vector<GIndex>& trackIndex, // Global ID's for associated tracks + std::vector<VRef>& vtxRefs); // references on these tracks + + ///< set InteractionRecods for the beginning of the TF + void setStartIR(const o2::InteractionRecord& ir) { mStartIR = ir; } + void setITSROFrameLengthInBC(int nbc); + int getITSROFrameLengthInBC() const { return mITSROFrameLengthInBC; } + + private: + void attachTPCITS(TmpMap& tmpMap, const gsl::span<const TrackTPCITS>& tpcits, const std::vector<int>& idTPCITS, const gsl::span<const PVertex>& vertices); + void attachITS(TmpMap& tmpMap, const gsl::span<const TrackITS>& its, const gsl::span<const ITSROFR>& itsROFR, const std::vector<int>& flITS, + const gsl::span<const PVertex>& vertices, std::vector<int>& idxVtx); + void attachTPC(TmpMap& tmpMap, const std::vector<TBracket>& tpcTimes, const std::vector<int>& idTPC, const gsl::span<const PVertex>& vertices, std::vector<int>& idVtx); + bool compatibleTimes(const TimeEst& vtxT, const TimeEst& trcT) const; + void updateTPCTimeDependentParams(); + float tpcTimeBin2MUS(float t) + { // convert TPC time bin to microseconds + return t * mTPCBin2MUS; + } + + o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF + int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) + float mMaxTPCDriftTimeMUS = 0; + float mTPCBin2MUS = 0; + const o2::vertexing::PVertexerParams* mPVParams = nullptr; + + ClassDefNV(VertexTrackMatcher, 1); +}; + +} // namespace vertexing +} // namespace o2 + +#endif diff --git a/Detectors/Vertexing/src/DetectorsVertexingLinkDef.h b/Detectors/Vertexing/src/DetectorsVertexingLinkDef.h index 94c2ba3af839e..14703c3d763b6 100644 --- a/Detectors/Vertexing/src/DetectorsVertexingLinkDef.h +++ b/Detectors/Vertexing/src/DetectorsVertexingLinkDef.h @@ -19,6 +19,15 @@ // this aliase are defined in the DCAFitterN.h as o2::vertexing::DCAFitterN<3,o2::track::TrackParCov> #pragma link C++ class o2::vertexing::DCAFitter3 + ; +#pragma link C++ class o2::vertexing::PVertexer + ; +#pragma link C++ class o2::vertexing::PVertexerParams + ; +#pragma link C++ class o2::vertexing::VertexTrackMatcher + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::vertexing::PVertexerParams> + ; + +#pragma link C++ class o2::vertexing::SVertexer + ; +#pragma link C++ class o2::vertexing::SVertexerParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::vertexing::SVertexerParams> + ; + #pragma link C++ class o2::track::TrackAuxPar + ; #pragma link C++ class o2::track::CrossInfo + ; diff --git a/Detectors/Vertexing/src/PVertexer.cxx b/Detectors/Vertexing/src/PVertexer.cxx new file mode 100644 index 0000000000000..8226e59e0b47c --- /dev/null +++ b/Detectors/Vertexing/src/PVertexer.cxx @@ -0,0 +1,756 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexer.cxx +/// \brief Primary vertex finder +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/PVertexer.h" +#include "ReconstructionDataFormats/DCA.h" +#include "DetectorsBase/Propagator.h" +#include "Math/SMatrix.h" +#include "Math/SVector.h" +#include <numeric> +#include <unordered_map> +#include <TStopwatch.h> + +using namespace o2::vertexing; + +constexpr float PVertexer::kAlmost0F; +constexpr double PVertexer::kAlmost0D; +constexpr float PVertexer::kHugeF; + +//___________________________________________________________________ +int PVertexer::process(gsl::span<const o2d::TrackTPCITS> tracksITSTPC, gsl::span<const o2::ft0::RecPoints> ft0Data, + std::vector<PVertex>& vertices, std::vector<GIndex>& vertexTrackIDs, std::vector<V2TRef>& v2tRefs) +{ + createTracksPool(tracksITSTPC); + dbscan_clusterize(); + + std::vector<PVertex> verticesLoc; + std::vector<int> vertexTrackIDsLoc; + std::vector<V2TRef> v2tRefsLoc; + std::vector<float> validationTimes; + + for (auto tc : mTimeZClusters) { + VertexingInput inp; + // inp.idRange = gsl::span<int>((int*)&mSortedTrackID[tc.first], tc.count); + inp.idRange = gsl::span<int>((int*)&mClusterTrackIDs[tc.first], tc.count); + inp.scaleSigma2 = 3. * estimateScale2(); + inp.timeEst = tc.timeEst; + findVertices(inp, verticesLoc, vertexTrackIDsLoc, v2tRefsLoc); + } + + // sort in time + std::vector<int> vtTimeSortID(verticesLoc.size()); + std::iota(vtTimeSortID.begin(), vtTimeSortID.end(), 0); + std::sort(vtTimeSortID.begin(), vtTimeSortID.end(), [&verticesLoc](int i, int j) { + return verticesLoc[i].getTimeStamp().getTimeStamp() < verticesLoc[j].getTimeStamp().getTimeStamp(); + }); + + vertices.clear(); + v2tRefs.clear(); + vertexTrackIDs.clear(); + vertices.reserve(verticesLoc.size()); + v2tRefs.reserve(v2tRefsLoc.size()); + vertexTrackIDs.reserve(vertexTrackIDsLoc.size()); + + int trCopied = 0, count = 0, vtimeID = 0; + for (auto i : vtTimeSortID) { + auto& vtx = verticesLoc[i]; + + bool irSet = setCompatibleIR(vtx); + if (!irSet) { + continue; + } + // do we need to validate by FT0 ? + if (mValidateWithFT0) { + auto bestMatch = getBestFT0Trigger(vtx, ft0Data, vtimeID); + if (bestMatch.first >= 0) { + vtx.setFlags(PVertex::TimeValidated); + if (bestMatch.second == 1) { + vtx.setIR(ft0Data[bestMatch.first].getInteractionRecord()); + } + LOG(DEBUG) << "Validated with t0 " << bestMatch.first << " with " << bestMatch.second << " candidates"; + } else if (vtx.getNContributors() >= mPVParams->minNContributorsForFT0cut) { + LOG(DEBUG) << "Discarding " << vtx; + continue; // reject + } + } + vertices.push_back(vtx); + int it = v2tRefsLoc[i].getFirstEntry(), itEnd = it + v2tRefsLoc[i].getEntries(), dest0 = vertexTrackIDs.size(); + for (; it < itEnd; it++) { + auto& gid = vertexTrackIDs.emplace_back(vertexTrackIDsLoc[it], GIndex::TPCITS); + gid.setPVContributor(); + } + v2tRefs.emplace_back(dest0, v2tRefsLoc[i].getEntries()); + LOG(DEBUG) << "#" << count++ << " " << vertices.back() << " | " << v2tRefs.back().getEntries() << " indices from " << v2tRefs.back().getFirstEntry(); // RS REM + } + + return vertices.size(); +} + +//___________________________________________________________________ +int PVertexer::process(gsl::span<const o2d::TrackTPCITS> tracksITSTPC, gsl::span<const o2::ft0::RecPoints> ft0Data, + std::vector<PVertex>& vertices, std::vector<GIndex>& vertexTrackIDs, std::vector<V2TRef>& v2tRefs, + gsl::span<const o2::MCCompLabel> lblITS, gsl::span<const o2::MCCompLabel> lblTPC, std::vector<o2::MCEventLabel>& lblVtx) +{ + auto nv = process(tracksITSTPC, ft0Data, vertices, vertexTrackIDs, v2tRefs); + if (lblITS.size() && lblTPC.size()) { + createMCLabels(lblITS, lblTPC, vertices, vertexTrackIDs, v2tRefs, lblVtx); + } + return nv; +} + +//______________________________________________ +int PVertexer::findVertices(const VertexingInput& input, std::vector<PVertex>& vertices, std::vector<int>& vertexTrackIDs, std::vector<V2TRef>& v2tRefs) +{ + // find vertices using tracks with indices (sorted in time) from idRange from "tracks" pool. The pool may containt arbitrary number of tracks, + // only those which are in the idRange and have canUse()==true, will be used. + // Results are placed in vertices and v2tRefs vectors + int nfound = 0, ntr = input.idRange.size(); + if (ntr < mPVParams->minTracksPerVtx) { + return nfound; + } + // + SeedHisto seedHisto(mPVParams->zHistoRange, mPVParams->zHistoBinSize); + + for (int i : input.idRange) { + if (mTracksPool[i].canUse()) { + mTracksPool[i].bin = seedHisto.findBin(mTracksPool[i].getZForXY(mMeanVertex.getX(), mMeanVertex.getY())); + seedHisto.incrementBin(mTracksPool[i].bin); + } + } + if (seedHisto.nFilled < mPVParams->minTracksPerVtx) { + return nfound; + } + LOG(DEBUG) << "New cluster: times: " << mTracksPool[input.idRange[0]].timeEst.getTimeStamp() << " : " << mTracksPool[input.idRange[ntr - 1]].timeEst.getTimeStamp() << " nfound " << nfound << " of " << ntr; // RSTMP + int nTrials = 0; + while (nfound < mPVParams->maxVerticesPerCluster && nTrials < mPVParams->maxTrialsPerCluster) { + int peakBin = seedHisto.findHighestPeakBin(); // find next seed + if (!seedHisto.isValidBin(peakBin)) { + break; + } + float zv = seedHisto.getBinCenter(peakBin); + LOG(DEBUG) << "Seeding with Z=" << zv << " bin " << peakBin << " on trial " << nTrials << " for vertex " << nfound; + PVertex vtx; + vtx.setXYZ(mMeanVertex.getX(), mMeanVertex.getY(), zv); + vtx.setTimeStamp(input.timeEst); + if (findVertex(input, vtx)) { + finalizeVertex(input, vtx, vertices, v2tRefs, vertexTrackIDs, seedHisto); + nfound++; + nTrials = 0; + } else { // suppress failed seeding bin and its proximities + auto delta = std::sqrt(vtx.getChi2()) * mStatZErr.getMean() * getTukey(); // largest scale used will be transferred as chi2 + int proximity = delta * seedHisto.binSizeInv; + int bmin = std::max(0, peakBin - proximity), bmax = std::min(peakBin + proximity + 1, seedHisto.size()); + LOG(DEBUG) << "suppress bins for delta=" << delta << " (" << std::sqrt(vtx.getChi2()) << "*" << mStatZErr.getMean() << "*" << getTukey() << ")" + << " bins " << bmin << " : " << bmax - 1; + for (int i = bmin; i < bmax; i++) { + seedHisto.discardBin(i); + } + nTrials++; + } + } + return nfound; +} + +//______________________________________________ +bool PVertexer::findVertex(const VertexingInput& input, PVertex& vtx) +{ + // fit vertex taking provided vertex as a seed + // tracks pool may contain arbitrary number of tracks, only those which are in + // the idRange (indices of tracks sorted in time) will be used. + + VertexSeed vtxSeed(vtx, input.useConstraint, input.fillErrors); + vtxSeed.setScale(input.scaleSigma2, mTukey2I); + vtxSeed.scaleSigma2Prev = input.scaleSigma2; + // vtxSeed.setTimeStamp( timeEstimate(input) ); + LOG(DEBUG) << "Start time guess: " << vtxSeed.getTimeStamp(); + vtx.setChi2(1.e30); + // + FitStatus result = FitStatus::IterateFurther; + bool found = false; + while (result == FitStatus::IterateFurther) { + vtxSeed.resetForNewIteration(); + vtxSeed.nIterations++; + LOG(DEBUG) << "iter " << vtxSeed.nIterations << " with scale=" << vtxSeed.scaleSigma2 << " prevScale=" << vtxSeed.scaleSigma2Prev; + result = fitIteration(input, vtxSeed); + + if (result == FitStatus::OK) { + result = evalIterations(vtxSeed, vtx); + } else if (result == FitStatus::NotEnoughTracks) { + if (vtxSeed.nIterations <= mPVParams->maxIterations && upscaleSigma(vtxSeed)) { + LOG(DEBUG) << "Upscaling scale to " << vtxSeed.scaleSigma2; + result = FitStatus::IterateFurther; + continue; // redo with stronger rescaling + } else { + break; + } + } else if (result == FitStatus::PoolEmpty || result == FitStatus::Failure) { + break; + } else { + LOG(FATAL) << "Unknown fit status " << int(result); + } + } + LOG(DEBUG) << "Stopped with scale=" << vtxSeed.scaleSigma2 << " prevScale=" << vtxSeed.scaleSigma2Prev << " result = " << int(result); + + if (result != FitStatus::OK) { + vtx.setChi2(vtxSeed.maxScaleSigma2Tested); + return false; + } else { + return true; + } +} + +//___________________________________________________________________ +PVertexer::FitStatus PVertexer::fitIteration(const VertexingInput& input, VertexSeed& vtxSeed) +{ + int nTested = 0; + for (int i : input.idRange) { + if (mTracksPool[i].canUse()) { + nTested++; + accountTrack(mTracksPool[i], vtxSeed); + } + } + vtxSeed.maxScaleSigma2Tested = vtxSeed.scaleSigma2; + if (vtxSeed.getNContributors() < mPVParams->minTracksPerVtx) { + return nTested < mPVParams->minTracksPerVtx ? FitStatus::PoolEmpty : FitStatus::NotEnoughTracks; + } + if (vtxSeed.useConstraint) { + applyConstraint(vtxSeed); + } + if (!solveVertex(vtxSeed)) { + return FitStatus::Failure; + } + return FitStatus::OK; +} + +//___________________________________________________________________ +void PVertexer::accountTrack(TrackVF& trc, VertexSeed& vtxSeed) const +{ + // deltas defined as track - vertex + float dt, trErr2I = 0, dy, dz, chi2T = trc.getResiduals(vtxSeed, dy, dz); // track-vertex residuals and chi2 + auto& timeV = vtxSeed.getTimeStamp(); + auto& timeT = trc.timeEst; + float ndff = 1. / 2; + bool noTime = false; + if (timeV.getTimeStampError() < 0.) { + noTime = true; + } else { + dt = timeT.getTimeStamp() - timeV.getTimeStamp(); + trErr2I = 1. / (timeT.getTimeStampError() * timeT.getTimeStampError()); + if (mPVParams->useTimeInChi2) { + chi2T += dt * dt * trErr2I; + ndff = 1. / 3.; + } + } + chi2T *= ndff; + float wghT = (1.f - chi2T * vtxSeed.scaleSig2ITuk2I); // weighted distance to vertex + if (wghT < kAlmost0F) { + trc.wgh = 0.f; + return; + } + float syyI(trc.sig2YI), szzI(trc.sig2ZI), syzI(trc.sigYZI); + + // + vtxSeed.wghSum += wghT; + vtxSeed.wghChi2 += wghT * chi2T; + // + syyI *= wghT; + syzI *= wghT; + szzI *= wghT; + trc.wgh = wghT; + // + // aux variables + double tmpSP = trc.sinAlp * trc.tgP, tmpCP = trc.cosAlp * trc.tgP, + tmpSC = trc.sinAlp + tmpCP, tmpCS = -trc.cosAlp + tmpSP, + tmpCL = trc.cosAlp * trc.tgL, tmpSL = trc.sinAlp * trc.tgL, + tmpYXP = trc.y - trc.tgP * trc.x, tmpZXL = trc.z - trc.tgL * trc.x, + tmpCLzz = tmpCL * szzI, tmpSLzz = tmpSL * szzI, tmpSCyz = tmpSC * syzI, + tmpCSyz = tmpCS * syzI, tmpCSyy = tmpCS * syyI, tmpSCyy = tmpSC * syyI, + tmpSLyz = tmpSL * syzI, tmpCLyz = tmpCL * syzI; + // + // symmetric matrix equation + vtxSeed.cxx += tmpCL * (tmpCLzz + tmpSCyz + tmpSCyz) + tmpSC * tmpSCyy; // dchi^2/dx/dx + vtxSeed.cxy += tmpCL * (tmpSLzz + tmpCSyz) + tmpSL * tmpSCyz + tmpSC * tmpCSyy; // dchi^2/dx/dy + vtxSeed.cxz += -trc.sinAlp * syzI - tmpCLzz - tmpCP * syzI; // dchi^2/dx/dz + vtxSeed.cx0 += -(tmpCLyz + tmpSCyy) * tmpYXP - (tmpCLzz + tmpSCyz) * tmpZXL; // RHS + // + vtxSeed.cyy += tmpSL * (tmpSLzz + tmpCSyz + tmpCSyz) + tmpCS * tmpCSyy; // dchi^2/dy/dy + vtxSeed.cyz += -(tmpCSyz + tmpSLzz); // dchi^2/dy/dz + vtxSeed.cy0 += -tmpYXP * (tmpCSyy + tmpSLyz) - tmpZXL * (tmpCSyz + tmpSLzz); // RHS + // + vtxSeed.czz += szzI; // dchi^2/dz/dz + vtxSeed.cz0 += tmpZXL * szzI + tmpYXP * syzI; // RHS + // + if (!noTime) { + trErr2I *= wghT; + vtxSeed.tMeanAcc += timeT.getTimeStamp() * trErr2I; + vtxSeed.tMeanAccErr += trErr2I; + } + vtxSeed.addContributor(); +} + +//___________________________________________________________________ +bool PVertexer::solveVertex(VertexSeed& vtxSeed) const +{ + ROOT::Math::SMatrix<double, 3, 3, ROOT::Math::MatRepSym<double, 3>> mat; + mat(0, 0) = vtxSeed.cxx; + mat(0, 1) = vtxSeed.cxy; + mat(0, 2) = vtxSeed.cxz; + mat(1, 1) = vtxSeed.cyy; + mat(1, 2) = vtxSeed.cyz; + mat(2, 2) = vtxSeed.czz; + if (!mat.InvertFast()) { + LOG(ERROR) << "Failed to invert matrix" << mat; + return false; + } + ROOT::Math::SVector<double, 3> rhs(vtxSeed.cx0, vtxSeed.cy0, vtxSeed.cz0); + auto sol = mat * rhs; + vtxSeed.setXYZ(sol(0), sol(1), sol(2)); + if (vtxSeed.fillErrors) { + vtxSeed.setCov(mat(0, 0), mat(1, 0), mat(1, 1), mat(2, 0), mat(2, 1), mat(2, 2)); + } + if (vtxSeed.tMeanAccErr > 0.) { + auto err2 = 1. / vtxSeed.tMeanAccErr; + vtxSeed.setTimeStamp({float(vtxSeed.tMeanAcc * err2), float(std::sqrt(err2))}); + } + + vtxSeed.setChi2((vtxSeed.getNContributors() - vtxSeed.wghSum) / vtxSeed.scaleSig2ITuk2I); // calculate chi^2 + auto newScale = vtxSeed.wghChi2 / vtxSeed.wghSum; + LOG(DEBUG) << "Solve: wghChi2=" << vtxSeed.wghChi2 << " wghSum=" << vtxSeed.wghSum << " -> scale= " << newScale << " old scale " << vtxSeed.scaleSigma2 << " prevScale: " << vtxSeed.scaleSigma2Prev; + vtxSeed.setScale(newScale < mPVParams->minScale2 ? mPVParams->minScale2 : newScale, mTukey2I); + return true; +} + +//___________________________________________________________________ +PVertexer::FitStatus PVertexer::evalIterations(VertexSeed& vtxSeed, PVertex& vtx) const +{ + // decide if new iteration should be done, prepare next one if needed + // if scaleSigma2 reached its lower limit stop + PVertexer::FitStatus result = PVertexer::FitStatus::IterateFurther; + + if (vtxSeed.nIterations > mPVParams->maxIterations) { + result = PVertexer::FitStatus::Failure; + } else if (vtxSeed.scaleSigma2Prev <= mPVParams->minScale2 + kAlmost0F) { + result = PVertexer::FitStatus::OK; + LOG(DEBUG) << "stop on simga :" << vtxSeed.scaleSigma2 << " prev: " << vtxSeed.scaleSigma2Prev; + } + if (fair::Logger::Logging(fair::Severity::debug)) { + auto dchi = (vtx.getChi2() - vtxSeed.getChi2()) / vtxSeed.getChi2(); + auto dx = vtxSeed.getX() - vtx.getX(), dy = vtxSeed.getY() - vtx.getY(), dz = vtxSeed.getZ() - vtx.getZ(); + auto dst = std::sqrt(dx * dx + dy * dy + dz * dz); + + LOG(DEBUG) << "dChi:" << vtx.getChi2() << "->" << vtxSeed.getChi2() << " :-> " << dchi; + LOG(DEBUG) << "dx: " << dx << " dy: " << dy << " dz: " << dz << " -> " << dst; + } + + vtx = reinterpret_cast<const PVertex&>(vtxSeed); + + if (result == PVertexer::FitStatus::OK) { + auto chi2Mean = vtxSeed.getChi2() / vtxSeed.getNContributors(); + if (chi2Mean > mPVParams->maxChi2Mean) { + result = PVertexer::FitStatus::Failure; + LOG(DEBUG) << "Rejecting at iteration " << vtxSeed.nIterations << " and ScalePrev " << vtxSeed.scaleSigma2Prev << " with meanChi2 = " << chi2Mean; + } else { + return result; + } + } + + if (vtxSeed.scaleSigma2 > vtxSeed.scaleSigma2Prev) { + if (++vtxSeed.nScaleIncrease > mPVParams->maxNScaleIncreased) { + result = PVertexer::FitStatus::Failure; + LOG(DEBUG) << "Rejecting at iteration " << vtxSeed.nIterations << " with NScaleIncreased " << vtxSeed.nScaleIncrease; + } + } else if (vtxSeed.scaleSigma2 > mPVParams->slowConvergenceFactor * vtxSeed.scaleSigma2Prev) { + if (++vtxSeed.nScaleSlowConvergence > mPVParams->maxNScaleSlowConvergence) { + if (vtxSeed.scaleSigma2 < mPVParams->acceptableScale2) { + vtxSeed.setScale(mPVParams->minScale2, mTukey2I); + LOG(DEBUG) << "Forcing scale2 to " << mPVParams->minScale2; + result = PVertexer::FitStatus::IterateFurther; + } else { + result = PVertexer::FitStatus::Failure; + LOG(DEBUG) << "Rejecting at iteration " << vtxSeed.nIterations << " with NScaleSlowConvergence " << vtxSeed.nScaleSlowConvergence; + } + } + } else { + vtxSeed.nScaleSlowConvergence = 0; + } + + return result; +} + +//___________________________________________________________________ +void PVertexer::initMeanVertexConstraint() +{ + // set mean vertex constraint and its errors + double det = mMeanVertex.getSigmaY2() * mMeanVertex.getSigmaZ2() - mMeanVertex.getSigmaYZ() * mMeanVertex.getSigmaYZ(); + if (det <= kAlmost0D || mMeanVertex.getSigmaY2() < kAlmost0D || mMeanVertex.getSigmaZ2() < kAlmost0D) { + throw std::runtime_error(fmt::format("Singular matrix for vertex constraint: syy={:+.4e} syz={:+.4e} szz={:+.4e}", + mMeanVertex.getSigmaY2(), mMeanVertex.getSigmaYZ(), mMeanVertex.getSigmaZ2())); + } + mXYConstraintInvErr[0] = mMeanVertex.getSigmaZ2() / det; + mXYConstraintInvErr[2] = mMeanVertex.getSigmaY2() / det; + mXYConstraintInvErr[1] = -mMeanVertex.getSigmaYZ() / det; +} + +//______________________________________________ +float PVertexer::getTukey() const +{ + // convert 1/tukey^2 to tukey + return sqrtf(1. / mTukey2I); +} + +//___________________________________________________________________ +TimeEst PVertexer::timeEstimate(const VertexingInput& input) const +{ + o2::math_utils::StatAccumulator test; + for (int i : input.idRange) { + if (mTracksPool[i].canUse()) { + const auto& timeT = mTracksPool[i].timeEst; + auto trErr2I = 1. / (timeT.getTimeStampError() * timeT.getTimeStampError()); + test.add(timeT.getTimeStamp(), trErr2I); + } + } + + const auto [t, te2] = test.getMeanRMS2<float>(); + return {t, te2}; +} + +//___________________________________________________________________ +void PVertexer::init() +{ + mPVParams = &PVertexerParams::Instance(); + mFT0Params = &o2::ft0::InteractionTag::Instance(); + setTukey(mPVParams->tukey); + initMeanVertexConstraint(); + + auto* prop = o2::base::Propagator::Instance(); + setBz(prop->getNominalBz()); +} + +//___________________________________________________________________ +void PVertexer::finalizeVertex(const VertexingInput& input, const PVertex& vtx, + std::vector<PVertex>& vertices, std::vector<V2TRef>& v2tRefs, std::vector<int>& vertexTrackIDs, + SeedHisto& histo) +{ + int lastID = vertices.size(); + vertices.emplace_back(vtx); + auto& ref = v2tRefs.emplace_back(vertexTrackIDs.size(), 0); + for (int i : input.idRange) { + if (mTracksPool[i].canAssign()) { + vertexTrackIDs.push_back(mTracksPool[i].entry); + mTracksPool[i].vtxID = lastID; + + // remove track from ZSeeds histo + histo.decrementBin(mTracksPool[i].bin); + } + } + ref.setEntries(vertexTrackIDs.size() - ref.getFirstEntry()); +} + +//___________________________________________________________________ +void PVertexer::createTracksPool(gsl::span<const o2d::TrackTPCITS> tracksITSTPC) +{ + // create pull of all candidate tracks in a global array ordered in time + mTracksPool.clear(); + mSortedTrackID.clear(); + + auto ntGlo = tracksITSTPC.size(); + mTracksPool.reserve(ntGlo); + // check all containers + float vtxErr2 = 0.5 * (mMeanVertex.getSigmaX2() + mMeanVertex.getSigmaY2()); + float pullIniCut = 9.; // RS FIXME pullIniCut should be a parameter + o2d::DCA dca; + + for (uint32_t i = 0; i < ntGlo; i++) { + o2::track::TrackParCov trc = tracksITSTPC[i]; + if (!trc.propagateToDCA(mMeanVertex, mBz, &dca, mPVParams->dcaTolerance) || + dca.getY() * dca.getY() / (dca.getSigmaY2() + vtxErr2) > mPVParams->pullIniCut) { + continue; + } + auto& tvf = mTracksPool.emplace_back(trc, tracksITSTPC[i].getTimeMUS(), i, 0); + mStatZErr.add(std::sqrt(trc.getSigmaZ2())); + mStatTErr.add(tvf.timeEst.getTimeStampError()); + } + // TODO: try to narrow timestamps using tof times + auto [zerrMean, zerrRMS] = mStatZErr.getMeanRMS2<float>(); + + auto [terrMean, terrRMS] = mStatTErr.getMeanRMS2<float>(); + + if (mTracksPool.empty()) { + return; + } + // + mSortedTrackID.resize(mTracksPool.size()); + std::iota(mSortedTrackID.begin(), mSortedTrackID.end(), 0); + + std::sort(mSortedTrackID.begin(), mSortedTrackID.end(), [this](int i, int j) { + return this->mTracksPool[i].timeEst.getTimeStamp() < this->mTracksPool[j].timeEst.getTimeStamp(); + }); + + auto tMin = mTracksPool[mSortedTrackID.front()].timeEst.getTimeStamp(); + auto tMax = mTracksPool[mSortedTrackID.back()].timeEst.getTimeStamp(); +} + +//___________________________________________________________________ +void PVertexer::createMCLabels(gsl::span<const o2::MCCompLabel> lblITS, gsl::span<const o2::MCCompLabel> lblTPC, + const std::vector<PVertex> vertices, const std::vector<o2::dataformats::VtxTrackIndex> vertexTrackIDs, const std::vector<V2TRef> v2tRefs, + std::vector<o2::MCEventLabel>& lblVtx) +{ + lblVtx.clear(); + int nv = vertices.size(); + if (lblITS.size() != lblITS.size() || !lblITS.size()) { + LOG(ERROR) << "labels are not provided or incorrect"; + return; + } + std::unordered_map<o2::MCEventLabel, int> labelOccurenceCorr, labelOccurenceITS; + + auto bestLbl = [](std::unordered_map<o2::MCEventLabel, int> mp, int norm) -> o2::MCEventLabel { + o2::MCEventLabel best; + int bestCount = 0; + for (auto [lbl, cnt] : mp) { + if (cnt > bestCount) { + bestCount = cnt; + best = lbl; + } + } + if (bestCount && norm) { + best.setCorrWeight(float(bestCount) / norm); + } + return best; + }; + + for (const auto& v2t : v2tRefs) { + int tref = v2t.getFirstEntry(), last = tref + v2t.getEntries(); + labelOccurenceCorr.clear(); + labelOccurenceITS.clear(); + o2::MCEventLabel winner; // unset at the moment + for (; tref < last; tref++) { + int tid = vertexTrackIDs[tref].getIndex(); + const auto& lITS = lblITS[tid]; + const auto& lTPC = lblTPC[tid]; + if (!lITS.isSet() || !lTPC.isSet()) { + break; + } + if (lITS.getTrackID() == lTPC.getTrackID() && lITS.getEventID() == lTPC.getEventID() && lITS.getSourceID() == lTPC.getSourceID()) { + labelOccurenceCorr[{lITS.getEventID(), lITS.getSourceID(), 0.}]++; + } else { + labelOccurenceITS[{lITS.getEventID(), lITS.getSourceID(), 0.}]++; + } + } + if (labelOccurenceCorr.size()) { + winner = bestLbl(labelOccurenceCorr, v2t.getEntries()); + } else if (labelOccurenceITS.size()) { + winner = bestLbl(labelOccurenceITS, 0); // in absence of correct matches, set the ITS only label but set its weight to 0 + } + lblVtx.push_back(winner); + } +} + +//___________________________________________________________________ +std::pair<int, int> PVertexer::getBestFT0Trigger(const PVertex& vtx, gsl::span<const o2::ft0::RecPoints> ft0Data, int& currEntry) const +{ + // select best matching FT0 recpoint + int best = -1, n = ft0Data.size(); + while (currEntry < n && ft0Data[currEntry].getInteractionRecord() < vtx.getIRMin()) { + currEntry++; // skip all times which have no chance to be matched + } + int i = currEntry, nCompatible = 0; + float bestDf = 1e12; + auto tVtxNS = (vtx.getTimeStamp().getTimeStamp() + mPVParams->timeBiasMS) * 1e3; // time in ns + while (i < n) { + if (ft0Data[i].getInteractionRecord() > vtx.getIRMax()) { + break; + } + if (mFT0Params->isSelected(ft0Data[i])) { + nCompatible++; + auto dfa = std::abs(mFT0Params->getInteractionTimeNS(ft0Data[i], mStartIR) - tVtxNS); + if (dfa <= bestDf) { + bestDf = dfa; + best = i; + } + } + i++; + } + return {best, nCompatible}; +} + +//___________________________________________________________________ +void PVertexer::setBunchFilling(const o2::BunchFilling& bf) +{ + mBunchFilling = bf; + // find closest (from above) filled bunch + int minBC = bf.getFirstFilledBC(), maxBC = bf.getLastFilledBC(); + if (minBC < 0) { + throw std::runtime_error("Bunch filling is not set in PVertexer"); + } + int bcAbove = minBC; + for (int i = o2::constants::lhc::LHCMaxBunches; i--;) { + if (bf.testBC(i)) { + bcAbove = i; + } + mClosestBunchAbove[i] = bcAbove; + } + int bcBelow = maxBC; + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + if (bf.testBC(i)) { + bcBelow = i; + } + mClosestBunchBelow[i] = bcBelow; + } +} + +//___________________________________________________________________ +bool PVertexer::setCompatibleIR(PVertex& vtx) +{ + // assign compatible IRs accounting for the bunch filling scheme + const auto& vtxT = vtx.getTimeStamp(); + o2::InteractionRecord irMin(mStartIR), irMax(mStartIR); + auto rangeT = mPVParams->nSigmaTimeCut * std::max(mPVParams->minTError, std::min(mPVParams->maxTError, vtxT.getTimeStampError())); + float t = vtxT.getTimeStamp() + mPVParams->timeBiasMS; + if (t > rangeT) { + irMin += o2::InteractionRecord(1.e3 * (t - rangeT)); + } + irMax += o2::InteractionRecord(1.e3 * (t + rangeT)); + irMax++; // to account for rounding + // restrict using bunch filling + int bc = mClosestBunchAbove[irMin.bc]; + if (bc < irMin.bc) { + irMin.orbit++; + } + irMin.bc = bc; + bc = mClosestBunchBelow[irMax.bc]; + if (bc > irMax.bc) { + if (irMax.orbit == 0) { + return false; + } + irMax.orbit--; + } + irMax.bc = bc; + vtx.setIRMin(irMin); + vtx.setIRMax(irMax); + return irMax >= irMin; +} + +//___________________________________________________________________ +int PVertexer::dbscan_RangeQuery(int id, std::vector<int>& cand, const std::vector<int>& status) +{ + // find neighbours for dbscan cluster core point candidate + // Since we use asymmetric distance definition, is it bit more complex than simple search within chi2 proximity + int nFound = 0; + const auto& tI = mTracksPool[mSortedTrackID[id]]; + int ntr = mTracksPool.size(); + + auto procPnt = [this, &tI, &status, &cand, &nFound, id](int idN) { + const auto& tL = this->mTracksPool[this->mSortedTrackID[idN]]; + if (std::abs(tI.timeEst.getTimeStamp() - tL.timeEst.getTimeStamp()) > this->mPVParams->dbscanDeltaT) { + return -1; + } + auto statN = status[idN], stat = status[id]; + if (statN >= 0 && (stat < 0 || (stat >= 0 && statN != stat))) { // do not consider as a neighbour if already added to other cluster + return 0; + } + auto dist2 = tL.getDist2(tI); + if (dist2 < this->mPVParams->dbscanMaxDist2) { + nFound++; + if (statN < 0) { + cand.push_back(idN); // no point in adding for check already assigned point + } + } + return 1; + }; + int idL = id; + while (--idL >= 0) { // index in time decreasing direction + if (procPnt(idL) < 0) { + break; + } + } + int idU = id; + while (++idU < ntr) { // index in time increasing direction + if (procPnt(idU) < 0) { + break; + } + } + return nFound; +} + +//_____________________________________________________ +void PVertexer::dbscan_clusterize() +{ + const int UNDEF = -2, NOISE = -1; + int ntr = mSortedTrackID.size(); + std::vector<std::vector<int>> clusters; + std::vector<int> status(ntr, UNDEF); + TStopwatch timer; + int clID = -1; + + std::vector<int> nbVec; + for (int it = 0; it < ntr; it++) { + if (status[it] != UNDEF) { + continue; + } + nbVec.clear(); + auto nnb0 = dbscan_RangeQuery(it, nbVec, status); + int minNeighbours = mPVParams->minTracksPerVtx - 1; + if (nnb0 < minNeighbours) { + status[it] = NOISE; // noise + continue; + } + if (nnb0 > minNeighbours) { + minNeighbours = std::max(minNeighbours, int(nnb0 * mPVParams->dbscanAdaptCoef)); + } + status[it] = ++clID; + auto& clusVec = clusters.emplace_back(1, it); // new cluster + + for (int j = 0; j < nnb0; j++) { + int jt = nbVec[j]; + auto statjt = status[jt]; + if (statjt >= 0) { + continue; + } + status[jt] = clID; + clusVec.push_back(jt); + if (statjt == NOISE) { // was border point, no check for being core point is needed + continue; + } + int ncurr = nbVec.size(); + if (clusVec.size() > minNeighbours) { + minNeighbours = std::max(minNeighbours, int(clusVec.size() * mPVParams->dbscanAdaptCoef)); + } + auto nnb1 = dbscan_RangeQuery(jt, nbVec, status); + if (nnb1 < minNeighbours) { + nbVec.resize(ncurr); // not a core point, reset the seeds pool to the state before RangeQuery + } else { + nnb0 = ncurr; // core point, its neighbours need to be checked + } + } + } + + mTimeZClusters.clear(); + mClusterTrackIDs.reserve(ntr); + for (const auto& clus : clusters) { + if (clus.size() < mPVParams->minTracksPerVtx) { + continue; + } + int first = mClusterTrackIDs.size(); + float tMean = 0; + for (const auto tid : clus) { + mClusterTrackIDs.push_back(mSortedTrackID[tid]); + tMean += mTracksPool[mClusterTrackIDs.back()].timeEst.getTimeStamp(); + } + int last = int(mClusterTrackIDs.size()); + int count = last - first; + mTimeZClusters.emplace_back(TimeZCluster{{tMean / count, 0.}, first, --last, count}); + } + timer.Stop(); + LOG(INFO) << "Found " << mTimeZClusters.size() << " clusters from DBSCAN out of " << clusters.size() << " seeds in " << timer.CpuTime() << " CPU s"; +} diff --git a/Detectors/Vertexing/src/PVertexerHelpers.cxx b/Detectors/Vertexing/src/PVertexerHelpers.cxx new file mode 100644 index 0000000000000..d3cf1756172cc --- /dev/null +++ b/Detectors/Vertexing/src/PVertexerHelpers.cxx @@ -0,0 +1,27 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexerHelpers.cxx +/// \brief Primary vertex finder helper classes +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/PVertexerHelpers.h" + +using namespace o2::vertexing; + +void VertexSeed::print() const +{ + auto terr2 = tMeanAccErr > 0 ? 1. / tMeanAccErr : 0.; + LOGF(INFO, "VtxSeed: Scale: %+e ScalePrev: %+e |NScaleIncreased: %d NSlowDecrease: %d| WChi2: %e WSum: %e | TMean: %e TMeanE: %e\n", + scaleSigma2, scaleSigma2Prev, nScaleIncrease, nScaleSlowConvergence, wghChi2, wghSum, tMeanAcc * terr2, std::sqrt(terr2)); + double dZP, rmsZP, dZN, rmsZN, dTP, rmsTP, dTN, rmsTN; + double dZ, rmsZ, dT, rmsT; + PVertex::print(); +} diff --git a/Detectors/Vertexing/src/PVertexerParams.cxx b/Detectors/Vertexing/src/PVertexerParams.cxx new file mode 100644 index 0000000000000..d18d70057f310 --- /dev/null +++ b/Detectors/Vertexing/src/PVertexerParams.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexerParams.cxx +/// \brief Configurable params for primary vertexer +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/PVertexerParams.h" + +O2ParamImpl(o2::vertexing::PVertexerParams); diff --git a/Detectors/Vertexing/src/SVertexer.cxx b/Detectors/Vertexing/src/SVertexer.cxx new file mode 100644 index 0000000000000..d89f8706717e4 --- /dev/null +++ b/Detectors/Vertexing/src/SVertexer.cxx @@ -0,0 +1,236 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SVertexer.cxx +/// \brief Secondary vertex finder +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/SVertexer.h" +#include "DetectorsBase/Propagator.h" + +using namespace o2::vertexing; + +using PID = o2::track::PID; + +void SVertexer::init() +{ + mSVParams = &SVertexerParams::Instance(); + + mFitter2Prong.setUseAbsDCA(mSVParams->useAbsDCA); + mFitter2Prong.setPropagateToPCA(false); + mFitter2Prong.setMaxR(mSVParams->maxRIni); + mFitter2Prong.setMinParamChange(mSVParams->minParamChange); + mFitter2Prong.setMinRelChi2Change(mSVParams->minRelChi2Change); + mFitter2Prong.setMaxDZIni(mSVParams->maxDZIni); + mFitter2Prong.setMaxChi2(mSVParams->maxChi2); + + // precalculated selection cuts + mMinR2ToMeanVertex = mSVParams->minRfromMeanVertex * mSVParams->minRfromMeanVertex; + mMaxDCAXY2ToMeanVertex = mSVParams->maxDCAXYfromMeanVertex * mSVParams->maxDCAXYfromMeanVertex; + mMinCosPointingAngle = mSVParams->minCosPointingAngle; + // + auto bz = o2::base::Propagator::Instance()->getNominalBz(); + mFitter2Prong.setBz(bz); + // + mV0Hyps[SVertexerParams::Photon].set(PID::Photon, PID::Electron, PID::Electron, mSVParams->pidCutsPhoton, bz); + mV0Hyps[SVertexerParams::K0].set(PID::K0, PID::Pion, PID::Pion, mSVParams->pidCutsK0, bz); + mV0Hyps[SVertexerParams::Lambda].set(PID::Lambda, PID::Proton, PID::Pion, mSVParams->pidCutsLambda, bz); + mV0Hyps[SVertexerParams::AntiLambda].set(PID::Lambda, PID::Pion, PID::Proton, mSVParams->pidCutsLambda, bz); + mV0Hyps[SVertexerParams::HyperTriton].set(PID::HyperTriton, PID::Helium3, PID::Pion, mSVParams->pidCutsHTriton, bz); + mV0Hyps[SVertexerParams::AntiHyperTriton].set(PID::HyperTriton, PID::Pion, PID::Helium3, mSVParams->pidCutsHTriton, bz); + // +} + +void SVertexer::process(const gsl::span<const PVertex>& vertices, // primary vertices + const gsl::span<const GIndex>& trackIndex, // Global ID's for associated tracks + const gsl::span<const VRef>& vtxRefs, // references from vertex to these track IDs + const gsl::span<const TrackTPCITS>& tpcits, // global tracks + const gsl::span<const TrackITS>& its, // ITS tracks + const gsl::span<const TrackTPC>& tpc, // TPC tracks + std::vector<V0>& v0s, // found V0s + std::vector<RRef>& vtx2V0refs // references from PVertex to V0 +) +{ + std::unordered_map<uint64_t, int> cache; // cache for tested combinations, the value >0 will give the entry of prevalidated V0 in the v0sTmp + std::vector<V0> v0sTmp(1); // 1st one is dummy! + std::vector<int> v0sIdx; // id's in v0sTmp used attached to p.vertices + std::vector<RRef> pv2v0sRefs; // p.vertex to v0 index references + + TrackAccessor tracksPool(tpcits, its, tpc); + + auto rejectTrack = [&tracksPool](GIndex id, char wantCharge) { + if (id.isPVContributor()) { + return true; + } + auto s = id.getSource(); + return (s != GIndex::TPCITS && s != GIndex::ITS) || tracksPool.getCharge(id) != wantCharge; + }; + + int nv = vertices.size(); + size_t countTot = 0, countTotUnique = 0; + for (int iv = 0; iv < nv; iv++) { + // + // select vertices + auto& pvrefs = pv2v0sRefs.emplace_back(v0sIdx.size(), 0); + const auto& pv = vertices[iv]; + if (pv.getNContributors() < 5) { + continue; + } + // + const auto& vtref = vtxRefs[iv]; + //LOG(INFO) << "P.Vertex " << iv << pv; + //LOG(INFO) << "P.V. -> tracks " << vtref; + // + int first = vtref.getFirstEntry(); + int last = first + vtref.getEntries(); + size_t count = 0, countUnique = 0; + for (int ipos = first; ipos < last; ipos++) { + auto idpos = trackIndex[ipos]; + if (rejectTrack(idpos, 1)) { // skip at the moment TPC only tracks + continue; + } + bool ambiguousPos = idpos.isAmbiguous(); // is this track compatible also with other vertex? + const auto& trPos = tracksPool.getTrack(idpos); + + for (int ineg = first; ineg < last; ineg++) { + auto idneg = trackIndex[ineg]; + if (rejectTrack(idneg, -1)) { // skip at the moment TPC only tracks + continue; + } + count++; + bool accept = false, newPair = true, ambiguousV0 = ambiguousPos && idneg.isAmbiguous(); // V0 is ambiguous if both tracks are compatible with other vertices + uint64_t idPosNeg = getPairIdx(idpos, idneg); + int* resPair = nullptr; + if (ambiguousV0) { // check if it was already processed + resPair = &cache[idPosNeg]; + if ((*resPair) < 0) { // was already checked and rejected + continue; + } else if ((*resPair) == 0) { // was not checked yet + countUnique++; + (*resPair) = -1; // this is rejection flag + } else { + newPair = false; + } + } + if (newPair) { + auto trNeg = tracksPool.getTrack(idneg); + // LOG(INFO) << "0: " << idpos << " " << tr0.o2::track::TrackPar::asString(); + // LOG(INFO) << "1: " << idneg << " " << tr1.o2::track::TrackPar::asString(); + int nCand = mFitter2Prong.process(trPos, trNeg); + if (nCand == 0) { // discard this pair + continue; + } + const auto& v0pos = mFitter2Prong.getPCACandidate(); + // check closeness to the beam-line + auto r2 = (v0pos[0] - mMeanVertex.getX()) * (v0pos[0] - mMeanVertex.getX()) + (v0pos[1] - mMeanVertex.getY()) * (v0pos[1] - mMeanVertex.getY()); + if (r2 < mMinR2ToMeanVertex) { + continue; + } + if (!mFitter2Prong.isPropagateTracksToVertexDone() && !mFitter2Prong.propagateTracksToVertex()) { + continue; + } + auto& trPosProp = mFitter2Prong.getTrack(0); + auto& trNegProp = mFitter2Prong.getTrack(1); + std::array<float, 3> pPos, pNeg; + trPosProp.getPxPyPzGlo(pPos); + trNegProp.getPxPyPzGlo(pNeg); + // 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<float, 3> pV0 = {pPos[0] + pNeg[0], pPos[1] + pNeg[1], pPos[2] + pNeg[2]}; + float dx = v0pos[0] - mMeanVertex.getX(), dy = v0pos[1] - mMeanVertex.getY(); + float pt2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1], prodXY = dx * pV0[0] + dy * pV0[1], tDCAXY = -prodXY / pt2V0; + float dcaX = dx + pV0[0] * tDCAXY, dcaY = dy + pV0[1] * tDCAXY, dca2 = dcaX * dcaX + dcaY * dcaY; + if (dca2 > mMaxDCAXY2ToMeanVertex) { + continue; + } + float p2V0 = pt2V0 + pV0[2] * pV0[2], ptV0 = std::sqrt(pt2V0); + + // apply mass selections + float p2Pos = pPos[0] * pPos[0] + pPos[1] * pPos[1] + pPos[2] * pPos[2], p2Neg = pNeg[0] * pNeg[0] + pNeg[1] * pNeg[1] + pNeg[2] * pNeg[2]; + bool goodHyp = false; + for (int ipid = 0; ipid < SVertexerParams::NPIDV0; ipid++) { + if (mV0Hyps[ipid].check(p2Pos, p2Neg, p2V0, ptV0)) { + goodHyp = true; + break; + } + } + if (!goodHyp) { + continue; + } + // check cos of pointing angle + float dz = v0pos[2] - pv.getZ(), cosPointingAngle = (prodXY + dz * pV0[2]) / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + if (cosPointingAngle < mMinCosPointingAngle) { + if (!ambiguousV0) { + continue; // no need to check this pair wrt other vertices + } + cosPointingAngle = mMinCosPointingAngle - 1e-6; + } else { // cuts passed, register v0 + accept = true; + } + // preliminary checks passed, cache V0 and proceed to specific vertex check + int szV0tmp = v0sTmp.size(); + if (resPair) { + *resPair = szV0tmp; // index of V0 to be created + } + // LOG(INFO) << "Adding new V0 " << szV0tmp; + + std::array<float, 3> v0posF = {float(v0pos[0]), float(v0pos[1]), float(v0pos[2])}; + auto& v0 = v0sTmp.emplace_back(v0posF, pV0, trPosProp, trNegProp, idpos, idneg); + if (accept) { + v0.setCosPA(cosPointingAngle); + v0.setDCA(mFitter2Prong.getChi2AtPCACandidate()); + v0.setVertexID(iv); + v0sIdx.push_back(szV0tmp); + pvrefs.changeEntriesBy(1); + } + } else { // check if already created V0 is good for this vertex + // LOG(INFO) << "Rechecking new V0 " << *resPair; + auto& v0 = v0sTmp[*resPair]; + std::array<float, 3> posV0, momV0; + v0.getXYZGlo(posV0); + v0.getPxPyPzGlo(momV0); + float cosPointingAngle = posV0[0] * momV0[0] + posV0[1] * momV0[1] + posV0[2] * momV0[2]; + cosPointingAngle /= std::sqrt((posV0[0] * posV0[0] + posV0[1] * posV0[1] + posV0[2] * posV0[2]) * (momV0[0] * momV0[0] + momV0[1] * momV0[1] + momV0[2] * momV0[2])); + if (cosPointingAngle > v0.getCosPA()) { // reassign + v0.setCosPA(cosPointingAngle); + v0.setVertexID(iv); + v0sIdx.push_back(*resPair); + pvrefs.changeEntriesBy(1); + } + } + } + } + countTot += count; + countTotUnique += countUnique; + // LOG(INFO) << "Tried " << count << " combs with " << countUnique << " uniques"; + } + // finalize V0s + for (int iv = 0; iv < nv; iv++) { + const auto& pvtx = vertices[iv]; + const auto& pvrefsTmp = pv2v0sRefs[iv]; + auto& pvrefsFin = vtx2V0refs.emplace_back(v0s.size(), 0); + int from = pvrefsTmp.getFirstEntry(), to = from + pvrefsTmp.getEntries(), nAdded = 0; + for (int iv0 = from; iv0 < to; iv0++) { + const auto& v0tmp = v0sTmp[v0sIdx[iv0]]; + if (v0tmp.getVertexID() != iv) { // this v0 - p.vertex association was reassigned + continue; + } + v0s.push_back(v0tmp); + nAdded++; + } + pvrefsFin.setEntries(nAdded); + LOG(INFO) << nAdded << " V0s added for vertex " << iv << " with " << pvtx.getNContributors() << " tracks, out of initial " << pvrefsTmp.getEntries(); + pvtx.print(); + } + + LOG(INFO) << "Tried " << countTot << " combs in total " << countTotUnique << " uniques"; +} diff --git a/Detectors/Vertexing/src/SVertexerParams.cxx b/Detectors/Vertexing/src/SVertexerParams.cxx new file mode 100644 index 0000000000000..802910be6c97b --- /dev/null +++ b/Detectors/Vertexing/src/SVertexerParams.cxx @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 PVertexerParams.cxx +/// \brief Configurable params for secondary vertexer +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/SVertexerParams.h" + +O2ParamImpl(o2::vertexing::SVertexerParams); diff --git a/Detectors/Vertexing/src/V0Hypothesis.cxx b/Detectors/Vertexing/src/V0Hypothesis.cxx new file mode 100644 index 0000000000000..7ccd60ea1d18f --- /dev/null +++ b/Detectors/Vertexing/src/V0Hypothesis.cxx @@ -0,0 +1,34 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 V0Hypothesis.cxx +/// \brief V0 hypothesis checker +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/V0Hypothesis.h" + +using namespace o2::vertexing; + +void V0Hypothesis::set(PID v0, PID ppos, PID pneg, float sig, float nSig, float margin, float cpt, float bz) +{ + mPIDV0 = v0; + mPIDPosProng = ppos; + mPIDNegProng = pneg; + mSigma = sig; + mNSigma = nSig; + mMargin = margin; + mCPt = std::abs(bz) > 1e-3 ? cpt * 5.0066791 / bz : 0.; // assume that pT dependent sigma is linear with B +} + +void V0Hypothesis::set(PID v0, PID ppos, PID pneg, const float pars[SVertexerParams::NPIDParams], float bz) +{ + set(v0, ppos, pneg, pars[SVertexerParams::SigmaMV0], pars[SVertexerParams::NSigmaMV0], + pars[SVertexerParams::Margin], pars[SVertexerParams::CPt], bz); +} diff --git a/Detectors/Vertexing/src/VertexTrackMatcher.cxx b/Detectors/Vertexing/src/VertexTrackMatcher.cxx new file mode 100644 index 0000000000000..e45f9da7d7fa5 --- /dev/null +++ b/Detectors/Vertexing/src/VertexTrackMatcher.cxx @@ -0,0 +1,267 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 VertexTrackMatcher.cxx +/// \brief Class for vertex track association +/// \author ruben.shahoyan@cern.ch + +#include "DetectorsVertexing/VertexTrackMatcher.h" +#include <unordered_map> +#include <numeric> +#include "TPCBase/ParameterElectronics.h" +#include "TPCBase/ParameterDetector.h" +#include "TPCBase/ParameterGas.h" + +using namespace o2::vertexing; + +//___________________________________________________________________ +void VertexTrackMatcher::init() +{ + mPVParams = &o2::vertexing::PVertexerParams::Instance(); + updateTPCTimeDependentParams(); // RS FIXME eventually should be set from CCDB for every TF +} + +void VertexTrackMatcher::updateTPCTimeDependentParams() +{ + // tpc time bin in microseconds + auto& gasParam = o2::tpc::ParameterGas::Instance(); + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + auto& detParam = o2::tpc::ParameterDetector::Instance(); + mTPCBin2MUS = elParam.ZbinWidth; + mMaxTPCDriftTimeMUS = detParam.TPClength / gasParam.DriftV; +} + +void VertexTrackMatcher::process(const gsl::span<const PVertex>& vertices, + const gsl::span<const GIndex>& v2tfitIDs, + const gsl::span<const VRef>& v2tfitRefs, + const gsl::span<const TrackTPCITS>& tpcitsTracks, + const gsl::span<const TrackITS>& itsTracks, + const gsl::span<const ITSROFR>& itsROFR, + const gsl::span<const TrackTPC>& tpcTracks, + std::vector<GIndex>& trackIndex, + std::vector<VRef>& vtxRefs) +{ + + TmpMap tmpMap; + tmpMap.reserve(vertices.size()); + + // TPC/ITS and TPC tracks are not sorted in time, do this and exclude indiced of tracks used in the vertex fit + std::vector<int> idTPCITS(tpcitsTracks.size()); // indices of TPCITS tracks sorted in time + std::vector<int> idVtxIRMin(vertices.size()); // indices of vertices sorted in IRmin + std::vector<int> flgITS(itsTracks.size(), 0); + std::vector<int> idTPC(tpcTracks.size()); // indices of TPC tracks sorted in min time + std::iota(idTPC.begin(), idTPC.end(), 0); + std::iota(idTPCITS.begin(), idTPCITS.end(), 0); + std::iota(idVtxIRMin.begin(), idVtxIRMin.end(), 0); + for (auto id : v2tfitIDs) { // flag matched ITS-TPC tracks used in the vertex fit, we exclude them from this association + idTPCITS[id.getIndex()] = -1; + } + for (const auto& tpcits : tpcitsTracks) { // flag standalone ITS and TPC tracks used in global matched, we exclude them from association to vertex + flgITS[tpcits.getRefITS()] = -1; + idTPC[tpcits.getRefTPC()] = -1; + } + std::sort(idVtxIRMin.begin(), idVtxIRMin.end(), [&vertices](int i, int j) { // sort according to IRMin + return vertices[i].getIRMin() < vertices[j].getIRMin(); + }); + std::sort(idTPCITS.begin(), idTPCITS.end(), [&tpcitsTracks](int i, int j) { // sort according to central time + float tI = (i < 0) ? 1e9 : tpcitsTracks[i].getTimeMUS().getTimeStamp(); + float tJ = (j < 0) ? 1e9 : tpcitsTracks[j].getTimeMUS().getTimeStamp(); + return tI < tJ; + }); + + std::vector<TBracket> tpcTimes; + tpcTimes.reserve(tpcTracks.size()); + for (const auto& trc : tpcTracks) { + tpcTimes.emplace_back(tpcTimeBin2MUS(trc.getTime0() - trc.getDeltaTBwd()), tpcTimeBin2MUS(trc.getTime0() + trc.getDeltaTFwd())); + } + std::sort(idTPC.begin(), idTPC.end(), [&tpcTimes](int i, int j) { // sort according to max time + float tI = (i < 0) ? 1e9 : tpcTimes[i].getMax(); + float tJ = (j < 0) ? 1e9 : tpcTimes[j].getMax(); + return tI < tJ; + }); + + // Important: do this in the same order in which VtxTrackIndex::Source enums are defined! + attachTPCITS(tmpMap, tpcitsTracks, idTPCITS, vertices); + attachITS(tmpMap, itsTracks, itsROFR, flgITS, vertices, idVtxIRMin); + attachTPC(tmpMap, tpcTimes, idTPC, vertices, idVtxIRMin); + + // build vector of global indices + trackIndex.clear(); + vtxRefs.clear(); + memset(idTPCITS.data(), 0, sizeof(int) * idTPCITS.size()); + memset(idTPC.data(), 0, sizeof(int) * idTPC.size()); + memset(flgITS.data(), 0, sizeof(int) * flgITS.size()); + std::array<std::vector<int>*, GIndex::NSources> vptr; + vptr[GIndex::TPCITS] = &idTPCITS; + vptr[GIndex::ITS] = &flgITS; + vptr[GIndex::TPC] = &idTPC; + int nv = vertices.size(); + // flag tracks attached to >1 vertex + for (int iv = 0; iv < nv; iv++) { + const auto& trvec = tmpMap[iv]; + for (auto gid : trvec) { + (*vptr[gid.getSource()])[gid.getIndex()]++; + } + } + + for (int iv = 0; iv < nv; iv++) { + int srcStart[GIndex::NSources + 1]; + int entry = trackIndex.size(); + srcStart[GIndex::TPCITS] = entry; + for (int is = 1; is < GIndex::NSources; is++) { + srcStart[is] = -1; + } + + // 1st: attach indices of global tracks used in vertex fit + int idMin = v2tfitRefs[iv].getFirstEntry(), idMax = idMin + v2tfitRefs[iv].getEntries(); + for (int id = idMin; id < idMax; id++) { + trackIndex.push_back(v2tfitIDs[id]); + } + + // 2nd: attach non-contributing tracks + const auto& trvec = tmpMap[iv]; + for (const auto gid0 : trvec) { + int src = gid0.getSource(); + if (srcStart[src] == -1) { + srcStart[src] = trackIndex.size(); + } + auto& gid = trackIndex.emplace_back(gid0); + if ((*vptr[src])[gid.getIndex()] > 1) { + gid.setAmbiguous(); + } + } + + auto& vr = vtxRefs.emplace_back(entry, trackIndex.size() - entry); + srcStart[GIndex::NSources] = trackIndex.size(); + for (int is = GIndex::NSources; is > 0; is--) { + if (srcStart[is] == -1) { // in case the source did not contribute + srcStart[is] = srcStart[is + 1]; + } + vr.setFirstEntryOfSource(is, srcStart[is]); + } + } +} + +///______________________________________________________________________________________ +void VertexTrackMatcher::attachTPCITS(TmpMap& tmpMap, const gsl::span<const TrackTPCITS>& tpcits, const std::vector<int>& idTPCITS, const gsl::span<const PVertex>& vertices) +{ + int itrCurr = 0, nvt = vertices.size(), ntr = tpcits.size(); + // indices of tracks used for vertex fit will be in the end, find their start and max error + float maxErr = 0; + for (int i = 0; i < ntr; i++) { + if (idTPCITS[i] < 0) { + ntr = i; + break; + } + float err = tpcits[idTPCITS[i]].getTimeMUS().getTimeStampError(); + if (maxErr < err) { + maxErr = err; + } + } + auto maxErr2 = maxErr * maxErr; + for (int ivtCurr = 0; ivtCurr < nvt; ivtCurr++) { + const auto& vtxT = vertices[ivtCurr].getTimeStamp(); + auto rangeT = mPVParams->nSigmaTimeCut * std::sqrt(maxErr2 + vtxT.getTimeStampError() * vtxT.getTimeStampError()); + float minTime = vtxT.getTimeStamp() - rangeT, maxTime = vtxT.getTimeStamp() + rangeT; + // proceed to the 1st track having compatible time + int itr = itrCurr; + while (itr < ntr) { + const auto& trcT = tpcits[idTPCITS[itr]].getTimeMUS(); + if (trcT.getTimeStamp() < minTime) { + itrCurr = itr; + } else if (trcT.getTimeStamp() > maxTime) { + break; + } else if (compatibleTimes(vtxT, trcT)) { + tmpMap[ivtCurr].emplace_back(idTPCITS[itr], GIndex::TPCITS); + } + itr++; + } + } +} + +///______________________________________________________________________________________ +void VertexTrackMatcher::attachITS(TmpMap& tmpMap, const gsl::span<const TrackITS>& itsTracks, const gsl::span<const ITSROFR>& itsROFR, const std::vector<int>& flITS, + const gsl::span<const PVertex>& vertices, std::vector<int>& idxVtx) +{ + int irofCurr = 0, nvt = vertices.size(), ntr = itsTracks.size(); + int nROFs = itsROFR.size(); + + for (int ivtCurr = 0; ivtCurr < nvt; ivtCurr++) { + const auto& vtx = vertices[idxVtx[ivtCurr]]; // we iterate over vertices in order of their getIRMin() + int irof = irofCurr; + while (irof < nROFs) { + const auto& rofr = itsROFR[irof]; + auto irMin = rofr.getBCData(), irMax = irMin + mITSROFrameLengthInBC; + if (irMax < vtx.getIRMin()) { + irofCurr = irof; + } else if (irMin > vtx.getIRMax()) { + break; + } else { + if (irMax == vtx.getIRMin()) { + irofCurr = irof; + } + int maxItr = rofr.getNEntries() + rofr.getFirstEntry(); + for (int itr = rofr.getFirstEntry(); itr < maxItr; itr++) { + if (flITS[itr] != -1) { + tmpMap[ivtCurr].emplace_back(itr, GIndex::ITS); + } + } + } + irof++; + } + } +} + +///______________________________________________________________________________________ +void VertexTrackMatcher::attachTPC(TmpMap& tmpMap, const std::vector<TBracket>& tpcTimes, const std::vector<int>& idTPC, + const gsl::span<const PVertex>& vertices, std::vector<int>& idVtx) +{ + int itrCurr = 0, nvt = vertices.size(), ntr = idTPC.size(); + // indices of tracks used for vertex fit will be in the end, find their start and max error + for (int i = 0; i < ntr; i++) { + if (idTPC[i] < 0) { + ntr = i; + break; + } + } + for (int ivtCurr = 0; ivtCurr < nvt; ivtCurr++) { + const auto& vtx = vertices[idVtx[ivtCurr]]; + TBracket tV(vtx.getIRMin().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS * 1e-3, + vtx.getIRMax().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS * 1e-3); + // proceed to the 1st track having compatible time, idTPC provides track IDs ordered in max time + int itr = itrCurr; + while (itr < ntr) { + const auto& tT = tpcTimes[idTPC[itr]]; + auto rel = tV.isOutside(tT); + if (rel == TBracket::Below) { // tmax of track < tmin of vtx + itrCurr = itr; + } else if (rel == TBracket::Inside) { + tmpMap[ivtCurr].emplace_back(idTPC[itr], GIndex::TPC); + } else if (tT.getMax() - mMaxTPCDriftTimeMUS > tV.getMax()) { + break; + } + itr++; + } + } +} + +//______________________________________________ +void VertexTrackMatcher::setITSROFrameLengthInBC(int nbc) +{ + mITSROFrameLengthInBC = nbc; +} + +//______________________________________________ +bool VertexTrackMatcher::compatibleTimes(const TimeEst& vtxT, const TimeEst& trcT) const +{ + float err2 = vtxT.getTimeStampError() * vtxT.getTimeStampError() + trcT.getTimeStampError() * trcT.getTimeStampError(); + float dfred = (vtxT.getTimeStamp() - trcT.getTimeStamp()) * mPVParams->nSigmaTimeCut; + return dfred * dfred < err2; +} diff --git a/Detectors/Vertexing/test/testDCAFitterN.cxx b/Detectors/Vertexing/test/testDCAFitterN.cxx index c23907cd6c6eb..add77c641f0f6 100644 --- a/Detectors/Vertexing/test/testDCAFitterN.cxx +++ b/Detectors/Vertexing/test/testDCAFitterN.cxx @@ -100,8 +100,8 @@ TLorentzVector generate(Vec3D& vtx, std::vector<o2::track::TrackParCov>& vctr, f dt->GetXYZT(p); float s, c, x; std::array<float, 5> params; - o2::utils::sincosf(dt->Phi(), s, c); - o2::utils::rotateZInv(vtx[0], vtx[1], x, params[0], s, c); + 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 diff --git a/Detectors/ZDC/CMakeLists.txt b/Detectors/ZDC/CMakeLists.txt index 364119694abdb..97eac3feb0cc2 100644 --- a/Detectors/ZDC/CMakeLists.txt +++ b/Detectors/ZDC/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(base) add_subdirectory(simulation) add_subdirectory(macro) +add_subdirectory(raw) diff --git a/Detectors/ZDC/base/include/ZDCBase/Constants.h b/Detectors/ZDC/base/include/ZDCBase/Constants.h index 56f3ff185e48a..c834344b2d071 100644 --- a/Detectors/ZDC/base/include/ZDCBase/Constants.h +++ b/Detectors/ZDC/base/include/ZDCBase/Constants.h @@ -39,8 +39,8 @@ enum ChannelTypeZNP { Common, enum ChannelTypeZEM { ZEMCh1, ZEMCh2 }; // channel IDs for ZEMs -constexpr int NTimeBinsPerBC = 12; //< number of samples per BC -constexpr int NBCReadOut = 4; // N BCs read out per trigger +constexpr int NTimeBinsPerBC = 12; //< number of samples per BC +constexpr int NBCReadOut = 4; // N BCs read out per trigger constexpr int NTimeBinsReadout = NTimeBinsPerBC * NBCReadOut; constexpr int NChannelsZN = 6; //< number of channels stored per ZN @@ -51,10 +51,15 @@ constexpr float ChannelTimeBinNS = 2.; //< bin length in NS constexpr float SampleLenghtNS = NTimeBinsPerBC * ChannelTimeBinNS; constexpr int NChannels = 2 * (NChannelsZN + NChannelsZP) + NChannelsZEM; -constexpr uint32_t AllChannelsMask = (0x1 << NChannels) - 1; +constexpr uint8_t ALICETriggerMask = 0x1; constexpr int NModules = 8; -constexpr int MaxTriggerChannels = 10; +constexpr int NChPerModule = 4; +constexpr int NLinks = NModules * 2; +constexpr int NDigiChannels = NModules * NChPerModule; +constexpr int NWPerBc = 3; +constexpr int MaxTriggerChannels = NChannels; +constexpr int ADCMin = -2048, ADCMax = 2047, ADCRange = 4096; // 12 bit ADC constexpr int MaxTDCValues = 5; // max number of TDC values to store in reconstructed event constexpr int NTDCChannels = 10; // max number of TDC values to store in reconstructed event diff --git a/Detectors/ZDC/base/include/ZDCBase/Geometry.h b/Detectors/ZDC/base/include/ZDCBase/Geometry.h index eedb9e3b38a48..3eec00b9a3656 100644 --- a/Detectors/ZDC/base/include/ZDCBase/Geometry.h +++ b/Detectors/ZDC/base/include/ZDCBase/Geometry.h @@ -11,6 +11,7 @@ #ifndef ALICEO2_ZDC_GEOMETRY_H #define ALICEO2_ZDC_GEOMETRY_H +#include <Rtypes.h> #include "TMath.h" #include <cmath> diff --git a/Detectors/ZDC/base/include/ZDCBase/ModuleConfig.h b/Detectors/ZDC/base/include/ZDCBase/ModuleConfig.h index f10a8870ca538..10548369121cf 100644 --- a/Detectors/ZDC/base/include/ZDCBase/ModuleConfig.h +++ b/Detectors/ZDC/base/include/ZDCBase/ModuleConfig.h @@ -34,13 +34,15 @@ struct Module { static constexpr int MaxChannels = 4, MaxTriggChannels = 2; int id = -1; // not active std::array<int8_t, MaxChannels> channelID = {IdDummy, IdDummy, IdDummy, IdDummy}; - std::array<int16_t, MaxChannels> linkID = {-1, -1, -1, -1}; + std::array<int16_t, MaxChannels> feeID = {-1, -1, -1, -1}; std::array<bool, MaxChannels> readChannel = {false}; std::array<bool, MaxChannels> trigChannel = {false}; std::array<TriggerChannelConfig, MaxChannels> trigChannelConf; - void setChannel(int slot, int8_t chID, int16_t lID, bool read, bool trig = false, int tF = 0, int tL = 0, int tS = 0, int tT = 0); + void setChannel(int slot, int8_t chID, int16_t fID, bool read, bool trig = false, int tF = 0, int tL = 0, int tS = 0, int tT = 0); void print() const; + void printCh() const; + void printTrig() const; void check() const; ClassDefNV(Module, 1); }; diff --git a/Detectors/ZDC/base/src/ModuleConfig.cxx b/Detectors/ZDC/base/src/ModuleConfig.cxx index ecb7b28766b85..f0b0157e14a6b 100644 --- a/Detectors/ZDC/base/src/ModuleConfig.cxx +++ b/Detectors/ZDC/base/src/ModuleConfig.cxx @@ -14,33 +14,54 @@ using namespace o2::zdc; -void Module::print() const +//______________________________________________________________________________ +void Module::printCh() const { - printf("Module %d [ChID/LinkID R:T ]", id); + printf("Module %d [ChID/FEEID R:T ]", id); for (int ic = 0; ic < MaxChannels; ic++) { - printf("[%s{%2d}/L%02d %c:%c ]", channelName(channelID[ic]), channelID[ic], linkID[ic], readChannel[ic] ? '+' : '-', trigChannel[ic] ? '+' : '-'); + printf("[%s{%2d}/L%02d %c:%c ]", channelName(channelID[ic]), channelID[ic], feeID[ic], readChannel[ic] ? 'R' : ' ', trigChannel[ic] ? 'T' : ' '); } printf("\n"); - printf("Trigger conf: "); +} + +//______________________________________________________________________________ +void Module::printTrig() const +{ + printf("Trigger conf %d: ", id); for (int ic = 0; ic < MaxChannels; ic++) { + const auto& cnf = trigChannelConf[ic]; if (trigChannel[ic]) { - const auto& cnf = trigChannelConf[ic]; - printf("[%s: F:%2d L:%2d S:%2d T:%2d] ", channelName(channelID[ic]), cnf.first, cnf.last, cnf.shift, cnf.threshold); + printf("[TRIG %s: F:%2d L:%2d S:%2d T:%2d] ", channelName(channelID[ic]), cnf.first, cnf.last, cnf.shift, cnf.threshold); + } else if (cnf.shift > 0 && cnf.threshold > 0) { + printf("[DISC %s: F:%2d L:%2d S:%2d T:%2d] ", channelName(channelID[ic]), cnf.first, cnf.last, cnf.shift, cnf.threshold); } } printf("\n"); } +//______________________________________________________________________________ +void Module::print() const +{ + printCh(); + printTrig(); +} + void ModuleConfig::print() const { printf("Modules configuration:\n"); for (const auto& md : modules) { if (md.id >= 0) { - md.print(); + md.printCh(); + } + } + for (const auto& md : modules) { + if (md.id >= 0) { + md.printTrig(); } } } +//______________________________________________________________________________ void ModuleConfig::check() const { for (const auto& md : modules) { @@ -48,6 +69,7 @@ void ModuleConfig::check() const } } +//______________________________________________________________________________ void Module::check() const { // make sure that the channel has <= 2 triggers @@ -61,17 +83,21 @@ void Module::check() const } } -void Module::setChannel(int slot, int8_t chID, int16_t lID, bool read, bool trig, int tF, int tL, int tS, int tT) +//______________________________________________________________________________ +void Module::setChannel(int slot, int8_t chID, int16_t fID, bool read, bool trig, int tF, int tL, int tS, int tT) { if (slot < 0 || slot >= MaxChannels || chID < 0 || chID > NChannels) { - LOG(FATAL) << "Improper module channel settings" << slot << ' ' << chID << ' ' << lID << ' ' << read << ' ' << trig + LOG(FATAL) << "Improper module channel settings" << slot << ' ' << chID << ' ' << fID << ' ' << read << ' ' << trig << ' ' << tF << ' ' << tL << ' ' << tS << ' ' << tT; } - linkID[slot] = lID; + feeID[slot] = fID; channelID[slot] = chID; readChannel[slot] = read; + // In the 2020 firmware implementation, autotrigger bits are computed for each channel + // Therefore we put the trig flag just for the triggering channels + // Discriminator parameters are stored for all modules trigChannel[slot] = trig; - if (trig) { + if (tS > 0 && tT > 0) { if (tL + tS + 1 >= NTimeBinsPerBC) { LOG(FATAL) << "Sum of Last and Shift trigger parameters exceed allowed range"; } diff --git a/Detectors/ZDC/macro/CreateModuleConfig.C b/Detectors/ZDC/macro/CreateModuleConfig.C index a06b585b494a4..164a71bb83d1f 100644 --- a/Detectors/ZDC/macro/CreateModuleConfig.C +++ b/Detectors/ZDC/macro/CreateModuleConfig.C @@ -32,14 +32,20 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, int modID; //------------------------------------------- + // Up to 8 modules with four channels + // setChannel(int slot, int8_t chID, int16_t fID, bool read, bool trig = false, int tF = 0, int tL = 0, int tS = 0, int tT = 0) + // module id must be in the range 0-7 + // channel id must be in range 0-3 + // frontend id must be in range 0-15 and identify the pair of channels connected to + // each fibre { modID = 0; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZNAC, 0, true, true, -5, 6, 4, 12); - module.setChannel(1, IdZNASum, 1, false, false); - module.setChannel(2, IdZNA1, 2, true, false); - module.setChannel(3, IdZNA2, 3, true, false); + module.setChannel(0, IdZNAC, 2 * modID, true, true, -5, 6, 4, 12); + module.setChannel(1, IdZNASum, 2 * modID, false, false, -5, 6, 4, 12); + module.setChannel(2, IdZNA1, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZNA2, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -47,10 +53,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 1; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZNAC, 4, false, true, -5, 6, 4, 12); - module.setChannel(1, IdZNASum, 5, true, false); - module.setChannel(2, IdZNA1, 6, true, false); - module.setChannel(3, IdZNA2, 7, true, false); + module.setChannel(0, IdZNAC, 2 * modID, false, true, -5, 6, 4, 12); + module.setChannel(1, IdZNASum, 2 * modID, true, false, -5, 6, 4, 12); + module.setChannel(2, IdZNA3, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZNA4, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -58,10 +64,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 2; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZNCC, 8, true, true, -5, 6, 4, 12); - module.setChannel(1, IdZNCSum, 9, false, false); - module.setChannel(2, IdZNC1, 10, true, false); - module.setChannel(3, IdZNC2, 11, true, false); + module.setChannel(0, IdZNCC, 2 * modID, true, true, -5, 6, 4, 12); + module.setChannel(1, IdZNCSum, 2 * modID, false, false, -5, 6, 4, 12); + module.setChannel(2, IdZNC1, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZNC2, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -69,10 +75,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 3; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZNCC, 12, false, true, -5, 6, 4, 12); - module.setChannel(1, IdZNCSum, 13, true, false); - module.setChannel(2, IdZNC3, 14, true, false); - module.setChannel(3, IdZNC4, 15, true, false); + module.setChannel(0, IdZNCC, 2 * modID, false, true, -5, 6, 4, 12); + module.setChannel(1, IdZNCSum, 2 * modID, true, false, -5, 6, 4, 12); + module.setChannel(2, IdZNC3, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZNC4, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -80,10 +86,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 4; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZPAC, 16, true, true, -5, 6, 4, 12); - module.setChannel(1, IdZEM1, 16, true, true, -5, 6, 4, 12); - module.setChannel(2, IdZPA1, 17, true, false); - module.setChannel(3, IdZPA2, 17, true, false); + module.setChannel(0, IdZPAC, 2 * modID, true, true, -5, 6, 4, 12); + module.setChannel(1, IdZEM1, 2 * modID, true, true, -5, 6, 4, 12); + module.setChannel(2, IdZPA1, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZPA2, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -91,10 +97,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 5; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZPAC, 18, false, true, -5, 6, 4, 12); - module.setChannel(1, IdZPASum, 18, true, false); - module.setChannel(2, IdZPA1, 19, true, false); - module.setChannel(3, IdZPA2, 19, true, false); + module.setChannel(0, IdZPAC, 2 * modID, false, true, -5, 6, 4, 12); + module.setChannel(1, IdZPASum, 2 * modID, true, false, -5, 6, 4, 12); + module.setChannel(2, IdZPA3, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZPA4, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -102,10 +108,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 6; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZPCC, 16, true, true, -5, 6, 4, 12); - module.setChannel(1, IdZEM2, 16, true, true, -5, 6, 4, 12); - module.setChannel(2, IdZPC1, 17, true, false); - module.setChannel(3, IdZPC2, 17, true, false); + module.setChannel(0, IdZPCC, 2 * modID, true, true, -5, 6, 4, 12); + module.setChannel(1, IdZEM2, 2 * modID, true, true, -5, 6, 4, 12); + module.setChannel(2, IdZPC1, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZPC2, 2 * modID + 1, true, false, -5, 6, 4, 12); // } //------------------------------------------- @@ -113,10 +119,10 @@ void CreateModuleConfig(long tmin = 0, long tmax = -1, modID = 7; auto& module = conf.modules[modID]; module.id = modID; - module.setChannel(0, IdZPCC, 18, false, true, -5, 6, 4, 12); - module.setChannel(1, IdZPCSum, 18, true, false); - module.setChannel(2, IdZPC3, 19, true, false); - module.setChannel(3, IdZPC4, 19, true, false); + module.setChannel(0, IdZPCC, 2 * modID, false, true, -5, 6, 4, 12); + module.setChannel(1, IdZPCSum, 2 * modID, true, false, -5, 6, 4, 12); + module.setChannel(2, IdZPC3, 2 * modID + 1, true, false, -5, 6, 4, 12); + module.setChannel(3, IdZPC4, 2 * modID + 1, true, false, -5, 6, 4, 12); // } conf.check(); diff --git a/Detectors/ZDC/macro/CreateSimCondition.C b/Detectors/ZDC/macro/CreateSimCondition.C index 38f80a5e1c844..e93b14e883a28 100644 --- a/Detectors/ZDC/macro/CreateSimCondition.C +++ b/Detectors/ZDC/macro/CreateSimCondition.C @@ -35,6 +35,16 @@ void CreateSimCondition(std::string sourceDataPath = "signal_shapes.root", const float Gains[5] = {15.e-3, 30.e-3, 100.e-3, 15.e-3, 30.e-3}; // gain (response per photoelectron) const float fudgeFactor = 2.7; // ad hoc factor to tune the gain in the MC + // Source of line shapes, pedestal and noise for each channel + // Missing histos for: towers 1-4 of all calorimeters, zem1, all towers of zpc + std::string ShapeName[o2::zdc::NChannels] = { + "znatc", "znatc", "znatc", "znatc", "znatc", "znatc", // ZNAC, ZNA1, ZNA2, ZNA3, ZNA4, ZNAS (shape not used) + "zpatc", "zpatc", "zpatc", "zpatc", "zpatc", "zpatc", // ZPAC, ZPA1, ZPA2, ZPA3, ZPA4, ZPAS (shape not used) + "zem2", "zem2", // ZEM1, ZEM2 + "znctc", "znctc", "znctc", "znctc", "znctc", "znctc", // ZNCC, ZNC1, ZNC2, ZNC3, ZNC4, ZNCS (shape not used) + "zpatc", "zpatc", "zpatc", "zpatc", "zpatc", "zpatc" // ZPCC, ZPC1, ZPC2, ZPC3, ZPC4, ZPCS (shape not used) + }; + for (int ic = 0; ic < o2::zdc::NChannels; ic++) { auto& channel = conf.channels[ic]; @@ -43,9 +53,7 @@ void CreateSimCondition(std::string sourceDataPath = "signal_shapes.root", // channel.gain = (tower != o2::zdc::Sum) ? fudgeFactor * Gains[det - 1] : 1.0; // - // at the moment we use wf_znatc histo for all channels, to be fixed when - // more histos are created. So, we read in the loop the same histo - std::string histoShapeName = "hw_znatc"; + std::string histoShapeName = "hw_" + ShapeName[ic]; TH1* histoShape = (TH1*)sourceData.GetObjectUnchecked(histoShapeName.c_str()); if (!histoShape) { LOG(FATAL) << "Failed to extract the shape histogram " << histoShapeName; @@ -72,14 +80,14 @@ void CreateSimCondition(std::string sourceDataPath = "signal_shapes.root", // channel.pedestal = gRandom->Gaus(1800., 30.); // - std::string histoPedNoiseName = "hb_znatc"; // same comment here (at the moment the same histo used) + std::string histoPedNoiseName = "hb_" + ShapeName[ic]; TH1* histoPedNoise = (TH1*)sourceData.GetObjectUnchecked(histoPedNoiseName.c_str()); if (!histoPedNoise) { LOG(FATAL) << "Failed to extract the pedestal noise histogram " << histoPedNoise; } channel.pedestalNoise = histoPedNoise->GetRMS(); // - std::string histoPedFluctName = "hp_znatc"; // same comment here (at the moment the same histo used) + std::string histoPedFluctName = "hp_" + ShapeName[ic]; TH1* histoPedFluct = (TH1*)sourceData.GetObjectUnchecked(histoPedFluctName.c_str()); if (!histoPedFluct) { LOG(FATAL) << "Failed to extract the pedestal fluctuation histogram " << histoPedFluct; diff --git a/Detectors/ZDC/raw/CMakeLists.txt b/Detectors/ZDC/raw/CMakeLists.txt new file mode 100644 index 0000000000000..64027eadc6d35 --- /dev/null +++ b/Detectors/ZDC/raw/CMakeLists.txt @@ -0,0 +1,31 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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(ZDCRaw + SOURCES src/DumpRaw.cxx src/raw-parser.cxx + PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::ZDCBase O2::ZDCSimulation + O2::DataFormatsZDC O2::CCDB O2::SimConfig O2::DPLUtils + O2::DetectorsRaw O2::Headers) + + +o2_target_root_dictionary(ZDCRaw + HEADERS include/ZDCRaw/DumpRaw.h) + +o2_add_executable(raw-parser + COMPONENT_NAME zdc + SOURCES src/raw-parser.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DPLUtils + O2::ZDCSimulation + O2::ZDCRaw + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils) + diff --git a/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h b/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h new file mode 100644 index 0000000000000..167b54f4b480e --- /dev/null +++ b/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h @@ -0,0 +1,50 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <TH1.h> +#include <TH2.h> +#include "ZDCBase/Constants.h" +#include "ZDCSimulation/ZDCSimParam.h" +#include "DataFormatsZDC/RawEventData.h" +#ifndef ALICEO2_ZDC_DUMPRAW_H_ +#define ALICEO2_ZDC_DUMPRAW_H_ +namespace o2 +{ +namespace zdc +{ +class DumpRaw +{ + public: + DumpRaw() = default; + void init(); + int process(const EventData& ev); + int process(const EventChData& ch); + int processWord(const UInt_t* word); + int getHPos(uint32_t board, uint32_t ch); + void write(); + void setVerbosity(int v) + { + mVerbosity = v; + } + int getVerbosity() const { return mVerbosity; } + + private: + void setStat(TH1* h); + int mVerbosity = 1; + TH1* mBaseline[NDigiChannels] = {nullptr}; + TH1* mCounts[NDigiChannels] = {nullptr}; + TH2* mSignal[NDigiChannels] = {nullptr}; + TH2* mBunch[NDigiChannels] = {nullptr}; + EventChData mCh; +}; +} // namespace zdc +} // namespace o2 + +#endif diff --git a/Detectors/ZDC/raw/src/DumpRaw.cxx b/Detectors/ZDC/raw/src/DumpRaw.cxx new file mode 100644 index 0000000000000..c139f74cdfaf8 --- /dev/null +++ b/Detectors/ZDC/raw/src/DumpRaw.cxx @@ -0,0 +1,263 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <TROOT.h> +#include <TPad.h> +#include <TString.h> +#include <TStyle.h> +#include <TPaveStats.h> +#include "ZDCRaw/DumpRaw.h" +#include "CommonConstants/LHCConstants.h" +#include "ZDCSimulation/Digits2Raw.h" +#include "FairLogger.h" + +using namespace o2::zdc; + +void DumpRaw::setStat(TH1* h) +{ + TString hn = h->GetName(); + h->Draw(); + gPad->Update(); + TPaveStats* st = (TPaveStats*)h->GetListOfFunctions()->FindObject("stats"); + st->SetFillStyle(1001); + st->SetBorderSize(1); + if (hn.BeginsWith("hp")) { + st->SetOptStat(111111); + st->SetX1NDC(0.1); + st->SetX2NDC(0.3); + st->SetY1NDC(0.640); + st->SetY2NDC(0.9); + } else if (hn.BeginsWith("hc")) { + st->SetOptStat(1111); + st->SetX1NDC(0.799); + st->SetX2NDC(0.999); + st->SetY1NDC(0.829); + st->SetY2NDC(0.999); + } else if (hn.BeginsWith("hs") || hn.BeginsWith("hb")) { + st->SetOptStat(11); + st->SetX1NDC(0.799); + st->SetX2NDC(0.9995); + st->SetY1NDC(0.904); + st->SetY2NDC(0.999); + } +} + +void DumpRaw::init() +{ + gROOT->SetBatch(); + auto& sopt = ZDCSimParam::Instance(); + int nbx = (sopt.nBCAheadTrig + 1) * NTimeBinsPerBC; + Double_t xmin = -sopt.nBCAheadTrig * NTimeBinsPerBC - 0.5; + Double_t xmax = NTimeBinsPerBC - 0.5; + for (UInt_t i = 0; i < NDigiChannels; i++) { + uint32_t imod = i / NChPerModule; + uint32_t ich = i % NChPerModule; + if (mBaseline[i]) { + mBaseline[i]->Reset(); + } else { + TString hname = TString::Format("hp%d%d", imod, ich); + TString htit = TString::Format("Baseline mod. %d ch. %d;Average orbit baseline", imod, ich); + //mBaseline[i]=new TH1F(hname,htit,ADCRange,ADCMin-0.5,ADCMax+0.5); + mBaseline[i] = new TH1F(hname, htit, 16378, -0.125, ADCMax + 0.125); + } + if (mCounts[i]) { + mCounts[i]->Reset(); + } else { + TString hname = TString::Format("hc%d%d", imod, ich); + TString htit = TString::Format("Counts mod. %d ch. %d; Orbit hits", imod, ich); + mCounts[i] = new TH1F(hname, htit, o2::constants::lhc::LHCMaxBunches + 1, -0.5, o2::constants::lhc::LHCMaxBunches + 0.5); + } + if (mSignal[i]) { + mSignal[i]->Reset(); + } else { + TString hname = TString::Format("hs%d%d", imod, ich); + TString htit = TString::Format("Signal mod. %d ch. %d; Sample; ADC", imod, ich); + mSignal[i] = new TH2F(hname, htit, nbx, xmin, xmax, ADCRange, ADCMin - 0.5, ADCMax + 0.5); + } + if (mBunch[i]) { + mBunch[i]->Reset(); + } else { + TString hname = TString::Format("hb%d%d", imod, ich); + TString htit = TString::Format("Bunch mod. %d ch. %d; Sample; ADC", imod, ich); + mBunch[i] = new TH2F(hname, htit, 100, -0.5, 99.5, 36, -35.5, 0.5); + } + } + // Word id not present in payload + mCh.f.fixed_0 = Id_wn; + mCh.f.fixed_1 = Id_wn; + mCh.f.fixed_2 = Id_wn; +} + +void DumpRaw::write() +{ + TFile* f = new TFile("ZDCDumpRaw.root", "recreate"); + if (f->IsZombie()) { + LOG(FATAL) << "Cannot write to file " << f->GetName(); + return; + } + for (UInt_t i = 0; i < NDigiChannels; i++) { + if (mBunch[i] && mBunch[i]->GetEntries() > 0) { + setStat(mBunch[i]); + mBunch[i]->Write(); + } + } + for (UInt_t i = 0; i < NDigiChannels; i++) { + if (mBaseline[i] && mBaseline[i]->GetEntries() > 0) { + setStat(mBaseline[i]); + mBaseline[i]->Write(); + } + } + for (UInt_t i = 0; i < NDigiChannels; i++) { + if (mCounts[i] && mCounts[i]->GetEntries() > 0) { + setStat(mCounts[i]); + mCounts[i]->Write(); + } + } + for (UInt_t i = 0; i < NDigiChannels; i++) { + if (mSignal[i] && mSignal[i]->GetEntries() > 0) { + setStat(mSignal[i]); + mSignal[i]->Write(); + } + } + f->Close(); +} + +inline int DumpRaw::getHPos(uint32_t board, uint32_t ch) +{ + int ih = board * 4 + ch; + if (ih < NDigiChannels) { + return ih; + } else { + LOG(ERROR) << "Wrong ih " << ih << " board " << board << " ch " << ch; + return -1; + } +} + +int DumpRaw::processWord(const UInt_t* word) +{ + if (word == 0) { + printf("NULL\n"); + return 1; + } + if ((word[0] & 0x3) == Id_w0) { + for (Int_t iw = 0; iw < NWPerGBTW; iw++) { + mCh.w[0][iw] = word[iw]; + } + } else if ((word[0] & 0x3) == Id_w1) { + if (mCh.f.fixed_0 == Id_w0) { + for (Int_t iw = 0; iw < NWPerGBTW; iw++) { + mCh.w[1][iw] = word[iw]; + } + } else { + LOG(ERROR) << "Wrong word sequence"; + mCh.f.fixed_0 = Id_wn; + mCh.f.fixed_1 = Id_wn; + mCh.f.fixed_2 = Id_wn; + } + } else if ((word[0] & 0x3) == Id_w2) { + if (mCh.f.fixed_0 == Id_w0 && mCh.f.fixed_1 == Id_w1) { + for (Int_t iw = 0; iw < NWPerGBTW; iw++) { + mCh.w[2][iw] = word[iw]; + } + process(mCh); + } else { + LOG(ERROR) << "Wrong word sequence"; + } + mCh.f.fixed_0 = Id_wn; + mCh.f.fixed_1 = Id_wn; + mCh.f.fixed_2 = Id_wn; + } else { + // Word not present in payload + LOG(FATAL) << "Event format error"; + return 1; + } + return 0; +} + +int DumpRaw::process(const EventChData& ch) +{ + static constexpr int last_bc = o2::constants::lhc::LHCMaxBunches - 1; + // Not empty event + auto f = ch.f; + int ih = getHPos(f.board, f.ch); + if (mVerbosity > 0) { + for (Int_t iw = 0; iw < NWPerBc; iw++) { + Digits2Raw::print_gbt_word(ch.w[iw]); + } + } + UShort_t us[12]; + Short_t s[12]; + us[0] = f.s00; + us[1] = f.s01; + us[2] = f.s02; + us[3] = f.s03; + us[4] = f.s04; + us[5] = f.s05; + us[6] = f.s06; + us[7] = f.s07; + us[8] = f.s08; + us[9] = f.s09; + us[10] = f.s10; + us[11] = f.s11; + for (Int_t i = 0; i < 12; i++) { + if (us[i] > ADCMax) { + s[i] = us[i] - ADCRange; + } else { + s[i] = us[i]; + } + //printf("%d %u %d\n",i,us[i],s[i]); + } + if (f.Alice_3) { + for (Int_t i = 0; i < 12; i++) { + mSignal[ih]->Fill(i - 36., Double_t(s[i])); + } + } + if (f.Alice_2) { + for (Int_t i = 0; i < 12; i++) { + mSignal[ih]->Fill(i - 24., Double_t(s[i])); + } + } + if (f.Alice_1 || f.Auto_1) { + for (Int_t i = 0; i < 12; i++) { + mSignal[ih]->Fill(i - 12., Double_t(s[i])); + } + } + if (f.Alice_0 || f.Auto_0) { + for (Int_t i = 0; i < 12; i++) { + mSignal[ih]->Fill(i + 0., Double_t(s[i])); + } + Double_t bc_d = UInt_t(f.bc / 100); + Double_t bc_m = UInt_t(f.bc % 100); + mBunch[ih]->Fill(bc_m, -bc_d); + } + if (f.bc == last_bc) { + Int_t offset = f.offset - 32768; + Double_t foffset = offset / 8.; + mBaseline[ih]->Fill(foffset); + mCounts[ih]->Fill(f.hits); + } + return 0; +} + +int DumpRaw::process(const EventData& ev) +{ + for (Int_t im = 0; im < NModules; im++) { + for (Int_t ic = 0; ic < NChPerModule; ic++) { + if (ev.data[im][ic].f.fixed_0 == Id_w0 && ev.data[im][ic].f.fixed_1 == Id_w1 && ev.data[im][ic].f.fixed_2 == Id_w2) { + process(ev.data[im][ic]); + } else if (ev.data[im][ic].f.fixed_0 == 0 && ev.data[im][ic].f.fixed_1 == 0 && ev.data[im][ic].f.fixed_2 == 0) { + // Empty channel + } else { + LOG(ERROR) << "Data format error"; + } + } + } + return 0; +} diff --git a/Detectors/ZDC/raw/src/ZDCRawLinkDef.h b/Detectors/ZDC/raw/src/ZDCRawLinkDef.h new file mode 100644 index 0000000000000..71519a02a652e --- /dev/null +++ b/Detectors/ZDC/raw/src/ZDCRawLinkDef.h @@ -0,0 +1,17 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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; + +#endif diff --git a/Detectors/ZDC/raw/src/raw-parser.cxx b/Detectors/ZDC/raw/src/raw-parser.cxx new file mode 100644 index 0000000000000..888d3e4d167e1 --- /dev/null +++ b/Detectors/ZDC/raw/src/raw-parser.cxx @@ -0,0 +1,104 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/WorkflowSpec.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/ConfigParamSpec.h" +#include "DPLUtils/DPLRawParser.h" +#include "Headers/DataHeader.h" +#include "DataFormatsZDC/RawEventData.h" +#include "ZDCSimulation/Digits2Raw.h" +#include "ZDCRaw/DumpRaw.h" +#include <vector> +#include <sstream> + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "input-spec", VariantType::String, "A:ZDC/RAWDATA", {"selection string input specs"}}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + printf("Ciao\n"); + WorkflowSpec workflow; + workflow.emplace_back(DataProcessorSpec{ + "zdc-raw-parser", + select(config.options().get<std::string>("input-spec").c_str()), + Outputs{}, + AlgorithmSpec{[](InitContext& setup) { + auto loglevel = setup.options().get<int>("log-level"); + return adaptStateless([loglevel](InputRecord& inputs, DataAllocator& outputs) { + o2::zdc::DumpRaw zdc_dr; + zdc_dr.init(); + zdc_dr.setVerbosity(loglevel); + DPLRawParser parser(inputs); + o2::header::DataHeader const* lastDataHeader = nullptr; + std::stringstream rdhprintout; + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + // retrieving RDH v4 + auto const* rdh = it.get_if<o2::header::RAWDataHeaderV4>(); + // retrieving the raw pointer of the page + auto const* raw = it.raw(); + // retrieving payload pointer of the page + auto const* payload = it.data(); + // size of payload + size_t payloadSize = it.size(); + // offset of payload in the raw page + size_t offset = it.offset(); + // Note: the following code is only for printing out raw page information + const auto* dh = it.o2DataHeader(); + if (loglevel > 0) { + if (dh != lastDataHeader) { + // print the DataHeader information only for the first part or if we have high verbosity + if (loglevel > 1 || dh->splitPayloadIndex == 0) { + rdhprintout << dh->dataOrigin.as<std::string>() << "/" + << dh->dataDescription.as<std::string>() << "/" + << dh->subSpecification << " "; + // at high verbosity print part number, otherwise only the total number of parts + if (loglevel > 1) { + rdhprintout << "part " + std::to_string(dh->splitPayloadIndex) + " of " + std::to_string(dh->splitPayloadParts); + } else { + rdhprintout << " " + std::to_string(dh->splitPayloadParts) + " part(s)"; + } + rdhprintout << " payload size " << dh->payloadSize << std::endl; + } + if (!rdhprintout.str().empty()) { + LOG(INFO) << rdhprintout.str(); + rdhprintout.str(std::string()); + } + } + if (payload != nullptr) { + for (Int_t ip = 0; ip < payloadSize; ip += 16) { + //o2::zdc::Digits2Raw::print_gbt_word((const UInt_t*)&payload[ip]); + zdc_dr.processWord((const UInt_t*)&payload[ip]); + } + } + } + lastDataHeader = dh; + } + if (loglevel > 0) { + LOG(INFO) << rdhprintout.str(); + } + zdc_dr.write(); + }); }}, + Options{ + {"log-level", VariantType::Int, 1, {"Logging level [0-2]"}}}}); + return workflow; +} diff --git a/Detectors/ZDC/simulation/CMakeLists.txt b/Detectors/ZDC/simulation/CMakeLists.txt index f58348a73e05d..e96e992eca329 100644 --- a/Detectors/ZDC/simulation/CMakeLists.txt +++ b/Detectors/ZDC/simulation/CMakeLists.txt @@ -10,9 +10,10 @@ o2_add_library(ZDCSimulation SOURCES src/Detector.cxx src/Digitizer.cxx src/SimCondition.cxx - src/ZDCSimParam.cxx src/SpatialPhotonResponse.cxx + src/ZDCSimParam.cxx src/SpatialPhotonResponse.cxx src/Digits2Raw.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::ZDCBase - O2::DataFormatsZDC O2::CCDB O2::SimConfig) + O2::DataFormatsZDC O2::CCDB O2::SimConfig + O2::DetectorsRaw O2::Headers) o2_target_root_dictionary(ZDCSimulation HEADERS include/ZDCSimulation/Hit.h @@ -23,3 +24,12 @@ o2_target_root_dictionary(ZDCSimulation include/ZDCSimulation/SpatialPhotonResponse.h) o2_data_file(COPY data DESTINATION Detectors/ZDC/simulation) + +o2_add_executable(digi2raw + COMPONENT_NAME zdc + SOURCES src/digi2raw.cxx + PUBLIC_LINK_LIBRARIES O2::ZDCSimulation + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils + Boost::program_options) diff --git a/Detectors/ZDC/simulation/include/ZDCSimulation/Detector.h b/Detectors/ZDC/simulation/include/ZDCSimulation/Detector.h index 0835250b0a82d..b6e79327cd041 100644 --- a/Detectors/ZDC/simulation/include/ZDCSimulation/Detector.h +++ b/Detectors/ZDC/simulation/include/ZDCSimulation/Detector.h @@ -83,7 +83,7 @@ class Detector : public o2::base::DetImpl<Detector> void addAlignableVolumes() const override {} o2::zdc::Hit* addHit(Int_t trackID, Int_t parentID, Int_t sFlag, Float_t primaryEnergy, Int_t detID, Int_t secID, - Vector3D<float> pos, Vector3D<float> mom, Float_t tof, Vector3D<float> xImpact, Double_t energyloss, + math_utils::Vector3D<float> pos, math_utils::Vector3D<float> mom, Float_t tof, math_utils::Vector3D<float> xImpact, Double_t energyloss, Int_t nphePMC, Int_t nphePMQ); private: @@ -96,8 +96,8 @@ class Detector : public o2::base::DetImpl<Detector> void createDetectors(); // determine detector; sector/tower and impact coordinates given volumename and position - void getDetIDandSecID(TString const& volname, Vector3D<float> const& x, - Vector3D<float>& xDet, int& detector, int& sector) const; + void getDetIDandSecID(TString const& volname, math_utils::Vector3D<float> const& x, + math_utils::Vector3D<float>& xDet, int& detector, int& sector) const; // Define sensitive volumes void defineSensitiveVolumes(); @@ -117,7 +117,7 @@ class Detector : public o2::base::DetImpl<Detector> int parent, float tof, float trackenergy, - Vector3D<float> const& xImp, + math_utils::Vector3D<float> const& xImp, float eDep, float x, float y, float z, float px, float py, float pz) { // A new hit is created when there is nothing yet for this det + sector @@ -129,8 +129,8 @@ class Detector : public o2::base::DetImpl<Detector> mTotLightPMQ = nphe; } - Vector3D<float> pos(x, y, z); - Vector3D<float> mom(px, py, pz); + math_utils::Vector3D<float> pos(x, y, z); + math_utils::Vector3D<float> mom(px, py, pz); addHit(trackn, parent, issecondary, trackenergy, detector, sector, pos, mom, tof, xImp, eDep, mTotLightPMC, mTotLightPMQ); // stack->addHit(GetDetId()); @@ -164,7 +164,7 @@ class Detector : public o2::base::DetImpl<Detector> Float_t mTrackEta; Float_t mPrimaryEnergy; - Vector3D<float> mXImpact; + math_utils::Vector3D<float> mXImpact; Float_t mTotLightPMC; Float_t mTotLightPMQ; Int_t mMediumPMCid = -1; diff --git a/Detectors/ZDC/simulation/include/ZDCSimulation/Digitizer.h b/Detectors/ZDC/simulation/include/ZDCSimulation/Digitizer.h index e8423b59a4a6d..a9d6cf8843556 100644 --- a/Detectors/ZDC/simulation/include/ZDCSimulation/Digitizer.h +++ b/Detectors/ZDC/simulation/include/ZDCSimulation/Digitizer.h @@ -37,11 +37,13 @@ class Digitizer public: struct BCCache : public o2::InteractionRecord { std::array<ChannelBCDataF, NChannels> data = {}; + std::array<ChannelBCDataF, NDigiChannels> digi = {}; std::vector<o2::zdc::MCLabel> labels; bool digitized = false; bool triggerChecked = false; - uint32_t trigChanMask = 0; // mask of triggered channels IDs - static constexpr uint32_t AllChannelsMask = 0x1 << NChannels; + uint32_t trigChanMask = 0; // mask of triggered channels + uint8_t extTrig = 0; // external trigger + static constexpr uint32_t AllChannelsMask = 0x80000000; BCCache(); @@ -108,7 +110,6 @@ class Digitizer private: static constexpr int BCCacheMin = -1, BCCacheMax = 5, NBC2Cache = 1 + BCCacheMax - BCCacheMin; - static constexpr int ADCMin = -2048, ADCMax = 2047; // 12 bit ADC std::bitset<NChannels> chanPattern(uint32_t v) const { @@ -133,7 +134,7 @@ class Digitizer o2::InteractionTimeRecord mIR; std::deque<o2::InteractionRecord> mIRExternalTrigger; // IRs of externally provided triggered (at the moment MC sampled interactions) - std::deque<BCCache> mCache; // cached BCs data + std::deque<BCCache> mCache; // cached BCs data std::array<std::vector<int16_t>, NChannels> mTrigChannelsData; // buffer for fast access to triggered channels data int mTrigBinMin = 0xffff; // prefetched min and max int mTrigBinMax = -0xffff; // bins to be checked for trigger @@ -143,6 +144,7 @@ class Digitizer const SimCondition* mSimCondition = nullptr; ///< externally set SimCondition const ModuleConfig* mModuleConfig = nullptr; ///< externally set ModuleConfig std::vector<TriggerChannelConfig> mTriggerConfig; ///< triggering channels + uint32_t mTriggerableChanMask = 0; ///< mask of digital discriminators that can actually trigger a module std::vector<ModuleConfAux> mModConfAux; ///< module check helper std::vector<BCCache*> mFastCache; ///< for the fast iteration over cached BCs + dummy std::vector<uint32_t> mStoreChanMask; ///< pattern of channels to store diff --git a/Detectors/ZDC/simulation/include/ZDCSimulation/Digits2Raw.h b/Detectors/ZDC/simulation/include/ZDCSimulation/Digits2Raw.h new file mode 100644 index 0000000000000..48b41b73c8c05 --- /dev/null +++ b/Detectors/ZDC/simulation/include/ZDCSimulation/Digits2Raw.h @@ -0,0 +1,103 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Digits2Raw.h +/// \brief converts digits to raw format +/// \author pietro.cortese@cern.ch + +#ifndef ALICEO2_ZDC_DIGITS2RAW_H_ +#define ALICEO2_ZDC_DIGITS2RAW_H_ +#include <string> +#include <bitset> +#include <Rtypes.h> +#include "Headers/RAWDataHeader.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DetectorsRaw/RawFileWriter.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DataFormatsZDC/RawEventData.h" +#include "ZDCBase/ModuleConfig.h" +#include "ZDCSimulation/SimCondition.h" +#include "DataFormatsZDC/BCData.h" +#include "DataFormatsZDC/ChannelData.h" + +namespace o2 +{ +namespace zdc +{ +class Digits2Raw +{ + public: + Digits2Raw() = default; + void emptyBunches(std::bitset<3564>& bunchPattern); /// Prepare list of clean empty bunches for baseline evaluation + void processDigits(const std::string& outDir, const std::string& fileDigitsName); + void setModuleConfig(const ModuleConfig* moduleConfig) { mModuleConfig = moduleConfig; }; + const ModuleConfig* getModuleConfig() { return mModuleConfig; }; + void setSimCondition(const SimCondition* SimCondition) { mSimCondition = SimCondition; }; + const SimCondition* getSimCondition() { return mSimCondition; }; + + o2::raw::RawFileWriter& getWriter() { return mWriter; } + + void setFilePerLink(bool v) { mOutputPerLink = v; } + bool getFilePerLink() const { return mOutputPerLink; } + + void setVerbosity(int v) + { + mVerbosity = v; + mWriter.setVerbosity(v); + } + int getVerbosity() const { return mVerbosity; } + + // void setContinuous(bool v = true) { mIsContinuous = v; } + bool isContinuous() const { return mIsContinuous; } + static void print_gbt_word(const UInt_t* word, const ModuleConfig* moduleConfig = nullptr); + + private: + void setTriggerMask(); + void updatePedestalReference(int bc); + void resetSums(uint32_t orbit); + void resetOutputStructure(UShort_t bc, UInt_t orbit, bool is_dummy); /// Reset output structure not incrementing scalers for dummy bunches + void assignTriggerBits(int ibc, UShort_t bc, UInt_t orbit, bool is_dummy); /// Assign trigger bits + void insertLastBunch(int ibc, uint32_t orbit); /// Insert an empty bunch at last position in orbit + void convertDigits(int ibc); /// Convert digits into raw data + void writeDigits(); /// Writes raw data to file + std::vector<o2::zdc::BCData> mzdcBCData, *mzdcBCDataPtr = &mzdcBCData; + std::vector<o2::zdc::ChannelData> mzdcChData, *mzdcChDataPtr = &mzdcChData; + int mNbc = 0; + BCData mBCD; + EventData mZDC; /// Output structure + bool mIsContinuous = true; /// Continuous (self-triggered) or externally-triggered readout + bool mOutputPerLink = false; /// Split output + const ModuleConfig* mModuleConfig = nullptr; /// Trigger/readout configuration object + const SimCondition* mSimCondition = nullptr; /// Pedestal/noise configuration object + UShort_t mScalers[NModules][NChPerModule] = {0}; /// ZDC orbit scalers + UInt_t mLastOrbit = 0; /// Last processed orbit + uint32_t mTriggerMask = 0; /// Trigger mask from ModuleConfig + std::string mPrintTriggerMask = ""; /// Nice printout of trigger mask + int32_t mNEmpty = -1; /// Number of clean empty bunches for pedestal evaluation + std::array<uint16_t, o2::constants::lhc::LHCMaxBunches> mEmpty = {0}; /// Clean empty bunches along orbit + UInt_t mLastNEmpty = 0; /// Last number of empty bunches used + Double_t mSumPed[NModules][NChPerModule] = {0}; /// Pedestal integrated on clean empty bunches + uint16_t mPed[NModules][NChPerModule] = {0}; /// Current pedestal + + o2::raw::RawFileWriter mWriter{"ZDC"}; + uint32_t mLinkID = 0; + uint16_t mCruID = 0; + uint32_t mEndPointID = 0; + uint64_t mFeeID = 0; + + int mVerbosity = 0; + + ///////////////////////////////////////////////// + ClassDefNV(Digits2Raw, 1); +}; +} // namespace zdc +} // namespace o2 + +#endif diff --git a/Detectors/ZDC/simulation/include/ZDCSimulation/Hit.h b/Detectors/ZDC/simulation/include/ZDCSimulation/Hit.h index a4536896134c6..9df0246c5f5db 100644 --- a/Detectors/ZDC/simulation/include/ZDCSimulation/Hit.h +++ b/Detectors/ZDC/simulation/include/ZDCSimulation/Hit.h @@ -45,7 +45,7 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> /// \param nphePMC light output on common PMT /// \param nphePMQ light output on sector PMT Hit(int trackID, int parent, Bool_t sFlag, Float_t primaryEnergy, Int_t detID, Int_t sectorID, - Vector3D<float> pos, Vector3D<float> mom, Float_t tof, Vector3D<float> xImpact, Float_t energyloss, Int_t nphePMC, + math_utils::Vector3D<float> pos, math_utils::Vector3D<float> mom, Float_t tof, math_utils::Vector3D<float> xImpact, Float_t energyloss, Int_t nphePMC, Int_t nphePMQ); void setPMCLightYield(float val) { mNphePMC = val; } @@ -64,8 +64,8 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> Float_t mPrimaryEnergy; Int_t mNoContributingSteps = 1; Int_t mSectorID; - Vector3D<float> mMomentum; - Vector3D<float> mXImpact; + math_utils::Vector3D<float> mMomentum; + math_utils::Vector3D<float> mXImpact; Int_t mNphePMC; Int_t mNphePMQ; @@ -73,7 +73,7 @@ class Hit : public o2::BasicXYZEHit<Float_t, Float_t> }; inline Hit::Hit(int trackID, int parent, Bool_t sFlag, Float_t primaryEnergy, Int_t detID, Int_t sectorID, - Vector3D<float> pos, Vector3D<float> mom, Float_t tof, Vector3D<float> xImpact, Float_t energyloss, + math_utils::Vector3D<float> pos, math_utils::Vector3D<float> mom, Float_t tof, math_utils::Vector3D<float> xImpact, Float_t energyloss, Int_t nphePMC, Int_t nphePMQ) : BasicXYZEHit(pos.X(), pos.Y(), pos.Z(), tof, energyloss, trackID, detID), mParentID(parent), diff --git a/Detectors/ZDC/simulation/src/Detector.cxx b/Detectors/ZDC/simulation/src/Detector.cxx index ebc113840be29..4b872f55346db 100644 --- a/Detectors/ZDC/simulation/src/Detector.cxx +++ b/Detectors/ZDC/simulation/src/Detector.cxx @@ -111,8 +111,9 @@ void Detector::InitializeO2Detector() std::string inputDir; const char* aliceO2env = std::getenv("O2_ROOT"); - if (aliceO2env) + if (aliceO2env) { inputDir = std::string(aliceO2env); + } inputDir += "/share/Detectors/ZDC/simulation/data/"; //ZN case loadLightTable(mLightTableZN, 0, ZNRADIUSBINS, inputDir + "light22620362207s"); @@ -188,26 +189,27 @@ void Detector::defineSensitiveVolumes() } // determines detectorID and sectorID from volume and coordinates -void Detector::getDetIDandSecID(TString const& volname, Vector3D<float> const& x, - Vector3D<float>& xDet, int& detector, int& sector) const +void Detector::getDetIDandSecID(TString const& volname, math_utils::Vector3D<float> const& x, + math_utils::Vector3D<float>& xDet, int& detector, int& sector) const { if (volname.BeginsWith("ZN")) { // for the neutron calorimeter if (x.Z() > 0) { detector = ZNA; - xDet = x - Vector3D<float>(Geometry::ZNAPOSITION[0], Geometry::ZNAPOSITION[1], Geometry::ZNAPOSITION[2]); + xDet = x - math_utils::Vector3D<float>(Geometry::ZNAPOSITION[0], Geometry::ZNAPOSITION[1], Geometry::ZNAPOSITION[2]); } else if (x.Z() < 0) { detector = ZNC; - xDet = x - Vector3D<float>(Geometry::ZNCPOSITION[0], Geometry::ZNCPOSITION[1], Geometry::ZNCPOSITION[2]); + xDet = x - math_utils::Vector3D<float>(Geometry::ZNCPOSITION[0], Geometry::ZNCPOSITION[1], Geometry::ZNCPOSITION[2]); } // now determine sector/tower if (xDet.X() <= 0.) { if (xDet.Y() <= 0.) { sector = Ch1; - } else + } else { sector = Ch3; + } } else { if (xDet.Y() <= 0.) { sector = Ch2; @@ -221,10 +223,10 @@ void Detector::getDetIDandSecID(TString const& volname, Vector3D<float> const& x // proton calorimeter if (x.Z() > 0) { detector = ZPA; // (NB -> DIFFERENT FROM AliRoot!!!) - xDet = x - Vector3D<float>(Geometry::ZPAPOSITION[0], Geometry::ZPAPOSITION[1], Geometry::ZPAPOSITION[2]); + xDet = x - math_utils::Vector3D<float>(Geometry::ZPAPOSITION[0], Geometry::ZPAPOSITION[1], Geometry::ZPAPOSITION[2]); } else if (x.Z() < 0) { detector = ZPC; // (NB -> DIFFERENT FROM AliRoot!!!) - xDet = x - Vector3D<float>(Geometry::ZPCPOSITION[0], Geometry::ZPCPOSITION[1], Geometry::ZPCPOSITION[2]); + xDet = x - math_utils::Vector3D<float>(Geometry::ZPCPOSITION[0], Geometry::ZPCPOSITION[1], Geometry::ZPCPOSITION[2]); } // determine sector/tower @@ -246,7 +248,7 @@ void Detector::getDetIDandSecID(TString const& volname, Vector3D<float> const& x } else if (volname.BeginsWith("ZE")) { // electromagnetic calorimeter detector = ZEM; - xDet = x - Vector3D<float>(Geometry::ZEMPOSITION[0], Geometry::ZEMPOSITION[1], Geometry::ZEMPOSITION[2]); + xDet = x - math_utils::Vector3D<float>(Geometry::ZEMPOSITION[0], Geometry::ZEMPOSITION[1], Geometry::ZEMPOSITION[2]); sector = (x.X() > 0.) ? Ch1 : Ch2; return; } @@ -292,8 +294,8 @@ Bool_t Detector::ProcessHits(FairVolume* v) // determine detectorID and sectorID int detector = -1; int sector = -1; - Vector3D<float> xImp; - getDetIDandSecID(volname, Vector3D<float>(x[0], x[1], x[2]), xImp, detector, sector); + math_utils::Vector3D<float> xImp; + getDetIDandSecID(volname, math_utils::Vector3D<float>(x[0], x[1], x[2]), xImp, detector, sector); auto stack = (o2::data::Stack*)fMC->GetStack(); int trackn = stack->GetCurrentTrackNumber(); @@ -306,8 +308,9 @@ Bool_t Detector::ProcessHits(FairVolume* v) // If the particle is in a ZN or ZP fiber connected to the common PMT // then the assigned sector is 0 (PMC) NB-> does not work for ZEM - if ((fMC->CurrentMedium() == mMediumPMCid) && (detector != ZEM)) + if ((fMC->CurrentMedium() == mMediumPMCid) && (detector != ZEM)) { sector = 0; + } //printf("ProcessHits: x=(%f, %f, %f) \n",x[0], x[1], x[2]); //printf("\tDET %d SEC %d -> XImpact=(%f, %f, %f)\n",detector,sector, xImp.X(), xImp.Y(), xImp.Z()); @@ -388,8 +391,8 @@ Bool_t Detector::ProcessHits(FairVolume* v) mTotLightPMQ = nphe; } - Vector3D<float> pos(x[0], x[1], x[2]); - Vector3D<float> mom(p[0], p[1], p[2]); + math_utils::Vector3D<float> pos(x[0], x[1], x[2]); + math_utils::Vector3D<float> mom(p[0], p[1], p[2]); addHit(trackn, mLastPrincipalTrackEntered, issecondary, trackenergy, detector, sector, pos, mom, tof, xImp, eDep, mTotLightPMC, mTotLightPMQ); stack->addHit(GetDetId()); @@ -430,7 +433,7 @@ Bool_t Detector::ProcessHits(FairVolume* v) bool Detector::createHitsFromImage(SpatialPhotonResponse const& image, int detector) { // one image will make one hit per sector - Vector3D<float> xImp(0., 0., 0.); // good value + math_utils::Vector3D<float> xImp(0., 0., 0.); // good value const int Nx = image.getNx(); const int Ny = image.getNy(); @@ -496,7 +499,7 @@ bool Detector::createHitsFromImage(SpatialPhotonResponse const& image, int detec //_____________________________________________________________________________ o2::zdc::Hit* Detector::addHit(Int_t trackID, Int_t parentID, Int_t sFlag, Float_t primaryEnergy, Int_t detID, - Int_t secID, Vector3D<float> pos, Vector3D<float> mom, Float_t tof, Vector3D<float> xImpact, + Int_t secID, math_utils::Vector3D<float> pos, math_utils::Vector3D<float> mom, Float_t tof, math_utils::Vector3D<float> xImpact, Double_t energyloss, Int_t nphePMC, Int_t nphePMQ) { LOG(DEBUG4) << "Adding hit for track " << trackID << " X (" << pos.X() << ", " << pos.Y() << ", " @@ -1389,8 +1392,9 @@ void Detector::createCsideBeamLine() if (mTCLIAAPERTURE < 3.5) { boxpar[0] = 5.4 / 2.; boxpar[1] = (3.5 - mTCLIAAPERTURE - mVCollSideCCentreY - 0.7) / 2.; // FIX IT!!!!!!!! - if (boxpar[1] < 0.) + if (boxpar[1] < 0.) { boxpar[1] = 0.; + } boxpar[2] = 124.4 / 2.; TVirtualMC::GetMC()->Gsvolu("QCVC", "BOX ", getMediumID(kGraphite), boxpar, 3); TVirtualMC::GetMC()->Gspos("QCVC", 1, "QE02", -boxpar[0], mTCLIAAPERTURE + mVCollSideCCentreY + boxpar[1], -totLength1 / 2. + 160.8 + 78. + 148. / 2., 0, "ONLY"); // FIX IT!!!!!!!! @@ -2327,19 +2331,22 @@ Bool_t Detector::calculateTableIndexes(int& ibeta, int& iangle, int& iradius) //particle velocity float ptot = TMath::Sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]); float beta = 0.; - if (energy > 0.) + if (energy > 0.) { beta = ptot / energy; + } if (beta >= 0.67) { - if (beta <= 0.75) + if (beta <= 0.75) { ibeta = 0; - else if (beta > 0.75 && beta <= 0.85) + } else if (beta > 0.75 && beta <= 0.85) { ibeta = 1; - else if (beta > 0.85 && beta <= 0.95) + } else if (beta > 0.85 && beta <= 0.95) { ibeta = 2; - else if (beta > 0.95) + } else if (beta > 0.95) { ibeta = 3; - } else + } + } else { return kFALSE; + } //track angle wrt fibre axis (||LHC axis) double umom[3] = {0., 0., 0.}, udet[3] = {0., 0., 0.}; umom[0] = p[0] / ptot; @@ -2348,18 +2355,20 @@ Bool_t Detector::calculateTableIndexes(int& ibeta, int& iangle, int& iradius) fMC->Gmtod(umom, udet, 2); double angleRad = TMath::ACos(udet[2]); double angleDeg = angleRad * kRaddeg; - if (angleDeg < 110.) + if (angleDeg < 110.) { iangle = int(0.5 + angleDeg / 2.); - else + } else { return kFALSE; + } //radius from fibre axis fMC->Gmtod(x, xDet, 1); float radius = 0.; if (TMath::Abs(udet[0]) > 0) { float dcoeff = udet[1] / udet[0]; radius = TMath::Abs((xDet[1] - dcoeff * xDet[0]) / TMath::Sqrt(dcoeff * dcoeff + 1.)); - } else + } else { radius = TMath::Abs(udet[0]); + } iradius = int(radius * 1000. + 1.); //printf("\t beta %f angle %f radius %f\n",beta, angleDeg, radius); return kTRUE; diff --git a/Detectors/ZDC/simulation/src/Digitizer.cxx b/Detectors/ZDC/simulation/src/Digitizer.cxx index 36c84ae72ea56..ab34933392945 100644 --- a/Detectors/ZDC/simulation/src/Digitizer.cxx +++ b/Detectors/ZDC/simulation/src/Digitizer.cxx @@ -8,6 +8,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "CommonConstants/LHCConstants.h" #include "CCDB/BasicCCDBManager.h" #include "CCDB/CCDBTimeStampUtils.h" #include "ZDCSimulation/Digitizer.h" @@ -19,26 +20,32 @@ using namespace o2::zdc; +//______________________________________________________________________________ Digitizer::BCCache::BCCache() { memset(&data, 0, NChannels * sizeof(ChannelBCDataF)); } +//______________________________________________________________________________ Digitizer::ModuleConfAux::ModuleConfAux(const Module& md) : id(md.id) { + if (md.id < 0 || md.id >= NModules) { + LOG(FATAL) << "Module id = " << md.id << " not in allowed range [0:" << NModules << ")"; + } // construct aux helper from full module description for (int ic = Module::MaxChannels; ic--;) { if (md.channelID[ic] >= IdDummy) { if (md.readChannel[ic]) { - readChannels |= 0x1 << md.channelID[ic]; + readChannels |= 0x1 << (NChPerModule * md.id + ic); } if (md.trigChannel[ic]) { - trigChannels |= 0x1 << md.channelID[ic]; + trigChannels |= 0x1 << (NChPerModule * md.id + ic); } } } } +//______________________________________________________________________________ // this will process hits and fill the digit vector with digits which are finalized void Digitizer::process(const std::vector<o2::zdc::Hit>& hits, std::vector<o2::zdc::BCData>& digitsBC, @@ -95,6 +102,7 @@ void Digitizer::process(const std::vector<o2::zdc::Hit>& hits, } } +//______________________________________________________________________________ void Digitizer::flush(std::vector<o2::zdc::BCData>& digitsBC, std::vector<o2::zdc::ChannelData>& digitsCh, o2::dataformats::MCTruthContainer<o2::zdc::MCLabel>& labels) @@ -160,6 +168,7 @@ void Digitizer::flush(std::vector<o2::zdc::BCData>& digitsBC, mCache.erase(mCache.begin(), mCache.end()); } +//______________________________________________________________________________ void Digitizer::generatePedestal() { for (int idet : {ZNA, ZPA, ZNC, ZPC}) { @@ -176,12 +185,14 @@ void Digitizer::generatePedestal() mPedestalBLFluct[IdZEM2] = gRandom->Gaus(0, mSimCondition->channels[IdZEM2].pedestalFluct); } +//______________________________________________________________________________ void Digitizer::digitizeBC(BCCache& bc) { auto& bcdata = bc.data; + auto& bcdigi = bc.digi; // apply gain for (int idet : {ZNA, ZPA, ZNC, ZPC}) { - for (int ic : {Ch1, Ch2, Ch3, Ch4}) { + for (int ic : {Common, Ch1, Ch2, Ch3, Ch4}) { int chan = toChannel(idet, ic); auto gain = mSimCondition->channels[chan].gain; for (int ib = NTimeBinsPerBC; ib--;) { @@ -193,7 +204,7 @@ void Digitizer::digitizeBC(BCCache& bc) bcdata[IdZEM1][ib] *= mSimCondition->channels[IdZEM1].gain; bcdata[IdZEM2][ib] *= mSimCondition->channels[IdZEM2].gain; } - // + // Prepare sum of towers before adding noise for (int ib = NTimeBinsPerBC; ib--;) { bcdata[IdZNASum][ib] = mSimCondition->channels[IdZNASum].gain * (bcdata[IdZNA1][ib] + bcdata[IdZNA2][ib] + bcdata[IdZNA3][ib] + bcdata[IdZNA4][ib]); @@ -204,54 +215,78 @@ void Digitizer::digitizeBC(BCCache& bc) bcdata[IdZPCSum][ib] = mSimCondition->channels[IdZPCSum].gain * (bcdata[IdZPC1][ib] + bcdata[IdZPC2][ib] + bcdata[IdZPC3][ib] + bcdata[IdZPC4][ib]); } - for (int chan = NChannels; chan--;) { - const auto& chanConf = mSimCondition->channels[chan]; - auto pedBaseLine = mSimCondition->channels[chan].pedestal; - for (int ib = NTimeBinsPerBC; ib--;) { - bcdata[chan][ib] += gRandom->Gaus(pedBaseLine + mPedestalBLFluct[chan], chanConf.pedestalNoise); - int adc = std::nearbyint(bcdata[chan][ib]); - bcdata[chan][ib] = adc < ADCMax ? (adc > ADCMin ? adc : ADCMin) : ADCMax; + // Digitize the signals connected to each channel of the different modules + for (const auto& md : mModuleConfig->modules) { + if (md.id >= 0 && md.id < NModules) { + for (int ic = Module::MaxChannels; ic--;) { + int id = md.channelID[ic]; + if (id >= 0 && id < NChannels) { + const auto& chanConf = mSimCondition->channels[id]; + auto pedBaseLine = mSimCondition->channels[id].pedestal; + // Geographical position of signal in the setup + auto ipos = NChPerModule * md.id + ic; + for (int ib = NTimeBinsPerBC; ib--;) { + bcdigi[ipos][ib] = bcdata[id][ib] + gRandom->Gaus(pedBaseLine + mPedestalBLFluct[id], chanConf.pedestalNoise); + int adc = std::nearbyint(bcdigi[ipos][ib]); + bcdigi[ipos][ib] = adc < ADCMax ? (adc > ADCMin ? adc : ADCMin) : ADCMax; + } + LOG(DEBUG) << "md " << md.id << " ch " << ic << " sig " << id << " " << ChannelNames[id] + << bcdigi[ipos][0] << " " << bcdigi[ipos][1] << " " << bcdigi[ipos][2] << " " << bcdigi[ipos][3] << " " << bcdigi[ipos][4] << " " << bcdigi[ipos][5] << " " + << bcdigi[ipos][6] << " " << bcdigi[ipos][7] << " " << bcdigi[ipos][8] << " " << bcdigi[ipos][9] << " " << bcdigi[ipos][10] << " " << bcdigi[ipos][11]; + } + } } } bc.digitized = true; } +//______________________________________________________________________________ bool Digitizer::triggerBC(int ibc) { // check trigger for the cached BC in the position ibc auto& bcCached = *mFastCache[ibc]; LOG(DEBUG) << "CHECK TRIGGER " << ibc << " IR=" << bcCached; - if (mIsContinuous) { - for (int ic = mTriggerConfig.size(); ic--;) { - const auto& trigCh = mTriggerConfig[ic]; - bool okPrev = false; - int last1 = trigCh.last + 2; - // look for 2 consecutive bins (the 1st one spanning trigCh.first : trigCh.last range) so that - // signal[bin]-signal[bin+trigCh.shift] > trigCh.threshold - for (int ib = trigCh.first; ib < last1; ib++) { // ib may be negative, so we shift by offs and look in the ADC cache - int binF, bcFidx = ibc + binHelper(ib, binF); - int binL, bcLidx = ibc + binHelper(ib + trigCh.shift, binL); - const auto& bcF = (bcFidx < 0 || !mFastCache[bcFidx]) ? mDummyBC : *mFastCache[bcFidx]; - const auto& bcL = (bcLidx < 0 || !mFastCache[bcLidx]) ? mDummyBC : *mFastCache[bcLidx]; - bool ok = bcF.data[trigCh.id][binF] - bcL.data[trigCh.id][binL] > trigCh.threshold; - if (ok && okPrev) { // trigger ok! - bcCached.trigChanMask |= 0x1 << trigCh.id; // register trigger mask - LOG(DEBUG) << " triggering channel " << int(trigCh.id) << " => " << bcCached.trigChanMask; - break; + + // Check trigger condition regardless of run type, will apply later the trigger mask + for (const auto& md : mModuleConfig->modules) { + if (md.id >= 0 && md.id < NModules) { + for (int ic = Module::MaxChannels; ic--;) { + //int id=md.channelID[ic]; + auto trigCh = md.trigChannelConf[ic]; + int id = trigCh.id; + if (id >= 0 && id < NChannels) { + auto ipos = NChPerModule * md.id + ic; + bool okPrev = false; + int last1 = trigCh.last + 2; // To be modified. The new requirement is 3 consecutive samples + // look for 2 consecutive bins (the 1st one spanning trigCh.first : trigCh.last range) so that + // signal[bin]-signal[bin+trigCh.shift] > trigCh.threshold + for (int ib = trigCh.first; ib < last1; ib++) { // ib may be negative, so we shift by offs and look in the ADC cache + int binF, bcFidx = ibc + binHelper(ib, binF); + int binL, bcLidx = ibc + binHelper(ib + trigCh.shift, binL); + const auto& bcF = (bcFidx < 0 || !mFastCache[bcFidx]) ? mDummyBC : *mFastCache[bcFidx]; + const auto& bcL = (bcLidx < 0 || !mFastCache[bcLidx]) ? mDummyBC : *mFastCache[bcLidx]; + bool ok = bcF.digi[ipos][binF] - bcL.digi[ipos][binL] > trigCh.threshold; + if (ok && okPrev) { // trigger ok! + bcCached.trigChanMask |= 0x1 << (NChPerModule * md.id + ic); // register trigger mask + LOG(DEBUG) << bcF.digi[ipos][binF] << " - " << bcL.digi[ipos][binL] << " = " << bcF.digi[ipos][binF] - bcL.digi[ipos][binL] << " > " << trigCh.threshold; + LOG(DEBUG) << " hit [" << md.id << "," << ic << "] " << int(id) << "(" << ChannelNames[id] << ") => " << bcCached.trigChanMask; + break; + } + okPrev = ok; + } } - okPrev = ok; } - } // loop over trigger channels - } else { - // just check if this BC IR corresponds to externall trigger - if (!mIRExternalTrigger.empty() && mIRExternalTrigger.front() == bcCached) { - bcCached.trigChanMask = AllChannelsMask; - mIRExternalTrigger.pop_front(); // suppress accounted external trigger } } - if (bcCached.trigChanMask) { // there are triggered channels, flag modules/channels to read + // just check if this BC IR corresponds to external trigger + if (!mIRExternalTrigger.empty() && mIRExternalTrigger.front() == bcCached) { + bcCached.extTrig = ALICETriggerMask; + mIRExternalTrigger.pop_front(); // suppress accounted external trigger + } + // This works in autotrigger mode, partially for triggered mode + if (bcCached.trigChanMask & mTriggerableChanMask) { // there are triggered channels, flag modules/channels to read for (int ibcr = ibc - mNBCAHead; ibcr <= ibc; ibcr++) { auto& bcr = mStoreChanMask[ibcr + mNBCAHead]; for (const auto& mdh : mModConfAux) { @@ -265,6 +300,7 @@ bool Digitizer::triggerBC(int ibc) return bcCached.trigChanMask; } +//______________________________________________________________________________ void Digitizer::storeBC(const BCCache& bc, uint32_t chan2Store, std::vector<o2::zdc::BCData>& digitsBC, std::vector<o2::zdc::ChannelData>& digitsCh, o2::dataformats::MCTruthContainer<o2::zdc::MCLabel>& labels) @@ -276,14 +312,20 @@ void Digitizer::storeBC(const BCCache& bc, uint32_t chan2Store, LOG(DEBUG) << "Storing ch: " << chanPattern(chan2Store) << " trigger: " << chanPattern(bc.trigChanMask) << " for BC " << bc; int first = digitsCh.size(), nSto = 0; - for (int ic = 0; ic < NChannels; ic++) { - if (chan2Store & (0x1 << ic)) { - digitsCh.emplace_back(ic, bc.data[ic]); - nSto++; + for (const auto& md : mModuleConfig->modules) { + if (md.id >= 0 && md.id < NModules) { + for (int ic = 0; ic < Module::MaxChannels; ic++) { + auto ipos = NChPerModule * md.id + ic; + if (chan2Store & (0x1 << ipos)) { + digitsCh.emplace_back(md.channelID[ic], bc.digi[ipos]); + nSto++; + } + } } } + int nBC = digitsBC.size(); - digitsBC.emplace_back(first, nSto, bc, chan2Store, bc.trigChanMask); + digitsBC.emplace_back(first, nSto, bc, chan2Store, bc.trigChanMask, bc.extTrig); // TODO clarify if we want to store MC labels for all channels or only for stored ones for (const auto& lbl : bc.labels) { if (chan2Store & (0x1 << lbl.getChannel())) { @@ -292,6 +334,7 @@ void Digitizer::storeBC(const BCCache& bc, uint32_t chan2Store, } } +//______________________________________________________________________________ void Digitizer::phe2Sample(int nphe, int parID, double timeHit, std::array<o2::InteractionRecord, NBC2Cache> const& cachedIR, int nCachedIR, int channel) { //function to simulate the waveform from no. of photoelectrons seen in a given sample @@ -314,7 +357,7 @@ void Digitizer::phe2Sample(int nphe, int parID, double timeHit, std::array<o2::I break; } if (sample >= 0) { - auto signal = chanConfig.shape[sample] * nphe; // signal accounting for the gain + auto signal = chanConfig.shape[sample] * nphe; // signal not accounting for the gain (*bcCache).data[channel][ib] += signal; added = true; } @@ -326,6 +369,7 @@ void Digitizer::phe2Sample(int nphe, int parID, double timeHit, std::array<o2::I } while (++ir < nCachedIR && !stop); } +//______________________________________________________________________________ o2::zdc::Digitizer::BCCache& Digitizer::getCreateBCCache(const o2::InteractionRecord& ir) { if (mCache.empty() || mCache.back() < ir) { @@ -354,6 +398,7 @@ o2::zdc::Digitizer::BCCache& Digitizer::getCreateBCCache(const o2::InteractionRe return mCache.front(); } +//______________________________________________________________________________ o2::zdc::Digitizer::BCCache* Digitizer::getBCCache(const o2::InteractionRecord& ir) { // get pointer on existing cache @@ -365,6 +410,7 @@ o2::zdc::Digitizer::BCCache* Digitizer::getBCCache(const o2::InteractionRecord& return nullptr; } +//______________________________________________________________________________ void Digitizer::init() { if (mCCDBServer.empty()) { @@ -380,6 +426,7 @@ void Digitizer::init() << " BCs will be stored ahead of Trigger"; } +//______________________________________________________________________________ void Digitizer::refreshCCDB() { // fetch ccdb objects. TODO: decide if this stays here or goes to the Spec @@ -396,41 +443,42 @@ void Digitizer::refreshCCDB() mTriggerConfig.clear(); mModConfAux.clear(); for (const auto& md : mModuleConfig->modules) { - if (md.id >= 0) { + if (md.id >= 0 && md.id < NModules) { mModConfAux.emplace_back(md); - // for (int ic = Module::MaxChannels; ic--;) { - if (md.trigChannel[ic]) { // check if this triggering channel was already registered - bool skip = false; - for (int is = mTriggerConfig.size(); is--;) { - if (mTriggerConfig[is].id == md.channelID[ic]) { - skip = true; - break; - } + // We consider all channels that can produce a hit + if (md.trigChannel[ic] || (md.trigChannelConf[ic].shift > 0 && md.trigChannelConf[ic].threshold > 0)) { + const auto& trgChanConf = md.trigChannelConf[ic]; + if (trgChanConf.last + trgChanConf.shift + 1 >= NTimeBinsPerBC) { + LOG(ERROR) << "Wrong trigger settings"; + } + mTriggerConfig.emplace_back(trgChanConf); + // We insert in the trigger mask only the channels that are actually triggering + // Trigger mask is geographical, bit position is relative to the module and channel + // where signal is connected + if (md.trigChannel[ic]) { + LOG(INFO) << "Adding channel [" << md.id << "," << ic << "] " << int(trgChanConf.id) << '(' << channelName(trgChanConf.id) << ") as triggering one"; + // TODO insert check if bit is already used. Should never happen + mTriggerableChanMask |= 0x1 << (NChPerModule * md.id + ic); + } else { + LOG(INFO) << "Adding channel [" << md.id << "," << ic << "] " << int(trgChanConf.id) << '(' << channelName(trgChanConf.id) << ") as discriminator"; } - if (!skip) { - const auto& trgChanConf = md.trigChannelConf[ic]; - if (trgChanConf.last + trgChanConf.shift + 1 >= NTimeBinsPerBC) { - LOG(FATAL) << "Wrong trigger settings"; - } - mTriggerConfig.emplace_back(trgChanConf); - LOG(INFO) << "Adding channel " << int(trgChanConf.id) << '(' << channelName(trgChanConf.id) << ") as triggering one"; - if (trgChanConf.first < mTrigBinMin) { - mTrigBinMin = trgChanConf.first; - } - if (trgChanConf.last + trgChanConf.shift > mTrigBinMax) { - mTrigBinMax = trgChanConf.last + trgChanConf.shift; - } + if (trgChanConf.first < mTrigBinMin) { + mTrigBinMin = trgChanConf.first; } + if (trgChanConf.last + trgChanConf.shift > mTrigBinMax) { + mTrigBinMax = trgChanConf.last + trgChanConf.shift; + } + } + if (md.feeID[ic] < 0 || md.feeID[ic] >= NLinks) { + LOG(FATAL) << "FEEID " << md.feeID[ic] << " not in allowed range [0:" << NLinks << ")"; } } + } else { + LOG(FATAL) << "Module id: " << md.id << " is out of range"; } } - if (int(mTriggerConfig.size()) > MaxTriggerChannels) { - LOG(FATAL) << "Too many triggering channels (" << mTriggerConfig.size() << ')'; - } mModuleConfig->print(); - // } if (!mSimCondition) { // load this only once @@ -440,7 +488,7 @@ void Digitizer::refreshCCDB() } } -//______________________________________________________________ +//______________________________________________________________________________ void Digitizer::BCCache::print() const { std::bitset<NChannels> tmsk(trigChanMask); diff --git a/Detectors/ZDC/simulation/src/Digits2Raw.cxx b/Detectors/ZDC/simulation/src/Digits2Raw.cxx new file mode 100644 index 0000000000000..029fedb3daad9 --- /dev/null +++ b/Detectors/ZDC/simulation/src/Digits2Raw.cxx @@ -0,0 +1,704 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <string> +#include <TFile.h> +#include <TTree.h> +#include <TRandom.h> +#include <TMath.h> +#include "ZDCBase/Constants.h" +#include "ZDCBase/ModuleConfig.h" +#include "ZDCSimulation/Digitizer.h" +#include "ZDCSimulation/Digits2Raw.h" +#include "ZDCSimulation/MCLabel.h" +#include "ZDCSimulation/ZDCSimParam.h" +#include "CommonUtils/StringUtils.h" +#include "FairLogger.h" + +using namespace o2::zdc; + +//ClassImp(Digits2Raw); +//______________________________________________________________________________ +void Digits2Raw::processDigits(const std::string& outDir, const std::string& fileDigitsName) +{ + auto& sopt = ZDCSimParam::Instance(); + mIsContinuous = sopt.continuous; + + if (!mModuleConfig) { + LOG(FATAL) << "Missing ModuleConfig configuration object"; + return; + } + + if (!mSimCondition) { + LOG(FATAL) << "Missing SimCondition configuration object"; + return; + } + + if (mNEmpty < 0) { + LOG(FATAL) << "Bunch crossing map is not initialized"; + return; + } + + if (mNEmpty == 0) { + LOG(WARNING) << "Bunch crossing map has zero clean empty bunches"; + } + + setTriggerMask(); + + std::string outd = outDir; + if (outd.back() != '/') { + outd += '/'; + } + + mLinkID = uint32_t(0); + mCruID = uint16_t(0); + mEndPointID = uint32_t(0); + for (int ilink = 0; ilink < NLinks; ilink++) { + mFeeID = uint64_t(ilink); + std::string outFileLink = mOutputPerLink ? o2::utils::concat_string(outd, "zdc_link", std::to_string(ilink), ".raw") : o2::utils::concat_string(outd, "zdc.raw"); + mWriter.registerLink(mFeeID, mCruID, mLinkID, mEndPointID, outFileLink); + } + + std::unique_ptr<TFile> digiFile(TFile::Open(fileDigitsName.c_str())); + if (!digiFile || digiFile->IsZombie()) { + LOG(ERROR) << "Failed to open input digits file " << fileDigitsName; + return; + } + + TTree* digiTree = (TTree*)digiFile->Get("o2sim"); + if (!digiTree) { + LOG(ERROR) << "Failed to get digits tree"; + return; + } + + if (digiTree->GetBranch("ZDCDigitBC")) { + digiTree->SetBranchAddress("ZDCDigitBC", &mzdcBCDataPtr); + } else { + LOG(ERROR) << "Branch ZDCDigitBC is missing"; + return; + } + + if (digiTree->GetBranch("ZDCDigitCh")) { + digiTree->SetBranchAddress("ZDCDigitCh", &mzdcChDataPtr); + } else { + LOG(ERROR) << "Branch ZDCDigitCh is missing"; + return; + } + + digiTree->SetBranchStatus("ZDCDigitLabel*", 0); + + for (int ient = 0; ient < digiTree->GetEntries(); ient++) { + digiTree->GetEntry(ient); + mNbc = mzdcBCData.size(); + LOG(INFO) << "Entry " << ient << " : " << mNbc << " BCs stored"; + for (int ibc = 0; ibc < mNbc; ibc++) { + mBCD = mzdcBCData[ibc]; + convertDigits(ibc); + writeDigits(); + // Detect last event or orbit change and insert last bunch + if (ibc == (mNbc - 1)) { + // For last event we need to close last orbit (if it is needed) + if (mzdcBCData[ibc].ir.bc != 3563) { + insertLastBunch(ibc, mzdcBCData[ibc].ir.orbit); + writeDigits(); + } + } else { + auto this_orbit = mzdcBCData[ibc].ir.orbit; + auto next_orbit = mzdcBCData[ibc + 1].ir.orbit; + // If current bunch is last bunch in the orbit we don't insert it again + if (mzdcBCData[ibc].ir.bc == 3563) { + this_orbit = this_orbit + 1; + } + // We may need to insert more than one orbit + for (auto orbit = this_orbit; orbit < next_orbit; orbit++) { + insertLastBunch(ibc, orbit); + writeDigits(); + } + } + } + } + digiFile->Close(); +} + +//______________________________________________________________________________ +void Digits2Raw::setTriggerMask() +{ + mTriggerMask = 0; + mPrintTriggerMask = ""; + for (Int_t im = 0; im < NModules; im++) { + if (im > 0) { + mPrintTriggerMask += " "; + } + mPrintTriggerMask += std::to_string(im); + mPrintTriggerMask += "["; + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + if (mModuleConfig->modules[im].trigChannel[ic]) { + UInt_t tmask = 0x1 << (im * NChPerModule + ic); + mTriggerMask = mTriggerMask | tmask; + mPrintTriggerMask += "T"; + } else { + mPrintTriggerMask += " "; + } + } + mPrintTriggerMask += "]"; + UInt_t mytmask = mTriggerMask >> (im * NChPerModule); + printf("Trigger mask for module %d 0123 %s%s%s%s\n", im, + mytmask & 0x1 ? "T" : "N", + mytmask & 0x2 ? "T" : "N", + mytmask & 0x4 ? "T" : "N", + mytmask & 0x8 ? "T" : "N"); + } + printf("trigger_mask=0x%08x %s\n", mTriggerMask, mPrintTriggerMask.data()); +} + +//______________________________________________________________________________ +inline void Digits2Raw::resetSums(uint32_t orbit) +{ + for (Int_t im = 0; im < NModules; im++) { + for (Int_t ic = 0; ic < NChPerModule; ic++) { + mScalers[im][ic] = 0; + mSumPed[im][ic] = 0; + mPed[im][ic] = 0; + } + } + mLastOrbit = orbit; + mLastNEmpty = 0; +} + +//______________________________________________________________________________ +inline void Digits2Raw::updatePedestalReference(int bc) +{ + // Compute or update baseline reference + if (mEmpty[bc] > 0 && mEmpty[bc] != mLastNEmpty) { + for (Int_t im = 0; im < NModules; im++) { + for (Int_t ic = 0; ic < NChPerModule; ic++) { + // Identify connected channel + auto id = mModuleConfig->modules[im].channelID[ic]; + auto base_m = mSimCondition->channels[id].pedestal; // Average pedestal + auto base_s = mSimCondition->channels[id].pedestalFluct; // Baseline oscillations + auto base_n = mSimCondition->channels[id].pedestalNoise; // Electronic noise + Double_t deltan = mEmpty[bc] - mLastNEmpty; + // We assume to have a fluctuation every two bunch crossings + // Will need to tune this parameter + Double_t k = 2.; + mSumPed[im][ic] += gRandom->Gaus(12. * deltan * base_m, 12. * k * base_s * TMath::Sqrt(deltan / k)); + // Adding in quadrature the RMS of pedestal electronic noise + mSumPed[im][ic] += gRandom->Gaus(0, base_n * TMath::Sqrt(12. * deltan)); + Double_t myped = TMath::Nint(8. * mSumPed[im][ic] / Double_t(mEmpty[bc]) / 12. + 32768); + if (myped < 0) { + myped = 0; + } + if (myped > 65535) { + myped = 65535; + } + mPed[im][ic] = myped; + } + } + mLastNEmpty = mEmpty[bc]; + } +} + +//______________________________________________________________________________ +inline void Digits2Raw::resetOutputStructure(UShort_t bc, UInt_t orbit, bool is_dummy) +{ + // Increment scalers and reset output structure + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + // Fixed words + mZDC.data[im][ic].w[0][0] = Id_w0; + mZDC.data[im][ic].w[0][1] = 0; + mZDC.data[im][ic].w[0][2] = 0; + mZDC.data[im][ic].w[0][3] = 0; + mZDC.data[im][ic].w[1][0] = Id_w1; + mZDC.data[im][ic].w[1][1] = 0; + mZDC.data[im][ic].w[1][2] = 0; + mZDC.data[im][ic].w[1][3] = 0; + mZDC.data[im][ic].w[2][0] = Id_w2; + mZDC.data[im][ic].w[2][1] = 0; + mZDC.data[im][ic].w[2][2] = 0; + mZDC.data[im][ic].w[2][3] = 0; + // Module and channel numbers + mZDC.data[im][ic].f.board = im; + mZDC.data[im][ic].f.ch = ic; + // Orbit and bunch crossing + mZDC.data[im][ic].f.orbit = orbit; + mZDC.data[im][ic].f.bc = bc; + // If channel is hit in current bunch crossing + if (!is_dummy) { + if (mBCD.triggers & (0x1 << (im * NChPerModule + ic))) { + mScalers[im][ic]++; // increment scalers + mZDC.data[im][ic].f.Hit = 1; // flag bunch crossing + } + } + mZDC.data[im][ic].f.hits = mScalers[im][ic]; + mZDC.data[im][ic].f.offset = mPed[im][ic]; + } + } +} + +//______________________________________________________________________________ +inline void Digits2Raw::assignTriggerBits(int ibc, UShort_t bc, UInt_t orbit, bool is_dummy) +{ + // Triggers refer to the HW trigger conditions (32 possible channels) + // Autotrigger, current bunch crossing + UInt_t triggers_0 = 0; + // Autotrigger and ALICE trigger bits are zero for a dummy bunch crossing + if (!is_dummy) { + triggers_0 = mBCD.triggers; + // ALICE current bunch crossing + if (mBCD.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_0 = 1; + } + } + } + } + + // Next bunch crossings (ALICE and autotrigger) + UInt_t triggers_1 = 0, triggers_2 = 0, triggers_3 = 0, triggers_m = 0; + for (Int_t is = 1; is < 4; is++) { + Int_t ibc_peek = ibc + is; + if (ibc_peek >= mNbc) { + break; + } + const auto& bcd_peek = mzdcBCData[ibc_peek]; + UShort_t bc_peek = bcd_peek.ir.bc; + UInt_t orbit_peek = bcd_peek.ir.orbit; + if (bcd_peek.triggers) { + if (orbit_peek == orbit) { + if ((bc_peek - bc) == 1) { + triggers_1 = bcd_peek.triggers; + if (bcd_peek.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_1 = 1; + } + } + } + } else if ((bc_peek - bc) == 2) { + triggers_2 = bcd_peek.triggers; + if (bcd_peek.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_2 = 1; + } + } + } + } else if ((bc_peek - bc) == 3) { + triggers_3 = bcd_peek.triggers; + if (bcd_peek.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_3 = 1; + } + } + } + break; + } + } else if (orbit_peek == (orbit + 1)) { + if ((bc_peek + 3564 - bc) == 1) { + triggers_1 = bcd_peek.triggers; + if (bcd_peek.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_1 = 1; + } + } + } + } else if ((bc_peek + 3564 - bc) == 2) { + triggers_2 = bcd_peek.triggers; + if (bcd_peek.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_2 = 1; + } + } + } + } else if ((bc_peek + 3564 - bc) == 3) { + triggers_3 = bcd_peek.triggers; + if (bcd_peek.ext_triggers) { + for (UInt_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Alice_3 = 1; + } + } + } + break; + } + } else { + break; + } + } + } + + // Previous bunch crossing just for autotrigger + // For a dummy last bunch crossing previous bunch is the one pointed by ibc + { + Int_t ibc_peek = is_dummy ? ibc : ibc - 1; + if (ibc_peek >= 0) { + const auto& bcd_peek = mzdcBCData[ibc - 1]; + UShort_t bc_peek = bcd_peek.ir.bc; + UInt_t orbit_peek = bcd_peek.ir.orbit; + if (bcd_peek.triggers) { + if (orbit_peek == orbit) { + if ((bc - bc_peek) == 1) { + triggers_m = bcd_peek.triggers; + } + } else if (orbit_peek == (orbit - 1)) { + if (bc == 0 && bc_peek == 3563) { + triggers_m = bcd_peek.triggers; + } + } + } + } + } + + // Assign trigger bits in payload + for (Int_t im = 0; im < NModules; im++) { + UInt_t tmask = (0xf << (im * NChPerModule)) & mTriggerMask; + if (triggers_m & tmask) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Auto_m = 1; + } + } + if (triggers_0 & tmask) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Auto_0 = 1; + } + } + if (triggers_1 & tmask) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Auto_1 = 1; + } + } + if (triggers_2 & tmask) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Auto_2 = 1; + } + } + if (triggers_3 & tmask) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + mZDC.data[im][ic].f.Auto_3 = 1; + } + } + } +} + +//______________________________________________________________________________ +void Digits2Raw::insertLastBunch(int ibc, uint32_t orbit) +{ + + // Orbit and bunch crossing identifiers + UShort_t bc = 3563; + + // Reset scalers at orbit change + if (orbit != mLastOrbit) { + resetSums(orbit); + } + + updatePedestalReference(bc); + + // Dummy bunch -> Do not increment scalers but reset output structure + resetOutputStructure(bc, orbit, true); + + // Compute autotrigger bits and assign ALICE trigger bits + assignTriggerBits(ibc, bc, orbit, true); + + // Insert payload for all channels + for (Int_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + if (mModuleConfig->modules[im].readChannel[ic]) { + auto id = mModuleConfig->modules[im].channelID[ic]; + auto base_m = mSimCondition->channels[id].pedestal; // Average pedestal + auto base_s = mSimCondition->channels[id].pedestalFluct; // Baseline oscillations + auto base_n = mSimCondition->channels[id].pedestalNoise; // Electronic noise + Double_t base = gRandom->Gaus(base_m, base_s); + Int_t is = 0; + Double_t val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s00 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s01 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s02 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s03 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s04 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s05 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s06 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s07 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s08 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s09 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s10 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + is++; + val = base + gRandom->Gaus(0, base_n); + mZDC.data[im][ic].f.s11 = val < ADCMax ? (val > ADCMin ? val : ADCMin) : ADCMax; + } + } + } +} // insertLastBunch + +//______________________________________________________________________________ +void Digits2Raw::convertDigits(int ibc) +{ + + // Orbit and bunch crossing identifiers + UShort_t bc = mBCD.ir.bc; + UInt_t orbit = mBCD.ir.orbit; + + // Reset scalers at orbit change + if (orbit != mLastOrbit) { + resetSums(orbit); + } + + updatePedestalReference(bc); + + // Not a dummy bunch -> Reset output structure and eventually flag hits and increment scalers + resetOutputStructure(bc, orbit, false); + + // Compute autotrigger bits and assign ALICE trigger bits + assignTriggerBits(ibc, bc, orbit, false); + + if (mVerbosity > 0) { + mBCD.print(); + printf("Mask: %s\n", mPrintTriggerMask.data()); + } + + int chEnt = mBCD.ref.getFirstEntry(); + for (int ic = 0; ic < mBCD.ref.getEntries(); ic++) { + const auto& chd = mzdcChData[chEnt++]; + if (mVerbosity > 0) { + chd.print(); + } + UShort_t bc = mBCD.ir.bc; + UInt_t orbit = mBCD.ir.orbit; + // Look for channel ID in digits and store channel (just one copy in output) + // This is a limitation of software but we are not supposed to acquire the + // same signal twice anyway + for (Int_t im = 0; im < NModules; im++) { + for (UInt_t ic = 0; ic < NChPerModule; ic++) { + if (mModuleConfig->modules[im].channelID[ic] == chd.id && + mModuleConfig->modules[im].readChannel[ic]) { + Int_t is = 0; + mZDC.data[im][ic].f.s00 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s01 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s02 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s03 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s04 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s05 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s06 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s07 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s08 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s09 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s10 = chd.data[is]; + is++; + mZDC.data[im][ic].f.s11 = chd.data[is]; + break; + } + } + } + } +} + +//______________________________________________________________________________ +void Digits2Raw::writeDigits() +{ + constexpr static int data_size = sizeof(uint32_t) * NWPerGBTW; + // Local interaction record (true and empty bunches) + o2::InteractionRecord ir(mZDC.data[0][0].f.bc, mZDC.data[0][0].f.orbit); + for (UInt_t im = 0; im < o2::zdc::NModules; im++) { + // Check if module has been filled with data + // N.B. All channels are initialized if module is supposed to be readout + // Trigger bits are the same for all the channels connected to a module + bool TM = mZDC.data[im][0].f.Auto_m; + bool T0 = mZDC.data[im][0].f.Auto_0; + bool T1 = mZDC.data[im][0].f.Auto_1; + bool T2 = mZDC.data[im][0].f.Auto_2; + bool T3 = mZDC.data[im][0].f.Auto_3; + bool A0 = mZDC.data[im][0].f.Alice_0; + bool A1 = mZDC.data[im][0].f.Alice_1; + bool A2 = mZDC.data[im][0].f.Alice_2; + bool A3 = mZDC.data[im][0].f.Alice_3; + bool tcond_continuous = T0 || T1; + bool tcond_triggered = A0 || A1 || (A2 && (T0 || TM)) || (A3 && T0); + bool tcond_last = mZDC.data[im][0].f.bc == 3563; + // Condition to write GBT data + if (tcond_triggered || (mIsContinuous && tcond_continuous) || (mZDC.data[im][0].f.bc == 3563)) { + for (UInt_t ic = 0; ic < o2::zdc::NChPerModule; ic++) { + if (mModuleConfig->modules[im].readChannel[ic]) { + for (Int_t iw = 0; iw < o2::zdc::NWPerBc; iw++) { + gsl::span<char> payload{reinterpret_cast<char*>(&mZDC.data[im][ic].w[iw][0]), data_size}; + mWriter.addData(mFeeID, mCruID, mLinkID, mEndPointID, ir, payload); + } + } + } + } + if (mVerbosity > 1) { + if (tcond_continuous) { + printf("M%d Cont. T0=%d || T1=%d\n", im, T0, T1); + } + if (tcond_triggered) { + printf("M%d Trig. %s A0=%d || A1=%d || (A2=%d && (T0=%d || TM=%d))=%d || (A3=%d && T0=%d )=%d\n", im, mIsContinuous ? "CM" : "TM", A0, A1, A2, T0, TM, A2 && (T0 || TM), A3, T0, A3 && T0); + } + if (mZDC.data[im][0].f.bc == 3563) { + printf("M%d is last BC\n", im); + } + if (tcond_triggered || (mIsContinuous && tcond_continuous) || (mZDC.data[im][0].f.bc == 3563)) { + for (UInt_t ic = 0; ic < o2::zdc::NChPerModule; ic++) { + if (mModuleConfig->modules[im].readChannel[ic]) { + for (Int_t iw = 0; iw < o2::zdc::NWPerBc; iw++) { + print_gbt_word(&mZDC.data[im][ic].w[iw][0], mModuleConfig); + } + } + } + } else { + if (mVerbosity > 2) { + printf("orbit %9u bc %4u M%d SKIP\n", mZDC.data[im][0].f.orbit, mZDC.data[im][0].f.bc, im); + } + } + } + } +} + +//______________________________________________________________________________ +void Digits2Raw::print_gbt_word(const UInt_t* word, const ModuleConfig* moduleConfig) +{ + if (word == nullptr) { + printf("NULL\n"); + return; + } + unsigned __int128 val = word[2]; + val = val << 32; + val = val | word[1]; + val = val << 32; + val = val | word[0]; + static UInt_t last_orbit = 0, last_bc = 0; + + ULong64_t lsb = val; + ULong64_t msb = val >> 64; + UInt_t a = word[0]; + UInt_t b = word[1]; + UInt_t c = word[2]; + //UInt_t d=(msb>>32)&0xffffffff; + //printf("\n%llx %llx ",lsb,msb); + //printf("\n%8x %8x %8x %8x ",d,c,b,a); + if ((a & 0x3) == 0) { + UInt_t myorbit = (val >> 48) & 0xffffffff; + UInt_t mybc = (val >> 36) & 0xfff; + if (myorbit != last_orbit || mybc != last_bc) { + printf("Orbit %9u bc %4u\n", myorbit, mybc); + last_orbit = myorbit; + last_bc = mybc; + } + printf("%04x %08x %08x ", c, b, a); + UInt_t hits = (val >> 24) & 0xfff; + Int_t offset = (lsb >> 8) & 0xffff - 32768; + Float_t foffset = offset / 8.; + UInt_t board = (lsb >> 2) & 0xf; + UInt_t ch = (lsb >> 6) & 0x3; + //printf("orbit %9u bc %4u hits %4u offset %+6i Board %2u Ch %1u", myorbit, mybc, hits, offset, board, ch); + printf("orbit %9u bc %4u hits %4u offset %+8.3f Board %2u Ch %1u", myorbit, mybc, hits, foffset, board, ch); + if (board >= NModules) { + printf(" ERROR with board"); + } + if (ch >= NChPerModule) { + printf(" ERROR with ch"); + } + if (moduleConfig) { + auto id = moduleConfig->modules[board].channelID[ch]; + if (id >= 0 && id < NChannels) { + printf(" %s", ChannelNames[id].data()); + } else { + printf(" error with ch id"); + } + } + } else if ((a & 0x3) == 1) { + printf("%04x %08x %08x ", c, b, a); + printf(" %s %s %s %s ", a & 0x10 ? "A0" : " ", a & 0x20 ? "A1" : " ", a & 0x40 ? "A2" : " ", a & 0x80 ? "A3" : " "); + printf("0-5 "); + Short_t s[6]; + val = val >> 8; + for (Int_t i = 0; i < 6; i++) { + s[i] = val & 0xfff; + if (s[i] > ADCMax) { + s[i] = s[i] - ADCRange; + } + val = val >> 12; + } + printf(" %5d %5d %5d %5d %5d %5d", s[0], s[1], s[2], s[3], s[4], s[5]); + } else if ((a & 0x3) == 2) { + printf("%04x %08x %08x ", c, b, a); + printf("%s %s %s %s %s %s ", a & 0x4 ? "H" : " ", a & 0x8 ? "TM" : " ", a & 0x10 ? "T0" : " ", a & 0x20 ? "T1" : " ", a & 0x40 ? "T2" : " ", a & 0x80 ? "T3" : " "); + printf("6-b "); + Short_t s[6]; + val = val >> 8; + for (Int_t i = 0; i < 6; i++) { + s[i] = val & 0xfff; + if (s[i] > ADCMax) { + s[i] = s[i] - ADCRange; + } + val = val >> 12; + } + printf(" %5d %5d %5d %5d %5d %5d", s[0], s[1], s[2], s[3], s[4], s[5]); + } else if ((a & 0x3) == 3) { + printf("%04x %08x %08x ", c, b, a); + printf("HB "); + } + printf("\n"); +} + +//______________________________________________________________________________ +void Digits2Raw::emptyBunches(std::bitset<3564>& bunchPattern) +{ + const int LHCMaxBunches = o2::constants::lhc::LHCMaxBunches; + mNEmpty = 0; + for (Int_t ib = 0; ib < LHCMaxBunches; ib++) { + Int_t mb = (ib + 31) % LHCMaxBunches; // beam gas from back of calorimeter + Int_t m1 = (ib + 1) % LHCMaxBunches; // previous bunch + Int_t cb = ib; // current bunch crossing + Int_t p1 = (ib - 1) % LHCMaxBunches; // colliding + 1 + Int_t p2 = (ib + 1) % LHCMaxBunches; // colliding + 2 + Int_t p3 = (ib + 1) % LHCMaxBunches; // colliding + 3 + if (bunchPattern[mb] || bunchPattern[m1] || bunchPattern[cb] || bunchPattern[p1] || bunchPattern[p2] || bunchPattern[p3]) { + mEmpty[ib] = mNEmpty; + } else { + mNEmpty++; + mEmpty[ib] = mNEmpty; + } + } + LOG(INFO) << "There are " << mNEmpty << " clean empty bunches"; +} diff --git a/Detectors/ZDC/simulation/src/digi2raw.cxx b/Detectors/ZDC/simulation/src/digi2raw.cxx new file mode 100644 index 0000000000000..8d06d3993051c --- /dev/null +++ b/Detectors/ZDC/simulation/src/digi2raw.cxx @@ -0,0 +1,171 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 digi2raw.cxx +/// \author ruben.shahoyan@cern.ch + +#include <boost/program_options.hpp> +#include <TSystem.h> +#include <TFile.h> +#include <TStopwatch.h> +#include "Framework/Logger.h" +#include <string> +#include <iomanip> +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "DetectorsRaw/HBFUtils.h" +#include "ZDCBase/Constants.h" +#include "ZDCBase/ModuleConfig.h" +#include "DataFormatsZDC/BCData.h" +#include "DataFormatsZDC/ChannelData.h" +#include "ZDCSimulation/Digits2Raw.h" +#include "DataFormatsParameters/GRPObject.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "SimConfig/DigiParams.h" + +/// MC->raw conversion for ZDC + +namespace bpo = boost::program_options; + +void digi2raw(const std::string& inpName, const std::string& outDir, int verbosity, bool filePerLink, uint32_t rdhV = 4, + const std::string& ccdbHost = "", int superPageSizeInB = 1024 * 1024); + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + "Convert ZDC digits to CRU raw 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<int>()->default_value(0), "verbosity level"); + // add_option("input-file,i", bpo::value<std::string>()->default_value(o2::base::NameConf::getDigitsFileName(o2::detectors::DetID::ZDC)),"input ZDC digits file"); // why not used? + add_option("input-file,i", bpo::value<std::string>()->default_value("zdcdigits.root"), "input ZDC digits file"); + add_option("file-per-link,l", bpo::value<bool>()->default_value(false)->implicit_value(true), "create output file per CRU (default: write single file)"); + add_option("output-dir,o", bpo::value<std::string>()->default_value("./"), "output directory for raw data"); + add_option("ccdb-url,o", bpo::value<std::string>()->default_value(""), "url of the ccdb repository"); + uint32_t defRDH = o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>(); + add_option("rdh-version,r", bpo::value<uint32_t>()->default_value(defRDH), "RDH version to use"); + add_option("configKeyValues", bpo::value<std::string>()->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<std::string>()); + + std::string ccdb_url = vm["ccdb-url"].as<std::string>(); + auto& dopt = o2::conf::DigiParams::Instance(); + std::string ccdbHost = dopt.ccdb; + if (ccdb_url.length() > 0) { + ccdbHost = ccdb_url; + LOG(INFO) << "CCDB url set to " << ccdb_url; + } + LOG(INFO) << "CCDB url " << ccdbHost; + digi2raw(vm["input-file"].as<std::string>(), + vm["output-dir"].as<std::string>(), + vm["verbosity"].as<int>(), + vm["file-per-link"].as<bool>(), + vm["rdh-version"].as<uint32_t>(), + ccdbHost); + + return 0; +} + +void digi2raw(const std::string& inpName, const std::string& outDir, int verbosity, bool filePerLink, uint32_t rdhV, const std::string& ccdbHost, int superPageSizeInB) +{ + long timeStamp = 0; + //std::string ccdbHost = "http://ccdb-test.cern.ch:8080"; + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(ccdbHost); + if (timeStamp == mgr.getTimestamp()) { + return; + } + mgr.setTimestamp(timeStamp); + auto moduleConfig = mgr.get<o2::zdc::ModuleConfig>(o2::zdc::CCDBPathConfigModule); + if (!moduleConfig) { + LOG(FATAL) << "Cannot module configuratio for timestamp " << timeStamp; + return; + } + LOG(INFO) << "Loaded module configuration for timestamp " << timeStamp; + + auto simCondition = mgr.get<o2::zdc::SimCondition>(o2::zdc::CCDBPathConfigSim); + if (!simCondition) { + LOG(FATAL) << "Cannot get simulation configuration for timestamp " << timeStamp; + return; + } + LOG(INFO) << "Loaded simulation configuration for timestamp " << timeStamp; + simCondition->print(); + + const auto* ctx = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto& bcfill = ctx->getBunchFilling(); + auto bf = ctx->getBunchFilling(); + if (verbosity > 0) { + bf.print(); + } + auto bp = bf.getPattern(); + + TStopwatch swTot; + swTot.Start(); + + o2::zdc::Digits2Raw d2r; + d2r.setFilePerLink(filePerLink); + d2r.setVerbosity(verbosity); + auto& wr = d2r.getWriter(); + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::ZDC)); // must be set explicitly + wr.setSuperPageSize(superPageSizeInB); + wr.useRDHVersion(rdhV); + + std::string outDirName(outDir); + if (outDirName.back() != '/') { + outDirName += '/'; + } + // if needed, create output directory + if (gSystem->AccessPathName(outDirName.c_str())) { + if (gSystem->mkdir(outDirName.c_str(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outDirName; + } else { + LOG(INFO) << "created output directory " << outDirName; + } + } + + d2r.setModuleConfig(moduleConfig); + d2r.setSimCondition(simCondition); + d2r.emptyBunches(bp); + d2r.setVerbosity(verbosity); + d2r.processDigits(outDirName, inpName); + wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); + // + swTot.Stop(); + swTot.Print(); +} diff --git a/Detectors/gconfig/CMakeLists.txt b/Detectors/gconfig/CMakeLists.txt index 77281e03b978e..2ac6f9809633e 100644 --- a/Detectors/gconfig/CMakeLists.txt +++ b/Detectors/gconfig/CMakeLists.txt @@ -8,20 +8,30 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. +o2_add_library(G3Setup + SOURCES src/G3Config.cxx + PUBLIC_LINK_LIBRARIES MC::Geant3 FairRoot::Base O2::SimulationDataFormat O2::Generators +) + +o2_add_library(G4Setup + SOURCES src/G4Config.cxx + PUBLIC_LINK_LIBRARIES MC::Geant4VMC MC::Geant4 FairRoot::Base O2::SimulationDataFormat O2::Generators ROOT::EGPythia6 +) + +if (FlukaVMC_FOUND) + message(STATUS "BUILDING WITH FLUKA") + o2_add_library(FLUKASetup + SOURCES src/FlukaConfig.cxx + PUBLIC_LINK_LIBRARIES MC::FlukaVMC FairRoot::Base O2::SimulationDataFormat O2::Generators ROOT::EGPythia6 + ) +else() + message(STATUS "BUILDING WITHOUT FLUKA") +endif (FlukaVMC_FOUND) + o2_add_library(SimSetup - SOURCES src/G3Config.cxx src/G4Config.cxx - src/GlobalProcessCutSimParam.cxx src/SimSetup.cxx - PUBLIC_LINK_LIBRARIES MC::Geant3 - MC::Geant4VMC - MC::Geant4 - O2::SimulationDataFormat - O2::DetectorsPassive - O2::Generators - MC::Pythia6 # this is needed by Geant3 and - # EGPythia6 - ROOT::EGPythia6 # this is needed by Geant4 - # (TPythia6Decayer) - ) + SOURCES src/GlobalProcessCutSimParam.cxx src/SimSetup.cxx src/SetCuts.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils O2::DetectorsBase + ) o2_target_root_dictionary(SimSetup HEADERS include/SimSetup/SimSetup.h @@ -49,3 +59,5 @@ o2_add_test_root_macro(g3Config.C o2_add_test_root_macro(g4Config.C PUBLIC_LINK_LIBRARIES O2::SimSetup LABELS simsetup) + +o2_data_file(COPY data DESTINATION Detectors/gconfig/) diff --git a/Detectors/gconfig/SetCuts.h b/Detectors/gconfig/SetCuts.h deleted file mode 100644 index 089f7c8636d2c..0000000000000 --- a/Detectors/gconfig/SetCuts.h +++ /dev/null @@ -1,75 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence version 3 (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -/** Configuration macro for setting common cuts and processes for G3, G4 and Fluka (M. Al-Turany 27.03.2008) - specific cuts and processes to g3 or g4 should be set in the g3Config.C, g4Config.C or flConfig.C - -*/ - -void SetCuts() -{ - cout << "SetCuts Macro: Setting Processes.." << endl; - - // ------>>>> IMPORTANT!!!! - // For a correct comparison between GEANE and MC (pull distributions) - // or for a simulation without the generation of secondary particles: - // 1. set LOSS = 2, DRAY = 0, BREM = 1 - // 2. set the following cut values: CUTGAM, CUTELE, CUTNEU, CUTHAD, CUTMUO = 1 MeV or less - // BCUTE, BCUTM, DCUTE, DCUTM, PPCUTM = 10 TeV - // (For an explanation of the chosen values, please refer to the GEANT User's Guide - // or to message #5362 in the PandaRoot Forum >> Monte Carlo Engines >> g3Config.C thread) - // - // The default settings refer to a complete simulation which generates and follows also the secondary particles. - - // \note All following settings could also be set in Cave since it is always loaded. - // Use MaterialManager to set processes and cuts - auto& mgr = MaterialManager::Instance(); - auto& params = GlobalProcessCutSimParam::Instance(); - - LOG(INFO) << "Set default settings for processes and cuts."; - mgr.DefaultProcesses({{EProc::kPAIR, params.PAIR}, /** pair production */ - {EProc::kCOMP, params.COMP}, /** Compton scattering */ - {EProc::kPHOT, params.PHOT}, /** photo electric effect */ - {EProc::kPFIS, params.PFIS}, /** photofission */ - {EProc::kDRAY, params.DRAY}, /** delta ray */ - {EProc::kANNI, params.ANNI}, /** annihilation */ - {EProc::kBREM, params.BREM}, /** bremsstrahlung */ - {EProc::kHADR, params.HADR}, /** hadronic process */ - {EProc::kMUNU, params.MUNU}, /** muon nuclear interaction */ - {EProc::kDCAY, params.DCAY}, /** decay */ - {EProc::kLOSS, params.LOSS}, /** energy loss */ - {EProc::kMULS, params.MULS}, /** multiple scattering */ - {EProc::kCKOV, params.CKOV}}); /** Cherenkov */ - - mgr.DefaultCuts({{ECut::kCUTGAM, params.CUTGAM}, /** gammas */ - {ECut::kCUTELE, params.CUTELE}, /** electrons */ - {ECut::kCUTNEU, params.CUTNEU}, /** neutral hadrons */ - {ECut::kCUTHAD, params.CUTHAD}, /** charged hadrons */ - {ECut::kCUTMUO, params.CUTMUO}, /** muons */ - {ECut::kBCUTE, params.BCUTE}, /** electron bremsstrahlung */ - {ECut::kBCUTM, params.BCUTM}, /** muon and hadron bremsstrahlung */ - {ECut::kDCUTE, params.DCUTE}, /** delta-rays by electrons */ - {ECut::kDCUTM, params.DCUTM}, /** delta-rays by muons */ - {ECut::kPPCUTM, params.PPCUTM}, /** direct pair production by muons */ - {ECut::kTOFMAX, params.TOFMAX}}); /** time of flight */ - - const char* settingProc = mgr.specialProcessesEnabled() ? "enabled" : "disabled"; - const char* settingCut = mgr.specialCutsEnabled() ? "enabled" : "disabled"; - LOG(INFO) << "Special process settings are " << settingProc << "."; - LOG(INFO) << "Special cut settings are " << settingCut << "."; -} diff --git a/Detectors/gconfig/data/coreFlukaVmc.inp b/Detectors/gconfig/data/coreFlukaVmc.inp new file mode 100755 index 0000000000000..cb3d48c9ad09f --- /dev/null +++ b/Detectors/gconfig/data/coreFlukaVmc.inp @@ -0,0 +1,29 @@ +GLOBAL 50000. 4.0 +DEFAULTS NEW-DEFA +OPEN 1. OLD +random.dat +GEOBEGIN COMBINAT +GEOEND +* +*...+....1....+....2....+....3....+....4....+....5....+....6....+....7....+ +* +EVENTYPE 1.0 +BEAM 7000.0 MUON- +MGNFIELD 10.0 0.001 +SOURCE +*...+....1....+....2....+....3....+....4....+....5....+....6....+....7....+ +USERDUMP 200. 37.0 -2.0 1.0 TRAKFILE +START 42000.99999999.0 2.0 +STOP + + + + + + + + + + + + diff --git a/Detectors/gconfig/g3libs.C b/Detectors/gconfig/g3libs.C index ce95807d40d08..dc9912ef54f9c 100644 --- a/Detectors/gconfig/g3libs.C +++ b/Detectors/gconfig/g3libs.C @@ -24,7 +24,7 @@ void g3libs() std::cout << "Loading Geant3 libraries ..." << std::endl; if (isLibrary("libdummies")) - gSystem->Load("libdummies.so"); + gSystem->Load("libdummies"); // libdummies.so needed from geant3_+vmc version 0.5 gSystem->Load("libgeant321"); diff --git a/Detectors/gconfig/g4config.in b/Detectors/gconfig/g4config.in index b5f576a2dc6f1..e36d1056884dd 100644 --- a/Detectors/gconfig/g4config.in +++ b/Detectors/gconfig/g4config.in @@ -6,6 +6,7 @@ /mcVerbose/opGeometryManager 1 /mcTracking/loopVerbose 1 /mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control #/tracking/verbose 1 #//control/cout/ignoreThreadsExcept 0 @@ -14,6 +15,7 @@ /mcDet/setIsMaxStepInLowDensityMaterials true /mcDet/setMaxStepInLowDensityMaterials 10 m /mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true # optical @@ -46,6 +48,11 @@ /mcMagField/setMaximumEpsilonStep 1.0e-05 /mcMagField/printParameters +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + # Define media with the INCLXX physics list; here basically in all ITS media #/mcVerbose/biasingConfigurationManager 3 /mcPhysics/biasing/setModel inclxx diff --git a/Detectors/gconfig/src/FlukaConfig.cxx b/Detectors/gconfig/src/FlukaConfig.cxx new file mode 100644 index 0000000000000..bdc3fa137f34c --- /dev/null +++ b/Detectors/gconfig/src/FlukaConfig.cxx @@ -0,0 +1,70 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FairRunSim.h" +#include "TFluka.h" +#include "SimulationDataFormat/Stack.h" +#include "SimulationDataFormat/StackParam.h" +#include <iostream> +#include "FairLogger.h" +#include "FairModule.h" +#include "Generators/DecayerPythia8.h" +#include "../commonConfig.C" + +// these are used in commonConfig.C +using o2::eventgen::DecayerPythia8; + +namespace o2 +{ +namespace flukaconfig +{ + +void linkFlukaFiles() +{ + // Link here some special Fluka files needed + gSystem->Exec("ln -s $FLUKADATA/neuxsc.bin ."); + gSystem->Exec("ln -s $FLUKADATA/elasct.bin ."); + gSystem->Exec("ln -s $FLUKADATA/gxsect.bin ."); + gSystem->Exec("ln -s $FLUKADATA/nuclear.bin ."); + gSystem->Exec("ln -s $FLUKADATA/sigmapi.bin ."); + gSystem->Exec("ln -s $FLUKADATA/brems_fin.bin ."); + gSystem->Exec("ln -s $FLUKADATA/cohff.bin ."); + gSystem->Exec("ln -s $FLUKADATA/fluodt.dat ."); + gSystem->Exec("ln -s $FLUKADATA/random.dat ."); + // Copy the random seed + gSystem->Exec("cp $FLUKADATA/random.dat old.seed"); + // Give some meaningfull name to the output + gSystem->Exec("ln -s fluka.out fort.11"); + gSystem->Exec("ln -s fluka.err fort.15"); + gSystem->Exec("ln -fs $ALICE_ROOT/TFluka/macro/FlukaConfig.C Config.C"); + gSystem->Exec("ln -fs $O2_ROOT/share/Detectors/gconfig/data/coreFlukaVmc.inp ."); +} + +void Config() +{ + linkFlukaFiles(); + FairRunSim* run = FairRunSim::Instance(); + TString* gModel = run->GetGeoModel(); + TFluka* fluka = new TFluka("C++ Interface to Fluka", 0); + stackSetup(fluka, run); + + // setup decayer + decayerSetup(fluka); + + // ******* FLUKA specific configuration for simulated Runs ******* +} + +void FlukaConfig() +{ + LOG(INFO) << "Setting up FLUKA sim from library code"; + Config(); +} +} // namespace flukaconfig +} // namespace o2 diff --git a/Detectors/gconfig/src/G3Config.cxx b/Detectors/gconfig/src/G3Config.cxx index 96192c0ca1705..72ee0ef35ab04 100644 --- a/Detectors/gconfig/src/G3Config.cxx +++ b/Detectors/gconfig/src/G3Config.cxx @@ -13,39 +13,23 @@ #include "TGeant3TGeo.h" #include "SimulationDataFormat/Stack.h" #include "SimulationDataFormat/StackParam.h" -#include <iostream> #include "FairLogger.h" #include "FairModule.h" -#include <DetectorsPassive/Cave.h> -#include "DetectorsBase/MaterialManager.h" -#include "SimSetup/GlobalProcessCutSimParam.h" #include "Generators/DecayerPythia8.h" -//using declarations here since SetCuts.C and g3Config.C are included within namespace -// these are needed for SetCuts.C inclusion -using o2::GlobalProcessCutSimParam; -using o2::base::ECut; -using o2::base::EProc; -using o2::base::MaterialManager; -// these are used in g3Config.C -using std::cout; -using std::endl; // these are used in commonConfig.C using o2::eventgen::DecayerPythia8; -#include <SimSetup/SimSetup.h> namespace o2 { namespace g3config { #include "../g3Config.C" -#include "../SetCuts.h" void G3Config() { LOG(INFO) << "Setting up G3 sim from library code"; Config(); - SetCuts(); } } // namespace g3config } // namespace o2 diff --git a/Detectors/gconfig/src/G4Config.cxx b/Detectors/gconfig/src/G4Config.cxx index e6d043d10ef4b..3aac0259f561c 100644 --- a/Detectors/gconfig/src/G4Config.cxx +++ b/Detectors/gconfig/src/G4Config.cxx @@ -17,18 +17,10 @@ #include "TG4RunConfiguration.h" #include "TPythia6Decayer.h" #include "FairModule.h" -#include <DetectorsPassive/Cave.h> -#include "DetectorsBase/MaterialManager.h" -#include "SimSetup/GlobalProcessCutSimParam.h" #include "SimConfig/G4Params.h" #include "Generators/DecayerPythia8.h" //using declarations here since SetCuts.C and g4Config.C are included within namespace -// these are needed for SetCuts.C inclusion -using o2::GlobalProcessCutSimParam; -using o2::base::ECut; -using o2::base::EProc; -using o2::base::MaterialManager; // these are used in g4Config.C using std::cout; using std::endl; @@ -40,13 +32,11 @@ namespace o2 namespace g4config { #include "../g4Config.C" -#include "../SetCuts.h" void G4Config() { LOG(INFO) << "Setting up G4 sim from library code"; Config(); - SetCuts(); } } // namespace g4config } // namespace o2 diff --git a/Detectors/gconfig/src/SetCuts.cxx b/Detectors/gconfig/src/SetCuts.cxx new file mode 100644 index 0000000000000..edea5655b568f --- /dev/null +++ b/Detectors/gconfig/src/SetCuts.cxx @@ -0,0 +1,74 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "SetCuts.h" +#include "SimSetup/GlobalProcessCutSimParam.h" +#include "DetectorsBase/MaterialManager.h" +#include "FairLogger.h" + +using namespace o2::base; + +namespace o2 +{ + +void SetCuts() +{ + LOG(INFO) << "Setup global cuts and processes"; + + // ------>>>> IMPORTANT!!!! + // For a correct comparison between GEANE and MC (pull distributions) + // or for a simulation without the generation of secondary particles: + // 1. set LOSS = 2, DRAY = 0, BREM = 1 + // 2. set the following cut values: CUTGAM, CUTELE, CUTNEU, CUTHAD, CUTMUO = 1 MeV or less + // BCUTE, BCUTM, DCUTE, DCUTM, PPCUTM = 10 TeV + // (For an explanation of the chosen values, please refer to the GEANT User's Guide + // or to message #5362 in the PandaRoot Forum >> Monte Carlo Engines >> g3Config.C thread) + // + // The default settings refer to a complete simulation which generates and follows also the secondary particles. + + // \note All following settings could also be set in Cave since it is always loaded. + // Use MaterialManager to set processes and cuts + auto& mgr = o2::base::MaterialManager::Instance(); + auto& params = o2::GlobalProcessCutSimParam::Instance(); + + LOG(INFO) << "Set default settings for processes and cuts."; + mgr.DefaultProcesses({{EProc::kPAIR, params.PAIR}, /** pair production */ + {EProc::kCOMP, params.COMP}, /** Compton scattering */ + {EProc::kPHOT, params.PHOT}, /** photo electric effect */ + {EProc::kPFIS, params.PFIS}, /** photofission */ + {EProc::kDRAY, params.DRAY}, /** delta ray */ + {EProc::kANNI, params.ANNI}, /** annihilation */ + {EProc::kBREM, params.BREM}, /** bremsstrahlung */ + {EProc::kHADR, params.HADR}, /** hadronic process */ + {EProc::kMUNU, params.MUNU}, /** muon nuclear interaction */ + {EProc::kDCAY, params.DCAY}, /** decay */ + {EProc::kLOSS, params.LOSS}, /** energy loss */ + {EProc::kMULS, params.MULS}, /** multiple scattering */ + {EProc::kCKOV, params.CKOV}}); /** Cherenkov */ + + mgr.DefaultCuts({{ECut::kCUTGAM, params.CUTGAM}, /** gammas */ + {ECut::kCUTELE, params.CUTELE}, /** electrons */ + {ECut::kCUTNEU, params.CUTNEU}, /** neutral hadrons */ + {ECut::kCUTHAD, params.CUTHAD}, /** charged hadrons */ + {ECut::kCUTMUO, params.CUTMUO}, /** muons */ + {ECut::kBCUTE, params.BCUTE}, /** electron bremsstrahlung */ + {ECut::kBCUTM, params.BCUTM}, /** muon and hadron bremsstrahlung */ + {ECut::kDCUTE, params.DCUTE}, /** delta-rays by electrons */ + {ECut::kDCUTM, params.DCUTM}, /** delta-rays by muons */ + {ECut::kPPCUTM, params.PPCUTM}, /** direct pair production by muons */ + {ECut::kTOFMAX, params.TOFMAX}}); /** time of flight */ + + const char* settingProc = mgr.specialProcessesEnabled() ? "enabled" : "disabled"; + const char* settingCut = mgr.specialCutsEnabled() ? "enabled" : "disabled"; + LOG(INFO) << "Special process settings are " << settingProc << "."; + LOG(INFO) << "Special cut settings are " << settingCut << "."; +} + +} // namespace o2 diff --git a/Detectors/gconfig/src/SetCuts.h b/Detectors/gconfig/src/SetCuts.h new file mode 100644 index 0000000000000..c039acde8a60c --- /dev/null +++ b/Detectors/gconfig/src/SetCuts.h @@ -0,0 +1,19 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SIMSETUP_SETCUTS_H +#define O2_SIMSETUP_SETCUTS_H + +namespace o2 +{ +void SetCuts(); +} + +#endif diff --git a/Detectors/gconfig/src/SimSetup.cxx b/Detectors/gconfig/src/SimSetup.cxx index 8d8af5886a560..8626abc22a007 100644 --- a/Detectors/gconfig/src/SimSetup.cxx +++ b/Detectors/gconfig/src/SimSetup.cxx @@ -11,27 +11,53 @@ #include <cstring> #include "SimSetup/SimSetup.h" #include "FairLogger.h" +#include "SetCuts.h" +#include <dlfcn.h> +#ifdef NDEBUG +#undef NDEBUG +#endif +#include <cassert> +#include <sstream> namespace o2 { -// forward declarations of functions -namespace g3config -{ -void G3Config(); -} -namespace g4config + +typedef void (*setup_fnc)(); + +void setupFromPlugin(const char* libname, const char* setupfuncname) { -void G4Config(); + LOG(INFO) << "Loading simulation plugin " << libname; + auto libHandle = dlopen(libname, RTLD_NOW); + // try to make the library loading a bit more portable: + if (!libHandle) { + // try appending *.so + std::stringstream stream; + stream << libname << ".so"; + libHandle = dlopen(stream.str().c_str(), RTLD_NOW); + } + if (!libHandle) { + // try appending *.dylib + std::stringstream stream; + stream << libname << ".dylib"; + libHandle = dlopen(stream.str().c_str(), RTLD_NOW); + } + assert(libHandle); + auto setup = (setup_fnc)dlsym(libHandle, setupfuncname); + assert(setup); + setup(); } void SimSetup::setup(const char* engine) { if (strcmp(engine, "TGeant3") == 0) { - g3config::G3Config(); + setupFromPlugin("libO2G3Setup", "_ZN2o28g3config8G3ConfigEv"); } else if (strcmp(engine, "TGeant4") == 0) { - g4config::G4Config(); + setupFromPlugin("libO2G4Setup", "_ZN2o28g4config8G4ConfigEv"); + } else if (strcmp(engine, "TFluka") == 0) { + setupFromPlugin("libO2FLUKASetup", "_ZN2o211flukaconfig11FlukaConfigEv"); } else { LOG(FATAL) << "Unsupported engine " << engine; } + o2::SetCuts(); } } // namespace o2 diff --git a/EventVisualisation/Base/include/EventVisualisationBase/DataInterpreter.h b/EventVisualisation/Base/include/EventVisualisationBase/DataInterpreter.h index 6f835909586ef..3568492c0fd98 100644 --- a/EventVisualisation/Base/include/EventVisualisationBase/DataInterpreter.h +++ b/EventVisualisation/Base/include/EventVisualisationBase/DataInterpreter.h @@ -45,11 +45,12 @@ class DataInterpreter virtual ~DataInterpreter() = default; static void removeInstances() { - for (int i = 0; i < EVisualisationGroup::NvisualisationGroups; i++) + for (int i = 0; i < EVisualisationGroup::NvisualisationGroups; i++) { if (instance[i] != nullptr) { delete instance[i]; instance[i] = nullptr; } + } } // Should return visualisation objects for required data type diff --git a/EventVisualisation/Base/src/DataSourceOffline.cxx b/EventVisualisation/Base/src/DataSourceOffline.cxx index 73b76214f96f8..9f5021c9d6759 100644 --- a/EventVisualisation/Base/src/DataSourceOffline.cxx +++ b/EventVisualisation/Base/src/DataSourceOffline.cxx @@ -37,16 +37,18 @@ DataReader* DataSourceOffline::instance[EVisualisationGroup::NvisualisationGroup TObject* DataSourceOffline::getEventData(int no, EVisualisationGroup purpose) { - if (instance[purpose] == nullptr) + if (instance[purpose] == nullptr) { return nullptr; + } return instance[purpose]->getEventData(no); } int DataSourceOffline::GetEventCount() { for (int i = 0; i < EVisualisationGroup::NvisualisationGroups; i++) { - if (instance[i] != nullptr) + if (instance[i] != nullptr) { return instance[i]->GetEventCount(); + } } return 1; }; diff --git a/EventVisualisation/Base/src/GeometryManager.cxx b/EventVisualisation/Base/src/GeometryManager.cxx index 0d61520e7eaee..92c595478c6d6 100644 --- a/EventVisualisation/Base/src/GeometryManager.cxx +++ b/EventVisualisation/Base/src/GeometryManager.cxx @@ -82,8 +82,9 @@ void GeometryManager::drawDeep(TEveGeoShape* geomShape, Color_t color, Char_t tr if (strcmp(geomShape->GetElementName(), "TPC_Drift_1") == 0) { // hack for TPC drift chamber geomShape->SetRnrSelf(kTRUE); - if (color >= 0) + if (color >= 0) { geomShape->SetMainColor(color); + } if (lineColor >= 0) { geomShape->SetLineColor(lineColor); geomShape->SetLineWidth(0.1); @@ -101,8 +102,9 @@ void GeometryManager::drawDeep(TEveGeoShape* geomShape, Color_t color, Char_t tr } } else { geomShape->SetRnrSelf(true); - if (color >= 0) + if (color >= 0) { geomShape->SetMainColor(color); + } if (lineColor >= 0) { geomShape->SetLineColor(lineColor); geomShape->SetLineWidth(0.1); diff --git a/EventVisualisation/DataConverter/src/VisualisationCluster.cxx b/EventVisualisation/DataConverter/src/VisualisationCluster.cxx index d51bb14bf8205..0b287a14a2573 100644 --- a/EventVisualisation/DataConverter/src/VisualisationCluster.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationCluster.cxx @@ -30,8 +30,9 @@ VisualisationCluster::VisualisationCluster(double XYZ[]) void VisualisationCluster::setCoordinates(double xyz[3]) { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { mCoordinates[i] = xyz[i]; + } } } // namespace event_visualisation diff --git a/EventVisualisation/DataConverter/src/VisualisationTrack.cxx b/EventVisualisation/DataConverter/src/VisualisationTrack.cxx index 71891a5376c72..8a64472ed8ef0 100644 --- a/EventVisualisation/DataConverter/src/VisualisationTrack.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationTrack.cxx @@ -65,20 +65,23 @@ void VisualisationTrack::addChild(int childID) void VisualisationTrack::addMomentum(double pxpypz[3]) { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { mMomentum[i] = pxpypz[i]; + } } void VisualisationTrack::addStartCoordinates(double xyz[3]) { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { mStartCoordinates[i] = xyz[i]; + } } void VisualisationTrack::addEndCoordinates(double xyz[3]) { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { mEndCoordinates[i] = xyz[i]; + } } void VisualisationTrack::addPolyPoint(double x, double y, double z) diff --git a/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterITS.h b/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterITS.h index bd06d6f97e497..20755fd52a93e 100644 --- a/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterITS.h +++ b/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterITS.h @@ -28,7 +28,7 @@ namespace o2 namespace event_visualisation { -class DataInterpreterITS : public DataInterpreter +class DataInterpreterITS final : public DataInterpreter { public: // Default constructor diff --git a/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterTPC.h b/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterTPC.h index f159653fbc9d9..c0572e468c861 100644 --- a/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterTPC.h +++ b/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterTPC.h @@ -29,7 +29,7 @@ namespace o2 namespace event_visualisation { -class DataInterpreterTPC : public DataInterpreter +class DataInterpreterTPC final : public DataInterpreter { public: // Default constructor diff --git a/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterVSD.h b/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterVSD.h index 701984979934f..6f7a18614a5c5 100644 --- a/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterVSD.h +++ b/EventVisualisation/Detectors/include/EventVisualisationDetectors/DataInterpreterVSD.h @@ -33,7 +33,7 @@ namespace o2 namespace event_visualisation { -class DataInterpreterVSD : public DataInterpreter +class DataInterpreterVSD final : public DataInterpreter { private: void LoadClusters(TEvePointSet*& ps, const TString& det_name, Int_t det_id); diff --git a/EventVisualisation/Detectors/src/DataInterpreterITS.cxx b/EventVisualisation/Detectors/src/DataInterpreterITS.cxx index 68d927d68653d..ef2269ac5666f 100644 --- a/EventVisualisation/Detectors/src/DataInterpreterITS.cxx +++ b/EventVisualisation/Detectors/src/DataInterpreterITS.cxx @@ -44,7 +44,7 @@ DataInterpreterITS::DataInterpreterITS() //Prepare coordinate translator base::GeometryManager::loadGeometry(); its::GeometryTGeo* gman = its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2GRot)); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)); } std::unique_ptr<VisualisationEvent> DataInterpreterITS::interpretDataForType(TObject* data, EVisualisationDataType type) diff --git a/EventVisualisation/Detectors/src/DataInterpreterTPC.cxx b/EventVisualisation/Detectors/src/DataInterpreterTPC.cxx index 8b90c2d4ac835..9e78c98c2e40b 100644 --- a/EventVisualisation/Detectors/src/DataInterpreterTPC.cxx +++ b/EventVisualisation/Detectors/src/DataInterpreterTPC.cxx @@ -60,15 +60,15 @@ std::unique_ptr<VisualisationEvent> DataInterpreterTPC::interpretDataForType(TOb auto access = std::make_unique<o2::tpc::ClusterNativeAccess>(); std::unique_ptr<tpc::ClusterNative[]> clusterBuffer; - tpc::MCLabelContainer clusterMCBuffer; + tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clusterMCBuffer; reader->fillIndex(*access, clusterBuffer, clusterMCBuffer); const auto& mapper = tpc::Mapper::instance(); const auto& clusterRefs = access->clusters; - for (int sector = 0; sector < o2::tpc::Constants::MAXSECTOR; sector++) { - for (int row = 0; row < o2::tpc::Constants::MAXGLOBALPADROW; row++) { + for (int sector = 0; sector < o2::tpc::constants::MAXSECTOR; sector++) { + for (int row = 0; row < o2::tpc::constants::MAXGLOBALPADROW; row++) { const auto& c = clusterRefs[sector][row]; const auto pad = mapper.globalPadNumber(tpc::PadPos(row, c->getPad())); diff --git a/EventVisualisation/Detectors/src/DataInterpreterVSD.cxx b/EventVisualisation/Detectors/src/DataInterpreterVSD.cxx index a826eeeecba00..be63509e60f94 100644 --- a/EventVisualisation/Detectors/src/DataInterpreterVSD.cxx +++ b/EventVisualisation/Detectors/src/DataInterpreterVSD.cxx @@ -43,8 +43,9 @@ DataInterpreterVSD::~DataInterpreterVSD() std::unique_ptr<VisualisationEvent> DataInterpreterVSD::interpretDataForType(TObject* data, EVisualisationDataType type) { - if (mVSD == nullptr) + if (mVSD == nullptr) { mVSD = new TEveVSD; + } this->DropEvent(); // Connect to new event-data. diff --git a/EventVisualisation/Detectors/src/DataReaderVSD.cxx b/EventVisualisation/Detectors/src/DataReaderVSD.cxx index 74e2f507991ea..77b902caa1591 100644 --- a/EventVisualisation/Detectors/src/DataReaderVSD.cxx +++ b/EventVisualisation/Detectors/src/DataReaderVSD.cxx @@ -20,6 +20,7 @@ #include <TPRegexp.h> #include <TEveTrackPropagator.h> #include <TEveEventManager.h> +#include <TKey.h> namespace o2 { @@ -37,8 +38,9 @@ DataReaderVSD::DataReaderVSD() DataReaderVSD::~DataReaderVSD() { if (mEvDirKeys.size() > 0) { - for (auto obj : mEvDirKeys) + for (auto obj : mEvDirKeys) { delete obj; + } mEvDirKeys.clear(); } diff --git a/EventVisualisation/View/include/EventVisualisationView/EventManager.h b/EventVisualisation/View/include/EventVisualisationView/EventManager.h index 313576de03d37..b76b8d178e260 100644 --- a/EventVisualisation/View/include/EventVisualisationView/EventManager.h +++ b/EventVisualisation/View/include/EventVisualisationView/EventManager.h @@ -43,7 +43,7 @@ namespace event_visualisation class DataSource; -class EventManager : public TEveEventManager, public TQObject +class EventManager final : public TEveEventManager, public TQObject { public: enum EDataSource { diff --git a/EventVisualisation/View/src/EventManager.cxx b/EventVisualisation/View/src/EventManager.cxx index bceb60d6a4bb0..15fcdd8cd8b41 100644 --- a/EventVisualisation/View/src/EventManager.cxx +++ b/EventVisualisation/View/src/EventManager.cxx @@ -43,20 +43,24 @@ EventManager* EventManager::instance = nullptr; EventManager& EventManager::getInstance() { - if (instance == nullptr) + if (instance == nullptr) { instance = new EventManager(); + } return *instance; } EventManager::EventManager() : TEveEventManager("Event", "") { LOG(INFO) << "Initializing TEveManager"; - for (unsigned int i = 0; i < elemof(dataInterpreters); i++) + for (unsigned int i = 0; i < elemof(dataInterpreters); i++) { dataInterpreters[i] = nullptr; - for (unsigned int i = 0; i < elemof(dataReaders); i++) + } + for (unsigned int i = 0; i < elemof(dataReaders); i++) { dataReaders[i] = nullptr; - for (unsigned int i = 0; i < elemof(dataTypeLists); i++) + } + for (unsigned int i = 0; i < elemof(dataTypeLists); i++) { dataTypeLists[i] = nullptr; + } } void EventManager::Open() diff --git a/EventVisualisation/View/src/Initializer.cxx b/EventVisualisation/View/src/Initializer.cxx index 0c4a93e759b3c..5df21b9f0fc15 100644 --- a/EventVisualisation/View/src/Initializer.cxx +++ b/EventVisualisation/View/src/Initializer.cxx @@ -108,12 +108,16 @@ void Initializer::setupGeometry() auto multiView = MultiView::getInstance(); //auto geometry_enabled = GeometryManager::getInstance().getR2Geometry()? R2Visualisation:R3Visualisation; for (int iDet = 0; iDet < NvisualisationGroups; ++iDet) { - if (GeometryManager::getInstance().getR2Geometry()) - if (!R2Visualisation[iDet]) + if (GeometryManager::getInstance().getR2Geometry()) { + if (!R2Visualisation[iDet]) { continue; - if (!GeometryManager::getInstance().getR2Geometry()) - if (!R3Visualisation[iDet]) + } + } + if (!GeometryManager::getInstance().getR2Geometry()) { + if (!R3Visualisation[iDet]) { continue; + } + } EVisualisationGroup det = static_cast<EVisualisationGroup>(iDet); string detName = gVisualisationGroupName[det]; diff --git a/EventVisualisation/View/src/main.cxx b/EventVisualisation/View/src/main.cxx index 680a338d032a4..f9d2c57aaf7d0 100644 --- a/EventVisualisation/View/src/main.cxx +++ b/EventVisualisation/View/src/main.cxx @@ -85,8 +85,9 @@ int main(int argc, char** argv) { LOG(INFO) << "Welcome in O2 event visualisation tool"; Options* options = processCommandLine(argc, argv); - if (options == nullptr) + if (options == nullptr) { exit(-1); + } srand(static_cast<unsigned int>(time(nullptr))); @@ -96,8 +97,9 @@ int main(int argc, char** argv) std::array<const char*, 7> keys = {"Gui.DefaultFont", "Gui.MenuFont", "Gui.MenuHiFont", "Gui.DocFixedFont", "Gui.DocPropFont", "Gui.IconFont", "Gui.StatusFont"}; for (const auto& key : keys) { - if (settings.Defined(key)) + if (settings.Defined(key)) { gEnv->SetValue(key, settings.GetValue(key, "")); + } } // create ROOT application environment diff --git a/Examples/Ex2/README.md b/Examples/Ex2/README.md index 3c96cc3e7193e..3f1b9d734ff97 100644 --- a/Examples/Ex2/README.md +++ b/Examples/Ex2/README.md @@ -4,4 +4,4 @@ ## Ex2 A basic library with a Root dictionary -See [CMakeInstructions](../doc/CMakeInstructions.md) for an explanation about this directory. +See [CMakeInstructions](/doc/CMakeInstructions.md) for an explanation about this directory. diff --git a/Examples/Ex3/README.md b/Examples/Ex3/README.md index 879ff48227de8..e0a1a35c1051c 100644 --- a/Examples/Ex3/README.md +++ b/Examples/Ex3/README.md @@ -4,4 +4,4 @@ ## Ex3 Adding an executable -See [CMakeInstructions](../doc/CMakeInstructions.md) for an explanation about this directory. +See [CMakeInstructions](/doc/CMakeInstructions.md) for an explanation about this directory. diff --git a/Examples/Ex4/README.md b/Examples/Ex4/README.md index 9e526d06a4636..6743ca4fc2705 100644 --- a/Examples/Ex4/README.md +++ b/Examples/Ex4/README.md @@ -4,4 +4,4 @@ ## Ex4 Adding tests -See [CMakeInstructions](../doc/CMakeInstructions.md) for an explanation about this directory. +See [CMakeInstructions](/doc/CMakeInstructions.md) for an explanation about this directory. diff --git a/Examples/Ex5/README.md b/Examples/Ex5/README.md index ebc693bb0c68a..6e309644afd4f 100644 --- a/Examples/Ex5/README.md +++ b/Examples/Ex5/README.md @@ -4,4 +4,4 @@ ## Ex5 Adding a man page -See [CMakeInstructions](../doc/CMakeInstructions.md) for an explanation about this directory. +See [CMakeInstructions](/doc/CMakeInstructions.md) for an explanation about this directory. diff --git a/Framework/AnalysisSupport/CMakeLists.txt b/Framework/AnalysisSupport/CMakeLists.txt new file mode 100644 index 0000000000000..f4eab0b534731 --- /dev/null +++ b/Framework/AnalysisSupport/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +# Given GCC 7.3 does not provide std::filesystem we use Boost instead +# Drop this once we move to GCC 8.2+ + +if(TARGET JAliEn::JAliEn) + set(EXTRA_TARGETS XRootD::Client JAliEn::JAliEn) +endif() + +o2_add_library(FrameworkAnalysisSupport + SOURCES src/Plugin.cxx + src/AODJAlienReaderHelpers.cxx + PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src + PUBLIC_LINK_LIBRARIES O2::Framework ${EXTRA_TARGETS}) diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx new file mode 100644 index 0000000000000..327358e2424ec --- /dev/null +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx @@ -0,0 +1,335 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "AODJAlienReaderHelpers.h" +#include "Framework/TableTreeHelpers.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/RootTableBuilderHelpers.h" +#include "Framework/AlgorithmSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/CallbackService.h" +#include "Framework/EndOfStreamContext.h" +#include "Framework/DeviceSpec.h" +#include "Framework/RawDeviceService.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/DataInputDirector.h" +#include "Framework/SourceInfoHeader.h" +#include "Framework/ChannelInfo.h" +#include "Framework/Logger.h" + +#include <Monitoring/Monitoring.h> + +#include <ROOT/RDataFrame.hxx> +#if __has_include(<TJAlienFile.h>) +#include <TJAlienFile.h> +#endif +#include <TGrid.h> +#include <TFile.h> +#include <TTreeCache.h> +#include <TTreePerfStats.h> + +#include <arrow/ipc/reader.h> +#include <arrow/ipc/writer.h> +#include <arrow/io/interfaces.h> +#include <arrow/table.h> +#include <arrow/util/key_value_metadata.h> + +#include <thread> + +using namespace o2; +using namespace o2::aod; + +struct RuntimeWatchdog { + int numberTimeFrames; + uint64_t startTime; + uint64_t lastTime; + double runTime; + uint64_t runTimeLimit; + + RuntimeWatchdog(Long64_t limit) + { + numberTimeFrames = -1; + startTime = uv_hrtime(); + lastTime = startTime; + runTime = 0.; + runTimeLimit = limit; + } + + bool update() + { + numberTimeFrames++; + if (runTimeLimit <= 0) { + return true; + } + + auto nowTime = uv_hrtime(); + + // time spent to process the time frame + double time_spent = numberTimeFrames < 1 ? (double)(nowTime - lastTime) / 1.E9 : 0.; + runTime += time_spent; + lastTime = nowTime; + + return ((double)(lastTime - startTime) / 1.E9 + runTime / (numberTimeFrames + 1)) < runTimeLimit; + } + + void printOut() + { + LOGP(INFO, "RuntimeWatchdog"); + LOGP(INFO, " run time limit: {}", runTimeLimit); + LOGP(INFO, " number of time frames: {}", numberTimeFrames); + LOGP(INFO, " estimated run time per time frame: {}", (numberTimeFrames >= 0) ? runTime / (numberTimeFrames + 1) : 0.); + LOGP(INFO, " estimated total run time: {}", (double)(lastTime - startTime) / 1.E9 + ((numberTimeFrames >= 0) ? runTime / (numberTimeFrames + 1) : 0.)); + } +}; + +template <typename... C> +static constexpr auto columnNamesTrait(framework::pack<C...>) +{ + return std::vector<std::string>{C::columnLabel()...}; +} + +std::vector<std::string> getColumnNames(header::DataHeader dh) +{ + auto description = std::string(dh.dataDescription.str); + auto origin = std::string(dh.dataOrigin.str); + + // get column names + // AOD / RN2 + if (origin == "AOD") { + if (description == "TRACK:PAR") { + return columnNamesTrait(typename StoredTracksMetadata::table_t::persistent_columns_t{}); + } else if (description == "TRACK:PARCOV") { + return columnNamesTrait(typename StoredTracksCovMetadata::table_t::persistent_columns_t{}); + } else if (description == "TRACK:EXTRA") { + return columnNamesTrait(typename TracksExtraMetadata::table_t::persistent_columns_t{}); + } + } + + // default: column names = {} + return std::vector<std::string>({}); +} + +using o2::monitoring::Metric; +using o2::monitoring::Monitoring; +using o2::monitoring::tags::Key; +using o2::monitoring::tags::Value; + +namespace o2::framework::readers +{ +auto setEOSCallback(InitContext& ic) +{ + ic.services().get<CallbackService>().set(CallbackService::Id::EndOfStream, + [](EndOfStreamContext& eosc) { + auto& control = eosc.services().get<ControlService>(); + control.endOfStream(); + control.readyToQuit(QuitRequest::Me); + }); +} + +template <typename O> +static inline auto extractTypedOriginal(ProcessingContext& pc) +{ + ///FIXME: this should be done in invokeProcess() as some of the originals may be compound tables + return O{pc.inputs().get<TableConsumer>(aod::MetadataTrait<O>::metadata::tableLabel())->asArrowTable()}; +} + +template <typename... Os> +static inline auto extractOriginalsTuple(framework::pack<Os...>, ProcessingContext& pc) +{ + return std::make_tuple(extractTypedOriginal<Os>(pc)...); +} + +AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback() +{ + auto callback = AlgorithmSpec{adaptStateful([](ConfigParamRegistry const& options, + DeviceSpec const& spec, + Monitoring& monitoring) { + monitoring.send(Metric{(uint64_t)0, "arrow-bytes-created"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.send(Metric{(uint64_t)0, "arrow-messages-created"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.send(Metric{(uint64_t)0, "arrow-bytes-destroyed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.send(Metric{(uint64_t)0, "arrow-messages-destroyed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.flushBuffer(); + + if (!options.isSet("aod-file")) { + LOGP(ERROR, "No input file defined!"); + throw std::runtime_error("Processing is stopped!"); + } + + auto filename = options.get<std::string>("aod-file"); + + // create a DataInputDirector + auto didir = std::make_shared<DataInputDirector>(filename); + if (options.isSet("aod-reader-json")) { + auto jsonFile = options.get<std::string>("aod-reader-json"); + if (!didir->readJson(jsonFile)) { + LOGP(ERROR, "Check the JSON document! Can not be properly parsed!"); + } + } + + // get the run time watchdog + auto* watchdog = new RuntimeWatchdog(options.get<int64_t>("time-limit")); + + // selected the TFN input and + // create list of requested tables + header::DataHeader TFNumberHeader; + std::vector<OutputRoute> requestedTables; + std::vector<OutputRoute> routes(spec.outputs); + for (auto route : routes) { + if (DataSpecUtils::partialMatch(route.matcher, header::DataOrigin("TFN"))) { + auto concrete = DataSpecUtils::asConcreteDataMatcher(route.matcher); + TFNumberHeader = header::DataHeader(concrete.description, concrete.origin, concrete.subSpec); + } else { + requestedTables.emplace_back(route); + } + } + + auto fileCounter = std::make_shared<int>(0); + auto numTF = std::make_shared<int>(-1); + return adaptStateless([TFNumberHeader, + requestedTables, + fileCounter, + numTF, + watchdog, + didir](Monitoring& monitoring, DataAllocator& outputs, ControlService& control, DeviceSpec const& device) { + // check if RuntimeLimit is reached + if (!watchdog->update()) { + LOGP(INFO, "Run time exceeds run time limit of {} seconds!", watchdog->runTimeLimit); + LOGP(INFO, "Stopping reader {} after time frame {}.", device.inputTimesliceId, watchdog->numberTimeFrames - 1); + monitoring.flushBuffer(); + didir->closeInputFiles(); + control.endOfStream(); + control.readyToQuit(QuitRequest::Me); + return; + } + + // Each parallel reader device.inputTimesliceId reads the files fileCounter*device.maxInputTimeslices+device.inputTimesliceId + // the TF to read is numTF + assert(device.inputTimesliceId < device.maxInputTimeslices); + uint64_t timeFrameNumber = 0; + int fcnt = (*fileCounter * device.maxInputTimeslices) + device.inputTimesliceId; + int ntf = *numTF + 1; + monitoring.send(Metric{(uint64_t)ntf, "tf-sent"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + static int currentFileCounter = -1; + static int filesProcessed = 0; + if (currentFileCounter != *fileCounter) { + currentFileCounter = *fileCounter; + monitoring.send(Metric{(uint64_t)++filesProcessed, "files-opened"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + } + + // loop over requested tables + TTree* tr = nullptr; + bool first = true; + static size_t totalSizeUncompressed = 0; + static size_t totalSizeCompressed = 0; + static size_t totalReadCalls = 0; + + for (auto route : requestedTables) { + + // create header + auto concrete = DataSpecUtils::asConcreteDataMatcher(route.matcher); + auto dh = header::DataHeader(concrete.description, concrete.origin, concrete.subSpec); + + // create a TreeToTable object + auto info = didir->getFileFolder(dh, fcnt, ntf); + size_t before = 0; + tr = didir->getDataTree(dh, fcnt, ntf); + if (!tr) { + if (first) { + // check if there is a next file to read + fcnt += device.maxInputTimeslices; + if (didir->atEnd(fcnt)) { + LOGP(INFO, "No input files left to read for reader {}!", device.inputTimesliceId); + didir->closeInputFiles(); + control.endOfStream(); + control.readyToQuit(QuitRequest::Me); + return; + } + // get first folder of next file + ntf = 0; + tr = didir->getDataTree(dh, fcnt, ntf); + if (!tr) { + LOGP(FATAL, "Can not retrieve tree for table {}: fileCounter {}, timeFrame {}", concrete.origin, fcnt, ntf); + throw std::runtime_error("Processing is stopped!"); + } + } else { + LOGP(FATAL, "Can not retrieve tree for table {}: fileCounter {}, timeFrame {}", concrete.origin, fcnt, ntf); + throw std::runtime_error("Processing is stopped!"); + } + } + TTreePerfStats ps("ioperf", tr); + + if (first) { + timeFrameNumber = didir->getTimeFrameNumber(dh, fcnt, ntf); + auto o = Output(TFNumberHeader); + outputs.make<uint64_t>(o) = timeFrameNumber; + } + + // create table output + auto o = Output(dh); + auto& t2t = outputs.make<TreeToTable>(o); + + // add branches to read + // fill the table + + auto colnames = getColumnNames(dh); + if (colnames.size() == 0) { + totalSizeCompressed += tr->GetZipBytes(); + totalSizeUncompressed += tr->GetTotBytes(); + t2t.addAllColumns(tr); + } else { + for (auto& colname : colnames) { + TBranch* branch = tr->GetBranch(colname.c_str()); + totalSizeCompressed += branch->GetZipBytes("*"); + totalSizeUncompressed += branch->GetTotBytes("*"); + t2t.addColumn(colname.c_str()); + } + } + t2t.fill(tr); + if (info.file) { + totalReadCalls += info.file->GetReadCalls() - before; + static std::string currentFileRead = ""; + std::string nextFileRead = info.file->GetPath(); + if (currentFileRead != nextFileRead) { + currentFileRead = nextFileRead; + std::string monitoringInfo(currentFileRead); + monitoringInfo += ","; + monitoringInfo += std::to_string(info.file->GetSize()); +#if __has_include(<TJAlienFile.h>) + auto alienFile = dynamic_cast<TJAlienFile*>(info.file); + if (alienFile) { + monitoringInfo += ","; + monitoringInfo += alienFile->GetSE(); + } +#endif + monitoring.send(Metric{monitoringInfo, "aod-file-read-info"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + LOGP(INFO, "File read info: {}", monitoringInfo); + // TODO extend to publish at the end of the file (or on each TF?) the sizes read *per file* + } + } + monitoring.send(Metric{(double)ps.GetReadCalls(), "aod-tree-read-calls"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + delete tr; + + first = false; + } + monitoring.send(Metric{(uint64_t)totalSizeUncompressed / 1000, "aod-bytes-read-uncompressed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.send(Metric{(uint64_t)totalSizeCompressed / 1000, "aod-bytes-read-compressed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.send(Metric{(uint64_t)totalReadCalls, "aod-total-read-calls"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + + // save file number and time frame + *fileCounter = (fcnt - device.inputTimesliceId) / device.maxInputTimeslices; + *numTF = ntf; + }); + })}; + + return callback; +} + +} // namespace o2::framework::readers diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h new file mode 100644 index 0000000000000..8ef579bd68edf --- /dev/null +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h @@ -0,0 +1,28 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_AODJALIENREADERHELPERS_H_ +#define O2_FRAMEWORK_AODJALIENREADERHELPERS_H_ + +#include "Framework/TableBuilder.h" +#include "Framework/AlgorithmSpec.h" +#include "Framework/Logger.h" +#include <uv.h> + +namespace o2::framework::readers +{ + +struct AODJAlienReaderHelpers { + static AlgorithmSpec rootFileReaderCallback(); +}; + +} // namespace o2::framework::readers + +#endif // O2_FRAMEWORK_AODREADERHELPERS_H_ diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx new file mode 100644 index 0000000000000..1fafab25a956b --- /dev/null +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/Plugins.h" +#include "Framework/AlgorithmSpec.h" +#include "AODJAlienReaderHelpers.h" + +struct ROOTFileReader : o2::framework::AlgorithmPlugin { + o2::framework::AlgorithmSpec create() override + { + return o2::framework::readers::AODJAlienReaderHelpers::rootFileReaderCallback(); + } +}; + +DEFINE_DPL_PLUGIN(ROOTFileReader, CustomAlgorithm); diff --git a/Framework/CMakeLists.txt b/Framework/CMakeLists.txt index fefcb8488ba4e..ca1aa9cf05b33 100644 --- a/Framework/CMakeLists.txt +++ b/Framework/CMakeLists.txt @@ -16,5 +16,11 @@ add_subdirectory(Core) add_subdirectory(Utils) -add_subdirectory(TestWorkflows) +add_subdirectory(AnalysisSupport) + +# Build the GUI support only if we have DebugGUI +if (TARGET AliceO2::DebugGUI) +add_subdirectory(GUISupport) +endif() +add_subdirectory(TestWorkflows) diff --git a/Framework/Core/ANALYSIS.md b/Framework/Core/ANALYSIS.md index a4c12f937352c..bb625129078dd 100644 --- a/Framework/Core/ANALYSIS.md +++ b/Framework/Core/ANALYSIS.md @@ -408,17 +408,17 @@ Produced tables can be saved to file as TTrees. This process is customized by va The options to consider are: -* --keep -* --res-file -* --ntfmerge -* --json-file +* --aod-writer-keep +* --aod-writer-resfile +* --aod-writer-ntfmerge +* --aod-writer-json -#### --keep +#### --aod-writer-keep -`keep` is a comma-separated list of `DataOuputDescriptors`. +`aod-writer-keep` is a comma-separated list of `DataOuputDescriptors`. -`keep` +`aod-writer-keep` ```csh DataOuputDescriptor1,DataOuputDescriptor2, ... ``` @@ -429,9 +429,9 @@ Each `DataOuputDescriptor` is a colon-separated list of 4 items ```csh table:tree:columns:file ``` -and instructs the internal-dpl-aod-writer, to save the columns `columns` of table `table` as TTree `tree` into files `file_x.root`, where `x` is an incremental number. The selected columns are saved as separate TBranches of TTree `tree`. +and instructs the internal-dpl-aod-writer, to save the columns `columns` of table `table` as TTree `tree` in folder `TF_x` of file `file.root`. The selected columns are saved as separate TBranches of TTree `tree`. -By default `x` is incremented with every time frame. This behavior can be modified with the command line option `--ntfmerge`. The value of `ntfmerge` specifies the number of time frames to merge into one file. +By default `x` is incremented with every time frame. This behavior can be modified with the command line option `--ntfmerge`. The value of `aod-writer-ntfmerge` specifies the number of time frames to merge into one `TF_x` folder. The first item of a `DataOuputDescriptor` (`table`) is mandatory and needs to be specified, otherwise the `DataOuputDescriptor` is ignored. The other three items are optional and are filled by default values if missing. @@ -443,7 +443,7 @@ AOD/tablename/0 ``` `tablename` is the name of the table as defined in the workflow definition. -The format of `tree` is a simple string which names the TTree the table is saved to. If `tree` is not specified then `tablename` is used as TTree name. +The format of `tree` is a simple string which names the TTree the table is saved to. If `tree` is not specified then `O2tablename` is used as TTree name. `columns` is a slash(/)-separated list of column names., e.g. @@ -453,32 +453,32 @@ col1/col2/col3 ``` The column names are expected to match column names of table `tablename` as defined in the respective workflow. Non-matching columns are ignored. The selected table columns are saved as separate TBranches with the same names as the corresponding table columns. If `columns` is not specified then all table columns are saved. -`file` finally specifies the base name of the files the tables are saved to. The actual file names are composed as `file`_`x`.root, where `x` is an incremental number. If `file` is not specified the default file name is used. The default file name can be set with the command line option `--res-file`. However, if `res-file` is missing then the default file name is set to `AnalysisResults`. +`file` finally specifies the base name of the file the tables are saved to. The actual file name is `file`.root. If `file` is not specified the default file name is used. The default file name can be set with the command line option `--aod-writer-resfile`. However, if `aod-writer-resfile` is missing then the default file name is set to `AnalysisResults_trees`. ##### Dangling outputs -The `keep` option also accepts the string "dangling" (or any leading sub-string of it). In +The `aod-writer-keep` option also accepts the string "dangling" (or any leading sub-string of it). In this case all dangling output tables are saved. For the parameters `tree`, `columns`, and `file` the default values ([see table below](#priorities)) are used. -#### --ntfmerge +#### --aod-writer-ntfmerge -`ntfmerge` specifies the number of time frames which are merged into a given root file. By default this value is set to 1. The actual file names are composed as `file`_`x`.root, where `x` is an incremental number. `x` is incremented by 1 at every `ntfmerge` time frame. +`aod-writer-ntfmerge` specifies the number of time frames which are merged into a given folder `TF_x`. By default this value is set to 1. `x` is incremented by 1 at every `aod-writer-ntfmerge` time frame. -#### --res-file +#### --aod-writer-resfile -`res-file` specifies the default base name of the results files to which tables are saved. If in any of the `DataOutputDescriptors` the `file` value is missing it will be set to this default value. +`aod-writer-resfile` specifies the default base name of the results files to which tables are saved. If in any of the `DataOutputDescriptors` the `file` value is missing it will be set to this default value. -#### --json-file +#### --aod-writer-json -`json-file` specifies the name of a json-file which contains the full information needed to customize the behavior of the internal-dpl-aod-writer. It can replace the other three options completely. Nevertheless, currently all options are supported ([see also discussion below](#redundancy)). +`aod-writer-json` specifies the name of a json-file which contains the full information needed to customize the behavior of the internal-dpl-aod-writer. It can replace the other three options completely. Nevertheless, currently all options are supported ([see also discussion below](#redundancy)). An example file is shown in the highlighted field below. The relevant information is contained in a json object `OutputDirector`. The `OutputDirector` can include three different items: - 1. `resfile` is a string and corresponds to the `res-file` command line option - 2.`ntfmerge` is an integer and corresponds to the `ntfmerge` command line option - 3.`OutputDescriptors` is an array of objects and corresponds to the `keep` command line option. The objects are equivalent to the `DataOuputDescriptors` of the `keep` option and are composed of 4 items which correspond to the 4 items of a `DataOuputDescriptor`. + 1. `resfile` is a string and corresponds to the `aod-writer-resfile` command line option + 2.`aod-writer-ntfmerge` is an integer and corresponds to the `aod-writer-ntfmerge` command line option + 3.`OutputDescriptors` is an array of objects and corresponds to the `aod-writer-keep` command line option. The objects are equivalent to the `DataOuputDescriptors` of the `aod-writer-keep` option and are composed of 4 items which correspond to the 4 items of a `DataOuputDescriptor`. a. `table` is a string b. `treename` is a string @@ -523,10 +523,10 @@ This hierarchy of the options is summarized in the following table. The columns <a name="priorities"></a> -| parameter\option | keep | res-file | ntfmerge | json-file | default | +| parameter\option | aod-writer-keep | aod-writer-resfile | aod-writer-ntfmerge | aod-writer-json | default | |--------------|:----:|:--------:|:--------:|----------:|:-------:| -| `default file name` | - | 1. | - | 2. | 3. (AnalysisResults)| -| `ntfmerge` | - | - | 1. | 2. | 3. (1) | +| `default file name` | - | 1. | - | 2. | 3. (AnalysisResults_trees)| +| `ntfmerge` | - | - | 1. | 2. | 3. (1) | | `tablename` | 1. | - | - | 2. | - | | `tree` | 1. | - | - | 2. | 3. (`tablename`) | | `columns` | 1. | - | - | 2. | 3. (all columns) | @@ -536,18 +536,18 @@ This hierarchy of the options is summarized in the following table. The columns #### Valid example command line options ```csh ---keep AOD/UNO/0 - # save all columns of table 'UNO' to TTree 'UNO' in files 'AnalysisResults'_x.root +--aod-writer-keep AOD/UNO/0 + # save all columns of table 'UNO' to TTree 'O2uno' in file `AnalysisResults_trees.root` ---keep AOD/UNO/0::c2/c4:unoresults - # save columns 'c2' and 'c4' of table 'UNO' to TTree 'UNO' in files 'unoresults'_x.root +--aod-writer-keep AOD/UNO/0::c2/c4:unoresults + # save columns 'c2' and 'c4' of table 'UNO' to TTree 'O2uno' in file 'unoresults.root` ---res-file myskim --ntfmerge 50 --keep AOD/UNO/0:trsel1:c1/c2,AOD/DUE/0:trsel2:c6/c7/c8 - # save columns 'c1' and 'c2' of table 'UNO' to TTree 'trsel1' in files 'myskim'_x.root and - # save columns 'c6', 'c7' and 'c8' of table 'DUE' to TTree 'trsel2' in files 'myskim'_x.root. - # Merge 50 time frames in each file. +--aod-writer-resfile myskim --aod-writer-ntfmerge 50 --aod-writer-keep AOD/UNO/0:trsel1:c1/c2,AOD/DUE/0:trsel2:c6/c7/c8 + # save columns 'c1' and 'c2' of table 'UNO' to TTree 'trsel1' in file 'myskim.root` and + # save columns 'c6', 'c7' and 'c8' of table 'DUE' to TTree 'trsel2' in file 'myskim.root`. + # Merge 50 time frames in each folder `TF_x`. ---json-file myconfig.json +--aod-writer-json myconfig.json # according to the contents of myconfig.json ``` @@ -563,7 +563,7 @@ two trees with equal name to a given file. The internal-dpl-aod-reader reads trees from root files and provides them as arrow tables to the requesting workflows. Its behavior is customized with the following command line options: * --aod-file -* --json-file +* --aod-reader-json #### --aod-file @@ -578,9 +578,9 @@ The internal-dpl-aod-reader reads trees from root files and provides them as arr ``` -#### --json-file +#### --aod-reader-json -'json-file' is a string and specifies a json file, which contains the +'aod-reader-json' is a string and specifies a json file, which contains the customization information for the internal-dpl-aod-reader. An example file is shown in the highlighted field below. The relevant information is contained in a json object `InputDirector`. The `InputDirector` can include the following @@ -595,12 +595,12 @@ three items: c. `resfiles` is either a string or an array of strings. It specifies a list of possible input files (see discussion of `resfiles` above). d. `fileregex` is a regular expression string which is used to select the input files from the file list specified by `resfiles` -The information contained in a `DataInputDescriptor` instructs the internal-dpl-aod-reader to fill table `table` with the values from the tree `treename` in the files which are defined by `resfiles` and which names match the regex `fileregex`. +The information contained in a `DataInputDescriptor` instructs the internal-dpl-aod-reader to fill table `table` with the values from the tree `treename` in folders `TF_x` of the files which are defined by `resfiles` and which names match the regex `fileregex`. Of the four items of a `DataInputDescriptor`, `table` is the only required information. If one of the other items is missing its value will be set as follows: - 1. `treename` is set to `tablename` of the respective `table` item. - 2. `resfiles` is set to `resfiles` of the `InputDirector` (1. item of the `InputDirector`). If that is missing, then the value of the `aod-file` option is used. If that is missing, then `AnalysisResults.root` is used. + 1. `treename` is set to `O2tablename` of the respective `table` item. + 2. `resfiles` is set to `resfiles` of the `InputDirector` (1. item of the `InputDirector`). If that is missing, then the value of the `aod-file` option is used. If that is missing, then `AnalysisResults_trees.root` is used. 3. `fileregex` is set to `fileregex` of the `InputDirector` (2. item of the `InputDirector`). If that is missing, then `.*` is used. @@ -635,17 +635,17 @@ Of the four items of a `DataInputDescriptor`, `table` is the only required infor } ``` -When the internal-dpl-aod-reader receives the request to fill a given table `tablename` it searches in the provided `InputDirector` for the corresponding `InputDescriptor` and proceeds as defined there. However, if there is no corresponding `InputDescriptor` it falls back to the information provided by the `resfiles` and `fileregex` options of the `InputDirector` and uses the `tablename` as `treename`. +When the internal-dpl-aod-reader receives the request to fill a given table `tablename` it searches in the provided `InputDirector` for the corresponding `InputDescriptor` and proceeds as defined there. However, if there is no corresponding `InputDescriptor` it falls back to the information provided by the `resfiles` and `fileregex` options of the `InputDirector` and uses `O2tablename` as `treename`. #### Some practical comments -The `json-file` option allows to setup the reading of tables in a rather +The `aod-reader-json` option allows to setup the reading of tables in a rather flexible way. Here a few presumably practical cases are discussed: 1. Let's assume a case where data from tables `tableA` and `tableB` need to be processed together. Table `tableA` was previously saved as tree `tableA` to -files `tableAResults_x.root`, where `x` is a number and `tableB` was saved as -tree `tableB` to `tableBResults_x.root`. The following json-file could be used +files `tableAResults_n.root`, where `n` is a number and `tableB` was saved as +tree `tableB` to `tableBResults_n.root`. The following json-file could be used to read these tables: ```csh @@ -667,10 +667,10 @@ to read these tables: } ``` - 2. In this case several tables need to be provided. All tables can be read from files `tableResult_x.root`, except for one table, namely `tableA`, which is saved as tree `treeA` in files `tableAResult_x.root`. + 2. In this case several tables need to be provided. All tables can be read from files `tableResult_n.root`, except for one table, namely `tableA`, which is saved as tree `treeA` in files `tableAResult_n.root`. ```csh - # file resfiles.txt lists all tableResults_x.root and tableAResults_x.root files. + # file resfiles.txt lists all tableResults_n.root and tableAResults_n.root files. "InputDirector": { "resfiles": "@resfiles.txt", @@ -684,7 +684,6 @@ to read these tables: ] } ``` - #### Limitations diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 0c389d10daa68..84e2d22b88bb7 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -8,22 +8,17 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -if(GLFW_FOUND) - set(GUI_SOURCES src/FrameworkGUIDebugger.cxx src/FrameworkGUIDevicesGraph.cxx - src/FrameworkGUIDeviceInspector.cxx - src/FrameworkGUIDataRelayerUsage.cxx src/PaletteHelpers.cxx) -else() - set(GUI_SOURCES src/FrameworkDummyDebugger.cxx) -endif() -if (TARGET AliceO2::DebugGUI) - set(DEBUGGUI_TARGET AliceO2::DebugGUI) -endif() +# Given GCC 7.3 does not provide std::filesystem we use Boost instead +# Drop this once we move to GCC 8.2+ +# if (NOT __APPLE__) + set(BOOST_FILESYSTEM Boost::filesystem) +# endif() o2_add_library(Framework SOURCES src/AODReaderHelpers.cxx src/ASoA.cxx - ${GUI_SOURCES} src/AnalysisHelpers.cxx + src/AnalysisDataModelHelpers.cxx src/BoostOptionsRetriever.cxx src/ChannelConfigurationPolicy.cxx src/ChannelMatching.cxx @@ -31,6 +26,7 @@ o2_add_library(Framework src/ChannelSpecHelpers.cxx src/CommonDataProcessors.cxx src/CommonServices.cxx + src/CommonMessageBackends.cxx src/CompletionPolicy.cxx src/CompletionPolicyHelpers.cxx src/ComputingResourceHelpers.cxx @@ -49,22 +45,11 @@ o2_add_library(Framework src/DataProcessor.cxx src/DataRelayer.cxx src/DataRelayerHelpers.cxx - src/DataSampling.cxx - src/DataSamplingConditionFactory.cxx - src/DataSamplingHeader.cxx - src/DataSamplingConditionCustom.cxx - src/DataSamplingConditionNConsecutive.cxx - src/DataSamplingConditionPayloadSize.cxx - src/DataSamplingConditionRandom.cxx - src/DataSamplingHeader.cxx - src/DataSamplingPolicy.cxx - src/DataSamplingReadoutAdapter.cxx src/DataSpecUtils.cxx src/DeviceConfigInfo.cxx src/DeviceMetricsInfo.cxx src/DeviceSpec.cxx src/DeviceSpecHelpers.cxx - src/Dispatcher.cxx src/DriverControl.cxx src/DriverInfo.cxx src/Expressions.cxx @@ -77,7 +62,6 @@ o2_add_library(Framework src/InputRecord.cxx src/InputSpec.cxx src/OutputSpec.cxx - src/Kernels.cxx src/LifetimeHelpers.cxx src/LocalRootFileService.cxx src/RootConfigParamHelpers.cxx @@ -90,7 +74,10 @@ o2_add_library(Framework src/PropertyTreeHelpers.cxx src/RCombinedDS.cxx src/ReadoutAdapter.cxx + src/ResourcesMonitoringHelper.cxx + src/ServiceRegistry.cxx src/SimpleResourceManager.cxx + src/SimpleRawDeviceService.cxx src/StreamOperators.cxx src/TMessageSerializer.cxx src/TableBuilder.cxx @@ -101,15 +88,17 @@ o2_add_library(Framework src/Task.cxx src/TextControlService.cxx src/Variant.cxx + src/WorkflowCustomizationHelpers.cxx src/WorkflowHelpers.cxx src/WorkflowSerializationHelpers.cxx src/WorkflowSpec.cxx src/runDataProcessing.cxx src/ExternalFairMQDeviceProxy.cxx + src/HistogramRegistry.cxx + src/StepTHn.cxx test/TestClasses.cxx PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src - PUBLIC_LINK_LIBRARIES ${DEBUGGUI_TARGET} - AliceO2::Common + PUBLIC_LINK_LIBRARIES AliceO2::Common AliceO2::Configuration AliceO2::InfoLogger AliceO2::Monitoring @@ -127,12 +116,14 @@ o2_add_library(Framework ROOT::ROOTDataFrame O2::FrameworkLogger Boost::serialization + ${BOOST_FILESYSTEM} arrow::gandiva_shared LibUV::LibUV ) o2_target_root_dictionary(Framework HEADERS test/TestClasses.h + include/Framework/StepTHn.h LINKDEF test/FrameworkCoreTestLinkDef.h) foreach(t @@ -149,14 +140,10 @@ foreach(t ComputingResourceHelpers ConfigParamStore ConfigParamRegistry - ContextRegistry DataDescriptorMatcher DataProcessorSpec DataRefUtils DataRelayer - DataSamplingCondition - DataSamplingHeader - DataSamplingPolicy DeviceConfigInfo DeviceMetricsInfo DeviceSpec @@ -169,6 +156,7 @@ foreach(t Graphviz GroupSlicer HistogramRegistry + IndexBuilder InfoLogger InputRecord InputRecordWalker @@ -204,6 +192,11 @@ foreach(t PUBLIC_LINK_LIBRARIES O2::Framework) endforeach() +o2_add_executable(dpl-null-sink + SOURCES src/o2NullSink.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + ) + o2_add_executable(dpl-run SOURCES src/dplRun.cxx PUBLIC_LINK_LIBRARIES O2::Framework @@ -214,17 +207,6 @@ o2_add_executable(verify-aod-file PUBLIC_LINK_LIBRARIES O2::Framework COMPONENT_NAME Framework) -# tests with input data - -o2_data_file(COPY test/test_DataSampling.json DESTINATION tests) - -o2_add_test(DataSampling NAME test_Framework_test_DataSampling - SOURCES test/test_DataSampling.cxx - COMPONENT_NAME Framework - LABELS framework - PUBLIC_LINK_LIBRARIES O2::Framework - ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) - # tests with a name not starting with test_... o2_add_test(unittest_DataSpecUtils NAME test_Framework_unittest_DataSpecUtils @@ -243,7 +225,6 @@ o2_add_test(unittest_SimpleOptionsRetriever NAME # benchmarks foreach(b - ContextRegistry DataDescriptorMatcher DataRelayer DeviceMetricsInfo @@ -265,14 +246,6 @@ endforeach() # #####################################################@ -if (TARGET AliceO2::DebugGUI) -set (DEBUG_GUI_TESTS_WORKFLOW - CustomGUIGL - CustomGUISokol - SimpleTracksED - ) -endif() - foreach(w BoostSerializedProcessing CallbackService @@ -294,7 +267,6 @@ foreach(w SingleDataSource Task ExternalFairMQDeviceWorkflow - ${DEBUG_GUI_TESTS_WORKFLOW} ) o2_add_test(${w} NAME test_Framework_test_${w} SOURCES test/test_${w}.cxx @@ -310,16 +282,6 @@ endforeach() # part. [WARN] Incoming data is already obsolete, not relaying. set_property(TEST test_Framework_test_DanglingInputs PROPERTY DISABLED TRUE) -if (TARGET AliceO2::DebugGUI) -# TODO: investigate the problem with the two unit tests, maybe setup of the CI -# environment assertion fired X11: The DISPLAY environment variable is missing -# glfw-3.2.1/src/window.c:579: glfwGetFramebufferSize: Assertion `window != -# ((void *)0)' failed. -set_property(TEST test_Framework_test_SimpleTracksED PROPERTY DISABLED TRUE) -set_property(TEST test_Framework_test_CustomGUIGL PROPERTY DISABLED TRUE) -set_property(TEST test_Framework_test_CustomGUISokol PROPERTY DISABLED TRUE) -endif() - # TODO: investigate the problem and re-enable set_property(TEST test_Framework_test_BoostSerializedProcessing PROPERTY DISABLED TRUE) diff --git a/Framework/Core/README.md b/Framework/Core/README.md index 52034947e7588..58b5f405fdd27 100644 --- a/Framework/Core/README.md +++ b/Framework/Core/README.md @@ -467,100 +467,6 @@ There is also a few demonstrator available in particular: - [MillWheel: Fault-Tolerant Stream Processing at Internet Scale](https://research.google.com/pubs/pub41378.html) : paper about Google previous generation system for stream processing -## Data Sampling - -Data Sampling provides possibility to sample data in DPL workflows, basing on certain conditions ( 5% randomly, when payload is greater than 4234 bytes, etc.). The job of passing the right data is done by a data processor called `Dispatcher`. A desired data stream is specified in form of Data Sampling Policies, configured by JSON structures (example below). -``` -{ - "id": "policy_example1", # name of the policy - "active": "false", # activation flag - "machines": [ # list of machines where the policy should be run (now ignored) - "aido2flp1", - "aido2flp2" - ], # list of data that should be sampled, the format is: - # binding1:origin1/description1/subSpec1[;binding2:...] - "query": "clusters:TPC/CLUSTERS/0;tracks:TPC/TRACKS/0", - "samplingConditions": [ # list of sampling conditions - { - "condition": "random", # condition type - "fraction": "0.1", # condition-dependent parameter: fraction of data to sample - "seed": "2112" # condition-dependent parameter: seed of PRNG - } - ], - "blocking": "false" # should the dispatcher block the main data flow? (now ignored) -} -``` - -### Usage - -To use Data Sampling in a DPL workflow insert following lines to your code: -```cpp -#include "Framework/DataSampling.h" -using namespace o2::framework; -void customize(std::vector<CompletionPolicy>& policies) -{ - DataSampling::CustomizeInfrastructure(policies); -} - -void customize(std::vector<ChannelConfigurationPolicy>& policies) -{ - DataSampling::CustomizeInfrastructure(policies); -} - -#include "Framework/runDataProcessing.h" - -std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext &ctx) -{ - - WorkflowSpec workflow; -// <declaration of other DPL processors> - - DataSampling::GenerateInfrastructure(workflow, "json:///absolute/path/to/config/file.json"); - - return workflow; -} -``` - -Sampled data can be subscribed to by adding `InputSpecs` provided by `std::vector<InputSpec> DataSampling::InputSpecsForPolicy(const std::string& policiesSource, const std::string& policyName)` to a chosen data processor. Then, they can be accessed by the bindings specified in the configuration file. Dispatcher adds a `DataSamplingHeader` to the header stack, which contains statistics like total number of evaluated/accepted messages for a given Policy or the sampling time since epoch. - -[o2-datasampling-pod-and-root](https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/TestWorkflows/src/dataSamplingPodAndRoot.cxx) workflow can serve as usage example. - -## Data Sampling Conditions - -The following sampling conditions are available. When more than one is used, a positive decision is taken when all the conditions are fulfilled. -- **DataSamplingConditionRandom** - pseudo-randomly accepts specified fraction of incoming messages. -```json -{ - "condition": "random", - "fraction": "0.1", - "seed": "22222" -} -``` -- **DataSamplingConditionNConsecutive** - approves n consecutive samples in defined cycle. It assumes that timesliceID always increments by one. -```json -{ - "condition": "nConsecutive", - "samplesNumber": "3", - "cycleSize": "100" -} -``` -- **DataSamplingConditionPayloadSize** - approves messages having payload size within specified boundaries. -```json -{ - "condition": "payloadSize", - "lowerLimit": "300", - "upperLimit": "500" -} -``` -- **DataSamplingConditionCustom** - loads a custom condition, which should inherit from DataSamplingCondition, from a specified library. -```json -{ - "condition": "custom", - "moduleName": "QcExample", - "className": "o2::quality_control_modules::example::ExampleCondition", - "customParam": "value" -} -``` ## Document history * v0.9: proposal for approval at the O2 TB - 19th June 2018 diff --git a/Framework/Core/include/Framework/AODReaderHelpers.h b/Framework/Core/include/Framework/AODReaderHelpers.h index 812e22beb669c..4290f2cccba4b 100644 --- a/Framework/Core/include/Framework/AODReaderHelpers.h +++ b/Framework/Core/include/Framework/AODReaderHelpers.h @@ -8,26 +8,24 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef o2_framework_readers_AODReaderHelpers_INCLUDED_H -#define o2_framework_readers_AODReaderHelpers_INCLUDED_H +#ifndef O2_FRAMEWORK_AODREADERHELPERS_H_ +#define O2_FRAMEWORK_AODREADERHELPERS_H_ #include "Framework/TableBuilder.h" #include "Framework/AlgorithmSpec.h" +#include "Framework/Logger.h" +#include <uv.h> -namespace o2 -{ -namespace framework -{ -namespace readers +namespace o2::framework::readers { + struct AODReaderHelpers { static AlgorithmSpec rootFileReaderCallback(); static AlgorithmSpec aodSpawnerCallback(std::vector<InputSpec> requested); + static AlgorithmSpec indexBuilderCallback(std::vector<InputSpec> requested); }; -} // namespace readers -} // namespace framework -} // namespace o2 +} // namespace o2::framework::readers -#endif +#endif // O2_FRAMEWORK_AODREADERHELPERS_H_ diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 5241809b4a4e7..42193623aa93d 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -18,11 +18,12 @@ #include "Framework/Traits.h" #include "Framework/Expressions.h" #include "Framework/ArrowTypes.h" +#include "Framework/RuntimeError.h" #include <arrow/table.h> #include <arrow/array.h> #include <arrow/util/variant.h> -#include <arrow/compute/context.h> #include <arrow/compute/kernel.h> +#include <arrow/compute/api_aggregate.h> #include <gandiva/selection_vector.h> #include <cassert> #include <fmt/format.h> @@ -48,6 +49,12 @@ constexpr bool is_type_with_originals_v = false; template <typename T> constexpr bool is_type_with_originals_v<T, std::void_t<decltype(sizeof(typename T::originals))>> = true; +template <typename T, typename = void> +constexpr bool is_type_with_parent_v = false; + +template <typename T> +constexpr bool is_type_with_parent_v<T, std::void_t<decltype(sizeof(typename T::parent_t))>> = true; + template <typename, typename = void> constexpr bool is_type_with_metadata_v = false; @@ -60,6 +67,18 @@ constexpr bool is_type_with_binding_v = false; template <typename T> constexpr bool is_type_with_binding_v<T, std::void_t<decltype(sizeof(typename T::binding_t))>> = true; +template <typename, typename = void> +constexpr bool is_type_spawnable_v = false; + +template <typename T> +constexpr bool is_type_spawnable_v<T, std::void_t<decltype(sizeof(typename T::spawnable_t))>> = true; + +template <typename T, typename = void> +constexpr bool is_index_table_v = false; + +template <typename T> +constexpr bool is_index_table_v<T, std::void_t<decltype(sizeof(typename T::indexing_t))>> = true; + template <typename T, typename TLambda> void call_if_has_originals(TLambda&& lambda) { @@ -85,6 +104,8 @@ constexpr auto make_originals_from_type() return typename decayed::originals{}; } else if constexpr (is_type_with_originals_v<typename decayed::table_t>) { return typename decayed::table_t::originals{}; + } else if constexpr (is_type_with_parent_v<decayed>) { + return make_originals_from_type<typename decayed::parent_t>(); } else { return framework::pack<decayed>{}; } @@ -477,6 +498,12 @@ struct DefaultIndexPolicy : IndexPolicyBase { { return O2_BUILTIN_UNLIKELY(this->mRowIndex == sentinel.index); } + + auto size() const + { + return mMaxRow; + } + int64_t mMaxRow = 0; }; @@ -561,11 +588,16 @@ struct FilteredIndexPolicy : IndexPolicyBase { this->mRowIndex = -1; } - int64_t getSelectionRow() const + auto getSelectionRow() const { return mSelectionRow; } + auto size() const + { + return mMaxSelection; + } + private: inline void updateRow() { @@ -600,12 +632,12 @@ struct RowViewCore : public IP, C... { using persistent_columns_t = framework::selected_pack<is_persistent_t, C...>; using dynamic_columns_t = framework::selected_pack<is_dynamic_t, C...>; using index_columns_t = framework::selected_pack<is_index_t, C...>; - constexpr inline static bool has_index_v = !std::is_same_v<index_columns_t, framework::pack<>>; + constexpr inline static bool has_index_v = framework::pack_size(index_columns_t{}) > 0; using external_index_columns_t = framework::selected_pack<is_external_index_t, C...>; RowViewCore(arrow::ChunkedArray* columnData[sizeof...(C)], IP&& policy) : IP{policy}, - C(columnData[framework::has_type_at<C>(all_columns{})])... + C(columnData[framework::has_type_at_v<C>(all_columns{})])... { bindIterators(persistent_columns_t{}); bindAllDynamicColumns(dynamic_columns_t{}); @@ -664,6 +696,19 @@ struct RowViewCore : public IP, C... { return copy; } + RowViewCore& operator--() + { + this->moveByIndex(-1); + return *this; + } + + RowViewCore operator--(int) + { + RowViewCore<IP, C...> copy = *this; + this->operator--(); + return copy; + } + /// Allow incrementing by more than one the iterator RowViewCore operator+(int64_t inc) const { @@ -696,12 +741,40 @@ struct RowViewCore : public IP, C... { (CL::setCurrent(current), ...); } + template <typename CL> + auto getCurrent() const + { + return CL::getCurrentRaw(); + } + + template <typename... Cs> + auto getIndexBindingsImpl(framework::pack<Cs...>) const + { + return std::vector<void*>{static_cast<Cs const&>(*this).getCurrentRaw()...}; + } + + auto getIndexBindings() const + { + return getIndexBindingsImpl(external_index_columns_t{}); + } + template <typename... TA> void bindExternalIndices(TA*... current) { (doSetCurrentIndex(external_index_columns_t{}, current), ...); } + template <typename... Cs> + void doSetCurrentIndexRaw(framework::pack<Cs...> p, std::vector<void*>&& ptrs) + { + (Cs::setCurrentRaw(ptrs[framework::has_type_at_v<Cs>(p)]), ...); + } + + void bindExternalIndicesRaw(std::vector<void*>&& ptrs) + { + doSetCurrentIndexRaw(external_index_columns_t{}, std::forward<std::vector<void*>>(ptrs)); + } + private: /// Helper to move to the correct chunk, if needed. /// FIXME: not needed? @@ -774,6 +847,62 @@ using is_soa_table_t = typename framework::is_specialization<T, soa::Table>; template <typename T> using is_soa_table_like_t = typename framework::is_base_of_template<soa::Table, T>; +/// Helper function to extract bound indices +template <typename... Is> +static constexpr auto extractBindings(framework::pack<Is...>) +{ + return framework::pack<typename Is::binding_t...>{}; +} + +template <typename T> +class Filtered; + +template <typename T> +auto select(T const& t, framework::expressions::Filter&& f) +{ + return Filtered<T>({t.asArrowTable()}, framework::expressions::createExpressionTree( + framework::expressions::createOperations(f), + t.asArrowTable()->schema())); +} + +namespace +{ +auto getSliceFor(int value, char const* key, std::shared_ptr<arrow::Table> const& input, std::shared_ptr<arrow::Table>& output, uint64_t& offset) +{ + arrow::Datum value_counts; + auto options = arrow::compute::CountOptions::Defaults(); + ARROW_ASSIGN_OR_RAISE(value_counts, + arrow::compute::CallFunction("value_counts", {input->GetColumnByName(key)}, + &options)); + auto pair = static_cast<arrow::StructArray>(value_counts.array()); + auto values = static_cast<arrow::NumericArray<arrow::Int32Type>>(pair.field(0)->data()); + auto counts = static_cast<arrow::NumericArray<arrow::Int64Type>>(pair.field(1)->data()); + + int slice; + for (slice = 0; slice < values.length(); ++slice) { + if (values.Value(slice) == value) { + offset = slice; + output = input->Slice(slice, counts.Value(slice)); + return arrow::Status::OK(); + } + } + output = input->Slice(0, 0); + return arrow::Status::OK(); +} +} // namespace + +template <typename T> +auto sliceBy(T const& t, framework::expressions::BindingNode const& node, int value) +{ + uint64_t offset = 0; + std::shared_ptr<arrow::Table> result = nullptr; + auto status = getSliceFor(value, node.name.c_str(), t.asArrowTable(), result, offset); + if (status.ok()) { + return T({result}, offset); + } + throw std::runtime_error("Failed to slice table"); +} + /// A Table class which observes an arrow::Table and provides /// It is templated on a set of Column / DynamicColumn types. template <typename... C> @@ -782,10 +911,15 @@ class Table public: using table_t = Table<C...>; using columns = framework::pack<C...>; + using column_types = framework::pack<typename C::type...>; using persistent_columns_t = framework::selected_pack<is_persistent_t, C...>; + using external_index_columns_t = framework::selected_pack<is_external_index_t, C...>; template <typename IP, typename Parent, typename... T> struct RowViewBase : public RowViewCore<IP, C...> { + + using external_index_columns_t = framework::selected_pack<is_external_index_t, C...>; + using bindings_pack_t = decltype(extractBindings(external_index_columns_t{})); using parent_t = Parent; using originals = originals_pack_t<T...>; @@ -819,6 +953,26 @@ class Table return *this; } + void matchTo(RowViewBase const& other) + { + this->mRowIndex = other.mRowIndex; + } + + template <typename TI> + auto getId() const + { + if constexpr (framework::has_type_v<std::decay_t<TI>, bindings_pack_t>) { + constexpr auto idx = framework::has_type_at_v<std::decay_t<TI>>(bindings_pack_t{}); + return framework::pack_element_t<idx, external_index_columns_t>::getId(); + } else if constexpr (std::is_same_v<std::decay_t<TI>, Parent>) { + return this->globalIndex(); + } else { + return static_cast<int32_t>(-1); + } + } + + using IP::size; + using RowViewCore<IP, C...>::operator++; /// Allow incrementing by more than one the iterator @@ -889,11 +1043,6 @@ class Table return RowViewSentinel{mEnd}; } - unfiltered_iterator iteratorAt(uint64_t i) - { - return mBegin + (i - mOffset); - } - filtered_iterator filtered_begin(SelectionVector selection) { // Note that the FilteredIndexPolicy will never outlive the selection which @@ -903,9 +1052,9 @@ class Table return filtered_iterator(mColumnChunks, {selection, mOffset}); } - RowViewSentinel filtered_end(SelectionVector selection) + unfiltered_iterator iteratorAt(uint64_t i) const { - return RowViewSentinel{selection.size()}; + return mBegin + (i - mOffset); } unfiltered_const_iterator begin() const @@ -923,13 +1072,22 @@ class Table { return mTable; } - + /// Return offset + auto offset() const + { + return mOffset; + } /// Size of the table, in rows. int64_t size() const { return mTable->num_rows(); } + int64_t tableSize() const + { + return size(); + } + /// Bind the columns which refer to other tables /// to the associated tables. template <typename... TA> @@ -938,17 +1096,48 @@ class Table mBegin.bindExternalIndices(current...); } + void bindExternalIndicesRaw(std::vector<void*>&& ptrs) + { + mBegin.bindExternalIndicesRaw(std::forward<std::vector<void*>>(ptrs)); + } + + template <typename T, typename... Cs> + void doCopyIndexBindings(framework::pack<Cs...>, T& dest) const + { + dest.bindExternalIndicesRaw(mBegin.getIndexBindings()); + } + + template <typename T> + void copyIndexBindings(T& dest) const + { + doCopyIndexBindings(external_index_columns_t{}, dest); + } + + auto select(framework::expressions::Filter&& f) const + { + auto t = o2::soa::select(*this, std::forward<framework::expressions::Filter>(f)); + copyIndexBindings(t); + return t; + } + + auto sliceBy(framework::expressions::BindingNode const& node, int value) const + { + auto t = o2::soa::sliceBy(*this, node, value); + copyIndexBindings(t); + return t; + } + private: template <typename T> arrow::ChunkedArray* lookupColumn() { if constexpr (T::persistent::value) { auto label = T::columnLabel(); - auto index = mTable->schema()->GetFieldIndex(label); - if (index == -1) { - throw std::runtime_error(std::string("Unable to find column with label ") + label); + auto index = mTable->schema()->GetAllFieldIndices(label); + if (index.empty() == true) { + throw o2::framework::runtime_error_f("Unable to find column with label %s", label); } - return mTable->column(index).get(); + return mTable->column(index[0]).get(); } else { return nullptr; } @@ -974,16 +1163,10 @@ struct PackToTable<framework::pack<C...>> { using table = o2::soa::Table<C...>; }; -template <typename T> -struct FilterPersistentColumns { - static_assert(framework::always_static_assert_v<T>, "Not a soa::Table"); -}; - -template <typename... C> -struct FilterPersistentColumns<soa::Table<C...>> { - using columns = typename soa::Table<C...>::columns; - using persistent_columns_pack = framework::selected_pack<is_persistent_t, C...>; - using persistent_table_t = typename PackToTable<persistent_columns_pack>::table; +template <typename... T> +struct TableWrap { + using all_columns = framework::concatenated_pack_unique_t<typename T::columns...>; + using table_t = typename PackToTable<all_columns>::table; }; /// Template trait which allows to map a given @@ -995,14 +1178,62 @@ class TableMetadata static constexpr char const* tableLabel() { return INHERIT::mLabel; } static constexpr char const (&origin())[4] { return INHERIT::mOrigin; } static constexpr char const (&description())[16] { return INHERIT::mDescription; } + static std::string sourceSpec() { return fmt::format("{}/{}/{}", INHERIT::mLabel, INHERIT::mOrigin, INHERIT::mDescription); }; }; +/// Helper template to define universal join +template <typename Key, typename H, typename... Ts> +struct IndexTable; + template <typename... C1, typename... C2> -constexpr auto join(o2::soa::Table<C1...> const& t1, o2::soa::Table<C2...> const& t2) +constexpr auto joinTables(o2::soa::Table<C1...> const& t1, o2::soa::Table<C2...> const& t2) { return o2::soa::Table<C1..., C2...>(ArrowHelpers::joinTables({t1.asArrowTable(), t2.asArrowTable()})); } +// special case for appending an index +template <typename... C1, typename Key, typename H, typename... C2> +constexpr auto joinTables(o2::soa::Table<C1...> const& t1, o2::soa::IndexTable<Key, H, C2...> const& t2) +{ + return joinTables(t1, o2::soa::Table<H, C2...>{t2.asArrowTable()}); +} + +template <typename T, typename... C, typename... O> +constexpr auto joinLeft(T const& t1, o2::soa::Table<C...> const& t2, framework::pack<O...>) +{ + return typename o2::soa::TableWrap<O..., o2::soa::Table<C...>>::table_t(ArrowHelpers::joinTables({t1.asArrowTable(), t2.asArrowTable()})); +} + +template <typename T, typename... C, typename... O> +constexpr auto joinRight(o2::soa::Table<C...> const& t1, T const& t2, framework::pack<O...>) +{ + return typename o2::soa::TableWrap<o2::soa::Table<C...>, O...>::table_t(ArrowHelpers::joinTables({t1.asArrowTable(), t2.asArrowTable()})); +} + +template <typename T1, typename T2, typename... O1, typename... O2> +constexpr auto joinBoth(T1 const& t1, T2 const& t2, framework::pack<O1...>, framework::pack<O2...>) +{ + return typename o2::soa::TableWrap<O1..., O2...>::table_t(ArrowHelpers::joinTables({t1.asArrowTable(), t2.asArrowTable()})); +} + +template <typename T1, typename T2> +constexpr auto join(T1 const& t1, T2 const& t2) +{ + if constexpr (soa::is_type_with_originals_v<T1>) { + if constexpr (soa::is_type_with_originals_v<T2>) { + return joinBoth(t1, t2, typename T1::originals{}, typename T2::originals{}); + } else { + return joinLeft(t1, t2, typename T1::originals{}); + } + } else { + if constexpr (soa::is_type_with_originals_v<T2>) { + return joinRight(t1, t2, typename T2::originals{}); + } else { + return joinTables(t1, t2); + } + } +} + template <typename T1, typename T2, typename... Ts> constexpr auto join(T1 const& t1, T2 const& t2, Ts const&... ts) { @@ -1022,6 +1253,13 @@ using JoinBase = decltype(join(std::declval<Ts>()...)); template <typename T1, typename T2> using ConcatBase = decltype(concat(std::declval<T1>(), std::declval<T2>())); +template <typename T1, typename T2> +constexpr auto is_binding_compatible_v() +{ + return framework::pack_size( + framework::intersected_pack_t<originals_pack_t<T1>, originals_pack_t<T2>>{}) > 0; +} + } // namespace o2::soa #define DECLARE_SOA_STORE() \ @@ -1064,6 +1302,7 @@ using ConcatBase = decltype(concat(std::declval<T1>(), std::declval<T2>())); using base = o2::soa::Column<_Type_, _Name_>; \ using type = _Type_; \ using column_t = _Name_; \ + using spawnable_t = std::true_type; \ _Name_(arrow::ChunkedArray const* column) \ : o2::soa::Column<_Type_, _Name_>(o2::soa::ColumnIterator<type>(column)) \ { \ @@ -1116,29 +1355,52 @@ using ConcatBase = decltype(concat(std::declval<T1>(), std::declval<T2>())); _Name_##Id() = default; \ _Name_##Id(_Name_##Id const& other) = default; \ _Name_##Id& operator=(_Name_##Id const& other) = default; \ + type inline getId() const \ + { \ + return _Getter_##Id(); \ + } \ \ type _Getter_##Id() const \ { \ return *mColumnIterator; \ } \ \ - binding_t::iterator _Getter_() const \ + bool has_##_Getter_() const \ + { \ + return *mColumnIterator >= 0; \ + } \ + \ + template <typename T> \ + auto _Getter_##_as() const \ { \ assert(mBinding != nullptr); \ - return mBinding->begin() + *mColumnIterator; \ + return static_cast<T*>(mBinding)->begin() + *mColumnIterator; \ + } \ + \ + auto _Getter_() const \ + { \ + return _Getter_##_as<binding_t>(); \ } \ + \ template <typename T> \ bool setCurrent(T* current) \ { \ - if constexpr (std::is_same_v<T, binding_t>) { \ + if constexpr (o2::soa::is_binding_compatible_v<T, binding_t>()) { \ assert(current != nullptr); \ this->mBinding = current; \ return true; \ } \ return false; \ } \ - binding_t* getCurrent() { return mBinding; } \ - binding_t* mBinding = nullptr; \ + \ + bool setCurrentRaw(void* current) \ + { \ + this->mBinding = current; \ + return true; \ + } \ + binding_t* getCurrent() const { return static_cast<binding_t*>(mBinding); } \ + void* getCurrentRaw() const { return mBinding; } \ + void* mBinding = nullptr; \ }; \ static const o2::framework::expressions::BindingNode _Getter_##Id { _Label_, \ o2::framework::expressions::selectArrowType<_Type_>() } @@ -1237,28 +1499,72 @@ using ConcatBase = decltype(concat(std::declval<T1>(), std::declval<T2>())); #define DECLARE_SOA_TABLE(_Name_, _Origin_, _Description_, ...) \ DECLARE_SOA_TABLE_FULL(_Name_, #_Name_, _Origin_, _Description_, __VA_ARGS__); -#define DECLARE_SOA_EXTENDED_TABLE(_Name_, _Table_, _Description_, ...) \ - using _Name_ = o2::soa::JoinBase<_Table_, o2::soa::Table<__VA_ARGS__>>; \ - \ - struct _Name_##Metadata : o2::soa::TableMetadata<_Name_##Metadata> { \ - using table_t = _Name_; \ - using base_table_t = _Table_; \ - using expression_pack_t = framework::pack<__VA_ARGS__>; \ - static constexpr char const* mLabel = #_Name_; \ - static constexpr char const mOrigin[4] = "DYN"; \ - static constexpr char const mDescription[16] = _Description_; \ - }; \ - \ - template <> \ - struct MetadataTrait<_Name_> { \ - using metadata = _Name_##Metadata; \ - }; \ - \ - template <> \ - struct MetadataTrait<_Name_::unfiltered_iterator> { \ - using metadata = _Name_##Metadata; \ +#define DECLARE_SOA_EXTENDED_TABLE_FULL(_Name_, _Table_, _Origin_, _Description_, ...) \ + using _Name_##Extension = o2::soa::Table<__VA_ARGS__>; \ + using _Name_ = o2::soa::Join<_Name_##Extension, _Table_>; \ + \ + struct _Name_##ExtensionMetadata : o2::soa::TableMetadata<_Name_##ExtensionMetadata> { \ + using table_t = _Name_##Extension; \ + using base_table_t = typename _Table_::table_t; \ + using expression_pack_t = framework::pack<__VA_ARGS__>; \ + using originals = soa::originals_pack_t<_Table_>; \ + static constexpr char const* mLabel = #_Name_ "Extension"; \ + static constexpr char const mOrigin[4] = _Origin_; \ + static constexpr char const mDescription[16] = _Description_; \ + }; \ + \ + template <> \ + struct MetadataTrait<_Name_##Extension> { \ + using metadata = _Name_##ExtensionMetadata; \ }; +#define DECLARE_SOA_EXTENDED_TABLE(_Name_, _Table_, _Description_, ...) \ + DECLARE_SOA_EXTENDED_TABLE_FULL(_Name_, _Table_, "DYN", _Description_, __VA_ARGS__) + +#define DECLARE_SOA_EXTENDED_TABLE_USER(_Name_, _Table_, _Description_, ...) \ + DECLARE_SOA_EXTENDED_TABLE_FULL(_Name_, _Table_, "AOD", _Description_, __VA_ARGS__) + +#define DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, _Origin_, _Description_, _Exclusive_, ...) \ + struct _Name_ : o2::soa::IndexTable<_Key_, __VA_ARGS__> { \ + _Name_(std::shared_ptr<arrow::Table> table, uint64_t offset = 0) : o2::soa::IndexTable<_Key_, __VA_ARGS__>(table, offset){}; \ + _Name_(_Name_ const&) = default; \ + _Name_(_Name_&&) = default; \ + using iterator = typename base_t::template RowView<_Name_, _Name_>; \ + using const_iterator = iterator; \ + }; \ + \ + struct _Name_##Metadata : o2::soa::TableMetadata<_Name_##Metadata> { \ + using Key = _Key_; \ + using index_pack_t = framework::pack<__VA_ARGS__>; \ + using originals = decltype(soa::extractBindings(index_pack_t{})); \ + static constexpr char const* mLabel = #_Name_; \ + static constexpr char const mOrigin[4] = _Origin_; \ + static constexpr char const mDescription[16] = _Description_; \ + static constexpr bool exclusive = _Exclusive_; \ + }; \ + \ + template <> \ + struct MetadataTrait<_Name_> { \ + using metadata = _Name_##Metadata; \ + }; \ + \ + template <> \ + struct MetadataTrait<_Name_::iterator> { \ + using metadata = _Name_##Metadata; \ + }; + +#define DECLARE_SOA_INDEX_TABLE(_Name_, _Key_, _Description_, ...) \ + DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, "IDX", _Description_, false, __VA_ARGS__) + +#define DECLARE_SOA_INDEX_TABLE_EXCLUSIVE(_Name_, _Key_, _Description_, ...) \ + DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, "IDX", _Description_, true, __VA_ARGS__) + +#define DECLARE_SOA_INDEX_TABLE_USER(_Name_, _Key_, _Description_, ...) \ + DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, "AOD", _Description_, false, __VA_ARGS__) + +#define DECLARE_SOA_INDEX_TABLE_EXCLUSIVE_USER(_Name_, _Key_, _Description_, ...) \ + DECLARE_SOA_INDEX_TABLE_FULL(_Name_, _Key_, "AOD", _Description_, true, __VA_ARGS__) + namespace o2::soa { template <typename... Ts> @@ -1322,45 +1628,43 @@ template <typename T> using is_soa_concat_t = typename framework::is_specialization<T, soa::Concat>; template <typename T> -class Filtered : public T +class FilteredPolicy : public T { public: using originals = originals_pack_t<T>; using table_t = typename T::table_t; using persistent_columns_t = typename T::persistent_columns_t; + using external_index_columns_t = typename T::external_index_columns_t; template <typename P, typename... Os> constexpr static auto make_it(framework::pack<Os...> const&) { return typename table_t::template RowViewFiltered<P, Os...>{}; } - using iterator = decltype(make_it<Filtered<T>>(originals{})); + using iterator = decltype(make_it<FilteredPolicy<T>>(originals{})); using const_iterator = iterator; - Filtered(std::vector<std::shared_ptr<arrow::Table>>&& tables, SelectionVector&& selection, uint64_t offset = 0) + FilteredPolicy(std::vector<std::shared_ptr<arrow::Table>>&& tables, SelectionVector&& selection, uint64_t offset = 0) : T{std::move(tables), offset}, - mSelectedRows{std::forward<SelectionVector>(selection)}, - mFilteredBegin{table_t::filtered_begin(mSelectedRows)}, - mFilteredEnd{mSelectedRows.size()} + mSelectedRows{std::forward<SelectionVector>(selection)} { + resetRanges(); } - Filtered(std::vector<std::shared_ptr<arrow::Table>>&& tables, framework::expressions::Selection selection, uint64_t offset = 0) + FilteredPolicy(std::vector<std::shared_ptr<arrow::Table>>&& tables, framework::expressions::Selection selection, uint64_t offset = 0) : T{std::move(tables), offset}, - mSelectedRows{copySelection(selection)}, - mFilteredBegin{table_t::filtered_begin(mSelectedRows)}, - mFilteredEnd{mSelectedRows.size()} + mSelectedRows{copySelection(selection)} { + resetRanges(); } - Filtered(std::vector<std::shared_ptr<arrow::Table>>&& tables, gandiva::NodePtr const& tree, uint64_t offset = 0) + FilteredPolicy(std::vector<std::shared_ptr<arrow::Table>>&& tables, gandiva::NodePtr const& tree, uint64_t offset = 0) : T{std::move(tables), offset}, mSelectedRows{copySelection(framework::expressions::createSelection(this->asArrowTable(), framework::expressions::createFilter(this->asArrowTable()->schema(), - framework::expressions::makeCondition(tree))))}, - mFilteredBegin{table_t::filtered_begin(mSelectedRows)}, - mFilteredEnd{mSelectedRows.size()} + framework::expressions::makeCondition(tree))))} { + resetRanges(); } iterator begin() @@ -1370,7 +1674,7 @@ class Filtered : public T RowViewSentinel end() { - return RowViewSentinel{mFilteredEnd}; + return RowViewSentinel{*mFilteredEnd}; } const_iterator begin() const @@ -1380,7 +1684,7 @@ class Filtered : public T RowViewSentinel end() const { - return RowViewSentinel{mFilteredEnd}; + return RowViewSentinel{*mFilteredEnd}; } iterator iteratorAt(uint64_t i) @@ -1421,21 +1725,237 @@ class Filtered : public T mFilteredBegin.bindExternalIndices(current...); } + void bindExternalIndicesRaw(std::vector<void*>&& ptrs) + { + mFilteredBegin.bindExternalIndicesRaw(std::forward<std::vector<void*>>(ptrs)); + } + + template <typename T1, typename... Cs> + void doCopyIndexBindings(framework::pack<Cs...>, T1& dest) const + { + dest.bindExternalIndicesRaw(mFilteredBegin.getIndexBindings()); + } + + template <typename T1> + void copyIndexBindings(T1& dest) const + { + doCopyIndexBindings(external_index_columns_t{}, dest); + } + + protected: + void sumWithSelection(SelectionVector const& selection) + { + SelectionVector rowsUnion; + std::set_union(mSelectedRows.begin(), mSelectedRows.end(), selection.begin(), selection.end(), std::back_inserter(rowsUnion)); + mSelectedRows = rowsUnion; + resetRanges(); + } + + void intersectWithSelection(SelectionVector const& selection) + { + SelectionVector intersection; + std::set_intersection(mSelectedRows.begin(), mSelectedRows.end(), selection.begin(), selection.end(), std::back_inserter(intersection)); + mSelectedRows = intersection; + resetRanges(); + } + private: + void resetRanges() + { + mFilteredEnd.reset(new RowViewSentinel{mSelectedRows.size()}); + if (tableSize() == 0) { + mFilteredBegin = *mFilteredEnd; + } else { + mFilteredBegin = table_t::filtered_begin(mSelectedRows); + } + } + SelectionVector mSelectedRows; iterator mFilteredBegin; - RowViewSentinel mFilteredEnd; + std::shared_ptr<RowViewSentinel> mFilteredEnd; }; template <typename T> -using is_soa_filtered_t = typename framework::is_specialization<T, soa::Filtered>; +class Filtered : public FilteredPolicy<T> +{ + public: + Filtered(std::vector<std::shared_ptr<arrow::Table>>&& tables, SelectionVector&& selection, uint64_t offset = 0) + : FilteredPolicy<T>(std::move(tables), std::forward<SelectionVector>(selection), offset) {} + + Filtered(std::vector<std::shared_ptr<arrow::Table>>&& tables, framework::expressions::Selection selection, uint64_t offset = 0) + : FilteredPolicy<T>(std::move(tables), selection, offset) {} + + Filtered(std::vector<std::shared_ptr<arrow::Table>>&& tables, gandiva::NodePtr const& tree, uint64_t offset = 0) + : FilteredPolicy<T>(std::move(tables), tree, offset) {} + + Filtered<T> operator+(SelectionVector const& selection) + { + Filtered<T> copy(*this); + copy.sumWithSelection(selection); + return copy; + } + + Filtered<T> operator+(Filtered<T> const& other) + { + return operator+(other.getSelectedRows()); + } + + Filtered<T> operator+=(SelectionVector const& selection) + { + this->sumWithSelection(selection); + return *this; + } + + Filtered<T> operator+=(Filtered<T> const& other) + { + return operator+=(other.getSelectedRows()); + } + + Filtered<T> operator*(SelectionVector const& selection) + { + Filtered<T> copy(*this); + copy.intersectWithSelection(selection); + return copy; + } + + Filtered<T> operator*(Filtered<T> const& other) + { + return operator*(other.getSelectedRows()); + } + + Filtered<T> operator*=(SelectionVector const& selection) + { + this->intersectWithSelection(selection); + return *this; + } + + Filtered<T> operator*=(Filtered<T> const& other) + { + return operator*=(other.getSelectedRows()); + } +}; template <typename T> -auto filter(T&& t, framework::expressions::Filter const& expr) +class Filtered<Filtered<T>> : public FilteredPolicy<typename T::table_t> { - return Filtered<T>(t.asArrowTable(), expr); -} + public: + using table_t = typename FilteredPolicy<typename T::table_t>::table_t; + + Filtered(std::vector<Filtered<T>>&& tables, SelectionVector&& selection, uint64_t offset = 0) + : FilteredPolicy<typename T::table_t>(std::move(extractTablesFromFiltered(std::move(tables))), std::forward<SelectionVector>(selection), offset) + { + for (auto& table : tables) { + *this *= table; + } + } + + Filtered(std::vector<Filtered<T>>&& tables, framework::expressions::Selection selection, uint64_t offset = 0) + : FilteredPolicy<typename T::table_t>(std::move(extractTablesFromFiltered(std::move(tables))), selection, offset) + { + for (auto& table : tables) { + *this *= table; + } + } + + Filtered(std::vector<Filtered<T>>&& tables, gandiva::NodePtr const& tree, uint64_t offset = 0) + : FilteredPolicy<typename T::table_t>(std::move(extractTablesFromFiltered(std::move(tables))), tree, offset) + { + for (auto& table : tables) { + *this *= table; + } + } + + Filtered<Filtered<T>> operator+(SelectionVector const& selection) + { + Filtered<Filtered<T>> copy(*this); + copy.sumWithSelection(selection); + return copy; + } + + Filtered<Filtered<T>> operator+(Filtered<T> const& other) + { + return operator+(other.getSelectedRows()); + } + Filtered<Filtered<T>> operator+=(SelectionVector const& selection) + { + this->sumWithSelection(selection); + return *this; + } + + Filtered<Filtered<T>> operator+=(Filtered<T> const& other) + { + return operator+=(other.getSelectedRows()); + } + + Filtered<Filtered<T>> operator*(SelectionVector const& selection) + { + Filtered<Filtered<T>> copy(*this); + copy.intersectionWithSelection(selection); + return copy; + } + + Filtered<Filtered<T>> operator*(Filtered<T> const& other) + { + return operator*(other.getSelectedRows()); + } + + Filtered<Filtered<T>> operator*=(SelectionVector const& selection) + { + this->intersectWithSelection(selection); + return *this; + } + + Filtered<Filtered<T>> operator*=(Filtered<T> const& other) + { + return operator*=(other.getSelectedRows()); + } + + private: + std::vector<std::shared_ptr<arrow::Table>> extractTablesFromFiltered(std::vector<Filtered<T>>&& tables) + { + std::vector<std::shared_ptr<arrow::Table>> outTables; + for (auto& table : tables) { + outTables.push_back(table.asArrowTable()); + } + return outTables; + } +}; + +template <typename T> +using is_soa_filtered_t = typename framework::is_base_of_template<soa::FilteredPolicy, T>; + +/// Template for building an index table to access matching rows from non- +/// joinable, but compatible tables, e.g. Collisions and ZDCs. +/// First argument is the key table (BCs for the Collisions+ZDCs case), the rest +/// are index columns defined for the required tables. +/// First index will be used by process() as the grouping +template <typename Key, typename H, typename... Ts> +struct IndexTable : Table<soa::Index<>, H, Ts...> { + using base_t = Table<soa::Index<>, H, Ts...>; + using table_t = base_t; + using safe_base_t = Table<H, Ts...>; + using indexing_t = Key; + using first_t = typename H::binding_t; + using rest_t = framework::pack<typename Ts::binding_t...>; + using sources_t = framework::pack<Key, typename H::binding_t, typename Ts::binding_t...>; + + IndexTable(std::shared_ptr<arrow::Table> table, uint64_t offset = 0) + : base_t{table, offset} + { + } + + IndexTable(IndexTable const&) = default; + IndexTable(IndexTable&&) = default; + IndexTable& operator=(IndexTable const&) = default; + IndexTable& operator=(IndexTable&&) = default; + + using iterator = typename base_t::template RowView<IndexTable<Key, H, Ts...>, IndexTable<Key, H, Ts...>>; + using const_iterator = iterator; +}; + +template <typename T> +using is_soa_index_table_t = typename framework::is_base_of_template<soa::IndexTable, T>; } // namespace o2::soa #endif // O2_FRAMEWORK_ASOA_H_ diff --git a/Framework/Core/include/Framework/ASoAHelpers.h b/Framework/Core/include/Framework/ASoAHelpers.h index 9d10f5cafe457..f82b58da8242f 100644 --- a/Framework/Core/include/Framework/ASoAHelpers.h +++ b/Framework/Core/include/Framework/ASoAHelpers.h @@ -13,10 +13,9 @@ #include "Framework/ASoA.h" #include "Framework/Kernels.h" +#include "Framework/RuntimeError.h" #include <arrow/table.h> -#include <arrow/compute/context.h> - #include <iterator> #include <tuple> #include <utility> @@ -124,7 +123,7 @@ auto groupTable(const T& table, const std::string& categoryColumnName, int minCa return doGroupTable<float, arrow::FloatArray>(arrowTable, categoryColumnName, minCatSize, outsider); } // FIXME: Should we support other types as well? - throw std::runtime_error("Combinations: category column must be of integral type"); + throw o2::framework::runtime_error("Combinations: category column must be of integral type"); } // Synchronize categories so as groupedIndices contain elements only of categories common to all tables @@ -926,6 +925,18 @@ auto selfCombinations(const char* categoryColumnName, int categoryNeighbours, co return CombinationsGenerator<CombinationsBlockStrictlyUpperSameIndexPolicy<T1, T2, T2s...>>(CombinationsBlockStrictlyUpperSameIndexPolicy(categoryColumnName, categoryNeighbours, outsider, table, tables...)); } +template <typename T1, typename T2> +auto selfPairCombinations(const char* categoryColumnName, int categoryNeighbours, const T1& outsider, const T2& table) +{ + return CombinationsGenerator<CombinationsBlockStrictlyUpperSameIndexPolicy<T1, T2, T2>>(CombinationsBlockStrictlyUpperSameIndexPolicy(categoryColumnName, categoryNeighbours, outsider, table, table)); +} + +template <typename T1, typename T2> +auto selfTripleCombinations(const char* categoryColumnName, int categoryNeighbours, const T1& outsider, const T2& table) +{ + return CombinationsGenerator<CombinationsBlockStrictlyUpperSameIndexPolicy<T1, T2, T2, T2>>(CombinationsBlockStrictlyUpperSameIndexPolicy(categoryColumnName, categoryNeighbours, outsider, table, table, table)); +} + template <typename T1, typename T2, typename... T2s> auto combinations(const char* categoryColumnName, int categoryNeighbours, const T1& outsider, const T2& table, const T2s&... tables) { @@ -956,6 +967,18 @@ auto combinations(const T2& table, const T2s&... tables) } } +template <typename T2> +auto pairCombinations(const T2& table) +{ + return CombinationsGenerator<CombinationsStrictlyUpperIndexPolicy<T2, T2>>(CombinationsStrictlyUpperIndexPolicy(table, table)); +} + +template <typename T2> +auto tripleCombinations(const T2& table) +{ + return CombinationsGenerator<CombinationsStrictlyUpperIndexPolicy<T2, T2, T2>>(CombinationsStrictlyUpperIndexPolicy(table, table, table)); +} + template <typename T2, typename... T2s> auto combinations(const o2::framework::expressions::Filter& filter, const T2& table, const T2s&... tables) { diff --git a/Framework/Core/include/Framework/AlgorithmSpec.h b/Framework/Core/include/Framework/AlgorithmSpec.h index 2b8471da8e01e..bf18576ad2ff2 100644 --- a/Framework/Core/include/Framework/AlgorithmSpec.h +++ b/Framework/Core/include/Framework/AlgorithmSpec.h @@ -18,9 +18,7 @@ #include <functional> -namespace o2 -{ -namespace framework +namespace o2::framework { /// This is the class holding the actual algorithm to be used. Notice that the @@ -88,13 +86,18 @@ struct AlgorithmSpec { ErrorCallback onError = nullptr; }; +/// Helper class for an algorithm which is loaded as a plugin. +struct AlgorithmPlugin { + virtual AlgorithmSpec create() = 0; +}; + template <typename T> struct ContextElementTraits { - static T& get(ProcessingContext& ctx) + static decltype(auto) get(ProcessingContext& ctx) { return ctx.services().get<T>(); } - static T& get(InitContext& ctx) + static decltype(auto) get(InitContext& ctx) { return ctx.services().get<T>(); } @@ -180,7 +183,6 @@ AlgorithmSpec::InitCallback adaptStateful(LAMBDA l) return adaptStatefulF(FFL(l)); } -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif // FRAMEWORK_ALGORITHMSPEC_H diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index 0c372371666db..fe08af8f20b56 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -42,6 +42,8 @@ DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); DECLARE_SOA_TABLE(Timestamps, "AOD", "TIMESTAMPS", timestamp::Timestamp); +using BCsWithTimestamps = soa::Join<aod::BCs, aod::Timestamps>; + namespace collision { DECLARE_SOA_INDEX_COLUMN(BC, bc); @@ -83,29 +85,31 @@ DECLARE_SOA_COLUMN(Z, z, float); DECLARE_SOA_COLUMN(Snp, snp, float); DECLARE_SOA_COLUMN(Tgl, tgl, float); DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); -DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float snp, float alpha) -> float { - float phi = std::asin(snp) + alpha; - constexpr float twopi = 2.0 * M_PI; +DECLARE_SOA_EXPRESSION_COLUMN(RawPhi, phiraw, float, nasin(aod::track::snp) + aod::track::alpha); +// FIXME: make expression column when conditional nodes are supported in Gandiva +DECLARE_SOA_DYNAMIC_COLUMN(NormalizedPhi, phi, [](float phi) -> float { + constexpr float twopi = 2.0f * static_cast<float>(M_PI); if (phi < 0) phi += twopi; if (phi > twopi) phi -= twopi; return phi; }); -DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float tgl) -> float { return std::log(std::tan(0.25f * static_cast<float>(M_PI) - 0.5f * std::atan(tgl))); }); -DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float signed1Pt) -> float { return std::abs(1.0f / signed1Pt); }); +DECLARE_SOA_EXPRESSION_COLUMN(Eta, eta, float, -1.f * nlog(ntan(0.25f * static_cast<float>(M_PI) - 0.5f * natan(aod::track::tgl)))); +DECLARE_SOA_EXPRESSION_COLUMN(Pt, pt, float, nabs(1.f / aod::track::signed1Pt)); + DECLARE_SOA_DYNAMIC_COLUMN(Charge, charge, [](float signed1Pt) -> short { return (signed1Pt > 0) ? 1 : -1; }); DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float signed1Pt, float snp, float alpha) -> float { auto pt = 1.f / std::abs(signed1Pt); float cs, sn; - utils::sincosf(alpha, sn, cs); + math_utils::sincos(alpha, sn, cs); auto r = std::sqrt((1.f - snp) * (1.f + snp)); return pt * (r * cs - snp * sn); }); DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float signed1Pt, float snp, float alpha) -> float { auto pt = 1.f / std::abs(signed1Pt); float cs, sn; - utils::sincosf(alpha, sn, cs); + math_utils::sincos(alpha, sn, cs); auto r = std::sqrt((1.f - snp) * (1.f + snp)); return pt * (snp * cs + r * sn); }); @@ -113,12 +117,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float signed1Pt, float tgl) -> float { auto pt = 1.f / std::abs(signed1Pt); return pt * tgl; }); -DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float signed1Pt, float tgl) -> float { - return std::sqrt(1.f + tgl * tgl) / std::abs(signed1Pt); -}); -DECLARE_SOA_EXPRESSION_COLUMN(P2, p2, float, (1.f + aod::track::tgl * aod::track::tgl) / (aod::track::signed1Pt * aod::track::signed1Pt)); -DECLARE_SOA_EXPRESSION_COLUMN(Pt2, pt2, float, (1.f / aod::track::signed1Pt) * (1.f / aod::track::signed1Pt)); +DECLARE_SOA_EXPRESSION_COLUMN(P, p, float, 0.5f * (ntan(0.25f * static_cast<float>(M_PI) - 0.5f * natan(aod::track::tgl)) + 1.f / ntan(0.25f * static_cast<float>(M_PI) - 0.5f * natan(aod::track::tgl))) / nabs(aod::track::signed1Pt)); // TRACKPARCOV TABLE definition DECLARE_SOA_COLUMN(SigmaY, sigmaY, float); @@ -155,7 +155,7 @@ DECLARE_SOA_EXPRESSION_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float, aod::track::sigma1Pt* // TRACKEXTRA TABLE definition DECLARE_SOA_COLUMN(TPCInnerParam, tpcInnerParam, float); -DECLARE_SOA_COLUMN(Flags, flags, uint64_t); +DECLARE_SOA_COLUMN(Flags, flags, uint32_t); DECLARE_SOA_COLUMN(ITSClusterMap, itsClusterMap, uint8_t); DECLARE_SOA_COLUMN(TPCNClsFindable, tpcNClsFindable, uint8_t); DECLARE_SOA_COLUMN(TPCNClsFindableMinusFound, tpcNClsFindableMinusFound, int8_t); @@ -171,55 +171,64 @@ DECLARE_SOA_COLUMN(TRDSignal, trdSignal, float); DECLARE_SOA_COLUMN(TOFSignal, tofSignal, float); DECLARE_SOA_COLUMN(Length, length, float); DECLARE_SOA_COLUMN(TOFExpMom, tofExpMom, float); -DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsFound, tpcNClsFound, [](uint8_t tpcNClsFindable, uint8_t tpcNClsFindableMinusFound) -> int16_t { return tpcNClsFindable - tpcNClsFindableMinusFound; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, [](uint8_t tpcNClsFindable, uint8_t TPCNClsFindableMinusCrossedRows) -> int16_t { return tpcNClsFindable - TPCNClsFindableMinusCrossedRows; }); +DECLARE_SOA_COLUMN(TrackEtaEMCAL, trackEtaEmcal, float); +DECLARE_SOA_COLUMN(TrackPhiEMCAL, trackPhiEmcal, float); +DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsFound, tpcNClsFound, [](uint8_t tpcNClsFindable, int8_t tpcNClsFindableMinusFound) -> int16_t { return (int16_t)tpcNClsFindable - tpcNClsFindableMinusFound; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, [](uint8_t tpcNClsFindable, int8_t TPCNClsFindableMinusCrossedRows) -> int16_t { return (int16_t)tpcNClsFindable - TPCNClsFindableMinusCrossedRows; }); DECLARE_SOA_DYNAMIC_COLUMN(ITSNCls, itsNCls, [](uint8_t itsClusterMap) -> uint8_t { uint8_t itsNcls = 0; constexpr uint8_t bit = 1; - for (int layer = 0; layer < 7; layer++) + for (int layer = 0; layer < 7; layer++) { if (itsClusterMap & (bit << layer)) itsNcls++; + } return itsNcls; }); +DECLARE_SOA_DYNAMIC_COLUMN(ITSNClsInnerBarrel, itsNClsInnerBarrel, [](uint8_t itsClusterMap) -> uint8_t { + uint8_t itsNclsInnerBarrel = 0; + constexpr uint8_t bit = 1; + for (int layer = 0; layer < 3; layer++) { + if (itsClusterMap & (bit << layer)) + itsNclsInnerBarrel++; + } + return itsNclsInnerBarrel; +}); DECLARE_SOA_DYNAMIC_COLUMN(TPCCrossedRowsOverFindableCls, tpcCrossedRowsOverFindableCls, - [](uint8_t tpcNClsFindable, uint8_t tpcNClsFindableMinusCrossedRows) -> float { - // FIXME: use int16 tpcNClsCrossedRows from dynamic column as argument - int16_t tpcNClsCrossedRows = tpcNClsFindable - tpcNClsFindableMinusCrossedRows; + [](uint8_t tpcNClsFindable, int8_t tpcNClsFindableMinusCrossedRows) -> float { + int16_t tpcNClsCrossedRows = (int16_t)tpcNClsFindable - tpcNClsFindableMinusCrossedRows; return (float)tpcNClsCrossedRows / (float)tpcNClsFindable; - ; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCFractionSharedCls, tpcFractionSharedCls, [](uint8_t tpcNClsShared, uint8_t tpcNClsFindable, uint8_t tpcNClsFindableMinusFound) -> float { - // FIXME: use tpcNClsFound from dynamic column as argument - int16_t tpcNClsFound = tpcNClsFindable - tpcNClsFindableMinusFound; +DECLARE_SOA_DYNAMIC_COLUMN(TPCFractionSharedCls, tpcFractionSharedCls, [](uint8_t tpcNClsShared, uint8_t tpcNClsFindable, int8_t tpcNClsFindableMinusFound) -> float { + int16_t tpcNClsFound = (int16_t)tpcNClsFindable - tpcNClsFindableMinusFound; return (float)tpcNClsShared / (float)tpcNClsFound; }); } // namespace track -DECLARE_SOA_TABLE_FULL(StoredTracks, "Tracks", "AOD", "TRACKPAR", +DECLARE_SOA_TABLE_FULL(StoredTracks, "Tracks", "AOD", "TRACK:PAR", o2::soa::Index<>, track::CollisionId, track::TrackType, track::X, track::Alpha, track::Y, track::Z, track::Snp, track::Tgl, track::Signed1Pt, - track::Phi<track::Snp, track::Alpha>, - track::Eta<track::Tgl>, - track::Pt<track::Signed1Pt>, + track::NormalizedPhi<track::RawPhi>, track::Px<track::Signed1Pt, track::Snp, track::Alpha>, track::Py<track::Signed1Pt, track::Snp, track::Alpha>, track::Pz<track::Signed1Pt, track::Tgl>, - track::P<track::Signed1Pt, track::Tgl>, track::Charge<track::Signed1Pt>); -DECLARE_SOA_EXTENDED_TABLE(Tracks, StoredTracks, "TRACKPAR", aod::track::Pt2, - aod::track::P2); +DECLARE_SOA_EXTENDED_TABLE(Tracks, StoredTracks, "TRACK:PAR", + aod::track::Pt, + aod::track::P, + aod::track::Eta, + aod::track::RawPhi); -DECLARE_SOA_TABLE_FULL(StoredTracksCov, "TracksCov", "AOD", "TRACKPARCOV", +DECLARE_SOA_TABLE_FULL(StoredTracksCov, "TracksCov", "AOD", "TRACK:PARCOV", track::SigmaY, track::SigmaZ, track::SigmaSnp, track::SigmaTgl, track::Sigma1Pt, track::RhoZY, track::RhoSnpY, track::RhoSnpZ, track::RhoTglY, track::RhoTglZ, track::RhoTglSnp, track::Rho1PtY, track::Rho1PtZ, track::Rho1PtSnp, track::Rho1PtTgl); -DECLARE_SOA_EXTENDED_TABLE(TracksCov, StoredTracksCov, "TRACKPARCOV", +DECLARE_SOA_EXTENDED_TABLE(TracksCov, StoredTracksCov, "TRACK:PARCOV", aod::track::CYY, aod::track::CZY, aod::track::CZZ, @@ -236,7 +245,7 @@ DECLARE_SOA_EXTENDED_TABLE(TracksCov, StoredTracksCov, "TRACKPARCOV", aod::track::C1PtTgl, aod::track::C1Pt21Pt2); -DECLARE_SOA_TABLE(TracksExtra, "AOD", "TRACKEXTRA", +DECLARE_SOA_TABLE(TracksExtra, "AOD", "TRACK:EXTRA", track::TPCInnerParam, track::Flags, track::ITSClusterMap, track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, track::TRDPattern, track::ITSChi2NCl, @@ -244,14 +253,18 @@ DECLARE_SOA_TABLE(TracksExtra, "AOD", "TRACKEXTRA", track::TPCSignal, track::TRDSignal, track::TOFSignal, track::Length, track::TOFExpMom, track::TPCNClsFound<track::TPCNClsFindable, track::TPCNClsFindableMinusFound>, track::TPCNClsCrossedRows<track::TPCNClsFindable, track::TPCNClsFindableMinusCrossedRows>, - track::ITSNCls<track::ITSClusterMap>, + track::ITSNCls<track::ITSClusterMap>, track::ITSNClsInnerBarrel<track::ITSClusterMap>, track::TPCCrossedRowsOverFindableCls<track::TPCNClsFindable, track::TPCNClsFindableMinusCrossedRows>, - track::TPCFractionSharedCls<track::TPCNClsShared, track::TPCNClsFindable, track::TPCNClsFindableMinusFound>); + track::TPCFractionSharedCls<track::TPCNClsShared, track::TPCNClsFindable, track::TPCNClsFindableMinusFound>, + track::TrackEtaEMCAL, track::TrackPhiEMCAL); using Track = Tracks::iterator; using TrackCov = TracksCov::iterator; using TrackExtra = TracksExtra::iterator; +using FullTracks = soa::Join<Tracks, TracksCov, TracksExtra>; +using FullTrack = FullTracks::iterator; + namespace unassignedtracks { DECLARE_SOA_INDEX_COLUMN(Collision, collision); @@ -306,17 +319,67 @@ DECLARE_SOA_COLUMN(ThetaY, thetaY, float); DECLARE_SOA_COLUMN(ZMu, zMu, float); DECLARE_SOA_COLUMN(BendingCoor, bendingCoor, float); DECLARE_SOA_COLUMN(NonBendingCoor, nonBendingCoor, float); -// FIXME: need to implement array columns... -// DECLARE_SOA_COLUMN(Covariances, covariances, float[], "fCovariances"); +DECLARE_SOA_COLUMN(Covariances, covariances, float[15]); DECLARE_SOA_COLUMN(Chi2, chi2, float); DECLARE_SOA_COLUMN(Chi2MatchTrigger, chi2MatchTrigger, float); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float inverseBendingMomentum, float thetaX, float thetaY) -> float { + float pz = -std::sqrt(1.0 + std::tan(thetaY) * std::tan(thetaY)) / std::abs(inverseBendingMomentum); + float pt = std::abs(pz) * std::sqrt(std::tan(thetaX) * std::tan(thetaX) + std::tan(thetaY) * std::tan(thetaY)); + float eta = std::acos(pz / std::sqrt(pt * pt + pz * pz)); + eta = std::tan(0.5 * eta); + if (eta > 0.0) + return -std::log(eta); + else + return 0.0; +}); +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float thetaX, float thetaY) -> float { + float phi = std::atan2(std::tan(thetaY), std::tan(thetaX)); + constexpr float twopi = 2.0f * static_cast<float>(M_PI); + return (phi >= 0.0 ? phi : phi + twopi); +}); +DECLARE_SOA_DYNAMIC_COLUMN(RAtAbsorberEnd, rAtAbsorberEnd, [](float bendingCoor, float nonBendingCoor, float zMu, float thetaX, float thetaY) -> float { + // linear extrapolation of the coordinates of the track to the position of the end of the absorber (-505 cm) + float dZ = -505. - zMu; + float NonBendingSlope = std::tan(thetaX); + float BendingSlope = std::tan(thetaY); + float xAbs = nonBendingCoor + NonBendingSlope * dZ; + float yAbs = bendingCoor + BendingSlope * dZ; + float rAtAbsorberEnd = std::sqrt(xAbs * xAbs + yAbs * yAbs); + return rAtAbsorberEnd; +}); +DECLARE_SOA_DYNAMIC_COLUMN(PDca, pDca, [](float inverseBendingMomentum, float thetaX, float thetaY, float bendingCoor, float nonBendingCoor, float zMu) -> float { + // linear extrapolation of the coordinates of the track to the position of the end of the absorber (-505 cm) + float dca = std::sqrt(bendingCoor * bendingCoor + nonBendingCoor * nonBendingCoor + zMu * zMu); + float pz = -std::sqrt(1.0 + std::tan(thetaY) * std::tan(thetaY)) / std::abs(inverseBendingMomentum); + float pt = std::abs(pz) * std::sqrt(std::tan(thetaX) * std::tan(thetaX) + std::tan(thetaY) * std::tan(thetaY)); + float pTot = std::sqrt(pt * pt + pz * pz); + float pDca = pTot * dca; + return pDca; +}); +DECLARE_SOA_EXPRESSION_COLUMN(Pt, pt, float, nsqrt(1.0f + ntan(aod::muon::thetaY) * ntan(aod::muon::thetaY)) * nsqrt(ntan(aod::muon::thetaX) * ntan(aod::muon::thetaX) + ntan(aod::muon::thetaY) * ntan(aod::muon::thetaY)) / nabs(aod::muon::inverseBendingMomentum)); +DECLARE_SOA_EXPRESSION_COLUMN(Px, px, float, -1.0f * ntan(aod::muon::thetaX) * nsqrt(1.0f + ntan(aod::muon::thetaY) * ntan(aod::muon::thetaY)) / nabs(aod::muon::inverseBendingMomentum)); +DECLARE_SOA_EXPRESSION_COLUMN(Py, py, float, -1.0f * ntan(aod::muon::thetaY) * nsqrt(1.0f + ntan(aod::muon::thetaY) * ntan(aod::muon::thetaY)) / nabs(aod::muon::inverseBendingMomentum)); +DECLARE_SOA_EXPRESSION_COLUMN(Pz, pz, float, -1.0f * nsqrt(1.0f + ntan(aod::muon::thetaY) * ntan(aod::muon::thetaY)) / nabs(aod::muon::inverseBendingMomentum)); +DECLARE_SOA_DYNAMIC_COLUMN(Charge, charge, [](float inverseBendingMomentum) -> short { return (inverseBendingMomentum > 0.0f) ? 1 : -1; }); } // namespace muon -DECLARE_SOA_TABLE(Muons, "AOD", "MUON", - muon::BCId, muon::InverseBendingMomentum, - muon::ThetaX, muon::ThetaY, muon::ZMu, - muon::BendingCoor, muon::NonBendingCoor, - muon::Chi2, muon::Chi2MatchTrigger); +DECLARE_SOA_TABLE_FULL(StoredMuons, "Muons", "AOD", "MUON", + muon::BCId, muon::InverseBendingMomentum, + muon::ThetaX, muon::ThetaY, muon::ZMu, + muon::BendingCoor, muon::NonBendingCoor, + muon::Covariances, muon::Chi2, muon::Chi2MatchTrigger, + muon::Eta<muon::InverseBendingMomentum, muon::ThetaX, muon::ThetaY>, + muon::Phi<muon::ThetaX, muon::ThetaY>, + muon::RAtAbsorberEnd<muon::BendingCoor, muon::NonBendingCoor, muon::ThetaX, muon::ThetaY, muon::ZMu>, + muon::PDca<muon::InverseBendingMomentum, muon::ThetaX, muon::ThetaY, muon::BendingCoor, muon::NonBendingCoor, muon::ZMu>, + muon::Charge<muon::InverseBendingMomentum>); + +DECLARE_SOA_EXTENDED_TABLE(Muons, StoredMuons, "MUON", + aod::muon::Pt, + aod::muon::Px, + aod::muon::Py, + aod::muon::Pz); + using Muon = Muons::iterator; // NOTE for now muon tracks are uniquely assigned to a BC / GlobalBC assuming they contain an MID hit. Discussion on tracks without MID hit is ongoing. @@ -362,92 +425,89 @@ DECLARE_SOA_COLUMN(TimeZPA, timeZPA, float); DECLARE_SOA_COLUMN(TimeZPC, timeZPC, float); } // namespace zdc -DECLARE_SOA_TABLE(Zdcs, "AOD", "ZDC", zdc::BCId, zdc::EnergyZEM1, zdc::EnergyZEM2, +DECLARE_SOA_TABLE(Zdcs, "AOD", "ZDC", o2::soa::Index<>, zdc::BCId, zdc::EnergyZEM1, zdc::EnergyZEM2, zdc::EnergyCommonZNA, zdc::EnergyCommonZNC, zdc::EnergyCommonZPA, zdc::EnergyCommonZPC, zdc::EnergySectorZNA, zdc::EnergySectorZNC, zdc::EnergySectorZPA, zdc::EnergySectorZPC, zdc::TimeZEM1, zdc::TimeZEM2, zdc::TimeZNA, zdc::TimeZNC, zdc::TimeZPA, zdc::TimeZPC); using Zdc = Zdcs::iterator; -namespace ft0 +namespace fv0a { DECLARE_SOA_INDEX_COLUMN(BC, bc); -DECLARE_SOA_COLUMN(Amplitude, amplitude, float[208]); -DECLARE_SOA_COLUMN(TimeA, timeA, float); -DECLARE_SOA_COLUMN(TimeC, timeC, float); -DECLARE_SOA_COLUMN(BCSignal, triggerSignal, uint8_t); -} // namespace ft0 +DECLARE_SOA_COLUMN(Amplitude, amplitude, float[48]); +DECLARE_SOA_COLUMN(Time, time, float); +DECLARE_SOA_COLUMN(TriggerMask, triggerMask, uint8_t); +} // namespace fv0a -DECLARE_SOA_TABLE(FT0s, "AOD", "FT0", ft0::BCId, - ft0::Amplitude, ft0::TimeA, ft0::TimeC, - ft0::BCSignal); -using FT0 = FT0s::iterator; +DECLARE_SOA_TABLE(FV0As, "AOD", "FV0A", o2::soa::Index<>, fv0a::BCId, fv0a::Amplitude, fv0a::Time, fv0a::TriggerMask); +using FV0A = FV0As::iterator; -namespace fv0 +// V0C table for Run2 only +namespace fv0c { DECLARE_SOA_INDEX_COLUMN(BC, bc); -DECLARE_SOA_COLUMN(Amplitude, amplitude, float[48]); +DECLARE_SOA_COLUMN(Amplitude, amplitude, float[32]); +DECLARE_SOA_COLUMN(Time, time, float); +} // namespace fv0c + +DECLARE_SOA_TABLE(FV0Cs, "AOD", "FV0C", o2::soa::Index<>, fv0c::BCId, fv0c::Amplitude, fv0c::Time); +using FV0C = FV0Cs::iterator; + +namespace ft0 +{ +DECLARE_SOA_INDEX_COLUMN(BC, bc); +DECLARE_SOA_COLUMN(AmplitudeA, amplitudeA, float[96]); +DECLARE_SOA_COLUMN(AmplitudeC, amplitudeC, float[112]); DECLARE_SOA_COLUMN(TimeA, timeA, float); -DECLARE_SOA_COLUMN(BCSignal, triggerSignal, uint8_t); -} // namespace fv0 +DECLARE_SOA_COLUMN(TimeC, timeC, float); +DECLARE_SOA_COLUMN(TriggerMask, triggerMask, uint8_t); +} // namespace ft0 -DECLARE_SOA_TABLE(FV0s, "AOD", "FV0", fv0::BCId, - fv0::Amplitude, fv0::TimeA, fv0::BCSignal); -using FV0 = FV0s::iterator; +DECLARE_SOA_TABLE(FT0s, "AOD", "FT0", o2::soa::Index<>, ft0::BCId, + ft0::AmplitudeA, ft0::AmplitudeC, ft0::TimeA, ft0::TimeC, + ft0::TriggerMask); +using FT0 = FT0s::iterator; namespace fdd { DECLARE_SOA_INDEX_COLUMN(BC, bc); -DECLARE_SOA_COLUMN(Amplitude, amplitude, float[8]); +DECLARE_SOA_COLUMN(AmplitudeA, amplitudeA, float[4]); +DECLARE_SOA_COLUMN(AmplitudeC, amplitudeC, float[4]); DECLARE_SOA_COLUMN(TimeA, timeA, float); DECLARE_SOA_COLUMN(TimeC, timeC, float); -DECLARE_SOA_COLUMN(BCSignal, triggerSignal, uint8_t); +DECLARE_SOA_COLUMN(TriggerMask, triggerMask, uint8_t); } // namespace fdd -DECLARE_SOA_TABLE(FDDs, "AOD", "FDD", fdd::BCId, - fdd::Amplitude, fdd::TimeA, fdd::TimeC, - fdd::BCSignal); +DECLARE_SOA_TABLE(FDDs, "AOD", "FDD", o2::soa::Index<>, fdd::BCId, + fdd::AmplitudeA, fdd::AmplitudeC, fdd::TimeA, fdd::TimeC, + fdd::TriggerMask); using FDD = FDDs::iterator; namespace v0 { -DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, Tracks, "fPosTrackID"); -DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, Tracks, "fNegTrackID"); +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, FullTracks, "fPosTrackID"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, FullTracks, "fNegTrackID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); } // namespace v0 -DECLARE_SOA_TABLE(V0s, "AOD", "V0", v0::PosTrackId, v0::NegTrackId); +DECLARE_SOA_TABLE(StoredV0s, "AOD", "V0", o2::soa::Index<>, v0::PosTrackId, v0::NegTrackId); +DECLARE_SOA_TABLE(TransientV0s, "AOD", "V0INDEX", v0::CollisionId); + +using V0s = soa::Join<TransientV0s, StoredV0s>; using V0 = V0s::iterator; namespace cascade { DECLARE_SOA_INDEX_COLUMN(V0, v0); -DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, Tracks, "fTracksID"); +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, FullTracks, "fTracksID"); +DECLARE_SOA_INDEX_COLUMN(Collision, collision); } // namespace cascade -DECLARE_SOA_TABLE(Cascades, "AOD", "CASCADE", cascade::V0Id, cascade::BachelorId); -using Casecade = Cascades::iterator; +DECLARE_SOA_TABLE(StoredCascades, "AOD", "CASCADE", o2::soa::Index<>, cascade::V0Id, cascade::BachelorId); +DECLARE_SOA_TABLE(TransientCascades, "AOD", "CASCADEINDEX", cascade::CollisionId); -// ---- LEGACY tables ---- - -namespace run2v0 -{ -DECLARE_SOA_INDEX_COLUMN(BC, bc); -DECLARE_SOA_COLUMN(Adc, adc, float[64]); -DECLARE_SOA_COLUMN(Time, time, float[64]); -DECLARE_SOA_COLUMN(Width, width, float[64]); -DECLARE_SOA_COLUMN(MultA, multA, float); -DECLARE_SOA_COLUMN(MultC, multC, float); -DECLARE_SOA_COLUMN(TimeA, timeA, float); -DECLARE_SOA_COLUMN(TimeC, timeC, float); -DECLARE_SOA_COLUMN(BBFlag, bbFlag, uint64_t); -DECLARE_SOA_COLUMN(BGFlag, bgFlag, uint64_t); -} // namespace run2v0 - -DECLARE_SOA_TABLE(Run2V0s, "AOD", "RUN2V0", run2v0::BCId, - run2v0::Adc, run2v0::Time, run2v0::Width, - run2v0::MultA, run2v0::MultC, - run2v0::TimeA, run2v0::TimeC, - run2v0::BBFlag, run2v0::BGFlag); -using Run2V0 = Run2V0s::iterator; +using Cascades = soa::Join<TransientCascades, StoredCascades>; +using Cascade = Cascades::iterator; // ---- MC tables ---- @@ -460,11 +520,13 @@ DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(T, t, float); DECLARE_SOA_COLUMN(Weight, weight, float); +DECLARE_SOA_COLUMN(ImpactParameter, impactParameter, float); } // namespace mccollision DECLARE_SOA_TABLE(McCollisions, "AOD", "MCCOLLISION", o2::soa::Index<>, mccollision::BCId, mccollision::GeneratorsID, - mccollision::PosX, mccollision::PosY, mccollision::PosZ, mccollision::T, mccollision::Weight); + mccollision::PosX, mccollision::PosY, mccollision::PosZ, mccollision::T, mccollision::Weight, + mccollision::ImpactParameter); using McCollision = McCollisions::iterator; namespace mcparticle @@ -473,8 +535,10 @@ DECLARE_SOA_INDEX_COLUMN(McCollision, mcCollision); DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); DECLARE_SOA_COLUMN(StatusCode, statusCode, int); DECLARE_SOA_COLUMN(Flags, flags, uint8_t); -DECLARE_SOA_COLUMN(Mother, mother, int[2]); // TODO needs INDEX pending NULL columns -DECLARE_SOA_COLUMN(Daughter, daughter, int[2]); // TODO needs INDEX pending NULL columns +DECLARE_SOA_COLUMN(Mother0, mother0, int); +DECLARE_SOA_COLUMN(Mother1, mother1, int); +DECLARE_SOA_COLUMN(Daughter0, daughter0, int); +DECLARE_SOA_COLUMN(Daughter1, daughter1, int); DECLARE_SOA_COLUMN(Weight, weight, float); DECLARE_SOA_COLUMN(Px, px, float); DECLARE_SOA_COLUMN(Py, py, float); @@ -485,7 +549,7 @@ DECLARE_SOA_COLUMN(Vy, vy, float); DECLARE_SOA_COLUMN(Vz, vz, float); DECLARE_SOA_COLUMN(Vt, vt, float); DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float px, float py) -> float { return static_cast<float>(M_PI) + std::atan2(-py, -px); }); -DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float px, float py, float pz) -> float { return -0.5f * std::log((std::sqrt(px * px + py * py + pz * pz) + pz) / (std::sqrt(px * px + py * py + pz * pz) - pz)); }); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float px, float py, float pz) -> float { return 0.5f * std::log((std::sqrt(px * px + py * py + pz * pz) + pz) / (std::sqrt(px * px + py * py + pz * pz) - pz)); }); DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float px, float py) -> float { return std::sqrt(px * px + py * py); }); DECLARE_SOA_DYNAMIC_COLUMN(ProducedByGenerator, producedByGenerator, [](uint8_t flags) -> bool { return (flags & 0x1) == 0x0; }); } // namespace mcparticle @@ -493,7 +557,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(ProducedByGenerator, producedByGenerator, [](uint8_t DECLARE_SOA_TABLE(McParticles, "AOD", "MCPARTICLE", o2::soa::Index<>, mcparticle::McCollisionId, mcparticle::PdgCode, mcparticle::StatusCode, mcparticle::Flags, - mcparticle::Mother, mcparticle::Daughter, mcparticle::Weight, + mcparticle::Mother0, mcparticle::Mother1, mcparticle::Daughter0, mcparticle::Daughter1, mcparticle::Weight, mcparticle::Px, mcparticle::Py, mcparticle::Pz, mcparticle::E, mcparticle::Vx, mcparticle::Vy, mcparticle::Vz, mcparticle::Vt, mcparticle::Phi<mcparticle::Px, mcparticle::Py>, @@ -540,6 +604,40 @@ DECLARE_SOA_TABLE(McCollisionLabels, "AOD", "MCCOLLISLABEL", mccollisionlabel::LabelId, mccollisionlabel::LabelMask); using McCollisionLabel = McCollisionLabels::iterator; +// --- Matching between collisions and other tables through BC --- + +namespace indices +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_INDEX_COLUMN(BC, bc); +DECLARE_SOA_INDEX_COLUMN(Zdc, zdc); +DECLARE_SOA_INDEX_COLUMN(FV0A, fv0a); +DECLARE_SOA_INDEX_COLUMN(FV0C, fv0c); +DECLARE_SOA_INDEX_COLUMN(FT0, ft0); +DECLARE_SOA_INDEX_COLUMN(FDD, fdd); +} // namespace indices + +// First entry: Collision +#define INDEX_LIST_RUN2 indices::CollisionId, indices::ZdcId, indices::BCId, indices::FT0Id, indices::FV0AId, indices::FV0CId, indices::FDDId +DECLARE_SOA_INDEX_TABLE_EXCLUSIVE(Run2MatchedExclusive, BCs, "MA_RN2_EX", INDEX_LIST_RUN2); +DECLARE_SOA_INDEX_TABLE(Run2MatchedSparse, BCs, "MA_RN2_SP", INDEX_LIST_RUN2); + +#define INDEX_LIST_RUN3 indices::CollisionId, indices::ZdcId, indices::BCId, indices::FT0Id, indices::FV0AId, indices::FDDId +DECLARE_SOA_INDEX_TABLE_EXCLUSIVE(Run3MatchedExclusive, BCs, "MA_RN3_EX", INDEX_LIST_RUN3); +DECLARE_SOA_INDEX_TABLE(Run3MatchedSparse, BCs, "MA_RN3_SP", INDEX_LIST_RUN3); + +// First entry: BC +DECLARE_SOA_INDEX_TABLE_EXCLUSIVE(MatchedBCCollisionsExclusive, BCs, "MA_BCCOL_EX", indices::BCId, indices::CollisionId); +DECLARE_SOA_INDEX_TABLE(MatchedBCCollisionsSparse, BCs, "MA_BCCOL_SP", indices::BCId, indices::CollisionId); + +DECLARE_SOA_INDEX_TABLE_EXCLUSIVE(Run3MatchedToBCExclusive, BCs, "MA_RN3_BC_EX", indices::BCId, indices::ZdcId, indices::FT0Id, indices::FV0AId, indices::FDDId); +DECLARE_SOA_INDEX_TABLE(Run3MatchedToBCSparse, BCs, "MA_RN3_BC_SP", indices::BCId, indices::ZdcId, indices::FT0Id, indices::FV0AId, indices::FDDId); + +// Joins with collisions (only for sparse ones) +// NOTE: index table needs to be always last argument +using CollisionMatchedRun2Sparse = soa::Join<Collisions, Run2MatchedSparse>::iterator; +using CollisionMatchedRun3Sparse = soa::Join<Collisions, Run3MatchedSparse>::iterator; + } // namespace aod } // namespace o2 diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 08c91d3f993ff..169deddc8a512 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -10,33 +10,568 @@ #ifndef o2_framework_AnalysisHelpers_H_DEFINED #define o2_framework_AnalysisHelpers_H_DEFINED -#include "Framework/AnalysisHelpers.h" - +#include "Framework/Traits.h" +#include "Framework/TableBuilder.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/OutputSpec.h" +#include "Framework/OutputRef.h" +#include "Framework/InputSpec.h" +#include "Framework/OutputObjHeader.h" +#include "Framework/StringHelpers.h" +#include "Framework/Output.h" #include <ROOT/RDataFrame.hxx> #include <string> -namespace o2 -{ -namespace framework +namespace o2::framework { class TableConsumer; -} -namespace analysis +template <typename T> +struct WritingCursor { + static_assert(always_static_assert_v<T>, "Type must be a o2::soa::Table"); +}; +/// Helper class actually implementing the cursor which can write to +/// a table. The provided template arguments are if type Column and +/// therefore refer only to the persisted columns. +template <typename... PC> +struct WritingCursor<soa::Table<PC...>> { + using persistent_table_t = soa::Table<PC...>; + using cursor_t = decltype(std::declval<TableBuilder>().cursor<persistent_table_t>()); + + template <typename... T> + void operator()(T... args) + { + static_assert(sizeof...(PC) == sizeof...(T), "Argument number mismatch"); + ++mCount; + cursor(0, extract(args)...); + } + + /// Last index inserted in the table + int64_t lastIndex() + { + return mCount; + } + + bool resetCursor(TableBuilder& builder) + { + mBuilder = &builder; + cursor = std::move(FFL(builder.cursor<persistent_table_t>())); + mCount = -1; + return true; + } + + /// reserve @a size rows when filling, so that we do not + /// spend time reallocating the buffers. + void reserve(int64_t size) + { + mBuilder->reserve(typename persistent_table_t::column_types{}, size); + } + + decltype(FFL(std::declval<cursor_t>())) cursor; + + private: + template <typename T> + static decltype(auto) extract(T const& arg) + { + if constexpr (soa::is_soa_iterator_t<T>::value) { + return arg.globalIndex(); + } else { + static_assert(!framework::has_type_v<T, framework::pack<PC...>>, "Argument type mismatch"); + return arg; + } + } + + /// The table builder which actually performs the + /// construction of the table. We keep it around to be + /// able to do all-columns methods like reserve. + TableBuilder* mBuilder = nullptr; + int64_t mCount = -1; +}; + +template <typename T> +struct Produces { + static_assert(always_static_assert_v<T>, "Type must be a o2::soa::Table"); +}; + +/// This helper class allows you to declare things which will be created by a +/// given analysis task. Notice how the actual cursor is implemented by the +/// means of the WritingCursor helper class, from which produces actually +/// derives. +template <typename... C> +struct Produces<soa::Table<C...>> : WritingCursor<typename soa::PackToTable<typename soa::Table<C...>::persistent_columns_t>::table> { + using table_t = soa::Table<C...>; + using metadata = typename aod::MetadataTrait<table_t>::metadata; + + // @return the associated OutputSpec + OutputSpec const spec() + { + return OutputSpec{OutputLabel{metadata::tableLabel()}, metadata::origin(), metadata::description()}; + } + + OutputRef ref() + { + return OutputRef{metadata::tableLabel(), 0}; + } +}; + +/// Helper template for table transformations +template <typename METADATA> +struct TableTransform { + using SOURCES = typename METADATA::originals; + + using metadata = METADATA; + using sources = SOURCES; + + constexpr auto sources_pack() const + { + return SOURCES{}; + } + + template <typename Oi> + constexpr auto base_spec() const + { + using o_metadata = typename aod::MetadataTrait<Oi>::metadata; + return InputSpec{ + o_metadata::tableLabel(), + header::DataOrigin{o_metadata::origin()}, + header::DataDescription{o_metadata::description()}}; + } + + template <typename... Os> + std::vector<InputSpec> base_specs_impl(framework::pack<Os...>) const + { + return {base_spec<Os>()...}; + } + + std::vector<InputSpec> base_specs() const + { + return base_specs_impl(sources_pack()); + } + + constexpr auto spec() const + { + return OutputSpec{OutputLabel{METADATA::tableLabel()}, METADATA::origin(), METADATA::description()}; + } + + constexpr auto output() const + { + return Output{METADATA::origin(), METADATA::description()}; + } + + constexpr auto ref() const + { + return OutputRef{METADATA::tableLabel(), 0}; + } +}; + +/// This helper struct allows you to declare extended tables which should be +/// created by the task (as opposed to those pre-defined by data model) +template <typename T> +struct Spawns : TableTransform<typename aod::MetadataTrait<framework::pack_head_t<typename T::originals>>::metadata> { + using extension_t = framework::pack_head_t<typename T::originals>; + using base_table_t = typename aod::MetadataTrait<extension_t>::metadata::base_table_t; + using expression_pack_t = typename aod::MetadataTrait<extension_t>::metadata::expression_pack_t; + + constexpr auto pack() + { + return expression_pack_t{}; + } + + T* operator->() + { + return table.get(); + } + T& operator*() + { + return *table.get(); + } + + auto asArrowTable() + { + return extension->asArrowTable(); + } + std::shared_ptr<typename T::table_t> table = nullptr; + std::shared_ptr<extension_t> extension = nullptr; +}; + +/// Policy to control index building +/// Exclusive index: each entry in a row has a valid index +struct IndexExclusive { + /// Generic builder for in index table + template <typename... Cs, typename Key, typename T1, typename... T> + static auto indexBuilder(framework::pack<Cs...>, Key const&, std::tuple<T1, T...> tables) + { + static_assert(sizeof...(Cs) == sizeof...(T) + 1, "Number of columns does not coincide with number of supplied tables"); + using tables_t = framework::pack<T...>; + using first_t = T1; + auto tail = tuple_tail(tables); + TableBuilder builder; + auto cursor = framework::FFL(builder.cursor<o2::soa::Table<Cs...>>()); + + std::array<int32_t, sizeof...(T)> values; + iterator_tuple_t<std::decay_t<T>...> iterators = std::apply( + [](auto&&... x) { + return std::make_tuple(x.begin()...); + }, + tail); + + using rest_it_t = decltype(pack_from_tuple(iterators)); + + int32_t idx = -1; + auto setValue = [&](auto& x) -> bool { + using type = std::decay_t<decltype(x)>; + constexpr auto position = framework::has_type_at_v<type>(rest_it_t{}); + + lowerBound<Key>(idx, x); + if (x == soa::RowViewSentinel{static_cast<uint64_t>(x.mMaxRow)}) { + return false; + } else if (x.template getId<Key>() != idx) { + return false; + } else { + values[position] = x.globalIndex(); + ++x; + return true; + } + }; + + auto first = std::get<first_t>(tables); + for (auto& row : first) { + idx = row.template getId<Key>(); + + if (std::apply( + [](auto&... x) { + return ((x == soa::RowViewSentinel{static_cast<uint64_t>(x.mMaxRow)}) && ...); + }, + iterators)) { + break; + } + + auto result = std::apply( + [&](auto&... x) { + std::array<bool, sizeof...(T)> results{setValue(x)...}; + return (results[framework::has_type_at_v<std::decay_t<decltype(x)>>(rest_it_t{})] && ...); + }, + iterators); + + if (result) { + cursor(0, row.globalIndex(), values[framework::has_type_at_v<T>(tables_t{})]...); + } + } + return builder.finalize(); + } +}; +/// Sparse index: values in a row can be (-1), index table is isomorphic (joinable) +/// to T1 +struct IndexSparse { + template <typename... Cs, typename Key, typename T1, typename... T> + static auto indexBuilder(framework::pack<Cs...>, Key const&, std::tuple<T1, T...> tables) + { + static_assert(sizeof...(Cs) == sizeof...(T) + 1, "Number of columns does not coincide with number of supplied tables"); + using tables_t = framework::pack<T...>; + using first_t = T1; + auto tail = tuple_tail(tables); + TableBuilder builder; + auto cursor = framework::FFL(builder.cursor<o2::soa::Table<Cs...>>()); + + std::array<int32_t, sizeof...(T)> values; + + iterator_tuple_t<std::decay_t<T>...> iterators = std::apply( + [](auto&&... x) { + return std::make_tuple(x.begin()...); + }, + tail); + + using rest_it_t = decltype(pack_from_tuple(iterators)); + + int32_t idx = -1; + auto setValue = [&](auto& x) -> bool { + using type = std::decay_t<decltype(x)>; + constexpr auto position = framework::has_type_at_v<type>(rest_it_t{}); + + lowerBound<Key>(idx, x); + if (x == soa::RowViewSentinel{static_cast<uint64_t>(x.mMaxRow)}) { + values[position] = -1; + return false; + } else if (x.template getId<Key>() != idx) { + values[position] = -1; + return false; + } else { + values[position] = x.globalIndex(); + ++x; + return true; + } + }; + + auto first = std::get<first_t>(tables); + for (auto& row : first) { + idx = row.template getId<Key>(); + std::apply( + [&](auto&... x) { + (setValue(x), ...); + }, + iterators); + + cursor(0, row.globalIndex(), values[framework::has_type_at_v<T>(tables_t{})]...); + } + return builder.finalize(); + } +}; + +/// This helper struct allows you to declare index tables to be created in a task +template <typename T, typename IP = IndexSparse> +struct Builds : TableTransform<typename aod::MetadataTrait<T>::metadata> { + using Key = typename T::indexing_t; + using H = typename T::first_t; + using Ts = typename T::rest_t; + using index_pack_t = typename aod::MetadataTrait<T>::metadata::index_pack_t; + + T* operator->() + { + return table.get(); + } + T& operator*() + { + return *table.get(); + } + + auto asArrowTable() + { + return table->asArrowTable(); + } + std::shared_ptr<T> table = nullptr; + + constexpr auto pack() + { + return index_pack_t{}; + } + + template <typename... Cs, typename Key, typename T1, typename... Ts> + auto build(framework::pack<Cs...>, Key const& key, std::tuple<T1, Ts...> tables) + { + this->table = std::make_shared<T>(IP::indexBuilder(framework::pack<Cs...>{}, key, tables)); + return (this->table != nullptr); + } +}; + +template <typename T> +using BuildsExclusive = Builds<T, IndexExclusive>; + +/// This helper class allows you to declare things which will be created by a +/// given analysis task. Currently wrapped objects are limited to be TNamed +/// descendants. Objects will be written to a ROOT file at the end of the +/// workflow, in directories, corresponding to the task they were declared in. +/// Each object has associated handling policy, which is used by the framework +/// to determine the target file, e.g. analysis result, QA or control histogram, +/// etc. +template <typename T> +struct OutputObj { + using obj_t = T; + + OutputObj(T&& t, OutputObjHandlingPolicy policy_ = OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType sourceType_ = OutputObjSourceType::OutputObjSource) + : object(std::make_shared<T>(t)), + label(t.GetName()), + policy{policy_}, + sourceType{sourceType_}, + mTaskHash{0} + { + } + + OutputObj(std::string const& label_, OutputObjHandlingPolicy policy_ = OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType sourceType_ = OutputObjSourceType::OutputObjSource) + : object(nullptr), + label(label_), + policy{policy_}, + sourceType{sourceType_}, + mTaskHash{0} + { + } + + void setObject(T const& t) + { + object = std::make_shared<T>(t); + object->SetName(label.c_str()); + } + + void setObject(T&& t) + { + object = std::make_shared<T>(t); + object->SetName(label.c_str()); + } + + void setObject(T* t) + { + object.reset(t); + object->SetName(label.c_str()); + } + + void setObject(std::shared_ptr<T> t) + { + object = t; + object->SetName(label.c_str()); + } + + void setHash(uint32_t hash) + { + mTaskHash = hash; + } + + /// @return the associated OutputSpec + OutputSpec const spec() + { + header::DataDescription desc{}; + auto lhash = compile_time_hash(label.c_str()); + std::memset(desc.str, '_', 16); + std::stringstream s; + s << std::hex << lhash; + s << std::hex << mTaskHash; + s << std::hex << reinterpret_cast<uint64_t>(this); + std::memcpy(desc.str, s.str().c_str(), 12); + return OutputSpec{OutputLabel{label}, "ATSK", desc, 0}; + } + + T* operator->() + { + return object.get(); + } + + T& operator*() + { + return *object.get(); + } + + OutputRef ref() + { + return OutputRef{std::string{label}, 0, + o2::header::Stack{OutputObjHeader{policy, sourceType, mTaskHash}}}; + } + + std::shared_ptr<T> object; + std::string label; + OutputObjHandlingPolicy policy; + OutputObjSourceType sourceType; + uint32_t mTaskHash; +}; + +/// This helper allows you to fetch a Sevice from the context or +/// by using some singleton. This hopefully will hide the Singleton and +/// We will be able to retrieve it in a more thread safe manner later on. +template <typename T> +struct Service { + T* service; + + T* operator->() const + { + return service; + } +}; + +template <typename T> +o2::soa::Filtered<T>* getTableFromFilter(const T& table, const expressions::Filter& filter) { + auto schema = table.asArrowTable()->schema(); + expressions::Operations ops = createOperations(filter); + gandiva::NodePtr tree = nullptr; + if (isSchemaCompatible(schema, ops)) { + tree = createExpressionTree(ops, schema); + } else { + throw std::runtime_error("Partition filter does not match declared table type"); + } + + if constexpr (soa::is_soa_filtered_t<std::decay_t<T>>::value) { + return new o2::soa::Filtered<T>{{table}, tree}; + } else { + return new o2::soa::Filtered<T>{{table.asArrowTable()}, tree}; + } +} + +template <typename T> +struct Partition { + Partition(expressions::Node&& filter_) : filter{std::move(filter_)} + { + } + + void bindTable(T& table) + { + mFiltered.reset(getTableFromFilter(table, filter)); + bindExternalIndices(&table); + getBoundToExternalIndices(table); + } + + void setTable(const T& table) + { + mFiltered.reset(getTableFromFilter(table, filter)); + } + + template <typename... Ts> + void bindExternalIndices(Ts*... tables) + { + if (mFiltered != nullptr) { + mFiltered->bindExternalIndices(tables...); + } + } -/// Do a single loop on all the entries of the @a input table -ROOT::RDataFrame doSingleLoopOn(std::unique_ptr<framework::TableConsumer>& input); + template <typename T2> + void getBoundToExternalIndices(T2& table) + { + if (mFiltered != nullptr) { + table.bindExternalIndices(mFiltered.get()); + } + } -/// Do a double loop on all the entries with the same value for the \a grouping -/// of the @a input table, where the entries for the outer index are prefixed -/// with `<name>_` while the entries for the inner loop are prefixed with -/// `<name>bar_`. -ROOT::RDataFrame doSelfCombinationsWith(std::unique_ptr<framework::TableConsumer>& input, - std::string name = "p", - std::string grouping = "eventID"); + void updatePlaceholders(InitContext& context) + { + expressions::updatePlaceholders(filter, context); + } -} // namespace analysis -} // namespace o2 + expressions::Filter filter; + std::unique_ptr<o2::soa::Filtered<T>> mFiltered = nullptr; + + using filtered_iterator = typename o2::soa::Filtered<T>::iterator; + using filtered_const_iterator = typename o2::soa::Filtered<T>::const_iterator; + inline filtered_iterator begin() + { + return mFiltered->begin(); + } + inline o2::soa::RowViewSentinel end() + { + return mFiltered->end(); + } + inline filtered_const_iterator begin() const + { + return mFiltered->begin(); + } + inline o2::soa::RowViewSentinel end() const + { + return mFiltered->end(); + } + + int64_t size() const + { + return mFiltered->size(); + } +}; + +} // namespace o2::framework + +namespace o2::soa +{ +/// On-the-fly adding of expression columns +template <typename T, typename... Cs> +auto Extend(T const& table) +{ + static_assert((soa::is_type_spawnable_v<Cs> && ...), "You can only extend a table with expression columns"); + using output_t = Join<T, soa::Table<Cs...>>; + return output_t{{o2::framework::spawner(framework::pack<Cs...>{}, table.asArrowTable().get()), table.asArrowTable()}, 0}; +} + +/// Template function to attach dynamic columns on-the-fly (e.g. inside +/// process() function). Dynamic columns need to be compatible with the table. +template <typename T, typename... Cs> +auto Attach(T const& table) +{ + static_assert((framework::is_base_of_template<o2::soa::DynamicColumn, Cs>::value && ...), "You can only attach dynamic columns"); + using output_t = Join<T, o2::soa::Table<Cs...>>; + return output_t{{table.asArrowTable()}, table.offset()}; +} +} // namespace o2::soa #endif // o2_framework_AnalysisHelpers_H_DEFINED diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 74c833c0164cf..e6867177bb734 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -11,26 +11,21 @@ #ifndef FRAMEWORK_ANALYSIS_TASK_H_ #define FRAMEWORK_ANALYSIS_TASK_H_ -#include "Framework/Kernels.h" +#include "../../src/AnalysisManagers.h" #include "Framework/AlgorithmSpec.h" -#include "Framework/AnalysisDataModel.h" #include "Framework/CallbackService.h" #include "Framework/ControlService.h" -#include "Framework/ConfigParamSpec.h" -#include "Framework/ConfigParamRegistry.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Expressions.h" +#include "../src/ExpressionHelpers.h" #include "Framework/EndOfStreamContext.h" #include "Framework/Logger.h" -#include "Framework/HistogramRegistry.h" #include "Framework/StructToTuple.h" #include "Framework/FunctionalHelpers.h" #include "Framework/Traits.h" #include "Framework/VariantHelpers.h" -#include "Framework/OutputObjHeader.h" -#include "Framework/RootConfigParamHelpers.h" +#include "Framework/RuntimeError.h" -#include <arrow/compute/context.h> #include <arrow/compute/kernel.h> #include <arrow/table.h> #include <gandiva/node.h> @@ -41,7 +36,6 @@ #include <iomanip> namespace o2::framework { - /// A more familiar task API for the DPL analysis framework. /// This allows you to define your own tasks as subclasses /// of o2::framework::AnalysisTask and to pass them in the specification @@ -52,230 +46,52 @@ namespace o2::framework // FIXME: for the moment this needs to stay outside AnalysisTask // because we cannot inherit from it due to a C++17 bug // in GCC 7.3. We need to move to 7.4+ -template <typename T> -struct WritingCursor { - static_assert(always_static_assert_v<T>, "Type must be a o2::soa::Table"); -}; - -template <typename T> -struct Produces { - static_assert(always_static_assert_v<T>, "Type must be a o2::soa::Table"); +struct AnalysisTask { }; -/// Helper class actually implementing the cursor which can write to -/// a table. The provided template arguments are if type Column and -/// therefore refer only to the persisted columns. -template <typename... PC> -struct WritingCursor<soa::Table<PC...>> { - using persistent_table_t = soa::Table<PC...>; - using cursor_t = decltype(std::declval<TableBuilder>().cursor<persistent_table_t>()); - - template <typename... T> - void operator()(T... args) - { - static_assert(sizeof...(PC) == sizeof...(T), "Argument number mismatch"); - ++mCount; - cursor(0, extract(args)...); - } - - /// Last index inserted in the table - int64_t lastIndex() - { - return mCount; - } - - bool resetCursor(TableBuilder& builder) - { - mBuilder = &builder; - cursor = std::move(FFL(builder.cursor<persistent_table_t>())); - mCount = -1; - return true; - } - - /// reserve @a size rows when filling, so that we do not - /// spend time reallocating the buffers. - void reserve(int64_t size) - { - mBuilder->reserve(typename persistent_table_t::columns{}, size); - } - - decltype(FFL(std::declval<cursor_t>())) cursor; +// Helper struct which builds a DataProcessorSpec from +// the contents of an AnalysisTask... - private: +struct AnalysisDataProcessorBuilder { template <typename T> - static decltype(auto) extract(T const& arg) + static ConfigParamSpec getSpec() { - if constexpr (soa::is_soa_iterator_t<T>::value) { - return arg.globalIndex(); + if constexpr (soa::is_type_with_metadata_v<aod::MetadataTrait<T>>) { + return ConfigParamSpec{aod::MetadataTrait<T>::metadata::tableLabel(), VariantType::String, aod::MetadataTrait<T>::metadata::sourceSpec(), {"\"\""}}; } else { - static_assert(!framework::has_type_v<T, framework::pack<PC...>>, "Argument type mismatch"); - return arg; + using O1 = framework::pack_element_t<0, typename T::originals>; + return ConfigParamSpec{aod::MetadataTrait<T>::metadata::tableLabel(), VariantType::String, aod::MetadataTrait<O1>::metadata::sourceSpec(), {"\"\""}}; } } - /// The table builder which actually performs the - /// construction of the table. We keep it around to be - /// able to do all-columns methods like reserve. - TableBuilder* mBuilder = nullptr; - int64_t mCount = -1; -}; - -/// This helper class allow you to declare things which will be crated by a -/// give analysis task. Notice how the actual cursor is implemented by the -/// means of the WritingCursor helper class, from which produces actually -/// derives. -template <typename... C> -struct Produces<soa::Table<C...>> : WritingCursor<typename soa::FilterPersistentColumns<soa::Table<C...>>::persistent_table_t> { - using table_t = soa::Table<C...>; - using metadata = typename aod::MetadataTrait<table_t>::metadata; - - // @return the associated OutputSpec - OutputSpec const spec() - { - return OutputSpec{OutputLabel{metadata::tableLabel()}, metadata::origin(), metadata::description()}; - } - - OutputRef ref() - { - return OutputRef{metadata::tableLabel(), 0}; - } -}; - -/// This helper class allows you to declare things which will be created by a -/// given analysis task. Currently wrapped objects are limited to be TNamed -/// descendants. Objects will be written to a ROOT file at the end of the -/// workflow, in directories, corresponding to the task they were declared in. -/// Each object has associated handling policy, which is used by the framework -/// to determine the target file, e.g. analysis result, QA or control histogram, -/// etc. -template <typename T> -struct OutputObj { - using obj_t = T; - - OutputObj(T&& t, OutputObjHandlingPolicy policy_ = OutputObjHandlingPolicy::AnalysisObject) - : object(std::make_shared<T>(t)), - label(t.GetName()), - policy{policy_}, - mTaskHash{0} - { - } - - OutputObj(std::string const& label_, OutputObjHandlingPolicy policy_ = OutputObjHandlingPolicy::AnalysisObject) - : object(nullptr), - label(label_), - policy{policy_}, - mTaskHash{0} - { - } - - void setObject(T const& t) - { - object = std::make_shared<T>(t); - object->SetName(label.c_str()); - } - - void setObject(T&& t) - { - object = std::make_shared<T>(t); - object->SetName(label.c_str()); - } - - void setObject(T* t) - { - object.reset(t); - object->SetName(label.c_str()); - } - - void setHash(uint32_t hash) - { - mTaskHash = hash; - } - - /// @return the associated OutputSpec - OutputSpec const spec() - { - header::DataDescription desc{}; - auto lhash = compile_time_hash(label.c_str()); - std::memset(desc.str, '_', 16); - std::stringstream s; - s << std::hex << lhash; - s << std::hex << mTaskHash; - s << std::hex << reinterpret_cast<uint64_t>(this); - std::memcpy(desc.str, s.str().c_str(), 12); - return OutputSpec{OutputLabel{label}, "ATSK", desc, 0}; - } - - T* operator->() - { - return object.get(); - } - - T& operator*() - { - return *object.get(); - } - - OutputRef ref() - { - return OutputRef{std::string{label}, 0, - o2::header::Stack{OutputObjHeader{policy, mTaskHash}}}; - } - - std::shared_ptr<T> object; - std::string label; - OutputObjHandlingPolicy policy; - uint32_t mTaskHash; -}; - -/// This helper allows you to fetch a Sevice from the context or -/// by using some singleton. This hopefully will hide the Singleton and -/// We will be able to retrieve it in a more thread safe manner later on. -template <typename T> -struct Service { - T* service; - - T* operator->() const + template <typename... T> + static std::vector<ConfigParamSpec> getInputSpecs(framework::pack<T...>) { - return service; + return std::vector{getSpec<T>()...}; } -}; -/// This helper allows you to create a configurable option associated to a task. -/// Internally it will be bound to a ConfigParamSpec. -template <typename T> -struct Configurable { - Configurable(std::string const& name, T defaultValue, std::string const& help) - : name(name), value(defaultValue), help(help) - { - } - using type = T; - std::string name; - T value; - std::string help; - operator T() - { - return value; - } - T const* operator->() const + template <typename T> + static std::vector<ConfigParamSpec> getIndexSources() { - return &value; + static_assert(soa::is_soa_index_table_t<T>::value, "Can only be used with IndexTable"); + return getInputSpecs(typename T::sources_t{}); } -}; -struct AnalysisTask { -}; - -// Helper struct which builds a DataProcessorSpec from -// the contents of an AnalysisTask... - -struct AnalysisDataProcessorBuilder { template <typename Arg> static void doAppendInputWithMetadata(std::vector<InputSpec>& inputs) { using metadata = typename aod::MetadataTrait<std::decay_t<Arg>>::metadata; static_assert(std::is_same_v<metadata, void> == false, "Could not find metadata. Did you register your type?"); - inputs.push_back({metadata::tableLabel(), metadata::origin(), metadata::description()}); + if constexpr (soa::is_soa_index_table_t<std::decay_t<Arg>>::value) { + auto inputSources = getIndexSources<std::decay_t<Arg>>(); + std::sort(inputSources.begin(), inputSources.end(), [](ConfigParamSpec const& a, ConfigParamSpec const& b) { return a.name < b.name; }); + auto last = std::unique(inputSources.begin(), inputSources.end(), [](ConfigParamSpec const& a, ConfigParamSpec const& b) { return a.name == b.name; }); + inputSources.erase(last, inputSources.end()); + inputs.push_back(InputSpec{metadata::tableLabel(), metadata::origin(), metadata::description(), Lifetime::Timeframe, inputSources}); + } else { + inputs.push_back({metadata::tableLabel(), metadata::origin(), metadata::description()}); + } } template <typename... Args> @@ -284,15 +100,15 @@ struct AnalysisDataProcessorBuilder { (doAppendInputWithMetadata<Args>(inputs), ...); } - template <typename T, size_t At> - static void appendSomethingWithMetadata(std::vector<InputSpec>& inputs, std::vector<ExpressionInfo>& eInfos) + template <typename T> + static void appendSomethingWithMetadata(std::vector<InputSpec>& inputs, std::vector<ExpressionInfo>& eInfos, size_t at) { using dT = std::decay_t<T>; if constexpr (framework::is_specialization<dT, soa::Filtered>::value) { - eInfos.push_back({At, o2::soa::createSchemaFromColumns(typename dT::table_t::persistent_columns_t{}), nullptr}); + eInfos.push_back({at, o2::soa::createSchemaFromColumns(typename dT::table_t::persistent_columns_t{}), nullptr}); } else if constexpr (soa::is_soa_iterator_t<dT>::value) { - if (std::is_same_v<typename dT::policy_t, soa::FilteredIndexPolicy>) { - eInfos.push_back({At, o2::soa::createSchemaFromColumns(typename dT::table_t::persistent_columns_t{}), nullptr}); + if constexpr (std::is_same_v<typename dT::policy_t, soa::FilteredIndexPolicy>) { + eInfos.push_back({at, o2::soa::createSchemaFromColumns(typename dT::table_t::persistent_columns_t{}), nullptr}); } } doAppendInputWithMetadata(soa::make_originals_from_type<dT>(), inputs); @@ -301,7 +117,7 @@ struct AnalysisDataProcessorBuilder { template <typename R, typename C, typename... Args> static void inputsFromArgs(R (C::*)(Args...), std::vector<InputSpec>& inputs, std::vector<ExpressionInfo>& eInfos) { - (appendSomethingWithMetadata<Args, has_type_at<Args>(pack<Args...>{})>(inputs, eInfos), ...); + (appendSomethingWithMetadata<Args>(inputs, eInfos, o2::framework::has_type_at_v<Args>(pack<Args...>{})), ...); } template <typename R, typename C, typename Grouping, typename... Args> @@ -313,7 +129,7 @@ struct AnalysisDataProcessorBuilder { template <typename R, typename C, typename Grouping, typename... Args> static auto bindGroupingTable(InputRecord& record, R (C::*)(Grouping, Args...), std::vector<ExpressionInfo> const& infos) { - return extractSomethingFromRecord<Grouping, 0>(record, infos); + return extractSomethingFromRecord<Grouping>(record, infos, 0); } template <typename R, typename C> @@ -327,7 +143,11 @@ struct AnalysisDataProcessorBuilder { static auto extractTableFromRecord(InputRecord& record) { if constexpr (soa::is_type_with_metadata_v<aod::MetadataTrait<T>>) { - return record.get<TableConsumer>(aod::MetadataTrait<T>::metadata::tableLabel())->asArrowTable(); + auto table = record.get<TableConsumer>(aod::MetadataTrait<T>::metadata::tableLabel())->asArrowTable(); + if (table->num_rows() == 0) { + table = makeEmptyTable<T>(); + } + return table; } else if constexpr (soa::is_type_with_originals_v<T>) { return extractFromRecord<T>(record, typename T::originals{}); } @@ -354,21 +174,23 @@ struct AnalysisDataProcessorBuilder { } } - template <typename T, size_t At> - static auto extractSomethingFromRecord(InputRecord& record, std::vector<ExpressionInfo> const infos) + template <typename T> + static auto extractSomethingFromRecord(InputRecord& record, std::vector<ExpressionInfo> const infos, size_t at) { using decayed = std::decay_t<T>; if constexpr (soa::is_soa_filtered_t<decayed>::value) { for (auto& info : infos) { - if (info.index == At) + if (info.index == at) { return extractFilteredFromRecord<decayed>(record, info, soa::make_originals_from_type<decayed>()); + } } } else if constexpr (soa::is_soa_iterator_t<decayed>::value) { if constexpr (std::is_same_v<typename decayed::policy_t, soa::FilteredIndexPolicy>) { for (auto& info : infos) { - if (info.index == At) + if (info.index == at) { return extractFilteredFromRecord<decayed>(record, info, soa::make_originals_from_type<decayed>()); + } } } else { return extractFromRecord<decayed>(record, soa::make_originals_from_type<decayed>()); @@ -382,7 +204,7 @@ struct AnalysisDataProcessorBuilder { template <typename R, typename C, typename Grouping, typename... Args> static auto bindAssociatedTables(InputRecord& record, R (C::*)(Grouping, Args...), std::vector<ExpressionInfo> const infos) { - return std::make_tuple(extractSomethingFromRecord<Args, has_type_at<Args>(pack<Args...>{}) + 1u>(record, infos)...); + return std::make_tuple(extractSomethingFromRecord<Args>(record, infos, has_type_at_v<Args>(pack<Args...>{}) + 1u)...); } template <typename R, typename C> @@ -419,7 +241,17 @@ struct AnalysisDataProcessorBuilder { auto getLabelFromType() { - if constexpr (soa::is_type_with_originals_v<std::decay_t<G>>) { + if constexpr (soa::is_soa_index_table_t<std::decay_t<G>>::value) { + using T = typename std::decay_t<G>::first_t; + if constexpr (soa::is_type_with_originals_v<std::decay_t<T>>) { + using O = typename framework::pack_element_t<0, typename std::decay_t<G>::originals>; + using groupingMetadata = typename aod::MetadataTrait<O>::metadata; + return std::string("f") + groupingMetadata::tableLabel() + "ID"; + } else { + using groupingMetadata = typename aod::MetadataTrait<T>::metadata; + return std::string("f") + groupingMetadata::tableLabel() + "ID"; + } + } else if constexpr (soa::is_type_with_originals_v<std::decay_t<G>>) { using T = typename framework::pack_element_t<0, typename std::decay_t<G>::originals>; using groupingMetadata = typename aod::MetadataTrait<T>::metadata; return std::string("f") + groupingMetadata::tableLabel() + "ID"; @@ -434,25 +266,27 @@ struct AnalysisDataProcessorBuilder { mGroupingElement{gt.begin()}, position{0} { + if constexpr (soa::is_soa_filtered_t<std::decay_t<G>>::value) { + groupSelection = >.getSelectedRows(); + } auto indexColumnName = getLabelFromType(); - arrow::compute::FunctionContext ctx; /// prepare slices and offsets for all associated tables that have index /// to grouping table /// auto splitter = [&](auto&& x) { using xt = std::decay_t<decltype(x)>; - constexpr auto index = framework::has_type_at<std::decay_t<decltype(x)>>(associated_pack_t{}); - if (hasIndexTo<std::decay_t<G>>(typename xt::persistent_columns_t{})) { - auto result = o2::framework::sliceByColumn(&ctx, indexColumnName, - static_cast<int32_t>(gt.size()), + constexpr auto index = framework::has_type_at_v<std::decay_t<decltype(x)>>(associated_pack_t{}); + if (x.size() != 0 && hasIndexTo<std::decay_t<G>>(typename xt::persistent_columns_t{})) { + auto result = o2::framework::sliceByColumn(indexColumnName.c_str(), x.asArrowTable(), + static_cast<int32_t>(gt.tableSize()), &groups[index], &offsets[index]); if (result.ok() == false) { - throw std::runtime_error("Cannot split collection"); + throw runtime_error("Cannot split collection"); } - if (groups[index].size() != gt.size()) { - throw std::runtime_error("Splitting collection resulted in different group number than there is rows in the grouping table."); + if (groups[index].size() != gt.tableSize()) { + throw runtime_error_f("Splitting collection resulted in different group number (%d) than there is rows in the grouping table (%d).", groups[index].size(), gt.tableSize()); }; } }; @@ -466,7 +300,7 @@ struct AnalysisDataProcessorBuilder { auto extractor = [&](auto&& x) { using xt = std::decay_t<decltype(x)>; if constexpr (soa::is_soa_filtered_t<xt>::value) { - constexpr auto index = framework::has_type_at<std::decay_t<decltype(x)>>(associated_pack_t{}); + constexpr auto index = framework::has_type_at_v<std::decay_t<decltype(x)>>(associated_pack_t{}); selections[index] = &x.getSelectedRows(); starts[index] = selections[index]->begin(); offsets[index].push_back(std::get<xt>(at).tableSize()); @@ -489,12 +323,23 @@ struct AnalysisDataProcessorBuilder { constexpr bool isIndexTo() { if constexpr (soa::is_type_with_binding_v<C>) { - if constexpr (soa::is_type_with_originals_v<std::decay_t<B>>) { - using TT = typename framework::pack_element_t<0, typename std::decay_t<B>::originals>; - return std::is_same_v<typename C::binding_t, TT>; + if constexpr (soa::is_soa_index_table_t<std::decay_t<B>>::value) { + using T = typename std::decay_t<B>::first_t; + if constexpr (soa::is_type_with_originals_v<std::decay_t<T>>) { + using TT = typename framework::pack_element_t<0, typename std::decay_t<T>::originals>; + return std::is_same_v<typename C::binding_t, TT>; + } else { + using TT = std::decay_t<T>; + return std::is_same_v<typename C::binding_t, TT>; + } } else { - using TT = std::decay_t<B>; - return std::is_same_v<typename C::binding_t, TT>; + if constexpr (soa::is_type_with_originals_v<std::decay_t<B>>) { + using TT = typename framework::pack_element_t<0, typename std::decay_t<B>::originals>; + return std::is_same_v<typename C::binding_t, TT>; + } else { + using TT = std::decay_t<B>; + return std::is_same_v<typename C::binding_t, TT>; + } } } return false; @@ -535,26 +380,32 @@ struct AnalysisDataProcessorBuilder { template <typename A1> auto prepareArgument() { - constexpr auto index = framework::has_type_at<A1>(associated_pack_t{}); - if (hasIndexTo<G>(typename std::decay_t<A1>::persistent_columns_t{})) { + constexpr auto index = framework::has_type_at_v<A1>(associated_pack_t{}); + if (std::get<A1>(*mAt).size() != 0 && hasIndexTo<G>(typename std::decay_t<A1>::persistent_columns_t{})) { + uint64_t pos; + if constexpr (soa::is_soa_filtered_t<std::decay_t<G>>::value) { + pos = (*groupSelection)[position]; + } else { + pos = position; + } if constexpr (soa::is_soa_filtered_t<std::decay_t<A1>>::value) { - auto groupedElementsTable = arrow::util::get<std::shared_ptr<arrow::Table>>(((groups[index])[position]).value); + auto groupedElementsTable = arrow::util::get<std::shared_ptr<arrow::Table>>(((groups[index])[pos]).value); // for each grouping element we need to slice the selection vector - auto start_iterator = std::lower_bound(starts[index], selections[index]->end(), (offsets[index])[position]); - auto stop_iterator = std::lower_bound(start_iterator, selections[index]->end(), (offsets[index])[position + 1]); + auto start_iterator = std::lower_bound(starts[index], selections[index]->end(), (offsets[index])[pos]); + auto stop_iterator = std::lower_bound(start_iterator, selections[index]->end(), (offsets[index])[pos + 1]); starts[index] = stop_iterator; soa::SelectionVector slicedSelection{start_iterator, stop_iterator}; std::transform(slicedSelection.begin(), slicedSelection.end(), slicedSelection.begin(), [&](int64_t idx) { - return idx - static_cast<int64_t>((offsets[index])[position]); + return idx - static_cast<int64_t>((offsets[index])[pos]); }); - std::decay_t<A1> typedTable{{groupedElementsTable}, std::move(slicedSelection), (offsets[index])[position]}; + std::decay_t<A1> typedTable{{groupedElementsTable}, std::move(slicedSelection), (offsets[index])[pos]}; return typedTable; } else { - auto groupedElementsTable = arrow::util::get<std::shared_ptr<arrow::Table>>(((groups[index])[position]).value); - std::decay_t<A1> typedTable{{groupedElementsTable}, (offsets[index])[position]}; + auto groupedElementsTable = arrow::util::get<std::shared_ptr<arrow::Table>>(((groups[index])[pos]).value); + std::decay_t<A1> typedTable{{groupedElementsTable}, (offsets[index])[pos]}; return typedTable; } } else { @@ -566,8 +417,9 @@ struct AnalysisDataProcessorBuilder { std::tuple<A...>* mAt; typename grouping_t::iterator mGroupingElement; uint64_t position = 0; + soa::SelectionVector const* groupSelection = nullptr; - std::array<std::vector<arrow::compute::Datum>, sizeof...(A)> groups; + std::array<std::vector<arrow::Datum>, sizeof...(A)> groups; std::array<std::vector<uint64_t>, sizeof...(A)> offsets; std::array<soa::SelectionVector const*, sizeof...(A)> selections; std::array<soa::SelectionVector::const_iterator, sizeof...(A)> starts; @@ -589,10 +441,23 @@ struct AnalysisDataProcessorBuilder { template <typename Task, typename R, typename C, typename Grouping, typename... Associated> static void invokeProcess(Task& task, InputRecord& inputs, R (C::*)(Grouping, Associated...), std::vector<ExpressionInfo> const& infos) { + auto tupledTask = o2::framework::to_tuple_refs(task); using G = std::decay_t<Grouping>; auto groupingTable = AnalysisDataProcessorBuilder::bindGroupingTable(inputs, &C::process, infos); + + // set filtered tables for partitions with grouping + std::apply([&groupingTable](auto&... x) { + (PartitionManager<std::decay_t<decltype(x)>>::setPartition(x, groupingTable), ...); + }, + tupledTask); + if constexpr (sizeof...(Associated) == 0) { // single argument to process + std::apply([&groupingTable](auto&... x) { + (PartitionManager<std::decay_t<decltype(x)>>::bindExternalIndices(x, &groupingTable), ...); + (PartitionManager<std::decay_t<decltype(x)>>::getBoundToExternalIndices(x, groupingTable), ...); + }, + tupledTask); if constexpr (soa::is_soa_iterator_t<G>::value) { for (auto& element : groupingTable) { task.process(*element); @@ -609,29 +474,52 @@ struct AnalysisDataProcessorBuilder { auto associatedTables = AnalysisDataProcessorBuilder::bindAssociatedTables(inputs, &C::process, infos); auto binder = [&](auto&& x) { x.bindExternalIndices(&groupingTable, &std::get<std::decay_t<Associated>>(associatedTables)...); + std::apply([&x](auto&... t) { + (PartitionManager<std::decay_t<decltype(t)>>::setPartition(t, x), ...); + (PartitionManager<std::decay_t<decltype(t)>>::bindExternalIndices(t, &x), ...); + (PartitionManager<std::decay_t<decltype(t)>>::getBoundToExternalIndices(t, x), ...); + }, + tupledTask); }; groupingTable.bindExternalIndices(&std::get<std::decay_t<Associated>>(associatedTables)...); + // always pre-bind full tables to support index hierarchy + std::apply( + [&](auto&&... x) { + (binder(x), ...); + }, + associatedTables); + if constexpr (soa::is_soa_iterator_t<std::decay_t<G>>::value) { // grouping case auto slicer = GroupSlicer(groupingTable, associatedTables); for (auto& slice : slicer) { auto associatedSlices = slice.associatedTables(); + std::apply( [&](auto&&... x) { (binder(x), ...); }, associatedSlices); + // bind partitions and grouping table + std::apply([&groupingTable](auto&... x) { + (PartitionManager<std::decay_t<decltype(x)>>::bindExternalIndices(x, &groupingTable), ...); + (PartitionManager<std::decay_t<decltype(x)>>::getBoundToExternalIndices(x, groupingTable), ...); + }, + tupledTask); + invokeProcessWithArgs(task, slice.groupingElement(), associatedSlices); } } else { // non-grouping case - std::apply( - [&](auto&&... x) { - (binder(x), ...); - }, - associatedTables); + + // bind partitions and grouping table + std::apply([&groupingTable](auto&... x) { + (PartitionManager<std::decay_t<decltype(x)>>::bindExternalIndices(x, &groupingTable), ...); + (PartitionManager<std::decay_t<decltype(x)>>::getBoundToExternalIndices(x, groupingTable), ...); + }, + tupledTask); invokeProcessWithArgs(task, groupingTable, associatedTables); } @@ -645,203 +533,6 @@ struct AnalysisDataProcessorBuilder { } }; -template <typename T> -struct FilterManager { - template <typename ANY> - static bool createExpressionTrees(ANY&, std::vector<ExpressionInfo>&) - { - return false; - } -}; - -template <> -struct FilterManager<expressions::Filter> { - static bool createExpressionTrees(expressions::Filter const& filter, std::vector<ExpressionInfo>& expressionInfos) - { - - updateExpressionInfos(filter, expressionInfos); - return true; - } -}; - -template <typename T> -struct OutputManager { - template <typename ANY> - static bool appendOutput(std::vector<OutputSpec>&, ANY&, uint32_t) - { - return false; - } - - template <typename ANY> - static bool prepare(ProcessingContext&, ANY&) - { - return false; - } - - template <typename ANY> - static bool postRun(EndOfStreamContext&, ANY&) - { - return true; - } - - template <typename ANY> - static bool finalize(ProcessingContext&, ANY&) - { - return true; - } -}; - -template <typename TABLE> -struct OutputManager<Produces<TABLE>> { - static bool appendOutput(std::vector<OutputSpec>& outputs, Produces<TABLE>& what, uint32_t) - { - outputs.emplace_back(what.spec()); - return true; - } - static bool prepare(ProcessingContext& context, Produces<TABLE>& what) - { - what.resetCursor(context.outputs().make<TableBuilder>(what.ref())); - return true; - } - static bool finalize(ProcessingContext&, Produces<TABLE>&) - { - return true; - } - static bool postRun(EndOfStreamContext&, Produces<TABLE>&) - { - return true; - } -}; - -template <> -struct OutputManager<HistogramRegistry> { - static bool appendOutput(std::vector<OutputSpec>& outputs, HistogramRegistry& what, uint32_t) - { - outputs.emplace_back(what.spec()); - return true; - } - static bool prepare(ProcessingContext&, HistogramRegistry&) - { - return true; - } - - static bool finalize(ProcessingContext&, HistogramRegistry&) - { - return true; - } - - static bool postRun(EndOfStreamContext&, HistogramRegistry&) - { - return true; - } -}; - -template <typename T> -struct OutputManager<OutputObj<T>> { - static bool appendOutput(std::vector<OutputSpec>& outputs, OutputObj<T>& what, uint32_t hash) - { - what.setHash(hash); - outputs.emplace_back(what.spec()); - return true; - } - static bool prepare(ProcessingContext&, OutputObj<T>&) - { - return true; - } - - static bool finalize(ProcessingContext&, OutputObj<T>&) - { - return true; - } - - static bool postRun(EndOfStreamContext& context, OutputObj<T>& what) - { - context.outputs().snapshot(what.ref(), *what); - return true; - } -}; - -template <typename T> -class has_instance -{ - typedef char one; - struct two { - char x[2]; - }; - - template <typename C> - static one test(decltype(&C::instance)); - template <typename C> - static two test(...); - - public: - enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; -}; - -template <typename T> -struct ServiceManager { - template <typename ANY> - static bool prepare(InitContext&, ANY&) - { - return false; - } -}; - -template <typename T> -struct ServiceManager<Service<T>> { - static bool prepare(InitContext& context, Service<T>& service) - { - if constexpr (has_instance<T>::value) { - service.service = &(T::instance()); // Sigh... - return true; - } else { - service.service = context.services().get<T>(); - return true; - } - return false; - } -}; - -template <typename T> -struct OptionManager { - template <typename ANY> - static bool appendOption(std::vector<ConfigParamSpec>&, ANY&) - { - return false; - } - - template <typename ANY> - static bool prepare(InitContext&, ANY&) - { - return false; - } -}; - -template <typename T> -struct OptionManager<Configurable<T>> { - static bool appendOption(std::vector<ConfigParamSpec>& options, Configurable<T>& what) - { - if constexpr (variant_trait_v<typename std::decay<T>::type> != VariantType::Unknown) { - options.emplace_back(ConfigParamSpec{what.name, variant_trait_v<typename std::decay<T>::type>, what.value, {what.help}}); - } else { - auto specs = RootConfigParamHelpers::asConfigParamSpecs<T>(what.name, what.value); - options.insert(options.end(), specs.begin(), specs.end()); - } - return true; - } - - static bool prepare(InitContext& context, Configurable<T>& what) - { - if constexpr (variant_trait_v<typename std::decay<T>::type> != VariantType::Unknown) { - what.value = context.options().get<T>(what.name.c_str()); - } else { - auto pt = context.options().get<boost::property_tree::ptree>(what.name.c_str()); - what.value = RootConfigParamHelpers::as<T>(pt); - } - return true; - } -}; - // SFINAE test template <typename T> class has_process @@ -913,20 +604,28 @@ DataProcessorSpec adaptAnalysisTask(char const* name, Args&&... args) std::vector<InputSpec> inputs; std::vector<ExpressionInfo> expressionInfos; + /// make sure options and configurables are set before expression infos are created + std::apply([&options, &hash](auto&... x) { return (OptionManager<std::decay_t<decltype(x)>>::appendOption(options, x), ...); }, tupledTask); + if constexpr (has_process<T>::value) { // this pushes (I,schemaPtr,nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, inputs, expressionInfos); - // here the FilterManager will prepare the gandiva trees matched to schemas and put the pointers into expressionInfos - std::apply([&expressionInfos](auto&... x) { - return (FilterManager<std::decay_t<decltype(x)>>::createExpressionTrees(x, expressionInfos), ...); - }, - tupledTask); } + //request base tables for spawnable extended tables + std::apply([&inputs](auto&... x) { + return (SpawnManager<std::decay_t<decltype(x)>>::requestInputs(inputs, x), ...); + }, + tupledTask); + + //request base tables for indices to be built + std::apply([&inputs](auto&... x) { + return (IndexManager<std::decay_t<decltype(x)>>::requestInputs(inputs, x), ...); + }, + tupledTask); std::apply([&outputs, &hash](auto&... x) { return (OutputManager<std::decay_t<decltype(x)>>::appendOutput(outputs, x, hash), ...); }, tupledTask); - std::apply([&options, &hash](auto&... x) { return (OptionManager<std::decay_t<decltype(x)>>::appendOption(options, x), ...); }, tupledTask); - auto algo = AlgorithmSpec::InitCallback{[task, expressionInfos](InitContext& ic) { + auto algo = AlgorithmSpec::InitCallback{[task, expressionInfos](InitContext& ic) mutable { auto tupledTask = o2::framework::to_tuple_refs(*task.get()); std::apply([&ic](auto&&... x) { return (OptionManager<std::decay_t<decltype(x)>>::prepare(ic, x), ...); }, tupledTask); std::apply([&ic](auto&&... x) { return (ServiceManager<std::decay_t<decltype(x)>>::prepare(ic, x), ...); }, tupledTask); @@ -939,9 +638,26 @@ DataProcessorSpec adaptAnalysisTask(char const* name, Args&&... args) }; callbacks.set(CallbackService::Id::EndOfStream, endofdatacb); + if constexpr (has_process<T>::value) { + /// update configurables in filters + std::apply( + [&ic](auto&&... x) { return (FilterManager<std::decay_t<decltype(x)>>::updatePlaceholders(x, ic), ...); }, + tupledTask); + /// update configurables in partitions + std::apply( + [&ic](auto&&... x) { return (PartitionManager<std::decay_t<decltype(x)>>::updatePlaceholders(x, ic), ...); }, + tupledTask); + /// create for filters gandiva trees matched to schemas and store the pointers into expressionInfos + std::apply([&expressionInfos](auto&... x) { + return (FilterManager<std::decay_t<decltype(x)>>::createExpressionTrees(x, expressionInfos), ...); + }, + tupledTask); + } + if constexpr (has_init<T>::value) { task->init(ic); } + return [task, expressionInfos](ProcessingContext& pc) { auto tupledTask = o2::framework::to_tuple_refs(*task.get()); std::apply([&pc](auto&&... x) { return (OutputManager<std::decay_t<decltype(x)>>::prepare(pc, x), ...); }, tupledTask); diff --git a/Framework/Core/include/Framework/Array2D.h b/Framework/Core/include/Framework/Array2D.h new file mode 100644 index 0000000000000..438818ce3aa51 --- /dev/null +++ b/Framework/Core/include/Framework/Array2D.h @@ -0,0 +1,112 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FRAMEWORK_ARRAY2D_H +#define FRAMEWORK_ARRAY2D_H +#include <cstdint> +#include <vector> + +namespace o2::framework +{ +// matrix-like wrapper for std::vector +// has no range checks +template <typename T> +struct Array2D { + using element_t = T; + + Array2D(T const* data_, uint32_t r, uint32_t c) + : rows{r}, cols{c} + { + data = new T[rows * cols]; + for (auto i = 0U; i < rows; ++i) { + for (auto j = 0U; j < cols; ++j) { + data[i * cols + j] = *(data_ + (i * cols + j)); + } + } + } + Array2D(std::vector<T> data_, uint32_t r, uint32_t c) + : rows{r}, cols{c} + { + data = new T[rows * cols]; + for (auto i = 0U; i < rows; ++i) { + for (auto j = 0U; j < cols; ++j) { + data[i * cols + j] = data_[i * cols + j]; + } + } + } + + Array2D(Array2D<T> const& other) + : rows{other.rows}, + cols{other.cols} + { + data = new T[rows * cols]; + for (auto i = 0U; i < rows; ++i) { + for (auto j = 0U; j < cols; ++j) { + data[i * cols + j] = *(other.data + (i * cols + j)); + } + } + } + + Array2D(Array2D<T>&& other) + : rows{other.rows}, + cols{other.cols} + { + data = other.data; + other.data = nullptr; + other.rows = 0; + other.cols = 0; + } + + Array2D& operator=(Array2D<T> const& other) + { + this->rows = other.rows; + this->cols = other.cols; + data = new T[rows * cols]; + for (auto i = 0U; i < rows; ++i) { + for (auto j = 0U; j < cols; ++j) { + data[i * cols + j] = *(other.data + (i * cols + j)); + } + } + return *this; + } + + Array2D& operator=(Array2D<T>&& other) + { + this->rows = other.rows; + this->cols = other.cols; + data = other.data; + other.data = nullptr; + other.rows = 0; + other.cols = 0; + return *this; + } + + ~Array2D() + { + if (data != nullptr) { + delete[] data; + } + } + + T operator()(uint32_t y, uint32_t x) const + { + return data[y * cols + x]; + } + T* operator[](uint32_t y) const + { + return data + y * cols; + } + + T* data = nullptr; + uint32_t rows = 0; + uint32_t cols = 0; +}; +} // namespace o2::framework + +#endif // FRAMEWORK_ARRAY2D_H diff --git a/Framework/Core/include/Framework/ArrowContext.h b/Framework/Core/include/Framework/ArrowContext.h index 4601ded5b457b..4fc2f1e320462 100644 --- a/Framework/Core/include/Framework/ArrowContext.h +++ b/Framework/Core/include/Framework/ArrowContext.h @@ -7,8 +7,8 @@ // In applying this license CERN does 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 FRAMEWORK_ARROWCONTEXT_H -#define FRAMEWORK_ARROWCONTEXT_H +#ifndef O2_FRAMEWORK_ARROWCONTEXT_H_ +#define O2_FRAMEWORK_ARROWCONTEXT_H_ #include "Framework/FairMQDeviceProxy.h" #include <cassert> @@ -19,9 +19,7 @@ class FairMQMessage; -namespace o2 -{ -namespace framework +namespace o2::framework { class FairMQResizableBuffer; @@ -92,11 +90,55 @@ class ArrowContext return mProxy; } + void updateBytesSent(size_t value) + { + mBytesSent += value; + } + + void updateBytesDestroyed(size_t value) + { + mBytesDestroyed += value; + } + + void updateMessagesSent(size_t value) + { + mMessagesCreated += value; + } + + void updateMessagesDestroyed(size_t value) + { + mMessagesDestroyed += value; + } + + size_t bytesSent() + { + return mBytesSent; + } + + size_t bytesDestroyed() + { + return mBytesDestroyed; + } + + size_t messagesCreated() + { + return mMessagesCreated; + } + + size_t messagesDestroyed() + { + return mMessagesDestroyed; + } + private: FairMQDeviceProxy mProxy; Messages mMessages; + size_t mBytesSent = 0; + size_t mBytesDestroyed = 0; + size_t mMessagesCreated = 0; + size_t mMessagesDestroyed = 0; + size_t mRateLimit = 0; }; -} // namespace framework -} // namespace o2 -#endif // FRAMEWORK_ARROWCONTEXT_H +} // namespace o2::framework +#endif // O2_FRAMEWORK_ARROWCONTEXT_H_ diff --git a/Framework/Core/include/Framework/BasicOps.h b/Framework/Core/include/Framework/BasicOps.h index 29ba6ad29907e..f6bbc0ebe4f08 100644 --- a/Framework/Core/include/Framework/BasicOps.h +++ b/Framework/Core/include/Framework/BasicOps.h @@ -26,9 +26,16 @@ enum BasicOp : unsigned int { Equal, NotEqual, Power, + Sqrt, Exp, Log, Log10, + Sin, + Cos, + Tan, + Asin, + Acos, + Atan, Abs }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/BoostOptionsRetriever.h b/Framework/Core/include/Framework/BoostOptionsRetriever.h index 6d33627e8bdc2..148d5c4192b20 100644 --- a/Framework/Core/include/Framework/BoostOptionsRetriever.h +++ b/Framework/Core/include/Framework/BoostOptionsRetriever.h @@ -14,9 +14,17 @@ #include "Framework/ParamRetriever.h" #include <boost/property_tree/ptree_fwd.hpp> -#include <boost/program_options/options_description.hpp> +#include <memory> #include <vector> +namespace boost +{ +namespace program_options +{ +class options_description; +} +} // namespace boost + namespace o2::framework { @@ -32,7 +40,7 @@ class BoostOptionsRetriever : public ParamRetriever boost::property_tree::ptree& provenance) override; private: - boost::program_options::options_description mDescription; + std::unique_ptr<boost::program_options::options_description> mDescription; int mArgc; char** mArgv; bool mIgnoreUnknown; diff --git a/Framework/Core/include/Framework/CallbackRegistry.h b/Framework/Core/include/Framework/CallbackRegistry.h index 76a79e78dc3de..1b72025d9a5f2 100644 --- a/Framework/Core/include/Framework/CallbackRegistry.h +++ b/Framework/Core/include/Framework/CallbackRegistry.h @@ -16,8 +16,8 @@ /// @brief A generic registry for callbacks #include "Framework/TypeTraits.h" +#include "Framework/RuntimeError.h" #include <tuple> -#include <stdexcept> // runtime_error #include <utility> // declval namespace o2 @@ -101,7 +101,7 @@ class CallbackRegistry template <std::size_t pos, typename U, typename F> typename std::enable_if<has_matching_callback<U, F>::value == false>::type setAt(F&& cb) { - throw std::runtime_error("mismatch in function substitution at position " + std::to_string(pos)); + throw runtime_error_f("mismatch in function substitution at position %d", pos); } // exec callback of specified id diff --git a/Framework/Core/include/Framework/CallbackService.h b/Framework/Core/include/Framework/CallbackService.h index 004a660d830aa..a44ba60977484 100644 --- a/Framework/Core/include/Framework/CallbackService.h +++ b/Framework/Core/include/Framework/CallbackService.h @@ -11,6 +11,7 @@ #define FRAMEWORK_CALLBACKSERVICE_H #include "CallbackRegistry.h" +#include "Framework/ServiceHandle.h" #include <tuple> class FairMQRegionInfo; @@ -27,6 +28,9 @@ class EndOfStreamContext; class CallbackService { public: + /// Callbacks are a global service because they will always be + /// invoked by the main thread only. + constexpr static ServiceKind service_kind = ServiceKind::Global; /// the defined processing steps at which a callback can be invoked enum class Id { Start, /**< Invoked before the inner loop is started */ @@ -56,13 +60,6 @@ class CallbackService RegionInfoCallback }; - template <typename T, T... v> - class EnumRegistry - { - constexpr static T values[] = {v...}; - constexpr static std::size_t size = sizeof...(v); - }; - using StartCallback = std::function<void()>; using StopCallback = std::function<void()>; using ResetCallback = std::function<void()>; diff --git a/Framework/Core/include/Framework/ChannelConfigurationPolicy.h b/Framework/Core/include/Framework/ChannelConfigurationPolicy.h index 29387ac05cc8e..785a60bb49bf3 100644 --- a/Framework/Core/include/Framework/ChannelConfigurationPolicy.h +++ b/Framework/Core/include/Framework/ChannelConfigurationPolicy.h @@ -7,20 +7,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. -#ifndef FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H -#define FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H +#ifndef O2_FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H_ +#define O2_FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H_ #include "Framework/ChannelConfigurationPolicyHelpers.h" #include "Framework/ChannelSpec.h" -#include "Framework/DeviceSpec.h" +#include <vector> #include <functional> -namespace o2 -{ -namespace framework +namespace o2::framework { +struct ConfigContext; + /// A ChannelConfigurationPolicy allows the user /// to customise connection method and type /// for a channel created between two devices. @@ -37,10 +37,10 @@ struct ChannelConfigurationPolicy { Helpers::InputChannelModifier modifyInput = nullptr; Helpers::OutputChannelModifier modifyOutput = nullptr; - static std::vector<ChannelConfigurationPolicy> createDefaultPolicies(); + /// Default policies to use, based on the contents of the @configContex content + static std::vector<ChannelConfigurationPolicy> createDefaultPolicies(ConfigContext const& configContext); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H +#endif // O2_FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H_ diff --git a/Framework/Core/include/Framework/ChannelConfigurationPolicyHelpers.h b/Framework/Core/include/Framework/ChannelConfigurationPolicyHelpers.h index 5703cc3e902b1..4f7df3f87f877 100644 --- a/Framework/Core/include/Framework/ChannelConfigurationPolicyHelpers.h +++ b/Framework/Core/include/Framework/ChannelConfigurationPolicyHelpers.h @@ -7,18 +7,23 @@ // In applying this license CERN does 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 FRAMEWORK_CHANNELCONFIGURATIONPOLICYHELPERS_H -#define FRAMEWORK_CHANNELCONFIGURATIONPOLICYHELPERS_H +#ifndef O2_FRAMEWORK_CHANNELCONFIGURATIONPOLICYHELPERS_H_ +#define O2_FRAMEWORK_CHANNELCONFIGURATIONPOLICYHELPERS_H_ #include "Framework/ChannelSpec.h" #include <functional> -namespace o2 -{ -namespace framework +namespace o2::framework { +struct FairMQChannelConfigSpec { + int64_t rateLogging; + int64_t recvBufferSize; + int64_t sendBufferSize; + std::string ipcPrefix; +}; + /// A set of helpers for common ChannelConfigurationPolicy behaviors struct ChannelConfigurationPolicyHelpers { // TODO: currently we allow matching of the policy only based on @@ -41,24 +46,23 @@ struct ChannelConfigurationPolicyHelpers { // Some trivial modifier which can be used by the policy. /// Makes the passed input channel connect and subscribe - static InputChannelModifier subscribeInput; + static InputChannelModifier subscribeInput(FairMQChannelConfigSpec const& spec); /// Makes the passed output channel bind and subscribe - static OutputChannelModifier publishOutput; + static OutputChannelModifier publishOutput(FairMQChannelConfigSpec const& spec); /// Makes the passed input channel connect and pull - static InputChannelModifier pullInput; + static InputChannelModifier pullInput(FairMQChannelConfigSpec const& spec); /// Makes the passed output channel bind and push - static OutputChannelModifier pushOutput; + static OutputChannelModifier pushOutput(FairMQChannelConfigSpec const& spec); /// Makes the passed input channel connect and request - static InputChannelModifier reqInput; + static InputChannelModifier reqInput(FairMQChannelConfigSpec const& spec); /// Makes the passed output channel bind and reply - static OutputChannelModifier replyOutput; + static OutputChannelModifier replyOutput(FairMQChannelConfigSpec const& spec); /// Makes the passed input channel connect and pair - static InputChannelModifier pairInput; + static InputChannelModifier pairInput(FairMQChannelConfigSpec const& spec); /// Makes the passed output channel bind and pair - static OutputChannelModifier pairOutput; + static OutputChannelModifier pairOutput(FairMQChannelConfigSpec const& spec); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H +#endif // O2_FRAMEWORK_CHANNELCONFIGURATIONPOLICY_H_ diff --git a/Framework/Core/include/Framework/ChannelSpec.h b/Framework/Core/include/Framework/ChannelSpec.h index 7b9601722edab..c8d256673aaba 100644 --- a/Framework/Core/include/Framework/ChannelSpec.h +++ b/Framework/Core/include/Framework/ChannelSpec.h @@ -7,14 +7,12 @@ // In applying this license CERN does 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 FRAMEWORK_CHANNELSPEC_H -#define FRAMEWORK_CHANNELSPEC_H +#ifndef O2_FRAMEWORK_CHANNELSPEC_H_ +#define O2_FRAMEWORK_CHANNELSPEC_H_ #include <string> -namespace o2 -{ -namespace framework +namespace o2::framework { /// These map to zeromq connection @@ -50,6 +48,10 @@ struct InputChannelSpec { std::string hostname; unsigned short port; ChannelProtocol protocol = ChannelProtocol::Network; + size_t rateLogging = 0; + size_t recvBufferSize = 1000; + size_t sendBufferSize = 1000; + std::string ipcPrefix = "."; }; /// This describes an output channel. Output channels are semantically @@ -65,9 +67,12 @@ struct OutputChannelSpec { unsigned short port; size_t listeners; ChannelProtocol protocol = ChannelProtocol::Network; + size_t rateLogging = 0; + size_t recvBufferSize = 1000; + size_t sendBufferSize = 1000; + std::string ipcPrefix = "."; }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // FRAMEWORK_CHANNELSPEC_H +#endif // O2_FRAMEWORK_CHANNELSPEC_H_ diff --git a/Framework/Core/include/Framework/CommonDataProcessors.h b/Framework/Core/include/Framework/CommonDataProcessors.h index f07133978e5dc..fbdff88521022 100644 --- a/Framework/Core/include/Framework/CommonDataProcessors.h +++ b/Framework/Core/include/Framework/CommonDataProcessors.h @@ -17,9 +17,7 @@ #include <vector> -namespace o2 -{ -namespace framework +namespace o2::framework { using outputTasks = std::vector<std::pair<uint32_t, std::string>>; using outputObjects = std::vector<std::pair<uint32_t, std::vector<std::string>>>; @@ -28,22 +26,28 @@ using outputObjects = std::vector<std::pair<uint32_t, std::vector<std::string>>> struct CommonDataProcessors { /// Match all inputs of kind ATSK and write them to a ROOT file, /// one root file per originating task. - static DataProcessorSpec getOutputObjSink(outputObjects const& objmap, const outputTasks& tskmap); + static DataProcessorSpec getOutputObjHistSink(outputObjects const& objmap, const outputTasks& tskmap); /// Given the list of @a danglingInputs @return a DataProcessor which does /// a binary dump for all the dangling inputs matching the Timeframe /// lifetime. @a unmatched will be filled with all the InputSpecs which are /// not going to be used by the returned DataProcessorSpec. static DataProcessorSpec getGlobalFileSink(std::vector<InputSpec> const& danglingInputs, std::vector<InputSpec>& unmatched); + /// Given the list of @a danglingInputs @return a DataProcessor which + /// exposes them through a FairMQ channel. + /// @fixme: for now we only support shmem and ipc + /// @fixme: for now only the dangling inputs are forwarded. + static DataProcessorSpec getGlobalFairMQSink(std::vector<InputSpec> const& danglingInputs); + /// writes inputs of kind AOD to file - static DataProcessorSpec getGlobalAODSink(std::vector<InputSpec> const& OutputInputs, - std::vector<bool> const& isdangling); + static DataProcessorSpec getGlobalAODSink(std::shared_ptr<DataOutputDirector> dod, + std::vector<InputSpec> const& outputInputs); + /// @return a dummy DataProcessorSpec which requires all the passed @a InputSpec /// and simply discards them. static DataProcessorSpec getDummySink(std::vector<InputSpec> const& danglingInputs); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif // o2_framework_CommonDataProcessors_H_INCLUDED diff --git a/Framework/Core/include/Framework/CommonMessageBackends.h b/Framework/Core/include/Framework/CommonMessageBackends.h new file mode 100644 index 0000000000000..048e2ef6648b4 --- /dev/null +++ b/Framework/Core/include/Framework/CommonMessageBackends.h @@ -0,0 +1,32 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_COMMONMESSAGEBACKENDS_H_ +#define O2_FRAMEWORK_COMMONMESSAGEBACKENDS_H_ + +#include "Framework/ServiceSpec.h" +#include "Framework/TypeIdHelpers.h" + +namespace o2::framework +{ + +/// A few ServiceSpecs data sending backends +struct CommonMessageBackends { + // Rate limiting service + static ServiceSpec rateLimitingSpec(); + // Create spec for backend used to send Arrow messages + static ServiceSpec arrowBackendSpec(); + static ServiceSpec fairMQBackendSpec(); + static ServiceSpec stringBackendSpec(); + static ServiceSpec rawBufferBackendSpec(); +}; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_COMMONMESSAGEBACKENDS_H_ diff --git a/Framework/Core/include/Framework/CommonServices.h b/Framework/Core/include/Framework/CommonServices.h index 25b33dd0dca32..6c26583efedd8 100644 --- a/Framework/Core/include/Framework/CommonServices.h +++ b/Framework/Core/include/Framework/CommonServices.h @@ -16,6 +16,10 @@ namespace o2::framework { +struct ThreadPool { + int poolSize; +}; + /// A few ServiceSpecs for services we know about and that / are needed by /// everyone. struct CommonServices { @@ -25,7 +29,7 @@ struct CommonServices { static ServiceInit simpleServiceInit() { return [](ServiceRegistry&, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { - return ServiceHandle{TypeIdHelpers::uniqueId<I>(), new T}; + return ServiceHandle{TypeIdHelpers::uniqueId<I>(), new T, ServiceKind::Serial, typeid(T).name()}; }; } @@ -34,11 +38,11 @@ struct CommonServices { static ServiceInit singletonServiceInit() { return [](ServiceRegistry&, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { - return ServiceHandle{TypeIdHelpers::uniqueId<I>(), T::instance()}; + return ServiceHandle{TypeIdHelpers::uniqueId<I>(), T::instance(), ServiceKind::Serial, typeid(T).name()}; }; } - static ServiceConfigure noConfiguration() + static ServiceConfigureCallback noConfiguration() { return [](InitContext&, void* service) -> void* { return service; }; } @@ -54,8 +58,11 @@ struct CommonServices { static ServiceSpec callbacksSpec(); static ServiceSpec timesliceIndex(); static ServiceSpec dataRelayer(); + static ServiceSpec tracingSpec(); + static ServiceSpec threadPool(int numWorkers); + static ServiceSpec dataProcessingStats(); - static std::vector<ServiceSpec> defaultServices(); + static std::vector<ServiceSpec> defaultServices(int numWorkers = 0); static std::vector<ServiceSpec> requiredServices(); }; diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index 9a3bc32f25664..ea892feb1852d 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -50,6 +50,8 @@ struct CompletionPolicyHelpers { } /// Attach a given @a op to a device matching @name. static CompletionPolicy defineByName(std::string const& name, CompletionPolicy::CompletionOp op); + /// Attach a given @a op to a device matching @name, check message of origin @origin is available + static CompletionPolicy defineByNameOrigin(std::string const& name, std::string const& origin, CompletionPolicy::CompletionOp op); /// Get a specific header from the input template <typename T, typename U> static auto getHeader(U const& input) diff --git a/Framework/Core/include/Framework/ConfigParamRegistry.h b/Framework/Core/include/Framework/ConfigParamRegistry.h index f0b96fe8bf75b..9f75b51172730 100644 --- a/Framework/Core/include/Framework/ConfigParamRegistry.h +++ b/Framework/Core/include/Framework/ConfigParamRegistry.h @@ -12,6 +12,7 @@ #include "Framework/ParamRetriever.h" #include "Framework/ConfigParamStore.h" +#include "Framework/Traits.h" #include <boost/property_tree/ptree.hpp> #include <memory> @@ -21,6 +22,43 @@ namespace o2::framework { +namespace +{ +template <typename T> +std::vector<T> extractVector(boost::property_tree::ptree const& tree) +{ + std::vector<T> result(tree.size()); + auto count = 0u; + for (auto& entry : tree) { + result[count++] = entry.second.get_value<T>(); + } + return result; +} + +template <typename T> +o2::framework::Array2D<T> extractMatrix(boost::property_tree::ptree const& tree) +{ + std::vector<T> cache; + uint32_t nrows = tree.size(); + uint32_t ncols = 0; + bool first = true; + auto irow = 0u; + for (auto& row : tree) { + if (first) { + ncols = row.second.size(); + first = false; + } + auto icol = 0u; + for (auto& entry : row.second) { + cache.push_back(entry.second.get_value<T>()); + ++icol; + } + ++irow; + } + return o2::framework::Array2D<T>{cache, nrows, ncols}; +} +} // namespace + class ConfigParamStore; /// This provides unified access to the parameters specified in the workflow @@ -63,6 +101,10 @@ class ConfigParamRegistry return mStore->store().get<std::string>(key); } else if constexpr (std::is_same_v<T, std::string_view>) { return std::string_view{mStore->store().get<std::string>(key)}; + } else if constexpr (is_base_of_template<std::vector, T>::value) { + return extractVector<typename T::value_type>(mStore->store().get_child(key)); + } else if constexpr (is_base_of_template<o2::framework::Array2D, T>::value) { + return extractMatrix<typename T::element_t>(mStore->store().get_child(key)); } else if constexpr (std::is_same_v<T, boost::property_tree::ptree>) { return mStore->store().get_child(key); } else if constexpr (std::is_constructible_v<T, boost::property_tree::ptree>) { diff --git a/Framework/Core/include/Framework/ConfigParamStore.h b/Framework/Core/include/Framework/ConfigParamStore.h index ff775818af844..111725b818432 100644 --- a/Framework/Core/include/Framework/ConfigParamStore.h +++ b/Framework/Core/include/Framework/ConfigParamStore.h @@ -37,6 +37,12 @@ class ConfigParamStore boost::property_tree::ptree& store() { return *mStore; }; boost::property_tree::ptree& provenanceTree() { return *mProvenance; }; + /// Get the specs + std::vector<ConfigParamSpec> const& specs() const + { + return mSpecs; + } + /// Activate the next store void activate(); diff --git a/Framework/Core/include/Framework/ConfigParamsHelper.h b/Framework/Core/include/Framework/ConfigParamsHelper.h index 54c2c28e8cf0a..36c5463e0857e 100644 --- a/Framework/Core/include/Framework/ConfigParamsHelper.h +++ b/Framework/Core/include/Framework/ConfigParamsHelper.h @@ -18,9 +18,7 @@ #include <string> #include <type_traits> -namespace o2 -{ -namespace framework +namespace o2::framework { using options_description = boost::program_options::options_description; @@ -36,7 +34,7 @@ struct ConfigParamsHelper { /// all options which are found in the vetos are skipped static bool dpl2BoostOptions(const std::vector<ConfigParamSpec>& spec, options_description& options, - options_description vetos = options_description()); + boost::program_options::options_description vetos = options_description()); /// populate boost program options for a complete workflow template <typename ContainerType> @@ -101,22 +99,44 @@ struct ConfigParamsHelper { { const char* name = spec.name.c_str(); const char* help = spec.help.c_str(); - using Type = typename variant_type<V>::type; - using BoostType = typename std::conditional<V == VariantType::String, std::string, Type>::type; - auto value = boost::program_options::value<BoostType>(); - if (spec.defaultValue.type() != VariantType::Empty) { - // set the default value if provided in the config spec - value = value->default_value(spec.defaultValue.get<Type>()); - } - if (V == VariantType::Bool) { - // for bool values we also support the zero_token option to make - // the option usable as a single switch - value = value->zero_tokens(); + + if constexpr (V == VariantType::Int || + V == VariantType::Int64 || + V == VariantType::Float || + V == VariantType::Double || + V == VariantType::Bool) { + using Type = typename variant_type<V>::type; + using BoostType = typename std::conditional<V == VariantType::String, std::string, Type>::type; + auto value = boost::program_options::value<BoostType>(); + value = value->default_value(spec.defaultValue.get<BoostType>()); + if constexpr (V == VariantType::Bool) { + // for bool values we also support the zero_token option to make + // the option usable as a single switch + value = value->zero_tokens(); + } + options.add_options()(name, value, help); + } else if constexpr (V == VariantType::ArrayInt || + V == VariantType::ArrayFloat || + V == VariantType::ArrayDouble || + V == VariantType::ArrayBool || + V == VariantType::ArrayString || + V == VariantType::MatrixInt || + V == VariantType::MatrixFloat || + V == VariantType::MatrixDouble) { + auto value = boost::program_options::value<std::string>(); + value = value->default_value(spec.defaultValue.asString()); + if constexpr (V != VariantType::String) { + value = value->multitoken(); + } + options.add_options()(name, value, help); + } else { + using Type = typename variant_type<V>::type; + using BoostType = typename std::conditional<V == VariantType::String, std::string, Type>::type; + auto value = boost::program_options::value<BoostType>(); + options.add_options()(name, value, help); } - options.add_options()(name, value, help); } }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif // FRAMEWORK_CONFIGPARAMSHELPER_H diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h new file mode 100644 index 0000000000000..118e7afa99d93 --- /dev/null +++ b/Framework/Core/include/Framework/Configurable.h @@ -0,0 +1,79 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_CONFIGURABLE_H_ +#define O2_FRAMEWORK_CONFIGURABLE_H_ +#include <string> +namespace o2::framework +{ +template <typename T> +struct ConfigurableBase { + ConfigurableBase(std::string const& name, T&& defaultValue, std::string const& help) + : name(name), value{std::move(defaultValue)}, help(help) + { + } + using type = T; + std::string name; + T value; + std::string help; +}; + +template <typename T> +struct ConfigurablePolicyConst : ConfigurableBase<T> { + ConfigurablePolicyConst(std::string const& name, T&& defaultValue, std::string const& help) + : ConfigurableBase<T>{name, std::forward<T>(defaultValue), help} + { + } + operator T() + { + return this->value; + } + T const* operator->() const + { + return &this->value; + } +}; + +template <typename T> +struct ConfigurablePolicyMutable : ConfigurableBase<T> { + ConfigurablePolicyMutable(std::string const& name, T&& defaultValue, std::string const& help) + : ConfigurableBase<T>{name, std::forward<T>(defaultValue), help} + { + } + operator T() + { + return this->value; + } + T* operator->() + { + return &this->value; + } +}; + +/// This helper allows you to create a configurable option associated to a task. +/// Internally it will be bound to a ConfigParamSpec. +template <typename T, typename IP = ConfigurablePolicyConst<T>> +struct Configurable : IP { + Configurable(std::string const& name, T&& defaultValue, std::string const& help) + : IP{name, std::forward<T>(defaultValue), help} + { + } +}; + +template <typename T> +using MutableConfigurable = Configurable<T, ConfigurablePolicyMutable<T>>; + +template <typename T, typename IP> +std::ostream& operator<<(std::ostream& os, Configurable<T, IP> const& c) +{ + os << c.value; + return os; +} +} // namespace o2::framework +#endif // O2_FRAMEWORK_CONFIGURABLE_H_ diff --git a/Framework/Core/include/Framework/ContextRegistry.h b/Framework/Core/include/Framework/ContextRegistry.h deleted file mode 100644 index a3e2e3ffd7898..0000000000000 --- a/Framework/Core/include/Framework/ContextRegistry.h +++ /dev/null @@ -1,108 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FRAMEWORK_CONTEXTREGISTRY_H -#define FRAMEWORK_CONTEXTREGISTRY_H - -#include "Framework/TypeIdHelpers.h" - -#include <typeinfo> -#include <typeindex> -#include <type_traits> -#include <string> -#include <stdexcept> -#include <vector> -#include <utility> -#include <array> - -namespace o2 -{ -namespace framework -{ - -/// @class ContextRegistry -/// Instances are registered by pointer and are not owned by the registry -/// Decouples getting the various contextes from the actual type -/// of context, so that the DataAllocator does not need to know -/// about the various serialization methods. -/// -class ContextRegistry -{ - public: - /// The maximum distance a entry can be from the optimal slot. - constexpr static int MAX_DISTANCE = 8; - /// The number of slots in the hashmap. - constexpr static int MAX_CONTEXT = 32; - /// The mask to use to calculate the initial slot id. - constexpr static int MAX_CONTEXT_MASK = MAX_CONTEXT - 1; - - ContextRegistry(); - - template <typename... Types> - ContextRegistry(Types*... instances) - { - mRegistryKey.fill(0); - mRegistryValue.fill(nullptr); - set(std::forward<Types*>(instances)...); - } - - template <typename T> - T* get() const - { - constexpr auto typeHash = TypeIdHelpers::uniqueId<std::decay_t<T>>(); - return reinterpret_cast<T*>(get(typeHash)); - } - - template <typename T, typename... Types> - void set(T* instance, Types*... more) - { - set(instance); - set(std::forward<Types*>(more)...); - } - - template <typename T> - void set(T* instance) - { - static_assert(std::is_void<T>::value == false, "can not register a void object"); - auto typeHash = TypeIdHelpers::uniqueId<std::decay_t<T>>(); - set(reinterpret_cast<void*>(instance), typeHash); - } - - void* get(uint32_t typeHash) const - { - auto id = typeHash & MAX_CONTEXT_MASK; - for (uint8_t i = 0; i < MAX_DISTANCE; ++i) { - if (mRegistryKey[i + id] == typeHash) { - return mRegistryValue[i + id]; - } - } - return nullptr; - } - - void set(void* instance, uint32_t typeHash) - { - auto id = typeHash & MAX_CONTEXT_MASK; - for (uint8_t i = 0; i < MAX_DISTANCE; ++i) { - if (mRegistryValue[i + id] == nullptr) { - mRegistryKey[i + id] = typeHash; - mRegistryValue[i + id] = instance; - return; - } - } - } - - private: - std::array<uint32_t, MAX_CONTEXT + MAX_DISTANCE> mRegistryKey; - std::array<void*, MAX_CONTEXT + MAX_DISTANCE> mRegistryValue; -}; - -} // namespace framework -} // namespace o2 -#endif // FRAMEWORK_CONTEXTREGISTRY_H diff --git a/Framework/Core/include/Framework/ControlService.h b/Framework/Core/include/Framework/ControlService.h index 492bc4071bd04..0fcfdcc696b42 100644 --- a/Framework/Core/include/Framework/ControlService.h +++ b/Framework/Core/include/Framework/ControlService.h @@ -10,6 +10,8 @@ #ifndef O2_FRAMEWORK_CONTROLSERVICE_H_ #define O2_FRAMEWORK_CONTROLSERVICE_H_ +#include "Framework/ServiceHandle.h" + namespace o2::framework { @@ -25,9 +27,12 @@ enum struct QuitRequest { /// A service that data processors can use to talk to control and ask for their /// own state change or others. +/// A ControlService is requried to be a ServiceKind::Global kind of service. class ControlService { public: + constexpr static ServiceKind service_kind = ServiceKind::Global; + /// Compatibility with old API. void readyToQuit(bool all) { this->readyToQuit(all ? QuitRequest::All : QuitRequest::Me); } /// Signal control that we are potentially ready to quit some / all diff --git a/Framework/Core/include/Framework/DanglingContext.h b/Framework/Core/include/Framework/DanglingContext.h new file mode 100644 index 0000000000000..7b02baeb40aa5 --- /dev/null +++ b/Framework/Core/include/Framework/DanglingContext.h @@ -0,0 +1,33 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DANGLINGCONTEXT_H_ +#define O2_FRAMEWORK_DANGLINGCONTEXT_H_ + +#include "Framework/ServiceRegistry.h" + +namespace o2::framework +{ + +class DanglingContext +{ + public: + DanglingContext(ServiceRegistry& services) + : mServices(services) + { + } + + ServiceRegistry& services() { return mServices; } + + ServiceRegistry& mServices; +}; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_DANGLINGCONTEXT_H_ diff --git a/Framework/Core/include/Framework/DataAllocator.h b/Framework/Core/include/Framework/DataAllocator.h index 3f838289ce2a3..22f08b1f1ed9b 100644 --- a/Framework/Core/include/Framework/DataAllocator.h +++ b/Framework/Core/include/Framework/DataAllocator.h @@ -10,7 +10,6 @@ #ifndef FRAMEWORK_DATAALLOCATOR_H #define FRAMEWORK_DATAALLOCATOR_H -#include "Framework/ContextRegistry.h" #include "Framework/MessageContext.h" #include "Framework/StringContext.h" #include "Framework/RawBufferContext.h" @@ -25,6 +24,8 @@ #include "Framework/Traits.h" #include "Framework/SerializationMethods.h" #include "Framework/CheckTypes.h" +#include "Framework/ServiceRegistry.h" +#include "Framework/RuntimeError.h" #include "Headers/DataHeader.h" #include <TClass.h> @@ -57,7 +58,7 @@ namespace o2 { namespace framework { -class ContextRegistry; +class ServiceRegistry; #define ERROR_STRING \ "data type T not supported by API, " \ @@ -81,7 +82,7 @@ class DataAllocator using SubSpecificationType = o2::header::DataHeader::SubSpecificationType; DataAllocator(TimingInfo* timingInfo, - ContextRegistry* contextes, + ServiceRegistry* contextes, const AllowedOutputRoutes& routes); DataChunk& newChunk(const Output&, size_t); @@ -104,20 +105,20 @@ class DataAllocator // has a root dictionary, so non-serialized transmission is preferred using ValueType = typename T::value_type; std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - auto context = mContextRegistry->get<MessageContext>(); + auto& context = mRegistry->get<MessageContext>(); // Note: initial payload size is 0 and will be set by the context before sending FairMQMessagePtr headerMessage = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodNone, 0); - return context->add<MessageContext::VectorObject<ValueType>>(std::move(headerMessage), channel, 0, std::forward<Args>(args)...).get(); + return context.add<MessageContext::VectorObject<ValueType>>(std::move(headerMessage), channel, 0, std::forward<Args>(args)...).get(); } else if constexpr (has_root_dictionary<T>::value == true && is_messageable<T>::value == false) { // Extended support for types implementing the Root ClassDef interface, both TObject // derived types and others std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - auto context = mContextRegistry->get<MessageContext>(); + auto& context = mRegistry->get<MessageContext>(); // Note: initial payload size is 0 and will be set by the context before sending FairMQMessagePtr headerMessage = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodROOT, 0); - return context->add<MessageContext::RootSerializedObject<T>>(std::move(headerMessage), channel, std::forward<Args>(args)...).get(); + return context.add<MessageContext::RootSerializedObject<T>>(std::move(headerMessage), channel, std::forward<Args>(args)...).get(); } else if constexpr (std::is_base_of_v<std::string, T>) { std::string* s = new std::string(args...); adopt(spec, s); @@ -133,7 +134,6 @@ class DataAllocator void* t2tr = nullptr; call_if_defined<struct TreeToTable>([&](auto* p) { auto t2t = new std::decay_t<decltype(*p)>(args...); - t2t->addAllColumns(); adopt(spec, t2t); t2tr = t2t; }); @@ -155,10 +155,10 @@ class DataAllocator auto [nElements] = std::make_tuple(args...); auto size = nElements * sizeof(T); std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - auto context = mContextRegistry->get<MessageContext>(); + auto& context = mRegistry->get<MessageContext>(); FairMQMessagePtr headerMessage = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodNone, size); - return context->add<MessageContext::SpanObject<T>>(std::move(headerMessage), channel, 0, nElements).get(); + return context.add<MessageContext::SpanObject<T>>(std::move(headerMessage), channel, 0, nElements).get(); } } else if constexpr (std::is_same_v<FirstArg, std::shared_ptr<arrow::Schema>>) { if constexpr (std::is_base_of_v<arrow::ipc::RecordBatchWriter, T>) { @@ -234,7 +234,7 @@ class DataAllocator delete tmpPtr; }; - mContextRegistry->get<RawBufferContext>()->addRawBuffer(std::move(header), std::move(payload), std::move(channel), std::move(lambdaSerialize), std::move(lambdaDestructor)); + mRegistry->get<RawBufferContext>().addRawBuffer(std::move(header), std::move(payload), std::move(channel), std::move(lambdaSerialize), std::move(lambdaDestructor)); } /// Send a snapshot of an object, depending on the object type it is serialized before. @@ -259,7 +259,7 @@ class DataAllocator template <typename T> void snapshot(const Output& spec, T const& object) { - auto proxy = mContextRegistry->get<MessageContext>()->proxy(); + auto proxy = mRegistry->get<MessageContext>().proxy(); FairMQMessagePtr payloadMessage; auto serializationType = o2::header::gSerializationMethodNone; if constexpr (is_messageable<T>::value == true) { @@ -327,13 +327,13 @@ class DataAllocator cl = TClass::GetClass(reinterpret_cast<const char*>(object.getHint())); } if (has_root_dictionary<WrappedType>::value == false && cl == nullptr) { - std::string msg("ROOT serialization not supported, dictionary not found for type "); if (std::is_same<typename T::hint_type, const char>::value) { - msg += reinterpret_cast<const char*>(object.getHint()); + throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", + reinterpret_cast<const char*>(object.getHint())); } else { - msg += typeid(WrappedType).name(); + throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", + typeid(WrappedType).name()); } - throw std::runtime_error(msg); } TMessageSerializer().Serialize(*payloadMessage, &object(), cl); } else { @@ -382,8 +382,8 @@ class DataAllocator o2::pmr::FairMQMemoryResource* getMemoryResource(const Output& spec) { std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - auto context = mContextRegistry->get<MessageContext>(); - return *context->proxy().getTransport(channel); + auto& context = mRegistry->get<MessageContext>(); + return *context.proxy().getTransport(channel); } //make a stl (pmr) vector @@ -404,15 +404,15 @@ class DataAllocator // and put it in the queue to be sent at the end of the processing std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - auto context = mContextRegistry->get<MessageContext>(); - FairMQMessagePtr payloadMessage = o2::pmr::getMessage(std::forward<ContainerT>(container), *context->proxy().getTransport(channel)); + auto& context = mRegistry->get<MessageContext>(); + FairMQMessagePtr payloadMessage = o2::pmr::getMessage(std::forward<ContainerT>(container), *context.proxy().getTransport(channel)); FairMQMessagePtr headerMessage = headerMessageFromOutput(spec, channel, // o2::header::gSerializationMethodNone, // payloadMessage->GetSize() // ); - context->add<MessageContext::TrivialObject>(std::move(headerMessage), std::move(payloadMessage), channel); + context.add<MessageContext::TrivialObject>(std::move(headerMessage), std::move(payloadMessage), channel); } /// snapshot object and route to output specified by OutputRef @@ -431,18 +431,18 @@ class DataAllocator o2::header::DataHeader* findMessageHeader(const Output& spec) { - return mContextRegistry->get<MessageContext>()->findMessageHeader(spec); + return mRegistry->get<MessageContext>().findMessageHeader(spec); } o2::header::DataHeader* findMessageHeader(OutputRef&& ref) { - return mContextRegistry->get<MessageContext>()->findMessageHeader(getOutputByBind(std::move(ref))); + return mRegistry->get<MessageContext>().findMessageHeader(getOutputByBind(std::move(ref))); } private: AllowedOutputRoutes mAllowedOutputRoutes; TimingInfo* mTimingInfo; - ContextRegistry* mContextRegistry; + ServiceRegistry* mRegistry; std::string const& matchDataHeader(const Output& spec, size_t timeframeId); FairMQMessagePtr headerMessageFromOutput(Output const& spec, // diff --git a/Framework/Core/include/Framework/DataDescriptorMatcher.h b/Framework/Core/include/Framework/DataDescriptorMatcher.h index 524ea084e60e5..829a627ba0254 100644 --- a/Framework/Core/include/Framework/DataDescriptorMatcher.h +++ b/Framework/Core/include/Framework/DataDescriptorMatcher.h @@ -12,6 +12,7 @@ #include "Framework/ConcreteDataMatcher.h" #include "Framework/DataProcessingHeader.h" +#include "Framework/RuntimeError.h" #include "Headers/DataHeader.h" #include "Headers/Stack.h" @@ -19,14 +20,12 @@ #include <cstdint> #include <iosfwd> #include <string> +#if !defined(__CLING__) && !defined(__ROOTCLING__) #include <variant> +#endif #include <vector> -namespace o2 -{ -namespace framework -{ -namespace data_matcher +namespace o2::framework::data_matcher { /// Marks an empty item in the context @@ -47,7 +46,12 @@ struct ContextRef { /// We do not have any float in the value, because AFAICT there is no need for /// it in the O2 DataHeader, however we could add it later on. struct ContextElement { + +#if !defined(__CLING__) && !defined(__ROOTCLING__) using Value = std::variant<uint32_t, uint64_t, std::string, None>; +#else + using Value = None; +#endif std::string label; /// The name of the variable contained in this element. Value value = None{}; /// The actual contents of the element. }; @@ -114,11 +118,17 @@ class ValueHolder template <typename VISITOR> decltype(auto) visit(VISITOR visitor) const { +#if !defined(__CLING__) && !defined(__ROOTCLING__) return std::visit(visitor, mValue); +#else + return ContextRef{}; +#endif } protected: +#if !defined(__CLING__) && !defined(__ROOTCLING__) std::variant<T, ContextRef> mValue; +#endif }; /// Something which can be matched against a header::DataOrigin @@ -219,8 +229,12 @@ struct DescriptorMatcherTrait<header::DataHeader::SubSpecificationType> { using Matcher = SubSpecificationTypeValueMatcher; }; +#if !defined(__CLING__) && !defined(__ROOTCLING__) class DataDescriptorMatcher; using Node = std::variant<OriginValueMatcher, DescriptionValueMatcher, SubSpecificationTypeValueMatcher, std::unique_ptr<DataDescriptorMatcher>, ConstantValueMatcher, StartTimeValueMatcher>; +#else +using Node = ConstantValueMatcher; +#endif // A matcher for a given O2 Data Model descriptor. We use a variant to hold // the different kind of matchers so that we can have a hierarchy or @@ -275,9 +289,7 @@ class DataDescriptorMatcher Node mRight; }; -} // namespace data_matcher -} // namespace framework -} // namespace o2 +} // namespace o2::framework::data_matcher // This is to work around CLING issues when parsing // GCC 7.3.0 std::variant implementation as described by: diff --git a/Framework/Core/include/Framework/DataDescriptorMatcher.inc b/Framework/Core/include/Framework/DataDescriptorMatcher.inc index bcd4f5630bd8d..8e1b86d45d585 100644 --- a/Framework/Core/include/Framework/DataDescriptorMatcher.inc +++ b/Framework/Core/include/Framework/DataDescriptorMatcher.inc @@ -81,7 +81,7 @@ inline OriginValueMatcher::OriginValueMatcher(std::string const& s) : ValueHolder{s} { if (s.size() > header::gSizeDataOriginString) { - throw std::runtime_error("Header DataDescription too long"); + throw runtime_error("Header DataDescription too long"); } } @@ -100,7 +100,7 @@ inline DescriptionValueMatcher::DescriptionValueMatcher(std::string const& s) : ValueHolder{s} { if (s.size() > header::gSizeDataDescriptionString) { - throw std::runtime_error("Header DataDescription too long"); + throw runtime_error("Header DataDescription too long"); } } diff --git a/Framework/Core/include/Framework/DataInputDirector.h b/Framework/Core/include/Framework/DataInputDirector.h index 36634a3b4a40f..1a7a3e5af5ee1 100644 --- a/Framework/Core/include/Framework/DataInputDirector.h +++ b/Framework/Core/include/Framework/DataInputDirector.h @@ -18,11 +18,21 @@ #include <regex> #include "rapidjson/fwd.h" -namespace o2 +namespace o2::framework { -namespace framework -{ -using namespace rapidjson; + +struct FileNameHolder { + std::string fileName; + int numberOfTimeFrames = 0; + std::vector<uint64_t> listOfTimeFrameNumbers; + std::vector<std::string> listOfTimeFrameKeys; +}; +FileNameHolder* makeFileNameHolder(std::string fileName); + +struct FileAndFolder { + TFile* file = nullptr; + std::string folderName = ""; +}; struct DataInputDescriptor { /// Holds information concerning the reading of an aod table. @@ -34,6 +44,7 @@ struct DataInputDescriptor { std::unique_ptr<data_matcher::DataDescriptorMatcher> matcher; DataInputDescriptor() = default; + DataInputDescriptor(bool alienSupport); void printOut(); @@ -43,29 +54,36 @@ struct DataInputDescriptor { void setFilenamesRegex(std::string fn) { mFilenameRegex = fn; } void setFilenamesRegex(std::string* fnptr) { mFilenameRegexPtr = fnptr; } - void setDefaultInputfiles(std::vector<std::string>* difnptr) { mdefaultFilenamesPtr = difnptr; } + void setDefaultInputfiles(std::vector<FileNameHolder*>* difnptr) { mdefaultFilenamesPtr = difnptr; } - void addFilename(std::string fn); + void addFileNameHolder(FileNameHolder* fn); int fillInputfiles(); + bool setFile(int counter); // getters std::string getInputfilesFilename(); std::string getFilenamesRegexString(); std::regex getFilenamesRegex(); int getNumberInputfiles() { return mfilenames.size(); } + int getNumberTimeFrames() { return mtotalNumberTimeFrames; } + + uint64_t getTimeFrameNumber(int counter, int numTF); + FileAndFolder getFileFolder(int counter, int numTF); - TFile* getInputFile(int counter); void closeInputFile(); - std::string getInputFilename(int counter); + bool isAlienSupportOn() { return mAlienSupport; } private: std::string minputfilesFile = ""; std::string* minputfilesFilePtr = nullptr; std::string mFilenameRegex = ""; std::string* mFilenameRegexPtr = nullptr; - std::vector<std::string> mfilenames; - std::vector<std::string>* mdefaultFilenamesPtr = nullptr; + std::vector<FileNameHolder*> mfilenames; + std::vector<FileNameHolder*>* mdefaultFilenamesPtr = nullptr; TFile* mcurrentFile = nullptr; + bool mAlienSupport = false; + + int mtotalNumberTimeFrames = 0; }; struct DataInputDirector { @@ -75,6 +93,7 @@ struct DataInputDirector { DataInputDirector(); DataInputDirector(std::string inputFile); + DataInputDirector(std::vector<std::string> inputFiles); void reset(); void createDefaultDataInputDescriptor(); @@ -89,27 +108,29 @@ struct DataInputDirector { // getters DataInputDescriptor* getDataInputDescriptor(header::DataHeader dh); - std::unique_ptr<TTreeReader> getTreeReader(header::DataHeader dh, int counter, std::string treeName); - std::string getInputFilename(header::DataHeader dh, int counter); - TTree* getDataTree(header::DataHeader dh, int counter); int getNumberInputDescriptors() { return mdataInputDescriptors.size(); } + std::unique_ptr<TTreeReader> getTreeReader(header::DataHeader dh, int counter, int numTF, std::string treeName); + TTree* getDataTree(header::DataHeader dh, int counter, int numTF); + uint64_t getTimeFrameNumber(header::DataHeader dh, int counter, int numTF); + FileAndFolder getFileFolder(header::DataHeader dh, int counter, int numTF); + private: std::string minputfilesFile; std::string* const minputfilesFilePtr = &minputfilesFile; std::string mFilenameRegex; std::string* const mFilenameRegexPtr = &mFilenameRegex; DataInputDescriptor* mdefaultDataInputDescriptor = nullptr; - std::vector<std::string> mdefaultInputFiles; + std::vector<FileNameHolder*> mdefaultInputFiles; std::vector<DataInputDescriptor*> mdataInputDescriptors; - bool mdebugmode = false; + bool mDebugMode = false; + bool mAlienSupport = false; - bool readJsonDocument(Document* doc); + bool readJsonDocument(rapidjson::Document* doc); bool isValid(); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif // o2_framework_DataInputDirector_H_INCLUDED diff --git a/Framework/Core/include/Framework/DataOutputDirector.h b/Framework/Core/include/Framework/DataOutputDirector.h index 3d819875d9c38..edf3cdb0f082f 100644 --- a/Framework/Core/include/Framework/DataOutputDirector.h +++ b/Framework/Core/include/Framework/DataOutputDirector.h @@ -16,12 +16,11 @@ #include "Framework/DataDescriptorMatcher.h" #include "Framework/DataSpecUtils.h" #include "Framework/InputSpec.h" +#include "Framework/DataInputDirector.h" #include "rapidjson/fwd.h" -namespace o2 -{ -namespace framework +namespace o2::framework { using namespace rapidjson; @@ -70,13 +69,19 @@ struct DataOutputDirector { std::tuple<std::string, std::string, int> readJson(std::string const& fnjson); std::tuple<std::string, std::string, int> readJsonString(std::string const& stjson); + // read/write private members + int getNumberTimeFramesToMerge() { return mnumberTimeFramesToMerge; } + void setNumberTimeFramesToMerge(int ntfmerge) { mnumberTimeFramesToMerge = ntfmerge > 0 ? ntfmerge : 1; } + std::string getFileMode() { return mfileMode; } + void setFileMode(std::string filemode) { mfileMode = filemode; } + // get matching DataOutputDescriptors std::vector<DataOutputDescriptor*> getDataOutputDescriptors(header::DataHeader dh); std::vector<DataOutputDescriptor*> getDataOutputDescriptors(InputSpec spec); // get the matching TFile - TFile* getDataOutputFile(DataOutputDescriptor* dod, - int ntf, int ntfmerge, std::string filemode); + FileAndFolder getFileFolder(DataOutputDescriptor* dodesc, uint64_t folderNumber); + void closeDataFiles(); void setFilenameBase(std::string dfn); @@ -89,15 +94,15 @@ struct DataOutputDirector { std::vector<DataOutputDescriptor*> mDataOutputDescriptors; std::vector<std::string> mtreeFilenames; std::vector<std::string> mfilenameBases; - std::vector<int> mfileCounts; std::vector<TFile*> mfilePtrs; bool mdebugmode = false; + int mnumberTimeFramesToMerge = 1; + std::string mfileMode = "RECREATE"; std::tuple<std::string, std::string, int> readJsonDocument(Document* doc); const std::tuple<std::string, std::string, int> memptyanswer = std::make_tuple(std::string(""), std::string(""), -1); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif // o2_framework_DataOutputDirector_H_INCLUDED diff --git a/Framework/Core/include/Framework/DataProcessingDevice.h b/Framework/Core/include/Framework/DataProcessingDevice.h index 39b95ab01d0da..89d8dd90d9f20 100644 --- a/Framework/Core/include/Framework/DataProcessingDevice.h +++ b/Framework/Core/include/Framework/DataProcessingDevice.h @@ -12,26 +12,23 @@ #include "Framework/AlgorithmSpec.h" #include "Framework/ConfigParamRegistry.h" -#include "Framework/ContextRegistry.h" #include "Framework/DataAllocator.h" #include "Framework/DataRelayer.h" #include "Framework/DeviceSpec.h" #include "Framework/DataProcessingStats.h" #include "Framework/ExpirationHandler.h" -#include "Framework/MessageContext.h" -#include "Framework/ArrowContext.h" -#include "Framework/StringContext.h" -#include "Framework/RawBufferContext.h" #include "Framework/ServiceRegistry.h" #include "Framework/InputRoute.h" #include "Framework/ForwardRoute.h" #include "Framework/TimingInfo.h" #include "Framework/TerminationPolicy.h" +#include "Framework/Tracing.h" #include <fairmq/FairMQDevice.h> #include <fairmq/FairMQParts.h> #include <memory> +#include <mutex> namespace o2::framework { @@ -39,6 +36,47 @@ namespace o2::framework struct InputChannelInfo; struct DeviceState; +/// Context associated to a given DataProcessor. +/// For the time being everything points to +/// members of the DataProcessingDevice and +/// we make sure that things are serialised by +/// locking a global lock. We can then have per +/// thread instances for what makes sense to have +/// per thread and relax the locks. +class DataProcessingDevice; + +struct DataProcessorContext { + // These are specific of a given context and therefore + // not shared by threads. + bool* wasActive = nullptr; + bool allDone = false; + + // These are pointers to the one owned by the DataProcessingDevice + // but they are fully reentrant / thread safe and therefore can + // be accessed without a lock. + + // FIXME: move stuff here from the list below... ;-) + + // These are pointers to the one owned by the DataProcessingDevice + // and therefore require actual locking + DataProcessingDevice* device = nullptr; + DeviceSpec const* spec = nullptr; + DeviceState* state = nullptr; + + DataRelayer* relayer = nullptr; + ServiceRegistry* registry = nullptr; + std::vector<DataRelayer::RecordAction>* completed = nullptr; + std::vector<ExpirationHandler>* expirationHandlers = nullptr; + TimingInfo* timingInfo = nullptr; + DataAllocator* allocator = nullptr; + AlgorithmSpec::ProcessCallback* statefulProcess = nullptr; + AlgorithmSpec::ProcessCallback* statelessProcess = nullptr; + AlgorithmSpec::ErrorCallback* error = nullptr; + + std::function<void(o2::framework::RuntimeErrorRef e, InputRecord& record)>* errorHandling = nullptr; + int* errorCount = nullptr; +}; + /// A device actually carrying out all the DPL /// Data Processing needs. class DataProcessingDevice : public FairMQDevice @@ -54,43 +92,45 @@ class DataProcessingDevice : public FairMQDevice bool ConditionalRun() final; void SetErrorPolicy(enum TerminationPolicy policy) { mErrorPolicy = policy; } + // Processing functions are now renetrant + static void doRun(DataProcessorContext& context); + static void doPrepare(DataProcessorContext& context); + static void handleData(DataProcessorContext& context, FairMQParts&, InputChannelInfo&); + static bool tryDispatchComputation(DataProcessorContext& context, std::vector<DataRelayer::RecordAction>& completed); + std::vector<DataProcessorContext> mDataProcessorContexes; + protected: - bool doRun(); - bool handleData(FairMQParts&, InputChannelInfo&); - bool tryDispatchComputation(std::vector<DataRelayer::RecordAction>& completed); void error(const char* msg); + void fillContext(DataProcessorContext& context); private: /// The specification used to create the initial state of this device DeviceSpec const& mSpec; /// The current internal state of this device. DeviceState& mState; + AlgorithmSpec::InitCallback mInit; AlgorithmSpec::ProcessCallback mStatefulProcess; AlgorithmSpec::ProcessCallback mStatelessProcess; AlgorithmSpec::ErrorCallback mError; - std::function<void(std::exception& e, InputRecord& record)> mErrorHandling; + std::function<void(RuntimeErrorRef e, InputRecord& record)> mErrorHandling; std::unique_ptr<ConfigParamRegistry> mConfigRegistry; ServiceRegistry& mServiceRegistry; TimingInfo mTimingInfo; - MessageContext mFairMQContext; - StringContext mStringContext; - ArrowContext mDataFrameContext; - RawBufferContext mRawBufferContext; - ContextRegistry mContextRegistry; DataAllocator mAllocator; DataRelayer* mRelayer = nullptr; + /// Expiration handler std::vector<ExpirationHandler> mExpirationHandlers; + /// Completed actions std::vector<DataRelayer::RecordAction> mCompleted; int mErrorCount; - int mProcessingCount; uint64_t mLastSlowMetricSentTimestamp = 0; /// The timestamp of the last time we sent slow metrics uint64_t mLastMetricFlushedTimestamp = 0; /// The timestamp of the last time we actually flushed metrics uint64_t mBeginIterationTimestamp = 0; /// The timestamp of when the current ConditionalRun was started DataProcessingStats mStats; /// Stats about the actual data processing. - int mCurrentBackoff = 0; /// The current exponential backoff value. std::vector<FairMQRegionInfo> mPendingRegionInfos; /// A list of the region infos not yet notified. + std::mutex mRegionInfoMutex; enum TerminationPolicy mErrorPolicy = TerminationPolicy::WAIT; /// What to do when an error arises bool mWasActive = false; /// Whether or not the device was active at last iteration. }; diff --git a/Framework/Core/include/Framework/DataProcessingHeader.h b/Framework/Core/include/Framework/DataProcessingHeader.h index 0627cf8887d6d..313c9598bcbf8 100644 --- a/Framework/Core/include/Framework/DataProcessingHeader.h +++ b/Framework/Core/include/Framework/DataProcessingHeader.h @@ -13,7 +13,6 @@ #include "Headers/DataHeader.h" #include <cstdint> -#include <cstdio> #include <memory> #include <cassert> #include <chrono> diff --git a/Framework/Core/include/Framework/DataProcessingStats.h b/Framework/Core/include/Framework/DataProcessingStats.h index d809a80547b73..b4d9fb5c26210 100644 --- a/Framework/Core/include/Framework/DataProcessingStats.h +++ b/Framework/Core/include/Framework/DataProcessingStats.h @@ -7,34 +7,39 @@ // In applying this license CERN does 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_DataProcessingStats_H_INCLUDED -#define o2_framework_DataProcessingStats_H_INCLUDED +#ifndef O2_FRAMEWORK_DATAPROCESSINGSTATS_H_ +#define O2_FRAMEWORK_DATAPROCESSINGSTATS_H_ #include <cstdint> -namespace o2 +namespace o2::framework { -namespace framework -{ - /// Helper struct to hold statistics about the data processing happening. struct DataProcessingStats { + constexpr static ServiceKind service_kind = ServiceKind::Global; + // If we have more than this, we probably have other issues in any case + constexpr static int MAX_RELAYER_STATES = 4096; // We use this to keep track of the latency of the first message we get for a given input record // and of the last one. struct InputLatency { int minLatency = 0; int maxLatency = 0; }; - int pendingInputs = 0; - int incomplete = 0; - int inputParts = 0; - int lastElapsedTimeMs = 0; - int lastTotalProcessedSize = 0; - InputLatency lastLatency = {}; - std::vector<int> relayerState; + std::atomic<int> pendingInputs = 0; + std::atomic<int> incomplete = 0; + std::atomic<int> inputParts = 0; + std::atomic<int> lastElapsedTimeMs = 0; + std::atomic<int> lastTotalProcessedSize = 0; + + std::atomic<uint64_t> lastSlowMetricSentTimestamp = 0; /// The timestamp of the last time we sent slow metrics + std::atomic<uint64_t> lastMetricFlushedTimestamp = 0; /// The timestamp of the last time we actually flushed metrics + std::atomic<uint64_t> beginIterationTimestamp = 0; /// The timestamp of when the current ConditionalRun was started + InputLatency lastLatency = {0, 0}; + + std::atomic<int> relayerState[MAX_RELAYER_STATES]; + std::atomic<size_t> statesSize; }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // o2_framework_DataProcessingStats_H_INCLUDED +#endif // O2_FRAMEWORK_DATAPROCESSINGSTATS_H_ diff --git a/Framework/Core/include/Framework/DataProcessor.h b/Framework/Core/include/Framework/DataProcessor.h index e4147aaef6c52..8aa83108c5851 100644 --- a/Framework/Core/include/Framework/DataProcessor.h +++ b/Framework/Core/include/Framework/DataProcessor.h @@ -7,33 +7,31 @@ // In applying this license CERN does 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 FRAMEWORK_DATAPROCESSOR_H -#define FRAMEWORK_DATAPROCESSOR_H +#ifndef O2_FRAMEWORK_DATAPROCESSOR_H_ +#define O2_FRAMEWORK_DATAPROCESSOR_H_ class FairMQDevice; class FairMQParts; -namespace o2 -{ -namespace framework +namespace o2::framework { class MessageContext; class StringContext; class ArrowContext; class RawBufferContext; +class ServiceRegistry; /// Helper class to send messages from a contex at the end /// of a computation. struct DataProcessor { - static void doSend(FairMQDevice&, MessageContext&); - static void doSend(FairMQDevice&, StringContext&); - static void doSend(FairMQDevice&, ArrowContext&); - static void doSend(FairMQDevice&, RawBufferContext&); + static void doSend(FairMQDevice&, MessageContext&, ServiceRegistry&); + static void doSend(FairMQDevice&, StringContext&, ServiceRegistry&); + static void doSend(FairMQDevice&, ArrowContext&, ServiceRegistry&); + static void doSend(FairMQDevice&, RawBufferContext&, ServiceRegistry&); static void doSend(FairMQDevice&, FairMQParts&&, const char*, unsigned int); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // FRAMEWORK_DATAPROCESSOR_H +#endif // O2_FRAMEWORK_DATAPROCESSOR_H_ diff --git a/Framework/Core/src/DataProcessorInfo.h b/Framework/Core/include/Framework/DataProcessorInfo.h similarity index 100% rename from Framework/Core/src/DataProcessorInfo.h rename to Framework/Core/include/Framework/DataProcessorInfo.h diff --git a/Framework/Core/include/Framework/DataProcessorSpec.h b/Framework/Core/include/Framework/DataProcessorSpec.h index 4cdae26b3f7a8..c60346dc5dc4c 100644 --- a/Framework/Core/include/Framework/DataProcessorSpec.h +++ b/Framework/Core/include/Framework/DataProcessorSpec.h @@ -14,6 +14,7 @@ #include "Framework/ConfigParamSpec.h" #include "Framework/DataProcessorLabel.h" #include "Framework/DataRef.h" +#include "Framework/DataAllocator.h" #include "Framework/InputSpec.h" #include "Framework/OutputSpec.h" #include "Framework/CommonServices.h" diff --git a/Framework/Core/include/Framework/DataRefUtils.h b/Framework/Core/include/Framework/DataRefUtils.h index f6fd9a1bfa4e9..5640cde2d2d50 100644 --- a/Framework/Core/include/Framework/DataRefUtils.h +++ b/Framework/Core/include/Framework/DataRefUtils.h @@ -17,11 +17,10 @@ #include "Framework/TypeTraits.h" #include "Headers/DataHeader.h" #include "Framework/CheckTypes.h" +#include "Framework/RuntimeError.h" #include <gsl/gsl> -#include <stdexcept> -#include <sstream> #include <type_traits> namespace o2::framework @@ -40,10 +39,10 @@ struct DataRefUtils { using DataHeader = o2::header::DataHeader; auto header = o2::header::get<const DataHeader*>(ref.header); if (header->payloadSerializationMethod != o2::header::gSerializationMethodNone) { - throw std::runtime_error("Attempt to extract a POD from a wrong message kind"); + throw runtime_error("Attempt to extract a POD from a wrong message kind"); } if ((header->payloadSize % sizeof(T)) != 0) { - throw std::runtime_error("Cannot extract POD from message as size do not match"); + throw runtime_error("Cannot extract POD from message as size do not match"); } //FIXME: provide a const collection return gsl::span<T>(reinterpret_cast<T*>(const_cast<char*>(ref.payload)), header->payloadSize / sizeof(T)); @@ -61,7 +60,7 @@ struct DataRefUtils { using DataHeader = o2::header::DataHeader; auto header = o2::header::get<const DataHeader*>(ref.header); if (header->payloadSerializationMethod != o2::header::gSerializationMethodROOT) { - throw std::runtime_error("Attempt to extract a TMessage from non-ROOT serialised message"); + throw runtime_error("Attempt to extract a TMessage from non-ROOT serialised message"); } typename RSS::FairTMessage ftm(const_cast<char*>(ref.payload), header->payloadSize); @@ -72,11 +71,8 @@ struct DataRefUtils { auto* object = ftm.ReadObjectAny(storedClass); if (object == nullptr) { - std::ostringstream ss; - ss << "Failed to read object with name " - << (storedClass != nullptr ? storedClass->GetName() : "<unknown>") - << " from message using ROOT serialization."; - throw std::runtime_error(ss.str()); + throw runtime_error_f("Failed to read object with name %s from message using ROOT serialization.", + (storedClass != nullptr ? storedClass->GetName() : "<unknown>")); } if constexpr (std::is_base_of<typename RSS::TObject, T>::value) { // compile time switch @@ -106,13 +102,9 @@ struct DataRefUtils { (*delfunc)(object); } - std::ostringstream ss; - ss << "Attempting to extract a " - << (requestedClass != nullptr ? requestedClass->GetName() : "<unknown>") - << " but a " - << (storedClass != nullptr ? storedClass->GetName() : "<unknown>") - << " is actually stored which cannot be casted to the requested one."; - throw std::runtime_error(ss.str()); + throw runtime_error_f("Attempting to extract a %s but a %s is actually stored which cannot be casted to the requested one.", + (requestedClass != nullptr ? requestedClass->GetName() : "<unknown>"), + (storedClass != nullptr ? storedClass->GetName() : "<unknown>")); } // collections in ROOT can be non-owning or owning and the proper cleanup depends on // this flag. Be it a bug or a feature in ROOT, but the owning flag of the extracted @@ -139,24 +131,17 @@ struct DataRefUtils { auto header = o2::header::get<const DataHeader*>(ref.header); if (header->payloadSerializationMethod != o2::header::gSerializationMethodROOT) { - throw std::runtime_error("Attempt to extract a TMessage from non-ROOT serialised message"); + throw runtime_error("Attempt to extract a TMessage from non-ROOT serialised message"); } auto* cl = RSS::TClass::GetClass(typeid(wrapped)); if (has_root_dictionary<wrapped>::value == false && cl == nullptr) { - throw std::runtime_error("ROOT serialization not supported, dictionary not found for data type"); + throw runtime_error("ROOT serialization not supported, dictionary not found for data type"); } typename RSS::FairTMessage ftm(const_cast<char*>(ref.payload), header->payloadSize); result.reset(static_cast<wrapped*>(ftm.ReadObjectAny(cl))); if (result.get() == nullptr) { - std::ostringstream ss; - ss << "Unable to extract class "; - if (cl == nullptr) { - ss << "<name not available>"; - } else { - ss << cl->GetName(); - } - throw std::runtime_error(ss.str()); + throw runtime_error_f("Unable to extract class %s", cl == nullptr ? "<name not available>" : cl->GetName()); } // workaround for ROOT feature, see above if constexpr (has_root_setowner<T>::value) { @@ -167,7 +152,7 @@ struct DataRefUtils { } } - static unsigned getPayloadSize(const DataRef& ref) + static o2::header::DataHeader::PayloadSizeType getPayloadSize(const DataRef& ref) { using DataHeader = o2::header::DataHeader; auto* header = o2::header::get<const DataHeader*>(ref.header); diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index 051517f6bc390..506213c72f4b8 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -7,8 +7,8 @@ // In applying this license CERN does 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 FRAMEWORK_DATARELAYER_H -#define FRAMEWORK_DATARELAYER_H +#ifndef O2_FRAMEWORK_DATARELAYER_H_ +#define O2_FRAMEWORK_DATARELAYER_H_ #include "Framework/RootSerializationSupport.h" #include "Framework/InputRoute.h" @@ -17,19 +17,20 @@ #include "Framework/CompletionPolicy.h" #include "Framework/MessageSet.h" #include "Framework/TimesliceIndex.h" +#include "Framework/Tracing.h" #include <cstddef> +#include <mutex> #include <vector> class FairMQMessage; -namespace o2 -{ -namespace monitoring +namespace o2::monitoring { class Monitoring; } -namespace framework + +namespace o2::framework { /// Helper struct to hold statistics about the relaying process. @@ -43,6 +44,10 @@ struct DataRelayerStats { class DataRelayer { public: + /// DataRelayer is thread safe because we have a lock around + /// each method and there is no particular order in which + /// methods need to be called. + constexpr static ServiceKind service_kind = ServiceKind::Global; enum RelayChoice { WillRelay, WillNotRelay @@ -84,10 +89,6 @@ class DataRelayer /// DataRelayer they need to be deleted once the processing is concluded. std::vector<MessageSet> getInputsForTimeslice(TimesliceSlot id); - /// Returns the index of the arguments which have to be forwarded to - /// the next processor - const std::vector<int>& forwardingMask(); - /// Returns how many timeslices we can handle in parallel size_t getParallelTimeslices() const; @@ -101,6 +102,10 @@ class DataRelayer void sendContextState(); void publishMetrics(); + /// Get timeslice associated to a given slot. + /// Notice how this avoids exposing the timesliceIndex directly + /// so that we can mutex on it. + TimesliceId getTimesliceForSlot(TimesliceSlot slot); /// Remove all pending messages void clear(); @@ -128,9 +133,9 @@ class DataRelayer static std::vector<std::string> sQueriesMetricsNames; DataRelayerStats mStats; + TracyLockableN(std::recursive_mutex, mMutex, "data relayer mutex"); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif +#endif // O2_FRAMEWORK_DATARELAYER_H_ diff --git a/Framework/Core/include/Framework/DataSampling.h b/Framework/Core/include/Framework/DataSampling.h deleted file mode 100644 index 8a6e097c5bfe8..0000000000000 --- a/Framework/Core/include/Framework/DataSampling.h +++ /dev/null @@ -1,115 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 FRAMEWORK_DATASAMPLING_H -#define FRAMEWORK_DATASAMPLING_H - -/// \file DataSampling.h -/// \brief Definition of O2 Data Sampling, v1.0 -/// -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#include "Framework/WorkflowSpec.h" -#include "Framework/InputSpec.h" -#include <string> -#include <boost/property_tree/ptree_fwd.hpp> - -namespace o2::configuration -{ -class ConfigurationInterface; -} - -namespace o2::framework -{ - -class CompletionPolicy; -class ChannelConfigurationPolicy; -class Dispatcher; - -/// A class responsible for providing data from main processing flow to QC tasks. -/// -/// This class generates message-passing infrastructure to provide desired amount of data to Quality Control tasks or -/// any other clients. Data to be sampled is declared in DataSamplingPolicy'ies configuration file - an example can be -/// found in O2/Framework/TestWorkflows/exampleDataSamplingConfig.json). -/// -/// In-code usage: -/// \code{.cxx} -/// void customize(std::vector<CompletionPolicy>& policies) -/// { -/// DataSampling::CustomizeInfrastructure(policies); -/// } -/// -/// void customize(std::vector<ChannelConfigurationPolicy>& policies) -/// { -/// DataSampling::CustomizeInfrastructure(policies); -/// } -/// -/// #include "Framework/runDataProcessing.h" -/// -/// std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext &ctx) -/// { -/// WorkflowSpec workflow; -/// // <declaration of other DPL processors> -/// -/// const std::string configurationFilePath = <absolute file path>; -/// DataSampling::GenerateInfrastructure(workflow, configurationFilePath); -/// -/// return workflow; -/// } -/// \endcode - -class DataSampling -{ - public: - /// \brief Deleted default constructor. This class is stateless. - DataSampling() = delete; - /// \brief Generates data sampling infrastructure. - /// \param workflow DPL workflow with already declared data processors which provide data desired by - /// QC tasks. - /// \param policiesSource Path to configuration file. - /// \param threads Number of dispatcher threads, that will handle the data - static void GenerateInfrastructure(WorkflowSpec& workflow, const std::string& policiesSource, size_t threads = 1); - - /// \brief Generates data sampling infrastructure. - /// \param workflow DPL workflow with already declared data processors which provide data desired by - /// QC tasks. - /// \param policiesSource boost::property_tree::ptree with the configuration - /// \param threads Number of dispatcher threads, that will handle the data - static void GenerateInfrastructure(WorkflowSpec& workflow, boost::property_tree::ptree const& policies, size_t threads = 1); - /// \brief Configures dispatcher to consume any data immediately. - static void CustomizeInfrastructure(std::vector<CompletionPolicy>&); - /// \brief Applies blocking/nonblocking data sampling configuration to the workflow. - static void CustomizeInfrastructure(std::vector<ChannelConfigurationPolicy>&); - /// \brief Provides InputSpecs to receive data for given DataSamplingPolicy - static std::vector<InputSpec> InputSpecsForPolicy(const std::string& policiesSource, const std::string& policyName); - /// \brief Provides InputSpecs to receive data for given DataSamplingPolicy - static std::vector<InputSpec> InputSpecsForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); - /// \brief Provides OutputSpecs of given DataSamplingPolicy - static std::vector<OutputSpec> OutputSpecsForPolicy(const std::string& policiesSource, const std::string& policyName); - /// \brief Provides OutputSpecs of given DataSamplingPolicy - static std::vector<OutputSpec> OutputSpecsForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); - /// \brief Provides the port to be used for a proxy of given DataSamplingPolicy - static uint16_t PortForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); - /// \brief Provides the port to be used for a proxy of given DataSamplingPolicy - static uint16_t PortForPolicy(const std::string& policiesSource, const std::string& policyName); - /// \brief Provides the machines where given DataSamplingPolicy is enabled - static std::vector<std::string> MachinesForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); - /// \brief Provides the port to be used for a proxy of given DataSamplingPolicy - static std::vector<std::string> MachinesForPolicy(const std::string& policiesSource, const std::string& policyName); - - private: - static void DoGenerateInfrastructure(Dispatcher&, WorkflowSpec& workflow, boost::property_tree::ptree const& policies, size_t threads = 1); - // Internal functions, used by GenerateInfrastructure() - static std::string createDispatcherName(); -}; - -} // namespace o2::framework - -#endif // FRAMEWORK_DATASAMPLING_H diff --git a/Framework/Core/include/Framework/DataSamplingPolicy.h b/Framework/Core/include/Framework/DataSamplingPolicy.h deleted file mode 100644 index 647c7a57ff331..0000000000000 --- a/Framework/Core/include/Framework/DataSamplingPolicy.h +++ /dev/null @@ -1,93 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_DATASAMPLINGPOLICY_H -#define ALICEO2_DATASAMPLINGPOLICY_H - -/// \file DataSamplingPolicy.h -/// \brief A declaration of O2 Data Sampling Policy -/// -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#include "Headers/DataHeader.h" -#include "Framework/InputSpec.h" -#include "Framework/Output.h" -#include "Framework/OutputSpec.h" -#include "Framework/DataSamplingCondition.h" -#include "Framework/DataSpecUtils.h" - -#include <boost/property_tree/ptree_fwd.hpp> - -namespace o2::framework -{ - -/// A class representing certain policy of sampling data. -/// -/// This class stores information about specified sampling policy - data headers and conditions of sampling. -/// For given InputSpec, it can provide corresponding Output to pass data further. -class DataSamplingPolicy -{ - private: - using PathVectorBase = std::vector<std::pair<InputSpec, OutputSpec>>; - class PathMap : public PathVectorBase - { - public: - ~PathMap() = default; - PathMap() = default; - const PathVectorBase::const_iterator find(const ConcreteDataMatcher& input) const - { - return std::find_if(begin(), end(), [input](const auto& el) { - return DataSpecUtils::match(el.first, input); - }); - } - }; - - public: - /// \brief Constructor. - DataSamplingPolicy(); - /// \brief Constructor. - DataSamplingPolicy(const boost::property_tree::ptree&); - /// \brief Destructor - ~DataSamplingPolicy(); - - /// \brief Configures a policy using structured configuration entry. - void configure(const boost::property_tree::ptree&); - /// \brief Returns true if this policy requires data with given InputSpec. - bool match(const ConcreteDataMatcher& input) const; - /// \brief Returns true if user-defined conditions of sampling are fulfilled. - bool decide(const o2::framework::DataRef&); - /// \brief Returns Output for given InputSpec to pass data forward. - const Output prepareOutput(const ConcreteDataMatcher& input, Lifetime lifetime = Lifetime::Timeframe) const; - - const std::string& getName() const; - const PathMap& getPathMap() const; - // optional fairmq channel to send stuff outside of DPL - const std::string& getFairMQOutputChannel() const; - std::string getFairMQOutputChannelName() const; - uint32_t getTotalAcceptedMessages() const; - uint32_t getTotalEvaluatedMessages() const; - - static header::DataOrigin createPolicyDataOrigin(); - static header::DataDescription createPolicyDataDescription(std::string policyName, size_t id); - - private: - std::string mName; - PathMap mPaths; - std::vector<std::unique_ptr<DataSamplingCondition>> mConditions; - std::string mFairMQOutputChannel; - - // stats - uint32_t mTotalAcceptedMessages = 0; - uint32_t mTotalEvaluatedMessages = 0; -}; - -} // namespace o2::framework - -#endif // ALICEO2_DATASAMPLINGPOLICY_H diff --git a/Framework/Core/include/Framework/DataSpecUtils.h b/Framework/Core/include/Framework/DataSpecUtils.h index 9ade6382b7e2c..8af549a8f6642 100644 --- a/Framework/Core/include/Framework/DataSpecUtils.h +++ b/Framework/Core/include/Framework/DataSpecUtils.h @@ -72,6 +72,12 @@ struct DataSpecUtils { /// @return true if the OutputSpec will match at least the provided @a origin. static bool partialMatch(OutputSpec const& spec, o2::header::DataOrigin const& origin); + /// @return true if the OutputSpec will match at least the provided @a description. + static bool partialMatch(InputSpec const& spec, o2::header::DataDescription const& description); + + /// @return true if the OutputSpec will match at least the provided @a description. + static bool partialMatch(OutputSpec const& spec, o2::header::DataDescription const& description); + template <typename T> static bool match(const T& spec, const o2::header::DataHeader& header) { @@ -179,6 +185,12 @@ struct DataSpecUtils { /// Build a DataDescriptMatcher which does not care about the subSpec and description. static data_matcher::DataDescriptorMatcher dataDescriptorMatcherFrom(header::DataOrigin const& origin); + + /// Build a DataDescriptMatcher which does not care about the subSpec and origin. + static data_matcher::DataDescriptorMatcher dataDescriptorMatcherFrom(header::DataDescription const& origin); + + /// Checks if left includes right (or is equal to) + static bool includes(const InputSpec& left, const InputSpec& right); }; } // namespace framework diff --git a/Framework/Core/include/Framework/DataTypes.h b/Framework/Core/include/Framework/DataTypes.h index fe56d1d2a5dd7..9ea0d1f9fb4c3 100644 --- a/Framework/Core/include/Framework/DataTypes.h +++ b/Framework/Core/include/Framework/DataTypes.h @@ -10,13 +10,21 @@ #ifndef O2_FRAMEWORK_DATATYPES_H_ #define O2_FRAMEWORK_DATATYPES_H_ +#include <cstdint> + namespace o2::aod::track { enum TrackTypeEnum : uint8_t { GlobalTrack = 0, ITSStandalone, MFTStandalone, - Run2Tracklet + Run2GlobalTrack = 254, + Run2Tracklet = 255 +}; +enum TrackFlagsRun2Enum { + ITSrefit = 0x1, + TPCrefit = 0x2, + GoldenChi2 = 0x4 }; } // namespace o2::aod::track diff --git a/Framework/Core/include/Framework/DebugGUI.h b/Framework/Core/include/Framework/DebugGUI.h new file mode 100644 index 0000000000000..d2344671a300e --- /dev/null +++ b/Framework/Core/include/Framework/DebugGUI.h @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DEBUGUIINTERFACE_H_ +#define O2_FRAMEWORK_DEBUGUIINTERFACE_H_ + +#include "Framework/DeviceInfo.h" +#include "Framework/DeviceSpec.h" +#include "Framework/DeviceControl.h" +#include "Framework/DataProcessorInfo.h" +#include "Framework/DeviceMetricsInfo.h" +#include "Framework/DriverInfo.h" +#include "Framework/DriverControl.h" + +#include <functional> +#include <vector> + +namespace o2::framework +{ +/// Plugin interface for DPL GUIs. +struct DebugGUI { + virtual std::function<void(void)> getGUIDebugger(std::vector<o2::framework::DeviceInfo> const& infos, + std::vector<o2::framework::DeviceSpec> const& devices, + std::vector<o2::framework::DataProcessorInfo> const& metadata, + std::vector<o2::framework::DeviceMetricsInfo> const& metricsInfos, + o2::framework::DriverInfo const& driverInfo, + std::vector<o2::framework::DeviceControl>& controls, + o2::framework::DriverControl& driverControl) = 0; + virtual void* initGUI(char const* windowTitle) = 0; + virtual bool pollGUI(void* context, std::function<void(void)> guiCallback) = 0; + virtual void disposeGUI() = 0; +}; +} // namespace o2::framework +#endif // O2_FRAMEWORK_DEBUGUIINTERFACE_H_ diff --git a/Framework/Core/include/Framework/DeviceExecution.h b/Framework/Core/include/Framework/DeviceExecution.h index 0dae0252c4319..8e3b57b6bc291 100644 --- a/Framework/Core/include/Framework/DeviceExecution.h +++ b/Framework/Core/include/Framework/DeviceExecution.h @@ -23,6 +23,8 @@ namespace framework struct DeviceExecution { /// The options passed to a given device std::vector<char*> args; + /// The environment to be passed to a given device + std::vector<char*> environ; }; } // namespace framework diff --git a/Framework/Core/include/Framework/DeviceInfo.h b/Framework/Core/include/Framework/DeviceInfo.h index 9fed22b0b5f29..f103484ef4322 100644 --- a/Framework/Core/include/Framework/DeviceInfo.h +++ b/Framework/Core/include/Framework/DeviceInfo.h @@ -7,12 +7,11 @@ // In applying this license CERN does 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 FRAMEWORK_DEVICEINFO_H -#define FRAMEWORK_DEVICEINFO_H +#ifndef O2_FRAMEWORK_DEVICEINFO_H_ +#define O2_FRAMEWORK_DEVICEINFO_H_ #include "Framework/LogParsingHelpers.h" #include "Framework/Metric2DViewIndex.h" -#include "Framework/Variant.h" #include "Framework/DeviceState.h" #include <cstddef> @@ -23,9 +22,7 @@ #include <array> #include <boost/property_tree/ptree.hpp> -namespace o2 -{ -namespace framework +namespace o2::framework { struct DeviceInfo { @@ -48,6 +45,7 @@ struct DeviceInfo { /// A circular buffer for the severity of each of the entries /// in the circular buffer associated to the device. std::vector<LogParsingHelpers::LogLevel> historyLevel; + std::string firstError; std::string lastError; /// An unterminated string which is not ready to be printed yet std::string unprinted; @@ -67,8 +65,9 @@ struct DeviceInfo { boost::property_tree::ptree currentConfig; /// Current provenance for the configuration keys boost::property_tree::ptree currentProvenance; + /// Port to use to connect to tracy profiler + short tracyPort; }; -} // namespace framework -} // namespace o2 -#endif // FRAMEWORK_DEVICEINFO_H +} // namespace o2::framework +#endif // O2_FRAMEWORK_DEVICEINFO_H_ diff --git a/Framework/Core/include/Framework/DeviceMetricsInfo.h b/Framework/Core/include/Framework/DeviceMetricsInfo.h index 54d89668f975a..a9167e1d50a56 100644 --- a/Framework/Core/include/Framework/DeviceMetricsInfo.h +++ b/Framework/Core/include/Framework/DeviceMetricsInfo.h @@ -8,34 +8,36 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -#ifndef FRAMEWORK_DEVICEMETRICSINFO_H -#define FRAMEWORK_DEVICEMETRICSINFO_H +#ifndef O2_FRAMEWORK_DEVICEMETRICSINFO_H_ +#define O2_FRAMEWORK_DEVICEMETRICSINFO_H_ +#include "Framework/RuntimeError.h" #include <array> #include <cstddef> +#include <cstring> #include <functional> #include <string> #include <string_view> #include <vector> -namespace o2 -{ -namespace framework +namespace o2::framework { enum class MetricType { Int = 0, String = 1, Float = 2, + Uint64 = 3, Unknown }; std::ostream& operator<<(std::ostream& oss, MetricType const& val); struct MetricInfo { - enum MetricType type; - size_t storeIdx; // Index in the actual store - size_t pos; // Last position in the circular buffer + enum MetricType type = MetricType::Unknown; + size_t storeIdx = -1; // Index in the actual store + size_t pos = 0; // Last position in the circular buffer + size_t filledMetrics = 0; // How many metrics were filled }; // We keep only fixed lenght strings for metrics, as in the end this is not @@ -45,11 +47,12 @@ struct StringMetric { char data[MAX_SIZE]; }; -// Also for the keys it does not make much sense to keep more than 256 chars. +// Also for the keys it does not make much sense to keep more than 247 chars. // They should be nevertheless 0 terminated. struct MetricLabelIndex { - static constexpr size_t MAX_METRIC_LABEL_SIZE = 256 - sizeof(size_t); // Maximum size for a metric name. + static constexpr size_t MAX_METRIC_LABEL_SIZE = 256 - sizeof(size_t) - sizeof(unsigned char); // Maximum size for a metric name. size_t index; + unsigned char size = 0; char label[MAX_METRIC_LABEL_SIZE]; }; @@ -61,6 +64,7 @@ struct ParsedMetricMatch { MetricType type; int intValue; float floatValue; + uint64_t uint64Value; char const* beginStringValue; char const* endStringValue; }; @@ -71,6 +75,7 @@ struct DeviceMetricsInfo { // We keep the size of each metric to 4096 bytes. No need for more // for the debug GUI std::vector<std::array<int, 1024>> intMetrics; + std::vector<std::array<uint64_t, 1024>> uint64Metrics; std::vector<std::array<StringMetric, 32>> stringMetrics; // We do not keep so many strings as metrics as history is less relevant. std::vector<std::array<float, 1024>> floatMetrics; std::vector<std::array<size_t, 1024>> timestamps; @@ -80,6 +85,7 @@ struct DeviceMetricsInfo { std::vector<size_t> maxDomain; std::vector<MetricLabelIndex> metricLabelsIdx; std::vector<MetricInfo> metrics; + std::vector<bool> changed; }; struct DeviceMetricsHelper { @@ -98,11 +104,99 @@ struct DeviceMetricsHelper { static bool processMetric(ParsedMetricMatch& results, DeviceMetricsInfo& info, NewMetricCallback newMetricCallback = nullptr); + /// @return the index in metrics for the information of given metric static size_t metricIdxByName(const std::string& name, const DeviceMetricsInfo& info); + + /// Typesafe way to get the actual store + template <typename T> + static auto& getMetricsStore(DeviceMetricsInfo& metrics) + { + if constexpr (std::is_same_v<T, int>) { + return metrics.intMetrics; + } else if constexpr (std::is_same_v<T, float>) { + return metrics.floatMetrics; + } else if constexpr (std::is_same_v<T, uint64_t>) { + return metrics.uint64Metrics; + } else { + throw runtime_error("Unhandled metric type"); + }; + } + + template <typename T> + static auto getMetricType() + { + if constexpr (std::is_same_v<T, int>) { + return MetricType::Int; + } else if constexpr (std::is_same_v<T, float>) { + return MetricType::Float; + } else if constexpr (std::is_same_v<T, uint64_t>) { + return MetricType::Uint64; + } else { + throw runtime_error("Unhandled metric type"); + }; + } + + /// @return helper to insert a given value in the metrics + template <typename T> + static std::function<void(DeviceMetricsInfo&, T value, size_t timestamp)> + createNumericMetric(DeviceMetricsInfo& metrics, + char const* name, + NewMetricCallback newMetricsCallback = nullptr) + { + static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint64_t> || std::is_same_v<T, float>, "Unsupported metric type"); + // Create a new metric + MetricInfo metricInfo; + metricInfo.pos = 0; + metricInfo.type = getMetricType<T>(); + metricInfo.filledMetrics = 0; + metricInfo.storeIdx = getMetricsStore<T>(metrics).size(); + getMetricsStore<T>(metrics).emplace_back(std::array<T, 1024>{}); + + // Add the timestamp buffer for it + metrics.timestamps.emplace_back(std::array<size_t, 1024>{}); + metrics.max.push_back(std::numeric_limits<float>::lowest()); + metrics.min.push_back(std::numeric_limits<float>::max()); + metrics.maxDomain.push_back(std::numeric_limits<size_t>::lowest()); + metrics.minDomain.push_back(std::numeric_limits<size_t>::max()); + metrics.changed.push_back(true); + + // Add the index by name in the correct position + // this will require moving the tail of the index, + // but inserting should happen only once for each metric, + // so who cares. + // Add the the actual Metric info to the store + MetricLabelIndex metricLabelIdx; + strncpy(metricLabelIdx.label, name, MetricLabelIndex::MAX_METRIC_LABEL_SIZE - 1); + metricLabelIdx.label[MetricLabelIndex::MAX_METRIC_LABEL_SIZE - 1] = '\0'; + metricLabelIdx.index = metrics.metrics.size(); + metricLabelIdx.size = strlen(metricLabelIdx.label); + metrics.metricLabelsIdx.push_back(metricLabelIdx); + + // Add the the actual Metric info to the store + auto metricIndex = metrics.metrics.size(); + metrics.metrics.push_back(metricInfo); + + if (newMetricsCallback != nullptr) { + newMetricsCallback(metricLabelIdx.label, metricInfo, 0, metricIndex); + } + return [metricIndex](DeviceMetricsInfo& metrics, T value, size_t timestamp) { + MetricInfo& metric = metrics.metrics[metricIndex]; + + metrics.minDomain[metricIndex] = std::min(metrics.minDomain[metricIndex], timestamp); + metrics.maxDomain[metricIndex] = std::max(metrics.maxDomain[metricIndex], timestamp); + metrics.max[metricIndex] = std::max(metrics.max[metricIndex], (float)value); + metrics.min[metricIndex] = std::min(metrics.min[metricIndex], (float)value); + metrics.changed.at(metricIndex) = true; + auto& store = getMetricsStore<T>(metrics); + size_t pos = metric.pos++ % store[metric.storeIdx].size(); + metrics.timestamps[metricIndex][pos] = timestamp; + store[metric.storeIdx][pos] = value; + metric.filledMetrics++; + }; + } }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // FRAMEWORK_DEVICEMETRICSINFO_H +#endif // O2_FRAMEWORK_DEVICEMETRICSINFO_H_ diff --git a/Framework/Core/include/Framework/DeviceSpec.h b/Framework/Core/include/Framework/DeviceSpec.h index c4b254c194be1..4497d556d704d 100644 --- a/Framework/Core/include/Framework/DeviceSpec.h +++ b/Framework/Core/include/Framework/DeviceSpec.h @@ -40,6 +40,7 @@ namespace framework struct DeviceSpec { std::string name; std::string id; + std::string channelPrefix; std::vector<InputChannelSpec> inputChannels; std::vector<OutputChannelSpec> outputChannels; std::vector<std::string> arguments; @@ -61,6 +62,7 @@ struct DeviceSpec { CompletionPolicy completionPolicy; DispatchPolicy dispatchPolicy; ComputingResource resource; + unsigned short resourceMonitoringInterval; }; } // namespace framework diff --git a/Framework/Core/include/Framework/DeviceState.h b/Framework/Core/include/Framework/DeviceState.h index a156f4553b6e9..b16d07e94dd4e 100644 --- a/Framework/Core/include/Framework/DeviceState.h +++ b/Framework/Core/include/Framework/DeviceState.h @@ -19,6 +19,7 @@ typedef struct uv_loop_s uv_loop_t; typedef struct uv_timer_s uv_timer_t; typedef struct uv_poll_s uv_poll_t; +typedef struct uv_signal_s uv_signal_t; namespace o2::framework { @@ -47,6 +48,8 @@ struct DeviceState { std::vector<uv_poll_t*> activeInputPollers; // The list of pollers for active output channels std::vector<uv_poll_t*> activeOutputPollers; + /// The list of active signal handlers + std::vector<uv_signal_t*> activeSignals; }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/Dispatcher.h b/Framework/Core/include/Framework/Dispatcher.h deleted file mode 100644 index 9ffbd06478dd0..0000000000000 --- a/Framework/Core/include/Framework/Dispatcher.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Dispatcher.h -/// \brief Declaration of Dispatcher for O2 Data Sampling -/// -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#ifndef ALICEO2_DISPATCHER_H -#define ALICEO2_DISPATCHER_H - -#include <string> -#include <vector> -#include <memory> - -#include "Framework/DataProcessorSpec.h" -#include "Framework/DeviceSpec.h" -#include "Framework/Task.h" - -class FairMQDevice; - -namespace o2::monitoring -{ -class Monitoring; -} - -namespace o2::framework -{ - -class DataSamplingHeader; -class DataSamplingPolicy; - -class Dispatcher : public Task -{ - public: - /// \brief Constructor - Dispatcher(const std::string name, const std::string reconfigurationSource); - /// \brief Destructor - ~Dispatcher() override; - - /// \brief Dispatcher init callback - void init(InitContext& ctx) override; - /// \brief Dispatcher process callback - void run(ProcessingContext& ctx) override; - - /// \brief Create appropriate inputSpecs and outputSpecs for sampled data during the workflow declaration phase. - void registerPath(const std::pair<InputSpec, OutputSpec>&); - - const std::string& getName(); - Inputs getInputSpecs(); - Outputs getOutputSpecs(); - - private: - DataSamplingHeader prepareDataSamplingHeader(const DataSamplingPolicy& policy, const DeviceSpec& spec); - header::Stack extractAdditionalHeaders(const char* inputHeaderStack) const; - void reportStats(monitoring::Monitoring& monitoring) const; - void send(DataAllocator& dataAllocator, const DataRef& inputData, Output&& output) const; - void sendFairMQ(FairMQDevice* device, const DataRef& inputData, const std::string& fairMQChannel, - header::Stack&& stack) const; - - std::string mName; - std::string mReconfigurationSource; - Inputs inputs; - Outputs outputs; - // policies should be shared between all pipeline threads - std::vector<std::shared_ptr<DataSamplingPolicy>> mPolicies; -}; - -} // namespace o2::framework - -#endif //ALICEO2_DISPATCHER_H diff --git a/Framework/Core/src/DriverControl.h b/Framework/Core/include/Framework/DriverControl.h similarity index 98% rename from Framework/Core/src/DriverControl.h rename to Framework/Core/include/Framework/DriverControl.h index 606e1c8ef6bff..b9ac9f303d41e 100644 --- a/Framework/Core/src/DriverControl.h +++ b/Framework/Core/include/Framework/DriverControl.h @@ -13,7 +13,7 @@ #include <functional> #include <vector> -#include "DriverInfo.h" +#include "Framework/DriverInfo.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DeviceSpec.h" #include "Framework/DeviceExecution.h" diff --git a/Framework/Core/src/DriverInfo.h b/Framework/Core/include/Framework/DriverInfo.h similarity index 90% rename from Framework/Core/src/DriverInfo.h rename to Framework/Core/include/Framework/DriverInfo.h index 2c3f8083ce5b2..d1df4a78c416c 100644 --- a/Framework/Core/src/DriverInfo.h +++ b/Framework/Core/include/Framework/DriverInfo.h @@ -20,6 +20,9 @@ #include "Framework/ChannelConfigurationPolicy.h" #include "Framework/ConfigParamSpec.h" #include "Framework/TerminationPolicy.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/DispatchPolicy.h" +#include "Framework/DeviceMetricsInfo.h" #include "DataProcessorInfo.h" namespace o2::framework @@ -74,8 +77,6 @@ enum struct DriverState { struct DriverInfo { /// Stack with the states to be processed next. std::vector<DriverState> states; - /// The first unused file descriptor - int maxFd; // Signal handler for children struct sigaction sa_handle_child; @@ -131,6 +132,18 @@ struct DriverInfo { float frameLatency; /// The unique id used for ipc communications std::string uniqueWorkflowId = ""; + /// Metrics gathering interval + unsigned short resourcesMonitoringInterval; + /// Last port used for tracy + short tracyPort = 8086; + /// Aggregate metrics calculated in the driver itself + DeviceMetricsInfo metrics; + /// Skip shared memory cleanup if set + bool noSHMCleanup; +}; + +struct DriverInfoHelper { + static char const* stateToString(enum DriverState state); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/ErrorContext.h b/Framework/Core/include/Framework/ErrorContext.h index fd151d83b504d..96528afa69c49 100644 --- a/Framework/Core/include/Framework/ErrorContext.h +++ b/Framework/Core/include/Framework/ErrorContext.h @@ -7,17 +7,14 @@ // In applying this license CERN does 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 FRAMEWORK_ERROR_CONTEXT_H -#define FRAMEWORK_ERROR_CONTEXT_H +#ifndef O2_FRAMEWORK_ERROR_CONTEXT_H_ +#define O2_FRAMEWORK_ERROR_CONTEXT_H_ #include "Framework/InputRecord.h" #include "Framework/ServiceRegistry.h" +#include "Framework/RuntimeError.h" -#include <exception> - -namespace o2 -{ -namespace framework +namespace o2::framework { // This is a utility class to reduce the amount of boilerplate when defining @@ -25,23 +22,23 @@ namespace framework class ErrorContext { public: - ErrorContext(InputRecord& inputs, ServiceRegistry& services, std::exception& e) + ErrorContext(InputRecord& inputs, ServiceRegistry& services, RuntimeErrorRef e) : mInputs{inputs}, mServices{services}, - mException{e} + mExceptionRef{e} { } InputRecord const& inputs() { return mInputs; } ServiceRegistry const& services() { return mServices; } - std::exception const& exception() { return mException; } + RuntimeErrorRef exception() { return mExceptionRef; } + private: InputRecord& mInputs; ServiceRegistry& mServices; - std::exception& mException; + RuntimeErrorRef mExceptionRef; }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif +#endif // o2::framework diff --git a/Framework/Core/include/Framework/Expressions.h b/Framework/Core/include/Framework/Expressions.h index fe8681c283da5..fc13daea14375 100644 --- a/Framework/Core/include/Framework/Expressions.h +++ b/Framework/Core/include/Framework/Expressions.h @@ -14,6 +14,12 @@ #include "Framework/CompilerBuiltins.h" #include "Framework/Pack.h" #include "Framework/CheckTypes.h" +#include "Framework/Configurable.h" +#include "Framework/Variant.h" +#include "Framework/InitContext.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/RootConfigParamHelpers.h" +#include "Framework/RuntimeError.h" #include <arrow/type_fwd.h> #include <gandiva/gandiva_aliases.h> #include <arrow/type.h> @@ -24,7 +30,6 @@ #include <gandiva/node.h> #include <gandiva/filter.h> #include <gandiva/projector.h> -#include <fmt/format.h> #else namespace gandiva { @@ -51,7 +56,7 @@ struct LiteralStorage { using stored_pack = framework::pack<T...>; }; -using LiteralValue = LiteralStorage<int, bool, float, double>; +using LiteralValue = LiteralStorage<int, bool, float, double, uint8_t, int64_t>; template <typename T> constexpr auto selectArrowType() @@ -68,6 +73,8 @@ constexpr auto selectArrowType() return atype::INT8; } else if constexpr (std::is_same_v<T, uint16_t>) { return atype::INT16; + } else if constexpr (std::is_same_v<T, int64_t>) { + return atype::INT64; } else if constexpr (std::is_same_v<T, uint8_t>) { return atype::UINT8; } else { @@ -85,6 +92,7 @@ struct LiteralNode { LiteralNode(T v) : value{v}, type{selectArrowType<T>()} { } + using var_t = LiteralValue::stored_type; var_t value; atype::type type = atype::NA; @@ -105,12 +113,39 @@ struct OpNode { BasicOp op; }; +/// A placeholder node for simple type configurable +struct PlaceholderNode : LiteralNode { + template <typename T> + PlaceholderNode(Configurable<T> v) : LiteralNode{v.value} + { + if constexpr (variant_trait_v<typename std::decay<T>::type> != VariantType::Unknown) { + retrieve = [name = v.name](InitContext& context) { return LiteralNode::var_t{context.options().get<T>(name.c_str())}; }; + } else { + retrieve = [name = v.name](InitContext& context) { + auto pt = context.options().get<boost::property_tree::ptree>(name.c_str()); + return LiteralNode::var_t{RootConfigParamHelpers::as<T>(pt)}; + }; + } + } + + void reset(InitContext& context) + { + value = retrieve(context); + } + + std::function<LiteralNode::var_t(InitContext&)> retrieve; +}; + /// A generic tree node struct Node { Node(LiteralNode v) : self{v}, left{nullptr}, right{nullptr} { } + Node(PlaceholderNode v) : self{v}, left{nullptr}, right{nullptr} + { + } + Node(Node&& n) : self{n.self}, left{std::move(n.left)}, right{std::move(n.right)} { } @@ -130,7 +165,7 @@ struct Node { right{nullptr} {} /// variant with possible nodes - using self_t = std::variant<LiteralNode, BindingNode, OpNode>; + using self_t = std::variant<LiteralNode, BindingNode, OpNode, PlaceholderNode>; self_t self; /// pointers to children std::unique_ptr<Node> left; @@ -176,6 +211,42 @@ inline Node operator!=(Node left, T rightValue) return Node{OpNode{BasicOp::NotEqual}, std::move(left), LiteralNode{rightValue}}; } +template <typename T> +inline Node operator>(Node left, Configurable<T> rightValue) +{ + return Node{OpNode{BasicOp::GreaterThan}, std::move(left), PlaceholderNode{rightValue}}; +} + +template <typename T> +inline Node operator<(Node left, Configurable<T> rightValue) +{ + return Node{OpNode{BasicOp::LessThan}, std::move(left), PlaceholderNode{rightValue}}; +} + +template <typename T> +inline Node operator>=(Node left, Configurable<T> rightValue) +{ + return Node{OpNode{BasicOp::GreaterThanOrEqual}, std::move(left), PlaceholderNode{rightValue}}; +} + +template <typename T> +inline Node operator<=(Node left, Configurable<T> rightValue) +{ + return Node{OpNode{BasicOp::LessThanOrEqual}, std::move(left), PlaceholderNode{rightValue}}; +} + +template <typename T> +inline Node operator==(Node left, Configurable<T> rightValue) +{ + return Node{OpNode{BasicOp::Equal}, std::move(left), PlaceholderNode{rightValue}}; +} + +template <typename T> +inline Node operator!=(Node left, Configurable<T> rightValue) +{ + return Node{OpNode{BasicOp::NotEqual}, std::move(left), PlaceholderNode{rightValue}}; +} + /// node comparisons inline Node operator>(Node left, Node right) { @@ -249,6 +320,11 @@ inline Node operator/(Node left, BindingNode right) return Node{OpNode{BasicOp::Division}, std::move(left), right}; } +inline Node operator/(BindingNode left, BindingNode right) +{ + return Node{OpNode{BasicOp::Division}, left, right}; +} + inline Node operator+(Node left, Node right) { return Node{OpNode{BasicOp::Addition}, std::move(left), std::move(right)}; @@ -301,6 +377,18 @@ inline Node operator+(T left, Node right) return Node{OpNode{BasicOp::Addition}, LiteralNode{left}, std::move(right)}; } +template <> +inline Node operator+(Node left, BindingNode right) +{ + return Node{OpNode{BasicOp::Addition}, std::move(left), right}; +} + +template <> +inline Node operator+(BindingNode left, Node right) +{ + return Node{OpNode{BasicOp::Addition}, left, std::move(right)}; +} + template <typename T> inline Node operator-(Node left, T right) { @@ -312,6 +400,54 @@ inline Node operator-(T left, Node right) { return Node{OpNode{BasicOp::Subtraction}, LiteralNode{left}, std::move(right)}; } + +template <typename T> +inline Node operator*(Node left, Configurable<T> right) +{ + return Node{OpNode{BasicOp::Multiplication}, std::move(left), PlaceholderNode{right}}; +} + +template <typename T> +inline Node operator*(Configurable<T> left, Node right) +{ + return Node{OpNode{BasicOp::Multiplication}, PlaceholderNode{left}, std::move(right)}; +} + +template <typename T> +inline Node operator/(Node left, Configurable<T> right) +{ + return Node{OpNode{BasicOp::Division}, std::move(left), PlaceholderNode{right}}; +} + +template <typename T> +inline Node operator/(Configurable<T> left, Node right) +{ + return Node{OpNode{BasicOp::Division}, PlaceholderNode{left}, std::move(right)}; +} + +template <typename T> +inline Node operator+(Node left, Configurable<T> right) +{ + return Node{OpNode{BasicOp::Addition}, std::move(left), PlaceholderNode{right}}; +} + +template <typename T> +inline Node operator+(Configurable<T> left, Node right) +{ + return Node{OpNode{BasicOp::Addition}, PlaceholderNode{left}, std::move(right)}; +} + +template <typename T> +inline Node operator-(Node left, Configurable<T> right) +{ + return Node{OpNode{BasicOp::Subtraction}, std::move(left), PlaceholderNode{right}}; +} + +template <typename T> +inline Node operator-(Configurable<T> left, Node right) +{ + return Node{OpNode{BasicOp::Subtraction}, PlaceholderNode{left}, std::move(right)}; +} /// semi-binary template <typename T> inline Node npow(Node left, T right) @@ -320,6 +456,11 @@ inline Node npow(Node left, T right) } /// unary operations on nodes +inline Node nsqrt(Node left) +{ + return Node{OpNode{BasicOp::Sqrt}, std::move(left)}; +} + inline Node nexp(Node left) { return Node{OpNode{BasicOp::Exp}, std::move(left)}; @@ -340,6 +481,36 @@ inline Node nabs(Node left) return Node{OpNode{BasicOp::Abs}, std::move(left)}; } +inline Node nsin(Node left) +{ + return Node{OpNode{BasicOp::Sin}, std::move(left)}; +} + +inline Node ncos(Node left) +{ + return Node{OpNode{BasicOp::Cos}, std::move(left)}; +} + +inline Node ntan(Node left) +{ + return Node{OpNode{BasicOp::Tan}, std::move(left)}; +} + +inline Node nasin(Node left) +{ + return Node{OpNode{BasicOp::Asin}, std::move(left)}; +} + +inline Node nacos(Node left) +{ + return Node{OpNode{BasicOp::Acos}, std::move(left)}; +} + +inline Node natan(Node left) +{ + return Node{OpNode{BasicOp::Atan}, std::move(left)}; +} + /// A struct, containing the root of the expression tree struct Filter { Filter(Node&& node_) : node{std::make_unique<Node>(std::move(node_))} {} @@ -386,6 +557,27 @@ void updateExpressionInfos(expressions::Filter const& filter, std::vector<Expres gandiva::ConditionPtr makeCondition(gandiva::NodePtr node); /// Function to create gandiva projecting expression from generic gandiva expression tree gandiva::ExpressionPtr makeExpression(gandiva::NodePtr node, gandiva::FieldPtr result); +/// Update placeholder nodes from context +void updatePlaceholders(Filter& filter, InitContext& context); + +template <typename... C> +std::shared_ptr<gandiva::Projector> createProjectors(framework::pack<C...>, gandiva::SchemaPtr schema) +{ + std::shared_ptr<gandiva::Projector> projector; + auto s = gandiva::Projector::Make( + schema, + {makeExpression( + framework::expressions::createExpressionTree( + framework::expressions::createOperations(C::Projector()), + schema), + C::asArrowField())...}, + &projector); + if (s.ok()) { + return projector; + } else { + throw o2::framework::runtime_error_f("Failed to create projector: %s", s.ToString().c_str()); + } +} } // namespace o2::framework::expressions #endif // O2_FRAMEWORK_EXPRESSIONS_H_ diff --git a/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h b/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h index 305163c5138d8..0708d37f3c0a1 100644 --- a/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h +++ b/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h @@ -21,9 +21,10 @@ namespace o2 { namespace framework { + /// A callback function to retrieve the FairMQChannel name to be used for sending /// messages of the specified OutputSpec -using ChannelRetriever = std::function<std::string(OutputSpec const&)>; +using ChannelRetriever = std::function<std::string(OutputSpec const&, DataProcessingHeader::StartTime)>; using InjectorFunction = std::function<void(FairMQDevice& device, FairMQParts& inputs, ChannelRetriever)>; struct InputChannelSpec; @@ -67,7 +68,7 @@ static auto gDefaultConverter = incrementalConverter(OutputSpec{"TST", "TEST", 0 /// Create a DataProcessorSpec which can be used to inject /// messages in the DPL. -/// @param label is the label of the DataProcessorSpec associated. +/// @param label is the label of the DataProcessorSpec associated and name of the input channel. /// @param outputs is the type of messages which this source produces. /// @param channelConfig is string to be passed to fairmq to create the device. /// notice that the name of the device will be added as the name of the channel if the diff --git a/Framework/Core/include/Framework/HistogramRegistry.h b/Framework/Core/include/Framework/HistogramRegistry.h index 75ca56a0eea43..d201c5d6f6d5f 100644 --- a/Framework/Core/include/Framework/HistogramRegistry.h +++ b/Framework/Core/include/Framework/HistogramRegistry.h @@ -15,164 +15,666 @@ #include "Framework/FunctionalHelpers.h" #include "Framework/Logger.h" #include "Framework/OutputRef.h" +#include "Framework/OutputObjHeader.h" #include "Framework/OutputSpec.h" +#include "Framework/SerializationMethods.h" #include "Framework/StringHelpers.h" #include "Framework/TableBuilder.h" +#include "Framework/RuntimeError.h" +#include "Framework/StepTHn.h" -#include "TH1.h" -#include "TH2.h" -#include "TH3.h" -#include "THn.h" -#include "THnSparse.h" +#include <TH1.h> +#include <TH2.h> +#include <TH3.h> +#include <THn.h> +#include <THnSparse.h> +#include <TProfile.h> +#include <TProfile2D.h> +#include <TProfile3D.h> +#include <TList.h> +#include <TDataMember.h> +#include <TDataType.h> #include <string> #include <variant> -namespace o2 -{ +#include <deque> + +#define HIST(name) CONST_STR(name) -namespace framework +namespace o2::framework { -/// Data sctructure that will allow to construct a fully qualified TH* histogram -/// Currently only supports TH1F -struct HistogramConfigSpec { - HistogramConfigSpec(char const* const kind_, unsigned int nBins_, double xmin_, double xmax_) - : kind(kind_), - nBins(nBins_), - xmin(xmin_), - xmax(xmax_) +// Available root histogram types +enum HistType : unsigned int { + kUndefinedHist = 0, + kTH1D, + kTH1F, + kTH1I, + kTH1C, + kTH1S, + kTH2D, + kTH2F, + kTH2I, + kTH2C, + kTH2S, + kTH3D, + kTH3F, + kTH3I, + kTH3C, + kTH3S, + kTHnD, + kTHnF, + kTHnI, + kTHnC, + kTHnS, + kTHnL, + kTHnSparseD, + kTHnSparseF, + kTHnSparseI, + kTHnSparseC, + kTHnSparseS, + kTHnSparseL, + kTProfile, + kTProfile2D, + kTProfile3D, + kStepTHnF, // FIXME: for these two to work we need to align StepTHn ctors with the root THn ones + kStepTHnD +}; + +// variant of all possible root pointers; here we use only the interface types since the underlying data representation (int,float,double,long,char) is irrelevant +using HistPtr = std::variant<std::shared_ptr<THn>, std::shared_ptr<THnSparse>, std::shared_ptr<TH3>, std::shared_ptr<TH2>, std::shared_ptr<TH1>, std::shared_ptr<TProfile3D>, std::shared_ptr<TProfile2D>, std::shared_ptr<TProfile>>; + +//************************************************************************************************** +/** + * Specification of an Axis. + */ +//************************************************************************************************** +struct AxisSpec { + AxisSpec(std::vector<double> binEdges_, std::optional<std::string> title_ = std::nullopt, std::optional<std::string> name_ = std::nullopt) + : nBins(std::nullopt), + binEdges(binEdges_), + title(title_), + name(name_) { } - HistogramConfigSpec() - : kind(""), - nBins(1), - xmin(0), - xmax(1) + AxisSpec(int nBins_, double binMin_, double binMax_, std::optional<std::string> title_ = std::nullopt, std::optional<std::string> name_ = std::nullopt) + : nBins(nBins_), + binEdges({binMin_, binMax_}), + title(title_), + name(name_) { } + + std::optional<int> nBins{}; + std::vector<double> binEdges{}; + std::optional<std::string> title{}; + std::optional<std::string> name{}; // optional axis name for ndim histograms +}; + +//************************************************************************************************** +/** + * Specification of a histogram configuration. + */ +//************************************************************************************************** +struct HistogramConfigSpec { + HistogramConfigSpec(HistType type_, std::vector<AxisSpec> axes_) + : type(type_), + axes(axes_) + { + } + HistogramConfigSpec() = default; HistogramConfigSpec(HistogramConfigSpec const& other) = default; HistogramConfigSpec(HistogramConfigSpec&& other) = default; - std::string kind; - unsigned int nBins; - double xmin; - double xmax; + void addAxis(const AxisSpec& axis) + { + axes.push_back(axis); + } + + void addAxis(int nBins_, double binMin_, double binMax_, std::optional<std::string> title_ = std::nullopt, std::optional<std::string> name_ = std::nullopt) + { + axes.push_back({nBins_, binMin_, binMax_, title_, name_}); + } + + void addAxis(std::vector<double> binEdges_, std::optional<std::string> title_ = std::nullopt, std::optional<std::string> name_ = std::nullopt) + { + axes.push_back({binEdges_, title_, name_}); + } + + void addAxes(std::vector<AxisSpec> axes_) + { + axes.insert(axes.end(), axes_.begin(), axes_.end()); + } + + // add axes defined in other HistogramConfigSpec object + void addAxes(const HistogramConfigSpec& other) + { + axes.insert(axes.end(), other.axes.begin(), other.axes.end()); + } + + HistType type{HistType::kUndefinedHist}; + std::vector<AxisSpec> axes{}; }; -/// Data structure containing histogram specification for the HistogramRegistry -/// Contains hashed name as id for fast lookup +//************************************************************************************************** +/** + * Specification of a histogram. + */ +//************************************************************************************************** struct HistogramSpec { - HistogramSpec(char const* const name_, char const* const readableName_, HistogramConfigSpec config_) + HistogramSpec(char const* const name_, char const* const title_, HistogramConfigSpec config_, bool callSumw2_ = false) : name(name_), - readableName(readableName_), - id(compile_time_hash(name_)), - config(config_) + hash(compile_time_hash(name_)), + title(title_), + config(config_), + callSumw2(callSumw2_) { } HistogramSpec() : name(""), - readableName(""), - id(0), + hash(0), config() { } HistogramSpec(HistogramSpec const& other) = default; HistogramSpec(HistogramSpec&& other) = default; - std::string name; - std::string readableName; - uint32_t id; - HistogramConfigSpec config; + std::string name{}; + uint32_t hash{}; + std::string title{}; + HistogramConfigSpec config{}; + bool callSumw2{}; // wether or not hist needs heavy error structure produced by Sumw2() +}; + +//************************************************************************************************** +/** + * Static helper class to generate histograms from the specifications. + * Also provides functions to obtain pointer to the created histogram casted to the correct alternative of the std::variant HistPtr that is used in HistogramRegistry. + */ +//************************************************************************************************** +struct HistFactory { + + // create histogram of type T with the axes defined in HistogramSpec + template <typename T> + static std::shared_ptr<T> createHist(const HistogramSpec& histSpec) + { + constexpr std::size_t MAX_DIM{10}; + const std::size_t nAxes{histSpec.config.axes.size()}; + if (nAxes == 0 || nAxes > MAX_DIM) { + LOGF(FATAL, "The histogram specification contains no (or too many) axes."); + return nullptr; + } + + int nBins[MAX_DIM]{0}; + double lowerBounds[MAX_DIM]{0.}; + double upperBounds[MAX_DIM]{0.}; + + // first figure out number of bins and dimensions + for (std::size_t i = 0; i < nAxes; i++) { + nBins[i] = (histSpec.config.axes[i].nBins) ? *histSpec.config.axes[i].nBins : histSpec.config.axes[i].binEdges.size() - 1; + lowerBounds[i] = histSpec.config.axes[i].binEdges.front(); + upperBounds[i] = histSpec.config.axes[i].binEdges.back(); + } + + // create histogram + std::shared_ptr<T> hist{generateHist<T>(histSpec.name, histSpec.title, nAxes, nBins, lowerBounds, upperBounds)}; + if (!hist) { + LOGF(FATAL, "The number of dimensions specified for histogram %s does not match the type.", histSpec.name); + return nullptr; + } + + // set axis properties + for (std::size_t i = 0; i < nAxes; i++) { + TAxis* axis{getAxis(i, hist)}; + if (axis) { + if (histSpec.config.axes[i].title) { + axis->SetTitle((*histSpec.config.axes[i].title).data()); + } + + // this helps to have axes not only called 0,1,2... in ndim histos + if constexpr (std::is_base_of_v<THnBase, T>) { + if (histSpec.config.axes[i].name) { + axis->SetName((std::string(axis->GetName()) + "-" + *histSpec.config.axes[i].name).data()); + } + } + + // move the bin edges in case a variable binning was requested + if (!histSpec.config.axes[i].nBins) { + if (!std::is_sorted(std::begin(histSpec.config.axes[i].binEdges), std::end(histSpec.config.axes[i].binEdges))) { + LOGF(FATAL, "The bin edges in histogram %s are not in increasing order!", histSpec.name); + return nullptr; + } + axis->Set(nBins[i], histSpec.config.axes[i].binEdges.data()); + } + } + } + if (histSpec.callSumw2) { + hist->Sumw2(); + } + return hist; + } + + // create histogram and return it casted to the correct alternative held in HistPtr variant + template <typename T> + static HistPtr createHistVariant(const HistogramSpec& histSpec) + { + if (auto hist = castToVariant(createHist<T>(histSpec))) { + return *hist; + } else { + throw runtime_error("Histogram was not created properly."); + } + } + + // runtime version of the above + static HistPtr createHistVariant(const HistogramSpec& histSpec) + { + if (histSpec.config.type == HistType::kUndefinedHist) { + throw runtime_error("Histogram type was not specified."); + } else { + return HistogramCreationCallbacks.at(histSpec.config.type)(histSpec); + } + } + + // helper function to get the axis via index for any type of root histogram + template <typename T> + static TAxis* getAxis(const int i, std::shared_ptr<T>& hist) + { + if constexpr (std::is_base_of_v<THnBase, T>) { + return hist->GetAxis(i); + } else { + return (i == 0) ? hist->GetXaxis() + : (i == 1) ? hist->GetYaxis() + : (i == 2) ? hist->GetZaxis() + : nullptr; + } + } + + private: + static const std::map<HistType, std::function<HistPtr(const HistogramSpec&)>> HistogramCreationCallbacks; + + // helper function to generate the actual histograms + template <typename T> + static T* generateHist(const std::string& name, const std::string& title, const std::size_t nDim, + const int nBins[], const double lowerBounds[], const double upperBounds[]) + { + if constexpr (std::is_base_of_v<THnBase, T>) { + return new T(name.data(), title.data(), nDim, nBins, lowerBounds, upperBounds); + } else if constexpr (std::is_base_of_v<TH3, T>) { + return (nDim != 3) ? nullptr + : new T(name.data(), title.data(), nBins[0], lowerBounds[0], + upperBounds[0], nBins[1], lowerBounds[1], upperBounds[1], + nBins[2], lowerBounds[2], upperBounds[2]); + } else if constexpr (std::is_base_of_v<TH2, T>) { + return (nDim != 2) ? nullptr + : new T(name.data(), title.data(), nBins[0], lowerBounds[0], + upperBounds[0], nBins[1], lowerBounds[1], upperBounds[1]); + } else if constexpr (std::is_base_of_v<TH1, T>) { + return (nDim != 1) + ? nullptr + : new T(name.data(), title.data(), nBins[0], lowerBounds[0], upperBounds[0]); + } + return nullptr; + } + + // helper function to cast the actual histogram type (e.g. TH2F) to the correct interface type (e.g. TH2) that is stored in the HistPtr variant + template <typename T> + static std::optional<HistPtr> castToVariant(std::shared_ptr<TObject> obj) + { + if (obj->InheritsFrom(T::Class())) { + return std::static_pointer_cast<T>(obj); + } + return std::nullopt; + } + + template <typename T, typename Next, typename... Rest> + static std::optional<HistPtr> castToVariant(std::shared_ptr<TObject> obj) + { + if (auto hist = castToVariant<T>(obj)) { + return hist; + } + return castToVariant<Next, Rest...>(obj); + } + + static std::optional<HistPtr> castToVariant(std::shared_ptr<TObject> obj) + { + if (obj) { + // TProfile3D is TH3, TProfile2D is TH2, TH3 is TH1, TH2 is TH1, TProfile is TH1 + return castToVariant<THn, THnSparse, TProfile3D, TH3, TProfile2D, TH2, TProfile, TH1>(obj); + } + return std::nullopt; + } +}; + +//************************************************************************************************** +/** + * Static helper class to fill existing root histograms of any type. + * Contains functionality to fill once per call or a whole (filtered) table at once. + */ +//************************************************************************************************** +struct HistFiller { + // fill any type of histogram (if weight was requested it must be the last argument) + template <typename T, typename... Ts> + static void fillHistAny(std::shared_ptr<T>& hist, const Ts&... positionAndWeight) + { + constexpr int nArgs = sizeof...(Ts); + + constexpr bool validTH3 = (std::is_same_v<TH3, T> && (nArgs == 3 || nArgs == 4)); + constexpr bool validTH2 = (std::is_same_v<TH2, T> && (nArgs == 2 || nArgs == 3)); + constexpr bool validTH1 = (std::is_same_v<TH1, T> && (nArgs == 1 || nArgs == 2)); + constexpr bool validTProfile3D = (std::is_same_v<TProfile3D, T> && (nArgs == 4 || nArgs == 5)); + constexpr bool validTProfile2D = (std::is_same_v<TProfile2D, T> && (nArgs == 3 || nArgs == 4)); + constexpr bool validTProfile = (std::is_same_v<TProfile, T> && (nArgs == 2 || nArgs == 3)); + + constexpr bool validSimpleFill = validTH1 || validTH2 || validTH3 || validTProfile || validTProfile2D || validTProfile3D; + // unfortunately we dont know at compile the dimension of THn(Sparse) + constexpr bool validComplexFill = std::is_base_of_v<THnBase, T>; + + if constexpr (validSimpleFill) { + hist->Fill(static_cast<double>(positionAndWeight)...); + } else if constexpr (validComplexFill) { + double tempArray[] = {static_cast<double>(positionAndWeight)...}; + double weight{1.}; + constexpr int nArgsMinusOne = nArgs - 1; + if (hist->GetNdimensions() == nArgsMinusOne) { + weight = tempArray[nArgsMinusOne]; + } else if (hist->GetNdimensions() != nArgs) { + LOGF(FATAL, "The number of arguments in fill function called for histogram %s is incompatible with histogram dimensions.", hist->GetName()); + } + hist->Fill(tempArray, weight); + } else { + LOGF(FATAL, "The number of arguments in fill function called for histogram %s is incompatible with histogram dimensions.", hist->GetName()); + } + } + + // fill any type of histogram with columns (Cs) of a filtered table (if weight is requested it must reside the last specified column) + template <typename... Cs, typename R, typename T> + static void fillHistAny(std::shared_ptr<R>& hist, const T& table, const o2::framework::expressions::Filter& filter) + { + auto filtered = o2::soa::Filtered<T>{{table.asArrowTable()}, o2::framework::expressions::createSelection(table.asArrowTable(), filter)}; + for (auto& t : filtered) { + fillHistAny(hist, (*(static_cast<Cs>(t).getIterator()))...); + } + } + + // function that returns rough estimate for the size of a histogram in MB + template <typename T> + static double getSize(std::shared_ptr<T>& hist, double fillFraction = 1.) + { + double size{0.}; + if constexpr (std::is_base_of_v<TH1, T>) { + size = hist->GetNcells() * (HistFiller::getBaseElementSize(hist.get()) + ((hist->GetSumw2()->fN) ? sizeof(double) : 0.)); + } else if constexpr (std::is_base_of_v<THn, T>) { + size = hist->GetNbins() * (HistFiller::getBaseElementSize(hist.get()) + ((hist->GetSumw2() != -1.) ? sizeof(double) : 0.)); + } else if constexpr (std::is_base_of_v<THnSparse, T>) { + // THnSparse has massive overhead and should only be used when histogram is large and a very small fraction of bins is filled + double nBinsTotal = 1.; + int compCoordSize = 0; // size required to store a compact coordinate representation + for (int d = 0; d < hist->GetNdimensions(); ++d) { + int nBins = hist->GetAxis(d)->GetNbins() + 2; + nBinsTotal *= nBins; + + // number of bits needed to store compact coordinates + int b = 1; + while (nBins /= 2) { + ++b; + } + compCoordSize += b; + } + compCoordSize = (compCoordSize + 7) / 8; // turn bits into bytes + + // THnSparse stores the data in an array of chunks (THnSparseArrayChunk), each containing a fixed number of bins (e.g. 1024 * 16) + double nBinsFilled = fillFraction * nBinsTotal; + int nCunks = ceil(nBinsFilled / hist->GetChunkSize()); + int chunkOverhead = sizeof(THnSparseArrayChunk); + + // each chunk holds array of compact bin-coordinates and an array of bin content (+ one of bin error if requested) + double binSize = compCoordSize + HistFiller::getBaseElementSize(hist.get()) + ((hist->GetSumw2() != -1.) ? sizeof(double) : 0.); + size = nCunks * (chunkOverhead + hist->GetChunkSize() * binSize); + // since THnSparse must keep track of all the stored bins, it stores a map that + // relates the compact bin coordinates (or a hash thereof) to a linear index + // this index determines in which chunk and therein at which position to find / store bin coordinate and content + size += nBinsFilled * 3 * sizeof(Long64_t); // hash, key, value; not sure why 3 are needed here... + } + return size / 1048576.; + } + + private: + // helper function to determine base element size of histograms (in bytes) + // the complicated casting gymnastics are needed here since we only store the interface types in the registry + template <typename T> + static int getBaseElementSize(T* ptr) + { + if constexpr (std::is_base_of_v<TH1, T> || std::is_base_of_v<THnSparse, T>) { + return getBaseElementSize<TArrayD, TArrayF, TArrayC, TArrayI, TArrayC, TArrayL>(ptr); + } else { + return getBaseElementSize<double, float, int, short, char, long>(ptr); + } + } + + template <typename T, typename Next, typename... Rest, typename P> + static int getBaseElementSize(P* ptr) + { + if (auto size = getBaseElementSize<T>(ptr)) { + return size; + } + return getBaseElementSize<Next, Rest...>(ptr); + } + + template <typename B, typename T> + static int getBaseElementSize(T* ptr) + { + if constexpr (std::is_base_of_v<THn, T>) { + if (dynamic_cast<THnT<B>*>(ptr)) { + return sizeof(B); + } + } else if constexpr (std::is_base_of_v<THnSparse, T>) { + if (dynamic_cast<THnSparseT<B>*>(ptr)) { + TDataMember* dm = B::Class()->GetDataMember("fArray"); + return dm ? dm->GetDataType()->Size() : 0; + } + } else if constexpr (std::is_base_of_v<TH1, T>) { + if (auto arrayPtr = dynamic_cast<B*>(ptr)) { + return sizeof(arrayPtr->At(0)); + } + } + return 0; + }; }; -/// Histogram registry for an analysis task that allows to define needed histograms -/// and serves as the container/wrapper to fill them +//************************************************************************************************** +/** + * Histogram name object for runtime use that provides the associated hash and a first guess for the index in the registry. + */ +//************************************************************************************************** +struct HistName { + constexpr HistName(char const* const name) + : str(name), + hash(compile_time_hash(str)), + idx(hash & 0x1FF) + { + } + char const* const str{}; + const uint32_t hash{}; + const uint32_t idx{}; +}; + +//************************************************************************************************** +/** + * HistogramRegistry that can be used to store and fill histograms of any type. + */ +//************************************************************************************************** class HistogramRegistry { public: - HistogramRegistry(char const* const name_, bool enable, std::vector<HistogramSpec> specs) - : name(name_), - enabled(enable), - mRegistryKey(), - mRegistryValue() + HistogramRegistry(char const* const name, std::vector<HistogramSpec> histSpecs = {}, OutputObjHandlingPolicy policy = OutputObjHandlingPolicy::AnalysisObject, bool sortHistos = true, bool createRegistryDir = false) + : mName(name), mPolicy(policy), mRegistryKey(), mRegistryValue(), mSortHistos(sortHistos), mCreateRegistryDir(createRegistryDir) { mRegistryKey.fill(0u); - for (auto& spec : specs) { - insert(spec); + for (auto& histSpec : histSpecs) { + insert(histSpec); } } + // functions to add histograms to the registry + void add(const HistogramSpec& histSpec); + void add(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2 = false); + void add(char const* const name, char const* const title, HistType histType, std::vector<AxisSpec> axes, bool callSumw2 = false); + void addClone(const std::string& source, const std::string& target); + + // function to query if name is already in use + bool contains(const HistName& histName); + + // gets the underlying histogram pointer + // we cannot automatically infer type here so it has to be explicitly specified + // -> get<TH1>(), get<TH2>(), get<TH3>(), get<THn>(), get<THnSparse>(), get<TProfile>(), get<TProfile2D>(), get<TProfile3D>() /// @return the histogram registered with name @a name - auto& get(char const* const name) const + template <typename T, typename H> + std::shared_ptr<T>& get(const H& histName) { - const uint32_t id = compile_time_hash(name); - const uint32_t i = imask(id); - if (O2_BUILTIN_LIKELY(id == mRegistryKey[i])) { - return mRegistryValue[i]; - } - for (auto j = 1u; j < MAX_REGISTRY_SIZE; ++j) { - if (id == mRegistryKey[imask(j + i)]) { - return mRegistryValue[imask(j + i)]; + //static_assert(is_const_str<H>::value, R"(Please use compile-time hashed strings for HistogramRegistry access: "histName" -> HIST("histName")!)"); + if constexpr (is_const_str<H>::value) { + if (auto histPtr = std::get_if<std::shared_ptr<T>>(&mRegistryValue[getHistIndex(histName)])) { + return *histPtr; + } else { + throw runtime_error_f(R"(Histogram type specified in get<>(HIST("%s")) does not match the actual type of the histogram!)", histName.str); } + } else { + throw runtime_error_f(R"(Please use compile-time hashed strings for HistogramRegistry access: "histName" -> HIST("histName")!)"); } - throw std::runtime_error("No match found!"); } /// @return the histogram registered with name @a name - auto& operator()(char const* const name) const + template <typename T, typename H> + auto& operator()(const H& name) { - return get(name); + return get<T>(name); } - // @return the associated OutputSpec + /// @return the associated OutputSpec OutputSpec const spec() { - ConcreteDataMatcher matcher{"HIST", "\0", 0}; - strncpy(matcher.description.str, this->name.data(), 16); - return OutputSpec{OutputLabel{this->name}, matcher}; + ConcreteDataMatcher matcher{"ATSK", "\0", 0}; + strncpy(matcher.description.str, mName.data(), 16); + return OutputSpec{OutputLabel{mName}, matcher}; } OutputRef ref() { - return OutputRef{this->name, 0}; + return OutputRef{std::string{mName}, 0, o2::header::Stack{OutputObjHeader{mPolicy, OutputObjSourceType::HistogramRegistrySource, mTaskHash}}}; + } + + void setHash(uint32_t hash) + { + mTaskHash = hash; + } + + TList* operator*(); + + // fill hist with values + template <typename... Ts, typename H> + void fill(const H& histName, Ts&&... positionAndWeight) + { + //static_assert(is_const_str<H>::value, R"(Please use compile-time hashed strings for HistogramRegistry filling: "histName" -> HIST("histName")!)"); + if constexpr (is_const_str<H>::value) { + std::visit([&positionAndWeight...](auto&& hist) { HistFiller::fillHistAny(hist, std::forward<Ts>(positionAndWeight)...); }, mRegistryValue[getHistIndex(histName)]); + } else { + LOGF(FATAL, R"(Please use compile-time hashed strings for HistogramRegistry filling: "histName" -> HIST("histName")!)"); + } + } + + // fill hist with content of (filtered) table columns + template <typename... Cs, typename T, typename H> + void fill(const H& histName, const T& table, const o2::framework::expressions::Filter& filter) + { + //static_assert(is_const_str<H>::value, R"(Please use compile-time hashed strings for HistogramRegistry filling: "histName" -> HIST("histName")!)"); + if constexpr (is_const_str<H>::value) { + std::visit([&table, &filter](auto&& hist) { HistFiller::fillHistAny<Cs...>(hist, table, filter); }, mRegistryValue[getHistIndex(histName)]); + } else { + LOGF(FATAL, R"(Please use compile-time hashed strings for HistogramRegistry filling: "histName" -> HIST("histName")!)"); + } } - /// lookup distance counter for benchmarking + // get rough estimate for size of histogram stored in registry + double getSize(const HistName& histName, double fillFraction = 1.); + + // get rough estimate for size of all histograms stored in registry + double getSize(double fillFraction = 1.); + + // print summary of the histograms stored in registry + void print(bool showAxisDetails = false); + + // lookup distance counter for benchmarking mutable uint32_t lookup = 0; private: - void insert(HistogramSpec& spec) - { - uint32_t i = imask(spec.id); - for (auto j = 0u; j < MAX_REGISTRY_SIZE; ++j) { - if (mRegistryValue[imask(j + i)].get() == nullptr) { - mRegistryKey[imask(j + i)] = spec.id; - mRegistryValue[imask(j + i)] = {std::make_unique<TH1F>(spec.name.data(), spec.readableName.data(), spec.config.nBins, spec.config.xmin, spec.config.xmax)}; - lookup += j; + // create histogram from specification and insert it into the registry + void insert(const HistogramSpec& histSpec); + + // clone an existing histogram and insert it into the registry + template <typename T> + void insertClone(const HistName& histName, const std::shared_ptr<T>& originalHist) + { + validateHash(histName.hash, histName.str); + for (auto i = 0u; i < MAX_REGISTRY_SIZE; ++i) { + TObject* rawPtr = nullptr; + std::visit([&](const auto& sharedPtr) { rawPtr = sharedPtr.get(); }, mRegistryValue[imask(histName.idx + i)]); + if (!rawPtr) { + registerName(histName.str); + mRegistryKey[imask(histName.idx + i)] = histName.hash; + mRegistryValue[imask(histName.idx + i)] = std::shared_ptr<T>(static_cast<T*>(originalHist->Clone(histName.str))); + lookup += i; return; } } - throw std::runtime_error("Internal array is full."); + LOGF(FATAL, "Internal array of HistogramRegistry %s is full.", mName); + } + + void validateHash(const uint32_t hash, const char* name); + + // helper function to find the histogram position in the registry + template <typename T> + uint32_t getHistIndex(const T& histName) + { + if (O2_BUILTIN_LIKELY(histName.hash == mRegistryKey[histName.idx])) { + return histName.idx; + } + for (auto i = 1u; i < MAX_REGISTRY_SIZE; ++i) { + if (histName.hash == mRegistryKey[imask(histName.idx + i)]) { + return imask(histName.idx + i); + } + } + throw runtime_error_f(R"(Could not find histogram "%s" in HistogramRegistry "%s"!)", histName.str, mName.data()); } - inline constexpr uint32_t imask(uint32_t i) const + constexpr uint32_t imask(uint32_t i) const { - return i & mask; + return i & MASK; } - std::string name; - bool enabled; - /// The maximum number of histograms in buffer is currently set to 512 - /// which seems to be both reasonably large and allowing for very fast lookup - static constexpr uint32_t mask = 0x1FF; - static constexpr uint32_t MAX_REGISTRY_SIZE = mask + 1; - std::array<uint32_t, MAX_REGISTRY_SIZE> mRegistryKey; - std::array<std::unique_ptr<TH1>, MAX_REGISTRY_SIZE> mRegistryValue; -}; + // helper function to create resp. find the subList defined by path + TList* getSubList(TList* list, std::deque<std::string>& path); + + // helper function to split user defined path/to/hist/name string + std::deque<std::string> splitPath(const std::string& pathAndNameUser); -} // namespace framework + // helper function that checks if name of histogram is reasonable and keeps track of names already in use + void registerName(const std::string& name); -} // namespace o2 + std::string mName{}; + OutputObjHandlingPolicy mPolicy{}; + bool mCreateRegistryDir{}; + bool mSortHistos{}; + uint32_t mTaskHash{}; + std::vector<std::string> mRegisteredNames{}; + + // The maximum number of histograms in buffer is currently set to 512 + // which seems to be both reasonably large and allowing for very fast lookup + static constexpr uint32_t MASK{0x1FF}; + static constexpr uint32_t MAX_REGISTRY_SIZE{MASK + 1}; + std::array<uint32_t, MAX_REGISTRY_SIZE> mRegistryKey{}; + std::array<HistPtr, MAX_REGISTRY_SIZE> mRegistryValue{}; +}; +} // namespace o2::framework #endif // FRAMEWORK_HISTOGRAMREGISTRY_H_ diff --git a/Framework/Core/include/Framework/InputRecord.h b/Framework/Core/include/Framework/InputRecord.h index b41614bd8f58d..47813bdb89757 100644 --- a/Framework/Core/include/Framework/InputRecord.h +++ b/Framework/Core/include/Framework/InputRecord.h @@ -17,6 +17,7 @@ #include "Framework/InputSpan.h" #include "Framework/TableConsumer.h" #include "Framework/Traits.h" +#include "Framework/RuntimeError.h" #include "MemoryResources/Types.h" #include "Headers/DataHeader.h" @@ -29,7 +30,6 @@ #include <vector> #include <cstring> #include <cassert> -#include <exception> #include <memory> #include <type_traits> @@ -139,7 +139,7 @@ class InputRecord if (mProperty == OwnershipProperty::Unknown) { mProperty = other.mProperty; } else if (mProperty != other.mProperty) { - throw std::runtime_error("Attemp to change resource control"); + throw runtime_error("Attemp to change resource control"); } } @@ -156,7 +156,7 @@ class InputRecord if (mProperty == OwnershipProperty::Unknown) { mProperty = other.mProperty; } else if (mProperty != other.mProperty) { - throw std::runtime_error("Attemp to change resource control"); + throw runtime_error("Attemp to change resource control"); } return *this; } @@ -180,13 +180,13 @@ class InputRecord DataRef getByPos(int pos, int part = 0) const { if (pos >= mSpan.size() || pos < 0) { - throw std::runtime_error("Unknown message requested at position " + std::to_string(pos)); + throw runtime_error_f("Unknown message requested at position %d", pos); } if (part > 0 && part >= getNofParts(pos)) { - throw std::runtime_error("Invalid message part index at " + std::to_string(pos) + ":" + std::to_string(part)); + throw runtime_error_f("Invalid message part index at %d:%d", pos, part); } if (pos >= mInputsSchema.size()) { - throw std::runtime_error("Unknown schema at position" + std::to_string(pos)); + throw runtime_error_f("Unknown schema at position %d", pos); } auto ref = mSpan.get(pos, part); ref.spec = &mInputsSchema[pos].matcher; @@ -232,8 +232,11 @@ class InputRecord } ref = this->getByPos(pos, part); } catch (const std::exception& e) { - throw std::runtime_error("Unknown argument requested " + std::string(binding) + - " - " + e.what()); + if constexpr (std::is_same_v<std::decay_t<R>, std::string>) { + throw runtime_error_f("Unknown argument requested %s - %s", binding.c_str(), e.what()); + } else { + throw runtime_error_f("Unknown argument requested %s - %s", binding, e.what()); + } } } else if constexpr (std::is_same_v<std::decay_t<R>, DataRef>) { ref = binding; @@ -296,13 +299,14 @@ class InputRecord auto header = header::get<const header::DataHeader*>(ref.header); assert(header); if (sizeof(typename T::value_type) > 1 && header->payloadSerializationMethod != o2::header::gSerializationMethodNone) { - throw std::runtime_error("Inconsistent serialization method for extracting span"); + throw runtime_error("Inconsistent serialization method for extracting span"); } using ValueT = typename T::value_type; if (header->payloadSize % sizeof(ValueT)) { - throw std::runtime_error("Inconsistent type and payload size at " + std::string(ref.spec->binding) + "(" + DataSpecUtils::describe(*ref.spec) + ")" + - ": type size " + std::to_string(sizeof(ValueT)) + - " payload size " + std::to_string(header->payloadSize)); + throw runtime_error(("Inconsistent type and payload size at " + std::string(ref.spec->binding) + "(" + DataSpecUtils::describe(*ref.spec) + ")" + + ": type size " + std::to_string(sizeof(ValueT)) + + " payload size " + std::to_string(header->payloadSize)) + .c_str()); } return gsl::span<ValueT const>(reinterpret_cast<ValueT const*>(ref.payload), header->payloadSize / sizeof(ValueT)); @@ -336,10 +340,10 @@ class InputRecord std::swap(const_cast<NonConstT&>(container), *object); return container; } else { - throw std::runtime_error("No supported conversion function for ROOT serialized message"); + throw runtime_error("No supported conversion function for ROOT serialized message"); } } else { - throw std::runtime_error("Attempt to extract object from message with unsupported serialization type"); + throw runtime_error("Attempt to extract object from message with unsupported serialization type"); } } else { static_assert(always_static_assert_v<T>, "unsupported code path"); @@ -358,7 +362,7 @@ class InputRecord if (method != o2::header::gSerializationMethodNone) { // FIXME: we could in principle support serialized content here as well if we // store all extracted objects internally and provide cleanup - throw std::runtime_error("Can not extract a plain object from serialized message"); + throw runtime_error("Can not extract a plain object from serialized message"); } return *reinterpret_cast<T const*>(ref.payload); @@ -387,7 +391,7 @@ class InputRecord std::unique_ptr<ValueT const, Deleter<ValueT const>> result(container.release(), Deleter<ValueT const>(true)); return result; } - throw std::runtime_error("unsupported code path"); + throw runtime_error("unsupported code path"); } else if (method == o2::header::gSerializationMethodROOT) { // This supports the common case of retrieving a root object and getting pointer. // Notice that this will return a copy of the actual contents of the buffer, because @@ -400,7 +404,7 @@ class InputRecord std::unique_ptr<ValueT const, Deleter<ValueT const>> result(DataRefUtils::as<ROOTSerialized<ValueT>>(ref).release()); return result; } else { - throw std::runtime_error("Attempt to extract object from message with unsupported serialization type"); + throw runtime_error("Attempt to extract object from message with unsupported serialization type"); } } else if constexpr (has_root_dictionary<T>::value) { // retrieving ROOT objects follows the pointer approach, i.e. T* has to be specified @@ -416,7 +420,7 @@ class InputRecord auto method = header->payloadSerializationMethod; if (method == o2::header::gSerializationMethodNone) { // this code path is only selected if the type is non-messageable - throw std::runtime_error( + throw runtime_error( "Type mismatch: attempt to extract a non-messagable object " "from message with unserialized data"); } else if (method == o2::header::gSerializationMethodROOT) { @@ -426,7 +430,7 @@ class InputRecord std::unique_ptr<T const, Deleter<T const>> result(DataRefUtils::as<ROOTSerialized<T>>(ref).release()); return result; } else { - throw std::runtime_error("Attempt to extract object from message with unsupported serialization type"); + throw runtime_error("Attempt to extract object from message with unsupported serialization type"); } } } @@ -452,11 +456,19 @@ class InputRecord bool isValid(char const* s) const; bool isValid(int pos) const; + /// @return the total number of inputs in the InputRecord. Notice that these will include + /// both valid and invalid inputs (i.e. inputs which have not arrived yet), depending + /// on the CompletionPolicy you have (using the default policy all inputs will be valid). size_t size() const { return mSpan.size(); } + /// @return the total number of valid inputs in the InputRecord. + /// Invalid inputs might happen if the CompletionPolicy allows + /// incomplete records to be consumed or processed. + size_t countValidInputs() const; + template <typename T> using IteratorBase = std::iterator<std::forward_iterator_tag, T>; diff --git a/Framework/Core/include/Framework/InputSpec.h b/Framework/Core/include/Framework/InputSpec.h index 87dbed0af3246..2a21f9d161b52 100644 --- a/Framework/Core/include/Framework/InputSpec.h +++ b/Framework/Core/include/Framework/InputSpec.h @@ -13,14 +13,15 @@ #include "Framework/Lifetime.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/DataDescriptorMatcher.h" +#include "Framework/ConfigParamSpec.h" #include <string> #include <ostream> +#if !defined(__CLING__) && !defined(__ROOTCLING__) #include <variant> +#endif -namespace o2 -{ -namespace framework +namespace o2::framework { /// A selector for some kind of data being processed, either in @@ -28,23 +29,48 @@ namespace framework /// specific payloads in a timeframe. struct InputSpec { /// Create a fully qualified InputSpec - InputSpec(std::string binding_, header::DataOrigin origin_, header::DataDescription description_, header::DataHeader::SubSpecificationType subSpec_, enum Lifetime lifetime_ = Lifetime::Timeframe); + InputSpec(std::string binding_, + header::DataOrigin origin_, + header::DataDescription description_, + header::DataHeader::SubSpecificationType subSpec_, + enum Lifetime lifetime_ = Lifetime::Timeframe, + std::vector<ConfigParamSpec> const& metadata_ = {}); /// Create a fully qualified InputSpec (alternative syntax) - InputSpec(std::string binding_, ConcreteDataMatcher const& dataType, enum Lifetime lifetime_ = Lifetime::Timeframe); + InputSpec(std::string binding_, + ConcreteDataMatcher const& dataType, + enum Lifetime lifetime_ = Lifetime::Timeframe, + std::vector<ConfigParamSpec> const& metadata_ = {}); /// Create a fully qualified InputSpec where the subSpec is 0 - InputSpec(std::string binding_, header::DataOrigin origin_, header::DataDescription description_, enum Lifetime lifetime_ = Lifetime::Timeframe); + InputSpec(std::string binding_, + header::DataOrigin origin_, + header::DataDescription description_, + enum Lifetime lifetime_ = Lifetime::Timeframe, + std::vector<ConfigParamSpec> const& metadata_ = {}); /// Create an InputSpec which does not check for the subSpec. - InputSpec(std::string binding_, ConcreteDataTypeMatcher const& dataType, enum Lifetime lifetime_ = Lifetime::Timeframe); - InputSpec(std::string binding, data_matcher::DataDescriptorMatcher&& matcher); + InputSpec(std::string binding_, + ConcreteDataTypeMatcher const& dataType, + enum Lifetime lifetime_ = Lifetime::Timeframe, + std::vector<ConfigParamSpec> const& metadata_ = {}); + InputSpec(std::string binding, + data_matcher::DataDescriptorMatcher&& matcher, + std::vector<ConfigParamSpec> const& metadata_ = {}); + /// A mnemonic name for the input spec. std::string binding; + + /// The actual matcher for the input spec. +#if !defined(__CLING__) && !defined(__ROOTCLING__) std::variant<ConcreteDataMatcher, data_matcher::DataDescriptorMatcher> matcher; +#endif + enum Lifetime lifetime; + /// A set of configurables which can be used to customise the InputSpec. + std::vector<ConfigParamSpec> metadata; + friend std::ostream& operator<<(std::ostream& stream, InputSpec const& arg); bool operator==(InputSpec const& that) const; }; -} // namespace framework } // namespace o2 #endif diff --git a/Framework/Core/include/Framework/Kernels.h b/Framework/Core/include/Framework/Kernels.h index a53eae4da1f26..75dc919c9fd75 100644 --- a/Framework/Core/include/Framework/Kernels.h +++ b/Framework/Core/include/Framework/Kernels.h @@ -21,181 +21,68 @@ #include <string> -namespace arrow -{ -class Array; -class DataType; - -namespace compute -{ -class FunctionContext; -} // namespace compute -} // namespace arrow - namespace o2::framework { - -struct ARROW_EXPORT HashByColumnOptions { - std::string columnName; -}; - -/// A kernel which provides a unique hash based on the contents of a given -/// column -/// * The input datum has to be a table like object. -/// * The output datum will be a column of integers which define the -/// category. -class ARROW_EXPORT HashByColumnKernel : public arrow::compute::UnaryKernel -{ - public: - HashByColumnKernel(HashByColumnOptions options = {}); - arrow::Status Call(arrow::compute::FunctionContext* ctx, - arrow::compute::Datum const& table, - arrow::compute::Datum* hashes) override; - -#pragma GCC diagnostic push -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Winconsistent-missing-override" -#endif // __clang__ - - std::shared_ptr<arrow::DataType> out_type() const final - { - return mType; - } -#pragma GCC diagnostic pop - - private: - HashByColumnOptions mOptions; - std::shared_ptr<arrow::DataType> mType; -}; - -template <typename T> -struct ARROW_EXPORT GroupByOptions { - std::string columnName; - T size; -}; - -/// Build ranges -template <typename T, typename ARRAY> -class ARROW_EXPORT SortedGroupByKernel : public arrow::compute::UnaryKernel -{ - public: - explicit SortedGroupByKernel(GroupByOptions<T> options = {}) : mOptions(options){}; - arrow::Status Call(arrow::compute::FunctionContext* ctx, - arrow::compute::Datum const& table, - arrow::compute::Datum* outputRanges) override - { - using namespace arrow; - if (table.kind() == arrow::compute::Datum::TABLE) { - auto atable = util::get<std::shared_ptr<arrow::Table>>(table.value); - auto columnIndex = atable->schema()->GetFieldIndex(mOptions.columnName); - auto chunkedArray = atable->column(columnIndex); - return doGrouping(chunkedArray, outputRanges); - }; - return arrow::Status::OK(); - }; -#pragma GCC diagnostic push -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Winconsistent-missing-override" -#endif // __clang__ - std::shared_ptr<arrow::DataType> out_type() const final - { - return mType; - } -#pragma GCC diagnostic pop - - private: - arrow::Status doGrouping(std::shared_ptr<arrow::ChunkedArray> chunkedArray, arrow::compute::Datum* outputRanges) - { - o2::framework::TableBuilder builder; - auto writer = builder.persist<T, T, T>({"start", "count", "index"}); - auto zeroChunk = std::static_pointer_cast<ARRAY>(chunkedArray->chunk(0)); - if (zeroChunk->length() == 0) { - *outputRanges = std::move(builder.finalize()); - return arrow::Status::OK(); - } - T currentIndex = 0; - T currentCount = 0; - T currentOffset = 0; - for (auto ci = 0; ci < chunkedArray->num_chunks(); ++ci) { - auto chunk = chunkedArray->chunk(ci); - T const* data = std::static_pointer_cast<ARRAY>(chunk)->raw_values(); - for (auto ai = 0; ai < chunk->length(); ++ai) { - if (currentIndex == data[ai]) { - currentCount++; - } else { - writer(0, currentOffset, currentCount, currentIndex); - currentOffset += currentCount; - while (data[ai] - currentIndex > 1) { - writer(0, currentOffset, 0, ++currentIndex); - } - currentIndex++; - currentCount = 1; - } - } - } - writer(0, currentOffset, currentCount, currentIndex); - while (currentIndex < mOptions.size - 1) { - writer(0, currentOffset, 0, ++currentIndex); - } - *outputRanges = std::move(builder.finalize()); - return arrow::Status::OK(); - } - std::shared_ptr<arrow::DataType> mType; - GroupByOptions<T> mOptions; -}; - -/// Slice a given table is a vector of tables each containing a slice. -/// @a outputSlices the arrow tables in which the original @a inputTable +/// Slice a given table in a vector of tables each containing a slice. +/// @a slices the arrow tables in which the original @a input /// is split into. /// @a offset the offset in the original table at which the corresponding /// slice was split. -/// Slice a given table is a vector of tables each containing a slice. template <typename T> -arrow::Status sliceByColumn(arrow::compute::FunctionContext* context, - std::string const& key, - T size, - arrow::compute::Datum const& inputTable, - std::vector<arrow::compute::Datum>* outputSlices, - std::vector<uint64_t>* offsets = nullptr) +auto sliceByColumn(char const* key, + std::shared_ptr<arrow::Table> const& input, + T fullSize, + std::vector<arrow::Datum>* slices, + std::vector<uint64_t>* offsets = nullptr) { - if (inputTable.kind() != arrow::compute::Datum::TABLE) { - return arrow::Status::Invalid("Input Datum was not a table"); - } - // build all the ranges on the fly. - arrow::compute::Datum outRanges; - auto table = arrow::util::get<std::shared_ptr<arrow::Table>>(inputTable.value); - o2::framework::SortedGroupByKernel<T, soa::arrow_array_for_t<T>> kernel{GroupByOptions<T>{key, size}}; + arrow::Datum value_counts; + auto options = arrow::compute::CountOptions::Defaults(); + ARROW_ASSIGN_OR_RAISE(value_counts, + arrow::compute::CallFunction("value_counts", {input->GetColumnByName(key)}, + &options)); + auto pair = static_cast<arrow::StructArray>(value_counts.array()); + auto values = static_cast<arrow::NumericArray<typename detail::ConversionTraits<T>::ArrowType>>(pair.field(0)->data()); + auto counts = static_cast<arrow::NumericArray<arrow::Int64Type>>(pair.field(1)->data()); + + // create slices and offsets + auto offset = 0; + auto count = 0; + + auto size = values.length(); + + auto makeSlice = [&](T count) { + slices->emplace_back(arrow::Datum{input->Slice(offset, count)}); + if (offsets) { + offsets->emplace_back(offset); + } + }; - ARROW_RETURN_NOT_OK(kernel.Call(context, inputTable, &outRanges)); - auto ranges = arrow::util::get<std::shared_ptr<arrow::Table>>(outRanges.value); - outputSlices->reserve(ranges->num_rows()); - if (offsets) { - offsets->reserve(ranges->num_rows()); + auto current = 0; + auto v = values.Value(0); + while (v - current >= 1) { + makeSlice(0); + ++current; } - auto startChunks = ranges->column(0); - assert(startChunks->num_chunks() == 1); - auto countChunks = ranges->column(1); - assert(countChunks->num_chunks() == 1); - auto startData = std::static_pointer_cast<soa::arrow_array_for_t<T>>(startChunks->chunk(0))->raw_values(); - auto countData = std::static_pointer_cast<soa::arrow_array_for_t<T>>(countChunks->chunk(0))->raw_values(); - - for (auto ri = 0; ri < ranges->num_rows(); ++ri) { - auto start = startData[ri]; - auto count = countData[ri]; - auto schema = table->schema(); - std::vector<std::shared_ptr<arrow::ChunkedArray>> slicedColumns; - slicedColumns.reserve(schema->num_fields()); - // if (count != 0) { - for (auto ci = 0; ci < schema->num_fields(); ++ci) { - slicedColumns.emplace_back(table->column(ci)->Slice(start, count)); + for (auto r = 0; r < size - 1; ++r) { + count = counts.Value(r); + makeSlice(count); + offset += count; + auto nextValue = values.Value(r + 1); + auto value = values.Value(r); + while (nextValue - value > 1) { + makeSlice(0); + ++value; } - // } - outputSlices->emplace_back(arrow::compute::Datum(arrow::Table::Make(table->schema(), slicedColumns))); - if (offsets) { - offsets->emplace_back(start); + } + makeSlice(counts.Value(size - 1)); + + if (values.Value(size - 1) < fullSize - 1) { + for (auto v = values.Value(size - 1) + 1; v < fullSize; ++v) { + makeSlice(0); } } + return arrow::Status::OK(); } diff --git a/Framework/Core/include/Framework/Lifetime.h b/Framework/Core/include/Framework/Lifetime.h index 3c142d0afa081..9f7434aac1017 100644 --- a/Framework/Core/include/Framework/Lifetime.h +++ b/Framework/Core/include/Framework/Lifetime.h @@ -7,25 +7,36 @@ // In applying this license CERN does 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 FRAMEWORK_LIFETIME_H -#define FRAMEWORK_LIFETIME_H +#ifndef O2_FRAMEWORK_LIFETIME_H_ +#define O2_FRAMEWORK_LIFETIME_H_ -namespace o2 -{ -namespace framework +namespace o2::framework { /// Possible Lifetime of objects being exchanged by the DPL. -/// FIXME: currently only Timeframe behaves as expected. enum struct Lifetime { + /// A message which is associated to a timeframe. DPL will wait indefinitely for it by default. Timeframe, + /// Eventually a message whose content is retrieved from CCDB Condition, + /// Do not use for now QA, + /// Do not use for now. Transient, + /// A message which is created whenever a Timer expires Timer, - Enumeration + /// A message which is created immediately, with payload / containing a + /// single value which gets incremented for every / invokation. + Enumeration, + /// A message which is created every time a SIGUSR1 is received. + Signal, + /// An optional message. When data arrives, if not already part of the data, + /// a dummy entry will be generated. + /// This comes handy e.g. to handle Raw Data, since DataDistribution will provide + /// everything in one go so whatever is expected but not there, for whatever reason + /// will be substituted with a dummy entry. + Optional }; -} // namespace framework -} // namespace o2 -#endif +} // namespace o2::framework +#endif // O2_FRAMEWORK_LIFETIME_H_ diff --git a/Framework/Core/include/Framework/LifetimeHelpers.h b/Framework/Core/include/Framework/LifetimeHelpers.h index 2c358af5a095b..797ac65f68fba 100644 --- a/Framework/Core/include/Framework/LifetimeHelpers.h +++ b/Framework/Core/include/Framework/LifetimeHelpers.h @@ -7,8 +7,8 @@ // In applying this license CERN does 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 FRAMEWORK_LIFETIMEHELPERS_H -#define FRAMEWORK_LIFETIMEHELPERS_H +#ifndef O2_FRAMEWORK_LIFETIMEHELPERS_H_ +#define O2_FRAMEWORK_LIFETIMEHELPERS_H_ #include "Framework/ExpirationHandler.h" #include "Framework/PartRef.h" @@ -17,9 +17,7 @@ #include <functional> #include <string> -namespace o2 -{ -namespace framework +namespace o2::framework { struct ConcreteDataMatcher; @@ -33,6 +31,7 @@ struct LifetimeHelpers { /// Callback which creates a new timeslice as soon as one is available and /// uses an incremental number as timestamp. static ExpirationHandler::Creator enumDrivenCreation(size_t first, size_t last, size_t step, size_t inputTimeslice, size_t maxTimeSliceId); + /// Callback which creates a new timeslice when timer /// expires and there is not a compatible datadriven callback /// available. @@ -78,9 +77,12 @@ struct LifetimeHelpers { /// The payload of each message will contain an incremental number for each /// message being created. static ExpirationHandler::Handler enumerate(ConcreteDataMatcher const& spec, std::string const& sourceChannel); + + /// Create a dummy (empty) message every time a record expires, suing @a spec + /// as content of the payload. + static ExpirationHandler::Handler dummy(ConcreteDataMatcher const& spec, std::string const& sourceChannel); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework -#endif // FRAMEWORK_LIFETIMEHELPERS_H +#endif // O2_FRAMEWORK_LIFETIMEHELPERS_H_ diff --git a/Framework/Core/include/Framework/MessageContext.h b/Framework/Core/include/Framework/MessageContext.h index b5b9718a70be3..fca8d7069ea32 100644 --- a/Framework/Core/include/Framework/MessageContext.h +++ b/Framework/Core/include/Framework/MessageContext.h @@ -12,8 +12,10 @@ #include "Framework/DispatchControl.h" #include "Framework/FairMQDeviceProxy.h" +#include "Framework/RuntimeError.h" #include "Framework/TMessageSerializer.h" #include "Framework/TypeTraits.h" + #include "Headers/DataHeader.h" #include "MemoryResources/MemoryResources.h" @@ -22,7 +24,6 @@ #include <cassert> #include <functional> -#include <stdexcept> #include <string> #include <type_traits> #include <unordered_map> @@ -233,10 +234,10 @@ class MessageContext // FIXME: drop this repeated check and make sure at initial setup of devices that everything is fine // introduce error policy if (mFactory == nullptr) { - throw std::runtime_error(std::string("failed to get transport factory for channel ") + bindingChannel); + throw runtime_error_f("failed to get transport factory for channel %s", bindingChannel.c_str()); } if (mResource.isValid() == false) { - throw std::runtime_error(std::string("no memory resource for channel ") + bindingChannel); + throw runtime_error_f("no memory resource for channel %s", bindingChannel.c_str()); } } ~ContainerRefObject() override = default; @@ -406,7 +407,7 @@ class MessageContext // TODO: decide whether this is an error or not // can also check if the standard constructor can be dropped to make sure that // the ScopeHook is always set up with a context - throw std::runtime_error("No context available to schedule the context object"); + throw runtime_error("No context available to schedule the context object"); return base::operator()(ptr); } // keep the object alive and add to message list of the context diff --git a/Framework/Core/include/Framework/Monitoring.h b/Framework/Core/include/Framework/Monitoring.h new file mode 100644 index 0000000000000..26d4c1c38c34a --- /dev/null +++ b/Framework/Core/include/Framework/Monitoring.h @@ -0,0 +1,26 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_MONITORING_H_ +#define O2_FRAMEWORK_MONITORING_H_ + +#include "Framework/ServiceHandle.h" + +#include <Monitoring/Monitoring.h> + +namespace o2::framework +{ +template <> +struct ServiceKindExtractor<o2::monitoring::Monitoring> { + constexpr static ServiceKind kind = ServiceKind::Global; +}; +} // namespace o2::framework + +#endif // O2_FRAMEWORK_MONITORING_H_ diff --git a/Framework/Core/include/Framework/OutputObjHeader.h b/Framework/Core/include/Framework/OutputObjHeader.h index ba539382db286..e31bee11eb302 100644 --- a/Framework/Core/include/Framework/OutputObjHeader.h +++ b/Framework/Core/include/Framework/OutputObjHeader.h @@ -26,6 +26,12 @@ enum OutputObjHandlingPolicy : unsigned int { numPolicies }; +enum OutputObjSourceType : unsigned int { + OutputObjSource, + HistogramRegistrySource, + numTypes +}; + /// @struct OutputObjHeader /// @brief O2 header for OutputObj metadata struct OutputObjHeader : public BaseHeader { @@ -33,15 +39,18 @@ struct OutputObjHeader : public BaseHeader { constexpr static const o2::header::HeaderType sHeaderType = "OutObjMD"; constexpr static const o2::header::SerializationMethod sSerializationMethod = o2::header::gSerializationMethodNone; OutputObjHandlingPolicy mPolicy; + OutputObjSourceType mSourceType; uint32_t mTaskHash; constexpr OutputObjHeader() : BaseHeader(sizeof(OutputObjHeader), sHeaderType, sSerializationMethod, sVersion), mPolicy{OutputObjHandlingPolicy::AnalysisObject}, + mSourceType{OutputObjSourceType::OutputObjSource}, mTaskHash{0} {} - constexpr OutputObjHeader(OutputObjHandlingPolicy policy, uint32_t hash) + constexpr OutputObjHeader(OutputObjHandlingPolicy policy, OutputObjSourceType sourceType, uint32_t hash) : BaseHeader(sizeof(OutputObjHeader), sHeaderType, sSerializationMethod, sVersion), mPolicy{policy}, + mSourceType{sourceType}, mTaskHash{hash} {} constexpr OutputObjHeader(OutputObjHeader const&) = default; }; diff --git a/Framework/Core/include/Framework/OutputSpec.h b/Framework/Core/include/Framework/OutputSpec.h index 8bb008774661e..6f8357c127e37 100644 --- a/Framework/Core/include/Framework/OutputSpec.h +++ b/Framework/Core/include/Framework/OutputSpec.h @@ -14,7 +14,9 @@ #include "Framework/Lifetime.h" #include "Framework/ConcreteDataMatcher.h" +#if !defined(__CLING__) && !defined(__ROOTCLING__) #include <variant> +#endif namespace o2::framework { @@ -28,7 +30,9 @@ struct OutputLabel { /// topology. struct OutputSpec { OutputLabel binding; +#if !defined(__CLING__) && !defined(__ROOTCLING__) std::variant<ConcreteDataMatcher, ConcreteDataTypeMatcher> matcher; +#endif enum Lifetime lifetime = Lifetime::Timeframe; /// Build a fully qualified tuple for the OutputSpec diff --git a/Framework/Core/include/Framework/ParallelContext.h b/Framework/Core/include/Framework/ParallelContext.h index 5718f9d1ab75a..79c0de7d70a4f 100644 --- a/Framework/Core/include/Framework/ParallelContext.h +++ b/Framework/Core/include/Framework/ParallelContext.h @@ -8,12 +8,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef FRAMEWORK_PARALLELCONTEXT_H -#define FRAMEWORK_PARALLELCONTEXT_H +#ifndef O2_FRAMEWORK_PARALLELCONTEXT_H_ +#define O2_FRAMEWORK_PARALLELCONTEXT_H_ -namespace o2 -{ -namespace framework +#include "Framework/ServiceHandle.h" + +namespace o2::framework { /// Purpose of this class is to provide DataProcessors which @@ -31,6 +31,7 @@ namespace framework class ParallelContext { public: + constexpr static ServiceKind service_kind = ServiceKind::Global; // FIXME: find better names... rank1D and rank1DSize? ParallelContext(size_t index1D, size_t index1DSize) : mIndex1D{index1D}, @@ -46,6 +47,5 @@ class ParallelContext size_t mIndex1DSize; }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif diff --git a/Framework/Core/include/Framework/Plugins.h b/Framework/Core/include/Framework/Plugins.h new file mode 100644 index 0000000000000..eaa15703d94f1 --- /dev/null +++ b/Framework/Core/include/Framework/Plugins.h @@ -0,0 +1,64 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_PLUGINS_H_ +#define O2_FRAMEWORK_PLUGINS_H_ + +#include "Framework/AlgorithmSpec.h" +#include <cstring> + +namespace o2::framework +{ + +enum struct DplPluginKind : int { + // A plugin which can customise the workflow. Needs to return + // an object of kind o2::framework::WorkflowCustomizationService + CustomAlgorithm, + // A plugin which implements a ImGUI GUI. Needs to return an + // object of the kind o2::framework::DebugGUIImpl + DebugGUIImpl +}; + +} // namespace o2::framework + +/// An handle for a generic DPL plugin. +/// The handle is returned by the dpl_plugin_callback() +struct DPLPluginHandle { + void* instance; + char const* name; + enum o2::framework::DplPluginKind kind; + DPLPluginHandle* previous; +}; + +#define DEFINE_DPL_PLUGIN(NAME, KIND) \ + extern "C" { \ + DPLPluginHandle* dpl_plugin_callback(DPLPluginHandle* previous) \ + { \ + return new DPLPluginHandle{new NAME{}, strdup(#NAME), o2::framework::DplPluginKind::KIND, previous}; \ + } \ + } + +namespace o2::framework +{ +struct PluginManager { + template <typename T> + static T* getByName(DPLPluginHandle* handle, char const* name) + { + while (handle != nullptr) { + if (strncmp(handle->name, name, strlen(name)) == 0) { + return reinterpret_cast<T*>(handle->instance); + } + handle = handle->previous; + } + return nullptr; + } +}; +} // namespace o2::framework + +#endif // O2_FRAMEWORK_PLUGINS_H_ diff --git a/Framework/Core/include/Framework/ProcessingContext.h b/Framework/Core/include/Framework/ProcessingContext.h index c28d88ff556fe..264b0679019f8 100644 --- a/Framework/Core/include/Framework/ProcessingContext.h +++ b/Framework/Core/include/Framework/ProcessingContext.h @@ -10,15 +10,14 @@ #ifndef O2_FRAMEWORK_PROCESSINGCONTEXT_H_ #define O2_FRAMEWORK_PROCESSINGCONTEXT_H_ -#include "Framework/InputRecord.h" -#include "Framework/DataAllocator.h" - namespace o2::framework { struct ServiceRegistry; +struct DataAllocator; +struct InputRecord; -// This is a utility class to reduce the amount of boilerplate when defining +// This is a utility class to reduce the amount of boilerplate when defining // an algorithm. class ProcessingContext { diff --git a/Framework/Core/include/Framework/RawDeviceService.h b/Framework/Core/include/Framework/RawDeviceService.h index 05a9b6b23e7ce..700df3c9b62a4 100644 --- a/Framework/Core/include/Framework/RawDeviceService.h +++ b/Framework/Core/include/Framework/RawDeviceService.h @@ -7,14 +7,14 @@ // In applying this license CERN does 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 FRAMEWORK_RAWDEVICESERVICE_H -#define FRAMEWORK_RAWDEVICESERVICE_H +#ifndef O2_FRAMEWORK_RAWDEVICESERVICE_H_ +#define O2_FRAMEWORK_RAWDEVICESERVICE_H_ + +#include "Framework/ServiceHandle.h" class FairMQDevice; -namespace o2 -{ -namespace framework +namespace o2::framework { class DeviceSpec; @@ -26,11 +26,17 @@ class DeviceSpec; class RawDeviceService { public: + constexpr static ServiceKind service_kind = ServiceKind::Global; + virtual FairMQDevice* device() = 0; virtual void setDevice(FairMQDevice* device) = 0; virtual DeviceSpec const& spec() = 0; + /// Expose FairMQDevice::WaitFor method to avoid having to include + /// FairMQDevice.h. + /// + /// @a time in millisecond to sleep + virtual void waitFor(unsigned int time) = 0; }; -} // namespace framework -} // namespace o2 -#endif // FRAMEWORK_RAWDEVICESERVICE_H +} // namespace o2::framework +#endif // O2_FRAMEWORK_RAWDEVICESERVICE_H_ diff --git a/Framework/Core/include/Framework/RootAnalysisHelpers.h b/Framework/Core/include/Framework/RootAnalysisHelpers.h new file mode 100644 index 0000000000000..7341d330b736f --- /dev/null +++ b/Framework/Core/include/Framework/RootAnalysisHelpers.h @@ -0,0 +1,32 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ROOTANALYSISHELPERS_H_ +#define O2_FRAMEWORK_ROOTANALYSISHELPERS_H_ + +#include "Framework/TableConsumer.h" +#include <ROOT/RDataFrame.hxx> + +namespace o2::analysis +{ + +/// Do a single loop on all the entries of the @a input table +ROOT::RDataFrame doSingleLoopOn(std::unique_ptr<framework::TableConsumer>& input); + +/// Do a double loop on all the entries with the same value for the \a grouping +/// of the @a input table, where the entries for the outer index are prefixed +/// with `<name>_` while the entries for the inner loop are prefixed with +/// `<name>bar_`. +ROOT::RDataFrame doSelfCombinationsWith(std::unique_ptr<framework::TableConsumer>& input, + std::string name = "p", + std::string grouping = "eventID"); + +} // namespace o2::analysis + +#endif // O2_FRAMEWORK_ROOTANALYSISHELPERS_H_ diff --git a/Framework/Core/include/Framework/RootConfigParamHelpers.h b/Framework/Core/include/Framework/RootConfigParamHelpers.h index bd91e969d4dc5..31e1c35712f37 100644 --- a/Framework/Core/include/Framework/RootConfigParamHelpers.h +++ b/Framework/Core/include/Framework/RootConfigParamHelpers.h @@ -11,6 +11,7 @@ #define O2_FRAMEWORK_ROOTCONFIGPARAMHELPERS_H_ #include "Framework/ConfigParamSpec.h" +#include "Framework/RuntimeError.h" #include <TClass.h> #include <boost/property_tree/ptree_fwd.hpp> #include <type_traits> @@ -34,7 +35,7 @@ struct RootConfigParamHelpers { { auto cl = TClass::GetClass<T>(); if (!cl) { - throw std::runtime_error(std::string("Unable to convert object ") + typeid(T).name()); + throw runtime_error_f("Unable to convert object %s", typeid(T).name()); } return asConfigParamSpecsImpl(mainKey, cl, reinterpret_cast<void*>(const_cast<T*>(&proto))); diff --git a/Framework/Core/include/Framework/RootTableBuilderHelpers.h b/Framework/Core/include/Framework/RootTableBuilderHelpers.h index eb1074385dd98..5ead9d8e69412 100644 --- a/Framework/Core/include/Framework/RootTableBuilderHelpers.h +++ b/Framework/Core/include/Framework/RootTableBuilderHelpers.h @@ -14,6 +14,7 @@ #include "Framework/TableBuilder.h" #include "Framework/Logger.h" +#include <Rtypes.h> #include <arrow/stl.h> #include <arrow/type_traits.h> #include <arrow/table.h> @@ -22,6 +23,8 @@ #include <TTreeReader.h> #include <TTreeReaderValue.h> #include <TTreeReaderArray.h> +#include <TBuffer.h> +#include <TBufferFile.h> #include <vector> #include <string> @@ -53,45 +56,7 @@ struct TreeReaderValueTraits<TTreeReaderArray<VALUE>> { using ArrowType = arrow::ListType; }; -template <typename T> -struct ReaderHolder { - using Reader = TTreeReaderValue<T>; - using Type = T; - std::unique_ptr<Reader> reader; -}; - -template <typename T, int N> -struct ReaderHolder<T[N]> { - using Reader = TTreeReaderArray<T>; - using Type = T (&)[N]; - std::unique_ptr<Reader> reader; -}; - -struct ValueExtractor { - template <typename T> - static T deref(TTreeReaderValue<T>& rv) - { - return *rv; - } - - template <typename T> - static std::pair<typename TTreeReaderArray<T>::iterator, typename TTreeReaderArray<T>::iterator> deref(TTreeReaderArray<T>& rv) - { - return std::make_pair(rv.begin(), rv.end()); - } - - template <typename T> - static T deref(ReaderHolder<T>& holder) - { - return **holder.reader; - } - - template <typename T, int N> - static T* deref(ReaderHolder<T[N]>& holder) - { - return &((*holder.reader)[0]); - } -}; +static constexpr int PREBUFFER_SIZE = 32 * 1024; // When reading from a ROOT file special care must happen // because uint64_t is platform specific while ULong64_t is @@ -126,12 +91,92 @@ struct Remap64Bit<uint64_t[N]> { template <typename T> using Remap64Bit_t = typename Remap64Bit<T>::type; +template <typename T> +struct ReaderHolder { + using Reader = TTreeReaderValue<T>; + using Type = T; + + ReaderHolder(TBranch* branch, std::unique_ptr<Reader> reader_) + : reader{std::move(reader_)} + { + } + + ReaderHolder(ReaderHolder&& other) + : reader{std::move(other.reader)}, + pos{other.pos} + { + } + + ReaderHolder& operator=(ReaderHolder&& other) = delete; + + std::unique_ptr<Reader> reader; + int pos = 0; + Remap64Bit_t<T> buffer[PREBUFFER_SIZE]; + int itemSize = sizeof(T); +}; + +template <typename T, int N> +struct ReaderHolder<T[N]> { + using Reader = TTreeReaderArray<T>; + using Type = T (&)[N]; + + ReaderHolder(TBranch* branch, std::unique_ptr<Reader> reader_) + : reader{std::move(reader_)} + { + } + + ReaderHolder(ReaderHolder&& other) + : reader{std::move(other.reader)}, + pos{other.pos} + { + } + + ReaderHolder& operator=(ReaderHolder&& other) = delete; + + std::unique_ptr<Reader> reader; + int pos = 0; + Remap64Bit_t<T> buffer[PREBUFFER_SIZE * N]; + int itemSize = sizeof(T) * N; +}; + +struct BulkExtractor { + template <typename T> + static auto deref(ReaderHolder<T>& holder, size_t maxSize) + { + holder.buffer[holder.pos % PREBUFFER_SIZE] = **holder.reader; + holder.pos++; + if (holder.pos == maxSize) { + return BulkInfo<Remap64Bit_t<T> const*>{holder.buffer, maxSize % PREBUFFER_SIZE}; + } + // We flush only after PREBUFFER_SIZE items have been inserted + if ((holder.pos % PREBUFFER_SIZE) != 0) { + return BulkInfo<Remap64Bit_t<T> const*>{nullptr, 0}; + } + return BulkInfo<Remap64Bit_t<T> const*>{holder.buffer, PREBUFFER_SIZE}; + } + + template <typename T, int N> + static auto deref(ReaderHolder<T[N]>& holder, size_t maxSize) + { + memcpy(&holder.buffer[(holder.pos % PREBUFFER_SIZE) * N], &((*holder.reader)[0]), N * sizeof(T)); + holder.pos++; + if (holder.pos == maxSize) { + return BulkInfo<Remap64Bit_t<T> const*>{holder.buffer, maxSize % PREBUFFER_SIZE}; + } + // We flush only after PREBUFFER_SIZE items have been inserted + if ((holder.pos % PREBUFFER_SIZE) != 0) { + return BulkInfo<Remap64Bit_t<T> const*>{nullptr, 0}; + } + return BulkInfo<Remap64Bit_t<T> const*>{reinterpret_cast<T const*>(holder.buffer), PREBUFFER_SIZE}; + } +}; + template <typename T> struct HolderMaker { static auto make(TTreeReader& reader, char const* branchName) { using Reader = TTreeReaderValue<T>; - return std::move(ReaderHolder<T>{std::move(std::make_unique<Reader>(reader, branchName))}); + return ReaderHolder<T>{reader.GetTree()->GetBranch(branchName), std::move(std::make_unique<Reader>(reader, branchName))}; } }; @@ -140,7 +185,7 @@ struct HolderMaker<T[N]> { static auto make(TTreeReader& reader, char const* branchName) { using Reader = TTreeReaderArray<T>; - return std::move(ReaderHolder<T[N]>{std::move(std::make_unique<Reader>(reader, branchName))}); + return ReaderHolder<T[N]>{reader.GetTree()->GetBranch(branchName), std::move(std::make_unique<Reader>(reader, branchName))}; } }; @@ -148,22 +193,27 @@ template <typename C> struct ColumnReaderTrait { static auto createReader(TTreeReader& reader) { - return std::move(HolderMaker<Remap64Bit_t<typename C::type>>::make(reader, C::base::columnLabel())); + return HolderMaker<Remap64Bit_t<typename C::type>>::make(reader, C::base::columnLabel()); } }; struct RootTableBuilderHelpers { - template <typename... TTREEREADERVALUE> + /// Use bulk insertion when TTreeReaderValue everywhere + template <typename... T> static void convertTTree(TableBuilder& builder, TTreeReader& reader, - ReaderHolder<TTREEREADERVALUE>... holders) + ReaderHolder<T>... holders) { std::vector<std::string> branchNames = {holders.reader->GetBranchName()...}; + TTree* tree = reader.GetTree(); + size_t maxExtries = reader.GetEntries(true); + tree->SetCacheSize(maxExtries * (holders.itemSize + ...)); + (tree->AddBranchToCache(tree->GetBranch(holders.reader->GetBranchName()), true), ...); + tree->StopCacheLearningPhase(); - auto filler = builder.preallocatedPersist<typename std::decay_t<decltype(holders)>::Type...>(branchNames, reader.GetEntries(true)); - reader.Restart(); + auto filler = builder.bulkPersistChunked<Remap64Bit_t<typename std::decay_t<decltype(holders)>::Type>...>(branchNames, maxExtries); while (reader.Next()) { - filler(0, ValueExtractor::deref(holders)...); + filler(0, BulkExtractor::deref(holders, maxExtries)...); } } diff --git a/Framework/Core/include/Framework/ServiceHandle.h b/Framework/Core/include/Framework/ServiceHandle.h new file mode 100644 index 0000000000000..3f3759bd1e9ca --- /dev/null +++ b/Framework/Core/include/Framework/ServiceHandle.h @@ -0,0 +1,44 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SERVICEHANDLE_H_ +#define O2_FRAMEWORK_SERVICEHANDLE_H_ + +#include <string> + +namespace o2::framework +{ + +/// The kind of service we are asking for +enum struct ServiceKind { + /// A Service which is not thread safe, therefore all accesses to it must be mutexed. + Serial, + /// A Service which is thread safe and therefore can be used by many threads the same time without risk + Global, + /// A Service which is specific to a given thread in a thread pool + Stream +}; + +/// Handle to the service hash must be calculated +/// using TypeIdHelper::uniqueId<BaseClass>() so that +/// we can retrieve the service by its baseclass. +struct ServiceHandle { + /// Unique hash associated to the type of service. + unsigned int hash; + /// Type erased pointer to a service + void* instance; + /// Kind of service + ServiceKind kind; + /// Mnemonic name to use for the service. + std::string name = "unknown"; +}; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_SERVICEHANDLE_H_ diff --git a/Framework/Core/include/Framework/ServiceRegistry.h b/Framework/Core/include/Framework/ServiceRegistry.h index 0700eace7f96c..b9bf54eddacce 100644 --- a/Framework/Core/include/Framework/ServiceRegistry.h +++ b/Framework/Core/include/Framework/ServiceRegistry.h @@ -10,65 +10,221 @@ #ifndef O2_FRAMEWORK_SERVICEREGISTRY_H_ #define O2_FRAMEWORK_SERVICEREGISTRY_H_ +#include "Framework/ServiceHandle.h" +#include "Framework/ServiceSpec.h" +#include "Framework/ServiceRegistryHelpers.h" #include "Framework/CompilerBuiltins.h" #include "Framework/TypeIdHelpers.h" +#include "Framework/RuntimeError.h" #include <algorithm> #include <array> -#include <exception> #include <functional> #include <string> #include <type_traits> #include <typeinfo> -#include <stdexcept> +#include <thread> +#include <atomic> +#include <mutex> namespace o2::framework { -/// Service registry to hold generic, singleton like, interfaces and retrieve -/// them by type. -class ServiceRegistry +struct ServiceMeta { + ServiceKind kind = ServiceKind::Serial; + uint64_t threadId = 0; +}; + +struct NoLocking { + void lock() {} + void unlock() {} +}; + +struct MutexLock { + void lock() { mutex.lock(); } + void unlock() { mutex.unlock(); } + std::mutex& mutex; +}; + +// A pointer to a service. Includes the locking policy +// for that service. +template <typename T, typename LOCKING = NoLocking> +class service_ptr : LOCKING { + public: + service_ptr(T* ptr, LOCKING policy) : LOCKING(policy), mPtr{ptr} { this->lock(); } + ~service_ptr() { this->unlock(); } + service_ptr(service_ptr<T, LOCKING> const&) = delete; + service_ptr& operator=(service_ptr<T, LOCKING> const&) = delete; + T& operator*() { return *mPtr; } + T* operator->() { return mPtr; } + + private: + T* mPtr; +}; + +template <typename, typename = void> +struct ServiceKindExtractor { + constexpr static ServiceKind kind = ServiceKind::Serial; +}; + +template <typename T> +struct ServiceKindExtractor<T, std::void_t<decltype(T::service_kind)>> : std::is_same<decltype(T::service_kind), enum ServiceKind> { + constexpr static ServiceKind kind = T::service_kind; +}; + +struct ServiceRegistry { /// The maximum distance a entry can be from the optimal slot. constexpr static int MAX_DISTANCE = 8; /// The number of slots in the hashmap. constexpr static int MAX_SERVICES = 256; /// The mask to use to calculate the initial slot id. constexpr static int MAX_SERVICES_MASK = MAX_SERVICES - 1; + /// Callbacks for services to be executed before every process method invokation + std::vector<ServiceProcessingHandle> mPreProcessingHandles; + /// Callbacks for services to be executed after every process method invokation + std::vector<ServiceProcessingHandle> mPostProcessingHandles; + /// Callbacks for services to be executed before every dangling check + std::vector<ServiceDanglingHandle> mPreDanglingHandles; + /// Callbacks for services to be executed after every dangling check + std::vector<ServiceDanglingHandle> mPostDanglingHandles; + /// Callbacks for services to be executed before every EOS user callback invokation + std::vector<ServiceEOSHandle> mPreEOSHandles; + /// Callbacks for services to be executed after every EOS user callback invokation + std::vector<ServiceEOSHandle> mPostEOSHandles; + /// Callbacks for services to be executed after every dispatching + std::vector<ServiceDispatchingHandle> mPostDispatchingHandles; public: using hash_type = decltype(TypeIdHelpers::uniqueId<void>()); + ServiceRegistry(); + + ServiceRegistry(ServiceRegistry const& other) + { + mServicesKey = other.mServicesKey; + mServicesValue = other.mServicesValue; + mServicesMeta = other.mServicesMeta; + for (size_t i = 0; i < other.mServicesBooked.size(); ++i) { + this->mServicesBooked[i] = other.mServicesBooked[i].load(); + } + } - ServiceRegistry() + ServiceRegistry& operator=(ServiceRegistry const& other) { - mServicesKey.fill(0L); - mServicesValue.fill(nullptr); + mServicesKey = other.mServicesKey; + mServicesValue = other.mServicesValue; + mServicesMeta = other.mServicesMeta; + for (size_t i = 0; i < other.mServicesBooked.size(); ++i) { + this->mServicesBooked[i] = other.mServicesBooked[i].load(); + } + return *this; } + /// Invoke callbacks to be executed before every process method invokation + void preProcessingCallbacks(ProcessingContext&); + /// Invoke callbacks to be executed after every process method invokation + void postProcessingCallbacks(ProcessingContext&); + /// Invoke callbacks to be executed before every dangling check + void preDanglingCallbacks(DanglingContext&); + /// Invoke callbacks to be executed after every dangling check + void postDanglingCallbacks(DanglingContext&); + /// Invoke callbacks to be executed before every EOS user callback invokation + void preEOSCallbacks(EndOfStreamContext&); + /// Invoke callbacks to be executed after every EOS user callback invokation + void postEOSCallbacks(EndOfStreamContext&); + /// Invoke callbacks to monitor inputs after dispatching, regardless of them + /// being discarded, consumed or processed. + void postDispatchingCallbacks(ProcessingContext&); + + /// Declare a service by its ServiceSpec. If of type Global + /// / Serial it will be immediately registered for tid 0, + /// so that subsequent gets will ultimately use it. + /// If it is of kind "Stream" we will create the Service only + /// when requested by a given thread. This function is not + /// thread safe. + void declareService(ServiceSpec const& spec, DeviceState& state, fair::mq::ProgOptions& options); + + /// Bind the callbacks of a service spec to a given service. + void bindService(ServiceSpec const& spec, void* service); + /// Type erased service registration. @a typeHash is the /// hash used to identify the service, @a service is /// a type erased pointer to the service itself. - void registerService(hash_type typeHash, void* service) + /// This method is supposed to be thread safe + void registerService(hash_type typeHash, void* service, ServiceKind kind, uint64_t threadId, char const* name = nullptr) const; + + // Lookup a given @a typeHash for a given @a threadId at + // a unique (per typeHash) location. There might + // be other typeHash which sit in the same place, but + // the if statement will rule them out. As long as + // only one thread writes in a given i + id location + // as guaranteed by the atomic, mServicesKey[i + id] will + // either be 0 or the final value. + // This method should NEVER register a new service, event when requested. + int getPos(uint32_t typeHash, uint64_t threadId) const { - hash_type id = typeHash & MAX_SERVICES_MASK; + auto threadHashId = (typeHash ^ threadId) & MAX_SERVICES_MASK; + std::atomic_thread_fence(std::memory_order_acquire); for (uint8_t i = 0; i < MAX_DISTANCE; ++i) { - if (mServicesValue[i + id] == nullptr) { - mServicesKey[i + id] = typeHash; - mServicesValue[i + id] = service; - return; + if (mServicesKey[i + threadHashId] == typeHash) { + return i + threadHashId; } } - O2_BUILTIN_UNREACHABLE(); + return -1; } - // Register a service for the given interface T - // with actual implementation C, i.e. C is derived from T. - // Only one instance of type C can be registered per type T. - // The fact we use a bare pointer indicates that the ownership - // of the service still belongs to whatever created it, and is - // not passed to the registry. It's therefore responsibility of - // the creator of the service to properly dispose it. - template <class I, class C> + // Basic, untemplated API. This will require explicitly + // providing the @a typeHash for the Service type, + // there @a threadId of the thread asking the service + // and the @a kind of service. This + // method might trigger the registering of a new service + // if the service is not a stream service and the global + // zero service is available. + // Use this API only if you know what you are doing. + void* get(uint32_t typeHash, uint64_t threadId, ServiceKind kind, char const* name = nullptr) const + { + // Look for the service. If found, return it. + // Notice how due to threading issues, we might + // find it with getPos, but the value can still + // be nullptr. + auto pos = getPos(typeHash, threadId); + if (pos != -1) { + void* ptr = mServicesValue[pos]; + if (ptr) { + return ptr; + } + } + // We are looking up a service which is not of + // stream kind and was not looked up by this thread + // before. + if (threadId != 0) { + int pos = getPos(typeHash, 0); + if (pos != -1 && kind != ServiceKind::Stream) { + registerService(typeHash, mServicesValue[pos], kind, threadId, name); + } + return mServicesValue[pos]; + } + // If we are here it means we never registered a + // service for the 0 thread (i.e. the main thread). + return nullptr; + } + + /// Register a service given an handle + void registerService(ServiceHandle handle) + { + auto tid = std::this_thread::get_id(); + std::hash<std::thread::id> hasher; + ServiceRegistry::registerService(handle.hash, handle.instance, handle.kind, hasher(tid), handle.name.c_str()); + } + + mutable std::vector<ServiceSpec> mSpecs; + mutable std::array<uint32_t, MAX_SERVICES + MAX_DISTANCE> mServicesKey; + mutable std::array<void*, MAX_SERVICES + MAX_DISTANCE> mServicesValue; + mutable std::array<ServiceMeta, MAX_SERVICES + MAX_DISTANCE> mServicesMeta; + mutable std::array<std::atomic<bool>, MAX_SERVICES + MAX_DISTANCE> mServicesBooked; + + /// @deprecated old API to be substituted with the ServiceHandle one + template <class I, class C, enum ServiceKind K = ServiceKind::Serial> void registerService(C* service) { // This only works for concrete implementations of the type T. @@ -77,10 +233,13 @@ class ServiceRegistry static_assert(std::is_base_of<I, C>::value == true, "Registered service is not derived from declared interface"); constexpr hash_type typeHash = TypeIdHelpers::uniqueId<I>(); - registerService(typeHash, reinterpret_cast<void*>(service)); + auto tid = std::this_thread::get_id(); + std::hash<std::thread::id> hasher; + ServiceRegistry::registerService(typeHash, reinterpret_cast<void*>(service), K, hasher(tid), typeid(C).name()); } - template <class I, class C> + /// @deprecated old API to be substituted with the ServiceHandle one + template <class I, class C, enum ServiceKind K = ServiceKind::Serial> void registerService(C const* service) { // This only works for concrete implementations of the type T. @@ -90,14 +249,9 @@ class ServiceRegistry "Registered service is not derived from declared interface"); constexpr auto typeHash = TypeIdHelpers::uniqueId<I const>(); constexpr auto id = typeHash & MAX_SERVICES_MASK; - for (uint8_t i = 0; i < MAX_DISTANCE; ++i) { - if (mServicesValue[i + id] == nullptr) { - mServicesKey[i + id] = typeHash; - mServicesValue[i + id] = (void*)(service); - return; - } - } - O2_BUILTIN_UNREACHABLE(); + auto tid = std::this_thread::get_id(); + std::hash<std::thread::id> hasher; + this->registerService(typeHash, reinterpret_cast<void*>(const_cast<C*>(service)), K, hasher(tid), typeid(C).name()); } /// Check if service of type T is currently active. @@ -105,8 +259,13 @@ class ServiceRegistry std::enable_if_t<std::is_const_v<T> == false, bool> active() const { constexpr auto typeHash = TypeIdHelpers::uniqueId<T>(); - auto ptr = get(typeHash); - return ptr != nullptr; + auto tid = std::this_thread::get_id(); + std::hash<std::thread::id> hasher; + if (this->getPos(typeHash, 0) != -1) { + return true; + } + auto result = this->getPos(typeHash, hasher(tid)) != -1; + return result; } /// Get a service for the given interface T. The returned reference exposed to @@ -116,7 +275,9 @@ class ServiceRegistry T& get() const { constexpr auto typeHash = TypeIdHelpers::uniqueId<T>(); - auto ptr = get(typeHash); + auto tid = std::this_thread::get_id(); + std::hash<std::thread::id> hasher; + auto ptr = this->get(typeHash, hasher(tid), ServiceKind::Serial, typeid(T).name()); if (O2_BUILTIN_LIKELY(ptr != nullptr)) { if constexpr (std::is_const_v<T>) { return *reinterpret_cast<T const*>(ptr); @@ -124,25 +285,9 @@ class ServiceRegistry return *reinterpret_cast<T*>(ptr); } } - throw std::runtime_error(std::string("Unable to find service of kind ") + - typeid(T).name() + - ". Make sure you use const / non-const correctly."); - } - - void* get(uint32_t typeHash) const - { - auto id = typeHash & MAX_SERVICES_MASK; - for (uint8_t i = 0; i < MAX_DISTANCE; ++i) { - if (mServicesKey[i + id] == typeHash) { - return mServicesValue[i + id]; - } - } - return nullptr; + throw runtime_error_f("Unable to find service of kind %s. Make sure you use const / non-const correctly.", + typeid(T).name()); } - - private: - std::array<uint32_t, MAX_SERVICES + MAX_DISTANCE> mServicesKey; - std::array<void*, MAX_SERVICES + MAX_DISTANCE> mServicesValue; }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/ServiceRegistryHelpers.h b/Framework/Core/include/Framework/ServiceRegistryHelpers.h new file mode 100644 index 0000000000000..f738517c4a254 --- /dev/null +++ b/Framework/Core/include/Framework/ServiceRegistryHelpers.h @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SERVICEREGISTRYHELPERS_H_ +#define O2_FRAMEWORK_SERVICEREGISTRYHELPERS_H_ + +#include "Framework/ServiceHandle.h" +#include "Framework/TypeIdHelpers.h" + +#include <algorithm> +#include <array> +#include <functional> +#include <string> +#include <type_traits> +#include <typeinfo> +#include <thread> + +namespace o2::framework +{ + +/// Helpers for ServiceRegistry manipulations +struct ServiceRegistryHelpers { + public: + // Create an handle for a service for the given interface T + // with actual implementation C, i.e. C is derived from T. + // Only one instance of type C can be registered per type T. + // The fact we use a bare pointer indicates that the ownership + // of the service still belongs to whatever created it, and is + // not passed to the registry. It's therefore responsibility of + // the creator of the service to properly dispose it. + template <class I, class C, enum ServiceKind K = ServiceKind::Serial> + static auto handleForService(C* service) -> ServiceHandle + { + // This only works for concrete implementations of the type T. + // We need type elision as we do not want to know all the services in + // advance + static_assert(std::is_base_of<I, C>::value == true, + "Registered service is not derived from declared interface"); + constexpr auto typeHash = TypeIdHelpers::uniqueId<I>(); + return ServiceHandle{typeHash, reinterpret_cast<void*>(service), K, typeid(C).name()}; + } + + /// Same as above, but for const instances + template <class I, class C, enum ServiceKind K = ServiceKind::Serial> + static auto handleForService(C const* service) -> ServiceHandle + { + // This only works for concrete implementations of the type T. + // We need type elision as we do not want to know all the services in + // advance + static_assert(std::is_base_of<I, C>::value == true, + "Registered service is not derived from declared interface"); + constexpr auto typeHash = TypeIdHelpers::uniqueId<I const>(); + return ServiceHandle{typeHash, reinterpret_cast<void*>(const_cast<C*>(service)), K, typeid(C).name()}; + } +}; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_SERVICEREGISTRYHELPERS_H_ diff --git a/Framework/Core/include/Framework/ServiceSpec.h b/Framework/Core/include/Framework/ServiceSpec.h index d1e0998bde052..e4f80382edc9a 100644 --- a/Framework/Core/include/Framework/ServiceSpec.h +++ b/Framework/Core/include/Framework/ServiceSpec.h @@ -10,10 +10,15 @@ #ifndef O2_FRAMEWORK_SERVICESPEC_H_ #define O2_FRAMEWORK_SERVICESPEC_H_ +#include "Framework/ServiceHandle.h" +#include "Framework/DeviceMetricsInfo.h" +#include "Framework/DeviceInfo.h" #include <functional> #include <string> #include <vector> +#include <boost/program_options/variables_map.hpp> + namespace fair::mq { struct ProgOptions; @@ -26,14 +31,9 @@ struct InitContext; struct DeviceSpec; struct ServiceRegistry; struct DeviceState; - -/// Handle to the service hash must be calculated -/// using TypeIdHelper::uniqueId<BaseClass>() so that -/// we can retrieve the service by its baseclass. -struct ServiceHandle { - uint32_t hash; - void* instance; -}; +struct ProcessingContext; +struct EndOfStreamContext; +class DanglingContext; /// A callback to create a given Service. using ServiceInit = std::function<ServiceHandle(ServiceRegistry&, DeviceState&, fair::mq::ProgOptions&)>; @@ -41,26 +41,123 @@ using ServiceInit = std::function<ServiceHandle(ServiceRegistry&, DeviceState&, /// A callback to configure a given Service. Notice that the /// service itself is type erased and it's responsibility of /// the configuration itself to cast it to the correct value -using ServiceConfigure = std::function<void*(InitContext&, void*)>; - -/// The kind of service we are asking for -enum struct ServiceKind { - /// A Service which is not thread safe, therefore all accesses to it must be mutexed. - Serial, - /// A Service which is thread safe and therefore can be used by many threads the same time without risk - Global, - /// A Service which is specific to a given thread in a thread pool - Stream -}; +using ServiceConfigureCallback = std::function<void*(InitContext&, void*)>; + +/// A callback which is executed before each processing loop. +using ServiceProcessingCallback = std::function<void(ProcessingContext&, void*)>; + +/// A callback which is executed before each dangling input loop +using ServiceDanglingCallback = std::function<void(DanglingContext&, void*)>; + +/// A callback which is executed before the end of stream loop. +using ServiceEOSCallback = std::function<void(EndOfStreamContext&, void*)>; + +/// Callback executed before the forking of a given device in the driver +/// Notice the forking can happen multiple times. It's responsibility of +/// the service to track how many times it happens and act accordingly. +using ServicePreFork = std::function<void(ServiceRegistry&, boost::program_options::variables_map const&)>; + +/// Callback executed after forking a given device in the driver, +/// but before doing exec / starting the device. +using ServicePostForkChild = std::function<void(ServiceRegistry&)>; + +/// Callback executed after forking a given device in the driver, +/// but before doing exec / starting the device. +using ServicePostForkParent = std::function<void(ServiceRegistry&)>; + +/// Callback executed before each redeployment of the whole configuration +using ServicePreSchedule = std::function<void(ServiceRegistry&, boost::program_options::variables_map const&)>; + +/// Callback executed after each redeployment of the whole configuration +using ServicePostSchedule = std::function<void(ServiceRegistry&, boost::program_options::variables_map const&)>; + +/// Callback executed in the driver in order to process a metric. +using ServiceMetricHandling = std::function<void(ServiceRegistry&, + std::vector<o2::framework::DeviceMetricsInfo>& metrics, + std::vector<o2::framework::DeviceSpec>& specs, + std::vector<o2::framework::DeviceInfo>& infos, + DeviceMetricsInfo& driverMetrics, + size_t timestamp)>; + +/// Callback executed in the child after dispatching happened. +using ServicePostDispatching = std::function<void(ProcessingContext&, void*)>; -/// A declaration of a service to be used by the associated DataProcessor +/// A specification for a Service. +/// A Service is a utility class which does not perform +/// data processing itself, but it can be used by the data processor +/// to carry out common tasks (e.g. monitoring) or by the framework +/// to perform data processing related ancillary work (e.g. send +/// messages after a computation happended). struct ServiceSpec { + /// Name of the service std::string name; + /// Callback to initialise the service. ServiceInit init; - ServiceConfigure configure; + /// Callback to configure the service. + ServiceConfigureCallback configure; + /// Callback executed before actual processing happens. + ServiceProcessingCallback preProcessing = nullptr; + /// Callback executed once actual processing happened. + ServiceProcessingCallback postProcessing = nullptr; + /// Callback executed before the dangling inputs loop + ServiceDanglingCallback preDangling = nullptr; + /// Callback executed after the dangling inputs loop + ServiceDanglingCallback postDangling = nullptr; + /// Callback executed before the end of stream callback of the user happended + ServiceEOSCallback preEOS = nullptr; + /// Callback executed after the end of stream callback of the user happended + ServiceEOSCallback postEOS = nullptr; + /// Callback executed before the forking of a given device in the driver + /// Notice the forking can happen multiple times. It's responsibility of + /// the service to track how many times it happens and act accordingly. + ServicePreFork preFork = nullptr; + /// Callback executed after forking a given device in the driver, + /// but before doing exec / starting the device. + ServicePostForkChild postForkChild = nullptr; + /// Callback executed after forking a given device in the driver, + /// but before doing exec / starting the device. + ServicePostForkParent postForkParent = nullptr; + + /// Callback executed before and after we schedule a topology + ServicePreSchedule preSchedule = nullptr; + ServicePostSchedule postSchedule = nullptr; + + ///Callback executed after each metric is received by the driver. + ServiceMetricHandling metricHandling = nullptr; + + /// Callback executed after a given input record has been successfully + /// dispatched. + ServicePostDispatching postDispatching = nullptr; + + /// Kind of service being specified. ServiceKind kind; }; +struct ServiceConfigureHandle { + ServiceConfigureCallback callback; + void* service; +}; + +struct ServiceProcessingHandle { + ServiceProcessingCallback callback; + void* service; +}; + +struct ServiceDanglingHandle { + ServiceDanglingCallback callback; + void* service; +}; + +struct ServiceEOSHandle { + ServiceEOSCallback callback; + void* service; +}; + +struct ServiceDispatchingHandle { + ServicePostDispatching callback; + void* service; +}; + } // namespace o2::framework #endif // O2_FRAMEWORK_SERVICESPEC_H_ diff --git a/Framework/Core/include/Framework/SimpleRawDeviceService.h b/Framework/Core/include/Framework/SimpleRawDeviceService.h index b4b8503e194c2..03e3e75e0c2db 100644 --- a/Framework/Core/include/Framework/SimpleRawDeviceService.h +++ b/Framework/Core/include/Framework/SimpleRawDeviceService.h @@ -7,15 +7,13 @@ // In applying this license CERN does 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 FRAMEWORK_SIMPLERAWDEVICESERVICE_H -#define FRAMEWORK_SIMPLERAWDEVICESERVICE_H +#ifndef O2_FRAMEWORK_SIMPLERAWDEVICESERVICE_H_ +#define O2_FRAMEWORK_SIMPLERAWDEVICESERVICE_H_ #include "Framework/RawDeviceService.h" #include "Framework/DeviceSpec.h" -namespace o2 -{ -namespace framework +namespace o2::framework { /// Fairly unsophisticated service which simply stores and returns the @@ -43,11 +41,12 @@ class SimpleRawDeviceService : public RawDeviceService return mSpec; } + void waitFor(unsigned int ms) final; + private: FairMQDevice* mDevice; DeviceSpec const& mSpec; }; -} // namespace framework -} // namespace o2 -#endif // FRAMEWORK_SIMPLERAWDEVICESERVICE_H +} // namespace o2::framework +#endif // O2_FRAMEWORK_SIMPLERAWDEVICESERVICE_H__ diff --git a/Framework/Core/include/Framework/SourceInfoHeader.h b/Framework/Core/include/Framework/SourceInfoHeader.h index cc024d69e2b66..ba870b270987e 100644 --- a/Framework/Core/include/Framework/SourceInfoHeader.h +++ b/Framework/Core/include/Framework/SourceInfoHeader.h @@ -14,7 +14,6 @@ #include "Framework/ChannelInfo.h" #include <cstdint> -#include <cstdio> #include <memory> #include <cassert> diff --git a/Framework/Core/include/Framework/StepTHn.h b/Framework/Core/include/Framework/StepTHn.h new file mode 100644 index 0000000000000..33a06d260ef77 --- /dev/null +++ b/Framework/Core/include/Framework/StepTHn.h @@ -0,0 +1,188 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 StepTHn_H +#define StepTHn_H + +// optimized data container reusing functionality of THn +// A THnSparse is used to have the axis functionality "for free" + +#include "TNamed.h" +#include "THnSparse.h" +#include "TAxis.h" +#include "TArray.h" + +#include "Framework/Logger.h" + +class TArray; +class TArrayF; +class TArrayD; +class TCollection; + +class StepTHn : public TNamed +{ + public: + StepTHn(); + StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, Int_t* nBins, std::vector<Double_t> binLimits[], const char** axisTitles); + ~StepTHn() override; + + template <typename... Ts> + void Fill(Int_t istep, const Ts&... valuesAndWeight); + + THnBase* getTHn(Int_t step, Bool_t sparse = kFALSE) + { + if (!mTarget || !mTarget[step]) { + createTarget(step, sparse); + } + return mTarget[step]; + } + Int_t getNSteps() { return mNSteps; } + Int_t getNVar() { return mNVars; } + + TArray* getValues(Int_t step) { return mValues[step]; } + TArray* getSumw2(Int_t step) { return mSumw2[step]; } + + StepTHn(const StepTHn& c); + StepTHn& operator=(const StepTHn& corr); + void Copy(TObject& c) const override; + + virtual Long64_t Merge(TCollection* list) = 0; + + protected: + void init(); + virtual TArray* createArray(const TArray* src = nullptr) const = 0; + void createTarget(Int_t step, Bool_t sparse); + void deleteContainers(); + + Long64_t getGlobalBinIndex(const Int_t* binIdx); + + Long64_t mNBins; // number of total bins + Int_t mNVars; // number of variables + Int_t mNSteps; // number of selection steps + TArray** mValues; //[mNSteps] data container + TArray** mSumw2; //[mNSteps] data container + + THnBase** mTarget; //! target histogram + + TAxis** mAxisCache; //! cache axis pointers (about 50% of the time in Fill is spent in GetAxis otherwise) + Int_t* mNbinsCache; //! cache Nbins per axis + Double_t* mLastVars; //! caching of last used bins (in many loops some vars are the same for a while) + Int_t* mLastBins; //! caching of last used bins (in many loops some vars are the same for a while) + + THnSparse* mPrototype; // not filled used as prototype histogram for axis functionality etc. + + ClassDef(StepTHn, 1) // THn like container +}; + +template <class TemplateArray> +class StepTHnT : public StepTHn +{ + public: + StepTHnT() : StepTHn() {} + StepTHnT(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, Int_t* nBins, std::vector<Double_t> binLimits[], const char** axisTitles); + ~StepTHnT() override = default; + + protected: + TArray* createArray(const TArray* src = nullptr) const override + { + if (src == nullptr) { + return new TemplateArray(mNBins); + } else { + return new TemplateArray(*((TemplateArray*)src)); + } + } + + Long64_t Merge(TCollection* list) override; + + ClassDef(StepTHnT, 1) // THn like container +}; + +typedef StepTHnT<TArrayF> StepTHnF; +typedef StepTHnT<TArrayD> StepTHnD; + +template <typename... Ts> +void StepTHn::Fill(Int_t istep, const Ts&... valuesAndWeight) +{ + constexpr int nParams = sizeof...(Ts); + // TODO Find a way to avoid the array + double tempArray[nParams] = {static_cast<double>(valuesAndWeight)...}; + + double weight = 1.0; + if (nParams == mNVars + 1) { + weight = tempArray[mNVars]; + } else if (nParams != mNVars) { + LOGF(fatal, "Fill called with invalid number of parameters (%d vs %d)", mNVars, nParams); + } + + // fill axis cache + if (!mAxisCache) { + mAxisCache = new TAxis*[mNVars]; + mNbinsCache = new Int_t[mNVars]; + for (Int_t i = 0; i < mNVars; i++) { + mAxisCache[i] = mPrototype->GetAxis(i); + mNbinsCache[i] = mAxisCache[i]->GetNbins(); + } + + mLastVars = new Double_t[mNVars]; + mLastBins = new Int_t[mNVars]; + + // initial values to prevent checking for 0 below + for (Int_t i = 0; i < mNVars; i++) { + mLastVars[i] = tempArray[i]; + mLastBins[i] = mAxisCache[i]->FindBin(mLastVars[i]); + } + } + + // calculate global bin index + Long64_t bin = 0; + for (Int_t i = 0; i < mNVars; i++) { + bin *= mNbinsCache[i]; + + Int_t tmpBin = 0; + if (mLastVars[i] == tempArray[i]) { + tmpBin = mLastBins[i]; + } else { + tmpBin = mAxisCache[i]->FindBin(tempArray[i]); + mLastBins[i] = tmpBin; + mLastVars[i] = tempArray[i]; + } + //Printf("%d", tmpBin); + + // under/overflow not supported + if (tmpBin < 1 || tmpBin > mNbinsCache[i]) { + return; + } + + // bins start from 0 here + bin += tmpBin - 1; + // Printf("%lld", bin); + } + + if (!mValues[istep]) { + mValues[istep] = createArray(); + LOGF(info, "Created values container for step %d", istep); + } + + if (weight != 1) { + // initialize with already filled entries (which have been filled with weight == 1), in this case mSumw2 := mValues + if (!mSumw2[istep]) { + mSumw2[istep] = createArray(); + LOGF(info, "Created sumw2 container for step %d", istep); + } + } + + // TODO probably slow; add StepTHnT::add ? + mValues[istep]->SetAt(mValues[istep]->GetAt(bin) + weight, bin); + if (mSumw2[istep]) { + mSumw2[istep]->SetAt(mSumw2[istep]->GetAt(bin) + weight, bin); + } +} + +#endif diff --git a/Framework/Core/include/Framework/StringHelpers.h b/Framework/Core/include/Framework/StringHelpers.h index 778aba4efa3e7..159d1bbf6ff42 100644 --- a/Framework/Core/include/Framework/StringHelpers.h +++ b/Framework/Core/include/Framework/StringHelpers.h @@ -12,6 +12,9 @@ #define O2_FRAMEWORK_STRINGHELPERS_H_ #include <cstdint> +#include <utility> +#include <type_traits> +#include <string_view> // CRC32 Table (zlib polynomial) static constexpr uint32_t crc_table[256] = {0x0L, 0x77073096L, 0xee0e612cL, @@ -73,4 +76,91 @@ constexpr uint32_t compile_time_hash(char const* str) return crc32(str, static_cast<int>(__builtin_strlen(str)) - 1) ^ 0xFFFFFFFF; } +template <int N> +constexpr uint32_t compile_time_hash_from_literal(const char (&str)[N]) +{ + return crc32(str, N - 2) ^ 0xFFFFFFFF; +} + +template <char... chars> +struct ConstStr { + static constexpr char str[] = {chars..., '\0'}; + static constexpr uint32_t hash = compile_time_hash_from_literal(str); + static constexpr uint32_t idx = hash & 0x1FF; +}; + +template <typename> +struct is_const_str : std::false_type { +}; + +template <char... chars> +struct is_const_str<ConstStr<chars...>> : std::true_type { +}; + +template <typename T> +constexpr bool is_const_str_v(T) +{ + return is_const_str<T>::value; +} + +template <char... chars1, char... chars2> +constexpr auto operator+(const ConstStr<chars1...>&, const ConstStr<chars2...>&) +{ + return ConstStr<chars1..., chars2...>{}; +} + +namespace const_str_details +{ + +template <typename T, std::size_t... Is> +constexpr auto as_chars_impl(std::index_sequence<Is...>) +{ + return ConstStr<T::str()[Is]...>{}; +} + +template <typename T> +constexpr auto as_chars() +{ + return as_chars_impl<T>( + std::make_index_sequence<T::size() - 1>{}); +} + +template <int N> +constexpr const char* const get_str(const char (&str)[N]) +{ + return str; +} + +template <int N> +constexpr const int get_size(const char (&str)[N]) +{ + return N; +} + +constexpr const char* const get_str(const std::string_view& str) +{ + return str.data(); +} + +constexpr const int get_size(const std::string_view& str) +{ + return str.size() + 1; +} +} // namespace const_str_details + +#define CONST_STR(literal) \ + [] { \ + struct literal_to_chars { \ + static constexpr decltype(auto) str() \ + { \ + return const_str_details::get_str(literal); \ + } \ + static constexpr decltype(auto) size() \ + { \ + return const_str_details::get_size(literal); \ + } \ + }; \ + return const_str_details::as_chars<literal_to_chars>(); \ + }() + #endif // O2_FRAMEWORK_STRINGHELPERS_H diff --git a/Framework/Core/include/Framework/TMessageSerializer.h b/Framework/Core/include/Framework/TMessageSerializer.h index dc12130e380da..b041125800396 100644 --- a/Framework/Core/include/Framework/TMessageSerializer.h +++ b/Framework/Core/include/Framework/TMessageSerializer.h @@ -12,6 +12,8 @@ #include <fairmq/FairMQMessage.h> +#include "Framework/RuntimeError.h" + #include <TList.h> #include <TMessage.h> #include <TObjArray.h> @@ -137,9 +139,7 @@ inline std::unique_ptr<T> TMessageSerializer::deserialize(gsl::span<o2::byte> bu { TClass* tgtClass = TClass::GetClass(typeid(T)); if (tgtClass == nullptr) { - std::string error("class is not ROOT-serializable: "); - error += typeid(T).name(); - throw std::runtime_error(error); + throw runtime_error_f("class is not ROOT-serializable: %s", typeid(T).name()); } // FIXME: we need to add consistency check for buffer data to be serialized // at the moment, TMessage might simply crash if an invalid or inconsistent @@ -147,15 +147,12 @@ inline std::unique_ptr<T> TMessageSerializer::deserialize(gsl::span<o2::byte> bu FairTMessage tm(buffer); TClass* serializedClass = tm.GetClass(); if (serializedClass == nullptr) { - std::string error("can not read class info from buffer"); - throw std::runtime_error(error); + throw runtime_error_f("can not read class info from buffer"); } if (tgtClass != serializedClass && serializedClass->GetBaseClass(tgtClass) == nullptr) { - std::string error("can not convert serialized class "); - error += tm.GetClass()->GetName(); - error += " into target class "; - error += tgtClass->GetName(); - throw std::runtime_error(error); + throw runtime_error_f("can not convert serialized class %s into target class %s", + tm.GetClass()->GetName(), + tgtClass->GetName()); } return std::unique_ptr<T>(reinterpret_cast<T*>(tm.ReadObjectAny(serializedClass))); } diff --git a/Framework/Core/include/Framework/TableBuilder.h b/Framework/Core/include/Framework/TableBuilder.h index b705f08704e99..43e45b9d79bb0 100644 --- a/Framework/Core/include/Framework/TableBuilder.h +++ b/Framework/Core/include/Framework/TableBuilder.h @@ -15,11 +15,14 @@ #include "Framework/StructToTuple.h" #include "Framework/FunctionalHelpers.h" #include "Framework/VariantHelpers.h" +#include "Framework/RuntimeError.h" #include "arrow/type_traits.h" // Apparently needs to be on top of the arrow includes. #include <sstream> +#include <arrow/status.h> +#include <arrow/memory_pool.h> #include <arrow/stl.h> #include <arrow/type_traits.h> #include <arrow/table.h> @@ -30,6 +33,7 @@ #include <string> #include <memory> #include <tuple> +#include <type_traits> namespace arrow { @@ -38,12 +42,17 @@ class Table; class Array; } // namespace arrow -namespace o2 -{ -namespace framework +template <typename T> +struct BulkInfo { + const T ptr; + size_t size; +}; + +namespace o2::framework { namespace detail { +/// FIXME: adapt type conversion to arrow 1.0 // This is needed by Arrow 0.12.0 which dropped // // using ArrowType = ArrowType_; @@ -88,96 +97,124 @@ O2_ARROW_STL_CONVERSION(std::string, StringType) struct BuilderUtils { template <typename T> - static arrow::Status appendToList(std::unique_ptr<arrow::FixedSizeListBuilder>& builder, T* data) + static arrow::Status appendToList(std::unique_ptr<arrow::FixedSizeListBuilder>& builder, T* data, int size = 1) { - using ArrowType = typename detail::ConversionTraits<T>::ArrowType; + using ArrowType = typename detail::ConversionTraits<std::decay_t<T>>::ArrowType; using BuilderType = typename arrow::TypeTraits<ArrowType>::BuilderType; size_t numElements = static_cast<const arrow::FixedSizeListType*>(builder->type().get())->list_size(); - auto status = builder->AppendValues(1); + auto status = builder->AppendValues(size); auto ValueBuilder = static_cast<BuilderType*>(builder->value_builder()); - status &= ValueBuilder->AppendValues(data, numElements, nullptr); + status &= ValueBuilder->AppendValues(data, numElements * size, nullptr); return status; } - template <typename BuilderType, typename T> - static arrow::Status append(BuilderType& builder, T value) + template <typename HolderType, typename T> + static arrow::Status append(HolderType& holder, T value) { - return builder->Append(value); + return static_cast<typename HolderType::Policy&>(holder).append(holder.builder, value); + } + + template <typename HolderType> + static arrow::Status flush(HolderType& holder) + { + return static_cast<typename HolderType::Policy&>(holder).flush(holder.builder); } /// Appender for the pointer case. /// Assumes that the pointer actually points to a buffer /// which contains the correct number of elements. - template <typename BuilderType, typename T> - static arrow::Status append(BuilderType& builder, T* data) + template <typename HolderType, typename T> + static arrow::Status append(HolderType& holder, T* data) { - if constexpr (std::is_same_v<BuilderType, std::unique_ptr<arrow::FixedSizeListBuilder>>) { - return appendToList<T>(builder, data); + if constexpr (std::is_same_v<decltype(holder.builder), std::unique_ptr<arrow::FixedSizeListBuilder>>) { + return appendToList<T>(holder.builder, data); } else { - return builder->Append(reinterpret_cast<const uint8_t*>(data)); + return holder.builder->Append(reinterpret_cast<const uint8_t*>(data)); } } /// Appender for the array case. - template <typename BuilderType, typename T, int N> - static arrow::Status append(BuilderType& builder, T (&data)[N]) + template <typename HolderType, typename T, int N> + static arrow::Status append(HolderType& holder, T (&data)[N]) { - return builder->Append(reinterpret_cast<const uint8_t*>(data)); + return holder.builder->Append(reinterpret_cast<const uint8_t*>(data)); } /// Appender for the array case. - template <typename BuilderType, typename T, int N> - static arrow::Status append(BuilderType& builder, std::array<T, N> const& data) + template <typename HolderType, typename T, int N> + static arrow::Status append(HolderType& holder, std::array<T, N> const& data) { - return builder->Append(reinterpret_cast<const uint8_t*>(data.data())); + return holder.builder->Append(reinterpret_cast<const uint8_t*>(data.data())); } - template <typename BuilderType, typename T> - static void unsafeAppend(BuilderType& builder, T value) + template <typename HolderType, typename T> + static void unsafeAppend(HolderType& holder, T value) { - return builder->UnsafeAppend(value); + return holder.builder->UnsafeAppend(value); } - template <typename BuilderType, typename T> - static void unsafeAppend(BuilderType& builder, T* value) + template <typename HolderType, typename T> + static void unsafeAppend(HolderType& holder, T* value) { - if constexpr (std::is_same_v<BuilderType, std::unique_ptr<arrow::FixedSizeListBuilder>>) { - auto status = appendToList<T>(builder, value); + if constexpr (std::is_same_v<decltype(holder.builder), std::unique_ptr<arrow::FixedSizeListBuilder>>) { + auto status = appendToList<T>(holder.builder, value); } else { - return builder->UnsafeAppend(reinterpret_cast<const uint8_t*>(value)); + return holder.builder->UnsafeAppend(reinterpret_cast<const uint8_t*>(value)); } } - template <typename BuilderType, typename PTR> - static arrow::Status bulkAppend(BuilderType& builder, size_t bulkSize, const PTR ptr) + template <typename HolderType, typename PTR> + static arrow::Status bulkAppend(HolderType& holder, size_t bulkSize, const PTR ptr) { - return builder->AppendValues(ptr, bulkSize, nullptr); + return holder.builder->AppendValues(ptr, bulkSize, nullptr); } - template <typename BuilderType, typename ITERATOR> - static arrow::Status append(BuilderType& builder, std::pair<ITERATOR, ITERATOR> ip) + template <typename HolderType, typename PTR> + static arrow::Status bulkAppendChunked(HolderType& holder, BulkInfo<PTR> info) + { + // Appending nullptr is a no-op. + if (info.ptr == nullptr) { + return arrow::Status::OK(); + } + if constexpr (std::is_same_v<decltype(holder.builder), std::unique_ptr<arrow::FixedSizeListBuilder>>) { + if (appendToList<std::remove_pointer_t<decltype(info.ptr)>>(holder.builder, info.ptr, info.size).ok() == false) { + throw runtime_error("Unable to append to column"); + } else { + return arrow::Status::OK(); + } + } else { + if (holder.builder->AppendValues(info.ptr, info.size, nullptr).ok() == false) { + throw runtime_error("Unable to append to column"); + } else { + return arrow::Status::OK(); + } + } + } + + template <typename HolderType, typename ITERATOR> + static arrow::Status append(HolderType& holder, std::pair<ITERATOR, ITERATOR> ip) { using ArrowType = typename detail::ConversionTraits<typename ITERATOR::value_type>::ArrowType; using ValueBuilderType = typename arrow::TypeTraits<ArrowType>::BuilderType; // FIXME: for the moment we do not fill things. - auto status = builder->Append(); - auto valueBuilder = reinterpret_cast<ValueBuilderType*>(builder->value_builder()); + auto status = holder.builder->Append(); + auto valueBuilder = reinterpret_cast<ValueBuilderType*>(holder.builder->value_builder()); return status & valueBuilder->AppendValues(&*ip.first, std::distance(ip.first, ip.second)); } // Lists do not have UnsafeAppend so we need to use the slow path in any case. - template <typename BuilderType, typename ITERATOR> - static void unsafeAppend(BuilderType& builder, std::pair<ITERATOR, ITERATOR> ip) + template <typename HolderType, typename ITERATOR> + static void unsafeAppend(HolderType& holder, std::pair<ITERATOR, ITERATOR> ip) { using ArrowType = typename detail::ConversionTraits<typename ITERATOR::value_type>::ArrowType; using ValueBuilderType = typename arrow::TypeTraits<ArrowType>::BuilderType; // FIXME: for the moment we do not fill things. - auto status = builder->Append(); - auto valueBuilder = reinterpret_cast<ValueBuilderType*>(builder->value_builder()); + auto status = holder.builder->Append(); + auto valueBuilder = reinterpret_cast<ValueBuilderType*>(holder.builder->value_builder()); status &= valueBuilder->AppendValues(&*ip.first, std::distance(ip.first, ip.second)); if (!status.ok()) { - throw std::runtime_error("Unable to append values to valueBuilder!"); + throw runtime_error("Unable to append values to valueBuilder!"); } return; } @@ -186,6 +223,7 @@ struct BuilderUtils { template <typename T> struct BuilderMaker { using FillType = T; + using STLValueType = T; using ArrowType = typename detail::ConversionTraits<T>::ArrowType; using BuilderType = typename arrow::TypeTraits<ArrowType>::BuilderType; @@ -214,6 +252,7 @@ struct BuilderMaker { template <> struct BuilderMaker<bool> { using FillType = bool; + using STLValueType = bool; using ArrowType = typename detail::ConversionTraits<bool>::ArrowType; using BuilderType = typename arrow::TypeTraits<ArrowType>::BuilderType; @@ -236,6 +275,7 @@ struct BuilderMaker<bool> { template <typename ITERATOR> struct BuilderMaker<std::pair<ITERATOR, ITERATOR>> { using FillType = std::pair<ITERATOR, ITERATOR>; + using STLValueType = typename ITERATOR::value_type; using ArrowType = arrow::ListType; using ValueType = typename detail::ConversionTraits<typename ITERATOR::value_type>::ArrowType; using BuilderType = arrow::ListBuilder; @@ -256,6 +296,7 @@ struct BuilderMaker<std::pair<ITERATOR, ITERATOR>> { template <typename T, int N> struct BuilderMaker<T (&)[N]> { using FillType = T*; + using STLValueType = T; using BuilderType = arrow::FixedSizeListBuilder; using ArrowType = arrow::FixedSizeListType; using ElementType = typename detail::ConversionTraits<T>::ArrowType; @@ -294,7 +335,6 @@ struct BuilderMaker<T[N]> { return arrow::fixed_size_list(arrow::TypeTraits<ElementType>::type_singleton(), N); } }; - template <typename... ARGS> auto make_builders() { @@ -323,6 +363,62 @@ struct BuilderTraits<T[N]> { using BuilderType = arrow::FixedSizeListBuilder; }; +template <typename T> +struct DirectInsertion { + template <typename BUILDER> + arrow::Status append(BUILDER& builder, T value) + { + return builder->Append(value); + } + + template <typename BUILDER> + arrow::Status flush(BUILDER&) + { + return arrow::Status::OK(); + } +}; + +template <typename T> +struct CachedInsertion { + static constexpr int CHUNK_SIZE = 256; + + template <typename BUILDER> + arrow::Status append(BUILDER& builder, T value) + { + cache[pos % CHUNK_SIZE] = value; + ++pos; + if (pos % CHUNK_SIZE == 0) { + return builder->AppendValues(cache, CHUNK_SIZE, nullptr); + } + return arrow::Status::OK(); + } + + template <typename BUILDER> + arrow::Status flush(BUILDER& builder) + { + if (pos % CHUNK_SIZE != 0) { + return builder->AppendValues(cache, pos % CHUNK_SIZE, nullptr); + } + return arrow::Status::OK(); + } + T cache[CHUNK_SIZE]; + int pos = 0; +}; + +template <typename T, template <typename U> typename InsertionPolicy> +struct BuilderHolder : InsertionPolicy<T> { + using Policy = InsertionPolicy<T>; + using ArrowType = typename detail::ConversionTraits<T>::ArrowType; + using BuilderType = typename arrow::TypeTraits<ArrowType>::BuilderType; + + BuilderHolder(arrow::MemoryPool* pool) + : builder{BuilderMaker<T>::make(pool)} + { + } + + std::unique_ptr<BuilderType> builder; +}; + struct TableBuilderHelpers { template <typename... ARGS> static auto makeFields(std::vector<std::string> const& names) @@ -336,38 +432,46 @@ struct TableBuilderHelpers { } /// Invokes the append method for each entry in the tuple - template <std::size_t... Is, typename BUILDERS, typename VALUES> - static bool append(BUILDERS& builders, std::index_sequence<Is...>, VALUES&& values) + template <std::size_t... Is, typename HOLDERS, typename VALUES> + static bool append(HOLDERS& holders, std::index_sequence<Is...>, VALUES&& values) { - return (BuilderUtils::append(std::get<Is>(builders), std::get<Is>(values)).ok() && ...); + return (BuilderUtils::append(std::get<Is>(holders), std::get<Is>(values)).ok() && ...); } /// Invokes the UnsafeAppend method for each entry in the tuple /// For this to be used, one should make sure the number of entries /// is known a-priori. - template <std::size_t... Is, typename BUILDERS, typename VALUES> - static void unsafeAppend(BUILDERS& builders, std::index_sequence<Is...>, VALUES&& values) + template <std::size_t... Is, typename HOLDERS, typename VALUES> + static void unsafeAppend(HOLDERS& holders, std::index_sequence<Is...>, VALUES&& values) { - (BuilderUtils::unsafeAppend(std::get<Is>(builders), std::get<Is>(values)), ...); + (BuilderUtils::unsafeAppend(std::get<Is>(holders), std::get<Is>(values)), ...); } - template <std::size_t... Is, typename BUILDERS, typename PTRS> - static bool bulkAppend(BUILDERS& builders, size_t bulkSize, std::index_sequence<Is...>, PTRS ptrs) + template <std::size_t... Is, typename HOLDERS, typename PTRS> + static bool bulkAppend(HOLDERS& holders, size_t bulkSize, std::index_sequence<Is...>, PTRS ptrs) { - return (BuilderUtils::bulkAppend(std::get<Is>(builders), bulkSize, std::get<Is>(ptrs)).ok() && ...); + return (BuilderUtils::bulkAppend(std::get<Is>(holders), bulkSize, std::get<Is>(ptrs)).ok() && ...); + } + + /// Return true if all columns are done. + template <std::size_t... Is, typename BUILDERS, typename INFOS> + static bool bulkAppendChunked(BUILDERS& builders, std::index_sequence<Is...>, INFOS infos) + { + return (BuilderUtils::bulkAppendChunked(std::get<Is>(builders), std::get<Is>(infos)).ok() && ...); } /// Invokes the append method for each entry in the tuple - template <typename BUILDERS, std::size_t... Is> - static bool finalize(std::vector<std::shared_ptr<arrow::Array>>& arrays, BUILDERS& builders, std::index_sequence<Is...> seq) + template <typename HOLDERS, std::size_t... Is> + static bool finalize(std::vector<std::shared_ptr<arrow::Array>>& arrays, HOLDERS& holders, std::index_sequence<Is...> seq) { - return (std::get<Is>(builders)->Finish(&arrays[Is]).ok() && ...); + bool ok = (BuilderUtils::flush(std::get<Is>(holders)).ok() && ...); + return ok && (std::get<Is>(holders).builder->Finish(&arrays[Is]).ok() && ...); } - template <typename BUILDERS, std::size_t... Is> - static bool reserveAll(BUILDERS& builders, size_t s, std::index_sequence<Is...>) + template <typename HOLDERS, std::size_t... Is> + static bool reserveAll(HOLDERS& holders, size_t s, std::index_sequence<Is...>) { - return (std::get<Is>(builders)->Reserve(s).ok() && ...); + return (std::get<Is>(holders).builder->Reserve(s).ok() && ...); } }; @@ -392,20 +496,75 @@ template <class T, std::size_t N> struct is_bounded_array<std::array<T, N>> : std::true_type { }; +template <typename T> +struct HolderTrait { + using Holder = BuilderHolder<T, DirectInsertion>; +}; + +template <> +struct HolderTrait<int8_t> { + using Holder = BuilderHolder<int8_t, CachedInsertion>; +}; + +template <> +struct HolderTrait<uint8_t> { + using Holder = BuilderHolder<uint8_t, CachedInsertion>; +}; + +template <> +struct HolderTrait<uint16_t> { + using Holder = BuilderHolder<uint16_t, CachedInsertion>; +}; + +template <> +struct HolderTrait<int16_t> { + using Holder = BuilderHolder<int16_t, CachedInsertion>; +}; + +template <> +struct HolderTrait<int> { + using Holder = BuilderHolder<int, CachedInsertion>; +}; + +template <> +struct HolderTrait<float> { + using Holder = BuilderHolder<float, CachedInsertion>; +}; + +template <> +struct HolderTrait<double> { + using Holder = BuilderHolder<double, CachedInsertion>; +}; + +template <> +struct HolderTrait<unsigned int> { + using Holder = BuilderHolder<unsigned int, CachedInsertion>; +}; + +template <> +struct HolderTrait<uint64_t> { + using Holder = BuilderHolder<uint64_t, CachedInsertion>; +}; + +template <> +struct HolderTrait<int64_t> { + using Holder = BuilderHolder<int64_t, CachedInsertion>; +}; + /// Helper class which creates a lambda suitable for building /// an arrow table from a tuple. This can be used, for example /// to build an arrow::Table from a TDataFrame. class TableBuilder { template <typename... ARGS> - using BuildersTuple = typename std::tuple<std::unique_ptr<typename BuilderTraits<ARGS>::BuilderType>...>; + using HoldersTuple = typename std::tuple<typename HolderTrait<ARGS>::Holder...>; /// Get the builders, assumning they were created with a given pack /// of basic types template <typename... ARGS> auto getBuilders(o2::framework::pack<ARGS...> pack) { - return (BuildersTuple<ARGS...>*)mBuilders; + return (HoldersTuple<ARGS...>*)mHolders; } template <typename... ARGS> @@ -413,10 +572,10 @@ class TableBuilder { constexpr int nColumns = sizeof...(ARGS); if (nColumns != columnNames.size()) { - throw std::runtime_error("Mismatching number of column types and names"); + throw runtime_error("Mismatching number of column types and names"); } - if (mBuilders != nullptr) { - throw std::runtime_error("TableBuilder::persist can only be invoked once per instance"); + if (mHolders != nullptr) { + throw runtime_error("TableBuilder::persist can only be invoked once per instance"); } } @@ -425,29 +584,28 @@ class TableBuilder { mSchema = std::make_shared<arrow::Schema>(TableBuilderHelpers::makeFields<ARGS...>(columnNames)); - auto builders = new BuildersTuple<ARGS...>(BuilderMaker<ARGS>::make(mMemoryPool)...); + auto holders = new HoldersTuple<ARGS...>(typename HolderTrait<ARGS>::Holder(mMemoryPool)...); if (nRows != -1) { auto seq = std::make_index_sequence<sizeof...(ARGS)>{}; - TableBuilderHelpers::reserveAll(*builders, nRows, seq); + TableBuilderHelpers::reserveAll(*holders, nRows, seq); } - mBuilders = builders; // We store the builders + mHolders = holders; // We store the builders } template <typename... ARGS> auto makeFinalizer() { - mFinalizer = [schema = mSchema, &arrays = mArrays, builders = (BuildersTuple<ARGS...>*)mBuilders]() -> std::shared_ptr<arrow::Table> { - auto status = TableBuilderHelpers::finalize(arrays, *builders, std::make_index_sequence<sizeof...(ARGS)>{}); + mFinalizer = [schema = mSchema, &arrays = mArrays, holders = mHolders]() -> void { + auto status = TableBuilderHelpers::finalize(arrays, *(HoldersTuple<ARGS...>*)holders, std::make_index_sequence<sizeof...(ARGS)>{}); if (status == false) { - throw std::runtime_error("Unable to finalize"); + throw runtime_error("Unable to finalize"); } - return arrow::Table::Make(schema, arrays); }; } public: TableBuilder(arrow::MemoryPool* pool = arrow::default_memory_pool()) - : mBuilders{nullptr}, + : mHolders{nullptr}, mMemoryPool{pool} { } @@ -499,10 +657,10 @@ class TableBuilder // Callback used to fill the builders using FillTuple = std::tuple<typename BuilderMaker<ARGS>::FillType...>; - return [builders = (BuildersTuple<ARGS...>*)mBuilders](unsigned int slot, FillTuple const& t) -> void { - auto status = TableBuilderHelpers::append(*builders, std::index_sequence_for<ARGS...>{}, t); + return [holders = mHolders](unsigned int slot, FillTuple const& t) -> void { + auto status = TableBuilderHelpers::append(*(HoldersTuple<ARGS...>*)holders, std::index_sequence_for<ARGS...>{}, t); if (status == false) { - throw std::runtime_error("Unable to append"); + throw runtime_error("Unable to append"); } }; } @@ -512,19 +670,17 @@ class TableBuilder template <typename T> auto cursor() { - using persistent_filter = soa::FilterPersistentColumns<T>; - using persistent_columns_pack = typename persistent_filter::persistent_columns_pack; + using persistent_columns_pack = typename T::table_t::persistent_columns_t; constexpr auto persistent_size = pack_size(persistent_columns_pack{}); - return cursorHelper<typename persistent_filter::persistent_table_t>(std::make_index_sequence<persistent_size>()); + return cursorHelper<typename soa::PackToTable<persistent_columns_pack>::table>(std::make_index_sequence<persistent_size>()); } template <typename T, typename E> auto cursor() { - using persistent_filter = soa::FilterPersistentColumns<T>; - using persistent_columns_pack = typename persistent_filter::persistent_columns_pack; + using persistent_columns_pack = typename T::table_t::persistent_columns_t; constexpr auto persistent_size = pack_size(persistent_columns_pack{}); - return cursorHelper<typename persistent_filter::persistent_table_t, E>(std::make_index_sequence<persistent_size>()); + return cursorHelper<typename soa::PackToTable<persistent_columns_pack>::table, E>(std::make_index_sequence<persistent_size>()); } template <typename... ARGS> @@ -537,8 +693,8 @@ class TableBuilder makeFinalizer<ARGS...>(); // Callback used to fill the builders - return [builders = (BuildersTuple<ARGS...>*)mBuilders](unsigned int slot, typename BuilderMaker<ARGS>::FillType... args) -> void { - TableBuilderHelpers::unsafeAppend(*builders, std::index_sequence_for<ARGS...>{}, std::forward_as_tuple(args...)); + return [holders = mHolders](unsigned int slot, typename BuilderMaker<ARGS>::FillType... args) -> void { + TableBuilderHelpers::unsafeAppend(*(HoldersTuple<ARGS...>*)holders, std::index_sequence_for<ARGS...>{}, std::forward_as_tuple(args...)); }; } @@ -551,8 +707,22 @@ class TableBuilder makeBuilders<ARGS...>(columnNames, nRows); makeFinalizer<ARGS...>(); - return [builders = (BuildersTuple<ARGS...>*)mBuilders](unsigned int slot, size_t batchSize, typename BuilderMaker<ARGS>::FillType const*... args) -> void { - TableBuilderHelpers::bulkAppend(*builders, batchSize, std::index_sequence_for<ARGS...>{}, std::forward_as_tuple(args...)); + return [holders = mHolders](unsigned int slot, size_t batchSize, typename BuilderMaker<ARGS>::FillType const*... args) -> void { + TableBuilderHelpers::bulkAppend(*(HoldersTuple<ARGS...>*)holders, batchSize, std::index_sequence_for<ARGS...>{}, std::forward_as_tuple(args...)); + }; + } + + template <typename... ARGS> + auto bulkPersistChunked(std::vector<std::string> const& columnNames, size_t nRows) + { + constexpr int nColumns = sizeof...(ARGS); + validate<ARGS...>(columnNames); + mArrays.resize(nColumns); + makeBuilders<ARGS...>(columnNames, nRows); + makeFinalizer<ARGS...>(); + + return [holders = mHolders](unsigned int slot, BulkInfo<typename BuilderMaker<ARGS>::STLValueType const*>... args) -> bool { + return TableBuilderHelpers::bulkAppendChunked(*(HoldersTuple<ARGS...>*)holders, std::index_sequence_for<ARGS...>{}, std::forward_as_tuple(args...)); }; } @@ -560,7 +730,7 @@ class TableBuilder template <typename... ARGS> auto reserve(o2::framework::pack<ARGS...> pack, int s) { - visitBuilders(pack, [s](auto& builder) { return builder.Reserve(s).ok(); }); + visitBuilders(pack, [s](auto& holder) { return holder.builder->Reserve(s).ok(); }); } /// Invoke the appropriate visitor on the various builders @@ -569,7 +739,7 @@ class TableBuilder { auto builders = getBuilders(pack); return std::apply(overloaded{ - [visitor](std::unique_ptr<typename BuilderTraits<ARGS>::BuilderType>&... args) { (visitor(*args), ...); }}, + [visitor](typename HolderTrait<ARGS>::Holder&... args) { (visitor(args), ...); }}, *builders); } @@ -581,26 +751,110 @@ class TableBuilder /// template argument T is a o2::soa::Table which contains only the /// persistent columns. template <typename T, size_t... Is> - auto cursorHelper(std::index_sequence<Is...> s) + auto cursorHelper(std::index_sequence<Is...>) { std::vector<std::string> columnNames{pack_element_t<Is, typename T::columns>::columnLabel()...}; return this->template persist<typename pack_element_t<Is, typename T::columns>::type...>(columnNames); } template <typename T, typename E, size_t... Is> - auto cursorHelper(std::index_sequence<Is...> s) + auto cursorHelper(std::index_sequence<Is...>) { std::vector<std::string> columnNames{pack_element_t<Is, typename T::columns>::columnLabel()...}; return this->template persist<E>(columnNames); } std::function<void(void)> mFinalizer; - void* mBuilders; + void* mHolders; arrow::MemoryPool* mMemoryPool; std::shared_ptr<arrow::Schema> mSchema; std::vector<std::shared_ptr<arrow::Array>> mArrays; }; -} // namespace framework -} // namespace o2 +template <typename T> +auto makeEmptyTable() +{ + TableBuilder b; + auto writer = b.cursor<T>(); + return b.finalize(); +} + +/// Expression-based column generator to materialize columns +template <typename... C> +auto spawner(framework::pack<C...> columns, arrow::Table* atable) +{ + static auto new_schema = o2::soa::createSchemaFromColumns(columns); + static auto projectors = framework::expressions::createProjectors(columns, atable->schema()); + + if (atable->num_rows() == 0) { + return makeEmptyTable<soa::Table<C...>>(); + } + + arrow::TableBatchReader reader(*atable); + std::shared_ptr<arrow::RecordBatch> batch; + arrow::ArrayVector v; + std::array<arrow::ArrayVector, sizeof...(C)> chunks; + std::vector<std::shared_ptr<arrow::ChunkedArray>> arrays; + + while (true) { + auto s = reader.ReadNext(&batch); + if (!s.ok()) { + throw runtime_error_f("Cannot read batches from table: %s", s.ToString().c_str()); + } + if (batch == nullptr) { + break; + } + s = projectors->Evaluate(*batch, arrow::default_memory_pool(), &v); + if (!s.ok()) { + throw runtime_error_f("Cannot apply projector: %s", s.ToString().c_str()); + } + for (auto i = 0u; i < sizeof...(C); ++i) { + chunks[i].emplace_back(v.at(i)); + } + } + + for (auto i = 0u; i < sizeof...(C); ++i) { + arrays.push_back(std::make_shared<arrow::ChunkedArray>(chunks[i])); + } + + return arrow::Table::Make(new_schema, arrays); +} + +/// Helper to get a tuple tail +template <typename Head, typename... Tail> +std::tuple<Tail...> tuple_tail(std::tuple<Head, Tail...>& t) +{ + return apply([](auto const&, auto&... tail) { return std::tie(tail...); }, t); +} + +/// Helpers to get type pack from tuple +template <typename... T> +constexpr auto pack_from_tuple(std::tuple<T...> const&) +{ + return framework::pack<T...>{}; +} + +/// Binary search for an index column +template <typename Key, typename T> +void lowerBound(int32_t value, T& start) +{ + static_assert(soa::is_soa_iterator_t<T>::value, "Argument needs to be a Table::iterator"); + int step; + auto count = start.mMaxRow - start.globalIndex(); + + while (count > 0) { + step = count / 2; + start.moveByIndex(step); + if (start.template getId<Key>() < value) { + count -= step + 1; + } else { + start.moveByIndex(-step); + count = step; + } + } +} + +template <typename... T> +using iterator_tuple_t = std::tuple<typename T::iterator...>; +} // namespace o2::framework #endif // FRAMEWORK_TABLEBUILDER_H diff --git a/Framework/Core/include/Framework/TableTreeHelpers.h b/Framework/Core/include/Framework/TableTreeHelpers.h index f229b3e289fe2..519c2aa418878 100644 --- a/Framework/Core/include/Framework/TableTreeHelpers.h +++ b/Framework/Core/include/Framework/TableTreeHelpers.h @@ -7,8 +7,8 @@ // In applying this license CERN does 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 FRAMEWORK_TABLETREE_H -#define FRAMEWORK_TABLETREE_H +#ifndef O2_FRAMEWORK_TABLETREEHELPERS_H_ +#define O2_FRAMEWORK_TABLETREEHELPERS_H_ #include "TFile.h" #include "TTreeReader.h" @@ -17,9 +17,7 @@ #include "TableBuilder.h" // ============================================================================= -namespace o2 -{ -namespace framework +namespace o2::framework { // ----------------------------------------------------------------------------- @@ -117,127 +115,29 @@ class TableToTree TTree* process(); }; -// ----------------------------------------------------------------------------- -// TreeToTable allows to fill the contents of a given TTree to an arrow::Table -// ColumnIterator is used by TreeToTable -// -// To copy the contents of a tree tr to a table ta do: -// . TreeToTable t2t(tr); -// . t2t.addColumn(columnname1); t2t.addColumn(columnname2); ... -// OR -// t2t.addAllColumns(); -// . auto ta = t2t.process(); -// -// ............................................................................. -class ColumnIterator -{ - - private: - // all the possible TTreeReaderValue<T> types - TTreeReaderValue<bool>* mReaderValue_o = nullptr; - TTreeReaderValue<uint8_t>* mReaderValue_ub = nullptr; - TTreeReaderValue<uint16_t>* mReaderValue_us = nullptr; - TTreeReaderValue<uint32_t>* mReaderValue_ui = nullptr; - TTreeReaderValue<uint64_t>* mReaderValue_ul = nullptr; - TTreeReaderValue<int8_t>* mReaderValue_b = nullptr; - TTreeReaderValue<int16_t>* mReaderValue_s = nullptr; - TTreeReaderValue<int32_t>* mReaderValue_i = nullptr; - TTreeReaderValue<int64_t>* mReaderValue_l = nullptr; - TTreeReaderValue<float>* mReaderValue_f = nullptr; - TTreeReaderValue<double>* mReaderValue_d = nullptr; - - // all the possible TTreeReaderArray<T> types - TTreeReaderArray<bool>* mReaderArray_o = nullptr; - TTreeReaderArray<uint8_t>* mReaderArray_ub = nullptr; - TTreeReaderArray<uint16_t>* mReaderArray_us = nullptr; - TTreeReaderArray<uint32_t>* mReaderArray_ui = nullptr; - TTreeReaderArray<uint64_t>* mReaderArray_ul = nullptr; - TTreeReaderArray<int8_t>* mReaderArray_b = nullptr; - TTreeReaderArray<int16_t>* mReaderArray_s = nullptr; - TTreeReaderArray<int32_t>* mReaderArray_i = nullptr; - TTreeReaderArray<int64_t>* mReaderArray_l = nullptr; - TTreeReaderArray<float>* mReaderArray_f = nullptr; - TTreeReaderArray<double>* mReaderArray_d = nullptr; - - // all the possible arrow::TBuilder types - std::shared_ptr<arrow::FixedSizeListBuilder> mTableBuilder_list; - - arrow::BooleanBuilder* mTableBuilder_o = nullptr; - arrow::UInt8Builder* mTableBuilder_ub = nullptr; - arrow::UInt16Builder* mTableBuilder_us = nullptr; - arrow::UInt32Builder* mTableBuilder_ui = nullptr; - arrow::UInt64Builder* mTableBuilder_ul = nullptr; - arrow::Int8Builder* mTableBuilder_b = nullptr; - arrow::Int16Builder* mTableBuilder_s = nullptr; - arrow::Int32Builder* mTableBuilder_i = nullptr; - arrow::Int64Builder* mTableBuilder_l = nullptr; - arrow::FloatBuilder* mTableBuilder_f = nullptr; - arrow::DoubleBuilder* mTableBuilder_d = nullptr; - - bool mStatus = false; - EDataType mElementType; - int64_t mNumberElements; - const char* mColumnName; - - std::shared_ptr<arrow::Field> mField; - std::shared_ptr<arrow::Array> mArray; - - public: - ColumnIterator(TTreeReader* reader, const char* colname); - ~ColumnIterator(); - - // has the iterator been properly initialized - bool getStatus(); - - // copy the TTreeReaderValue to the arrow::TBuilder - void push(); - - std::shared_ptr<arrow::Array> getArray() { return mArray; } - std::shared_ptr<arrow::Field> getSchema() { return mField; } - - // finish the arrow::TBuilder - // with this mArray is prepared to be used in arrow::Table::Make - void finish(); -}; - class TreeToTable { private: - // the TTreeReader allows to efficiently loop over - // the rows of a TTree - TTreeReader* mTreeReader; - - // a list of ColumnIterator* - std::vector<std::shared_ptr<ColumnIterator>> mColumnIterators; - - // Append next set of branch values to the - // corresponding table columns - void push(); + std::shared_ptr<arrow::Table> mTable; + std::vector<std::string> mColumnNames; public: - TreeToTable(TTree* tree); - ~TreeToTable(); - // add a column to be included in the arrow::table - bool addColumn(const char* colname); + void addColumn(const char* colname); - // add all columns - bool addAllColumns(); + // add all branches in @a tree as columns + bool addAllColumns(TTree* tree); // do the looping with the TTreeReader - void fill(); + void fill(TTree* tree); // create the table std::shared_ptr<arrow::Table> finalize(); - - // do the looping with the TTreeReader and create the table - std::shared_ptr<arrow::Table> process(); }; // ----------------------------------------------------------------------------- -} // namespace framework -} // namespace o2 +} // namespace o2::framework // ============================================================================= -#endif // FRAMEWORK_TABLETREE_H +#endif // O2_FRAMEWORK_TABLETREEHELPERS_H_ diff --git a/Framework/Core/include/Framework/TextControlService.h b/Framework/Core/include/Framework/TextControlService.h index 45c91128f3c4f..fd2e207cb479c 100644 --- a/Framework/Core/include/Framework/TextControlService.h +++ b/Framework/Core/include/Framework/TextControlService.h @@ -10,9 +10,12 @@ #ifndef O2_FRAMEWORK_TEXTCONTROLSERVICE_H_ #define O2_FRAMEWORK_TEXTCONTROLSERVICE_H_ +#include "Framework/ServiceHandle.h" #include "Framework/ControlService.h" + #include <string> #include <regex> +#include <mutex> namespace o2::framework { @@ -49,6 +52,7 @@ class TextControlService : public ControlService bool mOnce = false; ServiceRegistry& mRegistry; DeviceState& mDeviceState; + std::mutex mMutex; }; bool parseControl(std::string const& s, std::smatch& match); diff --git a/Framework/Core/include/Framework/TimesliceIndex.h b/Framework/Core/include/Framework/TimesliceIndex.h index acc5cc4f0f119..43cef66bcee5e 100644 --- a/Framework/Core/include/Framework/TimesliceIndex.h +++ b/Framework/Core/include/Framework/TimesliceIndex.h @@ -8,18 +8,17 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef FRAMEWORK_TIMESLICEINDEX_H -#define FRAMEWORK_TIMESLICEINDEX_H +#ifndef O2_FRAMEWORK_TIMESLICEINDEX_H_ +#define O2_FRAMEWORK_TIMESLICEINDEX_H_ #include "Framework/DataDescriptorMatcher.h" +#include "Framework/ServiceHandle.h" #include <cstdint> #include <tuple> #include <vector> -namespace o2 -{ -namespace framework +namespace o2::framework { struct TimesliceId { @@ -46,6 +45,9 @@ struct TimesliceSlot { class TimesliceIndex { public: + /// TimesliceIndex is threadsafe because it's accessed only by the + /// DataRelayer. + constexpr static ServiceKind service_kind = ServiceKind::Global; /// The outcome for the processing of a given timeslot enum struct ActionTaken { ReplaceUnused, /// An unused / invalid slot is used to hold the new context @@ -104,8 +106,7 @@ class TimesliceIndex std::vector<bool> mDirty; }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #include "TimesliceIndex.inc" #endif // FRAMEWORK_TIMESLICEINDEX_H diff --git a/Framework/Core/include/Framework/Variant.h b/Framework/Core/include/Framework/Variant.h index 139eea96a98cb..4fba20c8a4f70 100644 --- a/Framework/Core/include/Framework/Variant.h +++ b/Framework/Core/include/Framework/Variant.h @@ -9,19 +9,20 @@ // or submit itself to any jurisdiction. #ifndef FRAMEWORK_VARIANT_H #define FRAMEWORK_VARIANT_H + +#include "Framework/RuntimeError.h" +#include "Framework/Array2D.h" #include <type_traits> #include <cstring> #include <cstdint> #include <cstdlib> -#include <stdexcept> #include <iosfwd> #include <initializer_list> #include <string_view> +#include <vector> #include <string> -namespace o2 -{ -namespace framework +namespace o2::framework { enum class VariantType : int { Int = 0, @@ -30,6 +31,14 @@ enum class VariantType : int { Int = 0, Double, String, Bool, + ArrayInt, + ArrayFloat, + ArrayDouble, + ArrayBool, + ArrayString, + MatrixInt, + MatrixFloat, + MatrixDouble, Empty, Unknown }; @@ -37,52 +46,69 @@ template <typename T> struct variant_trait : std::integral_constant<VariantType, VariantType::Unknown> { }; -template <> -struct variant_trait<int> : std::integral_constant<VariantType, VariantType::Int> { -}; - -template <> -struct variant_trait<long int> : std::integral_constant<VariantType, VariantType::Int64> { -}; - -template <> -struct variant_trait<long long int> : std::integral_constant<VariantType, VariantType::Int64> { -}; - -template <> -struct variant_trait<float> : std::integral_constant<VariantType, VariantType::Float> { -}; - -template <> -struct variant_trait<double> : std::integral_constant<VariantType, VariantType::Double> { -}; - -template <> -struct variant_trait<const char*> : std::integral_constant<VariantType, VariantType::String> { -}; +#define DECLARE_VARIANT_TRAIT(_Type1_, _Type2_) \ + template <> \ + struct variant_trait<_Type1_> : std::integral_constant<VariantType, VariantType::_Type2_> { \ + }; + +DECLARE_VARIANT_TRAIT(int, Int); +DECLARE_VARIANT_TRAIT(long int, Int64); +DECLARE_VARIANT_TRAIT(long long int, Int64); +DECLARE_VARIANT_TRAIT(float, Float); +DECLARE_VARIANT_TRAIT(double, Double); +DECLARE_VARIANT_TRAIT(bool, Bool); + +DECLARE_VARIANT_TRAIT(const char*, String); +DECLARE_VARIANT_TRAIT(char*, String); +DECLARE_VARIANT_TRAIT(char* const, String); +DECLARE_VARIANT_TRAIT(const char* const, String); +DECLARE_VARIANT_TRAIT(std::string_view, String); +DECLARE_VARIANT_TRAIT(std::string, String); + +DECLARE_VARIANT_TRAIT(int*, ArrayInt); +DECLARE_VARIANT_TRAIT(float*, ArrayFloat); +DECLARE_VARIANT_TRAIT(double*, ArrayDouble); +DECLARE_VARIANT_TRAIT(bool*, ArrayBool); +DECLARE_VARIANT_TRAIT(std::string*, ArrayString); + +DECLARE_VARIANT_TRAIT(std::vector<int>, ArrayInt); +DECLARE_VARIANT_TRAIT(std::vector<float>, ArrayFloat); +DECLARE_VARIANT_TRAIT(std::vector<double>, ArrayDouble); +DECLARE_VARIANT_TRAIT(std::vector<bool>, ArrayBool); +DECLARE_VARIANT_TRAIT(std::vector<std::string>, ArrayString); + +DECLARE_VARIANT_TRAIT(Array2D<int>, MatrixInt); +DECLARE_VARIANT_TRAIT(Array2D<float>, MatrixFloat); +DECLARE_VARIANT_TRAIT(Array2D<double>, MatrixDouble); -template <> -struct variant_trait<char*> : std::integral_constant<VariantType, VariantType::String> { +template <typename T> +struct variant_array_symbol { + constexpr static char symbol = 'u'; }; template <> -struct variant_trait<char* const> : std::integral_constant<VariantType, VariantType::String> { +struct variant_array_symbol<int> { + constexpr static char symbol = 'i'; }; template <> -struct variant_trait<const char* const> : std::integral_constant<VariantType, VariantType::String> { +struct variant_array_symbol<float> { + constexpr static char symbol = 'f'; }; template <> -struct variant_trait<std::string_view> : std::integral_constant<VariantType, VariantType::String> { +struct variant_array_symbol<double> { + constexpr static char symbol = 'd'; }; template <> -struct variant_trait<std::string> : std::integral_constant<VariantType, VariantType::String> { +struct variant_array_symbol<bool> { + constexpr static char symbol = 'b'; }; template <> -struct variant_trait<bool> : std::integral_constant<VariantType, VariantType::Bool> { +struct variant_array_symbol<std::string> { + constexpr static char symbol = 's'; }; template <typename T> @@ -92,39 +118,40 @@ template <VariantType type> struct variant_type { }; -template <> -struct variant_type<VariantType::Int> { - using type = int; -}; - -template <> -struct variant_type<VariantType::Int64> { - using type = int64_t; -}; - -template <> -struct variant_type<VariantType::Float> { - using type = float; -}; - -template <> -struct variant_type<VariantType::Double> { - using type = double; -}; - -template <> -struct variant_type<VariantType::String> { - using type = const char*; -}; - -template <> -struct variant_type<VariantType::Bool> { - using type = bool; -}; +#define DECLARE_VARIANT_TYPE(_Type1_, _Type2_) \ + template <> \ + struct variant_type<VariantType::_Type2_> { \ + using type = _Type1_; \ + }; + +DECLARE_VARIANT_TYPE(int, Int); +DECLARE_VARIANT_TYPE(int64_t, Int64); +DECLARE_VARIANT_TYPE(float, Float); +DECLARE_VARIANT_TYPE(double, Double); +DECLARE_VARIANT_TYPE(const char*, String); +DECLARE_VARIANT_TYPE(bool, Bool); + +DECLARE_VARIANT_TYPE(int*, ArrayInt); +DECLARE_VARIANT_TYPE(float*, ArrayFloat); +DECLARE_VARIANT_TYPE(double*, ArrayDouble); +DECLARE_VARIANT_TYPE(bool*, ArrayBool); +DECLARE_VARIANT_TYPE(std::string*, ArrayString); + +DECLARE_VARIANT_TYPE(Array2D<int>, MatrixInt); +DECLARE_VARIANT_TYPE(Array2D<float>, MatrixFloat); +DECLARE_VARIANT_TYPE(Array2D<double>, MatrixDouble); template <typename S, typename T> struct variant_helper { static void set(S* store, T value) { *(reinterpret_cast<T*>(store)) = value; } + static void set(S* store, T values, size_t size) + { + *reinterpret_cast<T*>(store) = reinterpret_cast<T>(std::memcpy(std::malloc(size * sizeof(std::remove_pointer_t<T>)), reinterpret_cast<void*>(values), size * sizeof(std::remove_pointer_t<T>))); + } + static void reset(S* store, T values, size_t) + { + *reinterpret_cast<T*>(store) = values; + } static T get(const S* store) { return *(reinterpret_cast<const T*>(store)); } }; @@ -150,24 +177,34 @@ struct variant_helper<S, std::string> { static void set(S* store, std::string value) { *reinterpret_cast<char**>(store) = strdup(value.data()); } }; -// Poor man variant class. Does not take ownership of anything passed to it. -// FIXME: we should really use C++17 std::variant when it -// comes about +/// Variant for configuration parameter storage. Owns stored data. class Variant { - using storage_t = std::aligned_union<8, int, int64_t, const char*, float, double, bool>::type; + using storage_t = std::aligned_union<8, int, int64_t, const char*, float, double, bool, int*, float*, double*, bool*, Array2D<int>, Array2D<float>, Array2D<double>>::type; public: - Variant(VariantType type = VariantType::Unknown) : mType{type} {} + Variant(VariantType type = VariantType::Unknown) : mType{type}, mSize{1} {} template <typename T> - Variant(T value) : mType{variant_trait_v<T>} + Variant(T value) : mType{variant_trait_v<T>}, mSize{1} { variant_helper<storage_t, decltype(value)>::set(&mStore, value); } template <typename T> - Variant(std::initializer_list<T>) : mType{VariantType::Unknown} + Variant(T values, size_t size) : mType{variant_trait_v<T>}, mSize{size} + { + variant_helper<storage_t, T>::set(&mStore, values, mSize); + } + + template <typename T> + Variant(std::vector<T>& values) : mType{variant_trait_v<T*>}, mSize{values.size()} + { + variant_helper<storage_t, T*>::set(&mStore, values.data(), mSize); + } + + template <typename T> + Variant(std::initializer_list<T>) : mType{VariantType::Unknown}, mSize{1} { static_assert(sizeof(T) == 0, "brace-enclosed initializer list forbidden for Variant" @@ -176,51 +213,124 @@ class Variant Variant(const Variant& other) : mType(other.mType) { - // In case this is a string we need to duplicate it to avoid + // In case this is an array we need to duplicate it to avoid // double deletion. - if (mType == variant_trait_v<const char*>) { - variant_helper<storage_t, const char*>::set(&mStore, other.get<const char*>()); - } else { - mStore = other.mStore; + switch (mType) { + case variant_trait_v<const char*>: + mSize = other.mSize; + variant_helper<storage_t, const char*>::set(&mStore, other.get<const char*>()); + return; + case variant_trait_v<int*>: + mSize = other.mSize; + variant_helper<storage_t, int*>::set(&mStore, other.get<int*>(), mSize); + return; + case variant_trait_v<float*>: + mSize = other.mSize; + variant_helper<storage_t, float*>::set(&mStore, other.get<float*>(), mSize); + return; + case variant_trait_v<double*>: + mSize = other.mSize; + variant_helper<storage_t, double*>::set(&mStore, other.get<double*>(), mSize); + return; + case variant_trait_v<bool*>: + mSize = other.mSize; + variant_helper<storage_t, bool*>::set(&mStore, other.get<bool*>(), mSize); + return; + case variant_trait_v<std::string*>: + mSize = other.mSize; + variant_helper<storage_t, std::string*>::set(&mStore, other.get<std::string*>(), mSize); + return; + default: + mStore = other.mStore; + mSize = other.mSize; } } Variant(Variant&& other) : mType(other.mType) { - // In case this is a string we need to duplicate it to avoid - // double deletion. - if (mType == variant_trait_v<const char*>) { - mStore = other.mStore; - *reinterpret_cast<char**>(&(other.mStore)) = nullptr; - } else { - mStore = other.mStore; + mStore = other.mStore; + mSize = other.mSize; + switch (mType) { + case variant_trait_v<const char*>: + *reinterpret_cast<char**>(&(other.mStore)) = nullptr; + return; + case variant_trait_v<int*>: + *reinterpret_cast<int**>(&(other.mStore)) = nullptr; + return; + case variant_trait_v<float*>: + *reinterpret_cast<float**>(&(other.mStore)) = nullptr; + return; + case variant_trait_v<double*>: + *reinterpret_cast<double**>(&(other.mStore)) = nullptr; + return; + case variant_trait_v<bool*>: + *reinterpret_cast<bool**>(&(other.mStore)) = nullptr; + return; + case variant_trait_v<std::string*>: + *reinterpret_cast<std::string**>(&(other.mStore)) = nullptr; + default: + return; } } ~Variant() { - // In case we allocated a string out of bound, we + // In case we allocated an array, we // should delete it. - if (mType == variant_trait_v<const char*> || mType == variant_trait_v<char*>) { - free(*reinterpret_cast<void**>(&mStore)); + switch (mType) { + case variant_trait_v<const char*>: + case variant_trait_v<int*>: + case variant_trait_v<float*>: + case variant_trait_v<double*>: + case variant_trait_v<bool*>: + case variant_trait_v<std::string*>: + if (reinterpret_cast<void**>(&mStore) != nullptr) { + free(*reinterpret_cast<void**>(&mStore)); + } + return; + default: + return; } } void operator=(const Variant& other) { - if (mType == variant_trait_v<const char*>) { - variant_helper<storage_t, const char*>::set(&mStore, other.get<const char*>()); - } else { - mStore = other.mStore; + switch (mType) { + case variant_trait_v<const char*>: + mSize = other.mSize; + variant_helper<storage_t, const char*>::set(&mStore, other.get<const char*>()); + return; + case variant_trait_v<int*>: + mSize = other.mSize; + variant_helper<storage_t, int*>::set(&mStore, other.get<int*>(), mSize); + return; + case variant_trait_v<float*>: + mSize = other.mSize; + variant_helper<storage_t, float*>::set(&mStore, other.get<float*>(), mSize); + return; + case variant_trait_v<double*>: + mSize = other.mSize; + variant_helper<storage_t, double*>::set(&mStore, other.get<double*>(), mSize); + return; + case variant_trait_v<bool*>: + mSize = other.mSize; + variant_helper<storage_t, bool*>::set(&mStore, other.get<bool*>(), mSize); + return; + case variant_trait_v<std::string*>: + mSize = other.mSize; + variant_helper<storage_t, std::string*>::set(&mStore, other.get<std::string*>(), mSize); + return; + default: + mStore = other.mStore; + mSize = other.mSize; } } template <typename T> T get() const { - auto v = variant_trait_v<T>; if (mType != variant_trait_v<T>) { - throw std::runtime_error("Mismatch between types"); + throw runtime_error("Mismatch between types"); } return variant_helper<storage_t, T>::get(&mStore); } @@ -228,18 +338,33 @@ class Variant template <typename T> void set(T value) { - return variant_helper<storage_t, T>::set(mStore, value); + return variant_helper<storage_t, T>::set(&mStore, value); + } + + template <typename T> + void set(T value, size_t size) + { + mSize = size; + return variant_helper<storage_t, T>::set(&mStore, value, mSize); + } + + template <typename T> + void set(std::vector<T>& values) + { + return variant_helper<storage_t, T*>::set(&mStore, values.data(), values.size()); } VariantType type() const { return mType; } + size_t size() const { return mSize; } + std::string asString() const; private: friend std::ostream& operator<<(std::ostream& oss, Variant const& val); storage_t mStore; VariantType mType; + size_t mSize = 1; }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif diff --git a/Framework/Core/include/Framework/WorkflowCustomizationHelpers.h b/Framework/Core/include/Framework/WorkflowCustomizationHelpers.h new file mode 100644 index 0000000000000..edf4758adeb4f --- /dev/null +++ b/Framework/Core/include/Framework/WorkflowCustomizationHelpers.h @@ -0,0 +1,25 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_WORKFLOWCUSTOMIZATIONHELPERS_H_ +#define O2_FRAMEWORK_WORKFLOWCUSTOMIZATIONHELPERS_H_ + +#include "Framework/ConfigParamSpec.h" +#include <vector> + +namespace o2::framework +{ + +struct WorkflowCustomizationHelpers { + static std::vector<ConfigParamSpec> requiredWorkflowOptions(); +}; + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_WORKFLOWCUSTOMIZATIONHELPERS_H_ diff --git a/Framework/Core/include/Framework/runDataProcessing.h b/Framework/Core/include/Framework/runDataProcessing.h index 2ee228f1e8be6..3e97f6b4bc575 100644 --- a/Framework/Core/include/Framework/runDataProcessing.h +++ b/Framework/Core/include/Framework/runDataProcessing.h @@ -13,16 +13,15 @@ #include "Framework/ChannelConfigurationPolicy.h" #include "Framework/CompletionPolicy.h" #include "Framework/DispatchPolicy.h" -#include "Framework/ConfigParamsHelper.h" #include "Framework/DataProcessorSpec.h" +#include "Framework/DataAllocator.h" #include "Framework/WorkflowSpec.h" #include "Framework/ConfigContext.h" #include "Framework/BoostOptionsRetriever.h" #include "Framework/CustomWorkflowTerminationHook.h" #include "Framework/CommonServices.h" - -#include <boost/program_options/options_description.hpp> -#include <boost/program_options/variables_map.hpp> +#include "Framework/WorkflowCustomizationHelpers.h" +#include "Framework/Logger.h" #include <unistd.h> #include <vector> @@ -77,6 +76,9 @@ void defaultConfiguration(std::vector<o2::framework::ServiceSpec>& services) services = o2::framework::CommonServices::defaultServices(); } +/// Workflow options which are required by DPL in order to work. +std::vector<o2::framework::ConfigParamSpec> requiredWorkflowOptions(); + void defaultConfiguration(o2::framework::OnWorkflowTerminationHook& hook) { hook = [](const char*) {}; @@ -104,6 +106,12 @@ class ConfigContext; /// Helper used to customize a workflow pipelining options void overridePipeline(o2::framework::ConfigContext& ctx, std::vector<o2::framework::DataProcessorSpec>& workflow); +/// Helper used to customize a workflow via a template data processor +void overrideCloning(o2::framework::ConfigContext& ctx, std::vector<o2::framework::DataProcessorSpec>& workflow); + +/// Helper used to customize the workflow via a global suffix. +void overrideSuffix(o2::framework::ConfigContext& ctx, std::vector<o2::framework::DataProcessorSpec>& workflow); + // This comes from the framework itself. This way we avoid code duplication. int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& specs, std::vector<o2::framework::ChannelConfigurationPolicy> const& channelPolicies, @@ -127,12 +135,8 @@ int main(int argc, char** argv) // The default policy is a catch all pub/sub setup to be consistent with the past. std::vector<o2::framework::ConfigParamSpec> workflowOptions; UserCustomizationsHelper::userDefinedCustomization(workflowOptions, 0); - workflowOptions.push_back(ConfigParamSpec{"readers", VariantType::Int64, 1ll, {"number of parallel readers to use"}}); - workflowOptions.push_back(ConfigParamSpec{"pipeline", VariantType::String, "", {"override default pipeline size"}}); - std::vector<ChannelConfigurationPolicy> channelPolicies; - UserCustomizationsHelper::userDefinedCustomization(channelPolicies, 0); - auto defaultChannelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); - channelPolicies.insert(std::end(channelPolicies), std::begin(defaultChannelPolicies), std::end(defaultChannelPolicies)); + auto requiredWorkflowOptions = WorkflowCustomizationHelpers::requiredWorkflowOptions(); + workflowOptions.insert(std::end(workflowOptions), std::begin(requiredWorkflowOptions), std::end(requiredWorkflowOptions)); std::vector<CompletionPolicy> completionPolicies; UserCustomizationsHelper::userDefinedCustomization(completionPolicies, 0); @@ -153,10 +157,16 @@ int main(int argc, char** argv) ConfigParamRegistry workflowOptionsRegistry(std::move(workflowOptionsStore)); ConfigContext configContext(workflowOptionsRegistry, argc, argv); o2::framework::WorkflowSpec specs = defineDataProcessing(configContext); + overrideCloning(configContext, specs); + overrideSuffix(configContext, specs); overridePipeline(configContext, specs); for (auto& spec : specs) { UserCustomizationsHelper::userDefinedCustomization(spec.requiredServices, 0); } + std::vector<ChannelConfigurationPolicy> channelPolicies; + UserCustomizationsHelper::userDefinedCustomization(channelPolicies, 0); + auto defaultChannelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(configContext); + channelPolicies.insert(std::end(channelPolicies), std::begin(defaultChannelPolicies), std::end(defaultChannelPolicies)); result = doMain(argc, argv, specs, channelPolicies, completionPolicies, dispatchPolicies, workflowOptions, configContext); } catch (boost::exception& e) { doBoostException(e); diff --git a/Framework/Core/src/AODReaderHelpers.cxx b/Framework/Core/src/AODReaderHelpers.cxx index 278b0b818bf36..266ac4c048814 100644 --- a/Framework/Core/src/AODReaderHelpers.cxx +++ b/Framework/Core/src/AODReaderHelpers.cxx @@ -8,9 +8,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Framework/TableTreeHelpers.h" #include "Framework/AODReaderHelpers.h" -#include "Framework/AnalysisDataModel.h" +#include "Framework/TableTreeHelpers.h" +#include "Framework/AnalysisHelpers.h" +#include "AnalysisDataModelHelpers.h" #include "DataProcessingHelpers.h" #include "ExpressionHelpers.h" #include "Framework/RootTableBuilderHelpers.h" @@ -27,9 +28,13 @@ #include "Framework/ChannelInfo.h" #include "Framework/Logger.h" -#include <FairMQDevice.h> +#include <Monitoring/Monitoring.h> + #include <ROOT/RDataFrame.hxx> +#include <TGrid.h> #include <TFile.h> +#include <TTreeCache.h> +#include <TTreePerfStats.h> #include <arrow/ipc/reader.h> #include <arrow/ipc/writer.h> @@ -39,150 +44,39 @@ #include <thread> +using o2::monitoring::Metric; +using o2::monitoring::Monitoring; +using o2::monitoring::tags::Key; +using o2::monitoring::tags::Value; + namespace o2::framework::readers { -enum AODTypeMask : uint64_t { - None = 0, - Track = 1 << 0, - TrackCov = 1 << 1, - TrackExtra = 1 << 2, - Calo = 1 << 3, - CaloTrigger = 1 << 4, - Muon = 1 << 5, - MuonCluster = 1 << 6, - Zdc = 1 << 7, - BC = 1 << 8, - Collision = 1 << 9, - FT0 = 1 << 10, - FV0 = 1 << 11, - FDD = 1 << 12, - UnassignedTrack = 1 << 13, - Run2V0 = 1 << 14, - McCollision = 1 << 15, - McTrackLabel = 1 << 16, - McCaloLabel = 1 << 17, - McCollisionLabel = 1 << 18, - McParticle = 1 << 19, - Unknown = 1 << 20 -}; - -uint64_t getMask(header::DataDescription description) +auto setEOSCallback(InitContext& ic) { - - if (description == header::DataDescription{"TRACKPAR"}) { - return AODTypeMask::Track; - } else if (description == header::DataDescription{"TRACKPARCOV"}) { - return AODTypeMask::TrackCov; - } else if (description == header::DataDescription{"TRACKEXTRA"}) { - return AODTypeMask::TrackExtra; - } else if (description == header::DataDescription{"CALO"}) { - return AODTypeMask::Calo; - } else if (description == header::DataDescription{"CALOTRIGGER"}) { - return AODTypeMask::CaloTrigger; - } else if (description == header::DataDescription{"MUON"}) { - return AODTypeMask::Muon; - } else if (description == header::DataDescription{"MUONCLUSTER"}) { - return AODTypeMask::MuonCluster; - } else if (description == header::DataDescription{"ZDC"}) { - return AODTypeMask::Zdc; - } else if (description == header::DataDescription{"BC"}) { - return AODTypeMask::BC; - } else if (description == header::DataDescription{"COLLISION"}) { - return AODTypeMask::Collision; - } else if (description == header::DataDescription{"FT0"}) { - return AODTypeMask::FT0; - } else if (description == header::DataDescription{"FV0"}) { - return AODTypeMask::FV0; - } else if (description == header::DataDescription{"FDD"}) { - return AODTypeMask::FDD; - } else if (description == header::DataDescription{"UNASSIGNEDTRACK"}) { - return AODTypeMask::UnassignedTrack; - } else if (description == header::DataDescription{"RUN2V0"}) { - return AODTypeMask::Run2V0; - } else if (description == header::DataDescription{"MCCOLLISION"}) { - return AODTypeMask::McCollision; - } else if (description == header::DataDescription{"MCTRACKLABEL"}) { - return AODTypeMask::McTrackLabel; - } else if (description == header::DataDescription{"MCCALOLABEL"}) { - return AODTypeMask::McCaloLabel; - } else if (description == header::DataDescription{"MCCOLLISLABEL"}) { - return AODTypeMask::McCollisionLabel; - } else if (description == header::DataDescription{"MCPARTICLE"}) { - return AODTypeMask::McParticle; - } else { - LOG(DEBUG) << "This is a tree of unknown type! " << description.str; - return AODTypeMask::Unknown; - } + ic.services().get<CallbackService>().set(CallbackService::Id::EndOfStream, + [](EndOfStreamContext& eosc) { + auto& control = eosc.services().get<ControlService>(); + control.endOfStream(); + control.readyToQuit(QuitRequest::Me); + }); } -uint64_t calculateReadMask(std::vector<OutputRoute> const& routes, header::DataOrigin const& origin) +template <typename O> +static inline auto extractTypedOriginal(ProcessingContext& pc) { - uint64_t readMask = None; - for (auto& route : routes) { - auto concrete = DataSpecUtils::asConcreteDataTypeMatcher(route.matcher); - auto description = concrete.description; - - readMask |= getMask(description); - } - return readMask; + ///FIXME: this should be done in invokeProcess() as some of the originals may be compound tables + return O{pc.inputs().get<TableConsumer>(aod::MetadataTrait<O>::metadata::tableLabel())->asArrowTable()}; } -std::vector<OutputRoute> getListOfUnknown(std::vector<OutputRoute> const& routes) +template <typename... Os> +static inline auto extractOriginalsTuple(framework::pack<Os...>, ProcessingContext& pc) { - - std::vector<OutputRoute> unknows; - for (auto& route : routes) { - auto concrete = DataSpecUtils::asConcreteDataTypeMatcher(route.matcher); - - if (getMask(concrete.description) == AODTypeMask::Unknown) - unknows.push_back(route); - } - return unknows; + return std::make_tuple(extractTypedOriginal<Os>(pc)...); } -/// Expression-based column generator to materialize columns -template <typename... C> -auto spawner(framework::pack<C...> columns, arrow::Table* atable) -{ - arrow::TableBatchReader reader(*atable); - std::shared_ptr<arrow::RecordBatch> batch; - arrow::ArrayVector v; - std::vector<arrow::ArrayVector> chunks(sizeof...(C)); - - auto projectors = framework::expressions::createProjectors(columns, atable->schema()); - while (true) { - auto s = reader.ReadNext(&batch); - if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot read batches from table {}", s.ToString())); - } - if (batch == nullptr) { - break; - } - s = projectors->Evaluate(*batch, arrow::default_memory_pool(), &v); - if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot apply projector {}", s.ToString())); - } - for (auto i = 0u; i < sizeof...(C); ++i) { - chunks[i].emplace_back(v.at(i)); - } - } - std::vector<std::shared_ptr<arrow::ChunkedArray>> results(sizeof...(C)); - for (auto i = 0u; i < sizeof...(C); ++i) { - results[i] = std::make_shared<arrow::ChunkedArray>(chunks[i]); - } - return results; -} - -AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector<InputSpec> requested) +AlgorithmSpec AODReaderHelpers::indexBuilderCallback(std::vector<InputSpec> requested) { return AlgorithmSpec::InitCallback{[requested](InitContext& ic) { - auto& callbacks = ic.services().get<CallbackService>(); - auto endofdatacb = [](EndOfStreamContext& eosc) { - auto& control = eosc.services().get<ControlService>(); - control.endOfStream(); - control.readyToQuit(QuitRequest::Me); - }; - callbacks.set(CallbackService::Id::EndOfStream, endofdatacb); return [requested](ProcessingContext& pc) { auto outputs = pc.outputs(); @@ -202,140 +96,83 @@ AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector<InputSpec> reques auto maker = [&](auto metadata) { using metadata_t = decltype(metadata); - using expressions = typename metadata_t::expression_pack_t; - auto extra_schema = o2::soa::createSchemaFromColumns(expressions{}); - auto original_table = pc.inputs().get<TableConsumer>(input.binding)->asArrowTable(); - auto original_fields = original_table->schema()->fields(); - std::vector<std::shared_ptr<arrow::Field>> fields; - auto arrays = spawner(expressions{}, original_table.get()); - std::vector<std::shared_ptr<arrow::ChunkedArray>> columns = original_table->columns(); - std::copy(original_fields.begin(), original_fields.end(), std::back_inserter(fields)); - for (auto i = 0u; i < framework::pack_size(expressions{}); ++i) { - columns.push_back(arrays[i]); - fields.emplace_back(extra_schema->field(i)); + using Key = typename metadata_t::Key; + using index_pack_t = typename metadata_t::index_pack_t; + using sources = typename metadata_t::originals; + if constexpr (metadata_t::exclusive == true) { + return o2::framework::IndexExclusive::indexBuilder(index_pack_t{}, + extractTypedOriginal<Key>(pc), + extractOriginalsTuple(sources{}, pc)); + } else { + return o2::framework::IndexSparse::indexBuilder(index_pack_t{}, + extractTypedOriginal<Key>(pc), + extractOriginalsTuple(sources{}, pc)); } - auto new_schema = std::make_shared<arrow::Schema>(fields); - return arrow::Table::Make(new_schema, columns); }; - if (description == header::DataDescription{"TRACKPAR"}) { - outputs.adopt(Output{origin, description}, maker(o2::aod::TracksMetadata{})); - } else if (description == header::DataDescription{"TRACKPARCOV"}) { - outputs.adopt(Output{origin, description}, maker(o2::aod::TracksCovMetadata{})); + if (description == header::DataDescription{"MA_RN2_EX"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::Run2MatchedExclusiveMetadata{})); + } else if (description == header::DataDescription{"MA_RN2_SP"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::Run2MatchedSparseMetadata{})); + } else if (description == header::DataDescription{"MA_RN3_EX"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::Run3MatchedExclusiveMetadata{})); + } else if (description == header::DataDescription{"MA_RN3_SP"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::Run3MatchedSparseMetadata{})); + } else if (description == header::DataDescription{"MA_BCCOL_EX"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::MatchedBCCollisionsExclusiveMetadata{})); + } else if (description == header::DataDescription{"MA_BCCOL_SP"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::MatchedBCCollisionsSparseMetadata{})); + } else if (description == header::DataDescription{"MA_RN3_BC_SP"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::Run3MatchedToBCSparseMetadata{})); + } else if (description == header::DataDescription{"MA_RN3_BC_EX"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::Run3MatchedToBCExclusiveMetadata{})); } else { - throw std::runtime_error("Not an extended table"); + throw std::runtime_error("Not an index table"); } } }; }}; } -AlgorithmSpec AODReaderHelpers::rootFileReaderCallback() +AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector<InputSpec> requested) { - auto callback = AlgorithmSpec{adaptStateful([](ConfigParamRegistry const& options, - DeviceSpec const& spec) { - auto filename = options.get<std::string>("aod-file"); - - // create a DataInputDirector - auto didir = std::make_shared<DataInputDirector>(filename); - if (options.isSet("json-file")) { - auto jsonFile = options.get<std::string>("json-file"); - if (!didir->readJson(jsonFile)) { - LOGP(ERROR, "Check the JSON document! Can not be properly parsed!"); - } - } - - // analyze type of requested tables - uint64_t readMask = calculateReadMask(spec.outputs, header::DataOrigin{"AOD"}); - std::vector<OutputRoute> unknowns; - if (readMask & AODTypeMask::Unknown) { - unknowns = getListOfUnknown(spec.outputs); - } - - auto counter = std::make_shared<int>(0); - return adaptStateless([readMask, - unknowns, - counter, - didir](DataAllocator& outputs, ControlService& control, DeviceSpec const& device) { - // Each parallel reader reads the files whose index is associated to - // their inputTimesliceId - assert(device.inputTimesliceId < device.maxInputTimeslices); - size_t fi = (*counter * device.maxInputTimeslices) + device.inputTimesliceId; - *counter += 1; - - if (didir->atEnd(fi)) { - LOGP(INFO, "All input files processed"); - didir->closeInputFiles(); - control.endOfStream(); - control.readyToQuit(QuitRequest::Me); - return; - } - - auto tableMaker = [&readMask, &outputs, fi, didir](auto metadata, AODTypeMask mask, char const* treeName) { - if (readMask & mask) { - - auto dh = header::DataHeader(decltype(metadata)::description(), decltype(metadata)::origin(), 0); - auto reader = didir->getTreeReader(dh, fi, treeName); - - using table_t = typename decltype(metadata)::table_t; - if (!reader || (reader->IsInvalid())) { - LOGP(ERROR, "Requested \"{}\" tree not found in input file \"{}\"", treeName, didir->getInputFilename(dh, fi)); - } else { - auto& builder = outputs.make<TableBuilder>(Output{decltype(metadata)::origin(), decltype(metadata)::description()}); - RootTableBuilderHelpers::convertASoA<table_t>(builder, *reader); - } - } - }; - tableMaker(o2::aod::CollisionsMetadata{}, AODTypeMask::Collision, "O2collision"); - tableMaker(o2::aod::StoredTracksMetadata{}, AODTypeMask::Track, "O2track"); - tableMaker(o2::aod::StoredTracksCovMetadata{}, AODTypeMask::TrackCov, "O2track"); - tableMaker(o2::aod::TracksExtraMetadata{}, AODTypeMask::TrackExtra, "O2track"); - tableMaker(o2::aod::CalosMetadata{}, AODTypeMask::Calo, "O2calo"); - tableMaker(o2::aod::CaloTriggersMetadata{}, AODTypeMask::Calo, "O2calotrigger"); - tableMaker(o2::aod::MuonsMetadata{}, AODTypeMask::Muon, "O2muon"); - tableMaker(o2::aod::MuonClustersMetadata{}, AODTypeMask::Muon, "O2muoncluster"); - tableMaker(o2::aod::ZdcsMetadata{}, AODTypeMask::Zdc, "O2zdc"); - tableMaker(o2::aod::BCsMetadata{}, AODTypeMask::BC, "O2bc"); - tableMaker(o2::aod::FT0sMetadata{}, AODTypeMask::FT0, "O2ft0"); - tableMaker(o2::aod::FV0sMetadata{}, AODTypeMask::FV0, "O2fv0"); - tableMaker(o2::aod::FDDsMetadata{}, AODTypeMask::FDD, "O2fdd"); - tableMaker(o2::aod::UnassignedTracksMetadata{}, AODTypeMask::UnassignedTrack, "O2unassignedtrack"); - tableMaker(o2::aod::Run2V0sMetadata{}, AODTypeMask::Run2V0, "Run2v0"); - tableMaker(o2::aod::McCollisionsMetadata{}, AODTypeMask::McCollision, "O2mccollision"); - tableMaker(o2::aod::McTrackLabelsMetadata{}, AODTypeMask::McTrackLabel, "O2mctracklabel"); - tableMaker(o2::aod::McCaloLabelsMetadata{}, AODTypeMask::McCaloLabel, "O2mccalolabel"); - tableMaker(o2::aod::McCollisionLabelsMetadata{}, AODTypeMask::McCollisionLabel, "O2mccollisionlabel"); - tableMaker(o2::aod::McParticlesMetadata{}, AODTypeMask::McParticle, "O2mcparticle"); - - // tables not included in the DataModel - if (readMask & AODTypeMask::Unknown) { - - // loop over unknowns - for (auto route : unknowns) { + return AlgorithmSpec::InitCallback{[requested](InitContext& ic) { - // create a TreeToTable object - auto concrete = DataSpecUtils::asConcreteDataMatcher(route.matcher); - auto dh = header::DataHeader(concrete.description, concrete.origin, concrete.subSpec); + return [requested](ProcessingContext& pc) { + auto outputs = pc.outputs(); + // spawn tables + for (auto& input : requested) { + auto description = std::visit( + overloaded{ + [](ConcreteDataMatcher const& matcher) { return matcher.description; }, + [](auto&&) { return header::DataDescription{""}; }}, + input.matcher); - auto tr = didir->getDataTree(dh, fi); - if (!tr) { - char* table; - sprintf(table, "%s/%s/%" PRIu32, concrete.origin.str, concrete.description.str, concrete.subSpec); - LOGP(ERROR, "Error while retrieving the tree for \"{}\"!", table); - return; - } + auto origin = std::visit( + overloaded{ + [](ConcreteDataMatcher const& matcher) { return matcher.origin; }, + [](auto&&) { return header::DataOrigin{""}; }}, + input.matcher); - auto o = Output(dh); - auto& t2t = outputs.make<TreeToTable>(o, tr); + auto maker = [&](auto metadata) { + using metadata_t = decltype(metadata); + using expressions = typename metadata_t::expression_pack_t; + auto original_table = pc.inputs().get<TableConsumer>(input.binding)->asArrowTable(); + return o2::framework::spawner(expressions{}, original_table.get()); + }; - // fill the table - t2t.fill(); + if (description == header::DataDescription{"TRACK:PAR"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::TracksExtensionMetadata{})); + } else if (description == header::DataDescription{"TRACK:PARCOV"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::TracksCovExtensionMetadata{})); + } else if (description == header::DataDescription{"MUON"}) { + outputs.adopt(Output{origin, description}, maker(o2::aod::MuonsExtensionMetadata{})); + } else { + throw runtime_error("Not an extended table"); } } - }); - })}; - - return callback; + }; + }}; } } // namespace o2::framework::readers diff --git a/Framework/Core/src/ASoA.cxx b/Framework/Core/src/ASoA.cxx index 2508ee2f905c6..256fafc438c2e 100644 --- a/Framework/Core/src/ASoA.cxx +++ b/Framework/Core/src/ASoA.cxx @@ -19,16 +19,26 @@ std::shared_ptr<arrow::Table> ArrowHelpers::joinTables(std::vector<std::shared_p if (tables.size() == 1) { return tables[0]; } - std::vector<std::shared_ptr<arrow::ChunkedArray>> columns; + for (auto i = 0u; i < tables.size() - 1; ++i) { + assert(tables[i]->num_rows() == tables[i + 1]->num_rows()); + } std::vector<std::shared_ptr<arrow::Field>> fields; + std::vector<std::shared_ptr<arrow::ChunkedArray>> columns; for (auto& t : tables) { - for (auto i = 0; i < t->num_columns(); ++i) { - columns.push_back(t->column(i)); - fields.push_back(t->schema()->field(i)); + auto tf = t->fields(); + std::copy(tf.begin(), tf.end(), std::back_inserter(fields)); + } + + auto schema = std::make_shared<arrow::Schema>(fields); + + if (tables[0]->num_rows() != 0) { + for (auto& t : tables) { + auto tc = t->columns(); + std::copy(tc.begin(), tc.end(), std::back_inserter(columns)); } } - return arrow::Table::Make(std::make_shared<arrow::Schema>(fields), columns); + return arrow::Table::Make(schema, columns); } std::shared_ptr<arrow::Table> ArrowHelpers::concatTables(std::vector<std::shared_ptr<arrow::Table>>&& tables) diff --git a/Framework/Core/src/AnalysisDataModelHelpers.cxx b/Framework/Core/src/AnalysisDataModelHelpers.cxx new file mode 100644 index 0000000000000..8a254e70fdcb5 --- /dev/null +++ b/Framework/Core/src/AnalysisDataModelHelpers.cxx @@ -0,0 +1,44 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "AnalysisDataModelHelpers.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/StringHelpers.h" +#include "Framework/Logger.h" +#include <boost/algorithm/string.hpp> + +namespace o2::aod::datamodel +{ +std::string getTreeName(header::DataHeader dh) +{ + std::string description = std::string(dh.dataDescription.str); + std::string origin = std::string(dh.dataOrigin.str); + + // lower case of first part of description + auto found = description.find_first_of(":"); + std::string treeName = boost::algorithm::to_lower_copy(description).substr(0, found); + + // add prefix according to origin + if (origin == "AOD") { + treeName = "O2" + treeName; + } else if (origin == "RN2") { + treeName = "Run2" + treeName; + } + + // exceptions from this + if (origin == "AOD" && description == "MCCOLLISLABEL") { + treeName = "O2mccollisionlabel"; + } + + return treeName; +} + + +} // namespace o2::aod::datamodel diff --git a/Framework/Core/src/AnalysisDataModelHelpers.h b/Framework/Core/src/AnalysisDataModelHelpers.h new file mode 100644 index 0000000000000..074303e869b8d --- /dev/null +++ b/Framework/Core/src/AnalysisDataModelHelpers.h @@ -0,0 +1,20 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ANALYSISDATAMODELHELPERS_H_ +#define O2_FRAMEWORK_ANALYSISDATAMODELHELPERS_H_ + +#include "Framework/AnalysisDataModel.h" +#include "Headers/DataHeader.h" + +namespace o2::aod::datamodel +{ +std::string getTreeName(header::DataHeader dh); +} // namespace o2::aod::datamodel +#endif // O2_FRAMEWORK_ANALYSISDATAMODELHELPERS_H_ diff --git a/Framework/Core/src/AnalysisHelpers.cxx b/Framework/Core/src/AnalysisHelpers.cxx index 109cb5e2c4dd2..65dc086d2509d 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -35,7 +35,7 @@ ROOT::RDataFrame doSelfCombinationsWith(std::unique_ptr<framework::TableConsumer using Index = RCombinedDSBlockJoinIndex<int>; auto left = std::make_unique<RArrowDS>(table, std::vector<std::string>{}); auto right = std::make_unique<RArrowDS>(table, std::vector<std::string>{}); - auto combined = std::make_unique<RCombinedDS>(std::move(left), std::move(right), std::move(std::make_unique<Index>(grouping, true, BlockCombinationRule::StrictlyUpper)), name + "_", name + "bar_"); + auto combined = std::make_unique<RCombinedDS>(std::move(left), std::move(right), std::make_unique<Index>(grouping, true, BlockCombinationRule::StrictlyUpper), name + "_", name + "bar_"); ROOT::RDataFrame rdf(std::move(combined)); return rdf; diff --git a/Framework/Core/src/AnalysisManagers.h b/Framework/Core/src/AnalysisManagers.h new file mode 100644 index 0000000000000..f045d07bc8687 --- /dev/null +++ b/Framework/Core/src/AnalysisManagers.h @@ -0,0 +1,424 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FRAMEWORK_ANALYSISMANAGERS_H +#define FRAMEWORK_ANALYSISMANAGERS_H +#include "Framework/AnalysisHelpers.h" +#include "Framework/Kernels.h" +#include "Framework/ASoA.h" +#include "Framework/ProcessingContext.h" +#include "Framework/EndOfStreamContext.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InitContext.h" +#include "Framework/RootConfigParamHelpers.h" +#include "../src/ExpressionHelpers.h" + +namespace o2::framework +{ + +template <typename ANY> +struct PartitionManager { + template <typename... T2s> + static void setPartition(ANY&, T2s&...) + { + } + + template <typename... Ts> + static void bindExternalIndices(ANY&, Ts*...) + { + } + + template <typename... Ts> + static void getBoundToExternalIndices(ANY&, Ts&...) + { + } + + static void updatePlaceholders(ANY&, InitContext&) + { + } +}; + +template <typename T> +struct PartitionManager<Partition<T>> { + template <typename T2> + static void doSetPartition(Partition<T>& partition, T2& table) + { + if constexpr (std::is_same_v<T, T2>) { + partition.setTable(table); + } + } + + template <typename... T2s> + static void setPartition(Partition<T>& partition, T2s&... tables) + { + (doSetPartition(partition, tables), ...); + } + + template <typename... Ts> + static void bindExternalIndices(Partition<T>& partition, Ts*... tables) + { + partition.bindExternalIndices(tables...); + } + + template <typename... Ts> + static void getBoundToExternalIndices(Partition<T>& partition, Ts&... tables) + { + partition.getBoundToExternalIndices(tables...); + } + + static void updatePlaceholders(Partition<T>& partition, InitContext& context) + { + partition.updatePlaceholders(context); + } +}; + +template <typename ANY> +struct FilterManager { + static bool createExpressionTrees(ANY&, std::vector<ExpressionInfo>&) + { + return false; + } + + static bool updatePlaceholders(ANY&, InitContext&) + { + return false; + } +}; + +template <> +struct FilterManager<expressions::Filter> { + static bool createExpressionTrees(expressions::Filter const& filter, std::vector<ExpressionInfo>& expressionInfos) + { + expressions::updateExpressionInfos(filter, expressionInfos); + return true; + } + + static bool updatePlaceholders(expressions::Filter& filter, InitContext& ctx) + { + expressions::updatePlaceholders(filter, ctx); + return true; + } +}; + +/// SFINAE placeholder +template <typename T> +struct OutputManager { + template <typename ANY> + static bool appendOutput(std::vector<OutputSpec>&, ANY&, uint32_t) + { + return false; + } + + template <typename ANY> + static bool prepare(ProcessingContext&, ANY&) + { + return false; + } + + template <typename ANY> + static bool postRun(EndOfStreamContext&, ANY&) + { + return true; + } + + template <typename ANY> + static bool finalize(ProcessingContext&, ANY&) + { + return true; + } +}; + +/// Produces specialization +template <typename TABLE> +struct OutputManager<Produces<TABLE>> { + static bool appendOutput(std::vector<OutputSpec>& outputs, Produces<TABLE>& what, uint32_t) + { + outputs.emplace_back(what.spec()); + return true; + } + static bool prepare(ProcessingContext& context, Produces<TABLE>& what) + { + what.resetCursor(context.outputs().make<TableBuilder>(what.ref())); + return true; + } + static bool finalize(ProcessingContext&, Produces<TABLE>&) + { + return true; + } + static bool postRun(EndOfStreamContext&, Produces<TABLE>&) + { + return true; + } +}; + +/// HistogramRegistry specialization +template <> +struct OutputManager<HistogramRegistry> { + static bool appendOutput(std::vector<OutputSpec>& outputs, HistogramRegistry& what, uint32_t hash) + { + what.setHash(hash); + outputs.emplace_back(what.spec()); + return true; + } + static bool prepare(ProcessingContext&, HistogramRegistry&) + { + return true; + } + + static bool finalize(ProcessingContext&, HistogramRegistry&) + { + return true; + } + + static bool postRun(EndOfStreamContext& context, HistogramRegistry& what) + { + context.outputs().snapshot(what.ref(), *(*what)); + return true; + } +}; + +/// OutputObj specialization +template <typename T> +struct OutputManager<OutputObj<T>> { + static bool appendOutput(std::vector<OutputSpec>& outputs, OutputObj<T>& what, uint32_t hash) + { + what.setHash(hash); + outputs.emplace_back(what.spec()); + return true; + } + static bool prepare(ProcessingContext&, OutputObj<T>&) + { + return true; + } + + static bool finalize(ProcessingContext&, OutputObj<T>&) + { + return true; + } + + static bool postRun(EndOfStreamContext& context, OutputObj<T>& what) + { + context.outputs().snapshot(what.ref(), *what); + return true; + } +}; + +/// Spawns specializations +template <typename O> +static auto extractOriginal(ProcessingContext& pc) +{ + return pc.inputs().get<TableConsumer>(aod::MetadataTrait<O>::metadata::tableLabel())->asArrowTable(); +} + +template <typename... Os> +static std::vector<std::shared_ptr<arrow::Table>> extractOriginals(framework::pack<Os...>, ProcessingContext& pc) +{ + return {extractOriginal<Os>(pc)...}; +} + +template <typename T> +struct OutputManager<Spawns<T>> { + static bool appendOutput(std::vector<OutputSpec>& outputs, Spawns<T>& what, uint32_t) + { + outputs.emplace_back(what.spec()); + return true; + } + + static bool prepare(ProcessingContext& pc, Spawns<T>& what) + { + auto original_table = soa::ArrowHelpers::joinTables(extractOriginals(what.sources_pack(), pc)); + if (original_table->schema()->fields().empty() == true) { + using base_table_t = typename Spawns<T>::base_table_t; + original_table = makeEmptyTable<base_table_t>(); + } + + what.extension = std::make_shared<typename Spawns<T>::extension_t>(o2::framework::spawner(what.pack(), original_table.get())); + what.table = std::make_shared<typename T::table_t>(soa::ArrowHelpers::joinTables({what.extension->asArrowTable(), original_table})); + return true; + } + + static bool finalize(ProcessingContext& pc, Spawns<T>& what) + { + pc.outputs().adopt(what.output(), what.asArrowTable()); + return true; + } + + static bool postRun(EndOfStreamContext&, Spawns<T>&) + { + return true; + } +}; + +/// Builds specialization +template <typename O> +static inline auto extractTypedOriginal(ProcessingContext& pc) +{ + ///FIXME: this should be done in invokeProcess() as some of the originals may be compound tables + return O{pc.inputs().get<TableConsumer>(aod::MetadataTrait<O>::metadata::tableLabel())->asArrowTable()}; +} + +template <typename... Os> +static inline auto extractOriginalsTuple(framework::pack<Os...>, ProcessingContext& pc) +{ + return std::make_tuple(extractTypedOriginal<Os>(pc)...); +} + +template <typename T, typename P> +struct OutputManager<Builds<T, P>> { + static bool appendOutput(std::vector<OutputSpec>& outputs, Builds<T, P>& what, uint32_t) + { + outputs.emplace_back(what.spec()); + return true; + } + + static bool prepare(ProcessingContext& pc, Builds<T, P>& what) + { + return what.build(what.pack(), + extractTypedOriginal<typename Builds<T, P>::Key>(pc), + extractOriginalsTuple(what.sources_pack(), pc)); + } + + static bool finalize(ProcessingContext& pc, Builds<T, P>& what) + { + pc.outputs().adopt(what.output(), what.asArrowTable()); + return true; + } + + static bool postRun(EndOfStreamContext&, Builds<T, P>&) + { + return true; + } +}; + +template <typename T> +class has_instance +{ + typedef char one; + struct two { + char x[2]; + }; + + template <typename C> + static one test(decltype(&C::instance)); + template <typename C> + static two test(...); + + public: + enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; +}; + +template <typename T> +struct ServiceManager { + template <typename ANY> + static bool prepare(InitContext&, ANY&) + { + return false; + } +}; + +template <typename T> +struct ServiceManager<Service<T>> { + static bool prepare(InitContext& context, Service<T>& service) + { + if constexpr (has_instance<T>::value) { + service.service = &(T::instance()); // Sigh... + return true; + } else { + service.service = context.services().get<T>(); + return true; + } + return false; + } +}; + +template <typename T> +struct OptionManager { + template <typename ANY> + static bool appendOption(std::vector<ConfigParamSpec>&, ANY&) + { + return false; + } + + template <typename ANY> + static bool prepare(InitContext&, ANY&) + { + return false; + } +}; + +template <typename T, typename IP> +struct OptionManager<Configurable<T, IP>> { + static bool appendOption(std::vector<ConfigParamSpec>& options, Configurable<T, IP>& what) + { + if constexpr (variant_trait_v<typename std::decay<T>::type> != VariantType::Unknown) { + options.emplace_back(ConfigParamSpec{what.name, variant_trait_v<std::decay_t<T>>, what.value, {what.help}}); + } else { + auto specs = RootConfigParamHelpers::asConfigParamSpecs<T>(what.name, what.value); + options.insert(options.end(), specs.begin(), specs.end()); + } + return true; + } + + static bool prepare(InitContext& context, Configurable<T, IP>& what) + { + if constexpr (variant_trait_v<typename std::decay<T>::type> != VariantType::Unknown) { + what.value = context.options().get<T>(what.name.c_str()); + } else { + auto pt = context.options().get<boost::property_tree::ptree>(what.name.c_str()); + what.value = RootConfigParamHelpers::as<T>(pt); + } + return true; + } +}; + +/// Manager template to facilitate extended tables spawning +template <typename T> +struct SpawnManager { + static bool requestInputs(std::vector<InputSpec>&, T const&) { return false; } +}; + +template <typename TABLE> +struct SpawnManager<Spawns<TABLE>> { + static bool requestInputs(std::vector<InputSpec>& inputs, Spawns<TABLE>& spawns) + { + auto base_specs = spawns.base_specs(); + for (auto& base_spec : base_specs) { + if (std::find_if(inputs.begin(), inputs.end(), [&](InputSpec const& spec) { return base_spec.binding == spec.binding; }) == inputs.end()) { + inputs.emplace_back(base_spec); + } + } + return true; + } +}; + +/// Manager template for building index tables +template <typename T> +struct IndexManager { + static bool requestInputs(std::vector<InputSpec>&, T const&) { return false; }; +}; + +template <typename IDX, typename P> +struct IndexManager<Builds<IDX, P>> { + static bool requestInputs(std::vector<InputSpec>& inputs, Builds<IDX, P>& builds) + { + auto base_specs = builds.base_specs(); + for (auto& base_spec : base_specs) { + if (std::find_if(inputs.begin(), inputs.end(), [&](InputSpec const& spec) { return base_spec.binding == spec.binding; }) == inputs.end()) { + inputs.emplace_back(base_spec); + } + } + return true; + } +}; +} // namespace o2::framework + +#endif // ANALYSISMANAGERS_H diff --git a/Framework/Core/src/BoostOptionsRetriever.cxx b/Framework/Core/src/BoostOptionsRetriever.cxx index 4502385de1a9d..8a8da9cb68202 100644 --- a/Framework/Core/src/BoostOptionsRetriever.cxx +++ b/Framework/Core/src/BoostOptionsRetriever.cxx @@ -14,6 +14,7 @@ #include "PropertyTreeHelpers.h" #include <boost/program_options.hpp> +#include <boost/program_options/options_description.hpp> #include <string> #include <vector> @@ -28,10 +29,10 @@ namespace o2::framework BoostOptionsRetriever::BoostOptionsRetriever(bool ignoreUnknown, int argc, char** argv) - : mDescription{"ALICE O2 Framework - Available options"}, - mIgnoreUnknown{ignoreUnknown}, + : mDescription{std::make_unique<boost::program_options::options_description>("ALICE O2 Framework - Available options")}, mArgc{argc}, - mArgv{argv} + mArgv{argv}, + mIgnoreUnknown{ignoreUnknown} { } @@ -39,8 +40,8 @@ void BoostOptionsRetriever::update(std::vector<ConfigParamSpec> const& specs, boost::property_tree::ptree& store, boost::property_tree::ptree& provenance) { - auto options = mDescription.add_options(); - for (auto& spec : specs) { + auto options = mDescription->add_options(); + for (const auto& spec : specs) { const char* name = spec.name.c_str(); const char* help = spec.help.c_str(); // FIXME: propagate default value? @@ -63,14 +64,27 @@ void BoostOptionsRetriever::update(std::vector<ConfigParamSpec> const& specs, case VariantType::Bool: options = options(name, bpo::value<bool>()->zero_tokens()->default_value(spec.defaultValue.get<bool>()), help); break; + case VariantType::ArrayInt: + case VariantType::ArrayFloat: + case VariantType::ArrayDouble: + case VariantType::ArrayBool: + case VariantType::ArrayString: + case VariantType::MatrixInt: + case VariantType::MatrixFloat: + case VariantType::MatrixDouble: + options = options(name, bpo::value<std::string>()->default_value(spec.defaultValue.asString()), help); + break; case VariantType::Unknown: case VariantType::Empty: break; }; } - auto parsed = mIgnoreUnknown ? bpo::command_line_parser(mArgc, mArgv).options(mDescription).allow_unregistered().run() - : bpo::parse_command_line(mArgc, mArgv, mDescription); + using namespace bpo::command_line_style; + auto style = (allow_short | short_allow_adjacent | short_allow_next | allow_long | long_allow_adjacent | long_allow_next | allow_sticky | allow_dash_for_short); + + auto parsed = mIgnoreUnknown ? bpo::command_line_parser(mArgc, mArgv).options(*mDescription).style(style).allow_unregistered().run() + : bpo::parse_command_line(mArgc, mArgv, *mDescription, style); bpo::variables_map vmap; bpo::store(parsed, vmap); PropertyTreeHelpers::populate(specs, store, vmap, provenance); diff --git a/Framework/Core/src/ChannelConfigurationPolicy.cxx b/Framework/Core/src/ChannelConfigurationPolicy.cxx index 1e7ad06d89b41..3facdf12ed57e 100644 --- a/Framework/Core/src/ChannelConfigurationPolicy.cxx +++ b/Framework/Core/src/ChannelConfigurationPolicy.cxx @@ -9,20 +9,25 @@ // or submit itself to any jurisdiction. #include "Framework/ChannelConfigurationPolicy.h" +#include "Framework/ConfigContext.h" -namespace o2 -{ -namespace framework +namespace o2::framework { -std::vector<ChannelConfigurationPolicy> ChannelConfigurationPolicy::createDefaultPolicies() +std::vector<ChannelConfigurationPolicy> ChannelConfigurationPolicy::createDefaultPolicies(ConfigContext const& configContext) { ChannelConfigurationPolicy defaultPolicy; + FairMQChannelConfigSpec spec; + spec.rateLogging = configContext.options().get<int>("fairmq-rate-logging"); + spec.recvBufferSize = configContext.options().get<int>("fairmq-recv-buffer-size"); + spec.sendBufferSize = configContext.options().get<int>("fairmq-send-buffer-size"); + spec.ipcPrefix = configContext.options().get<std::string>("fairmq-ipc-prefix"); + defaultPolicy.match = ChannelConfigurationPolicyHelpers::matchAny; - defaultPolicy.modifyInput = ChannelConfigurationPolicyHelpers::pullInput; - defaultPolicy.modifyOutput = ChannelConfigurationPolicyHelpers::pushOutput; + defaultPolicy.modifyInput = ChannelConfigurationPolicyHelpers::pullInput(spec); + defaultPolicy.modifyOutput = ChannelConfigurationPolicyHelpers::pushOutput(spec); + return {defaultPolicy}; } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/ChannelConfigurationPolicyHelpers.cxx b/Framework/Core/src/ChannelConfigurationPolicyHelpers.cxx index 3c33d1a612d1f..9169edccd61bb 100644 --- a/Framework/Core/src/ChannelConfigurationPolicyHelpers.cxx +++ b/Framework/Core/src/ChannelConfigurationPolicyHelpers.cxx @@ -13,9 +13,7 @@ #include <string> #include "Framework/ChannelSpec.h" -namespace o2 -{ -namespace framework +namespace o2::framework { ChannelConfigurationPolicyHelpers::PolicyMatcher ChannelConfigurationPolicyHelpers::matchAny = @@ -37,41 +35,76 @@ ChannelConfigurationPolicyHelpers::PolicyMatcher ChannelConfigurationPolicyHelpe return [nameString](std::string const&, std::string const& consumerId) -> bool { return consumerId == nameString; }; } -ChannelConfigurationPolicyHelpers::InputChannelModifier ChannelConfigurationPolicyHelpers::subscribeInput = - [](InputChannelSpec& channel) { +ChannelConfigurationPolicyHelpers::InputChannelModifier ChannelConfigurationPolicyHelpers::subscribeInput(FairMQChannelConfigSpec const& spec) +{ + return [spec](InputChannelSpec& channel) { channel.method = ChannelMethod::Connect; channel.type = ChannelType::Sub; + channel.rateLogging = spec.rateLogging; + channel.recvBufferSize = spec.recvBufferSize; + channel.recvBufferSize = spec.sendBufferSize; + channel.ipcPrefix = spec.ipcPrefix; }; +} -ChannelConfigurationPolicyHelpers::OutputChannelModifier ChannelConfigurationPolicyHelpers::publishOutput = - [](OutputChannelSpec& channel) { +ChannelConfigurationPolicyHelpers::OutputChannelModifier ChannelConfigurationPolicyHelpers::publishOutput(FairMQChannelConfigSpec const& spec) +{ + return [spec](OutputChannelSpec& channel) { channel.method = ChannelMethod::Bind; channel.type = ChannelType::Pub; + channel.rateLogging = spec.rateLogging; + channel.recvBufferSize = spec.recvBufferSize; + channel.recvBufferSize = spec.sendBufferSize; + channel.ipcPrefix = spec.ipcPrefix; }; +} -ChannelConfigurationPolicyHelpers::InputChannelModifier ChannelConfigurationPolicyHelpers::pullInput = - [](InputChannelSpec& channel) { +ChannelConfigurationPolicyHelpers::InputChannelModifier ChannelConfigurationPolicyHelpers::pullInput(FairMQChannelConfigSpec const& spec) +{ + return [spec](InputChannelSpec& channel) { channel.method = ChannelMethod::Connect; channel.type = ChannelType::Pull; + channel.rateLogging = spec.rateLogging; + channel.recvBufferSize = spec.recvBufferSize; + channel.recvBufferSize = spec.sendBufferSize; + channel.ipcPrefix = spec.ipcPrefix; }; +} -ChannelConfigurationPolicyHelpers::OutputChannelModifier ChannelConfigurationPolicyHelpers::pushOutput = - [](OutputChannelSpec& channel) { +ChannelConfigurationPolicyHelpers::OutputChannelModifier ChannelConfigurationPolicyHelpers::pushOutput(FairMQChannelConfigSpec const& spec) +{ + return [spec](OutputChannelSpec& channel) { channel.method = ChannelMethod::Bind; channel.type = ChannelType::Push; + channel.rateLogging = spec.rateLogging; + channel.recvBufferSize = spec.recvBufferSize; + channel.recvBufferSize = spec.sendBufferSize; + channel.ipcPrefix = spec.ipcPrefix; }; +} -ChannelConfigurationPolicyHelpers::InputChannelModifier ChannelConfigurationPolicyHelpers::pairInput = - [](InputChannelSpec& channel) { +ChannelConfigurationPolicyHelpers::InputChannelModifier ChannelConfigurationPolicyHelpers::pairInput(FairMQChannelConfigSpec const& spec) +{ + return [spec](InputChannelSpec& channel) { channel.method = ChannelMethod::Connect; channel.type = ChannelType::Pair; + channel.rateLogging = spec.rateLogging; + channel.recvBufferSize = spec.recvBufferSize; + channel.recvBufferSize = spec.sendBufferSize; + channel.ipcPrefix = spec.ipcPrefix; }; +} -ChannelConfigurationPolicyHelpers::OutputChannelModifier ChannelConfigurationPolicyHelpers::pairOutput = - [](OutputChannelSpec& channel) { +ChannelConfigurationPolicyHelpers::OutputChannelModifier ChannelConfigurationPolicyHelpers::pairOutput(FairMQChannelConfigSpec const& spec) +{ + return [spec](OutputChannelSpec& channel) { channel.method = ChannelMethod::Bind; channel.type = ChannelType::Pair; + channel.rateLogging = spec.rateLogging; + channel.recvBufferSize = spec.recvBufferSize; + channel.recvBufferSize = spec.sendBufferSize; + channel.ipcPrefix = spec.ipcPrefix; }; +} -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/ChannelSpecHelpers.cxx b/Framework/Core/src/ChannelSpecHelpers.cxx index 374260335f6a9..5a15a05a7367b 100644 --- a/Framework/Core/src/ChannelSpecHelpers.cxx +++ b/Framework/Core/src/ChannelSpecHelpers.cxx @@ -8,10 +8,29 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. #include "ChannelSpecHelpers.h" +#include "Framework/RuntimeError.h" #include <fmt/format.h> #include <ostream> #include <cassert> -#include <stdexcept> +#if 0 +#include <filesystem> +namespace fs = std::filesystem; +#elif __has_include(<boost/filesystem.hpp>) +#include <boost/filesystem.hpp> +namespace fs = boost::filesystem; +#endif + +namespace +{ +std::string getTmpFolder() +{ + std::string tmppath = fs::temp_directory_path().native(); + while (tmppath.back() == '/') { + tmppath.pop_back(); + } + return tmppath; +} +} // namespace namespace o2::framework { @@ -30,7 +49,7 @@ char const* ChannelSpecHelpers::typeAsString(enum ChannelType type) case ChannelType::Pair: return "pair"; } - throw std::runtime_error("Unknown ChannelType"); + throw runtime_error("Unknown ChannelType"); } char const* ChannelSpecHelpers::methodAsString(enum ChannelMethod method) @@ -41,14 +60,14 @@ char const* ChannelSpecHelpers::methodAsString(enum ChannelMethod method) case ChannelMethod::Connect: return "connect"; } - throw std::runtime_error("Unknown ChannelMethod"); + throw runtime_error("Unknown ChannelMethod"); } std::string ChannelSpecHelpers::channelUrl(OutputChannelSpec const& channel) { switch (channel.protocol) { case ChannelProtocol::IPC: - return fmt::format("ipc://{}_{},transport=shmem", channel.hostname, channel.port); + return fmt::format("ipc://{}/{}_{},transport=shmem", channel.ipcPrefix, channel.hostname, channel.port); default: return channel.method == ChannelMethod::Bind ? fmt::format("tcp://*:{}", channel.port) : fmt::format("tcp://{}:{}", channel.hostname, channel.port); @@ -59,7 +78,7 @@ std::string ChannelSpecHelpers::channelUrl(InputChannelSpec const& channel) { switch (channel.protocol) { case ChannelProtocol::IPC: - return fmt::format("ipc://{}_{},transport=shmem", channel.hostname, channel.port); + return fmt::format("ipc://{}/{}_{},transport=shmem", channel.ipcPrefix, channel.hostname, channel.port); default: return channel.method == ChannelMethod::Bind ? fmt::format("tcp://*:{}", channel.port) : fmt::format("tcp://{}:{}", channel.hostname, channel.port); diff --git a/Framework/Core/src/CommonDataProcessors.cxx b/Framework/Core/src/CommonDataProcessors.cxx index 1fd9a947bdda6..50cae08dd1c46 100644 --- a/Framework/Core/src/CommonDataProcessors.cxx +++ b/Framework/Core/src/CommonDataProcessors.cxx @@ -29,6 +29,9 @@ #include "Framework/OutputObjHeader.h" #include "Framework/TableTreeHelpers.h" #include "Framework/StringHelpers.h" +#include "Framework/ChannelSpec.h" +#include "Framework/ExternalFairMQDeviceProxy.h" +#include "Framework/RuntimeError.h" #include "TFile.h" #include "TTree.h" @@ -38,7 +41,6 @@ #include <ROOT/RArrowDS.hxx> #include <ROOT/RVec.hxx> #include <chrono> -#include <exception> #include <fstream> #include <functional> #include <memory> @@ -58,6 +60,7 @@ struct InputObjectRoute { std::string directory; uint32_t taskHash; OutputObjHandlingPolicy policy; + OutputObjSourceType sourceType; }; struct InputObject { @@ -70,17 +73,17 @@ const static std::unordered_map<OutputObjHandlingPolicy, std::string> ROOTfileNa {OutputObjHandlingPolicy::QAObject, "QAResults.root"}}; // ============================================================================= -DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& objmap, outputTasks const& tskmap) +DataProcessorSpec CommonDataProcessors::getOutputObjHistSink(outputObjects const& objmap, outputTasks const& tskmap) { auto writerFunction = [objmap, tskmap](InitContext& ic) -> std::function<void(ProcessingContext&)> { auto& callbacks = ic.services().get<CallbackService>(); auto inputObjects = std::make_shared<std::vector<std::pair<InputObjectRoute, InputObject>>>(); auto endofdatacb = [inputObjects](EndOfStreamContext& context) { - LOG(DEBUG) << "Writing merged objects to file"; + LOG(DEBUG) << "Writing merged objects and histograms to file"; if (inputObjects->empty()) { LOG(ERROR) << "Output object map is empty!"; - context.services().get<ControlService>().readyToQuit(QuitRequest::All); + context.services().get<ControlService>().readyToQuit(QuitRequest::Me); return; } std::string currentDirectory = ""; @@ -104,7 +107,41 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob currentDirectory = nextDirectory; currentFile = filename; } - (f[route.policy]->GetDirectory(currentDirectory.c_str()))->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + + // translate the list-structure created by the registry into a directory structure within the file + std::function<void(TList*, TDirectory*)> writeListToFile; + writeListToFile = [&](TList* list, TDirectory* parentDir) { + TIter next(list); + TNamed* object = nullptr; + while ((object = (TNamed*)next())) { + if (object->InheritsFrom(TList::Class())) { + writeListToFile((TList*)object, parentDir->mkdir(object->GetName(), object->GetName(), true)); + } else { + parentDir->WriteObjectAny(object, object->Class(), object->GetName()); + list->Remove(object); + } + } + }; + + TDirectory* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); + if (route.sourceType == OutputObjSourceType::HistogramRegistrySource) { + TList* outputList = (TList*)entry.obj; + outputList->SetOwner(false); + + // if registry should live in dedicated folder a TNamed object is appended to the list + if (outputList->Last()->IsA() == TNamed::Class()) { + delete outputList->Last(); + outputList->RemoveLast(); + currentDir = currentDir->mkdir(outputList->GetName(), outputList->GetName(), true); + } + + writeListToFile(outputList, currentDir); + outputList->SetOwner(); + delete outputList; + entry.obj = nullptr; + } else { + currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + } } } for (auto i = 0u; i < OutputObjHandlingPolicy::numPolicies; ++i) { @@ -113,7 +150,7 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob } } LOG(DEBUG) << "All outputs merged in their respective target files"; - context.services().get<ControlService>().readyToQuit(QuitRequest::All); + context.services().get<ControlService>().readyToQuit(QuitRequest::Me); }; callbacks.set(CallbackService::Id::EndOfStream, endofdatacb); @@ -148,6 +185,7 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob } auto policy = objh->mPolicy; + auto sourceType = objh->mSourceType; auto hash = objh->mTaskHash; obj.obj = tm.ReadObjectAny(obj.kind); @@ -170,7 +208,7 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob return; } auto nameHash = compile_time_hash(obj.name.c_str()); - InputObjectRoute key{obj.name, nameHash, taskname, hash, policy}; + InputObjectRoute key{obj.name, nameHash, taskname, hash, policy, sourceType}; auto existing = std::find_if(inputObjects->begin(), inputObjects->end(), [&](auto&& x) { return (x.first.uniqueId == nameHash) && (x.first.taskHash == hash); }); if (existing == inputObjects->end()) { inputObjects->push_back(std::make_pair(key, obj)); @@ -178,7 +216,7 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob } auto merger = existing->second.kind->GetMerge(); if (!merger) { - LOG(error) << "Already one unmergeable object found for " << obj.name; + LOG(ERROR) << "Already one unmergeable object found for " << obj.name; return; } @@ -189,7 +227,7 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob }; DataProcessorSpec spec{ - "internal-dpl-global-analysis-file-sink", + "internal-dpl-aod-global-analysis-file-sink", {InputSpec("x", DataSpecUtils::dataDescriptorMatcherFrom(header::DataOrigin{"ATSK"}))}, Outputs{}, AlgorithmSpec(writerFunction), @@ -198,81 +236,23 @@ DataProcessorSpec CommonDataProcessors::getOutputObjSink(outputObjects const& ob return spec; } +enum FileType : int { + AOD, + DANGLING +}; + // add sink for the AODs DataProcessorSpec - CommonDataProcessors::getGlobalAODSink(std::vector<InputSpec> const& OutputInputs, - std::vector<bool> const& isdangling) + CommonDataProcessors::getGlobalAODSink(std::shared_ptr<DataOutputDirector> dod, + std::vector<InputSpec> const& outputInputs) { - auto writerFunction = [OutputInputs, isdangling](InitContext& ic) -> std::function<void(ProcessingContext&)> { - LOG(DEBUG) << "======== getGlobalAODSink::Init =========="; - - auto dod = std::make_shared<DataOutputDirector>(); - - // analyze ic and take actions accordingly - // default values - std::string fnbase("AnalysisResults"); - std::string filemode("RECREATE"); - int ntfmerge = 1; - - // values from json - if (ic.options().isSet("json-file")) { - auto fnjson = ic.options().get<std::string>("json-file"); - if (!fnjson.empty()) { - auto [fnb, fmo, ntfm] = dod->readJson(fnjson); - if (!fnb.empty()) { - fnbase = fnb; - } - if (!fmo.empty()) { - filemode = fmo; - } - if (ntfm > 0) { - ntfmerge = ntfm; - } - } - } - - // values from command line options, information from json is overwritten - if (ic.options().isSet("res-file")) { - fnbase = ic.options().get<std::string>("res-file"); - } - if (ic.options().isSet("res-mode")) { - filemode = ic.options().get<std::string>("res-mode"); - } - if (ic.options().isSet("ntfmerge")) { - auto ntfm = ic.options().get<int>("ntfmerge"); - if (ntfm > 0) { - ntfmerge = ntfm; - } - } - // parse the keepString - if (ic.options().isSet("keep")) { - dod->reset(); - auto keepString = ic.options().get<std::string>("keep"); - - std::string d("dangling"); - if (d.find(keepString) == 0) { - - // use the dangling outputs - std::vector<InputSpec> danglingOutputs; - for (auto ii = 0; ii < OutputInputs.size(); ii++) { - if (isdangling[ii]) { - danglingOutputs.emplace_back(OutputInputs[ii]); - } - } - dod->readSpecs(danglingOutputs); - - } else { - - // use the keep string - dod->readString(keepString); - } - } - dod->setFilenameBase(fnbase); + auto writerFunction = [dod, outputInputs](InitContext& ic) -> std::function<void(ProcessingContext&)> { + LOGP(DEBUG, "======== getGlobalAODSink::Init =========="); // find out if any table needs to be saved bool hasOutputsToWrite = false; - for (auto& outobj : OutputInputs) { + for (auto& outobj : outputInputs) { auto ds = dod->getDataOutputDescriptors(outobj); if (ds.size() > 0) { hasOutputsToWrite = true; @@ -281,6 +261,7 @@ DataProcessorSpec } // if nothing needs to be saved then return a trivial functor + // this happens when nothing needs to be saved but there are dangling outputs if (!hasOutputsToWrite) { return [](ProcessingContext&) mutable -> void { static bool once = false; @@ -294,49 +275,83 @@ DataProcessorSpec // end of data functor is called at the end of the data stream auto endofdatacb = [dod](EndOfStreamContext& context) { dod->closeDataFiles(); - - context.services().get<ControlService>().readyToQuit(QuitRequest::All); + context.services().get<ControlService>().readyToQuit(QuitRequest::Me); }; auto& callbacks = ic.services().get<CallbackService>(); callbacks.set(CallbackService::Id::EndOfStream, endofdatacb); + // prepare map<uint64_t, uint64_t>(startTime, tfNumber) + std::map<uint64_t, uint64_t> tfNumbers; + // this functor is called once per time frame - Int_t ntf = -1; - return std::move([ntf, ntfmerge, filemode, dod](ProcessingContext& pc) mutable -> void { - LOG(DEBUG) << "======== getGlobalAODSink::processing =========="; - LOG(DEBUG) << " processing data set with " << pc.inputs().size() << " entries"; - - // return immediately if pc.inputs() is empty - auto ninputs = pc.inputs().size(); - if (ninputs == 0) { - LOG(INFO) << "No inputs available!"; + return std::move([dod, tfNumbers](ProcessingContext& pc) mutable -> void { + LOGP(DEBUG, "======== getGlobalAODSink::processing =========="); + LOGP(DEBUG, " processing data set with {} entries", pc.inputs().size()); + + // return immediately if pc.inputs() is empty. This should never happen! + if (pc.inputs().size() == 0) { + LOGP(INFO, "No inputs available!"); return; } - // increment the time frame counter ntf - ntf++; + // update tfNumbers + uint64_t startTime = 0; + uint64_t tfNumber = 0; + auto ref = pc.inputs().get("tfn"); + if (ref.spec && ref.payload) { + startTime = DataRefUtils::getHeader<DataProcessingHeader*>(ref)->startTime; + tfNumber = pc.inputs().get<uint64_t>("tfn"); + tfNumbers.insert(std::pair<uint64_t, uint64_t>(startTime, tfNumber)); + } // loop over the DataRefs which are contained in pc.inputs() for (const auto& ref : pc.inputs()) { + if (!ref.spec || !ref.payload) { + LOGP(WARNING, "The input \"{}\" is not valid and will be skipped!", ref.spec->binding); + continue; + } + + // skip non-AOD refs + if (!DataSpecUtils::partialMatch(*ref.spec, header::DataOrigin("AOD"))) { + continue; + } + startTime = DataRefUtils::getHeader<DataProcessingHeader*>(ref)->startTime; // does this need to be saved? auto dh = DataRefUtils::getHeader<header::DataHeader*>(ref); auto ds = dod->getDataOutputDescriptors(*dh); - if (ds.size() > 0) { + // get TF number fro startTime + auto it = tfNumbers.find(startTime); + if (it != tfNumbers.end()) { + tfNumber = (it->second / dod->getNumberTimeFramesToMerge()) * dod->getNumberTimeFramesToMerge(); + } else { + LOGP(FATAL, "No time frame number found for output with start time {}", startTime); + throw std::runtime_error("Processing is stopped!"); + } + // get the TableConsumer and corresponding arrow table auto s = pc.inputs().get<TableConsumer>(ref.spec->binding); auto table = s->asArrowTable(); + if (!table->Validate().ok()) { + LOGP(WARNING, "The table \"{}\" is not valid and will not be saved!", dh->description.str); + continue; + } else if (table->num_rows() <= 0) { + LOGP(WARNING, "The table \"{}\" is empty but will be saved anyway!", dh->description.str); + } // loop over all DataOutputDescriptors // a table can be saved in multiple ways // e.g. different selections of columns to different files for (auto d : ds) { + + auto fileAndFolder = dod->getFileFolder(d, tfNumber); + auto treename = fileAndFolder.folderName + d->treename; TableToTree ta2tr(table, - dod->getDataOutputFile(d, ntf, ntfmerge, filemode), - d->treename.c_str()); + fileAndFolder.file, + treename.c_str()); if (d->colnames.size() > 0) { for (auto cn : d->colnames) { @@ -357,16 +372,14 @@ DataProcessorSpec }); }; // end of writerFunction + // the command line options relevant for the writer are global + // see runDataProcessing.h DataProcessorSpec spec{ "internal-dpl-aod-writer", - OutputInputs, + outputInputs, Outputs{}, AlgorithmSpec(writerFunction), - {{"json-file", VariantType::String, {"Name of the json configuration file"}}, - {"res-file", VariantType::String, {"Default name of the output file"}}, - {"res-mode", VariantType::String, {"Creation mode of the result files: NEW, CREATE, RECREATE, UPDATE"}}, - {"ntfmerge", VariantType::Int, {"Number of time frames to merge into one file"}}, - {"keep", VariantType::String, {"Comma separated list of ORIGIN/DESCRIPTION/SUBSPECIFICATION:treename:col1/col2/..:filename"}}}}; + {}}; return spec; } @@ -380,7 +393,7 @@ DataProcessorSpec auto keepString = ic.options().get<std::string>("keep"); if (filename.empty()) { - throw std::runtime_error("output file missing"); + throw runtime_error("output file missing"); } bool hasOutputsToWrite = false; @@ -422,11 +435,13 @@ DataProcessorSpec std::vector<InputSpec> validBinaryInputs; auto onlyTimeframe = [](InputSpec const& input) { - return input.lifetime == Lifetime::Timeframe; + return (DataSpecUtils::partialMatch(input, o2::header::DataOrigin("TFN")) == false) && + input.lifetime == Lifetime::Timeframe; }; auto noTimeframe = [](InputSpec const& input) { - return input.lifetime != Lifetime::Timeframe; + return (DataSpecUtils::partialMatch(input, o2::header::DataOrigin("TFN")) == true) || + input.lifetime != Lifetime::Timeframe; }; std::copy_if(danglingOutputInputs.begin(), danglingOutputInputs.end(), @@ -445,13 +460,37 @@ DataProcessorSpec return spec; } +DataProcessorSpec CommonDataProcessors::getGlobalFairMQSink(std::vector<InputSpec> const& danglingOutputInputs) +{ + + // we build the default channel configuration from the binding of the first input + // in order to have more than one we would need to possibility to have support for + // vectored options + // use the OutputChannelSpec as a tool to create the default configuration for the out-of-band channel + OutputChannelSpec externalChannelSpec; + externalChannelSpec.name = "downstream"; + externalChannelSpec.type = ChannelType::Push; + externalChannelSpec.method = ChannelMethod::Bind; + externalChannelSpec.hostname = "localhost"; + externalChannelSpec.port = 0; + externalChannelSpec.listeners = 0; + // in principle, protocol and transport are two different things but fur simplicity + // we use ipc when shared memory is selected and the normal tcp url whith zeromq, + // this is for building the default configuration which can be simply changed from the + // command line + externalChannelSpec.protocol = ChannelProtocol::IPC; + std::string defaultChannelConfig = formatExternalChannelConfiguration(externalChannelSpec); + // at some point the formatting tool might add the transport as well so we have to check + return specifyFairMQDeviceOutputProxy("internal-dpl-injected-output-proxy", danglingOutputInputs, defaultChannelConfig.c_str()); +} + DataProcessorSpec CommonDataProcessors::getDummySink(std::vector<InputSpec> const& danglingOutputInputs) { return DataProcessorSpec{ - "internal-dpl-dummy-sink", + "internal-dpl-injected-dummy-sink", danglingOutputInputs, Outputs{}, - }; + AlgorithmSpec([](ProcessingContext& ctx) {})}; } } // namespace framework diff --git a/Framework/Core/src/CommonMessageBackends.cxx b/Framework/Core/src/CommonMessageBackends.cxx new file mode 100644 index 0000000000000..001ba31fc4184 --- /dev/null +++ b/Framework/Core/src/CommonMessageBackends.cxx @@ -0,0 +1,448 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/CommonMessageBackends.h" +#include "Framework/MessageContext.h" +#include "Framework/ArrowContext.h" +#include "Framework/StringContext.h" +#include "Framework/RawBufferContext.h" +#include "Framework/DataProcessor.h" +#include "Framework/ServiceRegistry.h" +#include "Framework/RawDeviceService.h" +#include "Framework/DeviceSpec.h" +#include "Framework/EndOfStreamContext.h" +#include "Framework/Tracing.h" +#include "Framework/DeviceMetricsInfo.h" +#include "Framework/DeviceInfo.h" + +#include <Monitoring/Monitoring.h> +#include <Headers/DataHeader.h> + +#include <options/FairMQProgOptions.h> + +#include <uv.h> +#include <boost/program_options/variables_map.hpp> +#include <csignal> + +namespace o2::framework +{ + +struct EndOfStreamContext; +struct ProcessingContext; + +namespace +{ +template <typename T> +struct CommonMessageBackendsHelpers { + static ServiceInit createCallback() + { + return [](ServiceRegistry& services, DeviceState&, fair::mq::ProgOptions& options) { + auto& device = services.get<RawDeviceService>(); + return ServiceHandle{TypeIdHelpers::uniqueId<T>(), new T(FairMQDeviceProxy{device.device()})}; + }; + } + + static ServiceProcessingCallback sendCallback() + { + return [](ProcessingContext& ctx, void* service) { + ZoneScopedN("send message callback"); + T* context = reinterpret_cast<T*>(service); + auto& device = ctx.services().get<RawDeviceService>(); + DataProcessor::doSend(*device.device(), *context, ctx.services()); + }; + } + + static ServiceProcessingCallback clearContext() + { + return [](ProcessingContext&, void* service) { + T* context = reinterpret_cast<T*>(service); + context->clear(); + }; + } + + static ServiceEOSCallback clearContextEOS() + { + return [](EndOfStreamContext&, void* service) { + T* context = reinterpret_cast<T*>(service); + context->clear(); + }; + } + + static ServiceEOSCallback sendCallbackEOS() + { + return [](EndOfStreamContext& ctx, void* service) { + T* context = reinterpret_cast<T*>(service); + auto& device = ctx.services().get<RawDeviceService>(); + DataProcessor::doSend(*device.device(), *context, ctx.services()); + }; + } +}; +} // namespace + +enum struct RateLimitingState { + UNKNOWN = 0, // No information received yet. + STARTED = 1, // Information received, new timeframe not requested. + CHANGED = 2, // Information received, new timeframe requested but not yet accounted. + BELOW_LIMIT = 3, // New metric received, we are below limit. + NEXT_ITERATION_FROM_BELOW = 4, // Iteration when previously in BELOW_LIMIT. + ABOVE_LIMIT = 5, // New metric received, we are above limit. + EMPTY = 6, // +}; + +struct RateLimitConfig { + int64_t maxMemory = 0; +}; + +static int64_t memLimit = 0; + +/// Service for common handling of rate limiting +o2::framework::ServiceSpec CommonMessageBackends::rateLimitingSpec() +{ + return ServiceSpec{"aod-rate-limiting", + [](ServiceRegistry& services, DeviceState&, fair::mq::ProgOptions& options) { + return ServiceHandle{TypeIdHelpers::uniqueId<RateLimitConfig>(), new RateLimitConfig{}}; + }, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + [](ServiceRegistry& registry, boost::program_options::variables_map const& vm) { + if (!vm["aod-memory-rate-limit"].defaulted()) { + memLimit = std::stoll(vm["aod-memory-rate-limit"].as<std::string const>()); + // registry.registerService(ServiceRegistryHelpers::handleForService<RateLimitConfig>(new RateLimitConfig{memLimit})); + } + }, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +o2::framework::ServiceSpec CommonMessageBackends::arrowBackendSpec() +{ + using o2::monitoring::Metric; + using o2::monitoring::Monitoring; + using o2::monitoring::tags::Key; + using o2::monitoring::tags::Value; + + return ServiceSpec{"arrow-backend", + CommonMessageBackendsHelpers<ArrowContext>::createCallback(), + CommonServices::noConfiguration(), + CommonMessageBackendsHelpers<ArrowContext>::clearContext(), + CommonMessageBackendsHelpers<ArrowContext>::sendCallback(), + nullptr, + nullptr, + CommonMessageBackendsHelpers<ArrowContext>::clearContextEOS(), + CommonMessageBackendsHelpers<ArrowContext>::sendCallbackEOS(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + [](ServiceRegistry& registry, + std::vector<DeviceMetricsInfo>& allDeviceMetrics, + std::vector<DeviceSpec>& specs, + std::vector<DeviceInfo>& infos, + DeviceMetricsInfo& driverMetrics, + size_t timestamp) { + int64_t totalBytesCreated = 0; + int64_t totalBytesDestroyed = 0; + int64_t totalMessagesCreated = 0; + int64_t totalMessagesDestroyed = 0; + static RateLimitingState currentState = RateLimitingState::UNKNOWN; + static auto stateMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "rate-limit-state"); + static auto totalBytesCreatedMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "total-arrow-bytes-created"); + static auto totalBytesDestroyedMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "total-arrow-bytes-destroyed"); + static auto totalMessagesCreatedMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "total-arrow-messages-created"); + static auto totalMessagesDestroyedMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "total-arrow-messages-destroyed"); + static auto totalBytesDeltaMetric = DeviceMetricsHelper::createNumericMetric<int>(driverMetrics, "arrow-bytes-delta"); + static auto totalSignalsMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "aod-reader-signals"); + static auto skippedSignalsMetric = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "aod-skipped-signals"); + static auto remainingBytes = DeviceMetricsHelper::createNumericMetric<uint64_t>(driverMetrics, "aod-remaining-bytes"); + + bool changed = false; + bool hasMetrics = false; + // Find the last timestamp when we signaled. + size_t signalIndex = DeviceMetricsHelper::metricIdxByName("aod-reader-signals", driverMetrics); + size_t lastSignalTimestamp = 0; + if (signalIndex < driverMetrics.metrics.size()) { + MetricInfo info = driverMetrics.metrics.at(signalIndex); + lastSignalTimestamp = driverMetrics.timestamps[signalIndex][info.pos - 1]; + } + + size_t lastTimestamp = 0; + size_t firstTimestamp = -1; + size_t lastDecision = 0; + for (auto& deviceMetrics : allDeviceMetrics) { + { + size_t index = DeviceMetricsHelper::metricIdxByName("arrow-bytes-created", deviceMetrics); + if (index < deviceMetrics.metrics.size()) { + hasMetrics = true; + changed |= deviceMetrics.changed.at(index); + MetricInfo info = deviceMetrics.metrics.at(index); + auto& data = deviceMetrics.uint64Metrics.at(info.storeIdx); + totalBytesCreated += (int64_t)data.at((info.pos - 1) % data.size()); + lastTimestamp = std::max(lastTimestamp, deviceMetrics.timestamps[index][info.pos - 1]); + firstTimestamp = std::min(lastTimestamp, firstTimestamp); + } + } + { + size_t index = DeviceMetricsHelper::metricIdxByName("arrow-bytes-destroyed", deviceMetrics); + if (index < deviceMetrics.metrics.size()) { + hasMetrics = true; + changed |= deviceMetrics.changed.at(index); + MetricInfo info = deviceMetrics.metrics.at(index); + auto& data = deviceMetrics.uint64Metrics.at(info.storeIdx); + totalBytesDestroyed += (int64_t)data.at((info.pos - 1) % data.size()); + firstTimestamp = std::min(lastTimestamp, firstTimestamp); + } + } + { + size_t index = DeviceMetricsHelper::metricIdxByName("arrow-messages-created", deviceMetrics); + if (index < deviceMetrics.metrics.size()) { + MetricInfo info = deviceMetrics.metrics.at(index); + auto& data = deviceMetrics.uint64Metrics.at(info.storeIdx); + totalMessagesCreated += (int64_t)data.at((info.pos - 1) % data.size()); + } + } + { + size_t index = DeviceMetricsHelper::metricIdxByName("arrow-messages-destroyed", deviceMetrics); + if (index < deviceMetrics.metrics.size()) { + MetricInfo info = deviceMetrics.metrics.at(index); + auto& data = deviceMetrics.uint64Metrics.at(info.storeIdx); + totalMessagesDestroyed += (int64_t)data.at((info.pos - 1) % data.size()); + } + } + } + if (changed) { + totalBytesCreatedMetric(driverMetrics, totalBytesCreated, timestamp); + totalBytesDestroyedMetric(driverMetrics, totalBytesDestroyed, timestamp); + totalMessagesCreatedMetric(driverMetrics, totalMessagesCreated, timestamp); + totalMessagesDestroyedMetric(driverMetrics, totalMessagesDestroyed, timestamp); + totalBytesDeltaMetric(driverMetrics, totalBytesCreated - totalBytesDestroyed, timestamp); + } + bool done = false; + static int stateTransitions = 0; + static int signalsCount = 0; + static int skippedCount = 0; + static uint64_t now = 0; + static uint64_t lastSignal = 0; + now = uv_hrtime(); + while (!done) { + stateMetric(driverMetrics, (uint64_t)(currentState), stateTransitions++); + switch (currentState) { + case RateLimitingState::UNKNOWN: { + for (auto& deviceMetrics : allDeviceMetrics) { + size_t index = DeviceMetricsHelper::metricIdxByName("arrow-bytes-created", deviceMetrics); + if (index < deviceMetrics.metrics.size()) { + currentState = RateLimitingState::STARTED; + } + } + done = true; + } break; + case RateLimitingState::STARTED: { + for (size_t di = 0; di < specs.size(); ++di) { + if (specs[di].name == "internal-dpl-aod-reader") { + if (di < infos.size() && (now - lastSignal > 10000000)) { + kill(infos[di].pid, SIGUSR1); + totalSignalsMetric(driverMetrics, signalsCount++, timestamp); + lastSignal = now; + } else { + skippedSignalsMetric(driverMetrics, skippedCount++, timestamp); + } + } + } + changed = false; + currentState = RateLimitingState::CHANGED; + } break; + case RateLimitingState::CHANGED: { + remainingBytes(driverMetrics, totalBytesCreated <= (totalBytesDestroyed + memLimit) ? (totalBytesDestroyed + memLimit) - totalBytesCreated : 0, timestamp); + if (totalBytesCreated <= totalBytesDestroyed) { + currentState = RateLimitingState::EMPTY; + } else if (totalBytesCreated <= (totalBytesDestroyed + memLimit)) { + currentState = RateLimitingState::BELOW_LIMIT; + } else { + currentState = RateLimitingState::ABOVE_LIMIT; + } + changed = false; + } break; + case RateLimitingState::EMPTY: { + for (size_t di = 0; di < specs.size(); ++di) { + if (specs[di].name == "internal-dpl-aod-reader") { + if (di < infos.size()) { + kill(infos[di].pid, SIGUSR1); + totalSignalsMetric(driverMetrics, signalsCount++, timestamp); + lastSignal = now; + } + } + } + changed = false; + currentState = RateLimitingState::NEXT_ITERATION_FROM_BELOW; + } + case RateLimitingState::BELOW_LIMIT: { + for (size_t di = 0; di < specs.size(); ++di) { + if (specs[di].name == "internal-dpl-aod-reader") { + if (di < infos.size() && (now - lastSignal > 10000000)) { + kill(infos[di].pid, SIGUSR1); + totalSignalsMetric(driverMetrics, signalsCount++, timestamp); + lastSignal = now; + } else { + skippedSignalsMetric(driverMetrics, skippedCount++, timestamp); + } + } + } + changed = false; + currentState = RateLimitingState::NEXT_ITERATION_FROM_BELOW; + } break; + case RateLimitingState::NEXT_ITERATION_FROM_BELOW: { + if (!changed) { + done = true; + } else { + currentState = RateLimitingState::CHANGED; + } + } break; + case RateLimitingState::ABOVE_LIMIT: { + if (!changed) { + done = true; + } else if (totalBytesCreated > (totalBytesDestroyed + memLimit / 3)) { + done = true; + } else { + currentState = RateLimitingState::CHANGED; + } + }; + }; + } + }, + [](ProcessingContext& ctx, void* service) { + using DataHeader = o2::header::DataHeader; + ArrowContext* arrow = reinterpret_cast<ArrowContext*>(service); + auto totalBytes = 0; + auto totalMessages = 0; + for (auto& input : ctx.inputs()) { + if (input.header == nullptr) { + continue; + } + auto dh = o2::header::get<DataHeader*>(input.header); + if (dh->serialization != o2::header::gSerializationMethodArrow) { + continue; + } + auto dph = o2::header::get<DataProcessingHeader*>(input.header); + bool forwarded = false; + for (auto const& forward : ctx.services().get<DeviceSpec const>().forwards) { + if (DataSpecUtils::match(forward.matcher, dh->dataOrigin, dh->dataDescription, dh->subSpecification)) { + forwarded = true; + break; + } + } + if (forwarded) { + continue; + } + totalBytes += dh->payloadSize; + totalMessages += 1; + } + arrow->updateBytesDestroyed(totalBytes); + arrow->updateMessagesDestroyed(totalMessages); + auto& monitoring = ctx.services().get<Monitoring>(); + monitoring.send(Metric{(uint64_t)arrow->bytesDestroyed(), "arrow-bytes-destroyed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.send(Metric{(uint64_t)arrow->messagesDestroyed(), "arrow-messages-destroyed"}.addTag(Key::Subsystem, monitoring::tags::Value::DPL)); + monitoring.flushBuffer(); + }, + ServiceKind::Serial}; +} + +o2::framework::ServiceSpec CommonMessageBackends::fairMQBackendSpec() +{ + return ServiceSpec{"fairmq-backend", + [](ServiceRegistry& services, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle { + auto& device = services.get<RawDeviceService>(); + auto context = new MessageContext(FairMQDeviceProxy{device.device()}); + auto& spec = services.get<DeviceSpec const>(); + + auto dispatcher = [&device](FairMQParts&& parts, std::string const& channel, unsigned int index) { + DataProcessor::doSend(*device.device(), std::move(parts), channel.c_str(), index); + }; + + auto matcher = [policy = spec.dispatchPolicy](o2::header::DataHeader const& header) { + if (policy.triggerMatcher == nullptr) { + return true; + } + return policy.triggerMatcher(Output{header}); + }; + + if (spec.dispatchPolicy.action == DispatchPolicy::DispatchOp::WhenReady) { + context->init(DispatchControl{dispatcher, matcher}); + } + return ServiceHandle{TypeIdHelpers::uniqueId<MessageContext>(), context}; + }, + CommonServices::noConfiguration(), + CommonMessageBackendsHelpers<MessageContext>::clearContext(), + CommonMessageBackendsHelpers<MessageContext>::sendCallback(), + nullptr, + nullptr, + CommonMessageBackendsHelpers<MessageContext>::clearContextEOS(), + CommonMessageBackendsHelpers<MessageContext>::sendCallbackEOS(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +o2::framework::ServiceSpec CommonMessageBackends::stringBackendSpec() +{ + return ServiceSpec{"string-backend", + CommonMessageBackendsHelpers<StringContext>::createCallback(), + CommonServices::noConfiguration(), + CommonMessageBackendsHelpers<StringContext>::clearContext(), + CommonMessageBackendsHelpers<StringContext>::sendCallback(), + nullptr, + nullptr, + CommonMessageBackendsHelpers<StringContext>::clearContextEOS(), + CommonMessageBackendsHelpers<StringContext>::sendCallbackEOS(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +o2::framework::ServiceSpec CommonMessageBackends::rawBufferBackendSpec() +{ + return ServiceSpec{"raw-backend", + CommonMessageBackendsHelpers<RawBufferContext>::createCallback(), + CommonServices::noConfiguration(), + CommonMessageBackendsHelpers<RawBufferContext>::clearContext(), + CommonMessageBackendsHelpers<RawBufferContext>::sendCallback(), + nullptr, + nullptr, + CommonMessageBackendsHelpers<RawBufferContext>::clearContextEOS(), + CommonMessageBackendsHelpers<RawBufferContext>::sendCallbackEOS(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +} // namespace o2::framework diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 6ba2a33344c9a..f92114b1de2bd 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -16,6 +16,14 @@ #include "Framework/DeviceSpec.h" #include "Framework/LocalRootFileService.h" #include "Framework/DataRelayer.h" +#include "Framework/Signpost.h" +#include "Framework/DataProcessingStats.h" +#include "Framework/CommonMessageBackends.h" +#include "Framework/DanglingContext.h" +#include "Framework/EndOfStreamContext.h" +#include "Framework/Tracing.h" +#include "Framework/Monitoring.h" +#include "../src/DataProcessingStatus.h" #include <Configuration/ConfigurationInterface.h> #include <Configuration/ConfigurationFactory.h> @@ -24,15 +32,27 @@ #include <options/FairMQProgOptions.h> +#include <cstdlib> + using AliceO2::InfoLogger::InfoLogger; using AliceO2::InfoLogger::InfoLoggerContext; using o2::configuration::ConfigurationFactory; using o2::configuration::ConfigurationInterface; using o2::monitoring::Monitoring; using o2::monitoring::MonitoringFactory; +using Metric = o2::monitoring::Metric; +using Key = o2::monitoring::tags::Key; +using Value = o2::monitoring::tags::Value; namespace o2::framework { + +/// This is a global service because read only +template <> +struct ServiceKindExtractor<InfoLoggerContext> { + constexpr static ServiceKind kind = ServiceKind::Global; +}; + o2::framework::ServiceSpec CommonServices::monitoringSpec() { return ServiceSpec{"monitoring", @@ -41,6 +61,19 @@ o2::framework::ServiceSpec CommonServices::monitoringSpec() return ServiceHandle{TypeIdHelpers::uniqueId<Monitoring>(), service}; }, noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -49,6 +82,19 @@ o2::framework::ServiceSpec CommonServices::infologgerContextSpec() return ServiceSpec{"infologger-contex", simpleServiceInit<InfoLoggerContext, InfoLoggerContext>(), noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -125,6 +171,19 @@ o2::framework::ServiceSpec CommonServices::infologgerSpec() return ServiceHandle{TypeIdHelpers::uniqueId<InfoLogger>(), infoLoggerService}; }, noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -141,7 +200,20 @@ o2::framework::ServiceSpec CommonServices::configurationSpec() ConfigurationFactory::getConfiguration(backend).release()}; }, noConfiguration(), - ServiceKind::Serial}; + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Global}; } o2::framework::ServiceSpec CommonServices::controlSpec() @@ -153,6 +225,19 @@ o2::framework::ServiceSpec CommonServices::controlSpec() new TextControlService(services, state)}; }, noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -162,6 +247,19 @@ o2::framework::ServiceSpec CommonServices::rootFileSpec() "localrootfile", simpleServiceInit<LocalRootFileService, LocalRootFileService>(), noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -175,6 +273,19 @@ o2::framework::ServiceSpec CommonServices::parallelSpec() new ParallelContext(spec.rank, spec.nSlots)}; }, noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -184,6 +295,19 @@ o2::framework::ServiceSpec CommonServices::timesliceIndex() "timesliceindex", simpleServiceInit<TimesliceIndex, TimesliceIndex>(), noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -193,6 +317,19 @@ o2::framework::ServiceSpec CommonServices::callbacksSpec() "callbacks", simpleServiceInit<CallbackService, CallbackService>(), noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } @@ -209,12 +346,199 @@ o2::framework::ServiceSpec CommonServices::dataRelayer() services.get<TimesliceIndex>())}; }, noConfiguration(), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +struct TracingInfrastructure { + int processingCount; +}; + +o2::framework::ServiceSpec CommonServices::tracingSpec() +{ + return ServiceSpec{ + "tracing", + [](ServiceRegistry& services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { + return ServiceHandle{TypeIdHelpers::uniqueId<TracingInfrastructure>(), new TracingInfrastructure()}; + }, + noConfiguration(), + [](ProcessingContext&, void* service) { + TracingInfrastructure* t = reinterpret_cast<TracingInfrastructure*>(service); + t->processingCount += 1; + }, + [](ProcessingContext&, void* service) { + TracingInfrastructure* t = reinterpret_cast<TracingInfrastructure*>(service); + t->processingCount += 1; + }, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +// FIXME: allow configuring the default number of threads per device +// This should probably be done by overriding the preFork +// callback and using the boost program options there to +// get the default number of threads. +o2::framework::ServiceSpec CommonServices::threadPool(int numWorkers) +{ + return ServiceSpec{ + "threadpool", + [numWorkers](ServiceRegistry& services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { + ThreadPool* pool = new ThreadPool(); + pool->poolSize = numWorkers; + return ServiceHandle{TypeIdHelpers::uniqueId<ThreadPool>(), pool}; + }, + [numWorkers](InitContext&, void* service) -> void* { + ThreadPool* t = reinterpret_cast<ThreadPool*>(service); + t->poolSize = numWorkers; + return service; + }, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + [numWorkers](ServiceRegistry& service) -> void { + auto numWorkersS = std::to_string(numWorkers); + setenv("UV_THREADPOOL_SIZE", numWorkersS.c_str(), 0); + }, + nullptr, + nullptr, + nullptr, + nullptr, + ServiceKind::Serial}; +} + +namespace +{ +/// This will send metrics for the relayer at regular intervals of +/// 5 seconds, in order to avoid overloading the system. +auto sendRelayerMetrics(ServiceRegistry& registry, DataProcessingStats& stats) -> void +{ + if (stats.beginIterationTimestamp - stats.lastSlowMetricSentTimestamp < 5000) { + return; + } + ZoneScopedN("send metrics"); + auto& relayerStats = registry.get<DataRelayer>().getStats(); + auto& monitoring = registry.get<Monitoring>(); + + O2_SIGNPOST_START(MonitoringStatus::ID, MonitoringStatus::SEND, 0, 0, O2_SIGNPOST_BLUE); + + monitoring.send(Metric{(int)relayerStats.malformedInputs, "malformed_inputs"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(int)relayerStats.droppedComputations, "dropped_computations"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(int)relayerStats.droppedIncomingMessages, "dropped_incoming_messages"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(int)relayerStats.relayedMessages, "relayed_messages"}.addTag(Key::Subsystem, Value::DPL)); + + monitoring.send(Metric{(int)stats.pendingInputs, "inputs/relayed/pending"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(int)stats.incomplete, "inputs/relayed/incomplete"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(int)stats.inputParts, "inputs/relayed/total"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{stats.lastElapsedTimeMs, "elapsed_time_ms"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{stats.lastTotalProcessedSize, "processed_input_size_byte"} + .addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(stats.lastTotalProcessedSize.load() / (stats.lastElapsedTimeMs.load() ? stats.lastElapsedTimeMs.load() : 1) / 1000), + "processing_rate_mb_s"} + .addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{stats.lastLatency.minLatency, "min_input_latency_ms"} + .addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{stats.lastLatency.maxLatency, "max_input_latency_ms"} + .addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(stats.lastTotalProcessedSize / (stats.lastLatency.maxLatency ? stats.lastLatency.maxLatency : 1) / 1000), "input_rate_mb_s"} + .addTag(Key::Subsystem, Value::DPL)); + + stats.lastSlowMetricSentTimestamp.store(stats.beginIterationTimestamp.load()); + O2_SIGNPOST_END(MonitoringStatus::ID, MonitoringStatus::SEND, 0, 0, O2_SIGNPOST_BLUE); +}; + +/// This will flush metrics only once every second. +auto flushMetrics(ServiceRegistry& registry, DataProcessingStats& stats) -> void +{ + if (stats.beginIterationTimestamp - stats.lastMetricFlushedTimestamp < 1000) { + return; + } + ZoneScopedN("flush metrics"); + auto& monitoring = registry.get<Monitoring>(); + auto& relayer = registry.get<DataRelayer>(); + + O2_SIGNPOST_START(MonitoringStatus::ID, MonitoringStatus::FLUSH, 0, 0, O2_SIGNPOST_RED); + // Send all the relevant metrics for the relayer to update the GUI + // FIXME: do a delta with the previous version if too many metrics are still + // sent... + for (size_t si = 0; si < stats.statesSize.load(); ++si) { + auto value = std::atomic_load_explicit(&stats.relayerState[si], std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + monitoring.send({value, fmt::format("data_relayer/{}", si)}); + } + relayer.sendContextState(); + monitoring.flushBuffer(); + stats.lastMetricFlushedTimestamp.store(stats.beginIterationTimestamp.load()); + O2_SIGNPOST_END(MonitoringStatus::ID, MonitoringStatus::FLUSH, 0, 0, O2_SIGNPOST_RED); +}; +} // namespace + +o2::framework::ServiceSpec CommonServices::dataProcessingStats() +{ + return ServiceSpec{ + "data-processing-stats", + [](ServiceRegistry& services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { + DataProcessingStats* stats = new DataProcessingStats(); + return ServiceHandle{TypeIdHelpers::uniqueId<DataProcessingStats>(), stats}; + }, + noConfiguration(), + nullptr, + nullptr, + [](DanglingContext& context, void* service) { + DataProcessingStats* stats = (DataProcessingStats*)service; + sendRelayerMetrics(context.services(), *stats); + flushMetrics(context.services(), *stats); + }, + [](DanglingContext& context, void* service) { + DataProcessingStats* stats = (DataProcessingStats*)service; + sendRelayerMetrics(context.services(), *stats); + flushMetrics(context.services(), *stats); + }, + [](EndOfStreamContext& context, void* service) { + DataProcessingStats* stats = (DataProcessingStats*)service; + sendRelayerMetrics(context.services(), *stats); + flushMetrics(context.services(), *stats); + }, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, ServiceKind::Serial}; } -std::vector<ServiceSpec> CommonServices::defaultServices() +std::vector<ServiceSpec> CommonServices::defaultServices(int numThreads) { - return { + std::vector<ServiceSpec> specs{ timesliceIndex(), monitoringSpec(), infologgerContextSpec(), @@ -224,7 +548,16 @@ std::vector<ServiceSpec> CommonServices::defaultServices() rootFileSpec(), parallelSpec(), callbacksSpec(), - dataRelayer()}; + dataRelayer(), + dataProcessingStats(), + CommonMessageBackends::fairMQBackendSpec(), + CommonMessageBackends::arrowBackendSpec(), + CommonMessageBackends::stringBackendSpec(), + CommonMessageBackends::rawBufferBackendSpec()}; + if (numThreads) { + specs.push_back(threadPool(numThreads)); + } + return specs; } } // namespace o2::framework diff --git a/Framework/Core/src/CompletionPolicy.cxx b/Framework/Core/src/CompletionPolicy.cxx index 83c4334852b3b..06e1ef159fe3b 100644 --- a/Framework/Core/src/CompletionPolicy.cxx +++ b/Framework/Core/src/CompletionPolicy.cxx @@ -14,9 +14,7 @@ #include <functional> #include <iostream> -namespace o2 -{ -namespace framework +namespace o2::framework { /// By default the CompletionPolicy matches any Device and only runs a @@ -24,7 +22,9 @@ namespace framework std::vector<CompletionPolicy> CompletionPolicy::createDefaultPolicies() { - return {CompletionPolicyHelpers::consumeWhenAll()}; + return { + CompletionPolicyHelpers::defineByNameOrigin("internal-dpl-aod-writer", "TFN", CompletionOp::Consume), + CompletionPolicyHelpers::consumeWhenAll()}; } std::ostream& operator<<(std::ostream& oss, CompletionPolicy::CompletionOp const& val) @@ -46,5 +46,4 @@ std::ostream& operator<<(std::ostream& oss, CompletionPolicy::CompletionOp const return oss; } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index b481823f0ba1f..ab19536a93843 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -12,6 +12,7 @@ #include "Framework/CompletionPolicy.h" #include "Framework/DeviceSpec.h" #include "Framework/CompilerBuiltins.h" +#include "Framework/Logger.h" #include <cassert> #include <regex> @@ -21,6 +22,51 @@ namespace o2 namespace framework { +CompletionPolicy CompletionPolicyHelpers::defineByNameOrigin(std::string const& name, std::string const& origin, CompletionPolicy::CompletionOp op) +{ + auto matcher = [name](DeviceSpec const& device) -> bool { + return std::regex_match(device.name.begin(), device.name.end(), std::regex(name)); + }; + + auto originReceived = std::make_shared<std::vector<uint64_t>>(); + + auto callback = [originReceived, origin, op](CompletionPolicy::InputSet inputRefs) -> CompletionPolicy::CompletionOp { + // update list of the start times of inputs with origin @origin + for (auto& ref : inputRefs) { + if (ref.header != nullptr) { + auto header = CompletionPolicyHelpers::getHeader<o2::header::DataHeader>(ref); + if (header->dataOrigin.str == origin) { + auto startTime = DataRefUtils::getHeader<DataProcessingHeader*>(ref)->startTime; + auto it = std::find(originReceived->begin(), originReceived->end(), startTime); + if (it == originReceived->end()) { + originReceived->emplace_back(startTime); + } + } + } + } + + // find out if all inputs which are not of origin @origin have a corresponding entry in originReceived + // if one is missing then we have to wait + for (auto& ref : inputRefs) { + if (ref.header != nullptr) { + auto header = CompletionPolicyHelpers::getHeader<o2::header::DataHeader>(ref); + if (header->dataOrigin.str != origin) { + auto startTime = DataRefUtils::getHeader<DataProcessingHeader*>(ref)->startTime; + auto it = std::find(originReceived->begin(), originReceived->end(), startTime); + if (it == originReceived->end()) { + LOGP(INFO, "Have to wait until message of origin {} with startTime {} has arrived.", origin, startTime); + return CompletionPolicy::CompletionOp::Wait; + } + } + } + } + return op; + }; + return CompletionPolicy{"wait-origin", matcher, callback}; + + O2_BUILTIN_UNREACHABLE(); +} + CompletionPolicy CompletionPolicyHelpers::defineByName(std::string const& name, CompletionPolicy::CompletionOp op) { auto matcher = [name](DeviceSpec const& device) -> bool { diff --git a/Framework/Core/src/ConfigParamsHelper.cxx b/Framework/Core/src/ConfigParamsHelper.cxx index 72b70ea2a36ca..690ee53d11284 100644 --- a/Framework/Core/src/ConfigParamsHelper.cxx +++ b/Framework/Core/src/ConfigParamsHelper.cxx @@ -17,9 +17,7 @@ namespace bpo = boost::program_options; -namespace o2 -{ -namespace framework +namespace o2::framework { /// this creates the boost program options description from the ConfigParamSpec @@ -30,12 +28,11 @@ void ConfigParamsHelper::populateBoostProgramOptions( bpo::options_description vetos) { auto proxy = options.add_options(); - for (auto& spec : specs) { + for (auto const& spec : specs) { // skip everything found in the veto definition - if (vetos.find_nothrow(spec.name, false)) + if (vetos.find_nothrow(spec.name, false) != nullptr) { continue; - const char* name = spec.name.c_str(); - const char* help = spec.help.c_str(); + } switch (spec.type) { // FIXME: Should we handle int and size_t diffently? @@ -58,6 +55,30 @@ void ConfigParamsHelper::populateBoostProgramOptions( case VariantType::Bool: addConfigSpecOption<VariantType::Bool>(spec, options); break; + case VariantType::ArrayInt: + addConfigSpecOption<VariantType::ArrayInt>(spec, options); + break; + case VariantType::ArrayFloat: + addConfigSpecOption<VariantType::ArrayFloat>(spec, options); + break; + case VariantType::ArrayDouble: + addConfigSpecOption<VariantType::ArrayDouble>(spec, options); + break; + case VariantType::ArrayBool: + addConfigSpecOption<VariantType::ArrayBool>(spec, options); + break; + case VariantType::ArrayString: + addConfigSpecOption<VariantType::ArrayString>(spec, options); + break; + case VariantType::MatrixInt: + addConfigSpecOption<VariantType::MatrixInt>(spec, options); + break; + case VariantType::MatrixFloat: + addConfigSpecOption<VariantType::MatrixFloat>(spec, options); + break; + case VariantType::MatrixDouble: + addConfigSpecOption<VariantType::MatrixDouble>(spec, options); + break; case VariantType::Unknown: case VariantType::Empty: break; @@ -75,10 +96,11 @@ bool ConfigParamsHelper::dpl2BoostOptions(const std::vector<ConfigParamSpec>& sp for (const auto& configSpec : spec) { // skip everything found in the veto definition try { - if (vetos.find_nothrow(configSpec.name, false)) + if (vetos.find_nothrow(configSpec.name, false) != nullptr) { continue; + } } catch (boost::program_options::ambiguous_option& e) { - for (auto& alternative : e.alternatives()) { + for (auto const& alternative : e.alternatives()) { std::cerr << alternative << std::endl; } throw; @@ -90,7 +112,7 @@ bool ConfigParamsHelper::dpl2BoostOptions(const std::vector<ConfigParamSpec>& sp if (configSpec.type != VariantType::Bool) { if (configSpec.defaultValue.type() != VariantType::Empty) { options.add_options()(configSpec.name.c_str(), - bpo::value<std::string>()->default_value(defaultValue.str().c_str()), + bpo::value<std::string>()->default_value(defaultValue.str()), configSpec.help.c_str()); } else { options.add_options()(configSpec.name.c_str(), @@ -113,5 +135,4 @@ bool ConfigParamsHelper::dpl2BoostOptions(const std::vector<ConfigParamSpec>& sp return haveOption; } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/DDSConfigHelpers.cxx b/Framework/Core/src/DDSConfigHelpers.cxx index d2d76ccbd7a6a..83e6d36edfdc9 100644 --- a/Framework/Core/src/DDSConfigHelpers.cxx +++ b/Framework/Core/src/DDSConfigHelpers.cxx @@ -46,6 +46,13 @@ void dumpDeviceSpec2DDS(std::ostream& out, ai++; continue; } + // If channel-prefix is empty do not print it out + if (strcmp(arg, "--channel-prefix") == 0 && + ai + 1 < execution.args.size() && + *execution.args[ai + 1] == 0) { + ai++; + continue; + } out << arg << " "; } out << "--plugin-search-path $FAIRMQ_ROOT/lib --plugin dds"; diff --git a/Framework/Core/src/DataAllocator.cxx b/Framework/Core/src/DataAllocator.cxx index d2086c8ac0529..48d9eacaf3586 100644 --- a/Framework/Core/src/DataAllocator.cxx +++ b/Framework/Core/src/DataAllocator.cxx @@ -26,9 +26,7 @@ #include <TClonesArray.h> -namespace o2 -{ -namespace framework +namespace o2::framework { using DataHeader = o2::header::DataHeader; @@ -36,11 +34,11 @@ using DataDescription = o2::header::DataDescription; using DataProcessingHeader = o2::framework::DataProcessingHeader; DataAllocator::DataAllocator(TimingInfo* timingInfo, - ContextRegistry* contextRegistry, + ServiceRegistry* contextRegistry, const AllowedOutputRoutes& routes) : mAllowedOutputRoutes{routes}, mTimingInfo{timingInfo}, - mContextRegistry{contextRegistry} + mRegistry{contextRegistry} { } @@ -52,24 +50,24 @@ std::string const& DataAllocator::matchDataHeader(const Output& spec, size_t tim return output.channel; } } - std::ostringstream str; - str << "Worker is not authorised to create message with " - << "origin(" << spec.origin.as<std::string>() << ")" - << "description(" << spec.description.as<std::string>() << ")" - << "subSpec(" << spec.subSpec << ")"; - throw std::runtime_error(str.str()); + throw runtime_error_f( + "Worker is not authorised to create message with " + "origin(%s) description(%s) subSpec(%d)", + spec.origin.as<std::string>().c_str(), + spec.description.as<std::string>().c_str(), + spec.subSpec); } DataChunk& DataAllocator::newChunk(const Output& spec, size_t size) { std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - auto context = mContextRegistry->get<MessageContext>(); + auto& context = mRegistry->get<MessageContext>(); FairMQMessagePtr headerMessage = headerMessageFromOutput(spec, channel, // o2::header::gSerializationMethodNone, // size // ); - auto& co = context->add<MessageContext::ContainerRefObject<DataChunk>>(std::move(headerMessage), channel, 0, size); + auto& co = context.add<MessageContext::ContainerRefObject<DataChunk>>(std::move(headerMessage), channel, 0, size); return co; } @@ -85,8 +83,8 @@ void DataAllocator::adoptChunk(const Output& spec, char* buffer, size_t size, fa ); // FIXME: how do we want to use subchannels? time based parallelism? - auto context = mContextRegistry->get<MessageContext>(); - context->add<MessageContext::TrivialObject>(std::move(headerMessage), channel, 0, buffer, size, freefn, hint); + auto& context = mRegistry->get<MessageContext>(); + context.add<MessageContext::TrivialObject>(std::move(headerMessage), channel, 0, buffer, size, freefn, hint); } FairMQMessagePtr DataAllocator::headerMessageFromOutput(Output const& spec, // @@ -102,9 +100,9 @@ FairMQMessagePtr DataAllocator::headerMessageFromOutput(Output const& spec, dh.payloadSerializationMethod = method; DataProcessingHeader dph{mTimingInfo->timeslice, 1}; - auto context = mContextRegistry->get<MessageContext>(); + auto& context = mRegistry->get<MessageContext>(); - auto channelAlloc = o2::pmr::getTransportAllocator(context->proxy().getTransport(channel, 0)); + auto channelAlloc = o2::pmr::getTransportAllocator(context.proxy().getTransport(channel, 0)); return o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, spec.metaHeader}); } @@ -119,11 +117,11 @@ void DataAllocator::addPartToContext(FairMQMessagePtr&& payloadMessage, const Ou const DataHeader* cdh = o2::header::get<DataHeader*>(headerMessage->GetData()); DataHeader* dh = const_cast<DataHeader*>(cdh); dh->payloadSize = payloadMessage->GetSize(); - auto context = mContextRegistry->get<MessageContext>(); + auto& context = mRegistry->get<MessageContext>(); // make_scoped creates the context object inside of a scope handler, since it goes out of // scope immediately, the created object is scheduled and can be directly sent if the context // is configured with the dispatcher callback - context->make_scoped<MessageContext::TrivialObject>(std::move(headerMessage), std::move(payloadMessage), channel); + context.make_scoped<MessageContext::TrivialObject>(std::move(headerMessage), std::move(payloadMessage), channel); } void DataAllocator::adopt(const Output& spec, std::string* ptr) @@ -133,7 +131,7 @@ void DataAllocator::adopt(const Output& spec, std::string* ptr) // the correct payload size is set later when sending the // StringContext, see DataProcessor::doSend auto header = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodNone, 0); - mContextRegistry->get<StringContext>()->addString(std::move(header), std::move(payload), channel); + mRegistry->get<StringContext>().addString(std::move(header), std::move(payload), channel); assert(payload.get() == nullptr); } @@ -141,9 +139,9 @@ void DataAllocator::adopt(const Output& spec, TableBuilder* tb) { std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); auto header = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodArrow, 0); - auto context = mContextRegistry->get<ArrowContext>(); + auto& context = mRegistry->get<ArrowContext>(); - auto creator = [device = context->proxy().getDevice()](size_t s) -> std::unique_ptr<FairMQMessage> { return device->NewMessage(s); }; + auto creator = [device = context.proxy().getDevice()](size_t s) -> std::unique_ptr<FairMQMessage> { return device->NewMessage(s); }; auto buffer = std::make_shared<FairMQResizableBuffer>(creator); /// To finalise this we write the table to the buffer. @@ -152,28 +150,33 @@ void DataAllocator::adopt(const Output& spec, TableBuilder* tb) std::shared_ptr<TableBuilder> p(tb); auto finalizer = [payload = p](std::shared_ptr<FairMQResizableBuffer> b) -> void { auto table = payload->finalize(); + if (O2_BUILTIN_UNLIKELY(table->num_rows() == 0)) { + LOG(DEBUG) << "Empty table was produced: " << table->ToString(); + } auto stream = std::make_shared<arrow::io::BufferOutputStream>(b); auto outBatch = arrow::ipc::NewStreamWriter(stream.get(), table->schema()); - auto outStatus = outBatch.ValueOrDie()->WriteTable(*table); - if (outStatus.ok() == false) { - throw std::runtime_error("Unable to Write table"); + if (outBatch.ok() == true) { + auto outStatus = outBatch.ValueOrDie()->WriteTable(*table); + if (outStatus.ok() == false) { + throw std::runtime_error("Unable to Write table"); + } + } else { + throw ::std::runtime_error("Unable to create batch writer"); } }; - assert(context); - context->addBuffer(std::move(header), buffer, std::move(finalizer), channel); + context.addBuffer(std::move(header), buffer, std::move(finalizer), channel); } void DataAllocator::adopt(const Output& spec, TreeToTable* t2t) { std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); - LOG(INFO) << "DataAllocator::adopt channel " << channel.c_str(); auto header = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodArrow, 0); - auto context = mContextRegistry->get<ArrowContext>(); + auto& context = mRegistry->get<ArrowContext>(); - auto creator = [device = context->proxy().getDevice()](size_t s) -> std::unique_ptr<FairMQMessage> { + auto creator = [device = context.proxy().getDevice()](size_t s) -> std::unique_ptr<FairMQMessage> { return device->NewMessage(s); }; auto buffer = std::make_shared<FairMQResizableBuffer>(creator); @@ -181,33 +184,32 @@ void DataAllocator::adopt(const Output& spec, TreeToTable* t2t) /// To finalise this we write the table to the buffer. /// FIXME: most likely not a great idea. We should probably write to the buffer /// directly in the TableBuilder, incrementally. - std::shared_ptr<TreeToTable> p(t2t); - auto finalizer = [payload = p](std::shared_ptr<FairMQResizableBuffer> b) -> void { + auto finalizer = [payload = t2t](std::shared_ptr<FairMQResizableBuffer> b) -> void { auto table = payload->finalize(); - LOG(INFO) << "DataAllocator Table created!"; - LOG(INFO) << "Number of columns " << table->num_columns(); - LOG(INFO) << "Number of rows " << table->num_rows(); auto stream = std::make_shared<arrow::io::BufferOutputStream>(b); - std::shared_ptr<arrow::ipc::RecordBatchWriter> writer; auto outBatch = arrow::ipc::NewStreamWriter(stream.get(), table->schema()); - auto outStatus = outBatch.ValueOrDie()->WriteTable(*table); - if (outStatus.ok() == false) { - throw std::runtime_error("Unable to Write table"); + if (outBatch.ok() == true) { + auto outStatus = outBatch.ValueOrDie()->WriteTable(*table); + if (outStatus.ok() == false) { + throw std::runtime_error("Unable to Write table"); + } + } else { + throw ::std::runtime_error("Unable to create batch writer"); } + delete payload; }; - assert(context); - context->addBuffer(std::move(header), buffer, std::move(finalizer), channel); + context.addBuffer(std::move(header), buffer, std::move(finalizer), channel); } void DataAllocator::adopt(const Output& spec, std::shared_ptr<arrow::Table> ptr) { std::string const& channel = matchDataHeader(spec, mTimingInfo->timeslice); auto header = headerMessageFromOutput(spec, channel, o2::header::gSerializationMethodArrow, 0); - auto context = mContextRegistry->get<ArrowContext>(); + auto& context = mRegistry->get<ArrowContext>(); - auto creator = [device = context->proxy().getDevice()](size_t s) -> std::unique_ptr<FairMQMessage> { + auto creator = [device = context.proxy().getDevice()](size_t s) -> std::unique_ptr<FairMQMessage> { return device->NewMessage(s); }; auto buffer = std::make_shared<FairMQResizableBuffer>(creator); @@ -215,20 +217,23 @@ void DataAllocator::adopt(const Output& spec, std::shared_ptr<arrow::Table> ptr) auto writer = [table = ptr](std::shared_ptr<FairMQResizableBuffer> b) -> void { auto stream = std::make_shared<arrow::io::BufferOutputStream>(b); auto outBatch = arrow::ipc::NewStreamWriter(stream.get(), table->schema()); - auto outStatus = outBatch.ValueOrDie()->WriteTable(*table); - if (outStatus.ok() == false) { - throw std::runtime_error("Unable to Write table"); + if (outBatch.ok() == true) { + auto outStatus = outBatch.ValueOrDie()->WriteTable(*table); + if (outStatus.ok() == false) { + throw std::runtime_error("Unable to Write table"); + } + } else { + throw ::std::runtime_error("Unable to create batch writer"); } }; - assert(context); - context->addBuffer(std::move(header), buffer, std::move(writer), channel); + context.addBuffer(std::move(header), buffer, std::move(writer), channel); } void DataAllocator::snapshot(const Output& spec, const char* payload, size_t payloadSize, o2::header::SerializationMethod serializationMethod) { - auto proxy = mContextRegistry->get<MessageContext>()->proxy(); + auto& proxy = mRegistry->get<MessageContext>().proxy(); FairMQMessagePtr payloadMessage(proxy.createMessage(payloadSize)); memcpy(payloadMessage->GetData(), payload, payloadSize); @@ -238,7 +243,7 @@ void DataAllocator::snapshot(const Output& spec, const char* payload, size_t pay Output DataAllocator::getOutputByBind(OutputRef&& ref) { if (ref.label.empty()) { - throw std::runtime_error("Invalid (empty) OutputRef provided."); + throw runtime_error("Invalid (empty) OutputRef provided."); } for (auto ri = 0ul, re = mAllowedOutputRoutes.size(); ri != re; ++ri) { if (mAllowedOutputRoutes[ri].matcher.binding.value == ref.label) { @@ -247,7 +252,7 @@ Output DataAllocator::getOutputByBind(OutputRef&& ref) return Output{dataType.origin, dataType.description, ref.subSpec, spec.lifetime, std::move(ref.headerStack)}; } } - throw std::runtime_error("Unable to find OutputSpec with label " + ref.label); + throw runtime_error_f("Unable to find OutputSpec with label %s", ref.label.c_str()); O2_BUILTIN_UNREACHABLE(); } @@ -261,5 +266,4 @@ bool DataAllocator::isAllowed(Output const& query) return false; } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/DataDescriptorMatcher.cxx b/Framework/Core/src/DataDescriptorMatcher.cxx index c067233a6d3b2..ba08fd67ff50e 100644 --- a/Framework/Core/src/DataDescriptorMatcher.cxx +++ b/Framework/Core/src/DataDescriptorMatcher.cxx @@ -13,6 +13,7 @@ #include "Framework/DataMatcherWalker.h" #include "Framework/DataProcessingHeader.h" #include "Framework/VariantHelpers.h" +#include "Framework/RuntimeError.h" #include <iostream> namespace o2 @@ -63,7 +64,7 @@ bool OriginValueMatcher::match(header::DataHeader const& header, VariableContext } else if (auto s = std::get_if<std::string>(&mValue)) { return strncmp(header.dataOrigin.str, s->c_str(), 4) == 0; } - throw std::runtime_error("Mismatching type for variable"); + throw runtime_error("Mismatching type for variable"); } bool DescriptionValueMatcher::match(header::DataHeader const& header, VariableContext& context) const @@ -79,7 +80,7 @@ bool DescriptionValueMatcher::match(header::DataHeader const& header, VariableCo } else if (auto s = std::get_if<std::string>(&this->mValue)) { return strncmp(header.dataDescription.str, s->c_str(), 16) == 0; } - throw std::runtime_error("Mismatching type for variable"); + throw runtime_error("Mismatching type for variable"); } bool SubSpecificationTypeValueMatcher::match(header::DataHeader const& header, VariableContext& context) const @@ -94,7 +95,7 @@ bool SubSpecificationTypeValueMatcher::match(header::DataHeader const& header, V } else if (auto v = std::get_if<header::DataHeader::SubSpecificationType>(&mValue)) { return header.subSpecification == *v; } - throw std::runtime_error("Mismatching type for variable"); + throw runtime_error("Mismatching type for variable"); } /// This will match the timing information which is currently in @@ -112,7 +113,7 @@ bool StartTimeValueMatcher::match(DataProcessingHeader const& dph, VariableConte } else if (auto v = std::get_if<uint64_t>(&mValue)) { return (dph.startTime / mScale) == *v; } - throw std::runtime_error("Mismatching type for variable"); + throw runtime_error("Mismatching type for variable"); } DataDescriptorMatcher::DataDescriptorMatcher(DataDescriptorMatcher const& other) @@ -241,19 +242,19 @@ bool DataDescriptorMatcher::match(char const* d, VariableContext& context) const if (auto pval0 = std::get_if<OriginValueMatcher>(&mLeft)) { auto dh = o2::header::get<header::DataHeader*>(d); if (dh == nullptr) { - throw std::runtime_error("Cannot find DataHeader"); + throw runtime_error("Cannot find DataHeader"); } leftValue = pval0->match(*dh, context); } else if (auto pval1 = std::get_if<DescriptionValueMatcher>(&mLeft)) { auto dh = o2::header::get<header::DataHeader*>(d); if (dh == nullptr) { - throw std::runtime_error("Cannot find DataHeader"); + throw runtime_error("Cannot find DataHeader"); } leftValue = pval1->match(*dh, context); } else if (auto pval2 = std::get_if<SubSpecificationTypeValueMatcher>(&mLeft)) { auto dh = o2::header::get<header::DataHeader*>(d); if (dh == nullptr) { - throw std::runtime_error("Cannot find DataHeader"); + throw runtime_error("Cannot find DataHeader"); } leftValue = pval2->match(*dh, context); } else if (auto pval3 = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&mLeft)) { @@ -263,11 +264,11 @@ bool DataDescriptorMatcher::match(char const* d, VariableContext& context) const } else if (auto pval5 = std::get_if<StartTimeValueMatcher>(&mLeft)) { auto dph = o2::header::get<DataProcessingHeader*>(d); if (dph == nullptr) { - throw std::runtime_error("Cannot find DataProcessingHeader"); + throw runtime_error("Cannot find DataProcessingHeader"); } leftValue = pval5->match(*dph, context); } else { - throw std::runtime_error("Bad parsing tree"); + throw runtime_error("Bad parsing tree"); } // Common speedup. if (mOp == Op::And && leftValue == false) { @@ -309,12 +310,12 @@ bool DataDescriptorMatcher::match(char const* d, VariableContext& context) const case Op::Just: return leftValue; } - throw std::runtime_error("Bad parsing tree"); + throw runtime_error("Bad parsing tree"); }; bool DataDescriptorMatcher::operator==(DataDescriptorMatcher const& other) const { - if (mOp != this->mOp) { + if (other.mOp != this->mOp) { return false; } diff --git a/Framework/Core/src/DataDescriptorQueryBuilder.cxx b/Framework/Core/src/DataDescriptorQueryBuilder.cxx index 0a3737b2ba971..2edbb4f84be5d 100644 --- a/Framework/Core/src/DataDescriptorQueryBuilder.cxx +++ b/Framework/Core/src/DataDescriptorQueryBuilder.cxx @@ -285,8 +285,9 @@ DataDescriptorQuery DataDescriptorQueryBuilder::buildFromExtendedKeepConfig(std: s.end(), delim2, -1); - if (iter2 == end) + if (iter2 == end) { continue; + } s = iter2->str(); // create the corresponding DataDescriptorMatcher diff --git a/Framework/Core/src/DataInputDirector.cxx b/Framework/Core/src/DataInputDirector.cxx index a123c37704e2b..e5143a10637d8 100644 --- a/Framework/Core/src/DataInputDirector.cxx +++ b/Framework/Core/src/DataInputDirector.cxx @@ -10,17 +10,48 @@ #include "Framework/DataInputDirector.h" #include "Framework/DataDescriptorQueryBuilder.h" #include "Framework/Logger.h" +#include "AnalysisDataModelHelpers.h" #include "rapidjson/document.h" #include "rapidjson/prettywriter.h" #include "rapidjson/filereadstream.h" +#include "TGrid.h" +#include "TObjString.h" + namespace o2 { namespace framework { using namespace rapidjson; +FileNameHolder* makeFileNameHolder(std::string fileName) +{ + auto fileNameHolder = new FileNameHolder(); + fileNameHolder->fileName = fileName; + + return fileNameHolder; +} + +DataInputDescriptor::DataInputDescriptor(bool alienSupport) +{ + mAlienSupport = alienSupport; +} + +void DataInputDescriptor::printOut() +{ + LOGP(INFO, "DataInputDescriptor"); + LOGP(INFO, " Table name : {}", tablename); + LOGP(INFO, " Tree name : {}", treename); + LOGP(INFO, " Input files file : {}", getInputfilesFilename()); + LOGP(INFO, " File name regex : {}", getFilenamesRegexString()); + LOGP(INFO, " Input files : {}", mfilenames.size()); + for (auto fn : mfilenames) { + LOGP(INFO, " {} {}", fn->fileName, fn->numberOfTimeFrames); + } + LOGP(INFO, " Total number of TF: {}", getNumberTimeFrames()); +} + std::string DataInputDescriptor::getInputfilesFilename() { return (minputfilesFile.empty() && minputfilesFilePtr) ? (std::string)*minputfilesFilePtr : minputfilesFile; @@ -36,34 +67,108 @@ std::regex DataInputDescriptor::getFilenamesRegex() return std::regex(getFilenamesRegexString()); } -void DataInputDescriptor::addFilename(std::string fn) +void DataInputDescriptor::addFileNameHolder(FileNameHolder* fn) { + // remove leading file:// from file name + if (fn->fileName.rfind("file://", 0) == 0) { + fn->fileName.erase(0, 7); + } else if (!mAlienSupport && fn->fileName.rfind("alien://", 0) == 0) { + LOGP(DEBUG, "AliEn file requested. Enabling support."); + TGrid::Connect("alien://"); + mAlienSupport = true; + } + + mtotalNumberTimeFrames += fn->numberOfTimeFrames; mfilenames.emplace_back(fn); } -TFile* DataInputDescriptor::getInputFile(int counter) +bool DataInputDescriptor::setFile(int counter) { + // no files left + if (counter >= getNumberInputfiles()) { + return false; + } - if (counter < getNumberInputfiles()) { - if (mcurrentFile) { - if (mcurrentFile->GetName() != mfilenames[counter]) { - closeInputFile(); - mcurrentFile = new TFile(mfilenames[counter].c_str()); - } - } else { - mcurrentFile = new TFile(mfilenames[counter].c_str()); + // open file + auto filename = mfilenames[counter]->fileName; + if (mcurrentFile) { + if (mcurrentFile->GetName() != filename) { + closeInputFile(); + mcurrentFile = TFile::Open(filename.c_str()); } } else { - closeInputFile(); + mcurrentFile = TFile::Open(filename.c_str()); + } + if (!mcurrentFile) { + throw std::runtime_error(fmt::format("Couldn't open file \"{}\"!", filename)); + } + mcurrentFile->SetReadaheadSize(50 * 1024 * 1024); + + // get the directory names + if (mfilenames[counter]->numberOfTimeFrames <= 0) { + std::regex TFRegex = std::regex("TF_[0-9]+"); + TList* keyList = mcurrentFile->GetListOfKeys(); + + // extract TF numbers and sort accordingly + for (auto key : *keyList) { + if (std::regex_match(((TObjString*)key)->GetString().Data(), TFRegex)) { + auto folderNumber = std::stoul(std::string(((TObjString*)key)->GetString().Data()).substr(3)); + mfilenames[counter]->listOfTimeFrameNumbers.emplace_back(folderNumber); + } + } + std::sort(mfilenames[counter]->listOfTimeFrameNumbers.begin(), mfilenames[counter]->listOfTimeFrameNumbers.end()); + + for (auto folderNumber : mfilenames[counter]->listOfTimeFrameNumbers) { + auto folderName = "TF_" + std::to_string(folderNumber); + mfilenames[counter]->listOfTimeFrameKeys.emplace_back(folderName); + } + mfilenames[counter]->numberOfTimeFrames = mfilenames[counter]->listOfTimeFrameKeys.size(); + } + + return true; +} + +uint64_t DataInputDescriptor::getTimeFrameNumber(int counter, int numTF) +{ + + // open file + if (!setFile(counter)) { + return 0ul; + } + + // no TF left + if (mfilenames[counter]->numberOfTimeFrames > 0 && numTF >= mfilenames[counter]->numberOfTimeFrames) { + return 0ul; + } + + return (mfilenames[counter]->listOfTimeFrameNumbers)[numTF]; +} + +FileAndFolder DataInputDescriptor::getFileFolder(int counter, int numTF) +{ + FileAndFolder fileAndFolder; + + // open file + if (!setFile(counter)) { + return fileAndFolder; } - return mcurrentFile; + // no TF left + if (mfilenames[counter]->numberOfTimeFrames > 0 && numTF >= mfilenames[counter]->numberOfTimeFrames) { + return fileAndFolder; + } + + fileAndFolder.file = mcurrentFile; + fileAndFolder.folderName = (mfilenames[counter]->listOfTimeFrameKeys)[numTF]; + + return fileAndFolder; } void DataInputDescriptor::closeInputFile() { if (mcurrentFile) { mcurrentFile->Close(); + mcurrentFile = nullptr; delete mcurrentFile; } } @@ -81,9 +186,11 @@ int DataInputDescriptor::fillInputfiles() try { std::ifstream filelist(fileName); while (std::getline(filelist, fileName)) { - if (getFilenamesRegexString().empty() || - std::regex_match(fileName, getFilenamesRegex())) { - addFilename(fileName); + // remove white spaces, empty lines are skipped + fileName.erase(std::remove_if(fileName.begin(), fileName.end(), ::isspace), fileName.end()); + if (!fileName.empty() && (getFilenamesRegexString().empty() || + std::regex_match(fileName, getFilenamesRegex()))) { + addFileNameHolder(makeFileNameHolder(fileName)); } } } catch (...) { @@ -93,10 +200,10 @@ int DataInputDescriptor::fillInputfiles() } else { // 3. getFilenamesRegex() @ mdefaultFilenamesPtr if (mdefaultFilenamesPtr) { - for (auto fileName : *mdefaultFilenamesPtr) { + for (auto fileNameHolder : *mdefaultFilenamesPtr) { if (getFilenamesRegexString().empty() || - std::regex_match(fileName, getFilenamesRegex())) { - addFilename(fileName); + std::regex_match(fileNameHolder->fileName, getFilenamesRegex())) { + addFileNameHolder(fileNameHolder); } } } @@ -105,28 +212,6 @@ int DataInputDescriptor::fillInputfiles() return getNumberInputfiles(); } -std::string DataInputDescriptor::getInputFilename(int counter) -{ - std::string filename(""); - if (counter >= 0 && counter < getNumberInputfiles()) { - filename = mfilenames[counter]; - } - - return filename; -} - -void DataInputDescriptor::printOut() -{ - LOGP(INFO, "DataInputDescriptor"); - LOGP(INFO, " Table name : {}", tablename); - LOGP(INFO, " Tree name : {}", treename); - LOGP(INFO, " Input files file : {}", getInputfilesFilename()); - LOGP(INFO, " File name regex : {}", getFilenamesRegexString()); - LOGP(INFO, " Input files : {}", mfilenames.size()); - for (auto fn : mfilenames) - LOGP(INFO, " {}", fn); -} - DataInputDirector::DataInputDirector() { createDefaultDataInputDescriptor(); @@ -138,7 +223,16 @@ DataInputDirector::DataInputDirector(std::string inputFile) inputFile.erase(0, 1); setInputfilesFile(inputFile); } else { - mdefaultInputFiles.emplace_back(inputFile); + mdefaultInputFiles.emplace_back(makeFileNameHolder(inputFile)); + } + + createDefaultDataInputDescriptor(); +} + +DataInputDirector::DataInputDirector(std::vector<std::string> inputFiles) +{ + for (auto inputFile : inputFiles) { + mdefaultInputFiles.emplace_back(makeFileNameHolder(inputFile)); } createDefaultDataInputDescriptor(); @@ -156,7 +250,7 @@ void DataInputDirector::createDefaultDataInputDescriptor() if (mdefaultDataInputDescriptor) { delete mdefaultDataInputDescriptor; } - mdefaultDataInputDescriptor = new DataInputDescriptor(); + mdefaultDataInputDescriptor = new DataInputDescriptor(mAlienSupport); mdefaultDataInputDescriptor->setInputfilesFile(minputfilesFile); mdefaultDataInputDescriptor->setFilenamesRegex(mFilenameRegex); @@ -164,6 +258,8 @@ void DataInputDirector::createDefaultDataInputDescriptor() mdefaultDataInputDescriptor->tablename = "any"; mdefaultDataInputDescriptor->treename = "any"; mdefaultDataInputDescriptor->fillInputfiles(); + + mAlienSupport &= mdefaultDataInputDescriptor->isAlienSupportOn(); } bool DataInputDirector::readJson(std::string const& fnjson) @@ -207,24 +303,24 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) itemName = "InputDirector"; const Value& didirItem = (*jsonDoc)[itemName]; if (!didirItem.IsObject()) { - LOGP(ERROR, "Check the JSON document! Couldn't find an \"{}\" object!", itemName); - return false; + LOGP(INFO, "No \"{}\" object found in the JSON document!", itemName); + return true; } // now read various items itemName = "debugmode"; if (didirItem.HasMember(itemName)) { if (didirItem[itemName].IsBool()) { - mdebugmode = (didirItem[itemName].GetBool()); + mDebugMode = (didirItem[itemName].GetBool()); } else { LOGP(ERROR, "Check the JSON document! Item \"{}\" must be a boolean!", itemName); return false; } } else { - mdebugmode = false; + mDebugMode = false; } - if (mdebugmode) { + if (mDebugMode) { StringBuffer buffer; buffer.Clear(); PrettyWriter<StringBuffer> writer(buffer); @@ -251,13 +347,13 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) setInputfilesFile(fileName); } else { setInputfilesFile(""); - mdefaultInputFiles.emplace_back(fileName); + mdefaultInputFiles.emplace_back(makeFileNameHolder(fileName)); } } else if (didirItem[itemName].IsArray()) { setInputfilesFile(""); auto fns = didirItem[itemName].GetArray(); for (auto& fn : fns) { - mdefaultInputFiles.emplace_back(fn.GetString()); + mdefaultInputFiles.emplace_back(makeFileNameHolder(fn.GetString())); } } else { LOGP(ERROR, "Check the JSON document! Item \"{}\" must be a string or an array!", itemName); @@ -279,7 +375,7 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) return false; } // create a new dataInputDescriptor - auto didesc = new DataInputDescriptor(); + auto didesc = new DataInputDescriptor(mAlienSupport); didesc->setDefaultInputfiles(&mdefaultInputFiles); itemName = "table"; @@ -334,7 +430,7 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) } else { if (didesc->getFilenamesRegexString().empty() || std::regex_match(fileName, didesc->getFilenamesRegex())) { - didesc->addFilename(fileName); + didesc->addFileNameHolder(makeFileNameHolder(fileName)); } } } else if (didescItem[itemName].IsArray()) { @@ -342,7 +438,7 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) for (auto& fn : fns) { if (didesc->getFilenamesRegexString().empty() || std::regex_match(fn.GetString(), didesc->getFilenamesRegex())) { - didesc->addFilename(fn.GetString()); + didesc->addFileNameHolder(makeFileNameHolder(fn.GetString())); } } } else { @@ -360,6 +456,7 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) didesc->printOut(); LOGP(INFO, "This DataInputDescriptor is ignored because its file list is empty!"); } + mAlienSupport &= didesc->isAlienSupportOn(); } } @@ -373,7 +470,7 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) } // print the DataIputDirector - if (mdebugmode) { + if (mDebugMode) { printOut(); } @@ -397,7 +494,7 @@ DataInputDescriptor* DataInputDirector::getDataInputDescriptor(header::DataHeade return result; } -std::unique_ptr<TTreeReader> DataInputDirector::getTreeReader(header::DataHeader dh, int counter, std::string treename) +std::unique_ptr<TTreeReader> DataInputDirector::getTreeReader(header::DataHeader dh, int counter, int numTF, std::string treename) { std::unique_ptr<TTreeReader> reader = nullptr; auto didesc = getDataInputDescriptor(dh); @@ -405,56 +502,65 @@ std::unique_ptr<TTreeReader> DataInputDirector::getTreeReader(header::DataHeader if (!didesc) { didesc = mdefaultDataInputDescriptor; } - auto file = didesc->getInputFile(counter); - if (file->IsOpen()) { - reader = std::make_unique<TTreeReader>(treename.c_str(), file); + + auto fileAndFolder = didesc->getFileFolder(counter, numTF); + if (fileAndFolder.file) { + treename = fileAndFolder.folderName + "/" + treename; + reader = std::make_unique<TTreeReader>(treename.c_str(), fileAndFolder.file); if (!reader) { - LOGP(ERROR, "Couldn't create TTreeReader for tree \"{}\" in file \"{}\"", treename, file->GetName()); + throw std::runtime_error(fmt::format(R"(Couldn't create TTreeReader for tree "{}" in file "{}")", treename, fileAndFolder.file->GetName())); } - } else { - LOGP(ERROR, "Couldn't open file \"{}\"", file->GetName()); } return reader; } -std::string DataInputDirector::getInputFilename(header::DataHeader dh, int counter) +FileAndFolder DataInputDirector::getFileFolder(header::DataHeader dh, int counter, int numTF) +{ + auto didesc = getDataInputDescriptor(dh); + // if NOT match then use defaultDataInputDescriptor + if (!didesc) { + didesc = mdefaultDataInputDescriptor; + } + + return didesc->getFileFolder(counter, numTF); +} + +uint64_t DataInputDirector::getTimeFrameNumber(header::DataHeader dh, int counter, int numTF) { auto didesc = getDataInputDescriptor(dh); // if NOT match then use defaultDataInputDescriptor if (!didesc) { didesc = mdefaultDataInputDescriptor; } - auto filename = didesc->getInputFilename(counter); - return filename; + return didesc->getTimeFrameNumber(counter, numTF); } -TTree* DataInputDirector::getDataTree(header::DataHeader dh, int counter) +TTree* DataInputDirector::getDataTree(header::DataHeader dh, int counter, int numTF) { - const char* treename; + std::string treename; TTree* tree = nullptr; auto didesc = getDataInputDescriptor(dh); if (didesc) { // if match then use filename and treename from DataInputDescriptor - treename = didesc->treename.c_str(); + treename = didesc->treename; } else { // if NOT match then use // . filename from defaultDataInputDescriptor // . treename from DataHeader didesc = mdefaultDataInputDescriptor; - treename = dh.dataDescription.str; + treename = aod::datamodel::getTreeName(dh); } - auto file = didesc->getInputFile(counter); - if (file->IsOpen()) { - tree = (TTree*)file->Get(treename); + auto fileAndFolder = didesc->getFileFolder(counter, numTF); + if (fileAndFolder.file) { + treename = fileAndFolder.folderName + "/" + treename; + tree = (TTree*)fileAndFolder.file->Get(treename.c_str()); if (!tree) { - LOGP(ERROR, "Couldn't get TTree \"{}\" from \"{}\"", treename, file->GetName()); + throw std::runtime_error(fmt::format(R"(Couldn't get TTree "{}" from "{}")", treename, fileAndFolder.file->GetName())); } - } else { - LOGP(ERROR, "Couldn't open file \"{}\"", file->GetName()); } return tree; @@ -495,13 +601,15 @@ void DataInputDirector::printOut() LOGP(INFO, " Default input files file : {}", minputfilesFile); LOGP(INFO, " Default file name regex : {}", mFilenameRegex); LOGP(INFO, " Default file names : {}", mdefaultInputFiles.size()); - for (auto const& fn : mdefaultInputFiles) - LOGP(INFO, " {}", fn); + for (auto const& fn : mdefaultInputFiles) { + LOGP(INFO, " {} {}", fn->fileName, fn->numberOfTimeFrames); + } LOGP(INFO, " Default DataInputDescriptor:"); mdefaultDataInputDescriptor->printOut(); LOGP(INFO, " DataInputDescriptors : {}", getNumberInputDescriptors()); - for (auto const& didesc : mdataInputDescriptors) + for (auto const& didesc : mdataInputDescriptors) { didesc->printOut(); + } } } // namespace framework diff --git a/Framework/Core/src/DataOutputDirector.cxx b/Framework/Core/src/DataOutputDirector.cxx index 576e897815683..4c71b4ef1e554 100644 --- a/Framework/Core/src/DataOutputDirector.cxx +++ b/Framework/Core/src/DataOutputDirector.cxx @@ -56,8 +56,10 @@ DataOutputDescriptor::DataOutputDescriptor(std::string inString) } // get the tree name - // defaul tree name is the table name + // default tree name is the O2 + table name (lower case) treename = tablename; + std::transform(treename.begin(), treename.end(), treename.begin(), [](unsigned char c) { return std::tolower(c); }); + treename = std::string("O2") + treename; ++iter1; if (iter1 == end) { return; @@ -108,12 +110,13 @@ void DataOutputDescriptor::printOut() LOGP(INFO, " File name base : {}", getFilenameBase()); LOGP(INFO, " Tree name : {}", treename); if (colnames.empty()) { - LOGP(INFO, " Columns : \"all\""); + LOGP(INFO, " Columns : \"all\""); } else { - LOGP(INFO, " Columns : {}", colnames.size()); + LOGP(INFO, " Columns : {}", colnames.size()); } - for (auto cn : colnames) + for (auto cn : colnames) { LOGP(INFO, " {}", cn); + } } std::string DataOutputDescriptor::remove_ws(const std::string& s) @@ -139,7 +142,6 @@ void DataOutputDirector::reset() mtreeFilenames.clear(); closeDataFiles(); mfilePtrs.clear(); - mfileCounts.clear(); mfilenameBase = std::string(""); }; @@ -181,10 +183,9 @@ void DataOutputDirector::readString(std::string const& keepString) auto last = std::unique(mfilenameBases.begin(), mfilenameBases.end()); mfilenameBases.erase(last, mfilenameBases.end()); - // prepare list mfilePtrs of TFile and mfileCounts + // prepare list mfilePtrs of TFile for (auto fn : mfilenameBases) { mfilePtrs.emplace_back(new TFile()); - mfileCounts.emplace_back(-1); } } @@ -265,7 +266,7 @@ std::tuple<std::string, std::string, int> DataOutputDirector::readJsonDocument(D itemName = "OutputDirector"; const Value& dodirItem = (*jsonDocument)[itemName]; if (!dodirItem.IsObject()) { - LOGP(ERROR, "Check the JSON document! Couldn't find an \"{}\" object!", itemName); + LOGP(INFO, "No \"{}\" object found in the JSON document!", itemName); return memptyanswer; } @@ -305,6 +306,7 @@ std::tuple<std::string, std::string, int> DataOutputDirector::readJsonDocument(D if (dodirItem.HasMember(itemName)) { if (dodirItem[itemName].IsString()) { fmode = dodirItem[itemName].GetString(); + setFileMode(fmode); } else { LOGP(ERROR, "Check the JSON document! Item \"{}\" must be a string!", itemName); return memptyanswer; @@ -315,6 +317,7 @@ std::tuple<std::string, std::string, int> DataOutputDirector::readJsonDocument(D if (dodirItem.HasMember(itemName)) { if (dodirItem[itemName].IsNumber()) { ntfm = dodirItem[itemName].GetInt(); + setNumberTimeFramesToMerge(ntfm); } else { LOGP(ERROR, "Check the JSON document! Item \"{}\" must be a number!", itemName); return memptyanswer; @@ -425,42 +428,40 @@ std::vector<DataOutputDescriptor*> DataOutputDirector::getDataOutputDescriptors( return result; } -TFile* DataOutputDirector::getDataOutputFile(DataOutputDescriptor* dodesc, - int ntf, int ntfmerge, - std::string filemode) +FileAndFolder DataOutputDirector::getFileFolder(DataOutputDescriptor* dodesc, uint64_t folderNumber) { // initialisation - TFile* filePtr = nullptr; + FileAndFolder fileAndFolder; // search dodesc->filename in mfilenameBases and return corresponding filePtr auto it = std::find(mfilenameBases.begin(), mfilenameBases.end(), dodesc->getFilenameBase()); if (it != mfilenameBases.end()) { int ind = std::distance(mfilenameBases.begin(), it); + if (!mfilePtrs[ind]->IsOpen()) { + auto fn = mfilenameBases[ind] + ".root"; + mfilePtrs[ind] = new TFile(fn.c_str(), mfileMode.c_str()); + } + fileAndFolder.file = mfilePtrs[ind]; - // check if new version of file needs to be opened - int fcnt = (int)(ntf / ntfmerge); - if ((ntf % ntfmerge) == 0 && fcnt > mfileCounts[ind]) { - if (mfilePtrs[ind]) { - mfilePtrs[ind]->Close(); - } - - mfileCounts[ind] = fcnt; - auto fn = mfilenameBases[ind] + "_" + std::to_string(mfileCounts[ind]) + ".root"; - mfilePtrs[ind] = new TFile(fn.c_str(), filemode.c_str()); + // check if folder TF_* exists + fileAndFolder.folderName = "TF_" + std::to_string(folderNumber) + "/"; + auto key = fileAndFolder.file->GetKey(fileAndFolder.folderName.c_str()); + if (!key) { + fileAndFolder.file->mkdir(fileAndFolder.folderName.c_str()); } - filePtr = mfilePtrs[ind]; - filePtr->cd(); + fileAndFolder.file->cd(fileAndFolder.folderName.c_str()); } - return filePtr; + return fileAndFolder; } void DataOutputDirector::closeDataFiles() { - for (auto filePtr : mfilePtrs) + for (auto filePtr : mfilePtrs) { if (filePtr) { filePtr->Close(); } + } } void DataOutputDirector::printOut() @@ -470,12 +471,14 @@ void DataOutputDirector::printOut() LOGP(INFO, " Number of files : {}", mfilenameBases.size()); LOGP(INFO, " DataOutputDescriptors: {}", mDataOutputDescriptors.size()); - for (auto const& ds : mDataOutputDescriptors) + for (auto const& ds : mDataOutputDescriptors) { ds->printOut(); + } LOGP(INFO, " File name bases :"); - for (auto const& fb : mfilenameBases) + for (auto const& fb : mfilenameBases) { LOGP(INFO, fb); + } } void DataOutputDirector::setFilenameBase(std::string dfn) @@ -487,7 +490,6 @@ void DataOutputDirector::setFilenameBase(std::string dfn) mtreeFilenames.clear(); closeDataFiles(); mfilePtrs.clear(); - mfileCounts.clear(); // loop over DataOutputDescritors for (auto dodesc : mDataOutputDescriptors) { @@ -500,7 +502,7 @@ void DataOutputDirector::setFilenameBase(std::string dfn) auto it = std::unique(mtreeFilenames.begin(), mtreeFilenames.end()); if (it != mtreeFilenames.end()) { printOut(); - LOG(FATAL) << "Dublicate tree names in a file!"; + LOG(FATAL) << "Duplicate tree names in a file!"; } // make unique/sorted list of filenameBases @@ -508,10 +510,9 @@ void DataOutputDirector::setFilenameBase(std::string dfn) auto last = std::unique(mfilenameBases.begin(), mfilenameBases.end()); mfilenameBases.erase(last, mfilenameBases.end()); - // prepare list mfilePtrs of TFile and mfileCounts + // prepare list mfilePtrs of TFile for (auto fn : mfilenameBases) { mfilePtrs.emplace_back(new TFile()); - mfileCounts.emplace_back(-1); } } diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 16fc85850e4f2..04cebf5d94b4a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -7,6 +7,10 @@ // In applying this license CERN does 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 DPL_ENABLE_TRACING +#define TRACY_ENABLE +#include <tracy/TracyClient.cpp> +#endif #include "Framework/DataProcessingDevice.h" #include "Framework/ChannelMatching.h" #include "Framework/ControlService.h" @@ -16,6 +20,7 @@ #include "Framework/DeviceState.h" #include "Framework/DispatchPolicy.h" #include "Framework/DispatchControl.h" +#include "Framework/DanglingContext.h" #include "Framework/EndOfStreamContext.h" #include "Framework/FairOptionsRetriever.h" #include "ConfigurationOptionsRetriever.h" @@ -26,18 +31,21 @@ #include "Framework/Signpost.h" #include "Framework/SourceInfoHeader.h" #include "Framework/Logger.h" +#include "Framework/Monitoring.h" +#include "PropertyTreeHelpers.h" #include "DataProcessingStatus.h" #include "DataProcessingHelpers.h" #include "DataRelayerHelpers.h" #include "ScopedExit.h" +#include <Framework/Tracing.h> + #include <fairmq/FairMQParts.h> #include <fairmq/FairMQSocket.h> #include <options/FairMQProgOptions.h> #include <Configuration/ConfigurationInterface.h> #include <Configuration/ConfigurationFactory.h> -#include <Monitoring/Monitoring.h> #include <TMessage.h> #include <TClonesArray.h> @@ -46,6 +54,9 @@ #include <memory> #include <unordered_map> #include <uv.h> +#include <execinfo.h> +#include <sstream> +#include <boost/property_tree/json_parser.hpp> using namespace o2::framework; using Key = o2::monitoring::tags::Key; @@ -58,20 +69,19 @@ using DataHeader = o2::header::DataHeader; constexpr unsigned int MONITORING_QUEUE_SIZE = 100; constexpr unsigned int MIN_RATE_LOGGING = 60; -// This should result in a minimum of 10Hz which should guarantee we do not use -// much time when idle. We do not sleep at all when we are at less then 100us, -// because that's what the default rate enforces in any case. -constexpr int MAX_BACKOFF = 6; -constexpr int MIN_BACKOFF_DELAY = 100; -constexpr int BACKOFF_DELAY_STEP = 100; - namespace o2::framework { +template <> +struct ServiceKindExtractor<ConfigurationInterface> { + constexpr static ServiceKind kind = ServiceKind::Global; +}; + /// We schedule a timer to reduce CPU usage. /// Watching stdin for commands probably a better approach. void idle_timer(uv_timer_t* handle) { + ZoneScopedN("Idle timer"); } DataProcessingDevice::DataProcessingDevice(DeviceSpec const& spec, ServiceRegistry& registry, DeviceState& state) @@ -82,46 +92,29 @@ DataProcessingDevice::DataProcessingDevice(DeviceSpec const& spec, ServiceRegist mStatelessProcess{spec.algorithm.onProcess}, mError{spec.algorithm.onError}, mConfigRegistry{nullptr}, - mFairMQContext{FairMQDeviceProxy{this}}, - mStringContext{FairMQDeviceProxy{this}}, - mDataFrameContext{FairMQDeviceProxy{this}}, - mRawBufferContext{FairMQDeviceProxy{this}}, - mContextRegistry{&mFairMQContext, &mStringContext, &mDataFrameContext, &mRawBufferContext}, - mAllocator{&mTimingInfo, &mContextRegistry, spec.outputs}, + mAllocator{&mTimingInfo, ®istry, spec.outputs}, mServiceRegistry{registry}, - mErrorCount{0}, - mProcessingCount{0} + mErrorCount{0} { - StateMonitoring<DataProcessingStatus>::start(); - auto dispatcher = [this](FairMQParts&& parts, std::string const& channel, unsigned int index) { - DataProcessor::doSend(*this, std::move(parts), channel.c_str(), index); - }; - auto matcher = [policy = spec.dispatchPolicy](o2::header::DataHeader const& header) { - if (policy.triggerMatcher == nullptr) { - return true; - } - return policy.triggerMatcher(Output{header}); - }; - - if (spec.dispatchPolicy.action == DispatchPolicy::DispatchOp::WhenReady) { - mFairMQContext.init(DispatchControl{dispatcher, matcher}); - } - + /// FIXME: move erro handling to a service? if (mError != nullptr) { - mErrorHandling = [& errorCallback = mError, - &serviceRegistry = mServiceRegistry](std::exception& e, InputRecord& record) { - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_ERROR_CALLBACK); - LOG(ERROR) << "Exception caught: " << e.what() << std::endl; + mErrorHandling = [&errorCallback = mError, + &serviceRegistry = mServiceRegistry](RuntimeErrorRef e, InputRecord& record) { + ZoneScopedN("Error handling"); + auto& err = error_from_ref(e); + LOGP(ERROR, "Exception caught: {} ", err.what); + backtrace_symbols_fd(err.backtrace, err.maxBacktrace, STDERR_FILENO); serviceRegistry.get<Monitoring>().send({1, "error"}); ErrorContext errorContext{record, serviceRegistry, e}; errorCallback(errorContext); - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); }; } else { - mErrorHandling = [& errorPolicy = mErrorPolicy, - &serviceRegistry = mServiceRegistry](std::exception& e, InputRecord& record) { - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_ERROR_CALLBACK); - LOG(ERROR) << "Exception caught: " << e.what() << std::endl; + mErrorHandling = [&errorPolicy = mErrorPolicy, + &serviceRegistry = mServiceRegistry](RuntimeErrorRef e, InputRecord& record) { + ZoneScopedN("Error handling"); + auto& err = error_from_ref(e); + LOGP(ERROR, "Exception caught: {} ", err.what); + backtrace_symbols_fd(err.backtrace, err.maxBacktrace, STDERR_FILENO); serviceRegistry.get<Monitoring>().send({1, "error"}); switch (errorPolicy) { case TerminationPolicy::QUIT: @@ -129,26 +122,57 @@ DataProcessingDevice::DataProcessingDevice(DeviceSpec const& spec, ServiceRegist default: break; } - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); }; } } +// Callback to execute the processing. Notice how the data is +// is a vector of DataProcessorContext so that we can index the correct +// one with the thread id. For the moment we simply use the first one. +void run_callback(uv_work_t* handle) +{ + ZoneScopedN("run_callback"); + std::vector<DataProcessorContext>* contexes = (std::vector<DataProcessorContext>*)handle->data; + DataProcessorContext& context = contexes->at(0); + DataProcessingDevice::doPrepare(context); + DataProcessingDevice::doRun(context); + // FrameMark; +} + +// Once the processing in a thread is done, this is executed on the main thread. +void run_completion(uv_work_t* handle, int status) +{ + ZoneScopedN("run_completion"); +} + +// Context for polling +struct PollerContext { + char const* name; + uv_loop_t* loop; + DataProcessingDevice* device; + int fd; +}; + void on_socket_polled(uv_poll_t* poller, int status, int events) { + PollerContext* context = (PollerContext*)poller->data; switch (events) { - case UV_READABLE: - LOG(debug) << "socket polled UV_READABLE: " << (char*)poller->data; - break; - case UV_WRITABLE: + case UV_READABLE: { + ZoneScopedN("socket readable event"); + LOG(debug) << "socket polled UV_READABLE: " << context->name; + } break; + case UV_WRITABLE: { + ZoneScopedN("socket writeable"); LOG(debug) << "socket polled UV_WRITEABLE"; - break; - case UV_DISCONNECT: + } break; + case UV_DISCONNECT: { + ZoneScopedN("socket disconnect"); LOG(debug) << "socket polled UV_DISCONNECT"; - break; - case UV_PRIORITIZED: + } break; + case UV_PRIORITIZED: { + ZoneScopedN("socket prioritized"); LOG(debug) << "socket polled UV_PRIORITIZED"; - break; + } break; } // We do nothing, all the logic for now stays in DataProcessingDevice::doRun() } @@ -162,6 +186,8 @@ void on_socket_polled(uv_poll_t* poller, int status, int events) /// * Invoke the actual init callback, which returns the processing callback. void DataProcessingDevice::Init() { + TracyAppInfo(mSpec.name.data(), mSpec.name.size()); + ZoneScopedN("DataProcessingDevice::Init"); mRelayer = &mServiceRegistry.get<DataRelayer>(); // For some reason passing rateLogging does not work anymore. // This makes sure we never have more than one notification per minute. @@ -195,13 +221,25 @@ void DataProcessingDevice::Init() } else { retrievers.emplace_back(std::make_unique<FairOptionsRetriever>(GetConfig())); } - auto configStore = std::move(std::make_unique<ConfigParamStore>(mSpec.options, std::move(retrievers))); + auto configStore = std::make_unique<ConfigParamStore>(mSpec.options, std::move(retrievers)); configStore->preload(); configStore->activate(); + using boost::property_tree::ptree; + /// Dump the configuration so that we can get it from the driver. for (auto& entry : configStore->store()) { - LOG(INFO) << "[CONFIG] " << entry.first << "=" << configStore->store().get<std::string>(entry.first) << " 1 " << configStore->provenance(entry.first.c_str()); + std::stringstream ss; + std::string str; + if (entry.second.size() != 0) { + boost::property_tree::json_parser::write_json(ss, entry.second, false); + str = ss.str(); + str.pop_back(); //remove EoL + } else { + str = entry.second.get_value<std::string>(); + } + LOG(INFO) << "[CONFIG] " << entry.first << "=" << str << " 1 " << configStore->provenance(entry.first.c_str()); } + mConfigRegistry = std::make_unique<ConfigParamRegistry>(std::move(configStore)); mExpirationHandlers.clear(); @@ -239,9 +277,9 @@ void DataProcessingDevice::Init() /// expect them to create any data. for (size_t ci = 0; ci < mSpec.inputChannels.size(); ++ci) { auto& name = mSpec.inputChannels[ci].name; - if (name.find("from_internal-dpl-clock") == 0) { + if (name.find(mSpec.channelPrefix + "from_internal-dpl-clock") == 0) { mState.inputChannelInfos[ci].state = InputChannelState::Pull; - } else if (name.find("from_internal-dpl-ccdb-backend") == 0) { + } else if (name.find(mSpec.channelPrefix + "from_internal-dpl-ccdb-backend") == 0) { mState.inputChannelInfos[ci].state = InputChannelState::Pull; } } @@ -249,13 +287,15 @@ void DataProcessingDevice::Init() void on_signal_callback(uv_signal_t* handle, int signum) { - LOG(debug) << "Signal " << signum << "received." << std::endl; + ZoneScopedN("Signal callaback"); + LOG(debug) << "Signal " << signum << " received."; } void DataProcessingDevice::InitTask() { for (auto& channel : fChannels) { - channel.second.at(0).Transport()->SubscribeToRegionEvents([& pendingRegionInfos = mPendingRegionInfos](FairMQRegionInfo info) { + channel.second.at(0).Transport()->SubscribeToRegionEvents([& pendingRegionInfos = mPendingRegionInfos, ®ionInfoMutex = mRegionInfoMutex](FairMQRegionInfo info) { + std::lock_guard<std::mutex> lock(regionInfoMutex); LOG(debug) << ">>> Region info event" << info.event; LOG(debug) << "id: " << info.id; LOG(debug) << "ptr: " << info.ptr; @@ -272,16 +312,21 @@ void DataProcessingDevice::InitTask() uv_signal_t* sigusr1Handle = (uv_signal_t*)malloc(sizeof(uv_signal_t)); uv_signal_init(mState.loop, sigusr1Handle); uv_signal_start(sigusr1Handle, on_signal_callback, SIGUSR1); + // Handle SIGWINCH by simply forcing the event loop to continue. + // This will hopefully hide it from FairMQ on linux. + uv_signal_t* sigwinchHandle = (uv_signal_t*)malloc(sizeof(uv_signal_t)); + uv_signal_init(mState.loop, sigwinchHandle); + uv_signal_start(sigwinchHandle, on_signal_callback, SIGWINCH); // We add a timer only in case a channel poller is not there. if ((mStatefulProcess != nullptr) || (mStatelessProcess != nullptr)) { for (auto& x : fChannels) { - if ((x.first.rfind("from_internal-dpl", 0) == 0) && (x.first.rfind("from_internal-dpl-aod", 0) != 0)) { + if ((x.first.rfind("from_internal-dpl", 0) == 0) && (x.first.rfind("from_internal-dpl-aod", 0) != 0) && (x.first.rfind("from_internal-dpl-injected", 0)) != 0) { LOG(debug) << x.first << " is an internal channel. Skipping as no input will come from there." << std::endl; continue; } // We only watch receiving sockets. - if (x.first.rfind("from_" + mSpec.name + "_to", 0) == 0) { + if (x.first.rfind("from_" + mSpec.name + "_", 0) == 0) { LOG(debug) << x.first << " is to send data. Not polling." << std::endl; continue; } @@ -297,21 +342,26 @@ void DataProcessingDevice::InitTask() } LOG(debug) << "Polling socket for " << x.second[0].GetName(); // FIXME: leak - poller->data = strdup(x.first.c_str()); + PollerContext* pCtx = (PollerContext*)malloc(sizeof(PollerContext)); + pCtx->name = strdup(x.first.c_str()); + pCtx->loop = mState.loop; + pCtx->device = this; + pCtx->fd = zmq_fd; + poller->data = pCtx; uv_poll_init(mState.loop, poller, zmq_fd); uv_poll_start(poller, UV_READABLE | UV_DISCONNECT, &on_socket_polled); mState.activeInputPollers.push_back(poller); } // In case we do not have any input channel and we do not have - // any timers, we still wake up whenever we can send data to downstream + // any timers or signal watchers we still wake up whenever we can send data to downstream // devices to allow for enumerations. - if (mState.activeInputPollers.empty() && mState.activeTimers.empty()) { + if (mState.activeInputPollers.empty() && mState.activeTimers.empty() && mState.activeSignals.empty()) { for (auto& x : fChannels) { - if (x.first.rfind("from_internal-dpl", 0) == 0) { + if (x.first.rfind(mSpec.channelPrefix + "from_internal-dpl", 0) == 0) { LOG(debug) << x.first << " is an internal channel. Not polling." << std::endl; continue; } - assert(x.first.rfind("from_" + mSpec.name + "_to", 0) == 0); + assert(x.first.rfind(mSpec.channelPrefix + "from_" + mSpec.name + "_", 0) == 0); // We assume there is always a ZeroMQ socket behind. int zmq_fd = 0; size_t zmq_fd_len = sizeof(zmq_fd); @@ -324,7 +374,12 @@ void DataProcessingDevice::InitTask() } LOG(debug) << "Polling socket for " << x.second[0].GetName(); // FIXME: leak - poller->data = strdup(x.first.c_str()); + PollerContext* pCtx = (PollerContext*)malloc(sizeof(PollerContext)); + pCtx->name = strdup(x.first.c_str()); + pCtx->loop = mState.loop; + pCtx->device = this; + pCtx->fd = zmq_fd; + poller->data = pCtx; uv_poll_init(mState.loop, poller, zmq_fd); uv_poll_start(poller, UV_WRITABLE, &on_socket_polled); mState.activeOutputPollers.push_back(poller); @@ -339,10 +394,39 @@ void DataProcessingDevice::InitTask() uv_timer_start(timer, idle_timer, 2000, 2000); mState.activeTimers.push_back(timer); } + // Whenever we InitTask, we consider as if the previous iteration // was successful, so that even if there is no timer or receiving // channel, we can still start an enumeration. mWasActive = true; + + // We should be ready to run here. Therefore we copy all the + // required parts in the DataProcessorContext. Eventually we should + // do so on a per thread basis, with fine grained locks. + mDataProcessorContexes.resize(1); + this->fillContext(mDataProcessorContexes.at(0)); +} + +void DataProcessingDevice::fillContext(DataProcessorContext& context) +{ + context.wasActive = &mWasActive; + + context.device = this; + context.spec = &mSpec; + context.state = &mState; + + context.relayer = mRelayer; + context.registry = &mServiceRegistry; + context.completed = &mCompleted; + context.expirationHandlers = &mExpirationHandlers; + context.timingInfo = &mTimingInfo; + context.allocator = &mAllocator; + context.statefulProcess = &mStatefulProcess; + context.statelessProcess = &mStatelessProcess; + context.error = &mError; + /// Callback for the error handling + context.errorHandling = &mErrorHandling; + context.errorCount = &mErrorCount; } void DataProcessingDevice::PreRun() @@ -361,185 +445,169 @@ bool DataProcessingDevice::ConditionalRun() // so that devices which do not have a timer can still start an // enumeration. if (mState.loop) { - uv_run(mState.loop, mWasActive ? UV_RUN_NOWAIT : UV_RUN_ONCE); + ZoneScopedN("uv idle"); + TracyPlot("past activity", (int64_t)mWasActive); + auto shouldNotWait = (mWasActive && + (mDataProcessorContexes.at(0).state->streaming != StreamingState::Idle) && (mState.activeSignals.empty())) || + (mDataProcessorContexes.at(0).state->streaming == StreamingState::EndOfStreaming); + uv_run(mState.loop, shouldNotWait ? UV_RUN_NOWAIT : UV_RUN_ONCE); } - return DataProcessingDevice::doRun(); + + // Notify on the main thread the new region callbacks, making sure + // no callback is issued if there is something still processing. + { + std::lock_guard<std::mutex> lock(mRegionInfoMutex); + if (mPendingRegionInfos.empty() == false) { + std::vector<FairMQRegionInfo> toBeNotified; + toBeNotified.swap(mPendingRegionInfos); // avoid any MT issue. + for (auto const& info : toBeNotified) { + mServiceRegistry.get<CallbackService>()(CallbackService::Id::RegionInfoCallback, info); + } + } + } + // Synchronous execution of the callbacks. This will be moved in the + // moved in the on_socket_polled once we have threading in place. + uv_work_t handle; + handle.data = &mDataProcessorContexes; + run_callback(&handle); + run_completion(&handle, 0); + FrameMark; + return true; } /// We drive the state loop ourself so that we will be able to support /// non-data triggers like those which are time based. -bool DataProcessingDevice::doRun() +void DataProcessingDevice::doPrepare(DataProcessorContext& context) { - /// This will send metrics for the relayer at regular intervals of - /// 5 seconds, in order to avoid overloading the system. - auto sendRelayerMetrics = [relayerStats = mRelayer->getStats(), - &stats = mStats, - &lastSent = mLastSlowMetricSentTimestamp, - ¤tTime = mBeginIterationTimestamp, - ®istry = mServiceRegistry]() - -> void { - if (currentTime - lastSent < 5000) { - return; - } - auto& monitoring = registry.get<Monitoring>(); - - O2_SIGNPOST_START(MonitoringStatus::ID, MonitoringStatus::SEND, 0, 0, O2_SIGNPOST_BLUE); - - monitoring.send(Metric{(int)relayerStats.malformedInputs, "malformed_inputs"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(int)relayerStats.droppedComputations, "dropped_computations"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(int)relayerStats.droppedIncomingMessages, "dropped_incoming_messages"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(int)relayerStats.relayedMessages, "relayed_messages"}.addTag(Key::Subsystem, Value::DPL)); - - monitoring.send(Metric{(int)stats.pendingInputs, "inputs/relayed/pending"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(int)stats.incomplete, "inputs/relayed/incomplete"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(int)stats.inputParts, "inputs/relayed/total"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{stats.lastElapsedTimeMs, "elapsed_time_ms"}.addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{stats.lastTotalProcessedSize, "processed_input_size_byte"} - .addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(stats.lastTotalProcessedSize / (stats.lastElapsedTimeMs ? stats.lastElapsedTimeMs : 1) / 1000), - "processing_rate_mb_s"} - .addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{stats.lastLatency.minLatency, "min_input_latency_ms"} - .addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{stats.lastLatency.maxLatency, "max_input_latency_ms"} - .addTag(Key::Subsystem, Value::DPL)); - monitoring.send(Metric{(stats.lastTotalProcessedSize / (stats.lastLatency.maxLatency ? stats.lastLatency.maxLatency : 1) / 1000), "input_rate_mb_s"} - .addTag(Key::Subsystem, Value::DPL)); - - lastSent = currentTime; - O2_SIGNPOST_END(MonitoringStatus::ID, MonitoringStatus::SEND, 0, 0, O2_SIGNPOST_BLUE); - }; + ZoneScopedN("DataProcessingDevice::doPrepare"); + context.registry->get<DataProcessingStats>().beginIterationTimestamp = uv_hrtime() / 1000000; - /// This will flush metrics only once every second. - auto flushMetrics = [& stats = mStats, - &relayer = mRelayer, - &lastFlushed = mLastMetricFlushedTimestamp, - ¤tTime = mBeginIterationTimestamp, - ®istry = mServiceRegistry]() - -> void { - if (currentTime - lastFlushed < 1000) { - return; - } - auto& monitoring = registry.get<Monitoring>(); - - O2_SIGNPOST_START(MonitoringStatus::ID, MonitoringStatus::FLUSH, 0, 0, O2_SIGNPOST_RED); - // Send all the relevant metrics for the relayer to update the GUI - // FIXME: do a delta with the previous version if too many metrics are still - // sent... - for (size_t si = 0; si < stats.relayerState.size(); ++si) { - auto state = stats.relayerState[si]; - monitoring.send({state, "data_relayer/" + std::to_string(si)}); - } - relayer->sendContextState(); - monitoring.flushBuffer(); - lastFlushed = currentTime; - O2_SIGNPOST_END(MonitoringStatus::ID, MonitoringStatus::FLUSH, 0, 0, O2_SIGNPOST_RED); - }; - - auto switchState = [& registry = mServiceRegistry, - &state = mState.streaming](StreamingState newState) { - LOG(debug) << "New state " << (int)newState << " old state " << (int)state; - state = newState; - registry.get<ControlService>().notifyStreamingState(state); - }; - - mBeginIterationTimestamp = uv_hrtime() / 1000000; - - if (mPendingRegionInfos.empty() == false) { - std::vector<FairMQRegionInfo> toBeNotified; - toBeNotified.swap(mPendingRegionInfos); // avoid any MT issue. - for (auto const& info : toBeNotified) { - mServiceRegistry.get<CallbackService>()(CallbackService::Id::RegionInfoCallback, info); - } + *context.wasActive = false; + { + ZoneScopedN("CallbackService::Id::ClockTick"); + context.registry->get<CallbackService>()(CallbackService::Id::ClockTick); } - mWasActive = false; - mServiceRegistry.get<CallbackService>()(CallbackService::Id::ClockTick); // Whether or not we had something to do. // Notice that fake input channels (InputChannelState::Pull) cannot possibly // expect to receive an EndOfStream signal. Thus we do not wait for these // to be completed. In the case of data source devices, as they do not have // real data input channels, they have to signal EndOfStream themselves. - bool allDone = std::any_of(mState.inputChannelInfos.begin(), mState.inputChannelInfos.end(), [](const auto& info) { + context.allDone = std::any_of(context.state->inputChannelInfos.begin(), context.state->inputChannelInfos.end(), [](const auto& info) { return info.state != InputChannelState::Pull; }); // Whether or not all the channels are completed - for (size_t ci = 0; ci < mSpec.inputChannels.size(); ++ci) { - auto& channel = mSpec.inputChannels[ci]; - auto& info = mState.inputChannelInfos[ci]; + for (size_t ci = 0; ci < context.spec->inputChannels.size(); ++ci) { + auto& channel = context.spec->inputChannels[ci]; + auto& info = context.state->inputChannelInfos[ci]; if (info.state != InputChannelState::Completed && info.state != InputChannelState::Pull) { - allDone = false; + context.allDone = false; } if (info.state != InputChannelState::Running) { continue; } - FairMQParts parts; - auto result = this->Receive(parts, channel.name, 0, 0); - if (result > 0) { - // Receiving data counts as activity now, so that - // We can make sure we process all the pending - // messages without hanging on the uv_run. - mWasActive = true; - this->handleData(parts, info); - mCompleted.clear(); - mCompleted.reserve(16); - mWasActive |= this->tryDispatchComputation(mCompleted); + int64_t result = -2; + auto& fairMQChannel = context.device->GetChannel(channel.name, 0); + auto& socket = fairMQChannel.GetSocket(); + uint32_t events; + socket.Events(&events); + if ((events & 1) == 0) { + continue; } + // Notice that there seems to be a difference between the documentation + // of zeromq and the observed behavior. The fact that ZMQ_POLLIN + // is raised does not mean that a message is immediately available to + // read, just that it will be available soon, so the receive can + // still return -2. To avoid this we keep receiving on the socket until + // we get a message, consume all the consecutive messages, and then go back + // to the usual loop. + do { + if (events & 1) { + bool oneMessage = false; + while (true) { + FairMQParts parts; + result = fairMQChannel.Receive(parts, 0); + if (result >= 0) { + // Receiving data counts as activity now, so that + // We can make sure we process all the pending + // messages without hanging on the uv_run. + *context.wasActive = true; + DataProcessingDevice::handleData(context, parts, info); + oneMessage = true; + } else { + if (oneMessage) { + break; + } + } + } + } else { + break; + } + socket.Events(&events); + } while (events & 1); } - sendRelayerMetrics(); - flushMetrics(); - if (mWasActive == false) { - mServiceRegistry.get<CallbackService>()(CallbackService::Id::Idle); +} + +void DataProcessingDevice::doRun(DataProcessorContext& context) +{ + auto switchState = [& registry = context.registry, + &state = context.state](StreamingState newState) { + LOG(debug) << "New state " << (int)newState << " old state " << (int)state->streaming; + state->streaming = newState; + registry->get<ControlService>().notifyStreamingState(state->streaming); + }; + + context.completed->clear(); + context.completed->reserve(16); + *context.wasActive |= DataProcessingDevice::tryDispatchComputation(context, *context.completed); + DanglingContext danglingContext{*context.registry}; + + context.registry->preDanglingCallbacks(danglingContext); + if (*context.wasActive == false) { + context.registry->get<CallbackService>()(CallbackService::Id::Idle); } - auto activity = mRelayer->processDanglingInputs(mExpirationHandlers, mServiceRegistry); - mWasActive |= activity.expiredSlots > 0; + auto activity = context.relayer->processDanglingInputs(*context.expirationHandlers, *context.registry); + *context.wasActive |= activity.expiredSlots > 0; - mCompleted.clear(); - mWasActive |= this->tryDispatchComputation(mCompleted); + context.completed->clear(); + *context.wasActive |= DataProcessingDevice::tryDispatchComputation(context, *context.completed); - sendRelayerMetrics(); - flushMetrics(); + context.registry->postDanglingCallbacks(danglingContext); // If we got notified that all the sources are done, we call the EndOfStream // callback and return false. Notice that what happens next is actually // dependent on the callback, not something which is controlled by the // framework itself. - if (allDone == true && mState.streaming == StreamingState::Streaming) { + if (context.allDone == true && context.state->streaming == StreamingState::Streaming) { switchState(StreamingState::EndOfStreaming); - mWasActive = true; + *context.wasActive = true; } - if (mState.streaming == StreamingState::EndOfStreaming) { + if (context.state->streaming == StreamingState::EndOfStreaming) { // We keep processing data until we are Idle. // FIXME: not sure this is the correct way to drain the queues, but // I guess we will see. - while (this->tryDispatchComputation(mCompleted)) { - mRelayer->processDanglingInputs(mExpirationHandlers, mServiceRegistry); + while (DataProcessingDevice::tryDispatchComputation(context, *context.completed)) { + context.relayer->processDanglingInputs(*context.expirationHandlers, *context.registry); } - sendRelayerMetrics(); - flushMetrics(); - mContextRegistry.get<MessageContext>()->clear(); - mContextRegistry.get<StringContext>()->clear(); - mContextRegistry.get<ArrowContext>()->clear(); - mContextRegistry.get<RawBufferContext>()->clear(); - EndOfStreamContext eosContext{mServiceRegistry, mAllocator}; - mServiceRegistry.get<CallbackService>()(CallbackService::Id::EndOfStream, eosContext); - DataProcessor::doSend(*this, *mContextRegistry.get<MessageContext>()); - DataProcessor::doSend(*this, *mContextRegistry.get<StringContext>()); - DataProcessor::doSend(*this, *mContextRegistry.get<ArrowContext>()); - DataProcessor::doSend(*this, *mContextRegistry.get<RawBufferContext>()); - for (auto& channel : mSpec.outputChannels) { - DataProcessingHelpers::sendEndOfStream(*this, channel); + EndOfStreamContext eosContext{*context.registry, *context.allocator}; + + context.registry->preEOSCallbacks(eosContext); + context.registry->get<CallbackService>()(CallbackService::Id::EndOfStream, eosContext); + context.registry->postEOSCallbacks(eosContext); + + for (auto& channel : context.spec->outputChannels) { + DataProcessingHelpers::sendEndOfStream(*context.device, channel); } // This is needed because the transport is deleted before the device. - mRelayer->clear(); + context.relayer->clear(); switchState(StreamingState::Idle); - mWasActive = true; - return true; + *context.wasActive = true; + return; } - return true; + return; } void DataProcessingDevice::ResetTask() @@ -551,22 +619,15 @@ void DataProcessingDevice::ResetTask() /// is divided in two parts. In the first one we define a set of lambdas /// which describe what is actually going to happen, hiding all the state /// boilerplate which the user does not need to care about at top level. -bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info) +void DataProcessingDevice::handleData(DataProcessorContext& context, FairMQParts& parts, InputChannelInfo& info) { - assert(mSpec.inputChannels.empty() == false); + ZoneScopedN("DataProcessingDevice::handleData"); + assert(context.spec->inputChannels.empty() == false); assert(parts.Size() > 0); // Initial part. Let's hide all the unnecessary and have // simple lambdas for each of the steps I am planning to have. - assert(!mSpec.inputs.empty()); - - // These duplicate references are created so that each function - // does not need to know about the whole class state, but I can - // fine grain control what is exposed at each state. - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); - auto metricFlusher = make_scope_guard([]() noexcept { - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); - }); + assert(!context.spec->inputs.empty()); enum struct InputType { Invalid, @@ -578,9 +639,11 @@ bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info // and we do a few stats. We bind parts as a lambda captured variable, rather // than an input, because we do not want the outer loop actually be exposed // to the implementation details of the messaging layer. - auto getInputTypes = [& stats = mStats, &parts, &info]() -> std::optional<std::vector<InputType>> { + auto getInputTypes = [&stats = context.registry->get<DataProcessingStats>(), + &parts, &info, &context]() -> std::optional<std::vector<InputType>> { stats.inputParts = parts.Size(); + TracyPlot("messages received", (int64_t)parts.Size()); if (parts.Size() % 2) { return std::nullopt; } @@ -592,6 +655,7 @@ bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info if (sih) { info.state = sih->state; results[hi] = InputType::SourceInfo; + *context.wasActive = true; continue; } auto dh = o2::header::get<DataHeader*>(parts.At(pi)->GetData()); @@ -605,7 +669,9 @@ bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info LOGP(error, "DataHeader payloadSize mismatch"); continue; } + TracyPlot("payload size", (int64_t)dh->payloadSize); auto dph = o2::header::get<DataProcessingHeader*>(parts.At(pi)->GetData()); + TracyAlloc(parts.At(pi + 1)->GetData(), parts.At(pi + 1)->GetSize()); if (!dph) { results[hi] = InputType::Invalid; LOGP(error, "Header stack does not contain DataProcessingHeader"); @@ -616,11 +682,12 @@ bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info return results; }; - auto reportError = [& device = *this](const char* message) { - device.error(message); + auto reportError = [& registry = *context.registry, &context](const char* message) { + context.errorCount++; + registry.get<Monitoring>().send(Metric{*context.errorCount, "errors"}.addTag(Key::Subsystem, Value::DPL)); }; - auto handleValidMessages = [&parts, &relayer = *mRelayer, &reportError](std::vector<InputType> const& types) { + auto handleValidMessages = [&parts, &context = context, &relayer = *context.relayer, &reportError](std::vector<InputType> const& types) { // We relay execution to make sure we have a complete set of parts // available. for (size_t pi = 0; pi < (parts.Size() / 2); ++pi) { @@ -636,6 +703,7 @@ bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info } } break; case InputType::SourceInfo: { + *context.wasActive = true; } break; case InputType::Invalid: { @@ -656,58 +724,74 @@ bool DataProcessingDevice::handleData(FairMQParts& parts, InputChannelInfo& info auto inputTypes = getInputTypes(); if (bool(inputTypes) == false) { reportError("Parts should come in couples. Dropping it."); - return true; + return; } handleValidMessages(*inputTypes); - return true; + return; } -bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::RecordAction>& completed) +namespace +{ +auto calculateInputRecordLatency(InputRecord const& record, uint64_t currentTime) -> DataProcessingStats::InputLatency { + DataProcessingStats::InputLatency result{static_cast<int>(-1), 0}; + + for (auto& item : record) { + auto* header = o2::header::get<DataProcessingHeader*>(item.header); + if (header == nullptr) { + continue; + } + int partLatency = currentTime - header->creation; + result.minLatency = std::min(result.minLatency, partLatency); + result.maxLatency = std::max(result.maxLatency, partLatency); + } + return result; +}; + +auto calculateTotalInputRecordSize(InputRecord const& record) -> int +{ + size_t totalInputSize = 0; + for (auto& item : record) { + auto* header = o2::header::get<DataHeader*>(item.header); + if (header == nullptr) { + continue; + } + totalInputSize += header->payloadSize; + } + return totalInputSize; +}; + +template <typename T> +void update_maximum(std::atomic<T>& maximum_value, T const& value) noexcept +{ + T prev_value = maximum_value; + while (prev_value < value && + !maximum_value.compare_exchange_weak(prev_value, value)) { + } +} +} // namespace + +bool DataProcessingDevice::tryDispatchComputation(DataProcessorContext& context, std::vector<DataRelayer::RecordAction>& completed) +{ + ZoneScopedN("DataProcessingDevice::tryDispatchComputation"); // This is the actual hidden state for the outer loop. In case we decide we // want to support multithreaded dispatching of operations, I can simply // move these to some thread local store and the rest of the lambdas // should work just fine. std::vector<MessageSet> currentSetOfInputs; - auto& allocator = mAllocator; - auto& context = *mContextRegistry.get<MessageContext>(); - auto& device = *this; - auto& errorCallback = mError; - auto& errorCount = mErrorCount; - auto& forwards = mSpec.forwards; - auto& inputsSchema = mSpec.inputs; - auto& processingCount = mProcessingCount; - auto& rdfContext = *mContextRegistry.get<ArrowContext>(); - auto& relayer = *mRelayer; - auto& serviceRegistry = mServiceRegistry; - auto& statefulProcess = mStatefulProcess; - auto& statelessProcess = mStatelessProcess; - auto& stringContext = *mContextRegistry.get<StringContext>(); - auto& timingInfo = mTimingInfo; - auto& timesliceIndex = mServiceRegistry.get<TimesliceIndex>(); - auto& rawContext = *mContextRegistry.get<RawBufferContext>(); - - // These duplicate references are created so that each function - // does not need to know about the whole class state, but I can - // fine grain control what is exposed at each state. - // FIXME: I should use a different id for this state. - auto& monitoringService = mServiceRegistry.get<Monitoring>(); - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); - auto metricFlusher = make_scope_guard([]() noexcept -> void { - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); - }); - - auto reportError = [&device](const char* message) { - device.error(message); + auto reportError = [& registry = *context.registry, &context](const char* message) { + context.errorCount++; + registry.get<Monitoring>().send(Metric{*context.errorCount, "errors"}.addTag(Key::Subsystem, Value::DPL)); }; // For the moment we have a simple "immediately dispatch" policy for stuff // in the cache. This could be controlled from the outside e.g. by waiting // for a few sets of inputs to arrive before we actually dispatch the // computation, however this can be defined at a later stage. - auto canDispatchSomeComputation = [&completed, &relayer]() -> bool { - relayer.getReadyToProcess(completed); + auto canDispatchSomeComputation = [&completed, + &relayer = context.relayer]() -> bool { + relayer->getReadyToProcess(completed); return completed.empty() == false; }; @@ -715,8 +799,10 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor // indicate a complete set of inputs. Notice how I fill the completed // vector and return it, so that I can have a nice for loop iteration later // on. - auto getReadyActions = [&relayer, &completed, &stats = mStats]() -> std::vector<DataRelayer::RecordAction> { - stats.pendingInputs = (int)relayer.getParallelTimeslices() - completed.size(); + auto getReadyActions = [& relayer = context.relayer, + &completed, + &stats = context.registry->get<DataProcessingStats>()]() -> std::vector<DataRelayer::RecordAction> { + stats.pendingInputs = (int)relayer->getParallelTimeslices() - completed.size(); stats.incomplete = completed.empty() ? 1 : 0; return completed; }; @@ -724,8 +810,10 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor // This is needed to convert from a pair of pointers to an actual DataRef // and to make sure the ownership is moved from the cache in the relayer to // the execution. - auto fillInputs = [&relayer, &inputsSchema, ¤tSetOfInputs](TimesliceSlot slot) -> InputRecord { - currentSetOfInputs = std::move(relayer.getInputsForTimeslice(slot)); + auto fillInputs = [& relayer = context.relayer, + &spec = context.spec, + ¤tSetOfInputs](TimesliceSlot slot) -> InputRecord { + currentSetOfInputs = std::move(relayer->getInputsForTimeslice(slot)); auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { if (currentSetOfInputs[i].size() > partindex) { return DataRef{nullptr, @@ -738,47 +826,18 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor return currentSetOfInputs[i].size(); }; InputSpan span{getter, nofPartsGetter, currentSetOfInputs.size()}; - return InputRecord{inputsSchema, std::move(span)}; - }; - - // This is the thing which does the actual computation. No particular reason - // why we do the stateful processing before the stateless one. - // PROCESSING:{START,END} is done so that we can trigger on begin / end of processing - // in the GUI. - auto dispatchProcessing = [&processingCount, &allocator, &statefulProcess, &statelessProcess, &monitoringService, - &context, &stringContext, &rdfContext, &rawContext, &serviceRegistry, &device](TimesliceSlot slot, InputRecord& record) { - if (statefulProcess) { - ProcessingContext processContext{record, serviceRegistry, allocator}; - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_USER_CALLBACK); - statefulProcess(processContext); - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); - processingCount++; - } - if (statelessProcess) { - ProcessingContext processContext{record, serviceRegistry, allocator}; - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_USER_CALLBACK); - statelessProcess(processContext); - StateMonitoring<DataProcessingStatus>::moveTo(DataProcessingStatus::IN_DPL_OVERHEAD); - processingCount++; - } - - DataProcessor::doSend(device, context); - DataProcessor::doSend(device, stringContext); - DataProcessor::doSend(device, rdfContext); - DataProcessor::doSend(device, rawContext); + return InputRecord{spec->inputs, std::move(span)}; }; // I need a preparation step which gets the current timeslice id and // propagates it to the various contextes (i.e. the actual entities which // create messages) because the messages need to have the timeslice id into // it. - auto prepareAllocatorForCurrentTimeSlice = [&timingInfo, &stringContext, &rdfContext, &rawContext, &context, &relayer, ×liceIndex](TimesliceSlot i) { - auto timeslice = timesliceIndex.getTimesliceForSlot(i); - timingInfo.timeslice = timeslice.value; - context.clear(); - stringContext.clear(); - rdfContext.clear(); - rawContext.clear(); + auto prepareAllocatorForCurrentTimeSlice = [& timingInfo = context.timingInfo, + &relayer = context.relayer](TimesliceSlot i) { + ZoneScopedN("DataProcessingDevice::prepareForCurrentTimeslice"); + auto timeslice = relayer->getTimesliceForSlot(i); + timingInfo->timeslice = timeslice.value; }; // When processing them, timers will have to be cleaned up @@ -800,11 +859,36 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor } }; + // Function to cleanup record. For the moment we + // simply use it to keep track of input messages + // which are not needed, to display them in the GUI. + auto cleanupRecord = [](InputRecord& record) { + for (size_t ii = 0, ie = record.size(); ii < ie; ++ii) { + DataRef input = record.getByPos(ii); + if (input.header == nullptr) { + continue; + } + auto sih = o2::header::get<SourceInfoHeader*>(input.header); + if (sih) { + continue; + } + + auto dh = o2::header::get<DataHeader*>(input.header); + if (!dh) { + continue; + } + TracyFree(input.payload); + } + }; + // This is how we do the forwarding, i.e. we push // the inputs which are shared between this device and others // to the next one in the daisy chain. // FIXME: do it in a smarter way than O(N^2) - auto forwardInputs = [&reportError, &forwards, &device, ¤tSetOfInputs](TimesliceSlot slot, InputRecord& record) { + auto forwardInputs = [&reportError, + &spec = context.spec, + &device = context.device, ¤tSetOfInputs](TimesliceSlot slot, InputRecord& record) { + ZoneScopedN("forward inputs"); assert(record.size() == currentSetOfInputs.size()); // we collect all messages per forward in a map and send them together std::unordered_map<std::string, FairMQParts> forwardedParts; @@ -835,7 +919,7 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor } for (auto& part : currentSetOfInputs[ii]) { - for (auto const& forward : forwards) { + for (auto const& forward : spec->forwards) { if (DataSpecUtils::match(forward.matcher, dh->dataOrigin, dh->dataDescription, dh->subSpecification) == false || (dph->startTime % forward.maxTimeslices) != forward.timeslice) { continue; } @@ -858,6 +942,7 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor LOG(ERROR) << "Forwarded data does not have a DataHeader"; continue; } + forwardedParts[forward.channel].AddPart(std::move(header)); forwardedParts[forward.channel].AddPart(std::move(payload)); } @@ -870,103 +955,121 @@ bool DataProcessingDevice::tryDispatchComputation(std::vector<DataRelayer::Recor assert(channelParts.Size() % 2 == 0); assert(o2::header::get<DataProcessingHeader*>(channelParts.At(0)->GetData())); // in DPL we are using subchannel 0 only - device.Send(channelParts, channelName, 0); + device->Send(channelParts, channelName, 0); } }; - auto calculateInputRecordLatency = [](InputRecord const& record, uint64_t currentTime) -> DataProcessingStats::InputLatency { - DataProcessingStats::InputLatency result{static_cast<int>(-1), 0}; - - for (auto& item : record) { - auto* header = o2::header::get<DataProcessingHeader*>(item.header); - if (header == nullptr) { - continue; - } - int partLatency = currentTime - header->creation; - result.minLatency = std::min(result.minLatency, partLatency); - result.maxLatency = std::max(result.maxLatency, partLatency); - } - return result; + auto switchState = [& control = context.registry->get<ControlService>(), + &state = context.state](StreamingState newState) { + state->streaming = newState; + control.notifyStreamingState(state->streaming); }; - auto calculateTotalInputRecordSize = [](InputRecord const& record) -> int { - size_t totalInputSize = 0; - for (auto& item : record) { - auto* header = o2::header::get<DataHeader*>(item.header); - if (header == nullptr) { - continue; - } - totalInputSize += header->payloadSize; + if (canDispatchSomeComputation() == false) { + return false; + } + + auto postUpdateStats = [& stats = context.registry->get<DataProcessingStats>()](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart) { + std::atomic_thread_fence(std::memory_order_release); + for (size_t ai = 0; ai != record.size(); ai++) { + auto cacheId = action.slot.index * record.size() + ai; + auto state = record.isValid(ai) ? 3 : 0; + update_maximum(stats.statesSize, cacheId + 1); + assert(cacheId < DataProcessingStats::MAX_RELAYER_STATES); + stats.relayerState[cacheId].store(state); } - return totalInputSize; + uint64_t tEnd = uv_hrtime(); + stats.lastElapsedTimeMs = tEnd - tStart; + stats.lastTotalProcessedSize = calculateTotalInputRecordSize(record); + stats.lastLatency = calculateInputRecordLatency(record, tStart); }; - auto switchState = [& control = mServiceRegistry.get<ControlService>(), - &state = mState.streaming](StreamingState newState) { - state = newState; - control.notifyStreamingState(state); + auto preUpdateStats = [& stats = context.registry->get<DataProcessingStats>()](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart) { + std::atomic_thread_fence(std::memory_order_release); + for (size_t ai = 0; ai != record.size(); ai++) { + auto cacheId = action.slot.index * record.size() + ai; + auto state = record.isValid(ai) ? 2 : 0; + update_maximum(stats.statesSize, cacheId + 1); + assert(cacheId < DataProcessingStats::MAX_RELAYER_STATES); + stats.relayerState[cacheId].store(state); + } }; - if (canDispatchSomeComputation() == false) { - return false; + for (auto action : getReadyActions()) { + if (action.op == CompletionPolicy::CompletionOp::Wait) { + continue; } - for (auto action : getReadyActions()) { - if (action.op == CompletionPolicy::CompletionOp::Wait) { + prepareAllocatorForCurrentTimeSlice(TimesliceSlot{action.slot}); + InputRecord record = fillInputs(action.slot); + ProcessingContext processContext{record, *context.registry, *context.allocator}; + { + ZoneScopedN("service pre processing"); + context.registry->preProcessingCallbacks(processContext); + } + if (action.op == CompletionPolicy::CompletionOp::Discard) { + context.registry->postDispatchingCallbacks(processContext); + if (context.spec->forwards.empty() == false) { + forwardInputs(action.slot, record); continue; } + } - prepareAllocatorForCurrentTimeSlice(TimesliceSlot{action.slot}); - InputRecord record = fillInputs(action.slot); - if (action.op == CompletionPolicy::CompletionOp::Discard) { - if (forwards.empty() == false) { - forwardInputs(action.slot, record); - continue; + uint64_t tStart = uv_hrtime(); + preUpdateStats(action, record, tStart); + try { + if (context.state->quitRequested == false) { + + if (*context.statefulProcess) { + ZoneScopedN("statefull process"); + (*context.statefulProcess)(processContext); } - } - uint64_t tStart = uv_hrtime(); - for (size_t ai = 0; ai != record.size(); ai++) { - auto cacheId = action.slot.index * record.size() + ai; - auto state = record.isValid(ai) ? 2 : 0; - mStats.relayerState.resize(std::max(cacheId + 1, mStats.relayerState.size()), 0); - mStats.relayerState[cacheId] = state; - } - try { - if (mState.quitRequested == false) { - dispatchProcessing(action.slot, record); + if (*context.statelessProcess) { + ZoneScopedN("stateless process"); + (*context.statelessProcess)(processContext); } - } catch (std::exception& e) { - mErrorHandling(e, record); - } - for (size_t ai = 0; ai != record.size(); ai++) { - auto cacheId = action.slot.index * record.size() + ai; - auto state = record.isValid(ai) ? 3 : 0; - mStats.relayerState.resize(std::max(cacheId + 1, mStats.relayerState.size()), 0); - mStats.relayerState[cacheId] = state; - } - uint64_t tEnd = uv_hrtime(); - mStats.lastElapsedTimeMs = tEnd - tStart; - mStats.lastTotalProcessedSize = calculateTotalInputRecordSize(record); - mStats.lastLatency = calculateInputRecordLatency(record, tStart); - // We forward inputs only when we consume them. If we simply Process them, - // we keep them for next message arriving. - if (action.op == CompletionPolicy::CompletionOp::Consume) { - if (forwards.empty() == false) { - forwardInputs(action.slot, record); + + { + ZoneScopedN("service post processing"); + context.registry->postProcessingCallbacks(processContext); } - } else if (action.op == CompletionPolicy::CompletionOp::Process) { - cleanTimers(action.slot, record); } + } catch (std::exception& ex) { + ZoneScopedN("error handling"); + /// Convert a standatd exception to a RuntimeErrorRef + /// Notice how this will lose the backtrace information + /// and report the exception coming from here. + auto e = runtime_error(ex.what()); + (*context.errorHandling)(e, record); + } catch (o2::framework::RuntimeErrorRef e) { + ZoneScopedN("error handling"); + (*context.errorHandling)(e, record); } - // We now broadcast the end of stream if it was requested - if (mState.streaming == StreamingState::EndOfStreaming) { - for (auto& channel : mSpec.outputChannels) { - DataProcessingHelpers::sendEndOfStream(*this, channel); + + postUpdateStats(action, record, tStart); + // We forward inputs only when we consume them. If we simply Process them, + // we keep them for next message arriving. + if (action.op == CompletionPolicy::CompletionOp::Consume) { + context.registry->postDispatchingCallbacks(processContext); + if (context.spec->forwards.empty() == false) { + forwardInputs(action.slot, record); } - switchState(StreamingState::Idle); +#ifdef TRACY_ENABLE + cleanupRecord(record); +#endif + } else if (action.op == CompletionPolicy::CompletionOp::Process) { + cleanTimers(action.slot, record); + } + } + // We now broadcast the end of stream if it was requested + if (context.state->streaming == StreamingState::EndOfStreaming) { + for (auto& channel : context.spec->outputChannels) { + DataProcessingHelpers::sendEndOfStream(*context.device, channel); } + switchState(StreamingState::Idle); + } - return true; + return true; } void DataProcessingDevice::error(const char* msg) diff --git a/Framework/Core/src/DataProcessor.cxx b/Framework/Core/src/DataProcessor.cxx index f68c60d2de870..35a4ce1d12cbd 100644 --- a/Framework/Core/src/DataProcessor.cxx +++ b/Framework/Core/src/DataProcessor.cxx @@ -13,9 +13,12 @@ #include "Framework/ArrowContext.h" #include "Framework/RawBufferContext.h" #include "Framework/TMessageSerializer.h" +#include "Framework/ServiceRegistry.h" #include "FairMQResizableBuffer.h" #include "CommonUtils/BoostSerializer.h" #include "Headers/DataHeader.h" + +#include <Monitoring/Monitoring.h> #include <fairmq/FairMQParts.h> #include <fairmq/FairMQDevice.h> #include <arrow/io/memory.h> @@ -26,9 +29,7 @@ using namespace o2::framework; using DataHeader = o2::header::DataHeader; -namespace o2 -{ -namespace framework +namespace o2::framework { void DataProcessor::doSend(FairMQDevice& device, FairMQParts&& parts, const char* channel, unsigned int index) @@ -36,7 +37,7 @@ void DataProcessor::doSend(FairMQDevice& device, FairMQParts&& parts, const char device.Send(parts, channel, index); } -void DataProcessor::doSend(FairMQDevice& device, MessageContext& context) +void DataProcessor::doSend(FairMQDevice& device, MessageContext& context, ServiceRegistry&) { std::unordered_map<std::string const*, FairMQParts> outputs; auto contextMessages = context.getMessagesForSending(); @@ -54,7 +55,7 @@ void DataProcessor::doSend(FairMQDevice& device, MessageContext& context) } } -void DataProcessor::doSend(FairMQDevice& device, StringContext& context) +void DataProcessor::doSend(FairMQDevice& device, StringContext& context, ServiceRegistry&) { for (auto& messageRef : context) { FairMQParts parts; @@ -73,9 +74,13 @@ void DataProcessor::doSend(FairMQDevice& device, StringContext& context) } } -// FIXME: for the moment we simply send empty messages. -void DataProcessor::doSend(FairMQDevice& device, ArrowContext& context) +void DataProcessor::doSend(FairMQDevice& device, ArrowContext& context, ServiceRegistry& registry) { + using o2::monitoring::Metric; + using o2::monitoring::Monitoring; + using o2::monitoring::tags::Key; + using o2::monitoring::tags::Value; + for (auto& messageRef : context) { FairMQParts parts; // Depending on how the arrow table is constructed, we finalize @@ -89,13 +94,20 @@ void DataProcessor::doSend(FairMQDevice& device, ArrowContext& context) // exposing it to the user in the first place. DataHeader* dh = const_cast<DataHeader*>(cdh); dh->payloadSize = payload->GetSize(); + dh->serialization = o2::header::gSerializationMethodArrow; + context.updateBytesSent(payload->GetSize()); + context.updateMessagesSent(1); parts.AddPart(std::move(messageRef.header)); parts.AddPart(std::move(payload)); device.Send(parts, messageRef.channel, 0); } + auto& monitoring = registry.get<Monitoring>(); + monitoring.send(Metric{(uint64_t)context.bytesSent(), "arrow-bytes-created"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.send(Metric{(uint64_t)context.messagesCreated(), "arrow-messages-created"}.addTag(Key::Subsystem, Value::DPL)); + monitoring.flushBuffer(); } -void DataProcessor::doSend(FairMQDevice& device, RawBufferContext& context) +void DataProcessor::doSend(FairMQDevice& device, RawBufferContext& context, ServiceRegistry& registry) { for (auto& messageRef : context) { FairMQParts parts; @@ -116,5 +128,4 @@ void DataProcessor::doSend(FairMQDevice& device, RawBufferContext& context) } } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 9342f66c66482..b332304ca432d 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -43,7 +43,6 @@ constexpr int INVALID_INPUT = -1; // The number should really be tuned at runtime for each processor. constexpr int DEFAULT_PIPELINE_LENGTH = 16; -// FIXME: do we really need to pass the forwards? DataRelayer::DataRelayer(const CompletionPolicy& policy, std::vector<InputRoute> const& routes, monitoring::Monitoring& metrics, @@ -54,6 +53,8 @@ DataRelayer::DataRelayer(const CompletionPolicy& policy, mDistinctRoutesIndex{DataRelayerHelpers::createDistinctRouteIndex(routes)}, mInputMatchers{DataRelayerHelpers::createInputMatchers(routes)} { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + setPipelineLength(DEFAULT_PIPELINE_LENGTH); // The queries are all the same, so we only have width 1 @@ -71,9 +72,17 @@ DataRelayer::DataRelayer(const CompletionPolicy& policy, } } +TimesliceId DataRelayer::getTimesliceForSlot(TimesliceSlot slot) +{ + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + return mTimesliceIndex.getTimesliceForSlot(slot); +} + DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector<ExpirationHandler> const& expirationHandlers, ServiceRegistry& services) { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + ActivityStats activity; /// Nothing to do if nothing can expire. if (expirationHandlers.empty()) { @@ -179,6 +188,7 @@ DataRelayer::RelayChoice DataRelayer::relay(std::unique_ptr<FairMQMessage>&& header, std::unique_ptr<FairMQMessage>&& payload) { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); // STATE HOLDING VARIABLES // This is the class level state of the relaying. If we start supporting // multithreading this will have to be made thread safe before we can invoke @@ -383,6 +393,8 @@ DataRelayer::RelayChoice void DataRelayer::getReadyToProcess(std::vector<DataRelayer::RecordAction>& completed) { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + // THE STATE const auto& cache = mCache; const auto numInputTypes = mDistinctRoutesIndex.size(); @@ -460,6 +472,8 @@ void DataRelayer::getReadyToProcess(std::vector<DataRelayer::RecordAction>& comp std::vector<o2::framework::MessageSet> DataRelayer::getInputsForTimeslice(TimesliceSlot slot) { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + const auto numInputTypes = mDistinctRoutesIndex.size(); // State of the computation std::vector<MessageSet> messages(numInputTypes); @@ -513,6 +527,8 @@ std::vector<o2::framework::MessageSet> DataRelayer::getInputsForTimeslice(Timesl void DataRelayer::clear() { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + for (auto& cache : mCache) { cache.clear(); } @@ -533,6 +549,8 @@ size_t /// the time pipelining. void DataRelayer::setPipelineLength(size_t s) { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + mTimesliceIndex.resize(s); mVariableContextes.resize(s); publishMetrics(); @@ -540,6 +558,8 @@ void DataRelayer::setPipelineLength(size_t s) void DataRelayer::publishMetrics() { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); + auto numInputTypes = mDistinctRoutesIndex.size(); mCache.resize(numInputTypes * mTimesliceIndex.size()); mMetrics.send({(int)numInputTypes, "data_relayer/h"}); @@ -577,6 +597,7 @@ DataRelayerStats const& DataRelayer::getStats() const void DataRelayer::sendContextState() { + std::scoped_lock<LockableBase(std::recursive_mutex)> lock(mMutex); for (size_t ci = 0; ci < mTimesliceIndex.size(); ++ci) { auto slot = TimesliceSlot{ci}; sendVariableContextMetrics(mTimesliceIndex.getPublishedVariablesForSlot(slot), slot, diff --git a/Framework/Core/src/DataSamplingPolicy.cxx b/Framework/Core/src/DataSamplingPolicy.cxx deleted file mode 100644 index 5e69f90f91baf..0000000000000 --- a/Framework/Core/src/DataSamplingPolicy.cxx +++ /dev/null @@ -1,169 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 DataSamplingPolicy.cxx -/// \brief Implementation of O2 Data Sampling Policy -/// -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#include "Framework/DataSamplingPolicy.h" -#include "Framework/DataSamplingHeader.h" -#include "Framework/DataSamplingConditionFactory.h" -#include "Framework/DataSpecUtils.h" -#include "Framework/DataDescriptorQueryBuilder.h" - -#include <boost/property_tree/ptree.hpp> - -namespace o2::framework -{ - -using boost::property_tree::ptree; - -DataSamplingPolicy::DataSamplingPolicy() = default; - -DataSamplingPolicy::DataSamplingPolicy(const ptree& config) -{ - configure(config); -} - -DataSamplingPolicy::~DataSamplingPolicy() = default; - -void DataSamplingPolicy::configure(const ptree& config) -{ - mName = config.get<std::string>("id"); - if (mName.size() > 14) { - LOG(WARNING) << "DataSamplingPolicy name '" << mName << "' is longer than 14 characters, we have to trim it. " - << "Use a shorter policy name to avoid potential output name conflicts."; - mName.resize(14); - } - - auto subSpecString = config.get_optional<std::string>("subSpec").value_or("*"); - auto subSpec = subSpecString.find_first_of("-*") != std::string::npos ? -1 : std::strtoull(subSpecString.c_str(), nullptr, 10); - - mPaths.clear(); - size_t outputId = 0; - std::vector<InputSpec> inputSpecs = DataDescriptorQueryBuilder::parse(config.get<std::string>("query").c_str()); - - for (const auto& inputSpec : inputSpecs) { - - if (DataSpecUtils::getOptionalSubSpec(inputSpec).has_value()) { - OutputSpec outputSpec{ - {inputSpec.binding}, - createPolicyDataOrigin(), - createPolicyDataDescription(mName, outputId++), - DataSpecUtils::getOptionalSubSpec(inputSpec).value(), - inputSpec.lifetime}; - - mPaths.push_back({inputSpec, outputSpec}); - - } else { - OutputSpec outputSpec{ - {inputSpec.binding}, - {createPolicyDataOrigin(), createPolicyDataDescription(mName, outputId++)}, - inputSpec.lifetime}; - - mPaths.push_back({inputSpec, outputSpec}); - } - - if (outputId > 9) { - LOG(ERROR) << "Maximum 10 inputs in DataSamplingPolicy are supported. Call the developers if you really need more."; - break; - } - } - - mConditions.clear(); - for (const auto& conditionConfig : config.get_child("samplingConditions")) { - mConditions.push_back(DataSamplingConditionFactory::create(conditionConfig.second.get<std::string>("condition"))); - mConditions.back()->configure(conditionConfig.second); - } - - mFairMQOutputChannel = config.get_optional<std::string>("fairMQOutput").value_or(""); -} - -bool DataSamplingPolicy::match(const ConcreteDataMatcher& input) const -{ - return mPaths.find(input) != mPaths.end(); -} - -bool DataSamplingPolicy::decide(const o2::framework::DataRef& dataRef) -{ - bool decision = std::all_of(mConditions.begin(), mConditions.end(), - [dataRef](std::unique_ptr<DataSamplingCondition>& condition) { - return condition->decide(dataRef); - }); - - mTotalAcceptedMessages += decision; - mTotalEvaluatedMessages++; - - return decision; -} - -const Output DataSamplingPolicy::prepareOutput(const ConcreteDataMatcher& input, Lifetime lifetime) const -{ - auto result = mPaths.find(input); - if (result != mPaths.end()) { - auto dataType = DataSpecUtils::asConcreteDataTypeMatcher(result->second); - return Output{dataType.origin, dataType.description, input.subSpec, lifetime}; - } else { - return Output{header::gDataOriginInvalid, header::gDataDescriptionInvalid}; - } -} - -const std::string& DataSamplingPolicy::getName() const -{ - return mName; -} - -const DataSamplingPolicy::PathMap& DataSamplingPolicy::getPathMap() const -{ - return mPaths; -} - -const std::string& DataSamplingPolicy::getFairMQOutputChannel() const -{ - return mFairMQOutputChannel; -} - -std::string DataSamplingPolicy::getFairMQOutputChannelName() const -{ - size_t nameBegin = mFairMQOutputChannel.find("name=") + sizeof("name=") - 1; - size_t nameEnd = mFairMQOutputChannel.find_first_of(',', nameBegin); - std::string name = mFairMQOutputChannel.substr(nameBegin, nameEnd - nameBegin); - return name; -} - -uint32_t DataSamplingPolicy::getTotalAcceptedMessages() const -{ - return mTotalAcceptedMessages; -} -uint32_t DataSamplingPolicy::getTotalEvaluatedMessages() const -{ - return mTotalEvaluatedMessages; -} - -header::DataOrigin DataSamplingPolicy::createPolicyDataOrigin() -{ - return header::DataOrigin("DS"); -} - -header::DataDescription DataSamplingPolicy::createPolicyDataDescription(std::string policyName, size_t id) -{ - if (policyName.size() > 14) { - LOG(WARNING) << "DataSamplingPolicy name '" << policyName << "' is longer than 14 characters, we have to trim it. " - << "Use a shorter policy name to avoid potential output name conflicts."; - policyName.resize(14); - } - - header::DataDescription outputDescription; - outputDescription.runtimeInit(std::string(policyName + "-" + std::to_string(id)).c_str()); - return outputDescription; -} - -} // namespace o2::framework \ No newline at end of file diff --git a/Framework/Core/src/DataSamplingReadoutAdapter.cxx b/Framework/Core/src/DataSamplingReadoutAdapter.cxx deleted file mode 100644 index ed574c189679c..0000000000000 --- a/Framework/Core/src/DataSamplingReadoutAdapter.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/DataSamplingReadoutAdapter.h" -#include "Framework/DataProcessingHeader.h" -#include "Headers/DataHeader.h" -#include "Framework/DataSpecUtils.h" - -#include <Common/DataBlock.h> - -namespace o2::framework -{ - -using DataHeader = o2::header::DataHeader; - -InjectorFunction dataSamplingReadoutAdapter(OutputSpec const& spec) -{ - return [spec](FairMQDevice& device, FairMQParts& parts, ChannelRetriever channelRetriever) { - for (size_t i = 0; i < parts.Size() / 2; ++i) { - - auto dbh = reinterpret_cast<DataBlockHeaderBase*>(parts.At(2 * i)->GetData()); - assert(dbh->dataSize == parts.At(2 * i + 1)->GetSize()); - - DataHeader dh; - ConcreteDataTypeMatcher dataType = DataSpecUtils::asConcreteDataTypeMatcher(spec); - dh.dataOrigin = dataType.origin; - dh.dataDescription = dataType.description; - dh.subSpecification = DataSpecUtils::getOptionalSubSpec(spec).value_or(dbh->linkId); - dh.payloadSize = dbh->dataSize; - dh.payloadSerializationMethod = o2::header::gSerializationMethodNone; - - DataProcessingHeader dph{dbh->blockId, 0}; - o2::header::Stack headerStack{dh, dph}; - sendOnChannel(device, std::move(headerStack), std::move(parts.At(2 * i + 1)), spec, channelRetriever); - } - }; -} - -} // namespace o2::framework diff --git a/Framework/Core/src/DataSpecUtils.cxx b/Framework/Core/src/DataSpecUtils.cxx index 6cdf4faaf6d43..0303059f2cec6 100644 --- a/Framework/Core/src/DataSpecUtils.cxx +++ b/Framework/Core/src/DataSpecUtils.cxx @@ -11,6 +11,9 @@ #include "Framework/DataDescriptorMatcher.h" #include "Framework/DataMatcherWalker.h" #include "Framework/VariantHelpers.h" +#include "Framework/Logger.h" +#include "Framework/RuntimeError.h" + #include <cstring> #include <cinttypes> @@ -69,7 +72,7 @@ std::string DataSpecUtils::describe(InputSpec const& spec) ss << "<matcher query: " << *matcher << ">"; return ss.str(); } - throw std::runtime_error("Unhandled InputSpec kind."); + throw runtime_error("Unhandled InputSpec kind."); } std::string DataSpecUtils::describe(OutputSpec const& spec) @@ -94,7 +97,7 @@ void DataSpecUtils::describe(char* buffer, size_t size, InputSpec const& spec) ss << "<matcher query: " << *matcher << ">"; strncpy(buffer, ss.str().c_str(), size - 1); } else { - throw std::runtime_error("Unsupported InputSpec"); + throw runtime_error("Unsupported InputSpec"); } } @@ -111,7 +114,7 @@ std::string DataSpecUtils::label(InputSpec const& spec) return result; } - throw std::runtime_error("Unsupported InputSpec"); + throw runtime_error("Unsupported InputSpec"); } std::string DataSpecUtils::label(OutputSpec const& spec) @@ -128,7 +131,7 @@ std::string DataSpecUtils::restEndpoint(InputSpec const& spec) if (auto concrete = std::get_if<ConcreteDataMatcher>(&spec.matcher)) { return std::string("/") + join(*concrete, "/"); } else { - throw std::runtime_error("Unsupported InputSpec kind"); + throw runtime_error("Unsupported InputSpec kind"); } } @@ -214,7 +217,7 @@ bool DataSpecUtils::match(InputSpec const& spec, ConcreteDataMatcher const& targ data_matcher::VariableContext context; return matcher->match(target, context); } else { - throw std::runtime_error("Unsupported InputSpec"); + throw runtime_error("Unsupported InputSpec"); } } @@ -264,22 +267,22 @@ bool DataSpecUtils::partialMatch(InputSpec const& input, header::DataOrigin cons return DataSpecUtils::asConcreteOrigin(input) == origin; } -ConcreteDataMatcher DataSpecUtils::asConcreteDataMatcher(InputSpec const& spec) -{ - return std::get<ConcreteDataMatcher>(spec.matcher); -} - -ConcreteDataMatcher DataSpecUtils::asConcreteDataMatcher(OutputSpec const& spec) +bool DataSpecUtils::partialMatch(InputSpec const& input, header::DataDescription const& description) { - return std::get<ConcreteDataMatcher>(spec.matcher); + try { + return DataSpecUtils::asConcreteDataDescription(input) == description; + } catch (...) { + return false; + } } -ConcreteDataTypeMatcher DataSpecUtils::asConcreteDataTypeMatcher(OutputSpec const& spec) +bool DataSpecUtils::partialMatch(OutputSpec const& output, header::DataDescription const& description) { - return std::visit([](auto const& concrete) { - return ConcreteDataTypeMatcher{concrete.origin, concrete.description}; - }, - spec.matcher); + try { + return DataSpecUtils::asConcreteDataTypeMatcher(output).description == description; + } catch (...) { + return false; + } } struct MatcherInfo { @@ -370,10 +373,40 @@ MatcherInfo extractMatcherInfo(DataDescriptorMatcher const& top) return state; } +ConcreteDataMatcher DataSpecUtils::asConcreteDataMatcher(InputSpec const& spec) +{ + return std::visit(overloaded{[](ConcreteDataMatcher const& concrete) { + return concrete; + }, + [&binding = spec.binding](DataDescriptorMatcher const& matcher) { + auto info = extractMatcherInfo(matcher); + if (info.hasOrigin && info.hasUniqueOrigin && + info.hasDescription && info.hasDescription && + info.hasSubSpec && info.hasUniqueSubSpec) { + return ConcreteDataMatcher{info.origin, info.description, info.subSpec}; + } + throw std::runtime_error("Cannot convert " + binding + " to ConcreteDataMatcher"); + }}, + spec.matcher); +} + +ConcreteDataMatcher DataSpecUtils::asConcreteDataMatcher(OutputSpec const& spec) +{ + return std::get<ConcreteDataMatcher>(spec.matcher); +} + +ConcreteDataTypeMatcher DataSpecUtils::asConcreteDataTypeMatcher(OutputSpec const& spec) +{ + return std::visit([](auto const& concrete) { + return ConcreteDataTypeMatcher{concrete.origin, concrete.description}; + }, + spec.matcher); +} + ConcreteDataTypeMatcher DataSpecUtils::asConcreteDataTypeMatcher(InputSpec const& spec) { return std::visit(overloaded{ - [](ConcreteDataMatcher const& concrete) { + [](auto const& concrete) { return ConcreteDataTypeMatcher{concrete.origin, concrete.description}; }, [](DataDescriptorMatcher const& matcher) { @@ -381,7 +414,7 @@ ConcreteDataTypeMatcher DataSpecUtils::asConcreteDataTypeMatcher(InputSpec const if (state.hasUniqueOrigin && state.hasUniqueDescription) { return ConcreteDataTypeMatcher{state.origin, state.description}; } - throw std::runtime_error("Could not extract data type from query"); + throw runtime_error("Could not extract data type from query"); }}, spec.matcher); } @@ -389,7 +422,7 @@ ConcreteDataTypeMatcher DataSpecUtils::asConcreteDataTypeMatcher(InputSpec const header::DataOrigin DataSpecUtils::asConcreteOrigin(InputSpec const& spec) { return std::visit(overloaded{ - [](ConcreteDataMatcher const& concrete) { + [](auto const& concrete) { return concrete.origin; }, [](DataDescriptorMatcher const& matcher) { @@ -397,7 +430,7 @@ header::DataOrigin DataSpecUtils::asConcreteOrigin(InputSpec const& spec) if (state.hasUniqueOrigin) { return state.origin; } - throw std::runtime_error("Could not extract data type from query"); + throw runtime_error("Could not extract data type from query"); }}, spec.matcher); } @@ -405,7 +438,7 @@ header::DataOrigin DataSpecUtils::asConcreteOrigin(InputSpec const& spec) header::DataDescription DataSpecUtils::asConcreteDataDescription(InputSpec const& spec) { return std::visit(overloaded{ - [](ConcreteDataMatcher const& concrete) { + [](auto const& concrete) { return concrete.description; }, [](DataDescriptorMatcher const& matcher) { @@ -413,7 +446,7 @@ header::DataDescription DataSpecUtils::asConcreteDataDescription(InputSpec const if (state.hasUniqueDescription) { return state.description; } - throw std::runtime_error("Could not extract data type from query"); + throw runtime_error("Could not extract data type from query"); }}, spec.matcher); } @@ -432,7 +465,7 @@ OutputSpec DataSpecUtils::asOutputSpec(InputSpec const& spec) return OutputSpec{{spec.binding}, ConcreteDataTypeMatcher{state.origin, state.description}, spec.lifetime}; } - throw std::runtime_error("Could not extract neither ConcreteDataMatcher nor ConcreteDataTypeMatcher from query" + describe(spec)); + throw runtime_error_f("Could not extract neither ConcreteDataMatcher nor ConcreteDataTypeMatcher from query %s", describe(spec).c_str()); }}, spec.matcher); } @@ -467,6 +500,24 @@ DataDescriptorMatcher DataSpecUtils::dataDescriptorMatcherFrom(header::DataOrigi return std::move(matchOnlyOrigin); } +DataDescriptorMatcher DataSpecUtils::dataDescriptorMatcherFrom(header::DataDescription const& description) +{ + char buf[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + strncpy(buf, description.str, 16); + DataDescriptorMatcher matchOnlyOrigin{ + DataDescriptorMatcher::Op::And, + OriginValueMatcher{ContextRef{1}}, + std::make_unique<DataDescriptorMatcher>( + DataDescriptorMatcher::Op::And, + DescriptionValueMatcher{buf}, + std::make_unique<DataDescriptorMatcher>( + DataDescriptorMatcher::Op::And, + SubSpecificationTypeValueMatcher{ContextRef{2}}, + std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Just, + StartTimeValueMatcher{ContextRef{0}})))}; + return std::move(matchOnlyOrigin); +} + InputSpec DataSpecUtils::matchingInput(OutputSpec const& spec) { return std::visit(overloaded{ @@ -511,11 +562,38 @@ std::optional<header::DataHeader::SubSpecificationType> DataSpecUtils::getOption if (state.hasUniqueSubSpec) { return std::make_optional(state.subSpec); } else if (state.hasError) { - throw std::runtime_error("Could not extract subSpec from query"); + throw runtime_error("Could not extract subSpec from query"); } return {}; }}, spec.matcher); } +bool DataSpecUtils::includes(const InputSpec& left, const InputSpec& right) +{ + return std::visit( + overloaded{ + [&left](ConcreteDataMatcher const& rightMatcher) { + return match(left, rightMatcher); + }, + [&left](DataDescriptorMatcher const& rightMatcher) { + auto rightInfo = extractMatcherInfo(rightMatcher); + return std::visit( + overloaded{ + [&rightInfo](ConcreteDataMatcher const& leftMatcher) { + return rightInfo.hasUniqueOrigin && rightInfo.origin == leftMatcher.origin && + rightInfo.hasUniqueDescription && rightInfo.description == leftMatcher.description && + rightInfo.hasUniqueSubSpec && rightInfo.subSpec == leftMatcher.subSpec; + }, + [&rightInfo](DataDescriptorMatcher const& leftMatcher) { + auto leftInfo = extractMatcherInfo(leftMatcher); + return (!leftInfo.hasOrigin || (rightInfo.hasOrigin && leftInfo.origin == rightInfo.origin)) && + (!leftInfo.hasDescription || (rightInfo.hasDescription && leftInfo.description == rightInfo.description)) && + (!leftInfo.hasSubSpec || (rightInfo.hasSubSpec && leftInfo.subSpec == rightInfo.hasSubSpec)); + }}, + left.matcher); + }}, + right.matcher); +} + } // namespace o2::framework diff --git a/Framework/Core/src/DeviceConfigInfo.cxx b/Framework/Core/src/DeviceConfigInfo.cxx index 62266282d5f89..c5333d5874c12 100644 --- a/Framework/Core/src/DeviceConfigInfo.cxx +++ b/Framework/Core/src/DeviceConfigInfo.cxx @@ -10,15 +10,9 @@ #include "Framework/DeviceConfigInfo.h" #include "Framework/DeviceInfo.h" -#include <cassert> -#include <cinttypes> #include <cstdlib> - -#include <algorithm> -#include <regex> #include <string_view> -#include <tuple> -#include <iostream> +#include <boost/property_tree/json_parser.hpp> namespace o2::framework { @@ -67,10 +61,20 @@ bool DeviceConfigHelper::processConfig(ParsedConfigMatch& match, match.beginProvenance == nullptr || match.endProvenance == nullptr) { return false; } - info.currentConfig.put(std::string(match.beginKey, match.endKey - match.beginKey), - std::string(match.beginValue, match.endValue - match.beginValue)); - info.currentProvenance.put(std::string(match.beginKey, match.endKey - match.beginKey), - std::string(match.beginProvenance, match.endProvenance - match.beginProvenance)); + auto keyString = std::string(match.beginKey, match.endKey - match.beginKey); + auto valueString = std::string(match.beginValue, match.endValue - match.beginValue); + auto provenanceString = std::string(match.beginProvenance, match.endProvenance - match.beginProvenance); + boost::property_tree::ptree branch; + std::stringstream ss{valueString}; + try { + boost::property_tree::json_parser::read_json(ss, branch); + info.currentConfig.put_child(keyString, branch); + } catch (boost::exception&) { + // in case it is not a tree but a single value + info.currentConfig.put(keyString, valueString); + } + + info.currentProvenance.put(keyString, provenanceString); return true; } diff --git a/Framework/Core/src/DeviceMetricsInfo.cxx b/Framework/Core/src/DeviceMetricsInfo.cxx index 523cd8a69d50f..9f8721889caf5 100644 --- a/Framework/Core/src/DeviceMetricsInfo.cxx +++ b/Framework/Core/src/DeviceMetricsInfo.cxx @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #include "Framework/DeviceMetricsInfo.h" +#include "Framework/RuntimeError.h" #include <cassert> #include <cinttypes> #include <cstdlib> @@ -19,9 +20,7 @@ #include <tuple> #include <iostream> -namespace o2 -{ -namespace framework +namespace o2::framework { // Parses a metric in the form @@ -93,6 +92,13 @@ bool DeviceMetricsHelper::parseMetric(std::string_view const s, ParsedMetricMatc match.beginStringValue = spaces[1] + 1; match.endStringValue = *(space - 2); break; + case MetricType::Uint64: + match.uint64Value = strtoul(spaces[1] + 1, &ep, 10); + if (ep != *(space - 2)) { + return false; + } + break; + default: return false; } @@ -115,6 +121,7 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, switch (match.type) { case MetricType::Float: case MetricType::Int: + case MetricType::Uint64: break; case MetricType::String: { auto lastChar = std::min(match.endStringValue - match.beginStringValue, StringMetric::MAX_SIZE - 1); @@ -143,6 +150,7 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, MetricInfo metricInfo; metricInfo.pos = 0; metricInfo.type = match.type; + metricInfo.filledMetrics = 0; // Add a new empty buffer for it of the correct kind switch (match.type) { case MetricType::Int: @@ -157,6 +165,11 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, metricInfo.storeIdx = info.floatMetrics.size(); info.floatMetrics.emplace_back(std::array<float, 1024>{}); break; + case MetricType::Uint64: + metricInfo.storeIdx = info.uint64Metrics.size(); + info.uint64Metrics.emplace_back(std::array<uint64_t, 1024>{}); + break; + default: return false; }; @@ -166,6 +179,7 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, info.min.push_back(std::numeric_limits<float>::max()); info.maxDomain.push_back(std::numeric_limits<size_t>::lowest()); info.minDomain.push_back(std::numeric_limits<size_t>::max()); + info.changed.push_back(false); // Add the index by name in the correct position // this will require moving the tail of the index, @@ -175,6 +189,7 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, auto lastChar = std::min(match.endKey - match.beginKey, (ptrdiff_t)MetricLabelIndex::MAX_METRIC_LABEL_SIZE - 1); memcpy(metricLabelIdx.label, match.beginKey, lastChar); metricLabelIdx.label[lastChar] = '\0'; + metricLabelIdx.size = lastChar; metricLabelIdx.index = info.metrics.size(); info.metricLabelsIdx.insert(mi, metricLabelIdx); // Add the the actual Metric info to the store @@ -206,6 +221,7 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, info.timestamps[metricIndex][metricInfo.pos] = match.timestamp; // Update the position where to write the next metric metricInfo.pos = (metricInfo.pos + 1) % info.intMetrics[metricInfo.storeIdx].size(); + ++metricInfo.filledMetrics; } break; case MetricType::String: { info.stringMetrics[metricInfo.storeIdx][metricInfo.pos] = stringValue; @@ -213,6 +229,7 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, // so that we do not update timestamps for broken metrics info.timestamps[metricIndex][metricInfo.pos] = match.timestamp; metricInfo.pos = (metricInfo.pos + 1) % info.stringMetrics[metricInfo.storeIdx].size(); + ++metricInfo.filledMetrics; } break; case MetricType::Float: { info.floatMetrics[metricInfo.storeIdx][metricInfo.pos] = match.floatValue; @@ -222,23 +239,37 @@ bool DeviceMetricsHelper::processMetric(ParsedMetricMatch& match, // so that we do not update timestamps for broken metrics info.timestamps[metricIndex][metricInfo.pos] = match.timestamp; metricInfo.pos = (metricInfo.pos + 1) % info.floatMetrics[metricInfo.storeIdx].size(); + ++metricInfo.filledMetrics; } break; + case MetricType::Uint64: { + info.uint64Metrics[metricInfo.storeIdx][metricInfo.pos] = match.uint64Value; + info.max[metricIndex] = std::max(info.max[metricIndex], (float)match.uint64Value); + info.min[metricIndex] = std::min(info.min[metricIndex], (float)match.uint64Value); + // Save the timestamp for the current metric we do it here + // so that we do not update timestamps for broken metrics + info.timestamps[metricIndex][metricInfo.pos] = match.timestamp; + // Update the position where to write the next metric + metricInfo.pos = (metricInfo.pos + 1) % info.uint64Metrics[metricInfo.storeIdx].size(); + ++metricInfo.filledMetrics; + } break; + default: return false; break; }; - + // Note that we updated a given metric. + info.changed[metricIndex] = true; return true; } -/// @return the index in metrics for the information of given metric -size_t - DeviceMetricsHelper::metricIdxByName(const std::string& name, const DeviceMetricsInfo& info) +size_t DeviceMetricsHelper::metricIdxByName(const std::string& name, const DeviceMetricsInfo& info) { size_t i = 0; while (i < info.metricLabelsIdx.size()) { auto& metricName = info.metricLabelsIdx[i]; - if (metricName.label == name) { + // We check the size first and then the last character because that's + // likely to be different for multi-index metrics + if (metricName.size == name.size() && metricName.label[metricName.size - 1] == name[metricName.size - 1] && memcmp(metricName.label, name.c_str(), metricName.size) == 0) { return metricName.index; } ++i; @@ -256,6 +287,7 @@ std::ostream& operator<<(std::ostream& oss, MetricType const& val) oss << "string"; break; case MetricType::Int: + case MetricType::Uint64: oss << "float"; break; case MetricType::Unknown: @@ -266,5 +298,4 @@ std::ostream& operator<<(std::ostream& oss, MetricType const& val) return oss; } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/DeviceSpec.cxx b/Framework/Core/src/DeviceSpec.cxx index 0951548d6f95f..06f8f7d561a3f 100644 --- a/Framework/Core/src/DeviceSpec.cxx +++ b/Framework/Core/src/DeviceSpec.cxx @@ -35,16 +35,19 @@ size_t levenshteinDistance(const char* s, int len_s, const char* t, int len_t) size_t cost; /* base case: empty strings */ - if (len_s == 0) + if (len_s == 0) { return len_t; - if (len_t == 0) + } + if (len_t == 0) { return len_s; + } /* test if last characters of the strings match */ - if (s[len_s - 1] == t[len_t - 1]) + if (s[len_s - 1] == t[len_t - 1]) { cost = 0; - else + } else { cost = 1; + } return std::min(std::min(levenshteinDistance(s, len_s - 1, t, len_t) + 1, levenshteinDistance(s, len_s, t, len_t - 1) + 1), diff --git a/Framework/Core/src/DeviceSpecHelpers.cxx b/Framework/Core/src/DeviceSpecHelpers.cxx index be49cf0eeb670..89ed2328e2ca8 100644 --- a/Framework/Core/src/DeviceSpecHelpers.cxx +++ b/Framework/Core/src/DeviceSpecHelpers.cxx @@ -30,11 +30,17 @@ #include "Framework/WorkflowSpec.h" #include "Framework/ComputingResource.h" #include "Framework/Logger.h" +#include "Framework/RuntimeError.h" #include "WorkflowHelpers.h" #include <uv.h> #include <iostream> +#include <fmt/format.h> + +#include <sys/time.h> +#include <sys/resource.h> +#include <csignal> namespace bpo = boost::program_options; @@ -49,6 +55,11 @@ void timer_callback(uv_timer_t*) { // We simply wake up the event loop. Nothing to be done here. } + +void signal_callback(uv_signal_t*, int) +{ + // We simply wake up the event loop. Nothing to be done here. +} } // namespace detail struct ExpirationHandlerHelpers { @@ -74,6 +85,27 @@ struct ExpirationHandlerHelpers { }; } + static RouteConfigurator::CreationConfigurator signalDrivenConfigurator(InputSpec const& matcher, size_t inputTimeslice, size_t maxInputTimeslices) + { + return [matcher, inputTimeslice, maxInputTimeslices](DeviceState& state, ConfigParamRegistry const& options) { + std::string startName = std::string{"start-value-"} + matcher.binding; + std::string endName = std::string{"end-value-"} + matcher.binding; + std::string stepName = std::string{"step-value-"} + matcher.binding; + auto start = options.get<int64_t>(startName.c_str()); + auto stop = options.get<int64_t>(endName.c_str()); + auto step = options.get<int64_t>(stepName.c_str()); + // We create a timer to wake us up. Notice the actual + // timeslot creation and record expiration still happens + // in a synchronous way. + uv_signal_t* sh = (uv_signal_t*)(malloc(sizeof(uv_signal_t))); + uv_signal_init(state.loop, sh); + uv_signal_start(sh, detail::signal_callback, SIGUSR1); + state.activeSignals.push_back(sh); + + return LifetimeHelpers::enumDrivenCreation(start, stop, step, inputTimeslice, maxInputTimeslices); + }; + } + static RouteConfigurator::CreationConfigurator enumDrivenConfigurator(InputSpec const& matcher, size_t inputTimeslice, size_t maxInputTimeslices) { return [matcher, inputTimeslice, maxInputTimeslices](DeviceState&, ConfigParamRegistry const& options) { @@ -107,7 +139,7 @@ struct ExpirationHandlerHelpers { /// FIXME: seems convoluted... Maybe there is a way to avoid all this checking??? auto m = std::get_if<ConcreteDataMatcher>(&spec.matcher); if (m == nullptr) { - throw std::runtime_error("InputSpec for Conditions must be fully qualified"); + throw runtime_error("InputSpec for Conditions must be fully qualified"); } return [s = spec, matcher = *m, sourceChannel](DeviceState&, ConfigParamRegistry const& options) { @@ -148,7 +180,7 @@ struct ExpirationHandlerHelpers { { auto m = std::get_if<ConcreteDataMatcher>(&spec.matcher); if (m == nullptr) { - throw std::runtime_error("InputSpec for Timers must be fully qualified"); + throw runtime_error("InputSpec for Timers must be fully qualified"); } // We copy the matcher to avoid lifetime issues. return [matcher = *m, sourceChannel](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::enumerate(matcher, sourceChannel); }; @@ -158,7 +190,7 @@ struct ExpirationHandlerHelpers { { auto m = std::get_if<ConcreteDataMatcher>(&spec.matcher); if (m == nullptr) { - throw std::runtime_error("InputSpec for Enumeration must be fully qualified"); + throw runtime_error("InputSpec for Enumeration must be fully qualified"); } // We copy the matcher to avoid lifetime issues. return [matcher = *m, sourceChannel](DeviceState&, ConfigParamRegistry const&) { @@ -178,38 +210,62 @@ struct ExpirationHandlerHelpers { { return [](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::fetchFromObjectRegistry(); }; } + + /// This behaves as data. I.e. we never create it unless data arrives. + static RouteConfigurator::CreationConfigurator createOptionalConfigurator() + { + return [](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::dataDrivenCreation(); }; + } + + /// This will always exipire an optional record when no data is received. + static RouteConfigurator::DanglingConfigurator danglingOptionalConfigurator() + { + return [](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::expireAlways(); }; + } + + /// When the record expires, simply create a dummy entry. + static RouteConfigurator::ExpirationConfigurator expiringOptionalConfigurator(InputSpec const& spec, std::string const& sourceChannel) + { + try { + ConcreteDataMatcher concrete = DataSpecUtils::asConcreteDataMatcher(spec); + return [concrete, sourceChannel](DeviceState&, ConfigParamRegistry const&) { + return LifetimeHelpers::dummy(concrete, sourceChannel); + }; + } catch (...) { + ConcreteDataTypeMatcher dataType = DataSpecUtils::asConcreteDataTypeMatcher(spec); + ConcreteDataMatcher concrete{dataType.origin, dataType.description, 0xdeadbeef}; + return [concrete, sourceChannel](DeviceState&, ConfigParamRegistry const&) { + return LifetimeHelpers::dummy(concrete, sourceChannel); + }; + } + // We copy the matcher to avoid lifetime issues. + } }; /// This creates a string to configure channels of a FairMQDevice /// FIXME: support shared memory std::string DeviceSpecHelpers::inputChannel2String(const InputChannelSpec& channel) { - std::string result; - - if (!channel.name.empty()) { - result += "name=" + channel.name + ","; - } - result += std::string("type=") + ChannelSpecHelpers::typeAsString(channel.type); - result += std::string(",method=") + ChannelSpecHelpers::methodAsString(channel.method); - result += std::string(",address=") + ChannelSpecHelpers::channelUrl(channel); - result += std::string(",rateLogging=60"); - - return result; + return fmt::format("{}type={},method={},address={},rateLogging={},recvBufferSize={},sendBufferSize={}", + channel.name.empty() ? "" : "name=" + channel.name + ",", + ChannelSpecHelpers::typeAsString(channel.type), + ChannelSpecHelpers::methodAsString(channel.method), + ChannelSpecHelpers::channelUrl(channel), + channel.rateLogging, + channel.recvBufferSize, + channel.sendBufferSize); } std::string DeviceSpecHelpers::outputChannel2String(const OutputChannelSpec& channel) { - std::string result; - - if (!channel.name.empty()) { - result += "name=" + channel.name + ","; - } - result += std::string("type=") + ChannelSpecHelpers::typeAsString(channel.type); - result += std::string(",method=") + ChannelSpecHelpers::methodAsString(channel.method); - result += std::string(",address=") + ChannelSpecHelpers::channelUrl(channel); - result += std::string(",rateLogging=60"); - - return result; + return fmt::format("{}type={},method={},address={},rateLogging={},recvBufferSize={},sendBufferSize={}", + channel.name.empty() ? "" : "name=" + channel.name + ",", + ChannelSpecHelpers::typeAsString(channel.type), + ChannelSpecHelpers::methodAsString(channel.method), + ChannelSpecHelpers::channelUrl(channel), + channel.rateLogging, + channel.recvBufferSize, + channel.sendBufferSize); } void DeviceSpecHelpers::processOutEdgeActions(std::vector<DeviceSpec>& devices, @@ -221,6 +277,7 @@ void DeviceSpecHelpers::processOutEdgeActions(std::vector<DeviceSpec>& devices, const std::vector<EdgeAction>& actions, const WorkflowSpec& workflow, const std::vector<OutputSpec>& outputsMatchers, const std::vector<ChannelConfigurationPolicy>& channelPolicies, + std::string const& channelPrefix, ComputingOffer const& defaultOffer) { // The topology cannot be empty or not connected. If that is the case, than @@ -230,7 +287,9 @@ void DeviceSpecHelpers::processOutEdgeActions(std::vector<DeviceSpec>& devices, // Edges are navigated in order for each device, so the device associaited to // an edge is always the last one created. - auto deviceForEdge = [&actions, &workflow, &devices, &logicalEdges, &resourceManager, &defaultOffer](size_t ei, ComputingOffer& acceptedOffer) { + auto deviceForEdge = [&actions, &workflow, &devices, + &logicalEdges, &resourceManager, + &defaultOffer, &channelPrefix](size_t ei, ComputingOffer& acceptedOffer) { auto& edge = logicalEdges[ei]; auto& action = actions[ei]; @@ -262,6 +321,7 @@ void DeviceSpecHelpers::processOutEdgeActions(std::vector<DeviceSpec>& devices, DeviceSpec device; device.name = processor.name; device.id = processor.name; + device.channelPrefix = channelPrefix; if (processor.maxInputTimeslices != 1) { device.id = processor.name + "_t" + std::to_string(edge.producerTimeIndex); } @@ -287,7 +347,7 @@ void DeviceSpecHelpers::processOutEdgeActions(std::vector<DeviceSpec>& devices, if (consumer.maxInputTimeslices != 1) { consumerDeviceId += "_t" + std::to_string(edge.timeIndex); } - channel.name = "from_" + device.id + "_to_" + consumerDeviceId; + channel.name = device.channelPrefix + "from_" + device.id + "_to_" + consumerDeviceId; channel.port = acceptedOffer.startPort + acceptedOffer.rangeSize; channel.hostname = acceptedOffer.hostname; deviceResource.usedPorts += 1; @@ -387,7 +447,9 @@ void DeviceSpecHelpers::processOutEdgeActions(std::vector<DeviceSpec>& devices, } appendOutputRouteToSourceDeviceChannel(edge, device, channel); } - resourceManager.notifyAcceptedOffer(acceptedOffer); + if (std::string(acceptedOffer.hostname) != "") { + resourceManager.notifyAcceptedOffer(acceptedOffer); + } sortDeviceIndex(); } @@ -400,6 +462,7 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices, const std::vector<EdgeAction>& actions, const WorkflowSpec& workflow, std::vector<LogicalForwardInfo> const& availableForwardsInfo, std::vector<ChannelConfigurationPolicy> const& channelPolicies, + std::string const& channelPrefix, ComputingOffer const& defaultOffer) { auto const& constDeviceIndex = deviceIndex; @@ -440,7 +503,9 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices, return lastConsumerSearch->deviceIndex; }; - auto createNewDeviceForEdge = [&workflow, &logicalEdges, &devices, &deviceIndex, &resourceManager, &defaultOffer](size_t ei, ComputingOffer& acceptedOffer) { + auto createNewDeviceForEdge = [&workflow, &logicalEdges, &devices, + &deviceIndex, &resourceManager, &defaultOffer, + &channelPrefix](size_t ei, ComputingOffer& acceptedOffer) { auto& edge = logicalEdges[ei]; if (acceptedOffer.hostname != "") { @@ -467,6 +532,7 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices, DeviceSpec device; device.name = processor.name; device.id = processor.name; + device.channelPrefix = channelPrefix; if (processor.maxInputTimeslices != 1) { device.id += "_t" + std::to_string(edge.timeIndex); } @@ -519,7 +585,7 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices, auto const& producerDevice = devices[pi]; auto& consumerDevice = devices[ci]; InputChannelSpec channel; - channel.name = "from_" + producerDevice.id + "_to_" + consumerDevice.id; + channel.name = producerDevice.channelPrefix + "from_" + producerDevice.id + "_to_" + consumerDevice.id; channel.hostname = producerDevice.resource.hostname; channel.port = port; for (auto& policy : channelPolicies) { @@ -585,12 +651,24 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices, ExpirationHandlerHelpers::danglingEnumerationConfigurator(inputSpec), ExpirationHandlerHelpers::expiringEnumerationConfigurator(inputSpec, sourceChannel)}; break; + case Lifetime::Signal: + route.configurator = { + ExpirationHandlerHelpers::signalDrivenConfigurator(inputSpec, consumerDevice.inputTimesliceId, consumerDevice.maxInputTimeslices), + ExpirationHandlerHelpers::danglingEnumerationConfigurator(inputSpec), + ExpirationHandlerHelpers::expiringEnumerationConfigurator(inputSpec, sourceChannel)}; + break; case Lifetime::Transient: route.configurator = { ExpirationHandlerHelpers::dataDrivenConfigurator(), ExpirationHandlerHelpers::danglingTransientConfigurator(), ExpirationHandlerHelpers::expiringTransientConfigurator(inputSpec)}; break; + case Lifetime::Optional: + route.configurator = { + ExpirationHandlerHelpers::createOptionalConfigurator(), + ExpirationHandlerHelpers::danglingOptionalConfigurator(), + ExpirationHandlerHelpers::expiringOptionalConfigurator(inputSpec, sourceChannel)}; + break; default: break; } @@ -640,19 +718,24 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices, } appendInputRouteToDestDeviceChannel(edge, consumerDevice, channel); } - resourceManager.notifyAcceptedOffer(acceptedOffer); + if (acceptedOffer.hostname != "") { + resourceManager.notifyAcceptedOffer(acceptedOffer); + } } // Construct the list of actual devices we want, given a workflow. // // FIXME: make start port configurable? -void DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(WorkflowSpec const& workflow, +void DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(const WorkflowSpec& workflow, std::vector<ChannelConfigurationPolicy> const& channelPolicies, std::vector<CompletionPolicy> const& completionPolicies, std::vector<DispatchPolicy> const& dispatchPolicies, std::vector<DeviceSpec>& devices, ResourceManager& resourceManager, - std::string const& uniqueWorkflowId) + std::string const& uniqueWorkflowId, + bool optimizeTopology, + unsigned short resourcesMonitoringInterval, + std::string const& channelPrefix) { std::vector<LogicalForwardInfo> availableForwardsInfo; @@ -665,7 +748,6 @@ void DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(WorkflowSpec const& workf // them before assigning to a device. std::vector<OutputSpec> outputs; - WorkflowHelpers::verifyWorkflow(workflow); WorkflowHelpers::constructGraph(workflow, logicalEdges, outputs, availableForwardsInfo); // We need to instanciate one device per (me, timeIndex) in the @@ -705,13 +787,13 @@ void DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(WorkflowSpec const& workf defaultOffer.memory /= deviceCount + 1; processOutEdgeActions(devices, deviceIndex, connections, resourceManager, outEdgeIndex, logicalEdges, - outActions, workflow, outputs, channelPolicies, defaultOffer); + outActions, workflow, outputs, channelPolicies, channelPrefix, defaultOffer); // FIXME: is this not the case??? std::sort(connections.begin(), connections.end()); processInEdgeActions(devices, deviceIndex, connections, resourceManager, inEdgeIndex, logicalEdges, - inActions, workflow, availableForwardsInfo, channelPolicies, defaultOffer); + inActions, workflow, availableForwardsInfo, channelPolicies, channelPrefix, defaultOffer); // We apply the completion policies here since this is where we have all the // devices resolved. for (auto& device : devices) { @@ -729,6 +811,10 @@ void DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(WorkflowSpec const& workf } } + for (auto& device : devices) { + device.resourceMonitoringInterval = resourcesMonitoringInterval; + } + auto findDeviceIndex = [&deviceIndex](size_t processorIndex, size_t timeslice) { for (auto& deviceEdge : deviceIndex) { if (deviceEdge.processorIndex != processorIndex) { @@ -739,25 +825,27 @@ void DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(WorkflowSpec const& workf } return deviceEdge.deviceIndex; } - throw std::runtime_error("Unable to find device."); + throw runtime_error("Unable to find device."); }; // Optimize the topology when two devices are // running on the same node. - for (auto& connection : connections) { - auto& device1 = devices[findDeviceIndex(connection.consumer, connection.timeIndex)]; - auto& device2 = devices[findDeviceIndex(connection.producer, connection.producerTimeIndex)]; - // No need to do anything if they are not on the same host - if (device1.resource.hostname != device2.resource.hostname) { - continue; - } - for (auto& input : device1.inputChannels) { - for (auto& output : device2.outputChannels) { - if (input.hostname == output.hostname && input.port == output.port) { - input.protocol = ChannelProtocol::IPC; - output.protocol = ChannelProtocol::IPC; - input.hostname += uniqueWorkflowId; - output.hostname += uniqueWorkflowId; + if (optimizeTopology) { + for (auto& connection : connections) { + auto& device1 = devices[findDeviceIndex(connection.consumer, connection.timeIndex)]; + auto& device2 = devices[findDeviceIndex(connection.producer, connection.producerTimeIndex)]; + // No need to do anything if they are not on the same host + if (device1.resource.hostname != device2.resource.hostname) { + continue; + } + for (auto& input : device1.inputChannels) { + for (auto& output : device2.outputChannels) { + if (input.hostname == output.hostname && input.port == output.port) { + input.protocol = ChannelProtocol::IPC; + output.protocol = ChannelProtocol::IPC; + input.hostname += uniqueWorkflowId; + output.hostname += uniqueWorkflowId; + } } } } @@ -774,7 +862,7 @@ void DeviceSpecHelpers::reworkShmSegmentSize(std::vector<DataProcessorInfo>& inf } auto value = it + 1; if (value == info.cmdLineArgs.end()) { - throw std::runtime_error("--shm-segment-size requires an argument"); + throw runtime_error("--shm-segment-size requires an argument"); } char* err = nullptr; int64_t size = strtoll(value->c_str(), &err, 10); @@ -783,6 +871,14 @@ void DeviceSpecHelpers::reworkShmSegmentSize(std::vector<DataProcessorInfo>& inf } info.cmdLineArgs.erase(it, it + 2); } + /// If no segment size is set, make it max VSIZE - 1GB or 90% max VSIZE. + if (segmentSize == 0) { + struct rlimit limits; + getrlimit(RLIMIT_AS, &limits); + if (limits.rlim_cur != RLIM_INFINITY) { + segmentSize = std::min(limits.rlim_cur - 1000000000LL, (limits.rlim_cur * 90LL) / 100LL); + } + } if (segmentSize == 0) { segmentSize = 2000000000LL; } @@ -792,6 +888,18 @@ void DeviceSpecHelpers::reworkShmSegmentSize(std::vector<DataProcessorInfo>& inf } } +namespace +{ +template <class Container> +void split(const std::string& str, Container& cont) +{ + std::istringstream iss(str); + std::copy(std::istream_iterator<std::string>(iss), + std::istream_iterator<std::string>(), + std::back_inserter(cont)); +} +} // namespace + void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, std::vector<DataProcessorInfo> const& processorInfos, std::vector<DeviceSpec> const& deviceSpecs, @@ -852,6 +960,7 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, "--shm-monitor", "false", "--log-color", "false", "--color", "false"}; + std::vector<std::string> tmpEnv; if (defaultStopped) { tmpArgs.push_back("-s"); } @@ -877,12 +986,20 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, // if found in the argument list. If not found they will be added with the default value FilterFunctionT filterArgsFct = [&](int largc, char** largv, const bpo::options_description& odesc) { // spec contains options + using namespace bpo::command_line_style; + auto style = (allow_short | short_allow_adjacent | short_allow_next | allow_long | long_allow_adjacent | long_allow_next | allow_sticky | allow_dash_for_short); + bpo::command_line_parser parser{largc, largv}; parser.options(odesc).allow_unregistered(); + parser.style(style); bpo::parsed_options parsed_options = parser.run(); bpo::variables_map varmap; bpo::store(parsed_options, varmap); + if (varmap.count("environment")) { + auto environment = varmap["environment"].as<std::string>(); + split(environment, tmpEnv); + } // options can be grouped per processor spec, the group is entered by // the option created from the actual processor spec name @@ -899,8 +1016,15 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, realOdesc.add_options()("severity", bpo::value<std::string>()); realOdesc.add_options()("child-driver", bpo::value<std::string>()); realOdesc.add_options()("rate", bpo::value<std::string>()); + realOdesc.add_options()("environment", bpo::value<std::string>()); + realOdesc.add_options()("post-fork-command", bpo::value<std::string>()); realOdesc.add_options()("shm-segment-size", bpo::value<std::string>()); + realOdesc.add_options()("shm-mlock-segment", bpo::value<std::string>()); + realOdesc.add_options()("shm-zero-segment", bpo::value<std::string>()); + realOdesc.add_options()("shm-throw-bad-alloc", bpo::value<std::string>()); + realOdesc.add_options()("shm-segment-id", bpo::value<std::string>()); realOdesc.add_options()("shm-monitor", bpo::value<std::string>()); + realOdesc.add_options()("channel-prefix", bpo::value<std::string>()); realOdesc.add_options()("session", bpo::value<std::string>()); filterArgsFct(expansions.we_wordc, expansions.we_wordv, realOdesc); wordfree(&expansions); @@ -968,6 +1092,11 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, tmpArgs.emplace_back("dpl_" + uniqueWorkflowId); } + if (spec.resourceMonitoringInterval > 0) { + tmpArgs.emplace_back(std::string("--resources-monitoring")); + tmpArgs.emplace_back(std::to_string(spec.resourceMonitoringInterval)); + } + // We create the final option list, depending on the channels // which are present in a device. for (auto& arg : tmpArgs) { @@ -976,6 +1105,10 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, // execvp wants a NULL terminated list. execution.args.push_back(nullptr); + for (auto& env : tmpEnv) { + execution.environ.emplace_back(strdup(env.c_str())); + } + // FIXME: this should probably be reflected in the GUI std::ostringstream str; for (size_t ai = 0; ai < execution.args.size() - 1; ai++) { @@ -995,20 +1128,27 @@ boost::program_options::options_description DeviceSpecHelpers::getForwardedDevic // - rate is an option of FairMQ device for ConditionalRun // - child-driver is not a FairMQ device option but used per device to start to process bpo::options_description forwardedDeviceOptions; - forwardedDeviceOptions.add_options() // - ("severity", bpo::value<std::string>()->default_value("info"), "severity level of the log") // - ("plugin,P", bpo::value<std::string>(), "FairMQ plugin list") // - ("plugin-search-path,S", bpo::value<std::string>(), "FairMQ plugins search path") // - ("control-port", bpo::value<std::string>(), "Utility port to be used by O2 Control") // - ("rate", bpo::value<std::string>(), "rate for a data source device (Hz)") // - ("shm-monitor", bpo::value<std::string>(), "whether to use the shared memory monitor") // - ("shm-segment-size", bpo::value<std::string>(), "size of the shared memory segment in bytes") // - ("session", bpo::value<std::string>(), "unique label for the shared memory session") // - ("configuration,cfg", bpo::value<std::string>(), "configuration connection string") // - ("monitoring-backend", bpo::value<std::string>(), "monitoring connection string") // - ("infologger-mode", bpo::value<std::string>(), "INFOLOGGER_MODE override") // - ("infologger-severity", bpo::value<std::string>(), "minimun FairLogger severity which goes to info logger") // - ("child-driver", bpo::value<std::string>(), "external driver to start childs with (e.g. valgrind)"); // + forwardedDeviceOptions.add_options() // + ("severity", bpo::value<std::string>()->default_value("info"), "severity level of the log") // + ("plugin,P", bpo::value<std::string>(), "FairMQ plugin list") // + ("plugin-search-path,S", bpo::value<std::string>(), "FairMQ plugins search path") // + ("control-port", bpo::value<std::string>(), "Utility port to be used by O2 Control") // + ("rate", bpo::value<std::string>(), "rate for a data source device (Hz)") // + ("shm-monitor", bpo::value<std::string>(), "whether to use the shared memory monitor") // + ("channel-prefix", bpo::value<std::string>()->default_value(""), "prefix to use for multiplexing multiple workflows in the same session") // + ("shm-segment-size", bpo::value<std::string>(), "size of the shared memory segment in bytes") // + ("shm-mlock-segment", bpo::value<std::string>()->default_value("false"), "mlock shared memory segment") // + ("shm-zero-segment", bpo::value<std::string>()->default_value("false"), "zero shared memory segment") // + ("shm-throw-bad-alloc", bpo::value<std::string>()->default_value("true"), "throw if insufficient shm memory") // + ("shm-segment-id", bpo::value<std::string>()->default_value("0"), "shm segment id") // + ("environment", bpo::value<std::string>(), "comma separated list of environment variables to set for the device") // + ("post-fork-command", bpo::value<std::string>(), "post fork command to execute (e.g. numactl {pid}") // + ("session", bpo::value<std::string>(), "unique label for the shared memory session") // + ("configuration,cfg", bpo::value<std::string>(), "configuration connection string") // + ("monitoring-backend", bpo::value<std::string>(), "monitoring connection string") // + ("infologger-mode", bpo::value<std::string>(), "INFOLOGGER_MODE override") // + ("infologger-severity", bpo::value<std::string>(), "minimun FairLogger severity which goes to info logger") // + ("child-driver", bpo::value<std::string>(), "external driver to start childs with (e.g. valgrind)"); // return forwardedDeviceOptions; } diff --git a/Framework/Core/src/DeviceSpecHelpers.h b/Framework/Core/src/DeviceSpecHelpers.h index a17da50053910..244f6ddab3c1a 100644 --- a/Framework/Core/src/DeviceSpecHelpers.h +++ b/Framework/Core/src/DeviceSpecHelpers.h @@ -22,8 +22,8 @@ #include "Framework/AlgorithmSpec.h" #include "Framework/ConfigParamSpec.h" #include "Framework/OutputRoute.h" +#include "Framework/DataProcessorInfo.h" #include "ResourceManager.h" -#include "DataProcessorInfo.h" #include "WorkflowHelpers.h" #include <boost/program_options.hpp> @@ -48,7 +48,10 @@ struct DeviceSpecHelpers { std::vector<DispatchPolicy> const& dispatchPolicies, std::vector<DeviceSpec>& devices, ResourceManager& resourceManager, - std::string const& uniqueWorkflowId); + std::string const& uniqueWorkflowId, + bool optimizeTopology = false, + unsigned short resourcesMonitoringInterval = 0, + std::string const& channelPrefix = ""); static void dataProcessorSpecs2DeviceSpecs( const WorkflowSpec& workflow, @@ -56,10 +59,14 @@ struct DeviceSpecHelpers { std::vector<CompletionPolicy> const& completionPolicies, std::vector<DeviceSpec>& devices, ResourceManager& resourceManager, - std::string const& uniqueWorkflowId) + std::string const& uniqueWorkflowId, + bool optimizeTopology = false, + unsigned short resourcesMonitoringInterval = 0, + std::string const& channelPrefix = "") { std::vector<DispatchPolicy> dispatchPolicies = DispatchPolicy::createDefaultPolicies(); - dataProcessorSpecs2DeviceSpecs(workflow, channelPolicies, completionPolicies, dispatchPolicies, devices, resourceManager, uniqueWorkflowId); + dataProcessorSpecs2DeviceSpecs(workflow, channelPolicies, completionPolicies, + dispatchPolicies, devices, resourceManager, uniqueWorkflowId, optimizeTopology, resourcesMonitoringInterval, channelPrefix); } /// Helper to provide the channel configuration string for an input channel @@ -97,6 +104,7 @@ struct DeviceSpecHelpers { const WorkflowSpec& workflow, const std::vector<OutputSpec>& outputs, std::vector<ChannelConfigurationPolicy> const& channelPolicies, + std::string const& channelPrefix, ComputingOffer const& defaultOffer); /// This takes the list of preprocessed edges of a graph @@ -114,6 +122,7 @@ struct DeviceSpecHelpers { const WorkflowSpec& workflow, const std::vector<LogicalForwardInfo>& availableForwardsInfo, std::vector<ChannelConfigurationPolicy> const& channelPolicies, + std::string const& channelPrefix, ComputingOffer const& defaultOffer); /// return a description of all options to be forwarded to the device diff --git a/Framework/Core/src/Dispatcher.cxx b/Framework/Core/src/Dispatcher.cxx deleted file mode 100644 index e364a0fa73412..0000000000000 --- a/Framework/Core/src/Dispatcher.cxx +++ /dev/null @@ -1,226 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Dispatcher.cxx -/// \brief Implementation of Dispatcher for O2 Data Sampling -/// -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#include "Framework/Dispatcher.h" -#include "Framework/RawDeviceService.h" -#include "Framework/DataSamplingPolicy.h" -#include "Framework/DataSamplingHeader.h" -#include "Framework/DataProcessingHeader.h" -#include "Framework/DataSpecUtils.h" -#include "Framework/Logger.h" -#include "Framework/ConfigParamRegistry.h" - -#include <Monitoring/Monitoring.h> -#include <Configuration/ConfigurationInterface.h> -#include <Configuration/ConfigurationFactory.h> -#include <fairmq/FairMQDevice.h> - -using namespace o2::configuration; -using namespace o2::monitoring; - -namespace o2 -{ -namespace framework -{ - -Dispatcher::Dispatcher(std::string name, const std::string reconfigurationSource) - : mName(name), mReconfigurationSource(reconfigurationSource) -{ - header::DataDescription timerDescription; - timerDescription.runtimeInit(("TIMER-" + name).substr(0, 16).c_str()); - inputs.emplace_back(InputSpec{"timer-stats", "DS", timerDescription, 0, Lifetime::Timer}); -} - -Dispatcher::~Dispatcher() = default; - -void Dispatcher::init(InitContext& ctx) -{ - LOG(DEBUG) << "Reading Data Sampling Policies..."; - - boost::property_tree::ptree policiesTree; - - if (mReconfigurationSource.empty() == false) { - std::unique_ptr<ConfigurationInterface> cfg = ConfigurationFactory::getConfiguration(mReconfigurationSource); - policiesTree = cfg->getRecursive("dataSamplingPolicies"); - mPolicies.clear(); - } else { - policiesTree = ctx.options().get<boost::property_tree::ptree>("sampling-config-ptree"); - mPolicies.clear(); - } - - for (auto&& policyConfig : policiesTree) { - // we don't want the Dispatcher to exit due to one faulty Policy - try { - mPolicies.emplace_back(std::make_shared<DataSamplingPolicy>(policyConfig.second)); - } catch (std::exception& ex) { - LOG(WARN) << "Could not load the Data Sampling Policy '" - << policyConfig.second.get_optional<std::string>("id").value_or("") << "', because: " << ex.what(); - } catch (...) { - LOG(WARN) << "Could not load the Data Sampling Policy '" - << policyConfig.second.get_optional<std::string>("id").value_or("") << "'"; - } - } -} - -void Dispatcher::run(ProcessingContext& ctx) -{ - for (const auto& input : ctx.inputs()) { - if (input.header != nullptr && input.spec != nullptr) { - const auto* inputHeader = header::get<header::DataHeader*>(input.header); - ConcreteDataMatcher inputMatcher{inputHeader->dataOrigin, inputHeader->dataDescription, inputHeader->subSpecification}; - - for (auto& policy : mPolicies) { - // todo: consider getting the outputSpec in match to improve performance - // todo: consider matching (and deciding) in completion policy to save some time - - if (policy->match(inputMatcher) && policy->decide(input)) { - // We copy every header which is not DataHeader or DataProcessingHeader, - // so that custom data-dependent headers are passed forward, - // and we add a DataSamplingHeader. - header::Stack headerStack{ - std::move(extractAdditionalHeaders(input.header)), - std::move(prepareDataSamplingHeader(*policy.get(), ctx.services().get<const DeviceSpec>()))}; - - if (!policy->getFairMQOutputChannel().empty()) { - sendFairMQ(ctx.services().get<RawDeviceService>().device(), input, policy->getFairMQOutputChannelName(), - std::move(headerStack)); - } else { - Output output = policy->prepareOutput(inputMatcher, input.spec->lifetime); - output.metaHeader = std::move(header::Stack{std::move(output.metaHeader), std::move(headerStack)}); - send(ctx.outputs(), input, std::move(output)); - } - } - } - } - } - - if (ctx.inputs().isValid("timer-stats")) { - reportStats(ctx.services().get<Monitoring>()); - } -} - -void Dispatcher::reportStats(Monitoring& monitoring) const -{ - uint64_t dispatcherTotalEvaluatedMessages = 0; - uint64_t dispatcherTotalAcceptedMessages = 0; - - for (const auto& policy : mPolicies) { - dispatcherTotalEvaluatedMessages += policy->getTotalEvaluatedMessages(); - dispatcherTotalAcceptedMessages += policy->getTotalAcceptedMessages(); - } - - monitoring.send({dispatcherTotalEvaluatedMessages, "Dispatcher_messages_evaluated"}); - monitoring.send({dispatcherTotalAcceptedMessages, "Dispatcher_messages_passed"}); -} - -DataSamplingHeader Dispatcher::prepareDataSamplingHeader(const DataSamplingPolicy& policy, const DeviceSpec& spec) -{ - uint64_t sampleTime = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count()); - - DataSamplingHeader::DeviceIDType id; - id.runtimeInit(spec.id.substr(0, DataSamplingHeader::deviceIDTypeSize).c_str()); - - return { - sampleTime, - policy.getTotalAcceptedMessages(), - policy.getTotalEvaluatedMessages(), - id}; -} - -header::Stack Dispatcher::extractAdditionalHeaders(const char* inputHeaderStack) const -{ - header::Stack headerStack; - - const auto* first = header::BaseHeader::get(reinterpret_cast<const byte*>(inputHeaderStack)); - for (const auto* current = first; current != nullptr; current = current->next()) { - if (current->description != header::DataHeader::sHeaderType && - current->description != DataProcessingHeader::sHeaderType) { - headerStack = std::move(header::Stack{std::move(headerStack), *current}); - } - } - - return headerStack; -} - -void Dispatcher::send(DataAllocator& dataAllocator, const DataRef& inputData, Output&& output) const -{ - const auto* inputHeader = header::get<header::DataHeader*>(inputData.header); - dataAllocator.snapshot(output, inputData.payload, inputHeader->payloadSize, inputHeader->payloadSerializationMethod); -} - -// ideally this should be in a separate proxy device or use Lifetime::External -void Dispatcher::sendFairMQ(FairMQDevice* device, const DataRef& inputData, const std::string& fairMQChannel, - header::Stack&& stack) const -{ - const auto* dh = header::get<header::DataHeader*>(inputData.header); - assert(dh); - const auto* dph = header::get<DataProcessingHeader*>(inputData.header); - assert(dph); - - header::DataHeader dhout{dh->dataDescription, dh->dataOrigin, dh->subSpecification, dh->payloadSize}; - dhout.payloadSerializationMethod = dh->payloadSerializationMethod; - DataProcessingHeader dphout{dph->startTime, dph->duration}; - o2::header::Stack headerStack{dhout, dphout, stack}; - - auto channelAlloc = o2::pmr::getTransportAllocator(device->Transport()); - FairMQMessagePtr msgHeaderStack = o2::pmr::getMessage(std::move(headerStack), channelAlloc); - - char* payloadCopy = new char[dh->payloadSize]; - memcpy(payloadCopy, inputData.payload, dh->payloadSize); - auto cleanupFcn = [](void* data, void*) { delete[] reinterpret_cast<char*>(data); }; - FairMQMessagePtr msgPayload(device->NewMessage(payloadCopy, dh->payloadSize, cleanupFcn, payloadCopy)); - - FairMQParts message; - message.AddPart(move(msgHeaderStack)); - message.AddPart(move(msgPayload)); - - int64_t bytesSent = device->Send(message, fairMQChannel); -} - -void Dispatcher::registerPath(const std::pair<InputSpec, OutputSpec>& path) -{ - //todo: take care of inputs inclusive in others, when subSpec matchers are supported - auto cmp = [a = path.first](const InputSpec b) { - return a.matcher == b.matcher && a.lifetime == b.lifetime; - }; - - if (std::find_if(inputs.begin(), inputs.end(), cmp) == inputs.end()) { - inputs.push_back(path.first); - LOG(DEBUG) << "Registering input " << DataSpecUtils::describe(path.first); - } else { - LOG(DEBUG) << "Input " << DataSpecUtils::describe(path.first) - << " already registered"; - } - - outputs.push_back(path.second); -} - -const std::string& Dispatcher::getName() -{ - return mName; -} - -Inputs Dispatcher::getInputSpecs() -{ - return inputs; -} - -Outputs Dispatcher::getOutputSpecs() -{ - return outputs; -} - -} // namespace framework -} // namespace o2 diff --git a/Framework/Core/src/DriverControl.cxx b/Framework/Core/src/DriverControl.cxx index e36ec3c49c0c4..9ff709f28c8a7 100644 --- a/Framework/Core/src/DriverControl.cxx +++ b/Framework/Core/src/DriverControl.cxx @@ -7,4 +7,4 @@ // In applying this license CERN does 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 "DriverControl.h" +#include "Framework/DriverControl.h" diff --git a/Framework/Core/src/DriverInfo.cxx b/Framework/Core/src/DriverInfo.cxx index 8df33b8452a83..37989f6df0ee7 100644 --- a/Framework/Core/src/DriverInfo.cxx +++ b/Framework/Core/src/DriverInfo.cxx @@ -7,4 +7,23 @@ // In applying this license CERN does 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 "DriverInfo.h" +#include "Framework/DriverInfo.h" + +char const* o2::framework::DriverInfoHelper::stateToString(enum DriverState state) +{ + static const char* names[static_cast<int>(DriverState::LAST)] = { + "INIT", // + "SCHEDULE", // + "RUNNING", // + "REDEPLOY_GUI", // + "QUIT_REQUESTED", // + "HANDLE_CHILDREN", // + "EXIT", // + "UNKNOWN", // + "PERFORM_CALLBACKS", // + "MATERIALISE_WORKFLOW", // + "IMPORT_CURRENT_WORKFLOW", // + "DO_CHILD" // + }; + return names[static_cast<int>(state)]; +} diff --git a/Framework/Core/src/ExpressionHelpers.h b/Framework/Core/src/ExpressionHelpers.h index 44bf582808323..2d3f9cb704029 100644 --- a/Framework/Core/src/ExpressionHelpers.h +++ b/Framework/Core/src/ExpressionHelpers.h @@ -32,11 +32,18 @@ static std::array<std::string, BasicOp::Abs + 1> binaryOperationsMap = { "greater_than_or_equal_to", "equal", "not_equal", - "power", - "exp", - "log", - "log10", - "abs"}; + "powerf", + "sqrtf", + "expf", + "logf", + "log10f", + "sinf", + "cosf", + "tanf", + "asinf", + "acosf", + "atanf", + "absf"}; struct DatumSpec { /// datum spec either contains an index, a value of a literal or a binding label @@ -90,24 +97,13 @@ struct ColumnOperationSpec { } }; -template <typename... C> -std::shared_ptr<gandiva::Projector> createProjectors(framework::pack<C...>, gandiva::SchemaPtr schema) -{ - std::shared_ptr<gandiva::Projector> projector; - auto s = gandiva::Projector::Make( - schema, - {makeExpression( - framework::expressions::createExpressionTree( - framework::expressions::createOperations(C::Projector()), - schema), - C::asArrowField())...}, - &projector); - if (s.ok()) { - return projector; - } else { - throw std::runtime_error(fmt::format("Failed to create projector: {}", s.ToString())); - } -} +/// helper struct used to parse trees +struct NodeRecord { + /// pointer to the actual tree node + Node* node_ptr = nullptr; + size_t index = 0; + explicit NodeRecord(Node* node_, size_t index_) : node_ptr(node_), index{index_} {} +}; } // namespace o2::framework::expressions #endif // O2_FRAMEWORK_EXPRESSIONS_HELPERS_H_ diff --git a/Framework/Core/src/Expressions.cxx b/Framework/Core/src/Expressions.cxx index 5204caab15a52..60323a1305d95 100644 --- a/Framework/Core/src/Expressions.cxx +++ b/Framework/Core/src/Expressions.cxx @@ -11,6 +11,7 @@ #include "../src/ExpressionHelpers.h" #include "Framework/VariantHelpers.h" #include "Framework/Logger.h" +#include "Framework/RuntimeError.h" #include "gandiva/tree_expr_builder.h" #include "arrow/table.h" #include "fmt/format.h" @@ -46,6 +47,13 @@ struct OpNodeHelper { return ColumnOperationSpec{node.op}; } }; + +struct PlaceholderNodeHelper { + DatumSpec operator()(PlaceholderNode node) const + { + return DatumSpec{node.value, node.type}; + } +}; } // namespace std::shared_ptr<arrow::DataType> concreteArrowType(atype::type type) @@ -59,6 +67,8 @@ std::shared_ptr<arrow::DataType> concreteArrowType(atype::type type) return arrow::int16(); case atype::INT32: return arrow::int32(); + case atype::INT64: + return arrow::int64(); case atype::FLOAT: return arrow::float32(); case atype::DOUBLE: @@ -82,7 +92,7 @@ std::string upcastTo(atype::type f) case atype::DOUBLE: return "castFLOAT8"; default: - throw std::runtime_error(fmt::format("Do not know how to cast to {}", f)); + throw runtime_error_f("Do not know how to cast to %d", f); } } @@ -107,16 +117,47 @@ std::ostream& operator<<(std::ostream& os, DatumSpec const& spec) return os; } -namespace +void updatePlaceholders(Filter& filter, InitContext& context) { -/// helper struct used to parse trees -struct NodeRecord { - /// pointer to the actual tree node - Node* node_ptr = nullptr; - size_t index = 0; - explicit NodeRecord(Node* node_, size_t index_) : node_ptr(node_), index{index_} {} -}; -} // namespace + std::stack<NodeRecord> path; + + // insert the top node into stack + path.emplace(filter.node.get(), 0); + + auto updateNode = [&](Node* node) { + if (node->self.index() == 3) { + std::get_if<3>(&node->self)->reset(context); + } + }; + + auto isLeaf = [](Node const* const node) { + return ((node->left == nullptr) && (node->right == nullptr)); + }; + + // while the stack is not empty + while (path.empty() == false) { + auto& top = path.top(); + + updateNode(top.node_ptr); + + path.pop(); + + if (top.node_ptr->left != nullptr) { + if (isLeaf(top.node_ptr->left.get())) { + updateNode(top.node_ptr->left.get()); + } else { + path.emplace(top.node_ptr->left.get(), 0); + } + } + if (top.node_ptr->right != nullptr) { + if (isLeaf(top.node_ptr->right.get())) { + updateNode(top.node_ptr->right.get()); + } else { + path.emplace(top.node_ptr->right.get(), 0); + } + } + } +} Operations createOperations(Filter const& expression) { @@ -131,6 +172,7 @@ Operations createOperations(Filter const& expression) overloaded{ [lh = LiteralNodeHelper{}](LiteralNode node) { return lh(node); }, [bh = BindingNodeHelper{}](BindingNode node) { return bh(node); }, + [ph = PlaceholderNodeHelper{}](PlaceholderNode node) { return ph(node); }, [](auto&&) { return DatumSpec{}; }}, node->self); }; @@ -165,11 +207,13 @@ Operations createOperations(Filter const& expression) } decltype(left) right = nullptr; - if (top.node_ptr->right != nullptr) + if (top.node_ptr->right != nullptr) { right = top.node_ptr->right.get(); + } bool rightLeaf = true; - if (right != nullptr) + if (right != nullptr) { rightLeaf = isLeaf(right); + } size_t ri = 0; auto isUnary = false; if (top.node_ptr->right == nullptr) { @@ -185,10 +229,12 @@ Operations createOperations(Filter const& expression) } OperationSpecs.push_back(std::move(operationSpec)); - if (!leftLeaf) + if (!leftLeaf) { path.emplace(left, li); - if (!isUnary && !rightLeaf) + } + if (!isUnary && !rightLeaf) { path.emplace(right, ri); + } } // at this stage the operations vector is created, but the field types are // only set for the logical operations and leaf nodes @@ -198,22 +244,25 @@ Operations createOperations(Filter const& expression) auto inferResultType = [&resultTypes](DatumSpec& left, DatumSpec& right) { // if the left datum is monostate (error) if (left.datum.index() == 0) { - throw std::runtime_error("Malformed operation spec: empty left datum"); + throw runtime_error("Malformed operation spec: empty left datum"); } // check if the datums are references - if (left.datum.index() == 1) + if (left.datum.index() == 1) { left.type = resultTypes[std::get<size_t>(left.datum)]; + } - if (right.datum.index() == 1) + if (right.datum.index() == 1) { right.type = resultTypes[std::get<size_t>(right.datum)]; + } auto t1 = left.type; auto t2 = right.type; // if the right datum is monostate (unary op) if (right.datum.index() == 0) { - if (t1 == atype::DOUBLE) + if (t1 == atype::DOUBLE) { return atype::DOUBLE; + } return atype::FLOAT; } @@ -221,24 +270,29 @@ Operations createOperations(Filter const& expression) return t1; } - if (t1 == atype::INT32 || t1 == atype::INT8 || t1 == atype::INT16 || t1 == atype::UINT8) { - if (t2 == atype::INT32 || t2 == atype::INT8 || t2 == atype::INT16 || t2 == atype::UINT8) + if (t1 == atype::INT32 || t1 == atype::INT8 || t1 == atype::INT16 || t1 == atype::INT64 || t1 == atype::UINT8) { + if (t2 == atype::INT32 || t2 == atype::INT8 || t2 == atype::INT16 || t2 == atype::INT64 || t2 == atype::UINT8) { return atype::FLOAT; - if (t2 == atype::FLOAT) + } + if (t2 == atype::FLOAT) { return atype::FLOAT; - if (t2 == atype::DOUBLE) + } + if (t2 == atype::DOUBLE) { return atype::DOUBLE; + } } if (t1 == atype::FLOAT) { - if (t2 == atype::INT32 || t2 == atype::INT8 || t2 == atype::INT16 || t2 == atype::UINT8) + if (t2 == atype::INT32 || t2 == atype::INT8 || t2 == atype::INT16 || t2 == atype::INT64 || t2 == atype::UINT8) { return atype::FLOAT; - if (t2 == atype::DOUBLE) + } + if (t2 == atype::DOUBLE) { return atype::DOUBLE; + } } if (t1 == atype::DOUBLE) { return atype::DOUBLE; } - throw std::runtime_error(fmt::format("Invalid combination of argument types {} and {}", t1, t2)); + throw runtime_error_f("Invalid combination of argument types %d and %d", t1, t2); }; for (auto it = OperationSpecs.rbegin(); it != OperationSpecs.rend(); ++it) { @@ -270,11 +324,10 @@ std::shared_ptr<gandiva::Filter> auto s = gandiva::Filter::Make(Schema, makeCondition(createExpressionTree(opSpecs, Schema)), &filter); - if (s.ok()) { - return filter; - } else { - throw std::runtime_error(fmt::format("Failed to create filter: {}", s.ToString())); + if (!s.ok()) { + throw runtime_error_f("Failed to create filter: %s", s.ToString().c_str()); } + return filter; } std::shared_ptr<gandiva::Filter> @@ -284,11 +337,10 @@ std::shared_ptr<gandiva::Filter> auto s = gandiva::Filter::Make(Schema, condition, &filter); - if (s.ok()) { - return filter; - } else { - throw std::runtime_error(fmt::format("Failed to create filter: {}", s.ToString())); + if (!s.ok()) { + throw runtime_error_f("Failed to create filter: %s", s.ToString().c_str()); } + return filter; } std::shared_ptr<gandiva::Projector> @@ -298,11 +350,10 @@ std::shared_ptr<gandiva::Projector> auto s = gandiva::Projector::Make(Schema, {makeExpression(createExpressionTree(opSpecs, Schema), result)}, &projector); - if (s.ok()) { - return projector; - } else { - throw std::runtime_error(fmt::format("Failed to create projector: {}", s.ToString())); + if (!s.ok()) { + throw runtime_error_f("Failed to create projector: %s", s.ToString().c_str()); } + return projector; } std::shared_ptr<gandiva::Projector> @@ -318,21 +369,24 @@ Selection createSelection(std::shared_ptr<arrow::Table> table, std::shared_ptr<g arrow::default_memory_pool(), &selection); if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot allocate selection vector {}", s.ToString())); + throw runtime_error_f("Cannot allocate selection vector %s", s.ToString().c_str()); + } + if (table->num_rows() == 0) { + return selection; } arrow::TableBatchReader reader(*table); std::shared_ptr<arrow::RecordBatch> batch; while (true) { s = reader.ReadNext(&batch); if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot read batches from table {}", s.ToString())); + throw runtime_error_f("Cannot read batches from table %s", s.ToString().c_str()); } if (batch == nullptr) { break; } s = gfilter->Evaluate(*batch, selection); if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot apply filter {}", s.ToString())); + throw runtime_error_f("Cannot apply filter %s", s.ToString().c_str()); } } @@ -353,14 +407,14 @@ auto createProjection(std::shared_ptr<arrow::Table> table, std::shared_ptr<gandi while (true) { auto s = reader.ReadNext(&batch); if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot read batches from table {}", s.ToString())); + throw runtime_error_f("Cannot read batches from table %s", s.ToString().c_str()); } if (batch == nullptr) { break; } s = gprojector->Evaluate(*batch, arrow::default_memory_pool(), v.get()); if (!s.ok()) { - throw std::runtime_error(fmt::format("Cannot apply projector {}", s.ToString())); + throw runtime_error_f("Cannot apply projector %s", s.ToString().c_str()); } } return v; @@ -384,15 +438,25 @@ gandiva::NodePtr createExpressionTree(Operations const& opSpecs, if (spec.datum.index() == 2) { auto content = std::get<LiteralNode::var_t>(spec.datum); - if (content.index() == 0) + if (content.index() == 0) { return gandiva::TreeExprBuilder::MakeLiteral(static_cast<int32_t>(std::get<int>(content))); - if (content.index() == 1) + } + if (content.index() == 1) { return gandiva::TreeExprBuilder::MakeLiteral(std::get<bool>(content)); - if (content.index() == 2) + } + if (content.index() == 2) { return gandiva::TreeExprBuilder::MakeLiteral(std::get<float>(content)); - if (content.index() == 3) + } + if (content.index() == 3) { return gandiva::TreeExprBuilder::MakeLiteral(std::get<double>(content)); - throw std::runtime_error("Malformed LiteralNode"); + } + if (content.index() == 4) { + return gandiva::TreeExprBuilder::MakeLiteral(std::get<uint8_t>(content)); + } + if (content.index() == 5) { + return gandiva::TreeExprBuilder::MakeLiteral(std::get<int64_t>(content)); + } + throw runtime_error("Malformed LiteralNode"); } if (spec.datum.index() == 3) { @@ -403,13 +467,13 @@ gandiva::NodePtr createExpressionTree(Operations const& opSpecs, } auto field = Schema->GetFieldByName(name); if (field == nullptr) { - throw std::runtime_error(fmt::format("Cannot find field \"{}\"", name)); + throw runtime_error_f("Cannot find field \"%s\"", name.c_str()); } auto node = gandiva::TreeExprBuilder::MakeField(field); fieldNodes.insert({name, node}); return node; } - throw std::runtime_error("Malformed DatumSpec"); + throw runtime_error("Malformed DatumSpec"); }; gandiva::NodePtr tree = nullptr; @@ -425,6 +489,16 @@ gandiva::NodePtr createExpressionTree(Operations const& opSpecs, return node; }; + auto insertEqualizeUpcastNode = [&](gandiva::NodePtr& node1, gandiva::NodePtr& node2, atype::type t1, atype::type t2) { + if (t2 > t1) { + auto upcast = gandiva::TreeExprBuilder::MakeFunction(upcastTo(t2), {node1}, concreteArrowType(t2)); + node1 = upcast; + } else if (t1 > t2) { + auto upcast = gandiva::TreeExprBuilder::MakeFunction(upcastTo(t1), {node2}, concreteArrowType(t1)); + node2 = upcast; + } + }; + switch (it->op) { case BasicOp::LogicalOr: tree = gandiva::TreeExprBuilder::MakeOr({leftNode, rightNode}); @@ -433,10 +507,12 @@ gandiva::NodePtr createExpressionTree(Operations const& opSpecs, tree = gandiva::TreeExprBuilder::MakeAnd({leftNode, rightNode}); break; default: - if (it->op < BasicOp::Exp) { + if (it->op < BasicOp::Sqrt) { if (it->type != atype::BOOL) { leftNode = insertUpcastNode(leftNode, it->left.type); rightNode = insertUpcastNode(rightNode, it->right.type); + } else if (it->op == BasicOp::Equal || it->op == BasicOp::NotEqual) { + insertEqualizeUpcastNode(leftNode, rightNode, it->left.type, it->right.type); } tree = gandiva::TreeExprBuilder::MakeFunction(binaryOperationsMap[it->op], {leftNode, rightNode}, concreteArrowType(it->type)); } else { @@ -455,10 +531,12 @@ bool isSchemaCompatible(gandiva::SchemaPtr const& Schema, Operations const& opSp { std::set<std::string> opFieldNames; for (auto& spec : opSpecs) { - if (spec.left.datum.index() == 3) + if (spec.left.datum.index() == 3) { opFieldNames.insert(std::get<std::string>(spec.left.datum)); - if (spec.right.datum.index() == 3) + } + if (spec.right.datum.index() == 3) { opFieldNames.insert(std::get<std::string>(spec.right.datum)); + } } std::set<std::string> schemaFieldNames; @@ -473,7 +551,7 @@ bool isSchemaCompatible(gandiva::SchemaPtr const& Schema, Operations const& opSp void updateExpressionInfos(expressions::Filter const& filter, std::vector<ExpressionInfo>& eInfos) { if (eInfos.empty()) { - throw std::runtime_error("Empty expression info vector."); + throw runtime_error("Empty expression info vector."); } Operations ops = createOperations(filter); for (auto& info : eInfos) { diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 50ed80d7bcd42..105b3b3acbc57 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -104,13 +104,13 @@ void sendOnChannel(FairMQDevice& device, FairMQParts& messages, std::string cons messages.fParts.clear(); } -void sendOnChannel(FairMQDevice& device, FairMQParts& messages, OutputSpec const& spec, ChannelRetriever& channelRetriever) +void sendOnChannel(FairMQDevice& device, FairMQParts& messages, OutputSpec const& spec, DataProcessingHeader::StartTime tslice, ChannelRetriever& channelRetriever) { // Note: DPL is only setting up one instance of a channel while FairMQ allows to have an // array of channels, the index is 0 in the call - auto channel = channelRetriever(spec); + auto channel = channelRetriever(spec, tslice); if (channel.empty()) { - LOG(WARNING) << "can not find matching channel for " << DataSpecUtils::describe(spec); + LOG(WARNING) << "can not find matching channel for " << DataSpecUtils::describe(spec) << " timeslice " << tslice; return; } sendOnChannel(device, messages, channel); @@ -118,7 +118,12 @@ void sendOnChannel(FairMQDevice& device, FairMQParts& messages, OutputSpec const void sendOnChannel(FairMQDevice& device, o2::header::Stack&& headerStack, FairMQMessagePtr&& payloadMessage, OutputSpec const& spec, ChannelRetriever& channelRetriever) { - auto channelName = channelRetriever(spec); + const auto* dph = o2::header::get<DataProcessingHeader*>(headerStack.data()); + if (!dph) { + LOG(ERROR) << "Header Stack does not follow the O2 data model, DataProcessingHeader missing"; + return; + } + auto channelName = channelRetriever(spec, dph->startTime); constexpr auto index = 0; if (channelName.empty()) { LOG(WARNING) << "can not find matching channel for " << DataSpecUtils::describe(spec); @@ -144,10 +149,17 @@ void sendOnChannel(FairMQDevice& device, o2::header::Stack&& headerStack, FairMQ void sendOnChannel(FairMQDevice& device, FairMQMessagePtr&& headerMessage, FairMQMessagePtr&& payloadMessage, OutputSpec const& spec, ChannelRetriever& channelRetriever) { + // const auto* dph = o2::header::get<DataProcessingHeader*>( *reinterpret_cast<o2::header::Stack*>(headerMessage->GetData()) ); + const auto* dph = o2::header::get<DataProcessingHeader*>(headerMessage->GetData()); + if (!dph) { + LOG(ERROR) << "Header does not follow the O2 data model, DataProcessingHeader missing"; + return; + } + auto tslice = dph->startTime; FairMQParts out; out.AddPart(std::move(headerMessage)); out.AddPart(std::move(payloadMessage)); - sendOnChannel(device, out, spec, channelRetriever); + sendOnChannel(device, out, spec, tslice, channelRetriever); } InjectorFunction o2DataModelAdaptor(OutputSpec const& spec, uint64_t startTime, uint64_t step) @@ -211,20 +223,25 @@ InjectorFunction dplModelAdaptor(std::vector<OutputSpec> const& filterSpecs, boo continue; } - auto dh = o2::header::get<DataHeader*>(parts.At(msgidx * 2)->GetData()); + const auto dh = o2::header::get<DataHeader*>(parts.At(msgidx * 2)->GetData()); if (!dh) { LOG(ERROR) << "data on input " << msgidx << " does not follow the O2 data model, DataHeader missing"; continue; } + const auto dph = o2::header::get<DataProcessingHeader*>(parts.At(msgidx * 2)->GetData()); + if (!dph) { + LOG(ERROR) << "data on input " << msgidx << " does not follow the O2 data model, DataProcessingHeader missing"; + continue; + } LOG(DEBUG) << msgidx << ": " << DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}) << " part " << dh->splitPayloadIndex << " of " << dh->splitPayloadParts << " payload " << parts.At(msgidx * 2 + 1)->GetSize(); OutputSpec query{dh->dataOrigin, dh->dataDescription, dh->subSpecification}; - LOG(DEBUG) << "processing " << DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}) << " part " << dh->splitPayloadIndex << " of " << dh->splitPayloadParts; + LOG(DEBUG) << "processing " << DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}) << " time slice " << dph->startTime << " part " << dh->splitPayloadIndex << " of " << dh->splitPayloadParts; for (auto const& spec : filterSpecs) { // filter on the specified OutputSpecs, the default value is a ConcreteDataTypeMatcher with origin and description 'any' if (DataSpecUtils::match(spec, OutputSpec{{header::gDataOriginAny, header::gDataDescriptionAny}}) || DataSpecUtils::match(spec, query)) { - auto channelName = channelRetriever(query); + auto channelName = channelRetriever(query, dph->startTime); if (channelName.empty()) { LOG(WARNING) << "can not find matching channel, not able to adopt " << DataSpecUtils::describe(query); break; @@ -325,7 +342,7 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, char const* defaultChannelConfig, std::function<void(FairMQDevice&, FairMQParts&, - std::function<std::string(OutputSpec const&)>)> + ChannelRetriever)> converter) { DataProcessorSpec spec; @@ -356,10 +373,10 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, // Converter should pump messages auto handler = [device, converter, outputRoutes = std::move(outputRoutes)](FairMQParts& inputs, int) { - auto channelRetriever = [outputRoutes = std::move(outputRoutes)](OutputSpec const& query) -> std::string { + auto channelRetriever = [outputRoutes = std::move(outputRoutes)](OutputSpec const& query, DataProcessingHeader::StartTime timeslice) -> std::string { for (auto& route : outputRoutes) { LOG(DEBUG) << "matching: " << DataSpecUtils::describe(query) << " to route " << DataSpecUtils::describe(route.matcher); - if (DataSpecUtils::match(route.matcher, query)) { + if (DataSpecUtils::match(route.matcher, query) && ((timeslice % route.maxTimeslices) == route.timeslice)) { return route.channel; } } @@ -378,6 +395,15 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, return spec; } +namespace +{ +// Decide where to sent the output. Everything to "downstream" if there is such a channel. +std::string decideChannel(InputSpec const& input, const std::unordered_map<std::string, std::vector<FairMQChannel>>& channels) +{ + return channels.count("downstream") ? "downstream" : input.binding; +} +} // namespace + DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, Inputs const& inputSpecs, const char* defaultChannelConfig) @@ -394,8 +420,9 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, auto channelConfigurationChecker = [inputSpecs = std::move(inputSpecs), device]() { LOG(INFO) << "checking channel configuration"; for (auto const& spec : inputSpecs) { - if (device->fChannels.count(spec.binding) == 0) { - throw std::runtime_error("no corresponding output channel found for input '" + spec.binding + "'"); + auto channel = decideChannel(spec, device->fChannels); + if (device->fChannels.count(channel) == 0) { + throw std::runtime_error("no corresponding output channel found for input '" + channel + "'"); } } }; @@ -403,18 +430,8 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, return adaptStateless([](RawDeviceService& rds, InputRecord& inputs) { std::unordered_map<std::string, FairMQParts> outputs; auto& device = *rds.device(); - for (auto& input : inputs) { - // TODO: we need to make a copy of the messages, maybe we can implement functionality in - // the RawDeviceService to forward messages, but this also needs to take into account that - // other consumers might exist - size_t headerMsgSize = o2::header::Stack::headerStackSize(reinterpret_cast<o2::byte const*>(input.header)); - auto* dh = o2::header::get<DataHeader*>(input.header); - if (!dh) { - std::stringstream errorMessage; - errorMessage << "no data header in " << *input.spec; - throw std::runtime_error(errorMessage.str()); - } - size_t payloadMsgSize = dh->payloadSize; + for (size_t ii = 0; ii != inputs.size(); ++ii) { + auto first = inputs.getByPos(ii, 0); // we could probably do something like this but we do not know when the message is going to be sent // and if DPL is still owning a valid copy. //auto headerMessage = device.NewMessageFor(input.spec->binding, input.header, headerMsgSize, [](void*, void*) {}); @@ -422,12 +439,28 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, // Note: DPL is only setting up one instance of a channel while FairMQ allows to have an // array of channels, the index is 0 in the call constexpr auto index = 0; - auto headerMessage = device.NewMessageFor(input.spec->binding, index, headerMsgSize); - memcpy(headerMessage->GetData(), input.header, headerMsgSize); - auto payloadMessage = device.NewMessageFor(input.spec->binding, index, payloadMsgSize); - memcpy(payloadMessage->GetData(), input.payload, payloadMsgSize); - outputs[input.spec->binding].AddPart(std::move(headerMessage)); - outputs[input.spec->binding].AddPart(std::move(payloadMessage)); + for (size_t pi = 0; pi < inputs.getNofParts(ii); ++pi) { + auto part = inputs.getByPos(ii, pi); + // TODO: we need to make a copy of the messages, maybe we can implement functionality in + // the RawDeviceService to forward messages, but this also needs to take into account that + // other consumers might exist + size_t headerMsgSize = o2::header::Stack::headerStackSize(reinterpret_cast<o2::byte const*>(part.header)); + auto* dh = o2::header::get<DataHeader*>(part.header); + if (!dh) { + std::stringstream errorMessage; + errorMessage << "no data header in " << *first.spec; + throw std::runtime_error(errorMessage.str()); + } + size_t payloadMsgSize = dh->payloadSize; + + auto channel = decideChannel(*first.spec, device.fChannels); + auto headerMessage = device.NewMessageFor(channel, index, headerMsgSize); + memcpy(headerMessage->GetData(), part.header, headerMsgSize); + auto payloadMessage = device.NewMessageFor(channel, index, payloadMsgSize); + memcpy(payloadMessage->GetData(), part.payload, payloadMsgSize); + outputs[channel].AddPart(std::move(headerMessage)); + outputs[channel].AddPart(std::move(payloadMessage)); + } } for (auto& [channelName, channelParts] : outputs) { if (channelParts.Size() == 0) { @@ -439,7 +472,8 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, }); const char* d = strdup(((std::string(defaultChannelConfig).find("name=") == std::string::npos ? (std::string("name=") + name + ",") : "") + std::string(defaultChannelConfig)).c_str()); spec.options = { - ConfigParamSpec{"channel-config", VariantType::String, d, {"Out-of-band channel config"}}}; + ConfigParamSpec{"channel-config", VariantType::String, d, {"Out-of-band channel config"}}, + }; return spec; } diff --git a/Framework/Core/src/FrameworkDummyDebugger.cxx b/Framework/Core/src/FrameworkDummyDebugger.cxx deleted file mode 100644 index 0520e260cf071..0000000000000 --- a/Framework/Core/src/FrameworkDummyDebugger.cxx +++ /dev/null @@ -1,38 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "DataProcessorInfo.h" -#include "Framework/FrameworkGUIDebugger.h" - -#include <algorithm> -#include <vector> - -namespace o2 -{ -namespace framework -{ -namespace gui -{ -// Dummy function in case we want to build without debugger. -std::function<void(void)> getGUIDebugger(std::vector<DeviceInfo> const& infos, - std::vector<DeviceSpec> const& devices, - std::vector<DataProcessorInfo> const& metadata, - std::vector<DeviceMetricsInfo> const& metricsInfos, - DriverInfo const& driverInfo, - std::vector<DeviceControl>& controls, - DriverControl& driverControl) -{ - return []() {}; -} - -void showNodeGraph(bool* opened) {} - -} // namespace gui -} // namespace framework -} // namespace o2 diff --git a/Framework/Core/src/FrameworkGUIDebugger.cxx b/Framework/Core/src/FrameworkGUIDebugger.cxx deleted file mode 100644 index 56f3e8542c8ea..0000000000000 --- a/Framework/Core/src/FrameworkGUIDebugger.cxx +++ /dev/null @@ -1,760 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/FrameworkGUIDebugger.h" -#include <algorithm> -#include <iostream> -#include <set> -#include <string> -#include "Framework/ConfigContext.h" -#include "Framework/ConfigParamRegistry.h" -#include "DebugGUI/imgui.h" -#include "DebugGUI/imgui_extras.h" -#include "DriverControl.cxx" -#include "DriverInfo.cxx" -#include "FrameworkGUIDeviceInspector.h" -#include "Framework/FrameworkGUIDevicesGraph.h" -#include "Framework/FrameworkGUIDataRelayerUsage.h" -#include "Framework/PaletteHelpers.h" -#include "Framework/FrameworkGUIState.h" - -// Simplify debugging -template class std::vector<o2::framework::DeviceMetricsInfo>; - -static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } -static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } - -namespace o2 -{ -namespace framework -{ -namespace gui -{ -// Type erased information for the plotting -struct MultiplotData { - int mod; - size_t first; - size_t size; - const void* Y; - const void* X; - MetricType type; -}; - -} // namespace gui -} // namespace framework -} // namespace o2 - -template class std::vector<o2::framework::gui::MultiplotData>; - -namespace o2 -{ -namespace framework -{ -namespace gui -{ - -ImVec4 colorForLogLevel(LogParsingHelpers::LogLevel logLevel) -{ - switch (logLevel) { - case LogParsingHelpers::LogLevel::Info: - return PaletteHelpers::GREEN; - case LogParsingHelpers::LogLevel::Debug: - return ImVec4(153. / 255, 61. / 255, 61. / 255, 255. / 255); - case LogParsingHelpers::LogLevel::Warning: - return PaletteHelpers::DARK_YELLOW; - case LogParsingHelpers::LogLevel::Error: - return PaletteHelpers::RED; - case LogParsingHelpers::LogLevel::Unknown: - return ImVec4(153. / 255, 61. / 255, 61. / 255, 255. / 255); - default: - return PaletteHelpers::WHITE; - }; -} - -void displayHistory(const DeviceInfo& info, DeviceControl& control) -{ - if (info.history.empty()) { - return; - } - int startPos = info.historyPos; - const int historySize = info.history.size(); - - int triggerStartPos = startPos + 1 % historySize; - int triggerStopPos = startPos % historySize; - - int j = startPos; - // We look for a stop trigger, so that we know where to stop the search for - // out start search. If no stop trigger is found, we search until the end - if (control.logStopTrigger[0]) { - while ((j % historySize) != ((startPos + 1) % historySize)) { - assert(j >= 0); - assert(j < historySize); - auto& line = info.history[j]; - if (strstr(line.c_str(), control.logStopTrigger)) { - triggerStopPos = (j + 1) % historySize; - break; - } - // Wrap in case we end up below 0 - j = (j == 0) ? historySize - 1 : j - 1; - } - } - - // Look for the last instance of the start trigger before the - // last stop trigger. - j = startPos + 1; - if (control.logStartTrigger[0]) { - while ((j % historySize) != triggerStopPos) { - assert(historySize > j); - assert(historySize == 1000); - auto& line = info.history[j]; - if (strstr(line.c_str(), control.logStartTrigger)) { - triggerStartPos = j; - } - j = (j + 1) % historySize; - } - } - - // We start from the last trigger found. Eventually this is the first - // line in the ring buffer, if no trigger is specified. - size_t ji = triggerStartPos % historySize; - size_t je = triggerStopPos % historySize; - size_t iterations = 0; - while (historySize && ((ji % historySize) != je)) { - assert(iterations < historySize); - iterations++; - assert(historySize == 1000); - assert(ji < historySize); - auto& line = info.history[ji]; - auto logLevel = info.historyLevel[ji]; - - // Skip empty lines - if (line.empty()) { - ji = (ji + 1) % historySize; - continue; - } - // Print matching lines - if (strstr(line.c_str(), control.logFilter) != nullptr) { - auto color = colorForLogLevel(logLevel); - // We filter twice, once on input, to reduce the - // stream, a second time at display time, to avoid - // showing unrelevant messages from past. - if (logLevel >= control.logLevel) { - if (line.find('%', 0) != std::string::npos) { - ImGui::TextUnformatted(line.c_str(), line.c_str() + line.size()); - } else { - ImGui::TextColored(color, line.c_str(), line.c_str() + line.size()); - } - } - } - ji = (ji + 1) % historySize; - } -} - -template <typename T> -struct HistoData { - int mod; - size_t first; - size_t size; - const T* points; -}; - -enum struct MetricsDisplayStyle : int { - Lines = 0, - Histos = 1, - Sparks = 2, - Table = 3 -}; - -void displayDeviceMetrics(const char* label, ImVec2 canvasSize, std::string const& selectedMetricName, - size_t rangeBegin, size_t rangeEnd, size_t bins, MetricsDisplayStyle displayType, - std::vector<DeviceSpec> const& specs, std::vector<DeviceMetricsInfo> const& metricsInfos) -{ - static std::vector<ImColor> palette = { - ImColor{218, 124, 48}, - ImColor{62, 150, 81}, - ImColor{204, 37, 41}, - ImColor{83, 81, 84}, - ImColor{107, 76, 154}, - ImColor{146, 36, 40}, - ImColor{148, 139, 61}}; - std::vector<void const*> metricsToDisplay; - std::vector<const char*> deviceNames; - std::vector<MultiplotData> userData; - std::vector<ImColor> colors; - MetricType metricType; - size_t metricSize = 0; - assert(specs.size() == metricsInfos.size()); - float maxValue = std::numeric_limits<float>::lowest(); - float minValue = 0; - size_t maxDomain = std::numeric_limits<size_t>::lowest(); - size_t minDomain = std::numeric_limits<size_t>::max(); - - for (int mi = 0; mi < metricsInfos.size(); ++mi) { - auto vi = DeviceMetricsHelper::metricIdxByName(selectedMetricName, metricsInfos[mi]); - if (vi == metricsInfos[mi].metricLabelsIdx.size()) { - continue; - } - auto& metric = metricsInfos[mi].metrics[vi]; - deviceNames.push_back(specs[mi].name.c_str()); - colors.push_back(palette[mi % palette.size()]); - metricType = metric.type; - MultiplotData data; - data.mod = metricsInfos[mi].timestamps[vi].size(); - data.first = metric.pos - data.mod; - data.X = metricsInfos[mi].timestamps[vi].data(); - minValue = std::min(minValue, metricsInfos[mi].min[vi]); - maxValue = std::max(maxValue, metricsInfos[mi].max[vi]); - minDomain = std::min(minDomain, metricsInfos[mi].minDomain[vi]); - maxDomain = std::max(maxDomain, metricsInfos[mi].maxDomain[vi]); - switch (metric.type) { - case MetricType::Int: { - data.size = metricsInfos[mi].intMetrics[metric.storeIdx].size(); - data.Y = metricsInfos[mi].intMetrics[metric.storeIdx].data(); - data.type = MetricType::Int; - metricType = MetricType::Int; - metricSize = metricsInfos[mi].intMetrics[metric.storeIdx].size(); - } break; - case MetricType::Float: { - data.size = metricsInfos[mi].floatMetrics[metric.storeIdx].size(); - data.Y = metricsInfos[mi].floatMetrics[metric.storeIdx].data(); - data.type = MetricType::Float; - metricType = MetricType::Float; - metricSize = metricsInfos[mi].floatMetrics[metric.storeIdx].size(); - } break; - case MetricType::Unknown: - case MetricType::String: { - data.size = metricsInfos[mi].stringMetrics[metric.storeIdx].size(); - data.Y = nullptr; - data.type = MetricType::String; - metricType = MetricType::String; - metricSize = metricsInfos[mi].stringMetrics[metric.storeIdx].size(); - } break; - } - userData.emplace_back(data); - } - - maxDomain = std::max(minDomain + 1024, maxDomain); - - for (size_t ui = 0; ui < userData.size(); ++ui) { - metricsToDisplay.push_back(&(userData[ui])); - } - auto getterY = [](const void* hData, int idx) -> float { - auto histoData = reinterpret_cast<const MultiplotData*>(hData); - size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; - // size_t pos = (static_cast<size_t>(idx)) % histoData->mod; - assert(pos >= 0 && pos < 1024); - if (histoData->type == MetricType::Int) { - return static_cast<const int*>(histoData->Y)[pos]; - } else if (histoData->type == MetricType::Float) { - return static_cast<const float*>(histoData->Y)[pos]; - } else { - return 0; - } - }; - auto getterX = [](const void* hData, int idx) -> size_t { - auto histoData = reinterpret_cast<const MultiplotData*>(hData); - size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; - //size_t pos = (static_cast<size_t>(idx)) % histoData->mod; - assert(pos >= 0 && pos < 1024); - return static_cast<const size_t*>(histoData->X)[pos]; - }; - switch (displayType) { - case MetricsDisplayStyle::Histos: - ImGui::PlotMultiHistograms( - label, - userData.size(), - deviceNames.data(), - colors.data(), - getterY, - getterX, - metricsToDisplay.data(), - metricSize, - minValue, - maxValue * 1.2f, - minDomain, - maxDomain, - canvasSize); - break; - case MetricsDisplayStyle::Lines: - ImGui::PlotMultiLines( - label, - userData.size(), - deviceNames.data(), - colors.data(), - getterY, - getterX, - metricsToDisplay.data(), - metricSize, - minValue, - maxValue * 1.2f, - minDomain, - maxDomain, - canvasSize); - break; - default: - break; - } -} - -struct ColumnInfo { - MetricType type; - int index; -}; - -void metricsTableRow(std::vector<ColumnInfo> columnInfos, - std::vector<DeviceMetricsInfo> const& metricsInfos, - int row) -{ - ImGui::Text("%d", row); - ImGui::NextColumn(); - - for (size_t j = 0; j < columnInfos.size(); j++) { - auto& info = columnInfos[j]; - auto& metricsInfo = metricsInfos[j]; - - if (info.index == -1) { - ImGui::TextUnformatted("-"); - ImGui::NextColumn(); - continue; - } - switch (info.type) { - case MetricType::Int: { - ImGui::Text("%i (%i)", metricsInfo.intMetrics[info.index][row], info.index); - ImGui::NextColumn(); - } break; - case MetricType::Float: { - ImGui::Text("%f (%i)", metricsInfo.floatMetrics[info.index][row], info.index); - ImGui::NextColumn(); - } break; - case MetricType::String: { - ImGui::Text("%s (%i)", metricsInfo.stringMetrics[info.index][row].data, info.index); - ImGui::NextColumn(); - } break; - default: - ImGui::NextColumn(); - break; - } - } -} - -void historyBar(gui::WorkspaceGUIState& globalGUIState, - size_t rangeBegin, size_t rangeEnd, - gui::DeviceGUIState& state, - DriverInfo const& driverInfo, - DeviceSpec const& spec, - DeviceMetricsInfo const& metricsInfo) -{ - bool open = ImGui::TreeNode(state.label.c_str()); - if (open) { - ImGui::Text("# channels: %lu", spec.outputChannels.size() + spec.inputChannels.size()); - ImGui::TreePop(); - } - ImGui::NextColumn(); - - if (globalGUIState.selectedMetric == -1) { - ImGui::NextColumn(); - return; - } - - auto currentMetricName = driverInfo.availableMetrics[globalGUIState.selectedMetric]; - - size_t i = DeviceMetricsHelper::metricIdxByName(currentMetricName, metricsInfo); - // We did not find any plot, skipping this. - if (i == metricsInfo.metricLabelsIdx.size()) { - ImGui::NextColumn(); - return; - } - auto& metric = metricsInfo.metrics[i]; - - switch (metric.type) { - case MetricType::Int: { - HistoData<int> data; - data.mod = metricsInfo.timestamps[i].size(); - data.first = metric.pos - data.mod; - data.size = metricsInfo.intMetrics[metric.storeIdx].size(); - data.points = metricsInfo.intMetrics[metric.storeIdx].data(); - - auto getter = [](void* hData, int idx) -> float { - auto histoData = reinterpret_cast<HistoData<int>*>(hData); - size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; - assert(pos >= 0 && pos < 1024); - return histoData->points[pos]; - }; - ImGui::PlotLines(("##" + currentMetricName).c_str(), getter, &data, data.size); - ImGui::NextColumn(); - } break; - case MetricType::Float: { - HistoData<float> data; - data.mod = metricsInfo.timestamps[i].size(); - data.first = metric.pos - data.mod; - data.size = metricsInfo.floatMetrics[metric.storeIdx].size(); - data.points = metricsInfo.floatMetrics[metric.storeIdx].data(); - - auto getter = [](void* hData, int idx) -> float { - auto histoData = reinterpret_cast<HistoData<float>*>(hData); - size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; - assert(pos >= 0 && pos < 1024); - return histoData->points[pos]; - }; - ImGui::PlotLines(("##" + currentMetricName).c_str(), getter, &data, data.size); - ImGui::NextColumn(); - } break; - default: - ImGui::NextColumn(); - return; - break; - } -} - -/// Calculate where to find the coliumns for a give metric -std::vector<ColumnInfo> calculateTableIndex(gui::WorkspaceGUIState& globalGUIState, - int selectedMetric, - DriverInfo const& driverInfo, - std::vector<DeviceMetricsInfo> const& metricsInfos) -{ - std::vector<ColumnInfo> columns; - for (size_t j = 0; j < globalGUIState.devices.size(); ++j) { - const DeviceMetricsInfo& metricsInfo = metricsInfos[j]; - /// Nothing to draw, if no metric selected. - if (selectedMetric == -1) { - columns.push_back({MetricType::Int, -1}); - continue; - } - auto currentMetricName = driverInfo.availableMetrics[selectedMetric]; - size_t idx = DeviceMetricsHelper::metricIdxByName(currentMetricName, metricsInfo); - - // We did not find any plot, skipping this. - if (idx == metricsInfo.metricLabelsIdx.size()) { - columns.push_back({MetricType::Int, -1}); - continue; - } - auto metric = metricsInfos[j].metrics[idx]; - columns.push_back({metric.type, static_cast<int>(metric.storeIdx)}); - } - return columns; -}; - -void displayDeviceHistograms(gui::WorkspaceGUIState& state, - DriverInfo const& driverInfo, - std::vector<DeviceInfo> const& infos, - std::vector<DeviceSpec> const& devices, - std::vector<DataProcessorInfo> const& metadata, - std::vector<DeviceControl>& controls, - std::vector<DeviceMetricsInfo> const& metricsInfos) -{ - showTopologyNodeGraph(state, infos, devices, metadata, controls, metricsInfos); - if (state.bottomPaneVisible == false) { - return; - } - ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetIO().DisplaySize.y - state.bottomPaneSize), 0); - ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, state.bottomPaneSize), 0); - - ImGui::Begin("Devices", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); - ImGui::BeginGroup(); - char const* currentMetric = nullptr; - if (state.selectedMetric != -1) { - currentMetric = driverInfo.availableMetrics[state.selectedMetric].c_str(); - } else { - currentMetric = "Click to select metric"; - } - if (ImGui::BeginCombo("###Select metric", currentMetric, 0)) { - for (size_t mi = 0; mi < driverInfo.availableMetrics.size(); ++mi) { - auto metric = driverInfo.availableMetrics[mi]; - bool isSelected = mi == state.selectedMetric; - if (ImGui::Selectable(driverInfo.availableMetrics[mi].c_str(), isSelected)) { - state.selectedMetric = mi; - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - }; - - static char const* plotStyles[] = { - "lines", - "histograms", - "sparks", - "table"}; - ImGui::SameLine(); - static enum MetricsDisplayStyle currentStyle = MetricsDisplayStyle::Lines; - ImGui::Combo("##Select style", reinterpret_cast<int*>(¤tStyle), plotStyles, IM_ARRAYSIZE(plotStyles)); - - // Calculate the full timestamp range for the selected metric - size_t minTime = -1; - size_t maxTime = 0; - std::string currentMetricName; - if (state.selectedMetric >= 0) { - currentMetricName = driverInfo.availableMetrics[state.selectedMetric]; - for (auto& metricInfo : metricsInfos) { - size_t mi = DeviceMetricsHelper::metricIdxByName(currentMetricName, metricInfo); - if (mi == metricInfo.metricLabelsIdx.size()) { - continue; - } - auto& metric = metricInfo.metrics[mi]; - auto& timestamps = metricInfo.timestamps[mi]; - - for (size_t ti = 0; ti != metricInfo.timestamps.size(); ++ti) { - size_t minRangePos = (metric.pos + ti) % metricInfo.timestamps.size(); - size_t curMinTime = timestamps[minRangePos]; - if (curMinTime == 0) { - continue; - } - minTime = minTime < curMinTime ? minTime : curMinTime; - if (minTime != 0 && minTime != -1) { - break; - } - } - size_t maxRangePos = (size_t)(metric.pos) - 1 % metricInfo.timestamps.size(); - size_t curMaxTime = timestamps[maxRangePos]; - maxTime = maxTime > curMaxTime ? maxTime : curMaxTime; - } - } - if (minTime != -1) { - ImGui::Text("min timestamp: %zu, max timestamp: %zu", minTime, maxTime); - } - ImGui::EndGroup(); - if (!currentMetricName.empty()) { - switch (currentStyle) { - case MetricsDisplayStyle::Histos: - case MetricsDisplayStyle::Lines: { - displayDeviceMetrics("Metrics", - ImVec2(ImGui::GetIO().DisplaySize.x - 10, state.bottomPaneSize - ImGui::GetItemRectSize().y - 20), currentMetricName, minTime, maxTime, 1024, - currentStyle, devices, metricsInfos); - } break; - case MetricsDisplayStyle::Sparks: { - ImGui::BeginChild("##ScrollingRegion", ImVec2(ImGui::GetIO().DisplaySize.x + state.leftPaneSize + state.rightPaneSize - 10, -ImGui::GetItemsLineHeightWithSpacing()), false, - ImGuiWindowFlags_HorizontalScrollbar); - ImGui::Columns(2); - ImGui::SetColumnOffset(1, 300); - for (size_t i = 0; i < state.devices.size(); ++i) { - gui::DeviceGUIState& deviceGUIState = state.devices[i]; - const DeviceSpec& spec = devices[i]; - const DeviceMetricsInfo& metricsInfo = metricsInfos[i]; - - historyBar(state, minTime, maxTime, deviceGUIState, driverInfo, spec, metricsInfo); - } - ImGui::Columns(1); - ImGui::EndChild(); - } break; - case MetricsDisplayStyle::Table: { - ImGui::BeginChild("##ScrollingRegion", ImVec2(ImGui::GetIO().DisplaySize.x + state.leftPaneSize + state.rightPaneSize - 10, -ImGui::GetItemsLineHeightWithSpacing()), false, - ImGuiWindowFlags_HorizontalScrollbar); - - // The +1 is for the timestamp column - ImGui::Columns(state.devices.size() + 1); - ImGui::TextUnformatted("entry"); - ImGui::NextColumn(); - ImVec2 textsize = ImGui::CalcTextSize("extry", nullptr, true); - float offset = 0.f; - offset += std::max(100.f, textsize.x); - for (size_t j = 0; j < state.devices.size(); ++j) { - gui::DeviceGUIState& deviceGUIState = state.devices[j]; - const DeviceSpec& spec = devices[j]; - - ImGui::SetColumnOffset(-1, offset); - textsize = ImGui::CalcTextSize(spec.name.c_str(), nullptr, true); - offset += std::max(100.f, textsize.x); - ImGui::TextUnformatted(spec.name.c_str()); - ImGui::NextColumn(); - } - ImGui::Separator(); - - auto columns = calculateTableIndex(state, state.selectedMetric, driverInfo, metricsInfos); - - // Calculate which columns we want to see. - // FIXME: only one column for now. - for (size_t i = 0; i < 10; ++i) { - metricsTableRow(columns, metricsInfos, i); - } - ImGui::Columns(1); - - ImGui::EndChild(); - } break; - } - } - ImGui::End(); -} - -void pushWindowColorDueToStatus(const DeviceInfo& info) -{ - using LogLevel = LogParsingHelpers::LogLevel; - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.); - if (info.active == false) { - ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::DARK_RED); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED); - ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::RED); - return; - } - switch (info.maxLogLevel) { - case LogLevel::Error: - ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_RED); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED); - ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_RED); - break; - case LogLevel::Warning: - ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_YELLOW); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::YELLOW); - ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_YELLOW); - break; - case LogLevel::Info: - ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_GREEN); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::GREEN); - ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_GREEN); - break; - default: - ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_BLUE); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::BLUE); - ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_BLUE); - break; - } -} - -void popWindowColorDueToStatus() -{ - ImGui::PopStyleColor(3); - ImGui::PopStyleVar(1); -} - -struct DriverHelper { - static char const* stateToString(enum DriverState state) - { - static const char* names[static_cast<int>(DriverState::LAST)] = { - "INIT", // - "SCHEDULE", // - "RUNNING", // - "GUI", // - "REDEPLOY_GUI", // - "QUIT_REQUESTED", // - "HANDLE_CHILDREN", // - "EXIT", // - "UNKNOWN" // - "PERFORM_CALLBACKS" // - }; - return names[static_cast<int>(state)]; - } -}; - -/// Display information window about the driver -/// and its state. -void displayDriverInfo(DriverInfo const& driverInfo, DriverControl& driverControl) -{ - ImGui::Begin("Driver information"); - - if (driverControl.state == DriverControlState::STEP) { - driverControl.state = DriverControlState::PAUSE; - } - auto state = reinterpret_cast<int*>(&driverControl.state); - ImGui::RadioButton("Play", state, static_cast<int>(DriverControlState::PLAY)); - ImGui::SameLine(); - ImGui::RadioButton("Pause", state, static_cast<int>(DriverControlState::PAUSE)); - ImGui::SameLine(); - ImGui::RadioButton("Step", state, static_cast<int>(DriverControlState::STEP)); - - auto& registry = driverInfo.configContext->options(); - ImGui::Columns(); - - ImGui::Text("Frame cost (latency): %.1f(%.1f)ms", driverInfo.frameCost, driverInfo.frameLatency); - ImGui::Text("Input parsing cost (latency): %.1f(%.1f)ms", driverInfo.inputProcessingCost, driverInfo.inputProcessingLatency); - ImGui::Text("State stack (depth %lu)", driverInfo.states.size()); - if (ImGui::Button("SIGCONT all children")) { - kill(0, SIGCONT); - } - - for (size_t i = 0; i < driverInfo.states.size(); ++i) { - ImGui::Text("#%lu: %s", i, DriverHelper::stateToString(driverInfo.states[i])); - } - - ImGui::End(); -} - -// FIXME: return empty function in case we were not built -// with GLFW support. -/// -std::function<void(void)> getGUIDebugger(std::vector<DeviceInfo> const& infos, - std::vector<DeviceSpec> const& devices, - std::vector<DataProcessorInfo> const& metadata, - std::vector<DeviceMetricsInfo> const& metricsInfos, - DriverInfo const& driverInfo, - std::vector<DeviceControl>& controls, - DriverControl& driverControl) -{ - static gui::WorkspaceGUIState globalGUIState; - gui::WorkspaceGUIState& guiState = globalGUIState; - guiState.selectedMetric = -1; - guiState.metricMaxRange = 0UL; - guiState.metricMinRange = -1; - // FIXME: this should probaly have a better mapping between our window state and - guiState.devices.resize(infos.size()); - for (size_t i = 0; i < guiState.devices.size(); ++i) { - gui::DeviceGUIState& state = guiState.devices[i]; - state.label = devices[i].id + "(" + std::to_string(infos[i].pid) + ")"; - } - guiState.bottomPaneSize = 300; - guiState.leftPaneSize = 200; - guiState.rightPaneSize = 300; - - // Show all the panes by default. - guiState.bottomPaneVisible = true; - guiState.leftPaneVisible = true; - guiState.rightPaneVisible = true; - - return [&guiState, &infos, &devices, &metadata, &controls, &metricsInfos, &driverInfo, &driverControl]() { - ImGuiStyle& style = ImGui::GetStyle(); - style.FrameRounding = 0.; - style.WindowRounding = 0.; - style.Colors[ImGuiCol_WindowBg] = ImVec4(0x1b / 255.f, 0x1b / 255.f, 0x1b / 255.f, 1.00f); - style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0x1b / 255.f, 0x1b / 255.f, 0x1b / 255.f, 1.00f); - - displayDeviceHistograms(guiState, driverInfo, infos, devices, metadata, controls, metricsInfos); - displayDriverInfo(driverInfo, driverControl); - - int windowPosStepping = (ImGui::GetIO().DisplaySize.y - 500) / guiState.devices.size(); - - for (size_t i = 0; i < guiState.devices.size(); ++i) { - gui::DeviceGUIState& state = guiState.devices[i]; - assert(i < infos.size()); - assert(i < devices.size()); - const DeviceInfo& info = infos[i]; - const DeviceSpec& spec = devices[i]; - const DeviceMetricsInfo& metrics = metricsInfos[i]; - - assert(controls.size() == devices.size()); - DeviceControl& control = controls[i]; - - pushWindowColorDueToStatus(info); - if (control.logVisible) { - ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x / 3 * 2, i * windowPosStepping), ImGuiCond_Once); - ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x / 3, ImGui::GetIO().DisplaySize.y - 300), - ImGuiCond_Once); - ImGui::Begin(state.label.c_str(), &control.logVisible); - - ImGui::InputText("Log filter", control.logFilter, sizeof(control.logFilter)); - ImGui::InputText("Log start trigger", control.logStartTrigger, sizeof(control.logStartTrigger)); - ImGui::InputText("Log stop trigger", control.logStopTrigger, sizeof(control.logStopTrigger)); - ImGui::Checkbox("Stop logging", &control.quiet); - ImGui::SameLine(); - ImGui::Combo("Log level", reinterpret_cast<int*>(&control.logLevel), LogParsingHelpers::LOG_LEVELS, - (int)LogParsingHelpers::LogLevel::Size, 5); - - ImGui::Separator(); - ImGui::BeginChild("ScrollingRegion", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing()), false, - ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); - displayHistory(info, control); - ImGui::EndChild(); - ImGui::End(); - } - popWindowColorDueToStatus(); - } - }; -} - -} // namespace gui -} // namespace framework -} // namespace o2 diff --git a/Framework/Core/src/HistogramRegistry.cxx b/Framework/Core/src/HistogramRegistry.cxx new file mode 100644 index 0000000000000..bb19012367fe3 --- /dev/null +++ b/Framework/Core/src/HistogramRegistry.cxx @@ -0,0 +1,346 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/HistogramRegistry.h" + +namespace o2::framework +{ + +// define histogram callbacks for runtime histogram creation +#define CALLB(HType) \ + { \ + k##HType, \ + [](HistogramSpec const& histSpec) { \ + return HistFactory::createHistVariant<HType>(histSpec); \ + } \ + } + +const std::map<HistType, std::function<HistPtr(const HistogramSpec&)>> HistFactory::HistogramCreationCallbacks{ + CALLB(TH1D), CALLB(TH1F), CALLB(TH1I), CALLB(TH1C), CALLB(TH1S), + CALLB(TH2D), CALLB(TH2F), CALLB(TH2I), CALLB(TH2C), CALLB(TH2S), + CALLB(TH3D), CALLB(TH3F), CALLB(TH3I), CALLB(TH3C), CALLB(TH3S), + CALLB(THnD), CALLB(THnF), CALLB(THnI), CALLB(THnC), CALLB(THnS), CALLB(THnL), + CALLB(THnSparseD), CALLB(THnSparseF), CALLB(THnSparseI), CALLB(THnSparseC), CALLB(THnSparseS), CALLB(THnSparseL), + CALLB(TProfile), CALLB(TProfile2D), CALLB(TProfile3D), + //CALLB(StepTHnF), CALLB(StepTHnD) +}; + +#undef CALLB + +// create histogram from specification and insert it into the registry +void HistogramRegistry::insert(const HistogramSpec& histSpec) +{ + validateHash(histSpec.hash, histSpec.name.data()); + const uint32_t idx = imask(histSpec.hash); + for (auto i = 0u; i < MAX_REGISTRY_SIZE; ++i) { + TObject* rawPtr = nullptr; + std::visit([&](const auto& sharedPtr) { rawPtr = sharedPtr.get(); }, mRegistryValue[imask(idx + i)]); + if (!rawPtr) { + registerName(histSpec.name); + mRegistryKey[imask(idx + i)] = histSpec.hash; + mRegistryValue[imask(idx + i)] = HistFactory::createHistVariant(histSpec); + lookup += i; + return; + } + } + LOGF(FATAL, R"(Internal array of HistogramRegistry "%s" is full.)", mName); +} + +void HistogramRegistry::validateHash(const uint32_t hash, const char* name) +{ + auto it = std::find(mRegistryKey.begin(), mRegistryKey.end(), hash); + if (it != mRegistryKey.end()) { + auto idx = it - mRegistryKey.begin(); + std::string collidingName{}; + std::visit([&](const auto& hist) { collidingName = hist->GetName(); }, mRegistryValue[idx]); + LOGF(FATAL, R"(Hash collision in HistogramRegistry "%s"! Please rename histogram "%s" or "%s".)", mName, name, collidingName); + } +} + +void HistogramRegistry::add(const HistogramSpec& histSpec) +{ + insert(histSpec); +} + +void HistogramRegistry::add(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2) +{ + insert({name, title, histConfigSpec, callSumw2}); +} + +void HistogramRegistry::add(char const* const name, char const* const title, HistType histType, std::vector<AxisSpec> axes, bool callSumw2) +{ + insert({name, title, {histType, axes}, callSumw2}); +} + +// store a copy of an existing histogram (or group of histograms) under a different name +void HistogramRegistry::addClone(const std::string& source, const std::string& target) +{ + auto doInsertClone = [&](const auto& sharedPtr) { + if (!sharedPtr.get()) { + return; + } + std::string sourceName{((TNamed*)sharedPtr.get())->GetName()}; + // search for histograms starting with source_ substring + if (sourceName.rfind(source, 0) == 0) { + // when cloning groups of histograms source_ and target_ must end with "/" + if (sourceName.size() != source.size() && (source.back() != '/' || target.back() != '/')) { + return; + } + // when cloning a single histogram the specified target_ must not be a group name + if (sourceName.size() == source.size() && target.back() == '/') { + LOGF(FATAL, "Cannot turn histogram into folder!"); + } + std::string targetName{target}; + targetName += sourceName.substr(sourceName.find(source) + source.size()); + insertClone(targetName.data(), sharedPtr); + } + }; + + for (auto& histVariant : mRegistryValue) { + std::visit(doInsertClone, histVariant); + } +} + +// function to query if name is already in use +bool HistogramRegistry::contains(const HistName& histName) +{ + // check for all occurances of the hash + auto iter = mRegistryKey.begin(); + while ((iter = std::find(iter, mRegistryKey.end(), histName.hash)) != mRegistryKey.end()) { + const char* curName = nullptr; + std::visit([&](auto&& hist) { if(hist) { curName = hist->GetName(); } }, mRegistryValue[iter - mRegistryKey.begin()]); + // if hash is the same, make sure that name is indeed the same + if (strcmp(curName, histName.str) == 0) { + return true; + } + } + return false; +} + +// get rough estimate for size of histogram stored in registry +double HistogramRegistry::getSize(const HistName& histName, double fillFraction) +{ + double size{}; + std::visit([&fillFraction, &size](auto&& hist) { size = HistFiller::getSize(hist, fillFraction); }, mRegistryValue[getHistIndex(histName)]); + return size; +} + +// get rough estimate for size of all histograms stored in registry +double HistogramRegistry::getSize(double fillFraction) +{ + double size{}; + for (auto j = 0u; j < MAX_REGISTRY_SIZE; ++j) { + std::visit([&fillFraction, &size](auto&& hist) { if(hist) { size += HistFiller::getSize(hist, fillFraction);} }, mRegistryValue[j]); + } + return size; +} + +// print some useful meta-info about the stored histograms +void HistogramRegistry::print(bool showAxisDetails) +{ + std::vector<double> fillFractions{0.1, 0.25, 0.5}; + std::vector<double> totalSizes(fillFractions.size()); + + uint32_t nHistos{}; + bool containsSparseHist{}; + auto printHistInfo = [&](auto&& hist) { + if (hist) { + using T = std::decay_t<decltype(*hist)>; + bool isSparse{}; + if (hist->InheritsFrom(THnSparse::Class())) { + isSparse = true; + containsSparseHist = true; + } + ++nHistos; + std::vector<double> sizes; + std::string sizeInfo{}; + if (isSparse) { + std::transform(std::begin(fillFractions), std::end(fillFractions), std::back_inserter(sizes), [&hist](auto& fraction) { return HistFiller::getSize(hist, fraction); }); + for (int i = 0; i < fillFractions.size(); ++i) { + sizeInfo += fmt::format("{:.2f} kB ({:.0f} %)", sizes[i] * 1024, fillFractions[i] * 100); + if (i != fillFractions.size() - 1) { + sizeInfo += ", "; + } + } + } else { + double size = HistFiller::getSize(hist); + sizes.resize(fillFractions.size(), size); + sizeInfo = fmt::format("{:.2f} kB", sizes[0] * 1024); + } + std::transform(totalSizes.begin(), totalSizes.end(), sizes.begin(), totalSizes.begin(), std::plus<double>()); + LOGF(INFO, "Hist %03d: %-35s %-19s [%s]", nHistos, hist->GetName(), hist->IsA()->GetName(), sizeInfo); + + if (showAxisDetails) { + int nDim = 0; + if constexpr (std::is_base_of_v<THnBase, T>) { + nDim = hist->GetNdimensions(); + } else if constexpr (std::is_base_of_v<TH1, T>) { + nDim = hist->GetDimension(); + } + for (int d = 0; d < nDim; ++d) { + TAxis* axis = HistFactory::getAxis(d, hist); + LOGF(INFO, "- Axis %d: %-20s (%d bins)", d, axis->GetTitle(), axis->GetNbins()); + } + } + } + }; + + std::string titleString{"======================== HistogramRegistry ========================"}; + LOGF(INFO, ""); + LOGF(INFO, "%s", titleString); + LOGF(INFO, "%s\"%s\"", std::string((int)(0.5 * titleString.size() - (1 + 0.5 * mName.size())), ' '), mName); + std::sort(mRegisteredNames.begin(), mRegisteredNames.end()); + for (auto& curHistName : mRegisteredNames) { + std::visit(printHistInfo, mRegistryValue[getHistIndex(HistName{curHistName.data()})]); + } + std::string totalSizeInfo{}; + if (containsSparseHist) { + for (int i = 0; i < totalSizes.size(); ++i) { + totalSizeInfo += fmt::format("{:.2f} MB ({:.0f} %)", totalSizes[i], fillFractions[i] * 100); + if (i != totalSizes.size() - 1) { + totalSizeInfo += ", "; + } + } + } else { + totalSizeInfo = fmt::format("{:.2f} MB", totalSizes[0]); + } + LOGF(INFO, "%s", std::string(titleString.size(), '='), titleString); + LOGF(INFO, "Total: %d histograms, ca. %s", nHistos, totalSizeInfo); + if (lookup) { + LOGF(INFO, "Due to index collisions, histograms were shifted by %d registry slots in total.", lookup); + } + LOGF(INFO, "%s", std::string(titleString.size(), '='), titleString); + LOGF(INFO, ""); +} + +// create output structure will be propagated to file-sink +TList* HistogramRegistry::operator*() +{ + TList* list = new TList(); + list->SetName(mName.data()); + + for (auto i = 0u; i < MAX_REGISTRY_SIZE; ++i) { + TNamed* rawPtr = nullptr; + std::visit([&](const auto& sharedPtr) { rawPtr = (TNamed*)sharedPtr.get(); }, mRegistryValue[i]); + if (rawPtr) { + std::deque<std::string> path = splitPath(rawPtr->GetName()); + std::string name = path.back(); + path.pop_back(); + TList* targetList{getSubList(list, path)}; + if (targetList) { + rawPtr->SetName(name.data()); + targetList->Add(rawPtr); + } else { + LOGF(FATAL, "Specified subfolder could not be created."); + } + } + } + + // sort histograms in output file alphabetically + if (mSortHistos) { + std::function<void(TList*)> sortList; + sortList = [&](TList* list) { + list->Sort(); + TIter next(list); + TNamed* subList = nullptr; + std::vector<TObject*> subLists; + while ((subList = (TNamed*)next())) { + if (subList->InheritsFrom(TList::Class())) { + subLists.push_back(subList); + sortList((TList*)subList); + } + } + // place lists always at the top + std::reverse(subLists.begin(), subLists.end()); + for (auto curList : subLists) { + list->Remove(curList); + list->AddFirst(curList); + } + }; + sortList(list); + } + + // create dedicated directory containing all of the registrys histograms + if (mCreateRegistryDir) { + // propagate this to the writer by adding a 'flag' to the output list + list->AddLast(new TNamed("createFolder", "")); + } + return list; +} + +// helper function to create resp. find the subList defined by path +TList* HistogramRegistry::getSubList(TList* list, std::deque<std::string>& path) +{ + if (path.empty()) { + return list; + } + TList* targetList{nullptr}; + std::string nextList = path[0]; + path.pop_front(); + if (auto subList = (TList*)list->FindObject(nextList.data())) { + if (subList->InheritsFrom(TList::Class())) { + targetList = getSubList((TList*)subList, path); + } else { + return nullptr; + } + } else { + subList = new TList(); + subList->SetName(nextList.data()); + list->Add(subList); + targetList = getSubList(subList, path); + } + return targetList; +} + +// helper function to split user defined path/to/hist/name string +std::deque<std::string> HistogramRegistry::splitPath(const std::string& pathAndNameUser) +{ + std::istringstream pathAndNameStream(pathAndNameUser); + std::deque<std::string> pathAndName; + std::string curDir; + while (std::getline(pathAndNameStream, curDir, '/')) { + pathAndName.push_back(curDir); + } + return pathAndName; +} + +// helper function that checks if name of histogram is reasonable and keeps track of names already in use +void HistogramRegistry::registerName(const std::string& name) +{ + if (name.empty() || name.back() == '/') { + LOGF(FATAL, "Invalid name for a histogram."); + } + std::deque<std::string> path = splitPath(name); + std::string cumulativeName{}; + int depth = path.size(); + for (auto& step : path) { + if (step.empty()) { + LOGF(FATAL, R"(Found empty group name in path for histogram "%s".)", name); + } + cumulativeName += step; + for (auto& curName : mRegisteredNames) { + // there is already a histogram where we want to put a folder or histogram + if (cumulativeName == curName) { + LOGF(FATAL, R"(Histogram name "%s" is not compatible with existing names.)", name); + } + // for the full new histogram name we need to check that none of the existing histograms already uses this as a group name + if (depth == 1) { + if (curName.rfind(cumulativeName, 0) == 0 && curName.size() > cumulativeName.size() && curName.at(cumulativeName.size()) == '/') { + LOGF(FATAL, R"(Histogram name "%s" is not compatible with existing names.)", name); + } + } + } + --depth; + cumulativeName += "/"; + } + mRegisteredNames.push_back(name); +} + +} // namespace o2::framework diff --git a/Framework/Core/src/InputRecord.cxx b/Framework/Core/src/InputRecord.cxx index 70505dca6646d..48ff01ddda2f5 100644 --- a/Framework/Core/src/InputRecord.cxx +++ b/Framework/Core/src/InputRecord.cxx @@ -26,9 +26,7 @@ #pragma GCC diagnostic pop #endif -namespace o2 -{ -namespace framework +namespace o2::framework { InputRecord::InputRecord(std::vector<InputRoute> const& inputsSchema, @@ -80,5 +78,13 @@ bool InputRecord::isValid(int s) const return true; } -} // namespace framework -} // namespace o2 +size_t InputRecord::countValidInputs() const +{ + size_t count = 0; + for (auto&& _ : *this) { + ++count; + } + return count; +} + +} // namespace o2::framework diff --git a/Framework/Core/src/InputSpec.cxx b/Framework/Core/src/InputSpec.cxx index f67d8bb047da3..d52adfc24564c 100644 --- a/Framework/Core/src/InputSpec.cxx +++ b/Framework/Core/src/InputSpec.cxx @@ -12,44 +12,67 @@ #include "Framework/DataSpecUtils.h" #include <variant> +#include <vector> namespace o2 { namespace framework { -InputSpec::InputSpec(std::string binding_, ConcreteDataMatcher const& concrete, enum Lifetime lifetime_) +InputSpec::InputSpec(std::string binding_, + ConcreteDataMatcher const& concrete, + enum Lifetime lifetime_, + std::vector<ConfigParamSpec> const& metadata_) : binding{binding_}, matcher{concrete}, - lifetime{lifetime_} + lifetime{lifetime_}, + metadata{metadata_} { } -InputSpec::InputSpec(std::string binding_, header::DataOrigin origin_, header::DataDescription description_, header::DataHeader::SubSpecificationType subSpec_, enum Lifetime lifetime_) +InputSpec::InputSpec(std::string binding_, + header::DataOrigin origin_, + header::DataDescription description_, + header::DataHeader::SubSpecificationType subSpec_, + enum Lifetime lifetime_, + std::vector<ConfigParamSpec> const& metadata_) : binding{binding_}, matcher{ConcreteDataMatcher{origin_, description_, subSpec_}}, - lifetime{lifetime_} + lifetime{lifetime_}, + metadata{metadata_} { } -InputSpec::InputSpec(std::string binding_, header::DataOrigin origin_, header::DataDescription description_, enum Lifetime lifetime_) +InputSpec::InputSpec(std::string binding_, + header::DataOrigin origin_, + header::DataDescription description_, + enum Lifetime lifetime_, + std::vector<ConfigParamSpec> const& metadata_) : binding{binding_}, matcher{ConcreteDataMatcher{origin_, description_, 0}}, - lifetime{lifetime_} + lifetime{lifetime_}, + metadata{metadata_} { } -InputSpec::InputSpec(std::string binding_, ConcreteDataTypeMatcher const& dataType, enum Lifetime lifetime_) +InputSpec::InputSpec(std::string binding_, + ConcreteDataTypeMatcher const& dataType, + enum Lifetime lifetime_, + std::vector<ConfigParamSpec> const& metadata_) : binding{binding_}, matcher{DataSpecUtils::dataDescriptorMatcherFrom(dataType)}, - lifetime{lifetime_} + lifetime{lifetime_}, + metadata{metadata_} { } -InputSpec::InputSpec(std::string binding_, data_matcher::DataDescriptorMatcher&& matcher_) +InputSpec::InputSpec(std::string binding_, + data_matcher::DataDescriptorMatcher&& matcher_, + std::vector<ConfigParamSpec> const& metadata_) : binding{binding_}, matcher{matcher_}, - lifetime{Lifetime::Timeframe} + lifetime{Lifetime::Timeframe}, + metadata{metadata_} { } diff --git a/Framework/Core/src/Kernels.cxx b/Framework/Core/src/Kernels.cxx deleted file mode 100644 index eb5ebb4f0e578..0000000000000 --- a/Framework/Core/src/Kernels.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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/Kernels.h" -#include "Framework/ArrowTypes.h" - -#include "ArrowDebugHelpers.h" - -#include <arrow/builder.h> -#include <arrow/status.h> -#include <arrow/type.h> -#include <arrow/util/variant.h> -#include <memory> -#include <iostream> - -using namespace arrow; -using namespace arrow::compute; - -namespace o2 -{ -namespace framework -{ - -HashByColumnKernel::HashByColumnKernel(HashByColumnOptions options) - : mOptions{options} -{ -} - -Status HashByColumnKernel::Call(FunctionContext* ctx, Datum const& inputTable, Datum* hashes) -{ - if (inputTable.kind() == Datum::TABLE) { - auto table = arrow::util::get<std::shared_ptr<arrow::Table>>(inputTable.value); - auto columnIndex = table->schema()->GetFieldIndex(mOptions.columnName); - auto chunkedArray = table->column(columnIndex); - *hashes = std::move(chunkedArray); - return arrow::Status::OK(); - } - return Status::Invalid("Input Datum was not a table"); -} - -} // namespace framework -} // namespace o2 diff --git a/Framework/Core/src/LifetimeHelpers.cxx b/Framework/Core/src/LifetimeHelpers.cxx index c21214b17e6b1..7c9180a61de0e 100644 --- a/Framework/Core/src/LifetimeHelpers.cxx +++ b/Framework/Core/src/LifetimeHelpers.cxx @@ -28,9 +28,7 @@ using namespace o2::header; using namespace fair; -namespace o2 -{ -namespace framework +namespace o2::framework { namespace @@ -180,7 +178,7 @@ ExpirationHandler::Handler char* err; uint64_t overrideTimestampMilliseconds = strtoll(overrideTimestamp.c_str(), &err, 10); if (*err != 0) { - throw std::runtime_error("fetchFromCCDBCache: Unable to parse forced timestamp for conditions"); + throw runtime_error("fetchFromCCDBCache: Unable to parse forced timestamp for conditions"); } if (overrideTimestampMilliseconds) { LOGP(info, "fetchFromCCDBCache: forcing timestamp for conditions to {} milliseconds from epoch UTC", overrideTimestampMilliseconds); @@ -199,7 +197,7 @@ ExpirationHandler::Handler CURL* curl = curl_easy_init(); if (curl == nullptr) { - throw std::runtime_error("fetchFromCCDBCache: Unable to initialise CURL"); + throw runtime_error("fetchFromCCDBCache: Unable to initialise CURL"); } CURLcode res; if (overrideTimestampMilliseconds) { @@ -216,13 +214,13 @@ ExpirationHandler::Handler res = curl_easy_perform(curl); if (res != CURLE_OK) { - throw std::runtime_error(std::string("fetchFromCCDBCache: Unable to fetch ") + url + " from CCDB"); + throw runtime_error_f("fetchFromCCDBCache: Unable to fetch %s from CCDB", url.c_str()); } long responseCode; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode != 200) { - throw std::runtime_error(std::string("fetchFromCCDBCache: HTTP error ") + std::to_string(responseCode) + " while fetching " + url + " from CCDB"); + throw runtime_error_f("fetchFromCCDBCache: HTTP error %d while fetching %s from CCDB", responseCode, url.c_str()); } curl_easy_cleanup(curl); @@ -254,7 +252,7 @@ ExpirationHandler::Handler ExpirationHandler::Handler LifetimeHelpers::fetchFromQARegistry() { return [](ServiceRegistry&, PartRef& ref, uint64_t) -> void { - throw std::runtime_error("fetchFromQARegistry: Not yet implemented"); + throw runtime_error("fetchFromQARegistry: Not yet implemented"); return; }; } @@ -265,7 +263,7 @@ ExpirationHandler::Handler LifetimeHelpers::fetchFromQARegistry() ExpirationHandler::Handler LifetimeHelpers::fetchFromObjectRegistry() { return [](ServiceRegistry&, PartRef& ref, uint64_t) -> void { - throw std::runtime_error("fetchFromObjectRegistry: Not yet implemented"); + throw runtime_error("fetchFromObjectRegistry: Not yet implemented"); return; }; } @@ -303,5 +301,34 @@ ExpirationHandler::Handler LifetimeHelpers::enumerate(ConcreteDataMatcher const& return f; } -} // namespace framework -} // namespace o2 +/// Create a dummy message with the provided ConcreteDataMatcher +ExpirationHandler::Handler LifetimeHelpers::dummy(ConcreteDataMatcher const& matcher, std::string const& sourceChannel) +{ + using counter_t = int64_t; + auto counter = std::make_shared<counter_t>(0); + auto f = [matcher, counter, sourceChannel](ServiceRegistry& services, PartRef& ref, uint64_t timestamp) -> void { + // We should invoke the handler only once. + assert(!ref.header); + assert(!ref.payload); + auto& rawDeviceService = services.get<RawDeviceService>(); + + DataHeader dh; + dh.dataOrigin = matcher.origin; + dh.dataDescription = matcher.description; + dh.subSpecification = matcher.subSpec; + dh.payloadSize = 0; + dh.payloadSerializationMethod = gSerializationMethodNone; + + DataProcessingHeader dph{timestamp, 1}; + + auto&& transport = rawDeviceService.device()->GetChannel(sourceChannel, 0).Transport(); + auto channelAlloc = o2::pmr::getTransportAllocator(transport); + auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + ref.header = std::move(header); + auto payload = rawDeviceService.device()->NewMessage(0); + ref.payload = std::move(payload); + }; + return f; +} + +} // namespace o2::framework diff --git a/Framework/Core/src/MessageContext.cxx b/Framework/Core/src/MessageContext.cxx index 83945066848ae..039fbbb590849 100644 --- a/Framework/Core/src/MessageContext.cxx +++ b/Framework/Core/src/MessageContext.cxx @@ -19,7 +19,7 @@ namespace framework FairMQMessagePtr MessageContext::createMessage(const std::string& channel, int index, size_t size) { - return proxy().getDevice()->NewMessageFor(channel, 0, size); + return proxy().getDevice()->NewMessageFor(channel, 0, size, fair::mq::Alignment{64}); } FairMQMessagePtr MessageContext::createMessage(const std::string& channel, int index, void* data, size_t size, fairmq_free_fn* ffn, void* hint) diff --git a/Framework/Core/src/PropertyTreeHelpers.cxx b/Framework/Core/src/PropertyTreeHelpers.cxx index 70b9c21fd3d69..8c90458c31fa4 100644 --- a/Framework/Core/src/PropertyTreeHelpers.cxx +++ b/Framework/Core/src/PropertyTreeHelpers.cxx @@ -14,9 +14,11 @@ #include <boost/property_tree/ptree.hpp> #include <boost/program_options/variables_map.hpp> +#include <boost/lexical_cast.hpp> #include <vector> #include <string> +#include <regex> namespace o2::framework { @@ -25,8 +27,32 @@ void PropertyTreeHelpers::populateDefaults(std::vector<ConfigParamSpec> const& s boost::property_tree::ptree& pt, boost::property_tree::ptree& provenance) { - for (auto& spec : schema) { - std::string key = spec.name.substr(0, spec.name.find(",")); + auto addBranch = [&](std::string const& key, auto* values, size_t size) { + boost::property_tree::ptree branch; + for (auto i = 0u; i < size; ++i) { + boost::property_tree::ptree leaf; + leaf.put("", values[i]); + branch.push_back(std::make_pair("", leaf)); + } + pt.put_child(key, branch); + }; + + auto addSubTree = [&](std::string const& key, auto const& m) { + boost::property_tree::ptree subtree; + for (auto i = 0u; i < m.rows; ++i) { + boost::property_tree::ptree branch; + for (auto j = 0u; j < m.cols; ++j) { + boost::property_tree::ptree leaf; + leaf.put("", m(i, j)); + branch.push_back(std::make_pair("", leaf)); + } + subtree.push_back(std::make_pair("", branch)); + } + pt.put_child(key, subtree); + }; + + for (auto const& spec : schema) { + std::string key = spec.name.substr(0, spec.name.find(',')); try { if (spec.defaultValue.type() == VariantType::Empty) { continue; @@ -50,6 +76,30 @@ void PropertyTreeHelpers::populateDefaults(std::vector<ConfigParamSpec> const& s case VariantType::Bool: pt.put(key, spec.defaultValue.get<bool>()); break; + case VariantType::ArrayInt: + addBranch(key, spec.defaultValue.get<int*>(), spec.defaultValue.size()); + break; + case VariantType::ArrayFloat: + addBranch(key, spec.defaultValue.get<float*>(), spec.defaultValue.size()); + break; + case VariantType::ArrayDouble: + addBranch(key, spec.defaultValue.get<double*>(), spec.defaultValue.size()); + break; + case VariantType::ArrayBool: + addBranch(key, spec.defaultValue.get<bool*>(), spec.defaultValue.size()); + break; + case VariantType::ArrayString: + addBranch(key, spec.defaultValue.get<std::string*>(), spec.defaultValue.size()); + break; + case VariantType::MatrixInt: + addSubTree(key, spec.defaultValue.get<Array2D<int>>()); + break; + case VariantType::MatrixFloat: + addSubTree(key, spec.defaultValue.get<Array2D<float>>()); + break; + case VariantType::MatrixDouble: + addSubTree(key, spec.defaultValue.get<Array2D<double>>()); + break; case VariantType::Unknown: case VariantType::Empty: default: @@ -57,7 +107,7 @@ void PropertyTreeHelpers::populateDefaults(std::vector<ConfigParamSpec> const& s } provenance.put(key, "default"); } catch (std::runtime_error& re) { - throw re; + throw; } catch (std::exception& e) { throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { @@ -66,14 +116,103 @@ void PropertyTreeHelpers::populateDefaults(std::vector<ConfigParamSpec> const& s } } +namespace +{ +template <typename T> +std::vector<T> toVector(std::string const& input) +{ + std::vector<T> result; + //check if the array string has correct array type symbol + assert(input[0] == variant_array_symbol<T>::symbol); + std::regex nmatch(R"((?:(?!=,)|(?!=\[))[+-]?\d+\.?\d*(?:[eE][+-]?\d+)?(?=,|\]))"); + auto end = std::sregex_iterator(); + auto values = std::sregex_iterator(input.begin(), input.end(), nmatch); + for (auto& v = values; v != end; ++v) { + result.push_back(boost::lexical_cast<T>(v->str())); + } + return result; +} + +template <> +std::vector<std::string> toVector(std::string const& input) +{ + std::vector<std::string> result; + //check if the array string has correct array type symbol + assert(input[0] == variant_array_symbol<std::string>::symbol); + std::regex smatch(R"((?:(?!=,)|(?!=\[))\w+(?=,|\]))"); + auto end = std::sregex_iterator(); + auto values = std::sregex_iterator(input.begin(), input.end(), smatch); + for (auto v = values; v != end; ++v) { + result.push_back(v->str()); + } + return result; +} + +template <typename T> +Array2D<T> toMatrix(std::string const& input) +{ + std::vector<T> cache; + assert(input[0] == variant_array_symbol<T>::symbol); + std::regex mrows(R"(\[[^\[\]]+\])"); + std::regex marray(R"((?:(?!=,)|(?!=\[))[+-]?\d+\.?\d*(?:[eE][+-]?\d+)?(?=,|\]))"); + auto end = std::sregex_iterator(); + auto rows = std::sregex_iterator(input.begin(), input.end(), mrows); + uint32_t nrows = 0; + uint32_t ncols = 0; + bool first = true; + for (auto& row = rows; row != end; ++row) { + auto str = row->str(); + auto values = std::sregex_iterator(str.begin(), str.end(), marray); + if (first) { + ncols = 0; + } + for (auto& v = values; v != end; ++v) { + cache.push_back(boost::lexical_cast<T>(v->str())); + if (first) { + ++ncols; + } + } + if (first) { + first = false; + } + ++nrows; + } + return Array2D<T>{cache, nrows, ncols}; +} +} // namespace + void PropertyTreeHelpers::populate(std::vector<ConfigParamSpec> const& schema, boost::property_tree::ptree& pt, boost::program_options::variables_map const& vmap, boost::property_tree::ptree& provenance) { - for (auto& spec : schema) { + auto addBranch = [&](std::string const& key, auto vector) { + boost::property_tree::ptree branch; + for (auto i = 0u; i < vector.size(); ++i) { + boost::property_tree::ptree leaf; + leaf.put("", vector[i]); + branch.push_back(std::make_pair("", leaf)); + } + pt.put_child(key, branch); + }; + + auto addSubTree = [&](std::string const& key, auto m) { + boost::property_tree::ptree subtree; + for (auto i = 0u; i < m.rows; ++i) { + boost::property_tree::ptree branch; + for (auto j = 0u; j < m.cols; ++j) { + boost::property_tree::ptree leaf; + leaf.put("", m(i, j)); + branch.push_back(std::make_pair("", leaf)); + } + subtree.push_back(std::make_pair("", branch)); + } + pt.put_child(key, subtree); + }; + + for (auto const& spec : schema) { // strip short version to get the correct key - std::string key = spec.name.substr(0, spec.name.find(",")); + std::string key = spec.name.substr(0, spec.name.find(',')); if (vmap.count(key) == 0) { continue; } @@ -92,13 +231,53 @@ void PropertyTreeHelpers::populate(std::vector<ConfigParamSpec> const& schema, pt.put(key, vmap[key].as<double>()); break; case VariantType::String: - if (auto v = boost::any_cast<std::string>(&vmap[key].value())) { + if (auto const* v = boost::any_cast<std::string>(&vmap[key].value())) { pt.put(key, *v); } break; case VariantType::Bool: pt.put(key, vmap[key].as<bool>()); break; + case VariantType::ArrayInt: { + auto v = toVector<int>(vmap[key].as<std::string>()); + addBranch(key, v); + }; + break; + case VariantType::ArrayFloat: { + auto v = toVector<float>(vmap[key].as<std::string>()); + addBranch(key, v); + }; + break; + case VariantType::ArrayDouble: { + auto v = toVector<double>(vmap[key].as<std::string>()); + addBranch(key, v); + }; + break; + case VariantType::ArrayBool: { + auto v = toVector<bool>(vmap[key].as<std::string>()); + addBranch(key, v); + }; + break; + case VariantType::ArrayString: { + auto v = toVector<std::string>(vmap[key].as<std::string>()); + addBranch(key, v); + }; + break; + case VariantType::MatrixInt: { + auto m = toMatrix<int>(vmap[key].as<std::string>()); + addSubTree(key, m); + }; + break; + case VariantType::MatrixFloat: { + auto m = toMatrix<float>(vmap[key].as<std::string>()); + addSubTree(key, m); + }; + break; + case VariantType::MatrixDouble: { + auto m = toMatrix<double>(vmap[key].as<std::string>()); + addSubTree(key, m); + }; + break; case VariantType::Unknown: case VariantType::Empty: default: @@ -106,7 +285,7 @@ void PropertyTreeHelpers::populate(std::vector<ConfigParamSpec> const& schema, } provenance.put(key, "fairmq"); } catch (std::runtime_error& re) { - throw re; + throw; } catch (std::exception& e) { throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { @@ -121,9 +300,9 @@ void PropertyTreeHelpers::populate(std::vector<ConfigParamSpec> const& schema, boost::property_tree::ptree& provenance, std::string const& provenanceLabel) { - for (auto& spec : schema) { + for (auto const& spec : schema) { // strip short version to get the correct key - std::string key = spec.name.substr(0, spec.name.find(",")); + std::string key = spec.name.substr(0, spec.name.find(',')); auto it = in.get_child_optional(key); if (!it) { continue; @@ -148,6 +327,16 @@ void PropertyTreeHelpers::populate(std::vector<ConfigParamSpec> const& schema, case VariantType::Bool: pt.put(key, (*it).get_value<bool>()); break; + case VariantType::ArrayInt: + case VariantType::ArrayFloat: + case VariantType::ArrayDouble: + case VariantType::ArrayBool: + case VariantType::ArrayString: + case VariantType::MatrixInt: + case VariantType::MatrixFloat: + case VariantType::MatrixDouble: + pt.put_child(key, *it); + break; case VariantType::Unknown: case VariantType::Empty: default: @@ -155,7 +344,7 @@ void PropertyTreeHelpers::populate(std::vector<ConfigParamSpec> const& schema, } provenance.put(key, provenanceLabel); } catch (std::runtime_error& re) { - throw re; + throw; } catch (std::exception& e) { throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { @@ -186,15 +375,4 @@ void PropertyTreeHelpers::traverse(const boost::property_tree::ptree& parent, Pr traverseRecursive(parent, "", parent, method); } -void PropertyTreeHelpers::merge(boost::property_tree::ptree& dest, - boost::property_tree::ptree const& source, - boost::property_tree::ptree::path_type const& mergePoint) -{ - using boost::property_tree::ptree; - WalkerFunction merge = [&dest, &mergePoint](ptree const& parent, ptree::path_type childPath, ptree const& child) { - dest.put(mergePoint / childPath, child.data()); - }; - traverse(source, merge); -} - } // namespace o2::framework diff --git a/Framework/Core/src/ReadoutAdapter.cxx b/Framework/Core/src/ReadoutAdapter.cxx index 65642b6040094..a002d3b8ff3ad 100644 --- a/Framework/Core/src/ReadoutAdapter.cxx +++ b/Framework/Core/src/ReadoutAdapter.cxx @@ -13,8 +13,6 @@ #include "Framework/DataSpecUtils.h" #include "Headers/DataHeader.h" -#include <Common/DataBlock.h> - namespace o2 { namespace framework diff --git a/Framework/Core/src/ResourcesMonitoringHelper.cxx b/Framework/Core/src/ResourcesMonitoringHelper.cxx new file mode 100644 index 0000000000000..0b6ed333052af --- /dev/null +++ b/Framework/Core/src/ResourcesMonitoringHelper.cxx @@ -0,0 +1,163 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "ResourcesMonitoringHelper.h" +#include "Framework/DeviceMetricsInfo.h" +#include <boost/property_tree/json_parser.hpp> +#include <fstream> +#include <string_view> +#include <algorithm> +#include <cassert> + +using namespace o2::framework; + +template <typename T> +inline static T retriveValue(T val) +{ + return val; +} + +inline static std::string retriveValue(const std::reference_wrapper<const StringMetric> val) +{ + return std::string(val.get().data); +} + +template <typename T> +boost::property_tree::ptree fillNodeWithValue(const DeviceMetricsInfo& deviceMetrics, + const T& metricsStorage, size_t labelIndex, size_t storageIndex) +{ + + unsigned int loopRange = std::min(deviceMetrics.metrics[labelIndex].filledMetrics, metricsStorage[storageIndex].size()); + boost::property_tree::ptree metricNode; + + for (unsigned int idx = 0; idx < loopRange; ++idx) { + boost::property_tree::ptree values; + values.add("timestamp", deviceMetrics.timestamps[labelIndex][idx]); + if constexpr (std::is_arithmetic_v<T>) { + values.add("value", std::to_string(retriveValue(std::cref(metricsStorage[storageIndex][idx])))); + } else { + values.add("value", retriveValue(std::cref(metricsStorage[storageIndex][idx]))); + } + metricNode.push_back(std::make_pair("", values)); + } + return metricNode; +} + +bool ResourcesMonitoringHelper::dumpMetricsToJSON(const std::vector<DeviceMetricsInfo>& metrics, + const DeviceMetricsInfo& driverMetrics, + const std::vector<DeviceSpec>& specs, + std::vector<std::string> const& performanceMetrics) noexcept +{ + + assert(metrics.size() == specs.size()); + + if (metrics.empty()) { + return false; + } + + boost::property_tree::ptree root; + for (unsigned int idx = 0; idx < metrics.size(); ++idx) { + + const auto& deviceMetrics = metrics[idx]; + boost::property_tree::ptree deviceRoot; + + for (const auto& metricLabel : deviceMetrics.metricLabelsIdx) { + + //check if we are interested + if (std::find(std::begin(performanceMetrics), std::end(performanceMetrics), metricLabel.label) == std::end(performanceMetrics)) { + continue; + } + + //if so + + boost::property_tree::ptree metricNode; + + switch (deviceMetrics.metrics[metricLabel.index].type) { + case MetricType::Int: + metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.intMetrics, + metricLabel.index, deviceMetrics.metrics[metricLabel.index].storeIdx); + break; + + case MetricType::Float: + metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.floatMetrics, + metricLabel.index, deviceMetrics.metrics[metricLabel.index].storeIdx); + break; + + case MetricType::String: + metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.stringMetrics, + metricLabel.index, deviceMetrics.metrics[metricLabel.index].storeIdx); + break; + + case MetricType::Uint64: + metricNode = fillNodeWithValue(deviceMetrics, deviceMetrics.uint64Metrics, + metricLabel.index, deviceMetrics.metrics[metricLabel.index].storeIdx); + break; + + default: + continue; + } + deviceRoot.add_child(metricLabel.label, metricNode); + } + + root.add_child(specs[idx].id, deviceRoot); + } + + boost::property_tree::ptree driverRoot; + for (const auto& metricLabel : driverMetrics.metricLabelsIdx) { + + //check if we are interested + if (std::find(std::begin(performanceMetrics), std::end(performanceMetrics), metricLabel.label) == std::end(performanceMetrics)) { + continue; + } + + //if so + + boost::property_tree::ptree metricNode; + + switch (driverMetrics.metrics[metricLabel.index].type) { + case MetricType::Int: + metricNode = fillNodeWithValue(driverMetrics, driverMetrics.intMetrics, + metricLabel.index, driverMetrics.metrics[metricLabel.index].storeIdx); + break; + + case MetricType::Float: + metricNode = fillNodeWithValue(driverMetrics, driverMetrics.floatMetrics, + metricLabel.index, driverMetrics.metrics[metricLabel.index].storeIdx); + break; + + case MetricType::String: + metricNode = fillNodeWithValue(driverMetrics, driverMetrics.stringMetrics, + metricLabel.index, driverMetrics.metrics[metricLabel.index].storeIdx); + break; + + case MetricType::Uint64: + metricNode = fillNodeWithValue(driverMetrics, driverMetrics.uint64Metrics, + metricLabel.index, driverMetrics.metrics[metricLabel.index].storeIdx); + break; + + default: + continue; + } + driverRoot.add_child(metricLabel.label, metricNode); + } + + root.add_child("driver", driverRoot); + + std::ofstream file("performanceMetrics.json", std::ios::out); + if (file.is_open()) { + boost::property_tree::json_parser::write_json(file, root); + } else { + return false; + } + + file.close(); + + return true; +} diff --git a/Framework/Core/src/ResourcesMonitoringHelper.h b/Framework/Core/src/ResourcesMonitoringHelper.h new file mode 100644 index 0000000000000..791c475b31d7d --- /dev/null +++ b/Framework/Core/src/ResourcesMonitoringHelper.h @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_RESOURCESMONITORINGHELPER_H_ +#define O2_FRAMEWORK_RESOURCESMONITORINGHELPER_H_ + +#include "Framework/DeviceMetricsInfo.h" +#include "Monitoring/ProcessMonitor.h" +#include <boost/property_tree/ptree.hpp> +#include "Framework/DeviceSpec.h" + +#include <vector> +#include <type_traits> + +namespace o2::framework +{ + +struct ResourcesMonitoringHelper { + /// Dump the metrics in @a metrics which match the names specified in @a metricsToDump + /// @a specs are the DeviceSpecs associated to the metrics. + static bool dumpMetricsToJSON(std::vector<DeviceMetricsInfo> const& metrics, + DeviceMetricsInfo const& driverMetrics, + std::vector<DeviceSpec> const& specs, + std::vector<std::string> const& metricsToDump) noexcept; + static bool isResourcesMonitoringEnabled(unsigned short interval) noexcept { return interval > 0; } +}; + + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_RESOURCESMONITORINGHELPER_H_ diff --git a/Framework/Core/src/RootConfigParamHelpers.cxx b/Framework/Core/src/RootConfigParamHelpers.cxx index 171722e62d3f8..20fc32e88ffd8 100644 --- a/Framework/Core/src/RootConfigParamHelpers.cxx +++ b/Framework/Core/src/RootConfigParamHelpers.cxx @@ -10,6 +10,7 @@ #include "Framework/RootConfigParamHelpers.h" #include "Framework/ConfigParamSpec.h" +#include "Framework/StringHelpers.h" #include <TClass.h> #include <TDataMember.h> #include <TDataType.h> @@ -35,14 +36,18 @@ bool isString(TDataMember const& dm) // a generic looper of data members of a TClass; calling a callback // reused in various functions below void loopOverMembers(TClass* cl, void* obj, - std::function<void(const TDataMember*, int, int)>&& callback) + std::function<void(TDataMember*, int, int)>&& callback) { - auto memberlist = cl->GetListOfDataMembers(); + auto* memberlist = cl->GetListOfDataMembers(); for (int i = 0; i < memberlist->GetEntries(); ++i) { - auto dm = (TDataMember*)memberlist->At(i); + auto* dm = (TDataMember*)memberlist->At(i); auto isValidComplex = [dm]() { - return isString(*dm) || dm->IsEnum(); + auto typehash = compile_time_hash(dm->GetTypeName()); + return isString(*dm) || dm->IsEnum() || dm->IsSTLContainer() || + (typehash == compile_time_hash("o2::framework::Array2D<int>")) || + (typehash == compile_time_hash("o2::framework::Array2D<float>")) || + (typehash == compile_time_hash("o2::framework::Array2D<double>")); }; // filter out static members for now @@ -70,6 +75,43 @@ void loopOverMembers(TClass* cl, void* obj, } } +namespace +{ +template <typename T> +std::vector<T> extractVector(boost::property_tree::ptree const& tree) +{ + std::vector<T> result(tree.size()); + auto count = 0U; + for (auto const& entry : tree) { + result[count++] = entry.second.get_value<T>(); + } + return result; +} + +template <typename T> +Array2D<T> extractMatrix(boost::property_tree::ptree const& tree) +{ + std::vector<T> cache; + uint32_t nrows = tree.size(); + uint32_t ncols = 0; + bool first = true; + auto irow = 0u; + for (auto const& row : tree) { + if (first) { + ncols = row.second.size(); + first = false; + } + auto icol = 0u; + for (auto const& entry : row.second) { + cache.push_back(entry.second.get_value<T>()); + ++icol; + } + ++irow; + } + return Array2D<T>{cache, nrows, ncols}; +} +} // namespace + // construct name (in dependence on vector or scalar data and index) std::string getName(const TDataMember* dm, int index, int size) { @@ -83,10 +125,44 @@ std::string getName(const TDataMember* dm, int index, int size) void ptreeToMember(boost::property_tree::ptree const& value, char const* tname, - TDataMember const* dm, + TDataMember* dm, void* ptr) { - auto dt = dm->GetDataType(); + auto typehash = compile_time_hash(dm->GetTypeName()); + if (dm->IsSTLContainer()) { + switch (typehash) { + case compile_time_hash("vector<int>"): + *static_cast<std::vector<int>*>(ptr) = extractVector<int>(value); + return; + case compile_time_hash("vector<float>"): + *static_cast<std::vector<float>*>(ptr) = extractVector<float>(value); + return; + case compile_time_hash("vector<double>"): + *static_cast<std::vector<double>*>(ptr) = extractVector<double>(value); + return; + case compile_time_hash("vector<bool>"): + throw std::runtime_error("Bool arrays are not implemented yet"); + case compile_time_hash("vector<std::string>"): + case compile_time_hash("vector<string>"): + *static_cast<std::vector<std::string>*>(ptr) = extractVector<std::string>(value); + return; + default: + throw std::runtime_error("Not an int/float/double/bool vector"); + } + } else { + switch (typehash) { + case compile_time_hash("o2::framework::Array2D<int>"): + *static_cast<Array2D<int>*>(ptr) = extractMatrix<int>(value); + return; + case compile_time_hash("o2::framework::Array2D<float>"): + *static_cast<Array2D<float>*>(ptr) = extractMatrix<float>(value); + return; + case compile_time_hash("o2::framework::Array2D<double>"): + *static_cast<Array2D<double>*>(ptr) = extractMatrix<double>(value); + return; + } + } + auto* dt = dm->GetDataType(); if (dt != nullptr) { switch (dt->GetType()) { case kChar_t: { @@ -125,10 +201,7 @@ void ptreeToMember(boost::property_tree::ptree const& value, *(float*)ptr = value.get_value<float>(); return; } - case kDouble_t: { - *(double*)ptr = value.get_value<double>(); - return; - } + case kDouble_t: case kDouble32_t: { *(double*)ptr = value.get_value<double>(); return; @@ -151,16 +224,44 @@ void ptreeToMember(boost::property_tree::ptree const& value, } } // 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) { *(std::string*)ptr = value.get_value<std::string>(); } throw std::runtime_error("Unable to override value"); } // Convert a DataMember to a ConfigParamSpec -ConfigParamSpec memberToConfigParamSpec(const char* tname, TDataMember const* dm, void* ptr) +ConfigParamSpec memberToConfigParamSpec(const char* tname, TDataMember* dm, void* ptr) { - auto dt = dm->GetDataType(); + auto typehash = compile_time_hash(dm->GetTypeName()); + if (dm->IsSTLContainer()) { + switch (typehash) { + case compile_time_hash("vector<int>"): + return ConfigParamSpec{tname, VariantType::ArrayInt, *static_cast<std::vector<int>*>(ptr), {"No help"}}; + case compile_time_hash("vector<float>"): + return ConfigParamSpec{tname, VariantType::ArrayFloat, *static_cast<std::vector<float>*>(ptr), {"No help"}}; + case compile_time_hash("vector<double>"): + return ConfigParamSpec{tname, VariantType::ArrayDouble, *static_cast<std::vector<double>*>(ptr), {"No help"}}; + case compile_time_hash("vector<bool>"): + throw std::runtime_error("bool vector not supported yet"); + // return ConfigParamSpec{tname, VariantType::ArrayBool, *static_cast<std::vector<bool>*>(ptr), {"No help"}}; + case compile_time_hash("vector<std::string>"): + case compile_time_hash("vector<string>"): + return ConfigParamSpec{tname, VariantType::ArrayString, *static_cast<std::vector<std::string>*>(ptr), {"No help"}}; + default: + throw std::runtime_error("Not an int/float/double/bool vector"); + } + } else { + switch (typehash) { + case compile_time_hash("o2::framework::Array2D<int>"): + return ConfigParamSpec{tname, VariantType::MatrixInt, *static_cast<Array2D<int>*>(ptr), {"No help"}}; + case compile_time_hash("o2::framework::Array2D<float>"): + return ConfigParamSpec{tname, VariantType::MatrixFloat, *static_cast<Array2D<float>*>(ptr), {"No help"}}; + case compile_time_hash("o2::framework::Array2D<double>"): + return ConfigParamSpec{tname, VariantType::MatrixDouble, *static_cast<Array2D<double>*>(ptr), {"No help"}}; + } + } + auto* dt = dm->GetDataType(); if (dt != nullptr) { switch (dt->GetType()) { case kChar_t: { @@ -190,14 +291,12 @@ ConfigParamSpec memberToConfigParamSpec(const char* tname, TDataMember const* dm case kFloat_t: { return ConfigParamSpec{tname, VariantType::Float, *(float*)ptr, {"No help"}}; } - case kDouble_t: { - return ConfigParamSpec{tname, VariantType::Double, *(double*)ptr, {"No help"}}; - } + case kDouble_t: case kDouble32_t: { return ConfigParamSpec{tname, VariantType::Double, *(double*)ptr, {"No help"}}; } case kBool_t: { - return ConfigParamSpec{tname, VariantType::Double, *(bool*)ptr, {"No help"}}; + return ConfigParamSpec{tname, VariantType::Bool, *(bool*)ptr, {"No help"}}; } case kLong64_t: { return ConfigParamSpec{tname, VariantType::Int64, *(int64_t*)ptr, {"No help"}}; @@ -211,7 +310,7 @@ ConfigParamSpec memberToConfigParamSpec(const char* tname, TDataMember const* dm } } // 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 ConfigParamSpec{tname, VariantType::String, *(std::string*)ptr, {"No help"}}; } throw std::runtime_error("Cannot use " + std::string(tname)); @@ -226,9 +325,9 @@ std::vector<ConfigParamSpec> { std::vector<ConfigParamSpec> specs; - auto toDataMember = [&mainKey, &specs, obj](const TDataMember* dm, int index, int size) { - auto dt = dm->GetDataType(); - auto TS = dt ? dt->Size() : 0; + auto toDataMember = [&mainKey, &specs, obj](TDataMember* dm, int index, int size) { + auto* dt = dm->GetDataType(); + auto TS = dt != nullptr ? dt->Size() : 0; char* ptr = ((char*)obj) + dm->GetOffset() + index * TS; const std::string name = mainKey + "." + getName(dm, index, size); @@ -243,9 +342,9 @@ std::vector<ConfigParamSpec> /// using the values in the ptree to override, where appropriate. void RootConfigParamHelpers::fillFromPtree(TClass* cl, void* obj, boost::property_tree::ptree const& pt) { - auto toDataMember = [obj, &pt](const TDataMember* dm, int index, int size) { - auto dt = dm->GetDataType(); - auto TS = dt ? dt->Size() : 0; + auto toDataMember = [obj, &pt](TDataMember* dm, int index, int size) { + auto* dt = dm->GetDataType(); + auto TS = dt != nullptr ? dt->Size() : 0; char* ptr = ((char*)obj) + dm->GetOffset() + index * TS; const std::string name = getName(dm, index, size); auto it = pt.get_child_optional(name); diff --git a/Framework/Core/src/ServiceRegistry.cxx b/Framework/Core/src/ServiceRegistry.cxx new file mode 100644 index 0000000000000..55d25a6abd67d --- /dev/null +++ b/Framework/Core/src/ServiceRegistry.cxx @@ -0,0 +1,155 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/ServiceRegistry.h" +#include "Framework/Tracing.h" +#include "Framework/Logger.h" +#include <iostream> + +namespace o2::framework +{ + +ServiceRegistry::ServiceRegistry() +{ + mServicesKey.fill(0L); + mServicesValue.fill(nullptr); + for (size_t i = 0; i < mServicesBooked.size(); ++i) { + mServicesBooked[i] = false; + } +} + +/// Type erased service registration. @a typeHash is the +/// hash used to identify the service, @a service is +/// a type erased pointer to the service itself. +/// This method is supposed to be thread safe +void ServiceRegistry::registerService(hash_type typeHash, void* service, ServiceKind kind, uint64_t threadId, const char* name) const +{ + hash_type id = typeHash & MAX_SERVICES_MASK; + hash_type threadHashId = (typeHash ^ threadId) & MAX_SERVICES_MASK; + // If kind is not stream, there is only one copy of our service. + // So we look if it is already registered and reused it if it is. + // If not, we register it as thread id 0 and as the passed one. + if (kind != ServiceKind::Stream && threadId != 0) { + void* oldService = this->get(typeHash, 0, kind); + if (oldService == nullptr) { + registerService(typeHash, service, kind, 0); + } else { + service = oldService; + } + } + for (uint8_t i = 0; i < MAX_DISTANCE; ++i) { + // If the service slot was not taken, take it atomically + bool expected = false; + if (mServicesBooked[i + threadHashId].compare_exchange_strong(expected, true, + std::memory_order_seq_cst)) { + mServicesValue[i + threadHashId] = service; + mServicesMeta[i + threadHashId] = ServiceMeta{kind, threadId}; + mServicesKey[i + threadHashId] = typeHash; + std::atomic_thread_fence(std::memory_order_release); + return; + } + } + throw std::runtime_error(std::string("Unable to find a spot in the registry for service ") + + std::to_string(typeHash) + + ". Make sure you use const / non-const correctly."); +} + +void ServiceRegistry::declareService(ServiceSpec const& spec, DeviceState& state, fair::mq::ProgOptions& options) +{ + mSpecs.push_back(spec); + // Services which are not stream must have a single instance created upfront. + if (spec.kind != ServiceKind::Stream) { + ServiceHandle handle = spec.init(*this, state, options); + this->registerService(handle.hash, handle.instance, handle.kind, 0, handle.name.c_str()); + this->bindService(spec, handle.instance); + } +} + +void ServiceRegistry::bindService(ServiceSpec const& spec, void* service) +{ + static TracyLockableN(std::mutex, bindMutex, "bind mutex"); + std::scoped_lock<LockableBase(std::mutex)> lock(bindMutex); + if (spec.preProcessing) { + mPreProcessingHandles.push_back(ServiceProcessingHandle{spec.preProcessing, service}); + } + if (spec.postProcessing) { + mPostProcessingHandles.push_back(ServiceProcessingHandle{spec.postProcessing, service}); + } + if (spec.preDangling) { + mPreDanglingHandles.push_back(ServiceDanglingHandle{spec.preDangling, service}); + } + if (spec.postDangling) { + mPostDanglingHandles.push_back(ServiceDanglingHandle{spec.postDangling, service}); + } + if (spec.preEOS) { + mPreEOSHandles.push_back(ServiceEOSHandle{spec.preEOS, service}); + } + if (spec.postEOS) { + mPostEOSHandles.push_back(ServiceEOSHandle{spec.postEOS, service}); + } + if (spec.postDispatching) { + mPostDispatchingHandles.push_back(ServiceDispatchingHandle{spec.postDispatching, service}); + } +} + +/// Invoke callbacks to be executed before every process method invokation +void ServiceRegistry::preProcessingCallbacks(ProcessingContext& processContext) +{ + for (auto& handle : mPreProcessingHandles) { + handle.callback(processContext, handle.service); + } +} +/// Invoke callbacks to be executed after every process method invokation +void ServiceRegistry::postProcessingCallbacks(ProcessingContext& processContext) +{ + for (auto& handle : mPostProcessingHandles) { + handle.callback(processContext, handle.service); + } +} +/// Invoke callbacks to be executed before every dangling check +void ServiceRegistry::preDanglingCallbacks(DanglingContext& danglingContext) +{ + for (auto preDanglingHandle : mPreDanglingHandles) { + preDanglingHandle.callback(danglingContext, preDanglingHandle.service); + } +} + +/// Invoke callbacks to be executed after every dangling check +void ServiceRegistry::postDanglingCallbacks(DanglingContext& danglingContext) +{ + for (auto postDanglingHandle : mPostDanglingHandles) { + postDanglingHandle.callback(danglingContext, postDanglingHandle.service); + } +} + +/// Invoke callbacks to be executed before every EOS user callback invokation +void ServiceRegistry::preEOSCallbacks(EndOfStreamContext& eosContext) +{ + for (auto& eosHandle : mPreEOSHandles) { + eosHandle.callback(eosContext, eosHandle.service); + } +} + +/// Invoke callbacks to be executed after every EOS user callback invokation +void ServiceRegistry::postEOSCallbacks(EndOfStreamContext& eosContext) +{ + for (auto& eosHandle : mPostEOSHandles) { + eosHandle.callback(eosContext, eosHandle.service); + } +} + +/// Invoke callbacks to be executed after every data Dispatching +void ServiceRegistry::postDispatchingCallbacks(ProcessingContext& processContext) +{ + for (auto& dispatchingHandle : mPostDispatchingHandles) { + dispatchingHandle.callback(processContext, dispatchingHandle.service); + } +} + +} // namespace o2::framework diff --git a/Framework/Core/src/SimpleRawDeviceService.cxx b/Framework/Core/src/SimpleRawDeviceService.cxx new file mode 100644 index 0000000000000..daf1cb54e0c4d --- /dev/null +++ b/Framework/Core/src/SimpleRawDeviceService.cxx @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/SimpleRawDeviceService.h" + +#include <FairMQDevice.h> + +namespace o2::framework +{ + +void SimpleRawDeviceService::waitFor(unsigned int ms) +{ + mDevice->WaitFor(std::chrono::milliseconds(ms)); +} + +} // namespace o2::framework diff --git a/Framework/Core/src/SimpleResourceManager.cxx b/Framework/Core/src/SimpleResourceManager.cxx index 99961dd41b4e5..7becac3926160 100644 --- a/Framework/Core/src/SimpleResourceManager.cxx +++ b/Framework/Core/src/SimpleResourceManager.cxx @@ -9,12 +9,11 @@ // or submit itself to any jurisdiction. #include "SimpleResourceManager.h" #include "Framework/ComputingResource.h" +#include <fmt/format.h> #include <exception> #include <stdexcept> -namespace o2 -{ -namespace framework +namespace o2::framework { /// The simplest implementation of this allocates mMaxPorts ports starting from @@ -66,9 +65,14 @@ void SimpleResourceManager::notifyAcceptedOffer(ComputingOffer const& offer) } if (resourceFound == false) { - throw std::runtime_error("Could not match offer to original resource."); + std::string resources = "Available resources:\n"; + for (auto& resource : mResources) { + resources += fmt::format("- ({}, {}, {}, {})\n", resource.hostname.c_str(), resource.cpu, resource.memory, resource.startPort); + } + throw std::runtime_error(fmt::format("Could not match offer (host:{}, cpu:{}, mem:{}, ports:{}) to original resource.\n", + offer.hostname, offer.cpu, offer.memory, offer.rangeSize) + + resources); } } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/StepTHn.cxx b/Framework/Core/src/StepTHn.cxx new file mode 100644 index 0000000000000..9bc3fe8391138 --- /dev/null +++ b/Framework/Core/src/StepTHn.cxx @@ -0,0 +1,366 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Use StepTHn instead of THn and your memory consumption will be drastically reduced +// Once you have the merged output, use getTHn() to get a standard histogram +// +// this storage container is optimized for small memory usage +// under/over flow bins do not exist +// sumw2 structure is float only and only create when the weight != 1 +// +// Templated version allows also the use of double as storage container + +#include "Framework/StepTHn.h" +#include "TList.h" +#include "TCollection.h" +#include "TArrayF.h" +#include "TArrayD.h" +#include "THn.h" +#include "TMath.h" + +ClassImp(StepTHn) + templateClassImp(StepTHnT) + + StepTHn::StepTHn() : mNBins(0), + mNVars(0), + mNSteps(0), + mValues(nullptr), + mSumw2(nullptr), + mTarget(nullptr), + mAxisCache(nullptr), + mNbinsCache(nullptr), + mLastVars(nullptr), + mLastBins(nullptr), + mPrototype(nullptr) +{ + // Default constructor (for streaming) +} + +StepTHn::StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, Int_t* nBins, std::vector<Double_t> binEdges[], const char** axisTitles) : TNamed(name, title), + mNBins(0), + mNVars(nAxis), + mNSteps(nSteps), + mValues(nullptr), + mSumw2(nullptr), + mTarget(nullptr), + mAxisCache(nullptr), + mNbinsCache(nullptr), + mLastVars(nullptr), + mLastBins(nullptr), + mPrototype(nullptr) +{ + // Constructor + // + // This will create a container for <nSteps> steps. The memory for such a step is only allocated once the first value is filled. + // Therefore you can easily create many steps which are only filled under certain analysis settings. + // For each step a <nAxis> dimensional histogram is created. + // The axis have <nBins[i]> bins. The bin edges are given in <binEdges[i]>. If there are only two bin edges, equidistant binning is set. + + init(); +} + +template <class TemplateArray> +StepTHnT<TemplateArray>::StepTHnT(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxis, + Int_t* nBins, std::vector<Double_t> binEdges[], const char** axisTitles) : StepTHn(name, title, nSteps, nAxis, nBins, binEdges, axisTitles) +{ + mNBins = 1; + for (Int_t i = 0; i < mNVars; i++) { + mNBins *= nBins[i]; + } + if constexpr (std::is_same_v<TemplateArray, TArrayD>) { + mPrototype = new THnSparseD(Form("%s_sparse", name), title, nAxis, nBins); + } else { + mPrototype = new THnSparseF(Form("%s_sparse", name), title, nAxis, nBins); + } + for (Int_t i = 0; i < mNVars; i++) { + if (nBins[i] + 1 == binEdges[i].size()) { // variable-width binning + mPrototype->GetAxis(i)->Set(nBins[i], &(binEdges[i])[0]); + } else if (binEdges[i].size() == 2) { // equidistant binning + mPrototype->GetAxis(i)->Set(nBins[i], binEdges[i][0], binEdges[i][1]); + } else { + LOGF(fatal, "Invalid binning information for axis %d with %d bins and %d entries for bin edges", i, nBins[i], binEdges[i].size()); + } + LOGF(debug, "Histogram %s Axis %d created with %d bins and %d edges", name, i, nBins[i], binEdges[i].size()); + mPrototype->GetAxis(i)->SetTitle(axisTitles[i]); + } +} + +void StepTHn::init() +{ + // initialize + + mValues = new TArray*[mNSteps]; + mSumw2 = new TArray*[mNSteps]; + + for (Int_t i = 0; i < mNSteps; i++) { + mValues[i] = nullptr; + mSumw2[i] = nullptr; + } +} + +StepTHn::StepTHn(const StepTHn& c) : mNBins(c.mNBins), + mNVars(c.mNVars), + mNSteps(c.mNSteps), + mValues(new TArray*[c.mNSteps]), + mSumw2(new TArray*[c.mNSteps]), + mTarget(nullptr), + mAxisCache(nullptr), + mNbinsCache(nullptr), + mLastVars(nullptr), + mLastBins(nullptr), + mPrototype(nullptr) +{ + // + // StepTHn copy constructor + // + + ((StepTHn&)c).Copy(*this); +} + +StepTHn::~StepTHn() +{ + // Destructor + + deleteContainers(); + + delete[] mValues; + delete[] mSumw2; + delete[] mTarget; + delete[] mAxisCache; + delete[] mNbinsCache; + delete[] mLastVars; + delete[] mLastBins; + delete mPrototype; +} + +void StepTHn::deleteContainers() +{ + // delete data containers + + for (Int_t i = 0; i < mNSteps; i++) { + if (mValues && mValues[i]) { + delete mValues[i]; + mValues[i] = nullptr; + } + + if (mSumw2 && mSumw2[i]) { + delete mSumw2[i]; + mSumw2[i] = nullptr; + } + + if (mTarget && mTarget[i]) { + delete mTarget[i]; + mTarget[i] = nullptr; + } + } +} + +StepTHn& StepTHn::operator=(const StepTHn& c) +{ + // assigment operator + + if (this != &c) { + ((StepTHn&)c).Copy(*this); + } + + return *this; +} + +void StepTHn::Copy(TObject& c) const +{ + // copy function + + StepTHn& target = (StepTHn&)c; + + TNamed::Copy(c); + + target.mNBins = mNBins; + target.mNVars = mNVars; + target.mNSteps = mNSteps; + + target.init(); + + for (Int_t i = 0; i < mNSteps; i++) { + if (mValues[i]) { + target.mValues[i] = createArray(mValues[i]); + } else { + target.mValues[i] = nullptr; + } + + if (mSumw2[i]) { + target.mSumw2[i] = createArray(mSumw2[i]); + } else { + target.mSumw2[i] = nullptr; + } + } + + if (mPrototype) { + target.mPrototype = dynamic_cast<THnSparseF*>(mPrototype->Clone()); + } +} + +template <class TemplateArray> +Long64_t StepTHnT<TemplateArray>::Merge(TCollection* list) +{ + // Merge a list of StepTHn objects with this (needed for + // PROOF). + // Returns the number of merged objects (including this). + + if (!list) { + return 0; + } + + if (list->IsEmpty()) { + return 1; + } + + TIterator* iter = list->MakeIterator(); + TObject* obj; + + Int_t count = 0; + while ((obj = iter->Next())) { + + StepTHnT<TemplateArray>* entry = dynamic_cast<StepTHnT<TemplateArray>*>(obj); + if (entry == nullptr) { + continue; + } + + for (Int_t i = 0; i < mNSteps; i++) { + if (entry->mValues[i]) { + if (!mValues[i]) { + mValues[i] = createArray(); + } + + auto source = dynamic_cast<TemplateArray*>(entry->mValues[i])->GetArray(); + auto target = dynamic_cast<TemplateArray*>(mValues[i])->GetArray(); + for (Long64_t l = 0; l < mNBins; l++) { + target[l] += source[l]; + } + } + + if (entry->mSumw2[i]) { + if (!mSumw2[i]) { + mSumw2[i] = createArray(); + } + + auto source = dynamic_cast<TemplateArray*>(entry->mSumw2[i])->GetArray(); + auto target = dynamic_cast<TemplateArray*>(mSumw2[i])->GetArray(); + for (Long64_t l = 0; l < mNBins; l++) { + target[l] += source[l]; + } + } + } + + count++; + } + + return count + 1; +} + +Long64_t StepTHn::getGlobalBinIndex(const Int_t* binIdx) +{ + // calculates global bin index + // binIdx contains TAxis bin indexes + // here bin count starts at 0 because we do not have over/underflow bins + + Long64_t bin = 0; + for (Int_t i = 0; i < mNVars; i++) { + bin *= mPrototype->GetAxis(i)->GetNbins(); + bin += binIdx[i] - 1; + } + + return bin; +} + +void StepTHn::createTarget(Int_t step, Bool_t sparse) +{ + // fills the information stored in the buffer in this class into the target THn + + if (!mValues[step]) { + LOGF(fatal, "Histogram request for step %d which is empty.", step); + return; + } + + if (!mTarget) { + mTarget = new THnBase*[mNSteps]; + for (Int_t i = 0; i < mNSteps; i++) { + mTarget[i] = nullptr; + } + } + + if (mTarget[step]) { + return; + } + + TArray* source = mValues[step]; + // if mSumw2 is not stored, the sqrt of the number of bin entries in source is filled below; otherwise we use mSumw2 + TArray* sourceSumw2 = source; + if (mSumw2[step]) { + sourceSumw2 = mSumw2[step]; + } + + if (sparse) { + mTarget[step] = THnSparse::CreateSparse(Form("%s_%d", GetName(), step), Form("%s_%d", GetTitle(), step), mPrototype); + } else { + mTarget[step] = THn::CreateHn(Form("%s_%d", GetName(), step), Form("%s_%d", GetTitle(), step), mPrototype); + } + + THnBase* target = mTarget[step]; + + Int_t* binIdx = new Int_t[mNVars]; + Int_t* nBins = new Int_t[mNVars]; + for (Int_t j = 0; j < mNVars; j++) { + binIdx[j] = 1; + nBins[j] = target->GetAxis(j)->GetNbins(); + } + + Long64_t count = 0; + + while (1) { + // for (Int_t j=0; j<mNVars; j++) + // printf("%d ", binIdx[j]); + + Long64_t globalBin = getGlobalBinIndex(binIdx); + // Printf(" --> %lld", globalBin); + + // TODO probably slow + double value = source->GetAt(globalBin); + if (value != 0) { + target->SetBinContent(binIdx, value); + target->SetBinError(binIdx, TMath::Sqrt(sourceSumw2->GetAt(globalBin))); + + count++; + } + + binIdx[mNVars - 1]++; + + for (Int_t j = mNVars - 1; j > 0; j--) { + if (binIdx[j] > nBins[j]) { + binIdx[j] = 1; + binIdx[j - 1]++; + } + } + + if (binIdx[0] > nBins[0]) { + break; + } + } + + LOGF(info, "Step %d: copied %lld entries out of %lld bins", step, count, getGlobalBinIndex(binIdx)); + + delete[] binIdx; + delete[] nBins; + + delete mValues[step]; + mValues[step] = nullptr; +} + +template class StepTHnT<TArrayF>; +template class StepTHnT<TArrayD>; diff --git a/Framework/Core/src/TableBuilder.cxx b/Framework/Core/src/TableBuilder.cxx index c52ba0dda8827..f05604c365f38 100644 --- a/Framework/Core/src/TableBuilder.cxx +++ b/Framework/Core/src/TableBuilder.cxx @@ -51,14 +51,8 @@ std::shared_ptr<arrow::Table> TableBuilder::finalize() { mFinalizer(); - std::vector<std::shared_ptr<arrow::ChunkedArray>> columns; - columns.reserve(mArrays.size()); - for (size_t i = 0; i < mSchema->num_fields(); ++i) { - auto column = std::make_shared<arrow::ChunkedArray>(mArrays[i]); - columns.emplace_back(column); - } - auto table_ = arrow::Table::Make(mSchema, columns); - return table_; + assert(mSchema->num_fields() > 0 && "Schema needs to be non-empty"); + return arrow::Table::Make(mSchema, mArrays); } } // namespace o2::framework diff --git a/Framework/Core/src/TableTreeHelpers.cxx b/Framework/Core/src/TableTreeHelpers.cxx index 4d569ba422323..cdd9054b0f90f 100644 --- a/Framework/Core/src/TableTreeHelpers.cxx +++ b/Framework/Core/src/TableTreeHelpers.cxx @@ -8,15 +8,103 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. #include "Framework/TableTreeHelpers.h" +#include <stdexcept> #include "Framework/Logger.h" #include "arrow/type_traits.h" -namespace o2 +namespace o2::framework { -namespace framework + +namespace +{ +// ----------------------------------------------------------------------------- +// TreeToTable allows to fill the contents of a given TTree to an arrow::Table +// ColumnIterator is used by TreeToTable +// +// To copy the contents of a tree tr to a table ta do: +// . TreeToTable t2t(tr); +// . t2t.addColumn(columnname1); t2t.addColumn(columnname2); ... +// OR +// t2t.addAllColumns(); +// . auto ta = t2t.process(); +// +// ............................................................................. +class ColumnIterator { + private: + // all the possible TTreeReaderValue<T> types + TTreeReaderValue<bool>* mReaderValue_o = nullptr; + TTreeReaderValue<uint8_t>* mReaderValue_ub = nullptr; + TTreeReaderValue<uint16_t>* mReaderValue_us = nullptr; + TTreeReaderValue<uint32_t>* mReaderValue_ui = nullptr; + TTreeReaderValue<ULong64_t>* mReaderValue_ul = nullptr; + TTreeReaderValue<int8_t>* mReaderValue_b = nullptr; + TTreeReaderValue<int16_t>* mReaderValue_s = nullptr; + TTreeReaderValue<int32_t>* mReaderValue_i = nullptr; + TTreeReaderValue<int64_t>* mReaderValue_l = nullptr; + TTreeReaderValue<float>* mReaderValue_f = nullptr; + TTreeReaderValue<double>* mReaderValue_d = nullptr; + + // all the possible TTreeReaderArray<T> types + TTreeReaderArray<bool>* mReaderArray_o = nullptr; + TTreeReaderArray<uint8_t>* mReaderArray_ub = nullptr; + TTreeReaderArray<uint16_t>* mReaderArray_us = nullptr; + TTreeReaderArray<uint32_t>* mReaderArray_ui = nullptr; + TTreeReaderArray<uint64_t>* mReaderArray_ul = nullptr; + TTreeReaderArray<int8_t>* mReaderArray_b = nullptr; + TTreeReaderArray<int16_t>* mReaderArray_s = nullptr; + TTreeReaderArray<int32_t>* mReaderArray_i = nullptr; + TTreeReaderArray<int64_t>* mReaderArray_l = nullptr; + TTreeReaderArray<float>* mReaderArray_f = nullptr; + TTreeReaderArray<double>* mReaderArray_d = nullptr; + + // all the possible arrow::TBuilder types + arrow::FixedSizeListBuilder* mTableBuilder_list = nullptr; + + arrow::BooleanBuilder* mTableBuilder_o = nullptr; + arrow::UInt8Builder* mTableBuilder_ub = nullptr; + arrow::UInt16Builder* mTableBuilder_us = nullptr; + arrow::UInt32Builder* mTableBuilder_ui = nullptr; + arrow::UInt64Builder* mTableBuilder_ul = nullptr; + arrow::Int8Builder* mTableBuilder_b = nullptr; + arrow::Int16Builder* mTableBuilder_s = nullptr; + arrow::Int32Builder* mTableBuilder_i = nullptr; + arrow::Int64Builder* mTableBuilder_l = nullptr; + arrow::FloatBuilder* mTableBuilder_f = nullptr; + arrow::DoubleBuilder* mTableBuilder_d = nullptr; + + bool mStatus = false; + EDataType mElementType; + int64_t mNumberElements; + const char* mColumnName; + + std::shared_ptr<arrow::Field> mField; + std::shared_ptr<arrow::Array> mArray; + + public: + ColumnIterator(TTreeReader& reader, const char* colname); + ~ColumnIterator(); + + // has the iterator been properly initialized + bool getStatus(); + + // copy the TTreeReaderValue to the arrow::TBuilder + void push(); + + // reserve enough space to push s elements without reallocating + void reserve(size_t s); + + std::shared_ptr<arrow::Array> getArray() { return mArray; } + std::shared_ptr<arrow::Field> getSchema() { return mField; } + + // finish the arrow::TBuilder + // with this mArray is prepared to be used in arrow::Table::Make + void finish(); +}; +} // namespace + // is used in TableToTree BranchIterator::BranchIterator(TTree* tree, std::shared_ptr<arrow::ChunkedArray> col, std::shared_ptr<arrow::Field> field) { @@ -33,11 +121,10 @@ BranchIterator::BranchIterator(TTree* tree, std::shared_ptr<arrow::ChunkedArray> if (mFieldType == arrow::Type::type::FIXED_SIZE_LIST) { // element type - if (mField->type()->num_children() <= 0) { + if (mField->type()->num_fields() <= 0) { LOGP(FATAL, "Field {} of type {} has no children!", mField->name(), mField->type()->ToString().c_str()); } - mElementType = mField->type()->child(0)->type()->id(); - + mElementType = mField->type()->field(0)->type()->id(); // number of elements mNumberElements = static_cast<const arrow::FixedSizeListType*>(mField->type().get())->list_size(); mLeaflistString += "[" + std::to_string(mNumberElements) + "]"; @@ -286,7 +373,14 @@ TableToTree::TableToTree(std::shared_ptr<arrow::Table> table, // create the tree if it does not exist already if (!mTreePtr) { - mTreePtr = new TTree(treename, treename); + // does treename containe folder? + std::string treeName(treename); + auto pos = treeName.find_first_of("/"); + if (pos != std::string::npos) { + file->cd(treeName.substr(0, pos).c_str()); + } + treeName = treeName.substr(pos + 1, std::string::npos); + mTreePtr = new TTree(treeName.c_str(), treeName.c_str()); } } @@ -336,19 +430,19 @@ TTree* TableToTree::process() togo &= brit->push(); } } - mTreePtr->Write(); + mTreePtr->Write("", TObject::kOverwrite); return mTreePtr; } // ----------------------------------------------------------------------------- -#define MAKE_LIST_BUILDER(ElementType, NumElements) \ - std::unique_ptr<arrow::ArrayBuilder> ValueBuilder; \ - arrow::MemoryPool* MemoryPool = arrow::default_memory_pool(); \ - auto stat = MakeBuilder(MemoryPool, ElementType, &ValueBuilder); \ - mTableBuilder_list = std::make_shared<arrow::FixedSizeListBuilder>( \ - MemoryPool, \ - std::move(ValueBuilder), \ +#define MAKE_LIST_BUILDER(ElementType, NumElements) \ + std::unique_ptr<arrow::ArrayBuilder> ValueBuilder; \ + arrow::MemoryPool* MemoryPool = arrow::default_memory_pool(); \ + auto stat = MakeBuilder(MemoryPool, ElementType, &ValueBuilder); \ + mTableBuilder_list = new arrow::FixedSizeListBuilder( \ + MemoryPool, \ + std::move(ValueBuilder), \ NumElements); #define MAKE_FIELD(ElementType, NumElements) \ @@ -371,19 +465,19 @@ TTree* TableToTree::process() } // is used in TreeToTable -ColumnIterator::ColumnIterator(TTreeReader* reader, const char* colname) +ColumnIterator::ColumnIterator(TTreeReader& reader, const char* colname) { // find branch - auto tree = reader->GetTree(); + auto tree = reader.GetTree(); if (!tree) { LOGP(FATAL, "Can not locate tree!"); return; } - //tree->Print(); + auto br = tree->GetBranch(colname); if (!br) { - LOGP(FATAL, "Can not locate branch {}", colname); + LOGP(WARNING, "Can not locate branch {}", colname); return; } mColumnName = colname; @@ -413,47 +507,47 @@ ColumnIterator::ColumnIterator(TTreeReader* reader, const char* colname) if (mNumberElements == 1) { switch (mElementType) { case EDataType::kBool_t: - mReaderValue_o = new TTreeReaderValue<bool>(*reader, mColumnName); + mReaderValue_o = new TTreeReaderValue<bool>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(bool, 1, mTableBuilder_o); break; case EDataType::kUChar_t: - mReaderValue_ub = new TTreeReaderValue<uint8_t>(*reader, mColumnName); + mReaderValue_ub = new TTreeReaderValue<uint8_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint8_t, 1, mTableBuilder_ub); break; case EDataType::kUShort_t: - mReaderValue_us = new TTreeReaderValue<uint16_t>(*reader, mColumnName); + mReaderValue_us = new TTreeReaderValue<uint16_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint16_t, 1, mTableBuilder_us); break; case EDataType::kUInt_t: - mReaderValue_ui = new TTreeReaderValue<uint32_t>(*reader, mColumnName); + mReaderValue_ui = new TTreeReaderValue<uint32_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint32_t, 1, mTableBuilder_ui); break; case EDataType::kULong64_t: - mReaderValue_ul = new TTreeReaderValue<uint64_t>(*reader, mColumnName); + mReaderValue_ul = new TTreeReaderValue<ULong64_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint64_t, 1, mTableBuilder_ul); break; case EDataType::kChar_t: - mReaderValue_b = new TTreeReaderValue<int8_t>(*reader, mColumnName); + mReaderValue_b = new TTreeReaderValue<int8_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int8_t, 1, mTableBuilder_b); break; case EDataType::kShort_t: - mReaderValue_s = new TTreeReaderValue<int16_t>(*reader, mColumnName); + mReaderValue_s = new TTreeReaderValue<int16_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int16_t, 1, mTableBuilder_s); break; case EDataType::kInt_t: - mReaderValue_i = new TTreeReaderValue<int32_t>(*reader, mColumnName); + mReaderValue_i = new TTreeReaderValue<int32_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int32_t, 1, mTableBuilder_i); break; case EDataType::kLong64_t: - mReaderValue_l = new TTreeReaderValue<int64_t>(*reader, mColumnName); + mReaderValue_l = new TTreeReaderValue<int64_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int64_t, 1, mTableBuilder_l); break; case EDataType::kFloat_t: - mReaderValue_f = new TTreeReaderValue<float>(*reader, mColumnName); + mReaderValue_f = new TTreeReaderValue<float>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(float, 1, mTableBuilder_f); break; case EDataType::kDouble_t: - mReaderValue_d = new TTreeReaderValue<double>(*reader, mColumnName); + mReaderValue_d = new TTreeReaderValue<double>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(double, 1, mTableBuilder_d); break; default: @@ -463,47 +557,47 @@ ColumnIterator::ColumnIterator(TTreeReader* reader, const char* colname) } else { switch (mElementType) { case EDataType::kBool_t: - mReaderArray_o = new TTreeReaderArray<bool>(*reader, mColumnName); + mReaderArray_o = new TTreeReaderArray<bool>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(bool, mNumberElements, mTableBuilder_o); break; case EDataType::kUChar_t: - mReaderArray_ub = new TTreeReaderArray<uint8_t>(*reader, mColumnName); + mReaderArray_ub = new TTreeReaderArray<uint8_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint8_t, mNumberElements, mTableBuilder_ub); break; case EDataType::kUShort_t: - mReaderArray_us = new TTreeReaderArray<uint16_t>(*reader, mColumnName); + mReaderArray_us = new TTreeReaderArray<uint16_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint16_t, mNumberElements, mTableBuilder_us); break; case EDataType::kUInt_t: - mReaderArray_ui = new TTreeReaderArray<uint32_t>(*reader, mColumnName); + mReaderArray_ui = new TTreeReaderArray<uint32_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint32_t, mNumberElements, mTableBuilder_ui); break; case EDataType::kULong64_t: - mReaderArray_ul = new TTreeReaderArray<uint64_t>(*reader, mColumnName); + mReaderArray_ul = new TTreeReaderArray<uint64_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(uint64_t, mNumberElements, mTableBuilder_ul); break; case EDataType::kChar_t: - mReaderArray_b = new TTreeReaderArray<int8_t>(*reader, mColumnName); + mReaderArray_b = new TTreeReaderArray<int8_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int8_t, mNumberElements, mTableBuilder_b); break; case EDataType::kShort_t: - mReaderArray_s = new TTreeReaderArray<int16_t>(*reader, mColumnName); + mReaderArray_s = new TTreeReaderArray<int16_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int16_t, mNumberElements, mTableBuilder_s); break; case EDataType::kInt_t: - mReaderArray_i = new TTreeReaderArray<int32_t>(*reader, mColumnName); + mReaderArray_i = new TTreeReaderArray<int32_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int32_t, mNumberElements, mTableBuilder_i); break; case EDataType::kLong64_t: - mReaderArray_l = new TTreeReaderArray<int64_t>(*reader, mColumnName); + mReaderArray_l = new TTreeReaderArray<int64_t>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(int64_t, mNumberElements, mTableBuilder_l); break; case EDataType::kFloat_t: - mReaderArray_f = new TTreeReaderArray<float>(*reader, mColumnName); + mReaderArray_f = new TTreeReaderArray<float>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(float, mNumberElements, mTableBuilder_f); break; case EDataType::kDouble_t: - mReaderArray_d = new TTreeReaderArray<double>(*reader, mColumnName); + mReaderArray_d = new TTreeReaderArray<double>(reader, mColumnName); MAKE_FIELD_AND_BUILDER(double, mNumberElements, mTableBuilder_d); break; default: @@ -539,6 +633,22 @@ ColumnIterator::~ColumnIterator() delete mReaderArray_l; delete mReaderArray_f; delete mReaderArray_d; + + if (mTableBuilder_list) { + delete mTableBuilder_list; + } else { + delete mTableBuilder_o; + delete mTableBuilder_ub; + delete mTableBuilder_us; + delete mTableBuilder_ui; + delete mTableBuilder_ul; + delete mTableBuilder_b; + delete mTableBuilder_s; + delete mTableBuilder_i; + delete mTableBuilder_l; + delete mTableBuilder_f; + delete mTableBuilder_d; + } }; bool ColumnIterator::getStatus() @@ -546,6 +656,50 @@ bool ColumnIterator::getStatus() return mStatus; } +void ColumnIterator::reserve(size_t s) +{ + arrow::Status stat; + + switch (mElementType) { + case EDataType::kBool_t: + stat = mTableBuilder_o->Reserve(s * mNumberElements); + break; + case EDataType::kUChar_t: + stat = mTableBuilder_ub->Reserve(s * mNumberElements); + break; + case EDataType::kUShort_t: + stat = mTableBuilder_us->Reserve(s * mNumberElements); + break; + case EDataType::kUInt_t: + stat = mTableBuilder_ui->Reserve(s * mNumberElements); + break; + case EDataType::kULong64_t: + stat = mTableBuilder_ul->Reserve(s * mNumberElements); + break; + case EDataType::kChar_t: + stat = mTableBuilder_b->Reserve(s * mNumberElements); + break; + case EDataType::kShort_t: + stat = mTableBuilder_s->Reserve(s * mNumberElements); + break; + case EDataType::kInt_t: + stat = mTableBuilder_i->Reserve(s * mNumberElements); + break; + case EDataType::kLong64_t: + stat = mTableBuilder_l->Reserve(s * mNumberElements); + break; + case EDataType::kFloat_t: + stat = mTableBuilder_f->Reserve(s * mNumberElements); + break; + case EDataType::kDouble_t: + stat = mTableBuilder_d->Reserve(s * mNumberElements); + break; + default: + LOGP(FATAL, "Type {} not handled!", mElementType); + break; + } +} + void ColumnIterator::push() { arrow::Status stat; @@ -554,37 +708,37 @@ void ColumnIterator::push() if (mNumberElements == 1) { switch (mElementType) { case EDataType::kBool_t: - stat = mTableBuilder_o->Append((bool)**mReaderValue_o); + mTableBuilder_o->UnsafeAppend((bool)**mReaderValue_o); break; case EDataType::kUChar_t: - stat = mTableBuilder_ub->Append(**mReaderValue_ub); + mTableBuilder_ub->UnsafeAppend(**mReaderValue_ub); break; case EDataType::kUShort_t: - stat = mTableBuilder_us->Append(**mReaderValue_us); + mTableBuilder_us->UnsafeAppend(**mReaderValue_us); break; case EDataType::kUInt_t: - stat = mTableBuilder_ui->Append(**mReaderValue_ui); + mTableBuilder_ui->UnsafeAppend(**mReaderValue_ui); break; case EDataType::kULong64_t: - stat = mTableBuilder_ul->Append(**mReaderValue_ul); + mTableBuilder_ul->UnsafeAppend(**mReaderValue_ul); break; case EDataType::kChar_t: - stat = mTableBuilder_b->Append(**mReaderValue_b); + mTableBuilder_b->UnsafeAppend(**mReaderValue_b); break; case EDataType::kShort_t: - stat = mTableBuilder_s->Append(**mReaderValue_s); + mTableBuilder_s->UnsafeAppend(**mReaderValue_s); break; case EDataType::kInt_t: - stat = mTableBuilder_i->Append(**mReaderValue_i); + mTableBuilder_i->UnsafeAppend(**mReaderValue_i); break; case EDataType::kLong64_t: - stat = mTableBuilder_l->Append(**mReaderValue_l); + mTableBuilder_l->UnsafeAppend(**mReaderValue_l); break; case EDataType::kFloat_t: - stat = mTableBuilder_f->Append(**mReaderValue_f); + mTableBuilder_f->UnsafeAppend(**mReaderValue_f); break; case EDataType::kDouble_t: - stat = mTableBuilder_d->Append(**mReaderValue_d); + mTableBuilder_d->UnsafeAppend(**mReaderValue_d); break; default: LOGP(FATAL, "Type {} not handled!", mElementType); @@ -682,77 +836,62 @@ void ColumnIterator::finish() } } -TreeToTable::TreeToTable(TTree* tree) -{ - // initialize the TTreeReader - mTreeReader = new TTreeReader(tree); -} - -TreeToTable::~TreeToTable() -{ - delete mTreeReader; -}; - -bool TreeToTable::addColumn(const char* colname) +void TreeToTable::addColumn(const char* colname) { - auto colit = std::make_shared<ColumnIterator>(mTreeReader, colname); - auto stat = colit->getStatus(); - if (stat) { - mColumnIterators.push_back(std::move(colit)); - } - - return stat; + mColumnNames.push_back(colname); } -bool TreeToTable::addAllColumns() +bool TreeToTable::addAllColumns(TTree* tree) { - // get a list of column names - auto tree = mTreeReader->GetTree(); - if (!tree) { - LOGP(FATAL, "Tree not found!"); - return false; - } auto branchList = tree->GetListOfBranches(); // loop over branches - bool status = !branchList->IsEmpty(); + if (branchList->IsEmpty()) { + return false; + } for (Int_t ii = 0; ii < branchList->GetEntries(); ii++) { auto br = (TBranch*)branchList->At(ii); // IMPROVE: make sure that a column is not added more than one time - auto colit = std::make_shared<ColumnIterator>(mTreeReader, br->GetName()); - if (colit->getStatus()) { - mColumnIterators.push_back(std::move(colit)); - } else { - status = false; - } + mColumnNames.push_back(br->GetName()); } - - return status; + return true; } -void TreeToTable::push() +void TreeToTable::fill(TTree* tree) { - for (auto colit : mColumnIterators) { - colit->push(); + std::vector<std::unique_ptr<ColumnIterator>> columnIterators; + TTreeReader treeReader{tree}; + + tree->SetCacheSize(50000000); + tree->SetClusterPrefetch(true); + for (auto&& columnName : mColumnNames) { + tree->AddBranchToCache(columnName.c_str(), true); + auto colit = std::make_unique<ColumnIterator>(treeReader, columnName.c_str()); + auto stat = colit->getStatus(); + if (!stat) { + throw std::runtime_error("Unable to convert column " + columnName); + } + columnIterators.push_back(std::move(colit)); } -} + tree->StopCacheLearningPhase(); + auto numEntries = treeReader.GetEntries(true); -void TreeToTable::fill() -{ + for (auto&& column : columnIterators) { + column->reserve(numEntries); + } // copy all values from the tree to the table builders - mTreeReader->Restart(); - while (mTreeReader->Next()) { - push(); + treeReader.Restart(); + while (treeReader.Next()) { + for (auto&& column : columnIterators) { + column->push(); + } } -} -std::shared_ptr<arrow::Table> TreeToTable::finalize() -{ // prepare the elements needed to create the final table std::vector<std::shared_ptr<arrow::Array>> array_vector; std::vector<std::shared_ptr<arrow::Field>> schema_vector; - for (auto colit : mColumnIterators) { + for (auto&& colit : columnIterators) { colit->finish(); array_vector.push_back(colit->getArray()); schema_vector.push_back(colit->getSchema()); @@ -761,19 +900,12 @@ std::shared_ptr<arrow::Table> TreeToTable::finalize() // create the final table // ta is of type std::shared_ptr<arrow::Table> - auto table = (arrow::Table::Make(fields, array_vector)); - - return table; + mTable = (arrow::Table::Make(fields, array_vector)); } -std::shared_ptr<arrow::Table> TreeToTable::process() +std::shared_ptr<arrow::Table> TreeToTable::finalize() { - // do the looping with the TTreeReader - fill(); - - // create the table - return finalize(); + return mTable; } -} // namespace framework -} // namespace o2 +} // namespace o2::framework diff --git a/Framework/Core/src/TextControlService.cxx b/Framework/Core/src/TextControlService.cxx index 0846c9ba46310..15f15cd565774 100644 --- a/Framework/Core/src/TextControlService.cxx +++ b/Framework/Core/src/TextControlService.cxx @@ -31,12 +31,14 @@ TextControlService::TextControlService(ServiceRegistry& registry, DeviceState& d // This will send an end of stream to all the devices downstream. void TextControlService::endOfStream() { + std::scoped_lock lock(mMutex); mDeviceState.streaming = StreamingState::EndOfStreaming; } // All we do is to printout void TextControlService::readyToQuit(QuitRequest what) { + std::scoped_lock lock(mMutex); if (mOnce == true) { return; } @@ -55,6 +57,7 @@ void TextControlService::readyToQuit(QuitRequest what) void TextControlService::notifyStreamingState(StreamingState state) { + std::scoped_lock lock(mMutex); switch (state) { case StreamingState::Idle: LOG(INFO) << "CONTROL_ACTION: NOTIFY_STREAMING_STATE IDLE"; diff --git a/Framework/Core/src/Variant.cxx b/Framework/Core/src/Variant.cxx index 151a728f93b9c..20a650327b41d 100644 --- a/Framework/Core/src/Variant.cxx +++ b/Framework/Core/src/Variant.cxx @@ -9,11 +9,44 @@ // or submit itself to any jurisdiction. #include "Framework/Variant.h" #include <iostream> +#include <sstream> -namespace o2 +namespace o2::framework { -namespace framework + +namespace +{ +template <typename T> +void printArray(std::ostream& oss, T* array, size_t size) { + oss << variant_array_symbol<T>::symbol << "["; + oss << array[0]; + for (auto i = 1U; i < size; ++i) { + oss << ", " << array[i]; + } + oss << "]"; +} + +template <typename T> +void printMatrix(std::ostream& oss, Array2D<T> m) +{ + oss << variant_array_symbol<T>::symbol << "[["; + oss << m(0, 0); + for (auto j = 1U; j < m.cols; ++j) { + oss << ", " << m(0, j); + } + oss << "]"; + for (auto i = 1U; i < m.rows; ++i) { + oss << ", ["; + oss << m(i, 0); + for (auto j = 1U; j < m.cols; ++j) { + oss << ", " << m(i, j); + } + oss << "]"; + } + oss << "]"; +} +} // namespace std::ostream& operator<<(std::ostream& oss, Variant const& val) { @@ -36,6 +69,30 @@ std::ostream& operator<<(std::ostream& oss, Variant const& val) case VariantType::Bool: oss << val.get<bool>(); break; + case VariantType::ArrayInt: + printArray<int>(oss, val.get<int*>(), val.size()); + break; + case VariantType::ArrayFloat: + printArray<float>(oss, val.get<float*>(), val.size()); + break; + case VariantType::ArrayDouble: + printArray<double>(oss, val.get<double*>(), val.size()); + break; + case VariantType::ArrayBool: + printArray<bool>(oss, val.get<bool*>(), val.size()); + break; + case VariantType::ArrayString: + printArray<std::string>(oss, val.get<std::string*>(), val.size()); + break; + case VariantType::MatrixInt: + printMatrix<int>(oss, val.get<Array2D<int>>()); + break; + case VariantType::MatrixFloat: + printMatrix<float>(oss, val.get<Array2D<float>>()); + break; + case VariantType::MatrixDouble: + printMatrix<double>(oss, val.get<Array2D<double>>()); + break; case VariantType::Empty: break; default: @@ -45,5 +102,11 @@ std::ostream& operator<<(std::ostream& oss, Variant const& val) return oss; } -} // namespace framework -} // namespace o2 +std::string Variant::asString() const +{ + std::stringstream ss; + ss << *this; + return ss.str(); +} + +} // namespace o2::framework diff --git a/Framework/Core/src/WorkflowCustomizationHelpers.cxx b/Framework/Core/src/WorkflowCustomizationHelpers.cxx new file mode 100644 index 0000000000000..4d187933bce5e --- /dev/null +++ b/Framework/Core/src/WorkflowCustomizationHelpers.cxx @@ -0,0 +1,65 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/WorkflowCustomizationHelpers.h" +#include <string> +#include <cstdlib> +#include <unistd.h> + +namespace +{ +std::string defaultIPCFolder() +{ + /// Find out a place where we can write the sockets + char const* channelPrefix = getenv("TMPDIR"); + if (channelPrefix) { + return std::string(channelPrefix); + } + return access("/tmp", W_OK) == 0 ? "/tmp" : "."; +} +} // namespace + +namespace o2::framework +{ + +std::vector<ConfigParamSpec> WorkflowCustomizationHelpers::requiredWorkflowOptions() +{ + return std::vector<ConfigParamSpec>{{ConfigParamSpec{"readers", VariantType::Int64, 1ll, {"number of parallel readers to use"}}, + ConfigParamSpec{"pipeline", VariantType::String, "", {"override default pipeline size"}}, + ConfigParamSpec{"clone", VariantType::String, "", {"clone processors from a template"}}, + ConfigParamSpec{"workflow-suffix", VariantType::String, "", {"suffix to add to all dataprocessors"}}, + + // options for AOD rate limiting + ConfigParamSpec{"aod-memory-rate-limit", VariantType::Int64, 0LL, {"Rate limit AOD processing based on memory"}}, + + // options for AOD writer + ConfigParamSpec{"aod-writer-json", VariantType::String, "", {"Name of the json configuration file"}}, + ConfigParamSpec{"aod-writer-resfile", VariantType::String, "", {"Default name of the output file"}}, + ConfigParamSpec{"aod-writer-resmode", VariantType::String, "RECREATE", {"Creation mode of the result files: NEW, CREATE, RECREATE, UPDATE"}}, + ConfigParamSpec{"aod-writer-ntfmerge", VariantType::Int, -1, {"Number of time frames to merge into one file"}}, + ConfigParamSpec{"aod-writer-keep", VariantType::String, "", {"Comma separated list of ORIGIN/DESCRIPTION/SUBSPECIFICATION:treename:col1/col2/..:filename"}}, + + ConfigParamSpec{"fairmq-rate-logging", VariantType::Int, 0, {"Rate logging for FairMQ channels"}}, + ConfigParamSpec{"fairmq-recv-buffer-size", VariantType::Int, 1000, {"recvBufferSize option for FairMQ channels"}}, + ConfigParamSpec{"fairmq-send-buffer-size", VariantType::Int, 1000, {"sendBufferSize option for FairMQ channels"}}, + /// Find out a place where we can write the sockets + ConfigParamSpec{"fairmq-ipc-prefix", VariantType::String, defaultIPCFolder(), {"Prefix for FairMQ channels location"}}, + + ConfigParamSpec{"forwarding-policy", VariantType::String, "dangling", {"Which messages to forward." + " dangling: dangling outputs," + " all: all messages"}}, + ConfigParamSpec{"forwarding-destination", + VariantType::String, + "file", + {"Destination for forwarded messages." + " file: write to file," + " fairmq: send to output proxy"}}}}; +} +} // namespace o2::framework diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 3f578cabb6506..58f6f2842b580 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -15,11 +15,14 @@ #include "Framework/ConfigContext.h" #include "Framework/DeviceSpec.h" #include "Framework/DataSpecUtils.h" +#include "Framework/DataAllocator.h" #include "Framework/ControlService.h" #include "Framework/RawDeviceService.h" #include "Framework/StringHelpers.h" +#include "Framework/CommonMessageBackends.h" +#include "Framework/ExternalFairMQDeviceProxy.h" +#include "Framework/Plugins.h" -#include "fairmq/FairMQDevice.h" #include "Headers/DataHeader.h" #include <algorithm> #include <list> @@ -40,6 +43,12 @@ std::ostream& operator<<(std::ostream& out, TopoIndexInfo const& info) return out; } +enum OutputType : char { + UNKNOWN = 0, + DANGLING = 1, + ANALYSIS = 2, +}; + std::vector<TopoIndexInfo> WorkflowHelpers::topologicalSort(size_t nodeCount, int const* edgeIn, @@ -129,8 +138,6 @@ void addMissingOutputsToReader(std::vector<OutputSpec> const& providedOutputs, return DataSpecUtils::match(requested, provided); }; }; - auto last = std::unique(requestedInputs.begin(), requestedInputs.end()); - requestedInputs.erase(last, requestedInputs.end()); for (InputSpec const& requested : requestedInputs) { auto provided = std::find_if(providedOutputs.begin(), providedOutputs.end(), @@ -139,6 +146,14 @@ void addMissingOutputsToReader(std::vector<OutputSpec> const& providedOutputs, if (provided != providedOutputs.end()) { continue; } + + auto inList = std::find_if(publisher.outputs.begin(), + publisher.outputs.end(), + matchingOutputFor(requested)); + if (inList != publisher.outputs.end()) { + continue; + } + auto concrete = DataSpecUtils::asConcreteDataMatcher(requested); publisher.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec}); } @@ -156,6 +171,41 @@ void addMissingOutputsToSpawner(std::vector<InputSpec>&& requestedDYNs, } } +void addMissingOutputsToBuilder(std::vector<InputSpec>&& requestedIDXs, + std::vector<InputSpec>& requestedAODs, + DataProcessorSpec& publisher) +{ + auto inputSpecFromString = [](std::string s) { + std::regex word_regex("(\\w+)"); + auto words = std::sregex_iterator(s.begin(), s.end(), word_regex); + if (std::distance(words, std::sregex_iterator()) != 3) { + throw runtime_error_f("Malformed spec: %s", s.c_str()); + } + std::vector<std::string> data; + for (auto i = words; i != std::sregex_iterator(); ++i) { + data.emplace_back(i->str()); + } + char origin[4]; + char description[16]; + std::memcpy(&origin, data[1].c_str(), 4); + std::memcpy(&description, data[2].c_str(), 16); + return InputSpec{data[0], header::DataOrigin{origin}, header::DataDescription{description}}; + }; + + for (auto& input : requestedIDXs) { + auto concrete = DataSpecUtils::asConcreteDataMatcher(input); + publisher.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec}); + for (auto& i : input.metadata) { + auto spec = inputSpecFromString(i.defaultValue.get<std::string>()); + auto j = std::find_if(publisher.inputs.begin(), publisher.inputs.end(), [&](auto x) { return x.binding == spec.binding; }); + if (j == publisher.inputs.end()) { + publisher.inputs.push_back(spec); + } + requestedAODs.push_back(spec); + } + } +} + void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext const& ctx) { auto fakeCallback = AlgorithmSpec{[](InitContext& ic) { @@ -163,10 +213,10 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext LOG(INFO) << "To be hidden / removed at some point."; // mark this dummy process as ready-to-quit ic.services().get<ControlService>().readyToQuit(QuitRequest::Me); - + return [](ProcessingContext& pc) { // this callback is never called since there is no expiring input - pc.services().get<RawDeviceService>().device()->WaitFor(std::chrono::seconds(2)); + pc.services().get<RawDeviceService>().waitFor(2000); }; }}; @@ -194,31 +244,42 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // reads them from file. // // FIXME: source branch is DataOrigin, for the moment. We should - // make it configurable via ConfigParamsOptions. + // make it configurable via ConfigParamsOptions + auto aodLifetime = Lifetime::Enumeration; + if (ctx.options().get<int64_t>("aod-memory-rate-limit")) { + aodLifetime = Lifetime::Signal; + } + auto readerServices = CommonServices::defaultServices(); + readerServices.push_back(CommonMessageBackends::rateLimitingSpec()); + DataProcessorSpec aodReader{ "internal-dpl-aod-reader", {InputSpec{"enumeration", "DPL", "ENUM", - static_cast<DataAllocator::SubSpecificationType>(compile_time_hash("internal-dpl-aod-reader")), Lifetime::Enumeration}}, + static_cast<DataAllocator::SubSpecificationType>(compile_time_hash("internal-dpl-aod-reader")), + aodLifetime}}, {}, - readers::AODReaderHelpers::rootFileReaderCallback(), - {ConfigParamSpec{"aod-file", VariantType::String, "aod.root", {"Input AOD file"}}, - ConfigParamSpec{"json-file", VariantType::String, {"json configuration file"}}, + AlgorithmSpec::dummyAlgorithm(), + {ConfigParamSpec{"aod-file", VariantType::String, {"Input AOD file"}}, + ConfigParamSpec{"aod-reader-json", VariantType::String, {"json configuration file"}}, + ConfigParamSpec{"time-limit", VariantType::Int64, 0ll, {"Maximum run time limit in seconds"}}, ConfigParamSpec{"start-value-enumeration", VariantType::Int64, 0ll, {"initial value for the enumeration"}}, ConfigParamSpec{"end-value-enumeration", VariantType::Int64, -1ll, {"final value for the enumeration"}}, - ConfigParamSpec{"step-value-enumeration", VariantType::Int64, 1ll, {"step between one value and the other"}}}}; + ConfigParamSpec{"step-value-enumeration", VariantType::Int64, 1ll, {"step between one value and the other"}}}, + readerServices}; std::vector<InputSpec> requestedAODs; std::vector<OutputSpec> providedAODs; std::vector<InputSpec> requestedDYNs; + std::vector<InputSpec> requestedIDXs; std::vector<InputSpec> requestedCCDBs; std::vector<OutputSpec> providedCCDBs; - std::vector<OutputSpec> providedOutputObj; + std::vector<OutputSpec> providedOutputObjHist; outputTasks outTskMap; - outputObjects outObjMap; + outputObjects outObjHistMap; for (size_t wi = 0; wi < workflow.size(); ++wi) { auto& processor = workflow[wi]; @@ -245,13 +306,17 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } timer.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec, Lifetime::Timer}); } break; + case Lifetime::Signal: { + auto concrete = DataSpecUtils::asConcreteDataMatcher(input); + timer.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec, Lifetime::Signal}); + } break; case Lifetime::Enumeration: { auto concrete = DataSpecUtils::asConcreteDataMatcher(input); timer.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec, Lifetime::Enumeration}); } break; case Lifetime::Condition: { if (hasConditionOption == false) { - processor.options.emplace_back(ConfigParamSpec{"condition-backend", VariantType::String, "http://localhost:8080", {"Url for CCDB"}}); + processor.options.emplace_back(ConfigParamSpec{"condition-backend", VariantType::String, "http://localhost:8080", {"URL for CCDB"}}); processor.options.emplace_back(ConfigParamSpec{"condition-timestamp", VariantType::String, "", {"Force timestamp for CCDB lookup"}}); hasConditionOption = true; } @@ -260,28 +325,40 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext case Lifetime::QA: case Lifetime::Transient: case Lifetime::Timeframe: + case Lifetime::Optional: break; } if (DataSpecUtils::partialMatch(input, header::DataOrigin{"AOD"})) { requestedAODs.emplace_back(input); } + if (DataSpecUtils::partialMatch(input, header::DataOrigin{"RN2"})) { + requestedAODs.emplace_back(input); + } if (DataSpecUtils::partialMatch(input, header::DataOrigin{"DYN"})) { if (std::find_if(requestedDYNs.begin(), requestedDYNs.end(), [&](InputSpec const& spec) { return input.binding == spec.binding; }) == requestedDYNs.end()) { requestedDYNs.emplace_back(input); } } + if (DataSpecUtils::partialMatch(input, header::DataOrigin{"IDX"})) { + if (std::find_if(requestedIDXs.begin(), requestedIDXs.end(), [&](InputSpec const& spec) { return input.binding == spec.binding; }) == requestedIDXs.end()) { + requestedIDXs.emplace_back(input); + } + } } + std::stable_sort(timer.outputs.begin(), timer.outputs.end(), [](OutputSpec const& a, OutputSpec const& b) { return *DataSpecUtils::getOptionalSubSpec(a) < *DataSpecUtils::getOptionalSubSpec(b); }); for (size_t oi = 0; oi < processor.outputs.size(); ++oi) { auto& output = processor.outputs[oi]; if (DataSpecUtils::partialMatch(output, header::DataOrigin{"AOD"})) { providedAODs.emplace_back(output); + } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"RN2"})) { + providedAODs.emplace_back(output); } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"ATSK"})) { - providedOutputObj.emplace_back(output); - auto it = std::find_if(outObjMap.begin(), outObjMap.end(), [&](auto&& x) { return x.first == hash; }); - if (it == outObjMap.end()) { - outObjMap.push_back({hash, {output.binding.value}}); + providedOutputObjHist.emplace_back(output); + auto it = std::find_if(outObjHistMap.begin(), outObjHistMap.end(), [&](auto&& x) { return x.first == hash; }); + if (it == outObjHistMap.end()) { + outObjHistMap.push_back({hash, {output.binding.value}}); } else { it->second.push_back(output.binding.value); } @@ -291,10 +368,15 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } } } - + auto sortingEquals = [](InputSpec const& a, InputSpec const& b) { return DataSpecUtils::describe(a) == DataSpecUtils::describe(b); }; + std::sort(requestedDYNs.begin(), requestedDYNs.end(), sortingEquals); auto last = std::unique(requestedDYNs.begin(), requestedDYNs.end()); requestedDYNs.erase(last, requestedDYNs.end()); + std::sort(requestedIDXs.begin(), requestedIDXs.end(), sortingEquals); + last = std::unique(requestedIDXs.begin(), requestedIDXs.end()); + requestedIDXs.erase(last, requestedIDXs.end()); + DataProcessorSpec aodSpawner{ "internal-dpl-aod-spawner", {}, @@ -302,7 +384,16 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext readers::AODReaderHelpers::aodSpawnerCallback(requestedDYNs), {}}; + DataProcessorSpec indexBuilder{ + "internal-dpl-aod-index-builder", + {}, + {}, + readers::AODReaderHelpers::indexBuilderCallback(requestedIDXs), + {}}; + addMissingOutputsToSpawner(std::move(requestedDYNs), requestedAODs, aodSpawner); + addMissingOutputsToBuilder(std::move(requestedIDXs), requestedAODs, indexBuilder); + addMissingOutputsToReader(providedAODs, requestedAODs, aodReader); addMissingOutputsToReader(providedCCDBs, requestedCCDBs, ccdbBackend); @@ -322,78 +413,118 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext extraSpecs.push_back(aodSpawner); } + if (indexBuilder.outputs.empty() == false) { + extraSpecs.push_back(indexBuilder); + } + + // add the reader if (aodReader.outputs.empty() == false) { + uv_lib_t supportLib; + int result = 0; +#ifdef __APPLE__ + result = uv_dlopen("libO2FrameworkAnalysisSupport.dylib", &supportLib); +#else + result = uv_dlopen("libO2FrameworkAnalysisSupport.so", &supportLib); +#endif + if (result == -1) { + LOG(ERROR) << uv_dlerror(&supportLib); + } + void* callback = nullptr; + DPLPluginHandle* (*dpl_plugin_callback)(DPLPluginHandle*); + + result = uv_dlsym(&supportLib, "dpl_plugin_callback", (void**)&dpl_plugin_callback); + if (result == -1) { + LOG(ERROR) << uv_dlerror(&supportLib); + } + DPLPluginHandle* pluginInstance = dpl_plugin_callback(nullptr); + AlgorithmPlugin* creator = PluginManager::getByName<AlgorithmPlugin>(pluginInstance, "ROOTFileReader"); + aodReader.algorithm = creator->create(); + aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); extraSpecs.push_back(timePipeline(aodReader, ctx.options().get<int64_t>("readers"))); auto concrete = DataSpecUtils::asConcreteDataMatcher(aodReader.inputs[0]); timer.outputs.emplace_back(OutputSpec{concrete.origin, concrete.description, concrete.subSpec, Lifetime::Enumeration}); } + // add the timer if (timer.outputs.empty() == false) { extraSpecs.push_back(timer); } // This is to inject a file sink so that any dangling ATSK object is written // to a ROOT file. - if (providedOutputObj.size() != 0) { - auto rootSink = CommonDataProcessors::getOutputObjSink(outObjMap, outTskMap); + if (providedOutputObjHist.empty() == false) { + auto rootSink = CommonDataProcessors::getOutputObjHistSink(outObjHistMap, outTskMap); extraSpecs.push_back(rootSink); } workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); - - /// This will create different file sinks - /// . AOD - getGlobalAODSink - /// . dangling, not AOD - getGlobalFileSink - /// - // First analyze all ouputs - // outputtypes = isAOD*2 + isdangling*1 + 0 - auto [OutputsInputs, outputtypes] = analyzeOutputs(workflow); - - // file sink for any AOD output extraSpecs.clear(); - // select outputs of type AOD - std::vector<InputSpec> OutputsInputsAOD; - std::vector<bool> isdangling; - for (int ii = 0; ii < OutputsInputs.size(); ii++) { - if ((outputtypes[ii] & 2) == 2) { - - // temporarily also request to be dangling - if ((outputtypes[ii] & 1) == 1) { - OutputsInputsAOD.emplace_back(OutputsInputs[ii]); - isdangling.emplace_back((outputtypes[ii] & 1) == 1); + /// Analyze all ouputs + // outputTypes = isAOD*2 + isdangling*1 + 0 + auto [outputsInputs, outputTypes] = analyzeOutputs(workflow); + + // create DataOutputDescriptor + std::shared_ptr<DataOutputDirector> dod = getDataOutputDirector(ctx.options(), outputsInputs, outputTypes); + + // select outputs of type AOD which need to be saved + // ATTENTION: if there are dangling outputs the getGlobalAODSink + // has to be created in any case! + std::vector<InputSpec> outputsInputsAOD; + for (auto ii = 0u; ii < outputsInputs.size(); ii++) { + if ((outputTypes[ii] & ANALYSIS) == ANALYSIS) { + auto ds = dod->getDataOutputDescriptors(outputsInputs[ii]); + if (ds.size() > 0 || (outputTypes[ii] & DANGLING) == DANGLING) { + outputsInputsAOD.emplace_back(outputsInputs[ii]); } } } - if (OutputsInputsAOD.size() > 0) { - auto fileSink = CommonDataProcessors::getGlobalAODSink(OutputsInputsAOD, - isdangling); + // file sink for any AOD output + if (outputsInputsAOD.size() > 0) { + // add TFNumber as input to the writer + outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); + auto fileSink = CommonDataProcessors::getGlobalAODSink(dod, outputsInputsAOD); extraSpecs.push_back(fileSink); } - workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); - // file sink for notAOD dangling outputs + workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); extraSpecs.clear(); - // select dangling outputs which are not of type AOD - std::vector<InputSpec> OutputsInputsDangling; - for (int ii = 0; ii < OutputsInputs.size(); ii++) { - if ((outputtypes[ii] & 1) == 1 && (outputtypes[ii] & 2) == 0) - OutputsInputsDangling.emplace_back(OutputsInputs[ii]); + // Select dangling outputs which are not of type AOD + std::vector<InputSpec> redirectedOutputsInputs; + for (auto ii = 0u; ii < outputsInputs.size(); ii++) { + if (ctx.options().get<std::string>("forwarding-policy") == "none") { + continue; + } + // We forward to the output proxy all the inputs only if they are dangling + // or if the forwarding policy is "proxy". + if (!(outputTypes[ii] & DANGLING) && (ctx.options().get<std::string>("forwarding-policy") != "all")) { + continue; + } + // AODs are skipped in any case. + if ((outputTypes[ii] & ANALYSIS)) { + continue; + } + redirectedOutputsInputs.emplace_back(outputsInputs[ii]); } std::vector<InputSpec> unmatched; - if (OutputsInputsDangling.size() > 0) { - auto fileSink = CommonDataProcessors::getGlobalFileSink(OutputsInputsDangling, unmatched); - if (unmatched.size() != OutputsInputsDangling.size()) { + if (redirectedOutputsInputs.size() > 0 && ctx.options().get<std::string>("forwarding-destination") == "file") { + auto fileSink = CommonDataProcessors::getGlobalFileSink(redirectedOutputsInputs, unmatched); + if (unmatched.size() != redirectedOutputsInputs.size()) { extraSpecs.push_back(fileSink); } + } else if (redirectedOutputsInputs.size() > 0 && ctx.options().get<std::string>("forwarding-destination") == "fairmq") { + auto fairMQSink = CommonDataProcessors::getGlobalFairMQSink(redirectedOutputsInputs); + extraSpecs.push_back(fairMQSink); } if (unmatched.size() > 0) { extraSpecs.push_back(CommonDataProcessors::getDummySink(unmatched)); } + workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); + extraSpecs.clear(); } void WorkflowHelpers::constructGraph(const WorkflowSpec& workflow, @@ -402,6 +533,7 @@ void WorkflowHelpers::constructGraph(const WorkflowSpec& workflow, std::vector<LogicalForwardInfo>& forwardedInputsInfo) { assert(!workflow.empty()); + // This is the state. Oif is the iterator I use for the searches. std::list<LogicalOutputInfo> availableOutputsInfo; auto const& constOutputs = outputs; // const version of the outputs @@ -647,10 +779,10 @@ void WorkflowHelpers::sortEdges(std::vector<size_t>& inEdgeIndex, std::sort(outEdgeIndex.begin(), outEdgeIndex.end(), outSorter); } -void WorkflowHelpers::verifyWorkflow(const o2::framework::WorkflowSpec& workflow) +WorkflowParsingState WorkflowHelpers::verifyWorkflow(const o2::framework::WorkflowSpec& workflow) { if (workflow.empty()) { - throw std::runtime_error("Empty workflow!"); + return WorkflowParsingState::Empty; } std::set<std::string> validNames; std::vector<OutputSpec> availableOutputs; @@ -665,8 +797,9 @@ void WorkflowHelpers::verifyWorkflow(const o2::framework::WorkflowSpec& workflow std::ostringstream ss; for (auto& spec : workflow) { - if (spec.name.empty()) + if (spec.name.empty()) { throw std::runtime_error("Invalid DataProcessorSpec name"); + } if (validNames.find(spec.name) != validNames.end()) { throw std::runtime_error("Name " + spec.name + " is used twice."); } @@ -690,6 +823,7 @@ void WorkflowHelpers::verifyWorkflow(const o2::framework::WorkflowSpec& workflow } } } + return WorkflowParsingState::Valid; } using UnifiedDataSpecType = std::variant<InputSpec, OutputSpec>; @@ -698,6 +832,84 @@ struct DataMatcherId { size_t id; }; +std::shared_ptr<DataOutputDirector> WorkflowHelpers::getDataOutputDirector(ConfigParamRegistry const& options, std::vector<InputSpec> const& OutputsInputs, std::vector<unsigned char> const& outputTypes) +{ + std::shared_ptr<DataOutputDirector> dod = std::make_shared<DataOutputDirector>(); + + // analyze options and take actions accordingly + // default values + std::string fnb, fnbase("AnalysisResults_trees"); + std::string fmo, filemode("RECREATE"); + int ntfm, ntfmerge = 1; + + // values from json + if (options.isSet("aod-writer-json")) { + auto fnjson = options.get<std::string>("aod-writer-json"); + if (!fnjson.empty()) { + std::tie(fnb, fmo, ntfm) = dod->readJson(fnjson); + if (!fnb.empty()) { + fnbase = fnb; + } + if (!fmo.empty()) { + filemode = fmo; + } + if (ntfm > 0) { + ntfmerge = ntfm; + } + } + } + + // values from command line options, information from json is overwritten + if (options.isSet("aod-writer-resfile")) { + fnb = options.get<std::string>("aod-writer-resfile"); + if (!fnb.empty()) { + fnbase = fnb; + } + } + if (options.isSet("aod-writer-resmode")) { + fmo = options.get<std::string>("aod-writer-resmode"); + if (!fmo.empty()) { + filemode = fmo; + } + } + if (options.isSet("aod-writer-ntfmerge")) { + ntfm = options.get<int>("aod-writer-ntfmerge"); + if (ntfm > 0) { + ntfmerge = ntfm; + } + } + // parse the keepString + if (options.isSet("aod-writer-keep")) { + auto keepString = options.get<std::string>("aod-writer-keep"); + if (!keepString.empty()) { + + dod->reset(); + std::string d("dangling"); + if (d.find(keepString) == 0) { + + // use the dangling outputs + std::vector<InputSpec> danglingOutputs; + for (auto ii = 0; ii < OutputsInputs.size(); ii++) { + if ((outputTypes[ii] & 2) == 2 && (outputTypes[ii] & 1) == 1) { + danglingOutputs.emplace_back(OutputsInputs[ii]); + } + } + dod->readSpecs(danglingOutputs); + + } else { + + // use the keep string + dod->readString(keepString); + } + } + } + dod->setFilenameBase(fnbase); + dod->setFileMode(filemode); + dod->setNumberTimeFramesToMerge(ntfmerge); + + return dod; +} + std::tuple<std::vector<InputSpec>, std::vector<unsigned char>> WorkflowHelpers::analyzeOutputs(WorkflowSpec const& workflow) { // compute total number of input/output @@ -714,9 +926,9 @@ std::tuple<std::vector<InputSpec>, std::vector<unsigned char>> WorkflowHelpers:: outputs.reserve(totalOutputs); std::vector<InputSpec> results; - std::vector<unsigned char> outputtypes; + std::vector<unsigned char> outputTypes; results.reserve(totalOutputs); - outputtypes.reserve(totalOutputs); + outputTypes.reserve(totalOutputs); /// Prepare an index to do the iterations quickly. for (size_t wi = 0, we = workflow.size(); wi != we; ++wi) { @@ -734,11 +946,15 @@ std::tuple<std::vector<InputSpec>, std::vector<unsigned char>> WorkflowHelpers:: auto& outputSpec = workflow[output.workflowId].outputs[output.id]; // compute output type - unsigned char outputtype = 0; + unsigned char outputType = UNKNOWN; // is AOD? if (DataSpecUtils::partialMatch(outputSpec, header::DataOrigin("AOD"))) { - outputtype += 2; + outputType |= ANALYSIS; + } + // is RN2? + if (DataSpecUtils::partialMatch(outputSpec, header::DataOrigin("RN2"))) { + outputType |= ANALYSIS; } // is dangling output? @@ -756,10 +972,10 @@ std::tuple<std::vector<InputSpec>, std::vector<unsigned char>> WorkflowHelpers:: } } if (!matched) { - outputtype += 1; + outputType |= DANGLING; } - // update results and outputtypes + // update results and outputTypes auto input = DataSpecUtils::matchingInput(outputSpec); char buf[64]; input.binding = (snprintf(buf, 63, "output_%zu_%zu", output.workflowId, output.id), buf); @@ -767,24 +983,24 @@ std::tuple<std::vector<InputSpec>, std::vector<unsigned char>> WorkflowHelpers:: // make sure that entries are unique if (std::find(results.begin(), results.end(), input) == results.end()) { results.emplace_back(input); - outputtypes.emplace_back(outputtype); + outputTypes.emplace_back(outputType); } } // make sure that results is unique - return std::make_tuple(results, outputtypes); + return std::make_tuple(results, outputTypes); } std::vector<InputSpec> WorkflowHelpers::computeDanglingOutputs(WorkflowSpec const& workflow) { - auto [OutputsInputs, outputtypes] = analyzeOutputs(workflow); + auto [outputsInputs, outputTypes] = analyzeOutputs(workflow); std::vector<InputSpec> results; - for (int ii = 0; ii < OutputsInputs.size(); ii++) { - if ((outputtypes[ii] & 1) == 1) { - results.emplace_back(OutputsInputs[ii]); + for (auto ii = 0u; ii < outputsInputs.size(); ii++) { + if (outputTypes[ii] & DANGLING) { + results.emplace_back(outputsInputs[ii]); } } diff --git a/Framework/Core/src/WorkflowHelpers.h b/Framework/Core/src/WorkflowHelpers.h index 2f740609effcf..f7ffde511c415 100644 --- a/Framework/Core/src/WorkflowHelpers.h +++ b/Framework/Core/src/WorkflowHelpers.h @@ -14,7 +14,8 @@ #include "Framework/OutputSpec.h" #include "Framework/ForwardRoute.h" #include "Framework/WorkflowSpec.h" -#include "DataProcessorInfo.h" +#include "Framework/DataOutputDirector.h" +#include "Framework/DataProcessorInfo.h" #include <cstddef> #include <vector> @@ -23,6 +24,23 @@ namespace o2::framework { +inline static std::string debugWorkflow(std::vector<DataProcessorSpec> const& specs) +{ + std::ostringstream out; + for (auto& spec : specs) { + out << spec.name << "\n"; + out << " Inputs:\n"; + for (auto& ii : spec.inputs) { + out << " - " << DataSpecUtils::describe(ii) << "\n"; + } + // out << "\n Outputs:\n"; + // for (auto& ii : spec.outputs) { + // out << " - " << DataSpecUtils::describe(ii) << "\n"; + // } + } + return out.str(); +} + struct ConfigContext; // Structure to hold information which was derived // for output channels. @@ -125,6 +143,11 @@ struct OutputObj { bool isdangling; }; +enum struct WorkflowParsingState : int { + Valid, + Empty, +}; + /// A set of internal helper classes to manipulate a Workflow struct WorkflowHelpers { /// Topological sort for a graph of @a nodeCount nodes. @@ -145,7 +168,7 @@ struct WorkflowHelpers { // Helper method to verify that a given workflow is actually valid e.g. that // it contains no empty labels. - static void verifyWorkflow(const WorkflowSpec& workflow); + [[nodiscard]] static WorkflowParsingState verifyWorkflow(const WorkflowSpec& workflow); // Depending on the workflow and the dangling inputs inside it, inject "fake" // devices to mark the fact we might need some extra action to make sure @@ -173,6 +196,8 @@ struct WorkflowHelpers { const std::vector<DeviceConnectionEdge>& edges, const std::vector<size_t>& index); + static std::shared_ptr<DataOutputDirector> getDataOutputDirector(ConfigParamRegistry const& options, std::vector<InputSpec> const& OutputsInputs, std::vector<unsigned char> const& outputTypes); + /// Given @a workflow it gathers all the OutputSpec and in addition provides /// the information whether and output is dangling and/or of type AOD /// An Output is dangling if it does not have a corresponding InputSpec. diff --git a/Framework/Core/src/WorkflowSerializationHelpers.cxx b/Framework/Core/src/WorkflowSerializationHelpers.cxx index 22e13c0275948..20bb11ee7407e 100644 --- a/Framework/Core/src/WorkflowSerializationHelpers.cxx +++ b/Framework/Core/src/WorkflowSerializationHelpers.cxx @@ -51,6 +51,7 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, IN_INPUT_DESCRIPTION, IN_INPUT_SUBSPEC, IN_INPUT_LIFETIME, + IN_INPUT_OPTIONS, IN_OUTPUT, IN_OUTPUT_BINDING, IN_OUTPUT_ORIGIN, @@ -135,6 +136,9 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, case State::IN_INPUT_LIFETIME: s << "IN_INPUT_LIFETIME"; break; + case State::IN_INPUT_OPTIONS: + s << "IN_INPUT_OPTIONS"; + break; case State::IN_OUTPUT: s << "IN_OUTPUT"; break; @@ -202,7 +206,7 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, WorkflowImporter(std::vector<DataProcessorSpec>& o, std::vector<DataProcessorInfo>& m) : states{}, - output{o}, + dataProcessors{o}, metadata{m} { push(State::IN_START); @@ -215,9 +219,9 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, push(State::IN_EXECUTION); } else if (in(State::IN_DATAPROCESSORS)) { push(State::IN_DATAPROCESSOR); - output.push_back(DataProcessorSpec{}); + dataProcessors.push_back(DataProcessorSpec{}); } else if (in(State::IN_DATAPROCESSOR)) { - output.push_back(DataProcessorSpec{}); + dataProcessors.push_back(DataProcessorSpec{}); } else if (in(State::IN_INPUTS)) { push(State::IN_INPUT); inputHasSubSpec = false; @@ -226,6 +230,8 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, outputHasSubSpec = false; } else if (in(State::IN_OPTIONS)) { push(State::IN_OPTION); + } else if (in(State::IN_INPUT_OPTIONS)) { + push(State::IN_OPTION); } else if (in(State::IN_WORKFLOW_OPTIONS)) { push(State::IN_OPTION); } else if (in(State::IN_METADATA)) { @@ -242,16 +248,17 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, enter("END_OBJECT"); if (in(State::IN_INPUT)) { if (inputHasSubSpec) { - output.back().inputs.push_back(InputSpec(binding, origin, description, subspec, lifetime)); + dataProcessors.back().inputs.push_back(InputSpec(binding, origin, description, subspec, lifetime, inputOptions)); } else { - output.back().inputs.push_back(InputSpec(binding, {origin, description}, lifetime)); + dataProcessors.back().inputs.push_back(InputSpec(binding, {origin, description}, lifetime, inputOptions)); } + inputOptions.clear(); inputHasSubSpec = false; } else if (in(State::IN_OUTPUT)) { if (outputHasSubSpec) { - output.back().outputs.push_back(OutputSpec({binding}, origin, description, subspec, lifetime)); + dataProcessors.back().outputs.push_back(OutputSpec({binding}, origin, description, subspec, lifetime)); } else { - output.back().outputs.push_back(OutputSpec({binding}, {origin, description}, lifetime)); + dataProcessors.back().outputs.push_back(OutputSpec({binding}, {origin, description}, lifetime)); } outputHasSubSpec = false; } else if (in(State::IN_OPTION)) { @@ -282,9 +289,11 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, } // Depending on the previous state, push options to the right place. if (previousIs(State::IN_OPTIONS)) { - output.back().options.push_back(*opt); + dataProcessors.back().options.push_back(*opt); } else if (previousIs(State::IN_WORKFLOW_OPTIONS)) { metadata.back().workflowOptions.push_back(*opt); + } else if (previousIs(State::IN_INPUT_OPTIONS)) { + inputOptions.push_back(*opt); } else { assert(false); } @@ -301,6 +310,8 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, } else if (in(State::IN_INPUTS)) { push(State::IN_INPUT); inputHasSubSpec = false; + } else if (in(State::IN_INPUT_OPTIONS)) { + push(State::IN_OPTION); } else if (in(State::IN_OUTPUTS)) { push(State::IN_OUTPUT); outputHasSubSpec = false; @@ -345,6 +356,8 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, inputHasSubSpec = true; } else if (in(State::IN_INPUT) && strncmp(str, "lifetime", length) == 0) { push(State::IN_INPUT_LIFETIME); + } else if (in(State::IN_INPUT) && strncmp(str, "metadata", length) == 0) { + push(State::IN_INPUT_OPTIONS); } else if (in(State::IN_OUTPUT) && strncmp(str, "binding", length) == 0) { push(State::IN_OUTPUT_BINDING); } else if (in(State::IN_OUTPUT) && strncmp(str, "origin", length) == 0) { @@ -404,8 +417,8 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, enter(str); auto s = std::string(str, length); if (in(State::IN_DATAPROCESSOR_NAME)) { - assert(output.size()); - output.back().name = s; + assert(dataProcessors.size()); + dataProcessors.back().name = s; } else if (in(State::IN_METADATUM_NAME)) { assert(metadata.size()); metadata.back().name = s; @@ -459,13 +472,13 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, } else if (in(State::IN_OUTPUT_LIFETIME)) { lifetime = (Lifetime)i; } else if (in(State::IN_DATAPROCESSOR_RANK)) { - output.back().rank = i; + dataProcessors.back().rank = i; } else if (in(State::IN_DATAPROCESSOR_N_SLOTS)) { - output.back().nSlots = i; + dataProcessors.back().nSlots = i; } else if (in(State::IN_DATAPROCESSOR_TIMESLICE_ID)) { - output.back().inputTimeSliceId = i; + dataProcessors.back().inputTimeSliceId = i; } else if (in(State::IN_DATAPROCESSOR_MAX_TIMESLICES)) { - output.back().maxInputTimeslices = i; + dataProcessors.back().maxInputTimeslices = i; } pop(); return true; @@ -526,8 +539,9 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, std::ostringstream debug; std::vector<State> states; std::string spec; - std::vector<DataProcessorSpec>& output; + std::vector<DataProcessorSpec>& dataProcessors; std::vector<DataProcessorInfo>& metadata; + std::vector<ConfigParamSpec> inputOptions; std::string binding; header::DataOrigin origin; header::DataDescription description; @@ -612,6 +626,26 @@ void WorkflowSerializationHelpers::dump(std::ostream& out, } w.Key("lifetime"); w.Uint((int)input.lifetime); + if (input.metadata.empty() == false) { + w.Key("metadata"); + w.StartArray(); + for (auto& metadata : input.metadata) { + w.StartObject(); + w.Key("name"); + w.String(metadata.name.c_str()); + auto s = std::to_string(int(metadata.type)); + w.Key("type"); + w.String(s.c_str()); + std::ostringstream oss; + oss << metadata.defaultValue; + w.Key("defaultValue"); + w.String(oss.str().c_str()); + w.Key("help"); + w.String(metadata.help.c_str()); + w.EndObject(); + } + w.EndArray(); + } w.EndObject(); } w.EndArray(); diff --git a/Framework/Core/src/WorkflowSerializationHelpers.h b/Framework/Core/src/WorkflowSerializationHelpers.h index 671c14ef60dd7..1d9bd7fa8f904 100644 --- a/Framework/Core/src/WorkflowSerializationHelpers.h +++ b/Framework/Core/src/WorkflowSerializationHelpers.h @@ -11,14 +11,12 @@ #define O2_FRAMEWORK_CORE_WORKFLOWSERIALIZATIONHELPERS_H_ #include "Framework/DataProcessorSpec.h" -#include "DataProcessorInfo.h" +#include "Framework/DataProcessorInfo.h" #include <iosfwd> #include <vector> -namespace o2 -{ -namespace framework +namespace o2::framework { struct WorkflowSerializationHelpers { @@ -30,7 +28,6 @@ struct WorkflowSerializationHelpers { std::vector<DataProcessorInfo> const& metadata); }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework #endif // O2_FRAMEWORK_CORE_WORKFLOWSERIALIZATIONHELPERS_H_ diff --git a/Framework/Core/src/o2NullSink.cxx b/Framework/Core/src/o2NullSink.cxx new file mode 100644 index 0000000000000..79c18a9f6ba11 --- /dev/null +++ b/Framework/Core/src/o2NullSink.cxx @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/ConfigContext.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/Logger.h" +#include "Framework/ParallelContext.h" +#include "Framework/DataDescriptorQueryBuilder.h" + +#include <chrono> +#include <thread> +#include <vector> + +/// A DataProcessor which terminates a provided set of +/// inputs. +using namespace o2::framework; + +void customize(std::vector<ConfigParamSpec>& options) +{ + options.push_back(o2::framework::ConfigParamSpec{"inputs", VariantType::String, "", {"inputs for the dataprocessor"}}); +}; + +#include "Framework/runDataProcessing.h" + +// This is a simple consumer / producer workflow where both are +// stateful, i.e. they have context which comes from their initialization. +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + WorkflowSpec workflow; + // This is an example of how we can parallelize by subSpec. + // templatedProducer will be instanciated 32 times and the lambda function + // passed to the parallel statement will be applied to each one of the + // instances in order to modify it. Parallel will also make sure the name of + // the instance is amended from "some-producer" to "some-producer-<index>". + auto inputsDesc = context.options().get<std::string>("inputs"); + auto inputs = DataDescriptorQueryBuilder::parse(inputsDesc.c_str()); + + workflow.push_back(DataProcessorSpec{ + "null", + inputs, + {}, + AlgorithmSpec{[](ProcessingContext& ctx) {}}}); + + return workflow; +} diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 055b9a4a41bb2..54b1aa7bf78b4 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -15,14 +15,7 @@ #include "Framework/ConfigContext.h" #include "Framework/DataProcessingDevice.h" #include "Framework/DataProcessorSpec.h" -#if __has_include(<DebugGUI/DebugGUI.h>) -#include <DebugGUI/DebugGUI.h> -#else -// the DebugGUI is in a separate package and is optional for O2, -// we include a header implementing GUI interface dummy methods -#pragma message "Building DPL without Debug GUI" -#include "Framework/NoDebugGUI.h" -#endif +#include "Framework/Plugins.h" #include "Framework/DeviceControl.h" #include "Framework/DeviceExecution.h" #include "Framework/DeviceInfo.h" @@ -30,7 +23,7 @@ #include "Framework/DeviceConfigInfo.h" #include "Framework/DeviceSpec.h" #include "Framework/DeviceState.h" -#include "Framework/FrameworkGUIDebugger.h" +#include "Framework/DebugGUI.h" #include "Framework/LocalRootFileService.h" #include "Framework/LogParsingHelpers.h" #include "Framework/Logger.h" @@ -42,15 +35,16 @@ #include "Framework/TextControlService.h" #include "Framework/CallbackService.h" #include "Framework/WorkflowSpec.h" +#include "Framework/Monitoring.h" +#include "Framework/DataProcessorInfo.h" +#include "Framework/DriverInfo.h" +#include "Framework/DriverControl.h" #include "ComputingResourceHelpers.h" #include "DataProcessingStatus.h" #include "DDSConfigHelpers.h" #include "O2ControlHelpers.h" #include "DeviceSpecHelpers.h" -#include "DriverControl.h" -#include "DriverInfo.h" -#include "DataProcessorInfo.h" #include "GraphvizHelpers.h" #include "PropertyTreeHelpers.h" #include "SimpleResourceManager.h" @@ -60,9 +54,13 @@ #include <Configuration/ConfigurationFactory.h> #include <Monitoring/MonitoringFactory.h> #include <InfoLogger/InfoLogger.hxx> +#include "ResourcesMonitoringHelper.h" #include "FairMQDevice.h" #include <fairmq/DeviceRunner.h> +#if __has_include(<fairmq/shmem/Monitor.h>) +#include <fairmq/shmem/Monitor.h> +#endif #include "options/FairMQProgOptions.h" #include <boost/program_options.hpp> @@ -102,6 +100,28 @@ #include <sys/un.h> #include <sys/wait.h> #include <unistd.h> +#include <execinfo.h> +#if defined(__linux__) && __has_include(<sched.h>) +#include <sched.h> +#elif __has_include(<linux/getcpu.h>) +#include <linux/getcpu.h> +#elif __has_include(<cpuid.h>) +#include <cpuid.h> +#define CPUID(INFO, LEAF, SUBLEAF) __cpuid_count(LEAF, SUBLEAF, INFO[0], INFO[1], INFO[2], INFO[3]) +#define GETCPU(CPU) \ + { \ + uint32_t CPUInfo[4]; \ + CPUID(CPUInfo, 1, 0); \ + /* CPUInfo[1] is EBX, bits 24-31 are APIC ID */ \ + if ((CPUInfo[3] & (1 << 9)) == 0) { \ + CPU = -1; /* no APIC on chip */ \ + } else { \ + CPU = (unsigned)CPUInfo[1] >> 24; \ + } \ + if (CPU < 0) \ + CPU = 0; \ + } +#endif using namespace o2::monitoring; using namespace o2::configuration; @@ -138,8 +158,9 @@ std::istream& operator>>(std::istream& in, enum TerminationPolicy& policy) policy = TerminationPolicy::QUIT; } else if (token == "wait") { policy = TerminationPolicy::WAIT; - } else + } else { in.setstate(std::ios_base::failbit); + } return in; } @@ -149,13 +170,31 @@ std::ostream& operator<<(std::ostream& out, const enum TerminationPolicy& policy out << "quit"; } else if (policy == TerminationPolicy::WAIT) { out << "wait"; - } else + } else { out.setstate(std::ios_base::failbit); + } return out; } } // namespace framework } // namespace o2 +size_t current_time_with_ms() +{ + long ms; // Milliseconds + time_t s; // Seconds + struct timespec spec; + + clock_gettime(CLOCK_REALTIME, &spec); + + s = spec.tv_sec; + ms = round(spec.tv_nsec / 1.0e6); // Convert nanoseconds to milliseconds + if (ms > 999) { + s++; + ms = 0; + } + return s * 1000 + ms; +} + // Read from a given fd and print it. // return true if we can still read from it, // return false if we need to close the input pipe. @@ -228,24 +267,25 @@ bool areAllChildrenGone(std::vector<DeviceInfo>& infos) } /// Calculate exit code -int calculateExitCode(std::vector<DeviceInfo>& infos) +int calculateExitCode(DeviceSpecs& deviceSpecs, DeviceInfos& infos) { + std::regex regexp(R"(^\[([\d+:]*)\]\[\w+\] )"); int exitCode = 0; - for (auto& info : infos) { + for (size_t di = 0; di < deviceSpecs.size(); ++di) { + auto& info = infos[di]; + auto& spec = deviceSpecs[di]; if (exitCode == 0 && info.maxLogLevel >= LogParsingHelpers::LogLevel::Error) { - LOG(ERROR) << "Child " << info.pid << " had at least one " - << "message above severity ERROR: " << info.lastError; + LOG(ERROR) << "SEVERE: Device " << spec.name << " (" << info.pid << ") had at least one " + << "message above severity ERROR: " << std::regex_replace(info.firstError, regexp, ""); exitCode = 1; } } return exitCode; } -int createPipes(int maxFd, int* pipes) +void createPipes(int* pipes) { auto p = pipe(pipes); - maxFd = maxFd > pipes[0] ? maxFd : pipes[0]; - maxFd = maxFd > pipes[1] ? maxFd : pipes[1]; if (p == -1) { std::cerr << "Unable to create PIPE: "; @@ -265,7 +305,6 @@ int createPipes(int maxFd, int* pipes) // Kill immediately both the parent and all the children kill(-1 * getpid(), SIGKILL); } - return maxFd; } // We don't do anything in the signal handler but @@ -287,6 +326,11 @@ static void handle_sigint(int) /// Helper to invoke shared memory cleanup void cleanupSHM(std::string const& uniqueWorkflowId) { +#if __has_include(<fairmq/shmem/Monitor.h>) + using namespace fair::mq::shmem; + Monitor::Cleanup(SessionId{"dpl_" + uniqueWorkflowId}, false); +#else + // Old code, invoking external fairmq-shmmonitor auto shmCleanup = fmt::format("fairmq-shmmonitor --cleanup -s dpl_{} 2>&1 >/dev/null", uniqueWorkflowId); LOG(debug) << "Cleaning up shm memory session with " << shmCleanup; @@ -294,6 +338,7 @@ void cleanupSHM(std::string const& uniqueWorkflowId) if (result != 0) { LOG(error) << "Unable to cleanup shared memory, run " << shmCleanup << "by hand to fix"; } +#endif } static void handle_sigchld(int) { sigchld_requested = true; } @@ -302,8 +347,7 @@ void spawnRemoteDevice(std::string const& forwardedStdin, DeviceSpec const& spec, DeviceControl& control, DeviceExecution& execution, - std::vector<DeviceInfo>& deviceInfos, - int& maxFd) + std::vector<DeviceInfo>& deviceInfos) { LOG(INFO) << "Starting " << spec.id << " as remote device"; DeviceInfo info; @@ -350,20 +394,35 @@ void log_callback(uv_poll_t* handle, int status, int events) /// new child void spawnDevice(std::string const& forwardedStdin, DeviceSpec const& spec, + DriverInfo& driverInfo, DeviceControl& control, DeviceExecution& execution, std::vector<DeviceInfo>& deviceInfos, - int& maxFd, uv_loop_t* loop, - std::vector<uv_poll_t*> handles) + ServiceRegistry& serviceRegistry, + boost::program_options::variables_map& varmap, + uv_loop_t* loop, + std::vector<uv_poll_t*> handles, + unsigned parentCPU, + unsigned parentNode) { int childstdin[2]; int childstdout[2]; int childstderr[2]; - maxFd = createPipes(maxFd, childstdin); - maxFd = createPipes(maxFd, childstdout); - maxFd = createPipes(maxFd, childstderr); + createPipes(childstdin); + createPipes(childstdout); + createPipes(childstderr); + + // FIXME: this might not work when more than one DPL driver on the same + // machine. Hopefully we do not care. + // Not how the first port is actually used to broadcast clients. + driverInfo.tracyPort++; + for (auto& service : spec.services) { + if (service.preFork != nullptr) { + service.preFork(serviceRegistry, varmap); + } + } // If we have a framework id, it means we have already been respawned // and that we are in a child. If not, we need to fork and re-exec, adding // the framework-id as one of the options. @@ -388,12 +447,50 @@ void spawnDevice(std::string const& forwardedStdin, dup2(childstdin[0], STDIN_FILENO); dup2(childstdout[1], STDOUT_FILENO); dup2(childstderr[1], STDERR_FILENO); + auto portS = std::to_string(driverInfo.tracyPort); + setenv("TRACY_PORT", portS.c_str(), 1); + for (auto& service : spec.services) { + if (service.postForkChild != nullptr) { + service.postForkChild(serviceRegistry); + } + } + for (auto& env : execution.environ) { + char* formatted = strdup(fmt::format(env, + fmt::arg("timeslice0", spec.inputTimesliceId), + fmt::arg("timeslice1", spec.inputTimesliceId + 1), + fmt::arg("timeslice4", spec.inputTimesliceId + 4)) + .c_str()); + putenv(formatted); + } execvp(execution.args[0], execution.args.data()); } - + if (varmap.count("post-fork-command")) { + auto templateCmd = varmap["post-fork-command"]; + auto cmd = fmt::format(templateCmd.as<std::string>(), + fmt::arg("pid", id), + fmt::arg("id", spec.id), + fmt::arg("cpu", parentCPU), + fmt::arg("node", parentNode), + fmt::arg("name", spec.name), + fmt::arg("timeslice0", spec.inputTimesliceId), + fmt::arg("timeslice1", spec.inputTimesliceId + 1), + fmt::arg("rank0", spec.rank), + fmt::arg("maxRank0", spec.nSlots)); + int err = system(cmd.c_str()); + if (err) { + LOG(error) << "Post fork command `" << cmd << "` returned with status " << err; + } + LOG(debug) << "Successfully executed `" << cmd; + } // This is the parent. We close the write end of // the child pipe and and keep track of the fd so // that we can later select on it. + for (auto& service : spec.services) { + if (service.postForkParent != nullptr) { + service.postForkParent(serviceRegistry); + } + } + struct sigaction sa_handle_int; sa_handle_int.sa_handler = handle_sigint; sigemptyset(&sa_handle_int.sa_mask); @@ -414,6 +511,7 @@ void spawnDevice(std::string const& forwardedStdin, info.dataRelayerViewIndex = Metric2DViewIndex{"data_relayer", 0, 0, {}}; info.variablesViewIndex = Metric2DViewIndex{"matcher_variables", 0, 0, {}}; info.queriesViewIndex = Metric2DViewIndex{"data_queries", 0, 0, {}}; + info.tracyPort = driverInfo.tracyPort; deviceInfos.emplace_back(info); // Let's add also metrics information for the given device @@ -455,7 +553,7 @@ void spawnDevice(std::string const& forwardedStdin, addPoller(deviceInfos.size() - 1, childstderr[0]); } -void updateMetricsNames(DriverInfo& state, std::vector<DeviceMetricsInfo> const& metricsInfos) +void updateMetricsNames(DriverInfo& driverInfo, std::vector<DeviceMetricsInfo> const& metricsInfos) { // Calculate the unique set of metrics, as available in the metrics service static std::unordered_set<std::string> allMetricsNames; @@ -464,13 +562,27 @@ void updateMetricsNames(DriverInfo& state, std::vector<DeviceMetricsInfo> const& allMetricsNames.insert(std::string(labelsPairs.label)); } } + for (const auto& labelsPairs : driverInfo.metrics.metricLabelsIdx) { + allMetricsNames.insert(std::string(labelsPairs.label)); + } std::vector<std::string> result(allMetricsNames.begin(), allMetricsNames.end()); std::sort(result.begin(), result.end()); - state.availableMetrics.swap(result); + driverInfo.availableMetrics.swap(result); } -void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpecs const& specs, - DeviceControls& controls, std::vector<DeviceMetricsInfo>& metricsInfos) +struct LogProcessingState { + bool didProcessLog = false; + bool didProcessControl = true; + bool didProcessConfig = true; + bool didProcessMetric = false; + bool hasNewMetric = false; +}; + +LogProcessingState processChildrenOutput(DriverInfo& driverInfo, + DeviceInfos& infos, + DeviceSpecs const& specs, + DeviceControls& controls, + std::vector<DeviceMetricsInfo>& metricsInfos) { // Display part. All you need to display should actually be in // `infos`. @@ -485,6 +597,8 @@ void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpe ParsedConfigMatch configMatch; const std::string delimiter("\n"); bool hasNewMetric = false; + LogProcessingState result; + for (size_t di = 0, de = infos.size(); di < de; ++di) { DeviceInfo& info = infos[di]; DeviceControl& control = controls[di]; @@ -535,6 +649,7 @@ void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpe // We use this callback to cache which metrics are needed to provide a // the DataRelayer view. DeviceMetricsHelper::processMetric(metricMatch, metrics, newMetricCallback); + result.didProcessMetric = true; } else if (logLevel == LogParsingHelpers::LogLevel::Info && parseControl(token, match)) { auto command = match[1]; auto arg = match[2]; @@ -555,8 +670,10 @@ void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpe // FIXME: this should really be a policy... doToMatchingPid(info.pid, [](DeviceInfo& info) { info.streamingState = StreamingState::EndOfStreaming; }); } + result.didProcessControl = true; } else if (logLevel == LogParsingHelpers::LogLevel::Info && DeviceConfigHelper::parseConfig(token, configMatch)) { DeviceConfigHelper::processConfig(configMatch, info); + result.didProcessConfig = true; } else if (!control.quiet && (token.find(control.logFilter) != std::string::npos) && logLevel >= control.logLevel) { assert(info.historyPos >= 0); @@ -565,6 +682,7 @@ void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpe info.historyLevel[info.historyPos] = logLevel; info.historyPos = (info.historyPos + 1) % info.history.size(); std::cout << "[" << info.pid << ":" << spec.name << "]: " << token << std::endl; + result.didProcessLog = true; } // We keep track of the maximum log error a // device has seen. @@ -574,6 +692,9 @@ void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpe } if (logLevel == LogParsingHelpers::LogLevel::Error) { info.lastError = token; + if (info.firstError.empty()) { + info.firstError = token; + } } s.remove_prefix(pos + delimiter.length()); } @@ -581,13 +702,12 @@ void processChildrenOutput(DriverInfo& driverInfo, DeviceInfos& infos, DeviceSpe info.unprinted = std::string(s); O2_SIGNPOST_END(DriverStatus::ID, DriverStatus::BYTES_PROCESSED, oldSize - info.unprinted.size(), 0, 0); } + result.hasNewMetric = hasNewMetric; if (hasNewMetric) { hasNewMetric = false; updateMetricsNames(driverInfo, metricsInfos); } - // FIXME: for the gui to work correctly I would actually need to - // run the loop more often and update whenever enough time has - // passed. + return result; } // Process all the sigchld which are pending @@ -618,8 +738,7 @@ bool processSigChild(DeviceInfos& infos) return hasError; } - -int doChild(int argc, char** argv, const o2::framework::DeviceSpec& spec, TerminationPolicy errorPolicy, +int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, const o2::framework::DeviceSpec& spec, TerminationPolicy errorPolicy, uv_loop_t* loop) { fair::Logger::SetConsoleColor(false); @@ -640,10 +759,6 @@ int doChild(int argc, char** argv, const o2::framework::DeviceSpec& spec, Termin r.fConfig.AddToCmdLineOptions(optsDesc, true); }); - // We initialise this in the driver, because different drivers might have - // different versions of the service - ServiceRegistry serviceRegistry; - // This is to control lifetime. All these services get destroyed // when the runner is done. std::unique_ptr<SimpleRawDeviceService> simpleRawDeviceService; @@ -660,8 +775,8 @@ int doChild(int argc, char** argv, const o2::framework::DeviceSpec& spec, Termin simpleRawDeviceService = std::make_unique<SimpleRawDeviceService>(nullptr, spec); - serviceRegistry.registerService<RawDeviceService>(simpleRawDeviceService.get()); - serviceRegistry.registerService<DeviceSpec>(&spec); + serviceRegistry.registerService(ServiceRegistryHelpers::handleForService<RawDeviceService>(simpleRawDeviceService.get())); + serviceRegistry.registerService(ServiceRegistryHelpers::handleForService<DeviceSpec>(&spec)); // The decltype stuff is to be able to compile with both new and old // FairMQ API (one which uses a shared_ptr, the other one a unique_ptr. @@ -672,22 +787,35 @@ int doChild(int argc, char** argv, const o2::framework::DeviceSpec& spec, Termin serviceRegistry.get<RawDeviceService>().setDevice(device.get()); r.fDevice = std::move(device); fair::Logger::SetConsoleColor(false); + /// Create all the requested services and initialise them for (auto& service : spec.services) { - LOG(info) << "Initialising service " << service.name; - auto handle = service.init(serviceRegistry, *deviceState.get(), r.fConfig); - serviceRegistry.registerService(handle.hash, handle.instance); + LOG(debug) << "Declaring service " << service.name; + serviceRegistry.declareService(service, *deviceState.get(), r.fConfig); + } + if (ResourcesMonitoringHelper::isResourcesMonitoringEnabled(spec.resourceMonitoringInterval)) { + serviceRegistry.get<Monitoring>().enableProcessMonitoring(spec.resourceMonitoringInterval); } }; runner.AddHook<fair::mq::hooks::InstantiateDevice>(afterConfigParsingCallback); return runner.Run(); } catch (boost::exception& e) { - LOG(ERROR) << "Unhandled exception reached the top of main, device shutting down. Details follow: \n" + LOG(ERROR) << "Unhandled boost::exception reached the top of main, device shutting down. Details follow: \n" << boost::current_exception_diagnostic_information(true); return 1; + } catch (o2::framework::RuntimeErrorRef e) { + auto& err = o2::framework::error_from_ref(e); + if (err.maxBacktrace != 0) { + LOG(ERROR) << "Unhandled o2::framework::runtime_error reached the top of main, device shutting down. Details follow: \n"; + backtrace_symbols_fd(err.backtrace, err.maxBacktrace, STDERR_FILENO); + } else { + LOG(ERROR) << "Unhandled o2::framework::runtime_error reached the top of main, device shutting down." + " Recompile with DPL_ENABLE_BACKTRACE=1 to get more information."; + } + return 1; } catch (std::exception& e) { - LOG(ERROR) << "Unhandled exception reached the top of main: " << e.what() << ", device shutting down."; + LOG(ERROR) << "Unhandled std::exception reached the top of main: " << e.what() << ", device shutting down."; return 1; } catch (...) { LOG(ERROR) << "Unknown exception reached the top of main.\n"; @@ -706,6 +834,7 @@ struct GuiCallbackContext { uint64_t frameLast; float* frameLatency; float* frameCost; + DebugGUI* plugin; void* window; bool* guiQuitRequested; std::function<void(void)> callback; @@ -714,9 +843,12 @@ struct GuiCallbackContext { void gui_callback(uv_timer_s* ctx) { GuiCallbackContext* gui = reinterpret_cast<GuiCallbackContext*>(ctx->data); + if (gui->plugin == nullptr) { + return; + } uint64_t frameStart = uv_hrtime(); uint64_t frameLatency = frameStart - gui->frameLast; - *(gui->guiQuitRequested) = (pollGUI(gui->window, gui->callback) == false); + *(gui->guiQuitRequested) = (gui->plugin->pollGUI(gui->window, gui->callback) == false); uint64_t frameEnd = uv_hrtime(); *(gui->frameCost) = (frameEnd - frameStart) / 1000000; *(gui->frameLatency) = frameLatency / 1000000; @@ -737,6 +869,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, DriverControl& driverControl, DriverInfo& driverInfo, std::vector<DeviceMetricsInfo>& metricsInfos, + boost::program_options::variables_map& varmap, std::string frameworkId) { DeviceSpecs deviceSpecs; @@ -756,12 +889,39 @@ int runStateMachine(DataProcessorSpecs const& workflow, auto resourceManager = std::make_unique<SimpleResourceManager>(resources); + DebugGUI* debugGUI = nullptr; void* window = nullptr; - decltype(gui::getGUIDebugger(infos, deviceSpecs, dataProcessorInfos, metricsInfos, driverInfo, controls, driverControl)) debugGUICallback; + decltype(debugGUI->getGUIDebugger(infos, deviceSpecs, dataProcessorInfos, metricsInfos, driverInfo, controls, driverControl)) debugGUICallback; // An empty frameworkId means this is the driver, so we initialise the GUI if (driverInfo.batch == false && frameworkId.empty()) { - window = initGUI("O2 Framework debug GUI"); + auto initDebugGUI = []() -> DebugGUI* { + uv_lib_t supportLib; + int result = 0; + #ifdef __APPLE__ + result = uv_dlopen("libO2FrameworkGUISupport.dylib", &supportLib); + #else + result = uv_dlopen("libO2FrameworkGUISupport.so", &supportLib); + #endif + if (result == -1) { + LOG(ERROR) << uv_dlerror(&supportLib); + return nullptr; + } + void* callback = nullptr; + DPLPluginHandle* (*dpl_plugin_callback)(DPLPluginHandle*); + + result = uv_dlsym(&supportLib, "dpl_plugin_callback", (void**)&dpl_plugin_callback); + if (result == -1) { + LOG(ERROR) << uv_dlerror(&supportLib); + return nullptr; + } + DPLPluginHandle* pluginInstance = dpl_plugin_callback(nullptr); + return PluginManager::getByName<DebugGUI>(pluginInstance, "ImGUIDebugGUI"); + }; + debugGUI = initDebugGUI(); + if (debugGUI) { + window = debugGUI->initGUI("O2 Framework debug GUI"); + } } if (driverInfo.batch == false && window == nullptr && frameworkId.empty()) { LOG(WARN) << "Could not create GUI. Switching to batch mode. Do you have GLFW on your system?"; @@ -784,6 +944,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, } GuiCallbackContext guiContext; + guiContext.plugin = debugGUI; guiContext.frameLast = uv_hrtime(); guiContext.frameLatency = &driverInfo.frameLatency; guiContext.frameCost = &driverInfo.frameCost; @@ -793,6 +954,15 @@ int runStateMachine(DataProcessorSpecs const& workflow, uv_timer_t force_step_timer; uv_timer_init(loop, &force_step_timer); + // We initialise this in the driver, because different drivers might have + // different versions of the service + ServiceRegistry serviceRegistry; + std::vector<ServiceMetricHandling> metricProcessingCallbacks; + std::vector<ServicePreSchedule> preScheduleCallbacks; + std::vector<ServicePostSchedule> postScheduleCallbacks; + + bool guiDeployedOnce = false; + while (true) { // If control forced some transition on us, we push it to the queue. if (driverControl.forcedTransitions.empty() == false) { @@ -845,7 +1015,11 @@ int runStateMachine(DataProcessorSpecs const& workflow, /// Cleanup the shared memory for the uniqueWorkflowId, in /// case we are unlucky and an old one is already present. - cleanupSHM(driverInfo.uniqueWorkflowId); + if (driverInfo.noSHMCleanup) { + LOGP(warning, "Not cleaning up shared memory."); + } else { + cleanupSHM(driverInfo.uniqueWorkflowId); + } /// After INIT we go into RUNNING and eventually to SCHEDULE from /// there and back into running. This is because the general case /// would be that we start an application and then we wait for @@ -886,17 +1060,56 @@ int runStateMachine(DataProcessorSpecs const& workflow, break; case DriverState::MATERIALISE_WORKFLOW: try { + auto workflowState = WorkflowHelpers::verifyWorkflow(workflow); + if (driverInfo.batch == true && workflowState == WorkflowParsingState::Empty) { + throw runtime_error("Empty workflow provided while running in batch mode."); + } DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(workflow, driverInfo.channelPolicies, driverInfo.completionPolicies, driverInfo.dispatchPolicies, deviceSpecs, *resourceManager, - driverInfo.uniqueWorkflowId); + driverInfo.uniqueWorkflowId, + !varmap["no-IPC"].as<bool>(), + driverInfo.resourcesMonitoringInterval, + varmap["channel-prefix"].as<std::string>()); + metricProcessingCallbacks.clear(); + for (auto& device : deviceSpecs) { + for (auto& service : device.services) { + if (service.metricHandling) { + metricProcessingCallbacks.push_back(service.metricHandling); + } + } + } + preScheduleCallbacks.clear(); + for (auto& device : deviceSpecs) { + for (auto& service : device.services) { + if (service.preSchedule) { + preScheduleCallbacks.push_back(service.preSchedule); + } + } + } + postScheduleCallbacks.clear(); + for (auto& device : deviceSpecs) { + for (auto& service : device.services) { + if (service.postSchedule) { + postScheduleCallbacks.push_back(service.postSchedule); + } + } + } + // This should expand nodes so that we can build a consistent DAG. } catch (std::runtime_error& e) { std::cerr << "Invalid workflow: " << e.what() << std::endl; return 1; + } catch (o2::framework::RuntimeErrorRef ref) { + auto& err = o2::framework::error_from_ref(ref); +#ifdef DPL_ENABLE_BACKTRACE + backtrace_symbols_fd(err.backtrace, err.maxBacktrace, STDERR_FILENO); +#endif + std::cerr << "Invalid workflow: " << err.what << std::endl; + return 1; } catch (...) { std::cerr << "Unknown error while materialising workflow"; return 1; @@ -909,7 +1122,9 @@ int runStateMachine(DataProcessorSpecs const& workflow, } for (auto& spec : deviceSpecs) { if (spec.id == frameworkId) { - return doChild(driverInfo.argc, driverInfo.argv, spec, driverInfo.errorPolicy, loop); + return doChild(driverInfo.argc, driverInfo.argv, + serviceRegistry, spec, + driverInfo.errorPolicy, loop); } } { @@ -935,10 +1150,11 @@ int runStateMachine(DataProcessorSpecs const& workflow, // because getGUIDebugger actually recreates the GUI state. if (window) { uv_timer_stop(&gui_timer); - guiContext.callback = gui::getGUIDebugger(infos, deviceSpecs, dataProcessorInfos, metricsInfos, driverInfo, controls, driverControl); + guiContext.callback = debugGUI->getGUIDebugger(infos, deviceSpecs, dataProcessorInfos, metricsInfos, driverInfo, controls, driverControl); guiContext.window = window; gui_timer.data = &guiContext; uv_timer_start(&gui_timer, gui_callback, 0, 20); + guiDeployedOnce = true; } break; case DriverState::SCHEDULE: { @@ -961,18 +1177,36 @@ int runStateMachine(DataProcessorSpecs const& workflow, std::ostringstream forwardedStdin; WorkflowSerializationHelpers::dump(forwardedStdin, workflow, dataProcessorInfos); infos.reserve(deviceSpecs.size()); + + // This is guaranteed to be a single CPU. + unsigned parentCPU = -1; + unsigned parentNode = -1; +#if defined(__linux__) && __has_include(<sched.h>) + parentCPU = sched_getcpu(); +#elif __has_include(<linux/getcpu.h>) + getcpu(&parentCPU, &parentNode, nullptr); +#elif __has_include(<cpuid.h>) + // FIXME: this is a last resort as it is apparently buggy + // on some Intel CPUs. + GETCPU(parentCPU); +#endif + for (auto& callback : preScheduleCallbacks) { + callback(serviceRegistry, varmap); + } for (size_t di = 0; di < deviceSpecs.size(); ++di) { if (deviceSpecs[di].resource.hostname != driverInfo.deployHostname) { spawnRemoteDevice(forwardedStdin.str(), - deviceSpecs[di], controls[di], deviceExecutions[di], infos, - driverInfo.maxFd); + deviceSpecs[di], controls[di], deviceExecutions[di], infos); } else { spawnDevice(forwardedStdin.str(), - deviceSpecs[di], controls[di], deviceExecutions[di], infos, - driverInfo.maxFd, loop, pollHandles); + deviceSpecs[di], driverInfo, + controls[di], deviceExecutions[di], infos, + serviceRegistry, varmap, loop, pollHandles, parentCPU, parentNode); } } - driverInfo.maxFd += 1; + for (auto& callback : postScheduleCallbacks) { + callback(serviceRegistry, varmap); + } assert(infos.empty() == false); LOG(INFO) << "Redeployment of configuration done."; } break; @@ -999,17 +1233,30 @@ int runStateMachine(DataProcessorSpecs const& workflow, driverInfo.states.push_back(DriverState::RUNNING); driverInfo.states.push_back(DriverState::REDEPLOY_GUI); driverInfo.states.push_back(DriverState::SCHEDULE); - } else if (deviceSpecs.size() == 0) { + } else if (deviceSpecs.empty() && driverInfo.batch == true) { LOG(INFO) << "No device resulting from the workflow. Quitting."; // If there are no deviceSpecs, we exit. driverInfo.states.push_back(DriverState::EXIT); + } else if (deviceSpecs.empty() && driverInfo.batch == false && !guiDeployedOnce) { + // In case of an empty workflow, we need to deploy the GUI at least once. + driverInfo.states.push_back(DriverState::RUNNING); + driverInfo.states.push_back(DriverState::REDEPLOY_GUI); } else { driverInfo.states.push_back(DriverState::RUNNING); } { uint64_t inputProcessingStart = uv_hrtime(); auto inputProcessingLatency = inputProcessingStart - inputProcessingLast; - processChildrenOutput(driverInfo, infos, deviceSpecs, controls, metricsInfos); + auto outputProcessing = processChildrenOutput(driverInfo, infos, deviceSpecs, controls, metricsInfos); + if (outputProcessing.didProcessMetric) { + size_t timestamp = current_time_with_ms(); + for (auto& callback : metricProcessingCallbacks) { + callback(serviceRegistry, metricsInfos, deviceSpecs, infos, driverInfo.metrics, timestamp); + } + for (auto& metricsInfo : metricsInfos) { + std::fill(metricsInfo.changed.begin(), metricsInfo.changed.end(), false); + } + } auto inputProcessingEnd = uv_hrtime(); driverInfo.inputProcessingCost = (inputProcessingEnd - inputProcessingStart) / 1000000; driverInfo.inputProcessingLatency = (inputProcessingLatency) / 1000000; @@ -1029,7 +1276,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, uv_timer_start(&force_step_timer, single_step_callback, 0, 300); driverInfo.states.push_back(DriverState::HANDLE_CHILDREN); break; - case DriverState::HANDLE_CHILDREN: + case DriverState::HANDLE_CHILDREN: { // Run any pending libUV event loop, block if // any, so that we do not consume CPU time when the driver is // idle. @@ -1037,13 +1284,23 @@ int runStateMachine(DataProcessorSpecs const& workflow, // I allow queueing of more sigchld only when // I process the previous call if (forceful_exit == true) { - LOG(INFO) << "Forceful exit requested."; + static bool forcefulExitMessage = true; + if (forcefulExitMessage) { + LOG(INFO) << "Forceful exit requested."; + forcefulExitMessage = false; + } killChildren(infos, SIGCONT); killChildren(infos, SIGKILL); } sigchld_requested = false; driverInfo.sigchldRequested = false; - processChildrenOutput(driverInfo, infos, deviceSpecs, controls, metricsInfos); + auto outputProcessing = processChildrenOutput(driverInfo, infos, deviceSpecs, controls, metricsInfos); + if (outputProcessing.didProcessMetric) { + size_t timestamp = current_time_with_ms(); + for (auto& callback : metricProcessingCallbacks) { + callback(serviceRegistry, metricsInfos, deviceSpecs, infos, driverInfo.metrics, timestamp); + } + } hasError = processSigChild(infos); if (areAllChildrenGone(infos) == true && (guiQuitRequested || (checkIfCanExit(infos) == true) || graceful_exit)) { @@ -1059,22 +1316,42 @@ int runStateMachine(DataProcessorSpecs const& workflow, driverInfo.states.push_back(DriverState::QUIT_REQUESTED); } else { } - break; + } break; case DriverState::EXIT: { + if (ResourcesMonitoringHelper::isResourcesMonitoringEnabled(driverInfo.resourcesMonitoringInterval)) { + LOG(INFO) << "Dumping performance metrics to performanceMetrics.json file"; + auto performanceMetrics = o2::monitoring::ProcessMonitor::getAvailableMetricsNames(); + performanceMetrics.push_back("arrow-bytes-delta"); + performanceMetrics.push_back("aod-bytes-read-uncompressed"); + performanceMetrics.push_back("aod-bytes-read-compressed"); + performanceMetrics.push_back("aod-total-read-calls"); + performanceMetrics.push_back("aod-file-read-path"); + ResourcesMonitoringHelper::dumpMetricsToJSON(metricsInfos, driverInfo.metrics, deviceSpecs, performanceMetrics); + } // This is a clean exit. Before we do so, if required, // we dump the configuration of all the devices so that - // we can reuse it + // we can reuse it. Notice we do not dump anything if + // the workflow was not really run. + // NOTE: is this really what we want? should we run + // SCHEDULE and dump the full configuration as well? + if (infos.empty()) { + return 0; + } boost::property_tree::ptree finalConfig; assert(infos.size() == deviceSpecs.size()); for (size_t di = 0; di < infos.size(); ++di) { auto info = infos[di]; auto spec = deviceSpecs[di]; - PropertyTreeHelpers::merge(finalConfig, info.currentConfig, spec.name); + finalConfig.put_child(spec.name, info.currentConfig); } LOG(INFO) << "Dumping used configuration in dpl-config.json"; boost::property_tree::write_json("dpl-config.json", finalConfig); - cleanupSHM(driverInfo.uniqueWorkflowId); - return calculateExitCode(infos); + if (driverInfo.noSHMCleanup) { + LOGP(warning, "Not cleaning up shared memory."); + } else { + cleanupSHM(driverInfo.uniqueWorkflowId); + } + return calculateExitCode(deviceSpecs, infos); } case DriverState::PERFORM_CALLBACKS: for (auto& callback : driverControl.callbacks) { @@ -1137,6 +1414,68 @@ bool isOutputToPipe() return ((s.st_mode & S_IFIFO) != 0); } +void overrideSuffix(ConfigContext& ctx, WorkflowSpec& workflow) +{ + auto suffix = ctx.options().get<std::string>("workflow-suffix"); + if (suffix.empty()) { + return; + } + for (auto& processor : workflow) { + processor.name = processor.name + suffix; + } +} + +void overrideCloning(ConfigContext& ctx, WorkflowSpec& workflow) +{ + struct CloningSpec { + std::string templateMatcher; + std::string cloneName; + }; + auto s = ctx.options().get<std::string>("clone"); + std::vector<CloningSpec> specs; + std::string delimiter = ","; + + size_t pos = 0; + while (s.empty() == false) { + auto newPos = s.find(delimiter); + auto token = s.substr(0, newPos); + auto split = token.find(":"); + if (split == std::string::npos) { + throw std::runtime_error("bad clone definition. Syntax <template-processor>:<clone-name>"); + } + auto key = token.substr(0, split); + token.erase(0, split + 1); + size_t error; + std::string value = ""; + try { + auto numValue = std::stoll(token, &error, 10); + if (token[error] != '\0') { + throw std::runtime_error("bad name for clone:" + token); + } + value = key + "_c" + std::to_string(numValue); + } catch (std::invalid_argument& e) { + value = token; + } + specs.push_back({key, value}); + s.erase(0, newPos + (newPos == std::string::npos ? 0 : 1)); + } + if (s.empty() == false && specs.empty() == true) { + throw std::runtime_error("bad pipeline definition. Syntax <processor>:<pipeline>"); + } + + std::vector<DataProcessorSpec> extraSpecs; + for (auto& spec : specs) { + for (auto& processor : workflow) { + if (processor.name == spec.templateMatcher) { + auto clone = processor; + clone.name = spec.cloneName; + extraSpecs.push_back(clone); + } + } + } + workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); +} + void overridePipeline(ConfigContext& ctx, WorkflowSpec& workflow) { struct PipelineSpec { @@ -1336,23 +1675,6 @@ std::string debugTopoInfo(std::vector<DataProcessorSpec> const& specs, return out.str(); } -std::string debugWorkflow(std::vector<DataProcessorSpec> const& specs) -{ - std::ostringstream out; - for (auto& spec : specs) { - out << spec.name << "\n"; - // out << " Inputs:\n"; - // for (auto& ii : spec.inputs) { - // out << " - " << DataSpecUtils::describe(ii) << "\n"; - // } - // out << "\n Outputs:\n"; - // for (auto& ii : spec.outputs) { - // out << " - " << DataSpecUtils::describe(ii) << "\n"; - // } - } - return out.str(); -} - // This is a toy executor for the workflow spec // What it needs to do is: // @@ -1384,27 +1706,30 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, enum TerminationPolicy errorPolicy; bpo::options_description executorOptions("Executor options"); const char* helpDescription = "print help: short, full, executor, or processor name"; - executorOptions.add_options() // - ("help,h", bpo::value<std::string>()->implicit_value("short"), helpDescription) // - ("quiet,q", bpo::value<bool>()->zero_tokens()->default_value(false), "quiet operation") // - ("stop,s", bpo::value<bool>()->zero_tokens()->default_value(false), "stop before device start") // - ("single-step", bpo::value<bool>()->zero_tokens()->default_value(false), "start in single step mode") // - ("batch,b", bpo::value<bool>()->zero_tokens()->default_value(isatty(fileno(stdout)) == 0), "batch processing mode") // - ("hostname", bpo::value<std::string>()->default_value("localhost"), "hostname to deploy") // - ("resources", bpo::value<std::string>()->default_value(""), "resources allocated for the workflow") // - ("start-port,p", bpo::value<unsigned short>()->default_value(22000), "start port to allocate") // - ("port-range,pr", bpo::value<unsigned short>()->default_value(1000), "ports in range") // - ("completion-policy,c", bpo::value<TerminationPolicy>(&policy)->default_value(TerminationPolicy::QUIT), // - "what to do when processing is finished: quit, wait") // - ("error-policy", bpo::value<TerminationPolicy>(&errorPolicy)->default_value(TerminationPolicy::QUIT), // - "what to do when a device has an error: quit, wait") // - ("graphviz,g", bpo::value<bool>()->zero_tokens()->default_value(false), "produce graph output") // - ("timeout,t", bpo::value<uint64_t>()->default_value(0), "forced exit timeout (in seconds)") // - ("dds,D", bpo::value<bool>()->zero_tokens()->default_value(false), "create DDS configuration") // - ("dump-workflow,dump", bpo::value<bool>()->zero_tokens()->default_value(false), "dump workflow as JSON") // - ("dump-workflow-file", bpo::value<std::string>()->default_value("-"), "file to which do the dump") // - ("run", bpo::value<bool>()->zero_tokens()->default_value(false), "run workflow merged so far") // - ("o2-control,o2", bpo::value<bool>()->zero_tokens()->default_value(false), "create O2 Control configuration"); + executorOptions.add_options() // + ("help,h", bpo::value<std::string>()->implicit_value("short"), helpDescription) // // + ("quiet,q", bpo::value<bool>()->zero_tokens()->default_value(false), "quiet operation") // // + ("stop,s", bpo::value<bool>()->zero_tokens()->default_value(false), "stop before device start") // // + ("single-step", bpo::value<bool>()->zero_tokens()->default_value(false), "start in single step mode") // // + ("batch,b", bpo::value<bool>()->zero_tokens()->default_value(isatty(fileno(stdout)) == 0), "batch processing mode") // // + ("no-cleanup", bpo::value<bool>()->zero_tokens()->default_value(false), "do not cleanup the shm segment") // // + ("hostname", bpo::value<std::string>()->default_value("localhost"), "hostname to deploy") // // + ("resources", bpo::value<std::string>()->default_value(""), "resources allocated for the workflow") // // + ("start-port,p", bpo::value<unsigned short>()->default_value(22000), "start port to allocate") // // + ("port-range,pr", bpo::value<unsigned short>()->default_value(1000), "ports in range") // // + ("completion-policy,c", bpo::value<TerminationPolicy>(&policy)->default_value(TerminationPolicy::QUIT), // // + "what to do when processing is finished: quit, wait") // // + ("error-policy", bpo::value<TerminationPolicy>(&errorPolicy)->default_value(TerminationPolicy::QUIT), // // + "what to do when a device has an error: quit, wait") // // + ("graphviz,g", bpo::value<bool>()->zero_tokens()->default_value(false), "produce graph output") // // + ("timeout,t", bpo::value<uint64_t>()->default_value(0), "forced exit timeout (in seconds)") // // + ("dds,D", bpo::value<bool>()->zero_tokens()->default_value(false), "create DDS configuration") // // + ("dump-workflow,dump", bpo::value<bool>()->zero_tokens()->default_value(false), "dump workflow as JSON") // // + ("dump-workflow-file", bpo::value<std::string>()->default_value("-"), "file to which do the dump") // // + ("run", bpo::value<bool>()->zero_tokens()->default_value(false), "run workflow merged so far") // // + ("no-IPC", bpo::value<bool>()->zero_tokens()->default_value(false), "disable IPC topology optimization") // // + ("o2-control,o2", bpo::value<bool>()->zero_tokens()->default_value(false), "create O2 Control configuration") // + ("resources-monitoring", bpo::value<unsigned short>()->default_value(0), "enable cpu/memory monitoring for provided interval in seconds"); // // some of the options must be forwarded by default to the device executorOptions.add(DeviceSpecHelpers::getForwardedDeviceOptions()); @@ -1591,7 +1916,6 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, initialiseDriverControl(varmap, driverControl); DriverInfo driverInfo; - driverInfo.maxFd = 0; driverInfo.states.reserve(10); driverInfo.sigintRequested = false; driverInfo.sigchldRequested = false; @@ -1601,6 +1925,7 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, driverInfo.argc = argc; driverInfo.argv = argv; driverInfo.batch = varmap["batch"].as<bool>(); + driverInfo.noSHMCleanup = varmap["no-cleanup"].as<bool>(); driverInfo.terminationPolicy = varmap["completion-policy"].as<TerminationPolicy>(); if (varmap["error-policy"].defaulted() && driverInfo.batch == false) { driverInfo.errorPolicy = TerminationPolicy::WAIT; @@ -1611,6 +1936,7 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, driverInfo.timeout = varmap["timeout"].as<uint64_t>(); driverInfo.deployHostname = varmap["hostname"].as<std::string>(); driverInfo.resources = varmap["resources"].as<std::string>(); + driverInfo.resourcesMonitoringInterval = varmap["resources-monitoring"].as<unsigned short>(); // FIXME: should use the whole dataProcessorInfos, actually... driverInfo.processorInfo = dataProcessorInfos; @@ -1631,6 +1957,7 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow, driverControl, driverInfo, gDeviceMetricsInfos, + varmap, frameworkId); } diff --git a/Framework/Core/src/verifyAODFile.cxx b/Framework/Core/src/verifyAODFile.cxx index 2864a259d2b45..6c4d8aa23f163 100644 --- a/Framework/Core/src/verifyAODFile.cxx +++ b/Framework/Core/src/verifyAODFile.cxx @@ -43,11 +43,11 @@ int main(int argc, char** argv) return 1; } - verifyTable<o2::aod::Collisions>(infile.get(), "O2collisions"); - verifyTable<o2::aod::Tracks>(infile.get(), "O2tracks"); - verifyTable<o2::aod::TracksCov>(infile.get(), "O2tracks"); - verifyTable<o2::aod::TracksExtra>(infile.get(), "O2tracks"); + verifyTable<o2::aod::Collisions>(infile.get(), "O2collision"); + verifyTable<o2::aod::StoredTracks>(infile.get(), "O2track"); + verifyTable<o2::aod::StoredTracksCov>(infile.get(), "O2track"); + verifyTable<o2::aod::TracksExtra>(infile.get(), "O2track"); verifyTable<o2::aod::Calos>(infile.get(), "O2calo"); - verifyTable<o2::aod::Muons>(infile.get(), "O2muon"); + verifyTable<o2::aod::StoredMuons>(infile.get(), "O2muon"); return 0; } diff --git a/Framework/Core/test/FrameworkCoreTestLinkDef.h b/Framework/Core/test/FrameworkCoreTestLinkDef.h index e7aa30ec99193..9c4de4d65057d 100644 --- a/Framework/Core/test/FrameworkCoreTestLinkDef.h +++ b/Framework/Core/test/FrameworkCoreTestLinkDef.h @@ -18,3 +18,9 @@ #pragma link C++ class o2::test::SimplePODClass + ; #pragma link C++ class std::vector < o2::test::TriviallyCopyable > +; #pragma link C++ class std::vector < o2::test::Polymorphic > +; + +#pragma link C++ class StepTHn + ; +#pragma link C++ class StepTHnT < TArrayF> + ; +#pragma link C++ class StepTHnT < TArrayD> + ; +#pragma link C++ typedef StepTHnF; +#pragma link C++ typedef StepTHnD; diff --git a/Framework/Core/test/Mocking.h b/Framework/Core/test/Mocking.h new file mode 100644 index 0000000000000..8a8577fdbad84 --- /dev/null +++ b/Framework/Core/test/Mocking.h @@ -0,0 +1,31 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "test_HelperMacros.h" +#include "Framework/ConfigContext.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/SimpleOptionsRetriever.h" +#include "Framework/WorkflowCustomizationHelpers.h" +#include "../src/WorkflowHelpers.h" + +std::unique_ptr<o2::framework::ConfigContext> makeEmptyConfigContext() +{ + using namespace o2::framework; + // FIXME: Ugly... We need to fix ownership and make sure the ConfigContext + // either owns or shares ownership of the registry. + std::vector<std::unique_ptr<ParamRetriever>> retrievers; + static std::vector<ConfigParamSpec> specs = WorkflowCustomizationHelpers::requiredWorkflowOptions(); + auto store = std::make_unique<ConfigParamStore>(specs, std::move(retrievers)); + store->preload(); + store->activate(); + static ConfigParamRegistry registry(std::move(store)); + auto context = std::make_unique<ConfigContext>(registry, 0, nullptr); + return context; +} diff --git a/Framework/Core/test/TestClasses.h b/Framework/Core/test/TestClasses.h index 8c6cd9e304dc3..992499ae586a8 100644 --- a/Framework/Core/test/TestClasses.h +++ b/Framework/Core/test/TestClasses.h @@ -7,14 +7,13 @@ // In applying this license CERN does 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 FRAMEWORK_TEST_CLASSES_H -#define FRAMEWORK_TEST_CLASSES_H +#ifndef O2_FRAMEWORK_TEST_CLASSES_H_ +#define O2_FRAMEWORK_TEST_CLASSES_H_ #include <Rtypes.h> +#include "Framework/OutputSpec.h" -namespace o2 -{ -namespace test +namespace o2::test { class TriviallyCopyable @@ -84,6 +83,5 @@ class SimplePODClass ClassDefNV(SimplePODClass, 1); }; -} // namespace test -} // namespace o2 -#endif // FRAMEWORK_TEST_CLASSES_H +} // namespace o2::test +#endif // O2_FRAMEWORK_TEST_CLASSES_H_ diff --git a/Framework/Core/test/benchmark_ASoA.cxx b/Framework/Core/test/benchmark_ASoA.cxx index 88448f5e04dd0..941187159c9cb 100644 --- a/Framework/Core/test/benchmark_ASoA.cxx +++ b/Framework/Core/test/benchmark_ASoA.cxx @@ -19,6 +19,7 @@ using namespace o2::framework; using namespace arrow; using namespace o2::soa; +DECLARE_SOA_STORE(); namespace test { DECLARE_SOA_COLUMN_FULL(X, x, float, "x"); @@ -27,6 +28,8 @@ DECLARE_SOA_COLUMN_FULL(Z, z, float, "z"); DECLARE_SOA_DYNAMIC_COLUMN(Sum, sum, [](float x, float y) { return x + y; }); } // namespace test +DECLARE_SOA_TABLE(TestTable, "AOD", "TESTTBL", test::X, test::Y, test::Z, test::Sum<test::X, test::Y>); + #ifdef __APPLE__ constexpr unsigned int maxrange = 10; #else @@ -47,13 +50,13 @@ static void BM_SimpleForLoop(benchmark::State& state) std::default_random_engine e1(1234567891); std::uniform_real_distribution<float> uniform_dist(0, 1); - for (size_t i = 0; i < state.range(0); ++i) { + for (auto i = 0; i < state.range(0); ++i) { foo[i] = XYZ{uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)}; } for (auto _ : state) { float sum = 0; - for (auto& xyz : foo) { + for (auto& _ : foo) { benchmark::DoNotOptimize(sum++); } } @@ -79,7 +82,7 @@ static void BM_TrackForLoop(benchmark::State& state) std::default_random_engine e1(1234567891); std::uniform_real_distribution<float> uniform_dist(0, 1); - for (size_t i = 0; i < state.range(0); ++i) { + for (auto i = 0; i < state.range(0); ++i) { foo[i] = TestTrack{ uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)}; @@ -114,7 +117,7 @@ static void BM_WholeTrackForLoop(benchmark::State& state) std::default_random_engine e1(1234567891); std::uniform_real_distribution<float> uniform_dist(0, 1); - for (auto i = 0u; i < state.range(0); ++i) { + for (auto i = 0; i < state.range(0); ++i) { foo[i] = TestTrack{ uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)}; @@ -149,7 +152,7 @@ static void BM_TrackForPhi(benchmark::State& state) std::default_random_engine e1(1234567891); std::uniform_real_distribution<float> uniform_dist(0, 1); - for (auto i = 0u; i < state.range(0); ++i) { + for (auto i = 0; i < state.range(0); ++i) { foo[i] = TestTrack{ uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)}; @@ -183,7 +186,7 @@ static void BM_SimpleForLoopWithOp(benchmark::State& state) std::default_random_engine e1(1234567891); std::uniform_real_distribution<float> uniform_dist(0, 1); - for (auto i = 0u; i < state.range(0); ++i) { + for (auto i = 0; i < state.range(0); ++i) { foo[i] = XYZ{uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)}; } @@ -308,98 +311,6 @@ static void BM_ASoADynamicColumnCall(benchmark::State& state) } state.SetBytesProcessed(state.iterations() * state.range(0) * sizeof(float) * 2); } - BENCHMARK(BM_ASoADynamicColumnCall)->Range(8, 8 << maxrange); -static void BM_ASoAGettersPhi(benchmark::State& state) -{ - // Seed with a real random value, if available - std::default_random_engine e1(1234567891); - std::uniform_real_distribution<float> uniform_dist(0, 1); - - TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); - for (auto i = 0; i < state.range(0); ++i) { - rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); - } - auto table = builder.finalize(); - - o2::aod::Tracks tracks{table}; - for (auto _ : state) { - state.PauseTiming(); - std::vector<float> out; - out.resize(state.range(0)); - float* result = out.data(); - state.ResumeTiming(); - for (auto& track : tracks) { - *result++ = asin(track.snp()) + track.alpha() + M_PI; - } - benchmark::DoNotOptimize(result); - } - state.SetBytesProcessed(state.iterations() * state.range(0) * sizeof(float) * 2); -} - -BENCHMARK(BM_ASoAGettersPhi)->Range(8, 8 << maxrange); - -static void BM_ASoAWholeTrackForLoop(benchmark::State& state) -{ - // Seed with a real random value, if available - std::default_random_engine e1(1234567891); - std::uniform_real_distribution<float> uniform_dist(0, 1); - - TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); - for (auto i = 0; i < state.range(0); ++i) { - rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); - } - auto table = builder.finalize(); - - o2::aod::Tracks tracks{table}; - for (auto _ : state) { - float sum = 0; - for (auto& track : tracks) { - sum += track.x() + track.alpha() + track.y() + track.z() + track.snp() + track.tgl() + track.signed1Pt(); - } - benchmark::DoNotOptimize(sum); - } - state.SetBytesProcessed(state.iterations() * state.range(0) * sizeof(float) * 6); -} - -BENCHMARK(BM_ASoAWholeTrackForLoop)->Range(8, 8 << maxrange); - -static void BM_ASoADynamicColumnPhi(benchmark::State& state) -{ - // Seed with a real random value, if available - std::default_random_engine e1(1234567891); - std::uniform_real_distribution<float> uniform_dist(0, 1); - - TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); - for (auto i = 0; i < state.range(0); ++i) { - rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); - } - auto table = builder.finalize(); - - o2::aod::Tracks tracks{table}; - for (auto _ : state) { - state.PauseTiming(); - std::vector<float> out; - out.resize(state.range(0)); - float* result = out.data(); - state.ResumeTiming(); - for (auto& track : tracks) { - *result++ = track.phi(); - } - benchmark::DoNotOptimize(result); - } - state.SetBytesProcessed(state.iterations() * state.range(0) * sizeof(float) * 2); -} -BENCHMARK(BM_ASoADynamicColumnPhi)->Range(8, 8 << maxrange); - BENCHMARK_MAIN(); diff --git a/Framework/Core/test/benchmark_ASoAHelpers.cxx b/Framework/Core/test/benchmark_ASoAHelpers.cxx index 3e17b294ab340..4c8bb53a0c0b9 100644 --- a/Framework/Core/test/benchmark_ASoAHelpers.cxx +++ b/Framework/Core/test/benchmark_ASoAHelpers.cxx @@ -19,6 +19,8 @@ using namespace o2::framework; using namespace arrow; using namespace o2::soa; +/// FIXME: do not use data model tables + namespace test { DECLARE_SOA_COLUMN_FULL(X, x, float, "x"); @@ -162,21 +164,20 @@ static void BM_ASoAHelpersNaiveTracksPairs(benchmark::State& state) std::uniform_real_distribution<float> uniform_dist(0, 1); TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); + auto rowWriter = builder.cursor<o2::aod::Calos>(); for (auto i = 0; i < state.range(0); ++i) { rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); } auto table = builder.finalize(); - o2::aod::Tracks tracks{table}; + o2::aod::Calos Calos{table}; int64_t count = 0; for (auto _ : state) { count = 0; - for (auto t0 = tracks.begin(); t0 + 1 != tracks.end(); ++t0) { - for (auto t1 = t0 + 1; t1 != tracks.end(); ++t1) { + for (auto t0 = Calos.begin(); t0 + 1 != Calos.end(); ++t0) { + for (auto t1 = t0 + 1; t1 != Calos.end(); ++t1) { auto comb = std::make_tuple(t0, t1); count++; benchmark::DoNotOptimize(comb); @@ -197,24 +198,23 @@ static void BM_ASoAHelpersNaiveTracksFives(benchmark::State& state) std::uniform_real_distribution<float> uniform_dist(0, 1); TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); + auto rowWriter = builder.cursor<o2::aod::Calos>(); for (auto i = 0; i < state.range(0); ++i) { rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); } auto table = builder.finalize(); - o2::aod::Tracks tracks{table}; + o2::aod::Calos calos{table}; int64_t count = 0; for (auto _ : state) { count = 0; - for (auto t0 = tracks.begin(); t0 + 4 != tracks.end(); ++t0) { - for (auto t1 = t0 + 1; t1 + 3 != tracks.end(); ++t1) { - for (auto t2 = t1 + 1; t2 + 2 != tracks.end(); ++t2) { - for (auto t3 = t2 + 1; t3 + 1 != tracks.end(); ++t3) { - for (auto t4 = t3 + 1; t4 != tracks.end(); ++t4) { + for (auto t0 = calos.begin(); t0 + 4 != calos.end(); ++t0) { + for (auto t1 = t0 + 1; t1 + 3 != calos.end(); ++t1) { + for (auto t2 = t1 + 1; t2 + 2 != calos.end(); ++t2) { + for (auto t3 = t2 + 1; t3 + 1 != calos.end(); ++t3) { + for (auto t4 = t3 + 1; t4 != calos.end(); ++t4) { auto comb = std::make_tuple(t0, t1, t2, t3, t4); count++; benchmark::DoNotOptimize(comb); @@ -300,21 +300,20 @@ static void BM_ASoAHelpersCombGenTracksPairs(benchmark::State& state) std::uniform_real_distribution<float> uniform_dist(0, 1); TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); + auto rowWriter = builder.cursor<o2::aod::Calos>(); for (auto i = 0; i < state.range(0); ++i) { rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); } auto table = builder.finalize(); - o2::aod::Tracks tracks{table}; + o2::aod::Calos calos{table}; int64_t count = 0; for (auto _ : state) { count = 0; - for (auto& comb : combinations(tracks, tracks)) { + for (auto& comb : combinations(calos, calos)) { count++; } benchmark::DoNotOptimize(count); @@ -332,21 +331,20 @@ static void BM_ASoAHelpersCombGenTracksFives(benchmark::State& state) std::uniform_real_distribution<float> uniform_dist(0, 1); TableBuilder builder; - auto rowWriter = builder.cursor<o2::aod::StoredTracks>(); + auto rowWriter = builder.cursor<o2::aod::Calos>(); for (auto i = 0; i < state.range(0); ++i) { rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); } auto table = builder.finalize(); - o2::aod::Tracks tracks{table}; + o2::aod::Calos calos{table}; int64_t count = 0; for (auto _ : state) { count = 0; - for (auto& comb : combinations(tracks, tracks, tracks, tracks, tracks)) { + for (auto& comb : combinations(calos, calos, calos, calos, calos)) { count++; } benchmark::DoNotOptimize(count); @@ -405,32 +403,30 @@ static void BM_ASoAHelpersCombGenTracksFivesMultipleChunks(benchmark::State& sta std::uniform_real_distribution<float> uniform_dist(0, 1); TableBuilder builderA; - auto rowWriterA = builderA.cursor<o2::aod::StoredTracks>(); + auto rowWriterA = builderA.cursor<o2::aod::Calos>(); for (auto i = 0; i < state.range(0); ++i) { rowWriterA(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); } auto tableA = builderA.finalize(); TableBuilder builderB; - auto rowWriterB = builderB.cursor<o2::aod::StoredTracks>(); + auto rowWriterB = builderB.cursor<o2::aod::Calos>(); for (auto i = 0; i < state.range(0); ++i) { rowWriterB(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), - uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1), uniform_dist(e1)); } auto tableB = builderB.finalize(); - using ConcatTest = Concat<o2::aod::Tracks, o2::aod::Tracks>; + using ConcatTest = Concat<o2::aod::Calos, o2::aod::Calos>; - ConcatTest tracks{tableA, tableB}; + ConcatTest calos{tableA, tableB}; int64_t count = 0; for (auto _ : state) { count = 0; - for (auto& comb : combinations(tracks, tracks, tracks, tracks, tracks)) { + for (auto& comb : combinations(calos, calos, calos, calos, calos)) { count++; } benchmark::DoNotOptimize(count); diff --git a/Framework/Core/test/benchmark_ContextRegistry.cxx b/Framework/Core/test/benchmark_ContextRegistry.cxx deleted file mode 100644 index bdbfbf0185ead..0000000000000 --- a/Framework/Core/test/benchmark_ContextRegistry.cxx +++ /dev/null @@ -1,58 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <benchmark/benchmark.h> - -#include "Framework/ContextRegistry.h" -#include "Framework/ArrowContext.h" -#include "Framework/StringContext.h" -#include "Framework/RawBufferContext.h" -#include "Framework/MessageContext.h" -#include <TObject.h> - -using namespace o2::framework; - -// A simple test where an input is provided -// and the subsequent InputRecord is immediately requested. -static void BM_ContextRegistrySingleGet(benchmark::State& state) -{ - FairMQDeviceProxy proxy(nullptr); - ArrowContext c0(proxy); - StringContext c1(proxy); - RawBufferContext c2(proxy); - MessageContext c3(proxy); - ContextRegistry registry({&c0, &c1, &c2, &c3}); - - for (auto _ : state) { - registry.get<MessageContext>(); - } -} - -BENCHMARK(BM_ContextRegistrySingleGet); - -// A simple test where an input is provided -// and the subsequent InputRecord is immediately requested. -static void BM_ContextRegistryMultiGet(benchmark::State& state) -{ - FairMQDeviceProxy proxy(nullptr); - ArrowContext c0(proxy); - StringContext c1(proxy); - RawBufferContext c2(proxy); - MessageContext c3(proxy); - ContextRegistry registry({&c0, &c1, &c2, &c3}); - - for (auto _ : state) { - registry.get<MessageContext>(); - registry.get<RawBufferContext>(); - } -} - -BENCHMARK(BM_ContextRegistryMultiGet); - -BENCHMARK_MAIN(); diff --git a/Framework/Core/test/benchmark_HistogramRegistry.cxx b/Framework/Core/test/benchmark_HistogramRegistry.cxx index 18c09d1a31f34..ff870f53e913f 100644 --- a/Framework/Core/test/benchmark_HistogramRegistry.cxx +++ b/Framework/Core/test/benchmark_HistogramRegistry.cxx @@ -28,15 +28,15 @@ static void BM_HashedNameLookup(benchmark::State& state) { for (auto _ : state) { state.PauseTiming(); - std::vector<HistogramSpec> specs; + std::vector<HistogramSpec> histSpecs; for (auto i = 0; i < state.range(0); ++i) { - specs.push_back({(boost::format("histo%1%") % (i + 1)).str().c_str(), (boost::format("Histo %1%") % (i + 1)).str().c_str(), {"TH1F", 100, 0, 1}}); + histSpecs.push_back({fmt::format("histo{}", i + 1).c_str(), fmt::format("Histo {}", i + 1).c_str(), {HistType::kTH1F, {{100, 0, 1}}}}); } - HistogramRegistry registry{"registry", true, specs}; + HistogramRegistry registry{"registry", histSpecs}; state.ResumeTiming(); for (auto i = 0; i < nLookups; ++i) { - auto& x = registry.get("histo4"); + auto& x = registry.get<TH1>(HIST("histo4")); benchmark::DoNotOptimize(x); } state.counters["Average lookup distance"] = ((double)registry.lookup / (double)(state.range(0))); diff --git a/Framework/Core/test/benchmark_TreeToTable.cxx b/Framework/Core/test/benchmark_TreeToTable.cxx index dd2275c93ac51..381b8c09bad4a 100644 --- a/Framework/Core/test/benchmark_TreeToTable.cxx +++ b/Framework/Core/test/benchmark_TreeToTable.cxx @@ -10,6 +10,7 @@ #include "Framework/CommonDataProcessors.h" #include "Framework/TableTreeHelpers.h" +#include "Framework/Logger.h" #include <benchmark/benchmark.h> #include <random> #include <vector> @@ -71,9 +72,10 @@ static void BM_TreeToTable(benchmark::State& state) // benchmark TreeToTable if (tr) { - tr2ta = new TreeToTable(tr); - if (tr2ta->addAllColumns()) { - auto ta = tr2ta->process(); + tr2ta = new TreeToTable; + if (tr2ta->addAllColumns(tr)) { + tr2ta->fill(tr); + auto ta = tr2ta->finalize(); } } else { @@ -81,11 +83,10 @@ static void BM_TreeToTable(benchmark::State& state) } // clean up - if (tr2ta) - delete tr2ta; + delete tr2ta; + f->Close(); - if (f) - delete f; + delete f; } state.SetBytesProcessed(state.iterations() * state.range(0) * 24); diff --git a/Framework/Core/test/benchmark_WorkflowHelpers.cxx b/Framework/Core/test/benchmark_WorkflowHelpers.cxx index 2eb8f15dea25f..5dd95a5398faa 100644 --- a/Framework/Core/test/benchmark_WorkflowHelpers.cxx +++ b/Framework/Core/test/benchmark_WorkflowHelpers.cxx @@ -56,7 +56,9 @@ static void BM_CreateGraphOverhead(benchmark::State& state) std::vector<OutputSpec> outputs; std::vector<LogicalForwardInfo> availableForwardsInfo; - WorkflowHelpers::verifyWorkflow(workflow); + if (WorkflowHelpers::verifyWorkflow(workflow) != WorkflowParsingState::Valid) { + throw std::runtime_error("Invalid workflow"); + }; auto context = makeEmptyConfigContext(); WorkflowHelpers::injectServiceDevices(workflow, *context); WorkflowHelpers::constructGraph(workflow, @@ -92,7 +94,9 @@ static void BM_CreateGraphReverseOverhead(benchmark::State& state) std::vector<OutputSpec> outputs; std::vector<LogicalForwardInfo> availableForwardsInfo; - WorkflowHelpers::verifyWorkflow(workflow); + if (WorkflowHelpers::verifyWorkflow(workflow) != WorkflowParsingState::Valid) { + throw std::runtime_error("Invalid workflow"); + }; auto context = makeEmptyConfigContext(); WorkflowHelpers::injectServiceDevices(workflow, *context); WorkflowHelpers::constructGraph(workflow, logicalEdges, diff --git a/Framework/Core/test/test_ASoA.cxx b/Framework/Core/test/test_ASoA.cxx index cc57e5f47cf52..74dc0e7706262 100644 --- a/Framework/Core/test/test_ASoA.cxx +++ b/Framework/Core/test/test_ASoA.cxx @@ -14,6 +14,9 @@ #include "Framework/ASoA.h" #include "Framework/ASoAHelpers.h" +#include "Framework/Expressions.h" +#include "Framework/AnalysisHelpers.h" +#include "../src/ExpressionHelpers.h" #include "gandiva/tree_expr_builder.h" #include "arrow/status.h" #include "gandiva/filter.h" @@ -30,6 +33,7 @@ DECLARE_SOA_COLUMN_FULL(X, x, int32_t, "x"); DECLARE_SOA_COLUMN_FULL(Y, y, int32_t, "y"); DECLARE_SOA_COLUMN_FULL(Z, z, int32_t, "z"); DECLARE_SOA_DYNAMIC_COLUMN(Sum, sum, [](int32_t x, int32_t y) { return x + y; }); +DECLARE_SOA_EXPRESSION_COLUMN(ESum, esum, int32_t, 1 * test::x + test::y); } // namespace test DECLARE_SOA_TABLE(Points, "TST", "POINTS", test::X, test::Y); @@ -496,3 +500,149 @@ BOOST_AUTO_TEST_CASE(TestSchemaCreation) BOOST_CHECK_EQUAL(schema->field(0)->name(), "x"); BOOST_CHECK_EQUAL(schema->field(1)->name(), "y"); } + +BOOST_AUTO_TEST_CASE(TestFilteredOperators) +{ + TableBuilder builderA; + auto rowWriterA = builderA.persist<int32_t, int32_t>({"x", "y"}); + rowWriterA(0, 0, 8); + rowWriterA(0, 1, 9); + rowWriterA(0, 2, 10); + rowWriterA(0, 3, 11); + rowWriterA(0, 4, 12); + rowWriterA(0, 5, 13); + rowWriterA(0, 6, 14); + rowWriterA(0, 7, 15); + auto tableA = builderA.finalize(); + BOOST_REQUIRE_EQUAL(tableA->num_rows(), 8); + + using TestA = o2::soa::Table<o2::soa::Index<>, test::X, test::Y>; + using FilteredTest = Filtered<TestA>; + using NestedFilteredTest = Filtered<Filtered<TestA>>; + using namespace o2::framework; + + expressions::Filter f1 = test::x < 4; + expressions::Filter f2 = test::y > 13; + + TestA testA{tableA}; + FilteredTest filtered1{{testA.asArrowTable()}, expressions::createSelection(testA.asArrowTable(), f1)}; + BOOST_CHECK_EQUAL(4, filtered1.size()); + BOOST_CHECK(filtered1.begin() != filtered1.end()); + + FilteredTest filtered2{{testA.asArrowTable()}, expressions::createSelection(testA.asArrowTable(), f2)}; + BOOST_CHECK_EQUAL(2, filtered2.size()); + BOOST_CHECK(filtered2.begin() != filtered2.end()); + + FilteredTest filteredUnion = filtered1 + filtered2; + BOOST_CHECK_EQUAL(6, filteredUnion.size()); + + std::vector<std::tuple<int32_t, int32_t>> expectedUnion{ + {0, 8}, {1, 9}, {2, 10}, {3, 11}, {6, 14}, {7, 15}}; + auto i = 0; + for (auto& f : filteredUnion) { + BOOST_CHECK_EQUAL(std::get<0>(expectedUnion[i]), f.x()); + BOOST_CHECK_EQUAL(std::get<1>(expectedUnion[i]), f.y()); + BOOST_CHECK_EQUAL(std::get<0>(expectedUnion[i]), f.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 6); + + FilteredTest filteredIntersection = filtered1 * filtered2; + BOOST_CHECK_EQUAL(0, filteredIntersection.size()); + + i = 0; + for (auto& f : filteredIntersection) { + i++; + } + BOOST_CHECK_EQUAL(i, 0); + + expressions::Filter f3 = test::x < 3; + FilteredTest filtered3{{testA.asArrowTable()}, expressions::createSelection(testA.asArrowTable(), f3)}; + BOOST_CHECK_EQUAL(3, filtered3.size()); + BOOST_CHECK(filtered3.begin() != filtered3.end()); + + FilteredTest unionIntersection = (filtered1 + filtered2) * filtered3; + BOOST_CHECK_EQUAL(3, unionIntersection.size()); + + i = 0; + for (auto& f : unionIntersection) { + BOOST_CHECK_EQUAL(i, f.x()); + BOOST_CHECK_EQUAL(i + 8, f.y()); + BOOST_CHECK_EQUAL(i, f.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 3); +} + +BOOST_AUTO_TEST_CASE(TestNestedFiltering) +{ + TableBuilder builderA; + auto rowWriterA = builderA.persist<int32_t, int32_t>({"x", "y"}); + rowWriterA(0, 0, 8); + rowWriterA(0, 1, 9); + rowWriterA(0, 2, 10); + rowWriterA(0, 3, 11); + rowWriterA(0, 4, 12); + rowWriterA(0, 5, 13); + rowWriterA(0, 6, 14); + rowWriterA(0, 7, 15); + auto tableA = builderA.finalize(); + BOOST_REQUIRE_EQUAL(tableA->num_rows(), 8); + + using TestA = o2::soa::Table<o2::soa::Index<>, test::X, test::Y>; + using FilteredTest = Filtered<TestA>; + using NestedFilteredTest = Filtered<Filtered<TestA>>; + using TripleNestedFilteredTest = Filtered<Filtered<Filtered<TestA>>>; + using namespace o2::framework; + + expressions::Filter f1 = test::x < 4; + expressions::Filter f2 = test::y > 9; + expressions::Filter f3 = test::x < 3; + + TestA testA{tableA}; + FilteredTest filtered{{testA.asArrowTable()}, expressions::createSelection(testA.asArrowTable(), f1)}; + BOOST_CHECK_EQUAL(4, filtered.size()); + BOOST_CHECK(filtered.begin() != filtered.end()); + + NestedFilteredTest nestedFiltered{{filtered}, expressions::createSelection(filtered.asArrowTable(), f2)}; + BOOST_CHECK_EQUAL(2, nestedFiltered.size()); + auto i = 0; + for (auto& f : nestedFiltered) { + BOOST_CHECK_EQUAL(i + 2, f.x()); + BOOST_CHECK_EQUAL(i + 10, f.y()); + BOOST_CHECK_EQUAL(i + 2, f.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 2); + + TripleNestedFilteredTest tripleFiltered{{nestedFiltered}, expressions::createSelection(nestedFiltered.asArrowTable(), f3)}; + BOOST_CHECK_EQUAL(1, tripleFiltered.size()); + i = 0; + for (auto& f : tripleFiltered) { + BOOST_CHECK_EQUAL(i + 2, f.x()); + BOOST_CHECK_EQUAL(i + 10, f.y()); + BOOST_CHECK_EQUAL(i + 2, f.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 1); +} + +BOOST_AUTO_TEST_CASE(TestEmptyTables) +{ + TableBuilder bPoints; + auto pwriter = bPoints.cursor<Points>(); + auto pempty = bPoints.finalize(); + + TableBuilder bInfos; + auto iwriter = bInfos.cursor<Infos>(); + auto iempty = bInfos.finalize(); + + Points p{pempty}; + Infos i{iempty}; + + using PI = Join<Points, Infos>; + PI pi{0, pempty, iempty}; + BOOST_CHECK_EQUAL(pi.size(), 0); + auto spawned = Extend<Points, test::ESum>(p); + BOOST_CHECK_EQUAL(spawned.size(), 0); +} diff --git a/Framework/Core/test/test_ASoAHelpers.cxx b/Framework/Core/test/test_ASoAHelpers.cxx index 907f8dab09939..9fb4e598ebe63 100644 --- a/Framework/Core/test/test_ASoAHelpers.cxx +++ b/Framework/Core/test/test_ASoAHelpers.cxx @@ -1228,3 +1228,122 @@ BOOST_AUTO_TEST_CASE(BlockCombinations) } BOOST_CHECK_EQUAL(count, 0); } + +BOOST_AUTO_TEST_CASE(CombinationsHelpers) +{ + TableBuilder builderA; + auto rowWriterA = builderA.persist<int32_t, int32_t>({"x", "y"}); + rowWriterA(0, 0, 0); + rowWriterA(0, 1, 0); + rowWriterA(0, 2, 0); + rowWriterA(0, 3, 0); + rowWriterA(0, 4, 0); + rowWriterA(0, 5, 0); + rowWriterA(0, 6, 0); + rowWriterA(0, 7, 0); + auto tableA = builderA.finalize(); + BOOST_REQUIRE_EQUAL(tableA->num_rows(), 8); + + using TestA = o2::soa::Table<o2::soa::Index<>, test::X, test::Y>; + + TestA testsA{tableA}; + + BOOST_REQUIRE_EQUAL(8, testsA.size()); + int nA = testsA.size(); + + int count = 0; + int i = 0; + int j = 1; + for (auto& [t0, t1] : pairCombinations(testsA)) { + BOOST_CHECK_EQUAL(t0.x(), i); + BOOST_CHECK_EQUAL(t1.x(), j); + count++; + j++; + if (j == nA) { + i++; + j = i + 1; + } + } + BOOST_CHECK_EQUAL(count, 28); + + count = 0; + i = 0; + j = 1; + int k = 2; + for (auto& [t0, t1, t2] : tripleCombinations(testsA)) { + BOOST_CHECK_EQUAL(t0.x(), i); + BOOST_CHECK_EQUAL(t1.x(), j); + BOOST_CHECK_EQUAL(t2.x(), k); + count++; + k++; + if (k == nA) { + if (j == nA - 2) { + i++; + j = i; + } + j++; + k = j + 1; + } + } + BOOST_CHECK_EQUAL(count, 56); + + TableBuilder builderB; + auto rowWriterB = builderB.persist<int32_t, int32_t, float>({"x", "y", "floatZ"}); + rowWriterB(0, 0, 25, -6.0f); + rowWriterB(0, 1, 18, 0.0f); + rowWriterB(0, 2, 48, 8.0f); + rowWriterB(0, 3, 103, 2.0f); + rowWriterB(0, 4, 28, -6.0f); + rowWriterB(0, 5, 102, 2.0f); + rowWriterB(0, 6, 12, 0.0f); + rowWriterB(0, 7, 24, -7.0f); + rowWriterB(0, 8, 41, 8.0f); + rowWriterB(0, 9, 49, 8.0f); + auto tableB = builderB.finalize(); + BOOST_REQUIRE_EQUAL(tableB->num_rows(), 10); + + using TestB = o2::soa::Table<o2::soa::Index<>, test::X, test::Y, test::FloatZ>; + TestB testB{tableB}; + BOOST_REQUIRE_EQUAL(10, testB.size()); + + // Grouped data: + // [3, 5] [0, 4, 7], [1, 6], [2, 8, 9] + // Assuming bins intervals: [ , ) + std::vector<uint32_t> yBins{0, 5, 10, 20, 30, 40, 50, 101}; + std::vector<float> zBins{-7.0f, -5.0f, -3.0f, -1.0f, 1.0f, 3.0f, 5.0f, 7.0f}; + + TableBuilder builderAux; + auto rowWriterAux = builderAux.persist<int32_t, int32_t>({"x", "y"}); + for (auto it = testB.begin(); it != testB.end(); it++) { + auto& elem = *it; + rowWriterAux(0, elem.x(), getHash(yBins, zBins, elem.y(), elem.floatZ())); + } + auto tableAux = builderAux.finalize(); + BOOST_REQUIRE_EQUAL(tableAux->num_rows(), 10); + + // Auxiliary table: testsAux with id and hash, hash is the category for grouping + using TestsAux = o2::soa::Table<o2::soa::Index<>, test::X, test::Y>; + TestsAux testAux{tableAux}; + BOOST_REQUIRE_EQUAL(10, testAux.size()); + + std::vector<std::tuple<int32_t, int32_t>> expectedStrictlyUpperPairs{ + {0, 4}, {0, 7}, {4, 7}, {1, 6}, {3, 5}, {2, 8}, {2, 9}, {8, 9}}; + count = 0; + for (auto& [c0, c1] : selfPairCombinations("y", 2, -1, testAux)) { + BOOST_CHECK_EQUAL(c0.x(), std::get<0>(expectedStrictlyUpperPairs[count])); + BOOST_CHECK_EQUAL(c1.x(), std::get<1>(expectedStrictlyUpperPairs[count])); + count++; + } + BOOST_CHECK_EQUAL(count, expectedStrictlyUpperPairs.size()); + + std::vector<std::tuple<int32_t, int32_t, int32_t>> expectedStrictlyUpperTriples{ + {0, 4, 7}, {2, 8, 9}}; + count = 0; + for (auto& [c0, c1, c2] : selfTripleCombinations("y", 2, -1, testAux)) { + BOOST_CHECK_EQUAL(c0.x(), std::get<0>(expectedStrictlyUpperTriples[count])); + BOOST_CHECK_EQUAL(c1.x(), std::get<1>(expectedStrictlyUpperTriples[count])); + BOOST_CHECK_EQUAL(c2.x(), std::get<2>(expectedStrictlyUpperTriples[count])); + count++; + } + BOOST_CHECK_EQUAL(count, expectedStrictlyUpperTriples.size()); +} diff --git a/Framework/Core/test/test_AnalysisDataModel.cxx b/Framework/Core/test/test_AnalysisDataModel.cxx index f458bdadcc524..77b4264b289db 100644 --- a/Framework/Core/test/test_AnalysisDataModel.cxx +++ b/Framework/Core/test/test_AnalysisDataModel.cxx @@ -22,28 +22,41 @@ using namespace o2::framework; using namespace arrow; -using namespace o2::soa; -using namespace o2::aod; + +DECLARE_SOA_STORE(); + +namespace col +{ +DECLARE_SOA_COLUMN(X, x, float); +DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(Z, z, float); +DECLARE_SOA_COLUMN(D, d, float); +} // namespace col + +DECLARE_SOA_TABLE(XY, "AOD", "XY", col::X, col::Y); +DECLARE_SOA_TABLE(ZD, "AOD", "ZD", col::Z, col::D); BOOST_AUTO_TEST_CASE(TestJoinedTables) { - TableBuilder trackBuilder; - auto trackWriter = trackBuilder.cursor<StoredTracks>(); - trackWriter(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - auto tracks = trackBuilder.finalize(); + TableBuilder XYBuilder; + //FIXME: using full tracks, instead of stored because of unbound dynamic + // column (normalized phi) + auto xyWriter = XYBuilder.cursor<XY>(); + xyWriter(0, 0, 0); + auto tXY = XYBuilder.finalize(); - TableBuilder trackParCovBuilder; - auto trackParCovWriter = trackParCovBuilder.cursor<StoredTracksCov>(); - trackParCovWriter(0, 7, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4); - auto covs = trackParCovBuilder.finalize(); + TableBuilder ZDBuilder; + auto zdWriter = ZDBuilder.cursor<ZD>(); + zdWriter(0, 7, 1); + auto tZD = ZDBuilder.finalize(); - using Test = Join<StoredTracks, StoredTracksCov>; + using Test = o2::soa::Join<XY, ZD>; - Test tests{0, tracks, covs}; + Test tests{0, tXY, tZD}; BOOST_REQUIRE(tests.asArrowTable()->num_columns() != 0); BOOST_REQUIRE_EQUAL(tests.asArrowTable()->num_columns(), - tracks->num_columns() + covs->num_columns()); - auto tests2 = join(StoredTracks{tracks}, StoredTracksCov{covs}); + tXY->num_columns() + tZD->num_columns()); + auto tests2 = join(XY{tXY}, ZD{tZD}); static_assert(std::is_same_v<Test::table_t, decltype(tests2)>, "Joined tables should have the same type, regardless how we construct them"); } diff --git a/Framework/Core/test/test_AnalysisTask.cxx b/Framework/Core/test/test_AnalysisTask.cxx index 0de0a33381cab..fb4a0e70cd13b 100644 --- a/Framework/Core/test/test_AnalysisTask.cxx +++ b/Framework/Core/test/test_AnalysisTask.cxx @@ -50,7 +50,7 @@ DECLARE_SOA_TABLE(Events, "AOD", "EVENTS", struct ATask { Produces<aod::FooBars> foobars; - void process(o2::aod::Track const& track) + void process(o2::aod::Track const&) { foobars(0.01102005, 0.27092016); // dummy value for phi for now... } @@ -146,29 +146,34 @@ struct JTask { BOOST_AUTO_TEST_CASE(AdaptorCompilation) { auto task1 = adaptAnalysisTask<ATask>("test1"); - BOOST_CHECK_EQUAL(task1.inputs.size(), 1); + BOOST_CHECK_EQUAL(task1.inputs.size(), 2); BOOST_CHECK_EQUAL(task1.outputs.size(), 1); - BOOST_CHECK_EQUAL(task1.inputs[0].binding, std::string("Tracks")); + BOOST_CHECK_EQUAL(task1.inputs[0].binding, std::string("TracksExtension")); + BOOST_CHECK_EQUAL(task1.inputs[1].binding, std::string("Tracks")); BOOST_CHECK_EQUAL(task1.outputs[0].binding.value, std::string("FooBars")); auto task2 = adaptAnalysisTask<BTask>("test2"); - BOOST_CHECK_EQUAL(task2.inputs.size(), 7); + BOOST_CHECK_EQUAL(task2.inputs.size(), 9); BOOST_CHECK_EQUAL(task2.inputs[0].binding, "Collisions"); - BOOST_CHECK_EQUAL(task2.inputs[1].binding, "Tracks"); - BOOST_CHECK_EQUAL(task2.inputs[2].binding, "TracksExtra"); - BOOST_CHECK_EQUAL(task2.inputs[3].binding, "TracksCov"); - BOOST_CHECK_EQUAL(task2.inputs[4].binding, "UnassignedTracks"); - BOOST_CHECK_EQUAL(task2.inputs[5].binding, "Calos"); - BOOST_CHECK_EQUAL(task2.inputs[6].binding, "CaloTriggers"); + BOOST_CHECK_EQUAL(task2.inputs[1].binding, "TracksExtension"); + BOOST_CHECK_EQUAL(task2.inputs[2].binding, "Tracks"); + BOOST_CHECK_EQUAL(task2.inputs[3].binding, "TracksExtra"); + BOOST_CHECK_EQUAL(task2.inputs[4].binding, "TracksCovExtension"); + BOOST_CHECK_EQUAL(task2.inputs[5].binding, "TracksCov"); + BOOST_CHECK_EQUAL(task2.inputs[6].binding, "UnassignedTracks"); + BOOST_CHECK_EQUAL(task2.inputs[7].binding, "Calos"); + BOOST_CHECK_EQUAL(task2.inputs[8].binding, "CaloTriggers"); auto task3 = adaptAnalysisTask<CTask>("test3"); - BOOST_CHECK_EQUAL(task3.inputs.size(), 2); + BOOST_CHECK_EQUAL(task3.inputs.size(), 3); BOOST_CHECK_EQUAL(task3.inputs[0].binding, "Collisions"); - BOOST_CHECK_EQUAL(task3.inputs[1].binding, "Tracks"); + BOOST_CHECK_EQUAL(task3.inputs[1].binding, "TracksExtension"); + BOOST_CHECK_EQUAL(task3.inputs[2].binding, "Tracks"); auto task4 = adaptAnalysisTask<DTask>("test4"); - BOOST_CHECK_EQUAL(task4.inputs.size(), 1); - BOOST_CHECK_EQUAL(task4.inputs[0].binding, "Tracks"); + BOOST_CHECK_EQUAL(task4.inputs.size(), 2); + BOOST_CHECK_EQUAL(task4.inputs[0].binding, "TracksExtension"); + BOOST_CHECK_EQUAL(task4.inputs[1].binding, "Tracks"); auto task5 = adaptAnalysisTask<ETask>("test5"); BOOST_CHECK_EQUAL(task5.inputs.size(), 1); @@ -189,3 +194,68 @@ BOOST_AUTO_TEST_CASE(AdaptorCompilation) auto task10 = adaptAnalysisTask<JTask>("test10"); } + +BOOST_AUTO_TEST_CASE(TestPartitionIteration) +{ + TableBuilder builderA; + auto rowWriterA = builderA.persist<float, float>({"fX", "fY"}); + rowWriterA(0, 0.0f, 8.0f); + rowWriterA(0, 1.0f, 9.0f); + rowWriterA(0, 2.0f, 10.0f); + rowWriterA(0, 3.0f, 11.0f); + rowWriterA(0, 4.0f, 12.0f); + rowWriterA(0, 5.0f, 13.0f); + rowWriterA(0, 6.0f, 14.0f); + rowWriterA(0, 7.0f, 15.0f); + auto tableA = builderA.finalize(); + BOOST_REQUIRE_EQUAL(tableA->num_rows(), 8); + + using TestA = o2::soa::Table<o2::soa::Index<>, aod::test::X, aod::test::Y>; + using FilteredTest = o2::soa::Filtered<TestA>; + using PartitionTest = Partition<TestA>; + using PartitionFilteredTest = Partition<o2::soa::Filtered<TestA>>; + using PartitionNestedFilteredTest = Partition<o2::soa::Filtered<o2::soa::Filtered<TestA>>>; + using namespace o2::framework; + + TestA testA{tableA}; + + PartitionTest p1 = aod::test::x < 4.0f; + p1.setTable(testA); + BOOST_CHECK_EQUAL(4, p1.size()); + BOOST_CHECK(p1.begin() != p1.end()); + auto i = 0; + for (auto& p : p1) { + BOOST_CHECK_EQUAL(i, p.x()); + BOOST_CHECK_EQUAL(i + 8, p.y()); + BOOST_CHECK_EQUAL(i, p.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 4); + + expressions::Filter f1 = aod::test::x < 4.0f; + FilteredTest filtered{{testA.asArrowTable()}, expressions::createSelection(testA.asArrowTable(), f1)}; + PartitionFilteredTest p2 = aod::test::y > 9.0f; + p2.setTable(filtered); + + BOOST_CHECK_EQUAL(2, p2.size()); + i = 0; + for (auto& p : p2) { + BOOST_CHECK_EQUAL(i + 2, p.x()); + BOOST_CHECK_EQUAL(i + 10, p.y()); + BOOST_CHECK_EQUAL(i + 2, p.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 2); + + PartitionNestedFilteredTest p3 = aod::test::x < 3.0f; + p3.setTable(*(p2.mFiltered)); + BOOST_CHECK_EQUAL(1, p3.size()); + i = 0; + for (auto& p : p3) { + BOOST_CHECK_EQUAL(i + 2, p.x()); + BOOST_CHECK_EQUAL(i + 10, p.y()); + BOOST_CHECK_EQUAL(i + 2, p.index()); + i++; + } + BOOST_CHECK_EQUAL(i, 1); +} diff --git a/Framework/Core/test/test_ConfigurationOptionsRetriever.cxx b/Framework/Core/test/test_ConfigurationOptionsRetriever.cxx index d7eb05a2aef06..bba681ef59ea6 100644 --- a/Framework/Core/test/test_ConfigurationOptionsRetriever.cxx +++ b/Framework/Core/test/test_ConfigurationOptionsRetriever.cxx @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(TestOptionsRetriever) }; std::vector<std::unique_ptr<ParamRetriever>> retrievers; - std::unique_ptr<ParamRetriever> fairmqRetriver{new ConfigurationOptionsRetriever(conf.get(), ".")}; + std::unique_ptr<ParamRetriever> fairmqRetriver{new ConfigurationOptionsRetriever(conf.get(), "")}; retrievers.emplace_back(std::move(fairmqRetriver)); ConfigParamStore store{specs, std::move(retrievers)}; diff --git a/Framework/Core/test/test_ContextRegistry.cxx b/Framework/Core/test/test_ContextRegistry.cxx deleted file mode 100644 index fa521f08adb46..0000000000000 --- a/Framework/Core/test/test_ContextRegistry.cxx +++ /dev/null @@ -1,38 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Framework ContextRegistry -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include <boost/test/unit_test.hpp> -#include "Framework/ContextRegistry.h" -#include "Framework/ArrowContext.h" -#include "Framework/StringContext.h" -#include "Framework/RawBufferContext.h" -#include "Framework/MessageContext.h" -#include <TObject.h> - -using namespace o2::framework; - -BOOST_AUTO_TEST_CASE(TestContextRegistry) -{ - FairMQDeviceProxy proxy(nullptr); - ArrowContext c0(proxy); - StringContext c1(proxy); - RawBufferContext c2(proxy); - MessageContext c3(proxy); - ContextRegistry registry({&c0, &c1, &c2, &c3}); - - BOOST_REQUIRE_EQUAL(&c0, registry.get<ArrowContext>()); - BOOST_REQUIRE_EQUAL(&c1, registry.get<StringContext>()); - BOOST_REQUIRE_EQUAL(&c2, registry.get<RawBufferContext>()); - BOOST_REQUIRE_EQUAL(&c3, registry.get<MessageContext>()); -} diff --git a/Framework/Core/test/test_DataAllocator.cxx b/Framework/Core/test/test_DataAllocator.cxx index fde705c275dac..448ab70bd4626 100644 --- a/Framework/Core/test/test_DataAllocator.cxx +++ b/Framework/Core/test/test_DataAllocator.cxx @@ -42,7 +42,7 @@ using namespace o2::framework; void doTypeChecks() { TimingInfo* timingInfo = nullptr; - ContextRegistry* contextes = nullptr; + ServiceRegistry* contextes = nullptr; std::vector<OutputRoute> routes; DataAllocator allocator(timingInfo, contextes, routes); const Output output{"TST", "DUMMY", 0, Lifetime::Timeframe}; diff --git a/Framework/Core/test/test_DataDescriptorMatcher.cxx b/Framework/Core/test/test_DataDescriptorMatcher.cxx index c12e0c7f0cb69..6501a31a7e705 100644 --- a/Framework/Core/test/test_DataDescriptorMatcher.cxx +++ b/Framework/Core/test/test_DataDescriptorMatcher.cxx @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(TestMatcherInvariants) DataDescriptorMatcher matcherB{ DataDescriptorMatcher::Op::Just, OriginValueMatcher{"TPC"}}; - BOOST_CHECK(matcherA == matcherB); + BOOST_CHECK_EQUAL(matcherA, matcherB); } { @@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(TestMatcherInvariants) DataDescriptorMatcher matcherB{ DataDescriptorMatcher::Op::Just, DescriptionValueMatcher{"TRACKS"}}; - BOOST_CHECK(matcherA == matcherB); + BOOST_CHECK_EQUAL(matcherA, matcherB); } { @@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(TestMatcherInvariants) DataDescriptorMatcher matcherB{ DataDescriptorMatcher::Op::Just, SubSpecificationTypeValueMatcher{1}}; - BOOST_CHECK(matcherA == matcherB); + BOOST_CHECK_EQUAL(matcherA, matcherB); } { @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(TestMatcherInvariants) DataDescriptorMatcher matcherB{ DataDescriptorMatcher::Op::Just, ConstantValueMatcher{1}}; - BOOST_CHECK(matcherA == matcherB); + BOOST_CHECK_EQUAL(matcherA, matcherB); } { @@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(TestMatcherInvariants) DataDescriptorMatcher::Op::Just, ConstantValueMatcher{1}, DescriptionValueMatcher{"TPC"}}; - BOOST_CHECK(matcherA == matcherB); + BOOST_CHECK_NE(matcherA, matcherB); } { @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(TestMatcherInvariants) DataDescriptorMatcher::Op::And, SubSpecificationTypeValueMatcher{1}, ConstantValueMatcher{true}))}; - BOOST_CHECK(matcherA == matcherB); + BOOST_CHECK_EQUAL(matcherA, matcherB); } } @@ -572,7 +572,7 @@ BOOST_AUTO_TEST_CASE(DataQuery) StartTimeValueMatcher{ContextRef{0}})))}; auto matcher = std::get_if<DataDescriptorMatcher>(&result0[0].matcher); BOOST_REQUIRE(matcher != nullptr); - BOOST_CHECK(expectedMatcher00 == *matcher); + BOOST_CHECK_EQUAL(expectedMatcher00, *matcher); std::ostringstream ss0; ss0 << *matcher; std::ostringstream expectedSS00; diff --git a/Framework/Core/test/test_DataInputDirector.cxx b/Framework/Core/test/test_DataInputDirector.cxx index 5b01b583bcfc9..f8456d7935380 100644 --- a/Framework/Core/test/test_DataInputDirector.cxx +++ b/Framework/Core/test/test_DataInputDirector.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include <fstream> #include <boost/test/unit_test.hpp> #include "Headers/DataHeader.h" @@ -48,18 +49,61 @@ BOOST_AUTO_TEST_CASE(TestDatainputDirector) jf << R"(})" << std::endl; jf.close(); - DataInputDirector didir; - BOOST_CHECK(didir.readJson(jsonFile)); - //didir.printOut(); printf("\n\n"); + DataInputDirector didir1; + BOOST_CHECK(didir1.readJson(jsonFile)); + didir1.printOut(); + printf("\n\n"); - BOOST_CHECK_EQUAL(didir.getNumberInputDescriptors(), 2); + BOOST_CHECK_EQUAL(didir1.getNumberInputDescriptors(), 2); auto dh = DataHeader(DataDescription{"DUE"}, DataOrigin{"AOD"}, DataHeader::SubSpecificationType{0}); - BOOST_CHECK_EQUAL(didir.getInputFilename(dh, 1), "Bresults_1.root"); + //auto [file1, directory1] = didir1.getFileFolder(dh, 1, 0); + //BOOST_CHECK_EQUAL(file1->GetName(), "Bresults_1.root"); - auto didesc = didir.getDataInputDescriptor(dh); + auto didesc = didir1.getDataInputDescriptor(dh); BOOST_CHECK(didesc); BOOST_CHECK_EQUAL(didesc->getNumberInputfiles(), 2); + + // test initialization with "std::vector<std::string> inputFiles" + // in this case "resfile" of the InputDataDirector in the json file must be + // empty, otherwise files specified in the json file will be added to the + // list of input files + jf.open("testO2config.json", std::ofstream::out); + jf << R"({)" << std::endl; + jf << R"( "InputDirector": {)" << std::endl; + jf << R"delimiter( "fileregex": "(Ares)(.*)",)delimiter" << std::endl; + jf << R"( "InputDescriptors": [)" << std::endl; + jf << R"( {)" << std::endl; + jf << R"( "table": "AOD/UNO/0",)" << std::endl; + jf << R"( "treename": "uno")" << std::endl; + jf << R"( },)" << std::endl; + jf << R"( {)" << std::endl; + jf << R"( "table": "AOD/DUE/0",)" << std::endl; + jf << R"( "treename": "due",)" << std::endl; + jf << R"delimiter( "fileregex": "(Bres)(.*)")delimiter" << std::endl; + jf << R"( })" << std::endl; + jf << R"( ])" << std::endl; + jf << R"( })" << std::endl; + jf << R"(})" << std::endl; + jf.close(); + + std::vector<std::string> inputFiles = {"Aresults_0.root", + "Aresults_1.root", + "Bresults_0.root", + "Aresults_2.root", + "Bresults_1.root", + "Bresults_2.root"}; + DataInputDirector didir2(inputFiles); + didir2.printOut(); + printf("\n\n"); + BOOST_CHECK(didir2.readJson(jsonFile)); + + //auto [file2, directory2] = didir2.getFileFolder(dh, 1, 0); + //BOOST_CHECK_EQUAL(file2->GetName(), "Bresults_1.root"); + + didesc = didir2.getDataInputDescriptor(dh); + BOOST_CHECK(didesc); + BOOST_CHECK_EQUAL(didesc->getNumberInputfiles(), 3); } diff --git a/Framework/Core/test/test_DataOutputDirector.cxx b/Framework/Core/test/test_DataOutputDirector.cxx index 7fde2c6fc7977..f9f2becd04044 100644 --- a/Framework/Core/test/test_DataOutputDirector.cxx +++ b/Framework/Core/test/test_DataOutputDirector.cxx @@ -12,9 +12,9 @@ #define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> - #include "Headers/DataHeader.h" #include "Framework/DataOutputDirector.h" +#include <fstream> BOOST_AUTO_TEST_CASE(TestDataOutputDirector) { @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE(TestDataOutputDirector) BOOST_CHECK_EQUAL(ds[0]->getFilenameBase(), std::string("fn1")); BOOST_CHECK_EQUAL(ds[1]->tablename, std::string("UNO")); - BOOST_CHECK_EQUAL(ds[1]->treename, std::string("UNO")); + BOOST_CHECK_EQUAL(ds[1]->treename, std::string("O2uno")); BOOST_CHECK_EQUAL(ds[1]->colnames.size(), 1); BOOST_CHECK_EQUAL(ds[1]->getFilenameBase(), std::string("myresultfile")); diff --git a/Framework/Core/test/test_DataSampling.cxx b/Framework/Core/test/test_DataSampling.cxx deleted file mode 100644 index 9f7ecddbc36e0..0000000000000 --- a/Framework/Core/test/test_DataSampling.cxx +++ /dev/null @@ -1,217 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Framework DataSampling -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include <boost/test/unit_test.hpp> - -#include "Framework/DataSampling.h" -#include "Framework/DataProcessingHeader.h" -#include "Framework/ExternalFairMQDeviceProxy.h" -#include "Framework/DataSamplingReadoutAdapter.h" -#include "Framework/DataSpecUtils.h" - -#include "Headers/DataHeader.h" - -#include <Configuration/ConfigurationFactory.h> - -using namespace o2::framework; -using namespace o2::configuration; - -using DataHeader = o2::header::DataHeader; -using DataOrigin = o2::header::DataOrigin; -using DataDescription = o2::header::DataDescription; - -BOOST_AUTO_TEST_CASE(DataSamplingSimpleFlow) -{ - WorkflowSpec workflow{ - {"producer", - Inputs{}, - Outputs{{"TPC", "CLUSTERS"}}}, - {"processingStage", - Inputs{{"dataTPC", "TPC", "CLUSTERS"}}, - Outputs{{"TPC", "CLUSTERS_P"}}}}; - - std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; - std::cout << "config file : " - << "json:/" << configFilePath << std::endl; - DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath); - - auto disp = std::find_if(workflow.begin(), workflow.end(), - [](const DataProcessorSpec& d) { - return d.name.find("Dispatcher") != std::string::npos; - }); - BOOST_REQUIRE(disp != workflow.end()); - - auto input = std::find_if(disp->inputs.begin(), disp->inputs.end(), - [](const InputSpec& in) { - return DataSpecUtils::match(in, DataOrigin("TPC"), DataDescription("CLUSTERS"), 0) && in.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(input != disp->inputs.end()); - - input = std::find_if(disp->inputs.begin(), disp->inputs.end(), - [](const InputSpec& in) { - return DataSpecUtils::match(in, DataOrigin("TPC"), DataDescription("CLUSTERS_P"), 0) && in.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(input != disp->inputs.end()); - - auto output = std::find_if(disp->outputs.begin(), disp->outputs.end(), - [](const OutputSpec& out) { - return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters-0", 0}) && out.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(output != disp->outputs.end()); - - output = std::find_if(disp->outputs.begin(), disp->outputs.end(), - [](const OutputSpec& out) { - return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters-1", 0}) && out.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(output != disp->outputs.end()); - - BOOST_CHECK(disp->algorithm.onInit != nullptr); -} - -BOOST_AUTO_TEST_CASE(DataSamplingParallelFlow) -{ - WorkflowSpec workflow{ - {"producer", - Inputs{}, - {OutputSpec{"TPC", "CLUSTERS", 0}, - OutputSpec{"TPC", "CLUSTERS", 1}, - OutputSpec{"TPC", "CLUSTERS", 2}}}}; - - auto processingStages = parallel( - DataProcessorSpec{ - "processingStage", - Inputs{{"dataTPC", "TPC", "CLUSTERS"}}, - Outputs{{"TPC", "CLUSTERS_P"}}}, - 3, - [](DataProcessorSpec& spec, size_t index) { - DataSpecUtils::updateMatchingSubspec(spec.inputs[0], index); - DataSpecUtils::updateMatchingSubspec(spec.outputs[0], index); - }); - - workflow.insert(std::end(workflow), std::begin(processingStages), std::end(processingStages)); - - std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; - DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath); - - for (int i = 0; i < 3; ++i) { - auto disp = std::find_if(workflow.begin(), workflow.end(), - [i](const DataProcessorSpec& d) { - return d.name.find("Dispatcher") != std::string::npos; - }); - BOOST_REQUIRE(disp != workflow.end()); - - auto input = std::find_if(disp->inputs.begin(), disp->inputs.end(), - [i](const InputSpec& in) { - return DataSpecUtils::match(in, ConcreteDataMatcher{DataOrigin("TPC"), DataDescription("CLUSTERS"), static_cast<DataHeader::SubSpecificationType>(i)}) && in.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(input != disp->inputs.end()); - - input = std::find_if(disp->inputs.begin(), disp->inputs.end(), - [i](const InputSpec& in) { - return DataSpecUtils::match(in, ConcreteDataMatcher{DataOrigin("TPC"), DataDescription("CLUSTERS_P"), static_cast<DataHeader::SubSpecificationType>(i)}) && in.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(input != disp->inputs.end()); - - auto output = std::find_if(disp->outputs.begin(), disp->outputs.end(), - [](const OutputSpec& out) { - return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters-0", 0}) && out.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(output != disp->outputs.end()); - - output = std::find_if(disp->outputs.begin(), disp->outputs.end(), - [](const OutputSpec& out) { - return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters-1", 0}) && out.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(output != disp->outputs.end()); - - BOOST_CHECK(disp->algorithm.onInit != nullptr); - } -} - -BOOST_AUTO_TEST_CASE(DataSamplingTimePipelineFlow) -{ - WorkflowSpec workflow{ - {"producer", - Inputs{}, - {OutputSpec{"TPC", "CLUSTERS", 0, Lifetime::Timeframe}}}, - timePipeline( - DataProcessorSpec{ - "processingStage", - Inputs{ - {"dataTPC", "TPC", "CLUSTERS", 0, Lifetime::Timeframe}}, - Outputs{ - {"TPC", "CLUSTERS_P", 0, Lifetime::Timeframe}}}, - 3)}; - - std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; - DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath, 3); - - auto disp = std::find_if(workflow.begin(), workflow.end(), - [](const DataProcessorSpec& d) { - return d.name.find("Dispatcher") != std::string::npos; - }); - - BOOST_REQUIRE(disp != workflow.end()); - BOOST_CHECK_EQUAL(disp->inputs.size(), 4); - BOOST_CHECK_EQUAL(disp->outputs.size(), 3); - BOOST_CHECK(disp->algorithm.onInit != nullptr); - BOOST_CHECK_EQUAL(disp->maxInputTimeslices, 3); -} - -BOOST_AUTO_TEST_CASE(DataSamplingFairMq) -{ - WorkflowSpec workflow{ - specifyExternalFairMQDeviceProxy( - "readout-proxy", - Outputs{{"TPC", "RAWDATA"}}, - "fake-channel-config", - dataSamplingReadoutAdapter({"TPC", "RAWDATA"}))}; - - std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; - DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath); - - auto disp = std::find_if(workflow.begin(), workflow.end(), - [](const DataProcessorSpec& d) { - return d.name.find("Dispatcher") != std::string::npos; - }); - BOOST_REQUIRE(disp != workflow.end()); - - auto input = std::find_if(disp->inputs.begin(), disp->inputs.end(), - [](const InputSpec& in) { - return DataSpecUtils::match(in, ConcreteDataMatcher{DataOrigin("TPC"), DataDescription("RAWDATA"), 0}) && in.lifetime == Lifetime::Timeframe; - }); - BOOST_CHECK(input != disp->inputs.end()); - - auto channelConfig = std::find_if(disp->options.begin(), disp->options.end(), - [](const ConfigParamSpec& opt) { - return opt.name == "channel-config"; - }); - BOOST_REQUIRE(channelConfig != disp->options.end()); -} - -BOOST_AUTO_TEST_CASE(InputSpecsForPolicy) -{ - std::string configFilePath = "json:/" + std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; - std::vector<InputSpec> inputs = DataSampling::InputSpecsForPolicy(configFilePath, "tpcclusters"); - - BOOST_CHECK_EQUAL(inputs.size(), 2); - BOOST_CHECK(DataSpecUtils::match(inputs[0], ConcreteDataTypeMatcher{"DS", "tpcclusters-0"})); - BOOST_CHECK_EQUAL(inputs[0].binding, "clusters"); - BOOST_CHECK(DataSpecUtils::match(inputs[1], ConcreteDataTypeMatcher{"DS", "tpcclusters-1"})); - BOOST_CHECK_EQUAL(inputs[1].binding, "clusters_p"); - - std::unique_ptr<ConfigurationInterface> config = ConfigurationFactory::getConfiguration(configFilePath); - inputs = DataSampling::InputSpecsForPolicy(config.get(), "tpcclusters"); - - BOOST_CHECK_EQUAL(inputs.size(), 2); -} diff --git a/Framework/Core/test/test_DataSamplingPolicy.cxx b/Framework/Core/test/test_DataSamplingPolicy.cxx deleted file mode 100644 index cc003cf8caad9..0000000000000 --- a/Framework/Core/test/test_DataSamplingPolicy.cxx +++ /dev/null @@ -1,96 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Framework DataSamplingPolicy -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include <boost/test/unit_test.hpp> -#include <boost/property_tree/ptree.hpp> - -#include "Framework/DataSamplingPolicy.h" -#include "Framework/DataRef.h" -#include "Framework/DataProcessingHeader.h" - -using namespace o2::framework; - -// an example of DataSamplingPolicy JSON object -// { -// "id" : "policy_example1", -// "active" : "false", -// "machines" : [ -// "aidlalala1", -// "aidlalala2" -// ] -// "dataHeaders" : [ -// { -// "binding" : "clusters", -// "dataOrigin" : "TPC", -// "dataDescription" : "CLUSTERS" -// }, -// { -// "binding" : "tracks", -// "dataOrigin" : "TPC", -// "dataDescription" : "TRACKS" -// } -// ], -// "subSpec" : "*", -// "samplingConditions" : [ -// { -// "condition" : "random", -// "fraction" : "0.1", -// "seed" : "2137" -// } -// ], -// "blocking" : "false" -// } - -BOOST_AUTO_TEST_CASE(DataSamplingPolicyConfiguration) -{ - using boost::property_tree::ptree; - DataSamplingPolicy policy; - - ptree config; - config.put("id", "my_policy"); - config.put("active", "true"); - config.put("query", "c:TST/CHLEB/33;m:TST/MLEKO/33"); - ptree samplingConditions; - ptree conditionRandom; - conditionRandom.put("condition", "random"); - conditionRandom.put("fraction", "0.1"); - conditionRandom.put("seed", "2137"); - samplingConditions.push_back(std::make_pair("", conditionRandom)); - config.add_child("samplingConditions", samplingConditions); - config.put("blocking", "false"); - - policy.configure(config); - - BOOST_CHECK_EQUAL(policy.getName(), "my_policy"); - BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"DS", "my_policy-0", 33})); - BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "MLEKO", 33})) == (Output{"DS", "my_policy-1", 33})); - const auto& map = policy.getPathMap(); - BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "CHLEB", 33})).second == (OutputSpec{"DS", "my_policy-0", 33})); - BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "MLEKO", 33})).second == (OutputSpec{"DS", "my_policy-1", 33})); - BOOST_CHECK_EQUAL(map.size(), 2); - - BOOST_CHECK(policy.match(ConcreteDataMatcher{"TST", "CHLEB", 33})); - BOOST_CHECK(!policy.match(ConcreteDataMatcher{"TST", "SZYNKA", 33})); - - DataProcessingHeader dph{555, 0}; - o2::header::Stack headerStack{dph}; - DataRef dr{nullptr, reinterpret_cast<const char*>(headerStack.data()), nullptr}; - policy.decide(dr); // just make sure it does not crash - - config.put("id", "too-long-policy-name"); - policy.configure(config); - BOOST_CHECK_EQUAL(policy.getName(), "too-long-polic"); - BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"DS", "too-long-polic-0", 33})); - BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "MLEKO", 33})) == (Output{"DS", "too-long-polic-1", 33})); - BOOST_CHECK_EQUAL(policy.getPathMap().size(), 2); // previous paths should be cleared -} diff --git a/Framework/Core/test/test_DeviceConfigInfo.cxx b/Framework/Core/test/test_DeviceConfigInfo.cxx index de38eecc02164..4533ebfcac79a 100644 --- a/Framework/Core/test/test_DeviceConfigInfo.cxx +++ b/Framework/Core/test/test_DeviceConfigInfo.cxx @@ -13,10 +13,32 @@ #include "Framework/DeviceConfigInfo.h" #include "Framework/DeviceInfo.h" +#include "Framework/Variant.h" #include <boost/test/unit_test.hpp> +#include <boost/tokenizer.hpp> +#include <boost/lexical_cast.hpp> #include <string_view> +#include <regex> -BOOST_AUTO_TEST_CASE(TestDeviceMetricsInfo) +template <typename T> +std::string arrayPrinter(boost::property_tree::ptree const& tree) +{ + std::stringstream ss; + int size = tree.size(); + int count = 0; + ss << o2::framework::variant_array_symbol<T>::symbol << "["; + for (auto& element : tree) { + ss << element.second.get_value<T>(); + if (count < size - 1) { + ss << ","; + } + ++count; + } + ss << "]"; + return ss.str(); +} + +BOOST_AUTO_TEST_CASE(TestDeviceConfigInfo) { using namespace o2::framework; std::string configString; @@ -76,4 +98,22 @@ BOOST_AUTO_TEST_CASE(TestDeviceMetricsInfo) BOOST_CHECK_EQUAL(result, true); BOOST_CHECK_EQUAL(info.currentConfig.get<std::string>("foo"), "bar"); BOOST_CHECK_EQUAL(info.currentProvenance.get<std::string>("foo"), "prov"); + + // Parse an array + configString = "foo[XX:XX:XX][INFO] [CONFIG] array={\"\":\"1\",\"\":\"2\",\"\":\"3\",\"\":\"4\",\"\":\"5\"} 1789372894 prov\n"; + std::string_view configa{configString.data() + 3, configString.size() - 4}; + result = DeviceConfigHelper::parseConfig(configa, match); + auto valueString = std::string(match.beginValue, match.endValue - match.beginValue); + + BOOST_REQUIRE_EQUAL(result, true); + BOOST_CHECK(strncmp(match.beginKey, "array", 5) == 0); + BOOST_CHECK_EQUAL(match.timestamp, 1789372894); + BOOST_CHECK(strncmp(match.beginValue, "{\"\":\"1\",\"\":\"2\",\"\":\"3\",\"\":\"4\",\"\":\"5\"}", 35) == 0); + BOOST_CHECK(strncmp(match.beginProvenance, "prov", 4) == 0); + + // Process a given config entry + result = DeviceConfigHelper::processConfig(match, info); + BOOST_CHECK_EQUAL(result, true); + BOOST_CHECK_EQUAL(info.currentProvenance.get<std::string>("array"), "prov"); + BOOST_CHECK_EQUAL(arrayPrinter<int>(info.currentConfig.get_child("array")), "i[1,2,3,4,5]"); } diff --git a/Framework/Core/test/test_DeviceMetricsInfo.cxx b/Framework/Core/test/test_DeviceMetricsInfo.cxx index de32a3cc62c8a..9b056b315549d 100644 --- a/Framework/Core/test/test_DeviceMetricsInfo.cxx +++ b/Framework/Core/test/test_DeviceMetricsInfo.cxx @@ -129,3 +129,59 @@ BOOST_AUTO_TEST_CASE(TestDeviceMetricsInfo) result = DeviceMetricsHelper::processMetric(match, info); BOOST_CHECK_EQUAL(result, true); } + +BOOST_AUTO_TEST_CASE(TestDeviceMetricsInfo2) +{ + using namespace o2::framework; + DeviceMetricsInfo info; + auto bkey = DeviceMetricsHelper::createNumericMetric<int>(info, "bkey"); + auto akey = DeviceMetricsHelper::createNumericMetric<float>(info, "akey"); + auto ckey = DeviceMetricsHelper::createNumericMetric<uint64_t>(info, "ckey"); + BOOST_CHECK_EQUAL(DeviceMetricsHelper::metricIdxByName("akey", info), 1); + BOOST_CHECK_EQUAL(DeviceMetricsHelper::metricIdxByName("bkey", info), 0); + BOOST_CHECK_EQUAL(DeviceMetricsHelper::metricIdxByName("ckey", info), 2); + BOOST_CHECK_EQUAL(info.changed.at(0), true); + size_t t = 1000; + bkey(info, 0, t++); + bkey(info, 1, t++); + bkey(info, 2, t++); + bkey(info, 3, t++); + bkey(info, 4, t++); + bkey(info, 5, t++); + BOOST_CHECK_EQUAL(info.changed[0], true); + BOOST_CHECK_EQUAL(info.intMetrics[0][0], 0); + BOOST_CHECK_EQUAL(info.intMetrics[0][1], 1); + BOOST_CHECK_EQUAL(info.intMetrics[0][2], 2); + BOOST_CHECK_EQUAL(info.intMetrics[0][3], 3); + BOOST_CHECK_EQUAL(info.intMetrics[0][4], 4); + BOOST_CHECK_EQUAL(info.intMetrics[0][5], 5); + BOOST_CHECK_EQUAL(info.changed[1], true); + bkey(info, 5., t++); + akey(info, 1., t++); + akey(info, 2., t++); + akey(info, 3., t++); + akey(info, 4., t++); + BOOST_CHECK_EQUAL(info.intMetrics[0][6], 5); + BOOST_CHECK_EQUAL(info.floatMetrics[0][0], 1.); + BOOST_CHECK_EQUAL(info.floatMetrics[0][1], 2.); + BOOST_CHECK_EQUAL(info.floatMetrics[0][2], 3.); + BOOST_CHECK_EQUAL(info.floatMetrics[0][3], 4.); + BOOST_CHECK_EQUAL(info.timestamps.size(), 3); + BOOST_CHECK_EQUAL(info.timestamps[0][0], 1000); + BOOST_CHECK_EQUAL(info.timestamps[0][1], 1001); + BOOST_CHECK_EQUAL(info.timestamps[0][2], 1002); + BOOST_CHECK_EQUAL(info.timestamps[0][3], 1003); + BOOST_CHECK_EQUAL(info.timestamps[0][4], 1004); + BOOST_CHECK_EQUAL(info.timestamps[0][5], 1005); + BOOST_CHECK_EQUAL(info.timestamps[1][0], 1007); + BOOST_CHECK_EQUAL(info.timestamps[1][1], 1008); + BOOST_CHECK_EQUAL(info.timestamps[1][2], 1009); + BOOST_CHECK_EQUAL(info.timestamps[1][3], 1010); + BOOST_CHECK_EQUAL(info.changed.size(), 3); + for (int i = 0; i < 1026; ++i) { + ckey(info, i, t++); + } + BOOST_CHECK_EQUAL(info.uint64Metrics[0][0], 1024); + BOOST_CHECK_EQUAL(info.uint64Metrics[0][1], 1025); + BOOST_CHECK_EQUAL(info.uint64Metrics[0][2], 2); +} diff --git a/Framework/Core/test/test_DeviceSpec.cxx b/Framework/Core/test/test_DeviceSpec.cxx index fcf2f096e61d5..78fa8949a97f6 100644 --- a/Framework/Core/test/test_DeviceSpec.cxx +++ b/Framework/Core/test/test_DeviceSpec.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Mocking.h" #include <boost/test/unit_test.hpp> #include "../src/ChannelSpecHelpers.h" #include "../src/DeviceSpecHelpers.h" @@ -40,7 +41,8 @@ WorkflowSpec defineDataProcessing1() BOOST_AUTO_TEST_CASE(TestDeviceSpec1) { auto workflow = defineDataProcessing1(); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); BOOST_REQUIRE_EQUAL(channelPolicies.empty(), false); BOOST_REQUIRE_EQUAL(completionPolicies.empty(), false); @@ -80,10 +82,11 @@ BOOST_AUTO_TEST_CASE(TestDeviceSpec1PushPull) auto workflow = defineDataProcessing1(); ChannelConfigurationPolicy pushPullPolicy; pushPullPolicy.match = ChannelConfigurationPolicyHelpers::matchAny; - pushPullPolicy.modifyInput = ChannelConfigurationPolicyHelpers::pullInput; - pushPullPolicy.modifyOutput = ChannelConfigurationPolicyHelpers::pushOutput; + pushPullPolicy.modifyInput = ChannelConfigurationPolicyHelpers::pullInput({60}); + pushPullPolicy.modifyOutput = ChannelConfigurationPolicyHelpers::pushOutput({60}); std::vector<ChannelConfigurationPolicy> channelPolicies = {pushPullPolicy}; + auto configContext = makeEmptyConfigContext(); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); BOOST_REQUIRE_EQUAL(channelPolicies.empty(), false); @@ -128,7 +131,8 @@ WorkflowSpec defineDataProcessing2() BOOST_AUTO_TEST_CASE(TestDeviceSpec2) { auto workflow = defineDataProcessing2(); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<DeviceSpec> devices; @@ -170,7 +174,8 @@ WorkflowSpec defineDataProcessing3() BOOST_AUTO_TEST_CASE(TestDeviceSpec3) { auto workflow = defineDataProcessing3(); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<DeviceSpec> devices; @@ -218,7 +223,8 @@ WorkflowSpec defineDataProcessing4() BOOST_AUTO_TEST_CASE(TestDeviceSpec4) { auto workflow = defineDataProcessing4(); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<DeviceSpec> devices; std::vector<ComputingResource> resources{ComputingResourceHelpers::getLocalhostResource()}; @@ -287,7 +293,8 @@ WorkflowSpec defineDataProcessing5() BOOST_AUTO_TEST_CASE(TestTopologyForwarding) { auto workflow = defineDataProcessing5(); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<DeviceSpec> devices; @@ -404,7 +411,8 @@ BOOST_AUTO_TEST_CASE(TestOutEdgeProcessingHelpers) }; WorkflowSpec workflow = defineDataProcessing7(); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); std::vector<ComputingResource> resources{ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); @@ -413,7 +421,7 @@ BOOST_AUTO_TEST_CASE(TestOutEdgeProcessingHelpers) defaultOffer.memory = 0.01; DeviceSpecHelpers::processOutEdgeActions(devices, deviceIndex, connections, rm, edgeOutIndex, logicalEdges, - actions, workflow, globalOutputs, channelPolicies, defaultOffer); + actions, workflow, globalOutputs, channelPolicies, "", defaultOffer); std::vector<DeviceId> expectedDeviceIndex = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {1, 0, 1}, {1, 0, 1}, {1, 1, 2}, {1, 1, 2}, {1, 2, 3}, {1, 2, 3}}; BOOST_REQUIRE_EQUAL(devices.size(), 4); // For producers @@ -461,7 +469,7 @@ BOOST_AUTO_TEST_CASE(TestOutEdgeProcessingHelpers) std::sort(connections.begin(), connections.end()); DeviceSpecHelpers::processInEdgeActions(devices, deviceIndex, connections, rm, edgeInIndex, logicalEdges, - inActions, workflow, availableForwardsInfo, channelPolicies, defaultOffer); + inActions, workflow, availableForwardsInfo, channelPolicies, "", defaultOffer); // std::vector<DeviceId> expectedDeviceIndexFinal = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {1, 0, 1}, {1, 0, 1}, {1, 1, 2}, {1, 1, 2}, {1, 2, 3}, {1, 2, 3}, {2, 0, 4}, {2, 1, 5}}; BOOST_REQUIRE_EQUAL(expectedDeviceIndexFinal.size(), deviceIndex.size()); @@ -566,7 +574,8 @@ BOOST_AUTO_TEST_CASE(TestTopologyLayeredTimePipeline) { auto workflow = defineDataProcessing7(); std::vector<DeviceSpec> devices; - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<ComputingResource> resources{ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); @@ -690,7 +699,8 @@ BOOST_AUTO_TEST_CASE(TestSimpleWildcard) auto workflow = defineDataProcessing8(); std::vector<ComputingResource> resources{ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); std::vector<DeviceSpec> devices; std::vector<DeviceId> deviceIndex; @@ -729,7 +739,7 @@ BOOST_AUTO_TEST_CASE(TestSimpleWildcard) defaultOffer.memory = 0.01; DeviceSpecHelpers::processOutEdgeActions(devices, deviceIndex, connections, rm, edgeOutIndex, logicalEdges, - outActions, workflow, globalOutputs, channelPolicies, defaultOffer); + outActions, workflow, globalOutputs, channelPolicies, "", defaultOffer); BOOST_REQUIRE_EQUAL(devices.size(), 2); // Two devices have outputs: A and Timer BOOST_CHECK_EQUAL(devices[0].name, "A"); @@ -745,7 +755,7 @@ BOOST_AUTO_TEST_CASE(TestSimpleWildcard) std::sort(connections.begin(), connections.end()); DeviceSpecHelpers::processInEdgeActions(devices, deviceIndex, connections, rm, edgeInIndex, logicalEdges, - inActions, workflow, availableForwardsInfo, channelPolicies, defaultOffer); + inActions, workflow, availableForwardsInfo, channelPolicies, "", defaultOffer); BOOST_REQUIRE_EQUAL(devices.size(), 3); // Now we also have B BOOST_CHECK_EQUAL(devices[0].name, "A"); diff --git a/Framework/Core/test/test_DeviceSpecHelpers.cxx b/Framework/Core/test/test_DeviceSpecHelpers.cxx index df953aab44dca..f7790c3642ab9 100644 --- a/Framework/Core/test/test_DeviceSpecHelpers.cxx +++ b/Framework/Core/test/test_DeviceSpecHelpers.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Mocking.h" #include "Framework/WorkflowSpec.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DeviceExecution.h" @@ -136,8 +137,10 @@ BOOST_AUTO_TEST_CASE(test_prepareArguments) std::vector<ComputingResource> resources = {ComputingResourceHelpers::getLocalhostResource()}; auto rm = std::make_unique<SimpleResourceManager>(resources); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); DeviceSpecHelpers::dataProcessorSpecs2DeviceSpecs(workflow, - ChannelConfigurationPolicy::createDefaultPolicies(), + channelPolicies, CompletionPolicy::createDefaultPolicies(), deviceSpecs, *rm, "workflow-id"); diff --git a/Framework/Core/test/test_Expressions.cxx b/Framework/Core/test/test_Expressions.cxx index c50ba227efd0e..c188a5710af99 100644 --- a/Framework/Core/test/test_Expressions.cxx +++ b/Framework/Core/test/test_Expressions.cxx @@ -12,6 +12,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Framework/Configurable.h" #include "../src/ExpressionHelpers.h" #include "Framework/AnalysisDataModel.h" #include "Framework/AODReaderHelpers.h" @@ -28,6 +29,7 @@ static BindingNode eta{"eta", atype::FLOAT}; static BindingNode tgl{"tgl", atype::FLOAT}; static BindingNode signed1Pt{"signed1Pt", atype::FLOAT}; +static BindingNode testInt{"testInt", atype::INT32}; } // namespace nodes namespace o2::aod::track @@ -121,6 +123,16 @@ BOOST_AUTO_TEST_CASE(TestTreeParsing) BOOST_REQUIRE_EQUAL(uspecs[5].left, (DatumSpec{std::string{"eta"}, atype::FLOAT})); BOOST_REQUIRE_EQUAL(uspecs[5].right, (DatumSpec{})); BOOST_REQUIRE_EQUAL(uspecs[5].result, (DatumSpec{5u, atype::FLOAT})); + + Configurable<float> pTCut{"pTCut", 0.5f, "Lower pT limit"}; + Filter ptfilter = o2::aod::track::pt > pTCut; + BOOST_REQUIRE_EQUAL(ptfilter.node->self.index(), 2); + BOOST_REQUIRE_EQUAL(ptfilter.node->left->self.index(), 1); + BOOST_REQUIRE_EQUAL(ptfilter.node->right->self.index(), 3); + auto ptfilterspecs = createOperations(ptfilter); + BOOST_REQUIRE_EQUAL(ptfilterspecs[0].left, (DatumSpec{std::string{"fPt"}, atype::FLOAT})); + BOOST_REQUIRE_EQUAL(ptfilterspecs[0].right, (DatumSpec{LiteralNode::var_t{0.5f}, atype::FLOAT})); + BOOST_REQUIRE_EQUAL(ptfilterspecs[0].result, (DatumSpec{0u, atype::BOOL})); } BOOST_AUTO_TEST_CASE(TestGandivaTreeCreation) @@ -144,29 +156,25 @@ BOOST_AUTO_TEST_CASE(TestGandivaTreeCreation) BOOST_CHECK_EQUAL(gandiva_expression->ToString(), "float multiply((float) fTgl, float divide((const float) 1 raw(3f800000), (float) fSigned1Pt))"); auto projector = createProjector(schema, pzspecs, resfield); - Projector pte = o2::aod::track::Pt2::Projector(); + Projector pte = o2::aod::track::Pt::Projector(); auto ptespecs = createOperations(std::move(pte)); BOOST_REQUIRE_EQUAL(ptespecs[0].left, (DatumSpec{1u, atype::FLOAT})); - BOOST_REQUIRE_EQUAL(ptespecs[0].right, (DatumSpec{2u, atype::FLOAT})); + BOOST_REQUIRE_EQUAL(ptespecs[0].right, (DatumSpec{})); BOOST_REQUIRE_EQUAL(ptespecs[0].result, (DatumSpec{0u, atype::FLOAT})); BOOST_REQUIRE_EQUAL(ptespecs[1].left, (DatumSpec{LiteralNode::var_t{1.f}, atype::FLOAT})); BOOST_REQUIRE_EQUAL(ptespecs[1].right, (DatumSpec{std::string{"fSigned1Pt"}, atype::FLOAT})); - BOOST_REQUIRE_EQUAL(ptespecs[1].result, (DatumSpec{2u, atype::FLOAT})); - - BOOST_REQUIRE_EQUAL(ptespecs[2].left, (DatumSpec{LiteralNode::var_t{1.f}, atype::FLOAT})); - BOOST_REQUIRE_EQUAL(ptespecs[2].right, (DatumSpec{std::string{"fSigned1Pt"}, atype::FLOAT})); - BOOST_REQUIRE_EQUAL(ptespecs[2].result, (DatumSpec{1u, atype::FLOAT})); + BOOST_REQUIRE_EQUAL(ptespecs[1].result, (DatumSpec{1u, atype::FLOAT})); auto infield3 = o2::aod::track::Signed1Pt::asArrowField(); - auto resfield2 = o2::aod::track::Pt2::asArrowField(); + auto resfield2 = o2::aod::track::Pt::asArrowField(); auto schema2 = std::make_shared<arrow::Schema>(std::vector{infield3, resfield2}); auto gandiva_tree2 = createExpressionTree(ptespecs, schema2); auto gandiva_expression2 = makeExpression(gandiva_tree2, resfield2); - BOOST_CHECK_EQUAL(gandiva_expression2->ToString(), "float multiply(float divide((const float) 1 raw(3f800000), (float) fSigned1Pt), float divide((const float) 1 raw(3f800000), (float) fSigned1Pt))"); + BOOST_CHECK_EQUAL(gandiva_expression2->ToString(), "float absf(float divide((const float) 1 raw(3f800000), (float) fSigned1Pt))"); auto projector_b = createProjector(schema2, ptespecs, resfield2); auto schema_p = o2::soa::createSchemaFromColumns(o2::aod::Tracks::persistent_columns_t{}); - auto projector_alt = o2::framework::expressions::createProjectors(o2::framework::pack<o2::aod::track::Pt2>{}, schema_p); + auto projector_alt = o2::framework::expressions::createProjectors(o2::framework::pack<o2::aod::track::Pt>{}, schema_p); } diff --git a/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx b/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx index 0bf001a15f806..9adab2a7236a1 100644 --- a/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx +++ b/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx @@ -75,8 +75,8 @@ std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext const& config) // use the OutputChannelSpec as a tool to create the default configuration for the out-of-band channel OutputChannelSpec externalChannelSpec; - // Note: the name has to match the binding of the input spec - externalChannelSpec.name = "external"; + // Note: the name is hardcoded for now + externalChannelSpec.name = "downstream"; externalChannelSpec.type = ChannelType::Push; externalChannelSpec.method = ChannelMethod::Bind; externalChannelSpec.hostname = "localhost"; @@ -133,11 +133,15 @@ std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext const& config) LOG(ERROR) << "data on input " << msgidx << " does not follow the O2 data model, DataHeader missing"; return; } - + auto dph = o2::header::get<DataProcessingHeader*>(inputs.At(msgidx)->GetData()); + if (!dph) { + LOG(ERROR) << "data on input " << msgidx << " does not follow the O2 data model, DataProcessingHeader missing"; + return; + } // Note: we want to run both the output and input proxy in the same workflow and thus we need // different data identifiers and change the data origin in the forwarding OutputSpec query{"PRX", dh->dataDescription, dh->subSpecification}; - auto channelName = channelRetriever(query); + auto channelName = channelRetriever(query, dph->startTime); ASSERT_ERROR(!channelName.empty()); LOG(DEBUG) << "using channel '" << channelName << "' for " << DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}); if (channelName.empty()) { diff --git a/Framework/Core/test/test_FrameworkDataFlowToDDS.cxx b/Framework/Core/test/test_FrameworkDataFlowToDDS.cxx index 8861b0df3101b..4d74400fe15d0 100644 --- a/Framework/Core/test/test_FrameworkDataFlowToDDS.cxx +++ b/Framework/Core/test/test_FrameworkDataFlowToDDS.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Mocking.h" #include <boost/test/unit_test.hpp> #include "../src/DDSConfigHelpers.h" #include "../src/DeviceSpecHelpers.h" @@ -69,7 +70,8 @@ BOOST_AUTO_TEST_CASE(TestDDS) { auto workflow = defineDataProcessing(); std::ostringstream ss{""}; - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); std::vector<DeviceSpec> devices; std::vector<ComputingResource> resources{ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); @@ -97,16 +99,16 @@ BOOST_AUTO_TEST_CASE(TestDDS) dumpDeviceSpec2DDS(ss, devices, executions); BOOST_CHECK_EQUAL(ss.str(), R"EXPECTED(<topology name="o2-dataflow"> <decltask name="A"> - <exe reachable="true">foo --id A --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> + <exe reachable="true">foo --id A --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --shm-mlock-segment false --shm-segment-id 0 --shm-throw-bad-alloc true --shm-zero-segment false --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> </decltask> <decltask name="B"> - <exe reachable="true">foo --id B --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> + <exe reachable="true">foo --id B --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --shm-mlock-segment false --shm-segment-id 0 --shm-throw-bad-alloc true --shm-zero-segment false --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> </decltask> <decltask name="C"> - <exe reachable="true">foo --id C --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> + <exe reachable="true">foo --id C --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --shm-mlock-segment false --shm-segment-id 0 --shm-throw-bad-alloc true --shm-zero-segment false --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> </decltask> <decltask name="D"> - <exe reachable="true">foo --id D --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> + <exe reachable="true">foo --id D --control static --shm-monitor false --log-color false --color false --jobs 4 --severity info --shm-mlock-segment false --shm-segment-id 0 --shm-throw-bad-alloc true --shm-zero-segment false --session dpl_workflow-id --plugin-search-path $FAIRMQ_ROOT/lib --plugin dds</exe> </decltask> <declcollection name="DPL"> <tasks> diff --git a/Framework/Core/test/test_Graphviz.cxx b/Framework/Core/test/test_Graphviz.cxx index 262c104714900..3345b7c400e38 100644 --- a/Framework/Core/test/test_Graphviz.cxx +++ b/Framework/Core/test/test_Graphviz.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Mocking.h" #include "../src/ComputingResourceHelpers.h" #include "../src/DeviceSpecHelpers.h" #include "../src/GraphvizHelpers.h" @@ -95,7 +96,8 @@ BOOST_AUTO_TEST_CASE(TestGraphviz) for (auto& device : devices) { BOOST_CHECK(device.id != ""); } - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<ComputingResource> resources = {ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); @@ -133,7 +135,8 @@ BOOST_AUTO_TEST_CASE(TestGraphvizWithPipeline) for (auto& device : devices) { BOOST_CHECK(device.id != ""); } - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<ComputingResource> resources = {ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); diff --git a/Framework/Core/test/test_GroupSlicer.cxx b/Framework/Core/test/test_GroupSlicer.cxx index 13008121bc0c6..4117d629e0eda 100644 --- a/Framework/Core/test/test_GroupSlicer.cxx +++ b/Framework/Core/test/test_GroupSlicer.cxx @@ -23,12 +23,14 @@ namespace o2::aod { namespace test { +DECLARE_SOA_COLUMN(ID, id, int); DECLARE_SOA_COLUMN(Foo, foo, float); DECLARE_SOA_COLUMN(Bar, bar, float); DECLARE_SOA_COLUMN(EventProperty, eventProperty, float); } // namespace test DECLARE_SOA_TABLE(Events, "AOD", "EVTS", o2::soa::Index<>, + test::ID, test::EventProperty, test::Foo, test::Bar); @@ -56,13 +58,22 @@ DECLARE_SOA_TABLE(TrksU, "AOD", "TRKSU", test::X, test::Y, test::Z); + +namespace test +{ +DECLARE_SOA_COLUMN(Arr, arr, float[3]); +DECLARE_SOA_COLUMN(Boo, boo, bool); +} // namespace test + +DECLARE_SOA_TABLE(EventExtra, "AOD", "EVTSXTRA", test::Arr, test::Boo); + } // namespace o2::aod BOOST_AUTO_TEST_CASE(GroupSlicerOneAssociated) { TableBuilder builderE; auto evtsWriter = builderE.cursor<aod::Events>(); for (auto i = 0; i < 20; ++i) { - evtsWriter(0, 0.5f * i, 2.f * i, 3.f * i); + evtsWriter(0, i, 0.5f * i, 2.f * i, 3.f * i); } auto evtTable = builderE.finalize(); @@ -101,7 +112,7 @@ BOOST_AUTO_TEST_CASE(GroupSlicerSeveralAssociated) TableBuilder builderE; auto evtsWriter = builderE.cursor<aod::Events>(); for (auto i = 0; i < 20; ++i) { - evtsWriter(0, 0.5f * i, 2.f * i, 3.f * i); + evtsWriter(0, i, 0.5f * i, 2.f * i, 3.f * i); } auto evtTable = builderE.finalize(); @@ -189,7 +200,7 @@ BOOST_AUTO_TEST_CASE(GroupSlicerMismatchedGroups) TableBuilder builderE; auto evtsWriter = builderE.cursor<aod::Events>(); for (auto i = 0; i < 20; ++i) { - evtsWriter(0, 0.5f * i, 2.f * i, 3.f * i); + evtsWriter(0, i, 0.5f * i, 2.f * i, 3.f * i); } auto evtTable = builderE.finalize(); @@ -229,3 +240,140 @@ BOOST_AUTO_TEST_CASE(GroupSlicerMismatchedGroups) ++count; } } + +BOOST_AUTO_TEST_CASE(GroupSlicerMismatchedFilteredGroups) +{ + TableBuilder builderE; + auto evtsWriter = builderE.cursor<aod::Events>(); + for (auto i = 0; i < 20; ++i) { + evtsWriter(0, i, 0.5f * i, 2.f * i, 3.f * i); + } + auto evtTable = builderE.finalize(); + + TableBuilder builderT; + auto trksWriter = builderT.cursor<aod::TrksX>(); + for (auto i = 0; i < 20; ++i) { + if (i == 3 || i == 10 || i == 12 || i == 16) { + continue; + } + for (auto j = 0.f; j < 5; j += 0.5f) { + trksWriter(0, i, 0.5f * j); + } + } + auto trkTable = builderT.finalize(); + using FilteredEvents = soa::Filtered<aod::Events>; + soa::SelectionVector rows{2, 4, 10, 9, 15}; + FilteredEvents e{{evtTable}, {2, 4, 10, 9, 15}}; + aod::TrksX t{trkTable}; + BOOST_CHECK_EQUAL(e.size(), 5); + BOOST_CHECK_EQUAL(t.size(), 10 * (20 - 4)); + + auto tt = std::make_tuple(t); + o2::framework::AnalysisDataProcessorBuilder::GroupSlicer g(e, tt); + + unsigned int count = 0; + + for (auto& slice : g) { + auto as = slice.associatedTables(); + auto gg = slice.groupingElement(); + BOOST_CHECK_EQUAL(gg.globalIndex(), rows[count]); + auto trks = std::get<aod::TrksX>(as); + if (rows[count] == 3 || rows[count] == 10 || rows[count] == 12 || rows[count] == 16) { + BOOST_CHECK_EQUAL(trks.size(), 0); + } else { + BOOST_CHECK_EQUAL(trks.size(), 10); + } + for (auto& trk : trks) { + BOOST_CHECK_EQUAL(trk.eventId(), rows[count]); + } + ++count; + } +} + +BOOST_AUTO_TEST_CASE(EmptySliceables) +{ + TableBuilder builderE; + auto evtsWriter = builderE.cursor<aod::Events>(); + for (auto i = 0; i < 20; ++i) { + evtsWriter(0, i, 0.5f * i, 2.f * i, 3.f * i); + } + auto evtTable = builderE.finalize(); + + TableBuilder builderT; + auto trksWriter = builderT.cursor<aod::TrksX>(); + auto trkTable = builderT.finalize(); + + aod::Events e{evtTable}; + aod::TrksX t{trkTable}; + BOOST_CHECK_EQUAL(e.size(), 20); + BOOST_CHECK_EQUAL(t.size(), 0); + + auto tt = std::make_tuple(t); + o2::framework::AnalysisDataProcessorBuilder::GroupSlicer g(e, tt); + + unsigned int count = 0; + for (auto& slice : g) { + auto as = slice.associatedTables(); + auto gg = slice.groupingElement(); + auto trks = std::get<aod::TrksX>(as); + BOOST_CHECK_EQUAL(gg.globalIndex(), count); + BOOST_CHECK_EQUAL(trks.size(), 0); + ++count; + } +} + +BOOST_AUTO_TEST_CASE(ArrowDirectSlicing) +{ + int counts[] = {5, 5, 5, 4, 1}; + int offsets[] = {0, 5, 10, 15, 19, 20}; + int ids[] = {0, 1, 2, 3, 4}; + + using BigE = soa::Join<aod::Events, aod::EventExtra>; + + TableBuilder builderE; + auto evtsWriter = builderE.cursor<aod::Events>(); + auto step = 0; + for (auto i = 0; i < 20; ++i) { + if (i >= offsets[step + 1]) { + ++step; + } + evtsWriter(0, ids[step], 0.5f * i, 2.f * i, 3.f * i); + } + auto evtTable = builderE.finalize(); + + TableBuilder builderEE; + auto evtsEWriter = builderEE.cursor<aod::EventExtra>(); + for (auto i = 0; i < 20; ++i) { + float arr[3] = {0.1f * i, 0.2f * i, 0.3f * i}; + evtsEWriter(0, arr, i % 2 == 0); + } + auto evtETable = builderEE.finalize(); + + aod::Events e{evtTable}; + aod::EventExtra ee{evtETable}; + BigE b_e{{evtTable, evtETable}}; + + std::vector<std::shared_ptr<arrow::ChunkedArray>> slices_array; + std::vector<std::shared_ptr<arrow::ChunkedArray>> slices_bool; + auto offset = 0; + for (auto i = 0u; i < 5; ++i) { + slices_array.emplace_back(evtETable->column(0)->Slice(offset, counts[i])); + slices_bool.emplace_back(evtETable->column(1)->Slice(offset, counts[i])); + offset += counts[i]; + BOOST_REQUIRE_EQUAL(slices_array[i]->length(), counts[i]); + BOOST_REQUIRE_EQUAL(slices_bool[i]->length(), counts[i]); + } + + std::vector<arrow::Datum> slices; + std::vector<uint64_t> offsts; + auto status = sliceByColumn("fID", b_e.asArrowTable(), 20, &slices, &offsts); + for (auto i = 0u; i < 5; ++i) { + auto tbl = arrow::util::get<std::shared_ptr<arrow::Table>>(slices[i].value); + auto ca = tbl->GetColumnByName("fArr"); + auto cb = tbl->GetColumnByName("fBoo"); + BOOST_REQUIRE_EQUAL(ca->length(), counts[i]); + BOOST_REQUIRE_EQUAL(cb->length(), counts[i]); + BOOST_CHECK(ca->Equals(slices_array[i])); + BOOST_CHECK(cb->Equals(slices_bool[i])); + } +} diff --git a/Framework/Core/test/test_HistogramRegistry.cxx b/Framework/Core/test/test_HistogramRegistry.cxx index 5edccb200622c..d59587c444ad3 100644 --- a/Framework/Core/test/test_HistogramRegistry.cxx +++ b/Framework/Core/test/test_HistogramRegistry.cxx @@ -13,31 +13,97 @@ #include "Framework/HistogramRegistry.h" #include <boost/test/unit_test.hpp> +#include <iostream> using namespace o2; using namespace o2::framework; +namespace test +{ +DECLARE_SOA_COLUMN_FULL(X, x, float, "x"); +DECLARE_SOA_COLUMN_FULL(Y, y, float, "y"); +} // namespace test + HistogramRegistry foo() { - return {"r", true, {{"histo", "histo", {"TH1F", 100, 0, 1}}}}; + return {"r", {{"histo", "histo", {HistType::kTH1F, {{100, 0, 1}}}}}}; } BOOST_AUTO_TEST_CASE(HistogramRegistryLookup) { /// Construct a registry object with direct declaration - HistogramRegistry registry{"registry", true, {{"eta", "#Eta", {"TH1F", 100, -2.0, 2.0}}, {"phi", "#Phi", {"TH1D", 102, 0, 2 * M_PI}}, {"pt", "p_{T}", {"TH1D", 1002, -0.01, 50.1}}}}; + HistogramRegistry registry{ + "registry", { + {"eta", "#Eta", {HistType::kTH1F, {{100, -2.0, 2.0}}}}, // + {"phi", "#Phi", {HistType::kTH1D, {{102, 0, 2 * M_PI}}}}, // + {"pt", "p_{T}", {HistType::kTH1D, {{1002, -0.01, 50.1}}}}, // + {"ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + } // + }; /// Get histograms by name - BOOST_REQUIRE_EQUAL(registry.get("eta")->GetNbinsX(), 100); - BOOST_REQUIRE_EQUAL(registry.get("phi")->GetNbinsX(), 102); - BOOST_REQUIRE_EQUAL(registry.get("pt")->GetNbinsX(), 1002); + BOOST_REQUIRE_EQUAL(registry.get<TH1>(HIST("eta"))->GetNbinsX(), 100); + BOOST_REQUIRE_EQUAL(registry.get<TH1>(HIST("phi"))->GetNbinsX(), 102); + BOOST_REQUIRE_EQUAL(registry.get<TH1>(HIST("pt"))->GetNbinsX(), 1002); + BOOST_REQUIRE_EQUAL(registry.get<TH2>(HIST("ptToPt"))->GetNbinsX(), 100); + BOOST_REQUIRE_EQUAL(registry.get<TH2>(HIST("ptToPt"))->GetNbinsY(), 100); /// Get a pointer to the histogram - auto histo = registry.get("pt").get(); + auto histo = registry.get<TH1>(HIST("pt")).get(); BOOST_REQUIRE_EQUAL(histo->GetNbinsX(), 1002); /// Get registry object from a function auto r = foo(); - auto histo2 = r.get("histo").get(); + auto histo2 = r.get<TH1>(HIST("histo")).get(); BOOST_REQUIRE_EQUAL(histo2->GetNbinsX(), 100); + + registry.print(); + + // check that registry behaves correctly when two different names have equal hash: + /* + auto str1 = "Maria has nine red beds."; + auto str2 = "Steven has fifteen white tables."; + BOOST_REQUIRE_EQUAL(compile_time_hash(str1), compile_time_hash(str2)); + try { + registry.add(str1, "", kTH1F, {{20, 0.0f, 10.01f}}); + registry.add(str2, "", kTH1F, {{20, 0.0f, 10.01f}}); + } catch (...) { + std::cout << "Hash collision was detected correctly!" << std::endl; + } + */ +} + +BOOST_AUTO_TEST_CASE(HistogramRegistryExpressionFill) +{ + TableBuilder builderA; + auto rowWriterA = builderA.persist<float, float>({"x", "y"}); + rowWriterA(0, 0.0f, -2.0f); + rowWriterA(0, 1.0f, -4.0f); + rowWriterA(0, 2.0f, -1.0f); + rowWriterA(0, 3.0f, -5.0f); + rowWriterA(0, 4.0f, 0.0f); + rowWriterA(0, 5.0f, -9.0f); + rowWriterA(0, 6.0f, -7.0f); + rowWriterA(0, 7.0f, -4.0f); + auto tableA = builderA.finalize(); + BOOST_REQUIRE_EQUAL(tableA->num_rows(), 8); + using TestA = o2::soa::Table<o2::soa::Index<>, test::X, test::Y>; + TestA tests{tableA}; + BOOST_REQUIRE_EQUAL(8, tests.size()); + + /// Construct a registry object with direct declaration + HistogramRegistry registry{ + "registry", { + {"x", "test x", {HistType::kTH1F, {{100, 0.0f, 10.0f}}}}, // + {"xy", "test xy", {HistType::kTH2F, {{100, -10.0f, 10.01f}, {100, -10.0f, 10.01f}}}} // + } // + }; + + /// Fill histogram with expression and table + registry.fill<test::X>(HIST("x"), tests, test::x > 3.0f); + BOOST_CHECK_EQUAL(registry.get<TH1>(HIST("x"))->GetEntries(), 4); + + /// Fill histogram with expression and table + registry.fill<test::X, test::Y>(HIST("xy"), tests, test::x > 3.0f && test::y > -5.0f); + BOOST_CHECK_EQUAL(registry.get<TH2>(HIST("xy"))->GetEntries(), 2); } diff --git a/Framework/Core/test/test_IndexBuilder.cxx b/Framework/Core/test/test_IndexBuilder.cxx new file mode 100644 index 0000000000000..907f160a6a0d0 --- /dev/null +++ b/Framework/Core/test/test_IndexBuilder.cxx @@ -0,0 +1,139 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Framework IndexBuilder +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include <boost/test/unit_test.hpp> + +using namespace o2::framework; +using namespace arrow; +using namespace o2::soa; + +DECLARE_SOA_STORE(); +namespace coords +{ +DECLARE_SOA_COLUMN_FULL(X, x, float, "x"); +DECLARE_SOA_COLUMN_FULL(Y, y, float, "y"); +DECLARE_SOA_COLUMN_FULL(Z, z, float, "z"); +} // namespace coords +DECLARE_SOA_TABLE(Points, "TST", "POINTS", Index<>, coords::X, coords::Y, coords::Z); + +namespace extra_1 +{ +DECLARE_SOA_INDEX_COLUMN(Point, point); +DECLARE_SOA_COLUMN_FULL(D, d, float, "d"); +} // namespace extra_1 +DECLARE_SOA_TABLE(Distances, "TST", "DISTANCES", Index<>, extra_1::PointId, extra_1::D); + +namespace extra_2 +{ +DECLARE_SOA_INDEX_COLUMN(Point, point); +DECLARE_SOA_COLUMN_FULL(IsTrue, istrue, bool, "istrue"); +} // namespace extra_2 +DECLARE_SOA_TABLE(Flags, "TST", "Flags", Index<>, extra_2::PointId, extra_2::IsTrue); + +namespace extra_3 +{ +DECLARE_SOA_INDEX_COLUMN(Point, point); +DECLARE_SOA_COLUMN_FULL(Category, category, int32_t, "category"); +} // namespace extra_3 +DECLARE_SOA_TABLE(Categorys, "TST", "Categories", Index<>, extra_3::PointId, extra_3::Category); + +namespace indices +{ +DECLARE_SOA_INDEX_COLUMN(Point, point); +DECLARE_SOA_INDEX_COLUMN(Distance, distance); +DECLARE_SOA_INDEX_COLUMN(Flag, flag); +DECLARE_SOA_INDEX_COLUMN(Category, category); +} // namespace indices + +DECLARE_SOA_TABLE(IDXs, "TST", "Index", Index<>, indices::PointId, indices::DistanceId, indices::FlagId, indices::CategoryId); + +BOOST_AUTO_TEST_CASE(TestIndexBuilder) +{ + TableBuilder b1; + auto w1 = b1.cursor<Points>(); + TableBuilder b2; + auto w2 = b2.cursor<Distances>(); + TableBuilder b3; + auto w3 = b3.cursor<Flags>(); + TableBuilder b4; + auto w4 = b4.cursor<Categorys>(); + + for (auto i = 0; i < 10; ++i) { + w1(0, i * 2., i * 3., i * 4.); + } + + std::array<int, 7> d{0, 1, 2, 4, 7, 8, 9}; + std::array<int, 5> f{0, 1, 2, 5, 8}; + std::array<int, 7> c{0, 1, 2, 3, 5, 7, 8}; + + for (auto i : d) { + w2(0, i, i * 10.); + } + + for (auto i : f) { + w3(0, i, static_cast<bool>(i % 2)); + } + + for (auto i : c) { + w4(0, i, i + 2); + } + + auto t1 = b1.finalize(); + Points st1{t1}; + auto t2 = b2.finalize(); + Distances st2{t2}; + auto t3 = b3.finalize(); + Flags st3{t3}; + auto t4 = b4.finalize(); + Categorys st4{t4}; + + auto t5 = IndexExclusive::indexBuilder(typename IDXs::persistent_columns_t{}, st1, std::tie(st1, st2, st3, st4)); + BOOST_REQUIRE_EQUAL(t5->num_rows(), 4); + IDXs idxt{t5}; + idxt.bindExternalIndices(&st1, &st2, &st3, &st4); + for (auto& row : idxt) { + BOOST_REQUIRE(row.distance().pointId() == row.pointId()); + BOOST_REQUIRE(row.flag().pointId() == row.pointId()); + BOOST_REQUIRE(row.category().pointId() == row.pointId()); + } + + auto t6 = IndexSparse::indexBuilder(typename IDXs::persistent_columns_t{}, st1, std::tie(st1, st2, st3, st4)); + BOOST_REQUIRE_EQUAL(t6->num_rows(), st1.size()); + IDXs idxs{t6}; + std::array<int, 10> ds{0, 1, 2, -1, 4, -1, -1, 7, 8, 9}; + std::array<int, 10> fs{0, 1, 2, -1, -1, 5, -1, -1, 8, -1}; + std::array<int, 10> cs{0, 1, 2, 3, -1, 5, -1, 7, 8, -1}; + idxs.bindExternalIndices(&st1, &st2, &st3, &st4); + auto i = 0; + for (auto& row : idxs) { + if (row.has_distance()) { + BOOST_REQUIRE(row.distance().pointId() == ds[i]); + } else { + BOOST_REQUIRE(row.distanceId() == ds[i]); + } + if (row.has_flag()) { + BOOST_REQUIRE(row.flag().pointId() == fs[i]); + } else { + BOOST_REQUIRE(row.flagId() == fs[i]); + } + if (row.has_category()) { + BOOST_REQUIRE(row.category().pointId() == cs[i]); + } else { + BOOST_REQUIRE(row.categoryId() == cs[i]); + } + ++i; + } +} diff --git a/Framework/Core/test/test_InputRecord.cxx b/Framework/Core/test/test_InputRecord.cxx index 0fa93731c0572..56276be56c1ab 100644 --- a/Framework/Core/test/test_InputRecord.cxx +++ b/Framework/Core/test/test_InputRecord.cxx @@ -22,7 +22,7 @@ using namespace o2::framework; using DataHeader = o2::header::DataHeader; using Stack = o2::header::Stack; -bool any_exception(std::exception const& ex) { return true; } +bool any_exception(RuntimeErrorRef const& ex) { return true; } BOOST_AUTO_TEST_CASE(TestInputRecord) { @@ -48,12 +48,12 @@ BOOST_AUTO_TEST_CASE(TestInputRecord) // bunch of exceptions. InputRecord emptyRecord(schema, {[](size_t) { return DataRef{nullptr, nullptr, nullptr}; }, 0}); - BOOST_CHECK_EXCEPTION(emptyRecord.get("x"), std::exception, any_exception); - BOOST_CHECK_EXCEPTION(emptyRecord.get("y"), std::exception, any_exception); - BOOST_CHECK_EXCEPTION(emptyRecord.get("z"), std::exception, any_exception); - BOOST_CHECK_EXCEPTION(emptyRecord.getByPos(0), std::exception, any_exception); - BOOST_CHECK_EXCEPTION(emptyRecord.getByPos(1), std::exception, any_exception); - BOOST_CHECK_EXCEPTION(emptyRecord.getByPos(2), std::exception, any_exception); + BOOST_CHECK_EXCEPTION(emptyRecord.get("x"), RuntimeErrorRef, any_exception); + BOOST_CHECK_EXCEPTION(emptyRecord.get("y"), RuntimeErrorRef, any_exception); + BOOST_CHECK_EXCEPTION(emptyRecord.get("z"), RuntimeErrorRef, any_exception); + BOOST_CHECK_EXCEPTION(emptyRecord.getByPos(0), RuntimeErrorRef, any_exception); + BOOST_CHECK_EXCEPTION(emptyRecord.getByPos(1), RuntimeErrorRef, any_exception); + BOOST_CHECK_EXCEPTION(emptyRecord.getByPos(2), RuntimeErrorRef, any_exception); // Then we actually check with a real set of inputs. std::vector<void*> inputs; @@ -97,13 +97,13 @@ BOOST_AUTO_TEST_CASE(TestInputRecord) auto ref00 = record.get("x"); auto ref10 = record.get("y"); auto ref20 = record.get("z"); - BOOST_CHECK_EXCEPTION(record.get("err"), std::exception, any_exception); + BOOST_CHECK_EXCEPTION(record.get("err"), RuntimeErrorRef, any_exception); // Or we can get it positionally BOOST_CHECK_NO_THROW(record.get("x")); auto ref01 = record.getByPos(0); auto ref11 = record.getByPos(1); - BOOST_CHECK_EXCEPTION(record.getByPos(10), std::exception, any_exception); + BOOST_CHECK_EXCEPTION(record.getByPos(10), RuntimeErrorRef, any_exception); // This should be exactly the same pointers BOOST_CHECK_EQUAL(ref00.header, ref01.header); @@ -114,6 +114,8 @@ BOOST_AUTO_TEST_CASE(TestInputRecord) BOOST_CHECK_EQUAL(record.isValid("x"), true); BOOST_CHECK_EQUAL(record.isValid("y"), true); BOOST_CHECK_EQUAL(record.isValid("z"), false); + BOOST_CHECK_EQUAL(record.size(), 3); + BOOST_CHECK_EQUAL(record.countValidInputs(), 2); BOOST_CHECK_EQUAL(record.isValid(0), true); BOOST_CHECK_EQUAL(record.isValid(1), true); diff --git a/Framework/Core/test/test_Kernels.cxx b/Framework/Core/test/test_Kernels.cxx index 5817e2a7b1b80..b7f48bcc82c9f 100644 --- a/Framework/Core/test/test_Kernels.cxx +++ b/Framework/Core/test/test_Kernels.cxx @@ -8,95 +8,72 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test Framework AlgorithmSpec +#define BOOST_TEST_MODULE Test Framework Kernels #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK -#include "Framework/AnalysisDataModel.h" #include "Framework/Kernels.h" #include "Framework/TableBuilder.h" -#include <arrow/compute/context.h> -#include <arrow/compute/kernels/hash.h> +#include "Framework/Pack.h" #include <boost/test/unit_test.hpp> using namespace o2::framework; using namespace arrow; using namespace arrow::compute; -BOOST_AUTO_TEST_CASE(TestHashByColumnKernel) +BOOST_AUTO_TEST_CASE(TestSlicing) { TableBuilder builder; - auto rowWriter = builder.persist<uint64_t, uint64_t>({"x", "y"}); - rowWriter(0, 0, 0); - rowWriter(0, 0, 1); - rowWriter(0, 0, 2); - rowWriter(0, 0, 3); + auto rowWriter = builder.persist<int32_t, int32_t>({"x", "y"}); + rowWriter(0, 1, 4); rowWriter(0, 1, 5); rowWriter(0, 1, 6); rowWriter(0, 1, 7); - rowWriter(0, 2, 8); + rowWriter(0, 2, 7); + rowWriter(0, 4, 8); + rowWriter(0, 5, 9); + rowWriter(0, 5, 10); auto table = builder.finalize(); - arrow::compute::FunctionContext ctx; - HashByColumnKernel kernel{{"x"}}; - std::shared_ptr<arrow::Array> out; - auto outDatum = arrow::compute::Datum(out); - BOOST_CHECK_EQUAL(kernel.Call(&ctx, arrow::compute::Datum(table), &outDatum).ok(), true); - auto indices = arrow::util::get<std::shared_ptr<arrow::ChunkedArray>>(outDatum.value); - BOOST_CHECK_EQUAL(indices->length(), table->num_rows()); + auto options = arrow::compute::CountOptions::Defaults(); + auto value_counts = arrow::compute::CallFunction("value_counts", {table->GetColumnByName("x")}, &options).ValueOrDie(); + auto array = static_cast<arrow::StructArray>(value_counts.array()); - std::shared_ptr<arrow::Array> uniqueValues; - BOOST_CHECK_EQUAL(Unique(&ctx, arrow::compute::Datum(indices), &uniqueValues).ok(), true); - BOOST_REQUIRE(uniqueValues.get() != nullptr); - BOOST_CHECK_EQUAL(uniqueValues->length(), 3); + auto arr0 = static_cast<NumericArray<Int32Type>>(array.field(0)->data()); + auto arr1 = static_cast<NumericArray<Int64Type>>(array.field(1)->data()); - arrow::compute::Datum outRanges; - SortedGroupByKernel<uint64_t, arrow::UInt64Array> groupBy{{"x", 3}}; - BOOST_CHECK_EQUAL(groupBy.Call(&ctx, arrow::compute::Datum(table), &outRanges).ok(), true); - auto result = arrow::util::get<std::shared_ptr<arrow::Table>>(outRanges.value); - BOOST_REQUIRE(result.get() != nullptr); - BOOST_CHECK_EQUAL(result->num_rows(), 3); + std::array<int, 4> v{1, 2, 4, 5}; + std::array<int, 4> c{4, 1, 1, 2}; - std::vector<Datum> splitted; - BOOST_CHECK_EQUAL(sliceByColumn(&ctx, "x", static_cast<uint64_t>(3), arrow::compute::Datum(table), &splitted).ok(), true); - BOOST_REQUIRE_EQUAL(splitted.size(), 3); - BOOST_CHECK_EQUAL(util::get<std::shared_ptr<Table>>(splitted[0].value)->num_rows(), 4); - BOOST_CHECK_EQUAL(util::get<std::shared_ptr<Table>>(splitted[1].value)->num_rows(), 4); - BOOST_CHECK_EQUAL(util::get<std::shared_ptr<Table>>(splitted[2].value)->num_rows(), 1); + for (auto i = 0; i < arr0.length(); ++i) { + BOOST_REQUIRE_EQUAL(arr0.Value(i), v[i]); + BOOST_REQUIRE_EQUAL(arr1.Value(i), c[i]); + } } -BOOST_AUTO_TEST_CASE(TestWithSOATables) +BOOST_AUTO_TEST_CASE(TestSlicingFramework) { - using namespace o2; - TableBuilder builder2; - auto tracksCursor = builder2.cursor<aod::StoredTracks>(); - tracksCursor(0, 0, 2, 3, 4, 5, 6, 7, 8, 9); - tracksCursor(0, 0, 2, 3, 4, 5, 6, 7, 8, 9); - tracksCursor(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - tracksCursor(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - tracksCursor(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - tracksCursor(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - tracksCursor(0, 2, 2, 3, 4, 5, 6, 7, 8, 9); - auto tracks = builder2.finalize(); + TableBuilder builder; + auto rowWriter = builder.persist<int32_t, int32_t>({"x", "y"}); - arrow::compute::FunctionContext ctx; - arrow::compute::Datum outRanges; - SortedGroupByKernel<int32_t, arrow::Int32Array> groupBy{{"fCollisionsID", 3}}; - BOOST_CHECK_EQUAL(groupBy.Call(&ctx, arrow::compute::Datum(tracks), &outRanges).ok(), true); - auto result = arrow::util::get<std::shared_ptr<arrow::Table>>(outRanges.value); - BOOST_REQUIRE(result.get() != nullptr); - BOOST_CHECK_EQUAL(result->num_rows(), 3); + rowWriter(0, 1, 4); + rowWriter(0, 1, 5); + rowWriter(0, 1, 6); + rowWriter(0, 1, 7); + rowWriter(0, 2, 7); + rowWriter(0, 4, 8); + rowWriter(0, 5, 9); + rowWriter(0, 5, 10); + auto table = builder.finalize(); - std::vector<Datum> splitted; std::vector<uint64_t> offsets; - BOOST_CHECK_EQUAL(sliceByColumn(&ctx, "fCollisionsID", static_cast<int32_t>(3), arrow::compute::Datum(tracks), &splitted, &offsets).ok(), true); - BOOST_REQUIRE_EQUAL(splitted.size(), 3); - BOOST_CHECK_EQUAL(util::get<std::shared_ptr<Table>>(splitted[0].value)->num_rows(), 2); - BOOST_CHECK_EQUAL(util::get<std::shared_ptr<Table>>(splitted[1].value)->num_rows(), 4); - BOOST_CHECK_EQUAL(util::get<std::shared_ptr<Table>>(splitted[2].value)->num_rows(), 1); - - BOOST_CHECK_EQUAL(offsets[0], 0); - BOOST_CHECK_EQUAL(offsets[1], 2); - BOOST_CHECK_EQUAL(offsets[2], 6); + std::vector<arrow::Datum> slices; + auto status = sliceByColumn<int32_t>("x", table, 12, &slices, &offsets); + BOOST_REQUIRE(status.ok()); + BOOST_REQUIRE_EQUAL(slices.size(), 12); + std::array<int, 12> sizes{0, 4, 1, 0, 1, 2, 0, 0, 0, 0, 0, 0}; + for (auto i = 0u; i < slices.size(); ++i) { + BOOST_REQUIRE_EQUAL(slices[i].table()->num_rows(), sizes[i]); + } } diff --git a/Framework/Core/test/test_RegionInfoCallbackService.cxx b/Framework/Core/test/test_RegionInfoCallbackService.cxx index a357583194840..571da32501584 100644 --- a/Framework/Core/test/test_RegionInfoCallbackService.cxx +++ b/Framework/Core/test/test_RegionInfoCallbackService.cxx @@ -34,6 +34,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) AlgorithmSpec{ [](ProcessingContext& ctx) { auto& out = ctx.outputs().make<int>(OutputRef{"test", 0}); + ctx.services().get<ControlService>().endOfStream(); + ctx.services().get<ControlService>().readyToQuit(QuitRequest::Me); }}}, {"dest", Inputs{ diff --git a/Framework/Core/test/test_Root2ArrowTable.cxx b/Framework/Core/test/test_Root2ArrowTable.cxx index fc338031dbdf8..346790dc76388 100644 --- a/Framework/Core/test/test_Root2ArrowTable.cxx +++ b/Framework/Core/test/test_Root2ArrowTable.cxx @@ -91,6 +91,8 @@ BOOST_AUTO_TEST_CASE(RootTree2Table) auto chunkToUse = table->column(0)->chunk(0); chunkToUse = std::dynamic_pointer_cast<arrow::FixedSizeListArray>(chunkToUse)->values(); auto array = std::static_pointer_cast<arrow::FloatArray>(chunkToUse); + // array of 3 floats, time 1000. + BOOST_REQUIRE_EQUAL(array->length(), 3000); const float* c = reinterpret_cast<float const*>(array->values()->data()); //auto array = std::static_pointer_cast<arrow::FixedSizeBinaryArray>(table->column(0)->chunk(0)); diff --git a/Framework/Core/test/test_Services.cxx b/Framework/Core/test/test_Services.cxx index 419464a4cba44..ccba588e4c665 100644 --- a/Framework/Core/test/test_Services.cxx +++ b/Framework/Core/test/test_Services.cxx @@ -11,9 +11,13 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Framework/ServiceHandle.h" #include "Framework/ServiceRegistry.h" #include "Framework/CallbackService.h" +#include "Framework/CommonServices.h" +#include <Framework/DeviceState.h> #include <boost/test/unit_test.hpp> +#include <options/FairMQProgOptions.h> #include <iostream> #include <memory> @@ -48,14 +52,17 @@ BOOST_AUTO_TEST_CASE(TestServiceRegistry) ConcreteA serviceA; ConcreteB serviceB; ConcreteC const serviceC; - registry.registerService<InterfaceA>(&serviceA); - registry.registerService<InterfaceB>(&serviceB); - registry.registerService<InterfaceC>(&serviceC); + registry.registerService(ServiceRegistryHelpers::handleForService<InterfaceA>(&serviceA)); + registry.registerService(ServiceRegistryHelpers::handleForService<InterfaceB>(&serviceB)); + registry.registerService(ServiceRegistryHelpers::handleForService<InterfaceC>(&serviceC)); BOOST_CHECK(registry.get<InterfaceA>().method() == true); BOOST_CHECK(registry.get<InterfaceB>().method() == false); BOOST_CHECK(registry.get<InterfaceC const>().method() == false); - BOOST_CHECK_THROW(registry.get<InterfaceA const>(), std::runtime_error); - BOOST_CHECK_THROW(registry.get<InterfaceC>(), std::runtime_error); + BOOST_CHECK(registry.active<InterfaceA>() == true); + BOOST_CHECK(registry.active<InterfaceB>() == true); + BOOST_CHECK(registry.active<InterfaceC>() == false); + BOOST_CHECK_THROW(registry.get<InterfaceA const>(), RuntimeErrorRef); + BOOST_CHECK_THROW(registry.get<InterfaceC>(), RuntimeErrorRef); } BOOST_AUTO_TEST_CASE(TestCallbackService) @@ -63,7 +70,7 @@ BOOST_AUTO_TEST_CASE(TestCallbackService) using namespace o2::framework; ServiceRegistry registry; auto service = std::make_unique<CallbackService>(); - registry.registerService<CallbackService>(service.get()); + registry.registerService(ServiceRegistryHelpers::handleForService<CallbackService>(service.get())); // the callback simply sets the captured variable to indicated that it was called bool cbCalled = false; @@ -71,9 +78,108 @@ BOOST_AUTO_TEST_CASE(TestCallbackService) registry.get<CallbackService>().set(CallbackService::Id::Stop, cb); // check to set with the wrong type - BOOST_CHECK_THROW(registry.get<CallbackService>().set(CallbackService::Id::Stop, [](int) {}), std::runtime_error); + BOOST_CHECK_THROW(registry.get<CallbackService>().set(CallbackService::Id::Stop, [](int) {}), RuntimeErrorRef); // execute and check registry.get<CallbackService>()(CallbackService::Id::Stop); BOOST_CHECK(cbCalled); } + +struct DummyService { + int threadId; +}; + +BOOST_AUTO_TEST_CASE(TestSerialServices) +{ + using namespace o2::framework; + ServiceRegistry registry; + + DummyService t0{0}; + /// We register it pretending to be on thread 0 + registry.registerService(TypeIdHelpers::uniqueId<DummyService>(), &t0, ServiceKind::Serial, 0); + + auto tt0 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 0, ServiceKind::Serial)); + auto tt1 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 1, ServiceKind::Serial)); + auto tt2 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 2, ServiceKind::Serial)); + BOOST_CHECK_EQUAL(tt0->threadId, 0); + BOOST_CHECK_EQUAL(tt1->threadId, 0); + BOOST_CHECK_EQUAL(tt2->threadId, 0); +} + +BOOST_AUTO_TEST_CASE(TestGlobalServices) +{ + using namespace o2::framework; + ServiceRegistry registry; + + DummyService t0{0}; + /// We register it pretending to be on thread 0 + registry.registerService(TypeIdHelpers::uniqueId<DummyService>(), &t0, ServiceKind::Global, 0); + + auto tt0 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 0, ServiceKind::Serial)); + auto tt1 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 1, ServiceKind::Serial)); + auto tt2 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 2, ServiceKind::Serial)); + BOOST_CHECK_EQUAL(tt0->threadId, 0); + BOOST_CHECK_EQUAL(tt1->threadId, 0); + BOOST_CHECK_EQUAL(tt2->threadId, 0); +} + +BOOST_AUTO_TEST_CASE(TestGlobalServices02) +{ + using namespace o2::framework; + ServiceRegistry registry; + + DummyService t0{1}; + /// We register it pretending to be on thread 0 + registry.registerService(TypeIdHelpers::uniqueId<DummyService>(), &t0, ServiceKind::Global, 1); + + auto tt0 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 0, ServiceKind::Global)); + auto tt1 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 1, ServiceKind::Global)); + auto tt2 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 2, ServiceKind::Global)); + BOOST_CHECK_EQUAL(tt0->threadId, 1); + BOOST_CHECK_EQUAL(tt1->threadId, 1); + BOOST_CHECK_EQUAL(tt2->threadId, 1); +} + +BOOST_AUTO_TEST_CASE(TestStreamServices) +{ + using namespace o2::framework; + ServiceRegistry registry; + + DummyService t0{0}; + DummyService t1{1}; + DummyService t2{2}; + /// We register it pretending to be on thread 0 + registry.registerService(TypeIdHelpers::uniqueId<DummyService>(), &t0, ServiceKind::Stream, 0); + registry.registerService(TypeIdHelpers::uniqueId<DummyService>(), &t1, ServiceKind::Stream, 1); + registry.registerService(TypeIdHelpers::uniqueId<DummyService>(), &t2, ServiceKind::Stream, 2); + + auto tt0 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 0, ServiceKind::Stream)); + auto tt1 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 1, ServiceKind::Stream)); + auto tt2 = reinterpret_cast<DummyService*>(registry.get(TypeIdHelpers::uniqueId<DummyService>(), 2, ServiceKind::Stream)); + BOOST_CHECK_EQUAL(tt0->threadId, 0); + BOOST_CHECK_EQUAL(tt1->threadId, 1); + BOOST_CHECK_EQUAL(tt2->threadId, 2); +} + +BOOST_AUTO_TEST_CASE(TestServiceRegistryCtor) +{ + using namespace o2::framework; + ServiceRegistry registry; + registry = ServiceRegistry(); +} + +BOOST_AUTO_TEST_CASE(TestServiceDeclaration) +{ + using namespace o2::framework; + ServiceRegistry registry; + DeviceState state; + FairMQProgOptions options; + options.SetProperty("monitoring-backend", "no-op://"); + options.SetProperty("infologger-mode", "no-op://"); + options.SetProperty("infologger-severity", "info"); + options.SetProperty("configuration", "command-line"); + + registry.declareService(CommonServices::callbacksSpec(), state, options); + BOOST_CHECK(registry.active<CallbackService>() == true); + BOOST_CHECK(registry.active<DummyService>() == false); +} diff --git a/Framework/Core/test/test_SimpleDataProcessingDevice01.cxx b/Framework/Core/test/test_SimpleDataProcessingDevice01.cxx index a45b048e52757..5b485bb186be4 100644 --- a/Framework/Core/test/test_SimpleDataProcessingDevice01.cxx +++ b/Framework/Core/test/test_SimpleDataProcessingDevice01.cxx @@ -14,7 +14,6 @@ #include "Framework/RawDeviceService.h" #include "Framework/runDataProcessing.h" #include <Monitoring/Monitoring.h> -#include <FairMQDevice.h> using namespace o2::framework; @@ -40,7 +39,7 @@ std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext const&) {OutputSpec{"TPC", "CLUSTERS"}, OutputSpec{"ITS", "CLUSTERS"}}, adaptStateless([](DataAllocator& outputs, ControlService& control, RawDeviceService& service) { - service.device()->WaitFor(std::chrono::milliseconds(1000)); + service.waitFor(1000); // Creates a new message of size 1000 which // has "TPC" as data origin and "CLUSTERS" as data description. auto& tpcClusters = outputs.make<FakeCluster>(Output{"TPC", "CLUSTERS", 0}, 1000); diff --git a/Framework/Core/test/test_SimpleRDataFrameProcessing.cxx b/Framework/Core/test/test_SimpleRDataFrameProcessing.cxx index 472844f972eb7..6af1a87b4d114 100644 --- a/Framework/Core/test/test_SimpleRDataFrameProcessing.cxx +++ b/Framework/Core/test/test_SimpleRDataFrameProcessing.cxx @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #include "Framework/DataRefUtils.h" #include "Framework/AlgorithmSpec.h" +#include "Framework/DataAllocator.h" #include "Framework/ServiceRegistry.h" #include "Framework/runDataProcessing.h" #include "Framework/ControlService.h" diff --git a/Framework/Core/test/test_SimpleWildcard.cxx b/Framework/Core/test/test_SimpleWildcard.cxx index c5647b35454a4..a5101e8f87478 100644 --- a/Framework/Core/test/test_SimpleWildcard.cxx +++ b/Framework/Core/test/test_SimpleWildcard.cxx @@ -17,6 +17,7 @@ #include <algorithm> #include <memory> #include <unordered_map> +#include <TObjString.h> using namespace o2::framework; diff --git a/Framework/Core/test/test_SimpleWildcard02.cxx b/Framework/Core/test/test_SimpleWildcard02.cxx index 17d890a78df45..bd4acbc48b08f 100644 --- a/Framework/Core/test/test_SimpleWildcard02.cxx +++ b/Framework/Core/test/test_SimpleWildcard02.cxx @@ -17,6 +17,7 @@ #include <algorithm> #include <memory> #include <unordered_map> +#include <TObjString.h> using namespace o2::framework; diff --git a/Framework/Core/test/test_StaggeringWorkflow.cxx b/Framework/Core/test/test_StaggeringWorkflow.cxx index 126551647d2d5..f124d099dc4ba 100644 --- a/Framework/Core/test/test_StaggeringWorkflow.cxx +++ b/Framework/Core/test/test_StaggeringWorkflow.cxx @@ -24,8 +24,6 @@ #include "Framework/DispatchPolicy.h" #include "Framework/DeviceSpec.h" #include "Framework/Output.h" -#include <FairMQDevice.h> -#include <chrono> #include <cstring> #include <iostream> #include <regex> @@ -88,9 +86,9 @@ std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext const&) // because of the CompletionPolicy trigger matcher. This message will be // sent together with the second message. outputs.snapshot(Output{"PROD", "CHANNEL", subspec, Lifetime::Timeframe}, subspec); - device.device()->WaitFor(std::chrono::milliseconds(100)); + device.waitFor(100); outputs.snapshot(Output{"PROD", "TRIGGER", subspec, Lifetime::Timeframe}, subspec); - device.device()->WaitFor(std::chrono::milliseconds(100)); + device.waitFor(100); } control.endOfStream(); control.readyToQuit(QuitRequest::Me); diff --git a/Framework/Core/test/test_StringHelpers.cxx b/Framework/Core/test/test_StringHelpers.cxx index ee03e585f46ca..d5500f66d7e35 100644 --- a/Framework/Core/test/test_StringHelpers.cxx +++ b/Framework/Core/test/test_StringHelpers.cxx @@ -11,8 +11,9 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK -#include "Framework/StringHelpers.h" #include <boost/test/unit_test.hpp> +#include "Framework/StringHelpers.h" +#include <iostream> BOOST_AUTO_TEST_CASE(StringHelpersHash) { @@ -22,3 +23,47 @@ BOOST_AUTO_TEST_CASE(StringHelpersHash) BOOST_CHECK_EQUAL(compile_time_hash(cs), compile_time_hash("test-string")); BOOST_CHECK_EQUAL(compile_time_hash(s.c_str()), compile_time_hash(cs)); } + +template <typename T> +void printString(const T& constStr) +{ + static_assert(is_const_str<T>::value, "This function can only print compile-time strings!"); + + std::cout << "ConstStr:" << std::endl; + std::cout << "str -> " << constStr.str << std::endl; + std::cout << "hash -> " << constStr.hash << std::endl; + std::cout << "idx -> " << constStr.idx << std::endl; +}; + +BOOST_AUTO_TEST_CASE(StringHelpersConstStr) +{ + printString(CONST_STR("this/is/a/histogram")); + + auto myConstStr = CONST_STR("helloWorld"); + printString(myConstStr); + static_assert(std::is_same_v<decltype(myConstStr), ConstStr<'h', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'>>); + static_assert(myConstStr.hash == (uint32_t)942280617); + BOOST_CHECK_EQUAL(myConstStr.hash, compile_time_hash("helloWorld")); + + if constexpr (is_const_str_v(myConstStr)) { + std::cout << "myConstStr is a compile-time string" << std::endl; + } + + auto myConstStr2 = CONST_STR("hello") + CONST_STR("Universe"); + printString(myConstStr2); + static_assert(std::is_same_v<decltype(myConstStr2), ConstStr<'h', 'e', 'l', 'l', 'o', 'U', 'n', 'i', 'v', 'e', 'r', 's', 'e'>>); + + enum ParticleSpecies { + kPion, + kKaon + }; + static constexpr std::string_view hist[] = {"ptDist", "etaDist"}; + static constexpr std::string_view particleSuffix[] = {"_pions", "_kaons"}; + + printString(CONST_STR(hist[0]) + CONST_STR(particleSuffix[kPion])); + printString(CONST_STR(hist[0]) + CONST_STR(particleSuffix[kKaon])); + printString(CONST_STR(hist[1]) + CONST_STR(particleSuffix[kPion])); + printString(CONST_STR(hist[1]) + CONST_STR(particleSuffix[kKaon])); + + BOOST_CHECK_EQUAL(CONST_STR(hist[0]).hash, CONST_STR("ptDist").hash); +} diff --git a/Framework/Core/test/test_TMessageSerializer.cxx b/Framework/Core/test/test_TMessageSerializer.cxx index bf75bb814e9eb..bea66936c3452 100644 --- a/Framework/Core/test/test_TMessageSerializer.cxx +++ b/Framework/Core/test/test_TMessageSerializer.cxx @@ -45,15 +45,22 @@ BOOST_AUTO_TEST_CASE(TestTMessageSerializer) BOOST_CHECK_EQUAL(named->GetTitle(), testtitle); // test deserialization with a wrong target class and check the exception - try { - auto out2 = TMessageSerializer::deserialize<TNamed>(buf); - BOOST_FAIL("here we should never get, the function call must fail with exception"); - } catch (std::exception& e) { - std::string expected("can not convert serialized class TObjArray into target class TNamed"); - BOOST_CHECK_MESSAGE(expected == e.what(), e.what()); - } + BOOST_CHECK_EXCEPTION(TMessageSerializer::deserialize<TNamed>(buf), + RuntimeErrorRef, + [](RuntimeErrorRef const& ref) { + auto& e = error_from_ref(ref); + std::string expected("can not convert serialized class TObjArray into target class TNamed"); + return expected == e.what; + }); } +bool check_expected(RuntimeErrorRef const& ref) +{ + auto& e = error_from_ref(ref); + std::string expected("can not convert serialized class vector<o2::test::Polymorphic> into target class TObject"); + return expected == e.what; +}; + BOOST_AUTO_TEST_CASE(TestTMessageSerializer_NonTObject) { using namespace o2::framework; @@ -73,13 +80,7 @@ BOOST_AUTO_TEST_CASE(TestTMessageSerializer_NonTObject) BOOST_CHECK((*out.get())[1] == o2::test::Polymorphic(0xd00f)); // test deserialization with a wrong target class and check the exception - try { - auto out2 = TMessageSerializer::deserialize(as_span(msg)); - BOOST_FAIL("here we should never get, the function call must fail with exception"); - } catch (std::exception& e) { - std::string expected("can not convert serialized class vector<o2::test::Polymorphic> into target class TObject"); - BOOST_CHECK_MESSAGE(expected == e.what(), e.what()); - } + BOOST_CHECK_EXCEPTION(TMessageSerializer::deserialize(as_span(msg)), RuntimeErrorRef, check_expected); } BOOST_AUTO_TEST_CASE(TestTMessageSerializer_InvalidBuffer) @@ -97,12 +98,12 @@ BOOST_AUTO_TEST_CASE(TestTMessageSerializer_InvalidBuffer) } */ // test deserialization of invalid target class and check the exception - try { - struct Dummy { - }; - auto out = TMessageSerializer::deserialize<Dummy>((o2::byte*)buffer, strlen(buffer)); - BOOST_ERROR("here we should never get, the function call must fail with exception"); - } catch (std::exception& e) { - BOOST_CHECK_MESSAGE(std::string(e.what()).find("class is not ROOT-serializable") == 0, e.what()); - } + struct Dummy { + }; + BOOST_CHECK_EXCEPTION(TMessageSerializer::deserialize<Dummy>((o2::byte*)buffer, strlen(buffer)), + RuntimeErrorRef, + [](RuntimeErrorRef const& ref) { + auto& err = error_from_ref(ref); + return strcmp(err.what, "class is not ROOT-serializable") != 0; + }); } diff --git a/Framework/Core/test/test_TableBuilder.cxx b/Framework/Core/test/test_TableBuilder.cxx index 9bee17e6f11ed..018847df84641 100644 --- a/Framework/Core/test/test_TableBuilder.cxx +++ b/Framework/Core/test/test_TableBuilder.cxx @@ -345,10 +345,8 @@ BOOST_AUTO_TEST_CASE(TestSoAIntegration) BOOST_AUTO_TEST_CASE(TestDataAllocatorReturnType) { - TimingInfo* timingInfo = nullptr; - ContextRegistry* contextes = nullptr; std::vector<OutputRoute> routes; - DataAllocator allocator(timingInfo, contextes, routes); + DataAllocator allocator(nullptr, nullptr, routes); const Output output{"TST", "DUMMY", 0, Lifetime::Timeframe}; // we require reference to object owned by allocator context static_assert(std::is_lvalue_reference<decltype(allocator.make<TableBuilder>(output))>::value); diff --git a/Framework/Core/test/test_Task.cxx b/Framework/Core/test/test_Task.cxx index 90e2ee15b9c7f..379c56960a614 100644 --- a/Framework/Core/test/test_Task.cxx +++ b/Framework/Core/test/test_Task.cxx @@ -10,8 +10,7 @@ #include "Framework/runDataProcessing.h" #include "Framework/Task.h" #include "Framework/ControlService.h" -#include <Monitoring/Monitoring.h> -#include "Framework/ControlService.h" +#include "Framework/Monitoring.h" #define ASSERT_ERROR(condition) \ if ((condition) == false) { \ diff --git a/Framework/Core/test/test_TimeParallelPipelining.cxx b/Framework/Core/test/test_TimeParallelPipelining.cxx index 07b4113f5faa9..6f9123e5d59db 100644 --- a/Framework/Core/test/test_TimeParallelPipelining.cxx +++ b/Framework/Core/test/test_TimeParallelPipelining.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Mocking.h" #include <boost/test/unit_test.hpp> #include "../src/DeviceSpecHelpers.h" #include "../src/SimpleResourceManager.h" @@ -52,7 +53,8 @@ BOOST_AUTO_TEST_CASE(TimePipeliningSimple) { auto workflow = defineSimplePipelining(); std::vector<DeviceSpec> devices; - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<ComputingResource> resources = {ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); @@ -104,7 +106,8 @@ BOOST_AUTO_TEST_CASE(TimePipeliningFull) { auto workflow = defineDataProcessing(); std::vector<DeviceSpec> devices; - auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(); + auto configContext = makeEmptyConfigContext(); + auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext); auto completionPolicies = CompletionPolicy::createDefaultPolicies(); std::vector<ComputingResource> resources = {ComputingResourceHelpers::getLocalhostResource()}; SimpleResourceManager rm(resources); diff --git a/Framework/Core/test/test_TreeToTable.cxx b/Framework/Core/test/test_TreeToTable.cxx index 7961ef6ba7acf..de5eb1048eba4 100644 --- a/Framework/Core/test/test_TreeToTable.cxx +++ b/Framework/Core/test/test_TreeToTable.cxx @@ -74,13 +74,15 @@ BOOST_AUTO_TEST_CASE(TreeToTableConversion) t1.Write(); // Create an arrow table from this. - TreeToTable tr2ta(&t1); - auto stat = tr2ta.addAllColumns(); + TreeToTable tr2ta; + auto stat = tr2ta.addAllColumns(&t1); if (!stat) { LOG(ERROR) << "Table was not created!"; return; } - auto table = tr2ta.process(); + + tr2ta.fill(&t1); + auto table = tr2ta.finalize(); f1.Close(); // test result diff --git a/Framework/Core/test/test_Variants.cxx b/Framework/Core/test/test_Variants.cxx index 5950a75b3a299..b2a85045c5e10 100644 --- a/Framework/Core/test/test_Variants.cxx +++ b/Framework/Core/test/test_Variants.cxx @@ -13,15 +13,39 @@ #include <boost/test/unit_test.hpp> #include "Framework/Variant.h" -#include <stdexcept> #include <sstream> #include <cstring> using namespace o2::framework; -bool unknown_type(std::runtime_error const& ex) +bool unknown_type(RuntimeErrorRef const& ref) { - return strcmp(ex.what(), "Mismatch between types") == 0; + auto const& err = error_from_ref(ref); + return strcmp(err.what, "Mismatch between types") == 0; +} + +BOOST_AUTO_TEST_CASE(MatrixTest) +{ + float m[3][4] = {{0.1, 0.2, 0.3, 0.4}, {0.5, 0.6, 0.7, 0.8}, {0.9, 1.0, 1.1, 1.2}}; + Array2D mm(&m[0][0], 3, 4); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mm(i, j) == m[i][j]); + } + } + std::vector<float> v = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2}; + Array2D mv(v, 3, 4); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mm(i, j) == v[i * 4 + j]); + } + } + for (auto i = 0U; i < 3; ++i) { + auto const& vv = mm[i]; + for (auto j = 0u; j < 4; ++j) { + BOOST_CHECK(vv[j] == mm(i, j)); + } + } } BOOST_AUTO_TEST_CASE(VariantTest) @@ -36,7 +60,7 @@ BOOST_AUTO_TEST_CASE(VariantTest) Variant c(10.2); BOOST_CHECK(c.get<double>() == 10.2); ss << c; - BOOST_CHECK_EXCEPTION(a.get<char*>(), std::runtime_error, unknown_type); + BOOST_CHECK_EXCEPTION(a.get<char*>(), RuntimeErrorRef, unknown_type); Variant d("foo"); ss << d; BOOST_CHECK(std::string(d.get<const char*>()) == "foo"); @@ -55,10 +79,107 @@ BOOST_AUTO_TEST_CASE(VariantTest) Variant sa("foo"); Variant sb(sa); // Copy constructor Variant sc(std::move(sa)); // Move constructor - Variant sd = sc; // Copy operator + Variant sd = sc; // Copy assignment BOOST_CHECK(std::string(sb.get<const char*>()) == "foo"); BOOST_CHECK(std::string(sc.get<const char*>()) == "foo"); BOOST_CHECK(std::string(sd.get<const char*>()) == "foo"); BOOST_CHECK(sa.get<const char*>() == nullptr); + + int iarr[] = {1, 2, 3, 4, 5}; + float farr[] = {0.2, 0.3, 123.123, 123.123, 3.005e-5, 1.1e6}; + std::vector<double> dvec = {0.1, 0.2, 0.4, 0.9, 1.3, 14.5, 123.234, 1.213e-20}; + Variant viarr(iarr, 5); + Variant vfarr(farr, 6); + Variant vdvec(dvec); + + BOOST_CHECK(viarr.size() == 5); + BOOST_CHECK(viarr.get<int*>() != iarr); + for (auto i = 0U; i < viarr.size(); ++i) { + BOOST_CHECK(iarr[i] == (viarr.get<int*>())[i]); + } + + BOOST_CHECK(vfarr.size() == 6); + BOOST_CHECK(vfarr.get<float*>() != farr); + for (auto i = 0U; i < vfarr.size(); ++i) { + BOOST_CHECK(farr[i] == (vfarr.get<float*>())[i]); + } + + BOOST_CHECK(vdvec.size() == dvec.size()); + BOOST_CHECK(vdvec.get<double*>() != dvec.data()); + for (auto i = 0U; i < dvec.size(); ++i) { + BOOST_CHECK(dvec[i] == (vdvec.get<double*>())[i]); + } + + Variant fb(vfarr); // Copy constructor + Variant fc(std::move(vfarr)); // Move constructor + Variant fd = fc; // Copy assignment + + BOOST_CHECK(vfarr.get<float*>() == nullptr); + + BOOST_CHECK(fb.get<float*>() != farr); + for (auto i = 0U; i < fb.size(); ++i) { + BOOST_CHECK(farr[i] == (fb.get<float*>())[i]); + } + BOOST_CHECK(fc.get<float*>() != farr); + for (auto i = 0U; i < fc.size(); ++i) { + BOOST_CHECK(farr[i] == (fc.get<float*>())[i]); + } + BOOST_CHECK(fd.get<float*>() != farr); + for (auto i = 0U; i < fd.size(); ++i) { + BOOST_CHECK(farr[i] == (fd.get<float*>())[i]); + } + + std::vector<std::string> vstrings{"s1", "s2", "s3"}; + std::string strings[] = {"l1", "l2", "l3"}; + Variant vstr(strings, 3); + Variant vvstr(vstrings); + + BOOST_CHECK(vstr.size() == 3); + BOOST_CHECK(vvstr.size() == 3); + for (auto i = 0U; i < vstr.size(); ++i) { + BOOST_CHECK(strings[i] == (vstr.get<std::string*>())[i]); + } + for (auto i = 0U; i < vvstr.size(); ++i) { + BOOST_CHECK(vstrings[i] == (vvstr.get<std::string*>())[i]); + } + + Variant vsc(vstr); // Copy constructor + Variant vsm(std::move(vstr)); // Move constructor + Variant vscc = vsm; // Copy assignment + for (auto i = 0U; i < vsm.size(); ++i) { + BOOST_CHECK(strings[i] == (vsm.get<std::string*>())[i]); + } + for (auto i = 0U; i < vscc.size(); ++i) { + BOOST_CHECK(strings[i] == (vscc.get<std::string*>())[i]); + } + + float m[3][4] = {{0.1, 0.2, 0.3, 0.4}, {0.5, 0.6, 0.7, 0.8}, {0.9, 1.0, 1.1, 1.2}}; + Array2D mm(&m[0][0], 3, 4); + Variant vmm(mm); + auto const& mmc = vmm.get<Array2D<float>>(); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mmc(i, j) == mm(i, j)); + } + } + + Variant vmmc(vmm); // Copy constructor + Variant vmmm(std::move(vmm)); // Move constructor + Variant vmma = vmmm; // Copy assignment + auto const& mmc2 = vmmc.get<Array2D<float>>(); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mmc2(i, j) == mm(i, j)); + } + } + auto const& mmc3 = vmma.get<Array2D<float>>(); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mmc3(i, j) == mm(i, j)); + } + } + std::stringstream ssm; + ssm << vmma; + BOOST_CHECK(ssm.str() == "f[[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8], [0.9, 1, 1.1, 1.2]]"); } diff --git a/Framework/Core/test/test_WorkflowHelpers.cxx b/Framework/Core/test/test_WorkflowHelpers.cxx index 0bc40a06da78f..174d915b0c8c9 100644 --- a/Framework/Core/test/test_WorkflowHelpers.cxx +++ b/Framework/Core/test/test_WorkflowHelpers.cxx @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "Mocking.h" #include "test_HelperMacros.h" #include "Framework/ConfigContext.h" #include "Framework/WorkflowSpec.h" @@ -24,36 +25,22 @@ using namespace o2::framework; -std::unique_ptr<ConfigContext> makeEmptyConfigContext() -{ - // FIXME: Ugly... We need to fix ownership and make sure the ConfigContext - // either owns or shares ownership of the registry. - std::vector<std::unique_ptr<ParamRetriever>> retrievers; - static std::vector<ConfigParamSpec> specs; - auto store = std::make_unique<ConfigParamStore>(specs, std::move(retrievers)); - store->preload(); - store->activate(); - static ConfigParamRegistry registry(std::move(store)); - auto context = std::make_unique<ConfigContext>(registry, 0, nullptr); - return context; -} - BOOST_AUTO_TEST_CASE(TestVerifyWorkflow) { using namespace o2::framework; auto checkIncompleteInput = [](WorkflowSpec const& workflow) { // Empty workflows should be invalid. - BOOST_CHECK_THROW(WorkflowHelpers::verifyWorkflow(workflow), std::runtime_error); + BOOST_CHECK_THROW((void)WorkflowHelpers::verifyWorkflow(workflow), std::runtime_error); }; auto checkOk = [](WorkflowSpec const& workflow) { // Empty workflows should be invalid. - BOOST_CHECK_NO_THROW(WorkflowHelpers::verifyWorkflow(workflow)); + BOOST_CHECK_NO_THROW((void)WorkflowHelpers::verifyWorkflow(workflow)); }; auto checkNotOk = [](WorkflowSpec const& workflow) { // Empty workflows should be invalid. - BOOST_CHECK_THROW(WorkflowHelpers::verifyWorkflow(workflow), std::runtime_error); + BOOST_CHECK_THROW((void)WorkflowHelpers::verifyWorkflow(workflow), std::runtime_error); }; // A non fully specified input is an error, given the result is ambiguous. @@ -222,7 +209,8 @@ BOOST_AUTO_TEST_CASE(TestSimpleConnection) std::vector<OutputSpec> outputs; std::vector<LogicalForwardInfo> availableForwardsInfo; - WorkflowHelpers::verifyWorkflow(workflow); + auto result = WorkflowHelpers::verifyWorkflow(workflow); + BOOST_REQUIRE(result == WorkflowParsingState::Valid); auto context = makeEmptyConfigContext(); WorkflowHelpers::injectServiceDevices(workflow, *context); BOOST_CHECK_EQUAL(workflow.size(), 3); @@ -263,7 +251,7 @@ BOOST_AUTO_TEST_CASE(TestSimpleForward) std::vector<DeviceConnectionEdge> logicalEdges; std::vector<OutputSpec> outputs; std::vector<LogicalForwardInfo> availableForwardsInfo; - WorkflowHelpers::verifyWorkflow(workflow); + BOOST_REQUIRE(WorkflowHelpers::verifyWorkflow(workflow) == WorkflowParsingState::Valid); auto context = makeEmptyConfigContext(); WorkflowHelpers::injectServiceDevices(workflow, *context); WorkflowHelpers::constructGraph(workflow, logicalEdges, @@ -320,7 +308,7 @@ BOOST_AUTO_TEST_CASE(TestGraphConstruction) // channels, so that we can construct them before assigning to a device. std::vector<OutputSpec> outputs; - WorkflowHelpers::verifyWorkflow(workflow); + BOOST_REQUIRE(WorkflowHelpers::verifyWorkflow(workflow) == WorkflowParsingState::Valid); auto context = makeEmptyConfigContext(); WorkflowHelpers::injectServiceDevices(workflow, *context); WorkflowHelpers::constructGraph(workflow, logicalEdges, @@ -439,7 +427,7 @@ BOOST_AUTO_TEST_CASE(TestExternalInput) InputSpec{"external", "TST", "A", 0, Lifetime::Timer}}, Outputs{ OutputSpec{"TST", "B"}}}}; - WorkflowHelpers::verifyWorkflow(workflow); + BOOST_REQUIRE(WorkflowHelpers::verifyWorkflow(workflow) == WorkflowParsingState::Valid); std::vector<DeviceConnectionEdge> logicalEdges; std::vector<OutputSpec> outputs; std::vector<LogicalForwardInfo> availableForwardsInfo; @@ -526,7 +514,7 @@ BOOST_AUTO_TEST_CASE(TestOriginWildcard) std::vector<OutputSpec> outputs; std::vector<LogicalForwardInfo> availableForwardsInfo; - WorkflowHelpers::verifyWorkflow(workflow); + BOOST_REQUIRE(WorkflowHelpers::verifyWorkflow(workflow) == WorkflowParsingState::Valid); auto context = makeEmptyConfigContext(); WorkflowHelpers::injectServiceDevices(workflow, *context); BOOST_CHECK_EQUAL(workflow.size(), 3); diff --git a/Framework/Core/test/test_WorkflowSerialization.cxx b/Framework/Core/test/test_WorkflowSerialization.cxx index d9868e9bcccde..4618b5e28813c 100644 --- a/Framework/Core/test/test_WorkflowSerialization.cxx +++ b/Framework/Core/test/test_WorkflowSerialization.cxx @@ -20,31 +20,35 @@ using namespace o2::framework; BOOST_AUTO_TEST_CASE(TestVerifyWorkflow) { using namespace o2::framework; - WorkflowSpec w0{ - DataProcessorSpec{"A", - {InputSpec{"foo", "A", "COLLISIONCONTEXT", 1, Lifetime::Condition}}, - {OutputSpec{{"bar"}, "C", "D", 2, Lifetime::Timeframe}}, - AlgorithmSpec{[](ProcessingContext& ctx) {}}, - {ConfigParamSpec{"aInt", VariantType::Int, 0, {"An Int"}}, - ConfigParamSpec{"aFloat", VariantType::Float, 1.3, {"A Float"}}, - ConfigParamSpec{"aBool", VariantType::Bool, true, {"A Bool"}}, - ConfigParamSpec{"aString", VariantType::String, "some string", {"A String"}}}}, - DataProcessorSpec{"B", - {InputSpec{"foo", "C", "D"}}, - {OutputSpec{{"bar1"}, "E", "F", 0}, - OutputSpec{{"bar2"}, "E", "F", 1}}, - AlgorithmSpec{[](ProcessingContext& ctx) {}}, - {}}, - DataProcessorSpec{"C", - {}, - {OutputSpec{{"bar"}, "G", "H"}}, - AlgorithmSpec{[](ProcessingContext& ctx) {}}, - {}}, - DataProcessorSpec{"D", - {InputSpec{"foo", {"C", "D"}}}, - {OutputSpec{{"bar"}, {"I", "L"}}}, - AlgorithmSpec{[](ProcessingContext& ctx) {}}, - {}}}; + WorkflowSpec w0{ // + DataProcessorSpec{"A", // + {InputSpec{"foo", "A", "COLLISIONCONTEXT", 1, Lifetime::Condition, { + ConfigParamSpec{"aUrl", VariantType::String, "foo/bar", {"A InputSpec option"}}, // + ConfigParamSpec{"bUrl", VariantType::String, "foo/foo", {"Another InputSpec option"}}, // + }}}, // + {OutputSpec{{"bar"}, "C", "D", 2, Lifetime::Timeframe}}, // + AlgorithmSpec{[](ProcessingContext& ctx) {}}, // + { // + ConfigParamSpec{"aInt", VariantType::Int, 0, {"An Int"}}, // + ConfigParamSpec{"aFloat", VariantType::Float, 1.3, {"A Float"}}, // + ConfigParamSpec{"aBool", VariantType::Bool, true, {"A Bool"}}, // + ConfigParamSpec{"aString", VariantType::String, "some string", {"A String"}}}}, // // + DataProcessorSpec{"B", // + {InputSpec{"foo", "C", "D"}}, // + { // + OutputSpec{{"bar1"}, "E", "F", 0}, // + OutputSpec{{"bar2"}, "E", "F", 1}}, // + AlgorithmSpec{[](ProcessingContext& ctx) {}}, // + {}}, // + DataProcessorSpec{"C", {}, // + { // + OutputSpec{{"bar"}, "G", "H"}}, // + AlgorithmSpec{[](ProcessingContext& ctx) {}}, // + {}}, // + DataProcessorSpec{"D", {InputSpec{"foo", {"C", "D"}}}, // + {OutputSpec{{"bar"}, {"I", "L"}}}, // + AlgorithmSpec{[](ProcessingContext& ctx) {}}, // + {}}}; // std::vector<DataProcessorInfo> metadataOut{ {"A", "test_Framework_test_SerializationWorkflow", {"foo"}, {ConfigParamSpec{"aBool", VariantType::Bool, true, {"A Bool"}}}}, diff --git a/Framework/Core/test/unittest_DataSpecUtils.cxx b/Framework/Core/test/unittest_DataSpecUtils.cxx index 9a394ba481b35..f35c214a149a1 100644 --- a/Framework/Core/test/unittest_DataSpecUtils.cxx +++ b/Framework/Core/test/unittest_DataSpecUtils.cxx @@ -125,7 +125,11 @@ BOOST_AUTO_TEST_CASE(MatchingInputs) BOOST_CHECK(DataSpecUtils::match(matchingInput2, concreteExample3) == false); BOOST_CHECK(DataSpecUtils::match(matchingInput2, concreteExample4) == false); - BOOST_CHECK_THROW(DataSpecUtils::asConcreteDataMatcher(matchingInput2), std::bad_variant_access); + BOOST_CHECK_THROW(DataSpecUtils::asConcreteDataMatcher(matchingInput2), std::runtime_error); + auto concrete2 = DataSpecUtils::asConcreteDataMatcher(matchingInput1); + BOOST_CHECK_EQUAL(concrete.origin.as<std::string>(), "TEST"); + BOOST_CHECK_EQUAL(concrete.description.as<std::string>(), "FOOO"); + BOOST_CHECK_EQUAL(concrete.subSpec, 1); } BOOST_AUTO_TEST_CASE(MatchingOutputs) @@ -201,6 +205,12 @@ BOOST_AUTO_TEST_CASE(PartialMatching) BOOST_CHECK(DataSpecUtils::partialMatch(fullySpecifiedOutput, header::DataOrigin("FOO")) == false); BOOST_CHECK(DataSpecUtils::partialMatch(fullySpecifiedInput, header::DataOrigin("FOO")) == false); + + BOOST_CHECK(DataSpecUtils::partialMatch(fullySpecifiedOutput, header::DataDescription("TEST")) == false); + BOOST_CHECK(DataSpecUtils::partialMatch(fullySpecifiedInput, header::DataDescription("TSET")) == false); + + BOOST_CHECK(DataSpecUtils::partialMatch(fullySpecifiedOutput, header::DataDescription("FOOO")) == true); + BOOST_CHECK(DataSpecUtils::partialMatch(fullySpecifiedInput, header::DataDescription("FOOO")) == true); } BOOST_AUTO_TEST_CASE(GetOptionalSubSpecWithMatcher) @@ -236,6 +246,16 @@ BOOST_AUTO_TEST_CASE(GetOptionalSubSpecWithMatcher) BOOST_CHECK_EQUAL(std::string(dataType2.description.as<std::string>()), "FOOO"); } +BOOST_AUTO_TEST_CASE(TestMatcherFromDescription) +{ + auto fromQueryInputSpec = DataSpecUtils::dataDescriptorMatcherFrom(header::DataDescription{"TSET"}); + InputSpec ddSpec{ + "binding", + std::move(fromQueryInputSpec)}; + + BOOST_CHECK_EQUAL(DataSpecUtils::asConcreteDataDescription(ddSpec).as<std::string>(), "TSET"); +} + BOOST_AUTO_TEST_CASE(FindOutputSpec) { std::vector<OutputSpec> specs = { @@ -282,5 +302,29 @@ BOOST_AUTO_TEST_CASE(GettingConcreteMembers) BOOST_REQUIRE_EQUAL(justOriginInputSpec.size(), 1); BOOST_CHECK_EQUAL(DataSpecUtils::asConcreteOrigin(justOriginInputSpec.at(0)).as<std::string>(), "TST"); - BOOST_CHECK_THROW(DataSpecUtils::asConcreteDataDescription(justOriginInputSpec.at(0)).as<std::string>(), std::runtime_error); -} \ No newline at end of file + BOOST_CHECK_THROW(DataSpecUtils::asConcreteDataDescription(justOriginInputSpec.at(0)).as<std::string>(), RuntimeErrorRef); +} + +BOOST_AUTO_TEST_CASE(Includes) +{ + InputSpec concreteInput1{"binding", "TSET", "FOOO", 1, Lifetime::Timeframe}; + InputSpec concreteInput2{"binding", "TSET", "BAAAR", 1, Lifetime::Timeframe}; + InputSpec wildcardInput1{"binding", {"TSET", "FOOO"}, Lifetime::Timeframe}; + InputSpec wildcardInput2{"binding", {"TSET", "BAAAR"}, Lifetime::Timeframe}; + + // wildcard and concrete + BOOST_CHECK(DataSpecUtils::includes(wildcardInput1, concreteInput1)); + BOOST_CHECK(!DataSpecUtils::includes(wildcardInput1, concreteInput2)); + BOOST_CHECK(!DataSpecUtils::includes(concreteInput1, wildcardInput1)); + BOOST_CHECK(!DataSpecUtils::includes(concreteInput2, wildcardInput1)); + + // concrete and concrete + BOOST_CHECK(DataSpecUtils::includes(concreteInput1, concreteInput1)); + BOOST_CHECK(!DataSpecUtils::includes(concreteInput1, concreteInput2)); + BOOST_CHECK(!DataSpecUtils::includes(concreteInput2, concreteInput1)); + + // wildcard and wildcard + BOOST_CHECK(DataSpecUtils::includes(wildcardInput1, wildcardInput1)); + BOOST_CHECK(!DataSpecUtils::includes(wildcardInput1, wildcardInput2)); + BOOST_CHECK(!DataSpecUtils::includes(wildcardInput2, wildcardInput1)); +} diff --git a/Framework/Foundation/CMakeLists.txt b/Framework/Foundation/CMakeLists.txt index 907b9f5991736..ab4c3d7c4fe99 100644 --- a/Framework/Foundation/CMakeLists.txt +++ b/Framework/Foundation/CMakeLists.txt @@ -8,7 +8,17 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -o2_add_header_only_library(FrameworkFoundation) +install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/Framework + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +o2_add_library(FrameworkFoundation + SOURCES src/RuntimeError.cxx + TARGETVARNAME targetName + ) +if (DPL_ENABLE_BACKTRACE) +target_compile_definitions(${targetName} PUBLIC -DDPL_ENABLE_BACKTRACE) +endif() + o2_add_test(test_FunctionalHelpers NAME test_FrameworkFoundation_test_FunctionalHelpers COMPONENT_NAME FrameworkFoundation @@ -34,3 +44,8 @@ o2_add_test(test_Signpost NAME test_FrameworkFoundation_Signpost COMPONENT_NAME FrameworkFoundation SOURCES test/test_Signpost.cxx PUBLIC_LINK_LIBRARIES O2::FrameworkFoundation) + +o2_add_test(test_RuntimeError NAME test_FrameworkFoundation_RuntimeError + COMPONENT_NAME FrameworkFoundation + SOURCES test/test_RuntimeError.cxx + PUBLIC_LINK_LIBRARIES O2::FrameworkFoundation) diff --git a/Framework/Foundation/include/Framework/Pack.h b/Framework/Foundation/include/Framework/Pack.h index 983a256b6e7b9..c14f65e6bda8e 100644 --- a/Framework/Foundation/include/Framework/Pack.h +++ b/Framework/Foundation/include/Framework/Pack.h @@ -12,7 +12,6 @@ #include <cstddef> #include <utility> -#include <cstdio> namespace o2::framework { @@ -33,6 +32,13 @@ constexpr std::size_t pack_size(pack<Ts...> const&) template <std::size_t I, typename T> struct pack_element; +#ifdef __clang__ +template <std::size_t I, typename... Ts> +struct pack_element<I, pack<Ts...>> { + using type = __type_pack_element<I, Ts...>; +}; +#else + // recursive case template <std::size_t I, typename Head, typename... Tail> struct pack_element<I, pack<Head, Tail...>> @@ -44,10 +50,20 @@ template <typename Head, typename... Tail> struct pack_element<0, pack<Head, Tail...>> { typedef Head type; }; +#endif template <std::size_t I, typename T> using pack_element_t = typename pack_element<I, T>::type; +template <typename T> +using pack_head_t = typename pack_element<0, T>::type; + +template <typename Head, typename... Tail> +constexpr auto pack_tail(pack<Head, Tail...>) +{ + return pack<Tail...>{}; +} + /// Templates for manipulating type lists in pack /// (see https://codereview.stackexchange.com/questions/201209/filter-template-meta-function/201222#201222) /// Example of use: @@ -83,10 +99,11 @@ constexpr auto select_pack(Result result, pack<>) template <template <typename> typename Condition, typename Result, typename T, typename... Ts> constexpr auto select_pack(Result result, pack<T, Ts...>) { - if constexpr (Condition<T>()) + if constexpr (Condition<T>()) { return select_pack<Condition>(concatenate_pack(result, pack<T>{}), pack<Ts...>{}); - else + } else { return select_pack<Condition>(result, pack<Ts...>{}); + } } template <template <typename> typename Condition, typename... Types> @@ -102,10 +119,11 @@ constexpr auto filter_pack(Result result, pack<>) template <template <typename> typename Condition, typename Result, typename T, typename... Ts> constexpr auto filter_pack(Result result, pack<T, Ts...>) { - if constexpr (Condition<T>()) + if constexpr (Condition<T>()) { return filter_pack<Condition>(result, pack<Ts...>{}); - else + } else { return filter_pack<Condition>(concatenate_pack(result, pack<T>{}), pack<Ts...>{}); + } } template <typename T> @@ -137,13 +155,51 @@ constexpr size_t has_type_at(pack<> const&) template <typename T, typename T1, typename... Ts> constexpr size_t has_type_at(pack<T1, Ts...> const&) { - if constexpr (std::is_same_v<T, T1>) + if constexpr (std::is_same_v<T, T1>) { return 0; - if constexpr (has_type_v<T, pack<T1, Ts...>>) + } + if constexpr (has_type_v<T, pack<T1, Ts...>>) { return 1 + has_type_at<T>(pack<Ts...>{}); + } return sizeof...(Ts) + 2; } +namespace +{ +template <std::size_t I, typename T> +struct indexed { + using type = T; + constexpr static std::size_t index = I; +}; + +template <typename Is, typename... Ts> +struct indexer; + +template <std::size_t... Is, typename... Ts> +struct indexer<std::index_sequence<Is...>, Ts...> + : indexed<Is, Ts>... { +}; + +template <typename T, std::size_t I> +indexed<I, T> select(indexed<I, T>); + +template <typename W, typename... Ts> +constexpr std::size_t has_type_at_t = decltype(select<W>( + indexer<std::index_sequence_for<Ts...>, Ts...>{}))::index; +} // namespace + +template <typename W> +constexpr std::size_t has_type_at_v(o2::framework::pack<>) +{ + return -1; +} + +template <typename W, typename... Ts> +constexpr std::size_t has_type_at_v(o2::framework::pack<Ts...>) +{ + return has_type_at_t<W, Ts...>; +} + /// Intersect two packs template <typename S1, typename S2> struct intersect_pack { @@ -161,6 +217,36 @@ struct intersect_pack { template <typename S1, typename S2> using intersected_pack_t = typename intersect_pack<S1, S2>::type; +/// Subtract two packs +template <typename S1, typename S2> +struct subtract_pack { + template <std::size_t... Indices> + static constexpr auto make_subtraction(std::index_sequence<Indices...>) + { + return filtered_pack<std::is_void, + std::conditional_t< + !has_type_v<pack_element_t<Indices, S1>, S2>, + pack_element_t<Indices, S1>, void>...>{}; + } + using type = decltype(make_subtraction(std::make_index_sequence<pack_size(S1{})>{})); +}; + +template <typename... Args1, typename... Args2> +constexpr auto concatenate_pack_unique(pack<Args1...>, pack<Args2...>) +{ + using p1 = typename subtract_pack<pack<Args1...>, pack<Args2...>>::type; + return concatenate_pack(p1{}, pack<Args2...>{}); +} + +template <typename P1, typename P2, typename... Ps> +constexpr auto concatenate_pack_unique(P1 p1, P2 p2, Ps... ps) +{ + return concatenate_pack_unique(p1, concatenate_pack_unique(p2, ps...)); +} + +template <typename... Ps> +using concatenated_pack_unique_t = decltype(concatenate_pack_unique(Ps{}...)); + } // namespace o2::framework #endif // O2_FRAMEWORK_PACK_H_ diff --git a/Framework/Foundation/include/Framework/RuntimeError.h b/Framework/Foundation/include/Framework/RuntimeError.h new file mode 100644 index 0000000000000..05692690d1bc7 --- /dev/null +++ b/Framework/Foundation/include/Framework/RuntimeError.h @@ -0,0 +1,35 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_RUNTIMEERROR_H_ +#define O2_FRAMEWORK_RUNTIMEERROR_H_ + +namespace o2::framework +{ + +struct RuntimeError { + static constexpr unsigned int MAX_RUNTIME_ERRORS = 64; + static constexpr unsigned int MAX_RUNTIME_ERROR_SIZE = 1024; + static constexpr unsigned int MAX_BACKTRACE_SIZE = 100; + char what[MAX_RUNTIME_ERROR_SIZE]; + void* backtrace[MAX_BACKTRACE_SIZE]; + int maxBacktrace = 0; +}; + +struct RuntimeErrorRef { + int index = 0; +}; + +RuntimeErrorRef runtime_error(const char*); +RuntimeErrorRef runtime_error_f(const char*, ...); +RuntimeError& error_from_ref(RuntimeErrorRef); + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_RUNTIMEERROR_H_ diff --git a/Framework/Foundation/include/Framework/Tracing.h b/Framework/Foundation/include/Framework/Tracing.h new file mode 100644 index 0000000000000..e1ec3d57c52a2 --- /dev/null +++ b/Framework/Foundation/include/Framework/Tracing.h @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_TRACING_H_ +#define O2_FRAMEWORK_TRACING_H_ + +#if DPL_ENABLE_TRACING && __has_include(<tracy/Tracy.hpp>) +#define DPL_HAS_TRACING +#include <tracy/Tracy.hpp> +#else +#define ZoneScoped \ + while (false) { \ + } +#define FrameMark \ + while (false) { \ + } +#define TracyPlot(...) \ + while (false) { \ + } +#define ZoneScopedN(...) \ + while (false) { \ + } +#define ZoneScopedNS(...) \ + while (false) { \ + } +#define TracyAlloc(...) \ + while (false) { \ + } +#define TracyFree(...) \ + while (false) { \ + } +#define TracyAppInfo(...) \ + while (false) { \ + } +#define TracyLockableN(T, V, N) T V +#define LockableBase(T) T +#endif + +#endif // O2_FRAMEWORK_TRACING_H_ diff --git a/Framework/Foundation/src/RuntimeError.cxx b/Framework/Foundation/src/RuntimeError.cxx new file mode 100644 index 0000000000000..0bb39a70601a7 --- /dev/null +++ b/Framework/Foundation/src/RuntimeError.cxx @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/RuntimeError.h" + +#include <cstdio> +#include <atomic> +#include <cstdarg> +#include <execinfo.h> +#include <cstring> + +namespace o2::framework +{ + +namespace +{ +static RuntimeError gError[RuntimeError::MAX_RUNTIME_ERRORS]; +static std::atomic<bool> gErrorBooking[RuntimeError::MAX_RUNTIME_ERRORS]; + +bool canDumpBacktrace() +{ +#ifdef DPL_ENABLE_BACKTRACE + return true; +#else + return false; +#endif +} +} // namespace + +void clean_all_runtime_errors() +{ + for (size_t i = 0; i < RuntimeError::MAX_RUNTIME_ERRORS; ++i) { + gErrorBooking[i].store(false); + } +} + +void clean_runtime_error(int i) +{ + gErrorBooking[i].store(false); +} + +RuntimeError& error_from_ref(RuntimeErrorRef ref) +{ + return gError[ref.index]; +} + +RuntimeErrorRef runtime_error_f(const char* format, ...) +{ + int i = 0; + bool expected = false; + while (gErrorBooking[i].compare_exchange_strong(expected, true) == false) { + ++i; + } + va_list args; + va_start(args, format); + vsnprintf(gError[i].what, RuntimeError::MAX_RUNTIME_ERROR_SIZE, format, args); + gError[i].maxBacktrace = canDumpBacktrace() ? backtrace(gError[i].backtrace, RuntimeError::MAX_BACKTRACE_SIZE) : 0; + return RuntimeErrorRef{i}; +} + +RuntimeErrorRef runtime_error(const char* s) +{ + int i = 0; + bool expected = false; + while (gErrorBooking[i].compare_exchange_strong(expected, true) == false) { + ++i; + } + strncpy(gError[i].what, s, RuntimeError::MAX_RUNTIME_ERROR_SIZE); + gError[i].maxBacktrace = canDumpBacktrace() ? backtrace(gError[i].backtrace, RuntimeError::MAX_BACKTRACE_SIZE) : 0; + return RuntimeErrorRef{i}; +} + +} // namespace o2::framework diff --git a/Framework/Foundation/test/test_FunctionalHelpers.cxx b/Framework/Foundation/test/test_FunctionalHelpers.cxx index db6ff3449274b..af49a4c3ce7c8 100644 --- a/Framework/Foundation/test/test_FunctionalHelpers.cxx +++ b/Framework/Foundation/test/test_FunctionalHelpers.cxx @@ -44,6 +44,12 @@ BOOST_AUTO_TEST_CASE(TestOverride) static_assert(std::is_same_v<concatenated_pack_t<pack<int, float, char>, pack<float, double>>, pack<int, float, char, float, double>>, "pack should be concatenated"); static_assert(std::is_same_v<concatenated_pack_t<pack<int, float, char>, pack<float, double>, pack<char, short>>, pack<int, float, char, float, double, char, short>>, "pack should be concatenated"); + using p1 = pack<int, float, bool>; + using p2 = pack<int, double, char>; + using p3 = concatenated_pack_unique_t<p1, p2>; + print_pack<p3>(); + static_assert(std::is_same_v<p3, pack<float, bool, int, double, char>>, "pack should not have duplicated types"); + struct ForwardDeclared; static_assert(is_type_complete_v<ForwardDeclared> == false, "This should not be complete because the struct is simply forward declared."); diff --git a/Framework/Foundation/test/test_RuntimeError.cxx b/Framework/Foundation/test/test_RuntimeError.cxx new file mode 100644 index 0000000000000..44867d627f5df --- /dev/null +++ b/Framework/Foundation/test/test_RuntimeError.cxx @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Framework RuntimeError +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> +#include "Framework/RuntimeError.h" +#include <execinfo.h> + +BOOST_AUTO_TEST_CASE(TestRuntimeError) +{ + try { + throw o2::framework::runtime_error("foo"); + } catch (o2::framework::RuntimeErrorRef ref) { + auto& err = o2::framework::error_from_ref(ref); + BOOST_CHECK_EQUAL(strncmp(err.what, "foo", 3), 0); +#ifdef DPL_ENABLE_BACKTRACE + backtrace_symbols_fd(err.backtrace, err.maxBacktrace, STDERR_FILENO); +#endif + } + + try { + throw o2::framework::runtime_error_f("foo %d", 1); + } catch (o2::framework::RuntimeErrorRef ref) { + auto& err = o2::framework::error_from_ref(ref); + BOOST_CHECK_EQUAL(strncmp(err.what, "foo", 3), 0); +#ifdef DPL_ENABLE_BACKTRACE + backtrace_symbols_fd(err.backtrace, err.maxBacktrace, STDERR_FILENO); +#endif + } +} diff --git a/Framework/GUISupport/CMakeLists.txt b/Framework/GUISupport/CMakeLists.txt new file mode 100644 index 0000000000000..b04d078c7f7dd --- /dev/null +++ b/Framework/GUISupport/CMakeLists.txt @@ -0,0 +1,52 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +# Given GCC 7.3 does not provide std::filesystem we use Boost instead +# Drop this once we move to GCC 8.2+ +o2_add_library(FrameworkGUISupport + SOURCES src/Plugin.cxx + src/FrameworkGUIDebugger.cxx + src/FrameworkGUIDevicesGraph.cxx + src/FrameworkGUIDeviceInspector.cxx + src/FrameworkGUIDataRelayerUsage.cxx + src/PaletteHelpers.cxx + PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src + PUBLIC_LINK_LIBRARIES O2::Framework AliceO2::DebugGUI) + +if (NOT APPLE) +set (DEBUG_GUI_TESTS_WORKFLOW + CustomGUIGL + CustomGUISokol + SimpleTracksED + ) +endif() + +foreach(w + ${DEBUG_GUI_TESTS_WORKFLOW} + ) + o2_add_test(${w} NAME test_Framework_test_${w} + SOURCES test/test_${w}.cxx + COMPONENT_NAME Framework + LABELS framework workflow + PUBLIC_LINK_LIBRARIES O2::Framework AliceO2::DebugGUI + TIMEOUT 30 + NO_BOOST_TEST + COMMAND_LINE_ARGS ${DPL_WORKFLOW_TESTS_EXTRA_OPTIONS} --run --shm-segment-size 20000000) +endforeach() + +# TODO: investigate the problem with the two unit tests, maybe setup of the CI +# environment assertion fired X11: The DISPLAY environment variable is missing +# glfw-3.2.1/src/window.c:579: glfwGetFramebufferSize: Assertion `window != +# ((void *)0)' failed. +if(NOT APPLE) +set_property(TEST test_Framework_test_SimpleTracksED PROPERTY DISABLED TRUE) +set_property(TEST test_Framework_test_CustomGUIGL PROPERTY DISABLED TRUE) +set_property(TEST test_Framework_test_CustomGUISokol PROPERTY DISABLED TRUE) +endif() diff --git a/Framework/Core/src/FrameworkGUIDataRelayerUsage.cxx b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx similarity index 94% rename from Framework/Core/src/FrameworkGUIDataRelayerUsage.cxx rename to Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx index 0534afb3dd974..0a5b5e865793d 100644 --- a/Framework/Core/src/FrameworkGUIDataRelayerUsage.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx @@ -17,11 +17,7 @@ static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } -namespace o2 -{ -namespace framework -{ -namespace gui +namespace o2::framework::gui { // This is to display the information in the data relayer @@ -137,14 +133,18 @@ void displayDataRelayer(DeviceMetricsInfo const& metrics, }; auto describeCell = [&metrics, &variablesIndex, &queriesIndex](int input, int slot) -> void { ImGui::BeginTooltip(); + ImGui::Text("Input query matched values for slot: %d", slot); for (size_t vi = 0; vi < variablesIndex.w; ++vi) { auto idx = (slot * variablesIndex.w) + vi; assert(idx < variablesIndex.indexes.size()); MetricInfo const& metricInfo = metrics.metrics[variablesIndex.indexes[idx]]; assert(metricInfo.storeIdx < metrics.stringMetrics.size()); - //assert(metricInfo.type == MetricType::String); auto& data = metrics.stringMetrics[metricInfo.storeIdx]; - ImGui::Text("$%zu: %s", vi, data[(metricInfo.pos - 1) % data.size()].data); + if (vi == 0) { + ImGui::Text("$%zu (timeslice): %s", vi, data[(metricInfo.pos - 1) % data.size()].data); + } else { + ImGui::Text("$%zu: %s", vi, data[(metricInfo.pos - 1) % data.size()].data); + } } ImGui::EndTooltip(); }; @@ -162,6 +162,4 @@ void displayDataRelayer(DeviceMetricsInfo const& metrics, } } -} // namespace gui -} // namespace framework -} // namespace o2 +} // namespace o2::framework::gui diff --git a/Framework/Core/include/Framework/FrameworkGUIDataRelayerUsage.h b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.h similarity index 100% rename from Framework/Core/include/Framework/FrameworkGUIDataRelayerUsage.h rename to Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.h diff --git a/Framework/GUISupport/src/FrameworkGUIDebugger.cxx b/Framework/GUISupport/src/FrameworkGUIDebugger.cxx new file mode 100644 index 0000000000000..cf5e380ffe07d --- /dev/null +++ b/Framework/GUISupport/src/FrameworkGUIDebugger.cxx @@ -0,0 +1,853 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "FrameworkGUIDebugger.h" +#include "Framework/ConfigContext.h" +#include "Framework/ConfigParamRegistry.h" +#include "DebugGUI/imgui.h" +#include "DebugGUI/implot.h" +#include "DebugGUI/imgui_extras.h" +#include "Framework/DriverControl.h" +#include "Framework/DriverInfo.h" +#include "FrameworkGUIDeviceInspector.h" +#include "FrameworkGUIDevicesGraph.h" +#include "FrameworkGUIDataRelayerUsage.h" +#include "PaletteHelpers.h" +#include "FrameworkGUIState.h" + +#include <fmt/format.h> + +#include <algorithm> +#include <iostream> +#include <set> +#include <string> +#include <cinttypes> + +// Simplify debugging +template class std::vector<o2::framework::DeviceMetricsInfo>; + +static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } +static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } + +namespace o2 +{ +namespace framework +{ +namespace gui +{ +// Type erased information for the plotting +struct MultiplotData { + int mod; + size_t first; + size_t size; + const void* Y; + const void* X; + MetricType type; +}; + +} // namespace gui +} // namespace framework +} // namespace o2 + +template class std::vector<o2::framework::gui::MultiplotData>; + +namespace o2 +{ +namespace framework +{ +namespace gui +{ + +ImVec4 colorForLogLevel(LogParsingHelpers::LogLevel logLevel) +{ + switch (logLevel) { + case LogParsingHelpers::LogLevel::Info: + return PaletteHelpers::GREEN; + case LogParsingHelpers::LogLevel::Debug: + return ImVec4(153. / 255, 61. / 255, 61. / 255, 255. / 255); + case LogParsingHelpers::LogLevel::Warning: + return PaletteHelpers::DARK_YELLOW; + case LogParsingHelpers::LogLevel::Error: + return PaletteHelpers::RED; + case LogParsingHelpers::LogLevel::Unknown: + return ImVec4(194. / 255, 195. / 255, 199. / 255, 255. / 255); + default: + return ImVec4(194. / 255, 195. / 255, 199. / 255, 255. / 255); + }; +} + +void displayHistory(const DeviceInfo& info, DeviceControl& control) +{ + if (info.history.empty()) { + return; + } + int startPos = info.historyPos; + const int historySize = info.history.size(); + + int triggerStartPos = startPos + 1 % historySize; + int triggerStopPos = startPos % historySize; + + int j = startPos; + // We look for a stop trigger, so that we know where to stop the search for + // out start search. If no stop trigger is found, we search until the end + if (control.logStopTrigger[0]) { + while ((j % historySize) != ((startPos + 1) % historySize)) { + assert(j >= 0); + assert(j < historySize); + auto& line = info.history[j]; + if (strstr(line.c_str(), control.logStopTrigger)) { + triggerStopPos = (j + 1) % historySize; + break; + } + // Wrap in case we end up below 0 + j = (j == 0) ? historySize - 1 : j - 1; + } + } + + // Look for the last instance of the start trigger before the + // last stop trigger. + j = startPos + 1; + if (control.logStartTrigger[0]) { + while ((j % historySize) != triggerStopPos) { + assert(historySize > j); + assert(historySize == 1000); + auto& line = info.history[j]; + if (strstr(line.c_str(), control.logStartTrigger)) { + triggerStartPos = j; + } + j = (j + 1) % historySize; + } + } + + // We start from the last trigger found. Eventually this is the first + // line in the ring buffer, if no trigger is specified. + size_t ji = triggerStartPos % historySize; + size_t je = triggerStopPos % historySize; + size_t iterations = 0; + while (historySize && ((ji % historySize) != je)) { + assert(iterations < historySize); + iterations++; + assert(historySize == 1000); + assert(ji < historySize); + auto& line = info.history[ji]; + auto logLevel = info.historyLevel[ji]; + + // Skip empty lines + if (line.empty()) { + ji = (ji + 1) % historySize; + continue; + } + // Print matching lines + if (strstr(line.c_str(), control.logFilter) != nullptr) { + auto color = colorForLogLevel(logLevel); + // We filter twice, once on input, to reduce the + // stream, a second time at display time, to avoid + // showing unrelevant messages from past. + if (logLevel >= control.logLevel) { + if (line.find('%', 0) != std::string::npos) { + ImGui::TextUnformatted(line.c_str(), line.c_str() + line.size()); + } else { + ImGui::TextColored(color, line.c_str(), line.c_str() + line.size()); + } + } + } + ji = (ji + 1) % historySize; + } +} + +template <typename T> +struct HistoData { + int mod; + size_t first; + size_t size; + const T* points; +}; + +enum struct MetricsDisplayStyle : int { + Lines = 0, + Histos = 1, + Sparks = 2, + Table = 3, + Stems = 4 +}; + +void displayDeviceMetrics(const char* label, std::string const& selectedMetricName, + size_t rangeBegin, size_t rangeEnd, size_t bins, MetricsDisplayStyle displayType, + std::vector<DeviceSpec> const& specs, + std::vector<DeviceMetricsInfo> const& metricsInfos, + DeviceMetricsInfo const& driverMetric) +{ + std::vector<void*> metricsToDisplay; + std::vector<const char*> deviceNames; + std::vector<MultiplotData> userData; + MetricType metricType; + size_t metricSize = 0; + assert(specs.size() == metricsInfos.size()); + float maxValue = std::numeric_limits<float>::lowest(); + float minValue = 0; + size_t maxDomain = std::numeric_limits<size_t>::lowest(); + size_t minDomain = std::numeric_limits<size_t>::max(); + + for (int mi = 0; mi < metricsInfos.size(); ++mi) { + auto vi = DeviceMetricsHelper::metricIdxByName(selectedMetricName, metricsInfos[mi]); + if (vi == metricsInfos[mi].metricLabelsIdx.size()) { + continue; + } + auto& metric = metricsInfos[mi].metrics[vi]; + deviceNames.push_back(specs[mi].name.c_str()); + metricType = metric.type; + MultiplotData data; + data.mod = metricsInfos[mi].timestamps[vi].size(); + data.first = metric.pos - data.mod; + data.X = metricsInfos[mi].timestamps[vi].data(); + minValue = std::min(minValue, metricsInfos[mi].min[vi]); + maxValue = std::max(maxValue, metricsInfos[mi].max[vi]); + minDomain = std::min(minDomain, metricsInfos[mi].minDomain[vi]); + maxDomain = std::max(maxDomain, metricsInfos[mi].maxDomain[vi]); + metricType = metric.type; + data.type = metric.type; + switch (metric.type) { + case MetricType::Int: { + data.size = metricsInfos[mi].intMetrics[metric.storeIdx].size(); + data.Y = metricsInfos[mi].intMetrics[metric.storeIdx].data(); + } break; + case MetricType::Uint64: { + data.size = metricsInfos[mi].uint64Metrics[metric.storeIdx].size(); + data.Y = metricsInfos[mi].uint64Metrics[metric.storeIdx].data(); + } break; + case MetricType::Float: { + data.size = metricsInfos[mi].floatMetrics[metric.storeIdx].size(); + data.Y = metricsInfos[mi].floatMetrics[metric.storeIdx].data(); + } break; + case MetricType::Unknown: + case MetricType::String: { + data.size = metricsInfos[mi].stringMetrics[metric.storeIdx].size(); + data.Y = nullptr; + data.type = MetricType::String; + metricType = MetricType::String; + } break; + } + metricSize = data.size; + userData.emplace_back(data); + } + if (true) { + auto vi = DeviceMetricsHelper::metricIdxByName(selectedMetricName, driverMetric); + if (vi < driverMetric.metricLabelsIdx.size()) { + auto& metric = driverMetric.metrics[vi]; + deviceNames.push_back("driver"); + metricType = metric.type; + MultiplotData data; + data.mod = driverMetric.timestamps[vi].size(); + data.first = metric.pos - data.mod; + data.X = driverMetric.timestamps[vi].data(); + minValue = std::min(minValue, driverMetric.min[vi]); + maxValue = std::max(maxValue, driverMetric.max[vi]); + minDomain = std::min(minDomain, driverMetric.minDomain[vi]); + maxDomain = std::max(maxDomain, driverMetric.maxDomain[vi]); + metricType = metric.type; + data.type = metric.type; + switch (metric.type) { + case MetricType::Int: { + data.size = driverMetric.intMetrics[metric.storeIdx].size(); + data.Y = driverMetric.intMetrics[metric.storeIdx].data(); + } break; + case MetricType::Uint64: { + data.size = driverMetric.uint64Metrics[metric.storeIdx].size(); + data.Y = driverMetric.uint64Metrics[metric.storeIdx].data(); + } break; + case MetricType::Float: { + data.size = driverMetric.floatMetrics[metric.storeIdx].size(); + data.Y = driverMetric.floatMetrics[metric.storeIdx].data(); + } break; + case MetricType::Unknown: + case MetricType::String: { + data.size = driverMetric.stringMetrics[metric.storeIdx].size(); + data.Y = nullptr; + data.type = MetricType::String; + metricType = MetricType::String; + } break; + } + metricSize = data.size; + userData.emplace_back(data); + } + } + + maxDomain = std::max(minDomain + 1024, maxDomain); + + for (size_t ui = 0; ui < userData.size(); ++ui) { + metricsToDisplay.push_back(&(userData[ui])); + } + auto getterXY = [](void* hData, int idx) -> ImPlotPoint { + auto histoData = reinterpret_cast<const MultiplotData*>(hData); + size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; + double x = static_cast<const size_t*>(histoData->X)[pos]; + double y = 0.; + if (histoData->type == MetricType::Int) { + y = static_cast<const int*>(histoData->Y)[pos]; + } else if (histoData->type == MetricType::Uint64) { + y = static_cast<const uint64_t*>(histoData->Y)[pos]; + } else if (histoData->type == MetricType::Float) { + y = static_cast<const float*>(histoData->Y)[pos]; + } + return ImPlotPoint{x, y}; + }; + + switch (displayType) { + case MetricsDisplayStyle::Histos: + ImPlot::SetNextPlotLimitsX(minDomain, maxDomain, ImGuiCond_Once); + ImPlot::SetNextPlotLimitsY(minValue, maxValue, ImGuiCond_Always); + ImPlot::SetNextPlotTicksX(minDomain, maxDomain, 5); + if (ImPlot::BeginPlot(selectedMetricName.c_str(), "time", "value")) { + for (size_t pi = 0; pi < metricsToDisplay.size(); ++pi) { + ImPlot::PlotBarsG(deviceNames[pi], getterXY, metricsToDisplay[pi], metricSize, 1, 0); + } + ImPlot::EndPlot(); + } + + break; + case MetricsDisplayStyle::Lines: + ImPlot::SetNextPlotLimitsX(minDomain, maxDomain, ImGuiCond_Once); + ImPlot::SetNextPlotLimitsY(minValue, maxValue * 1.2, ImGuiCond_Always); + ImPlot::SetNextPlotTicksX(minDomain, maxDomain, 5); + if (ImPlot::BeginPlot("##Some plot", "time", "value")) { + for (size_t pi = 0; pi < metricsToDisplay.size(); ++pi) { + ImPlot::PlotLineG(deviceNames[pi], getterXY, metricsToDisplay[pi], metricSize, 0); + } + ImPlot::EndPlot(); + } + break; + case MetricsDisplayStyle::Stems: + ImPlot::SetNextPlotLimitsX(minDomain, maxDomain, ImGuiCond_Once); + ImPlot::SetNextPlotLimitsY(minValue, maxValue * 1.2, ImGuiCond_Always); + ImPlot::SetNextPlotTicksX(minDomain, maxDomain, 5); + if (ImPlot::BeginPlot("##Some plot", "time", "value")) { + for (size_t pi = 0; pi < userData.size(); ++pi) { + auto stemsData = reinterpret_cast<const MultiplotData*>(metricsToDisplay[pi]); + // FIXME: display a message for other metrics + if (stemsData->type == MetricType::Uint64) { + ImPlot::PlotStems(deviceNames[pi], (const ImU64*)stemsData->X, (const ImU64*)stemsData->Y, metricSize); + } + } + ImPlot::EndPlot(); + } + break; + default: + break; + } +} + +struct ColumnInfo { + MetricType type; + int index; +}; + +void metricsTableRow(std::vector<ColumnInfo> columnInfos, + std::vector<DeviceMetricsInfo> const& metricsInfos, + int row) +{ + ImGui::Text("%d", row); + ImGui::NextColumn(); + + for (size_t j = 0; j < columnInfos.size(); j++) { + auto& info = columnInfos[j]; + auto& metricsInfo = metricsInfos[j]; + + if (info.index == -1) { + ImGui::TextUnformatted("-"); + ImGui::NextColumn(); + continue; + } + switch (info.type) { + case MetricType::Int: { + ImGui::Text("%i (%i)", metricsInfo.intMetrics[info.index][row], info.index); + ImGui::NextColumn(); + } break; + case MetricType::Uint64: { + ImGui::Text("%" PRIu64 " (%i)", metricsInfo.uint64Metrics[info.index][row], info.index); + ImGui::NextColumn(); + } break; + case MetricType::Float: { + ImGui::Text("%f (%i)", metricsInfo.floatMetrics[info.index][row], info.index); + ImGui::NextColumn(); + } break; + case MetricType::String: { + ImGui::Text("%s (%i)", metricsInfo.stringMetrics[info.index][row].data, info.index); + ImGui::NextColumn(); + } break; + default: + ImGui::NextColumn(); + break; + } + } +} + +void historyBar(gui::WorkspaceGUIState& globalGUIState, + size_t rangeBegin, size_t rangeEnd, + gui::DeviceGUIState& state, + DriverInfo const& driverInfo, + DeviceSpec const& spec, + DeviceMetricsInfo const& metricsInfo) +{ + bool open = ImGui::TreeNode(state.label.c_str()); + if (open) { + ImGui::Text("# channels: %lu", spec.outputChannels.size() + spec.inputChannels.size()); + ImGui::TreePop(); + } + ImGui::NextColumn(); + + if (globalGUIState.selectedMetric == -1) { + ImGui::NextColumn(); + return; + } + + auto currentMetricName = driverInfo.availableMetrics[globalGUIState.selectedMetric]; + + size_t i = DeviceMetricsHelper::metricIdxByName(currentMetricName, metricsInfo); + // We did not find any plot, skipping this. + if (i == metricsInfo.metricLabelsIdx.size()) { + ImGui::NextColumn(); + return; + } + auto& metric = metricsInfo.metrics[i]; + + switch (metric.type) { + case MetricType::Int: { + HistoData<int> data; + data.mod = metricsInfo.timestamps[i].size(); + data.first = metric.pos - data.mod; + data.size = metricsInfo.intMetrics[metric.storeIdx].size(); + data.points = metricsInfo.intMetrics[metric.storeIdx].data(); + + auto getter = [](void* hData, int idx) -> float { + auto histoData = reinterpret_cast<HistoData<int>*>(hData); + size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; + assert(pos >= 0 && pos < 1024); + return histoData->points[pos]; + }; + ImGui::PlotLines(("##" + currentMetricName).c_str(), getter, &data, data.size); + ImGui::NextColumn(); + } break; + case MetricType::Uint64: { + HistoData<uint64_t> data; + data.mod = metricsInfo.timestamps[i].size(); + data.first = metric.pos - data.mod; + data.size = metricsInfo.uint64Metrics[metric.storeIdx].size(); + data.points = metricsInfo.uint64Metrics[metric.storeIdx].data(); + + auto getter = [](void* hData, int idx) -> float { + auto histoData = reinterpret_cast<HistoData<uint64_t>*>(hData); + size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; + assert(pos >= 0 && pos < 1024); + return histoData->points[pos]; + }; + ImGui::PlotLines(("##" + currentMetricName).c_str(), getter, &data, data.size); + ImGui::NextColumn(); + } break; + case MetricType::Float: { + HistoData<float> data; + data.mod = metricsInfo.timestamps[i].size(); + data.first = metric.pos - data.mod; + data.size = metricsInfo.floatMetrics[metric.storeIdx].size(); + data.points = metricsInfo.floatMetrics[metric.storeIdx].data(); + + auto getter = [](void* hData, int idx) -> float { + auto histoData = reinterpret_cast<HistoData<float>*>(hData); + size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod; + assert(pos >= 0 && pos < 1024); + return histoData->points[pos]; + }; + ImGui::PlotLines(("##" + currentMetricName).c_str(), getter, &data, data.size); + ImGui::NextColumn(); + } break; + default: + ImGui::NextColumn(); + return; + break; + } +} + +/// Calculate where to find the coliumns for a give metric +std::vector<ColumnInfo> calculateTableIndex(gui::WorkspaceGUIState& globalGUIState, + int selectedMetric, + DriverInfo const& driverInfo, + std::vector<DeviceMetricsInfo> const& metricsInfos) +{ + std::vector<ColumnInfo> columns; + for (size_t j = 0; j < globalGUIState.devices.size(); ++j) { + const DeviceMetricsInfo& metricsInfo = metricsInfos[j]; + /// Nothing to draw, if no metric selected. + if (selectedMetric == -1) { + columns.push_back({MetricType::Int, -1}); + continue; + } + auto currentMetricName = driverInfo.availableMetrics[selectedMetric]; + size_t idx = DeviceMetricsHelper::metricIdxByName(currentMetricName, metricsInfo); + + // We did not find any plot, skipping this. + if (idx == metricsInfo.metricLabelsIdx.size()) { + columns.push_back({MetricType::Int, -1}); + continue; + } + auto metric = metricsInfos[j].metrics[idx]; + columns.push_back({metric.type, static_cast<int>(metric.storeIdx)}); + } + return columns; +}; + +void displayDeviceHistograms(gui::WorkspaceGUIState& state, + DriverInfo const& driverInfo, + std::vector<DeviceInfo> const& infos, + std::vector<DeviceSpec> const& devices, + std::vector<DataProcessorInfo> const& metadata, + std::vector<DeviceControl>& controls, + std::vector<DeviceMetricsInfo> const& metricsInfos) +{ + showTopologyNodeGraph(state, infos, devices, metadata, controls, metricsInfos); + if (state.bottomPaneVisible == false) { + return; + } + ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetIO().DisplaySize.y - state.bottomPaneSize), 0); + ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, state.bottomPaneSize), 0); + + ImGui::Begin("Devices", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); + ImGui::BeginGroup(); + char const* currentMetric = nullptr; + if (state.selectedMetric != -1) { + currentMetric = driverInfo.availableMetrics[state.selectedMetric].c_str(); + } else { + currentMetric = "Click to select metric"; + } + if (ImGui::BeginCombo("###Select metric", currentMetric, 0)) { + for (size_t mi = 0; mi < driverInfo.availableMetrics.size(); ++mi) { + auto metric = driverInfo.availableMetrics[mi]; + bool isSelected = mi == state.selectedMetric; + if (ImGui::Selectable(driverInfo.availableMetrics[mi].c_str(), isSelected)) { + state.selectedMetric = mi; + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + }; + + static char const* plotStyles[] = { + "lines", + "histograms", + "sparks", + "table", + "stems"}; + ImGui::SameLine(); + static enum MetricsDisplayStyle currentStyle = MetricsDisplayStyle::Lines; + ImGui::Combo("##Select style", reinterpret_cast<int*>(¤tStyle), plotStyles, IM_ARRAYSIZE(plotStyles)); + + // Calculate the full timestamp range for the selected metric + size_t minTime = -1; + size_t maxTime = 0; + std::string currentMetricName; + if (state.selectedMetric >= 0) { + currentMetricName = driverInfo.availableMetrics[state.selectedMetric]; + for (auto& metricInfo : metricsInfos) { + size_t mi = DeviceMetricsHelper::metricIdxByName(currentMetricName, metricInfo); + if (mi == metricInfo.metricLabelsIdx.size()) { + continue; + } + auto& metric = metricInfo.metrics[mi]; + auto& timestamps = metricInfo.timestamps[mi]; + + for (size_t ti = 0; ti != metricInfo.timestamps.size(); ++ti) { + size_t minRangePos = (metric.pos + ti) % metricInfo.timestamps.size(); + size_t curMinTime = timestamps[minRangePos]; + if (curMinTime == 0) { + continue; + } + minTime = minTime < curMinTime ? minTime : curMinTime; + if (minTime != 0 && minTime != -1) { + break; + } + } + size_t maxRangePos = (size_t)(metric.pos) - 1 % metricInfo.timestamps.size(); + size_t curMaxTime = timestamps[maxRangePos]; + maxTime = maxTime > curMaxTime ? maxTime : curMaxTime; + } + } + ImGui::EndGroup(); + if (!currentMetricName.empty()) { + switch (currentStyle) { + case MetricsDisplayStyle::Stems: + case MetricsDisplayStyle::Histos: + case MetricsDisplayStyle::Lines: { + displayDeviceMetrics("Metrics", + currentMetricName, minTime, maxTime, 1024, + currentStyle, devices, metricsInfos, driverInfo.metrics); + } break; + case MetricsDisplayStyle::Sparks: { +#if defined(ImGuiCol_ChildWindowBg) + ImGui::BeginChild("##ScrollingRegion", ImVec2(ImGui::GetIO().DisplaySize.x + state.leftPaneSize + state.rightPaneSize - 10, -ImGui::GetItemsLineHeightWithSpacing()), false, + ImGuiWindowFlags_HorizontalScrollbar); +#else + ImGui::BeginChild("##ScrollingRegion", ImVec2(ImGui::GetIO().DisplaySize.x + state.leftPaneSize + state.rightPaneSize - 10, -ImGui::GetTextLineHeightWithSpacing()), false, + ImGuiWindowFlags_HorizontalScrollbar); +#endif + ImGui::Columns(2); + ImGui::SetColumnOffset(1, 300); + for (size_t i = 0; i < state.devices.size(); ++i) { + gui::DeviceGUIState& deviceGUIState = state.devices[i]; + const DeviceSpec& spec = devices[i]; + const DeviceMetricsInfo& metricsInfo = metricsInfos[i]; + + historyBar(state, minTime, maxTime, deviceGUIState, driverInfo, spec, metricsInfo); + } + ImGui::Columns(1); + ImGui::EndChild(); + } break; + case MetricsDisplayStyle::Table: { +#if defined(ImGuiCol_ChildWindowBg) + ImGui::BeginChild("##ScrollingRegion", ImVec2(ImGui::GetIO().DisplaySize.x + state.leftPaneSize + state.rightPaneSize - 10, -ImGui::GetItemsLineHeightWithSpacing()), false, + ImGuiWindowFlags_HorizontalScrollbar); +#else + ImGui::BeginChild("##ScrollingRegion", ImVec2(ImGui::GetIO().DisplaySize.x + state.leftPaneSize + state.rightPaneSize - 10, -ImGui::GetTextLineHeightWithSpacing()), false, + ImGuiWindowFlags_HorizontalScrollbar); +#endif + + // The +1 is for the timestamp column + ImGui::Columns(state.devices.size() + 1); + ImGui::TextUnformatted("entry"); + ImGui::NextColumn(); + ImVec2 textsize = ImGui::CalcTextSize("extry", nullptr, true); + float offset = 0.f; + offset += std::max(100.f, textsize.x); + for (size_t j = 0; j < state.devices.size(); ++j) { + gui::DeviceGUIState& deviceGUIState = state.devices[j]; + const DeviceSpec& spec = devices[j]; + + ImGui::SetColumnOffset(-1, offset); + textsize = ImGui::CalcTextSize(spec.name.c_str(), nullptr, true); + offset += std::max(100.f, textsize.x); + ImGui::TextUnformatted(spec.name.c_str()); + ImGui::NextColumn(); + } + ImGui::Separator(); + + auto columns = calculateTableIndex(state, state.selectedMetric, driverInfo, metricsInfos); + + // Calculate which columns we want to see. + // FIXME: only one column for now. + for (size_t i = 0; i < 10; ++i) { + metricsTableRow(columns, metricsInfos, i); + } + ImGui::Columns(1); + + ImGui::EndChild(); + } break; + } + } + ImGui::End(); +} + +void pushWindowColorDueToStatus(const DeviceInfo& info) +{ + using LogLevel = LogParsingHelpers::LogLevel; + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.); + if (info.active == false) { + ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::DARK_RED); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED); + ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::RED); + return; + } + switch (info.maxLogLevel) { + case LogLevel::Error: + ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_RED); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED); + ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_RED); + break; + case LogLevel::Warning: + ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_YELLOW); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::YELLOW); + ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_YELLOW); + break; + case LogLevel::Info: + ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_GREEN); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::GREEN); + ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_GREEN); + break; + default: + ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_BLUE); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::BLUE); + ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_BLUE); + break; + } +} + +void popWindowColorDueToStatus() +{ + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(1); +} + +/// Display information window about the driver +/// and its state. +void displayDriverInfo(DriverInfo const& driverInfo, DriverControl& driverControl) +{ + ImGui::Begin("Driver information"); + static int pid = getpid(); + + if (driverControl.state == DriverControlState::STEP) { + driverControl.state = DriverControlState::PAUSE; + } + auto state = reinterpret_cast<int*>(&driverControl.state); + ImGui::RadioButton("Play", state, static_cast<int>(DriverControlState::PLAY)); + ImGui::SameLine(); + ImGui::RadioButton("Pause", state, static_cast<int>(DriverControlState::PAUSE)); + ImGui::SameLine(); + ImGui::RadioButton("Step", state, static_cast<int>(DriverControlState::STEP)); + + auto& registry = driverInfo.configContext->options(); + ImGui::Columns(); + + ImGui::Text("PID: %d", pid); + ImGui::Text("Frame cost (latency): %.1f(%.1f)ms", driverInfo.frameCost, driverInfo.frameLatency); + ImGui::Text("Input parsing cost (latency): %.1f(%.1f)ms", driverInfo.inputProcessingCost, driverInfo.inputProcessingLatency); + ImGui::Text("State stack (depth %lu)", driverInfo.states.size()); + if (ImGui::Button("SIGCONT all children")) { + kill(0, SIGCONT); + } + ImGui::SameLine(); + if (ImGui::Button("Debug driver")) { + std::string pidStr = std::to_string(pid); + setenv("O2DEBUGGEDPID", pidStr.c_str(), 1); +#ifdef __APPLE__ + std::string defaultAppleDebugCommand = + "osascript -e 'tell application \"Terminal\" to activate'" + " -e 'tell application \"Terminal\" to do script \"lldb -p \" & (system attribute \"O2DEBUGGEDPID\")'"; + setenv("O2DPLDEBUG", defaultAppleDebugCommand.c_str(), 0); +#else + setenv("O2DPLDEBUG", "xterm -hold -e gdb attach $O2DEBUGGEDPID &", 0); +#endif + int retVal = system(getenv("O2DPLDEBUG")); + (void)retVal; + } + + ImGui::SameLine(); + if (ImGui::Button("Profile")) { + std::string pidStr = std::to_string(pid); + setenv("O2PROFILEDPID", pidStr.c_str(), 1); +#ifdef __APPLE__ + auto defaultAppleProfileCommand = fmt::format( + "osascript -e 'tell application \"Terminal\" to activate'" + " -e 'tell application \"Terminal\" to do script \"xcrun xctrace record --output dpl-profile-{}.trace" + " --time-limit 30s --template Time\\\\ Profiler --attach {} " + " && open dpl-profile-{}.trace && exit\"'" + " && open dpl-driver-profile-{}.trace", + pid, pid, pid, pid); + std::cout << defaultAppleProfileCommand << std::endl; + setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0); +#else + setenv("O2DPLPROFILE", "xterm -hold -e perf record -a -g -p $O2PROFILEDPID > perf-$O2PROFILEDPID.data &", 0); +#endif + int retVal = system(getenv("O2DPLPROFILE")); + (void)retVal; + } + + for (size_t i = 0; i < driverInfo.states.size(); ++i) { + ImGui::Text("#%lu: %s", i, DriverInfoHelper::stateToString(driverInfo.states[i])); + } + + ImGui::End(); +} + +// FIXME: return empty function in case we were not built +// with GLFW support. +/// +std::function<void(void)> getGUIDebugger(std::vector<DeviceInfo> const& infos, + std::vector<DeviceSpec> const& devices, + std::vector<DataProcessorInfo> const& metadata, + std::vector<DeviceMetricsInfo> const& metricsInfos, + DriverInfo const& driverInfo, + std::vector<DeviceControl>& controls, + DriverControl& driverControl) +{ + static gui::WorkspaceGUIState globalGUIState; + gui::WorkspaceGUIState& guiState = globalGUIState; + guiState.selectedMetric = -1; + guiState.metricMaxRange = 0UL; + guiState.metricMinRange = -1; + // FIXME: this should probaly have a better mapping between our window state and + guiState.devices.resize(infos.size()); + for (size_t i = 0; i < guiState.devices.size(); ++i) { + gui::DeviceGUIState& state = guiState.devices[i]; + state.label = devices[i].id + "(" + std::to_string(infos[i].pid) + ")"; + } + guiState.bottomPaneSize = 340; + guiState.leftPaneSize = 200; + guiState.rightPaneSize = 300; + + // Show all the panes by default. + guiState.bottomPaneVisible = true; + guiState.leftPaneVisible = true; + guiState.rightPaneVisible = true; + + return [&guiState, &infos, &devices, &metadata, &controls, &metricsInfos, &driverInfo, &driverControl]() { + ImGuiStyle& style = ImGui::GetStyle(); + style.FrameRounding = 0.; + style.WindowRounding = 0.; + style.Colors[ImGuiCol_WindowBg] = ImVec4(0x1b / 255.f, 0x1b / 255.f, 0x1b / 255.f, 1.00f); + style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0x1b / 255.f, 0x1b / 255.f, 0x1b / 255.f, 1.00f); + + displayDeviceHistograms(guiState, driverInfo, infos, devices, metadata, controls, metricsInfos); + displayDriverInfo(driverInfo, driverControl); + + int windowPosStepping = (ImGui::GetIO().DisplaySize.y - 500) / guiState.devices.size(); + + for (size_t i = 0; i < guiState.devices.size(); ++i) { + gui::DeviceGUIState& state = guiState.devices[i]; + assert(i < infos.size()); + assert(i < devices.size()); + const DeviceInfo& info = infos[i]; + const DeviceSpec& spec = devices[i]; + const DeviceMetricsInfo& metrics = metricsInfos[i]; + + assert(controls.size() == devices.size()); + DeviceControl& control = controls[i]; + + pushWindowColorDueToStatus(info); + if (control.logVisible) { + ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x / 3 * 2, i * windowPosStepping), ImGuiCond_Once); + ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x / 3, ImGui::GetIO().DisplaySize.y - 300), + ImGuiCond_Once); + ImGui::Begin(state.label.c_str(), &control.logVisible); + + ImGui::InputText("Log filter", control.logFilter, sizeof(control.logFilter)); + ImGui::InputText("Log start trigger", control.logStartTrigger, sizeof(control.logStartTrigger)); + ImGui::InputText("Log stop trigger", control.logStopTrigger, sizeof(control.logStopTrigger)); + ImGui::Checkbox("Stop logging", &control.quiet); + ImGui::SameLine(); + ImGui::Combo("Log level", reinterpret_cast<int*>(&control.logLevel), LogParsingHelpers::LOG_LEVELS, + (int)LogParsingHelpers::LogLevel::Size, 5); + + ImGui::Separator(); +#if defined(ImGuiCol_ChildWindowBg) + ImGui::BeginChild("ScrollingRegion", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing()), false, + ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); +#else + ImGui::BeginChild("ScrollingRegion", ImVec2(0, -ImGui::GetTextLineHeightWithSpacing()), false, + ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove); +#endif + displayHistory(info, control); + ImGui::EndChild(); + ImGui::End(); + } + popWindowColorDueToStatus(); + } + }; +} + +} // namespace gui +} // namespace framework +} // namespace o2 diff --git a/Framework/Core/include/Framework/FrameworkGUIDebugger.h b/Framework/GUISupport/src/FrameworkGUIDebugger.h similarity index 85% rename from Framework/Core/include/Framework/FrameworkGUIDebugger.h rename to Framework/GUISupport/src/FrameworkGUIDebugger.h index 20cb8d1671e07..0788169177a68 100644 --- a/Framework/Core/include/Framework/FrameworkGUIDebugger.h +++ b/Framework/GUISupport/src/FrameworkGUIDebugger.h @@ -7,10 +7,10 @@ // In applying this license CERN does 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 FRAMEWORK_FRAMEWORKGUIDEBUGGER_H_ -#define FRAMEWORK_FRAMEWORKGUIDEBUGGER_H_ +#ifndef O2_FRAMEWORK_FRAMEWORKGUIDEBUGGER_H_ +#define O2_FRAMEWORK_FRAMEWORKGUIDEBUGGER_H_ -#include "DataProcessorInfo.h" +#include "Framework/DataProcessorInfo.h" #include "Framework/DeviceControl.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" @@ -19,9 +19,7 @@ #include <functional> #include <vector> -namespace o2 -{ -namespace framework +namespace o2::framework { class DriverInfo; @@ -38,6 +36,5 @@ std::function<void(void)> getGUIDebugger(std::vector<DeviceInfo> const& infos, std::vector<DeviceControl>& controls, DriverControl& driverControl); } // namespace gui -} // namespace framework -} // namespace o2 -#endif // FRAMEWORK_FRAMEWORKGUIDEBUGGER_H +} // namespace o2::framework +#endif // O2_FRAMEWORK_FRAMEWORKGUIDEBUGGER_H_ diff --git a/Framework/Core/src/FrameworkGUIDeviceInspector.cxx b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx similarity index 83% rename from Framework/Core/src/FrameworkGUIDeviceInspector.cxx rename to Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx index 3e0ee83020e02..d5ff824070799 100644 --- a/Framework/Core/src/FrameworkGUIDeviceInspector.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx @@ -9,23 +9,21 @@ // or submit itself to any jurisdiction. #include "FrameworkGUIDeviceInspector.h" -#include "DataProcessorInfo.h" +#include "Framework/DataProcessorInfo.h" #include "Framework/DeviceControl.h" #include "Framework/DeviceSpec.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" #include "Framework/ChannelSpec.h" +#include "Framework/Logger.h" #include "DebugGUI/imgui.h" #include <csignal> #include <cstdlib> +#include <iostream> -namespace o2 -{ -namespace framework -{ -namespace gui +namespace o2::framework::gui { struct ChannelsTableHelper { @@ -143,6 +141,41 @@ void optionsTable(const char* label, std::vector<ConfigParamSpec> const& options ImGui::Columns(1); } +void servicesTable(const char* label, std::vector<ServiceSpec> const& services) +{ + if (services.empty()) { + return; + } + if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Columns(2); + auto labels = {"Service", "Kind"}; + for (auto& label : labels) { + ImGui::TextUnformatted(label); + ImGui::NextColumn(); + } + for (auto& service : services) { + ImGui::TextUnformatted(service.name.c_str()); + ImGui::NextColumn(); + switch (service.kind) { + case ServiceKind::Serial: + ImGui::TextUnformatted("Serial"); + break; + case ServiceKind::Global: + ImGui::TextUnformatted("Global"); + break; + case ServiceKind::Stream: + ImGui::TextUnformatted("Stream"); + break; + default: + ImGui::TextUnformatted("unknown"); + } + ImGui::NextColumn(); + } + } + + ImGui::Columns(1); +} + void displayDeviceInspector(DeviceSpec const& spec, DeviceInfo const& info, DeviceMetricsInfo const& metrics, @@ -156,6 +189,9 @@ void displayDeviceInspector(DeviceSpec const& spec, } else { ImGui::Text("Pid: %d (exit status: %d)", info.pid, info.exitStatus); } +#ifdef DPL_ENABLE_TRACING + ImGui::Text("Tracy Port: %d", info.tracyPort); +#endif ImGui::Text("Rank: %zu/%zu%%%zu/%zu", spec.rank, spec.nSlots, spec.inputTimesliceId, spec.maxInputTimeslices); if (ImGui::Button("Attach debugger")) { @@ -164,8 +200,7 @@ void displayDeviceInspector(DeviceSpec const& spec, #ifdef __APPLE__ std::string defaultAppleDebugCommand = "osascript -e 'tell application \"Terminal\" to activate'" - " -e 'tell application \"Terminal\" to do script \"lldb -p " + - pid + "\"'"; + " -e 'tell application \"Terminal\" to do script \"lldb -p \" & (system attribute \"O2DEBUGGEDPID\")'"; setenv("O2DPLDEBUG", defaultAppleDebugCommand.c_str(), 0); #else setenv("O2DPLDEBUG", "xterm -hold -e gdb attach $O2DEBUGGEDPID &", 0); @@ -194,12 +229,24 @@ void displayDeviceInspector(DeviceSpec const& spec, (void)retVal; } + ImGui::SameLine(); +#if DPL_ENABLE_TRACING + if (ImGui::Button("Tracy")) { + std::string tracyPort = std::to_string(info.tracyPort); + auto cmd = fmt::format("tracy-profiler -p {} -a 127.0.0.1 &", info.tracyPort); + LOG(debug) << cmd; + int retVal = system(cmd.c_str()); + (void)retVal; + } +#endif + deviceInfoTable(info, metrics); for (auto& option : info.currentConfig) { ImGui::Text("%s: %s", option.first.c_str(), option.second.data().c_str()); } configurationTable(info.currentConfig, info.currentProvenance); optionsTable("Workflow Options", metadata.workflowOptions, control); + servicesTable("Services", spec.services); if (ImGui::CollapsingHeader("Command line arguments", ImGuiTreeNodeFlags_DefaultOpen)) { for (auto& arg : metadata.cmdLineArgs) { ImGui::Text("%s", arg.c_str()); @@ -240,6 +287,4 @@ void displayDeviceInspector(DeviceSpec const& spec, } } -} // namespace gui -} // namespace framework -} // namespace o2 +} // namespace o2::framework::gui diff --git a/Framework/Core/src/FrameworkGUIDeviceInspector.h b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.h similarity index 100% rename from Framework/Core/src/FrameworkGUIDeviceInspector.h rename to Framework/GUISupport/src/FrameworkGUIDeviceInspector.h diff --git a/Framework/Core/src/FrameworkGUIDevicesGraph.cxx b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx similarity index 94% rename from Framework/Core/src/FrameworkGUIDevicesGraph.cxx rename to Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx index 28550e67600ee..ecb3587d3d915 100644 --- a/Framework/Core/src/FrameworkGUIDevicesGraph.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx @@ -7,22 +7,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. -#include "Framework/FrameworkGUIDevicesGraph.h" -#include "Framework/FrameworkGUIDataRelayerUsage.h" -#include "Framework/FrameworkGUIState.h" +#include "FrameworkGUIDevicesGraph.h" +#include "FrameworkGUIDataRelayerUsage.h" +#include "FrameworkGUIState.h" +#include "PaletteHelpers.h" #include "Framework/DeviceSpec.h" #include "Framework/DeviceInfo.h" #include "Framework/LogParsingHelpers.h" -#include "Framework/PaletteHelpers.h" +#include "Framework/Logger.h" #include "FrameworkGUIDeviceInspector.h" +#include "Framework/Logger.h" #include "../src/WorkflowHelpers.h" #include "DebugGUI/imgui.h" -#if __has_include("DebugGUI/icons_font_awesome.h") -#include "DebugGUI/icons_font_awesome.h" -#else -#define ICON_FA_EXCLAMATION_CIRCLE "(Errors!)" -#define ICON_FA_EXCLAMATION_TRIANGLE "(Warnings!)" -#endif +#include <DebugGUI/icons_font_awesome.h> #include <algorithm> #include <cmath> #include <vector> @@ -30,13 +27,8 @@ static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } -namespace o2 -{ -namespace framework -{ -namespace gui +namespace o2::framework::gui { - struct NodeColor { ImVec4 normal; ImVec4 hovered; @@ -235,7 +227,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, int groupId = 0; auto metadatum = std::find_if(metadata.begin(), metadata.end(), - [& name = spec.name](DataProcessorInfo const& info) { return info.name == name; }); + [&name = spec.name](DataProcessorInfo const& info) { return info.name == name; }); for (size_t gi = 0; gi < groupList.Size; ++gi) { if (metadatum == metadata.end()) { @@ -385,7 +377,11 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1, 1)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); +#if defined(ImGuiCol_ChildWindowBg) ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, (ImU32)ImColor(60, 60, 70, 200)); +#else + ImGui::PushStyleColor(ImGuiCol_WindowBg, (ImU32)ImColor(60, 60, 70, 200)); +#endif ImVec2 graphSize = ImGui::GetWindowSize(); if (state.leftPaneVisible) { graphSize.x -= state.leftPaneSize; @@ -460,6 +456,15 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImVec2 node_rect_max = node_rect_min + node->Size; ImVec2 node_rect_title = node_rect_min + ImVec2(node->Size.x, 24); + if (node_rect_min.x > 20 + 2 * NODE_WINDOW_PADDING.x + state.leftPaneSize + graphSize.x) { + ImGui::PopID(); + continue; + } + if (node_rect_min.y > 20 + 2 * NODE_WINDOW_PADDING.y + toolbarSize.y + graphSize.y) { + ImGui::PopID(); + continue; + } + // Display node box draw_list->ChannelsSetCurrent(backgroundLayer); // Background ImGui::SetCursorScreenPos(node_rect_min); @@ -472,12 +477,15 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, } } bool node_moving_active = ImGui::IsItemActive(); - if (node_widgets_active || node_moving_active) + if (node_widgets_active || node_moving_active) { node_selected = node->ID; - if (node_moving_active && ImGui::IsMouseDragging(0)) + } + if (node_moving_active && ImGui::IsMouseDragging(0)) { pos->pos = pos->pos + ImGui::GetIO().MouseDelta; - if (ImGui::IsWindowHovered() && !node_moving_active && ImGui::IsMouseDragging(0)) + } + if (ImGui::IsWindowHovered() && !node_moving_active && ImGui::IsMouseDragging(0)) { scrolling = scrolling - ImVec2(ImGui::GetIO().MouseDelta.x / 4.f, ImGui::GetIO().MouseDelta.y / 4.f); + } auto nodeBg = decideColorForNode(info); @@ -500,8 +508,9 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, draw_list->AddTriangleFilled(pp1, pp2, pp3, color); draw_list->AddCircleFilled(offset + NodePos::GetInputSlotPos(nodes, positions, node_idx, slot_idx), NODE_SLOT_RADIUS, ImColor(150, 150, 150, 150)); } - for (int slot_idx = 0; slot_idx < node->OutputsCount; slot_idx++) + for (int slot_idx = 0; slot_idx < node->OutputsCount; slot_idx++) { draw_list->AddCircleFilled(offset + NodePos::GetOutputSlotPos(nodes, positions, node_idx, slot_idx), NODE_SLOT_RADIUS, ImColor(150, 150, 150, 150)); + } ImGui::PopID(); } @@ -514,10 +523,12 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, } if (open_context_menu) { ImGui::OpenPopup("context_menu"); - if (node_hovered_in_list != -1) + if (node_hovered_in_list != -1) { node_selected = node_hovered_in_list; - if (node_hovered_in_scene != -1) + } + if (node_hovered_in_scene != -1) { node_selected = node_hovered_in_scene; + } } // Scrolling @@ -557,6 +568,4 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImGui::End(); } -} // namespace gui -} // namespace framework -} // namespace o2 +} // namespace o2::framework::gui diff --git a/Framework/Core/include/Framework/FrameworkGUIDevicesGraph.h b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.h similarity index 78% rename from Framework/Core/include/Framework/FrameworkGUIDevicesGraph.h rename to Framework/GUISupport/src/FrameworkGUIDevicesGraph.h index 7842fcdec2343..31c86c7e57140 100644 --- a/Framework/Core/include/Framework/FrameworkGUIDevicesGraph.h +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.h @@ -7,21 +7,17 @@ // In applying this license CERN does 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 FRAMEWORK_FRAMEWORKGUIDEVICEGRAPH_H_ -#define FRAMEWORK_FRAMEWORKGUIDEVICEGRAPH_H_ +#ifndef O2_FRAMEWORK_FRAMEWORKGUIDEVICEGRAPH_H_ +#define O2_FRAMEWORK_FRAMEWORKGUIDEVICEGRAPH_H_ -#include "../../src/DataProcessorInfo.h" +#include "Framework/DataProcessorInfo.h" #include "Framework/DeviceSpec.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" #include <vector> -namespace o2 -{ -namespace framework -{ -namespace gui +namespace o2::framework::gui { class WorkspaceGUIState; @@ -33,8 +29,6 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, std::vector<DeviceControl>& controls, std::vector<DeviceMetricsInfo> const& metricsInfos); -} // namespace gui -} // namespace framework -} // namespace o2 +} // namespace o2::framework::gui -#endif // FRAMEWORK_FRAMEWORKGUIDEVICEGRAPH_H_ +#endif // O2_FRAMEWORK_FRAMEWORKGUIDEVICEGRAPH_H_ diff --git a/Framework/Core/include/Framework/FrameworkGUIState.h b/Framework/GUISupport/src/FrameworkGUIState.h similarity index 100% rename from Framework/Core/include/Framework/FrameworkGUIState.h rename to Framework/GUISupport/src/FrameworkGUIState.h diff --git a/Framework/Core/include/Framework/NoDebugGUI.h b/Framework/GUISupport/src/NoDebugGUI.h similarity index 100% rename from Framework/Core/include/Framework/NoDebugGUI.h rename to Framework/GUISupport/src/NoDebugGUI.h diff --git a/Framework/Core/src/PaletteHelpers.cxx b/Framework/GUISupport/src/PaletteHelpers.cxx similarity index 98% rename from Framework/Core/src/PaletteHelpers.cxx rename to Framework/GUISupport/src/PaletteHelpers.cxx index 57405823bce57..5a0bf13f385b7 100644 --- a/Framework/Core/src/PaletteHelpers.cxx +++ b/Framework/GUISupport/src/PaletteHelpers.cxx @@ -7,7 +7,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. -#include "Framework/PaletteHelpers.h" +#include "PaletteHelpers.h" namespace o2 { diff --git a/Framework/Core/include/Framework/PaletteHelpers.h b/Framework/GUISupport/src/PaletteHelpers.h similarity index 100% rename from Framework/Core/include/Framework/PaletteHelpers.h rename to Framework/GUISupport/src/PaletteHelpers.h diff --git a/Framework/GUISupport/src/Plugin.cxx b/Framework/GUISupport/src/Plugin.cxx new file mode 100644 index 0000000000000..067e90e81ab00 --- /dev/null +++ b/Framework/GUISupport/src/Plugin.cxx @@ -0,0 +1,49 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 __has_include(<DebugGUI/DebugGUI.h>) +#include <DebugGUI/DebugGUI.h> +#else +#pragma message "Building DPL without Debug GUI" +#include "NoDebugGUI.h" +#endif + +#include "Framework/Plugins.h" +#include "Framework/DebugGUI.h" +#include "FrameworkGUIDebugger.h" + +using namespace o2::framework; + +struct ImGUIDebugGUI : o2::framework::DebugGUI { + std::function<void(void)> getGUIDebugger(std::vector<DeviceInfo> const& infos, + std::vector<DeviceSpec> const& devices, + std::vector<DataProcessorInfo> const& metadata, + std::vector<DeviceMetricsInfo> const& metricsInfos, + DriverInfo const& driverInfo, + std::vector<DeviceControl>& controls, + DriverControl& driverControl) override + { + return o2::framework::gui::getGUIDebugger(infos, devices, metadata, metricsInfos, driverInfo, controls, driverControl); + } + + void* initGUI(char const* windowTitle) override + { + return o2::framework::initGUI(windowTitle); + } + bool pollGUI(void* context, std::function<void(void)> guiCallback) override + { + return o2::framework::pollGUI(context, guiCallback); + } + void disposeGUI() override + { + o2::framework::disposeGUI(); + } +}; + +DEFINE_DPL_PLUGIN(ImGUIDebugGUI, DebugGUIImpl); diff --git a/Framework/Core/test/test_CustomGUIGL.cxx b/Framework/GUISupport/test/test_CustomGUIGL.cxx similarity index 100% rename from Framework/Core/test/test_CustomGUIGL.cxx rename to Framework/GUISupport/test/test_CustomGUIGL.cxx diff --git a/Framework/Core/test/test_CustomGUISokol.cxx b/Framework/GUISupport/test/test_CustomGUISokol.cxx similarity index 100% rename from Framework/Core/test/test_CustomGUISokol.cxx rename to Framework/GUISupport/test/test_CustomGUISokol.cxx diff --git a/Framework/Core/test/test_SimpleTracksED.cxx b/Framework/GUISupport/test/test_SimpleTracksED.cxx similarity index 87% rename from Framework/Core/test/test_SimpleTracksED.cxx rename to Framework/GUISupport/test/test_SimpleTracksED.cxx index 57a10996970c9..35d113a0e85a2 100644 --- a/Framework/Core/test/test_SimpleTracksED.cxx +++ b/Framework/GUISupport/test/test_SimpleTracksED.cxx @@ -33,7 +33,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) return WorkflowSpec{ {"trackDisplay", Inputs{ - {"tracks", "AOD", "TRACKPAR"}}, + {"Collisions", "AOD", "COLLISION"}}, Outputs{}, AlgorithmSpec{adaptStateful( [](CallbackService& callbacks) { @@ -51,12 +51,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) [count, window, guiCallback]() { (*count)++; window ? pollGUI(window, guiCallback) : false; }); return adaptStateless([count](InputRecord& inputs, ControlService& control) { - auto input = inputs.get<TableConsumer>("tracks"); + auto input = inputs.get<TableConsumer>("Collisions"); - o2::aod::Tracks myTracks{input->asArrowTable()}; + o2::aod::Collisions myCollisions{{input->asArrowTable()}}; - for (auto& track : myTracks) { - LOGF(info, "CollisionId %d", track.collisionId()); + for (auto& collision : myCollisions) { + LOGF(info, "CollisionId %d", collision.globalIndex()); } if (*count > 1000) { diff --git a/Framework/Logger/include/Framework/Logger.h b/Framework/Logger/include/Framework/Logger.h index 8835c1e46dd43..cf1c7511c850d 100644 --- a/Framework/Logger/include/Framework/Logger.h +++ b/Framework/Logger/include/Framework/Logger.h @@ -10,91 +10,10 @@ #ifndef O2_FRAMEWORK_LOGGER_H_ #define O2_FRAMEWORK_LOGGER_H_ -// FIXME: until we actually have fmt widely available we simply print out the -// format string. -// If FairLogger is not available, we use fmt::printf directly, with no level. -#if __has_include(<fairlogger/Logger.h>) #include <fairlogger/Logger.h> -#if (!defined(LOGP)) && __has_include(<fmt/format.h>) -#include <fmt/format.h> -#include <fmt/printf.h> -FMT_BEGIN_NAMESPACE -#if FMT_VERSION >= 60000 -template <typename S, typename Char = char_t<S>> -inline int vfprintf(fair::Logger& logger, - const S& format, - basic_format_args<basic_printf_context_t<Char>> args) -{ - basic_memory_buffer<Char> buffer; - printf(buffer, to_string_view(format), args); - logger << std::string_view(buffer.data(), buffer.size()); - return static_cast<int>(buffer.size()); -} -template <typename S, typename... Args> -inline enable_if_t<internal::is_string<S>::value, int> - fprintf(fair::Logger& logger, - const S& format_str, const Args&... args) -{ - internal::check_format_string<Args...>(format_str); - using context = basic_printf_context_t<char_t<S>>; - format_arg_store<context, Args...> as{args...}; - return vfprintf(logger, to_string_view(format_str), - basic_format_args<context>(as)); -} -#else -template <typename S, typename Char = FMT_CHAR(S)> -inline int vfprintf(fair::Logger& logger, - const S& format, - basic_format_args<typename basic_printf_context_t< - internal::basic_buffer<Char>>::type> - args) -{ - basic_memory_buffer<Char> buffer; - printf(buffer, to_string_view(format), args); - logger << std::string_view(buffer.data(), buffer.size()); - return static_cast<int>(buffer.size()); -} - -template <typename S, typename... Args> -inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int) - fprintf(fair::Logger& logger, - const S& format_str, const Args&... args) -{ - internal::check_format_string<Args...>(format_str); - typedef internal::basic_buffer<FMT_CHAR(S)> buffer; - typedef typename basic_printf_context_t<buffer>::type context; - format_arg_store<context, Args...> as{args...}; - return vfprintf(logger, to_string_view(format_str), - basic_format_args<context>(as)); -} -#endif -FMT_END_NAMESPACE - -// clang-format off -// Must not add braces since it can break the usage in certain if clauses -#define LOGF(severity, ...) \ - if (fair::Logger::SuppressSeverity(fair::Severity::severity)) ; else \ - for (bool fairLOggerunLikelyvariable = false; fair::Logger::Logging(fair::Severity::severity) && !fairLOggerunLikelyvariable; fairLOggerunLikelyvariable = true) \ - fmt::fprintf(fair::Logger(fair::Severity::severity, __FILE__, CONVERTTOSTRING(__LINE__), __FUNCTION__).Log(), __VA_ARGS__) -// clang-format on -#define LOGP(severity, ...) \ - LOG(severity) << fmt::format(__VA_ARGS__) -#elif (!defined(LOGP)) -#define O2_FIRST_ARG(N, ...) N -#define LOGF(level, ...) LOG(level) << O2_FIRST_ARG(__VA_ARGS__) -#define LOGP(level, ...) LOG(level) << O2_FIRST_ARG(__VA_ARGS__) -#endif #define O2DEBUG(...) LOGF(debug, __VA_ARGS__) #define O2INFO(...) LOGF(info, __VA_ARGS__) #define O2ERROR(...) LOGF(error, __VA_ARGS__) -#elif (!defined(LOGP)) && __has_include(<fmt/format.h>) -#include <fmt/format.h> -#define LOGF(level, ...) fmt::printf(__VA_ARGS__) -#define LOGP(level, ...) fmt::print(__VA_ARGS__) -#define O2DEBUG(...) LOGF("dummy", __VA_ARGS__) -#define O2INFO(...) LOGF("dummy", __VA_ARGS__) -#define O2ERROR(...) LOGF("dummy", __VA_ARGS__) -#endif #endif // O2_FRAMEWORK_LOGGER_H_ diff --git a/Framework/TestWorkflows/CMakeLists.txt b/Framework/TestWorkflows/CMakeLists.txt index f1e0bf0c7d3a4..7ae2e382ce8ec 100644 --- a/Framework/TestWorkflows/CMakeLists.txt +++ b/Framework/TestWorkflows/CMakeLists.txt @@ -78,28 +78,10 @@ o2_add_dpl_workflow(test_CompletionPolicies SOURCES src/test_CompletionPolicies.cxx COMPONENT_NAME TestWorkflows) -o2_data_file(COPY etc/exampleDataSamplingConfig.json DESTINATION etc) - -o2_add_dpl_workflow(datasampling-pod-and-root - SOURCES src/dataSamplingPodAndRoot.cxx - COMPONENT_NAME TestWorkflows) - -o2_add_dpl_workflow(datasampling-parallel - SOURCES src/dataSamplingParallel.cxx - COMPONENT_NAME TestWorkflows) - -o2_add_dpl_workflow(datasampling-time-pipeline - SOURCES src/dataSamplingTimePipeline.cxx - COMPONENT_NAME TestWorkflows) - o2_add_dpl_workflow(ccdb-fetch-to-timeframe SOURCES src/test_CCDBFetchToTimeframe.cxx COMPONENT_NAME TestWorkflows) -o2_add_dpl_workflow(datasampling-benchmark - SOURCES src/dataSamplingBenchmark.cxx - COMPONENT_NAME TestWorkflows) - o2_add_dpl_workflow(simple-source SOURCES src/o2SimpleSource.cxx COMPONENT_NAME TestWorkflows) diff --git a/Framework/TestWorkflows/src/o2D0Analysis.cxx b/Framework/TestWorkflows/src/o2D0Analysis.cxx index 393dcc20e7fa7..728f3ca50a35a 100644 --- a/Framework/TestWorkflows/src/o2D0Analysis.cxx +++ b/Framework/TestWorkflows/src/o2D0Analysis.cxx @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #include "Framework/runDataProcessing.h" #include "Framework/AnalysisHelpers.h" +#include "Framework/RootAnalysisHelpers.h" #include <ROOT/RDataFrame.hxx> diff --git a/Framework/TestWorkflows/src/o2DiamondWorkflow.cxx b/Framework/TestWorkflows/src/o2DiamondWorkflow.cxx index c280248e2496b..34f5f8bfafe01 100644 --- a/Framework/TestWorkflows/src/o2DiamondWorkflow.cxx +++ b/Framework/TestWorkflows/src/o2DiamondWorkflow.cxx @@ -10,6 +10,8 @@ #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DeviceSpec.h" +#include "Framework/RawDeviceService.h" +#include <FairMQDevice.h> #include <InfoLogger/InfoLogger.hxx> #include <chrono> @@ -57,15 +59,15 @@ WorkflowSpec defineDataProcessing(ConfigContext const& specs) {OutputSpec{{"a1"}, "TST", "A1"}, OutputSpec{{"a2"}, "TST", "A2"}}, AlgorithmSpec{adaptStateless( - [](DataAllocator& outputs, InfoLogger& logger) { - std::this_thread::sleep_for(std::chrono::seconds(rand() % 2)); + [](DataAllocator& outputs, InfoLogger& logger, RawDeviceService& device) { + device.device()->WaitFor(std::chrono::seconds(rand() % 2)); auto& aData = outputs.make<int>(OutputRef{"a1"}, 1); auto& bData = outputs.make<int>(OutputRef{"a2"}, 1); logger.log("This goes to infologger"); })}, {ConfigParamSpec{"some-device-param", VariantType::Int, 1, {"Some device parameter"}}}}, {"B", - {InputSpec{"x", "TST", "A1"}}, + {InputSpec{"x", "TST", "A1", Lifetime::Timeframe, {ConfigParamSpec{"somestring", VariantType::String, "", {"Some input param"}}}}}, {OutputSpec{{"b1"}, "TST", "B1"}}, simplePipe("b1", 0)}, {"C", diff --git a/Framework/TestWorkflows/src/o2DummyWorkflow.cxx b/Framework/TestWorkflows/src/o2DummyWorkflow.cxx index 8ae63bbb8d077..ae4c86ade35f2 100644 --- a/Framework/TestWorkflows/src/o2DummyWorkflow.cxx +++ b/Framework/TestWorkflows/src/o2DummyWorkflow.cxx @@ -9,7 +9,7 @@ // or submit itself to any jurisdiction. #include "Framework/DataRefUtils.h" #include "Framework/runDataProcessing.h" -#include <Monitoring/Monitoring.h> +#include "Framework/Monitoring.h" #include "Framework/Logger.h" #include <chrono> diff --git a/Framework/TestWorkflows/src/o2SimpleTracksAnalysis.cxx b/Framework/TestWorkflows/src/o2SimpleTracksAnalysis.cxx index eaa6eb2d081aa..d42924ae64095 100644 --- a/Framework/TestWorkflows/src/o2SimpleTracksAnalysis.cxx +++ b/Framework/TestWorkflows/src/o2SimpleTracksAnalysis.cxx @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #include "Framework/runDataProcessing.h" #include "Framework/AnalysisHelpers.h" +#include "Framework/RootAnalysisHelpers.h" #include "Framework/TableBuilder.h" #include "Framework/AnalysisDataModel.h" @@ -20,9 +21,7 @@ using namespace ROOT::RDF; using namespace o2; using namespace o2::framework; -namespace o2 -{ -namespace aod +namespace o2::aod { namespace tracks { @@ -31,9 +30,7 @@ DECLARE_SOA_COLUMN(Phi, phi, float); } // namespace tracks using TracksDerived = o2::soa::Table<tracks::Eta, tracks::Phi>; - -} // namespace aod -} // namespace o2 +} // namespace o2::aod // A dummy workflow which creates a few of the tables proposed by Ruben, // using ARROW @@ -57,8 +54,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& specs) // in Ruben's table. The first string is just a label so that the // algorithm can be in principle be reused for different kind of // tracks. - InputSpec{"tracks", "AOD", "TRACKPAR"}, - }, + InputSpec{"Tracks", "DYN", "TRACKPAR"}, + InputSpec{"TracksExtension", "AOD", "TRACKPAR"}}, // No outputs for the time being. Outputs{ OutputSpec{{"derived"}, "AOD", "TRACKDERIVED"}}, @@ -68,15 +65,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& specs) // FIXME: Too much boilerplate. adaptStateless([](InputRecord& inputs, DataAllocator& outputs) { /// Get the input from the converter. - auto input = inputs.get<TableConsumer>("tracks"); + auto input1 = inputs.get<TableConsumer>("Tracks"); + auto input2 = inputs.get<TableConsumer>("TracksExtension"); /// Get a table builder to build the results auto& etaPhiBuilder = outputs.make<TableBuilder>(Output{"AOD", "TRACKDERIVED"}); auto etaPhiWriter = etaPhiBuilder.cursor<o2::aod::TracksDerived>(); - /// Documentation for arrow at: - /// - /// https://arrow.apache.org/docs/cpp/namespacearrow.html - auto tracks = aod::Tracks(input->asArrowTable()); + auto tracks = aod::Tracks({input1->asArrowTable(), input2->asArrowTable()}); for (auto& track : tracks) { auto phi = asin(track.snp()) + track.alpha() + M_PI; diff --git a/Framework/TestWorkflows/src/test_o2RootMessageWorkflow.cxx b/Framework/TestWorkflows/src/test_o2RootMessageWorkflow.cxx index 538fc3c39066e..3068e320196ca 100644 --- a/Framework/TestWorkflows/src/test_o2RootMessageWorkflow.cxx +++ b/Framework/TestWorkflows/src/test_o2RootMessageWorkflow.cxx @@ -20,7 +20,7 @@ #include <TClonesArray.h> #include <TH1F.h> #include <TString.h> - +#include <TObjString.h> #include <chrono> using namespace o2::framework; diff --git a/Framework/TestWorkflows/test/test_MakeDPLObjects.cxx b/Framework/TestWorkflows/test/test_MakeDPLObjects.cxx index f39328d7d3a2c..285a0957178b9 100644 --- a/Framework/TestWorkflows/test/test_MakeDPLObjects.cxx +++ b/Framework/TestWorkflows/test/test_MakeDPLObjects.cxx @@ -56,8 +56,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) // simply make a vector of pointers, snapshot will include the latest // change, but not the one which is done after taking the snapshot std::vector<XYZ*> p; - for (auto& i : v) + for (auto& i : v) { p.push_back(&i); + } ctx.outputs().snapshot(Output{"TST", "LINEARIZED"}, p); v[999] = XYZ{3, 4, 5}; diff --git a/Framework/Utils/include/DPLUtils/DPLRawParser.h b/Framework/Utils/include/DPLUtils/DPLRawParser.h index 9548eba2c2b19..a227ebaa753a9 100644 --- a/Framework/Utils/include/DPLUtils/DPLRawParser.h +++ b/Framework/Utils/include/DPLUtils/DPLRawParser.h @@ -19,6 +19,7 @@ #include "Framework/InputRecord.h" #include "Framework/DataRef.h" #include "Framework/DataRefUtils.h" +#include "Framework/Logger.h" #include "Headers/DataHeader.h" #include <utility> // std::declval diff --git a/Framework/Utils/include/DPLUtils/RootTreeReader.h b/Framework/Utils/include/DPLUtils/RootTreeReader.h index d5a20d708c9dd..20d2772d82f6e 100644 --- a/Framework/Utils/include/DPLUtils/RootTreeReader.h +++ b/Framework/Utils/include/DPLUtils/RootTreeReader.h @@ -18,6 +18,8 @@ #include "Framework/RootSerializationSupport.h" #include "Framework/Output.h" #include "Framework/ProcessingContext.h" +#include "Framework/DataAllocator.h" +#include "Framework/Logger.h" #include "Headers/DataHeader.h" #include <TChain.h> #include <TTree.h> @@ -153,6 +155,20 @@ class GenericRootTreeReader /// start over at entry 0 Loop, }; + + /// A struct holding a callback to specialize publishing based on branch name. This might be useful + /// when the read data needs to be transformed before publishing. + /// The hook gets the following input: + /// name: name of branch (may be used for selecting filtering) + /// context: The processing context (so that we can snapshot or publish) + /// Output: The DPL output channel on which to publish + /// data: pointer to the data object read by the TreeReader + /// The hook should return true when publishing succeeded and false when the ordinary publishing procedure + /// should proceed. + struct SpecialPublishHook { + std::function<bool(std::string_view name, ProcessingContext& context, Output const&, char* data)> hook; + }; + // the key must not be of type const char* to make sure that the variable argument // list of the constructor can be parsed static_assert(std::is_same<KeyType, const char*>::value == false, "the key type must not be const char*"); @@ -188,7 +204,7 @@ class GenericRootTreeReader virtual ~BranchConfigurationInterface() = default; /// Setup the branch configuration, namely get the branch and class information from the tree - virtual void setup(TTree&) {} + virtual void setup(TTree&, SpecialPublishHook* h = nullptr) {} /// Run the reader process /// This will recursively run every level of the the branch configuration and fetch the object /// at position \a entry. The object is published via the DPL ProcessingContext. A creator callback @@ -225,20 +241,21 @@ class GenericRootTreeReader /// Setup branch configuration /// This is the virtal overload entry point to the upper most stage of the branch configuration - void setup(TTree& tree) override + void setup(TTree& tree, SpecialPublishHook* publishhook = nullptr) override { - setupInstance(tree); + setupInstance(tree, publishhook); } /// Run the setup, first recursively for all lower stages, and then the current stage /// This fetches the branch corresponding to the configured name - void setupInstance(TTree& tree) + void setupInstance(TTree& tree, SpecialPublishHook* publishhook = nullptr) { // recursing through the tree structure by simply using method of the previous type, // i.e. the base class method. if constexpr (STAGE > 1) { - PrevT::setupInstance(tree); + PrevT::setupInstance(tree, publishhook); } + mPublishHook = publishhook; // right now we allow the same key to appear for multiple branches mBranch = tree.GetBranch(mName.c_str()); @@ -291,30 +308,39 @@ class GenericRootTreeReader char* data = nullptr; mBranch->SetAddress(&data); mBranch->GetEntry(entry); - if (mSizeBranch != nullptr) { - size_t datasize = 0; - mSizeBranch->SetAddress(&datasize); - mSizeBranch->GetEntry(entry); - auto* buffer = reinterpret_cast<BinaryDataStoreType*>(data); - if (buffer->size() == datasize) { - LOG(INFO) << "branch " << mName << ": publishing binary chunk of " << datasize << " bytes(s)"; - snapshot(mKey, std::move(*buffer)); - } else { - LOG(ERROR) << "branch " << mName << ": inconsitent size of binary chunk " - << buffer->size() << " vs " << datasize; - BinaryDataStoreType empty; - snapshot(mKey, empty); - } - } else { - if constexpr (std::is_void<value_type>::value == true) { - // the default branch configuration publishes the object ROOT serialized - snapshot(mKey, std::move(ROOTSerializedByClass(*data, mClassInfo))); + + // execute hook if it was registered; if this return true do not proceed further + if (mPublishHook != nullptr && (*mPublishHook).hook(mName, context, Output{mKey.origin, mKey.description, mKey.subSpec, mKey.lifetime, std::move(stackcreator())}, data)) { + + } + // try to figureout when we need to do something special + else { + if (mSizeBranch != nullptr) { + size_t datasize = 0; + mSizeBranch->SetAddress(&datasize); + mSizeBranch->GetEntry(entry); + auto* buffer = reinterpret_cast<BinaryDataStoreType*>(data); + if (buffer->size() == datasize) { + LOG(INFO) << "branch " << mName << ": publishing binary chunk of " << datasize << " bytes(s)"; + snapshot(mKey, std::move(*buffer)); + } else { + LOG(ERROR) << "branch " << mName << ": inconsitent size of binary chunk " + << buffer->size() << " vs " << datasize; + BinaryDataStoreType empty; + snapshot(mKey, empty); + } } else { - // if type is specified in the branch configuration, the allocator API decides - // upon serialization - snapshot(mKey, *reinterpret_cast<value_type*>(data)); + if constexpr (std::is_void<value_type>::value == true) { + // the default branch configuration publishes the object ROOT serialized + snapshot(mKey, std::move(ROOTSerializedByClass(*data, mClassInfo))); + } else { + // if type is specified in the branch configuration, the allocator API decides + // upon serialization + snapshot(mKey, *reinterpret_cast<value_type*>(data)); + } } } + // cleanup the memory auto* delfunc = mClassInfo->GetDelete(); if (delfunc) { (*delfunc)(data); @@ -328,6 +354,7 @@ class GenericRootTreeReader TBranch* mBranch = nullptr; TBranch* mSizeBranch = nullptr; TClass* mClassInfo = nullptr; + SpecialPublishHook* mPublishHook = nullptr; }; /// branch definition structure @@ -371,7 +398,7 @@ class GenericRootTreeReader { mInput.SetCacheSize(0); parseConstructorArgs<0>(std::forward<Args>(args)...); - mBranchConfiguration->setup(mInput); + mBranchConfiguration->setup(mInput, mPublishHook); } /// add a file as source for the tree @@ -480,6 +507,8 @@ class GenericRootTreeReader mMaxEntries = def; } else if constexpr (std::is_same<U, PublishingMode>::value) { mPublishingMode = def; + } else if constexpr (std::is_same<U, SpecialPublishHook*>::value) { + mPublishHook = def; } else if constexpr (is_specialization<U, BranchDefinition>::value) { cargs.emplace_back(key_type(def.key), def.name); using type = BranchConfigurationElement<typename U::type, BASE>; @@ -521,6 +550,8 @@ class GenericRootTreeReader int mMaxEntries = -1; /// publishing mode PublishingMode mPublishingMode = PublishingMode::Single; + /// special user hook + SpecialPublishHook* mPublishHook = nullptr; }; using RootTreeReader = GenericRootTreeReader<rtr::DefaultKey>; diff --git a/Framework/Utils/include/DPLUtils/RootTreeWriter.h b/Framework/Utils/include/DPLUtils/RootTreeWriter.h index 5161c5c77028c..c6291a651ef7e 100644 --- a/Framework/Utils/include/DPLUtils/RootTreeWriter.h +++ b/Framework/Utils/include/DPLUtils/RootTreeWriter.h @@ -18,6 +18,7 @@ #include "Framework/RootSerializationSupport.h" #include "Framework/InputRecord.h" #include "Framework/DataRef.h" +#include "Framework/Logger.h" #include <TFile.h> #include <TTree.h> #include <TBranch.h> @@ -339,6 +340,31 @@ class RootTreeWriter return (mTreeStructure != nullptr ? mTreeStructure->size() : 0); } + /// A static helper function to change the type of a (yet unused) branch. + /// Removes the old branch from the TTree system and creates a new branch. + /// The function is useful for situations in which we want to transform data after + /// the RootTreeWriter was created and change the type of the branch - such as in user callbacks. + /// The function needs to be used with care. The user should ensure that "branch" is no longer used + /// after a call to this function. + template <typename T> + static TBranch* remapBranch(TBranch& branch, T* newdata) + { + auto name = branch.GetName(); + auto branchleaves = branch.GetListOfLeaves(); + auto tree = branch.GetTree(); + branch.DropBaskets("all"); + branch.DeleteBaskets("all"); + tree->GetListOfBranches()->Remove(&branch); + for (auto entry : *branchleaves) { + tree->GetListOfLeaves()->Remove(entry); + } + // ROOT leaves holes in the arrays so we need to compress as well + tree->GetListOfBranches()->Compress(); + tree->GetListOfLeaves()->Compress(); + // create a new branch with the same original name but attached to the new data + return tree->Branch(name, newdata); + } + private: /// internal input and branch properties struct BranchSpec { @@ -602,20 +628,51 @@ class RootTreeWriter template <typename S, typename std::enable_if_t<std::is_same<S, MessageableVectorSpecialization>::value, int> = 0> void fillData(InputContext& context, DataRef const& ref, TBranch* branch, size_t branchIdx) { - using ValueType = typename value_type::value_type; - static_assert(is_messageable<ValueType>::value, "logical error: should be correctly selected by StructureElementTypeTrait"); + using ElementType = typename value_type::value_type; + static_assert(is_messageable<ElementType>::value, "logical error: should be correctly selected by StructureElementTypeTrait"); + + // A helper struct mimicking data layout of std::vector containers + // We assume a standard layout of begin, end, end_capacity + struct VecBase { + VecBase() = default; + const ElementType* start = nullptr; + const ElementType* end = nullptr; + const ElementType* cap = nullptr; + }; + + // a low level hack to make a gsl::span appear as a std::vector so that ROOT serializes the correct type + // but without the need for an extra copy + auto adopt = [](auto const& data, value_type& v) { + static_assert(sizeof(v) == 24); + if (data.size() == 0) { + return; + } + VecBase impl; + impl.start = &(data[0]); + impl.end = &(data[data.size() - 1]) + 1; // end pointer (beyond last element) + impl.cap = impl.end; + std::memcpy(&v, &impl, sizeof(VecBase)); + }; + // if the value type is messagable and has a ROOT dictionary, two serialization methods are possible // for the moment, the InputRecord API can not handle both with the same call try { // try extracting from message with serialization method NONE, throw runtime error // if message is serialized - auto data = context.get<gsl::span<ValueType>>(ref); - value_type clone(data.begin(), data.end()); - if (!runCallback(branch, clone, ref)) { - mStore[branchIdx] = &clone; + auto data = context.get<gsl::span<ElementType>>(ref); + // take an ordinary std::vector "view" on the data + auto* dataview = new value_type; + adopt(data, *dataview); + if (!runCallback(branch, *dataview, ref)) { + mStore[branchIdx] = dataview; branch->Fill(); } - } catch (const std::runtime_error& e) { + // we delete JUST the view without deleting the data (which is handled by DPL) + auto ptr = (VecBase*)dataview; + if (ptr) { + delete ptr; + } + } catch (RuntimeErrorRef e) { if constexpr (has_root_dictionary<value_type>::value == true) { // try extracting from message with serialization method ROOT auto data = context.get<typename std::add_pointer<value_type>::type>(ref); diff --git a/Framework/Utils/src/dpl-output-proxy.cxx b/Framework/Utils/src/dpl-output-proxy.cxx index 700f81c380efb..d7941cc520778 100644 --- a/Framework/Utils/src/dpl-output-proxy.cxx +++ b/Framework/Utils/src/dpl-output-proxy.cxx @@ -63,7 +63,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) // vectored options // use the OutputChannelSpec as a tool to create the default configuration for the out-of-band channel OutputChannelSpec externalChannelSpec; - externalChannelSpec.name = inputs[0].binding; + externalChannelSpec.name = "downstream"; externalChannelSpec.type = ChannelType::Push; externalChannelSpec.method = ChannelMethod::Bind; externalChannelSpec.hostname = "localhost"; diff --git a/GPU/CMakeLists.txt b/GPU/CMakeLists.txt index e2ba9741f3508..03b9b7d00427f 100644 --- a/GPU/CMakeLists.txt +++ b/GPU/CMakeLists.txt @@ -21,6 +21,3 @@ add_subdirectory(Common) add_subdirectory(Utils) add_subdirectory(TPCFastTransformation) add_subdirectory(GPUTracking) -if(NOT ALIGPU_BUILD_TYPE STREQUAL "Standalone") - add_subdirectory(TPCSpaceChargeBase) -endif() diff --git a/GPU/Common/CMakeLists.txt b/GPU/Common/CMakeLists.txt index 8cff6021f2cc3..57f0f083e9f8f 100644 --- a/GPU/Common/CMakeLists.txt +++ b/GPU/Common/CMakeLists.txt @@ -18,13 +18,15 @@ set(HDRS_INSTALL GPUCommonLogger.h GPUCommonMath.h GPUCommonRtypes.h + GPUCommonArray.h GPUCommonTransform3D.h GPUDef.h GPUDefConstantsAndSettings.h GPUDefGPUParameters.h GPUDefOpenCL12Templates.h GPULogging.h - GPUDefMacros.h) + GPUDefMacros.h + GPUROOTCartesianFwd.h) if(ALIGPU_BUILD_TYPE STREQUAL "O2") o2_add_library(${MODULE} @@ -42,7 +44,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") if(CUDA_ENABLED) o2_add_test(GPUsortCUDA NAME test_GPUsortCUDA SOURCES test/testGPUsortCUDA.cu - PUBLIC_LINK_LIBRARIES O2::${MODULE} cub::cub + PUBLIC_LINK_LIBRARIES O2::${MODULE} COMPONENT_NAME GPU LABELS gpu) endif() diff --git a/GPU/Common/GPUCommonAlgorithm.h b/GPU/Common/GPUCommonAlgorithm.h index 7464f1e941d50..13b4774d6998f 100644 --- a/GPU/Common/GPUCommonAlgorithm.h +++ b/GPU/Common/GPUCommonAlgorithm.h @@ -114,32 +114,37 @@ GPUdi() I GPUCommonAlgorithm::MedianOf3Select(I f, I l, Cmp cmp) noexcept --l; if (cmp(*f, *m)) { - if (cmp(*m, *l)) + if (cmp(*m, *l)) { return m; - else if (cmp(*f, *l)) + } else if (cmp(*f, *l)) { return l; - else + } else { return f; - } else if (cmp(*f, *l)) + } + } else if (cmp(*f, *l)) { return f; - else if (cmp(*m, *l)) + } else if (cmp(*m, *l)) { return l; - else + } else { return m; + } } template <typename I, typename T, typename Cmp> GPUdi() I GPUCommonAlgorithm::UnguardedPartition(I f, I l, T piv, Cmp cmp) noexcept { do { - while (cmp(*f, piv)) + while (cmp(*f, piv)) { ++f; + } --l; - while (cmp(piv, *l)) + while (cmp(piv, *l)) { --l; + } - if (l <= f) + if (l <= f) { return f; + } IterSwap(f, l); ++f; } while (true); @@ -148,10 +153,9 @@ GPUdi() I GPUCommonAlgorithm::UnguardedPartition(I f, I l, T piv, Cmp cmp) noexc template <typename I, typename Cmp> GPUdi() void GPUCommonAlgorithm::QuickSort(I f, I l, Cmp cmp) noexcept { - if (f == l) - + if (f == l) { return; - + } using IndexType = unsigned short; struct pair { @@ -186,15 +190,19 @@ GPUdi() void GPUCommonAlgorithm::QuickSort(I f, I l, Cmp cmp) noexcept const auto lsz = pp - it0; const auto rsz = it1 - pp; if (lsz < rsz) { - if (rsz > cutoff) + if (rsz > cutoff) { s.emplace(pp - f, it1 - f); - if (lsz > cutoff) + } + if (lsz > cutoff) { s.emplace(it0 - f, pp - f); + } } else { - if (lsz > cutoff) + if (lsz > cutoff) { s.emplace(it0 - f, pp - f); - if (rsz > cutoff) + } + if (rsz > cutoff) { s.emplace(pp - f, it1 - f); + } } } InsertionSort(f, l, cmp); @@ -212,7 +220,7 @@ typedef GPUCommonAlgorithm CAAlgo; } // namespace gpu } // namespace GPUCA_NAMESPACE -#if (defined(__CUDACC__) && !defined(__clang__)) || defined(__HIPCC__) +#if ((defined(__CUDACC__) && !defined(__clang__)) || defined(__HIPCC__)) #include "GPUCommonAlgorithmThrust.h" @@ -332,14 +340,16 @@ GPUdi() void GPUCommonAlgorithm::swap(T& a, T& b) #ifdef __OPENCL__ // Nothing to do, work_group functions available -#elif defined(__CUDACC__) || defined(__HIPCC__) +#elif (defined(__CUDACC__) || defined(__HIPCC__)) // CUDA and HIP work the same way using cub, need just different header +#ifndef GPUCA_GPUCODE_GENRTC #if defined(__CUDACC__) #include <cub/cub.cuh> #elif defined(__HIPCC__) #include <hipcub/hipcub.hpp> #endif +#endif #define work_group_scan_inclusive_add(v) work_group_scan_inclusive_add_FUNC(v, smem) template <class T, class S> @@ -363,6 +373,16 @@ GPUdi() T work_group_broadcast_FUNC(T v, int i, S& smem) return retVal; } +#define work_group_reduce_add(v) work_group_reduce_add_FUNC(v, smem) +template <class T, class S> +GPUdi() T work_group_reduce_add_FUNC(T v, S& smem) +{ + v = typename S::BlockReduce(smem.cubReduceTmpMem).Sum(v); + __syncthreads(); + v = work_group_broadcast(v, 0); + return v; +} + #define warp_scan_inclusive_add(v) warp_scan_inclusive_add_FUNC(v, smem) template <class T, class S> GPUdi() T warp_scan_inclusive_add_FUNC(T v, S& smem) @@ -380,6 +400,12 @@ GPUdi() T work_group_scan_inclusive_add(T v) return v; } +template <class T> +GPUdi() T work_group_reduce_add(T v) +{ + return v; +} + template <class T> GPUdi() T work_group_broadcast(T v, int i) { diff --git a/GPU/Common/GPUCommonAlgorithmThrust.h b/GPU/Common/GPUCommonAlgorithmThrust.h index cd5e4227bace5..b4fdfacde3d85 100644 --- a/GPU/Common/GPUCommonAlgorithmThrust.h +++ b/GPU/Common/GPUCommonAlgorithmThrust.h @@ -14,12 +14,14 @@ #ifndef GPUCOMMONALGORITHMTHRUST_H #define GPUCOMMONALGORITHMTHRUST_H +#ifndef GPUCA_GPUCODE_GENRTC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" #include <thrust/sort.h> #include <thrust/execution_policy.h> #include <thrust/device_ptr.h> #pragma GCC diagnostic pop +#endif #include "GPUCommonDef.h" diff --git a/GPU/Common/GPUCommonArray.h b/GPU/Common/GPUCommonArray.h new file mode 100644 index 0000000000000..c70443bfc13a9 --- /dev/null +++ b/GPU/Common/GPUCommonArray.h @@ -0,0 +1,37 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUCommonArray.h +/// \author David Rohr + +#ifndef GPUCOMMONFAIRARRAY_H +#define GPUCOMMONFAIRARRAY_H + +#ifndef GPUCA_GPUCODE_DEVICE +#include <array> +#endif + +#include "GPUCommonDef.h" +namespace o2::gpu::gpustd +{ +#ifdef GPUCA_GPUCODE_DEVICE +template <typename T, size_t N> +struct array { + GPUd() T& operator[](size_t i) { return m_internal_V__[i]; } + GPUd() const T& operator[](size_t i) const { return m_internal_V__[i]; } + T m_internal_V__[N]; +}; +#else +template <typename T, size_t N> +using array = std::array<T, N>; +#endif +} // namespace o2::gpu::gpustd + +#endif diff --git a/GPU/Common/GPUCommonDef.h b/GPU/Common/GPUCommonDef.h index 73c576144faf7..3adf1a81c21a8 100644 --- a/GPU/Common/GPUCommonDef.h +++ b/GPU/Common/GPUCommonDef.h @@ -56,17 +56,14 @@ #define CONSTEXPR constexpr #define CONSTEXPRRET CONSTEXPR #if defined(__cplusplus) && __cplusplus >= 201703L - #define CONSTEXPRIF if constexpr #define CONSTEXPR17 constexpr #else - #define CONSTEXPRIF if #define CONSTEXPR17 #endif #else #define CON_DELETE #define CON_DEFAULT #define CONSTEXPR const - #define CONSTEXPRIF if #define CONSTEXPR17 #define CONSTEXPRRET #endif @@ -77,7 +74,7 @@ #endif //Set AliRoot / O2 namespace -#if defined(GPUCA_STANDALONE) || (defined(GPUCA_O2_LIB) && !defined(GPUCA_O2_INTERFACE)) || defined(GPUCA_ALIROOT_LIB) || defined(GPUCA_GPULIBRARY) +#if defined(GPUCA_STANDALONE) || (defined(GPUCA_O2_LIB) && !defined(GPUCA_O2_INTERFACE)) || defined(GPUCA_ALIROOT_LIB) || defined(GPUCA_GPULIBRARY) || defined (GPUCA_GPUCODE) #define GPUCA_ALIGPUCODE #endif #ifdef GPUCA_ALIROOT_LIB diff --git a/GPU/Common/GPUCommonDefAPI.h b/GPU/Common/GPUCommonDefAPI.h index bbfffa4d5a314..576d03cd897a1 100644 --- a/GPU/Common/GPUCommonDefAPI.h +++ b/GPU/Common/GPUCommonDefAPI.h @@ -45,26 +45,29 @@ #define GPUprivate() // private memory variable declaration #define GPUgeneric() // reference / ptr to generic address space #define GPUbarrier() // synchronize all GPU threads in block + #define GPUbarrierWarp() // synchronize threads inside warp #define GPUAtomic(type) type // atomic variable type #define GPUsharedref() // reference / ptr to shared memory #define GPUglobalref() // reference / ptr to global memory #define GPUconstantref() // reference / ptr to constant memory #define GPUconstexprref() // reference / ptr to variable declared as GPUconstexpr() - struct float4 { float x, y, z, w; }; - struct float3 { float x, y, z; }; - struct float2 { float x; float y; }; - struct uchar2 { unsigned char x, y; }; - struct short2 { short x, y; }; - struct ushort2 { unsigned short x, y; }; - struct int2 { int x, y; }; - struct int3 { int x, y, z; }; - struct int4 { int x, y, z, w; }; - struct uint1 { unsigned int x; }; - struct uint2 { unsigned int x, y; }; - struct uint3 { unsigned int x, y, z; }; - struct uint4 { unsigned int x, y, z, w; }; - struct dim3 { unsigned int x, y, z; }; + #ifndef __VECTOR_TYPES_H__ // ROOT will pull in these CUDA definitions if built against CUDA, so we have to add an ugly protection here + struct float4 { float x, y, z, w; }; + struct float3 { float x, y, z; }; + struct float2 { float x; float y; }; + struct uchar2 { unsigned char x, y; }; + struct short2 { short x, y; }; + struct ushort2 { unsigned short x, y; }; + struct int2 { int x, y; }; + struct int3 { int x, y, z; }; + struct int4 { int x, y, z, w; }; + struct uint1 { unsigned int x; }; + struct uint2 { unsigned int x, y; }; + struct uint3 { unsigned int x, y, z; }; + struct uint4 { unsigned int x, y, z, w; }; + struct dim3 { unsigned int x, y, z; }; + #endif #elif defined(__OPENCL__) // Defines for OpenCL #define GPUd() #define GPUdDefault() @@ -87,10 +90,12 @@ #define GPUconstexprref() GPUconstexpr() #if defined(__OPENCLCPP__) && !defined(__clang__) #define GPUbarrier() work_group_barrier(mem_fence::global | mem_fence::local); + #define GPUbarrierWarp() #define GPUAtomic(type) atomic<type> static_assert(sizeof(atomic<unsigned int>) == sizeof(unsigned int), "Invalid size of atomic type"); #else #define GPUbarrier() barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE) + #define GPUbarrierWarp() #if defined(__OPENCLCPP__) && defined(GPUCA_OPENCL_CPP_CLANG_C11_ATOMICS) namespace GPUCA_NAMESPACE { namespace gpu { template <class T> struct oclAtomic; @@ -144,10 +149,11 @@ #define GPUglobal() #endif #define GPUconstant() __constant__ - #define GPUconstexpr() __constant__ + #define GPUconstexpr() constexpr __constant__ #define GPUprivate() #define GPUgeneric() #define GPUbarrier() __syncthreads() + #define GPUbarrierWarp() #define GPUAtomic(type) type #elif defined(__CUDACC__) //Defines for CUDA #define GPUd() __device__ @@ -169,9 +175,17 @@ #define GPUprivate() #define GPUgeneric() #define GPUbarrier() __syncthreads() + #define GPUbarrierWarp() __syncwarp() #define GPUAtomic(type) type #endif +#ifndef GPUdic // Takes different parameter for inlining: 0 = never, 1 = always, 2 = compiler-decision +#define GPUdic(...) GPUd() +#endif +#define GPUCA_GPUdic_select_0() GPUdni() +#define GPUCA_GPUdic_select_1() GPUdii() +#define GPUCA_GPUdic_select_2() GPUd() + #ifdef GPUCA_CONSTANT_AS_ARGUMENT #undef GPUconstant #define GPUconstant() diff --git a/GPU/Common/GPUCommonLogger.h b/GPU/Common/GPUCommonLogger.h index c3e93414371d3..5cabc470ad865 100644 --- a/GPU/Common/GPUCommonLogger.h +++ b/GPU/Common/GPUCommonLogger.h @@ -14,15 +14,42 @@ #ifndef GPUCOMMONFAIRLOGGER_H #define GPUCOMMONFAIRLOGGER_H +#include "GPUCommonDef.h" + +#if defined(GPUCA_GPUCODE_DEVICE) || defined(__HIPCC__) +namespace o2::gpu::detail +{ +struct DummyLogger { + template <typename... Args> + GPUhd() DummyLogger& operator<<(Args... args) + { + return *this; + } +}; +} // namespace o2::gpu::detail +#endif + #if defined(__OPENCL__) -#define LOG(...) +#define LOG(...) o2::gpu::detail::DummyLogger() #define LOGF(...) +#define LOGP(...) + +#elif defined(GPUCA_GPUCODE_DEVICE) || defined(__HIPCC__) +#define LOG(...) o2::gpu::detail::DummyLogger() +//#define LOG(...) static_assert(false, "LOG(...) << ... unsupported in GPU code"); +#define LOGF(type, string, ...) \ + { \ + printf(string "\n", ##__VA_ARGS__); \ + } +#define LOGP(type, string, ...) \ + { \ + printf(string "\n"); \ + } + #elif defined(GPUCA_STANDALONE) || \ defined(GPUCA_ALIROOT_LIB) || \ - defined(GPUCA_GPUCODE_DEVICE) || \ (!defined(__cplusplus) || __cplusplus < 201703L) || \ (defined(__HIPCC__) && (!defined(_GLIBCXX_USE_CXX11_ABI) || _GLIBCXX_USE_CXX11_ABI == 0)) - #include <iostream> #include <cstdio> #define LOG(type) std::cout @@ -30,9 +57,12 @@ { \ printf(string "\n", ##__VA_ARGS__); \ } +#define LOGP(type, string, ...) \ + { \ + printf(string "\n"); \ + } #else - #include <Framework/Logger.h> #endif diff --git a/GPU/Common/GPUCommonMath.h b/GPU/Common/GPUCommonMath.h index 73fbe5cb9dac0..22b33479b952d 100644 --- a/GPU/Common/GPUCommonMath.h +++ b/GPU/Common/GPUCommonMath.h @@ -16,11 +16,11 @@ #include "GPUCommonDef.h" -#if defined(__CUDACC__) && !defined(__clang__) +#if defined(__CUDACC__) && !defined(__clang__) && !defined(GPUCA_GPUCODE_GENRTC) #include <sm_20_atomic_functions.h> #endif -#if !defined(__OPENCL__) +#if !defined(GPUCA_GPUCODE_DEVICE) #include <cmath> #include <algorithm> #endif @@ -35,39 +35,45 @@ namespace gpu class GPUCommonMath { public: - GPUhdni() static float2 MakeFloat2(float x, float y); // TODO: Find better appraoch that is constexpr + GPUd() static float2 MakeFloat2(float x, float y); // TODO: Find better appraoch that is constexpr template <class T> GPUhd() static T Min(const T x, const T y); template <class T> GPUhd() static T Max(const T x, const T y); template <class T, class S, class R> - GPUhd() static T MinWithRef(T x, T y, S refX, S refY, R& r); + GPUd() static T MinWithRef(T x, T y, S refX, S refY, R& r); template <class T, class S, class R> - GPUhd() static T MaxWithRef(T x, T y, S refX, S refY, R& r); + GPUd() static T MaxWithRef(T x, T y, S refX, S refY, R& r); template <class T, class S, class R> - GPUhd() static T MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, S refW, R& r); + GPUd() static T MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, S refW, R& r); + template <class T> + GPUdi() static T Clamp(const T v, const T lo, const T hi) + { + return Max(lo, Min(v, hi)); + } GPUhdni() static float Sqrt(float x); - GPUhdni() static float FastInvSqrt(float x); + GPUd() static float FastInvSqrt(float x); template <class T> GPUhd() static T Abs(T x); - GPUhdni() static float ASin(float x); - GPUhdni() static float ATan(float x); - GPUhdni() static float ATan2(float y, float x); - GPUhdni() static float Sin(float x); - GPUhdni() static float Cos(float x); + GPUd() static float ASin(float x); + GPUd() static float ATan(float x); + GPUd() static float ATan2(float y, float x); + GPUd() static float Sin(float x); + GPUd() static float Cos(float x); GPUhdni() static void SinCos(float x, float& s, float& c); - GPUhdni() static void SinCos(double x, double& s, double& c); - GPUhdni() static float Tan(float x); + GPUhdni() static void SinCosd(double x, double& s, double& c); + GPUd() static float Tan(float x); GPUhdni() static float Copysign(float x, float y); - GPUhdni() static float TwoPi() { return 6.28319f; } - GPUhdni() static float Pi() { return 3.1415926535897f; } - GPUhdni() static int Nint(float x); - GPUhdni() static bool Finite(float x); - GPUhdni() static unsigned int Clz(unsigned int val); - GPUhdni() static unsigned int Popcount(unsigned int val); - - GPUhdni() static float Log(float x); + GPUhdni() static double Copysignd(double x, double y); + GPUd() static float TwoPi() { return 6.28319f; } + GPUd() static float Pi() { return 3.1415926535897f; } + GPUd() static int Nint(float x); + GPUd() static bool Finite(float x); + GPUd() static unsigned int Clz(unsigned int val); + GPUd() static unsigned int Popcount(unsigned int val); + + GPUd() static float Log(float x); template <class T> GPUdi() static T AtomicExch(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val) { @@ -141,6 +147,23 @@ class GPUCommonMath template <int I, class T> GPUd() CONSTEXPR17 static T nextMultipleOf(T val); +#ifdef GPUCA_NOCOMPAT + GPUdi() static float Sum2() // Needed for legacy C++, For >=17 the below if constexpr handles the case + { + return 0.f; + } + + template <typename... Args> + GPUdi() static float Sum2(float w, Args... args) + { + if CONSTEXPR17 (sizeof...(Args) == 0) { + return w * w; + } else { + return w * w + Sum2(args...); + } + } +#endif + private: template <class S, class T> GPUd() static unsigned int AtomicExchInt(S* addr, T val); @@ -167,20 +190,19 @@ typedef GPUCommonMath CAMath; template <int I, class T> GPUdi() CONSTEXPR17 T GPUCommonMath::nextMultipleOf(T val) { - CONSTEXPRIF(I & (I - 1)) - { + if CONSTEXPR17 (I & (I - 1)) { T tmp = val % I; - if (tmp) + if (tmp) { val += I - tmp; + } return val; - } - else - { + } else { return (val + I - 1) & ~(T)(I - 1); } + return 0; // BUG: Cuda complains about missing return value with constexpr if } -GPUhdi() float2 GPUCommonMath::MakeFloat2(float x, float y) +GPUdi() float2 GPUCommonMath::MakeFloat2(float x, float y) { #if !defined(GPUCA_GPUCODE) || defined(__OPENCL__) || defined(__OPENCL_HOST__) float2 ret = {x, y}; @@ -190,56 +212,58 @@ GPUhdi() float2 GPUCommonMath::MakeFloat2(float x, float y) #endif // GPUCA_GPUCODE } -GPUhdi() int GPUCommonMath::Nint(float x) +GPUdi() int GPUCommonMath::Nint(float x) { int i; if (x >= 0) { i = int(x + 0.5f); - if (x + 0.5f == float(i) && i & 1) + if (x + 0.5f == float(i) && i & 1) { i--; + } } else { i = int(x - 0.5f); - if (x - 0.5f == float(i) && i & 1) + if (x - 0.5f == float(i) && i & 1) { i++; + } } return i; } -GPUhdi() bool GPUCommonMath::Finite(float x) { return CHOICE(std::isfinite(x), true, true); } +GPUdi() bool GPUCommonMath::Finite(float x) { return CHOICE(std::isfinite(x), true, true); } -GPUhdi() float GPUCommonMath::ATan(float x) { return CHOICE(atanf(x), atanf(x), atan(x)); } +GPUdi() float GPUCommonMath::ATan(float x) { return CHOICE(atanf(x), atanf(x), atan(x)); } -GPUhdi() float GPUCommonMath::ATan2(float y, float x) { return CHOICE(atan2f(y, x), atan2f(y, x), atan2(y, x)); } +GPUdi() float GPUCommonMath::ATan2(float y, float x) { return CHOICE(atan2f(y, x), atan2f(y, x), atan2(y, x)); } -GPUhdi() float GPUCommonMath::Sin(float x) { return CHOICE(sinf(x), sinf(x), sin(x)); } +GPUdi() float GPUCommonMath::Sin(float x) { return CHOICE(sinf(x), sinf(x), sin(x)); } -GPUhdi() float GPUCommonMath::Cos(float x) { return CHOICE(cosf(x), cosf(x), cos(x)); } +GPUdi() float GPUCommonMath::Cos(float x) { return CHOICE(cosf(x), cosf(x), cos(x)); } GPUhdi() void GPUCommonMath::SinCos(float x, float& s, float& c) { #if !defined(GPUCA_GPUCODE_DEVICE) && defined(__APPLE__) __sincosf(x, &s, &c); -#elif !defined(GPUCA_GPUCODE_DEVICE) && defined(__GNU_SOURCE__) +#elif !defined(GPUCA_GPUCODE_DEVICE) && (defined(__GNU_SOURCE__) || defined(_GNU_SOURCE) || defined(GPUCA_GPUCODE)) sincosf(x, &s, &c); #else - CHOICE({s = sin(x); c = cos(x); }, sincosf(x, &s, &c), s = sincos(x, &c)); + CHOICE((void)((s = sinf(x)) + (c = cosf(x))), sincosf(x, &s, &c), s = sincos(x, &c)); #endif } -GPUhdi() void GPUCommonMath::SinCos(double x, double& s, double& c) +GPUhdi() void GPUCommonMath::SinCosd(double x, double& s, double& c) { #if !defined(GPUCA_GPUCODE_DEVICE) && defined(__APPLE__) __sincos(x, &s, &c); -#elif !defined(GPUCA_GPUCODE_DEVICE) && defined(__GNU_SOURCE__) +#elif !defined(GPUCA_GPUCODE_DEVICE) && (defined(__GNU_SOURCE__) || defined(_GNU_SOURCE) || defined(GPUCA_GPUCODE)) sincos(x, &s, &c); #else - CHOICE({s = sin(x); c = cos(x); }, sincos(x, &s, &c), s = sincos(x, &c)); + CHOICE((void)((s = sin(x)) + (c = cos(x))), sincos(x, &s, &c), s = sincos(x, &c)); #endif } -GPUhdi() float GPUCommonMath::Tan(float x) { return CHOICE(tanf(x), tanf(x), tan(x)); } +GPUdi() float GPUCommonMath::Tan(float x) { return CHOICE(tanf(x), tanf(x), tan(x)); } -GPUhdi() unsigned int GPUCommonMath::Clz(unsigned int x) +GPUdi() unsigned int GPUCommonMath::Clz(unsigned int x) { #if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && (!defined(__OPENCL__) || defined(__OPENCLCPP__)) return x == 0 ? 32 : CHOICE(__builtin_clz(x), __clz(x), __builtin_clz(x)); // use builtin if available @@ -253,10 +277,11 @@ GPUhdi() unsigned int GPUCommonMath::Clz(unsigned int x) #endif } -GPUhdi() unsigned int GPUCommonMath::Popcount(unsigned int x) +GPUdi() unsigned int GPUCommonMath::Popcount(unsigned int x) { #if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && (!defined(__OPENCL__) /*|| defined(__OPENCLCPP__)*/) // TODO: remove OPENCLCPP workaround when reported SPIR-V bug is fixed - return CHOICE(__builtin_popcount(x), __popc(x), __builtin_popcount(x)); // use builtin if available + // use builtin if available + return CHOICE(__builtin_popcount(x), __popc(x), __builtin_popcount(x)); #else unsigned int retVal = 0; for (int i = 0; i < 32; i++) { @@ -271,17 +296,17 @@ GPUhdi() unsigned int GPUCommonMath::Popcount(unsigned int x) template <class T> GPUhdi() T GPUCommonMath::Min(const T x, const T y) { - return CHOICE(std::min(x, y), std::min(x, y), (x < y ? x : y)); + return CHOICE(std::min(x, y), min(x, y), (x < y ? x : y)); } template <class T> GPUhdi() T GPUCommonMath::Max(const T x, const T y) { - return CHOICE(std::max(x, y), std::max(x, y), (x > y ? x : y)); + return CHOICE(std::max(x, y), max(x, y), (x > y ? x : y)); } template <class T, class S, class R> -GPUhdi() T GPUCommonMath::MinWithRef(T x, T y, S refX, S refY, R& r) +GPUdi() T GPUCommonMath::MinWithRef(T x, T y, S refX, S refY, R& r) { if (x < y) { r = refX; @@ -292,7 +317,7 @@ GPUhdi() T GPUCommonMath::MinWithRef(T x, T y, S refX, S refY, R& r) } template <class T, class S, class R> -GPUhdi() T GPUCommonMath::MaxWithRef(T x, T y, S refX, S refY, R& r) +GPUdi() T GPUCommonMath::MaxWithRef(T x, T y, S refX, S refY, R& r) { if (x > y) { r = refX; @@ -303,7 +328,7 @@ GPUhdi() T GPUCommonMath::MaxWithRef(T x, T y, S refX, S refY, R& r) } template <class T, class S, class R> -GPUhdi() T GPUCommonMath::MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, S refW, R& r) +GPUdi() T GPUCommonMath::MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, S refW, R& r) { T retVal = x; S retRef = refX; @@ -325,7 +350,7 @@ GPUhdi() T GPUCommonMath::MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, GPUhdi() float GPUCommonMath::Sqrt(float x) { return CHOICE(sqrtf(x), sqrtf(x), sqrt(x)); } -GPUhdi() float GPUCommonMath::FastInvSqrt(float _x) +GPUdi() float GPUCommonMath::FastInvSqrt(float _x) { // the function calculates fast inverse sqrt union { @@ -358,9 +383,9 @@ GPUhdi() int GPUCommonMath::Abs<int>(int x) return CHOICE(abs(x), abs(x), abs(x)); } -GPUhdi() float GPUCommonMath::ASin(float x) { return CHOICE(asinf(x), asinf(x), asin(x)); } +GPUdi() float GPUCommonMath::ASin(float x) { return CHOICE(asinf(x), asinf(x), asin(x)); } -GPUhdi() float GPUCommonMath::Log(float x) { return CHOICE(logf(x), logf(x), log(x)); } +GPUdi() float GPUCommonMath::Log(float x) { return CHOICE(logf(x), logf(x), log(x)); } GPUhdi() float GPUCommonMath::Copysign(float x, float y) { @@ -376,6 +401,20 @@ GPUhdi() float GPUCommonMath::Copysign(float x, float y) #endif // GPUCA_GPUCODE } +GPUhdi() double GPUCommonMath::Copysignd(double x, double y) +{ +#if defined(__OPENCLCPP__) + return copysign(x, y); +#elif defined(GPUCA_GPUCODE) && !defined(__OPENCL__) + return copysignf(x, y); +#elif defined(__cplusplus) && __cplusplus >= 201103L + return std::copysign(x, y); +#else + x = GPUCommonMath::Abs(x); + return (y >= 0) ? x : -x; +#endif // GPUCA_GPUCODE +} + template <class S, class T> GPUdi() unsigned int GPUCommonMath::AtomicExchInt(S* addr, T val) { @@ -473,6 +512,34 @@ GPUdi() void GPUCommonMath::AtomicMinInt(S* addr, T val) #endif // GPUCA_GPUCODE } +#if (defined(__CUDACC__) || defined(__HIPCC__)) && !defined(__ROOTCINT__) && !defined(G__ROOT) +#define GPUCA_HAVE_ATOMIC_MINMAX_FLOAT +template <> +GPUdii() void GPUCommonMath::AtomicMaxInt(GPUglobalref() GPUgeneric() GPUAtomic(float) * addr, float val) +{ + if (val == -0.f) { + val = 0.f; + } + if (val >= 0) { + AtomicMaxInt((GPUAtomic(int)*)addr, __float_as_int(val)); + } else { + AtomicMinInt((GPUAtomic(unsigned int)*)addr, __float_as_uint(val)); + } +} +template <> +GPUdii() void GPUCommonMath::AtomicMinInt(GPUglobalref() GPUgeneric() GPUAtomic(float) * addr, float val) +{ + if (val == -0.f) { + val = 0.f; + } + if (val >= 0) { + AtomicMinInt((GPUAtomic(int)*)addr, __float_as_int(val)); + } else { + AtomicMaxInt((GPUAtomic(unsigned int)*)addr, __float_as_uint(val)); + } +} +#endif + #undef CHOICE #if !defined(__OPENCL__) || defined(__OPENCLCPP__) diff --git a/GPU/Common/GPUDefConstantsAndSettings.h b/GPU/Common/GPUDefConstantsAndSettings.h index fc6833b402364..e298cb51e8631 100644 --- a/GPU/Common/GPUDefConstantsAndSettings.h +++ b/GPU/Common/GPUDefConstantsAndSettings.h @@ -19,9 +19,7 @@ // clang-format off -#ifndef GPUDEF_H - #error Please include GPUDef.h -#endif +#include "GPUCommonDef.h" #if !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) && !defined(GPUCA_O2_LIB) && !defined(GPUCA_O2_INTERFACE) #error You are using the CA GPU tracking without defining the build type (O2/AliRoot/Standalone). If you are running an O2 ROOT macro, please include GPUO2Interface.h first! @@ -63,8 +61,8 @@ #if defined(HAVE_O2HEADERS) && (!defined(__OPENCL__) || defined(__OPENCLCPP__)) && !(defined(ROOT_VERSION_CODE) && ROOT_VERSION_CODE < 393216) //Use definitions from the O2 headers if available for nicer code and type safety #include "DataFormatsTPC/Constants.h" - #define GPUCA_NSLICES o2::tpc::Constants::MAXSECTOR - #define GPUCA_ROW_COUNT o2::tpc::Constants::MAXGLOBALPADROW + #define GPUCA_NSLICES o2::tpc::constants::MAXSECTOR + #define GPUCA_ROW_COUNT o2::tpc::constants::MAXGLOBALPADROW #else //Define it manually, if O2 headers not available, ROOT5, and OpenCL 1.2, which do not know C++11. #define GPUCA_NSLICES 36 diff --git a/GPU/Common/GPUDefGPUParameters.h b/GPU/Common/GPUDefGPUParameters.h index 648711a132111..19ca5e8df42ba 100644 --- a/GPU/Common/GPUDefGPUParameters.h +++ b/GPU/Common/GPUDefGPUParameters.h @@ -27,13 +27,14 @@ #include "GPUDefMacros.h" // GPU Run Configuration +#ifdef GPUCA_GPUCODE #if defined(GPUCA_GPUTYPE_VEGA) #define GPUCA_WARP_SIZE 64 #define GPUCA_THREAD_COUNT 256 - #define GPUCA_LB_GPUTPCCreateSliceData 256 + #define GPUCA_LB_GPUTPCCreateSliceData 128 #define GPUCA_LB_GPUTPCStartHitsSorter 1024, 2 #define GPUCA_LB_GPUTPCStartHitsFinder 1024 - #define GPUCA_LB_GPUTPCTrackletConstructor 512, 1 + #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 #define GPUCA_LB_GPUTPCTrackletSelector 256, 8 #define GPUCA_LB_GPUTPCNeighboursFinder 1024, 1 #define GPUCA_LB_GPUTPCNeighboursCleaner 896 @@ -41,7 +42,7 @@ #define GPUCA_LB_GPUTPCCFDecodeZS 64, 4 #define GPUCA_LB_GPUTPCCFGather 1024, 1 #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 1 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 1 + #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 4, 200 #define GPUCA_LB_GPUTPCGMMergerSliceRefit 256 #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 @@ -57,7 +58,7 @@ #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 #define GPUCA_LB_GPUTPCGMMergerLinkGlobalTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 256 + #define GPUCA_LB_GPUTPCGMMergerCollect 512 #define GPUCA_LB_GPUTPCGMMergerSortTracks 256 #define GPUCA_LB_GPUTPCGMMergerSortTracksQPt 256 #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 @@ -67,18 +68,80 @@ #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512 - #define GPUCA_LB_GPUTPCCompressionKernels_step2gather 128 + #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 192, 2 + #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 + #define GPUCA_LB_COMPRESSION_GATHER 1024 #define GPUCA_LB_CLUSTER_FINDER 512 #define GPUCA_NEIGHBOURS_FINDER_MAX_NNEIGHUP 5 - #define GPUCA_TRACKLET_SELECTOR_HITS_REG_SIZE 6 - #define GPUCA_CONSTRUCTOR_IN_PIPELINE 0 - #define GPUCA_SELECTOR_IN_PIPELINE 0 - #define GPUCA_ALTERNATE_BORDER_SORT 0 - #define GPUCA_SORT_BEFORE_FIT 0 - #define GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION 0 + #define GPUCA_TRACKLET_SELECTOR_HITS_REG_SIZE 20 + #define GPUCA_CONSTRUCTOR_IN_PIPELINE 1 + #define GPUCA_SELECTOR_IN_PIPELINE 1 + #define GPUCA_ALTERNATE_BORDER_SORT 1 + #define GPUCA_SORT_BEFORE_FIT 1 + #define GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION 1 + #define GPUCA_TRACKLET_SELECTOR_SLICE_COUNT 1 + #define GPUCA_NO_ATOMIC_PRECHECK 1 + #define GPUCA_DEDX_STORAGE_TYPE unsigned short + #define GPUCA_MERGER_INTERPOLATION_ERROR_TYPE half + #define GPUCA_COMP_GATHER_KERNEL 4 + #define GPUCA_COMP_GATHER_MODE 3 +#elif defined(GPUCA_GPUTYPE_AMPERE) + #define GPUCA_WARP_SIZE 32 + #define GPUCA_THREAD_COUNT 512 + #define GPUCA_LB_GPUTPCCreateSliceData 384 + #define GPUCA_LB_GPUTPCStartHitsSorter 512, 1 + #define GPUCA_LB_GPUTPCStartHitsFinder 512 + #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 // best single-kernel: 128, 4 + #define GPUCA_LB_GPUTPCTrackletSelector 192, 3 // best single-kernel: 128, 4 + #define GPUCA_LB_GPUTPCNeighboursFinder 640, 1 // best single-kernel: 768, 1 + #define GPUCA_LB_GPUTPCNeighboursCleaner 512 + #define GPUCA_LB_GPUTPCGlobalTracking 128, 4 + #define GPUCA_LB_GPUTPCCFDecodeZS 64, 10 + #define GPUCA_LB_GPUTPCCFGather 1024, 1 + #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 4 + #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 64, 12 + #define GPUCA_LB_GPUTPCGMMergerSliceRefit 32, 6 + #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 + #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 + #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 + #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 + #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 + #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 + #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256, 4 + #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 + #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 + #define GPUCA_LB_GPUTPCGMMergerMergeSlicesPrepare 256, 2 + #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 192 + #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 64, 2 + #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 + #define GPUCA_LB_GPUTPCGMMergerLinkGlobalTracks 256 + #define GPUCA_LB_GPUTPCGMMergerCollect 256, 2 + #define GPUCA_LB_GPUTPCGMMergerSortTracks 256 + #define GPUCA_LB_GPUTPCGMMergerSortTracksQPt 256 + #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 + #define GPUCA_LB_GPUTPCGMMergerPrepareClusters_step0 256 + #define GPUCA_LB_GPUTPCGMMergerPrepareClusters_step1 256 + #define GPUCA_LB_GPUTPCGMMergerPrepareClusters_step2 256 + #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 + #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 + #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 + #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 64, 2 + #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 3 + #define GPUCA_LB_COMPRESSION_GATHER 1024 + #define GPUCA_LB_CLUSTER_FINDER 448 + #define GPUCA_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 + #define GPUCA_TRACKLET_SELECTOR_HITS_REG_SIZE 20 + #define GPUCA_CONSTRUCTOR_IN_PIPELINE 1 + #define GPUCA_SELECTOR_IN_PIPELINE 1 + #define GPUCA_ALTERNATE_BORDER_SORT 1 + #define GPUCA_SORT_BEFORE_FIT 1 + #define GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION 1 + #define GPUCA_TRACKLET_SELECTOR_SLICE_COUNT 1 #define GPUCA_NO_ATOMIC_PRECHECK 1 + #define GPUCA_DEDX_STORAGE_TYPE unsigned short + #define GPUCA_MERGER_INTERPOLATION_ERROR_TYPE half + #define GPUCA_COMP_GATHER_KERNEL 4 + #define GPUCA_COMP_GATHER_MODE 3 #elif defined(GPUCA_GPUTYPE_TURING) #define GPUCA_WARP_SIZE 32 #define GPUCA_THREAD_COUNT 512 @@ -93,8 +156,8 @@ #define GPUCA_LB_GPUTPCCFDecodeZS 64, 8 #define GPUCA_LB_GPUTPCCFGather 1024, 1 #define GPUCA_LB_GPUTPCGMMergerTrackFit 32, 8 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 1 - #define GPUCA_LB_GPUTPCGMMergerSliceRefit 64 + #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 128, 4 + #define GPUCA_LB_GPUTPCGMMergerSliceRefit 64, 5 #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 @@ -109,7 +172,7 @@ #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 #define GPUCA_LB_GPUTPCGMMergerLinkGlobalTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 256, 2 + #define GPUCA_LB_GPUTPCGMMergerCollect 128, 2 #define GPUCA_LB_GPUTPCGMMergerSortTracks 256 #define GPUCA_LB_GPUTPCGMMergerSortTracksQPt 256 #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 @@ -121,7 +184,7 @@ #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 128 #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCCompressionKernels_step2gather 1024 + #define GPUCA_LB_COMPRESSION_GATHER 1024 #define GPUCA_LB_CLUSTER_FINDER 512 #define GPUCA_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 #define GPUCA_TRACKLET_SELECTOR_HITS_REG_SIZE 20 @@ -132,11 +195,15 @@ #define GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION 1 #define GPUCA_TRACKLET_SELECTOR_SLICE_COUNT 1 #define GPUCA_NO_ATOMIC_PRECHECK 1 + #define GPUCA_COMP_GATHER_KERNEL 0 + #define GPUCA_DEDX_STORAGE_TYPE unsigned short + #define GPUCA_MERGER_INTERPOLATION_ERROR_TYPE half // #define GPUCA_USE_TEXTURES #elif defined(GPUCA_GPUTYPE_OPENCL) -#elif defined(GPUCA_GPUCODE) +#else #error GPU TYPE NOT SET #endif +#endif // GPUCA_GPUCODE #ifdef GPUCA_GPUCODE // Default settings, if not already set for selected GPU type @@ -173,15 +240,15 @@ #ifndef GPUCA_LB_GPUTPCCompressionKernels_step1unattached #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 256 #endif - #ifndef GPUCA_LB_GPUTPCCompressionKernels_step2gather - #define GPUCA_LB_GPUTPCCompressionKernels_step2gather 256 - #endif #ifndef GPUCA_LB_GPUTPCCFDecodeZS #define GPUCA_LB_GPUTPCCFDecodeZS 128, 4 #endif #ifndef GPUCA_LB_GPUTPCCFGather #define GPUCA_LB_GPUTPCCFGather 1024, 1 #endif + #ifndef GPUCA_LB_COMPRESSION_GATHER + #define GPUCA_LB_COMPRESSION_GATHER 1024 + #endif #ifndef GPUCA_LB_CLUSTER_FINDER #define GPUCA_LB_CLUSTER_FINDER 128 #endif @@ -266,6 +333,9 @@ #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step2 #define GPUCA_LB_GPUTPCGMMergerFinalize_step2 256 #endif + #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers + #define GPUCA_LB_GPUTPCGMMergerMergeLoopers 256 + #endif #ifndef GPUCA_LB_GPUITSFitterKernel #define GPUCA_LB_GPUITSFitterKernel 256 #endif @@ -275,6 +345,12 @@ #ifndef GPUCA_LB_GPUTPCStartHitsSorter #define GPUCA_LB_GPUTPCStartHitsSorter 256 #endif + #ifndef GPUCA_LB_GPUTrackingRefitKernel_mode0asGPU + #define GPUCA_LB_GPUTrackingRefitKernel_mode0asGPU 256 + #endif + #ifndef GPUCA_LB_GPUTrackingRefitKernel_mode1asTrackParCov + #define GPUCA_LB_GPUTrackingRefitKernel_mode1asTrackParCov 256 + #endif #define GPUCA_GET_THREAD_COUNT(...) GPUCA_M_FIRST(__VA_ARGS__) #else // The following defaults are needed to compile the host code @@ -285,6 +361,7 @@ #define GPUCA_THREAD_COUNT_SCAN 512 // TODO: WARNING!!! Must not be GPUTYPE-dependent right now! // TODO: Fix! +#define GPUCA_LB_GPUTPCCFCheckPadBaseline GPUCA_WARP_SIZE #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap GPUCA_LB_CLUSTER_FINDER #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits GPUCA_LB_CLUSTER_FINDER #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart GPUCA_LB_CLUSTER_FINDER @@ -300,6 +377,11 @@ #define GPUCA_LB_GPUTPCCFStreamCompaction_compactDigits GPUCA_THREAD_COUNT_SCAN #define GPUCA_LB_GPUTPCTrackletConstructor_singleSlice GPUCA_LB_GPUTPCTrackletConstructor #define GPUCA_LB_GPUTPCTrackletConstructor_allSlices GPUCA_LB_GPUTPCTrackletConstructor +#define GPUCA_LB_GPUTPCCompressionGatherKernels_unbuffered GPUCA_LB_COMPRESSION_GATHER +#define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered32 GPUCA_LB_COMPRESSION_GATHER +#define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered64 GPUCA_LB_COMPRESSION_GATHER +#define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered128 GPUCA_LB_COMPRESSION_GATHER +#define GPUCA_LB_GPUTPCCompressionGatherKernels_multiBlock GPUCA_LB_COMPRESSION_GATHER #ifndef GPUCA_NEIGHBORSFINDER_REGS #define GPUCA_NEIGHBORSFINDER_REGS NONE, 0 @@ -329,6 +411,12 @@ #ifndef GPUCA_TRACKLET_SELECTOR_SLICE_COUNT #define GPUCA_TRACKLET_SELECTOR_SLICE_COUNT 8 // Currently must be smaller than avaiable MultiProcessors on GPU or will result in wrong results #endif + #ifndef GPUCA_COMP_GATHER_KERNEL + #define GPUCA_COMP_GATHER_KERNEL 0 + #endif + #ifndef GPUCA_COMP_GATHER_MODE + #define GPUCA_COMP_GATHER_MODE 2 + #endif #else #define GPUCA_NEIGHBOURS_FINDER_MAX_NNEIGHUP 0 #define GPUCA_TRACKLET_SELECTOR_HITS_REG_SIZE 0 @@ -339,6 +427,14 @@ #define GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION 0 #define GPUCA_TRACKLET_SELECTOR_SLICE_COUNT 1 #define GPUCA_THREAD_COUNT_FINDER 1 + #define GPUCA_COMP_GATHER_KERNEL 0 + #define GPUCA_COMP_GATHER_MODE 0 +#endif +#ifndef GPUCA_DEDX_STORAGE_TYPE +#define GPUCA_DEDX_STORAGE_TYPE float +#endif +#ifndef GPUCA_MERGER_INTERPOLATION_ERROR_TYPE +#define GPUCA_MERGER_INTERPOLATION_ERROR_TYPE float #endif #ifndef GPUCA_WARP_SIZE @@ -365,7 +461,7 @@ #define GPUCA_MAX_ITS_FIT_TRACKS ((size_t) 96 * 1024) // Max number of tracks for ITS track fit #define GPUCA_TRACKER_CONSTANT_MEM ((size_t) 63 * 1024) // Amount of Constant Memory to reserve #define GPUCA_MEMORY_SIZE ((size_t) 6 * 1024 * 1024 * 1024) // Size of memory allocated on Device -#define GPUCA_HOST_MEMORY_SIZE ((size_t) 6 * 1024 * 1024 * 1024) // Size of memory allocated on Host +#define GPUCA_HOST_MEMORY_SIZE ((size_t) 1 * 1024 * 1024 * 1024) // Size of memory allocated on Host #define GPUCA_GPU_STACK_SIZE ((size_t) 8 * 1024) // Stack size per GPU thread #define GPUCA_GPU_HEAP_SIZE ((size_t) 16 * 1025 * 1024) // Stack size per GPU thread diff --git a/GPU/Common/GPUDefMacros.h b/GPU/Common/GPUDefMacros.h index 4c5946de3bb8e..81b350ecd0329 100644 --- a/GPU/Common/GPUDefMacros.h +++ b/GPU/Common/GPUDefMacros.h @@ -31,12 +31,17 @@ #define GPUCA_M_FIRST(...) GPUCA_M_FIRST_A(__VA_ARGS__) #define GPUCA_M_SHIFT_A(a, ...) __VA_ARGS__ #define GPUCA_M_SHIFT(...) GPUCA_M_SHIFT_A(__VA_ARGS__) +#define GPUCA_M_FIRST2_A(a, b, ...) a, b +#define GPUCA_M_FIRST2(...) GPUCA_M_FIRST2_A(__VA_ARGS__, 0) #define GPUCA_M_STRIP_FIRST(a) GPUCA_M_FIRST(GPUCA_M_STRIP(a)) #define GPUCA_M_COUNT_A(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, N, ...) N +#define GPUCA_M_COUNT3_A(_1, _2, _3, N, ...) N #define GPUCA_M_COUNT(...) GPUCA_M_COUNT_A(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define GPUCA_M_SINGLEOPT(...) GPUCA_M_COUNT_A(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) +#define GPUCA_M_MAX2_3(...) GPUCA_M_COUNT3_A(__VA_ARGS__, GPUCA_M_FIRST2(__VA_ARGS__), GPUCA_M_FIRST2(__VA_ARGS__), GPUCA_M_FIRST(__VA_ARGS__), ) +#define GPUCA_M_MAX1_3(...) GPUCA_M_COUNT3_A(__VA_ARGS__, GPUCA_M_FIRST(__VA_ARGS__), GPUCA_M_FIRST(__VA_ARGS__), GPUCA_M_FIRST(__VA_ARGS__), ) #define GPUCA_M_UNROLL_ #define GPUCA_M_UNROLL_U(...) _Pragma(GPUCA_M_STR(unroll __VA_ARGS__)) diff --git a/GPU/Common/GPULogging.h b/GPU/Common/GPULogging.h index bc677c546ba5b..c78e21dce9941 100644 --- a/GPU/Common/GPULogging.h +++ b/GPU/Common/GPULogging.h @@ -23,7 +23,7 @@ #define GPUWarning(...) #define GPUError(...) #define GPUFatal(...) -#elif defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE_DEVICE) && !defined(GPUCA_NO_FMT) +#elif defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE_DEVICE) && !defined(GPUCA_NO_FMT) && !defined(__HIPCC__) #include <fmt/printf.h> #define GPUInfo(string, ...) \ { \ @@ -40,7 +40,7 @@ fmt::fprintf(stderr, string "\n", ##__VA_ARGS__); \ throw std::exception(); \ } -#elif defined(GPUCA_STANDALONE) || defined(GPUCA_GPUCODE_DEVICE) || (defined(GPUCA_ALIROOT_LIB) && defined(GPUCA_GPUCODE) && defined(__cplusplus) && __cplusplus < 201703L) +#elif defined(GPUCA_STANDALONE) || defined(GPUCA_GPUCODE_DEVICE) || (defined(GPUCA_ALIROOT_LIB) && defined(GPUCA_GPUCODE) && defined(__cplusplus) && __cplusplus < 201703L) || defined(__HIPCC__) // For standalone / CUDA / HIP, we just use printf, which should be available // Temporarily, we also have to handle CUDA on AliRoot with O2 defaults due to ROOT / CUDA incompatibilities #define GPUInfo(string, ...) \ @@ -48,7 +48,7 @@ printf(string "\n", ##__VA_ARGS__); \ } #define GPUImportant(...) GPUInfo(__VA_ARGS__) - #ifdef GPUCA_GPUCODE_DEVICE + #if defined(GPUCA_GPUCODE_DEVICE) || defined(__HIPCC__) #define GPUWarning(...) GPUInfo(__VA_ARGS__) #define GPUError(...) GPUInfo(__VA_ARGS__) #define GPUFatal(...) GPUInfo(__VA_ARGS__) diff --git a/GPU/Common/GPUROOTCartesianFwd.h b/GPU/Common/GPUROOTCartesianFwd.h new file mode 100644 index 0000000000000..dd8b9b53bbcb8 --- /dev/null +++ b/GPU/Common/GPUROOTCartesianFwd.h @@ -0,0 +1,84 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUROOTCartesianFwd.h +/// \author David Rohr + +#ifndef GPUROOTCARTESIANFWD_H +#define GPUROOTCARTESIANFWD_H + +// Standalone forward declarations for Cartesian2D / Cartesian3D / Point2D / Point3D etc. +// To be used on GPU where ROOT is not available. + +#include "GPUCommonDef.h" + +namespace ROOT +{ +namespace Math +{ +template <class T, unsigned int D1, unsigned int D2, class R> +class SMatrix; +template <class T, unsigned int D> +class MatRepSym; +template <class T, unsigned int D1, unsigned int D2> +class MatRepStd; +template <class CoordSystem, class Tag> +class PositionVector2D; +template <class CoordSystem, class Tag> +class PositionVector3D; +template <class CoordSystem, class Tag> +class DisplacementVector2D; +template <class CoordSystem, class Tag> +class DisplacementVector3D; +template <class T> +class Cartesian2D; +template <class T> +class Cartesian3D; +class DefaultCoordinateSystemTag; +} // namespace Math +} // namespace ROOT + +namespace o2 +{ +namespace math_utils +{ + +namespace detail +{ +template <typename T, int I> +struct GPUPoint2D; +template <typename T, int I> +struct GPUPoint3D; +} // namespace detail + +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) +template <typename T> +using Point2D = ROOT::Math::PositionVector2D<ROOT::Math::Cartesian2D<T>, ROOT::Math::DefaultCoordinateSystemTag>; +template <typename T> +using Vector2D = ROOT::Math::DisplacementVector2D<ROOT::Math::Cartesian2D<T>, ROOT::Math::DefaultCoordinateSystemTag>; +template <typename T> +using Point3D = ROOT::Math::PositionVector3D<ROOT::Math::Cartesian3D<T>, ROOT::Math::DefaultCoordinateSystemTag>; +template <typename T> +using Vector3D = ROOT::Math::DisplacementVector3D<ROOT::Math::Cartesian3D<T>, ROOT::Math::DefaultCoordinateSystemTag>; +#else +template <typename T> +using Point2D = detail::GPUPoint2D<T, 0>; +template <typename T> +using Vector2D = detail::GPUPoint2D<T, 1>; +template <typename T> +using Point3D = detail::GPUPoint3D<T, 0>; +template <typename T> +using Vector3D = detail::GPUPoint3D<T, 1>; +#endif + +} // namespace math_utils +} // namespace o2 + +#endif diff --git a/GPU/Common/test/testGPUsortCUDA.cu b/GPU/Common/test/testGPUsortCUDA.cu index 44a302bcf9520..e6bd5206cbec9 100644 --- a/GPU/Common/test/testGPUsortCUDA.cu +++ b/GPU/Common/test/testGPUsortCUDA.cu @@ -11,8 +11,6 @@ /// \file testGPUsortCUDA.cu /// \author Michael Lettrich -#define GPUCA_GPUTYPE_TURING - #define BOOST_TEST_MODULE Test GPUCommonAlgorithm Sorting CUDA #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK diff --git a/GPU/GPUTracking/Base/GPUConstantMem.h b/GPU/GPUTracking/Base/GPUConstantMem.h index 2fecd58fd725b..8372eaa7a9582 100644 --- a/GPU/GPUTracking/Base/GPUConstantMem.h +++ b/GPU/GPUTracking/Base/GPUConstantMem.h @@ -47,6 +47,7 @@ class GPUTRDTracker_t #include "GPUTPCCompression.h" #include "GPUITSFitter.h" #include "GPUTPCClusterFinder.h" +#include "GPUTrackingRefit.h" #else #include "GPUO2FakeClasses.h" #endif @@ -71,6 +72,7 @@ struct GPUConstantMem { GPUTRDTrackerGPU trdTracker; GPUTPCClusterFinder tpcClusterer[GPUCA_NSLICES]; GPUITSFitter itsFitter; + GPUTrackingRefitProcessor trackingRefit; GPUTrackingInOutPointers ioPtrs; GPUCalibObjectsConst calibObjects; GPUErrors errorCodes; diff --git a/GPU/GPUTracking/Base/GPUDataTypes.h b/GPU/GPUTracking/Base/GPUDataTypes.h index aa866b024ebfa..6612f77ee229c 100644 --- a/GPU/GPUTracking/Base/GPUDataTypes.h +++ b/GPU/GPUTracking/Base/GPUDataTypes.h @@ -18,16 +18,16 @@ // These are basic and non-comprex data types, which will also be visible on the GPU. // Please add complex data types required on the host but not GPU to GPUHostDataTypes.h and forward-declare! -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cstddef> -#endif #ifdef GPUCA_NOCOMPAT_ALLOPENCL #include <type_traits> #endif +#endif #ifdef GPUCA_NOCOMPAT #include "GPUTRDDef.h" -class AliHLTTPCClusterMCLabel; +struct AliHLTTPCClusterMCLabel; struct AliHLTTPCRawCluster; namespace o2 { @@ -36,6 +36,9 @@ namespace tpc struct ClusterNativeAccess; struct CompressedClustersFlat; class Digit; +namespace constants +{ +} // namespace constants } // namespace tpc } // namespace o2 #endif @@ -45,16 +48,19 @@ namespace o2 class MCCompLabel; namespace base { +class Propagator; class MatLayerCylSet; } // namespace base namespace trd { -class TRDGeometryFlat; +class GeometryFlat; } // namespace trd namespace dataformats { template <class T> class MCTruthContainer; +template <class T> +class ConstMCTruthContainerView; } // namespace dataformats } // namespace o2 @@ -64,6 +70,7 @@ namespace gpu { class TPCFastTransform; class TPCdEdxCalibrationSplines; +struct TPCPadGainCalib; } // namespace gpu } // namespace GPUCA_NAMESPACE @@ -82,7 +89,7 @@ namespace gpu #define GPUCA_RECO_STEP GPUDataTypes #endif -#ifdef __OPENCL__ +#if defined(__OPENCL__) && !defined(__OPENCLCPP__) MEM_CLASS_PRE() // Macro with some template magic for OpenCL 1.2 #endif class GPUTPCTrack; @@ -91,7 +98,7 @@ class GPUTPCGMMergedTrack; struct GPUTPCGMMergedTrackHit; struct GPUTPCGMMergedTrackHitXYZ; class GPUTRDTrackletWord; -class GPUTPCMCInfo; +struct GPUTPCMCInfo; struct GPUTPCClusterData; struct GPUTRDTrackletLabels; struct GPUTPCDigitsMCInput; @@ -118,6 +125,7 @@ class GPUDataTypes TPCdEdx = 64, TPCClusterFinding = 128, TPCDecompression = 256, + Refit = 512, AllRecoSteps = 0x7FFFFFFF, NoRecoStep = 0 }; enum ENUM_CLASS InOutType { TPCClusters = 1, @@ -129,7 +137,7 @@ class GPUDataTypes TPCRaw = 64 }; #ifdef GPUCA_NOCOMPAT_ALLOPENCL - static constexpr const char* const RECO_STEP_NAMES[] = {"TPC Transformation", "TPC Sector Tracking", "TPC Track Merging and Fit", "TPC Compression", "TRD Tracking", "ITS Tracking", "TPC dEdx Computation", "TPC Cluster Finding", "TPC Decompression"}; + static constexpr const char* const RECO_STEP_NAMES[] = {"TPC Transformation", "TPC Sector Tracking", "TPC Track Merging and Fit", "TPC Compression", "TRD Tracking", "ITS Tracking", "TPC dEdx Computation", "TPC Cluster Finding", "TPC Decompression", "Global Refit"}; static constexpr const char* const GENERAL_STEP_NAMES[] = {"Prepare", "QA"}; typedef bitfield<RecoStep, unsigned int> RecoStepField; typedef bitfield<InOutType, unsigned int> InOutTypeField; @@ -166,8 +174,10 @@ template <template <typename T> class S> struct GPUCalibObjectsTemplate { typename S<TPCFastTransform>::type* fastTransform = nullptr; typename S<o2::base::MatLayerCylSet>::type* matLUT = nullptr; - typename S<o2::trd::TRDGeometryFlat>::type* trdGeometry = nullptr; + typename S<o2::trd::GeometryFlat>::type* trdGeometry = nullptr; typename S<TPCdEdxCalibrationSplines>::type* dEdxSplines = nullptr; + typename S<TPCPadGainCalib>::type* tpcPadGain = nullptr; + typename S<o2::base::Propagator>::type* o2Propagator = nullptr; }; typedef GPUCalibObjectsTemplate<DefaultPtr> GPUCalibObjects; typedef GPUCalibObjectsTemplate<ConstPtr> GPUCalibObjectsConst; @@ -223,6 +233,7 @@ struct GPUTrackingInOutPointers { const GPUTPCGMMergedTrackHitXYZ* mergedTrackHitsXYZ = nullptr; unsigned int nMergedTrackHits = 0; unsigned int* mergedTrackHitAttachment = nullptr; + unsigned char* mergedTrackHitStates = nullptr; const o2::tpc::CompressedClustersFlat* tpcCompressedClusters = nullptr; const GPUTRDTrackletWord* trdTracklets = nullptr; unsigned int nTRDTracklets = 0; diff --git a/GPU/GPUTracking/Base/GPUGeneralKernels.h b/GPU/GPUTracking/Base/GPUGeneralKernels.h index 1a29fd3dc22ac..250a0420ad19f 100644 --- a/GPU/GPUTracking/Base/GPUGeneralKernels.h +++ b/GPU/GPUTracking/Base/GPUGeneralKernels.h @@ -17,13 +17,19 @@ #include "GPUDef.h" #include "GPUDataTypes.h" +#if defined(__HIPCC__) +#define GPUCA_CUB hipcub +#else +#define GPUCA_CUB cub +#endif + +#ifndef GPUCA_GPUCODE_GENRTC #ifdef GPUCA_GPUCODE #ifdef __CUDACC__ #include <cub/cub.cuh> -#define GPUCA_CUB cub #elif defined(__HIPCC__) #include <hipcub/hipcub.hpp> -#define GPUCA_CUB hipcub +#endif #endif #endif @@ -54,9 +60,11 @@ class GPUKernelTemplate // Provides the shared memory resources for CUB collectives #if (defined(__CUDACC__) || defined(__HIPCC__)) && defined(GPUCA_GPUCODE) typedef GPUCA_CUB::BlockScan<T, I> BlockScan; + typedef GPUCA_CUB::BlockReduce<T, I> BlockReduce; typedef GPUCA_CUB::WarpScan<T> WarpScan; union { typename BlockScan::TempStorage cubTmpMem; + typename BlockReduce::TempStorage cubReduceTmpMem; typename WarpScan::TempStorage cubWarpTmpMem; int tmpBroadcast; }; @@ -94,4 +102,6 @@ class GPUMemClean16 : public GPUKernelTemplate } // namespace gpu } // namespace GPUCA_NAMESPACE +#undef GPUCA_CUB + #endif diff --git a/GPU/GPUTracking/Base/GPUHostDataTypes.h b/GPU/GPUTracking/Base/GPUHostDataTypes.h index 0f90719ccbfc5..d46826d283768 100644 --- a/GPU/GPUTracking/Base/GPUHostDataTypes.h +++ b/GPU/GPUTracking/Base/GPUHostDataTypes.h @@ -26,10 +26,8 @@ #include <vector> #include <array> -#include <memory> -#include <mutex> #include "DataFormatsTPC/Constants.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" namespace GPUCA_NAMESPACE @@ -38,12 +36,11 @@ namespace gpu { struct GPUTPCDigitsMCInput { - std::array<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>*, o2::tpc::Constants::MAXSECTOR> v; + std::array<const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>*, o2::tpc::constants::MAXSECTOR> v; }; struct GPUTPCClusterMCInterim { std::vector<o2::MCCompLabel> labels; - uint offset; }; struct GPUTPCLinearLabels { diff --git a/GPU/GPUTracking/Base/GPUO2DataTypes.h b/GPU/GPUTracking/Base/GPUO2DataTypes.h index f1356ddd2c7ee..66a53883cb8fc 100644 --- a/GPU/GPUTracking/Base/GPUO2DataTypes.h +++ b/GPU/GPUTracking/Base/GPUO2DataTypes.h @@ -18,8 +18,10 @@ #if defined(HAVE_O2HEADERS) && (!defined(__OPENCL__) || defined(__OPENCLCPP__)) #include "DataFormatsTPC/ClusterNative.h" +#include "DataFormatsTPC/Digit.h" #include "DetectorsBase/MatLayerCylSet.h" -#include "TRDBase/TRDGeometryFlat.h" +#include "DetectorsBase/Propagator.h" +#include "TRDBase/GeometryFlat.h" #else #include "GPUO2FakeClasses.h" #endif diff --git a/GPU/GPUTracking/Base/GPUO2FakeClasses.h b/GPU/GPUTracking/Base/GPUO2FakeClasses.h index bdb625767a80f..5449549c2a25e 100644 --- a/GPU/GPUTracking/Base/GPUO2FakeClasses.h +++ b/GPU/GPUTracking/Base/GPUO2FakeClasses.h @@ -69,7 +69,7 @@ class MatLayerCylSet } // namespace base namespace trd { -class TRDGeometryFlat +class GeometryFlat { }; } // namespace trd @@ -79,6 +79,9 @@ namespace GPUCA_NAMESPACE { namespace gpu { +class TPCCFCalibration +{ +}; class TPCdEdxCalibrationSplines { }; @@ -99,6 +102,11 @@ class GPUTPCCompression class GPUTPCClusterFinder { }; +class GPUTrackingRefitProcessor +{ +}; +struct GPUTPCCFChainContext { +}; #ifndef __OPENCL__ struct GPUParam; class GPUTPCClusterStatistics diff --git a/GPU/GPUTracking/Base/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index 4edfc7f402622..b7adba5efb4e4 100644 --- a/GPU/GPUTracking/Base/GPUParam.cxx +++ b/GPU/GPUTracking/Base/GPUParam.cxx @@ -12,30 +12,36 @@ /// \author David Rohr, Sergey Gorbunov #include "GPUParam.h" +#include "GPUParamRTC.h" #include "GPUDef.h" #include "GPUCommonMath.h" #include "GPUTPCGMPolynomialFieldManager.h" #include "GPUDataTypes.h" +#include "GPUConstantMem.h" using namespace GPUCA_NAMESPACE::gpu; -#if !defined(GPUCA_GPUCODE) && defined(GPUCA_ALIROOT_LIB) +#ifdef GPUCA_ALIROOT_LIB #include "AliTPCClusterParam.h" #include "AliTPCcalibDB.h" #include <iostream> #endif - -#if !defined(GPUCA_GPUCODE) #include <cstring> +#include <tuple> +#ifdef HAVE_O2HEADERS +#include "DetectorsBase/Propagator.h" +#endif + +#include "utils/qconfigrtc.h" void GPUParam::SetDefaults(float solenoidBz) { memset((void*)this, 0, sizeof(*this)); new (&tpcGeometry) GPUTPCGeometry; - rec.SetDefaults(); + new (&rec) GPUSettingsRec; // clang-format off - float const kParamS0Par[2][3][6] = + const float kParamS0Par[2][3][6] = { { { 6.45913474727e-04, 2.51547407970e-05, 1.57551113516e-02, 1.99872811635e-08, -5.86769729853e-03, 9.16301505640e-05 }, { 9.71546804067e-04, 1.70938055817e-05, 2.17084009200e-02, 3.90275758377e-08, -1.68631039560e-03, 8.40498323669e-05 }, @@ -45,8 +51,7 @@ void GPUParam::SetDefaults(float solenoidBz) { 1.15970033221e-03, 1.30452335725e-05, 1.87015570700e-02, 5.39766737973e-08, 1.64790824056e-02, 1.44115634612e-04 }, { 6.27940462437e-04, 1.78520094778e-05, 2.83537860960e-02, 1.16867742150e-08, 5.02607785165e-02, 1.88510020962e-04 } } }; - - float const kParamRMS0[2][3][4] = + const float kParamRMS0[2][3][4] = { { { 4.17516864836e-02, 1.87623649254e-04, 5.63788712025e-02, 5.38373768330e-01, }, { 8.29434990883e-02, 2.03291710932e-04, 6.81538805366e-02, 9.70965325832e-01, }, @@ -74,17 +79,11 @@ void GPUParam::SetDefaults(float solenoidBz) } } - RMin = 83.65f; - RMax = 247.7f; - DAlpha = 0.349066f; - PadPitch = 0.4f; - BzkG = solenoidBz; + par.DAlpha = 0.349066f; + par.BzkG = solenoidBz; constexpr double kCLight = 0.000299792458f; - ConstBz = solenoidBz * kCLight; - ErrX = PadPitch / CAMath::Sqrt(12.f); - ErrY = 1.; - ErrZ = 0.228808; - dodEdx = 0; + par.ConstBz = solenoidBz * kCLight; + par.dodEdx = 0; constexpr float plusZmin = 0.0529937; constexpr float plusZmax = 249.778; @@ -101,51 +100,51 @@ void GPUParam::SetDefaults(float solenoidBz) if (tmp >= GPUCA_NSLICES / 4) { tmp -= GPUCA_NSLICES / 2; } - SliceParam[i].Alpha = 0.174533 + DAlpha * tmp; + SliceParam[i].Alpha = 0.174533 + par.DAlpha * tmp; SliceParam[i].CosAlpha = CAMath::Cos(SliceParam[i].Alpha); SliceParam[i].SinAlpha = CAMath::Sin(SliceParam[i].Alpha); - SliceParam[i].AngleMin = SliceParam[i].Alpha - DAlpha / 2.f; - SliceParam[i].AngleMax = SliceParam[i].Alpha + DAlpha / 2.f; + SliceParam[i].AngleMin = SliceParam[i].Alpha - par.DAlpha / 2.f; + SliceParam[i].AngleMax = SliceParam[i].Alpha + par.DAlpha / 2.f; } - AssumeConstantBz = false; - ToyMCEventsFlag = false; - ContinuousTracking = false; - continuousMaxTimeBin = 0; - debugLevel = 0; - resetTimers = false; - earlyTpcTransform = false; + par.AssumeConstantBz = false; + par.ToyMCEventsFlag = false; + par.ContinuousTracking = false; + par.continuousMaxTimeBin = 0; + par.debugLevel = 0; + par.resetTimers = false; + par.earlyTpcTransform = false; polynomialField.Reset(); // set very wrong initial value in order to see if the field was not properly initialised - GPUTPCGMPolynomialFieldManager::GetPolynomialField(BzkG, polynomialField); + GPUTPCGMPolynomialFieldManager::GetPolynomialField(par.BzkG, polynomialField); } -void GPUParam::UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsDeviceProcessing* p) +void GPUParam::UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsProcessing* p) { if (e) { - AssumeConstantBz = e->constBz; - ToyMCEventsFlag = e->homemadeEvents; - ContinuousTracking = e->continuousMaxTimeBin != 0; - continuousMaxTimeBin = e->continuousMaxTimeBin == -1 ? GPUSettings::TPC_MAX_TF_TIME_BIN : e->continuousMaxTimeBin; + par.AssumeConstantBz = e->constBz; + par.ToyMCEventsFlag = e->homemadeEvents; + par.ContinuousTracking = e->continuousMaxTimeBin != 0; + par.continuousMaxTimeBin = e->continuousMaxTimeBin == -1 ? GPUSettings::TPC_MAX_TF_TIME_BIN : e->continuousMaxTimeBin; polynomialField.Reset(); - if (AssumeConstantBz) { - GPUTPCGMPolynomialFieldManager::GetPolynomialField(GPUTPCGMPolynomialFieldManager::kUniform, BzkG, polynomialField); + if (par.AssumeConstantBz) { + GPUTPCGMPolynomialFieldManager::GetPolynomialField(GPUTPCGMPolynomialFieldManager::kUniform, par.BzkG, polynomialField); } else { - GPUTPCGMPolynomialFieldManager::GetPolynomialField(BzkG, polynomialField); + GPUTPCGMPolynomialFieldManager::GetPolynomialField(par.BzkG, polynomialField); } } if (p) { - debugLevel = p->debugLevel; - resetTimers = p->resetTimers; + par.debugLevel = p->debugLevel; + par.resetTimers = p->resetTimers; } - earlyTpcTransform = rec.ForceEarlyTPCTransform == -1 ? (!ContinuousTracking) : rec.ForceEarlyTPCTransform; + par.earlyTpcTransform = rec.ForceEarlyTPCTransform == -1 ? (!par.ContinuousTracking) : rec.ForceEarlyTPCTransform; } -void GPUParam::SetDefaults(const GPUSettingsEvent* e, const GPUSettingsRec* r, const GPUSettingsDeviceProcessing* p, const GPURecoStepConfiguration* w) +void GPUParam::SetDefaults(const GPUSettingsEvent* e, const GPUSettingsRec* r, const GPUSettingsProcessing* p, const GPURecoStepConfiguration* w) { SetDefaults(e->solenoidBz); if (w) { - dodEdx = w->steps.isSet(GPUDataTypes::RecoStep::TPCdEdx); + par.dodEdx = w->steps.isSet(GPUDataTypes::RecoStep::TPCdEdx); } if (r) { rec = *r; @@ -156,10 +155,7 @@ void GPUParam::SetDefaults(const GPUSettingsEvent* e, const GPUSettingsRec* r, c UpdateEventSettings(e, p); } -#endif - -#if !defined(GPUCA_GPUCODE) -#if !defined(GPUCA_ALIROOT_LIB) +#ifndef GPUCA_ALIROOT_LIB void GPUParam::LoadClusterErrors(bool Print) { } @@ -236,4 +232,38 @@ void GPUParam::LoadClusterErrors(bool Print) } } #endif + +void GPUParamRTC::setFrom(const GPUParam& param) +{ + memcpy((char*)this + sizeof(gpu_rtc::GPUSettingsRec) + sizeof(gpu_rtc::GPUSettingsParam), (char*)¶m + sizeof(GPUSettingsRec) + sizeof(GPUSettingsParam), sizeof(param) - sizeof(GPUSettingsRec) - sizeof(GPUSettingsParam)); + qConfigConvertRtc(this->rec, param.rec); + qConfigConvertRtc(this->par, param.par); +} + +std::string GPUParamRTC::generateRTCCode(const GPUParam& param, bool useConstexpr) +{ + return "namespace o2::gpu { class GPUDisplayBackend; }\n" + qConfigPrintRtc(std::make_tuple(¶m.rec, ¶m.par), useConstexpr); +} + +static_assert(alignof(GPUCA_NAMESPACE::gpu::GPUParam) == alignof(GPUCA_NAMESPACE::gpu::GPUSettingsRec)); +static_assert(alignof(GPUCA_NAMESPACE::gpu::GPUParam) == alignof(GPUCA_NAMESPACE::gpu::GPUSettingsParam)); +static_assert(sizeof(GPUCA_NAMESPACE::gpu::GPUParam) - sizeof(GPUCA_NAMESPACE::gpu::GPUParamRTC) == sizeof(GPUCA_NAMESPACE::gpu::GPUSettingsRec) + sizeof(GPUCA_NAMESPACE::gpu::GPUSettingsParam) - sizeof(GPUCA_NAMESPACE::gpu::gpu_rtc::GPUSettingsRec) - sizeof(GPUCA_NAMESPACE::gpu::gpu_rtc::GPUSettingsParam)); +static_assert(sizeof(GPUParam) % alignof(GPUConstantMem) == 0 && sizeof(GPUParamRTC) % alignof(GPUConstantMem) == 0, "Size of both GPUParam and of GPUParamRTC must be a multiple of the alignmeent of GPUConstantMem"); + +o2::base::Propagator* GPUParam::GetDefaultO2Propagator(bool useGPUField) const +{ + o2::base::Propagator* prop = nullptr; +#ifdef HAVE_O2HEADERS + if (useGPUField == false) { + throw std::runtime_error("o2 propagator withouzt gpu field unsupported"); + } + prop = o2::base::Propagator::Instance(); + if (useGPUField) { + prop->setGPUField(&polynomialField); + prop->setBz(polynomialField.GetNominalBz()); + } +#else + throw std::runtime_error("o2 propagator unsupported"); #endif + return prop; +} diff --git a/GPU/GPUTracking/Base/GPUParam.h b/GPU/GPUTracking/Base/GPUParam.h index f76f109a8869b..031cc8b78eee6 100644 --- a/GPU/GPUTracking/Base/GPUParam.h +++ b/GPU/GPUTracking/Base/GPUParam.h @@ -21,6 +21,14 @@ #include "GPUTPCGeometry.h" #include "GPUTPCGMPolynomialField.h" +namespace o2 +{ +namespace base +{ +class Propagator; +} // namespace base +} // namespace o2 + namespace GPUCA_NAMESPACE { namespace gpu @@ -36,36 +44,34 @@ struct GPUParamSlice { float ZMin, ZMax; // slice Z range }; -MEM_CLASS_PRE() -struct GPUParam { - public: - GPUSettingsRec rec; - - float DAlpha; // angular size - float RMin, RMax; // slice R range - float ErrX, ErrY, ErrZ; // default cluster errors - float PadPitch; // pad pitch - float BzkG; // constant magnetic field value in kG - float ConstBz; // constant magnetic field value in kG*clight - - char AssumeConstantBz; // Assume a constant magnetic field - char ToyMCEventsFlag; // events were build with home-made event generator - char ContinuousTracking; // Continuous tracking, estimate bz and errors for abs(z) = 125cm during seeding - char resetTimers; // Reset benchmark timers before event processing - char dodEdx; // Do dEdx computation - char earlyTpcTransform; // do Early TPC transformation - int debugLevel; // Debug level - int continuousMaxTimeBin; // Max time bin for continuous tracking +namespace internal +{ +template <class T, class S> +struct GPUParam_t { + T rec; + S par; + GPUTPCGeometry tpcGeometry; // TPC Geometry GPUTPCGMPolynomialField polynomialField; // Polynomial approx. of magnetic field for TPC GM GPUParamSlice SliceParam[GPUCA_NSLICES]; + protected: + float ParamRMS0[2][3][4]; // cluster shape parameterization coeficients + float ParamS0Par[2][3][6]; // cluster error parameterization coeficients +}; +} // namespace internal + +#if !(defined(__CINT__) || defined(__ROOTCINT__)) || defined(__CLING__) // Hide from ROOT 5 CINT since it triggers a CINT but +MEM_CLASS_PRE() +struct GPUParam : public internal::GPUParam_t<GPUSettingsRec, GPUSettingsParam> { + #ifndef GPUCA_GPUCODE void SetDefaults(float solenoidBz); - void SetDefaults(const GPUSettingsEvent* e, const GPUSettingsRec* r = nullptr, const GPUSettingsDeviceProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr); - void UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsDeviceProcessing* p = nullptr); + void SetDefaults(const GPUSettingsEvent* e, const GPUSettingsRec* r = nullptr, const GPUSettingsProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr); + void UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsProcessing* p = nullptr); void LoadClusterErrors(bool Print = 0); + o2::base::Propagator* GetDefaultO2Propagator(bool useGPUField = false) const; #endif GPUd() float Alpha(int iSlice) const @@ -76,7 +82,7 @@ struct GPUParam { if (iSlice >= GPUCA_NSLICES / 4) { iSlice -= GPUCA_NSLICES / 2; } - return 0.174533f + DAlpha * iSlice; + return 0.174533f + par.DAlpha * iSlice; } GPUd() float GetClusterRMS(int yz, int type, float z, float angle2) const; GPUd() void GetClusterRMS2(int row, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const; @@ -87,11 +93,8 @@ struct GPUParam { GPUd() void Slice2Global(int iSlice, float x, float y, float z, float* X, float* Y, float* Z) const; GPUd() void Global2Slice(int iSlice, float x, float y, float z, float* X, float* Y, float* Z) const; - - protected: - float ParamRMS0[2][3][4]; // cluster shape parameterization coeficients - float ParamS0Par[2][3][6]; // cluster error parameterization coeficients }; +#endif } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/Base/GPUParam.inc b/GPU/GPUTracking/Base/GPUParam.inc index 4c04619184e17..d269adea60bb0 100644 --- a/GPU/GPUTracking/Base/GPUParam.inc +++ b/GPU/GPUTracking/Base/GPUParam.inc @@ -25,7 +25,7 @@ namespace gpu MEM_CLASS_PRE() GPUdi() void MEM_LG(GPUParam)::Slice2Global(int iSlice, float x, float y, float z, float* X, float* Y, float* Z) const { - // conversion of coorinates sector->global + // conversion of coordinates sector->global *X = x * SliceParam[iSlice].CosAlpha - y * SliceParam[iSlice].SinAlpha; *Y = y * SliceParam[iSlice].CosAlpha + x * SliceParam[iSlice].SinAlpha; *Z = z; @@ -34,7 +34,7 @@ GPUdi() void MEM_LG(GPUParam)::Slice2Global(int iSlice, float x, float y, float MEM_CLASS_PRE() GPUdi() void MEM_LG(GPUParam)::Global2Slice(int iSlice, float X, float Y, float Z, float* x, float* y, float* z) const { - // conversion of coorinates global->sector + // conversion of coordinates global->sector *x = X * SliceParam[iSlice].CosAlpha + Y * SliceParam[iSlice].SinAlpha; *y = Y * SliceParam[iSlice].CosAlpha - X * SliceParam[iSlice].SinAlpha; *z = Z; @@ -43,7 +43,7 @@ GPUdi() void MEM_LG(GPUParam)::Global2Slice(int iSlice, float X, float Y, float MEM_CLASS_PRE() GPUdi() float MEM_LG(GPUParam)::GetClusterRMS(int yz, int type, float z, float angle2) const { - //* recalculate the cluster error wih respect to the track slope + //* recalculate the cluster RMS wih respect to the track slope MakeType(const float*) c = ParamRMS0[yz][type]; float v = c[0] + c[1] * z + c[2] * angle2; diff --git a/GPU/GPUTracking/Base/GPUParamRTC.h b/GPU/GPUTracking/Base/GPUParamRTC.h new file mode 100644 index 0000000000000..2647d7152dbea --- /dev/null +++ b/GPU/GPUTracking/Base/GPUParamRTC.h @@ -0,0 +1,43 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUParamRTC.h +/// \author David Rohr + +#ifndef GPUPARAMRTC_H +#define GPUPARAMRTC_H + +#include "GPUParam.h" +#include <string> + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ +namespace gpu_rtc +{ +#define QCONFIG_GENRTC +#define BeginNamespace(...) +#define EndNamespace(...) +#include "utils/qconfig.h" +#undef QCONFIG_GENRTC +#undef BeginNamespace +#undef EndNamespace +} // namespace gpu_rtc + +struct GPUParamRTC : public internal::GPUParam_t<gpu_rtc::GPUSettingsRec, gpu_rtc::GPUSettingsParam> { + void setFrom(const GPUParam& param); + static std::string generateRTCCode(const GPUParam& param, bool useConstexpr); +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/GPUTracking/Base/GPUProcessor.cxx b/GPU/GPUTracking/Base/GPUProcessor.cxx index fe3b27ae646f0..c6408f1d04530 100644 --- a/GPU/GPUTracking/Base/GPUProcessor.cxx +++ b/GPU/GPUTracking/Base/GPUProcessor.cxx @@ -21,7 +21,7 @@ GPUProcessor::GPUProcessor() : mRec(nullptr), mGPUProcessorType(PROCESSOR_TYPE_C GPUProcessor::~GPUProcessor() { - if (mRec && mRec->GetDeviceProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (mRec && mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { Clear(); } } diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index 9b9cad6be1152..1c4fdf78573ba 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -45,6 +45,10 @@ #define GPUCA_LOGGING_PRINTF #include "GPULogging.h" +#ifdef GPUCA_O2_LIB +#include "GPUO2InterfaceConfiguration.h" +#endif + namespace GPUCA_NAMESPACE { namespace gpu @@ -76,10 +80,10 @@ constexpr GPUReconstruction::GeometryType GPUReconstruction::geometryType; static long long int ptrDiff(void* a, void* b) { return (long long int)((char*)a - (char*)b); } -GPUReconstruction::GPUReconstruction(const GPUSettingsProcessing& cfg) : mHostConstantMem(new GPUConstantMem), mProcessingSettings(cfg) +GPUReconstruction::GPUReconstruction(const GPUSettingsDeviceBackend& cfg) : mHostConstantMem(new GPUConstantMem), mDeviceBackendSettings(cfg) { if (cfg.master) { - if (cfg.master->mProcessingSettings.deviceType != cfg.deviceType) { + if (cfg.master->mDeviceBackendSettings.deviceType != cfg.deviceType) { throw std::invalid_argument("device type of master and slave GPUReconstruction does not match"); } if (cfg.master->mMaster) { @@ -88,8 +92,8 @@ GPUReconstruction::GPUReconstruction(const GPUSettingsProcessing& cfg) : mHostCo mMaster = cfg.master; cfg.master->mSlaves.emplace_back(this); } - mDeviceProcessingSettings.SetDefaults(); - mEventSettings.SetDefaults(); + new (&mProcessingSettings) GPUSettingsProcessing; + new (&mEventSettings) GPUSettingsEvent; param().SetDefaults(&mEventSettings); mMemoryScalers.reset(new GPUMemorySizeScalers); for (unsigned int i = 0; i < NSLICES; i++) { @@ -193,102 +197,115 @@ int GPUReconstruction::InitPhaseBeforeDevice() mRecoStepsGPU.set((unsigned char)0); } - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_AUTO) { - mDeviceProcessingSettings.memoryAllocationStrategy = IsGPU() ? GPUMemoryResource::ALLOCATION_GLOBAL : GPUMemoryResource::ALLOCATION_INDIVIDUAL; + if (mProcessingSettings.forceMemoryPoolSize >= 1024 || mProcessingSettings.forceHostMemoryPoolSize >= 1024) { + mProcessingSettings.memoryAllocationStrategy = GPUMemoryResource::ALLOCATION_GLOBAL; + } + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_AUTO) { + mProcessingSettings.memoryAllocationStrategy = IsGPU() ? GPUMemoryResource::ALLOCATION_GLOBAL : GPUMemoryResource::ALLOCATION_INDIVIDUAL; } - if (mDeviceProcessingSettings.debugLevel >= 4) { - mDeviceProcessingSettings.keepAllMemory = true; + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + mProcessingSettings.forceMemoryPoolSize = mProcessingSettings.forceHostMemoryPoolSize = 0; } - if (mDeviceProcessingSettings.debugLevel >= 5 && mDeviceProcessingSettings.allocDebugLevel < 2) { - mDeviceProcessingSettings.allocDebugLevel = 2; + if (mProcessingSettings.debugLevel >= 4) { + mProcessingSettings.keepAllMemory = true; } - if (mDeviceProcessingSettings.eventDisplay || mDeviceProcessingSettings.keepAllMemory) { - mDeviceProcessingSettings.keepDisplayMemory = true; + if (mProcessingSettings.debugLevel >= 5 && mProcessingSettings.allocDebugLevel < 2) { + mProcessingSettings.allocDebugLevel = 2; } - if (mDeviceProcessingSettings.debugLevel < 6) { - mDeviceProcessingSettings.debugMask = 0; + if (mProcessingSettings.eventDisplay || mProcessingSettings.keepAllMemory) { + mProcessingSettings.keepDisplayMemory = true; } - if (mDeviceProcessingSettings.debugLevel < 1) { - mDeviceProcessingSettings.deviceTimers = false; + if (mProcessingSettings.debugLevel < 6) { + mProcessingSettings.debugMask = 0; } - if (mDeviceProcessingSettings.debugLevel >= 6 && mDeviceProcessingSettings.comparableDebutOutput) { - mDeviceProcessingSettings.nTPCClustererLanes = 1; - if (mDeviceProcessingSettings.trackletConstructorInPipeline < 0) { - mDeviceProcessingSettings.trackletConstructorInPipeline = 1; + if (mProcessingSettings.debugLevel < 1) { + mProcessingSettings.deviceTimers = false; + } + if (mProcessingSettings.debugLevel >= 6 && mProcessingSettings.comparableDebutOutput) { + mProcessingSettings.nTPCClustererLanes = 1; + if (mProcessingSettings.trackletConstructorInPipeline < 0) { + mProcessingSettings.trackletConstructorInPipeline = 1; } - if (mDeviceProcessingSettings.trackletSelectorInPipeline < 0) { - mDeviceProcessingSettings.trackletSelectorInPipeline = 1; + if (mProcessingSettings.trackletSelectorInPipeline < 0) { + mProcessingSettings.trackletSelectorInPipeline = 1; } - if (mDeviceProcessingSettings.trackletSelectorSlices < 0) { - mDeviceProcessingSettings.trackletSelectorSlices = 1; + if (mProcessingSettings.trackletSelectorSlices < 0) { + mProcessingSettings.trackletSelectorSlices = 1; } } - if (mDeviceProcessingSettings.tpcCompressionGatherMode < 0) { - mDeviceProcessingSettings.tpcCompressionGatherMode = (mRecoStepsGPU & GPUDataTypes::RecoStep::TPCCompression) ? 2 : 0; - } if (!(mRecoStepsGPU & GPUDataTypes::RecoStep::TPCMerging)) { - mDeviceProcessingSettings.mergerSortTracks = false; + mProcessingSettings.mergerSortTracks = false; } if (!IsGPU()) { - mDeviceProcessingSettings.nDeviceHelperThreads = 0; + mProcessingSettings.nDeviceHelperThreads = 0; + mProcessingSettings.nTPCClustererLanes = 1; } if (param().rec.NonConsecutiveIDs) { param().rec.DisableRefitAttachment = 0xFF; } - if (!(mRecoStepsGPU & RecoStep::TPCMerging)) { - mDeviceProcessingSettings.fullMergerOnGPU = false; + if (!(mRecoStepsGPU & RecoStep::TPCMerging) || !param().rec.mergerReadFromTrackerDirectly) { + mProcessingSettings.fullMergerOnGPU = false; } - if (mDeviceProcessingSettings.debugLevel || !mDeviceProcessingSettings.fullMergerOnGPU) { - mDeviceProcessingSettings.delayedOutput = false; + if (mProcessingSettings.debugLevel || !mProcessingSettings.fullMergerOnGPU) { + mProcessingSettings.delayedOutput = false; } UpdateSettings(); GPUCA_GPUReconstructionUpdateDefailts(); - if (!mDeviceProcessingSettings.trackletConstructorInPipeline) { - mDeviceProcessingSettings.trackletSelectorInPipeline = false; + if (!mProcessingSettings.trackletConstructorInPipeline) { + mProcessingSettings.trackletSelectorInPipeline = false; + } + if (!mProcessingSettings.enableRTC) { + mProcessingSettings.rtcConstexpr = false; } - mMemoryScalers->factor = mDeviceProcessingSettings.memoryScalingFactor; + mMemoryScalers->factor = mProcessingSettings.memoryScalingFactor; #ifdef WITH_OPENMP - if (mDeviceProcessingSettings.nThreads <= 0) { - mDeviceProcessingSettings.nThreads = omp_get_max_threads(); + if (mProcessingSettings.ompThreads <= 0) { + mProcessingSettings.ompThreads = omp_get_max_threads(); } else { - omp_set_num_threads(mDeviceProcessingSettings.nThreads); + omp_set_num_threads(mProcessingSettings.ompThreads); } #else - mDeviceProcessingSettings.nThreads = 1; + mProcessingSettings.ompThreads = 1; #endif - mMaxThreads = std::max(mMaxThreads, mDeviceProcessingSettings.nThreads); + mMaxThreads = std::max(mMaxThreads, mProcessingSettings.ompThreads); if (IsGPU()) { - mNStreams = std::max(mDeviceProcessingSettings.nStreams, 3); + mNStreams = std::max(mProcessingSettings.nStreams, 3); } - if (mDeviceProcessingSettings.doublePipeline && (mChains.size() != 1 || mChains[0]->SupportsDoublePipeline() == false || !IsGPU() || mDeviceProcessingSettings.memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_GLOBAL)) { + if (mProcessingSettings.doublePipeline && (mChains.size() != 1 || mChains[0]->SupportsDoublePipeline() == false || !IsGPU() || mProcessingSettings.memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_GLOBAL)) { GPUError("Must use double pipeline mode only with exactly one chain that must support it"); return 1; } - if (mMaster == nullptr && mDeviceProcessingSettings.doublePipeline) { + if (mMaster == nullptr && mProcessingSettings.doublePipeline) { mPipelineContext.reset(new GPUReconstructionPipelineContext); } mDeviceMemorySize = mHostMemorySize = 0; for (unsigned int i = 0; i < mChains.size(); i++) { mChains[i]->RegisterPermanentMemoryAndProcessors(); - size_t memGpu, memHost; - mChains[i]->MemorySize(memGpu, memHost); - mDeviceMemorySize += memGpu; - mHostMemorySize += memHost; - } - if (mDeviceProcessingSettings.forceMemoryPoolSize && mDeviceProcessingSettings.forceMemoryPoolSize <= 2 && CanQueryMaxMemory()) { - mDeviceMemorySize = mDeviceProcessingSettings.forceMemoryPoolSize; - } else if (mDeviceProcessingSettings.forceMemoryPoolSize > 2) { - mDeviceMemorySize = mHostMemorySize = mDeviceProcessingSettings.forceMemoryPoolSize; + size_t memPrimary, memPageLocked; + mChains[i]->MemorySize(memPrimary, memPageLocked); + if (!IsGPU() || mOutputControl.OutputType == GPUOutputControl::AllocateInternal) { + memPageLocked = memPrimary; + } + mDeviceMemorySize += memPrimary; + mHostMemorySize += memPageLocked; + } + if (mProcessingSettings.forceMemoryPoolSize && mProcessingSettings.forceMemoryPoolSize <= 2 && CanQueryMaxMemory()) { + mDeviceMemorySize = mProcessingSettings.forceMemoryPoolSize; + } else if (mProcessingSettings.forceMemoryPoolSize > 2) { + mDeviceMemorySize = mProcessingSettings.forceMemoryPoolSize; + if (!IsGPU() || mOutputControl.OutputType == GPUOutputControl::AllocateInternal) { + mHostMemorySize = mDeviceMemorySize; + } } - if (mDeviceProcessingSettings.forceHostMemoryPoolSize) { - mHostMemorySize = mDeviceProcessingSettings.forceHostMemoryPoolSize; + if (mProcessingSettings.forceHostMemoryPoolSize) { + mHostMemorySize = mProcessingSettings.forceHostMemoryPoolSize; } for (unsigned int i = 0; i < mProcessors.size(); i++) { @@ -355,7 +372,7 @@ int GPUReconstruction::Exit() mChains.clear(); // Make sure we destroy a possible ITS GPU tracker before we call the destructors mHostConstantMem.reset(); // Reset these explicitly before the destruction of other members unloads the library - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { for (unsigned int i = 0; i < mMemoryResources.size(); i++) { if (mMemoryResources[i].mReuse >= 0) { continue; @@ -392,7 +409,7 @@ void GPUReconstruction::ComputeReuseMax(GPUProcessor* proc) size_t GPUReconstruction::AllocateRegisteredMemory(GPUProcessor* proc, bool resetCustom) { - if (mDeviceProcessingSettings.debugLevel >= 5) { + if (mProcessingSettings.debugLevel >= 5) { GPUInfo("Allocating memory %p", (void*)proc); } size_t total = 0; @@ -400,12 +417,12 @@ size_t GPUReconstruction::AllocateRegisteredMemory(GPUProcessor* proc, bool rese if (proc == nullptr ? !mMemoryResources[i].mProcessor->mAllocateAndInitializeLate : mMemoryResources[i].mProcessor == proc) { if (!(mMemoryResources[i].mType & GPUMemoryResource::MEMORY_CUSTOM)) { total += AllocateRegisteredMemory(i); - } else if (resetCustom) { + } else if (resetCustom && (mMemoryResources[i].mPtr || mMemoryResources[i].mPtrDevice)) { ResetRegisteredMemoryPointers(i); } } } - if (mDeviceProcessingSettings.debugLevel >= 5) { + if (mProcessingSettings.debugLevel >= 5) { GPUInfo("Allocating memory done"); } return total; @@ -413,7 +430,7 @@ size_t GPUReconstruction::AllocateRegisteredMemory(GPUProcessor* proc, bool rese size_t GPUReconstruction::AllocateRegisteredPermanentMemory() { - if (mDeviceProcessingSettings.debugLevel >= 5) { + if (mProcessingSettings.debugLevel >= 5) { GPUInfo("Allocating Permanent Memory"); } int total = 0; @@ -424,7 +441,7 @@ size_t GPUReconstruction::AllocateRegisteredPermanentMemory() } mHostMemoryPermanent = mHostMemoryPool; mDeviceMemoryPermanent = mDeviceMemoryPool; - if (mDeviceProcessingSettings.debugLevel >= 5) { + if (mProcessingSettings.debugLevel >= 5) { GPUInfo("Permanent Memory Done"); } return total; @@ -443,7 +460,7 @@ size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, GPUError("Insufficient reuse memory %lu < %lu (%s)", mMemoryResources[res->mReuse].mSize, retVal, res->mName); throw std::bad_alloc(); } - if (mDeviceProcessingSettings.allocDebugLevel >= 2) { + if (mProcessingSettings.allocDebugLevel >= 2) { std::cout << "Reused " << res->mName << ": " << retVal << "\n"; } return retVal; @@ -477,7 +494,7 @@ size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, std::cout << "Memory pool size exceeded (" << res->mName << ": " << (memorypoolend ? (memorysize + ((char*)memorypool - (char*)memorypoolend)) : (char*)memorypool - (char*)memorybase) << " < " << memorysize << "\n"; throw std::bad_alloc(); } - if (mDeviceProcessingSettings.allocDebugLevel >= 2) { + if (mProcessingSettings.allocDebugLevel >= 2) { std::cout << "Allocated " << res->mName << ": " << retVal << " - available: " << (memorypoolend ? ((char*)memorypoolend - (char*)memorypool) : (memorysize - ((char*)memorypool - (char*)memorybase))) << "\n"; } return retVal; @@ -485,7 +502,7 @@ size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, void GPUReconstruction::AllocateRegisteredMemoryInternal(GPUMemoryResource* res, GPUOutputControl* control, GPUReconstruction* recPool) { - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL && (control == nullptr || control->OutputType == GPUOutputControl::AllocateInternal)) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL && (control == nullptr || control->OutputType == GPUOutputControl::AllocateInternal)) { if (!(res->mType & GPUMemoryResource::MEMORY_EXTERNAL)) { if (res->mPtrDevice && res->mReuse < 0) { operator delete(res->mPtrDevice GPUCA_OPERATOR_NEW_ALIGNMENT); @@ -502,9 +519,12 @@ void GPUReconstruction::AllocateRegisteredMemoryInternal(GPUMemoryResource* res, } res->mPtr = GPUProcessor::alignPointer<GPUCA_BUFFER_ALIGNMENT>(res->mPtrDevice); res->SetPointers(res->mPtr); - if (mDeviceProcessingSettings.allocDebugLevel >= 2) { + if (mProcessingSettings.allocDebugLevel >= 2) { std::cout << (res->mReuse >= 0 ? "Reused " : "Allocated ") << res->mName << ": " << res->mSize << "\n"; } + if (res->mType & GPUMemoryResource::MEMORY_STACK) { + mNonPersistentIndividualAllocations.emplace_back(res); + } } } else { if (res->mPtr != nullptr) { @@ -514,7 +534,7 @@ void GPUReconstruction::AllocateRegisteredMemoryInternal(GPUMemoryResource* res, if (IsGPU() && res->mOverrideSize < GPUCA_BUFFER_ALIGNMENT) { res->mOverrideSize = GPUCA_BUFFER_ALIGNMENT; } - if ((!IsGPU() || (res->mType & GPUMemoryResource::MEMORY_HOST) || mDeviceProcessingSettings.keepDisplayMemory) && !(res->mType & GPUMemoryResource::MEMORY_EXTERNAL)) { // keepAllMemory --> keepDisplayMemory + if ((!IsGPU() || (res->mType & GPUMemoryResource::MEMORY_HOST) || mProcessingSettings.keepDisplayMemory) && !(res->mType & GPUMemoryResource::MEMORY_EXTERNAL)) { // keepAllMemory --> keepDisplayMemory if (control && control->OutputType == GPUOutputControl::UseExternalBuffer) { if (control->OutputAllocator) { res->mSize = std::max((size_t)res->SetPointers((void*)1) - 1, res->mOverrideSize); @@ -567,7 +587,7 @@ void* GPUReconstruction::AllocateUnmanagedMemory(size_t size, int type) if (type != GPUMemoryResource::MEMORY_HOST && (!IsGPU() || type != GPUMemoryResource::MEMORY_GPU)) { throw std::bad_alloc(); } - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { mUnmanagedChunks.emplace_back(new char[size + GPUCA_BUFFER_ALIGNMENT]); return GPUProcessor::alignPointer<GPUCA_BUFFER_ALIGNMENT>(mUnmanagedChunks.back().get()); } else { @@ -588,6 +608,9 @@ void* GPUReconstruction::AllocateVolatileDeviceMemory(size_t size) if (mVolatileMemoryStart == nullptr) { mVolatileMemoryStart = mDeviceMemoryPool; } + if (size == 0) { + return nullptr; // Future GPU memory allocation is volatile + } char* retVal; GPUProcessor::computePointerWithAlignment(mDeviceMemoryPool, retVal, size); if (mDeviceMemoryPool > mDeviceMemoryPoolEnd) { @@ -636,11 +659,15 @@ void GPUReconstruction::FreeRegisteredMemory(GPUProcessor* proc, bool freeCustom void GPUReconstruction::FreeRegisteredMemory(short ires) { - GPUMemoryResource* res = &mMemoryResources[ires]; - if (mDeviceProcessingSettings.allocDebugLevel >= 2 && (res->mPtr || res->mPtrDevice)) { + FreeRegisteredMemory(&mMemoryResources[ires]); +} + +void GPUReconstruction::FreeRegisteredMemory(GPUMemoryResource* res) +{ + if (mProcessingSettings.allocDebugLevel >= 2 && (res->mPtr || res->mPtrDevice)) { std::cout << "Freeing " << res->mName << ": size " << res->mSize << " (reused " << res->mReuse << ")\n"; } - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL && res->mReuse < 0) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL && res->mReuse < 0) { operator delete(res->mPtrDevice GPUCA_OPERATOR_NEW_ALIGNMENT); } res->mPtr = nullptr; @@ -657,19 +684,31 @@ void GPUReconstruction::ReturnVolatileDeviceMemory() void GPUReconstruction::PushNonPersistentMemory() { - mNonPersistentMemoryStack.emplace_back(mHostMemoryPoolEnd, mDeviceMemoryPoolEnd); + mNonPersistentMemoryStack.emplace_back(mHostMemoryPoolEnd, mDeviceMemoryPoolEnd, mNonPersistentIndividualAllocations.size()); } void GPUReconstruction::PopNonPersistentMemory(RecoStep step) { - if (mDeviceProcessingSettings.debugLevel >= 3 || mDeviceProcessingSettings.allocDebugLevel) { - printf("Allocated memory after %30s: %'13lld (non temporary %'13lld, blocked %'13lld)\n", GPUDataTypes::RECO_STEP_NAMES[getRecoStepNum(step, true)], ptrDiff(mDeviceMemoryPool, mDeviceMemoryBase) + ptrDiff((char*)mDeviceMemoryBase + mDeviceMemorySize, mDeviceMemoryPoolEnd), ptrDiff(mDeviceMemoryPool, mDeviceMemoryBase), mDeviceMemoryPoolBlocked == nullptr ? 0ll : ptrDiff((char*)mDeviceMemoryBase + mDeviceMemorySize, mDeviceMemoryPoolBlocked)); + if ((mProcessingSettings.debugLevel >= 3 || mProcessingSettings.allocDebugLevel) && (IsGPU() || mProcessingSettings.forceHostMemoryPoolSize)) { + if (IsGPU()) { + printf("Allocated Device memory after %30s: %'13lld (non temporary %'13lld, blocked %'13lld)\n", GPUDataTypes::RECO_STEP_NAMES[getRecoStepNum(step, true)], ptrDiff(mDeviceMemoryPool, mDeviceMemoryBase) + ptrDiff((char*)mDeviceMemoryBase + mDeviceMemorySize, mDeviceMemoryPoolEnd), ptrDiff(mDeviceMemoryPool, mDeviceMemoryBase), mDeviceMemoryPoolBlocked == nullptr ? 0ll : ptrDiff((char*)mDeviceMemoryBase + mDeviceMemorySize, mDeviceMemoryPoolBlocked)); + } + printf("Allocated Host memory after %30s: %'13lld (non temporary %'13lld, blocked %'13lld)\n", GPUDataTypes::RECO_STEP_NAMES[getRecoStepNum(step, true)], ptrDiff(mHostMemoryPool, mHostMemoryBase) + ptrDiff((char*)mHostMemoryBase + mHostMemorySize, mHostMemoryPoolEnd), ptrDiff(mHostMemoryPool, mHostMemoryBase), mHostMemoryPoolBlocked == nullptr ? 0ll : ptrDiff((char*)mHostMemoryBase + mHostMemorySize, mHostMemoryPoolBlocked)); } - if (mDeviceProcessingSettings.keepDisplayMemory || mDeviceProcessingSettings.disableMemoryReuse) { + if (mProcessingSettings.keepDisplayMemory || mProcessingSettings.disableMemoryReuse) { return; } - mHostMemoryPoolEnd = mNonPersistentMemoryStack.back().first; - mDeviceMemoryPoolEnd = mNonPersistentMemoryStack.back().second; + mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); + mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); + for (unsigned int i = std::get<2>(mNonPersistentMemoryStack.back()); i < mNonPersistentIndividualAllocations.size(); i++) { + GPUMemoryResource* res = mNonPersistentIndividualAllocations[i]; + if (res->mReuse < 0) { + operator delete(res->mPtrDevice GPUCA_OPERATOR_NEW_ALIGNMENT); + } + res->mPtr = nullptr; + res->mPtrDevice = nullptr; + } + mNonPersistentIndividualAllocations.resize(std::get<2>(mNonPersistentMemoryStack.back())); mNonPersistentMemoryStack.pop_back(); } @@ -710,6 +749,7 @@ void GPUReconstruction::ClearAllocatedMemory(bool clearOutputs) mUnmanagedChunks.clear(); mVolatileMemoryStart = nullptr; mNonPersistentMemoryStack.clear(); + mNonPersistentIndividualAllocations.clear(); mHostMemoryPoolEnd = mHostMemoryPoolBlocked ? mHostMemoryPoolBlocked : ((char*)mHostMemoryBase + mHostMemorySize); mDeviceMemoryPoolEnd = mDeviceMemoryPoolBlocked ? mDeviceMemoryPoolBlocked : ((char*)mDeviceMemoryBase + mDeviceMemorySize); } @@ -727,7 +767,7 @@ void GPUReconstruction::PrintMemoryMax() void GPUReconstruction::PrintMemoryOverview() { - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { printf("Memory Allocation: Host %'lld / %'lld (Permanent %'lld), Device %'lld / %'lld, (Permanent %'lld) %d chunks\n", ptrDiff(mHostMemoryPool, mHostMemoryBase) + ptrDiff((char*)mHostMemoryBase + mHostMemorySize, mHostMemoryPoolEnd), (long long int)mHostMemorySize, ptrDiff(mHostMemoryPermanent, mHostMemoryBase), ptrDiff(mDeviceMemoryPool, mDeviceMemoryBase) + ptrDiff((char*)mDeviceMemoryBase + mDeviceMemorySize, mDeviceMemoryPoolEnd), (long long int)mDeviceMemorySize, ptrDiff(mDeviceMemoryPermanent, mDeviceMemoryBase), (int)mMemoryResources.size()); @@ -782,10 +822,10 @@ int GPUReconstruction::getGeneralStepNum(GeneralStep step, bool validCheck) { re void GPUReconstruction::RunPipelineWorker() { - if (!mInitialized || !mDeviceProcessingSettings.doublePipeline || mMaster != nullptr || !mSlaves.size()) { + if (!mInitialized || !mProcessingSettings.doublePipeline || mMaster != nullptr || !mSlaves.size()) { throw std::invalid_argument("Cannot start double pipeline mode"); } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("Pipeline worker started"); } bool terminate = false; @@ -809,7 +849,7 @@ void GPUReconstruction::RunPipelineWorker() q->done = true; q->c.notify_one(); } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("Pipeline worker ended"); } } @@ -870,11 +910,11 @@ void GPUReconstruction::PrepareEvent() // TODO: Clean this up, this should not b AllocateRegisteredMemory(nullptr); } -int GPUReconstruction::CheckErrorCodes() +int GPUReconstruction::CheckErrorCodes(bool cpuOnly) { int retVal = 0; for (unsigned int i = 0; i < mChains.size(); i++) { - if (mChains[i]->CheckErrorCodes()) { + if (mChains[i]->CheckErrorCodes(cpuOnly)) { retVal++; } } @@ -892,7 +932,7 @@ void GPUReconstruction::DumpSettings(const char* dir) } } -void GPUReconstruction::UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsDeviceProcessing* p) +void GPUReconstruction::UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsProcessing* p) { param().UpdateEventSettings(e, p); if (mInitialized) { @@ -905,7 +945,7 @@ int GPUReconstruction::ReadSettings(const char* dir) std::string f; f = dir; f += "settings.dump"; - mEventSettings.SetDefaults(); + new (&mEventSettings) GPUSettingsEvent; if (ReadStructFromFile(f.c_str(), &mEventSettings)) { return 1; } @@ -916,15 +956,23 @@ int GPUReconstruction::ReadSettings(const char* dir) return 0; } -void GPUReconstruction::SetSettings(float solenoidBz) +void GPUReconstruction::SetSettings(float solenoidBz, const GPURecoStepConfiguration* workflow) { +#ifdef GPUCA_O2_LIB + GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(); + if (config.configEvent.solenoidBz <= -1e6f) { + config.configEvent.solenoidBz = solenoidBz; + } + SetSettings(&config.configEvent, &config.configReconstruction, &config.configProcessing, workflow); +#else GPUSettingsEvent ev; - ev.SetDefaults(); ev.solenoidBz = solenoidBz; - SetSettings(&ev, nullptr, nullptr); + SetSettings(&ev, nullptr, nullptr, workflow); +#endif } -void GPUReconstruction::SetSettings(const GPUSettingsEvent* settings, const GPUSettingsRec* rec, const GPUSettingsDeviceProcessing* proc, const GPURecoStepConfiguration* workflow) +void GPUReconstruction::SetSettings(const GPUSettingsEvent* settings, const GPUSettingsRec* rec, const GPUSettingsProcessing* proc, const GPURecoStepConfiguration* workflow) { if (mInitialized) { GPUError("Cannot update settings while initialized"); @@ -932,7 +980,7 @@ void GPUReconstruction::SetSettings(const GPUSettingsEvent* settings, const GPUS } mEventSettings = *settings; if (proc) { - mDeviceProcessingSettings = *proc; + mProcessingSettings = *proc; } if (workflow) { mRecoSteps = workflow->steps; @@ -955,19 +1003,22 @@ void GPUReconstruction::SetInputControl(void* ptr, size_t size) mInputControl.set(ptr, size); } +GPUReconstruction::GPUThreadContext::GPUThreadContext() = default; +GPUReconstruction::GPUThreadContext::~GPUThreadContext() = default; + std::unique_ptr<GPUReconstruction::GPUThreadContext> GPUReconstruction::GetThreadContext() { return std::unique_ptr<GPUReconstruction::GPUThreadContext>(new GPUThreadContext); } GPUReconstruction* GPUReconstruction::CreateInstance(DeviceType type, bool forceType, GPUReconstruction* master) { - GPUSettingsProcessing cfg; - cfg.SetDefaults(); + GPUSettingsDeviceBackend cfg; + new (&cfg) GPUSettingsDeviceBackend; cfg.deviceType = type; cfg.forceDeviceType = forceType; cfg.master = master; return CreateInstance(cfg); } -GPUReconstruction* GPUReconstruction::CreateInstance(const GPUSettingsProcessing& cfg) +GPUReconstruction* GPUReconstruction::CreateInstance(const GPUSettingsDeviceBackend& cfg) { GPUReconstruction* retVal = nullptr; unsigned int type = cfg.deviceType; @@ -999,7 +1050,7 @@ GPUReconstruction* GPUReconstruction::CreateInstance(const GPUSettingsProcessing GPUError("Error: Could not load GPUReconstruction for specified device: %s (%u)", DEVICE_TYPE_NAMES[type], type); } else { GPUError("Could not load GPUReconstruction for device type %s (%u), falling back to CPU version", DEVICE_TYPE_NAMES[type], type); - GPUSettingsProcessing cfg2 = cfg; + GPUSettingsDeviceBackend cfg2 = cfg; cfg2.deviceType = DeviceType::CPU; retVal = CreateInstance(cfg2); } @@ -1106,7 +1157,7 @@ int GPUReconstruction::LibraryLoader::LoadLibrary() return 0; } -GPUReconstruction* GPUReconstruction::LibraryLoader::GetPtr(const GPUSettingsProcessing& cfg) +GPUReconstruction* GPUReconstruction::LibraryLoader::GetPtr(const GPUSettingsDeviceBackend& cfg) { if (LoadLibrary()) { return nullptr; @@ -1114,7 +1165,7 @@ GPUReconstruction* GPUReconstruction::LibraryLoader::GetPtr(const GPUSettingsPro if (mGPUEntry == nullptr) { return nullptr; } - GPUReconstruction* (*tmp)(const GPUSettingsProcessing& cfg) = (GPUReconstruction * (*)(const GPUSettingsProcessing& cfg)) mGPUEntry; + GPUReconstruction* (*tmp)(const GPUSettingsDeviceBackend& cfg) = (GPUReconstruction * (*)(const GPUSettingsDeviceBackend& cfg)) mGPUEntry; return tmp(cfg); } diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index bff0aa230ad24..f61f7366b9739 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -30,7 +30,6 @@ #include "GPUMemoryResource.h" #include "GPUConstantMem.h" #include "GPUTPCSliceOutput.h" -#include "GPUDataTypes.h" #include "GPULogging.h" namespace o2 @@ -47,7 +46,7 @@ namespace GPUCA_NAMESPACE namespace gpu { class GPUChain; -class GPUMemorySizeScalers; +struct GPUMemorySizeScalers; struct GPUReconstructionPipelineContext; class GPUReconstruction @@ -105,7 +104,7 @@ class GPUReconstruction static unsigned int getNIOTypeMultiplicity(InOutPointerType type) { return (type == CLUSTER_DATA || type == SLICE_OUT_TRACK || type == SLICE_OUT_CLUSTER || type == RAW_CLUSTERS || type == TPC_DIGIT) ? NSLICES : 1; } // Functionality to create an instance of GPUReconstruction for the desired device - static GPUReconstruction* CreateInstance(const GPUSettingsProcessing& cfg); + static GPUReconstruction* CreateInstance(const GPUSettingsDeviceBackend& cfg); static GPUReconstruction* CreateInstance(DeviceType type = DeviceType::CPU, bool forceType = true, GPUReconstruction* master = nullptr); static GPUReconstruction* CreateInstance(int type, bool forceType, GPUReconstruction* master = nullptr) { return CreateInstance((DeviceType)type, forceType, master); } static GPUReconstruction* CreateInstance(const char* type, bool forceType, GPUReconstruction* master = nullptr); @@ -147,10 +146,11 @@ class GPUReconstruction }; struct krnlProperties { - krnlProperties(int t = 0, int b = 1) : nThreads(t), minBlocks(b) {} + krnlProperties(int t = 0, int b = 1, int b2 = 0) : nThreads(t), minBlocks(b), forceBlocks(b2) {} unsigned int nThreads; unsigned int minBlocks; - unsigned int total() { return nThreads * minBlocks; } + unsigned int forceBlocks; + unsigned int total() { return forceBlocks ? forceBlocks : (nThreads * minBlocks); } }; struct krnlSetup { @@ -180,7 +180,7 @@ class GPUReconstruction virtual void* getGPUPointer(void* ptr) { return ptr; } virtual void startGPUProfiling() {} virtual void endGPUProfiling() {} - int CheckErrorCodes(); + int CheckErrorCodes(bool cpuOnly = false); void RunPipelineWorker(); void TerminatePipelineWorker(); @@ -217,19 +217,19 @@ class GPUReconstruction bool slavesExist() { return mSlaves.size() || mMaster; } // Getters / setters for parameters - DeviceType GetDeviceType() const { return (DeviceType)mProcessingSettings.deviceType; } + DeviceType GetDeviceType() const { return (DeviceType)mDeviceBackendSettings.deviceType; } bool IsGPU() const { return GetDeviceType() != DeviceType::INVALID_DEVICE && GetDeviceType() != DeviceType::CPU; } const GPUParam& GetParam() const { return mHostConstantMem->param; } const GPUConstantMem& GetConstantMem() const { return *mHostConstantMem; } const GPUSettingsEvent& GetEventSettings() const { return mEventSettings; } - const GPUSettingsProcessing& GetProcessingSettings() { return mProcessingSettings; } - const GPUSettingsDeviceProcessing& GetDeviceProcessingSettings() const { return mDeviceProcessingSettings; } + const GPUSettingsDeviceBackend& GetDeviceBackendSettings() { return mDeviceBackendSettings; } + const GPUSettingsProcessing& GetProcessingSettings() const { return mProcessingSettings; } bool IsInitialized() const { return mInitialized; } - void SetSettings(float solenoidBz); - void SetSettings(const GPUSettingsEvent* settings, const GPUSettingsRec* rec = nullptr, const GPUSettingsDeviceProcessing* proc = nullptr, const GPURecoStepConfiguration* workflow = nullptr); - void SetResetTimers(bool reset) { mDeviceProcessingSettings.resetTimers = reset; } // May update also after Init() - void SetDebugLevelTmp(int level) { mDeviceProcessingSettings.debugLevel = level; } // Temporarily, before calling SetSettings() - void UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsDeviceProcessing* p = nullptr); + void SetSettings(float solenoidBz, const GPURecoStepConfiguration* workflow = nullptr); + void SetSettings(const GPUSettingsEvent* settings, const GPUSettingsRec* rec = nullptr, const GPUSettingsProcessing* proc = nullptr, const GPURecoStepConfiguration* workflow = nullptr); + void SetResetTimers(bool reset) { mProcessingSettings.resetTimers = reset; } // May update also after Init() + void SetDebugLevelTmp(int level) { mProcessingSettings.debugLevel = level; } // Temporarily, before calling SetSettings() + void UpdateEventSettings(const GPUSettingsEvent* e, const GPUSettingsProcessing* p = nullptr); void SetOutputControl(const GPUOutputControl& v) { mOutputControl = v; } void SetOutputControl(void* ptr, size_t size); void SetInputControl(void* ptr, size_t size); @@ -260,7 +260,8 @@ class GPUReconstruction protected: void AllocateRegisteredMemoryInternal(GPUMemoryResource* res, GPUOutputControl* control, GPUReconstruction* recPool); - GPUReconstruction(const GPUSettingsProcessing& cfg); // Constructor + void FreeRegisteredMemory(GPUMemoryResource* res); + GPUReconstruction(const GPUSettingsDeviceBackend& cfg); // Constructor int InitPhaseBeforeDevice(); virtual void UpdateSettings() {} virtual int InitDevice() = 0; @@ -277,8 +278,8 @@ class GPUReconstruction class GPUThreadContext { public: - GPUThreadContext() = default; - virtual ~GPUThreadContext() = default; + GPUThreadContext(); + virtual ~GPUThreadContext(); }; virtual std::unique_ptr<GPUThreadContext> GetThreadContext(); @@ -319,8 +320,8 @@ class GPUReconstruction // Settings GPUSettingsEvent mEventSettings; // Event Parameters - GPUSettingsProcessing mProcessingSettings; // Processing Parameters (at constructor level) - GPUSettingsDeviceProcessing mDeviceProcessingSettings; // Processing Parameters (at init level) + GPUSettingsDeviceBackend mDeviceBackendSettings; // Processing Parameters (at constructor level) + GPUSettingsProcessing mProcessingSettings; // Processing Parameters (at init level) GPUOutputControl mOutputControl; // Controls the output of the individual components GPUOutputControl mInputControl; // Prefefined input memory location for reading standalone dumps std::unique_ptr<GPUMemorySizeScalers> mMemoryScalers; // Scalers how much memory will be needed @@ -380,7 +381,8 @@ class GPUReconstruction std::vector<unsigned short> res; }; std::unordered_map<GPUMemoryReuse::ID, MemoryReuseMeta> mMemoryReuse1to1; - std::vector<std::pair<void*, void*>> mNonPersistentMemoryStack; + std::vector<std::tuple<void*, void*, size_t>> mNonPersistentMemoryStack; + std::vector<GPUMemoryResource*> mNonPersistentIndividualAllocations; std::unique_ptr<GPUReconstructionPipelineContext> mPipelineContext; @@ -397,7 +399,7 @@ class GPUReconstruction LibraryLoader(const char* lib, const char* func); int LoadLibrary(); int CloseLibrary(); - GPUReconstruction* GetPtr(const GPUSettingsProcessing& cfg); + GPUReconstruction* GetPtr(const GPUSettingsDeviceBackend& cfg); const char* mLibName; const char* mFuncName; @@ -406,7 +408,7 @@ class GPUReconstruction }; static std::shared_ptr<LibraryLoader> sLibCUDA, sLibHIP, sLibOCL, sLibOCL2; - static GPUReconstruction* GPUReconstruction_Create_CPU(const GPUSettingsProcessing& cfg); + static GPUReconstruction* GPUReconstruction_Create_CPU(const GPUSettingsDeviceBackend& cfg); }; template <class T> @@ -426,7 +428,7 @@ inline T* GPUReconstruction::AllocateIOMemoryHelper(size_t n, const T*& ptr, std } else { u.reset(new T[n]); retVal = u.get(); - if (mDeviceProcessingSettings.registerStandaloneInputMemory) { + if (mProcessingSettings.registerStandaloneInputMemory) { if (registerMemoryForGPU(u.get(), n * sizeof(T))) { GPUError("Error registering memory for GPU: %p - %lld bytes\n", (void*)u.get(), (long long int)(n * sizeof(T))); throw std::bad_alloc(); @@ -448,7 +450,7 @@ template <class T> inline short GPUReconstruction::RegisterMemoryAllocation(T* proc, void* (T::*setPtr)(void*), int type, const char* name, const GPUMemoryReuse& re) { if (!(type & (GPUMemoryResource::MEMORY_HOST | GPUMemoryResource::MEMORY_GPU))) { - if ((type & GPUMemoryResource::MEMORY_SCRATCH) && !mDeviceProcessingSettings.keepDisplayMemory) { // keepAllMemory --> keepDisplayMemory + if ((type & GPUMemoryResource::MEMORY_SCRATCH) && !mProcessingSettings.keepDisplayMemory) { // keepAllMemory --> keepDisplayMemory type |= (proc->mGPUProcessorType == GPUProcessor::PROCESSOR_TYPE_CPU ? GPUMemoryResource::MEMORY_HOST : GPUMemoryResource::MEMORY_GPU); } else { type |= GPUMemoryResource::MEMORY_HOST | GPUMemoryResource::MEMORY_GPU; @@ -462,7 +464,7 @@ inline short GPUReconstruction::RegisterMemoryAllocation(T* proc, void* (T::*set throw std::bad_alloc(); } unsigned short retVal = mMemoryResources.size() - 1; - if (re.type != GPUMemoryReuse::NONE && !mDeviceProcessingSettings.disableMemoryReuse) { + if (re.type != GPUMemoryReuse::NONE && !mProcessingSettings.disableMemoryReuse) { const auto& it = mMemoryReuse1to1.find(re.id); if (it == mMemoryReuse1to1.end()) { mMemoryReuse1to1[re.id] = {proc, retVal}; @@ -548,7 +550,7 @@ inline size_t GPUReconstruction::ReadData(FILE* fp, const T** entries, S* num, s numTotal += num[i]; } (void)r; - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Read %lld %s", (long long int)numTotal, IOTYPENAMES[type]); } return numTotal; @@ -588,7 +590,7 @@ inline std::unique_ptr<T> GPUReconstruction::ReadFlatObjectFromFile(const char* r = fread((void*)retVal.get(), 1, size[0], fp); r = fread(buf, 1, size[1], fp); fclose(fp); - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Read %lld bytes from %s", (long long int)r, file); } retVal->clearInternalBufferPtr(); @@ -627,7 +629,7 @@ inline std::unique_ptr<T> GPUReconstruction::ReadStructFromFile(const char* file std::unique_ptr<T> newObj(new T); r = fread(newObj.get(), 1, size, fp); fclose(fp); - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Read %lld bytes from %s", (long long int)r, file); } return newObj; @@ -648,7 +650,7 @@ inline int GPUReconstruction::ReadStructFromFile(const char* file, T* obj) } r = fread(obj, 1, size, fp); fclose(fp); - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Read %lld bytes from %s", (long long int)r, file); } return 0; diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index f00c7112cd9fe..e82f45c5a307d 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -51,7 +51,7 @@ using namespace GPUCA_NAMESPACE::gpu; constexpr GPUReconstructionCPU::krnlRunRange GPUReconstructionCPU::krnlRunRangeNone; constexpr GPUReconstructionCPU::krnlEvent GPUReconstructionCPU::krnlEventNone; -GPUReconstruction* GPUReconstruction::GPUReconstruction_Create_CPU(const GPUSettingsProcessing& cfg) { return new GPUReconstructionCPU(cfg); } +GPUReconstruction* GPUReconstruction::GPUReconstruction_Create_CPU(const GPUSettingsDeviceBackend& cfg) { return new GPUReconstructionCPU(cfg); } GPUReconstructionCPU::~GPUReconstructionCPU() { @@ -71,8 +71,12 @@ int GPUReconstructionCPUBackend::runKernelBackend(krnlSetup& _xyz, const Args&.. } unsigned int num = y.num == 0 || y.num == -1 ? 1 : y.num; for (unsigned int k = 0; k < num; k++) { - if (mDeviceProcessingSettings.ompKernels) { - GPUCA_OPENMP(parallel for num_threads(mDeviceProcessingSettings.nThreads)) + int ompThreads = mProcessingSettings.ompKernels ? (mProcessingSettings.ompKernels == 2 ? ((mProcessingSettings.ompThreads + mNestedLoopOmpFactor - 1) / mNestedLoopOmpFactor) : mProcessingSettings.ompThreads) : 1; + if (ompThreads > 1) { + if (mProcessingSettings.debugLevel >= 5) { + printf("Running %d ompThreads\n", ompThreads); + } + GPUCA_OPENMP(parallel for num_threads(ompThreads)) for (unsigned int iB = 0; iB < x.nBlocks; iB++) { typename T::GPUSharedMemory smem; T::template Thread<I>(x.nBlocks, 1, iB, 0, smem, T::Processor(*mHostConstantMem)[y.start + k], args...); @@ -125,7 +129,7 @@ size_t GPUReconstructionCPU::TransferMemoryResourcesHelper(GPUProcessor* proc, i if (!(res.mType & GPUMemoryResource::MEMORY_GPU) || (res.mType & GPUMemoryResource::MEMORY_CUSTOM_TRANSFER)) { continue; } - if (!mDeviceProcessingSettings.keepAllMemory && !all && (res.mType & exc) && !(res.mType & inc)) { + if (!mProcessingSettings.keepAllMemory && !all && (res.mType & exc) && !(res.mType & inc)) { continue; } if (toGPU) { @@ -151,7 +155,7 @@ int GPUReconstructionCPU::GetThread() int GPUReconstructionCPU::InitDevice() { - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { if (mDeviceMemorySize > mHostMemorySize) { mHostMemorySize = mDeviceMemorySize; } @@ -161,16 +165,17 @@ int GPUReconstructionCPU::InitDevice() mHostMemoryPermanent = mHostMemoryBase; ClearAllocatedMemory(); } - if (mDeviceProcessingSettings.ompKernels) { + if (mProcessingSettings.ompKernels) { mBlockCount = getOMPMaxThreads(); } mThreadId = GetThread(); + mProcShadow.mProcessorsProc = processors(); return 0; } int GPUReconstructionCPU::ExitDevice() { - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { if (mMaster == nullptr) { operator delete(mHostMemoryBase); } @@ -186,13 +191,13 @@ int GPUReconstructionCPU::RunChains() mNEventsProcessed++; timerTotal.Start(); - if (mDeviceProcessingSettings.doublePipeline) { + if (mProcessingSettings.doublePipeline) { if (EnqueuePipeline()) { return 1; } } else { if (mThreadId != GetThread()) { - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Thread changed, migrating context, Previous Thread: %d, New Thread: %d", mThreadId, GetThread()); } mThreadId = GetThread(); @@ -210,7 +215,7 @@ int GPUReconstructionCPU::RunChains() timerTotal.Stop(); mStatWallTime = (timerTotal.GetElapsedTime() * 1000000. / mStatNEvents); - if (GetDeviceProcessingSettings().debugLevel >= 1) { + if (GetProcessingSettings().debugLevel >= 1) { double kernelTotal = 0; std::vector<double> kernelStepTimes(GPUDataTypes::N_RECO_STEPS); @@ -222,7 +227,7 @@ int GPUReconstructionCPU::RunChains() for (int j = 0; j < mTimers[i]->num; j++) { HighResTimer& timer = mTimers[i]->timer[j]; time += timer.GetElapsedTime(); - if (mDeviceProcessingSettings.resetTimers) { + if (mProcessingSettings.resetTimers) { timer.Reset(); } } @@ -239,7 +244,7 @@ int GPUReconstructionCPU::RunChains() snprintf(bandwidth, 256, " (%6.3f GB/s - %'14lu bytes)", mTimers[i]->memSize / time * 1e-9, (unsigned long)(mTimers[i]->memSize / mStatNEvents)); } printf("Execution Time: Task (%c %8ux): %50s Time: %'10d us%s\n", type, mTimers[i]->count, mTimers[i]->name.c_str(), (int)(time * 1000000 / mStatNEvents), bandwidth); - if (mDeviceProcessingSettings.resetTimers) { + if (mProcessingSettings.resetTimers) { mTimers[i]->count = 0; mTimers[i]->memSize = 0; } @@ -256,7 +261,7 @@ int GPUReconstructionCPU::RunChains() printf("Execution Time: Step (D %8ux): %11s %38s Time: %'10d us (%6.3f GB/s - %'14lu bytes - %'14lu per call)\n", mTimersRecoSteps[i].countToHost, "DMA to Host", GPUDataTypes::RECO_STEP_NAMES[i], (int)(mTimersRecoSteps[i].timerToHost.GetElapsedTime() * 1000000 / mStatNEvents), mTimersRecoSteps[i].bytesToHost / mTimersRecoSteps[i].timerToHost.GetElapsedTime() * 1e-9, mTimersRecoSteps[i].bytesToHost / mStatNEvents, mTimersRecoSteps[i].bytesToHost / mTimersRecoSteps[i].countToHost); } - if (mDeviceProcessingSettings.resetTimers) { + if (mProcessingSettings.resetTimers) { mTimersRecoSteps[i].bytesToGPU = mTimersRecoSteps[i].bytesToHost = 0; mTimersRecoSteps[i].timerToGPU.Reset(); mTimersRecoSteps[i].timerToHost.Reset(); @@ -273,10 +278,10 @@ int GPUReconstructionCPU::RunChains() mStatKernelTime = kernelTotal * 1000000 / mStatNEvents; printf("Execution Time: Total : %50s Time: %'10d us\n", "Total Kernel", (int)mStatKernelTime); printf("Execution Time: Total : %50s Time: %'10d us\n", "Total Wall", (int)mStatWallTime); - } else if (GetDeviceProcessingSettings().debugLevel >= 0) { - printf("Total Wall Time: %'d us\n", (int)mStatWallTime); + } else if (GetProcessingSettings().debugLevel >= 0) { + GPUInfo("Total Wall Time: %d us", (int)mStatWallTime); } - if (mDeviceProcessingSettings.resetTimers) { + if (mProcessingSettings.resetTimers) { mStatNEvents = 0; timerTotal.Reset(); } @@ -307,8 +312,9 @@ static std::atomic_flag timerFlag; // TODO: Should be a class member not global, GPUReconstructionCPU::timerMeta* GPUReconstructionCPU::insertTimer(unsigned int id, std::string&& name, int J, int num, int type, RecoStep step) { - while (timerFlag.test_and_set()) + while (timerFlag.test_and_set()) { ; + } if (mTimers.size() <= id) { mTimers.resize(id + 1); } @@ -326,8 +332,9 @@ GPUReconstructionCPU::timerMeta* GPUReconstructionCPU::insertTimer(unsigned int GPUReconstructionCPU::timerMeta* GPUReconstructionCPU::getTimerById(unsigned int id) { timerMeta* retVal = nullptr; - while (timerFlag.test_and_set()) + while (timerFlag.test_and_set()) { ; + } if (mTimers.size() > id && mTimers[id]) { retVal = mTimers[id].get(); retVal->count++; @@ -341,3 +348,16 @@ unsigned int GPUReconstructionCPU::getNextTimerId() static std::atomic<unsigned int> id{0}; return id.fetch_add(1); } + +unsigned int GPUReconstructionCPU::SetAndGetNestedLoopOmpFactor(bool condition, unsigned int max) +{ + if (condition && mProcessingSettings.ompKernels != 1) { + mNestedLoopOmpFactor = mProcessingSettings.ompKernels == 2 ? std::min<unsigned int>(max, mProcessingSettings.ompThreads) : mProcessingSettings.ompThreads; + } else { + mNestedLoopOmpFactor = 1; + } + if (mProcessingSettings.debugLevel >= 5) { + printf("Running %d OMP threads in outer loop\n", mNestedLoopOmpFactor); + } + return mNestedLoopOmpFactor; +} diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.h b/GPU/GPUTracking/Base/GPUReconstructionCPU.h index 940d50385fc84..201c6d0f85ee2 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.h +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.h @@ -39,6 +39,7 @@ #include "GPUTPCConvertKernel.h" #include "GPUTPCCompressionKernels.h" #include "GPUTPCClusterFinderKernels.h" +#include "GPUTrackingRefitKernel.h" #endif namespace GPUCA_NAMESPACE @@ -51,11 +52,12 @@ class GPUReconstructionCPUBackend : public GPUReconstruction ~GPUReconstructionCPUBackend() override = default; protected: - GPUReconstructionCPUBackend(const GPUSettingsProcessing& cfg) : GPUReconstruction(cfg) {} + GPUReconstructionCPUBackend(const GPUSettingsDeviceBackend& cfg) : GPUReconstruction(cfg) {} template <class T, int I = 0, typename... Args> int runKernelBackend(krnlSetup& _xyz, const Args&... args); template <class T, int I> krnlProperties getKernelPropertiesBackend(); + unsigned int mNestedLoopOmpFactor = 1; }; template <class T> @@ -66,7 +68,7 @@ class GPUReconstructionKernels : public T template <class X, int Y = 0> using classArgument = GPUReconstruction::classArgument<X, Y>; virtual ~GPUReconstructionKernels() = default; // NOLINT: BUG: Do not declare override in template class! AMD hcc will not create the destructor otherwise. - GPUReconstructionKernels(const GPUSettingsProcessing& cfg) : T(cfg) {} + GPUReconstructionKernels(const GPUSettingsDeviceBackend& cfg) : T(cfg) {} protected: #define GPUCA_KRNL(x_class, attributes, x_arguments, x_forward) \ @@ -92,7 +94,7 @@ class GPUReconstructionKernels<GPUReconstructionCPUBackend> : public GPUReconstr template <class X, int Y = 0> using classArgument = GPUReconstruction::classArgument<X, Y>; virtual ~GPUReconstructionKernels() = default; // NOLINT: Do not declare override in template class! AMD hcc will not create the destructor otherwise. - GPUReconstructionKernels(const GPUSettingsProcessing& cfg) : GPUReconstructionCPUBackend(cfg) {} + GPUReconstructionKernels(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionCPUBackend(cfg) {} protected: #define GPUCA_KRNL(x_class, x_attributes, x_arguments, x_forward) \ @@ -105,7 +107,7 @@ class GPUReconstructionKernels<GPUReconstructionCPUBackend> : public GPUReconstr class GPUReconstructionCPU : public GPUReconstructionKernels<GPUReconstructionCPUBackend> { - friend GPUReconstruction* GPUReconstruction::GPUReconstruction_Create_CPU(const GPUSettingsProcessing& cfg); + friend GPUReconstruction* GPUReconstruction::GPUReconstruction_Create_CPU(const GPUSettingsDeviceBackend& cfg); friend class GPUChain; public: @@ -147,6 +149,9 @@ class GPUReconstructionCPU : public GPUReconstructionKernels<GPUReconstructionCP HighResTimer& getRecoStepTimer(RecoStep step) { return mTimersRecoSteps[getRecoStepNum(step)].timerTotal; } HighResTimer& getGeneralStepTimer(GeneralStep step) { return mTimersGeneralSteps[getGeneralStepNum(step)]; } + void SetNestedLoopOmpFactor(unsigned int f) { mNestedLoopOmpFactor = f; } + unsigned int SetAndGetNestedLoopOmpFactor(bool condition, unsigned int max); + protected: struct GPUProcessorProcessors : public GPUProcessor { GPUConstantMem* mProcessorsProc = nullptr; @@ -154,7 +159,7 @@ class GPUReconstructionCPU : public GPUReconstructionKernels<GPUReconstructionCP short mMemoryResProcessors = -1; }; - GPUReconstructionCPU(const GPUSettingsProcessing& cfg) : GPUReconstructionKernels(cfg) {} + GPUReconstructionCPU(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionKernels(cfg) {} virtual void SynchronizeStream(int stream) {} virtual void SynchronizeEvents(deviceEvent* evList, int nEvents = 1) {} @@ -251,7 +256,7 @@ inline int GPUReconstructionCPU::runKernel(const krnlExec& x, const krnlRunRange unsigned int nBlocks = x.nBlocks; auto prop = getKernelProperties<S, I>(); const int autoThreads = cpuFallback ? 1 : prop.nThreads; - const int autoBlocks = cpuFallback ? 1 : (prop.minBlocks * mBlockCount); + const int autoBlocks = cpuFallback ? 1 : (prop.forceBlocks ? prop.forceBlocks : (prop.minBlocks * mBlockCount)); if (nBlocks == (unsigned int)-1) { nBlocks = (nThreads + autoThreads - 1) / autoThreads; nThreads = autoThreads; @@ -267,15 +272,15 @@ inline int GPUReconstructionCPU::runKernel(const krnlExec& x, const krnlRunRange if (nThreads > GPUCA_MAX_THREADS) { throw std::runtime_error("GPUCA_MAX_THREADS exceeded"); } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("Running kernel %s (Stream %d, Range %d/%d, Grid %d/%d) on %s", GetKernelName<S, I>(), x.stream, y.start, y.num, nBlocks, nThreads, cpuFallback == 2 ? "CPU (forced)" : cpuFallback ? "CPU (fallback)" : mDeviceName.c_str()); } if (nThreads == 0 || nBlocks == 0) { return 0; } - if (mDeviceProcessingSettings.debugLevel >= 0) { + if (mProcessingSettings.debugLevel >= 1) { t = &getKernelTimer<S, I, J>(myStep, !IsGPU() || cpuFallback ? getOMPThreadNum() : x.stream); - if (!mDeviceProcessingSettings.deviceTimers || !IsGPU() || cpuFallback) { + if (!mProcessingSettings.deviceTimers || !IsGPU() || cpuFallback) { t->Start(); } } @@ -289,18 +294,18 @@ inline int GPUReconstructionCPU::runKernel(const krnlExec& x, const krnlRunRange return 1; } } - if (mDeviceProcessingSettings.debugLevel >= 0) { - if (GPUDebug(GetKernelName<S, I>(), x.stream)) { - throw std::runtime_error("kernel failure"); - } + if (GPUDebug(GetKernelName<S, I>(), x.stream)) { + throw std::runtime_error("kernel failure"); + } + if (mProcessingSettings.debugLevel >= 1) { if (t) { - if (!mDeviceProcessingSettings.deviceTimers || !IsGPU() || cpuFallback) { + if (!mProcessingSettings.deviceTimers || !IsGPU() || cpuFallback) { t->Stop(); } else { t->AddTime(setup.t); } } - if (mDeviceProcessingSettings.debugLevel >= 1 && CheckErrorCodes()) { + if (CheckErrorCodes(cpuFallback)) { throw std::runtime_error("kernel error code"); } } @@ -346,7 +351,7 @@ HighResTimer& GPUReconstructionCPU::getTimer(const char* name, int num) static int id = getNextTimerId(); timerMeta* timer = getTimerById(id); if (timer == nullptr) { - int max = std::max({getOMPMaxThreads(), mDeviceProcessingSettings.nDeviceHelperThreads + 1, mDeviceProcessingSettings.nStreams}); + int max = std::max({getOMPMaxThreads(), mProcessingSettings.nDeviceHelperThreads + 1, mProcessingSettings.nStreams}); timer = insertTimer(id, name, J, max, 1, RecoStep::NoRecoStep); } if (num == -1) { diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx index 53a9da0231a83..8bbb555ad4afa 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx @@ -40,6 +40,7 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; +using namespace o2::tpc::constants; void GPUReconstructionConvert::ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr<GPUTPCClusterData[]>* clusters, unsigned int* nClusters, const TPCFastTransform* transform, int continuousMaxTimeBin) { @@ -160,7 +161,7 @@ int GPUReconstructionConvert::GetMaxTimeBin(const GPUTrackingInOutZS& zspages) for (unsigned int l = 0; l < zspages.slice[i].nZSPtr[j][k]; l++) { RAWDataHeaderGPU* rdh = (RAWDataHeaderGPU*)(page + l * TPCZSHDR::TPC_ZS_PAGE_SIZE); TPCZSHDR* hdr = (TPCZSHDR*)(page + l * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(RAWDataHeaderGPU)); - unsigned int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / Constants::LHCBCPERTIMEBIN + hdr->nTimeBins; + unsigned int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN + hdr->nTimeBins; if (timeBin > retVal) { retVal = timeBin; } @@ -273,7 +274,7 @@ void GPUReconstructionConvert::RunZSEncoder(const S& in, std::unique_ptr<unsigne if (ZSEncoderGetRow(a) != ZSEncoderGetRow(b)) { return ZSEncoderGetRow(a) <= ZSEncoderGetRow(b); } - return ZSEncoderGetPad(a) <= ZSEncoderGetPad(b); + return ZSEncoderGetPad(a) < ZSEncoderGetPad(b); }); int lastEndpoint = -1, lastRow = GPUCA_ROW_COUNT, lastTime = -1; long hbf = -1, nexthbf = 0; @@ -308,7 +309,7 @@ void GPUReconstructionConvert::RunZSEncoder(const S& in, std::unique_ptr<unsigne lastEndpoint = -1; } if (ZSEncoderGetTime(tmpBuffer[k]) != lastTime) { - nexthbf = ((long)ZSEncoderGetTime(tmpBuffer[k]) * Constants::LHCBCPERTIMEBIN + bcShiftInFirstHBF) / o2::constants::lhc::LHCMaxBunches; + nexthbf = ((long)ZSEncoderGetTime(tmpBuffer[k]) * LHCBCPERTIMEBIN + bcShiftInFirstHBF) / o2::constants::lhc::LHCMaxBunches; if (hbf != nexthbf) { lastEndpoint = -2; } @@ -389,7 +390,7 @@ void GPUReconstructionConvert::RunZSEncoder(const S& in, std::unique_ptr<unsigne hdr->cruID = i * 10 + region; rawcru = i * 10 + region; rawendpoint = endpoint & 1; - hdr->timeOffset = ZSEncoderGetTime(tmpBuffer[k]) * Constants::LHCBCPERTIMEBIN - (hbf - 0) * o2::constants::lhc::LHCMaxBunches; + hdr->timeOffset = ZSEncoderGetTime(tmpBuffer[k]) * LHCBCPERTIMEBIN - (hbf - 0) * o2::constants::lhc::LHCMaxBunches; lastTime = -1; tbHdr = nullptr; lastEndpoint = endpoint; @@ -474,7 +475,7 @@ void GPUReconstructionConvert::RunZSEncoder(const S& in, std::unique_ptr<unsigne } int nRowsRegion = param.tpcGeometry.GetRegionRows(region); - int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches) / Constants::LHCBCPERTIMEBIN; + int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; for (int l = 0; l < hdr->nTimeBins; l++) { if ((pagePtr - reinterpret_cast<unsigned char*>(page)) & 1) { pagePtr++; @@ -566,7 +567,7 @@ void GPUReconstructionConvert::RunZSEncoderCreateMeta(const unsigned long long i } } -void GPUReconstructionConvert::RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* buffers, const o2::tpc::Digit* const* ptrs, size_t* nsb, const size_t* ns, const GPUParam& param, bool zs12bit) +void GPUReconstructionConvert::RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* buffers, const o2::tpc::Digit* const* ptrs, size_t* nsb, const size_t* ns, const GPUParam& param, bool zs12bit, float threshold) { #ifdef HAVE_O2HEADERS for (unsigned int i = 0; i < NSLICES; i++) { @@ -577,7 +578,7 @@ void GPUReconstructionConvert::RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* bu const unsigned int decodeBits = zs12bit ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1; const float decodeBitsFactor = (1 << (decodeBits - 10)); for (unsigned int k = 0; k < ns[i]; k++) { - if (buffers[i][k].getChargeFloat() >= param.rec.tpcZSthreshold) { + if (buffers[i][k].getChargeFloat() >= threshold) { if (k > j) { buffers[i][j] = buffers[i][k]; } diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.h b/GPU/GPUTracking/Base/GPUReconstructionConvert.h index 81ad8c235fe3c..255b36e90dac4 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.h +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.h @@ -32,14 +32,14 @@ class RawFileWriter; } // namespace raw } // namespace o2 -class AliHLTTPCRawCluster; +struct AliHLTTPCRawCluster; namespace GPUCA_NAMESPACE { namespace gpu { -class GPUParam; -class GPUTPCClusterData; +struct GPUParam; +struct GPUTPCClusterData; class TPCFastTransform; struct GPUTrackingInOutDigits; struct GPUTrackingInOutZS; @@ -53,7 +53,7 @@ class GPUReconstructionConvert template <class T, class S> static void RunZSEncoder(const S& in, std::unique_ptr<unsigned long long int[]>* outBuffer, unsigned int* outSizes, o2::raw::RawFileWriter* raw, const o2::InteractionRecord* ir, const GPUParam& param, bool zs12bit, bool verify, float threshold = 0.f, bool padding = true); static void RunZSEncoderCreateMeta(const unsigned long long int* buffer, const unsigned int* sizes, void** ptrs, GPUTrackingInOutZS* out); - static void RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* buffers, const o2::tpc::Digit* const* ptrs, size_t* nsb, const size_t* ns, const GPUParam& param, bool zs12bit); + static void RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* buffers, const o2::tpc::Digit* const* ptrs, size_t* nsb, const size_t* ns, const GPUParam& param, bool zs12bit, float threshold); static int GetMaxTimeBin(const o2::tpc::ClusterNativeAccess& native); static int GetMaxTimeBin(const GPUTrackingInOutDigits& digits); static int GetMaxTimeBin(const GPUTrackingInOutZS& zspages); diff --git a/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.cxx b/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.cxx index 6b8bc57037e46..9b71edde5659c 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.cxx @@ -32,7 +32,7 @@ class GPUTPCRow; #define SemLockName "AliceHLTTPCGPUTrackerInitLockSem" -GPUReconstructionDeviceBase::GPUReconstructionDeviceBase(const GPUSettingsProcessing& cfg, size_t sizeCheck) : GPUReconstructionCPU(cfg) +GPUReconstructionDeviceBase::GPUReconstructionDeviceBase(const GPUSettingsDeviceBackend& cfg, size_t sizeCheck) : GPUReconstructionCPU(cfg) { if (sizeCheck != sizeof(GPUReconstructionDeviceBase)) { GPUFatal("Mismatch of C++ object size between GPU compilers!"); @@ -55,7 +55,7 @@ void* GPUReconstructionDeviceBase::helperWrapper_static(void* arg) void* GPUReconstructionDeviceBase::helperWrapper(GPUReconstructionHelpers::helperParam* par) { - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("\tHelper thread %d starting", par->num); } @@ -66,8 +66,8 @@ void* GPUReconstructionDeviceBase::helperWrapper(GPUReconstructionHelpers::helpe par->mutex[0].lock(); while (par->terminate == false) { - for (int i = par->num + 1; i < par->count; i += mDeviceProcessingSettings.nDeviceHelperThreads + 1) { - // if (mDeviceProcessingSettings.debugLevel >= 3) GPUInfo("\tHelper Thread %d Running, Slice %d+%d, Phase %d", par->num, i, par->phase); + for (int i = par->num + 1; i < par->count; i += mProcessingSettings.nDeviceHelperThreads + 1) { + // if (mProcessingSettings.debugLevel >= 3) GPUInfo("\tHelper Thread %d Running, Slice %d+%d, Phase %d", par->num, i, par->phase); if ((par->functionCls->*par->function)(i, par->num + 1, par)) { par->error = 1; } @@ -75,12 +75,12 @@ void* GPUReconstructionDeviceBase::helperWrapper(GPUReconstructionHelpers::helpe break; } par->done = i + 1; - // if (mDeviceProcessingSettings.debugLevel >= 3) GPUInfo("\tHelper Thread %d Finished, Slice %d+%d, Phase %d", par->num, i, par->phase); + // if (mProcessingSettings.debugLevel >= 3) GPUInfo("\tHelper Thread %d Finished, Slice %d+%d, Phase %d", par->num, i, par->phase); } ResetThisHelperThread(par); par->mutex[0].lock(); } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("\tHelper thread %d terminating", par->num); } par->mutex[1].unlock(); @@ -145,11 +145,11 @@ void GPUReconstructionDeviceBase::ReleaseGlobalLock(void* sem) void GPUReconstructionDeviceBase::ResetHelperThreads(int helpers) { - GPUImportant("Error occurred, GPU tracker helper threads will be reset (Number of threads %d (%d))", mDeviceProcessingSettings.nDeviceHelperThreads, mNSlaveThreads); + GPUImportant("Error occurred, GPU tracker helper threads will be reset (Number of threads %d (%d))", mProcessingSettings.nDeviceHelperThreads, mNSlaveThreads); SynchronizeGPU(); - for (int i = 0; i < mDeviceProcessingSettings.nDeviceHelperThreads; i++) { + for (int i = 0; i < mProcessingSettings.nDeviceHelperThreads; i++) { mHelperParams[i].reset = true; - if (helpers || i >= mDeviceProcessingSettings.nDeviceHelperThreads) { + if (helpers || i >= mProcessingSettings.nDeviceHelperThreads) { pthread_mutex_lock(&((pthread_mutex_t*)mHelperParams[i].mutex)[1]); } } @@ -158,7 +158,7 @@ void GPUReconstructionDeviceBase::ResetHelperThreads(int helpers) int GPUReconstructionDeviceBase::StartHelperThreads() { - int nThreads = mDeviceProcessingSettings.nDeviceHelperThreads; + int nThreads = mProcessingSettings.nDeviceHelperThreads; if (nThreads) { mHelperParams = new GPUReconstructionHelpers::helperParam[nThreads]; if (mHelperParams == nullptr) { @@ -206,14 +206,14 @@ int GPUReconstructionDeviceBase::StopHelperThreads() void GPUReconstructionDeviceBase::WaitForHelperThreads() { - for (int i = 0; i < mDeviceProcessingSettings.nDeviceHelperThreads; i++) { + for (int i = 0; i < mProcessingSettings.nDeviceHelperThreads; i++) { pthread_mutex_lock(&((pthread_mutex_t*)mHelperParams[i].mutex)[1]); } } void GPUReconstructionDeviceBase::RunHelperThreads(int (GPUReconstructionHelpers::helperDelegateBase::*function)(int i, int t, GPUReconstructionHelpers::helperParam* p), GPUReconstructionHelpers::helperDelegateBase* functionCls, int count) { - for (int i = 0; i < mDeviceProcessingSettings.nDeviceHelperThreads; i++) { + for (int i = 0; i < mProcessingSettings.nDeviceHelperThreads; i++) { mHelperParams[i].done = 0; mHelperParams[i].error = 0; mHelperParams[i].function = function; @@ -230,22 +230,22 @@ int GPUReconstructionDeviceBase::InitDevice() // CPU_SET(0, &mask); // sched_setaffinity(0, sizeof(mask), &mask); - if (mDeviceProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (mProcessingSettings.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { GPUError("Individual memory allocation strategy unsupported for device\n"); return (1); } - if (mDeviceProcessingSettings.nStreams > GPUCA_MAX_STREAMS) { - GPUError("Too many straems requested %d > %d\n", mDeviceProcessingSettings.nStreams, GPUCA_MAX_STREAMS); + if (mProcessingSettings.nStreams > GPUCA_MAX_STREAMS) { + GPUError("Too many straems requested %d > %d\n", mProcessingSettings.nStreams, GPUCA_MAX_STREAMS); return (1); } mThreadId = GetThread(); void* semLock = nullptr; - if (mDeviceProcessingSettings.globalInitMutex && GetGlobalLock(semLock)) { + if (mProcessingSettings.globalInitMutex && GetGlobalLock(semLock)) { return (1); } - if (mDeviceProcessingSettings.deviceTimers) { + if (mProcessingSettings.deviceTimers) { AddGPUEvents(mDebugEvents); } @@ -255,7 +255,7 @@ int GPUReconstructionDeviceBase::InitDevice() return (1); } - if (mDeviceProcessingSettings.globalInitMutex) { + if (mProcessingSettings.globalInitMutex) { ReleaseGlobalLock(semLock); } @@ -271,7 +271,7 @@ int GPUReconstructionDeviceBase::InitDevice() return (1); } - if (mMaster == nullptr || mDeviceProcessingSettings.debugLevel >= 2) { + if (mMaster == nullptr || mProcessingSettings.debugLevel >= 2) { GPUInfo("GPU Tracker initialization successfull"); // Verbosity reduced because GPU backend will print GPUImportant message! } diff --git a/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.h b/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.h index cb3a2bc1d95d2..7e81e7b59151a 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.h +++ b/GPU/GPUTracking/Base/GPUReconstructionDeviceBase.h @@ -35,7 +35,7 @@ class GPUReconstructionDeviceBase : public GPUReconstructionCPU const GPUParam* DeviceParam() const { return &mDeviceConstantMem->param; } protected: - GPUReconstructionDeviceBase(const GPUSettingsProcessing& cfg, size_t sizeCheck); + GPUReconstructionDeviceBase(const GPUSettingsDeviceBackend& cfg, size_t sizeCheck); int InitDevice() override; virtual int InitDevice_Runtime() = 0; diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludes.h b/GPU/GPUTracking/Base/GPUReconstructionIncludes.h index c7f409cb45453..04409ce27c7e0 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludes.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludes.h @@ -14,12 +14,6 @@ #ifndef GPURECONSTRUCTIONINCLUDES_H #define GPURECONSTRUCTIONINCLUDES_H -// Disable assertions since they produce errors in GPU Code -#ifdef assert -#undef assert -#endif -#define assert(param) - #ifndef WIN32 #include <sys/syscall.h> #include <semaphore.h> @@ -29,6 +23,7 @@ #include "GPUDef.h" #include "GPULogging.h" +#include "GPUDataTypes.h" #include <iostream> #include <fstream> @@ -43,24 +38,30 @@ #define RANDOM_ERROR //#define RANDOM_ERROR || rand() % 500 == 1 -#define GPUCA_GPUReconstructionUpdateDefailts() \ - if (mDeviceProcessingSettings.trackletConstructorInPipeline < 0) { \ - mDeviceProcessingSettings.trackletConstructorInPipeline = GPUCA_CONSTRUCTOR_IN_PIPELINE; \ - } \ - if (mDeviceProcessingSettings.trackletSelectorInPipeline < 0) { \ - mDeviceProcessingSettings.trackletSelectorInPipeline = GPUCA_SELECTOR_IN_PIPELINE; \ - } \ - if (mDeviceProcessingSettings.trackletSelectorSlices < 0) { \ - mDeviceProcessingSettings.trackletSelectorSlices = GPUCA_TRACKLET_SELECTOR_SLICE_COUNT; \ - } \ - if (mDeviceProcessingSettings.alternateBorderSort < 0) { \ - mDeviceProcessingSettings.alternateBorderSort = GPUCA_ALTERNATE_BORDER_SORT; \ - } \ - if (mDeviceProcessingSettings.mergerSortTracks < 0) { \ - mDeviceProcessingSettings.mergerSortTracks = GPUCA_SORT_BEFORE_FIT; \ - } \ - if (param().rec.loopInterpolationInExtraPass < 0) { \ - param().rec.loopInterpolationInExtraPass = GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION; \ +#define GPUCA_GPUReconstructionUpdateDefailts() \ + if (mProcessingSettings.trackletConstructorInPipeline < 0) { \ + mProcessingSettings.trackletConstructorInPipeline = GPUCA_CONSTRUCTOR_IN_PIPELINE; \ + } \ + if (mProcessingSettings.trackletSelectorInPipeline < 0) { \ + mProcessingSettings.trackletSelectorInPipeline = GPUCA_SELECTOR_IN_PIPELINE; \ + } \ + if (mProcessingSettings.trackletSelectorSlices < 0) { \ + mProcessingSettings.trackletSelectorSlices = GPUCA_TRACKLET_SELECTOR_SLICE_COUNT; \ + } \ + if (mProcessingSettings.alternateBorderSort < 0) { \ + mProcessingSettings.alternateBorderSort = GPUCA_ALTERNATE_BORDER_SORT; \ + } \ + if (mProcessingSettings.mergerSortTracks < 0) { \ + mProcessingSettings.mergerSortTracks = GPUCA_SORT_BEFORE_FIT; \ + } \ + if (param().rec.loopInterpolationInExtraPass < 0) { \ + param().rec.loopInterpolationInExtraPass = GPUCA_MERGER_SPLIT_LOOP_INTERPOLATION; \ + } \ + if (mProcessingSettings.tpcCompressionGatherModeKernel < 0) { \ + mProcessingSettings.tpcCompressionGatherModeKernel = GPUCA_COMP_GATHER_KERNEL; \ + } \ + if (mProcessingSettings.tpcCompressionGatherMode < 0) { \ + mProcessingSettings.tpcCompressionGatherMode = GPUCA_COMP_GATHER_MODE; \ } #endif diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludesDevice.h b/GPU/GPUTracking/Base/GPUReconstructionIncludesDevice.h index d8653412fa8a7..f05f120c1b18b 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludesDevice.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludesDevice.h @@ -28,7 +28,6 @@ using namespace GPUCA_NAMESPACE::gpu; #include "GPUTPCTrack.cxx" #include "GPUTPCGrid.cxx" #include "GPUTPCRow.cxx" -#include "GPUParam.cxx" #include "GPUTPCTracker.cxx" #include "GPUGeneralKernels.cxx" @@ -59,6 +58,12 @@ using namespace GPUCA_NAMESPACE::gpu; #include "MatLayerCyl.cxx" #include "Ray.cxx" +// O2 track model +#include "TrackParametrization.cxx" +#include "TrackParametrizationWithError.cxx" +#include "Propagator.cxx" +#include "TrackLTIntegral.cxx" + // Files for GPU dEdx #include "GPUdEdx.cxx" @@ -78,6 +83,7 @@ using namespace GPUCA_NAMESPACE::gpu; #include "GPUTPCCFClusterizer.cxx" #include "GPUTPCCFDeconvolution.cxx" #include "GPUTPCCFMCLabelFlattener.cxx" +#include "GPUTPCCFCheckPadBaseline.cxx" #include "GPUTPCCFDecodeZS.cxx" #include "GPUTPCCFGather.cxx" @@ -86,18 +92,22 @@ using namespace GPUCA_NAMESPACE::gpu; #include "GPUTRDTrack.cxx" #include "GPUTRDTracker.cxx" #include "GPUTRDTrackletWord.cxx" -#include "TRDGeometryBase.cxx" +#include "GeometryBase.cxx" // Files for ITS Track Fit #include "GPUITSFitterKernels.cxx" -#if !defined(GPUCA_O2_LIB) && defined(__HIPCC__) && !defined(GPUCA_NO_ITS_TRAITS) +// Files for Refit +#include "GPUTrackingRefit.cxx" +#include "GPUTrackingRefitKernel.cxx" + +#if !defined(GPUCA_O2_LIB) && defined(__HIPCC__) && !defined(GPUCA_NO_ITS_TRAITS) && !defined(GPUCA_GPUCODE_GENRTC) #include "VertexerTraitsHIP.hip.cxx" #include "ContextHIP.hip.cxx" #include "DeviceStoreVertexerHIP.hip.cxx" #include "ClusterLinesHIP.hip.cxx" #include "UtilsHIP.hip.cxx" -#elif !defined(GPUCA_O2_LIB) && defined(__CUDACC__) && !defined(GPUCA_NO_ITS_TRAITS) +#elif !defined(GPUCA_O2_LIB) && defined(__CUDACC__) && !defined(GPUCA_NO_ITS_TRAITS) && !defined(GPUCA_GPUCODE_GENRTC) #include "TrackerTraitsNV.cu" #include "VertexerTraitsGPU.cu" #include "Context.cu" diff --git a/GPU/GPUTracking/Base/GPUReconstructionKernels.h b/GPU/GPUTracking/Base/GPUReconstructionKernels.h index adb6b460463cb..c300714ccf3a0 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionKernels.h +++ b/GPU/GPUTracking/Base/GPUReconstructionKernels.h @@ -44,7 +44,7 @@ GPUCA_KRNL_LB((GPUTPCGMMergerMergeSlicesPrepare ), (simple), (, int GPUCA_KRNL_LB((GPUTPCGMMergerMergeBorders, step0 ), (simple), (, int iSlice, char withinSlice, char mergeMode), (, iSlice, withinSlice, mergeMode)) GPUCA_KRNL(( GPUTPCGMMergerMergeBorders, step1 ), (simple), (, int iSlice, char withinSlice, char mergeMode), (, iSlice, withinSlice, mergeMode)) GPUCA_KRNL_LB((GPUTPCGMMergerMergeBorders, step2 ), (simple), (, int iSlice, char withinSlice, char mergeMode), (, iSlice, withinSlice, mergeMode)) -GPUCA_KRNL(( GPUTPCGMMergerMergeBorders, variant ), (simple), (, GPUPtr1(GPUTPCGMMergerTypes::GPUTPCGMBorderRange*, range), int N, int cmpMax), (, GPUPtr2(GPUTPCGMMergerTypes::GPUTPCGMBorderRange*, range), N, cmpMax)) +GPUCA_KRNL(( GPUTPCGMMergerMergeBorders, variant ), (simple), (, GPUPtr1(gputpcgmmergertypes::GPUTPCGMBorderRange*, range), int N, int cmpMax), (, GPUPtr2(gputpcgmmergertypes::GPUTPCGMBorderRange*, range), N, cmpMax)) GPUCA_KRNL_LB((GPUTPCGMMergerMergeCE ), (simple), (), ()) GPUCA_KRNL_LB((GPUTPCGMMergerLinkGlobalTracks ), (simple), (), ()) GPUCA_KRNL_LB((GPUTPCGMMergerCollect ), (simple), (), ()) @@ -57,30 +57,38 @@ GPUCA_KRNL_LB((GPUTPCGMMergerPrepareClusters, step2 ), (simple), (), ()) GPUCA_KRNL_LB((GPUTPCGMMergerFinalize, step0 ), (simple), (), ()) GPUCA_KRNL_LB((GPUTPCGMMergerFinalize, step1 ), (simple), (), ()) GPUCA_KRNL_LB((GPUTPCGMMergerFinalize, step2 ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCGMMergerMergeLoopers ), (simple), (), ()) #ifdef HAVE_O2HEADERS GPUCA_KRNL_LB((GPUTRDTrackerKernels ), (simple), (), ()) GPUCA_KRNL_LB((GPUITSFitterKernel ), (simple), (), ()) GPUCA_KRNL_LB((GPUTPCConvertKernel ), (simple), (), ()) -GPUCA_KRNL_LB((GPUTPCCompressionKernels, step0attached ), (simple), (), ()) -GPUCA_KRNL_LB((GPUTPCCompressionKernels, step1unattached ), (simple), (), ()) -GPUCA_KRNL_LB((GPUTPCCompressionKernels, step2gather ), (simple), (), ()) -GPUCA_KRNL_LB((GPUTPCCFChargeMapFiller, fillIndexMap ), (single), (), ()) -GPUCA_KRNL_LB((GPUTPCCFChargeMapFiller, fillFromDigits ), (single), (), ()) -GPUCA_KRNL_LB((GPUTPCCFChargeMapFiller, findFragmentStart), (single), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionKernels, step0attached ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionKernels, step1unattached ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionGatherKernels, unbuffered ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionGatherKernels, buffered32 ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionGatherKernels, buffered64 ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionGatherKernels, buffered128 ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCompressionGatherKernels, multiBlock ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTPCCFCheckPadBaseline ), (single), (), ()) +GPUCA_KRNL_LB((GPUTPCCFChargeMapFiller, fillIndexMap ), (single), (), ()) +GPUCA_KRNL_LB((GPUTPCCFChargeMapFiller, fillFromDigits ), (single), (), ()) +GPUCA_KRNL_LB((GPUTPCCFChargeMapFiller, findFragmentStart ), (single), (, char setPositions), (, setPositions)) GPUCA_KRNL_LB((GPUTPCCFPeakFinder ), (single), (), ()) -GPUCA_KRNL_LB((GPUTPCCFNoiseSuppression, noiseSuppression ), (single), (), ()) -GPUCA_KRNL_LB((GPUTPCCFNoiseSuppression, updatePeaks ), (single), (), ()) +GPUCA_KRNL_LB((GPUTPCCFNoiseSuppression, noiseSuppression ), (single), (), ()) +GPUCA_KRNL_LB((GPUTPCCFNoiseSuppression, updatePeaks ), (single), (), ()) GPUCA_KRNL_LB((GPUTPCCFDeconvolution ), (single), (), ()) -GPUCA_KRNL_LB((GPUTPCCFClusterizer ), (single), (), ()) -GPUCA_KRNL(( GPUTPCCFMCLabelFlattener, setRowOffsets ), (single), (), ()) -GPUCA_KRNL(( GPUTPCCFMCLabelFlattener, flatten ), (single), (, unsigned int row, GPUPtr1(GPUTPCLinearLabels*, out)), (, row, GPUPtr2(GPUTPCLinearLabels*, out))) -GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanStart ), (single), (, int iBuf, int stage), (, iBuf, stage)) -GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanUp ), (single), (, int iBuf, int nElems), (, iBuf, nElems)) -GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanTop ), (single), (, int iBuf, int nElems), (, iBuf, nElems)) -GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanDown ), (single), (, int iBuf, unsigned int offset, int nElems), (, iBuf, offset, nElems)) -GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, compactDigits ), (single), (, int iBuf, int stage, GPUPtr1(ChargePos*, in), GPUPtr1(ChargePos*, out)), (, iBuf, stage, GPUPtr2(ChargePos*, in), GPUPtr2(ChargePos*, out))) +GPUCA_KRNL_LB((GPUTPCCFClusterizer ), (single), (, char onlyMC), (, onlyMC)) +GPUCA_KRNL(( GPUTPCCFMCLabelFlattener, setRowOffsets ), (single), (), ()) +GPUCA_KRNL(( GPUTPCCFMCLabelFlattener, flatten ), (single), (, GPUPtr1(GPUTPCLinearLabels*, out)), (, GPUPtr2(GPUTPCLinearLabels*, out))) +GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanStart ), (single), (, int iBuf, int stage), (, iBuf, stage)) +GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanUp ), (single), (, int iBuf, int nElems), (, iBuf, nElems)) +GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanTop ), (single), (, int iBuf, int nElems), (, iBuf, nElems)) +GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, scanDown ), (single), (, int iBuf, unsigned int offset, int nElems), (, iBuf, offset, nElems)) +GPUCA_KRNL_LB((GPUTPCCFStreamCompaction, compactDigits ), (single), (, int iBuf, int stage, GPUPtr1(ChargePos*, in), GPUPtr1(ChargePos*, out)), (, iBuf, stage, GPUPtr2(ChargePos*, in), GPUPtr2(ChargePos*, out))) GPUCA_KRNL_LB((GPUTPCCFDecodeZS ), (single), (, int firstHBF), (, firstHBF)) GPUCA_KRNL_LB((GPUTPCCFGather ), (single), (, GPUPtr1(o2::tpc::ClusterNative*, dest)), (, GPUPtr2(o2::tpc::ClusterNative*, dest))) +GPUCA_KRNL_LB((GPUTrackingRefitKernel, mode0asGPU ), (simple), (), ()) +GPUCA_KRNL_LB((GPUTrackingRefitKernel, mode1asTrackParCov ), (simple), (), ()) #endif #endif // clang-format on diff --git a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx index bda4478a41bbd..a7cd332069943 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx @@ -31,7 +31,11 @@ using namespace GPUCA_NAMESPACE::gpu; -static auto& config = configStandalone.configTF; +namespace GPUCA_NAMESPACE::gpu +{ +extern GPUSettingsStandalone configStandalone; +} +static auto& config = configStandalone.TF; GPUReconstructionTimeframe::GPUReconstructionTimeframe(GPUChainTracking* chain, int (*read)(int), int nEvents) : mChain(chain), mReadEvent(read), mNEventsInDirectory(nEvents), mDisUniReal(0., 1.), mRndGen1(configStandalone.seed), mRndGen2(mDisUniInt(mRndGen1)) { @@ -214,7 +218,7 @@ void GPUReconstructionTimeframe::MergeShiftedEvents() int GPUReconstructionTimeframe::LoadCreateTimeFrame(int iEvent) { - if (config.nTotalInTFEvents && mNTotalCollisions >= config.nTotalInTFEvents) { + if (config.nTotalEventsInTF && mNTotalCollisions >= config.nTotalEventsInTF) { return (2); } @@ -229,7 +233,7 @@ int GPUReconstructionTimeframe::LoadCreateTimeFrame(int iEvent) for (int iTrain = 0; iTrain < config.bunchTrainCount && nBunch < lastBunch; iTrain++) { int nCollisionsInTrain = 0; for (int iBunch = 0; iBunch < config.bunchCount && nBunch < lastBunch; iBunch++) { - const bool inTF = nBunch >= 0 && nBunch < lastTFBunch && (config.nTotalInTFEvents == 0 || nCollisions < mNTotalCollisions + config.nTotalInTFEvents); + const bool inTF = nBunch >= 0 && nBunch < lastTFBunch && (config.nTotalEventsInTF == 0 || nCollisions < mNTotalCollisions + config.nTotalEventsInTF); if (mcMin == -1 && inTF) { mcMin = mChain->mIOPtrs.nMCInfosTPC; } diff --git a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h index 20996b2de1f87..c0f21f7b94f23 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h +++ b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.h @@ -20,13 +20,10 @@ #include <random> #include <tuple> -namespace o2 -{ -namespace tpc +namespace o2::tpc { struct ClusterNative; -} -} // namespace o2 +} // namespace o2::tpc namespace GPUCA_NAMESPACE { diff --git a/GPU/GPUTracking/Base/GPUSettings.cxx b/GPU/GPUTracking/Base/GPUSettings.cxx index 529cd9b008ed0..f7156a1fad96d 100644 --- a/GPU/GPUTracking/Base/GPUSettings.cxx +++ b/GPU/GPUTracking/Base/GPUSettings.cxx @@ -18,46 +18,14 @@ using namespace GPUCA_NAMESPACE::gpu; -void GPUSettingsRec::SetDefaults() +GPUSettingsDeviceBackend::GPUSettingsDeviceBackend() { - HitPickUpFactor = 2.; - NeighboursSearchArea = 3.f; - ClusterError2CorrectionY = 1.f; - ClusterError2CorrectionZ = 1.f; - MinNTrackClusters = -1; - MaxTrackQPt = 1.f / GPUCA_MIN_TRACK_PT_DEFAULT; - NWays = 3; - NWaysOuter = false; - RejectMode = 5; - GlobalTracking = true; - SearchWindowDZDR = 0.f; - TrackReferenceX = 1000.f; - NonConsecutiveIDs = false; - ForceEarlyTPCTransform = -1; - DisableRefitAttachment = 0; - dEdxTruncLow = 2; - dEdxTruncHigh = 77; - tpcRejectionMode = GPUSettings::RejectionStrategyA; - tpcRejectQPt = 1.f / 0.05f; - tpcCompressionModes = GPUSettings::CompressionFull; - tpcCompressionSortOrder = GPUSettings::SortPad; - tpcSigBitsCharge = 4; - tpcSigBitsWidth = 3; - tpcZSthreshold = 2; - fwdTPCDigitsAsClusters = 0; - bz0Pt = 60; - dropLoopers = false; - mergerCovSource = 2; - mergerInterpolateErrors = 1; - fitInProjections = -1; - fitPropagateBzOnly = -1; - retryRefit = 1; - loopInterpolationInExtraPass = -1; - mergerReadFromTrackerDirectly = 1; - useMatLUT = false; + deviceType = GPUDataTypes::DeviceType::CPU; + forceDeviceType = true; + master = nullptr; } -void GPUSettingsEvent::SetDefaults() +GPUSettingsEvent::GPUSettingsEvent() { solenoidBz = -5.00668; constBz = 0; @@ -65,53 +33,3 @@ void GPUSettingsEvent::SetDefaults() continuousMaxTimeBin = 0; needsClusterer = 0; } - -void GPUSettingsProcessing::SetDefaults() -{ - deviceType = GPUDataTypes::DeviceType::CPU; - forceDeviceType = true; - master = nullptr; -} - -void GPUSettingsDeviceProcessing::SetDefaults() -{ - nThreads = 1; - ompKernels = false; - deviceNum = -1; - platformNum = -1; - globalInitMutex = false; - gpuDeviceOnly = false; - nDeviceHelperThreads = 2; - debugLevel = -1; - allocDebugLevel = 0; - deviceTimers = true; - debugMask = -1; - comparableDebutOutput = true; - resetTimers = 1; - eventDisplay = nullptr; - runQA = false; - runCompressionStatistics = false; - stuckProtection = 0; - memoryAllocationStrategy = 0; - keepAllMemory = false; - keepDisplayMemory = false; - nStreams = 8; - trackletConstructorInPipeline = -1; - trackletSelectorInPipeline = -1; - trackletSelectorSlices = -1; - forceMemoryPoolSize = 0; - forceHostMemoryPoolSize = 0; - nTPCClustererLanes = 3; - registerStandaloneInputMemory = false; - tpcCompressionGatherMode = -1; - mergerSortTracks = -1; - runMC = false; - memoryScalingFactor = 1.f; - disableMemoryReuse = false; - fullMergerOnGPU = true; - alternateBorderSort = -1; - delayedOutput = true; - tpccfGatherKernel = true; - prefetchTPCpageScan = false; - doublePipeline = false; -} diff --git a/GPU/GPUTracking/Base/GPUSettings.h b/GPU/GPUTracking/Base/GPUSettings.h index e34d721e3cfb0..5b7bda9d647ca 100644 --- a/GPU/GPUTracking/Base/GPUSettings.h +++ b/GPU/GPUTracking/Base/GPUSettings.h @@ -14,7 +14,11 @@ #ifndef GPUSETTINGS_H #define GPUSETTINGS_H -#include "GPUCommonMath.h" +#include "GPUCommonDef.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include <vector> +#include <string> +#endif namespace GPUCA_NAMESPACE { @@ -44,66 +48,10 @@ class GPUSettings #endif }; -// Settings concerning the reconstruction -struct GPUSettingsRec { -#ifndef GPUCA_GPUCODE - GPUSettingsRec() - { - SetDefaults(); - } - void SetDefaults(); - void SetMinTrackPt(float v) { MaxTrackQPt = CAMath::Abs(v) > 0.001 ? 1. / CAMath::Abs(v) : 1. / 0.001; } -#endif - - // There must be no bool in here, use char, as sizeof(bool) is compiler dependent and fails on GPUs!!!!!! - float HitPickUpFactor; // multiplier for the chi2 window for hit pick up procedure - float NeighboursSearchArea; // area in cm for the search of neighbours - float ClusterError2CorrectionY; // correction for the squared cluster error during tracking - float ClusterError2CorrectionZ; // correction for the squared cluster error during tracking - int MinNTrackClusters; //* required min number of clusters on the track - float MaxTrackQPt; //* required max Q/Pt (==min Pt) of tracks - char NWays; // Do N fit passes in final fit of merger - char NWaysOuter; // Store outer param - char RejectMode; // 0: no limit on rejection or missed hits, >0: break after n rejected hits, <0: reject at max -n hits - char GlobalTracking; // Enable Global Tracking (prolong tracks to adjacent sectors to find short segments) - float SearchWindowDZDR; // Use DZDR window for seeding instead of vertex window - float TrackReferenceX; // Transport all tracks to this X after tracking (disabled if > 500) - char NonConsecutiveIDs; // Non-consecutive cluster IDs as in HLT, disables features that need access to slice data in TPC merger - char ForceEarlyTPCTransform; // Force early TPC transformation also for continuous data (-1 = auto) - unsigned char DisableRefitAttachment; // Bitmask to disable cluster attachment steps in refit: 1: attachment, 2: propagation, 4: loop following, 8: mirroring - unsigned char dEdxTruncLow; // Low truncation threshold, fraction of 128 - unsigned char dEdxTruncHigh; // High truncation threshold, fraction of 128 - unsigned char tpcRejectionMode; // 0: do not reject clusters, 1: do reject identified junk, 2: reject everything but good tracks - float tpcRejectQPt; // Reject tracks below this Pt - unsigned char tpcCompressionModes; // Enabled steps of TPC compression as flags: 1=truncate charge/width LSB, 2=differences, 4=track-model - unsigned char tpcCompressionSortOrder; // Sort order for clusters storred as differences (0 = time, 1 = pad, 2 = Z-curve-time-pad, 3 = Z-curve-pad-time) - unsigned char tpcSigBitsCharge; // Number of significant bits for TPC cluster charge in compression mode 1 - unsigned char tpcSigBitsWidth; // Number of significant bits for TPC cluster width in compression mode 1 - unsigned char tpcZSthreshold; // TPC Zero Suppression Threshold (for loading digits / forwarging digits as clusters) - unsigned char fwdTPCDigitsAsClusters; // Simply forward TPC digits as clusters - unsigned char bz0Pt; // Nominal Pt to set when bz = 0 (in 10 MeV) - unsigned char dropLoopers; // Drop all clusters after starting from the second loop from tracks - unsigned char mergerCovSource; // 0 = simpleFilterErrors, 1 = use from track following - unsigned char mergerInterpolateErrors; // Use interpolation for cluster rejection based on chi-2 instead of extrapolation - char fitInProjections; // -1 for automatic - char fitPropagateBzOnly; // Use only Bz for the propagation during the fit in the first n passes, -1 = NWays -1 - char retryRefit; // Retry refit with larger cluster errors when fit fails - char loopInterpolationInExtraPass; // Perform the loop interpolation in an extra pass - char mergerReadFromTrackerDirectly; // Make the TPC merger read the output directly from the tracker class - char useMatLUT; // Use material lookup table for TPC refit -}; - // Settings describing the events / time frames struct GPUSettingsEvent { -#ifndef GPUCA_GPUCODE - GPUSettingsEvent() - { - SetDefaults(); - } - void SetDefaults(); -#endif - - // All new members must be sizeof(int)/sizeof(float) for alignment reasons! + // All new members must be sizeof(int) resp. sizeof(float) for alignment reasons! + GPUSettingsEvent(); float solenoidBz; // solenoid field strength int constBz; // for test-MC events with constant Bz int homemadeEvents; // Toy-MC events @@ -112,71 +60,22 @@ struct GPUSettingsEvent { }; // Settings defining the setup of the GPUReconstruction processing (basically selecting the device / class instance) -struct GPUSettingsProcessing { -#ifndef GPUCA_GPUCODE - GPUSettingsProcessing() - { - SetDefaults(); - } - void SetDefaults(); -#endif - +struct GPUSettingsDeviceBackend { + GPUSettingsDeviceBackend(); unsigned int deviceType; // Device type, shall use GPUDataTypes::DEVICE_TYPE constants, e.g. CPU / CUDA char forceDeviceType; // Fail if device initialization fails, otherwise falls back to CPU GPUReconstruction* master; // GPUReconstruction master object }; -// Settings steering the processing once the device was selected -struct GPUSettingsDeviceProcessing { -#ifndef GPUCA_GPUCODE - GPUSettingsDeviceProcessing() - { - SetDefaults(); - } - void SetDefaults(); -#endif - - int nThreads; // Numnber of threads on CPU, 0 = auto-detect - bool ompKernels; // OMP Parallelization inside kernels - int deviceNum; // Device number to use, in case the backend provides multiple devices (-1 = auto-select) - int platformNum; // Platform to use, in case the backend provides multiple platforms (-1 = auto-select) - bool globalInitMutex; // Global mutex to synchronize initialization over multiple instances - bool gpuDeviceOnly; // Use only GPU as device (i.e. no CPU for OpenCL) - int nDeviceHelperThreads; // Additional CPU helper-threads for CPU parts of processing with accelerator - int debugLevel; // Level of debug output (-1 = silent) - int allocDebugLevel; // Some debug output for memory allocations (without messing with normal debug level) - int debugMask; // Mask for debug output dumps to file - bool comparableDebutOutput; // Make CPU and GPU debug output comparable (sort / skip concurrent parts) - int resetTimers; // Reset timers every event - GPUDisplayBackend* eventDisplay; // Run event display after processing, ptr to backend - bool runQA; // Run QA after processing - bool runCompressionStatistics; // Run statistics and verification for cluster compression - int stuckProtection; // Timeout in us, When AMD GPU is stuck, just continue processing and skip tracking, do not crash or stall the chain - int memoryAllocationStrategy; // 0 = auto, 1 = new/delete per resource (default for CPU), 2 = big chunk single allocation (default for device) - bool keepAllMemory; // Allocate all memory on both device and host, and do not reuse - bool keepDisplayMemory; // Like keepAllMemory, but only for memory required for event display - int nStreams; // Number of parallel GPU streams - char trackletConstructorInPipeline; // Run tracklet constructor in pileline like the preceeding tasks instead of as one big block - char trackletSelectorInPipeline; // Run tracklet selector in pipeline, requres also tracklet constructor in pipeline - char trackletSelectorSlices; // Number of slices to processes in parallel at max - size_t forceMemoryPoolSize; // Override size of memory pool to be allocated on GPU / Host (set =1 to force allocating all device memory, if supported) - size_t forceHostMemoryPoolSize; // Override size of host memory pool, overriding forceMemoryPoolSize on the host - int nTPCClustererLanes; // Number of TPC clusterers that can run in parallel - bool deviceTimers; // Use device timers instead of host-based timers - bool registerStandaloneInputMemory; // Automatically register memory for the GPU which is used as input for the standalone benchmark - int tpcCompressionGatherMode; // Modes: 0 = gather by DMA, 1 = DMA + gather on host, 2 = gather by kernel - char mergerSortTracks; // Sort track indices for GPU track fit - bool runMC; // Process MC labels - float memoryScalingFactor; // Factor to apply to all memory scalers - bool disableMemoryReuse; // Disable memory reusage (for debugging only) - bool fullMergerOnGPU; // Perform full TPC track merging on GPU instead of only refit - char alternateBorderSort; // Alternative scheduling for sorting of border tracks - bool delayedOutput; // Delay output to be parallel to track fit - bool tpccfGatherKernel; // Use a kernel instead of the DMA engine to gather the clusters - char prefetchTPCpageScan; // Prefetch headers during TPC page scan - bool doublePipeline; // Use a double-pipeline driven by 2 threads -}; } // namespace gpu } // namespace GPUCA_NAMESPACE +#ifdef GPUCA_GPUCODE_DEVICE +#define QCONFIG_GPU +#endif +// See GPUSettingsList.h for a list of all available settings of GPU Reconstruction +#ifndef GPUCA_GPUCODE_GENRTC +#include "utils/qconfig.h" +#endif + #endif diff --git a/GPU/GPUTracking/Base/GPUSettingsList.h b/GPU/GPUTracking/Base/GPUSettingsList.h new file mode 100644 index 0000000000000..3d43ec7118887 --- /dev/null +++ b/GPU/GPUTracking/Base/GPUSettingsList.h @@ -0,0 +1,352 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUSettingsList.h +/// \author David Rohr + +// This file contains macros to generate all settings for the GPU Reconstruction. +// Macros are used in the following places: +// Create ConfigurableParam object for workflow. +// Configure standalone benchmark. +// Create plain-C struct for GPU code. +// Create static constexpr with default values for GPU run time compilation + +#include "GPUDefConstantsAndSettings.h" +#ifndef GPUSETTINGS_H +#error Please include GPUSettings.h! +#endif + +// clang-format off + +#ifdef QCONFIG_INSTANCE +using namespace GPUCA_NAMESPACE::gpu; +#endif +#ifdef BeginNamespace // File should not be included without defining the macros, but rootcling will do for dictionary generation +BeginNamespace(GPUCA_NAMESPACE) +BeginNamespace(gpu) + +// Settings concerning the reconstruction +// There must be no bool in here, use char, as sizeof(bool) is compiler dependent and fails on GPUs!!!!!! +BeginSubConfig(GPUSettingsRec, rec, configStandalone, "REC", 0, "Reconstruction settings") +AddOptionRTC(tpcRejectQPt, float, 1.f / 0.05f, "", 0, "QPt threshold to reject clusters of TPC tracks (Inverse Pt!!!)") +AddOptionRTC(HitPickUpFactor, float, 2., "", 0, "multiplier for the chi2 window for hit pick up procedure") +AddOptionRTC(NeighboursSearchArea, float, 3., "", 0, "area in cm for the search of neighbours") +AddOptionRTC(ClusterError2CorrectionY, float, 1., "", 0, "correction for the squared cluster error during tracking") +AddOptionRTC(ClusterError2CorrectionZ, float, 1., "", 0, "correction for the squared cluster error during tracking") +AddOptionRTC(MinNTrackClusters, int, -1, "", 0, "required min number of clusters on the track") +AddOptionRTC(MaxTrackQPt, float, 1.f / GPUCA_MIN_TRACK_PT_DEFAULT, "", 0, "required max Q/Pt (==min Pt) of tracks") +AddOptionRTC(SearchWindowDZDR, float, 2.5, "", 0, "Use DZDR window for seeding instead of vertex window") +AddOptionRTC(TrackReferenceX, float, 1000.f, "", 0, "Transport all tracks to this X after tracking (disabled if > 500, auto = 1000)") +AddOptionRTC(tpcZSthreshold, float, 2.0f, "", 0, "Zero-Suppression threshold") +AddOptionRTC(tpcTubeChi2, float, 5.f * 5.f, "", 0, "Max chi2 to mark cluster adjacent to track") +AddOptionRTC(tpcTubeMaxSize2, float, 2.5f * 2.5f, "", 0, "Square of max tube size (normally derrived from tpcTubeChi2)") +AddOptionRTC(trdMinTrackPt, float, .5f, "", 0, "Min Pt for tracks to be propagated through the TRD") +AddOptionRTC(trdMaxChi2, float, 15.f, "", 0, "Max chi2 for TRD tracklets to be matched to a track") +AddOptionRTC(trdPenaltyChi2, float, 12.f, "", 0, "Chi2 penalty for no available TRD tracklet (effective chi2 cut value)") +AddOptionRTC(noisyPadsQuickCheck, unsigned char, 1, "", 0, "Only check first fragment for noisy pads instead of all fragments (when test is enabled).") +AddOptionRTC(maxTimeBinAboveThresholdIn1000Bin, unsigned short, 500, "", 0, "Except pad from cluster finding if total number of charges in a fragment is above this baseline (disable = 0)") +AddOptionRTC(maxConsecTimeBinAboveThreshold, unsigned short, 200, "", 0, "Except pad from cluster finding if number of consecutive charges in a fragment is above this baseline (disable = 0)") +AddOptionRTC(tpcCFqmaxCutoff, unsigned char, 3, "", 0, "Cluster Finder rejects cluster with qmax below this threshold") +AddOptionRTC(tpcCFqtotCutoff, unsigned char, 0, "", 0, "Cluster Finder rejects cluster with qtot below this threshold") +AddOptionRTC(tpcCFinnerThreshold, unsigned char, 0, "", 0, "Cluster Finder extends cluster if inner charge above this threshold") +AddOptionRTC(tpcCFminSplitNum, unsigned char, 1, "", 0, "Minimum number of split charges in a cluster for the cluster to be marked as split") +AddOptionRTC(tpcCFnoiseSuppressionEpsilon, unsigned char, 10, "", 0, "Cluster Finder: Difference between peak and charge for the charge to count as a minima during noise suppression") +AddOptionRTC(NWays, char, 3, "", 0, "Do N fit passes in final fit of merger") +AddOptionRTC(NWaysOuter, char, 0, "", 0, "Store outer param") +AddOptionRTC(RejectMode, char, 5, "", 0, "0: no limit on rejection or missed hits, >0: break after n rejected hits, <0: reject at max -n hits") +AddOptionRTC(NonConsecutiveIDs, char, false, "", 0, "Non-consecutive cluster IDs as in HLT, disables features that need access to slice data in TPC merger") +AddOptionRTC(dEdxTruncLow, unsigned char, 2, "", 0, "Low truncation threshold, fraction of 128") +AddOptionRTC(dEdxTruncHigh, unsigned char, 77, "", 0, "High truncation threshold, fraction of 128") +AddOptionRTC(GlobalTracking, char, 1, "", 0, "Enable Global Tracking (prolong tracks to adjacent sectors to find short segments)") +AddOptionRTC(DisableRefitAttachment, unsigned char, 0, "", 0, "Bitmask to disable certain attachment steps during refit (1: attachment, 2: propagation, 4: loop following, 8: mirroring)") +AddOptionRTC(tpcRejectionMode, unsigned char, GPUCA_NAMESPACE::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression (0 = no, 1 = strategy A, 2 = strategy B)") +AddOptionRTC(tpcMergeLoopersAfterburner, unsigned char, 0, "", 0, "Run afterburner for additional looper merging") +AddOptionRTC(tpcCompressionModes, unsigned char, GPUCA_NAMESPACE::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits (1=truncate charge/width LSB, 2=differences, 4=track-model)") +AddOptionRTC(tpcCompressionSortOrder, unsigned char, GPUCA_NAMESPACE::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression (0 = time, 1 = pad, 2 = Z-time-pad, 3 = Z-pad-time, 4 = no sorting (use incoming order))") +AddOptionRTC(tpcSigBitsCharge, unsigned char, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") +AddOptionRTC(tpcSigBitsWidth, unsigned char, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") +AddOptionRTC(ForceEarlyTPCTransform, char, -1, "", 0, "Force early TPC transformation also for continuous data (-1 = auto)") +AddOptionRTC(fwdTPCDigitsAsClusters, unsigned char, 0, "", 0, "Forward TPC digits as clusters (if they pass the ZS threshold)") +AddOptionRTC(bz0Pt, unsigned char, 60, "", 0, "Nominal Pt to set when bz = 0 (in 10 MeV)") +AddOptionRTC(dropLoopers, unsigned char, 0, "", 0, "Drop looping tracks starting from second loop") +AddOptionRTC(mergerCovSource, unsigned char, 2, "", 0, "Method to obtain covariance in track merger: 0 = simple filterErrors method, 1 = use cov from track following, 2 = refit") +AddOptionRTC(mergerInterpolateErrors, unsigned char, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") +AddOptionRTC(fitInProjections, char, -1, "", 0, "Fit in projection, -1 to enable for all but passes but the first one") +AddOptionRTC(fitPropagateBzOnly, char, -1, "", 0, "Propagate using Bz only for n passes") +AddOptionRTC(retryRefit, char, 1, "", 0, "Retry refit when fit fails") +AddOptionRTC(loopInterpolationInExtraPass, char, -1, "", 0, "Perform loop interpolation in an extra pass") +AddOptionRTC(mergerReadFromTrackerDirectly, char, 1, "", 0, "Forward data directly from tracker to merger on GPU") +AddOptionRTC(useMatLUT, char, 0, "", 0, "Use material lookup table for TPC refit") +AddOptionRTC(trdStopTrkAfterNMissLy, unsigned char, 6, "", 0, "Abandon track following after N layers without a TRD match") +AddOptionRTC(trackingRefitGPUModel, char, 1, "", 0, "Use GPU track model for the Global Track Refit") +AddCustomCPP(void SetMinTrackPt(float v) { MaxTrackQPt = v > 0.001 ? (1. / v) : (1. / 0.001); }) +AddVariable(dummyRTC, void*, nullptr) // Ensure non empty struct and proper alignment even if all normal members are constexpr +AddHelp("help", 'h') +EndConfig() + +// Settings steering the processing once the device was selected +BeginSubConfig(GPUSettingsProcessing, proc, configStandalone, "PROC", 0, "Processing settings") +AddOption(platformNum, int, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (-1 = auto-select)") +AddOption(trdNCandidates, int, 1, "", 0, "Number of branching track candidates for single input track during propagation") +AddOption(trdNMaxCollisions, int, 1000, "", 0, "Maximum number of collisions per TF which the TRD tracker can handle") +AddOption(gpuDeviceOnly, bool, false, "", 0, "Use only GPU as device (i.e. no CPU for OpenCL)") +AddOption(debugMask, int, -1, "", 0, "Mask for debug output dumps to file") +AddOption(comparableDebutOutput, bool, true, "", 0, "Make CPU and GPU debug output comparable (sort / skip concurrent parts)") +AddOption(resetTimers, int, 1, "", 0, "Reset timers every event") +AddOption(stuckProtection, int, 0, "", 0, "Timeout in us, When AMD GPU is stuck, just continue processing and skip tracking, do not crash or stall the chain") +AddOption(keepAllMemory, bool, false, "", 0, "Allocate all memory on both device and host, and do not reuse") +AddOption(keepDisplayMemory, bool, false, "", 0, "Like keepAllMemory, but only for memory required for event display") +AddOption(trackletSelectorSlices, char, -1, "", 0, "Number of slices to processes in parallel at max") +AddOption(nTPCClustererLanes, int, 3, "", 0, "Number of TPC clusterers that can run in parallel") +AddOption(disableMemoryReuse, bool, false, "", 0, "Disable memory reusage (for debugging only)") +AddOption(fullMergerOnGPU, bool, true, "", 0, "Perform full TPC track merging on GPU instead of only refit") +AddOption(delayedOutput, bool, true, "", 0, "Delay output to be parallel to track fit") +AddOption(tpccfGatherKernel, bool, true, "", 0, "Use a kernel instead of the DMA engine to gather the clusters") +AddOption(doublePipelineClusterizer, bool, true, "", 0, "Include the input data of the clusterizer in the double-pipeline") +AddOption(deviceNum, int, -1, "gpuDevice", 0, "Set GPU device to use (-1: automatic, -2: for round-robin usage in timeslice-pipeline)") +AddOption(globalInitMutex, bool, false, "", 0, "Use global mutex to synchronize initialization of multiple GPU instances") +AddOption(ompThreads, int, -1, "omp", 't', "Number of OMP threads to run (-1: all)", min(-1), message("Using %s OMP threads")) +AddOption(nDeviceHelperThreads, int, 1, "", 0, "Number of CPU helper threads for CPU processing") +AddOption(nStreams, int, 8, "", 0, "Number of GPU streams / command queues") +AddOption(trackletConstructorInPipeline, int, -1, "", 0, "Run tracklet constructor in the pipeline") +AddOption(trackletSelectorInPipeline, int, -1, "", 0, "Run tracklet selector in the pipeline") +AddOption(mergerSortTracks, int, -1, "", 0, "Sort track indizes for GPU track fit") +AddOption(tpcCompressionGatherMode, int, -1, "", 0, "TPC Compressed Clusters Gather Mode (0: DMA transfer gather gpu to host, 1: serial DMA to host and gather by copy on CPU, 2. gather via GPU kernal DMA access, 3. gather on GPU via kernel, dma afterwards") +AddOption(tpcCompressionGatherModeKernel, int, -1, "", 0, "TPC Compressed Clusters Gather Mode Kernel (0: unbufferd, 1-3: buffered, 4: multi-block)") +AddOption(runMC, bool, false, "", 0, "Process MC labels") +AddOption(ompKernels, unsigned char, 2, "", 0, "Parallelize with OMP inside kernels instead of over slices, 2 for nested parallelization over TPC sectors and inside kernels") +AddOption(doublePipeline, bool, false, "", 0, "Double pipeline mode") +AddOption(prefetchTPCpageScan, int, 0, "", 0, "Prefetch Data for TPC page scan in CPU cache") +AddOption(debugLevel, int, -1, "debug", 'd', "Set debug level (-1 = silend)") +AddOption(allocDebugLevel, int, 0, "allocDebug", 0, "Some debug output for memory allocations (without messing with normal debug level)") +AddOption(runQA, int, 0, "qa", 'q', "Enable tracking QA (negative number to provide bitmask for QA tasks)", message("Running QA: %s"), def(1)) +AddOption(runCompressionStatistics, bool, false, "compressionStat", 0, "Run statistics and verification for cluster compression") +AddOption(forceMemoryPoolSize, unsigned long, 1, "memSize", 0, "Force size of allocated GPU / page locked host memory", min(0ul)) +AddOption(forceHostMemoryPoolSize, unsigned long, 0, "hostMemSize", 0, "Force size of allocated host page locked host memory (overriding memSize)", min(0ul)) +AddOption(memoryAllocationStrategy, int, 0, "", 0, "Memory Allocation Stragegy (0 = auto, 1 = individual allocations, 2 = single global allocation)") +AddOption(deviceTimers, bool, true, "", 0, "Use device timers instead of host-based time measurement") +AddOption(registerStandaloneInputMemory, bool, false, "registerInputMemory", 0, "Automatically register input memory buffers for the GPU") +AddOption(memoryScalingFactor, float, 1.f, "", 0, "Factor to apply to all memory scalers") +AddOption(alternateBorderSort, int, -1, "", 0, "Alternative implementation for sorting of border tracks") +AddOption(enableRTC, bool, false, "", 0, "Use RTC to optimize GPU code") +AddOption(rtcConstexpr, bool, true, "", 0, "Replace constant variables by static constexpr expressions") +AddOption(showOutputStat, bool, false, "", 0, "Print some track output statistics") +AddVariable(eventDisplay, GPUCA_NAMESPACE::gpu::GPUDisplayBackend*, nullptr) +AddHelp("help", 'h') +EndConfig() + +#ifndef GPUCA_GPUCODE_DEVICE +// Settings concerning the event display +BeginSubConfig(GPUSettingsDisplay, GL, configStandalone, "GL", 'g', "OpenGL display settings") +AddOption(clustersOnly, bool, false, "", 0, "Visualize clusters only") +AddOption(clustersOnNominalRow, bool, false, "", 0, "Show clusters at nominal x of pad row for early-transformed data") +AddHelp("help", 'h') +EndConfig() + +// Light settings concerning the event display (can be changed without rebuilding vertices) +BeginSubConfig(GPUSettingsDisplayLight, GLlight, configStandalone, "GL", 'g', "OpenGL display settings") +AddOption(animationMode, int, 0, "", 0, "") +AddOption(smoothPoints, bool, true, "", 0, "Apply smoothing to points") +AddOption(smoothLines, bool, false, "", 0, "Apply smoothing to lines") +AddOption(depthBuffer, bool, false, "", 0, "Enable Z-buffer") +AddOption(drawClusters, bool, true, "", 0, "Highlight clusters") +AddOption(drawLinks, bool, false, "", 0, "Highlight links") +AddOption(drawInitLinks, bool, false, "", 0, "Highlight cleaned-up links") +AddOption(drawSeeds, bool, false, "", 0, "Highlight seeds") +AddOption(drawTracklets, bool, false, "", 0, "Highlight tracklets") +AddOption(drawTracks, bool, false, "", 0, "Highlight sector tracks") +AddOption(drawGlobalTracks, bool, false, "", 0, "Highlight global sector tracks prolonged into adjacent sector") +AddOption(drawFinal, bool, false, "", 0, "Highlight final tracks") +AddOption(excludeClusters, int, 0, "", 0, "Exclude clusters from selected draw objects from display") +AddOption(propagateTracks, int, 0, "", 0, "Propagate final tracks further (inward / outward / show MC tracks)") +AddOption(colorClusters, int, 1, "", 0, "Color clusters belonging to track objects") +AddOption(drawSlice, int, -1, "", 0, "Show individual slice") +AddOption(drawRelatedSlices, int, 0, "", 0, "Show related slices (if drawSlice != -1)") +AddOption(drawGrid, int, 0, "", 0, "Highlight grid") +AddOption(colorCollisions, int, 0, "", 0, "Distinguish collisions in timeframe by color") +AddOption(showCollision, int, -1, "", 0, "Show only individual collision") +AddOption(pointSize, float, 2.0f, "", 0, "Set point size") +AddOption(lineWidth, float, 1.4f, "", 0, "Set line width") +AddOption(drawTPC, bool, true, "", 0, "Enable drawing TPC data") +AddOption(drawTRD, bool, true, "", 0, "Enabale drawing TRD data") +AddHelp("help", 'h') +EndConfig() + +// Settings concerning the standalone QA +BeginSubConfig(GPUSettingsQA, QA, configStandalone, "QA", 'q', "QA settings") +AddOptionVec(compareInputs, const char*, "QAinput", 0, "Read histogram from these input files and include them in the output plots") +AddOptionVec(compareInputNames, const char*, "QAinputName", 0, "Legend entries for data from comparison histogram files") +AddOption(name, std::string, "", "", 0, "Legend entry for new data from current processing") +AddOption(output, std::string, "", "", 0, "Store histograms in output root file", def(std::string("histograms.root"))) +AddOption(inputHistogramsOnly, bool, false, "", 0, "Do not run tracking, but just create PDFs from input root files") +AddOption(strict, bool, true, "", 0, "Strict QA mode: Only consider resolution of tracks where the fit ended within 5 cm of the reference, and remove outliers.") +AddOption(qpt, float, 10.f, "", 0, "Set cut for Q/Pt", def(2.f)) +AddOption(recThreshold, float, 0.9f, "", 0, "Compute the efficiency including impure tracks with fake contamination") +AddOption(csvDump, bool, false, "", 0, "Dump all clusters and Pt information into csv file") +AddOption(maxResX, float, 1e6f, "", 0, "Maxmimum X (~radius) for reconstructed track position to take into accound for resolution QA in cm") +AddOption(resPrimaries, int, 0, "", 0, "0: Resolution for all tracks, 1: only for primary tracks, 2: only for non-primaries", def(1)) +AddOption(nativeFitResolutions, bool, false, "", 0, "Create resolution histograms in the native fit units (sin(phi), tan(lambda), Q/Pt)") +AddOption(filterCharge, int, 0, "", 0, "Filter for positive (+1) or negative (-1) charge") +AddOption(filterPID, int, -1, "", 0, "Filter for Particle Type (0 Electron, 1 Muon, 2 Pion, 3 Kaon, 4 Proton)") +AddOption(writeMCLabels, bool, false, "", 0, "Store mc labels to file for later matching") +AddOptionVec(matchMCLabels, const char*, "", 0, "Read labels from files and match them, only process tracks where labels differ") +AddOption(matchDisplayMinPt, float, 0, "", 0, "Minimum Pt of a matched track to be displayed") +AddOption(writeRootFiles, bool, false, "", 0, "Create ROOT canvas files") +AddOption(noMC, bool, false, "", 0, "Force running QA without MC labels even if present") +AddOption(shipToQC, bool, false, "", 0, "Do not write output files but ship histograms for QC") +AddOption(shipToQCAsCanvas, bool, false, "", 0, "Send TCanvases with full layout to QC instead of individual histograms") +AddOption(enableLocalOutput, bool, true, "", 0, "Enable normal output to local PDF files / console") +AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "--qa", "--QAinputHistogramsOnly") +AddHelp("help", 'h') +EndConfig() + +#ifdef GPUCA_STANDALONE +// Settings concerning the standlone timeframe from run 2 events assembly tool +BeginSubConfig(GPUSettingsTF, TF, configStandalone, "TF", 't', "Timeframe settings") +AddOption(nMerge, int, 0, "", 0, "Merge n events in a timeframe", min(0)) +AddOption(averageDistance, float, 50., "", 0, "Average distance in cm of events merged into timeframe", min(0.f)) +AddOption(randomizeDistance, bool, true, "", 0, "Randomize distance around average distance of merged events") +AddOption(shiftFirstEvent, bool, true, "", 0, "Also shift the first event in z when merging events to a timeframe") +AddOption(bunchSim, int, 0, "", 0, "Simulate correct bunch interactions instead of placing only the average number of events. A value [n] > 1 sets TFlen for [n] collisions in average. (Incompatible to TFmerge)") +AddOption(bunchCount, int, 12, "", 0, "Number of bunches per trainsort") +AddOption(bunchSpacing, int, 50, "", 0, "Spacing between benches in ns") +AddOption(bunchTrainCount, int, 48, "", 0, "Number of bunch trains") +AddOption(abortGapTime, int, (3000), "", 0, "Length of abort gap in ns") +AddOption(interactionRate, int, 50000, "", 0, "Instantaneous interaction rate") +AddOption(timeFrameLen, long, (1000000000 / 44), "", 'l', "Timeframe len in ns") +AddOption(noBorder, bool, false, "", 0, "Do not simulate border effects (partial events)") +AddOption(noEventRepeat, int, 0, "", 0, "0: Place random events, 1: Place events in timeframe one after another, 2: Place random events but do not repat", def(1)) +AddOption(nTotalEventsInTF, int, 0, "", 0, "Total number of collisions to be placed in the interior of all time frames (excluding borders)") +AddOption(eventStride, int, 0, "", 0, "Do not select random event, but walk over array of events in stride steps") +AddOption(overlayRaw, bool, false, "", 0, "Overlay raw TPC data instead of spatial clusters") +AddHelp("help", 'h') +EndConfig() + +// Settings concerning standalone toy event generator +BeginSubConfig(GPUSettingsEG, EG, configStandalone, "EG", 0, "Event generator settings") +AddOption(numberOfTracks, int, 1, "", 0, "Number of tracks per generated event") +AddHelp("help", 'h') +EndConfig() + +// Settings for the standalone benchmark +BeginConfig(GPUSettingsStandalone, configStandalone) +#if defined(CUDA_ENABLED) || defined(OPENCL1_ENABLED) || defined(OPENCL2_ENABLED) || defined(HIP_ENABLED) +AddOption(runGPU, bool, true, "", 'g', "Use GPU for processing", message("GPU processing: %s")) +#else +AddOption(runGPU, bool, false, "", 'g', "Use GPU for processing", message("GPU processing: %s")) +#endif +AddOptionSet(runGPU, bool, false, "", 'c', "Use CPU for processing", message("CPU enabled")) +#if defined(CUDA_ENABLED) +AddOption(gpuType, const char*, "CUDA", "", 0, "GPU type (CUDA / HIP / OCL / OCL2)") +#elif defined(OPENCL2_ENABLED) +AddOption(gpuType, const char*, "OCL2", "", 0, "GPU type (CUDA / HIP / OCL / OCL2)") +#elif defined(OPENCL1_ENABLED) +AddOption(gpuType, const char*, "OCL", "", 0, "GPU type (CUDA / HIP / OCL / OCL2)") +#elif defined(HIP_ENABLED) +AddOption(gpuType, const char*, "HIP", "", 0, "GPU type (CUDA / HIP / OCL / OCL2)") +#else +AddOption(gpuType, const char*, "", "", 0, "GPU type (CUDA / HIP / OCL / OCL2)") +#endif +AddOption(runGPUforce, bool, true, "", 0, "Force usage of the specified GPU device type, no CPU fallback") +AddOption(noprompt, bool, true, "", 0, "Do prompt for keypress before exiting") +AddOption(continueOnError, bool, false, "", 0, "Continue processing after an error") +AddOption(seed, int, -1, "", 0, "Set srand seed (-1: random)") +AddOption(StartEvent, int, 0, "", 's', "First event to process", min(0)) +AddOption(NEvents, int, -1, "", 'n', "Number of events to process (-1; all)", min(0)) +AddOption(runs, int, 1, "runs", 'r', "Number of iterations to perform (repeat each event)", min(0)) +AddOption(runs2, int, 1, "runsExternal", 0, "Number of iterations to perform (repeat full processing)", min(1)) +AddOption(runsInit, int, 1, "", 0, "Number of initial iterations excluded from average", min(0)) +AddOption(EventsDir, const char*, "pp", "events", 'e', "Directory with events to process", message("Reading events from Directory events/%s")) +AddOption(eventDisplay, int, 0, "display", 'd', "Show standalone event display", def(1)) //1: default display (Windows / X11), 2: glut, 3: glfw +AddOption(eventGenerator, bool, false, "", 0, "Run event generator") +AddOption(cont, bool, false, "", 0, "Process continuous timeframe data") +AddOption(outputcontrolmem, unsigned long, 0, "outputMemory", 0, "Use predefined output buffer of this size", min(0ul), message("Using %s bytes as output memory")) +AddOption(inputcontrolmem, unsigned long, 0, "inputMemory", 0, "Use predefined input buffer of this size", min(0ul), message("Using %s bytes as input memory")) +AddOption(cpuAffinity, int, -1, "", 0, "Pin CPU affinity to this CPU core", min(-1)) +AddOption(fifoScheduler, bool, false, "", 0, "Use FIFO realtime scheduler", message("Setting FIFO scheduler: %s")) +AddOption(fpe, bool, true, "", 0, "Trap on floating point exceptions") +AddOption(flushDenormals, bool, true, "", 0, "Enable FTZ and DAZ (Flush all denormals to zero)") +AddOption(solenoidBz, float, -1e6f, "", 0, "Field strength of solenoid Bz in kGaus") +AddOption(constBz, bool, false, "", 0, "Force constand Bz") +AddOption(overrideMaxTimebin, bool, false, "", 0, "Override max time bin setting for continuous data with max time bin in time frame") +AddOption(encodeZS, int, -1, "", 0, "Zero-Suppress TPC data", def(1)) +AddOption(zsFilter, int, -1, "", 0, "Apply Zero-Suppression when loading digits and remove those below threshold", def(1)) +AddOption(zs12bit, bool, true, "", 0, "Perform 12 bit zero-suppression encoding / filter") +AddOption(dumpEvents, bool, false, "", 0, "Dump events (after transformation such as encodeZS") +AddOption(stripDumpedEvents, bool, false, "", 0, "Remove redundant inputs (e.g. digits and ZS) before dumping") +AddOption(printSettings, bool, false, "", 0, "Print all settings") +AddOption(memoryStat, bool, false, "", 0, "Print memory statistics") +AddOption(testSyncAsync, bool, false, "syncAsync", 0, "Test first synchronous and then asynchronous processing") +AddOption(testSync, bool, false, "sync", 0, "Test settings for synchronous phase") +AddOption(timeFrameTime, bool, false, "tfTime", 0, "Print some debug information about time frame processing time") +AddOption(controlProfiler, bool, false, "", 0, "Issues GPU profiler stop and start commands to profile only the relevant processing part") +AddOption(preloadEvents, bool, false, "", 0, "Preload events into host memory before start processing") +AddOption(recoSteps, int, -1, "", 0, "Bitmask for RecoSteps") +AddOption(recoStepsGPU, int, -1, "", 0, "Bitmask for RecoSteps") +AddOption(runMerger, int, 1, "", 0, "Run track merging / refit", min(0), max(1)) +AddOption(runTRD, int, -1, "", 0, "Enable TRD processing") +AddOption(rundEdx, int, -1, "", 0, "Enable dEdx processing") +AddOption(runCompression, int, 1, "", 0, "Enable TPC Compression") +AddOption(runTransformation, int, 1, "", 0, "Enable TPC Transformation") +AddOption(runRefit, bool, false, "", 0, "Enable final track refit") +AddHelp("help", 'h') +AddHelpAll("helpall", 'H') +AddSubConfig(GPUSettingsRec, rec) +AddSubConfig(GPUSettingsProcessing, proc) +AddSubConfig(GPUSettingsTF, TF) +AddSubConfig(GPUSettingsQA, QA) +AddSubConfig(GPUSettingsDisplay, GL) +AddSubConfig(GPUSettingsDisplayLight, GLlight) +AddSubConfig(GPUSettingsEG, EG) +EndConfig() +#elif defined(GPUCA_O2_LIB) || defined(GPUCA_O2_INTERFACE) // GPUCA_STANDALONE +BeginSubConfig(GPUSettingsO2, global, configStandalone, "O2", 0, "O2 workflow settings") +AddOption(solenoidBz, float, -1e6f, "", 0, "solenoid field strength") +AddOption(constBz, bool, false, "", 0, "force constant Bz for tests") +AddOption(continuousMaxTimeBin, int, 0, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for default of 23ms") +AddOption(deviceType, std::string, "CPU", "", 0, "Device type, CPU | CUDA | HIP | OCL1 | OCL2") +AddOption(forceDeviceType, bool, true, "", 0, "force device type, otherwise allows fall-back to CPU") +AddOption(dump, int, 0, "", 0, "Dump events for standalone benchmark: 1 = dump events, 2 = dump events and skip processing in workflow") +AddOption(runDisplay, bool, false, "", 0, "Run event visualization after processing") +AddOption(dEdxFile, std::string, "", "", 0, "File name of dEdx Splines file") +AddOption(transformationFile, std::string, "", "", 0, "File name of TPC fast transformation map") +AddOption(matLUTFile, std::string, "", "", 0, "File name of material LUT file") +AddOption(gainCalibFile, std::string, "", "", 0, "File name of TPC pad gain calibration") +AddOption(allocateOutputOnTheFly, bool, true, "", 0, "Allocate shm output buffers on the fly, instead of using preallocated buffer with upper bound size") +AddOption(outputBufferSize, unsigned long, 200000000ul, "", 0, "Size of the output buffers to be allocated") +AddOption(synchronousProcessing, bool, false, "", 0, "Apply performance shortcuts for synchronous processing, disable unneeded steps") +AddOption(mutexMemReg, bool, false, "", 0, "Global mutex to serialize GPU memory registration") +AddOption(display, bool, false, "", 0, "Enable standalone gpu tracking visualizaion") +AddOption(dropSecondaryLegs, bool, true, "", 0, "Do not store secondary legs of looping track in TrackTPC") +AddOption(memoryBufferScaleFactor, float, 1.f, "", 0, "Factor to scale buffer size estimations") +EndConfig() +#endif // GPUCA_O2_LIB +#endif // !GPUCA_GPUCODE_DEVICE + +// Derrived parameters used in GPUParam +BeginHiddenConfig(GPUSettingsParam, param) +AddVariableRTC(DAlpha, float, 0.f) // angular size +AddVariableRTC(BzkG, float, 0.f) // constant magnetic field value in kG +AddVariableRTC(ConstBz, float, 0.f) // constant magnetic field value in kG*clight +AddVariableRTC(AssumeConstantBz, char, 0) // Assume a constant magnetic field +AddVariableRTC(ToyMCEventsFlag, char, 0) // events were build with home-made event generator +AddVariableRTC(ContinuousTracking, char, 0) // Continuous tracking, estimate bz and errors for abs(z) = 125cm during seeding +AddVariableRTC(resetTimers, char, 0) // Reset benchmark timers before event processing +AddVariableRTC(dodEdx, char, 0) // Do dEdx computation +AddVariableRTC(earlyTpcTransform, char, 0) // do Early TPC transformation +AddVariableRTC(debugLevel, char, 0) // Debug level +AddVariableRTC(continuousMaxTimeBin, int, 0) // Max time bin for continuous tracking +AddVariable(dummyRTC, void*, nullptr) // Ensure non empty struct and proper alignment even if all normal members are constexpr +EndConfig() + +EndNamespace() // gpu +EndNamespace() // GPUCA_NAMESPACE +#endif // #ifdef BeginNamespace + // clang-format on diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 21173bed8ad3f..36b776e059cd5 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -16,7 +16,97 @@ endif() message(STATUS "Building GPUTracking with CUDA support ${TMP_TARGET}") set(SRCS GPUReconstructionCUDA.cu) -set(HDRS GPUReconstructionCUDA.h GPUReconstructionCUDAInternals.h) +set(HDRS GPUReconstructionCUDA.h GPUReconstructionCUDAInternals.h GPUReconstructionCUDADef.h GPUReconstructionCUDAIncludes.h) + +# -------------------------------- Prepare RTC ------------------------------------------------------- +if(NOT ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") + enable_language(ASM) + if(ALIGPU_BUILD_TYPE STREQUAL "O2") + set(defineIncludeSrc "O2::${MODULE}") + else() + set(defineIncludeSrc "${MODULE}") + endif() + set(CURTC_DEFINES "-D$<JOIN:$<TARGET_PROPERTY:${defineIncludeSrc},COMPILE_DEFINITIONS>,$<SEMICOLON>-D>" + -DGPUCA_GPUCODE_GENRTC + ) + set(CURTC_INCLUDES "-I$<JOIN:$<TARGET_PROPERTY:${defineIncludeSrc},INCLUDE_DIRECTORIES>,$<SEMICOLON>-I>" + -I${CMAKE_SOURCE_DIR}/Detectors/Base/src + -I${CMAKE_SOURCE_DIR}/Detectors/TRD/base/src + ) + if(ALIGPU_BUILD_TYPE STREQUAL "O2") + set(CURTC_INCLUDES ${CURTC_INCLUDES} "-I$<JOIN:$<TARGET_PROPERTY:O2::ITStrackingCUDA,INCLUDE_DIRECTORIES>,$<SEMICOLON>-I>") + endif() + #set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -keep") + + set(CUDARTC_FLAGS "${CMAKE_CUDA_FLAGS} ${CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE}} -std=c++${CMAKE_CUDA_STANDARD}") + if(CUDA_COMPUTETARGET) + set(CUDARTC_FLAGS "${CUDARTC_FLAGS} -gencode arch=compute_${CUDA_COMPUTETARGET},code=sm_${CUDA_COMPUTETARGET}") + set(RTC_CUDA_ARCH "${CUDA_COMPUTETARGET}0") + else() + set(RTC_CUDA_ARCH "750") + endif() + separate_arguments(CUDARTC_FLAGS) + + # convenience variables + if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") + get_filename_component(GPUDIR ${CMAKE_SOURCE_DIR}/../ ABSOLUTE) + else() + set(GPUDIR ${CMAKE_SOURCE_DIR}/GPU/GPUTracking) + endif() + set(CURTC_SRC ${GPUDIR}/Base/cuda/GPUReconstructionCUDArtc.cu) + set(CURTC_BIN ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionCUDArtc) + + # cmake-format: off + add_custom_command( + OUTPUT ${CURTC_BIN}.src + COMMAND cat ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludes.h > ${CURTC_BIN}.src + COMMAND ${CMAKE_CXX_COMPILER} ${CURTC_DEFINES} ${CURTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -D__CUDACC__ -x c++ -E ${CURTC_SRC} >> ${CURTC_BIN}.src + MAIN_DEPENDENCY ${CURTC_SRC} + IMPLICIT_DEPENDS CXX ${CURTC_SRC} + COMMAND_EXPAND_LISTS + ) + + add_custom_command( + OUTPUT ${CURTC_BIN}.src.S + COMMAND cat ${GPUDIR}/Standalone/makefiles/include.S | sed "s/FILENAMEMOD/_curtc_GPUReconstructionCUDArtc_cu_src/g" | sed "s,FILENAMENORMAL,${CURTC_BIN}.src,g" > ${CURTC_BIN}.src.S + MAIN_DEPENDENCY ${GPUDIR}/Standalone/makefiles/include.S + ) + + add_custom_command( + OUTPUT ${CURTC_BIN}.command + COMMAND echo -n "${CMAKE_CUDA_COMPILER} ${CUDARTC_FLAGS} ${CURTC_DEFINES}" > ${CURTC_BIN}.command + COMMAND_EXPAND_LISTS + VERBATIM + ) + + add_custom_command( + OUTPUT ${CURTC_BIN}.command.S + COMMAND cat ${GPUDIR}/Standalone/makefiles/include.S | sed "s/FILENAMEMOD/_curtc_GPUReconstructionCUDArtc_cu_command/g" | sed "s,FILENAMENORMAL,${CURTC_BIN}.command,g" > ${CURTC_BIN}.command.S + MAIN_DEPENDENCY ${GPUDIR}/Standalone/makefiles/include.S + DEPENDS ${CURTC_BIN}.command + ) + # cmake-format: on + + # make cmake compile the assembler file, add proper dependency on included + # binary code + set_source_files_properties( + ${CURTC_BIN}.src.S + PROPERTIES + LANGUAGE + ASM + OBJECT_DEPENDS + "${CURTC_BIN}.src;${GPUDIR}/Standalone/makefiles/include.S") + + set_source_files_properties( + ${CURTC_BIN}.command.S + PROPERTIES + LANGUAGE + ASM + ) + + set(SRCS ${SRCS} ${CURTC_BIN}.src.S ${CURTC_BIN}.command.S) +endif() +# -------------------------------- End RTC ------------------------------------------------------- if(ALIGPU_BUILD_TYPE STREQUAL "O2") o2_add_library( @@ -25,14 +115,12 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/Detectors/Base/src ${CMAKE_SOURCE_DIR}/Detectors/TRD/base/src - PUBLIC_LINK_LIBRARIES O2::GPUTracking O2::ITStrackingCUDA cub::cub + ${CMAKE_SOURCE_DIR}/DataFormats/Reconstruction/src + PUBLIC_LINK_LIBRARIES O2::GPUTracking O2::ITStrackingCUDA TARGETVARNAME targetName) target_compile_definitions( - ${targetName} PUBLIC GPUCA_GPULIBRARY=CUDA - $<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>) - - target_compile_options(${targetName} PUBLIC --expt-relaxed-constexpr) + ${targetName} PUBLIC $<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>) set_target_properties(${targetName} PROPERTIES LINKER_LANGUAGE CXX) @@ -40,39 +128,45 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") endif() if(ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") - add_definitions(-DGPUCA_GPULIBRARY=CUDA) - + set(targetName Ali${MODULE}) # Generate the dictionary get_directory_property(incdirs INCLUDE_DIRECTORIES) - generate_dictionary("Ali${MODULE}" "" "GPUReconstructionCUDA.h" + generate_dictionary("${targetName}" "" "GPUReconstructionCUDA.h" "${incdirs} .") # Generate the ROOT map Dependecies - generate_rootmap("Ali${MODULE}" "" "") + generate_rootmap("${targetName}" "" "") # Add a library to the project using the specified source files - add_library_tested(Ali${MODULE} SHARED ${SRCS} G__Ali${MODULE}.cxx) + add_library_tested(${targetName} SHARED ${SRCS} G__${targetName}.cxx) # CUDA run-time and driver - target_link_libraries(Ali${MODULE} PUBLIC AliGPUTracking) + target_link_libraries(${targetName} PUBLIC AliGPUTracking) # Additional compilation flags - set_target_properties(Ali${MODULE} PROPERTIES COMPILE_FLAGS "") + set_target_properties(${targetName} PROPERTIES COMPILE_FLAGS "") # Installation - install(TARGETS Ali${MODULE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) + install(TARGETS ${targetName} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) install(FILES ${HDRS} DESTINATION include) - set(targetName Ali${MODULE}) + endif() if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") - add_definitions(-DGPUCA_GPULIBRARY=CUDA) add_library(${MODULE} SHARED ${SRCS}) - target_link_libraries(${MODULE} PUBLIC GPUTracking cub::cub) + target_link_libraries(${MODULE} PUBLIC GPUTracking) set(targetName ${MODULE}) install(TARGETS GPUTrackingCUDA) endif() # Since -target_link_libraries(${targetName} PRIVATE cuda cudart) +target_link_libraries(${targetName} PRIVATE cuda cudart nvrtc) +target_compile_definitions(${targetName} PUBLIC GPUCA_GPULIBRARY=CUDA) +if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "86") +target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_AMPERE) +elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "75") +target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_TURING) +else() +target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_AMPERE) +endif() diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index eae5b0f82c556..52066afae40c6 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -11,41 +11,22 @@ /// \file GPUReconstructionCUDA.cu /// \author David Rohr -#define GPUCA_GPUTYPE_TURING -#define GPUCA_UNROLL(CUDA, HIP) GPUCA_M_UNROLL_##CUDA +#include "GPUReconstructionCUDADef.h" +#include "GPUReconstructionCUDAIncludes.h" -#include <cuda.h> #include <cuda_profiler_api.h> -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#include <thrust/sort.h> -#include <thrust/device_ptr.h> -#pragma GCC diagnostic pop - -#ifdef __clang__ -#define assert(...) -#endif - -#include "GPUDef.h" - -#ifndef GPUCA_NO_CONSTANT_MEMORY -#define GPUCA_CONSMEM_PTR -#define GPUCA_CONSMEM_CALL -#define GPUCA_CONSMEM (gGPUConstantMemBuffer.v) -#else -#define GPUCA_CONSMEM_PTR const GPUConstantMem *gGPUConstantMemBuffer, -#define GPUCA_CONSMEM_CALL me->mDeviceConstantMem, -#define GPUCA_CONSMEM ((GPUConstantMem&)(*gGPUConstantMemBuffer)) -#endif -#define GPUCA_KRNL_BACKEND_CLASS GPUReconstructionCUDABackend +#include <unistd.h> #include "GPUReconstructionCUDA.h" #include "GPUReconstructionCUDAInternals.h" #include "GPUReconstructionIncludes.h" +#include "GPUParamRTC.h" -static constexpr size_t REQUIRE_MIN_MEMORY = 1024u * 1024 * 1024; -static constexpr size_t REQUIRE_MEMORY_RESERVED = 512u * 1024 * 1024; -static constexpr size_t REQUIRE_FREE_MEMORY_RESERVED = 1280u * 1024 * 1024; +static constexpr size_t REQUIRE_MIN_MEMORY = 1024L * 1024 * 1024; +static constexpr size_t REQUIRE_MEMORY_RESERVED = 512L * 1024 * 1024; +static constexpr size_t REQUIRE_FREE_MEMORY_RESERVED_PER_SM = 40L * 1024 * 1024; +static constexpr size_t RESERVE_EXTRA_MEM_THRESHOLD = 10L * 1024 * 1024 * 1024; +static constexpr size_t RESERVE_EXTRA_MEM_OFFSET = 1L * 512 * 1024 * 1024; using namespace GPUCA_NAMESPACE::gpu; @@ -54,7 +35,7 @@ texture<cahit2, cudaTextureType1D, cudaReadModeElementType> gAliTexRefu2; texture<calink, cudaTextureType1D, cudaReadModeElementType> gAliTexRefu; #endif -__global__ void dummyInitKernel(void* foo) +__global__ void dummyInitKernel(void*) { } @@ -62,9 +43,7 @@ __global__ void dummyInitKernel(void* foo) #include "ITStrackingCUDA/TrackerTraitsNV.h" #include "ITStrackingCUDA/VertexerTraitsGPU.h" #else -namespace o2 -{ -namespace its +namespace o2::its { class TrackerTraitsNV : public TrackerTraits { @@ -72,8 +51,7 @@ class TrackerTraitsNV : public TrackerTraits class VertexerTraitsGPU : public VertexerTraits { }; -} // namespace its -} // namespace o2 +} // namespace o2::its #endif class GPUDebugTiming @@ -116,6 +94,12 @@ class GPUDebugTiming #include "GPUReconstructionIncludesDevice.h" +#ifndef GPUCA_ALIROOT_LIB +extern "C" char _curtc_GPUReconstructionCUDArtc_cu_src[]; +extern "C" unsigned int _curtc_GPUReconstructionCUDArtc_cu_src_size; +extern "C" char _curtc_GPUReconstructionCUDArtc_cu_command[]; +#endif + /* // Not using templated kernel any more, since nvidia profiler does not resolve template names template <class T, int I, typename... Args> @@ -127,7 +111,7 @@ GPUg() void runKernelCUDA(GPUCA_CONSMEM_PTR int iSlice_internal, Args... args) */ #undef GPUCA_KRNL_REG -#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_STRIP(args)) +#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) #define GPUCA_KRNL(x_class, x_attributes, x_arguments, x_forward) \ GPUCA_KRNL_PROP(x_class, x_attributes) \ GPUCA_KRNL_WRAP(GPUCA_KRNL_, x_class, x_attributes, x_arguments, x_forward) @@ -139,18 +123,53 @@ GPUg() void runKernelCUDA(GPUCA_CONSMEM_PTR int iSlice_internal, Args... args) #include "GPUReconstructionKernels.h" #undef GPUCA_KRNL +template <bool multi, class T, int I> +int GPUReconstructionCUDAInternals::getRTCkernelNum(int k) +{ + static int num = k; + if (num < 0) { + throw std::runtime_error("Invalid kernel"); + } + return num; +} + template <> void GPUReconstructionCUDABackend::runKernelBackendInternal<GPUMemClean16, 0>(krnlSetup& _xyz, void* const& ptr, unsigned long const& size) { - GPUDebugTiming timer(mDeviceProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); + GPUDebugTiming timer(mProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); GPUFailedMsg(cudaMemsetAsync(ptr, 0, size, mInternals->Streams[_xyz.x.stream])); } +static void getArgPtrs(const void** pArgs) {} +template <typename T, typename... Args> +static void getArgPtrs(const void** pArgs, const T& arg, const Args&... args) +{ + *pArgs = &arg; + getArgPtrs(pArgs + 1, args...); +} + template <class T, int I, typename... Args> void GPUReconstructionCUDABackend::runKernelBackendInternal(krnlSetup& _xyz, const Args&... args) { - GPUDebugTiming timer(mDeviceProcessingSettings.deviceTimers, (void**)mDebugEvents, mInternals->Streams, _xyz); - backendInternal<T, I>::runKernelBackendMacro(_xyz, this, args...); + GPUDebugTiming timer(mProcessingSettings.deviceTimers && mProcessingSettings.debugLevel > 0, (void**)mDebugEvents, mInternals->Streams, _xyz); + if (mProcessingSettings.enableRTC) { + auto& x = _xyz.x; + auto& y = _xyz.y; + if (y.num <= 1) { + const void* pArgs[sizeof...(Args) + 1]; + pArgs[0] = &y.start; + getArgPtrs(&pArgs[1], args...); + GPUFailedMsg(cuLaunchKernel(*mInternals->rtcFunctions[mInternals->getRTCkernelNum<false, T, I>()], x.nBlocks, 1, 1, x.nThreads, 1, 1, 0, mInternals->Streams[x.stream], (void**)pArgs, nullptr)); + } else { + const void* pArgs[sizeof...(Args) + 2]; + pArgs[0] = &y.start; + pArgs[1] = &y.num; + getArgPtrs(&pArgs[2], args...); + GPUFailedMsg(cuLaunchKernel(*mInternals->rtcFunctions[mInternals->getRTCkernelNum<true, T, I>()], x.nBlocks, 1, 1, x.nThreads, 1, 1, 0, mInternals->Streams[x.stream], (void**)pArgs, nullptr)); + } + } else { + backendInternal<T, I>::runKernelBackendMacro(_xyz, this, args...); + } } template <class T, int I, typename... Args> @@ -171,12 +190,12 @@ int GPUReconstructionCUDABackend::runKernelBackend(krnlSetup& _xyz, const Args&. return 0; } -GPUReconstructionCUDABackend::GPUReconstructionCUDABackend(const GPUSettingsProcessing& cfg) : GPUReconstructionDeviceBase(cfg, sizeof(GPUReconstructionDeviceBase)) +GPUReconstructionCUDABackend::GPUReconstructionCUDABackend(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionDeviceBase(cfg, sizeof(GPUReconstructionDeviceBase)) { if (mMaster == nullptr) { mInternals = new GPUReconstructionCUDAInternals; } - mProcessingSettings.deviceType = DeviceType::CUDA; + mDeviceBackendSettings.deviceType = DeviceType::CUDA; } GPUReconstructionCUDABackend::~GPUReconstructionCUDABackend() @@ -187,7 +206,7 @@ GPUReconstructionCUDABackend::~GPUReconstructionCUDABackend() } } -GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUSettingsProcessing& cfg) { return new GPUReconstructionCUDA(cfg); } +GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUSettingsDeviceBackend& cfg) { return new GPUReconstructionCUDA(cfg); } void GPUReconstructionCUDABackend::GetITSTraits(std::unique_ptr<o2::its::TrackerTraits>* trackerTraits, std::unique_ptr<o2::its::VertexerTraits>* vertexerTraits) { @@ -218,7 +237,7 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() GPUError("Error getting CUDA Device Count"); return (1); } - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Available CUDA devices:"); } const int reqVerMaj = 2; @@ -227,7 +246,7 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() std::vector<size_t> devMemory(count, 0); bool contextCreated = false; for (int i = 0; i < count; i++) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Examining device %d", i); } size_t free, total; @@ -237,14 +256,14 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() return (1); } if (GPUFailedMsgI(cuCtxCreate(&mInternals->CudaContext, 0, tmpDevice))) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUWarning("Couldn't create context for device %d. Skipping it.", i); } continue; } contextCreated = true; if (GPUFailedMsgI(cuMemGetInfo(&free, &total))) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUWarning("Error obtaining CUDA memory info about device %d! Skipping it.", i); } GPUFailedMsg(cuCtxDestroy(mInternals->CudaContext)); @@ -254,13 +273,13 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() GPUFailedMsg(cuCtxDestroy(mInternals->CudaContext)); contextCreated = false; } - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Obtained current memory usage for device %d", i); } if (GPUFailedMsgI(cudaGetDeviceProperties(&cudaDeviceProp, i))) { continue; } - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Obtained device properties for device %d", i); } int deviceOK = true; @@ -268,25 +287,25 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() if (cudaDeviceProp.major < reqVerMaj || (cudaDeviceProp.major == reqVerMaj && cudaDeviceProp.minor < reqVerMin)) { deviceOK = false; deviceFailure = "Too low device revision"; - } else if (free < std::max(mDeviceMemorySize, REQUIRE_MIN_MEMORY)) { + } else if (free < std::max<size_t>(mDeviceMemorySize, REQUIRE_MIN_MEMORY)) { deviceOK = false; deviceFailure = "Insufficient GPU memory"; } deviceSpeed = (double)cudaDeviceProp.multiProcessorCount * (double)cudaDeviceProp.clockRate * (double)cudaDeviceProp.warpSize * (double)free * (double)cudaDeviceProp.major * (double)cudaDeviceProp.major; - if (mDeviceProcessingSettings.debugLevel >= 2) { - GPUImportant("Device %s%2d: %s (Rev: %d.%d - Mem Avail %lld / %lld)%s %s", deviceOK ? " " : "[", i, cudaDeviceProp.name, cudaDeviceProp.major, cudaDeviceProp.minor, (long long int)free, (long long int)cudaDeviceProp.totalGlobalMem, deviceOK ? " " : " ]", deviceOK ? "" : deviceFailure); + if (mProcessingSettings.debugLevel >= 2) { + GPUImportant("Device %s%2d: %s (Rev: %d.%d - Mem Avail %lu / %lu)%s %s", deviceOK ? " " : "[", i, cudaDeviceProp.name, cudaDeviceProp.major, cudaDeviceProp.minor, free, (size_t)cudaDeviceProp.totalGlobalMem, deviceOK ? " " : " ]", deviceOK ? "" : deviceFailure); } if (!deviceOK) { continue; } devicesOK[i] = true; - devMemory[i] = std::min(free, total - REQUIRE_MEMORY_RESERVED); + devMemory[i] = std::min<size_t>(free, std::max<long int>(0, total - REQUIRE_MEMORY_RESERVED)); if (deviceSpeed > bestDeviceSpeed) { bestDevice = i; bestDeviceSpeed = deviceSpeed; } else { - if (mDeviceProcessingSettings.debugLevel >= 2 && mDeviceProcessingSettings.deviceNum < 0) { + if (mProcessingSettings.debugLevel >= 2 && mProcessingSettings.deviceNum < 0) { GPUInfo("Skipping: Speed %f < %f\n", deviceSpeed, bestDeviceSpeed); } } @@ -295,17 +314,17 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() bool noDevice = false; if (bestDevice == -1) { GPUWarning("No %sCUDA Device available, aborting CUDA Initialisation", count ? "appropriate " : ""); - GPUImportant("Requiring Revision %d.%d, Mem: %lld", reqVerMaj, reqVerMin, (long long int)std::max(mDeviceMemorySize, REQUIRE_MIN_MEMORY)); + GPUImportant("Requiring Revision %d.%d, Mem: %lu", reqVerMaj, reqVerMin, std::max<size_t>(mDeviceMemorySize, REQUIRE_MIN_MEMORY)); noDevice = true; - } else if (mDeviceProcessingSettings.deviceNum > -1) { - if (mDeviceProcessingSettings.deviceNum >= (signed)count) { - GPUError("Requested device ID %d does not exist", mDeviceProcessingSettings.deviceNum); + } else if (mProcessingSettings.deviceNum > -1) { + if (mProcessingSettings.deviceNum >= (signed)count) { + GPUError("Requested device ID %d does not exist", mProcessingSettings.deviceNum); noDevice = true; - } else if (!devicesOK[mDeviceProcessingSettings.deviceNum]) { - GPUError("Unsupported device requested (%d)", mDeviceProcessingSettings.deviceNum); + } else if (!devicesOK[mProcessingSettings.deviceNum]) { + GPUError("Unsupported device requested (%d)", mProcessingSettings.deviceNum); noDevice = true; } else { - bestDevice = mDeviceProcessingSettings.deviceNum; + bestDevice = mProcessingSettings.deviceNum; } } if (noDevice) { @@ -318,7 +337,7 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() GPUFailedMsgI(cudaGetDeviceProperties(&cudaDeviceProp, mDeviceId)); - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Using CUDA Device %s with Properties:", cudaDeviceProp.name); GPUInfo("\ttotalGlobalMem = %lld", (unsigned long long int)cudaDeviceProp.totalGlobalMem); GPUInfo("\tsharedMemPerBlock = %lld", (unsigned long long int)cudaDeviceProp.sharedMemPerBlock); @@ -337,6 +356,9 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() GPUInfo("\ttextureAlignment = %lld", (unsigned long long int)cudaDeviceProp.textureAlignment); GPUInfo(" "); } + if (cudaDeviceProp.warpSize != GPUCA_WARP_SIZE) { + throw std::runtime_error("Invalid warp size on GPU"); + } mBlockCount = cudaDeviceProp.multiProcessorCount; mWarpSize = 32; mMaxThreads = std::max<int>(mMaxThreads, cudaDeviceProp.maxThreadsPerBlock * mBlockCount); @@ -377,10 +399,14 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() return (1); } + if (mDeviceMemorySize == 1 || mDeviceMemorySize == 2) { + mDeviceMemorySize = std::max<long int>(0, devMemory[mDeviceId] - REQUIRE_FREE_MEMORY_RESERVED_PER_SM * cudaDeviceProp.multiProcessorCount); // Take all GPU memory but some reserve + if (mDeviceMemorySize >= RESERVE_EXTRA_MEM_THRESHOLD) { + mDeviceMemorySize -= RESERVE_EXTRA_MEM_OFFSET; + } + } if (mDeviceMemorySize == 2) { - mDeviceMemorySize = devMemory[mDeviceId] * 2 / 3; // Leave 1/3 of GPU memory for event display - } else if (mDeviceMemorySize == 1) { - mDeviceMemorySize = devMemory[mDeviceId] - REQUIRE_FREE_MEMORY_RESERVED; // Take all GPU memory but 1/2 GB + mDeviceMemorySize = mDeviceMemorySize * 2 / 3; // Leave 1/3 of GPU memory for event display } if (mDeviceMemorySize > cudaDeviceProp.totalGlobalMem || GPUFailedMsgI(cudaMalloc(&mDeviceMemoryBase, mDeviceMemorySize))) { @@ -393,7 +419,7 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() GPUFailedMsgI(cudaDeviceReset()); return (1); } - if (mDeviceProcessingSettings.debugLevel >= 1) { + if (mProcessingSettings.debugLevel >= 1) { GPUInfo("Memory ptrs: GPU (%lld bytes): %p - Host (%lld bytes): %p", (long long int)mDeviceMemorySize, mDeviceMemoryBase, (long long int)mHostMemorySize, mHostMemoryBase); memset(mHostMemoryBase, 0xDD, mHostMemorySize); if (GPUFailedMsgI(cudaMemset(mDeviceMemoryBase, 0xDD, mDeviceMemorySize))) { @@ -411,25 +437,78 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() } } + dummyInitKernel<<<mBlockCount, 256>>>(mDeviceMemoryBase); + GPUInfo("CUDA Initialisation successfull (Device %d: %s (Frequency %d, Cores %d), %lld / %lld bytes host / global memory, Stack frame %d, Constant memory %lld)", mDeviceId, cudaDeviceProp.name, cudaDeviceProp.clockRate, cudaDeviceProp.multiProcessorCount, (long long int)mHostMemorySize, + (long long int)mDeviceMemorySize, (int)GPUCA_GPU_STACK_SIZE, (long long int)gGPUConstantMemBufferSize); + +#ifndef GPUCA_ALIROOT_LIB + if (mProcessingSettings.enableRTC) { + if (mProcessingSettings.debugLevel >= 0) { + GPUInfo("Starting CUDA RTC Compilation"); + } + HighResTimer rtcTimer; + rtcTimer.ResetStart(); + std::string filename = "/tmp/o2cagpu_rtc_"; + filename += std::to_string(getpid()); + filename += "_"; + filename += std::to_string(rand()); + if (mProcessingSettings.debugLevel >= 3) { + printf("Writing to %s\n", filename.c_str()); + } + FILE* fp = fopen((filename + ".cu").c_str(), "w+b"); + if (fp == nullptr) { + throw std::runtime_error("Error opening file"); + } + std::string rtcparam = GPUParamRTC::generateRTCCode(param(), mProcessingSettings.rtcConstexpr); + if (fwrite(rtcparam.c_str(), 1, rtcparam.size(), fp) != rtcparam.size()) { + throw std::runtime_error("Error writing file"); + } + if (fwrite(_curtc_GPUReconstructionCUDArtc_cu_src, 1, _curtc_GPUReconstructionCUDArtc_cu_src_size, fp) != _curtc_GPUReconstructionCUDArtc_cu_src_size) { + throw std::runtime_error("Error writing file"); + } + fclose(fp); + std::string command = _curtc_GPUReconstructionCUDArtc_cu_command; + command += " -cubin -c " + filename + ".cu -o " + filename + ".o"; + if (mProcessingSettings.debugLevel >= 3) { + printf("Running command %s\n", command.c_str()); + } + if (system(command.c_str())) { + throw std::runtime_error("Runtime compilation failed"); + } + GPUFailedMsg(cuModuleLoad(&mInternals->rtcModule, (filename + ".o").c_str())); + +#define GPUCA_KRNL(x_class, x_attributes, x_arguments, x_forward) GPUCA_KRNL_WRAP(GPUCA_KRNL_LOAD_, x_class, x_attributes, x_arguments, x_forward) +#define GPUCA_KRNL_LOAD_single(x_class, x_attributes, x_arguments, x_forward) \ + mInternals->getRTCkernelNum<false, GPUCA_M_KRNL_TEMPLATE(x_class)>(mInternals->rtcFunctions.size()); \ + mInternals->rtcFunctions.emplace_back(new CUfunction); \ + GPUFailedMsg(cuModuleGetFunction(mInternals->rtcFunctions.back().get(), mInternals->rtcModule, GPUCA_M_STR(GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class))))); +#define GPUCA_KRNL_LOAD_multi(x_class, x_attributes, x_arguments, x_forward) \ + mInternals->getRTCkernelNum<true, GPUCA_M_KRNL_TEMPLATE(x_class)>(mInternals->rtcFunctions.size()); \ + mInternals->rtcFunctions.emplace_back(new CUfunction); \ + GPUFailedMsg(cuModuleGetFunction(mInternals->rtcFunctions.back().get(), mInternals->rtcModule, GPUCA_M_STR(GPUCA_M_CAT3(krnl_, GPUCA_M_KRNL_NAME(x_class), _multi)))); +#include "GPUReconstructionKernels.h" +#undef GPUCA_KRNL +#undef GPUCA_KRNL_LOAD_single +#undef GPUCA_KRNL_LOAD_multi + + remove((filename + ".cu").c_str()); + remove((filename + ".o").c_str()); + if (mProcessingSettings.debugLevel >= 0) { + GPUInfo("RTC Compilation finished (%f seconds)", rtcTimer.GetCurrentElapsedTime()); + } + } +#endif void* devPtrConstantMem; #ifndef GPUCA_NO_CONSTANT_MEMORY - if (GPUFailedMsgI(cudaGetSymbolAddress(&devPtrConstantMem, gGPUConstantMemBuffer))) { - GPUError("Error getting ptr to constant memory"); - GPUFailedMsgI(cudaDeviceReset()); - return 1; + if (mProcessingSettings.enableRTC) { + GPUFailedMsg(cuModuleGetGlobal((CUdeviceptr*)&devPtrConstantMem, nullptr, mInternals->rtcModule, "gGPUConstantMemBuffer")); + } else { + GPUFailedMsg(cudaGetSymbolAddress(&devPtrConstantMem, gGPUConstantMemBuffer)); } #else - if (GPUFailedMsgI(cudaMalloc(&devPtrConstantMem, gGPUConstantMemBufferSize))) { - GPUError("CUDA Memory Allocation Error"); - GPUFailedMsgI(cudaDeviceReset()); - return (1); - } + GPUFailedMsg(cudaMalloc(&devPtrConstantMem, gGPUConstantMemBufferSize)); #endif mDeviceConstantMem = (GPUConstantMem*)devPtrConstantMem; - - dummyInitKernel<<<mBlockCount, 256>>>(mDeviceMemoryBase); - GPUInfo("CUDA Initialisation successfull (Device %d: %s (Frequency %d, Cores %d), %lld / %lld bytes host / global memory, Stack frame %d, Constant memory %lld)", mDeviceId, cudaDeviceProp.name, cudaDeviceProp.clockRate, cudaDeviceProp.multiProcessorCount, (long long int)mHostMemorySize, - (long long int)mDeviceMemorySize, (int)GPUCA_GPU_STACK_SIZE, (long long int)gGPUConstantMemBufferSize); } else { GPUReconstructionCUDABackend* master = dynamic_cast<GPUReconstructionCUDABackend*>(mMaster); mDeviceId = master->mDeviceId; @@ -442,7 +521,7 @@ int GPUReconstructionCUDABackend::InitDevice_Runtime() GPUFailedMsgI(cuCtxPushCurrent(mInternals->CudaContext)); } - if (mDeviceProcessingSettings.debugLevel >= 1) { + if (mProcessingSettings.debugLevel >= 1) { } for (unsigned int i = 0; i < mEvents.size(); i++) { cudaEvent_t* events = (cudaEvent_t*)mEvents[i].data(); @@ -505,7 +584,7 @@ int GPUReconstructionCUDABackend::ExitDevice_Runtime() size_t GPUReconstructionCUDABackend::GPUMemCpy(void* dst, const void* src, size_t size, int stream, int toGPU, deviceEvent* ev, deviceEvent* evList, int nEvents) { - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { stream = -1; } if (stream == -1) { @@ -529,12 +608,12 @@ size_t GPUReconstructionCUDABackend::GPUMemCpy(void* dst, const void* src, size_ size_t GPUReconstructionCUDABackend::TransferMemoryInternal(GPUMemoryResource* res, int stream, deviceEvent* ev, deviceEvent* evList, int nEvents, bool toGPU, const void* src, void* dst) { if (!(res->Type() & GPUMemoryResource::MEMORY_GPU)) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Skipped transfer of non-GPU memory resource: %s", res->Name()); } return 0; } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3 && (strcmp(res->Name(), "ErrorCodes") || mProcessingSettings.debugLevel >= 4)) { GPUInfo("Copying to %s: %s - %lld bytes", toGPU ? "GPU" : "Host", res->Name(), (long long int)res->Size()); } return GPUMemCpy(dst, src, res->Size(), stream, toGPU, ev, evList, nEvents); @@ -548,15 +627,29 @@ size_t GPUReconstructionCUDABackend::WriteToConstantMemory(size_t offset, const } else { GPUFailedMsg(cudaMemcpyToSymbolAsync(gGPUConstantMemBuffer, src, size, offset, cudaMemcpyHostToDevice, mInternals->Streams[stream])); } - -#else - if (stream == -1) { - GPUFailedMsg(cudaMemcpy(((char*)mDeviceConstantMem) + offset, src, size, cudaMemcpyHostToDevice)); - } else { - GPUFailedMsg(cudaMemcpyAsync(((char*)mDeviceConstantMem) + offset, src, size, cudaMemcpyHostToDevice, mInternals->Streams[stream])); - } - + if (mProcessingSettings.enableRTC) #endif + { + std::unique_ptr<GPUParamRTC> tmpParam; + if (mProcessingSettings.rtcConstexpr) { + if (offset < sizeof(GPUParam) && (offset != 0 || size > sizeof(GPUParam))) { + throw std::runtime_error("Invalid write to constant memory, crossing GPUParam border"); + } + if (offset == 0) { + tmpParam.reset(new GPUParamRTC); + tmpParam->setFrom(*(GPUParam*)src); + src = tmpParam.get(); + size = sizeof(*tmpParam); + } else { + offset = offset - sizeof(GPUParam) + sizeof(GPUParamRTC); + } + } + if (stream == -1) { + GPUFailedMsg(cudaMemcpy(((char*)mDeviceConstantMem) + offset, src, size, cudaMemcpyHostToDevice)); + } else { + GPUFailedMsg(cudaMemcpyAsync(((char*)mDeviceConstantMem) + offset, src, size, cudaMemcpyHostToDevice, mInternals->Streams[stream])); + } + } if (ev && stream != -1) { GPUFailedMsg(cudaEventRecord(*(cudaEvent_t*)ev, mInternals->Streams[stream])); } @@ -618,14 +711,14 @@ int GPUReconstructionCUDABackend::GPUDebug(const char* state, int stream) GPUError("Cuda Error %s while running kernel (%s) (Stream %d)", cudaGetErrorString(cuErr), state, stream); return (1); } - if (mDeviceProcessingSettings.debugLevel == 0) { + if (mProcessingSettings.debugLevel <= 0) { return (0); } if (GPUFailedMsgI(cudaDeviceSynchronize())) { GPUError("CUDA Error while synchronizing (%s) (Stream %d)", state, stream); return (1); } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("GPU Sync Done"); } return (0); @@ -656,16 +749,19 @@ int GPUReconstructionCUDABackend::unregisterMemoryForGPU(const void* ptr) void GPUReconstructionCUDABackend::PrintKernelOccupancies() { int maxBlocks, threads, suggestedBlocks; + cudaFuncAttributes attr; GPUFailedMsg(cuCtxPushCurrent(mInternals->CudaContext)); #define GPUCA_KRNL(x_class, x_attributes, x_arguments, x_forward) GPUCA_KRNL_WRAP(GPUCA_KRNL_LOAD_, x_class, x_attributes, x_arguments, x_forward) #define GPUCA_KRNL_LOAD_single(x_class, x_attributes, x_arguments, x_forward) \ GPUFailedMsg(cudaOccupancyMaxPotentialBlockSize(&suggestedBlocks, &threads, GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class)))); \ GPUFailedMsg(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&maxBlocks, GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class)), threads, 0)); \ - GPUInfo("Kernel: %50s Block size: %4d, Maximum active blocks: %3d, Suggested blocks: %3d", GPUCA_M_STR(GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class))), threads, maxBlocks, suggestedBlocks); + GPUFailedMsg(cudaFuncGetAttributes(&attr, GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class)))); \ + GPUInfo("Kernel: %50s Block size: %4d, Maximum active blocks: %3d, Suggested blocks: %3d, Regs: %3d, smem: %3d", GPUCA_M_STR(GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class))), threads, maxBlocks, suggestedBlocks, attr.numRegs, (int)attr.sharedSizeBytes); #define GPUCA_KRNL_LOAD_multi(x_class, x_attributes, x_arguments, x_forward) \ GPUFailedMsg(cudaOccupancyMaxPotentialBlockSize(&suggestedBlocks, &threads, GPUCA_M_CAT3(krnl_, GPUCA_M_KRNL_NAME(x_class), _multi))); \ GPUFailedMsg(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&maxBlocks, GPUCA_M_CAT3(krnl_, GPUCA_M_KRNL_NAME(x_class), _multi), threads, 0)); \ - GPUInfo("Kernel: %50s Block size: %4d, Maximum active blocks: %3d, Suggested blocks: %3d", GPUCA_M_STR(GPUCA_M_CAT3(krnl_, GPUCA_M_KRNL_NAME(x_class), _multi)), threads, maxBlocks, suggestedBlocks); + GPUFailedMsg(cudaFuncGetAttributes(&attr, GPUCA_M_CAT3(krnl_, GPUCA_M_KRNL_NAME(x_class), _multi))); \ + GPUInfo("Kernel: %50s Block size: %4d, Maximum active blocks: %3d, Suggested blocks: %3d, Regs: %3d, smem: %3d", GPUCA_M_STR(GPUCA_M_CAT3(krnl_, GPUCA_M_KRNL_NAME(x_class), _multi)), threads, maxBlocks, suggestedBlocks, attr.numRegs, (int)attr.sharedSizeBytes); #include "GPUReconstructionKernels.h" #undef GPUCA_KRNL #undef GPUCA_KRNL_LOAD_single diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h index 1ef0adb8af580..d3188865df4b6 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h @@ -17,9 +17,9 @@ #include "GPUReconstructionDeviceBase.h" #ifdef _WIN32 -extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #else -extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #endif namespace GPUCA_NAMESPACE @@ -34,7 +34,7 @@ class GPUReconstructionCUDABackend : public GPUReconstructionDeviceBase ~GPUReconstructionCUDABackend() override; protected: - GPUReconstructionCUDABackend(const GPUSettingsProcessing& cfg); + GPUReconstructionCUDABackend(const GPUSettingsDeviceBackend& cfg); int InitDevice_Runtime() override; int ExitDevice_Runtime() override; @@ -44,7 +44,7 @@ class GPUReconstructionCUDABackend : public GPUReconstructionDeviceBase { public: GPUThreadContextCUDA(GPUReconstructionCUDAInternals* context); - virtual ~GPUThreadContextCUDA(); + ~GPUThreadContextCUDA() override; private: GPUReconstructionCUDAInternals* mContext = nullptr; diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDADef.h b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDADef.h new file mode 100644 index 0000000000000..9ef1ce9a820e6 --- /dev/null +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDADef.h @@ -0,0 +1,33 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUReconstructionCUDDef.h +/// \author David Rohr + +#ifndef O2_GPU_GPURECONSTRUCTIONCUDADEF_H +#define O2_GPU_GPURECONSTRUCTIONCUDADEF_H + +#define GPUCA_UNROLL(CUDA, HIP) GPUCA_M_UNROLL_##CUDA +#define GPUdic(CUDA, HIP) GPUCA_GPUdic_select_##CUDA() + +#include "GPUDef.h" + +#ifndef GPUCA_NO_CONSTANT_MEMORY +#define GPUCA_CONSMEM_PTR +#define GPUCA_CONSMEM_CALL +#define GPUCA_CONSMEM (gGPUConstantMemBuffer.v) +#else +#define GPUCA_CONSMEM_PTR const GPUConstantMem *gGPUConstantMemBuffer, +#define GPUCA_CONSMEM_CALL me->mDeviceConstantMem, +#define GPUCA_CONSMEM ((GPUConstantMem&)(*gGPUConstantMemBuffer)) +#endif +#define GPUCA_KRNL_BACKEND_CLASS GPUReconstructionCUDABackend + +#endif diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAIncludes.h b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAIncludes.h new file mode 100644 index 0000000000000..cecac16ffd628 --- /dev/null +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAIncludes.h @@ -0,0 +1,31 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUReconstructionCUDIncludes.h +/// \author David Rohr + +#ifndef O2_GPU_GPURECONSTRUCTIONCUDAINCLUDES_H +#define O2_GPU_GPURECONSTRUCTIONCUDAINCLUDES_H + +#include <cstdint> +#include <cuda_runtime.h> +#include <cuda.h> +#include <cooperative_groups.h> +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <cub/cub.cuh> +#include <cub/block/block_scan.cuh> +#include <thrust/sort.h> +#include <thrust/execution_policy.h> +#include <thrust/device_ptr.h> +#pragma GCC diagnostic pop +#include <sm_20_atomic_functions.h> + +#endif diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAInternals.h b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAInternals.h index f335a118e51ab..11a41fdb4fba7 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAInternals.h +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAInternals.h @@ -17,15 +17,22 @@ #define GPURECONSTRUCTIONCUDAINTERNALS_H #include "GPULogging.h" +#include <vector> +#include <memory> namespace GPUCA_NAMESPACE { namespace gpu { struct GPUReconstructionCUDAInternals { - CUcontext CudaContext; // Pointer to CUDA context - unsigned int cudaContextObtained = 0; // If multiple instances of GPUThreadContextCUDA are obtained, we count them and return the context only after all are destroyed - cudaStream_t Streams[GPUCA_MAX_STREAMS]; // Pointer to array of CUDA Streams + CUcontext CudaContext; // CUDA context + CUmodule rtcModule; // module for RTC compilation + std::vector<std::unique_ptr<CUfunction>> rtcFunctions; // vector of ptrs to RTC kernels + unsigned int cudaContextObtained = 0; // If multiple instances of GPUThreadContextCUDA are obtained, we count them and return the context only after all are destroyed + cudaStream_t Streams[GPUCA_MAX_STREAMS]; // Pointer to array of CUDA Streams + + template <bool multi, class T, int I = 0> + static int getRTCkernelNum(int k = -1); }; #define GPUFailedMsg(x) GPUFailedMsgA(x, __FILE__, __LINE__) @@ -68,7 +75,8 @@ class ThrustVolatileAsyncAllocator } // namespace GPUCA_NAMESPACE // Override synchronize call at end of thrust algorithm running on stream, just don't run cudaStreamSynchronize -THRUST_BEGIN_NS +namespace thrust +{ namespace cuda_cub { @@ -85,6 +93,6 @@ __host__ __device__ inline cudaError_t synchronize<thrustStreamPolicy>(thrustStr } } // namespace cuda_cub -THRUST_END_NS +} // namespace thrust #endif diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu new file mode 100644 index 0000000000000..194093439f34b --- /dev/null +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu @@ -0,0 +1,32 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUReconstructionCUDArtc.cu +/// \author David Rohr + +#include "GPUReconstructionCUDADef.h" +#include "GPUReconstructionIncludesDevice.h" + +#ifndef GPUCA_GPUCODE_DEVICE +#error RTC Preprocessing must run on device code +#endif +#ifdef GPUCA_NO_CONSTANT_MEMORY +#error CUDA RTC does not support processing without constant memory +#endif + +extern "C" { +#undef GPUCA_KRNL_REG +#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) +#define GPUCA_KRNL(x_class, x_attributes, x_arguments, x_forward) GPUCA_KRNL_WRAP(GPUCA_KRNL_LOAD_, x_class, x_attributes, x_arguments, x_forward) +#define GPUCA_KRNL_LOAD_single(x_class, x_attributes, x_arguments, x_forward) GPUCA_KRNLGPU_SINGLE(x_class, x_attributes, x_arguments, x_forward) +#define GPUCA_KRNL_LOAD_multi(x_class, x_attributes, x_arguments, x_forward) GPUCA_KRNLGPU_MULTI(x_class, x_attributes, x_arguments, x_forward) +#include "GPUReconstructionKernels.h" +#undef GPUCA_KRNL +} diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index c24925582f824..775b58d6d9198 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -13,16 +13,9 @@ set(MODULE GPUTrackingHIP) set(CMAKE_CXX_COMPILER ${hip_HIPCC_EXECUTABLE}) set(CMAKE_CXX_EXTENSIONS OFF) -string(REPLACE " -g " " " CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} ") -string(REPLACE " -g " " " CMAKE_CXX_FLAGS_${CMAKE_BUILT_TYPE} " ${CMAKE_CXX_FLAGS_${CMAKE_BUILT_TYPE}} ") - -string(REPLACE " -ggdb " " " CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} ") -string(REPLACE " -ggdb " " " CMAKE_CXX_FLAGS_${CMAKE_BUILT_TYPE} " ${CMAKE_CXX_FLAGS_${CMAKE_BUILT_TYPE}} ") - #setting flags as a global option for all HIP targets. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-gpu-rdc -Wno-invalid-command-line-argument -Wno-unused-command-line-argument -Wno-invalid-constexpr -Wno-ignored-optimization-argument -Wno-unused-private-field") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fno-gpu-rdc") if(DEFINED HIP_AMDGPUTARGET) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --amdgpu-target=${HIP_AMDGPUTARGET}") set(TMP_TARGET "(GPU Target ${HIP_AMDGPUTARGET})") endif() @@ -35,9 +28,10 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") o2_add_library( ${MODULE} SOURCES ${SRCS} - PUBLIC_LINK_LIBRARIES O2::GPUTracking O2::ITStrackingHIP hip::host hip::device hip::hipcub ROCm::rocThrust + PUBLIC_LINK_LIBRARIES O2::GPUTracking O2::ITStrackingHIP hip::host hip::device hip::hipcub roc::rocthrust PUBLIC_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/Detectors/TRD/base/src ${CMAKE_SOURCE_DIR}/Detectors/Base/src + ${CMAKE_SOURCE_DIR}/DataFormats/Reconstruction/src TARGETVARNAME targetName) target_compile_definitions( @@ -48,7 +42,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") o2_add_test(GPUsortHIP NAME test_GPUsortHIP SOURCES test/testGPUsortHIP.hip.cxx - PUBLIC_LINK_LIBRARIES O2::GPUCommon hip::host hip::device hip::hipcub ROCm::rocThrust + PUBLIC_LINK_LIBRARIES O2::GPUCommon hip::host hip::device hip::hipcub roc::rocthrust COMPONENT_NAME GPU LABELS gpu) endif() @@ -73,7 +67,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") set(ALILIBSTESTED ${ALILIBSTESTED} CACHE INTERNAL "ALILIBSTESTED") # HIP run-time and driver - target_link_libraries(Ali${MODULE} PUBLIC AliGPUTracking hip::host hip::device hip::hipcub ROCm::rocThrust) + target_link_libraries(Ali${MODULE} PUBLIC AliGPUTracking hip::host hip::device hip::hipcub roc::rocthrust) # Additional compilation flags set_target_properties(Ali${MODULE} PROPERTIES COMPILE_FLAGS "") @@ -88,7 +82,7 @@ endif() if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") add_definitions(-DGPUCA_GPULIBRARY=HIP) add_library(${MODULE} SHARED ${SRCS}) - target_link_libraries(${MODULE} GPUTracking hip::host hip::device hip::hipcub ROCm::rocThrust) + target_link_libraries(${MODULE} GPUTracking hip::host hip::device hip::hipcub roc::rocthrust) set(targetName "${MODULE}") install(TARGETS GPUTrackingHIP) endif() diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h index eb118a38b35c4..9f36d0db0fb78 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h @@ -17,9 +17,9 @@ #include "GPUReconstructionDeviceBase.h" #ifdef _WIN32 -extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_HIP(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_HIP(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #else -extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_HIP(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_HIP(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #endif namespace GPUCA_NAMESPACE @@ -34,7 +34,7 @@ class GPUReconstructionHIPBackend : public GPUReconstructionDeviceBase ~GPUReconstructionHIPBackend() override; protected: - GPUReconstructionHIPBackend(const GPUSettingsProcessing& cfg); + GPUReconstructionHIPBackend(const GPUSettingsDeviceBackend& cfg); int InitDevice_Runtime() override; int ExitDevice_Runtime() override; diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx index 0e57f80cb587b..432c6ccbf4419 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx @@ -11,8 +11,10 @@ /// \file GPUReconstructionHIP.hip.cxx /// \author David Rohr +#define __HIP_ENABLE_DEVICE_MALLOC__ 1 //Fix SWDEV-239120 #define GPUCA_GPUTYPE_VEGA #define GPUCA_UNROLL(CUDA, HIP) GPUCA_M_UNROLL_##HIP +#define GPUdic(CUDA, HIP) GPUCA_GPUdic_select_##HIP() #include <hip/hip_runtime.h> #ifdef __CUDACC__ @@ -52,14 +54,12 @@ __global__ void gGPUConstantMemBuffer_dummy(int* p) { *p = *(int*)&gGPUConstantM using namespace GPUCA_NAMESPACE::gpu; -__global__ void dummyInitKernel(void* foo) {} +__global__ void dummyInitKernel(void*) {} #if defined(HAVE_O2HEADERS) && !defined(GPUCA_NO_ITS_TRAITS) #include "ITStrackingHIP/VertexerTraitsHIP.h" #else -namespace o2 -{ -namespace its +namespace o2::its { class VertexerTraitsHIP : public VertexerTraits { @@ -67,8 +67,7 @@ class VertexerTraitsHIP : public VertexerTraits class TrackerTraitsHIP : public TrackerTraits { }; -} // namespace its -} // namespace o2 +} // namespace o2::its #endif class GPUDebugTiming @@ -122,7 +121,7 @@ GPUg() void runKernelHIP(GPUCA_CONSMEM_PTR int iSlice_internal, Args... args) */ #undef GPUCA_KRNL_REG -#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_STRIP(args)) +#define GPUCA_KRNL_REG(args) __launch_bounds__(GPUCA_M_MAX2_3(GPUCA_M_STRIP(args))) #undef GPUCA_KRNL_CUSTOM #define GPUCA_KRNL_CUSTOM(args) GPUCA_M_STRIP(args) #undef GPUCA_KRNL_BACKEND_XARGS @@ -148,14 +147,14 @@ GPUg() void runKernelHIP(GPUCA_CONSMEM_PTR int iSlice_internal, Args... args) template <> void GPUReconstructionHIPBackend::runKernelBackendInternal<GPUMemClean16, 0>(krnlSetup& _xyz, void* const& ptr, unsigned long const& size) { - GPUDebugTiming timer(mDeviceProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); + GPUDebugTiming timer(mProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); GPUFailedMsg(hipMemsetAsync(ptr, 0, size, mInternals->Streams[_xyz.x.stream])); } template <class T, int I, typename... Args> void GPUReconstructionHIPBackend::runKernelBackendInternal(krnlSetup& _xyz, const Args&... args) { - if (mDeviceProcessingSettings.deviceTimers) { + if (mProcessingSettings.deviceTimers && mProcessingSettings.debugLevel > 0) { #ifdef __CUDACC__ GPUFailedMsg(hipEventRecord((hipEvent_t)mDebugEvents->DebugStart, mInternals->Streams[x.stream])); #endif @@ -190,12 +189,12 @@ int GPUReconstructionHIPBackend::runKernelBackend(krnlSetup& _xyz, const Args&.. return 0; } -GPUReconstructionHIPBackend::GPUReconstructionHIPBackend(const GPUSettingsProcessing& cfg) : GPUReconstructionDeviceBase(cfg, sizeof(GPUReconstructionDeviceBase)) +GPUReconstructionHIPBackend::GPUReconstructionHIPBackend(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionDeviceBase(cfg, sizeof(GPUReconstructionDeviceBase)) { if (mMaster == nullptr) { mInternals = new GPUReconstructionHIPInternals; } - mProcessingSettings.deviceType = DeviceType::HIP; + mDeviceBackendSettings.deviceType = DeviceType::HIP; } GPUReconstructionHIPBackend::~GPUReconstructionHIPBackend() @@ -206,7 +205,7 @@ GPUReconstructionHIPBackend::~GPUReconstructionHIPBackend() } } -GPUReconstruction* GPUReconstruction_Create_HIP(const GPUSettingsProcessing& cfg) { return new GPUReconstructionHIP(cfg); } +GPUReconstruction* GPUReconstruction_Create_HIP(const GPUSettingsDeviceBackend& cfg) { return new GPUReconstructionHIP(cfg); } void GPUReconstructionHIPBackend::GetITSTraits(std::unique_ptr<o2::its::TrackerTraits>* trackerTraits, std::unique_ptr<o2::its::VertexerTraits>* vertexerTraits) { @@ -233,28 +232,28 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() GPUError("Error getting HIP Device Count"); return (1); } - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Available HIP devices:"); } std::vector<bool> devicesOK(count, false); for (int i = 0; i < count; i++) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Examining device %d", i); } - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Obtained current memory usage for device %d", i); } if (GPUFailedMsgI(hipGetDeviceProperties(&hipDeviceProp, i))) { continue; } - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Obtained device properties for device %d", i); } int deviceOK = true; const char* deviceFailure = ""; deviceSpeed = (double)hipDeviceProp.multiProcessorCount * (double)hipDeviceProp.clockRate * (double)hipDeviceProp.warpSize * (double)hipDeviceProp.major * (double)hipDeviceProp.major; - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUImportant("Device %s%2d: %s (Rev: %d.%d - Mem %lld)%s %s", deviceOK ? " " : "[", i, hipDeviceProp.name, hipDeviceProp.major, hipDeviceProp.minor, (long long int)hipDeviceProp.totalGlobalMem, deviceOK ? " " : " ]", deviceOK ? "" : deviceFailure); } if (!deviceOK) { @@ -265,7 +264,7 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() bestDevice = i; bestDeviceSpeed = deviceSpeed; } else { - if (mDeviceProcessingSettings.debugLevel >= 2 && mDeviceProcessingSettings.deviceNum < 0) { + if (mProcessingSettings.debugLevel >= 2 && mProcessingSettings.deviceNum < 0) { GPUInfo("Skipping: Speed %f < %f\n", deviceSpeed, bestDeviceSpeed); } } @@ -275,23 +274,22 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() return (1); } - if (mDeviceProcessingSettings.deviceNum > -1) { - if (mDeviceProcessingSettings.deviceNum >= (signed)count) { - GPUWarning("Requested device ID %d does not exist", mDeviceProcessingSettings.deviceNum); + if (mProcessingSettings.deviceNum > -1) { + if (mProcessingSettings.deviceNum >= (signed)count) { + GPUWarning("Requested device ID %d does not exist", mProcessingSettings.deviceNum); return (1); - } else if (!devicesOK[mDeviceProcessingSettings.deviceNum]) { - GPUWarning("Unsupported device requested (%d)", mDeviceProcessingSettings.deviceNum); + } else if (!devicesOK[mProcessingSettings.deviceNum]) { + GPUWarning("Unsupported device requested (%d)", mProcessingSettings.deviceNum); return (1); } else { - bestDevice = mDeviceProcessingSettings.deviceNum; + bestDevice = mProcessingSettings.deviceNum; } } mDeviceId = bestDevice; GPUFailedMsgI(hipGetDeviceProperties(&hipDeviceProp, mDeviceId)); - hipDeviceProp.totalConstMem = 65536; // TODO: Remove workaround, fixes incorrectly reported HIP constant memory - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Using HIP Device %s with Properties:", hipDeviceProp.name); GPUInfo("\ttotalGlobalMem = %lld", (unsigned long long int)hipDeviceProp.totalGlobalMem); GPUInfo("\tsharedMemPerBlock = %lld", (unsigned long long int)hipDeviceProp.sharedMemPerBlock); @@ -329,6 +327,10 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() GPUError("Could not set HIP Device!"); return (1); } + if (GPUFailedMsgI(hipSetDeviceFlags(hipDeviceScheduleBlockingSync))) { + GPUError("Could not set HIP Device!"); + return (1); + } /*if (GPUFailedMsgI(hipDeviceSetLimit(hipLimitStackSize, GPUCA_GPU_STACK_SIZE))) { @@ -347,7 +349,7 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() GPUFailedMsgI(hipDeviceReset()); return (1); } - if (mDeviceProcessingSettings.debugLevel >= 1) { + if (mProcessingSettings.debugLevel >= 1) { GPUInfo("Memory ptrs: GPU (%lld bytes): %p - Host (%lld bytes): %p", (long long int)mDeviceMemorySize, mDeviceMemoryBase, (long long int)mHostMemorySize, mHostMemoryBase); memset(mHostMemoryBase, 0, mHostMemorySize); if (GPUFailedMsgI(hipMemset(mDeviceMemoryBase, 0xDD, mDeviceMemorySize))) { @@ -397,7 +399,7 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() for (unsigned int i = 0; i < mEvents.size(); i++) { hipEvent_t* events = (hipEvent_t*)mEvents[i].data(); for (unsigned int j = 0; j < mEvents[i].size(); j++) { - if (GPUFailedMsgI(hipEventCreate(&events[j]))) { + if (GPUFailedMsgI(hipEventCreateWithFlags(&events[j], hipEventBlockingSync))) { GPUError("Error creating event"); GPUFailedMsgI(hipDeviceReset()); return 1; @@ -447,7 +449,7 @@ int GPUReconstructionHIPBackend::ExitDevice_Runtime() size_t GPUReconstructionHIPBackend::GPUMemCpy(void* dst, const void* src, size_t size, int stream, int toGPU, deviceEvent* ev, deviceEvent* evList, int nEvents) { - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { stream = -1; } if (stream == -1) { @@ -471,12 +473,12 @@ size_t GPUReconstructionHIPBackend::GPUMemCpy(void* dst, const void* src, size_t size_t GPUReconstructionHIPBackend::TransferMemoryInternal(GPUMemoryResource* res, int stream, deviceEvent* ev, deviceEvent* evList, int nEvents, bool toGPU, const void* src, void* dst) { if (!(res->Type() & GPUMemoryResource::MEMORY_GPU)) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Skipped transfer of non-GPU memory resource: %s", res->Name()); } return 0; } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3 && (strcmp(res->Name(), "ErrorCodes") || mProcessingSettings.debugLevel >= 4)) { GPUInfo("Copying to %s: %s - %lld bytes", toGPU ? "GPU" : "Host", res->Name(), (long long int)res->Size()); } return GPUMemCpy(dst, src, res->Size(), stream, toGPU, ev, evList, nEvents); @@ -549,14 +551,14 @@ int GPUReconstructionHIPBackend::GPUDebug(const char* state, int stream) GPUError("HIP Error %s while running kernel (%s) (Stream %d)", hipGetErrorString(cuErr), state, stream); return (1); } - if (mDeviceProcessingSettings.debugLevel == 0) { + if (mProcessingSettings.debugLevel <= 0) { return (0); } if (GPUFailedMsgI(hipDeviceSynchronize())) { GPUError("HIP Error while synchronizing (%s) (Stream %d)", state, stream); return (1); } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("GPU Sync Done"); } return (0); @@ -581,7 +583,7 @@ void* GPUReconstructionHIPBackend::getGPUPointer(void* ptr) void GPUReconstructionHIPBackend::PrintKernelOccupancies() { - unsigned int maxBlocks, threads, suggestedBlocks; + int maxBlocks, threads, suggestedBlocks; #define GPUCA_KRNL(x_class, x_attributes, x_arguments, x_forward) GPUCA_KRNL_WRAP(GPUCA_KRNL_LOAD_, x_class, x_attributes, x_arguments, x_forward) #define GPUCA_KRNL_LOAD_single(x_class, x_attributes, x_arguments, x_forward) \ GPUFailedMsg(hipOccupancyMaxPotentialBlockSize(&suggestedBlocks, &threads, GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class)), 0, 0)); \ diff --git a/GPU/GPUTracking/Base/opencl-common/CMakeLists.txt b/GPU/GPUTracking/Base/opencl-common/CMakeLists.txt index 6e81224ecb7d1..f2f39ae5f040e 100644 --- a/GPU/GPUTracking/Base/opencl-common/CMakeLists.txt +++ b/GPU/GPUTracking/Base/opencl-common/CMakeLists.txt @@ -55,6 +55,6 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") add_definitions(-DGPUCA_GPULIBRARY=OCL) add_library(${MODULE} SHARED ${SRCS}) target_link_libraries(${MODULE} GPUTracking OpenCL) - target_include_directories(${MODULE} PUBLIC foo ${CMAKE_CURRENT_LIST_DIR}) + target_include_directories(${MODULE} PUBLIC ${CMAKE_CURRENT_LIST_DIR}) install(TARGETS ${MODULE}) endif() diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx index 2f3136190921f..69fd3d5bba07e 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx @@ -36,12 +36,12 @@ using namespace GPUCA_NAMESPACE::gpu; #include "GPUReconstructionKernels.h" #undef GPUCA_KRNL -GPUReconstructionOCL::GPUReconstructionOCL(const GPUSettingsProcessing& cfg) : GPUReconstructionDeviceBase(cfg, sizeof(GPUReconstructionDeviceBase)) +GPUReconstructionOCL::GPUReconstructionOCL(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionDeviceBase(cfg, sizeof(GPUReconstructionDeviceBase)) { if (mMaster == nullptr) { mInternals = new GPUReconstructionOCLInternals; } - mProcessingSettings.deviceType = DeviceType::OCL; + mDeviceBackendSettings.deviceType = DeviceType::OCL; } GPUReconstructionOCL::~GPUReconstructionOCL() @@ -68,7 +68,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() if (num_platforms == 0) { quit("No OpenCL Platform found"); } - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("%d OpenCL Platforms found", num_platforms); } @@ -79,11 +79,11 @@ int GPUReconstructionOCL::InitDevice_Runtime() } bool found = false; - if (mDeviceProcessingSettings.platformNum >= 0) { - if (mDeviceProcessingSettings.platformNum >= (int)num_platforms) { + if (mProcessingSettings.platformNum >= 0) { + if (mProcessingSettings.platformNum >= (int)num_platforms) { quit("Invalid platform specified"); } - mInternals->platform = mInternals->platforms[mDeviceProcessingSettings.platformNum]; + mInternals->platform = mInternals->platforms[mProcessingSettings.platformNum]; found = true; } else { for (unsigned int i_platform = 0; i_platform < num_platforms; i_platform++) { @@ -92,7 +92,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() clGetPlatformInfo(mInternals->platforms[i_platform], CL_PLATFORM_VERSION, sizeof(platform_version), platform_version, nullptr); clGetPlatformInfo(mInternals->platforms[i_platform], CL_PLATFORM_NAME, sizeof(platform_name), platform_name, nullptr); clGetPlatformInfo(mInternals->platforms[i_platform], CL_PLATFORM_VENDOR, sizeof(platform_vendor), platform_vendor, nullptr); - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Available Platform %d: (%s %s) %s %s", i_platform, platform_profile, platform_version, platform_vendor, platform_name); } if (CheckPlatform(i_platform)) { @@ -121,12 +121,12 @@ int GPUReconstructionOCL::InitDevice_Runtime() cl_device_type device_type; cl_uint freq, shaders; - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Available OPENCL devices:"); } std::vector<bool> devicesOK(count, false); for (unsigned int i = 0; i < count; i++) { - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("Examining device %d", i); } cl_uint nbits; @@ -141,7 +141,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() clGetDeviceInfo(mInternals->devices[i], CL_DEVICE_ENDIAN_LITTLE, sizeof(endian), &endian, nullptr); int deviceOK = true; const char* deviceFailure = ""; - if (mDeviceProcessingSettings.gpuDeviceOnly && ((device_type & CL_DEVICE_TYPE_CPU) || !(device_type & CL_DEVICE_TYPE_GPU))) { + if (mProcessingSettings.gpuDeviceOnly && ((device_type & CL_DEVICE_TYPE_CPU) || !(device_type & CL_DEVICE_TYPE_GPU))) { deviceOK = false; deviceFailure = "No GPU device"; } @@ -155,7 +155,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() } double bestDeviceSpeed = -1, deviceSpeed = (double)freq * (double)shaders; - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUImportant("Device %s%2d: %s %s (Frequency %d, Shaders %d, %d bit) (Speed Value: %lld)%s %s", deviceOK ? " " : "[", i, device_vendor, device_name, (int)freq, (int)shaders, (int)nbits, (long long int)deviceSpeed, deviceOK ? " " : " ]", deviceOK ? "" : deviceFailure); } if (!deviceOK) { @@ -166,7 +166,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() bestDevice = i; bestDeviceSpeed = deviceSpeed; } else { - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Skipping: Speed %f < %f", deviceSpeed, bestDeviceSpeed); } } @@ -175,13 +175,13 @@ int GPUReconstructionOCL::InitDevice_Runtime() quit("No %sOPENCL Device available, aborting OPENCL Initialisation", count ? "appropriate " : ""); } - if (mDeviceProcessingSettings.deviceNum > -1) { - if (mDeviceProcessingSettings.deviceNum >= (signed)count) { - quit("Requested device ID %d does not exist", mDeviceProcessingSettings.deviceNum); - } else if (!devicesOK[mDeviceProcessingSettings.deviceNum]) { - quit("Unsupported device requested (%d)", mDeviceProcessingSettings.deviceNum); + if (mProcessingSettings.deviceNum > -1) { + if (mProcessingSettings.deviceNum >= (signed)count) { + quit("Requested device ID %d does not exist", mProcessingSettings.deviceNum); + } else if (!devicesOK[mProcessingSettings.deviceNum]) { + quit("Unsupported device requested (%d)", mProcessingSettings.deviceNum); } else { - bestDevice = mDeviceProcessingSettings.deviceNum; + bestDevice = mProcessingSettings.deviceNum; } } mInternals->device = mInternals->devices[bestDevice]; @@ -200,7 +200,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() clGetDeviceInfo(mInternals->device, CL_DEVICE_VERSION, sizeof(deviceVersion) - 1, deviceVersion, nullptr); clGetDeviceInfo(mInternals->device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(maxWorkGroup), &maxWorkGroup, nullptr); clGetDeviceInfo(mInternals->device, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(maxWorkItems), maxWorkItems, nullptr); - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Using OpenCL device %d: %s %s with properties:", bestDevice, device_vendor, device_name); GPUInfo("\tVersion = %s", deviceVersion); GPUInfo("\tFrequency = %d", (int)freq); @@ -233,7 +233,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() return 1; } - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("OpenCL program and kernels loaded successfully"); } @@ -253,7 +253,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() for (int i = 0; i < mNStreams; i++) { #ifdef CL_VERSION_2_0 cl_queue_properties prop = 0; - if (mDeviceProcessingSettings.deviceTimers) { + if (mProcessingSettings.deviceTimers) { prop |= CL_QUEUE_PROFILING_ENABLE; } mInternals->command_queue[i] = clCreateCommandQueueWithProperties(mInternals->context, mInternals->device, &prop, &ocl_error); @@ -300,7 +300,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() quit("Error obtaining device memory ptr"); } - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("Mapping hostmemory"); } mHostMemoryBase = clEnqueueMapBuffer(mInternals->command_queue[0], mInternals->mem_host, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, mHostMemorySize, 0, nullptr, nullptr, &ocl_error); @@ -311,7 +311,7 @@ int GPUReconstructionOCL::InitDevice_Runtime() mDeviceMemoryBase = ((void**)mHostMemoryBase)[0]; mDeviceConstantMem = (GPUConstantMem*)((void**)mHostMemoryBase)[1]; - if (mDeviceProcessingSettings.debugLevel >= 1) { + if (mProcessingSettings.debugLevel >= 1) { GPUInfo("Memory ptrs: GPU (%lld bytes): %p - Host (%lld bytes): %p", (long long int)mDeviceMemorySize, mDeviceMemoryBase, (long long int)mHostMemorySize, mHostMemoryBase); memset(mHostMemoryBase, 0xDD, mHostMemorySize); } @@ -368,7 +368,7 @@ size_t GPUReconstructionOCL::GPUMemCpy(void* dst, const void* src, size_t size, if (evList == nullptr) { nEvents = 0; } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { stream = -1; } if (stream == -1) { @@ -387,12 +387,12 @@ size_t GPUReconstructionOCL::GPUMemCpy(void* dst, const void* src, size_t size, size_t GPUReconstructionOCL::TransferMemoryInternal(GPUMemoryResource* res, int stream, deviceEvent* ev, deviceEvent* evList, int nEvents, bool toGPU, const void* src, void* dst) { if (!(res->Type() & GPUMemoryResource::MEMORY_GPU)) { - if (mDeviceProcessingSettings.debugLevel >= 4) { + if (mProcessingSettings.debugLevel >= 4) { GPUInfo("Skipped transfer of non-GPU memory resource: %s", res->Name()); } return 0; } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3 && (strcmp(res->Name(), "ErrorCodes") || mProcessingSettings.debugLevel >= 4)) { GPUInfo("Copying to %s: %s - %lld bytes", toGPU ? "GPU" : "Host", res->Name(), (long long int)res->Size()); } return GPUMemCpy(dst, src, res->Size(), stream, toGPU, ev, evList, nEvents); @@ -413,9 +413,9 @@ void GPUReconstructionOCL::RecordMarker(deviceEvent* ev, int stream) { GPUFailed int GPUReconstructionOCL::DoStuckProtection(int stream, void* event) { - if (mDeviceProcessingSettings.stuckProtection) { + if (mProcessingSettings.stuckProtection) { cl_int tmp = 0; - for (int i = 0; i <= mDeviceProcessingSettings.stuckProtection / 50; i++) { + for (int i = 0; i <= mProcessingSettings.stuckProtection / 50; i++) { usleep(50); clGetEventInfo(*(cl_event*)event, CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(tmp), &tmp, nullptr); if (tmp == CL_COMPLETE) { @@ -465,7 +465,7 @@ bool GPUReconstructionOCL::IsEventDone(deviceEvent* evList, int nEvents) int GPUReconstructionOCL::GPUDebug(const char* state, int stream) { // Wait for OPENCL-Kernel to finish and check for OPENCL errors afterwards, in case of debugmode - if (mDeviceProcessingSettings.debugLevel == 0) { + if (mProcessingSettings.debugLevel <= 0) { return (0); } for (int i = 0; i < mNStreams; i++) { @@ -473,7 +473,7 @@ int GPUReconstructionOCL::GPUDebug(const char* state, int stream) GPUError("OpenCL Error while synchronizing (%s) (Stream %d/%d)", state, stream, i); } } - if (mDeviceProcessingSettings.debugLevel >= 3) { + if (mProcessingSettings.debugLevel >= 3) { GPUInfo("GPU Sync Done"); } return (0); diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h index 1a80dd3772e22..46e50e8547357 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h @@ -17,9 +17,9 @@ #include "GPUReconstructionDeviceBase.h" #ifdef _WIN32 -extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCLconst GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCLconst GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #else -extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #endif namespace GPUCA_NAMESPACE::gpu @@ -30,7 +30,7 @@ class GPUReconstructionOCL : public GPUReconstructionDeviceBase { public: ~GPUReconstructionOCL() override; - GPUReconstructionOCL(const GPUSettingsProcessing& cfg); + GPUReconstructionOCL(const GPUSettingsDeviceBackend& cfg); protected: int InitDevice_Runtime() override; diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCLInternals.h b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCLInternals.h index 5078b3bcbb5a5..10d397ed9fe0a 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCLInternals.h +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCLInternals.h @@ -16,6 +16,7 @@ #ifndef GPUTPCGPUTRACKEROPENCLINTERNALS_H #define GPUTPCGPUTRACKEROPENCLINTERNALS_H +#define CL_TARGET_OPENCL_VERSION 220 #include <CL/opencl.h> #include <CL/cl_ext.h> #include <vector> @@ -201,14 +202,14 @@ int GPUReconstructionOCL::runKernelBackendCommon(krnlSetup& _xyz, K& k, const Ar cl_event ev; cl_event* evr; bool tmpEvent = false; - if (z.ev == nullptr && mDeviceProcessingSettings.deviceTimers) { + if (z.ev == nullptr && mProcessingSettings.deviceTimers && mProcessingSettings.debugLevel > 0) { evr = &ev; tmpEvent = true; } else { evr = (cl_event*)z.ev; } int retVal = clExecuteKernelA(mInternals->command_queue[x.stream], k, x.nThreads, x.nThreads * x.nBlocks, evr, (cl_event*)z.evList, z.nEvents); - if (mDeviceProcessingSettings.deviceTimers) { + if (mProcessingSettings.deviceTimers && mProcessingSettings.debugLevel > 0) { cl_ulong time_start, time_end; GPUFailedMsg(clWaitForEvents(1, evr)); GPUFailedMsg(clGetEventProfilingInfo(*evr, CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, nullptr)); diff --git a/GPU/GPUTracking/Base/opencl/CMakeLists.txt b/GPU/GPUTracking/Base/opencl/CMakeLists.txt index 781cf2643b3b9..342aa4d3527ce 100644 --- a/GPU/GPUTracking/Base/opencl/CMakeLists.txt +++ b/GPU/GPUTracking/Base/opencl/CMakeLists.txt @@ -9,6 +9,7 @@ # submit itself to any jurisdiction. set(MODULE GPUTrackingOCL) +enable_language(ASM) # AMD APP SDK required for OpenCL tracker as it's using specific extensions # (currently) not provided by other vendors @@ -57,6 +58,7 @@ add_custom_command( -I${GPUDIR}/Global -I${GPUDIR}/SliceTracker -I${GPUDIR}/TRDTracking + -I${GPUDIR}/Standalone -DGPUCA_GPULIBRARY=OCL1 -x clc++ MAIN_DEPENDENCY ${CL_SRC} @@ -77,7 +79,7 @@ set_source_files_properties( ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.S PROPERTIES LANGUAGE - CXX + ASM OBJECT_DEPENDS "${CL_BIN};${GPUDIR}/Standalone/makefiles/include.S") diff --git a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.cxx b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.cxx index db8d72fb5391e..051bdcfeeb7da 100644 --- a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.cxx +++ b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.cxx @@ -28,9 +28,9 @@ using namespace GPUCA_NAMESPACE::gpu; #include "../makefiles/opencl_obtain_program.h" extern "C" char _makefile_opencl_program_Base_opencl_common_GPUReconstructionOCL_cl[]; -GPUReconstruction* GPUReconstruction_Create_OCL(const GPUSettingsProcessing& cfg) { return new GPUReconstructionOCL1(cfg); } +GPUReconstruction* GPUReconstruction_Create_OCL(const GPUSettingsDeviceBackend& cfg) { return new GPUReconstructionOCL1(cfg); } -GPUReconstructionOCL1Backend::GPUReconstructionOCL1Backend(const GPUSettingsProcessing& cfg) : GPUReconstructionOCL(cfg) +GPUReconstructionOCL1Backend::GPUReconstructionOCL1Backend(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionOCL(cfg) { } @@ -91,7 +91,7 @@ bool GPUReconstructionOCL1Backend::CheckPlatform(unsigned int i) float ver = 0; sscanf(platform_version, "OpenCL 2.0 AMD-APP (%f)", &ver); if (ver < 2000.f) { - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("AMD APP OpenCL Platform found"); } return true; diff --git a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.h b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.h index d143f51dfecff..28131147f8463 100644 --- a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.h +++ b/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1.h @@ -17,9 +17,9 @@ #include "GPUReconstructionOCL.h" #ifdef _WIN32 -extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #else -extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #endif namespace GPUCA_NAMESPACE::gpu @@ -32,7 +32,7 @@ class GPUReconstructionOCL1Backend : public GPUReconstructionOCL ~GPUReconstructionOCL1Backend() override = default; protected: - GPUReconstructionOCL1Backend(const GPUSettingsProcessing& cfg); + GPUReconstructionOCL1Backend(const GPUSettingsDeviceBackend& cfg); template <class T, int I = 0, typename... Args> int runKernelBackend(krnlSetup& _xyz, const Args&... args); diff --git a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt index 2a97b94f6e5ee..7e9593ff45cbe 100644 --- a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt +++ b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt @@ -9,6 +9,7 @@ # submit itself to any jurisdiction. set(MODULE GPUTrackingOCL2) +enable_language(ASM) if(DEFINED OCL2_GPUTARGET) set(TMP_TARGET "(AMDGPU Target ${OCL2_GPUTARGET})") @@ -25,10 +26,11 @@ set(CL_SRC ${GPUDIR}/Base/opencl-common/GPUReconstructionOCL.cl) set(CL_BIN ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCL2Code) set(OCL_FLAGS -cl-denorms-are-zero -cl-mad-enable -cl-no-signed-zeros -ferror-limit=1000 -Xclang -finclude-default-header -Dcl_clang_storage_class_specifiers -Wno-invalid-constexpr -Wno-unused-command-line-argument -cl-std=clc++) -set(OCL_DEFINCL "-D$<JOIN:$<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>,$<SEMICOLON>-D>" +set(OCL_DEFINECL "-D$<JOIN:$<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>,$<SEMICOLON>-D>" "-I$<JOIN:$<TARGET_PROPERTY:O2::GPUTracking,INCLUDE_DIRECTORIES>,$<SEMICOLON>-I>" -I${CMAKE_SOURCE_DIR}/Detectors/TRD/base/src -I${CMAKE_SOURCE_DIR}/Detectors/Base/src + -I${CMAKE_SOURCE_DIR}/DataFormats/Reconstruction/src -I${CMAKE_SOURCE_DIR}/Detectors/ITSMFT/ITS/tracking/cuda/include -DGPUCA_GPULIBRARY=OCL2 -D__OPENCLCPP__ @@ -47,7 +49,7 @@ if(OPENCL2_ENABLED_AMD) # BUILD OpenCL2 binaries for AMD target OUTPUT ${CL_BIN}.amd COMMAND ${CLANG_OCL} ${OCL_FLAGS} - ${OCL_DEFINCL} + ${OCL_DEFINECL} -mcpu=${OCL2_GPUTARGET} -o ${CL_BIN}.amd ${CL_SRC} MAIN_DEPENDENCY ${CL_SRC} @@ -68,7 +70,7 @@ if(OPENCL2_ENABLED_AMD) # BUILD OpenCL2 binaries for AMD target ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.amd.S PROPERTIES LANGUAGE - CXX + ASM OBJECT_DEPENDS "${CL_BIN}.amd;${GPUDIR}/Standalone/makefiles/include.S") @@ -82,7 +84,7 @@ if(OPENCL2_ENABLED_SPIRV) # BUILD OpenCL2 intermediate code for SPIR-V target COMMAND clang -emit-llvm --target=spir64-unknown-unknown ${OCL_FLAGS} - ${OCL_DEFINCL} + ${OCL_DEFINECL} -o ${CL_BIN}.bc -c ${CL_SRC} MAIN_DEPENDENCY ${CL_SRC} IMPLICIT_DEPENDS CXX ${CL_SRC} @@ -108,7 +110,7 @@ if(OPENCL2_ENABLED_SPIRV) # BUILD OpenCL2 intermediate code for SPIR-V target ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.spirv.S PROPERTIES LANGUAGE - CXX + ASM OBJECT_DEPENDS "${CL_BIN}.spirv;${GPUDIR}/Standalone/makefiles/include.S") @@ -116,11 +118,11 @@ if(OPENCL2_ENABLED_SPIRV) # BUILD OpenCL2 intermediate code for SPIR-V target endif() if(OPENCL2_ENABLED) # BUILD OpenCL2 source code for runtime compilation target - # executes clang to create llvm IL code + # executes clang to preprocess add_custom_command( OUTPUT ${CL_BIN}.src COMMAND clang - ${OCL_DEFINCL} + ${OCL_DEFINECL} -E ${CL_SRC} > ${CL_BIN}.src MAIN_DEPENDENCY ${CL_SRC} IMPLICIT_DEPENDS CXX ${CL_SRC} @@ -140,7 +142,7 @@ if(OPENCL2_ENABLED) # BUILD OpenCL2 source code for runtime compilation target ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.src.S PROPERTIES LANGUAGE - CXX + ASM OBJECT_DEPENDS "${CL_BIN}.src;${GPUDIR}/Standalone/makefiles/include.S") diff --git a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx b/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx index c46a6beeaa18b..700dfd9e3942a 100644 --- a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx +++ b/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx @@ -36,9 +36,9 @@ extern "C" unsigned int _makefile_opencl_program_Base_opencl_GPUReconstructionOC extern "C" char _makefile_opencl_program_Base_opencl_GPUReconstructionOCL2_cl_src[]; extern "C" unsigned int _makefile_opencl_program_Base_opencl_GPUReconstructionOCL2_cl_src_size; -GPUReconstruction* GPUReconstruction_Create_OCL2(const GPUSettingsProcessing& cfg) { return new GPUReconstructionOCL2(cfg); } +GPUReconstruction* GPUReconstruction_Create_OCL2(const GPUSettingsDeviceBackend& cfg) { return new GPUReconstructionOCL2(cfg); } -GPUReconstructionOCL2Backend::GPUReconstructionOCL2Backend(const GPUSettingsProcessing& cfg) : GPUReconstructionOCL(cfg) +GPUReconstructionOCL2Backend::GPUReconstructionOCL2Backend(const GPUSettingsDeviceBackend& cfg) : GPUReconstructionOCL(cfg) { } @@ -134,7 +134,7 @@ bool GPUReconstructionOCL2Backend::CheckPlatform(unsigned int i) float ver1 = 0; sscanf(platform_version, "OpenCL %f", &ver1); if (ver1 >= 2.2f) { - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("OpenCL 2.2 capable platform found"); } return true; @@ -147,7 +147,7 @@ bool GPUReconstructionOCL2Backend::CheckPlatform(unsigned int i) sscanf(pos, "(%f)", &ver2); } if ((ver1 >= 2.f && ver2 >= 2000.f) || ver1 >= 2.1f) { - if (mDeviceProcessingSettings.debugLevel >= 2) { + if (mProcessingSettings.debugLevel >= 2) { GPUInfo("AMD ROCm OpenCL Platform found"); } return true; diff --git a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.h b/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.h index 859e76adeef53..dbaa05f290d6e 100644 --- a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.h +++ b/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.h @@ -17,9 +17,9 @@ #include "GPUReconstructionOCL.h" #ifdef _WIN32 -extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL2(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" __declspec(dllexport) GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL2(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #else -extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL2(const GPUCA_NAMESPACE::gpu::GPUSettingsProcessing& cfg); +extern "C" GPUCA_NAMESPACE::gpu::GPUReconstruction* GPUReconstruction_Create_OCL2(const GPUCA_NAMESPACE::gpu::GPUSettingsDeviceBackend& cfg); #endif namespace GPUCA_NAMESPACE::gpu @@ -32,7 +32,7 @@ class GPUReconstructionOCL2Backend : public GPUReconstructionOCL ~GPUReconstructionOCL2Backend() override = default; protected: - GPUReconstructionOCL2Backend(const GPUSettingsProcessing& cfg); + GPUReconstructionOCL2Backend(const GPUSettingsDeviceBackend& cfg); template <class T, int I = 0, typename... Args> int runKernelBackend(krnlSetup& _xyz, const Args&... args); diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 54c39c206bd50..7f340f56467e2 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -21,7 +21,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") else() if(OPENGL_FOUND AND GLFW_FOUND - AND (ALIGPU_BUILD_TYPE STREQUAL "O2" OR GLEW_FOUND) + AND ((ALIGPU_BUILD_TYPE STREQUAL "O2" AND TARGET AliceO2::DebugGUI) OR GLEW_FOUND) AND OPENGL_GLU_FOUND AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(GPUCA_EVENT_DISPLAY ON) @@ -109,9 +109,8 @@ set(HDRS_INSTALL TRDTracking/GPUTRDGeometry.h TRDTracking/GPUTRDTrackerDebug.h TRDTracking/GPUTRDInterfaces.h - Standalone/display/GPUDisplayConfig.h - Standalone/qa/GPUQAConfig.h Base/GPUConstantMem.h + Base/GPUParamRTC.h Base/GPUReconstructionKernels.h Base/GPUReconstructionIncludesITS.h Base/GPUReconstructionHelpers.h @@ -129,12 +128,17 @@ set(HDRS_INSTALL Base/GPUParam.inc Merger/GPUTPCGMMergerTypes.h Global/GPUErrorCodes.h + Standalone/qconfigoptions.h + Standalone/qa/GPUQAHelper.h + Base/GPUSettingsList.h + DataCompression/GPUTPCClusterRejection.h ) # Sources only for O2 if(ALIGPU_BUILD_TYPE STREQUAL "O2") - set(SRCS ${SRCS} Interface/GPUO2Interface.cxx) - set(HDRS_CINT_O2 ${HDRS_CINT_O2} Interface/GPUO2Interface.h dEdx/TPCdEdxCalibrationSplines.h) + set(SRCS ${SRCS} Interface/GPUO2Interface.cxx Interface/GPUO2InterfaceRefit.cxx Interface/GPUO2InterfaceQA.cxx Interface/GPUO2InterfaceConfigurableParam.cxx) + set(HDRS_CINT_O2 ${HDRS_CINT_O2} Interface/GPUO2Interface.h Interface/GPUO2InterfaceRefit.h Interface/GPUO2InterfaceQA.h Interface/GPUO2InterfaceConfigurableParam.h dEdx/TPCdEdxCalibrationSplines.h) + set(HDRS_CINT_O2_ADDITIONAL Base/GPUSettings.h Base/GPUSettingsList.h TRDTracking/GPUTRDTrack.h) # Manual dependencies for ROOT dictionary generation endif() # Sources for O2 and for Standalone if requested in config file @@ -159,6 +163,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2" OR CONFIG_O2_EXTENSIONS) TPCClusterFinder/GPUTPCClusterFinder.cxx TPCClusterFinder/ClusterAccumulator.cxx TPCClusterFinder/MCLabelAccumulator.cxx + TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx TPCClusterFinder/GPUTPCCFStreamCompaction.cxx TPCClusterFinder/GPUTPCCFChargeMapFiller.cxx TPCClusterFinder/GPUTPCCFPeakFinder.cxx @@ -168,7 +173,10 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2" OR CONFIG_O2_EXTENSIONS) TPCClusterFinder/GPUTPCCFMCLabelFlattener.cxx TPCClusterFinder/GPUTPCCFDecodeZS.cxx TPCClusterFinder/GPUTPCCFGather.cxx - dEdx/TPCdEdxCalibrationSplines.cxx) + TPCClusterFinder/TPCPadGainCalib.cxx + dEdx/TPCdEdxCalibrationSplines.cxx + Refit/GPUTrackingRefit.cxx + Refit/GPUTrackingRefitKernel.cxx) set(SRCS_NO_H ${SRCS_NO_H} TPCClusterFinder/GPUTPCClusterFinderDump.cxx) @@ -229,10 +237,6 @@ else() set(HDRS_INSTALL ${HDRS_INSTALL} Standalone/qa/GPUQA.h) endif() -if(GPUCA_EVENT_DISPLAY OR GPUCA_QA) - set(HDRS_INSTALL ${HDRS_INSTALL} Standalone/qconfigoptions.h) -endif() - # Update HDR variables with files derrived from SRC variables string(REPLACE ".cxx" ".h" HDRS_CINT_ALIROOT "${SRCS}") string(REPLACE ".cxx" ".h" HDRS_TMP "${SRCS_NO_CINT}") @@ -257,6 +261,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") O2::TPCFastTransformation O2::DetectorsRaw ${DEBUGGUI_TARGET} + O2::Steer PUBLIC_INCLUDE_DIRECTORIES SliceTracker Base TPCConvert @@ -270,6 +275,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") HLTHeaders Interface Merger + Refit DataCompression TPCClusterFinder TARGETVARNAME targetName @@ -280,7 +286,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") PRIVATE $<TARGET_PROPERTY:O2::Framework,INTERFACE_INCLUDE_DIRECTORIES>) o2_target_root_dictionary(${MODULE} - HEADERS ${HDRS_CINT_O2} + HEADERS ${HDRS_CINT_O2} ${HDRS_CINT_O2_ADDITIONAL} LINKDEF GPUTrackingLinkDef_O2.h) target_compile_definitions(${targetName} PRIVATE GPUCA_O2_LIB @@ -324,6 +330,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") ${CMAKE_SOURCE_DIR}/GPU/Common ${CMAKE_SOURCE_DIR}/GPU/Utils ${CMAKE_SOURCE_DIR}/GPU/GPUTracking + ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Standalone ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Base ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/SliceTracker ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Merger @@ -418,8 +425,8 @@ endif() if(OpenMP_CXX_FOUND) message(STATUS "GPU: Using OpenMP: ${OpenMP_CXX_SPEC_DATE}") - target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) # Must be private, depending libraries might be compiled by compiler not understanding -fopenmp + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) endif() diff --git a/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h b/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h new file mode 100644 index 0000000000000..890e15b7df61d --- /dev/null +++ b/GPU/GPUTracking/DataCompression/GPUTPCClusterRejection.h @@ -0,0 +1,72 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTPCClusterRejection.h +/// \author David Rohr + +#ifndef GPUTPCCLUSTERREJECTION_H +#define GPUTPCCLUSTERREJECTION_H + +#include "GPUTPCGMMergerTypes.h" + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ +struct GPUTPCClusterRejection { + template <bool C, class T = void, class S = void> + static constexpr inline bool GetProtectionStatus(int attach, bool& physics, bool& protect, T* counts = nullptr, S* mev200 = nullptr) + { + (void)counts; // Avoid incorrect -Wunused-but-set-parameter warning + (void)mev200; + if (attach == 0) { + return false; + } else if ((attach & gputpcgmmergertypes::attachGoodLeg) == 0) { + if constexpr (C) { + counts->nLoopers++; + } + return true; + } else if (attach & gputpcgmmergertypes::attachHighIncl) { + if constexpr (C) { + counts->nHighIncl++; + } + return true; + } else if (attach & gputpcgmmergertypes::attachTube) { + protect = true; + if constexpr (C) { + if (*mev200) { + counts->nTube200++; + } else { + counts->nTube++; + } + } + return false; + } else if ((attach & gputpcgmmergertypes::attachGood) == 0) { + protect = true; + if constexpr (C) { + counts->nRejected++; + } + return false; + } else { + physics = true; + return false; + } + } + + static constexpr inline bool GetIsRejected(int attach) + { + bool physics = false, protect = false; + return GetProtectionStatus<false>(attach, physics, protect); + } +}; +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.cxx b/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.cxx index a0f60ac46b608..e2b85cd4e1dfd 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.cxx @@ -224,7 +224,7 @@ void GPUTPCClusterStatistics::Finish() GPUInfo("Combined Sigma: %6.4f --> %6.4f (%6.4f%%)", eSigma, eSigmaCombined, eSigma > 1e-3 ? (100. * (eSigma - eSigmaCombined) / eSigma) : 0.f); GPUInfo("Combined Q: %6.4f --> %6.4f (%6.4f%%)", eQ, eQCombined, eQ > 1e-3 ? (100. * (eQ - eQCombined) / eQ) : 0.f); - printf("\nConbined Entropy: %7.4f (Size %'13.0f, %'lld cluster)\nCombined Huffman: %7.4f (Size %'13.0f, %f%%)\n\n", mEntropy / mNTotalClusters, mEntropy, (long long int)mNTotalClusters, mHuffman / mNTotalClusters, mHuffman, 100. * (mHuffman - mEntropy) / mHuffman); + printf("\nCombined Entropy: %7.4f (Size %'13.0f, %'lld cluster)\nCombined Huffman: %7.4f (Size %'13.0f, %f%%)\n\n", mEntropy / mNTotalClusters, mEntropy, (long long int)mNTotalClusters, mHuffman / mNTotalClusters, mHuffman, 100. * (mHuffman - mEntropy) / mHuffman); } float GPUTPCClusterStatistics::Analyze(std::vector<int>& p, const char* name, bool count) diff --git a/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.h b/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.h index 9605ff508b04f..1c1279d529b3c 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCClusterStatistics.h @@ -18,17 +18,12 @@ #include "TPCClusterDecompressor.h" #include <vector> -namespace o2 -{ -namespace tpc +namespace o2::tpc { struct ClusterNativeAccess; -} -} // namespace o2 +} // namespace o2::tpc -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCClusterStatistics { @@ -93,7 +88,6 @@ class GPUTPCClusterStatistics size_t mNTotalClusters = 0; #endif }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx index cf96cf8f15c28..02f141fe7baee 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx @@ -19,6 +19,12 @@ using namespace GPUCA_NAMESPACE::gpu; void GPUTPCCompression::InitializeProcessor() {} +void* GPUTPCCompression::SetPointersOutputGPU(void* mem) +{ + SetPointersCompressedClusters(mem, *mOutputA, mOutputA->nAttachedClusters, mOutputA->nTracks, mOutputA->nUnattachedClusters, true); + return mem; +} + void* GPUTPCCompression::SetPointersOutputHost(void* mem) { computePointerWithoutAlignment(mem, mOutputFlat); @@ -29,10 +35,10 @@ void* GPUTPCCompression::SetPointersOutputHost(void* mem) void* GPUTPCCompression::SetPointersScratch(void* mem) { computePointerWithAlignment(mem, mClusterStatus, mMaxClusters); - if (mRec->GetDeviceProcessingSettings().tpcCompressionGatherMode == 2) { + if (mRec->GetProcessingSettings().tpcCompressionGatherMode >= 2) { computePointerWithAlignment(mem, mAttachedClusterFirstIndex, mMaxTracks); } - if (mRec->GetDeviceProcessingSettings().tpcCompressionGatherMode != 1) { + if (mRec->GetProcessingSettings().tpcCompressionGatherMode != 1) { SetPointersCompressedClusters(mem, mPtrs, mMaxTrackClusters, mMaxTracks, mMaxClusters, false); } return mem; @@ -41,7 +47,7 @@ void* GPUTPCCompression::SetPointersScratch(void* mem) void* GPUTPCCompression::SetPointersOutput(void* mem) { computePointerWithAlignment(mem, mAttachedClusterFirstIndex, mMaxTrackClusters); - if (mRec->GetDeviceProcessingSettings().tpcCompressionGatherMode == 1) { + if (mRec->GetProcessingSettings().tpcCompressionGatherMode == 1) { SetPointersCompressedClusters(mem, mPtrs, mMaxTrackClusters, mMaxTracks, mMaxClusters, false); } return mem; @@ -50,7 +56,7 @@ void* GPUTPCCompression::SetPointersOutput(void* mem) template <class T> void GPUTPCCompression::SetPointersCompressedClusters(void*& mem, T& c, unsigned int nClA, unsigned int nTr, unsigned int nClU, bool reducedClA) { - computePointerWithAlignment(mem, c.qTotU, nClU); + computePointerWithAlignment(mem, c.qTotU, nClU); // Do not reorder, qTotU ist used as first address in GPUChainTracking::RunTPCCompression computePointerWithAlignment(mem, c.qMaxU, nClU); computePointerWithAlignment(mem, c.flagsU, nClU); computePointerWithAlignment(mem, c.padDiffU, nClU); @@ -87,6 +93,7 @@ void* GPUTPCCompression::SetPointersMemory(void* mem) { computePointerWithAlignment(mem, mMemory); computePointerWithAlignment(mem, mOutput); + mOutputA = mOutput; return mem; } @@ -94,10 +101,14 @@ void GPUTPCCompression::RegisterMemoryAllocation() { AllocateAndInitializeLate(); mMemoryResOutputHost = mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutputHost, GPUMemoryResource::MEMORY_OUTPUT_FLAG | GPUMemoryResource::MEMORY_HOST | GPUMemoryResource::MEMORY_CUSTOM, "TPCCompressionOutputHost"); - if (mRec->GetDeviceProcessingSettings().tpcCompressionGatherMode != 2) { - mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_STACK, "TPCCompressionOutput"); + if (mRec->GetProcessingSettings().tpcCompressionGatherMode == 3) { + mMemoryResOutputGPU = mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutputGPU, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_STACK, "TPCCompressionOutputGPU"); + } + unsigned int stackScratch = (mRec->GetProcessingSettings().tpcCompressionGatherMode != 3) ? GPUMemoryResource::MEMORY_STACK : 0; + if (mRec->GetProcessingSettings().tpcCompressionGatherMode < 2) { + mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersOutput, GPUMemoryResource::MEMORY_OUTPUT | stackScratch, "TPCCompressionOutput"); } - mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCCompressionScratch"); + mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersScratch, GPUMemoryResource::MEMORY_SCRATCH | stackScratch, "TPCCompressionScratch"); mRec->RegisterMemoryAllocation(this, &GPUTPCCompression::SetPointersMemory, GPUMemoryResource::MEMORY_PERMANENT, "TPCCompressionMemory"); } diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompression.h b/GPU/GPUTracking/DataCompression/GPUTPCCompression.h index f9f427372028f..b0f449bd81edb 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompression.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompression.h @@ -22,9 +22,7 @@ #ifdef HAVE_O2HEADERS #include "DataFormatsTPC/CompressedClusters.h" #else -namespace o2 -{ -namespace tpc +namespace o2::tpc { struct CompressedClustersPtrs { }; @@ -32,19 +30,17 @@ struct CompressedClusters { }; struct CompressedClustersFlat { }; -} // namespace tpc -} // namespace o2 +} // namespace o2::tpc #endif -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCGMMerger; class GPUTPCCompression : public GPUProcessor { friend class GPUTPCCompressionKernels; + friend class GPUTPCCompressionGatherKernels; friend class GPUChainTracking; public: @@ -53,6 +49,7 @@ class GPUTPCCompression : public GPUProcessor void RegisterMemoryAllocation(); void SetMaxData(const GPUTrackingInOutPointers& io); + void* SetPointersOutputGPU(void* mem); void* SetPointersOutputHost(void* mem); void* SetPointersOutputPtrs(void* mem); void* SetPointersOutput(void* mem); @@ -83,6 +80,7 @@ class GPUTPCCompression : public GPUProcessor o2::tpc::CompressedClustersPtrs mPtrs; o2::tpc::CompressedClusters* mOutput = nullptr; + o2::tpc::CompressedClusters* mOutputA = nullptr; // Always points to host buffer o2::tpc::CompressedClustersFlat* mOutputFlat = nullptr; memory* mMemory = nullptr; @@ -100,6 +98,7 @@ class GPUTPCCompression : public GPUProcessor GPUd() static void truncateSignificantBits(T& val, unsigned int nBits, unsigned int max); short mMemoryResOutputHost = -1; + short mMemoryResOutputGPU = -1; }; template <class T> @@ -124,7 +123,6 @@ GPUdi() void GPUTPCCompression::truncateSignificantBits(T& v, unsigned int nBits v = val; } } -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx index 995c32e7d41a8..734681c163a4e 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx @@ -14,11 +14,11 @@ #include "GPUTPCCompressionKernels.h" #include "GPUConstantMem.h" #include "GPUO2DataTypes.h" -#include "GPUTPCGMMerger.h" #include "GPUParam.h" #include "GPUCommonAlgorithm.h" #include "GPUTPCCompressionTrackModel.h" #include "GPUTPCGeometry.h" +#include "GPUTPCClusterRejection.h" using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; @@ -26,35 +26,37 @@ using namespace o2::tpc; template <> GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step0attached>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors) { - const GPUTPCGMMerger& GPUrestrict() merger = processors.tpcMerger; - const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = processors.ioPtrs.clustersNative; + const GPUTrackingInOutPointers& GPUrestrict() ioPtrs = processors.ioPtrs; + const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = ioPtrs.clustersNative; GPUTPCCompression& GPUrestrict() compressor = processors.tpcCompressor; const GPUParam& GPUrestrict() param = processors.param; char lastLeg = 0; int myTrack = 0; - for (unsigned int i = get_global_id(0); i < (unsigned int)merger.NOutputTracks(); i += get_global_size(0)) { - const GPUTPCGMMergedTrack& GPUrestrict() trk = merger.OutputTracks()[i]; + for (unsigned int i = get_global_id(0); i < ioPtrs.nMergedTracks; i += get_global_size(0)) { + GPUbarrierWarp(); + const GPUTPCGMMergedTrack& GPUrestrict() trk = ioPtrs.mergedTracks[i]; if (!trk.OK()) { continue; } - bool rejectTrk = CAMath::Abs(trk.GetParam().GetQPt()) > processors.param.rec.tpcRejectQPt; + bool rejectTrk = CAMath::Abs(trk.GetParam().GetQPt()) > processors.param.rec.tpcRejectQPt || trk.MergedLooper(); unsigned int nClustersStored = 0; CompressedClustersPtrs& GPUrestrict() c = compressor.mPtrs; unsigned int lastRow = 0, lastSlice = 0; // BUG: These should be unsigned char, but then CUDA breaks GPUTPCCompressionTrackModel track; + float zOffset = 0; for (int k = trk.NClusters() - 1; k >= 0; k--) { - const GPUTPCGMMergedTrackHit& GPUrestrict() hit = merger.Clusters()[trk.FirstClusterRef() + k]; + const GPUTPCGMMergedTrackHit& GPUrestrict() hit = ioPtrs.mergedTrackHits[trk.FirstClusterRef() + k]; if (hit.state & GPUTPCGMMergedTrackHit::flagReject) { continue; } int hitId = hit.num; - int attach = merger.ClusterAttachment()[hitId]; - if ((attach & GPUTPCGMMergerTypes::attachTrackMask) != i) { + int attach = ioPtrs.mergedTrackHitAttachment[hitId]; + if ((attach & gputpcgmmergertypes::attachTrackMask) != i) { continue; // Main attachment to different track } - bool rejectCluster = processors.param.rec.tpcRejectionMode && (rejectTrk || ((attach & GPUTPCGMMergerTypes::attachGoodLeg) == 0) || (attach & GPUTPCGMMergerTypes::attachHighIncl)); + bool rejectCluster = processors.param.rec.tpcRejectionMode && (rejectTrk || GPUTPCClusterRejection::GetIsRejected(attach)); if (rejectCluster) { compressor.mClusterStatus[hitId] = 1; // Cluster rejected, do not store continue; @@ -84,7 +86,8 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step0at int cidx = trk.FirstClusterRef() + nClustersStored++; if (nClustersStored == 1) { unsigned char qpt = fabs(trk.GetParam().GetQPt()) < 20.f ? (trk.GetParam().GetQPt() * (127.f / 20.f) + 127.5f) : (trk.GetParam().GetQPt() > 0 ? 254 : 0); - track.Init(x, y, z, param.SliceParam[hit.slice].Alpha, qpt, param); // TODO: Compression track model must respect Z offset! + zOffset = z; + track.Init(x, y, z - zOffset, param.SliceParam[hit.slice].Alpha, qpt, param); myTrack = CAMath::AtomicAdd(&compressor.mMemory->nStoredTracks, 1u); compressor.mAttachedClusterFirstIndex[myTrack] = trk.FirstClusterRef(); @@ -112,12 +115,10 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step0at c.sliceLegDiffA[cidx] = (hit.leg == lastLeg ? 0 : compressor.NSLICES) + slice; float pad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(hit.slice, hit.row, track.Y()))); c.padResA[cidx] = orgCl.padPacked - orgCl.packPad(pad); - float time = CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(hit.slice, track.Z())); + float time = CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(hit.slice, track.Z() + zOffset)); c.timeResA[cidx] = (orgCl.getTimePacked() - orgCl.packTime(time)) & 0xFFFFFF; lastLeg = hit.leg; } - lastRow = hit.row; - lastSlice = hit.slice; unsigned short qtot = orgCl.qTot, qmax = orgCl.qMax; unsigned char sigmapad = orgCl.sigmaPadPacked, sigmatime = orgCl.sigmaTimePacked; if (param.rec.tpcCompressionModes & GPUSettings::CompressionTruncate) { @@ -131,9 +132,11 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step0at c.sigmaPadA[cidx] = sigmapad; c.sigmaTimeA[cidx] = sigmatime; c.flagsA[cidx] = orgCl.getFlags(); - if (k && track.Filter(y, z, hit.row)) { + if (k && track.Filter(y, z - zOffset, hit.row)) { break; } + lastRow = hit.row; + lastSlice = hit.slice; } if (nClustersStored) { CAMath::AtomicAdd(&compressor.mMemory->nStoredAttachedClusters, nClustersStored); @@ -175,17 +178,17 @@ GPUd() bool GPUTPCCompressionKernels::GPUTPCCompressionKernels_Compare<3>::opera template <> GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1unattached>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) { - const GPUTPCGMMerger& GPUrestrict() merger = processors.tpcMerger; - const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = processors.ioPtrs.clustersNative; + const GPUTrackingInOutPointers& GPUrestrict() ioPtrs = processors.ioPtrs; + const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = ioPtrs.clustersNative; GPUTPCCompression& GPUrestrict() compressor = processors.tpcCompressor; GPUParam& GPUrestrict() param = processors.param; - unsigned int* sortBuffer = smem.step1.sortBuffer; + unsigned int* sortBuffer = smem.sortBuffer; for (int iSliceRow = iBlock; iSliceRow < GPUCA_NSLICES * GPUCA_ROW_COUNT; iSliceRow += nBlocks) { const int iSlice = iSliceRow / GPUCA_ROW_COUNT; const int iRow = iSliceRow % GPUCA_ROW_COUNT; const int idOffset = clusters->clusterOffset[iSlice][iRow]; if (iThread == nThreads - 1) { - smem.step1.nCount = 0; + smem.nCount = 0; } unsigned int totalCount = 0; GPUbarrier(); @@ -203,7 +206,7 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1un if (compressor.mClusterStatus[idx]) { break; } - int attach = merger.ClusterAttachment()[idx]; + int attach = ioPtrs.mergedTrackHitAttachment[idx]; bool unattached = attach == 0; if (unattached) { @@ -211,14 +214,12 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1un break; } } else if (processors.param.rec.tpcRejectionMode >= GPUSettings::RejectionStrategyA) { - if ((attach & GPUTPCGMMergerTypes::attachGoodLeg) == 0) { - break; - } - if (attach & GPUTPCGMMergerTypes::attachHighIncl) { + if (GPUTPCClusterRejection::GetIsRejected(attach)) { break; } - int id = attach & GPUTPCGMMergerTypes::attachTrackMask; - if (CAMath::Abs(merger.OutputTracks()[id].GetParam().GetQPt()) > processors.param.rec.tpcRejectQPt) { + int id = attach & gputpcgmmergertypes::attachTrackMask; + auto& trk = ioPtrs.mergedTracks[id]; + if (CAMath::Abs(trk.GetParam().GetQPt()) > processors.param.rec.tpcRejectQPt || trk.MergedLooper()) { break; } } @@ -229,23 +230,23 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1un int myIndex = work_group_scan_inclusive_add(cidx); int storeLater = -1; if (cidx) { - if (smem.step1.nCount + myIndex <= GPUCA_TPC_COMP_CHUNK_SIZE) { - sortBuffer[smem.step1.nCount + myIndex - 1] = i; + if (smem.nCount + myIndex <= GPUCA_TPC_COMP_CHUNK_SIZE) { + sortBuffer[smem.nCount + myIndex - 1] = i; } else { - storeLater = smem.step1.nCount + myIndex - 1 - GPUCA_TPC_COMP_CHUNK_SIZE; + storeLater = smem.nCount + myIndex - 1 - GPUCA_TPC_COMP_CHUNK_SIZE; } } GPUbarrier(); if (iThread == nThreads - 1) { - smem.step1.nCount += myIndex; + smem.nCount += myIndex; } GPUbarrier(); - if (smem.step1.nCount < GPUCA_TPC_COMP_CHUNK_SIZE && i < nn) { + if (smem.nCount < GPUCA_TPC_COMP_CHUNK_SIZE && i < nn) { continue; } - const unsigned int count = CAMath::Min(smem.step1.nCount, (unsigned int)GPUCA_TPC_COMP_CHUNK_SIZE); + const unsigned int count = CAMath::Min(smem.nCount, (unsigned int)GPUCA_TPC_COMP_CHUNK_SIZE); if (param.rec.tpcCompressionModes & GPUSettings::CompressionDifferences) { if (param.rec.tpcCompressionSortOrder == GPUSettings::SortZPadTime) { CAAlgo::sortInBlock(sortBuffer, sortBuffer + count, GPUTPCCompressionKernels_Compare<GPUSettings::SortZPadTime>(clusters->clusters[iSlice][iRow])); @@ -270,7 +271,7 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1un lastPad = orgClPre.padPacked; lastTime = orgClPre.getTimePacked(); } else if (totalCount != 0) { - const ClusterNative& GPUrestrict() orgClPre = clusters->clusters[iSlice][iRow][smem.step1.lastIndex]; + const ClusterNative& GPUrestrict() orgClPre = clusters->clusters[iSlice][iRow][smem.lastIndex]; lastPad = orgClPre.padPacked; lastTime = orgClPre.getTimePacked(); } @@ -303,8 +304,8 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1un } totalCount += count; if (iThread == nThreads - 1 && count) { - smem.step1.lastIndex = sortBuffer[count - 1]; - smem.step1.nCount -= count; + smem.lastIndex = sortBuffer[count - 1]; + smem.nCount -= count; } } @@ -316,14 +317,38 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step1un } } -template <typename T> -GPUdi() bool GPUTPCCompressionKernels::isAlignedTo(const void* ptr) +template <> +GPUdi() GPUTPCCompressionGatherKernels::Vec32* GPUTPCCompressionGatherKernels::GPUSharedMemory::getBuffer<GPUTPCCompressionGatherKernels::Vec32>(int iWarp) +{ + return buf32[iWarp]; +} + +template <> +GPUdi() GPUTPCCompressionGatherKernels::Vec64* GPUTPCCompressionGatherKernels::GPUSharedMemory::getBuffer<GPUTPCCompressionGatherKernels::Vec64>(int iWarp) { - return reinterpret_cast<size_t>(ptr) % alignof(T) == 0; + return buf64[iWarp]; } template <> -GPUdi() void GPUTPCCompressionKernels::compressorMemcpy<unsigned char>(unsigned char* GPUrestrict() dst, const unsigned char* GPUrestrict() src, unsigned int size, int nThreads, int iThread) +GPUdi() GPUTPCCompressionGatherKernels::Vec128* GPUTPCCompressionGatherKernels::GPUSharedMemory::getBuffer<GPUTPCCompressionGatherKernels::Vec128>(int iWarp) +{ + return buf128[iWarp]; +} + +template <typename T, typename S> +GPUdi() bool GPUTPCCompressionGatherKernels::isAlignedTo(const S* ptr) +{ + if CONSTEXPR17 (alignof(S) >= alignof(T)) { + static_cast<void>(ptr); + return true; + } else { + return reinterpret_cast<size_t>(ptr) % alignof(T) == 0; + } + return false; // BUG: Cuda complains about missing return value with constexpr if +} + +template <> +GPUdi() void GPUTPCCompressionGatherKernels::compressorMemcpy<unsigned char>(unsigned char* GPUrestrict() dst, const unsigned char* GPUrestrict() src, unsigned int size, int nThreads, int iThread) { CONSTEXPR int vec128Elems = CpyVector<unsigned char, Vec128>::Size; CONSTEXPR int vec64Elems = CpyVector<unsigned char, Vec64>::Size; @@ -344,7 +369,7 @@ GPUdi() void GPUTPCCompressionKernels::compressorMemcpy<unsigned char>(unsigned } template <> -GPUdi() void GPUTPCCompressionKernels::compressorMemcpy<unsigned short>(unsigned short* GPUrestrict() dst, const unsigned short* GPUrestrict() src, unsigned int size, int nThreads, int iThread) +GPUdi() void GPUTPCCompressionGatherKernels::compressorMemcpy<unsigned short>(unsigned short* GPUrestrict() dst, const unsigned short* GPUrestrict() src, unsigned int size, int nThreads, int iThread) { CONSTEXPR int vec128Elems = CpyVector<unsigned short, Vec128>::Size; CONSTEXPR int vec64Elems = CpyVector<unsigned short, Vec64>::Size; @@ -362,7 +387,7 @@ GPUdi() void GPUTPCCompressionKernels::compressorMemcpy<unsigned short>(unsigned } template <> -GPUdi() void GPUTPCCompressionKernels::compressorMemcpy<unsigned int>(unsigned int* GPUrestrict() dst, const unsigned int* GPUrestrict() src, unsigned int size, int nThreads, int iThread) +GPUdi() void GPUTPCCompressionGatherKernels::compressorMemcpy<unsigned int>(unsigned int* GPUrestrict() dst, const unsigned int* GPUrestrict() src, unsigned int size, int nThreads, int iThread) { CONSTEXPR int vec128Elems = CpyVector<unsigned int, Vec128>::Size; CONSTEXPR int vec64Elems = CpyVector<unsigned int, Vec64>::Size; @@ -376,18 +401,8 @@ GPUdi() void GPUTPCCompressionKernels::compressorMemcpy<unsigned int>(unsigned i } } -template <typename T> -GPUdi() void GPUTPCCompressionKernels::compressorMemcpyBasic(T* GPUrestrict() dst, const T* GPUrestrict() src, unsigned int size, int nThreads, int iThread, int nBlocks, int iBlock) -{ - unsigned int start = (size + nBlocks - 1) / nBlocks * iBlock + iThread; - unsigned int end = CAMath::Min(size, (size + nBlocks - 1) / nBlocks * (iBlock + 1)); - for (unsigned int i = start; i < end; i += nThreads) { - dst[i] = src[i]; - } -} - template <typename Scalar, typename BaseVector> -GPUdi() void GPUTPCCompressionKernels::compressorMemcpyVectorised(Scalar* dst, const Scalar* src, unsigned int size, int nThreads, int iThread) +GPUdi() void GPUTPCCompressionGatherKernels::compressorMemcpyVectorised(Scalar* dst, const Scalar* src, unsigned int size, int nThreads, int iThread) { if (not isAlignedTo<BaseVector>(dst)) { size_t dsti = reinterpret_cast<size_t>(dst); @@ -421,25 +436,92 @@ GPUdi() void GPUTPCCompressionKernels::compressorMemcpyVectorised(Scalar* dst, c } template <typename T> -GPUdi() unsigned int GPUTPCCompressionKernels::calculateWarpOffsets(GPUSharedMemory& smem, T* nums, unsigned int start, unsigned int end, int iWarp, int nLanes, int iLane) +GPUdi() void GPUTPCCompressionGatherKernels::compressorMemcpyBasic(T* GPUrestrict() dst, const T* GPUrestrict() src, unsigned int size, int nThreads, int iThread, int nBlocks, int iBlock) { - unsigned int offset = 0; - if (iWarp > -1) { - for (unsigned int i = start + iLane; i < end; i += nLanes) { - offset += nums[i]; + unsigned int start = (size + nBlocks - 1) / nBlocks * iBlock + iThread; + unsigned int end = CAMath::Min(size, (size + nBlocks - 1) / nBlocks * (iBlock + 1)); + for (unsigned int i = start; i < end; i += nThreads) { + dst[i] = src[i]; + } +} + +template <typename V, typename T, typename S> +GPUdi() void GPUTPCCompressionGatherKernels::compressorMemcpyBuffered(V* buf, T* GPUrestrict() dst, const T* GPUrestrict() src, const S* GPUrestrict() nums, const unsigned int* GPUrestrict() srcOffsets, unsigned int nTracks, int nLanes, int iLane, int diff) +{ + int shmPos = 0; + unsigned int dstOffset = 0; + V* GPUrestrict() dstAligned = nullptr; + + T* bufT = reinterpret_cast<T*>(buf); + CONSTEXPR int bufSize = GPUCA_WARP_SIZE; + CONSTEXPR int bufTSize = bufSize * sizeof(V) / sizeof(T); + + for (unsigned int i = 0; i < nTracks; i++) { + unsigned int srcPos = 0; + unsigned int srcOffset = srcOffsets[i] + diff; + unsigned int srcSize = nums[i] - diff; + + if (dstAligned == nullptr) { + if (not isAlignedTo<V>(dst)) { + size_t dsti = reinterpret_cast<size_t>(dst); + unsigned int offset = (alignof(V) - dsti % alignof(V)) / sizeof(T); + offset = CAMath::Min<unsigned int>(offset, srcSize); + compressorMemcpyBasic(dst, src + srcOffset, offset, nLanes, iLane); + dst += offset; + srcPos += offset; + } + if (isAlignedTo<V>(dst)) { + dstAligned = reinterpret_cast<V*>(dst); + } + } + while (srcPos < srcSize) { + unsigned int shmElemsLeft = bufTSize - shmPos; + unsigned int srcElemsLeft = srcSize - srcPos; + unsigned int size = CAMath::Min(srcElemsLeft, shmElemsLeft); + compressorMemcpyBasic(bufT + shmPos, src + srcOffset + srcPos, size, nLanes, iLane); + srcPos += size; + shmPos += size; + + if (shmPos >= bufTSize) { + compressorMemcpyBasic(dstAligned + dstOffset, buf, bufSize, nLanes, iLane); + dstOffset += bufSize; + shmPos = 0; + } } } - offset = work_group_scan_inclusive_add(int(offset)); // FIXME: use scan with unsigned int + + compressorMemcpyBasic(reinterpret_cast<T*>(dstAligned + dstOffset), bufT, shmPos, nLanes, iLane); +} + +template <typename T> +GPUdi() unsigned int GPUTPCCompressionGatherKernels::calculateWarpOffsets(GPUSharedMemory& smem, T* nums, unsigned int start, unsigned int end, int nWarps, int iWarp, int nLanes, int iLane) +{ + unsigned int blockOffset = 0; + int iThread = nLanes * iWarp + iLane; + int nThreads = nLanes * nWarps; + unsigned int blockStart = work_group_broadcast(start, 0); + for (unsigned int i = iThread; i < blockStart; i += nThreads) { + blockOffset += nums[i]; + } + blockOffset = work_group_reduce_add(blockOffset); + + unsigned int offset = 0; + for (unsigned int i = start + iLane; i < end; i += nLanes) { + offset += nums[i]; + } + offset = work_group_scan_inclusive_add(offset); if (iWarp > -1 && iLane == nLanes - 1) { - smem.step2.warpOffset[iWarp] = offset; + smem.warpOffset[iWarp] = offset; } GPUbarrier(); - offset = (iWarp <= 0) ? 0 : smem.step2.warpOffset[iWarp - 1]; + offset = (iWarp <= 0) ? 0 : smem.warpOffset[iWarp - 1]; + GPUbarrier(); - return offset; + return offset + blockOffset; } + template <> -GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step2gather>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +GPUdii() void GPUTPCCompressionGatherKernels::Thread<GPUTPCCompressionGatherKernels::unbuffered>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) { GPUTPCCompression& GPUrestrict() compressor = processors.tpcCompressor; const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = processors.ioPtrs.clustersNative; @@ -456,8 +538,12 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step2ga unsigned int rowsPerWarp = (nRows + nWarps - 1) / nWarps; unsigned int rowStart = rowsPerWarp * iWarp; unsigned int rowEnd = CAMath::Min(nRows, rowStart + rowsPerWarp); + if (rowStart >= nRows) { + rowStart = 0; + rowEnd = 0; + } - unsigned int rowsOffset = calculateWarpOffsets(smem, compressor.mPtrs.nSliceRowClusters, rowStart, rowEnd, iWarp, nLanes, iLane); + unsigned int rowsOffset = calculateWarpOffsets(smem, compressor.mPtrs.nSliceRowClusters, rowStart, rowEnd, nWarps, iWarp, nLanes, iLane); compressorMemcpy(compressor.mOutput->nSliceRowClusters, compressor.mPtrs.nSliceRowClusters, compressor.NSLICES * GPUCA_ROW_COUNT, nThreads, iThread); compressorMemcpy(compressor.mOutput->nTrackClusters, compressor.mPtrs.nTrackClusters, compressor.mMemory->nStoredTracks, nThreads, iThread); @@ -492,8 +578,12 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step2ga unsigned int tracksPerWarp = (compressor.mMemory->nStoredTracks + nWarps - 1) / nWarps; unsigned int trackStart = tracksPerWarp * iWarp; unsigned int trackEnd = CAMath::Min(compressor.mMemory->nStoredTracks, trackStart + tracksPerWarp); + if (trackStart >= compressor.mMemory->nStoredTracks) { + trackStart = 0; + trackEnd = 0; + } - unsigned int tracksOffset = calculateWarpOffsets(smem, compressor.mPtrs.nTrackClusters, trackStart, trackEnd, iWarp, nLanes, iLane); + unsigned int tracksOffset = calculateWarpOffsets(smem, compressor.mPtrs.nTrackClusters, trackStart, trackEnd, nWarps, iWarp, nLanes, iLane); for (unsigned int i = trackStart; i < trackEnd; i += nLanes) { unsigned int nTrackClusters = 0; @@ -503,14 +593,14 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step2ga nTrackClusters = compressor.mPtrs.nTrackClusters[i + iLane]; srcOffset = compressor.mAttachedClusterFirstIndex[i + iLane]; } - smem.step2.sizes[iWarp][iLane] = nTrackClusters; - smem.step2.srcOffsets[iWarp][iLane] = srcOffset; + smem.unbuffered.sizes[iWarp][iLane] = nTrackClusters; + smem.unbuffered.srcOffsets[iWarp][iLane] = srcOffset; unsigned int elems = (i + nLanes < trackEnd) ? nLanes : (trackEnd - i); for (unsigned int j = 0; j < elems; j++) { - nTrackClusters = smem.step2.sizes[iWarp][j]; - srcOffset = smem.step2.srcOffsets[iWarp][j]; + nTrackClusters = smem.unbuffered.sizes[iWarp][j]; + srcOffset = smem.unbuffered.srcOffsets[iWarp][j]; unsigned int idx = i + j; compressorMemcpy(compressor.mOutput->qTotA + tracksOffset, compressor.mPtrs.qTotA + srcOffset, nTrackClusters, nLanes, iLane); compressorMemcpy(compressor.mOutput->qMaxA + tracksOffset, compressor.mPtrs.qMaxA + srcOffset, nTrackClusters, nLanes, iLane); @@ -529,3 +619,189 @@ GPUdii() void GPUTPCCompressionKernels::Thread<GPUTPCCompressionKernels::step2ga } } } + +template <typename V> +GPUdii() void GPUTPCCompressionGatherKernels::gatherBuffered(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + + GPUTPCCompression& GPUrestrict() compressor = processors.tpcCompressor; + const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = processors.ioPtrs.clustersNative; + + int nWarps = nThreads / GPUCA_WARP_SIZE; + int iWarp = iThread / GPUCA_WARP_SIZE; + + int nGlobalWarps = nWarps * nBlocks; + int iGlobalWarp = nWarps * iBlock + iWarp; + + int nLanes = GPUCA_WARP_SIZE; + int iLane = iThread % GPUCA_WARP_SIZE; + + auto& input = compressor.mPtrs; + auto* output = compressor.mOutput; + + unsigned int nRows = compressor.NSLICES * GPUCA_ROW_COUNT; + unsigned int rowsPerWarp = (nRows + nGlobalWarps - 1) / nGlobalWarps; + unsigned int rowStart = rowsPerWarp * iGlobalWarp; + unsigned int rowEnd = CAMath::Min(nRows, rowStart + rowsPerWarp); + if (rowStart >= nRows) { + rowStart = 0; + rowEnd = 0; + } + rowsPerWarp = rowEnd - rowStart; + + unsigned int rowsOffset = calculateWarpOffsets(smem, input.nSliceRowClusters, rowStart, rowEnd, nWarps, iWarp, nLanes, iLane); + + unsigned int nStoredTracks = compressor.mMemory->nStoredTracks; + unsigned int tracksPerWarp = (nStoredTracks + nGlobalWarps - 1) / nGlobalWarps; + unsigned int trackStart = tracksPerWarp * iGlobalWarp; + unsigned int trackEnd = CAMath::Min(nStoredTracks, trackStart + tracksPerWarp); + if (trackStart >= nStoredTracks) { + trackStart = 0; + trackEnd = 0; + } + tracksPerWarp = trackEnd - trackStart; + + unsigned int tracksOffset = calculateWarpOffsets(smem, input.nTrackClusters, trackStart, trackEnd, nWarps, iWarp, nLanes, iLane); + + if (iBlock == 0) { + compressorMemcpyBasic(output->nSliceRowClusters, input.nSliceRowClusters, compressor.NSLICES * GPUCA_ROW_COUNT, nThreads, iThread); + compressorMemcpyBasic(output->nTrackClusters, input.nTrackClusters, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->qPtA, input.qPtA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->rowA, input.rowA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->sliceA, input.sliceA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->timeA, input.timeA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->padA, input.padA, compressor.mMemory->nStoredTracks, nThreads, iThread); + } + + const unsigned int* clusterOffsets = reinterpret_cast<const unsigned int*>(clusters->clusterOffset) + rowStart; + const unsigned int* nSliceRowClusters = input.nSliceRowClusters + rowStart; + + auto* buf = smem.getBuffer<V>(iWarp); + + compressorMemcpyBuffered(buf, output->qTotU + rowsOffset, input.qTotU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->qMaxU + rowsOffset, input.qMaxU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->flagsU + rowsOffset, input.flagsU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->padDiffU + rowsOffset, input.padDiffU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->timeDiffU + rowsOffset, input.timeDiffU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaPadU + rowsOffset, input.sigmaPadU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaTimeU + rowsOffset, input.sigmaTimeU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + + const unsigned short* nTrackClustersPtr = input.nTrackClusters + trackStart; + const unsigned int* aClsFstIdx = compressor.mAttachedClusterFirstIndex + trackStart; + + compressorMemcpyBuffered(buf, output->qTotA + tracksOffset, input.qTotA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->qMaxA + tracksOffset, input.qMaxA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->flagsA + tracksOffset, input.flagsA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaPadA + tracksOffset, input.sigmaPadA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaTimeA + tracksOffset, input.sigmaTimeA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + + // First index stored with track + unsigned int tracksOffsetDiff = tracksOffset - trackStart; + compressorMemcpyBuffered(buf, output->rowDiffA + tracksOffsetDiff, input.rowDiffA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + compressorMemcpyBuffered(buf, output->sliceLegDiffA + tracksOffsetDiff, input.sliceLegDiffA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + compressorMemcpyBuffered(buf, output->padResA + tracksOffsetDiff, input.padResA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + compressorMemcpyBuffered(buf, output->timeResA + tracksOffsetDiff, input.timeResA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); +} + +GPUdii() void GPUTPCCompressionGatherKernels::gatherMulti(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + GPUTPCCompression& GPUrestrict() compressor = processors.tpcCompressor; + const o2::tpc::ClusterNativeAccess* GPUrestrict() clusters = processors.ioPtrs.clustersNative; + const auto& input = compressor.mPtrs; + auto* output = compressor.mOutput; + + const int nWarps = nThreads / GPUCA_WARP_SIZE; + const int iWarp = iThread / GPUCA_WARP_SIZE; + const int nLanes = GPUCA_WARP_SIZE; + const int iLane = iThread % GPUCA_WARP_SIZE; + auto* buf = smem.getBuffer<Vec128>(iWarp); + + if (iBlock == 0) { + compressorMemcpyBasic(output->nSliceRowClusters, input.nSliceRowClusters, compressor.NSLICES * GPUCA_ROW_COUNT, nThreads, iThread); + compressorMemcpyBasic(output->nTrackClusters, input.nTrackClusters, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->qPtA, input.qPtA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->rowA, input.rowA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->sliceA, input.sliceA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->timeA, input.timeA, compressor.mMemory->nStoredTracks, nThreads, iThread); + compressorMemcpyBasic(output->padA, input.padA, compressor.mMemory->nStoredTracks, nThreads, iThread); + } else if (iBlock & 1) { + const unsigned int nGlobalWarps = nWarps * (nBlocks - 1) / 2; + const unsigned int iGlobalWarp = nWarps * (iBlock - 1) / 2 + iWarp; + + const unsigned int nRows = compressor.NSLICES * GPUCA_ROW_COUNT; + unsigned int rowsPerWarp = (nRows + nGlobalWarps - 1) / nGlobalWarps; + unsigned int rowStart = rowsPerWarp * iGlobalWarp; + unsigned int rowEnd = CAMath::Min(nRows, rowStart + rowsPerWarp); + if (rowStart >= nRows) { + rowStart = 0; + rowEnd = 0; + } + rowsPerWarp = rowEnd - rowStart; + + const unsigned int rowsOffset = calculateWarpOffsets(smem, input.nSliceRowClusters, rowStart, rowEnd, nWarps, iWarp, nLanes, iLane); + const unsigned int* clusterOffsets = reinterpret_cast<const unsigned int*>(clusters->clusterOffset) + rowStart; + const unsigned int* nSliceRowClusters = input.nSliceRowClusters + rowStart; + + compressorMemcpyBuffered(buf, output->qTotU + rowsOffset, input.qTotU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->qMaxU + rowsOffset, input.qMaxU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->flagsU + rowsOffset, input.flagsU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->padDiffU + rowsOffset, input.padDiffU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->timeDiffU + rowsOffset, input.timeDiffU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaPadU + rowsOffset, input.sigmaPadU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaTimeU + rowsOffset, input.sigmaTimeU, nSliceRowClusters, clusterOffsets, rowsPerWarp, nLanes, iLane, 0); + } else { + const unsigned int nGlobalWarps = nWarps * (nBlocks - 1) / 2; + const unsigned int iGlobalWarp = nWarps * (iBlock / 2 - 1) + iWarp; + + const unsigned int nStoredTracks = compressor.mMemory->nStoredTracks; + unsigned int tracksPerWarp = (nStoredTracks + nGlobalWarps - 1) / nGlobalWarps; + unsigned int trackStart = tracksPerWarp * iGlobalWarp; + unsigned int trackEnd = CAMath::Min(nStoredTracks, trackStart + tracksPerWarp); + if (trackStart >= nStoredTracks) { + trackStart = 0; + trackEnd = 0; + } + tracksPerWarp = trackEnd - trackStart; + + const unsigned int tracksOffset = calculateWarpOffsets(smem, input.nTrackClusters, trackStart, trackEnd, nWarps, iWarp, nLanes, iLane); + const unsigned short* nTrackClustersPtr = input.nTrackClusters + trackStart; + const unsigned int* aClsFstIdx = compressor.mAttachedClusterFirstIndex + trackStart; + + compressorMemcpyBuffered(buf, output->qTotA + tracksOffset, input.qTotA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->qMaxA + tracksOffset, input.qMaxA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->flagsA + tracksOffset, input.flagsA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaPadA + tracksOffset, input.sigmaPadA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + compressorMemcpyBuffered(buf, output->sigmaTimeA + tracksOffset, input.sigmaTimeA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 0); + + // First index stored with track + unsigned int tracksOffsetDiff = tracksOffset - trackStart; + compressorMemcpyBuffered(buf, output->rowDiffA + tracksOffsetDiff, input.rowDiffA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + compressorMemcpyBuffered(buf, output->sliceLegDiffA + tracksOffsetDiff, input.sliceLegDiffA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + compressorMemcpyBuffered(buf, output->padResA + tracksOffsetDiff, input.padResA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + compressorMemcpyBuffered(buf, output->timeResA + tracksOffsetDiff, input.timeResA, nTrackClustersPtr, aClsFstIdx, tracksPerWarp, nLanes, iLane, 1); + } +} + +template <> +GPUdii() void GPUTPCCompressionGatherKernels::Thread<GPUTPCCompressionGatherKernels::buffered32>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + gatherBuffered<Vec32>(nBlocks, nThreads, iBlock, iThread, smem, processors); +} + +template <> +GPUdii() void GPUTPCCompressionGatherKernels::Thread<GPUTPCCompressionGatherKernels::buffered64>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + gatherBuffered<Vec64>(nBlocks, nThreads, iBlock, iThread, smem, processors); +} + +template <> +GPUdii() void GPUTPCCompressionGatherKernels::Thread<GPUTPCCompressionGatherKernels::buffered128>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + gatherBuffered<Vec128>(nBlocks, nThreads, iBlock, iThread, smem, processors); +} + +template <> +GPUdii() void GPUTPCCompressionGatherKernels::Thread<GPUTPCCompressionGatherKernels::multiBlock>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + gatherMulti(nBlocks, nThreads, iBlock, iThread, smem, processors); +} diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h index 8b4e685b3a34e..bcee016899094 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.h @@ -16,17 +16,12 @@ #include "GPUGeneralKernels.h" -namespace o2 -{ -namespace tpc +namespace o2::tpc { struct ClusterNative; -} -} // namespace o2 +} // namespace o2::tpc -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCCompressionKernels : public GPUKernelTemplate { @@ -36,35 +31,62 @@ class GPUTPCCompressionKernels : public GPUKernelTemplate enum K : int { step0attached = 0, step1unattached = 1, - step2gather = 2, }; -#if GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step1unattached) > GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step2gather) -#define GPUCA_COMPRESSION_SCAN_MAX_THREADS GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step1unattached) -#else -#define GPUCA_COMPRESSION_SCAN_MAX_THREADS GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step2gather) -#endif + struct GPUSharedMemory : public GPUKernelTemplate::GPUSharedMemoryScan64<int, GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step1unattached)> { + GPUAtomic(unsigned int) nCount; + unsigned int lastIndex; + unsigned int sortBuffer[GPUCA_TPC_COMP_CHUNK_SIZE]; + }; - struct GPUSharedMemory : public GPUKernelTemplate::GPUSharedMemoryScan64<int, GPUCA_COMPRESSION_SCAN_MAX_THREADS> { - union { - struct { - GPUAtomic(unsigned int) nCount; - unsigned int lastIndex; - unsigned int sortBuffer[GPUCA_TPC_COMP_CHUNK_SIZE]; - } step1; - struct { - unsigned int warpOffset[GPUCA_GET_WARP_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step2gather)]; - unsigned int sizes[GPUCA_GET_WARP_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step2gather)][GPUCA_WARP_SIZE]; - unsigned int srcOffsets[GPUCA_GET_WARP_COUNT(GPUCA_LB_GPUTPCCompressionKernels_step2gather)][GPUCA_WARP_SIZE]; - } step2; - }; + template <int iKernel = defaultKernel> + GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); + + template <int I> + class GPUTPCCompressionKernels_Compare + { + public: + GPUhdi() GPUTPCCompressionKernels_Compare(const o2::tpc::ClusterNative* p) : mClsPtr(p) {} + GPUd() bool operator()(unsigned int a, unsigned int b) const; + + protected: + const o2::tpc::ClusterNative* mClsPtr; + }; +}; + +class GPUTPCCompressionGatherKernels : public GPUKernelTemplate +{ + + public: + enum K : int { + unbuffered, + buffered32, + buffered64, + buffered128, + multiBlock }; using Vec16 = unsigned short; using Vec32 = unsigned int; - using Vec64 = unsigned long long int; + using Vec64 = unsigned long int; using Vec128 = uint4; + struct GPUSharedMemory : public GPUKernelTemplate::GPUSharedMemoryScan64<unsigned int, GPUCA_GET_THREAD_COUNT(GPUCA_LB_COMPRESSION_GATHER)> { + union { + unsigned int warpOffset[GPUCA_GET_WARP_COUNT(GPUCA_LB_COMPRESSION_GATHER)]; + Vec32 buf32[GPUCA_GET_WARP_COUNT(GPUCA_LB_COMPRESSION_GATHER)][GPUCA_WARP_SIZE]; + Vec64 buf64[GPUCA_GET_WARP_COUNT(GPUCA_LB_COMPRESSION_GATHER)][GPUCA_WARP_SIZE]; + Vec128 buf128[GPUCA_GET_WARP_COUNT(GPUCA_LB_COMPRESSION_GATHER)][GPUCA_WARP_SIZE]; + struct { + unsigned int sizes[GPUCA_GET_WARP_COUNT(GPUCA_LB_COMPRESSION_GATHER)][GPUCA_WARP_SIZE]; + unsigned int srcOffsets[GPUCA_GET_WARP_COUNT(GPUCA_LB_COMPRESSION_GATHER)][GPUCA_WARP_SIZE]; + } unbuffered; + }; + + template <typename V> + GPUdi() V* getBuffer(int iWarp); + }; + template <typename Scalar, typename BaseVector> union CpyVector { enum { @@ -77,8 +99,8 @@ class GPUTPCCompressionKernels : public GPUKernelTemplate template <int iKernel = defaultKernel> GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); - template <typename T> - GPUdi() static bool isAlignedTo(const void* ptr); + template <typename T, typename S> + GPUdi() static bool isAlignedTo(const S* ptr); template <typename T> GPUdi() static void compressorMemcpy(GPUgeneric() T* dst, GPUgeneric() const T* src, unsigned int size, int nThreads, int iThread); @@ -89,22 +111,18 @@ class GPUTPCCompressionKernels : public GPUKernelTemplate template <typename T> GPUdi() static void compressorMemcpyBasic(T* dst, const T* src, unsigned int size, int nThreads, int iThread, int nBlocks = 1, int iBlock = 0); + template <typename V, typename T, typename S> + GPUdi() static void compressorMemcpyBuffered(V* buf, T* dst, const T* src, const S* nums, const unsigned int* srcOffets, unsigned int nTracks, int nLanes, int iLane, int diff = 0); + template <typename T> - GPUdi() static unsigned int calculateWarpOffsets(GPUSharedMemory& smem, T* nums, unsigned int start, unsigned int end, int iWarp, int nLanes, int iLane); + GPUdi() static unsigned int calculateWarpOffsets(GPUSharedMemory& smem, T* nums, unsigned int start, unsigned int end, int nWarps, int iWarp, int nLanes, int iLane); - public: - template <int I> - class GPUTPCCompressionKernels_Compare - { - public: - GPUhdi() GPUTPCCompressionKernels_Compare(const o2::tpc::ClusterNative* p) : mClsPtr(p) {} - GPUd() bool operator()(unsigned int a, unsigned int b) const; + template <typename V> + GPUdii() static void gatherBuffered(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); - protected: - const o2::tpc::ClusterNative* mClsPtr; - }; + GPUdii() static void gatherMulti(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE + +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.cxx index 82f7eb6da8f7d..1434885468d2c 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.cxx @@ -90,7 +90,7 @@ GPUd() int GPUTPCCompressionTrackModel::Propagate(float x, float alpha) if (alpha != mAlpha && !mTrk.Rotate(alpha, t0, GPUCA_MAX_SIN_PHI)) { return 2; } - int retVal = !mTrk.TransportToX(x, t0, mParam->ConstBz, GPUCA_MAX_SIN_PHI); + int retVal = !mTrk.TransportToX(x, t0, mParam->par.ConstBz, GPUCA_MAX_SIN_PHI); // GPUInfo("Propagated to: x %f y %f z %f alpha %f qPt %f", x, mTrk.Y(), mTrk.Z(), alpha, mTrk.QPt()); return retVal; } @@ -112,4 +112,823 @@ GPUd() int GPUTPCCompressionTrackModel::Mirror() #else // Default internal track model for compression +GPUd() void GPUTPCCompressionTrackModel::Init(float x, float y, float z, float alpha, unsigned char qPt, const GPUParam& GPUrestrict() param) +{ + // initialize track model + mX = x; + mAlpha = alpha; + mCosAlpha = CAMath::Cos(alpha); + mSinAlpha = CAMath::Sin(alpha); + mP[0] = y; + mP[1] = z; + mP[2] = 0.f; + mP[3] = 0.f; + mP[4] = (qPt - 127.f) * (20.f / 127.f); + resetCovariance(); + mNDF = -5; + mBz = param.par.ConstBz; + float pti = CAMath::Abs(mP[4]); + if (pti < 1.e-4f) { + pti = 1.e-4f; // set 10.000 GeV momentum for straight track + } + mTrk.x = x; + mTrk.y = y; + mTrk.z = z; + mTrk.q = (mP[4] >= 0) ? 1.f : -1.f; + mTrk.pt = 1.f / pti; + mTrk.p = mTrk.pt; + mTrk.px = mTrk.pt; + mTrk.py = 0.f; + mTrk.pz = 0.f; + mTrk.qpt = mTrk.q * pti; + calculateMaterialCorrection(); +} + +GPUd() int GPUTPCCompressionTrackModel::Propagate(float x, float alpha) +{ + // constrain sin(phi) + if (mP[2] > MaxSinPhi) { + mP[2] = MaxSinPhi; + } else if (mP[2] < -MaxSinPhi) { + mP[2] = -MaxSinPhi; + } + // propagate track parameters to specified x + if (CAMath::Abs(alpha - mAlpha) > 1.e-4) { + if (rotateToAlpha(alpha) != 0) { + return -2; + } + } + if (CAMath::Abs(x - mX) < 1.e-7f) { + mX = x; + return 0; + } + + // propagate mTrk to t0e + PhysicalTrackModel t0e(mTrk); + float dLp = 0; + if (CAMath::Abs(x - t0e.x) < 1.e-8f) { + return 0; + } + if (propagateToXBzLightNoUpdate(t0e, x, mBz, dLp)) { + return 1; + } + updatePhysicalTrackValues(t0e); + if (CAMath::Abs(t0e.sinphi) >= MaxSinPhi) { + return -3; + } + return followLinearization(t0e, mBz, dLp); +} + +GPUd() int GPUTPCCompressionTrackModel::Filter(float y, float z, int iRow) +{ + // apply kalman filter update with measurement y/z + float err2Y, err2Z; + getClusterRMS2(iRow, z, mTrk.sinphi, mTrk.dzds, err2Y, err2Z); + if (mNDF == -5) { + // first measurement: no need to filter, as the result is known in advance. so just set it + // ignore offline statistical errors for now (as is also done by default) + mP[0] = y; + mP[1] = z; + mC[0] = err2Y; + mC[2] = err2Z; + mNDF = -3; + return 0; + } + + // constrain sin(phi) + if (mP[2] > MaxSinPhi) { + mP[2] = MaxSinPhi; + } else if (mP[2] < -MaxSinPhi) { + mP[2] = -MaxSinPhi; + } + + const float d00 = mC[0], d01 = mC[1], d02 = mC[3], d03 = mC[6], d04 = mC[10]; + const float d10 = mC[1], d11 = mC[2], d12 = mC[4], d13 = mC[7], d14 = mC[11]; + + const float z0 = y - mP[0]; + const float z1 = z - mP[1]; + float w0, w1, w2; + if (mNDF <= 0) { + w0 = 1.f / (err2Y + d00); + w1 = 0; + w2 = 1.f / (err2Z + d11); + } else { + w0 = d11 + err2Z, w1 = d10, w2 = d00 + err2Y; + { // Invert symmetric matrix + float det = w0 * w2 - w1 * w1; + if (CAMath::Abs(det) < 1.e-10f) { + return -1; + } + det = 1.f / det; + w0 = w0 * det; + w1 = -w1 * det; + w2 = w2 * det; + } + } + mNDF += 2; + + if (mNDF <= 0) { + const float k00 = d00 * w0; + const float k20 = d02 * w0; + const float k40 = d04 * w0; + const float k11 = d11 * w2; + const float k31 = d13 * w2; + mP[0] += k00 * z0; + mP[1] += k11 * z1; + mP[2] += k20 * z0; + mP[3] += k31 * z1; + mP[4] += k40 * z0; + + mC[0] -= k00 * d00; + mC[2] -= k11 * d11; + mC[3] -= k20 * d00; + mC[5] -= k20 * d02; + mC[7] -= k31 * d11; + mC[9] -= k31 * d13; + mC[10] -= k00 * d04; + mC[12] -= k40 * d02; + mC[14] -= k40 * d04; + } else { + const float k00 = d00 * w0 + d01 * w1; + const float k01 = d00 * w1 + d10 * w2; + const float k10 = d01 * w0 + d11 * w1; + const float k11 = d01 * w1 + d11 * w2; + const float k20 = d02 * w0 + d12 * w1; + const float k21 = d02 * w1 + d12 * w2; + const float k30 = d03 * w0 + d13 * w1; + const float k31 = d03 * w1 + d13 * w2; + const float k40 = d04 * w0 + d14 * w1; + const float k41 = d04 * w1 + d14 * w2; + + mP[0] += k00 * z0 + k01 * z1; + mP[1] += k10 * z0 + k11 * z1; + mP[2] += k20 * z0 + k21 * z1; + mP[3] += k30 * z0 + k31 * z1; + mP[4] += k40 * z0 + k41 * z1; + + mC[0] -= k00 * d00 + k01 * d10; + + mC[2] -= k10 * d01 + k11 * d11; + + mC[3] -= k20 * d00 + k21 * d10; + mC[5] -= k20 * d02 + k21 * d12; + + mC[7] -= k30 * d01 + k31 * d11; + mC[9] -= k30 * d03 + k31 * d13; + + mC[10] -= k40 * d00 + k41 * d10; + mC[12] -= k40 * d02 + k41 * d12; + mC[14] -= k40 * d04 + k41 * d14; + + mC[1] -= k10 * d00 + k11 * d10; + mC[4] -= k20 * d01 + k21 * d11; + mC[6] -= k30 * d00 + k31 * d10; + mC[8] -= k30 * d02 + k31 * d12; + mC[11] -= k40 * d01 + k41 * d11; + mC[13] -= k40 * d03 + k41 * d13; + } + return 0; +} + +GPUd() int GPUTPCCompressionTrackModel::Mirror() +{ + float dy = -2.f * mTrk.q * mTrk.px / mBz; + float dS; // path in XY + { + float chord = dy; // chord to the extrapolated point == |dy|*sign(x direction) + float sa = -mTrk.cosphi; // sin( half of the rotation angle ) == (chord/2) / radius + + // dS = (Pt/b)*2*arcsin( sa ) + // = (Pt/b)*2*sa*(1 + 1/6 sa^2 + 3/40 sa^4 + 5/112 sa^6 +... ) + // = chord*(1 + 1/6 sa^2 + 3/40 sa^4 + 5/112 sa^6 +... ) + + float sa2 = sa * sa; + const float k2 = 1.f / 6.f; + const float k4 = 3.f / 40.f; + // const float k6 = 5.f/112.f; + dS = chord + chord * sa2 * (k2 + k4 * sa2); + // dS = sqrtf(pt2)/b*2.*CAMath::ASin( sa ); + } + + if (mTrk.sinphi < 0.f) { + dS = -dS; + } + + mTrk.y = mTrk.y + 2.f * dy; // TODO check why dy is added TWICE to the track position + mTrk.z = mTrk.z + 2.f * mTrk.dzds * dS; + changeDirection(); + + // Energy Loss + + float dL = CAMath::Copysign(dS * mTrk.dlds, -1.f); // we are in flight direction + + float& mC40 = mC[10]; + float& mC41 = mC[11]; + float& mC42 = mC[12]; + float& mC43 = mC[13]; + float& mC44 = mC[14]; + + float dLmask = 0.f; + bool maskMS = (CAMath::Abs(dL) < mMaterial.DLMax); + if (maskMS) { + dLmask = dL; + } + float dLabs = CAMath::Abs(dLmask); + float corr = 1.f - mMaterial.EP2 * dLmask; + + float corrInv = 1.f / corr; + mTrk.px *= corrInv; + mTrk.py *= corrInv; + mTrk.pz *= corrInv; + mTrk.pt *= corrInv; + mTrk.p *= corrInv; + mTrk.qpt *= corr; + + mP[4] *= corr; + + mC40 *= corr; + mC41 *= corr; + mC42 *= corr; + mC43 *= corr; + mC44 = mC44 * corr * corr + dLabs * mMaterial.sigmadE2; + + return 0; +} + +GPUd() void GPUTPCCompressionTrackModel::updatePhysicalTrackValues(PhysicalTrackModel& trk) +{ + float px = trk.px; + if (CAMath::Abs(px) < 1.e-4f) { + px = CAMath::Copysign(1.e-4f, px); + } + + trk.pt = sqrt(px * px + trk.py * trk.py); + float pti = 1.f / trk.pt; + trk.p = sqrt(px * px + trk.py * trk.py + trk.pz * trk.pz); + trk.sinphi = trk.py * pti; + trk.cosphi = px * pti; + trk.secphi = trk.pt / px; + trk.dzds = trk.pz * pti; + trk.dlds = trk.p * pti; + trk.qpt = trk.q * pti; +} + +GPUd() void GPUTPCCompressionTrackModel::changeDirection() +{ + mTrk.py = -mTrk.py; + mTrk.pz = -mTrk.pz; + mTrk.q = -mTrk.q; + mTrk.sinphi = -mTrk.sinphi; + mTrk.dzds = -mTrk.dzds; + mTrk.qpt = -mTrk.qpt; + updatePhysicalTrackValues(mTrk); + + mC[3] = -mC[3]; + mC[4] = -mC[4]; + mC[6] = -mC[6]; + mC[7] = -mC[7]; + mC[10] = -mC[10]; + mC[11] = -mC[11]; +} + +GPUd() int GPUTPCCompressionTrackModel::rotateToAlpha(float newAlpha) +{ + // + // Rotate the track coordinate system in XY to the angle newAlpha + // return value is error code (0==no error) + // + + float newCosAlpha = CAMath::Cos(newAlpha); + float newSinAlpha = CAMath::Sin(newAlpha); + + float cc = newCosAlpha * mCosAlpha + newSinAlpha * mSinAlpha; // cos(newAlpha - mAlpha); + float ss = newSinAlpha * mCosAlpha - newCosAlpha * mSinAlpha; // sin(newAlpha - mAlpha); + + PhysicalTrackModel t0 = mTrk; + + float x0 = mTrk.x; + float y0 = mTrk.y; + float px0 = mTrk.px; + float py0 = mTrk.py; + + if (CAMath::Abs(mP[2]) >= MaxSinPhi || CAMath::Abs(px0) < (1 - MaxSinPhi)) { + return -1; + } + + // rotate t0 track + float px1 = px0 * cc + py0 * ss; + float py1 = -px0 * ss + py0 * cc; + + { + t0.x = x0 * cc + y0 * ss; + t0.y = -x0 * ss + y0 * cc; + t0.px = px1; + t0.py = py1; + updatePhysicalTrackValues(t0); + } + + if (CAMath::Abs(py1) > MaxSinPhi * mTrk.pt || CAMath::Abs(px1) < (1 - MaxSinPhi)) { + return -1; + } + + // calculate X of rotated track: + float trackX = x0 * cc + ss * mP[0]; + + // transport t0 to trackX + float dLp = 0; + if (propagateToXBzLightNoUpdate(t0, trackX, mBz, dLp)) { + return -1; + } + updatePhysicalTrackValues(t0); + + if (CAMath::Abs(t0.sinphi) >= MaxSinPhi) { + return -1; + } + + // now t0 is rotated and propagated, all checks are passed + + // Rotate track using mTrk for linearisation. After rotation X is not fixed, but has a covariance + + // Y Z Sin DzDs q/p + // Jacobian J0 = { { j0, 0, 0, 0, 0 }, // Y + // { 0, 1, 0, 0, 0 }, // Z + // { 0, 0, j1, 0, 0 }, // SinPhi + // { 0, 0, 0, 1, 0 }, // DzDs + // { 0, 0, 0, 0, 1 }, // q/p + // { j2, 0, 0, 0, 0 } }// X (rotated ) + + float j0 = cc; + float j1 = px1 / px0; + float j2 = ss; + // float dy = mT->Y() - y0; + // float ds = mT->SinPhi() - mTrk.SinPhi(); + + mX = trackX; // == x0*cc + ss*mP[0] == t0.x + j0*dy; + mP[0] = -x0 * ss + cc * mP[0]; //== t0.y + j0*dy; + // mP[2] = py1/pt0 + j1*ds; // == t0.sinphi + j1*ds; // use py1, since t0.sinphi can have different sign + mP[2] = -CAMath::Sqrt(1.f - mP[2] * mP[2]) * ss + mP[2] * cc; + + // Rotate cov. matrix Cr = J0 x C x J0T. Cr has one more row+column for X: + float* c = mC; + + float c15 = c[0] * j0 * j2; + float c16 = c[1] * j2; + float c17 = c[3] * j1 * j2; + float c18 = c[6] * j2; + float c19 = c[10] * j2; + float c20 = c[0] * j2 * j2; + + c[0] *= j0 * j0; + c[3] *= j0; + c[10] *= j0; + + c[3] *= j1; + c[5] *= j1 * j1; + c[12] *= j1; + + if (setDirectionAlongX(t0)) { // change direction if Px < 0 + mP[2] = -mP[2]; + mP[3] = -mP[3]; + mP[4] = -mP[4]; + c[3] = -c[3]; // covariances with SinPhi + c[4] = -c[4]; + c17 = -c17; + c[6] = -c[6]; // covariances with DzDs + c[7] = -c[7]; + c18 = -c18; + c[10] = -c[10]; // covariances with QPt + c[11] = -c[11]; + c19 = -c19; + } + + // Now fix the X coordinate: so to say, transport track T to fixed X = mX. + // only covariance changes. Use rotated and transported t0 for linearisation + float j3 = -t0.py / t0.px; + float j4 = -t0.pz / t0.px; + float j5 = t0.qpt * mBz; + + // Y Z Sin DzDs q/p X + // Jacobian J1 = { { 1, 0, 0, 0, 0, j3 }, // Y + // { 0, 1, 0, 0, 0, j4 }, // Z + // { 0, 0, 1, 0, 0, j5 }, // SinPhi + // { 0, 0, 0, 1, 0, 0 }, // DzDs + // { 0, 0, 0, 0, 1, 0 } }; // q/p + + float h15 = c15 + c20 * j3; + float h16 = c16 + c20 * j4; + float h17 = c17 + c20 * j5; + + c[0] += j3 * (c15 + h15); + + c[2] += j4 * (c16 + h16); + + c[3] += c17 * j3 + h15 * j5; + c[5] += j5 * (c17 + h17); + + c[7] += c18 * j4; + // c[ 9] = c[ 9]; + + c[10] += c19 * j3; + c[12] += c19 * j5; + // c[14] = c[14]; + + mAlpha = newAlpha; + mCosAlpha = newCosAlpha; + mSinAlpha = newSinAlpha; + mTrk = t0; + + return 0; +} + +GPUd() int GPUTPCCompressionTrackModel::propagateToXBzLightNoUpdate(PhysicalTrackModel& t, float x, float Bz, float& dLp) +{ + // + // transport the track to X=x in magnetic field B = ( 0, 0, Bz[kG*0.000299792458] ) + // dLp is a return value == path length / track momentum [cm/(GeV/c)] + // the method returns error code (0 == no error) + // + // Additional values are not recalculated, UpdateValues() has to be called afterwards!! + // + float b = t.q * Bz; + float pt2 = t.px * t.px + t.py * t.py; + float dx = x - t.x; + float pye = t.py - dx * b; // extrapolated py + float pxe2 = pt2 - pye * pye; + + if (t.px < (1.f - MaxSinPhi) || pxe2 < (1.f - MaxSinPhi) * (1.f - MaxSinPhi)) { + return -1; // can not transport to x=x + } + float pxe = CAMath::Sqrt(pxe2); // extrapolated px + float pti = 1.f / CAMath::Sqrt(pt2); + + float ty = (t.py + pye) / (t.px + pxe); + float dy = dx * ty; + float dS; // path in XY + { + float chord = dx * CAMath::Sqrt(1.f + ty * ty); // chord to the extrapolated point == sqrt(dx^2+dy^2)*sign(dx) + float sa = 0.5f * chord * b * pti; // sin( half of the rotation angle ) == (chord/2) / radius + + // dS = (Pt/b)*2*arcsin( sa ) + // = (Pt/b)*2*sa*(1 + 1/6 sa^2 + 3/40 sa^4 + 5/112 sa^6 +... ) + // = chord*(1 + 1/6 sa^2 + 3/40 sa^4 + 5/112 sa^6 +... ) + + float sa2 = sa * sa; + const float k2 = 1.f / 6.f; + const float k4 = 3.f / 40.f; + // const float k6 = 5.f/112.f; + dS = chord + chord * sa2 * (k2 + k4 * sa2); + // dS = sqrt(pt2)/b*2.*CAMath::ASin( sa ); + } + + dLp = pti * dS; // path in XYZ / p == path in XY / pt + + float dz = t.pz * dLp; + + t.x = x; + t.y += dy; + t.z += dz; + t.px = pxe; + t.py = pye; + return 0; +} + +GPUd() bool GPUTPCCompressionTrackModel::setDirectionAlongX(PhysicalTrackModel& t) +{ + // + // set direction of movenment collinear to X axis + // return value is true when direction has been changed + // + if (t.px >= 0) { + return 0; + } + + t.px = -t.px; + t.py = -t.py; + t.pz = -t.pz; + t.q = -t.q; + updatePhysicalTrackValues(t); + return 1; +} + +GPUd() int GPUTPCCompressionTrackModel::followLinearization(const PhysicalTrackModel& t0e, float Bz, float dLp) +{ + // t0e is alrerady extrapolated t0 + + // propagate track and cov matrix with derivatives for (0,0,Bz) field + + float dS = dLp * t0e.pt; + float dL = CAMath::Abs(dLp * t0e.p); + + dL = -dL; // we are always in flight direction + + float ey = mTrk.sinphi; + float ex = mTrk.cosphi; + float exi = mTrk.secphi; + float ey1 = t0e.sinphi; + float ex1 = t0e.cosphi; + float ex1i = t0e.secphi; + + float k = -mTrk.qpt * Bz; + float dx = t0e.x - mTrk.x; + float kdx = k * dx; + float cc = ex + ex1; + float cci = 1.f / cc; + + float dxcci = dx * cci; + float hh = dxcci * ex1i * (1.f + ex * ex1 + ey * ey1); + + float j02 = exi * hh; + float j04 = -Bz * dxcci * hh; + float j13 = dS; + float j24 = -dx * Bz; + + float* p = mP; + + float d0 = p[0] - mTrk.y; + float d1 = p[1] - mTrk.z; + float d2 = p[2] - mTrk.sinphi; + float d3 = p[3] - mTrk.dzds; + float d4 = p[4] - mTrk.qpt; + + float newSinPhi = ey1 + d2 + j24 * d4; + if (mNDF >= 15 && CAMath::Abs(newSinPhi) > MaxSinPhi) { + return -4; + } + + mTrk = t0e; + mX = t0e.x; + p[0] = t0e.y + d0 + j02 * d2 + j04 * d4; + p[1] = t0e.z + d1 + j13 * d3; + p[2] = newSinPhi; + p[3] = t0e.dzds + d3; + p[4] = t0e.qpt + d4; + + float* c = mC; + + float c20 = c[3]; + float c21 = c[4]; + float c22 = c[5]; + + float c30 = c[6]; + float c31 = c[7]; + float c32 = c[8]; + float c33 = c[9]; + + float c40 = c[10]; + float c41 = c[11]; + float c42 = c[12]; + float c43 = c[13]; + float c44 = c[14]; + + if (mNDF <= 0) { + float c20ph04c42 = c20 + j04 * c42; + float j02c22 = j02 * c22; + float j04c44 = j04 * c44; + + float n6 = c30 + j02 * c32 + j04 * c43; + float n7 = c31 + j13 * c33; + float n10 = c40 + j02 * c42 + j04c44; + float n11 = c41 + j13 * c43; + float n12 = c42 + j24 * c44; + + c[0] += j02 * j02c22 + j04 * j04c44 + 2.f * (j02 * c20ph04c42 + j04 * c40); + c[1] += j02 * c21 + j04 * c41 + j13 * n6; + c[2] += j13 * (c31 + n7); + c[3] = c20ph04c42 + j02c22 + j24 * n10; + c[4] = c21 + j13 * c32 + j24 * n11; + c[5] = c22 + j24 * (c42 + n12); + c[6] = n6; + c[7] = n7; + c[8] = c32 + c43 * j24; + c[10] = n10; + c[11] = n11; + c[12] = n12; + } else { + float c00 = c[0]; + float c10 = c[1]; + float c11 = c[2]; + + float ss = ey + ey1; + float tg = ss * cci; + float xx = 1.f - 0.25f * kdx * kdx * (1.f + tg * tg); + if (xx < 1.e-8f) { + return -1; + } + xx = CAMath::Sqrt(xx); + float yy = CAMath::Sqrt(ss * ss + cc * cc); + + float j12 = dx * mTrk.dzds * tg * (2.f + tg * (ey * exi + ey1 * ex1i)) / (xx * yy); + float j14 = 0; + if (CAMath::Abs(mTrk.qpt) > 1.e-6f) { + j14 = (2.f * xx * ex1i * dx / yy - dS) * mTrk.dzds / mTrk.qpt; + } else { + j14 = -mTrk.dzds * Bz * dx * dx * exi * exi * exi * (0.5f * ey + (1.f / 3.f) * kdx * (1 + 2.f * ey * ey) * exi * exi); + } + + p[1] += j12 * d2 + j14 * d4; + + float h00 = c00 + c20 * j02 + c40 * j04; + // float h01 = c10 + c21*j02 + c41*j04; + float h02 = c20 + c22 * j02 + c42 * j04; + // float h03 = c30 + c32*j02 + c43*j04; + float h04 = c40 + c42 * j02 + c44 * j04; + + float h10 = c10 + c20 * j12 + c30 * j13 + c40 * j14; + float h11 = c11 + c21 * j12 + c31 * j13 + c41 * j14; + float h12 = c21 + c22 * j12 + c32 * j13 + c42 * j14; + float h13 = c31 + c32 * j12 + c33 * j13 + c43 * j14; + float h14 = c41 + c42 * j12 + c43 * j13 + c44 * j14; + + float h20 = c20 + c40 * j24; + float h21 = c21 + c41 * j24; + float h22 = c22 + c42 * j24; + float h23 = c32 + c43 * j24; + float h24 = c42 + c44 * j24; + + c[0] = h00 + h02 * j02 + h04 * j04; + + c[1] = h10 + h12 * j02 + h14 * j04; + c[2] = h11 + h12 * j12 + h13 * j13 + h14 * j14; + + c[3] = h20 + h22 * j02 + h24 * j04; + c[4] = h21 + h22 * j12 + h23 * j13 + h24 * j14; + c[5] = h22 + h24 * j24; + + c[6] = c30 + c32 * j02 + c43 * j04; + c[7] = c31 + c32 * j12 + c33 * j13 + c43 * j14; + c[8] = c32 + c43 * j24; + // c[ 9] = c33; + + c[10] = c40 + c42 * j02 + c44 * j04; + c[11] = c41 + c42 * j12 + c43 * j13 + c44 * j14; + c[12] = c42 + c44 * j24; + // c[13] = c43; + // c[14] = c44; + } + + float& mC22 = c[5]; + float& mC33 = c[9]; + float& mC40 = c[10]; + float& mC41 = c[11]; + float& mC42 = c[12]; + float& mC43 = c[13]; + float& mC44 = c[14]; + + float dLmask = 0.f; + bool maskMS = (CAMath::Abs(dL) < mMaterial.DLMax); + if (maskMS) { + dLmask = dL; + } + float dLabs = CAMath::Abs(dLmask); + + // Energy Loss + { + // std::cout<<"APPLY ENERGY LOSS!!!"<<std::endl; + float corr = 1.f - mMaterial.EP2 * dLmask; + float corrInv = 1.f / corr; + mTrk.px *= corrInv; + mTrk.py *= corrInv; + mTrk.pz *= corrInv; + mTrk.pt *= corrInv; + mTrk.p *= corrInv; + mTrk.qpt *= corr; + + p[4] *= corr; + + mC40 *= corr; + mC41 *= corr; + mC42 *= corr; + mC43 *= corr; + mC44 = mC44 * corr * corr + dLabs * mMaterial.sigmadE2; + } + // Multiple Scattering + + { + mC22 += dLabs * mMaterial.k22 * mTrk.cosphi * mTrk.cosphi; + mC33 += dLabs * mMaterial.k33; + mC43 += dLabs * mMaterial.k43; + mC44 += dLabs * mMaterial.k44; + } + + return 0; +} + +GPUd() void GPUTPCCompressionTrackModel::calculateMaterialCorrection() +{ + const float mass = 0.13957f; + + float qpt = mTrk.qpt; + if (CAMath::Abs(qpt) > 20) { + qpt = 20; + } + + float w2 = (1.f + mTrk.dzds * mTrk.dzds); //==(P/pt)2 + float pti2 = qpt * qpt; + if (pti2 < 1.e-4f) { + pti2 = 1.e-4f; + } + + float mass2 = mass * mass; + float beta2 = w2 / (w2 + mass2 * pti2); + + float p2 = w2 / pti2; // impuls 2 + float betheRho = approximateBetheBloch(p2 / mass2) * mMaterial.rho; + float E = CAMath::Sqrt(p2 + mass2); + float theta2 = (14.1f * 14.1f / 1.e6f) / (beta2 * p2) * mMaterial.radLenInv; + + mMaterial.EP2 = E / p2; + + // Approximate energy loss fluctuation (M.Ivanov) + + const float knst = 0.07f; // To be tuned. + mMaterial.sigmadE2 = knst * mMaterial.EP2 * qpt; + mMaterial.sigmadE2 = mMaterial.sigmadE2 * mMaterial.sigmadE2; + + mMaterial.k22 = theta2 * w2; + mMaterial.k33 = mMaterial.k22 * w2; + mMaterial.k43 = 0.f; + mMaterial.k44 = theta2 * mTrk.dzds * mTrk.dzds * pti2; + + float br = (betheRho > 1.e-8f) ? betheRho : 1.e-8f; + mMaterial.DLMax = 0.3f * E / br; + mMaterial.EP2 *= betheRho; + mMaterial.sigmadE2 = mMaterial.sigmadE2 * betheRho; // + mMaterial.fK44; +} + +GPUd() float GPUTPCCompressionTrackModel::approximateBetheBloch(float beta2) +{ + //------------------------------------------------------------------ + // This is an approximation of the Bethe-Bloch formula with + // the density effect taken into account at beta*gamma > 3.5 + // (the approximation is reasonable only for solid materials) + //------------------------------------------------------------------ + + const float log0 = log(5940.f); + const float log1 = log(3.5f * 5940.f); + + bool bad = (beta2 >= .999f) || (beta2 < 1.e-8f); + + if (bad) { + return 0.f; + } + + float a = beta2 / (1.f - beta2); + float b = 0.5f * log(a); + float d = 0.153e-3f / beta2; + float c = b - beta2; + + float ret = d * (log0 + b + c); + float case1 = d * (log1 + c); + + if (a > 3.5f * 3.5f) { + ret = case1; + } + + return ret; +} + +GPUd() void GPUTPCCompressionTrackModel::getClusterRMS2(int iRow, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const +{ + // Only O2 geometry considered at the moment. Is AliRoot geometry support needed? + int rowType = iRow < 97 ? (iRow < 63 ? 0 : 1) : (iRow < 127 ? 2 : 3); + if (rowType > 2) { + rowType = 2; // TODO: Add type 3 + } + z = CAMath::Abs((250.f - 0.275f) - CAMath::Abs(z)); + float s2 = sinPhi * sinPhi; + if (s2 > 0.95f * 0.95f) { + s2 = 0.95f * 0.95f; + } + float sec2 = 1.f / (1.f - s2); + float angleY2 = s2 * sec2; // dy/dx + float angleZ2 = DzDs * DzDs * sec2; // dz/dx + + const float* cY = mParamRMS0[0][rowType]; + ErrY2 = cY[0] + cY[1] * z + cY[2] * angleY2; + ErrY2 *= ErrY2; + + const float* cZ = mParamRMS0[1][rowType]; + ErrZ2 = cZ[0] + cZ[1] * z + cZ[2] * angleZ2; + ErrZ2 *= ErrZ2; +} + +GPUd() void GPUTPCCompressionTrackModel::resetCovariance() +{ + mC[0] = 100.f; + mC[1] = 0.f; + mC[2] = 100.f; + mC[3] = 0.f; + mC[4] = 0.f; + mC[5] = 1.f; + mC[6] = 0.f; + mC[7] = 0.f; + mC[8] = 0.f; + mC[9] = 10.f; + mC[10] = 0.f; + mC[11] = 0.f; + mC[12] = 0.f; + mC[13] = 0.f; + mC[14] = 10.f; +} + #endif diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.h b/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.h index 3b1ccf89cb981..7f13e434bea45 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionTrackModel.h @@ -15,7 +15,7 @@ #define GPUTPCCOMPRESSIONTRACKMODEL_H // For debugging purposes, we provide means to use other track models -#define GPUCA_COMPRESSION_TRACK_MODEL_MERGER +// #define GPUCA_COMPRESSION_TRACK_MODEL_MERGER // #define GPUCA_COMPRESSION_TRACK_MODEL_SLICETRACKER #include "GPUDef.h" @@ -28,12 +28,9 @@ #include "GPUTPCTrackParam.h" #else // Default internal track model for compression -#error Not yet implemented #endif -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { // ATTENTION! This track model is used for the data compression. // Changes to the propagation and fit will prevent the decompression of data @@ -41,6 +38,8 @@ namespace gpu struct GPUParam; +constexpr float MaxSinPhi = 0.999f; + class GPUTPCCompressionTrackModel { public: @@ -62,24 +61,97 @@ class GPUTPCCompressionTrackModel #else // Default internal track model for compression + struct PhysicalTrackModel { // see GPUTPCGMPhysicalTrackModel + // physical parameters of the trajectory + + float x = 0.f; // X + float y = 0.f; // Y + float z = 0.f; // Z + float px = 1.e4f; // Px, >0 + float py = 0.f; // Py + float pz = 0.f; // Pz + float q = 1.f; // charge, +-1 + + // some additional variables needed for GMTrackParam transport + + float sinphi = 0.f; // SinPhi = Py/Pt + float cosphi = 1.f; // CosPhi = abs(Px)/Pt + float secphi = 1.f; // 1/cos(phi) = Pt/abs(Px) + float dzds = 0.f; // DzDs = Pz/Pt + float dlds = 1.f; // DlDs = P/Pt + float qpt = 0.f; // QPt = q/Pt + float p = 1.e4f; // momentum + float pt = 1.e4f; // Pt momentum + }; + + GPUd() float Y() const { return mP[0]; } + GPUd() float Z() const { return mP[1]; } + + // helper functions for standalone propagation and update methods + GPUd() void updatePhysicalTrackValues(PhysicalTrackModel& trk); + GPUd() void changeDirection(); + GPUd() int rotateToAlpha(float newAlpha); + GPUd() int propagateToXBzLightNoUpdate(PhysicalTrackModel& t, float x, float Bz, float& dLp); + GPUd() bool setDirectionAlongX(PhysicalTrackModel& t); + GPUd() int followLinearization(const PhysicalTrackModel& t0e, float Bz, float dLp); + GPUd() void calculateMaterialCorrection(); + GPUd() float approximateBetheBloch(float beta2); + GPUd() void getClusterRMS2(int iRow, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const; + GPUd() void resetCovariance(); + #endif protected: - const GPUParam* mParam; #ifdef GPUCA_COMPRESSION_TRACK_MODEL_MERGER GPUTPCGMPropagator mProp; GPUTPCGMTrackParam mTrk; + const GPUParam* mParam; #elif defined(GPUCA_COMPRESSION_TRACK_MODEL_SLICETRACKER) GPUTPCTrackParam mTrk; float mAlpha; + const GPUParam* mParam; #else // Default internal track model for compression + struct MaterialCorrection { + GPUhd() MaterialCorrection() : radLen(28811.7f), rho(1.025e-3f), radLenInv(1.f / radLen), DLMax(0.f), EP2(0.f), sigmadE2(0.f), k22(0.f), k33(0.f), k43(0.f), k44(0.f) {} + + float radLen; // [cm] + float rho; // [g/cm^3] + float radLenInv, DLMax, EP2, sigmadE2, k22, k33, k43, k44; // precalculated values for MS and EnergyLoss correction + }; + + // default TPC cluster error parameterization taken from GPUParam.cxx + // clang-format off + const float mParamRMS0[2][3][4] = + { + { { 4.17516864836e-02, 1.87623649254e-04, 5.63788712025e-02, 5.38373768330e-01, }, + { 8.29434990883e-02, 2.03291710932e-04, 6.81538805366e-02, 9.70965325832e-01, }, + { 8.67543518543e-02, 2.10733342101e-04, 1.38366967440e-01, 2.55089461803e-01, } + }, { + { 5.96254616976e-02, 8.62886518007e-05, 3.61776389182e-02, 4.79704320431e-01, }, + { 6.12571723759e-02, 7.23929333617e-05, 3.93057651818e-02, 9.29222583771e-01, }, + { 6.58465921879e-02, 1.03639606095e-04, 6.07583411038e-02, 9.90289509296e-01, } } + }; + // clang-format on + + float mX; + float mAlpha; + float mP[5]; + float mC[15]; + int mNDF = -5; + float mCosAlpha; + float mSinAlpha; + + // propagation parameters + float mBz; + MaterialCorrection mMaterial; + + PhysicalTrackModel mTrk; #endif }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx index fa55bbe7f0988..51df22a39a175 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx @@ -38,6 +38,7 @@ int TPCClusterDecompressor::decompress(const CompressedClusters* clustersCompres { std::vector<ClusterNative> clusters[NSLICES][GPUCA_ROW_COUNT]; unsigned int offset = 0; + float zOffset = 0; for (unsigned int i = 0; i < clustersCompressed->nTracks; i++) { unsigned int slice = clustersCompressed->sliceA[i]; unsigned int row = clustersCompressed->rowA[i]; @@ -75,7 +76,7 @@ int TPCClusterDecompressor::decompress(const CompressedClusters* clustersCompres if (timeTmp & 800000) { timeTmp |= 0xFF000000; } - time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z()))); + time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z() + zOffset))); float tmpPad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(slice, row, track.Y()))); pad = clustersCompressed->padResA[offset - i - 1] + ClusterNative::packPad(tmpPad); } else { @@ -87,9 +88,10 @@ int TPCClusterDecompressor::decompress(const CompressedClusters* clustersCompres float y = param.tpcGeometry.LinearPad2Y(slice, row, clusterVector.back().getPad()); float z = param.tpcGeometry.LinearTime2Z(slice, clusterVector.back().getTime()); if (j == 0) { - track.Init(param.tpcGeometry.Row2X(row), y, z, param.SliceParam[slice].Alpha, clustersCompressed->qPtA[i], param); + zOffset = z; + track.Init(param.tpcGeometry.Row2X(row), y, z - zOffset, param.SliceParam[slice].Alpha, clustersCompressed->qPtA[i], param); } - if (j + 1 < clustersCompressed->nTrackClusters[i] && track.Filter(y, z, row)) { + if (j + 1 < clustersCompressed->nTrackClusters[i] && track.Filter(y, z - zOffset, row)) { offset += clustersCompressed->nTrackClusters[i] - j; break; } diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h index 0922339de22c1..991151bade1e9 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h @@ -18,18 +18,13 @@ #include <vector> #include <functional> -namespace o2 -{ -namespace tpc +namespace o2::tpc { struct ClusterNativeAccess; struct ClusterNative; -} // namespace tpc -} // namespace o2 +} // namespace o2::tpc -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct GPUParam; @@ -42,7 +37,6 @@ class TPCClusterDecompressor protected: }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h index df36878f8dee3..104303af7cff6 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h @@ -18,6 +18,17 @@ #pragma link off all functions; #pragma link C++ class o2::gpu::GPUTPCO2Interface + ; +#pragma link C++ class o2::gpu::GPUTPCO2InterfaceRefit + ; +#pragma link C++ class o2::gpu::GPUO2InterfaceQA + ; #pragma link C++ class o2::gpu::TPCdEdxCalibrationSplines + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsO2 + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRec + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessing + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplay + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayLight + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsQA + ; +#pragma link C++ class o2::gpu::trackInterface < o2::dataformats::TrackTPCITS> + ; +#pragma link C++ class o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::dataformats::TrackTPCITS>> + ; +#pragma link C++ class std::vector < o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::dataformats::TrackTPCITS>>> + ; #endif diff --git a/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.cxx b/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.cxx index 5a7406188f9e6..d88ae53768a2a 100644 --- a/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.cxx +++ b/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.cxx @@ -54,7 +54,7 @@ using namespace GPUCA_NAMESPACE::gpu; -AliHLTGPUDumpComponent::AliHLTGPUDumpComponent() : fSolenoidBz(0.f), fRec(nullptr), fChain(nullptr), fFastTransformManager(new TPCFastTransformManager), fCalib(nullptr), fRecParam(nullptr), fOfflineRecoParam(), fOrigTransform(nullptr), fIsMC(false) +AliHLTGPUDumpComponent::AliHLTGPUDumpComponent() : fSolenoidBz(0.f), fRec(nullptr), fChain(nullptr), fFastTransformManager(new TPCFastTransformManager), fCalib(nullptr), fRecParam(nullptr), fOfflineRecoParam(), fOrigTransform(nullptr), fIsMC(false), fInitTimestamp(0.) { fRec = GPUReconstruction::CreateInstance(); fChain = fRec->AddChain<GPUChainTracking>(); @@ -150,6 +150,7 @@ int AliHLTGPUDumpComponent::DoInit(int argc, const char** argv) HLTFatal("No TPC Reco Param entry found for the given event specification"); } fCalib->GetTransform()->SetCurrentRecoParam(fRecParam); + fInitTimestamp = GetTimeStamp(); return 0; } @@ -252,8 +253,21 @@ int AliHLTGPUDumpComponent::DoEvent(const AliHLTComponentEventData& evtData, con #endif AliHLTTPCRawCluster tmp = cRaw; tmp.fPadRow += firstRow; + if ((unsigned int)cluster.amp >= 25 * 1024) { + GPUError("Invalid cluster charge, truncating (%d >= %d)", (int)cluster.amp, 25 * 1024); + cluster.amp = 25 * 1024 - 1; + } + if ((unsigned int)tmp.GetCharge() >= 25 * 1024) { + GPUError("Invalid raw cluster charge, truncating (%d >= %d)", (int)tmp.GetCharge(), 25 * 1024); + tmp.SetCharge(25 * 1024 - 1); + } + if ((unsigned int)tmp.GetQMax() >= 1024) { + GPUError("Invalid raw cluster charge max, truncating (%d >= %d)", (int)tmp.GetQMax(), 1024); + tmp.SetQMax(1024 - 1); + } clusterData[slice].emplace_back(cluster); rawClusters[slice].emplace_back(tmp); + nClustersTotal++; } } @@ -447,7 +461,7 @@ int AliHLTGPUDumpComponent::DoEvent(const AliHLTComponentEventData& evtData, con if (nEvent == 0) { std::unique_ptr<TPCFastTransform> fFastTransformIRS(new TPCFastTransform); - long TimeStamp = GetTimeStamp(); + long TimeStamp = (getenv("DUMP_TIMESTAMP_SOR") && atoi(getenv("DUMP_TIMESTAMP_SOR"))) ? fInitTimestamp : GetTimeStamp(); if (fIsMC && !fRecParam->GetUseCorrectionMap()) { TimeStamp = 0; } diff --git a/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.h b/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.h index c1514e41bcfa3..c30d31de544b4 100644 --- a/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.h +++ b/GPU/GPUTracking/Global/AliHLTGPUDumpComponent.h @@ -68,6 +68,7 @@ class AliHLTGPUDumpComponent : public AliHLTProcessor AliRecoParam fOfflineRecoParam; AliTPCTransform* fOrigTransform; bool fIsMC; + long fInitTimestamp; }; #endif diff --git a/GPU/GPUTracking/Global/GPUChain.h b/GPU/GPUTracking/Global/GPUChain.h index ba01a99603903..255672c880fa1 100644 --- a/GPU/GPUTracking/Global/GPUChain.h +++ b/GPU/GPUTracking/Global/GPUChain.h @@ -46,7 +46,7 @@ class GPUChain virtual int RunChain() = 0; virtual void MemorySize(size_t& gpuMem, size_t& pageLockedHostMem) = 0; virtual void PrintMemoryStatistics(){}; - virtual int CheckErrorCodes() { return 0; } + virtual int CheckErrorCodes(bool cpuOnly = false) { return 0; } virtual bool SupportsDoublePipeline() { return false; } virtual int FinalizePipelinedProcessing() { return 0; } @@ -57,8 +57,8 @@ class GPUChain const GPUParam& GetParam() const { return mRec->mHostConstantMem->param; } const GPUSettingsEvent& GetEventSettings() const { return mRec->mEventSettings; } + const GPUSettingsDeviceBackend& GetDeviceBackendSettings() const { return mRec->mDeviceBackendSettings; } const GPUSettingsProcessing& GetProcessingSettings() const { return mRec->mProcessingSettings; } - const GPUSettingsDeviceProcessing& GetDeviceProcessingSettings() const { return mRec->mDeviceProcessingSettings; } GPUReconstruction* rec() { return mRec; } const GPUReconstruction* rec() const { return mRec; } @@ -79,7 +79,7 @@ class GPUChain inline GPUConstantMem* processorsDevice() { return mRec->mDeviceConstantMem; } inline GPUParam& param() { return mRec->param(); } inline const GPUConstantMem* processors() const { return mRec->processors(); } - inline GPUSettingsDeviceProcessing& DeviceProcessingSettings() { return mRec->mDeviceProcessingSettings; } + inline GPUSettingsProcessing& ProcessingSettings() { return mRec->mProcessingSettings; } inline void SynchronizeStream(int stream) { mRec->SynchronizeStream(stream); } inline void SynchronizeEvents(deviceEvent* evList, int nEvents = 1) { mRec->SynchronizeEvents(evList, nEvents); } template <class T> @@ -246,7 +246,7 @@ inline void GPUChain::timeCpy(RecoStep step, int toGPU, S T::*func, Args... args } HighResTimer* timer = nullptr; size_t* bytes = nullptr; - if (mRec->mDeviceProcessingSettings.debugLevel >= 1 && toGPU >= 0) { // Todo: time special cases toGPU < 0 + if (mRec->mProcessingSettings.debugLevel >= 1 && toGPU >= 0) { // Todo: time special cases toGPU < 0 int id = mRec->getRecoStepNum(step, false); if (id != -1) { auto& tmp = mRec->mTimersRecoSteps[id]; @@ -267,11 +267,11 @@ inline void GPUChain::timeCpy(RecoStep step, int toGPU, S T::*func, Args... args template <class T, class S, typename... Args> bool GPUChain::DoDebugAndDump(GPUChain::RecoStep step, int mask, bool transfer, T& processor, S T::*func, Args&&... args) { - if (GetDeviceProcessingSettings().keepAllMemory) { + if (GetProcessingSettings().keepAllMemory) { if (transfer) { TransferMemoryResourcesToHost(step, &processor, -1, true); } - if (GetDeviceProcessingSettings().debugLevel >= 6 && (mask == 0 || (GetDeviceProcessingSettings().debugMask & mask))) { + if (GetProcessingSettings().debugLevel >= 6 && (mask == 0 || (GetProcessingSettings().debugMask & mask))) { (processor.*func)(args...); return true; } @@ -283,11 +283,11 @@ template <class T, class S, typename... Args> int GPUChain::runRecoStep(RecoStep step, S T::*func, Args... args) { if (GetRecoSteps().isSet(step)) { - if (GetDeviceProcessingSettings().debugLevel >= 1) { + if (GetProcessingSettings().debugLevel >= 1) { mRec->getRecoStepTimer(step).Start(); } int retVal = (reinterpret_cast<T*>(this)->*func)(args...); - if (GetDeviceProcessingSettings().debugLevel >= 1) { + if (GetProcessingSettings().debugLevel >= 1) { mRec->getRecoStepTimer(step).Stop(); } return retVal; diff --git a/GPU/GPUTracking/Global/GPUChainITS.cxx b/GPU/GPUTracking/Global/GPUChainITS.cxx index 90bca4dbd39d7..7552684f255d4 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.cxx +++ b/GPU/GPUTracking/Global/GPUChainITS.cxx @@ -70,13 +70,13 @@ int GPUChainITS::Finalize() { return 0; } int GPUChainITS::RunChain() { return 0; } -int GPUChainITS::PrepareAndRunITSTrackFit(std::vector<Road>& roads, std::array<const Cluster*, 7> clusters, std::array<const Cell*, 5> cells, const std::array<std::vector<TrackingFrameInfo>, 7>& tf, std::vector<TrackITSExt>& tracks) +int GPUChainITS::PrepareAndRunITSTrackFit(std::vector<o2::its::Road>& roads, std::vector<const o2::its::Cluster*>& clusters, std::vector<const o2::its::Cell*>& cells, const std::vector<std::vector<o2::its::TrackingFrameInfo>>& tf, std::vector<o2::its::TrackITSExt>& tracks) { mRec->PrepareEvent(); return RunITSTrackFit(roads, clusters, cells, tf, tracks); } -int GPUChainITS::RunITSTrackFit(std::vector<Road>& roads, std::array<const Cluster*, 7> clusters, std::array<const Cell*, 5> cells, const std::array<std::vector<TrackingFrameInfo>, 7>& tf, std::vector<TrackITSExt>& tracks) +int GPUChainITS::RunITSTrackFit(std::vector<o2::its::Road>& roads, std::vector<const o2::its::Cluster*>& clusters, std::vector<const o2::its::Cell*>& cells, const std::vector<std::vector<o2::its::TrackingFrameInfo>>& tf, std::vector<o2::its::TrackITSExt>& tracks) { auto threadContext = GetThreadContext(); bool doGPU = GetRecoStepsGPU() & RecoStep::ITSTracking; @@ -85,7 +85,7 @@ int GPUChainITS::RunITSTrackFit(std::vector<Road>& roads, std::array<const Clust Fitter.clearMemory(); Fitter.SetNumberOfRoads(roads.size()); - for (int i = 0; i < 7; i++) { + for (unsigned int i = 0; i < clusters.size(); i++) { Fitter.SetNumberTF(i, tf[i].size()); } Fitter.SetMaxData(processors()->ioPtrs); @@ -93,7 +93,7 @@ int GPUChainITS::RunITSTrackFit(std::vector<Road>& roads, std::array<const Clust std::copy(cells.begin(), cells.end(), Fitter.cells()); SetupGPUProcessor(&Fitter, true); std::copy(roads.begin(), roads.end(), Fitter.roads()); - for (int i = 0; i < 7; i++) { + for (unsigned int i = 0; i < clusters.size(); i++) { std::copy(tf[i].begin(), tf[i].end(), Fitter.trackingFrame()[i]); } @@ -113,7 +113,6 @@ int GPUChainITS::RunITSTrackFit(std::vector<Road>& roads, std::array<const Clust {trkin.Cov()[0], trkin.Cov()[1], trkin.Cov()[2], trkin.Cov()[3], trkin.Cov()[4], trkin.Cov()[5], trkin.Cov()[6], trkin.Cov()[7], trkin.Cov()[8], trkin.Cov()[9], trkin.Cov()[10], trkin.Cov()[11], trkin.Cov()[12], trkin.Cov()[13], trkin.Cov()[14]}}, (short int)((trkin.NDF() + 5) / 2), trkin.Chi2(), - 0, {trkin.mOuterParam.X, trkin.mOuterParam.alpha, {trkin.mOuterParam.P[0], trkin.mOuterParam.P[1], trkin.mOuterParam.P[2], trkin.mOuterParam.P[3], trkin.mOuterParam.P[4]}, diff --git a/GPU/GPUTracking/Global/GPUChainITS.h b/GPU/GPUTracking/Global/GPUChainITS.h index f9583aee00042..6dcf2d37b3b53 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.h +++ b/GPU/GPUTracking/Global/GPUChainITS.h @@ -17,10 +17,10 @@ #include "GPUChain.h" namespace o2::its { -class Cluster; +struct Cluster; class Road; class Cell; -class TrackingFrameInfo; +struct TrackingFrameInfo; class TrackITSExt; } // namespace o2::its @@ -40,8 +40,8 @@ class GPUChainITS : public GPUChain int RunChain() override; void MemorySize(size_t& gpuMem, size_t& pageLockedHostMem) override; - int PrepareAndRunITSTrackFit(std::vector<o2::its::Road>& roads, std::array<const o2::its::Cluster*, 7> clusters, std::array<const o2::its::Cell*, 5> cells, const std::array<std::vector<o2::its::TrackingFrameInfo>, 7>& tf, std::vector<o2::its::TrackITSExt>& tracks); - int RunITSTrackFit(std::vector<o2::its::Road>& roads, std::array<const o2::its::Cluster*, 7> clusters, std::array<const o2::its::Cell*, 5> cells, const std::array<std::vector<o2::its::TrackingFrameInfo>, 7>& tf, std::vector<o2::its::TrackITSExt>& tracks); + int PrepareAndRunITSTrackFit(std::vector<o2::its::Road>& roads, std::vector<const o2::its::Cluster*>& clusters, std::vector<const o2::its::Cell*>& cells, const std::vector<std::vector<o2::its::TrackingFrameInfo>>& tf, std::vector<o2::its::TrackITSExt>& tracks); + int RunITSTrackFit(std::vector<o2::its::Road>& roads, std::vector<const o2::its::Cluster*>& clusters, std::vector<const o2::its::Cell*>& cells, const std::vector<std::vector<o2::its::TrackingFrameInfo>>& tf, std::vector<o2::its::TrackITSExt>& tracks); o2::its::TrackerTraits* GetITSTrackerTraits(); o2::its::VertexerTraits* GetITSVertexerTraits(); diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 680438a7adcf2..b6ba7214cb89e 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -57,6 +57,7 @@ #include "TPCdEdxCalibrationSplines.h" #include "TPCClusterDecompressor.h" #include "GPUTPCCFChainContext.h" +#include "GPUTrackingRefit.h" #else #include "GPUO2FakeClasses.h" #endif @@ -70,6 +71,7 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; using namespace o2::trd; +using namespace o2::tpc::constants; namespace GPUCA_NAMESPACE { @@ -123,6 +125,9 @@ void GPUChainTracking::RegisterPermanentMemoryAndProcessors() mRec->RegisterGPUProcessor(&processors()->tpcClusterer[i], GetRecoStepsGPU() & RecoStep::TPCClusterFinding); } } + if (GetRecoSteps() & RecoStep::Refit) { + mRec->RegisterGPUProcessor(&processors()->trackingRefit, GetRecoStepsGPU() & RecoStep::Refit); + } #endif #ifdef GPUCA_KERNEL_DEBUGGER_OUTPUT mRec->RegisterGPUProcessor(&processors()->debugOutput, true); @@ -160,6 +165,9 @@ void GPUChainTracking::RegisterGPUProcessors() mRec->RegisterGPUDeviceProcessor(&processorsShadow()->tpcClusterer[i], &processors()->tpcClusterer[i]); } } + if (GetRecoStepsGPU() & RecoStep::Refit) { + mRec->RegisterGPUDeviceProcessor(&processorsShadow()->trackingRefit, &processors()->trackingRefit); + } #endif #ifdef GPUCA_KERNEL_DEBUGGER_OUTPUT mRec->RegisterGPUDeviceProcessor(&processorsShadow()->debugOutput, &processors()->debugOutput); @@ -182,7 +190,7 @@ bool GPUChainTracking::ValidateSteps() GPUError("Invalid GPU Reconstruction Step Setting: dEdx requires TPC Merger to be active"); return false; } - if (!param().earlyTpcTransform) { + if (!param().par.earlyTpcTransform) { if (((GetRecoSteps() & GPUDataTypes::RecoStep::TPCSliceTracking) || (GetRecoSteps() & GPUDataTypes::RecoStep::TPCMerging)) && !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCConversion)) { GPUError("Invalid Reconstruction Step Setting: Tracking without early transform requires TPC Conversion to be active"); return false; @@ -253,6 +261,14 @@ bool GPUChainTracking::ValidateSteps() GPUError("Cannot run dE/dx without calibration splines"); return false; } + if ((GetRecoSteps() & GPUDataTypes::RecoStep::TPCClusterFinding) && processors()->calibObjects.tpcPadGain == nullptr) { + GPUError("Cannot run gain calibration without calibration object"); + return false; + } + if ((GetRecoSteps() & GPUDataTypes::RecoStep::Refit) && !param().rec.trackingRefitGPUModel && (processors()->calibObjects.o2Propagator == nullptr || processors()->calibObjects.matLUT == nullptr)) { + GPUError("Cannot run refit with o2 track model without o2 propagator"); + return false; + } return true; } @@ -266,19 +282,23 @@ bool GPUChainTracking::ValidateSettings() GPUError("Cannot do error interpolation with NWays = 1!"); return false; } - if ((param().rec.mergerReadFromTrackerDirectly || !param().earlyTpcTransform) && param().rec.NonConsecutiveIDs) { + if ((param().rec.mergerReadFromTrackerDirectly || !param().par.earlyTpcTransform) && param().rec.NonConsecutiveIDs) { GPUError("incompatible settings for non consecutive ids"); return false; } - if (param().continuousMaxTimeBin > (int)GPUSettings::TPC_MAX_TF_TIME_BIN) { + if (!param().rec.mergerReadFromTrackerDirectly && GetProcessingSettings().ompKernels) { + GPUError("OMP Kernels require mergerReadFromTrackerDirectly"); + return false; + } + if (param().par.continuousMaxTimeBin > (int)GPUSettings::TPC_MAX_TF_TIME_BIN) { GPUError("configure max time bin exceeds 256 orbits"); return false; } - if (mRec->IsGPU() && std::max(GetDeviceProcessingSettings().nTPCClustererLanes + 1, GetDeviceProcessingSettings().nTPCClustererLanes * 2) + (GetDeviceProcessingSettings().doublePipeline ? 1 : 0) > mRec->NStreams()) { + if (mRec->IsGPU() && std::max(GetProcessingSettings().nTPCClustererLanes + 1, GetProcessingSettings().nTPCClustererLanes * 2) + (GetProcessingSettings().doublePipeline ? 1 : 0) > mRec->NStreams()) { GPUError("NStreams must be > nTPCClustererLanes"); return false; } - if (GetDeviceProcessingSettings().doublePipeline) { + if (GetProcessingSettings().doublePipeline) { if (!GetRecoStepsOutputs().isOnlySet(GPUDataTypes::InOutType::TPCMergedTracks, GPUDataTypes::InOutType::TPCCompressedClusters, GPUDataTypes::InOutType::TPCClusters)) { GPUError("Invalid outputs for double pipeline mode 0x%x", (unsigned int)GetRecoStepsOutputs()); return false; @@ -287,7 +307,7 @@ bool GPUChainTracking::ValidateSettings() GPUError("Must use external output for double pipeline mode"); return false; } - if (DeviceProcessingSettings().tpcCompressionGatherMode == 1) { + if (ProcessingSettings().tpcCompressionGatherMode == 1) { GPUError("Double pipeline incompatible to compression mode 1"); return false; } @@ -296,13 +316,17 @@ bool GPUChainTracking::ValidateSettings() return false; } } + if (!(GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression) && (ProcessingSettings().tpcCompressionGatherMode == 1 || ProcessingSettings().tpcCompressionGatherMode == 3)) { + GPUError("Invalid tpcCompressionGatherMode for compression on CPU"); + return false; + } return true; } int GPUChainTracking::Init() { const auto& threadContext = GetThreadContext(); - if (GetDeviceProcessingSettings().debugLevel >= 1) { + if (GetProcessingSettings().debugLevel >= 1) { printf("Enabled Reconstruction Steps: 0x%x (on GPU: 0x%x)", (int)GetRecoSteps().get(), (int)GetRecoStepsGPU().get()); for (unsigned int i = 0; i < sizeof(GPUDataTypes::RECO_STEP_NAMES) / sizeof(GPUDataTypes::RECO_STEP_NAMES[0]); i++) { if (GetRecoSteps().isSet(1u << i)) { @@ -332,11 +356,11 @@ int GPUChainTracking::Init() return 1; } - if (GPUQA::QAAvailable() && (GetDeviceProcessingSettings().runQA || GetDeviceProcessingSettings().eventDisplay)) { + if (GPUQA::QAAvailable() && (GetProcessingSettings().runQA || GetProcessingSettings().eventDisplay)) { mQA.reset(new GPUQA(this)); } - if (GetDeviceProcessingSettings().eventDisplay) { - mEventDisplay.reset(new GPUDisplay(GetDeviceProcessingSettings().eventDisplay, this, mQA.get())); + if (GetProcessingSettings().eventDisplay) { + mEventDisplay.reset(new GPUDisplay(GetProcessingSettings().eventDisplay, this, mQA.get())); } processors()->errorCodes.setMemory(mInputsHost->mErrorCodes); @@ -369,177 +393,31 @@ int GPUChainTracking::Init() memcpy((void*)mFlatObjectsShadow.mCalibObjects.trdGeometry, (const void*)processors()->calibObjects.trdGeometry, sizeof(*processors()->calibObjects.trdGeometry)); mFlatObjectsShadow.mCalibObjects.trdGeometry->clearInternalBufferPtr(); } + if (processors()->calibObjects.tpcPadGain) { + memcpy((void*)mFlatObjectsShadow.mCalibObjects.tpcPadGain, (const void*)processors()->calibObjects.tpcPadGain, sizeof(*processors()->calibObjects.tpcPadGain)); + } + if (processors()->calibObjects.o2Propagator) { + memcpy((void*)mFlatObjectsShadow.mCalibObjects.o2Propagator, (const void*)processors()->calibObjects.o2Propagator, sizeof(*processors()->calibObjects.o2Propagator)); + mFlatObjectsShadow.mCalibObjects.o2Propagator->setGPUField(&processorsDevice()->param.polynomialField); + mFlatObjectsShadow.mCalibObjects.o2Propagator->setBz(param().polynomialField.GetNominalBz()); + mFlatObjectsShadow.mCalibObjects.o2Propagator->setMatLUT(mFlatObjectsShadow.mCalibObjects.matLUT); + } #endif TransferMemoryResourceLinkToGPU(RecoStep::NoRecoStep, mFlatObjectsShadow.mMemoryResFlat); + memcpy((void*)&processorsShadow()->calibObjects, (void*)&mFlatObjectsDevice.mCalibObjects, sizeof(mFlatObjectsDevice.mCalibObjects)); WriteToConstantMemory(RecoStep::NoRecoStep, (char*)&processors()->calibObjects - (char*)processors(), &mFlatObjectsDevice.mCalibObjects, sizeof(mFlatObjectsDevice.mCalibObjects), -1); // First initialization, for users not using RunChain processorsShadow()->errorCodes.setMemory(mInputsShadow->mErrorCodes); WriteToConstantMemory(RecoStep::NoRecoStep, (char*)&processors()->errorCodes - (char*)processors(), &processorsShadow()->errorCodes, sizeof(processorsShadow()->errorCodes), -1); TransferMemoryResourceLinkToGPU(RecoStep::NoRecoStep, mInputsHost->mResourceErrorCodes); } - if (GetDeviceProcessingSettings().debugLevel >= 6) { + if (GetProcessingSettings().debugLevel >= 6) { mDebugFile->open(mRec->IsGPU() ? "GPU.out" : "CPU.out"); } return 0; } -std::pair<unsigned int, unsigned int> GPUChainTracking::TPCClusterizerDecodeZSCountUpdate(unsigned int iSlice, const CfFragment& fragment) -{ - bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; -#ifdef HAVE_O2HEADERS - GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSlice]; - GPUTPCClusterFinder::ZSOffset* o = processors()->tpcClusterer[iSlice].mPzsOffsets; - unsigned int digits = 0; - unsigned short pages = 0; - for (unsigned short j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { - unsigned short posInEndpoint = 0; - unsigned short pagesEndpoint = 0; - clusterer.mMinMaxCN[j] = mCFContext->fragmentData[fragment.index].minMaxCN[iSlice][j]; - if (doGPU) { - for (unsigned int k = clusterer.mMinMaxCN[j].minC; k < clusterer.mMinMaxCN[j].maxC; k++) { - const unsigned int minL = (k == clusterer.mMinMaxCN[j].minC) ? clusterer.mMinMaxCN[j].minN : 0; - const unsigned int maxL = (k + 1 == clusterer.mMinMaxCN[j].maxC) ? clusterer.mMinMaxCN[j].maxN : mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]; - for (unsigned int l = minL; l < maxL; l++) { - unsigned short pageDigits = mCFContext->fragmentData[fragment.index].pageDigits[iSlice][j][posInEndpoint++]; - if (pageDigits) { - *(o++) = GPUTPCClusterFinder::ZSOffset{digits, j, pagesEndpoint}; - digits += pageDigits; - } - pagesEndpoint++; - } - } - pages += pagesEndpoint; - } else { - clusterer.mPzsOffsets[j] = GPUTPCClusterFinder::ZSOffset{digits, j, 0}; - digits += mCFContext->fragmentData[fragment.index].nDigits[iSlice][j]; - pages += mCFContext->fragmentData[fragment.index].nPages[iSlice][j]; - } - } - - return {digits, pages}; -#else - return {0, 0}; -#endif -} - -std::pair<unsigned int, unsigned int> GPUChainTracking::TPCClusterizerDecodeZSCount(unsigned int iSlice, const CfFragment& fragment) -{ - mRec->getGeneralStepTimer(GeneralStep::Prepare).Start(); - unsigned int nDigits = 0; - unsigned int nPages = 0; -#ifdef HAVE_O2HEADERS - bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; - int firstHBF = o2::raw::RDHUtils::getHeartBeatOrbit(*(const RAWDataHeaderGPU*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[0][0]); - - for (unsigned short j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { - for (unsigned int k = 0; k < mCFContext->nFragments; k++) { - mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxC = mIOPtrs.tpcZS->slice[iSlice].count[j]; - mCFContext->fragmentData[k].minMaxCN[iSlice][j].minC = mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxC; - mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxN = mIOPtrs.tpcZS->slice[iSlice].count[j] ? mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][mIOPtrs.tpcZS->slice[iSlice].count[j] - 1] : 0; - mCFContext->fragmentData[k].minMaxCN[iSlice][j].minN = mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxN; - } - -#ifndef GPUCA_NO_VC - if (GetDeviceProcessingSettings().prefetchTPCpageScan >= 3 && j < GPUTrackingInOutZS::NENDPOINTS - 1) { - for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[iSlice].count[j + 1]; k++) { - for (unsigned int l = 0; l < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j + 1][k]; l++) { - Vc::Common::prefetchMid(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j + 1][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE); - Vc::Common::prefetchMid(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j + 1][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(RAWDataHeaderGPU)); - } - } - } -#endif - - bool firstNextFound = false; - CfFragment f = fragment; - CfFragment fNext = f.next(); - bool firstSegment = true; - - for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[iSlice].count[j]; k++) { - for (unsigned int l = 0; l < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]; l++) { - if (f.isEnd()) { - GPUError("Time bin passed last fragment"); - return {0, 0}; - } -#ifndef GPUCA_NO_VC - if (GetDeviceProcessingSettings().prefetchTPCpageScan >= 2 && l + 1 < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]) { - Vc::Common::prefetchForOneRead(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j][k]) + (l + 1) * TPCZSHDR::TPC_ZS_PAGE_SIZE); - Vc::Common::prefetchForOneRead(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j][k]) + (l + 1) * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(RAWDataHeaderGPU)); - } -#endif - const unsigned char* const page = ((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE; - const RAWDataHeaderGPU* rdh = (const RAWDataHeaderGPU*)page; - if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(RAWDataHeaderGPU)) { - nPages++; - if (mCFContext->fragmentData[f.index].nPages[iSlice][j]) { - mCFContext->fragmentData[f.index].nPages[iSlice][j]++; - mCFContext->fragmentData[f.index].pageDigits[iSlice][j].emplace_back(0); - } - continue; - } - const TPCZSHDR* const hdr = (const TPCZSHDR*)(page + sizeof(RAWDataHeaderGPU)); - unsigned int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / Constants::LHCBCPERTIMEBIN; - for (unsigned int m = 0; m < 2; m++) { - CfFragment& fm = m ? fNext : f; - if (timeBin + hdr->nTimeBins >= (unsigned int)fm.first() && timeBin < (unsigned int)fm.last() && !fm.isEnd()) { - mCFContext->fragmentData[fm.index].nPages[iSlice][j]++; - mCFContext->fragmentData[fm.index].nDigits[iSlice][j] += hdr->nADCsamples; - if (doGPU) { - mCFContext->fragmentData[fm.index].pageDigits[iSlice][j].emplace_back(hdr->nADCsamples); - } - } - } - if (firstSegment && timeBin + hdr->nTimeBins >= (unsigned int)f.first()) { - mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].minC = k; - mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].minN = l; - firstSegment = false; - } - if (!firstNextFound && timeBin + hdr->nTimeBins >= (unsigned int)fNext.first() && !fNext.isEnd()) { - mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minC = k; - mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minN = l; - firstNextFound = true; - } - while (!f.isEnd() && timeBin >= (unsigned int)f.last()) { - if (!firstNextFound && !fNext.isEnd()) { - mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minC = k; - mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minN = l; - } - mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].maxC = k + 1; - mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].maxN = l; - f = fNext; - fNext = f.next(); - firstNextFound = false; - } - if (timeBin + hdr->nTimeBins > mCFContext->tpcMaxTimeBin) { - mCFContext->tpcMaxTimeBin = timeBin + hdr->nTimeBins; - } - - nPages++; - nDigits += hdr->nADCsamples; - } - } - } -#endif - mCFContext->nPagesTotal += nPages; - mCFContext->nPagesSector[iSlice] = nPages; - mCFContext->nPagesSectorMax = std::max(mCFContext->nPagesSectorMax, nPages); - - unsigned int digitsFragment = 0; - for (unsigned int i = 0; i < mCFContext->nFragments; i++) { - unsigned int pages = 0; - unsigned int digits = 0; - for (unsigned short j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { - pages += mCFContext->fragmentData[i].nPages[iSlice][j]; - digits += mCFContext->fragmentData[i].nDigits[iSlice][j]; - } - mCFContext->nPagesFragmentMax = std::max(mCFContext->nPagesSectorMax, pages); - digitsFragment = std::max(digitsFragment, digits); - } - mRec->getGeneralStepTimer(GeneralStep::Prepare).Stop(); - return {nDigits, digitsFragment}; -} - int GPUChainTracking::PrepareEvent() { mRec->MemoryScalers()->nTRDTracklets = mIOPtrs.nTRDTracklets; @@ -560,10 +438,10 @@ int GPUChainTracking::ForceInitQA() int GPUChainTracking::Finalize() { - if (GetDeviceProcessingSettings().runQA && mQA->IsInitialized()) { + if (GetProcessingSettings().runQA && mQA->IsInitialized() && !(mConfigQA && mConfigQA->shipToQC)) { mQA->DrawQAHistograms(); } - if (GetDeviceProcessingSettings().debugLevel >= 6) { + if (GetProcessingSettings().debugLevel >= 6) { mDebugFile->close(); } if (mCompressionStatistics) { @@ -578,6 +456,9 @@ void* GPUChainTracking::GPUTrackingFlatObjects::SetPointersFlatObjects(void* mem computePointerWithAlignment(mem, mCalibObjects.fastTransform, 1); computePointerWithAlignment(mem, mTpcTransformBuffer, mChainTracking->GetTPCTransform()->getFlatBufferSize()); } + if (mChainTracking->GetTPCPadGainCalib()) { + computePointerWithAlignment(mem, mCalibObjects.tpcPadGain, 1); + } #ifdef HAVE_O2HEADERS if (mChainTracking->GetdEdxSplines()) { computePointerWithAlignment(mem, mCalibObjects.dEdxSplines, 1); @@ -590,7 +471,9 @@ void* GPUChainTracking::GPUTrackingFlatObjects::SetPointersFlatObjects(void* mem if (mChainTracking->GetTRDGeometry()) { computePointerWithAlignment(mem, mCalibObjects.trdGeometry, 1); } - + if (mChainTracking->GetO2Propagator()) { + computePointerWithAlignment(mem, mCalibObjects.o2Propagator, 1); + } #endif return mem; } @@ -610,8 +493,10 @@ void GPUChainTracking::AllocateIOMemory() AllocateIOMemoryHelper(mIOPtrs.nSliceTracks[i], mIOPtrs.sliceTracks[i], mIOMem.sliceTracks[i]); AllocateIOMemoryHelper(mIOPtrs.nSliceClusters[i], mIOPtrs.sliceClusters[i], mIOMem.sliceClusters[i]); } - AllocateIOMemoryHelper(mClusterNativeAccess->nClustersTotal, mClusterNativeAccess->clustersLinear, mIOMem.clustersNative); - mIOPtrs.clustersNative = mClusterNativeAccess->nClustersTotal ? mClusterNativeAccess.get() : nullptr; + mIOMem.clusterNativeAccess.reset(new ClusterNativeAccess); + std::memset(mIOMem.clusterNativeAccess.get(), 0, sizeof(ClusterNativeAccess)); // ClusterNativeAccess has no its own constructor + AllocateIOMemoryHelper(mIOMem.clusterNativeAccess->nClustersTotal, mIOMem.clusterNativeAccess->clustersLinear, mIOMem.clustersNative); + mIOPtrs.clustersNative = mIOMem.clusterNativeAccess->nClustersTotal ? mIOMem.clusterNativeAccess.get() : nullptr; AllocateIOMemoryHelper(mIOPtrs.nMCLabelsTPC, mIOPtrs.mcLabelsTPC, mIOMem.mcLabelsTPC); AllocateIOMemoryHelper(mIOPtrs.nMCInfosTPC, mIOPtrs.mcInfosTPC, mIOMem.mcInfosTPC); AllocateIOMemoryHelper(mIOPtrs.nMergedTracks, mIOPtrs.mergedTracks, mIOMem.mergedTracks); @@ -645,8 +530,8 @@ int GPUChainTracking::ConvertNativeToClusterData() TransferMemoryResourceLinkToGPU(RecoStep::TPCConversion, mInputsHost->mResourceClusterNativeAccess, 0); } } - if (!param().earlyTpcTransform) { - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (!param().par.earlyTpcTransform) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Early transform inactive, skipping TPC Early transformation kernel, transformed on the fly during slice data creation / refit"); } return 0; @@ -672,14 +557,14 @@ int GPUChainTracking::ConvertNativeToClusterData() void GPUChainTracking::ConvertNativeToClusterDataLegacy() { - ClusterNativeAccess* tmp = mClusterNativeAccess.get(); + ClusterNativeAccess* tmp = mIOMem.clusterNativeAccess.get(); if (tmp != mIOPtrs.clustersNative) { *tmp = *mIOPtrs.clustersNative; } - GPUReconstructionConvert::ConvertNativeToClusterData(mClusterNativeAccess.get(), mIOMem.clusterData, mIOPtrs.nClusterData, processors()->calibObjects.fastTransform, param().continuousMaxTimeBin); + GPUReconstructionConvert::ConvertNativeToClusterData(mIOMem.clusterNativeAccess.get(), mIOMem.clusterData, mIOPtrs.nClusterData, processors()->calibObjects.fastTransform, param().par.continuousMaxTimeBin); for (unsigned int i = 0; i < NSLICES; i++) { mIOPtrs.clusterData[i] = mIOMem.clusterData[i].get(); - if (GetDeviceProcessingSettings().registerStandaloneInputMemory) { + if (GetProcessingSettings().registerStandaloneInputMemory) { if (mRec->registerMemoryForGPU(mIOMem.clusterData[i].get(), mIOPtrs.nClusterData[i] * sizeof(*mIOPtrs.clusterData[i]))) { throw std::runtime_error("Error registering memory for GPU"); } @@ -691,7 +576,7 @@ void GPUChainTracking::ConvertNativeToClusterDataLegacy() void GPUChainTracking::ConvertRun2RawToNative() { - GPUReconstructionConvert::ConvertRun2RawToNative(*mClusterNativeAccess, mIOMem.clustersNative, mIOPtrs.rawClusters, mIOPtrs.nRawClusters); + GPUReconstructionConvert::ConvertRun2RawToNative(*mIOMem.clusterNativeAccess, mIOMem.clustersNative, mIOPtrs.rawClusters, mIOPtrs.nRawClusters); for (unsigned int i = 0; i < NSLICES; i++) { mIOPtrs.rawClusters[i] = nullptr; mIOPtrs.nRawClusters[i] = 0; @@ -700,9 +585,9 @@ void GPUChainTracking::ConvertRun2RawToNative() mIOPtrs.nClusterData[i] = 0; mIOMem.clusterData[i].reset(nullptr); } - mIOPtrs.clustersNative = mClusterNativeAccess.get(); - if (GetDeviceProcessingSettings().registerStandaloneInputMemory) { - if (mRec->registerMemoryForGPU(mIOMem.clustersNative.get(), mClusterNativeAccess->nClustersTotal * sizeof(*mClusterNativeAccess->clustersLinear))) { + mIOPtrs.clustersNative = mIOMem.clusterNativeAccess.get(); + if (GetProcessingSettings().registerStandaloneInputMemory) { + if (mRec->registerMemoryForGPU(mIOMem.clustersNative.get(), mIOMem.clusterNativeAccess->nClustersTotal * sizeof(*mIOMem.clusterNativeAccess->clustersLinear))) { throw std::runtime_error("Error registering memory for GPU"); } } @@ -711,13 +596,12 @@ void GPUChainTracking::ConvertRun2RawToNative() void GPUChainTracking::ConvertZSEncoder(bool zs12bit) { #ifdef HAVE_O2HEADERS - mTPCZSSizes.reset(new unsigned int[NSLICES * GPUTrackingInOutZS::NENDPOINTS]); - mTPCZSPtrs.reset(new void*[NSLICES * GPUTrackingInOutZS::NENDPOINTS]); - mTPCZS.reset(new GPUTrackingInOutZS); - GPUReconstructionConvert::RunZSEncoder<o2::tpc::Digit>(*mIOPtrs.tpcPackedDigits, &mTPCZSBuffer, mTPCZSSizes.get(), nullptr, nullptr, param(), zs12bit, true); - GPUReconstructionConvert::RunZSEncoderCreateMeta(mTPCZSBuffer.get(), mTPCZSSizes.get(), mTPCZSPtrs.get(), mTPCZS.get()); - mIOPtrs.tpcZS = mTPCZS.get(); - if (GetDeviceProcessingSettings().registerStandaloneInputMemory) { + mIOMem.tpcZSmeta2.reset(new GPUTrackingInOutZS::GPUTrackingInOutZSMeta); + mIOMem.tpcZSmeta.reset(new GPUTrackingInOutZS); + GPUReconstructionConvert::RunZSEncoder<o2::tpc::Digit>(*mIOPtrs.tpcPackedDigits, &mIOMem.tpcZSpages, &mIOMem.tpcZSmeta2->n[0][0], nullptr, nullptr, param(), zs12bit, true); + GPUReconstructionConvert::RunZSEncoderCreateMeta(mIOMem.tpcZSpages.get(), &mIOMem.tpcZSmeta2->n[0][0], &mIOMem.tpcZSmeta2->ptr[0][0], mIOMem.tpcZSmeta.get()); + mIOPtrs.tpcZS = mIOMem.tpcZSmeta.get(); + if (GetProcessingSettings().registerStandaloneInputMemory) { for (unsigned int i = 0; i < NSLICES; i++) { for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[i].count[j]; k++) { @@ -733,7 +617,7 @@ void GPUChainTracking::ConvertZSEncoder(bool zs12bit) void GPUChainTracking::ConvertZSFilter(bool zs12bit) { - GPUReconstructionConvert::RunZSFilter(mIOMem.tpcDigits, mIOPtrs.tpcPackedDigits->tpcDigits, mDigitMap->nTPCDigits, mIOPtrs.tpcPackedDigits->nTPCDigits, param(), zs12bit); + GPUReconstructionConvert::RunZSFilter(mIOMem.tpcDigits, mIOPtrs.tpcPackedDigits->tpcDigits, mIOMem.digitMap->nTPCDigits, mIOPtrs.tpcPackedDigits->nTPCDigits, param(), zs12bit, param().rec.tpcZSthreshold); } void GPUChainTracking::LoadClusterErrors() { param().LoadClusterErrors(); } @@ -756,7 +640,7 @@ void GPUChainTracking::SetMatLUT(std::unique_ptr<o2::base::MatLayerCylSet>&& lut processors()->calibObjects.matLUT = mMatLUTU.get(); } -void GPUChainTracking::SetTRDGeometry(std::unique_ptr<o2::trd::TRDGeometryFlat>&& geo) +void GPUChainTracking::SetTRDGeometry(std::unique_ptr<o2::trd::GeometryFlat>&& geo) { mTRDGeometryU = std::move(geo); processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); @@ -764,11 +648,11 @@ void GPUChainTracking::SetTRDGeometry(std::unique_ptr<o2::trd::TRDGeometryFlat>& int GPUChainTracking::ReadEvent(unsigned int iSlice, int threadId) { - if (GetDeviceProcessingSettings().debugLevel >= 5) { + if (GetProcessingSettings().debugLevel >= 5) { GPUInfo("Running ReadEvent for slice %d on thread %d\n", iSlice, threadId); } runKernel<GPUTPCCreateSliceData>({GetGridAuto(0, GPUReconstruction::krnlDeviceType::CPU)}, {iSlice}); - if (GetDeviceProcessingSettings().debugLevel >= 5) { + if (GetProcessingSettings().debugLevel >= 5) { GPUInfo("Finished ReadEvent for slice %d on thread %d\n", iSlice, threadId); } return (0); @@ -776,20 +660,20 @@ int GPUChainTracking::ReadEvent(unsigned int iSlice, int threadId) void GPUChainTracking::WriteOutput(int iSlice, int threadId) { - if (GetDeviceProcessingSettings().debugLevel >= 5) { + if (GetProcessingSettings().debugLevel >= 5) { GPUInfo("Running WriteOutput for slice %d on thread %d\n", iSlice, threadId); } - if (GetDeviceProcessingSettings().nDeviceHelperThreads) { + if (GetProcessingSettings().nDeviceHelperThreads) { while (mLockAtomic.test_and_set(std::memory_order_acquire)) { ; } } processors()->tpcTrackers[iSlice].WriteOutputPrepare(); - if (GetDeviceProcessingSettings().nDeviceHelperThreads) { + if (GetProcessingSettings().nDeviceHelperThreads) { mLockAtomic.clear(); } processors()->tpcTrackers[iSlice].WriteOutput(); - if (GetDeviceProcessingSettings().debugLevel >= 5) { + if (GetProcessingSettings().debugLevel >= 5) { GPUInfo("Finished WriteOutput for slice %d on thread %d\n", iSlice, threadId); } } @@ -839,28 +723,178 @@ int GPUChainTracking::ForwardTPCDigits() int GPUChainTracking::GlobalTracking(unsigned int iSlice, int threadId, bool synchronizeOutput) { - if (GetDeviceProcessingSettings().debugLevel >= 5) { + if (GetProcessingSettings().debugLevel >= 5) { GPUInfo("GPU Tracker running Global Tracking for slice %u on thread %d\n", iSlice, threadId); } - GPUReconstruction::krnlDeviceType deviceType = GetDeviceProcessingSettings().fullMergerOnGPU ? GPUReconstruction::krnlDeviceType::Auto : GPUReconstruction::krnlDeviceType::CPU; + GPUReconstruction::krnlDeviceType deviceType = GetProcessingSettings().fullMergerOnGPU ? GPUReconstruction::krnlDeviceType::Auto : GPUReconstruction::krnlDeviceType::CPU; runKernel<GPUTPCGlobalTracking>(GetGridBlk(256, iSlice % mRec->NStreams(), deviceType), {iSlice}); - if (GetDeviceProcessingSettings().fullMergerOnGPU) { + if (GetProcessingSettings().fullMergerOnGPU) { TransferMemoryResourceLinkToHost(RecoStep::TPCSliceTracking, processors()->tpcTrackers[iSlice].MemoryResCommon(), iSlice % mRec->NStreams()); } if (synchronizeOutput) { SynchronizeStream(iSlice % mRec->NStreams()); } - if (GetDeviceProcessingSettings().debugLevel >= 5) { + if (GetProcessingSettings().debugLevel >= 5) { GPUInfo("GPU Tracker finished Global Tracking for slice %u on thread %d\n", iSlice, threadId); } return (0); } +#ifdef GPUCA_TPC_GEOMETRY_O2 +std::pair<unsigned int, unsigned int> GPUChainTracking::TPCClusterizerDecodeZSCountUpdate(unsigned int iSlice, const CfFragment& fragment) +{ + bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; + GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSlice]; + GPUTPCClusterFinder::ZSOffset* o = processors()->tpcClusterer[iSlice].mPzsOffsets; + unsigned int digits = 0; + unsigned short pages = 0; + for (unsigned short j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + unsigned short posInEndpoint = 0; + unsigned short pagesEndpoint = 0; + clusterer.mMinMaxCN[j] = mCFContext->fragmentData[fragment.index].minMaxCN[iSlice][j]; + if (doGPU) { + for (unsigned int k = clusterer.mMinMaxCN[j].minC; k < clusterer.mMinMaxCN[j].maxC; k++) { + const unsigned int minL = (k == clusterer.mMinMaxCN[j].minC) ? clusterer.mMinMaxCN[j].minN : 0; + const unsigned int maxL = (k + 1 == clusterer.mMinMaxCN[j].maxC) ? clusterer.mMinMaxCN[j].maxN : mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]; + for (unsigned int l = minL; l < maxL; l++) { + unsigned short pageDigits = mCFContext->fragmentData[fragment.index].pageDigits[iSlice][j][posInEndpoint++]; + if (pageDigits) { + *(o++) = GPUTPCClusterFinder::ZSOffset{digits, j, pagesEndpoint}; + digits += pageDigits; + } + pagesEndpoint++; + } + } + pages += pagesEndpoint; + } else { + clusterer.mPzsOffsets[j] = GPUTPCClusterFinder::ZSOffset{digits, j, 0}; + digits += mCFContext->fragmentData[fragment.index].nDigits[iSlice][j]; + pages += mCFContext->fragmentData[fragment.index].nPages[iSlice][j]; + } + } + + return {digits, pages}; +} + +std::pair<unsigned int, unsigned int> GPUChainTracking::TPCClusterizerDecodeZSCount(unsigned int iSlice, const CfFragment& fragment) +{ + mRec->getGeneralStepTimer(GeneralStep::Prepare).Start(); + unsigned int nDigits = 0; + unsigned int nPages = 0; + bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; + int firstHBF = o2::raw::RDHUtils::getHeartBeatOrbit(*(const RAWDataHeaderGPU*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[0][0]); + + for (unsigned short j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + for (unsigned int k = 0; k < mCFContext->nFragments; k++) { + mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxC = mIOPtrs.tpcZS->slice[iSlice].count[j]; + mCFContext->fragmentData[k].minMaxCN[iSlice][j].minC = mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxC; + mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxN = mIOPtrs.tpcZS->slice[iSlice].count[j] ? mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][mIOPtrs.tpcZS->slice[iSlice].count[j] - 1] : 0; + mCFContext->fragmentData[k].minMaxCN[iSlice][j].minN = mCFContext->fragmentData[k].minMaxCN[iSlice][j].maxN; + } + +#ifndef GPUCA_NO_VC + if (GetProcessingSettings().prefetchTPCpageScan >= 3 && j < GPUTrackingInOutZS::NENDPOINTS - 1) { + for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[iSlice].count[j + 1]; k++) { + for (unsigned int l = 0; l < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j + 1][k]; l++) { + Vc::Common::prefetchMid(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j + 1][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE); + Vc::Common::prefetchMid(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j + 1][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(RAWDataHeaderGPU)); + } + } + } +#endif + + bool firstNextFound = false; + CfFragment f = fragment; + CfFragment fNext = f.next(); + bool firstSegment = true; + + for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[iSlice].count[j]; k++) { + for (unsigned int l = 0; l < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]; l++) { + if (f.isEnd()) { + GPUError("Time bin passed last fragment"); + return {0, 0}; + } +#ifndef GPUCA_NO_VC + if (GetProcessingSettings().prefetchTPCpageScan >= 2 && l + 1 < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]) { + Vc::Common::prefetchForOneRead(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j][k]) + (l + 1) * TPCZSHDR::TPC_ZS_PAGE_SIZE); + Vc::Common::prefetchForOneRead(((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j][k]) + (l + 1) * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(RAWDataHeaderGPU)); + } +#endif + const unsigned char* const page = ((const unsigned char*)mIOPtrs.tpcZS->slice[iSlice].zsPtr[j][k]) + l * TPCZSHDR::TPC_ZS_PAGE_SIZE; + const RAWDataHeaderGPU* rdh = (const RAWDataHeaderGPU*)page; + if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(RAWDataHeaderGPU)) { + nPages++; + if (mCFContext->fragmentData[f.index].nPages[iSlice][j]) { + mCFContext->fragmentData[f.index].nPages[iSlice][j]++; + mCFContext->fragmentData[f.index].pageDigits[iSlice][j].emplace_back(0); + } + continue; + } + const TPCZSHDR* const hdr = (const TPCZSHDR*)(page + sizeof(RAWDataHeaderGPU)); + unsigned int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; + for (unsigned int m = 0; m < 2; m++) { + CfFragment& fm = m ? fNext : f; + if (timeBin + hdr->nTimeBins >= (unsigned int)fm.first() && timeBin < (unsigned int)fm.last() && !fm.isEnd()) { + mCFContext->fragmentData[fm.index].nPages[iSlice][j]++; + mCFContext->fragmentData[fm.index].nDigits[iSlice][j] += hdr->nADCsamples; + if (doGPU) { + mCFContext->fragmentData[fm.index].pageDigits[iSlice][j].emplace_back(hdr->nADCsamples); + } + } + } + if (firstSegment && timeBin + hdr->nTimeBins >= (unsigned int)f.first()) { + mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].minC = k; + mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].minN = l; + firstSegment = false; + } + if (!firstNextFound && timeBin + hdr->nTimeBins >= (unsigned int)fNext.first() && !fNext.isEnd()) { + mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minC = k; + mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minN = l; + firstNextFound = true; + } + while (!f.isEnd() && timeBin >= (unsigned int)f.last()) { + if (!firstNextFound && !fNext.isEnd()) { + mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minC = k; + mCFContext->fragmentData[fNext.index].minMaxCN[iSlice][j].minN = l; + } + mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].maxC = k + 1; + mCFContext->fragmentData[f.index].minMaxCN[iSlice][j].maxN = l; + f = fNext; + fNext = f.next(); + firstNextFound = false; + } + if (timeBin + hdr->nTimeBins > mCFContext->tpcMaxTimeBin) { + mCFContext->tpcMaxTimeBin = timeBin + hdr->nTimeBins; + } + + nPages++; + nDigits += hdr->nADCsamples; + } + } + } + mCFContext->nPagesTotal += nPages; + mCFContext->nPagesSector[iSlice] = nPages; + mCFContext->nPagesSectorMax = std::max(mCFContext->nPagesSectorMax, nPages); + + unsigned int digitsFragment = 0; + for (unsigned int i = 0; i < mCFContext->nFragments; i++) { + unsigned int pages = 0; + unsigned int digits = 0; + for (unsigned short j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + pages += mCFContext->fragmentData[i].nPages[iSlice][j]; + digits += mCFContext->fragmentData[i].nDigits[iSlice][j]; + } + mCFContext->nPagesFragmentMax = std::max(mCFContext->nPagesSectorMax, pages); + digitsFragment = std::max(digitsFragment, digits); + } + mRec->getGeneralStepTimer(GeneralStep::Prepare).Stop(); + return {nDigits, digitsFragment}; +} + void GPUChainTracking::RunTPCClusterizer_compactPeaks(GPUTPCClusterFinder& clusterer, GPUTPCClusterFinder& clustererShadow, int stage, bool doGPU, int lane) { -#ifdef HAVE_O2HEADERS auto& in = stage ? clustererShadow.mPpeakPositions : clustererShadow.mPpositions; auto& out = stage ? clustererShadow.mPfilteredPeakPositions : clustererShadow.mPpeakPositions; if (doGPU) { @@ -907,12 +941,10 @@ void GPUChainTracking::RunTPCClusterizer_compactPeaks(GPUTPCClusterFinder& clust } nOut = count; } -#endif } std::pair<unsigned int, unsigned int> GPUChainTracking::RunTPCClusterizer_transferZS(int iSlice, const CfFragment& fragment, int lane) { -#ifdef GPUCA_TPC_GEOMETRY_O2 bool doGPU = GetRecoStepsGPU() & RecoStep::TPCClusterFinding; const auto& retVal = TPCClusterizerDecodeZSCountUpdate(iSlice, fragment); if (doGPU) { @@ -942,20 +974,17 @@ std::pair<unsigned int, unsigned int> GPUChainTracking::RunTPCClusterizer_transf GPUMemCpy(RecoStep::TPCClusterFinding, clustererShadow.mPzsOffsets, clusterer.mPzsOffsets, clusterer.mNMaxPages * sizeof(*clusterer.mPzsOffsets), lane, true); } return retVal; -#else - return {0, 0}; -#endif } int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) { -#ifdef GPUCA_TPC_GEOMETRY_O2 if (restorePointers) { for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { processors()->tpcClusterer[iSlice].mPzsOffsets = mCFContext->ptrSave[iSlice].zsOffsetHost; processorsShadow()->tpcClusterer[iSlice].mPzsOffsets = mCFContext->ptrSave[iSlice].zsOffsetDevice; processorsShadow()->tpcClusterer[iSlice].mPzs = mCFContext->ptrSave[iSlice].zsDevice; } + processorsShadow()->ioPtrs.clustersNative = mCFContext->ptrClusterNativeSave; return 0; } const auto& threadContext = GetThreadContext(); @@ -963,7 +992,7 @@ int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) if (mCFContext == nullptr) { mCFContext.reset(new GPUTPCCFChainContext); } - mCFContext->tpcMaxTimeBin = std::max<int>(param().continuousMaxTimeBin, TPC_MAX_FRAGMENT_LEN); + mCFContext->tpcMaxTimeBin = std::max<int>(param().par.continuousMaxTimeBin, TPC_MAX_FRAGMENT_LEN); const CfFragment fragmentMax{(tpccf::TPCTime)mCFContext->tpcMaxTimeBin + 1, TPC_MAX_FRAGMENT_LEN}; mCFContext->prepare(mIOPtrs.tpcZS, fragmentMax); if (mIOPtrs.tpcZS) { @@ -979,7 +1008,7 @@ int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) return 1; } #ifndef GPUCA_NO_VC - if (GetDeviceProcessingSettings().prefetchTPCpageScan >= 1 && iSlice < NSLICES - 1) { + if (GetProcessingSettings().prefetchTPCpageScan >= 1 && iSlice < NSLICES - 1) { for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[iSlice].count[j]; k++) { for (unsigned int l = 0; l < mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]; l++) { @@ -1000,7 +1029,7 @@ int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) if (mRec->IsGPU()) { processorsShadow()->tpcClusterer[iSlice].SetNMaxDigits(processors()->tpcClusterer[iSlice].mPmemory->counters.nDigits, mCFContext->nPagesFragmentMax, nDigitsFragment[iSlice]); } - if (mPipelineNotifyCtx) { + if (mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer) { mPipelineNotifyCtx->rec->AllocateRegisteredForeignMemory(processors()->tpcClusterer[iSlice].mZSOffsetId, mRec); mPipelineNotifyCtx->rec->AllocateRegisteredForeignMemory(processors()->tpcClusterer[iSlice].mZSId, mRec); } else { @@ -1021,23 +1050,22 @@ int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) GPUInfo("Event has %lld TPC Digits", (long long int)mRec->MemoryScalers()->nTPCdigits); } mCFContext->fragmentFirst = CfFragment{std::max<int>(mCFContext->tpcMaxTimeBin + 1, TPC_MAX_FRAGMENT_LEN), TPC_MAX_FRAGMENT_LEN}; - for (int lane = 0; lane < GetDeviceProcessingSettings().nTPCClustererLanes && lane < NSLICES; lane++) { - unsigned int iSlice = lane; + for (int iSlice = 0; iSlice < GetProcessingSettings().nTPCClustererLanes && iSlice < NSLICES; iSlice++) { if (mIOPtrs.tpcZS && mCFContext->nPagesSector[iSlice]) { - mCFContext->nextPos[iSlice] = RunTPCClusterizer_transferZS(iSlice, mCFContext->fragmentFirst, GetDeviceProcessingSettings().nTPCClustererLanes + lane); + mCFContext->nextPos[iSlice] = RunTPCClusterizer_transferZS(iSlice, mCFContext->fragmentFirst, GetProcessingSettings().nTPCClustererLanes + iSlice); } } - if (mPipelineNotifyCtx) { + if (mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer) { for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { mCFContext->ptrSave[iSlice].zsOffsetHost = processors()->tpcClusterer[iSlice].mPzsOffsets; mCFContext->ptrSave[iSlice].zsOffsetDevice = processorsShadow()->tpcClusterer[iSlice].mPzsOffsets; mCFContext->ptrSave[iSlice].zsDevice = processorsShadow()->tpcClusterer[iSlice].mPzs; } } -#endif return 0; } +#endif int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) { @@ -1048,7 +1076,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) mRec->PushNonPersistentMemory(); const auto& threadContext = GetThreadContext(); bool doGPU = GetRecoStepsGPU() & RecoStep::TPCClusterFinding; - if (RunTPCClusterizer_prepare(mPipelineNotifyCtx)) { + if (RunTPCClusterizer_prepare(mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer)) { return 1; } @@ -1059,7 +1087,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { SetupGPUProcessor(&processors()->tpcClusterer[iSlice], true); // Now we allocate } - if (mPipelineNotifyCtx) { + if (mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer) { RunTPCClusterizer_prepare(true); // Restore some pointers, allocated by the other pipeline, and set to 0 by SetupGPUProcessor (since not allocated in this pipeline) } @@ -1075,40 +1103,42 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) ClusterNativeAccess* tmpNative = mClusterNativeAccess.get(); // setup MC Labels - bool propagateMCLabels = !doGPU && GetDeviceProcessingSettings().runMC && processors()->ioPtrs.tpcPackedDigits->tpcDigitsMC != nullptr; + bool propagateMCLabels = GetProcessingSettings().runMC && processors()->ioPtrs.tpcPackedDigits->tpcDigitsMC != nullptr; auto* digitsMC = propagateMCLabels ? processors()->ioPtrs.tpcPackedDigits->tpcDigitsMC : nullptr; - GPUTPCLinearLabels mcLinearLabels; - if (propagateMCLabels) { - mcLinearLabels.header.reserve(mRec->MemoryScalers()->nTPCHits); - mcLinearLabels.data.reserve(mRec->MemoryScalers()->nTPCHits * 16); // Assumption: cluster will have less than 16 labels on average - } - - if (param().continuousMaxTimeBin > 0 && mCFContext->tpcMaxTimeBin >= (unsigned int)std::max(param().continuousMaxTimeBin + 1, TPC_MAX_FRAGMENT_LEN)) { + if (param().par.continuousMaxTimeBin > 0 && mCFContext->tpcMaxTimeBin >= (unsigned int)std::max(param().par.continuousMaxTimeBin + 1, TPC_MAX_FRAGMENT_LEN)) { GPUError("Input data has invalid time bin\n"); return 1; } bool buildNativeGPU = (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCConversion) || (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSliceTracking) || (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging) || (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCCompression); bool buildNativeHost = mRec->GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCClusters; // TODO: Should do this also when clusters are needed for later steps on the host but not requested as output - mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = mRec->MemoryScalers()->NTPCClusters(mRec->MemoryScalers()->nTPCdigits); + mRec->MemoryScalers()->nTPCHits = mRec->MemoryScalers()->NTPCClusters(mRec->MemoryScalers()->nTPCdigits); + mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = mRec->MemoryScalers()->nTPCHits; if (buildNativeGPU) { AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); } - if (buildNativeHost && !(buildNativeGPU && GetDeviceProcessingSettings().delayedOutput)) { + if (buildNativeHost && !(buildNativeGPU && GetProcessingSettings().delayedOutput)) { AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mOutputClustersNative); } + GPUTPCLinearLabels mcLinearLabels; + if (propagateMCLabels) { + // No need to overallocate here, nTPCHits is anyway an upper bound used for the GPU cluster buffer, and we can always enlarge the buffer anyway + mcLinearLabels.header.reserve(mRec->MemoryScalers()->nTPCHits / 2); + mcLinearLabels.data.reserve(mRec->MemoryScalers()->nTPCHits); + } + char transferRunning[NSLICES] = {0}; unsigned int outputQueueStart = mOutputQueue.size(); - for (unsigned int iSliceBase = 0; iSliceBase < NSLICES; iSliceBase += GetDeviceProcessingSettings().nTPCClustererLanes) { - std::vector<bool> laneHasData(GetDeviceProcessingSettings().nTPCClustererLanes, false); + for (unsigned int iSliceBase = 0; iSliceBase < NSLICES; iSliceBase += GetProcessingSettings().nTPCClustererLanes) { + std::vector<bool> laneHasData(GetProcessingSettings().nTPCClustererLanes, false); for (CfFragment fragment = mCFContext->fragmentFirst; !fragment.isEnd(); fragment = fragment.next()) { - if (GetDeviceProcessingSettings().debugLevel >= 3) { - GPUInfo("Processing time bins [%d, %d)", fragment.start, fragment.last()); + if (GetProcessingSettings().debugLevel >= 3) { + GPUInfo("Processing time bins [%d, %d) for sectors %d to %d", fragment.start, fragment.last(), iSliceBase, iSliceBase + GetProcessingSettings().nTPCClustererLanes - 1); } - for (int lane = 0; lane < GetDeviceProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { + for (int lane = 0; lane < GetProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { if (fragment.index != 0) { SynchronizeStream(lane); // Don't overwrite charge map from previous iteration until cluster computation is finished } @@ -1119,7 +1149,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) clusterer.mPmemory->counters.nPeaks = clusterer.mPmemory->counters.nClusters = 0; clusterer.mPmemory->fragment = fragment; - if (propagateMCLabels) { + if (propagateMCLabels && fragment.index == 0) { clusterer.PrepareMC(); clusterer.mPinputLabels = digitsMC->v[iSlice]; // TODO: Why is the number of header entries in truth container @@ -1127,14 +1157,17 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) assert(clusterer.mPinputLabels->getIndexedSize() >= mIOPtrs.tpcPackedDigits->nTPCDigits[iSlice]); } - if (not mIOPtrs.tpcZS || propagateMCLabels) { + if (mIOPtrs.tpcPackedDigits) { + bool setDigitsOnGPU = doGPU && not mIOPtrs.tpcZS; + bool setDigitsOnHost = (not doGPU && not mIOPtrs.tpcZS) || propagateMCLabels; auto* inDigits = mIOPtrs.tpcPackedDigits; size_t numDigits = inDigits->nTPCDigits[iSlice]; - clusterer.mPmemory->counters.nDigits = numDigits; - if (doGPU) { + if (setDigitsOnGPU) { GPUMemCpy(RecoStep::TPCClusterFinding, clustererShadow.mPdigits, inDigits->tpcDigits[iSlice], sizeof(clustererShadow.mPdigits[0]) * numDigits, lane, true); - } else { - clustererShadow.mPdigits = const_cast<o2::tpc::Digit*>(inDigits->tpcDigits[iSlice]); // TODO: Needs fixing (invalid const cast) + clusterer.mPmemory->counters.nDigits = numDigits; + } else if (setDigitsOnHost) { + clusterer.mPdigits = const_cast<o2::tpc::Digit*>(inDigits->tpcDigits[iSlice]); // TODO: Needs fixing (invalid const cast) + clusterer.mPmemory->counters.nDigits = numDigits; } } @@ -1148,11 +1181,15 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) using PeakMapType = decltype(*clustererShadow.mPpeakMap); runKernel<GPUMemClean16>(GetGridAutoStep(lane, RecoStep::TPCClusterFinding), krnlRunRangeNone, {}, clustererShadow.mPchargeMap, TPCMapMemoryLayout<ChargeMapType>::items() * sizeof(ChargeMapType)); runKernel<GPUMemClean16>(GetGridAutoStep(lane, RecoStep::TPCClusterFinding), krnlRunRangeNone, {}, clustererShadow.mPpeakMap, TPCMapMemoryLayout<PeakMapType>::items() * sizeof(PeakMapType)); + if (fragment.index == 0) { + using HasLostBaselineType = decltype(*clustererShadow.mPpadHasLostBaseline); + runKernel<GPUMemClean16>(GetGridAutoStep(lane, RecoStep::TPCClusterFinding), krnlRunRangeNone, {}, clustererShadow.mPpadHasLostBaseline, TPC_PADS_IN_SECTOR * sizeof(HasLostBaselineType)); + } DoDebugAndDump(RecoStep::TPCClusterFinding, 0, clusterer, &GPUTPCClusterFinder::DumpChargeMap, *mDebugFile, "Zeroed Charges"); if (mIOPtrs.tpcZS && mCFContext->nPagesSector[iSlice]) { TransferMemoryResourceLinkToGPU(RecoStep::TPCClusterFinding, mInputsHost->mResourceZS, lane); - SynchronizeStream(GetDeviceProcessingSettings().nTPCClustererLanes + lane); + SynchronizeStream(GetProcessingSettings().nTPCClustererLanes + lane); } SynchronizeStream(mRec->NStreams() - 1); // Wait for copying to constant memory @@ -1161,9 +1198,12 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) continue; } - if (propagateMCLabels || not mIOPtrs.tpcZS) { - runKernel<GPUTPCCFChargeMapFiller, GPUTPCCFChargeMapFiller::findFragmentStart>(GetGrid(1, lane), {iSlice}, {}); + if (not mIOPtrs.tpcZS) { + runKernel<GPUTPCCFChargeMapFiller, GPUTPCCFChargeMapFiller::findFragmentStart>(GetGrid(1, lane), {iSlice}, {}, mIOPtrs.tpcZS == nullptr); TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); + } else if (propagateMCLabels) { + runKernel<GPUTPCCFChargeMapFiller, GPUTPCCFChargeMapFiller::findFragmentStart>(GetGrid(1, lane, GPUReconstruction::krnlDeviceType::CPU), {iSlice}, {}, mIOPtrs.tpcZS == nullptr); + TransferMemoryResourceLinkToGPU(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); } if (mIOPtrs.tpcZS) { @@ -1172,18 +1212,18 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); } } - for (int lane = 0; lane < GetDeviceProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { + for (int lane = 0; lane < GetProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { unsigned int iSlice = iSliceBase + lane; SynchronizeStream(lane); if (mIOPtrs.tpcZS && mCFContext->nPagesSector[iSlice]) { CfFragment f = fragment.next(); int nextSlice = iSlice; if (f.isEnd()) { - nextSlice += GetDeviceProcessingSettings().nTPCClustererLanes; + nextSlice += GetProcessingSettings().nTPCClustererLanes; f = mCFContext->fragmentFirst; } if (nextSlice < NSLICES && mIOPtrs.tpcZS && mCFContext->nPagesSector[iSlice]) { - mCFContext->nextPos[nextSlice] = RunTPCClusterizer_transferZS(nextSlice, f, GetDeviceProcessingSettings().nTPCClustererLanes + lane); + mCFContext->nextPos[nextSlice] = RunTPCClusterizer_transferZS(nextSlice, f, GetProcessingSettings().nTPCClustererLanes + lane); } } GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSlice]; @@ -1201,7 +1241,15 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } if (propagateMCLabels) { - runKernel<GPUTPCCFChargeMapFiller, GPUTPCCFChargeMapFiller::fillIndexMap>(GetGrid(clusterer.mPmemory->counters.nPositions, lane), {iSlice}, {}); + runKernel<GPUTPCCFChargeMapFiller, GPUTPCCFChargeMapFiller::fillIndexMap>(GetGrid(clusterer.mPmemory->counters.nDigitsInFragment, lane, GPUReconstruction::krnlDeviceType::CPU), {iSlice}, {}); + } + + bool checkForNoisyPads = (rec()->GetParam().rec.maxTimeBinAboveThresholdIn1000Bin > 0) || (rec()->GetParam().rec.maxConsecTimeBinAboveThreshold > 0); + checkForNoisyPads &= (rec()->GetParam().rec.noisyPadsQuickCheck ? fragment.index == 0 : true); + + if (checkForNoisyPads) { + int nBlocks = TPC_PADS_IN_SECTOR / GPUTPCCFCheckPadBaseline::getPadsPerBlock(doGPU); + runKernel<GPUTPCCFCheckPadBaseline>(GetGridBlk(nBlocks, lane), {iSlice}, {}); } runKernel<GPUTPCCFPeakFinder>(GetGrid(clusterer.mPmemory->counters.nPositions, lane), {iSlice}, {}); @@ -1211,7 +1259,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); DoDebugAndDump(RecoStep::TPCClusterFinding, 0, clusterer, &GPUTPCClusterFinder::DumpPeaksCompacted, *mDebugFile); } - for (int lane = 0; lane < GetDeviceProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { + for (int lane = 0; lane < GetProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { unsigned int iSlice = iSliceBase + lane; GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSlice]; GPUTPCClusterFinder& clustererShadow = doGPU ? processorsShadow()->tpcClusterer[iSlice] : clusterer; @@ -1227,7 +1275,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); DoDebugAndDump(RecoStep::TPCClusterFinding, 0, clusterer, &GPUTPCClusterFinder::DumpSuppressedPeaksCompacted, *mDebugFile); } - for (int lane = 0; lane < GetDeviceProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { + for (int lane = 0; lane < GetProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { unsigned int iSlice = iSliceBase + lane; GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSlice]; GPUTPCClusterFinder& clustererShadow = doGPU ? processorsShadow()->tpcClusterer[iSlice] : clusterer; @@ -1245,10 +1293,16 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) runKernel<GPUTPCCFDeconvolution>(GetGrid(clusterer.mPmemory->counters.nPositions, lane), {iSlice}, {}); DoDebugAndDump(RecoStep::TPCClusterFinding, 0, clusterer, &GPUTPCClusterFinder::DumpChargeMap, *mDebugFile, "Split Charges"); - runKernel<GPUTPCCFClusterizer>(GetGrid(clusterer.mPmemory->counters.nClusters, lane), {iSlice}, {}); - if (GetDeviceProcessingSettings().debugLevel >= 3) { + runKernel<GPUTPCCFClusterizer>(GetGrid(clusterer.mPmemory->counters.nClusters, lane), {iSlice}, {}, 0); + if (doGPU && propagateMCLabels) { + TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mScratchId, lane); + SynchronizeStream(lane); + runKernel<GPUTPCCFClusterizer>(GetGrid(clusterer.mPmemory->counters.nClusters, lane, GPUReconstruction::krnlDeviceType::CPU), {iSlice}, {}, 1); + } + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Lane %d: Found clusters: digits %u peaks %u clusters %u", lane, (int)clusterer.mPmemory->counters.nPositions, (int)clusterer.mPmemory->counters.nPeaks, (int)clusterer.mPmemory->counters.nClusters); } + TransferMemoryResourcesToHost(RecoStep::TPCClusterFinding, &clusterer, lane); laneHasData[lane] = true; if (DoDebugAndDump(RecoStep::TPCClusterFinding, 0, clusterer, &GPUTPCClusterFinder::DumpCountedPeaks, *mDebugFile)) { @@ -1258,20 +1312,20 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } size_t nClsFirst = nClsTotal; bool anyLaneHasData = false; - for (int lane = 0; lane < GetDeviceProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { + for (int lane = 0; lane < GetProcessingSettings().nTPCClustererLanes && iSliceBase + lane < NSLICES; lane++) { unsigned int iSlice = iSliceBase + lane; - std::fill(&tmpNative->nClusters[iSlice][0], &tmpNative->nClusters[iSlice][0] + tpc::Constants::MAXGLOBALPADROW, 0); + std::fill(&tmpNative->nClusters[iSlice][0], &tmpNative->nClusters[iSlice][0] + MAXGLOBALPADROW, 0); SynchronizeStream(lane); GPUTPCClusterFinder& clusterer = processors()->tpcClusterer[iSlice]; GPUTPCClusterFinder& clustererShadow = doGPU ? processorsShadow()->tpcClusterer[iSlice] : clusterer; if (laneHasData[lane]) { anyLaneHasData = true; - if (buildNativeGPU && GetDeviceProcessingSettings().tpccfGatherKernel) { + if (buildNativeGPU && GetProcessingSettings().tpccfGatherKernel) { runKernel<GPUTPCCFGather>(GetGridBlk(GPUCA_ROW_COUNT, mRec->NStreams() - 1), {iSlice}, {}, &mInputsShadow->mPclusterNativeBuffer[nClsTotal]); } for (unsigned int j = 0; j < GPUCA_ROW_COUNT; j++) { if (buildNativeGPU) { - if (!GetDeviceProcessingSettings().tpccfGatherKernel) { + if (!GetProcessingSettings().tpccfGatherKernel) { GPUMemCpyAlways(RecoStep::TPCClusterFinding, (void*)&mInputsShadow->mPclusterNativeBuffer[nClsTotal], (const void*)&clustererShadow.mPclusterByRow[j * clusterer.mNMaxClusterPerRow], sizeof(mIOPtrs.clustersNative->clustersLinear[0]) * clusterer.mPclusterInRow[j], mRec->NStreams() - 1, -2); } } else if (buildNativeHost) { @@ -1287,40 +1341,48 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) transferRunning[lane] = 1; } - if (not propagateMCLabels) { + if (not propagateMCLabels || clusterer.mPmemory->counters.nClusters == 0) { continue; } - runKernel<GPUTPCCFMCLabelFlattener, GPUTPCCFMCLabelFlattener::setRowOffsets>(GetGrid(GPUCA_ROW_COUNT, lane), {iSlice}, {}); + runKernel<GPUTPCCFMCLabelFlattener, GPUTPCCFMCLabelFlattener::setRowOffsets>(GetGrid(GPUCA_ROW_COUNT, lane, GPUReconstruction::krnlDeviceType::CPU), {iSlice}, {}); GPUTPCCFMCLabelFlattener::setGlobalOffsetsAndAllocate(clusterer, mcLinearLabels); - for (unsigned int j = 0; j < GPUCA_ROW_COUNT; j++) { - if (clusterer.mPclusterInRow[j] == 0) { - continue; - } - runKernel<GPUTPCCFMCLabelFlattener, GPUTPCCFMCLabelFlattener::flatten>(GetGrid(clusterer.mPclusterInRow[j], lane), {iSlice}, {}, j, &mcLinearLabels); - } + runKernel<GPUTPCCFMCLabelFlattener, GPUTPCCFMCLabelFlattener::flatten>(GetGrid(GPUCA_ROW_COUNT, lane, GPUReconstruction::krnlDeviceType::CPU), {iSlice}, {}, &mcLinearLabels); + clusterer.clearMCMemory(); } if (buildNativeHost && buildNativeGPU && anyLaneHasData) { - if (GetDeviceProcessingSettings().delayedOutput) { + if (GetProcessingSettings().delayedOutput) { mOutputQueue.emplace_back(outputQueueEntry{(void*)((char*)&mInputsHost->mPclusterNativeOutput[nClsFirst] - (char*)&mInputsHost->mPclusterNativeOutput[0]), &mInputsShadow->mPclusterNativeBuffer[nClsFirst], (nClsTotal - nClsFirst) * sizeof(mInputsHost->mPclusterNativeOutput[nClsFirst]), RecoStep::TPCClusterFinding}); } else { GPUMemCpy(RecoStep::TPCClusterFinding, (void*)&mInputsHost->mPclusterNativeOutput[nClsFirst], (void*)&mInputsShadow->mPclusterNativeBuffer[nClsFirst], (nClsTotal - nClsFirst) * sizeof(mInputsHost->mPclusterNativeOutput[nClsFirst]), mRec->NStreams() - 1, false); } } } - for (int i = 0; i < GetDeviceProcessingSettings().nTPCClustererLanes; i++) { + for (int i = 0; i < GetProcessingSettings().nTPCClustererLanes; i++) { if (transferRunning[i]) { ReleaseEvent(&mEvents->stream[i]); } } - static o2::dataformats::MCTruthContainer<o2::MCCompLabel> mcLabels; + ClusterNativeAccess::ConstMCLabelContainerView* mcLabelsConstView = nullptr; + if (propagateMCLabels) { + // TODO: write to buffer directly + o2::dataformats::MCTruthContainer<o2::MCCompLabel> mcLabels; + if (mOutputClusterLabels == nullptr || !mOutputClusterLabels->OutputAllocator) { + throw std::runtime_error("Cluster MC Label buffer missing"); + } + ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer* container = reinterpret_cast<ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer*>(mOutputClusterLabels->OutputAllocator(0)); - assert(propagateMCLabels ? mcLinearLabels.header.size() == nClsTotal : true); + assert(propagateMCLabels ? mcLinearLabels.header.size() == nClsTotal : true); + assert(propagateMCLabels ? mcLinearLabels.data.size() >= nClsTotal : true); - mcLabels.setFrom(mcLinearLabels.header, mcLinearLabels.data); + mcLabels.setFrom(mcLinearLabels.header, mcLinearLabels.data); + mcLabels.flatten_to(container->first); + container->second = container->first; + mcLabelsConstView = &container->second; + } - if (buildNativeHost && buildNativeGPU && GetDeviceProcessingSettings().delayedOutput) { + if (buildNativeHost && buildNativeGPU && GetProcessingSettings().delayedOutput) { mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = nClsTotal; AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mOutputClustersNative); for (unsigned int i = outputQueueStart; i < mOutputQueue.size(); i++) { @@ -1330,10 +1392,18 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (buildNativeHost) { tmpNative->clustersLinear = mInputsHost->mPclusterNativeOutput; - tmpNative->clustersMCTruth = propagateMCLabels ? &mcLabels : nullptr; + tmpNative->clustersMCTruth = mcLabelsConstView; tmpNative->setOffsetPtrs(); mIOPtrs.clustersNative = tmpNative; } + + if (mPipelineNotifyCtx) { + SynchronizeStream(mRec->NStreams() - 2); // Must finish before updating ioPtrs in (global) constant memory + std::lock_guard<std::mutex> lock(mPipelineNotifyCtx->mutex); + mPipelineNotifyCtx->ready = true; + mPipelineNotifyCtx->cond.notify_one(); + } + if (buildNativeGPU) { processorsShadow()->ioPtrs.clustersNative = mInputsShadow->mPclusterNativeAccess; WriteToConstantMemory(RecoStep::TPCClusterFinding, (char*)&processors()->ioPtrs - (char*)processors(), &processorsShadow()->ioPtrs, sizeof(processorsShadow()->ioPtrs), 0); @@ -1345,7 +1415,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (synchronizeOutput) { SynchronizeStream(mRec->NStreams() - 1); } - if (buildNativeHost && GetDeviceProcessingSettings().debugLevel >= 4) { + if (buildNativeHost && GetProcessingSettings().debugLevel >= 4) { for (unsigned int i = 0; i < NSLICES; i++) { for (unsigned int j = 0; j < GPUCA_ROW_COUNT; j++) { std::sort(&mInputsHost->mPclusterNativeOutput[tmpNative->clusterOffset[i][j]], &mInputsHost->mPclusterNativeOutput[tmpNative->clusterOffset[i][j] + tmpNative->nClusters[i][j]]); @@ -1354,13 +1424,9 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } mRec->MemoryScalers()->nTPCHits = nClsTotal; mRec->PopNonPersistentMemory(RecoStep::TPCClusterFinding); - if (mPipelineNotifyCtx) { - SynchronizeStream(mRec->NStreams() - 2); mRec->UnblockStackedMemory(); - std::lock_guard<std::mutex> lock(mPipelineNotifyCtx->mutex); - mPipelineNotifyCtx->ready = true; - mPipelineNotifyCtx->cond.notify_one(); + mPipelineNotifyCtx = nullptr; } #endif @@ -1388,12 +1454,12 @@ int GPUChainTracking::RunTPCTrackingSlices() int GPUChainTracking::RunTPCTrackingSlices_internal() { - if (GetDeviceProcessingSettings().debugLevel >= 2) { + if (GetProcessingSettings().debugLevel >= 2) { GPUInfo("Running TPC Slice Tracker"); } bool doGPU = GetRecoStepsGPU() & RecoStep::TPCSliceTracking; bool doSliceDataOnGPU = processors()->tpcTrackers[0].SliceDataOnGPU(); - if (!param().earlyTpcTransform) { + if (!param().par.earlyTpcTransform) { for (unsigned int i = 0; i < NSLICES; i++) { processors()->tpcTrackers[i].Data().SetClusterData(nullptr, mIOPtrs.clustersNative->nClustersSector[i], mIOPtrs.clustersNative->clusterOffset[i][0]); if (doGPU) { @@ -1428,7 +1494,7 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() mRec->PushNonPersistentMemory(); for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { SetupGPUProcessor(&processors()->tpcTrackers[iSlice], true); // Now we allocate - mRec->ResetRegisteredMemoryPointers(&processors()->tpcTrackers[iSlice]); // TODO: The above call breaks the GPU ptrs to already allocated memory. This fixes them. Should actually be cleaner up at the source. + mRec->ResetRegisteredMemoryPointers(&processors()->tpcTrackers[iSlice]); // TODO: The above call breaks the GPU ptrs to already allocated memory. This fixes them. Should actually be cleaned up at the source. processors()->tpcTrackers[iSlice].SetupCommonMemory(); } @@ -1449,7 +1515,7 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() } // Copy Tracker Object to GPU Memory - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Copying Tracker objects to GPU"); } if (PrepareProfile()) { @@ -1470,44 +1536,47 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() int streamMap[NSLICES]; bool error = false; - GPUCA_OPENMP(parallel for num_threads(doGPU || GetDeviceProcessingSettings().ompKernels ? 1 : GetDeviceProcessingSettings().nThreads)) + GPUCA_OPENMP(parallel for if(!doGPU && GetProcessingSettings().ompKernels != 1) num_threads(mRec->SetAndGetNestedLoopOmpFactor(!doGPU, NSLICES))) for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { + if (mRec->GetDeviceType() == GPUReconstruction::DeviceType::HIP) { + SynchronizeGPU(); // BUG: Workaround for probable bug in AMD runtime, crashes randomly if not synchronized here + } GPUTPCTracker& trk = processors()->tpcTrackers[iSlice]; GPUTPCTracker& trkShadow = doGPU ? processorsShadow()->tpcTrackers[iSlice] : trk; int useStream = (iSlice % mRec->NStreams()); - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Creating Slice Data (Slice %d)", iSlice); } if (doSliceDataOnGPU) { TransferMemoryResourcesToGPU(RecoStep::TPCSliceTracking, &trk, useStream); runKernel<GPUTPCCreateSliceData>(GetGridBlk(GPUCA_ROW_COUNT, useStream), {iSlice}, {nullptr, streamInit[useStream] ? nullptr : &mEvents->init}); streamInit[useStream] = true; - } else if (!doGPU || iSlice % (GetDeviceProcessingSettings().nDeviceHelperThreads + 1) == 0) { + } else if (!doGPU || iSlice % (GetProcessingSettings().nDeviceHelperThreads + 1) == 0) { if (ReadEvent(iSlice, 0)) { GPUError("Error reading event"); error = 1; continue; } } else { - if (GetDeviceProcessingSettings().debugLevel >= 3) { - GPUInfo("Waiting for helper thread %d", iSlice % (GetDeviceProcessingSettings().nDeviceHelperThreads + 1) - 1); + if (GetProcessingSettings().debugLevel >= 3) { + GPUInfo("Waiting for helper thread %d", iSlice % (GetProcessingSettings().nDeviceHelperThreads + 1) - 1); } - while (HelperDone(iSlice % (GetDeviceProcessingSettings().nDeviceHelperThreads + 1) - 1) < (int)iSlice) { + while (HelperDone(iSlice % (GetProcessingSettings().nDeviceHelperThreads + 1) - 1) < (int)iSlice) { ; } - if (HelperError(iSlice % (GetDeviceProcessingSettings().nDeviceHelperThreads + 1) - 1)) { + if (HelperError(iSlice % (GetProcessingSettings().nDeviceHelperThreads + 1) - 1)) { error = 1; continue; } } - if (!doGPU && trk.CheckEmptySlice() && GetDeviceProcessingSettings().debugLevel == 0) { + if (!doGPU && trk.CheckEmptySlice() && GetProcessingSettings().debugLevel == 0) { continue; } - if (GetDeviceProcessingSettings().debugLevel >= 6) { + if (GetProcessingSettings().debugLevel >= 6) { *mDebugFile << "\n\nReconstruction: Slice " << iSlice << "/" << NSLICES << std::endl; - if (GetDeviceProcessingSettings().debugMask & 1) { + if (GetProcessingSettings().debugMask & 1) { if (doSliceDataOnGPU) { TransferMemoryResourcesToHost(RecoStep::TPCSliceTracking, &trk, -1, true); } @@ -1516,10 +1585,10 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() } // Initialize temporary memory where needed - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Copying Slice Data to GPU and initializing temporary memory"); } - if (GetDeviceProcessingSettings().keepAllMemory && !doSliceDataOnGPU) { + if (GetProcessingSettings().keepDisplayMemory && !doSliceDataOnGPU) { memset((void*)trk.Data().HitWeights(), 0, trkShadow.Data().NumberOfHitsPlusAlign() * sizeof(*trkShadow.Data().HitWeights())); } else { runKernel<GPUMemClean16>(GetGridAutoStep(useStream, RecoStep::TPCSliceTracking), krnlRunRangeNone, {}, trkShadow.Data().HitWeights(), trkShadow.Data().NumberOfHitsPlusAlign() * sizeof(*trkShadow.Data().HitWeights())); @@ -1536,10 +1605,10 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() runKernel<GPUTPCNeighboursFinder>(GetGridBlk(GPUCA_ROW_COUNT, useStream), {iSlice}, {nullptr, streamInit[useStream] ? nullptr : &mEvents->init}); streamInit[useStream] = true; - if (GetDeviceProcessingSettings().keepDisplayMemory) { + if (GetProcessingSettings().keepDisplayMemory) { TransferMemoryResourcesToHost(RecoStep::TPCSliceTracking, &trk, -1, true); memcpy(trk.LinkTmpMemory(), mRec->Res(trk.MemoryResLinks()).Ptr(), mRec->Res(trk.MemoryResLinks()).Size()); - if (GetDeviceProcessingSettings().debugMask & 2) { + if (GetProcessingSettings().debugMask & 2) { trk.DumpLinks(*mDebugFile); } } @@ -1555,43 +1624,44 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() #endif DoDebugAndDump(RecoStep::TPCSliceTracking, 32, trk, &GPUTPCTracker::DumpStartHits, *mDebugFile); - if (GetDeviceProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { trk.UpdateMaxData(); AllocateRegisteredMemory(trk.MemoryResTracklets()); AllocateRegisteredMemory(trk.MemoryResOutput()); } - if (!(doGPU || GetDeviceProcessingSettings().debugLevel >= 1) || GetDeviceProcessingSettings().trackletConstructorInPipeline) { + if (!(doGPU || GetProcessingSettings().debugLevel >= 1) || GetProcessingSettings().trackletConstructorInPipeline) { runKernel<GPUTPCTrackletConstructor>(GetGridAuto(useStream), {iSlice}); DoDebugAndDump(RecoStep::TPCSliceTracking, 128, trk, &GPUTPCTracker::DumpTrackletHits, *mDebugFile); - if (GetDeviceProcessingSettings().debugMask & 256 && !GetDeviceProcessingSettings().comparableDebutOutput) { + if (GetProcessingSettings().debugMask & 256 && !GetProcessingSettings().comparableDebutOutput) { trk.DumpHitWeights(*mDebugFile); } } - if (!(doGPU || GetDeviceProcessingSettings().debugLevel >= 1) || GetDeviceProcessingSettings().trackletSelectorInPipeline) { + if (!(doGPU || GetProcessingSettings().debugLevel >= 1) || GetProcessingSettings().trackletSelectorInPipeline) { runKernel<GPUTPCTrackletSelector>(GetGridAuto(useStream), {iSlice}); runKernel<GPUTPCGlobalTrackingCopyNumbers>({1, -ThreadCount(), useStream}, {iSlice}, {}, 1); TransferMemoryResourceLinkToHost(RecoStep::TPCSliceTracking, trk.MemoryResCommon(), useStream, &mEvents->slice[iSlice]); streamMap[iSlice] = useStream; - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Slice %u, Number of tracks: %d", iSlice, *trk.NTracks()); } DoDebugAndDump(RecoStep::TPCSliceTracking, 512, trk, &GPUTPCTracker::DumpTrackHits, *mDebugFile); } } + mRec->SetNestedLoopOmpFactor(1); if (error) { return (3); } - if (doGPU || GetDeviceProcessingSettings().debugLevel >= 1) { + if (doGPU || GetProcessingSettings().debugLevel >= 1) { ReleaseEvent(&mEvents->init); if (!doSliceDataOnGPU) { WaitForHelperThreads(); } - if (!GetDeviceProcessingSettings().trackletSelectorInPipeline) { - if (GetDeviceProcessingSettings().trackletConstructorInPipeline) { + if (!GetProcessingSettings().trackletSelectorInPipeline) { + if (GetProcessingSettings().trackletConstructorInPipeline) { SynchronizeGPU(); } else { for (int i = 0; i < mRec->NStreams(); i++) { @@ -1605,7 +1675,7 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() ReleaseEvent(&mEvents->single); } - if (GetDeviceProcessingSettings().debugLevel >= 4) { + if (GetProcessingSettings().debugLevel >= 4) { for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { DoDebugAndDump(RecoStep::TPCSliceTracking, 128, processors()->tpcTrackers[iSlice], &GPUTPCTracker::DumpTrackletHits, *mDebugFile); } @@ -1614,7 +1684,7 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() int runSlices = 0; int useStream = 0; for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice += runSlices) { - if (runSlices < GetDeviceProcessingSettings().trackletSelectorSlices) { + if (runSlices < GetProcessingSettings().trackletSelectorSlices) { runSlices++; } runSlices = CAMath::Min<int>(runSlices, NSLICES - iSlice); @@ -1622,7 +1692,7 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() runSlices = getKernelProperties<GPUTPCTrackletSelector>().minBlocks * BlockCount(); } - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Running TPC Tracklet selector (Stream %d, Slice %d to %d)", useStream, iSlice, iSlice + runSlices); } runKernel<GPUTPCTrackletSelector>(GetGridAuto(useStream), {iSlice, runSlices}); @@ -1650,7 +1720,7 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() unsigned int tmpSlice = 0; for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Transfering Tracks from GPU to Host"); } @@ -1667,15 +1737,15 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() tmpSlice++; } - if (GetDeviceProcessingSettings().keepAllMemory) { + if (GetProcessingSettings().keepAllMemory) { TransferMemoryResourcesToHost(RecoStep::TPCSliceTracking, &processors()->tpcTrackers[iSlice], -1, true); - if (!GetDeviceProcessingSettings().trackletConstructorInPipeline) { - if (GetDeviceProcessingSettings().debugMask & 256 && !GetDeviceProcessingSettings().comparableDebutOutput) { + if (!GetProcessingSettings().trackletConstructorInPipeline) { + if (GetProcessingSettings().debugMask & 256 && !GetProcessingSettings().comparableDebutOutput) { processors()->tpcTrackers[iSlice].DumpHitWeights(*mDebugFile); } } - if (!GetDeviceProcessingSettings().trackletSelectorInPipeline) { - if (GetDeviceProcessingSettings().debugMask & 512) { + if (!GetProcessingSettings().trackletSelectorInPipeline) { + if (GetProcessingSettings().debugMask & 512) { processors()->tpcTrackers[iSlice].DumpTrackHits(*mDebugFile); } } @@ -1684,24 +1754,20 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() if (transferRunning[iSlice]) { SynchronizeEvents(&mEvents->slice[iSlice]); } - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Tracks Transfered: %d / %d", *processors()->tpcTrackers[iSlice].NTracks(), *processors()->tpcTrackers[iSlice].NTrackHits()); } - if (GetDeviceProcessingSettings().debugLevel >= 3) { - GPUInfo("Data ready for slice %d, helper thread %d", iSlice, iSlice % (GetDeviceProcessingSettings().nDeviceHelperThreads + 1)); + if (GetProcessingSettings().debugLevel >= 3) { + GPUInfo("Data ready for slice %d, helper thread %d", iSlice, iSlice % (GetProcessingSettings().nDeviceHelperThreads + 1)); } mSliceSelectorReady = iSlice; if (param().rec.GlobalTracking) { - for (unsigned int tmpSlice2a = 0; tmpSlice2a <= iSlice; tmpSlice2a += GetDeviceProcessingSettings().nDeviceHelperThreads + 1) { + for (unsigned int tmpSlice2a = 0; tmpSlice2a <= iSlice; tmpSlice2a += GetProcessingSettings().nDeviceHelperThreads + 1) { unsigned int tmpSlice2 = GPUTPCGlobalTracking::GlobalTrackingSliceOrder(tmpSlice2a); - unsigned int sliceLeft = (tmpSlice2 + (NSLICES / 2 - 1)) % (NSLICES / 2); - unsigned int sliceRight = (tmpSlice2 + 1) % (NSLICES / 2); - if (tmpSlice2 >= (int)NSLICES / 2) { - sliceLeft += NSLICES / 2; - sliceRight += NSLICES / 2; - } + unsigned int sliceLeft, sliceRight; + GPUTPCGlobalTracking::GlobalTrackingSliceLeftRight(tmpSlice2, sliceLeft, sliceRight); if (tmpSlice2 <= iSlice && sliceLeft <= iSlice && sliceRight <= iSlice && mWriteOutputDone[tmpSlice2] == 0) { GlobalTracking(tmpSlice2, 0); @@ -1710,24 +1776,45 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() } } } else { - if (iSlice % (GetDeviceProcessingSettings().nDeviceHelperThreads + 1) == 0) { + if (iSlice % (GetProcessingSettings().nDeviceHelperThreads + 1) == 0) { WriteOutput(iSlice, 0); } } } WaitForHelperThreads(); } + if (!(GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCSectorTracks) && param().rec.GlobalTracking) { + std::vector<bool> blocking(NSLICES * mRec->NStreams()); + for (int i = 0; i < NSLICES; i++) { + for (int j = 0; j < mRec->NStreams(); j++) { + blocking[i * mRec->NStreams() + j] = i % mRec->NStreams() == j; + } + } + for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { + unsigned int tmpSlice = GPUTPCGlobalTracking::GlobalTrackingSliceOrder(iSlice); + if (!((GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCSectorTracks) || (doGPU && !(GetRecoStepsGPU() & RecoStep::TPCMerging)))) { + unsigned int sliceLeft, sliceRight; + GPUTPCGlobalTracking::GlobalTrackingSliceLeftRight(tmpSlice, sliceLeft, sliceRight); + if (!blocking[tmpSlice * mRec->NStreams() + sliceLeft % mRec->NStreams()]) { + StreamWaitForEvents(tmpSlice % mRec->NStreams(), &mEvents->slice[sliceLeft]); + blocking[tmpSlice * mRec->NStreams() + sliceLeft % mRec->NStreams()] = true; + } + if (!blocking[tmpSlice * mRec->NStreams() + sliceRight % mRec->NStreams()]) { + StreamWaitForEvents(tmpSlice % mRec->NStreams(), &mEvents->slice[sliceRight]); + blocking[tmpSlice * mRec->NStreams() + sliceRight % mRec->NStreams()] = true; + } + } + GlobalTracking(tmpSlice, 0, !GetProcessingSettings().fullMergerOnGPU); + } + } for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { if (transferRunning[iSlice]) { ReleaseEvent(&mEvents->slice[iSlice]); } - if (!(GetRecoStepsOutputs() & GPUDataTypes::InOutType::TPCSectorTracks) && param().rec.GlobalTracking) { - GlobalTracking(iSlice, 0, !GetDeviceProcessingSettings().fullMergerOnGPU); - } } } else { mSliceSelectorReady = NSLICES; - GPUCA_OPENMP(parallel for num_threads(doGPU || GetDeviceProcessingSettings().ompKernels ? 1 : GetDeviceProcessingSettings().nThreads)) + GPUCA_OPENMP(parallel for if(!doGPU && GetProcessingSettings().ompKernels != 1) num_threads(mRec->SetAndGetNestedLoopOmpFactor(!doGPU, NSLICES))) for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { if (param().rec.GlobalTracking) { GlobalTracking(iSlice, 0); @@ -1736,16 +1823,17 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() WriteOutput(iSlice, 0); } } + mRec->SetNestedLoopOmpFactor(1); } - if (param().rec.GlobalTracking && GetDeviceProcessingSettings().debugLevel >= 3) { + if (param().rec.GlobalTracking && GetProcessingSettings().debugLevel >= 3) { for (unsigned int iSlice = 0; iSlice < NSLICES; iSlice++) { GPUInfo("Slice %d - Tracks: Local %d Global %d - Hits: Local %d Global %d", iSlice, processors()->tpcTrackers[iSlice].CommonMemory()->nLocalTracks, processors()->tpcTrackers[iSlice].CommonMemory()->nTracks, processors()->tpcTrackers[iSlice].CommonMemory()->nLocalTrackHits, processors()->tpcTrackers[iSlice].CommonMemory()->nTrackHits); } } - if (GetDeviceProcessingSettings().debugMask & 1024 && !GetDeviceProcessingSettings().comparableDebutOutput) { + if (GetProcessingSettings().debugMask & 1024 && !GetProcessingSettings().comparableDebutOutput) { for (unsigned int i = 0; i < NSLICES; i++) { processors()->tpcTrackers[i].DumpOutput(*mDebugFile); } @@ -1759,12 +1847,11 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() mIOPtrs.sliceTracks[i] = processors()->tpcTrackers[i].Tracks(); mIOPtrs.nSliceClusters[i] = *processors()->tpcTrackers[i].NTrackHits(); mIOPtrs.sliceClusters[i] = processors()->tpcTrackers[i].TrackHits(); - if (GetDeviceProcessingSettings().keepDisplayMemory && !GetDeviceProcessingSettings().keepAllMemory) { + if (GetProcessingSettings().keepDisplayMemory && !GetProcessingSettings().keepAllMemory) { TransferMemoryResourcesToHost(RecoStep::TPCSliceTracking, &processors()->tpcTrackers[i], -1, true); } } - SynchronizeGPU(); // Todo: why needed? processing small data fails otherwise - if (GetDeviceProcessingSettings().debugLevel >= 2) { + if (GetProcessingSettings().debugLevel >= 2) { GPUInfo("TPC Slice Tracker finished"); } mRec->PopNonPersistentMemory(RecoStep::TPCSliceTracking); @@ -1774,8 +1861,8 @@ int GPUChainTracking::RunTPCTrackingSlices_internal() void GPUChainTracking::RunTPCTrackingMerger_MergeBorderTracks(char withinSlice, char mergeMode, GPUReconstruction::krnlDeviceType deviceType) { unsigned int n = withinSlice == -1 ? NSLICES / 2 : NSLICES; - bool doGPUall = GetRecoStepsGPU() & RecoStep::TPCMerging && GetDeviceProcessingSettings().fullMergerOnGPU; - if (GetDeviceProcessingSettings().alternateBorderSort && (!mRec->IsGPU() || doGPUall)) { + bool doGPUall = GetRecoStepsGPU() & RecoStep::TPCMerging && GetProcessingSettings().fullMergerOnGPU; + if (GetProcessingSettings().alternateBorderSort && (!mRec->IsGPU() || doGPUall)) { GPUTPCGMMerger& Merger = processors()->tpcMerger; GPUTPCGMMerger& MergerShadow = doGPUall ? processorsShadow()->tpcMerger : Merger; TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResMemory(), 0, &mEvents->init); @@ -1793,8 +1880,8 @@ void GPUChainTracking::RunTPCTrackingMerger_MergeBorderTracks(char withinSlice, GPUTPCGMBorderTrack *b1, *b2; int jSlice; Merger.MergeBorderTracksSetup(n1, n2, b1, b2, jSlice, i, withinSlice, mergeMode); - GPUTPCGMMergerTypes::GPUTPCGMBorderRange* range1 = MergerShadow.BorderRange(i); - GPUTPCGMMergerTypes::GPUTPCGMBorderRange* range2 = MergerShadow.BorderRange(jSlice) + *processors()->tpcTrackers[jSlice].NTracks(); + gputpcgmmergertypes::GPUTPCGMBorderRange* range1 = MergerShadow.BorderRange(i); + gputpcgmmergertypes::GPUTPCGMBorderRange* range2 = MergerShadow.BorderRange(jSlice) + *processors()->tpcTrackers[jSlice].NTracks(); runKernel<GPUTPCGMMergerMergeBorders, 3>({1, -WarpSize(), stream, deviceType}, krnlRunRangeNone, krnlEventNone, range1, n1, 0); runKernel<GPUTPCGMMergerMergeBorders, 3>({1, -WarpSize(), stream, deviceType}, krnlRunRangeNone, krnlEventNone, range2, n2, 1); deviceEvent** e = nullptr; @@ -1833,11 +1920,11 @@ void GPUChainTracking::RunTPCTrackingMerger_Resolve(char useOrigTrackParam, char int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) { - if (GetDeviceProcessingSettings().debugLevel >= 6 && GetDeviceProcessingSettings().comparableDebutOutput && param().rec.mergerReadFromTrackerDirectly) { + if (GetProcessingSettings().debugLevel >= 6 && GetProcessingSettings().comparableDebutOutput && param().rec.mergerReadFromTrackerDirectly) { for (unsigned int i = 0; i < NSLICES; i++) { GPUTPCTracker& trk = processors()->tpcTrackers[i]; TransferMemoryResourcesToHost(RecoStep::NoRecoStep, &trk); - auto sorter = [&trk](GPUTPCTrack& trk1, GPUTPCTrack& trk2) { + auto sorter = [](GPUTPCTrack& trk1, GPUTPCTrack& trk2) { if (trk1.NHits() == trk2.NHits()) { return trk1.Param().Y() > trk2.Param().Y(); } @@ -1850,18 +1937,18 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) } mRec->PushNonPersistentMemory(); bool doGPU = GetRecoStepsGPU() & RecoStep::TPCMerging; - bool doGPUall = doGPU && GetDeviceProcessingSettings().fullMergerOnGPU; + bool doGPUall = doGPU && GetProcessingSettings().fullMergerOnGPU; GPUReconstruction::krnlDeviceType deviceType = doGPUall ? GPUReconstruction::krnlDeviceType::Auto : GPUReconstruction::krnlDeviceType::CPU; unsigned int numBlocks = (!mRec->IsGPU() || doGPUall) ? BlockCount() : 1; - GPUTPCGMMerger& Merger = processors()->tpcMerger; GPUTPCGMMerger& MergerShadow = doGPU ? processorsShadow()->tpcMerger : Merger; GPUTPCGMMerger& MergerShadowAll = doGPUall ? processorsShadow()->tpcMerger : Merger; - if (GetDeviceProcessingSettings().debugLevel >= 2) { + if (GetProcessingSettings().debugLevel >= 2) { GPUInfo("Running TPC Merger"); } const auto& threadContext = GetThreadContext(); + SynchronizeGPU(); // Need to know the full number of slice tracks SetupGPUProcessor(&Merger, true); AllocateRegisteredMemory(Merger.MemoryResOutput(), mOutputTPCTracks); @@ -1926,7 +2013,7 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) waitForTransfer = 1; } - if (GetDeviceProcessingSettings().mergerSortTracks) { + if (GetProcessingSettings().mergerSortTracks) { runKernel<GPUTPCGMMergerSortTracksPrepare>(GetGridAuto(0, deviceType), krnlRunRangeNone, krnlEventNone); CondWaitEvent(waitForTransfer, &mEvents->single); runKernel<GPUTPCGMMergerSortTracks>(GetGridAuto(0, deviceType), krnlRunRangeNone, krnlEventNone); @@ -1958,19 +2045,19 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) TransferMemoryResourcesToGPU(RecoStep::TPCMerging, &Merger, 0); } - if (GetDeviceProcessingSettings().delayedOutput) { + if (GetProcessingSettings().delayedOutput) { for (unsigned int i = 0; i < mOutputQueue.size(); i++) { GPUMemCpy(mOutputQueue[i].step, mOutputQueue[i].dst, mOutputQueue[i].src, mOutputQueue[i].size, mRec->NStreams() - 2, false); } mOutputQueue.clear(); } - runKernel<GPUTPCGMMergerTrackFit>(GetGridBlk(Merger.NOutputTracks(), 0), krnlRunRangeNone, krnlEventNone, GetDeviceProcessingSettings().mergerSortTracks ? 1 : 0); + runKernel<GPUTPCGMMergerTrackFit>(doGPU ? GetGrid(Merger.NOutputTracks(), 0) : GetGridAuto(0), krnlRunRangeNone, krnlEventNone, GetProcessingSettings().mergerSortTracks ? 1 : 0); if (param().rec.retryRefit == 1) { runKernel<GPUTPCGMMergerTrackFit>(GetGridAuto(0), krnlRunRangeNone, krnlEventNone, -1); } if (param().rec.loopInterpolationInExtraPass) { - runKernel<GPUTPCGMMergerFollowLoopers>(GetGridBlk(Merger.NOutputTracks(), 0), krnlRunRangeNone, krnlEventNone); + runKernel<GPUTPCGMMergerFollowLoopers>(GetGridAuto(0), krnlRunRangeNone, krnlEventNone); } if (doGPU && !doGPUall) { TransferMemoryResourcesToHost(RecoStep::TPCMerging, &Merger, 0); @@ -1983,16 +2070,19 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) runKernel<GPUTPCGMMergerFinalize, 1>(GetGridAuto(0, deviceType), krnlRunRangeNone, krnlEventNone); runKernel<GPUTPCGMMergerFinalize, 2>(GetGridAuto(0, deviceType), krnlRunRangeNone, krnlEventNone); } + if (param().rec.tpcMergeLoopersAfterburner) { + runKernel<GPUTPCGMMergerMergeLoopers>(GetGridAuto(0, deviceType), krnlRunRangeNone, krnlEventNone); + } DoDebugAndDump(RecoStep::TPCMerging, 0, doGPUall, Merger, &GPUTPCGMMerger::DumpFinal, *mDebugFile); if (doGPUall) { RecordMarker(&mEvents->single, 0); - if (!GetDeviceProcessingSettings().fullMergerOnGPU) { + if (!GetProcessingSettings().fullMergerOnGPU) { TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResOutput(), mRec->NStreams() - 2, nullptr, &mEvents->single); } else { GPUMemCpy(RecoStep::TPCMerging, Merger.OutputTracks(), MergerShadowAll.OutputTracks(), Merger.NOutputTracks() * sizeof(*Merger.OutputTracks()), mRec->NStreams() - 2, 0, nullptr, &mEvents->single); GPUMemCpy(RecoStep::TPCMerging, Merger.Clusters(), MergerShadowAll.Clusters(), Merger.NOutputTrackClusters() * sizeof(*Merger.Clusters()), mRec->NStreams() - 2, 0); - if (param().earlyTpcTransform) { + if (param().par.earlyTpcTransform) { GPUMemCpy(RecoStep::TPCMerging, Merger.ClustersXYZ(), MergerShadowAll.ClustersXYZ(), Merger.NOutputTrackClusters() * sizeof(*Merger.ClustersXYZ()), mRec->NStreams() - 2, 0); } GPUMemCpy(RecoStep::TPCMerging, Merger.ClusterAttachment(), MergerShadowAll.ClusterAttachment(), Merger.NMaxClusters() * sizeof(*Merger.ClusterAttachment()), mRec->NStreams() - 2, 0); @@ -2004,7 +2094,7 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) } else { TransferMemoryResourcesToGPU(RecoStep::TPCMerging, &Merger, 0); } - if (GetDeviceProcessingSettings().keepDisplayMemory && !GetDeviceProcessingSettings().keepAllMemory) { + if (GetProcessingSettings().keepDisplayMemory && !GetProcessingSettings().keepAllMemory) { TransferMemoryResourcesToHost(RecoStep::TPCMerging, &Merger, -1, true); } mRec->ReturnVolatileDeviceMemory(); @@ -2014,8 +2104,18 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) mIOPtrs.mergedTrackHits = Merger.Clusters(); mIOPtrs.nMergedTrackHits = Merger.NOutputTrackClusters(); mIOPtrs.mergedTrackHitAttachment = Merger.ClusterAttachment(); + mIOPtrs.mergedTrackHitStates = Merger.ClusterStateExt(); + if (doGPU) { + processorsShadow()->ioPtrs.mergedTracks = MergerShadow.OutputTracks(); + processorsShadow()->ioPtrs.nMergedTracks = Merger.NOutputTracks(); + processorsShadow()->ioPtrs.mergedTrackHits = MergerShadow.Clusters(); + processorsShadow()->ioPtrs.nMergedTrackHits = Merger.NOutputTrackClusters(); + processorsShadow()->ioPtrs.mergedTrackHitAttachment = MergerShadow.ClusterAttachment(); + processorsShadow()->ioPtrs.mergedTrackHitStates = MergerShadow.ClusterStateExt(); + WriteToConstantMemory(RecoStep::TPCMerging, (char*)&processors()->ioPtrs - (char*)processors(), &processorsShadow()->ioPtrs, sizeof(processorsShadow()->ioPtrs), 0); + } - if (GetDeviceProcessingSettings().debugLevel >= 2) { + if (GetProcessingSettings().debugLevel >= 2) { GPUInfo("TPC Merger Finished (output clusters %d / input clusters %d)", Merger.NOutputTrackClusters(), Merger.NClusters()); } mRec->PopNonPersistentMemory(RecoStep::TPCMerging); @@ -2031,7 +2131,7 @@ int GPUChainTracking::RunTPCCompression() GPUTPCCompression& Compressor = processors()->tpcCompressor; GPUTPCCompression& CompressorShadow = doGPU ? processorsShadow()->tpcCompressor : Compressor; const auto& threadContext = GetThreadContext(); - if (mPipelineFinalizationCtx) { + if (mPipelineFinalizationCtx && GetProcessingSettings().doublePipelineClusterizer) { RecordMarker(&mEvents->single, 0); } Compressor.mNMaxClusterSliceRow = 0; @@ -2042,6 +2142,10 @@ int GPUChainTracking::RunTPCCompression() } } } + + if (ProcessingSettings().tpcCompressionGatherMode == 3) { + mRec->AllocateVolatileDeviceMemory(0); // make future device memory allocation volatile + } SetupGPUProcessor(&Compressor, true); new (Compressor.mMemory) GPUTPCCompression::memory; @@ -2051,11 +2155,14 @@ int GPUChainTracking::RunTPCCompression() runKernel<GPUTPCCompressionKernels, GPUTPCCompressionKernels::step0attached>(GetGridAuto(0), krnlRunRangeNone, krnlEventNone); runKernel<GPUTPCCompressionKernels, GPUTPCCompressionKernels::step1unattached>(GetGridAuto(0), krnlRunRangeNone, krnlEventNone); TransferMemoryResourcesToHost(myStep, &Compressor, 0); - if (mPipelineFinalizationCtx) { +#ifdef GPUCA_TPC_GEOMETRY_O2 + if (mPipelineFinalizationCtx && GetProcessingSettings().doublePipelineClusterizer) { SynchronizeEvents(&mEvents->single); ReleaseEvent(&mEvents->single); ((GPUChainTracking*)GetNextChainInQueue())->RunTPCClusterizer_prepare(false); + ((GPUChainTracking*)GetNextChainInQueue())->mCFContext->ptrClusterNativeSave = processorsShadow()->ioPtrs.clustersNative; } +#endif SynchronizeStream(0); o2::tpc::CompressedClusters* O = Compressor.mOutput; memset((void*)O, 0, sizeof(*O)); @@ -2067,31 +2174,78 @@ int GPUChainTracking::RunTPCCompression() O->nComppressionModes = param().rec.tpcCompressionModes; size_t outputSize = AllocateRegisteredMemory(Compressor.mMemoryResOutputHost, mOutputCompressedClusters); Compressor.mOutputFlat->set(outputSize, *Compressor.mOutput); + char* hostFlatPtr = (char*)Compressor.mOutput->qTotU; // First array as allocated in GPUTPCCompression::SetPointersCompressedClusters + size_t copySize = 0; + if (ProcessingSettings().tpcCompressionGatherMode == 3) { + CompressorShadow.mOutputA = Compressor.mOutput; + copySize = AllocateRegisteredMemory(Compressor.mMemoryResOutputGPU); // We overwrite Compressor.mOutput with the allocated output pointers on the GPU + } const o2::tpc::CompressedClustersPtrs* P = nullptr; HighResTimer* gatherTimer = nullptr; int outputStream = 0; - if (DeviceProcessingSettings().doublePipeline) { + if (ProcessingSettings().doublePipeline) { SynchronizeStream(mRec->NStreams() - 2); // Synchronize output copies running in parallel from memory that might be released, only the following async copy from stacked memory is safe after the chain finishes. outputStream = mRec->NStreams() - 2; } - if (DeviceProcessingSettings().tpcCompressionGatherMode == 2) { - void* devicePtr = mRec->getGPUPointer(Compressor.mOutputFlat); - if (devicePtr != Compressor.mOutputFlat) { - CompressedClustersPtrs& ptrs = *Compressor.mOutput; // We need to update the ptrs with the gpu-mapped version of the host address space - for (unsigned int i = 0; i < sizeof(ptrs) / sizeof(void*); i++) { - reinterpret_cast<char**>(&ptrs)[i] = reinterpret_cast<char**>(&ptrs)[i] + (reinterpret_cast<char*>(devicePtr) - reinterpret_cast<char*>(Compressor.mOutputFlat)); + if (ProcessingSettings().tpcCompressionGatherMode >= 2) { + if (ProcessingSettings().tpcCompressionGatherMode == 2) { + void* devicePtr = mRec->getGPUPointer(Compressor.mOutputFlat); + if (devicePtr != Compressor.mOutputFlat) { + CompressedClustersPtrs& ptrs = *Compressor.mOutput; // We need to update the ptrs with the gpu-mapped version of the host address space + for (unsigned int i = 0; i < sizeof(ptrs) / sizeof(void*); i++) { + reinterpret_cast<char**>(&ptrs)[i] = reinterpret_cast<char**>(&ptrs)[i] + (reinterpret_cast<char*>(devicePtr) - reinterpret_cast<char*>(Compressor.mOutputFlat)); + } } } TransferMemoryResourcesToGPU(myStep, &Compressor, outputStream); - unsigned int nBlocks = 2; - runKernel<GPUTPCCompressionKernels, GPUTPCCompressionKernels::step2gather>(GetGridBlk(nBlocks, outputStream), krnlRunRangeNone, krnlEventNone); - getKernelTimer<GPUTPCCompressionKernels, GPUTPCCompressionKernels::step2gather>(RecoStep::TPCCompression, 0, outputSize); + constexpr unsigned int nBlocksDefault = 2; + constexpr unsigned int nBlocksMulti = 1 + 2 * 200; + switch (ProcessingSettings().tpcCompressionGatherModeKernel) { + case 0: + runKernel<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::unbuffered>(GetGridBlkStep(nBlocksDefault, outputStream, RecoStep::TPCCompression), krnlRunRangeNone, krnlEventNone); + getKernelTimer<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::unbuffered>(RecoStep::TPCCompression, 0, outputSize); + break; + case 1: + runKernel<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::buffered32>(GetGridBlkStep(nBlocksDefault, outputStream, RecoStep::TPCCompression), krnlRunRangeNone, krnlEventNone); + getKernelTimer<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::buffered32>(RecoStep::TPCCompression, 0, outputSize); + break; + case 2: + runKernel<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::buffered64>(GetGridBlkStep(nBlocksDefault, outputStream, RecoStep::TPCCompression), krnlRunRangeNone, krnlEventNone); + getKernelTimer<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::buffered64>(RecoStep::TPCCompression, 0, outputSize); + break; + case 3: + runKernel<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::buffered128>(GetGridBlkStep(nBlocksDefault, outputStream, RecoStep::TPCCompression), krnlRunRangeNone, krnlEventNone); + getKernelTimer<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::buffered128>(RecoStep::TPCCompression, 0, outputSize); + break; + case 4: + + static_assert((nBlocksMulti & 1) && nBlocksMulti >= 3); + runKernel<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::multiBlock>(GetGridBlkStep(nBlocksMulti, outputStream, RecoStep::TPCCompression), krnlRunRangeNone, krnlEventNone); + getKernelTimer<GPUTPCCompressionGatherKernels, GPUTPCCompressionGatherKernels::multiBlock>(RecoStep::TPCCompression, 0, outputSize); + break; + default: + GPUError("Invalid compression kernel selected."); + return 1; + } + if (ProcessingSettings().tpcCompressionGatherMode == 3) { + RecordMarker(&mEvents->stream[outputStream], outputStream); + char* deviceFlatPts = (char*)Compressor.mOutput->qTotU; + if (GetProcessingSettings().doublePipeline) { + const size_t blockSize = CAMath::nextMultipleOf<1024>(copySize / 30); + const unsigned int n = (copySize + blockSize - 1) / blockSize; + for (unsigned int i = 0; i < n; i++) { + GPUMemCpy(myStep, hostFlatPtr + i * blockSize, deviceFlatPts + i * blockSize, CAMath::Min(blockSize, copySize - i * blockSize), outputStream, false); + } + } else { + GPUMemCpy(myStep, hostFlatPtr, deviceFlatPts, copySize, outputStream, false); + } + } } else { char direction = 0; - if (DeviceProcessingSettings().tpcCompressionGatherMode == 0) { + if (ProcessingSettings().tpcCompressionGatherMode == 0) { P = &CompressorShadow.mPtrs; - } else if (DeviceProcessingSettings().tpcCompressionGatherMode == 1) { + } else if (ProcessingSettings().tpcCompressionGatherMode == 1) { P = &Compressor.mPtrs; direction = -1; gatherTimer = &getTimer<GPUTPCCompressionKernels>("GPUTPCCompression_GatherOnCPU", 0); @@ -2134,10 +2288,16 @@ int GPUChainTracking::RunTPCCompression() GPUMemCpyAlways(myStep, O->timeA, P->timeA, O->nTracks * sizeof(O->timeA[0]), outputStream, direction); GPUMemCpyAlways(myStep, O->padA, P->padA, O->nTracks * sizeof(O->padA[0]), outputStream, direction); } - if (DeviceProcessingSettings().tpcCompressionGatherMode == 1) { + if (ProcessingSettings().tpcCompressionGatherMode == 1) { gatherTimer->Stop(); } mIOPtrs.tpcCompressedClusters = Compressor.mOutputFlat; + if (ProcessingSettings().tpcCompressionGatherMode == 3) { + SynchronizeEvents(&mEvents->stream[outputStream]); + ReleaseEvent(&mEvents->stream[outputStream]); + mRec->ReturnVolatileDeviceMemory(); + } + if (mPipelineFinalizationCtx == nullptr) { SynchronizeStream(outputStream); } else { @@ -2195,34 +2355,24 @@ int GPUChainTracking::RunTRDTracking() mRec->PushNonPersistentMemory(); SetupGPUProcessor(&Tracker, true); - std::vector<GPUTRDTrackGPU> tracksTPC; - std::vector<int> tracksTPCLab; + + for (unsigned int iTracklet = 0; iTracklet < mIOPtrs.nTRDTracklets; ++iTracklet) { + if (Tracker.LoadTracklet(mIOPtrs.trdTracklets[iTracklet], mIOPtrs.trdTrackletsMC ? mIOPtrs.trdTrackletsMC[iTracklet].mLabel : nullptr)) { + return 1; + } + } for (unsigned int i = 0; i < mIOPtrs.nMergedTracks; i++) { const GPUTPCGMMergedTrack& trk = mIOPtrs.mergedTracks[i]; - if (!trk.OK()) { + if (!Tracker.PreCheckTrackTRDCandidate(trk)) { continue; } - if (trk.Looper()) { + const GPUTRDTrackGPU& trktrd = param().rec.NWaysOuter ? (GPUTRDTrackGPU)trk.OuterParam() : (GPUTRDTrackGPU)trk; + if (!Tracker.CheckTrackTRDCandidate(trktrd)) { continue; } - if (param().rec.NWaysOuter) { - tracksTPC.emplace_back(trk.OuterParam()); - } else { - tracksTPC.emplace_back(trk); - } - tracksTPC.back().SetTPCtrackId(i); - tracksTPCLab.push_back(-1); - } - - for (unsigned int iTracklet = 0; iTracklet < mIOPtrs.nTRDTracklets; ++iTracklet) { - if (Tracker.LoadTracklet(mIOPtrs.trdTracklets[iTracklet], mIOPtrs.trdTrackletsMC ? mIOPtrs.trdTrackletsMC[iTracklet].mLabel : nullptr)) { - return 1; - } - } - for (unsigned int iTrack = 0; iTrack < tracksTPC.size(); ++iTrack) { - if (Tracker.LoadTrack(tracksTPC[iTrack], tracksTPCLab[iTrack])) { + if (Tracker.LoadTrack(trktrd, -1, nullptr, -1, i, false)) { return 1; } } @@ -2254,26 +2404,51 @@ int GPUChainTracking::DoTRDGPUTracking() TransferMemoryResourcesToHost(RecoStep::TRDTracking, &Tracker, 0); SynchronizeStream(0); - if (GetDeviceProcessingSettings().debugLevel >= 2) { + if (GetProcessingSettings().debugLevel >= 2) { GPUInfo("GPU TRD tracker Finished"); } #endif return (0); } +int GPUChainTracking::RunRefit() +{ +#ifdef HAVE_O2HEADERS + bool doGPU = GetRecoStepsGPU() & RecoStep::Refit; + GPUTrackingRefitProcessor& Refit = processors()->trackingRefit; + GPUTrackingRefitProcessor& RefitShadow = doGPU ? processorsShadow()->trackingRefit : Refit; + + const auto& threadContext = GetThreadContext(); + SetupGPUProcessor(&Refit, false); + RefitShadow.SetPtrsFromGPUConstantMem(processorsShadow(), doGPU ? &processorsDevice()->param : nullptr); + RefitShadow.SetPropagator(doGPU ? processorsShadow()->calibObjects.o2Propagator : GetO2Propagator()); + RefitShadow.mPTracks = (doGPU ? processorsShadow() : processors())->tpcMerger.OutputTracks(); + WriteToConstantMemory(RecoStep::Refit, (char*)&processors()->trackingRefit - (char*)processors(), &RefitShadow, sizeof(RefitShadow), 0); + //TransferMemoryResourcesToGPU(RecoStep::Refit, &Refit, 0); + if (param().rec.trackingRefitGPUModel) { + runKernel<GPUTrackingRefitKernel, GPUTrackingRefitKernel::mode0asGPU>(GetGrid(mIOPtrs.nMergedTracks, 0), krnlRunRangeNone); + } else { + runKernel<GPUTrackingRefitKernel, GPUTrackingRefitKernel::mode1asTrackParCov>(GetGrid(mIOPtrs.nMergedTracks, 0), krnlRunRangeNone); + } + //TransferMemoryResourcesToHost(RecoStep::Refit, &Refit, 0); + SynchronizeStream(0); +#endif + return 0; +} + int GPUChainTracking::RunChain() { const auto threadContext = GetThreadContext(); - if (GetDeviceProcessingSettings().runCompressionStatistics && mCompressionStatistics == nullptr) { + if (GetProcessingSettings().runCompressionStatistics && mCompressionStatistics == nullptr) { mCompressionStatistics.reset(new GPUTPCClusterStatistics); } - const bool needQA = GPUQA::QAAvailable() && (GetDeviceProcessingSettings().runQA || (GetDeviceProcessingSettings().eventDisplay && mIOPtrs.nMCInfosTPC)); + const bool needQA = GPUQA::QAAvailable() && (GetProcessingSettings().runQA || (GetProcessingSettings().eventDisplay && mIOPtrs.nMCInfosTPC)); if (needQA && mQA->IsInitialized() == false) { - if (mQA->InitQA()) { + if (mQA->InitQA(GetProcessingSettings().runQA ? -GetProcessingSettings().runQA : -1)) { return 1; } } - if (GetDeviceProcessingSettings().debugLevel >= 6) { + if (GetProcessingSettings().debugLevel >= 6) { *mDebugFile << "\n\nProcessing event " << mRec->getNEventsProcessed() << std::endl; } if (mRec->slavesExist() && mRec->IsGPU()) { @@ -2326,10 +2501,10 @@ int GPUChainTracking::RunChain() mRec->PopNonPersistentMemory(RecoStep::TPCSliceTracking); // Release 1st stack level, TPC slice data not needed after merger if (mIOPtrs.clustersNative) { - if (GetDeviceProcessingSettings().doublePipeline) { + if (GetProcessingSettings().doublePipeline) { GPUChainTracking* foreignChain = (GPUChainTracking*)GetNextChainInQueue(); if (foreignChain && foreignChain->mIOPtrs.tpcZS) { - if (GetDeviceProcessingSettings().debugLevel >= 3) { + if (GetProcessingSettings().debugLevel >= 3) { GPUInfo("Preempting tpcZS input of foreign chain"); } mPipelineFinalizationCtx.reset(new GPUChainTrackingFinalContext); @@ -2346,7 +2521,11 @@ int GPUChainTracking::RunChain() return 1; } - if (!GetDeviceProcessingSettings().doublePipeline) { // Synchronize with output copies running asynchronously + if (runRecoStep(RecoStep::Refit, &GPUChainTracking::RunRefit)) { + return 1; + } + + if (!GetProcessingSettings().doublePipeline) { // Synchronize with output copies running asynchronously SynchronizeStream(mRec->NStreams() - 2); } @@ -2354,26 +2533,34 @@ int GPUChainTracking::RunChain() return 1; } - return GetDeviceProcessingSettings().doublePipeline ? 0 : RunChainFinalize(); + return GetProcessingSettings().doublePipeline ? 0 : RunChainFinalize(); } int GPUChainTracking::RunChainFinalize() { #ifdef HAVE_O2HEADERS - if (mIOPtrs.clustersNative && (GetRecoSteps() & RecoStep::TPCCompression) && GetDeviceProcessingSettings().runCompressionStatistics) { + if (mIOPtrs.clustersNative && (GetRecoSteps() & RecoStep::TPCCompression) && GetProcessingSettings().runCompressionStatistics) { CompressedClusters c = *mIOPtrs.tpcCompressedClusters; mCompressionStatistics->RunStatistics(mIOPtrs.clustersNative, &c, param()); } #endif - const bool needQA = GPUQA::QAAvailable() && (GetDeviceProcessingSettings().runQA || (GetDeviceProcessingSettings().eventDisplay && mIOPtrs.nMCInfosTPC)); + const bool needQA = GPUQA::QAAvailable() && (GetProcessingSettings().runQA || (GetProcessingSettings().eventDisplay && mIOPtrs.nMCInfosTPC)); if (needQA) { mRec->getGeneralStepTimer(GeneralStep::QA).Start(); - mQA->RunQA(!GetDeviceProcessingSettings().runQA); + mQA->RunQA(!GetProcessingSettings().runQA); mRec->getGeneralStepTimer(GeneralStep::QA).Stop(); } - if (GetDeviceProcessingSettings().eventDisplay) { + if (GetProcessingSettings().showOutputStat) { + PrintOutputStat(); + } + + PrintDebugOutput(); + + //PrintMemoryRelations(); + + if (GetProcessingSettings().eventDisplay) { if (!mDisplayRunning) { if (mEventDisplay->StartDisplay()) { return (1); @@ -2383,7 +2570,7 @@ int GPUChainTracking::RunChainFinalize() mEventDisplay->ShowNextEvent(); } - if (GetDeviceProcessingSettings().eventDisplay->EnableSendKey()) { + if (GetProcessingSettings().eventDisplay->EnableSendKey()) { while (kbhit()) { getch(); } @@ -2393,35 +2580,32 @@ int GPUChainTracking::RunChainFinalize() int iKey; do { Sleep(10); - if (GetDeviceProcessingSettings().eventDisplay->EnableSendKey()) { + if (GetProcessingSettings().eventDisplay->EnableSendKey()) { iKey = kbhit() ? getch() : 0; if (iKey == 'q') { - GetDeviceProcessingSettings().eventDisplay->mDisplayControl = 2; + GetProcessingSettings().eventDisplay->mDisplayControl = 2; } else if (iKey == 'n') { break; } else if (iKey) { - while (GetDeviceProcessingSettings().eventDisplay->mSendKey != 0) { + while (GetProcessingSettings().eventDisplay->mSendKey != 0) { Sleep(1); } - GetDeviceProcessingSettings().eventDisplay->mSendKey = iKey; + GetProcessingSettings().eventDisplay->mSendKey = iKey; } } - } while (GetDeviceProcessingSettings().eventDisplay->mDisplayControl == 0); - if (GetDeviceProcessingSettings().eventDisplay->mDisplayControl == 2) { + } while (GetProcessingSettings().eventDisplay->mDisplayControl == 0); + if (GetProcessingSettings().eventDisplay->mDisplayControl == 2) { mDisplayRunning = false; - GetDeviceProcessingSettings().eventDisplay->DisplayExit(); - DeviceProcessingSettings().eventDisplay = nullptr; + GetProcessingSettings().eventDisplay->DisplayExit(); + ProcessingSettings().eventDisplay = nullptr; return (2); } - GetDeviceProcessingSettings().eventDisplay->mDisplayControl = 0; + GetProcessingSettings().eventDisplay->mDisplayControl = 0; GPUInfo("Loading next event"); mEventDisplay->WaitForNextEvent(); } - PrintDebugOutput(); - - //PrintMemoryRelations(); return 0; } @@ -2443,15 +2627,11 @@ int GPUChainTracking::HelperReadEvent(int iSlice, int threadId, GPUReconstructio int GPUChainTracking::HelperOutput(int iSlice, int threadId, GPUReconstructionHelpers::helperParam* par) { if (param().rec.GlobalTracking) { - int tmpSlice = GPUTPCGlobalTracking::GlobalTrackingSliceOrder(iSlice); - int sliceLeft = (tmpSlice + (NSLICES / 2 - 1)) % (NSLICES / 2); - int sliceRight = (tmpSlice + 1) % (NSLICES / 2); - if (tmpSlice >= (int)NSLICES / 2) { - sliceLeft += NSLICES / 2; - sliceRight += NSLICES / 2; - } + unsigned int tmpSlice = GPUTPCGlobalTracking::GlobalTrackingSliceOrder(iSlice); + unsigned int sliceLeft, sliceRight; + GPUTPCGlobalTracking::GlobalTrackingSliceLeftRight(tmpSlice, sliceLeft, sliceRight); - while (mSliceSelectorReady < tmpSlice || mSliceSelectorReady < sliceLeft || mSliceSelectorReady < sliceRight) { + while (mSliceSelectorReady < (int)tmpSlice || mSliceSelectorReady < (int)sliceLeft || mSliceSelectorReady < (int)sliceRight) { if (par->reset) { return 1; } @@ -2469,13 +2649,13 @@ int GPUChainTracking::HelperOutput(int iSlice, int threadId, GPUReconstructionHe return 0; } -int GPUChainTracking::CheckErrorCodes() +int GPUChainTracking::CheckErrorCodes(bool cpuOnly) { int retVal = 0; - for (int i = 0; i < 1 + mRec->IsGPU(); i++) { + for (int i = 0; i < 1 + (!cpuOnly && mRec->IsGPU()); i++) { if (i) { const auto& threadContext = GetThreadContext(); - if (GetDeviceProcessingSettings().doublePipeline) { + if (GetProcessingSettings().doublePipeline) { TransferMemoryResourceLinkToHost(RecoStep::NoRecoStep, mInputsHost->mResourceErrorCodes, 0); SynchronizeStream(0); } else { @@ -2500,3 +2680,10 @@ void GPUChainTracking::ClearErrorCodes() } TransferMemoryResourceLinkToGPU(RecoStep::NoRecoStep, mInputsHost->mResourceErrorCodes, 0); } + +void GPUChainTracking::SetDefaultO2PropagatorForGPU() +{ + o2::base::Propagator* prop = param().GetDefaultO2Propagator(true); + prop->setMatLUT(processors()->calibObjects.matLUT); + SetO2Propagator(prop); +} diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 5780a331fd6a8..a757e3598934a 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -25,7 +25,7 @@ namespace o2 { namespace trd { -class TRDGeometryFlat; +class GeometryFlat; } } // namespace o2 @@ -59,7 +59,7 @@ class GPUTRDGeometry; class TPCFastTransform; class TPCdEdxCalibrationSplines; class GPUTrackingInputProvider; -class GPUChainTrackingFinalContext; +struct GPUChainTrackingFinalContext; struct GPUTPCCFChainContext; class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelegateBase @@ -75,7 +75,7 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega int Finalize() override; int RunChain() override; void MemorySize(size_t& gpuMem, size_t& pageLockedHostMem) override; - int CheckErrorCodes() override; + int CheckErrorCodes(bool cpuOnly = false) override; bool SupportsDoublePipeline() override { return true; } int FinalizePipelinedProcessing() override; void ClearErrorCodes(); @@ -89,13 +89,16 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega InOutMemory(InOutMemory&&); InOutMemory& operator=(InOutMemory&&); - std::unique_ptr<char[]> tpcZSpages; + std::unique_ptr<unsigned long long int[]> tpcZSpages; + std::unique_ptr<char[]> tpcZSpagesChar; // Same as above, but as char (needed for reading dumps, but deprecated, since alignment can be wrong) std::unique_ptr<GPUTrackingInOutZS> tpcZSmeta; std::unique_ptr<GPUTrackingInOutZS::GPUTrackingInOutZSMeta> tpcZSmeta2; std::unique_ptr<o2::tpc::Digit[]> tpcDigits[NSLICES]; + std::unique_ptr<GPUTrackingInOutDigits> digitMap; std::unique_ptr<GPUTPCClusterData[]> clusterData[NSLICES]; std::unique_ptr<AliHLTTPCRawCluster[]> rawClusters[NSLICES]; std::unique_ptr<o2::tpc::ClusterNative[]> clustersNative; + std::unique_ptr<o2::tpc::ClusterNativeAccess> clusterNativeAccess; std::unique_ptr<GPUTPCTrack[]> sliceTracks[NSLICES]; std::unique_ptr<GPUTPCHitId[]> sliceClusters[NSLICES]; std::unique_ptr<AliHLTTPCClusterMCLabel[]> mcLabelsTPC; @@ -145,27 +148,34 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega int DoTRDGPUTracking(); int RunTPCCompression(); int RunTPCDecompression(); + int RunRefit(); // Getters / setters for parameters const TPCFastTransform* GetTPCTransform() const { return processors()->calibObjects.fastTransform; } + const TPCPadGainCalib* GetTPCPadGainCalib() const { return processors()->calibObjects.tpcPadGain; } const TPCdEdxCalibrationSplines* GetdEdxSplines() const { return processors()->calibObjects.dEdxSplines; } const o2::base::MatLayerCylSet* GetMatLUT() const { return processors()->calibObjects.matLUT; } const GPUTRDGeometry* GetTRDGeometry() const { return (GPUTRDGeometry*)processors()->calibObjects.trdGeometry; } + const o2::base::Propagator* GetO2Propagator() const { return processors()->calibObjects.o2Propagator; } void SetTPCFastTransform(std::unique_ptr<TPCFastTransform>&& tpcFastTransform); void SetdEdxSplines(std::unique_ptr<TPCdEdxCalibrationSplines>&& dEdxSplines); void SetMatLUT(std::unique_ptr<o2::base::MatLayerCylSet>&& lut); - void SetTRDGeometry(std::unique_ptr<o2::trd::TRDGeometryFlat>&& geo); + void SetTRDGeometry(std::unique_ptr<o2::trd::GeometryFlat>&& geo); void SetTPCFastTransform(const TPCFastTransform* tpcFastTransform) { processors()->calibObjects.fastTransform = tpcFastTransform; } + void SetTPCPadGainCalib(const TPCPadGainCalib* tpcPadGainCalib) { processors()->calibObjects.tpcPadGain = tpcPadGainCalib; } void SetdEdxSplines(const TPCdEdxCalibrationSplines* dEdxSplines) { processors()->calibObjects.dEdxSplines = dEdxSplines; } void SetMatLUT(const o2::base::MatLayerCylSet* lut) { processors()->calibObjects.matLUT = lut; } - void SetTRDGeometry(const o2::trd::TRDGeometryFlat* geo) { processors()->calibObjects.trdGeometry = geo; } + void SetTRDGeometry(const o2::trd::GeometryFlat* geo) { processors()->calibObjects.trdGeometry = geo; } + void SetO2Propagator(const o2::base::Propagator* prop) { processors()->calibObjects.o2Propagator = prop; } + void SetDefaultO2PropagatorForGPU(); void LoadClusterErrors(); void SetOutputControlCompressedClusters(GPUOutputControl* v) { mOutputCompressedClusters = v; } void SetOutputControlClustersNative(GPUOutputControl* v) { mOutputClustersNative = v; } void SetOutputControlTPCTracks(GPUOutputControl* v) { mOutputTPCTracks = v; } + void SetOutputControlClusterLabels(GPUOutputControl* v) { mOutputClusterLabels = v; } - const void* mConfigDisplay = nullptr; // Abstract pointer to Standalone Display Configuration Structure - const void* mConfigQA = nullptr; // Abstract pointer to Standalone QA Configuration Structure + const GPUSettingsDisplay* mConfigDisplay = nullptr; // Abstract pointer to Standalone Display Configuration Structure + const GPUSettingsQA* mConfigQA = nullptr; // Abstract pointer to Standalone QA Configuration Structure protected: struct GPUTrackingFlatObjects : public GPUProcessor { @@ -205,6 +215,7 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega void PrintMemoryStatistics() override; void PrepareDebugOutput(); void PrintDebugOutput(); + void PrintOutputStat(); bool ValidateSteps(); bool ValidateSettings(); @@ -223,21 +234,17 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega // Ptr to detector / calibration objects std::unique_ptr<TPCFastTransform> mTPCFastTransformU; // Global TPC fast transformation object + std::unique_ptr<TPCPadGainCalib> mTPCPadGainCalibU; // TPC gain calibration and cluster finder parameters std::unique_ptr<TPCdEdxCalibrationSplines> mdEdxSplinesU; // TPC dEdx calibration splines std::unique_ptr<o2::base::MatLayerCylSet> mMatLUTU; // Material Lookup Table - std::unique_ptr<o2::trd::TRDGeometryFlat> mTRDGeometryU; // TRD Geometry + std::unique_ptr<o2::trd::GeometryFlat> mTRDGeometryU; // TRD Geometry - // Ptr to internal reconstruction data objects - std::unique_ptr<o2::tpc::ClusterNativeAccess> mClusterNativeAccess; // Internal memory for clusterNativeAccess - std::unique_ptr<GPUTrackingInOutDigits> mDigitMap; // Internal memory for digit-map, if needed - std::unique_ptr<unsigned long long int[]> mTPCZSBuffer; // Memory to store TPC ZS pages - std::unique_ptr<unsigned int[]> mTPCZSSizes; // Array with TPC ZS numbers of pages - std::unique_ptr<void*[]> mTPCZSPtrs; // Array with pointers to TPC ZS pages - std::unique_ptr<GPUTrackingInOutZS> mTPCZS; // TPC ZS Data Structure + std::unique_ptr<o2::tpc::ClusterNativeAccess> mClusterNativeAccess; GPUOutputControl* mOutputCompressedClusters = nullptr; GPUOutputControl* mOutputClustersNative = nullptr; GPUOutputControl* mOutputTPCTracks = nullptr; + GPUOutputControl* mOutputClusterLabels = nullptr; std::unique_ptr<GPUTPCCFChainContext> mCFContext; @@ -259,10 +266,12 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega int RunChainFinalize(); int RunTPCTrackingSlices_internal(); int RunTPCClusterizer_prepare(bool restorePointers); +#ifdef GPUCA_TPC_GEOMETRY_O2 std::pair<unsigned int, unsigned int> RunTPCClusterizer_transferZS(int iSlice, const CfFragment& fragment, int lane); void RunTPCClusterizer_compactPeaks(GPUTPCClusterFinder& clusterer, GPUTPCClusterFinder& clustererShadow, int stage, bool doGPU, int lane); std::pair<unsigned int, unsigned int> TPCClusterizerDecodeZSCount(unsigned int iSlice, const CfFragment& fragment); std::pair<unsigned int, unsigned int> TPCClusterizerDecodeZSCountUpdate(unsigned int iSlice, const CfFragment& fragment); +#endif void RunTPCTrackingMerger_MergeBorderTracks(char withinSlice, char mergeMode, GPUReconstruction::krnlDeviceType deviceType); void RunTPCTrackingMerger_Resolve(char useOrigTrackParam, char mergeAll, GPUReconstruction::krnlDeviceType deviceType); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index d15ae64a01904..9cd4e25155ee7 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -135,8 +135,10 @@ void GPUChainTracking::PrintMemoryStatistics() { std::map<std::string, GPUChainTrackingMemUsage> usageMap; for (int i = 0; i < NSLICES; i++) { +#ifdef GPUCA_TPC_GEOMETRY_O2 addToMap("TPC Clusterer Sector Peaks", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nPeaks, processors()->tpcClusterer[i].mNMaxPeaks); addToMap("TPC Clusterer Sector Clusters", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nClusters, processors()->tpcClusterer[i].mNMaxClusters); +#endif addToMap("TPC Start Hits", usageMap, *processors()->tpcTrackers[i].NStartHits(), processors()->tpcTrackers[i].NMaxStartHits()); addToMap("TPC Tracklets", usageMap, *processors()->tpcTrackers[i].NTracklets(), processors()->tpcTrackers[i].NMaxTracklets()); addToMap("TPC TrackletHits", usageMap, *processors()->tpcTrackers[i].NRowHits(), processors()->tpcTrackers[i].NMaxRowHits()); @@ -186,3 +188,35 @@ void GPUChainTracking::PrintDebugOutput() processors()->debugOutput.Print(); #endif } + +void GPUChainTracking::PrintOutputStat() +{ + int nTracks = 0, nAttachedClusters = 0, nAttachedClustersFitted = 0, nAdjacentClusters = 0; + for (unsigned int k = 0; k < mIOPtrs.nMergedTracks; k++) { + if (mIOPtrs.mergedTracks[k].OK()) { + nTracks++; + nAttachedClusters += mIOPtrs.mergedTracks[k].NClusters(); + nAttachedClustersFitted += mIOPtrs.mergedTracks[k].NClustersFitted(); + } + } + unsigned int nCls = GetProcessingSettings().doublePipeline ? mIOPtrs.clustersNative->nClustersTotal : GetTPCMerger().NMaxClusters(); + for (unsigned int k = 0; k < nCls; k++) { + int attach = mIOPtrs.mergedTrackHitAttachment[k]; + if (attach & gputpcgmmergertypes::attachFlagMask) { + nAdjacentClusters++; + } + } + + char trdText[1024] = ""; + if (GetRecoSteps() & GPUDataTypes::RecoStep::TRDTracking) { + int nTRDTracks = 0; + int nTRDTracklets = 0; + for (unsigned int k = 0; k < mIOPtrs.nTRDTracks; k++) { + auto& trk = mIOPtrs.trdTracks[k]; + nTRDTracklets += trk.GetNtracklets(); + nTRDTracks += trk.GetNtracklets() != 0; + } + snprintf(trdText, 1024, " - TRD Tracker reconstructed %d tracks (%d tracklets)", nTRDTracks, nTRDTracklets); + } + printf("Output Tracks: %d (%d / %d / %d / %d clusters (fitted / attached / adjacent / total))%s\n", nTracks, nAttachedClustersFitted, nAttachedClusters, nAdjacentClusters, nCls, trdText); +} diff --git a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx index d98debac29e6e..e688512a832a7 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx @@ -65,7 +65,7 @@ static constexpr char DUMP_HEADER[DUMP_HEADER_SIZE + 1] = "CAv1"; GPUChainTracking::InOutMemory::InOutMemory() = default; GPUChainTracking::InOutMemory::~InOutMemory() = default; GPUChainTracking::InOutMemory::InOutMemory(GPUChainTracking::InOutMemory&&) = default; -GPUChainTracking::InOutMemory& GPUChainTracking::InOutMemory::operator=(GPUChainTracking::InOutMemory&&) = default; +GPUChainTracking::InOutMemory& GPUChainTracking::InOutMemory::operator=(GPUChainTracking::InOutMemory&&) = default; // NOLINT: False positive in clang-tidy void GPUChainTracking::DumpData(const char* filename) { @@ -150,19 +150,20 @@ int GPUChainTracking::ReadData(const char* filename) ReadData(fp, mIOPtrs.rawClusters, mIOPtrs.nRawClusters, mIOMem.rawClusters, InOutPointerType::RAW_CLUSTERS, ptrRawClusters); int nClustersTotal = 0; #ifdef HAVE_O2HEADERS - if (ReadData<ClusterNative>(fp, &mClusterNativeAccess->clustersLinear, &mClusterNativeAccess->nClustersTotal, &mIOMem.clustersNative, InOutPointerType::CLUSTERS_NATIVE)) { - r = fread(&mClusterNativeAccess->nClusters[0][0], sizeof(mClusterNativeAccess->nClusters[0][0]), NSLICES * GPUCA_ROW_COUNT, fp); - mClusterNativeAccess->setOffsetPtrs(); - mIOPtrs.clustersNative = mClusterNativeAccess.get(); + mIOMem.clusterNativeAccess.reset(new ClusterNativeAccess); + if (ReadData<ClusterNative>(fp, &mIOMem.clusterNativeAccess->clustersLinear, &mIOMem.clusterNativeAccess->nClustersTotal, &mIOMem.clustersNative, InOutPointerType::CLUSTERS_NATIVE)) { + r = fread(&mIOMem.clusterNativeAccess->nClusters[0][0], sizeof(mIOMem.clusterNativeAccess->nClusters[0][0]), NSLICES * GPUCA_ROW_COUNT, fp); + mIOMem.clusterNativeAccess->setOffsetPtrs(); + mIOPtrs.clustersNative = mIOMem.clusterNativeAccess.get(); } - mDigitMap.reset(new GPUTrackingInOutDigits); - if (ReadData(fp, mDigitMap->tpcDigits, mDigitMap->nTPCDigits, mIOMem.tpcDigits, InOutPointerType::TPC_DIGIT)) { - mIOPtrs.tpcPackedDigits = mDigitMap.get(); + mIOMem.digitMap.reset(new GPUTrackingInOutDigits); + if (ReadData(fp, mIOMem.digitMap->tpcDigits, mIOMem.digitMap->nTPCDigits, mIOMem.tpcDigits, InOutPointerType::TPC_DIGIT)) { + mIOPtrs.tpcPackedDigits = mIOMem.digitMap.get(); } const char* ptr; size_t total; char* ptrZSPages; - if (ReadData(fp, &ptr, &total, &mIOMem.tpcZSpages, InOutPointerType::TPC_ZS, &ptrZSPages)) { + if (ReadData(fp, &ptr, &total, &mIOMem.tpcZSpagesChar, InOutPointerType::TPC_ZS, &ptrZSPages)) { GPUTrackingInOutZS::GPUTrackingInOutZSCounts counts; r = fread(&counts, sizeof(counts), 1, fp); mIOMem.tpcZSmeta.reset(new GPUTrackingInOutZS); @@ -223,7 +224,11 @@ void GPUChainTracking::DumpSettings(const char* dir) f += "tpctransform.dump"; DumpFlatObjectToFile(processors()->calibObjects.fastTransform, f.c_str()); } - + if (processors()->calibObjects.tpcPadGain != nullptr) { + f = dir; + f += "tpcpadgaincalib.dump"; + DumpStructToFile(processors()->calibObjects.tpcPadGain, f.c_str()); + } #ifdef HAVE_O2HEADERS if (processors()->calibObjects.dEdxSplines != nullptr) { f = dir; @@ -251,6 +256,10 @@ void GPUChainTracking::ReadSettings(const char* dir) f += "tpctransform.dump"; mTPCFastTransformU = ReadFlatObjectFromFile<TPCFastTransform>(f.c_str()); processors()->calibObjects.fastTransform = mTPCFastTransformU.get(); + f = dir; + f += "tpcpadgaincalib.dump"; + mTPCPadGainCalibU = ReadStructFromFile<TPCPadGainCalib>(f.c_str()); + processors()->calibObjects.tpcPadGain = mTPCPadGainCalibU.get(); #ifdef HAVE_O2HEADERS f = dir; f += "dedxsplines.dump"; @@ -262,7 +271,7 @@ void GPUChainTracking::ReadSettings(const char* dir) processors()->calibObjects.matLUT = mMatLUTU.get(); f = dir; f += "trdgeometry.dump"; - mTRDGeometryU = ReadStructFromFile<o2::trd::TRDGeometryFlat>(f.c_str()); + mTRDGeometryU = ReadStructFromFile<o2::trd::GeometryFlat>(f.c_str()); processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); #endif } diff --git a/GPU/GPUTracking/Global/GPUTrackingInputProvider.h b/GPU/GPUTracking/Global/GPUTrackingInputProvider.h index 247250c08ff8d..621f2271823cd 100644 --- a/GPU/GPUTracking/Global/GPUTrackingInputProvider.h +++ b/GPU/GPUTracking/Global/GPUTrackingInputProvider.h @@ -31,7 +31,7 @@ namespace GPUCA_NAMESPACE namespace gpu { -class GPUTrackingInOutZS; +struct GPUTrackingInOutZS; class GPUTrackingInputProvider : public GPUProcessor { diff --git a/GPU/GPUTracking/ITS/GPUITSFitter.h b/GPU/GPUTracking/ITS/GPUITSFitter.h index 9c2cd61c4edd0..fc27cc985808a 100644 --- a/GPU/GPUTracking/ITS/GPUITSFitter.h +++ b/GPU/GPUTracking/ITS/GPUITSFitter.h @@ -17,20 +17,15 @@ #include "GPUProcessor.h" #include "GPUITSTrack.h" -namespace o2 -{ -namespace its +namespace o2::its { class Road; struct TrackingFrameInfo; struct Cluster; class Cell; -} // namespace its -} // namespace o2 +} // namespace o2::its -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUITSTrack; @@ -61,6 +56,8 @@ class GPUITSFitter : public GPUProcessor { return mMemory->mNumberOfTracks; } + GPUd() void SetNumberOfLayers(int i) { mNumberOfLayers = i; } + GPUd() int NumberOfLayers() { return mNumberOfLayers; } GPUd() void SetNumberTF(int i, int v) { mNTF[i] = v; } GPUd() o2::its::TrackingFrameInfo** trackingFrame() { @@ -82,22 +79,22 @@ class GPUITSFitter : public GPUProcessor }; protected: + int mNumberOfLayers; int mNumberOfRoads = 0; int mNMaxTracks = 0; - int mNTF[7] = {}; + int* mNTF = nullptr; Memory* mMemory = nullptr; o2::its::Road* mRoads = nullptr; - o2::its::TrackingFrameInfo* mTF[7] = {}; + o2::its::TrackingFrameInfo** mTF = {nullptr}; GPUITSTrack* mTracks = nullptr; - const o2::its::Cluster* mClusterPtrs[7]; - const o2::its::Cell* mCellPtrs[5]; + const o2::its::Cluster** mClusterPtrs; + const o2::its::Cell** mCellPtrs; short mMemoryResInput = -1; short mMemoryResTracks = -1; short mMemoryResMemory = -1; }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/ITS/GPUITSFitterKernels.cxx b/GPU/GPUTracking/ITS/GPUITSFitterKernels.cxx index 9aa51d047b13f..67360fbb49ed9 100644 --- a/GPU/GPUTracking/ITS/GPUITSFitterKernels.cxx +++ b/GPU/GPUTracking/ITS/GPUITSFitterKernels.cxx @@ -78,7 +78,7 @@ GPUdii() void GPUITSFitterKernel::Thread<0>(int nBlocks, int nThreads, int iBloc int lastCellLevel = o2::its::constants::its::UnusedIndex; CA_DEBUGGER(int nClusters = 2); - for (int iCell{0}; iCell < o2::its::constants::its::CellsPerRoad; ++iCell) { + for (int iCell{0}; iCell < Fitter.NumberOfLayers() - 2; ++iCell) { const int cellIndex = road[iCell]; if (cellIndex == o2::its::constants::its::UnusedIndex) { continue; @@ -121,16 +121,16 @@ GPUdii() void GPUITSFitterKernel::Thread<0>(int nBlocks, int nThreads, int iBloc const float y3 = cluster3.positionTrackingFrame[0]; const float z3 = cluster3.positionTrackingFrame[1]; - const float crv = math_utils::computeCurvature(x1, y1, x2, y2, x3, y3); - const float x0 = math_utils::computeCurvatureCentreX(x1, y1, x2, y2, x3, y3); - const float tgl12 = math_utils::computeTanDipAngle(x1, y1, x2, y2, z1, z2); - const float tgl23 = math_utils::computeTanDipAngle(x2, y2, x3, y3, z2, z3); + const float crv = o2::its::math_utils::computeCurvature(x1, y1, x2, y2, x3, y3); + const float x0 = o2::its::math_utils::computeCurvatureCentreX(x1, y1, x2, y2, x3, y3); + const float tgl12 = o2::its::math_utils::computeTanDipAngle(x1, y1, x2, y2, z1, z2); + const float tgl23 = o2::its::math_utils::computeTanDipAngle(x2, y2, x3, y3, z2, z3); const float r2 = CAMath::Sqrt(cluster2.xCoordinate * cluster2.xCoordinate + cluster2.yCoordinate * cluster2.yCoordinate); const float r3 = CAMath::Sqrt(cluster3.xCoordinate * cluster3.xCoordinate + cluster3.yCoordinate * cluster3.yCoordinate); const float fy = 1. / (r2 - r3); const float& tz = fy; - const float cy = (math_utils::computeCurvature(x1, y1, x2, y2 + o2::its::constants::its::Resolution, x3, y3) - crv) / (o2::its::constants::its::Resolution * bz * constants::math::B2C) * 20.f; // FIXME: MS contribution to the cov[14] (*20 added) + const float cy = (o2::its::math_utils::computeCurvature(x1, y1, x2, y2 + o2::its::constants::its::Resolution, x3, y3) - crv) / (o2::its::constants::its::Resolution * bz * constants::math::B2C) * 20.f; // FIXME: MS contribution to the cov[14] (*20 added) constexpr float s2 = o2::its::constants::its::Resolution * o2::its::constants::its::Resolution; temporaryTrack.X() = cluster3.xTrackingFrame; @@ -164,13 +164,13 @@ GPUdii() void GPUITSFitterKernel::Thread<0>(int nBlocks, int nThreads, int iBloc for (size_t iC = 0; iC < 7; ++iC) { temporaryTrack.mClusters[iC] = clusters[iC]; } - bool fitSuccess = fitTrack(Fitter, prop, temporaryTrack, o2::its::constants::its::LayersNumber - 4, -1, -1); + bool fitSuccess = fitTrack(Fitter, prop, temporaryTrack, Fitter.NumberOfLayers() - 4, -1, -1); if (!fitSuccess) { continue; } CA_DEBUGGER(fitCounters[nClusters - 4]++); temporaryTrack.ResetCovariance(); - fitSuccess = fitTrack(Fitter, prop, temporaryTrack, 0, o2::its::constants::its::LayersNumber, 1); + fitSuccess = fitTrack(Fitter, prop, temporaryTrack, 0, Fitter.NumberOfLayers(), 1); if (!fitSuccess) { continue; } @@ -184,7 +184,7 @@ GPUdii() void GPUITSFitterKernel::Thread<0>(int nBlocks, int nThreads, int iBloc temporaryTrack.mOuterParam.X = temporaryTrack.X(); temporaryTrack.mOuterParam.alpha = prop.GetAlpha(); temporaryTrack.ResetCovariance(); - fitSuccess = fitTrack(Fitter, prop, temporaryTrack, o2::its::constants::its::LayersNumber - 1, -1, -1); + fitSuccess = fitTrack(Fitter, prop, temporaryTrack, Fitter.NumberOfLayers() - 1, -1, -1); if (!fitSuccess) { continue; } diff --git a/GPU/GPUTracking/ITS/GPUITSFitterKernels.h b/GPU/GPUTracking/ITS/GPUITSFitterKernels.h index 9e3528174502b..59ff83030f546 100644 --- a/GPU/GPUTracking/ITS/GPUITSFitterKernels.h +++ b/GPU/GPUTracking/ITS/GPUITSFitterKernels.h @@ -15,17 +15,12 @@ #define GPUITSFITTERKERNELS_H #include "GPUGeneralKernels.h" -namespace o2 -{ -namespace its +namespace o2::its { struct TrackingFrameInfo; -} -} // namespace o2 +} // namespace o2::its -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCGMPropagator; class GPUITSFitter; @@ -41,7 +36,6 @@ class GPUITSFitterKernel : public GPUKernelTemplate protected: GPUd() static bool fitTrack(GPUITSFitter& Fitter, GPUTPCGMPropagator& prop, GPUITSTrack& track, int start, int end, int step); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/ITS/GPUITSTrack.h b/GPU/GPUTracking/ITS/GPUITSTrack.h index 7b5183d2be20c..3ced243587ba5 100644 --- a/GPU/GPUTracking/ITS/GPUITSTrack.h +++ b/GPU/GPUTracking/ITS/GPUITSTrack.h @@ -16,9 +16,7 @@ #include "GPUTPCGMTrackParam.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUITSTrack : public GPUTPCGMTrackParam { @@ -27,7 +25,6 @@ class GPUITSTrack : public GPUTPCGMTrackParam float mAlpha; int mClusters[7]; }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.cxx b/GPU/GPUTracking/Interface/GPUO2Interface.cxx index 879d8b83e1442..67005037a4b44 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.cxx +++ b/GPU/GPUTracking/Interface/GPUO2Interface.cxx @@ -18,6 +18,7 @@ #include "GPUOutputControl.h" #include "GPUO2InterfaceConfiguration.h" #include "GPUParam.inc" +#include "GPUQA.h" #include <iostream> #include <fstream> #ifdef WITH_OPENMP @@ -39,7 +40,7 @@ int GPUTPCO2Interface::Initialize(const GPUO2InterfaceConfiguration& config) } mConfig.reset(new GPUO2InterfaceConfiguration(config)); mContinuous = mConfig->configEvent.continuousMaxTimeBin != 0; - mRec.reset(GPUReconstruction::CreateInstance(mConfig->configProcessing)); + mRec.reset(GPUReconstruction::CreateInstance(mConfig->configDeviceBackend)); if (mRec == nullptr) { GPUError("Error obtaining instance of GPUReconstruction"); return 1; @@ -50,11 +51,13 @@ int GPUTPCO2Interface::Initialize(const GPUO2InterfaceConfiguration& config) if (mConfig->configWorkflow.inputs.isSet(GPUDataTypes::InOutType::TPCRaw)) { mConfig->configEvent.needsClusterer = 1; } - mRec->SetSettings(&mConfig->configEvent, &mConfig->configReconstruction, &mConfig->configDeviceProcessing, &mConfig->configWorkflow); + mRec->SetSettings(&mConfig->configEvent, &mConfig->configReconstruction, &mConfig->configProcessing, &mConfig->configWorkflow); mChain->SetTPCFastTransform(mConfig->configCalib.fastTransform); + mChain->SetTPCPadGainCalib(mConfig->configCalib.tpcPadGain); mChain->SetdEdxSplines(mConfig->configCalib.dEdxSplines); mChain->SetMatLUT(mConfig->configCalib.matLUT); mChain->SetTRDGeometry(mConfig->configCalib.trdGeometry); + mChain->SetO2Propagator(mConfig->configCalib.o2Propagator); if (mConfig->configInterface.outputToExternalBuffers) { mOutputCompressedClusters.reset(new GPUOutputControl); mChain->SetOutputControlCompressedClusters(mOutputCompressedClusters.get()); @@ -62,14 +65,22 @@ int GPUTPCO2Interface::Initialize(const GPUO2InterfaceConfiguration& config) mChain->SetOutputControlClustersNative(mOutputClustersNative.get()); mOutputTPCTracks.reset(new GPUOutputControl); mChain->SetOutputControlTPCTracks(mOutputTPCTracks.get()); + GPUOutputControl dummy; + dummy.set([](size_t size) -> void* {throw std::runtime_error("invalid output memory request, no common output buffer set"); return nullptr; }); + mRec->SetOutputControl(dummy); + } + if (mConfig->configProcessing.runMC) { + mOutputTPCClusterLabels.reset(new GPUOutputControl); + mChain->SetOutputControlClusterLabels(mOutputTPCClusterLabels.get()); } if (mRec->Init()) { return (1); } - if (!mRec->IsGPU() && mConfig->configDeviceProcessing.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (!mRec->IsGPU() && mConfig->configProcessing.memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { mRec->MemoryScalers()->factor *= 2; } + mRec->MemoryScalers()->factor *= mConfig->configInterface.memoryBufferScaleFactor; mInitialized = true; return (0); } @@ -88,8 +99,8 @@ int GPUTPCO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceO if (!mInitialized) { return (1); } - static int nEvent = 0; if (mConfig->configInterface.dumpEvents) { + static int nEvent = 0; mChain->ClearIOPointers(); mChain->mIOPtrs.clustersNative = data->clustersNative; mChain->mIOPtrs.tpcPackedDigits = data->tpcPackedDigits; @@ -101,6 +112,7 @@ int GPUTPCO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceO if (nEvent == 0) { mRec->DumpSettings(); } + nEvent++; if (mConfig->configInterface.dumpEvents >= 2) { return 0; } @@ -130,6 +142,13 @@ int GPUTPCO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceO mOutputTPCTracks->reset(); } } + if (mConfig->configProcessing.runMC) { + if (outputs->clusterLabels.allocator) { + mOutputTPCClusterLabels->set(outputs->clusterLabels.allocator); + } else { + mOutputTPCClusterLabels->reset(); + } + } int retVal = mRec->RunChains(); if (retVal == 2) { retVal = 0; // 2 signals end of event display, ignore @@ -143,9 +162,13 @@ int GPUTPCO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceO outputs->clustersNative.size = mOutputClustersNative->EndOfSpace ? 0 : (mChain->mIOPtrs.clustersNative->nClustersTotal * sizeof(*mChain->mIOPtrs.clustersNative->clustersLinear)); outputs->tpcTracks.size = mOutputCompressedClusters->EndOfSpace ? 0 : (size_t)((char*)mOutputCompressedClusters->OutputPtr - (char*)mOutputCompressedClusters->OutputBase); } + if (mConfig->configQA.shipToQC) { + outputs->qa.hist1 = &mChain->GetQA()->getHistograms1D(); + outputs->qa.hist2 = &mChain->GetQA()->getHistograms2D(); + outputs->qa.hist3 = &mChain->GetQA()->getHistograms1Dd(); + } *data = mChain->mIOPtrs; - nEvent++; return 0; } diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.h b/GPU/GPUTracking/Interface/GPUO2Interface.h index af22dfd7ced58..6fdf163d9b84c 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.h +++ b/GPU/GPUTracking/Interface/GPUO2Interface.h @@ -75,6 +75,7 @@ class GPUTPCO2Interface std::unique_ptr<GPUOutputControl> mOutputCompressedClusters; std::unique_ptr<GPUOutputControl> mOutputClustersNative; std::unique_ptr<GPUOutputControl> mOutputTPCTracks; + std::unique_ptr<GPUOutputControl> mOutputTPCClusterLabels; }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx new file mode 100644 index 0000000000000..55f754a88b4e5 --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx @@ -0,0 +1,115 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUO2InterfaceConfigurableParam.cxx +/// \author David Rohr + +#include "GPUO2InterfaceConfigurableParam.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUDataTypes.h" + +using namespace o2::gpu; +#define BeginNamespace(name) +#define EndNamespace() +#define AddOption(name, type, default, optname, optnameshort, help, ...) +#define AddOptionRTC(...) AddOption(__VA_ARGS__) +#define AddVariable(name, type, default) +#define AddVariableRTC(...) AddVariable(__VA_ARGS__) +#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) +#define AddOptionVec(name, type, optname, optnameshort, help, ...) +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) +#define AddSubConfig(name, instance) +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) O2ParamImpl(GPUCA_M_CAT(GPUConfigurableParam, name)) +#define BeginHiddenConfig(...) +#define EndConfig() +#define AddCustomCPP(...) +#define AddHelp(...) +#define AddShortcut(...) +#include "GPUSettingsList.h" +#undef BeginNamespace +#undef EndNamespace +#undef AddOption +#undef AddOptionRTC +#undef AddVariable +#undef AddVariableRTC +#undef AddOptionSet +#undef AddOptionVec +#undef AddOptionArray +#undef AddSubConfig +#undef BeginSubConfig +#undef BeginHiddenConfig +#undef EndConfig +#undef AddCustomCPP +#undef AddHelp +#undef AddShortcut + +GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam() +{ +#define BeginNamespace(name) +#define EndNamespace() +#define AddOption(name, type, default, optname, optnameshort, help, ...) dst.name = src.name; +#define AddOptionRTC(...) AddOption(__VA_ARGS__) +#define AddVariable(name, type, default) +#define AddVariableRTC(...) AddVariable(__VA_ARGS__) +#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) +#define AddOptionVec(name, type, optname, optnameshort, help, ...) +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) \ + for (int i = 0; i < count; i++) { \ + dst.name[i] = src.name[i]; \ + } +#define AddSubConfig(name, instance) +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ + name instance; \ + { \ + auto& src = GPUCA_M_CAT(GPUConfigurableParam, name)::Instance(); \ + name& dst = instance; +#define BeginHiddenConfig(name, instance) { +#define EndConfig() } +#define AddCustomCPP(...) +#define AddHelp(...) +#define AddShortcut(...) +#include "GPUSettingsList.h" +#undef BeginNamespace +#undef EndNamespace +#undef AddOption +#undef AddOptionRTC +#undef AddVariable +#undef AddVariableRTC +#undef AddOptionSet +#undef AddOptionVec +#undef AddOptionArray +#undef AddSubConfig +#undef BeginSubConfig +#undef BeginHiddenConfig +#undef EndConfig +#undef AddCustomCPP +#undef AddHelp +#undef AddShortcut + + configProcessing = proc; + configReconstruction = rec; + configDisplay = GL; + configQA = QA; + if (global.continuousMaxTimeBin) { + configEvent.continuousMaxTimeBin = global.continuousMaxTimeBin; + } + if (global.solenoidBz > -1e6f) { + configEvent.solenoidBz = global.solenoidBz; + } + if (global.constBz) { + configEvent.constBz = global.constBz; + } + if (configReconstruction.TrackReferenceX == 1000.f) { + configReconstruction.TrackReferenceX = 83.f; + } + configDeviceBackend.deviceType = GPUDataTypes::GetDeviceType(global.deviceType.c_str()); + configDeviceBackend.forceDeviceType = global.forceDeviceType; + return global; +} diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h new file mode 100644 index 0000000000000..0f55a60f17874 --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h @@ -0,0 +1,82 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUO2InterfaceConfigurableParam.h +/// \author David Rohr + +// This file auto-generates a ConfigurableParam object from the GPU parameter macros. +// Set via: +// --configKeyValues "GPU_global.[x]=[y]" : for global GPU run configurations, like solenoidBz, gpuType, configuration object files. +// --configKeyValues "GPU_rec.[x]=[y]" : for GPU reconstruction related settings used on the GPU, like pt threshold for track rejection. +// --configKeyValues "GPU_proc.[x]=[y]" : for processing options steering GPU reconstruction like GPU device ID, debug output level, number of CPU threads. +// Check GPUSettingsList.h for all options + +#ifndef GPUO2INTERFACECONFIGURABLEPARAM_H +#define GPUO2INTERFACECONFIGURABLEPARAM_H + +// Some defines denoting that we are compiling for O2 +#ifndef HAVE_O2HEADERS +#define HAVE_O2HEADERS +#endif +#ifndef GPUCA_TPC_GEOMETRY_O2 +#define GPUCA_TPC_GEOMETRY_O2 +#endif +#ifndef GPUCA_O2_INTERFACE +#define GPUCA_O2_INTERFACE +#endif + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "GPUSettings.h" +#include "GPUDefMacros.h" +#include <vector> + +#define BeginNamespace(name) \ + namespace name \ + { +#define EndNamespace() } +#define AddOption(name, type, default, optname, optnameshort, help, ...) type name = default; +#define AddOptionRTC(...) AddOption(__VA_ARGS__) +#define AddVariable(name, type, default) +#define AddVariableRTC(...) AddVariable(__VA_ARGS__) +#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) +#define AddOptionVec(name, type, optname, optnameshort, help, ...) +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {default}; +#define AddSubConfig(name, instance) +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ + struct GPUCA_M_CAT(GPUConfigurableParam, name) : public o2::conf::ConfigurableParamHelper<GPUCA_M_CAT(GPUConfigurableParam, name)> { \ + O2ParamDef(GPUCA_M_CAT(GPUConfigurableParam, name), GPUCA_M_STR(GPUCA_M_CAT(GPU_, instance))) public: +#define BeginHiddenConfig(name, instance) struct GPUCA_M_CAT(GPUConfigurableParam, name) { +#define EndConfig() \ + } \ + ; +#define AddCustomCPP(...) __VA_ARGS__ +#define AddHelp(...) +#define AddShortcut(...) +#define AddOptionRTC(...) AddOption(__VA_ARGS__) +#include "GPUSettingsList.h" +#undef BeginNamespace +#undef EndNamespace +#undef AddOption +#undef AddOptionRTC +#undef AddVariable +#undef AddVariableRTC +#undef AddOptionSet +#undef AddOptionVec +#undef AddOptionArray +#undef AddSubConfig +#undef BeginSubConfig +#undef BeginHiddenConfig +#undef EndConfig +#undef AddCustomCPP +#undef AddHelp +#undef AddShortcut + +#endif diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h index f4646c2c1b9b3..b3ca1cabe5ab0 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h @@ -30,12 +30,14 @@ #include <functional> #include <gsl/gsl> #include "GPUSettings.h" -#include "GPUDisplayConfig.h" -#include "GPUQAConfig.h" #include "GPUDataTypes.h" #include "GPUHostDataTypes.h" #include "DataFormatsTPC/Constants.h" +class TH1F; +class TH1D; +class TH2F; + namespace o2 { namespace tpc @@ -46,6 +48,7 @@ class Digit; namespace gpu { class TPCFastTransform; +struct GPUSettingsO2; // This defines an output region. Ptr points to a memory buffer, which should have a proper alignment. // Since DPL does not respect the alignment of data types, we do not impose anything specic but just use a char data type, but it should be >= 64 bytes ideally. @@ -58,10 +61,18 @@ struct GPUInterfaceOutputRegion { std::function<void*(size_t)> allocator = nullptr; }; +struct GPUInterfaceQAOutputs { + const std::vector<TH1F>* hist1; + const std::vector<TH2F>* hist2; + const std::vector<TH1D>* hist3; +}; + struct GPUInterfaceOutputs { GPUInterfaceOutputRegion compressedClusters; GPUInterfaceOutputRegion clustersNative; GPUInterfaceOutputRegion tpcTracks; + GPUInterfaceOutputRegion clusterLabels; + GPUInterfaceQAOutputs qa; }; // Full configuration structure with all available settings of GPU... @@ -74,21 +85,26 @@ struct GPUO2InterfaceConfiguration { struct GPUInterfaceSettings { int dumpEvents = 0; bool outputToExternalBuffers = false; - // These constants affect GPU memory allocation and do not limit the CPU processing + bool dropSecondaryLegs = true; + float memoryBufferScaleFactor = 1.f; + // These constants affect GPU memory allocation only and do not limit the CPU processing + unsigned long maxTPCZS = 8192ul * 1024 * 1024; unsigned int maxTPCHits = 1024 * 1024 * 1024; unsigned int maxTRDTracklets = 128 * 1024; unsigned int maxITSTracks = 96 * 1024; }; + GPUSettingsDeviceBackend configDeviceBackend; GPUSettingsProcessing configProcessing; - GPUSettingsDeviceProcessing configDeviceProcessing; GPUSettingsEvent configEvent; GPUSettingsRec configReconstruction; - GPUDisplayConfig configDisplay; - GPUQAConfig configQA; + GPUSettingsDisplay configDisplay; + GPUSettingsQA configQA; GPUInterfaceSettings configInterface; GPURecoStepConfiguration configWorkflow; GPUCalibObjects configCalib; + + GPUSettingsO2 ReadConfigurableParam(); }; // Structure with pointers to actual data for input and output @@ -103,14 +119,14 @@ struct GPUO2InterfaceConfiguration { struct GPUO2InterfaceIOPtrs { // Input: TPC clusters in cluster native format, or digits, or list of ZS pages - const as it can only be input const o2::tpc::ClusterNativeAccess* clusters = nullptr; - const std::array<gsl::span<const o2::tpc::Digit>, o2::tpc::Constants::MAXSECTOR>* o2Digits = nullptr; - std::array<std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>>, o2::tpc::Constants::MAXSECTOR>* o2DigitsMC = nullptr; + const std::array<gsl::span<const o2::tpc::Digit>, o2::tpc::constants::MAXSECTOR>* o2Digits = nullptr; + std::array<const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>*, o2::tpc::constants::MAXSECTOR>* o2DigitsMC = nullptr; const o2::gpu::GPUTrackingInOutZS* tpcZS = nullptr; // Input / Output for Merged TPC tracks, two ptrs, for the tracks themselves, and for the MC labels. std::vector<o2::tpc::TrackTPC>* outputTracks = nullptr; std::vector<uint32_t>* outputClusRefs = nullptr; - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* outputTracksMCTruth = nullptr; + std::vector<o2::MCCompLabel>* outputTracksMCTruth = nullptr; // Output for entropy-reduced clusters of TPC compression const o2::tpc::CompressedClustersFlat* compressedClusters = nullptr; diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx new file mode 100644 index 0000000000000..7e354ea2c2d1c --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx @@ -0,0 +1,63 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUO2InterfaceQA.cxx +/// \author David Rohr + +#include "GPUQA.h" +#include "GPUO2InterfaceQA.h" + +using namespace o2::gpu; +using namespace o2::tpc; + +GPUO2InterfaceQA::GPUO2InterfaceQA(const GPUSettingsQA* config) : mQA(new GPUQA(nullptr, config)) +{ +} + +GPUO2InterfaceQA::~GPUO2InterfaceQA() = default; + +int GPUO2InterfaceQA::initializeForProcessing(int tasks) +{ + return mQA->InitQA(tasks); +} + +void GPUO2InterfaceQA::runQA(const std::vector<o2::tpc::TrackTPC>* tracksExternal, const std::vector<o2::MCCompLabel>* tracksExtMC, const o2::tpc::ClusterNativeAccess* clNative) +{ + mQA->RunQA(false, tracksExternal, tracksExtMC, clNative); +} +int GPUO2InterfaceQA::postprocess(TObjArray& out) +{ + return mQA->DrawQAHistograms(&out); +} + +int GPUO2InterfaceQA::postprocessExternal(std::vector<TH1F>& in1, std::vector<TH2F>& in2, std::vector<TH1D>& in3, TObjArray& out, int tasks) +{ + if (mQA->loadHistograms(in1, in2, in3, tasks)) { + return 1; + } + return mQA->DrawQAHistograms(&out); +} + +void GPUO2InterfaceQA::cleanup() +{ + mQA->DrawQAHistogramsCleanup(); +} + +void GPUO2InterfaceQA::getHists(const std::vector<TH1F>*& h1, const std::vector<TH2F>*& h2, const std::vector<TH1D>*& h3) +{ + h1 = &mQA->getHistograms1D(); + h2 = &mQA->getHistograms2D(); + h3 = &mQA->getHistograms1Dd(); +} + +void GPUO2InterfaceQA::resetHists() +{ + mQA->resetHists(); +} diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h new file mode 100644 index 0000000000000..63be5a92141d2 --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.h @@ -0,0 +1,73 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUO2InterfaceQA.h +/// \author David Rohr + +#ifndef GPUO2INTERFACEQA_H +#define GPUO2INTERFACEQA_H + +// Some defines denoting that we are compiling for O2 +#ifndef HAVE_O2HEADERS +#define HAVE_O2HEADERS +#endif +#ifndef GPUCA_TPC_GEOMETRY_O2 +#define GPUCA_TPC_GEOMETRY_O2 +#endif +#ifndef GPUCA_O2_INTERFACE +#define GPUCA_O2_INTERFACE +#endif + +#include <memory> +#include <vector> + +class TH1F; +class TH1D; +class TH2F; +class TObjArray; + +namespace o2 +{ +class MCCompLabel; +namespace tpc +{ +class TrackTPC; +struct ClusterNativeAccess; +} // namespace tpc +} // namespace o2 + +namespace o2::gpu +{ +class GPUQA; +class GPUSettingsQA; +class GPUO2InterfaceQA +{ + public: + GPUO2InterfaceQA(const GPUSettingsQA* config = nullptr); + ~GPUO2InterfaceQA(); + + int initializeForProcessing(int tasks); // only needed for processing, not for postprocessing + + void runQA(const std::vector<o2::tpc::TrackTPC>* tracksExternal, const std::vector<o2::MCCompLabel>* tracksExtMC, const o2::tpc::ClusterNativeAccess* clNative); + int postprocess(TObjArray& out); + + // Input might be modified, so we assume non-const. If it is const, a copy should be created before. + int postprocessExternal(std::vector<TH1F>& in1, std::vector<TH2F>& in2, std::vector<TH1D>& in3, TObjArray& out, int tasks); + + void getHists(const std::vector<TH1F>*& h1, const std::vector<TH2F>*& h2, const std::vector<TH1D>*& h3); + void resetHists(); + void cleanup(); + + private: + std::unique_ptr<GPUQA> mQA; +}; +} // namespace o2::gpu + +#endif diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx new file mode 100644 index 0000000000000..f3b7e871cdbf0 --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx @@ -0,0 +1,63 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUO2InterfaceRefit.cxx +/// \author David Rohr + +#include "GPUO2InterfaceRefit.h" +#include "DataFormatsTPC/ClusterNative.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "GPUParam.h" +#include "GPUTPCGMMergedTrackHit.h" + +using namespace o2::gpu; +using namespace o2::tpc; + +void GPUTPCO2InterfaceRefit::fillSharedClustersMap(const ClusterNativeAccess* cl, const gsl::span<const TrackTPC> trks, const TPCClRefElem* trackRef, unsigned char* shmap) +{ + if (!cl || !shmap) { + throw std::runtime_error("Must provide clusters access and preallocated recepient for shared map"); + } + memset(shmap, 0, sizeof(char) * cl->nClustersTotal); + for (unsigned int i = 0; i < trks.size(); i++) { + for (unsigned int j = 0; j < trks[i].getNClusterReferences(); j++) { + size_t idx = &trks[i].getCluster(trackRef, j, *cl) - cl->clustersLinear; + shmap[idx] = shmap[idx] ? 2 : 1; + } + } + for (unsigned int i = 0; i < cl->nClustersTotal; i++) { + shmap[i] = (shmap[i] > 1 ? GPUTPCGMMergedTrackHit::flagShared : 0) | cl->clustersLinear[i].getFlags(); + } +} + +GPUTPCO2InterfaceRefit::GPUTPCO2InterfaceRefit(const ClusterNativeAccess* cl, const TPCFastTransform* trans, float bz, const TPCClRefElem* trackRef, const unsigned char* sharedmap, const std::vector<TrackTPC>* trks, o2::base::Propagator* p) : mRefit(), mParam(new GPUParam) +{ + if (sharedmap == nullptr && trks == nullptr) { + throw std::runtime_error("Must provide either shared cluster map or vector of tpc tracks to build the map"); + } + if (sharedmap == nullptr) { + mSharedMap.resize(cl->nClustersTotal); + sharedmap = mSharedMap.data(); + fillSharedClustersMap(cl, *trks, trackRef, mSharedMap.data()); + } + + mParam->SetDefaults(bz); + mRefit.SetGPUParam(mParam.get()); + mRefit.SetClusterStateArray(sharedmap); + mRefit.SetPropagator(p); + mRefit.SetClusterNative(cl); + mRefit.SetTrackHitReferences(trackRef); + mRefit.SetFastTransform(trans); +} + +void GPUTPCO2InterfaceRefit::setGPUTrackFitInProjections(bool v) { mParam->rec.fitInProjections = v; } +void GPUTPCO2InterfaceRefit::setTrackReferenceX(float v) { mParam->rec.TrackReferenceX = v; } + +GPUTPCO2InterfaceRefit::~GPUTPCO2InterfaceRefit() = default; diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h new file mode 100644 index 0000000000000..5e7e5327549d4 --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h @@ -0,0 +1,69 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUO2InterfaceRefit.h +/// \author David Rohr + +#ifndef GPUO2INTERFACEREFIT_H +#define GPUO2INTERFACEREFIT_H + +// Some defines denoting that we are compiling for O2 +#ifndef HAVE_O2HEADERS +#define HAVE_O2HEADERS +#endif +#ifndef GPUCA_TPC_GEOMETRY_O2 +#define GPUCA_TPC_GEOMETRY_O2 +#endif +#ifndef GPUCA_O2_INTERFACE +#define GPUCA_O2_INTERFACE +#endif + +#include "GPUTrackingRefit.h" +#include <memory> +#include <vector> +#include <gsl/span> + +namespace o2::tpc +{ +using TPCClRefElem = uint32_t; +} + +namespace o2::gpu +{ +class GPUParam; +class GPUTPCO2InterfaceRefit +{ + public: + // Must initialize with: + // - In any case: Cluster Native access structure (cl), TPC Fast Transformation instance (trans), solenoid field (bz), TPC Track hit references (trackRef) + // - Either the shared cluster map (sharedmap) or the vector of tpc tracks (trks) to build the shared cluster map internally + // - o2::base::Propagator (p) in case RefitTrackAsTrackParCov is to be used + + GPUTPCO2InterfaceRefit(const o2::tpc::ClusterNativeAccess* cl, const TPCFastTransform* trans, float bz, const o2::tpc::TPCClRefElem* trackRef, const unsigned char* sharedmap = nullptr, const std::vector<o2::tpc::TrackTPC>* trks = nullptr, o2::base::Propagator* p = nullptr); + ~GPUTPCO2InterfaceRefit(); + + int RefitTrackAsGPU(o2::tpc::TrackTPC& trk, bool outward = false, bool resetCov = false) { return mRefit.RefitTrackAsGPU(trk, outward, resetCov); } + int RefitTrackAsTrackParCov(o2::tpc::TrackTPC& trk, bool outward = false, bool resetCov = false) { return mRefit.RefitTrackAsTrackParCov(trk, outward, resetCov); } + int RefitTrackAsGPU(o2::track::TrackParCov& trk, const o2::tpc::TrackTPCClusRef& clusRef, float time0, float* chi2 = nullptr, bool outward = false, bool resetCov = false) { return mRefit.RefitTrackAsGPU(trk, clusRef, time0, chi2, outward, resetCov); } + int RefitTrackAsTrackParCov(o2::track::TrackParCov& trk, const o2::tpc::TrackTPCClusRef& clusRef, float time0, float* chi2 = nullptr, bool outward = false, bool resetCov = false) { return mRefit.RefitTrackAsTrackParCov(trk, clusRef, time0, chi2, outward, resetCov); } + void setGPUTrackFitInProjections(bool v = true); + void setTrackReferenceX(float v); + void setIgnoreErrorsAtTrackEnds(bool v) { mRefit.mIgnoreErrorsOnTrackEnds = v; } + + static void fillSharedClustersMap(const o2::tpc::ClusterNativeAccess* cl, const gsl::span<const o2::tpc::TrackTPC> trks, const o2::tpc::TPCClRefElem* trackRef, unsigned char* shmap); + + private: + GPUTrackingRefit mRefit; + std::unique_ptr<GPUParam> mParam; + std::vector<unsigned char> mSharedMap; +}; +} // namespace o2::gpu + +#endif diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergedTrack.h b/GPU/GPUTracking/Merger/GPUTPCGMMergedTrack.h index 6f72a23f8b13c..1a4479fc74d38 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergedTrack.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergedTrack.h @@ -50,6 +50,7 @@ class GPUTPCGMMergedTrack GPUd() bool Looper() const { return mFlags & 0x02; } GPUd() bool CSide() const { return mFlags & 0x04; } GPUd() bool CCE() const { return mFlags & 0x08; } + GPUd() bool MergedLooper() const { return mFlags & 0x10; } GPUd() void SetNClusters(int v) { mNClusters = v; } GPUd() void SetNClustersFitted(int v) { mNClustersFitted = v; } @@ -91,6 +92,14 @@ class GPUTPCGMMergedTrack mFlags &= 0xF7; } } + GPUd() void SetMergedLooper(bool v) + { + if (v) { + mFlags |= 0x10; + } else { + mFlags &= 0xEF; + } + } GPUd() void SetFlags(unsigned char v) { mFlags = v; } GPUd() void SetLegs(unsigned char v) { mLegs = v; } GPUd() unsigned char Legs() const { return mLegs; } diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergedTrackHit.h b/GPU/GPUTracking/Merger/GPUTPCGMMergedTrackHit.h index ebfef2c40f508..50cc8ac2e9412 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergedTrackHit.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergedTrackHit.h @@ -23,6 +23,10 @@ namespace gpu struct GPUTPCGMMergedTrackHit { unsigned int num; unsigned char slice, row, leg, state; +#ifdef GPUCA_ALIROOT_LIB + float x, y, z; + unsigned short amp; +#endif // NOTE: the lower states must match those from ClusterNative! enum hitState { flagSplitPad = 0x1, diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 10177cf926e5c..6bb95ab55d02d 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -12,18 +12,19 @@ /// \author Sergey Gorbunov, David Rohr #define GPUCA_CADEBUG 0 +#define GPUCA_MERGE_LOOPER_MC 0 -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cstdio> #include <cstring> #include <cmath> +#include "GPUReconstruction.h" #endif #include "GPUTPCTracker.h" #include "GPUTPCClusterData.h" #include "GPUTPCTrackParam.h" #include "GPUTPCGMMerger.h" -#include "GPUReconstruction.h" #include "GPUO2DataTypes.h" #include "TPCFastTransform.h" #include "GPUTPCConvertImpl.h" @@ -41,13 +42,18 @@ #include "GPUTPCGMSliceTrack.h" #include "GPUTPCGMBorderTrack.h" -#ifdef GPUCA_CADEBUG_ENABLED +#if !defined(GPUCA_GPUCODE) && (defined(GPUCA_MERGER_BY_MC_LABEL) || defined(GPUCA_CADEBUG_ENABLED) || GPUCA_MERGE_LOOPER_MC) #include "AliHLTTPCClusterMCData.h" +#ifdef HAVE_O2HEADERS +#include "DataFormatsTPC/ClusterNative.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#endif #endif using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; -using namespace GPUTPCGMMergerTypes; +using namespace gputpcgmmergertypes; static constexpr int kMaxParts = 400; static constexpr int kMaxClusters = GPUCA_MERGER_MAX_TRACK_CLUSTERS; @@ -85,7 +91,9 @@ GPUTPCGMMerger::GPUTPCGMMerger() } // DEBUG CODE -#if defined(GPUCA_MERGER_BY_MC_LABEL) || defined(GPUCA_CADEBUG_ENABLED) +#if !defined(GPUCA_GPUCODE) && (defined(GPUCA_MERGER_BY_MC_LABEL) || defined(GPUCA_CADEBUG_ENABLED) || GPUCA_MERGE_LOOPER_MC) +#include "GPUQAHelper.h" + void GPUTPCGMMerger::CheckMergedTracks() { std::vector<bool> trkUsed(SliceTrackInfoLocalTotal()); @@ -135,47 +143,58 @@ void GPUTPCGMMerger::CheckMergedTracks() } } -int GPUTPCGMMerger::GetTrackLabel(const GPUTPCGMBorderTrack& trk) +template <class T> +inline const auto* resolveMCLabels(const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* a, const AliHLTTPCClusterMCLabel* b) { - GPUTPCGMSliceTrack* track = &mSliceTrackInfos[trk.TrackID()]; - int nClusters = track->OrigTrack()->NHits(); - std::vector<int> labels; + return a; +} +template <> +inline const auto* resolveMCLabels<AliHLTTPCClusterMCLabel>(const o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>* a, const AliHLTTPCClusterMCLabel* b) +{ + return b; +} + +template <class T, class S> +long int GPUTPCGMMerger::GetTrackLabelA(const S& trk) +{ + GPUTPCGMSliceTrack* sliceTrack = nullptr; + int nClusters = 0; + if constexpr (std::is_same<S, GPUTPCGMBorderTrack&>::value) { + sliceTrack = &mSliceTrackInfos[trk.TrackID()]; + nClusters = sliceTrack->OrigTrack()->NHits(); + } else { + nClusters = trk.NClusters(); + } + auto acc = GPUTPCTrkLbl<false, GPUTPCTrkLbl_ret>(resolveMCLabels<T>(GetConstantMem()->ioPtrs.clustersNative ? GetConstantMem()->ioPtrs.clustersNative->clustersMCTruth : nullptr, GetConstantMem()->ioPtrs.mcLabelsTPC), 0.5f); for (int i = 0; i < nClusters; i++) { int id; - if (Param().rec.mergerReadFromTrackerDirectly) { - const GPUTPCTracker& tracker = GetConstantMem()->tpcTrackers[track->Slice()]; - const GPUTPCHitId& ic = tracker.TrackHits()[track->OrigTrack()->FirstHitID() + i]; - id = tracker.Data().ClusterDataIndex(tracker.Data().Row(ic.RowIndex()), ic.HitIndex()) + GetConstantMem()->ioPtrs.clustersNative->clusterOffset[track->Slice()][0]; - } else { - id = track->OrigTrack()->OutTrackClusters()[i].GetId(); - } - for (int j = 0; j < 3; j++) { - int label = GetConstantMem()->ioPtrs.mcLabelsTPC[id].fClusterID[j].fMCID; - if (label >= 0) { - labels.push_back(label); + if constexpr (std::is_same<S, GPUTPCGMBorderTrack&>::value) { + if (Param().rec.mergerReadFromTrackerDirectly) { + const GPUTPCTracker& tracker = GetConstantMem()->tpcTrackers[sliceTrack->Slice()]; + const GPUTPCHitId& ic = tracker.TrackHits()[sliceTrack->OrigTrack()->FirstHitID() + i]; + id = tracker.Data().ClusterDataIndex(tracker.Data().Row(ic.RowIndex()), ic.HitIndex()) + GetConstantMem()->ioPtrs.clustersNative->clusterOffset[sliceTrack->Slice()][0]; + } else { + id = sliceTrack->OrigTrack()->OutTrackClusters()[i].GetId(); } - } - } - if (labels.size() == 0) { - return (-1); - } - labels.push_back(-1); - std::sort(labels.begin(), labels.end()); - int bestLabel = -1, bestLabelCount = 0; - int curLabel = labels[0], curCount = 1; - for (unsigned int i = 1; i < labels.size(); i++) { - if (labels[i] == curLabel) { - curCount++; } else { - if (curCount > bestLabelCount) { - bestLabelCount = curCount; - bestLabel = curLabel; - } - curLabel = labels[i]; - curCount = 1; + id = mClusters[trk.FirstClusterRef() + i].num; } + acc.addLabel(id); + } + return acc.computeLabel().id; +} + +template <class S> +long int GPUTPCGMMerger::GetTrackLabel(const S& trk) +{ +#ifdef GPUCA_TPC_GEOMETRY_O2 + if (GetConstantMem()->ioPtrs.clustersNative->clustersMCTruth) { + return GetTrackLabelA<o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>, S>(trk); + } else +#endif + { + return GetTrackLabelA<AliHLTTPCClusterMCLabel, S>(trk); } - return bestLabel; } #endif @@ -252,7 +271,7 @@ void* GPUTPCGMMerger::SetPointersRefitScratch(void* mem) { computePointerWithAlignment(mem, mRetryRefitIds, mNMaxTracks); computePointerWithAlignment(mem, mLoopData, mNMaxTracks); - if (mRec->GetDeviceProcessingSettings().fullMergerOnGPU) { + if (mRec->GetProcessingSettings().fullMergerOnGPU) { mem = SetPointersRefitScratch2(mem); } return mem; @@ -261,7 +280,7 @@ void* GPUTPCGMMerger::SetPointersRefitScratch(void* mem) void* GPUTPCGMMerger::SetPointersRefitScratch2(void* mem) { computePointerWithAlignment(mem, mTrackOrderAttach, mNMaxTracks); - if (mRec->GetDeviceProcessingSettings().mergerSortTracks) { + if (mRec->GetProcessingSettings().mergerSortTracks) { computePointerWithAlignment(mem, mTrackOrderProcess, mNMaxTracks); } return mem; @@ -271,13 +290,18 @@ void* GPUTPCGMMerger::SetPointersOutput(void* mem) { computePointerWithAlignment(mem, mOutputTracks, mNMaxTracks); computePointerWithAlignment(mem, mClusters, mNMaxOutputTrackClusters); - if (mRec->GetParam().earlyTpcTransform) { + if (mRec->GetParam().par.earlyTpcTransform) { computePointerWithAlignment(mem, mClustersXYZ, mNMaxOutputTrackClusters); } computePointerWithAlignment(mem, mClusterAttachment, mNMaxClusters); - if (!mRec->GetDeviceProcessingSettings().fullMergerOnGPU) { + if (!mRec->GetProcessingSettings().fullMergerOnGPU) { mem = SetPointersRefitScratch2(mem); } + if (mRec->GetRecoSteps() & GPUDataTypes::RecoStep::Refit) { + computePointerWithAlignment(mem, mClusterStateExt, mNMaxClusters); + } else { + mClusterStateExt = nullptr; + } return mem; } @@ -285,9 +309,9 @@ void* GPUTPCGMMerger::SetPointersOutput(void* mem) void GPUTPCGMMerger::RegisterMemoryAllocation() { AllocateAndInitializeLate(); - mRec->RegisterMemoryAllocation(this, &GPUTPCGMMerger::SetPointersMerger, (mRec->GetDeviceProcessingSettings().fullMergerOnGPU ? 0 : GPUMemoryResource::MEMORY_HOST) | GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCMerger"); + mRec->RegisterMemoryAllocation(this, &GPUTPCGMMerger::SetPointersMerger, (mRec->GetProcessingSettings().fullMergerOnGPU ? 0 : GPUMemoryResource::MEMORY_HOST) | GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCMerger"); mRec->RegisterMemoryAllocation(this, &GPUTPCGMMerger::SetPointersRefitScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCMergerRefitScratch"); - mMemoryResOutput = mRec->RegisterMemoryAllocation(this, &GPUTPCGMMerger::SetPointersOutput, (mRec->GetDeviceProcessingSettings().fullMergerOnGPU ? GPUMemoryResource::MEMORY_OUTPUT : GPUMemoryResource::MEMORY_INOUT) | GPUMemoryResource::MEMORY_CUSTOM, "TPCMergerOutput"); + mMemoryResOutput = mRec->RegisterMemoryAllocation(this, &GPUTPCGMMerger::SetPointersOutput, (mRec->GetProcessingSettings().fullMergerOnGPU ? GPUMemoryResource::MEMORY_OUTPUT : GPUMemoryResource::MEMORY_INOUT) | GPUMemoryResource::MEMORY_CUSTOM, "TPCMergerOutput"); mMemoryResMemory = mRec->RegisterMemoryAllocation(this, &GPUTPCGMMerger::SetPointersMemory, GPUMemoryResource::MEMORY_PERMANENT, "TPCMergerMemory"); } @@ -357,7 +381,7 @@ GPUd() int GPUTPCGMMerger::RefitSliceTrack(GPUTPCGMSliceTrack& sliceTrack, const trk.SinPhi() = inTrack->Param().GetSinPhi(); trk.DzDs() = inTrack->Param().GetDzDs(); trk.QPt() = inTrack->Param().GetQPt(); - trk.TZOffset() = Param().earlyTpcTransform ? inTrack->Param().GetZOffset() : GetConstantMem()->calibObjects.fastTransform->convZOffsetToVertexTime(slice, inTrack->Param().GetZOffset(), Param().continuousMaxTimeBin); + trk.TZOffset() = Param().par.earlyTpcTransform ? inTrack->Param().GetZOffset() : GetConstantMem()->calibObjects.fastTransform->convZOffsetToVertexTime(slice, inTrack->Param().GetZOffset(), Param().par.continuousMaxTimeBin); trk.ShiftZ(this, slice, sliceTrack.ClusterZT0(), sliceTrack.ClusterZTN()); for (int way = 0; way < 2; way++) { if (way) { @@ -379,7 +403,7 @@ GPUd() int GPUTPCGMMerger::RefitSliceTrack(GPUTPCGMSliceTrack& sliceTrack, const row = ic.RowIndex(); const ClusterNative& cl = GetConstantMem()->ioPtrs.clustersNative->clustersLinear[GetConstantMem()->ioPtrs.clustersNative->clusterOffset[slice][0] + clusterIndex]; flags = cl.getFlags(); - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { x = tracker.Data().ClusterData()[clusterIndex].x; y = tracker.Data().ClusterData()[clusterIndex].y; z = tracker.Data().ClusterData()[clusterIndex].z - trk.TZOffset(); @@ -390,7 +414,7 @@ GPUd() int GPUTPCGMMerger::RefitSliceTrack(GPUTPCGMSliceTrack& sliceTrack, const const GPUTPCSliceOutCluster& clo = inTrack->OutTrackCluster(i); row = clo.GetRow(); flags = clo.GetFlags(); - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { x = clo.GetX(); y = clo.GetY(); z = clo.GetZ() - trk.TZOffset(); @@ -426,14 +450,14 @@ GPUd() void GPUTPCGMMerger::SetTrackClusterZT(GPUTPCGMSliceTrack& track, int iSl const GPUTPCHitId& ic2 = trk.TrackHits()[sliceTr->FirstHitID() + sliceTr->NHits() - 1]; int clusterIndex1 = trk.Data().ClusterDataIndex(trk.Data().Row(ic1.RowIndex()), ic1.HitIndex()); int clusterIndex2 = trk.Data().ClusterDataIndex(trk.Data().Row(ic2.RowIndex()), ic2.HitIndex()); - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { track.SetClusterZT(trk.Data().ClusterData()[clusterIndex1].z, trk.Data().ClusterData()[clusterIndex2].z); } else { const ClusterNative* cl = GetConstantMem()->ioPtrs.clustersNative->clustersLinear + GetConstantMem()->ioPtrs.clustersNative->clusterOffset[iSlice][0]; track.SetClusterZT(cl[clusterIndex1].getTime(), cl[clusterIndex2].getTime()); } } else { - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { track.SetClusterZT(sliceTr->OutTrackClusters()->GetZ(), (sliceTr->OutTrackClusters() + sliceTr->NHits() - 1)->GetZ()); } else { const ClusterNative* cls = mConstantMem->ioPtrs.clustersNative->clustersLinear; @@ -481,7 +505,6 @@ GPUd() void GPUTPCGMMerger::UnpackSliceGlobal(int nBlocks, int nThreads, int iBl GPUd() void GPUTPCGMMerger::UnpackResetIds(int nBlocks, int nThreads, int iBlock, int iThread, int iSlice) { int* TrackIds = (int*)mTmpMem; - const GPUTPCTracker& trk = GetConstantMem()->tpcTrackers[iSlice]; unsigned int nLocalTracks = Param().rec.mergerReadFromTrackerDirectly ? trk.CommonMemory()->nLocalTracks : mkSlices[iSlice]->NLocalTracks(); for (unsigned int i = iBlock * nThreads + iThread; i < nLocalTracks; i += nBlocks * nThreads) { @@ -539,14 +562,23 @@ GPUd() void GPUTPCGMMerger::RefitSliceTracks(int nBlocks, int nThreads, int iBlo } } +GPUd() void GPUTPCGMMerger::LinkGlobalTracks(int nBlocks, int nThreads, int iBlock, int iThread) +{ + for (int itr = SliceTrackInfoGlobalFirst(0) + iBlock * nThreads + iThread; itr < SliceTrackInfoGlobalLast(NSLICES - 1); itr += nThreads * nBlocks) { + GPUTPCGMSliceTrack& globalTrack = mSliceTrackInfos[itr]; + GPUTPCGMSliceTrack& localTrack = mSliceTrackInfos[globalTrack.LocalTrackId()]; + localTrack.SetGlobalTrackId(localTrack.GlobalTrackId(0) != -1, itr); // Todo: broken in parallel + } +} + GPUd() void GPUTPCGMMerger::MakeBorderTracks(int nBlocks, int nThreads, int iBlock, int iThread, int iBorder, GPUTPCGMBorderTrack** B, GPUAtomic(unsigned int) * nB, bool useOrigTrackParam) { //* prepare slice tracks for merging with next/previous/same sector //* each track transported to the border line - float fieldBz = Param().ConstBz; + float fieldBz = Param().par.ConstBz; - float dAlpha = Param().DAlpha / 2; + float dAlpha = Param().par.DAlpha / 2; float x0 = 0; if (iBorder == 0) { // transport to the left edge of the sector and rotate horizontally @@ -683,7 +715,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int nBlocks, int nThreads, int } } -#if defined(__CUDACC__) || defined(__HIPCC__) // Specialize MergeBorderTracks<3> +#if (defined(__CUDACC__) || defined(__HIPCC__)) && !defined(GPUCA_GPUCODE_GENRTC) // Specialize MergeBorderTracks<3> struct MergeBorderTracks_compMax { GPUd() bool operator()(const GPUTPCGMBorderRange& a, const GPUTPCGMBorderRange& b) { @@ -700,7 +732,7 @@ struct MergeBorderTracks_compMin { template <> void GPUCA_KRNL_BACKEND_CLASS::runKernelBackendInternal<GPUTPCGMMergerMergeBorders, 3>(krnlSetup& _xyz, GPUTPCGMBorderRange* const& range, int const& N, int const& cmpMax) { - GPUDebugTiming timer(mDeviceProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); + GPUDebugTiming timer(mProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); thrust::device_ptr<GPUTPCGMBorderRange> p(range); ThrustVolatileAsyncAllocator alloc(this); if (cmpMax) { @@ -768,8 +800,10 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int nBlocks, int nThreads, int // do check GPUTPCGMBorderTrack& b2 = B2[r2.fId]; -#ifdef GPUCA_MERGER_BY_MC_LABEL - if (GetTrackLabel(b1) != GetTrackLabel(b2)) // DEBUG CODE, match by MC label +#if defined(GPUCA_MERGER_BY_MC_LABEL) && !defined(GPUCA_GPUCODE) + long int label1 = GetTrackLabel(b1); + long int label2 = GetTrackLabel(b2); + if (label1 != label2 && label1 != -1) // DEBUG CODE, match by MC label #endif { CADEBUG( @@ -879,7 +913,7 @@ GPUd() void GPUTPCGMMerger::MergeWithinSlicesPrepare(int nBlocks, int nThreads, int iSlice = track.Slice(); GPUTPCGMBorderTrack b; ; - if (track.TransportToX(this, x0, Param().ConstBz, b, maxSin)) { + if (track.TransportToX(this, x0, Param().par.ConstBz, b, maxSin)) { b.SetTrackID(itr); CADEBUG( printf("WITHIN SLICE %d Track %d - ", iSlice, itr); for (int i = 0; i < 5; i++) { printf("%8.3f ", b.Par()[i]); } printf(" - "); for (int i = 0; i < 5; i++) { printf("%8.3f ", b.Cov()[i]); } printf("\n")); @@ -1192,7 +1226,7 @@ GPUd() void GPUTPCGMMerger::ResolveMergeSlices(GPUResolveSharedMemory& smem, int } } -GPUd() void GPUTPCGMMerger::MergeCEFill(const GPUTPCGMSliceTrack* track, const GPUTPCGMMergedTrackHit& cls, const GPUTPCGMMergedTrackHitXYZ& clsXYZ, int itr) +GPUd() void GPUTPCGMMerger::MergeCEFill(const GPUTPCGMSliceTrack* track, const GPUTPCGMMergedTrackHit& cls, const GPUTPCGMMergedTrackHitXYZ* clsXYZ, int itr) { if (Param().rec.NonConsecutiveIDs) { return; @@ -1205,22 +1239,22 @@ GPUd() void GPUTPCGMMerger::MergeCEFill(const GPUTPCGMSliceTrack* track, const G #endif float z = 0; - if (Param().earlyTpcTransform) { - z = clsXYZ.z; + if (Param().par.earlyTpcTransform) { + z = clsXYZ->z; } else { float x, y; auto& cln = mConstantMem->ioPtrs.clustersNative->clustersLinear[cls.num]; GPUTPCConvertImpl::convert(*mConstantMem, cls.slice, cls.row, cln.getPad(), cln.getTime(), x, y, z); } - if (!Param().ContinuousTracking && CAMath::Abs(z) > 10) { + if (!Param().par.ContinuousTracking && CAMath::Abs(z) > 10) { return; } int slice = track->Slice(); for (int attempt = 0; attempt < 2; attempt++) { GPUTPCGMBorderTrack b; const float x0 = Param().tpcGeometry.Row2X(attempt == 0 ? 63 : cls.row); - if (track->TransportToX(this, x0, Param().ConstBz, b, GPUCA_MAX_SIN_PHI_LOW)) { + if (track->TransportToX(this, x0, Param().par.ConstBz, b, GPUCA_MAX_SIN_PHI_LOW)) { b.SetTrackID(itr); b.SetNClusters(mOutputTracks[itr].NClusters()); if (CAMath::Abs(b.Cov()[4]) >= 0.5) { @@ -1241,7 +1275,7 @@ GPUd() void GPUTPCGMMerger::MergeCEFill(const GPUTPCGMSliceTrack* track, const G GPUd() void GPUTPCGMMerger::MergeCE(int nBlocks, int nThreads, int iBlock, int iThread) { - const ClusterNative* cls = Param().earlyTpcTransform ? nullptr : mConstantMem->ioPtrs.clustersNative->clustersLinear; + const ClusterNative* cls = Param().par.earlyTpcTransform ? nullptr : mConstantMem->ioPtrs.clustersNative->clustersLinear; for (unsigned int i = iBlock * nThreads + iThread; i < mMemory->nOutputTracks; i += nThreads * nBlocks) { if (mOutputTracks[i].CSide() == 0 && mTrackLinks[i] >= 0) { if (mTrackLinks[mTrackLinks[i]] != (int)i) { @@ -1271,7 +1305,7 @@ GPUd() void GPUTPCGMMerger::MergeCE(int nBlocks, int nThreads, int iBlock, int i bool needswap = false; if (looper) { float z0max, z1max; - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { z0max = CAMath::Max(CAMath::Abs(mClustersXYZ[trk[0]->FirstClusterRef()].z), CAMath::Abs(mClustersXYZ[trk[0]->FirstClusterRef() + trk[0]->NClusters() - 1].z)); z1max = CAMath::Max(CAMath::Abs(mClustersXYZ[trk[1]->FirstClusterRef()].z), CAMath::Abs(mClustersXYZ[trk[1]->FirstClusterRef() + trk[1]->NClusters() - 1].z)); } else { @@ -1292,7 +1326,7 @@ GPUd() void GPUTPCGMMerger::MergeCE(int nBlocks, int nThreads, int iBlock, int i bool reverse[2] = {false, false}; if (looper) { - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { reverse[0] = (mClustersXYZ[trk[0]->FirstClusterRef()].z > mClustersXYZ[trk[0]->FirstClusterRef() + trk[0]->NClusters() - 1].z) ^ (trk[0]->CSide() > 0); reverse[1] = (mClustersXYZ[trk[1]->FirstClusterRef()].z < mClustersXYZ[trk[1]->FirstClusterRef() + trk[1]->NClusters() - 1].z) ^ (trk[1]->CSide() > 0); } else { @@ -1301,8 +1335,8 @@ GPUd() void GPUTPCGMMerger::MergeCE(int nBlocks, int nThreads, int iBlock, int i } } - if (Param().ContinuousTracking) { - if (Param().earlyTpcTransform) { + if (Param().par.ContinuousTracking) { + if (Param().par.earlyTpcTransform) { const float z0 = trk[0]->CSide() ? CAMath::Max(mClustersXYZ[trk[0]->FirstClusterRef()].z, mClustersXYZ[trk[0]->FirstClusterRef() + trk[0]->NClusters() - 1].z) : CAMath::Min(mClustersXYZ[trk[0]->FirstClusterRef()].z, mClustersXYZ[trk[0]->FirstClusterRef() + trk[0]->NClusters() - 1].z); const float z1 = trk[1]->CSide() ? CAMath::Max(mClustersXYZ[trk[1]->FirstClusterRef()].z, mClustersXYZ[trk[1]->FirstClusterRef() + trk[1]->NClusters() - 1].z) : CAMath::Min(mClustersXYZ[trk[1]->FirstClusterRef()].z, mClustersXYZ[trk[1]->FirstClusterRef() + trk[1]->NClusters() - 1].z); const float offset = CAMath::Abs(z1) > CAMath::Abs(z0) ? -z0 : z1; @@ -1324,14 +1358,14 @@ GPUd() void GPUTPCGMMerger::MergeCE(int nBlocks, int nThreads, int iBlock, int i for (int k = 1; k >= 0; k--) { if (reverse[k]) { for (int j = trk[k]->NClusters() - 1; j >= 0; j--) { - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { mClustersXYZ[pos] = mClustersXYZ[trk[k]->FirstClusterRef() + j]; } mClusters[pos++] = mClusters[trk[k]->FirstClusterRef() + j]; } } else { for (unsigned int j = 0; j < trk[k]->NClusters(); j++) { - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { mClustersXYZ[pos] = mClustersXYZ[trk[k]->FirstClusterRef() + j]; } mClusters[pos++] = mClusters[trk[k]->FirstClusterRef() + j]; @@ -1397,15 +1431,6 @@ struct GPUTPCGMMerger_CompareClusterIds { } }; -GPUd() void GPUTPCGMMerger::LinkGlobalTracks(int nBlocks, int nThreads, int iBlock, int iThread) -{ - for (int itr = SliceTrackInfoGlobalFirst(0) + iBlock * nThreads + iThread; itr < SliceTrackInfoGlobalLast(NSLICES - 1); itr += nThreads * nBlocks) { - GPUTPCGMSliceTrack& globalTrack = mSliceTrackInfos[itr]; - GPUTPCGMSliceTrack& localTrack = mSliceTrackInfos[globalTrack.LocalTrackId()]; - localTrack.SetGlobalTrackId(localTrack.GlobalTrackId(0) != -1, itr); - } -} - GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int iBlock, int iThread) { @@ -1532,7 +1557,7 @@ GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int i for (int i = 0; i < nParts; i++) { if (trackParts[i]->Leg() == 0 || trackParts[i]->Leg() == leg) { float zt; - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { zt = CAMath::Min(CAMath::Abs(trackParts[i]->ClusterZT0()), CAMath::Abs(trackParts[i]->ClusterZTN())); } else { zt = -trackParts[i]->MinClusterZT(); // Negative time ~ smallest z, to behave the same way // TODO: Check all these min / max ZT @@ -1555,7 +1580,7 @@ GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int i } } bool outwards; - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { outwards = (trackParts[iLongest]->ClusterZT0() > trackParts[iLongest]->ClusterZTN()) ^ trackParts[iLongest]->CSide(); } else { outwards = trackParts[iLongest]->ClusterZT0() < trackParts[iLongest]->ClusterZTN(); @@ -1620,7 +1645,7 @@ GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int i cl[i] XYZ.time = c->mTime; #endif state = c->GetFlags(); - } else if (Param().earlyTpcTransform) { + } else if (Param().par.earlyTpcTransform) { const GPUTPCClusterData& c = GetConstantMem()->tpcTrackers[trackClusters[i].slice].ClusterData()[trackClusters[i].id - GetConstantMem()->tpcTrackers[trackClusters[i].slice].Data().ClusterIdOffset()]; clXYZ[i].x = c.x; clXYZ[i].y = c.y; @@ -1635,6 +1660,12 @@ GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int i const ClusterNative& c = GetConstantMem()->ioPtrs.clustersNative->clustersLinear[trackClusters[i].id]; state = c.getFlags(); } +#ifdef GPUCA_ALIROOT_LIB + cl[i].x = clXYZ[i].x; + cl[i].y = clXYZ[i].y; + cl[i].z = clXYZ[i].z; + cl[i].amp = clXYZ[i].amp; +#endif cl[i].state = state & GPUTPCGMMergedTrackHit::clustererAndSharedFlags; // Only allow edge, deconvoluted, and shared flags cl[i].row = trackClusters[i].row; if (!Param().rec.NonConsecutiveIDs) // We already have global consecutive numbers from the slice tracker, and we need to keep them for late cluster attachment @@ -1663,8 +1694,8 @@ GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int i mergedTrack.SetCSide(p2.CSide()); GPUTPCGMBorderTrack b; - const float toX = Param().earlyTpcTransform ? clXYZ[0].x : Param().tpcGeometry.Row2X(cl[0].row); - if (p2.TransportToX(this, toX, Param().ConstBz, b, GPUCA_MAX_SIN_PHI, false)) { + const float toX = Param().par.earlyTpcTransform ? clXYZ[0].x : Param().tpcGeometry.Row2X(cl[0].row); + if (p2.TransportToX(this, toX, Param().par.ConstBz, b, GPUCA_MAX_SIN_PHI, false)) { p1.X() = toX; p1.Y() = b.Par()[0]; p1.Z() = b.Par()[1]; @@ -1694,13 +1725,13 @@ GPUd() void GPUTPCGMMerger::CollectMergedTracks(int nBlocks, int nThreads, int i if (mergedTrack.NClusters() && mergedTrack.OK()) */ { bool CEside; - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { CEside = (mergedTrack.CSide() != 0) ^ (clXYZ[0].z > clXYZ[nHits - 1].z); } else { auto& cls = mConstantMem->ioPtrs.clustersNative->clustersLinear; CEside = cls[cl[0].num].getTime() < cls[cl[nHits - 1].num].getTime(); } - MergeCEFill(trackParts[CEside ? lastTrackIndex : firstTrackIndex], cl[CEside ? (nHits - 1) : 0], clXYZ[CEside ? (nHits - 1) : 0], iOutputTrack); + MergeCEFill(trackParts[CEside ? lastTrackIndex : firstTrackIndex], cl[CEside ? (nHits - 1) : 0], &clXYZ[CEside ? (nHits - 1) : 0], iOutputTrack); } } // itr } @@ -1720,7 +1751,7 @@ GPUd() void GPUTPCGMMerger::PrepareClustersForFit0(int nBlocks, int nThreads, in } } -#if defined(__CUDACC__) || defined(__HIPCC__) // Specialize GPUTPCGMMergerSortTracks and GPUTPCGMMergerSortTracksQPt +#if (defined(__CUDACC__) || defined(__HIPCC__)) && !defined(GPUCA_GPUCODE_GENRTC) // Specialize GPUTPCGMMergerSortTracks and GPUTPCGMMergerSortTracksQPt struct GPUTPCGMMergerSortTracks_comp { const GPUTPCGMMergedTrack* const mCmp; GPUhd() GPUTPCGMMergerSortTracks_comp(GPUTPCGMMergedTrack* cmp) : mCmp(cmp) {} @@ -1741,7 +1772,7 @@ struct GPUTPCGMMergerSortTracks_comp { template <> void GPUCA_KRNL_BACKEND_CLASS::runKernelBackendInternal<GPUTPCGMMergerSortTracks, 0>(krnlSetup& _xyz) { - GPUDebugTiming timer(mDeviceProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); + GPUDebugTiming timer(mProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); thrust::device_ptr<unsigned int> trackSort((unsigned int*)mProcessorsShadow->tpcMerger.TrackOrderProcess()); ThrustVolatileAsyncAllocator alloc(this); thrust::sort(GPUCA_THRUST_NAMESPACE::par(alloc).on(mInternals->Streams[_xyz.x.stream]), trackSort, trackSort + processors()->tpcMerger.NOutputTracks(), GPUTPCGMMergerSortTracks_comp(mProcessorsShadow->tpcMerger.OutputTracks())); @@ -1761,7 +1792,7 @@ struct GPUTPCGMMergerSortTracksQPt_comp { template <> void GPUCA_KRNL_BACKEND_CLASS::runKernelBackendInternal<GPUTPCGMMergerSortTracksQPt, 0>(krnlSetup& _xyz) { - GPUDebugTiming timer(mDeviceProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); + GPUDebugTiming timer(mProcessingSettings.debugLevel, nullptr, mInternals->Streams, _xyz, this); thrust::device_ptr<unsigned int> trackSort((unsigned int*)mProcessorsShadow->tpcMerger.TmpMem()); ThrustVolatileAsyncAllocator alloc(this); thrust::sort(GPUCA_THRUST_NAMESPACE::par(alloc).on(mInternals->Streams[_xyz.x.stream]), trackSort, trackSort + processors()->tpcMerger.NOutputTracks(), GPUTPCGMMergerSortTracksQPt_comp(mProcessorsShadow->tpcMerger.OutputTracks())); @@ -1805,10 +1836,13 @@ GPUd() void GPUTPCGMMerger::PrepareClustersForFit1(int nBlocks, int nThreads, in GPUAtomic(unsigned int)* sharedCount = (GPUAtomic(unsigned int)*)(trackSort + CAMath::nextMultipleOf<4>(mMemory->nOutputTracks)); for (unsigned int i = iBlock * nThreads + iThread; i < mMemory->nOutputTracks; i += nBlocks * nThreads) { mTrackOrderAttach[trackSort[i]] = i; - } - for (unsigned int i = iBlock * nThreads + iThread; i < mMemory->nOutputTrackClusters; i += nBlocks * nThreads) { - mClusterAttachment[mClusters[i].num] = attachAttached | attachGood; - CAMath::AtomicAdd(&sharedCount[mClusters[i].num], 1u); + const GPUTPCGMMergedTrack& trk = mOutputTracks[i]; + if (trk.OK()) { + for (unsigned int j = 0; j < trk.NClusters(); j++) { + mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num] = attachAttached | attachGood; + CAMath::AtomicAdd(&sharedCount[mClusters[trk.FirstClusterRef() + j].num], 1u); + } + } } } @@ -1820,6 +1854,15 @@ GPUd() void GPUTPCGMMerger::PrepareClustersForFit2(int nBlocks, int nThreads, in mClusters[i].state |= GPUTPCGMMergedTrackHit::flagShared; } } + if (mClusterStateExt) { + for (unsigned int i = iBlock * nThreads + iThread; i < mNMaxClusters; i += nBlocks * nThreads) { + unsigned char state = GetConstantMem()->ioPtrs.clustersNative->clustersLinear[i].getFlags(); + if (sharedCount[i] > 1) { + state |= GPUTPCGMMergedTrackHit::flagShared; + } + mClusterStateExt[i] = state; + } + } } GPUd() void GPUTPCGMMerger::Finalize0(int nBlocks, int nThreads, int iBlock, int iThread) @@ -1873,3 +1916,136 @@ GPUd() void GPUTPCGMMerger::Finalize2(int nBlocks, int nThreads, int iBlock, int } } } + +struct MergeLooperParam { + float absz; + float tgl; + float qpt; + float x; + float y; + unsigned int id; +}; + +GPUd() void GPUTPCGMMerger::MergeLoopers(int nBlocks, int nThreads, int iBlock, int iThread) +{ + if (iThread || iBlock) { + return; + } +#ifndef GPUCA_GPUCODE + std::vector<MergeLooperParam> params; + const float lowPtThresh = Param().rec.tpcRejectQPt * 1.1f; // Might need to merge tracks above the threshold with parts below the threshold + for (unsigned int i = 0; i < mMemory->nOutputTracks; i++) { + const auto& trk = mOutputTracks[i]; + const auto& p = trk.GetParam(); + const float qptabs = CAMath::Abs(p.GetQPt()); + if (trk.NClusters() && qptabs > 5.f && qptabs <= lowPtThresh) { + const int slice = mClusters[trk.FirstClusterRef() + trk.NClusters() - 1].slice; + const float z = p.GetZ() + (Param().par.earlyTpcTransform ? p.GetTZOffset() : GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(slice, p.GetTZOffset(), Param().par.continuousMaxTimeBin)); + float sinA, cosA; + CAMath::SinCos(trk.GetAlpha(), sinA, cosA); + float gx = cosA * p.GetX() - sinA * p.GetY(); + float gy = cosA * p.GetY() + sinA * p.GetX(); + float bz = Param().polynomialField.GetFieldBz(gx, gy, p.GetZ()); + const float r1 = p.GetQPt() * bz; + const float r = CAMath::Abs(r1) > 0.0001 ? (1.f / r1) : 10000; + const float mx = p.GetX() + r * p.GetSinPhi(); + const float my = p.GetY() - r * CAMath::Sqrt(1 - p.GetSinPhi() * p.GetSinPhi()); + const float gmx = cosA * mx - sinA * my; + const float gmy = cosA * my + sinA * mx; + params.emplace_back(MergeLooperParam{CAMath::Abs(z), CAMath::Abs(p.GetDzDs()), p.GetDzDs() > 0 ? p.GetQPt() : -p.GetQPt(), gmx, gmy, i}); + + /*printf("Track %d Sanity qpt %f snp %f bz %f\n", (int)params.size(), p.GetQPt(), p.GetSinPhi(), bz); + for (unsigned int k = 0;k < trk.NClusters();k++) { + float xx, yy, zz; + if (Param().par.earlyTpcTransform) { + const float zOffset = (mClusters[trk.FirstClusterRef() + k].slice < 18) == (mClusters[trk.FirstClusterRef() + 0].slice < 18) ? p.GetTZOffset() : -p.GetTZOffset(); + xx = mClustersXYZ[trk.FirstClusterRef() + k].x; + yy = mClustersXYZ[trk.FirstClusterRef() + k].y; + zz = mClustersXYZ[trk.FirstClusterRef() + k].z - zOffset; + } else { + const ClusterNative& GPUrestrict() cl = GetConstantMem()->ioPtrs.clustersNative->clustersLinear[mClusters[trk.FirstClusterRef() + k].num]; + GetConstantMem()->calibObjects.fastTransform->Transform(mClusters[trk.FirstClusterRef() + k].slice, mClusters[trk.FirstClusterRef() + k].row, cl.getPad(), cl.getTime(), xx, yy, zz, p.GetTZOffset()); + } + float sa2, ca2; + CAMath::SinCos(Param().Alpha(mClusters[trk.FirstClusterRef() + k].slice), sa2, ca2); + float cx = ca2 * xx - sa2 * yy; + float cy = ca2 * yy + sa2 * xx; + float dist = sqrtf((cx - gmx) * (cx - gmx) + (cy - gmy) * (cy - gmy)); + printf("Hit %3d/%3d slice %d xy %f %f R %f\n", k, trk.NClusters(), (int)mClusters[trk.FirstClusterRef() + k].slice, cx, cy, dist); + }*/ + } + } + std::sort(params.begin(), params.end(), [](const MergeLooperParam& a, const MergeLooperParam& b) { return a.absz < b.absz; }); +#if GPUCA_MERGE_LOOPER_MC + std::vector<long int> paramLabels(params.size()); + for (unsigned int i = 0; i < params.size(); i++) { + paramLabels[i] = GetTrackLabel(mOutputTracks[params[i].id]); + } + std::vector<bool> dropped(params.size()); + std::vector<bool> droppedMC(params.size()); + std::vector<int> histMatch(101); + std::vector<int> histFail(101); +#endif + + for (unsigned int i = 0; i < params.size(); i++) { + for (unsigned int j = i + 1; j < params.size(); j++) { + if (params[j].absz > params[i].absz + 100.f) { + break; + } + float dqpt = CAMath::Min(CAMath::Abs(params[i].tgl), CAMath::Abs(params[j].tgl)) < 0.05f ? (CAMath::Abs(params[i].qpt) - CAMath::Abs(params[i].qpt)) : (params[i].qpt - params[j].qpt); + float d = CAMath::Sum2((params[i].x - params[j].x) * (1.f / 5.f), (params[i].y - params[j].y) * (1.f / 5.f), (params[i].tgl - params[j].tgl) * (1.f / 0.15f), dqpt / CAMath::Min(params[i].qpt, params[j].qpt) * (1.f / 0.15f)); + //bool EQ = CAMath::Abs(params[i].x - params[j].x) < 10.f && CAMath::Abs(params[i].y - params[j].y) < 10.f && CAMath::Abs(params[i].tgl - params[j].tgl) < 0.15f && CAMath::Abs((params[i].qpt - params[j].qpt) / CAMath::Min(params[i].qpt, params[j].qpt)) < 0.15f; + bool EQ = d < 1.5f; +#if GPUCA_MERGE_LOOPER_MC + const long int label1 = paramLabels[i]; + const long int label2 = paramLabels[j]; + bool labelEQ = label1 != -1 && label1 == label2; + if (EQ || labelEQ) { + printf("Matching track %d/%d %u-%u (%ld/%ld): dist %f side %d %d, tgl %f %f, qpt %f %f, x %f %f, y %f %f\n", (int)EQ, (int)labelEQ, i, j, label1, label2, d, (int)mOutputTracks[params[i].id].CSide(), (int)mOutputTracks[params[j].id].CSide(), params[i].tgl, params[j].tgl, params[i].qpt, params[j].qpt, params[i].x, params[j].x, params[i].y, params[j].y); + } + if (EQ) { + dropped[j] = true; + } + if (labelEQ) { + droppedMC[j] = true; + histMatch[CAMath::Min<int>(100, d * 10.f)]++; + } + if (d < 10.f && !labelEQ) { + histFail[CAMath::Min<int>(100, d * 10.f)]++; + } +#endif + if (EQ) { + mOutputTracks[params[j].id].SetMergedLooper(true); + if (CAMath::Abs(params[j].qpt) >= Param().rec.tpcRejectQPt) { + mOutputTracks[params[i].id].SetMergedLooper(true); + } + } + } + } +#if GPUCA_MERGE_LOOPER_MC + int total = 0, totalmc = 0, good = 0, missed = 0, fake = 0; + for (unsigned int i = 0; i < params.size(); i++) { + total += dropped[i]; + totalmc += droppedMC[i]; + good += dropped[i] && droppedMC[i]; + missed += droppedMC[i] && !dropped[i]; + fake += dropped[i] && !droppedMC[i]; + } + if (good) { + printf("%20s: %8d\n", "Total", total); + printf("%20s: %8d\n", "TotalMC", totalmc); + printf("%20s: %8d (%8.3f%% %8.3f%%)\n", "Good", good, 100.f * good / total, 100.f * good / totalmc); + printf("%20s: %8d (%8.3f%%)\n", "Missed", missed, 100.f * missed / totalmc); + printf("%20s: %8d (%8.3f%%)\n", "Fake", fake, 100.f * fake / total); + } + printf("Match histo\n"); + for (unsigned int i = 0; i < histMatch.size(); i++) { + printf("%8.3f: %3d\n", i / 10.f + 0.05f, histMatch[i]); + } + printf("Fake histo\n"); + for (unsigned int i = 0; i < histFail.size(); i++) { + printf("%8.3f: %3d\n", i / 10.f + 0.05f, histFail[i]); + } +#endif +#endif +} diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 539ff4275ca3a..c5e0da4c571d8 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -110,11 +110,12 @@ class GPUTPCGMMerger : public GPUProcessor GPUhdi() unsigned int* TrackOrderAttach() const { return mTrackOrderAttach; } GPUhdi() unsigned int* TrackOrderProcess() const { return mTrackOrderProcess; } GPUhdi() unsigned int* RetryRefitIds() const { return mRetryRefitIds; } + GPUhdi() unsigned char* ClusterStateExt() const { return mClusterStateExt; } GPUhdi() GPUTPCGMLoopData* LoopData() const { return mLoopData; } GPUhdi() memory* Memory() const { return mMemory; } GPUhdi() GPUAtomic(unsigned int) * TmpCounter() { return mMemory->tmpCounter; } GPUhdi() uint4* TmpMem() { return mTmpMem; } - GPUhdi() GPUTPCGMMergerTypes::GPUTPCGMBorderRange* BorderRange(int i) { return mBorderRange[i]; } + GPUhdi() gputpcgmmergertypes::GPUTPCGMBorderRange* BorderRange(int i) { return mBorderRange[i]; } GPUd() unsigned short MemoryResMemory() { return mMemoryResMemory; } GPUd() unsigned short MemoryResOutput() const { return mMemoryResOutput; } @@ -135,7 +136,7 @@ class GPUTPCGMMerger : public GPUProcessor GPUd() void MergeBorderTracks(int nBlocks, int nThreads, int iBlock, int iThread, int iSlice, char withinSlice, char mergeMode); GPUd() void MergeBorderTracksSetup(int& n1, int& n2, GPUTPCGMBorderTrack*& b1, GPUTPCGMBorderTrack*& b2, int& jSlice, int iSlice, char withinSlice, char mergeMode); template <int I> - GPUd() void MergeBorderTracks(int nBlocks, int nThreads, int iBlock, int iThread, GPUTPCGMMergerTypes::GPUTPCGMBorderRange* range, int N, int cmpMax); + GPUd() void MergeBorderTracks(int nBlocks, int nThreads, int iBlock, int iThread, gputpcgmmergertypes::GPUTPCGMBorderRange* range, int N, int cmpMax); GPUd() void SortTracks(int nBlocks, int nThreads, int iBlock, int iThread); GPUd() void SortTracksQPt(int nBlocks, int nThreads, int iBlock, int iThread); GPUd() void SortTracksPrepare(int nBlocks, int nThreads, int iBlock, int iThread); @@ -151,7 +152,8 @@ class GPUTPCGMMerger : public GPUProcessor GPUd() void ResolveFindConnectedComponentsHookNeighbors(int nBlocks, int nThreads, int iBlock, int iThread); GPUd() void ResolveFindConnectedComponentsHookLinks(int nBlocks, int nThreads, int iBlock, int iThread); GPUd() void ResolveFindConnectedComponentsMultiJump(int nBlocks, int nThreads, int iBlock, int iThread); - GPUd() void ResolveMergeSlices(GPUTPCGMMergerTypes::GPUResolveSharedMemory& smem, int nBlocks, int nThreads, int iBlock, int iThread, char useOrigTrackParam, char mergeAll); + GPUd() void ResolveMergeSlices(gputpcgmmergertypes::GPUResolveSharedMemory& smem, int nBlocks, int nThreads, int iBlock, int iThread, char useOrigTrackParam, char mergeAll); + GPUd() void MergeLoopers(int nBlocks, int nThreads, int iBlock, int iThread); #ifndef GPUCA_GPUCODE void DumpSliceTracks(std::ostream& out); @@ -169,12 +171,15 @@ class GPUTPCGMMerger : public GPUProcessor template <int I> GPUd() void MergeBorderTracks(int nBlocks, int nThreads, int iBlock, int iThread, int iSlice1, GPUTPCGMBorderTrack* B1, int N1, int iSlice2, GPUTPCGMBorderTrack* B2, int N2, int mergeMode = 0); - GPUd() void MergeCEFill(const GPUTPCGMSliceTrack* track, const GPUTPCGMMergedTrackHit& cls, const GPUTPCGMMergedTrackHitXYZ& clsXYZ, int itr); + GPUd() void MergeCEFill(const GPUTPCGMSliceTrack* track, const GPUTPCGMMergedTrackHit& cls, const GPUTPCGMMergedTrackHitXYZ* clsXYZ, int itr); void CheckMergedTracks(); #ifndef GPUCA_GPUCODE void PrintMergeGraph(const GPUTPCGMSliceTrack* trk, std::ostream& out); - int GetTrackLabel(const GPUTPCGMBorderTrack& trk); + template <class T, class S> + long int GetTrackLabelA(const S& trk); + template <class S> + long int GetTrackLabel(const S& trk); #endif GPUdi() int SliceTrackInfoFirst(int iSlice) @@ -218,11 +223,12 @@ class GPUTPCGMMerger : public GPUProcessor GPUAtomic(unsigned int) * mClusterAttachment; unsigned int* mTrackOrderAttach; unsigned int* mTrackOrderProcess; + unsigned char* mClusterStateExt; uint4* mTmpMem; GPUTPCGMBorderTrack* mBorderMemory; // memory for border tracks GPUTPCGMBorderTrack* mBorder[2 * NSLICES]; - GPUTPCGMMergerTypes::GPUTPCGMBorderRange* mBorderRangeMemory; // memory for border tracks - GPUTPCGMMergerTypes::GPUTPCGMBorderRange* mBorderRange[NSLICES]; // memory for border tracks + gputpcgmmergertypes::GPUTPCGMBorderRange* mBorderRangeMemory; // memory for border tracks + gputpcgmmergertypes::GPUTPCGMBorderRange* mBorderRange[NSLICES]; // memory for border tracks memory* mMemory; unsigned int* mRetryRefitIds; GPUTPCGMLoopData* mLoopData; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx index 21d79de6cc2a7..d9c9e0c5cec23 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx @@ -33,7 +33,7 @@ #include "GPUReconstruction.h" using namespace GPUCA_NAMESPACE::gpu; -using namespace GPUTPCGMMergerTypes; +using namespace gputpcgmmergertypes; static std::vector<int> trackOrder, trackOrderReverse; static int getTrackOrderReverse(int i) { return i == -1 ? -1 : trackOrderReverse[i]; } diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.cxx index 33f29d5ea5bdd..8068955daaac6 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.cxx @@ -23,7 +23,7 @@ template <> GPUdii() void GPUTPCGMMergerTrackFit::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger, int mode) { const int iEnd = mode == -1 ? merger.Memory()->nRetryRefit : merger.NOutputTracks(); - GPUCA_OPENMP(parallel for num_threads(merger.GetRec().GetDeviceProcessingSettings().ompKernels ? 1 : merger.GetRec().GetDeviceProcessingSettings().nThreads)) + GPUCA_OPENMP(parallel for if(!merger.GetRec().GetProcessingSettings().ompKernels) num_threads(merger.GetRec().GetProcessingSettings().ompThreads)) for (int ii = get_global_id(0); ii < iEnd; ii += get_global_size(0)) { const int i = mode == -1 ? merger.RetryRefitIds()[ii] : mode ? merger.TrackOrderProcess()[ii] : ii; GPUTPCGMTrackParam::RefitTrack(merger.OutputTracks()[i], i, &merger, mode == -1); @@ -33,7 +33,7 @@ GPUdii() void GPUTPCGMMergerTrackFit::Thread<0>(int nBlocks, int nThreads, int i template <> GPUdii() void GPUTPCGMMergerFollowLoopers::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger) { - GPUCA_OPENMP(parallel for num_threads(merger.GetRec().GetDeviceProcessingSettings().ompKernels ? 1 : merger.GetRec().GetDeviceProcessingSettings().nThreads)) + GPUCA_OPENMP(parallel for if(!merger.GetRec().GetProcessingSettings().ompKernels) num_threads(merger.GetRec().GetProcessingSettings().ompThreads)) for (unsigned int i = get_global_id(0); i < merger.Memory()->nLoopData; i += get_global_size(0)) { GPUTPCGMTrackParam::RefitLoop(&merger, i); } @@ -120,7 +120,7 @@ GPUdii() void GPUTPCGMMergerMergeBorders::Thread(int nBlocks, int nThreads, int } template GPUd() void GPUTPCGMMergerMergeBorders::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger, int iSlice, char withinSlice, char mergeMode); template GPUd() void GPUTPCGMMergerMergeBorders::Thread<2>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger, int iSlice, char withinSlice, char mergeMode); -template GPUd() void GPUTPCGMMergerMergeBorders::Thread<3>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger, GPUTPCGMMergerTypes::GPUTPCGMBorderRange* range, int N, int cmpMax); +template GPUd() void GPUTPCGMMergerMergeBorders::Thread<3>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger, gputpcgmmergertypes::GPUTPCGMBorderRange* range, int N, int cmpMax); template <> GPUdii() void GPUTPCGMMergerMergeBorders::Thread<1>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger, int iSlice, char withinSlice, char mergeMode) { @@ -204,3 +204,9 @@ GPUdii() void GPUTPCGMMergerFinalize::Thread<2>(int nBlocks, int nThreads, int i { merger.Finalize2(nBlocks, nThreads, iBlock, iThread); } + +template <> +GPUdii() void GPUTPCGMMergerMergeLoopers::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() merger) +{ + merger.MergeLoopers(nBlocks, nThreads, iBlock, iThread); +} diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h b/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h index c93099151eccc..ee6a50d56f9ed 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h @@ -92,7 +92,7 @@ class GPUTPCGMMergerUnpackResetIds : public GPUTPCGMMergerGeneral class GPUTPCGMMergerResolve : public GPUTPCGMMergerGeneral { public: - struct GPUSharedMemory : public GPUTPCGMMergerTypes::GPUResolveSharedMemory { + struct GPUSharedMemory : public gputpcgmmergertypes::GPUResolveSharedMemory { }; #if !defined(GPUCA_ALIROOT_LIB) || !defined(GPUCA_GPUCODE) @@ -214,6 +214,15 @@ class GPUTPCGMMergerFinalize : public GPUTPCGMMergerGeneral #endif }; +class GPUTPCGMMergerMergeLoopers : public GPUTPCGMMergerGeneral +{ + public: +#if !defined(GPUCA_ALIROOT_LIB) || !defined(GPUCA_GPUCODE) + template <int iKernel = defaultKernel> + GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& smem, processorType& merger); +#endif +}; + } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h b/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h index bac688e79884b..4911313921c8e 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerTypes.h @@ -21,7 +21,7 @@ namespace GPUCA_NAMESPACE { namespace gpu { -namespace GPUTPCGMMergerTypes +namespace gputpcgmmergertypes { enum attachTypes { attachAttached = 0x40000000, @@ -33,10 +33,8 @@ enum attachTypes { attachAttached = 0x40000000, attachFlagMask = 0xFC000000 }; struct InterpolationErrorHit { - float posY; - float errorY; - float posZ; - float errorZ; + float posY, posZ; + GPUCA_MERGER_INTERPOLATION_ERROR_TYPE errorY, errorZ; }; struct InterpolationErrors { @@ -53,7 +51,7 @@ struct GPUTPCGMBorderRange { float fMin, fMax; }; -} // namespace GPUTPCGMMergerTypes +} // namespace gputpcgmmergertypes } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/Merger/GPUTPCGMPolynomialField.h b/GPU/GPUTracking/Merger/GPUTPCGMPolynomialField.h index 674bd8092a789..d334cc7ca2563 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMPolynomialField.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMPolynomialField.h @@ -14,7 +14,7 @@ #ifndef GPUTPCGMPOLYNOMIALFIELD_H #define GPUTPCGMPOLYNOMIALFIELD_H -#include "GPUTPCDef.h" +#include "GPUCommonDef.h" namespace GPUCA_NAMESPACE { @@ -44,13 +44,13 @@ class GPUTPCGMPolynomialField GPUdi() float GetNominalBz() const { return mNominalBz; } - GPUd() void GetField(float x, float y, float z, float B[3]) const; + GPUd() void GetField(float x, float y, float z, float* B) const; GPUd() float GetFieldBz(float x, float y, float z) const; - GPUd() void GetFieldTrd(float x, float y, float z, float B[3]) const; + GPUd() void GetFieldTrd(float x, float y, float z, float* B) const; GPUd() float GetFieldTrdBz(float x, float y, float z) const; - GPUd() void GetFieldIts(float x, float y, float z, float B[3]) const; + GPUd() void GetFieldIts(float x, float y, float z, float* B) const; GPUd() float GetFieldItsBz(float x, float y, float z) const; void Print() const; @@ -164,7 +164,7 @@ GPUdi() void GPUTPCGMPolynomialField::GetPolynomsTpc(float x, float y, float z, f[9] = z * z; } -GPUdi() void GPUTPCGMPolynomialField::GetField(float x, float y, float z, float B[3]) const +GPUdi() void GPUTPCGMPolynomialField::GetField(float x, float y, float z, float* B) const { const float* fBxS = &mTpcBx[1]; const float* fByS = &mTpcBy[1]; @@ -220,7 +220,7 @@ GPUdi() void GPUTPCGMPolynomialField::GetPolynomsTrd(float x, float y, float z, f[19] = z * zz; } -GPUdi() void GPUTPCGMPolynomialField::GetFieldTrd(float x, float y, float z, float B[3]) const +GPUdi() void GPUTPCGMPolynomialField::GetFieldTrd(float x, float y, float z, float* B) const { float f[NTRDM]; GetPolynomsTrd(x, y, z, f); @@ -266,7 +266,7 @@ GPUdi() void GPUTPCGMPolynomialField::GetPolynomsIts(float x, float y, float z, */ } -GPUdi() void GPUTPCGMPolynomialField::GetFieldIts(float x, float y, float z, float B[3]) const +GPUdi() void GPUTPCGMPolynomialField::GetFieldIts(float x, float y, float z, float* B) const { const float* fBxS = &mItsBx[1]; const float* fByS = &mItsBy[1]; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx index 1cec87e24e0c9..ea7659578305f 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx @@ -19,10 +19,6 @@ #include "GPUParam.inc" #include "GPUTPCGMMergerTypes.h" -#ifndef __OPENCL__ -#include <cmath> -#endif - #if defined(GPUCA_GM_USE_FULL_FIELD) #include "AliTracker.h" #include "AliMagF.h" @@ -121,8 +117,8 @@ GPUd() int GPUTPCGMPropagator::RotateToAlpha(float newAlpha) // return value is error code (0==no error) // - float newCosAlpha = CAMath::Cos(newAlpha); - float newSinAlpha = CAMath::Sin(newAlpha); + float newCosAlpha, newSinAlpha; + CAMath::SinCos(newAlpha, newSinAlpha, newCosAlpha); float cc = newCosAlpha * mCosAlpha + newSinAlpha * mSinAlpha; // cos(newAlpha - mAlpha); float ss = newSinAlpha * mCosAlpha - newCosAlpha * mSinAlpha; //sin(newAlpha - mAlpha); @@ -159,11 +155,20 @@ GPUd() int GPUTPCGMPropagator::RotateToAlpha(float newAlpha) float trackX = x0 * cc + ss * mT->Y(); // transport t0 to trackX - float B[3]; - GetBxByBz(newAlpha, t0.X(), t0.Y(), t0.Z(), B); float dLp = 0; - if (t0.PropagateToXBxByBz(trackX, B[0], B[1], B[2], dLp)) { - return -1; + float Bz; + if (mPropagateBzOnly) { + Bz = GetBzBase(newCosAlpha, newSinAlpha, t0.X(), t0.Y(), t0.Z()); + if (t0.PropagateToXBzLight(trackX, Bz, dLp)) { + return -1; + } + } else { + float B[3]; + GetBxByBz(newAlpha, t0.X(), t0.Y(), t0.Z(), B); + Bz = B[2]; + if (t0.PropagateToXBxByBz(trackX, B[0], B[1], B[2], dLp)) { + return -1; + } } if (CAMath::Abs(t0.SinPhi()) >= mMaxSinPhi) { @@ -237,7 +242,7 @@ GPUd() int GPUTPCGMPropagator::RotateToAlpha(float newAlpha) // only covariance changes. Use rotated and transported t0 for linearisation float j3 = -t0.Py() / t0.Px(); float j4 = -t0.Pz() / t0.Px(); - float j5 = t0.QPt() * B[2]; + float j5 = t0.QPt() * Bz; // Y Z Sin DzDs q/p X // Jacobian J1 = { { 1, 0, 0, 0, 0, j3 }, // Y @@ -332,11 +337,13 @@ GPUd() int GPUTPCGMPropagator::PropagateToXAlphaBz(float posX, float posAlpha, b GPUTPCGMPhysicalTrackModel t0e(mT0); float dLp = 0; - if (t0e.PropagateToXBzLight(posX, Bz, dLp)) + if (t0e.PropagateToXBzLight(posX, Bz, dLp)) { return 1; + } - if (CAMath::Abs(t0e.SinPhi()) >= mMaxSinPhi) + if (CAMath::Abs(t0e.SinPhi()) >= mMaxSinPhi) { return -3; + } return FollowLinearization(t0e, Bz, dLp, inFlyDirection); } @@ -631,13 +638,13 @@ GPUd() float GPUTPCGMPropagator::PredictChi2(float posY, float posZ, float err2Y } } -GPUd() int GPUTPCGMPropagator::Update(float posY, float posZ, int iRow, const GPUParam& GPUrestrict() param, short clusterState, char rejectChi2, GPUTPCGMMergerTypes::InterpolationErrorHit* inter, bool refit) +GPUd() int GPUTPCGMPropagator::Update(float posY, float posZ, int iRow, const GPUParam& GPUrestrict() param, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit) { float err2Y, err2Z; GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState); if (rejectChi2 >= 2) { - if (rejectChi2 == 3 && inter->errorY < 0) { + if (rejectChi2 == 3 && inter->errorY < (GPUCA_MERGER_INTERPOLATION_ERROR_TYPE)0) { rejectChi2 = 1; } else { int retVal = InterpolateReject(posY, posZ, clusterState, rejectChi2, inter, err2Y, err2Z); @@ -667,7 +674,7 @@ GPUd() int GPUTPCGMPropagator::Update(float posY, float posZ, int iRow, const GP return Update(posY, posZ, clusterState, rejectChi2 == 1, err2Y, err2Z); } -GPUd() int GPUTPCGMPropagator::InterpolateReject(float posY, float posZ, short clusterState, char rejectChi2, GPUTPCGMMergerTypes::InterpolationErrorHit* inter, float err2Y, float err2Z) +GPUd() int GPUTPCGMPropagator::InterpolateReject(float posY, float posZ, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, float err2Y, float err2Z) { float* GPUrestrict() mC = mT->Cov(); float* GPUrestrict() mP = mT->Par(); @@ -681,8 +688,8 @@ GPUd() int GPUTPCGMPropagator::InterpolateReject(float posY, float posZ, short c if (mFitInProjections || mT->NDF() <= 0) { const float Iz0 = inter->posY - mP[0]; const float Iz1 = inter->posZ - mP[1]; - const float Iw0 = 1.f / (mC[0] + inter->errorY); - const float Iw2 = 1.f / (mC[2] + inter->errorZ); + const float Iw0 = 1.f / (mC[0] + (float)inter->errorY); + const float Iw2 = 1.f / (mC[2] + (float)inter->errorZ); const float Ik00 = mC[0] * Iw0; const float Ik11 = mC[2] * Iw2; const float ImP0 = mP[0] + Ik00 * Iz0; @@ -699,8 +706,8 @@ GPUd() int GPUTPCGMPropagator::InterpolateReject(float posY, float posZ, short c } else { const float Iz0 = inter->posY - mP[0]; const float Iz1 = inter->posZ - mP[1]; - float Iw0 = mC[2] + inter->errorZ; - float Iw2 = mC[0] + inter->errorY; + float Iw0 = mC[2] + (float)inter->errorZ; + float Iw2 = mC[0] + (float)inter->errorY; float Idet = CAMath::Max(1e-10f, Iw0 * Iw2 - mC[1] * mC[1]); Idet = 1.f / Idet; Iw0 *= Idet; @@ -1064,7 +1071,7 @@ GPUd() o2::base::MatBudget GPUTPCGMPropagator::getMatBudget(const float* p1, con #endif } -GPUdni() void GPUTPCGMPropagator::UpdateMaterial(const GPUTPCGMPhysicalTrackModel& GPUrestrict() t0e) +GPUdic(0, 1) void GPUTPCGMPropagator::UpdateMaterial(const GPUTPCGMPhysicalTrackModel& GPUrestrict() t0e) { #ifdef HAVE_O2HEADERS float xyz1[3] = {getGlobalX(mT0.GetX(), mT0.GetY()), getGlobalY(mT0.GetX(), mT0.GetY()), mT0.GetZ()}; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h index 4e7f01e976391..5e42cc1be2993 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h @@ -35,7 +35,7 @@ namespace gpu { class GPUTPCGMTrackParam; struct GPUParam; -namespace GPUTPCGMMergerTypes +namespace gputpcgmmergertypes { struct InterpolationErrorHit; } @@ -98,9 +98,9 @@ class GPUTPCGMPropagator GPUd() int PropagateToXAlphaBz(float posX, float posAlpha, bool inFlyDirection); - GPUd() int Update(float posY, float posZ, int iRow, const GPUParam& param, short clusterState, char rejectChi2, GPUTPCGMMergerTypes::InterpolationErrorHit* inter, bool refit); + GPUd() int Update(float posY, float posZ, int iRow, const GPUParam& param, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit); GPUd() int Update(float posY, float posZ, short clusterState, bool rejectChi2, float err2Y, float err2Z); - GPUd() int InterpolateReject(float posY, float posZ, short clusterState, char rejectChi2, GPUTPCGMMergerTypes::InterpolationErrorHit* inter, float err2Y, float err2Z); + GPUd() int InterpolateReject(float posY, float posZ, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, float err2Y, float err2Z); GPUd() float PredictChi2(float posY, float posZ, int iRow, const GPUParam& param, short clusterState) const; GPUd() float PredictChi2(float posY, float posZ, float err2Y, float err2Z) const; GPUd() int RejectCluster(float chiY, float chiZ, unsigned char clusterState) @@ -130,6 +130,7 @@ class GPUTPCGMPropagator GPUd() void GetErr2(float& err2Y, float& err2Z, const GPUParam& param, float posZ, int iRow, short clusterState) const; GPUd() float GetAlpha() const { return mAlpha; } + GPUd() void SetAlpha(float v) { mAlpha = v; } GPUd() float GetQPt0() const { return mT0.GetQPt(); } GPUd() float GetSinPhi0() const { return mT0.GetSinPhi(); } GPUd() float GetCosPhi0() const { return mT0.GetCosPhi(); } @@ -186,12 +187,16 @@ class GPUTPCGMPropagator GPUdi() void GPUTPCGMPropagator::GetBxByBz(float Alpha, float X, float Y, float Z, float B[3]) const { - GetBxByBzBase(CAMath::Cos(Alpha), CAMath::Sin(Alpha), X, Y, Z, B); + float c, s; + CAMath::SinCos(Alpha, s, c); + GetBxByBzBase(c, s, X, Y, Z, B); } GPUdi() float GPUTPCGMPropagator::GetBz(float Alpha, float X, float Y, float Z) const { - return GetBzBase(CAMath::Cos(Alpha), CAMath::Sin(Alpha), X, Y, Z); + float c, s; + CAMath::SinCos(Alpha, s, c); + return GetBzBase(c, s, X, Y, Z); } GPUdi() void GPUTPCGMPropagator::GetBxByBz(float X, float Y, float Z, float B[3]) const @@ -220,8 +225,7 @@ GPUdi() void GPUTPCGMPropagator::SetTrack(GPUTPCGMTrackParam* GPUrestrict() trac } mT0.Set(*mT); mAlpha = Alpha; - mCosAlpha = CAMath::Cos(mAlpha); - mSinAlpha = CAMath::Sin(mAlpha); + CAMath::SinCos(mAlpha, mSinAlpha, mCosAlpha); CalculateMaterialCorrection(); } diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.cxx b/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.cxx index c91de763bc92b..1627d5bdcbe67 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.cxx @@ -18,9 +18,6 @@ #include "GPUTPCGMMerger.h" #include "GPUTPCConvertImpl.h" #include "GPUParam.inc" -#ifndef __OPENCLCPP__ -#include <cmath> -#endif using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; @@ -39,10 +36,10 @@ GPUd() void GPUTPCGMSliceTrack::Set(const GPUTPCGMMerger* merger, const GPUTPCTr mParam.mSecPhi = 1.f / mParam.mCosPhi; mAlpha = alpha; mSlice = slice; - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { mTZOffset = t.GetZOffset(); } else { - mTZOffset = merger->GetConstantMem()->calibObjects.fastTransform->convZOffsetToVertexTime(slice, t.GetZOffset(), merger->Param().continuousMaxTimeBin); + mTZOffset = merger->GetConstantMem()->calibObjects.fastTransform->convZOffsetToVertexTime(slice, t.GetZOffset(), merger->Param().par.continuousMaxTimeBin); } mNClusters = sliceTr->NHits(); } @@ -97,7 +94,7 @@ GPUd() void GPUTPCGMSliceTrack::SetParam2(const GPUTPCGMTrackParam& trk) GPUd() bool GPUTPCGMSliceTrack::FilterErrors(const GPUTPCGMMerger* merger, int iSlice, float maxSinPhi, float sinPhiMargin) { float lastX; - if (merger->Param().earlyTpcTransform && !merger->Param().rec.mergerReadFromTrackerDirectly) { + if (merger->Param().par.earlyTpcTransform && !merger->Param().rec.mergerReadFromTrackerDirectly) { lastX = mOrigTrack->OutTrackCluster(mOrigTrack->NHits() - 1).GetX(); // TODO: Why is this needed, Row2X should work, but looses some tracks } else { //float lastX = merger->Param().tpcGeometry.Row2X(mOrigTrack->Cluster(mOrigTrack->NClusters() - 1).GetRow()); // TODO: again, why does this reduce efficiency? @@ -120,7 +117,7 @@ GPUd() bool GPUTPCGMSliceTrack::FilterErrors(const GPUTPCGMMerger* merger, int i const int N = 3; - float bz = -merger->Param().ConstBz; + float bz = -merger->Param().par.ConstBz; float k = mParam.mQPt * bz; float dx = (1.f / N) * (lastX - mParam.mX); @@ -324,10 +321,10 @@ GPUd() bool GPUTPCGMSliceTrack::TransportToX(GPUTPCGMMerger* merger, float x, fl b.SetPar(2, ey1); b.SetPar(3, mParam.mDzDs); b.SetPar(4, mParam.mQPt); - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { b.SetZOffsetLinear(mTZOffset); } else { - b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(mSlice, mTZOffset, merger->Param().continuousMaxTimeBin)); + b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(mSlice, mTZOffset, merger->Param().par.continuousMaxTimeBin)); } if (!doCov) { @@ -480,10 +477,10 @@ GPUd() bool GPUTPCGMSliceTrack::TransportToXAlpha(GPUTPCGMMerger* merger, float b.SetPar(2, ey1); b.SetPar(3, dzds); b.SetPar(4, qpt); - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { b.SetZOffsetLinear(mTZOffset); } else { - b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(mSlice, mTZOffset, merger->Param().continuousMaxTimeBin)); + b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(mSlice, mTZOffset, merger->Param().par.continuousMaxTimeBin)); } b.SetCov(0, c00 + h2 * h2c22 + h4 * h4c44 + 2.f * (h2 * c20ph4c42 + h4 * c40)); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index b61c55d0eed52..024981340509d 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -32,6 +32,7 @@ #include "TPCFastTransform.h" #include "GPUTPCConvertImpl.h" #include "GPUTPCGMMergerTypes.h" +#include "GPUParam.inc" #ifdef GPUCA_ALIROOT_LIB #include "AliExternalTrackParam.h" @@ -42,7 +43,7 @@ #include "AliHLTTPCClusterMCData.h" #endif -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cmath> #include <cstdlib> #endif @@ -61,11 +62,11 @@ GPUd() bool GPUTPCGMTrackParam::Fit(const GPUTPCGMMerger* GPUrestrict() merger, GPUdEdx dEdx; GPUTPCGMPropagator prop; - GPUTPCGMMergerTypes::InterpolationErrors interpolation; + gputpcgmmergertypes::InterpolationErrors interpolation; prop.SetMaterialTPC(); prop.SetPolynomialField(&merger->Param().polynomialField); prop.SetMaxSinPhi(maxSinPhi); - prop.SetToyMCEventsFlag(param.ToyMCEventsFlag); + prop.SetToyMCEventsFlag(param.par.ToyMCEventsFlag); if ((clusters[0].slice < 18) == (clusters[N - 1].slice < 18)) { ShiftZ2(clusters, clustersXYZ, merger, N); } @@ -142,7 +143,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(const GPUTPCGMMerger* GPUrestrict() merger, prop.SetStatErrorCurCluster(&clusters[ihit]); float xx, yy, zz; - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { const float zOffset = (clusters[ihit].slice < 18) == (clusters[0].slice < 18) ? mTZOffset : -mTZOffset; xx = clustersXYZ[ihit].x; yy = clustersXYZ[ihit].y; @@ -172,7 +173,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(const GPUTPCGMMerger* GPUrestrict() merger, if (tryFollow) { const GPUTPCGMTrackParam backup = *this; const float backupAlpha = prop.GetAlpha(); - if (FollowCircle<0>(merger, prop, lastSlice, lastRow, iTrk, clusters[ihit].leg == clusters[maxN - 1].leg, clAlpha, xx, yy, clusters[ihit].slice, clusters[ihit].row, inFlyDirection)) { + if (FollowCircle<0>(merger, prop, lastSlice, lastRow, iTrk, clAlpha, xx, yy, clusters[ihit].slice, clusters[ihit].row, inFlyDirection)) { CADEBUG(printf("Error during follow circle, resetting track!\n")); *this = backup; prop.SetTrack(this, backupAlpha); @@ -310,6 +311,8 @@ GPUd() bool GPUTPCGMTrackParam::Fit(const GPUTPCGMMerger* GPUrestrict() merger, } else if (retVal == 2) { // cluster far away form the track if (allowModification) { MarkClusters(clusters, ihitMergeFirst, ihit, wayDirection, GPUTPCGMMergedTrackHit::flagRejectDistance); + } else if (iWay == nWays - 1) { + MarkClusters(clusters, ihitMergeFirst, ihit, wayDirection, GPUTPCGMMergedTrackHit::flagRejectErr); } nMissed++; nMissed2++; @@ -323,15 +326,23 @@ GPUd() bool GPUTPCGMTrackParam::Fit(const GPUTPCGMMerger* GPUrestrict() merger, } ConstrainSinPhi(); - bool ok = N + NTolerated >= GPUCA_TRACKLET_SELECTOR_MIN_HITS(mP[4]) && CheckNumericalQuality(covYYUpd); - if (!ok) { - return (false); + if (!(N + NTolerated >= GPUCA_TRACKLET_SELECTOR_MIN_HITS(mP[4]) && 2 * NTolerated <= CAMath::Max(10, N) && CheckNumericalQuality(covYYUpd))) { + return (false); // TODO: NTolerated should never become that large, check what is going wrong! } + // TODO: we have looping tracks here with 0 accepted clusters in the primary leg. In that case we should refit the track using only the primary leg. if (dEdxOut) { dEdx.computedEdx(*dEdxOut, param); } Alpha = prop.GetAlpha(); + MoveToReference(prop, param, Alpha); + NormalizeAlpha(Alpha); + + return (true); +} + +GPUd() void GPUTPCGMTrackParam::MoveToReference(GPUTPCGMPropagator& prop, const GPUParam& param, float& Alpha) +{ if (param.rec.TrackReferenceX <= 500) { for (int k = 0; k < 3; k++) // max 3 attempts { @@ -354,13 +365,6 @@ GPUd() bool GPUTPCGMTrackParam::Fit(const GPUTPCGMMerger* GPUrestrict() merger, ConstrainSinPhi(); Alpha += dAngle; } - if (Alpha > M_PI) { - Alpha -= 2 * M_PI; - } else if (Alpha <= -M_PI) { - Alpha += 2 * M_PI; - } - - return (ok); } GPUd() void GPUTPCGMTrackParam::MirrorTo(GPUTPCGMPropagator& GPUrestrict() prop, float toY, float toZ, bool inFlyDirection, const GPUParam& param, unsigned char row, unsigned char clusterState, bool mirrorParameters) @@ -410,7 +414,7 @@ GPUd() int GPUTPCGMTrackParam::MergeDoubleRowClusters(int& ihit, int wayDirectio clusterState = 0; while (true) { float clx, cly, clz, clamp; - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { const float zOffset = (clusters[ihit].slice < 18) == (clusters[0].slice < 18) ? mTZOffset : -mTZOffset; clx = clustersXYZ[ihit].x; cly = clustersXYZ[ihit].y; @@ -456,7 +460,7 @@ GPUd() int GPUTPCGMTrackParam::MergeDoubleRowClusters(int& ihit, int wayDirectio GPUd() void GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int iRow, int iTrack, bool goodLeg, GPUTPCGMPropagator& prop) { float Y, Z; - if (Merger->Param().earlyTpcTransform) { + if (Merger->Param().par.earlyTpcTransform) { Y = mP[0]; Z = mP[1]; } else { @@ -485,30 +489,37 @@ GPUd() void GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestrict return; } - const float zOffset = Merger->Param().earlyTpcTransform ? ((Merger->OutputTracks()[iTrack].CSide() ^ (slice >= 18)) ? -mTZOffset : mTZOffset) : Merger->GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(slice, mTZOffset, Merger->Param().continuousMaxTimeBin); + const float zOffset = Merger->Param().par.earlyTpcTransform ? ((Merger->OutputTracks()[iTrack].CSide() ^ (slice >= 18)) ? -mTZOffset : mTZOffset) : Merger->GetConstantMem()->calibObjects.fastTransform->convVertexTimeToZOffset(slice, mTZOffset, Merger->Param().par.continuousMaxTimeBin); const float y0 = row.Grid().YMin(); const float stepY = row.HstepY(); const float z0 = row.Grid().ZMin() - zOffset; // We can use our own ZOffset, since this is only used temporarily anyway const float stepZ = row.HstepZ(); int bin, ny, nz; - const float tube = 2.5f; + + float err2Y, err2Z; + Merger->Param().GetClusterErrors2(iRow, Z, mP[2], mP[3], err2Y, err2Z); + const float sy2 = CAMath::Min(Merger->Param().rec.tpcTubeMaxSize2, Merger->Param().rec.tpcTubeChi2 * (err2Y + CAMath::Abs(mC[0]))); // Cov can be bogus when following circle + const float sz2 = CAMath::Min(Merger->Param().rec.tpcTubeMaxSize2, Merger->Param().rec.tpcTubeChi2 * (err2Z + CAMath::Abs(mC[2]))); // In that case we should provide the track error externally + const float tubeY = CAMath::Sqrt(sy2); + const float tubeZ = CAMath::Sqrt(sz2); + const float sy21 = 1.f / sy2; + const float sz21 = 1.f / sz2; float nY, nZ; - if (Merger->Param().earlyTpcTransform) { + if (Merger->Param().par.earlyTpcTransform) { nY = Y; nZ = Z; } else { Merger->GetConstantMem()->calibObjects.fastTransform->InverseTransformYZtoNominalYZ(slice, iRow, Y, Z, nY, nZ); } - row.Grid().GetBinArea(nY, nZ + zOffset, tube, tube, bin, ny, nz); - float sy2 = tube * tube, sz2 = tube * tube; + row.Grid().GetBinArea(nY, nZ + zOffset, tubeY, tubeZ, bin, ny, nz); const int nBinsY = row.Grid().Ny(); const int idOffset = tracker.Data().ClusterIdOffset(); const int* ids = &(tracker.Data().ClusterDataIndex()[row.HitNumberOffset()]); - unsigned int myWeight = Merger->TrackOrderAttach()[iTrack] | GPUTPCGMMergerTypes::attachAttached | GPUTPCGMMergerTypes::attachTube; + unsigned int myWeight = Merger->TrackOrderAttach()[iTrack] | gputpcgmmergertypes::attachAttached | gputpcgmmergertypes::attachTube; GPUAtomic(unsigned int)* const weights = Merger->ClusterAttachment(); if (goodLeg) { - myWeight |= GPUTPCGMMergerTypes::attachGoodLeg; + myWeight |= gputpcgmmergertypes::attachGoodLeg; } for (int k = 0; k <= nz; k++) { const int mybin = bin + k * nBinsY; @@ -528,7 +539,7 @@ GPUd() void GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestrict const float z = z0 + hh.y * stepZ; const float dy = y - nY; const float dz = z - nZ; - if (dy * dy < sy2 && dz * dz < sz2) { + if (dy * dy * sy21 + dz * dz * sz21 <= CAMath::Sqrt(2.f)) { // CADEBUG(printf("Found Y %f Z %f\n", y, z)); CAMath::AtomicMax(weight, myWeight); } @@ -566,7 +577,7 @@ GPUd() bool GPUTPCGMTrackParam::FollowCircleChk(float lrFactor, float toY, float (up ? (-mP[0] * lrFactor > toX || (right ^ (mP[2] > 0))) : (-mP[0] * lrFactor < toX || (right ^ (mP[2] < 0)))); // don't overshoot in X } -GPUdni() void GPUTPCGMTrackParam::StoreAttachMirror(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int iRow, int iTrack, bool goodLeg, float toAlpha, float toY, float toX, int toSlice, int toRow, bool inFlyDirection, float alpha) +GPUdic(0, 1) void GPUTPCGMTrackParam::StoreAttachMirror(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int iRow, int iTrack, float toAlpha, float toY, float toX, int toSlice, int toRow, bool inFlyDirection, float alpha) { unsigned int nLoopData = CAMath::AtomicAdd(&Merger->Memory()->nLoopData, 1u); if (nLoopData >= Merger->NMaxTracks()) { @@ -578,7 +589,6 @@ GPUdni() void GPUTPCGMTrackParam::StoreAttachMirror(const GPUTPCGMMerger* GPUres data.param = *this; data.alpha = alpha; data.track = iTrack; - data.goodLeg = goodLeg; data.toAlpha = toAlpha; data.toY = toY; data.toX = toX; @@ -590,13 +600,13 @@ GPUdni() void GPUTPCGMTrackParam::StoreAttachMirror(const GPUTPCGMMerger* GPUres Merger->LoopData()[nLoopData] = data; } -GPUd() void GPUTPCGMTrackParam::RefitLoop(const GPUTPCGMMerger* GPUrestrict() Merger, int loopIdx) +GPUdii() void GPUTPCGMTrackParam::RefitLoop(const GPUTPCGMMerger* GPUrestrict() Merger, int loopIdx) { GPUTPCGMPropagator prop; prop.SetMaterialTPC(); prop.SetPolynomialField(&Merger->Param().polynomialField); prop.SetMaxSinPhi(GPUCA_MAX_SIN_PHI); - prop.SetToyMCEventsFlag(Merger->Param().ToyMCEventsFlag); + prop.SetToyMCEventsFlag(Merger->Param().par.ToyMCEventsFlag); prop.SetMatLUT(Merger->Param().rec.useMatLUT ? Merger->GetConstantMem()->calibObjects.matLUT : nullptr); prop.SetSeedingErrors(false); prop.SetFitInProjections(true); @@ -607,18 +617,18 @@ GPUd() void GPUTPCGMTrackParam::RefitLoop(const GPUTPCGMMerger* GPUrestrict() Me if (data.toRow == 0) { data.param.AttachClustersMirror<1>(Merger, data.slice, data.row, data.track, data.toY, prop, true); } else { - data.param.FollowCircle<1>(Merger, prop, data.slice, data.row, data.track, data.goodLeg, data.toAlpha, data.toY, data.toX, data.toSlice, data.toRow, data.inFlyDirection, true); + data.param.FollowCircle<1>(Merger, prop, data.slice, data.row, data.track, data.toAlpha, data.toY, data.toX, data.toSlice, data.toRow, data.inFlyDirection, true); } } template <int I> -GPUdni() int GPUTPCGMTrackParam::FollowCircle(const GPUTPCGMMerger* GPUrestrict() Merger, GPUTPCGMPropagator& GPUrestrict() prop, int slice, int iRow, int iTrack, bool goodLeg, float toAlpha, float toX, float toY, int toSlice, int toRow, bool inFlyDirection, bool phase2) +GPUdic(0, 1) int GPUTPCGMTrackParam::FollowCircle(const GPUTPCGMMerger* GPUrestrict() Merger, GPUTPCGMPropagator& GPUrestrict() prop, int slice, int iRow, int iTrack, float toAlpha, float toX, float toY, int toSlice, int toRow, bool inFlyDirection, bool phase2) { if (Merger->Param().rec.DisableRefitAttachment & 4) { return 1; } if (Merger->Param().rec.loopInterpolationInExtraPass && phase2 == false) { - StoreAttachMirror(Merger, slice, iRow, iTrack, goodLeg, toAlpha, toY, toX, toSlice, toRow, inFlyDirection, prop.GetAlpha()); + StoreAttachMirror(Merger, slice, iRow, iTrack, toAlpha, toY, toX, toSlice, toRow, inFlyDirection, prop.GetAlpha()); return 1; } const GPUParam& GPUrestrict() param = Merger->Param(); @@ -636,7 +646,7 @@ GPUdni() int GPUTPCGMTrackParam::FollowCircle(const GPUTPCGMMerger* GPUrestrict( CADEBUG(printf("CIRCLE Track %d: Slice %d Alpha %f X %f Y %f Z %f SinPhi %f DzDs %f - Next hit: Slice %d Alpha %f X %f Y %f - Right %d Up %d dAlpha %f lrFactor %f\n", iTrack, slice, prop.GetAlpha(), mX, mP[0], mP[1], mP[2], mP[3], toSlice, toAlpha, toX, toY, (int)right, (int)up, dAlpha, lrFactor)); // clang-format on - AttachClustersPropagate(Merger, slice, iRow, targetRow, iTrack, goodLeg, prop, inFlyDirection, 0.7f); + AttachClustersPropagate(Merger, slice, iRow, targetRow, iTrack, false, prop, inFlyDirection, 0.7f); if (prop.RotateToAlpha(prop.GetAlpha() + (M_PI / 2.f) * lrFactor)) { return 1; } @@ -707,7 +717,7 @@ GPUdni() int GPUTPCGMTrackParam::FollowCircle(const GPUTPCGMMerger* GPUrestrict( } } prop.PropagateToXAlpha(Merger->Param().tpcGeometry.Row2X(iRow) + dx, prop.GetAlpha(), inFlyDirection); - AttachClustersPropagate(Merger, slice, iRow, toRow, iTrack, !goodLeg, prop, inFlyDirection); + AttachClustersPropagate(Merger, slice, iRow, toRow, iTrack, false, prop, inFlyDirection); } if (prop.PropagateToXAlpha(toX, prop.GetAlpha(), inFlyDirection)) { mX = toX; @@ -723,7 +733,7 @@ GPUdni() void GPUTPCGMTrackParam::AttachClustersMirror(const GPUTPCGMMerger* GPU return; } if (Merger->Param().rec.loopInterpolationInExtraPass && phase2 == false) { - StoreAttachMirror(Merger, slice, iRow, iTrack, false, 0, toY, 0, 0, 0, 0, prop.GetAlpha()); + StoreAttachMirror(Merger, slice, iRow, iTrack, 0, toY, 0, 0, 0, 0, prop.GetAlpha()); return; } float X = mP[2] > 0 ? mP[0] : -mP[0]; @@ -786,7 +796,7 @@ GPUdni() void GPUTPCGMTrackParam::AttachClustersMirror(const GPUTPCGMMerger* GPU GPUd() void GPUTPCGMTrackParam::ShiftZ2(const GPUTPCGMMergedTrackHit* clusters, GPUTPCGMMergedTrackHitXYZ* clustersXYZ, const GPUTPCGMMerger* merger, int N) { float tzInner, tzOuter; - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { tzInner = clustersXYZ[N - 1].z; tzOuter = clustersXYZ[0].z; } else { @@ -799,14 +809,14 @@ GPUd() void GPUTPCGMTrackParam::ShiftZ2(const GPUTPCGMMergedTrackHit* clusters, GPUd() void GPUTPCGMTrackParam::ShiftZ(const GPUTPCGMMerger* GPUrestrict() merger, int slice, float tz1, float tz2) { - if (!merger->Param().ContinuousTracking) { + if (!merger->Param().par.ContinuousTracking) { return; } const float cosPhi = CAMath::Abs(mP[2]) < 1.f ? CAMath::Sqrt(1 - mP[2] * mP[2]) : 0.f; const float dxf = -CAMath::Abs(mP[2]); const float dyf = cosPhi * (mP[2] > 0 ? 1.f : -1.f); const float r1 = CAMath::Abs(mP[4] * merger->Param().polynomialField.GetNominalBz()); - const float r = r1 > 0.0001 ? (1.f / CAMath::Abs(r1)) : 10000; + const float r = r1 > 0.0001 ? (1.f / r1) : 10000; float xp = mX + dxf * r; float yp = mP[0] + dyf * r; // printf("X %f Y %f SinPhi %f QPt %f R %f --> XP %f YP %f\n", mX, mP[0], mP[2], mP[4], r, xp, yp); @@ -824,7 +834,7 @@ GPUd() void GPUTPCGMTrackParam::ShiftZ(const GPUTPCGMMerger* GPUrestrict() merge z0 = z0 > 0 ? 250.f : -250.f; } float deltaZ = mP[1] - z0; - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { mTZOffset += deltaZ; mP[1] -= deltaZ; deltaZ = 0; @@ -972,7 +982,7 @@ GPUd() void GPUTPCGMTrackParam::RefitTrack(GPUTPCGMMergedTrack& GPUrestrict() tr GPUTPCGMTrackParam t = track.Param(); float Alpha = track.Alpha(); CADEBUG(int nTrackHitsOld = nTrackHits; float ptOld = t.QPt()); - bool ok = t.Fit(merger, iTrk, merger->Clusters() + track.FirstClusterRef(), merger->ClustersXYZ() + track.FirstClusterRef(), nTrackHits, NTolerated, Alpha, attempt, GPUCA_MAX_SIN_PHI, &track.OuterParam(), merger->Param().dodEdx ? &track.dEdxInfo() : nullptr); + bool ok = t.Fit(merger, iTrk, merger->Clusters() + track.FirstClusterRef(), merger->ClustersXYZ() + track.FirstClusterRef(), nTrackHits, NTolerated, Alpha, attempt, GPUCA_MAX_SIN_PHI, &track.OuterParam(), merger->Param().par.dodEdx ? &track.dEdxInfo() : nullptr); CADEBUG(printf("Finished Fit Track %d\n", iTrk)); CADEBUG(printf("OUTPUT hits %d -> %d+%d = %d, QPt %f -> %f, SP %f, ok %d chi2 %f chi2ndf %f\n", nTrackHitsOld, nTrackHits, NTolerated, nTrackHits + NTolerated, ptOld, t.QPt(), t.SinPhi(), (int)ok, t.Chi2(), t.Chi2() / CAMath::Max(1, nTrackHits))); @@ -986,7 +996,7 @@ GPUd() void GPUTPCGMTrackParam::RefitTrack(GPUTPCGMMergedTrack& GPUrestrict() tr NTolerated = 0; // Clusters not fit but tollerated for track length cut t = track.Param(); Alpha = track.Alpha(); - ok = t.Fit(merger, iTrk, merger->Clusters() + track.FirstClusterRef(), merger->ClustersXYZ() + track.FirstClusterRef(), nTrackHits, NTolerated, Alpha, 1, GPUCA_MAX_SIN_PHI, &track.OuterParam(), merger->Param().dodEdx ? &track.dEdxInfo() : nullptr); + ok = t.Fit(merger, iTrk, merger->Clusters() + track.FirstClusterRef(), merger->ClustersXYZ() + track.FirstClusterRef(), nTrackHits, NTolerated, Alpha, 1, GPUCA_MAX_SIN_PHI, &track.OuterParam(), merger->Param().par.dodEdx ? &track.dEdxInfo() : nullptr); } else { unsigned int nRefit = CAMath::AtomicAdd(&merger->Memory()->nRetryRefit, 1u); merger->RetryRefitIds()[nRefit] = iTrk; @@ -1007,7 +1017,7 @@ GPUd() void GPUTPCGMTrackParam::RefitTrack(GPUTPCGMMergedTrack& GPUrestrict() tr const GPUParam& GPUrestrict() param = merger->Param(); float alphaa = param.Alpha(merger->Clusters()[ind].slice); float xx, yy, zz; - if (merger->Param().earlyTpcTransform) { + if (merger->Param().par.earlyTpcTransform) { xx = merger->ClustersXYZ()[ind].x; yy = merger->ClustersXYZ()[ind].y; zz = merger->ClustersXYZ()[ind].z - track.Param().GetTZOffset(); @@ -1015,8 +1025,8 @@ GPUd() void GPUTPCGMTrackParam::RefitTrack(GPUTPCGMMergedTrack& GPUrestrict() tr const ClusterNative& GPUrestrict() cl = merger->GetConstantMem()->ioPtrs.clustersNative->clustersLinear[merger->Clusters()[ind].num]; merger->GetConstantMem()->calibObjects.fastTransform->Transform(merger->Clusters()[ind].slice, merger->Clusters()[ind].row, cl.getPad(), cl.getTime(), xx, yy, zz, track.Param().GetTZOffset()); } - float sinA = CAMath::Sin(alphaa - track.Alpha()); - float cosA = CAMath::Cos(alphaa - track.Alpha()); + float sinA, cosA; + CAMath::SinCos(alphaa - track.Alpha(), sinA, cosA); track.SetLastX(xx * cosA - yy * sinA); track.SetLastY(xx * sinA + yy * cosA); track.SetLastZ(zz); @@ -1025,8 +1035,8 @@ GPUd() void GPUTPCGMTrackParam::RefitTrack(GPUTPCGMMergedTrack& GPUrestrict() tr GPUd() bool GPUTPCGMTrackParam::Rotate(float alpha) { - float cA = CAMath::Cos(alpha); - float sA = CAMath::Sin(alpha); + float cA, sA; + CAMath::SinCos(alpha, sA, cA); float x0 = mX; float sinPhi0 = mP[2], cosPhi0 = CAMath::Sqrt(1 - mP[2] * mP[2]); float cosPhi = cosPhi0 * cA + sinPhi0 * sA; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h index 233231d2618d5..dd7706547f8fd 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h @@ -19,7 +19,7 @@ #include "GPUCommonMath.h" #include "GPUdEdxInfo.h" -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <cstddef> #endif @@ -148,6 +148,7 @@ class GPUTPCGMTrackParam GPUd() bool CheckCov() const; GPUd() bool Fit(const GPUTPCGMMerger* merger, int iTrk, GPUTPCGMMergedTrackHit* clusters, GPUTPCGMMergedTrackHitXYZ* clustersXYZ, int& N, int& NTolerated, float& Alpha, int attempt = 0, float maxSinPhi = GPUCA_MAX_SIN_PHI, GPUTPCOuterParam* outerParam = nullptr, GPUdEdxInfo* dEdxOut = nullptr); + GPUd() void MoveToReference(GPUTPCGMPropagator& prop, const GPUParam& param, float& alpha); GPUd() void MirrorTo(GPUTPCGMPropagator& prop, float toY, float toZ, bool inFlyDirection, const GPUParam& param, unsigned char row, unsigned char clusterState, bool mirrorParameters); GPUd() int MergeDoubleRowClusters(int& ihit, int wayDirection, GPUTPCGMMergedTrackHit* clusters, GPUTPCGMMergedTrackHitXYZ* clustersXYZ, const GPUTPCGMMerger* merger, GPUTPCGMPropagator& prop, float& xx, float& yy, float& zz, int maxN, float clAlpha, unsigned char& clusterState, bool rejectChi2); @@ -158,8 +159,8 @@ class GPUTPCGMTrackParam template <int I> GPUd() void AttachClustersMirror(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int iRow, int iTrack, float toY, GPUTPCGMPropagator& prop, bool phase2 = false); template <int I> - GPUd() int FollowCircle(const GPUTPCGMMerger* GPUrestrict() Merger, GPUTPCGMPropagator& prop, int slice, int iRow, int iTrack, bool goodLeg, float toAlpha, float toX, float toY, int toSlice, int toRow, bool inFlyDirection, bool phase2 = false); - GPUd() void StoreAttachMirror(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int iRow, int iTrack, bool goodLeg, float toAlpha, float toY, float toX, int toSlice, int toRow, bool inFlyDirection, float alpha); + GPUd() int FollowCircle(const GPUTPCGMMerger* GPUrestrict() Merger, GPUTPCGMPropagator& prop, int slice, int iRow, int iTrack, float toAlpha, float toX, float toY, int toSlice, int toRow, bool inFlyDirection, bool phase2 = false); + GPUd() void StoreAttachMirror(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int iRow, int iTrack, float toAlpha, float toY, float toX, int toSlice, int toRow, bool inFlyDirection, float alpha); GPUd() static void RefitLoop(const GPUTPCGMMerger* GPUrestrict() Merger, int loopIdx); GPUdi() void MarkClusters(GPUTPCGMMergedTrackHit* GPUrestrict() clusters, int ihitFirst, int ihitLast, int wayDirection, unsigned char state) @@ -178,6 +179,14 @@ class GPUTPCGMTrackParam clusters[ihitFirst].state &= ~state; } } + GPUdi() static void NormalizeAlpha(float& alpha) + { + if (alpha > M_PI) { + alpha -= 2 * M_PI; + } else if (alpha <= -M_PI) { + alpha += 2 * M_PI; + } + } GPUd() bool Rotate(float alpha); GPUd() void ShiftZ(const GPUTPCGMMerger* merger, int slice, float tzInner, float tzOuter); @@ -238,7 +247,6 @@ struct GPUTPCGMLoopData { unsigned char toSlice; unsigned char toRow; unsigned char inFlyDirection; - unsigned char goodLeg; }; GPUdi() int GPUTPCGMTrackParam::initResetT0() diff --git a/GPU/GPUTracking/Merger/GPUTPCGlobalMergerComponent.cxx b/GPU/GPUTracking/Merger/GPUTPCGlobalMergerComponent.cxx index c9ecc673658b9..773f66f3dd20b 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGlobalMergerComponent.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGlobalMergerComponent.cxx @@ -120,7 +120,7 @@ void GPUTPCGlobalMergerComponent::SetDefaultConfiguration() fSolenoidBz = -5.00668; fClusterErrorCorrectionY = 0; - fClusterErrorCorrectionZ = 1.1; + fClusterErrorCorrectionZ = 0; fNWays = 1; fNWaysOuter = 0; fNoClear = false; @@ -292,7 +292,7 @@ int GPUTPCGlobalMergerComponent::Configure(const char* cdbEntry, const char* cha GPUSettingsEvent ev; GPUSettingsRec rec; - GPUSettingsDeviceProcessing devProc; + GPUSettingsProcessing devProc; ev.solenoidBz = fSolenoidBz; if (fClusterErrorCorrectionY > 1.e-4) { rec.ClusterError2CorrectionY = fClusterErrorCorrectionY * fClusterErrorCorrectionY; @@ -305,6 +305,8 @@ int GPUTPCGlobalMergerComponent::Configure(const char* cdbEntry, const char* cha rec.mergerInterpolateErrors = false; rec.NonConsecutiveIDs = true; rec.mergerReadFromTrackerDirectly = false; + devProc.ompThreads = 1; + devProc.ompKernels = false; GPURecoStepConfiguration steps; steps.steps.set(GPUDataTypes::RecoStep::TPCMerging); diff --git a/GPU/GPUTracking/Merger/macros/checkPropagation.C b/GPU/GPUTracking/Merger/macros/checkPropagation.C index c4c60b472a58a..7c4315e8a04d6 100644 --- a/GPU/GPUTracking/Merger/macros/checkPropagation.C +++ b/GPU/GPUTracking/Merger/macros/checkPropagation.C @@ -58,7 +58,7 @@ int RecalculateSlice(GPUTPCGMPhysicalTrackModel& t, AliExternalTrackParam& t0, i int checkPropagation() { - // gSystem->Load("libAliHLTTPC.so"); + // gSystem->Load("libAliHLTTPC"); TH1F* hDiff[3] = {0, 0, 0}; diff --git a/GPU/GPUTracking/Merger/macros/fitPolynomialFieldIts.C b/GPU/GPUTracking/Merger/macros/fitPolynomialFieldIts.C index 4b42316c9ab41..a61004ce6f828 100644 --- a/GPU/GPUTracking/Merger/macros/fitPolynomialFieldIts.C +++ b/GPU/GPUTracking/Merger/macros/fitPolynomialFieldIts.C @@ -1,6 +1,6 @@ int fitPolynomialFieldIts() { - gSystem->Load("libAliHLTTPC.so"); + gSystem->Load("libAliHLTTPC"); GPUTPCGMPolynomialField polyField; AliMagF* fld = new AliMagF("Fit", "Fit", 1., 1., AliMagF::k5kG); GPUTPCGMPolynomialFieldManager::FitFieldIts(fld, polyField, 1.); diff --git a/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTpc.C b/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTpc.C index 74e2824215659..7b1424163a6c6 100644 --- a/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTpc.C +++ b/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTpc.C @@ -1,6 +1,6 @@ int fitPolynomialFieldTpc() { - gSystem->Load("libAliHLTTPC.so"); + gSystem->Load("libAliHLTTPC"); GPUTPCGMPolynomialField polyField; AliMagF* fld = new AliMagF("Fit", "Fit", 1., 1., AliMagF::k5kG); GPUTPCGMPolynomialFieldManager::FitFieldTpc(fld, polyField, 10); diff --git a/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTrd.C b/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTrd.C index 03ca5caf05f22..debfba0c34cf0 100644 --- a/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTrd.C +++ b/GPU/GPUTracking/Merger/macros/fitPolynomialFieldTrd.C @@ -1,6 +1,6 @@ int fitPolynomialFieldTrd() { - gSystem->Load("libAliHLTTPC.so"); + gSystem->Load("libAliHLTTPC"); GPUTPCGMPolynomialField polyField; AliMagF* fld = new AliMagF("Fit", "Fit", 1., 1., AliMagF::k5kG); GPUTPCGMPolynomialFieldManager::FitFieldTrd(fld, polyField, 2); diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx b/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx new file mode 100644 index 0000000000000..809bc6929f16e --- /dev/null +++ b/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx @@ -0,0 +1,427 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTrackingRefit.cxx +/// \author David Rohr + +#define GPUCA_CADEBUG 0 + +#include "GPUTrackingRefit.h" +#include "GPUO2DataTypes.h" +#include "GPUTPCGMTrackParam.h" +#include "GPUTPCGMMergedTrack.h" +#include "GPUTPCGMPropagator.h" +#include "GPUConstantMem.h" +#include "TPCFastTransform.h" +#include "ReconstructionDataFormats/Track.h" +#include "DetectorsBase/Propagator.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "GPUParam.inc" +#include "GPUCommonArray.h" +#include "GPUParam.h" + +using namespace GPUCA_NAMESPACE::gpu; +using namespace o2::track; +using namespace o2::base; +using namespace o2::tpc; + +static constexpr int kIGNORE_ENDS = 3; + +#define IgnoreErrors(SNP) \ + if (mIgnoreErrorsOnTrackEnds) { \ + if (abs(i - stop) <= kIGNORE_ENDS && (CAMath::Abs(SNP) >= Propagator::MAX_SIN_PHI || abs(start - stop) < 30)) { \ + break; \ + } \ + if (abs(i - start) <= kIGNORE_ENDS && (CAMath::Abs(SNP) >= Propagator::MAX_SIN_PHI || abs(start - stop) < 30)) { \ + continue; \ + } \ + } +// End IgnoreErrors + +#ifndef GPUCA_GPUCODE +void GPUTrackingRefitProcessor::InitializeProcessor() {} + +void GPUTrackingRefitProcessor::RegisterMemoryAllocation() +{ + AllocateAndInitializeLate(); +} + +void GPUTrackingRefitProcessor::SetMaxData(const GPUTrackingInOutPointers& io) +{ +} +#endif + +namespace +{ +template <class T> +struct refitTrackTypes; +template <> +struct refitTrackTypes<GPUTPCGMTrackParam> { + using propagator = GPUTPCGMPropagator; +}; +template <> +struct refitTrackTypes<TrackParCov> { + using propagator = const Propagator*; +}; +} // anonymous namespace + +template <> +GPUd() void GPUTrackingRefit::initProp<GPUTPCGMPropagator>(GPUTPCGMPropagator& prop) +{ + prop.SetMaterialTPC(); + prop.SetMaxSinPhi(GPUCA_MAX_SIN_PHI); + prop.SetToyMCEventsFlag(false); + prop.SetSeedingErrors(false); + prop.SetFitInProjections(mPparam->rec.fitInProjections != 0); + prop.SetPropagateBzOnly(false); + prop.SetPolynomialField(&mPparam->polynomialField); + prop.SetMatLUT(mPmatLUT); +} + +template <> +GPUd() void GPUTrackingRefit::initProp<const Propagator*>(const Propagator*& prop) +{ + prop = mPpropagator; +} + +template <class T, class S, class U> +GPUd() void GPUTrackingRefit::convertTrack(T& trk, const S& trkX, U& prop, float* chi2) +{ + trk = trkX; +} +GPUd() static void convertTrackParam(GPUTPCGMTrackParam& trk, const TrackParCov& trkX) +{ + for (int i = 0; i < 5; i++) { + trk.Par()[i] = trkX.getParams()[i]; + } + for (int i = 0; i < 15; i++) { + trk.Cov()[i] = trkX.getCov()[i]; + } + trk.X() = trkX.getX(); +} +GPUd() static void convertTrackParam(TrackParCov& trk, const GPUTPCGMTrackParam& trkX) +{ + for (int i = 0; i < 5; i++) { + trk.setParam(trkX.GetPar()[i], i); + } + for (int i = 0; i < 15; i++) { + trk.setCov(trkX.GetCov()[i], i); + } + trk.setX(trkX.GetX()); +} +// Generic +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTPCGMTrackParam, TrackParCov, GPUTPCGMPropagator>(GPUTPCGMTrackParam& trk, const TrackParCov& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + convertTrackParam(trk, trkX); + prop.SetTrack(&trk, trkX.getAlpha()); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<TrackParCov, GPUTPCGMTrackParam, GPUTPCGMPropagator>(TrackParCov& trk, const GPUTPCGMTrackParam& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + convertTrackParam(trk, trkX); + trk.setAlpha(prop.GetAlpha()); +} +// GPUTPCGMMergedTrack input +template <> +GPUd() void GPUTrackingRefit::convertTrack<TrackParCov, GPUTPCGMMergedTrack, const Propagator*>(TrackParCov& trk, const GPUTPCGMMergedTrack& trkX, const Propagator*& prop, float* chi2) +{ + initProp(prop); + convertTrackParam(trk, trkX.GetParam()); + trk.setAlpha(trkX.GetAlpha()); + *chi2 = trkX.GetParam().GetChi2(); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTPCGMMergedTrack, TrackParCov, const Propagator*>(GPUTPCGMMergedTrack& trk, const TrackParCov& trkX, const Propagator*& prop, float* chi2) +{ + convertTrackParam(trk.Param(), trkX); + trk.SetAlpha(trkX.getAlpha()); + trk.Param().SetChi2(*chi2); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTPCGMTrackParam, GPUTPCGMMergedTrack, GPUTPCGMPropagator>(GPUTPCGMTrackParam& trk, const GPUTPCGMMergedTrack& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + initProp(prop); + trk = trkX.GetParam(); + prop.SetTrack(&trk, trkX.GetAlpha()); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTPCGMMergedTrack, GPUTPCGMTrackParam, GPUTPCGMPropagator>(GPUTPCGMMergedTrack& trk, const GPUTPCGMTrackParam& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + trk.SetParam(trkX); + trk.SetAlpha(prop.GetAlpha()); +} +// TrackTPC input +template <> +GPUd() void GPUTrackingRefit::convertTrack<TrackParCov, TrackTPC, const Propagator*>(TrackParCov& trk, const TrackTPC& trkX, const Propagator*& prop, float* chi2) +{ + initProp(prop); + convertTrack<TrackParCov, TrackParCov, const Propagator*>(trk, trkX, prop, nullptr); + *chi2 = trkX.getChi2(); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<TrackTPC, TrackParCov, const Propagator*>(TrackTPC& trk, const TrackParCov& trkX, const Propagator*& prop, float* chi2) +{ + convertTrack<TrackParCov, TrackParCov, const Propagator*>(trk, trkX, prop, nullptr); + trk.setChi2(*chi2); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTPCGMTrackParam, TrackTPC, GPUTPCGMPropagator>(GPUTPCGMTrackParam& trk, const TrackTPC& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + initProp(prop); + convertTrack<GPUTPCGMTrackParam, TrackParCov, GPUTPCGMPropagator>(trk, trkX, prop, nullptr); + trk.SetChi2(trkX.getChi2()); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<TrackTPC, GPUTPCGMTrackParam, GPUTPCGMPropagator>(TrackTPC& trk, const GPUTPCGMTrackParam& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + convertTrack<TrackParCov, GPUTPCGMTrackParam, GPUTPCGMPropagator>(trk, trkX, prop, nullptr); + trk.setChi2(trkX.GetChi2()); +} +// TrackParCovWithArgs input +template <> +GPUd() void GPUTrackingRefit::convertTrack<TrackParCov, GPUTrackingRefit::TrackParCovWithArgs, const Propagator*>(TrackParCov& trk, const GPUTrackingRefit::TrackParCovWithArgs& trkX, const Propagator*& prop, float* chi2) +{ + initProp(prop); + convertTrack<TrackParCov, TrackParCov, const Propagator*>(trk, trkX.trk, prop, nullptr); + *chi2 = trkX.chi2 ? *trkX.chi2 : 0.f; +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTrackingRefit::TrackParCovWithArgs, TrackParCov, const Propagator*>(GPUTrackingRefit::TrackParCovWithArgs& trk, const TrackParCov& trkX, const Propagator*& prop, float* chi2) +{ + convertTrack<TrackParCov, TrackParCov, const Propagator*>(trk.trk, trkX, prop, nullptr); + if (trk.chi2) { + *trk.chi2 = *chi2; + } +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTPCGMTrackParam, GPUTrackingRefit::TrackParCovWithArgs, GPUTPCGMPropagator>(GPUTPCGMTrackParam& trk, const GPUTrackingRefit::TrackParCovWithArgs& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + initProp(prop); + convertTrack<GPUTPCGMTrackParam, TrackParCov, GPUTPCGMPropagator>(trk, trkX.trk, prop, nullptr); + trk.SetChi2(trkX.chi2 ? *trkX.chi2 : 0.f); +} +template <> +GPUd() void GPUTrackingRefit::convertTrack<GPUTrackingRefit::TrackParCovWithArgs, GPUTPCGMTrackParam, GPUTPCGMPropagator>(GPUTrackingRefit::TrackParCovWithArgs& trk, const GPUTPCGMTrackParam& trkX, GPUTPCGMPropagator& prop, float* chi2) +{ + convertTrack<TrackParCov, GPUTPCGMTrackParam, GPUTPCGMPropagator>(trk.trk, trkX, prop, chi2); + if (trk.chi2) { + *trk.chi2 = trkX.GetChi2(); + } +} + +GPUd() static const float* getPar(const GPUTPCGMTrackParam& trk) { return trk.GetPar(); } +GPUd() static const float* getPar(const TrackParCov& trk) { return trk.getParams(); } + +template <class T, class S> +GPUd() int GPUTrackingRefit::RefitTrack(T& trkX, bool outward, bool resetCov) +{ +#ifndef __OPENCL__ + CADEBUG(int ii; printf("\nRefitting track\n")); + typename refitTrackTypes<S>::propagator prop; + S trk; + float TrackParCovChi2 = 0.f; + convertTrack(trk, trkX, prop, &TrackParCovChi2); + int begin = 0, count; + float tOffset; + if constexpr (std::is_same<T, GPUTPCGMMergedTrack>::value) { + count = trkX.NClusters(); + if (trkX.Looper()) { + int leg = mPtrackHits[trkX.FirstClusterRef() + trkX.NClusters() - 1].leg; + for (int i = trkX.NClusters() - 2; i > 0; i--) { + if (mPtrackHits[trkX.FirstClusterRef() + i].leg != leg) { + begin = i + 1; + break; + } + } + } + tOffset = trkX.GetParam().GetTZOffset(); + } else if constexpr (std::is_same<T, TrackTPC>::value) { + count = trkX.getNClusters(); + tOffset = trkX.getTime0(); + } else if constexpr (std::is_same<T, TrackParCovWithArgs>::value) { + count = trkX.clusRef.getEntries(); + tOffset = trkX.time0; + } else { + static_assert("Invalid template"); + } + int direction = outward ? -1 : 1; + int start = outward ? count - 1 : begin; + int stop = outward ? begin - 1 : count; + const ClusterNative* cl = nullptr; + uint8_t sector, row = 0, currentSector = 0, currentRow = 0; + short clusterState = 0, nextState; + int nFitted = 0; + for (int i = start; i != stop; i += cl ? 0 : direction) { + float x, y, z, charge = 0.f; + int clusters = 0; + while (true) { + if (!cl) { + CADEBUG(ii = i); + if constexpr (std::is_same<T, GPUTPCGMMergedTrack>::value) { + const auto& hit = mPtrackHits[trkX.FirstClusterRef() + i]; + cl = &mPclusterNative->clustersLinear[hit.num]; + if (hit.state & (GPUTPCGMMergedTrackHit::flagReject | GPUTPCGMMergedTrackHit::flagNotFit)) { + cl = nullptr; + if (i + direction != stop) { + i += direction; + continue; + } + break; + } + row = hit.row; + sector = hit.slice; + nextState = mPclusterState[hit.num]; + } else if constexpr (std::is_same<T, TrackTPC>::value) { + cl = &trkX.getCluster(mPtrackHitReferences, i, *mPclusterNative, sector, row); + nextState = mPclusterState[cl - mPclusterNative->clustersLinear]; + } else if constexpr (std::is_same<T, TrackParCovWithArgs>::value) { + cl = &TrackTPC::getCluster(mPtrackHitReferences, i, *mPclusterNative, sector, row, trkX.clusRef); + nextState = mPclusterState[cl - mPclusterNative->clustersLinear]; + } else { + static_assert("Invalid template"); + } + } + if (clusters == 0 || (row == currentRow && sector == currentSector)) { + if (clusters == 1) { + x *= charge; + y *= charge; + z *= charge; + } + if (clusters == 0) { + mPfastTransform->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tOffset); + CADEBUG(printf("\tHit %3d/%3d Row %3d: Cluster Alpha %8.3f %3d, X %8.3f - Y %8.3f, Z %8.3f\n", ii, count, row, mPparam->Alpha(sector), (int)sector, x, y, z)); + currentRow = row; + currentSector = sector; + charge = cl->qTot; + clusterState = nextState; + } else { + float xx, yy, zz; + mPfastTransform->Transform(sector, row, cl->getPad(), cl->getTime(), xx, yy, zz, tOffset); + CADEBUG(printf("\tHit %3d/%3d Row %3d: Cluster Alpha %8.3f %3d, X %8.3f - Y %8.3f, Z %8.3f\n", ii, count, row, mPparam->Alpha(sector), (int)sector, xx, yy, zz)); + x += xx * cl->qTot; + y += yy * cl->qTot; + z += zz * cl->qTot; + charge += cl->qTot; + clusterState |= nextState; + } + cl = nullptr; + clusters++; + if (i + direction != stop) { + i += direction; + continue; + } + } + break; + } + if (clusters == 0) { + continue; + } else if (clusters > 1) { + x /= charge; + y /= charge; + z /= charge; + CADEBUG(printf("\tMerged Hit Row %3d: Cluster Alpha %8.3f %3d, X %8.3f - Y %8.3f, Z %8.3f\n", row, mPparam->Alpha(sector), (int)sector, x, y, z)); + } + + if constexpr (std::is_same<S, GPUTPCGMTrackParam>::value) { + if (prop.PropagateToXAlpha(x, mPparam->Alpha(currentSector), !outward)) { + IgnoreErrors(trk.GetSinPhi()); + return -2; + } + if (resetCov) { + trk.ResetCovariance(); + } + CADEBUG(printf("\t%21sPropaga Alpha %8.3f , X %8.3f - Y %8.3f, Z %8.3f - QPt %7.2f (%7.2f), SP %5.2f (%5.2f) --- Res %8.3f %8.3f --- Cov sY %8.3f sZ %8.3f sSP %8.3f sPt %8.3f - YPt %8.3f\n", "", prop.GetAlpha(), x, trk.Par()[0], trk.Par()[1], trk.Par()[4], prop.GetQPt0(), trk.Par()[2], prop.GetSinPhi0(), trk.Par()[0] - y, trk.Par()[1] - z, sqrtf(trk.Cov()[0]), sqrtf(trk.Cov()[2]), sqrtf(trk.Cov()[5]), sqrtf(trk.Cov()[14]), trk.Cov()[10])); + if (prop.Update(y, z, row, *mPparam, clusterState, 0, nullptr, true)) { + IgnoreErrors(trk.GetSinPhi()); + return -3; + } + CADEBUG(printf("\t%21sFit Alpha %8.3f , X %8.3f - Y %8.3f, Z %8.3f - QPt %7.2f (%7.2f), SP %5.2f (%5.2f), DzDs %5.2f %16s --- Cov sY %8.3f sZ %8.3f sSP %8.3f sPt %8.3f - YPt %8.3f\n", "", prop.GetAlpha(), x, trk.Par()[0], trk.Par()[1], trk.Par()[4], prop.GetQPt0(), trk.Par()[2], prop.GetSinPhi0(), trk.Par()[3], "", sqrtf(trk.Cov()[0]), sqrtf(trk.Cov()[2]), sqrtf(trk.Cov()[5]), sqrtf(trk.Cov()[14]), trk.Cov()[10])); + } else if constexpr (std::is_same<S, TrackParCov>::value) { + if (!trk.rotate(mPparam->Alpha(currentSector))) { + IgnoreErrors(trk.getSnp()); + return -1; + } + if (!prop->PropagateToXBxByBz(trk, x, GPUCA_MAX_SIN_PHI_LOW)) { + IgnoreErrors(trk.getSnp()); + return -2; + } + if (resetCov) { + trk.resetCovariance(); + TrackParCovChi2 = 0.f; + } + CADEBUG(printf("\t%21sPropaga Alpha %8.3f , X %8.3f - Y %8.3f, Z %8.3f - QPt %7.2f (%7.2f), SP %5.2f (%5.2f) --- Res %8.3f %8.3f --- Cov sY %8.3f sZ %8.3f sSP %8.3f sPt %8.3f - YPt %8.3f\n", "", trk.getAlpha(), x, trk.getParams()[0], trk.getParams()[1], trk.getParams()[4], trk.getParams()[4], trk.getParams()[2], trk.getParams()[2], trk.getParams()[0] - y, trk.getParams()[1] - z, sqrtf(trk.getCov()[0]), sqrtf(trk.getCov()[2]), sqrtf(trk.getCov()[5]), sqrtf(trk.getCov()[14]), trk.getCov()[10])); + gpu::gpustd::array<float, 2> p = {y, z}; + gpu::gpustd::array<float, 3> c = {0, 0, 0}; + mPparam->GetClusterErrors2(currentRow, z, getPar(trk)[2], getPar(trk)[3], c[0], c[2]); + mPparam->UpdateClusterError2ByState(clusterState, c[0], c[2]); + TrackParCovChi2 += trk.getPredictedChi2(p, c); + if (!trk.update(p, c)) { + IgnoreErrors(trk.getSnp()); + return -3; + } + CADEBUG(printf("\t%21sFit Alpha %8.3f , X %8.3f - Y %8.3f, Z %8.3f - QPt %7.2f (%7.2f), SP %5.2f (%5.2f), DzDs %5.2f %16s --- Cov sY %8.3f sZ %8.3f sSP %8.3f sPt %8.3f - YPt %8.3f\n", "", trk.getAlpha(), x, trk.getParams()[0], trk.getParams()[1], trk.getParams()[4], trk.getParams()[4], trk.getParams()[2], trk.getParams()[2], trk.getParams()[3], "", sqrtf(trk.getCov()[0]), sqrtf(trk.getCov()[2]), sqrtf(trk.getCov()[5]), sqrtf(trk.getCov()[14]), trk.getCov()[10])); + } else { + static_assert("Invalid template"); + } + resetCov = false; + nFitted++; + } + if constexpr (std::is_same<S, GPUTPCGMTrackParam>::value) { + float alpha = prop.GetAlpha(); + trk.MoveToReference(prop, *mPparam, alpha); + trk.NormalizeAlpha(alpha); + prop.SetAlpha(alpha); + } else if constexpr (std::is_same<S, TrackParCov>::value) { + static constexpr float kDeg2Rad = M_PI / 180.f; + static constexpr float kSectAngle = 2 * M_PI / 18.f; + if (mPparam->rec.TrackReferenceX <= 500) { + if (prop->PropagateToXBxByBz(trk, mPparam->rec.TrackReferenceX)) { + if (CAMath::Abs(trk.getY()) > trk.getX() * CAMath::Tan(kSectAngle / 2.f)) { + float newAlpha = trk.getAlpha() + floor(CAMath::ATan2(trk.getY(), trk.getX()) / kDeg2Rad / 20.f + 0.5f) * kSectAngle; + GPUTPCGMTrackParam::NormalizeAlpha(newAlpha); + trk.rotate(newAlpha) && prop->PropagateToXBxByBz(trk, mPparam->rec.TrackReferenceX); + } + } + } + } else { + static_assert("Invalid template"); + } + + convertTrack(trkX, trk, prop, &TrackParCovChi2); + return nFitted; +#else + return 0; // TODO: Fixme, implement std::isSame for opencl +#endif +} + +template GPUd() int GPUTrackingRefit::RefitTrack<GPUTPCGMMergedTrack, TrackParCov>(GPUTPCGMMergedTrack& trk, bool outward, bool resetCov); +template GPUd() int GPUTrackingRefit::RefitTrack<GPUTPCGMMergedTrack, GPUTPCGMTrackParam>(GPUTPCGMMergedTrack& trk, bool outward, bool resetCov); +template GPUd() int GPUTrackingRefit::RefitTrack<TrackTPC, TrackParCov>(TrackTPC& trk, bool outward, bool resetCov); +template GPUd() int GPUTrackingRefit::RefitTrack<TrackTPC, GPUTPCGMTrackParam>(TrackTPC& trk, bool outward, bool resetCov); +template GPUd() int GPUTrackingRefit::RefitTrack<GPUTrackingRefit::TrackParCovWithArgs, TrackParCov>(GPUTrackingRefit::TrackParCovWithArgs& trk, bool outward, bool resetCov); +template GPUd() int GPUTrackingRefit::RefitTrack<GPUTrackingRefit::TrackParCovWithArgs, GPUTPCGMTrackParam>(GPUTrackingRefit::TrackParCovWithArgs& trk, bool outward, bool resetCov); + +#ifndef GPUCA_GPUCODE +void GPUTrackingRefit::SetPtrsFromGPUConstantMem(const GPUConstantMem* v, MEM_CONSTANT(GPUParam) * p) +{ + mPclusterState = v->ioPtrs.mergedTrackHitStates; + mPclusterNative = v->ioPtrs.clustersNative; + mPtrackHits = v->ioPtrs.mergedTrackHits; + mPfastTransform = v->calibObjects.fastTransform; + mPmatLUT = v->calibObjects.matLUT; + mPparam = p ? p : &v->param; +} + +void GPUTrackingRefit::SetPropagatorDefault() +{ + mPpropagator = mPparam->GetDefaultO2Propagator(false); +} +#endif diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefit.h b/GPU/GPUTracking/Refit/GPUTrackingRefit.h new file mode 100644 index 0000000000000..0a8970dabaf6c --- /dev/null +++ b/GPU/GPUTracking/Refit/GPUTrackingRefit.h @@ -0,0 +1,120 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTrackingRefit.h +/// \author David Rohr + +#ifndef GPUTRACKINGREFIT_H +#define GPUTRACKINGREFIT_H + +#include "GPUDef.h" +#include "GPUProcessor.h" + +namespace o2::dataformats +{ +template <typename FirstEntry, typename NElem> +class RangeReference; +} +namespace o2::track +{ +template <typename T> +class TrackParametrizationWithError; +using TrackParCov = TrackParametrizationWithError<float>; +} // namespace o2::track +namespace o2::base +{ +class Propagator; +class MatLayerCylSet; +} // namespace o2::base +namespace o2::tpc +{ +struct ClusterNativeAccess; +class TrackTPC; +using TrackTPCClusRef = o2::dataformats::RangeReference<uint32_t, uint16_t>; +} // namespace o2::tpc + +namespace o2::gpu +{ +class GPUTPCGMTrackParam; +class GPUTPCGMMergedTrack; +MEM_CLASS_PRE() +struct GPUConstantMem; +MEM_CLASS_PRE() +struct GPUParam; +struct GPUTPCGMMergedTrackHit; +class TPCFastTransform; + +class GPUTrackingRefit +{ + public: + void SetClusterStateArray(const unsigned char* v) { mPclusterState = v; } + void SetPtrsFromGPUConstantMem(const GPUConstantMem* v, MEM_CONSTANT(GPUParam) * p = nullptr); + void SetPropagator(const o2::base::Propagator* v) { mPpropagator = v; } + void SetPropagatorDefault(); + void SetClusterNative(const o2::tpc::ClusterNativeAccess* v) { mPclusterNative = v; } + void SetTrackHits(const GPUTPCGMMergedTrackHit* v) { mPtrackHits = v; } + void SetTrackHitReferences(const unsigned int* v) { mPtrackHitReferences = v; } + void SetFastTransform(const TPCFastTransform* v) { mPfastTransform = v; } + void SetGPUParam(const MEM_CONSTANT(GPUParam) * v) { mPparam = v; } + GPUd() int RefitTrackAsGPU(GPUTPCGMMergedTrack& trk, bool outward = false, bool resetCov = false) { return RefitTrack<GPUTPCGMMergedTrack, GPUTPCGMTrackParam>(trk, outward, resetCov); } + GPUd() int RefitTrackAsTrackParCov(GPUTPCGMMergedTrack& trk, bool outward = false, bool resetCov = false) { return RefitTrack<GPUTPCGMMergedTrack, o2::track::TrackParCov>(trk, outward, resetCov); } + GPUd() int RefitTrackAsGPU(o2::tpc::TrackTPC& trk, bool outward = false, bool resetCov = false) { return RefitTrack<o2::tpc::TrackTPC, GPUTPCGMTrackParam>(trk, outward, resetCov); } + GPUd() int RefitTrackAsTrackParCov(o2::tpc::TrackTPC& trk, bool outward = false, bool resetCov = false) { return RefitTrack<o2::tpc::TrackTPC, o2::track::TrackParCov>(trk, outward, resetCov); } + + struct TrackParCovWithArgs { + o2::track::TrackParCov& trk; + const o2::tpc::TrackTPCClusRef& clusRef; + float time0; + float* chi2; + }; + GPUd() int RefitTrackAsGPU(o2::track::TrackParCov& trk, const o2::tpc::TrackTPCClusRef& clusRef, float time0, float* chi2 = nullptr, bool outward = false, bool resetCov = false) + { + TrackParCovWithArgs x{trk, clusRef, time0, chi2}; + return RefitTrack<TrackParCovWithArgs, GPUTPCGMTrackParam>(x, outward, resetCov); + } + GPUd() int RefitTrackAsTrackParCov(o2::track::TrackParCov& trk, const o2::tpc::TrackTPCClusRef& clusRef, float time0, float* chi2 = nullptr, bool outward = false, bool resetCov = false) + { + TrackParCovWithArgs x{trk, clusRef, time0, chi2}; + return RefitTrack<TrackParCovWithArgs, o2::track::TrackParCov>(x, outward, resetCov); + } + + bool mIgnoreErrorsOnTrackEnds = true; // Ignore errors during propagation / update at the beginning / end of tracks for short tracks / tracks with high incl. angle + + private: + const unsigned char* mPclusterState = nullptr; // Ptr to shared cluster state + const o2::base::Propagator* mPpropagator = nullptr; // Ptr to propagator for TrackParCov track model + const o2::base::MatLayerCylSet* mPmatLUT = nullptr; // Ptr to material LUT + const o2::tpc::ClusterNativeAccess* mPclusterNative = nullptr; // Ptr to cluster native access structure + const GPUTPCGMMergedTrackHit* mPtrackHits = nullptr; // Ptr to hits for GPUTPCGMMergedTrack tracks + const unsigned int* mPtrackHitReferences = nullptr; // Ptr to hits for TrackTPC tracks + const TPCFastTransform* mPfastTransform = nullptr; // Ptr to TPC fast transform object + const MEM_CONSTANT(GPUParam) * mPparam = nullptr; // Ptr to GPUParam + template <class T, class S> + GPUd() int RefitTrack(T& trk, bool outward, bool resetCov); + template <class T, class S, class U> + GPUd() void convertTrack(T& trk, const S& trkX, U& prop, float* chi2); + template <class U> + GPUd() void initProp(U& prop); +}; + +class GPUTrackingRefitProcessor : public GPUTrackingRefit, public GPUProcessor +{ + public: +#ifndef GPUCA_GPUCODE + void InitializeProcessor(); + void RegisterMemoryAllocation(); + void SetMaxData(const GPUTrackingInOutPointers& io); +#endif + GPUTPCGMMergedTrack* mPTracks = nullptr; +}; + +} // namespace o2::gpu + +#endif diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx new file mode 100644 index 0000000000000..de4f94aa586c3 --- /dev/null +++ b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx @@ -0,0 +1,41 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTrackingRefitKernel.cxx +/// \author David Rohr + +#include "GPUTrackingRefitKernel.h" +#include "GPUTrackingRefit.h" + +using namespace GPUCA_NAMESPACE::gpu; + +template <int I> +GPUdii() void GPUTrackingRefitKernel::Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors) +{ + auto& refit = processors.trackingRefit; + for (unsigned int i = get_global_id(0); i < processors.ioPtrs.nMergedTracks; i += get_global_size(0)) { + if (refit.mPTracks[i].OK()) { + GPUTPCGMMergedTrack trk = refit.mPTracks[i]; + int retval; + if constexpr (I == mode0asGPU) { + retval = refit.RefitTrackAsGPU(trk, false, true); + } else if constexpr (I == mode1asTrackParCov) { + retval = refit.RefitTrackAsTrackParCov(trk, false, true); + } + if (retval > 0) { + refit.mPTracks[i] = trk; + } else { + refit.mPTracks[i].SetOK(false); + } + } + } +} +template GPUd() void GPUTrackingRefitKernel::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); +template GPUd() void GPUTrackingRefitKernel::Thread<1>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h new file mode 100644 index 0000000000000..4b2e5fdc16b6c --- /dev/null +++ b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.h @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTrackingRefitKernel.h +/// \author David Rohr + +#ifndef GPUTRACKINGREFITKERNEL_H +#define GPUTRACKINGREFITKERNEL_H + +#include "GPUGeneralKernels.h" +#include "GPUConstantMem.h" + +namespace o2::gpu +{ + +class GPUTrackingRefitKernel : public GPUKernelTemplate +{ + public: + GPUhdi() CONSTEXPR static GPUDataTypes::RecoStep GetRecoStep() { return GPUDataTypes::RecoStep::TPCCompression; } + + enum K : int { + mode0asGPU = 0, + mode1asTrackParCov = 1, + }; + + template <int iKernel = defaultKernel> + GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& GPUrestrict() smem, processorType& GPUrestrict() processors); +}; + +} // namespace o2::gpu + +#endif diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx index 84f9e062e2555..0498d0a44c9b6 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx @@ -50,7 +50,7 @@ GPUd() int GPUTPCGlobalTracking::PerformGlobalTrackingRun(GPUTPCTracker& tracker GPUTPCTrackLinearisation t0(tParam); do { rowIndex += direction; - if (!tParam.TransportToX(tracker.Row(rowIndex).X(), t0, tracker.Param().ConstBz, GPUCA_MAX_SIN_PHI)) { + if (!tParam.TransportToX(tracker.Row(rowIndex).X(), t0, tracker.Param().par.ConstBz, GPUCA_MAX_SIN_PHI)) { return (0); // Reuse t0 linearization until we are in the next sector } // GPUInfo("Transported X %f Y %f Z %f SinPhi %f DzDs %f QPt %f SignCosPhi %f (MaxY %f)", tParam.X(), tParam.Y(), tParam.Z(), tParam.SinPhi(), tParam.DzDs(), tParam.QPt(), tParam.SignCosPhi(), Row(rowIndex).MaxY()); @@ -86,7 +86,7 @@ GPUd() int GPUTPCGlobalTracking::PerformGlobalTrackingRun(GPUTPCTracker& tracker if (rowHit != CALINK_INVAL) { // GPUInfo("New track: entry %d, row %d, hitindex %d", i, rowIndex, mTrackletRowHits[rowIndex * tracker.CommonMemory()->nTracklets]); tracker.TrackHits()[hitId + i].Set(rowIndex, rowHit); - // if (i == 0) tParam.TransportToX(Row(rowIndex).X(), Param().ConstBz(), GPUCA_MAX_SIN_PHI); //Use transport with new linearisation, we have changed the track in between - NOT needed, fitting will always start at outer end of global track! + // if (i == 0) tParam.TransportToX(Row(rowIndex).X(), Param().par.ConstBz(), GPUCA_MAX_SIN_PHI); //Use transport with new linearisation, we have changed the track in between - NOT needed, fitting will always start at outer end of global track! i++; } rowIndex++; @@ -128,11 +128,11 @@ GPUd() void GPUTPCGlobalTracking::PerformGlobalTracking(int nBlocks, int nThread } if (!right && Y < -row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_LOWER) { // GPUInfo("Track %d, lower row %d, left border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, -row.MaxY()); - PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, -tracker.Param().DAlpha, -1); + PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, -tracker.Param().par.DAlpha, -1); } if (right && Y > row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_LOWER) { // GPUInfo("Track %d, lower row %d, right border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, row.MaxY()); - PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, tracker.Param().DAlpha, -1); + PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, tracker.Param().par.DAlpha, -1); } } } @@ -149,11 +149,11 @@ GPUd() void GPUTPCGlobalTracking::PerformGlobalTracking(int nBlocks, int nThread } if (!right && Y < -row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_UPPER) { // GPUInfo("Track %d, upper row %d, left border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, -row.MaxY()); - PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, -tracker.Param().DAlpha, 1); + PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, -tracker.Param().par.DAlpha, 1); } if (right && Y > row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_UPPER) { // GPUInfo("Track %d, upper row %d, right border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, row.MaxY()); - PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, tracker.Param().DAlpha, 1); + PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, tracker.Param().par.DAlpha, 1); } } } @@ -191,6 +191,16 @@ GPUd() int GPUTPCGlobalTracking::GlobalTrackingSliceOrder(int iSlice) } return iSlice; } + +GPUd() void GPUTPCGlobalTracking::GlobalTrackingSliceLeftRight(unsigned int iSlice, unsigned int& left, unsigned int& right) +{ + left = (iSlice + (GPUDataTypes::NSLICES / 2 - 1)) % (GPUDataTypes::NSLICES / 2); + right = (iSlice + 1) % (GPUDataTypes::NSLICES / 2); + if (iSlice >= (int)GPUDataTypes::NSLICES / 2) { + left += GPUDataTypes::NSLICES / 2; + right += GPUDataTypes::NSLICES / 2; + } +} #endif // !__OPENCL__ || __OPENCLCPP__ template <> diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h index 836db229d7ce5..d981be3ba5423 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h @@ -42,6 +42,7 @@ class GPUTPCGlobalTracking : public GPUKernelTemplate GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() MEM_LOCAL(GPUSharedMemory) & smem, processorType& tracker); GPUd() static int GlobalTrackingSliceOrder(int iSlice); + GPUd() static void GlobalTrackingSliceLeftRight(unsigned int iSlice, unsigned int& left, unsigned int& right); private: GPUd() static int PerformGlobalTrackingRun(GPUTPCTracker& tracker, GPUsharedref() MEM_LOCAL(GPUSharedMemory) & GPUrestrict() smem, const GPUTPCTracker& sliceSource, int iTrack, int rowIndex, float angle, int direction); diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCGrid.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCGrid.cxx index 5b3262825749f..310f77dcb5230 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCGrid.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCGrid.cxx @@ -15,7 +15,7 @@ #include "GPUCommonMath.h" using namespace GPUCA_NAMESPACE::gpu; -#ifndef assert +#if !defined(assert) && !defined(GPUCA_GPUCODE) #include <cassert> #endif diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCMCInfo.h b/GPU/GPUTracking/SliceTracker/GPUTPCMCInfo.h index 810ce33f67422..107ef6e472bd1 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCMCInfo.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCMCInfo.h @@ -30,6 +30,9 @@ struct GPUTPCMCInfo { float pY; float pZ; float genRadius; +#ifdef GPUCA_TPC_GEOMETRY_O2 + float t0; +#endif }; } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx index a38db5d671e53..22bb02f75a61f 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx @@ -15,16 +15,16 @@ #include "GPUTPCClusterData.h" #include "GPUTPCHit.h" #include "GPUTPCSliceData.h" -#include "GPUReconstruction.h" #include "GPUProcessor.h" #include "GPUO2DataTypes.h" #include "GPUTPCConvertImpl.h" #include "GPUCommonMath.h" -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include "utils/vecpod.h" #include <iostream> #include <cstring> +#include "GPUReconstruction.h" #endif using namespace GPUCA_NAMESPACE::gpu; @@ -39,7 +39,7 @@ void GPUTPCSliceData::InitializeRows(const MEM_CONSTANT(GPUParam) & p) } for (int i = 0; i < GPUCA_ROW_COUNT; ++i) { mRows[i].mX = p.tpcGeometry.Row2X(i); - mRows[i].mMaxY = CAMath::Tan(p.DAlpha / 2.) * mRows[i].mX; + mRows[i].mMaxY = CAMath::Tan(p.par.DAlpha / 2.) * mRows[i].mX; } } @@ -111,7 +111,7 @@ void* GPUTPCSliceData::SetPointersRows(void* mem) GPUd() void GPUTPCSliceData::GetMaxNBins(GPUconstantref() const MEM_CONSTANT(GPUConstantMem) * mem, GPUTPCRow* GPUrestrict() row, int& maxY, int& maxZ) { maxY = row->mMaxY * 2.f / GPUCA_MIN_BIN_SIZE + 1; - maxZ = mem->param.continuousMaxTimeBin > 0 ? mem->calibObjects.fastTransform->convTimeToZinTimeFrame(0, 0, mem->param.continuousMaxTimeBin) + 50 : 300; + maxZ = mem->param.par.continuousMaxTimeBin > 0 ? mem->calibObjects.fastTransform->convTimeToZinTimeFrame(0, 0, mem->param.par.continuousMaxTimeBin) + 50 : 300; maxZ = maxZ / GPUCA_MIN_BIN_SIZE + 1; } @@ -154,12 +154,12 @@ GPUdi() static void UpdateMinMaxYZ(float& yMin, float& yMax, float& zMin, float& } } -GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int iBlock, int iThread, GPUconstantref() const MEM_CONSTANT(GPUConstantMem) * GPUrestrict() mem, int iSlice, float* tmpMinMax) +GPUdii() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int iBlock, int iThread, GPUconstantref() const MEM_CONSTANT(GPUConstantMem) * GPUrestrict() mem, int iSlice, float* tmpMinMax) { #ifdef GPUCA_GPUCODE constexpr bool EarlyTransformWithoutClusterNative = false; #else - bool EarlyTransformWithoutClusterNative = mem->param.earlyTpcTransform && mem->ioPtrs.clustersNative == nullptr; + bool EarlyTransformWithoutClusterNative = mem->param.par.earlyTpcTransform && mem->ioPtrs.clustersNative == nullptr; #endif int* tmpHitIndex = nullptr; const unsigned int* NumberOfClustersInRow = nullptr; @@ -172,7 +172,7 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i unsigned int NumberOfClustersInRowA[GPUCA_ROW_COUNT]; vecpod<int> tmpHitIndexA; - if (EarlyTransformWithoutClusterNative) { // Implies mem->param.earlyTpcTransform but no ClusterNative present + if (EarlyTransformWithoutClusterNative) { // Implies mem->param.par.earlyTpcTransform but no ClusterNative present NumberOfClustersInRow = NumberOfClustersInRowA; RowOffsets = RowOffsetsA; tmpHitIndexA.resize(mNumberOfHits); @@ -215,7 +215,7 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i const unsigned int NumberOfClusters = EarlyTransformWithoutClusterNative ? NumberOfClustersInRow[rowIndex] : mem->ioPtrs.clustersNative->nClusters[iSlice][rowIndex]; const unsigned int RowOffset = EarlyTransformWithoutClusterNative ? RowOffsets[rowIndex] : (mem->ioPtrs.clustersNative->clusterOffset[iSlice][rowIndex] - mem->ioPtrs.clustersNative->clusterOffset[iSlice][0]); - CONSTEXPR unsigned int maxN = sizeof(calink) < 3 ? (1 << (sizeof(calink) * 8)) : (1 << 24); + CONSTEXPR unsigned int maxN = 1u << (sizeof(calink) < 3 ? (sizeof(calink) * 8) : 24); if (NumberOfClusters >= maxN) { if (iThread == 0) { mem->errorCodes.raiseError(GPUErrors::ERROR_SLICEDATA_HITINROW_OVERFLOW, rowIndex, NumberOfClusters, maxN); @@ -225,9 +225,9 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i GPUTPCRow& row = mRows[rowIndex]; if (iThread == 0) { - tmpMinMax[0] = -yMin; + tmpMinMax[0] = yMin; tmpMinMax[1] = yMax; - tmpMinMax[2] = -zMin; + tmpMinMax[2] = zMin; tmpMinMax[3] = zMax; row.mFirstHitInBinOffset = CAMath::nextMultipleOf<GPUCA_ROWALIGNMENT / sizeof(calink)>(GetGridSize(RowOffset, rowIndex) + rowIndex * GPUCA_ROWALIGNMENT / sizeof(int)); } @@ -256,7 +256,7 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i UpdateMinMaxYZ(yMin, yMax, zMin, zMax, YZData[RowOffset + i].x, YZData[RowOffset + i].y); } } else { - if (mem->param.earlyTpcTransform) { // Early transform case with ClusterNative present + if (mem->param.par.earlyTpcTransform) { // Early transform case with ClusterNative present for (unsigned int i = iThread; i < NumberOfClusters; i += nThreads) { float2 tmp; tmp.x = mClusterData[RowOffset + i].y; @@ -279,35 +279,38 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i row.mHitNumberOffset = CAMath::nextMultipleOf<GPUCA_ROWALIGNMENT / sizeof(calink)>(RowOffset + rowIndex * GPUCA_ROWALIGNMENT / sizeof(calink)); } - /* CAMath::AtomicMaxShared(&tmpMinMax[0], -yMin); // Atomic max not supported for float +#ifdef GPUCA_HAVE_ATOMIC_MINMAX_FLOAT + CAMath::AtomicMinShared(&tmpMinMax[0], yMin); CAMath::AtomicMaxShared(&tmpMinMax[1], yMax); - CAMath::AtomicMaxShared(&tmpMinMax[2], -zMin); - CAMath::AtomicMaxShared(&tmpMinMax[3], zMax); */ - for (int i = 0; i < nThreads; i++) { // Todo: Implement a better version of this stupid atomic max replacement + CAMath::AtomicMinShared(&tmpMinMax[2], zMin); + CAMath::AtomicMaxShared(&tmpMinMax[3], zMax); +#else + for (int i = 0; i < nThreads; i++) { GPUbarrier(); if (iThread == i) { - if (tmpMinMax[0] < -yMin) { - tmpMinMax[0] = -yMin; + if (tmpMinMax[0] > yMin) { + tmpMinMax[0] = yMin; } if (tmpMinMax[1] < yMax) { tmpMinMax[1] = yMax; } - if (tmpMinMax[2] < -zMin) { - tmpMinMax[2] = -zMin; + if (tmpMinMax[2] > zMin) { + tmpMinMax[2] = zMin; } if (tmpMinMax[3] < zMax) { tmpMinMax[3] = zMax; } } } +#endif GPUbarrier(); if (iThread == 0) { - CreateGrid(mem, &row, -tmpMinMax[0], tmpMinMax[1], -tmpMinMax[2], tmpMinMax[3]); + CreateGrid(mem, &row, tmpMinMax[0], tmpMinMax[1], tmpMinMax[2], tmpMinMax[3]); } GPUbarrier(); const GPUTPCGrid& grid = row.mGrid; const int numberOfBins = grid.N(); - CONSTEXPR int maxBins = sizeof(calink) < 4 ? (1 << (sizeof(calink) * 8)) : 0x7FFFFFFF; + CONSTEXPR int maxBins = sizeof(calink) < 4 ? (1 << (sizeof(calink) * 8)) : 0x7FFFFFFF; // NOLINT: false warning if (sizeof(calink) < 4 && numberOfBins >= maxBins) { if (iThread == 0) { mem->errorCodes.raiseError(GPUErrors::ERROR_SLICEDATA_BIN_OVERFLOW, rowIndex, numberOfBins, maxBins); @@ -350,7 +353,7 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i } GPUbarrier(); - constexpr float maxVal = (((long long int)1 << (sizeof(cahit) < 3 ? sizeof(cahit) * 8 : 24)) - 1); // Stay within float precision in any case! + constexpr float maxVal = (((long int)1 << (sizeof(cahit) < 3 ? sizeof(cahit) * 8 : 24)) - 1); // Stay within float precision in any case! constexpr float packingConstant = 1.f / (maxVal - 2.); const float y0 = row.mGrid.YMin(); const float z0 = row.mGrid.ZMin(); @@ -390,7 +393,7 @@ GPUd() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int i if (iThread == 0) { const float maxAbsZ = CAMath::Max(CAMath::Abs(tmpMinMax[2]), CAMath::Abs(tmpMinMax[3])); - if (maxAbsZ > 300 && !mem->param.ContinuousTracking) { + if (maxAbsZ > 300 && !mem->param.par.ContinuousTracking) { mem->errorCodes.raiseError(GPUErrors::ERROR_SLICEDATA_Z_OVERFLOW, (unsigned int)maxAbsZ); return 1; } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx index 74754beb788fa..b339cf2666ebc 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx @@ -78,7 +78,7 @@ void* GPUTPCTracker::SetPointersDataRows(void* mem) { return mData.SetPointersRo void* GPUTPCTracker::SetPointersScratch(void* mem) { computePointerWithAlignment(mem, mTrackletStartHits, mNMaxStartHits); - if (mRec->GetDeviceProcessingSettings().memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (mRec->GetProcessingSettings().memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_INDIVIDUAL) { mem = SetPointersTracklets(mem); } if (mRec->IsGPU()) { @@ -90,7 +90,7 @@ void* GPUTPCTracker::SetPointersScratch(void* mem) void* GPUTPCTracker::SetPointersScratchHost(void* mem) { - if (mRec->GetDeviceProcessingSettings().keepDisplayMemory) { + if (mRec->GetProcessingSettings().keepDisplayMemory) { computePointerWithAlignment(mem, mLinkTmpMemory, mRec->Res(mMemoryResLinks).Size()); } mem = mData.SetPointersClusterIds(mem, mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCMerging); @@ -106,21 +106,21 @@ void* GPUTPCTracker::SetPointersCommon(void* mem) void GPUTPCTracker::RegisterMemoryAllocation() { AllocateAndInitializeLate(); - bool reuseCondition = !mRec->GetDeviceProcessingSettings().keepDisplayMemory && mRec->GetDeviceProcessingSettings().trackletSelectorInPipeline && ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSliceTracking) || mRec->GetDeviceProcessingSettings().nThreads == 1); - GPUMemoryReuse reLinks{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerDataLinks, (unsigned short)(mISlice % mRec->GetDeviceProcessingSettings().nStreams)}; + bool reuseCondition = !mRec->GetProcessingSettings().keepDisplayMemory && mRec->GetProcessingSettings().trackletSelectorInPipeline && ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSliceTracking) || mRec->GetProcessingSettings().ompKernels == 1 || mRec->GetProcessingSettings().ompThreads == 1); + GPUMemoryReuse reLinks{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerDataLinks, (unsigned short)(mISlice % mRec->GetProcessingSettings().nStreams)}; mMemoryResLinks = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataLinks, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCSliceLinks", reLinks); mMemoryResSliceScratch = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK | GPUMemoryResource::MEMORY_CUSTOM, "TPCSliceScratch"); mMemoryResSliceInput = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_STACK | GPUMemoryResource::MEMORY_CUSTOM, "TPCSliceInput"); - GPUMemoryReuse reWeights{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerDataWeights, (unsigned short)(mISlice % mRec->GetDeviceProcessingSettings().nStreams)}; + GPUMemoryReuse reWeights{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerDataWeights, (unsigned short)(mISlice % mRec->GetProcessingSettings().nStreams)}; mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataWeights, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCSliceWeights", reWeights); - GPUMemoryReuse reScratch{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerScratch, (unsigned short)(mISlice % mRec->GetDeviceProcessingSettings().nStreams)}; + GPUMemoryReuse reScratch{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerScratch, (unsigned short)(mISlice % mRec->GetProcessingSettings().nStreams)}; mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCTrackerScratch", reScratch); mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersScratchHost, GPUMemoryResource::MEMORY_SCRATCH_HOST, "TPCTrackerHost"); mMemoryResCommon = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersCommon, GPUMemoryResource::MEMORY_PERMANENT, "TPCTrackerCommon"); mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataRows, GPUMemoryResource::MEMORY_PERMANENT, "TPCSliceRows"); - unsigned int type = mRec->GetDeviceProcessingSettings().fullMergerOnGPU ? GPUMemoryResource::MEMORY_SCRATCH : GPUMemoryResource::MEMORY_OUTPUT; - if (mRec->GetDeviceProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { // For individual scheme, we allocate tracklets separately, and change the type for the following allocations to custom + unsigned int type = mRec->GetProcessingSettings().fullMergerOnGPU ? GPUMemoryResource::MEMORY_SCRATCH : GPUMemoryResource::MEMORY_OUTPUT; + if (mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { // For individual scheme, we allocate tracklets separately, and change the type for the following allocations to custom type |= GPUMemoryResource::MEMORY_CUSTOM; mMemoryResTracklets = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersTracklets, type, "TPCTrackerTracklets"); } @@ -143,7 +143,7 @@ GPUhd() void* GPUTPCTracker::SetPointersOutput(void* mem) void GPUTPCTracker::SetMaxData(const GPUTrackingInOutPointers& io) { - if (mRec->GetDeviceProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + if (mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { mNMaxStartHits = mData.NumberOfHits(); } else { mNMaxStartHits = mRec->MemoryScalers()->NTPCStartHits(mData.NumberOfHits()); @@ -177,9 +177,11 @@ GPUh() int GPUTPCTracker::CheckEmptySlice() // Check if the Slice is empty, if so set the output apropriate and tell the reconstuct procesdure to terminate if (NHitsTotal() < 1) { mCommonMem->nTracks = mCommonMem->nTrackHits = 0; - WriteOutputPrepare(); - mOutput->SetNTracks(0); - mOutput->SetNTrackClusters(0); + if (mOutput) { + WriteOutputPrepare(); + mOutput->SetNTracks(0); + mOutput->SetNTrackClusters(0); + } return 1; } return 0; @@ -253,7 +255,7 @@ GPUh() void GPUTPCTracker::WriteOutput() unsigned char flags; unsigned short amp; int id; - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { origX = mData.ClusterData()[clusterIndex].x; origY = mData.ClusterData()[clusterIndex].y; origZ = mData.ClusterData()[clusterIndex].z; @@ -290,7 +292,7 @@ GPUh() void GPUTPCTracker::WriteOutput() mOutput->SetNTracks(nStoredTracks); mOutput->SetNLocalTracks(nStoredLocalTracks); mOutput->SetNTrackClusters(nStoredHits); - if (Param().debugLevel >= 3) { + if (Param().par.debugLevel >= 3) { GPUInfo("Slice %d, Output: Tracks %d, local tracks %d, hits %d", mISlice, nStoredTracks, nStoredLocalTracks, nStoredHits); } } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h index d948662d91774..6f70b47a96464 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h @@ -111,19 +111,19 @@ class GPUTPCTracker : public GPUProcessor GPUdi() static void GetErrors2Seeding(const MEM_CONSTANT(GPUParam) & param, int iRow, const MEM_LG2(GPUTPCTrackParam) & t, float& ErrY2, float& ErrZ2) { // param.GetClusterErrors2( iRow, param.GetContinuousTracking() != 0. ? 125. : t.Z(), t.SinPhi(), t.DzDs(), ErrY2, ErrZ2 ); - param.GetClusterRMS2(iRow, param.ContinuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), ErrY2, ErrZ2); + param.GetClusterRMS2(iRow, param.par.ContinuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), ErrY2, ErrZ2); } MEM_CLASS_PRE2() GPUdi() void GetErrors2Seeding(int iRow, const MEM_LG2(GPUTPCTrackParam) & t, float& ErrY2, float& ErrZ2) const { // Param().GetClusterErrors2( iRow, Param().GetContinuousTracking() != 0. ? 125. : t.Z(), t.SinPhi(), t.DzDs(), ErrY2, ErrZ2 ); - Param().GetClusterRMS2(iRow, Param().ContinuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), ErrY2, ErrZ2); + Param().GetClusterRMS2(iRow, Param().par.ContinuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), ErrY2, ErrZ2); } GPUdi() void GetErrors2Seeding(int iRow, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const { // Param().GetClusterErrors2( iRow, Param().GetContinuousTracking() != 0. ? 125. : z, sinPhi, DzDs, ErrY2, ErrZ2 ); - Param().GetClusterRMS2(iRow, Param().ContinuousTracking != 0.f ? 125.f : z, sinPhi, DzDs, ErrY2, ErrZ2); + Param().GetClusterRMS2(iRow, Param().par.ContinuousTracking != 0.f ? 125.f : z, sinPhi, DzDs, ErrY2, ErrZ2); } void SetupCommonMemory(); diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackerComponent.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackerComponent.cxx index 9fa63b304fddd..58039886112ce 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackerComponent.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackerComponent.cxx @@ -370,7 +370,7 @@ int GPUTPCTrackerComponent::ConfigureSlices() // Initialize the tracker slices GPUSettingsRec rec; GPUSettingsEvent ev; - GPUSettingsDeviceProcessing devProc; + GPUSettingsProcessing devProc; ev.solenoidBz = fSolenoidBz; ev.continuousMaxTimeBin = 0; // triggered events @@ -391,6 +391,8 @@ int GPUTPCTrackerComponent::ConfigureSlices() devProc.stuckProtection = fGPUStuckProtection; rec.NonConsecutiveIDs = true; rec.mergerReadFromTrackerDirectly = false; + devProc.ompThreads = 1; + devProc.ompKernels = false; GPURecoStepConfiguration steps; steps.steps.set(GPUDataTypes::RecoStep::TPCSliceTracking); diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackerDump.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackerDump.cxx index e0466f685eef3..1b9f6a0af18f7 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackerDump.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackerDump.cxx @@ -27,7 +27,7 @@ using namespace GPUCA_NAMESPACE::gpu; void GPUTPCTracker::DumpOutput(std::ostream& out) { - if (Param().earlyTpcTransform) { + if (Param().par.earlyTpcTransform) { out << "Slice " << mISlice << "\n"; const GPUTPCTrack* track = (Output())->GetFirstTrack(); for (unsigned int j = 0; j < (Output())->NTracks(); j++) { @@ -114,7 +114,7 @@ void GPUTPCTracker::DumpStartHits(std::ostream& out) { // sort start hits and dump to file out << "Start Hits: (Slice" << mISlice << ") (" << *NStartHits() << ")" << std::endl; - if (mRec->GetDeviceProcessingSettings().comparableDebutOutput) { + if (mRec->GetProcessingSettings().comparableDebutOutput) { qsort(TrackletStartHits(), *NStartHits(), sizeof(GPUTPCHitId), StarthitSortComparison); } for (unsigned int i = 0; i < *NStartHits(); i++) { @@ -137,7 +137,7 @@ void GPUTPCTracker::DumpTrackHits(std::ostream& out) for (int i = 0; i < Tracks()[j].NHits(); i++) { out << TrackHits()[Tracks()[j].FirstHitID() + i].RowIndex() << "-" << TrackHits()[Tracks()[j].FirstHitID() + i].HitIndex() << ", "; } - if (!mRec->GetDeviceProcessingSettings().comparableDebutOutput) { + if (!mRec->GetProcessingSettings().comparableDebutOutput) { out << "(Track: " << j << ")"; } out << std::endl; @@ -157,7 +157,7 @@ void GPUTPCTracker::DumpTrackletHits(std::ostream& out) out << "Tracklets: (Slice" << mISlice << ") (" << nTracklets << ")" << std::endl; std::vector<int> Ids(nTracklets); std::iota(Ids.begin(), Ids.end(), 0); - if (mRec->GetDeviceProcessingSettings().comparableDebutOutput) { + if (mRec->GetProcessingSettings().comparableDebutOutput) { std::sort(Ids.begin(), Ids.end(), [this](const int& a, const int& b) { if (this->Tracklets()[a].FirstRow() == this->Tracklets()[b].FirstRow()) { return this->Tracklets()[a].Param().Y() > this->Tracklets()[b].Param().Y(); diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx index 865925d2181b1..42590e8f64849 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx @@ -84,7 +84,7 @@ GPUd() void GPUTPCTrackletConstructor::StoreTracklet(int /*nBlocks*/, int /*nThr GPUglobalref() MEM_GLOBAL(GPUTPCTracklet) & GPUrestrict() tracklet = tracker.Tracklets()[itrout]; tracklet.SetNHits(r.mNHits); - CADEBUG(GPUInfo(" DONE %d hits", r.mNHits)); + CADEBUG(printf(" DONE %d hits\n", r.mNHits)); tracklet.SetFirstRow(r.mFirstRow); tracklet.SetLastRow(r.mLastRow); @@ -107,7 +107,7 @@ GPUd() void GPUTPCTrackletConstructor::StoreTracklet(int /*nBlocks*/, int /*nThr } MEM_CLASS_PRE2_TEMPLATE(class T) -GPUd() void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nThreads*/, int /*iBlock*/, int /*iThread*/, GPUsharedref() T& GPUrestrict() s, GPUTPCThreadMemory& GPUrestrict() r, GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, MEM_LG2(GPUTPCTrackParam) & GPUrestrict() tParam, int iRow, calink& rowHit) +GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nThreads*/, int /*iBlock*/, int /*iThread*/, GPUsharedref() T& GPUrestrict() s, GPUTPCThreadMemory& GPUrestrict() r, GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, MEM_LG2(GPUTPCTrackParam) & GPUrestrict() tParam, int iRow, calink& rowHit) { // reconstruction of tracklets, tracklets update step CA_MAKE_SHARED_REF(GPUTPCRow, row, tracker.Row(iRow), s.mRows[iRow]); @@ -140,7 +140,7 @@ GPUd() void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nTh tParam.SetX(x); tParam.SetY(y); r.mLastY = y; - if (tracker.Param().ContinuousTracking) { + if (tracker.Param().par.ContinuousTracking) { tParam.SetZ(0.f); r.mLastZ = 0.f; tParam.SetZOffset(z); @@ -186,12 +186,12 @@ GPUd() void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nTh } CADEBUG( printf("%14s: FIT TRACK ROW %3d X %8.3f -", "", iRow, tParam.X()); for (int i = 0; i < 5; i++) { printf(" %8.3f", tParam.Par()[i]); } printf(" -"); for (int i = 0; i < 15; i++) { printf(" %8.3f", tParam.Cov()[i]); } printf("\n")); - if (!tParam.TransportToX(x, sinPhi, cosPhi, tracker.Param().ConstBz, GPUCA_MAX_SIN_PHI)) { + if (!tParam.TransportToX(x, sinPhi, cosPhi, tracker.Param().par.ConstBz, GPUCA_MAX_SIN_PHI)) { rowHit = CALINK_INVAL; break; } CADEBUG( - printf("%15s hits %3d: FIT PROP ROW %3d X %8.3f -", "", r.mNHits, iRow, tParam.X()); for (int i = 0; i < 5; i++) { printf(" %8.3f", tParam.Par()[i]); } printf(" -"); for (int i = 0; i < 15; i++) { printf(" %8.3f", tParam.Cov()[i]); } printf("\n")); + printf("%5s hits %3d: FIT PROP ROW %3d X %8.3f -", "", r.mNHits, iRow, tParam.X()); for (int i = 0; i < 5; i++) { printf(" %8.3f", tParam.Par()[i]); } printf(" -"); for (int i = 0; i < 15; i++) { printf(" %8.3f", tParam.Cov()[i]); } printf("\n")); tracker.GetErrors2Seeding(iRow, tParam.GetZ(), sinPhi, tParam.GetDzDs(), err2Y, err2Z); if (r.mNHits >= 10) { @@ -257,7 +257,7 @@ GPUd() void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nTh float err2Y, err2Z; CADEBUG( printf("%14s: SEA TRACK ROW %3d X %8.3f -", "", iRow, tParam.X()); for (int i = 0; i < 5; i++) { printf(" %8.3f", tParam.Par()[i]); } printf(" -"); for (int i = 0; i < 15; i++) { printf(" %8.3f", tParam.Cov()[i]); } printf("\n")); - if (!tParam.TransportToX(x, tParam.SinPhi(), tParam.GetCosPhi(), tracker.Param().ConstBz, GPUCA_MAX_SIN_PHI_LOW)) { + if (!tParam.TransportToX(x, tParam.SinPhi(), tParam.GetCosPhi(), tracker.Param().par.ConstBz, GPUCA_MAX_SIN_PHI_LOW)) { r.mGo = 0; rowHit = CALINK_INVAL; break; @@ -332,7 +332,7 @@ GPUd() void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nTh float y = y0 + hh.x * stepY; float z = z0 + hh.y * stepZ; - CADEBUG(GPUInfo("%14s: SEA Hit %5d, Res %f %f", "", best, tParam.Y() - y, tParam.Z() - z)); + CADEBUG(printf("%14s: SEA Hit %5d (%8.3f %8.3f), Res %f %f\n", "", best, y, z, tParam.Y() - y, tParam.Z() - z)); calink oldHit = (r.mStage == 2 && iRow >= r.mStartRow) ? rowHit : CALINK_INVAL; if (oldHit != best && !tParam.Filter(y, z, err2Y, err2Z, GPUCA_MAX_SIN_PHI_LOW, oldHit != CALINK_INVAL)) { @@ -353,7 +353,7 @@ GPUd() void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int /*nTh } } -GPUd() void GPUTPCTrackletConstructor::DoTracklet(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, GPUsharedref() GPUTPCTrackletConstructor::MEM_LOCAL(GPUSharedMemory) & GPUrestrict() s, GPUTPCThreadMemory& GPUrestrict() r) +GPUdic(2, 1) void GPUTPCTrackletConstructor::DoTracklet(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, GPUsharedref() GPUTPCTrackletConstructor::MEM_LOCAL(GPUSharedMemory) & GPUrestrict() s, GPUTPCThreadMemory& GPUrestrict() r) { int iRow = 0, iRowEnd = GPUCA_ROW_COUNT; MEM_PLAIN(GPUTPCTrackParam) @@ -370,7 +370,6 @@ GPUd() void GPUTPCTrackletConstructor::DoTracklet(GPUconstantref() MEM_GLOBAL(GP } r.mStage = 0; r.mNHits = 0; - // if (tracker.Param().ISlice() != 35 && tracker.Param().ISlice() != 34 || r.mISH == CALINK_INVAL) {StoreTracklet( 0, 0, 0, 0, s, r, tracker, tParam );return;} #ifdef __HIPCC__ // Todo: fixme! for (int k = -1; ++k < 2; /*k++*/) { @@ -392,7 +391,7 @@ GPUd() void GPUTPCTrackletConstructor::DoTracklet(GPUconstantref() MEM_GLOBAL(GP StoreTracklet(0, 0, 0, 0, s, r, tracker, tParam, rowHits); } else { r.mNMissed = 0; - if ((r.mGo = (tParam.TransportToX(tracker.Row(r.mEndRow).X(), tracker.Param().ConstBz, GPUCA_MAX_SIN_PHI) && tParam.Filter(r.mLastY, r.mLastZ, tParam.Err2Y() * 0.5f, tParam.Err2Z() * 0.5f, GPUCA_MAX_SIN_PHI_LOW, true)))) { + if ((r.mGo = (tParam.TransportToX(tracker.Row(r.mEndRow).X(), tracker.Param().par.ConstBz, GPUCA_MAX_SIN_PHI) && tParam.Filter(r.mLastY, r.mLastZ, tParam.Err2Y() * 0.5f, tParam.Err2Z() * 0.5f, GPUCA_MAX_SIN_PHI_LOW, true)))) { CADEBUG( printf("%14s: SEA BACK ROW %3d X %8.3f -", "", iRow, tParam.X()); for (int i = 0; i < 5; i++) { printf(" %8.3f", tParam.Par()[i]); } printf(" -"); for (int i = 0; i < 15; i++) { printf(" %8.3f", tParam.Cov()[i]); } printf("\n")); float err2Y, err2Z; diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletSelector.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletSelector.cxx index 00b999c821bdb..8c4d3a56687ca 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletSelector.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletSelector.cxx @@ -41,6 +41,7 @@ GPUdii() void GPUTPCTrackletSelector::Thread<0>(int nBlocks, int nThreads, int i return; } } + GPUbarrierWarp(); GPUglobalref() MEM_GLOBAL(GPUTPCTracklet) & GPUrestrict() tracklet = tracker.Tracklets()[itr]; const int kMaxRowGap = 4; @@ -58,6 +59,7 @@ GPUdii() void GPUTPCTrackletSelector::Thread<0>(int nBlocks, int nThreads, int i int nHits = 0; const int minHits = tracker.Param().rec.MinNTrackClusters == -1 ? GPUCA_TRACKLET_SELECTOR_MIN_HITS(tracklet.Param().QPt()) : tracker.Param().rec.MinNTrackClusters; + GPUCA_UNROLL(, U(1)) for (irow = firstRow; irow <= lastRow && lastRow - irow + nHits >= minHits; irow++) { gap++; calink ih = tracker.TrackletRowHits()[tracklet.FirstHit() + (irow - firstRow)]; diff --git a/GPU/GPUTracking/Standalone/CMakeLists.txt b/GPU/GPUTracking/Standalone/CMakeLists.txt index 7d57de2bfc234..d85292225ff27 100644 --- a/GPU/GPUTracking/Standalone/CMakeLists.txt +++ b/GPU/GPUTracking/Standalone/CMakeLists.txt @@ -9,7 +9,7 @@ # submit itself to any jurisdiction. # Some general CMake settings -cmake_minimum_required(VERSION 3.14 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) project(GPUTrackingStandalone) include(FeatureSummary) set(CMAKE_INSTALL_BINDIR "${CMAKE_INSTALL_PREFIX}") @@ -23,6 +23,18 @@ if(NOT EXISTS "${CMAKE_BINARY_DIR}/config.cmake") file(COPY "${CMAKE_SOURCE_DIR}/cmake/config.cmake" DESTINATION "${CMAKE_BINARY_DIR}") endif() include("${CMAKE_BINARY_DIR}/config.cmake") +if(DEFINED CONFIG_COMPILER) + if(CONFIG_COMPILER STREQUAL "clang") + set(CMAKE_C_COMPILER "clang") + set(CMAKE_CXX_COMPILER "clang++") + elseif(CONFIG_COMPILER STREQUAL "gcc") + set(CMAKE_C_COMPILER "gcc") + set(CMAKE_CXX_COMPILER "c++") + else() + set(CMAKE_C_COMPILER "${CONFIG_COMPILER}") + set(CMAKE_CXX_COMPILER "${CONFIG_COMPILER}") + endif() +endif() # Set Build and Compiler settings set(ALIGPU_BUILD_TYPE "Standalone") @@ -35,7 +47,10 @@ if(BUILD_DEBUG) set(CMAKE_CXX_FLAGS "-O0 -ggdb") set(CMAKE_BUILD_TYPE DEBUG) else() - set(CMAKE_CXX_FLAGS "-O3 -march=native -ggdb -minline-all-stringops -ftracer -funroll-loops -fprefetch-loop-arrays -ffast-math -fno-stack-protector") + set(CMAKE_CXX_FLAGS "-O3 -march=native -ggdb -minline-all-stringops -funroll-loops -ffast-math -fno-stack-protector") + if (NOT CMAKE_CXX_COMPILER STREQUAL "clang++") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftracer -fprefetch-loop-arrays") + endif() set(CMAKE_RELEASE_TYPE RELEASE) add_definitions(-DNDEBUG) endif() @@ -91,7 +106,7 @@ if(CONFIG_O2_EXTENSIONS) endif() if(CONFIG_FMT) - find_package(fmt REQUIRED) + find_package(fmt REQUIRED HINTS $ENV{FMT_ROOT}) else() add_definitions(-DGPUCA_NO_FMT) endif() @@ -112,23 +127,27 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/Global ${CMAKE_SOURCE_DIR}/HLTHeaders ${CMAKE_SOURCE_DIR}/Merger + ${CMAKE_SOURCE_DIR}/Refit ${CMAKE_SOURCE_DIR}/qa ${CMAKE_SOURCE_DIR}/SliceTracker + ${CMAKE_SOURCE_DIR}/DataCompression ${CMAKE_SOURCE_DIR}/TRDTracking) if(CONFIG_O2_EXTENSIONS) include_directories(${CMAKE_SOURCE_DIR}/TPCClusterFinder ${CMAKE_SOURCE_DIR}/ITS - ${CMAKE_SOURCE_DIR}/DataCompression + ${CMAKE_SOURCE_DIR}/../../../Common/Field/include ${CMAKE_SOURCE_DIR}/../../../Common/Constants/include ${CMAKE_SOURCE_DIR}/../../../Common/MathUtils/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/common/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/Detectors/Common/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/Detectors/ITSMFT/ITS/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/Detectors/TPC/include + ${CMAKE_SOURCE_DIR}/../../../DataFormats/Detectors/TRD/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/Headers/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/MemoryResources/include ${CMAKE_SOURCE_DIR}/../../../DataFormats/Reconstruction/include + ${CMAKE_SOURCE_DIR}/../../../DataFormats/Reconstruction/src ${CMAKE_SOURCE_DIR}/../../../DataFormats/simulation/include ${CMAKE_SOURCE_DIR}/../../../Detectors/Base/include ${CMAKE_SOURCE_DIR}/../../../Detectors/Base/src @@ -158,21 +177,29 @@ target_compile_definitions(standalone_support PUBLIC $<TARGET_PROPERTY:O2::GPUTr # Add all sources and dependencies to to support based on Config File if(CONFIG_O2_EXTENSIONS) target_sources(standalone_support PRIVATE - ../../..//DataFormats/Detectors/TPC/src/CompressedClusters.cxx - ../../..//DataFormats/simulation/src/MCCompLabel.cxx - ../../..//Detectors/TRD/base/src/TRDGeometryBase.cxx - ../../..//Detectors/Base/src/MatLayerCylSet.cxx - ../../..//Detectors/Base/src/MatLayerCyl.cxx - ../../..//Detectors/Base/src/Ray.cxx - ../../..//Detectors/ITSMFT/ITS/tracking/src/Road.cxx) + ../../../Common/Field/src/MagFieldFast.cxx + ../../../DataFormats/Detectors/TPC/src/CompressedClusters.cxx + ../../../DataFormats/simulation/src/MCCompLabel.cxx + ../../../DataFormats/Reconstruction/src/TrackParametrization.cxx + ../../../DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx + ../../../DataFormats/Reconstruction/src/Vertex.cxx + ../../../DataFormats/Reconstruction/src/TrackLTIntegral.cxx + ../../../DataFormats/Reconstruction/src/TrackParametrization.cxx + ../../../DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx + ../../../Detectors/TRD/base/src/GeometryBase.cxx + ../../../Detectors/Base/src/MatLayerCylSet.cxx + ../../../Detectors/Base/src/MatLayerCyl.cxx + ../../../Detectors/Base/src/Ray.cxx + ../../../Detectors/Base/src/Propagator.cxx + ../../../Detectors/ITSMFT/ITS/tracking/src/Road.cxx) if(CONFIG_O2_ITS_TRAITS) target_sources(standalone_support PRIVATE - ../../..//Detectors/ITSMFT/ITS/tracking/src/PrimaryVertexContext.cxx - ../../..//Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx - ../../..//Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx - ../../..//Detectors/ITSMFT/ITS/tracking/src/TrackerTraitsCPU.cxx - ../../..//Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx - ../../..//Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx) + ../../../Detectors/ITSMFT/ITS/tracking/src/PrimaryVertexContext.cxx + ../../../Detectors/ITSMFT/ITS/tracking/src/Cluster.cxx + ../../../Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx + ../../../Detectors/ITSMFT/ITS/tracking/src/TrackerTraitsCPU.cxx + ../../../Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx + ../../../Detectors/ITSMFT/ITS/tracking/src/ROframe.cxx) target_link_libraries(standalone_support PUBLIC Boost::boost) endif() endif() @@ -199,7 +226,7 @@ if(BUILD_EVENT_DISPLAY) target_sources(GPUTracking PRIVATE display/3rdparty/gl3w.c) target_compile_definitions(GPUTracking PUBLIC GPUCA_DISPLAY_GL3W) endif() - target_sources(GPUTracking PRIVATE display/3rdparty/HandMadeMathImpl.cxx) + target_sources(GPUTracking PRIVATE display/3rdparty/HandMadeMath/HandMadeMathImpl.cxx) target_include_directories(GPUTracking SYSTEM PUBLIC display/3rdparty) endif() @@ -220,6 +247,10 @@ endif() if(OpenMP_CXX_FOUND) target_link_libraries(ca PUBLIC OpenMP::OpenMP_CXX) + if (CMAKE_CXX_COMPILER STREQUAL "clang++") + target_link_libraries(ca PUBLIC -fopenmp) + target_link_libraries(GPUTracking PUBLIC -fopenmp) + endif() endif() # Installation diff --git a/GPU/GPUTracking/Standalone/Refit b/GPU/GPUTracking/Standalone/Refit new file mode 120000 index 0000000000000..2cdf319240e0d --- /dev/null +++ b/GPU/GPUTracking/Standalone/Refit @@ -0,0 +1 @@ +../Refit/ \ No newline at end of file diff --git a/GPU/GPUTracking/Standalone/cmake/config.cmake b/GPU/GPUTracking/Standalone/cmake/config.cmake index 1535aa5fd642d..f4d4330760e23 100644 --- a/GPU/GPUTracking/Standalone/cmake/config.cmake +++ b/GPU/GPUTracking/Standalone/cmake/config.cmake @@ -1,5 +1,5 @@ -#This is the configuration file for the standalone build -#Its options do not affect the O2 build +# This is the configuration file for the standalone build +# Its options do not affect the O2 build !!!! set(ENABLE_CUDA AUTO) set(ENABLE_HIP AUTO) diff --git a/GPU/GPUTracking/Standalone/cmake/makefile b/GPU/GPUTracking/Standalone/cmake/makefile index d640ad8fa4552..698f0f58d3b20 100644 --- a/GPU/GPUTracking/Standalone/cmake/makefile +++ b/GPU/GPUTracking/Standalone/cmake/makefile @@ -1,4 +1,5 @@ all: + +$(MAKE) -C build depend +$(MAKE) -C build install clean: diff --git a/GPU/GPUTracking/Standalone/display/3rdparty/HandMadeMathImpl.cxx b/GPU/GPUTracking/Standalone/display/3rdparty/HandMadeMath/HandMadeMathImpl.cxx similarity index 100% rename from GPU/GPUTracking/Standalone/display/3rdparty/HandMadeMathImpl.cxx rename to GPU/GPUTracking/Standalone/display/3rdparty/HandMadeMath/HandMadeMathImpl.cxx diff --git a/GPU/GPUTracking/Standalone/display/GPUDisplay.cxx b/GPU/GPUTracking/Standalone/display/GPUDisplay.cxx index 66d139964fe81..5bf25ea776330 100644 --- a/GPU/GPUTracking/Standalone/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/Standalone/display/GPUDisplay.cxx @@ -76,24 +76,30 @@ using namespace GPUCA_NAMESPACE::gpu; #define SEPERATE_GLOBAL_TRACKS_LIMIT (mSeparateGlobalTracks ? tGLOBALTRACK : TRACK_TYPE_ID_LIMIT) -#define GET_CID(slice, i) (tracker.Param().earlyTpcTransform ? tracker.ClusterData()[i].id : (tracker.GetConstantMem()->ioPtrs.clustersNative->clusterOffset[slice][0] + i)) +#define GET_CID(slice, i) (tracker.Param().par.earlyTpcTransform ? tracker.ClusterData()[i].id : (tracker.GetConstantMem()->ioPtrs.clustersNative->clusterOffset[slice][0] + i)) -static const GPUDisplay::configDisplay& GPUDisplay_GetConfig(GPUChainTracking* rec) +#ifdef GPUCA_STANDALONE +namespace GPUCA_NAMESPACE::gpu +{ +extern GPUSettingsStandalone configStandalone; +} +#endif +static const GPUSettingsDisplay& GPUDisplay_GetConfig(GPUChainTracking* chain) { #if !defined(GPUCA_STANDALONE) - static GPUDisplay::configDisplay defaultConfig; - if (rec->mConfigDisplay) { - return *((const GPUDisplay::configDisplay*)rec->mConfigDisplay); + static GPUSettingsDisplay defaultConfig; + if (chain->mConfigDisplay) { + return *chain->mConfigDisplay; } else { return defaultConfig; } #else - return configStandalone.configGL; + return configStandalone.GL; #endif } -GPUDisplay::GPUDisplay(GPUDisplayBackend* backend, GPUChainTracking* rec, GPUQA* qa) : mBackend(backend), mChain(rec), mConfig(GPUDisplay_GetConfig(rec)), mQA(qa), mMerger(rec->GetTPCMerger()) { backend->mDisplay = this; } +GPUDisplay::GPUDisplay(GPUDisplayBackend* backend, GPUChainTracking* chain, GPUQA* qa) : mBackend(backend), mChain(chain), mConfig(GPUDisplay_GetConfig(chain)), mQA(qa), mMerger(chain->GetTPCMerger()) { backend->mDisplay = this; } const GPUParam& GPUDisplay::param() { return mChain->GetParam(); } const GPUTPCTracker& GPUDisplay::sliceTracker(int iSlice) { return mChain->GetTPCSliceTrackers()[iSlice]; } @@ -389,12 +395,21 @@ inline void GPUDisplay::SetColorFinal() inline void GPUDisplay::SetColorGrid() { if (mInvertColors) { - mDrawColor = {0.5, 0.5, 0}; + mDrawColor = {0.5, 0.5, 0.0}; } else { mDrawColor = {0.7, 0.7, 0.0}; } ActivateColor(); } +inline void GPUDisplay::SetColorGridTRD() +{ + if (mInvertColors) { + mDrawColor = {0.5, 0.5, 0.5}; + } else { + mDrawColor = {0.7, 0.7, 0.5}; + } + ActivateColor(); +} inline void GPUDisplay::SetColorMarked() { if (mInvertColors) { @@ -629,20 +644,14 @@ int GPUDisplay::InitGL_internal() setDepthBuffer(); setQuality(); ReSizeGLScene(GPUDisplayBackend::INIT_WIDTH, GPUDisplayBackend::INIT_HEIGHT, true); -#ifdef WITH_OPENMP - int maxThreads = mChain->GetDeviceProcessingSettings().nThreads > 1 ? mChain->GetDeviceProcessingSettings().nThreads : 1; - omp_set_num_threads(maxThreads); -#else - int maxThreads = 1; -#endif - mThreadBuffers.resize(maxThreads); - mThreadTracks.resize(maxThreads); + mThreadBuffers.resize(mChain->GetProcessingSettings().ompThreads); + mThreadTracks.resize(mChain->GetProcessingSettings().ompThreads); #ifdef GPUCA_DISPLAY_OPENGL_CORE CHKERR(mVertexShader = glCreateShader(GL_VERTEX_SHADER)); - CHKERR(glShaderSource(mVertexShader, 1, &GPUDisplayShaders::vertexShader, NULL)); + CHKERR(glShaderSource(mVertexShader, 1, &GPUDisplayShaders::vertexShader, nullptr)); CHKERR(glCompileShader(mVertexShader)); CHKERR(mFragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); - CHKERR(glShaderSource(mFragmentShader, 1, &GPUDisplayShaders::fragmentShader, NULL)); + CHKERR(glShaderSource(mFragmentShader, 1, &GPUDisplayShaders::fragmentShader, nullptr)); CHKERR(glCompileShader(mFragmentShader)); CHKERR(mShaderProgram = glCreateProgram()); CHKERR(glAttachShader(mShaderProgram, mVertexShader)); @@ -710,27 +719,29 @@ GPUDisplay::vboList GPUDisplay::DrawClusters(const GPUTPCTracker& tracker, int s bool draw = mGlobalPos[cid].w == select; if (mMarkAdjacentClusters) { - const int attach = mMerger.ClusterAttachment()[cid]; + const int attach = tracker.GetConstantMem()->ioPtrs.mergedTrackHitAttachment[cid]; if (attach) { - if (mMarkAdjacentClusters >= 16) { - if (mQA && mQA->clusterRemovable(cid, mMarkAdjacentClusters == 17)) { + if (mMarkAdjacentClusters >= 32) { + if (mQA && mQA->clusterRemovable(attach, mMarkAdjacentClusters == 33)) { draw = select == tMARKED; } - } else if ((mMarkAdjacentClusters & 2) && (attach & GPUTPCGMMergerTypes::attachTube)) { + } else if ((mMarkAdjacentClusters & 2) && (attach & gputpcgmmergertypes::attachTube)) { + draw = select == tMARKED; + } else if ((mMarkAdjacentClusters & 1) && (attach & (gputpcgmmergertypes::attachGood | gputpcgmmergertypes::attachTube)) == 0) { draw = select == tMARKED; - } else if ((mMarkAdjacentClusters & 1) && (attach & (GPUTPCGMMergerTypes::attachGood | GPUTPCGMMergerTypes::attachTube)) == 0) { + } else if ((mMarkAdjacentClusters & 4) && (attach & gputpcgmmergertypes::attachGoodLeg) == 0) { draw = select == tMARKED; - } else if ((mMarkAdjacentClusters & 4) && (attach & GPUTPCGMMergerTypes::attachGoodLeg) == 0) { + } else if ((mMarkAdjacentClusters & 16) && (attach & gputpcgmmergertypes::attachHighIncl)) { draw = select == tMARKED; } else if (mMarkAdjacentClusters & 8) { - if (fabsf(mMerger.OutputTracks()[attach & GPUTPCGMMergerTypes::attachTrackMask].GetParam().GetQPt()) > 20.f) { + if (fabsf(tracker.GetConstantMem()->ioPtrs.mergedTracks[attach & gputpcgmmergertypes::attachTrackMask].GetParam().GetQPt()) > 20.f) { draw = select == tMARKED; } } } } else if (mMarkClusters) { short flags; - if (tracker.Param().earlyTpcTransform) { + if (tracker.Param().par.earlyTpcTransform) { flags = tracker.ClusterData()[cidInSlice].flags; } else { flags = tracker.GetConstantMem()->ioPtrs.clustersNative->clustersLinear[cid].getFlags(); @@ -878,11 +889,21 @@ void GPUDisplay::DrawFinal(int iSlice, int /*iCol*/, GPUTPCGMPropagator* prop, s break; } i = trackList[0][ii]; - track = &mMerger.OutputTracks()[i]; + track = &mMerger.GetConstantMem()->ioPtrs.mergedTracks[i]; size_t startCountInner = mVertexBuffer[iSlice].size(); bool drawing = false; - if (mTRDTrackIds[i]) { + + if (mTrackFilter) { + if (mTrackFilter == 2 && (!trdTracker().PreCheckTrackTRDCandidate(*track) || !trdTracker().CheckTrackTRDCandidate((GPUTRDTrackGPU)*track))) { + break; + } + if (mTrackFilter == 1 && mTRDTrackIds[i] == -1) { + break; + } + } + + if (mTRDTrackIds[i] != -1) { auto& trk = trdTracker().Tracks()[mTRDTrackIds[i]]; for (int k = 5; k >= 0; k--) { int cid = trk.GetTrackletIndex(k); @@ -896,10 +917,10 @@ void GPUDisplay::DrawFinal(int iSlice, int /*iCol*/, GPUTPCGMPropagator* prop, s } } for (unsigned int k = 0; k < track->NClusters(); k++) { - if (mHideRejectedClusters && (mMerger.Clusters()[track->FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject)) { + if (mHideRejectedClusters && (mMerger.GetConstantMem()->ioPtrs.mergedTrackHits[track->FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject)) { continue; } - int cid = mMerger.Clusters()[track->FirstClusterRef() + k].num; + int cid = mMerger.GetConstantMem()->ioPtrs.mergedTrackHits[track->FirstClusterRef() + k].num; int w = mGlobalPos[cid].w; if (drawing) { drawPointLinestrip(iSlice, cid, tFINALTRACK, SEPERATE_GLOBAL_TRACKS_LIMIT); @@ -917,7 +938,7 @@ void GPUDisplay::DrawFinal(int iSlice, int /*iCol*/, GPUTPCGMPropagator* prop, s drawPointLinestrip(iSlice, cid, tFINALTRACK, SEPERATE_GLOBAL_TRACKS_LIMIT); } if (!drawing && lastCluster != -1) { - drawPointLinestrip(iSlice, mMerger.Clusters()[track->FirstClusterRef() + lastCluster].num, 7, SEPERATE_GLOBAL_TRACKS_LIMIT); + drawPointLinestrip(iSlice, mMerger.GetConstantMem()->ioPtrs.mergedTrackHits[track->FirstClusterRef() + lastCluster].num, 7, SEPERATE_GLOBAL_TRACKS_LIMIT); } drawing = true; } @@ -951,16 +972,16 @@ void GPUDisplay::DrawFinal(int iSlice, int /*iCol*/, GPUTPCGMPropagator* prop, s float alpha = param().Alpha(slice); if (iMC == 0) { trkParam.Set(track->GetParam()); - if (mMerger.Param().earlyTpcTransform) { - auto cl = mMerger.ClustersXYZ()[track->FirstClusterRef() + lastCluster]; + if (mMerger.Param().par.earlyTpcTransform) { + auto cl = mMerger.ClustersXYZ()[track->FirstClusterRef() + lastCluster]; // Todo: Remove direct usage of merger x = cl.x; ZOffset = track->GetParam().GetTZOffset(); } else { - auto cl = mMerger.Clusters()[track->FirstClusterRef() + lastCluster]; + auto cl = mMerger.GetConstantMem()->ioPtrs.mergedTrackHits[track->FirstClusterRef() + lastCluster]; const auto& cln = mMerger.GetConstantMem()->ioPtrs.clustersNative->clustersLinear[cl.num]; float y, z; GPUTPCConvertImpl::convert(*mMerger.GetConstantMem(), cl.slice, cl.row, cln.getPad(), cln.getTime(), x, y, z); - ZOffset = mMerger.GetConstantMem()->calibObjects.fastTransform->convTimeToZinTimeFrame(slice, track->GetParam().GetTZOffset(), mMerger.Param().continuousMaxTimeBin); + ZOffset = mMerger.GetConstantMem()->calibObjects.fastTransform->convTimeToZinTimeFrame(slice, track->GetParam().GetTZOffset(), mMerger.Param().par.continuousMaxTimeBin); } } else { const GPUTPCMCInfo& mc = ioptrs().mcInfosTPC[i]; @@ -1109,6 +1130,75 @@ GPUDisplay::vboList GPUDisplay::DrawGrid(const GPUTPCTracker& tracker) return (vboList(startCount, mVertexBufferStart[iSlice].size() - startCount, iSlice)); } +GPUDisplay::vboList GPUDisplay::DrawGridTRD(int sector) +{ + // TODO: tilted pads ignored at the moment + size_t startCount = mVertexBufferStart[sector].size(); + size_t startCountInner = mVertexBuffer[sector].size(); +#ifdef HAVE_O2HEADERS + auto* geo = mChain->GetTRDGeometry(); + if (geo) { + int trdsector = NSLICES / 2 - 1 - sector; + float alpha = geo->GetAlpha() / 2.f + geo->GetAlpha() * trdsector; + if (trdsector >= 9) { + alpha -= 2 * M_PI; + } + for (int iLy = 0; iLy < GPUTRDTracker::EGPUTRDTracker::kNLayers; ++iLy) { + for (int iStack = 0; iStack < GPUTRDTracker::EGPUTRDTracker::kNStacks; ++iStack) { + int iDet = geo->GetDetector(iLy, iStack, trdsector); + auto matrix = geo->GetClusterMatrix(iDet); + if (!matrix) { + continue; + } + auto pp = geo->GetPadPlane(iDet); + for (int i = 0; i < pp->GetNrows(); ++i) { + float xyzLoc1[3]; + float xyzLoc2[3]; + float xyzGlb1[3]; + float xyzGlb2[3]; + xyzLoc1[0] = xyzLoc2[0] = geo->AnodePos(); + xyzLoc1[1] = pp->GetCol0(); + xyzLoc2[1] = pp->GetColEnd(); + xyzLoc1[2] = xyzLoc2[2] = pp->GetRowPos(i) - pp->GetRowPos(pp->GetNrows() / 2); + matrix->LocalToMaster(xyzLoc1, xyzGlb1); + matrix->LocalToMaster(xyzLoc2, xyzGlb2); + float x1Tmp = xyzGlb1[0]; + xyzGlb1[0] = xyzGlb1[0] * cosf(alpha) + xyzGlb1[1] * sinf(alpha); + xyzGlb1[1] = -x1Tmp * sinf(alpha) + xyzGlb1[1] * cosf(alpha); + float x2Tmp = xyzGlb2[0]; + xyzGlb2[0] = xyzGlb2[0] * cosf(alpha) + xyzGlb2[1] * sinf(alpha); + xyzGlb2[1] = -x2Tmp * sinf(alpha) + xyzGlb2[1] * cosf(alpha); + mVertexBuffer[sector].emplace_back(xyzGlb1[0] / GL_SCALE_FACTOR, xyzGlb1[1] / GL_SCALE_FACTOR, xyzGlb1[2] / GL_SCALE_FACTOR); + mVertexBuffer[sector].emplace_back(xyzGlb2[0] / GL_SCALE_FACTOR, xyzGlb2[1] / GL_SCALE_FACTOR, xyzGlb2[2] / GL_SCALE_FACTOR); + } + for (int j = 0; j < pp->GetNcols(); ++j) { + float xyzLoc1[3]; + float xyzLoc2[3]; + float xyzGlb1[3]; + float xyzGlb2[3]; + xyzLoc1[0] = xyzLoc2[0] = geo->AnodePos(); + xyzLoc1[1] = xyzLoc2[1] = pp->GetColPos(j) + pp->GetColSize(j) / 2.f; + xyzLoc1[2] = pp->GetRow0() - pp->GetRowPos(pp->GetNrows() / 2); + xyzLoc2[2] = pp->GetRowEnd() - pp->GetRowPos(pp->GetNrows() / 2); + matrix->LocalToMaster(xyzLoc1, xyzGlb1); + matrix->LocalToMaster(xyzLoc2, xyzGlb2); + float x1Tmp = xyzGlb1[0]; + xyzGlb1[0] = xyzGlb1[0] * cosf(alpha) + xyzGlb1[1] * sinf(alpha); + xyzGlb1[1] = -x1Tmp * sinf(alpha) + xyzGlb1[1] * cosf(alpha); + float x2Tmp = xyzGlb2[0]; + xyzGlb2[0] = xyzGlb2[0] * cosf(alpha) + xyzGlb2[1] * sinf(alpha); + xyzGlb2[1] = -x2Tmp * sinf(alpha) + xyzGlb2[1] * cosf(alpha); + mVertexBuffer[sector].emplace_back(xyzGlb1[0] / GL_SCALE_FACTOR, xyzGlb1[1] / GL_SCALE_FACTOR, xyzGlb1[2] / GL_SCALE_FACTOR); + mVertexBuffer[sector].emplace_back(xyzGlb2[0] / GL_SCALE_FACTOR, xyzGlb2[1] / GL_SCALE_FACTOR, xyzGlb2[2] / GL_SCALE_FACTOR); + } + } + } + } +#endif + insertVertexList(sector, startCountInner, mVertexBuffer[sector].size()); + return (vboList(startCount, mVertexBufferStart[sector].size() - startCount, sector)); +} + int GPUDisplay::DrawGLScene(bool mixAnimation, float mAnimateTime) { try { @@ -1152,10 +1242,12 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) mGlobalPosTRD = mGlobalPosPtrTRD.get(); mGlobalPosTRD2 = mGlobalPosPtrTRD2.get(); } - if ((size_t)mMerger.NOutputTracks() > mTRDTrackIds.size()) { - mTRDTrackIds.resize(mMerger.NOutputTracks()); + if ((size_t)mMerger.GetConstantMem()->ioPtrs.nMergedTracks > mTRDTrackIds.size()) { + mTRDTrackIds.resize(mMerger.GetConstantMem()->ioPtrs.nMergedTracks); + } + for (unsigned int i = 0; i < mMerger.GetConstantMem()->ioPtrs.nMergedTracks; i++) { + mTRDTrackIds[i] = -1; } - memset(mTRDTrackIds.data(), 0, sizeof(mTRDTrackIds[0]) * mMerger.NOutputTracks()); for (int i = 0; i < trdTracker().NTracks(); i++) { if (trdTracker().Tracks()[i].GetNtracklets()) { mTRDTrackIds[trdTracker().Tracks()[i].GetTPCtrackId()] = i; @@ -1164,18 +1256,19 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) mMaxClusterZ = 0; bool error = false; - GPUCA_OPENMP(parallel for num_threads(mChain->GetDeviceProcessingSettings().nThreads) reduction(max : mMaxClusterZ)) + GPUCA_OPENMP(parallel for num_threads(mChain->GetProcessingSettings().ompThreads) reduction(max : mMaxClusterZ)) for (int iSlice = 0; iSlice < NSLICES; iSlice++) { if (error) { continue; } int row = 0; - unsigned int nCls = mMerger.Param().earlyTpcTransform ? ioptrs().nClusterData[iSlice] : ioptrs().clustersNative->nClustersSector[iSlice]; + unsigned int nCls = mMerger.Param().par.earlyTpcTransform ? ioptrs().nClusterData[iSlice] : ioptrs().clustersNative->nClustersSector[iSlice]; for (unsigned int i = 0; i < nCls; i++) { int cid; - if (mMerger.Param().earlyTpcTransform) { + if (mMerger.Param().par.earlyTpcTransform) { const auto& cl = ioptrs().clusterData[iSlice][i]; cid = cl.id; + row = cl.row; } else { cid = ioptrs().clustersNative->clusterOffset[iSlice][0] + i; while (row < GPUCA_ROW_COUNT && ioptrs().clustersNative->clusterOffset[iSlice][row + 1] <= (unsigned int)cid) { @@ -1188,9 +1281,9 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) break; } float4* ptr = &mGlobalPos[cid]; - if (mMerger.Param().earlyTpcTransform) { + if (mMerger.Param().par.earlyTpcTransform) { const auto& cl = ioptrs().clusterData[iSlice][i]; - mChain->GetParam().Slice2Global(iSlice, cl.x + mXadd, cl.y, cl.z, &ptr->x, &ptr->y, &ptr->z); + mChain->GetParam().Slice2Global(iSlice, (mConfig.clustersOnNominalRow ? mMerger.Param().tpcGeometry.Row2X(row) : cl.x) + mXadd, cl.y, cl.z, &ptr->x, &ptr->y, &ptr->z); } else { float x, y, z; const auto& cln = ioptrs().clustersNative->clusters[iSlice][0][i]; @@ -1219,7 +1312,7 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) return (1); } - GPUCA_OPENMP(parallel for num_threads(mChain->GetDeviceProcessingSettings().nThreads) reduction(max : mMaxClusterZ)) + GPUCA_OPENMP(parallel for num_threads(mChain->GetProcessingSettings().ompThreads) reduction(max : mMaxClusterZ)) for (int i = 0; i < mCurrentSpacePointsTRD; i++) { const auto& sp = trdTracker().SpacePoints()[i]; int iSec = mChain->GetTRDGeometry()->GetSector(trdTracker().Tracklets()[i].GetDetector()); @@ -1382,7 +1475,7 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) nextViewMatrix = nextViewMatrix * HMM_Translate({-vals[0], -vals[1], -vals[2]}); } } else if (mResetScene) { - nextViewMatrix = nextViewMatrix * HMM_Translate({0, 0, param().ContinuousTracking ? (-mMaxClusterZ / GL_SCALE_FACTOR - 8) : -8}); + nextViewMatrix = nextViewMatrix * HMM_Translate({0, 0, param().par.ContinuousTracking ? (-mMaxClusterZ / GL_SCALE_FACTOR - 8) : -8}); mCfg.pointSize = 2.0; mCfg.drawSlice = -1; @@ -1551,7 +1644,7 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) mGlDLFinal[iSlice].resize(mNCollissions); } } - GPUCA_OPENMP(parallel num_threads(mChain->GetDeviceProcessingSettings().nThreads)) + GPUCA_OPENMP(parallel num_threads(mChain->GetProcessingSettings().ompThreads)) { #ifdef WITH_OPENMP int numThread = omp_get_thread_num(); @@ -1570,7 +1663,7 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) prop.SetMaxSinPhi(.999); prop.SetMaterialTPC(); prop.SetPolynomialField(&mMerger.Param().polynomialField); - prop.SetToyMCEventsFlag(mMerger.Param().ToyMCEventsFlag); + prop.SetToyMCEventsFlag(mMerger.Param().par.ToyMCEventsFlag); GPUCA_OPENMP(barrier) GPUCA_OPENMP(for) @@ -1582,6 +1675,9 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) mGlDLLines[iSlice][tTRACKLET] = DrawTracklets(tracker); mGlDLLines[iSlice][tSLICETRACK] = DrawTracks(tracker, 0); mGlDLGrid[iSlice] = DrawGrid(tracker); + if (iSlice < NSLICES / 2) { + mGlDLGridTRD[iSlice] = DrawGridTRD(iSlice); + } } GPUCA_OPENMP(barrier) @@ -1601,15 +1697,15 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) } } GPUCA_OPENMP(for) - for (int i = 0; i < mMerger.NOutputTracks(); i++) { - const GPUTPCGMMergedTrack* track = &mMerger.OutputTracks()[i]; + for (unsigned int i = 0; i < mMerger.GetConstantMem()->ioPtrs.nMergedTracks; i++) { + const GPUTPCGMMergedTrack* track = &mMerger.GetConstantMem()->ioPtrs.mergedTracks[i]; if (track->NClusters() == 0) { continue; } if (mHideRejectedTracks && !track->OK()) { continue; } - int slice = mMerger.Clusters()[track->FirstClusterRef() + track->NClusters() - 1].slice; + int slice = mMerger.GetConstantMem()->ioPtrs.mergedTrackHits[track->FirstClusterRef() + track->NClusters() - 1].slice; unsigned int col = 0; if (mNCollissions > 1) { int label = mQA ? mQA->GetMCTrackLabel(i) : -1; @@ -1756,7 +1852,8 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) #endif } -#define LOOP_SLICE for (int iSlice = (mCfg.drawSlice == -1 ? 0 : mCfg.drawRelatedSlices ? (mCfg.drawSlice % 9) : mCfg.drawSlice); iSlice < NSLICES; iSlice += (mCfg.drawSlice == -1 ? 1 : mCfg.drawRelatedSlices ? 9 : NSLICES)) +#define LOOP_SLICE for (int iSlice = (mCfg.drawSlice == -1 ? 0 : mCfg.drawRelatedSlices ? (mCfg.drawSlice % (NSLICES / 4)) : mCfg.drawSlice); iSlice < NSLICES; iSlice += (mCfg.drawSlice == -1 ? 1 : mCfg.drawRelatedSlices ? (NSLICES / 4) : NSLICES)) +#define LOOP_SLICE2 for (int iSlice = (mCfg.drawSlice == -1 ? 0 : mCfg.drawRelatedSlices ? (mCfg.drawSlice % (NSLICES / 4)) : mCfg.drawSlice) % (NSLICES / 2); iSlice < NSLICES / 2; iSlice += (mCfg.drawSlice == -1 ? 1 : mCfg.drawRelatedSlices ? (NSLICES / 4) : NSLICES)) #define LOOP_COLLISION for (int iCol = (mCfg.showCollision == -1 ? 0 : mCfg.showCollision); iCol < mNCollissions; iCol += (mCfg.showCollision == -1 ? 1 : mNCollissions)) #define LOOP_COLLISION_COL(cmd) \ LOOP_COLLISION \ @@ -1768,8 +1865,14 @@ int GPUDisplay::DrawGLScene_internal(bool mixAnimation, float mAnimateTime) } if (mCfg.drawGrid) { - SetColorGrid(); - LOOP_SLICE drawVertices(mGlDLGrid[iSlice], GL_LINES); + if (mCfg.drawTPC) { + SetColorGrid(); + LOOP_SLICE drawVertices(mGlDLGrid[iSlice], GL_LINES); + } + if (mCfg.drawTRD) { + SetColorGridTRD(); + LOOP_SLICE2 drawVertices(mGlDLGridTRD[iSlice], GL_LINES); + } } if (mCfg.drawClusters) { if (mCfg.drawTRD) { diff --git a/GPU/GPUTracking/Standalone/display/GPUDisplay.h b/GPU/GPUTracking/Standalone/display/GPUDisplay.h index 3ec116569ad3e..81b1fd39b6716 100644 --- a/GPU/GPUTracking/Standalone/display/GPUDisplay.h +++ b/GPU/GPUTracking/Standalone/display/GPUDisplay.h @@ -32,7 +32,7 @@ #endif #endif -#include "GPUDisplayConfig.h" +#include "GPUSettings.h" #include "GPUDisplayBackend.h" namespace GPUCA_NAMESPACE @@ -53,12 +53,10 @@ namespace gpu class GPUDisplay { public: - GPUDisplay(GPUDisplayBackend* backend, GPUChainTracking* rec, GPUQA* qa) {} + GPUDisplay(GPUDisplayBackend* backend, GPUChainTracking* chain, GPUQA* qa) {} ~GPUDisplay() = default; GPUDisplay(const GPUDisplay&) = delete; - typedef structConfigGL configDisplay; - int StartDisplay() { return 1; } void ShowNextEvent() {} void WaitForNextEvent() {} @@ -96,12 +94,10 @@ struct GPUParam; class GPUDisplay { public: - GPUDisplay(GPUDisplayBackend* backend, GPUChainTracking* rec, GPUQA* qa); + GPUDisplay(GPUDisplayBackend* backend, GPUChainTracking* chain, GPUQA* qa); ~GPUDisplay() = default; GPUDisplay(const GPUDisplay&) = delete; - typedef GPUDisplayConfig configDisplay; - int StartDisplay(); void ShowNextEvent(); void WaitForNextEvent(); @@ -142,38 +138,6 @@ class GPUDisplay GLvertex(GLfloat a, GLfloat b, GLfloat c) : x(a), y(b), z(c) {} }; - struct OpenGLConfig { - int animationMode = 0; - - bool smoothPoints = true; - bool smoothLines = false; - bool depthBuffer = false; - - bool drawClusters = true; - bool drawLinks = false; - bool drawSeeds = false; - bool drawInitLinks = false; - bool drawTracklets = false; - bool drawTracks = false; - bool drawGlobalTracks = false; - bool drawFinal = false; - int excludeClusters = 0; - int propagateTracks = 0; - - int colorClusters = 1; - int drawSlice = -1; - int drawRelatedSlices = 0; - int drawGrid = 0; - int colorCollisions = 0; - int showCollision = -1; - - float pointSize = 2.0; - float lineWidth = 1.4; - - bool drawTPC = true; - bool drawTRD = true; - }; - struct DrawArraysIndirectCommand { DrawArraysIndirectCommand(unsigned int a = 0, unsigned int b = 0, unsigned int c = 0, unsigned int d = 0) : count(a), instanceCount(b), first(c), baseInstance(d) {} unsigned int count; @@ -238,6 +202,7 @@ class GPUDisplay void SetInfo(Args... args) { snprintf(mInfoText2, 1024, args...); + GPUInfo("%s", mInfoText2); mInfoText2Timer.ResetStart(); } void PrintGLHelpText(float colorValue); @@ -260,6 +225,7 @@ class GPUDisplay void SetColorGlobalTracks(); void SetColorFinal(); void SetColorGrid(); + void SetColorGridTRD(); void SetColorMarked(); void SetCollisionColor(int col); void setQuality(); @@ -281,6 +247,7 @@ class GPUDisplay vboList DrawTracks(const GPUTPCTracker& tracker, int global); void DrawFinal(int iSlice, int /*iCol*/, GPUTPCGMPropagator* prop, std::array<vecpod<int>, 2>& trackList, threadVertexBuffer& threadBuffer); vboList DrawGrid(const GPUTPCTracker& tracker); + vboList DrawGridTRD(int sector); void DoScreenshot(char* filename, float mAnimateTime = -1.f); void PrintHelp(); void createQuaternionFromMatrix(float* v, const float* mat); @@ -294,8 +261,8 @@ class GPUDisplay GPUDisplayBackend* mBackend; GPUChainTracking* mChain; - const configDisplay& mConfig; - OpenGLConfig mCfg; + const GPUSettingsDisplay& mConfig; + GPUSettingsDisplayLight mCfg; GPUQA* mQA; const GPUTPCGMMerger& mMerger; qSem mSemLockDisplay; @@ -354,6 +321,7 @@ class GPUDisplay int mHideRejectedTracks = 1; int mMarkAdjacentClusters = 0; int mMarkFakeClusters = 0; + int mTrackFilter = 0; vecpod<std::array<int, 37>> mCollisionClusters; int mNCollissions = 1; @@ -385,7 +353,7 @@ class GPUDisplay bool mAnimationChangeConfig = true; float mAnimationDelay = 2.f; vecpod<float> mAnimateVectors[9]; - vecpod<OpenGLConfig> mAnimateConfig; + vecpod<GPUSettingsDisplayLight> mAnimateConfig; opengl_spline mAnimationSplines[8]; volatile int mResetScene = 0; @@ -406,6 +374,7 @@ class GPUDisplay vecpod<std::array<vboList, N_FINAL_TYPE>> mGlDLFinal[NSLICES]; vecpod<vboList> mGlDLPoints[NSLICES][N_POINTS_TYPE]; vboList mGlDLGrid[NSLICES]; + vboList mGlDLGridTRD[NSLICES / 2]; vecpod<DrawArraysIndirectCommand> mCmdBuffer; }; } // namespace gpu diff --git a/GPU/GPUTracking/Standalone/display/GPUDisplayConfig.h b/GPU/GPUTracking/Standalone/display/GPUDisplayConfig.h deleted file mode 100644 index 11586934c89a5..0000000000000 --- a/GPU/GPUTracking/Standalone/display/GPUDisplayConfig.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 GPUDisplayConfig.h -/// \author David Rohr - -#ifndef GPUDISPLAYCONFIG_H -#define GPUDISPLAYCONFIG_H - -#include "GPUCommonDef.h" - -#if !defined(GPUCA_STANDALONE) -#define QCONFIG_CPP11_INIT -#endif -#include "utils/qconfig.h" - -namespace GPUCA_NAMESPACE -{ -namespace gpu -{ -typedef structConfigGL GPUDisplayConfig; -} -} // namespace GPUCA_NAMESPACE - -#endif diff --git a/GPU/GPUTracking/Standalone/display/GPUDisplayKeys.cxx b/GPU/GPUTracking/Standalone/display/GPUDisplayKeys.cxx index b2d3b9cc7fe53..6d453b0af8e69 100644 --- a/GPU/GPUTracking/Standalone/display/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/Standalone/display/GPUDisplayKeys.cxx @@ -23,7 +23,7 @@ const char* HelpText[] = { "[l] / [k] / [J] Draw single slice (next / previous slice), draw related slices (same plane in phi)", "[;] / [:] Show splitting of TPC in slices by extruding volume, [:] resets", "[#] Invert colors", - "[y] / [Y] / ['] / [X] / [M] Start Animation, Add / remove Animation point, Reset, Cycle mode", + "[y] / [Y] / ['] / [X] / [M] Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)", "[>] / [<] Toggle config interpolation during Animation / change Animation interval (via movement)", "[g] Draw Grid", "[i] Project onto XY-plane", @@ -35,8 +35,8 @@ const char* HelpText[] = { "[L] / [K] Draw single collisions (next / previous)", "[C] Colorcode clusters of different collisions", "[v] Hide rejected clusters from tracks", - "[b] Hide all clusters not belonging or related to matched tracks", "[j] Show global tracks as additional segments of final tracks", + "[u] Cycle through track filter", "[E] / [G] Extrapolate tracks / loopers", "[t] / [T] Take Screenshot / Record Animation to pictures", "[Z] Change screenshot resolution (scaling factor)", @@ -58,7 +58,8 @@ const char* HelpText[] = { "[ALT] / [CTRL] / [m] Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", "[1] ... [8] / [N] Enable display of clusters, preseeds, seeds, starthits, tracklets, tracks, global tracks, merged tracks / Show assigned clusters in colors" "[F1] / [F2] Enable / disable drawing of TPC / TRD" - // FREE: u + // FREE: b + // Test setting: # --> mHideUnmatchedClusters }; void GPUDisplay::PrintHelp() @@ -182,15 +183,18 @@ void GPUDisplay::HandleKeyRelease(unsigned char key) if (mMarkAdjacentClusters == 9) { mMarkAdjacentClusters = 15; } - if (mMarkAdjacentClusters == 18) { + if (mMarkAdjacentClusters == 17) { + mMarkAdjacentClusters = 31; + } + if (mMarkAdjacentClusters == 34) { mMarkAdjacentClusters = 0; } - if (mMarkAdjacentClusters == 17) { + if (mMarkAdjacentClusters == 33) { SetInfo("Marking protected clusters (%d)", mMarkAdjacentClusters); - } else if (mMarkAdjacentClusters == 16) { + } else if (mMarkAdjacentClusters == 32) { SetInfo("Marking removable clusters (%d)", mMarkAdjacentClusters); } else { - SetInfo("Marking adjacent clusters (%d): rejected %s, tube %s, looper leg %s, low Pt %s", mMarkAdjacentClusters, (mMarkAdjacentClusters & 1) ? "yes" : " no", (mMarkAdjacentClusters & 2) ? "yes" : " no", (mMarkAdjacentClusters & 4) ? "yes" : " no", (mMarkAdjacentClusters & 8) ? "yes" : " no"); + SetInfo("Marking adjacent clusters (%d): rejected %s, tube %s, looper leg %s, low Pt %s, high incl %s", mMarkAdjacentClusters, (mMarkAdjacentClusters & 1) ? "yes" : " no", (mMarkAdjacentClusters & 2) ? "yes" : " no", (mMarkAdjacentClusters & 4) ? "yes" : " no", (mMarkAdjacentClusters & 8) ? "yes" : " no", (mMarkAdjacentClusters & 16) ? "yes" : " no"); } mUpdateDLList = true; } else if (key == 'C') { @@ -214,10 +218,6 @@ void GPUDisplay::HandleKeyRelease(unsigned char key) mHideRejectedClusters ^= 1; SetInfo("Rejected clusters are %s", mHideRejectedClusters ? "hidden" : "shown"); mUpdateDLList = true; - } else if (key == 'b') { - mHideUnmatchedClusters ^= 1; - SetInfo("Unmatched clusters are %s", mHideUnmatchedClusters ? "hidden" : "shown"); - mUpdateDLList = true; } else if (key == 'i') { mProjectXY ^= 1; SetInfo("Projection onto xy plane %s", mProjectXY ? "enabled" : "disabled"); @@ -355,6 +355,10 @@ void GPUDisplay::HandleKeyRelease(unsigned char key) } else { SetInfo("Animation mode %d - Position: %s, Direction: %s", mCfg.animationMode, (mCfg.animationMode & 2) ? "Spherical (spherical rotation)" : (mCfg.animationMode & 4) ? "Spherical (Euler angles)" : "Cartesian", (mCfg.animationMode & 1) ? "Euler angles" : "Quaternion"); } + } else if (key == 'u') { + mTrackFilter = (mTrackFilter + 1) % 3; + mUpdateDLList = true; + SetInfo("Track filter: %s", mTrackFilter == 2 ? "TRD Track candidates" : mTrackFilter ? "TRD Tracks only" : "None"); } else if (key == 'o') { FILE* ftmp = fopen("glpos.tmp", "w+b"); if (ftmp) { @@ -421,12 +425,15 @@ void GPUDisplay::HandleKeyRelease(unsigned char key) PrintHelp(); SetInfo("Showing help text", 1); } - /*else if (key == '#') - { - mTestSetting++; - SetInfo("Debug test variable set to %d", mTestSetting); - mUpdateDLList = true; - }*/ + /* + else if (key == '#') + { + mTestSetting++; + SetInfo("Debug test variable set to %d", mTestSetting); + // mHideUnmatchedClusters ^= 1; + mUpdateDLList = true; + } + */ } void GPUDisplay::HandleSendKey(int key) diff --git a/GPU/GPUTracking/Standalone/makefiles/makefile_opencl_compiler.cxx b/GPU/GPUTracking/Standalone/makefiles/makefile_opencl_compiler.cxx index c4178a0523745..58918fe988539 100644 --- a/GPU/GPUTracking/Standalone/makefiles/makefile_opencl_compiler.cxx +++ b/GPU/GPUTracking/Standalone/makefiles/makefile_opencl_compiler.cxx @@ -11,6 +11,7 @@ /// \file makefile_opencl_compiler.cxx /// \author David Rohr +#define CL_TARGET_OPENCL_VERSION 220 #define _CRT_SECURE_NO_WARNINGS #include "CL/opencl.h" #include <cstdlib> diff --git a/GPU/GPUTracking/Standalone/qa/GPUQA.cxx b/GPU/GPUTracking/Standalone/qa/GPUQA.cxx index fc0e9bbf2a536..6f3670db34718 100644 --- a/GPU/GPUTracking/Standalone/qa/GPUQA.cxx +++ b/GPU/GPUTracking/Standalone/qa/GPUQA.cxx @@ -11,6 +11,9 @@ /// \file GPUQA.cxx /// \author David Rohr +#define QA_DEBUG 0 +#define QA_TIMING 0 + #include "Rtypes.h" #include "TH1F.h" @@ -26,6 +29,7 @@ #include "TTree.h" #include "TStyle.h" #include "TLatex.h" +#include "TObjArray.h" #include <sys/stat.h> #include "GPUQA.h" @@ -41,17 +45,27 @@ #include "GPUTPCClusterData.h" #include "GPUO2DataTypes.h" #include "GPUParam.inc" -#ifdef GPUCA_O2_LIB -#include "SimulationDataFormat/MCTruthContainer.h" +#include "GPUTPCClusterRejection.h" +#include "TPCFastTransform.h" +#ifdef HAVE_O2HEADERS +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" +#endif +#ifdef GPUCA_O2_LIB +#include "DetectorsRaw/HBFUtils.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTPC/Constants.h" #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/TrackReference.h" +#include "SimulationDataFormat/DigitizationContext.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/NameConf.h" +#include "Steer/MCKinematicsReader.h" #include "TPDGCode.h" #include "TParticlePDG.h" #include "TDatabasePDG.h" #endif +#include "GPUQAHelper.h" #include <algorithm> #include <cstdio> @@ -60,121 +74,168 @@ using namespace GPUCA_NAMESPACE::gpu; -#define QA_DEBUG 0 -#define QA_TIMING 0 - #ifdef GPUCA_MERGER_BY_MC_LABEL -#define CHECK_CLUSTER_STATE_INIT_LEG_BY_MC() \ - if (!unattached && mTrackMCLabels[id].isValid()) { \ - int mcLabel = mTrackMCLabels[id].getTrackID()); \ - if (mTrackMCLabelsReverse[mcLabel] != id) { \ - attach &= (~GPUTPCGMMergerTypes::attachGoodLeg); \ - } \ +#define CHECK_CLUSTER_STATE_INIT_LEG_BY_MC() \ + if (!unattached && mTrackMCLabels[id].isValid()) { \ + int mcLabel = mTrackMCLabels[id].getTrackID(); \ + int mcEvent = mTrackMCLabels[id].getEventID(); \ + if (mTrackMCLabelsReverse[mcEvent][mcLabel] != id) { \ + attach &= (~gputpcgmmergertypes::attachGoodLeg); \ + } \ } #else #define CHECK_CLUSTER_STATE_INIT_LEG_BY_MC() #endif -#define CHECK_CLUSTER_STATE_INIT() \ - bool unattached = attach == 0; \ - float qpt = 0; \ - bool lowPt = false; \ - bool mev200 = false; \ - int id = attach & GPUTPCGMMergerTypes::attachTrackMask; \ - if (!unattached) { \ - qpt = fabsf(merger.OutputTracks()[id].GetParam().GetQPt()); \ - lowPt = qpt > 20; \ - mev200 = qpt > 5; \ - } \ - bool physics = false, protect = false; \ +#define CHECK_CLUSTER_STATE_INIT() \ + bool unattached = attach == 0; \ + float qpt = 0; \ + bool lowPt = false; \ + bool mev200 = false; \ + bool mergedLooper = false; \ + int id = attach & gputpcgmmergertypes::attachTrackMask; \ + if (!unattached) { \ + qpt = fabsf(mTracking->mIOPtrs.mergedTracks[id].GetParam().GetQPt()); \ + lowPt = qpt > mTracking->GetParam().rec.tpcRejectQPt; \ + mev200 = qpt > 5; \ + mergedLooper = mTracking->mIOPtrs.mergedTracks[id].MergedLooper(); \ + } \ + bool physics = false, protect = false; \ CHECK_CLUSTER_STATE_INIT_LEG_BY_MC(); -#define CHECK_CLUSTER_STATE_CHK_COUNT() \ - if (!unattached && mev200) { \ - mNRecClusters200MeV++; \ - } \ - if (unattached) { \ - } else if (lowPt) { \ - mNRecClustersLowPt++; \ - } else if ((attach & GPUTPCGMMergerTypes::attachGoodLeg) == 0) { \ - mNRecClustersLoopers++; \ - } else if ((attach & GPUTPCGMMergerTypes::attachTube) && mev200) { \ - mNRecClustersTube200++; \ - } else if (attach & GPUTPCGMMergerTypes::attachHighIncl) { \ - mNRecClustersHighIncl++; \ - } else if (attach & GPUTPCGMMergerTypes::attachTube) { \ - protect = true; \ - mNRecClustersTube++; \ - } else if ((attach & GPUTPCGMMergerTypes::attachGood) == 0) { \ - protect = true; \ - mNRecClustersRejected++; \ - } else { \ - physics = true; \ +#define CHECK_CLUSTER_STATE() \ + CHECK_CLUSTER_STATE_INIT() \ + if (mev200) { \ + mClusterCounts.n200MeV++; \ + } \ + if (lowPt) { \ + mClusterCounts.nLowPt++; \ + } else if (mergedLooper) { \ + mClusterCounts.nMergedLooper++; \ + } else { \ + GPUTPCClusterRejection::GetProtectionStatus<true>(attach, physics, protect, &mClusterCounts, &mev200); \ } -#define CHECK_CLUSTER_STATE_CHK_NOCOUNT() \ - if (unattached) { \ - } else if (lowPt) { \ - } else if ((attach & GPUTPCGMMergerTypes::attachGoodLeg) == 0) { \ - } else if ((attach & GPUTPCGMMergerTypes::attachTube) && mev200) { \ - } else if (attach & GPUTPCGMMergerTypes::attachHighIncl) { \ - } else if (attach & GPUTPCGMMergerTypes::attachTube) { \ - protect = true; \ - } else if ((attach & GPUTPCGMMergerTypes::attachGood) == 0) { \ - protect = true; \ - } else { \ - physics = true; \ +#define CHECK_CLUSTER_STATE_NOCOUNT() \ + CHECK_CLUSTER_STATE_INIT() \ + (void)mev200; /* silence unused variable warning*/ \ + if (!lowPt && !mergedLooper) { \ + GPUTPCClusterRejection::GetProtectionStatus<false>(attach, physics, protect); \ } -// clang-format off -#define CHECK_CLUSTER_STATE() \ - CHECK_CLUSTER_STATE_INIT() \ - CHECK_CLUSTER_STATE_CHK_COUNT() //Fill state variables, increase counters - -#define CHECK_CLUSTER_STATE_NOCOUNT() \ - CHECK_CLUSTER_STATE_INIT() \ - CHECK_CLUSTER_STATE_CHK_NOCOUNT() //Fill state variables, do not increase counters -// clang-format on - -static const GPUQA::configQA& GPUQA_GetConfig(GPUChainTracking* rec) +#ifdef GPUCA_STANDALONE +namespace GPUCA_NAMESPACE::gpu +{ +extern GPUSettingsStandalone configStandalone; +} +#endif +static const GPUSettingsQA& GPUQA_GetConfig(GPUChainTracking* chain) { #if !defined(GPUCA_STANDALONE) - static GPUQA::configQA defaultConfig; - if (rec->mConfigQA) { - return *((const GPUQA::configQA*)rec->mConfigQA); + static GPUSettingsQA defaultConfig; + if (chain && chain->mConfigQA) { + return *chain->mConfigQA; } else { return defaultConfig; } #else - return configStandalone.configQA; + return configStandalone.QA; #endif } -#ifdef GPUCA_TPC_GEOMETRY_O2 -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +static const constexpr bool PLOT_ROOT = 0; +static const constexpr bool FIX_SCALES = 0; +static const constexpr bool PERF_FIGURE = 0; +static const constexpr float FIXED_SCALES_MIN[5] = {-0.05, -0.05, -0.2, -0.2, -0.5}; +static const constexpr float FIXED_SCALES_MAX[5] = {0.4, 0.7, 5, 3, 6.5}; +static const constexpr float LOG_PT_MIN = -1.; + +static constexpr float Y_MAX = 40; +static constexpr float Z_MAX = 100; +static constexpr float PT_MIN = GPUCA_MIN_TRACK_PT_DEFAULT; +static constexpr float PT_MIN2 = 0.1; +static constexpr float PT_MIN_PRIM = 0.1; +static constexpr float PT_MIN_CLUST = GPUCA_MIN_TRACK_PT_DEFAULT; +static constexpr float PT_MAX = 20; +static constexpr float ETA_MAX = 1.5; +static constexpr float ETA_MAX2 = 0.9; + +static constexpr float MIN_WEIGHT_CLS = 40; +static constexpr float FINDABLE_WEIGHT_CLS = 70; + +static constexpr bool CLUST_HIST_INT_SUM = false; + +static constexpr const int COLORCOUNT = 12; + +static const constexpr char* EFF_TYPES[4] = {"Rec", "Clone", "Fake", "All"}; +static const constexpr char* FINDABLE_NAMES[2] = {"", "Findable"}; +static const constexpr char* PRIM_NAMES[2] = {"Prim", "Sec"}; +static const constexpr char* PARAMETER_NAMES[5] = {"Y", "Z", "#Phi", "#lambda", "Relative #it{p}_{T}"}; +static const constexpr char* PARAMETER_NAMES_NATIVE[5] = {"Y", "Z", "sin(#Phi)", "tan(#lambda)", "q/#it{p}_{T} (curvature)"}; +static const constexpr char* VSPARAMETER_NAMES[6] = {"Y", "Z", "Phi", "Eta", "Pt", "Pt_log"}; +static const constexpr char* EFF_NAMES[3] = {"Efficiency", "Clone Rate", "Fake Rate"}; +static const constexpr char* EFFICIENCY_TITLES[4] = {"Efficiency (Primary Tracks, Findable)", "Efficiency (Secondary Tracks, Findable)", "Efficiency (Primary Tracks)", "Efficiency (Secondary Tracks)"}; +static const constexpr double SCALE[5] = {10., 10., 1000., 1000., 100.}; +static const constexpr double SCALE_NATIVE[5] = {10., 10., 1000., 1000., 1.}; +static const constexpr char* XAXIS_TITLES[5] = {"#it{y}_{mc} (cm)", "#it{z}_{mc} (cm)", "#Phi_{mc} (rad)", "#eta_{mc}", "#it{p}_{Tmc} (GeV/#it{c})"}; +static const constexpr char* AXIS_TITLES[5] = {"#it{y}-#it{y}_{mc} (mm) (Resolution)", "#it{z}-#it{z}_{mc} (mm) (Resolution)", "#phi-#phi_{mc} (mrad) (Resolution)", "#lambda-#lambda_{mc} (mrad) (Resolution)", "(#it{p}_{T} - #it{p}_{Tmc}) / #it{p}_{Tmc} (%) (Resolution)"}; +static const constexpr char* AXIS_TITLES_NATIVE[5] = {"#it{y}-#it{y}_{mc} (mm) (Resolution)", "#it{z}-#it{z}_{mc} (mm) (Resolution)", "sin(#phi)-sin(#phi_{mc}) (Resolution)", "tan(#lambda)-tan(#lambda_{mc}) (Resolution)", "q*(q/#it{p}_{T} - q/#it{p}_{Tmc}) (Resolution)"}; +static const constexpr char* AXIS_TITLES_PULL[5] = {"#it{y}-#it{y}_{mc}/#sigma_{y} (Pull)", "#it{z}-#it{z}_{mc}/#sigma_{z} (Pull)", "sin(#phi)-sin(#phi_{mc})/#sigma_{sin(#phi)} (Pull)", "tan(#lambda)-tan(#lambda_{mc})/#sigma_{tan(#lambda)} (Pull)", + "q*(q/#it{p}_{T} - q/#it{p}_{Tmc})/#sigma_{q/#it{p}_{T}} (Pull)"}; +static const constexpr char* CLUSTER_NAMES[GPUQA::N_CLS_HIST] = {"Correctly attached clusters", "Fake attached clusters", "Attached + adjacent clusters", "Fake adjacent clusters", "Clusters of reconstructed tracks", "Used in Physics", "Protected", "All clusters"}; +static const constexpr char* CLUSTER_TITLES[GPUQA::N_CLS_TYPE] = {"Clusters Pt Distribution / Attachment", "Clusters Pt Distribution / Attachment (relative to all clusters)", "Clusters Pt Distribution / Attachment (integrated)"}; +static const constexpr char* CLUSTER_NAMES_SHORT[GPUQA::N_CLS_HIST] = {"Attached", "Fake", "AttachAdjacent", "FakeAdjacent", "FoundTracks", "Physics", "Protected", "All"}; +static const constexpr char* CLUSTER_TYPES[GPUQA::N_CLS_TYPE] = {"", "Ratio", "Integral"}; +static const constexpr int COLORS_HEX[COLORCOUNT] = {0xB03030, 0x00A000, 0x0000C0, 0x9400D3, 0x19BBBF, 0xF25900, 0x7F7F7F, 0xFFD700, 0x07F707, 0x07F7F7, 0xF08080, 0x000000}; + +static const constexpr int CONFIG_DASHED_MARKERS = 0; + +static const constexpr float AXES_MIN[5] = {-Y_MAX, -Z_MAX, 0.f, -ETA_MAX, PT_MIN}; +static const constexpr float AXES_MAX[5] = {Y_MAX, Z_MAX, 2.f * M_PI, ETA_MAX, PT_MAX}; +static const constexpr int AXIS_BINS[5] = {51, 51, 144, 31, 50}; +static const constexpr int RES_AXIS_BINS[] = {1017, 113}; // Consecutive bin sizes, histograms are binned down until the maximum entry is 50, each bin size should evenly divide its predecessor. +static const constexpr float RES_AXES[5] = {1., 1., 0.03, 0.03, 1.0}; +static const constexpr float RES_AXES_NATIVE[5] = {1., 1., 0.1, 0.1, 5.0}; +static const constexpr float PULL_AXIS = 10.f; + +std::vector<TColor*> GPUQA::mColors; +int GPUQA::initColors() +{ + mColors.reserve(COLORCOUNT); + for (int i = 0; i < COLORCOUNT; i++) { + float f1 = (float)((COLORS_HEX[i] >> 16) & 0xFF) / (float)0xFF; + float f2 = (float)((COLORS_HEX[i] >> 8) & 0xFF) / (float)0xFF; + float f3 = (float)((COLORS_HEX[i] >> 0) & 0xFF) / (float)0xFF; + mColors.emplace_back(new TColor(10000 + i, f1, f2, f3)); + } + return 0; +} +static constexpr Color_t defaultColorNUms[COLORCOUNT] = {kRed, kBlue, kGreen, kMagenta, kOrange, kAzure, kBlack, kYellow, kGray, kTeal, kSpring, kPink}; +#define TRACK_EXPECTED_REFERENCE_X_DEFAULT 81 +#ifdef GPUCA_TPC_GEOMETRY_O2 inline unsigned int GPUQA::GetNMCCollissions() { return mNColTracks.size(); } inline unsigned int GPUQA::GetNMCTracks(int iCol) { return mNColTracks[iCol]; } -inline unsigned int GPUQA::GetNMCLabels() { return mTracking->mIOPtrs.clustersNative->clustersMCTruth ? mTracking->mIOPtrs.clustersNative->clustersMCTruth->getIndexedSize() : 0; } +inline unsigned int GPUQA::GetNMCLabels() { return mClNative->clustersMCTruth ? mClNative->clustersMCTruth->getIndexedSize() : 0; } inline const GPUQA::mcInfo_t& GPUQA::GetMCTrack(unsigned int iTrk, unsigned int iCol) { return mMCInfos[iCol][iTrk]; } inline const GPUQA::mcInfo_t& GPUQA::GetMCTrack(const mcLabel_t& label) { return mMCInfos[label.getEventID()][label.getTrackID()]; } -inline GPUQA::mcLabels_t GPUQA::GetMCLabel(unsigned int i) { return mTracking->mIOPtrs.clustersNative->clustersMCTruth->getLabels(i); } +inline GPUQA::mcLabels_t GPUQA::GetMCLabel(unsigned int i) { return mClNative->clustersMCTruth->getLabels(i); } inline int GPUQA::GetMCLabelNID(const mcLabels_t& label) { return label.size(); } -inline int GPUQA::GetMCLabelNID(unsigned int i) { return mTracking->mIOPtrs.clustersNative->clustersMCTruth->getLabels(i).size(); } -inline GPUQA::mcLabel_t GPUQA::GetMCLabel(unsigned int i, unsigned int j) { return mTracking->mIOPtrs.clustersNative->clustersMCTruth->getLabels(i)[j]; } -inline int GPUQA::GetMCLabelID(unsigned int i, unsigned int j) { return mTracking->mIOPtrs.clustersNative->clustersMCTruth->getLabels(i)[j].getTrackID(); } +inline int GPUQA::GetMCLabelNID(unsigned int i) { return mClNative->clustersMCTruth->getLabels(i).size(); } +inline GPUQA::mcLabel_t GPUQA::GetMCLabel(unsigned int i, unsigned int j) { return mClNative->clustersMCTruth->getLabels(i)[j]; } +inline int GPUQA::GetMCLabelID(unsigned int i, unsigned int j) { return mClNative->clustersMCTruth->getLabels(i)[j].getTrackID(); } inline int GPUQA::GetMCLabelID(const mcLabels_t& label, unsigned int j) { return label[j].getTrackID(); } inline int GPUQA::GetMCLabelID(const mcLabel_t& label) { return label.getTrackID(); } -inline int GPUQA::GetMCLabelCol(unsigned int i, unsigned int j) { return mTracking->mIOPtrs.clustersNative->clustersMCTruth->getLabels(i)[j].getEventID(); } +inline int GPUQA::GetMCLabelCol(unsigned int i, unsigned int j) { return mClNative->clustersMCTruth->getLabels(i)[j].getEventID(); } +inline const auto& GPUQA::GetClusterLabels() { return mClNative->clustersMCTruth; } inline float GPUQA::GetMCLabelWeight(unsigned int i, unsigned int j) { return 1; } inline float GPUQA::GetMCLabelWeight(const mcLabels_t& label, unsigned int j) { return 1; } inline float GPUQA::GetMCLabelWeight(const mcLabel_t& label) { return 1; } -inline bool GPUQA::mcPresent() { return mTracking->mIOPtrs.clustersNative->clustersMCTruth && mNColTracks.size(); } +inline bool GPUQA::mcPresent() { return !mConfig.noMC && mTracking && mClNative->clustersMCTruth && mNColTracks.size(); } #define TRACK_EXPECTED_REFERENCE_X 78 #else inline GPUQA::mcLabelI_t::mcLabelI_t(const GPUQA::mcLabel_t& l) : track(l.fMCID) @@ -195,13 +256,14 @@ inline int GPUQA::GetMCLabelID(unsigned int i, unsigned int j) { return mTrackin inline int GPUQA::GetMCLabelID(const mcLabels_t& label, unsigned int j) { return label.fClusterID[j].fMCID; } inline int GPUQA::GetMCLabelID(const mcLabel_t& label) { return label.fMCID; } inline int GPUQA::GetMCLabelCol(unsigned int i, unsigned int j) { return 0; } +inline const auto& GPUQA::GetClusterLabels() { return mTracking->mIOPtrs.mcLabelsTPC; } inline float GPUQA::GetMCLabelWeight(unsigned int i, unsigned int j) { return mTracking->mIOPtrs.mcLabelsTPC[i].fClusterID[j].fWeight; } inline float GPUQA::GetMCLabelWeight(const mcLabels_t& label, unsigned int j) { return label.fClusterID[j].fWeight; } inline float GPUQA::GetMCLabelWeight(const mcLabel_t& label) { return label.fWeight; } inline int GPUQA::FakeLabelID(int id) { return id < 0 ? id : (-2 - id); } inline int GPUQA::AbsLabelID(int id) { return id >= 0 ? id : (-id - 2); } -inline bool GPUQA::mcPresent() { return GetNMCLabels() && GetNMCTracks(0); } -#define TRACK_EXPECTED_REFERENCE_X 81 +inline bool GPUQA::mcPresent() { return !mConfig.noMC && mTracking && GetNMCLabels() && GetNMCTracks(0); } +#define TRACK_EXPECTED_REFERENCE_X TRACK_EXPECTED_REFERENCE_X_DEFAULT #endif template <class T> inline auto& GPUQA::GetMCTrackObj(T& obj, const GPUQA::mcLabelI_t& l) @@ -209,16 +271,79 @@ inline auto& GPUQA::GetMCTrackObj(T& obj, const GPUQA::mcLabelI_t& l) return obj[l.getEventID()][l.getTrackID()]; } -GPUQA::GPUQA(GPUChainTracking* rec) : mTracking(rec), mConfig(GPUQA_GetConfig(rec)) {} +template <> +auto GPUQA::getHistArray<TH1F>() +{ + return std::make_pair(mHist1D, &mHist1D_pos); +} +template <> +auto GPUQA::getHistArray<TH2F>() +{ + return std::make_pair(mHist2D, &mHist2D_pos); +} +template <> +auto GPUQA::getHistArray<TH1D>() +{ + return std::make_pair(mHist1Dd, &mHist1Dd_pos); +} +template <class T, typename... Args> +void GPUQA::createHist(T*& h, const char* name, Args... args) +{ + const auto& p = getHistArray<T>(); + if (mHaveExternalHists) { + if (p.first->size() <= p.second->size()) { + throw std::runtime_error("Incoming histogram array incomplete"); + } + if (strcmp((*p.first)[p.second->size()].GetName(), name)) { + throw std::runtime_error("Incoming histogram has incorrect name"); + } + } else { + p.first->emplace_back(name, args...); + } + p.second->emplace_back(&h); + h = &p.first->back(); +} + +namespace GPUCA_NAMESPACE::gpu +{ +struct GPUQAGarbageCollection { + std::tuple<std::vector<std::unique_ptr<TCanvas>>, std::vector<std::unique_ptr<TLegend>>, std::vector<std::unique_ptr<TPad>>, std::vector<std::unique_ptr<TLatex>>, std::vector<std::unique_ptr<TH1D>>> v; +}; +} // namespace GPUCA_NAMESPACE::gpu + +template <class T, typename... Args> +T* GPUQA::createGarbageCollected(Args... args) +{ + auto& v = std::get<std::vector<std::unique_ptr<T>>>(mGarbageCollector->v); + v.emplace_back(std::make_unique<T>(args...)); + return v.back().get(); +} +void GPUQA::clearGarbagageCollector() +{ + std::get<std::vector<std::unique_ptr<TPad>>>(mGarbageCollector->v).clear(); // Make sure to depete TPad first due to ROOT ownership (std::tuple has no defined order in its destructor) + std::apply([](auto&&... args) { ((args.clear()), ...); }, mGarbageCollector->v); +} -GPUQA::~GPUQA() = default; +GPUQA::GPUQA(GPUChainTracking* chain, const GPUSettingsQA* config) : mTracking(chain), mConfig(config ? *config : GPUQA_GetConfig(chain)), mGarbageCollector(std::make_unique<GPUQAGarbageCollection>()) +{ + static int initColorsInitialized = initColors(); + (void)initColorsInitialized; +} + +GPUQA::~GPUQA() +{ + if (mQAInitialized && !mHaveExternalHists) { + delete mHist1D; + delete mHist2D; + delete mHist1Dd; + } + clearGarbagageCollector(); // Needed to guarantee correct order for ROOT ownership +} inline bool GPUQA::MCComp(const mcLabel_t& a, const mcLabel_t& b) { return (GPUQA::GetMCLabelID(a) > GPUQA::GetMCLabelID(b)); } -bool GPUQA::clusterRemovable(int cid, bool prot) const +bool GPUQA::clusterRemovable(int attach, bool prot) const { - const GPUTPCGMMerger& merger = mTracking->GetTPCMerger(); - int attach = merger.ClusterAttachment()[cid]; CHECK_CLUSTER_STATE_NOCOUNT(); if (prot) { return protect || physics; @@ -284,7 +409,7 @@ void GPUQA::doPerfFigure(float x, float y, float size) if (!PERF_FIGURE) { return; } - TLatex* t = new TLatex; + TLatex* t = createGarbageCollected<TLatex>(); t->SetNDC(kTRUE); t->SetTextColor(1); t->SetTextSize(size); @@ -300,37 +425,27 @@ void GPUQA::SetMCTrackRange(int min, int max) int GPUQA::GetMCTrackLabel(unsigned int trackId) const { return (trackId >= mTrackMCLabels.size() ? MC_LABEL_INVALID : mTrackMCLabels[trackId].getTrackID()); } -int GPUQA::InitQA() +int GPUQA::InitQACreateHistograms() { - if (mQAInitialized) { - return 1; - } char name[2048], fname[1024]; - - mColorNums = new Color_t[COLORCOUNT]; - for (int i = 0; i < COLORCOUNT; i++) { - float f1 = (float)((COLORS_HEX[i] >> 16) & 0xFF) / (float)0xFF; - float f2 = (float)((COLORS_HEX[i] >> 8) & 0xFF) / (float)0xFF; - float f3 = (float)((COLORS_HEX[i] >> 0) & 0xFF) / (float)0xFF; - TColor* c = new TColor(10000 + i, f1, f2, f3); - mColorNums[i] = c->GetNumber(); - } - - // Create Efficiency Histograms - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 2; j++) { - for (int k = 0; k < 2; k++) { - for (int l = 0; l < 5; l++) { - for (int m = 0; m < 2; m++) { - sprintf(name, "%s%s%s%sVs%s", m ? "eff" : "tracks", EFF_TYPES[i], FINDABLE_NAMES[j], PRIM_NAMES[k], VSPARAMETER_NAMES[l]); - if (l == 4) { - double* binsPt = CreateLogAxis(AXIS_BINS[4], k == 0 ? PT_MIN_PRIM : AXES_MIN[4], AXES_MAX[4]); - mEff[i][j][k][l][m] = new TH1F(name, name, AXIS_BINS[l], binsPt); - delete[] binsPt; - } else { - mEff[i][j][k][l][m] = new TH1F(name, name, AXIS_BINS[l], AXES_MIN[l], AXES_MAX[l]); + if (mQATasks & taskTrackingEff) { + // Create Efficiency Histograms + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + for (int l = 0; l < 5; l++) { + for (int m = 0; m < 2; m++) { + sprintf(name, "%s%s%s%sVs%s", m ? "eff" : "tracks", EFF_TYPES[i], FINDABLE_NAMES[j], PRIM_NAMES[k], VSPARAMETER_NAMES[l]); + if (l == 4) { + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], k == 0 ? PT_MIN_PRIM : AXES_MIN[4], AXES_MAX[4])}; + createHist(mEff[i][j][k][l][m], name, name, AXIS_BINS[l], binsPt.get()); + } else { + createHist(mEff[i][j][k][l][m], name, name, AXIS_BINS[l], AXES_MIN[l], AXES_MAX[l]); + } + if (!mHaveExternalHists) { + mEff[i][j][k][l][m]->Sumw2(); + } } - mEff[i][j][k][l][m]->Sumw2(); } } } @@ -338,93 +453,143 @@ int GPUQA::InitQA() } // Create Resolution Histograms - for (int i = 0; i < 5; i++) { - for (int j = 0; j < 5; j++) { - sprintf(name, "rms_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); - sprintf(fname, "mean_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); - if (j == 4) { - double* binsPt = CreateLogAxis(AXIS_BINS[4], mConfig.resPrimaries == 1 ? PT_MIN_PRIM : AXES_MIN[4], AXES_MAX[4]); - mRes[i][j][0] = new TH1F(name, name, AXIS_BINS[j], binsPt); - mRes[i][j][1] = new TH1F(fname, fname, AXIS_BINS[j], binsPt); - delete[] binsPt; - } else { - mRes[i][j][0] = new TH1F(name, name, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); - mRes[i][j][1] = new TH1F(fname, fname, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); - } - sprintf(name, "res_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); - const float* axis = mConfig.nativeFitResolutions ? RES_AXES_NATIVE : RES_AXES; - const int nbins = i == 4 && mConfig.nativeFitResolutions ? (10 * RES_AXIS_BINS[0]) : RES_AXIS_BINS[0]; - if (j == 4) { - double* binsPt = CreateLogAxis(AXIS_BINS[4], mConfig.resPrimaries == 1 ? PT_MIN_PRIM : AXES_MIN[4], AXES_MAX[4]); - mRes2[i][j] = new TH2F(name, name, nbins, -axis[i], axis[i], AXIS_BINS[j], binsPt); - delete[] binsPt; - } else { - mRes2[i][j] = new TH2F(name, name, nbins, -axis[i], axis[i], AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + if (mQATasks & taskTrackingRes) { + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + sprintf(name, "rms_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); + sprintf(fname, "mean_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); + if (j == 4) { + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], mConfig.resPrimaries == 1 ? PT_MIN_PRIM : AXES_MIN[4], AXES_MAX[4])}; + createHist(mRes[i][j][0], name, name, AXIS_BINS[j], binsPt.get()); + createHist(mRes[i][j][1], fname, fname, AXIS_BINS[j], binsPt.get()); + } else { + createHist(mRes[i][j][0], name, name, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + createHist(mRes[i][j][1], fname, fname, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + } + sprintf(name, "res_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); + const float* axis = mConfig.nativeFitResolutions ? RES_AXES_NATIVE : RES_AXES; + const int nbins = i == 4 && mConfig.nativeFitResolutions ? (10 * RES_AXIS_BINS[0]) : RES_AXIS_BINS[0]; + if (j == 4) { + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], mConfig.resPrimaries == 1 ? PT_MIN_PRIM : AXES_MIN[4], AXES_MAX[4])}; + createHist(mRes2[i][j], name, name, nbins, -axis[i], axis[i], AXIS_BINS[j], binsPt.get()); + } else { + createHist(mRes2[i][j], name, name, nbins, -axis[i], axis[i], AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + } } } } // Create Pull Histograms - for (int i = 0; i < 5; i++) { - for (int j = 0; j < 5; j++) { - sprintf(name, "pull_rms_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); - sprintf(fname, "pull_mean_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); - if (j == 4) { - double* binsPt = CreateLogAxis(AXIS_BINS[4], AXES_MIN[4], AXES_MAX[4]); - mPull[i][j][0] = new TH1F(name, name, AXIS_BINS[j], binsPt); - mPull[i][j][1] = new TH1F(fname, fname, AXIS_BINS[j], binsPt); - delete[] binsPt; - } else { - mPull[i][j][0] = new TH1F(name, name, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); - mPull[i][j][1] = new TH1F(fname, fname, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); - } - sprintf(name, "pull_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); - if (j == 4) { - double* binsPt = CreateLogAxis(AXIS_BINS[4], AXES_MIN[4], AXES_MAX[4]); - mPull2[i][j] = new TH2F(name, name, RES_AXIS_BINS[0], -PULL_AXIS, PULL_AXIS, AXIS_BINS[j], binsPt); - delete[] binsPt; - } else { - mPull2[i][j] = new TH2F(name, name, RES_AXIS_BINS[0], -PULL_AXIS, PULL_AXIS, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + if (mQATasks & taskTrackingResPull) { + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + sprintf(name, "pull_rms_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); + sprintf(fname, "pull_mean_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); + if (j == 4) { + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], AXES_MIN[4], AXES_MAX[4])}; + createHist(mPull[i][j][0], name, name, AXIS_BINS[j], binsPt.get()); + createHist(mPull[i][j][1], fname, fname, AXIS_BINS[j], binsPt.get()); + } else { + createHist(mPull[i][j][0], name, name, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + createHist(mPull[i][j][1], fname, fname, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + } + sprintf(name, "pull_%s_vs_%s", VSPARAMETER_NAMES[i], VSPARAMETER_NAMES[j]); + if (j == 4) { + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], AXES_MIN[4], AXES_MAX[4])}; + createHist(mPull2[i][j], name, name, RES_AXIS_BINS[0], -PULL_AXIS, PULL_AXIS, AXIS_BINS[j], binsPt.get()); + } else { + createHist(mPull2[i][j], name, name, RES_AXIS_BINS[0], -PULL_AXIS, PULL_AXIS, AXIS_BINS[j], AXES_MIN[j], AXES_MAX[j]); + } } } } // Create Cluster Histograms - for (int i = 0; i < N_CLS_TYPE * N_CLS_HIST - 1; i++) { - int ioffset = i >= (2 * N_CLS_HIST - 1) ? (2 * N_CLS_HIST - 1) : i >= N_CLS_HIST ? N_CLS_HIST : 0; - int itype = i >= (2 * N_CLS_HIST - 1) ? 2 : i >= N_CLS_HIST ? 1 : 0; - sprintf(name, "clusters%s%s", CLUSTER_NAMES_SHORT[i - ioffset], CLUSTER_TYPES[itype]); - double* binsPt = CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX); - mClusters[i] = new TH1D(name, name, AXIS_BINS[4], binsPt); - delete[] binsPt; + if (mQATasks & taskClusterAttach) { + for (int i = 0; i < N_CLS_TYPE * N_CLS_HIST - 1; i++) { + int ioffset = i >= (2 * N_CLS_HIST - 1) ? (2 * N_CLS_HIST - 1) : i >= N_CLS_HIST ? N_CLS_HIST : 0; + int itype = i >= (2 * N_CLS_HIST - 1) ? 2 : i >= N_CLS_HIST ? 1 : 0; + sprintf(name, "clusters%s%s", CLUSTER_NAMES_SHORT[i - ioffset], CLUSTER_TYPES[itype]); + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX)}; + createHist(mClusters[i], name, name, AXIS_BINS[4], binsPt.get()); + } } - { + + if (mQATasks & taskTrackStatistics) { + // Create Tracks Histograms sprintf(name, "nclusters"); - mNCl = new TH1F(name, name, 160, 0, 159); + createHist(mNCl, name, name, 160, 0, 159); + sprintf(name, "tracks"); + std::unique_ptr<double[]> binsPt{CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX)}; + createHist(mTracks, name, name, AXIS_BINS[4], binsPt.get()); } - // Create Tracks Histograms - { - sprintf(name, "tracks"); - double* binsPt = CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX); - mTracks = new TH1F(name, name, AXIS_BINS[4], binsPt); + for (unsigned int i = 0; i < mHist1D->size(); i++) { + *mHist1D_pos[i] = &(*mHist1D)[i]; + } + for (unsigned int i = 0; i < mHist2D->size(); i++) { + *mHist2D_pos[i] = &(*mHist2D)[i]; + } + for (unsigned int i = 0; i < mHist1Dd->size(); i++) { + *mHist1Dd_pos[i] = &(*mHist1Dd)[i]; } - mkdir("plots", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + return 0; +} -#ifdef GPUCA_O2_LIB - TFile* fileSim = new TFile(o2::base::NameConf::getMCKinematicsFileName("o2sim").c_str()); - TTree* treeSim = (TTree*)fileSim->Get("o2sim"); - std::vector<o2::MCTrack>* tracksX; - std::vector<o2::TrackReference>* trackRefsX; - if (treeSim == nullptr) { - GPUError("Error reading o2sim tree"); - exit(1); +int GPUQA::loadHistograms(std::vector<TH1F>& i1, std::vector<TH2F>& i2, std::vector<TH1D>& i3, int tasks) +{ + if (tasks == -1) { + tasks = taskDefaultPostprocess; + } + if (mQAInitialized && (!mHaveExternalHists || tasks != mQATasks)) { + throw std::runtime_error("QA not initialized or initialized with different task array"); + } + if (tasks & taskClusterCounts) { + throw std::runtime_error("Cluster counts impossible with external histograms"); + } + mHist1D = &i1; + mHist2D = &i2; + mHist1Dd = &i3; + mHist1D_pos.clear(); + mHist2D_pos.clear(); + mHist1Dd_pos.clear(); + mHaveExternalHists = true; + mQATasks = tasks; + if (InitQACreateHistograms()) { + return 1; + } + mQAInitialized = true; + return 0; +} + +int GPUQA::InitQA(int tasks) +{ + if (mQAInitialized) { + throw std::runtime_error("QA already initialized"); + } + if (tasks == -1) { + tasks = taskDefault; + } + + mHist1D = new std::vector<TH1F>; + mHist2D = new std::vector<TH2F>; + mHist1Dd = new std::vector<TH1D>; + mQATasks = tasks; + + if (InitQACreateHistograms()) { + return 1; + } + + if (mConfig.enableLocalOutput) { + mkdir("plots", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); } - treeSim->SetBranchAddress("MCTrack", &tracksX); - treeSim->SetBranchAddress("TrackRefs", &trackRefsX); - int nSimEvents = treeSim->GetEntries(); +#ifdef GPUCA_O2_LIB + static constexpr float PRIM_MAX_T = 0.01f; + + o2::steer::MCKinematicsReader mcReader("collisioncontext.root"); + int nSimEvents = mcReader.getNEvents(0); mTrackMCLabelsReverse.resize(nSimEvents); mRecTracks.resize(nSimEvents); mFakeTracks.resize(nSimEvents); @@ -432,10 +597,17 @@ int GPUQA::InitQA() mMCInfos.resize(nSimEvents); mNColTracks.resize(nSimEvents); std::vector<int> refId; + + auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + auto evrec = dc->getEventRecords(); + for (int i = 0; i < nSimEvents; i++) { - treeSim->GetEntry(i); - const std::vector<o2::MCTrack>& tracks = *tracksX; - const std::vector<o2::TrackReference>& trackRefs = *trackRefsX; + auto ir = evrec[i]; + auto ir0 = o2::raw::HBFUtils::Instance().getFirstIRofTF(ir); + float timebin = (float)ir.differenceInBC(ir0) / o2::tpc::constants::LHCBCPERTIMEBIN; + + const std::vector<o2::MCTrack>& tracks = mcReader.getTracks(0, i); + const std::vector<o2::TrackReference>& trackRefs = mcReader.getTrackRefsByEvent(0, i); refId.resize(tracks.size()); std::fill(refId.begin(), refId.end(), -1); @@ -471,9 +643,18 @@ int GPUQA::InitQA() } info.charge = particle ? particle->Charge() : 0; - info.prim = 1; + info.prim = trk.T() < PRIM_MAX_T; info.primDaughters = 0; + if (trk.getFirstDaughterTrackId() != -1) { + for (int k = trk.getFirstDaughterTrackId(); k <= trk.getLastDaughterTrackId(); k++) { + if (tracks[k].T() < PRIM_MAX_T) { + info.primDaughters = 1; + break; + } + } + } info.pid = pid; + info.t0 = timebin; if (refId[j] >= 0) { const auto& trkRef = trackRefs[refId[j]]; info.x = trkRef.X(); @@ -489,18 +670,15 @@ int GPUQA::InitQA() } } } - - fileSim->Close(); - delete fileSim; #endif if (mConfig.matchMCLabels.size()) { unsigned int nFiles = mConfig.matchMCLabels.size(); - std::vector<TFile*> files(nFiles); + std::vector<std::unique_ptr<TFile>> files; std::vector<std::vector<std::vector<int>>*> labelsBuffer(nFiles); std::vector<std::vector<std::vector<int>>*> effBuffer(nFiles); for (unsigned int i = 0; i < nFiles; i++) { - files[i] = new TFile(mConfig.matchMCLabels[i]); + files.emplace_back(std::make_unique<TFile>(mConfig.matchMCLabels[i])); labelsBuffer[i] = (std::vector<std::vector<int>>*)files[i]->Get("mcLabelBuffer"); effBuffer[i] = (std::vector<std::vector<int>>*)files[i]->Get("mcEffBuffer"); if (labelsBuffer[i] == nullptr || effBuffer[i] == nullptr) { @@ -530,24 +708,29 @@ int GPUQA::InitQA() mGoodTracks[iEvent][k] = labelsOK[abs((*labelsBuffer[0])[iEvent][k])]; } } - - for (unsigned int i = 0; i < nFiles; i++) { - delete files[i]; - } } mQAInitialized = true; return 0; } -void GPUQA::RunQA(bool matchOnly) +void GPUQA::RunQA(bool matchOnly, const std::vector<o2::tpc::TrackTPC>* tracksExternal, const std::vector<o2::MCCompLabel>* tracksExtMC, const o2::tpc::ClusterNativeAccess* clNative) { if (!mQAInitialized) { - return; + throw std::runtime_error("QA not initialized"); } + if (!clNative && mTracking) { + clNative = mTracking->mIOPtrs.clustersNative; + } + mClNative = clNative; // Initialize Arrays - const GPUTPCGMMerger& merger = mTracking->GetTPCMerger(); - mTrackMCLabels.resize(merger.NOutputTracks()); + if (tracksExternal) { +#ifdef GPUCA_O2_LIB + mTrackMCLabels.resize(tracksExternal->size()); +#endif + } else { + mTrackMCLabels.resize(mTracking->mIOPtrs.nMergedTracks); + } for (unsigned int iCol = 0; iCol < GetNMCCollissions(); iCol++) { mTrackMCLabelsReverse[iCol].resize(GetNMCTracks(iCol)); mRecTracks[iCol].resize(GetNMCTracks(iCol)); @@ -559,9 +742,10 @@ void GPUQA::RunQA(bool matchOnly) mTrackMCLabelsReverse[iCol][i] = -1; } } - mClusterParam.resize(GetNMCLabels()); - memset(mClusterParam.data(), 0, mClusterParam.size() * sizeof(mClusterParam[0])); - mNTotalFakes = 0; + if (mQATasks & taskClusterAttach) { + mClusterParam.resize(GetNMCLabels()); + memset(mClusterParam.data(), 0, mClusterParam.size() * sizeof(mClusterParam[0])); + } HighResTimer timer; mNEvents++; @@ -569,7 +753,7 @@ void GPUQA::RunQA(bool matchOnly) mcEffBuffer.resize(mNEvents); mcLabelBuffer.resize(mNEvents); mcEffBuffer[mNEvents - 1].resize(GetNMCTracks(0)); - mcLabelBuffer[mNEvents - 1].resize(merger.NOutputTracks()); + mcLabelBuffer[mNEvents - 1].resize(mTracking->mIOPtrs.nMergedTracks); } bool mcAvail = mcPresent(); @@ -582,163 +766,141 @@ void GPUQA::RunQA(bool matchOnly) if (mcAvail) { // Assign Track MC Labels timer.Start(); - bool ompError = false; + if (tracksExternal) { +#ifdef GPUCA_O2_LIB + for (unsigned int i = 0; i < tracksExternal->size(); i++) { + mTrackMCLabels[i] = (*tracksExtMC)[i]; + } +#endif + } else { + auto acc = GPUTPCTrkLbl<true, mcLabelI_t>(GetClusterLabels(), 1.f - mConfig.recThreshold); #if QA_DEBUG == 0 -GPUCA_OPENMP(parallel for) + GPUCA_OPENMP(parallel for firstprivate(acc)) #endif - for (int i = 0; i < merger.NOutputTracks(); i++) { - if (ompError) { - continue; - } - int nClusters = 0; - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[i]; - std::vector<mcLabel_t> labels; - for (unsigned int k = 0; k < track.NClusters(); k++) { - if (merger.Clusters()[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { - continue; - } - nClusters++; - unsigned int hitId = merger.Clusters()[track.FirstClusterRef() + k].num; - if (hitId >= GetNMCLabels()) { - GPUError("Invalid hit id %u > %d", hitId, GetNMCLabels()); - ompError = true; - break; - } - for (int j = 0; j < GetMCLabelNID(hitId); j++) { - if (GetMCLabelID(hitId, j) >= (int)GetNMCTracks(GetMCLabelCol(hitId, j))) { - GPUError("Invalid label %d > %d (hit %d, label %d, col %d)", GetMCLabelID(hitId, j), GetNMCTracks(GetMCLabelCol(hitId, j)), hitId, j, (int)GetMCLabelCol(hitId, j)); - ompError = true; - break; + for (unsigned int i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { + acc.reset(); + int nClusters = 0; + const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[i]; + std::vector<mcLabel_t> labels; + for (unsigned int k = 0; k < track.NClusters(); k++) { + if (mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { + continue; } - if (GetMCLabelID(hitId, j) >= 0) { - if (QA_DEBUG >= 3 && track.OK()) { - GPUInfo("Track %d Cluster %u Label %d: %d (%f)", i, k, j, GetMCLabelID(hitId, j), GetMCLabelWeight(hitId, j)); + nClusters++; + unsigned int hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; + if (hitId >= GetNMCLabels()) { + GPUError("Invalid hit id %u > %d", hitId, GetNMCLabels()); + throw std::runtime_error("qa error"); + } + acc.addLabel(hitId); + for (int j = 0; j < GetMCLabelNID(hitId); j++) { + if (GetMCLabelID(hitId, j) >= (int)GetNMCTracks(GetMCLabelCol(hitId, j))) { + GPUError("Invalid label %d > %d (hit %d, label %d, col %d)", GetMCLabelID(hitId, j), GetNMCTracks(GetMCLabelCol(hitId, j)), hitId, j, (int)GetMCLabelCol(hitId, j)); + throw std::runtime_error("qa error"); + } + if (GetMCLabelID(hitId, j) >= 0) { + if (QA_DEBUG >= 3 && track.OK()) { + GPUInfo("Track %d Cluster %u Label %d: %d (%f)", i, k, j, GetMCLabelID(hitId, j), GetMCLabelWeight(hitId, j)); + } } - labels.push_back(GetMCLabel(hitId, j)); } } - if (ompError) { - break; - } - } - if (ompError) { - continue; - } - if (labels.size() == 0) { - mTrackMCLabels[i].setNoise(); - mNTotalFakes++; - continue; - } - std::sort(labels.data(), labels.data() + labels.size(), MCComp); - mcLabelI_t maxLabel; - mcLabelI_t cur = labels[0]; - float curweight = GetMCLabelWeight(labels[0]); - float maxweight = 0.f; - float sumweight = 0.f; - int curcount = 1, maxcount = 0; - if (QA_DEBUG >= 2 && track.OK()) { - for (unsigned int k = 0; k < labels.size(); k++) { - GPUInfo("\t%d %f", GetMCLabelID(labels[k]), GetMCLabelWeight(labels[k])); - } - } - for (unsigned int k = 1; k <= labels.size(); k++) { - if (k == labels.size() || cur != labels[k]) { - sumweight += curweight; - if (curcount > maxcount) { - maxLabel = cur; - maxcount = curcount; - maxweight = curweight; - } - if (k < labels.size()) { - cur = labels[k]; - curweight = GetMCLabelWeight(labels[k]); - curcount = 1; - } - } else { - curweight += GetMCLabelWeight(labels[k]); - curcount++; + float maxweight, sumweight; + int maxcount; + auto maxLabel = acc.computeLabel(&maxweight, &sumweight, &maxcount); + mTrackMCLabels[i] = maxLabel; + if (QA_DEBUG && track.OK() && GetNMCTracks(maxLabel.getEventID()) > (unsigned int)maxLabel.getTrackID()) { + const mcInfo_t& mc = GetMCTrack(maxLabel); + GPUInfo("Track %d label %d (fake %d) weight %f clusters %d (fitted %d) (%f%% %f%%) Pt %f", i, maxLabel.getTrackID(), (int)(maxLabel.isFake()), maxweight, nClusters, track.NClustersFitted(), 100.f * maxweight / sumweight, 100.f * (float)maxcount / (float)nClusters, + std::sqrt(mc.pX * mc.pX + mc.pY * mc.pY)); } } - - if (maxcount < mConfig.recThreshold * nClusters) { - maxLabel.setFakeFlag(true); - } - mTrackMCLabels[i] = maxLabel; - if (QA_DEBUG && track.OK() && GetNMCTracks(maxLabel.getEventID()) > maxLabel.getTrackID()) { - const mcInfo_t& mc = GetMCTrack(maxLabel); - GPUInfo("Track %d label %d (fake %d) weight %f clusters %d (fitted %d) (%f%% %f%%) Pt %f", i, maxLabel.getTrackID(), (int)(maxLabel.isFake()), maxweight, nClusters, track.NClustersFitted(), 100.f * maxweight / sumweight, 100.f * (float)maxcount / (float)nClusters, - std::sqrt(mc.pX * mc.pX + mc.pY * mc.pY)); - } } - if (ompError) { - return; + if (QA_TIMING) { + GPUInfo("QA Time: Assign Track Labels:\t\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); } - for (int i = 0; i < merger.NOutputTracks(); i++) { - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[i]; - if (!track.OK()) { - continue; - } - if (!mTrackMCLabels[i].isValid()) { - for (unsigned int k = 0; k < track.NClusters(); k++) { - if (merger.Clusters()[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { - continue; - } - mClusterParam[merger.Clusters()[track.FirstClusterRef() + k].num].fakeAttached++; + + if (mQATasks & taskClusterAttach) { + // fill cluster attachment status + for (unsigned int i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { + const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[i]; + if (!track.OK()) { + continue; } - continue; - } - mcLabelI_t label = mTrackMCLabels[i]; - if (mMCTrackMin == -1 || (label.getTrackID() >= mMCTrackMin && label.getTrackID() < mMCTrackMax)) { - for (unsigned int k = 0; k < track.NClusters(); k++) { - if (merger.Clusters()[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { - continue; - } - int hitId = merger.Clusters()[track.FirstClusterRef() + k].num; - bool correct = false; - for (int j = 0; j < GetMCLabelNID(hitId); j++) { - if (label == GetMCLabel(hitId, j)) { - correct = true; - break; + if (!mTrackMCLabels[i].isValid()) { + for (unsigned int k = 0; k < track.NClusters(); k++) { + if (mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { + continue; } + mClusterParam[mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num].fakeAttached++; } - if (correct) { - mClusterParam[hitId].attached++; - } else { - mClusterParam[hitId].fakeAttached++; - } + continue; } - } - if (mTrackMCLabels[i].isFake()) { - (GetMCTrackObj(mFakeTracks, label))++; - } else { - GetMCTrackObj(mRecTracks, label)++; + mcLabelI_t label = mTrackMCLabels[i]; if (mMCTrackMin == -1 || (label.getTrackID() >= mMCTrackMin && label.getTrackID() < mMCTrackMax)) { - int& revLabel = GetMCTrackObj(mTrackMCLabelsReverse, label); - if (revLabel == -1 || !merger.OutputTracks()[revLabel].OK() || (merger.OutputTracks()[i].OK() && fabsf(merger.OutputTracks()[i].GetParam().GetZ()) < fabsf(merger.OutputTracks()[revLabel].GetParam().GetZ()))) { - revLabel = i; + for (unsigned int k = 0; k < track.NClusters(); k++) { + if (mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { + continue; + } + int hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; + bool correct = false; + for (int j = 0; j < GetMCLabelNID(hitId); j++) { + if (label == GetMCLabel(hitId, j)) { + correct = true; + break; + } + } + if (correct) { + mClusterParam[hitId].attached++; + } else { + mClusterParam[hitId].fakeAttached++; + } } } - } - } - for (unsigned int i = 0; i < GetNMCLabels(); i++) { - if (mClusterParam[i].attached == 0 && mClusterParam[i].fakeAttached == 0) { - int attach = merger.ClusterAttachment()[i]; - if (attach & GPUTPCGMMergerTypes::attachFlagMask) { - int track = attach & GPUTPCGMMergerTypes::attachTrackMask; - mcLabelI_t trackL = mTrackMCLabels[track]; - bool fake = true; - for (int j = 0; j < GetMCLabelNID(i); j++) { - // GPUInfo("Attach %x Track %d / %d:%d", attach, track, j, GetMCLabelID(i, j)); - if (trackL == GetMCLabel(i, j)) { - fake = false; - break; + if (mTrackMCLabels[i].isFake()) { + (GetMCTrackObj(mFakeTracks, label))++; + } else if (!track.MergedLooper()) { + GetMCTrackObj(mRecTracks, label)++; + if (mMCTrackMin == -1 || (label.getTrackID() >= mMCTrackMin && label.getTrackID() < mMCTrackMax)) { + int& revLabel = GetMCTrackObj(mTrackMCLabelsReverse, label); + const auto* trks = mTracking->mIOPtrs.mergedTracks; + bool comp; + if (revLabel == -1) { + comp = true; + } else if (mTracking->GetParam().par.earlyTpcTransform) { + comp = fabsf(trks[i].GetParam().GetZ() + trks[i].GetParam().GetTZOffset()) < fabsf(trks[revLabel].GetParam().GetZ() + trks[revLabel].GetParam().GetTZOffset()); + } else { + float shift1 = mTracking->GetTPCTransform()->convDeltaTimeToDeltaZinTimeFrame(trks[i].CSide() * GPUChainTracking::NSLICES / 2, trks[i].GetParam().GetTZOffset()); + float shift2 = mTracking->GetTPCTransform()->convDeltaTimeToDeltaZinTimeFrame(trks[revLabel].CSide() * GPUChainTracking::NSLICES / 2, trks[revLabel].GetParam().GetTZOffset()); + comp = fabsf(trks[i].GetParam().GetZ() + shift1) < fabsf(trks[revLabel].GetParam().GetZ() + shift2); + } + if (revLabel == -1 || !trks[revLabel].OK() || (trks[i].OK() && comp)) { + revLabel = i; } } - if (fake) { - mClusterParam[i].fakeAdjacent++; - } else { - mClusterParam[i].adjacent++; + } + } + // fill cluster adjacent status + for (unsigned int i = 0; i < GetNMCLabels(); i++) { + if (mClusterParam[i].attached == 0 && mClusterParam[i].fakeAttached == 0) { + int attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; + if (attach & gputpcgmmergertypes::attachFlagMask) { + int track = attach & gputpcgmmergertypes::attachTrackMask; + mcLabelI_t trackL = mTrackMCLabels[track]; + bool fake = true; + for (int j = 0; j < GetMCLabelNID(i); j++) { + // GPUInfo("Attach %x Track %d / %d:%d", attach, track, j, GetMCLabelID(i, j)); + if (trackL == GetMCLabel(i, j)) { + fake = false; + break; + } + } + if (fake) { + mClusterParam[i].fakeAdjacent++; + } else { + mClusterParam[i].adjacent++; + } } } } @@ -749,7 +911,7 @@ GPUCA_OPENMP(parallel for) for (unsigned int k = 0; k < GetNMCTracks(0); k++) { allowMCLabels[k] = false; } - for (int i = 0; i < merger.NOutputTracks(); i++) { + for (unsigned int i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { if (!mGoodTracks[mNEvents - 1][i]) { continue; } @@ -763,9 +925,9 @@ GPUCA_OPENMP(parallel for) } } - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[i]; + const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[i]; for (unsigned int j = 0; j < track.NClusters(); j++) { - int hitId = merger.Clusters()[track.FirstClusterRef() + j].num; + int hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + j].num; if (GetMCLabelNID(hitId)) { int mcID = GetMCLabelID(hitId, 0); if (mcID >= 0) { @@ -783,16 +945,14 @@ GPUCA_OPENMP(parallel for) } } } + if (QA_TIMING) { + GPUInfo("QA Time: Cluster attach status:\t\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); + } if (matchOnly) { return; } - if (QA_TIMING) { - GPUInfo("QA Time: Assign Track Labels:\t\t%6.0f us", timer.GetCurrentElapsedTime() * 1e6); - } - timer.ResetStart(); - // Recompute fNWeightCls (might have changed after merging events into timeframes) for (unsigned int iCol = 0; iCol < GetNMCCollissions(); iCol++) { for (unsigned int i = 0; i < GetNMCTracks(iCol); i++) { @@ -813,10 +973,10 @@ GPUCA_OPENMP(parallel for) } } if (QA_TIMING) { - GPUInfo("QA Time: Compute cluster label weights:\t%6.0f us", timer.GetCurrentElapsedTime() * 1e6); + GPUInfo("QA Time: Compute cluster label weights:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); } - timer.ResetStart(); + // Compute MC Track Parameters for MC Tracks GPUCA_OPENMP(parallel for) for (unsigned int iCol = 0; iCol < GetNMCCollissions(); iCol++) { for (unsigned int i = 0; i < GetNMCTracks(iCol); i++) { @@ -837,330 +997,432 @@ GPUCA_OPENMP(parallel for) } } } - // Compute MC Track Parameters for MC Tracks - // Fill Efficiency Histograms - for (unsigned int iCol = 0; iCol < GetNMCCollissions(); iCol++) { - for (unsigned int i = 0; i < GetNMCTracks(iCol); i++) { - if ((mMCTrackMin != -1 && (int)i < mMCTrackMin) || (mMCTrackMax != -1 && (int)i >= mMCTrackMax)) { - continue; - } - const mcInfo_t& info = GetMCTrack(i, iCol); - const additionalMCParameters& mc2 = mMCParam[iCol][i]; - if (mc2.nWeightCls == 0.f) { - continue; - } - const float& mcpt = mc2.pt; - const float& mcphi = mc2.phi; - const float& mceta = mc2.eta; - - if (info.prim && info.primDaughters) { - continue; - } - if (mc2.nWeightCls < MIN_WEIGHT_CLS) { - continue; - } - int findable = mc2.nWeightCls >= FINDABLE_WEIGHT_CLS; - if (info.pid < 0) { - continue; - } - if (info.charge == 0.f) { - continue; - } - if (mConfig.filterCharge && info.charge * mConfig.filterCharge < 0) { - continue; - } - if (mConfig.filterPID >= 0 && info.pid != mConfig.filterPID) { - continue; - } - - if (fabsf(mceta) > ETA_MAX || mcpt < PT_MIN || mcpt > PT_MAX) { - continue; - } + if (QA_TIMING) { + GPUInfo("QA Time: Compute track mc parameters:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); + } - float alpha = std::atan2(info.y, info.x); - alpha /= M_PI / 9.f; - alpha = std::floor(alpha); - alpha *= M_PI / 9.f; - alpha += M_PI / 18.f; + // Fill Efficiency Histograms + if (mQATasks & taskTrackingEff) { + for (unsigned int iCol = 0; iCol < GetNMCCollissions(); iCol++) { + for (unsigned int i = 0; i < GetNMCTracks(iCol); i++) { + if ((mMCTrackMin != -1 && (int)i < mMCTrackMin) || (mMCTrackMax != -1 && (int)i >= mMCTrackMax)) { + continue; + } + const mcInfo_t& info = GetMCTrack(i, iCol); + const additionalMCParameters& mc2 = mMCParam[iCol][i]; + if (mc2.nWeightCls == 0.f) { + continue; + } + const float& mcpt = mc2.pt; + const float& mcphi = mc2.phi; + const float& mceta = mc2.eta; - float c = std::cos(alpha); - float s = std::sin(alpha); - float localY = -info.x * s + info.y * c; + if (info.primDaughters) { + continue; + } + if (mc2.nWeightCls < MIN_WEIGHT_CLS) { + continue; + } + int findable = mc2.nWeightCls >= FINDABLE_WEIGHT_CLS; + if (info.pid < 0) { + continue; + } + if (info.charge == 0.f) { + continue; + } + if (mConfig.filterCharge && info.charge * mConfig.filterCharge < 0) { + continue; + } + if (mConfig.filterPID >= 0 && info.pid != mConfig.filterPID) { + continue; + } - for (int j = 0; j < 4; j++) { - for (int k = 0; k < 2; k++) { - if (k == 0 && findable == 0) { - continue; - } + if (fabsf(mceta) > ETA_MAX || mcpt < PT_MIN || mcpt > PT_MAX) { + continue; + } - int val = (j == 0) ? (mRecTracks[iCol][i] ? 1 : 0) : (j == 1) ? (mRecTracks[iCol][i] ? mRecTracks[iCol][i] - 1 : 0) : (j == 2) ? mFakeTracks[iCol][i] : 1; + float alpha = std::atan2(info.y, info.x); + alpha /= M_PI / 9.f; + alpha = std::floor(alpha); + alpha *= M_PI / 9.f; + alpha += M_PI / 18.f; - for (int l = 0; l < 5; l++) { - if (info.prim && mcpt < PT_MIN_PRIM) { - continue; - } - if (l != 3 && fabsf(mceta) > ETA_MAX2) { - continue; - } - if (l < 4 && mcpt < 1.f / mConfig.qpt) { + float c = std::cos(alpha); + float s = std::sin(alpha); + float localY = -info.x * s + info.y * c; + + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 2; k++) { + if (k == 0 && findable == 0) { continue; } - float pos = l == 0 ? localY : l == 1 ? info.z : l == 2 ? mcphi : l == 3 ? mceta : mcpt; + int val = (j == 0) ? (mRecTracks[iCol][i] ? 1 : 0) : (j == 1) ? (mRecTracks[iCol][i] ? mRecTracks[iCol][i] - 1 : 0) : (j == 2) ? mFakeTracks[iCol][i] : 1; + + for (int l = 0; l < 5; l++) { + if (info.prim && mcpt < PT_MIN_PRIM) { + continue; + } + if (l != 3 && fabsf(mceta) > ETA_MAX2) { + continue; + } + if (l < 4 && mcpt < 1.f / mConfig.qpt) { + continue; + } + + float pos = l == 0 ? localY : l == 1 ? info.z : l == 2 ? mcphi : l == 3 ? mceta : mcpt; - mEff[j][k][!info.prim][l][0]->Fill(pos, val); + mEff[j][k][!info.prim][l][0]->Fill(pos, val); + } } } } } + if (QA_TIMING) { + GPUInfo("QA Time: Fill efficiency histograms:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); + } } - if (QA_TIMING) { - GPUInfo("QA Time: Fill efficiency histograms:\t%6.0f us", timer.GetCurrentElapsedTime() * 1e6); - } - timer.ResetStart(); // Fill Resolution Histograms - GPUTPCGMPropagator prop; - prop.SetMaxSinPhi(.999); - prop.SetMaterialTPC(); - prop.SetPolynomialField(&merger.Param().polynomialField); - prop.SetToyMCEventsFlag(merger.Param().ToyMCEventsFlag); - - for (int i = 0; i < merger.NOutputTracks(); i++) { - if (mConfig.writeMCLabels) { - std::vector<int>& labelBuffer = mcLabelBuffer[mNEvents - 1]; - labelBuffer[i] = mTrackMCLabels[i].getTrackID(); - } - if (mTrackMCLabels[i].isFake()) { - continue; - } - const mcInfo_t& mc1 = GetMCTrack(mTrackMCLabels[i]); - const additionalMCParameters& mc2 = GetMCTrackObj(mMCParam, mTrackMCLabels[i]); - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[i]; - - if ((mMCTrackMin != -1 && mTrackMCLabels[i].getTrackID() < mMCTrackMin) || (mMCTrackMax != -1 && mTrackMCLabels[i].getTrackID() >= mMCTrackMax)) { - continue; - } - - if (!track.OK()) { - continue; - } - if (fabsf(mc2.eta) > ETA_MAX || mc2.pt < PT_MIN || mc2.pt > PT_MAX) { - continue; - } - if (mc1.charge == 0.f) { - continue; - } - if (mc1.pid < 0) { - continue; - } - if (mConfig.filterCharge && mc1.charge * mConfig.filterCharge < 0) { - continue; - } - if (mConfig.filterPID >= 0 && mc1.pid != mConfig.filterPID) { - continue; - } - if (mc2.nWeightCls < MIN_WEIGHT_CLS) { - continue; - } - if (mConfig.resPrimaries == 1 && (!mc1.prim || mc1.primDaughters)) { - continue; - } else if (mConfig.resPrimaries == 2 && (mc1.prim || mc1.primDaughters)) { - continue; - } - if (GetMCTrackObj(mTrackMCLabelsReverse, mTrackMCLabels[i]) != i) { - continue; - } - - float mclocal[4]; // Rotated x,y,Px,Py mc-coordinates - the MC data should be rotated since the track is propagated best along x - float c = std::cos(track.GetAlpha()); - float s = std::sin(track.GetAlpha()); - float x = mc1.x; - float y = mc1.y; - mclocal[0] = x * c + y * s; - mclocal[1] = -x * s + y * c; - float px = mc1.pX; - float py = mc1.pY; - mclocal[2] = px * c + py * s; - mclocal[3] = -px * s + py * c; - - GPUTPCGMTrackParam param = track.GetParam(); + if (mQATasks & (taskTrackingRes | taskTrackingResPull)) { + GPUTPCGMPropagator prop; + prop.SetMaxSinPhi(.999); + prop.SetMaterialTPC(); + prop.SetPolynomialField(&mTracking->GetParam().polynomialField); + prop.SetToyMCEventsFlag(mTracking->GetParam().par.ToyMCEventsFlag); + + for (unsigned int i = 0; i < mTrackMCLabels.size(); i++) { + if (mConfig.writeMCLabels) { + std::vector<int>& labelBuffer = mcLabelBuffer[mNEvents - 1]; + labelBuffer[i] = mTrackMCLabels[i].getTrackID(); + } + if (mTrackMCLabels[i].isFake()) { + continue; + } + const mcInfo_t& mc1 = GetMCTrack(mTrackMCLabels[i]); + const additionalMCParameters& mc2 = GetMCTrackObj(mMCParam, mTrackMCLabels[i]); - if (mclocal[0] < TRACK_EXPECTED_REFERENCE_X - 3) { - continue; - } - if (mclocal[0] > param.GetX() + 20) { - continue; - } - if (param.GetX() > mConfig.maxResX) { - continue; - } + if (mc1.primDaughters) { + continue; + } + if (!tracksExternal) { + if (!mTracking->mIOPtrs.mergedTracks[i].OK()) { + continue; + } + if (mTracking->mIOPtrs.mergedTracks[i].MergedLooper()) { + continue; + } + } + if ((mMCTrackMin != -1 && mTrackMCLabels[i].getTrackID() < mMCTrackMin) || (mMCTrackMax != -1 && mTrackMCLabels[i].getTrackID() >= mMCTrackMax)) { + continue; + } + if (fabsf(mc2.eta) > ETA_MAX || mc2.pt < PT_MIN || mc2.pt > PT_MAX) { + continue; + } + if (mc1.charge == 0.f) { + continue; + } + if (mc1.pid < 0) { + continue; + } + if (mConfig.filterCharge && mc1.charge * mConfig.filterCharge < 0) { + continue; + } + if (mConfig.filterPID >= 0 && mc1.pid != mConfig.filterPID) { + continue; + } + if (mc2.nWeightCls < MIN_WEIGHT_CLS) { + continue; + } + if (mConfig.resPrimaries == 1 && !mc1.prim) { + continue; + } else if (mConfig.resPrimaries == 2 && mc1.prim) { + continue; + } + if (GetMCTrackObj(mTrackMCLabelsReverse, mTrackMCLabels[i]) != (int)i) { + continue; + } - float alpha = track.GetAlpha(); - prop.SetTrack(¶m, alpha); - bool inFlyDirection = 0; -#ifdef GPUCA_TPC_GEOMETRY_O2 // ignore z here, larger difference in X due to shifted reference - if (mConfig.strict && (param.X() - mclocal[0]) * (param.X() - mclocal[0]) + (param.Y() - mclocal[1]) * (param.Y() - mclocal[1]) + (merger.Param().continuousMaxTimeBin ? 0 : ((param.Z() - mc1.z) * (param.Z() - mc1.z))) > (5 + abs(81 - TRACK_EXPECTED_REFERENCE_X)) * (5 + abs(81 - TRACK_EXPECTED_REFERENCE_X))) { -#else // Consider Z offset (pseudo-tf mc tracks have shifted z) - if (mConfig.strict && (param.X() - mclocal[0]) * (param.X() - mclocal[0]) + (param.Y() - mclocal[1]) * (param.Y() - mclocal[1]) + (param.Z() + param.TZOffset() - mc1.z) * (param.Z() + param.TZOffset() - mc1.z) > 25) { // TODO: fix TZOffset + GPUTPCGMTrackParam param; + float alpha = 0.f; + int side; + if (tracksExternal) { +#ifdef GPUCA_O2_LIB + for (int k = 0; k < 5; k++) { + param.Par()[k] = (*tracksExternal)[i].getParams()[k]; + } + for (int k = 0; k < 15; k++) { + param.Cov()[k] = (*tracksExternal)[i].getCov()[k]; + } + param.X() = (*tracksExternal)[i].getX(); + param.TZOffset() = (*tracksExternal)[i].getTime0(); + alpha = (*tracksExternal)[i].getAlpha(); + side = (*tracksExternal)[i].hasBothSidesClusters() ? 2 : ((*tracksExternal)[i].hasCSideClusters() ? 1 : 0); #endif - continue; - } + } else { + param = mTracking->mIOPtrs.mergedTracks[i].GetParam(); + alpha = mTracking->mIOPtrs.mergedTracks[i].GetAlpha(); + side = mTracking->mIOPtrs.mergedTracks[i].CCE() ? 2 : (mTracking->mIOPtrs.mergedTracks[i].CSide() ? 1 : 0); + } - if (prop.PropagateToXAlpha(mclocal[0], alpha, inFlyDirection)) { - continue; - } -#ifdef GPUCA_TPC_GEOMETRY_O2 // ignore z here, larger difference in X due to shifted reference - if (fabsf(param.Y() - mclocal[1]) > (mConfig.strict ? 1.f : 4.f) || (merger.Param().continuousMaxTimeBin == 0 && fabsf(param.Z() + param.TZOffset() - mc1.z) > (mConfig.strict ? 1.f : 4.f))) { // TODO: fix TZOffset here -#else - if (fabsf(param.Y() - mclocal[1]) > (mConfig.strict ? 1.f : 4.f) || fabsf(param.Z() + param.TZOffset() - mc1.z) > (mConfig.strict ? 1.f : 4.f)) { // TODO: fix TZOffset here + float mclocal[4]; // Rotated x,y,Px,Py mc-coordinates - the MC data should be rotated since the track is propagated best along x + float c = std::cos(alpha); + float s = std::sin(alpha); + float x = mc1.x; + float y = mc1.y; + mclocal[0] = x * c + y * s; + mclocal[1] = -x * s + y * c; + float px = mc1.pX; + float py = mc1.pY; + mclocal[2] = px * c + py * s; + mclocal[3] = -px * s + py * c; + + if (mclocal[0] < TRACK_EXPECTED_REFERENCE_X - 3) { + continue; + } + if (mclocal[0] > param.GetX() + 20) { + continue; + } + if (param.GetX() > mConfig.maxResX) { + continue; + } + + auto getdz = [this, ¶m, &mc1, &side]() { + if (!mTracking->GetParam().par.continuousMaxTimeBin) { + return param.Z() - mc1.z; + } +#ifdef GPUCA_TPC_GEOMETRY_O2 + if (!mTracking->GetParam().par.earlyTpcTransform) { + float shift = side == 2 ? 0 : mTracking->GetTPCTransform()->convDeltaTimeToDeltaZinTimeFrame(side * GPUChainTracking::NSLICES / 2, param.GetTZOffset() - mc1.t0); + return param.GetZ() + shift - mc1.z; + } #endif - continue; - } + return param.Z() + param.TZOffset() - mc1.z; + }; + + prop.SetTrack(¶m, alpha); + bool inFlyDirection = 0; + if (mConfig.strict) { + const float dx = param.X() - std::max<float>(mclocal[0], TRACK_EXPECTED_REFERENCE_X_DEFAULT); // Limit distance check if the O2 MC position is farther inside than the AliRoot MC position. + const float dy = param.Y() - mclocal[1]; + const float dz = getdz(); + if (dx * dx + dy * dy + dz * dz > 5.f * 5.f) { + continue; + } + } - float charge = mc1.charge > 0 ? 1.f : -1.f; + if (prop.PropagateToXAlpha(mclocal[0], alpha, inFlyDirection)) { + continue; + } + if (fabsf(param.Y() - mclocal[1]) > (mConfig.strict ? 1.f : 4.f) || fabsf(getdz()) > (mConfig.strict ? 1.f : 4.f)) { + continue; + } + float charge = mc1.charge > 0 ? 1.f : -1.f; - float deltaY = param.GetY() - mclocal[1]; - float deltaZ = param.GetZ() + param.TZOffset() - mc1.z; // TODO: fix TZOffset here - float deltaPhiNative = param.GetSinPhi() - mclocal[3] / mc2.pt; - float deltaPhi = std::asin(param.GetSinPhi()) - std::atan2(mclocal[3], mclocal[2]); - float deltaLambdaNative = param.GetDzDs() - mc1.pZ / mc2.pt; - float deltaLambda = std::atan(param.GetDzDs()) - std::atan2(mc1.pZ, mc2.pt); - float deltaPtNative = (param.GetQPt() - charge / mc2.pt) * charge; - float deltaPt = (fabsf(1.f / param.GetQPt()) - mc2.pt) / mc2.pt; + float deltaY = param.GetY() - mclocal[1]; + float deltaZ = getdz(); + float deltaPhiNative = param.GetSinPhi() - mclocal[3] / mc2.pt; + float deltaPhi = std::asin(param.GetSinPhi()) - std::atan2(mclocal[3], mclocal[2]); + float deltaLambdaNative = param.GetDzDs() - mc1.pZ / mc2.pt; + float deltaLambda = std::atan(param.GetDzDs()) - std::atan2(mc1.pZ, mc2.pt); + float deltaPtNative = (param.GetQPt() - charge / mc2.pt) * charge; + float deltaPt = (fabsf(1.f / param.GetQPt()) - mc2.pt) / mc2.pt; - float paramval[5] = {mclocal[1], mc1.z, mc2.phi, mc2.eta, mc2.pt}; - float resval[5] = {deltaY, deltaZ, mConfig.nativeFitResolutions ? deltaPhiNative : deltaPhi, mConfig.nativeFitResolutions ? deltaLambdaNative : deltaLambda, mConfig.nativeFitResolutions ? deltaPtNative : deltaPt}; - float pullval[5] = {deltaY / std::sqrt(param.GetErr2Y()), deltaZ / std::sqrt(param.GetErr2Z()), deltaPhiNative / std::sqrt(param.GetErr2SinPhi()), deltaLambdaNative / std::sqrt(param.GetErr2DzDs()), deltaPtNative / std::sqrt(param.GetErr2QPt())}; + float paramval[5] = {mclocal[1], mc1.z, mc2.phi, mc2.eta, mc2.pt}; + float resval[5] = {deltaY, deltaZ, mConfig.nativeFitResolutions ? deltaPhiNative : deltaPhi, mConfig.nativeFitResolutions ? deltaLambdaNative : deltaLambda, mConfig.nativeFitResolutions ? deltaPtNative : deltaPt}; + float pullval[5] = {deltaY / std::sqrt(param.GetErr2Y()), deltaZ / std::sqrt(param.GetErr2Z()), deltaPhiNative / std::sqrt(param.GetErr2SinPhi()), deltaLambdaNative / std::sqrt(param.GetErr2DzDs()), deltaPtNative / std::sqrt(param.GetErr2QPt())}; - for (int j = 0; j < 5; j++) { - for (int k = 0; k < 5; k++) { - if (k != 3 && fabsf(mc2.eta) > ETA_MAX2) { - continue; - } - if (k < 4 && mc2.pt < 1.f / mConfig.qpt) { - continue; + for (int j = 0; j < 5; j++) { + for (int k = 0; k < 5; k++) { + if (k != 3 && fabsf(mc2.eta) > ETA_MAX2) { + continue; + } + if (k < 4 && mc2.pt < 1.f / mConfig.qpt) { + continue; + } + if (mQATasks & taskTrackingRes) { + mRes2[j][k]->Fill(resval[j], paramval[k]); + } + if (mQATasks & taskTrackingResPull) { + mPull2[j][k]->Fill(pullval[j], paramval[k]); + } } - mRes2[j][k]->Fill(resval[j], paramval[k]); - mPull2[j][k]->Fill(pullval[j], paramval[k]); } } + if (QA_TIMING) { + GPUInfo("QA Time: Fill resolution histograms:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); + } } - if (QA_TIMING) { - GPUInfo("QA Time: Fill resolution histograms:\t%6.0f us", timer.GetCurrentElapsedTime() * 1e6); - } - timer.ResetStart(); - // Fill cluster histograms - for (int iTrk = 0; iTrk < merger.NOutputTracks(); iTrk++) { - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[iTrk]; - if (!track.OK()) { - continue; - } - if (!mTrackMCLabels[iTrk].isValid()) { + if (mQATasks & taskClusterAttach) { + // Fill cluster histograms + for (unsigned int iTrk = 0; iTrk < mTracking->mIOPtrs.nMergedTracks; iTrk++) { + const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[iTrk]; + if (!track.OK()) { + continue; + } + if (!mTrackMCLabels[iTrk].isValid()) { + for (unsigned int k = 0; k < track.NClusters(); k++) { + if (mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { + continue; + } + int hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; + float totalWeight = 0.; + for (int j = 0; j < GetMCLabelNID(hitId); j++) { + if (GetMCLabelID(hitId, j) >= 0 && GetMCTrackObj(mMCParam, GetMCLabel(hitId, j)).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { + totalWeight += GetMCLabelWeight(hitId, j); + } + } + int attach = mTracking->mIOPtrs.mergedTrackHitAttachment[hitId]; + CHECK_CLUSTER_STATE_NOCOUNT(); + if (totalWeight > 0) { + float weight = 1.f / (totalWeight * (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached)); + for (int j = 0; j < GetMCLabelNID(hitId); j++) { + mcLabelI_t label = GetMCLabel(hitId, j); + if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { + float pt = GetMCTrackObj(mMCParam, label).pt; + if (pt < PT_MIN_CLUST) { + pt = PT_MIN_CLUST; + } + mClusters[CL_fake]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + mClusters[CL_att_adj]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + if (GetMCTrackObj(mRecTracks, label)) { + mClusters[CL_tracks]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + } + mClusters[CL_all]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + if (protect || physics) { + mClusters[CL_prot]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + } + if (physics) { + mClusters[CL_physics]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + } + } + } + } else { + float weight = 1.f / (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached); + mClusters[CL_fake]->Fill(0.f, weight); + mClusters[CL_att_adj]->Fill(0.f, weight); + mClusters[CL_all]->Fill(0.f, weight); + mClusterCounts.nUnaccessible += weight; + if (protect || physics) { + mClusters[CL_prot]->Fill(0.f, weight); + } + if (physics) { + mClusters[CL_physics]->Fill(0.f, weight); + } + } + } + continue; + } + mcLabelI_t label = mTrackMCLabels[iTrk]; + if (mMCTrackMin != -1 && (label.getTrackID() < mMCTrackMin || label.getTrackID() >= mMCTrackMax)) { + continue; + } for (unsigned int k = 0; k < track.NClusters(); k++) { - if (merger.Clusters()[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { + if (mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { continue; } - int hitId = merger.Clusters()[track.FirstClusterRef() + k].num; - float totalWeight = 0.; + int hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; + float pt = GetMCTrackObj(mMCParam, label).pt; + if (pt < PT_MIN_CLUST) { + pt = PT_MIN_CLUST; + } + float weight = 1.f / (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached); + bool correct = false; for (int j = 0; j < GetMCLabelNID(hitId); j++) { - if (GetMCLabelID(hitId, j) >= 0 && GetMCTrackObj(mMCParam, GetMCLabel(hitId, j)).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { - totalWeight += GetMCLabelWeight(hitId, j); + if (label == GetMCLabel(hitId, j)) { + correct = true; + break; } } - int attach = merger.ClusterAttachment()[hitId]; + if (correct) { + mClusters[CL_attached]->Fill(pt, weight); + mClusters[CL_tracks]->Fill(pt, weight); + } else { + mClusters[CL_fake]->Fill(pt, weight); + } + mClusters[CL_att_adj]->Fill(pt, weight); + mClusters[CL_all]->Fill(pt, weight); + int attach = mTracking->mIOPtrs.mergedTrackHitAttachment[hitId]; CHECK_CLUSTER_STATE_NOCOUNT(); - if (totalWeight > 0) { - float weight = 1.f / (totalWeight * (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached)); - for (int j = 0; j < GetMCLabelNID(hitId); j++) { - mcLabelI_t label = GetMCLabel(hitId, j); - if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { - float pt = GetMCTrackObj(mMCParam, label).pt; - if (pt < PT_MIN_CLUST) { - pt = PT_MIN_CLUST; - } - mClusters[CL_fake]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); - mClusters[CL_att_adj]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); - if (GetMCTrackObj(mRecTracks, label)) { - mClusters[CL_tracks]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); - } - mClusters[CL_all]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); - if (protect || physics) { - mClusters[CL_prot]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); - } - if (physics) { - mClusters[CL_physics]->Fill(pt, GetMCLabelWeight(hitId, j) * weight); + if (protect || physics) { + mClusters[CL_prot]->Fill(pt, weight); + } + if (physics) { + mClusters[CL_physics]->Fill(pt, weight); + } + } + } + for (unsigned int i = 0; i < GetNMCLabels(); i++) { + if ((mMCTrackMin != -1 && GetMCLabelID(i, 0) < mMCTrackMin) || (mMCTrackMax != -1 && GetMCLabelID(i, 0) >= mMCTrackMax)) { + continue; + } + if (mClusterParam[i].attached || mClusterParam[i].fakeAttached) { + continue; + } + int attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; + CHECK_CLUSTER_STATE_NOCOUNT(); + if (mClusterParam[i].adjacent) { + int label = mTracking->mIOPtrs.mergedTrackHitAttachment[i] & gputpcgmmergertypes::attachTrackMask; + if (!mTrackMCLabels[label].isValid()) { + float totalWeight = 0.; + for (int j = 0; j < GetMCLabelNID(i); j++) { + mcLabelI_t labelT = GetMCLabel(i, j); + if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { + totalWeight += GetMCLabelWeight(i, j); + } + } + float weight = 1.f / totalWeight; + if (totalWeight > 0) { + for (int j = 0; j < GetMCLabelNID(i); j++) { + mcLabelI_t labelT = GetMCLabel(i, j); + if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { + float pt = GetMCTrackObj(mMCParam, labelT).pt; + if (pt < PT_MIN_CLUST) { + pt = PT_MIN_CLUST; + } + if (GetMCTrackObj(mRecTracks, labelT)) { + mClusters[CL_tracks]->Fill(pt, GetMCLabelWeight(i, j) * weight); + } + mClusters[CL_att_adj]->Fill(pt, GetMCLabelWeight(i, j) * weight); + mClusters[CL_fakeAdj]->Fill(pt, GetMCLabelWeight(i, j) * weight); + mClusters[CL_all]->Fill(pt, GetMCLabelWeight(i, j) * weight); + if (protect || physics) { + mClusters[CL_prot]->Fill(pt, GetMCLabelWeight(i, j) * weight); + } + if (physics) { + mClusters[CL_physics]->Fill(pt, GetMCLabelWeight(i, j) * weight); + } } } + } else { + mClusters[CL_att_adj]->Fill(0.f, 1.f); + mClusters[CL_fakeAdj]->Fill(0.f, 1.f); + mClusters[CL_all]->Fill(0.f, 1.f); + mClusterCounts.nUnaccessible++; + if (protect || physics) { + mClusters[CL_prot]->Fill(0.f, 1.f); + } + if (physics) { + mClusters[CL_physics]->Fill(0.f, 1.f); + } } } else { - float weight = 1.f / (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached); - mClusters[CL_fake]->Fill(0.f, weight); - mClusters[CL_att_adj]->Fill(0.f, weight); - mClusters[CL_all]->Fill(0.f, weight); - mNRecClustersUnaccessible += weight; + float pt = GetMCTrackObj(mMCParam, mTrackMCLabels[label]).pt; + if (pt < PT_MIN_CLUST) { + pt = PT_MIN_CLUST; + } + mClusters[CL_att_adj]->Fill(pt, 1.f); + mClusters[CL_tracks]->Fill(pt, 1.f); + mClusters[CL_all]->Fill(pt, 1.f); if (protect || physics) { - mClusters[CL_prot]->Fill(0.f, weight); + mClusters[CL_prot]->Fill(pt, 1.f); } if (physics) { - mClusters[CL_physics]->Fill(0.f, weight); + mClusters[CL_physics]->Fill(pt, 1.f); } } - } - continue; - } - mcLabelI_t label = mTrackMCLabels[iTrk]; - if (mMCTrackMin != -1 && (label.getTrackID() < mMCTrackMin || label.getTrackID() >= mMCTrackMax)) { - continue; - } - for (unsigned int k = 0; k < track.NClusters(); k++) { - if (merger.Clusters()[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { - continue; - } - int hitId = merger.Clusters()[track.FirstClusterRef() + k].num; - float pt = GetMCTrackObj(mMCParam, label).pt; - if (pt < PT_MIN_CLUST) { - pt = PT_MIN_CLUST; - } - float weight = 1.f / (mClusterParam[hitId].attached + mClusterParam[hitId].fakeAttached); - bool correct = false; - for (int j = 0; j < GetMCLabelNID(hitId); j++) { - if (label == GetMCLabel(hitId, j)) { - correct = true; - break; - } - } - if (correct) { - mClusters[CL_attached]->Fill(pt, weight); - mClusters[CL_tracks]->Fill(pt, weight); } else { - mClusters[CL_fake]->Fill(pt, weight); - } - mClusters[CL_att_adj]->Fill(pt, weight); - mClusters[CL_all]->Fill(pt, weight); - int attach = merger.ClusterAttachment()[hitId]; - CHECK_CLUSTER_STATE_NOCOUNT(); - if (protect || physics) { - mClusters[CL_prot]->Fill(pt, weight); - } - if (physics) { - mClusters[CL_physics]->Fill(pt, weight); - } - } - } - for (unsigned int i = 0; i < GetNMCLabels(); i++) { - if ((mMCTrackMin != -1 && GetMCLabelID(i, 0) < mMCTrackMin) || (mMCTrackMax != -1 && GetMCLabelID(i, 0) >= mMCTrackMax)) { - continue; - } - if (mClusterParam[i].attached || mClusterParam[i].fakeAttached) { - continue; - } - int attach = merger.ClusterAttachment()[i]; - CHECK_CLUSTER_STATE_NOCOUNT(); - if (mClusterParam[i].adjacent) { - int label = merger.ClusterAttachment()[i] & GPUTPCGMMergerTypes::attachTrackMask; - if (!mTrackMCLabels[label].isValid()) { float totalWeight = 0.; for (int j = 0; j < GetMCLabelNID(i); j++) { mcLabelI_t labelT = GetMCLabel(i, j); @@ -1168,34 +1430,42 @@ GPUCA_OPENMP(parallel for) totalWeight += GetMCLabelWeight(i, j); } } - float weight = 1.f / totalWeight; if (totalWeight > 0) { for (int j = 0; j < GetMCLabelNID(i); j++) { - mcLabelI_t labelT = GetMCLabel(i, j); - if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { - float pt = GetMCTrackObj(mMCParam, labelT).pt; + mcLabelI_t label = GetMCLabel(i, j); + if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { + float pt = GetMCTrackObj(mMCParam, label).pt; if (pt < PT_MIN_CLUST) { pt = PT_MIN_CLUST; } - if (GetMCTrackObj(mRecTracks, labelT)) { - mClusters[CL_tracks]->Fill(pt, GetMCLabelWeight(i, j) * weight); + float weight = GetMCLabelWeight(i, j) / totalWeight; + if (mClusterParam[i].fakeAdjacent) { + mClusters[CL_fakeAdj]->Fill(pt, weight); + } + if (mClusterParam[i].fakeAdjacent) { + mClusters[CL_att_adj]->Fill(pt, weight); } - mClusters[CL_att_adj]->Fill(pt, GetMCLabelWeight(i, j) * weight); - mClusters[CL_fakeAdj]->Fill(pt, GetMCLabelWeight(i, j) * weight); - mClusters[CL_all]->Fill(pt, GetMCLabelWeight(i, j) * weight); + if (GetMCTrackObj(mRecTracks, label)) { + mClusters[CL_tracks]->Fill(pt, weight); + } + mClusters[CL_all]->Fill(pt, weight); if (protect || physics) { - mClusters[CL_prot]->Fill(pt, GetMCLabelWeight(i, j) * weight); + mClusters[CL_prot]->Fill(pt, weight); } if (physics) { - mClusters[CL_physics]->Fill(pt, GetMCLabelWeight(i, j) * weight); + mClusters[CL_physics]->Fill(pt, weight); } } } } else { - mClusters[CL_att_adj]->Fill(0.f, 1.f); - mClusters[CL_fakeAdj]->Fill(0.f, 1.f); + if (mClusterParam[i].fakeAdjacent) { + mClusters[CL_fakeAdj]->Fill(0.f, 1.f); + } + if (mClusterParam[i].fakeAdjacent) { + mClusters[CL_att_adj]->Fill(0.f, 1.f); + } mClusters[CL_all]->Fill(0.f, 1.f); - mNRecClustersUnaccessible++; + mClusterCounts.nUnaccessible++; if (protect || physics) { mClusters[CL_prot]->Fill(0.f, 1.f); } @@ -1203,160 +1473,99 @@ GPUCA_OPENMP(parallel for) mClusters[CL_physics]->Fill(0.f, 1.f); } } - } else { - float pt = GetMCTrackObj(mMCParam, mTrackMCLabels[label]).pt; - if (pt < PT_MIN_CLUST) { - pt = PT_MIN_CLUST; - } - mClusters[CL_att_adj]->Fill(pt, 1.f); - mClusters[CL_tracks]->Fill(pt, 1.f); - mClusters[CL_all]->Fill(pt, 1.f); - if (protect || physics) { - mClusters[CL_prot]->Fill(pt, 1.f); - } - if (physics) { - mClusters[CL_physics]->Fill(pt, 1.f); - } - } - } else { - float totalWeight = 0.; - for (int j = 0; j < GetMCLabelNID(i); j++) { - mcLabelI_t labelT = GetMCLabel(i, j); - if (!labelT.isFake() && GetMCTrackObj(mMCParam, labelT).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { - totalWeight += GetMCLabelWeight(i, j); - } - } - if (totalWeight > 0) { - for (int j = 0; j < GetMCLabelNID(i); j++) { - mcLabelI_t label = GetMCLabel(i, j); - if (!label.isFake() && GetMCTrackObj(mMCParam, label).pt > GPUCA_MIN_TRACK_PT_DEFAULT) { - float pt = GetMCTrackObj(mMCParam, label).pt; - if (pt < PT_MIN_CLUST) { - pt = PT_MIN_CLUST; - } - float weight = GetMCLabelWeight(i, j) / totalWeight; - if (mClusterParam[i].fakeAdjacent) { - mClusters[CL_fakeAdj]->Fill(pt, weight); - } - if (mClusterParam[i].fakeAdjacent) { - mClusters[CL_att_adj]->Fill(pt, weight); - } - if (GetMCTrackObj(mRecTracks, label)) { - mClusters[CL_tracks]->Fill(pt, weight); - } - mClusters[CL_all]->Fill(pt, weight); - if (protect || physics) { - mClusters[CL_prot]->Fill(pt, weight); - } - if (physics) { - mClusters[CL_physics]->Fill(pt, weight); - } - } - } - } else { - if (mClusterParam[i].fakeAdjacent) { - mClusters[CL_fakeAdj]->Fill(0.f, 1.f); - } - if (mClusterParam[i].fakeAdjacent) { - mClusters[CL_att_adj]->Fill(0.f, 1.f); - } - mClusters[CL_all]->Fill(0.f, 1.f); - mNRecClustersUnaccessible++; - if (protect || physics) { - mClusters[CL_prot]->Fill(0.f, 1.f); - } - if (physics) { - mClusters[CL_physics]->Fill(0.f, 1.f); - } } } - } - if (QA_TIMING) { - GPUInfo("QA Time: Fill cluster histograms:\t%6.0f us", timer.GetCurrentElapsedTime() * 1e6); + if (QA_TIMING) { + GPUInfo("QA Time: Fill cluster histograms:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); + } } - timer.ResetStart(); } else if (!mConfig.inputHistogramsOnly) { - GPUWarning("No MC information available, cannot run QA!"); + GPUWarning("No MC information available, only running partial TPC QA!"); } - // Fill other histograms - for (int i = 0; i < merger.NOutputTracks(); i++) { - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[i]; - if (!track.OK()) { - continue; + if (mQATasks & taskTrackStatistics) { + // Fill track statistic histograms + for (unsigned int i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { + const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[i]; + if (!track.OK()) { + continue; + } + mTracks->Fill(1.f / fabsf(track.GetParam().GetQPt())); + mNCl->Fill(track.NClustersFitted()); } - mTracks->Fill(1.f / fabsf(track.GetParam().GetQPt())); - mNCl->Fill(track.NClustersFitted()); } - for (unsigned int i = 0; i < merger.NMaxClusters(); i++) { - int attach = merger.ClusterAttachment()[i]; - CHECK_CLUSTER_STATE(); + if (mQATasks & taskClusterCounts) { + unsigned int nCl = clNative ? clNative->nClustersTotal : mTracking->GetTPCMerger().NMaxClusters(); + for (unsigned int i = 0; i < nCl; i++) { + int attach = mTracking->mIOPtrs.mergedTrackHitAttachment[i]; + CHECK_CLUSTER_STATE(); - if (mcAvail) { - float totalWeight = 0, weight400 = 0, weight40 = 0; - for (int j = 0; j < GetMCLabelNID(i); j++) { - const auto& label = GetMCLabel(i, j); - if (GetMCLabelID(label) >= 0) { - totalWeight += GetMCLabelWeight(label); - if (GetMCTrackObj(mMCParam, label).pt >= 0.4) { - weight400 += GetMCLabelWeight(label); - } - if (GetMCTrackObj(mMCParam, label).pt <= 0.04) { - weight40 += GetMCLabelWeight(label); + if (mcAvail) { + float totalWeight = 0, weight400 = 0, weight40 = 0; + for (int j = 0; j < GetMCLabelNID(i); j++) { + const auto& label = GetMCLabel(i, j); + if (GetMCLabelID(label) >= 0) { + totalWeight += GetMCLabelWeight(label); + if (GetMCTrackObj(mMCParam, label).pt >= 0.4) { + weight400 += GetMCLabelWeight(label); + } + if (GetMCTrackObj(mMCParam, label).pt <= 0.04) { + weight40 += GetMCLabelWeight(label); + } } } - } - if (totalWeight > 0 && 10.f * weight400 >= totalWeight) { - if (!unattached && !protect && !physics) { - mNRecClustersFakeRemove400++; - int totalFake = weight400 < 0.9f * totalWeight; - if (totalFake) { - mNRecClustersFullFakeRemove400++; + if (totalWeight > 0 && 10.f * weight400 >= totalWeight) { + if (!unattached && !protect && !physics) { + mClusterCounts.nFakeRemove400++; + int totalFake = weight400 < 0.9f * totalWeight; + if (totalFake) { + mClusterCounts.nFullFakeRemove400++; + } + /*printf("Fake removal (%d): Hit %7d, attached %d lowPt %d looper %d tube200 %d highIncl %d tube %d bad %d recPt %7.2f recLabel %6d", totalFake, i, (int) (mClusterParam[i].attached || mClusterParam[i].fakeAttached), + (int) lowPt, (int) ((attach & gputpcgmmergertypes::attachGoodLeg) == 0), (int) ((attach & gputpcgmmergertypes::attachTube) && mev200), + (int) ((attach & gputpcgmmergertypes::attachHighIncl) != 0), (int) ((attach & gputpcgmmergertypes::attachTube) != 0), (int) ((attach & gputpcgmmergertypes::attachGood) == 0), + fabsf(qpt) > 0 ? 1.f / qpt : 0.f, id); + for (int j = 0;j < GetMCLabelNID(i);j++) + { + //if (GetMCLabelID(i, j) < 0) break; + printf(" - label%d %6d weight %5d", j, GetMCLabelID(i, j), (int) GetMCLabelWeight(i, j)); + if (GetMCLabelID(i, j) >= 0) printf(" - pt %7.2f", mMCParam[GetMCLabelID(i, j)].pt); + else printf(" "); + } + printf("\n");*/ } - /*printf("Fake removal (%d): Hit %7d, attached %d lowPt %d looper %d tube200 %d highIncl %d tube %d bad %d recPt %7.2f recLabel %6d", totalFake, i, (int) (mClusterParam[i].attached || mClusterParam[i].fakeAttached), - (int) lowPt, (int) ((attach & GPUTPCGMMergerTypes::attachGoodLeg) == 0), (int) ((attach & GPUTPCGMMergerTypes::attachTube) && mev200), - (int) ((attach & GPUTPCGMMergerTypes::attachHighIncl) != 0), (int) ((attach & GPUTPCGMMergerTypes::attachTube) != 0), (int) ((attach & GPUTPCGMMergerTypes::attachGood) == 0), - fabsf(qpt) > 0 ? 1.f / qpt : 0.f, id); - for (int j = 0;j < GetMCLabelNID(i);j++) - { - //if (GetMCLabelID(i, j) < 0) break; - printf(" - label%d %6d weight %5d", j, GetMCLabelID(i, j), (int) GetMCLabelWeight(i, j)); - if (GetMCLabelID(i, j) >= 0) printf(" - pt %7.2f", mMCParam[GetMCLabelID(i, j)].pt); - else printf(" "); + mClusterCounts.nAbove400++; + } + if (totalWeight > 0 && weight40 >= 0.9 * totalWeight) { + mClusterCounts.nBelow40++; + if (protect || physics) { + mClusterCounts.nFakeProtect40++; } - printf("\n");*/ } - mNRecClustersAbove400++; - } - if (totalWeight == 0 && weight40 >= 0.9 * totalWeight) { - mNRecClustersBelow40++; - if (protect || physics) { - mNRecClustersFakeProtect40++; + } else { + mClusterCounts.nTotal++; + if (physics) { + mClusterCounts.nPhysics++; + } + if (physics || protect) { + mClusterCounts.nProt++; + } + if (unattached) { + mClusterCounts.nUnattached++; } - } - } else { - mNRecClustersTotal++; - if (physics) { - mNRecClustersPhysics++; - } - if (physics || protect) { - mNRecClustersProt++; - } - if (unattached) { - mNRecClustersUnattached++; } } } if (QA_TIMING) { - GPUInfo("QA Time: Others:\t%6.0f us", timer.GetCurrentElapsedTime() * 1e6); + GPUInfo("QA Time: Others:\t%6.0f us", timer.GetCurrentElapsedTime(true) * 1e6); } // Create CSV DumpTrackHits if (mConfig.csvDump) { - if (!merger.Param().earlyTpcTransform) { + if (!mTracking->GetParam().par.earlyTpcTransform) { GPUError("Unsupported settings for csv dump\n"); return; } @@ -1369,16 +1578,16 @@ GPUCA_OPENMP(parallel for) std::vector<float> clusterInfo(totalNCls); memset(clusterInfo.data(), 0, clusterInfo.size() * sizeof(clusterInfo[0])); - for (int i = 0; i < merger.NOutputTracks(); i++) { - const GPUTPCGMMergedTrack& track = merger.OutputTracks()[i]; + for (unsigned int i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { + const GPUTPCGMMergedTrack& track = mTracking->mIOPtrs.mergedTracks[i]; if (!track.OK()) { continue; } for (unsigned int k = 0; k < track.NClusters(); k++) { - if (merger.Clusters()[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { + if (mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject) { continue; } - int hitId = merger.Clusters()[track.FirstClusterRef() + k].num; + int hitId = mTracking->mIOPtrs.mergedTrackHits[track.FirstClusterRef() + k].num; float pt = fabsf(1.f / track.GetParam().GetQPt()); if (pt > clusterInfo[hitId]) { clusterInfo[hitId] = pt; @@ -1396,7 +1605,7 @@ GPUCA_OPENMP(parallel for) const auto& cl = mTracking->mIOPtrs.clusterData[iSlice][i]; float x, y, z; const int cid = cl.id; - merger.Param().Slice2Global(iSlice, cl.x, cl.y, cl.z, &x, &y, &z); + mTracking->GetParam().Slice2Global(iSlice, cl.x, cl.y, cl.z, &x, &y, &z); float totalWeight = 0.f; if (mcPresent()) { @@ -1432,7 +1641,7 @@ GPUCA_OPENMP(parallel for) } dumpClTot++; - int attach = merger.ClusterAttachment()[cid]; + int attach = mTracking->mIOPtrs.mergedTrackHitAttachment[cid]; CHECK_CLUSTER_STATE(); if (protect || physics) { continue; @@ -1471,9 +1680,9 @@ GPUCA_OPENMP(parallel for) void GPUQA::GetName(char* fname, int k) { const int nNewInput = mConfig.inputHistogramsOnly ? 0 : 1; - if (k || mConfig.inputHistogramsOnly || mConfig.name) { + if (k || mConfig.inputHistogramsOnly || mConfig.name.size()) { if (!(mConfig.inputHistogramsOnly || k)) { - snprintf(fname, 1024, "%s - ", mConfig.name); + snprintf(fname, 1024, "%s - ", mConfig.name.c_str()); } else if (mConfig.compareInputNames.size() > (unsigned)(k - nNewInput)) { snprintf(fname, 1024, "%s - ", mConfig.compareInputNames[k - nNewInput]); } else { @@ -1489,7 +1698,7 @@ void GPUQA::GetName(char* fname, int k) } template <class T> -T* GPUQA::GetHist(T*& ee, std::vector<TFile*>& tin, int k, int nNewInput) +T* GPUQA::GetHist(T*& ee, std::vector<std::unique_ptr<TFile>>& tin, int k, int nNewInput) { T* e = ee; if ((mConfig.inputHistogramsOnly || k) && (e = dynamic_cast<T*>(tin[k - nNewInput]->Get(e->GetName()))) == nullptr) { @@ -1500,506 +1709,617 @@ T* GPUQA::GetHist(T*& ee, std::vector<TFile*>& tin, int k, int nNewInput) return (e); } -int GPUQA::DrawQAHistograms() +void GPUQA::DrawQAHistogramsCleanup() +{ + clearGarbagageCollector(); +} + +void GPUQA::resetHists() { if (!mQAInitialized) { - return 1; + throw std::runtime_error("QA not initialized"); } + if (mHaveExternalHists) { + throw std::runtime_error("Cannot reset external hists"); + } + for (auto& h : *mHist1D) { + h.Reset(); + } + for (auto& h : *mHist2D) { + h.Reset(); + } + for (auto& h : *mHist1Dd) { + h.Reset(); + } +} + +int GPUQA::DrawQAHistograms(TObjArray* qcout) +{ + if (!mQAInitialized) { + throw std::runtime_error("QA not initialized"); + } + + std::vector<Color_t> colorNums(COLORCOUNT); + for (int i = 0; i < COLORCOUNT; i++) { + colorNums[i] = qcout ? defaultColorNUms[i] : mColors[i]->GetNumber(); + } + bool mcAvail = mcPresent(); char name[2048], fname[1024]; const int nNewInput = mConfig.inputHistogramsOnly ? 0 : 1; const int ConfigNumInputs = nNewInput + mConfig.compareInputs.size(); - std::vector<TFile*> tin(mConfig.compareInputs.size()); + std::vector<std::unique_ptr<TFile>> tin; for (unsigned int i = 0; i < mConfig.compareInputs.size(); i++) { - tin[i] = new TFile(mConfig.compareInputs[i]); + tin.emplace_back(std::make_unique<TFile>(mConfig.compareInputs[i])); } - TFile* tout = nullptr; - if (mConfig.output) { - tout = new TFile(mConfig.output, "RECREATE"); + std::unique_ptr<TFile> tout = nullptr; + if (mConfig.output.size()) { + tout = std::make_unique<TFile>(mConfig.output.c_str(), "RECREATE"); } - float legendSpacingString = 0.025; - for (int i = 0; i < ConfigNumInputs; i++) { - GetName(fname, i); - if (strlen(fname) * 0.006 > legendSpacingString) { - legendSpacingString = strlen(fname) * 0.006; + if (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) { + float legendSpacingString = 0.025; + for (int i = 0; i < ConfigNumInputs; i++) { + GetName(fname, i); + if (strlen(fname) * 0.006 > legendSpacingString) { + legendSpacingString = strlen(fname) * 0.006; + } } - } - - // Create Canvas / Pads for Efficiency Histograms - for (int ii = 0; ii < 6; ii++) { - int i = ii == 5 ? 4 : ii; - sprintf(fname, "ceff_%d", ii); - sprintf(name, "Efficiency versus %s", VSPARAMETER_NAMES[i]); - mCEff[ii] = new TCanvas(fname, name, 0, 0, 700, 700. * 2. / 3.); - mCEff[ii]->cd(); - float dy = 1. / 2.; - mPEff[ii][0] = new TPad("p0", "", 0.0, dy * 0, 0.5, dy * 1); - mPEff[ii][0]->Draw(); - mPEff[ii][0]->SetRightMargin(0.04); - mPEff[ii][1] = new TPad("p1", "", 0.5, dy * 0, 1.0, dy * 1); - mPEff[ii][1]->Draw(); - mPEff[ii][1]->SetRightMargin(0.04); - mPEff[ii][2] = new TPad("p2", "", 0.0, dy * 1, 0.5, dy * 2 - .001); - mPEff[ii][2]->Draw(); - mPEff[ii][2]->SetRightMargin(0.04); - mPEff[ii][3] = new TPad("p3", "", 0.5, dy * 1, 1.0, dy * 2 - .001); - mPEff[ii][3]->Draw(); - mPEff[ii][3]->SetRightMargin(0.04); - mLEff[ii] = new TLegend(0.92 - legendSpacingString * 1.45, 0.83 - (0.93 - 0.82) / 2. * (float)ConfigNumInputs, 0.98, 0.849); - SetLegend(mLEff[ii]); - } - // Create Canvas / Pads for Resolution Histograms - for (int ii = 0; ii < 7; ii++) { - int i = ii == 5 ? 4 : ii; - sprintf(fname, "cres_%d", ii); - if (ii == 6) { - sprintf(name, "Integral Resolution"); - } else { - sprintf(name, "Resolution versus %s", VSPARAMETER_NAMES[i]); - } - mCRes[ii] = new TCanvas(fname, name, 0, 0, 700, 700. * 2. / 3.); - mCRes[ii]->cd(); - gStyle->SetOptFit(1); - - float dy = 1. / 2.; - mPRes[ii][3] = new TPad("p0", "", 0.0, dy * 0, 0.5, dy * 1); - mPRes[ii][3]->Draw(); - mPRes[ii][3]->SetRightMargin(0.04); - mPRes[ii][4] = new TPad("p1", "", 0.5, dy * 0, 1.0, dy * 1); - mPRes[ii][4]->Draw(); - mPRes[ii][4]->SetRightMargin(0.04); - mPRes[ii][0] = new TPad("p2", "", 0.0, dy * 1, 1. / 3., dy * 2 - .001); - mPRes[ii][0]->Draw(); - mPRes[ii][0]->SetRightMargin(0.04); - mPRes[ii][0]->SetLeftMargin(0.15); - mPRes[ii][1] = new TPad("p3", "", 1. / 3., dy * 1, 2. / 3., dy * 2 - .001); - mPRes[ii][1]->Draw(); - mPRes[ii][1]->SetRightMargin(0.04); - mPRes[ii][1]->SetLeftMargin(0.135); - mPRes[ii][2] = new TPad("p4", "", 2. / 3., dy * 1, 1.0, dy * 2 - .001); - mPRes[ii][2]->Draw(); - mPRes[ii][2]->SetRightMargin(0.06); - mPRes[ii][2]->SetLeftMargin(0.135); - if (ii < 6) { - mLRes[ii] = new TLegend(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLRes[ii]); + // Create Canvas / Pads for Efficiency Histograms + if (mQATasks & taskTrackingEff) { + for (int ii = 0; ii < 6; ii++) { + int i = ii == 5 ? 4 : ii; + sprintf(fname, "eff_vs_%s_layout", VSPARAMETER_NAMES[ii]); + sprintf(name, "Efficiency versus %s", VSPARAMETER_NAMES[i]); + mCEff[ii] = createGarbageCollected<TCanvas>(fname, name, 0, 0, 700, 700. * 2. / 3.); + mCEff[ii]->cd(); + float dy = 1. / 2.; + mPEff[ii][0] = createGarbageCollected<TPad>("p0", "", 0.0, dy * 0, 0.5, dy * 1); + mPEff[ii][0]->Draw(); + mPEff[ii][0]->SetRightMargin(0.04); + mPEff[ii][1] = createGarbageCollected<TPad>("p1", "", 0.5, dy * 0, 1.0, dy * 1); + mPEff[ii][1]->Draw(); + mPEff[ii][1]->SetRightMargin(0.04); + mPEff[ii][2] = createGarbageCollected<TPad>("p2", "", 0.0, dy * 1, 0.5, dy * 2 - .001); + mPEff[ii][2]->Draw(); + mPEff[ii][2]->SetRightMargin(0.04); + mPEff[ii][3] = createGarbageCollected<TPad>("p3", "", 0.5, dy * 1, 1.0, dy * 2 - .001); + mPEff[ii][3]->Draw(); + mPEff[ii][3]->SetRightMargin(0.04); + mLEff[ii] = createGarbageCollected<TLegend>(0.92 - legendSpacingString * 1.45, 0.83 - (0.93 - 0.82) / 2. * (float)ConfigNumInputs, 0.98, 0.849); + SetLegend(mLEff[ii]); + } } - } - // Create Canvas / Pads for Pull Histograms - for (int ii = 0; ii < 7; ii++) { - int i = ii == 5 ? 4 : ii; - sprintf(fname, "cpull_%d", ii); - if (ii == 6) { - sprintf(name, "Integral Pull"); - } else { - sprintf(name, "Pull versus %s", VSPARAMETER_NAMES[i]); - } - mCPull[ii] = new TCanvas(fname, name, 0, 0, 700, 700. * 2. / 3.); - mCPull[ii]->cd(); - gStyle->SetOptFit(1); - - float dy = 1. / 2.; - mPPull[ii][3] = new TPad("p0", "", 0.0, dy * 0, 0.5, dy * 1); - mPPull[ii][3]->Draw(); - mPPull[ii][3]->SetRightMargin(0.04); - mPPull[ii][4] = new TPad("p1", "", 0.5, dy * 0, 1.0, dy * 1); - mPPull[ii][4]->Draw(); - mPPull[ii][4]->SetRightMargin(0.04); - mPPull[ii][0] = new TPad("p2", "", 0.0, dy * 1, 1. / 3., dy * 2 - .001); - mPPull[ii][0]->Draw(); - mPPull[ii][0]->SetRightMargin(0.04); - mPPull[ii][0]->SetLeftMargin(0.15); - mPPull[ii][1] = new TPad("p3", "", 1. / 3., dy * 1, 2. / 3., dy * 2 - .001); - mPPull[ii][1]->Draw(); - mPPull[ii][1]->SetRightMargin(0.04); - mPPull[ii][1]->SetLeftMargin(0.135); - mPPull[ii][2] = new TPad("p4", "", 2. / 3., dy * 1, 1.0, dy * 2 - .001); - mPPull[ii][2]->Draw(); - mPPull[ii][2]->SetRightMargin(0.06); - mPPull[ii][2]->SetLeftMargin(0.135); - if (ii < 6) { - mLPull[ii] = new TLegend(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLPull[ii]); + // Create Canvas / Pads for Resolution Histograms + if (mQATasks & taskTrackingRes) { + for (int ii = 0; ii < 7; ii++) { + int i = ii == 5 ? 4 : ii; + if (ii == 6) { + sprintf(fname, "res_integral_layout"); + sprintf(name, "Integral Resolution"); + } else { + sprintf(fname, "res_vs_%s_layout", VSPARAMETER_NAMES[ii]); + sprintf(name, "Resolution versus %s", VSPARAMETER_NAMES[i]); + } + mCRes[ii] = createGarbageCollected<TCanvas>(fname, name, 0, 0, 700, 700. * 2. / 3.); + mCRes[ii]->cd(); + gStyle->SetOptFit(1); + + float dy = 1. / 2.; + mPRes[ii][3] = createGarbageCollected<TPad>("p0", "", 0.0, dy * 0, 0.5, dy * 1); + mPRes[ii][3]->Draw(); + mPRes[ii][3]->SetRightMargin(0.04); + mPRes[ii][4] = createGarbageCollected<TPad>("p1", "", 0.5, dy * 0, 1.0, dy * 1); + mPRes[ii][4]->Draw(); + mPRes[ii][4]->SetRightMargin(0.04); + mPRes[ii][0] = createGarbageCollected<TPad>("p2", "", 0.0, dy * 1, 1. / 3., dy * 2 - .001); + mPRes[ii][0]->Draw(); + mPRes[ii][0]->SetRightMargin(0.04); + mPRes[ii][0]->SetLeftMargin(0.15); + mPRes[ii][1] = createGarbageCollected<TPad>("p3", "", 1. / 3., dy * 1, 2. / 3., dy * 2 - .001); + mPRes[ii][1]->Draw(); + mPRes[ii][1]->SetRightMargin(0.04); + mPRes[ii][1]->SetLeftMargin(0.135); + mPRes[ii][2] = createGarbageCollected<TPad>("p4", "", 2. / 3., dy * 1, 1.0, dy * 2 - .001); + mPRes[ii][2]->Draw(); + mPRes[ii][2]->SetRightMargin(0.06); + mPRes[ii][2]->SetLeftMargin(0.135); + if (ii < 6) { + mLRes[ii] = createGarbageCollected<TLegend>(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); + SetLegend(mLRes[ii]); + } + } } - } - // Create Canvas for Cluster Histos - for (int i = 0; i < 3; i++) { - sprintf(fname, "cclust_%d", i); - mCClust[i] = new TCanvas(fname, CLUSTER_TITLES[i], 0, 0, 700, 700. * 2. / 3.); - mCClust[i]->cd(); - mPClust[i] = new TPad("p0", "", 0.0, 0.0, 1.0, 1.0); - mPClust[i]->Draw(); - float y1 = i != 1 ? 0.77 : 0.27, y2 = i != 1 ? 0.9 : 0.42; - mLClust[i] = new TLegend(i == 2 ? 0.1 : (0.65 - legendSpacingString * 1.45), y2 - (y2 - y1) * (ConfigNumInputs + (i != 1) / 2.) + 0.005, i == 2 ? (0.3 + legendSpacingString * 1.45) : 0.9, y2); - SetLegend(mLClust[i]); - } + // Create Canvas / Pads for Pull Histograms + if (mQATasks & taskTrackingResPull) { + for (int ii = 0; ii < 7; ii++) { + int i = ii == 5 ? 4 : ii; - // Create Canvas for other histos - { - mCTracks = new TCanvas("ctracks", "Track Pt", 0, 0, 700, 700. * 2. / 3.); - mCTracks->cd(); - mPTracks = new TPad("p0", "", 0.0, 0.0, 1.0, 1.0); - mPTracks->Draw(); - mLTracks = new TLegend(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLTracks); + if (ii == 6) { + sprintf(fname, "pull_integral_layout"); + sprintf(name, "Integral Pull"); + } else { + sprintf(fname, "pull_vs_%s_layout", VSPARAMETER_NAMES[ii]); + sprintf(name, "Pull versus %s", VSPARAMETER_NAMES[i]); + } + mCPull[ii] = createGarbageCollected<TCanvas>(fname, name, 0, 0, 700, 700. * 2. / 3.); + mCPull[ii]->cd(); + gStyle->SetOptFit(1); + + float dy = 1. / 2.; + mPPull[ii][3] = createGarbageCollected<TPad>("p0", "", 0.0, dy * 0, 0.5, dy * 1); + mPPull[ii][3]->Draw(); + mPPull[ii][3]->SetRightMargin(0.04); + mPPull[ii][4] = createGarbageCollected<TPad>("p1", "", 0.5, dy * 0, 1.0, dy * 1); + mPPull[ii][4]->Draw(); + mPPull[ii][4]->SetRightMargin(0.04); + mPPull[ii][0] = createGarbageCollected<TPad>("p2", "", 0.0, dy * 1, 1. / 3., dy * 2 - .001); + mPPull[ii][0]->Draw(); + mPPull[ii][0]->SetRightMargin(0.04); + mPPull[ii][0]->SetLeftMargin(0.15); + mPPull[ii][1] = createGarbageCollected<TPad>("p3", "", 1. / 3., dy * 1, 2. / 3., dy * 2 - .001); + mPPull[ii][1]->Draw(); + mPPull[ii][1]->SetRightMargin(0.04); + mPPull[ii][1]->SetLeftMargin(0.135); + mPPull[ii][2] = createGarbageCollected<TPad>("p4", "", 2. / 3., dy * 1, 1.0, dy * 2 - .001); + mPPull[ii][2]->Draw(); + mPPull[ii][2]->SetRightMargin(0.06); + mPPull[ii][2]->SetLeftMargin(0.135); + if (ii < 6) { + mLPull[ii] = createGarbageCollected<TLegend>(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); + SetLegend(mLPull[ii]); + } + } + } - mCNCl = new TCanvas("cncl", "Number of clusters per track", 0, 0, 700, 700. * 2. / 3.); - mCNCl->cd(); - mPNCl = new TPad("p0", "", 0.0, 0.0, 1.0, 1.0); - mPNCl->Draw(); - mLNCl = new TLegend(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLNCl); + // Create Canvas for Cluster Histos + if (mQATasks & taskClusterAttach) { + for (int i = 0; i < 3; i++) { + sprintf(fname, "clusters_%s_layout", CLUSTER_TYPES[i]); + mCClust[i] = createGarbageCollected<TCanvas>(fname, CLUSTER_TITLES[i], 0, 0, 700, 700. * 2. / 3.); + mCClust[i]->cd(); + mPClust[i] = createGarbageCollected<TPad>("p0", "", 0.0, 0.0, 1.0, 1.0); + mPClust[i]->Draw(); + float y1 = i != 1 ? 0.77 : 0.27, y2 = i != 1 ? 0.9 : 0.42; + mLClust[i] = createGarbageCollected<TLegend>(i == 2 ? 0.1 : (0.65 - legendSpacingString * 1.45), y2 - (y2 - y1) * (ConfigNumInputs + (i != 1) / 2.) + 0.005, i == 2 ? (0.3 + legendSpacingString * 1.45) : 0.9, y2); + SetLegend(mLClust[i]); + } + } + + // Create Canvas for track statistic histos + if (mQATasks & taskTrackStatistics) { + mCTracks = createGarbageCollected<TCanvas>("ctracks", "Track Pt", 0, 0, 700, 700. * 2. / 3.); + mCTracks->cd(); + mPTracks = createGarbageCollected<TPad>("p0", "", 0.0, 0.0, 1.0, 1.0); + mPTracks->Draw(); + mLTracks = createGarbageCollected<TLegend>(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); + SetLegend(mLTracks); + + mCNCl = createGarbageCollected<TCanvas>("cncl", "Number of clusters per track", 0, 0, 700, 700. * 2. / 3.); + mCNCl->cd(); + mPNCl = createGarbageCollected<TPad>("p0", "", 0.0, 0.0, 1.0, 1.0); + mPNCl->Draw(); + mLNCl = createGarbageCollected<TLegend>(0.9 - legendSpacingString * 1.45, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); + SetLegend(mLNCl); + } } - if (!mConfig.inputHistogramsOnly) { + if (mConfig.enableLocalOutput && !mConfig.inputHistogramsOnly) { GPUInfo("QA Stats: Eff: Tracks Prim %d (Eta %d, Pt %d) %f%% (%f%%) Sec %d (Eta %d, Pt %d) %f%% (%f%%) - Res: Tracks %d (Eta %d, Pt %d)", (int)mEff[3][1][0][0][0]->GetEntries(), (int)mEff[3][1][0][3][0]->GetEntries(), (int)mEff[3][1][0][4][0]->GetEntries(), mEff[0][0][0][0][0]->GetSumOfWeights() / std::max(1., mEff[3][0][0][0][0]->GetSumOfWeights()), mEff[0][1][0][0][0]->GetSumOfWeights() / std::max(1., mEff[3][1][0][0][0]->GetSumOfWeights()), (int)mEff[3][1][1][0][0]->GetEntries(), (int)mEff[3][1][1][3][0]->GetEntries(), (int)mEff[3][1][1][4][0]->GetEntries(), mEff[0][0][1][0][0]->GetSumOfWeights() / std::max(1., mEff[3][0][1][0][0]->GetSumOfWeights()), mEff[0][1][1][0][0]->GetSumOfWeights() / std::max(1., mEff[3][1][1][0][0]->GetSumOfWeights()), (int)mRes2[0][0]->GetEntries(), (int)mRes2[0][3]->GetEntries(), (int)mRes2[0][4]->GetEntries()); } - // Process / Draw Efficiency Histograms - for (int ii = 0; ii < 6; ii++) { - int i = ii == 5 ? 4 : ii; - for (int k = 0; k < ConfigNumInputs; k++) { - for (int j = 0; j < 4; j++) { - mPEff[ii][j]->cd(); - for (int l = 0; l < 3; l++) { - if (k == 0 && mConfig.inputHistogramsOnly == 0 && ii != 5) { - if (l == 0) { - // Divide eff, compute all for fake/clone - mEff[0][j / 2][j % 2][i][1]->Divide(mEff[l][j / 2][j % 2][i][0], mEff[3][j / 2][j % 2][i][0], 1, 1, "B"); - mEff[3][j / 2][j % 2][i][1]->Reset(); // Sum up rec + clone + fake for clone/fake rate - mEff[3][j / 2][j % 2][i][1]->Add(mEff[0][j / 2][j % 2][i][0]); - mEff[3][j / 2][j % 2][i][1]->Add(mEff[1][j / 2][j % 2][i][0]); - mEff[3][j / 2][j % 2][i][1]->Add(mEff[2][j / 2][j % 2][i][0]); - } else { - // Divide fake/clone - mEff[l][j / 2][j % 2][i][1]->Divide(mEff[l][j / 2][j % 2][i][0], mEff[3][j / 2][j % 2][i][1], 1, 1, "B"); - } + int flagShowVsPtLog = (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) ? 1 : 0; + + if (mQATasks & taskTrackingEff) { + // Process / Draw Efficiency Histograms + for (int ii = 0; ii < 5 + flagShowVsPtLog; ii++) { + int i = ii == 5 ? 4 : ii; + for (int k = 0; k < ConfigNumInputs; k++) { + for (int j = 0; j < 4; j++) { + if (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) { + mPEff[ii][j]->cd(); } + for (int l = 0; l < 3; l++) { + if (k == 0 && mConfig.inputHistogramsOnly == 0 && ii != 5) { + if (l == 0) { + // Divide eff, compute all for fake/clone + mEff[0][j / 2][j % 2][i][1]->Divide(mEff[l][j / 2][j % 2][i][0], mEff[3][j / 2][j % 2][i][0], 1, 1, "B"); + mEff[3][j / 2][j % 2][i][1]->Reset(); // Sum up rec + clone + fake for clone/fake rate + mEff[3][j / 2][j % 2][i][1]->Add(mEff[0][j / 2][j % 2][i][0]); + mEff[3][j / 2][j % 2][i][1]->Add(mEff[1][j / 2][j % 2][i][0]); + mEff[3][j / 2][j % 2][i][1]->Add(mEff[2][j / 2][j % 2][i][0]); + } else { + // Divide fake/clone + mEff[l][j / 2][j % 2][i][1]->Divide(mEff[l][j / 2][j % 2][i][0], mEff[3][j / 2][j % 2][i][1], 1, 1, "B"); + } + } - TH1F* e = mEff[l][j / 2][j % 2][i][1]; + TH1F* e = mEff[l][j / 2][j % 2][i][1]; - e->SetStats(kFALSE); - e->SetMaximum(1.02); - e->SetMinimum(-0.02); - if (!mConfig.inputHistogramsOnly && k == 0) { - if (tout) { - mEff[l][j / 2][j % 2][i][0]->Write(); - e->Write(); - if (l == 2) { - mEff[3][j / 2][j % 2][i][0]->Write(); // Store also all histogram! + e->SetStats(kFALSE); + e->SetMaximum(1.02); + e->SetMinimum(-0.02); + if (!mConfig.inputHistogramsOnly && k == 0) { + if (tout) { + mEff[l][j / 2][j % 2][i][0]->Write(); + e->Write(); + if (l == 2) { + mEff[3][j / 2][j % 2][i][0]->Write(); // Store also all histogram! + } } + } else if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; } - } else if (GetHist(e, tin, k, nNewInput) == nullptr) { - continue; - } - e->SetTitle(EFFICIENCY_TITLES[j]); - e->GetYaxis()->SetTitle("(Efficiency)"); - e->GetXaxis()->SetTitle(XAXIS_TITLES[i]); + e->SetTitle(EFFICIENCY_TITLES[j]); + e->GetYaxis()->SetTitle("(Efficiency)"); + e->GetXaxis()->SetTitle(XAXIS_TITLES[i]); - e->SetMarkerColor(kBlack); - e->SetLineWidth(1); - e->SetLineColor(mColorNums[(l == 2 ? (ConfigNumInputs * 2 + k) : (k * 2 + l)) % COLORCOUNT]); - e->SetLineStyle(CONFIG_DASHED_MARKERS ? k + 1 : 1); - SetAxisSize(e); - e->Draw(k || l ? "same" : ""); - if (j == 0) { - GetName(fname, k); - sprintf(name, "%s%s", fname, EFF_NAMES[l]); - mLEff[ii]->AddEntry(e, name, "l"); + e->SetLineWidth(1); + e->SetLineStyle(CONFIG_DASHED_MARKERS ? k + 1 : 1); + SetAxisSize(e); + if (qcout && !mConfig.shipToQCAsCanvas) { + qcout->Add(e); + } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + e->SetMarkerColor(kBlack); + e->SetLineColor(colorNums[(l == 2 ? (ConfigNumInputs * 2 + k) : (k * 2 + l)) % COLORCOUNT]); + e->Draw(k || l ? "same" : ""); + if (j == 0) { + GetName(fname, k); + sprintf(name, "%s%s", fname, EFF_NAMES[l]); + mLEff[ii]->AddEntry(e, name, "l"); + } + if (ii == 5) { + mPEff[ii][j]->SetLogx(); + } } - if (ii == 5) { - mPEff[ii][j]->SetLogx(); + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; } + mCEff[ii]->cd(); + ChangePadTitleSize(mPEff[ii][j], 0.056); } - mCEff[ii]->cd(); - ChangePadTitleSize(mPEff[ii][j], 0.056); } - } - mLEff[ii]->Draw(); + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } - doPerfFigure(0.2, 0.295, 0.025); + mLEff[ii]->Draw(); - mCEff[ii]->Print(Form("plots/eff_vs_%s.pdf", VSPARAMETER_NAMES[ii])); - if (mConfig.writeRootFiles) { - mCEff[ii]->Print(Form("plots/eff_vs_%s.root", VSPARAMETER_NAMES[ii])); + if (qcout) { + qcout->Add(mCEff[ii]); + } + if (!mConfig.enableLocalOutput) { + continue; + } + doPerfFigure(0.2, 0.295, 0.025); + mCEff[ii]->Print(Form("plots/eff_vs_%s.pdf", VSPARAMETER_NAMES[ii])); + if (mConfig.writeRootFiles) { + mCEff[ii]->Print(Form("plots/eff_vs_%s.root", VSPARAMETER_NAMES[ii])); + } } } - // Process / Draw Resolution Histograms - TH1D *resIntegral[5] = {}, *pullIntegral[5] = {}; - TF1* customGaus = new TF1("G", "[0]*exp(-(x-[1])*(x-[1])/(2.*[2]*[2]))"); - for (int p = 0; p < 2; p++) { - for (int ii = 0; ii < 6; ii++) { - TCanvas* can = p ? mCPull[ii] : mCRes[ii]; - TLegend* leg = p ? mLPull[ii] : mLRes[ii]; - int i = ii == 5 ? 4 : ii; - for (int j = 0; j < 5; j++) { - TH2F* src = p ? mPull2[j][i] : mRes2[j][i]; - TH1F** dst = p ? mPull[j][i] : mRes[j][i]; - TH1D*& dstIntegral = p ? pullIntegral[j] : resIntegral[j]; - TPad* pad = p ? mPPull[ii][j] : mPRes[ii][j]; - - if (!mConfig.inputHistogramsOnly && ii != 5) { - TCanvas cfit; - cfit.cd(); - - TAxis* axis = src->GetYaxis(); - int nBins = axis->GetNbins(); - int integ = 1; - for (int bin = 1; bin <= nBins; bin++) { - int bin0 = std::max(bin - integ, 0); - int bin1 = std::min(bin + integ, nBins); - TH1D* proj = src->ProjectionX("proj", bin0, bin1); - proj->ClearUnderflowAndOverflow(); - if (proj->GetEntries()) { - unsigned int rebin = 1; - while (proj->GetMaximum() < 50 && rebin < sizeof(RES_AXIS_BINS) / sizeof(RES_AXIS_BINS[0])) { - proj->Rebin(RES_AXIS_BINS[rebin - 1] / RES_AXIS_BINS[rebin]); - rebin++; - } - - if (proj->GetEntries() < 20 || proj->GetRMS() < 0.00001) { - dst[0]->SetBinContent(bin, proj->GetRMS()); - dst[0]->SetBinError(bin, std::sqrt(proj->GetRMS())); - dst[1]->SetBinContent(bin, proj->GetMean()); - dst[1]->SetBinError(bin, std::sqrt(proj->GetRMS())); - } else { - proj->GetXaxis()->SetRangeUser(proj->GetMean() - 6. * proj->GetRMS(), proj->GetMean() + 6. * proj->GetRMS()); - proj->GetXaxis()->SetRangeUser(proj->GetMean() - 3. * proj->GetRMS(), proj->GetMean() + 3. * proj->GetRMS()); - bool forceLogLike = proj->GetMaximum() < 20; - for (int k = forceLogLike ? 2 : 0; k < 3; k++) { - proj->Fit("gaus", forceLogLike || k == 2 ? "sQl" : k ? "sQww" : "sQ"); - TF1* fitFunc = proj->GetFunction("gaus"); - - if (k && !forceLogLike) { - customGaus->SetParameters(fitFunc->GetParameter(0), fitFunc->GetParameter(1), fitFunc->GetParameter(2)); - proj->Fit(customGaus, "sQ"); - fitFunc = customGaus; - } + if (mQATasks & (taskTrackingRes | taskTrackingResPull)) { + // Process / Draw Resolution Histograms + TH1D *resIntegral[5] = {}, *pullIntegral[5] = {}; + TCanvas* cfit = nullptr; + std::unique_ptr<TF1> customGaus = std::make_unique<TF1>("G", "[0]*exp(-(x-[1])*(x-[1])/(2.*[2]*[2]))"); + for (int p = 0; p < 2; p++) { + if ((p == 0 && (mQATasks & taskTrackingRes) == 0) || (p == 1 && (mQATasks & taskTrackingResPull) == 0)) { + continue; + } + for (int ii = 0; ii < 5 + flagShowVsPtLog; ii++) { + TCanvas* can = p ? mCPull[ii] : mCRes[ii]; + TLegend* leg = p ? mLPull[ii] : mLRes[ii]; + int i = ii == 5 ? 4 : ii; + for (int j = 0; j < 5; j++) { + TH2F* src = p ? mPull2[j][i] : mRes2[j][i]; + TH1F** dst = p ? mPull[j][i] : mRes[j][i]; + TH1D*& dstIntegral = p ? pullIntegral[j] : resIntegral[j]; + TPad* pad = p ? mPPull[ii][j] : mPRes[ii][j]; + + if (!mConfig.inputHistogramsOnly && ii != 5) { + if (cfit == nullptr) { + cfit = createGarbageCollected<TCanvas>(); + } + cfit->cd(); + + TAxis* axis = src->GetYaxis(); + int nBins = axis->GetNbins(); + int integ = 1; + for (int bin = 1; bin <= nBins; bin++) { + int bin0 = std::max(bin - integ, 0); + int bin1 = std::min(bin + integ, nBins); + std::unique_ptr<TH1D> proj{src->ProjectionX("proj", bin0, bin1)}; + proj->ClearUnderflowAndOverflow(); + if (proj->GetEntries()) { + unsigned int rebin = 1; + while (proj->GetMaximum() < 50 && rebin < sizeof(RES_AXIS_BINS) / sizeof(RES_AXIS_BINS[0])) { + proj->Rebin(RES_AXIS_BINS[rebin - 1] / RES_AXIS_BINS[rebin]); + rebin++; + } - const float sigma = fabs(fitFunc->GetParameter(2)); - dst[0]->SetBinContent(bin, sigma); - dst[1]->SetBinContent(bin, fitFunc->GetParameter(1)); - dst[0]->SetBinError(bin, fitFunc->GetParError(2)); - dst[1]->SetBinError(bin, fitFunc->GetParError(1)); - - const bool fail1 = sigma <= 0.f; - const bool fail2 = fabs(proj->GetMean() - dst[1]->GetBinContent(bin)) > std::min<float>(p ? PULL_AXIS : mConfig.nativeFitResolutions ? RES_AXES_NATIVE[j] : RES_AXES[j], 3.f * proj->GetRMS()); - const bool fail3 = dst[0]->GetBinContent(bin) > 3.f * proj->GetRMS() || dst[0]->GetBinError(bin) > 1 || dst[1]->GetBinError(bin) > 1; - const bool fail4 = fitFunc->GetParameter(0) < proj->GetMaximum() / 5.; - const bool fail = fail1 || fail2 || fail3 || fail4; - // if (p == 0 && ii == 4 && j == 2) DrawHisto(proj, Form("Hist_bin_%d-%d_vs_%d____%d_%d___%f-%f___%f-%f___%d.pdf", p, j, ii, bin, k, dst[0]->GetBinContent(bin), proj->GetRMS(), dst[1]->GetBinContent(bin), proj->GetMean(), (int) fail), ""); - - if (!fail) { - break; - } else if (k >= 2) { - dst[0]->SetBinContent(bin, proj->GetRMS()); - dst[0]->SetBinError(bin, std::sqrt(proj->GetRMS())); - dst[1]->SetBinContent(bin, proj->GetMean()); - dst[1]->SetBinError(bin, std::sqrt(proj->GetRMS())); + if (proj->GetEntries() < 20 || proj->GetRMS() < 0.00001) { + dst[0]->SetBinContent(bin, proj->GetRMS()); + dst[0]->SetBinError(bin, std::sqrt(proj->GetRMS())); + dst[1]->SetBinContent(bin, proj->GetMean()); + dst[1]->SetBinError(bin, std::sqrt(proj->GetRMS())); + } else { + proj->GetXaxis()->SetRangeUser(proj->GetMean() - 6. * proj->GetRMS(), proj->GetMean() + 6. * proj->GetRMS()); + proj->GetXaxis()->SetRangeUser(proj->GetMean() - 3. * proj->GetRMS(), proj->GetMean() + 3. * proj->GetRMS()); + bool forceLogLike = proj->GetMaximum() < 20; + for (int k = forceLogLike ? 2 : 0; k < 3; k++) { + proj->Fit("gaus", forceLogLike || k == 2 ? "sQl" : k ? "sQww" : "sQ"); + TF1* fitFunc = proj->GetFunction("gaus"); + + if (k && !forceLogLike) { + customGaus->SetParameters(fitFunc->GetParameter(0), fitFunc->GetParameter(1), fitFunc->GetParameter(2)); + proj->Fit(customGaus.get(), "sQ"); + fitFunc = customGaus.get(); + } + + const float sigma = fabs(fitFunc->GetParameter(2)); + dst[0]->SetBinContent(bin, sigma); + dst[1]->SetBinContent(bin, fitFunc->GetParameter(1)); + dst[0]->SetBinError(bin, fitFunc->GetParError(2)); + dst[1]->SetBinError(bin, fitFunc->GetParError(1)); + + const bool fail1 = sigma <= 0.f; + const bool fail2 = fabs(proj->GetMean() - dst[1]->GetBinContent(bin)) > std::min<float>(p ? PULL_AXIS : mConfig.nativeFitResolutions ? RES_AXES_NATIVE[j] : RES_AXES[j], 3.f * proj->GetRMS()); + const bool fail3 = dst[0]->GetBinContent(bin) > 3.f * proj->GetRMS() || dst[0]->GetBinError(bin) > 1 || dst[1]->GetBinError(bin) > 1; + const bool fail4 = fitFunc->GetParameter(0) < proj->GetMaximum() / 5.; + const bool fail = fail1 || fail2 || fail3 || fail4; + // if (p == 0 && ii == 4 && j == 2) DrawHisto(proj, Form("Hist_bin_%d-%d_vs_%d____%d_%d___%f-%f___%f-%f___%d.pdf", p, j, ii, bin, k, dst[0]->GetBinContent(bin), proj->GetRMS(), dst[1]->GetBinContent(bin), proj->GetMean(), (int) fail), ""); + + if (!fail) { + break; + } else if (k >= 2) { + dst[0]->SetBinContent(bin, proj->GetRMS()); + dst[0]->SetBinError(bin, std::sqrt(proj->GetRMS())); + dst[1]->SetBinContent(bin, proj->GetMean()); + dst[1]->SetBinError(bin, std::sqrt(proj->GetRMS())); + } } } + } else { + dst[0]->SetBinContent(bin, 0.f); + dst[0]->SetBinError(bin, 0.f); + dst[1]->SetBinContent(bin, 0.f); + dst[1]->SetBinError(bin, 0.f); + } + } + if (ii == 0) { + dstIntegral = src->ProjectionX(mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j], 0, nBins + 1); + unsigned int rebin = 1; + while (dstIntegral->GetMaximum() < 50 && rebin < sizeof(RES_AXIS_BINS) / sizeof(RES_AXIS_BINS[0])) { + dstIntegral->Rebin(RES_AXIS_BINS[rebin - 1] / RES_AXIS_BINS[rebin]); + rebin++; } - } else { - dst[0]->SetBinContent(bin, 0.f); - dst[0]->SetBinError(bin, 0.f); - dst[1]->SetBinContent(bin, 0.f); - dst[1]->SetBinError(bin, 0.f); } - delete proj; } if (ii == 0) { - dstIntegral = src->ProjectionX(mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j], 0, nBins + 1); - unsigned int rebin = 1; - while (dstIntegral->GetMaximum() < 50 && rebin < sizeof(RES_AXIS_BINS) / sizeof(RES_AXIS_BINS[0])) { - dstIntegral->Rebin(RES_AXIS_BINS[rebin - 1] / RES_AXIS_BINS[rebin]); - rebin++; + if (mConfig.inputHistogramsOnly) { + dstIntegral = createGarbageCollected<TH1D>(); } + sprintf(fname, p ? "IntPull%s" : "IntRes%s", VSPARAMETER_NAMES[j]); + sprintf(name, p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j]); + dstIntegral->SetName(fname); + dstIntegral->SetTitle(name); } - } - if (ii == 0) { - if (mConfig.inputHistogramsOnly) { - dstIntegral = new TH1D; + if (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) { + pad->cd(); } - sprintf(fname, p ? "IntPull%s" : "IntRes%s", VSPARAMETER_NAMES[j]); - sprintf(name, p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j]); - dstIntegral->SetName(fname); - dstIntegral->SetTitle(name); - } - pad->cd(); - - int numColor = 0; - float tmpMax = -1000.; - float tmpMin = 1000.; + int numColor = 0; + float tmpMax = -1000.; + float tmpMin = 1000.; - for (int l = 0; l < 2; l++) { - for (int k = 0; k < ConfigNumInputs; k++) { - TH1F* e = dst[l]; - if (GetHist(e, tin, k, nNewInput) == nullptr) { - continue; - } - if (nNewInput && k == 0 && ii != 5) { - if (p == 0) { - e->Scale(mConfig.nativeFitResolutions ? SCALE_NATIVE[j] : SCALE[j]); + for (int l = 0; l < 2; l++) { + for (int k = 0; k < ConfigNumInputs; k++) { + TH1F* e = dst[l]; + if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; } - } - if (ii == 4) { - e->GetXaxis()->SetRangeUser(0.2, PT_MAX); - } else if (LOG_PT_MIN > 0 && ii == 5) { - e->GetXaxis()->SetRangeUser(LOG_PT_MIN, PT_MAX); - } else if (ii == 5) { - e->GetXaxis()->SetRange(1, 0); - } - e->SetMinimum(-1111); - e->SetMaximum(-1111); + if (nNewInput && k == 0 && ii != 5) { + if (p == 0) { + e->Scale(mConfig.nativeFitResolutions ? SCALE_NATIVE[j] : SCALE[j]); + } + } + if (ii == 4) { + e->GetXaxis()->SetRangeUser(0.2, PT_MAX); + } else if (LOG_PT_MIN > 0 && ii == 5) { + e->GetXaxis()->SetRangeUser(LOG_PT_MIN, PT_MAX); + } else if (ii == 5) { + e->GetXaxis()->SetRange(1, 0); + } + e->SetMinimum(-1111); + e->SetMaximum(-1111); - if (e->GetMaximum() > tmpMax) { - tmpMax = e->GetMaximum(); - } - if (e->GetMinimum() < tmpMin) { - tmpMin = e->GetMinimum(); + if (e->GetMaximum() > tmpMax) { + tmpMax = e->GetMaximum(); + } + if (e->GetMinimum() < tmpMin) { + tmpMin = e->GetMinimum(); + } } } - } - float tmpSpan; - tmpSpan = tmpMax - tmpMin; - tmpMax += tmpSpan * .02; - tmpMin -= tmpSpan * .02; - if (j == 2 && i < 3) { - tmpMax += tmpSpan * 0.13 * ConfigNumInputs; - } + float tmpSpan; + tmpSpan = tmpMax - tmpMin; + tmpMax += tmpSpan * .02; + tmpMin -= tmpSpan * .02; + if (j == 2 && i < 3) { + tmpMax += tmpSpan * 0.13 * ConfigNumInputs; + } - for (int k = 0; k < ConfigNumInputs; k++) { - for (int l = 0; l < 2; l++) { - TH1F* e = dst[l]; - if (!mConfig.inputHistogramsOnly && k == 0) { - sprintf(name, p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j]); - e->SetTitle(name); - e->SetStats(kFALSE); - if (tout) { - if (l == 0) { - mRes2[j][i]->SetOption("colz"); - mRes2[j][i]->Write(); + for (int k = 0; k < ConfigNumInputs; k++) { + for (int l = 0; l < 2; l++) { + TH1F* e = dst[l]; + if (!mConfig.inputHistogramsOnly && k == 0) { + sprintf(name, p ? "%s Pull" : "%s Resolution", p || mConfig.nativeFitResolutions ? PARAMETER_NAMES_NATIVE[j] : PARAMETER_NAMES[j]); + e->SetTitle(name); + e->SetStats(kFALSE); + if (tout) { + if (l == 0) { + mRes2[j][i]->SetOption("colz"); + mRes2[j][i]->Write(); + } + e->Write(); } - e->Write(); + } else if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; + } + e->SetMaximum(tmpMax); + e->SetMinimum(tmpMin); + e->SetLineWidth(1); + e->SetLineStyle(CONFIG_DASHED_MARKERS ? k + 1 : 1); + SetAxisSize(e); + e->GetYaxis()->SetTitle(p ? AXIS_TITLES_PULL[j] : mConfig.nativeFitResolutions ? AXIS_TITLES_NATIVE[j] : AXIS_TITLES[j]); + e->GetXaxis()->SetTitle(XAXIS_TITLES[i]); + if (LOG_PT_MIN > 0 && ii == 5) { + e->GetXaxis()->SetRangeUser(LOG_PT_MIN, PT_MAX); } - } else if (GetHist(e, tin, k, nNewInput) == nullptr) { - continue; - } - e->SetMaximum(tmpMax); - e->SetMinimum(tmpMin); - e->SetMarkerColor(kBlack); - e->SetLineWidth(1); - e->SetLineColor(mColorNums[numColor++ % COLORCOUNT]); - e->SetLineStyle(CONFIG_DASHED_MARKERS ? k + 1 : 1); - SetAxisSize(e); - e->GetYaxis()->SetTitle(p ? AXIS_TITLES_PULL[j] : mConfig.nativeFitResolutions ? AXIS_TITLES_NATIVE[j] : AXIS_TITLES[j]); - e->GetXaxis()->SetTitle(XAXIS_TITLES[i]); - if (LOG_PT_MIN > 0 && ii == 5) { - e->GetXaxis()->SetRangeUser(LOG_PT_MIN, PT_MAX); - } - if (j == 0) { - e->GetYaxis()->SetTitleOffset(1.5); - } else if (j < 3) { - e->GetYaxis()->SetTitleOffset(1.4); - } - e->Draw(k || l ? "same" : ""); - if (j == 0) { - GetName(fname, k); - if (p) { - sprintf(name, "%s%s", fname, l ? "Mean" : "Pull"); - } else { - sprintf(name, "%s%s", fname, l ? "Mean" : "Resolution"); + if (j == 0) { + e->GetYaxis()->SetTitleOffset(1.5); + } else if (j < 3) { + e->GetYaxis()->SetTitleOffset(1.4); + } + if (qcout && !mConfig.shipToQCAsCanvas) { + qcout->Add(e); + } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + + e->SetMarkerColor(kBlack); + e->SetLineColor(colorNums[numColor++ % COLORCOUNT]); + e->Draw(k || l ? "same" : ""); + if (j == 0) { + GetName(fname, k); + if (p) { + sprintf(name, "%s%s", fname, l ? "Mean" : "Pull"); + } else { + sprintf(name, "%s%s", fname, l ? "Mean" : "Resolution"); + } + leg->AddEntry(e, name, "l"); } - leg->AddEntry(e, name, "l"); } } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + + if (ii == 5) { + pad->SetLogx(); + } + can->cd(); + if (j == 4) { + ChangePadTitleSize(pad, 0.056); + } } - if (ii == 5) { - pad->SetLogx(); - } - can->cd(); - if (j == 4) { - ChangePadTitleSize(pad, 0.056); + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; } - } - leg->Draw(); - doPerfFigure(0.2, 0.295, 0.025); + leg->Draw(); - can->Print(Form(p ? "plots/pull_vs_%s.pdf" : "plots/res_vs_%s.pdf", VSPARAMETER_NAMES[ii])); - if (mConfig.writeRootFiles) { - can->Print(Form(p ? "plots/pull_vs_%s.root" : "plots/res_vs_%s.root", VSPARAMETER_NAMES[ii])); + if (qcout) { + qcout->Add(can); + } + if (!mConfig.enableLocalOutput) { + continue; + } + doPerfFigure(0.2, 0.295, 0.025); + can->Print(Form(p ? "plots/pull_vs_%s.pdf" : "plots/res_vs_%s.pdf", VSPARAMETER_NAMES[ii])); + if (mConfig.writeRootFiles) { + can->Print(Form(p ? "plots/pull_vs_%s.root" : "plots/res_vs_%s.root", VSPARAMETER_NAMES[ii])); + } } } - } - delete customGaus; - // Process Integral Resolution Histogreams - for (int p = 0; p < 2; p++) { - TCanvas* can = p ? mCPull[6] : mCRes[6]; - for (int i = 0; i < 5; i++) { - TPad* pad = p ? mPPull[6][i] : mPRes[6][i]; - TH1D* hist = p ? pullIntegral[i] : resIntegral[i]; - int numColor = 0; - pad->cd(); - if (!mConfig.inputHistogramsOnly && mcAvail) { - TH1D* e = hist; - e->GetEntries(); - e->Fit("gaus", "sQ"); + // Process Integral Resolution Histogreams + for (int p = 0; p < 2; p++) { + if ((p == 0 && (mQATasks & taskTrackingRes) == 0) || (p == 1 && (mQATasks & taskTrackingResPull) == 0)) { + continue; } - - float tmpMax = 0; - for (int k = 0; k < ConfigNumInputs; k++) { - TH1D* e = hist; - if (GetHist(e, tin, k, nNewInput) == nullptr) { - continue; + TCanvas* can = p ? mCPull[6] : mCRes[6]; + for (int i = 0; i < 5; i++) { + TPad* pad = p ? mPPull[6][i] : mPRes[6][i]; + TH1D* hist = p ? pullIntegral[i] : resIntegral[i]; + int numColor = 0; + if (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) { + pad->cd(); } - e->SetMaximum(-1111); - if (e->GetMaximum() > tmpMax) { - tmpMax = e->GetMaximum(); + if (!mConfig.inputHistogramsOnly && mcAvail) { + TH1D* e = hist; + e->GetEntries(); + e->Fit("gaus", "sQ"); } - } - for (int k = 0; k < ConfigNumInputs; k++) { - TH1D* e = hist; - if (GetHist(e, tin, k, nNewInput) == nullptr) { - continue; + float tmpMax = 0; + for (int k = 0; k < ConfigNumInputs; k++) { + TH1D* e = hist; + if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; + } + e->SetMaximum(-1111); + if (e->GetMaximum() > tmpMax) { + tmpMax = e->GetMaximum(); + } + } + + for (int k = 0; k < ConfigNumInputs; k++) { + TH1D* e = hist; + if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; + } + e->SetMaximum(tmpMax * 1.02); + e->SetMinimum(tmpMax * -0.02); + if (tout && !mConfig.inputHistogramsOnly && k == 0) { + e->Write(); + } + if (qcout && !mConfig.shipToQCAsCanvas) { + qcout->Add(e); + } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + + e->SetLineColor(colorNums[numColor++ % COLORCOUNT]); + e->Draw(k == 0 ? "" : "same"); } - e->SetMaximum(tmpMax * 1.02); - e->SetMinimum(tmpMax * -0.02); - e->SetLineColor(mColorNums[numColor++ % COLORCOUNT]); - if (tout && !mConfig.inputHistogramsOnly && k == 0) { - e->Write(); + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; } - e->Draw(k == 0 ? "" : "same"); + can->cd(); } - can->cd(); - } - can->Print(p ? "plots/pull_integral.pdf" : "plots/res_integral.pdf"); - if (mConfig.writeRootFiles) { - can->Print(p ? "plots/pull_integral.root" : "plots/res_integral.root"); - } - if (!mConfig.inputHistogramsOnly) { - for (int i = 0; i < 5; i++) { - delete (p ? pullIntegral : resIntegral)[i]; + if (qcout) { + qcout->Add(can); + } + if (!mConfig.enableLocalOutput) { + continue; + } + + can->Print(p ? "plots/pull_integral.pdf" : "plots/res_integral.pdf"); + if (mConfig.writeRootFiles) { + can->Print(p ? "plots/pull_integral.root" : "plots/res_integral.root"); } } } - // Process Cluster Histograms - { + if (mQATasks & taskClusterCounts) { + // Process Cluster Histograms if (mConfig.inputHistogramsOnly == 0) { for (int i = N_CLS_HIST; i < N_CLS_TYPE * N_CLS_HIST - 1; i++) { mClusters[i]->Sumw2(true); @@ -2022,41 +2342,42 @@ int GPUQA::DrawQAHistograms() } counts[i] = val; } - mNRecClustersRejected += mNRecClustersHighIncl; + mClusterCounts.nRejected += mClusterCounts.nHighIncl; if (!mcAvail) { - counts[N_CLS_HIST - 1] = mNRecClustersTotal; + counts[N_CLS_HIST - 1] = mClusterCounts.nTotal; } - if (counts[N_CLS_HIST - 1]) { + if (mConfig.enableLocalOutput && counts[N_CLS_HIST - 1]) { if (mcAvail) { for (int i = 0; i < N_CLS_HIST; i++) { printf("\t%35s: %'12llu (%6.2f%%)\n", CLUSTER_NAMES[i], counts[i], 100.f * counts[i] / counts[N_CLS_HIST - 1]); } printf("\t%35s: %'12llu (%6.2f%%)\n", "Unattached", counts[N_CLS_HIST - 1] - counts[CL_att_adj], 100.f * (counts[N_CLS_HIST - 1] - counts[CL_att_adj]) / counts[N_CLS_HIST - 1]); printf("\t%35s: %'12llu (%6.2f%%)\n", "Removed", counts[CL_att_adj] - counts[CL_prot], 100.f * (counts[CL_att_adj] - counts[CL_prot]) / counts[N_CLS_HIST - 1]); // Attached + Adjacent (also fake) - protected - printf("\t%35s: %'12llu (%6.2f%%)\n", "Unaccessible", (unsigned long long int)mNRecClustersUnaccessible, 100.f * mNRecClustersUnaccessible / counts[N_CLS_HIST - 1]); // No contribution from track >= 10 MeV, unattached or fake-attached/adjacent + printf("\t%35s: %'12llu (%6.2f%%)\n", "Unaccessible", (unsigned long long int)mClusterCounts.nUnaccessible, 100.f * mClusterCounts.nUnaccessible / counts[N_CLS_HIST - 1]); // No contribution from track >= 10 MeV, unattached or fake-attached/adjacent } else { printf("\t%35s: %'12llu (%6.2f%%)\n", "All Clusters", counts[N_CLS_HIST - 1], 100.f); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Used in Physics", mNRecClustersPhysics, 100.f * mNRecClustersPhysics / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Protected", mNRecClustersProt, 100.f * mNRecClustersProt / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Unattached", mNRecClustersUnattached, 100.f * mNRecClustersUnattached / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Removed", mNRecClustersTotal - mNRecClustersUnattached - mNRecClustersProt, 100.f * (mNRecClustersTotal - mNRecClustersUnattached - mNRecClustersProt) / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Used in Physics", mClusterCounts.nPhysics, 100.f * mClusterCounts.nPhysics / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Protected", mClusterCounts.nProt, 100.f * mClusterCounts.nProt / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Unattached", mClusterCounts.nUnattached, 100.f * mClusterCounts.nUnattached / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Removed", mClusterCounts.nTotal - mClusterCounts.nUnattached - mClusterCounts.nProt, 100.f * (mClusterCounts.nTotal - mClusterCounts.nUnattached - mClusterCounts.nProt) / counts[N_CLS_HIST - 1]); } - printf("\t%35s: %'12llu (%6.2f%%)\n", "High Inclination Angle", mNRecClustersHighIncl, 100.f * mNRecClustersHighIncl / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Rejected", mNRecClustersRejected, 100.f * mNRecClustersRejected / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Tube (> 200 MeV)", mNRecClustersTube, 100.f * mNRecClustersTube / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Tube (< 200 MeV)", mNRecClustersTube200, 100.f * mNRecClustersTube200 / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Looping Legs", mNRecClustersLoopers, 100.f * mNRecClustersLoopers / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Low Pt < 50 MeV", mNRecClustersLowPt, 100.f * mNRecClustersLowPt / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Low Pt < 200 MeV", mNRecClusters200MeV, 100.f * mNRecClusters200MeV / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Merged Loopers (Afterburner)", mClusterCounts.nMergedLooper, 100.f * mClusterCounts.nMergedLooper / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "High Inclination Angle", mClusterCounts.nHighIncl, 100.f * mClusterCounts.nHighIncl / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Rejected", mClusterCounts.nRejected, 100.f * mClusterCounts.nRejected / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Tube (> 200 MeV)", mClusterCounts.nTube, 100.f * mClusterCounts.nTube / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Tube (< 200 MeV)", mClusterCounts.nTube200, 100.f * mClusterCounts.nTube200 / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Looping Legs", mClusterCounts.nLoopers, 100.f * mClusterCounts.nLoopers / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Low Pt < 50 MeV", mClusterCounts.nLowPt, 100.f * mClusterCounts.nLowPt / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Low Pt < 200 MeV", mClusterCounts.n200MeV, 100.f * mClusterCounts.n200MeV / counts[N_CLS_HIST - 1]); if (mcAvail) { - printf("\t%35s: %'12llu (%6.2f%%)\n", "Tracks > 400 MeV", mNRecClustersAbove400, 100.f * mNRecClustersAbove400 / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Fake Removed (> 400 MeV)", mNRecClustersFakeRemove400, 100.f * mNRecClustersFakeRemove400 / std::max(mNRecClustersAbove400, 1ll)); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Full Fake Removed (> 400 MeV)", mNRecClustersFullFakeRemove400, 100.f * mNRecClustersFullFakeRemove400 / std::max(mNRecClustersAbove400, 1ll)); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Tracks > 400 MeV", mClusterCounts.nAbove400, 100.f * mClusterCounts.nAbove400 / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Fake Removed (> 400 MeV)", mClusterCounts.nFakeRemove400, 100.f * mClusterCounts.nFakeRemove400 / std::max(mClusterCounts.nAbove400, 1ll)); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Full Fake Removed (> 400 MeV)", mClusterCounts.nFullFakeRemove400, 100.f * mClusterCounts.nFullFakeRemove400 / std::max(mClusterCounts.nAbove400, 1ll)); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Tracks < 40 MeV", mNRecClustersBelow40, 100.f * mNRecClustersBelow40 / counts[N_CLS_HIST - 1]); - printf("\t%35s: %'12llu (%6.2f%%)\n", "Fake Protect (< 40 MeV)", mNRecClustersFakeProtect40, 100.f * mNRecClustersFakeProtect40 / std::max(mNRecClustersBelow40, 1ll)); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Tracks < 40 MeV", mClusterCounts.nBelow40, 100.f * mClusterCounts.nBelow40 / counts[N_CLS_HIST - 1]); + printf("\t%35s: %'12llu (%6.2f%%)\n", "Fake Protect (< 40 MeV)", mClusterCounts.nFakeProtect40, 100.f * mClusterCounts.nFakeProtect40 / std::max(mClusterCounts.nBelow40, 1ll)); } } @@ -2106,8 +2427,10 @@ int GPUQA::DrawQAHistograms() } for (int i = 0; i < N_CLS_TYPE; i++) { - mPClust[i]->cd(); - mPClust[i]->SetLogx(); + if (mConfig.enableLocalOutput || mConfig.shipToQCAsCanvas) { + mPClust[i]->cd(); + mPClust[i]->SetLogx(); + } int begin = i == 2 ? (2 * N_CLS_HIST - 1) : i == 1 ? N_CLS_HIST : 0; int end = i == 2 ? (3 * N_CLS_HIST - 1) : i == 1 ? (2 * N_CLS_HIST - 1) : N_CLS_HIST; int numColor = 0; @@ -2127,13 +2450,20 @@ int GPUQA::DrawQAHistograms() e->Write(); } e->SetStats(kFALSE); - e->SetMarkerColor(kBlack); e->SetLineWidth(1); - e->SetLineColor(mColorNums[numColor++ % COLORCOUNT]); e->SetLineStyle(CONFIG_DASHED_MARKERS ? j + 1 : 1); if (i == 0) { e->GetXaxis()->SetRange(2, AXIS_BINS[4]); } + if (qcout && !mConfig.shipToQCAsCanvas) { + qcout->Add(e); + } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + + e->SetMarkerColor(kBlack); + e->SetLineColor(colorNums[numColor++ % COLORCOUNT]); e->Draw(j == end - 1 && k == 0 ? "" : "same"); GetName(fname, k); sprintf(name, "%s%s", fname, CLUSTER_NAMES[j - begin]); @@ -2143,14 +2473,30 @@ int GPUQA::DrawQAHistograms() if (ConfigNumInputs == 1) { TH1* e = reinterpret_cast<TH1F*>(mClusters[begin + CL_att_adj]->Clone()); e->Add(mClusters[begin + CL_prot], -1); - e->SetLineColor(mColorNums[numColor++ % COLORCOUNT]); + if (qcout && !mConfig.shipToQCAsCanvas) { + qcout->Add(e); + } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + + e->SetLineColor(colorNums[numColor++ % COLORCOUNT]); e->Draw("same"); mLClust[i]->AddEntry(e, "Removed", "l"); } + if (!mConfig.enableLocalOutput && !mConfig.shipToQCAsCanvas) { + continue; + } + mLClust[i]->Draw(); + if (qcout) { + qcout->Add(mCClust[i]); + } + if (!mConfig.enableLocalOutput) { + continue; + } doPerfFigure(i != 2 ? 0.37 : 0.6, 0.295, 0.030); - mCClust[i]->cd(); mCClust[i]->Print(i == 2 ? "plots/clusters_integral.pdf" : i == 1 ? "plots/clusters_relative.pdf" : "plots/clusters.pdf"); if (mConfig.writeRootFiles) { @@ -2159,8 +2505,8 @@ int GPUQA::DrawQAHistograms() } } - // Process track histograms - { + if (mQATasks & taskTrackStatistics) { + // Process track statistic histograms float tmpMax = 0.; for (int k = 0; k < ConfigNumInputs; k++) { TH1F* e = mTracks; @@ -2185,11 +2531,14 @@ int GPUQA::DrawQAHistograms() e->SetMaximum(tmpMax * 1.02); e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); - e->SetMarkerColor(kBlack); e->SetLineWidth(1); - e->SetLineColor(mColorNums[k % COLORCOUNT]); e->GetYaxis()->SetTitle("a.u."); e->GetXaxis()->SetTitle("#it{p}_{Tmc} (GeV/#it{c})"); + if (qcout) { + qcout->Add(e); + } + e->SetMarkerColor(kBlack); + e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k); sprintf(name, "%sTrack Pt", fname); @@ -2224,11 +2573,14 @@ int GPUQA::DrawQAHistograms() e->SetMaximum(tmpMax * 1.02); e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); - e->SetMarkerColor(kBlack); e->SetLineWidth(1); - e->SetLineColor(mColorNums[k % COLORCOUNT]); e->GetYaxis()->SetTitle("a.u."); e->GetXaxis()->SetTitle("NClusters"); + if (qcout) { + qcout->Add(e); + } + e->SetMarkerColor(kBlack); + e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k); sprintf(name, "%sNClusters", fname); @@ -2254,11 +2606,12 @@ int GPUQA::DrawQAHistograms() if (tout) { tout->Close(); - delete tout; } for (unsigned int i = 0; i < mConfig.compareInputs.size(); i++) { tin[i]->Close(); - delete tin[i]; + } + if (!qcout) { + clearGarbagageCollector(); } return (0); } diff --git a/GPU/GPUTracking/Standalone/qa/GPUQA.h b/GPU/GPUTracking/Standalone/qa/GPUQA.h index 459288fd6d5b5..8df5717537f2a 100644 --- a/GPU/GPUTracking/Standalone/qa/GPUQA.h +++ b/GPU/GPUTracking/Standalone/qa/GPUQA.h @@ -14,7 +14,7 @@ #ifndef GPUQA_H #define GPUQA_H -#include "GPUQAConfig.h" +#include "GPUSettings.h" struct AliHLTTPCClusterMCWeight; class TH1F; class TH2F; @@ -25,6 +25,8 @@ class TPad; class TH1; class TFile; class TH1D; +class TObjArray; +class TColor; typedef short int Color_t; #if !defined(GPUCA_BUILD_QA) || defined(GPUCA_GPUCODE) @@ -38,20 +40,18 @@ class GPUChainTracking; class GPUQA { public: - GPUQA(GPUChainTracking* rec) {} + GPUQA(GPUChainTracking* chain) {} ~GPUQA() = default; - typedef structConfigQA configQA; - - int InitQA() { return 1; } + int InitQA(int tasks = 0) { return 1; } void RunQA(bool matchOnly = false) {} int DrawQAHistograms() { return 1; } void SetMCTrackRange(int min, int max) {} bool SuppressTrack(int iTrack) const { return false; } bool SuppressHit(int iHit) const { return false; } - bool HitAttachStatus(int iHit) const { return false; } + int HitAttachStatus(int iHit) const { return false; } int GetMCTrackLabel(unsigned int trackId) const { return -1; } - bool clusterRemovable(int cid, bool prot) const { return false; } + bool clusterRemovable(int attach, bool prot) const { return false; } static bool QAAvailable() { return false; } static bool IsInitialized() { return false; } }; @@ -62,7 +62,8 @@ class GPUQA #include "GPUTPCDef.h" #include <cmath> - +#include <vector> +#include <memory> #ifdef GPUCA_TPC_GEOMETRY_O2 #include <gsl/span> #endif @@ -70,37 +71,63 @@ class GPUQA namespace o2 { class MCCompLabel; -} +namespace tpc +{ +class TrackTPC; +struct ClusterNativeAccess; +} // namespace tpc +} // namespace o2 -class AliHLTTPCClusterMCLabel; +struct AliHLTTPCClusterMCLabel; -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUChainTracking; -class GPUTPCMCInfo; +struct GPUTPCMCInfo; +struct GPUQAGarbageCollection; class GPUQA { public: - GPUQA(GPUChainTracking* rec); + GPUQA(); + GPUQA(GPUChainTracking* chain, const GPUSettingsQA* config = nullptr); ~GPUQA(); - typedef GPUQAConfig configQA; - - int InitQA(); - void RunQA(bool matchOnly = false); - int DrawQAHistograms(); + int InitQA(int tasks = -1); + void RunQA(bool matchOnly = false, const std::vector<o2::tpc::TrackTPC>* tracksExternal = nullptr, const std::vector<o2::MCCompLabel>* tracksExtMC = nullptr, const o2::tpc::ClusterNativeAccess* clNative = nullptr); + int DrawQAHistograms(TObjArray* qcout = nullptr); + void DrawQAHistogramsCleanup(); // Needed after call to DrawQAHistograms with qcout != nullptr when GPUSettingsQA.shipToQCAsCanvas = true to clean up the Canvases etc. void SetMCTrackRange(int min, int max); bool SuppressTrack(int iTrack) const; bool SuppressHit(int iHit) const; - bool HitAttachStatus(int iHit) const; + int HitAttachStatus(int iHit) const; int GetMCTrackLabel(unsigned int trackId) const; - bool clusterRemovable(int cid, bool prot) const; + bool clusterRemovable(int attach, bool prot) const; static bool QAAvailable() { return true; } bool IsInitialized() { return mQAInitialized; } + const std::vector<TH1F>& getHistograms1D() const { return *mHist1D; } + const std::vector<TH2F>& getHistograms2D() const { return *mHist2D; } + const std::vector<TH1D>& getHistograms1Dd() const { return *mHist1Dd; } + void resetHists(); + int loadHistograms(std::vector<TH1F>& i1, std::vector<TH2F>& i2, std::vector<TH1D>& i3, int tasks = -1); + + static constexpr int N_CLS_HIST = 8; + static constexpr int N_CLS_TYPE = 3; + + static constexpr int MC_LABEL_INVALID = -1e9; + + enum QA_TASKS { + taskTrackingEff = 1, + taskTrackingRes = 2, + taskTrackingResPull = 4, + taskClusterAttach = 8, + taskTrackStatistics = 16, + taskClusterCounts = 32, + taskDefault = 63, + taskDefaultPostprocess = 31 + }; + private: struct additionalMCParameters { float pt, phi, theta, eta, nWeightCls; @@ -111,6 +138,8 @@ class GPUQA float pt; }; + int InitQACreateHistograms(); + void SetAxisSize(TH1F* e); void SetLegend(TLegend* l); double* CreateLogAxis(int nbins, float xmin, float xmax); @@ -119,7 +148,7 @@ class GPUQA void doPerfFigure(float x, float y, float size); void GetName(char* fname, int k); template <class T> - T* GetHist(T*& ee, std::vector<TFile*>& tin, int k, int nNewInput); + T* GetHist(T*& ee, std::vector<std::unique_ptr<TFile>>& tin, int k, int nNewInput); #ifdef GPUCA_TPC_GEOMETRY_O2 using mcLabels_t = gsl::span<const o2::MCCompLabel>; @@ -134,6 +163,7 @@ class GPUQA struct mcLabelI_t { int getTrackID() const { return AbsLabelID(track); } int getEventID() const { return 0; } + long int getTrackEventSourceID() const { return getTrackID(); } bool isFake() const { return track < 0; } bool isValid() const { return track != MC_LABEL_INVALID; } void invalidate() { track = MC_LABEL_INVALID; } @@ -169,20 +199,13 @@ class GPUQA float GetMCLabelWeight(unsigned int i, unsigned int j); float GetMCLabelWeight(const mcLabels_t& label, unsigned int j); float GetMCLabelWeight(const mcLabel_t& label); + const auto& GetClusterLabels(); bool mcPresent(); static bool MCComp(const mcLabel_t& a, const mcLabel_t& b); GPUChainTracking* mTracking; - const configQA& mConfig; - - //-------------------------: Some compile time settings.... - static const constexpr bool PLOT_ROOT = 0; - static const constexpr bool FIX_SCALES = 0; - static const constexpr bool PERF_FIGURE = 0; - static const constexpr float FIXED_SCALES_MIN[5] = {-0.05, -0.05, -0.2, -0.2, -0.5}; - static const constexpr float FIXED_SCALES_MAX[5] = {0.4, 0.7, 5, 3, 6.5}; - static const constexpr float LOG_PT_MIN = -1.; + const GPUSettingsQA& mConfig; const char* str_perf_figure_1 = "ALICE Performance 2018/03/20"; // const char* str_perf_figure_2 = "2015, MC pp, #sqrt{s} = 5.02 TeV"; @@ -223,8 +246,6 @@ class GPUQA TPad* mPPull[7][5]; TLegend* mLPull[6]; - static constexpr int N_CLS_HIST = 8; - static constexpr int N_CLS_TYPE = 3; enum CL_types { CL_attached = 0, CL_fake = 1, CL_att_adj = 2, @@ -238,9 +259,10 @@ class GPUQA TPad* mPClust[N_CLS_TYPE]; TLegend* mLClust[N_CLS_TYPE]; - long long int mNRecClustersRejected = 0, mNRecClustersTube = 0, mNRecClustersTube200 = 0, mNRecClustersLoopers = 0, mNRecClustersLowPt = 0, mNRecClusters200MeV = 0, mNRecClustersPhysics = 0, mNRecClustersProt = 0, mNRecClustersUnattached = 0, mNRecClustersTotal = 0, mNRecClustersHighIncl = 0, - mNRecClustersAbove400 = 0, mNRecClustersFakeRemove400 = 0, mNRecClustersFullFakeRemove400 = 0, mNRecClustersBelow40 = 0, mNRecClustersFakeProtect40 = 0; - double mNRecClustersUnaccessible = 0; + struct counts_t { + long long int nRejected = 0, nTube = 0, nTube200 = 0, nLoopers = 0, nLowPt = 0, n200MeV = 0, nPhysics = 0, nProt = 0, nUnattached = 0, nTotal = 0, nHighIncl = 0, nAbove400 = 0, nFakeRemove400 = 0, nFullFakeRemove400 = 0, nBelow40 = 0, nFakeProtect40 = 0, nMergedLooper = 0; + double nUnaccessible = 0; + } mClusterCounts; TH1F* mTracks; TCanvas* mCTracks; @@ -252,73 +274,44 @@ class GPUQA TPad* mPNCl; TLegend* mLNCl; + std::vector<TH1F>* mHist1D = nullptr; + std::vector<TH2F>* mHist2D = nullptr; + std::vector<TH1D>* mHist1Dd = nullptr; + bool mHaveExternalHists = false; + std::vector<TH1F**> mHist1D_pos{}; + std::vector<TH2F**> mHist2D_pos{}; + std::vector<TH1D**> mHist1Dd_pos{}; + template <class T> + auto getHistArray(); + template <class T, typename... Args> + void createHist(T*& h, const char* name, Args... args); + + std::unique_ptr<GPUQAGarbageCollection> mGarbageCollector; + template <class T, typename... Args> + T* createGarbageCollected(Args... args); + void clearGarbagageCollector(); + int mNEvents = 0; bool mQAInitialized = false; + int mQATasks = 0; std::vector<std::vector<int>> mcEffBuffer; std::vector<std::vector<int>> mcLabelBuffer; std::vector<std::vector<bool>> mGoodTracks; std::vector<std::vector<bool>> mGoodHits; - static constexpr float Y_MAX = 40; - static constexpr float Z_MAX = 100; - static constexpr float PT_MIN = GPUCA_MIN_TRACK_PT_DEFAULT; - static constexpr float PT_MIN2 = 0.1; - static constexpr float PT_MIN_PRIM = 0.1; - static constexpr float PT_MIN_CLUST = GPUCA_MIN_TRACK_PT_DEFAULT; - static constexpr float PT_MAX = 20; - static constexpr float ETA_MAX = 1.5; - static constexpr float ETA_MAX2 = 0.9; - - static constexpr float MIN_WEIGHT_CLS = 40; - static constexpr float FINDABLE_WEIGHT_CLS = 70; - - static constexpr int MC_LABEL_INVALID = -1e9; - - static constexpr bool CLUST_HIST_INT_SUM = false; - - static constexpr const int COLORCOUNT = 12; - Color_t* mColorNums; - - static const constexpr char* EFF_TYPES[4] = {"Rec", "Clone", "Fake", "All"}; - static const constexpr char* FINDABLE_NAMES[2] = {"", "Findable"}; - static const constexpr char* PRIM_NAMES[2] = {"Prim", "Sec"}; - static const constexpr char* PARAMETER_NAMES[5] = {"Y", "Z", "#Phi", "#lambda", "Relative #it{p}_{T}"}; - static const constexpr char* PARAMETER_NAMES_NATIVE[5] = {"Y", "Z", "sin(#Phi)", "tan(#lambda)", "q/#it{p}_{T} (curvature)"}; - static const constexpr char* VSPARAMETER_NAMES[6] = {"Y", "Z", "Phi", "Eta", "Pt", "Pt_log"}; - static const constexpr char* EFF_NAMES[3] = {"Efficiency", "Clone Rate", "Fake Rate"}; - static const constexpr char* EFFICIENCY_TITLES[4] = {"Efficiency (Primary Tracks, Findable)", "Efficiency (Secondary Tracks, Findable)", "Efficiency (Primary Tracks)", "Efficiency (Secondary Tracks)"}; - static const constexpr double SCALE[5] = {10., 10., 1000., 1000., 100.}; - static const constexpr double SCALE_NATIVE[5] = {10., 10., 1000., 1000., 1.}; - static const constexpr char* XAXIS_TITLES[5] = {"#it{y}_{mc} (cm)", "#it{z}_{mc} (cm)", "#Phi_{mc} (rad)", "#eta_{mc}", "#it{p}_{Tmc} (GeV/#it{c})"}; - static const constexpr char* AXIS_TITLES[5] = {"#it{y}-#it{y}_{mc} (mm) (Resolution)", "#it{z}-#it{z}_{mc} (mm) (Resolution)", "#phi-#phi_{mc} (mrad) (Resolution)", "#lambda-#lambda_{mc} (mrad) (Resolution)", "(#it{p}_{T} - #it{p}_{Tmc}) / #it{p}_{Tmc} (%) (Resolution)"}; - static const constexpr char* AXIS_TITLES_NATIVE[5] = {"#it{y}-#it{y}_{mc} (mm) (Resolution)", "#it{z}-#it{z}_{mc} (mm) (Resolution)", "sin(#phi)-sin(#phi_{mc}) (Resolution)", "tan(#lambda)-tan(#lambda_{mc}) (Resolution)", "q*(q/#it{p}_{T} - q/#it{p}_{Tmc}) (Resolution)"}; - static const constexpr char* AXIS_TITLES_PULL[5] = {"#it{y}-#it{y}_{mc}/#sigma_{y} (Pull)", "#it{z}-#it{z}_{mc}/#sigma_{z} (Pull)", "sin(#phi)-sin(#phi_{mc})/#sigma_{sin(#phi)} (Pull)", "tan(#lambda)-tan(#lambda_{mc})/#sigma_{tan(#lambda)} (Pull)", - "q*(q/#it{p}_{T} - q/#it{p}_{Tmc})/#sigma_{q/#it{p}_{T}} (Pull)"}; - static const constexpr char* CLUSTER_NAMES[N_CLS_HIST] = {"Correctly attached clusters", "Fake attached clusters", "Attached + adjacent clusters", "Fake adjacent clusters", "Clusters of reconstructed tracks", "Used in Physics", "Protected", "All clusters"}; - static const constexpr char* CLUSTER_TITLES[N_CLS_TYPE] = {"Clusters Pt Distribution / Attachment", "Clusters Pt Distribution / Attachment (relative to all clusters)", "Clusters Pt Distribution / Attachment (integrated)"}; - static const constexpr char* CLUSTER_NAMES_SHORT[N_CLS_HIST] = {"Attached", "Fake", "AttachAdjacent", "FakeAdjacent", "FoundTracks", "Physics", "Protected", "All"}; - static const constexpr char* CLUSTER_TYPES[N_CLS_TYPE] = {"", "Ratio", "Integral"}; - static const constexpr int COLORS_HEX[COLORCOUNT] = {0xB03030, 0x00A000, 0x0000C0, 0x9400D3, 0x19BBBF, 0xF25900, 0x7F7F7F, 0xFFD700, 0x07F707, 0x07F7F7, 0xF08080, 0x000000}; - - static const constexpr int CONFIG_DASHED_MARKERS = 0; - - static const constexpr float AXES_MIN[5] = {-Y_MAX, -Z_MAX, 0.f, -ETA_MAX, PT_MIN}; - static const constexpr float AXES_MAX[5] = {Y_MAX, Z_MAX, 2.f * M_PI, ETA_MAX, PT_MAX}; - static const constexpr int AXIS_BINS[5] = {51, 51, 144, 31, 50}; - static const constexpr int RES_AXIS_BINS[] = {1017, 113}; // Consecutive bin sizes, histograms are binned down until the maximum entry is 50, each bin size should evenly divide its predecessor. - static const constexpr float RES_AXES[5] = {1., 1., 0.03, 0.03, 1.0}; - static const constexpr float RES_AXES_NATIVE[5] = {1., 1., 0.1, 0.1, 5.0}; - static const constexpr float PULL_AXIS = 10.f; + static std::vector<TColor*> mColors; + static int initColors(); int mMCTrackMin = -1, mMCTrackMax = -1; + + const o2::tpc::ClusterNativeAccess* mClNative; }; inline bool GPUQA::SuppressTrack(int iTrack) const { return (mConfig.matchMCLabels.size() && !mGoodTracks[mNEvents][iTrack]); } inline bool GPUQA::SuppressHit(int iHit) const { return (mConfig.matchMCLabels.size() && !mGoodHits[mNEvents - 1][iHit]); } -inline bool GPUQA::HitAttachStatus(int iHit) const { return (mClusterParam.size() && mClusterParam[iHit].fakeAttached ? (mClusterParam[iHit].attached ? 1 : 2) : 0); } +inline int GPUQA::HitAttachStatus(int iHit) const { return (mClusterParam.size() && mClusterParam[iHit].fakeAttached ? (mClusterParam[iHit].attached ? 1 : 2) : 0); } -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif #endif diff --git a/GPU/GPUTracking/Standalone/qa/GPUQAConfig.h b/GPU/GPUTracking/Standalone/qa/GPUQAConfig.h deleted file mode 100644 index 25084fa3acbcc..0000000000000 --- a/GPU/GPUTracking/Standalone/qa/GPUQAConfig.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 GPUQAConfig.h -/// \author David Rohr - -#ifndef GPUQACONFIG_H -#define GPUQACONFIG_H - -#include "GPUCommonDef.h" - -#if !defined(GPUCA_STANDALONE) -#define QCONFIG_CPP11_INIT -#endif -#include "utils/qconfig.h" - -namespace GPUCA_NAMESPACE -{ -namespace gpu -{ -typedef structConfigQA GPUQAConfig; -} -} // namespace GPUCA_NAMESPACE - -#endif diff --git a/GPU/GPUTracking/Standalone/qa/GPUQAHelper.h b/GPU/GPUTracking/Standalone/qa/GPUQAHelper.h new file mode 100644 index 0000000000000..18499cec90a74 --- /dev/null +++ b/GPU/GPUTracking/Standalone/qa/GPUQAHelper.h @@ -0,0 +1,167 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUQAHelper.h +/// \author David Rohr + +#ifndef GPUQAHELPER_H +#define GPUQAHELPER_H + +#include <utility> +#include <vector> +#include <type_traits> + +struct AliHLTTPCClusterMCWeight; +struct AliHLTTPCClusterMCLabel; + +namespace o2 +{ +class MCCompLabel; +namespace gpu +{ +namespace internal +{ + +template <bool WEIGHT, class T, class S, class U = T> +class GPUTPCTrkLbl +{ + public: + GPUTPCTrkLbl(const S* v, float maxFake = 0.1f) : mClusterLabels(v), mTrackMCMaxFake(maxFake) { mLabels.reserve(5); }; + GPUTPCTrkLbl(const GPUTPCTrkLbl&) = default; + inline void reset() + { + mLabels.clear(); + mNCl = 0; + mTotalWeight = 0.f; + } + inline void addLabel(unsigned int elementId) + { + if constexpr (std::is_same<T, AliHLTTPCClusterMCWeight>::value) { + for (unsigned int i = 0; i < sizeof(mClusterLabels[elementId]) / sizeof(mClusterLabels[elementId].fClusterID[0]); i++) { + const auto& element = mClusterLabels[elementId].fClusterID[i]; + if (element.fMCID >= 0) { + if constexpr (WEIGHT) { + mTotalWeight += element.fWeight; + } + bool found = false; + for (unsigned int l = 0; l < mLabels.size(); l++) { + if (mLabels[l].first.fMCID == element.fMCID) { + mLabels[l].second++; + if constexpr (WEIGHT) { + mLabels[l].first.fWeight += element.fWeight; + } + found = true; + break; + } + } + if (!found) { + mLabels.emplace_back(element, 1); + } + } + } + } else { + for (const auto& element : mClusterLabels->getLabels(elementId)) { + bool found = false; + for (unsigned int l = 0; l < mLabels.size(); l++) { + if (mLabels[l].first == element) { + mLabels[l].second++; + found = true; + break; + } + } + if (!found) { + mLabels.emplace_back(element, 1); + } + } + } + mNCl++; + } + inline U computeLabel(float* labelWeight = nullptr, float* totalWeight = nullptr, int* maxCount = nullptr) + { + if (mLabels.size() == 0) { + return U(); //default constructor creates NotSet label + } else { + unsigned int bestLabelNum = 0, bestLabelCount = 0; + for (unsigned int j = 0; j < mLabels.size(); j++) { + if (mLabels[j].second > bestLabelCount) { + bestLabelNum = j; + bestLabelCount = mLabels[j].second; + } + } + auto& bestLabel = mLabels[bestLabelNum].first; + if constexpr (std::is_same<T, AliHLTTPCClusterMCWeight>::value && WEIGHT) { + *labelWeight = bestLabel.fWeight; + *totalWeight = mTotalWeight; + *maxCount = bestLabelCount; + } else { + (void)labelWeight; + (void)totalWeight; + (void)maxCount; + } + U retVal = bestLabel; + if (bestLabelCount < (1.f - mTrackMCMaxFake) * mNCl) { + retVal.setFakeFlag(); + } + return retVal; + } + } + + private: + const S* mClusterLabels; + std::vector<std::pair<T, unsigned int>> mLabels; + const float mTrackMCMaxFake; + unsigned int mNCl = 0; + float mTotalWeight = 0.f; +}; +} // namespace internal + +struct GPUTPCTrkLbl_ret { + long int id = -1; + GPUTPCTrkLbl_ret() = default; + template <class T> + GPUTPCTrkLbl_ret(T){}; +#ifdef GPUCA_TPC_GEOMETRY_O2 + GPUTPCTrkLbl_ret(const MCCompLabel& a) : id(a.getTrackEventSourceID()){}; +#endif +#ifdef GPUCA_STANDALONE + GPUTPCTrkLbl_ret(const AliHLTTPCClusterMCWeight& a) : id(a.fMCID){}; +#endif + void setFakeFlag() + { + id = -1; + } +}; + +template <bool WEIGHT = false, class U = void, class T, template <class> class S, typename... Args> +static inline auto GPUTPCTrkLbl(const S<T>* x, Args... args) +{ + if constexpr (std::is_same<U, void>::value) { + return internal::GPUTPCTrkLbl<WEIGHT, T, S<T>>(x, args...); + } else { + return internal::GPUTPCTrkLbl<WEIGHT, T, S<T>, U>(x, args...); + } +} + +template <bool WEIGHT = false, class U = void, typename... Args> +static inline auto GPUTPCTrkLbl(const AliHLTTPCClusterMCLabel* x, Args... args) +{ + using S = AliHLTTPCClusterMCLabel; + using T = AliHLTTPCClusterMCWeight; + if constexpr (std::is_same<U, void>::value) { + return internal::GPUTPCTrkLbl<WEIGHT, T, S>(x, args...); + } else { + return internal::GPUTPCTrkLbl<WEIGHT, T, S, U>(x, args...); + } +} + +} // namespace gpu +} // namespace o2 + +#endif diff --git a/GPU/GPUTracking/Standalone/qa/genEvents.cxx b/GPU/GPUTracking/Standalone/qa/genEvents.cxx index d22238a33aa33..4c99df72b8b46 100644 --- a/GPU/GPUTracking/Standalone/qa/genEvents.cxx +++ b/GPU/GPUTracking/Standalone/qa/genEvents.cxx @@ -41,6 +41,10 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace std; +namespace GPUCA_NAMESPACE::gpu +{ +extern GPUSettingsStandalone configStandalone; +} int genEvents::GetSlice(double GlobalPhi) { @@ -87,7 +91,7 @@ double genEvents::GetGaus(double sigma) double x = 0; do { x = gRandom->Gaus(0., sigma); - if (fabsf(x) <= 3.5 * sigma) { + if (fabs(x) <= 3.5 * sigma) { break; } } while (1); @@ -153,7 +157,7 @@ int genEvents::GenerateEvent(const GPUParam& param, char* filename) gRandom->SetSeed(configStandalone.seed); } - int nTracks = configStandalone.configEG.numberOfTracks; // Number of MC tracks, must be at least as large as the largest fMCID assigned above + int nTracks = configStandalone.EG.numberOfTracks; // Number of MC tracks, must be at least as large as the largest fMCID assigned above cout << "NTracks " << nTracks << endl; std::vector<GPUTPCMCInfo> mcInfo(nTracks); memset(mcInfo.data(), 0, nTracks * sizeof(mcInfo[0])); diff --git a/GPU/GPUTracking/Standalone/qconfigoptions.h b/GPU/GPUTracking/Standalone/qconfigoptions.h index 7800fa610e296..5c44f007ffd47 100644 --- a/GPU/GPUTracking/Standalone/qconfigoptions.h +++ b/GPU/GPUTracking/Standalone/qconfigoptions.h @@ -11,183 +11,6 @@ /// \file qconfigoptions.h /// \author David Rohr -// clang-format off -BeginSubConfig(structConfigGL, configGL, configStandalone, "GL", 'g', "OpenGL display settings") -AddOption(clustersOnly, bool, false, "clustersOnly", 0, "Visualize clusters only") -AddHelp("help", 'h') -EndConfig() - -BeginSubConfig(structConfigTF, configTF, configStandalone, "TF", 't', "Timeframe settings") -AddOption(nMerge, int, 0, "merge", 0, "Merge n events in a timeframe", min(0)) -AddOption(averageDistance, float, 50., "mergeDist", 0, "Average distance in cm of events merged into timeframe", min(0.f)) -AddOption(randomizeDistance, bool, true, "mergeRand", 0, "Randomize distance around average distance of merged events") -AddOption(shiftFirstEvent, bool, true, "mergeFirst", 0, "Also shift the first event in z when merging events to a timeframe") -AddOption(bunchSim, int, 0, "simBunches", 0, "Simulate correct bunch interactions instead of placing only the average number of events. A value [n] > 1 sets TFlen for [n] collisions in average. (Incompatible to TFmerge)") -AddOption(bunchCount, int, 12, "bunchCount", 0, "Number of bunches per train") -AddOption(bunchSpacing, int, 50, "bunchSpacing", 0, "Spacing between benches in ns") -AddOption(bunchTrainCount, int, 48, "bunchTrainCount", 0, "Number of bunch trains") -AddOption(abortGapTime, int, (3000), "abortGap", 0, "Length of abort gap in ns") -AddOption(interactionRate, int, 50000, "rate", 0, "Instantaneous interaction rate") -AddOption(timeFrameLen, long long int, (1000000000 / 44), "len", 'l', "Timeframe len in ns") -AddOption(noBorder, bool, false, "noBorder", 0, "Do not simulate border effects (partial events)") -AddOption(noEventRepeat, int, 0, "noEventRepeat", 0, "0: Place random events, 1: Place events in timeframe one after another, 2: Place random events but do not repat", def(1)) -AddOption(nTotalInTFEvents, int, 0, "nTotal", 0, "Total number of collisions to be placed in the interior of all time frames (excluding borders)") -AddOption(eventStride, int, 0, "eventStride", 0, "Do not select random event, but walk over array of events in stride steps") -AddOption(overlayRaw, bool, false, "overlayRaw", 0, "Overlay raw TPC data instead of spatial clusters") -AddHelp("help", 'h') -EndConfig() - -BeginSubConfig(structConfigQA, configQA, configStandalone, "QA", 'q', "QA settings") -AddOptionVec(compareInputs, const char*, "input", 0, "Read histogram from these input files and include them in the output plots") -AddOptionVec(compareInputNames, const char*, "inputName", 0, "Legend entries for data from comparison histogram files") -AddOption(name, const char*, nullptr, "name", 0, "Legend entry for new data from current processing") -AddOption(output, const char*, nullptr, "histOut", 0, "Store histograms in output root file", def("histograms.root")) -AddOption(inputHistogramsOnly, bool, false, "only", 0, "Do not run tracking, but just create PDFs from input root files") -AddOption(strict, bool, true, "strict", 0, "Strict QA mode: Only consider resolution of tracks where the fit ended within 5 cm of the reference, and remove outliers.") -AddOption(qpt, float, 10.f, "qpt", 0, "Set cut for Q/Pt", def(2.f)) -AddOption(recThreshold, float, 0.9f, "recThreshold", 0, "Compute the efficiency including impure tracks with fake contamination") -AddOption(csvDump, bool, false, "csvDump", 0, "Dump all clusters and Pt information into csv file") -AddOption(maxResX, float, 1e6f, "maxResX", 0, "Maxmimum X (~radius) for reconstructed track position to take into accound for resolution QA in cm") -AddOption(resPrimaries, int, 0, "resPrimaries", 0, "0: Resolution for all tracks, 1: only for primary tracks, 2: only for non-primaries", def(1)) -AddOption(nativeFitResolutions, bool, false, "nativeFitResolutions", 0, "Create resolution histograms in the native fit units (sin(phi), tan(lambda), Q/Pt)") -AddOption(filterCharge, int, 0, "filterCharge", 0, "Filter for positive (+1) or negative (-1) charge") -AddOption(filterPID, int, -1, "filterPID", 0, "Filter for Particle Type (0 Electron, 1 Muon, 2 Pion, 3 Kaon, 4 Proton)") -AddOption(writeMCLabels, bool, false, "writeLabels", 0, "Store mc labels to file for later matching") -AddOptionVec(matchMCLabels, const char*, "matchLabels", 0, "Read labels from files and match them, only process tracks where labels differ") -AddOption(matchDisplayMinPt, float, 0, "matchDisplayMinPt", 0, "Minimum Pt of a matched track to be displayed") -AddOption(writeRootFiles, bool, false, "root", 0, "Create ROOT canvas files") -AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "--qa", "--QAonly") -AddHelp("help", 'h') -EndConfig() - -BeginSubConfig(structConfigEG, configEG, configStandalone, "EG", 0, "Event generator settings") -AddOption(numberOfTracks, int, 1, "numberOfTracks", 0, "Number of tracks per generated event") -AddHelp("help", 'h') -EndConfig() - -BeginSubConfig(structConfigRec, configRec, configStandalone, "REC", 0, "Reconstruction settings") -AddOption(globalTracking, bool, true, "globalTracking", 0, "Enable global tracking") -AddOption(runTRD, int, -1, "trd", 0, "Enable TRD processing") -AddOption(rundEdx, int, 1, "dEdx", 0, "Enable dEdx processing") -AddOption(runCompression, int, 1, "compression", 0, "Enable TPC Compression") -AddOption(runTransformation, int, 1, "transformation", 0, "Enable TPC Transformation") -AddOption(disableRefitAttachment, int, 0, "refitAttachmentMask", 0, "Mask to disable certain attachment steps during refit (1: attachment, 2: propagation, 4: loop following, 8: mirroring)") -AddOption(tpcReject, int, -1, "tpcReject", 0, "Enable rejection of TPC clusters for compression (-1 = default, 0 = no, 1 = strategy A, 2 = strategy B)") -AddOption(tpcRejectThreshold, float, 0.f, "tpcRejectThreshold", 0, "Pt threshold to reject clusters of TPC tracks") -AddOption(tpcCompression, int, 7, "tpcCompression", 0, "TPC Compression mode bits (1=truncate charge/width LSB, 2=differences, 4=track-model)") -AddOption(tpcCompressionSort, int, 0, "tpcCompressionSort", 0, "Sort order of TPC compression (0 = time, 1 = pad, 2 = Z-time-pad, 3 = Z-pad-time, 4 = no sorting (use incoming order))") -AddOption(ForceEarlyTPCTransform, int, -1, "ForceEarlyTPCTransform", 0, "Force early TPC transformation also for continuous data (-1 = auto)") -AddOption(fwdTPCDigitsAsClusters, bool, false, "forwardTPCdigits", 0, "Forward TPC digits as clusters (if they pass the ZS threshold)") -AddOption(dropLoopers, bool, false, "dropLoopers", 0, "Drop looping tracks starting from second loop") -AddOption(mergerCovSource, int, -1, "mergerCovSource", 0, "Method to obtain covariance in track merger: 0 = simple filterErrors method, 1 = use cov from track following") -AddOption(mergerInterpolateErrors, int, -1, "mergerInterpolateErrors", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") -AddOption(fitInProjections, int, -1, "fitInProjections", 0, "Fit in projection, -1 to enable for all but passes but the first one") -AddOption(fitPropagateBzOnly, int, -1, "fitPropagateBzOnly", 0, "Propagate using Bz only for n passes") -AddOption(retryRefit, int, -1, "retryRefit", 0, "Retry refit when fit fails") -AddOption(loopInterpolationInExtraPass, int, -1, "loopPass", 0, "Perform loop interpolation in an extra pass") -AddOption(mergerReadFromTrackerDirectly, bool, true, "mergerReadFromTrackerDirectly", 0, "Forward data directly from tracker to merger on GPU") -AddHelp("help", 'h') -EndConfig() - -BeginSubConfig(structConfigProc, configProc, configStandalone, "PROC", 0, "Processing settings") -AddOption(nStreams, int, -1, "nStreams", 0, "Number of GPU streams / command queues") -AddOption(constructorPipeline, int, -1, "constructorPipeline", 0, "Run tracklet constructor in pipeline") -AddOption(selectorPipeline, int, -1, "selectorPipeline", 0, "Run tracklet selector in pipeline") -AddOption(mergerSortTracks, int, -1, "mergerSortTracks", 0, "Sort track indizes for GPU track fit") -AddOption(recoSteps, int, -1, "recoSteps", 0, "Bitmask for RecoSteps") -AddOption(recoStepsGPU, int, -1, "recoStepsGPU", 0, "Bitmask for RecoSteps") -AddOption(tpcCompressionGatherMode, int, -1, "tpcCompressionGatherMode", 0, "TPC Compressed Clusters Gather Mode") -AddOption(runMC, bool, false, "runMC", 0, "Process MC labels") -AddOption(ompKernels, bool, false, "ompKernels", 0, "Parallelize with OMP inside kernels instead of over slices") -AddOption(doublePipeline, bool, false, "doublePipeline", 0, "Double pipeline mode") -AddOption(prefetchTPCpageScan, int, 0, "prefetchTPCpageScan", 0, "Prefetch Data for TPC page scan in CPU cache") -AddHelp("help", 'h') -EndConfig() - -BeginConfig(structConfigStandalone, configStandalone) -#if defined(CUDA_ENABLED) || defined(OPENCL1_ENABLED) || defined(HIP_ENABLED) -AddOption(runGPU, bool, true, "gpu", 'g', "Use GPU for processing", message("GPU processing: %s")) -#else -AddOption(runGPU, bool, false, "gpu", 'g', "Use GPU for processing", message("GPU processing: %s")) -#endif -AddOptionSet(runGPU, bool, false, "cpu", 'c', "Use CPU for processing", message("CPU enabled")) -#if defined(CUDA_ENABLED) -AddOption(gpuType, const char*, "CUDA", "gpuType", 0, "GPU type (CUDA / HIP / OCL / OCL2)") -#elif defined(OPENCL2_ENABLED) -AddOption(gpuType, const char*, "OCL2", "gpuType", 0, "GPU type (CUDA / HIP / OCL / OCL2)") -#elif defined(OPENCL1_ENABLED) -AddOption(gpuType, const char*, "OCL", "gpuType", 0, "GPU type (CUDA / HIP / OCL / OCL2)") -#elif defined(HIP_ENABLED) -AddOption(gpuType, const char*, "HIP", "gpuType", 0, "GPU type (CUDA / HIP / OCL / OCL2)") -#else -AddOption(gpuType, const char*, "", "gpuType", 0, "GPU type (CUDA / HIP / OCL / OCL2)") -#endif -AddOption(runGPUforce, bool, true, "gpuForce", 0, "Force usage of the specified GPU device type, no CPU fallback") -AddOption(gpuInitMutex, bool, false, "gpuInitMutex", 0, "Use mutex to synchronize initialization of multiple GPU instances") -AddOption(oclGPUonly, bool, false, "oclGPUonly", 0, "Allow only GPU as OpenCL device") -AddOption(helperThreads, int, 1, "helperThreads", 0, "Number of CPU helper threads for CPU processing") -AddOption(noprompt, bool, true, "prompt", 0, "Do prompt for keypress before exiting", def(false)) -AddOption(continueOnError, bool, false, "continue", 0, "Continue processing after an error") -AddOption(DebugLevel, int, 0, "debug", 'd', "Set debug level") -AddOption(allocDebugLevel, int, 0, "allocDebug", 0, "Set alloc debug level") -AddOption(DeviceTiming, bool, true, "deviceTiming", 0, "Use device timers instead of host-based time measurement") -AddOption(seed, int, -1, "seed", 0, "Set srand seed (-1: random)") -AddOption(cleardebugout, bool, false, "clearDebugFile", 0, "Clear debug output file when processing next event") -AddOption(cudaDevice, int, -1, "gpuDevice", 0, "Set GPU device to use (-1: automatic)") -AddOption(StartEvent, int, 0, "s", 's', "First event to process", min(0)) -AddOption(NEvents, int, -1, "n", 'n', "Number of events to process (-1; all)", min(0)) -AddOption(merger, int, 1, "runMerger", 0, "Run track merging / refit", min(0), max(1)) -AddOption(runs, int, 1, "runs", 'r', "Number of iterations to perform (repeat each event)", min(0)) -AddOption(runs2, int, 1, "runsExternal", 0, "Number of iterations to perform (repeat full processing)", min(1)) -AddOption(runsInit, int, 1, "runsInit", 0, "Number of initial iterations excluded from average", min(0)) -AddOption(EventsDir, const char*, "pp", "events", 'e', "Directory with events to process", message("Reading events from Directory events/%s")) -AddOption(OMPThreads, int, -1, "omp", 't', "Number of OMP threads to run (-1: all)", min(-1), message("Using %d OMP threads")) -AddOption(eventDisplay, int, 0, "display", 'd', "Show standalone event display", def(1)) //1: default display (Windows / X11), 2: glut, 3: glfw -AddOption(qa, bool, false, "qa", 'q', "Enable tracking QA", message("Running QA: %s")) -AddOption(eventGenerator, bool, false, "eventGenerator", 0, "Run event generator") -AddOption(nways, int, 3, "NWays", 0, "Use n-way track-fit", min(1)) -AddOptionSet(nways, int, 3, "3Way", 0, "Use 3-way track-fit") -AddOptionSet(nways, int, 1, "1Way", 0, "Use 3-way track-fit") -AddOption(nwaysouter, bool, false, "OuterParam", 0, "Create OuterParam") -AddOption(dzdr, float, 2.5f, "DzDr", 0, "Use dZ/dR search window instead of vertex window") -AddOption(cont, bool, false, "continuous", 0, "Process continuous timeframe data") -AddOption(forceMemorySize, unsigned long long int, 1, "memSize", 0, "Force size of allocated GPU / page locked host memory", min(0ull)) -AddOption(forceHostMemorySize, unsigned long long int, 0, "hostMemSize", 0, "Force size of allocated host page locked host memory (overriding memSize)", min(0ull)) -AddOption(memoryScalingFactor, float, 1.f, "memoryScalingFactor", 0, "Factor to apply to all memory scalers") -AddOption(outputcontrolmem, unsigned long long int, 0, "outputMemory", 0, "Use predefined output buffer of this size", min(0ull), message("Using %lld bytes as output memory")) -AddOption(inputcontrolmem, unsigned long long int, 0, "inputMemory", 0, "Use predefined input buffer of this size", min(0ull), message("Using %lld bytes as input memory")) -AddOption(registerInputMemory, bool, false, "registerInputMemory", 0, "Automatically register input memory buffers for the GPU") -AddOption(affinity, int, -1, "cpuAffinity", 0, "Pin CPU affinity to this CPU core", min(-1)) -AddOption(fifo, bool, false, "fifoScheduler", 0, "Use FIFO realtime scheduler", message("Setting FIFO scheduler: %s")) -AddOption(fpe, bool, true, "fpe", 0, "Trap on floating point exceptions") -AddOption(flushDenormals, bool, true, "flushDenormals", 0, "Enable FTZ and DAZ (Flush all denormals to zero)") -AddOption(solenoidBz, float, -1e6f, "solenoidBz", 0, "Field strength of solenoid Bz in kGaus") -AddOption(constBz, bool, false, "constBz", 0, "Force constand Bz") -AddOption(overrideMaxTimebin, bool, false, "overrideMaxTimebin", 0, "Override max time bin setting for continuous data with max time bin in time frame") -AddOption(encodeZS, int, -1, "encodeZS", 0, "Zero-Suppress TPC data", def(1)) -AddOption(zsFilter, int, -1, "zsFilter", 0, "Apply Zero-Suppression when loading digits and remove those below threshold", def(1)) -AddOption(zsThreshold, float, 2.0f, "zsThreshold", 0, "Zero-Suppression threshold") -AddOption(zs12bit, bool, true, "zs12bit", 0, "Perform 12 bit zero-suppression encoding / filter") -AddOption(dumpEvents, bool, false, "dumpEvents", 0, "Dump events (after transformation such as encodeZS") -AddOption(stripDumpedEvents, bool, false, "stripDumpedEvents", 0, "Remove redundant inputs (e.g. digits and ZS) before dumping") -AddOption(referenceX, float, 500.f, "referenceX", 0, "Reference X position to transport track to after fit") -AddOption(rejectMode, char, 5, "rejectMode", 0, "Merger Reject Mode") -AddOption(allocationStrategy, int, 0, "allocationStrategy", 0, "Memory Allocation Stragegy (0 = auto, 1 = individual allocations, 2 = single global allocation)") -AddOption(printSettings, bool, false, "printSettings", 0, "Print all settings") -AddOption(compressionStat, bool, false, "compressionStat", 0, "Run statistics and verification for cluster compression") -AddOption(memoryStat, bool, false, "memoryStat", 0, "Print memory statistics") -AddOption(testSyncAsync, bool, false, "syncAsync", 0, "Test first synchronous and then asynchronous processing") -AddOption(testSync, bool, false, "sync", 0, "Test settings for synchronous phase") -AddOption(timeFrameTime, bool, false, "tfTime", 0, "Print some debug information about time frame processing time") -AddOption(controlProfiler, bool, false, "controlProfiler", 0, "Issues GPU profiler stop and start commands to profile only the relevant processing part") -AddOption(alternateBorderSort, int, -1, "alternateBorderSort", 0, "Alternative implementation for sorting of border tracks") -AddHelp("help", 'h') -AddHelpAll("helpall", 'H') -AddSubConfig(structConfigTF, configTF) -AddSubConfig(structConfigQA, configQA) -AddSubConfig(structConfigEG, configEG) -AddSubConfig(structConfigGL, configGL) -AddSubConfig(structConfigRec, configRec) -AddSubConfig(structConfigProc, configProc) -EndConfig() - // clang-format on +#include "GPUSettings.h" // Usually we are included from GPUSettings.h, so this doesn't do anything. + // But ROOT or qconfig.cpp will include us directly, so load the settings first. +#include "GPUSettingsList.h" diff --git a/GPU/GPUTracking/Standalone/standalone.cxx b/GPU/GPUTracking/Standalone/standalone.cxx index 629377b79eb81..d859367c2490e 100644 --- a/GPU/GPUTracking/Standalone/standalone.cxx +++ b/GPU/GPUTracking/Standalone/standalone.cxx @@ -75,6 +75,11 @@ using namespace GPUCA_NAMESPACE::gpu; //#define BROKEN_EVENTS +namespace GPUCA_NAMESPACE::gpu +{ +extern GPUSettingsStandalone configStandalone; +} + GPUReconstruction *rec, *recAsync, *recPipeline; GPUChainTracking *chainTracking, *chainTrackingAsync, *chainTrackingPipeline; #ifdef HAVE_O2HEADERS @@ -86,6 +91,9 @@ std::unique_ptr<GPUReconstructionTimeframe> tf; int nEventsInDirectory = 0; std::atomic<unsigned int> nIteration, nIterationEnd; +std::vector<GPUTrackingInOutPointers> ioPtrEvents; +std::vector<GPUChainTracking::InOutMemory> ioMemEvents; + void SetCPUAndOSSettings() { #ifdef FE_DFL_DISABLE_SSE_DENORMS_ENV // Flush and load denormals to zero in any case @@ -113,22 +121,24 @@ int ReadConfiguration(int argc, char** argv) if (configStandalone.printSettings) { qConfigPrint(); } - + if (configStandalone.proc.debugLevel < 0) { + configStandalone.proc.debugLevel = 0; + } #ifndef _WIN32 setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, ""); - if (configStandalone.affinity != -1) { + if (configStandalone.cpuAffinity != -1) { cpu_set_t mask; CPU_ZERO(&mask); - CPU_SET(configStandalone.affinity, &mask); + CPU_SET(configStandalone.cpuAffinity, &mask); - printf("Setting affinitiy to restrict on CPU core %d\n", configStandalone.affinity); + printf("Setting affinitiy to restrict on CPU core %d\n", configStandalone.cpuAffinity); if (0 != sched_setaffinity(0, sizeof(mask), &mask)) { printf("Error setting CPU affinity\n"); return 1; } } - if (configStandalone.fifo) { + if (configStandalone.fifoScheduler) { printf("Setting FIFO scheduler\n"); sched_param param; sched_getparam(0, ¶m); @@ -146,11 +156,11 @@ int ReadConfiguration(int argc, char** argv) } #else - if (configStandalone.affinity != -1) { + if (configStandalone.cpuAffinity != -1) { printf("Affinity setting not supported on Windows\n"); return 1; } - if (configStandalone.fifo) { + if (configStandalone.fifoScheduler) { printf("FIFO Scheduler setting not supported on Windows\n"); return 1; } @@ -160,19 +170,24 @@ int ReadConfiguration(int argc, char** argv) } #endif #ifndef HAVE_O2HEADERS - configStandalone.configRec.runTRD = configStandalone.configRec.rundEdx = configStandalone.configRec.runCompression = configStandalone.configRec.runTransformation = configStandalone.testSyncAsync = configStandalone.testSync = 0; - configStandalone.configRec.ForceEarlyTPCTransform = 1; + configStandalone.runTRD = configStandalone.rundEdx = configStandalone.runCompression = configStandalone.runTransformation = configStandalone.testSyncAsync = configStandalone.testSync = 0; + configStandalone.rec.ForceEarlyTPCTransform = 1; + configStandalone.runRefit = false; #endif #ifndef GPUCA_TPC_GEOMETRY_O2 - configStandalone.configRec.mergerReadFromTrackerDirectly = 0; + configStandalone.rec.mergerReadFromTrackerDirectly = 0; + configStandalone.proc.ompKernels = false; + if (configStandalone.rundEdx == -1) { + configStandalone.rundEdx = 0; + } #endif #ifndef GPUCA_BUILD_QA - if (configStandalone.qa || configStandalone.eventGenerator) { + if (configStandalone.proc.runQA || configStandalone.eventGenerator) { printf("QA not enabled in build\n"); return 1; } #endif - if (configStandalone.qa) { + if (configStandalone.proc.runQA) { if (getenv("LC_NUMERIC")) { printf("Please unset the LC_NUMERIC env variable, otherwise ROOT will not be able to fit correctly\n"); // BUG: ROOT Problem return 1; @@ -184,55 +199,45 @@ int ReadConfiguration(int argc, char** argv) return 1; } #endif - if (configStandalone.configProc.doublePipeline && configStandalone.testSyncAsync) { + if (configStandalone.proc.doublePipeline && configStandalone.testSyncAsync) { printf("Cannot run asynchronous processing with double pipeline\n"); return 1; } - if (configStandalone.configProc.doublePipeline && (configStandalone.runs < 3 || !configStandalone.outputcontrolmem)) { + if (configStandalone.proc.doublePipeline && (configStandalone.runs < 4 || !configStandalone.outputcontrolmem)) { printf("Double pipeline mode needs at least 3 runs per event and external output\n"); return 1; } - if (configStandalone.configTF.bunchSim && configStandalone.configTF.nMerge) { + if (configStandalone.TF.bunchSim && configStandalone.TF.nMerge) { printf("Cannot run --MERGE and --SIMBUNCHES togeterh\n"); return 1; } - if (configStandalone.configTF.bunchSim > 1) { - configStandalone.configTF.timeFrameLen = 1.e9 * configStandalone.configTF.bunchSim / configStandalone.configTF.interactionRate; + if (configStandalone.TF.bunchSim > 1) { + configStandalone.TF.timeFrameLen = 1.e9 * configStandalone.TF.bunchSim / configStandalone.TF.interactionRate; } - if (configStandalone.configTF.nMerge) { - double len = configStandalone.configTF.nMerge - 1; - if (configStandalone.configTF.randomizeDistance) { + if (configStandalone.TF.nMerge) { + double len = configStandalone.TF.nMerge - 1; + if (configStandalone.TF.randomizeDistance) { len += 0.5; } - if (configStandalone.configTF.shiftFirstEvent) { + if (configStandalone.TF.shiftFirstEvent) { len += 0.5; } - configStandalone.configTF.timeFrameLen = (len * configStandalone.configTF.averageDistance / GPUReconstructionTimeframe::TPCZ + 1) * GPUReconstructionTimeframe::DRIFT_TIME; + configStandalone.TF.timeFrameLen = (len * configStandalone.TF.averageDistance / GPUReconstructionTimeframe::TPCZ + 1) * GPUReconstructionTimeframe::DRIFT_TIME; } - if (configStandalone.configQA.inputHistogramsOnly && configStandalone.configQA.compareInputs.size() == 0) { + if (configStandalone.QA.inputHistogramsOnly && configStandalone.QA.compareInputs.size() == 0) { printf("Can only produce QA pdf output when input files are specified!\n"); return 1; } + if (configStandalone.QA.inputHistogramsOnly) { + configStandalone.rundEdx = false; + } if (configStandalone.eventDisplay) { configStandalone.noprompt = 1; } - if (configStandalone.DebugLevel >= 4) { - configStandalone.OMPThreads = 1; + if (configStandalone.proc.debugLevel >= 4 && !configStandalone.proc.ompKernels) { + configStandalone.proc.ompThreads = 1; } -#ifdef WITH_OPENMP - if (configStandalone.OMPThreads != -1) { - omp_set_num_threads(configStandalone.OMPThreads); - } else { - configStandalone.OMPThreads = omp_get_max_threads(); - } - if (configStandalone.OMPThreads != omp_get_max_threads()) { - printf("Cannot set number of OMP threads!\n"); - return 1; - } -#else - configStandalone.OMPThreads = 1; -#endif if (configStandalone.outputcontrolmem) { bool forceEmptyMemory = getenv("LD_PRELOAD") && strstr(getenv("LD_PRELOAD"), "valgrind") != nullptr; outputmemory.reset(new char[configStandalone.outputcontrolmem]); @@ -240,7 +245,7 @@ int ReadConfiguration(int argc, char** argv) printf("Valgrind detected, emptying GPU output memory to avoid false positive undefined reads"); memset(outputmemory.get(), 0, configStandalone.outputcontrolmem); } - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { outputmemoryPipeline.reset(new char[configStandalone.outputcontrolmem]); if (forceEmptyMemory) { memset(outputmemoryPipeline.get(), 0, configStandalone.outputcontrolmem); @@ -259,6 +264,8 @@ int ReadConfiguration(int argc, char** argv) return 1; } #endif + + configStandalone.proc.showOutputStat = true; return (0); } @@ -275,14 +282,16 @@ int SetupReconstruction() if (configStandalone.testSyncAsync) { recAsync->ReadSettings(filename); } - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { recPipeline->ReadSettings(filename); } } GPUSettingsEvent ev = rec->GetEventSettings(); GPUSettingsRec recSet; - GPUSettingsDeviceProcessing devProc; + GPUSettingsProcessing devProc; + memcpy((void*)&recSet, (void*)&configStandalone.rec, sizeof(GPUSettingsRec)); + memcpy((void*)&devProc, (void*)&configStandalone.proc, sizeof(GPUSettingsProcessing)); GPURecoStepConfiguration steps; if (configStandalone.eventGenerator) { @@ -294,7 +303,7 @@ int SetupReconstruction() if (configStandalone.constBz) { ev.constBz = true; } - if (configStandalone.configTF.nMerge || configStandalone.configTF.bunchSim) { + if (configStandalone.TF.nMerge || configStandalone.TF.bunchSim) { if (ev.continuousMaxTimeBin) { printf("ERROR: requested to overlay continuous data - not supported\n"); return 1; @@ -304,7 +313,7 @@ int SetupReconstruction() configStandalone.cont = true; } if (chainTracking->GetTPCTransform()) { - ev.continuousMaxTimeBin = configStandalone.configTF.timeFrameLen * ((double)GPUReconstructionTimeframe::TPCZ / (double)GPUReconstructionTimeframe::DRIFT_TIME) / chainTracking->GetTPCTransform()->getVDrift(); + ev.continuousMaxTimeBin = configStandalone.TF.timeFrameLen * ((double)GPUReconstructionTimeframe::TPCZ / (double)GPUReconstructionTimeframe::DRIFT_TIME) / chainTracking->GetTPCTransform()->getVDrift(); } } if (configStandalone.cont && ev.continuousMaxTimeBin == 0) { @@ -316,58 +325,7 @@ int SetupReconstruction() printf("Standalone Test Framework for CA Tracker - Using GPU\n"); } - recSet.SetMinTrackPt(GPUCA_MIN_TRACK_PT_DEFAULT); - recSet.NWays = configStandalone.nways; - recSet.NWaysOuter = configStandalone.nwaysouter; - recSet.RejectMode = configStandalone.rejectMode; - recSet.SearchWindowDZDR = configStandalone.dzdr; - recSet.GlobalTracking = configStandalone.configRec.globalTracking; - recSet.DisableRefitAttachment = configStandalone.configRec.disableRefitAttachment; - recSet.ForceEarlyTPCTransform = configStandalone.configRec.ForceEarlyTPCTransform; - recSet.fwdTPCDigitsAsClusters = configStandalone.configRec.fwdTPCDigitsAsClusters; - recSet.dropLoopers = configStandalone.configRec.dropLoopers; - if (configStandalone.configRec.mergerCovSource != -1) { - recSet.mergerCovSource = configStandalone.configRec.mergerCovSource; - } - if (configStandalone.configRec.mergerInterpolateErrors != -1) { - recSet.mergerInterpolateErrors = configStandalone.configRec.mergerInterpolateErrors; - } - if (configStandalone.referenceX < 500.) { - recSet.TrackReferenceX = configStandalone.referenceX; - } - recSet.tpcZSthreshold = configStandalone.zsThreshold; - if (configStandalone.configRec.fitInProjections != -1) { - recSet.fitInProjections = configStandalone.configRec.fitInProjections; - } - if (configStandalone.configRec.fitPropagateBzOnly != -1) { - recSet.fitPropagateBzOnly = configStandalone.configRec.fitPropagateBzOnly; - } - if (configStandalone.configRec.retryRefit != -1) { - recSet.retryRefit = configStandalone.configRec.retryRefit; - } - recSet.loopInterpolationInExtraPass = configStandalone.configRec.loopInterpolationInExtraPass; - recSet.mergerReadFromTrackerDirectly = configStandalone.configRec.mergerReadFromTrackerDirectly; - if (!recSet.mergerReadFromTrackerDirectly) { - devProc.fullMergerOnGPU = false; - } - - if (configStandalone.OMPThreads != -1) { - devProc.nThreads = configStandalone.OMPThreads; - } - devProc.deviceNum = configStandalone.cudaDevice; - devProc.forceMemoryPoolSize = (configStandalone.forceMemorySize == 1 && configStandalone.eventDisplay) ? 2 : configStandalone.forceMemorySize; - devProc.forceHostMemoryPoolSize = configStandalone.forceHostMemorySize; - devProc.debugLevel = configStandalone.DebugLevel; - devProc.allocDebugLevel = configStandalone.allocDebugLevel; - devProc.deviceTimers = configStandalone.DeviceTiming; - devProc.runQA = configStandalone.qa; - devProc.runMC = configStandalone.configProc.runMC; - devProc.ompKernels = configStandalone.configProc.ompKernels; - devProc.runCompressionStatistics = configStandalone.compressionStat; - devProc.memoryScalingFactor = configStandalone.memoryScalingFactor; - devProc.alternateBorderSort = configStandalone.alternateBorderSort; - devProc.doublePipeline = configStandalone.configProc.doublePipeline; - devProc.prefetchTPCpageScan = configStandalone.configProc.prefetchTPCpageScan; + configStandalone.proc.forceMemoryPoolSize = (configStandalone.proc.forceMemoryPoolSize == 1 && configStandalone.eventDisplay) ? 2 : configStandalone.proc.forceMemoryPoolSize; if (configStandalone.eventDisplay) { #ifdef GPUCA_BUILD_EVENT_DISPLAY #ifdef _WIN32 @@ -395,54 +353,32 @@ int SetupReconstruction() #endif devProc.eventDisplay = eventDisplay.get(); } - devProc.nDeviceHelperThreads = configStandalone.helperThreads; - devProc.globalInitMutex = configStandalone.gpuInitMutex; - devProc.gpuDeviceOnly = configStandalone.oclGPUonly; - devProc.memoryAllocationStrategy = configStandalone.allocationStrategy; - devProc.registerStandaloneInputMemory = configStandalone.registerInputMemory; - if (configStandalone.configRec.tpcReject != -1) { - recSet.tpcRejectionMode = configStandalone.configRec.tpcReject; - } - if (configStandalone.configRec.tpcRejectThreshold != 0.f) { - recSet.tpcRejectQPt = 1.f / configStandalone.configRec.tpcRejectThreshold; - } - recSet.tpcCompressionModes = configStandalone.configRec.tpcCompression; - recSet.tpcCompressionSortOrder = configStandalone.configRec.tpcCompressionSort; - - if (configStandalone.configProc.nStreams >= 0) { - devProc.nStreams = configStandalone.configProc.nStreams; - } - if (configStandalone.configProc.constructorPipeline >= 0) { - devProc.trackletConstructorInPipeline = configStandalone.configProc.constructorPipeline; - } - if (configStandalone.configProc.selectorPipeline >= 0) { - devProc.trackletSelectorInPipeline = configStandalone.configProc.selectorPipeline; - } - devProc.mergerSortTracks = configStandalone.configProc.mergerSortTracks; - devProc.tpcCompressionGatherMode = configStandalone.configProc.tpcCompressionGatherMode; steps.steps = GPUDataTypes::RecoStep::AllRecoSteps; - if (configStandalone.configRec.runTRD != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, configStandalone.configRec.runTRD > 0); + if (configStandalone.runTRD != -1) { + steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, configStandalone.runTRD > 0); } else if (chainTracking->GetTRDGeometry() == nullptr) { steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, false); } - if (configStandalone.configRec.rundEdx != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, configStandalone.configRec.rundEdx > 0); + if (configStandalone.rundEdx != -1) { + steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, configStandalone.rundEdx > 0); } - if (configStandalone.configRec.runCompression != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, configStandalone.configRec.runCompression > 0); + if (configStandalone.runCompression != -1) { + steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, configStandalone.runCompression > 0); } - if (configStandalone.configRec.runTransformation != -1) { - steps.steps.setBits(GPUDataTypes::RecoStep::TPCConversion, configStandalone.configRec.runTransformation > 0); + if (configStandalone.runTransformation != -1) { + steps.steps.setBits(GPUDataTypes::RecoStep::TPCConversion, configStandalone.runTransformation > 0); } - if (!configStandalone.merger) { + steps.steps.setBits(GPUDataTypes::RecoStep::Refit, configStandalone.runRefit); + if (!configStandalone.runMerger) { steps.steps.setBits(GPUDataTypes::RecoStep::TPCMerging, false); steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, false); steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, false); steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); + steps.steps.setBits(GPUDataTypes::RecoStep::Refit, false); } - if (configStandalone.configTF.bunchSim || configStandalone.configTF.nMerge) { + + if (configStandalone.TF.bunchSim || configStandalone.TF.nMerge) { steps.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, false); } steps.inputs.set(GPUDataTypes::InOutType::TPCClusters, GPUDataTypes::InOutType::TRDTracklets); @@ -453,11 +389,11 @@ int SetupReconstruction() steps.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, false); } - if (configStandalone.configProc.recoSteps >= 0) { - steps.steps &= configStandalone.configProc.recoSteps; + if (configStandalone.recoSteps >= 0) { + steps.steps &= configStandalone.recoSteps; } - if (configStandalone.configProc.recoStepsGPU >= 0) { - steps.stepsGPUMask &= configStandalone.configProc.recoStepsGPU; + if (configStandalone.recoStepsGPU >= 0) { + steps.stepsGPUMask &= configStandalone.recoStepsGPU; } steps.outputs.clear(); @@ -478,7 +414,7 @@ int SetupReconstruction() } } rec->SetSettings(&ev, &recSet, &devProc, &steps); - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { recPipeline->SetSettings(&ev, &recSet, &devProc, &steps); } if (configStandalone.testSyncAsync) { @@ -504,17 +440,27 @@ int SetupReconstruction() if (configStandalone.outputcontrolmem) { rec->SetOutputControl(outputmemory.get(), configStandalone.outputcontrolmem); - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { recPipeline->SetOutputControl(outputmemoryPipeline.get(), configStandalone.outputcontrolmem); } } +#ifdef HAVE_O2HEADERS + chainTracking->SetDefaultO2PropagatorForGPU(); + if (configStandalone.testSyncAsync) { + chainTrackingAsync->SetDefaultO2PropagatorForGPU(); + } + if (configStandalone.proc.doublePipeline) { + chainTrackingPipeline->SetDefaultO2PropagatorForGPU(); + } +#endif + if (rec->Init()) { printf("Error initializing GPUReconstruction!\n"); return 1; } if (configStandalone.outputcontrolmem && rec->IsGPU()) { - if (rec->registerMemoryForGPU(outputmemory.get(), configStandalone.outputcontrolmem) || (configStandalone.configProc.doublePipeline && recPipeline->registerMemoryForGPU(outputmemoryPipeline.get(), configStandalone.outputcontrolmem))) { + if (rec->registerMemoryForGPU(outputmemory.get(), configStandalone.outputcontrolmem) || (configStandalone.proc.doublePipeline && recPipeline->registerMemoryForGPU(outputmemoryPipeline.get(), configStandalone.outputcontrolmem))) { printf("ERROR registering memory for the GPU!!!\n"); return 1; } @@ -525,7 +471,7 @@ int SetupReconstruction() return 1; } } - if (configStandalone.DebugLevel >= 4) { + if (configStandalone.proc.debugLevel >= 4) { rec->PrintKernelOccupancies(); } return (0); @@ -535,15 +481,15 @@ int ReadEvent(int n) { char filename[256]; snprintf(filename, 256, "events/%s/" GPUCA_EVDUMP_FILE ".%d.dump", configStandalone.EventsDir, n); - if (configStandalone.inputcontrolmem) { + if (configStandalone.inputcontrolmem && !configStandalone.preloadEvents) { rec->SetInputControl(inputmemory.get(), configStandalone.inputcontrolmem); } int r = chainTracking->ReadData(filename); if (r) { return r; } - if (chainTracking->mIOPtrs.clustersNative && (configStandalone.configTF.bunchSim || configStandalone.configTF.nMerge || !configStandalone.configRec.runTransformation)) { - if (configStandalone.DebugLevel >= 2) { + if (chainTracking->mIOPtrs.clustersNative && (configStandalone.TF.bunchSim || configStandalone.TF.nMerge || !configStandalone.runTransformation)) { + if (configStandalone.proc.debugLevel >= 2) { printf("Converting Native to Legacy ClusterData for overlaying - WARNING: No raw clusters produced - Compression etc will not run!!!\n"); } chainTracking->ConvertNativeToClusterDataLegacy(); @@ -551,42 +497,80 @@ int ReadEvent(int n) return 0; } +int LoadEvent(int iEvent, int x) +{ + if (configStandalone.TF.bunchSim) { + if (tf->LoadCreateTimeFrame(iEvent)) { + return 1; + } + } else if (configStandalone.TF.nMerge) { + if (tf->LoadMergedEvents(iEvent)) { + return 1; + } + } else { + if (ReadEvent(iEvent)) { + return 1; + } + } + bool encodeZS = configStandalone.encodeZS == -1 ? (chainTracking->mIOPtrs.tpcPackedDigits && !chainTracking->mIOPtrs.tpcZS) : (bool)configStandalone.encodeZS; + bool zsFilter = configStandalone.zsFilter == -1 ? (!encodeZS && chainTracking->mIOPtrs.tpcPackedDigits) : (bool)configStandalone.zsFilter; + if (encodeZS || zsFilter) { + if (!chainTracking->mIOPtrs.tpcPackedDigits) { + printf("Need digit input to run ZS\n"); + return 1; + } + if (zsFilter) { + chainTracking->ConvertZSFilter(configStandalone.zs12bit); + } + if (encodeZS) { + chainTracking->ConvertZSEncoder(configStandalone.zs12bit); + } + } + if (!configStandalone.runTransformation) { + chainTracking->mIOPtrs.clustersNative = nullptr; + } else { + for (int i = 0; i < chainTracking->NSLICES; i++) { + if (chainTracking->mIOPtrs.rawClusters[i]) { + if (configStandalone.proc.debugLevel >= 2) { + printf("Converting Legacy Raw Cluster to Native\n"); + } + chainTracking->ConvertRun2RawToNative(); + break; + } + } + } + + if (configStandalone.stripDumpedEvents) { + if (chainTracking->mIOPtrs.tpcZS) { + chainTracking->mIOPtrs.tpcPackedDigits = nullptr; + } + } + + if (!rec->GetParam().par.earlyTpcTransform && chainTracking->mIOPtrs.clustersNative == nullptr && chainTracking->mIOPtrs.tpcPackedDigits == nullptr && chainTracking->mIOPtrs.tpcZS == nullptr) { + printf("Need cluster native data for on-the-fly TPC transform\n"); + return 1; + } + + ioPtrEvents[x] = chainTracking->mIOPtrs; + ioMemEvents[x] = std::move(chainTracking->mIOMem); + return 0; +} + void OutputStat(GPUChainTracking* t, long long int* nTracksTotal = nullptr, long long int* nClustersTotal = nullptr) { - int nTracks = 0, nAttachedClusters = 0, nAttachedClustersFitted = 0, nAdjacentClusters = 0; + int nTracks = 0; for (unsigned int k = 0; k < t->mIOPtrs.nMergedTracks; k++) { if (t->mIOPtrs.mergedTracks[k].OK()) { nTracks++; - nAttachedClusters += t->mIOPtrs.mergedTracks[k].NClusters(); - nAttachedClustersFitted += t->mIOPtrs.mergedTracks[k].NClustersFitted(); } } - unsigned int nCls = configStandalone.configProc.doublePipeline ? t->mIOPtrs.clustersNative->nClustersTotal : t->GetTPCMerger().NMaxClusters(); - for (unsigned int k = 0; k < nCls; k++) { - int attach = t->mIOPtrs.mergedTrackHitAttachment[k]; - if (attach & GPUTPCGMMergerTypes::attachFlagMask) { - nAdjacentClusters++; - } - } - if (nTracksTotal && nClustersTotal) { *nTracksTotal += nTracks; *nClustersTotal += t->mIOPtrs.nMergedTrackHits; } - - char trdText[1024] = ""; - if (t->GetRecoSteps() & GPUDataTypes::RecoStep::TRDTracking) { - int nTracklets = 0; - for (unsigned int k = 0; k < t->mIOPtrs.nTRDTracks; k++) { - auto& trk = t->mIOPtrs.trdTracks[k]; - nTracklets += trk.GetNtracklets(); - } - snprintf(trdText, 1024, " - TRD Tracker reconstructed %d tracks (%d tracklets)", t->mIOPtrs.nTRDTracks, nTracklets); - } - printf("Output Tracks: %d (%d / %d / %d / %d clusters (fitted / attached / adjacent / total))%s\n", nTracks, nAttachedClustersFitted, nAttachedClusters, nAdjacentClusters, nCls, trdText); } -int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, int runs, const GPUTrackingInOutPointers& ioPtrs, long long int* nTracksTotal, long long int* nClustersTotal, int threadId = 0, HighResTimer* timerPipeline = nullptr) +int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, int runs, int iEvent, long long int* nTracksTotal, long long int* nClustersTotal, int threadId = 0, HighResTimer* timerPipeline = nullptr) { int iRun = 0, iteration = 0; while ((iteration = nIteration.fetch_add(1)) < runs) { @@ -601,9 +585,10 @@ int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, if (configStandalone.testSyncAsync) { printf("Running synchronous phase\n"); } + const GPUTrackingInOutPointers& ioPtrs = ioPtrEvents[!configStandalone.preloadEvents ? 0 : configStandalone.proc.doublePipeline ? (iteration % ioPtrEvents.size()) : (iEvent - configStandalone.StartEvent)]; chainTrackingUse->mIOPtrs = ioPtrs; - if (iteration == (configStandalone.configProc.doublePipeline ? 1 : (configStandalone.runs - 1))) { - if (configStandalone.configProc.doublePipeline) { + if (iteration == (configStandalone.proc.doublePipeline ? 2 : (configStandalone.runs - 1))) { + if (configStandalone.proc.doublePipeline) { timerPipeline->Start(); } if (configStandalone.controlProfiler) { @@ -613,7 +598,7 @@ int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, int tmpRetVal = recUse->RunChains(); int iterationEnd = nIterationEnd.fetch_add(1); if (iterationEnd == configStandalone.runs - 1) { - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { timerPipeline->Stop(); } if (configStandalone.controlProfiler) { @@ -625,7 +610,7 @@ int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, OutputStat(chainTrackingUse, iRun == 0 ? nTracksTotal : nullptr, iRun == 0 ? nClustersTotal : nullptr); if (configStandalone.memoryStat) { recUse->PrintMemoryStatistics(); - } else if (configStandalone.DebugLevel >= 2) { + } else if (configStandalone.proc.debugLevel >= 2) { recUse->PrintMemoryOverview(); } } @@ -665,7 +650,7 @@ int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, recAsync->ClearAllocatedMemory(); } #endif - if (!configStandalone.configProc.doublePipeline) { + if (!configStandalone.proc.doublePipeline) { recUse->ClearAllocatedMemory(); } @@ -681,9 +666,10 @@ int RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingUse, } iRun++; } - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { recUse->ClearAllocatedMemory(); } + nIteration.store(runs); return 0; } @@ -703,7 +689,7 @@ int main(int argc, char** argv) recUniqueAsync.reset(GPUReconstruction::CreateInstance(configStandalone.runGPU ? configStandalone.gpuType : GPUReconstruction::DEVICE_TYPE_NAMES[GPUReconstruction::DeviceType::CPU], configStandalone.runGPUforce, rec)); recAsync = recUniqueAsync.get(); } - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { recUniquePipeline.reset(GPUReconstruction::CreateInstance(configStandalone.runGPU ? configStandalone.gpuType : GPUReconstruction::DEVICE_TYPE_NAMES[GPUReconstruction::DeviceType::CPU], configStandalone.runGPUforce, rec)); recPipeline = recUniquePipeline.get(); } @@ -711,22 +697,22 @@ int main(int argc, char** argv) printf("Error initializing GPUReconstruction\n"); return 1; } - rec->SetDebugLevelTmp(configStandalone.DebugLevel); + rec->SetDebugLevelTmp(configStandalone.proc.debugLevel); chainTracking = rec->AddChain<GPUChainTracking>(); if (configStandalone.testSyncAsync) { - if (configStandalone.DebugLevel >= 3) { - recAsync->SetDebugLevelTmp(configStandalone.DebugLevel); + if (configStandalone.proc.debugLevel >= 3) { + recAsync->SetDebugLevelTmp(configStandalone.proc.debugLevel); } chainTrackingAsync = recAsync->AddChain<GPUChainTracking>(); } - if (configStandalone.configProc.doublePipeline) { - if (configStandalone.DebugLevel >= 3) { - recPipeline->SetDebugLevelTmp(configStandalone.DebugLevel); + if (configStandalone.proc.doublePipeline) { + if (configStandalone.proc.debugLevel >= 3) { + recPipeline->SetDebugLevelTmp(configStandalone.proc.debugLevel); } chainTrackingPipeline = recPipeline->AddChain<GPUChainTracking>(); } #ifdef HAVE_O2HEADERS - if (!configStandalone.configProc.doublePipeline) { + if (!configStandalone.proc.doublePipeline) { chainITS = rec->AddChain<GPUChainITS>(0); if (configStandalone.testSyncAsync) { chainITSAsync = recAsync->AddChain<GPUChainITS>(0); @@ -739,12 +725,10 @@ int main(int argc, char** argv) } std::unique_ptr<std::thread> pipelineThread; - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { pipelineThread.reset(new std::thread([]() { rec->RunPipelineWorker(); })); } - // hlt.SetRunMerger(configStandalone.merger); //TODO! - if (configStandalone.seed == -1) { std::random_device rd; configStandalone.seed = (int)rd(); @@ -764,92 +748,64 @@ int main(int argc, char** argv) in.close(); } - if (configStandalone.configTF.bunchSim || configStandalone.configTF.nMerge) { + if (configStandalone.TF.bunchSim || configStandalone.TF.nMerge) { tf.reset(new GPUReconstructionTimeframe(chainTracking, ReadEvent, nEventsInDirectory)); } if (configStandalone.eventGenerator) { genEvents::RunEventGenerator(chainTracking); - return 1; + return 0; + } + + int nEvents = configStandalone.NEvents; + if (configStandalone.TF.bunchSim) { + nEvents = configStandalone.NEvents > 0 ? configStandalone.NEvents : 1; } else { - int nEvents = configStandalone.NEvents; - if (configStandalone.configTF.bunchSim) { - nEvents = configStandalone.NEvents > 0 ? configStandalone.NEvents : 1; - } else { - if (nEvents == -1 || nEvents > nEventsInDirectory) { - if (nEvents >= 0) { - printf("Only %d events available in directors %s (%d events requested)\n", nEventsInDirectory, configStandalone.EventsDir, nEvents); - } - nEvents = nEventsInDirectory; - } - if (configStandalone.configTF.nMerge > 1) { - nEvents /= configStandalone.configTF.nMerge; + if (nEvents == -1 || nEvents > nEventsInDirectory) { + if (nEvents >= 0) { + printf("Only %d events available in directors %s (%d events requested)\n", nEventsInDirectory, configStandalone.EventsDir, nEvents); } + nEvents = nEventsInDirectory; } + if (configStandalone.TF.nMerge > 1) { + nEvents /= configStandalone.TF.nMerge; + } + } - for (int iRun = 0; iRun < configStandalone.runs2; iRun++) { - if (configStandalone.configQA.inputHistogramsOnly) { - chainTracking->ForceInitQA(); - break; - } - if (configStandalone.runs2 > 1) { - printf("RUN2: %d\n", iRun); - } - long long int nTracksTotal = 0; - long long int nClustersTotal = 0; - int nEventsProcessed = 0; + ioPtrEvents.resize(configStandalone.preloadEvents ? (nEvents - configStandalone.StartEvent) : 1); + ioMemEvents.resize(configStandalone.preloadEvents ? (nEvents - configStandalone.StartEvent) : 1); + if (configStandalone.preloadEvents) { + printf("Preloading events"); + fflush(stdout); + for (int i = 0; i < nEvents - configStandalone.StartEvent; i++) { + LoadEvent(configStandalone.StartEvent + i, i); + printf(" %d", i); + fflush(stdout); + } + printf("\n"); + } - for (int iEvent = configStandalone.StartEvent; iEvent < nEvents; iEvent++) { - if (iEvent != configStandalone.StartEvent) { - printf("\n"); - } + for (int iRunOuter = 0; iRunOuter < configStandalone.runs2; iRunOuter++) { + if (configStandalone.QA.inputHistogramsOnly) { + chainTracking->ForceInitQA(); + break; + } + if (configStandalone.runs2 > 1) { + printf("RUN2: %d\n", iRunOuter); + } + long long int nTracksTotal = 0; + long long int nClustersTotal = 0; + int nEventsProcessed = 0; + + for (int iEvent = configStandalone.StartEvent; iEvent < nEvents; iEvent++) { + if (iEvent != configStandalone.StartEvent) { + printf("\n"); + } + if (!configStandalone.preloadEvents) { HighResTimer timerLoad; timerLoad.Start(); - if (configStandalone.configTF.bunchSim) { - if (tf->LoadCreateTimeFrame(iEvent)) { - break; - } - } else if (configStandalone.configTF.nMerge) { - if (tf->LoadMergedEvents(iEvent)) { - break; - } - } else { - if (ReadEvent(iEvent)) { - break; - } - } - bool encodeZS = configStandalone.encodeZS == -1 ? (chainTracking->mIOPtrs.tpcPackedDigits && !chainTracking->mIOPtrs.tpcZS) : (bool)configStandalone.encodeZS; - bool zsFilter = configStandalone.zsFilter == -1 ? (!encodeZS && chainTracking->mIOPtrs.tpcPackedDigits) : (bool)configStandalone.zsFilter; - if (encodeZS || zsFilter) { - if (!chainTracking->mIOPtrs.tpcPackedDigits) { - printf("Need digit input to run ZS\n"); - goto breakrun; - } - if (zsFilter) { - chainTracking->ConvertZSFilter(configStandalone.zs12bit); - } - if (encodeZS) { - chainTracking->ConvertZSEncoder(configStandalone.zs12bit); - } - } - if (!configStandalone.configRec.runTransformation) { - chainTracking->mIOPtrs.clustersNative = nullptr; - } else { - for (int i = 0; i < chainTracking->NSLICES; i++) { - if (chainTracking->mIOPtrs.rawClusters[i]) { - if (configStandalone.DebugLevel >= 2) { - printf("Converting Legacy Raw Cluster to Native\n"); - } - chainTracking->ConvertRun2RawToNative(); - break; - } - } - } - - if (configStandalone.stripDumpedEvents) { - if (chainTracking->mIOPtrs.tpcZS) { - chainTracking->mIOPtrs.tpcPackedDigits = nullptr; - } + if (LoadEvent(iEvent, 0)) { + goto breakrun; } if (configStandalone.dumpEvents) { char fname[1024]; @@ -868,83 +824,84 @@ int main(int argc, char** argv) ev.continuousMaxTimeBin = chainTracking->mIOPtrs.tpcZS ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcZS) : chainTracking->mIOPtrs.tpcPackedDigits ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcPackedDigits) : GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.clustersNative); printf("Max time bin set to %d\n", (int)ev.continuousMaxTimeBin); rec->UpdateEventSettings(&ev); + if (recAsync) { + recAsync->UpdateEventSettings(&ev); + } + if (recPipeline) { + recPipeline->UpdateEventSettings(&ev); + } } } - if (!rec->GetParam().earlyTpcTransform && chainTracking->mIOPtrs.clustersNative == nullptr && chainTracking->mIOPtrs.tpcPackedDigits == nullptr && chainTracking->mIOPtrs.tpcZS == nullptr) { - printf("Need cluster native data for on-the-fly TPC transform\n"); + printf("Loading time: %'d us\n", (int)(1000000 * timerLoad.GetCurrentElapsedTime())); + } + printf("Processing Event %d\n", iEvent); + + nIteration.store(0); + nIterationEnd.store(0); + double pipelineWalltime = 1.; + if (configStandalone.proc.doublePipeline) { + HighResTimer timerPipeline; + if (RunBenchmark(rec, chainTracking, 1, iEvent, &nTracksTotal, &nClustersTotal) || RunBenchmark(recPipeline, chainTrackingPipeline, 2, iEvent, &nTracksTotal, &nClustersTotal)) { goto breakrun; } - - printf("Loading time: %'d us\n", (int)(1000000 * timerLoad.GetCurrentElapsedTime())); - - printf("Processing Event %d\n", iEvent); - GPUTrackingInOutPointers ioPtrSave = chainTracking->mIOPtrs; - - nIteration.store(0); - nIterationEnd.store(0); - double pipelineWalltime = 1.; - if (configStandalone.configProc.doublePipeline) { - HighResTimer timerPipeline; - if (RunBenchmark(rec, chainTracking, 1, ioPtrSave, &nTracksTotal, &nClustersTotal)) { - goto breakrun; - } - nIteration.store(1); - nIterationEnd.store(1); - auto pipeline1 = std::async(std::launch::async, RunBenchmark, rec, chainTracking, configStandalone.runs, ioPtrSave, &nTracksTotal, &nClustersTotal, 0, &timerPipeline); - auto pipeline2 = std::async(std::launch::async, RunBenchmark, recPipeline, chainTrackingPipeline, configStandalone.runs, ioPtrSave, &nTracksTotal, &nClustersTotal, 1, &timerPipeline); - if (pipeline1.get() || pipeline2.get()) { - goto breakrun; - } - pipelineWalltime = timerPipeline.GetElapsedTime() / (configStandalone.runs - 1); - printf("Pipeline wall time: %f, %d iterations, %f per event\n", timerPipeline.GetElapsedTime(), configStandalone.runs - 1, pipelineWalltime); - } else { - if (RunBenchmark(rec, chainTracking, configStandalone.runs, ioPtrSave, &nTracksTotal, &nClustersTotal)) { - goto breakrun; - } + auto pipeline1 = std::async(std::launch::async, RunBenchmark, rec, chainTracking, configStandalone.runs, iEvent, &nTracksTotal, &nClustersTotal, 0, &timerPipeline); + auto pipeline2 = std::async(std::launch::async, RunBenchmark, recPipeline, chainTrackingPipeline, configStandalone.runs, iEvent, &nTracksTotal, &nClustersTotal, 1, &timerPipeline); + if (pipeline1.get() || pipeline2.get()) { + goto breakrun; } - nEventsProcessed++; - - if (configStandalone.timeFrameTime) { - double nClusters = chainTracking->GetTPCMerger().NMaxClusters(); - if (nClusters > 0) { - double nClsPerTF = 550000. * 1138.3; - double timePerTF = (configStandalone.configProc.doublePipeline ? pipelineWalltime : ((configStandalone.DebugLevel ? rec->GetStatKernelTime() : rec->GetStatWallTime()) / 1000000.)) * nClsPerTF / nClusters; - double nGPUsReq = timePerTF / 0.02277; - char stat[1024]; - snprintf(stat, 1024, "Sync phase: %.2f sec per 256 orbit TF, %.1f GPUs required", timePerTF, nGPUsReq); - if (configStandalone.testSyncAsync) { - timePerTF = (configStandalone.DebugLevel ? recAsync->GetStatKernelTime() : recAsync->GetStatWallTime()) / 1000000. * nClsPerTF / nClusters; - snprintf(stat + strlen(stat), 1024 - strlen(stat), " - Async phase: %f sec per TF", timePerTF); - } - printf("%s (Measured %s time - Extrapolated from %d clusters to %d)\n", stat, configStandalone.DebugLevel ? "kernel" : "wall", (int)nClusters, (int)nClsPerTF); + pipelineWalltime = timerPipeline.GetElapsedTime() / (configStandalone.runs - 2); + printf("Pipeline wall time: %f, %d iterations, %f per event\n", timerPipeline.GetElapsedTime(), configStandalone.runs - 2, pipelineWalltime); + } else { + if (RunBenchmark(rec, chainTracking, configStandalone.runs, iEvent, &nTracksTotal, &nClustersTotal)) { + goto breakrun; + } + } + nEventsProcessed++; + + if (configStandalone.timeFrameTime) { + double nClusters = chainTracking->GetTPCMerger().NMaxClusters(); + if (nClusters > 0) { + double nClsPerTF = 550000. * 1138.3; + double timePerTF = (configStandalone.proc.doublePipeline ? pipelineWalltime : ((configStandalone.proc.debugLevel ? rec->GetStatKernelTime() : rec->GetStatWallTime()) / 1000000.)) * nClsPerTF / nClusters; + double nGPUsReq = timePerTF / 0.02277; + char stat[1024]; + snprintf(stat, 1024, "Sync phase: %.2f sec per 256 orbit TF, %.1f GPUs required", timePerTF, nGPUsReq); + if (configStandalone.testSyncAsync) { + timePerTF = (configStandalone.proc.debugLevel ? recAsync->GetStatKernelTime() : recAsync->GetStatWallTime()) / 1000000. * nClsPerTF / nClusters; + snprintf(stat + strlen(stat), 1024 - strlen(stat), " - Async phase: %f sec per TF", timePerTF); } + printf("%s (Measured %s time - Extrapolated from %d clusters to %d)\n", stat, configStandalone.proc.debugLevel ? "kernel" : "wall", (int)nClusters, (int)nClsPerTF); } } - if (nEventsProcessed > 1) { - printf("Total: %lld clusters, %lld tracks\n", nClustersTotal, nTracksTotal); + + if (configStandalone.preloadEvents && configStandalone.proc.doublePipeline) { + break; } } + if (nEventsProcessed > 1) { + printf("Total: %lld clusters, %lld tracks\n", nClustersTotal, nTracksTotal); + } } -breakrun: - if (rec->GetDeviceProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { +breakrun: + if (rec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_GLOBAL) { rec->PrintMemoryMax(); } #ifndef _WIN32 - if (configStandalone.qa && configStandalone.fpe) { + if (configStandalone.proc.runQA && configStandalone.fpe) { fedisableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); } #endif - if (configStandalone.configProc.doublePipeline) { + if (configStandalone.proc.doublePipeline) { rec->TerminatePipelineWorker(); pipelineThread->join(); } rec->Finalize(); if (configStandalone.outputcontrolmem && rec->IsGPU()) { - if (rec->unregisterMemoryForGPU(outputmemory.get()) || (configStandalone.configProc.doublePipeline && recPipeline->unregisterMemoryForGPU(outputmemoryPipeline.get()))) { + if (rec->unregisterMemoryForGPU(outputmemory.get()) || (configStandalone.proc.doublePipeline && recPipeline->unregisterMemoryForGPU(outputmemoryPipeline.get()))) { printf("Error unregistering memory\n"); } } diff --git a/GPU/GPUTracking/Standalone/tools/createGeo.C b/GPU/GPUTracking/Standalone/tools/createGeo.C index e084cd7cae9ad..b0837ff2604b4 100644 --- a/GPU/GPUTracking/Standalone/tools/createGeo.C +++ b/GPU/GPUTracking/Standalone/tools/createGeo.C @@ -1,8 +1,8 @@ #if !defined(__CLING__) || defined(__ROOTCLING__) #include <TSystem.h> -#include "TRDBase/TRDGeometryFlat.h" +#include "TRDBase/GeometryFlat.h" #include "DetectorsBase/GeometryManager.h" -#include "TRDBase/TRDGeometry.h" +#include "TRDBase/Geometry.h" #include "GPUO2Interface.h" #include "GPUReconstruction.h" #include "GPUChainTracking.h" @@ -12,11 +12,12 @@ using namespace GPUCA_NAMESPACE::gpu; void createGeo() { o2::base::GeometryManager::loadGeometry(); - auto gm = o2::trd::TRDGeometry::instance(); + auto gm = o2::trd::Geometry::instance(); gm->createPadPlaneArray(); gm->createClusterMatrixArray(); - o2::trd::TRDGeometryFlat gf(*gm); - gSystem->Load("libO2GPUTracking.so"); + o2::trd::GeometryFlat gf(*gm); + //if (!gf.readMatricesFromFile()) return; // uncomment this line when the matrices dumped from AliRoot should be used + gSystem->Load("libO2GPUTracking"); GPUReconstruction* rec = GPUReconstruction::CreateInstance(GPUReconstruction::DeviceType::CPU); GPUChainTracking* chain = rec->AddChain<GPUChainTracking>(); chain->SetTRDGeometry(&gf); diff --git a/GPU/GPUTracking/Standalone/tools/createLUT.C b/GPU/GPUTracking/Standalone/tools/createLUT.C index 53534273277c2..ae146c92e4608 100644 --- a/GPU/GPUTracking/Standalone/tools/createLUT.C +++ b/GPU/GPUTracking/Standalone/tools/createLUT.C @@ -10,7 +10,7 @@ using namespace GPUCA_NAMESPACE::gpu; void createLUT() { o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::loadFromFile("matbud.root", "MatBud"); - gSystem->Load("libO2GPUTracking.so"); + gSystem->Load("libO2GPUTracking"); GPUReconstruction* rec = GPUReconstruction::CreateInstance(GPUReconstruction::DeviceType::CPU); GPUChainTracking* chain = rec->AddChain<GPUChainTracking>(); chain->SetMatLUT(lut); diff --git a/GPU/GPUTracking/Standalone/tools/dumpTRDClusterMatrices.C b/GPU/GPUTracking/Standalone/tools/dumpTRDClusterMatrices.C new file mode 100644 index 0000000000000..51a2b4922ab6e --- /dev/null +++ b/GPU/GPUTracking/Standalone/tools/dumpTRDClusterMatrices.C @@ -0,0 +1,83 @@ +#include "AliCDBManager.h" +#include "AliGeomManager.h" +#include "AliTRDgeometry.h" + +#include "TGeoMatrix.h" + +#include <iostream> +#include <fstream> + +/* + This macro loads the TRD geometry taking into account the alignment from Run 2 + and dumps the inverse of all cluster matrices to a file. + To load the matrices add the following include and method to + GeometryFlat.h: + +#include <fstream> + + GPUd() bool readMatricesFromFile() + { + std::ifstream fIn("matrices.dat", std::ios::in | std::ios::binary); + if (!fIn) { + printf("Cannot read file\n"); + return false; + } + for (int iDet = 0; iDet < constants::NCHAMBER; ++iDet) { + float m[12]; + for (int j=0; j<12; ++j) { + fIn.read((char*) &m[j], sizeof(float)); + } + mMatrixCache[iDet] = o2::gpu::Transform3D(m); + } + return true; + } + + Don't forget to uncomment the line in createGeo.C which calls this method. + + Note that only 521 chambers are installed. + +*/ + +void dumpTRDClusterMatrices() +{ + auto man = AliCDBManager::Instance(); + if (!man->IsDefaultStorageSet()) { + man->SetDefaultStorage("local:///cvmfs/alice-ocdb.cern.ch/calibration/data/2015/OCDB"); + man->SetRun(244340); + } + AliGeomManager::LoadGeometry(); + AliGeomManager::ApplyAlignObjsFromCDB("ITS TPC TRD TOF"); + + auto geo = new AliTRDgeometry(); + + std::ofstream fOut("matrices.dat", std::ios::out | std::ios::binary); + if (!fOut) { + printf("Cannot open file\n"); + return; + } + + int nMatrices = 0; + + for (int i = 0; i < 540; ++i) { + // dump all available matrices to a file + auto matrix = geo->GetClusterMatrix(i); + if (!matrix) { + printf("No matrix for chamber %i\n", i); + continue; + } + ++nMatrices; + auto matrixO2 = matrix->Inverse(); + + auto tr = matrixO2.GetTranslation(); + auto rot = matrixO2.GetRotationMatrix(); + + float m[12] = {static_cast<float>(rot[0]), static_cast<float>(rot[1]), static_cast<float>(rot[2]), static_cast<float>(tr[0]), static_cast<float>(rot[3]), static_cast<float>(rot[4]), static_cast<float>(rot[5]), static_cast<float>(tr[1]), static_cast<float>(rot[6]), static_cast<float>(rot[7]), static_cast<float>(rot[8]), static_cast<float>(tr[2])}; + + for (int j = 0; j < 12; ++j) { + fOut.write((char*)&m[j], sizeof(float)); + } + } + + fOut.close(); + printf("Dumped %i matrices\n", nMatrices); +} diff --git a/GPU/GPUTracking/Standalone/tools/rtc/rtcsource.sh b/GPU/GPUTracking/Standalone/tools/rtc/rtcsource.sh new file mode 100755 index 0000000000000..6eeb456d272c1 --- /dev/null +++ b/GPU/GPUTracking/Standalone/tools/rtc/rtcsource.sh @@ -0,0 +1,15 @@ +#!/bin/bash +cat <<EOT > source.cu +# 1 "/home/qon/alice/O2/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu" +# 1 "/home/qon/alice/O2/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtcPre.h" 1 +EOT +cat src/Base/cuda/GPUReconstructionCUDArtcPre2.h >> source.cu +nvcc -std=c++17 -gencode arch=compute_75,code=sm_75 -E \ + -I src/ -I src/Common/ -I src/Base/ -I src/SliceTracker/ -I src/Merger/ -I src/TRDTracking/ -I src/TPCClusterFinder/ -I src/TPCConvert/ -I src/Global/ -I src/dEdx/ -I src/TPCFastTransformation/ -I src/GPUUtils/ -I src/DataCompression -I src/ITS \ + -I$HOME/alice/O2/DataFormats/Detectors/TPC/include -I$HOME/alice/O2/Detectors/Base/include -I$HOME/alice/O2/Detectors/Base/src -I$HOME/alice/O2/Common/MathUtils/include -I$HOME/alice/O2/DataFormats/Headers/include \ + -I$HOME/alice/O2/Detectors/TRD/base/include -I$HOME/alice/O2/Detectors/TRD/base/src -I$HOME/alice/O2/Detectors/ITSMFT/ITS/tracking/include -I$HOME/alice/O2/Detectors/ITSMFT/ITS/tracking/cuda/include -I$HOME/alice/O2/Common/Constants/include \ + -I$HOME/alice/O2/DataFormats/common/include -I$HOME/alice/O2/DataFormats/Detectors/TRD/include -I$HOME/alice/O2/Detectors/Raw/include \ + -DHAVE_O2HEADERS -DGPUCA_TPC_GEOMETRY_O2 -DGPUCA_STANDALONE -DGPUCA_NO_ITS_TRAITS -DGPUCA_GPUCODE_GENRTC \ + ~/alice/O2/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu \ +| sed '1,/^# 1 ".*GPUReconstructionCUDArtcPre.h" 1$/d' \ +>> source.cu diff --git a/GPU/GPUTracking/Standalone/tools/rtc/test.cu b/GPU/GPUTracking/Standalone/tools/rtc/test.cu new file mode 100644 index 0000000000000..c99aab15f342b --- /dev/null +++ b/GPU/GPUTracking/Standalone/tools/rtc/test.cu @@ -0,0 +1,86 @@ +#include <cuda.h> +#include <nvrtc.h> +#include <iostream> + +#define NVRTC_SAFE_CALL(x) \ + do { \ + nvrtcResult result = x; \ + if (result != NVRTC_SUCCESS) { \ + std::cerr << "\nerror: " #x " failed with error " \ + << nvrtcGetErrorString(result) << '\n'; \ + exit(1); \ + } \ + } while (0) + +#define CUDA_SAFE_CALL(x) \ + do { \ + CUresult result = x; \ + if (result != CUDA_SUCCESS) { \ + const char* msg; \ + cuGetErrorName(result, &msg); \ + std::cerr << "\nerror: " #x " failed with error " \ + << msg << '\n'; \ + exit(1); \ + } \ + } while (0) + +int main(int argc, char** argv) +{ + //Read Sourcecode from file + unsigned int filesize; + FILE* pFile; + //Open file + if ((pFile = fopen("source.cu", "rb")) == NULL) + exit(1); + //Optain File Size + fseek(pFile, 0, SEEK_END); + filesize = ftell(pFile); + rewind(pFile); + //Read file + char* sourceCode = new char[filesize + 1]; + if (fread(sourceCode, 1, filesize, pFile) != filesize) + exit(1); + //Make sourceCode 0-terminated + sourceCode[filesize] = 0; + fclose(pFile); + + nvrtcProgram prog; + NVRTC_SAFE_CALL(nvrtcCreateProgram(&prog, // prog + sourceCode, // buffer + "saxpy.cu", // name + 0, // numHeaders + NULL, // headers + NULL)); // includeNames + delete[] sourceCode; + //const char *opts[] = {"-default-device -std=c++17 --extended-lambda -Xptxas -O4 -Xcompiler -O4 -use_fast_math --ftz=true"}; + const char* opts[] = {"-default-device", "--std=c++17", "-use_fast_math", "-ftz=true"}; + nvrtcResult compileResult = nvrtcCompileProgram(prog, // prog + sizeof(opts) / sizeof(opts[0]), // numOptions + opts); // options + size_t logSize; + NVRTC_SAFE_CALL(nvrtcGetProgramLogSize(prog, &logSize)); + char* log = new char[logSize]; + NVRTC_SAFE_CALL(nvrtcGetProgramLog(prog, log)); + std::cout << log << '\n'; + delete[] log; + if (compileResult != NVRTC_SUCCESS) { + exit(1); + } + size_t ptxSize; + NVRTC_SAFE_CALL(nvrtcGetPTXSize(prog, &ptxSize)); + char* ptx = new char[ptxSize]; + NVRTC_SAFE_CALL(nvrtcGetPTX(prog, ptx)); + NVRTC_SAFE_CALL(nvrtcDestroyProgram(&prog)); + CUmodule module; + CUfunction kernel; + CUDA_SAFE_CALL(cuModuleLoadDataEx(&module, ptx, 0, 0, 0)); + CUDA_SAFE_CALL(cuModuleGetFunction(&kernel, module, "foo")); + void* args[] = {}; + CUDA_SAFE_CALL( + cuLaunchKernel(kernel, + 1, 1, 1, // grid dim + 32, 1, 1, // block dim + 0, NULL, // shared mem and stream + args, 0)); // arguments + return 0; +} diff --git a/GPU/GPUTracking/Standalone/tools/testCL.sh b/GPU/GPUTracking/Standalone/tools/testCL.sh index d963dbf108e52..a980531bbe8d1 100755 --- a/GPU/GPUTracking/Standalone/tools/testCL.sh +++ b/GPU/GPUTracking/Standalone/tools/testCL.sh @@ -9,9 +9,10 @@ LLVM_SPIRV=llvm-spirv #COMPILER=/home/qon/clang-build/install/bin/clang++ #LLVM_SPIRV=/home/qon/clang-build/SPIRV-LLVM-Translator/build/tools/llvm-spirv/llvm-spirv -INCLUDES="-I../. -I../Base -I../SliceTracker -I../Common -I../Merger -I../TRDTracking -I../ITS -I../dEdx -I../TPCConvert -I../TPCFastTransformation -I../DataCompression -I../TPCClusterFinder \ +INCLUDES="-I../. -I../Base -I../SliceTracker -I../Common -I../Merger -I../TRDTracking -I../ITS -I../dEdx -I../TPCConvert -I../TPCFastTransformation -I../DataCompression -I../TPCClusterFinder -I../Global -I ../GPUUtils \ -I$HOME/alice/O2/DataFormats/Detectors/TPC/include -I$HOME/alice/O2/Detectors/Base/include -I$HOME/alice/O2/Detectors/Base/src -I$HOME/alice/O2/Common/MathUtils/include -I$HOME/alice/O2/DataFormats/Headers/include \ - -I$HOME/alice/O2/Detectors/TRD/base/include -I$HOME/alice/O2/Detectors/TRD/base/src -I$HOME/alice/O2/Detectors/ITSMFT/ITS/tracking/include -I$HOME/alice/O2/Detectors/ITSMFT/ITS/tracking/cuda/include -I$HOME/alice/O2/Common/Constants/include" + -I$HOME/alice/O2/Detectors/TRD/base/include -I$HOME/alice/O2/Detectors/TRD/base/src -I$HOME/alice/O2/Detectors/ITSMFT/ITS/tracking/include -I$HOME/alice/O2/Detectors/ITSMFT/ITS/tracking/cuda/include -I$HOME/alice/O2/Common/Constants/include \ + -I$HOME/alice/O2/DataFormats/common/include -I$HOME/alice/O2/DataFormats/Detectors/TRD/include" DEFINES="-DGPUCA_STANDALONE -DGPUCA_GPULIBRARY=OCL -DNDEBUG -D__OPENCLCPP__ -DHAVE_O2HEADERS -DGPUCA_TPC_GEOMETRY_O2" FLAGS="-O3 -cl-denorms-are-zero -cl-mad-enable -cl-no-signed-zeros -ferror-limit=1000 -Xclang -finclude-default-header -Dcl_clang_storage_class_specifiers" diff --git a/GPU/GPUTracking/Standalone/utils/bitfield.h b/GPU/GPUTracking/Standalone/utils/bitfield.h index 981804b4bb2af..bf102cee5e343 100644 --- a/GPU/GPUTracking/Standalone/utils/bitfield.h +++ b/GPU/GPUTracking/Standalone/utils/bitfield.h @@ -14,7 +14,7 @@ #ifndef Q_BITFIELD_H #define Q_BITFIELD_H -#ifdef GPUCA_NOCOMPAT_ALLOPENCL +#if !defined(GPUCA_NOCOMPAT_ALLOPENCL) && !defined(GPUCA_GPUCODE_GENRTC) #include <type_traits> #endif @@ -91,7 +91,7 @@ class bitfield return retVal; } -#ifdef GPUCA_NOCOMPAT_ALLOPENCL +#if defined(GPUCA_NOCOMPAT_ALLOPENCL) && !defined(GPUCA_GPUCODE_DEVICE) static_assert(std::is_integral<S>::value, "Storage type non integral"); static_assert(sizeof(S) >= sizeof(T), "Storage type has insufficient capacity"); #endif diff --git a/GPU/GPUTracking/Standalone/utils/qconfig.cxx b/GPU/GPUTracking/Standalone/utils/qconfig.cxx index 32552fc61a569..7489f5c416b62 100644 --- a/GPU/GPUTracking/Standalone/utils/qconfig.cxx +++ b/GPU/GPUTracking/Standalone/utils/qconfig.cxx @@ -19,7 +19,9 @@ #include <functional> #include <iostream> #include <tuple> +#include <vector> #include "qconfig.h" +#include "qconfig_helpers.h" // Create config instances #define QCONFIG_INSTANCE @@ -71,9 +73,9 @@ static inline const char* getOptName(const char** argv, int i) template <typename T> struct qConfigSettings { - qConfigSettings() : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(0), max(0), set(0), message(nullptr), allowEmpty(false){}; + qConfigSettings() : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(), max(), set(), message(nullptr), allowEmpty(false){}; template <typename S> - qConfigSettings(const qConfigSettings<S> v) : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(0), max(0), set(0), message(v.message), allowEmpty(v.allowEmpty){}; + qConfigSettings(const qConfigSettings<S> v) : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(), max(), set(), message(v.message), allowEmpty(v.allowEmpty){}; bool checkMin, checkMax; bool doSet, doDefault; T min, max; @@ -258,7 +260,13 @@ struct qConfigType { argBuffer[argBufferPos++] = preoptshort == 0 ? '-' : preoptshort; argBuffer[argBufferPos++] = optnameshort; } - std::cout << "\t" << name << ": " << argBuffer << (optnameshort == 0 ? "" : arguments) << "--" << preopt << optname << (optnameshort == 0 ? arguments : ", ") << "type: " << type; + std::cout << "\t" << name << ": " << argBuffer << (optnameshort == 0 ? "" : arguments) << "--"; + if (optname[0] == 0) { + std::cout << preopt << name; + } else { + std::cout << optname; + } + std::cout << (optnameshort == 0 ? arguments : ", ") << "type: " << type; if (optionType == 0) { std::cout << ", default: " << def; } @@ -339,6 +347,15 @@ inline int qAddOptionType<char>(qConfigSettings<char>& settings, char& ref, int& settings.doDefault); } template <> +inline int qAddOptionType<unsigned char>(qConfigSettings<unsigned char>& settings, unsigned char& ref, int& i, const char** argv, const int argc, unsigned char /*def*/) +{ + return qAddOptionGeneric<unsigned char>( + settings, ref, i, argv, argc, settings.set, [](const char* a) -> unsigned char { + return atoi(a); + }, + settings.doDefault); +} +template <> inline int qAddOptionType<int>(qConfigSettings<int>& settings, int& ref, int& i, const char** argv, const int argc, int /*def*/) { return qAddOptionGeneric<int>( @@ -357,19 +374,37 @@ inline int qAddOptionType<unsigned int>(qConfigSettings<unsigned int>& settings, settings.doDefault); } template <> -inline int qAddOptionType<long long int>(qConfigSettings<long long int>& settings, long long int& ref, int& i, const char** argv, const int argc, long long int /*def*/) +inline int qAddOptionType<short>(qConfigSettings<short>& settings, short& ref, int& i, const char** argv, const int argc, short /*def*/) { - return qAddOptionGeneric<long long int>( - settings, ref, i, argv, argc, settings.set, [](const char* a) -> long long int { + return qAddOptionGeneric<short>( + settings, ref, i, argv, argc, settings.set, [](const char* a) -> short { + return atoi(a); + }, + settings.doDefault); +} +template <> +inline int qAddOptionType<unsigned short>(qConfigSettings<unsigned short>& settings, unsigned short& ref, int& i, const char** argv, const int argc, unsigned short /*def*/) +{ + return qAddOptionGeneric<unsigned short>( + settings, ref, i, argv, argc, settings.set, [](const char* a) -> unsigned short { + return strtoul(a, nullptr, 0); + }, + settings.doDefault); +} +template <> +inline int qAddOptionType<long>(qConfigSettings<long>& settings, long& ref, int& i, const char** argv, const int argc, long /*def*/) +{ + return qAddOptionGeneric<long>( + settings, ref, i, argv, argc, settings.set, [](const char* a) -> long { return strtoll(a, nullptr, 0); }, settings.doDefault); } template <> -inline int qAddOptionType<unsigned long long int>(qConfigSettings<unsigned long long int>& settings, unsigned long long int& ref, int& i, const char** argv, const int argc, unsigned long long int /*def*/) +inline int qAddOptionType<unsigned long>(qConfigSettings<unsigned long>& settings, unsigned long& ref, int& i, const char** argv, const int argc, unsigned long /*def*/) { - return qAddOptionGeneric<unsigned long long int>( - settings, ref, i, argv, argc, settings.set, [](const char* a) -> unsigned long long int { + return qAddOptionGeneric<unsigned long>( + settings, ref, i, argv, argc, settings.set, [](const char* a) -> unsigned long { return strtoull(a, nullptr, 0); }, settings.doDefault); @@ -401,6 +436,15 @@ inline int qAddOptionType<const char*>(qConfigSettings<const char*>& settings, c }, settings.doDefault); } +template <> +inline int qAddOptionType<std::string>(qConfigSettings<std::string>& settings, std::string& ref, int& i, const char** argv, const int argc, std::string /*def*/) +{ + return qAddOptionGeneric<std::string>( + settings, ref, i, argv, argc, settings.set, [](const char* a) -> std::string { + return a; + }, + settings.doDefault); +} // Checks and messages for additional settings template <typename T> @@ -426,16 +470,9 @@ template <typename T> inline void qAddOptionMessage(qConfigSettings<T>& settings, T& ref) { if (settings.message) { - printf(settings.message, ref); - printf("\n"); - } -} -template <> -inline void qAddOptionMessage<bool>(qConfigSettings<bool>& settings, bool& ref) -{ - if (settings.message) { - printf(settings.message, ref ? "ON" : "OFF"); - printf("\n"); + std::string tmp = print_type(ref); + std::string msg = std::string(settings.message) + "\n"; + printf(msg.c_str(), tmp.c_str()); } } @@ -473,9 +510,9 @@ static inline int qConfigParse(int argc, const char** argv, const char* /*filena // Main parse function called from outside int qConfigParse(int argc, const char** argv, const char* filename) { return (qConfig::qConfigParse(argc, argv, filename)); } -// Print current config settings void qConfigPrint() { + std::string blockName; #define QCONFIG_PRINT #include "qconfig.h" #undef QCONFIG_PRINT diff --git a/GPU/GPUTracking/Standalone/utils/qconfig.h b/GPU/GPUTracking/Standalone/utils/qconfig.h index e53bc50226fbd..80cad5f540275 100644 --- a/GPU/GPUTracking/Standalone/utils/qconfig.h +++ b/GPU/GPUTracking/Standalone/utils/qconfig.h @@ -11,7 +11,25 @@ /// \file qconfig.h /// \author David Rohr -#include <vector> +#ifndef QCONFIG_H_GENERAL +#define QCONFIG_H_GENERAL +extern int qConfigParse(int argc, const char** argv, const char* filename = nullptr); +extern void qConfigPrint(); +namespace qConfig +{ +enum qConfigRetVal { qcrOK = 0, + qcrError = 1, + qcrMinFailure = 2, + qcrMaxFailure = 3, + qcrHelp = 4, + qcrCmd = 5, + qcrArgMissing = 6, + qcrArgIncomplete = 7, + qcrArrayOverflow = 8 }; +} +#endif + +#if !defined(QCONFIG_HEADER_GUARD_NO_INCLUDE) || defined(QCONFIG_GENRTC) #define AddArrayDefaults(...) \ { \ @@ -20,12 +38,13 @@ #ifdef QCONFIG_PARSE -#define QCONFIG_COMPARE(optname, optnameshort) \ - (thisoption[0] == '-' && ((preoptshort == 0 && thisoption[1] == optnameshort && thisoption[2] == 0) || (thisoption[1] == '-' && strlen(preopt) == 0 && strcmp(&thisoption[2], optname) == 0) || \ - (preoptshort != 0 && thisoption[1] == preoptshort && thisoption[2] == optnameshort && thisoption[3] == 0) || (thisoption[1] == '-' && strlen(preopt) && strncmp(&thisoption[2], preopt, strlen(preopt)) == 0 && strcmp(&thisoption[2 + strlen(preopt)], optname) == 0))) +#define QCONFIG_COMPARE(name, optname, optnameshort) \ + (thisoption[0] == '-' && ((thisoption[1] == '-' && optname[0] != 0 && strcmp(&thisoption[2], optname) == 0) || \ + (preoptshort == 0 && thisoption[1] == optnameshort && thisoption[2] == 0) || (thisoption[1] == '-' && strlen(preopt) == 0 && strcmp(&thisoption[2], name) == 0) || \ + (preoptshort != 0 && thisoption[1] == preoptshort && thisoption[2] == optnameshort && thisoption[3] == 0) || (thisoption[1] == '-' && strlen(preopt) && strncmp(&thisoption[2], preopt, strlen(preopt)) == 0 && strcmp(&thisoption[2 + strlen(preopt)], name) == 0))) #define AddOption(name, type, default, optname, optnameshort, ...) \ - else if (QCONFIG_COMPARE(optname, optnameshort)) \ + else if (QCONFIG_COMPARE(#name, optname, optnameshort)) \ { \ int retVal = qConfigType<type>::qAddOption(tmp.name, i, argv, argc, default, __VA_ARGS__); \ if (retVal) { \ @@ -34,7 +53,7 @@ } #define AddOptionSet(name, type, value, optname, optnameshort, ...) \ - else if (QCONFIG_COMPARE(optname, optnameshort)) \ + else if (QCONFIG_COMPARE(optname, "", optnameshort)) \ { \ int retVal = qConfigType<type>::qAddOption(tmp.name, i, nullptr, 0, value, __VA_ARGS__, set(value)); \ if (retVal) { \ @@ -43,7 +62,7 @@ } #define AddOptionVec(name, type, optname, optnameshort, ...) \ - else if (QCONFIG_COMPARE(optname, optnameshort)) \ + else if (QCONFIG_COMPARE(#name, optname, optnameshort)) \ { \ int retVal = qConfigType<type>::qAddOptionVec(tmp.name, i, argv, argc, __VA_ARGS__); \ if (retVal) { \ @@ -52,7 +71,7 @@ } #define AddOptionArray(name, type, count, default, optname, optnameshort, ...) \ - else if (QCONFIG_COMPARE(optname, optnameshort)) \ + else if (QCONFIG_COMPARE(#name, optname, optnameshort)) \ { \ int retVal = qConfigType<type>::qAddOptionArray(tmp.name, count, i, argv, argc, __VA_ARGS__); \ if (retVal) { \ @@ -69,17 +88,24 @@ name& tmp = instance; \ bool tmpfound = true; \ if (found) { \ - ; \ } #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ { \ constexpr const char* preopt = preoptname; \ + (void)preopt; \ constexpr const char preoptshort = preoptnameshort; \ + (void)preoptshort; \ name& tmp = parent.instance; \ + (void)tmp; \ bool tmpfound = true; \ if (found) { \ - ; \ + } + +#define BeginHiddenConfig(name, instance) \ + { \ + bool tmpfound; \ + if (0) { \ } #define EndConfig() \ @@ -89,24 +115,24 @@ } \ } -#define AddHelp(cmd, cmdshort) \ - else if (QCONFIG_COMPARE(cmd, cmdshort)) \ - { \ - const char* arg = getArg(i, argv, argc); \ - qConfigHelp(arg ? arg : preopt); \ - return (qcrHelp); \ +#define AddHelp(cmd, cmdshort) \ + else if (QCONFIG_COMPARE(cmd, "", cmdshort)) \ + { \ + const char* arg = getArg(i, argv, argc); \ + qConfigHelp(arg ? arg : preopt); \ + return (qcrHelp); \ } -#define AddHelpAll(cmd, cmdshort) \ - else if (QCONFIG_COMPARE(cmd, cmdshort)) \ - { \ - const char* arg = getArg(i, argv, argc); \ - qConfigHelp(arg ? arg : "", true); \ - return (qcrHelp); \ +#define AddHelpAll(cmd, cmdshort) \ + else if (QCONFIG_COMPARE(cmd, "", cmdshort)) \ + { \ + const char* arg = getArg(i, argv, argc); \ + qConfigHelp(arg ? arg : "", true); \ + return (qcrHelp); \ } #define AddCommand(cmd, cmdshort, command, help) \ - else if (QCONFIG_COMPARE(cmd, cmdshort)) \ + else if (QCONFIG_COMPARE(cmd, "", cmdshort)) \ { \ if (command) { \ return (qcrCmd); \ @@ -114,7 +140,7 @@ } #define AddShortcut(cmd, cmdshort, forward, help, ...) \ - else if (QCONFIG_COMPARE(cmd, cmdshort)) \ + else if (QCONFIG_COMPARE(cmd, "", cmdshort)) \ { \ const char* options[] = {"", __VA_ARGS__, nullptr}; \ const int nOptions = sizeof(options) / sizeof(options[0]) - 1; \ @@ -123,6 +149,7 @@ goto repeat; \ } +// End QCONFIG_PARSE #elif defined(QCONFIG_HELP) #define AddOption(name, type, default, optname, optnameshort, ...) qConfigType<type>::qConfigHelpOption(qon_mxstr(name), qon_mxstr(type), qon_mxstr(default), optname, optnameshort, preopt, preoptshort, 0, __VA_ARGS__); #define AddOptionSet(name, type, value, optname, optnameshort, ...) qConfigType<type>::qConfigHelpOption(qon_mxstr(name), qon_mxstr(type), qon_mxstr(value), optname, optnameshort, preopt, preoptshort, 1, __VA_ARGS__); @@ -140,11 +167,15 @@ printf("\n"); #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ const char* qon_mxcat(qConfig_subconfig_, name) = preoptnameshort == 0 ? (qon_mxstr(name) ": --" preoptname "\n\t\t" descr) : (qon_mxstr(name) ": -" qon_mxstr('a') " (--" preoptname ")\n\t\t" descr); \ + (void)qon_mxcat(qConfig_subconfig_, name); \ if (subConfig == nullptr || strcmp(subConfig, followSub == 2 ? qon_mxstr(name) : preoptname) == 0) { \ constexpr const char* preopt = preoptname; \ + (void)preopt; \ constexpr const char preoptshort = preoptnameshort; \ + (void)preoptshort; \ char argBuffer[2] = {preoptnameshort, 0}; \ printf("\n %s: (--%s%s%s)\n", descr, preoptname, preoptnameshort == 0 ? "" : " or -", argBuffer); +#define BeginHiddenConfig(name, instance) { #define EndConfig() } #define AddHelp(cmd, cmdshort) qConfigType<void*>::qConfigHelpOption("help", "help", nullptr, cmd, cmdshort, preopt, preoptshort, 3, "Show usage information"); #define AddHelpAll(cmd, cmdshort) qConfigType<void*>::qConfigHelpOption("help all", "help all", nullptr, cmd, cmdshort, preopt, preoptshort, 3, "Show usage info including all subparameters"); @@ -152,137 +183,151 @@ #define AddShortcut(cmd, cmdshort, forward, help, ...) qConfigType<void*>::qConfigHelpOption("shortcut", "shortcut", nullptr, cmd, cmdshort, preopt, preoptshort, 4, help); #define AddHelpText(text) printf("\n " text ":\n"); +// End QCONFIG_HELP #elif defined(QCONFIG_PRINT) -#define AddOption(name, type, default, optname, optnameshort, ...) std::cout << "\t" << qon_mxstr(name) << ": " << tmp.name << "\n"; -#define AddVariable(name, type, default) std::cout << "\t" << qon_mxstr(name) << ": " << tmp.name << "\n"; +#define AddOption(name, type, default, optname, optnameshort, ...) std::cout << "\t" << blockName << qon_mxstr(name) << ": " << qConfig::print_type(tmp.name) << "\n"; +#define AddVariable(name, type, default) std::cout << "\t" << blockName << qon_mxstr(name) << ": " << qConfig::print_type(tmp.name) << "\n"; #define AddOptionSet(name, type, value, optname, optnameshort, ...) -#define AddOptionVec(name, type, optname, optnameshort, ...) \ - { \ - std::cout << "\t" << qon_mxstr(name) << "[]: "; \ - for (unsigned int i = 0; i < tmp.name.size(); i++) { \ - if (i) { \ - std::cout << ", "; \ - } /*std::cout << tmp.name[i];*/ \ - } \ - std::cout << "\n"; \ +#define AddOptionVec(name, type, optname, optnameshort, ...) \ + { \ + std::cout << "\t" << blockName << qon_mxstr(name) << "[]: "; \ + for (unsigned int i = 0; i < tmp.name.size(); i++) { \ + if (i) { \ + std::cout << ", "; \ + } \ + std::cout << qConfig::print_type(tmp.name[i]); \ + } \ + std::cout << "\n"; \ } -#define AddOptionArray(name, type, count, default, optname, optnameshort, ...) \ - { \ - std::cout << "\t" << qon_mxstr(name) << "[" << count << "]: " << tmp.name[0]; \ - for (int i = 1; i < count; i++) { \ - std::cout << ", " << tmp.name[i]; \ - } \ - std::cout << "\n"; \ +#define AddOptionArray(name, type, count, default, optname, optnameshort, ...) \ + { \ + std::cout << "\t" << blockName << qon_mxstr(name) << "[" << count << "]: " << qConfig::print_type(tmp.name[0]); \ + for (int i = 1; i < count; i++) { \ + std::cout << ", " << qConfig::print_type(tmp.name[i]); \ + } \ + std::cout << "\n"; \ } #define AddSubConfig(name, instance) #define AddHelpText(text) printf(" " text ":\n"); #define BeginConfig(name, instance) \ { \ - name& tmp = instance; + name& tmp = instance; \ + blockName = ""; #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ { \ - name& tmp = parent.instance; + name& tmp = parent.instance; \ + blockName = std::string("\t") + qon_mxstr(name) + "."; \ + std::cout << "\t" << qon_mxstr(name) << "\n"; +#define BeginHiddenConfig(name, instance) \ + if (0) { \ + name tmp; #define EndConfig() } +// End QCONFIG_PRINT #elif defined(QCONFIG_INSTANCE) -#define AddOption(name, type, default, optname, optnameshort, help, ...) name(default), -#define AddVariable(name, type, default) name(default), -#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) -#define AddOptionVec(name, type, optname, optnameshort, help, ...) name(), -#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) name default, -#define AddSubConfig(name, instance) instance(), -#define BeginConfig(name, instance) \ - name instance; \ - name::name() : -#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) name::name() : -#define EndConfig() \ - _qConfigDummy() {} - -#elif defined(QCONFIG_EXTERNS) +#define BeginNamespace(name) \ + namespace name \ + { +#define EndNamespace() } #define AddOption(name, type, default, optname, optnameshort, help, ...) +#define AddVariable(name, type, default) #define AddOptionSet(name, type, value, optname, optnameshort, help, ...) #define AddOptionVec(name, type, optname, optnameshort, help, ...) #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) #define AddSubConfig(name, instance) -#define BeginConfig(name, instance) extern "C" name instance; +#define BeginConfig(name, instance) name instance; #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) #define EndConfig() -#undef QCONFIG_EXTERNS -extern int qConfigParse(int argc, const char** argv, const char* filename = nullptr); -extern void qConfigPrint(); -namespace qConfig -{ -enum qConfigRetVal { qcrOK = 0, - qcrError = 1, - qcrMinFailure = 2, - qcrMaxFailure = 3, - qcrHelp = 4, - qcrCmd = 5, - qcrArgMissing = 6, - qcrArgIncomplete = 7, - qcrArrayOverflow = 8 }; -} -#else -#ifdef QCONFIG_HEADER_GUARD +// End QCONFIG_INSTANCE +#elif defined(QCONFIG_PRINT_RTC) +#define AddOption(name, type, default, optname, optnameshort, help, ...) out << qon_mxstr(type) << " " << qon_mxstr(name) << ";\n"; +#define AddVariable(name, type, default) out << qon_mxstr(type) << " " << qon_mxstr(name) << ";\n"; +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) out << qon_mxstr(type) << " " << qon_mxstr(name) << "[" << qon_mxstr(count) << "];\n"; +#define AddOptionVec(name, type, optname, optnameshort, help, ...) out << "std::vector<" << qon_mxstr(type) << "> " << qon_mxstr(name) << ";\n"; +#define AddVariableRTC(name, type, default) \ + if (useConstexpr) { \ + out << "static constexpr " << qon_mxstr(type) << " " << qon_mxstr(name) << " = " << qConfig::print_type(std::get<const qConfigCurrentType*>(tSrc)->name) << ";\n"; \ + } else { \ + AddOption(name, type, default, optname, optnameshort, help); \ + } +#define AddOptionRTC(name, type, default, optname, optnameshort, help, ...) AddVariableRTC(name, type, default) +#define BeginConfig(name, instance) \ + { \ + using qConfigCurrentType = name; \ + out << "struct " << qon_mxstr(name) << " {\n"; +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) BeginConfig(name, instance) +#define EndConfig() \ + out << "};"; \ + } +#define BeginNamespace(name) out << "namespace " << qon_mxstr(name) << " {\n"; +#define EndNamespace() out << "}\n"; +#define AddSubConfig(name, instance) +#define AddOptionSet(...) + +// End QCONFIG_PRINT_RTC +#elif defined(QCONFIG_CONVERT_RTC) +#define AddOption(name, type, default, optname, optnameshort, help, ...) out.name = in.name; +#define AddVariable(name, type, default) out.name = in.name; +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) \ + for (unsigned int i = 0; i < count; i++) \ + out.name[i] = in.name[i]; +#define AddOptionVec(name, type, optname, optnameshort, help, ...) \ + for (unsigned int i = 0; i < in.name.size(); i++) \ + out.name[i] = in.name[i]; +#define AddOptionRTC(name, type, default, optname, optnameshort, help, ...) +#define AddVariableRTC(name, type, default) +#define BeginConfig(name, instance) \ + template <class T> \ + void qConfigConvertRtc(T& out, const name& in) \ + { +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) BeginConfig(name, instance) +#define EndConfig() } +#define AddSubConfig(name, instance) +#define AddOptionSet(...) + +// End QCONFIG_CONVERT_RTC +#else // Define structures +#if defined(QCONFIG_HEADER_GUARD) && !defined(QCONFIG_GENRTC) #define QCONFIG_HEADER_GUARD_NO_INCLUDE #else #define QCONFIG_HEADER_GUARD -#if defined(QCONFIG_CPP11_INIT) && !defined(QCONFIG_GPU) +#ifndef BeginNamespace +#define BeginNamespace(name) \ + namespace name \ + { +#define EndNamespace() } +#endif +#if !defined(QCONFIG_GPU) #define AddOption(name, type, default, optname, optnameshort, help, ...) type name = default; #define AddVariable(name, type, default) type name = default; -#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) -#define AddSubConfig(name, instance) name instance; #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {default}; #define AddOptionVec(name, type, optname, optnameshort, help, ...) std::vector<type> name; -#define BeginConfig(name, instance) \ - struct name { -#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ - struct name { -#define EndConfig() \ - } \ - ; #else #define AddOption(name, type, default, optname, optnameshort, help, ...) type name; #define AddVariable(name, type, default) type name; -#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) -#define AddSubConfig(name, instance) name instance; #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count]; -#define EndConfig() \ - qConfigDummy _qConfigDummy; \ - } \ - ; -#ifdef QCONFIG_GPU #define AddOptionVec(name, type, optname, optnameshort, help, ...) void* name[sizeof(std::vector<type>) / sizeof(void*)]; -#define BeginConfig(name, instance) \ - struct name { -#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ - struct name { -struct qConfigDummy { -}; -#else -#define AddOptionVec(name, type, optname, optnameshort, help, ...) std::vector<type> name; -#define BeginConfig(name, instance) \ - struct name { \ - name(); \ - name(const name& s); \ - name& operator=(const name& s); -#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) \ - struct name { \ - name(); \ - name(const name& s); \ - name& operator=(const name& s); -; -struct qConfigDummy { - qConfigDummy() {} -}; -#define QCONFIG_EXTERNS #endif +#ifdef QCONFIG_GENRTC +#define AddOptionRTC(name, type, default, optname, optnameshort, help, ...) static constexpr type name = default; +#define AddVariableRTC(name, type, default) static constexpr type name = default; +#else +#define AddCustomCPP(...) __VA_ARGS__ #endif +#define AddOptionSet(name, type, value, optname, optnameshort, help, ...) +#define AddSubConfig(name, instance) name instance; +#define BeginConfig(name, instance) struct name { +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr) struct name { +#define EndConfig() \ + } \ + ; + #endif #endif +#ifndef QCONFIG_HEADER_GUARD_NO_INCLUDE #ifndef AddHelp #define AddHelp(cmd, cmdshort) #endif @@ -301,13 +346,31 @@ struct qConfigDummy { #ifndef AddHelpText #define AddHelpText(text) #endif +#ifndef BeginNamespace +#define BeginNamespace(name) +#endif +#ifndef EndNamespace +#define EndNamespace() +#endif +#ifndef AddCustomCPP +#define AddCustomCPP(...) +#endif +#ifndef AddOptionRTC +#define AddOptionRTC(...) AddOption(__VA_ARGS__) +#endif +#ifndef AddVariableRTC +#define AddVariableRTC(...) AddVariable(__VA_ARGS__) +#endif +#ifndef BeginHiddenConfig +#define BeginHiddenConfig(name, instance) BeginSubConfig(name, instance, , , , ) +#endif -#ifndef QCONFIG_HEADER_GUARD_NO_INCLUDE #include "qconfigoptions.h" -#endif #undef AddOption +#undef AddOptionRTC #undef AddVariable +#undef AddVariableRTC #undef AddOptionSet #undef AddOptionVec #undef AddOptionArray @@ -315,16 +378,22 @@ struct qConfigDummy { #undef AddSubConfig #undef BeginConfig #undef BeginSubConfig +#undef BeginHiddenConfig #undef EndConfig #undef AddHelp #undef AddHelpAll #undef AddHelpText #undef AddCommand #undef AddShortcut +#undef BeginNamespace +#undef EndNamespace +#undef AddCustomCPP #ifdef QCONFIG_COMPARE #undef QCONFIG_COMPARE #endif -#ifdef QCONFIG_EXTERNS -#include "qconfig.h" +#else +#undef QCONFIG_HEADER_GUARD_NO_INCLUDE #endif + +#endif // QCONFIG_HEADER_GUARD_NO_INCLUDE diff --git a/GPU/GPUTracking/Standalone/utils/qconfig_helpers.h b/GPU/GPUTracking/Standalone/utils/qconfig_helpers.h new file mode 100644 index 0000000000000..d230dcbf419b1 --- /dev/null +++ b/GPU/GPUTracking/Standalone/utils/qconfig_helpers.h @@ -0,0 +1,46 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 qconfig_helpers.h +/// \author David Rohr + +#ifndef QCONFIG_HELPERS_H +#define QCONFIG_HELPERS_H + +#include <string> +#include <sstream> + +namespace qConfig +{ +template <class T> +std::string print_type(T val) +{ + std::ostringstream s; + s << val; + return s.str(); +}; +template <> +std::string print_type<char>(char val) +{ + return std::to_string(val); +}; +template <> +std::string print_type<unsigned char>(unsigned char val) +{ + return std::to_string(val); +}; +template <> +std::string print_type<bool>(bool val) +{ + return val ? "true" : "false"; +}; +} // namespace qConfig + +#endif diff --git a/GPU/GPUTracking/Standalone/utils/qconfigrtc.h b/GPU/GPUTracking/Standalone/utils/qconfigrtc.h new file mode 100644 index 0000000000000..e389ab6aab484 --- /dev/null +++ b/GPU/GPUTracking/Standalone/utils/qconfigrtc.h @@ -0,0 +1,43 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 qconfigrtc.h +/// \author David Rohr + +#ifndef QCONFIG_RTC_H +#define QCONFIG_RTC_H + +#include "qconfig.h" +#include "qconfig_helpers.h" + +#ifndef qon_mxstr +#define qon_mstr(a) #a +#define qon_mxstr(a) qon_mstr(a) +#endif + +template <class T> +static std::string qConfigPrintRtc(const T& tSrc, bool useConstexpr) +{ +#if defined(__cplusplus) && __cplusplus >= 201703L + std::stringstream out; +#define QCONFIG_PRINT_RTC +#include "qconfig.h" +#undef QCONFIG_PRINT_RTC + return out.str(); +#else + throw std::runtime_error("not supported"); +#endif +} + +#define QCONFIG_CONVERT_RTC +#include "qconfig.h" +#undef QCONFIG_CONVERT_RTC + +#endif diff --git a/GPU/GPUTracking/TPCClusterFinder/Array2D.h b/GPU/GPUTracking/TPCClusterFinder/Array2D.h index c25d4d2b210be..07964d4031c63 100644 --- a/GPU/GPUTracking/TPCClusterFinder/Array2D.h +++ b/GPU/GPUTracking/TPCClusterFinder/Array2D.h @@ -17,9 +17,7 @@ #include "clusterFinderDefs.h" #include "ChargePos.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { template <typename T, typename Layout> @@ -122,7 +120,6 @@ using TPCMapMemoryLayout = LinearLayout; template <typename T> using Array2D = AbstractArray2D<T, TPCMapMemoryLayout<T>>; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/CfConsts.h b/GPU/GPUTracking/TPCClusterFinder/CfConsts.h index 8edb84dbbb9fb..62a53afa45efb 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfConsts.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfConsts.h @@ -20,7 +20,7 @@ namespace GPUCA_NAMESPACE { namespace gpu { -namespace CfConsts +namespace cfconsts { GPUconstexpr() tpccf::Delta2 InnerNeighbors[8] = @@ -189,7 +189,7 @@ GPUconstexpr() uint NoiseSuppressionMinima[NOISE_SUPPRESSION_NEIGHBOR_NUM] = (1 << 24), (1 << 24) | (1 << 25)}; -} // namespace CfConsts +} // namespace cfconsts } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/TPCClusterFinder/CfFragment.h b/GPU/GPUTracking/TPCClusterFinder/CfFragment.h index 089c481b03913..dd338ba77aab3 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfFragment.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfFragment.h @@ -17,9 +17,7 @@ #include "clusterFinderDefs.h" #include "GPUCommonMath.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct CfFragment { @@ -74,6 +72,21 @@ struct CfFragment { return (hasBacklog ? t < OverlapTimebins : false) || (hasFuture ? t >= (length - OverlapTimebins) : false); } + GPUdi() tpccf::TPCFragmentTime lengthWithoutOverlap() const + { + return length - (hasBacklog ? OverlapTimebins : 0) - (hasFuture ? OverlapTimebins : 0); + } + + GPUdi() tpccf::TPCFragmentTime firstNonOverlapTimeBin() const + { + return (hasBacklog ? OverlapTimebins : 0); + } + + GPUdi() tpccf::TPCFragmentTime lastNonOverlapTimeBin() const + { + return length - (hasFuture ? OverlapTimebins : 0); + } + GPUdi() tpccf::TPCFragmentTime toLocal(tpccf::TPCTime t) const { return t - first(); @@ -98,7 +111,6 @@ struct CfFragment { } }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/CfUtils.h b/GPU/GPUTracking/TPCClusterFinder/CfUtils.h index 183fc8ab3905d..e16353acf6cc3 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfUtils.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfUtils.h @@ -20,9 +20,7 @@ #include "CfConsts.h" #include "GPUTPCClusterFinderKernels.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class CfUtils @@ -36,12 +34,12 @@ class CfUtils static GPUdi() bool innerAboveThreshold(uchar aboveThreshold, ushort outerIdx) { - return aboveThreshold & (1 << CfConsts::OuterToInner[outerIdx]); + return aboveThreshold & (1 << cfconsts::OuterToInner[outerIdx]); } static GPUdi() bool innerAboveThresholdInv(uchar aboveThreshold, ushort outerIdx) { - return aboveThreshold & (1 << CfConsts::OuterToInnerInv[outerIdx]); + return aboveThreshold & (1 << cfconsts::OuterToInnerInv[outerIdx]); } static GPUdi() bool isPeak(uchar peak) { return peak & 0x01; } @@ -166,7 +164,6 @@ class CfUtils } }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx b/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx index 0283f062e7c2f..547301ae80a0c 100644 --- a/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx @@ -17,16 +17,18 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace GPUCA_NAMESPACE::gpu::tpccf; -GPUd() void ClusterAccumulator::toNative(const ChargePos& pos, Charge q, tpc::ClusterNative& cn) const +GPUd() void ClusterAccumulator::toNative(const ChargePos& pos, Charge q, int minSplitNum, tpc::ClusterNative& cn) const { bool isEdgeCluster = CfUtils::isAtEdge(pos); - bool wasSplitInTime = mSplitInTime >= MIN_SPLIT_NUM; - bool wasSplitInPad = mSplitInPad >= MIN_SPLIT_NUM; + bool wasSplitInTime = mSplitInTime >= minSplitNum; + bool wasSplitInPad = mSplitInPad >= minSplitNum; + bool isSingleCluster = (mPadSigma == 0) || (mTimeSigma == 0); uchar flags = 0; flags |= (isEdgeCluster) ? tpc::ClusterNative::flagEdge : 0; flags |= (wasSplitInTime) ? tpc::ClusterNative::flagSplitTime : 0; flags |= (wasSplitInPad) ? tpc::ClusterNative::flagSplitPad : 0; + flags |= (isSingleCluster) ? tpc::ClusterNative::flagSingle : 0; cn.qMax = q; cn.qTot = mQtot; diff --git a/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.h b/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.h index 7711868041901..d39a4ed10496e 100644 --- a/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.h +++ b/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.h @@ -38,7 +38,7 @@ class ClusterAccumulator GPUd() tpccf::Charge updateOuter(PackedCharge, tpccf::Delta2); GPUd() void finalize(const ChargePos&, tpccf::Charge, tpccf::TPCTime); - GPUd() void toNative(const ChargePos&, tpccf::Charge, tpc::ClusterNative&) const; + GPUd() void toNative(const ChargePos&, tpccf::Charge, int, tpc::ClusterNative&) const; private: float mQtot = 0; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h index 46500a63969ac..7a01918708164 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChainContext.h @@ -51,6 +51,7 @@ struct GPUTPCCFChainContext { CfFragment fragmentFirst; std::pair<unsigned int, unsigned int> nextPos[GPUCA_NSLICES]; PtrSave ptrSave[GPUCA_NSLICES]; + const o2::tpc::ClusterNativeAccess* ptrClusterNativeSave; void prepare(bool tpcZS, const CfFragment& fragmentMax) { diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.cxx index fa4e3e438da4f..8af28459db03c 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.cxx @@ -22,7 +22,7 @@ template <> GPUdii() void GPUTPCCFChargeMapFiller::Thread<GPUTPCCFChargeMapFiller::fillIndexMap>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer) { Array2D<uint> indexMap(clusterer.mPindexMap); - fillIndexMapImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), clusterer.mPmemory->fragment, clusterer.mPdigits, indexMap, clusterer.mPmemory->counters.nPositions); + fillIndexMapImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), clusterer.mPmemory->fragment, clusterer.mPdigits, indexMap, clusterer.mPmemory->counters.nDigitsInFragment); } GPUd() void GPUTPCCFChargeMapFiller::fillIndexMapImpl(int nBlocks, int nThreads, int iBlock, int iThread, @@ -65,23 +65,48 @@ GPUd() void GPUTPCCFChargeMapFiller::fillFromDigitsImpl(int nBlocks, int nThread } template <> -GPUdii() void GPUTPCCFChargeMapFiller::Thread<GPUTPCCFChargeMapFiller::findFragmentStart>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer) +GPUdii() void GPUTPCCFChargeMapFiller::Thread<GPUTPCCFChargeMapFiller::findFragmentStart>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, char setPositions) { - // TODO: use binary search - if (get_global_id(0) != 0) { + if (iThread != 0) { return; } size_t nDigits = clusterer.mPmemory->counters.nDigits; const tpc::Digit* digits = clusterer.mPdigits; - size_t st = 0; - for (; st < nDigits && digits[st].getTimeStamp() < clusterer.mPmemory->fragment.first(); st++) { + size_t st = findTransition(clusterer.mPmemory->fragment.first(), digits, nDigits, 0); + size_t end = findTransition(clusterer.mPmemory->fragment.last(), digits, nDigits, st); + + clusterer.mPmemory->fragment.digitsStart = st; + + size_t elems = end - st; + + clusterer.mPmemory->counters.nDigitsInFragment = elems; + if (setPositions) { + clusterer.mPmemory->counters.nPositions = elems; } +} - size_t end = st; - for (; end < nDigits && digits[end].getTimeStamp() < clusterer.mPmemory->fragment.last(); end++) { +GPUd() size_t GPUTPCCFChargeMapFiller::findTransition(int time, const tpc::Digit* digits, size_t nDigits, size_t lower) +{ + if (!nDigits) { + return 0; } + size_t upper = nDigits - 1; - clusterer.mPmemory->fragment.digitsStart = st; - clusterer.mPmemory->counters.nPositions = end - st; + while (lower <= upper) { + size_t middle = (lower + upper) / 2; + + if (middle == 0) { + return 0; + } + + if (digits[middle].getTimeStamp() < time) { + lower = middle + 1; + } else if (digits[middle - 1].getTimeStamp() < time) { + return middle; + } else { + upper = middle - 1; + } + } + return lower; } diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h index aa3244679f723..40432943fa362 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFChargeMapFiller.h @@ -21,17 +21,12 @@ #include "Array2D.h" #include "PackedCharge.h" -namespace o2 -{ -namespace tpc +namespace o2::tpc { class Digit; -} -} // namespace o2 +} // namespace o2::tpc -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct ChargePos; @@ -64,9 +59,11 @@ class GPUTPCCFChargeMapFiller : public GPUKernelTemplate static GPUd() void fillIndexMapImpl(int, int, int, int, const CfFragment&, const tpc::Digit*, Array2D<uint>&, size_t); static GPUd() void fillFromDigitsImpl(int, int, int, int, const CfFragment&, size_t, const tpc::Digit*, ChargePos*, Array2D<PackedCharge>&); + + private: + static GPUd() size_t findTransition(int, const tpc::Digit*, size_t, size_t); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx new file mode 100644 index 0000000000000..d78ddf4a5e083 --- /dev/null +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTPCCFCheckPadBaseline.h +/// \author Felix Weiglhofer + +#include "GPUTPCCFCheckPadBaseline.h" +#include "Array2D.h" +#include "PackedCharge.h" +#include "clusterFinderDefs.h" + +using namespace GPUCA_NAMESPACE::gpu; +using namespace GPUCA_NAMESPACE::gpu::tpccf; + +template <> +GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer) +{ + static_assert(TPC_MAX_FRAGMENT_LEN % NumOfCachedTimebins == 0); + + Array2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap)); + + int totalCharges = 0; + int consecCharges = 0; + int maxConsecCharges = 0; + + int localPadId = iThread / NumOfCachedTimebins; + int localTimeBin = iThread % NumOfCachedTimebins; + bool handlePad = localTimeBin == 0; + int basePad = iBlock * PadsPerBlock; + + CfFragment& fragment = clusterer.mPmemory->fragment; + + ChargePos basePos = padToChargePos(basePad + localPadId, clusterer); + + for (tpccf::TPCFragmentTime t = localTimeBin + fragment.firstNonOverlapTimeBin(); t < fragment.lastNonOverlapTimeBin(); t += NumOfCachedTimebins) { + ChargePos pos = basePos.delta({0, t}); + smem.charges[localPadId][localTimeBin] = (pos.valid()) ? chargeMap[pos].unpack() : 0; + GPUbarrierWarp(); + if (handlePad) { + for (int i = 0; i < NumOfCachedTimebins; i++) { + Charge q = smem.charges[localPadId][i]; + totalCharges += (q > 0); + consecCharges = (q > 0) ? consecCharges + 1 : 0; + maxConsecCharges = CAMath::Max(consecCharges, maxConsecCharges); + } + } + } + + GPUbarrierWarp(); + + if (handlePad) { + int totalChargesBaseline = clusterer.Param().rec.maxTimeBinAboveThresholdIn1000Bin * fragment.lengthWithoutOverlap() / 1000; + int consecChargesBaseline = clusterer.Param().rec.maxConsecTimeBinAboveThreshold; + bool hasLostBaseline = (totalChargesBaseline > 0 && totalCharges >= totalChargesBaseline) || (consecChargesBaseline > 0 && maxConsecCharges >= consecChargesBaseline); + clusterer.mPpadHasLostBaseline[basePad + localPadId] |= hasLostBaseline; + } +} + +GPUd() ChargePos GPUTPCCFCheckPadBaseline::padToChargePos(int pad, const GPUTPCClusterFinder& clusterer) +{ + const GPUTPCGeometry& geo = clusterer.Param().tpcGeometry; + + int padOffset = 0; + for (Row r = 0; r < TPC_NUM_OF_ROWS; r++) { + int padInRow = pad - padOffset; + if (0 <= padInRow && padInRow < geo.NPads(r)) { + return ChargePos{r, Pad(padInRow), 0}; + } + padOffset += geo.NPads(r); + } + + return ChargePos{0, 0, INVALID_TIME_BIN}; +} diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h new file mode 100644 index 0000000000000..0a0efee0c957c --- /dev/null +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h @@ -0,0 +1,75 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 GPUTPCCFCheckPadBaseline.h +/// \author Felix Weiglhofer + +#ifndef O2_GPU_GPU_TPC_CF_CHECK_PAD_BASELINE_H +#define O2_GPU_GPU_TPC_CF_CHECK_PAD_BASELINE_H + +#include "GPUGeneralKernels.h" +#include "GPUConstantMem.h" + +#include "clusterFinderDefs.h" + +namespace GPUCA_NAMESPACE::gpu +{ + +class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate +{ + + private: + // Only use these constants on device side... + // Use getPadsPerBlock() for host side + enum { + PadsPerBlockGPU = 4, // Number of pads in a single cache line + PadsPerBlockCPU = 1, +#ifdef GPUCA_GPUCODE + PadsPerBlock = PadsPerBlockGPU, +#else + PadsPerBlock = PadsPerBlockCPU, +#endif + NumOfCachedTimebins = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFCheckPadBaseline) / PadsPerBlock, + }; + + public: + struct GPUSharedMemory { + tpccf::Charge charges[PadsPerBlock][NumOfCachedTimebins]; + }; + +#ifdef HAVE_O2HEADERS + typedef GPUTPCClusterFinder processorType; + GPUhdi() static processorType* Processor(GPUConstantMem& processors) + { + return processors.tpcClusterer; + } +#endif + + GPUhdi() CONSTEXPR static GPUDataTypes::RecoStep GetRecoStep() + { + return GPUDataTypes::RecoStep::TPCClusterFinding; + } + + // Use this to get num of pads per block on host side. Can't use constant there. + static int getPadsPerBlock(bool isGPU) + { + return (isGPU) ? PadsPerBlockGPU : PadsPerBlockCPU; + } + + template <int iKernel = defaultKernel> + GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer); + + private: + GPUd() static ChargePos padToChargePos(int pad, const GPUTPCClusterFinder&); +}; + +} // namespace GPUCA_NAMESPACE::gpu + +#endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.cxx index 10f3bc1ed43a7..36f0e1f0d6c92 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.cxx @@ -25,13 +25,15 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace GPUCA_NAMESPACE::gpu::tpccf; template <> -GPUdii() void GPUTPCCFClusterizer::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer) +GPUdii() void GPUTPCCFClusterizer::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, char onlyMC) { Array2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap)); CPU_ONLY( MCLabelAccumulator labelAcc(clusterer)); - GPUTPCCFClusterizer::computeClustersImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), clusterer.mPmemory->fragment, smem, chargeMap, clusterer.mPfilteredPeakPositions, CPU_PTR(&labelAcc), clusterer.mPmemory->counters.nClusters, clusterer.mNMaxClusterPerRow, clusterer.mPclusterInRow, clusterer.mPclusterByRow); + tpc::ClusterNative* clusterOut = (onlyMC) ? nullptr : clusterer.mPclusterByRow; + + GPUTPCCFClusterizer::computeClustersImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), clusterer.mPmemory->fragment, smem, chargeMap, clusterer.mPfilteredPeakPositions, clusterer.Param().rec, CPU_PTR(&labelAcc), clusterer.mPmemory->counters.nClusters, clusterer.mNMaxClusterPerRow, clusterer.mPclusterInRow, clusterOut, clusterer.mPclusterPosInRow); } GPUdii() void GPUTPCCFClusterizer::computeClustersImpl(int nBlocks, int nThreads, int iBlock, int iThread, @@ -39,11 +41,13 @@ GPUdii() void GPUTPCCFClusterizer::computeClustersImpl(int nBlocks, int nThreads GPUSharedMemory& smem, const Array2D<PackedCharge>& chargeMap, const ChargePos* filteredPeakPositions, + const GPUSettingsRec& calib, MCLabelAccumulator* labelAcc, uint clusternum, uint maxClusterPerRow, uint* clusterInRow, - tpc::ClusterNative* clusterByRow) + tpc::ClusterNative* clusterByRow, + uint* clusterPosInRow) { uint idx = get_global_id(0); @@ -56,7 +60,8 @@ GPUdii() void GPUTPCCFClusterizer::computeClustersImpl(int nBlocks, int nThreads ClusterAccumulator pc; CPU_ONLY(labelAcc->collect(pos, charge)); - buildClusterScratchPad( + buildCluster( + calib, chargeMap, pos, smem.posBcast, @@ -71,30 +76,35 @@ GPUdii() void GPUTPCCFClusterizer::computeClustersImpl(int nBlocks, int nThreads pc.finalize(pos, charge, fragment.start); tpc::ClusterNative myCluster; - pc.toNative(pos, charge, myCluster); + pc.toNative(pos, charge, calib.tpcCFminSplitNum, myCluster); -#if defined(CUT_QTOT) - bool aboveQTotCutoff = (pc.Q > QTOT_CUTOFF); -#else - bool aboveQTotCutoff = true; -#endif + bool aboveQTotCutoff = (myCluster.qTot > calib.tpcCFqtotCutoff); if (!aboveQTotCutoff) { + clusterPosInRow[idx] = maxClusterPerRow; return; } - uint rowIndex = sortIntoBuckets( - myCluster, - pos.row(), - maxClusterPerRow, - clusterInRow, - clusterByRow); - static_cast<void>(rowIndex); // Avoid unused varible warning on GPU. + uint rowIndex; + if (clusterByRow != nullptr) { + rowIndex = sortIntoBuckets( + myCluster, + pos.row(), + maxClusterPerRow, + clusterInRow, + clusterByRow); + if (clusterPosInRow != nullptr) { + clusterPosInRow[idx] = rowIndex; + } + } else { + rowIndex = clusterPosInRow[idx]; + } CPU_ONLY(labelAcc->commit(pos.row(), rowIndex, maxClusterPerRow)); } -GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadInner( +GPUdii() void GPUTPCCFClusterizer::updateClusterInner( + const GPUSettingsRec& calib, ushort lid, ushort N, const PackedCharge* buf, @@ -107,7 +117,7 @@ GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadInner( GPUCA_UNROLL(U(), U()) for (ushort i = 0; i < N; i++) { - Delta2 d = CfConsts::InnerNeighbors[i]; + Delta2 d = cfconsts::InnerNeighbors[i]; PackedCharge p = buf[N * lid + i]; @@ -116,7 +126,7 @@ GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadInner( CPU_ONLY( labelAcc->collect(pos.delta(d), q)); - aboveThreshold |= (uchar(q > CHARGE_THRESHOLD) << i); + aboveThreshold |= (uchar(q > calib.tpcCFinnerThreshold) << i); } innerAboveThreshold[lid] = aboveThreshold; @@ -124,7 +134,7 @@ GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadInner( GPUbarrier(); } -GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadOuter( +GPUdii() void GPUTPCCFClusterizer::updateClusterOuter( ushort lid, ushort N, ushort M, @@ -138,7 +148,7 @@ GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadOuter( for (ushort i = offset; i < M + offset; i++) { PackedCharge p = buf[N * lid + i]; - Delta2 d = CfConsts::OuterNeighbors[i]; + Delta2 d = cfconsts::OuterNeighbors[i]; Charge q = cluster->updateOuter(p, d); static_cast<void>(q); // Avoid unused varible warning on GPU. @@ -148,7 +158,8 @@ GPUdii() void GPUTPCCFClusterizer::updateClusterScratchpadOuter( } } -GPUdii() void GPUTPCCFClusterizer::buildClusterScratchPad( +GPUdii() void GPUTPCCFClusterizer::buildCluster( + const GPUSettingsRec& calib, const Array2D<PackedCharge>& chargeMap, ChargePos pos, ChargePos* posBcast, @@ -169,10 +180,11 @@ GPUdii() void GPUTPCCFClusterizer::buildClusterScratchPad( ll, 0, 8, - CfConsts::InnerNeighbors, + cfconsts::InnerNeighbors, posBcast, buf); - updateClusterScratchpadInner( + updateClusterInner( + calib, ll, 8, buf, @@ -194,13 +206,13 @@ GPUdii() void GPUTPCCFClusterizer::buildClusterScratchPad( ll, 0, 16, - CfConsts::OuterNeighbors, + cfconsts::OuterNeighbors, posBcast, innerAboveThreshold, buf); if (inGroup1) { - updateClusterScratchpadOuter( + updateClusterOuter( llhalf, 16, 16, @@ -219,12 +231,12 @@ GPUdii() void GPUTPCCFClusterizer::buildClusterScratchPad( ll, 0, 16, - CfConsts::OuterNeighbors, + cfconsts::OuterNeighbors, posBcast + wgSizeHalf, innerAboveThreshold + wgSizeHalf, buf); if (!inGroup1) { - updateClusterScratchpadOuter( + updateClusterOuter( llhalf, 16, 16, diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h index 94db2c61fa80a..fab830119aa0a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFClusterizer.h @@ -21,15 +21,12 @@ #include "Array2D.h" #include "PackedCharge.h" -namespace GPUCA_NAMESPACE -{ - -namespace tpc +namespace o2::tpc { struct ClusterNative; -} +} // namespace o2::tpc -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class ClusterAccumulator; @@ -58,22 +55,21 @@ class GPUTPCCFClusterizer : public GPUKernelTemplate return GPUDataTypes::RecoStep::TPCClusterFinding; } - template <int iKernel = defaultKernel, typename... Args> - GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); + template <int iKernel = defaultKernel> + GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, char); - static GPUd() void computeClustersImpl(int, int, int, int, const CfFragment&, GPUSharedMemory&, const Array2D<PackedCharge>&, const ChargePos*, MCLabelAccumulator*, uint, uint, uint*, tpc::ClusterNative*); + static GPUd() void computeClustersImpl(int, int, int, int, const CfFragment&, GPUSharedMemory&, const Array2D<PackedCharge>&, const ChargePos*, const GPUSettingsRec&, MCLabelAccumulator*, uint, uint, uint*, tpc::ClusterNative*, uint*); private: - static GPUd() void updateClusterScratchpadInner(ushort, ushort, const PackedCharge*, const ChargePos&, ClusterAccumulator*, MCLabelAccumulator*, uchar*); + static GPUd() void updateClusterInner(const GPUSettingsRec&, ushort, ushort, const PackedCharge*, const ChargePos&, ClusterAccumulator*, MCLabelAccumulator*, uchar*); - static GPUd() void updateClusterScratchpadOuter(ushort, ushort, ushort, ushort, const PackedCharge*, const ChargePos&, ClusterAccumulator*, MCLabelAccumulator*); + static GPUd() void updateClusterOuter(ushort, ushort, ushort, ushort, const PackedCharge*, const ChargePos&, ClusterAccumulator*, MCLabelAccumulator*); - static GPUd() void buildClusterScratchPad(const Array2D<PackedCharge>&, ChargePos, ChargePos*, PackedCharge*, uchar*, ClusterAccumulator*, MCLabelAccumulator*); + static GPUd() void buildCluster(const GPUSettingsRec&, const Array2D<PackedCharge>&, ChargePos, ChargePos*, PackedCharge*, uchar*, ClusterAccumulator*, MCLabelAccumulator*); static GPUd() uint sortIntoBuckets(const tpc::ClusterNative&, uint, uint, uint*, tpc::ClusterNative*); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index fcdeb2d608481..ae9d5ab3fda0c 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -24,6 +24,7 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace GPUCA_NAMESPACE::gpu::tpccf; using namespace o2::tpc; +using namespace o2::tpc::constants; template <> GPUdii() void GPUTPCCFDecodeZS::Thread<GPUTPCCFDecodeZS::decodeZS>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, int firstHBF) @@ -87,7 +88,7 @@ GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUShared const unsigned int decodeBits = decode12bit ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1; const float decodeBitsFactor = 1.f / (1 << (decodeBits - 10)); unsigned int mask = (1 << decodeBits) - 1; - int timeBin = (hdr->timeOffset + (GPURawDataUtils::getOrbit(rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / Constants::LHCBCPERTIMEBIN; + int timeBin = (hdr->timeOffset + (GPURawDataUtils::getOrbit(rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; const int rowOffset = s.regionStartRow + ((endpoint & 1) ? (s.nRowsRegion / 2) : 0); const int nRows = (endpoint & 1) ? (s.nRowsRegion - s.nRowsRegion / 2) : (s.nRowsRegion / 2); @@ -164,11 +165,15 @@ GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUShared const CfFragment& fragment = clusterer.mPmemory->fragment; TPCTime globalTime = timeBin + l; bool inFragment = fragment.contains(globalTime); - ChargePos pos(Row(rowOffset + m), Pad(pad++), inFragment ? fragment.toLocal(globalTime) : INVALID_TIME_BIN); + Row row = rowOffset + m; + ChargePos pos(row, Pad(pad), inFragment ? fragment.toLocal(globalTime) : INVALID_TIME_BIN); positions[nDigitsTmp++] = pos; if (inFragment) { - chargeMap[pos] = PackedCharge(float(byte & mask) * decodeBitsFactor); + float q = float(byte & mask) * decodeBitsFactor; + q *= clusterer.GetConstantMem()->calibObjects.tpcPadGain->getGainCorrection(slice, row, pad); + chargeMap[pos] = PackedCharge(q); } + pad++; byte = byte >> decodeBits; bits -= decodeBits; seqLen--; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index 4833b630de7bb..602515675f7a0 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -19,9 +19,7 @@ #include "GPUConstantMem.h" #include "DataFormatsTPC/ZeroSuppression.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCClusterFinder; @@ -62,7 +60,6 @@ class GPUTPCCFDecodeZS : public GPUKernelTemplate GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.cxx index 95dde7bc6f430..26f4dceeb96f6 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.cxx @@ -25,14 +25,14 @@ GPUdii() void GPUTPCCFDeconvolution::Thread<0>(int nBlocks, int nThreads, int iB { Array2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap)); Array2D<uchar> isPeakMap(clusterer.mPpeakMap); - GPUTPCCFDeconvolution::countPeaksImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), smem, isPeakMap, chargeMap, clusterer.mPpositions, clusterer.mPmemory->counters.nPositions); + GPUTPCCFDeconvolution::deconvolutionImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), smem, isPeakMap, chargeMap, clusterer.mPpositions, clusterer.mPmemory->counters.nPositions); } -GPUdii() void GPUTPCCFDeconvolution::countPeaksImpl(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, - const Array2D<uchar>& peakMap, - Array2D<PackedCharge>& chargeMap, - const ChargePos* positions, - const uint digitnum) +GPUdii() void GPUTPCCFDeconvolution::deconvolutionImpl(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, + const Array2D<uchar>& peakMap, + Array2D<PackedCharge>& chargeMap, + const ChargePos* positions, + const uint digitnum) { SizeT idx = get_global_id(0); @@ -63,13 +63,13 @@ GPUdii() void GPUTPCCFDeconvolution::countPeaksImpl(int nBlocks, int nThreads, i ll, 0, 8, - CfConsts::InnerNeighbors, + cfconsts::InnerNeighbors, smem.posBcast1, smem.buf); uchar aboveThreshold = 0; if (partId < in3x3) { - peakCount = countPeaksScratchpadInner(partId, smem.buf, &aboveThreshold); + peakCount = countPeaksInner(partId, smem.buf, &aboveThreshold); } ushort in5x5 = 0; @@ -88,13 +88,13 @@ GPUdii() void GPUTPCCFDeconvolution::countPeaksImpl(int nBlocks, int nThreads, i ll, 0, 16, - CfConsts::OuterNeighbors, + cfconsts::OuterNeighbors, smem.posBcast1, smem.aboveThresholdBcast, smem.buf); if (partId < in5x5) { - peakCount = countPeaksScratchpadOuter(partId, aboveThreshold, smem.buf); + peakCount = countPeaksOuter(partId, aboveThreshold, smem.buf); peakCount *= -1; } @@ -114,7 +114,7 @@ GPUdii() void GPUTPCCFDeconvolution::countPeaksImpl(int nBlocks, int nThreads, i chargeMap[pos] = p; } -GPUd() char GPUTPCCFDeconvolution::countPeaksScratchpadInner( +GPUdi() char GPUTPCCFDeconvolution::countPeaksInner( ushort ll, const uchar* isPeak, uchar* aboveThreshold) @@ -130,7 +130,7 @@ GPUd() char GPUTPCCFDeconvolution::countPeaksScratchpadInner( return peaks; } -GPUd() char GPUTPCCFDeconvolution::countPeaksScratchpadOuter( +GPUdi() char GPUTPCCFDeconvolution::countPeaksOuter( ushort ll, uchar aboveThreshold, const uchar* isPeak) diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h index 4a8c88dd56600..d2af024ef221e 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.h @@ -21,9 +21,7 @@ #include "Array2D.h" #include "PackedCharge.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCCFDeconvolution : public GPUKernelTemplate @@ -36,7 +34,6 @@ class GPUTPCCFDeconvolution : public GPUKernelTemplate uchar buf[SCRATCH_PAD_WORK_GROUP_SIZE * SCRATCH_PAD_COUNT_N]; }; - static GPUd() void countPeaksImpl(int, int, int, int, GPUSharedMemory&, const Array2D<uchar>&, Array2D<PackedCharge>&, const ChargePos*, const uint); #ifdef HAVE_O2HEADERS typedef GPUTPCClusterFinder processorType; @@ -55,11 +52,12 @@ class GPUTPCCFDeconvolution : public GPUKernelTemplate GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); private: - static GPUd() char countPeaksScratchpadInner(ushort, const uchar*, uchar*); - static GPUd() char countPeaksScratchpadOuter(ushort, uchar, const uchar*); + static GPUd() void deconvolutionImpl(int, int, int, int, GPUSharedMemory&, const Array2D<uchar>&, Array2D<PackedCharge>&, const ChargePos*, const uint); + + static GPUdi() char countPeaksInner(ushort, const uchar*, uchar*); + static GPUdi() char countPeaksOuter(ushort, uchar, const uchar*); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h index d6d4f83df1129..37dcb4eda0833 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFGather.h @@ -17,9 +17,7 @@ #include "GPUGeneralKernels.h" #include "GPUConstantMem.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCClusterFinder; @@ -44,7 +42,6 @@ class GPUTPCCFGather : public GPUKernelTemplate GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.cxx index 01d1701fdacac..cbeb7fe2edef2 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.cxx @@ -28,16 +28,12 @@ void GPUTPCCFMCLabelFlattener::setGlobalOffsetsAndAllocate( uint headerOffset = labels.header.size(); uint dataOffset = labels.data.size(); + cls.mPlabelsHeaderGlobalOffset = headerOffset; + cls.mPlabelsDataGlobalOffset = dataOffset; + for (Row row = 0; row < GPUCA_ROW_COUNT; row++) { - cls.mPlabelHeaderOffset[row] = headerOffset; headerOffset += cls.mPclusterInRow[row]; - - cls.mPlabelDataOffset[row] = dataOffset; - if (cls.mPclusterInRow[row] > 0) { - auto& lastInterim = cls.mPlabelsByRow[cls.mNMaxClusterPerRow * row + cls.mPclusterInRow[row] - 1]; - uint labelsInRow = lastInterim.offset + lastInterim.labels.size(); - dataOffset += labelsInRow; - } + dataOffset += cls.mPlabelsInRow[row]; } labels.header.resize(headerOffset); @@ -52,32 +48,40 @@ GPUd() void GPUTPCCFMCLabelFlattener::Thread<GPUTPCCFMCLabelFlattener::setRowOff Row row = get_global_id(0); uint clusterInRow = clusterer.mPclusterInRow[row]; - uint offset = 0; + uint labelCount = 0; for (uint i = 0; i < clusterInRow; i++) { auto& interim = clusterer.mPlabelsByRow[row * clusterer.mNMaxClusterPerRow + i]; - interim.offset = offset; - offset += interim.labels.size(); + labelCount += interim.labels.size(); } + + clusterer.mPlabelsInRow[row] = labelCount; #endif } template <> -GPUd() void GPUTPCCFMCLabelFlattener::Thread<GPUTPCCFMCLabelFlattener::flatten>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory&, processorType& clusterer, uint row, GPUTPCLinearLabels* out) +GPUd() void GPUTPCCFMCLabelFlattener::Thread<GPUTPCCFMCLabelFlattener::flatten>(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory&, processorType& clusterer, GPUTPCLinearLabels* out) { #if !defined(GPUCA_GPUCODE) - uint idx = get_global_id(0); - - GPUTPCClusterMCInterim& interim = clusterer.mPlabelsByRow[row * clusterer.mNMaxClusterPerRow + idx]; + uint row = get_global_id(0); - uint headerOffset = clusterer.mPlabelHeaderOffset[row] + idx; - uint dataOffset = clusterer.mPlabelDataOffset[row] + interim.offset; - - assert(dataOffset + interim.labels.size() <= out->data.size()); + uint headerOffset = clusterer.mPlabelsHeaderGlobalOffset; + uint dataOffset = clusterer.mPlabelsDataGlobalOffset; + for (uint r = 0; r < row; r++) { + headerOffset += clusterer.mPclusterInRow[r]; + dataOffset += clusterer.mPlabelsInRow[r]; + } - out->header[headerOffset] = dataOffset; - std::copy(interim.labels.cbegin(), interim.labels.cend(), out->data.begin() + dataOffset); + auto* labels = &clusterer.mPlabelsByRow[row * clusterer.mNMaxClusterPerRow]; + for (uint c = 0; c < clusterer.mPclusterInRow[row]; c++) { + GPUTPCClusterMCInterim& interim = labels[c]; + assert(dataOffset + interim.labels.size() <= out->data.size()); + out->header[headerOffset] = dataOffset; + std::copy(interim.labels.cbegin(), interim.labels.cend(), out->data.begin() + dataOffset); - interim = {}; // ensure interim labels are destroyed to prevent memleak + headerOffset++; + dataOffset += interim.labels.size(); + interim = {}; // ensure interim labels are destroyed to prevent memleak + } #endif } diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h index 79441b43c399b..23c4717af237a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFMCLabelFlattener.h @@ -20,9 +20,7 @@ #include "GPUTPCClusterFinder.h" #include "GPUConstantMem.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct GPUTPCLinearLabels; @@ -58,7 +56,6 @@ class GPUTPCCFMCLabelFlattener : public GPUKernelTemplate static void setGlobalOffsetsAndAllocate(GPUTPCClusterFinder&, GPUTPCLinearLabels&); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.cxx index ff3b0c2ee9e9b..b80258e8d2de2 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.cxx @@ -25,7 +25,7 @@ GPUdii() void GPUTPCCFNoiseSuppression::Thread<GPUTPCCFNoiseSuppression::noiseSu { Array2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap)); Array2D<uchar> isPeakMap(clusterer.mPpeakMap); - noiseSuppressionImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), smem, chargeMap, isPeakMap, clusterer.mPpeakPositions, clusterer.mPmemory->counters.nPeaks, clusterer.mPisPeak); + noiseSuppressionImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), smem, clusterer.Param().rec, chargeMap, isPeakMap, clusterer.mPpeakPositions, clusterer.mPmemory->counters.nPeaks, clusterer.mPisPeak); } template <> @@ -36,6 +36,7 @@ GPUdii() void GPUTPCCFNoiseSuppression::Thread<GPUTPCCFNoiseSuppression::updateP } GPUdii() void GPUTPCCFNoiseSuppression::noiseSuppressionImpl(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, + const GPUSettingsRec& calibration, const Array2D<PackedCharge>& chargeMap, const Array2D<uchar>& peakMap, const ChargePos* peakPositions, @@ -48,9 +49,10 @@ GPUdii() void GPUTPCCFNoiseSuppression::noiseSuppressionImpl(int nBlocks, int nT Charge charge = chargeMap[pos].unpack(); ulong minimas, bigger, peaksAround; - findMinimaAndPeaksScratchpad( + findMinimaAndPeaks( chargeMap, peakMap, + calibration, charge, pos, smem.posBcast, @@ -91,7 +93,7 @@ GPUd() void GPUTPCCFNoiseSuppression::updatePeaksImpl(int nBlocks, int nThreads, // So we can just set the bit and avoid rereading the charge } -GPUd() void GPUTPCCFNoiseSuppression::checkForMinima( +GPUdi() void GPUTPCCFNoiseSuppression::checkForMinima( float q, float epsilon, PackedCharge other, @@ -108,7 +110,7 @@ GPUd() void GPUTPCCFNoiseSuppression::checkForMinima( *bigger |= (lq << pos); } -GPUd() void GPUTPCCFNoiseSuppression::findMinimaScratchPad( +GPUdi() void GPUTPCCFNoiseSuppression::findMinima( const PackedCharge* buf, const ushort ll, const int N, @@ -118,6 +120,7 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaScratchPad( ulong* minimas, ulong* bigger) { + GPUCA_UNROLL(U(), U()) for (int i = 0; i < N; i++, pos++) { PackedCharge other = buf[N * ll + i]; @@ -125,13 +128,14 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaScratchPad( } } -GPUd() void GPUTPCCFNoiseSuppression::findPeaksScratchPad( +GPUdi() void GPUTPCCFNoiseSuppression::findPeaks( const uchar* buf, const ushort ll, const int N, int pos, ulong* peaks) { + GPUCA_UNROLL(U(), U()) for (int i = 0; i < N; i++, pos++) { ulong p = CfUtils::isPeak(buf[N * ll + i]); @@ -139,15 +143,16 @@ GPUd() void GPUTPCCFNoiseSuppression::findPeaksScratchPad( } } -GPUd() bool GPUTPCCFNoiseSuppression::keepPeak( +GPUdi() bool GPUTPCCFNoiseSuppression::keepPeak( ulong minima, ulong peaks) { bool keepMe = true; + GPUCA_UNROLL(U(), U()) for (int i = 0; i < NOISE_SUPPRESSION_NEIGHBOR_NUM; i++) { bool otherPeak = (peaks & (ulong(1) << i)); - bool minimaBetween = (minima & CfConsts::NoiseSuppressionMinima[i]); + bool minimaBetween = (minima & cfconsts::NoiseSuppressionMinima[i]); keepMe &= (!otherPeak || minimaBetween); } @@ -155,9 +160,10 @@ GPUd() bool GPUTPCCFNoiseSuppression::keepPeak( return keepMe; } -GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( +GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaks( const Array2D<PackedCharge>& chargeMap, const Array2D<uchar>& peakMap, + const GPUSettingsRec& calibration, float q, const ChargePos& pos, ChargePos* posBcast, @@ -191,17 +197,17 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 16, 2, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast, buf); - findMinimaScratchPad( + findMinima( buf, ll, 2, 16, q, - NOISE_SUPPRESSION_MINIMA_EPSILON, + calibration.tpcCFnoiseSuppressionEpsilon, minimas, bigger); @@ -212,18 +218,18 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 0, 16, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast, buf); if (inGroup1) { - findMinimaScratchPad( + findMinima( buf, llhalf, 16, 0, q, - NOISE_SUPPRESSION_MINIMA_EPSILON, + calibration.tpcCFnoiseSuppressionEpsilon, minimas, bigger); } @@ -235,18 +241,18 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 18, 16, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast, buf); if (inGroup1) { - findMinimaScratchPad( + findMinima( buf, llhalf, 16, 18, q, - NOISE_SUPPRESSION_MINIMA_EPSILON, + calibration.tpcCFnoiseSuppressionEpsilon, minimas, bigger); } @@ -259,18 +265,18 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 0, 16, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast + wgSizeHalf, buf); if (!inGroup1) { - findMinimaScratchPad( + findMinima( buf, llhalf, 16, 0, q, - NOISE_SUPPRESSION_MINIMA_EPSILON, + calibration.tpcCFnoiseSuppressionEpsilon, minimas, bigger); } @@ -282,18 +288,18 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 18, 16, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast + wgSizeHalf, buf); if (!inGroup1) { - findMinimaScratchPad( + findMinima( buf, llhalf, 16, 18, q, - NOISE_SUPPRESSION_MINIMA_EPSILON, + calibration.tpcCFnoiseSuppressionEpsilon, minimas, bigger); } @@ -312,11 +318,11 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 0, 16, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast, bufp); - findPeaksScratchPad( + findPeaks( bufp, ll, 16, @@ -330,11 +336,11 @@ GPUd() void GPUTPCCFNoiseSuppression::findMinimaAndPeaksScratchpad( ll, 18, 16, - CfConsts::NoiseSuppressionNeighbors, + cfconsts::NoiseSuppressionNeighbors, posBcast, bufp); - findPeaksScratchPad( + findPeaks( bufp, ll, 16, diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h index ac1d0a1b9ff12..4cb04bacb4a90 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFNoiseSuppression.h @@ -21,9 +21,7 @@ #include "Array2D.h" #include "PackedCharge.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct ChargePos; @@ -59,22 +57,21 @@ class GPUTPCCFNoiseSuppression : public GPUKernelTemplate GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); private: - static GPUd() void noiseSuppressionImpl(int, int, int, int, GPUSharedMemory&, const Array2D<PackedCharge>&, const Array2D<uchar>&, const ChargePos*, const uint, uchar*); + static GPUd() void noiseSuppressionImpl(int, int, int, int, GPUSharedMemory&, const GPUSettingsRec&, const Array2D<PackedCharge>&, const Array2D<uchar>&, const ChargePos*, const uint, uchar*); static GPUd() void updatePeaksImpl(int, int, int, int, const ChargePos*, const uchar*, const uint, Array2D<uchar>&); - static GPUd() void checkForMinima(float, float, PackedCharge, int, ulong*, ulong*); + static GPUdi() void checkForMinima(float, float, PackedCharge, int, ulong*, ulong*); - static GPUd() void findMinimaScratchPad(const PackedCharge*, const ushort, const int, int, const float, const float, ulong*, ulong*); + static GPUdi() void findMinima(const PackedCharge*, const ushort, const int, int, const float, const float, ulong*, ulong*); - static GPUd() void findPeaksScratchPad(const uchar*, const ushort, const int, int, ulong*); + static GPUdi() void findPeaks(const uchar*, const ushort, const int, int, ulong*); - static GPUd() bool keepPeak(ulong, ulong); + static GPUdi() bool keepPeak(ulong, ulong); - static GPUd() void findMinimaAndPeaksScratchpad(const Array2D<PackedCharge>&, const Array2D<uchar>&, float, const ChargePos&, ChargePos*, PackedCharge*, ulong*, ulong*, ulong*); + static GPUd() void findMinimaAndPeaks(const Array2D<PackedCharge>&, const Array2D<uchar>&, const GPUSettingsRec&, float, const ChargePos&, ChargePos*, PackedCharge*, ulong*, ulong*, ulong*); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx index d6484147c502f..e812f5e00fb2d 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx @@ -16,6 +16,7 @@ #include "Array2D.h" #include "CfUtils.h" #include "PackedCharge.h" +#include "TPCPadGainCalib.h" using namespace GPUCA_NAMESPACE::gpu; using namespace GPUCA_NAMESPACE::gpu::tpccf; @@ -25,21 +26,22 @@ GPUdii() void GPUTPCCFPeakFinder::Thread<0>(int nBlocks, int nThreads, int iBloc { Array2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap)); Array2D<uchar> isPeakMap(clusterer.mPpeakMap); - findPeaksImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), smem, chargeMap, clusterer.mPpositions, clusterer.mPmemory->counters.nPositions, clusterer.mPisPeak, isPeakMap); + findPeaksImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), smem, chargeMap, clusterer.mPpadHasLostBaseline, clusterer.mPpositions, clusterer.mPmemory->counters.nPositions, clusterer.Param().rec, *clusterer.GetConstantMem()->calibObjects.tpcPadGain, clusterer.mPisPeak, isPeakMap); } -GPUdii() bool GPUTPCCFPeakFinder::isPeakScratchPad( +GPUdii() bool GPUTPCCFPeakFinder::isPeak( GPUSharedMemory& smem, Charge q, const ChargePos& pos, ushort N, const Array2D<PackedCharge>& chargeMap, + const GPUSettingsRec& calib, ChargePos* posBcast, PackedCharge* buf) { ushort ll = get_local_id(0); - bool belowThreshold = (q <= QMAX_CUTOFF); + bool belowThreshold = (q <= calib.tpcCFqmaxCutoff); ushort lookForPeaks; ushort partId = CfUtils::partition( @@ -61,7 +63,7 @@ GPUdii() bool GPUTPCCFPeakFinder::isPeakScratchPad( ll, 0, N, - CfConsts::InnerNeighbors, + cfconsts::InnerNeighbors, posBcast, buf); @@ -89,8 +91,11 @@ GPUdii() bool GPUTPCCFPeakFinder::isPeakScratchPad( GPUd() void GPUTPCCFPeakFinder::findPeaksImpl(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, const Array2D<PackedCharge>& chargeMap, + const uchar* padHasLostBaseline, const ChargePos* positions, SizeT digitnum, + const GPUSettingsRec& calib, + const TPCPadGainCalib& gainCorrection, // Only used for globalPad() function uchar* isPeakPredicate, Array2D<uchar>& peakMap) { @@ -102,8 +107,10 @@ GPUd() void GPUTPCCFPeakFinder::findPeaksImpl(int nBlocks, int nThreads, int iBl ChargePos pos = positions[CAMath::Min(idx, (SizeT)(digitnum - 1))]; Charge charge = pos.valid() ? chargeMap[pos].unpack() : Charge(0); - uchar peak; - peak = isPeakScratchPad(smem, charge, pos, SCRATCH_PAD_SEARCH_N, chargeMap, smem.posBcast, smem.buf); + bool hasLostBaseline = padHasLostBaseline[gainCorrection.globalPad(pos.row(), pos.pad())]; + charge = (hasLostBaseline) ? 0.f : charge; + + uchar peak = isPeak(smem, charge, pos, SCRATCH_PAD_SEARCH_N, chargeMap, calib, smem.posBcast, smem.buf); // Exit early if dummy. See comment above. bool iamDummy = (idx >= digitnum); @@ -113,5 +120,5 @@ GPUd() void GPUTPCCFPeakFinder::findPeaksImpl(int nBlocks, int nThreads, int iBl isPeakPredicate[idx] = peak; - peakMap[pos] = (uchar(charge > CHARGE_THRESHOLD) << 1) | peak; + peakMap[pos] = (uchar(charge > calib.tpcCFinnerThreshold) << 1) | peak; } diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h index 8fcded952f924..e6107af137313 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.h @@ -21,9 +21,7 @@ #include "Array2D.h" #include "PackedCharge.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct ChargePos; @@ -37,8 +35,6 @@ class GPUTPCCFPeakFinder : public GPUKernelTemplate PackedCharge buf[SCRATCH_PAD_WORK_GROUP_SIZE * SCRATCH_PAD_SEARCH_N]; }; - static GPUd() void findPeaksImpl(int, int, int, int, GPUSharedMemory&, const Array2D<PackedCharge>&, const ChargePos*, tpccf::SizeT, uchar*, Array2D<uchar>&); - #ifdef HAVE_O2HEADERS typedef GPUTPCClusterFinder processorType; GPUhdi() static processorType* Processor(GPUConstantMem& processors) @@ -56,10 +52,11 @@ class GPUTPCCFPeakFinder : public GPUKernelTemplate GPUd() static void Thread(int nBlocks, int nThreads, int iBlock, int iThread, GPUSharedMemory& smem, processorType& clusterer, Args... args); private: - static GPUd() bool isPeakScratchPad(GPUSharedMemory&, tpccf::Charge, const ChargePos&, ushort, const Array2D<o2::gpu::PackedCharge>&, ChargePos*, PackedCharge*); + static GPUd() void findPeaksImpl(int, int, int, int, GPUSharedMemory&, const Array2D<PackedCharge>&, const uchar*, const ChargePos*, tpccf::SizeT, const GPUSettingsRec&, const TPCPadGainCalib&, uchar*, Array2D<uchar>&); + + static GPUd() bool isPeak(GPUSharedMemory&, tpccf::Charge, const ChargePos&, ushort, const Array2D<PackedCharge>&, const GPUSettingsRec&, ChargePos*, PackedCharge*); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h index f817fdc13f5a5..2090b7835dab0 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.h @@ -19,9 +19,7 @@ #include "GPUConstantMem.h" #include "GPUTPCClusterFinder.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCCFStreamCompaction : public GPUKernelTemplate @@ -76,7 +74,6 @@ class GPUTPCCFStreamCompaction : public GPUKernelTemplate static GPUd() int compactionElems(processorType& clusterer, int stage); }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx index d05fd115b4850..37d35ebcb149e 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx @@ -32,6 +32,7 @@ void GPUTPCClusterFinder::InitializeProcessor() GPUTPCClusterFinder::~GPUTPCClusterFinder() { + delete[] mMinMaxCN; clearMCMemory(); } @@ -74,9 +75,13 @@ void* GPUTPCClusterFinder::SetPointersOutput(void* mem) void* GPUTPCClusterFinder::SetPointersScratch(void* mem) { + computePointerWithAlignment(mem, mPpadHasLostBaseline, TPC_PADS_IN_SECTOR); computePointerWithAlignment(mem, mPpositions, mNMaxDigitsFragment); computePointerWithAlignment(mem, mPpeakPositions, mNMaxPeaks); computePointerWithAlignment(mem, mPfilteredPeakPositions, mNMaxClusters); + if (mRec->GetProcessingSettings().runMC) { + computePointerWithAlignment(mem, mPclusterPosInRow, mNMaxClusters); + } computePointerWithAlignment(mem, mPisPeak, mNMaxDigitsFragment); computePointerWithAlignment(mem, mPchargeMap, TPCMapMemoryLayout<decltype(*mPchargeMap)>::items()); computePointerWithAlignment(mem, mPpeakMap, TPCMapMemoryLayout<decltype(*mPpeakMap)>::items()); @@ -89,10 +94,16 @@ void GPUTPCClusterFinder::RegisterMemoryAllocation() { AllocateAndInitializeLate(); mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_STACK, "TPCClustererInput"); - mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCClustererScratch", GPUMemoryReuse{GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::ClustererScratch, (unsigned short)(mISlice % mRec->GetDeviceProcessingSettings().nTPCClustererLanes)}); + + int scratchType = GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK; + if (mRec->GetProcessingSettings().runMC) { + scratchType |= GPUMemoryResource::MEMORY_HOST | GPUMemoryResource::MEMORY_GPU; + } + mScratchId = mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersScratch, scratchType, "TPCClustererScratch", GPUMemoryReuse{GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::ClustererScratch, (unsigned short)(mISlice % mRec->GetProcessingSettings().nTPCClustererLanes)}); + mMemoryId = mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersMemory, GPUMemoryResource::MEMORY_PERMANENT, "TPCClustererMemory"); mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_STACK, "TPCClustererOutput"); - mZSId = mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersZS, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_CUSTOM_TRANSFER | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_STACK, "TPCClustererZSData", GPUMemoryReuse{GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::ClustererZS, (unsigned short)(mISlice % mRec->GetDeviceProcessingSettings().nTPCClustererLanes)}); + mZSId = mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersZS, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_CUSTOM_TRANSFER | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_STACK, "TPCClustererZSData", GPUMemoryReuse{GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::ClustererZS, (unsigned short)(mISlice % mRec->GetProcessingSettings().nTPCClustererLanes)}); mZSOffsetId = mRec->RegisterMemoryAllocation(this, &GPUTPCClusterFinder::SetPointersZSOffset, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_CUSTOM_TRANSFER | GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_STACK, "TPCClustererZSOffsets"); } @@ -133,8 +144,7 @@ void GPUTPCClusterFinder::PrepareMC() clearMCMemory(); mPindexMap = new uint[TPCMapMemoryLayout<decltype(*mPindexMap)>::items()]; mPlabelsByRow = new GPUTPCClusterMCInterim[GPUCA_ROW_COUNT * mNMaxClusterPerRow]; - mPlabelHeaderOffset = new uint[GPUCA_ROW_COUNT]; - mPlabelDataOffset = new uint[GPUCA_ROW_COUNT]; + mPlabelsInRow = new uint[GPUCA_ROW_COUNT]; } void GPUTPCClusterFinder::clearMCMemory() @@ -143,10 +153,6 @@ void GPUTPCClusterFinder::clearMCMemory() mPindexMap = nullptr; delete[] mPlabelsByRow; mPlabelsByRow = nullptr; - delete[] mPlabelHeaderOffset; - mPlabelHeaderOffset = nullptr; - delete[] mPlabelDataOffset; - mPlabelDataOffset = nullptr; - delete[] mMinMaxCN; - mMinMaxCN = nullptr; + delete[] mPlabelsInRow; + mPlabelsInRow = nullptr; } diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h index a582595ed32d5..d48f1e5d453c4 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.h @@ -18,6 +18,7 @@ #include "GPUProcessor.h" #include "GPUDataTypes.h" #include "CfFragment.h" +#include "TPCPadGainCalib.h" namespace o2 { @@ -28,6 +29,8 @@ namespace dataformats { template <typename TruthElement> class MCTruthContainer; +template <typename TruthElement> +class ConstMCTruthContainerView; } // namespace dataformats namespace tpc @@ -38,20 +41,22 @@ class Digit; } // namespace o2 -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { struct GPUTPCClusterMCInterim; +struct TPCPadGainCalib; struct ChargePos; +class GPUTPCGeometry; + class GPUTPCClusterFinder : public GPUProcessor { public: struct Memory { struct counters_t { size_t nDigits = 0; + tpccf::SizeT nDigitsInFragment = 0; // num of digits in fragment can differ from nPositions if ZS is active tpccf::SizeT nPositions = 0; tpccf::SizeT nPeaks = 0; tpccf::SizeT nClusters = 0; @@ -93,11 +98,15 @@ class GPUTPCClusterFinder : public GPUProcessor unsigned char* mPzs = nullptr; ZSOffset* mPzsOffsets = nullptr; MinMaxCN* mMinMaxCN = nullptr; + unsigned char* mPpadHasLostBaseline = nullptr; tpc::Digit* mPdigits = nullptr; // input digits, only set if ZS is skipped ChargePos* mPpositions = nullptr; ChargePos* mPpeakPositions = nullptr; ChargePos* mPfilteredPeakPositions = nullptr; unsigned char* mPisPeak = nullptr; + uint* mPclusterPosInRow = nullptr; // store the index where the corresponding cluster is stored in a bucket. + // Required when MC are enabled to write the mc data to the correct position. + // Set to >= mNMaxClusterPerRow if cluster was discarded. ushort* mPchargeMap = nullptr; unsigned char* mPpeakMap = nullptr; uint* mPindexMap = nullptr; @@ -107,9 +116,10 @@ class GPUTPCClusterFinder : public GPUProcessor int* mPbuf = nullptr; Memory* mPmemory = nullptr; - o2::dataformats::MCTruthContainer<o2::MCCompLabel> const* mPinputLabels = nullptr; - uint* mPlabelHeaderOffset = nullptr; - uint* mPlabelDataOffset = nullptr; + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> const* mPinputLabels = nullptr; + uint* mPlabelsInRow = nullptr; + uint mPlabelsHeaderGlobalOffset = 0; + uint mPlabelsDataGlobalOffset = 0; int mISlice = 0; constexpr static int mScanWorkGroupSize = GPUCA_THREAD_COUNT_SCAN; @@ -123,10 +133,13 @@ class GPUTPCClusterFinder : public GPUProcessor unsigned int mNBufs = 0; short mMemoryId = -1; + short mScratchId = -1; short mZSId = -1; short mZSOffsetId = -1; short mOutputId = -1; + GPUdi() const GPUTPCGeometry* getGeometry() const; + #ifndef GPUCA_GPUCODE void DumpDigits(std::ostream& out); void DumpChargeMap(std::ostream& out, std::string_view); @@ -139,7 +152,6 @@ class GPUTPCClusterFinder : public GPUProcessor #endif }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderKernels.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderKernels.h index e2298ad4e683a..2f0e5fe4166bb 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderKernels.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderKernels.h @@ -22,6 +22,7 @@ #include "GPUTPCCFStreamCompaction.h" #include "GPUTPCCFClusterizer.h" #include "GPUTPCCFMCLabelFlattener.h" +#include "GPUTPCCFCheckPadBaseline.h" #include "GPUTPCCFDecodeZS.h" #include "GPUTPCCFGather.h" diff --git a/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.cxx b/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.cxx index 3f5afeb0d3d20..96300ad3883a3 100644 --- a/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.cxx @@ -23,9 +23,6 @@ using namespace GPUCA_NAMESPACE::gpu::tpccf; MCLabelAccumulator::MCLabelAccumulator(GPUTPCClusterFinder& clusterer) : mIndexMap(clusterer.mPindexMap), mLabels(clusterer.mPinputLabels), mOutput(clusterer.mPlabelsByRow) { - if (engaged()) { - mClusterLabels.reserve(32); - } } void MCLabelAccumulator::collect(const ChargePos& pos, Charge q) @@ -36,7 +33,7 @@ void MCLabelAccumulator::collect(const ChargePos& pos, Charge q) uint index = mIndexMap[pos]; - auto labels = mLabels->getLabels(index); + const auto& labels = mLabels->getLabels(index); for (const auto& label : labels) { int h = label.getRawValue() % mMaybeHasLabel.size(); diff --git a/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.h b/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.h index 641721ce9d999..f839402ba310b 100644 --- a/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.h +++ b/GPU/GPUTracking/TPCClusterFinder/MCLabelAccumulator.h @@ -16,19 +16,22 @@ #include "clusterFinderDefs.h" #include "Array2D.h" -#include "SimulationDataFormat/MCCompLabel.h" #include <bitset> #include <vector> -namespace GPUCA_NAMESPACE +namespace o2 { +class MCCompLabel; namespace dataformats { +class MCCompLabel; template <typename T> -class MCTruthContainer; -} +class ConstMCTruthContainerView; +using ConstMCLabelContainerView = o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel>; +} // namespace dataformats +} // namespace o2 -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class GPUTPCClusterFinder; @@ -47,17 +50,14 @@ class MCLabelAccumulator void commit(tpccf::Row, uint, uint); private: - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; - Array2D<const uint> mIndexMap; - const MCLabelContainer* mLabels = nullptr; + const o2::dataformats::ConstMCLabelContainerView* mLabels = nullptr; GPUTPCClusterMCInterim* mOutput = nullptr; std::bitset<64> mMaybeHasLabel; std::vector<o2::MCCompLabel> mClusterLabels; }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/PackedCharge.h b/GPU/GPUTracking/TPCClusterFinder/PackedCharge.h index 64d8e71ba5816..e610508b1864b 100644 --- a/GPU/GPUTracking/TPCClusterFinder/PackedCharge.h +++ b/GPU/GPUTracking/TPCClusterFinder/PackedCharge.h @@ -17,9 +17,7 @@ #include "clusterFinderDefs.h" #include "GPUCommonMath.h" -namespace GPUCA_NAMESPACE -{ -namespace gpu +namespace GPUCA_NAMESPACE::gpu { class PackedCharge @@ -57,7 +55,6 @@ class PackedCharge BasicType mVal; }; -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/GPUTracking/TPCClusterFinder/TPCPadGainCalib.cxx b/GPU/GPUTracking/TPCClusterFinder/TPCPadGainCalib.cxx new file mode 100644 index 0000000000000..a2cad4760ada0 --- /dev/null +++ b/GPU/GPUTracking/TPCClusterFinder/TPCPadGainCalib.cxx @@ -0,0 +1,39 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TPCPadGainCalib.cxx +/// \author Felix Weiglhofer + +#include "TPCPadGainCalib.h" + +#include "GPUTPCGeometry.h" +#include "DataFormatsTPC/Constants.h" +#include "TPCBase/CalDet.h" + +using namespace GPUCA_NAMESPACE::gpu; + +TPCPadGainCalib::TPCPadGainCalib() +{ + GPUTPCGeometry geo{}; + int offset = 0; + for (int r = 0; r < TPC_NUM_OF_ROWS; r++) { + mPadOffsetPerRow[r] = offset; + offset += geo.NPads(r); + } +} + +TPCPadGainCalib::TPCPadGainCalib(const o2::tpc::CalDet<float>& gainMap) : TPCPadGainCalib() +{ + for (int sector = 0; sector < o2::tpc::constants::MAXSECTOR; sector++) { + for (int p = 0; p < TPC_PADS_IN_SECTOR; p++) { + mGainCorrection[sector].set(p, gainMap.getValue(sector, p)); + } + } +} diff --git a/GPU/GPUTracking/TPCClusterFinder/TPCPadGainCalib.h b/GPU/GPUTracking/TPCClusterFinder/TPCPadGainCalib.h new file mode 100644 index 0000000000000..cf26ef5e0168e --- /dev/null +++ b/GPU/GPUTracking/TPCClusterFinder/TPCPadGainCalib.h @@ -0,0 +1,133 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 TPCPadGainCalib.h +/// \author Felix Weiglhofer + +#ifndef O2_GPU_TPC_PAD_GAIN_CALIB_H +#define O2_GPU_TPC_PAD_GAIN_CALIB_H + +#include "clusterFinderDefs.h" +#include "GPUCommonMath.h" + +namespace o2::tpc +{ +template <class T> +class CalDet; +} // namespace o2::tpc + +namespace GPUCA_NAMESPACE::gpu +{ + +template <typename T> +struct TPCPadGainCorrectionStepNum { +}; + +template <> +struct TPCPadGainCorrectionStepNum<unsigned char> { + static constexpr int value = 254; +}; + +template <> +struct TPCPadGainCorrectionStepNum<unsigned short> { + static constexpr int value = 65534; +}; + +struct TPCPadGainCalib { + public: +#ifndef GPUCA_GPUCODE + TPCPadGainCalib(); + TPCPadGainCalib(const o2::tpc::CalDet<float>&); +#endif + + // Deal with pad gain correction from here on + GPUdi() void setGainCorrection(int sector, tpccf::Row row, tpccf::Pad pad, float c) + { + mGainCorrection[sector].set(globalPad(row, pad), c); + } + + GPUdi() float getGainCorrection(int sector, tpccf::Row row, tpccf::Pad pad) const + { + return mGainCorrection[sector].get(globalPad(row, pad)); + } + + GPUdi() unsigned short globalPad(tpccf::Row row, tpccf::Pad pad) const + { + return mPadOffsetPerRow[row] + pad; + } + + private: + template <typename T = unsigned short> + class SectorPadGainCorrection + { + + public: + constexpr static float MinCorrectionFactor = 0.f; + constexpr static float MaxCorrectionFactor = 2.f; + constexpr static int NumOfSteps = TPCPadGainCorrectionStepNum<T>::value; + + GPUdi() SectorPadGainCorrection() + { + reset(); + } + + GPUdi() void set(unsigned short globalPad, float c) + { + at(globalPad) = pack(c); + } + + GPUdi() float get(unsigned short globalPad) const + { + return unpack(at(globalPad)); + } + + GPUd() void reset() + { + for (unsigned short p = 0; p < TPC_PADS_IN_SECTOR; p++) { + set(p, 1.0f); + } + } + + private: + GPUd() static T pack(float f) + { + f = CAMath::Clamp(f, MinCorrectionFactor, MaxCorrectionFactor); + f -= MinCorrectionFactor; + f *= float(NumOfSteps); + f /= (MaxCorrectionFactor - MinCorrectionFactor); + return CAMath::Nint(f); + } + + GPUd() static float unpack(T c) + { + return MinCorrectionFactor + (MaxCorrectionFactor - MinCorrectionFactor) * float(c) / float(NumOfSteps); + } + + T mGainCorrection[TPC_PADS_IN_SECTOR]; + + GPUdi() T& at(unsigned short globalPad) + { + return mGainCorrection[globalPad]; + } + + GPUdi() const T& at(unsigned short globalPad) const + { + return mGainCorrection[globalPad]; + } + }; + + unsigned short mPadOffsetPerRow[TPC_NUM_OF_ROWS]; + SectorPadGainCorrection<unsigned short> mGainCorrection[TPC_SECTORS]; + +}; + +} // namespace GPUCA_NAMESPACE::gpu + +#endif diff --git a/GPU/GPUTracking/TPCClusterFinder/clusterFinderDefs.h b/GPU/GPUTracking/TPCClusterFinder/clusterFinderDefs.h index 6c3a78128c296..8ace539cd7e8c 100644 --- a/GPU/GPUTracking/TPCClusterFinder/clusterFinderDefs.h +++ b/GPU/GPUTracking/TPCClusterFinder/clusterFinderDefs.h @@ -23,9 +23,6 @@ using uchar = unsigned char; using ulong = unsigned long; #endif -#define QMAX_CUTOFF 3 -#define QTOT_CUTOFF 0 -#define NOISE_SUPPRESSION_MINIMA_EPSILON 10 #define SCRATCH_PAD_WORK_GROUP_SIZE GPUCA_GET_THREAD_COUNT(GPUCA_LB_CLUSTER_FINDER) /* #define CHARGEMAP_TIME_MAJOR_LAYOUT */ @@ -50,6 +47,7 @@ using ulong = unsigned long; #define TPC_PADS_PER_ROW 138 #define TPC_PADS_PER_ROW_PADDED (TPC_PADS_PER_ROW + PADDING_PAD) #define TPC_NUM_OF_PADS (TPC_NUM_OF_ROWS * TPC_PADS_PER_ROW_PADDED + PADDING_PAD) +#define TPC_PADS_IN_SECTOR 14560 #define TPC_MAX_FRAGMENT_LEN 4000 #define TPC_MAX_FRAGMENT_LEN_PADDED (TPC_MAX_FRAGMENT_LEN + 2 * PADDING_TIME) @@ -67,11 +65,7 @@ using ulong = unsigned long; #define CPU_PTR(x) x #endif -namespace GPUCA_NAMESPACE -{ -namespace gpu -{ -namespace tpccf +namespace GPUCA_NAMESPACE::gpu::tpccf { using SizeT = size_t; @@ -89,13 +83,6 @@ using Delta2 = short2; using local_id = short2; -GPUconstexpr() float CHARGE_THRESHOLD = 0.f; -GPUconstexpr() float OUTER_CHARGE_THRESHOLD = 0.f; -GPUconstexpr() float QTOT_THRESHOLD = 500.f; -GPUconstexpr() int MIN_SPLIT_NUM = 1; - -} // namespace tpccf -} // namespace gpu -} // namespace GPUCA_NAMESPACE +} // namespace GPUCA_NAMESPACE::gpu::tpccf #endif diff --git a/GPU/GPUTracking/TPCConvert/GPUTPCConvert.cxx b/GPU/GPUTracking/TPCConvert/GPUTPCConvert.cxx index 9247ed1dcc56b..47edc6bd72727 100644 --- a/GPU/GPUTracking/TPCConvert/GPUTPCConvert.cxx +++ b/GPU/GPUTracking/TPCConvert/GPUTPCConvert.cxx @@ -23,7 +23,7 @@ void GPUTPCConvert::InitializeProcessor() {} void* GPUTPCConvert::SetPointersOutput(void* mem) { - if (mRec->GetParam().earlyTpcTransform) { + if (mRec->GetParam().par.earlyTpcTransform) { computePointerWithAlignment(mem, mClusters, mNClustersTotal); } return mem; diff --git a/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h b/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h index 654d4a00a61fb..3bd58e336ca50 100644 --- a/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h +++ b/GPU/GPUTracking/TPCConvert/GPUTPCConvertImpl.h @@ -28,8 +28,8 @@ class GPUTPCConvertImpl public: GPUd() static void convert(const GPUConstantMem& GPUrestrict() cm, int slice, int row, float pad, float time, float& GPUrestrict() x, float& GPUrestrict() y, float& GPUrestrict() z) { - if (cm.param.ContinuousTracking) { - cm.calibObjects.fastTransform->TransformInTimeFrame(slice, row, pad, time, x, y, z, cm.param.continuousMaxTimeBin); + if (cm.param.par.ContinuousTracking) { + cm.calibObjects.fastTransform->TransformInTimeFrame(slice, row, pad, time, x, y, z, cm.param.par.continuousMaxTimeBin); } else { cm.calibObjects.fastTransform->Transform(slice, row, pad, time, x, y, z); } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDDef.h b/GPU/GPUTracking/TRDTracking/GPUTRDDef.h index ff03fc8af18ca..54f2f7110e581 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDDef.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDDef.h @@ -52,9 +52,8 @@ typedef float My_Float; #if defined(TRD_TRACK_TYPE_ALIROOT) typedef AliExternalTrackParam TRDBaseTrack; -typedef AliExternalTrackParam TRDBaseTrackGPU; -// class GPUTPCGMTrackParam; -// typedef GPUTPCGMTrackParam TRDBaseTrack; +class GPUTPCGMTrackParam; +typedef GPUTPCGMTrackParam TRDBaseTrackGPU; #elif defined(TRD_TRACK_TYPE_O2) typedef o2::dataformats::TrackTPCITS TRDBaseTrack; class GPUTPCGMTrackParam; @@ -63,9 +62,8 @@ typedef GPUTPCGMTrackParam TRDBaseTrackGPU; #ifdef GPUCA_ALIROOT_LIB typedef AliTrackerBase TRDBasePropagator; -typedef AliTrackerBase TRDBasePropagatorGPU; -// class GPUTPCGMPropagator; -// typedef GPUTPCGMPropagator TRDBasePropagator; +class GPUTPCGMPropagator; +typedef GPUTPCGMPropagator TRDBasePropagatorGPU; #else typedef o2::base::Propagator TRDBasePropagator; class GPUTPCGMPropagator; diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDGeometry.h b/GPU/GPUTracking/TRDTracking/GPUTRDGeometry.h index d31f146499321..92dd33af49a02 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDGeometry.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDGeometry.h @@ -47,8 +47,9 @@ class GPUTRDGeometry : public AliTRDgeometry class TObjArray; #include "GPUDef.h" -#include "TRDBase/TRDGeometryFlat.h" -#include "TRDBase/TRDPadPlane.h" +#include "TRDBase/GeometryFlat.h" +#include "TRDBase/PadPlane.h" +#include "DataFormatsTRD/Constants.h" #include "GPUCommonTransform3D.h" namespace GPUCA_NAMESPACE @@ -56,20 +57,23 @@ namespace GPUCA_NAMESPACE namespace gpu { -class GPUTRDpadPlane : private o2::trd::TRDPadPlane +class GPUTRDpadPlane : private o2::trd::PadPlane { public: GPUd() float GetTiltingAngle() const { return getTiltingAngle(); } GPUd() float GetRowSize(int row) const { return getRowSize(row); } + GPUd() float GetColSize(int col) const { return getColSize(col); } GPUd() float GetRow0() const { return getRow0(); } + GPUd() float GetCol0() const { return getCol0(); } GPUd() float GetRowEnd() const { return getRowEnd(); } GPUd() float GetColEnd() const { return getColEnd(); } GPUd() float GetRowPos(int row) const { return getRowPos(row); } GPUd() float GetColPos(int col) const { return getColPos(col); } GPUd() float GetNrows() const { return getNrows(); } + GPUd() float GetNcols() const { return getNcols(); } }; -class GPUTRDGeometry : private o2::trd::TRDGeometryFlat +class GPUTRDGeometry : private o2::trd::GeometryFlat { public: GPUd() static bool CheckGeometryAvailable() { return true; } @@ -80,7 +84,7 @@ class GPUTRDGeometry : private o2::trd::TRDGeometryFlat GPUd() float GetPadPlaneRowSize(int layer, int stack, int row) const { return getPadPlane(layer, stack)->getRowSize(row); } GPUd() int GetGeomManagerVolUID(int det, int modId) const { return 0; } - // Base functionality of TRDGeometry + // Base functionality of Geometry GPUd() float GetTime0(int layer) const { return getTime0(layer); } GPUd() float GetCol0(int layer) const { return getCol0(layer); } GPUd() int GetLayer(int det) const { return getLayer(det); } @@ -98,7 +102,7 @@ class GPUTRDGeometry : private o2::trd::TRDGeometryFlat GPUd() int GetRowMax(int layer, int stack, int sector) const { return getRowMax(layer, stack, sector); } GPUd() bool ChamberInGeometry(int det) const { return chamberInGeometry(det); } - static constexpr int kNstack = o2::trd::kNstack; + static constexpr int kNstack = o2::trd::constants::NSTACK; }; } // namespace gpu } // namespace GPUCA_NAMESPACE @@ -147,7 +151,7 @@ class GPUTRDGeometry GPUd() float GetPadPlaneRowSize(int layer, int stack, int row) const { return 0; } GPUd() int GetGeomManagerVolUID(int det, int modId) const { return 0; } - // Base functionality of TRDGeometry + // Base functionality of Geometry GPUd() float GetTime0(int layer) const { return 0; } GPUd() float GetCol0(int layer) const { return 0; } GPUd() int GetLayer(int det) const { return 0; } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h b/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h index e7800219b3e87..f6d71d93c60f6 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h @@ -80,6 +80,7 @@ class trackInterface<AliExternalTrackParam> : public AliExternalTrackParam const My_Float* getPar() const { return GetParameter(); } const My_Float* getCov() const { return GetCovariance(); } + float getTime() const { return -1.f; } bool CheckNumericalQuality() const { return true; } // parameter manipulation @@ -102,7 +103,7 @@ class propagatorInterface<AliTrackerBase> : public AliTrackerBase propagatorInterface<AliTrackerBase>& operator=(const propagatorInterface<AliTrackerBase>&) CON_DELETE; bool propagateToX(float x, float maxSnp, float maxStep) { return PropagateTrackToBxByBz(mParam, x, 0.13957, maxStep, false, maxSnp); } - int getPropagatedYZ(My_Float x, My_Float& projY, My_Float& projZ) + int getPropagatedYZ(float x, float& projY, float& projZ) { Double_t yz[2] = {0.}; mParam->GetYZAt(x, GetBz(), yz); @@ -179,11 +180,16 @@ class trackInterface<o2::dataformats::TrackTPCITS> : public o2::dataformats::Tra } } - const float* getPar() { return getParams(); } + const float* getPar() const { return getParams(); } + float getTime() const { return mTime; } + void setTime(float t) { mTime = t; } bool CheckNumericalQuality() const { return true; } typedef o2::dataformats::TrackTPCITS baseClass; + + private: + float mTime; }; template <> @@ -194,8 +200,8 @@ class propagatorInterface<o2::base::Propagator> propagatorInterface<o2::base::Propagator>(const propagatorInterface<o2::base::Propagator>&) = delete; propagatorInterface<o2::base::Propagator>& operator=(const propagatorInterface<o2::base::Propagator>&) = delete; - bool propagateToX(float x, float maxSnp, float maxStep) { return mProp->PropagateToXBxByBz(*mParam, x, 0.13957, maxSnp, maxStep); } - int getPropagatedYZ(My_Float x, My_Float& projY, My_Float& projZ) { return static_cast<int>(mParam->getYZAt(x, mProp->getNominalBz(), projY, projZ)); } + bool propagateToX(float x, float maxSnp, float maxStep) { return mProp->PropagateToXBxByBz(*mParam, x, maxSnp, maxStep); } + int getPropagatedYZ(float x, float& projY, float& projZ) { return static_cast<int>(mParam->getYZAt(x, mProp->getNominalBz(), projY, projZ)); } void setTrack(trackInterface<o2::dataformats::TrackTPCITS>* trk) { mParam = trk; } void setFitInProjections(bool flag) {} @@ -294,6 +300,7 @@ class trackInterface<GPUTPCGMTrackParam> : public GPUTPCGMTrackParam GPUd() const float* getPar() const { return GetPar(); } GPUd() const float* getCov() const { return GetCov(); } + GPUd() float getTime() const { return -1.f; } GPUd() void setAlpha(float alpha) { mAlpha = alpha; } GPUd() void set(float x, float alpha, const float param[5], const float cov[15]) @@ -338,7 +345,6 @@ class propagatorInterface<GPUTPCGMPropagator> : public GPUTPCGMPropagator { //bool ok = PropagateToXAlpha(x, GetAlpha(), true) == 0 ? true : false; int retVal = PropagateToXAlpha(x, GetAlpha(), true); - printf("Return value of PropagateToXAlpha: %i\n", retVal); bool ok = (retVal == 0) ? true : false; ok = mTrack->CheckNumericalQuality(); return ok; diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx index f11316502faea..49dbfb3390e8c 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx @@ -22,7 +22,6 @@ #include "GPUTRDTrackletWord.h" #include "GPUTRDGeometry.h" #include "GPUTRDTrackerDebug.h" -#include "GPUReconstruction.h" #include "GPUMemoryResource.h" #include "GPUCommonMath.h" #include "GPUCommonAlgorithm.h" @@ -33,7 +32,8 @@ class GPUTPCGMPolynomialField; #ifndef GPUCA_GPUCODE -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE +#include "GPUReconstruction.h" #ifdef WITH_OPENMP #include <omp.h> #endif @@ -44,9 +44,9 @@ class GPUTPCGMPolynomialField; #include "TDatabasePDG.h" #include "AliMCParticle.h" #include "AliMCEvent.h" -static const float piMass = TDatabasePDG::Instance()->GetParticle(211)->Mass(); +//static const float piMass = TDatabasePDG::Instance()->GetParticle(211)->Mass(); #else -static const float piMass = 0.139f; +//static const float piMass = 0.139f; #endif #include "GPUChainTracking.h" @@ -75,7 +75,7 @@ void* GPUTRDTracker_t<TRDTRK, PROP>::SetPointersBase(void* base) //-------------------------------------------------------------------- mMaxThreads = mRec->GetMaxThreads(); computePointerWithAlignment(base, mR, kNChambers); - computePointerWithAlignment(base, mTrackletIndexArray, kNChambers + 1); + computePointerWithAlignment(base, mTrackletIndexArray, (kNChambers + 1) * mNMaxCollisions); computePointerWithAlignment(base, mHypothesis, mNCandidates * mMaxThreads); computePointerWithAlignment(base, mCandidates, mNCandidates * 2 * mMaxThreads); return base; @@ -88,9 +88,9 @@ void* GPUTRDTracker_t<TRDTRK, PROP>::SetPointersTracklets(void* base) // Allocate memory for tracklets and space points // (size might change for different events) //-------------------------------------------------------------------- - computePointerWithAlignment(base, mTracklets, mNMaxSpacePoints); - computePointerWithAlignment(base, mSpacePoints, mNMaxSpacePoints); - computePointerWithAlignment(base, mTrackletLabels, 3 * mNMaxSpacePoints); + computePointerWithAlignment(base, mTracklets, mNMaxSpacePoints * mNMaxCollisions); + computePointerWithAlignment(base, mSpacePoints, mNMaxSpacePoints * mNMaxCollisions); + computePointerWithAlignment(base, mTrackletLabels, 3 * mNMaxSpacePoints * mNMaxCollisions); return base; } @@ -105,7 +105,7 @@ void* GPUTRDTracker_t<TRDTRK, PROP>::SetPointersTracks(void* base) } template <class TRDTRK, class PROP> -GPUTRDTracker_t<TRDTRK, PROP>::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mTracklets(nullptr), mMaxThreads(100), mNTracklets(0), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mTrackletLabels(nullptr), mGeo(nullptr), mRPhiA2(0), mRPhiB(0), mRPhiC2(0), mDyA2(0), mDyB(0), mDyC2(0), mAngleToDyA(0), mAngleToDyB(0), mAngleToDyC(0), mDebugOutput(false), mRadialOffset(-0.1), mMinPt(2.f), mMaxEta(0.84f), mExtraRoadY(2.f), mRoadZ(18.f), mMaxChi2(15.0f), mMaxMissingLy(6), mChi2Penalty(12.0f), mZCorrCoefNRC(1.4f), mMCEvent(nullptr), mDebug(new GPUTRDTrackerDebug<TRDTRK>()) +GPUTRDTracker_t<TRDTRK, PROP>::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mProcessPerTimeFrame(false), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(1), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mNCandidates(1), mNCollisions(1), mNTracks(0), mNEvents(0), mTriggerRecordIndices(nullptr), mTriggerRecordTimes(nullptr), mTracklets(nullptr), mMaxThreads(100), mNTracklets(0), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mTrackletLabels(nullptr), mGeo(nullptr), mRPhiA2(0), mRPhiB(0), mRPhiC2(0), mDyA2(0), mDyB(0), mDyC2(0), mAngleToDyA(0), mAngleToDyB(0), mAngleToDyC(0), mDebugOutput(false), mTimeWindow(.1f), mRadialOffset(-0.1), mMaxEta(0.84f), mExtraRoadY(2.f), mRoadZ(18.f), mZCorrCoefNRC(1.4f), mMCEvent(nullptr), mDebug(new GPUTRDTrackerDebug<TRDTRK>()) { //-------------------------------------------------------------------- // Default constructor @@ -132,7 +132,7 @@ void GPUTRDTracker_t<TRDTRK, PROP>::InitializeProcessor() Error("Init", "TRD geometry must be provided externally"); } - float Bz = Param().BzkG; + float Bz = Param().par.BzkG; GPUInfo("Initializing with B-field: %f kG", Bz); if (abs(abs(Bz) - 2) < 0.1) { // magnetic field +-0.2 T @@ -191,31 +191,13 @@ void GPUTRDTracker_t<TRDTRK, PROP>::InitializeProcessor() } template <class TRDTRK, class PROP> -void GPUTRDTracker_t<TRDTRK, PROP>::Reset(bool fast) +void GPUTRDTracker_t<TRDTRK, PROP>::Reset() { //-------------------------------------------------------------------- // Reset tracker //-------------------------------------------------------------------- mNTracklets = 0; mNTracks = 0; - if (fast) { - return; - } - for (int i = 0; i < mNMaxSpacePoints; ++i) { - mTracklets[i] = 0x0; - mSpacePoints[i].mR = 0.f; - mSpacePoints[i].mX[0] = 0.f; - mSpacePoints[i].mX[1] = 0.f; - mSpacePoints[i].mCov[0] = 0.f; - mSpacePoints[i].mCov[1] = 0.f; - mSpacePoints[i].mCov[2] = 0.f; - mSpacePoints[i].mDy = 0.f; - mSpacePoints[i].mId = 0; - mSpacePoints[i].mLabel[0] = -1; - mSpacePoints[i].mLabel[1] = -1; - mSpacePoints[i].mLabel[2] = -1; - mSpacePoints[i].mVolumeId = 0; - } } template <class TRDTRK, class PROP> @@ -226,28 +208,39 @@ void GPUTRDTracker_t<TRDTRK, PROP>::DoTracking(GPUChainTracking* chainTracking) //-------------------------------------------------------------------- // sort tracklets and fill index array - CAAlgo::sort(mTracklets, mTracklets + mNTracklets); // tracklets are sorted by HCId - int* trkltIndexArray = &mTrackletIndexArray[1]; - trkltIndexArray[-1] = 0; - int currDet = 0; - int nextDet = 0; - int trkltCounter = 0; - for (int iTrklt = 0; iTrklt < mNTracklets; ++iTrklt) { - if (mTracklets[iTrklt].GetDetector() > currDet) { - nextDet = mTracklets[iTrklt].GetDetector(); - for (int iDet = currDet; iDet < nextDet; ++iDet) { - trkltIndexArray[iDet] = trkltCounter; + for (int iColl = 0; iColl < mNCollisions; ++iColl) { + int nTrklts = 0; + if (mProcessPerTimeFrame) { + // FIXME maybe two nested if statements are not so good in terms of performance? + nTrklts = (iColl < mNCollisions - 1) ? mTriggerRecordIndices[iColl + 1] - mTriggerRecordIndices[iColl] : mNTracklets - mTriggerRecordIndices[iColl]; + } else { + nTrklts = mNTracklets; + } + GPUTRDTrackletWord* tracklets = (mProcessPerTimeFrame) ? &(mTracklets[mTriggerRecordIndices[iColl]]) : mTracklets; + CAAlgo::sort(tracklets, tracklets + nTrklts); // tracklets are sorted by HCId + int* trkltIndexArray = &mTrackletIndexArray[iColl * (kNChambers + 1) + 1]; + trkltIndexArray[-1] = 0; + int currDet = 0; + int nextDet = 0; + int trkltCounter = 0; + for (int iTrklt = 0; iTrklt < nTrklts; ++iTrklt) { + if (tracklets[iTrklt].GetDetector() > currDet) { + nextDet = tracklets[iTrklt].GetDetector(); + for (int iDet = currDet; iDet < nextDet; ++iDet) { + trkltIndexArray[iDet] = trkltCounter; + } + currDet = nextDet; } - currDet = nextDet; + ++trkltCounter; + } + for (int iDet = currDet; iDet <= kNChambers; ++iDet) { + trkltIndexArray[iDet] = trkltCounter; } - ++trkltCounter; - } - for (int iDet = currDet; iDet <= kNChambers; ++iDet) { - trkltIndexArray[iDet] = trkltCounter; - } - if (!CalculateSpacePoints()) { - Error("DoTracking", "Space points for at least one chamber could not be calculated"); + if (!CalculateSpacePoints(iColl)) { + GPUError("Space points for at least one chamber could not be calculated (for interaction %i)", iColl); + break; + } } auto timeStart = std::chrono::high_resolution_clock::now(); @@ -259,7 +252,7 @@ void GPUTRDTracker_t<TRDTRK, PROP>::DoTracking(GPUChainTracking* chainTracking) #pragma omp parallel for for (int iTrk = 0; iTrk < mNTracks; ++iTrk) { if (omp_get_num_threads() > mMaxThreads) { - Error("DoTracking", "number of parallel threads too high, aborting tracking"); + GPUError("Number of parallel threads too high, aborting tracking"); // break statement not possible in OpenMP for loop iTrk = mNTracks; continue; @@ -274,6 +267,7 @@ void GPUTRDTracker_t<TRDTRK, PROP>::DoTracking(GPUChainTracking* chainTracking) } auto duration = std::chrono::high_resolution_clock::now() - timeStart; + (void)duration; // suppress warning about unused variable /* std::cout << "---> -----> -------> ---------> "; std::cout << "Time for event " << mNEvents << ": " << std::chrono::duration_cast<std::chrono::microseconds>(duration).count() << " us "; @@ -306,8 +300,8 @@ void GPUTRDTracker_t<TRDTRK, PROP>::PrintSettings() const //-------------------------------------------------------------------- GPUInfo("##############################################################"); GPUInfo("Current settings for GPU TRD tracker:"); - GPUInfo(" mMaxChi2(%.2f), mChi2Penalty(%.2f), nCandidates(%i), maxMissingLayers(%i)", mMaxChi2, mChi2Penalty, mNCandidates, mMaxMissingLy); - GPUInfo(" ptCut = %.2f GeV, abs(eta) < %.2f", mMinPt, mMaxEta); + GPUInfo(" maxChi2(%.2f), chi2Penalty(%.2f), nCandidates(%i), maxMissingLayers(%i)", Param().rec.trdMaxChi2, Param().rec.trdPenaltyChi2, mNCandidates, Param().rec.trdStopTrkAfterNMissLy); + GPUInfo(" ptCut = %.2f GeV, abs(eta) < %.2f", Param().rec.trdMinTrackPt, mMaxEta); GPUInfo("##############################################################"); } @@ -412,8 +406,7 @@ GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::CheckTrackRefs(const int trackID, boo layer = 5; } if (layer < 0) { - Error("CheckTrackRefs", "No layer can be determined"); - GPUError("x=%f, y=%f, z=%f, layer=%i", xLoc, trackReference->LocalY(), trackReference->Z(), layer); + GPUError("No layer can be determined for x=%f, y=%f, z=%f, layer=%i", xLoc, trackReference->LocalY(), trackReference->Z(), layer); continue; } if (trackReference->TestBits(0x1 << 18)) { @@ -434,13 +427,59 @@ GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::CheckTrackRefs(const int trackID, boo } #endif //! GPUCA_GPUCODE +template <class TRDTRK, class PROP> +GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CheckTrackTRDCandidate(const TRDTRK& trk) const +{ + if (!trk.CheckNumericalQuality()) { + return false; + } + if (CAMath::Abs(trk.getEta()) > mMaxEta) { + return false; + } + if (trk.getPt() < Param().rec.trdMinTrackPt) { + return false; + } + return true; +} + +template <class TRDTRK, class PROP> +GPUd() int GPUTRDTracker_t<TRDTRK, PROP>::LoadTrack(const TRDTRK& trk, const int label, const int* nTrkltsOffline, const int labelOffline, int tpcTrackId, bool checkTrack) +{ + if (mNTracks >= mNMaxTracks) { +#ifndef GPUCA_GPUCODE + GPUError("Error: Track dropped (no memory available) -> must not happen"); +#endif + return (1); + } + if (checkTrack && !CheckTrackTRDCandidate(trk)) { + return 2; + } +#ifdef GPUCA_ALIROOT_LIB + new (&mTracks[mNTracks]) TRDTRK(trk); // We need placement new, since the class is virtual +#else + mTracks[mNTracks] = trk; +#endif + mTracks[mNTracks].SetTPCtrackId(tpcTrackId >= 0 ? tpcTrackId : mNTracks); + if (label >= 0) { + mTracks[mNTracks].SetLabel(label); + } + if (nTrkltsOffline) { + for (int i = 0; i < 4; ++i) { + mTracks[mNTracks].SetNtrackletsOffline(i, nTrkltsOffline[i]); // see GPUTRDTrack.h for information on the index + } + } + mTracks[mNTracks].SetLabelOffline(labelOffline); + mNTracks++; + return (0); +} + template <class TRDTRK, class PROP> GPUd() int GPUTRDTracker_t<TRDTRK, PROP>::LoadTracklet(const GPUTRDTrackletWord& tracklet, const int* labels) { //-------------------------------------------------------------------- // Add single tracklet to tracker //-------------------------------------------------------------------- - if (mNTracklets >= mNMaxSpacePoints) { + if (mNTracklets >= mNMaxSpacePoints * mNMaxCollisions) { Error("LoadTracklet", "Running out of memory for tracklets, skipping tracklet(s). This should actually never happen."); return 1; } @@ -466,22 +505,47 @@ GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::DumpTracks() } } +template <class TRDTRK, class PROP> +GPUd() int GPUTRDTracker_t<TRDTRK, PROP>::GetCollisionID(float trkTime) const +{ + for (int iColl = 0; iColl < mNCollisions; ++iColl) { + if (CAMath::Abs(trkTime - mTriggerRecordTimes[iColl]) < mTimeWindow) { + if (ENABLE_INFO) { + GPUInfo("TRD info found from interaction %i at %f for track with time %f", iColl, mTriggerRecordTimes[iColl], trkTime); + } + return iColl; + } + } + return -1; +} + template <class TRDTRK, class PROP> GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::DoTrackingThread(int iTrk, int threadId) { //-------------------------------------------------------------------- // perform the tracking for one track (must be threadsafe) //-------------------------------------------------------------------- + int collisionId = 0; + if (mProcessPerTimeFrame) { + collisionId = GetCollisionID(mTracks[iTrk].getTime()); + if (collisionId < 0) { + if (ENABLE_INFO) { + GPUInfo("Did not find TRD data for track with t=%f", mTracks[iTrk].getTime()); + } + // no TRD data available for the bunch crossing this track originates from + return; + } + } PROP prop(&Param().polynomialField); auto trkCopy = mTracks[iTrk]; prop.setTrack(&trkCopy); prop.setFitInProjections(true); - FollowProlongation(&prop, &trkCopy, threadId); + FollowProlongation(&prop, &trkCopy, threadId, collisionId); mTracks[iTrk] = trkCopy; // copy back the resulting track } template <class TRDTRK, class PROP> -GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CalculateSpacePoints() +GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CalculateSpacePoints(int iCollision) { //-------------------------------------------------------------------- // Calculates TRD space points in sector tracking coordinates @@ -489,17 +553,20 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CalculateSpacePoints() //-------------------------------------------------------------------- bool result = true; + int idxOffset = iCollision * (kNChambers + 1); for (int iDet = 0; iDet < kNChambers; ++iDet) { - - int nTracklets = mTrackletIndexArray[iDet + 1] - mTrackletIndexArray[iDet]; + int nTracklets = mTrackletIndexArray[idxOffset + iDet + 1] - mTrackletIndexArray[idxOffset + iDet]; if (nTracklets == 0) { continue; } - + if (!mGeo->ChamberInGeometry(iDet)) { + GPUError("Found TRD tracklets in chamber %i which is not included in the geometry", iDet); + return false; + } auto* matrix = mGeo->GetClusterMatrix(iDet); if (!matrix) { - Error("CalculateSpacePoints", "Invalid TRD cluster matrix, skipping detector %i", iDet); + GPUError("No cluster matrix available for chamber %i. Skipping it...", iDet); result = false; continue; } @@ -509,7 +576,7 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CalculateSpacePoints() float c2 = 1.f / (1.f + t2); // cos^2 (tilt) float sy2 = 0.1f * 0.1f; // sigma_rphi^2, currently assume sigma_rphi = 1 mm - for (int trkltIdx = mTrackletIndexArray[iDet]; trkltIdx < mTrackletIndexArray[iDet + 1]; ++trkltIdx) { + for (int trkltIdx = mTrackletIndexArray[idxOffset + iDet]; trkltIdx < mTrackletIndexArray[idxOffset + iDet + 1]; ++trkltIdx) { int trkltZbin = mTracklets[trkltIdx].GetZbin(); float sz2 = pp->GetRowSize(trkltZbin) * pp->GetRowSize(trkltZbin) / 12.f; // sigma_z = l_pad/sqrt(12) TODO try a larger z error My_Float xTrkltDet[3] = {0.f}; // trklt position in chamber coordinates @@ -541,7 +608,7 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CalculateSpacePoints() } template <class TRDTRK, class PROP> -GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK* t, int threadId) +GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK* t, int threadId, int collisionId) { //-------------------------------------------------------------------- // Propagate TPC track layerwise through TRD and pick up closest @@ -574,6 +641,7 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK int candidateIdxOffset = threadId * 2 * mNCandidates; int hypothesisIdxOffset = threadId * mNCandidates; + int trkltIdxOffset = collisionId * (kNChambers + 1); auto trkWork = t; if (mNCandidates > 1) { @@ -625,16 +693,14 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK if (ENABLE_INFO) { GPUInfo("Track propagation failed for track %i candidate %i in layer %i (pt=%f, x=%f, mR[layer]=%f)", iTrack, iCandidate, iLayer, trkWork->getPt(), trkWork->getX(), mR[2 * kNLayers + iLayer]); } - GPUInfo("Propagation failed"); continue; } // rotate track in new sector in case of sector crossing - if (!AdjustSector(prop, trkWork, iLayer)) { + if (!AdjustSector(prop, trkWork)) { if (ENABLE_INFO) { GPUInfo("FollowProlongation: Adjusting sector failed for track %i candidate %i in layer %i", iTrack, iCandidate, iLayer); } - GPUInfo("Adjust sector failed"); continue; } @@ -652,7 +718,6 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK if (ENABLE_INFO) { GPUInfo("FollowProlongation: Track out of TRD acceptance with z=%f in layer %i (eta=%f)", trkWork->getZ(), iLayer, trkWork->getEta()); } - GPUInfo("Out of z acceptance"); continue; } @@ -670,8 +735,7 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK } int currSec = mGeo->GetSector(currDet); if (currSec != GetSector(prop->getAlpha())) { - float currAlpha = GetAlphaOfSector(currSec); - if (!prop->rotate(currAlpha)) { + if (!prop->rotate(GetAlphaOfSector(currSec))) { if (ENABLE_WARNING) { Warning("FollowProlongation", "Track could not be rotated in tracklet coordinate system"); } @@ -689,13 +753,13 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK } } // first propagate track to x of tracklet - for (int trkltIdx = mTrackletIndexArray[currDet]; trkltIdx < mTrackletIndexArray[currDet + 1]; ++trkltIdx) { + for (int trkltIdx = mTrackletIndexArray[trkltIdxOffset + currDet]; trkltIdx < mTrackletIndexArray[trkltIdxOffset + currDet + 1]; ++trkltIdx) { if (CAMath::Abs(trkWork->getY() - mSpacePoints[trkltIdx].mX[0]) > roadY || CAMath::Abs(trkWork->getZ() - mSpacePoints[trkltIdx].mX[1]) > roadZ) { // skip tracklets which are too far away // although the radii of space points and tracks may differ by ~ few mm the roads are large enough to allow no efficiency loss by this cut continue; } - My_Float projY, projZ; + float projY, projZ; prop->getPropagatedYZ(mSpacePoints[trkltIdx].mR, projY, projZ); // correction for tilted pads (only applied if deltaZ < l_pad && track z err << l_pad) float tiltCorr = tilt * (mSpacePoints[trkltIdx].mX[1] - projZ); @@ -715,22 +779,22 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK RecalcTrkltCov(tilt, trkWork->getSnp(), pad->GetRowSize(mTracklets[trkltIdx].GetZbin()), trkltCovTmp); float chi2 = prop->getPredictedChi2(trkltPosTmpYZ, trkltCovTmp); // GPUInfo("layer %i: chi2 = %f", iLayer, chi2); - if (chi2 < mMaxChi2 && CAMath::Abs(GetAngularPull(mSpacePoints[trkltIdx].mDy, trkWork->getSnp())) < 4) { - Hypothesis hypo(trkWork->GetNlayers(), iCandidate, trkltIdx, trkWork->GetChi2() + chi2, GetPredictedChi2(trkltPosTmpYZ, trkltCovTmp, trkWork->getPar(), trkWork->getCov())); + if (chi2 < Param().rec.trdMaxChi2 && CAMath::Abs(GetAngularPull(mSpacePoints[trkltIdx].mDy, trkWork->getSnp())) < 4) { + Hypothesis hypo(trkWork->GetNlayers(), iCandidate, trkltIdx, trkWork->GetChi2() + chi2); InsertHypothesis(hypo, nCurrHypothesis, hypothesisIdxOffset); - } // end tracklet chi2 < mMaxChi2 + } // end tracklet chi2 < Param().rec.trdMaxChi2 } // end tracklet in window } // tracklet loop } // chamber loop // add no update to hypothesis list - Hypothesis hypoNoUpdate(trkWork->GetNlayers(), iCandidate, -1, trkWork->GetChi2() + mChi2Penalty); + Hypothesis hypoNoUpdate(trkWork->GetNlayers(), iCandidate, -1, trkWork->GetChi2() + Param().rec.trdPenaltyChi2); InsertHypothesis(hypoNoUpdate, nCurrHypothesis, hypothesisIdxOffset); isOK = true; } // end candidate loop #ifdef ENABLE_GPUMC - // in case matching tracklet exists in this layer -> store position information for debugging + // in case matching tracklet exists in this layer -> store position information for debugging FIXME: does not yet work for time frames in o2, but here we anyway do not yet have MC labels... if (matchAvailableAll[iLayer].size() > 0 && mDebugOutput) { mDebug->SetNmatchAvail(matchAvailableAll[iLayer].size(), iLayer); int realTrkltId = matchAvailableAll[iLayer].at(0); @@ -754,7 +818,6 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK My_Float covReal[3] = {0.}; RecalcTrkltCov(tilt, trkWork->getSnp(), pad->GetRowSize(mTracklets[realTrkltId].GetZbin()), covReal); mDebug->SetChi2Real(prop->getPredictedChi2(yzPosReal, covReal), iLayer); - mDebug->SetChi2YZPhiReal(GetPredictedChi2(yzPosReal, covReal, trkWork->getPar(), trkWork->getCov()), iLayer); mDebug->SetRawTrackletPositionReal(mSpacePoints[realTrkltId].mR, mSpacePoints[realTrkltId].mX, iLayer); mDebug->SetCorrectedTrackletPositionReal(yzPosReal, iLayer); mDebug->SetTrackletPropertiesReal(mTracklets[realTrkltId].GetDetector(), iLayer); @@ -763,7 +826,6 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK #endif // mDebug->SetChi2Update(mHypothesis[0 + hypothesisIdxOffset].mChi2 - t->GetChi2(), iLayer); // only meaningful for ONE candidate!!! - mDebug->SetChi2YZPhiUpdate(mHypothesis[0 + hypothesisIdxOffset].mChi2YZPhi, iLayer); // only meaningful for ONE candidate!!! mDebug->SetRoad(roadY, roadZ, iLayer); // only meaningful for ONE candidate bool wasTrackStored = false; // -------------------------------------------------------------------------------- @@ -792,10 +854,10 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK if (mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId == -1) { // no matching tracklet found if (trkWork->GetIsFindable(iLayer)) { - if (trkWork->GetNmissingConsecLayers(iLayer) > mMaxMissingLy) { + if (trkWork->GetNmissingConsecLayers(iLayer) > Param().rec.trdStopTrkAfterNMissLy) { trkWork->SetIsStopped(); } - trkWork->SetChi2(trkWork->GetChi2() + mChi2Penalty); + trkWork->SetChi2(trkWork->GetChi2() + Param().rec.trdPenaltyChi2); } if (iUpdate == 0 && mNCandidates > 1) { // TODO: is thie really necessary????? CHECK! *t = mCandidates[2 * iUpdate + nextIdx]; @@ -815,9 +877,9 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK if (ENABLE_WARNING) { Warning("FollowProlongation", "Final track propagation for track %i update %i in layer %i failed", iTrack, iUpdate, iLayer); } - trkWork->SetChi2(trkWork->GetChi2() + mChi2Penalty); + trkWork->SetChi2(trkWork->GetChi2() + Param().rec.trdPenaltyChi2); if (trkWork->GetIsFindable(iLayer)) { - if (trkWork->GetNmissingConsecLayers(iLayer) >= mMaxMissingLy) { + if (trkWork->GetNmissingConsecLayers(iLayer) >= Param().rec.trdStopTrkAfterNMissLy) { trkWork->SetIsStopped(); } } @@ -861,9 +923,9 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK if (ENABLE_WARNING) { Warning("FollowProlongation", "Failed to update track %i with space point in layer %i", iTrack, iLayer); } - trkWork->SetChi2(trkWork->GetChi2() + mChi2Penalty); + trkWork->SetChi2(trkWork->GetChi2() + Param().rec.trdPenaltyChi2); if (trkWork->GetIsFindable(iLayer)) { - if (trkWork->GetNmissingConsecLayers(iLayer) >= mMaxMissingLy) { + if (trkWork->GetNmissingConsecLayers(iLayer) >= Param().rec.trdStopTrkAfterNMissLy) { trkWork->SetIsStopped(); } } @@ -974,40 +1036,6 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK return true; } -template <class TRDTRK, class PROP> -GPUd() float GPUTRDTracker_t<TRDTRK, PROP>::GetPredictedChi2(const My_Float* pTRD, const My_Float* covTRD, const My_Float* pTrk, const My_Float* covTrk) const -{ - // FIXME: This function cannot yet be used in production (maybe it is not necessary at all?) - // conversion from tracklet deflection to sin(phi) needs to be parametrized - // predict chi2 for update of track with pTrk and covTrk with TRD space point with pTRD and covTRD - // taking into account y, z and azimuthal angle of the track and tracklet - float deltaY = pTrk[0] - pTRD[0]; - float deltaZ = pTrk[1] - pTRD[1]; - float deltaS = pTrk[2] - pTRD[2]; // FIXME: pTRD[2] is not defined, needs conversion dy -> sin(phi) - - // add errors for track and space point, assume no correlation in y-sin(phi) and z-sin(phi) for space point - float sigmaZ2 = covTrk[2] + covTRD[2]; - float sigmaS2 = covTrk[5] + GetAngularResolution(pTrk[2]); // FIXME: convert angular resolution from dy to sin(phi) for the TRD space point - float sigmaY2 = covTrk[0] + covTRD[0]; - float sigmaZS = covTrk[4]; - float sigmaYS = covTrk[3]; - float sigmaYZ = covTrk[1] + covTRD[1]; - // inverse of the covariance matrix - float c11 = sigmaZ2 * sigmaS2 - sigmaZS * sigmaZS; - float c21 = sigmaZS * sigmaYS - sigmaYZ * sigmaS2; - float c22 = sigmaY2 * sigmaS2 - sigmaYS * sigmaYS; - float c31 = sigmaYZ * sigmaZS - sigmaZ2 * sigmaYS; - float c32 = sigmaYZ * sigmaYS - sigmaY2 * sigmaZS; - float c33 = sigmaY2 * sigmaZ2 - sigmaYZ * sigmaYZ; - // determinant - float det = sigmaY2 * sigmaZ2 * sigmaS2 + 2 * sigmaYZ * sigmaZS * sigmaYS - sigmaYS * sigmaYS * sigmaZ2 - sigmaZS * sigmaZS * sigmaY2 - sigmaS2 * sigmaYZ * sigmaYZ; - if (CAMath::Abs(det) < 1.e-10f) { - printf("Determinant too small: %f\n", det); - det = 1.e-10f; - } - det = 1.f / det; - return (c11 * deltaY * deltaY + 2.f * c21 * deltaY * deltaZ + 2.f * c31 * deltaY * deltaS + c22 * deltaZ * deltaZ + 2.f * c32 * deltaZ * deltaS + c33 * deltaS * deltaS) * det; -} template <class TRDTRK, class PROP> GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::InsertHypothesis(Hypothesis hypo, int& nCurrHypothesis, int idxOffset) @@ -1070,11 +1098,11 @@ GPUd() int GPUTRDTracker_t<TRDTRK, PROP>::GetDetectorNumber(const float zPos, co } template <class TRDTRK, class PROP> -GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::AdjustSector(PROP* prop, TRDTRK* t, const int layer) const +GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::AdjustSector(PROP* prop, TRDTRK* t) const { //-------------------------------------------------------------------- // rotate track in new sector if necessary and - // propagate to correct x of layer + // propagate to previous x afterwards // cancel if track crosses two sector boundaries //-------------------------------------------------------------------- float alpha = mGeo->GetAlpha(); @@ -1096,7 +1124,13 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::AdjustSector(PROP* prop, TRDTRK* t, c return false; } int sign = (y > 0) ? 1 : -1; - if (!prop->rotate(alphaCurr + alpha * sign)) { + float alphaNew = alphaCurr + alpha * sign; + if (alphaNew > M_PI) { + alphaNew -= 2 * M_PI; + } else if (alphaNew < -M_PI) { + alphaNew += 2 * M_PI; + } + if (!prop->rotate(alphaNew)) { return false; } if (!prop->propagateToX(xTmp, .8f, 2.f)) { @@ -1128,7 +1162,11 @@ GPUd() float GPUTRDTracker_t<TRDTRK, PROP>::GetAlphaOfSector(const int sec) cons //-------------------------------------------------------------------- // rotation angle for TRD sector sec //-------------------------------------------------------------------- - return (2.0f * M_PI / (float)kNSectors * ((float)sec + 0.5f)); + float alpha = 2.0f * M_PI / (float)kNSectors * ((float)sec + 0.5f); + if (alpha > M_PI) { + alpha -= 2 * M_PI; + } + return alpha; } template <class TRDTRK, class PROP> @@ -1182,7 +1220,7 @@ GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::FindChambersInRoad(const TRDTRK* t, c const GPUTRDpadPlane* pp = mGeo->GetPadPlane(iLayer, currStack); int lastPadRow = mGeo->GetRowMax(iLayer, currStack, 0); float zCenter = pp->GetRowPos(lastPadRow / 2); - if ((t->getZ() + roadZ) > pp->GetRowPos(0) || (t->getZ() - roadZ) < pp->GetRowPos(lastPadRow)) { + if ((t->getZ() + roadZ) > pp->GetRow0() || (t->getZ() - roadZ) < pp->GetRowEnd()) { int addStack = t->getZ() > zCenter ? currStack - 1 : currStack + 1; if (addStack < kNStacks && addStack > -1) { det[nDets++] = mGeo->GetDetector(iLayer, addStack, currSec); @@ -1273,18 +1311,30 @@ GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::IsGeoFindable(const TRDTRK* t, const return true; } +template <class TRDTRK, class PROP> +GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::SetNCollisions(int nColl) +{ + // Set the number of collisions for a given time frame. + // The number is taken from the TRD trigger records + if (nColl < mNMaxCollisions) { + mNCollisions = nColl; + } else { + GPUError("Cannot process more than %i collisions. The last %i collisions will be dropped", mNMaxCollisions, nColl - mNMaxCollisions); + mNCollisions = mNMaxCollisions; + } +} + #ifndef GPUCA_GPUCODE namespace GPUCA_NAMESPACE { namespace gpu { -// instatiate the tracker for both for GPU data types and for AliRoot/O2 data types #if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) +// instantiate version for non-GPU data types template class GPUTRDTracker_t<GPUTRDTrack, GPUTRDPropagator>; #endif -#ifndef GPUCA_ALIROOT_LIB +// always instantiate version for GPU data types template class GPUTRDTracker_t<GPUTRDTrackGPU, GPUTRDPropagatorGPU>; -#endif } // namespace gpu } // namespace GPUCA_NAMESPACE #endif diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h index ba99f630dc62e..02739fbdad80b 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h @@ -24,7 +24,7 @@ #include "GPUTRDTrack.h" #include "GPULogging.h" -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <vector> #endif @@ -95,11 +95,10 @@ class GPUTRDTracker_t : public GPUProcessor int mCandidateId; // to which track candidate the hypothesis belongs int mTrackletId; // tracklet index to be used for update float mChi2; // predicted chi2 for given space point - float mChi2YZPhi; // not yet ready (see GetPredictedChi2 method in cxx file) GPUd() float GetReducedChi2() { return mLayers > 0 ? mChi2 / mLayers : mChi2; } GPUd() Hypothesis() : mLayers(0), mCandidateId(-1), mTrackletId(-1), mChi2(9999.f) {} - GPUd() Hypothesis(int layers, int candidateId, int trackletId, float chi2, float chi2YZPhi = -1.f) : mLayers(layers), mCandidateId(candidateId), mTrackletId(trackletId), mChi2(chi2), mChi2YZPhi(chi2YZPhi) {} + GPUd() Hypothesis(int layers, int candidateId, int trackletId, float chi2, float chi2YZPhi = -1.f) : mLayers(layers), mCandidateId(candidateId), mTrackletId(trackletId), mChi2(chi2) {} }; short MemoryPermanent() const { return mMemoryPermanent; } @@ -107,50 +106,23 @@ class GPUTRDTracker_t : public GPUProcessor short MemoryTracks() const { return mMemoryTracks; } GPUhd() void OverrideGPUGeometry(TRD_GEOMETRY_CONST GPUTRDGeometry* geo) { mGeo = geo; } - void Reset(bool fast = false); + void Reset(); GPUd() int LoadTracklet(const GPUTRDTrackletWord& tracklet, const int* labels = nullptr); - //template <class T> - GPUd() int LoadTrack(const TRDTRK& trk, const int label = -1, const int* nTrkltsOffline = nullptr, const int labelOffline = -1) + template <class T> + GPUd() bool PreCheckTrackTRDCandidate(const T& trk) const { - if (mNTracks >= mNMaxTracks) { -#ifndef GPUCA_GPUCODE - GPUError("Error: Track dropped (no memory available) -> must not happen"); -#endif - return (1); - } - if (!trk.CheckNumericalQuality()) { - return (0); - } - if (CAMath::Abs(trk.getEta()) > mMaxEta) { - return (0); - } - if (trk.getPt() < mMinPt) { - return (0); - } -#ifdef GPUCA_ALIROOT_LIB - new (&mTracks[mNTracks]) TRDTRK(trk); // We need placement new, since the class is virtual -#else - mTracks[mNTracks] = trk; -#endif - mTracks[mNTracks].SetTPCtrackId(mNTracks); - if (label >= 0) { - mTracks[mNTracks].SetLabel(label); - } - if (nTrkltsOffline) { - for (int i = 0; i < 4; ++i) { - mTracks[mNTracks].SetNtrackletsOffline(i, nTrkltsOffline[i]); // see GPUTRDTrack.h for information on the index - } - } - mTracks[mNTracks].SetLabelOffline(labelOffline); - mNTracks++; - return (0); + return true; } + GPUd() bool PreCheckTrackTRDCandidate(const GPUTPCGMMergedTrack& trk) const { return trk.OK() && !trk.Looper(); } + GPUd() bool CheckTrackTRDCandidate(const TRDTRK& trk) const; + GPUd() int LoadTrack(const TRDTRK& trk, const int label = -1, const int* nTrkltsOffline = nullptr, const int labelOffline = -1, int tpcTrackId = -1, bool checkTrack = true); + + GPUd() int GetCollisionID(float trkTime) const; GPUd() void DoTrackingThread(int iTrk, int threadId = 0); - GPUd() bool CalculateSpacePoints(); - GPUd() bool FollowProlongation(PROP* prop, TRDTRK* t, int threadId); - GPUd() float GetPredictedChi2(const My_Float* pTRD, const My_Float* covTRD, const My_Float* pTrk, const My_Float* covTrk) const; + GPUd() bool CalculateSpacePoints(int iCollision = 0); + GPUd() bool FollowProlongation(PROP* prop, TRDTRK* t, int threadId, int collisionId); GPUd() int GetDetectorNumber(const float zPos, const float alpha, const int layer) const; - GPUd() bool AdjustSector(PROP* prop, TRDTRK* t, const int layer) const; + GPUd() bool AdjustSector(PROP* prop, TRDTRK* t) const; GPUd() int GetSector(float alpha) const; GPUd() float GetAlphaOfSector(const int sec) const; GPUd() float GetRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mRPhiB) * (snp - mRPhiB)); } // parametrization obtained from track-tracklet residuals: @@ -166,24 +138,23 @@ class GPUTRDTracker_t : public GPUProcessor GPUd() void Quicksort(const int left, const int right, const int size); GPUd() void InsertHypothesis(Hypothesis hypo, int& nCurrHypothesis, int idxOffset); + // input from TRD trigger record + GPUd() void SetNMaxCollisions(int nColl) { mNMaxCollisions = nColl; } // can this be fixed to a sufficiently large value? + GPUd() void SetNCollisions(int nColl); + GPUd() void SetTriggerRecordIndices(int* indices) { mTriggerRecordIndices = indices; } + GPUd() void SetTriggerRecordTimes(float* times) { mTriggerRecordTimes = times; } + // settings + GPUd() void SetProcessPerTimeFrame() { mProcessPerTimeFrame = true; } GPUd() void SetMCEvent(AliMCEvent* mc) { mMCEvent = mc; } GPUd() void EnableDebugOutput() { mDebugOutput = true; } - GPUd() void SetPtThreshold(float minPt) { mMinPt = minPt; } GPUd() void SetMaxEta(float maxEta) { mMaxEta = maxEta; } - GPUd() void SetChi2Threshold(float chi2) { mMaxChi2 = chi2; } - GPUd() void SetChi2Penalty(float chi2) { mChi2Penalty = chi2; } - GPUd() void SetMaxMissingLayers(int ly) { mMaxMissingLy = ly; } GPUd() void SetExtraRoadY(float extraRoadY) { mExtraRoadY = extraRoadY; } GPUd() void SetRoadZ(float roadZ) { mRoadZ = roadZ; } GPUd() AliMCEvent* GetMCEvent() const { return mMCEvent; } GPUd() bool GetIsDebugOutputOn() const { return mDebugOutput; } - GPUd() float GetPtThreshold() const { return mMinPt; } GPUd() float GetMaxEta() const { return mMaxEta; } - GPUd() float GetChi2Threshold() const { return mMaxChi2; } - GPUd() float GetChi2Penalty() const { return mChi2Penalty; } - GPUd() int GetMaxMissingLayers() const { return mMaxMissingLy; } GPUd() int GetNCandidates() const { return mNCandidates; } GPUd() float GetExtraRoadY() const { return mExtraRoadY; } GPUd() float GetRoadZ() const { return mRoadZ; } @@ -199,15 +170,20 @@ class GPUTRDTracker_t : public GPUProcessor protected: float* mR; // radial position of each TRD chamber, alignment taken into account, radial spread within chambers < 7mm bool mIsInitialized; // flag is set upon initialization + bool mProcessPerTimeFrame; // if true, tracking is done per time frame instead of on a single events basis //FIXME is this needed?? short mMemoryPermanent; // size of permanent memory for the tracker short mMemoryTracklets; // size of memory for TRD tracklets short mMemoryTracks; // size of memory for tracks (used for i/o) + int mNMaxCollisions; // max number of collisions to process (per time frame) int mNMaxTracks; // max number of tracks the tracker can handle (per event) int mNMaxSpacePoints; // max number of space points hold by the tracker (per event) TRDTRK* mTracks; // array of trd-updated tracks int mNCandidates; // max. track hypothesis per layer + int mNCollisions; // number of collisions with TRD tracklet data int mNTracks; // number of TPC tracks to be matched int mNEvents; // number of processed events + int* mTriggerRecordIndices; // index of first tracklet for each collision + float* mTriggerRecordTimes; // time in us for each collision GPUTRDTrackletWord* mTracklets; // array of all tracklets, later sorted by HCId int mMaxThreads; // maximum number of supported threads int mNTracklets; // total number of tracklets in event @@ -229,14 +205,11 @@ class GPUTRDTracker_t : public GPUProcessor float mAngleToDyC; // parameterization for conversion track angle -> tracklet deflection /// ---- end error parametrization ---- bool mDebugOutput; // store debug output + float mTimeWindow; // max. deviation of the ITS-TPC track time w.r.t. TRD trigger record time stamp (in us, default is 100 ns) float mRadialOffset; // due to mis-calibration of t0 - float mMinPt; // min pt of TPC tracks for tracking float mMaxEta; // TPC tracks with higher eta are ignored float mExtraRoadY; // addition to search road in r-phi to account for not exact radial match of tracklets and tracks in first iteration float mRoadZ; // in z, a constant search road is used - float mMaxChi2; // max chi2 for tracklets - int mMaxMissingLy; // max number of missing layers per track - float mChi2Penalty; // chi2 added to the track for no update float mZCorrCoefNRC; // tracklet z-position depends linearly on track dip angle AliMCEvent* mMCEvent; //! externaly supplied optional MC event GPUTRDTrackerDebug<TRDTRK>* mDebug; // debug output diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.cxx index 2b1003cf7a678..fcfdb62b13969 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.cxx @@ -130,7 +130,7 @@ int GPUTRDTrackerComponent::ReadConfigurationString(const char* arguments) if (argument.CompareTo("-debugOutput") == 0) { fDebugTrackOutput = true; fVerboseDebugOutput = true; - HLTInfo("Tracks are dumped in the GPUTRDTrack format"); + HLTInfo("Tracks are dumped in the GPUTRDTrackGPU format"); continue; } @@ -185,13 +185,13 @@ int GPUTRDTrackerComponent::DoInit(int argc, const char** argv) GPUSettingsEvent cfgEvent; cfgEvent.solenoidBz = GetBz(); GPUSettingsRec cfgRec; - GPUSettingsDeviceProcessing cfgDeviceProcessing; + GPUSettingsProcessing cfgDeviceProcessing; GPURecoStepConfiguration cfgRecoStep; cfgRecoStep.steps = GPUDataTypes::RecoStep::NoRecoStep; cfgRecoStep.inputs.clear(); cfgRecoStep.outputs.clear(); fRec = GPUReconstruction::CreateInstance("CPU", true); - fRec->SetSettings(GetBz()); + fRec->SetSettings(&cfgEvent, &cfgRec, &cfgDeviceProcessing, &cfgRecoStep); fChain = fRec->AddChain<GPUChainTracking>(); fGeo = new GPUTRDGeometry(); @@ -202,7 +202,7 @@ int GPUTRDTrackerComponent::DoInit(int argc, const char** argv) HLTError("TRD geometry not available"); return -EINVAL; } - fTracker = new GPUTRDTracker(); + fTracker = new GPUTRDTrackerGPU(); if (!fTracker) { return -ENOMEM; } @@ -210,7 +210,7 @@ int GPUTRDTrackerComponent::DoInit(int argc, const char** argv) fTracker->EnableDebugOutput(); } fRec->RegisterGPUProcessor(fTracker, false); - fChain->SetTRDGeometry(reinterpret_cast<o2::trd::TRDGeometryFlat*>(fGeo)); + fChain->SetTRDGeometry(reinterpret_cast<o2::trd::GeometryFlat*>(fGeo)); if (fRec->Init()) { return -EINVAL; } @@ -252,7 +252,7 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con int iResult = 0; if (fTrackList->GetEntries() != 0) { - fTrackList->Clear(); // tracks are owned by GPUTRDTracker + fTrackList->Clear(); // tracks are owned by GPUTRDTrackerGPU } int nBlocks = evtData.fBlockCnt; @@ -261,7 +261,7 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con AliHLTTracksData* itsData = nullptr; AliHLTTrackMCData* tpcDataMC = nullptr; - std::vector<GPUTRDTrack> tracksTPC; + std::vector<GPUTRDTrackGPU> tracksTPC; std::vector<int> tracksTPCLab; std::vector<int> tracksTPCId; @@ -338,7 +338,7 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con if (itsData != nullptr && !itsAvail.at(currOutTrackTPC->fTrackID)) { continue; } - GPUTRDTrack t(*currOutTrackTPC); + GPUTRDTrackGPU t(*currOutTrackTPC); int mcLabel = -1; if (tpcDataMC) { if (mcLabels.find(currOutTrackTPC->fTrackID) != mcLabels.end()) { @@ -384,9 +384,9 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con fTracker->DoTracking(NULL); fBenchmark.Stop(1); - GPUTRDTrack* trackArray = fTracker->Tracks(); + GPUTRDTrackGPU* trackArray = fTracker->Tracks(); int nTracks = fTracker->NTracks(); - GPUTRDTracker::GPUTRDSpacePointInternal* spacePoints = fTracker->SpacePoints(); + GPUTRDTrackerGPU::GPUTRDSpacePointInternal* spacePoints = fTracker->SpacePoints(); // TODO delete fTrackList since it only works for TObjects (or use compiler flag after tests with GPU track type) // for (int iTrack=0; iTrack<nTracks; ++iTrack) { @@ -408,12 +408,14 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con GPUTRDTrackData* outTracks = (GPUTRDTrackData*)(outputPtr); outTracks->fCount = 0; + int assignedTracklets = 0; for (int iTrk = 0; iTrk < nTracks; ++iTrk) { - GPUTRDTrack& t = trackArray[iTrk]; + GPUTRDTrackGPU& t = trackArray[iTrk]; if (t.GetNtracklets() == 0) { continue; } + assignedTracklets += t.GetNtracklets(); GPUTRDTrackDataRecord& currOutTrack = outTracks->fTracks[outTracks->fCount]; t.ConvertTo(currOutTrack); outTracks->fCount++; @@ -456,7 +458,7 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con } for (int i = 0; i < nTrackletsTotal; ++i) { - const GPUTRDTracker::GPUTRDSpacePointInternal& sp = spacePoints[i]; + const GPUTRDTrackerGPU::GPUTRDSpacePointInternal& sp = spacePoints[i]; int id = sp.mId; if (id < 0 || id >= nTrackletsTotal) { HLTError("Internal error: wrong space point index %d", id); @@ -477,7 +479,7 @@ int GPUTRDTrackerComponent::DoEvent(const AliHLTComponentEventData& evtData, con size += blockSize; outputPtr += resultDataSP.fSize; - HLTInfo("TRD tracker: output %d tracks and %d track points", outTracks->fCount, outTrackPoints->fCount); + HLTInfo("TRD tracker: output %d tracks (%d assigned tracklets) and %d track points", outTracks->fCount, assignedTracklets, outTrackPoints->fCount); } fBenchmark.Stop(0); diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.h b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.h index 9be4c4de9eff0..6adc8704e7e79 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerComponent.h @@ -131,7 +131,7 @@ class GPUTRDTrackerComponent : public AliHLTProcessor * Members - private * --------------------------------------------------------------------------------- */ - GPUCA_NAMESPACE::gpu::GPUTRDTracker* fTracker; // the tracker itself + GPUCA_NAMESPACE::gpu::GPUTRDTrackerGPU* fTracker; // the tracker itself GPUCA_NAMESPACE::gpu::GPUTRDGeometry* fGeo; // TRD geometry needed by the tracker GPUCA_NAMESPACE::gpu::GPUReconstruction* fRec; // GPU Reconstruction object GPUCA_NAMESPACE::gpu::GPUChainTracking* fChain; // Tracking Chain Object diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerDebug.h b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerDebug.h index 209dfdfc807f3..08b453d4f2345 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerDebug.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerDebug.h @@ -95,9 +95,7 @@ class GPUTRDTrackerDebug fTrackYReal.ResizeTo(6); fTrackZReal.ResizeTo(6); fTrackSecReal.ResizeTo(6); - fChi2YZPhiUpdate.ResizeTo(6); fChi2Update.ResizeTo(6); - fChi2YZPhiReal.ResizeTo(6); fChi2Real.ResizeTo(6); fNmatchesAvail.ResizeTo(6); fFindable.ResizeTo(6); @@ -151,9 +149,7 @@ class GPUTRDTrackerDebug fTrackYReal.Zero(); fTrackZReal.Zero(); fTrackSecReal.Zero(); - fChi2YZPhiUpdate.Zero(); fChi2Update.Zero(); - fChi2YZPhiReal.Zero(); fChi2Real.Zero(); fNmatchesAvail.Zero(); fFindable.Zero(); @@ -290,8 +286,6 @@ class GPUTRDTrackerDebug // update information void SetChi2Update(float chi2, int ly) { fChi2Update(ly) = chi2; } void SetChi2Real(float chi2, int ly) { fChi2Real(ly) = chi2; } - void SetChi2YZPhiUpdate(float chi2, int ly) { fChi2YZPhiUpdate(ly) = chi2; } - void SetChi2YZPhiReal(float chi2, int ly) { fChi2YZPhiReal(ly) = chi2; } // other infos void SetRoad(float roadY, float roadZ, int ly) @@ -370,8 +364,6 @@ class GPUTRDTrackerDebug "trackletDetReal.=" << &fTrackletDetReal << // detector number for matching or related tracklet if available, otherwise -1 "chi2Update.=" << &fChi2Update << // chi2 for update "chi2Real.=" << &fChi2Real << // chi2 for first tracklet w/ matching MC label - "chi2YZPhiUpdate.=" << &fChi2YZPhiUpdate << // chi2 for update taking into account full tracklet information (y, z, dY aka sin(phi)) - "chi2YZPhiReal.=" << &fChi2YZPhiReal << // chi2 for first tracklet w/ matching MC label taking into account full tracklet information (y, z, dY aka sin(phi)) "chi2Total=" << fChi2 << // total chi2 for track "nLayers=" << fNlayers << // number of layers in which track was findable "nTracklets=" << fNtrklts << // number of attached tracklets @@ -456,8 +448,6 @@ class GPUTRDTrackerDebug TVectorF fTrackletDetReal; TVectorF fChi2Update; TVectorF fChi2Real; - TVectorF fChi2YZPhiUpdate; - TVectorF fChi2YZPhiReal; TVectorF fRoadY; TVectorF fRoadZ; TVectorF fFindable; @@ -512,7 +502,6 @@ class GPUTRDTrackerDebug GPUd() void SetChi2Update(float chi2, int ly) {} GPUd() void SetChi2Real(float chi2, int ly) {} GPUd() void SetChi2YZPhiUpdate(float chi2, int ly) {} - GPUd() void SetChi2YZPhiReal(float chi2, int ly) {} // other infos GPUd() void SetRoad(float roadY, float roadZ, int ly) {} diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.cxx index d4756f20b2562..296407ed2af00 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTrackerKernels.cxx @@ -23,7 +23,7 @@ using namespace GPUCA_NAMESPACE::gpu; template <> GPUdii() void GPUTRDTrackerKernels::Thread<0>(int nBlocks, int nThreads, int iBlock, int iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors) { - GPUCA_OPENMP(parallel for num_threads(processors.trdTracker.GetRec().GetDeviceProcessingSettings().ompKernels ? 1 : processors.trdTracker.GetRec().GetDeviceProcessingSettings().nThreads)) + GPUCA_OPENMP(parallel for if(!processors.trdTracker.GetRec().GetProcessingSettings().ompKernels) num_threads(processors.trdTracker.GetRec().GetProcessingSettings().ompThreads)) for (int i = get_global_id(0); i < processors.trdTracker.NTracks(); i += get_global_size(0)) { processors.trdTracker.DoTrackingThread(i, get_global_id(0)); } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTrackletWord.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTrackletWord.cxx index 1a899c409620d..f12833a6ddb9f 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTrackletWord.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTrackletWord.cxx @@ -13,7 +13,7 @@ #include "GPUTRDTrackletWord.h" using namespace GPUCA_NAMESPACE::gpu; -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <new> #endif diff --git a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C index a7413e4894325..d53118770e0d6 100644 --- a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C +++ b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C @@ -15,37 +15,55 @@ #include "GPUTRDGeometry.h" // O2 header +#include "DetectorsCommonDataFormats/NameConf.h" +#include "CommonConstants/LHCConstants.h" #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" +#include "TRDBase/Geometry.h" #include "ReconstructionDataFormats/TrackTPCITS.h" -#include "TRDBase/Tracklet.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/TriggerRecord.h" #endif using namespace GPUCA_NAMESPACE::gpu; +unsigned int convertTrkltWordToRun2Format(uint64_t trkltWordRun3) +{ + // FIXME: this is currently a dummy function + // need proper functionality to convert the new tracklet data format to + // something compatible with the TRD tracker, but this macro is probably + // not the right place for this + unsigned int trkltWord = 0; + return trkltWord; +} + void run_trd_tracker(std::string path = "./", - std::string inputGRP = "o2sim_grp.root", std::string inputTracks = "o2match_itstpc.root", std::string inputTracklets = "trdtracklets.root") { + //-------- debug time information from tracks and tracklets + std::vector<float> trdTriggerTimes; + std::vector<int> trdTriggerIndices; //-------- init geometry and field --------// - o2::base::GeometryManager::loadGeometry(path); - o2::base::Propagator::initFieldFromGRP(path + inputGRP); + o2::base::GeometryManager::loadGeometry(); + o2::base::Propagator::initFieldFromGRP(o2::base::NameConf::getGRPFileName()); - auto geo = o2::trd::TRDGeometry::instance(); + auto geo = o2::trd::Geometry::instance(); geo->createPadPlaneArray(); geo->createClusterMatrixArray(); - const o2::trd::TRDGeometryFlat geoFlat(*geo); + const o2::trd::GeometryFlat geoFlat(*geo); //-------- init GPU reconstruction --------// GPUSettingsEvent cfgEvent; // defaults should be ok GPUSettingsRec cfgRec; // don't care for now, NWaysOuter is set in here for instance - GPUSettingsDeviceProcessing cfgDeviceProcessing; // also keep defaults here, or adjust debug level - cfgDeviceProcessing.debugLevel = 10; + GPUSettingsProcessing cfgDeviceProcessing; // also keep defaults here, or adjust debug level + cfgDeviceProcessing.debugLevel = 5; GPURecoStepConfiguration cfgRecoStep; cfgRecoStep.steps = GPUDataTypes::RecoStep::NoRecoStep; + cfgRecoStep.inputs.clear(); + cfgRecoStep.outputs.clear(); auto rec = GPUReconstruction::CreateInstance("CPU", true); rec->SetSettings(&cfgEvent, &cfgRec, &cfgDeviceProcessing, &cfgRecoStep); @@ -53,12 +71,14 @@ void run_trd_tracker(std::string path = "./", auto tracker = new GPUTRDTracker(); tracker->SetNCandidates(1); // must be set before initialization + tracker->SetProcessPerTimeFrame(); + tracker->SetNMaxCollisions(100); rec->RegisterGPUProcessor(tracker, false); chainTracking->SetTRDGeometry(&geoFlat); - tracker->SetTrackingChain(chainTracking); - rec->Init(); - rec->AllocateRegisteredMemory(nullptr); + if (rec->Init()) { + printf("ERROR: GPUReconstruction not initialized\n"); + } // configure the tracker //tracker->EnableDebugOutput(); @@ -66,7 +86,7 @@ void run_trd_tracker(std::string path = "./", tracker->SetPtThreshold(0.5); tracker->SetChi2Threshold(15); tracker->SetChi2Penalty(12); - tracker->SetMaxMissingLayers(6); + tracker->SetStopTrkFollowingAfterNMissingLayers(6); tracker->PrintSettings(); // load input tracks @@ -85,26 +105,38 @@ void run_trd_tracker(std::string path = "./", TChain trdTracklets("o2sim"); trdTracklets.AddFile((path + inputTracklets).c_str()); - std::vector<o2::trd::Tracklet>* trackletsInArrayPtr = nullptr; + std::vector<o2::trd::TriggerRecord>* triggerRecordsInArrayPtr = nullptr; + trdTracklets.SetBranchAddress("TrackTrg", &triggerRecordsInArrayPtr); + std::vector<o2::trd::Tracklet64>* trackletsInArrayPtr = nullptr; trdTracklets.SetBranchAddress("Tracklet", &trackletsInArrayPtr); trdTracklets.GetEntry(0); + int nCollisions = triggerRecordsInArrayPtr->size(); int nTracklets = trackletsInArrayPtr->size(); - printf("There are %i tracklets in total\n", nTracklets); - - auto pp = geo->getPadPlane(0, 0); - printf("Tilt=%f\n", pp->getTiltingAngle()); + printf("There are %i tracklets in total from %i trigger records\n", nTracklets, nCollisions); + + for (int iEv = 0; iEv < nCollisions; ++iEv) { + o2::trd::TriggerRecord& trg = triggerRecordsInArrayPtr->at(iEv); + int nTrackletsCurrent = trg.getNumberOfObjects(); + int iFirstTracklet = trg.getFirstEntry(); + int64_t evTime = trg.getBCData().toLong() * o2::constants::lhc::LHCBunchSpacingNS; // event time in ns + trdTriggerTimes.push_back(evTime / 1000.); + trdTriggerIndices.push_back(iFirstTracklet); + printf("Event %i: Occured at %li us after SOR, contains %i tracklets, index of first tracklet is %i\n", iEv, evTime / 1000, nTrackletsCurrent, iFirstTracklet); + } - tracker->Reset(true); + tracker->Reset(); chainTracking->mIOPtrs.nMergedTracks = nTracks; chainTracking->mIOPtrs.nTRDTracklets = nTracklets; chainTracking->AllocateIOMemory(); rec->PrepareEvent(); - rec->AllocateRegisteredMemory(tracker->MemoryTracks()); + rec->SetupGPUProcessor(tracker, true); + printf("Start loading input into TRD tracker\n"); // load everything into the tracker for (int iTrk = 0; iTrk < nTracks; ++iTrk) { - auto trk = tracksInArrayPtr->at(iTrk); + const auto& match = (*tracksInArrayPtr)[iTrk]; + const auto& trk = match.getParamOut(); GPUTRDTrack trkLoad; trkLoad.setX(trk.getX()); trkLoad.setAlpha(trk.getAlpha()); @@ -114,22 +146,27 @@ void run_trd_tracker(std::string path = "./", for (int i = 0; i < 15; ++i) { trkLoad.setCov(trk.getCov()[i], i); } + trkLoad.setTime(match.getTimeMUS().getTimeStamp()); tracker->LoadTrack(trkLoad); + printf("Loaded track %i with time %f\n", iTrk, trkLoad.getTime()); } + for (int iTrklt = 0; iTrklt < nTracklets; ++iTrklt) { auto trklt = trackletsInArrayPtr->at(iTrklt); - unsigned int trkltWord = trklt.getTrackletWord(); + unsigned int trkltWord = convertTrkltWordToRun2Format(trklt.getTrackletWord()); GPUTRDTrackletWord trkltLoad; trkltLoad.SetId(iTrklt); - trkltLoad.SetHCId(trklt.getHCId()); + trkltLoad.SetHCId(trklt.getHCID()); trkltLoad.SetTrackletWord(trkltWord); if (tracker->LoadTracklet(trkltLoad) > 0) { printf("Could not load tracklet %i\n", iTrklt); } } + tracker->SetTriggerRecordTimes(&(trdTriggerTimes[0])); + tracker->SetTriggerRecordIndices(&(trdTriggerIndices[0])); + tracker->SetNCollisions(nCollisions); tracker->DumpTracks(); - tracker->DoTracking(); + tracker->DoTracking(chainTracking); tracker->DumpTracks(); - printf("Done\n"); } diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.cxx b/GPU/GPUTracking/dEdx/GPUdEdx.cxx index ff13cdbce96d5..2fe03a6e20cb3 100644 --- a/GPU/GPUTracking/dEdx/GPUdEdx.cxx +++ b/GPU/GPUTracking/dEdx/GPUdEdx.cxx @@ -19,7 +19,9 @@ using namespace GPUCA_NAMESPACE::gpu; +#ifndef GPUCA_GPUCODE_DEVICE GPUd() void GPUdEdx::clear() { new (this) GPUdEdx; } +#endif GPUd() void GPUdEdx::computedEdx(GPUdEdxInfo& GPUrestrict() output, const GPUParam& GPUrestrict() param) { @@ -50,7 +52,7 @@ GPUd() void GPUdEdx::computedEdx(GPUdEdxInfo& GPUrestrict() output, const GPUPar output.NHitsSubThresholdOROC3 = countOROC3; } -GPUd() float GPUdEdx::GetSortTruncMean(float* GPUrestrict() array, int count, int trunclow, int trunchigh) +GPUd() float GPUdEdx::GetSortTruncMean(GPUCA_DEDX_STORAGE_TYPE* GPUrestrict() array, int count, int trunclow, int trunchigh) { trunclow = count * trunclow / 128; trunchigh = count * trunchigh / 128; @@ -60,7 +62,7 @@ GPUd() float GPUdEdx::GetSortTruncMean(float* GPUrestrict() array, int count, in CAAlgo::sort(array, array + count); float mean = 0; for (int i = trunclow; i < trunchigh; i++) { - mean += array[i]; + mean += (float)array[i] * (1.f / scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::factor); } return (mean / (trunchigh - trunclow)); } diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.h b/GPU/GPUTracking/dEdx/GPUdEdx.h index d48eabc8995b5..ac7a45db63101 100644 --- a/GPU/GPUTracking/dEdx/GPUdEdx.h +++ b/GPU/GPUTracking/dEdx/GPUdEdx.h @@ -19,7 +19,7 @@ #include "GPUCommonMath.h" #include "GPUParam.h" #include "GPUdEdxInfo.h" -#ifndef GPUCA_ALIROOT_LIB +#if defined(HAVE_O2HEADERS) && !defined(GPUCA_OPENCL1) #include "TPCdEdxCalibrationSplines.h" #endif @@ -27,7 +27,7 @@ namespace GPUCA_NAMESPACE { namespace gpu { -#ifndef HAVE_O2HEADERS +#if !defined(HAVE_O2HEADERS) || defined(GPUCA_OPENCL1) class GPUdEdx { @@ -50,13 +50,33 @@ class GPUdEdx GPUd() void computedEdx(GPUdEdxInfo& output, const GPUParam& param); private: - GPUd() float GetSortTruncMean(float* array, int count, int trunclow, int trunchigh); + GPUd() float GetSortTruncMean(GPUCA_DEDX_STORAGE_TYPE* array, int count, int trunclow, int trunchigh); GPUd() void checkSubThresh(int roc); + template <typename T, typename fake = void> + struct scalingFactor; + template <typename fake> + struct scalingFactor<unsigned short, fake> { + static constexpr float factor = 4.f; + static constexpr float round = 0.5f; + }; + template <typename fake> + struct scalingFactor<float, fake> { + static constexpr float factor = 1.f; + static constexpr float round = 0.f; + }; +#if defined(__CUDACC__) || defined(__HIPCC__) + template <typename fake> + struct scalingFactor<half, fake> { + static constexpr float factor = 1.f; + static constexpr float round = 0.f; + }; +#endif + static constexpr int MAX_NCL = GPUCA_ROW_COUNT; // Must fit in mNClsROC (unsigned char)! - float mChargeTot[MAX_NCL]; // No need for default, just some memory - float mChargeMax[MAX_NCL]; // No need for default, just some memory + GPUCA_DEDX_STORAGE_TYPE mChargeTot[MAX_NCL]; // No need for default, just some memory + GPUCA_DEDX_STORAGE_TYPE mChargeMax[MAX_NCL]; // No need for default, just some memory float mSubThreshMinTot = 0.f; float mSubThreshMinMax = 0.f; unsigned char mNClsROC[4] = {0}; @@ -71,8 +91,8 @@ GPUdi() void GPUdEdx::checkSubThresh(int roc) if (roc != mLastROC) { if (mNSubThresh && mCount + mNSubThresh <= MAX_NCL) { for (int i = 0; i < mNSubThresh; i++) { - mChargeTot[mCount] = mSubThreshMinTot; - mChargeMax[mCount++] = mSubThreshMinMax; + mChargeTot[mCount] = (GPUCA_DEDX_STORAGE_TYPE)(mSubThreshMinTot * scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::factor + scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::round); + mChargeMax[mCount++] = (GPUCA_DEDX_STORAGE_TYPE)(mSubThreshMinMax * scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::factor + scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::round); } mNClsROC[mLastROC] += mNSubThresh; mNClsROCSubThresh[mLastROC] += mNSubThresh; @@ -118,8 +138,8 @@ GPUdnii() void GPUdEdx::fillCluster(float qtot, float qmax, int padRow, float tr qmax /= qMaxCorr; qtot /= qTotCorr; - mChargeTot[mCount] = qtot; - mChargeMax[mCount++] = qmax; + mChargeTot[mCount] = (GPUCA_DEDX_STORAGE_TYPE)(qtot * scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::factor + scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::round); + mChargeMax[mCount++] = (GPUCA_DEDX_STORAGE_TYPE)(qmax * scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::factor + scalingFactor<GPUCA_DEDX_STORAGE_TYPE>::round); mNClsROC[roc]++; if (qtot < mSubThreshMinTot) { mSubThreshMinTot = qtot; @@ -136,7 +156,7 @@ GPUdi() void GPUdEdx::fillSubThreshold(int padRow, const GPUParam& GPUrestrict() mNSubThresh++; } -#endif // !HAVE_O2HEADERS +#endif // !HAVE_O2HEADERS || __OPENCL1__ } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.cxx b/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.cxx index 074b22517fab5..7cf0d7b3608a7 100644 --- a/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.cxx +++ b/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.cxx @@ -183,7 +183,7 @@ void TPCdEdxCalibrationSplines::setDefaultSplines() int offsets1[FSplines]; int offsets2[FSplines]; - auto defaultFnd2D = [&](float x1, float x2, float f[]) { + auto defaultFnd2D = [&](double x1, double x2, double f[]) { f[0] = 1.f; }; diff --git a/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.h b/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.h index 6fd5999d94340..cffd3f7cadccf 100644 --- a/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.h +++ b/GPU/GPUTracking/dEdx/TPCdEdxCalibrationSplines.h @@ -68,7 +68,7 @@ namespace gpu class TPCdEdxCalibrationSplines : public FlatObject { public: - typedef Spline2D<float, 1, 1> SplineType; + typedef Spline2D<float, 1> SplineType; /// _____________ Constructors / destructors __________________________ diff --git a/GPU/TPCFastTransformation/CMakeLists.txt b/GPU/TPCFastTransformation/CMakeLists.txt index 981b20ff8a8d0..1b91851677d6e 100644 --- a/GPU/TPCFastTransformation/CMakeLists.txt +++ b/GPU/TPCFastTransformation/CMakeLists.txt @@ -11,10 +11,15 @@ set(MODULE TPCFastTransformation) set(SRCS + SplineSpec.cxx + Spline.cxx + SplineHelper.cxx + Spline1DSpec.cxx Spline1D.cxx + Spline1DHelper.cxx + Spline2DSpec.cxx Spline2D.cxx - SplineHelper1D.cxx - SplineHelper2D.cxx + Spline2DHelper.cxx ChebyshevFit1D.cxx devtools/IrregularSpline1D.cxx devtools/IrregularSpline2D3D.cxx @@ -26,7 +31,7 @@ set(SRCS ) string(REPLACE ".cxx" ".h" HDRS_CINT_O2 "${SRCS}") -set(HDRS_CINT_O2 ${HDRS_CINT_O2} devtools/RegularSpline1D.h) +set(HDRS_CINT_O2 ${HDRS_CINT_O2} SplineUtil.h devtools/RegularSpline1D.h) if(${ALIGPU_BUILD_TYPE} STREQUAL "O2") o2_add_library(${MODULE} diff --git a/GPU/TPCFastTransformation/Spline.cxx b/GPU/TPCFastTransformation/Spline.cxx new file mode 100644 index 0000000000000..cdebd17ad68f1 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline.cxx @@ -0,0 +1,23 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline.cxx +/// \brief Implementation of Spline class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#include "Spline.h" + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +templateClassImp(GPUCA_NAMESPACE::gpu::Spline); +#endif + +template class GPUCA_NAMESPACE::gpu::Spline<float>; +template class GPUCA_NAMESPACE::gpu::Spline<double>; diff --git a/GPU/TPCFastTransformation/Spline.h b/GPU/TPCFastTransformation/Spline.h new file mode 100644 index 0000000000000..ef06f560c76e9 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline.h @@ -0,0 +1,117 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline.h +/// \brief Definition of Spline class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE_H + +#include "SplineSpec.h" + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ +/// +/// The Spline class performs a cubic spline interpolation on an two-dimensional nonunifom grid. +/// The class is an extension of the Spline1D class. +/// See Spline1D.h for more details. +/// +/// The spline S(x) approximates a function F(x):R^n->R^m, +/// with multi-dimensional domain and multi-dimensional codomain. +/// x belongs to [xmin,xmax]. +/// +/// --- Example of creating a spline --- +/// +/// constexpr int nDimX = 2, nDimY = 1; +/// int nKnots[nDimX] = {2, 3}; // 2 x 3 knots +/// int knotsU1[] = {0, 1}; // relative knot positions +/// int knotsU2[] = {0, 2, 5}; +/// int *knotsU[nDimX] = {knotsU1, knotsU2}; +/// +/// o2::gpu::Spline<float, nDimX, nDimY> spline(nKnots, knotsU); +/// +/// auto F = [&](const double x[], double f[]) { +/// f[0] = 1.f + x[0] + x[1] * x[1]; // F(x) +/// }; +/// double xMin[nDimX] = {0.f, 0.f}; +/// double xMax[nDimX] = {1.f, 1.f}; +/// spline.approximateFunction( xMin, xMax, F); // initialize spline to approximate F on [0., 1.]x[0., 1.] area +/// +/// float x[] = {.1, .3}; +/// float S = spline.interpolate(x); // interpolated value at (.1,.3) +/// +/// -- another way to create of the spline is: +/// +/// o2::gpu::Spline<float> spline(nDimX, nDimY, nKnots, knotsU ); +/// spline.interpolate(x, &S); +/// +/// --- See also SplineHelper::test(); +/// + +/// ================================================================================================== +/// +/// Declare the Spline class as a template with two optional parameters. +/// +/// Class specializations depend on the XdimT, YdimT values. They can be found in SplineSpecs.h +/// +/// \param DataT data type: float or double +/// \param XdimT +/// XdimT > 0 : the number of X dimensions is known at the compile time and is equal to XdimT +/// XdimT = 0 : the number of X dimensions will be set in the runtime +/// XdimT < 0 : the number of X dimensions will be set in the runtime, and it will not exceed abs(XdimT) +/// \param YdimT same for the Y dimensions +/// +template <typename DataT, int XdimT = 0, int YdimT = 0> +class Spline + : public SplineSpec<DataT, XdimT, YdimT, SplineUtil::getSpec(XdimT, YdimT)> +{ + typedef SplineContainer<DataT> TVeryBase; + typedef SplineSpec<DataT, XdimT, YdimT, SplineUtil::getSpec(XdimT, YdimT)> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + typedef typename TVeryBase::Knot Knot; + +#if !defined(GPUCA_GPUCODE) + using TBase::TBase; // inherit constructors + + /// Assignment operator + Spline& operator=(const Spline& v) + { + TVeryBase::cloneFromObject(v, nullptr); + return *this; + } +#else + /// Disable constructors for the GPU implementation + Spline() CON_DELETE; + Spline(const Spline&) CON_DELETE; +#endif + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) + /// read a class object from the file + static Spline* readFromFile(TFile& inpf, const char* name) + { + return (Spline*)TVeryBase::readFromFile(inpf, name); + } +#endif + +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline, 0); +#endif +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/Spline1D.cxx b/GPU/TPCFastTransformation/Spline1D.cxx index 03459763aa6f6..e5bf074ca410f 100644 --- a/GPU/TPCFastTransformation/Spline1D.cxx +++ b/GPU/TPCFastTransformation/Spline1D.cxx @@ -13,504 +13,11 @@ /// /// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation -#include "Rtypes.h" -#endif - #include "Spline1D.h" -#include <cmath> - -#if !defined(GPUCA_GPUCODE) // code invisible on GPU -#include <vector> -#include <algorithm> -#include <iostream> -#endif #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation -#include "TRandom.h" -#include "Riostream.h" -#include "TMath.h" -#include "SplineHelper1D.h" -#include "TCanvas.h" -#include "TNtuple.h" -#include "TH1.h" -#include "TFile.h" -#include "GPUCommonMath.h" - templateClassImp(GPUCA_NAMESPACE::gpu::Spline1D); - -#endif - -using namespace GPUCA_NAMESPACE::gpu; - -template <typename DataT> -void Spline1D<DataT>::destroy() -{ - /// See FlatObject for description - mNumberOfKnots = 0; - mUmax = 0; - mFdimensions = 0; - mXmin = 0.; - mXtoUscale = 1.; - mUtoKnotMap = nullptr; - mFparameters = nullptr; - FlatObject::destroy(); -} - -#if !defined(GPUCA_GPUCODE) -template <typename DataT> -void Spline1D<DataT>::cloneFromObject(const Spline1D& obj, char* newFlatBufferPtr) -{ - /// See FlatObject for description - const char* oldFlatBufferPtr = obj.mFlatBufferPtr; - FlatObject::cloneFromObject(obj, newFlatBufferPtr); - mNumberOfKnots = obj.mNumberOfKnots; - mUmax = obj.mUmax; - mFdimensions = obj.mFdimensions; - mXmin = obj.mXmin; - mXtoUscale = obj.mXtoUscale; - mUtoKnotMap = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mUtoKnotMap); - mFparameters = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mFparameters); -} - -template <typename DataT> -void Spline1D<DataT>::moveBufferTo(char* newFlatBufferPtr) -{ - /// See FlatObject for description - const char* oldFlatBufferPtr = mFlatBufferPtr; - FlatObject::moveBufferTo(newFlatBufferPtr); - mUtoKnotMap = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, mUtoKnotMap); - mFparameters = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, mFparameters); -} -#endif - -template <typename DataT> -void Spline1D<DataT>::setActualBufferAddress(char* actualFlatBufferPtr) -{ - /// See FlatObject for description - //mUtoKnotMap = FlatObject::relocatePointer(mFlatBufferPtr, actualFlatBufferPtr, mUtoKnotMap); - //mFparameters = FlatObject::relocatePointer(mFlatBufferPtr, actualFlatBufferPtr, mFparameters); - - const int uToKnotMapOffset = mNumberOfKnots * sizeof(Spline1D::Knot); - const int parametersOffset = uToKnotMapOffset + (mUmax + 1) * sizeof(int); - //int bufferSize = parametersOffset + getSizeOfParameters(mFdimensions); - - FlatObject::setActualBufferAddress(actualFlatBufferPtr); - mUtoKnotMap = reinterpret_cast<int*>(mFlatBufferPtr + uToKnotMapOffset); - mFparameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); -} - -template <typename DataT> -void Spline1D<DataT>::setFutureBufferAddress(char* futureFlatBufferPtr) -{ - /// See FlatObject for description - mUtoKnotMap = FlatObject::relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mUtoKnotMap); - mFparameters = FlatObject::relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mFparameters); - FlatObject::setFutureBufferAddress(futureFlatBufferPtr); -} - -#if !defined(GPUCA_GPUCODE) - -template <typename DataT> -void Spline1D<DataT>::recreate(int numberOfKnots, const int inputKnots[], int nFdimensions) -{ - /// Main constructor for an irregular spline - /// - /// Number of created knots may differ from the input values: - /// - Duplicated knots will be deleted - /// - At least 2 knots will be created - /// - /// \param numberOfKnots Number of knots in knots[] array - /// \param knots Array of relative knot positions (integer values) - /// - - FlatObject::startConstruction(); - - std::vector<int> knotU; - - { // sort knots - std::vector<int> tmp; - for (int i = 0; i < numberOfKnots; i++) { - tmp.push_back(inputKnots[i]); - } - std::sort(tmp.begin(), tmp.end()); - - knotU.push_back(0); // first knot at 0 - - for (unsigned int i = 1; i < tmp.size(); ++i) { - int u = tmp[i] - tmp[0]; - if (knotU.back() < u) { // remove duplicated knots - knotU.push_back(u); - } - } - if (knotU.back() < 1) { // at least 2 knots - knotU.push_back(1); - } - } - - mNumberOfKnots = knotU.size(); - mUmax = knotU.back(); - mFdimensions = nFdimensions; - mXmin = 0.; - mXtoUscale = 1.; - - const int uToKnotMapOffset = mNumberOfKnots * sizeof(Spline1D::Knot); - const int parametersOffset = uToKnotMapOffset + (mUmax + 1) * sizeof(int); - const int bufferSize = parametersOffset + getSizeOfParameters(mFdimensions); - - FlatObject::finishConstruction(bufferSize); - - mUtoKnotMap = reinterpret_cast<int*>(mFlatBufferPtr + uToKnotMapOffset); - mFparameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); - - Knot* s = getKnots(); - - for (int i = 0; i < mNumberOfKnots; i++) { - s[i].u = knotU[i]; - } - - for (int i = 0; i < mNumberOfKnots - 1; i++) { - s[i].Li = 1. / (s[i + 1].u - s[i].u); // do division in double - } - - s[mNumberOfKnots - 1].Li = 0.; // the value will not be used, we define it for consistency - - // Set up the map (integer U) -> (knot index) - - int* map = getUtoKnotMap(); - - const int iKnotMax = mNumberOfKnots - 2; - - // - // With iKnotMax=nKnots-2 we map the U==Umax coordinate to the last [nKnots-2, nKnots-1] segment. - // This trick allows one to avoid a special condition for this edge case. - // Any U from [0,Umax] is mapped to some knot_i such, that the next knot_i+1 always exist - // - - for (int u = 0, iKnot = 0; u <= mUmax; u++) { - if ((knotU[iKnot + 1] == u) && (iKnot < iKnotMax)) { - iKnot = iKnot + 1; - } - map[u] = iKnot; - } - - for (int i = 0; i < getNumberOfParameters(mFdimensions); i++) { - mFparameters[i] = 0.f; - } -} - -template <typename DataT> -void Spline1D<DataT>::recreate(int numberOfKnots, int nFdimensions) -{ - /// Constructor for a regular spline - /// \param numberOfKnots Number of knots - - if (numberOfKnots < 2) { - numberOfKnots = 2; - } - - std::vector<int> knots(numberOfKnots); - for (int i = 0; i < numberOfKnots; i++) { - knots[i] = i; - } - recreate(numberOfKnots, knots.data(), nFdimensions); -} - #endif -template <typename DataT> -void Spline1D<DataT>::print() const -{ - printf(" Compact Spline 1D: \n"); - printf(" mNumberOfKnots = %d \n", mNumberOfKnots); - printf(" mUmax = %d\n", mUmax); - printf(" mUtoKnotMap = %p \n", (void*)mUtoKnotMap); - printf(" knots: "); - for (int i = 0; i < mNumberOfKnots; i++) { - printf("%d ", (int)getKnot(i).u); - } - printf("\n"); -} - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) -template <typename DataT> -int Spline1D<DataT>::writeToFile(TFile& outf, const char* name) -{ - /// write a class object to the file - return FlatObject::writeToFile(*this, outf, name); -} - -template <typename DataT> -Spline1D<DataT>* Spline1D<DataT>::readFromFile(TFile& inpf, const char* name) -{ - /// read a class object from the file - return FlatObject::readFromFile<Spline1D<DataT>>(inpf, name); -} - -template <typename DataT> -void Spline1D<DataT>::approximateFunction(DataT xMin, DataT xMax, - std::function<void(DataT x, DataT f[/*mFdimensions*/])> F, - int nAxiliaryDataPoints) -{ - /// Approximate F with this spline - setXrange(xMin, xMax); - SplineHelper1D<DataT> helper; - helper.approximateFunction(*this, xMin, xMax, F, nAxiliaryDataPoints); -} - -template <typename DataT> -int Spline1D<DataT>::test(const bool draw, const bool drawDataPoints) -{ - using namespace std; - - // input function F - - const int Ndim = 5; - const int Fdegree = 4; - double Fcoeff[Ndim][2 * (Fdegree + 1)]; - - auto F = [&](DataT x, DataT f[]) -> void { - double cosx[Fdegree + 1], sinx[Fdegree + 1]; - double xi = 0; - for (int i = 0; i <= Fdegree; i++, xi += x) { - GPUCommonMath::SinCos(xi, sinx[i], cosx[i]); - } - for (int dim = 0; dim < Ndim; dim++) { - f[dim] = 0; // Fcoeff[0]/2; - for (int i = 1; i <= Fdegree; i++) { - f[dim] += Fcoeff[dim][2 * i] * cosx[i] + Fcoeff[dim][2 * i + 1] * sinx[i]; - } - } - }; - - TCanvas* canv = nullptr; - TNtuple* nt = nullptr; - TNtuple* knots = nullptr; - - auto ask = [&]() -> bool { - if (!canv) { - return 0; - } - canv->Update(); - cout << "type 'q ' to exit" << endl; - std::string str; - std::getline(std::cin, str); - return (str != "q" && str != ".q"); - }; - - std::cout << "Test 1D interpolation with the compact spline" << std::endl; - - int nTries = 100; - - if (draw) { - canv = new TCanvas("cQA", "Spline1D QA", 1000, 600); - nTries = 10000; - } - - double statDf1 = 0; - double statDf2 = 0; - double statDf1D = 0; - double statN = 0; - - int seed = 1; - - for (int itry = 0; itry < nTries; itry++) { - - // init random F - for (int dim = 0; dim < Ndim; dim++) { - gRandom->SetSeed(seed++); - for (int i = 0; i < 2 * (Fdegree + 1); i++) { - Fcoeff[dim][i] = gRandom->Uniform(-1, 1); - } - } - - // spline - - int nKnots = 4; - const int uMax = nKnots * 3; - - Spline1D spline1; - int knotsU[nKnots]; - - do { // set knots randomly - knotsU[0] = 0; - double du = 1. * uMax / (nKnots - 1); - for (int i = 1; i < nKnots; i++) { - knotsU[i] = (int)(i * du); // + gRandom->Uniform(-du / 3, du / 3); - } - knotsU[nKnots - 1] = uMax; - spline1.recreate(nKnots, knotsU, Ndim); - if (nKnots != spline1.getNumberOfKnots()) { - cout << "warning: n knots changed during the initialisation " << nKnots - << " -> " << spline1.getNumberOfKnots() << std::endl; - continue; - } - } while (0); - - std::string err = FlatObject::stressTest(spline1); - if (!err.empty()) { - cout << "error at FlatObject functionality: " << err << endl; - return -1; - } else { - // cout << "flat object functionality is ok" << endl; - } - - nKnots = spline1.getNumberOfKnots(); - int nAxiliaryPoints = 1; - Spline1D spline2; //(spline1); - spline2.cloneFromObject(spline1, nullptr); - - spline1.approximateFunction(0., TMath::Pi(), F, nAxiliaryPoints); - - //if (itry == 0) - { - TFile outf("testSpline1D.root", "recreate"); - if (outf.IsZombie()) { - cout << "Failed to open output file testSpline1D.root " << std::endl; - } else { - const char* name = "spline1Dtest"; - spline1.writeToFile(outf, name); - Spline1D<DataT>* p = spline1.readFromFile(outf, name); - if (p == nullptr) { - cout << "Failed to read Spline1D from file testSpline1D.root " << std::endl; - } else { - spline1 = *p; - } - outf.Close(); - } - } - - SplineHelper1D<DataT> helper; - helper.setSpline(spline2, Ndim, nAxiliaryPoints); - helper.approximateFunctionGradually(spline2, 0., TMath::Pi(), F, nAxiliaryPoints); - - // 1-D splines for each dimension - Spline1D splines3[Ndim]; - { - for (int dim = 0; dim < Ndim; dim++) { - auto F3 = [&](DataT u, DataT f[]) -> void { - DataT ff[Ndim]; - F(u, ff); - f[0] = ff[dim]; - }; - splines3[dim].recreate(nKnots, knotsU, 1); - splines3[dim].approximateFunction(0., TMath::Pi(), F3, nAxiliaryPoints); - } - } - - double stepX = 1.e-2; - for (double x = 0; x < TMath::Pi(); x += stepX) { - DataT f[Ndim], s1[Ndim], s2[Ndim]; - F(x, f); - spline1.interpolate(x, s1); - spline2.interpolate(x, s2); - for (int dim = 0; dim < Ndim; dim++) { - statDf1 += (s1[dim] - f[dim]) * (s1[dim] - f[dim]); - statDf2 += (s2[dim] - f[dim]) * (s2[dim] - f[dim]); - DataT s1D = splines3[dim].interpolate(x); - statDf1D += (s1D - s1[dim]) * (s1D - s1[dim]); - } - statN += Ndim; - } - // cout << "std dev : " << sqrt(statDf1 / statN) << std::endl; - - if (draw) { - delete nt; - delete knots; - nt = new TNtuple("nt", "nt", "u:f:s"); - DataT drawMax = -1.e20; - DataT drawMin = 1.e20; - DataT stepX = 1.e-4; - for (double x = 0; x < TMath::Pi(); x += stepX) { - DataT f[Ndim], s[Ndim]; - F(x, f); - spline1.interpolate(x, s); - nt->Fill(spline1.convXtoU(x), f[0], s[0]); - drawMax = std::max(drawMax, std::max(f[0], s[0])); - drawMin = std::min(drawMin, std::min(f[0], s[0])); - } - - nt->SetMarkerStyle(8); - - { - TNtuple* ntRange = new TNtuple("ntRange", "nt", "u:f"); - drawMin -= 0.1 * (drawMax - drawMin); - - ntRange->Fill(0, drawMin); - ntRange->Fill(0, drawMax); - ntRange->Fill(uMax, drawMin); - ntRange->Fill(uMax, drawMax); - ntRange->SetMarkerColor(kWhite); - ntRange->SetMarkerSize(0.1); - ntRange->Draw("f:u", "", ""); - delete ntRange; - } - - nt->SetMarkerColor(kGray); - nt->SetMarkerSize(2.); - nt->Draw("f:u", "", "P,same"); - - nt->SetMarkerSize(.5); - nt->SetMarkerColor(kBlue); - nt->Draw("s:u", "", "P,same"); - - knots = new TNtuple("knots", "knots", "type:u:s"); - for (int i = 0; i < nKnots; i++) { - double u = spline1.getKnot(i).u; - DataT s[Ndim]; - spline1.interpolate(spline1.convUtoX(u), s); - knots->Fill(1, u, s[0]); - } - - knots->SetMarkerStyle(8); - knots->SetMarkerSize(1.5); - knots->SetMarkerColor(kRed); - knots->SetMarkerSize(1.5); - knots->Draw("s:u", "type==1", "same"); // knots - - if (drawDataPoints) { - for (int j = 0; j < helper.getNumberOfDataPoints(); j++) { - const typename SplineHelper1D<DataT>::DataPoint& p = helper.getDataPoint(j); - if (p.isKnot) { - continue; - } - DataT s[Ndim]; - spline1.interpolate(spline1.convUtoX(p.u), s); - knots->Fill(2, p.u, s[0]); - } - knots->SetMarkerColor(kBlack); - knots->SetMarkerSize(1.); - knots->Draw("s:u", "type==2", "same"); // data points - } - - if (!ask()) { - break; - } - } // draw - } - //delete canv; - //delete nt; - //delete knots; - - statDf1 = sqrt(statDf1 / statN); - statDf2 = sqrt(statDf2 / statN); - statDf1D = sqrt(statDf1D / statN); - - cout << "\n std dev for Compact Spline : " << statDf1 << " / " << statDf2 - << std::endl; - cout << " mean difference between 1-D and " << Ndim - << "-D splines : " << statDf1D << std::endl; - - if (statDf1 < 0.05 && statDf2 < 0.06 && statDf1D < 1.e-20) { - cout << "Everything is fine" << endl; - } else { - cout << "Something is wrong!!" << endl; - return -2; - } - return 0; -} - -#endif // GPUCA_ALIGPUCODE - template class GPUCA_NAMESPACE::gpu::Spline1D<float>; -template class GPUCA_NAMESPACE::gpu::Spline1D<double>; +template class GPUCA_NAMESPACE::gpu::Spline1D<double>; \ No newline at end of file diff --git a/GPU/TPCFastTransformation/Spline1D.h b/GPU/TPCFastTransformation/Spline1D.h index 57c53786f9f43..772231386d112 100644 --- a/GPU/TPCFastTransformation/Spline1D.h +++ b/GPU/TPCFastTransformation/Spline1D.h @@ -16,458 +16,162 @@ #ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE1D_H #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE1D_H -#include "GPUCommonDef.h" -#include "FlatObject.h" -#if !defined(GPUCA_GPUCODE) -#include <functional> -#endif - -class TFile; +#include "Spline1DSpec.h" namespace GPUCA_NAMESPACE { namespace gpu { +/// The Spline1D class performs a cubic spline interpolation on a one-dimensional non-uniform grid. /// -/// The Spline1D class performs a cubic spline interpolation on an one-dimensional nonunifom grid. -/// -/// The class is a flat C structure. It inherits from the FlatObjects. +/// The class is a flat C structure. It inherits from the FlatObject. /// No virtual methods, no ROOT types are used. /// /// --- Interpolation --- /// -/// The spline approximates a function F:[xMin,xMax]->R^m +/// The spline S(x) approximates a function F(x):[Xmin,Xmax]->Y +/// X is one-dimensional, Y may be multi-dimensional. /// -/// The function parameter is called X, the function value is called F. F value may be multi-dimensional. -/// The spline value is called S(x). +/// The spline has n knots x_i. The spline value S(x_i) and its derivative S'(x_i) are stored for every knot. +/// Inbetween the knots, S(x) is evaluated via interpolation by 3-rd degree polynomials. /// -/// The interpolation is performed on N knots by 3-rd degree polynoms. -/// It uses spline values S_i and spline derivatives D_i==S'_i at the knots. -/// The polynoms and they first derivatives are always continuous at the knots. -/// Depending on the initialization of the derivatives D_i, the second derivative may or may not be continious. +/// The spline S(x) and its first derivative are continuous. +/// Depending on the initialization of the derivatives S'(x_i), +/// the second derivative may or may not be continuous at the knots. /// /// --- Knots --- /// -/// Knot positions are not completelly irregular. -/// There is an internal scaled coordinate, called U, where all N knots have integer positions: +/// The knots are not entirely irregular. +/// There is an internal scaled coordinate, called U, where all N knots have some integer positions: /// {U0==0, U1, .., Un-1==Umax}. /// It is implemented this way for fast matching of any X value to its neighboring knots. /// -/// For example, three knots with U coordinates U_i={0, 3, 5}, -/// being stretched on the X segment [0., 1.], will have X coordinates X_i={0., 3./5., 1.} +/// For example, three knots with U coordinates u_i={0, 3, 5}, +/// being stretched on the X segment [0., 1.], will have X coordinates x_i={0., 3./5., 1.} /// -/// For a few reasons, it is better to minimize gaps between knots. -/// A spline with knots {0,4,8} is the same as the spline with knots {0,1,2}, -/// but the later one uses less memory. +/// For a few reasons, it is better to keep U-gaps between the knots minimal. +/// A spline with knots u_i={0,1,2} is mathematically the same as the spline with knots u_i={0,2,4}. +/// However, it uses less memory. /// /// The minimal number of knots is 2. /// +/// --- Output dimensionality --- +/// +/// There are two ways to set the dimensionality of Y - either in the constructor or as a template argument: +/// +/// Spline1D<float> s( nYdimensions, nKnots ); +/// Spline1D<float, nYdimensions> s( nKnots ); +/// +/// The second implementation works faster. Use it when nYdimensions is known at the compile time. +/// +/// There is also a variation of the first specification which lets the compiler know +/// the maximal possible number of Y dimensions: +/// +/// Spline1D<float, -nYdimensionsMax> s( nYdimensions, nKnots ); +/// +/// This specification does not allocate any variable-size arrays and therefore compiles for the GPU. +/// /// ---- External storage of spline parameters for a given F --- /// /// One can store all F-dependent spline parameters outside of the spline object -/// and provide them at each call of the interpolation. -/// To do so, create a spline with Fdimentions=0, -/// create F parameters via SplineHelper1D class, and use interpolateU(..) method for interpoltion. +/// and provide them at each interpolation call. +/// To do so, create a spline with nYdimensions=0; create spline parameters for F via Spline1DHelper class; +/// then use special interpolateU(..) methods for the interpolation. /// -/// This feature allows one to use the same spline object for approximation of different functions +/// This feature allows one to use the same spline object for the approximation of different functions /// on the same grid of knots. /// /// ---- Creation of a spline ---- /// -/// The splines are best-fit splines. It means, that spline values S_i and derivatives D_i at knots -/// are calibrated such, that they minimize the integral difference between S(x) and F(x). -/// This integral difference is caluclated at all integer values of U coordinate (in particular, at all knots) -/// and at extra nAxiliaryPoints points between the integers. +/// The spline is supposed to be a best-fit spline, created by the approximateFunction() method. +/// +/// Best-fit means that the spline values S_i and its derivatives D_i at the knots +/// are adjusted to minimize the overall difference between S(x) and F(x). +/// The spline constructed this way is much more accurate than a classical interpolation spline. /// -/// nAxiliaryPoints can be set as a parameter of approximateFunction() method. -/// With nAxiliaryPoints==3 the approximation accuracy is noticeably better than with 1 or 2. +/// The difference to F() is minimized at all integer values of U coordinate (in particular, at all knots) +/// and at extra nAuxiliaryPoints points between the integer numbers. +/// +/// nAuxiliaryPoints is given as a parameter of approximateFunction() method. +/// With nAuxiliaryPoints==3, the approximation accuracy is noticeably better than the one with 1 or 2. /// Higher values usually give a little improvement over 3. /// -/// The value of nAxiliaryPoints has no influence on the interpolation speed, -/// it can only slow down the approximateFunction() method. +/// The number of auxiliary points does not influence the interpolation speed, +/// but a high number can slow down the spline's creation. /// -/// One can also create a spline in a classical way using corresponding method from SplineHelper1D. +/// It is also possible to construct the spline classically - by taking F(x) values only at knots and making +/// the first and the second derivatives of S(x) continuous. Use the corresponding method from Spline1DHelper. /// /// ---- Example of creating a spline ---- /// -/// auto F = [&](float x,float &f) { +/// auto F = [&](double x, double &f) { // a function to be approximated /// f[0] = x*x+3.f; // F(x) /// }; /// -/// const int nKnots=3; -/// int knots[nKnots] = {0, 1, 5}; -/// Spline1D<float> spline( nKnots, knots, 1 ); // prepare memory for 1-dimensional F +/// const int nKnots = 3; +/// +/// int knots[nKnots] = {0, 1, 5}; // relative(!) knot positions /// -/// spline.approximateFunction(0., 1., F); // initialize spline to approximate F on a segment [0., 1.] +/// Spline1D<float,1> spline( nKnots, knots ); // create 1-dimensional spline with the knots /// -/// float f = spline.interpolate(0.2); // interpolated value at x==0.2 +/// spline.approximateFunction(0., 1., F); // let the spline approximate F on a segment [0., 1.] +/// +/// float s = spline.interpolate(0.2); // interpolated value at x==0.2 /// /// --- See also Spline1D::test() method for examples /// + +/// ================================================================================================== /// -template <typename DataT> -class Spline1D : public FlatObject +/// Declare the Spline1D class as a template with one optional parameters. +/// +/// Class specializations depend on the XdimT, YdimT values. They can be found in SplineSpecs.h +/// +/// \param DataT data type: float or double +/// \param YdimT +/// YdimT > 0 : the number of Y dimensions is known at the compile time and is equal to XdimT +/// YdimT = 0 : the number of Y dimensions will be set in the runtime +/// YdimT < 0 : the number of Y dimensions will be set in the runtime, and it will not exceed abs(YdimT) +/// +template <typename DataT, int YdimT = 0> +class Spline1D + : public Spline1DSpec<DataT, YdimT, SplineUtil::getSpec(YdimT)> { - public: - /// - /// \brief The struct Knot represents a knot(i) and the interval [ knot(i), knot(i+1) ] - /// - struct Knot { - DataT u; ///< u coordinate of the knot i (an integer number in float format) - DataT Li; ///< inverse length of the [knot_i, knot_{i+1}] segment ( == 1.f/ a (small) integer number) - }; + typedef Spline1DContainer<DataT> TVeryBase; + typedef Spline1DSpec<DataT, YdimT, SplineUtil::getSpec(YdimT)> TBase; - /// _____________ Version control __________________________ - - /// Version number - GPUhd() static constexpr int getVersion() { return 1; } - - /// _____________ Constructors / destructors __________________________ + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + typedef typename TVeryBase::Knot Knot; #if !defined(GPUCA_GPUCODE) - /// Constructor for a regular spline. - Spline1D(int numberOfKnots = 2, int nFDimensions = 1); - - /// Constructor for an irregular spline. - Spline1D(int numberOfKnots, const int knots[], int nFDimensions); - - /// Copy constructor - Spline1D(const Spline1D&); + using TBase::TBase; // inherit constructors /// Assignment operator - Spline1D& operator=(const Spline1D&); + Spline1D& operator=(const Spline1D& v) + { + TVeryBase::cloneFromObject(v, nullptr); + return *this; + } #else /// Disable constructors for the GPU implementation Spline1D() CON_DELETE; Spline1D(const Spline1D&) CON_DELETE; - Spline1D& operator=(const Spline1D&) CON_DELETE; -#endif - - /// Destructor - ~Spline1D() CON_DEFAULT; - - /// _______________ Construction interface ________________________ - -#if !defined(GPUCA_GPUCODE) - /// Constructor for a regular spline. - void recreate(int numberOfKnots, int nFDimensions); - - /// Constructor for an irregular spline - void recreate(int numberOfKnots, const int knots[], int nFDimensions); -#endif - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - /// approximate a function F with this spline. - void approximateFunction(DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*mFdimensions*/])> F, - int nAxiliaryDataPoints = 4); #endif - /// _______________ Main functionality ________________________ - - /// Get interpolated value for F(x) - GPUhd() void interpolate(DataT x, GPUgeneric() DataT Sx[/*mFdimensions*/]) const; - - /// Get interpolated value for the first dimension of F(x). (Simplified interface for 1D) - GPUhd() DataT interpolate(DataT x) const; - - /// _______________ IO ________________________ - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - /// write a class object to the file - int writeToFile(TFile& outf, const char* name); - +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) /// read a class object from the file - static Spline1D* readFromFile(TFile& inpf, const char* name); -#endif - - /// ================ Expert tools ================================ - - /// Get interpolated value {S(u): 1D -> nFdim} at the segment [knotL, next knotR] - /// using the spline values Sl, Sr and the slopes Dl, Dr - template <typename T> - GPUhd() static void interpolateU(int nFdim, const Knot& knotL, - GPUgeneric() const T Sl[/*nFdim*/], GPUgeneric() const T Dl[/*nFdim*/], - GPUgeneric() const T Sr[/*nFdim*/], GPUgeneric() const T Dr[/*nFdim*/], - DataT u, GPUgeneric() T Su[/*nFdim*/]); - - /// Get interpolated value for an nFdim-dimensional F(u) using spline parameters Fparameters. - /// Fparameters can be created via SplineHelper1D. A border check for u is performed. - GPUhd() void interpolateU(int nFdim, GPUgeneric() const DataT Fparameters[], - DataT u, GPUgeneric() DataT Su[/*nFdim*/]) const; - - /// Get interpolated value for an nFdim-dimensional F(u) using spline parameters Fparameters. - /// Fparameters can be created via SplineHelper1D. No border check for u is performed. - GPUhd() void interpolateUnonSafe(int nFdim, GPUgeneric() const DataT Fparameters[], - DataT u, GPUgeneric() DataT Su[/*nFdim*/]) const; - - /// _______________ Getters ________________________ - - /// Get U coordinate of the last knot - GPUhd() int getUmax() const { return mUmax; } - - /// Get minimal required alignment for the spline parameters - static size_t getParameterAlignmentBytes(int nFdim) - { - size_t s = 2 * sizeof(DataT) * nFdim; - return (s < 16) ? s : 16; - } - - /// Size of the parameter array in bytes - GPUhd() size_t getSizeOfParameters(int nFdim) const + static Spline1D* readFromFile(TFile& inpf, const char* name) { - return sizeof(DataT) * (size_t)getNumberOfParameters(nFdim); + return (Spline1D*)TVeryBase::readFromFile(inpf, name); } - - /// Number of parameters - GPUhd() int getNumberOfParameters(int nFdim) const - { - return (2 * nFdim) * mNumberOfKnots; - } - - /// Get number of knots - GPUhd() int getNumberOfKnots() const { return mNumberOfKnots; } - - /// Get the array of knots - GPUhd() const Spline1D::Knot* getKnots() const { return reinterpret_cast<const Spline1D::Knot*>(mFlatBufferPtr); } - - /// Get i-th knot - GPUhd() const Spline1D::Knot& getKnot(int i) const { return getKnots()[i < 0 ? 0 : (i >= mNumberOfKnots ? mNumberOfKnots - 1 : i)]; } - - /// Get index of an associated knot for a given U coordinate. Performs a border check. - GPUhd() int getKnotIndexU(DataT u) const; - - /// Get number of F dimensions - GPUhd() int getFdimensions() const { return mFdimensions; } - - /// Get number of F parameters - GPUhd() DataT* getFparameters() { return mFparameters; } - - /// Get number of F parameters - GPUhd() const DataT* getFparameters() const { return mFparameters; } - - /// _______________ Getters with no border check ________________________ - - /// Get i-th knot. No border check performed! - GPUhd() const Spline1D::Knot& getKnotNonSafe(int i) const { return getKnots()[i]; } - - /// Get index of an associated knot for a given U coordinate. No border check preformed! - GPUhd() int getKnotIndexUnonSafe(DataT u) const; - - /// _______________ Technical stuff ________________________ - - /// Get a map (integer U -> corresponding knot index) - GPUhd() const int* getUtoKnotMap() const { return mUtoKnotMap; } - - /// Convert X coordinate to U - GPUhd() DataT convXtoU(DataT x) const { return (x - mXmin) * mXtoUscale; } - - /// Convert U coordinate to X - GPUhd() DataT convUtoX(DataT u) const { return mXmin + u / mXtoUscale; } - - /// Get Xmin - GPUhd() DataT getXmin() const { return mXmin; } - - /// Get XtoUscale - GPUhd() DataT getXtoUscale() const { return mXtoUscale; } - - /// Set X range - void setXrange(DataT xMin, DataT xMax); - - /// Print method - void print() const; - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation - /// Test the class functionality - static int test(const bool draw = 0, const bool drawDataPoints = 1); #endif - /// _____________ FlatObject functionality, see FlatObject class for description ____________ - - /// Memory alignment - - using FlatObject::getBufferAlignmentBytes; - using FlatObject::getClassAlignmentBytes; - - /// Construction interface - -#if !defined(GPUCA_GPUCODE) - void cloneFromObject(const Spline1D& obj, char* newFlatBufferPtr); -#endif - void destroy(); - - /// Making the parameter buffer external - - using FlatObject::releaseInternalBuffer; - -#if !defined(GPUCA_GPUCODE) - void moveBufferTo(char* newBufferPtr); -#endif - - /// Moving the class with its external buffer to another location - - void setActualBufferAddress(char* actualFlatBufferPtr); - void setFutureBufferAddress(char* futureFlatBufferPtr); - - private: - /// Non-const accessor to knots array - Spline1D::Knot* getKnots() { return reinterpret_cast<Spline1D::Knot*>(mFlatBufferPtr); } - - /// Non-const accessor to U->knots map - int* getUtoKnotMap() { return mUtoKnotMap; } - - /// _____________ Data members ____________ - - int mNumberOfKnots; ///< n knots on the grid - int mUmax; ///< U of the last knot - DataT mXmin; ///< X of the first knot - DataT mXtoUscale; ///< a scaling factor to convert X to U - int mFdimensions; ///< number of F dimensions - int* mUtoKnotMap; //! (transient!!) pointer to (integer U -> knot index) map inside the mFlatBufferPtr array - DataT* mFparameters; //! (transient!!) F-dependent parameters of the spline #ifndef GPUCA_ALIROOT_LIB - ClassDefNV(Spline1D<DataT>, 1); + ClassDefNV(Spline1D, 0); #endif }; -/// -/// ======================================================================================================== -/// Inline implementations of some methods -/// ======================================================================================================== -/// - -#if !defined(GPUCA_GPUCODE) -template <typename DataT> -Spline1D<DataT>::Spline1D(int numberOfKnots, int nFdimensions) - : FlatObject() -{ - recreate(numberOfKnots, nFdimensions); -} - -template <typename DataT> -Spline1D<DataT>::Spline1D(int numberOfKnots, const int knots[], int nFdimensions) - : FlatObject() -{ - recreate(numberOfKnots, knots, nFdimensions); -} - -template <typename DataT> -Spline1D<DataT>::Spline1D(const Spline1D& spline) - : FlatObject() -{ - cloneFromObject(spline, nullptr); -} - -template <typename DataT> -Spline1D<DataT>& Spline1D<DataT>::operator=(const Spline1D& spline) -{ - cloneFromObject(spline, nullptr); - return *this; -} -#endif - -template <typename DataT> -GPUhd() void Spline1D<DataT>::interpolate(DataT x, GPUgeneric() DataT Sx[/*mFdimensions*/]) const -{ - /// Get interpolated value for F(x) - interpolateU(mFdimensions, mFparameters, convXtoU(x), Sx); -} - -template <typename DataT> -GPUhd() DataT Spline1D<DataT>::interpolate(DataT x) const -{ - /// Simplified interface for 1D: get interpolated value for the first dimension of F(x) - DataT u = convXtoU(x); - int iknot = getKnotIndexU(u); - const DataT* d = mFparameters + (2 * iknot) * mFdimensions; - DataT S = 0.; - interpolateU(1, getKnotNonSafe(iknot), &(d[0]), &(d[mFdimensions]), - &(d[2 * mFdimensions]), &(d[3 * mFdimensions]), u, &S); - return S; -} - -template <typename DataT> -template <typename T> -GPUhd() void Spline1D<DataT>::interpolateU(int nFdim, const Spline1D<DataT>::Knot& knotL, - GPUgeneric() const T Sl[/*nFdim*/], GPUgeneric() const T Dl[/*nFdim*/], - GPUgeneric() const T Sr[/*nFdim*/], GPUgeneric() const T Dr[/*nFdim*/], - DataT u, GPUgeneric() T Su[/*nFdim*/]) -{ - /// A static method. - /// Gives interpolated value of N-dimensional S(u) at u - /// input: Sl,Dl,Sr,Dr[nFdim] - N-dim function values and slopes at knots {knotL,knotR} - /// output: Su[nFdim] - N-dim interpolated value for S(u) - - T uu = T(u - knotL.u); - T li = T(knotL.Li); - T v = uu * li; // scaled u - for (int dim = 0; dim < nFdim; ++dim) { - T df = (Sr[dim] - Sl[dim]) * li; - T a = Dl[dim] + Dr[dim] - df - df; - T b = df - Dl[dim] - a; - Su[dim] = ((a * v + b) * v + Dl[dim]) * uu + Sl[dim]; - } - - /* another way to calculate f(u): - T uu = T(u - knotL.u); - T v = uu * T(knotL.Li); // scaled u - T vm1 = v-1; - T v2 = v * v; - float cSr = v2*(3-2*v); - float cSl = 1-cSr; - float cDl = v*vm1*vm1*knotL.L; - float cDr = v2*vm1*knotL.L; - return cSl*Sl + cSr*Sr + cDl*Dl + cDr*Dr; - */ -} - -template <typename DataT> -GPUhdi() void Spline1D<DataT>::interpolateU(int nFdim, GPUgeneric() const DataT parameters[], DataT u, GPUgeneric() DataT Su[/*nFdim*/]) const -{ - /// Get interpolated value for F(u) using given spline parameters with a border check - int iknot = getKnotIndexU(u); - const DataT* d = parameters + (2 * iknot) * nFdim; - interpolateU(nFdim, getKnotNonSafe(iknot), &(d[0]), &(d[nFdim]), &(d[2 * nFdim]), &(d[3 * nFdim]), u, Su); -} - -template <typename DataT> -GPUhdi() void Spline1D<DataT>::interpolateUnonSafe(int nFdim, GPUgeneric() const DataT parameters[], DataT u, GPUgeneric() DataT Su[/*nFdim*/]) const -{ - /// Get interpolated value for F(u) using given spline parameters without border check - int iknot = getKnotIndexUnonSafe(u); - const DataT* d = parameters + (2 * iknot) * nFdim; - interpolateU(nFdim, getKnotNonSafe(iknot), &(d[0]), &(d[nFdim]), &(d[2 * nFdim]), &(d[3 * nFdim]), u, Su); -} - -template <typename DataT> -GPUhdi() int Spline1D<DataT>::getKnotIndexU(DataT u) const -{ - /// Get i: u is in [knot_i, knot_{i+1}) interval - /// when u is otside of [0, mUmax], return the edge intervals - int iu = (int)u; - if (iu < 0) { - iu = 0; - } - if (iu > mUmax) { - iu = mUmax; - } - return getUtoKnotMap()[iu]; -} - -template <typename DataT> -GPUhdi() int Spline1D<DataT>::getKnotIndexUnonSafe(DataT u) const -{ - /// Get i: u is in [knot_i, knot_{i+1}) interval - /// no border check! u must be in [0,mUmax] - return getUtoKnotMap()[(int)u]; -} - -template <typename DataT> -void Spline1D<DataT>::setXrange(DataT xMin, DataT xMax) -{ - mXmin = xMin; - mXtoUscale = mUmax / (((double)xMax) - xMin); -} - } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/TPCFastTransformation/Spline1DHelper.cxx b/GPU/TPCFastTransformation/Spline1DHelper.cxx new file mode 100644 index 0000000000000..72840fa10a1ee --- /dev/null +++ b/GPU/TPCFastTransformation/Spline1DHelper.cxx @@ -0,0 +1,744 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline1DHelper.cxx +/// \brief Implementation of Spline1DHelper class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + +#include "Spline1DHelper.h" +#include "TMath.h" +#include "TMatrixD.h" +#include "TVectorD.h" +#include "TDecompBK.h" +#include <vector> + +#include "TRandom.h" +#include "TMath.h" +#include "TCanvas.h" +#include "TNtuple.h" +#include "TFile.h" +#include "GPUCommonMath.h" +#include <iostream> + +templateClassImp(GPUCA_NAMESPACE::gpu::Spline1DHelper); + +using namespace GPUCA_NAMESPACE::gpu; + +template <typename DataT> +Spline1DHelper<DataT>::Spline1DHelper() : mError(), mSpline(), mFdimensions(0) +{ +} + +template <typename DataT> +int Spline1DHelper<DataT>::storeError(int code, const char* msg) +{ + mError = msg; + return code; +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunctionClassic(Spline1DContainer<DataT>& spline, + double xMin, double xMax, std::function<void(double x, double f[/*spline.getFdimensions()*/])> F) +{ + /// Create classic spline parameters for a given input function F + /// set slopes at the knots such, that the second derivative of the spline is continious. + + int Ndim = spline.getYdimensions(); + const int nKnots = spline.getNumberOfKnots(); + + TMatrixD A(nKnots, nKnots); + + A.Zero(); + + /* + const Spline1D::Knot& knot0 = spline.getKnot(i); + double x = (u - knot0.u) * knot0.Li; // scaled u + double cS1 = (6 - 12*x)*knot0.Li*knot0.Li; + double cZ0 = (6*x-4)*knot0.Li; + double cZ1 = (6*x-2)*knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + */ + + // second derivative at knot0 is 0 + { + const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(0); + double cZ0 = (-4) * knot0.Li; + double cZ1 = (-2) * knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + A(0, 0) = cZ0; + A(0, 1) = cZ1; + } + + // second derivative at knot nKnots-1 is 0 + { + const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(nKnots - 2); + double cZ0 = (6 - 4) * knot0.Li; + double cZ1 = (6 - 2) * knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + A(nKnots - 1, nKnots - 2) = cZ0; + A(nKnots - 1, nKnots - 1) = cZ1; + } + + // second derivative at other knots is same from the left and from the right + for (int i = 1; i < nKnots - 1; i++) { + const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(i - 1); + double cZ0 = (6 - 4) * knot0.Li; + double cZ1_0 = (6 - 2) * knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + + const typename Spline1D<DataT>::Knot& knot1 = spline.getKnot(i); + double cZ1_1 = (-4) * knot1.Li; + double cZ2 = (-2) * knot1.Li; + // f''(u) = cS2*(f2-f1) + cZ1_1*z1 + cZ2*z2; + A(i, i - 1) = cZ0; + A(i, i) = cZ1_0 - cZ1_1; + A(i, i + 1) = -cZ2; + } + + A.Invert(); + + spline.setXrange(xMin, xMax); + DataT* parameters = spline.getParameters(); + + TVectorD b(nKnots); + b.Zero(); + + double uToXscale = (((double)xMax) - xMin) / spline.getUmax(); + + for (int i = 0; i < nKnots; ++i) { + const typename Spline1D<DataT>::Knot& knot = spline.getKnot(i); + double u = knot.u; + double f[Ndim]; + F(xMin + u * uToXscale, f); + for (int dim = 0; dim < Ndim; dim++) { + parameters[(2 * i) * Ndim + dim] = f[dim]; + } + } + + for (int dim = 0; dim < Ndim; dim++) { + + // second derivative at knot0 is 0 + { + double f0 = parameters[(2 * 0) * Ndim + dim]; + double f1 = parameters[(2 * 1) * Ndim + dim]; + const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(0); + double cS1 = (6) * knot0.Li * knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + b(0) = -cS1 * (f1 - f0); + } + + // second derivative at knot nKnots-1 is 0 + { + double f0 = parameters[2 * (nKnots - 2) * Ndim + dim]; + double f1 = parameters[2 * (nKnots - 1) * Ndim + dim]; + const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(nKnots - 2); + double cS1 = (6 - 12) * knot0.Li * knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + b(nKnots - 1) = -cS1 * (f1 - f0); + } + + // second derivative at other knots is same from the left and from the right + for (int i = 1; i < nKnots - 1; i++) { + double f0 = parameters[2 * (i - 1) * Ndim + dim]; + double f1 = parameters[2 * (i)*Ndim + dim]; + double f2 = parameters[2 * (i + 1) * Ndim + dim]; + const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(i - 1); + double cS1 = (6 - 12) * knot0.Li * knot0.Li; + // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; + + const typename Spline1D<DataT>::Knot& knot1 = spline.getKnot(i); + double cS2 = (6) * knot1.Li * knot1.Li; + // f''(u) = cS2*(f2-f1) + cZ1_1*z1 + cZ2*z2; + b(i) = -cS1 * (f1 - f0) + cS2 * (f2 - f1); + } + + TVectorD c = A * b; + for (int i = 0; i < nKnots; i++) { + parameters[(2 * i + 1) * Ndim + dim] = c[i]; + } + } +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunction( + Spline1DContainer<DataT>& spline, double xMin, double xMax, std::function<void(double x, double f[/*spline.getFdimensions()*/])> F, + int nAuxiliaryDataPoints) +{ + /// Create best-fit spline parameters for a given input function F + setSpline(spline, spline.getYdimensions(), nAuxiliaryDataPoints); + approximateFunction(spline.getParameters(), xMin, xMax, F); + spline.setXrange(xMin, xMax); +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunctionGradually( + Spline1DContainer<DataT>& spline, double xMin, double xMax, std::function<void(double x, double f[/*spline.getFdimensions()*/])> F, + int nAuxiliaryDataPoints) +{ + /// Create best-fit spline parameters gradually for a given input function F + setSpline(spline, spline.getYdimensions(), nAuxiliaryDataPoints); + approximateFunctionGradually(spline.getParameters(), xMin, xMax, F); + spline.setXrange(xMin, xMax); +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunction( + DataT* Sparameters, double xMin, double xMax, std::function<void(double x, double f[])> F) const +{ + /// Create best-fit spline parameters for a given input function F + /// output in Sparameters + std::vector<double> vF(getNumberOfDataPoints() * mFdimensions); + double mUtoXscale = (((double)xMax) - xMin) / mSpline.getUmax(); + for (int i = 0; i < getNumberOfDataPoints(); i++) { + F(xMin + mUtoXscale * mDataPoints[i].u, &vF[i * mFdimensions]); + } + approximateFunction(Sparameters, vF.data()); +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunctionGradually( + DataT* Sparameters, double xMin, double xMax, std::function<void(double x, double f[])> F) const +{ + /// Create best-fit spline parameters gradually for a given input function F + /// output in Sparameters + std::vector<double> vF(getNumberOfDataPoints() * mFdimensions); + double mUtoXscale = (((double)xMax) - xMin) / mSpline.getUmax(); + for (int i = 0; i < getNumberOfDataPoints(); i++) { + F(xMin + mUtoXscale * mDataPoints[i].u, &vF[i * mFdimensions]); + } + approximateFunctionGradually(Sparameters, vF.data()); +} + +template <typename DataT> +int Spline1DHelper<DataT>::setSpline( + const Spline1DContainer<DataT>& spline, int nFdimensions, int nAuxiliaryDataPoints) +{ + // Prepare creation of a best-fit spline + // + // Data points will be set at all integer U (that includes all knots), + // plus at nAuxiliaryDataPoints points between the integers. + // + // nAuxiliaryDataPoints must be >= 2 + // + // nAuxiliaryDataPoints==1 is also possible, but there must be at least + // one integer U without a knot, in order to get 2*nKnots data points in total. + // + // The return value is an error index, 0 means no error + + int ret = 0; + + mFdimensions = nFdimensions; + int nPoints = 0; + if (!spline.isConstructed()) { + ret = storeError(-1, "Spline1DHelper<DataT>::setSpline: input spline is not constructed"); + mSpline.recreate(0, 2); + nAuxiliaryDataPoints = 2; + nPoints = 4; + } else { + std::vector<int> knots; + for (int i = 0; i < spline.getNumberOfKnots(); i++) { + knots.push_back(spline.getKnot(i).getU()); + } + mSpline.recreate(0, spline.getNumberOfKnots(), knots.data()); + + nPoints = 1 + mSpline.getUmax() + mSpline.getUmax() * nAuxiliaryDataPoints; + if (nPoints < 2 * mSpline.getNumberOfKnots()) { + nAuxiliaryDataPoints = 2; + nPoints = 1 + mSpline.getUmax() + mSpline.getUmax() * nAuxiliaryDataPoints; + ret = storeError(-3, "Spline1DHelper::setSpline: too few nAuxiliaryDataPoints, increase to 2"); + } + } + + const int nPar = 2 * mSpline.getNumberOfKnots(); // n parameters for 1D + + mDataPoints.resize(nPoints); + double scalePoints2Knots = ((double)mSpline.getUmax()) / (nPoints - 1.); + + for (int i = 0; i < nPoints; ++i) { + DataPoint& p = mDataPoints[i]; + double u = i * scalePoints2Knots; + int iKnot = mSpline.getLeftKnotIndexForU(u); + const typename Spline1D<double>::Knot& knot0 = mSpline.getKnot(iKnot); + const typename Spline1D<double>::Knot& knot1 = mSpline.getKnot(iKnot + 1); + double l = knot1.u - knot0.u; + double s = (u - knot0.u) * knot0.Li; // scaled u + double s2 = s * s; + double sm1 = s - 1.; + + p.iKnot = iKnot; + p.isKnot = 0; + p.u = u; + p.cS1 = s2 * (3. - 2. * s); + p.cS0 = 1. - p.cS1; + p.cZ0 = s * sm1 * sm1 * l; + p.cZ1 = s2 * sm1 * l; + } + + const int nKnots = mSpline.getNumberOfKnots(); + + mKnotDataPoints.resize(nKnots); + + for (int i = 0; i < nKnots; ++i) { + const typename Spline1D<double>::Knot& knot = mSpline.getKnot(i); + int iu = (int)(knot.u + 0.1f); + mKnotDataPoints[i] = iu * (1 + nAuxiliaryDataPoints); + mDataPoints[mKnotDataPoints[i]].isKnot = 1; + } + + TMatrixDSym A(nPar); + A.Zero(); + + for (int i = 0; i < nPoints; ++i) { + DataPoint& p = mDataPoints[i]; + int j = p.iKnot * 2; + A(j + 0, j + 0) += p.cS0 * p.cS0; + A(j + 1, j + 0) += p.cS0 * p.cZ0; + A(j + 2, j + 0) += p.cS0 * p.cS1; + A(j + 3, j + 0) += p.cS0 * p.cZ1; + + A(j + 1, j + 1) += p.cZ0 * p.cZ0; + A(j + 2, j + 1) += p.cZ0 * p.cS1; + A(j + 3, j + 1) += p.cZ0 * p.cZ1; + + A(j + 2, j + 2) += p.cS1 * p.cS1; + A(j + 3, j + 2) += p.cS1 * p.cZ1; + + A(j + 3, j + 3) += p.cZ1 * p.cZ1; + } + + // copy symmetric matrix elements + + for (int i = 0; i < nPar; i++) { + for (int j = i + 1; j < nPar; j++) { + A(i, j) = A(j, i); + } + } + + TMatrixDSym Z(nKnots); + mLSMmatrixSvalues.resize(nKnots * nKnots); + for (int i = 0, k = 0; i < nKnots; i++) { + for (int j = 0; j < nKnots; j++, k++) { + mLSMmatrixSvalues[k] = A(i * 2 + 1, j * 2); + Z(i, j) = A(i * 2 + 1, j * 2 + 1); + } + } + + { + TDecompBK bk(A, 0); + bool ok = bk.Invert(A); + + if (!ok) { + ret = storeError(-4, "Spline1DHelper::setSpline: internal error - can not invert the matrix"); + A.Zero(); + } + mLSMmatrixFull.resize(nPar * nPar); + for (int i = 0, k = 0; i < nPar; i++) { + for (int j = 0; j < nPar; j++, k++) { + mLSMmatrixFull[k] = A(i, j); + } + } + } + + { + TDecompBK bk(Z, 0); + if (!bk.Invert(Z)) { + ret = storeError(-5, "Spline1DHelper::setSpline: internal error - can not invert the matrix"); + Z.Zero(); + } + mLSMmatrixSderivatives.resize(nKnots * nKnots); + for (int i = 0, k = 0; i < nKnots; i++) { + for (int j = 0; j < nKnots; j++, k++) { + mLSMmatrixSderivatives[k] = Z(i, j); + } + } + } + + return ret; +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunction( + DataT* Sparameters, + const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const +{ + /// Approximate a function given as an array of values at data points + + const int nPar = 2 * mSpline.getNumberOfKnots(); + double b[nPar]; + for (int idim = 0; idim < mFdimensions; idim++) { + for (int i = 0; i < nPar; i++) { + b[i] = 0.; + } + for (int i = 0; i < getNumberOfDataPoints(); ++i) { + const DataPoint& p = mDataPoints[i]; + double* bb = &(b[p.iKnot * 2]); + double f = (double)DataPointF[i * mFdimensions + idim]; + bb[0] += f * p.cS0; + bb[1] += f * p.cZ0; + bb[2] += f * p.cS1; + bb[3] += f * p.cZ1; + } + + const double* row = mLSMmatrixFull.data(); + + for (int i = 0; i < nPar; i++, row += nPar) { + double s = 0.; + for (int j = 0; j < nPar; j++) { + s += row[j] * b[j]; + } + Sparameters[i * mFdimensions + idim] = (float)s; + } + } +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateFunctionGradually( + DataT* Sparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim */]) const +{ + /// gradually approximate a function given as an array of values at data points + /// output in Sparameters + copySfromDataPoints(Sparameters, DataPointF); + approximateDerivatives(Sparameters, DataPointF); +} + +template <typename DataT> +void Spline1DHelper<DataT>::copySfromDataPoints( + DataT* Sparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const +{ + /// a tool for the gradual approximation: set spline values S_i at knots == function values + /// output in Sparameters + for (int i = 0; i < mSpline.getNumberOfKnots(); ++i) { // set F values at knots + int ip = mKnotDataPoints[i]; + for (int d = 0; d < mFdimensions; d++) { + Sparameters[2 * i * mFdimensions + d] = DataPointF[ip * mFdimensions + d]; + } + } +} + +template <typename DataT> +void Spline1DHelper<DataT>::approximateDerivatives( + DataT* Sparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const +{ + /// a tool for the gradual approximation: + /// calibrate spline derivatives D_i using already calibrated spline values S_i + /// input and output output in Sparameters + + const int nKnots = mSpline.getNumberOfKnots(); + const int Ndim = mFdimensions; + double b[nKnots * mFdimensions]; + for (int i = 0; i < nKnots * Ndim; i++) { + b[i] = 0.; + } + + for (int i = 0; i < getNumberOfDataPoints(); ++i) { + const DataPoint& p = mDataPoints[i]; + if (p.isKnot) { + continue; + } + for (int d = 0; d < Ndim; d++) { + double f = (double)DataPointF[i * Ndim + d]; + b[(p.iKnot + 0) * Ndim + d] += f * p.cZ0; + b[(p.iKnot + 1) * Ndim + d] += f * p.cZ1; + } + } + + const double* row = mLSMmatrixSvalues.data(); + for (int i = 0; i < nKnots; ++i, row += nKnots) { + double s[Ndim]; + for (int d = 0; d < Ndim; d++) { + s[d] = 0.; + } + for (int j = 0; j < nKnots; ++j) { + for (int d = 0; d < Ndim; d++) { + s[d] += row[j] * Sparameters[2 * j * Ndim + d]; + } + } + for (int d = 0; d < Ndim; d++) { + b[i * Ndim + d] -= s[d]; + } + } + + row = mLSMmatrixSderivatives.data(); + for (int i = 0; i < nKnots; ++i, row += nKnots) { + double s[Ndim]; + for (int d = 0; d < Ndim; d++) { + s[d] = 0.; + } + for (int j = 0; j < nKnots; ++j) { + for (int d = 0; d < Ndim; d++) { + s[d] += row[j] * b[j * Ndim + d]; + } + } + for (int d = 0; d < Ndim; d++) { + Sparameters[(2 * i + 1) * Ndim + d] = (float)s[d]; + } + } +} + +#ifndef GPUCA_ALIROOT_LIB +template <typename DataT> +int Spline1DHelper<DataT>::test(const bool draw, const bool drawDataPoints) +{ + using namespace std; + + // input function F + + const int Ndim = 5; + const int Fdegree = 4; + double Fcoeff[Ndim][2 * (Fdegree + 1)]; + + auto F = [&](double x, double f[]) -> void { + double cosx[Fdegree + 1], sinx[Fdegree + 1]; + double xi = 0; + for (int i = 0; i <= Fdegree; i++, xi += x) { + GPUCommonMath::SinCosd(xi, sinx[i], cosx[i]); + } + for (int dim = 0; dim < Ndim; dim++) { + f[dim] = 0; // Fcoeff[0]/2; + for (int i = 1; i <= Fdegree; i++) { + f[dim] += Fcoeff[dim][2 * i] * cosx[i] + Fcoeff[dim][2 * i + 1] * sinx[i]; + } + } + }; + + TCanvas* canv = nullptr; + TNtuple* nt = nullptr; + TNtuple* knots = nullptr; + + auto ask = [&]() -> bool { + if (!canv) { + return 0; + } + canv->Update(); + cout << "type 'q ' to exit" << endl; + std::string str; + std::getline(std::cin, str); + return (str != "q" && str != ".q"); + }; + + std::cout << "Test 1D interpolation with the compact spline" << std::endl; + + int nTries = 100; + + if (draw) { + canv = new TCanvas("cQA", "Spline1D QA", 1000, 600); + nTries = 10000; + } + + double statDf1 = 0; + double statDf2 = 0; + double statDf1D = 0; + double statN = 0; + + int seed = 1; + + for (int itry = 0; itry < nTries; itry++) { + + // init random F + for (int dim = 0; dim < Ndim; dim++) { + gRandom->SetSeed(seed++); + for (int i = 0; i < 2 * (Fdegree + 1); i++) { + Fcoeff[dim][i] = gRandom->Uniform(-1, 1); + } + } + + // spline + + int nKnots = 4; + const int uMax = nKnots * 3; + + Spline1D<DataT, Ndim> spline1; + int knotsU[nKnots]; + + do { // set knots randomly + knotsU[0] = 0; + double du = 1. * uMax / (nKnots - 1); + for (int i = 1; i < nKnots; i++) { + knotsU[i] = (int)(i * du); // + gRandom->Uniform(-du / 3, du / 3); + } + knotsU[nKnots - 1] = uMax; + spline1.recreate(nKnots, knotsU); + if (nKnots != spline1.getNumberOfKnots()) { + cout << "warning: n knots changed during the initialisation " << nKnots + << " -> " << spline1.getNumberOfKnots() << std::endl; + continue; + } + } while (0); + + std::string err = FlatObject::stressTest(spline1); + if (!err.empty()) { + cout << "error at FlatObject functionality: " << err << endl; + return -1; + } else { + // cout << "flat object functionality is ok" << endl; + } + + nKnots = spline1.getNumberOfKnots(); + int nAuxiliaryPoints = 1; + Spline1D<DataT, Ndim> spline2(spline1); + spline1.approximateFunction(0., TMath::Pi(), F, nAuxiliaryPoints); + //if (itry == 0) + { + TFile outf("testSpline1D.root", "recreate"); + if (outf.IsZombie()) { + cout << "Failed to open output file testSpline1D.root " << std::endl; + } else { + const char* name = "Spline1Dtest"; + spline1.writeToFile(outf, name); + Spline1D<DataT, Ndim>* p = spline1.readFromFile(outf, name); + if (p == nullptr) { + cout << "Failed to read Spline1D from file testSpline1D.root " << std::endl; + } else { + spline1 = *p; + } + outf.Close(); + } + } + + Spline1DHelper<DataT> helper; + helper.setSpline(spline2, Ndim, nAuxiliaryPoints); + helper.approximateFunctionGradually(spline2, 0., TMath::Pi(), F, nAuxiliaryPoints); + + // 1-D splines for each dimension + Spline1D<DataT, 1> splines3[Ndim]; + { + for (int dim = 0; dim < Ndim; dim++) { + auto F3 = [&](double u, double f[]) -> void { + double ff[Ndim]; + F(u, ff); + f[0] = ff[dim]; + }; + splines3[dim].recreate(nKnots, knotsU); + splines3[dim].approximateFunction(0., TMath::Pi(), F3, nAuxiliaryPoints); + } + } + + double stepX = 1.e-2; + for (double x = 0; x < TMath::Pi(); x += stepX) { + double f[Ndim]; + DataT s1[Ndim], s2[Ndim]; + F(x, f); + spline1.interpolate(x, s1); + spline2.interpolate(x, s2); + for (int dim = 0; dim < Ndim; dim++) { + statDf1 += (s1[dim] - f[dim]) * (s1[dim] - f[dim]); + statDf2 += (s2[dim] - f[dim]) * (s2[dim] - f[dim]); + DataT s1D = splines3[dim].interpolate(x); + statDf1D += (s1D - s1[dim]) * (s1D - s1[dim]); + } + statN += Ndim; + } + // cout << "std dev : " << sqrt(statDf1 / statN) << std::endl; + + if (draw) { + delete nt; + delete knots; + nt = new TNtuple("nt", "nt", "u:f:s"); + double drawMax = -1.e20; + double drawMin = 1.e20; + double stepX = 1.e-4; + for (double x = 0; x < TMath::Pi(); x += stepX) { + double f[Ndim]; + DataT s[Ndim]; + F(x, f); + spline1.interpolate(x, s); + nt->Fill(spline1.convXtoU(x), f[0], s[0]); + drawMax = std::max(drawMax, std::max(f[0], (double)s[0])); + drawMin = std::min(drawMin, std::min(f[0], (double)s[0])); + } + + nt->SetMarkerStyle(8); + + { + TNtuple* ntRange = new TNtuple("ntRange", "nt", "u:f"); + drawMin -= 0.1 * (drawMax - drawMin); + + ntRange->Fill(0, drawMin); + ntRange->Fill(0, drawMax); + ntRange->Fill(uMax, drawMin); + ntRange->Fill(uMax, drawMax); + ntRange->SetMarkerColor(kWhite); + ntRange->SetMarkerSize(0.1); + ntRange->Draw("f:u", "", ""); + delete ntRange; + } + + nt->SetMarkerColor(kGray); + nt->SetMarkerSize(2.); + nt->Draw("f:u", "", "P,same"); + + nt->SetMarkerSize(.5); + nt->SetMarkerColor(kBlue); + nt->Draw("s:u", "", "P,same"); + + knots = new TNtuple("knots", "knots", "type:u:s"); + for (int i = 0; i < nKnots; i++) { + double u = spline1.getKnot(i).u; + DataT s[Ndim]; + spline1.interpolate(spline1.convUtoX(u), s); + knots->Fill(1, u, s[0]); + } + + knots->SetMarkerStyle(8); + knots->SetMarkerSize(1.5); + knots->SetMarkerColor(kRed); + knots->SetMarkerSize(1.5); + knots->Draw("s:u", "type==1", "same"); // knots + + if (drawDataPoints) { + for (int j = 0; j < helper.getNumberOfDataPoints(); j++) { + const typename Spline1DHelper<DataT>::DataPoint& p = helper.getDataPoint(j); + if (p.isKnot) { + continue; + } + DataT s[Ndim]; + spline1.interpolate(spline1.convUtoX(p.u), s); + knots->Fill(2, p.u, s[0]); + } + knots->SetMarkerColor(kBlack); + knots->SetMarkerSize(1.); + knots->Draw("s:u", "type==2", "same"); // data points + } + + if (!ask()) { + break; + } + } // draw + } + //delete canv; + //delete nt; + //delete knots; + + statDf1 = sqrt(statDf1 / statN); + statDf2 = sqrt(statDf2 / statN); + statDf1D = sqrt(statDf1D / statN); + + cout << "\n std dev for Compact Spline : " << statDf1 << " / " << statDf2 + << std::endl; + cout << " mean difference between 1-D and " << Ndim + << "-D splines : " << statDf1D << std::endl; + + if (statDf1 < 0.05 && statDf2 < 0.06 && statDf1D < 1.e-20) { + cout << "Everything is fine" << endl; + } else { + cout << "Something is wrong!!" << endl; + return -2; + } + return 0; +} +#endif + +template class GPUCA_NAMESPACE::gpu::Spline1DHelper<float>; +template class GPUCA_NAMESPACE::gpu::Spline1DHelper<double>; + +#endif diff --git a/GPU/TPCFastTransformation/Spline1DHelper.h b/GPU/TPCFastTransformation/Spline1DHelper.h new file mode 100644 index 0000000000000..07dcc8827597e --- /dev/null +++ b/GPU/TPCFastTransformation/Spline1DHelper.h @@ -0,0 +1,149 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline1DHelper.h +/// \brief Definition of Spline1DHelper class + +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_Spline1DHelper_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_Spline1DHelper_H + +#include <cmath> +#include <vector> + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "Spline1D.h" +#include <functional> +#include <string> + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ +/// +/// The Spline1DHelper class is to initialize parameters for Spline1D class +/// +template <typename DataT> +class Spline1DHelper +{ + public: + /// + /// \brief Helper structure for 1D spline construction + /// + struct DataPoint { + double u; ///< u coordinate + double cS0; ///< a coefficient for s0 + double cZ0; ///< a coefficient for s'0 + double cS1; ///< a coefficient for s1 + double cZ1; ///< a coefficient for s'1 + int iKnot; ///< index of the left knot of the segment + bool isKnot; ///< is the point placed at a knot + }; + + /// _____________ Constructors / destructors __________________________ + + /// Default constructor + Spline1DHelper(); + + /// Copy constructor: disabled + Spline1DHelper(const Spline1DHelper&) CON_DEFAULT; + + /// Assignment operator: disabled + Spline1DHelper& operator=(const Spline1DHelper&) CON_DEFAULT; + + /// Destructor + ~Spline1DHelper() CON_DEFAULT; + + /// _______________ Main functionality ________________________ + + /// Create best-fit spline parameters for a given input function F + void approximateFunction(Spline1DContainer<DataT>& spline, + double xMin, double xMax, std::function<void(double x, double f[/*spline.getFdimensions()*/])> F, + int nAuxiliaryDataPoints = 4); + + /// Create best-fit spline parameters gradually for a given input function F + void approximateFunctionGradually(Spline1DContainer<DataT>& spline, + double xMin, double xMax, std::function<void(double x, double f[/*spline.getFdimensions()*/])> F, + int nAuxiliaryDataPoints = 4); + + /// Create classic spline parameters for a given input function F + void approximateFunctionClassic(Spline1DContainer<DataT>& spline, + double xMin, double xMax, std::function<void(double x, double f[/*spline.getFdimensions()*/])> F); + + /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ + + /// precompute everything needed for the construction + int setSpline(const Spline1DContainer<DataT>& spline, int nFdimensions, int nAuxiliaryDataPoints); + + /// approximate std::function, output in Fparameters + void approximateFunction(DataT* Fparameters, double xMin, double xMax, std::function<void(double x, double f[])> F) const; + + /// approximate std::function gradually, output in Fparameters + void approximateFunctionGradually(DataT* Fparameters, double xMin, double xMax, std::function<void(double x, double f[])> F) const; + + /// number of data points + int getNumberOfDataPoints() const { return mDataPoints.size(); } + + /// approximate a function given as an array of values at data points + void approximateFunction(DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; + + /// gradually approximate a function given as an array of values at data points + void approximateFunctionGradually(DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim */]) const; + + /// a tool for the gradual approximation: set spline values S_i at knots == function values + void copySfromDataPoints(DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; + + /// a tool for the gradual approximation: + /// calibrate spline derivatives D_i using already calibrated spline values S_i + void approximateDerivatives(DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; + + /// _______________ Utilities ________________________ + + const Spline1D<double>& getSpline() const { return mSpline; } + + int getKnotDataPoint(int iknot) const { return mKnotDataPoints[iknot]; } + + const DataPoint& getDataPoint(int ip) const { return mDataPoints[ip]; } + + /// Gives error string + const char* getLastError() const { return mError.c_str(); } + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) // code invisible on GPU and in the standalone compilation + /// Test the Spline1D class functionality + static int test(const bool draw = 0, const bool drawDataPoints = 1); +#endif + + private: + /// Stores an error message + int storeError(int code, const char* msg); + + std::string mError = ""; ///< error string + + /// helpers for the construction of 1D spline + + Spline1D<double> mSpline; ///< copy of the spline + int mFdimensions; ///< n of F dimensions + std::vector<DataPoint> mDataPoints; ///< measurement points + std::vector<int> mKnotDataPoints; ///< which measurement points are at knots + std::vector<double> mLSMmatrixFull; ///< a matrix to convert the measurements into the spline parameters with the LSM method + std::vector<double> mLSMmatrixSderivatives; + std::vector<double> mLSMmatrixSvalues; + +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline1DHelper, 0); +#endif +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/Spline1DSpec.cxx b/GPU/TPCFastTransformation/Spline1DSpec.cxx new file mode 100644 index 0000000000000..002e9cd173940 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline1DSpec.cxx @@ -0,0 +1,277 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline1DSpec.cxx +/// \brief Implementation of Spline1DContainer & Spline1DSpec classes +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +//#include "Rtypes.h" +#endif + +#include "Spline1DSpec.h" + +#if !defined(GPUCA_GPUCODE) +#include <iostream> +#endif + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +#include "Spline1DHelper.h" +#include "TFile.h" +#include "GPUCommonMath.h" +templateClassImp(GPUCA_NAMESPACE::gpu::Spline1DContainer); +templateClassImp(GPUCA_NAMESPACE::gpu::Spline1DSpec); +#endif + +using namespace std; +using namespace GPUCA_NAMESPACE::gpu; + +#if !defined(GPUCA_GPUCODE) + +template <class DataT> +void Spline1DContainer<DataT>::recreate(int nYdim, int numberOfKnots) +{ + /// Constructor for a regular spline + /// \param numberOfKnots Number of knots + + if (numberOfKnots < 2) { + numberOfKnots = 2; + } + + std::vector<int> knots(numberOfKnots); + for (int i = 0; i < numberOfKnots; i++) { + knots[i] = i; + } + recreate(nYdim, numberOfKnots, knots.data()); +} + +template <class DataT> +void Spline1DContainer<DataT>::recreate(int nYdim, int numberOfKnots, const int inputKnots[]) +{ + /// Main constructor for an irregular spline + /// + /// Number of created knots may differ from the input values: + /// - Duplicated knots will be deleted + /// - At least 2 knots will be created + /// + /// \param numberOfKnots Number of knots in knots[] array + /// \param knots Array of relative knot positions (integer values) + /// + + FlatObject::startConstruction(); + + mYdim = (nYdim >= 0) ? nYdim : 0; + + std::vector<int> knotU; + + { // sort knots + std::vector<int> tmp; + for (int i = 0; i < numberOfKnots; i++) { + tmp.push_back(inputKnots[i]); + } + std::sort(tmp.begin(), tmp.end()); + + knotU.push_back(0); // first knot at 0 + + for (unsigned int i = 1; i < tmp.size(); ++i) { + int u = tmp[i] - tmp[0]; + if (knotU.back() < u) { // remove duplicated knots + knotU.push_back(u); + } + } + if (knotU.back() < 1) { // there is only one knot at u=0, add the second one at u=1 + knotU.push_back(1); + } + } + + mNumberOfKnots = knotU.size(); + mUmax = knotU.back(); + mXmin = 0.; + mXtoUscale = 1.; + + const int uToKnotMapOffset = mNumberOfKnots * sizeof(Knot); + int parametersOffset = uToKnotMapOffset + (mUmax + 1) * sizeof(int); + int bufferSize = parametersOffset; + if (mYdim > 0) { + parametersOffset = alignSize(bufferSize, getParameterAlignmentBytes()); + bufferSize = parametersOffset + getSizeOfParameters(); + } + + FlatObject::finishConstruction(bufferSize); + + mUtoKnotMap = reinterpret_cast<int*>(mFlatBufferPtr + uToKnotMapOffset); + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); + + for (int i = 0; i < getNumberOfParameters(); i++) { + mParameters[i] = 0; + } + + Knot* s = getKnots(); + + for (int i = 0; i < mNumberOfKnots; i++) { + s[i].u = knotU[i]; + } + + for (int i = 0; i < mNumberOfKnots - 1; i++) { + s[i].Li = 1. / (s[i + 1].u - s[i].u); // do division in double + } + + s[mNumberOfKnots - 1].Li = 0.; // the value will not be used, we define it for consistency + + // Set up the map (integer U) -> (knot index) + + int* map = getUtoKnotMap(); + + const int iKnotMax = mNumberOfKnots - 2; + + // + // With iKnotMax=nKnots-2 we map the U==Umax coordinate to the last [nKnots-2, nKnots-1] segment. + // This trick allows one to avoid a special condition for this edge case. + // Any U from [0,Umax] is mapped to some knot_i such, that the next knot_i+1 always exist + // + + for (int u = 0, iKnot = 0; u <= mUmax; u++) { + if ((knotU[iKnot + 1] == u) && (iKnot < iKnotMax)) { + iKnot = iKnot + 1; + } + map[u] = iKnot; + } +} + +#endif //GPUCA_GPUCODE + +template <class DataT> +void Spline1DContainer<DataT>::print() const +{ + printf(" Spline 1D: \n"); + printf(" mNumberOfKnots = %d \n", mNumberOfKnots); + printf(" mUmax = %d\n", mUmax); + printf(" mUtoKnotMap = %p \n", (void*)mUtoKnotMap); + printf(" knots: "); + for (int i = 0; i < mNumberOfKnots; i++) { + printf("%d ", (int)getKnot(i).u); + } + printf("\n"); +} + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + +template <class DataT> +void Spline1DContainer<DataT>::approximateFunction( + double xMin, double xMax, + std::function<void(double x, double f[])> F, + int nAxiliaryDataPoints) +{ + /// approximate a function F with this spline + Spline1DHelper<DataT> helper; + helper.approximateFunction(*reinterpret_cast<Spline1D<DataT>*>(this), xMin, xMax, F, nAxiliaryDataPoints); +} + +#ifndef GPUCA_ALIROOT_LIB +template <class DataT> +int Spline1DContainer<DataT>::writeToFile(TFile& outf, const char* name) +{ + /// write a class object to the file + return FlatObject::writeToFile(*this, outf, name); +} + +template <class DataT> +Spline1DContainer<DataT>* Spline1DContainer<DataT>::readFromFile( + TFile& inpf, const char* name) +{ + /// read a class object from the file + return FlatObject::readFromFile<Spline1DContainer<DataT>>(inpf, name); +} + +#endif +#endif + +#if !defined(GPUCA_GPUCODE) + +template <class DataT> +void Spline1DContainer<DataT>::cloneFromObject(const Spline1DContainer<DataT>& obj, char* newFlatBufferPtr) +{ + /// See FlatObject for description + + const char* oldFlatBufferPtr = obj.mFlatBufferPtr; + FlatObject::cloneFromObject(obj, newFlatBufferPtr); + mYdim = obj.mYdim; + mNumberOfKnots = obj.mNumberOfKnots; + mUmax = obj.mUmax; + mXmin = obj.mXmin; + mXtoUscale = obj.mXtoUscale; + mUtoKnotMap = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mUtoKnotMap); + mParameters = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mParameters); +} + +template <class DataT> +void Spline1DContainer<DataT>::moveBufferTo(char* newFlatBufferPtr) +{ + /// See FlatObject for description + char* oldFlatBufferPtr = mFlatBufferPtr; + FlatObject::moveBufferTo(newFlatBufferPtr); + char* currFlatBufferPtr = mFlatBufferPtr; + mFlatBufferPtr = oldFlatBufferPtr; + setActualBufferAddress(currFlatBufferPtr); +} +#endif // GPUCA_GPUCODE + +template <class DataT> +void Spline1DContainer<DataT>::destroy() +{ + /// See FlatObject for description + mNumberOfKnots = 0; + mUmax = 0; + mYdim = 0; + mXmin = 0.; + mXtoUscale = 1.; + mUtoKnotMap = nullptr; + mParameters = nullptr; + FlatObject::destroy(); +} + +template <class DataT> +void Spline1DContainer<DataT>::setActualBufferAddress(char* actualFlatBufferPtr) +{ + /// See FlatObject for description + + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + + const int uToKnotMapOffset = mNumberOfKnots * sizeof(Knot); + mUtoKnotMap = reinterpret_cast<int*>(mFlatBufferPtr + uToKnotMapOffset); + int parametersOffset = uToKnotMapOffset + (mUmax + 1) * sizeof(int); + if (mYdim > 0) { + parametersOffset = alignSize(parametersOffset, getParameterAlignmentBytes()); + } + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); +} + +template <class DataT> +void Spline1DContainer<DataT>::setFutureBufferAddress(char* futureFlatBufferPtr) +{ + /// See FlatObject for description + mUtoKnotMap = FlatObject::relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mUtoKnotMap); + mParameters = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mParameters); + FlatObject::setFutureBufferAddress(futureFlatBufferPtr); +} + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) +template <class DataT> +int Spline1DContainer<DataT>::test(const bool draw, const bool drawDataPoints) +{ + return Spline1DHelper<DataT>::test(draw, drawDataPoints); +} +#endif // GPUCA_GPUCODE + +template class GPUCA_NAMESPACE::gpu::Spline1DContainer<float>; +template class GPUCA_NAMESPACE::gpu::Spline1DContainer<double>; +template class GPUCA_NAMESPACE::gpu::Spline1DSpec<float, 0, 2>; +template class GPUCA_NAMESPACE::gpu::Spline1DSpec<double, 0, 2>; \ No newline at end of file diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h new file mode 100644 index 0000000000000..9b7a3357fd0e6 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -0,0 +1,517 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline1DSpec.h +/// \brief Definition of Spline1DSpec class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE1DSPEC_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE1DSPEC_H + +#include "GPUCommonDef.h" +#include "FlatObject.h" +#include "SplineUtil.h" + +#if !defined(GPUCA_GPUCODE) +#include <functional> +#endif + +class TFile; + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +/// ================================================================================================== +/// The class Spline1DContainer is a base class of Spline1D. +/// It contains all the class members and those methods which only depends on the DataT data type. +/// It also contains all non-inlined methods with the implementation in Spline1DSpec.cxx file. +/// +/// DataT is a data type, which is supposed to be either double or float. +/// For other possible data types one has to add the corresponding instantiation line +/// at the end of the Spline1DSpec.cxx file +/// +template <typename DataT> +class Spline1DContainer : public FlatObject +{ + public: + /// Named enumeration for the safety level used by some methods + enum SafetyLevel { kNotSafe, + kSafe }; + + /// The struct Knot represents the i-th knot and the segment [knot_i, knot_i+1] + /// + struct Knot { + DataT u; ///< u coordinate of the knot i (an integer number in float format) + DataT Li; ///< inverse length of the [knot_i, knot_{i+1}] segment ( == 1./ a (small) integer ) + /// Get u as an integer + GPUd() int getU() const { return (int)(u + 0.1); } + }; + + /// _____________ Version control __________________________ + + /// Version control + GPUd() static constexpr int getVersion() { return 1; } + + /// _____________ C++ constructors / destructors __________________________ + + /// Default constructor, required by the Root IO + Spline1DContainer() CON_DEFAULT; + + /// Disable all other constructors + Spline1DContainer(const Spline1DContainer&) CON_DELETE; + + /// Destructor + ~Spline1DContainer() CON_DEFAULT; + + /// _______________ Construction interface ________________________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// approximate a function F with this spline + void approximateFunction(double xMin, double xMax, + std::function<void(double x, double f[/*mYdim*/])> F, + int nAuxiliaryDataPoints = 4); +#endif + + /// _______________ IO ________________________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// write a class object to the file + int writeToFile(TFile& outf, const char* name); + + /// read a class object from the file + static Spline1DContainer* readFromFile(TFile& inpf, const char* name); +#endif + + /// _______________ Getters ________________________ + + /// Get U coordinate of the last knot + GPUd() int getUmax() const { return mUmax; } + + /// Get number of Y dimensions + GPUd() int getYdimensions() const { return mYdim; } + + /// Get minimal required alignment for the spline parameters + GPUd() size_t getParameterAlignmentBytes() const + { + size_t s = 2 * sizeof(DataT) * mYdim; + return (s < 16) ? s : 16; + } + + /// Number of parameters + GPUd() int getNumberOfParameters() const { return calcNumberOfParameters(mYdim); } + + /// Size of the parameter array in bytes + GPUd() size_t getSizeOfParameters() const { return sizeof(DataT) * getNumberOfParameters(); } + + /// Get a number of knots + GPUd() int getNumberOfKnots() const { return mNumberOfKnots; } + + /// Get the array of knots + GPUd() const Knot* getKnots() const { return reinterpret_cast<const Knot*>(mFlatBufferPtr); } + + /// Get i-th knot + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() const Knot& getKnot(int i) const + { + if (SafeT == SafetyLevel::kSafe) { + i = (i < 0) ? 0 : (i >= mNumberOfKnots ? mNumberOfKnots - 1 : i); + } + return getKnots()[i]; + } + + /// Get index of an associated knot for a given U coordinate. Performs a boundary check. + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() int getLeftKnotIndexForU(DataT u) const; + + /// Get spline parameters + GPUd() DataT* getParameters() { return mParameters; } + + /// Get spline parameters const + GPUd() const DataT* getParameters() const { return mParameters; } + + /// _______________ Technical stuff ________________________ + + /// Get a map (integer U -> corresponding knot index) + GPUd() const int* getUtoKnotMap() const { return mUtoKnotMap; } + + /// Convert X coordinate to U + GPUd() DataT convXtoU(DataT x) const { return (x - mXmin) * mXtoUscale; } + + /// Convert U coordinate to X + GPUd() DataT convUtoX(DataT u) const { return mXmin + u / mXtoUscale; } + + /// Get Xmin + GPUd() DataT getXmin() const { return mXmin; } + + /// Get XtoUscale + GPUd() DataT getXtoUscale() const { return mXtoUscale; } + + /// Set X range + GPUd() void setXrange(DataT xMin, DataT xMax); + + /// Print method + void print() const; + + /// _______________ Expert tools _______________ + + /// Number of parameters for given Y dimensions + GPUd() int calcNumberOfParameters(int nYdim) const { return (2 * nYdim) * getNumberOfKnots(); } + + ///_______________ Test tools _______________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) // code invisible on GPU and in the standalone compilation + /// Test the class functionality + static int test(const bool draw = 0, const bool drawDataPoints = 1); +#endif + + /// _____________ FlatObject functionality, see FlatObject class for description ____________ + + using FlatObject::getBufferAlignmentBytes; + using FlatObject::getClassAlignmentBytes; + +#if !defined(GPUCA_GPUCODE) + void cloneFromObject(const Spline1DContainer& obj, char* newFlatBufferPtr); + void moveBufferTo(char* newBufferPtr); +#endif + + using FlatObject::releaseInternalBuffer; + + void destroy(); + void setActualBufferAddress(char* actualFlatBufferPtr); + void setFutureBufferAddress(char* futureFlatBufferPtr); + + protected: + /// Non-const accessor to the knots array + Knot* getKnots() { return reinterpret_cast<Knot*>(mFlatBufferPtr); } + + /// Non-const accessor to U->knots map + int* getUtoKnotMap() { return mUtoKnotMap; } + +#if !defined(GPUCA_GPUCODE) + /// Constructor for a regular spline + void recreate(int nYdim, int numberOfKnots); + + /// Constructor for an irregular spline + void recreate(int nYdim, int numberOfKnots, const int knotU[]); +#endif + + /// _____________ Data members ____________ + + int mYdim = 0; ///< dimentionality of F + int mNumberOfKnots = 0; ///< n knots on the grid + int mUmax = 0; ///< U of the last knot + DataT mXmin = 0; ///< X of the first knot + DataT mXtoUscale = 0; ///< a scaling factor to convert X to U + int* mUtoKnotMap = nullptr; //! (transient!!) pointer to (integer U -> knot index) map inside the mFlatBufferPtr array + DataT* mParameters = nullptr; //! (transient!!) pointer to F-dependent parameters inside the mFlatBufferPtr array + +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline1DContainer, 1); +#endif +}; + +template <typename DataT> +template <typename Spline1DContainer<DataT>::SafetyLevel SafeT> +GPUdi() int Spline1DContainer<DataT>::getLeftKnotIndexForU(DataT u) const +{ + /// Get i: u is in [knot_i, knot_{i+1}) segment + /// when u is otside of [0, mUmax], return a corresponding edge segment + int iu = (int)u; + if (SafeT == SafetyLevel::kSafe) { + iu = (iu < 0) ? 0 : (iu > mUmax ? mUmax : iu); + } + return getUtoKnotMap()[iu]; +} + +template <typename DataT> +GPUdi() void Spline1DContainer<DataT>::setXrange(DataT xMin, DataT xMax) +{ + mXmin = xMin; + double l = ((double)xMax) - xMin; + if (l < 1.e-8) { + l = 1.e-8; + } + mXtoUscale = mUmax / l; +} + +/// ================================================================================================== +/// +/// Spline1DSpec class declares different specializations of the Spline1D class. +/// +/// The specializations depend on the value of Spline1D's template parameter YdimT. +/// specializations have different constructors and slightly different declarations of methods. +/// +/// The meaning of the template parameters: +/// +/// \param DataT data type: float or double +/// \param YdimT +/// YdimT > 0 : the number of Y dimensions is known at the compile time and is equal to YdimT +/// YdimT = 0 : the number of Y dimensions will be set in the runtime +/// YdimT < 0 : the number of Y dimensions will be set in the runtime, and it will not exceed abs(XdimT) +/// \param SpecT specialisation number: +/// 0 - a parent class for all other specializations +/// 1 - nYdim>0: nYdim is set at the compile time +/// 2 - nYdim<0: nYdim must be set during runtime +/// 3 - specialization where nYdim==1 (a small add-on on top of the other specs) +/// +template <typename DataT, int YdimT, int SpecT> +class Spline1DSpec; + +/// ================================================================================================== +/// Specialization 0 declares common methods for all other Spline2D specializations. +/// Implementations of the methods may depend on the YdimT value. +/// +template <typename DataT, int YdimT> +class Spline1DSpec<DataT, YdimT, 0> : public Spline1DContainer<DataT> +{ + typedef Spline1DContainer<DataT> TBase; + + public: + typedef typename TBase::SafetyLevel SafetyLevel; + typedef typename TBase::Knot Knot; + + /// _______________ Interpolation math ________________________ + + /// Get interpolated value S(x) + GPUd() void interpolate(DataT x, GPUgeneric() DataT S[/*mYdim*/]) const + { + interpolateU<SafetyLevel::kSafe>(mYdim, mParameters, convXtoU(x), S); + } + + /// Get interpolated value for an nYdim-dimensional S(u) using spline parameters Parameters. + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() void interpolateU(int inpYdim, GPUgeneric() const DataT Parameters[], + DataT u, GPUgeneric() DataT S[/*nYdim*/]) const + { + const auto nYdimTmp = SplineUtil::getNdim<YdimT>(inpYdim); + const auto nYdim = nYdimTmp.get(); + int iknot = TBase::template getLeftKnotIndexForU<SafeT>(u); + const DataT* d = Parameters + (2 * nYdim) * iknot; + interpolateU(nYdim, getKnots()[iknot], &(d[0]), &(d[nYdim]), &(d[2 * nYdim]), &(d[3 * nYdim]), u, S); + } + + /// The main mathematical utility. + /// Get interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] + /// using the spline values Sl, Sr and the slopes Dl, Dr + template <typename T> + GPUd() void interpolateU(int inpYdim, const Knot& knotL, + GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], + GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], + DataT u, GPUgeneric() T S[/*mYdim*/]) const + { + const auto nYdimTmp = SplineUtil::getNdim<YdimT>(inpYdim); + const auto nYdim = nYdimTmp.get(); + T uu = T(u - knotL.u); + T li = T(knotL.Li); + T v = uu * li; // scaled u + for (int dim = 0; dim < nYdim; ++dim) { + T df = (Sr[dim] - Sl[dim]) * li; + T a = Dl[dim] + Dr[dim] - df - df; + T b = df - Dl[dim] - a; + S[dim] = ((a * v + b) * v + Dl[dim]) * uu + Sl[dim]; + } + /* + another way to calculate f(u): + T uu = T(u - knotL.u); + T v = uu * T(knotL.Li); // scaled u + T vm1 = v-1; + T v2 = v * v; + float cSr = v2*(3-2*v); + float cSl = 1-cSr; + float cDl = v*vm1*vm1*knotL.L; + float cDr = v2*vm1*knotL.L; + return cSl*Sl + cSr*Sr + cDl*Dl + cDr*Dr; + */ + } + + using TBase::convXtoU; + using TBase::getKnot; + using TBase::getKnots; + using TBase::getNumberOfKnots; + + protected: + using TBase::mParameters; + using TBase::mYdim; + using TBase::TBase; // inherit constructors and hide them +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline1DSpec, 0); +#endif +}; + +/// ================================================================================================== +/// Specialization 1: YdimT>0 where the number of Y dimensions is taken from template parameters +/// at the compile time +/// +template <typename DataT, int YdimT> +class Spline1DSpec<DataT, YdimT, 1> + : public Spline1DSpec<DataT, YdimT, 0> +{ + typedef Spline1DContainer<DataT> TVeryBase; + typedef Spline1DSpec<DataT, YdimT, 0> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + +#if !defined(GPUCA_GPUCODE) + /// Default constructor + Spline1DSpec() : Spline1DSpec(2) {} + + /// Constructor for a regular spline + Spline1DSpec(int numberOfKnots) : TBase() + { + recreate(numberOfKnots); + } + /// Constructor for an irregular spline + Spline1DSpec(int numberOfKnots, const int knotU[]) + : TBase() + { + recreate(numberOfKnots, knotU); + } + /// Copy constructor + Spline1DSpec(const Spline1DSpec& v) : TBase() + { + TBase::cloneFromObject(v, nullptr); + } + /// Constructor for a regular spline + void recreate(int numberOfKnots) { TBase::recreate(YdimT, numberOfKnots); } + + /// Constructor for an irregular spline + void recreate(int numberOfKnots, const int knotU[]) + { + TBase::recreate(YdimT, numberOfKnots, knotU); + } +#endif + + /// Get number of Y dimensions + GPUd() constexpr int getYdimensions() const { return YdimT; } + + /// Get minimal required alignment for the spline parameters + GPUd() constexpr size_t getParameterAlignmentBytes() const + { + size_t s = 2 * sizeof(DataT) * YdimT; + return (s < 16) ? s : 16; + } + + /// Number of parameters + GPUd() int getNumberOfParameters() const { return (2 * YdimT) * getNumberOfKnots(); } + + /// Size of the parameter array in bytes + GPUd() size_t getSizeOfParameters() const { return (sizeof(DataT) * 2 * YdimT) * getNumberOfKnots(); } + + /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ + + /// Get interpolated value for an YdimT-dimensional S(u) using spline parameters Parameters. + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], + DataT u, GPUgeneric() DataT S[/*nYdim*/]) const + { + TBase::template interpolateU<SafeT>(YdimT, Parameters, u, S); + } + + /// Get interpolated value for an YdimT-dimensional S(u) at the segment [knotL, next knotR] + /// using the spline values Sl, Sr and the slopes Dl, Dr + template <typename T> + GPUd() void interpolateU(const typename TBase::Knot& knotL, + GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], + GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], + DataT u, GPUgeneric() T S[/*mYdim*/]) const + { + TBase::template interpolateU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); + } + + using TBase::getNumberOfKnots; + + /// _______________ Suppress some parent class methods ________________________ + private: +#if !defined(GPUCA_GPUCODE) + using TBase::recreate; +#endif + using TBase::interpolateU; +}; + +/// ================================================================================================== +/// Specialization 2 (YdimT<=0) where the numbaer of Y dimensions +/// must be set in the runtime via a constructor parameter +/// +template <typename DataT, int YdimT> +class Spline1DSpec<DataT, YdimT, 2> + : public Spline1DSpec<DataT, YdimT, 0> +{ + typedef Spline1DContainer<DataT> TVeryBase; + typedef Spline1DSpec<DataT, YdimT, 0> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + +#if !defined(GPUCA_GPUCODE) + /// Default constructor + Spline1DSpec() : Spline1DSpec(0, 2) {} + + /// Constructor for a regular spline + Spline1DSpec(int nYdim, int numberOfKnots) : TBase() + { + TBase::recreate(nYdim, numberOfKnots); + } + /// Constructor for an irregular spline + Spline1DSpec(int nYdim, int numberOfKnots, const int knotU[]) : TBase() + { + TBase::recreate(nYdim, numberOfKnots, knotU); + } + /// Copy constructor + Spline1DSpec(const Spline1DSpec& v) : TBase() + { + TVeryBase::cloneFromObject(v, nullptr); + } + /// Constructor for a regular spline + void recreate(int nYdim, int numberOfKnots) { TBase::recreate(nYdim, numberOfKnots); } + + /// Constructor for an irregular spline + void recreate(int nYdim, int numberOfKnots, const int knotU[]) + { + TBase::recreate(nYdim, numberOfKnots, knotU); + } +#endif + + /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ + + using TBase::interpolateU; +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline1DSpec, 0); +#endif +}; + +/// ================================================================================================== +/// Specialization 3, where the number of Y dimensions is 1. +/// +template <typename DataT> +class Spline1DSpec<DataT, 1, 3> + : public Spline1DSpec<DataT, 1, SplineUtil::getSpec(999)> +{ + typedef Spline1DSpec<DataT, 1, SplineUtil::getSpec(999)> TBase; + + public: + using TBase::TBase; // inherit constructors + + /// Simplified interface for 1D: return the interpolated value + GPUd() DataT interpolate(DataT x) const + { + DataT S = 0.; + TBase::interpolate(x, &S); + return S; + } +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/Spline2D.cxx b/GPU/TPCFastTransformation/Spline2D.cxx index a198fc6006179..08f832b408a55 100644 --- a/GPU/TPCFastTransformation/Spline2D.cxx +++ b/GPU/TPCFastTransformation/Spline2D.cxx @@ -13,493 +13,11 @@ /// /// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation -#include "Rtypes.h" -#endif - #include "Spline2D.h" -#if !defined(GPUCA_GPUCODE) -#include <iostream> -#endif - #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation -#include "TRandom.h" -#include "Riostream.h" -#include "TMath.h" -#include "SplineHelper2D.h" -#include "TCanvas.h" -#include "TNtuple.h" -#include "TFile.h" -#include "GPUCommonMath.h" - -templateClassImp(GPUCA_NAMESPACE::gpu::Spline2DBase); - -#endif - -using namespace std; -using namespace GPUCA_NAMESPACE::gpu; - -template <typename DataT, bool isConsistentT> -Spline2DBase<DataT, isConsistentT>::Spline2DBase(int nDim) - : FlatObject(), mFdim(nDim), mGridU1(), mGridU2(), mFparameters(nullptr) -{ - recreate(2, 2); -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::destroy() -{ - /// See FlatObject for description - mGridU1.destroy(); - mGridU2.destroy(); - FlatObject::destroy(); -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::setActualBufferAddress(char* actualFlatBufferPtr) -{ - /// See FlatObject for description - - FlatObject::setActualBufferAddress(actualFlatBufferPtr); - - const size_t u2Offset = alignSize(mGridU1.getFlatBufferSize(), mGridU2.getBufferAlignmentBytes()); - int parametersOffset = u2Offset; - //int bufferSize = parametersOffset; - mFparameters = nullptr; - - if (isConsistentT) { - parametersOffset = alignSize(u2Offset + mGridU2.getFlatBufferSize(), getParameterAlignmentBytes()); - //bufferSize = parametersOffset + getSizeOfParameters(); - mFparameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); - } - - mGridU1.setActualBufferAddress(mFlatBufferPtr); - mGridU2.setActualBufferAddress(mFlatBufferPtr + u2Offset); -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::setFutureBufferAddress(char* futureFlatBufferPtr) -{ - /// See FlatObject for description - char* bufferU = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mGridU1.getFlatBufferPtr()); - char* bufferV = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mGridU2.getFlatBufferPtr()); - mGridU1.setFutureBufferAddress(bufferU); - mGridU2.setFutureBufferAddress(bufferV); - if (isConsistentT) { - mFparameters = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mFparameters); - } else { - mFparameters = nullptr; - } - FlatObject::setFutureBufferAddress(futureFlatBufferPtr); -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::print() const -{ - printf(" Irregular Spline 2D: \n"); - printf(" grid U1: \n"); - mGridU1.print(); - printf(" grid U2: \n"); - mGridU2.print(); -} - -#if !defined(GPUCA_GPUCODE) -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::cloneFromObject(const Spline2DBase<DataT, isConsistentT>& obj, char* newFlatBufferPtr) -{ - /// See FlatObject for description - if (isConsistentT && mFdim != obj.mFdim) { - assert(0); - return; - } - - const char* oldFlatBufferPtr = obj.mFlatBufferPtr; - - FlatObject::cloneFromObject(obj, newFlatBufferPtr); - - char* bufferU = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mGridU1.getFlatBufferPtr()); - char* bufferV = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mGridU2.getFlatBufferPtr()); - - mGridU1.cloneFromObject(obj.mGridU1, bufferU); - mGridU2.cloneFromObject(obj.mGridU2, bufferV); - - if (isConsistentT) { - mFparameters = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mFparameters); - } else { - mFparameters = nullptr; - } -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::moveBufferTo(char* newFlatBufferPtr) -{ - /// See FlatObject for description - char* oldFlatBufferPtr = mFlatBufferPtr; - FlatObject::moveBufferTo(newFlatBufferPtr); - char* currFlatBufferPtr = mFlatBufferPtr; - mFlatBufferPtr = oldFlatBufferPtr; - setActualBufferAddress(currFlatBufferPtr); -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::recreate( - int numberOfKnotsU1, const int knotsU1[], int numberOfKnotsU2, const int knotsU2[]) -{ - /// Constructor for an irregular spline - - FlatObject::startConstruction(); - - mGridU1.recreate(numberOfKnotsU1, knotsU1, 0); - mGridU2.recreate(numberOfKnotsU2, knotsU2, 0); - - const size_t u2Offset = alignSize(mGridU1.getFlatBufferSize(), mGridU2.getBufferAlignmentBytes()); - int parametersOffset = u2Offset + mGridU2.getFlatBufferSize(); - int bufferSize = parametersOffset; - mFparameters = nullptr; - - if (isConsistentT) { - parametersOffset = alignSize(bufferSize, getParameterAlignmentBytes()); - bufferSize = parametersOffset + getSizeOfParameters(); - } - - FlatObject::finishConstruction(bufferSize); - - mGridU1.moveBufferTo(mFlatBufferPtr); - mGridU2.moveBufferTo(mFlatBufferPtr + u2Offset); - if (isConsistentT) { - mFparameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); - for (int i = 0; i < getNumberOfParameters(); i++) { - mFparameters[i] = 0; - } - } -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::recreate( - int numberOfKnotsU1, int numberOfKnotsU2) -{ - /// Constructor for a regular spline - - FlatObject::startConstruction(); - - mGridU1.recreate(numberOfKnotsU1, 0); - - mGridU2.recreate(numberOfKnotsU2, 0); - - const size_t u2Offset = alignSize(mGridU1.getFlatBufferSize(), mGridU2.getBufferAlignmentBytes()); - int parametersOffset = u2Offset + mGridU2.getFlatBufferSize(); - int bufferSize = parametersOffset; - mFparameters = nullptr; - - if (isConsistentT) { - parametersOffset = alignSize(bufferSize, getParameterAlignmentBytes()); - bufferSize = parametersOffset + getSizeOfParameters(); - } - - FlatObject::finishConstruction(bufferSize); - - mGridU1.moveBufferTo(mFlatBufferPtr); - mGridU2.moveBufferTo(mFlatBufferPtr + u2Offset); - - if (isConsistentT) { - mFparameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); - for (int i = 0; i < getNumberOfParameters(); i++) { - mFparameters[i] = 0; - } - } -} - +templateClassImp(GPUCA_NAMESPACE::gpu::Spline2D); #endif -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) // code invisible on GPU and in the standalone compilation and in AliRoot - -template <typename DataT, bool isConsistentT> -int Spline2DBase<DataT, isConsistentT>::writeToFile(TFile& outf, const char* name) -{ - /// write a class object to the file - return FlatObject::writeToFile(*this, outf, name); -} - -template <typename DataT, bool isConsistentT> -Spline2DBase<DataT, isConsistentT>* Spline2DBase<DataT, isConsistentT>::readFromFile( - TFile& inpf, const char* name) -{ - /// read a class object from the file - return FlatObject::readFromFile<Spline2DBase<DataT, isConsistentT>>(inpf, name); -} - -template <typename DataT, bool isConsistentT> -void Spline2DBase<DataT, isConsistentT>::approximateFunction( - DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(DataT x1, DataT x2, DataT f[])> F, - int nAxiliaryDataPointsU1, int nAxiliaryDataPointsU2) -{ - /// approximate a function F with this spline - SplineHelper2D<DataT> helper; - helper.approximateFunction(*this, x1Min, x1Max, x2Min, x2Max, F, nAxiliaryDataPointsU1, nAxiliaryDataPointsU2); -} - -template <typename DataT, bool isConsistentT> -int Spline2DBase<DataT, isConsistentT>::test(const bool draw, const bool drawDataPoints) -{ - using namespace std; - - const int Ndim = 3; - - const int Fdegree = 4; - - double Fcoeff[Ndim][4 * (Fdegree + 1) * (Fdegree + 1)]; - - constexpr int nKnots = 4; - constexpr int nAxiliaryPoints = 1; - constexpr int uMax = nKnots * 3; - - auto F = [&](DataT u, DataT v, DataT Fuv[]) { - const double scale = TMath::Pi() / uMax; - double uu = u * scale; - double vv = v * scale; - double cosu[Fdegree + 1], sinu[Fdegree + 1], cosv[Fdegree + 1], sinv[Fdegree + 1]; - double ui = 0, vi = 0; - for (int i = 0; i <= Fdegree; i++, ui += uu, vi += vv) { - GPUCommonMath::SinCos(ui, sinu[i], cosu[i]); - GPUCommonMath::SinCos(vi, sinv[i], cosv[i]); - } - for (int dim = 0; dim < Ndim; dim++) { - double f = 0; // Fcoeff[dim][0]/2; - for (int i = 1; i <= Fdegree; i++) { - for (int j = 1; j <= Fdegree; j++) { - double* c = &(Fcoeff[dim][4 * (i * Fdegree + j)]); - f += c[0] * cosu[i] * cosv[j]; - f += c[1] * cosu[i] * sinv[j]; - f += c[2] * sinu[i] * cosv[j]; - f += c[3] * sinu[i] * sinv[j]; - } - } - Fuv[dim] = f; - } - }; - - TCanvas* canv = nullptr; - TNtuple* nt = nullptr; - TNtuple* knots = nullptr; - - auto ask = [&]() -> bool { - if (!canv) { - return 0; - } - canv->Update(); - cout << "type 'q ' to exit" << endl; - std::string str; - std::getline(std::cin, str); - return (str != "q" && str != ".q"); - }; - - std::cout << "Test 2D interpolation with the compact spline" << std::endl; - - int nTries = 10; - - if (draw) { - canv = new TCanvas("cQA", "Spline2D QA", 1500, 800); - nTries = 10000; - } - - long double statDf = 0; - long double statDf1D = 0; - long double statN = 0; - - for (int seed = 1; seed < nTries + 1; seed++) { - //cout << "next try.." << endl; - - gRandom->SetSeed(seed); - - for (int dim = 0; dim < Ndim; dim++) { - for (int i = 0; i < 4 * (Fdegree + 1) * (Fdegree + 1); i++) { - Fcoeff[dim][i] = gRandom->Uniform(-1, 1); - } - } - - Spline2D<DataT, Ndim> spline; - - int knotsU[nKnots], knotsV[nKnots]; - do { - knotsU[0] = 0; - knotsV[0] = 0; - double du = 1. * uMax / (nKnots - 1); - for (int i = 1; i < nKnots; i++) { - knotsU[i] = (int)(i * du); // + gRandom->Uniform(-du / 3, du / 3); - knotsV[i] = (int)(i * du); // + gRandom->Uniform(-du / 3, du / 3); - } - knotsU[nKnots - 1] = uMax; - knotsV[nKnots - 1] = uMax; - spline.recreate(nKnots, knotsU, nKnots, knotsV); - - if (nKnots != spline.getGridU1().getNumberOfKnots() || - nKnots != spline.getGridU2().getNumberOfKnots()) { - cout << "warning: n knots changed during the initialisation " << nKnots - << " -> " << spline.getNumberOfKnots() << std::endl; - continue; - } - } while (0); - - std::string err = FlatObject::stressTest(spline); - if (!err.empty()) { - cout << "error at FlatObject functionality: " << err << endl; - return -1; - } else { - // cout << "flat object functionality is ok" << endl; - } - - // Ndim-D spline - spline.approximateFunction(0., uMax, 0., uMax, F, 4, 4); - - //if (itry == 0) - if (1) { - TFile outf("testSpline2D.root", "recreate"); - if (outf.IsZombie()) { - cout << "Failed to open output file testSpline2D.root " << std::endl; - } else { - const char* name = "spline2Dtest"; - spline.writeToFile(outf, name); - Spline2D<DataT, Ndim>* p = Spline2D<DataT, Ndim>::readFromFile(outf, name); - if (p == nullptr) { - cout << "Failed to read Spline1D from file testSpline1D.root " << std::endl; - } else { - spline = *p; - } - outf.Close(); - } - } - - // 1-D splines for each of Ndim dimensions - - Spline2D<DataT, 1> splines1D[Ndim]; - - for (int dim = 0; dim < Ndim; dim++) { - auto F1 = [&](DataT x1, DataT x2, DataT f[]) { - DataT ff[Ndim]; - F(x1, x2, ff); - f[0] = ff[dim]; - }; - splines1D[dim].recreate(nKnots, knotsU, nKnots, knotsV); - splines1D[dim].approximateFunction(0., uMax, 0., uMax, F1, 4, 4); - } - - double stepU = .1; - for (double u = 0; u < uMax; u += stepU) { - for (double v = 0; v < uMax; v += stepU) { - DataT f[Ndim]; - F(u, v, f); - DataT s[Ndim]; - DataT s1; - spline.interpolate(u, v, s); - for (int dim = 0; dim < Ndim; dim++) { - statDf += (s[dim] - f[dim]) * (s[dim] - f[dim]); - splines1D[dim].interpolate(u, v, &s1); - statDf1D += (s[dim] - s1) * (s[dim] - s1); - } - statN += Ndim; - // cout << u << " " << v << ": f " << f << " s " << s << " df " - // << s - f << " " << sqrt(statDf / statN) << std::endl; - } - } - // cout << "Spline2D standard deviation : " << sqrt(statDf / statN) - // << std::endl; - - if (draw) { - delete nt; - delete knots; - nt = new TNtuple("nt", "nt", "u:v:f:s"); - knots = new TNtuple("knots", "knots", "type:u:v:s"); - double stepU = .3; - for (double u = 0; u < uMax; u += stepU) { - for (double v = 0; v < uMax; v += stepU) { - DataT f[Ndim]; - F(u, v, f); - DataT s[Ndim]; - spline.interpolate(u, v, s); - nt->Fill(u, v, f[0], s[0]); - } - } - nt->SetMarkerStyle(8); - - nt->SetMarkerSize(.5); - nt->SetMarkerColor(kBlue); - nt->Draw("s:u:v", "", ""); - - nt->SetMarkerColor(kGray); - nt->SetMarkerSize(2.); - nt->Draw("f:u:v", "", "same"); - - nt->SetMarkerSize(.5); - nt->SetMarkerColor(kBlue); - nt->Draw("s:u:v", "", "same"); - - for (int i = 0; i < nKnots; i++) { - for (int j = 0; j < nKnots; j++) { - double u = spline.getGridU1().getKnot(i).u; - double v = spline.getGridU2().getKnot(j).u; - DataT s[Ndim]; - spline.interpolate(u, v, s); - knots->Fill(1, u, v, s[0]); - } - } - - knots->SetMarkerStyle(8); - knots->SetMarkerSize(1.5); - knots->SetMarkerColor(kRed); - knots->SetMarkerSize(1.5); - knots->Draw("s:u:v", "type==1", "same"); // knots - - if (drawDataPoints) { - SplineHelper2D<DataT> helper; - helper.setSpline(spline, 4, 4); - for (int ipu = 0; ipu < helper.getHelperU1().getNumberOfDataPoints(); ipu++) { - const typename SplineHelper1D<DataT>::DataPoint& pu = helper.getHelperU1().getDataPoint(ipu); - for (int ipv = 0; ipv < helper.getHelperU2().getNumberOfDataPoints(); ipv++) { - const typename SplineHelper1D<DataT>::DataPoint& pv = helper.getHelperU2().getDataPoint(ipv); - if (pu.isKnot && pv.isKnot) { - continue; - } - DataT s[Ndim]; - spline.interpolate(pu.u, pv.u, s); - knots->Fill(2, pu.u, pv.u, s[0]); - } - } - knots->SetMarkerColor(kBlack); - knots->SetMarkerSize(1.); - knots->Draw("s:u:v", "type==2", "same"); // data points - } - - if (!ask()) { - break; - } - } - } - // delete canv; - // delete nt; - // delete knots; - - statDf = sqrt(statDf / statN); - statDf1D = sqrt(statDf1D / statN); - - cout << "\n std dev for Spline2D : " << statDf << std::endl; - cout << " mean difference between 1-D and " << Ndim - << "-D splines : " << statDf1D << std::endl; - - if (statDf < 0.15 && statDf1D < 1.e-20) { - cout << "Everything is fine" << endl; - } else { - cout << "Something is wrong!!" << endl; - return -2; - } - - return 0; -} - -#endif // GPUCA_GPUCODE - -template class GPUCA_NAMESPACE::gpu::Spline2DBase<float, false>; -template class GPUCA_NAMESPACE::gpu::Spline2DBase<float, true>; -template class GPUCA_NAMESPACE::gpu::Spline2DBase<double, false>; -template class GPUCA_NAMESPACE::gpu::Spline2DBase<double, true>; +template class GPUCA_NAMESPACE::gpu::Spline2D<float>; +template class GPUCA_NAMESPACE::gpu::Spline2D<double>; diff --git a/GPU/TPCFastTransformation/Spline2D.h b/GPU/TPCFastTransformation/Spline2D.h index bc04816f33b9b..f4dfe34c7b2fc 100644 --- a/GPU/TPCFastTransformation/Spline2D.h +++ b/GPU/TPCFastTransformation/Spline2D.h @@ -17,6 +17,7 @@ #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE2D_H #include "Spline1D.h" +#include "Spline2DSpec.h" #include "FlatObject.h" #include "GPUCommonDef.h" @@ -37,431 +38,75 @@ namespace gpu /// See Spline1D.h for more details. /// /// The spline S(x1,x2) approximates a function F(x1,x2):R^2->R^m, -/// where x1,x2 belong to [x1Min,x1Max] x [x2Min,x2Max]. F value may be multi-dimensional. +/// with 2-dimensional domain and multi-dimensional codomain. +/// x1,x2 belong to [x1min,x1max] x [x2min,x2max]. /// /// --- Example of creating a spline --- /// -/// auto F = [&](float x1, float x2, float f[] ) { +/// auto F = [&](double x1, double x2, double f[] ) { /// f[0] = 1.f + x1 + x2*x2; // F(x1,x2) /// }; /// const int nKnotsU=2; /// const int nKnotsV=3; /// int knotsU[nKnotsU] = {0, 1}; /// int knotsV[nKnotsV] = {0, 2, 5}; -/// Spline2D<float,1> spline(nKnotsU, knotsU, nKnotsV, knotsV ); // prepare memory for 1-dimensional F -/// spline.approximateFunction(0., 1., 0.,1., F); //initialize spline to approximate F on area [0., 1.]x[0., 1.] +/// Spline2D<float,1> spline(nKnotsU, knotsU, nKnotsV, knotsV ); // spline with 1-dimensional codomain +/// spline.approximateFunction(0., 1., 0.,1., F); //initialize spline to approximate F on [0., 1.]x[0., 1.] area /// float S = spline.interpolate(.1, .3 ); // interpolated value at (.1,.3) /// -/// --- See also Spline2D::test(); +/// --- See also Spline2DHelper::test(); /// -/// Base class to store data members and non-inline methods -template <typename DataT, bool isConsistentT> -class Spline2DBase : public FlatObject -{ - public: - /// _____________ Version control __________________________ - - /// Version control - GPUhd() static constexpr int getVersion() { return 1; } - - /// _____________ Constructors / destructors __________________________ - -#if !defined(GPUCA_GPUCODE) - /// constructor && default constructor for the ROOT streamer - Spline2DBase(int nDim = 1); -#else - /// Disable constructors - Spline2DBase() CON_DELETE; -#endif - /// Disable constructors - Spline2DBase(const Spline2DBase&) CON_DELETE; - Spline2DBase& operator=(const Spline2DBase&) CON_DELETE; - - /// Destructor - ~Spline2DBase() CON_DEFAULT; - - /// _______________ Construction interface ________________________ - -#if !defined(GPUCA_GPUCODE) - /// Constructor for a regular spline. - void recreate(int numberOfKnotsU1, int numberOfKnotsU2); - - /// Constructor for an irregular spline - void recreate(int numberOfKnotsU1, const int knotsU1[], int numberOfKnotsU2, const int knotsU2[]); -#endif - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - /// approximate a function F with this spline. - void approximateFunction(DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(DataT x1, DataT x2, DataT f[])> F, - int nAxiliaryDataPointsU1 = 4, int nAxiliaryDataPointsU2 = 4); -#endif - - /// _______________ IO ________________________ - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - /// write a class object to the file - int writeToFile(TFile& outf, const char* name); - - /// read a class object from the file - static Spline2DBase* readFromFile(TFile& inpf, const char* name); -#endif - - /// _______________ Getters ________________________ - - /// Get number of F dimensions - GPUhd() int getFdimensions() const { return mFdim; } - - /// - GPUhd() static constexpr bool isConsistent() { return isConsistentT; } - - /// Get minimal required alignment for the spline parameters - GPUhd() static constexpr size_t getParameterAlignmentBytes() { return 16; } - - /// Number of parameters - GPUhd() int getNumberOfParameters() const { return (4 * mFdim) * getNumberOfKnots(); } - - /// Size of the parameter array in bytes - GPUhd() size_t getSizeOfParameters() const { return sizeof(DataT) * getNumberOfParameters(); } - - /// Get number total of knots: UxV - GPUhd() int getNumberOfKnots() const { return mGridU1.getNumberOfKnots() * mGridU2.getNumberOfKnots(); } - - /// Get 1-D grid for U1 coordinate - GPUhd() const Spline1D<DataT>& getGridU1() const { return mGridU1; } - - /// Get 1-D grid for U2 coordinate - GPUhd() const Spline1D<DataT>& getGridU2() const { return mGridU2; } - - /// Get 1-D grid for U1 or U2 coordinate - GPUhd() const Spline1D<DataT>& getGrid(int iu) const { return (iu == 0) ? mGridU1 : mGridU2; } - - /// Get u1,u2 of i-th knot - GPUhd() void getKnotU(int iKnot, DataT& u1, DataT& u2) const; - - /// Get index of a knot (iKnotU1,iKnotU2) - GPUhd() int getKnotIndex(int iKnotU1, int iKnotU2) const; - - /// Get number of F parameters - GPUhd() DataT* getFparameters() { return mFparameters; } - - /// Get number of F parameters - GPUhd() const DataT* getFparameters() const { return mFparameters; } - - /// _______________ Technical stuff ________________________ - - /// Get offset of GridU flat data in the flat buffer - GPUhd() size_t getGridU1Offset() const { return mGridU1.getFlatBufferPtr() - mFlatBufferPtr; } - - /// Get offset of GridU2 flat data in the flat buffer - GPUhd() size_t getGridU2Offset() const { return mGridU2.getFlatBufferPtr() - mFlatBufferPtr; } - - /// Set X range - GPUhd() void setXrange(DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max); - - /// Print method - void print() const; - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation - /// Test the class functionality - static int test(const bool draw = 0, const bool drawDataPoints = 1); -#endif - - /// _____________ FlatObject functionality, see FlatObject class for description ____________ - - using FlatObject::getBufferAlignmentBytes; - using FlatObject::getClassAlignmentBytes; - -#if !defined(GPUCA_GPUCODE) - void cloneFromObject(const Spline2DBase& obj, char* newFlatBufferPtr); - void moveBufferTo(char* newBufferPtr); -#endif - - using FlatObject::releaseInternalBuffer; - - void destroy(); - void setActualBufferAddress(char* actualFlatBufferPtr); - void setFutureBufferAddress(char* futureFlatBufferPtr); - - private: - int mFdim; ///< dimentionality of F - - protected: /// _____________ Data members ____________ - Spline1D<DataT> mGridU1; ///< grid for U axis - Spline1D<DataT> mGridU2; ///< grid for V axis - DataT* mFparameters; //! (transient!!) F-dependent parameters of the spline -#ifndef GPUCA_ALIROOT_LIB - ClassDefNV(Spline2DBase, 1); -#endif -}; - +/// ================================================================================================== /// -/// The main Spline class. Contains constructors and interpolation. +/// Declare the Spline1D class as a template with one optional parameters. /// -/// F dimensions can be set as a template parameter (faster; the only option for the GPU) -/// or as a constructor parameter (slower; the only option for the ROOT interpretator). -/// In a compiled CPU code one can use both options. +/// Class specializations depend on the XdimT, YdimT values. They can be found in SplineSpecs.h /// -template <typename DataT, int nFdimT = 0, bool isConsistentT = 1> -class Spline2D : public Spline2DBase<DataT, isConsistentT> +/// \param DataT data type: float or double +/// \param YdimT +/// YdimT > 0 : the number of Y dimensions is known at the compile time and is equal to XdimT +/// YdimT = 0 : the number of Y dimensions will be set in the runtime +/// YdimT < 0 : the number of Y dimensions will be set in the runtime, and it will not exceed abs(YdimT) +/// +template <typename DataT, int YdimT = 0> +class Spline2D + : public Spline2DSpec<DataT, YdimT, SplineUtil::getSpec(YdimT)> { - public: - typedef Spline2DBase<DataT, isConsistentT> TBase; + typedef Spline2DContainer<DataT> TVeryBase; + typedef Spline2DSpec<DataT, YdimT, SplineUtil::getSpec(YdimT)> TBase; - /// _____________ Constructors / destructors __________________________ + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + typedef typename TVeryBase::Knot Knot; #if !defined(GPUCA_GPUCODE) - - /// Constructor for a regular spline && default constructor - Spline2D(int numberOfKnotsU1 = 2, int numberOfKnotsU2 = 2); - - /// Constructor for an irregular spline - Spline2D(int numberOfKnotsU1, const int knotsU1[], int numberOfKnotsU2, const int knotsU2[]); - - /// Copy constructor - Spline2D(const Spline2D&); + using TBase::TBase; // inherit constructors /// Assignment operator - Spline2D& operator=(const Spline2D&); + Spline2D& operator=(const Spline2D& v) + { + TVeryBase::cloneFromObject(v, nullptr); + return *this; + } #else /// Disable constructors for the GPU implementation Spline2D() CON_DELETE; Spline2D(const Spline2D&) CON_DELETE; - Spline2D& operator=(const Spline2D&) CON_DELETE; #endif - /// Destructor - ~Spline2D() CON_DEFAULT; - - /// _______________ Main functionality ________________________ - - /// Get interpolated value for F(x1,x2) - GPUhd() void interpolate(DataT x1, DataT x2, GPUgeneric() DataT S[]) const; - - /// Get interpolated value for the first dimension of F(x1,x2). (Simplified interface for 1D) - GPUhd() DataT interpolate(DataT x1, DataT x2) const; - - /// Same as interpolate(), but using vectorized calculation. - GPUhd() void interpolateVec(DataT x1, DataT x2, GPUgeneric() DataT S[]) const; - - /// ================ Expert tools ================================ - - /// Get interpolated value for an nFdim-dimensional F(u) using spline parameters Fparameters. - /// Fparameters can be created via SplineHelper2D. - GPUhd() void interpolateU(GPUgeneric() const DataT Fparameters[], - DataT u1, DataT u2, GPUgeneric() DataT Su[]) const; - - /// Same as interpolateU(), but using vectorized calculation. - GPUhd() void interpolateUvec(GPUgeneric() const DataT Fparameters[], - DataT u1, DataT u2, GPUgeneric() DataT Su[]) const; - - /// _______________ IO ________________________ - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - /// write a class object to the file - using TBase::writeToFile; - +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) /// read a class object from the file static Spline2D* readFromFile(TFile& inpf, const char* name) { - return reinterpret_cast<Spline2D*>(TBase::readFromFile(inpf, name)); - } -#endif - - /// _______________ Getters ________________________ - - /// Get number of F dimensions. - GPUhd() static constexpr int getFdimensions() { return nFdimT; } - - using TBase::mFparameters; - using TBase::mGridU1; - using TBase::mGridU2; -}; - -/// -/// ======================================================================================================== -/// Inline implementations of some methods -/// ======================================================================================================== -/// - -template <typename DataT, bool isConsistentT> -GPUhdi() void Spline2DBase<DataT, isConsistentT>::getKnotU(int iKnot, DataT& u1, DataT& u2) const -{ - /// Get u1,u2 of i-th knot - int nu1 = mGridU1.getNumberOfKnots(); - int iu2 = iKnot / nu1; - int iu1 = iKnot % nu1; - u1 = mGridU1.getKnot(iu1).u; - u2 = mGridU2.getKnot(iu2).u; -} - -template <typename DataT, bool isConsistentT> -GPUhdi() int Spline2DBase<DataT, isConsistentT>::getKnotIndex(int iKnotU1, int iKnotU2) const -{ - /// Get index of a knot (iKnotU1,iKnotU2) - int nu1 = mGridU1.getNumberOfKnots(); - return nu1 * iKnotU2 + iKnotU1; -} - -template <typename DataT, bool isConsistentT> -GPUhdi() void Spline2DBase<DataT, isConsistentT>::setXrange( - DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max) -{ - mGridU1.setXrange(x1Min, x1Max); - mGridU2.setXrange(x2Min, x2Max); -} - -#if !defined(GPUCA_GPUCODE) - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() Spline2D<DataT, nFdimT, isConsistentT>:: - Spline2D(int numberOfKnotsU1, int numberOfKnotsU2) - : Spline2DBase<DataT, isConsistentT>(nFdimT) -{ - this->recreate(numberOfKnotsU1, numberOfKnotsU2); -} - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() Spline2D<DataT, nFdimT, isConsistentT>:: - Spline2D(int numberOfKnotsU1, const int knotsU1[], - int numberOfKnotsU2, const int knotsU2[]) - : Spline2DBase<DataT, isConsistentT>(nFdimT) -{ - this->recreate(numberOfKnotsU1, knotsU1, numberOfKnotsU2, knotsU2); -} - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() Spline2D<DataT, nFdimT, isConsistentT>:: - Spline2D(const Spline2D& spline) - : Spline2DBase<DataT, isConsistentT>(nFdimT) -{ - this->cloneFromObject(spline, nullptr); -} - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhd() Spline2D<DataT, nFdimT, isConsistentT>& Spline2D<DataT, nFdimT, isConsistentT>:: - operator=(const Spline2D<DataT, nFdimT, isConsistentT>& spline) -{ - this->cloneFromObject(spline, nullptr); - return *this; -} -#endif - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() void Spline2D<DataT, nFdimT, isConsistentT>:: - interpolate(DataT x1, DataT x2, GPUgeneric() DataT S[]) const -{ - /// Get interpolated value for F(x1,x2) - assert(isConsistentT); - if (isConsistentT) { - interpolateU(mFparameters, mGridU1.convXtoU(x1), mGridU2.convXtoU(x2), S); - } else { - for (int i = 0; i < nFdimT; i++) { - S[i] = -1.; - } + return (Spline2D*)TVeryBase::readFromFile(inpf, name); } -} - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() DataT Spline2D<DataT, nFdimT, isConsistentT>:: - interpolate(DataT x1, DataT x2) const -{ - /// Simplified interface for 1D: get interpolated value for the first dimension of F(x) - -#if defined(GPUCA_GPUCODE) - DataT S[nFdimT]; // constexpr array size for the GPU compiler -#else - DataT S[getFdimensions()]; #endif - interpolate(x1, x2, S); - return S[0]; -} -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() void Spline2D<DataT, nFdimT, isConsistentT>::interpolateVec( - DataT x1, DataT x2, GPUgeneric() DataT S[]) const -{ - /// Same as interpolate(), but using vectorized calculation - assert(isConsistentT); - if (isConsistentT) { - interpolateUvec(mFparameters, mGridU1.convXtoU(x1), mGridU2.convXtoU(x2), S); - } else { - for (int i = 0; i < getFdimensions(); i++) { - S[i] = 0.; - } - } -} - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() void Spline2D<DataT, nFdimT, isConsistentT>::interpolateU( - GPUgeneric() const DataT Fparameters[], - DataT u, DataT v, GPUgeneric() DataT S[]) const -{ - /// Get interpolated value for an nFdim-dimensional F(u) using spline parameters Fparameters. - /// Fparameters can be created via SplineHelper2D. - - int nu = mGridU1.getNumberOfKnots(); - int iu = mGridU1.getKnotIndexU(u); - int iv = mGridU2.getKnotIndexU(v); - - const typename Spline1D<DataT>::Knot& knotU = mGridU1.getKnot(iu); - const typename Spline1D<DataT>::Knot& knotV = mGridU2.getKnot(iv); - -#if defined(GPUCA_GPUCODE) // constexpr array size for the GPU compiler - const int nFdim = nFdimT; -#else - const int nFdim = getFdimensions(); +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline2D, 0); #endif - - const int nFdim2 = nFdimT * 2; - const int nFdim4 = nFdimT * 4; - - // X:=Sx, Y:=Sy, Z:=Sz - - const DataT* par00 = Fparameters + (nu * iv + iu) * nFdim4; // values { {X,Y,Z}, {X,Y,Z}'v, {X,Y,Z}'u, {X,Y,Z}''vu } at {u0, v0} - const DataT* par10 = par00 + nFdim4; // values { ... } at {u1, v0} - const DataT* par01 = par00 + nFdim4 * nu; // values { ... } at {u0, v1} - const DataT* par11 = par01 + nFdim4; // values { ... } at {u1, v1} - - DataT Su0[nFdim4]; // values { {X,Y,Z,X'v,Y'v,Z'v}(v0), {X,Y,Z,X'v,Y'v,Z'v}(v1) }, at u0 - DataT Du0[nFdim4]; // derivatives {}'_u at u0 - DataT Su1[nFdim4]; // values { {X,Y,Z,X'v,Y'v,Z'v}(v0), {X,Y,Z,X'v,Y'v,Z'v}(v1) }, at u1 - DataT Du1[nFdim4]; // derivatives {}'_u at u1 - - for (int i = 0; i < nFdim2; i++) { - Su0[i] = par00[i]; - Su0[nFdim2 + i] = par01[i]; - - Du0[i] = par00[nFdim2 + i]; - Du0[nFdim2 + i] = par01[nFdim2 + i]; - - Su1[i] = par10[i]; - Su1[nFdim2 + i] = par11[i]; - - Du1[i] = par10[nFdim2 + i]; - Du1[nFdim2 + i] = par11[nFdim2 + i]; - } - - DataT parU[nFdim4]; // interpolated values { {X,Y,Z,X'v,Y'v,Z'v}(v0), {X,Y,Z,X'v,Y'v,Z'v}(v1) } at u - mGridU1.interpolateU(nFdim4, knotU, Su0, Du0, Su1, Du1, u, parU); - - const DataT* Sv0 = parU + 0; - const DataT* Dv0 = parU + nFdim; - const DataT* Sv1 = parU + nFdim2; - const DataT* Dv1 = parU + nFdim2 + nFdim; - - mGridU2.interpolateU(nFdim, knotV, Sv0, Dv0, Sv1, Dv1, v, S); -} - -template <typename DataT, int nFdimT, bool isConsistentT> -GPUhdi() void Spline2D<DataT, nFdimT, isConsistentT>::interpolateUvec( - GPUgeneric() const DataT Fparameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[]) const -{ - /// Same as interpolateU(), but using vectorized calculation - interpolateU(mFparameters, u1, u2, S); -} +}; } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/TPCFastTransformation/Spline2DHelper.cxx b/GPU/TPCFastTransformation/Spline2DHelper.cxx new file mode 100644 index 0000000000000..580db36949067 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline2DHelper.cxx @@ -0,0 +1,486 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline2DHelper.cxx +/// \brief Implementation of Spline2DHelper class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + +#include "Spline2DHelper.h" +#include "TMath.h" +#include "TMatrixD.h" +#include "TVectorD.h" +#include "TDecompBK.h" + +#include <vector> +#include "TRandom.h" +#include "TMath.h" +#include "TCanvas.h" +#include "TNtuple.h" +#include "TFile.h" +#include "GPUCommonMath.h" +#include <iostream> + +using namespace GPUCA_NAMESPACE::gpu; + +template <typename DataT> +Spline2DHelper<DataT>::Spline2DHelper() : mError(), mFdimensions(0), mHelperU1(), mHelperU2() +{ +} + +template <typename DataT> +int Spline2DHelper<DataT>::storeError(int code, const char* msg) +{ + mError = msg; + return code; +} + +template <typename DataT> +void Spline2DHelper<DataT>::approximateFunction( + DataT* Fparameters, double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(double x1, double x2, double f[/*spline.getYdimensions()*/])> F) const +{ + /// Create best-fit spline parameters for a given input function F + /// output in Fparameters + + std::vector<double> dataPointF(getNumberOfDataPoints() * mFdimensions); + + double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); + double scaleX2 = (x2Max - x2Min) / ((double)mHelperU2.getSpline().getUmax()); + + for (int iv = 0; iv < getNumberOfDataPointsU2(); iv++) { + double x2 = x2Min + mHelperU2.getDataPoint(iv).u * scaleX2; + for (int iu = 0; iu < getNumberOfDataPointsU1(); iu++) { + double x1 = x1Min + mHelperU1.getDataPoint(iu).u * scaleX1; + F(x1, x2, &dataPointF[(iv * getNumberOfDataPointsU1() + iu) * mFdimensions]); + } + } + approximateFunction(Fparameters, dataPointF.data()); +} + +template <typename DataT> +void Spline2DHelper<DataT>::approximateFunctionBatch( + DataT* Fparameters, double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(const std::vector<double>& x1, const std::vector<double>& x2, std::vector<double> f[/*mFdimensions*/])> F, + unsigned int batchsize) const +{ + /// Create best-fit spline parameters for a given input function F. + /// F calculates values for a batch of points. + /// output in Fparameters + + std::vector<double> dataPointF(getNumberOfDataPoints() * mFdimensions); + + double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); + double scaleX2 = (x2Max - x2Min) / ((double)mHelperU2.getSpline().getUmax()); + + std::vector<double> x1; + x1.reserve(batchsize); + + std::vector<double> x2; + x2.reserve(batchsize); + + std::vector<int> index; + index.reserve(batchsize); + + std::vector<double> dataPointFTmp[mFdimensions]; + for (unsigned int iDim = 0; iDim < mFdimensions; ++iDim) { + dataPointFTmp[iDim].reserve(batchsize); + } + + unsigned int counter = 0; + for (int iv = 0; iv < getNumberOfDataPointsU2(); iv++) { + double x2Tmp = x2Min + mHelperU2.getDataPoint(iv).u * scaleX2; + for (int iu = 0; iu < getNumberOfDataPointsU1(); iu++) { + double x1Tmp = x1Min + mHelperU1.getDataPoint(iu).u * scaleX1; + x1.emplace_back(x1Tmp); + x2.emplace_back(x2Tmp); + index.emplace_back((iv * getNumberOfDataPointsU1() + iu) * mFdimensions); + ++counter; + + if (counter == batchsize || (iu == (getNumberOfDataPointsU1() - 1) && (iv == (getNumberOfDataPointsU2() - 1)))) { + counter = 0; + F(x1, x2, dataPointFTmp); + unsigned int entries = index.size(); + + for (unsigned int i = 0; i < entries; ++i) { + const unsigned int indexTmp = index[i]; + for (unsigned int iDim = 0; iDim < mFdimensions; ++iDim) { + dataPointF[indexTmp + iDim] = dataPointFTmp[iDim][i]; + } + } + + x1.clear(); + x2.clear(); + index.clear(); + for (unsigned int iDim = 0; iDim < mFdimensions; ++iDim) { + dataPointFTmp[iDim].clear(); + } + } + } + } + approximateFunction(Fparameters, dataPointF.data()); +} + +template <typename DataT> +void Spline2DHelper<DataT>::approximateFunction( + DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const +{ + /// approximate a function given as an array of values at data points + + const int Ndim = mFdimensions; + const int Ndim2 = 2 * Ndim; + const int Ndim3 = 3 * Ndim; + const int Ndim4 = 4 * Ndim; + + int nDataPointsU = getNumberOfDataPointsU1(); + int nDataPointsV = getNumberOfDataPointsU2(); + + int nKnotsU = mHelperU1.getSpline().getNumberOfKnots(); + int nKnotsV = mHelperU2.getSpline().getNumberOfKnots(); + + std::unique_ptr<double[]> rotDataPointF(new double[nDataPointsU * nDataPointsV * Ndim]); // U DataPoints x V DataPoints : rotated DataPointF for one output dimension + std::unique_ptr<double[]> Dv(new double[nKnotsV * nDataPointsU * Ndim]); // V knots x U DataPoints + + std::unique_ptr<DataT[]> parU(new DataT[mHelperU1.getSpline().calcNumberOfParameters(Ndim)]); + std::unique_ptr<DataT[]> parV(new DataT[mHelperU2.getSpline().calcNumberOfParameters(Ndim)]); + + std::unique_ptr<double[]> parUdbl(new double[mHelperU1.getSpline().calcNumberOfParameters(Ndim)]); + std::unique_ptr<double[]> parVdbl(new double[mHelperU2.getSpline().calcNumberOfParameters(Ndim)]); + + // rotated data points (u,v)->(v,u) + + for (int ipu = 0; ipu < nDataPointsU; ipu++) { + for (int ipv = 0; ipv < nDataPointsV; ipv++) { + for (int dim = 0; dim < Ndim; dim++) { + rotDataPointF[Ndim * (ipu * nDataPointsV + ipv) + dim] = DataPointF[Ndim * (ipv * nDataPointsU + ipu) + dim]; + } + } + } + + // get S and S'u at all the knots by interpolating along the U axis + + for (int iKnotV = 0; iKnotV < nKnotsV; ++iKnotV) { + int ipv = mHelperU2.getKnotDataPoint(iKnotV); + const double* DataPointFrow = &(DataPointF[Ndim * ipv * nDataPointsU]); + mHelperU1.approximateFunctionGradually(parU.get(), DataPointFrow); + + for (int i = 0; i < mHelperU1.getSpline().calcNumberOfParameters(Ndim); i++) { + parUdbl[i] = parU[i]; + } + for (int iKnotU = 0; iKnotU < nKnotsU; ++iKnotU) { + DataT* knotPar = &Fparameters[Ndim4 * (iKnotV * nKnotsU + iKnotU)]; + for (int dim = 0; dim < Ndim; ++dim) { + knotPar[dim] = parU[Ndim * (2 * iKnotU) + dim]; // store S for all the knots + knotPar[Ndim2 + dim] = parU[Ndim * (2 * iKnotU) + Ndim + dim]; // store S'u for all the knots //SG!!! + } + } + + // recalculate F values for all ipu DataPoints at V = ipv + for (int ipu = 0; ipu < nDataPointsU; ipu++) { + double splineF[Ndim]; + double u = mHelperU1.getDataPoint(ipu).u; + mHelperU1.getSpline().interpolateU(Ndim, parUdbl.get(), u, splineF); + for (int dim = 0; dim < Ndim; dim++) { + rotDataPointF[(ipu * nDataPointsV + ipv) * Ndim + dim] = splineF[dim]; + } + } + } + + // calculate S'v at all data points with V == V of a knot + + for (int ipu = 0; ipu < nDataPointsU; ipu++) { + const double* DataPointFcol = &(rotDataPointF[ipu * nDataPointsV * Ndim]); + mHelperU2.approximateFunctionGradually(parV.get(), DataPointFcol); + for (int iKnotV = 0; iKnotV < nKnotsV; iKnotV++) { + for (int dim = 0; dim < Ndim; dim++) { + double dv = parV[(iKnotV * 2 + 1) * Ndim + dim]; + Dv[(iKnotV * nDataPointsU + ipu) * Ndim + dim] = dv; + } + } + } + + // fit S'v and S''_vu at all the knots + + for (int iKnotV = 0; iKnotV < nKnotsV; ++iKnotV) { + const double* Dvrow = &(Dv[iKnotV * nDataPointsU * Ndim]); + mHelperU1.approximateFunction(parU.get(), Dvrow); + for (int iKnotU = 0; iKnotU < nKnotsU; ++iKnotU) { + for (int dim = 0; dim < Ndim; ++dim) { + Fparameters[Ndim4 * (iKnotV * nKnotsU + iKnotU) + Ndim + dim] = parU[Ndim * 2 * iKnotU + dim]; // store S'v for all the knots + Fparameters[Ndim4 * (iKnotV * nKnotsU + iKnotU) + Ndim3 + dim] = parU[Ndim * 2 * iKnotU + Ndim + dim]; // store S''vu for all the knots + } + } + } +} + +#ifndef GPUCA_ALIROOT_LIB +template <typename DataT> +int Spline2DHelper<DataT>::test(const bool draw, const bool drawDataPoints) +{ + using namespace std; + + const int Ndim = 3; + + const int Fdegree = 4; + + double Fcoeff[Ndim][4 * (Fdegree + 1) * (Fdegree + 1)]; + + constexpr int nKnots = 4; + constexpr int nAuxiliaryPoints = 1; + constexpr int uMax = nKnots * 3; + + auto F = [&](double u, double v, double Fuv[]) { + const double scale = TMath::Pi() / uMax; + double uu = u * scale; + double vv = v * scale; + double cosu[Fdegree + 1], sinu[Fdegree + 1], cosv[Fdegree + 1], sinv[Fdegree + 1]; + double ui = 0, vi = 0; + for (int i = 0; i <= Fdegree; i++, ui += uu, vi += vv) { + GPUCommonMath::SinCosd(ui, sinu[i], cosu[i]); + GPUCommonMath::SinCosd(vi, sinv[i], cosv[i]); + } + for (int dim = 0; dim < Ndim; dim++) { + double f = 0; // Fcoeff[dim][0]/2; + for (int i = 1; i <= Fdegree; i++) { + for (int j = 1; j <= Fdegree; j++) { + double* c = &(Fcoeff[dim][4 * (i * Fdegree + j)]); + f += c[0] * cosu[i] * cosv[j]; + f += c[1] * cosu[i] * sinv[j]; + f += c[2] * sinu[i] * cosv[j]; + f += c[3] * sinu[i] * sinv[j]; + } + } + Fuv[dim] = f; + } + }; + + TCanvas* canv = nullptr; + TNtuple* nt = nullptr; + TNtuple* knots = nullptr; + + auto ask = [&]() -> bool { + if (!canv) { + return 0; + } + canv->Update(); + cout << "type 'q ' to exit" << endl; + std::string str; + std::getline(std::cin, str); + return (str != "q" && str != ".q"); + }; + + std::cout << "Test 2D interpolation with the compact spline" << std::endl; + + int nTries = 10; + + if (draw) { + canv = new TCanvas("cQA", "Spline2D QA", 1500, 800); + nTries = 10000; + } + + long double statDf = 0; + long double statDf1D = 0; + long double statN = 0; + + for (int seed = 1; seed < nTries + 1; seed++) { + //cout << "next try.." << endl; + + gRandom->SetSeed(seed); + + for (int dim = 0; dim < Ndim; dim++) { + for (int i = 0; i < 4 * (Fdegree + 1) * (Fdegree + 1); i++) { + Fcoeff[dim][i] = gRandom->Uniform(-1, 1); + } + } + + Spline2D<DataT, Ndim> spline; + + int knotsU[nKnots], knotsV[nKnots]; + do { + knotsU[0] = 0; + knotsV[0] = 0; + double du = 1. * uMax / (nKnots - 1); + for (int i = 1; i < nKnots; i++) { + knotsU[i] = (int)(i * du); // + gRandom->Uniform(-du / 3, du / 3); + knotsV[i] = (int)(i * du); // + gRandom->Uniform(-du / 3, du / 3); + } + knotsU[nKnots - 1] = uMax; + knotsV[nKnots - 1] = uMax; + spline.recreate(nKnots, knotsU, nKnots, knotsV); + + if (nKnots != spline.getGridX1().getNumberOfKnots() || + nKnots != spline.getGridX2().getNumberOfKnots()) { + cout << "warning: n knots changed during the initialisation " << nKnots + << " -> " << spline.getNumberOfKnots() << std::endl; + continue; + } + } while (0); + + std::string err = FlatObject::stressTest(spline); + if (!err.empty()) { + cout << "error at FlatObject functionality: " << err << endl; + return -1; + } else { + // cout << "flat object functionality is ok" << endl; + } + + // Ndim-D spline + spline.approximateFunction(0., uMax, 0., uMax, F, 4, 4); + + //if (itry == 0) + if (1) { + TFile outf("testSpline2D.root", "recreate"); + if (outf.IsZombie()) { + cout << "Failed to open output file testSpline2D.root " << std::endl; + } else { + const char* name = "spline2Dtest"; + spline.writeToFile(outf, name); + Spline2D<DataT, Ndim>* p = Spline2D<DataT, Ndim>::readFromFile(outf, name); + if (p == nullptr) { + cout << "Failed to read Spline1DOld from file testSpline1DOld.root " << std::endl; + } else { + spline = *p; + } + outf.Close(); + } + } + + // 1-D splines for each of Ndim dimensions + + Spline2D<DataT, 1> splines1D[Ndim]; + + for (int dim = 0; dim < Ndim; dim++) { + auto F1 = [&](double x1, double x2, double f[]) { + double ff[Ndim]; + F(x1, x2, ff); + f[0] = ff[dim]; + }; + splines1D[dim].recreate(nKnots, knotsU, nKnots, knotsV); + splines1D[dim].approximateFunction(0., uMax, 0., uMax, F1, 4, 4); + } + + double stepU = .1; + for (double u = 0; u < uMax; u += stepU) { + for (double v = 0; v < uMax; v += stepU) { + double f[Ndim]; + F(u, v, f); + DataT s[Ndim]; + spline.interpolate(u, v, s); + for (int dim = 0; dim < Ndim; dim++) { + statDf += (s[dim] - f[dim]) * (s[dim] - f[dim]); + DataT s1 = splines1D[dim].interpolate(u, v); + statDf1D += (s[dim] - s1) * (s[dim] - s1); + } + statN += Ndim; + // cout << u << " " << v << ": f " << f << " s " << s << " df " + // << s - f << " " << sqrt(statDf / statN) << std::endl; + } + } + // cout << "Spline2D standard deviation : " << sqrt(statDf / statN) + // << std::endl; + + if (draw) { + delete nt; + delete knots; + nt = new TNtuple("nt", "nt", "u:v:f:s"); + knots = new TNtuple("knots", "knots", "type:u:v:s"); + double stepU = .3; + for (double u = 0; u < uMax; u += stepU) { + for (double v = 0; v < uMax; v += stepU) { + double f[Ndim]; + F(u, v, f); + DataT s[Ndim]; + spline.interpolate(u, v, s); + nt->Fill(u, v, f[0], s[0]); + } + } + nt->SetMarkerStyle(8); + + nt->SetMarkerSize(.5); + nt->SetMarkerColor(kBlue); + nt->Draw("s:u:v", "", ""); + + nt->SetMarkerColor(kGray); + nt->SetMarkerSize(2.); + nt->Draw("f:u:v", "", "same"); + + nt->SetMarkerSize(.5); + nt->SetMarkerColor(kBlue); + nt->Draw("s:u:v", "", "same"); + + for (int i = 0; i < nKnots; i++) { + for (int j = 0; j < nKnots; j++) { + double u = spline.getGridX1().getKnot(i).u; + double v = spline.getGridX2().getKnot(j).u; + DataT s[Ndim]; + spline.interpolate(u, v, s); + knots->Fill(1, u, v, s[0]); + } + } + + knots->SetMarkerStyle(8); + knots->SetMarkerSize(1.5); + knots->SetMarkerColor(kRed); + knots->SetMarkerSize(1.5); + knots->Draw("s:u:v", "type==1", "same"); // knots + + if (drawDataPoints) { + Spline2DHelper<DataT> helper; + helper.setSpline(spline, 4, 4); + for (int ipu = 0; ipu < helper.getHelperU1().getNumberOfDataPoints(); ipu++) { + const typename Spline1DHelper<DataT>::DataPoint& pu = helper.getHelperU1().getDataPoint(ipu); + for (int ipv = 0; ipv < helper.getHelperU2().getNumberOfDataPoints(); ipv++) { + const typename Spline1DHelper<DataT>::DataPoint& pv = helper.getHelperU2().getDataPoint(ipv); + if (pu.isKnot && pv.isKnot) { + continue; + } + DataT s[Ndim]; + spline.interpolate(pu.u, pv.u, s); + knots->Fill(2, pu.u, pv.u, s[0]); + } + } + knots->SetMarkerColor(kBlack); + knots->SetMarkerSize(1.); + knots->Draw("s:u:v", "type==2", "same"); // data points + } + + if (!ask()) { + break; + } + } + } + // delete canv; + // delete nt; + // delete knots; + + statDf = sqrt(statDf / statN); + statDf1D = sqrt(statDf1D / statN); + + cout << "\n std dev for Spline2D : " << statDf << std::endl; + cout << " mean difference between 1-D and " << Ndim + << "-D splines : " << statDf1D << std::endl; + + if (statDf < 0.15 && statDf1D < 1.e-20) { + cout << "Everything is fine" << endl; + } else { + cout << "Something is wrong!!" << endl; + return -2; + } + + return 0; +} +#endif + +template class GPUCA_NAMESPACE::gpu::Spline2DHelper<float>; +template class GPUCA_NAMESPACE::gpu::Spline2DHelper<double>; + +#endif diff --git a/GPU/TPCFastTransformation/Spline2DHelper.h b/GPU/TPCFastTransformation/Spline2DHelper.h new file mode 100644 index 0000000000000..76f11bf8cc709 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline2DHelper.h @@ -0,0 +1,153 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline2DHelper.h +/// \brief Definition of Spline2DHelper class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_Spline2DHelper_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_Spline2DHelper_H + +#include <cmath> +#include <vector> + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "Spline1D.h" +#include "Spline2D.h" +#include "Spline1DHelper.h" +#include <functional> +#include <string> + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +/// +/// The Spline2DHelper class is to initialize Spline* objects +/// +template <typename DataT> +class Spline2DHelper +{ + public: + /// _____________ Constructors / destructors __________________________ + + /// Default constructor + Spline2DHelper(); + + /// Copy constructor: disabled + Spline2DHelper(const Spline2DHelper&) CON_DELETE; + + /// Assignment operator: disabled + Spline2DHelper& operator=(const Spline2DHelper&) CON_DELETE; + + /// Destructor + ~Spline2DHelper() CON_DEFAULT; + + /// _______________ Main functionality ________________________ + + /// Create best-fit spline parameters for a given input function F + void approximateFunction( + Spline2DContainer<DataT>& spline, + double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(double x1, double x2, double f[/*spline.getYdimensions()*/])> F, + int nAuxiliaryDataPointsU1 = 4, int nAuxiliaryDataPointsU2 = 4); + + /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ + + /// precompute everything needed for the construction + int setSpline(const Spline2DContainer<DataT>& spline, int nAuxiliaryPointsU1, int nAuxiliaryPointsU2); + + /// approximate std::function, output in Fparameters + void approximateFunction( + DataT* Fparameters, double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(double x1, double x2, double f[/*mFdimensions*/])> F) const; + + /// approximate std::function, output in Fparameters. F calculates values for a batch of points. + void approximateFunctionBatch( + DataT* Fparameters, double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(const std::vector<double>& x1, const std::vector<double>& x2, std::vector<double> f[/*mFdimensions*/])> F, + unsigned int batchsize) const; + + /// approximate a function given as an array of values at data points + void approximateFunction( + DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; + + int getNumberOfDataPointsU1() const { return mHelperU1.getNumberOfDataPoints(); } + + int getNumberOfDataPointsU2() const { return mHelperU2.getNumberOfDataPoints(); } + + int getNumberOfDataPoints() const { return getNumberOfDataPointsU1() * getNumberOfDataPointsU2(); } + + const Spline1DHelper<DataT>& getHelperU1() const { return mHelperU1; } + const Spline1DHelper<DataT>& getHelperU2() const { return mHelperU2; } + + /// _______________ Utilities ________________________ + + /// Gives error string + const char* getLastError() const { return mError.c_str(); } + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) // code invisible on GPU and in the standalone compilation + /// Test the Spline2D class functionality + static int test(const bool draw = 0, const bool drawDataPoints = 1); +#endif + + private: + /// Stores an error message + int storeError(int code, const char* msg); + + std::string mError = ""; ///< error string + int mFdimensions; ///< n of F dimensions + Spline1DHelper<DataT> mHelperU1; + Spline1DHelper<DataT> mHelperU2; + +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline2DHelper, 0); +#endif +}; + +template <typename DataT> +void Spline2DHelper<DataT>::approximateFunction( + Spline2DContainer<DataT>& spline, + double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(double x1, double x2, double f[/*spline.getYdimensions()*/])> F, + int nAuxiliaryDataPointsU1, int nAuxiliaryDataPointsU2) +{ + /// Create best-fit spline parameters for a given input function F + setSpline(spline, nAuxiliaryDataPointsU1, nAuxiliaryDataPointsU2); + approximateFunction(spline.getParameters(), x1Min, x1Max, x2Min, x2Max, F); + spline.setXrange(x1Min, x1Max, x2Min, x2Max); +} + +template <typename DataT> +int Spline2DHelper<DataT>::setSpline( + const Spline2DContainer<DataT>& spline, int nAuxiliaryPointsU, int nAuxiliaryPointsV) +{ + // Prepare creation of 2D irregular spline + // The should be at least one (better, two) Auxiliary measurements on each segnment between two knots and at least 2*nKnots measurements in total + // Returns 0 when the spline can not be constructed with the given nAuxiliaryPoints + + int ret = 0; + mFdimensions = spline.getYdimensions(); + if (mHelperU1.setSpline(spline.getGridX1(), mFdimensions, nAuxiliaryPointsU) != 0) { + ret = storeError(-2, "Spline2DHelper::setSpline2D: error by setting U axis"); + } + if (mHelperU2.setSpline(spline.getGridX2(), mFdimensions, nAuxiliaryPointsV) != 0) { + ret = storeError(-3, "Spline2DHelper::setSpline2D: error by setting V axis"); + } + return ret; +} + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/Spline2DSpec.cxx b/GPU/TPCFastTransformation/Spline2DSpec.cxx new file mode 100644 index 0000000000000..7e96f73e6b254 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline2DSpec.cxx @@ -0,0 +1,232 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline2DSpec.cxx +/// \brief Implementation of Spline2DSpec class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +#include "Rtypes.h" +#endif + +#include "Spline2DSpec.h" + +#if !defined(GPUCA_GPUCODE) +#include <iostream> +#endif + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +#include "TRandom.h" +#include "Riostream.h" +#include "TMath.h" +#include "Spline2DHelper.h" +#include "TCanvas.h" +#include "TNtuple.h" +#include "TFile.h" +#include "GPUCommonMath.h" + +templateClassImp(GPUCA_NAMESPACE::gpu::Spline2DContainer); +templateClassImp(GPUCA_NAMESPACE::gpu::Spline2DSpec); + +#endif + +using namespace std; +using namespace GPUCA_NAMESPACE::gpu; + +template <typename DataT> +void Spline2DContainer<DataT>::destroy() +{ + /// See FlatObject for description + mGridX1.destroy(); + mGridX2.destroy(); + mYdim = 0; + mParameters = nullptr; + FlatObject::destroy(); +} + +template <typename DataT> +void Spline2DContainer<DataT>::setActualBufferAddress(char* actualFlatBufferPtr) +{ + /// See FlatObject for description + + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + + const size_t u2Offset = alignSize(mGridX1.getFlatBufferSize(), mGridX2.getBufferAlignmentBytes()); + int parametersOffset = u2Offset; + //int bufferSize = parametersOffset; + mParameters = nullptr; + + parametersOffset = alignSize(u2Offset + mGridX2.getFlatBufferSize(), getParameterAlignmentBytes()); + //bufferSize = parametersOffset + getSizeOfParameters(); + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); + + mGridX1.setActualBufferAddress(mFlatBufferPtr); + mGridX2.setActualBufferAddress(mFlatBufferPtr + u2Offset); +} + +template <typename DataT> +void Spline2DContainer<DataT>::setFutureBufferAddress(char* futureFlatBufferPtr) +{ + /// See FlatObject for description + char* bufferU = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mGridX1.getFlatBufferPtr()); + char* bufferV = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mGridX2.getFlatBufferPtr()); + mGridX1.setFutureBufferAddress(bufferU); + mGridX2.setFutureBufferAddress(bufferV); + mParameters = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mParameters); + FlatObject::setFutureBufferAddress(futureFlatBufferPtr); +} + +template <typename DataT> +void Spline2DContainer<DataT>::print() const +{ + printf(" Irregular Spline 2D: \n"); + printf(" grid U1: \n"); + mGridX1.print(); + printf(" grid U2: \n"); + mGridX2.print(); +} + +#if !defined(GPUCA_GPUCODE) + +template <typename DataT> +void Spline2DContainer<DataT>::cloneFromObject(const Spline2DContainer<DataT>& obj, char* newFlatBufferPtr) +{ + /// See FlatObject for description + + const char* oldFlatBufferPtr = obj.mFlatBufferPtr; + + FlatObject::cloneFromObject(obj, newFlatBufferPtr); + + mYdim = obj.mYdim; + char* bufferU = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mGridX1.getFlatBufferPtr()); + char* bufferV = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mGridX2.getFlatBufferPtr()); + + mGridX1.cloneFromObject(obj.mGridX1, bufferU); + mGridX2.cloneFromObject(obj.mGridX2, bufferV); + mParameters = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mParameters); +} + +template <typename DataT> +void Spline2DContainer<DataT>::moveBufferTo(char* newFlatBufferPtr) +{ + /// See FlatObject for description + char* oldFlatBufferPtr = mFlatBufferPtr; + FlatObject::moveBufferTo(newFlatBufferPtr); + char* currFlatBufferPtr = mFlatBufferPtr; + mFlatBufferPtr = oldFlatBufferPtr; + setActualBufferAddress(currFlatBufferPtr); +} + +template <typename DataT> +void Spline2DContainer<DataT>::recreate( + int nYdim, + int numberOfKnotsU1, const int knotsU1[], int numberOfKnotsU2, const int knotsU2[]) +{ + /// Constructor for an irregular spline + + mYdim = nYdim; + FlatObject::startConstruction(); + + mGridX1.recreate(0, numberOfKnotsU1, knotsU1); + mGridX2.recreate(0, numberOfKnotsU2, knotsU2); + + const size_t u2Offset = alignSize(mGridX1.getFlatBufferSize(), mGridX2.getBufferAlignmentBytes()); + int parametersOffset = u2Offset + mGridX2.getFlatBufferSize(); + int bufferSize = parametersOffset; + mParameters = nullptr; + + parametersOffset = alignSize(bufferSize, getParameterAlignmentBytes()); + bufferSize = parametersOffset + getSizeOfParameters(); + + FlatObject::finishConstruction(bufferSize); + + mGridX1.moveBufferTo(mFlatBufferPtr); + mGridX2.moveBufferTo(mFlatBufferPtr + u2Offset); + + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); + for (int i = 0; i < getNumberOfParameters(); i++) { + mParameters[i] = 0; + } +} + +template <typename DataT> +void Spline2DContainer<DataT>::recreate(int nYdim, + int numberOfKnotsU1, int numberOfKnotsU2) +{ + /// Constructor for a regular spline + + mYdim = nYdim; + FlatObject::startConstruction(); + + mGridX1.recreate(0, numberOfKnotsU1); + mGridX2.recreate(0, numberOfKnotsU2); + + const size_t u2Offset = alignSize(mGridX1.getFlatBufferSize(), mGridX2.getBufferAlignmentBytes()); + int parametersOffset = u2Offset + mGridX2.getFlatBufferSize(); + int bufferSize = parametersOffset; + mParameters = nullptr; + + parametersOffset = alignSize(bufferSize, getParameterAlignmentBytes()); + bufferSize = parametersOffset + getSizeOfParameters(); + + FlatObject::finishConstruction(bufferSize); + + mGridX1.moveBufferTo(mFlatBufferPtr); + mGridX2.moveBufferTo(mFlatBufferPtr + u2Offset); + + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + parametersOffset); + for (int i = 0; i < getNumberOfParameters(); i++) { + mParameters[i] = 0; + } +} + +#endif // GPUCA_GPUCODE + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation + +template <typename DataT> +void Spline2DContainer<DataT>::approximateFunction( + double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(double x1, double x2, double f[])> F, + int nAuxiliaryDataPointsX1, int nAuxiliaryDataPointsX2) +{ + /// approximate a function F with this spline + Spline2DHelper<DataT> helper; + helper.approximateFunction(*reinterpret_cast<Spline2D<DataT>*>(this), x1Min, x1Max, x2Min, x2Max, F, nAuxiliaryDataPointsX1, nAuxiliaryDataPointsX2); +} + +#ifndef GPUCA_ALIROOT_LIB +template <typename DataT> +int Spline2DContainer<DataT>::writeToFile(TFile& outf, const char* name) +{ + /// write a class object to the file + return FlatObject::writeToFile(*this, outf, name); +} + +template <typename DataT> +Spline2DContainer<DataT>* Spline2DContainer<DataT>::readFromFile( + TFile& inpf, const char* name) +{ + /// read a class object from the file + return FlatObject::readFromFile<Spline2DContainer<DataT>>(inpf, name); +} + +template <typename DataT> +int Spline2DContainer<DataT>::test(const bool draw, const bool drawDataPoints) +{ + return Spline2DHelper<DataT>::test(draw, drawDataPoints); +} +#endif + +#endif // GPUCA_GPUCODE && !GPUCA_STANDALONE + +template class GPUCA_NAMESPACE::gpu::Spline2DContainer<float>; +template class GPUCA_NAMESPACE::gpu::Spline2DContainer<double>; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h new file mode 100644 index 0000000000000..68c8518dc81b6 --- /dev/null +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -0,0 +1,476 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Spline2DSpec.h +/// \brief Definition of Spline2DSpec class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE2DSPEC_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINE2DSPEC_H + +#include "Spline1D.h" +#include "FlatObject.h" +#include "GPUCommonDef.h" +#include "SplineUtil.h" + +#if !defined(__CINT__) && !defined(__ROOTCINT__) && !defined(GPUCA_GPUCODE) && !defined(GPUCA_NO_VC) && defined(__cplusplus) && __cplusplus >= 201703L +#include <Vc/Vc> +#include <Vc/SimdArray> +#endif + +class TFile; + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +/// ================================================================================================== +/// The class Spline2DContainer is a base Spline2D class. +/// It contains all the class members and those methods which only depends on the DataT data type. +/// It also contains all non-inlined methods with the implementation in SplineSpec.cxx file. +/// +/// DataT is a data type, which is supposed to be either double or float. +/// For other possible data types one has to add the corresponding instantiation line +/// at the end of the Spline2DSpec.cxx file +/// +template <typename DataT> +class Spline2DContainer : public FlatObject +{ + public: + typedef typename Spline1D<DataT>::SafetyLevel SafetyLevel; + typedef typename Spline1D<DataT>::Knot Knot; + + /// _____________ Version control __________________________ + + /// Version control + GPUd() static constexpr int getVersion() { return (1 << 16) + Spline1D<DataT>::getVersion(); } + + /// _____________ C++ constructors / destructors __________________________ + + /// Default constructor + Spline2DContainer() CON_DEFAULT; + + /// Disable all other constructors + Spline2DContainer(const Spline2DContainer&) CON_DELETE; + + /// Destructor + ~Spline2DContainer() CON_DEFAULT; + + /// _______________ Construction interface ________________________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// approximate a function F with this spline + void approximateFunction(double x1Min, double x1Max, double x2Min, double x2Max, + std::function<void(double x1, double x2, double f[/*mYdim*/])> F, + int nAuxiliaryDataPointsU1 = 4, int nAuxiliaryDataPointsU2 = 4); +#endif + + /// _______________ IO ________________________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// write a class object to the file + int writeToFile(TFile& outf, const char* name); + + /// read a class object from the file + static Spline2DContainer* readFromFile(TFile& inpf, const char* name); +#endif + + /// _______________ Getters ________________________ + + /// Get number of Y dimensions + GPUd() int getYdimensions() const { return mYdim; } + + /// Get minimal required alignment for the spline parameters + GPUd() static constexpr size_t getParameterAlignmentBytes() { return 16; } + + /// Number of parameters + GPUd() int getNumberOfParameters() const { return this->calcNumberOfParameters(mYdim); } + + /// Size of the parameter array in bytes + GPUd() size_t getSizeOfParameters() const { return sizeof(DataT) * this->getNumberOfParameters(); } + + /// Get a number of knots + GPUd() int getNumberOfKnots() const { return mGridX1.getNumberOfKnots() * mGridX2.getNumberOfKnots(); } + + /// Get 1-D grid for the X1 coordinate + GPUd() const Spline1D<DataT>& getGridX1() const { return mGridX1; } + + /// Get 1-D grid for the X2 coordinate + GPUd() const Spline1D<DataT>& getGridX2() const { return mGridX2; } + + /// Get 1-D grid for X1 or X2 coordinate + GPUd() const Spline1D<DataT>& getGrid(int ix) const { return (ix == 0) ? mGridX1 : mGridX2; } + + /// Get (u1,u2) of i-th knot + GPUd() void getKnotU(int iKnot, int& u1, int& u2) const + { + u1 = mGridX1.getKnot(iKnot % mGridX1.getNumberOfKnots()).getU(); + u2 = mGridX2.getKnot(iKnot / mGridX1.getNumberOfKnots()).getU(); + } + + /// Get index of a knot (iKnotX1,iKnotX2) + GPUd() int getKnotIndex(int iKnotX1, int iKnotX2) const + { + return mGridX1.getNumberOfKnots() * iKnotX2 + iKnotX1; + } + + /// Get spline parameters + GPUd() DataT* getParameters() { return mParameters; } + + /// Get spline parameters const + GPUd() const DataT* getParameters() const { return mParameters; } + + /// _______________ Technical stuff ________________________ + + /// Get offset of GridX1 flat data in the flat buffer + GPUd() size_t getGridX1Offset() const { return mGridX1.getFlatBufferPtr() - mFlatBufferPtr; } + + /// Get offset of GridX2 flat data in the flat buffer + GPUd() size_t getGridX2Offset() const { return mGridX2.getFlatBufferPtr() - mFlatBufferPtr; } + + /// Set X range + GPUd() void setXrange(DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max) + { + mGridX1.setXrange(x1Min, x1Max); + mGridX2.setXrange(x2Min, x2Max); + } + + /// Print method + void print() const; + + /// _______________ Expert tools _______________ + + /// Number of parameters for given Y dimensions + GPUd() int calcNumberOfParameters(int nYdim) const { return (4 * nYdim) * getNumberOfKnots(); } + + ///_______________ Test tools _______________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) // code invisible on GPU and in the standalone compilation + /// Test the class functionality + static int test(const bool draw = 0, const bool drawDataPoints = 1); +#endif + + /// _____________ FlatObject functionality, see FlatObject class for description ____________ + + using FlatObject::getBufferAlignmentBytes; + using FlatObject::getClassAlignmentBytes; + +#if !defined(GPUCA_GPUCODE) + void cloneFromObject(const Spline2DContainer& obj, char* newFlatBufferPtr); + void moveBufferTo(char* newBufferPtr); +#endif + + using FlatObject::releaseInternalBuffer; + + void destroy(); + void setActualBufferAddress(char* actualFlatBufferPtr); + void setFutureBufferAddress(char* futureFlatBufferPtr); + + protected: +#if !defined(GPUCA_GPUCODE) + /// Constructor for a regular spline + void recreate(int nYdim, int nKnotsX1, int nKnotsX2); + + /// Constructor for an irregular spline + void recreate(int nYdim, int nKnotsX1, const int knotU1[], int nKnotsX2, const int knotU2[]); +#endif + + /// _____________ Data members ____________ + + int mYdim = 0; ///< dimentionality of F + Spline1D<DataT> mGridX1; ///< grid for U axis + Spline1D<DataT> mGridX2; ///< grid for V axis + DataT* mParameters = nullptr; //! (transient!!) F-dependent parameters of the spline + +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(Spline2DContainer, 1); +#endif +}; + +/// ================================================================================================== +/// +/// Spline2DSpec class declares different specializations of the Spline2D class. +/// They are the same as the Spline1D specializations. (See Spline1DSpec.h) +/// +/// The meaning of the template parameters: +/// +/// \param DataT data type: float or double +/// \param YdimT +/// YdimT > 0 : the number of Y dimensions is known at the compile time and is equal to YdimT +/// YdimT = 0 : the number of Y dimensions will be set in the runtime +/// YdimT < 0 : the number of Y dimensions will be set in the runtime, and it will not exceed abs(XdimT) +/// \param SpecT specialisation number: +/// 0 - a parent class for all other specializations +/// 1 - nYdim>0: nYdim is set at the compile time +/// 2 - nYdim<0: nYdim must be set during runtime +/// 3 - specialization where nYdim==1 (a small add-on on top of the other specs) +/// +template <typename DataT, int YdimT, int SpecT> +class Spline2DSpec; + +/// ================================================================================================== +/// Specialization 0 declares common methods for all other Spline2D specializations. +/// Implementations of the methods may depend on the YdimT value. +/// +template <typename DataT, int YdimT> +class Spline2DSpec<DataT, YdimT, 0> + : public Spline2DContainer<DataT> +{ + typedef Spline2DContainer<DataT> TBase; + + public: + typedef typename TBase::SafetyLevel SafetyLevel; + typedef typename TBase::Knot Knot; + + /// _______________ Interpolation math ________________________ + + /// Get interpolated value S(x) + GPUd() void interpolate(DataT x1, DataT x2, GPUgeneric() DataT S[/*mYdim*/]) const + { + interpolateU<SafetyLevel::kSafe>(mYdim, mParameters, mGridX1.convXtoU(x1), mGridX2.convXtoU(x2), S); + } + + /// Get interpolated value for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() void interpolateU(int inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const + { + + const auto nYdimTmp = SplineUtil::getNdim<YdimT>(inpYdim); + const int nYdim = nYdimTmp.get(); + + const auto maxYdim = SplineUtil::getMaxNdim<YdimT>(inpYdim); + const int maxYdim4 = 4 * maxYdim.get(); + + const auto nYdim2 = nYdim * 2; + const auto nYdim4 = nYdim * 4; + + const float& u = u1; + const float& v = u2; + int nu = mGridX1.getNumberOfKnots(); + int iu = mGridX1.template getLeftKnotIndexForU<SafeT>(u); + int iv = mGridX2.template getLeftKnotIndexForU<SafeT>(v); + + const typename TBase::Knot& knotU = mGridX1.template getKnot<SafetyLevel::kNotSafe>(iu); + const typename TBase::Knot& knotV = mGridX2.template getKnot<SafetyLevel::kNotSafe>(iv); + + const DataT* par00 = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} + const DataT* par10 = par00 + nYdim4; // values { ... } at {u1, v0} + const DataT* par01 = par00 + nYdim4 * nu; // values { ... } at {u0, v1} + const DataT* par11 = par01 + nYdim4; // values { ... } at {u1, v1} + + DataT Su0[maxYdim4]; // values { {Y1,Y2,Y3,Y1'v,Y2'v,Y3'v}(v0), {Y1,Y2,Y3,Y1'v,Y2'v,Y3'v}(v1) }, at u0 + DataT Du0[maxYdim4]; // derivatives {}'_u at u0 + DataT Su1[maxYdim4]; // values { {Y1,Y2,Y3,Y1'v,Y2'v,Y3'v}(v0), {Y1,Y2,Y3,Y1'v,Y2'v,Y3'v}(v1) }, at u1 + DataT Du1[maxYdim4]; // derivatives {}'_u at u1 + + for (int i = 0; i < nYdim2; i++) { + Su0[i] = par00[i]; + Su0[nYdim2 + i] = par01[i]; + + Du0[i] = par00[nYdim2 + i]; + Du0[nYdim2 + i] = par01[nYdim2 + i]; + + Su1[i] = par10[i]; + Su1[nYdim2 + i] = par11[i]; + + Du1[i] = par10[nYdim2 + i]; + Du1[nYdim2 + i] = par11[nYdim2 + i]; + } + + DataT parU[maxYdim4]; // interpolated values { {Y1,Y2,Y3,Y1'v,Y2'v,Y3'v}(v0), {Y1,Y2,Y3,Y1'v,Y2'v,Y3'v}(v1) } at u + + typedef Spline1DSpec<DataT, 4 * YdimT, 0> TGridX1; + const TGridX1& gridX1 = reinterpret_cast<const TGridX1&>(mGridX1); + + gridX1.interpolateU(nYdim4, knotU, Su0, Du0, Su1, Du1, u, parU); + + const DataT* Sv0 = parU + 0; + const DataT* Dv0 = parU + nYdim; + const DataT* Sv1 = parU + nYdim2; + const DataT* Dv1 = parU + nYdim2 + nYdim; + + typedef Spline1DSpec<DataT, YdimT, 0> TGridX2; + const TGridX2& gridX2 = reinterpret_cast<const TGridX2&>(mGridX2); + gridX2.interpolateU(nYdim, knotV, Sv0, Dv0, Sv1, Dv1, v, S); + } + + protected: + using TBase::mGridX1; + using TBase::mGridX2; + using TBase::mParameters; + using TBase::mYdim; + using TBase::TBase; // inherit constructors and hide them +}; + +/// ================================================================================================== +/// Specialization 1: YdimT>0 where the number of Y dimensions is taken from template parameters +/// at the compile time +/// +template <typename DataT, int YdimT> +class Spline2DSpec<DataT, YdimT, 1> + : public Spline2DSpec<DataT, YdimT, 0> +{ + typedef Spline2DContainer<DataT> TVeryBase; + typedef Spline2DSpec<DataT, YdimT, 0> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + +#if !defined(GPUCA_GPUCODE) + /// Default constructor + Spline2DSpec() : Spline2DSpec(2, 2) {} + + /// Constructor for a regular spline + Spline2DSpec(int nKnotsX1, int nKnotsX2) : TBase() + { + recreate(nKnotsX1, nKnotsX2); + } + /// Constructor for an irregular spline + Spline2DSpec(int nKnotsX1, const int knotU1[], + int nKnotsX2, const int knotU2[]) + : TBase() + { + recreate(nKnotsX1, knotU1, nKnotsX2, knotU2); + } + /// Copy constructor + Spline2DSpec(const Spline2DSpec& v) : TBase() + { + TBase::cloneFromObject(v, nullptr); + } + /// Constructor for a regular spline + void recreate(int nKnotsX1, int nKnotsX2) + { + TBase::recreate(YdimT, nKnotsX1, nKnotsX2); + } + + /// Constructor for an irregular spline + void recreate(int nKnotsX1, const int knotU1[], + int nKnotsX2, const int knotU2[]) + { + TBase::recreate(YdimT, nKnotsX1, knotU1, nKnotsX2, knotU2); + } +#endif + + /// Get number of Y dimensions + GPUd() constexpr int getYdimensions() const { return YdimT; } + + /// Number of parameters + GPUd() int getNumberOfParameters() const { return (4 * YdimT) * getNumberOfKnots(); } + + /// Size of the parameter array in bytes + GPUd() size_t getSizeOfParameters() const { return (sizeof(DataT) * 4 * YdimT) * getNumberOfKnots(); } + + /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ + + /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + { + TBase::template interpolateU<SafeT>(YdimT, Parameters, u1, u2, S); + } + + using TBase::getNumberOfKnots; + + /// _______________ Suppress some parent class methods ________________________ + private: +#if !defined(GPUCA_GPUCODE) + using TBase::recreate; +#endif + using TBase::interpolateU; +}; + +/// ================================================================================================== +/// Specialization 2 (YdimT<=0) where the numbaer of Y dimensions +/// must be set in the runtime via a constructor parameter +/// +template <typename DataT, int YdimT> +class Spline2DSpec<DataT, YdimT, 2> + : public Spline2DSpec<DataT, YdimT, 0> +{ + typedef Spline2DContainer<DataT> TVeryBase; + typedef Spline2DSpec<DataT, YdimT, 0> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + +#if !defined(GPUCA_GPUCODE) + /// Default constructor + Spline2DSpec() : Spline2DSpec(0, 2, 2) {} + + /// Constructor for a regular spline + Spline2DSpec(int nYdim, int nKnotsX1, int nKnotsX2) : TBase() + { + TBase::recreate(nYdim, nKnotsX1, nKnotsX2); + } + + /// Constructor for an irregular spline + Spline2DSpec(int nYdim, int nKnotsX1, const int knotU1[], + int nKnotsX2, const int knotU2[]) : TBase() + { + TBase::recreate(nYdim, nKnotsX1, knotU1, nKnotsX2, knotU2); + } + + /// Copy constructor + Spline2DSpec(const Spline2DSpec& v) : TBase() + { + cloneFromObject(v, nullptr); + } + + /// Constructor for a regular spline + void recreate(int nYdim, int nKnotsX1, int nKnotsX2) + { + TBase::recreate(nYdim, nKnotsX1, nKnotsX2); + } + + /// Constructor for an irregular spline + void recreate(int nYdim, int nKnotsX1, const int knotU1[], + int nKnotsX2, const int knotU2[]) + { + TBase::recreate(nYdim, nKnotsX1, knotU1, nKnotsX2, knotU2); + } +#endif + + /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ + + using TBase::interpolateU; +}; + +/// ================================================================================================== +/// Specialization 3, where the number of Y dimensions is 1. +/// +template <typename DataT> +class Spline2DSpec<DataT, 1, 3> + : public Spline2DSpec<DataT, 1, SplineUtil::getSpec(999)> +{ + typedef Spline2DSpec<DataT, 1, SplineUtil::getSpec(999)> TBase; + + public: + using TBase::TBase; // inherit constructors + + /// Simplified interface for 1D: return the interpolated value + GPUd() DataT interpolate(DataT x1, DataT x2) const + { + DataT S = 0.; + TBase::interpolate(x1, x2, &S); + return S; + } + + // this parent method should be public anyhow, + // but w/o this extra declaration compiler gets confused + using TBase::interpolate; +}; +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/SplineHelper.cxx b/GPU/TPCFastTransformation/SplineHelper.cxx new file mode 100644 index 0000000000000..f499f80c07420 --- /dev/null +++ b/GPU/TPCFastTransformation/SplineHelper.cxx @@ -0,0 +1,583 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SplineHelper.cxx +/// \brief Implementation of SplineHelper class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + +#include "SplineHelper.h" +#include "Spline2D.h" + +#include "TMath.h" +#include "TMatrixD.h" +#include "TVectorD.h" +#include "TDecompBK.h" + +#include <vector> +#include "TRandom.h" +#include "TMath.h" +#include "TCanvas.h" +#include "TNtuple.h" +#include "TFile.h" +#include "GPUCommonMath.h" +#include <iostream> + +using namespace GPUCA_NAMESPACE::gpu; + +template <typename DataT> +SplineHelper<DataT>::SplineHelper() : mError(), mXdimensions(0), mFdimensions(0), mNumberOfDataPoints(0), mHelpers() +{ +} + +template <typename DataT> +int SplineHelper<DataT>::storeError(int code, const char* msg) +{ + mError = msg; + return code; +} + +//////////////// +// pointstoarray +// HILFSFUNKTION, +template <typename DataT> +int SplineHelper<DataT>::pointstoarray(const int indices[], const int numbers[], int dim) +{ + + int result = 0; + int factor = 1; + for (int i = 0; i < dim; i++) { + result += indices[i] * factor; + factor *= numbers[i]; + } + return result; +} + +//////////////// +//arraytopoints +// HILFSFUNKTION +template <typename DataT> +int SplineHelper<DataT>::arraytopoints(int point, int result[], const int numbers[], int dim) +{ + + if (point == 0) { + for (int i = 0; i < dim; i++) { + result[i] = 0; + } + } else { + int divisor = 1; + int modoperand = 1; + for (int i = 0; i < dim; i++) { + modoperand *= numbers[i]; + result[i] = (int)((point % modoperand) / divisor); + divisor *= numbers[i]; + } + } + return 0; +} + +template <typename DataT> +void SplineHelper<DataT>::approximateFunction( + DataT* Fparameters, const double xMin[/* mXdimensions */], const double xMax[/* mXdimensions */], + std::function<void(const double x[/* mXdimensions */], double f[/* mFdimensions */])> F) const +{ + /// Create best-fit spline parameters for a given input function F + /// output in Fparameter + // TODO: implement + // MY VERSION + //std::cout << "approximateFunction(Fparameters, xMin[],xMax[],F) :" << std::endl; + double scaleX[mXdimensions]; + for (int i = 0; i < mXdimensions; i++) { + scaleX[i] = (xMax[i] - xMin[i]) / ((double)(mHelpers[i].getSpline().getUmax())); + } + + // calculate F-Values at all datapoints: + int nrOfAllPoints = getNumberOfDataPoints(); + std::vector<double> dataPointF(nrOfAllPoints * mFdimensions); + + int nrOfPoints[mXdimensions]; + for (int i = 0; i < mXdimensions; i++) { + nrOfPoints[i] = mHelpers[i].getNumberOfDataPoints(); + } + double x[mXdimensions]; + for (int d = 0; d < nrOfAllPoints; d++) { // for all DataPoints + + int indices[mXdimensions]; + int modoperand = 1; + int divisor = 1; + + // get the DataPoint index + for (int i = 0; i < mXdimensions; i++) { + modoperand *= nrOfPoints[i]; + + indices[i] = (int)((d % modoperand) / divisor); + divisor *= nrOfPoints[i]; + // get the respecting u-values: + x[i] = xMin[i] + mHelpers[i].getDataPoint(indices[i]).u * scaleX[i]; + } + + for (int j = 0; j < mXdimensions; j++) { + F(x, &dataPointF[d * mFdimensions]); + } + + } // end for all DataPoints d + //END MY VERSION + + //std::vector<DataT> dataPointF(getNumberOfDataPoints() * mFdimensions); + //DUMYY VERSION Commented out + /* for (int i = 0; i < getNumberOfDataPoints() * mFdimensions; i++) { + dataPointF[i] = 1.; + } */ + /* + double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); + double scaleX2 = (x2Max - x2Min) / ((double)mHelperU2.getSpline().getUmax()); + + for (int iv = 0; iv < getNumberOfDataPointsU2(); iv++) { + DataT x2 = x2Min + mHelperU2.getDataPoint(iv).u * scaleX2; + for (int iu = 0; iu < getNumberOfDataPointsU1(); iu++) { + DataT x1 = x1Min + mHelperU1.getDataPoint(iu).u * scaleX1; + F(x1, x2, &dataPointF[(iv * getNumberOfDataPointsU1() + iu) * mFdimensions]); + } + } + */ + approximateFunction(Fparameters, dataPointF.data()); +} + +template <typename DataT> +void SplineHelper<DataT>::approximateFunctionBatch( + DataT* Fparameters, const double xMin[], const double xMax[], + std::function<void(const std::vector<double> x[], double f[/*mFdimensions*/])> F, + unsigned int batchsize) const +{ + /// Create best-fit spline parameters for a given input function F. + /// F calculates values for a batch of points. + /// output in Fparameters + + double scaleX[mXdimensions]; + for (int i = 0; i < mXdimensions; i++) { + scaleX[i] = (xMax[i] - xMin[i]) / ((double)(mHelpers[i].getSpline().getUmax())); + } + + const int nrOfAllPoints = getNumberOfDataPoints(); + std::vector<double> dataPointF(nrOfAllPoints * mFdimensions); + + int nrOfPoints[mXdimensions]; + for (int i = 0; i < mXdimensions; i++) { + nrOfPoints[i] = mHelpers[i].getNumberOfDataPoints(); + } + + std::vector<double> x[mXdimensions]; + for (unsigned int iDim = 0; iDim < mXdimensions; ++iDim) { + x[iDim].reserve(batchsize); + } + + int ibatch = 0; + int index = 0; + for (int d = 0; d < nrOfAllPoints; d++) { // for all DataPoints + int indices[mXdimensions]; + int modoperand = 1; + int divisor = 1; + + // get the DataPoint index + for (int i = 0; i < mXdimensions; i++) { + modoperand *= nrOfPoints[i]; + indices[i] = (int)((d % modoperand) / divisor); + divisor *= nrOfPoints[i]; + // get the respecting u-values: + x[i].emplace_back(xMin[i] + mHelpers[i].getDataPoint(indices[i]).u * scaleX[i]); + } + ++ibatch; + + if (ibatch == batchsize || d == nrOfAllPoints - 1) { + ibatch = 0; + + F(x, &dataPointF[index]); + index = (d + 1) * mFdimensions; + + for (unsigned int iDim = 0; iDim < mXdimensions; ++iDim) { + x[iDim].clear(); + } + } + } // end for all DataPoints d + + approximateFunction(Fparameters, dataPointF.data()); +} + +template <typename DataT> +void SplineHelper<DataT>::approximateFunction( + DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const +{ + /// approximate a function given as an array of values at data points + + int numberOfKnots[mXdimensions]; //getting number of Knots for all dimensions into one array + for (int i = 0; i < mXdimensions; i++) { + numberOfKnots[i] = mHelpers[i].getSpline().getNumberOfKnots(); + } + + int numberOfDataPoints[mXdimensions]; // getting number of datapoints (incl knots) in all dimensions into one array + for (int i = 0; i < mXdimensions; i++) { + numberOfDataPoints[i] = mHelpers[i].getNumberOfDataPoints(); + } + + int numberOfAllKnots = 1; // getting Number of all knots for the entire spline + for (int i = 0; i < mXdimensions; i++) { + numberOfAllKnots *= numberOfKnots[i]; + } + // TO BE REMOVED (TEST-OUTPUT): + std::cout << "total number of knots: " << numberOfAllKnots << ", " << std::endl; + + int numberOfAllDataPoints = 1; // getting Number of all Datapoints for the entire spline + for (int i = 0; i < mXdimensions; i++) { + numberOfAllDataPoints *= numberOfDataPoints[i]; + //std::cout << mHelpers[0].getNumberOfDataPoints()<<std::endl; + } + + // TO BE REMOVED TEST: + //std::cout << "total number of DataPoints (including knots): " << numberOfAllDataPoints << ", "<< std::endl; + + int numberOfParameterTypes = (int)(pow(2.0, mXdimensions)); //number of Parameters per Knot + + // TO BE REMOVED TEST: + //std::cout << "number of paramtertypes per knot : " << numberOfParameterTypes << ", "<< std::endl; + + std::unique_ptr<double[]> allParameters[numberOfParameterTypes]; //Array for the different parametertypes s, s'u, s'v, s''uv,... + for (int i = 0; i < numberOfParameterTypes; i++) { + allParameters[i] = std::unique_ptr<double[]>(new double[numberOfAllDataPoints * mFdimensions]); //To-Do:Fdim!! + } + //filling allParameters[0] and FParameters with s: + for (int i = 0; i < numberOfAllDataPoints; i++) { + for (int f = 0; f < mFdimensions; f++) { // for all f-dimensions + allParameters[0][i * mFdimensions + f] = DataPointF[i * mFdimensions + f]; // TO DO - Just get the pointer adress there PLEASE! + } + int p0indices[mXdimensions]; + arraytopoints(i, p0indices, numberOfDataPoints, mXdimensions); + bool isKnot = 1; + for (int j = 0; j < mXdimensions; j++) { // is the current datapoint a knot? + if (!mHelpers[j].getDataPoint(p0indices[j]).isKnot) { + isKnot = 0; + break; + } + } + if (isKnot) { + int knotindices[mXdimensions]; + for (int j = 0; j < mXdimensions; j++) { // calculate KNotindices for all dimensions + //WORKAROUND Getting Knotindices: + knotindices[j] = p0indices[j] / ((numberOfDataPoints[j] - 1) / (numberOfKnots[j] - 1)); + //knotindices[j] = mHelpers[j].getDataPoint(p0indices[j]).iKnot; //in der Annahme der wert ist ein Knotenindex und falls der datapoint ein knoten ist, gibt er seinen eigenen knotenindex zurück + } + // get the knotindexvalue for FParameters: + int knotind = pointstoarray(knotindices, numberOfKnots, mXdimensions); + + for (int f = 0; f < mFdimensions; f++) { // for all f-dimensions get function values into Fparameters + Fparameters[knotind * numberOfParameterTypes * mFdimensions + f] = DataPointF[i * mFdimensions + f]; ///write derivatives in FParameters + } + } // end if isKnot + } //end i (filling DataPointF Values into allParameters[0] and FParameters) + //now: allParameters[0] = dataPointF; + + //Array for input DataPointF-values for Spline1D::approximateFunctionGradually(...); + std::unique_ptr<double[]> dataPointF1D[mXdimensions]; + for (int i = 0; i < mXdimensions; i++) { + dataPointF1D[i] = std::unique_ptr<double[]>(new double[numberOfDataPoints[i] * mFdimensions]); // To-Do:Fdim!! For s and derivetives at all knots. + } + //Array to be filled by Spline1D::approximateFunctionGradually(...); + std::unique_ptr<DataT[]> par[mXdimensions]; + std::unique_ptr<double[]> parD[mXdimensions]; + + for (int i = 0; i < mXdimensions; i++) { + par[i] = std::unique_ptr<DataT[]>(new DataT[numberOfKnots[i] * mFdimensions * 2]); + parD[i] = std::unique_ptr<double[]>(new double[numberOfKnots[i] * mFdimensions * 2]); + } + + //std::cout << "NumberOfParameters: " << mNumberOfParameters <<std::endl; + + //STARTING MAIN-LOOP, for all Parametertypes: + for (int p = 1; p < numberOfParameterTypes; p++) { // p = 1!! Wir kriegen s (p0) durch approximateFunction()oben + int dimension; // find the dimension for approximation + for (int i = (int)(log2f((float)p)); i >= 0; i--) { + if (p % (int)(pow(2.0, i)) == 0) { + dimension = i; + break; + } + } + + int currentDataPointF = p - (int)(pow(2.0, dimension)); + //std::cout << std::endl << "p:" << p << ", dim of approximation: " << dimension << ", based on: " << currentDataPointF << std::endl; + + int nrOf1DSplines = (numberOfAllDataPoints / numberOfDataPoints[dimension]); // number of Splines for Parametertyp p in direction dim + //std::cout << "nr of splines: " << nrOf1DSplines; + + // getting the numbers of Datapoints for all dimension eccept the dimension of interpolation + int currentNumbers[mXdimensions - 1]; + for (int i = 0; i < dimension; i++) { + currentNumbers[i] = numberOfDataPoints[i]; + } + for (int i = dimension; i < mXdimensions - 1; i++) { + currentNumbers[i] = numberOfDataPoints[i + 1]; + } + ///std::cout << " current numbers: "; + for (int i = 0; i < mXdimensions - 1; i++) { + //std::cout << currentNumbers[i] << ","; + } + //std::cout << std::endl; + + //// for all Splines in current dimension: + for (int s = 0; s < nrOf1DSplines; s++) { + int indices[mXdimensions - 1]; + arraytopoints(s, indices, currentNumbers, mXdimensions - 1); + int startpoint[mXdimensions]; //startpoint for the current 1DSpline + for (int i = 0; i < dimension; i++) { + startpoint[i] = indices[i]; + } + startpoint[dimension] = 0; + for (int i = dimension + 1; i < mXdimensions; i++) { + startpoint[i] = indices[i - 1]; + } + // NOW WE HAVE THE DATAPOINTINDICES OF THE CURRENT STARTPOINT IN startpoint-Array. + int startdatapoint = pointstoarray(startpoint, numberOfDataPoints, mXdimensions); + int distance = 1; // distance to the next dataPoint in the array for the current dimension + for (int i = 0; i < dimension; i++) { + distance *= numberOfDataPoints[i]; + } + distance *= mFdimensions; + + for (int i = 0; i < numberOfDataPoints[dimension]; i++) { //Fill the dataPointF1D-Array + for (int f = 0; f < mFdimensions; f++) { + dataPointF1D[dimension][i * mFdimensions + f] = allParameters[currentDataPointF][startdatapoint * mFdimensions + (i * distance + f)]; // uiuiui index kuddelmuddel???!! + } + } + mHelpers[dimension].approximateFunction(par[dimension].get(), dataPointF1D[dimension].get()); + for (int i = 0; i < numberOfKnots[dimension] * mFdimensions * 2; i++) { + parD[dimension][i] = par[dimension][i]; + } + // now we have all s and s' values in par[dimension] + + int redistributionindex[mXdimensions]; + for (int i = 0; i < mXdimensions; i++) { + redistributionindex[i] = startpoint[i]; + } + //redistributing the derivatives at dimension-Knots into array p + for (int i = 0; i < numberOfKnots[dimension]; i++) { //for all dimension-Knots + redistributionindex[dimension] = mHelpers[dimension].getKnotDataPoint(i); //find the indices + int finalposition = pointstoarray(redistributionindex, numberOfDataPoints, mXdimensions); + + for (int f = 0; f < mFdimensions; f++) { + allParameters[p][finalposition * mFdimensions + f] = par[dimension][2 * i * mFdimensions + mFdimensions + f]; + } + + bool isKnot = 1; + for (int j = 0; j < mXdimensions; j++) { //is dataPoint a knot? + if (!mHelpers[j].getDataPoint(redistributionindex[j]).isKnot) { + isKnot = 0; + break; + } //noch mal checken!! Das muss noch anders!! + } + + if (isKnot) { // for all knots + int knotindices[mXdimensions]; + + for (int j = 0; j < mXdimensions; j++) { // calculate Knotindices for all dimensions + knotindices[j] = redistributionindex[j] / ((numberOfDataPoints[j] - 1) / (numberOfKnots[j] - 1)); + //knotindices[j] = mHelpers[j].getDataPoint(redistributionindex[j]).iKnot; //in der Annahme der wert ist ein Knotenindex und falls der datapoint ein knoten ist, gibt er seinen eigenen knotenindex zurück + } + // get the knotindexvalue for FParameters: + int knotind = pointstoarray(knotindices, numberOfKnots, mXdimensions); + for (int f = 0; f < mFdimensions; f++) { + Fparameters[knotind * numberOfParameterTypes * mFdimensions + p * mFdimensions + f] = par[dimension][2 * i * mFdimensions + mFdimensions + f]; ///write derivatives in FParameters + } + } + } // end for all fknots (for redistribution) + + // recalculation: + for (int i = 0; i < numberOfDataPoints[dimension]; i++) { // this is somehow still redundant// TO DO: ONLY PART OF approximateFunction WHERE NDIM is considerd!! + redistributionindex[dimension] = i; // getting current datapointindices + bool isKnot = 1; // check is current datapoint a knot? + for (int j = 0; j < mXdimensions; j++) { + if (!mHelpers[j].getDataPoint(redistributionindex[j]).isKnot) { + isKnot = 0; + break; + } + } + double splineF[mFdimensions]; + double u = mHelpers[dimension].getDataPoint(i).u; + mHelpers[dimension].getSpline().interpolateU(mFdimensions, parD[dimension].get(), u, splineF); //recalculate at all datapoints of dimension + for (int dim = 0; dim < mFdimensions; dim++) { //writing it in allParameters + //std::cout<<allParameters [p-(int)(pow(2.0, dimension))] [(int)(startdatapoint*mFdimensions + i*distance + dim)]<<", "; + allParameters[p - (int)(pow(2.0, dimension))][(int)(startdatapoint * mFdimensions + i * distance + dim)] = splineF[dim]; //write it in the array. + //std::cout<<allParameters [p-(int)(pow(2.0, dimension))] [(int)(startdatapoint*mFdimensions + i*distance + dim)]<<", "; + } + + if (isKnot) { + int knotindices[mXdimensions]; + + for (int j = 0; j < mXdimensions; j++) { // calculate KNotindices for all dimensions + knotindices[j] = redistributionindex[j] / ((numberOfDataPoints[j] - 1) / (numberOfKnots[j] - 1)); + //knotindices[j] = mHelpers[j].getDataPoint(redistributionindex[j]).iKnot; //in der Annahme der wert ist ein Knotenindex und falls der datapoint ein knoten ist, gibt er seinen eigenen knotenindex zurück + } + int currentknotarrayindex = pointstoarray(knotindices, numberOfKnots, mXdimensions); + // getting the recalculated value into FParameters: + for (int f = 0; f < mFdimensions; f++) { + Fparameters[currentknotarrayindex * numberOfParameterTypes * mFdimensions + (p - (int)(pow(2.0, dimension))) * mFdimensions + f] = splineF[f]; + } + } // end if isKnot + } // end recalculation + } //end of all1DSplines + } //end of for parametertypes +} //end of approxymateFunction MYVERSION! + +template <typename DataT> +int SplineHelper<DataT>::test(const bool draw, const bool drawDataPoints) +{ + // Test method + + using namespace std; + + constexpr int nDimX = 2; + constexpr int nDimY = 2; + constexpr int Fdegree = 4; + + double xMin[nDimX]; + double xMax[nDimX]; + int nKnots[nDimX]; + int* knotsU[nDimX]; + int nAxiliaryDatapoints[nDimX]; + + for (int i = 0; i < nDimX; i++) { + xMin[i] = 0.; + xMax[i] = 1.; + nKnots[i] = 4; + knotsU[i] = new int[nKnots[i]]; + nAxiliaryDatapoints[i] = 4; + } + + // Function F + const int nTerms1D = 2 * (Fdegree + 1); + int nFcoeff = nDimY; + for (int i = 0; i < nDimX; i++) { + nFcoeff *= nTerms1D; + } + + double Fcoeff[nFcoeff]; + + auto F = [&](const double x[nDimX], double f[nDimY]) { + double a[nFcoeff]; + a[0] = 1; + int na = 1; + for (int d = 0; d < nDimX; d++) { + double b[nFcoeff]; + int nb = 0; + double t = (x[d] - xMin[d]) * TMath::Pi() / (xMax[d] - xMin[d]); + for (int i = 0; i < nTerms1D; i++) { + double c = (i % 2) ? cos((i / 2) * t) : cos((i / 2) * t); + for (int j = 0; j < na; j++) { + b[nb++] = c * a[j]; + assert(nb <= nFcoeff); + } + } + na = nb; + for (int i = 0; i < nb; i++) { + a[i] = b[i]; + } + } + + double* c = Fcoeff; + for (int dim = 0; dim < nDimY; dim++) { + f[dim] = 0; + for (int i = 0; i < na; i++) { + f[dim] += a[i] * (*c++); + } + } + }; + + auto F2D = [&](double x1, double x2, double f[nDimY]) { + double x[2] = {x1, x2}; + F(x, f); + }; + + for (int seed = 1; seed < 10; seed++) { + + gRandom->SetSeed(seed); + + // getting the coefficents filled randomly + for (int i = 0; i < nFcoeff; i++) { + Fcoeff[i] = gRandom->Uniform(-1, 1); + } + + for (int i = 0; i < nDimX; i++) { + knotsU[i][0] = 0; + for (int j = 1; j < nKnots[i]; j++) { + knotsU[i][j] = j * 4; //+ int(gRandom->Integer(3)) - 1; + } + } + + Spline<float, nDimX, nDimY> spline(nKnots, knotsU); + Spline2D<float, nDimY> spline2D(nKnots[0], knotsU[0], nKnots[1], knotsU[1]); + + spline.approximateFunction(xMin, xMax, F, nAxiliaryDatapoints); + spline2D.approximateFunction(xMin[0], xMax[0], xMin[1], xMax[1], + F2D, nAxiliaryDatapoints[0], nAxiliaryDatapoints[0]); + + long double statDf = 0; + long double statDf2D = 0; + + long double statN = 0; + + double x[nDimX]; + for (int i = 0; i < nDimX; i++) { + x[i] = xMin[i]; + } + do { + float xf[nDimX]; + float s[nDimY]; + float s2D[nDimY]; + double f[nDimY]; + for (int i = 0; i < nDimX; i++) { + xf[i] = x[i]; + } + F(x, f); + spline.interpolate(xf, s); + spline2D.interpolate(xf[0], xf[1], s2D); + + for (int dim = 0; dim < nDimY; dim++) { + statDf += (s[dim] - f[dim]) * (s[dim] - f[dim]); + statDf2D += (s2D[dim] - f[dim]) * (s2D[dim] - f[dim]); + statN++; + } + int dim = 0; + for (; dim < nDimX; dim++) { + x[dim] += 0.01; + if (x[dim] <= xMax[dim]) { + break; + } + x[dim] = xMin[dim]; + } + if (dim >= nDimX) { + break; + } + } while (1); + + cout << "\n std dev for SplineND : " << sqrt(statDf / statN) << std::endl; + cout << "\n std dev for Spline2D : " << sqrt(statDf2D / statN) << std::endl; + + } // seed + + for (int i = 0; i < nDimX; i++) { + delete[] knotsU[i]; + } + + return 0; +} + +template class GPUCA_NAMESPACE::gpu::SplineHelper<float>; +template class GPUCA_NAMESPACE::gpu::SplineHelper<double>; + +#endif diff --git a/GPU/TPCFastTransformation/SplineHelper.h b/GPU/TPCFastTransformation/SplineHelper.h new file mode 100644 index 0000000000000..6eb0ade996359 --- /dev/null +++ b/GPU/TPCFastTransformation/SplineHelper.h @@ -0,0 +1,164 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SplineHelper.h +/// \brief Definition of SplineHelper class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEHELPER_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEHELPER_H + +#include <cmath> +#include <vector> + +#include "GPUCommonDef.h" +#include "Rtypes.h" +#include "TString.h" +#include "Spline1D.h" +#include "Spline.h" +#include "Spline1DHelper.h" +#include <functional> + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +/// +/// The SplineHelper class is to initialize Spline* objects +/// +template <typename DataT> +class SplineHelper +{ + public: + /// _____________ Constructors / destructors __________________________ + + /// Default constructor + SplineHelper(); + + /// Copy constructor: disabled + SplineHelper(const SplineHelper&) CON_DELETE; + + /// Assignment operator: disabled + SplineHelper& operator=(const SplineHelper&) CON_DELETE; + + /// Destructor + ~SplineHelper() CON_DEFAULT; + + /// _______________ Main functionality ________________________ + + /// Create best-fit spline parameters for a given input function F + void approximateFunction(SplineContainer<DataT>& spline, + const double xMin[/* Xdim */], const double xMax[/* Xdim */], + std::function<void(const double x[/* Xdim */], double f[/* Fdim */])> F, + const int nAxiliaryDataPoints[/* Xdim */] = nullptr); + + /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ + + /// precompute everything needed for the construction + int setSpline(const SplineContainer<DataT>& spline, const int nAxiliaryPoints[/* Xdim */]); + + /// approximate std::function, output in Fparameters + void approximateFunction( + DataT* Fparameters, const double xMin[/* mXdimensions */], const double xMax[/* mXdimensions */], + std::function<void(const double x[/* mXdimensions */], double f[/* mFdimensions */])> F) const; + + /// approximate std::function, output in Fparameters. F calculates values for a batch of points. + void approximateFunctionBatch( + DataT* Fparameters, const double xMin[/* mXdimensions */], const double xMax[/* mXdimensions */], + std::function<void(const std::vector<double> x[/* mXdimensions */], double f[/*mFdimensions*/])> F, + unsigned int batchsize) const; + + /// approximate a function given as an array of values at data points + void approximateFunction( + DataT* Fparameters, const double DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; + + int getNumberOfDataPoints(int dimX) const { return mHelpers[dimX].getNumberOfDataPoints(); } + + int getNumberOfDataPoints() const { return mNumberOfDataPoints; } + + const Spline1DHelper<DataT>& getHelper(int dimX) const { return mHelpers[dimX]; } + + /// _______________ Utilities ________________________ + + /// Gives error string + const char* getLastError() const { return mError.Data(); } + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// Test the Spline class functionality + static int test(const bool draw = 0, const bool drawDataPoints = 1); +#endif + + static int arraytopoints(int point, int result[], const int numbers[], int dim); + + static int pointstoarray(const int indices[], const int numbers[], int dim); + + private: + /// Stores an error message + int storeError(Int_t code, const char* msg); + + TString mError = ""; ///< error string + int mXdimensions; ///< number of X dimensions + int mFdimensions; ///< number of F dimensions + int mNumberOfParameters; ///< number of parameters + int mNumberOfDataPoints; ///< number of data points + std::vector<Spline1DHelper<DataT>> mHelpers; +}; + +template <typename DataT> +void SplineHelper<DataT>::approximateFunction( + SplineContainer<DataT>& spline, + const double xMin[/* Xdim */], const double xMax[/* Xdim */], + std::function<void(const double x[/* Xdim */], double f[/* Fdim */])> F, + const int nAxiliaryDataPoints[/* Xdim */]) +{ + /// Create best-fit spline parameters for a given input function F + setSpline(spline, nAxiliaryDataPoints); + approximateFunction(spline.getParameters(), xMin, xMax, F); + DataT xxMin[spline.getXdimensions()]; + DataT xxMax[spline.getXdimensions()]; + for (int i = 0; i < spline.getXdimensions(); i++) { + xxMin[i] = xMin[i]; + xxMax[i] = xMax[i]; + } + spline.setXrange(xxMin, xxMax); +} + +template <typename DataT> +int SplineHelper<DataT>::setSpline( + const SplineContainer<DataT>& spline, const int nAxiliaryPoints[/* Xdim */]) +{ + // Prepare creation of an irregular spline + // The should be at least one (better, two) axiliary measurements on each segnment between two knots and at least 2*nKnots measurements in total + // Returns 0 when the spline can not be constructed with the given nAxiliaryPoints + + int ret = 0; + mXdimensions = spline.getXdimensions(); + mFdimensions = spline.getYdimensions(); + mNumberOfParameters = spline.getNumberOfParameters(); + mNumberOfDataPoints = 1; + mHelpers.clear(); + mHelpers.resize(mXdimensions); + for (int i = 0; i < mXdimensions; i++) { + int np = (nAxiliaryPoints != nullptr) ? nAxiliaryPoints[i] : 4; + if (mHelpers[i].setSpline(spline.getGrid(i), mFdimensions, np) != 0) { + ret = storeError(-2, "SplineHelper::setSpline: error by setting an axis"); + } + mNumberOfDataPoints *= mHelpers[i].getNumberOfDataPoints(); + } + + return ret; +} + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/SplineHelper1D.cxx b/GPU/TPCFastTransformation/SplineHelper1D.cxx deleted file mode 100644 index 1d6e0c513d830..0000000000000 --- a/GPU/TPCFastTransformation/SplineHelper1D.cxx +++ /dev/null @@ -1,473 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SplineHelper1D.cxx -/// \brief Implementation of SplineHelper1D class -/// -/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - -#include "SplineHelper1D.h" -#include "TMath.h" -#include "TMatrixD.h" -#include "TVectorD.h" -#include "TDecompBK.h" - -using namespace GPUCA_NAMESPACE::gpu; - -template <typename DataT> -SplineHelper1D<DataT>::SplineHelper1D() : mError(), mSpline(), mFdimensions(0) -{ -} - -template <typename DataT> -int SplineHelper1D<DataT>::storeError(int code, const char* msg) -{ - mError = msg; - return code; -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunctionClassic(Spline1D<DataT>& spline, - DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*spline.getFdimensions()*/])> F) -{ - /// Create classic spline parameters for a given input function F - /// set slopes at the knots such, that the second derivative of the spline is continious. - - int Ndim = spline.getFdimensions(); - const int nKnots = spline.getNumberOfKnots(); - - TMatrixD A(nKnots, nKnots); - - A.Zero(); - - /* - const Spline1D::Knot& knot0 = spline.getKnot(i); - double x = (u - knot0.u) * knot0.Li; // scaled u - double cS1 = (6 - 12*x)*knot0.Li*knot0.Li; - double cZ0 = (6*x-4)*knot0.Li; - double cZ1 = (6*x-2)*knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - */ - - // second derivative at knot0 is 0 - { - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(0); - double cZ0 = (-4) * knot0.Li; - double cZ1 = (-2) * knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - A(0, 0) = cZ0; - A(0, 1) = cZ1; - } - - // second derivative at knot nKnots-1 is 0 - { - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(nKnots - 2); - double cZ0 = (6 - 4) * knot0.Li; - double cZ1 = (6 - 2) * knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - A(nKnots - 1, nKnots - 2) = cZ0; - A(nKnots - 1, nKnots - 1) = cZ1; - } - - // second derivative at other knots is same from the left and from the right - for (int i = 1; i < nKnots - 1; i++) { - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(i - 1); - double cZ0 = (6 - 4) * knot0.Li; - double cZ1_0 = (6 - 2) * knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - - const typename Spline1D<DataT>::Knot& knot1 = spline.getKnot(i); - double cZ1_1 = (-4) * knot1.Li; - double cZ2 = (-2) * knot1.Li; - // f''(u) = cS2*(f2-f1) + cZ1_1*z1 + cZ2*z2; - A(i, i - 1) = cZ0; - A(i, i) = cZ1_0 - cZ1_1; - A(i, i + 1) = -cZ2; - } - - A.Invert(); - - spline.setXrange(xMin, xMax); - DataT* parameters = spline.getFparameters(); - - TVectorD b(nKnots); - b.Zero(); - - double uToXscale = (((double)xMax) - xMin) / spline.getUmax(); - - for (int i = 0; i < nKnots; ++i) { - const typename Spline1D<DataT>::Knot& knot = spline.getKnot(i); - double u = knot.u; - DataT f[Ndim]; - F(xMin + u * uToXscale, f); - for (int dim = 0; dim < Ndim; dim++) { - parameters[(2 * i) * Ndim + dim] = f[dim]; - } - } - - for (int dim = 0; dim < Ndim; dim++) { - - // second derivative at knot0 is 0 - { - double f0 = parameters[(2 * 0) * Ndim + dim]; - double f1 = parameters[(2 * 1) * Ndim + dim]; - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(0); - double cS1 = (6) * knot0.Li * knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - b(0) = -cS1 * (f1 - f0); - } - - // second derivative at knot nKnots-1 is 0 - { - double f0 = parameters[2 * (nKnots - 2) * Ndim + dim]; - double f1 = parameters[2 * (nKnots - 1) * Ndim + dim]; - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(nKnots - 2); - double cS1 = (6 - 12) * knot0.Li * knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - b(nKnots - 1) = -cS1 * (f1 - f0); - } - - // second derivative at other knots is same from the left and from the right - for (int i = 1; i < nKnots - 1; i++) { - double f0 = parameters[2 * (i - 1) * Ndim + dim]; - double f1 = parameters[2 * (i)*Ndim + dim]; - double f2 = parameters[2 * (i + 1) * Ndim + dim]; - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(i - 1); - double cS1 = (6 - 12) * knot0.Li * knot0.Li; - // f''(u) = cS1*(f1-f0) + cZ0*z0 + cZ1*z1; - - const typename Spline1D<DataT>::Knot& knot1 = spline.getKnot(i); - double cS2 = (6) * knot1.Li * knot1.Li; - // f''(u) = cS2*(f2-f1) + cZ1_1*z1 + cZ2*z2; - b(i) = -cS1 * (f1 - f0) + cS2 * (f2 - f1); - } - - TVectorD c = A * b; - for (int i = 0; i < nKnots; i++) { - parameters[(2 * i + 1) * Ndim + dim] = c[i]; - } - } -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunction( - Spline1D<DataT>& spline, DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*spline.getFdimensions()*/])> F, - int nAxiliaryDataPoints) -{ - /// Create best-fit spline parameters for a given input function F - setSpline(spline, spline.getFdimensions(), nAxiliaryDataPoints); - approximateFunction(spline.getFparameters(), xMin, xMax, F); - spline.setXrange(xMin, xMax); -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunctionGradually( - Spline1D<DataT>& spline, DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*spline.getFdimensions()*/])> F, - int nAxiliaryDataPoints) -{ - /// Create best-fit spline parameters gradually for a given input function F - setSpline(spline, spline.getFdimensions(), nAxiliaryDataPoints); - approximateFunctionGradually(spline.getFparameters(), xMin, xMax, F); - spline.setXrange(xMin, xMax); -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunction( - DataT* Fparameters, DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[])> F) const -{ - /// Create best-fit spline parameters for a given input function F - /// output in Fparameters - std::vector<DataT> vF(getNumberOfDataPoints() * mFdimensions); - double mUtoXscale = (((double)xMax) - xMin) / mSpline.getUmax(); - for (int i = 0; i < getNumberOfDataPoints(); i++) { - F(xMin + mUtoXscale * mDataPoints[i].u, &vF[i * mFdimensions]); - } - approximateFunction(Fparameters, vF.data()); -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunctionGradually( - DataT* Fparameters, DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[])> F) const -{ - /// Create best-fit spline parameters gradually for a given input function F - /// output in Fparameters - std::vector<DataT> vF(getNumberOfDataPoints() * mFdimensions); - double mUtoXscale = (((double)xMax) - xMin) / mSpline.getUmax(); - for (int i = 0; i < getNumberOfDataPoints(); i++) { - F(xMin + mUtoXscale * mDataPoints[i].u, &vF[i * mFdimensions]); - } - approximateFunctionGradually(Fparameters, vF.data()); -} - -template <typename DataT> -int SplineHelper1D<DataT>::setSpline( - const Spline1D<DataT>& spline, int nFdimensions, int nAxiliaryDataPoints) -{ - // Prepare creation of a best-fit spline - // - // Data points will be set at all integer U (that includes all knots), - // plus at nAxiliaryDataPoints points between the integers. - // - // nAxiliaryDataPoints must be >= 2 - // - // nAxiliaryDataPoints==1 is also possible, but there must be at least - // one integer U without a knot, in order to get 2*nKnots data points in total. - // - // The return value is an error index, 0 means no error - - int ret = 0; - - mSpline.cloneFromObject(spline, nullptr); - mFdimensions = nFdimensions; - int nPoints = 0; - if (!spline.isConstructed()) { - ret = storeError(-1, "SplineHelper1D<DataT>::setSpline: input spline is not constructed"); - mSpline.recreate(2, 0); - nAxiliaryDataPoints = 2; - nPoints = 4; - } else { - mSpline.cloneFromObject(spline, nullptr); - nPoints = 1 + spline.getUmax() + spline.getUmax() * nAxiliaryDataPoints; - if (nPoints < 2 * spline.getNumberOfKnots()) { - nAxiliaryDataPoints = 2; - nPoints = 1 + spline.getUmax() + spline.getUmax() * nAxiliaryDataPoints; - ret = storeError(-3, "SplineHelper1D::setSpline: too few nAxiliaryDataPoints, increase to 2"); - } - } - - const int nPar = mSpline.getNumberOfParameters(1); // n parameters for 1D - - mDataPoints.resize(nPoints); - double scalePoints2Knots = ((double)spline.getUmax()) / (nPoints - 1.); - - for (int i = 0; i < nPoints; ++i) { - DataPoint& p = mDataPoints[i]; - double u = i * scalePoints2Knots; - int iKnot = spline.getKnotIndexU(u); - const typename Spline1D<DataT>::Knot& knot0 = spline.getKnot(iKnot); - const typename Spline1D<DataT>::Knot& knot1 = spline.getKnot(iKnot + 1); - double l = knot1.u - knot0.u; - double s = (u - knot0.u) * knot0.Li; // scaled u - double s2 = s * s; - double sm1 = s - 1.; - - p.iKnot = iKnot; - p.isKnot = 0; - p.u = u; - p.cS1 = s2 * (3. - 2. * s); - p.cS0 = 1. - p.cS1; - p.cZ0 = s * sm1 * sm1 * l; - p.cZ1 = s2 * sm1 * l; - } - - const int nKnots = mSpline.getNumberOfKnots(); - - mKnotDataPoints.resize(nKnots); - - for (int i = 0; i < nKnots; ++i) { - const typename Spline1D<DataT>::Knot& knot = spline.getKnot(i); - int iu = (int)(knot.u + 0.1f); - mKnotDataPoints[i] = iu * (1 + nAxiliaryDataPoints); - mDataPoints[mKnotDataPoints[i]].isKnot = 1; - } - - TMatrixDSym A(nPar); - A.Zero(); - - for (int i = 0; i < nPoints; ++i) { - DataPoint& p = mDataPoints[i]; - int j = p.iKnot * 2; - A(j + 0, j + 0) += p.cS0 * p.cS0; - A(j + 1, j + 0) += p.cS0 * p.cZ0; - A(j + 2, j + 0) += p.cS0 * p.cS1; - A(j + 3, j + 0) += p.cS0 * p.cZ1; - - A(j + 1, j + 1) += p.cZ0 * p.cZ0; - A(j + 2, j + 1) += p.cZ0 * p.cS1; - A(j + 3, j + 1) += p.cZ0 * p.cZ1; - - A(j + 2, j + 2) += p.cS1 * p.cS1; - A(j + 3, j + 2) += p.cS1 * p.cZ1; - - A(j + 3, j + 3) += p.cZ1 * p.cZ1; - } - - // copy symmetric matrix elements - - for (int i = 0; i < nPar; i++) { - for (int j = i + 1; j < nPar; j++) { - A(i, j) = A(j, i); - } - } - - TMatrixDSym Z(nKnots); - mLSMmatrixSvalues.resize(nKnots * nKnots); - for (int i = 0, k = 0; i < nKnots; i++) { - for (int j = 0; j < nKnots; j++, k++) { - mLSMmatrixSvalues[k] = A(i * 2 + 1, j * 2); - Z(i, j) = A(i * 2 + 1, j * 2 + 1); - } - } - - { - TDecompBK bk(A, 0); - bool ok = bk.Invert(A); - - if (!ok) { - ret = storeError(-4, "SplineHelper1D::setSpline: internal error - can not invert the matrix"); - A.Zero(); - } - mLSMmatrixFull.resize(nPar * nPar); - for (int i = 0, k = 0; i < nPar; i++) { - for (int j = 0; j < nPar; j++, k++) { - mLSMmatrixFull[k] = A(i, j); - } - } - } - - { - TDecompBK bk(Z, 0); - if (!bk.Invert(Z)) { - ret = storeError(-5, "SplineHelper1D::setSpline: internal error - can not invert the matrix"); - Z.Zero(); - } - mLSMmatrixSderivatives.resize(nKnots * nKnots); - for (int i = 0, k = 0; i < nKnots; i++) { - for (int j = 0; j < nKnots; j++, k++) { - mLSMmatrixSderivatives[k] = Z(i, j); - } - } - } - - return ret; -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunction( - DataT* Fparameters, - const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const -{ - /// Approximate a function given as an array of values at data points - - const int nPar = mSpline.getNumberOfParameters(1); - double b[nPar]; - for (int idim = 0; idim < mFdimensions; idim++) { - for (int i = 0; i < nPar; i++) - b[i] = 0.; - - for (int i = 0; i < getNumberOfDataPoints(); ++i) { - const DataPoint& p = mDataPoints[i]; - double* bb = &(b[p.iKnot * 2]); - double f = (double)DataPointF[i * mFdimensions + idim]; - bb[0] += f * p.cS0; - bb[1] += f * p.cZ0; - bb[2] += f * p.cS1; - bb[3] += f * p.cZ1; - } - - const double* row = mLSMmatrixFull.data(); - - for (int i = 0; i < nPar; i++, row += nPar) { - double s = 0.; - for (int j = 0; j < nPar; j++) { - s += row[j] * b[j]; - } - Fparameters[i * mFdimensions + idim] = (float)s; - } - } -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateFunctionGradually( - DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim */]) const -{ - /// gradually approximate a function given as an array of values at data points - /// output in Fparameters - copySfromDataPoints(Fparameters, DataPointF); - approximateDerivatives(Fparameters, DataPointF); -} - -template <typename DataT> -void SplineHelper1D<DataT>::copySfromDataPoints( - DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const -{ - /// a tool for the gradual approximation: set spline values S_i at knots == function values - /// output in Fparameters - for (int i = 0; i < mSpline.getNumberOfKnots(); ++i) { // set F values at knots - int ip = mKnotDataPoints[i]; - for (int d = 0; d < mFdimensions; d++) { - Fparameters[2 * i * mFdimensions + d] = DataPointF[ip * mFdimensions + d]; - } - } -} - -template <typename DataT> -void SplineHelper1D<DataT>::approximateDerivatives( - DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const -{ - /// a tool for the gradual approximation: - /// calibrate spline derivatives D_i using already calibrated spline values S_i - /// input and output output in Fparameters - - const int nKnots = mSpline.getNumberOfKnots(); - const int Ndim = mFdimensions; - double b[nKnots * mFdimensions]; - for (int i = 0; i < nKnots * Ndim; i++) { - b[i] = 0.; - } - - for (int i = 0; i < getNumberOfDataPoints(); ++i) { - const DataPoint& p = mDataPoints[i]; - if (p.isKnot) { - continue; - } - for (int d = 0; d < Ndim; d++) { - double f = (double)DataPointF[i * Ndim + d]; - b[(p.iKnot + 0) * Ndim + d] += f * p.cZ0; - b[(p.iKnot + 1) * Ndim + d] += f * p.cZ1; - } - } - - const double* row = mLSMmatrixSvalues.data(); - for (int i = 0; i < nKnots; ++i, row += nKnots) { - double s[Ndim]; - for (int d = 0; d < Ndim; d++) - s[d] = 0.; - for (int j = 0; j < nKnots; ++j) { - for (int d = 0; d < Ndim; d++) - s[d] += row[j] * Fparameters[2 * j * Ndim + d]; - } - for (int d = 0; d < Ndim; d++) - b[i * Ndim + d] -= s[d]; - } - - row = mLSMmatrixSderivatives.data(); - for (int i = 0; i < nKnots; ++i, row += nKnots) { - double s[Ndim]; - for (int d = 0; d < Ndim; d++) { - s[d] = 0.; - } - for (int j = 0; j < nKnots; ++j) { - for (int d = 0; d < Ndim; d++) { - s[d] += row[j] * b[j * Ndim + d]; - } - } - for (int d = 0; d < Ndim; d++) { - Fparameters[(2 * i + 1) * Ndim + d] = (float)s[d]; - } - } -} - -template class GPUCA_NAMESPACE::gpu::SplineHelper1D<float>; -template class GPUCA_NAMESPACE::gpu::SplineHelper1D<double>; - -#endif diff --git a/GPU/TPCFastTransformation/SplineHelper1D.h b/GPU/TPCFastTransformation/SplineHelper1D.h deleted file mode 100644 index e6ed084fdc5a1..0000000000000 --- a/GPU/TPCFastTransformation/SplineHelper1D.h +++ /dev/null @@ -1,140 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SplineHelper1D.h -/// \brief Definition of SplineHelper1D class - -/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> - -#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEHELPER1D_H -#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEHELPER1D_H - -#include <cmath> -#include <vector> - -#include "GPUCommonDef.h" -#include "GPUCommonRtypes.h" -#include "Spline1D.h" -#include <functional> -#include <string> - -namespace GPUCA_NAMESPACE -{ -namespace gpu -{ -/// -/// The SplineHelper1D class is to initialize parameters for Spline1D class -/// -template <typename DataT> -class SplineHelper1D -{ - public: - /// - /// \brief Helper structure for 1D spline construction - /// - struct DataPoint { - double u; ///< u coordinate - double cS0; ///< a coefficient for s0 - double cZ0; ///< a coefficient for s'0 - double cS1; ///< a coefficient for s1 - double cZ1; ///< a coefficient for s'1 - int iKnot; ///< index of the left knot of the segment - bool isKnot; ///< is the point placed at a knot - }; - - /// _____________ Constructors / destructors __________________________ - - /// Default constructor - SplineHelper1D(); - - /// Copy constructor: disabled - SplineHelper1D(const SplineHelper1D&) CON_DEFAULT; - - /// Assignment operator: disabled - SplineHelper1D& operator=(const SplineHelper1D&) CON_DEFAULT; - - /// Destructor - ~SplineHelper1D() CON_DEFAULT; - - /// _______________ Main functionality ________________________ - - /// Create best-fit spline parameters for a given input function F - void approximateFunction(Spline1D<DataT>& spline, - DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*spline.getFdimensions()*/])> F, - int nAxiliaryDataPoints = 4); - - /// Create best-fit spline parameters gradually for a given input function F - void approximateFunctionGradually(Spline1D<DataT>& spline, - DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*spline.getFdimensions()*/])> F, - int nAxiliaryDataPoints = 4); - - /// Create classic spline parameters for a given input function F - void approximateFunctionClassic(Spline1D<DataT>& spline, - DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[/*spline.getFdimensions()*/])> F); - - /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ - - /// precompute everything needed for the construction - int setSpline(const Spline1D<DataT>& spline, int nFdimensions, int nAxiliaryDataPoints); - - /// approximate std::function, output in Fparameters - void approximateFunction(DataT* Fparameters, DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[])> F) const; - - /// approximate std::function gradually, output in Fparameters - void approximateFunctionGradually(DataT* Fparameters, DataT xMin, DataT xMax, std::function<void(DataT x, DataT f[])> F) const; - - /// number of data points - int getNumberOfDataPoints() const { return mDataPoints.size(); } - - /// approximate a function given as an array of values at data points - void approximateFunction(DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; - - /// gradually approximate a function given as an array of values at data points - void approximateFunctionGradually(DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim */]) const; - - /// a tool for the gradual approximation: set spline values S_i at knots == function values - void copySfromDataPoints(DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; - - /// a tool for the gradual approximation: - /// calibrate spline derivatives D_i using already calibrated spline values S_i - void approximateDerivatives(DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; - - /// _______________ Utilities ________________________ - - const Spline1D<DataT>& getSpline() const { return mSpline; } - - int getKnotDataPoint(int iknot) const { return mKnotDataPoints[iknot]; } - - const DataPoint& getDataPoint(int ip) const { return mDataPoints[ip]; } - - /// Gives error string - const char* getLastError() const { return mError.c_str(); } - - private: - /// Stores an error message - int storeError(int code, const char* msg); - - std::string mError = ""; ///< error string - - /// helpers for the construction of 1D spline - - Spline1D<DataT> mSpline; ///< copy of the spline - int mFdimensions; ///< n of F dimensions - std::vector<DataPoint> mDataPoints; ///< measurement points - std::vector<int> mKnotDataPoints; ///< which measurement points are at knots - std::vector<double> mLSMmatrixFull; ///< a matrix to convert the measurements into the spline parameters with the LSM method - std::vector<double> mLSMmatrixSderivatives; - std::vector<double> mLSMmatrixSvalues; -}; - -} // namespace gpu -} // namespace GPUCA_NAMESPACE - -#endif diff --git a/GPU/TPCFastTransformation/SplineHelper2D.cxx b/GPU/TPCFastTransformation/SplineHelper2D.cxx deleted file mode 100644 index 51aee34911eaa..0000000000000 --- a/GPU/TPCFastTransformation/SplineHelper2D.cxx +++ /dev/null @@ -1,213 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Spline182.cxx -/// \brief Implementation of SplineHelper2D class -/// -/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) - -#include "SplineHelper2D.h" -#include "TMath.h" -#include "TMatrixD.h" -#include "TVectorD.h" -#include "TDecompBK.h" - -using namespace GPUCA_NAMESPACE::gpu; - -template <typename DataT> -SplineHelper2D<DataT>::SplineHelper2D() : mError(), mFdimensions(0), mHelperU1(), mHelperU2() -{ -} - -template <typename DataT> -int SplineHelper2D<DataT>::storeError(int code, const char* msg) -{ - mError = msg; - return code; -} - -template <typename DataT> -void SplineHelper2D<DataT>::approximateFunction( - DataT* Fparameters, DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(DataT x1, DataT x2, DataT f[/*spline.getFdimensions()*/])> F) const -{ - /// Create best-fit spline parameters for a given input function F - /// output in Fparameters - - std::vector<DataT> dataPointF(getNumberOfDataPoints() * mFdimensions); - - double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); - double scaleX2 = (x2Max - x2Min) / ((double)mHelperU2.getSpline().getUmax()); - - for (int iv = 0; iv < getNumberOfDataPointsU2(); iv++) { - DataT x2 = x2Min + mHelperU2.getDataPoint(iv).u * scaleX2; - for (int iu = 0; iu < getNumberOfDataPointsU1(); iu++) { - DataT x1 = x1Min + mHelperU1.getDataPoint(iu).u * scaleX1; - F(x1, x2, &dataPointF[(iv * getNumberOfDataPointsU1() + iu) * mFdimensions]); - } - } - approximateFunction(Fparameters, dataPointF.data()); -} - -template <typename DataT> -void SplineHelper2D<DataT>::approximateFunctionBatch( - DataT* Fparameters, DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(const std::vector<DataT>& x1, const std::vector<DataT>& x2, std::vector<DataT> f[/*mFdimensions*/])> F, - unsigned int batchsize) const -{ - /// Create best-fit spline parameters for a given input function F. - /// F calculates values for a batch of points. - /// output in Fparameters - - std::vector<DataT> dataPointF(getNumberOfDataPoints() * mFdimensions); - - double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); - double scaleX2 = (x2Max - x2Min) / ((double)mHelperU2.getSpline().getUmax()); - - std::vector<DataT> x1; - x1.reserve(batchsize); - - std::vector<DataT> x2; - x2.reserve(batchsize); - - std::vector<int> index; - index.reserve(batchsize); - - std::vector<DataT> dataPointFTmp[mFdimensions]; - for (unsigned int iDim = 0; iDim < mFdimensions; ++iDim) { - dataPointFTmp[iDim].reserve(batchsize); - } - - unsigned int counter = 0; - for (int iv = 0; iv < getNumberOfDataPointsU2(); iv++) { - DataT x2Tmp = x2Min + mHelperU2.getDataPoint(iv).u * scaleX2; - for (int iu = 0; iu < getNumberOfDataPointsU1(); iu++) { - DataT x1Tmp = x1Min + mHelperU1.getDataPoint(iu).u * scaleX1; - x1.emplace_back(x1Tmp); - x2.emplace_back(x2Tmp); - index.emplace_back((iv * getNumberOfDataPointsU1() + iu) * mFdimensions); - ++counter; - - if (counter == batchsize || (iu == (getNumberOfDataPointsU1() - 1) && (iv == (getNumberOfDataPointsU2() - 1)))) { - counter = 0; - F(x1, x2, dataPointFTmp); - unsigned int entries = index.size(); - - for (unsigned int i = 0; i < entries; ++i) { - const unsigned int indexTmp = index[i]; - for (unsigned int iDim = 0; iDim < mFdimensions; ++iDim) { - dataPointF[indexTmp + iDim] = dataPointFTmp[iDim][i]; - } - } - - x1.clear(); - x2.clear(); - index.clear(); - for (unsigned int iDim = 0; iDim < mFdimensions; ++iDim) { - dataPointFTmp[iDim].clear(); - } - } - } - } - approximateFunction(Fparameters, dataPointF.data()); -} - -template <typename DataT> -void SplineHelper2D<DataT>::approximateFunction( - DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const -{ - /// approximate a function given as an array of values at data points - - const int Ndim = mFdimensions; - const int Ndim2 = 2 * Ndim; - const int Ndim3 = 3 * Ndim; - const int Ndim4 = 4 * Ndim; - - int nDataPointsU = getNumberOfDataPointsU1(); - int nDataPointsV = getNumberOfDataPointsU2(); - - int nKnotsU = mHelperU1.getSpline().getNumberOfKnots(); - int nKnotsV = mHelperU2.getSpline().getNumberOfKnots(); - - std::unique_ptr<DataT[]> rotDataPointF(new DataT[nDataPointsU * nDataPointsV * Ndim]); // U DataPoints x V DataPoints : rotated DataPointF for one output dimension - std::unique_ptr<DataT[]> Dv(new DataT[nKnotsV * nDataPointsU * Ndim]); // V knots x U DataPoints - - std::unique_ptr<DataT[]> parU(new DataT[mHelperU1.getSpline().getNumberOfParameters(Ndim)]); - std::unique_ptr<DataT[]> parV(new DataT[mHelperU2.getSpline().getNumberOfParameters(Ndim)]); - - // rotated data points (u,v)->(v,u) - - for (int ipu = 0; ipu < nDataPointsU; ipu++) { - for (int ipv = 0; ipv < nDataPointsV; ipv++) { - for (int dim = 0; dim < Ndim; dim++) { - rotDataPointF[Ndim * (ipu * nDataPointsV + ipv) + dim] = DataPointF[Ndim * (ipv * nDataPointsU + ipu) + dim]; - } - } - } - - // get S and S'u at all the knots by interpolating along the U axis - - for (int iKnotV = 0; iKnotV < nKnotsV; ++iKnotV) { - int ipv = mHelperU2.getKnotDataPoint(iKnotV); - const DataT* DataPointFrow = &(DataPointF[Ndim * ipv * nDataPointsU]); - mHelperU1.approximateFunctionGradually(parU.get(), DataPointFrow); - - for (int iKnotU = 0; iKnotU < nKnotsU; ++iKnotU) { - DataT* knotPar = &Fparameters[Ndim4 * (iKnotV * nKnotsU + iKnotU)]; - for (int dim = 0; dim < Ndim; ++dim) { - knotPar[dim] = parU[Ndim * (2 * iKnotU) + dim]; // store S for all the knots - knotPar[Ndim2 + dim] = parU[Ndim * (2 * iKnotU) + Ndim + dim]; // store S'u for all the knots //SG!!! - } - } - - // recalculate F values for all ipu DataPoints at V = ipv - for (int ipu = 0; ipu < nDataPointsU; ipu++) { - DataT splineF[Ndim]; - DataT u = mHelperU1.getDataPoint(ipu).u; - mHelperU1.getSpline().interpolateU(Ndim, parU.get(), u, splineF); - for (int dim = 0; dim < Ndim; dim++) { - rotDataPointF[(ipu * nDataPointsV + ipv) * Ndim + dim] = splineF[dim]; - } - } - } - - // calculate S'v at all data points with V == V of a knot - - for (int ipu = 0; ipu < nDataPointsU; ipu++) { - const DataT* DataPointFcol = &(rotDataPointF[ipu * nDataPointsV * Ndim]); - mHelperU2.approximateFunctionGradually(parV.get(), DataPointFcol); - for (int iKnotV = 0; iKnotV < nKnotsV; iKnotV++) { - for (int dim = 0; dim < Ndim; dim++) { - DataT dv = parV[(iKnotV * 2 + 1) * Ndim + dim]; - Dv[(iKnotV * nDataPointsU + ipu) * Ndim + dim] = dv; - } - } - } - - // fit S'v and S''_vu at all the knots - - for (int iKnotV = 0; iKnotV < nKnotsV; ++iKnotV) { - const DataT* Dvrow = &(Dv[iKnotV * nDataPointsU * Ndim]); - mHelperU1.approximateFunction(parU.get(), Dvrow); - for (int iKnotU = 0; iKnotU < nKnotsU; ++iKnotU) { - for (int dim = 0; dim < Ndim; ++dim) { - Fparameters[Ndim4 * (iKnotV * nKnotsU + iKnotU) + Ndim + dim] = parU[Ndim * 2 * iKnotU + dim]; // store S'v for all the knots - Fparameters[Ndim4 * (iKnotV * nKnotsU + iKnotU) + Ndim3 + dim] = parU[Ndim * 2 * iKnotU + Ndim + dim]; // store S''vu for all the knots - } - } - } -} - -template class GPUCA_NAMESPACE::gpu::SplineHelper2D<float>; -template class GPUCA_NAMESPACE::gpu::SplineHelper2D<double>; - -#endif diff --git a/GPU/TPCFastTransformation/SplineHelper2D.h b/GPU/TPCFastTransformation/SplineHelper2D.h deleted file mode 100644 index 23fe726251db1..0000000000000 --- a/GPU/TPCFastTransformation/SplineHelper2D.h +++ /dev/null @@ -1,150 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SplineHelper2D.h -/// \brief Definition of SplineHelper2D class -/// -/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> - -#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEHELPER2D_H -#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEHELPER2D_H - -#include <cmath> -#include <vector> - -#include "GPUCommonDef.h" -#include "GPUCommonRtypes.h" -#include "Spline1D.h" -#include "Spline2D.h" -#include "SplineHelper1D.h" -#include <functional> -#include <string> - -namespace GPUCA_NAMESPACE -{ -namespace gpu -{ - -/// -/// The SplineHelper2D class is to initialize Spline* objects -/// -template <typename DataT> -class SplineHelper2D -{ - public: - /// _____________ Constructors / destructors __________________________ - - /// Default constructor - SplineHelper2D(); - - /// Copy constructor: disabled - SplineHelper2D(const SplineHelper2D&) CON_DELETE; - - /// Assignment operator: disabled - SplineHelper2D& operator=(const SplineHelper2D&) CON_DELETE; - - /// Destructor - ~SplineHelper2D() CON_DEFAULT; - - /// _______________ Main functionality ________________________ - - /// Create best-fit spline parameters for a given input function F - template <bool isConsistentT> - void approximateFunction( - Spline2DBase<DataT, isConsistentT>& spline, - DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(DataT x1, DataT x2, DataT f[/*spline.getFdimensions()*/])> F, - int nAxiliaryDataPointsU1 = 4, int nAxiliaryDataPointsU2 = 4); - - /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ - - /// precompute everything needed for the construction - template <bool isConsistentT> - int setSpline(const Spline2DBase<DataT, isConsistentT>& spline, int nAxiliaryPointsU1, int nAxiliaryPointsU2); - - /// approximate std::function, output in Fparameters - void approximateFunction( - DataT* Fparameters, DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(DataT x1, DataT x2, DataT f[/*mFdimensions*/])> F) const; - - /// approximate std::function, output in Fparameters. F calculates values for a batch of points. - void approximateFunctionBatch( - DataT* Fparameters, DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(const std::vector<DataT>& x1, const std::vector<DataT>& x2, std::vector<DataT> f[/*mFdimensions*/])> F, - unsigned int batchsize) const; - - /// approximate a function given as an array of values at data points - void approximateFunction( - DataT* Fparameters, const DataT DataPointF[/*getNumberOfDataPoints() x nFdim*/]) const; - - int getNumberOfDataPointsU1() const { return mHelperU1.getNumberOfDataPoints(); } - - int getNumberOfDataPointsU2() const { return mHelperU2.getNumberOfDataPoints(); } - - int getNumberOfDataPoints() const { return getNumberOfDataPointsU1() * getNumberOfDataPointsU2(); } - - const SplineHelper1D<DataT>& getHelperU1() const { return mHelperU1; } - const SplineHelper1D<DataT>& getHelperU2() const { return mHelperU2; } - - /// _______________ Utilities ________________________ - - /// Gives error string - const char* getLastError() const { return mError.c_str(); } - - private: - /// Stores an error message - int storeError(int code, const char* msg); - - std::string mError = ""; ///< error string - int mFdimensions; ///< n of F dimensions - SplineHelper1D<DataT> mHelperU1; - SplineHelper1D<DataT> mHelperU2; -}; - -template <typename DataT> -template <bool isConsistentT> -void SplineHelper2D<DataT>::approximateFunction( - Spline2DBase<DataT, isConsistentT>& spline, - DataT x1Min, DataT x1Max, DataT x2Min, DataT x2Max, - std::function<void(DataT x1, DataT x2, DataT f[/*spline.getFdimensions()*/])> F, - int nAxiliaryDataPointsU1, int nAxiliaryDataPointsU2) -{ - /// Create best-fit spline parameters for a given input function F - if (spline.isConsistent()) { - setSpline(spline, nAxiliaryDataPointsU1, nAxiliaryDataPointsU2); - approximateFunction(spline.getFparameters(), x1Min, x1Max, x2Min, x2Max, F); - } - spline.setXrange(x1Min, x1Max, x2Min, x2Max); -} - -template <typename DataT> -template <bool isConsistentT> -int SplineHelper2D<DataT>::setSpline( - const Spline2DBase<DataT, isConsistentT>& spline, int nAxiliaryPointsU, int nAxiliaryPointsV) -{ - // Prepare creation of 2D irregular spline - // The should be at least one (better, two) axiliary measurements on each segnment between two knots and at least 2*nKnots measurements in total - // Returns 0 when the spline can not be constructed with the given nAxiliaryPoints - - int ret = 0; - mFdimensions = spline.getFdimensions(); - if (mHelperU1.setSpline(spline.getGridU1(), mFdimensions, nAxiliaryPointsU) != 0) { - ret = storeError(-2, "SplineHelper2D::setSpline2D: error by setting U axis"); - } - if (mHelperU2.setSpline(spline.getGridU2(), mFdimensions, nAxiliaryPointsV) != 0) { - ret = storeError(-3, "SplineHelper2D::setSpline2D: error by setting V axis"); - } - return ret; -} - -} // namespace gpu -} // namespace GPUCA_NAMESPACE - -#endif diff --git a/GPU/TPCFastTransformation/SplineSpec.cxx b/GPU/TPCFastTransformation/SplineSpec.cxx new file mode 100644 index 0000000000000..35beb0d3c8417 --- /dev/null +++ b/GPU/TPCFastTransformation/SplineSpec.cxx @@ -0,0 +1,234 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SplineSpec.cxx +/// \brief Implementation of SplineSpec class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +#include "Rtypes.h" +#endif + +#include "SplineSpec.h" + +#if !defined(GPUCA_GPUCODE) +#include <iostream> +#endif + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +#include "TRandom.h" +#include "Riostream.h" +#include "TMath.h" +#include "SplineHelper.h" +#include "TCanvas.h" +#include "TNtuple.h" +#include "TFile.h" +#include "GPUCommonMath.h" + +templateClassImp(GPUCA_NAMESPACE::gpu::SplineContainer); +templateClassImp(GPUCA_NAMESPACE::gpu::SplineSpec); + +#endif + +using namespace std; +using namespace GPUCA_NAMESPACE::gpu; + +template <typename DataT> +void SplineContainer<DataT>::destroy() +{ + /// See FlatObject for description + mXdim = 0; + mYdim = 0; + mNknots = 0; + mGrid = nullptr; + mParameters = nullptr; + FlatObject::destroy(); +} + +template <typename DataT> +void SplineContainer<DataT>::setActualBufferAddress(char* actualFlatBufferPtr) +{ + /// See FlatObject for description + + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + mGrid = reinterpret_cast<Spline1D<DataT>*>(mFlatBufferPtr); + int offset = sizeof(*mGrid) * mXdim; + for (int i = 0; i < mXdim; i++) { + offset = alignSize(offset, mGrid[i].getBufferAlignmentBytes()); + mGrid[i].setActualBufferAddress(mFlatBufferPtr + offset); + offset += mGrid[i].getFlatBufferSize(); + } + offset = alignSize(offset, getParameterAlignmentBytes()); + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + offset); +} + +template <typename DataT> +void SplineContainer<DataT>::setFutureBufferAddress(char* futureFlatBufferPtr) +{ + /// See FlatObject for description + mParameters = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mParameters); + for (int i = 0; i < mXdim; i++) { + char* buffer = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mGrid[i].getFlatBufferPtr()); + mGrid[i].setFutureBufferAddress(buffer); + } + mGrid = relocatePointer(mFlatBufferPtr, futureFlatBufferPtr, mGrid); + FlatObject::setFutureBufferAddress(futureFlatBufferPtr); +} + +template <typename DataT> +void SplineContainer<DataT>::print() const +{ + printf(" Irregular Spline %dD->%dD: \n", mXdim, mYdim); + for (int i = 0; i < mXdim; i++) { + printf(" grid X%d: \n", i); + mGrid[i].print(); + } +} + +#if !defined(GPUCA_GPUCODE) + +template <typename DataT> +void SplineContainer<DataT>::cloneFromObject(const SplineContainer<DataT>& obj, char* newFlatBufferPtr) +{ + /// See FlatObject for description + + const char* oldFlatBufferPtr = obj.mFlatBufferPtr; + FlatObject::cloneFromObject(obj, newFlatBufferPtr); + mXdim = obj.mXdim; + mYdim = obj.mYdim; + mNknots = obj.mNknots; + + Spline1D<DataT>* newGrid = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mGrid); + for (int i = 0; i < mXdim; i++) { + char* buffer = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mGrid[i].getFlatBufferPtr()); + newGrid[i].cloneFromObject(obj.mGrid[i], buffer); + } + mGrid = newGrid; + mParameters = FlatObject::relocatePointer(oldFlatBufferPtr, mFlatBufferPtr, obj.mParameters); +} + +template <typename DataT> +void SplineContainer<DataT>::moveBufferTo(char* newFlatBufferPtr) +{ + /// See FlatObject for description + char* oldFlatBufferPtr = mFlatBufferPtr; + FlatObject::moveBufferTo(newFlatBufferPtr); + char* currFlatBufferPtr = mFlatBufferPtr; + mFlatBufferPtr = oldFlatBufferPtr; + setActualBufferAddress(currFlatBufferPtr); +} + +template <typename DataT> +void SplineContainer<DataT>::recreate( + int nXdim, int nYdim, const int numberOfKnots[/* nXdim */], const int* const knots[/* nXdim */]) +{ + /// Constructor for an irregular spline + + mXdim = nXdim; + mYdim = nYdim; + FlatObject::startConstruction(); + + Spline1D<DataT> vGrids[mXdim]; + + mNknots = 1; + for (int i = 0; i < mXdim; i++) { + if (knots) { + vGrids[i].recreate(0, numberOfKnots[i], knots[i]); + } else if (numberOfKnots) { + vGrids[i].recreate(0, numberOfKnots[i]); + } else { + vGrids[i].recreate(0, 2); + } + mNknots *= vGrids[i].getNumberOfKnots(); + } + + int offset = sizeof(Spline1D<DataT>) * mXdim; + + for (int i = 0; i < mXdim; i++) { + offset = alignSize(offset, vGrids[i].getBufferAlignmentBytes()); + offset += vGrids[i].getFlatBufferSize(); + } + + offset = alignSize(offset, getParameterAlignmentBytes()); + offset += getSizeOfParameters(); + + FlatObject::finishConstruction(offset); + + mGrid = reinterpret_cast<Spline1D<DataT>*>(mFlatBufferPtr); + + offset = sizeof(Spline1D<DataT>) * mXdim; + + for (int i = 0; i < mXdim; i++) { + new (&mGrid[i]) Spline1D<DataT>; // constructor + offset = alignSize(offset, mGrid[i].getBufferAlignmentBytes()); + mGrid[i].cloneFromObject(vGrids[i], mFlatBufferPtr + offset); + offset += mGrid[i].getFlatBufferSize(); + } + + offset = alignSize(offset, getParameterAlignmentBytes()); + mParameters = reinterpret_cast<DataT*>(mFlatBufferPtr + offset); + offset += getSizeOfParameters(); + + for (int i = 0; i < getNumberOfParameters(); i++) { + mParameters[i] = 0; + } +} + +template <typename DataT> +void SplineContainer<DataT>::recreate( + int nXdim, int nYdim, const int numberOfKnots[/* nXdim */]) +{ + /// Constructor for a regular spline + recreate(nXdim, nYdim, numberOfKnots, nullptr); +} + +#endif // GPUCA_GPUCODE + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation + +template <typename DataT> +void SplineContainer<DataT>:: + approximateFunction(const double xMin[/* mXdim */], const double xMax[/* mXdim */], + std::function<void(const double x[/* mXdim */], double f[/*mYdim*/])> F, + const int nAuxiliaryDataPoints[/* mXdim */]) +{ + /// approximate a function F with this spline + SplineHelper<DataT> helper; + helper.approximateFunction(*reinterpret_cast<Spline<DataT>*>(this), xMin, xMax, F, nAuxiliaryDataPoints); +} + +#ifndef GPUCA_ALIROOT_LIB +template <typename DataT> +int SplineContainer<DataT>::writeToFile(TFile& outf, const char* name) +{ + /// write a class object to the file + return FlatObject::writeToFile(*this, outf, name); +} + +template <typename DataT> +SplineContainer<DataT>* SplineContainer<DataT>::readFromFile( + TFile& inpf, const char* name) +{ + /// read a class object from the file + return FlatObject::readFromFile<SplineContainer<DataT>>(inpf, name); +} + +template <typename DataT> +int SplineContainer<DataT>::test(const bool draw, const bool drawDataPoints) +{ + return SplineHelper<DataT>::test(draw, drawDataPoints); +} +#endif + +#endif // GPUCA_GPUCODE && !GPUCA_STANDALONE + +template class GPUCA_NAMESPACE::gpu::SplineContainer<float>; +template class GPUCA_NAMESPACE::gpu::SplineContainer<double>; diff --git a/GPU/TPCFastTransformation/SplineSpec.h b/GPU/TPCFastTransformation/SplineSpec.h new file mode 100644 index 0000000000000..00f236940188b --- /dev/null +++ b/GPU/TPCFastTransformation/SplineSpec.h @@ -0,0 +1,554 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SplineSpec.h +/// \brief Definition of SplineSpec class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINESPEC_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINESPEC_H + +#include "Spline1D.h" +#include "FlatObject.h" +#include "GPUCommonDef.h" +#include "SplineUtil.h" + +#if !defined(__CINT__) && !defined(__ROOTCINT__) && !defined(GPUCA_GPUCODE) && !defined(GPUCA_NO_VC) && defined(__cplusplus) && __cplusplus >= 201703L +#include <Vc/Vc> +#include <Vc/SimdArray> +#endif + +class TFile; + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +/// ================================================================================================== +/// The class SplineContainer is a base Spline class. +/// It contains all the class members and those methods which only depends on the DataT data type. +/// It also contains all non-inlined methods with the implementation in SplineSpec.cxx file. +/// +/// DataT is a data type, which is supposed to be either double or float. +/// For other possible data types one has to add the corresponding instantiation line +/// at the end of the SplineSpec.cxx file +/// +template <typename DataT> +class SplineContainer : public FlatObject +{ + public: + typedef typename Spline1D<DataT>::SafetyLevel SafetyLevel; + typedef typename Spline1D<DataT>::Knot Knot; + + /// _____________ Version control __________________________ + + /// Version control + GPUd() static constexpr int getVersion() { return (1 << 16) + Spline1D<DataT>::getVersion(); } + + /// _____________ C++ constructors / destructors __________________________ + + /// Default constructor + SplineContainer() CON_DEFAULT; + + /// Disable all other constructors + SplineContainer(const SplineContainer&) CON_DELETE; + + /// Destructor + ~SplineContainer() CON_DEFAULT; + + /// _______________ Construction interface ________________________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// approximate a function F with this spline + void approximateFunction(const double xMin[/* mXdim */], const double xMax[/* mXdim */], + std::function<void(const double x[/* mXdim */], double f[/*mYdim*/])> F, + const int nAuxiliaryDataPoints[/* mXdim */] = nullptr); +#endif + + /// _______________ IO ________________________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// write a class object to the file + int writeToFile(TFile& outf, const char* name); + + /// read a class object from the file + static SplineContainer* readFromFile(TFile& inpf, const char* name); +#endif + + /// _______________ Getters ________________________ + + /// Get number of X dimensions + GPUd() int getXdimensions() const { return mXdim; } + + /// Get number of Y dimensions + GPUd() int getYdimensions() const { return mYdim; } + + /// Get minimal required alignment for the spline parameters + GPUd() static constexpr size_t getParameterAlignmentBytes() { return 16; } + + /// Number of parameters + GPUd() int getNumberOfParameters() const { return this->calcNumberOfParameters(mYdim); } + + /// Size of the parameter array in bytes + GPUd() size_t getSizeOfParameters() const { return sizeof(DataT) * this->getNumberOfParameters(); } + + /// Get a number of knots + GPUd() int getNumberOfKnots() const { return mNknots; } + + /// Number of parameters per knot + GPUd() int getNumberOfParametersPerKnot() const { return calcNumberOfParametersPerKnot(mYdim); } + + /// Get 1-D grid for dimX dimension + GPUd() const Spline1D<DataT>& getGrid(int dimX) const { return mGrid[dimX]; } + + /// Get u[] coordinate of i-th knot + GPUd() void getKnotU(int iKnot, int u[/* mXdim */]) const; + + /// Get index of a knot (iKnot1,iKnot2,..,iKnotN) + GPUd() int getKnotIndex(const int iKnot[/* mXdim */]) const; + + /// Get spline parameters + GPUd() DataT* getParameters() { return mParameters; } + + /// Get spline parameters const + GPUd() const DataT* getParameters() const { return mParameters; } + + /// _______________ Technical stuff ________________________ + + /// Get offset of Grid[dimX] flat data in the flat buffer + GPUd() size_t getGridOffset(int dimX) const { return mGrid[dimX].getFlatBufferPtr() - mFlatBufferPtr; } + + /// Set X range + void setXrange(const DataT xMin[/* mXdim */], const DataT xMax[/* mXdim */]); + + /// Print method + void print() const; + + /// _______________ Expert tools _______________ + + /// Number of parameters for given Y dimensions + GPUd() int calcNumberOfParameters(int nYdim) const + { + return calcNumberOfParametersPerKnot(nYdim) * getNumberOfKnots(); + } + + /// Number of parameters per knot + GPUd() int calcNumberOfParametersPerKnot(int nYdim) const + { + return (1 << mXdim) * nYdim; // 2^mXdim parameters per Y dimension + } + + ///_______________ Test tools _______________ + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && !defined(GPUCA_ALIROOT_LIB) // code invisible on GPU and in the standalone compilation + /// Test the class functionality + static int test(const bool draw = 0, const bool drawDataPoints = 1); +#endif + + /// _____________ FlatObject functionality, see FlatObject class for description ____________ + + using FlatObject::getBufferAlignmentBytes; + using FlatObject::getClassAlignmentBytes; + +#if !defined(GPUCA_GPUCODE) + void cloneFromObject(const SplineContainer& obj, char* newFlatBufferPtr); + void moveBufferTo(char* newBufferPtr); +#endif + + using FlatObject::releaseInternalBuffer; + + void destroy(); + void setActualBufferAddress(char* actualFlatBufferPtr); + void setFutureBufferAddress(char* futureFlatBufferPtr); + + protected: +#if !defined(GPUCA_GPUCODE) + /// Constructor for a regular spline + void recreate(int nXdim, int nYdim, const int nKnots[/* nXdim */]); + + /// Constructor for an irregular spline + void recreate(int nXdim, int nYdim, const int nKnots[/* nXdim */], const int* const knotU[/* nXdim */]); +#endif + + /// _____________ Data members ____________ + + int mXdim = 0; ///< dimentionality of X + int mYdim = 0; ///< dimentionality of Y + int mNknots = 0; ///< number of spline knots + + Spline1D<DataT>* mGrid; //! (transient!!) mXdim grids + DataT* mParameters; //! (transient!!) F-dependent parameters of the spline + +#ifndef GPUCA_ALIROOT_LIB + ClassDefNV(SplineContainer, 1); +#endif +}; + +template <typename DataT> +GPUdi() void SplineContainer<DataT>::getKnotU(int iKnot, int u[/* mXdim */]) const +{ + /// Get u[] coordinate of i-th knot + for (int dim = 0; dim < mXdim; dim++) { + int n = mGrid[dim].getNumberOfKnots(); + u[dim] = mGrid[dim].getKnot(iKnot % n).getU(); + iKnot /= n; + } +} + +template <typename DataT> +GPUdi() int SplineContainer<DataT>::getKnotIndex(const int iKnot[/* mXdim */]) const +{ + /// Get index of a knot (iKnot1,iKnot2,..,iKnotN) + int ind = iKnot[0]; + int n = 1; + for (int dim = 1; dim < mXdim; dim++) { + n *= mGrid[dim - 1].getNumberOfKnots(); + ind += n * iKnot[dim]; + } + return ind; +} + +template <typename DataT> +GPUdi() void SplineContainer<DataT>:: + setXrange(const DataT xMin[/* mXdim */], const DataT xMax[/* mXdim */]) +{ + /// Set X range + for (int i = 0; i < mXdim; i++) { + mGrid[i].setXrange(xMin[i], xMax[i]); + } +} + +/// ================================================================================================== +/// +/// SplineSpec class declares different specializations of the Spline class. +/// (See Spline.h for the description.) +/// +/// The specializations depend on the value of Spline's template parameters XdimT and YdimT. +/// specializations have different constructors and slightly different declarations of methods. +/// +/// The meaning of the template parameters: +/// +/// \param DataT data type: float or double +/// \param XdimT +/// XdimT > 0 : the number of X dimensions is known at the compile time and is equal to XdimT +/// XdimT = 0 : the number of X dimensions will be set in the runtime +/// XdimT < 0 : the number of X dimensions will be set in the runtime, and it will not exceed abs(XdimT) +/// \param YdimT same for the X dimensions +/// \param SpecT specialisation number: +/// 0 - a parent class for all other specializations +/// 1 - nXdim>0, nYdim>0: both nXdim and nYdim are set at the compile time +/// 2 - at least one of the dimensions must be set during runtime +/// 3 - specialization where nYdim==1 (a small add-on on top of the other specs) +/// +template <typename DataT, int XdimT, int YdimT, int SpecT> +class SplineSpec; + +/// ================================================================================================== +/// Specialization 0 declares common methods for all other Spline specializations. +/// Implementations of the methods may depend on the YdimT value. +/// +template <typename DataT, int XdimT, int YdimT> +class SplineSpec<DataT, XdimT, YdimT, 0> : public SplineContainer<DataT> +{ + typedef SplineContainer<DataT> TBase; + + public: + typedef typename TBase::SafetyLevel SafetyLevel; + typedef typename TBase::Knot Knot; + + /// _______________ Interpolation math ________________________ + + /// Get interpolated value S(x) + GPUd() void interpolate(const DataT x[/*mXdim*/], GPUgeneric() DataT S[/*mYdim*/]) const + { + const auto nXdimTmp = SplineUtil::getNdim<XdimT>(mXdim); + const auto nXdim = nXdimTmp.get(); + const auto maxXdimTmp = SplineUtil::getMaxNdim<XdimT>(mXdim); + DataT u[maxXdimTmp.get()]; + for (int i = 0; i < nXdim; i++) { + u[i] = mGrid[i].convXtoU(x[i]); + } + interpolateU<SafetyLevel::kSafe>(mXdim, mYdim, mParameters, u, S); + } + + /// Get interpolated value for S(u):inpXdim->inpYdim using spline parameters Parameters + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() void interpolateU(int inpXdim, int inpYdim, GPUgeneric() const DataT Parameters[], + const DataT u[/*inpXdim*/], GPUgeneric() DataT S[/*inpYdim*/]) const + { + const auto nXdimTmp = SplineUtil::getNdim<XdimT>(mXdim); + const auto nXdim = nXdimTmp.get(); + const auto maxXdimTmp = SplineUtil::getMaxNdim<XdimT>(mXdim); + const auto maxXdim = maxXdimTmp.get(); + const auto nYdimTmp = SplineUtil::getNdim<YdimT>(mYdim); + const auto nYdim = nYdimTmp.get(); + const auto maxYdimTmp = SplineUtil::getMaxNdim<XdimT>(mYdim); + const auto maxYdim = maxYdimTmp.get(); + + const auto nParameters = 1 << (2 * nXdim); //total Nr of Parameters necessary for one interpolation + const auto nKnotParametersPerY = 1 << nXdim; // Nr of Parameters per Knot per Y dimension + const auto nKnotParameters = (1 << nXdim) * nYdim; // Nr of Parameters per Knot + + DataT iParameters[(1 << (2 * maxXdim)) * maxYdim]; // Array for all parameters + + //get the indices of the "most left" Knot: + + int indices[maxXdim]; //indices of the 'most left' knot + for (int i = 0; i < nXdim; i++) { + indices[i] = mGrid[i].getLeftKnotIndexForU(u[i]); + } + // get all the needed parameters into one array iParameters[nParameters]: + int indicestmp[maxXdim]; + for (int i = 0; i < nKnotParametersPerY; i++) { // for every necessary Knot + for (int k = 0; k < nXdim; k++) { + indicestmp[k] = indices[k] + (i / (1 << k)) % 2; //get the knot-indices in every dimension (mirrored order binary counting) + } + int index = TBase::getKnotIndex(indicestmp); //get index of the current Knot + + for (int j = 0; j < nKnotParameters; j++) { //and fill the iparameter array with according parameters + iParameters[i * nKnotParameters + j] = Parameters[index * nKnotParameters + j]; + } + } + //now start with the interpolation loop: + + auto maxInterpolations = (1 << (2 * maxXdim - 2)) * maxYdim; + + DataT S0[maxInterpolations]; + DataT D0[maxInterpolations]; + DataT S1[maxInterpolations]; + DataT D1[maxInterpolations]; + + int nInterpolations = (1 << (2 * nXdim - 2)) * nYdim; + int nKnots = 1 << (nXdim); + + for (int d = 0; d < nXdim; d++) { //for every dimension + DataT* pointer[4] = {S0, D0, S1, D1}; // pointers for interpolation arrays S0, D0, S1, D1 point to Arraystart + for (int i = 0; i < nKnots; i++) { //for every knot + for (int j = 0; j < nKnots; j++) { // for every parametertype + int pointernr = 2 * (i % 2) + (j % 2); //to which array should it be delivered + for (int k = 0; k < nYdim; k++) { + pointer[pointernr][0] = iParameters[(i * nKnots + j) * nYdim + k]; + pointer[pointernr]++; + } + } // end for j (every parametertype) + } // end for i (every knot) + + const typename Spline1D<DataT>::Knot& knotL = mGrid[d].getKnot(indices[d]); + DataT coordinate = u[d]; + typedef Spline1DSpec<DataT, 0, 0> TGridX; + const TGridX& gridX = *((const TGridX*)&(mGrid[d])); + gridX.interpolateU(nInterpolations, knotL, S0, D0, S1, D1, coordinate, iParameters); + nInterpolations /= 4; + nKnots /= 2; + } //end d (every dimension) + + for (int i = 0; i < nYdim; i++) { + S[i] = iParameters[i]; // write into result-array + //std::cout<<iParameters[i] <<", "; + } + } // end interpolateU + + protected: + using TBase::mGrid; + using TBase::mParameters; + using TBase::mXdim; + using TBase::mYdim; + using TBase::TBase; // inherit constructors and hide them +}; + +/// ================================================================================================== +/// Specialization 1: XdimT>0, YdimT>0 where the number of dimensions is taken from template parameters +/// at the compile time +/// +template <typename DataT, int XdimT, int YdimT> +class SplineSpec<DataT, XdimT, YdimT, 1> + : public SplineSpec<DataT, XdimT, YdimT, 0> +{ + typedef SplineContainer<DataT> TVeryBase; + typedef SplineSpec<DataT, XdimT, YdimT, 0> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + +#if !defined(GPUCA_GPUCODE) + /// Default constructor + SplineSpec() : SplineSpec(nullptr) {} + + /// Constructor for a regular spline + SplineSpec(const int nKnots[/*XdimT*/]) : TBase() + { + recreate(nKnots); + } + /// Constructor for an irregular spline + SplineSpec(const int nKnots[/*XdimT*/], const int* const knotU[/*XdimT*/]) + : TBase() + { + recreate(nKnots, knotU); + } + /// Copy constructor + SplineSpec(const SplineSpec& v) : TBase() + { + TBase::cloneFromObject(v, nullptr); + } + /// Constructor for a regular spline + void recreate(const int nKnots[/*XdimT*/]) + { + TBase::recreate(XdimT, YdimT, nKnots); + } + + /// Constructor for an irregular spline + void recreate(const int nKnots[/*XdimT*/], const int* const knotU[/*XdimT*/]) + { + TBase::recreate(XdimT, YdimT, nKnots, knotU); + } +#endif + + /// Get number of X dimensions + GPUd() constexpr int getXdimensions() const { return XdimT; } + + /// Get number of Y dimensions + GPUd() constexpr int getYdimensions() const { return YdimT; } + + /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ + + /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. + template <SafetyLevel SafeT = SafetyLevel::kSafe> + GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], + const DataT u[/*XdimT*/], GPUgeneric() DataT S[/*YdimT*/]) const + { + TBase::template interpolateU<SafeT>(XdimT, YdimT, Parameters, u, S); + } + + /// _______________ Suppress some parent class methods ________________________ + private: +#if !defined(GPUCA_GPUCODE) + using TBase::recreate; +#endif + using TBase::interpolateU; +}; + +/// ================================================================================================== +/// Specialization 2 (XdimT<=0 || YdimT<=0) where at least one of the dimensions +/// must be set in the runtime via a constructor parameter +/// +template <typename DataT, int XdimT, int YdimT> +class SplineSpec<DataT, XdimT, YdimT, 2> + : public SplineSpec<DataT, XdimT, YdimT, 0> +{ + typedef SplineContainer<DataT> TVeryBase; + typedef SplineSpec<DataT, XdimT, YdimT, 0> TBase; + + public: + typedef typename TVeryBase::SafetyLevel SafetyLevel; + +#if !defined(GPUCA_GPUCODE) + /// Default constructor + SplineSpec() : SplineSpec((XdimT > 0 ? XdimT : 0), (YdimT > 0 ? YdimT : 0), nullptr) {} + + /// Constructor for a regular spline + SplineSpec(int nXdim, int nYdim, const int nKnots[/* nXdim */]) : TBase() + { + this->recreate(nXdim, nYdim, nKnots); + } + + /// Constructor for an irregular spline + SplineSpec(int nXdim, int nYdim, const int nKnots[/* nXdim */], const int* const knotU[/* nXdim */]) + : TBase() + { + this->recreate(nXdim, nYdim, nKnots, knotU); + } + + /// Copy constructor + SplineSpec(const SplineSpec& v) : TBase() + { + cloneFromObject(v, nullptr); + } + + /// Constructor for a regular spline + void recreate(int nXdim, int nYdim, const int nKnots[/* nXdim */]) + { + checkDimensions(nXdim, nYdim); + TBase::recreate(nXdim, nYdim, nKnots); + } + + /// Constructor for an irregular spline + void recreate(int nXdim, int nYdim, const int nKnots[/* nXdim */], const int* const knotU[/* nXdim */]) + { + checkDimensions(nXdim, nYdim); + TBase::recreate(nXdim, nYdim, nKnots, knotU); + } + +#endif + + /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ + + using TBase::interpolateU; + + /// Check dimensions + void checkDimensions(int& nXdim, int& nYdim) + { + if (XdimT > 0 && nXdim != XdimT) { + assert(0); + nXdim = XdimT; + } + if (XdimT < 0 && nXdim > abs(XdimT)) { + assert(0); + nXdim = abs(XdimT); + } + if (nXdim < 0) { + assert(0); + nXdim = 0; + } + if (YdimT > 0 && nYdim != YdimT) { + assert(0); + nYdim = YdimT; + } + if (YdimT < 0 && nYdim > abs(YdimT)) { + assert(0); + nYdim = abs(YdimT); + } + if (nYdim < 0) { + assert(0); + nYdim = 0; + } + } +}; + +/// ================================================================================================== +/// Specialization 3 where the number of Y dimensions is 1. +/// +template <typename DataT, int XdimT> +class SplineSpec<DataT, XdimT, 1, 3> + : public SplineSpec<DataT, XdimT, 1, SplineUtil::getSpec(XdimT, 999)> +{ + typedef SplineSpec<DataT, XdimT, 1, SplineUtil::getSpec(XdimT, 999)> TBase; + + public: + using TBase::TBase; // inherit constructors + + /// Simplified interface for 1D: return the interpolated value + GPUd() DataT interpolate(const DataT x[]) const + { + DataT S = 0.; + TBase::interpolate(x, &S); + return S; + } + + // this parent method should be public anyhow, + // but the compiler gets confused w/o this extra declaration + using TBase::interpolate; +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/SplineUtil.h b/GPU/TPCFastTransformation/SplineUtil.h new file mode 100644 index 0000000000000..78c1c540d2715 --- /dev/null +++ b/GPU/TPCFastTransformation/SplineUtil.h @@ -0,0 +1,115 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SplineSpec.h +/// \brief Definition of SplineSpec class +/// +/// \author Sergey Gorbunov <sergey.gorbunov@cern.ch> + +#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEUTIL_H +#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_SPLINEUTIL_H + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +/// ================================================================================================== +/// Utilities for the Spline class +/// +class SplineUtil +{ + public: + /// Calculate a Spline specialization number depending on nXdim, nYdim + /// + static constexpr int getSpec(int nXdim, int nYdim) + { + // List of the Spline class specializations: + // + // 0 - a parent class for other specializations + // 1 - nXdim>0, nYdim>0: both nXdim and nYdim are set at the compile time + // 2 - at least one of the dimensions must be set during runtime + // 3 - specialization where nYdim==1 (a small add-on on top of the other specs) + + // calculate it as one return statement to make the AliRoot compiler happy + return (nYdim == 1) ? 3 : ((nXdim > 0 && nYdim > 0) ? 1 : 2); + /* + if (nYdim == 1) { + return 3; + } + if (nXdim > 0 && nYdim > 0) { + return 1; + } else { + return 2; + } + */ + } + + /// Spline1D & Spline2D specialization number depending on nYdim + /// + static constexpr int getSpec(int nYdim) + { + return getSpec(1, nYdim); + } + + /// abs() as a constexpr method, to make the GPU compiler happy + static constexpr int abs(int v) { return (v >= 0) ? v : -v; } + + /// class lets one to switch between constexpr int ValTrueT and int mValFalse, depending on the ConditionT + template <bool ConditionT, int ValTrueT> + class Switch; + + /// An expression + /// const auto tmp = getNdim<nDimT>(int Ndim); + /// tmp.get(); + /// returns either a constexpr integer NdimT, or an integer Ndim, depending on the (NdimT>0) value + /// (a temporary variable tmp is needed to make the GPU compiler happy) + /// + template <int NdimT> + GPUd() static Switch<(NdimT > 0), NdimT> getNdim(int Ndim) + { + return Switch<(NdimT > 0), NdimT>(Ndim); + } + + /// An expression + /// const auto tmp = getMaxNdim(int Ndim); + /// tmp.get(); + /// returns either a constexpr integer abs(NdimT), or an integer Ndim, depending on the (NdimT!=0) value + /// + template <int NdimT> + GPUd() static Switch<(NdimT != 0), abs(NdimT)> getMaxNdim(int Ndim) + { + return Switch<(NdimT != 0), abs(NdimT)>(Ndim); + } +}; + +template <int ValTrueT> +class SplineUtil::Switch<true, ValTrueT> +{ + public: + GPUd() Switch(int /*valFalse*/) {} + GPUd() static constexpr int get() { return ValTrueT; } +}; + +template <int ValTrueT> +class SplineUtil::Switch<false, ValTrueT> +{ + public: + GPUd() Switch(int valFalse) : mValFalse(valFalse) {} + GPUd() int get() const { return mValFalse; } + + private: + int mValFalse; +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5bf65cf95d127..aed967c972429 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -19,7 +19,7 @@ #include <iostream> #include <cmath> #include "ChebyshevFit1D.h" -#include "SplineHelper2D.h" +#include "Spline2DHelper.h" #endif using namespace GPUCA_NAMESPACE::gpu; @@ -219,8 +219,8 @@ void TPCFastSpaceChargeCorrection::print() const const SplineType& spline = getSpline(is, ir); const float* d = getSplineData(is, ir); int k = 0; - for (int i = 0; i < spline.getGridU1().getNumberOfKnots(); i++) { - for (int j = 0; j < spline.getGridU2().getNumberOfKnots(); j++, k++) { + for (int i = 0; i < spline.getGridX1().getNumberOfKnots(); i++) { + for (int j = 0; j < spline.getGridX2().getNumberOfKnots(); j++, k++) { std::cout << d[k] << " "; } std::cout << std::endl; @@ -414,8 +414,8 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() area.vMax = vLength; area.cvMax = vLength; info.CorrU0 = area.cuMin; - info.scaleCorrUtoGrid = (spline.getGridU1().getNumberOfKnots() - 1) / (area.cuMax - area.cuMin); - info.scaleCorrVtoGrid = (spline.getGridU2().getNumberOfKnots() - 1) / area.cvMax; + info.scaleCorrUtoGrid = (spline.getGridX1().getNumberOfKnots() - 1) / (area.cuMax - area.cuMin); + info.scaleCorrVtoGrid = (spline.getGridX2().getNumberOfKnots() - 1) / area.cvMax; } // row } // slice } @@ -497,8 +497,8 @@ void TPCFastSpaceChargeCorrection::initInverse(bool prn) initMaxDriftLength(prn); - SplineHelper2D<float> helper; - std::vector<float> dataPointF; + Spline2DHelper<float> helper; + std::vector<double> dataPointF; std::vector<float> splineParameters; ChebyshevFit1D chebFitterX, chebFitterU, chebFitterV; @@ -572,8 +572,9 @@ void TPCFastSpaceChargeCorrection::initInverse(bool prn) } chebFitterV.addMeasurement(cv, dv); } // v - if (prn) + if (prn) { std::cout << "u " << u << " nmeas " << chebFitterV.getNmeasurements() << std::endl; + } if (chebFitterV.getNmeasurements() < 1) { continue; } @@ -629,8 +630,8 @@ void TPCFastSpaceChargeCorrection::initInverse(bool prn) } SliceRowInfo& info = mSliceRowInfoPtr[slice * mGeo.getNumberOfRows() + row]; info.CorrU0 = area.cuMin; - info.scaleCorrUtoGrid = (spline.getGridU1().getNumberOfKnots() - 1) / (area.cuMax - area.cuMin); - info.scaleCorrVtoGrid = (spline.getGridU2().getNumberOfKnots() - 1) / area.cvMax; + info.scaleCorrUtoGrid = (spline.getGridX1().getNumberOfKnots() - 1) / (area.cuMax - area.cuMin); + info.scaleCorrVtoGrid = (spline.getGridX2().getNumberOfKnots() - 1) / area.cvMax; dataPointF.resize(helper.getNumberOfDataPoints() * 3); @@ -645,7 +646,7 @@ void TPCFastSpaceChargeCorrection::initInverse(bool prn) double dcol = (area.cuMax - area.cuMin) / (helper.getNumberOfDataPointsU1() - 1); for (int iv = 0; iv < helper.getNumberOfDataPointsU2(); iv++) { double cv = iv * drow; - float* dataPointFrow = &dataPointF[iv * helper.getNumberOfDataPointsU1() * 3]; + double* dataPointFrow = &dataPointF[iv * helper.getNumberOfDataPointsU1() * 3]; for (int iu = 0; iu < helper.getNumberOfDataPointsU1(); iu++) { dataPointFrow[iu * 3 + 0] = 0; dataPointFrow[iu * 3 + 1] = 0; @@ -735,8 +736,9 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) getCorrectionInvUV(slice, row, cu, cv, nu, nv); double d[3] = {nx - cx, nu - u, nv - v}; for (int i = 0; i < 3; i++) { - if (fabs(d[i]) > fabs(maxDrow[i])) + if (fabs(d[i]) > fabs(maxDrow[i])) { maxDrow[i] = d[i]; + } } if (prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index d1ca35e43357f..b78e5f233c703 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -64,7 +64,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject float vMax; }; - typedef Spline2D<float, 3, 0> SplineType; + typedef Spline2D<float, 3> SplineType; /// _____________ Constructors / destructors __________________________ @@ -270,8 +270,8 @@ GPUdi() int TPCFastSpaceChargeCorrection::getCorrection(int slice, int row, floa const float* splineData = getSplineData(slice, row); float su = 0, sv = 0; mGeo.convUVtoScaledUV(slice, row, u, v, su, sv); - su *= spline.getGridU1().getUmax(); - sv *= spline.getGridU2().getUmax(); + su *= spline.getGridX1().getUmax(); + sv *= spline.getGridX2().getUmax(); float dxuv[3]; spline.interpolateU(splineData, su, sv, dxuv); dx = dxuv[0]; @@ -285,7 +285,7 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( { //const RowInfo& rowInfo = getRowInfo(row); const SliceRowInfo& sliceRowInfo = getSliceRowInfo(slice, row); - const Spline2D<float, 1, 0>& spline = reinterpret_cast<const Spline2D<float, 1, 0>&>(getSpline(slice, row)); + const Spline2D<float, 1>& spline = reinterpret_cast<const Spline2D<float, 1>&>(getSpline(slice, row)); const float* splineData = getSplineData(slice, row, 1); float gridU = (cu - sliceRowInfo.CorrU0) * sliceRowInfo.scaleCorrUtoGrid; float gridV = cv * sliceRowInfo.scaleCorrVtoGrid; @@ -299,7 +299,7 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( { //const RowInfo& rowInfo = getRowInfo(row); const SliceRowInfo& sliceRowInfo = getSliceRowInfo(slice, row); - const Spline2D<float, 2, 0>& spline = reinterpret_cast<const Spline2D<float, 2, 0>&>(getSpline(slice, row)); + const Spline2D<float, 2>& spline = reinterpret_cast<const Spline2D<float, 2>&>(getSpline(slice, row)); const float* splineData = getSplineData(slice, row, 2); float gridU = (corrU - sliceRowInfo.CorrU0) * sliceRowInfo.scaleCorrUtoGrid; float gridV = corrV * sliceRowInfo.scaleCorrVtoGrid; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.cxx b/GPU/TPCFastTransformation/TPCFastTransform.cxx index a3878dbf4360e..67466abe5b93f 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransform.cxx @@ -158,12 +158,12 @@ int TPCFastTransform::writeToFile(std::string outFName, std::string name) /// store to file assert(isConstructed()); - if (outFName.empty()) + if (outFName.empty()) { outFName = "tpcFastTransform.root"; - - if (name.empty()) + } + if (name.empty()) { name = "TPCFastTransform"; - + } TFile outf(outFName.data(), "recreate"); if (outf.IsZombie()) { LOG(ERROR) << "Failed to open output file " << outFName; @@ -171,12 +171,14 @@ int TPCFastTransform::writeToFile(std::string outFName, std::string name) } bool isBufferExternal = !isBufferInternal(); - if (isBufferExternal) + if (isBufferExternal) { adoptInternalBuffer(mFlatBufferPtr); + } outf.WriteObjectAny(this, Class(), name.data()); outf.Close(); - if (isBufferExternal) + if (isBufferExternal) { clearInternalBufferPtr(); + } return 0; } @@ -184,12 +186,12 @@ TPCFastTransform* TPCFastTransform::loadFromFile(std::string inpFName, std::stri { /// load from file - if (inpFName.empty()) + if (inpFName.empty()) { inpFName = "tpcFastTransform.root"; - - if (name.empty()) + } + if (name.empty()) { name = "TPCFastTransform"; - + } TFile inpf(inpFName.data()); if (inpf.IsZombie()) { LOG(ERROR) << "Failed to open input file " << inpFName; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index ae09e5f94cee2..d8fdf6b73aa0f 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -152,12 +152,12 @@ int TPCFastTransformGeo::test(int slice, int row, float ly, float lz) const int error = 0; - if (!isConstructed()) + if (!isConstructed()) { error = -1; - - if (mNumberOfRows <= 0 || mNumberOfRows >= MaxNumberOfRows) + } + if (mNumberOfRows <= 0 || mNumberOfRows >= MaxNumberOfRows) { error = -2; - + } float lx = getRowInfo(row).x; float lx1 = 0.f, ly1 = 0.f, lz1 = 0.f; float gx = 0.f, gy = 0.f, gz = 0.f; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 4deabbbb1b48d..c8676c61751b1 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -17,7 +17,7 @@ #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMGEO_H #include "GPUCommonDef.h" -#ifndef __OPENCL__ +#ifndef GPUCA_GPUCODE_DEVICE #include <memory> #endif diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx index 50a066eeeba82..4fbc08c2e8ba1 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx @@ -20,7 +20,7 @@ #include "AliTPCTransform.h" #include "AliTPCcalibDB.h" #include "TPCFastTransform.h" -#include "SplineHelper2D.h" +#include "Spline2DHelper.h" using namespace GPUCA_NAMESPACE::gpu; @@ -280,9 +280,9 @@ int TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransform, const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(slice, row); float* data = correction.getSplineData(slice, row); - SplineHelper2D<float> helper; + Spline2DHelper<float> helper; helper.setSpline(spline, 4, 4); - auto F = [&](float su, float sv, float dxuv[3]) { + auto F = [&](double su, double sv, double dxuv[3]) { float x = rowInfo.x; // x, u, v cordinates of the knot (local cartesian coord. of slice // towards central electrode ) diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_AliRoot.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_AliRoot.h index e3a1e1149b2ed..d64a7fc0f8fd7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_AliRoot.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_AliRoot.h @@ -16,37 +16,5 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; -/* -#pragma link C++ nestedclasses; -#pragma link C++ nestedtypedef; - -#pragma link C++ namespace AliGPU::gpu; - -#pragma link C++ class AliGPU::gpu::Spline1D < float> + ; -#pragma link C++ class AliGPU::gpu::Spline1D < double> + ; -#pragma link C++ class AliGPU::gpu::Spline2DBase < float, false> + ; -#pragma link C++ class AliGPU::gpu::Spline2DBase < double, false> + ; -#pragma link C++ class AliGPU::gpu::Spline2DBase < float, true> + ; -#pragma link C++ class AliGPU::gpu::Spline2DBase < double, true> + ; - -#pragma link C++ class AliGPU::gpu::Spline2D < float, 1> - ; - -#pragma link C++ class AliGPU::gpu::SplineHelper1D < float>; -#pragma link C++ class AliGPU::gpu::SplineHelper1D < double>; -#pragma link C++ class AliGPU::gpu::SplineHelper2D < float>; -#pragma link C++ class AliGPU::gpu::SplineHelper2D < double>; - -#pragma link C++ class AliGPU::gpu::RegularSpline1D + ; -#pragma link C++ class AliGPU::gpu::IrregularSpline1D + ; -#pragma link C++ class AliGPU::gpu::IrregularSpline2D3D + ; -#pragma link C++ class AliGPU::gpu::SemiregularSpline2D3D + ; -#pragma link C++ class AliGPU::gpu::IrregularSpline2D3DCalibrator + ; -#pragma link C++ class AliGPU::gpu::TPCFastTransformGeo + ; -#pragma link C++ class AliGPU::gpu::TPCFastTransformGeo::SliceInfo + ; -#pragma link C++ class AliGPU::gpu::TPCFastTransformGeo::RowInfo + ; -#pragma link C++ class AliGPU::gpu::TPCFastTransform + ; - -#pragma link C++ class AliGPU::gpu::TPCFastSpaceChargeCorrection + ; -*/ #endif diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index c35b1c2dcdd29..4db934adfbfad 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -20,27 +20,32 @@ #pragma link C++ nestedclasses; #pragma link C++ nestedtypedef; -//#pragma link C++ function o2::gpu::initSplineLibrary; - #pragma link C++ namespace o2::gpu; -//#pragma link C++ namespace o2::gpu::test+; -//#pragma link C++ class o2::gpu::test::A<float>+; +#pragma link C++ class o2::gpu::Spline1DContainer < float> + ; +#pragma link C++ class o2::gpu::Spline1DContainer < double> + ; #pragma link C++ class o2::gpu::Spline1D < float> + ; #pragma link C++ class o2::gpu::Spline1D < double> + ; -#pragma link C++ class o2::gpu::Spline2DBase < float, false> + ; -#pragma link C++ class o2::gpu::Spline2DBase < double, false> + ; -#pragma link C++ class o2::gpu::Spline2DBase < float, true> + ; -#pragma link C++ class o2::gpu::Spline2DBase < double, true> + ; - -#pragma link C++ class o2::gpu::Spline2D < float, 1> - ; - -#pragma link C++ class o2::gpu::SplineHelper1D < float>; -#pragma link C++ class o2::gpu::SplineHelper1D < double>; -#pragma link C++ class o2::gpu::SplineHelper2D < float>; -#pragma link C++ class o2::gpu::SplineHelper2D < double>; - -#pragma link C++ class o2::gpu::ChebyshevFit1D; +#pragma link C++ class o2::gpu::Spline1DHelper < float> + ; +#pragma link C++ class o2::gpu::Spline1DHelper < double> + ; +#pragma link C++ class o2::gpu::Spline1DSpec < float, 0, 2> + ; +#pragma link C++ class o2::gpu::Spline1DSpec < double, 0, 2> + ; + +#pragma link C++ class o2::gpu::Spline2DContainer < float> + ; +#pragma link C++ class o2::gpu::Spline2DContainer < double> + ; +#pragma link C++ class o2::gpu::Spline2D < float> + ; +#pragma link C++ class o2::gpu::Spline2D < double> + ; +#pragma link C++ class o2::gpu::Spline2DHelper < float> + ; +#pragma link C++ class o2::gpu::Spline2DHelper < double> + ; + +#pragma link C++ class o2::gpu::SplineContainer < float> + ; +#pragma link C++ class o2::gpu::SplineContainer < double> + ; +#pragma link C++ class o2::gpu::Spline < float> + ; +#pragma link C++ class o2::gpu::Spline < double> + ; +#pragma link C++ class o2::gpu::SplineHelper < float> + ; +#pragma link C++ class o2::gpu::SplineHelper < double> + ; + +#pragma link C++ class o2::gpu::ChebyshevFit1D + ; #pragma link C++ class o2::gpu::RegularSpline1D + ; #pragma link C++ class o2::gpu::IrregularSpline1D + ; diff --git a/GPU/TPCFastTransformation/devtools/IrregularSpline1D.cxx b/GPU/TPCFastTransformation/devtools/IrregularSpline1D.cxx index ae97939980c2f..7c4deefd01f3f 100644 --- a/GPU/TPCFastTransformation/devtools/IrregularSpline1D.cxx +++ b/GPU/TPCFastTransformation/devtools/IrregularSpline1D.cxx @@ -187,8 +187,9 @@ void IrregularSpline1D::constructRegular(int numberOfKnots) /// \param numberOfKnots Number of knots /// - if (numberOfKnots < 5) + if (numberOfKnots < 5) { numberOfKnots = 5; + } std::vector<float> knots(numberOfKnots); double du = 1. / (numberOfKnots - 1.); diff --git a/GPU/TPCFastTransformation/devtools/IrregularSpline2D3D.h b/GPU/TPCFastTransformation/devtools/IrregularSpline2D3D.h index e0e652717dad2..7fc312bfc7bcd 100644 --- a/GPU/TPCFastTransformation/devtools/IrregularSpline2D3D.h +++ b/GPU/TPCFastTransformation/devtools/IrregularSpline2D3D.h @@ -353,9 +353,9 @@ GPUdi() void IrregularSpline2D3D::getSplineVec(const float* correctedData, float //dataVvec.scatter(dataV, Vc::SimdArray<uint, 12>(Vc::IndexesFromZero)); dataVvec.store(dataV, Vc::Unaligned); - for (unsigned int i = 12; i < 9 + V::size(); i++) // fill not used part of the vector with 0 + for (unsigned int i = 12; i < 9 + V::size(); i++) { // fill not used part of the vector with 0 dataV[i] = 0.f; - + } // calculate F values at V==v and U == u V dataU0vec(dataV), dataU1vec(dataV + 3), dataU2vec(dataV + 6), dataU3vec(dataV + 9); diff --git a/GPU/TPCFastTransformation/devtools/IrregularSpline2D3DCalibrator.cxx b/GPU/TPCFastTransformation/devtools/IrregularSpline2D3DCalibrator.cxx index ec8c4667a0ca6..a21b7119c95e1 100644 --- a/GPU/TPCFastTransformation/devtools/IrregularSpline2D3DCalibrator.cxx +++ b/GPU/TPCFastTransformation/devtools/IrregularSpline2D3DCalibrator.cxx @@ -38,8 +38,9 @@ void IrregularSpline2D3DCalibrator::setRasterSize(int nKnotsU, int nKnotsV) int n[2] = {nKnotsU, nKnotsV}; for (int uv = 0; uv < 2; ++uv) { - if (n[uv] < mMaxNKnots[uv]) + if (n[uv] < mMaxNKnots[uv]) { n[uv] = mMaxNKnots[uv]; + } } mRaster.constructRegular(n[0], n[1]); @@ -53,8 +54,9 @@ void IrregularSpline2D3DCalibrator::setMaxNKnots(int nKnotsU, int nKnotsV) mMaxNKnots[1] = nKnotsV; for (int uv = 0; uv < 2; ++uv) { - if (mMaxNKnots[uv] < 5) + if (mMaxNKnots[uv] < 5) { mMaxNKnots[uv] = 5; + } } } @@ -88,10 +90,12 @@ void IrregularSpline2D3DCalibrator::startCalibration(std::function<void(float, f double u = i * du; d.rasterKnot = nearbyint(u * (mRaster.getGrid(uv).getNumberOfKnots() - 1)); //std::cout<<"uv "<<uv<<" i "<<d.rasterKnot<<" u "<<u<<std::endl; - if (d.rasterKnot <= lastKnot) + if (d.rasterKnot <= lastKnot) { continue; - if (d.rasterKnot >= mRaster.getGrid(uv).getNumberOfKnots() - 1) + } + if (d.rasterKnot >= mRaster.getGrid(uv).getNumberOfKnots() - 1) { continue; + } mKnots[uv].push_back(d); lastKnot = d.rasterKnot; } @@ -158,8 +162,9 @@ IrregularSpline2D3DCalibrator::Action IrregularSpline2D3DCalibrator::checkAction std::list<KnotData>::iterator next = knot; ++next; if (next != mKnots[uv].end()) { - if (next->rasterKnot <= knot->rasterKnot + 1) + if (next->rasterKnot <= knot->rasterKnot + 1) { isSpaceUp = 0; + } } } @@ -170,14 +175,15 @@ IrregularSpline2D3DCalibrator::Action IrregularSpline2D3DCalibrator::checkAction std::list<KnotData>::iterator prev = knot; if (prev != mKnots[uv].begin()) { --prev; - if (prev->rasterKnot >= knot->rasterKnot - 1) + if (prev->rasterKnot >= knot->rasterKnot - 1) { isSpaceDn = 0; + } } } - if (!isSpaceUp && !isSpaceDn) + if (!isSpaceUp && !isSpaceDn) { return ret; - + } // get the area of interest int regionKnotFirst = knot->rasterKnot; @@ -224,9 +230,9 @@ IrregularSpline2D3DCalibrator::Action IrregularSpline2D3DCalibrator::checkAction int uv = knot->uv; - if (mSpline.getGrid(uv).getNumberOfKnots() <= 5) + if (mSpline.getGrid(uv).getNumberOfKnots() <= 5) { return ret; - + } // get the area of interest int regionKnotFirst = knot->rasterKnot; @@ -289,8 +295,9 @@ bool IrregularSpline2D3DCalibrator::doCalibrationStep() for (int uv = 0; uv < 2; ++uv) { for (std::list<KnotData>::iterator i = mKnots[uv].begin(); i != mKnots[uv].end(); ++i) { Action a = checkActionShift(i); - if (a.cost < bestAction.cost) + if (a.cost < bestAction.cost) { bestAction = a; + } } } @@ -320,8 +327,9 @@ bool IrregularSpline2D3DCalibrator::doCalibrationStep() for (std::list<KnotData>::iterator i = mKnots[uv].begin(); i != mKnots[uv].end(); ++i) { Action a = checkActionRemove(i); - if (a.cost < bestAction.cost) + if (a.cost < bestAction.cost) { bestAction = a; + } } } bestAction.cost = sqrt(bestAction.cost / 3.); @@ -349,8 +357,9 @@ std::unique_ptr<float[]> IrregularSpline2D3DCalibrator::calibrateSpline(Irregula // main method: spline calibration startCalibration(F); - while (doCalibrationStep()) + while (doCalibrationStep()) { ; + } createCurrentSpline(); spline_uv.cloneFromObject(mSpline, nullptr); std::unique_ptr<float[]> tmp(new float[mSpline.getNumberOfKnots()]); @@ -377,8 +386,9 @@ double IrregularSpline2D3DCalibrator::getMaxDeviationLine(const IrregularSpline2 double dy = fy - fy0; double dz = fz - fz0; double d2 = dx * dx + dy * dy + dz * dz; - if (dMax2 < d2) + if (dMax2 < d2) { dMax2 = d2; + } } return dMax2; } @@ -389,8 +399,9 @@ double IrregularSpline2D3DCalibrator::getMaxDeviationArea(const IrregularSpline2 double dMax = 0.; for (int knot = knotFirst; knot <= knotLast; ++knot) { double d = getMaxDeviationLine(spline, data, axis, knot); - if (dMax < d) + if (dMax < d) { dMax = d; + } } return dMax; } diff --git a/GPU/TPCFastTransformation/devtools/RegularSpline1D.h b/GPU/TPCFastTransformation/devtools/RegularSpline1D.h index cacc8a6523e2e..8d2fa51440b1d 100644 --- a/GPU/TPCFastTransformation/devtools/RegularSpline1D.h +++ b/GPU/TPCFastTransformation/devtools/RegularSpline1D.h @@ -141,10 +141,12 @@ inline T RegularSpline1D::getSpline(const T correctedData[], float u) const inline double RegularSpline1D::knotIndexToU(int iknot) const { - if (iknot <= 0) + if (iknot <= 0) { return 0; - if (iknot >= mNumberOfKnots) + } + if (iknot >= mNumberOfKnots) { return 1; + } return iknot / ((double)mNumberOfKnots - 1.); } @@ -152,10 +154,11 @@ inline int RegularSpline1D::getKnotIndex(float u) const { //index is just u elem [0, 1] * numberOfKnots and then floored. (so the "left" coordinate beside u gets chosen) int index = (int)(u * (mNumberOfKnots - 1)); - if (index <= 1) + if (index <= 1) { index = 1; - else if (index >= mNumberOfKnots - 3) + } else if (index >= mNumberOfKnots - 3) { index = mNumberOfKnots - 3; + } return index; } diff --git a/GPU/TPCFastTransformation/devtools/SemiregularSpline2D3D.h b/GPU/TPCFastTransformation/devtools/SemiregularSpline2D3D.h index be03655206c6b..309bdcf4cb569 100644 --- a/GPU/TPCFastTransformation/devtools/SemiregularSpline2D3D.h +++ b/GPU/TPCFastTransformation/devtools/SemiregularSpline2D3D.h @@ -287,7 +287,6 @@ inline void SemiregularSpline2D3D::correctEdges(T* data) const } { // ==== high edge of V ==== - const RegularSpline1D& gridU = getGridU(nv - 4); int nu = getGridU(nv - 1).getNumberOfKnots(); for (int iu = 0; iu < nu; iu++) { diff --git a/GPU/TPCFastTransformation/macro/SplineDemo.C b/GPU/TPCFastTransformation/macro/SplineDemo.C index 9decb4da5a665..c549d2d3d0a53 100644 --- a/GPU/TPCFastTransformation/macro/SplineDemo.C +++ b/GPU/TPCFastTransformation/macro/SplineDemo.C @@ -17,7 +17,7 @@ #include "TLine.h" #include "GPU/Spline1D.h" #include "GPU/IrregularSpline1D.h" -#include "GPU/SplineHelper1D.h" +#include "GPU/Spline1DHelper.h" #include "Math/Functor.h" #include "Math/ChebyshevApprox.h" @@ -28,7 +28,7 @@ int nKnots = 4; static double Fcoeff[2 * (Fdegree + 1)]; -void F(float u, float f[]) +void F(double u, double f[]) { double uu = u * TMath::Pi() / (nKnots - 1); f[0] = 0; //Fcoeff[0]/2; @@ -55,14 +55,14 @@ void F(float u, float f[]) double F1D(double u) { - float f = 0; + double f = 0; F(u, &f); return f; } -float Flocal(float u) +double Flocal(double u) { - float f = 0; + double f = 0; F(u * (nKnots - 1), &f); return f; } @@ -134,11 +134,11 @@ int SplineDemo() Fcoeff[i] = gRandom->Uniform(-1, 1); } - o2::gpu::Spline1D<float> spline(nKnots, 1); + o2::gpu::Spline1D<float, 1> spline(nKnots); spline.approximateFunction(0, nKnots - 1, F, nAxiliaryPoints); - o2::gpu::Spline1D<float> splineClassic(nKnots, 1); - o2::gpu::SplineHelper1D<float> helper; + o2::gpu::Spline1D<float, 1> splineClassic(nKnots); + o2::gpu::Spline1DHelper<float> helper; helper.approximateFunctionClassic(splineClassic, 0, nKnots - 1, F); IrregularSpline1D splineLocal; @@ -169,8 +169,8 @@ int SplineDemo() helper.setSpline(spline, 1, nAxiliaryPoints); for (int j = 0; j < helper.getNumberOfDataPoints(); j++) { - const typename SplineHelper1D<float>::DataPoint& p = helper.getDataPoint(j); - float f0; + const typename Spline1DHelper<float>::DataPoint& p = helper.getDataPoint(j); + double f0; F(p.u, &f0); double fs = spline.interpolate(spline.convUtoX(p.u)); if (p.isKnot) { @@ -210,7 +210,7 @@ int SplineDemo() int statN = 0; for (float s = 0; s < 1. + stepS; s += stepS) { double u = s * (nKnots - 1); - float f0; + double f0; F(u, &f0); double fBestFit = spline.interpolate(spline.convUtoX(u)); double fClass = splineClassic.interpolate(splineClassic.convUtoX(u)); diff --git a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C index 9dfd2a4d271fe..d052e03efe2e7 100644 --- a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C +++ b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C @@ -24,17 +24,22 @@ #include "TH3.h" #include "DataFormatsTPC/Defs.h" -#include "TPCSimulation/SpaceCharge.h" +#include "TPCSpaceCharge/SpaceCharge.h" #include "GPU/TPCFastTransform.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #endif -o2::tpc::SpaceCharge* sc = 0; +// define number of bins in r, rphi, z for the lookup tables +// o2::tpc::SpaceCharge<double, nZ, nR, nPhi> +// for valid values see the definitions in TPCSpacechargeLinkDef.h +using SC = o2::tpc::SpaceCharge<double, 129, 129, 180>; +SC* sc = nullptr; + using namespace o2::tpc; using namespace o2::gpu; -void generateTPCCorrectionNTuple() +void generateTPCCorrectionNTuple(const char* path = "InputSCDensityHistograms.root", const char* histoName = "inputSCDensity3D8") { // open file with space-charge density histograms // location: alien:///alice/cern.ch/user/e/ehellbar/TPCSpaceCharge/RUN3/ SCDensityHistograms/InputSCDensityHistograms.root @@ -43,35 +48,30 @@ void generateTPCCorrectionNTuple() // - nPileUpEvents = 0,2,4,6,8,10,12 // - number of events (in units of 1000) contributing to the ion density. 0 = 130000 events, 2 = 2000 events, 4 = 4000 events, etc. // - expected default map with 8000 pileup events: inputSCDensity3D8 - auto mFileSCDensity = std::unique_ptr<TFile>(TFile::Open("InputSCDensityHistograms.root")); + auto mFileSCDensity = std::unique_ptr<TFile>(TFile::Open(path)); if (!mFileSCDensity || !mFileSCDensity->IsOpen()) { std::cout << " input file does not exist!" << std::endl; return; } // get the histogram with the sc density - std::unique_ptr<TH3> mHisSCDensity3D = std::unique_ptr<TH3>((TH3*)mFileSCDensity->Get("inputSCDensity3D8")); + std::unique_ptr<TH3> mHisSCDensity3D = std::unique_ptr<TH3>((TH3*)mFileSCDensity->Get(histoName)); if (!mHisSCDensity3D) { - std::cout << "inputSCDensity3D8 histogramm does not exist!" << std::endl; + std::cout << Form("%s histogramm does not exist!", histoName) << std::endl; return; } - // define number of bins in r, rphi, z for the lookup tables - // nR annd nZ have to be 2^n + 1, nPhi is arbitrary - // expected default for desired precision: 129, 129, 144 (takes a lot of time, you can also us 65 in r and z) - int mNRRows = 129; - int mNZCols = 129; - int mNPhiSlices = 180; - // create space-charge object - sc = new o2::tpc::SpaceCharge(mNRRows, mNPhiSlices, mNZCols); - sc->setInitialSpaceChargeDensity((TH3*)mHisSCDensity3D.get()); + sc = new SC; + sc->setGlobalDistType(SC::GlobalDistType::None); + sc->fillChargeDensityFromHisto(*mHisSCDensity3D.get()); // select constant distortions (over time), realistic distortions changing in time not yet pushed to official code - sc->setSCDistortionType(o2::tpc::SpaceCharge::SCDistortionType::SCDistortionsConstant); + sc->setSCDistortionType(SC::SCDistortionType::SCDistortionsConstant); // gas parameters nor Ne-CO2-N2 90-10-5 sc->setOmegaTauT1T2(0.32, 1, 1); // start calculation of lookup tables (takes some time) - sc->calculateLookupTables(); + sc->calculateDistortionsCorrections(Side::A); + sc->calculateDistortionsCorrections(Side::C); // create TPC transformation to get the TPC geometry diff --git a/GPU/TPCFastTransformation/test/testSplines.cxx b/GPU/TPCFastTransformation/test/testSplines.cxx index 62cbcadb218a2..306cfc1099542 100644 --- a/GPU/TPCFastTransformation/test/testSplines.cxx +++ b/GPU/TPCFastTransformation/test/testSplines.cxx @@ -19,22 +19,17 @@ #include "Spline1D.h" #include "Spline2D.h" -namespace o2 -{ -namespace gpu +namespace o2::gpu { /// @brief Basic test if we can create the interface BOOST_AUTO_TEST_CASE(Spline_test1) { - o2::gpu::Spline1D<float> s1; - int err1 = s1.test(0); + int err1 = o2::gpu::Spline1D<float>::test(0); BOOST_CHECK_MESSAGE(err1 == 0, "test of GPU/TPCFastTransform/Spline1D failed with the error code " << err1); - o2::gpu::Spline2D<float, 1> s2; - int err2 = s2.test(0); + int err2 = o2::gpu::Spline2D<float>::test(0); BOOST_CHECK_MESSAGE(err2 == 0, "test of GPU/TPCFastTransform/Spline2D failed with the error code " << err2); } -} // namespace gpu -} // namespace o2 +} // namespace o2::gpu diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.cxx b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.cxx deleted file mode 100644 index 9fbfa42db6491..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.cxx +++ /dev/null @@ -1,533 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPC3DCylindricalInterpolator.cxx -/// \brief Interpolator for cylindrical coordinate -/// this class provides: cubic spline, quadratic and linear interpolation -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#include "TMath.h" -#include "AliTPC3DCylindricalInterpolator.h" - -/// \cond CLASSIMP -ClassImp(AliTPC3DCylindricalInterpolator); -/// \endcond - -/// constructor -/// -AliTPC3DCylindricalInterpolator::AliTPC3DCylindricalInterpolator() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; - fIsInitCubic = kFALSE; -} - -/// destructor -/// -AliTPC3DCylindricalInterpolator::~AliTPC3DCylindricalInterpolator() -{ - delete fValue; - delete fRList; - delete fPhiList; - delete fZList; - if (fIsInitCubic) { - delete fSecondDerZ; - } -} - -/// Get interpolation value on a point in a cylindrical volume -/// -/// \param r position r -/// \param phi position $\phi$ -/// \param z position z -/// -/// \return interpolation value -Double_t AliTPC3DCylindricalInterpolator::GetValue(Double_t r, Double_t phi, Double_t z) const -{ - return InterpolateCylindrical(r, z, phi); -} - -/// Get interpolation value on a point in a cylindrical volume -/// -/// \param r Double_t position r -/// \param phi Double_t position $\phi$ -/// \param z Double_t position z -/// -/// \return interpolation value -Double_t AliTPC3DCylindricalInterpolator::InterpolateCylindrical(Double_t r, Double_t z, Double_t phi) const -{ - Int_t iLow = 0, jLow = 0, m = 0; - Int_t kLow = 0; - Int_t index; - - // tri cubic points - Double_t saveArray[fOrder + 1]; - Double_t savedArray[fOrder + 1]; - Double_t zListM1[fOrder + 1]; - Double_t valueM1[fOrder + 1]; - - Bool_t neg = kFALSE; - - // check phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - // search lowest index related to r,z and phi - Search(fNR, fRList, r, iLow); - Search(fNZ, fZList, z, jLow); - Search(fNPhi, fPhiList, phi, kLow); - - // order >= 3 - kLow -= (fOrder / 2); - iLow -= (fOrder / 2); - jLow -= (fOrder / 2); - - // check if out of range - if (iLow < 0) { - iLow = 0; - } - if (jLow < 0) { - jLow = 0; - } - if (kLow < 0) { - kLow = fNPhi + kLow; - } - // check if out of range - if (iLow + fOrder >= fNR - 1) { - iLow = fNR - 1 - fOrder; - } - if (jLow + fOrder >= fNZ - 1) { - jLow = fNZ - 1 - fOrder; - } - - // do for each - for (Int_t k = 0; k < fOrder + 1; k++) { - m = (kLow + k) % fNPhi; - // interpolate - for (Int_t i = iLow; i < iLow + fOrder + 1; i++) { - if (fOrder < 3) { - if (jLow >= 0) { - index = m * (fNZ * fNR) + i * (fNZ) + jLow; - saveArray[i - iLow] = Interpolate(&fZList[jLow], &fValue[index], z); - } else { - index = m * (fNZ * fNR) + i * (fNZ); - zListM1[0] = fZList[0] - (fZList[1] - fZList[0]); - zListM1[1] = fZList[0]; - zListM1[2] = fZList[1]; - valueM1[0] = fValue[index] - (fValue[index + 1] - fValue[index]); - valueM1[1] = fValue[index]; - valueM1[2] = fValue[index + 1]; - saveArray[i - iLow] = Interpolate(&zListM1[0], &valueM1[0], z); - } - - } else { - index = m * (fNZ * fNR) + i * (fNZ); - saveArray[i - iLow] = InterpolateCubicSpline(fZList, &fValue[index], &fSecondDerZ[index], fNZ, fNZ, fNZ, - z, 1); - } - } - savedArray[k] = Interpolate(&fRList[iLow], saveArray, r); - } - return (InterpolatePhi(&fPhiList[0], kLow, fNPhi, savedArray, phi)); -} - -/// Get interpolation for 1 dimension non cyclic -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param x unknown position -/// -/// \return interpolation value f(x) -Double_t AliTPC3DCylindricalInterpolator::Interpolate(Double_t xArray[], Double_t yArray[], Double_t x) const -{ - Double_t y; - - // if cubic spline - if (fOrder > 2) { - Double_t y2Array[fOrder + 1]; - InitCubicSpline(xArray, yArray, fOrder + 1, y2Array, 1); - y = InterpolateCubicSpline(xArray, yArray, y2Array, fOrder + 1, fOrder + 1, fOrder + 1, x, 1); - } else if (fOrder == 2) { - // Quadratic Interpolation = 2 - y = (x - xArray[1]) * (x - xArray[2]) * yArray[0] / ((xArray[0] - xArray[1]) * (xArray[0] - xArray[2])); - y += (x - xArray[2]) * (x - xArray[0]) * yArray[1] / ((xArray[1] - xArray[2]) * (xArray[1] - xArray[0])); - y += (x - xArray[0]) * (x - xArray[1]) * yArray[2] / ((xArray[2] - xArray[0]) * (xArray[2] - xArray[1])); - } else { - // Linear Interpolation = 1 - y = yArray[0] + (yArray[1] - yArray[0]) * (x - xArray[0]) / (xArray[1] - xArray[0]); - } - return (y); -} - -/// Get interpolation for 1 dimension cyclic -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param x unknown position -/// -/// \return interpolation value f(x) -Double_t AliTPC3DCylindricalInterpolator::InterpolatePhi( - Double_t xArray[], const Int_t iLow, const Int_t lenX, Double_t yArray[], Double_t x) const -{ - Int_t i0 = iLow; - Double_t xi0 = xArray[iLow]; - Int_t i1 = (iLow + 1) % lenX; - Double_t xi1 = xArray[i1]; - Int_t i2 = (iLow + 2) % lenX; - Double_t xi2 = xArray[i2]; - - if (fOrder <= 2) { - if (xi1 < xi0) { - xi1 = TMath::TwoPi() + xi1; - } - if (xi2 < xi1) { - xi2 = TMath::TwoPi() + xi2; - } - if (x < xi0) { - x = TMath::TwoPi() + x; - } - } - - Double_t y; - if (fOrder > 2) { - Double_t y2Array[fOrder + 1]; - Double_t xArrayTemp[fOrder + 1]; - Double_t dPhi = xArray[1] - xArray[0]; - // make list phi ascending order - for (Int_t i = 0; i < fOrder + 1; i++) { - xArrayTemp[i] = xArray[iLow] + (dPhi * i); - } - if (x < xArrayTemp[0]) { - x = TMath::TwoPi() + x; - } - if (x < xArrayTemp[0] || x > xArrayTemp[fOrder]) { - printf("x (%f) is outside of interpolation box (%f,%f)\n", x, xArrayTemp[0], xArrayTemp[fOrder]); - } - - InitCubicSpline(xArrayTemp, yArray, fOrder + 1, y2Array, 1); - y = InterpolateCubicSpline(xArrayTemp, yArray, y2Array, fOrder + 1, fOrder + 1, fOrder + 1, x, 1); - } else if (fOrder == 2) { // Quadratic Interpolation = 2 - y = (x - xi1) * (x - xi2) * yArray[0] / ((xi0 - xi1) * (xi0 - xi2)); - y += (x - xi2) * (x - xi0) * yArray[1] / ((xi1 - xi2) * (xi1 - xi0)); - y += (x - xi0) * (x - xi1) * yArray[2] / ((xi2 - xi0) * (xi2 - xi1)); - } else { // Li2near Interpolation = 1 - y = yArray[0] + (yArray[1] - yArray[0]) * (x - xi0) / (xi1 - xi0); - } - return (y); -} - -/// Solving cubic splines for system of splines -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param n Int_t length of splines -/// \param y2Array Double_t[] calculated $d^2Y$ spline (output) -/// \param skip memory offset for xArray -/// -void AliTPC3DCylindricalInterpolator::InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, - const Int_t skip) const -{ - Double_t u[n]; - Double_t sig, p, qn, un; - - y2Array[0] = 0.0; - u[0] = 0.0; //natural condition - - for (Int_t i = 1; i <= n - 2; i++) { - sig = (xArray[i] - xArray[i - 1]) / (xArray[i + 1] - xArray[i - 1]); - p = sig * y2Array[(i - 1) * skip] + 2.0; - y2Array[i * skip] = (sig - 1.0) / p; - u[i] = (yArray[(i + 1) * skip] - yArray[i * skip]) / (xArray[i + 1] - xArray[i]) - - (yArray[i * skip] - yArray[(i - 1) * skip]) / (xArray[i] - xArray[i - 1]); - u[i] = (6.0 * u[i] / (xArray[i + 1] - xArray[i - 1]) - sig * u[i - 1]) / p; - } - - qn = un = 0.0; - - y2Array[(n - 1) * skip] = (un - qn * u[n - 2]) / (qn * y2Array[(n - 2) * skip] + 1.0); - for (Int_t k = n - 2; k >= 0; k--) { - y2Array[k * skip] = y2Array[k * skip] * y2Array[(k + 1) * skip] + u[k]; - } -} - -/// Solving cubic splines for system of splines -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param n Int_t length of splines -/// \param y2Array Double_t[] calculated $d^2Y$ spline (output) -/// \param skip memory offset for xArray -/// -void AliTPC3DCylindricalInterpolator::InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, - const Int_t skip, Double_t yp0, Double_t ypn1) const -{ - Double_t u[n]; - Double_t sig, p, qn, un; - - y2Array[0] = 0.0; - u[0] = 0.0; //natural condition - - for (Int_t i = 1; i <= n - 2; i++) { - sig = (xArray[i] - xArray[i - 1]) / (xArray[i + 1] - xArray[i - 1]); - p = sig * y2Array[(i - 1) * skip] + 2.0; - y2Array[i * skip] = (sig - 1.0) / p; - u[i] = (yArray[(i + 1) * skip] - yArray[i * skip]) / (xArray[i + 1] - xArray[i]) - - (yArray[i * skip] - yArray[(i - 1) * skip]) / (xArray[i] - xArray[i - 1]); - u[i] = (6.0 * u[i] / (xArray[i + 1] - xArray[i - 1]) - sig * u[i - 1]) / p; - } - - qn = un = 0.0; - y2Array[(n - 1) * skip] = (un - qn * u[n - 2]) / (qn * y2Array[(n - 2) * skip] + 1.0); - for (Int_t k = n - 2; k >= 0; k--) { - y2Array[k * skip] = y2Array[k * skip] * y2Array[(k + 1) * skip] + u[k]; - } -} - -/// Interpolate initialized cubic spline -/// -/// \param xArray -/// \param yArray -/// \param y2Array -/// \param nxArray -/// \param nyArray -/// \param ny2Array -/// \param x -/// \param skip -/// \return -Double_t AliTPC3DCylindricalInterpolator::InterpolateCubicSpline(Double_t* xArray, Double_t* yArray, Double_t* y2Array, - const Int_t nxArray, const Int_t nyArray, - const Int_t ny2Array, Double_t x, Int_t skip) const -{ - Int_t klo, khi, k; - Float_t h, b, a; - klo = 0; - khi = nxArray - 1; - - while (khi - klo > 1) { - k = (khi + klo) >> 1; - if (xArray[k] > x) { - khi = k; - } else { - klo = k; - } - } - - h = xArray[khi] - xArray[klo]; - - if (TMath::Abs(h) < 1e-10) { - return 0.0; - } - - a = (xArray[khi] - x) / h; - b = (x - xArray[klo]) / h; - - Double_t y = a * yArray[klo] + b * yArray[khi] + - ((a * a * a - a) * y2Array[klo * skip] + (b * b * b - b) * y2Array[khi * skip]) * (h * h) / 6.0; - - return y; -} - -/// init cubic spline for all -/// -void AliTPC3DCylindricalInterpolator::InitCubicSpline() -{ - - Double_t yp0, ypn1; - if (fIsInitCubic != kTRUE) { - fSecondDerZ = new Double_t[fNR * fNZ * fNPhi]; - - // Init at Z direction - for (Int_t m = 0; m < fNPhi; m++) { - for (Int_t i = 0; i < fNR; i++) { - yp0 = (-(11.0 / 6.0) * fValue[(m * (fNZ * fNR) + i * fNZ)] + - (3.0 * fValue[(m * (fNZ * fNR) + i * fNZ) + 1]) - - (1.5 * fValue[(m * (fNZ * fNR) + i * fNZ) + 2]) + - ((1.0 / 3.0) * fValue[(m * (fNZ * fNR) + i * fNZ) + 4])) / - (fZList[1] - fZList[0]); - ypn1 = (-(11.0 / 6.0) * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 1)] + - (3.0 * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 2)]) - - (1.5 * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 3)]) + - ((1.0 / 3.0) * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 4)])) / - (fZList[0] - fZList[1]); - InitCubicSpline(fZList, &fValue[m * (fNZ * fNR) + i * fNZ], fNZ, - &fSecondDerZ[m * (fNZ * fNR) + i * fNZ], 1); - } - } - - fIsInitCubic = kTRUE; - } -} - -/// Search the nearest grid index position to a Point -/// -/// \param n -/// \param xArray -/// \param x -/// \param low -void AliTPC3DCylindricalInterpolator::Search(Int_t n, const Double_t xArray[], Double_t x, Int_t& low) const -{ - /// Search an ordered table by starting at the most recently used point - - Long_t middle, high; - Int_t ascend = 0, increment = 1; - - if (xArray[n - 1] > xArray[0]) { - ascend = 1; // Ascending ordered table if true - } - if (low < 0 || low > n - 1) { - low = -1; - high = n; - } else { // Ordered Search phase - if ((Int_t)(x > xArray[low]) == ascend) { - if (low == n - 1) { - return; - } - high = low + 1; - while ((Int_t)(x > xArray[high]) == ascend) { - low = high; - increment *= 2; - high = low + increment; - if (high > n - 1) { - high = n; - break; - } - } - } else { - if (low == 0) { - low = -1; - return; - } - high = low - 1; - while ((Int_t)(x < xArray[low]) == ascend) { - high = low; - increment *= 2; - if (increment >= high) { - low = -1; - break; - } else { - low = high - increment; - } - } - } - } - - while ((high - low) != 1) { // Binary Search Phase - middle = (high + low) / 2; - if ((Int_t)(x > xArray[middle]) == ascend) { - low = middle; - } else { - high = middle; - } - } - - if (x > xArray[n - 1]) { - low = n; - } - if (x < xArray[0]) { - low = -1; - } -} - -/// Set the value as interpolation point -/// -/// \param matricesVal TMatrixD** reference value for each point -void AliTPC3DCylindricalInterpolator::SetValue(TMatrixD** matricesVal) -{ - Int_t indexVal1D; - Int_t index1D; - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fIsAllocatingLookUp = kTRUE; - } - for (Int_t m = 0; m < fNPhi; m++) { - indexVal1D = m * fNR * fNZ; - TMatrixD* mat = matricesVal[m]; - for (Int_t i = 0; i < fNR; i++) { - index1D = indexVal1D + i * fNZ; - for (Int_t j = 0; j < fNZ; j++) { - fValue[index1D + j] = (*mat)(i, j); - } - } - } -} - -/// Set the value as interpolation point -/// -/// \param matricesVal TMatrixD** reference value for each point -void AliTPC3DCylindricalInterpolator::SetValue(TMatrixD** matricesVal, Int_t iZ) -{ - Int_t indexVal1D; - Int_t index1D; - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fIsAllocatingLookUp = kTRUE; - } - for (Int_t m = 0; m < fNPhi; m++) { - indexVal1D = m * fNR * fNZ; - TMatrixD* mat = matricesVal[m]; - for (Int_t i = 0; i < fNR; i++) { - index1D = indexVal1D + i * fNZ; - fValue[index1D + iZ] = (*mat)(i, iZ); - } - } -} - -/// set the position of R -/// -/// \param rList -void AliTPC3DCylindricalInterpolator::SetRList(Double_t* rList) -{ - fRList = new Double_t[fNR]; - for (Int_t i = 0; i < fNR; i++) { - fRList[i] = rList[i]; - } -} - -/// set the position of phi -/// -/// \param phiList -void AliTPC3DCylindricalInterpolator::SetPhiList(Double_t* phiList) -{ - fPhiList = new Double_t[fNPhi]; - for (Int_t i = 0; i < fNPhi; i++) { - fPhiList[i] = phiList[i]; - } -} - -/// Setting z position -/// -/// \param zList -void AliTPC3DCylindricalInterpolator::SetZList(Double_t* zList) -{ - fZList = new Double_t[fNZ]; - for (Int_t i = 0; i < fNZ; i++) { - fZList[i] = zList[i]; - } -} - -/// Setting values from 1D -/// -/// \param valueList -void AliTPC3DCylindricalInterpolator::SetValue(Double_t* valueList) { fValue = valueList; } - -/// Set number of total grid points -void AliTPC3DCylindricalInterpolator::SetNGridPoints() -{ - if (fNR == 0 || fNPhi == 0 || fNZ == 0) { - Error("AliTPC3DCylindricalInterpolator::SetNGridPoints", "Error in calculating total number of grid points! Either nR, nPhi or nZ are zero!"); - } - fNGridPoints = fNR * fNPhi * fNZ; -} \ No newline at end of file diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.h b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.h deleted file mode 100644 index 67e01e640dbd4..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.h +++ /dev/null @@ -1,80 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPC3DCylindricalInterpolator.h -/// \brief Interpolator for cylindrical coordinate -/// this class provides: cubic spline, quadratic and linear interpolation -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#ifndef AliTPC3DCylindricalInterpolator_H -#define AliTPC3DCylindricalInterpolator_H - -#include <TMatrixD.h> - -class AliTPC3DCylindricalInterpolator -{ - public: - AliTPC3DCylindricalInterpolator(); - ~AliTPC3DCylindricalInterpolator(); - Double_t GetValue(Double_t r, Double_t phi, Double_t z) const; - void InitCubicSpline(); - void SetOrder(Int_t order) { fOrder = order; } - void SetNR(Int_t nR) { fNR = nR; } - void SetNPhi(Int_t nPhi) { fNPhi = nPhi; } - void SetNZ(Int_t nZ) { fNZ = nZ; } - void SetNGridPoints(); - void SetRList(Double_t* rList); - void SetPhiList(Double_t* phiList); - void SetZList(Double_t* zList); - void SetValue(Double_t* vList); - void SetValue(TMatrixD** vList); - void SetValue(TMatrixD** vList, Int_t iZ); - - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - Int_t GetOrder() { return fOrder; } - - Double_t* GetSecondDerZ() { return fSecondDerZ; } - - private: - Int_t fOrder; ///< Order of interpolation, 1 - linear, 2 - quadratic, 3 >= - cubic, - Int_t fNR; ///< Grid size in direction of R - Int_t fNPhi; ///< Grid size in direction of Phi - Int_t fNZ; ///< Grid size in direction of Z - Int_t fNGridPoints; ///< Total number of grid points (needed for streamer) - - Double_t* fValue = nullptr; //[fNGridPoints] Description 3D for storing known values interpolation should be in size fNR*fNPhi*fNZ - Double_t* fRList = nullptr; //[fNR] coordinate in R (cm) (should be increasing) - Double_t* fPhiList = nullptr; //[fNPhi] coordinate in phiList (rad) (should be increasing) 0 <= < 2 pi (cyclic) - Double_t* fZList = nullptr; //[fNZ] coordinate in z list (cm) (should be increasing) - Double_t* fSecondDerZ = nullptr; //[fNGridPoints] store second derivative of cubic interpolation in z direction - - Bool_t fIsAllocatingLookUp; ///< is allocating memory - Bool_t fIsInitCubic; ///< is cubic second derivative already been initialized - - Double_t InterpolatePhi(Double_t xArray[], const Int_t iLow, const Int_t lenX, Double_t yArray[], Double_t x) const; - Double_t InterpolateCylindrical(Double_t r, Double_t z, Double_t phi) const; - Double_t Interpolate(Double_t xArray[], Double_t yArray[], Double_t x) const; - Double_t InterpolateCubicSpline(Double_t* xArray, Double_t* yArray, Double_t* y2Array, const Int_t nxArray, - const Int_t nyArray, const Int_t ny2Array, Double_t x, const Int_t skip) const; - void Search(Int_t n, const Double_t xArray[], Double_t x, Int_t& low) const; - void InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, const Int_t skip) const; - void InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, const Int_t skip, - Double_t yp0, Double_t ypn1) const; - - /// \cond CLASSIMP - ClassDefNV(AliTPC3DCylindricalInterpolator, 1); - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.cxx b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.cxx deleted file mode 100644 index b4d395ceadbaa..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.cxx +++ /dev/null @@ -1,1514 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPC3DCylindricalInterpolatorIrregular.cxx -/// \brief Irregular grid interpolator for cylindrical coordinate with r,phi,z different coordinates -/// RBF-based interpolation -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#include "TMath.h" -#include "TVector.h" -#include "TVectorD.h" -#include "TMatrix.h" -#include "TMatrixD.h" -#include "TDecompSVD.h" -#include "AliTPCPoissonSolver.h" -#include "AliTPC3DCylindricalInterpolatorIrregular.h" -#include <cstdlib> - -/// \cond CLASSIMP3 -ClassImp(AliTPC3DCylindricalInterpolatorIrregular); -/// \endcond - -/// constructor -/// -/// \param nRRow -/// \param nZColumn -/// \param nPhiSlice -/// \param rStep -/// \param zStep -/// \param phiStep -/// \param type -AliTPC3DCylindricalInterpolatorIrregular::AliTPC3DCylindricalInterpolatorIrregular( - Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, Int_t rStep, Int_t zStep, Int_t phiStep, Int_t type) -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; - fMinZIndex = 0; - fNR = nRRow; - fNZ = nZColumn; - fNPhi = nPhiSlice; - fNGridPoints = nRRow * nZColumn * nPhiSlice; - - fRBFWeightLookUp = new Int_t[nRRow * nZColumn * nPhiSlice]; - - Int_t nd = rStep * zStep * phiStep; - fStepR = rStep; - fStepZ = zStep; - fStepPhi = phiStep; - fNRBFpoints = nRRow * nZColumn * nPhiSlice * nd; - - fType = type; - fRBFWeight = new Double_t[nRRow * nZColumn * nPhiSlice * nd]; - for (Int_t i = 0; i < nRRow * nZColumn * nPhiSlice; i++) { - fRBFWeightLookUp[i] = 0; - } - - SetKernelType(kRBFInverseMultiQuadratic); -} - -/// constructor -/// -AliTPC3DCylindricalInterpolatorIrregular::AliTPC3DCylindricalInterpolatorIrregular() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; - - fMinZIndex = 0; -} - -/// destructor -/// -AliTPC3DCylindricalInterpolatorIrregular::~AliTPC3DCylindricalInterpolatorIrregular() -{ - - delete fValue; - delete fRList; - delete fPhiList; - delete fZList; - - if (fKDTreeIrregularPoints) { - delete[] fKDTreeIrregularPoints; - delete fKDTreeIrregularRoot; - } - delete[] fRBFWeightLookUp; - delete[] fRBFWeight; -} - -/// irregular grid interpolation with IDW (inverse distance weight) -/// -/// \param r -/// \param z -/// \param phi -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \return -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Interpolate3DTableCylIDW( - Double_t r, Double_t z, Double_t phi, Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, - Int_t zStep) -{ - Double_t r0, z0, phi0, d; - Double_t MIN_DIST = 1e-3; - Double_t val = 0.0; - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - Int_t index; - Double_t sum_w = 0.0; - Double_t sum_d = 0.0; - Double_t shortest_d = 10000.0; - Int_t new_rIndex = 0; - Int_t new_zIndex = 0; - Int_t new_phiIndex = 0; - - for (Int_t iPhi = startPhi; iPhi < startPhi + phiStep; iPhi++) { - indexPhi = iPhi % fNPhi; - for (Int_t index_r = startR; index_r < startR + rStep; index_r++) { - for (Int_t index_z = startZ; index_z < startZ + zStep; index_z++) { - // check for the closest poInt_t - index = indexPhi * (fNZ * fNR) + index_r * fNZ + index_z; - - r0 = fRList[index]; - z0 = fZList[index]; - phi0 = fPhiList[index]; - - d = Distance(r0, phi0, z0, r, phi, z); - if (d < shortest_d) { - shortest_d = d; - new_rIndex = index_r; - new_phiIndex = indexPhi; - new_zIndex = index_z; - } - } - } - } - - phiStep = 3; - rStep = 3; - startPhi = new_phiIndex - phiStep / 2; - startR = new_rIndex - rStep / 2; - startZ = new_zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - for (Int_t iPhi = startPhi; iPhi < startPhi + phiStep; iPhi++) { - indexPhi = iPhi % fNPhi; - for (Int_t index_r = startR; index_r < startR + rStep; index_r++) { - for (Int_t index_z = startZ; index_z < startZ + zStep; index_z++) { - // check for the closest poInt_t - index = indexPhi * (fNZ * fNR) + index_r * fNZ + index_z; - - r0 = fRList[indexPhi * (fNR * fNZ) + index_r * fNZ + index_z]; - z0 = fZList[indexPhi * (fNR * fNZ) + index_r * fNZ + index_z]; - phi0 = fPhiList[indexPhi * (fNR * fNZ) + index_r * fNZ + index_z]; - d = Distance(r0, phi0, z0, r, phi, z); - if (d < MIN_DIST) { - return fValue[index]; - } - d = 1.0 / d; - sum_w += (fValue[index] * d * d * d * d); - sum_d += d * d * d * d; - } - } - } - return (sum_w / sum_d); -} - -/// distance in Cyl coordinate -/// -/// \param r0 -/// \param phi0 -/// \param z0 -/// \param r -/// \param phi -/// \param z -/// \return -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Distance(Double_t r0, Double_t phi0, Double_t z0, Double_t r, Double_t phi, - Double_t z) -{ - if (phi < 0) { - phi = TMath::TwoPi() + phi; - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - if (phi0 < 0) { - phi0 = TMath::TwoPi() + phi0; - } - if (phi0 > TMath::TwoPi()) { - phi0 = phi0 - TMath::TwoPi(); - } - - Double_t dPhi = phi - phi0; - if (dPhi > TMath::Pi()) { - dPhi = TMath::TwoPi() - dPhi; - } - if (dPhi < -TMath::Pi()) { - dPhi = TMath::TwoPi() + dPhi; - } - - Double_t ret = r * r + r0 * r0 - 2 * r0 * r * TMath::Cos(dPhi) + (z - z0) * (z - z0); - - return TMath::Sqrt(ret); -} - -/// main operation -/// interpolation by RBF -/// -/// \param r -/// \param z -/// \param phi -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radiusRBF0 -/// \return -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Interpolate3DTableCylRBF( - Double_t r, Double_t z, Double_t phi, Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, - Int_t zStep, Double_t radiusRBF0) -{ - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNR - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZ - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhi; - Double_t r0, z0, phi0, d; - Double_t MIN_DIST = 1e-3; - Double_t val = 0.0; - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - Int_t index; - Double_t sum_w = 0.0; - Double_t sum_d = 0.0; - Double_t shortest_d = 10000.0; - Int_t new_rIndex = 0; - Int_t new_zIndex = 0; - Int_t new_phiIndex = 0; - - for (Int_t iPhi = startPhi; iPhi < startPhi + phiStep; iPhi++) { - indexPhi = iPhi % fNPhi; - for (Int_t index_r = startR; index_r < startR + rStep; index_r++) { - for (Int_t index_z = startZ; index_z < startZ + zStep; index_z++) { - // check for the closest poInt_t - index = indexPhi * (fNZ * fNR) + index_r * fNZ + index_z; - - r0 = fRList[index]; - z0 = fZList[index]; - phi0 = fPhiList[index]; - - d = Distance(r0, phi0, z0, r, phi, z); - if (d < shortest_d) { - shortest_d = d; - new_rIndex = index_r; - new_phiIndex = indexPhi; - new_zIndex = index_z; - } - } - } - } - - index = new_phiIndex * (fNZ * fNR) + new_rIndex * fNZ + new_zIndex; - phiStep = fStepPhi; - rStep = fStepR; - zStep = fStepZ; - startPhi = new_phiIndex - phiStep / 2; - - startR = new_rIndex - rStep / 2; - startZ = new_zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - Double_t* w; - - //Int_t nd = (phiStep-1) + (rStep-1) + (zStep-1) + 1; - Int_t nd = phiStep * rStep * zStep; - - w = new Double_t[nd]; - - Float_t minTemp, minTemp2; - - radiusRBF0 = GetRadius0RBF(new_rIndex, new_phiIndex, new_zIndex); - - if (fType == 1) { - - for (Int_t i = 0; i < nd; i++) { - w[i] = 0.0; - } - GetRBFWeight(new_rIndex, new_zIndex, new_phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBF(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } else { - GetRBFWeightHalf(new_rIndex, new_zIndex, new_phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBFHalf(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } - delete[] w; - return val; -} - -/// Search nearest point at grid -/// \param n -/// \param xArray -/// \param offset -/// \param x -/// \param low -void AliTPC3DCylindricalInterpolatorIrregular::Search(Int_t n, Double_t* xArray, Int_t offset, Double_t x, Int_t& low) -{ - /// Search an ordered table by starting at the most recently used poInt_t - - Long_t middle, high; - Int_t ascend = 0, increment = 1; - - if (xArray[(n - 1) * offset] >= xArray[0 * offset]) { - ascend = 1; // Ascending ordered table if true - } - if (low < 0 || low > n - 1) { - low = -1; - high = n; - } else { // Ordered Search phase - if ((Int_t)(x >= xArray[low * offset]) == ascend) { - if (low == n - 1) { - return; - } - high = low + 1; - while ((Int_t)(x >= xArray[high * offset]) == ascend) { - low = high; - increment *= 2; - high = low + increment; - if (high > n - 1) { - high = n; - break; - } - } - } else { - if (low == 0) { - low = -1; - return; - } - high = low - 1; - while ((Int_t)(x < xArray[low * offset]) == ascend) { - high = low; - increment *= 2; - if (increment >= high) { - low = -1; - break; - } else { - low = high - increment; - } - } - } - } - - while ((high - low) != 1) { // Binary Search Phase - middle = (high + low) / 2; - if ((Int_t)(x >= xArray[middle * offset]) == ascend) { - low = middle; - } else { - high = middle; - } - } - - if (x > xArray[n - 1]) { - low = n; - } - if (x < xArray[0]) { - low = -1; - } -} - -/// get value, interpolation with RBF -/// -/// \param r -/// \param phi -/// \param z -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::GetValue( - Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t rStep, Int_t phiStep, - Int_t zStep) -{ - - fMinZIndex = 0; - return Interpolate3DTableCylRBF(r, z, phi, rIndex, zIndex, phiIndex, rStep, phiStep, zStep, 0.0); -} - -/// get value -/// -/// \param r -/// \param phi -/// \param z -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param minZColumnIndex -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::GetValue( - Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t rStep, Int_t phiStep, - Int_t zStep, Int_t minZColumnIndex) -{ - fMinZIndex = minZColumnIndex; - return Interpolate3DTableCylRBF(r, z, phi, rIndex, zIndex, phiIndex, rStep, phiStep, zStep, 0.0); -} - -// GetValue using searching at KDTree -Double_t AliTPC3DCylindricalInterpolatorIrregular::GetValue( - Double_t r, Double_t phi, Double_t z) -{ - - KDTreeNode n; - n.pR = &r; - n.pPhi = φ - n.pZ = &z; - KDTreeNode* nearest; - Double_t dist; - dist = 100000000.0; - Int_t startIndex = 0; // Z - Int_t dim = 3; // dimenstion - KDTreeNearest(fKDTreeIrregularRoot, &n, startIndex, dim, &nearest, &dist); - return Interpolate3DTableCylRBF(r, z, phi, nearest); -} -/// Set value and distorted point for irregular grid interpolation -/// -/// \param matrixRicesValue -/// \param matrixRicesRPoint -/// \param matrixRicesPhiPoint -/// \param matrixRicesZPoint -void AliTPC3DCylindricalInterpolatorIrregular::SetValue( - TMatrixD** matrixRicesValue, TMatrixD** matrixRicesRPoint, TMatrixD** matrixRicesPhiPoint, - TMatrixD** matrixRicesZPoint) -{ - Int_t indexInner; - Int_t index; - - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fRList = new Double_t[fNPhi * fNR * fNZ]; - fPhiList = new Double_t[fNPhi * fNR * fNZ]; - fZList = new Double_t[fNPhi * fNR * fNZ]; - fIsAllocatingLookUp = kTRUE; - } - - for (Int_t m = 0; m < fNPhi; m++) { - indexInner = m * fNR * fNZ; - TMatrixD* mat = matrixRicesValue[m]; - TMatrixD* matrixR = matrixRicesRPoint[m]; - TMatrixD* matrixPhi = matrixRicesPhiPoint[m]; - TMatrixD* matrixZ = matrixRicesZPoint[m]; - - for (Int_t i = 0; i < fNR; i++) { - index = indexInner + i * fNZ; - for (Int_t j = 0; j < fNZ; j++) { - fValue[index + j] = (*mat)(i, j); - - fRList[index + j] = (*matrixR)(i, j); - fPhiList[index + j] = (*matrixPhi)(i, j); - fZList[index + j] = (*matrixZ)(i, j); - } - } - } - // KD Tree is used for look-up a point to irregular grid to find - // closest neughboor point - InitKDTree(); - InitRBFWeight(); -} - -/// init RBF Weights assume value already been set -/// -void AliTPC3DCylindricalInterpolatorIrregular::InitRBFWeight() -{ - - Int_t indexInner; - Int_t rIndex; - Int_t index; - Int_t startR; - Int_t nd; - - const Double_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNR - 1); - const Double_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZ - 1); - const Double_t gridSizePhi = TMath::TwoPi() / fNPhi; - - Float_t r0; - Double_t radiusRBF0, minTemp, minTemp2; - - nd = fStepR * fStepPhi * fStepZ; - for (Int_t m = 0; m < fNPhi; m++) { - indexInner = m * fNR * fNZ; - for (Int_t i = 0; i < fNR; i++) { - rIndex = indexInner + i * fNZ; - - startR = i - fStepR / 2; - - if (startR < 0) { - startR = 0; - } - if (startR + fStepR >= fNR) { - startR = fNR - fStepR; - } - - for (Int_t j = 0; j < fNZ; j++) { - index = rIndex + j; - - radiusRBF0 = GetRadius0RBF(i, j, m); - - RBFWeight( - i, - j, - m, - fStepR, - fStepPhi, - fStepZ, - radiusRBF0, - fKernelType, - &fRBFWeight[index * nd]); - fRBFWeightLookUp[index] = 1; - } - } - } -} - -/// Set value and distorted Point -/// -/// \param matrixRicesValue -/// \param matrixRicesRPoint -/// \param matrixRicesPhiPoint -/// \param matrixRicesZPoint -/// \param jy -void AliTPC3DCylindricalInterpolatorIrregular::SetValue( - TMatrixD** matrixRicesValue, TMatrixD** matrixRicesRPoint, TMatrixD** matrixRicesPhiPoint, - TMatrixD** matrixRicesZPoint, - Int_t jy) -{ - Int_t indexInner; - Int_t index; - - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fRList = new Double_t[fNPhi * fNR * fNZ]; - fPhiList = new Double_t[fNPhi * fNR * fNZ]; - fZList = new Double_t[fNPhi * fNR * fNZ]; - - fIsAllocatingLookUp = kTRUE; - } - - for (Int_t m = 0; m < fNPhi; m++) { - indexInner = m * fNR * fNZ; - TMatrixD* mat = matrixRicesValue[m]; - TMatrixD* matrixR = matrixRicesRPoint[m]; - TMatrixD* matrixPhi = matrixRicesPhiPoint[m]; - TMatrixD* matrixZ = matrixRicesZPoint[m]; - - for (Int_t i = 0; i < fNR; i++) { - index = indexInner + i * fNZ; - fValue[index + jy] = (*mat)(i, jy); - fRList[index + jy] = (*matrixR)(i, jy); - fPhiList[index + jy] = (*matrixPhi)(i, jy); - fZList[index + jy] = (*matrixZ)(i, jy); - } - } -} - -/// calculate -/// RBFWeight for all points in the interpolation -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::RBFWeight( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - - Double_t* a; - Int_t i; - Int_t j; - Int_t k; - Int_t ii; - Int_t jj; - Int_t kk; - - Int_t index0, index1; - Int_t indexCyl0, indexCyl1; - Double_t* r; - Double_t* v; - - Double_t phi0; - Double_t z0; - Double_t r0; - - Double_t phi1; - Double_t z1; - Double_t r1; - - Int_t nd = rStep * phiStep * zStep; - - a = new Double_t[nd * nd]; - r = new Double_t[nd]; - v = new Double_t[nd]; - - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t indexPhi1; - - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - index0 = 0; - - for (i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (j = startR; j < startR + rStep; j++) { - for (k = startZ; k < startZ + zStep; k++) { - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - indexCyl1 = indexPhi1 * fNR * fNZ + jj * fNZ + kk; - r1 = fRList[indexCyl1]; - z1 = fZList[indexCyl1]; - phi1 = fPhiList[indexCyl1]; - r[index1] = Distance(r0, phi0, z0, r1, phi1, z1); - - index1++; - } - } - } - - Phi(nd, r, radius0, v); - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - a[index0 * nd + index1] = v[index1]; - index1++; - } - } - } - w[index0] = fValue[indexCyl0]; - index0++; - } - } - } - - TMatrixD mat_a; - mat_a.Use(nd, nd, a); - TVectorD vec_w; - vec_w.Use(nd, w); - TDecompSVD svd(mat_a); - - svd.Solve(vec_w); - - delete[] a; - delete[] r; - delete[] v; -} - -/// rbf1 -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::rbf1(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - v[i] = sqrt(1 + (r[i] * r[i] + r0 * r0)); - } - return; -} - -/// rbf2 -/// \param n -/// \param r -/// \param r0 -/// \param v - -void AliTPC3DCylindricalInterpolatorIrregular::rbf2(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - v[i] = 1.0 / sqrt(1 + (r[i] * r[i] + r0 * r0)); - } - return; -} - -/// rbf3 -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::rbf3(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - if (r[i] <= 0.0) { - v[i] = 0.0; - } else { - v[i] = r[i] * r[i] * log(r[i] / r0); - } - } - return; -} - -/// rbf4 -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::rbf4(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - v[i] = TMath::Exp(-0.5 * r[i] * r[i] / (r0 * r0)); - } - return; -} - -// RBF based interpolation -// return interpolated value -/// -/// \param r -/// \param phi -/// \param z -/// \param startR -/// \param startPhi -/// \param startZ -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param weight -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::InterpRBF( - Double_t r, Double_t phi, Double_t z, Int_t startR, Int_t startPhi, Int_t startZ, Int_t rStep, Int_t phiStep, - Int_t zStep, Double_t radius0, Int_t kernelType, Double_t* weight) -{ - Double_t interpVal = 0.0; - Double_t r0, z0, phi0; - Double_t* dList; - Double_t* v; - - Int_t indexCyl0, index0, indexPhi; - - Int_t nd = rStep * phiStep * zStep; - - dList = new Double_t[nd]; - v = new Double_t[nd]; - - index0 = 0; - for (Int_t i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (Int_t j = startR; j < startR + rStep; j++) { - for (Int_t k = startZ; k < startZ + zStep; k++) { - - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - dList[index0] = Distance(r, phi, z, r0, phi0, z0); - index0++; - } - } - } - - Phi(nd, dList, radius0, v); - - TVectorD vec_v; - vec_v.Use(nd, v); - - TVectorD vec_w; - vec_w.Use(nd, weight); - - interpVal = vec_v * vec_w; - delete[] v; - delete[] dList; - return interpVal; -} - -// calculate -// RBFWeight for all points in the interpolation -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::GetRBFWeight( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - - Int_t index = phiIndex * fNR * fNZ + rIndex * fNZ + zIndex; - if (fRBFWeightLookUp[index] == 0) { - RBFWeight(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radius0, kernelType, w); - - fRBFWeightLookUp[index] = 1; - Int_t nd = rStep * zStep * phiStep; - - for (Int_t i = 0; i < nd; i++) { - fRBFWeight[index * nd + i] = w[i]; - } - } else { - - Int_t ndw = rStep * zStep * phiStep; - - Int_t nd = fStepR * fStepZ * fStepPhi; - Int_t indexWeight = phiIndex * fNR * fNZ * nd + rIndex * fNZ * nd + zIndex * nd; - - for (Int_t i = 0; i < nd; i++) { - w[i] = fRBFWeight[indexWeight + i]; - } - } -} - -// calculate -// RBFWeight for all points in the interpolation -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::GetRBFWeightHalf( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - - Int_t index = phiIndex * fNR * fNZ + rIndex * fNZ + zIndex; - - if (fRBFWeightLookUp[index] == 0) { - RBFWeightHalf(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radius0, kernelType, w); - - if ((rStep == fStepR) && (zStep == fStepZ) && (phiStep == fStepPhi) && (zIndex > fMinZIndex + fStepZ)) { - fRBFWeightLookUp[index] = 1; - // copy to lookup - Int_t nd = rStep + zStep + phiStep - 2; - - for (Int_t i = 0; i < nd; i++) { - fRBFWeight[index * nd + i] = w[i]; - } - } - } else { - - //Int_t ndw = rStep*zStep*phiStep; - Int_t nd = rStep + zStep + phiStep - 2; - Int_t indexWeight = phiIndex * fNR * fNZ * nd + rIndex * fNZ * nd + zIndex * nd; - - for (Int_t i = 0; i < nd; i++) { - w[i] = fRBFWeight[indexWeight + i]; - } - } -} - -// calculate -// RBFWeight for all points in the interpolation -// half cubes (not included -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::RBFWeightHalf( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - Double_t* a; - Int_t i; - Int_t j; - Int_t k; - Int_t ii; - Int_t jj; - Int_t kk; - - Int_t index0, index1; - Int_t indexCyl0, indexCyl1; - Double_t* r; - Double_t* v; - - Double_t phi0; - Double_t z0; - Double_t r0; - - Double_t phi1; - Double_t z1; - Double_t r1; - - Int_t nd = (rStep - 1) + (phiStep - 1) + (zStep - 1) + 1; - - a = new Double_t[nd * nd]; - r = new Double_t[nd]; - v = new Double_t[nd]; - - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t indexPhi1; - - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - index0 = 0; - - for (i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (j = startR; j < startR + rStep; j++) { - for (k = startZ; k < startZ + zStep; k++) { - - if ( - (i == (startPhi + phiStep / 2) && j == (startR + rStep / 2)) || - (i == (startPhi + phiStep / 2) && k == (startZ + zStep / 2)) || - (j == (startR + rStep / 2) && k == (startZ + zStep / 2))) { - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - if ( - (ii == (startPhi + phiStep / 2) && jj == (startR + rStep / 2)) || - (ii == (startPhi + phiStep / 2) && kk == (startZ + zStep / 2)) || - (jj == (startR + rStep / 2) && kk == (startZ + zStep / 2))) { - - indexCyl1 = indexPhi1 * fNR * fNZ + jj * fNZ + kk; - r1 = fRList[indexCyl1]; - z1 = fZList[indexCyl1]; - phi1 = fPhiList[indexCyl1]; - - r[index1] = Distance(r0, phi0, z0, r1, phi1, z1); - index1++; - } - } - } - } - - Phi(nd, r, radius0, v); - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - if ( - (ii == (startPhi + phiStep / 2) && jj == (startR + rStep / 2)) || - (ii == (startPhi + phiStep / 2) && kk == (startZ + zStep / 2)) || - (jj == (startR + rStep / 2) && kk == (startZ + zStep / 2))) { - a[index0 * nd + index1] = v[index1]; - index1++; - } - } - } - } - - w[index0] = fValue[indexCyl0]; - index0++; - } - } - } - } - - TMatrixD mat_a; - mat_a.Use(nd, nd, a); - TVectorD vec_w; - - vec_w.Use(nd, w); - TDecompSVD svd(mat_a); - - svd.Solve(vec_w); - - delete[] a; - delete[] r; - delete[] v; -} - -// RBF based interpolation -// return interpolated value -// half points -/// -/// \param r -/// \param phi -/// \param z -/// \param startR -/// \param startPhi -/// \param startZ -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param weight -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::InterpRBFHalf( - Double_t r, Double_t phi, Double_t z, Int_t startR, Int_t startPhi, Int_t startZ, Int_t rStep, Int_t phiStep, - Int_t zStep, Double_t radius0, Int_t kernelType, Double_t* weight) -{ - Double_t interpVal = 0.0; - Double_t r0, z0, phi0; - Double_t* dList; - Double_t* v; - - Int_t indexCyl0, index0, indexPhi; - - // Int_t nd = rStep * phiStep * zStep; - Int_t nd = (rStep - 1) + (phiStep - 1) + (zStep - 1) + 1; - - dList = new Double_t[nd]; - v = new Double_t[nd]; - - index0 = 0; - for (Int_t i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (Int_t j = startR; j < startR + rStep; j++) { - for (Int_t k = startZ; k < startZ + zStep; k++) { - if ( - (i == (startPhi + phiStep / 2) && j == (startR + rStep / 2)) || - (i == (startPhi + phiStep / 2) && k == (startZ + zStep / 2)) || - (j == (startR + rStep / 2) && k == (startZ + zStep / 2))) { - - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - dList[index0] = Distance(r, phi, z, r0, phi0, z0); - index0++; - } - } - } - } - - Phi(nd, dList, radius0, v); - - TVectorD vec_v; - vec_v.Use(nd, v); - - TVectorD vec_w; - vec_w.Use(nd, weight); - - interpVal = vec_v * vec_w; - delete[] v; - delete[] dList; - return interpVal; -} - -// set Radius0 -/// -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::Phi(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - - switch (fKernelType) { - case kRBFMultiQuadratic: - rbf1(n, r, r0, v); - break; - case kRBFInverseMultiQuadratic: - rbf2(n, r, r0, v); - break; - case kRBFThinPlateSpline: - rbf3(n, r, r0, v); - break; - case kRBFGaussian: - rbf4(n, r, r0, v); - break; - - default: - rbf1(n, r, r0, v); - break; - } -} - -// -Double_t - AliTPC3DCylindricalInterpolatorIrregular::GetRadius0RBF(const Int_t rIndex, const Int_t phiIndex, const Int_t zIndex) -{ - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNR - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZ - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhi; - Int_t startPhi = phiIndex - fStepPhi / 2; - - Int_t startR = rIndex - fStepR / 2; - Int_t startZ = zIndex - fStepZ / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + fStepR >= fNR) { - startR = fNR - fStepR; - } - - if (startZ < 0) { - startZ = 0; - } - if (startZ + fStepZ >= fNZ) { - startZ = fNZ - fStepZ; - } - - Double_t r0 = AliTPCPoissonSolver::fgkIFCRadius + (startR * gridSizeR); - Double_t phi0 = startPhi * gridSizePhi; - Double_t z0 = startZ * gridSizeZ; - - Double_t r1 = AliTPCPoissonSolver::fgkIFCRadius + (startR * gridSizeR); - Double_t phi1 = (startPhi + 1) * gridSizePhi; - Double_t z1 = (startZ + 1) * gridSizeZ; - - if (fKernelType == kRBFThinPlateSpline) { - r0 = AliTPCPoissonSolver::fgkIFCRadius + ((startR - 1) * gridSizeR); - } else { - r0 = AliTPCPoissonSolver::fgkIFCRadius + (startR * gridSizeR); - } - - return Distance(r0, 0.0, 0.0, r0 + gridSizeR, gridSizePhi, gridSizeR); -} - -// make kdtree for irregular look-up -void AliTPC3DCylindricalInterpolatorIrregular::InitKDTree() -{ - Int_t count = fNR * fNZ * fNPhi; - - fKDTreeIrregularPoints = new KDTreeNode[count]; - - for (Int_t i = 0; i < count; i++) { - fKDTreeIrregularPoints[i].pR = &fRList[i]; - fKDTreeIrregularPoints[i].pZ = &fZList[i]; - fKDTreeIrregularPoints[i].pPhi = &fPhiList[i]; - fKDTreeIrregularPoints[i].index = i; - } - - fKDTreeIrregularRoot = MakeKDTree(fKDTreeIrregularPoints, count, 0, 3); -} - -// create KDTree -AliTPC3DCylindricalInterpolatorIrregular::KDTreeNode* AliTPC3DCylindricalInterpolatorIrregular::MakeKDTree(KDTreeNode* t, Int_t count, Int_t index, Int_t dim) -{ - KDTreeNode* n; - - if (!count) { - return nullptr; - } - if ((n = FindMedian(t, t + count, index))) { - index = (index + 1) % dim; - n->left = MakeKDTree(t, (n - t), index, dim); - n->right = MakeKDTree(n + 1, (t + count) - (n + 1), index, dim); - } - return n; -} - -// find median -AliTPC3DCylindricalInterpolatorIrregular::KDTreeNode* AliTPC3DCylindricalInterpolatorIrregular::FindMedian(KDTreeNode* start, KDTreeNode* end, Int_t index) -{ - if (end <= start) { - return nullptr; - } - if (end == start + 1) { - return start; - } - - KDTreeNode *p, *store, *md = start + (end - start) / 2; - Double_t pivot; - - while (1) { - if (index == 0) { - pivot = *(md->pZ); - } else if (index == 1) { - pivot = *(md->pR); - } else { - pivot = *(md->pPhi); - } - - Swap(md, end - 1); - - for (store = p = start; p < end; p++) { - - if (((index == 0) && (*(p->pZ) < pivot)) || - ((index == 1) && (*(p->pR) < pivot)) || - ((index == 2) && (*(p->pPhi) < pivot))) - - { - if (p != store) { - Swap(p, store); - } - store++; - } - } - Swap(store, end - 1); - - if ((index == 0) && (*(store->pZ) == *(md->pZ))) { - return md; - } - if ((index == 1) && (*(store->pR) == *(md->pR))) { - return md; - } - if ((index == 2) && (*(store->pPhi) == *(md->pPhi))) { - return md; - } - - // if (md->index == store->index) return md; - - if (store > md) { - end = store; - } else { - start = store; - } - } -} - -//swap -void AliTPC3DCylindricalInterpolatorIrregular::Swap(KDTreeNode* x, KDTreeNode* y) -{ - KDTreeNode* tmp = new KDTreeNode; - tmp->pR = x->pR; - tmp->pZ = x->pZ; - tmp->pPhi = x->pPhi; - tmp->index = x->index; - - x->pR = y->pR; - x->pZ = y->pZ; - x->pPhi = y->pPhi; - x->index = y->index; - - y->pR = tmp->pR; - y->pZ = tmp->pZ; - y->pPhi = tmp->pPhi; - y->index = tmp->index; - - delete tmp; -} - -// look for nearest point -void AliTPC3DCylindricalInterpolatorIrregular::KDTreeNearest(KDTreeNode* root, KDTreeNode* nd, Int_t index, Int_t dim, - KDTreeNode** best, Double_t* best_dist) -{ - Double_t d, dx2, dx; - - if (!root) { - return; - } - d = Distance(*(root->pR), *(root->pPhi), *(root->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - if (index == 0) { - dx = *(root->pZ) - *(nd->pZ); - dx2 = Distance(*(nd->pR), *(nd->pPhi), *(root->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - } else if (index == 1) { - dx = *(root->pR) - *(nd->pR); - dx2 = Distance(*(root->pR), *(nd->pPhi), *(nd->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - } else { - dx = *(root->pPhi) - *(nd->pPhi); - dx2 = Distance(*(nd->pR), *(root->pPhi), *(nd->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - } - - if (!*best || (d < *best_dist)) { - *best_dist = d; - *best = root; - } - - if (!*best_dist) { - return; - } - - if (++index >= dim) { - index = 0; - } - - KDTreeNearest(dx > 0 ? root->left : root->right, nd, index, dim, best, best_dist); - if (dx2 >= *best_dist) { - return; - } - KDTreeNearest(dx > 0 ? root->right : root->left, nd, index, dim, best, best_dist); -} - -// interpolate on the nearest neighbor of irregular grid -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Interpolate3DTableCylRBF( - Double_t r, Double_t z, Double_t phi, KDTreeNode* nearestNode) -{ - Double_t val = 0.0; - Int_t startPhi, startR, startZ; - Int_t phiIndex, rIndex, zIndex; - - phiIndex = nearestNode->index / (fNR * fNZ); - rIndex = (nearestNode->index - (phiIndex * (fNR * fNZ))) / fNZ; - zIndex = nearestNode->index - (phiIndex * (fNR * fNZ) + rIndex * fNZ); - - startPhi = phiIndex - fStepPhi / 2; - startR = rIndex - fStepR / 2; - startZ = zIndex - fStepZ / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + fStepR >= fNR) { - startR = fNR - fStepR; - } - - if (startZ < 0) { - startZ = 0; - } - if (startZ + fStepZ >= fNZ) { - startZ = fNZ - fStepZ; - } - Int_t indexPhi; - - Int_t index; - Double_t r0, z0, phi0; - - Int_t rStep = fStepR; - Int_t zStep = fStepZ; - Int_t phiStep = fStepPhi; - - Double_t* w; - - //Int_t nd = (phiStep-1) + (rStep-1) + (zStep-1) + 1; - Int_t nd = fStepPhi * fStepR * fStepZ; - - w = new Double_t[nd]; - - Float_t minTemp, minTemp2; - - Double_t radiusRBF0 = GetRadius0RBF(rIndex, phiIndex, zIndex); - - if (fType == 1) { - - for (Int_t i = 0; i < nd; i++) { - w[i] = 0.0; - } - GetRBFWeight(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBF(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } else { - GetRBFWeightHalf(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBFHalf(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } - delete[] w; - return val; -} diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.h b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.h deleted file mode 100644 index 952114b174cd2..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.h +++ /dev/null @@ -1,151 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPC3DCylindricalInterpolatorIrregular.h -/// \brief Irregular grid interpolator for cylindrical coordinate with r,phi,z different coordinates -/// RBF-based interpolation -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#ifndef AliTPC3DCylindricalInterpolatorIrregular_H -#define AliTPC3DCylindricalInterpolatorIrregular_H - -#include "TMatrixD.h" - -class AliTPC3DCylindricalInterpolatorIrregular -{ - public: - AliTPC3DCylindricalInterpolatorIrregular(Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, Int_t rStep, Int_t zStep, - Int_t phiStep, Int_t intType); - AliTPC3DCylindricalInterpolatorIrregular(); - ~AliTPC3DCylindricalInterpolatorIrregular(); - - Double_t - GetValue(Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, - Int_t stepZ); - Double_t - GetValue(Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, - Int_t stepZ, Int_t minZColumnIndex); - Double_t GetValue(Double_t r, Double_t phi, Double_t z); - void SetOrder(Int_t order) { fOrder = order; } - - void InitRBFWeight(); - void SetIrregularGridSize(Int_t size) { fIrregularGridSize = size; } - Int_t GetIrregularGridSize() { return fIrregularGridSize; } - void SetKernelType(Int_t kernelType) { fKernelType = kernelType; } - Int_t GetKernelType() { return fKernelType; } - - ///< Enumeration of Poisson Solver Strategy Type - enum RBFKernelType { - kRBFMultiQuadratic = 0, - kRBFInverseMultiQuadratic = 1, - kRBFThinPlateSpline = 2, - kRBFGaussian = 3 - }; - - void SetNR(Int_t nRRow) { fNR = nRRow; } - void SetNPhi(Int_t nPhiSlice) { fNPhi = nPhiSlice; } - void SetNZ(Int_t nZColumn) { fNZ = nZColumn; } - - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - - void SetRList(Double_t* rList) { fRList = rList; } - void SetPhiList(Double_t* phiList) { fPhiList = phiList; } - void SetZList(Double_t* zList) { fZList = zList; } - - void SetValue(Double_t* value) { fValue = value; } - void - SetValue(TMatrixD** matricesValue, TMatrixD** matricesRPoint, TMatrixD** matricesPhiPoint, TMatrixD** matricesZPoint); - void - SetValue(TMatrixD** matricesValue, TMatrixD** matricesRPoint, TMatrixD** matricesPhiPoint, TMatrixD** matricesZPoint, - Int_t jy); - - struct KDTreeNode { - Double_t* pR; //!<! TODO: fix for streamers - Double_t* pZ; //!<! - Double_t* pPhi; //!<! - Int_t index; - struct KDTreeNode *left, *right; - }; - - private: - Int_t fOrder; ///< Order of interpolation, 1 - linear, 2 - quadratic, 3 - cubic - Int_t fType; ///< 0 INVERSE WEIGHT, 1 RBF FULL, 2 RBF Half - Int_t fKernelType; ///< type kernel RBF 1--5 - Int_t fIrregularGridSize; ///< size when interpolating for irregular grid - Int_t fNR; ///< Grid size in direction of R - Int_t fNPhi; ///< Grid size in direction of Phi - Int_t fNZ; ///< Grid size in direction of Z - Int_t fNGridPoints; ///< Total number of grid points (needed for streamer) - Int_t fNRBFpoints; ///< Total number of points for RBF weights - Int_t fMinZIndex; ///<index z minimal as lower bound - Int_t fStepR; ///< step in R direction for irregular grid - Int_t fStepZ; ///< step in Z direction for irregular grid - Int_t fStepPhi; ///< step in Phi direction for irregular grid - Int_t* fRBFWeightLookUp = nullptr; //[fNGridPoints] weighted look up - - Double_t fRadiusRBF0; ///< Radius RBF0 - Double_t* fValue = nullptr; //[fNGridPoints] 3D for storing known values interpolation should be in size fNR*fNPhi*fNZ - Double_t* fRList = nullptr; //[fNGridPoints] coordinate in R (cm) (should be increasing) in 3D - Double_t* fPhiList = nullptr; //[fNGridPoints] coordinate in phiList (rad) (should be increasing) 0 <= < 2 pi (cyclic) in 3D - Double_t* fZList = nullptr; //[fNGridPoints] coordinate in z list (cm) (should be increasing) in 3D - Double_t* fRBFWeight = nullptr; //[fNRBFpoints] weight for RBF - Bool_t fIsAllocatingLookUp; ///< is allocating memory? - - Double_t Interpolate3DTableCylIDW(Double_t r, Double_t z, Double_t phi, Int_t rIndex, Int_t zIndex, Int_t phiIndex, - Int_t stepR, Int_t stepZ, Int_t stepPhi); - Double_t Interpolate3DTableCylRBF(Double_t r, Double_t z, Double_t phi, Int_t rIndex, Int_t zIndex, Int_t phiIndex, - Int_t stepR, Int_t stepZ, Int_t stepPhi, Double_t radiusRBF0); - Double_t Interpolate3DTableCylRBF(Double_t r, Double_t z, Double_t phi, KDTreeNode* nearestNode); - - void Search(Int_t n, const Double_t xArray[], Double_t x, Int_t& low); - void Search(Int_t n, Double_t* xArray, Int_t offset, Double_t x, Int_t& low); - Double_t Distance(Double_t r0, Double_t phi0, Double_t z0, Double_t r, Double_t phi, Double_t z); - void RBFWeight(Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Double_t radius0, - Int_t kernelType, Double_t* weight); - void - GetRBFWeight(Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Double_t radius0, - Int_t kernelType, Double_t* weight); - void Phi(Int_t n, Double_t r[], Double_t r0, Double_t v[]); - void rbf1(Int_t n, Double_t r[], Double_t r0, Double_t v[]); - void rbf2(Int_t n, Double_t r[], Double_t r0, Double_t v[]); - void rbf3(Int_t n, Double_t r[], Double_t r0, Double_t v[]); - void rbf4(Int_t n, Double_t r[], Double_t r0, Double_t v[]); - Double_t InterpRBF(Double_t r, Double_t phi, Double_t z, Int_t startR, Int_t startPhi, Int_t startZ, Int_t stepR, - Int_t stepPhi, Int_t stepZ, Double_t radius0, Int_t kernelType, Double_t* weight); - void - RBFWeightHalf(Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Double_t radius0, - Int_t kernelType, Double_t* weight); - Double_t InterpRBFHalf(Double_t r, Double_t phi, Double_t z, Int_t startR, Int_t startPhi, Int_t startZ, Int_t stepR, - Int_t stepPhi, Int_t stepZ, Double_t radius0, Int_t kernelType, Double_t* weight); - void GetRBFWeightHalf(Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, - Double_t radius0, Int_t kernelType, Double_t* weight); - Double_t GetRadius0RBF(const Int_t rIndex, const Int_t phiIndex, const Int_t zIndex); - - KDTreeNode* fKDTreeIrregularPoints = nullptr; //!<![fNGridPoints] to save tree as list - KDTreeNode* fKDTreeIrregularRoot = nullptr; //!<! kdtree root TODO: make this streamable - - void InitKDTree(); - KDTreeNode* MakeKDTree(KDTreeNode* tree, Int_t count, Int_t index, Int_t dimention); - - KDTreeNode* FindMedian(KDTreeNode* startTree, KDTreeNode* endTree, Int_t index); - void Swap(KDTreeNode* x, KDTreeNode* y); - - void KDTreeNearest(KDTreeNode* root, KDTreeNode* nd, Int_t index, Int_t dim, - KDTreeNode** best, Double_t* best_dist); - /// \cond CLASSIMP - ClassDefNV(AliTPC3DCylindricalInterpolatorIrregular, 1); - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.cxx b/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.cxx deleted file mode 100644 index 10e0837b95699..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.cxx +++ /dev/null @@ -1,215 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCLookUpTable3DInterpolatorD.cxx -/// \brief Wrap up look-up table for correction/distortion integral or derivative (electric field) -/// assume 3 components: r-component, phi-component and z-component -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#include "AliTPCLookUpTable3DInterpolatorD.h" - -/// \cond CLASSIMP3 -ClassImp(AliTPCLookUpTable3DInterpolatorD); -/// \endcond - -/// constructor -AliTPCLookUpTable3DInterpolatorD::AliTPCLookUpTable3DInterpolatorD() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; -} - -/// constructor -/// -/// \param nRRow Int_t size of grid in R direction -/// \param rMin Double_t minimal value of R -/// \param rMax Double_t maximal value of R -/// \param nPhiSlice Int_t size of grid Phi direction -/// \param phiMin Double_t minimal value of Phi -/// \param phiMax Double_t maximal value of Phi -/// \param nZColumn Int_t size of grid Z direction -/// \param zMin Double_t minimal value of Z -/// \param zMax Double_t maximal value of Z - -/** - AliTPCLookUpTable3DInterpolatorD::AliTPCLookUpTable3DInterpolatorD(Int_t nRRow, Double_t rMin, Double_t rMax, - Int_t nPhiSlice, - Double_t phiMin, Double_t phiMax, Int_t nZColumn, - Double_t zMin, Double_t zMax) { - fOrder = 1; - fIsAllocatingLookUp = kTRUE; - - fNR = nRRow; - fNPhi = nPhiSlice; - fNZ = nZColumn; - - fLookUpR = new TMatrixD *[fNPhi]; - fLookUpPhi = new TMatrixD *[fNPhi]; - fLookUpZ = new TMatrixD *[fNPhi]; - - for (Int_t m = 0; m < fNPhi; m++) { - fLookUpR[m] = new TMatrixD(fNR, fNZ); - fLookUpPhi[m] = new TMatrixD(fNR, fNZ); - fLookUpZ[m] = new TMatrixD(fNR, fNZ); - } - - fRList = new Double_t[fNR]; - fPhiList = new Double_t[fNPhi]; - fZList = new Double_t[fNZ]; - - Double_t dR = (rMax - rMin) / fNR; - Double_t dPhi = (phiMax - phiMin) / fNPhi; - Double_t dZ = (zMax - zMin) / fNPhi; - - for (Int_t m = 0; m < fNPhi; m++) fPhiList[m] = phiMin + dPhi * m; - for (Int_t m = 0; m < fNR; m++) fRList[m] = rMin + dR * m; - for (Int_t m = 0; m < fNZ; m++) fZList[m] = zMin + dZ * m; - } - **/ - -/// Constructor -/// -/// \param nRRow Int_t size of grid in R direction -/// \param matricesRValue TMatrixD** values of component R -/// \param rList Double_t* list of position R -/// \param nPhiSlice Int_t size of grid in Phi direction -/// \param matricesPhiValue TMatrixD** values of component Phi -/// \param phiList Double_t* list of position Phi -/// \param nZColumn Int_t size of grid in Z direction -/// \param matricesZValue TMatrixD** values of component Z -/// \param zList Double_t* list of position Z -/// \param order Int_t order of interpolation -AliTPCLookUpTable3DInterpolatorD::AliTPCLookUpTable3DInterpolatorD( - Int_t nRRow, TMatrixD** matricesRValue, Double_t* rList, - Int_t nPhiSlice, TMatrixD** matricesPhiValue, Double_t* phiList, - Int_t nZColumn, TMatrixD** matricesZValue, Double_t* zList, Int_t order) -{ - fIsAllocatingLookUp = kFALSE; - - SetNR(nRRow); - SetLookUpR(matricesRValue); - SetRList(rList); - SetNPhi(nPhiSlice); - SetLookUpPhi(matricesPhiValue); - SetPhiList(phiList); - SetNZ(nZColumn); - SetLookUpZ(matricesZValue); - SetZList(zList); - - fInterpolatorR = new AliTPC3DCylindricalInterpolator(); - fInterpolatorZ = new AliTPC3DCylindricalInterpolator(); - fInterpolatorPhi = new AliTPC3DCylindricalInterpolator(); - - SetOrder(order); - fInterpolatorR->SetNR(nRRow); - fInterpolatorR->SetNZ(nZColumn); - fInterpolatorR->SetNPhi(nPhiSlice); - fInterpolatorR->SetNGridPoints(); - fInterpolatorR->SetRList(rList); - fInterpolatorR->SetZList(zList); - fInterpolatorR->SetPhiList(phiList); - fInterpolatorR->SetOrder(order); - - fInterpolatorZ->SetNR(nRRow); - fInterpolatorZ->SetNZ(nZColumn); - fInterpolatorZ->SetNPhi(nPhiSlice); - fInterpolatorZ->SetNGridPoints(); - fInterpolatorZ->SetRList(rList); - fInterpolatorZ->SetZList(zList); - fInterpolatorZ->SetPhiList(phiList); - fInterpolatorZ->SetOrder(order); - - fInterpolatorPhi->SetNR(nRRow); - fInterpolatorPhi->SetNZ(nZColumn); - fInterpolatorPhi->SetNPhi(nPhiSlice); - fInterpolatorPhi->SetNGridPoints(); - fInterpolatorPhi->SetRList(rList); - fInterpolatorPhi->SetZList(zList); - fInterpolatorPhi->SetPhiList(phiList); - fInterpolatorPhi->SetOrder(order); -} - -/// destructor -AliTPCLookUpTable3DInterpolatorD::~AliTPCLookUpTable3DInterpolatorD() -{ - delete fInterpolatorR; - delete fInterpolatorZ; - delete fInterpolatorPhi; -} - -/// copy from matrices to 1D array for interpolation algorithm -void AliTPCLookUpTable3DInterpolatorD::CopyFromMatricesToInterpolator() -{ - fInterpolatorR->SetValue(fLookUpR); - fInterpolatorZ->SetValue(fLookUpZ); - fInterpolatorPhi->SetValue(fLookUpPhi); - - if (fOrder > 2) { - fInterpolatorR->InitCubicSpline(); - fInterpolatorZ->InitCubicSpline(); - fInterpolatorPhi->InitCubicSpline(); - } -} - -/// copy from matrices to 1D array for interpolation algorithm -void AliTPCLookUpTable3DInterpolatorD::CopyFromMatricesToInterpolator(Int_t iZ) -{ - fInterpolatorR->SetValue(fLookUpR, iZ); - fInterpolatorZ->SetValue(fLookUpZ, iZ); - fInterpolatorPhi->SetValue(fLookUpPhi, iZ); - - // no implementation for cubic spline interpolation -} - -/// get value of 3-components at a P(r,phi,z) -/// -/// \param r Double_t r position -/// \param phi Double_t phi position -/// \param z Double_t z position -/// \param rValue Double_t value of r-component -/// \param phiValue Double_t value of phi-component -/// \param zValue Double_t value of z-component -void AliTPCLookUpTable3DInterpolatorD::GetValue( - Double_t r, Double_t phi, Double_t z, - Double_t& rValue, Double_t& phiValue, Double_t& zValue) const -{ - rValue = fInterpolatorR->GetValue(r, phi, z); - phiValue = fInterpolatorPhi->GetValue(r, phi, z); - zValue = fInterpolatorZ->GetValue(r, phi, z); -} - -/// get value for return value is a Float_t -/// -/// \param r Double_t r position -/// \param phi Double_t phi position -/// \param z Double_t z position -/// \param rValue Float_t value of r-component -/// \param phiValue Float_t value of phi-component -/// \param zValue Float_t value of z-component -void AliTPCLookUpTable3DInterpolatorD::GetValue( - Double_t r, Double_t phi, Double_t z, - Float_t& rValue, Float_t& phiValue, Float_t& zValue) const -{ - rValue = fInterpolatorR->GetValue(r, phi, z); - phiValue = fInterpolatorPhi->GetValue(r, phi, z); - zValue = fInterpolatorZ->GetValue(r, phi, z); -} - -// Set Order of interpolation -// -void AliTPCLookUpTable3DInterpolatorD::SetOrder(Int_t order) -{ - fOrder = order; - fInterpolatorR->SetOrder(order); - fInterpolatorZ->SetOrder(order); - fInterpolatorPhi->SetOrder(order); -} diff --git a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.h b/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.h deleted file mode 100644 index 470a84577a69e..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.h +++ /dev/null @@ -1,87 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCLookUpTable3DInterpolatorD.h -/// \brief Wrap up look-up table for correction/distortion integral or derivative (electric field) -/// assume 3 components: r-component, phi-component and z-component -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#ifndef AliTPCLookUpTable3DInterpolatorD_H -#define AliTPCLookUpTable3DInterpolatorD_H - -#include "TMatrixD.h" -#include "AliTPC3DCylindricalInterpolator.h" - -class AliTPCLookUpTable3DInterpolatorD -{ - public: - AliTPCLookUpTable3DInterpolatorD(); - //AliTPCLookUpTable3DInterpolatorD(Int_t nRRow, Double_t rMin, Double_t rMax, Int_t nPhiSlice, Double_t phiMin, Double_t phiMax, Int_t nZColumn , Double_t zMin, Double_t zMax ); - AliTPCLookUpTable3DInterpolatorD(Int_t nRRow, TMatrixD** matricesRValue, Double_t* rList, Int_t nPhiSlice, TMatrixD** matricesPhiValue, Double_t* phiList, Int_t nZColumn, TMatrixD** matricesZValue, Double_t* zList, Int_t order); - ~AliTPCLookUpTable3DInterpolatorD(); - - void SetNR(Int_t nRRow) { fNR = nRRow; } - void SetNPhi(Int_t nPhiSlice) { fNPhi = nPhiSlice; } - void SetNZ(Int_t nZColumn) { fNZ = nZColumn; } - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - - void SetRList(Double_t* rList) { fRList = rList; } - void SetPhiList(Double_t* phiList) { fPhiList = phiList; } - void SetZList(Double_t* zList) { fZList = zList; } - void SetLookUpR(TMatrixD** matricesRValue) { fLookUpR = matricesRValue; } - void SetLookUpPhi(TMatrixD** matricesPhiValue) { fLookUpPhi = matricesPhiValue; } - void SetLookUpZ(TMatrixD** matricesZValue) { fLookUpZ = matricesZValue; } - void SetOrder(Int_t order); - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue) const; - void GetValue(Double_t r, Double_t phi, Double_t z, Float_t& rValue, Float_t& phiValue, Float_t& zValue) const; - void CopyFromMatricesToInterpolator(); - void CopyFromMatricesToInterpolator(Int_t iZ); // copy only iZ - - TMatrixD** GetLookUpR() { return fLookUpR; } - TMatrixD** GetLookUpPhi() { return fLookUpPhi; } - TMatrixD** GetLookUpZ() { return fLookUpZ; } - Double_t* GetRList() { return fRList; } - Double_t* GetPhiList() { return fPhiList; } - Double_t* GetZList() { return fZList; } - - AliTPC3DCylindricalInterpolator* GetInterpolatorR() { return fInterpolatorR; } - AliTPC3DCylindricalInterpolator* GetInterpolatorPhi() { return fInterpolatorPhi; } - AliTPC3DCylindricalInterpolator* GetInterpolatorZ() { return fInterpolatorZ; } - - private: - Int_t fOrder; ///< order of interpolation - Int_t fNR; ///< number of grid in R - Int_t fNPhi; ///< number of grid in Phi - Int_t fNZ; ///< number of grid in Z - - TMatrixD** fLookUpR = nullptr; //!<! Array to store distortion following the drift - TMatrixD** fLookUpPhi = nullptr; //!<! to store distortion following the drift - TMatrixD** fLookUpZ = nullptr; //!<! Array to store distortion following the drift - - AliTPC3DCylindricalInterpolator* fInterpolatorR = nullptr; //-> Interpolator for R component - AliTPC3DCylindricalInterpolator* fInterpolatorPhi = nullptr; //-> Interpolator for Phi component - AliTPC3DCylindricalInterpolator* fInterpolatorZ = nullptr; //-> Interpolator for Z component - - Double_t* fRList = nullptr; //!<! List of R coordinate (regular grid) - Double_t* fPhiList = nullptr; //!<! List of Phi coordinate (regular grid) - Double_t* fZList = nullptr; //!<! List of Z coordinate (regular grid) - - Bool_t fIsAllocatingLookUp; ///< flag for initialization of cubic spline - - /// \cond CLASSIMP - ClassDefNV(AliTPCLookUpTable3DInterpolatorD, 1); - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.cxx b/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.cxx deleted file mode 100644 index b12c36566941a..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.cxx +++ /dev/null @@ -1,188 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCLookUpTable3DInterpolatorIrregularD.cxx -/// \brief Wrap up look-up table with irregular grid -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#include "AliTPCLookUpTable3DInterpolatorIrregularD.h" - -/// \cond CLASSIMP3 -ClassImp(AliTPCLookUpTable3DInterpolatorIrregularD); -/// \endcond - -/// constructor -AliTPCLookUpTable3DInterpolatorIrregularD::AliTPCLookUpTable3DInterpolatorIrregularD() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; -} - -/// constructor -/// -/// \param nRRow -/// \param matricesRValue -/// \param matricesRPoint -/// \param nPhiSlice -/// \param matricesPhiValue -/// \param matricesPhiPoint -/// \param nZColumn -/// \param matricesZValue -/// \param matricesZPoint -/// \param order -/// \param stepR -/// \param stepZ -/// \param stepPhi -/// \param type -AliTPCLookUpTable3DInterpolatorIrregularD::AliTPCLookUpTable3DInterpolatorIrregularD( - Int_t nRRow, TMatrixD** matricesRValue, TMatrixD** matricesRPoint, Int_t nPhiSlice, TMatrixD** matricesPhiValue, - TMatrixD** matricesPhiPoint, Int_t nZColumn, - TMatrixD** matricesZValue, TMatrixD** matricesZPoint, Int_t order, Int_t stepR, Int_t stepZ, Int_t stepPhi, - Int_t type) -{ - fIsAllocatingLookUp = kFALSE; - - SetNR(nRRow); - SetLookUpR(matricesRValue); - SetRList(matricesRPoint); - SetNPhi(nPhiSlice); - SetLookUpPhi(matricesPhiValue); - SetPhiList(matricesPhiPoint); - SetNZ(nZColumn); - SetLookUpZ(matricesZValue); - SetZList(matricesZPoint); - SetOrder(order); - - fInterpolatorR = new AliTPC3DCylindricalInterpolatorIrregular( - nRRow, nZColumn, nPhiSlice, stepR, stepZ, stepPhi, type); - fInterpolatorZ = new AliTPC3DCylindricalInterpolatorIrregular( - nRRow, nZColumn, nPhiSlice, stepR, stepZ, stepPhi, type); - fInterpolatorPhi = new AliTPC3DCylindricalInterpolatorIrregular( - nRRow, nZColumn, nPhiSlice, stepR, stepZ, stepPhi, type); - - fInterpolatorR->SetNR(nRRow); - fInterpolatorR->SetNZ(nZColumn); - fInterpolatorR->SetNPhi(nPhiSlice); - - fInterpolatorR->SetOrder(order); - fInterpolatorZ->SetNR(nRRow); - fInterpolatorZ->SetNZ(nZColumn); - fInterpolatorZ->SetNPhi(nPhiSlice); - fInterpolatorZ->SetOrder(order); - fInterpolatorPhi->SetNR(nRRow); - fInterpolatorPhi->SetNZ(nZColumn); - fInterpolatorPhi->SetNPhi(nPhiSlice); - fInterpolatorPhi->SetOrder(order); -} - -/// destructor -AliTPCLookUpTable3DInterpolatorIrregularD::~AliTPCLookUpTable3DInterpolatorIrregularD() -{ - delete fInterpolatorR; - delete fInterpolatorZ; - delete fInterpolatorPhi; -} - -/// copy from matrices to the interpolator -void AliTPCLookUpTable3DInterpolatorIrregularD::CopyFromMatricesToInterpolator() -{ - - fInterpolatorR->SetValue(fMatricesRValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint); - fInterpolatorZ->SetValue(fMatricesZValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint); - fInterpolatorPhi->SetValue(fMatricesPhiValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint); -} - -/// -/// \param j -void AliTPCLookUpTable3DInterpolatorIrregularD::CopyFromMatricesToInterpolator(Int_t j) -{ - fInterpolatorR->SetValue(fMatricesRValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint, j); - fInterpolatorZ->SetValue(fMatricesZValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint, j); - fInterpolatorPhi->SetValue(fMatricesPhiValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint, j); -} - -/// Get interpolation -/// \param r -/// \param phi -/// \param z -/// \param rValue -/// \param phiValue -/// \param zValue -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param stepR -/// \param stepPhi -/// \param stepZ -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, - Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ) -{ - rValue = fInterpolatorR->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ); - phiValue = fInterpolatorPhi->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ); - zValue = fInterpolatorZ->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ); -} - -/// Interpolation for a point (r,phi,z) -/// -/// \param r -/// \param phi -/// \param z -/// \param rValue -/// \param phiValue -/// \param zValue -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param stepR -/// \param stepPhi -/// \param stepZ -/// \param minZColumnIndex -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Int_t minZColumnIndex) -{ - rValue = fInterpolatorR->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ, minZColumnIndex); - phiValue = fInterpolatorPhi->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ, minZColumnIndex); - zValue = fInterpolatorZ->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ, minZColumnIndex); -} - -/// Get interpolation -/// \param r -/// \param phi -/// \param z -/// \param rValue -/// \param phiValue -/// \param zValue -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param startR -/// \param startPhi -/// \param startZ -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Float_t& rValue, Float_t& phiValue, Float_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t startR, Int_t startPhi, Int_t startZ) -{ - rValue = fInterpolatorR->GetValue(r, phi, z, rIndex, phiIndex, zIndex, startR, startPhi, startZ); - phiValue = fInterpolatorPhi->GetValue(r, phi, z, rIndex, phiIndex, zIndex, startR, startPhi, startZ); - zValue = fInterpolatorZ->GetValue(r, phi, z, rIndex, phiIndex, zIndex, startR, startPhi, startZ); -} - -// using kdtree -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue) -{ - rValue = fInterpolatorR->GetValue(r, phi, z); - phiValue = fInterpolatorPhi->GetValue(r, phi, z); - zValue = fInterpolatorZ->GetValue(r, phi, z); -} diff --git a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.h b/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.h deleted file mode 100644 index f7466360f3f85..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCLookUpTable3DInterpolatorIrregularD.h -/// \brief Wrap up look-up table with irregular grid -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#ifndef AliTPCLookUpTable3DInterpolatorIrregularD_H -#define AliTPCLookUpTable3DInterpolatorIrregularD_H - -#include "TMatrixD.h" -#include "AliTPC3DCylindricalInterpolatorIrregular.h" - -class AliTPCLookUpTable3DInterpolatorIrregularD -{ - public: - void SetNR(Int_t nRRow) { fNR = nRRow; } - void SetNPhi(Int_t nPhiSlice) { fNPhi = nPhiSlice; } - void SetNZ(Int_t nZColumn) { fNZ = nZColumn; } - - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - - void SetRList(TMatrixD** matricesRPoint) { fMatricesRPoint = matricesRPoint; } - void SetPhiList(TMatrixD** matricesPhiPoint) { fMatricesPhiPoint = matricesPhiPoint; } - void SetZList(TMatrixD** matricesZPoint) { fMatricesZPoint = matricesZPoint; } - - void SetLookUpR(TMatrixD** matricesRValue) { fMatricesRValue = matricesRValue; } - void SetLookUpPhi(TMatrixD** matricesPhiValue) { fMatricesPhiValue = matricesPhiValue; } - void SetLookUpZ(TMatrixD** matricesZValue) { fMatricesZValue = matricesZValue; } - - AliTPCLookUpTable3DInterpolatorIrregularD(); - AliTPCLookUpTable3DInterpolatorIrregularD(Int_t nRRow, TMatrixD** matricesRValue, TMatrixD** r, Int_t nPhiSlice, - TMatrixD** matricesPhiValue, TMatrixD** matricesPhiPoint, Int_t nZColumn, - TMatrixD** matricesZValue, TMatrixD** matricesZPoint, Int_t order, - Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t type); - - ~AliTPCLookUpTable3DInterpolatorIrregularD(); - - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ); - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Int_t minZColumnIndex); - void GetValue(Double_t r, Double_t phi, Double_t z, Float_t& rValue, Float_t& phiValue, Float_t& zValue, Int_t rIndex, Int_t phiIndex, - Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ); - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue); - void SetOrder(Int_t order) { fOrder = order; } - void CopyFromMatricesToInterpolator(); - void CopyFromMatricesToInterpolator(Int_t j); - - Int_t GetIrregularGridSize() { return fInterpolatorR->GetIrregularGridSize(); } - void SetIrregularGridSize(Int_t size) - { - fInterpolatorR->SetIrregularGridSize(size); - fInterpolatorPhi->SetIrregularGridSize(size); - fInterpolatorZ->SetIrregularGridSize(size); - } - void SetKernelType(Int_t kernelType) - { - fInterpolatorR->SetKernelType(kernelType); - fInterpolatorPhi->SetKernelType(kernelType); - fInterpolatorZ->SetKernelType(kernelType); - } - Int_t GetKernelType() { return fInterpolatorR->GetKernelType(); } - - private: - Int_t fOrder; ///< Order of interpolation - Int_t fIrregularGridSize; ///< Size of irregular interpolation neighborhood - Int_t fNR; ///< Number of grid in R - Int_t fNPhi; ///< Number of grid in Phi - Int_t fNZ; ///< Number of grid in Z - - TMatrixD** fMatricesRValue = nullptr; //!<! Matrices to store r-component - TMatrixD** fMatricesPhiValue = nullptr; //!<! Matrices to store phi-component - TMatrixD** fMatricesZValue = nullptr; //!<! Matrices to store z-component - - AliTPC3DCylindricalInterpolatorIrregular* fInterpolatorR = nullptr; //-> Irregular interpolator for R-component - AliTPC3DCylindricalInterpolatorIrregular* fInterpolatorPhi = nullptr; //-> Irregular interpolator for Phi-component - AliTPC3DCylindricalInterpolatorIrregular* fInterpolatorZ = nullptr; //-> Irregular interpolator for Z-component - - TMatrixD** fMatricesRPoint = nullptr; //!<! Matrices to store distorted point (r component) - TMatrixD** fMatricesPhiPoint = nullptr; //!<! Matrices to store distorted point (phi component) - TMatrixD** fMatricesZPoint = nullptr; //!<! Matrices to store distorted point (z component) - - Bool_t fIsAllocatingLookUp; - - /// \cond CLASSIMP - ClassDefNV(AliTPCLookUpTable3DInterpolatorIrregularD, 1); - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.cxx b/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.cxx deleted file mode 100644 index bf789cedfcb68..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.cxx +++ /dev/null @@ -1,3031 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCPoissonSolver.cxx -/// \brief This class provides implementation of Poisson Eq -/// solver by MultiGrid Method -/// -/// -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#include <TMath.h> -#include "AliTPCPoissonSolver.h" - -/// \cond CLASSIMP -ClassImp(AliTPCPoissonSolver); -/// \endcond - -const Double_t AliTPCPoissonSolver::fgkTPCZ0 = 249.7; ///< nominal gating grid position -const Double_t AliTPCPoissonSolver::fgkIFCRadius = 83.5; ///< radius which renders the "18 rod manifold" best -> compare calc. of Jim Thomas -const Double_t AliTPCPoissonSolver::fgkOFCRadius = 254.5; ///< Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm) -const Double_t AliTPCPoissonSolver::fgkZOffSet = 0.2; ///< Offset from CE: calculate all distortions closer to CE as if at this point -const Double_t AliTPCPoissonSolver::fgkCathodeV = -100000.0; ///< Cathode Voltage (volts) -const Double_t AliTPCPoissonSolver::fgkGG = -70.0; ///< Gating Grid voltage (volts) -const Double_t AliTPCPoissonSolver::fgkdvdE = 0.0024; ///< [cm/V] drift velocity dependency on the E field (from Magboltz for NeCO2N2 at standard environment) -const Double_t AliTPCPoissonSolver::fgkEM = -1.602176487e-19 / 9.10938215e-31; ///< charge/mass in [C/kg] -const Double_t AliTPCPoissonSolver::fgke0 = 8.854187817e-12; ///< vacuum permittivity [A·s/(V·m)] - -Double_t AliTPCPoissonSolver::fgExactErr = 1e-4; -Double_t AliTPCPoissonSolver::fgConvergenceError = 1e-3; - -/// constructor -/// -AliTPCPoissonSolver::AliTPCPoissonSolver() - : TNamed("poisson solver", "solver"), - fErrorConvergenceNorm2{new TVectorD(fMgParameters.nMGCycle)}, - fErrorConvergenceNormInf{new TVectorD(fMgParameters.nMGCycle)}, - fError{new TVectorD(fMgParameters.nMGCycle)} - -{ - // default strategy -} - -/// Constructor -/// \param name name of the object -/// \param title title of the object -AliTPCPoissonSolver::AliTPCPoissonSolver(const char* name, const char* title) - : TNamed(name, title), - fErrorConvergenceNorm2{new TVectorD(fMgParameters.nMGCycle)}, - fErrorConvergenceNormInf{new TVectorD(fMgParameters.nMGCycle)}, - fError{new TVectorD(fMgParameters.nMGCycle)} -{ - /// constructor -} - -/// destructor -AliTPCPoissonSolver::~AliTPCPoissonSolver() -{ - /// virtual destructor - delete[] fExactSolution; - delete fErrorConvergenceNorm2; - delete fErrorConvergenceNormInf; - delete fError; -} - -/// Provides poisson solver in 2D -/// -/// Based on the strategy (relaxation, multi grid or FFT) -/// -/// \param matrixV TMatrixD& potential in matrix -/// \param matrixCharge TMatrixD& charge density in matrix (side effect -/// \param nRRow Int_t number of nRRow in the grid -/// \param nZColumn Int_t number of nZColumn in the grid -/// \param maxIteration Int_t maximum iteration for relaxation method -/// -/// \return A fixed number that has nothing to do with what the function does -void AliTPCPoissonSolver::PoissonSolver2D(TMatrixD& matrixV, TMatrixD& matrixCharge, Int_t nRRow, Int_t nZColumn, - Int_t maxIteration) -{ - switch (fStrategy) { - case kMultiGrid: - PoissonMultiGrid2D(matrixV, matrixCharge, nRRow, nZColumn); - break; - default: - PoissonRelaxation2D(matrixV, matrixCharge, nRRow, nZColumn, maxIteration); - } -} - -/// Provides poisson solver in Cylindrical 3D (TPC geometry) -/// -/// Strategy based on parameter settings (fStrategy and fMgParameters)provided -/// * Cascaded multi grid with S.O.R -/// * Geometric MultiGrid -/// * Cycles: V, W, Full -/// * Relaxation: Jacobi, Weighted-Jacobi, Gauss-Seidel -/// * Grid transfer operators: Full, Half -/// * Spectral Methods (TODO) -/// -/// \param matricesV TMatrixD** potential in 3D matrix -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method -/// \param symmetry Int_t symmetry or not -/// -/// \pre Charge density distribution in **matricesCharge** is known and boundary values for **matricesV** are set -/// \post Numerical solution for potential distribution is calculated and stored in each rod at **matricesV** -void AliTPCPoissonSolver::PoissonSolver3D(TMatrixD** matricesV, TMatrixD** matricesCharge, - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Int_t symmetry) -{ - switch (fStrategy) { - case kMultiGrid: - if (fMgParameters.isFull3D) { - PoissonMultiGrid3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, symmetry); - } else { - PoissonMultiGrid3D2D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, symmetry); - } - break; - default: - PoissonRelaxation3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry); - } -} - -/// Solve Poisson's Equation by Relaxation Technique in 2D (assuming cylindrical symmetry) -/// -/// Solve Poisson's equation in a cylindrical coordinate system. The matrixV matrix must be filled with the -/// boundary conditions on the first and last nRRow, and the first and last nZColumn. The remainder of the -/// array can be blank or contain a preliminary guess at the solution. The Charge density matrix contains -/// the enclosed spacecharge density at each point. The charge density matrix can be full of zero's if -/// you wish to solve Laplace equation however it should not contain random numbers or you will get -/// random numbers back as a solution. -/// Poisson's equation is solved by iteratively relaxing the matrix to the final solution. In order to -/// speed up the convergence to the best solution, this algorithm does a binary expansion of the solution -/// space. First it solves the problem on a very sparse grid by skipping nRRow and nZColumn in the original -/// matrix. Then it doubles the number of points and solves the problem again. Then it doubles the -/// number of points and solves the problem again. This happens several times until the maximum number -/// of points has been included in the array. -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// So nRRow == 2**M + 1 and nZColumn == 2**N + 1. The number of nRRow and nZColumn can be different. -/// -/// Method for relaxation: S.O.R Weighted Jacobi -/// -/// \param matrixV TMatrixD& potential in matrix -/// \param matrixCharge TMatrixD& charge density in matrix (side effect -/// \param nRRow Int_t number of nRRow in the grid -/// \param nZColumn Int_t number of nZColumn in the grid -/// \param maxIteration Int_t maximum iteration for relaxation method -/// -/// \return A fixed number that has nothing to do with what the function does -/// -/// -/// Original code by Jim Thomas (STAR TPC Collaboration) -void AliTPCPoissonSolver::PoissonRelaxation2D(TMatrixD& matrixV, TMatrixD& matrixCharge, Int_t nRRow, Int_t nZColumn, - Int_t maxIteration) -{ - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t ratio = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); - - TMatrixD arrayEr(nRRow, nZColumn); - TMatrixD arrayEz(nRRow, nZColumn); - - //Check that number of nRRow and nZColumn is suitable for a binary expansion - - if (!IsPowerOfTwo(nRRow - 1)) { - Error("PoissonRelaxation2D", "PoissonRelaxation - Error in the number of nRRow. Must be 2**M - 1"); - return; - } - - if (!IsPowerOfTwo(nZColumn - 1)) { - Error("PoissonRelaxation2D", "PoissonRelaxation - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by relaxation technique - // Allow for different size grid spacing in R and Z directions - // Use a binary expansion of the size of the matrix to speed up the solution of the problem - - Int_t iOne = (nRRow - 1) / 4; - Int_t jOne = (nZColumn - 1) / 4; - - // Coarse until nLoop - Int_t nLoop = 1 + (int)(0.5 + TMath::Log2((double)TMath::Max(iOne, jOne))); - - // Loop while the matrix expands & the resolution increases. - for (Int_t count = 0; count < nLoop; count++) { - - Float_t tempGridSizeR = gridSizeR * iOne; - Float_t tempRatio = ratio * iOne * iOne / (jOne * jOne); - Float_t tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - - // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector<float> coefficient1(nRRow); - std::vector<float> coefficient2(nRRow); - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - coefficient1[i] = 1.0 + tempGridSizeR / (2 * radius); - coefficient2[i] = 1.0 - tempGridSizeR / (2 * radius); - } - - TMatrixD sumChargeDensity(nRRow, nZColumn); - - // average charge at the coarse point - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + iOne * gridSizeR; - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - if (iOne == 1 && jOne == 1) { - sumChargeDensity(i, j) = matrixCharge(i, j); - } else { - // Add up all enclosed charge density contributions within 1/2 unit in all directions - Float_t weight = 0.0; - Float_t sum = 0.0; - sumChargeDensity(i, j) = 0.0; - for (Int_t ii = i - iOne / 2; ii <= i + iOne / 2; ii++) { - for (Int_t jj = j - jOne / 2; jj <= j + jOne / 2; jj++) { - if (ii == i - iOne / 2 || ii == i + iOne / 2 || jj == j - jOne / 2 || jj == j + jOne / 2) { - weight = 0.5; - } else { - weight = 1.0; - } - sumChargeDensity(i, j) += matrixCharge(ii, jj) * weight * radius; - sum += weight * radius; - } - } - sumChargeDensity(i, j) /= sum; - } - sumChargeDensity(i, j) *= tempGridSizeR * tempGridSizeR; // just saving a step later on - } - } - - // Iterate on the current level - for (Int_t k = 1; k <= maxIteration; k++) { - // Solve Poisson's Equation - // Over-relaxation index, must be >= 1 but < 2. Arrange for it to evolve from 2 => 1 - // as iteration increase. - Float_t overRelax = 1.0 + TMath::Sqrt(TMath::Cos((k * TMath::PiOver2()) / maxIteration)); - Float_t overRelaxM1 = overRelax - 1.0; - Float_t overRelaxTemp4, overRelaxCoefficient5; - overRelaxTemp4 = overRelax * tempFourth; - overRelaxCoefficient5 = overRelaxM1 / overRelaxTemp4; - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - // S.O.R - // - matrixV(i, j) = (coefficient2[i] * matrixV(i - iOne, j) + tempRatio * (matrixV(i, j - jOne) + matrixV(i, j + jOne)) - overRelaxCoefficient5 * matrixV(i, j) + coefficient1[i] * matrixV(i + iOne, j) + sumChargeDensity(i, j)) * overRelaxTemp4; - } - } - - // if already at maxIteration - // TODO: stop when it converged - if (k == maxIteration) { - - // After full solution is achieved, copy low resolution solution into higher res array - // Interpolate solution - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - if (iOne > 1) { - matrixV(i + iOne / 2, j) = (matrixV(i + iOne, j) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j) = (matrixV(0, j) + matrixV(iOne, j)) / 2; - } - } - if (jOne > 1) { - matrixV(i, j + jOne / 2) = (matrixV(i, j + jOne) + matrixV(i, j)) / 2; - if (j == jOne) { - matrixV(i, j - jOne / 2) = (matrixV(i, 0) + matrixV(i, jOne)) / 2; - } - } - if (iOne > 1 && jOne > 1) { - matrixV(i + iOne / 2, j + jOne / 2) = (matrixV(i + iOne, j + jOne) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(0, j - jOne) + matrixV(iOne, j)) / 2; - } - if (j == jOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(i - iOne, 0) + matrixV(i, jOne)) / 2; - } - // Note that this leaves a point at the upper left and lower right corners uninitialized. - // -> Not a big deal. - } - } - } - } - } - - iOne = iOne / 2; - if (iOne < 1) { - iOne = 1; - } - jOne = jOne / 2; - if (jOne < 1) { - jOne = 1; - } - sumChargeDensity.Clear(); - } -} - -/// Solve Poisson's Equation by MultiGrid Technique in 2D (assuming cylindrical symmetry) -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// So nRRow == 2**M + 1 and nZColumn == 2**N + 1. The number of nRRow and nZColumn can be different. -/// -/// \param matrixV TMatrixD& potential in matrix -/// \param matrixCharge TMatrixD& charge density in matrix (side effect -/// \param nRRow Int_t number of nRRow -/// \param nZColumn Int_t number of nZColumn -/// \param maxIteration Int_t maximum iteration for relaxation method -/// -/// \return A fixed number that has nothing to do with what the function does -void AliTPCPoissonSolver::PoissonMultiGrid2D(TMatrixD& matrixV, TMatrixD& matrixCharge, Int_t nRRow, Int_t nZColumn) -{ - /// Geometry of TPC -- should be use AliTPCParams instead - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t ratio = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); - - Int_t nGridRow = 0; // number grid - Int_t nGridCol = 0; // number grid - Int_t nnRow; - Int_t nnCol; - - nnRow = nRRow; - while (nnRow >>= 1) { - nGridRow++; - } - - nnCol = nZColumn; - while (nnCol >>= 1) { - nGridCol++; - } - - //Check that number of nRRow and nZColumn is suitable for multi grid - if (!IsPowerOfTwo(nRRow - 1)) { - Error("PoissonMultiGrid2D", "PoissonMultiGrid - Error in the number of nRRow. Must be 2**M - 1"); - return; - } - if (!IsPowerOfTwo(nZColumn - 1)) { - Error("PoissonMultiGrid2D", "PoissonMultiGrid - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - - Int_t nLoop = TMath::Max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion - - Info("PoissonMultiGrid2D", "%s", Form("nGridRow=%d, nGridCol=%d, nLoop=%d, nMGCycle=%d", nGridRow, nGridCol, nLoop, fMgParameters.nMGCycle)); - - Float_t h, h2, radius; - Int_t iOne = 1; // in/dex - Int_t jOne = 1; // index - Int_t tnRRow = nRRow, tnZColumn = nZColumn; - Int_t count; - Float_t tempRatio, tempFourth; - - // Vector for storing multi grid array - std::vector<TMatrixD*> tvChargeFMG(nLoop); - std::vector<TMatrixD*> tvArrayV(nLoop); - std::vector<TMatrixD*> tvCharge(nLoop); - std::vector<TMatrixD*> tvResidue(nLoop); - - // Allocate memory for temporary grid - for (count = 1; count <= nLoop; count++) { - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - // if one just address to matrixV - tvResidue[count - 1] = new TMatrixD(tnRRow, tnZColumn); - if (count == 1) { - tvChargeFMG[count - 1] = &matrixCharge; - tvArrayV[count - 1] = &matrixV; - tvCharge[count - 1] = &matrixCharge; - } else { - tvArrayV[count - 1] = new TMatrixD(tnRRow, tnZColumn); - tvCharge[count - 1] = new TMatrixD(tnRRow, tnZColumn); - tvChargeFMG[count - 1] = new TMatrixD(tnRRow, tnZColumn); - Restrict2D(*tvChargeFMG[count - 1], *tvChargeFMG[count - 2], tnRRow, tnZColumn); - } - iOne = 2 * iOne; - jOne = 2 * jOne; - } - - /// full multi grid - if (fMgParameters.cycleType == kFCycle) { - - Info("PoissonMultiGrid2D", "Do full cycle"); - // FMG - // 1) Relax on the coarsest grid - iOne = iOne / 2; - jOne = jOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - h = gridSizeR * count; - h2 = h * h; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - - std::vector<float> coefficient1(tnRRow); - std::vector<float> coefficient2(tnRRow); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - Relax2D(*tvArrayV[nLoop - 1], *tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - - // Do VCycle from nLoop H to h - for (count = nLoop - 2; count >= 0; count--) { - - iOne = iOne / 2; - jOne = jOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - Interp2D(*tvArrayV[count], *tvArrayV[count + 1], tnRRow, tnZColumn); - // Copy the relax charge to the tvCharge - *tvCharge[count] = *tvChargeFMG[count]; //copy - //tvCharge[count]->Print(); - // Do V cycle - - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - - VCycle2D(nRRow, nZColumn, count + 1, nLoop, fMgParameters.nPre, fMgParameters.nPost, gridSizeR, ratio, tvArrayV, - tvCharge, tvResidue); - } - } - } else if (fMgParameters.cycleType == kVCycle) { - // 2. VCycle - Info("PoissonMultiGrid2D", "Do V cycle"); - - Int_t gridFrom = 1; - Int_t gridTo = nLoop; - - // Do MGCycle - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - VCycle2D(nRRow, nZColumn, gridFrom, gridTo, fMgParameters.nPre, fMgParameters.nPost, gridSizeR, ratio, tvArrayV, - tvCharge, tvResidue); - } - } else if (fMgParameters.cycleType == kWCycle) { - - // 3. W Cycle (TODO:) - - Int_t gridFrom = 1; - - //nLoop = nLoop >= 4 ? 4 : nLoop; - - Int_t gridTo = nLoop; - //Int_t gamma = 1; - - // Do MGCycle - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - WCycle2D(nRRow, nZColumn, gridFrom, gridTo, fMgParameters.gamma, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratio, tvArrayV, tvCharge, tvResidue); - } - } - - // Deallocate memory - for (count = nLoop; count >= 1; count--) { - // if one just address to matrixV - if (count > 1) { - delete tvArrayV[count - 1]; - delete tvCharge[count - 1]; - delete tvChargeFMG[count - 1]; - } - delete tvResidue[count - 1]; - } -} - -/// 3D - Solve Poisson's Equation in 3D by Relaxation Technique -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// -/// SYMMETRY = 0 if no phi symmetries, and no phi boundary conditions -/// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). -/// -/// \param matricesV TMatrixD** potential in 3D matrix -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method -/// \param symmetry Int_t symmetry or not -/// -void AliTPCPoissonSolver::PoissonRelaxation3D(TMatrixD** matricesV, TMatrixD** matricesCharge, - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Int_t symmetry) -{ - - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t ratioPhi = gridSizeR * gridSizeR / (gridSizePhi * gridSizePhi); - const Float_t ratioZ = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); - - Info("PoissonRelaxation3D", "%s", Form("in Poisson Solver 3D relaxation nRRow=%d, cols=%d, phiSlice=%d \n", nRRow, nZColumn, phiSlice)); - // Check that the number of nRRow and nZColumn is suitable for a binary expansion - if (!IsPowerOfTwo((nRRow - 1))) { - Error("PoissonRelaxation3D", "Poisson3DRelaxation - Error in the number of nRRow. Must be 2**M - 1"); - return; - } - if (!IsPowerOfTwo((nZColumn - 1))) { - Error("PoissonRelaxation3D", "Poisson3DRelaxation - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - if (phiSlice <= 3) { - Error("PoissonRelaxation3D", "Poisson3DRelaxation - Error in the number of phiSlice. Must be larger than 3"); - return; - } - if (phiSlice > 1000) { - Error("PoissonRelaxation3D", "Poisson3D phiSlice > 1000 is not allowed (nor wise) "); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by relaxation technique - // Allow for different size grid spacing in R and Z directions - // Use a binary expansion of the matrix to speed up the solution of the problem - - Int_t nLoop, mPlus, mMinus, signPlus, signMinus; - Int_t iOne = (nRRow - 1) / 4; - Int_t jOne = (nZColumn - 1) / 4; - nLoop = TMath::Max(iOne, jOne); // Calculate the number of nLoop for the binary expansion - nLoop = 1 + (int)(0.5 + TMath::Log2((double)nLoop)); // Solve for N in 2**N - - TMatrixD* matricesSumChargeDensity[1000]; // Create temporary arrays to store low resolution charge arrays - - std::vector<float> coefficient1( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector<float> coefficient2( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector<float> coefficient3( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector<float> coefficient4( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector<float> overRelaxCoefficient4(nRRow); // Do this the standard C++ way to avoid gcc extensions - std::vector<float> overRelaxCoefficient5(nRRow); // Do this the standard C++ way to avoid gcc extensions - for (Int_t i = 0; i < phiSlice; i++) { - matricesSumChargeDensity[i] = new TMatrixD(nRRow, nZColumn); - } - - ///// Test of Convergence - TMatrixD* prevArrayV[phiSlice]; - - for (Int_t m = 0; m < phiSlice; m++) { - prevArrayV[m] = new TMatrixD(nRRow, nZColumn); - } - ///// - - // START the master loop and do the binary expansion - for (Int_t count = 0; count < nLoop; count++) { - Float_t tempGridSizeR = gridSizeR * iOne; - Float_t tempRatioPhi = ratioPhi * iOne * iOne; - Float_t tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - coefficient1[i] = 1.0 + tempGridSizeR / (2 * radius); - coefficient2[i] = 1.0 - tempGridSizeR / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - for (Int_t m = 0; m < phiSlice; m++) { - TMatrixD& matrixCharge = *matricesCharge[m]; - TMatrixD& sumChargeDensity = *matricesSumChargeDensity[m]; - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - if (iOne == 1 && jOne == 1) { - sumChargeDensity(i, j) = matrixCharge(i, j); - } else { // Add up all enclosed charge density contributions within 1/2 unit in all directions - Float_t weight = 0.0; - Float_t sum = 0.0; - sumChargeDensity(i, j) = 0.0; - for (Int_t ii = i - iOne / 2; ii <= i + iOne / 2; ii++) { - for (Int_t jj = j - jOne / 2; jj <= j + jOne / 2; jj++) { - if (ii == i - iOne / 2 || ii == i + iOne / 2 || jj == j - jOne / 2 || jj == j + jOne / 2) { - weight = 0.5; - } else { - weight = 1.0; - } - sumChargeDensity(i, j) += matrixCharge(ii, jj) * weight * radius; - sum += weight * radius; - } - } - sumChargeDensity(i, j) /= sum; - } - sumChargeDensity(i, j) *= tempGridSizeR * tempGridSizeR; // just saving a step later on - } - } - } - - for (Int_t k = 1; k <= maxIteration; k++) { - if (count == nLoop - 1) { - //// Test of Convergence - for (Int_t m = 0; m < phiSlice; m++) { - (*prevArrayV[m]) = (*matricesV[m]); - } - //// - } - - Float_t overRelax = 1.0 + TMath::Sqrt(TMath::Cos((k * TMath::PiOver2()) / maxIteration)); - Float_t overRelaxM1 = overRelax - 1.0; - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - overRelaxCoefficient4[i] = overRelax * coefficient4[i]; - overRelaxCoefficient5[i] = overRelaxM1 / overRelaxCoefficient4[i]; - } - - for (Int_t m = 0; m < phiSlice; m++) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - if (symmetry == 1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } else if (symmetry == -1) { // Anti-symmetry in phi - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - TMatrixD& matrixV = *matricesV[m]; - TMatrixD& matrixVP = *matricesV[mPlus]; - TMatrixD& matrixVM = *matricesV[mMinus]; - TMatrixD& sumChargeDensity = *matricesSumChargeDensity[m]; - Double_t* matrixVFast = matrixV.GetMatrixArray(); - Double_t* matrixVPFast = matrixVP.GetMatrixArray(); - Double_t* matrixVMFast = matrixVM.GetMatrixArray(); - Double_t* sumChargeDensityFast = sumChargeDensity.GetMatrixArray(); - - if (fStrategy == kRelaxation) { - // slow implementation - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - - matrixV(i, j) = (coefficient2[i] * matrixV(i - iOne, j) + tempRatioZ * (matrixV(i, j - jOne) + matrixV(i, j + jOne)) - overRelaxCoefficient5[i] * matrixV(i, j) + coefficient1[i] * matrixV(i + iOne, j) + coefficient3[i] * (signPlus * matrixVP(i, j) + signMinus * matrixVM(i, j)) + sumChargeDensity(i, j)) * overRelaxCoefficient4[i]; - // Note: over-relax the solution at each step. This speeds up the convergence. - } - } - } else { - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Double_t* matrixVFastI = &(matrixVFast[i * nZColumn]); - Double_t* matrixVPFastI = &(matrixVPFast[i * nZColumn]); - Double_t* matrixVMFastI = &(matrixVMFast[i * nZColumn]); - Double_t* sumChargeDensityFastI = &(sumChargeDensityFast[i * nZColumn]); - - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - Double_t /*resSlow*/ resFast; - - resFast = (coefficient2[i] * matrixVFastI[j - nZColumn * iOne] + tempRatioZ * (matrixVFastI[j - jOne] + matrixVFastI[j + jOne]) - overRelaxCoefficient5[i] * matrixVFastI[j] + coefficient1[i] * matrixVFastI[j + nZColumn * iOne] + coefficient3[i] * (signPlus * matrixVPFastI[j] + signMinus * matrixVMFastI[j]) + sumChargeDensityFastI[j]) * overRelaxCoefficient4[i]; - matrixVFastI[j] = resFast; - // Note: over-relax the solution at each step. This speeds up the convergence. - } // end j - } //end i - } // end phi - - // After full solution is achieved, copy low resolution solution into higher res array - if (k == maxIteration) { - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - - if (iOne > 1) { - matrixV(i + iOne / 2, j) = (matrixV(i + iOne, j) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j) = (matrixV(0, j) + matrixV(iOne, j)) / 2; - } - } - if (jOne > 1) { - matrixV(i, j + jOne / 2) = (matrixV(i, j + jOne) + matrixV(i, j)) / 2; - if (j == jOne) { - matrixV(i, j - jOne / 2) = (matrixV(i, 0) + matrixV(i, jOne)) / 2; - } - } - if (iOne > 1 && jOne > 1) { - matrixV(i + iOne / 2, j + jOne / 2) = (matrixV(i + iOne, j + jOne) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(0, j - jOne) + matrixV(iOne, j)) / 2; - } - if (j == jOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(i - iOne, 0) + matrixV(i, jOne)) / 2; - } - // Note that this leaves a point at the upper left and lower right corners uninitialized. Not a big deal. - } - } - } - } - } - - if (count == nLoop - 1) { - - (*fErrorConvergenceNormInf)(k - 1) = GetConvergenceError(matricesV, prevArrayV, phiSlice); - (*fError)(k - 1) = GetExactError(matricesV, prevArrayV, phiSlice); - - // if error already achieved then stop mg iteration - fIterations = k - 1; - if ((*fErrorConvergenceNormInf)(k - 1) <= fgConvergenceError) { - Info("PoissonRelaxation3D", "%s", Form("Exact Err: %f, Iteration : %d", (*fError)(k - 1), k - 1)); - break; - } - if (k == maxIteration) { - Info("PoissonRelaxation3D", "%s", Form("Exact Err: %f, Iteration : %d", (*fError)(k - 1), k - 1)); - } - } - } - - iOne = iOne / 2; - if (iOne < 1) { - iOne = 1; - } - jOne = jOne / 2; - if (jOne < 1) { - jOne = 1; - } - } - - for (Int_t k = 0; k < phiSlice; k++) { - matricesSumChargeDensity[k]->Delete(); - } - - for (Int_t m = 0; m < phiSlice; m++) { - delete prevArrayV[m]; - } -} - -/// 3D - Solve Poisson's Equation in 3D by MultiGrid with constant phi slices -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ -/// -/// Algorithm for MultiGrid Full Cycle (FMG) -/// - Relax on the coarsest grid -/// - Do from coarsest to finest -/// - Interpolate potential from coarse -> fine -/// - Do V-Cycle to the current coarse level to the coarsest -/// - Stop if converged -/// -/// DeltaPhi in Radians -/// \param matricesV TMatrixD** potential in 3D matrix \f$ V(r,\phi,z) \f$ -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) \f$ - f(r,\phi,z) \f$ -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method (NOT USED) -/// \param symmetry Int_t symmetry (TODO for symmetry = 1) -// -/// SYMMETRY = 0 if no phi symmetries, and no phi boundary condition -/// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). -/// -void AliTPCPoissonSolver::PoissonMultiGrid3D2D(TMatrixD** matricesV, TMatrixD** matricesCharge, Int_t nRRow, - Int_t nZColumn, Int_t phiSlice, Int_t symmetry) -{ - - const Float_t gridSizeR = - (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); // h_{r} - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; // h_{phi} - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); // h_{z} - const Float_t ratioPhi = - gridSizeR * gridSizeR / (gridSizePhi * gridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - const Float_t ratioZ = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} - - // error tolerate - //const Float_t ERR = 1e-8; - Double_t convergenceError; - - Info("PoissonMultiGrid3D2D", "%s", Form("in Poisson Solver 3D multiGrid semi coarsening nRRow=%d, cols=%d, phiSlice=%d \n", nRRow, nZColumn, phiSlice)); - - // Check that the number of nRRow and nZColumn is suitable for a binary expansion - if (!IsPowerOfTwo((nRRow - 1))) { - Error("PoissonMultiGrid3D2D", "Poisson3DMultiGrid - Error in the number of nRRow. Must be 2**M + 1"); - return; - } - if (!IsPowerOfTwo((nZColumn - 1))) { - Error("PoissonMultiGrid3D2D", "Poisson3DMultiGrid - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - if (phiSlice <= 3) { - Error("PoissonMultiGrid3D2D", "Poisson3DMultiGrid - Error in the number of phiSlice. Must be larger than 3"); - return; - } - if (phiSlice > 1000) { - Error("PoissonMultiGrid3D2D", "Poisson3D phiSlice > 1000 is not allowed (nor wise) "); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by multiGrid technique - // Allow for different size grid spacing in R and Z directions - - Int_t nGridRow = 0; // number grid - Int_t nGridCol = 0; // number grid - Int_t nnRow; - Int_t nnCol; - - nnRow = nRRow; - while (nnRow >>= 1) { - nGridRow++; - } - nnCol = nZColumn; - while (nnCol >>= 1) { - nGridCol++; - } - - Int_t nLoop = TMath::Max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion - nLoop = (nLoop > fMgParameters.maxLoop) ? fMgParameters.maxLoop : nLoop; - Int_t count; - Int_t iOne = 1; // index i in gridSize r (original) - Int_t jOne = 1; // index j in gridSize z (original) - Int_t tnRRow = nRRow, tnZColumn = nZColumn; - std::vector<TMatrixD**> tvChargeFMG(nLoop); // charge is restricted in full multiGrid - std::vector<TMatrixD**> tvArrayV(nLoop); // potential <--> error - std::vector<TMatrixD**> tvCharge(nLoop); // charge <--> residue - std::vector<TMatrixD**> tvResidue(nLoop); // residue calculation - std::vector<TMatrixD**> tvPrevArrayV(nLoop); // error calculation - - for (count = 1; count <= nLoop; count++) { - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tvResidue[count - 1] = new TMatrixD*[phiSlice]; - tvPrevArrayV[count - 1] = new TMatrixD*[phiSlice]; - for (Int_t k = 0; k < phiSlice; k++) { - tvResidue[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvPrevArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - - // memory for the finest grid is from parameters - if (count == 1) { - tvChargeFMG[count - 1] = matricesCharge; - tvArrayV[count - 1] = matricesV; - tvCharge[count - 1] = matricesCharge; - } else { - // allocate for coarser grid - tvChargeFMG[count - 1] = new TMatrixD*[phiSlice]; - tvArrayV[count - 1] = new TMatrixD*[phiSlice]; - tvCharge[count - 1] = new TMatrixD*[phiSlice]; - for (Int_t k = 0; k < phiSlice; k++) { - tvArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvCharge[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvChargeFMG[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - Restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, phiSlice, phiSlice); - RestrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, phiSlice, phiSlice); - } - iOne = 2 * iOne; // doubling - jOne = 2 * jOne; // doubling - } - Float_t h, h2, radius; - Float_t tempRatioPhi, tempRatioZ; - std::vector<float> coefficient1( - nRRow); // coefficient1(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector<float> coefficient2( - nRRow); // coefficient2(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector<float> coefficient3( - nRRow); // coefficient3(nRRow) for storing (1/r_{i}^2) from central differences in phi direction - std::vector<float> coefficient4(nRRow); // coefficient4(nRRow) for storing 1/2 - std::vector<float> inverseCoefficient4(nRRow); // inverse of coefficient4(nRRow) - - // Case full multi grid (FMG) - if (fMgParameters.cycleType == kFCycle) { - - // 1) Relax on the coarsest grid - iOne = iOne / 2; - jOne = jOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - // relax on the coarsest level - Relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, - coefficient2, coefficient3, coefficient4); - // 2) Do multiGrid v-cycle from coarsest to finest - for (count = nLoop - 2; count >= 0; count--) { - // move to finer grid - iOne = iOne / 2; - jOne = jOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - // 2) a) Interpolate potential for h -> 2h (coarse -> fine) - Interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, phiSlice, phiSlice); - // 2) c) Copy the restricted charge to charge for calculation - for (Int_t m = 0; m < phiSlice; m++) { - *tvCharge[count][m] = *tvChargeFMG[count][m]; //copy - } - // 2) c) Do V cycle fMgParameters.nMGCycle times at most - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // Copy the potential to temp array for convergence calculation - for (Int_t m = 0; m < phiSlice; m++) { - *tvPrevArrayV[count][m] = *tvArrayV[count][m]; //copy - } - // 2) c) i) Call V cycle from grid count+1 (current fine level) to nLoop (coarsest) - VCycle3D2D(nRRow, nZColumn, phiSlice, symmetry, count + 1, nLoop, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, ratioPhi, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, - coefficient4, inverseCoefficient4); - - convergenceError = GetConvergenceError(tvArrayV[count], tvPrevArrayV[count], phiSlice); - - if (count == 0) { - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[count], phiSlice); - } - /// if already converge just break move to finer grid - if (convergenceError <= fgConvergenceError) { - fIterations = mgCycle + 1; - break; - } - } - } - } // Case V multi grid (VMG) - else if (fMgParameters.cycleType == kVCycle) { - Int_t gridFrom = 1; - Int_t gridTo = nLoop; - // do v cycle fMgParameters.nMGCycle from the coarsest to finest - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // copy to store previous potential - for (Int_t m = 0; m < phiSlice; m++) { - *tvPrevArrayV[0][m] = *tvArrayV[0][m]; //copy - } - // Do V Cycle for constant phiSlice - VCycle3D2D(nRRow, nZColumn, phiSlice, symmetry, gridFrom, gridTo, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, ratioPhi, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, - coefficient4, inverseCoefficient4); - - // convergence error - convergenceError = GetConvergenceError(tvArrayV[0], tvPrevArrayV[0], phiSlice); - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[0], phiSlice); - - // if error already achieved then stop mg iteration - if (convergenceError <= fgConvergenceError) { - fIterations = mgCycle + 1; - break; - } - } - } - // Deallocate memory - for (count = 1; count <= nLoop; count++) { - delete[] tvResidue[count - 1]; - delete[] tvPrevArrayV[count - 1]; - - if (count > 1) { - delete[] tvChargeFMG[count - 1]; - delete[] tvArrayV[count - 1]; - delete[] tvCharge[count - 1]; - } - } -} - -/// 3D - Solve Poisson's Equation in 3D in all direction by MultiGrid -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slices == Arbitrary but greater than 3 -/// -/// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ -/// -/// Algorithm for MultiGrid Full Cycle (FMG) -/// - Relax on the coarsest grid -/// - Do from coarsest to finest -/// - Interpolate potential from coarse -> fine -/// - Do V-Cycle to the current coarse level to the coarsest -/// - Stop if converged -/// -/// DeltaPhi in Radians -/// \param matricesV TMatrixD** potential in 3D matrix -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method -/// \param symmetry Int_t symmetry or not -// -/// SYMMETRY = 0 if no phi symmetries, and no phi boundary condition -/// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). -/// -void AliTPCPoissonSolver::PoissonMultiGrid3D(TMatrixD** matricesV, TMatrixD** matricesCharge, - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t symmetry) -{ - - const Float_t gridSizeR = - (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); // h_{r} - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); // h_{z} - const Float_t ratioZ = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} - - Float_t gridSizePhi = TMath::TwoPi() / phiSlice; // h_{phi} - Float_t h, h2, radius; - Float_t tempRatioPhi, tempRatioZ; - - Float_t convergenceError; // Convergence error - - Info("PoissonMultiGrid3D", "%s", Form("in Poisson Solver 3D multi grid full coarsening nRRow=%d, cols=%d, phiSlice=%d \n", nRRow, nZColumn, phiSlice)); - - // Check that the number of nRRow and nZColumn is suitable for a binary expansion - if (!IsPowerOfTwo((nRRow - 1))) { - Error("PoissonMultiGrid3D", "Poisson3DMultiGrid - Error in the number of nRRow. Must be 2**M + 1"); - return; - } - if (!IsPowerOfTwo((nZColumn - 1))) { - Error("PoissonMultiGrid3D", "Poisson3DMultiGrid - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - if (phiSlice <= 3) { - Error("PoissonMultiGrid3D", "Poisson3DMultiGrid - Error in the number of phiSlice. Must be larger than 3"); - return; - } - if (phiSlice > 1000) { - Error("PoissonMultiGrid3D", "Poisson3D phiSlice > 1000 is not allowed (nor wise) "); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by multi grid technique - // Allow for different size grid spacing in R and Z directions - - Int_t nGridRow = 0; // number grid - Int_t nGridCol = 0; // number grid - Int_t nGridPhi = 0; - - Int_t nnRow; - Int_t nnCol; - Int_t nnPhi; - - nnRow = nRRow; - while (nnRow >>= 1) { - nGridRow++; - } - - nnCol = nZColumn; - while (nnCol >>= 1) { - nGridCol++; - } - - nnPhi = phiSlice; - - while (nnPhi % 2 == 0) { - nGridPhi++; - nnPhi /= 2; - } - - Info("PoissonMultiGrid3D", "%s", Form("nGridRow=%d, nGridCol=%d, nGridPhi=%d", nGridRow, nGridCol, nGridPhi)); - Int_t nLoop = TMath::Max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion - nLoop = TMath::Max(nLoop, nGridPhi); - - // Vector for storing multi grid array - Int_t iOne = 1; // index i in gridSize r (original) - Int_t jOne = 1; // index j in gridSize z (original) - Int_t kOne = 1; // index k in gridSize phi - Int_t tnRRow = nRRow, tnZColumn = nZColumn, tPhiSlice = phiSlice, otPhiSlice; - - // 1) Memory allocation for multi grid - std::vector<TMatrixD**> tvChargeFMG(nLoop); // charge is restricted in full multiGrid - std::vector<TMatrixD**> tvArrayV(nLoop); // potential <--> error - std::vector<TMatrixD**> tvCharge(nLoop); // charge <--> residue - std::vector<TMatrixD**> tvResidue(nLoop); // residue calculation - std::vector<TMatrixD**> tvPrevArrayV(nLoop); // error calculation - - // these vectors for storing the coefficients in smoother - std::vector<float> coefficient1( - nRRow); // coefficient1(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector<float> coefficient2( - nRRow); // coefficient2(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector<float> coefficient3( - nRRow); // coefficient3(nRRow) for storing (1/r_{i}^2) from central differences in phi direction - std::vector<float> coefficient4(nRRow); // coefficient4(nRRow) for storing 1/2 - std::vector<float> inverseCoefficient4(nRRow); // inverse of coefficient4(nRRow) - - for (Int_t count = 1; count <= nLoop; count++) { - - // tnRRow,tnZColumn in new grid - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - // allocate memory for residue - tvResidue[count - 1] = new TMatrixD*[tPhiSlice]; - tvPrevArrayV[count - 1] = new TMatrixD*[tPhiSlice]; - for (Int_t k = 0; k < tPhiSlice; k++) { - tvResidue[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvPrevArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - - // memory for the finest grid is from parameters - if (count == 1) { - tvChargeFMG[count - 1] = matricesCharge; - tvArrayV[count - 1] = matricesV; - tvCharge[count - 1] = matricesCharge; - } else { - // allocate for coarser grid - tvChargeFMG[count - 1] = new TMatrixD*[tPhiSlice]; - tvArrayV[count - 1] = new TMatrixD*[tPhiSlice]; - tvCharge[count - 1] = new TMatrixD*[tPhiSlice]; - for (Int_t k = 0; k < tPhiSlice; k++) { - tvArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvCharge[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvChargeFMG[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - } - iOne = 2 * iOne; // doubling - jOne = 2 * jOne; // doubling - kOne = 2 * kOne; - } - - // Case full multi grid (FMG) - - if (fMgParameters.cycleType == kFCycle) { - // Restrict the charge to coarser grid - iOne = 2; - jOne = 2; - kOne = 2; - otPhiSlice = phiSlice; - - // 1) Restrict Charge and Boundary to coarser grid - for (Int_t count = 2; count <= nLoop; count++) { - // tnRRow,tnZColumn in new grid - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - Info("PoissonMultiGrid3D", "%s", Form("Restrict3D, tnRRow=%d, tnZColumn=%d, newPhiSlice=%d, oldPhiSlice=%d\n", tnRRow, tnZColumn, tPhiSlice, otPhiSlice)); - Restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - // copy boundary values of V - RestrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - otPhiSlice = tPhiSlice; - - iOne = 2 * iOne; // doubling - jOne = 2 * jOne; // doubling - kOne = 2 * kOne; - } - - // Relax on the coarsest grid - // FMG - // 2) Relax on the coarsest grid - - // move to the coarsest + 1 - iOne = iOne / 2; - jOne = jOne / 2; - kOne = kOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - otPhiSlice = tPhiSlice; - - h = gridSizeR * iOne; - h2 = h * h; - gridSizePhi = TMath::TwoPi() / tPhiSlice; // h_{phi} - tempRatioPhi = h * h / (gridSizePhi * gridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - // 3) Relax on the coarsest grid - Relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, - coefficient1, - coefficient2, coefficient3, coefficient4); - - // 4) V Cycle from coarsest to finest - for (Int_t count = nLoop - 2; count >= 0; count--) { - // move to finer grid - coefficient1.clear(); - coefficient2.clear(); - coefficient3.clear(); - coefficient4.clear(); - inverseCoefficient4.clear(); - - iOne = iOne / 2; - jOne = jOne / 2; - kOne = kOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - // 4) a) interpolate from 2h --> h grid - Interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - - // Copy the relax charge to the tvCharge - if (count > 0) { - for (Int_t m = 0; m < tPhiSlice; m++) { - *tvCharge[count][m] = *tvChargeFMG[count][m]; //copy - } - } - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // copy to store previous potential - for (Int_t m = 0; m < tPhiSlice; m++) { - *tvPrevArrayV[count][m] = *tvArrayV[count][m]; //copy - } - - VCycle3D(nRRow, nZColumn, phiSlice, symmetry, count + 1, nLoop, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, tvArrayV, - tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); - - /// converge error - convergenceError = GetConvergenceError(tvArrayV[count], tvPrevArrayV[count], tPhiSlice); - //// error counting ///// - if (count == 0) { - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[count], phiSlice); - } - /// if already converge just break move to finer grid - if (convergenceError <= fgConvergenceError) { - fIterations = mgCycle + 1; - break; - } - } - // keep old slice information - otPhiSlice = tPhiSlice; - } - - } else if (fMgParameters.cycleType == kVCycle) { - // V-cycle - Int_t gridFrom = 1; - Int_t gridTo = nLoop; - - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // copy to store previous potential - for (Int_t m = 0; m < phiSlice; m++) { - *tvPrevArrayV[0][m] = *tvArrayV[0][m]; //copy - } - // Do V Cycle from the coarsest to finest grid - VCycle3D(nRRow, nZColumn, phiSlice, symmetry, gridFrom, gridTo, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, tvArrayV, tvCharge, tvResidue, - coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); - // convergence error - convergenceError = GetConvergenceError(tvArrayV[0], tvPrevArrayV[0], phiSlice); - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[0], phiSlice); - // if error already achieved then stop mg iteration - if (convergenceError <= fgConvergenceError) { - //Info("PoissonMultiGrid3D",Form("Exact Err: %f, MG Iteration : %d", (*fError)(mgCycle), mgCycle)); - fIterations = mgCycle + 1; - break; - } - } - } - // deallocate memory for multiGrid - for (Int_t count = 1; count <= nLoop; count++) { - delete[] tvResidue[count - 1]; - delete[] tvPrevArrayV[count - 1]; - if (count > 1) { - delete[] tvChargeFMG[count - 1]; - delete[] tvArrayV[count - 1]; - delete[] tvCharge[count - 1]; - } - } -} - -/// Helper function to check if the integer is equal to a power of two -/// \param i Int_t the number -/// \return 1 if it is a power of two, else 0 -Int_t AliTPCPoissonSolver::IsPowerOfTwo(Int_t i) const -{ - Int_t j = 0; - while (i > 0) { - j += (i & 1); - i = (i >> 1); - } - if (j == 1) { - return (1); // True - } - return (0); // False -} - -/// Relax3D -/// -/// Relaxation operation for multiGrid -/// relaxation used 7 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param matricesCurrentV TMatrixD** potential in 3D (matrices of matrix) -/// \param matricesCurrentCharge TMatrixD** charge in 3D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param h2 const Float_t \f$ h_{r}^{2} \f$ -/// \param tempRatioZ const Float_t ration between grid size in z-direction and r-direction -/// \param coefficient1 std::vector<float> coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector<float> coefficient for \f$ V_{x-1,y,z} \f$ -/// \param coefficient3 std::vector<float> coefficient for z -/// \param coefficient4 std::vector<float> coefficient for f(r,\phi,z) -/// -void AliTPCPoissonSolver::Relax3D(TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentCharge, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t phiSlice, const Int_t symmetry, const Float_t h2, - const Float_t tempRatioZ, std::vector<float>& coefficient1, - std::vector<float>& coefficient2, - std::vector<float>& coefficient3, std::vector<float>& coefficient4) -{ - - Int_t mPlus, mMinus, signPlus, signMinus; - TMatrixD* matrixV; - TMatrixD* matrixVP; - TMatrixD* matrixVM; - TMatrixD* arrayCharge; - - // Gauss-Seidel (Read Black} - if (fMgParameters.relaxType == kGaussSeidel) { - // for each slice - Int_t isw, jsw, msw; - msw = 1; - for (Int_t iPass = 1; iPass <= 2; iPass++, msw = 3 - msw) { - jsw = msw; - for (Int_t m = 0; m < phiSlice; m++, jsw = 3 - jsw) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (symmetry == 1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } - // Anti-symmetry in phi - else if (symmetry == -1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - matrixV = matricesCurrentV[m]; - matrixVP = matricesCurrentV[mPlus]; // slice - matrixVM = matricesCurrentV[mMinus]; // slice - arrayCharge = matricesCurrentCharge[m]; - - isw = jsw; - for (Int_t j = 1; j < tnZColumn - 1; j++, isw = 3 - isw) { - for (Int_t i = isw; i < tnRRow - 1; i += 2) { - //Info("Relax3D",Form("Doing slice %d, z=%d, r=%d", m,j,i)); - (*matrixV)(i, j) = (coefficient2[i] * (*matrixV)(i - 1, j) + tempRatioZ * ((*matrixV)(i, j - 1) + (*matrixV)(i, j + 1)) + coefficient1[i] * (*matrixV)(i + 1, j) + coefficient3[i] * (signPlus * (*matrixVP)(i, j) + signMinus * (*matrixVM)(i, j)) + (h2 * (*arrayCharge)(i, j))) * coefficient4[i]; - } // end cols - } // end nRRow - } // end phi - } // end sweep - } else if (fMgParameters.relaxType == kJacobi) { - // for each slice - for (Int_t m = 0; m < phiSlice; m++) { - - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - - // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (symmetry == 1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } - // Anti-symmetry in phi - else if (symmetry == -1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - matrixV = matricesCurrentV[m]; - matrixVP = matricesCurrentV[mPlus]; // slice - matrixVM = matricesCurrentV[mMinus]; // slice - arrayCharge = matricesCurrentCharge[m]; - - // Jacobian - for (Int_t j = 1; j < tnZColumn - 1; j++) { - for (Int_t i = 1; i < tnRRow - 1; i++) { - (*matrixV)(i, j) = (coefficient2[i] * (*matrixV)(i - 1, j) + tempRatioZ * ((*matrixV)(i, j - 1) + (*matrixV)(i, j + 1)) + coefficient1[i] * (*matrixV)(i + 1, j) + coefficient3[i] * (signPlus * (*matrixVP)(i, j) + signMinus * (*matrixVM)(i, j)) + (h2 * (*arrayCharge)(i, j))) * coefficient4[i]; - - } // end cols - } // end nRRow - - } // end phi - - } else { - // Case weighted Jacobi - // TODO - } -} - -/// Relax2D -/// -/// Relaxation operation for multiGrid -/// relaxation used 5 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param matricesCurrentV TMatrixD& potential in 3D (matrices of matrix) -/// \param matricesCurrentCharge TMatrixD& charge in 3D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param h2 const Float_t \f$ h_{r}^{2} \f$ -/// \param tempFourth const Float_t coefficient for h -/// \param tempRatio const Float_t ratio between grid size in z-direction and r-direction -/// \param coefficient1 std::vector<float> coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector<float> coefficient for \f$ V_{x-1,y,z} \f$ -/// -void AliTPCPoissonSolver::Relax2D(TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentCharge, const Int_t tnRRow, - const Int_t tnZColumn, - const Float_t h2, const Float_t tempFourth, const Float_t tempRatio, - std::vector<float>& coefficient1, std::vector<float>& coefficient2) -{ - - // Gauss-Seidel - if (fMgParameters.relaxType == kGaussSeidel) { - - Int_t isw, jsw = 1; - for (Int_t iPass = 1; iPass <= 2; iPass++, jsw = 3 - jsw) { - isw = jsw; - for (Int_t j = 1; j < tnZColumn - 1; j++, isw = 3 - isw) { - for (Int_t i = isw; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j) + - coefficient2[i] * matricesCurrentV(i - 1, j) + - tempRatio * (matricesCurrentV(i, j + 1) + matricesCurrentV(i, j - 1)) + - (h2 * matricesCurrentCharge(i, j))); - } // end cols - } // end nRRow - } // end pass red-black - } else if (fMgParameters.relaxType == kJacobi) { - for (Int_t j = 1; j < tnZColumn - 1; j++) { - for (Int_t i = 1; i < tnRRow - 1; i++) { - matricesCurrentV(i, j) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j) + - coefficient2[i] * matricesCurrentV(i - 1, j) + tempRatio * (matricesCurrentV(i, j + 1) + matricesCurrentV(i, j - 1)) + - (h2 * matricesCurrentCharge(i, j))); - } // end cols - } // end nRRow - } else if (fMgParameters.relaxType == kWeightedJacobi) { - // Weighted Jacobi - // TODO - } -} - -/// Residue3D -/// -/// Compute residue from V(.) where V(.) is numerical potential and f(.). -/// residue used 7 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param residue TMatrixD** residue in 3D (matrices of matrix) -/// \param matricesCurrentV TMatrixD** potential in 3D (matrices of matrix) -/// \param matricesCurrentCharge TMatrixD** charge in 3D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param ih2 const Float_t \f$ 1/ h_{r}^{2} \f$ -/// \param tempRatioZ const Float_t ration between grid size in z-direction and r-direction -/// \param coefficient1 std::vector<float> coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector<float> coefficient for \f$ V_{x-1,y,z} \f$ -/// \param coefficient3 std::vector<float> coefficient for z -/// \param inverseCoefficient4 std::vector<float> inverse coefficient for f(r,\phi,z) -/// -void AliTPCPoissonSolver::Residue3D(TMatrixD** residue, TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentCharge, - const Int_t tnRRow, - const Int_t tnZColumn, const Int_t phiSlice, const Int_t symmetry, - const Float_t ih2, - const Float_t tempRatioZ, std::vector<float>& coefficient1, - std::vector<float>& coefficient2, - std::vector<float>& coefficient3, std::vector<float>& inverseCoefficient4) -{ - Int_t mPlus, mMinus, signPlus, signMinus; - for (Int_t m = 0; m < phiSlice; m++) { - - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - - // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (symmetry == 1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } - // Anti-symmetry in phi - else if (symmetry == -1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - TMatrixD& arrayResidue = *residue[m]; - TMatrixD& matrixV = *matricesCurrentV[m]; - TMatrixD& matrixVP = *matricesCurrentV[mPlus]; // slice - TMatrixD& matrixVM = *matricesCurrentV[mMinus]; // slice - TMatrixD& arrayCharge = *matricesCurrentCharge[m]; - - for (Int_t j = 1; j < tnZColumn - 1; j++) { - for (Int_t i = 1; i < tnRRow - 1; i++) { - - arrayResidue(i, j) = - ih2 * (coefficient2[i] * matrixV(i - 1, j) + tempRatioZ * (matrixV(i, j - 1) + matrixV(i, j + 1)) + coefficient1[i] * matrixV(i + 1, j) + - coefficient3[i] * (signPlus * matrixVP(i, j) + signMinus * matrixVM(i, j)) - - inverseCoefficient4[i] * matrixV(i, j)) + - arrayCharge(i, j); - - } // end cols - } // end nRRow - - //arrayResidue.Print(); - } -} - -/// Residue2D -/// -/// Compute residue from V(.) where V(.) is numerical potential and f(.). -/// residue used 5 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param residue TMatrixD& potential in 2D -/// \param matricesCurrentV TMatrixD& potential in 2D -/// \param matricesCurrentCharge TMatrixD& charge in 2D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param h2 const Float_t \f$ h_{r}^{2} \f$ -/// \param tempFourth const Float_t coefficient for h -/// \param tempRatio const Float_t ratio between grid size in z-direction and r-direction -/// \param coefficient1 std::vector<float> coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector<float> coefficient for \f$ V_{x-1,y,z} \f$ -/// -void AliTPCPoissonSolver::Residue2D(TMatrixD& residue, TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentCharge, - const Int_t tnRRow, - const Int_t tnZColumn, const Float_t ih2, const Float_t inverseTempFourth, - const Float_t tempRatio, std::vector<float>& coefficient1, - std::vector<float>& coefficient2) -{ - for (Int_t i = 1; i < tnRRow - 1; i++) { - for (Int_t j = 1; j < tnZColumn - 1; j++) { - residue(i, j) = ih2 * (coefficient1[i] * matricesCurrentV(i + 1, j) + coefficient2[i] * matricesCurrentV(i - 1, j) + tempRatio * (matricesCurrentV(i, j + 1) + matricesCurrentV(i, j - 1)) - - inverseTempFourth * matricesCurrentV(i, j)) + - matricesCurrentCharge(i, j); - - } // end cols - } // end nRRow - - //Boundary points. - for (Int_t i = 0; i < tnRRow; i++) { - residue(i, 0) = residue(i, tnZColumn - 1) = 0.0; - } - - for (Int_t j = 0; j < tnZColumn; j++) { - residue(0, j) = residue(tnRRow - 1, j) = 0.0; - } -} - -/// Restrict2D -/// -/// Grid transfer operator, restrict from fine -> coarse grid -/// provide full-half weighting -/// -/// \[ \frac{1}{16}\left( \begin{array}{ccc} -/// 1 & 2 & 1 \\ -/// 2 & 4 & 2 \\ -/// 1 & 2 & 1 \end{array} \right) \] -/// -/// \param matricesCurrentCharge TMatrixD& coarse grid (2h) -/// \param residue TMatrixD& fine grid (h) -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// -void AliTPCPoissonSolver::Restrict2D(TMatrixD& matricesCurrentCharge, TMatrixD& residue, const Int_t tnRRow, - const Int_t tnZColumn) -{ - - for (Int_t i = 1, ii = 2; i < tnRRow - 1; i++, ii += 2) { - for (Int_t j = 1, jj = 2; j < tnZColumn - 1; j++, jj += 2) { - if (fMgParameters.gtType == kHalf) { - // half - matricesCurrentCharge(i, j) = 0.5 * residue(ii, jj) + - 0.125 * - (residue(ii + 1, jj) + residue(ii - 1, jj) + residue(ii, jj + 1) + - residue(ii, jj - 1)); - - } else - // full - if (fMgParameters.gtType == kFull) { - matricesCurrentCharge(i, j) = 0.25 * residue(ii, jj) + - 0.125 * - (residue(ii + 1, jj) + residue(ii - 1, jj) + residue(ii, jj + 1) + - residue(ii, jj - 1)) + - 0.0625 * - (residue(ii + 1, jj + 1) + residue(ii - 1, jj + 1) + residue(ii + 1, jj - 1) + - residue(ii - 1, jj - 1)); - } - - } // end cols - } // end nRRow - - // boundary - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - matricesCurrentCharge(0, j) = residue(0, jj); - matricesCurrentCharge(tnRRow - 1, j) = residue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - matricesCurrentCharge(i, 0) = residue(ii, 0); - matricesCurrentCharge(i, tnZColumn - 1) = residue(ii, (tnZColumn - 1) * 2); - } -} - -/// RestrictBoundary2D -/// -/// Boundary transfer restrict from fine -> coarse grid -/// -/// \param matricesCurrentCharge TMatrixD& coarse grid (2h) -/// \param residue TMatrixD& fine grid (h) -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// -void AliTPCPoissonSolver::RestrictBoundary2D(TMatrixD& matricesCurrentCharge, TMatrixD& residue, const Int_t tnRRow, - const Int_t tnZColumn) -{ - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - matricesCurrentCharge(0, j) = residue(0, jj); - matricesCurrentCharge(tnRRow - 1, j) = residue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - matricesCurrentCharge(i, 0) = residue(ii, 0); - matricesCurrentCharge(i, tnZColumn - 1) = residue(ii, (tnZColumn - 1) * 2); - } -} - -/// Restriction in 3D -/// -/// Restriction is a map from fine grid (h) to coarse grid (2h) -/// -/// In case of 3D -/// Full weighting: -/// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] -/// -/// -/// Restriction in all direction r-phi-z -/// restriction in phi only if oldPhi == 2*newPhi -/// \param matricesCurrentCharge TMatrixD** coarser grid 2h -/// \param residue TMatrixD ** fine grid h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::Restrict3D(TMatrixD** matricesCurrentCharge, TMatrixD** residue, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - - Double_t s1, s2, s3; - - if (2 * newPhiSlice == oldPhiSlice) { - - Int_t mPlus, mMinus; - Int_t mm = 0; - - for (Int_t m = 0; m < newPhiSlice; m++, mm += 2) { - - // assuming no symmetry - mPlus = mm + 1; - mMinus = mm - 1; - - if (mPlus > (oldPhiSlice)-1) { - mPlus = mm + 1 - (oldPhiSlice); - } - if (mMinus < 0) { - mMinus = mm - 1 + (oldPhiSlice); - } - - TMatrixD& arrayResidue = *residue[mm]; - TMatrixD& arrayResidueP = *residue[mPlus]; - TMatrixD& arrayResidueM = *residue[mMinus]; // slice - TMatrixD& arrayCharge = *matricesCurrentCharge[m]; - - for (Int_t i = 1, ii = 2; i < tnRRow - 1; i++, ii += 2) { - for (Int_t j = 1, jj = 2; j < tnZColumn - 1; j++, jj += 2) { - - // at the same plane - s1 = arrayResidue(ii + 1, jj) + arrayResidue(ii - 1, jj) + arrayResidue(ii, jj + 1) + - arrayResidue(ii, jj - 1) + arrayResidueP(ii, jj) + arrayResidueM(ii, jj); - s2 = (arrayResidue(ii + 1, jj + 1) + arrayResidue(ii + 1, jj - 1) + arrayResidueP(ii + 1, jj) + - arrayResidueM(ii + 1, jj)) + - (arrayResidue(ii - 1, jj - 1) + arrayResidue(ii - 1, jj + 1) + arrayResidueP(ii - 1, jj) + - arrayResidueM(ii - 1, jj)) + - arrayResidueP(ii, jj - 1) + arrayResidueM(ii, jj + 1) + arrayResidueM(ii, jj - 1) + - arrayResidueP(ii, jj + 1); - - s3 = (arrayResidueP(ii + 1, jj + 1) + arrayResidueP(ii + 1, jj - 1) + arrayResidueM(ii + 1, jj + 1) + - arrayResidueM(ii + 1, jj - 1)) + - (arrayResidueM(ii - 1, jj - 1) + arrayResidueM(ii - 1, jj + 1) + arrayResidueP(ii - 1, jj - 1) + - arrayResidueP(ii - 1, jj + 1)); - - arrayCharge(i, j) = 0.125 * arrayResidue(ii, jj) + 0.0625 * s1 + 0.03125 * s2 + 0.015625 * s3; - } // end cols - } // end nRRow - - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - arrayCharge(0, j) = arrayResidue(0, jj); - arrayCharge(tnRRow - 1, j) = arrayResidue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - arrayCharge(i, 0) = arrayResidue(ii, 0); - arrayCharge(i, tnZColumn - 1) = arrayResidue(ii, (tnZColumn - 1) * 2); - } - } // end phis - - } else { - for (int m = 0; m < newPhiSlice; m++) { - Restrict2D(*matricesCurrentCharge[m], *residue[m], tnRRow, tnZColumn); - } - } -} - -/// Restrict Boundary in 3D -/// -/// Pass boundary information to coarse grid -/// -/// \param matricesCurrentCharge TMatrixD** coarser grid 2h -/// \param residue TMatrixD ** fine grid h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::RestrictBoundary3D(TMatrixD** matricesCurrentCharge, TMatrixD** residue, const Int_t tnRRow, - const Int_t tnZColumn, const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - - // in case of full 3d and the phiSlice is also coarsening - - if (2 * newPhiSlice == oldPhiSlice) { - - for (Int_t m = 0, mm = 0; m < newPhiSlice; m++, mm += 2) { - - TMatrixD& arrayResidue = *residue[mm]; - TMatrixD& arrayCharge = *matricesCurrentCharge[m]; - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - arrayCharge(0, j) = arrayResidue(0, jj); - arrayCharge(tnRRow - 1, j) = arrayResidue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - arrayCharge(i, 0) = arrayResidue(ii, 0); - arrayCharge(i, tnZColumn - 1) = arrayResidue(ii, (tnZColumn - 1) * 2); - } - } // end phis - } else { - for (int m = 0; m < newPhiSlice; m++) { - RestrictBoundary2D(*matricesCurrentCharge[m], *residue[m], tnRRow, tnZColumn); - } - } -} - -/// Prolongation with Addition for 2D -/// -/// Interpolation with addition from coarse level (2h) --> fine level (h) -/// -/// Interpolation in all direction r-phi-z -/// \param matricesCurrentV TMatrixD& fine grid h -/// \param matricesCurrentVC TMatrixD& coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a -/// -void AliTPCPoissonSolver::AddInterp2D(TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn) -{ - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = matricesCurrentV(i, j) + matricesCurrentVC(i / 2, j / 2); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = - matricesCurrentV(i, j) + 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = - matricesCurrentV(i, j) + 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2 + 1, j / 2)); - } - } - - // only if full - if (fMgParameters.gtType == kFull) { - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = - matricesCurrentV(i, j) + 0.25 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1) + matricesCurrentVC(i / 2 + 1, j / 2) + matricesCurrentVC(i / 2 + 1, j / 2 + 1)); - } - } - } -} - -/// Prolongation with Addition for 3D -/// -/// Interpolation with addition from coarse level (2h) --> fine level (h) -/// -/// Interpolation in all direction r-phi-z -/// Interpolation in phi only if oldPhi == 2*newPhi -/// \param matricesCurrentV TMatrixD& fine grid h -/// \param matricesCurrentVC TMatrixD& coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::AddInterp3D(TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - // Do restrict 2 D for each slice - - //const Float_t h = (AliTPCPoissonSolver::fgkOFCRadius-AliTPCPoissonSolver::fgkIFCRadius) / ((tnRRow-1)/2); // h_{r} - //Float_t radius,ratio; - //std::vector<float> coefficient1((tnRRow-1) / 2 ); // coefficient1(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - //std::vector<float> coefficient2((tnRRow-1) / 2); // coefficient2(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - - if (newPhiSlice == 2 * oldPhiSlice) { - Int_t mPlus, mmPlus; - Int_t mm = 0; - - for (Int_t m = 0; m < newPhiSlice; m += 2) { - - // assuming no symmetry - mm = m / 2; - mmPlus = mm + 1; - mPlus = m + 1; - - // round - if (mmPlus > (oldPhiSlice)-1) { - mmPlus = mm + 1 - (oldPhiSlice); - } - if (mPlus > (newPhiSlice)-1) { - mPlus = m + 1 - (newPhiSlice); - } - - TMatrixD& fineV = *matricesCurrentV[m]; - TMatrixD& fineVP = *matricesCurrentV[mPlus]; - TMatrixD& coarseV = *matricesCurrentVC[mm]; - TMatrixD& coarseVP = *matricesCurrentVC[mmPlus]; - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) += coarseV(i / 2, j / 2); - // point on corner lines at phi direction - fineVP(i, j) += 0.5 * (coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) += 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)); - // point on corner lines at phi direction - fineVP(i, j) += 0.25 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) += 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2 + 1, j / 2)); - - // point on line at phi direction - fineVP(i, j) += 0.25 * ((coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)) + - (coarseVP(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2))); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) += 0.25 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1))); - - // point at the center at phi direction - fineVP(i, j) += 0.125 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1) + - coarseVP(i / 2 + 1, j / 2) + coarseVP(i / 2 + 1, j / 2 + 1))); - } - } - } - - } else { - for (int m = 0; m < newPhiSlice; m++) { - AddInterp2D(*matricesCurrentV[m], *matricesCurrentVC[m], tnRRow, tnZColumn); - } - } -} - -/// Interpolation/Prolongation in 3D -/// -/// Interpolation is a map from coarse grid (h) to fine grid (2h) -/// -/// In case of 3D -/// Full weighting: -/// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] -/// -/// -/// Restriction in all direction r-phi-z -/// restriction in phi only if oldPhi == 2*newPhi -/// \param matricesCurrentV TMatrixD** finer grid h -/// \param curArrayCV TMatrixD ** coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::Interp3D(TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - - // Do restrict 2 D for each slice - if (newPhiSlice == 2 * oldPhiSlice) { - Int_t mPlus, mmPlus; - Int_t mm = 0; - - for (Int_t m = 0; m < newPhiSlice; m += 2) { - - // assuming no symmetry - mm = m / 2; - mmPlus = mm + 1; - mPlus = m + 1; - - // round - if (mmPlus > (oldPhiSlice)-1) { - mmPlus = mm + 1 - (oldPhiSlice); - } - if (mPlus > (newPhiSlice)-1) { - mPlus = m + 1 - (newPhiSlice); - } - - TMatrixD& fineV = *matricesCurrentV[m]; - TMatrixD& fineVP = *matricesCurrentV[mPlus]; - TMatrixD& coarseV = *matricesCurrentVC[mm]; - TMatrixD& coarseVP = *matricesCurrentVC[mmPlus]; - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) = coarseV(i / 2, j / 2); - - // point on corner lines at phi direction - fineVP(i, j) = 0.5 * (coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) = 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)); - - // point on corner lines at phi direction - fineVP(i, j) = 0.25 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) = 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2 + 1, j / 2)); - - // point on line at phi direction - fineVP(i, j) = 0.25 * ((coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)) + - (coarseVP(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2))); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) = 0.25 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1))); - - // point at the center at phi direction - fineVP(i, j) = 0.125 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1) + - coarseVP(i / 2 + 1, j / 2) + coarseVP(i / 2 + 1, j / 2 + 1))); - } - } - } - - } else { - for (int m = 0; m < newPhiSlice; m++) { - Interp2D(*matricesCurrentV[m], *matricesCurrentVC[m], tnRRow, tnZColumn); - } - } -} - -/// Interpolation/Prolongation in 2D -/// -/// Interpolation is a map from coarse grid (h) to fine grid (2h) -/// -/// In case of 2D -/// Full weighting: -/// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] -/// -/// -/// Restriction in all direction r-phi-z -/// \param matricesCurrentV TMatrixD** finer grid h -/// \param curArrayCV TMatrixD ** coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// -void AliTPCPoissonSolver::Interp2D(TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn) -{ - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = matricesCurrentVC(i / 2, j / 2); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2 + 1, j / 2)); - } - } - - // only if full - if (fMgParameters.gtType == kFull) { - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = 0.25 * - (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1) + - matricesCurrentVC(i / 2 + 1, j / 2) + - matricesCurrentVC(i / 2 + 1, j / 2 + 1)); - } - } - } -} - -/// V-Cycle 2D -/// -/// Implementation non-recursive V-cycle for 2D -/// -/// Algorithms: -/// -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector<TMatrixD *> vector of V potential in different grids -/// \param tvCharge vector<TMatrixD *> vector of charge distribution in different grids -/// \param tvResidue vector<TMatrixD *> vector of residue calculation in different grids -/// -void AliTPCPoissonSolver::VCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, - std::vector<TMatrixD*>& tvArrayV, - std::vector<TMatrixD*>& tvCharge, std::vector<TMatrixD*>& tvResidue) -{ - - Float_t h, h2, ih2, tempRatio, tempFourth, inverseTempFourth, radius; - TMatrixD *matricesCurrentV, *matricesCurrentVC; - TMatrixD* matricesCurrentCharge; - TMatrixD* residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - std::vector<float> coefficient1(nRRow); - std::vector<float> coefficient2(nZColumn); - - // 1) Go to coarsest level - for (count = gridFrom; count <= gridTo - 1; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } - - // 2) Residue calculation - Residue2D(*residue, *matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, - coefficient1, - coefficient2); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - Restrict2D(*matricesCurrentCharge, *residue, tnRRow, tnZColumn); - - //4) Zeroing coarser V - matricesCurrentV->Zero(); - } - - // 5) coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - - // Go to finest grid - for (count = gridTo - 1; count >= gridFrom; count--) { - - iOne = iOne / 2; - jOne = jOne / 2; - - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 6) Interpolation/Prolongation - AddInterp2D(*matricesCurrentV, *matricesCurrentVC, tnRRow, tnZColumn); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - // 7) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } // end post smoothing - - //// DEBUG //// - //Info("VCycle2D",Form("Count %d", count)); - //Info("VCycle2D",Form("Exact Err: %f, MG Iteration : %d", (*fError)(mgCycle), mgCycle)); - //matricesCurrentV->Print(); - //matricesCurrentCharge->Print(); - } -} - -/// W-Cycle 2D -/// -/// Implementation non-recursive W-cycle for 2D -/// -/// Algorithms: -/// -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param gamma const Int_t number of iterations at coarsest level -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector<TMatrixD *> vector of V potential in different grids -/// \param tvCharge vector<TMatrixD *> vector of charge distribution in different grids -/// \param tvResidue vector<TMatrixD *> vector of residue calculation in different grids -/// -void AliTPCPoissonSolver::WCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, - const int gamma, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, - std::vector<TMatrixD*>& tvArrayV, - std::vector<TMatrixD*>& tvCharge, std::vector<TMatrixD*>& tvResidue) -{ - - Float_t h, h2, ih2, tempRatio, tempFourth, inverseTempFourth, radius; - TMatrixD *matricesCurrentV, *matricesCurrentVC; - TMatrixD* matricesCurrentCharge; - TMatrixD* residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - std::vector<float> coefficient1(nRRow); - std::vector<float> coefficient2(nZColumn); - - // 1) Go to coarsest level - for (count = gridFrom; count <= gridTo - 2; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } - - // 2) Residue calculation - Residue2D(*residue, *matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, - coefficient1, - coefficient2); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - Restrict2D(*matricesCurrentCharge, *residue, tnRRow, tnZColumn); - - //4) Zeroing coarser V - matricesCurrentV->Zero(); - } - - // Do V cycle from: gridTo-1 to gridTo gamma times - for (Int_t iGamma = 0; iGamma < gamma; iGamma++) { - VCycle2D(nRRow, nZColumn, gridTo - 1, gridTo, - nPre, nPost, gridSizeR, ratio, tvArrayV, - tvCharge, tvResidue); - } - - // Go to finest grid - for (count = gridTo - 2; count >= gridFrom; count--) { - - iOne = iOne / 2; - jOne = jOne / 2; - - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 6) Interpolation/Prolongation - AddInterp2D(*matricesCurrentV, *matricesCurrentVC, tnRRow, tnZColumn); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - // 7) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } // end post smoothing - } -} - -/// VCycle 3D2D, V Cycle 3D in multiGrid with constant phiSlice -/// fine-->coarsest-->fine, propagating the residue to correct initial guess of V -/// -/// Algorithm: -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector<TMatrixD *> vector of V potential in different grids -/// \param tvCharge vector<TMatrixD *> vector of charge distribution in different grids -/// \param tvResidue vector<TMatrixD *> vector of residue calculation in different grids -/// \param coefficient1 std::vector<float>& coefficient for relaxation (r direction) -/// \param coefficient2 std::vector<float>& coefficient for relaxation (r direction) -/// \param coefficient3 std::vector<float>& coefficient for relaxation (ratio r/z) -/// \param coefficient4 std::vector<float>& coefficient for relaxation (ratio for grid_r) -/// \param inverseCoefficient4 std::vector<float>& coefficient for relaxation (inverse coefficient4) -/// -void AliTPCPoissonSolver::VCycle3D2D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, const Int_t nPost, - const Float_t gridSizeR, const Float_t ratioZ, const Float_t ratioPhi, - std::vector<TMatrixD**>& tvArrayV, std::vector<TMatrixD**>& tvCharge, - std::vector<TMatrixD**>& tvResidue, std::vector<float>& coefficient1, - std::vector<float>& coefficient2, std::vector<float>& coefficient3, - std::vector<float>& coefficient4, - std::vector<float>& inverseCoefficient4) -{ - - Float_t h, h2, ih2, tempRatioZ, tempRatioPhi, radius; - TMatrixD **matricesCurrentV, **matricesCurrentVC; - TMatrixD** matricesCurrentCharge; - TMatrixD** residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - for (count = gridFrom; count <= gridTo - 1; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - inverseCoefficient4[i] = 1.0 / coefficient4[i]; - } - - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - //Info("VCycle3D2D","Before Pre-smoothing"); - //matricesCurrentV->Print(); - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end pre smoothing - - // 2) Residue calculation - Residue3D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, ih2, tempRatioZ, - coefficient1, - coefficient2, - coefficient3, inverseCoefficient4); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - //Restrict2D(*matricesCurrentCharge,*residue,tnRRow,tnZColumn); - Restrict3D(matricesCurrentCharge, residue, tnRRow, tnZColumn, phiSlice, phiSlice); - - //4) Zeroing coarser V - for (Int_t m = 0; m < phiSlice; m++) { - matricesCurrentV[m]->Zero(); - } - } - - // coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 3) Relax on the coarsest grid - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, coefficient1, - coefficient2, - coefficient3, coefficient4); - - // back to fine - for (count = gridTo - 1; count >= gridFrom; count--) { - iOne = iOne / 2; - jOne = jOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 4) Interpolation/Prolongation - AddInterp3D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, phiSlice, phiSlice); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 5) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end post smoothing - } -} - -/// VCycle 3D, V Cycle in multiGrid, fine-->coarsest-->fine, propagating the residue to correct initial guess of V -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param symmetry Int_t symmetry or not -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratioz const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector<TMatrixD *> vector of V potential in different grids -/// \param tvCharge vector<TMatrixD *> vector of charge distribution in different grids -/// \param tvResidue vector<TMatrixD *> vector of residue calculation in different grids -/// \param coefficient1 std::vector<float>& coefficient for relaxation (r direction) -/// \param coefficient2 std::vector<float>& coefficient for relaxation (r direction) -/// \param coefficient3 std::vector<float>& coefficient for relaxation (ratio r/z) -/// \param coefficient4 std::vector<float>& coefficient for relaxation (ratio for grid_r) -/// \param inverseCoefficient4 std::vector<float>& coefficient for relaxation (inverse coefficient4) -/// -void AliTPCPoissonSolver::VCycle3D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratioZ, - std::vector<TMatrixD**>& tvArrayV, std::vector<TMatrixD**>& tvCharge, - std::vector<TMatrixD**>& tvResidue, - std::vector<float>& coefficient1, std::vector<float>& coefficient2, - std::vector<float>& coefficient3, - std::vector<float>& coefficient4, std::vector<float>& inverseCoefficient4) -{ - - Float_t h, h2, ih2, tempRatioZ, tempRatioPhi, radius, tempGridSizePhi; - TMatrixD **matricesCurrentV, **matricesCurrentVC; - TMatrixD** matricesCurrentCharge; - TMatrixD** residue; - Int_t iOne, jOne, kOne, tnRRow, tnZColumn, tPhiSlice, otPhiSlice, count, nnPhi; - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - kOne = 1 << (gridFrom - 1); - - nnPhi = phiSlice; - - while (nnPhi % 2 == 0) { - nnPhi /= 2; - } - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - //Info("VCycle3D",Form("Grid information: tnRRow=%d, tcols=%d, tPhiSlice=%d\n", tnRRow,tnZColumn,tPhiSlice)); - - for (count = gridFrom; count <= gridTo - 1; count++) { - otPhiSlice = tPhiSlice; - - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempGridSizePhi = TMath::TwoPi() / tPhiSlice; // phi now is multiGrid - - tempRatioPhi = h * h / (tempGridSizePhi * tempGridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - inverseCoefficient4[i] = 1.0 / coefficient4[i]; - } - - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - //Info("VCycle3D","Before Pre-smoothing"); - //matricesCurrentV->Print(); - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, coefficient3, coefficient4); - } // end pre smoothing - - // 2) Residue calculation - - Residue3D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, ih2, tempRatioZ, - coefficient1, coefficient2, coefficient3, inverseCoefficient4); - - iOne = 2 * iOne; - jOne = 2 * jOne; - kOne = 2 * kOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - //3) Restriction - Restrict3D(matricesCurrentCharge, residue, tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - - //4) Zeroing coarser V - for (Int_t m = 0; m < tPhiSlice; m++) { - matricesCurrentV[m]->Zero(); - } - } - - // coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - tempGridSizePhi = TMath::TwoPi() / tPhiSlice; // phi now is multiGrid - - tempRatioPhi = h * h / (tempGridSizePhi * tempGridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 3) Relax on the coarsest grid - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, - coefficient2, coefficient3, coefficient4); - - // back to fine - for (count = gridTo - 1; count >= gridFrom; count--) { - otPhiSlice = tPhiSlice; - - iOne = iOne / 2; - jOne = jOne / 2; - kOne = kOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - h = gridSizeR * iOne; - h2 = h * h; - tempGridSizePhi = TMath::TwoPi() / tPhiSlice; // phi now is multiGrid - - tempRatioPhi = h * h / (tempGridSizePhi * tempGridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 4) Interpolation/Prolongation - - AddInterp3D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 5) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } - } -} - -/// -/// Set matrix exact solution for relative error calculation -/// -/// \param exactSolution TMatrixD** pointer to exact solution (potential) in 3D -/// \param fPhiSlices const Int_t number of phi slices -/// -void AliTPCPoissonSolver::SetExactSolution(TMatrixD** exactSolution, const Int_t fPhiSlices) -{ - Double_t maxAbs; - fExactSolution = exactSolution; - fExactPresent = kTRUE; - fMaxExact = 0.0; - for (Int_t m = 0; m < fPhiSlices; m++) { - maxAbs = TMath::Max(TMath::Abs((*fExactSolution[m]).Max()), TMath::Abs((*fExactSolution[m]).Min())); - if (maxAbs > fMaxExact) { - fMaxExact = maxAbs; - } - } -} - -/// -/// Relative error calculation: comparison with exact solution -/// -/// \param matricesCurrentV TMatrixD** current potential (numerical solution) -/// \param tempArrayV TMatrixD** temporary matrix for calculating error -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param phiSlice const Int_t phi slices -/// -Double_t AliTPCPoissonSolver::GetExactError(TMatrixD** matricesCurrentV, TMatrixD** tempArrayV, const Int_t phiSlice) -{ - Double_t error = 0.0; - - if (fExactPresent == kTRUE) { - for (Int_t m = 0; m < phiSlice; m++) { - (*tempArrayV[m]) = (*fExactSolution[m]) - (*matricesCurrentV[m]); - (*tempArrayV[m]) *= 1.0 / GetMaxExact(); - if (tempArrayV[m]->E2Norm() > error) { - error = tempArrayV[m]->E2Norm(); - } - //printf("%f\n",tempArrayV[m]->E2Norm(); - } - } - return error; -} - -/// -/// Relative error calculation: comparison with exact solution -/// -/// \param matricesCurrentV TMatrixD** current potential (numerical solution) -/// \param tempArrayV TMatrixD** temporary matrix for calculating error -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param phiSlice const Int_t phi slices -/// -Double_t - AliTPCPoissonSolver::GetConvergenceError(TMatrixD** matricesCurrentV, TMatrixD** prevArrayV, const Int_t phiSlice) -{ - Double_t error = 0.0; - - for (Int_t m = 0; m < phiSlice; m++) { - - // absolute - (*prevArrayV[m]) = (*prevArrayV[m]) - (*matricesCurrentV[m]); - - if (prevArrayV[m]->E2Norm() > error) { - error = prevArrayV[m]->E2Norm(); - } - } - return error; -} - -///////////////////// interface for GPU /////////////////// - -/// VCycle 3D2D, V Cycle 3D in multiGrid with constant phiSlice -/// fine-->coarsest-->fine, propagating the residue to correct initial guess of V -/// -/// Algorithm: -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector<TMatrixD *> vector of V potential in different grids -/// \param tvCharge vector<TMatrixD *> vector of charge distribution in different grids -/// \param tvResidue vector<TMatrixD *> vector of residue calculation in different grids -/// \param coefficient1 std::vector<float>& coefficient for relaxation (r direction) -/// \param coefficient2 std::vector<float>& coefficient for relaxation (r direction) -/// \param coefficient3 std::vector<float>& coefficient for relaxation (ratio r/z) -/// \param coefficient4 std::vector<float>& coefficient for relaxation (ratio for grid_r) -/// \param inverseCoefficient4 std::vector<float>& coefficient for relaxation (inverse coefficient4) -/// -void AliTPCPoissonSolver::VCycle3D2DGPU( - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, - const Float_t ratioZ, const Float_t ratioPhi, std::vector<TMatrixD**>& tvArrayV, - std::vector<TMatrixD**>& tvCharge, std::vector<TMatrixD**>& tvResidue, std::vector<float>& coefficient1, - std::vector<float>& coefficient2, std::vector<float>& coefficient3, std::vector<float>& coefficient4, - std::vector<float>& inverseCoefficient4) -{ - Float_t h, h2, ih2, tempRatioZ, tempRatioPhi, radius; - TMatrixD **matricesCurrentV, **matricesCurrentVC; - TMatrixD** matricesCurrentCharge; - TMatrixD** residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - for (count = gridFrom; count <= gridTo - 1; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - inverseCoefficient4[i] = 1.0 / coefficient4[i]; - } - - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - //Info("VCycle3D2DGPU","Before Pre-smoothing"); - //matricesCurrentV->Print(); - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end pre smoothing - - // 2) Residue calculation - Residue3D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, ih2, tempRatioZ, - coefficient1, - coefficient2, - coefficient3, inverseCoefficient4); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - //Restrict2D(*matricesCurrentCharge,*residue,tnRRow,tnZColumn); - Restrict3D(matricesCurrentCharge, residue, tnRRow, tnZColumn, phiSlice, phiSlice); - - //4) Zeroing coarser V - for (Int_t m = 0; m < phiSlice; m++) { - matricesCurrentV[m]->Zero(); - } - } - - // coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 3) Relax on the coarsest grid - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, coefficient1, - coefficient2, - coefficient3, coefficient4); - - // back to fine - for (count = gridTo - 1; count >= gridFrom; count--) { - iOne = iOne / 2; - jOne = jOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 4) Interpolation/Prolongation - AddInterp3D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, phiSlice, phiSlice); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 5) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end post smoothing - } -} diff --git a/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.h b/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.h deleted file mode 100644 index 5aead1e7b555c..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.h +++ /dev/null @@ -1,219 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCPoissonSolver.h -/// \brief This class provides implementation of Poisson Eq -/// solver by MultiGrid Method -/// -/// -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#ifndef ALITPCPOISSONSOLVER_H -#define ALITPCPOISSONSOLVER_H - -#include <TNamed.h> -#include "TMatrixD.h" -#include "TVectorD.h" - -class AliTPCPoissonSolver : public TNamed -{ - public: - ///< Enumeration of Poisson Solver Strategy Type - enum StrategyType { - kRelaxation = 0, ///< S.O.R Cascaded MultiGrid - kMultiGrid = 1, ///< Geometric MG - kFastRelaxation = 2 ///< Spectral (TODO) - }; - - ///< Enumeration of Cycles Type - enum CycleType { - kVCycle = 0, ///< V Cycle - kWCycle = 1, ///< W Cycle (TODO) - kFCycle = 2 ///< Full Cycle - }; - - ///< Fine -> Coarse Grid transfer operator types - enum GridTransferType { - kHalf = 0, ///< Half weighting - kFull = 1, ///< Full weighting - }; - - ///< Smoothing (Relax) operator types - enum RelaxType { - kJacobi = 0, ///< Jacobi (5 Stencil 2D, 7 Stencil 3D_ - kWeightedJacobi = 1, ///< (TODO) - kGaussSeidel = 2 ///< Gauss Seidel 2D (2 Color, 5 Stencil), 3D (7 Stencil) - }; - - ///< Coarse -> fine operator types (TODO: Interp and Restrict in one packet, just one enumeration) - enum InterpType { - kHalfInterp = 0, ///< Half bi linear interpolation - kFullInterp = 1 ///< Full bi linear interpolation - }; - - ///< Parameters choice for MultiGrid algorithm - struct MGParameters { - Bool_t isFull3D; ///< TRUE: full coarsening, FALSE: semi coarsening - CycleType cycleType; ///< cycleType follow CycleType - GridTransferType gtType; ///< gtType grid transfer type follow GridTransferType - RelaxType relaxType; ///< relaxType follow RelaxType - Int_t gamma; ///< number of iteration at coarsest level - Int_t nPre; ///< number of iteration for pre smoothing - Int_t nPost; ///< number of iteration for post smoothing - Int_t nMGCycle; ///< number of multi grid cycle (V type) - Int_t maxLoop; ///< the number of tree-deep of multi grid - - // default values - MGParameters() - { - isFull3D = kFALSE; - cycleType = kFCycle; - gtType = kFull; // default full - relaxType = kGaussSeidel; // default relaxation method - nPre = 2; - nPost = 2; - nMGCycle = 200; - maxLoop = 6; - } - }; - - AliTPCPoissonSolver(); - AliTPCPoissonSolver(const char* name, const char* title); -#if (defined(__CINT__) || defined(__ROOTCINT__)) && !defined(__CLING__) - ~AliTPCPoissonSolver(); -#else - ~AliTPCPoissonSolver() override; -#endif - void PoissonSolver2D(TMatrixD& matrixV, TMatrixD& chargeDensity, Int_t nRRow, Int_t nZColumn, Int_t maxIterations); - void PoissonSolver3D(TMatrixD** matricesV, TMatrixD** matricesChargeDensities, Int_t nRRow, Int_t nZColumn, - Int_t phiSlice, Int_t maxIterations, Int_t symmetry); - - void SetStrategy(StrategyType strategy) { fStrategy = strategy; } - StrategyType GetStrategy() { return fStrategy; } - - static const Double_t fgkTPCZ0; ///< nominal gating grid position - static const Double_t fgkIFCRadius; ///< Mean Radius of the Inner Field Cage ( 82.43 min, 83.70 max) (cm) - static const Double_t fgkOFCRadius; ///< Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm) - static const Double_t fgkZOffSet; ///< Offset from CE: calculate all distortions closer to CE as if at this point - static const Double_t fgkCathodeV; ///< Cathode Voltage (volts) - static const Double_t fgkGG; ///< Gating Grid voltage (volts) - static const Double_t fgkdvdE; ///< [cm/V] drift velocity dependency on the E field (from Magboltz for NeCO2N2 at standard environment) - static const Double_t fgkEM; ///< charge/mass in [C/kg] - static const Double_t fgke0; ///< vacuum permittivity [A·s/(V·m)] - - static Double_t fgExactErr; ///< Error tolerated - static Double_t fgConvergenceError; ///< Error tolerated - Int_t fIterations; ///< number of maximum iteration - MGParameters fMgParameters; ///< parameters multi grid - - void SetExactSolution(TMatrixD** exactSolution, const Int_t fPhiSlices); - void SetCycleType(AliTPCPoissonSolver::CycleType cycleType) { fMgParameters.cycleType = cycleType; } - - private: - AliTPCPoissonSolver(const AliTPCPoissonSolver&); // not implemented - AliTPCPoissonSolver& operator=(const AliTPCPoissonSolver&); // not implemented - StrategyType fStrategy = kMultiGrid; ///< strategy used default multiGrid - TMatrixD** fExactSolution = nullptr; //!<! Pointer to exact solution - /// TODO: remove pointers? - TVectorD* fErrorConvergenceNorm2; ///< for storing convergence error norm2 - TVectorD* fErrorConvergenceNormInf; ///< for storing convergence error normInf - TVectorD* fError; ///< for storing error - Double_t GetMaxExact() { return fMaxExact; }; - - void PoissonRelaxation2D(TMatrixD& matrixV, TMatrixD& chargeDensity, Int_t nRRow, Int_t nZColumn, - Int_t maxIterations); - void PoissonRelaxation3D(TMatrixD** matricesV, TMatrixD** matricesChargeDensities, Int_t nRRow, - Int_t nZColumn, Int_t phiSlice, Int_t maxIterations, Int_t symmetry); - void PoissonMultiGrid2D(TMatrixD& matrixV, TMatrixD& chargeDensity, Int_t nRRow, Int_t nZColumn); - void PoissonMultiGrid3D2D(TMatrixD** matricesV, TMatrixD** matricesChargeDensities, Int_t nRRow, - Int_t nZColumn, Int_t phiSlice, Int_t symmetry); - void PoissonMultiGrid3D(TMatrixD** matricesV, TMatrixD** matricesChargeDensities, Int_t nRRow, - Int_t nZColumn, Int_t phiSlice, Int_t symmetry); - Int_t IsPowerOfTwo(Int_t i) const; - void Relax2D(TMatrixD& matrixV, TMatrixD& matrixCharge, const Int_t tnRRow, const Int_t tnZColumn, - const Float_t h2, const Float_t tempFourth, const Float_t tempRatio, - std::vector<float>& vectorCoefficient1, - std::vector<float>& vectorCoefficient2); - void Relax3D(TMatrixD** currentMatricesV, TMatrixD** matricesCharge, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t phiSlice, const Int_t symmetry, const Float_t h2, const Float_t tempRatioZ, - std::vector<float>& vectorCoefficient1, std::vector<float>& vectorCoefficient2, - std::vector<float>& vectorCoefficient3, - std::vector<float>& vectorCoefficient4); - void Residue2D(TMatrixD& residue, TMatrixD& matrixV, TMatrixD& matrixCharge, - const Int_t tnRRow, const Int_t tnZColumn, const Float_t ih2, const Float_t iTempFourth, - const Float_t tempRatio, std::vector<float>& vectorCoefficient1, - std::vector<float>& vectorCoefficient2); - void Residue3D(TMatrixD** residue, TMatrixD** currentMatricesV, TMatrixD** matricesCharge, const Int_t tnRRow, - const Int_t tnZColumn, const Int_t phiSlice, const Int_t symmetry, const Float_t ih2, - const Float_t tempRatio, std::vector<float>& vectorCoefficient1, - std::vector<float>& vectorCoefficient2, - std::vector<float>& vectorCoefficient3, std::vector<float>& vectorInverseCoefficient4); - void Restrict2D(TMatrixD& matrixCharge, TMatrixD& residue, const Int_t tnRRow, const Int_t tnZColumn); - void Restrict3D(TMatrixD** matricesCharge, TMatrixD** residue, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - void RestrictBoundary2D(TMatrixD& matrixCharge, TMatrixD& residue, const Int_t tnRRow, const Int_t tnZColumn); - void RestrictBoundary3D(TMatrixD** matricesCharge, TMatrixD** residue, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - - void AddInterp2D(TMatrixD& matrixV, TMatrixD& matrixVC, const Int_t tnRRow, const Int_t tnZColumn); - void AddInterp3D(TMatrixD** currentMatricesV, TMatrixD** currentMatricesVC, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - void Interp2D(TMatrixD& matrixV, TMatrixD& matrixVC, const Int_t tnRRow, const Int_t tnZColumn); - - void Interp3D(TMatrixD** currentMatricesV, TMatrixD** currentMatricesVC, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - void VCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, - const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, std::vector<TMatrixD*>& tvArrayV, - std::vector<TMatrixD*>& tvCharge, std::vector<TMatrixD*>& tvResidue); - void WCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, const Int_t gamma, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, - std::vector<TMatrixD*>& tvArrayV, - std::vector<TMatrixD*>& tvCharge, std::vector<TMatrixD*>& tvResidue); - void - VCycle3D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, const Int_t gridFrom, - const Int_t gridTo, const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratioZ, - std::vector<TMatrixD**>& tvArrayV, std::vector<TMatrixD**>& tvCharge, - std::vector<TMatrixD**>& tvResidue, std::vector<float>& vectorCoefficient1, - std::vector<float>& vectorCoefficient2, - std::vector<float>& vectorCoefficient3, std::vector<float>& vectorCoefficient4, - std::vector<float>& vectorInverseCoefficient4); - void VCycle3D2D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, const Int_t nPost, - const Float_t gridSizeR, - const Float_t ratioZ, const Float_t ratioPhi, std::vector<TMatrixD**>& tvArrayV, - std::vector<TMatrixD**>& tvCharge, std::vector<TMatrixD**>& tvResidue, - std::vector<float>& vectorCoefficient1, - std::vector<float>& vectorCoefficient2, std::vector<float>& vectorCoefficient3, - std::vector<float>& vectorCoefficient4, - std::vector<float>& vectorInverseCoefficient4); - void VCycle3D2DGPU(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, - const Int_t nPost, const Float_t gridSizeR, const Float_t ratioZ, const Float_t ratioPhi, - std::vector<TMatrixD**>& tvArrayV, std::vector<TMatrixD**>& tvCharge, - std::vector<TMatrixD**>& tvResidue, std::vector<float>& vectorCoefficient1, - std::vector<float>& vectorCoefficient2, - std::vector<float>& vectorCoefficient3, std::vector<float>& vectorCoefficient4, - std::vector<float>& vectorInverseCoefficient4); - Double_t GetExactError(TMatrixD** currentMatricesV, TMatrixD** tempArrayV, const Int_t phiSlice); - Double_t GetConvergenceError(TMatrixD** currentMatricesV, TMatrixD** prevArrayV, const Int_t phiSlice); - Double_t fMaxExact; - Bool_t fExactPresent = kFALSE; - /// \cond CLASSIMP -#if defined(ROOT_VERSION_CODE) && ROOT_VERSION_CODE >= ROOT_VERSION(6, 0, 0) - ClassDefOverride(AliTPCPoissonSolver, 5); -#else - ClassDefNV(AliTPCPoissonSolver, 5); -#endif - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.cxx b/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.cxx deleted file mode 100644 index 14dea14dbc555..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.cxx +++ /dev/null @@ -1,5315 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCSpaceCharge3DCalc.cxx -/// \brief This class provides distortion and correction map with integration following electron drift -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#include "TStopwatch.h" -#include "TMath.h" -#include "AliTPCSpaceCharge3DCalc.h" - -/// \cond CLASSIMP -ClassImp(AliTPCSpaceCharge3DCalc); -/// \endcond - -/// Construction for AliTPCSpaceCharge3DCalc class -/// Default values -/// ~~~ -/// fInterpolationOrder = 5; // interpolation cubic spline with 5 points -/// fNRRows = 129; -/// fNPhiSlices = 180; // the maximum of phi-slices so far = (8 per sector) -/// fNZColumns = 129; // the maximum on column-slices so ~ 2cm slicing -/// ~~~ -AliTPCSpaceCharge3DCalc::AliTPCSpaceCharge3DCalc() -{ - InitAllocateMemory(); -} - -/// Member values from params -/// -/// \param nRRow Int_t number of grid in r direction -/// \param nZColumn Int_t number of grid in z direction -/// \param nPhiSlice Int_t number of grid in \f$ \phi \f$ direction -/// -AliTPCSpaceCharge3DCalc::AliTPCSpaceCharge3DCalc(Int_t nRRow, - Int_t nZColumn, Int_t nPhiSlice) - : fNRRows(nRRow), - fNPhiSlices(nPhiSlice), - fNZColumns(nZColumn) -{ - InitAllocateMemory(); -} - -/// Construction for AliTPCSpaceCharge3DCalc class -/// Member values from params -/// -/// \param nRRow Int_t number of grid in r direction -/// \param nZColumn Int_t number of grid in z direction -/// \param nPhiSlice Int_t number of grid in \f$ \phi \f$ direction -/// \param interpolationOrder Int_t order of interpolation -/// \param strategy Int_t strategy for global distortion -/// \param rbfKernelType Int_t strategy for global distortion -/// -AliTPCSpaceCharge3DCalc::AliTPCSpaceCharge3DCalc( - Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, Int_t interpolationOrder, - Int_t irregularGridSize, Int_t rbfKernelType) - : fNRRows(nRRow), - fNPhiSlices(nPhiSlice), - fNZColumns(nZColumn), - fInterpolationOrder(interpolationOrder), - fIrregularGridSize(irregularGridSize), - fRBFKernelType(rbfKernelType) -{ - InitAllocateMemory(); -} - -/// Memory allocation for working/output memory -/// -void AliTPCSpaceCharge3DCalc::InitAllocateMemory() -{ - fPoissonSolver = new AliTPCPoissonSolver(); - - fListR = new Double_t[fNRRows]; - fListPhi = new Double_t[fNPhiSlices]; - fListZ = new Double_t[fNZColumns]; - fListZA = new Double_t[fNZColumns]; - fListZC = new Double_t[fNZColumns]; - - // allocate for boundary - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Int_t len = 2 * fNPhiSlices * (fNZColumns + fNRRows) - (4 * fNPhiSlices); - // fListPotentialBoundaryA = new Double_t[len]; - // fListPotentialBoundaryC = new Double_t[len]; - - Int_t phiSlicesPerSector = fNPhiSlices / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - for (Int_t k = 0; k < fNPhiSlices; k++) { - fListPhi[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < fNRRows; i++) { - fListR[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < fNZColumns; j++) { - fListZ[j] = (j * gridSizeZ); - fListZA[j] = (j * gridSizeZ); - fListZC[j] = (j * gridSizeZ); - } - - fMatrixIntDistDrEzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDPhiREzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDzA = new TMatrixD*[fNPhiSlices]; - - fMatrixIntDistDrEzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDPhiREzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDzC = new TMatrixD*[fNPhiSlices]; - - fMatrixErOverEzA = new TMatrixD*[fNPhiSlices]; - fMatrixEPhiOverEzA = new TMatrixD*[fNPhiSlices]; - fMatrixDeltaEzA = new TMatrixD*[fNPhiSlices]; - - fMatrixErOverEzC = new TMatrixD*[fNPhiSlices]; - fMatrixEPhiOverEzC = new TMatrixD*[fNPhiSlices]; - fMatrixDeltaEzC = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzA = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzC = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixRListIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixPhiListIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixZListIrregularA = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixRListIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixPhiListIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixZListIrregularC = new TMatrixD*[fNPhiSlices]; - - fMatrixChargeA = new TMatrixD*[fNPhiSlices]; - fMatrixChargeC = new TMatrixD*[fNPhiSlices]; - fMatrixChargeInverseA = new TMatrixD*[fNPhiSlices]; - fMatrixChargeInverseC = new TMatrixD*[fNPhiSlices]; - fMatrixPotentialA = new TMatrixD*[fNPhiSlices]; - fMatrixPotentialC = new TMatrixD*[fNPhiSlices]; - - for (Int_t k = 0; k < fNPhiSlices; k++) { - - fMatrixIntDistDrEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDPhiREzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDzA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntDistDrEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDPhiREzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDzC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixErOverEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixEPhiOverEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixDeltaEzA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixErOverEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixEPhiOverEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixDeltaEzC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixRListIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPhiListIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixZListIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixRListIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPhiListIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixZListIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixChargeA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixChargeC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixChargeInverseA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixChargeInverseC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPotentialA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPotentialC[k] = new TMatrixD(fNRRows, fNZColumns); - } - - fLookupIntDistA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntDistDrEzA, fListR, fNPhiSlices, fMatrixIntDistDPhiREzA, fListPhi, - fNZColumns, fMatrixIntDistDzA, fListZA, fInterpolationOrder); - fLookupIntDistC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntDistDrEzC, fListR, fNPhiSlices, fMatrixIntDistDPhiREzC, fListPhi, - fNZColumns, fMatrixIntDistDzC, fListZC, fInterpolationOrder); - fLookupIntCorrA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntCorrDrEzA, fListR, fNPhiSlices, fMatrixIntCorrDPhiREzA, fListPhi, - fNZColumns, fMatrixIntCorrDzA, fListZA, fInterpolationOrder); - fLookupIntCorrC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntCorrDrEzC, fListR, fNPhiSlices, fMatrixIntCorrDPhiREzC, fListPhi, - fNZColumns, fMatrixIntCorrDzC, fListZC, fInterpolationOrder); - - fLookupIntENoDriftA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixErOverEzA, fListR, fNPhiSlices, fMatrixEPhiOverEzA, fListPhi, - fNZColumns, fMatrixDeltaEzA, fListZA, fInterpolationOrder); - fLookupIntENoDriftC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixErOverEzC, fListR, fNPhiSlices, fMatrixEPhiOverEzC, fListPhi, - fNZColumns, fMatrixDeltaEzC, fListZC, fInterpolationOrder); - fLookupIntCorrIrregularA = - new AliTPCLookUpTable3DInterpolatorIrregularD( - fNRRows, fMatrixIntCorrDrEzIrregularA, fMatrixRListIrregularA, fNPhiSlices, - fMatrixIntCorrDPhiREzIrregularA, fMatrixPhiListIrregularA, fNZColumns, - fMatrixIntCorrDzIrregularA, fMatrixZListIrregularA, 2, GetIrregularGridSize(), - GetIrregularGridSize(), GetIrregularGridSize(), 1); - - fLookupIntCorrIrregularC = - new AliTPCLookUpTable3DInterpolatorIrregularD( - fNRRows, fMatrixIntCorrDrEzIrregularC, fMatrixRListIrregularC, fNPhiSlices, - fMatrixIntCorrDPhiREzIrregularC, fMatrixPhiListIrregularC, fNZColumns, - fMatrixIntCorrDzIrregularC, fMatrixZListIrregularC, 2, GetIrregularGridSize(), - GetIrregularGridSize(), GetIrregularGridSize(), 1); - - fInterpolatorChargeA = new AliTPC3DCylindricalInterpolator(); - fInterpolatorChargeC = new AliTPC3DCylindricalInterpolator(); - fInterpolatorPotentialA = new AliTPC3DCylindricalInterpolator(); - fInterpolatorPotentialC = new AliTPC3DCylindricalInterpolator(); - fInterpolatorInverseChargeA = new AliTPC3DCylindricalInterpolator(); - fInterpolatorInverseChargeC = new AliTPC3DCylindricalInterpolator(); - - fInterpolatorChargeA->SetNR(fNRRows); - fInterpolatorChargeA->SetNZ(fNZColumns); - fInterpolatorChargeA->SetNPhi(fNPhiSlices); - fInterpolatorChargeA->SetNGridPoints(); - fInterpolatorChargeA->SetRList(fListR); - fInterpolatorChargeA->SetZList(fListZA); - fInterpolatorChargeA->SetPhiList(fListPhi); - fInterpolatorChargeA->SetOrder(fInterpolationOrder); - - fInterpolatorChargeC->SetNR(fNRRows); - fInterpolatorChargeC->SetNZ(fNZColumns); - fInterpolatorChargeC->SetNPhi(fNPhiSlices); - fInterpolatorChargeC->SetNGridPoints(); - fInterpolatorChargeC->SetRList(fListR); - fInterpolatorChargeC->SetZList(fListZC); - fInterpolatorChargeC->SetPhiList(fListPhi); - fInterpolatorChargeC->SetOrder(fInterpolationOrder); - - fInterpolatorPotentialA->SetNR(fNRRows); - fInterpolatorPotentialA->SetNZ(fNZColumns); - fInterpolatorPotentialA->SetNPhi(fNPhiSlices); - fInterpolatorPotentialA->SetNGridPoints(); - fInterpolatorPotentialA->SetRList(fListR); - fInterpolatorPotentialA->SetZList(fListZA); - fInterpolatorPotentialA->SetPhiList(fListPhi); - fInterpolatorPotentialA->SetOrder(fInterpolationOrder); - - fInterpolatorPotentialC->SetNR(fNRRows); - fInterpolatorPotentialC->SetNZ(fNZColumns); - fInterpolatorPotentialC->SetNPhi(fNPhiSlices); - fInterpolatorPotentialC->SetNGridPoints(); - fInterpolatorPotentialC->SetRList(fListR); - fInterpolatorPotentialC->SetZList(fListZA); - fInterpolatorPotentialC->SetPhiList(fListPhi); - fInterpolatorPotentialC->SetOrder(fInterpolationOrder); - - fInterpolatorInverseChargeA->SetNR(fNRRows); - fInterpolatorInverseChargeA->SetNZ(fNZColumns); - fInterpolatorInverseChargeA->SetNPhi(fNPhiSlices); - fInterpolatorInverseChargeA->SetNGridPoints(); - fInterpolatorInverseChargeA->SetRList(fListR); - fInterpolatorInverseChargeA->SetZList(fListZA); - fInterpolatorInverseChargeA->SetPhiList(fListPhi); - fInterpolatorInverseChargeA->SetOrder(fInterpolationOrder); - - fInterpolatorInverseChargeC->SetNR(fNRRows); - fInterpolatorInverseChargeC->SetNZ(fNZColumns); - fInterpolatorInverseChargeC->SetNPhi(fNPhiSlices); - fInterpolatorInverseChargeC->SetNGridPoints(); - fInterpolatorInverseChargeC->SetRList(fListR); - fInterpolatorInverseChargeC->SetZList(fListZC); - fInterpolatorInverseChargeC->SetPhiList(fListPhi); - fInterpolatorInverseChargeC->SetOrder(fInterpolationOrder); - - fLookupDistA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupDistC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupCorrA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupCorrC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupInverseDistA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupInverseDistC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupElectricFieldA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupElectricFieldC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupIntCorrIrregularA->SetKernelType(fRBFKernelType); - fLookupIntCorrIrregularC->SetKernelType(fRBFKernelType); -} -/// Destruction for AliTPCSpaceCharge3DCalc -/// Deallocate memory for lookup table and charge distribution -/// -AliTPCSpaceCharge3DCalc::~AliTPCSpaceCharge3DCalc() -{ - - if (fPoissonSolver != nullptr) { - delete fPoissonSolver; - } - - for (Int_t k = 0; k < fNPhiSlices; k++) { - delete fMatrixIntDistDrEzA[k]; - delete fMatrixIntDistDPhiREzA[k]; - delete fMatrixIntDistDzA[k]; - delete fMatrixIntDistDrEzC[k]; - delete fMatrixIntDistDPhiREzC[k]; - delete fMatrixIntDistDzC[k]; - delete fMatrixIntCorrDrEzA[k]; - delete fMatrixIntCorrDPhiREzA[k]; - delete fMatrixIntCorrDzA[k]; - delete fMatrixIntCorrDrEzC[k]; - delete fMatrixIntCorrDPhiREzC[k]; - delete fMatrixIntCorrDzC[k]; - delete fMatrixErOverEzA[k]; - delete fMatrixEPhiOverEzA[k]; - delete fMatrixDeltaEzA[k]; - delete fMatrixErOverEzC[k]; - delete fMatrixEPhiOverEzC[k]; - delete fMatrixDeltaEzC[k]; - delete fMatrixIntCorrDrEzIrregularA[k]; - delete fMatrixIntCorrDPhiREzIrregularA[k]; - delete fMatrixIntCorrDzIrregularA[k]; - delete fMatrixRListIrregularA[k]; - delete fMatrixPhiListIrregularA[k]; - delete fMatrixZListIrregularA[k]; - delete fMatrixIntCorrDrEzIrregularC[k]; - delete fMatrixIntCorrDPhiREzIrregularC[k]; - delete fMatrixIntCorrDzIrregularC[k]; - delete fMatrixRListIrregularC[k]; - delete fMatrixPhiListIrregularC[k]; - delete fMatrixZListIrregularC[k]; - delete fMatrixChargeA[k]; - delete fMatrixChargeC[k]; - delete fMatrixChargeInverseA[k]; - delete fMatrixChargeInverseC[k]; - - delete fMatrixPotentialA[k]; - delete fMatrixPotentialC[k]; - } - delete[] fListR; - delete[] fListPhi; - delete[] fListZ; - delete[] fListZA; - delete[] fListZC; - - delete fLookupIntDistA; - delete fLookupIntDistC; - delete fLookupIntENoDriftA; - delete fLookupIntENoDriftC; - delete fLookupIntCorrA; - delete fLookupIntCorrC; - delete fLookupIntCorrIrregularA; - delete fLookupIntCorrIrregularC; - delete fLookupDistA; - delete fLookupDistC; - delete fLookupInverseDistA; - delete fLookupInverseDistC; - delete fLookupElectricFieldA; - delete fLookupElectricFieldC; - delete fInterpolatorChargeA; - delete fInterpolatorPotentialA; - delete fInterpolatorChargeC; - delete fInterpolatorPotentialC; - delete fInterpolatorInverseChargeA; - delete fInterpolatorInverseChargeC; - - delete fHistogram3DSpaceCharge; - delete fHistogram3DSpaceChargeA; - delete fHistogram3DSpaceChargeC; - - delete fFormulaBoundaryIFCA; - delete fFormulaBoundaryIFCC; - delete fFormulaBoundaryOFCA; - delete fFormulaBoundaryOFCC; - delete fFormulaBoundaryROCA; - delete fFormulaBoundaryROCC; - delete fFormulaBoundaryCE; - - delete fFormulaPotentialV; - delete fFormulaChargeRho; - - delete fFormulaEPhi; - delete fFormulaEr; - delete fFormulaEz; - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // delete[] fListPotentialBoundaryA; - // delete[] fListPotentialBoundaryC; -} - -/// Creating look-up tables of Correction/Distortion by integration following -/// drift line, input from space charge 3d histogram (fSpaceCharge3D) and boundary values are filled with zeroes -/// -/// TODO: provide an interface for setting boundary values -/// -/// The algorithm and implementations of this function is the following: -/// -/// Do for each side A,C -/// -/// 1) Solving \f$ \nabla^2 \Phi(r,\phi,z) = - \rho(r,\phi,z)\f$ -/// ~~~ Calling poisson solver -/// fPoissonSolver->PoissonSolver3D( matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry ) ; -/// ~~~ -/// -/// 2) Get the electric field \f$ \vec{E} = - \nabla \Phi(r,\phi,z) \f$ -/// ~~~ -/// ElectricField( matricesV, matricesEr, matricesEPhi, matricesEz, nRRow, nZColumn, phiSlice, -/// gridSizeR, gridSizePhi ,gridSizeZ,symmetry, AliTPCPoissonSolver::fgkIFCRadius); -/// ~~~ -/// -/// 3) Calculate local distortion and correction, using Langevin formula -/// ~~~ cxx -/// LocalDistCorrDz (matricesEr, matricesEPhi, matricesEz, -/// matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, -/// matricesCorrDrDz, matricesCorrDPhiRDz, matricesCorrDz, -/// nRRow, nZColumn, phiSlice, gridSizeZ, ezField); -/// ~~~ -/// -/// 4) Integrate distortion by following the drift line -/// -/// 5) Fill look up table for Integral distortion -/// -/// 6) Fill look up table for Integral correction -/// -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slice in \f$ phi \f$ direction -/// \param maxIteration Int_t Maximum iteration for poisson solver -/// \param stoppingConvergence Convergence error stopping condition for poisson solver -/// -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stoppingConvergence) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - TMatrixD **matricesV, *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - TMatrixD *matricesDistDrDz[phiSlice], *matricesDistDPhiRDz[phiSlice], *matricesDistDz[phiSlice]; - TMatrixD *matricesCorrDrDz[phiSlice], *matricesCorrDPhiRDz[phiSlice], *matricesCorrDz[phiSlice]; - TMatrixD *matricesGDistDrDz[phiSlice], *matricesGDistDPhiRDz[phiSlice], *matricesGDistDz[phiSlice]; - TMatrixD *matricesGCorrDrDz[phiSlice], *matricesGCorrDPhiRDz[phiSlice], *matricesGCorrDz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - //matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDz[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - // pointer to current TF1 for potential boundary values - TF1* f1BoundaryIFC = nullptr; - TF1* f1BoundaryOFC = nullptr; - TF1* f1BoundaryROC = nullptr; - TStopwatch w; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - // allocate look up local distortion - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesDistDrDz, rList, phiSlice, matricesDistDPhiRDz, phiList, nZColumn, matricesDistDz, - zList, fInterpolationOrder); - - // allocate look up local correction - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesCorrDrDz, rList, phiSlice, matricesCorrDPhiRDz, phiList, nZColumn, matricesCorrDz, - zList, fInterpolationOrder); - - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, fInterpolationOrder); - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGCorrDrDz, rList, phiSlice, matricesGCorrDPhiRDz, phiList, nZColumn, matricesGCorrDz, - zList, fInterpolationOrder); - - // should be set, in another place - const Int_t symmetry = 0; // fSymmetry - - // for irregular - TMatrixD** matricesIrregularDrDz = nullptr; - TMatrixD** matricesIrregularDPhiRDz = nullptr; - TMatrixD** matricesIrregularDz = nullptr; - TMatrixD** matricesPhiIrregular = nullptr; - TMatrixD** matricesRIrregular = nullptr; - TMatrixD** matricesZIrregular = nullptr; - - // for charge - TMatrixD** matricesLookUpCharge = nullptr; - AliTPC3DCylindricalInterpolator* chargeInterpolator = nullptr; - AliTPC3DCylindricalInterpolator* potentialInterpolator = nullptr; - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* potentialBoundary = nullptr; - TMatrixD* matrixV; - TMatrixD* matrixCharge; - // for potential - TMatrixD** matricesVPotential; - - Int_t pIndex = 0; - - // do if look up table haven't be initialized - if (!fInitLookUp) { - // initialize for working memory - for (Int_t side = 0; side < 2; side++) { - // zeroing global distortion/correction - for (Int_t k = 0; k < phiSlice; k++) { - matricesDistDrDz[k]->Zero(); - matricesDistDPhiRDz[k]->Zero(); - matricesDistDz[k]->Zero(); - matricesCorrDrDz[k]->Zero(); - matricesCorrDPhiRDz[k]->Zero(); - matricesCorrDz[k]->Zero(); - - matricesGDistDrDz[k]->Zero(); - matricesGDistDPhiRDz[k]->Zero(); - matricesGDistDz[k]->Zero(); - matricesGCorrDrDz[k]->Zero(); - matricesGCorrDPhiRDz[k]->Zero(); - matricesGCorrDz[k]->Zero(); - } - if (side == 0) { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularA; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularA; - matricesIrregularDz = fMatrixIntCorrDzIrregularA; - - matricesPhiIrregular = fMatrixPhiListIrregularA; - matricesRIrregular = fMatrixRListIrregularA; - matricesZIrregular = fMatrixZListIrregularA; - matricesLookUpCharge = fMatrixChargeA; - - matricesV = fMatrixPotentialA; - chargeInterpolator = fInterpolatorChargeA; - potentialInterpolator = fInterpolatorPotentialA; - fLookupDistA->SetLookUpR(matricesDistDrDz); - fLookupDistA->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistA->SetLookUpZ(matricesDistDz); - fLookupCorrA->SetLookUpR(matricesCorrDrDz); - fLookupCorrA->SetLookUpPhi(matricesCorrDPhiRDz); - fLookupCorrA->SetLookUpZ(matricesCorrDz); - - fLookupElectricFieldA->SetLookUpR(matricesEr); - fLookupElectricFieldA->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldA->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryA; - f1BoundaryIFC = fFormulaBoundaryIFCA; - f1BoundaryOFC = fFormulaBoundaryOFCA; - f1BoundaryROC = fFormulaBoundaryROCA; - } else { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularC; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularC; - matricesIrregularDz = fMatrixIntCorrDzIrregularC; - matricesPhiIrregular = fMatrixPhiListIrregularC; - matricesRIrregular = fMatrixRListIrregularC; - matricesZIrregular = fMatrixZListIrregularC; - matricesLookUpCharge = fMatrixChargeC; - matricesV = fMatrixPotentialC; - chargeInterpolator = fInterpolatorChargeC; - potentialInterpolator = fInterpolatorPotentialC; - fLookupDistC->SetLookUpR(matricesDistDrDz); - fLookupDistC->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistC->SetLookUpZ(matricesDistDz); - fLookupCorrC->SetLookUpR(matricesCorrDrDz); - fLookupCorrC->SetLookUpPhi(matricesCorrDPhiRDz); - fLookupCorrC->SetLookUpZ(matricesCorrDz); - fLookupElectricFieldC->SetLookUpR(matricesEr); - fLookupElectricFieldC->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldC->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryC; - f1BoundaryIFC = fFormulaBoundaryIFCC; - f1BoundaryOFC = fFormulaBoundaryOFCC; - f1BoundaryROC = fFormulaBoundaryROCC; - } - - // fill the potential boundary - // guess the initial potential - // fill also charge - //pIndex = 0; - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step = 0: Fill Boundary and Charge Densities")); - for (Int_t k = 0; k < phiSlice; k++) { - phi0 = k * gridSizePhi; - matrixV = matricesV[k]; - matrixCharge = matricesCharge[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = j * gridSizeZ; - (*matrixCharge)(i, j) = chargeInterpolator->GetValue(rList[i], phiList[k], zList[j]); - (*matrixV)(i, j) = 0.0; // fill zeros - - if (fFormulaPotentialV == nullptr) { - // boundary IFC - if (i == 0) { - if (f1BoundaryIFC != nullptr) { - if (TF2* f2BoundaryIFC = dynamic_cast<TF2*>(f1BoundaryIFC)) { - (*matrixV)(i, j) = f2BoundaryIFC->Eval(z0, phi0); - } else { - (*matrixV)(i, j) = f1BoundaryIFC->Eval(z0); - } - } - } - if (i == (nRRow - 1)) { - if (f1BoundaryOFC != nullptr) { - if (TF2* f2BoundaryOFC = dynamic_cast<TF2*>(f1BoundaryOFC)) { - (*matrixV)(i, j) = f2BoundaryOFC->Eval(z0, phi0); - } else { - (*matrixV)(i, j) = f1BoundaryOFC->Eval(z0); - } - } - } - if (j == 0) { - if (fFormulaBoundaryCE) { - if (TF2* f2FormulaBoundaryCE = dynamic_cast<TF2*>(fFormulaBoundaryCE)) { - (*matrixV)(i, j) = f2FormulaBoundaryCE->Eval(radius0, phi0); - } else { - (*matrixV)(i, j) = fFormulaBoundaryCE->Eval(radius0); - } - } - } - if (j == (nZColumn - 1)) { - if (f1BoundaryROC != nullptr) { - if (TF2* f2BoundaryROC = dynamic_cast<TF2*>(f1BoundaryROC)) { - (*matrixV)(i, j) = f2BoundaryROC->Eval(radius0, phi0); - } else { - (*matrixV)(i, j) = f1BoundaryROC->Eval(radius0); - } - } - } - } else { - if ((i == 0) || (i == (nRRow - 1)) || (j == 0) || (j == (nZColumn - 1))) { - (*matrixV)(i, j) = fFormulaPotentialV->Eval(radius0, phi0, z0); - } - } - } - } - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 0: Preparing Charge interpolator: %f\n", w.CpuTime())); - AliTPCPoissonSolver::fgConvergenceError = stoppingConvergence; - - //fPoissonSolver->SetStrategy(AliTPCPoissonSolver::kMultiGrid); - //(fPoissonSolver->fMgParameters).cycleType = AliTPCPoissonSolver::kFCycle; - //(fPoissonSolver->fMgParameters).isFull3D = kFALSE; - //(fPoissonSolver->fMgParameters).nMGCycle = maxIteration; - //(fPoissonSolver->fMgParameters).maxLoop = 6; - - w.Start(); - fPoissonSolver->PoissonSolver3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry); - w.Stop(); - - potentialInterpolator->SetValue(matricesV); - potentialInterpolator->InitCubicSpline(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - if (side == 0) { - myProfile.poissonSolverTime = w.CpuTime(); - } - if (side == 0) { - myProfile.iteration = fPoissonSolver->fIterations; - } - - w.Start(); - ElectricField(matricesV, - matricesEr, matricesEPhi, matricesEz, nRRow, nZColumn, phiSlice, - gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - w.Stop(); - - myProfile.electricFieldTime = w.CpuTime(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - LocalDistCorrDz(matricesEr, matricesEPhi, matricesEz, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, - matricesCorrDrDz, matricesCorrDPhiRDz, matricesCorrDz, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - myProfile.localDistortionTime = w.CpuTime(); - - // copy to interpolator - if (side == 0) { - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistA->CopyFromMatricesToInterpolator(); - fLookupCorrA->CopyFromMatricesToInterpolator(); - fLookupElectricFieldA->CopyFromMatricesToInterpolator(); - } else { - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistC->CopyFromMatricesToInterpolator(); - fLookupCorrC->CopyFromMatricesToInterpolator(); - fLookupElectricFieldC->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - w.Start(); - if (fIntegrationStrategy == kNaive) { - IntegrateDistCorrDriftLineDz( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - matricesIrregularDrDz, matricesIrregularDPhiRDz, matricesIrregularDz, - matricesRIrregular, matricesPhiIrregular, matricesZIrregular, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } else { - IntegrateDistCorrDriftLineDzWithLookUp( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 4: Global correction/distortion: %f\n", w.CpuTime())); - myProfile.globalDistortionTime = w.CpuTime(); - - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - } - //// - - w.Stop(); - - if (side == 0) { - - w.Start(); - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzA, fMatrixIntDistDPhiREzA, fMatrixIntDistDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - if (fCorrectionType == 0) { - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzA, fMatrixIntCorrDPhiREzA, fMatrixIntCorrDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - fLookupIntDistA->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrA->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularA->CopyFromMatricesToInterpolator(); - } - - w.Stop(); - myProfile.interpolationInitTime = w.CpuTime(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 5: Filling up the look up: %f\n", w.CpuTime())); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - w.Start(); - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzC, fMatrixIntDistDPhiREzC, fMatrixIntDistDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - if (fCorrectionType == 0) { - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzC, fMatrixIntCorrDPhiREzC, fMatrixIntCorrDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - fLookupIntDistC->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrC->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularC->CopyFromMatricesToInterpolator(); - } - w.Stop(); - myProfile.interpolationInitTime = w.CpuTime(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - } - - fInitLookUp = kTRUE; - } - - // memory de-allocation for temporary matrices - for (Int_t k = 0; k < phiSlice; k++) { - //delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - delete matricesDistDrDz[k]; - delete matricesDistDPhiRDz[k]; - delete matricesDistDz[k]; - - delete matricesCorrDrDz[k]; - delete matricesCorrDPhiRDz[k]; - delete matricesCorrDz[k]; - delete matricesGDistDrDz[k]; - delete matricesGDistDPhiRDz[k]; - delete matricesGDistDz[k]; - - delete matricesGCorrDrDz[k]; - delete matricesGCorrDPhiRDz[k]; - delete matricesGCorrDz[k]; - } - delete lookupLocalDist; - delete lookupLocalCorr; - delete lookupGlobalDist; - delete lookupGlobalCorr; -} - -// outdated, to be removed once modifications in aliroot are pushed -/// Creating look-up tables of Correction/Distortion by integration following -/// drift line with known distributions for potential and space charge. -/// -/// -/// \param nRRow Int_t number of grid in row direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of slices in phi direction -/// \param maxIteration Int_t max iteration for convergence -/// \param stopConvergence Double_t stopping criteria for convergence -/// \param matricesDistDrDzA TMatrixD** local r distortion (output) A side -/// \param matricesDistDPhiRDzA TMatrixD** local r phi distortion (output) A side -/// \param matricesDistDzA TMatrixD** local z distortion (output) A side -/// \param matricesCorrDrDzA TMatrixD** local r correction (output) A side -/// \param matricesCorrDPhiRDzA TMatrixD** local r phi correction (output) A side -/// \param matricesCorrDzA TMatrixD** local z correction (output) A side -/// \param matricesDistDrDzC TMatrixD** local r distortion (output) C side -/// \param matricesDistDPhiRDzC TMatrixD** local r phi distortion (output) C side -/// \param matricesDistDzC TMatrixD** local z distortion (output) C side -/// \param matricesCorrDrDzC TMatrixD** local r phi correction (output) C side -/// \param matricesCorrDPhiRDzC TMatrixD** local r phi correction (output) C side -/// \param matricesCorrDzC TMatrixD** local z correction (output) C side -/// -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TMatrixD** matricesErA, TMatrixD** matricesEPhiA, TMatrixD** matricesEzA, - TMatrixD** matricesErC, TMatrixD** matricesEPhiC, TMatrixD** matricesEzC, - TMatrixD** matricesDistDrDzA, TMatrixD** matricesDistDPhiRDzA, TMatrixD** matricesDistDzA, - TMatrixD** matricesCorrDrDzA, TMatrixD** matricesCorrDPhiRDzA, TMatrixD** matricesCorrDzA, - TMatrixD** matricesDistDrDzC, TMatrixD** matricesDistDPhiRDzC, TMatrixD** matricesDistDzC, - TMatrixD** matricesCorrDrDzC, TMatrixD** matricesCorrDPhiRDzC, TMatrixD** matricesCorrDzC, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - TMatrixD *matricesV[phiSlice], *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - TMatrixD *matricesDistDrDz[phiSlice], *matricesDistDPhiRDz[phiSlice], *matricesDistDz[phiSlice]; - TMatrixD *matricesCorrDrDz[phiSlice], *matricesCorrDPhiRDz[phiSlice], *matricesCorrDz[phiSlice]; - TMatrixD *matricesGDistDrDz[phiSlice], *matricesGDistDPhiRDz[phiSlice], *matricesGDistDz[phiSlice]; - TMatrixD *matricesGCorrDrDz[phiSlice], *matricesGCorrDPhiRDz[phiSlice], *matricesGCorrDz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDz[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - // pointer to current TF1 for potential boundary values - TF1* f1BoundaryIFC = nullptr; - TF1* f1BoundaryOFC = nullptr; - TF1* f1BoundaryROC = nullptr; - TStopwatch w; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - // allocate look up local distortion - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesDistDrDz, rList, phiSlice, matricesDistDPhiRDz, phiList, nZColumn, matricesDistDz, - zList, fInterpolationOrder); - - // allocate look up local correction - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesCorrDrDz, rList, phiSlice, matricesCorrDPhiRDz, phiList, nZColumn, matricesCorrDz, - zList, fInterpolationOrder); - - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, fInterpolationOrder); - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGCorrDrDz, rList, phiSlice, matricesGCorrDPhiRDz, phiList, nZColumn, matricesGCorrDz, - zList, fInterpolationOrder); - - // should be set, in another place - const Int_t symmetry = 0; // fSymmetry - - // for irregular - TMatrixD** matricesIrregularDrDz = nullptr; - TMatrixD** matricesIrregularDPhiRDz = nullptr; - TMatrixD** matricesIrregularDz = nullptr; - TMatrixD** matricesPhiIrregular = nullptr; - TMatrixD** matricesRIrregular = nullptr; - TMatrixD** matricesZIrregular = nullptr; - - // for charge - TMatrixD** matricesLookUpCharge = nullptr; - AliTPC3DCylindricalInterpolator* chargeInterpolator = nullptr; - AliTPC3DCylindricalInterpolator* potentialInterpolator = nullptr; - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* potentialBoundary = nullptr; - TMatrixD* matrixV; - TMatrixD* matrixCharge; - Int_t pIndex = 0; - - // do if look up table haven't be initialized - if (!fInitLookUp) { - // initialize for working memory - for (Int_t side = 0; side < 2; side++) { - // zeroing global distortion/correction - for (Int_t k = 0; k < phiSlice; k++) { - matricesDistDrDz[k]->Zero(); - matricesDistDPhiRDz[k]->Zero(); - matricesDistDz[k]->Zero(); - matricesCorrDrDz[k]->Zero(); - matricesCorrDPhiRDz[k]->Zero(); - matricesCorrDz[k]->Zero(); - - matricesGDistDrDz[k]->Zero(); - matricesGDistDPhiRDz[k]->Zero(); - matricesGDistDz[k]->Zero(); - matricesGCorrDrDz[k]->Zero(); - matricesGCorrDPhiRDz[k]->Zero(); - matricesGCorrDz[k]->Zero(); - } - if (side == 0) { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularA; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularA; - matricesIrregularDz = fMatrixIntCorrDzIrregularA; - - matricesPhiIrregular = fMatrixPhiListIrregularA; - matricesRIrregular = fMatrixRListIrregularA; - matricesZIrregular = fMatrixZListIrregularA; - matricesLookUpCharge = fMatrixChargeA; - chargeInterpolator = fInterpolatorChargeA; - potentialInterpolator = fInterpolatorPotentialA; - fLookupDistA->SetLookUpR(matricesDistDrDzA); - fLookupDistA->SetLookUpPhi(matricesDistDPhiRDzA); - fLookupDistA->SetLookUpZ(matricesDistDzA); - lookupLocalDist->SetLookUpR(matricesDistDrDzA); - lookupLocalDist->SetLookUpPhi(matricesDistDPhiRDzA); - lookupLocalDist->SetLookUpZ(matricesDistDzA); - - lookupLocalCorr->SetLookUpR(matricesCorrDrDzA); - lookupLocalCorr->SetLookUpPhi(matricesCorrDPhiRDzA); - lookupLocalCorr->SetLookUpZ(matricesCorrDzA); - - fLookupElectricFieldA->SetLookUpR(matricesErA); - fLookupElectricFieldA->SetLookUpPhi(matricesEPhiA); - fLookupElectricFieldA->SetLookUpZ(matricesEzA); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryA; - f1BoundaryIFC = fFormulaBoundaryIFCA; - f1BoundaryOFC = fFormulaBoundaryOFCA; - f1BoundaryROC = fFormulaBoundaryROCA; - } else { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularC; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularC; - matricesIrregularDz = fMatrixIntCorrDzIrregularC; - matricesPhiIrregular = fMatrixPhiListIrregularC; - matricesRIrregular = fMatrixRListIrregularC; - matricesZIrregular = fMatrixZListIrregularC; - matricesLookUpCharge = fMatrixChargeC; - chargeInterpolator = fInterpolatorChargeC; - potentialInterpolator = fInterpolatorPotentialC; - fLookupDistC->SetLookUpR(matricesDistDrDzC); - fLookupDistC->SetLookUpPhi(matricesDistDPhiRDzC); - fLookupDistC->SetLookUpZ(matricesDistDzC); - fLookupElectricFieldC->SetLookUpR(matricesErC); - fLookupElectricFieldC->SetLookUpPhi(matricesEPhiC); - fLookupElectricFieldC->SetLookUpZ(matricesEzC); - - lookupLocalDist->SetLookUpR(matricesDistDrDzC); - lookupLocalDist->SetLookUpPhi(matricesDistDPhiRDzC); - lookupLocalDist->SetLookUpZ(matricesDistDzC); - - lookupLocalCorr->SetLookUpR(matricesCorrDrDzC); - lookupLocalCorr->SetLookUpPhi(matricesCorrDPhiRDzC); - lookupLocalCorr->SetLookUpZ(matricesCorrDzC); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryC; - f1BoundaryIFC = fFormulaBoundaryIFCC; - f1BoundaryOFC = fFormulaBoundaryOFCC; - f1BoundaryROC = fFormulaBoundaryROCC; - } - - // fill the potential boundary - // guess the initial potential - // fill also charge - //pIndex = 0; - - //Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz","%s",Form("Step = 0: Fill Boundary and Charge Densities")); - for (Int_t k = 0; k < phiSlice; k++) { - phi0 = k * gridSizePhi; - matrixV = matricesV[k]; - matrixCharge = matricesCharge[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = j * gridSizeZ; - (*matrixCharge)(i, j) = chargeInterpolator->GetValue(rList[i], phiList[k], zList[j]); - (*matrixV)(i, j) = fFormulaPotentialV->Eval(radius0, phi0, z0); - } - } - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 0: Preparing Charge interpolator: %f\n", w.CpuTime())); - //AliTPCPoissonSolver::fgConvergenceError = stoppingConvergence; - - //fPoissonSolver->SetStrategy(AliTPCPoissonSolver::kMultiGrid); - //(fPoissonSolver->fMgParameters).cycleType = AliTPCPoissonSolver::kFCycle; - //(fPoissonSolver->fMgParameters).isFull3D = kFALSE; - //(fPoissonSolver->fMgParameters).nMGCycle = maxIteration; - //(fPoissonSolver->fMgParameters).maxLoop = 6; - - w.Start(); - //fPoissonSolver->PoissonSolver3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, - // symmetry); - w.Stop(); - - potentialInterpolator->SetValue(matricesV); - potentialInterpolator->InitCubicSpline(); - - // copy to interpolator - if (side == 0) { - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - w.Start(); - ElectricField(matricesV, - matricesErA, matricesEPhiA, matricesEzA, nRRow, nZColumn, phiSlice, - gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - w.Stop(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - LocalDistCorrDz(matricesErA, matricesEPhiA, matricesEzA, - matricesDistDrDzA, matricesDistDPhiRDzA, matricesDistDzA, - matricesCorrDrDzA, matricesCorrDPhiRDzA, matricesCorrDzA, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - //LocalDistCorrDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, - // GetEzFormula(), rList, phiList, zList, - // matricesDistDrDzA, matricesDistDPhiRDzA, matricesDistDzA, - // matricesCorrDrDzA, matricesCorrDPhiRDzA, matricesCorrDzA, - // nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistA->CopyFromMatricesToInterpolator(); - fLookupElectricFieldA->CopyFromMatricesToInterpolator(); - } else { - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - w.Start(); - ElectricField(matricesV, - matricesErC, matricesEPhiC, matricesEzC, nRRow, nZColumn, phiSlice, - gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - w.Stop(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - LocalDistCorrDz(matricesErC, matricesEPhiC, matricesEzC, - matricesDistDrDzC, matricesDistDPhiRDzC, matricesDistDzC, - matricesCorrDrDzC, matricesCorrDPhiRDzC, matricesCorrDzC, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - //LocalDistCorrDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, - // GetEzFormula(), rList, phiList, zList, - // matricesDistDrDzC, matricesDistDPhiRDzC, matricesDistDzC, - // matricesCorrDrDzC, matricesCorrDPhiRDzC, matricesCorrDzC, - // nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistC->CopyFromMatricesToInterpolator(); - fLookupElectricFieldC->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - w.Start(); - if (fIntegrationStrategy == kNaive) { - IntegrateDistCorrDriftLineDz( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - matricesIrregularDrDz, matricesIrregularDPhiRDz, matricesIrregularDz, - matricesRIrregular, matricesPhiIrregular, matricesZIrregular, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } else { - IntegrateDistCorrDriftLineDzWithLookUp( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 4: Global correction/distortion: %f\n", w.CpuTime())); - - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - } - //// - - w.Start(); - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - //// - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 5: Filling up the look up: %f\n", w.CpuTime())); - - if (side == 0) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzA, fMatrixIntDistDPhiREzA, fMatrixIntDistDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzA, fMatrixIntCorrDPhiREzA, fMatrixIntCorrDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistA->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrA->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularA->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzC, fMatrixIntDistDPhiREzC, fMatrixIntDistDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzC, fMatrixIntCorrDPhiREzC, fMatrixIntCorrDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistC->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrC->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularC->CopyFromMatricesToInterpolator(); - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - } - - fInitLookUp = kTRUE; - } - - // memory de-allocation for temporary matrices - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - delete matricesDistDrDz[k]; - delete matricesDistDPhiRDz[k]; - delete matricesDistDz[k]; - - delete matricesCorrDrDz[k]; - delete matricesCorrDPhiRDz[k]; - delete matricesCorrDz[k]; - delete matricesGDistDrDz[k]; - delete matricesGDistDPhiRDz[k]; - delete matricesGDistDz[k]; - - delete matricesGCorrDrDz[k]; - delete matricesGCorrDPhiRDz[k]; - delete matricesGCorrDz[k]; - } - delete lookupLocalDist; - delete lookupLocalCorr; - delete lookupGlobalDist; - delete lookupGlobalCorr; -} - -/// Creating look-up tables of Correction/Distortion by integration following -/// drift line with known distributions for potential and space charge. -/// -/// -/// \param nRRow Int_t number of grid in row direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of slices in phi direction -/// \param maxIteration Int_t max iteration for convergence -/// \param stopConvergence Double_t stopping criteria for convergence -/// \param matricesCorrDzC TMatrixD** local z correction (output) C side -/// \param intErDzTestFunction TFormula* analytic function for closed integration of Er in z direction -/// \param intEPhiRDzTestFunction TFormula* analytic function for closed integration of EPhi in z direction -/// \param intDzTestFunction TFormula* analytic function for closed integration of Ez in z direction -/// -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - TMatrixD *matricesV[phiSlice], *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - TMatrixD *matricesDistDrDz[phiSlice], *matricesDistDPhiRDz[phiSlice], *matricesDistDz[phiSlice]; - TMatrixD *matricesCorrDrDz[phiSlice], *matricesCorrDPhiRDz[phiSlice], *matricesCorrDz[phiSlice]; - TMatrixD *matricesGDistDrDz[phiSlice], *matricesGDistDPhiRDz[phiSlice], *matricesGDistDz[phiSlice]; - TMatrixD *matricesGCorrDrDz[phiSlice], *matricesGCorrDPhiRDz[phiSlice], *matricesGCorrDz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDz[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - // pointer to current TF1 for potential boundary values - TF1* f1BoundaryIFC = nullptr; - TF1* f1BoundaryOFC = nullptr; - TF1* f1BoundaryROC = nullptr; - TStopwatch w; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - // allocate look up local distortion - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesDistDrDz, rList, phiSlice, matricesDistDPhiRDz, phiList, nZColumn, matricesDistDz, - zList, fInterpolationOrder); - - // allocate look up local correction - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesCorrDrDz, rList, phiSlice, matricesCorrDPhiRDz, phiList, nZColumn, matricesCorrDz, - zList, fInterpolationOrder); - - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, fInterpolationOrder); - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGCorrDrDz, rList, phiSlice, matricesGCorrDPhiRDz, phiList, nZColumn, matricesGCorrDz, - zList, fInterpolationOrder); - - // should be set, in another place - const Int_t symmetry = 0; // fSymmetry - - // for irregular - TMatrixD** matricesIrregularDrDz = nullptr; - TMatrixD** matricesIrregularDPhiRDz = nullptr; - TMatrixD** matricesIrregularDz = nullptr; - TMatrixD** matricesPhiIrregular = nullptr; - TMatrixD** matricesRIrregular = nullptr; - TMatrixD** matricesZIrregular = nullptr; - - // for charge - TMatrixD** matricesLookUpCharge = nullptr; - AliTPC3DCylindricalInterpolator* chargeInterpolator = nullptr; - AliTPC3DCylindricalInterpolator* potentialInterpolator = nullptr; - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* potentialBoundary = nullptr; - TMatrixD* matrixV; - TMatrixD* matrixCharge; - - // do if look up table haven't be initialized - if (!fInitLookUp) { - // initialize for working memory - for (Int_t side = 0; side < 2; side++) { - // zeroing global distortion/correction - for (Int_t k = 0; k < phiSlice; k++) { - matricesDistDrDz[k]->Zero(); - matricesDistDPhiRDz[k]->Zero(); - matricesDistDz[k]->Zero(); - matricesCorrDrDz[k]->Zero(); - matricesCorrDPhiRDz[k]->Zero(); - matricesCorrDz[k]->Zero(); - - matricesGDistDrDz[k]->Zero(); - matricesGDistDPhiRDz[k]->Zero(); - matricesGDistDz[k]->Zero(); - matricesGCorrDrDz[k]->Zero(); - matricesGCorrDPhiRDz[k]->Zero(); - matricesGCorrDz[k]->Zero(); - } - - if (side == 0) { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularA; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularA; - matricesIrregularDz = fMatrixIntCorrDzIrregularA; - - matricesPhiIrregular = fMatrixPhiListIrregularA; - matricesRIrregular = fMatrixRListIrregularA; - matricesZIrregular = fMatrixZListIrregularA; - matricesLookUpCharge = fMatrixChargeA; - - chargeInterpolator = fInterpolatorChargeA; - potentialInterpolator = fInterpolatorPotentialA; - fLookupDistA->SetLookUpR(matricesDistDrDz); - fLookupDistA->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistA->SetLookUpZ(matricesDistDz); - - fLookupElectricFieldA->SetLookUpR(matricesEr); - fLookupElectricFieldA->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldA->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryA; - f1BoundaryIFC = fFormulaBoundaryIFCA; - f1BoundaryOFC = fFormulaBoundaryOFCA; - f1BoundaryROC = fFormulaBoundaryROCA; - } else { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularC; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularC; - matricesIrregularDz = fMatrixIntCorrDzIrregularC; - matricesPhiIrregular = fMatrixPhiListIrregularC; - matricesRIrregular = fMatrixRListIrregularC; - matricesZIrregular = fMatrixZListIrregularC; - matricesLookUpCharge = fMatrixChargeC; - - chargeInterpolator = fInterpolatorChargeC; - potentialInterpolator = fInterpolatorPotentialC; - fLookupDistC->SetLookUpR(matricesDistDrDz); - fLookupDistC->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistC->SetLookUpZ(matricesDistDz); - fLookupElectricFieldC->SetLookUpR(matricesEr); - fLookupElectricFieldC->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldC->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryC; - f1BoundaryIFC = fFormulaBoundaryIFCC; - f1BoundaryOFC = fFormulaBoundaryOFCC; - f1BoundaryROC = fFormulaBoundaryROCC; - } - // fill the potential boundary - // guess the initial potential - // fill also charge - //pIndex = 0; - - //Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz","%s",Form("Step = 0: Fill Boundary and Charge Densities")); - for (Int_t k = 0; k < phiSlice; k++) { - phi0 = k * gridSizePhi; - matrixV = matricesV[k]; - matrixCharge = matricesCharge[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = j * gridSizeZ; - (*matrixCharge)(i, j) = chargeInterpolator->GetValue(rList[i], phiList[k], zList[j]); - (*matrixV)(i, j) = fFormulaPotentialV->Eval(radius0, phi0, z0); - } - } - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 0: Preparing Charge interpolator: %f\n", w.CpuTime())); - - potentialInterpolator->SetValue(matricesV); - potentialInterpolator->InitCubicSpline(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - w.Start(); - w.Stop(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - w.Stop(); - - // copy to interpolator - LocalDistCorrDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, - GetEzFormula(), rList, phiList, zList, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, - matricesCorrDrDz, matricesCorrDPhiRDz, matricesCorrDz, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - if (side == 0) { - fLookupDistA->CopyFromMatricesToInterpolator(); - fLookupElectricFieldA->CopyFromMatricesToInterpolator(); - } else { - fLookupDistC->CopyFromMatricesToInterpolator(); - fLookupElectricFieldC->CopyFromMatricesToInterpolator(); - } - - w.Start(); - - IntegrateDistCorrDriftLineDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, GetEzFormula(), ezField, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - matricesIrregularDrDz, matricesIrregularDPhiRDz, matricesIrregularDz, - matricesRIrregular, matricesPhiIrregular, matricesZIrregular, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 4: Global correction/distortion: %f\n", w.CpuTime())); - w.Start(); - - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - //// - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 5: Filling up the look up: %f\n", w.CpuTime())); - - if (side == 0) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzA, fMatrixIntDistDPhiREzA, fMatrixIntDistDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzA, fMatrixIntCorrDPhiREzA, fMatrixIntCorrDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistA->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrA->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularA->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzC, fMatrixIntDistDPhiREzC, fMatrixIntDistDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzC, fMatrixIntCorrDPhiREzC, fMatrixIntCorrDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistC->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrC->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularC->CopyFromMatricesToInterpolator(); - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - } - - fInitLookUp = kTRUE; - } - - // memory de-allocation for temporary matrices - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - delete matricesDistDrDz[k]; - delete matricesDistDPhiRDz[k]; - delete matricesDistDz[k]; - - delete matricesCorrDrDz[k]; - delete matricesCorrDPhiRDz[k]; - delete matricesCorrDz[k]; - delete matricesGDistDrDz[k]; - delete matricesGDistDPhiRDz[k]; - delete matricesGDistDz[k]; - - delete matricesGCorrDrDz[k]; - delete matricesGCorrDPhiRDz[k]; - delete matricesGCorrDz[k]; - } - delete lookupLocalDist; - delete lookupLocalCorr; - delete lookupGlobalDist; - delete lookupGlobalCorr; -} -/// Creating look-up tables of Correction/Distortion by linear integration -/// on z line -/// -/// \param nRRow Int_t number of grid in row direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of slices in phi direction -/// \param maxIteration Int_t max iteration for convergence -/// \param stoppingConvergence Double_t stopping criteria for convergence -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ fo -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoisson(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Double_t stoppingConvergence) -{ - // Compute grid size for all direction - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - - TMatrixD *matricesV[phiSlice], *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - // should be set, in another place - const Int_t symmetry = 0; - // do if look up table haven't be initialized - if (!fInitLookUp) { - for (Int_t side = 0; side < 2; side++) { - for (Int_t k = 0; k < phiSlice; k++) { - TMatrixD* mV = matricesV[k]; - TMatrixD* mCharge = matricesCharge[k]; - phi0 = phiList[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = rList[i]; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = zList[j]; - if (side == 1) { - z0 = -TMath::Abs(zList[j]); - } - if (fHistogram3DSpaceCharge != nullptr) { - // * Boundary values and charge distribution setup - (*mV)(i, j) = 0.0; - (*mCharge)(i, j) = -1 * InterpolatePhi(fHistogram3DSpaceCharge, phi0, radius0, z0); - } - } - } - } - AliTPCLookUpTable3DInterpolatorD* lookupEField = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, - matricesEr, - rList, phiSlice, - matricesEPhi, - phiList, nZColumn, - matricesEz, - zList, - fInterpolationOrder); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "Step 1: Solving poisson solver"); - fPoissonSolver->PoissonSolver3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "Step 2: Calculate electric field"); - CalculateEField( - matricesV, - matricesEr, - matricesEPhi, - matricesEz, - nRRow, - nZColumn, - phiSlice, - maxIteration, - symmetry); - lookupEField->CopyFromMatricesToInterpolator(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "Step 3: Fill the look up table"); - - if (side == 0) { - FillLookUpTable(lookupEField, - fMatrixErOverEzA, fMatrixEPhiOverEzA, fMatrixDeltaEzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - fLookupIntENoDriftA->CopyFromMatricesToInterpolator(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - FillLookUpTable(lookupEField, - fMatrixErOverEzC, fMatrixEPhiOverEzC, fMatrixDeltaEzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - fLookupIntENoDriftC->CopyFromMatricesToInterpolator(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - delete lookupEField; - } - fInitLookUp = kTRUE; - } - - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - } -} -/// Force creating look-up table of Correction/Distortion by integration following -/// drift line. -/// -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param maxIteration Int_t Maximum iteration for poisson solver -/// \param stoppingConvergence Convergence error stopping condition for poisson solver -/// -void AliTPCSpaceCharge3DCalc::ForceInitSpaceCharge3DPoissonIntegralDz(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, - Int_t maxIteration, Double_t stoppingConvergence) -{ - fInitLookUp = kFALSE; - InitSpaceCharge3DPoissonIntegralDz(nRRow, nZColumn, phiSlice, maxIteration, stoppingConvergence); -} -/// Electric field Calculation: -/// -/// -/// \param matricesV -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param gridSizeR -/// \param gridSizePhi -/// \param gridSizeZ -/// \param symmetry -/// \param innerRadius -/// -/// \pre Matrix matricesV is assumed had been calculated by Poisson solver -/// \post Results of E-fields are calculated by measuring gradient at potential distribution -/// -/// -/// * Differentiate potential on all direction (r,z and phi) -/// * Non-boundary -> Central difference (3 stencil) TODO: 5 Stencil -/// -/// \f$ \nabla_{r} V(r_{i},\phi_{j},z_{k}) \approx -( V_{i+1,j,k} - V_{i-1,j,k}) / (2* h_{r}) \f$ -/// -/// \f$ -\nabla_{\phi} V(r_{i},\phi_{j},z_{k}) \approx -( V_{i,j-1,k} - V_{i,j+1,k}) / (2* r_{j} * h_{\phi}) \f$ -/// -/// \f$ -\nabla_{z} V(r_{i},\phi_{j},z_{k}) \approx -( V_{i,j,k+1} - V_{i,j,k-1}) / (2* h_{z}) \f$ -/// -/// ~~~ cxx -/// matrixEr(i,j) = -1 * ( arrayV(i+1,j) - arrayV(i-1,j) ) / (2*gridSizeR); // r direction -/// matrixEz(i,j) = -1 * ( arrayV(i,j+1) - arrayV(i,j-1) ) / (2*gridSizeZ) ; // z direction -/// matrixEPhi(i,j) = -1 * (signPlus * arrayVP(i,j) - signMinus * arrayVM(i,j) ) / (2*radius*gridSizePhi) -/// ~~~ -/// -/// * Boundary -> Forward/Backward difference (3 stencil) TODO: 5 Stencil -/// -/// \f$ -\nabla_{r} V(r_{0},\phi_{j},z_{k}) \approx -( -0.5 V_{2,j,k} + 2 V_{1,j,k} - 1.5 * V_{0,j,k}) / h_{r} \f$ -/// -/// \f$ -\nabla_{r} V(r_{nRRow - 1},\phi_{j},z_{k}) \approx -( 1.5 V_{nRRow-1,j,k} - 2.0 V_{nRRow-2,j,k} + 0.5 V_{nRRow -3,j,k}) / h_{\phi} \f$ -/// -void AliTPCSpaceCharge3DCalc::ElectricField(TMatrixD** matricesV, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, - const Float_t gridSizeR, const Float_t gridSizePhi, - const Float_t gridSizeZ, - const Int_t symmetry, const Float_t innerRadius) -{ - Float_t radius; - Int_t mPlus, mMinus, signPlus, signMinus; - for (Int_t m = 0; m < phiSlice; m++) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - if (symmetry == 1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } else if (symmetry == -1) { // Anti-symmetry in phi - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculations is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - TMatrixD& arrayVP = *matricesV[mPlus]; - TMatrixD& arrayVM = *matricesV[mMinus]; - TMatrixD& arrayV = *matricesV[m]; - TMatrixD& matrixEr = *matricesEr[m]; - TMatrixD& matrixEz = *matricesEz[m]; - TMatrixD& matrixEPhi = *matricesEPhi[m]; - - // for non-boundary V - for (Int_t i = 1; i < nRRow - 1; i++) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 1; j < nZColumn - 1; j++) { - matrixEr(i, j) = -1 * (arrayV(i + 1, j) - arrayV(i - 1, j)) / (2 * gridSizeR); // r direction - matrixEz(i, j) = -1 * (arrayV(i, j + 1) - arrayV(i, j - 1)) / (2 * gridSizeZ); // z direction - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - - // for boundary-r - for (Int_t j = 0; j < nZColumn; j++) { - matrixEr(0, j) = -1 * (-0.5 * arrayV(2, j) + 2.0 * arrayV(1, j) - 1.5 * arrayV(0, j)) / - gridSizeR; // forward difference - matrixEr(nRRow - 1, j) = - -1 * (1.5 * arrayV(nRRow - 1, j) - 2.0 * arrayV(nRRow - 2, j) + 0.5 * arrayV(nRRow - 3, j)) / - gridSizeR; // backward difference - } - - for (Int_t i = 0; i < nRRow; i += nRRow - 1) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 1; j < nZColumn - 1; j++) { - matrixEz(i, j) = -1 * (arrayV(i, j + 1) - arrayV(i, j - 1)) / (2 * gridSizeZ); // z direction - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - - // for boundary-z - for (Int_t i = 0; i < nRRow; i++) { - matrixEz(i, 0) = -1 * (-0.5 * arrayV(i, 2) + 2.0 * arrayV(i, 1) - 1.5 * arrayV(i, 0)) / gridSizeZ; - matrixEz(i, nZColumn - 1) = - -1 * - (1.5 * arrayV(i, nZColumn - 1) - 2.0 * arrayV(i, nZColumn - 2) + 0.5 * arrayV(i, nZColumn - 3)) / - gridSizeZ; - } - - for (Int_t i = 1; i < nRRow - 1; i++) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j += nZColumn - 1) { - matrixEr(i, j) = -1 * (arrayV(i + 1, j) - arrayV(i - 1, j)) / (2 * gridSizeR); // r direction - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - - // corner points for EPhi - for (Int_t i = 0; i < nRRow; i += nRRow - 1) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j += nZColumn - 1) { - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - } -} -/// -/// Local distortion and correction, calculate local distortion/correction -/// based on simplified langevin equation, see internal note ALICE-INT-2010-016. -/// -/// <b> Local Distortion </b> -/// -/// Local distortion is calculated based on formulation in ALICE-INT-2010-016, this function assume that -/// electric field \f$\vec{E}(r_{i},z_{j},\phi_{m})\f$ is provided. -/// -/// First, we calculate integration of the Electric field in z-direction for all direction. -/// Assumption: \f$ z_{0} \f$ is location of CE (Central Electrode) and \f$ z_{nZColumn - 1} \f$ is location of End Plate. -/// -/// This integration is in \f$z\f$ direction we can only use trapezoidal rule. -/// -/// Let suppose we want to calculate local distortion at \f$(r_{i},z_{j},\phi_{m})\f$. -/// Assume \f$\vec{E}(r_{i},z_{j+1},\phi_{m}) \f$ and \f$\vec{E}(r_{i},z_{j},\phi_{m}) \f$ are known, see Figure \ref fig1 (a), -/// -/// \anchor fig1 -/// ![Local Distortion](localdist.png) -/// -/// Than we can calculate definite integrations for each directions in respect of $z$ from \f$ z_{j} \f$ to \f$ z_{j + 1} \f$ as follows: -/// -/// \f$ \int^{z_{j+1}}_{z_{j}} \frac{E_{r}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{r}(r_{i},z_{j},\phi_{m}) + E_{r}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j+1}}_{z_{j}} \frac{E_{\phi}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{\phi}(r_{i},z_{j},\phi_{m}) + E_{\phi}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j+1}}_{z_{j}} E_{z}(r_{i},z_{j},\phi_{m}) dzDist \approx \frac{h_{z}}{2.0} \left( E_{z}(r_{i},z_{j},\phi_{m}) + E_{z}(r_{i},z_{j+1},\phi_{m}) \right) \f$ -/// -/// Code sample at \ref impllocaldist is an implementation of the local integration of electric field. -/// -/// \anchor impllocaldist -/// ~~~ -/// Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV-AliTPCPoissonSolver::fgkGG)/AliTPCPoissonSolver::fgkTPCZ0; // = Electric Field (V/cm) Magnitude ~ -400 V/cm; -/// -/// localIntErOverEz = (gridSizeZ/2.0)*((*eR)(i,j)+(*eR)(i,j+1))/(ezField + (*eZ)(i,j)) ; -/// localIntEPhiOverEz = (gridSizeZ/2.0)*((*ePhi)(i,j)+(*ePhi)(i,j+1))/(ezField + (*eZ)(i,j)) ; -/// localIntDeltaEz = (gridSizeZ/2.0)*((*eZ)(i,j)+(*eZ)(i,j+1)) ; -/// ~~~ -/// -/// -/// After we have local integrations for electric fields in each direction, -/// local distortion \f$\hat{\delta}(r_{i},z_{j},\phi_{m})\f$ is calculated by simplified Langevin equation (see Figure \ref1 (b) for illustration): -/// -/// \f$ \hat{\delta}_{rE}(r_{i},z_{j},\phi_{m}) = c_{0} \int^{z_{j+1}}_{z_{j}} \frac{E_{r}}{E_{z}} dzDist + c_{1} \int^{z_{j+1}}_{z_{j}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// ~~~ -/// (*distDrDz)(i,j) = fC0*localIntErOverEz + fC1*localIntEPhiOverEz; -/// ~~~ -/// -/// \f$ r\hat{\delta}_{\phi E}(r_{i},z_{j},\phi_{m}) = - c_{1} \int^{z_{j+1}}_{z_{j}} \frac{E_{j}}{E_{j}} dzDist + c_{0} \int^{z_{j+1}}_{j_{j}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// ~~~ -/// (*distDPhiRDz)(i,j) = fC0*localIntEPhiOverEz - fC1*localIntErOverEz ; -/// ~~~ -/// -/// \f$ \hat{\delta}_{z}(r_{i},z_{j},\phi_{m}) = \int_{z_{j}}^{z_{j+1}} \frac{v^{\prime}(E)}{v_{0}} (E - E_{0}) dzDist\f$ -/// -/// ~~~ -/// (*distDz)(i,j) = localIntDeltaEz*-1*AliTPCPoissonSolver::fgkdvdE; -/// ~~~ -/// -/// Where \f$c_{0}\f$ and \f$c_{1}\f$ are constants (see the ALICE-INT-2010-016 for further details). -/// -/// <b> Local correction </b> -/// -/// Local correction is computed as local distortion where the electric fields are in opposite direction (see Figure \ref fig2 (a)). -/// -/// \anchor fig2 -/// ![Local Correction](localcorr.png) -/// -/// Let suppose we want to calculate local correction at \f$(r_{i},\mathbf{z_{j+1}},\phi_{m})\f$. -/// Assume \f$\vec{E}(r_{i},z_{j+1},\phi_{m}) \f$ and \f$\vec{E}(r_{i},z_{j},\phi_{m}) \f$ are known. -/// -/// Than we can calculate definite integrations for each directions in respect of \f$z\f$ from \f$ z_{j+1} \f$ to \f$ z_{j} \f$ as follows: -/// -/// \f$ \int^{z_{j}}_{z_{j+1}} \frac{E_{r}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx -1 * \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{r}(r_{i},z_{j},\phi_{m}) + E_{r}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j}}_{z_{j+1}} \frac{E_{\phi}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx -1 * \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{\phi}(r_{i},z_{j},\phi_{m}) + E_{\phi}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j}}_{z_{j+1}} E_{z}(r_{i},z_{j},\phi_{m}) dzDist \approx -1 * \frac{h_{z}}{2.0} \left( E_{z}(r_{i},z_{j},\phi_{m}) + E_{z}(r_{i},z_{j+1},\phi_{m}) \right) \f$ -/// -/// Local correction at \f$\hat{\delta'}(r_{i},\mathbf{z_{j+1}},\phi_{m})\f$ is calculated by simplified Langevin equation (see Figure \ref fig2 (b) for illustration): -/// -/// \f$ \hat{\delta'}_{rE}(r_{i},z_{j+1},\phi_{m}) = c_{0} \int^{z_{j}}_{z_{j+1}} \frac{E_{r}}{E_{z}} dzDist + c_{1} \int^{z_{j-1}}_{z_{j}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// \f$ r\hat{\delta'}_{\phi E}(r_{i},z_{j+1},\phi_{m}) = - c_{1} \int^{z_{j}}_{z_{j+1}} \frac{E_{j}}{E_{j}} dzDist + c_{0} \int^{z_{j-1}}_{j_{k}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// \f$ \hat{\delta'}_{z}(r_{i},z_{j+1},\phi_{m}) = \int_{z_{j}}^{z_{j+1}} \frac{v^{\prime}(E)}{v_{0}} (E - E_{0}) dzDist\f$ -/// -/// For implementation, we use the fact that -/// -/// \f$ \hat{\delta'}_{rE}(r_{i},z_{j+1},\phi_{m}) = -1 * \hat{\delta}_{rE}(r_{i},z_{j},\phi_{m}) \f$ -/// -/// \f$ r\hat{\delta'}_{\phi E}(r_{i},z_{j+1},\phi_{m}) = -1 * r\hat{\delta}_{\phi E}(r_{i},z_{j},\phi_{m}) \f$ -/// -/// \f$ \hat{\delta'}_{z}(r_{i},z_{j+1},\phi_{m}) = -1 * \hat{\delta}_{z}(r_{i},z_{j},\phi_{m}) \f$ -/// -/// ~~~ -/// (*corrDrDz)(i,j+1) = -1* (*distDrDz)(i,j) ; -/// (*corrDPhiRDz)(i,j+1) = -1* (*distDPhiRDz)(i,j); -/// (*corrDz)(i,j+1) = -1* (*distDz)(i,j); -/// ~~~ -/// -/// \param matricesEr TMatrixD** electric field for \f$r\f$ component -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesDistDrDz TMatrixD** local distortion \f$\hat{\delta}_{r}\f$ -/// \param matricesDistDPhiRDz TMatrixD** local distortion \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesDistDz TMatrixD** local distortion \f$ \hat{\delta}_{z}\f$ -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculated from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez assume already been calculated -/// \post Local distortion and correction are computed according simplified Langevin equation -/// ~~~ -/// matricesDistDrDz,matricesDistDPhiRDz,matricesDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// matricesCorrDrDz,matricesCorrDPhiRDz,matricesCorrDz -/// ~~~ -/// -void AliTPCSpaceCharge3DCalc::LocalDistCorrDz(TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, - TMatrixD** matricesCorrDrDz, TMatrixD** matricesCorrDPhiRDz, - TMatrixD** matricesCorrDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Float_t gridSizeZ, - const Double_t ezField) -{ - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - TMatrixD* eR; - TMatrixD* ePhi; - TMatrixD* eZ; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - TMatrixD* corrDrDz; - TMatrixD* corrDPhiRDz; - TMatrixD* corrDz; - - // Initialization for j == column-1 integration is 0.0 - for (Int_t m = 0; m < phiSlice; m++) { - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - - (*corrDrDz)(i, 0) = 0.0; - (*corrDPhiRDz)(i, 0) = 0.0; - (*corrDz)(i, 0) = 0.0; - } - } - - // for this case - // use trapezoidal rule assume no ROC displacement - for (Int_t m = 0; m < phiSlice; m++) { - eR = matricesEr[m]; - ePhi = matricesEPhi[m]; - eZ = matricesEz[m]; - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t j = 0; j < nZColumn - 1; j++) { - for (Int_t i = 0; i < nRRow; i++) { - double eZ0 = ezField + (*eZ)(i, j); - double eZ1 = ezField + (*eZ)(i, j + 1); - localIntErOverEz = (gridSizeZ * 0.5) * ((*eR)(i, j) / eZ0 + (*eR)(i, j + 1) / eZ1); - localIntEPhiOverEz = (gridSizeZ * 0.5) * ((*ePhi)(i, j) / eZ0 + (*ePhi)(i, j + 1) / eZ1); - localIntDeltaEz = (gridSizeZ * 0.5) * ((*eZ)(i, j) + (*eZ)(i, j + 1)); - - (*distDrDz)(i, j) = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - (*distDPhiRDz)(i, j) = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - (*distDz)(i, j) = localIntDeltaEz * -1 * AliTPCPoissonSolver::fgkdvdE; - - (*corrDrDz)(i, j + 1) = -1 * (*distDrDz)(i, j); - (*corrDPhiRDz)(i, j + 1) = -1 * (*distDPhiRDz)(i, j); - (*corrDz)(i, j + 1) = -1 * (*distDz)(i, j); - } - } - } -} -/// -/// Local distortion and correction, calculate local distortion/correction for known analitics functions -/// based on simplified langevin equation, see internal note ALICE-INT-2010-016. -/// -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesDistDrDz TMatrixD** local distortion \f$\hat{\delta}_{r}\f$ -/// \param matricesDistDPhiRDz TMatrixD** local distortion \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesDistDz TMatrixD** local distortion \f$ \hat{\delta}_{z}\f$ -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculated from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez assume already been calculated -/// \post Local distortion and correction are computed according simplified Langevin equation -/// ~~~ -/// matricesDistDrDz,matricesDistDPhiRDz,matricesDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// matricesCorrDrDz,matricesCorrDPhiRDz,matricesCorrDz -/// ~~~ -/// -void AliTPCSpaceCharge3DCalc::LocalDistCorrDz(TFormula* intErDzFunction, TFormula* intEPhiRDzFunction, TFormula* intDzFunction, - TFormula* ezFunction, Double_t* rList, Double_t* phiList, Double_t* zList, TMatrixD** matricesDistDrDz, - TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, TMatrixD** matricesCorrDrDz, - TMatrixD** matricesCorrDPhiRDz, TMatrixD** matricesCorrDz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Float_t gridSizeZ, const Double_t ezField) -{ - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - TMatrixD* corrDrDz; - TMatrixD* corrDPhiRDz; - TMatrixD* corrDz; - - Double_t radius, phi, z0, z1; - - // Initialization for j == column-1 integration is 0.0 - for (Int_t m = 0; m < phiSlice; m++) { - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - - (*corrDrDz)(i, 0) = 0.0; - (*corrDPhiRDz)(i, 0) = 0.0; - (*corrDz)(i, 0) = 0.0; - } - } - - // for this case - // use trapezoidal rule assume no ROC displacement - for (Int_t m = 0; m < phiSlice; m++) { - phi = phiList[m]; - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - radius = rList[i]; - - for (Int_t j = 0; j < nZColumn - 1; j++) { - z0 = zList[j]; - z1 = zList[j + 1]; - - localIntErOverEz = (intErDzFunction->Eval(radius, phi, z1) - intErDzFunction->Eval(radius, phi, z0)) / (ezField + ezFunction->Eval(radius, phi, z0)); - localIntEPhiOverEz = (intEPhiRDzFunction->Eval(radius, phi, z1) - intEPhiRDzFunction->Eval(radius, phi, z0)) / (ezField + ezFunction->Eval(radius, phi, z0)); - localIntDeltaEz = intDzFunction->Eval(radius, phi, z1) - intDzFunction->Eval(radius, phi, z0); - - (*distDrDz)(i, j) = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - (*distDPhiRDz)(i, j) = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - (*distDz)(i, j) = localIntDeltaEz * -1 * AliTPCPoissonSolver::fgkdvdE; - - (*corrDrDz)(i, j + 1) = -1 * (*distDrDz)(i, j); - (*corrDPhiRDz)(i, j + 1) = -1 * (*distDPhiRDz)(i, j); - (*corrDz)(i, j + 1) = -1 * (*distDz)(i, j); - } - } - } -} - -/// IntegrateDistCorrDriftLineDz, integration of local distortion by following electron drift -/// See explanation at LocalDistCorrDz -/// -/// -/// \param matricesEr TMatrixD** electric field for \f$r\f$ component -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculate from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez are provided -/// \post Local correction are computed according simplified Langevin equation -/// ~~~ -/// matricesCorrDz,matricesCorrDPhiRDz,matricesDistDz -/// ~~~ -/// -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDz( - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist, - TMatrixD** matricesGDistDrDz, - TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, - TMatrixD** matricesGCorrIrregularDz, - TMatrixD** matricesRIrregular, TMatrixD** matricesPhiIrregular, TMatrixD** matricesZIrregular, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Double_t* rList, const Double_t* phiList, const Double_t* zList) -{ - - Float_t drDist, dPhi, dzDist, ddR, ddRPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection; - radiusCorrection = 0.0; - radius = 0.0; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - TMatrixD* mCorrIrregularDrDz; - TMatrixD* mCorrIrregularDPhiRDz; - TMatrixD* mCorrIrregularDz; - TMatrixD* mRIrregular; - TMatrixD* mPhiIrregular; - TMatrixD* mZIrregular; - Int_t j = nZColumn - 1; - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - - /// - (*mDistDrDz)(i, j) = 0.; - (*mDistDPhiRDz)(i, j) = 0.; - (*mDistDz)(i, j) = 0.; - - (*mCorrDrDz)(i, j) = 0.; - (*mCorrDPhiRDz)(i, j) = 0.; - (*mCorrDz)(i, j) = 0.; - - //////////////// use irregular grid look up table for correction - if (fCorrectionType == kIrregularInterpolator) { - (*mCorrIrregularDrDz)(i, j) = 0.0; - (*mCorrIrregularDPhiRDz)(i, j) = 0.0; - (*mCorrIrregularDz)(i, j) = -0.0; - - // distorted point - (*mRIrregular)(i, j) = radius0; - (*mPhiIrregular)(i, j) = phi0; - (*mZIrregular)(i, j) = z0; - } - /////////////// - } - } - - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - - drDist = 0.0; - dPhi = 0.0; - dzDist = 0.0; - - // follow the drift line from z=j --> nZColumn - 1 - for (Int_t jj = j; jj < nZColumn; jj++) { - // interpolation the local distortion for current position - // phi += ddRPhi / radius; - phi = phi0 + dPhi; - radius = radius0 + drDist; - z = zList[jj] + dzDist; - - // regulate phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - lookupLocalDist->GetValue(radius, phi, z, ddR, ddRPhi, ddZ); - - // add local distortion - drDist += ddR; - dPhi += (ddRPhi / radius); - dzDist += ddZ; - } - // set the global distortion after following the electron drift - (*mDistDrDz)(i, j) = drDist; - (*mDistDPhiRDz)(i, j) = dPhi * radius0; - (*mDistDz)(i, j) = dzDist; - /////////////// use irregular grid look up table for correction - // set - if (fCorrectionType == kIrregularInterpolator) { - // values should be negative of distortions - (*mCorrIrregularDrDz)(i, j) = -drDist; - (*mCorrIrregularDPhiRDz)(i, j) = -1 * dPhi * (radius0 + drDist); - (*mCorrIrregularDz)(i, j) = -dzDist; - - // distorted point - (*mRIrregular)(i, j) = radius0 + drDist; - (*mPhiIrregular)(i, j) = phi0 + dPhi; - (*mZIrregular)(i, j) = z0 + dzDist; - } - /////////////// - - // put the radius to the original value - if (fCorrectionType == kRegularInterpolator) { - if (j == nZColumn - 2) { - radiusCorrection = radius0; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - - drDist += ddR; - dzDist += ddZ; - dPhi += ddRPhi / radiusCorrection; - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - } - } -} - -// oudated, to be removed once changes in aliroot are pushed -/// follow the drift for exact function -/// -/// \param intDrDzF -/// \param intDPhiDzF -/// \param intDzDzF -/// \param ezField -/// \param matricesGDistDrDz -/// \param matricesGDistDPhiRDz -/// \param matricesGDistDz -/// \param matricesGCorrDrDz -/// \param matricesGCorrDPhiRDz -/// \param matricesGCorrDz -/// \param matricesGCorrIrregularDrDz -/// \param matricesGCorrIrregularDPhiRDz -/// \param matricesGCorrIrregularDz -/// \param matricesRIrregular -/// \param matricesPhiIrregular -/// \param matricesZIrregular -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param rList -/// \param phiList -/// \param zList -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDz( - TFormula* intDrDzF, TFormula* intDPhiDzF, TFormula* intDzDzF, const Double_t ezField, - TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, TMatrixD** matricesGCorrIrregularDz, TMatrixD** matricesRIrregular, - TMatrixD** matricesPhiIrregular, TMatrixD** matricesZIrregular, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Double_t* rList, const Double_t* phiList, const Double_t* zList) -{ - Float_t drDist, dPhi, dzDist, ddR, ddRPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection, z1; - - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - // set parameters for function - // hard coded, will be modified after pull at AliRoot - - TFormula* ezF = GetEzFormula(); - radiusCorrection = 0.0; - radius = 0.0; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - TMatrixD* mCorrIrregularDrDz; - TMatrixD* mCorrIrregularDPhiRDz; - TMatrixD* mCorrIrregularDz; - TMatrixD* mRIrregular; - TMatrixD* mPhiIrregular; - TMatrixD* mZIrregular; - Int_t j = nZColumn - 1; - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - /// - (*mDistDrDz)(i, j) = 0.0; - (*mDistDPhiRDz)(i, j) = 0.0; - (*mDistDz)(i, j) = 0.0; - - //////////////// use irregular grid look up table for correction - // set - (*mCorrIrregularDrDz)(i, j) = 0.0; - (*mCorrIrregularDPhiRDz)(i, j) = 0.0; - (*mCorrIrregularDz)(i, j) = 0.0; - - // distorted point - (*mRIrregular)(i, j) = radius0; - (*mPhiIrregular)(i, j) = phi0; - (*mZIrregular)(i, j) = z0; - /////////////// - } - } - - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - - drDist = 0.0; - dPhi = 0.0; - dzDist = 0.0; - ddRPhi = 0.0; - - // follow the drift line from z=j --> nZColumn - 1 - for (Int_t jj = j; jj < nZColumn; jj++) { - // interpolation the local distortion for current position - phi = phi0 + dPhi; - radius = radius0 + drDist; - z = zList[jj] + dzDist; - z1 = z + (zList[j + 1] - zList[j]); - // regulate phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalDist->GetValue(radius, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radius, phi, z1) - intDrDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radius, phi, z1) - intDPhiDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radius, phi, z1) - intDzDzF->Eval(radius, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; - - drDist += ddR; - dPhi += (ddRPhi / radius); - dzDist += ddZ; - - // add local distortion - } - // set the global distortion after following the electron drift - (*mDistDrDz)(i, j) = drDist; - (*mDistDPhiRDz)(i, j) = dPhi * radius0; - (*mDistDz)(i, j) = dzDist; - /////////////// use irregular grid look up table for correction - // use oppsite directions of distortion - (*mCorrIrregularDrDz)(i, j) = -drDist; - (*mCorrIrregularDPhiRDz)(i, j) = -dPhi * (radius0 + drDist); - (*mCorrIrregularDz)(i, j) = -dzDist; - - // distorted point - (*mRIrregular)(i, j) = radius0 + drDist; - (*mPhiIrregular)(i, j) = phi0 + dPhi; - (*mZIrregular)(i, j) = z0 + dzDist; - /////////////// - - // put the radius to the original value - if (j == nZColumn - 2) { - radiusCorrection = radius0; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - //dPhi = (*mCorrDPhiRDz)(i, j + 1) /radiusCorrection; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - z1 = z - (zList[j + 1] - zList[j]); - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radiusCorrection, phi, z1) - intDrDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radiusCorrection, phi, z1) - intDPhiDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radiusCorrection, phi, z1) - intDzDzF->Eval(radiusCorrection, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; - - drDist += ddR; - dzDist += ddZ; - dPhi += ddRPhi / radiusCorrection; - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - } -} - -/// follow the drift for exact function -/// -/// \param intDrDzF -/// \param intDPhiDzF -/// \param intDzDzF -/// \param ezField -/// \param matricesGDistDrDz -/// \param matricesGDistDPhiRDz -/// \param matricesGDistDz -/// \param matricesGCorrDrDz -/// \param matricesGCorrDPhiRDz -/// \param matricesGCorrDz -/// \param matricesGCorrIrregularDrDz -/// \param matricesGCorrIrregularDPhiRDz -/// \param matricesGCorrIrregularDz -/// \param matricesRIrregular -/// \param matricesPhiIrregular -/// \param matricesZIrregular -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param rList -/// \param phiList -/// \param zList -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDz( - TFormula* intDrDzF, TFormula* intDPhiDzF, TFormula* intDzDzF, TFormula* ezF, const Double_t ezField, - TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, TMatrixD** matricesGCorrIrregularDz, TMatrixD** matricesRIrregular, - TMatrixD** matricesPhiIrregular, TMatrixD** matricesZIrregular, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Double_t* rList, const Double_t* phiList, const Double_t* zList) -{ - - Float_t drDist, dPhi, dzDist, ddR, ddRPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection, z1; - - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - - radiusCorrection = 0.0; - radius = 0.0; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - TMatrixD* mCorrIrregularDrDz; - TMatrixD* mCorrIrregularDPhiRDz; - TMatrixD* mCorrIrregularDz; - TMatrixD* mRIrregular; - TMatrixD* mPhiIrregular; - TMatrixD* mZIrregular; - Int_t j = nZColumn - 1; - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - /// - (*mDistDrDz)(i, j) = 0.0; - (*mDistDPhiRDz)(i, j) = 0.0; - (*mDistDz)(i, j) = 0.0; - - //////////////// use irregular grid look up table for correction - // set - (*mCorrIrregularDrDz)(i, j) = 0.0; - (*mCorrIrregularDPhiRDz)(i, j) = 0.0; - (*mCorrIrregularDz)(i, j) = 0.0; - - // distorted point - (*mRIrregular)(i, j) = radius0; - (*mPhiIrregular)(i, j) = phi0; - (*mZIrregular)(i, j) = z0; - /////////////// - } - } - - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - - drDist = 0.0; - dPhi = 0.0; - dzDist = 0.0; - ddRPhi = 0.0; - - // follow the drift line from z=j --> nZColumn - 1 - for (Int_t jj = j; jj < nZColumn; jj++) { - // interpolation the local distortion for current position - phi = phi0 + dPhi; - radius = radius0 + drDist; - z = zList[jj] + dzDist; - z1 = z + (zList[j + 1] - zList[j]); - // regulate phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalDist->GetValue(radius, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radius, phi, z1) - intDrDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radius, phi, z1) - intDPhiDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radius, phi, z1) - intDzDzF->Eval(radius, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; - - drDist += ddR; - dPhi += (ddRPhi / radius); - dzDist += ddZ; - - // add local distortion - } - // set the global distortion after following the electron drift - (*mDistDrDz)(i, j) = drDist; - (*mDistDPhiRDz)(i, j) = dPhi * radius0; - (*mDistDz)(i, j) = dzDist; - /////////////// use irregular grid look up table for correction - // use oppsite directions of distortion - (*mCorrIrregularDrDz)(i, j) = -drDist; - (*mCorrIrregularDPhiRDz)(i, j) = -dPhi * (radius0 + drDist); - (*mCorrIrregularDz)(i, j) = -dzDist; - - // distorted point - (*mRIrregular)(i, j) = radius0 + drDist; - (*mPhiIrregular)(i, j) = phi0 + dPhi; - (*mZIrregular)(i, j) = z0 + dzDist; - /////////////// - - // put the radius to the original value - if (j == nZColumn - 2) { - radiusCorrection = radius0; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - //dPhi = (*mCorrDPhiRDz)(i, j + 1) /radiusCorrection; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - z1 = z - (zList[j + 1] - zList[j]); - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radiusCorrection, phi, z1) - intDrDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radiusCorrection, phi, z1) - intDPhiDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radiusCorrection, phi, z1) - intDzDzF->Eval(radiusCorrection, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; // two times? - - drDist += ddR; - dzDist += ddZ; - dPhi += ddRPhi / radiusCorrection; - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - } -} - -/// See explanation at LocalDistCorrDz -/// -/// -/// \param matricesEr TMatrixD** electric field for \f$r\f$ component -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculate from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez are provided -/// \post Local correction are computed according simplified Langevin equation -/// ~~~ -/// matricesCorrDz,matricesCorrDPhiRDz,matricesDistDz -/// ~~~ -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDzWithLookUp(AliTPCLookUpTable3DInterpolatorD* lookupLocalDist, TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr, TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, Double_t* rList, Double_t* phiList, Double_t* zList) -{ - - Float_t drDist, dRPhi, dPhi, dzDist, ddR, ddRPhi, ddPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - - // allocate look up for temporal - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDistTemp = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, 1); - - Int_t j = nZColumn - 1; - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - /// - (*mDistDrDz)(i, j) = 0.0; - (*mDistDPhiRDz)(i, j) = 0.0; - (*mDistDz)(i, j) = 0.0; - - (*mCorrDrDz)(i, j) = 0.0; - (*mCorrDPhiRDz)(i, j) = 0.0; - (*mCorrDz)(i, j) = 0.0; - } - } - - drDist = 0.0; - dRPhi = 0.0; - dPhi = 0.0; - dzDist = 0.0; - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - - // do from j to 0 - // follow the drift - radius0 = rList[i]; - - lookupLocalDist->GetValue(radius0, phi0, z0, ddR, ddRPhi, ddZ); - ddPhi = ddRPhi / radius0; - - if (j < nZColumn - 2) { - phi = phi0 + ddPhi; - radius = radius0 + ddR; - z = zList[j + 1] + ddZ; - - lookupGlobalDistTemp->GetValue(radius, phi, z, drDist, dRPhi, dzDist); - dPhi = dRPhi / radius; - } - - (*mDistDrDz)(i, j) = drDist + ddR; - (*mDistDPhiRDz)(i, j) = (dPhi + ddPhi) * radius0; - (*mDistDz)(i, j) = dzDist + ddZ; - - // copy to 1D for being able to interpolate at next step - if (j > 0) { - (*mDistDrDz)(i, j - 1) = drDist + ddR; - (*mDistDPhiRDz)(i, j - 1) = (dPhi + ddPhi) * radius0; - (*mDistDz)(i, j - 1) = dzDist + ddZ; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - - drDist += ddR; - dzDist += ddZ; - dPhi += (ddRPhi / radiusCorrection); - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - - lookupGlobalDistTemp->CopyFromMatricesToInterpolator(j); - if (j > 0) { - lookupGlobalDistTemp->CopyFromMatricesToInterpolator(j - 1); - } - } - delete lookupGlobalDistTemp; -} - -/// -/// \param lookupGlobal -/// \param lookupRDz -/// \param lookupPhiRDz -/// \param lookupDz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param rList -/// \param phiList -/// \param zList -void AliTPCSpaceCharge3DCalc::FillLookUpTable(AliTPCLookUpTable3DInterpolatorD* lookupGlobal, TMatrixD** lookupRDz, - TMatrixD** lookupPhiRDz, TMatrixD** lookupDz, const Int_t nRRow, - const Int_t nZColumn, const Int_t phiSlice, const Double_t* rList, - const Double_t* phiList, const Double_t* zList) -{ - Double_t r, phi, z; - TMatrixD* mR; - TMatrixD* mPhiR; - TMatrixD* mDz; - - /// * Interpolate basicLookup tables; once for each rod, then sum the results - for (Int_t k = 0; k < fNPhiSlices; k++) { - phi = fListPhi[k]; - - mR = lookupRDz[k]; - mPhiR = lookupPhiRDz[k]; - mDz = lookupDz[k]; - for (Int_t j = 0; j < fNZColumns; j++) { - z = fListZ[j]; // Symmetric solution in Z that depends only on ABS(Z) - - for (Int_t i = 0; i < fNRRows; i++) { - r = fListR[i]; - - lookupGlobal->GetValue(r, phi, z, (*mR)(i, j), (*mPhiR)(i, j), (*mDz)(i, j)); - } - } - } -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - GetDistortionCylAC(x, roc, dx); -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetDistortionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetDistortionCylAC", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning distortion vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::GetDistortionCylAC", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupIntDistA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntDistC->GetValue(r, phi, -1 * z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -/// Get Correction from irregular table -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning correction vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Double_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - r = x[0]; - phi = x[1]; - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceChargeCalc3D::GetCorrectionCylACIrregular", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - // get distortion from irregular table - - if (z > 0) { - fLookupIntCorrIrregularA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntCorrIrregularC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// Get Correction from irregular table -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning correction vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Double_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - r = x[0]; - phi = x[1]; - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceChargeCalc3D::GetCorrectionCylACIrregular", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - // get distortion from irregular table - - if (side == 0) { - fLookupIntCorrIrregularA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntCorrIrregularC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// Get correction regular grid by following electron -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetDistortionCylAC", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning correction vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceChargeCalc3D::GetCorrectionCylAC", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupIntCorrA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntCorrC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -void AliTPCSpaceCharge3DCalc::GetDistortion(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - Float_t pCyl[3]; // a point in cylindrical coordinate - Float_t dCyl[3]; // distortion - - pCyl[0] = TMath::Sqrt(x[0] * x[0] + x[1] * x[1]); - pCyl[1] = TMath::ATan2(x[1], x[0]); - - // normalize phi - while (pCyl[1] > TMath::Pi()) { - pCyl[1] -= TMath::TwoPi(); - } - while (pCyl[1] < -TMath::Pi()) { - pCyl[1] += TMath::TwoPi(); - } - - pCyl[2] = x[2]; // Create temporary copy of x[2] - - GetDistortionCylAC(pCyl, roc, dCyl); - - // Calculate distorted position - if (pCyl[0] > 0.0) { - //pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - pCyl[1] = pCyl[1] + fCorrectionFactor * dCyl[1] / pCyl[0]; - pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - } - - dCyl[2] = fCorrectionFactor * dCyl[2]; - - // distortion in x,y and z - dx[0] = (pCyl[0] * TMath::Cos(pCyl[1]) - x[0]); - dx[1] = (pCyl[0] * TMath::Sin(pCyl[1]) - x[1]); - dx[2] = dCyl[2]; -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (fCorrectionType == kRegularInterpolator) { - GetCorrectionCylAC(x, roc, dx); - } else { - GetCorrectionCylACIrregular(x, roc, dx); - } -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrection(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - Float_t pCyl[3]; // a point in cylindrical coordinate - Float_t dCyl[3]; // distortion - - pCyl[0] = TMath::Sqrt(x[0] * x[0] + x[1] * x[1]); - pCyl[1] = TMath::ATan2(x[1], x[0]); - pCyl[2] = x[2]; // Create temporary copy of x[2] - - if (fCorrectionType == kRegularInterpolator) { - while (pCyl[1] > TMath::Pi()) { - pCyl[1] -= TMath::TwoPi(); - } - while (pCyl[1] < -TMath::Pi()) { - pCyl[1] += TMath::TwoPi(); - } - - GetCorrectionCylAC(pCyl, roc, dCyl); - } else { - GetCorrectionCylACIrregular(pCyl, roc, dCyl); - } - - // Calculate distorted position - if (pCyl[0] > 0.0) { - //pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - pCyl[1] = pCyl[1] + fCorrectionFactor * dCyl[1] / pCyl[0]; - pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - } - - dCyl[2] = fCorrectionFactor * dCyl[2]; - - // distortion in x,y and z - dx[0] = (pCyl[0] * TMath::Cos(pCyl[1]) - x[0]); - dx[1] = (pCyl[0] * TMath::Sin(pCyl[1]) - x[1]); - dx[2] = dCyl[2]; -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrection(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const -{ - Float_t pCyl[3]; // a point in cylindrical coordinate - Float_t dCyl[3]; // distortion - - pCyl[0] = TMath::Sqrt(x[0] * x[0] + x[1] * x[1]); - pCyl[1] = TMath::ATan2(x[1], x[0]); - pCyl[2] = x[2]; // Create temporary copy of x[2] - - if (fCorrectionType == kRegularInterpolator) { - while (pCyl[1] > TMath::Pi()) { - pCyl[1] -= TMath::TwoPi(); - } - while (pCyl[1] < -TMath::Pi()) { - pCyl[1] += TMath::TwoPi(); - } - - GetCorrectionCylAC(pCyl, roc, dCyl); - } else { - GetCorrectionCylACIrregular(pCyl, roc, dCyl, side); - } - - // Calculate distorted position - if (pCyl[0] > 0.0) { - pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - pCyl[1] = pCyl[1] + fCorrectionFactor * dCyl[1] / pCyl[0]; - } - - dCyl[2] = fCorrectionFactor * dCyl[2]; - - // distortion in x,y and z - dx[0] = (pCyl[0] * TMath::Cos(pCyl[1]) - x[0]); - dx[1] = (pCyl[0] * TMath::Sin(pCyl[1]) - x[1]); - dx[2] = dCyl[2]; -} - -/// Use 3D space charge map as an optional input -/// The layout of the input histogram is assumed to be: (phi,r,z) -/// Density histogram is expected to bin in C/m^3 -/// -/// Standard histogram interpolation is used in order to use the density at center of bin -/// -/// \param hisSpaceCharge3D -/// \param norm -void AliTPCSpaceCharge3DCalc::SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm) -{ - fHistogram3DSpaceCharge = hisSpaceCharge3D; - fInitLookUp = kFALSE; - - Info("AliTPCSpaceCharge3DCalc:SetInputSpaceCharge", "Set Input Space Charge by 3D"); - - Double_t radius0, z0, phi0; - TMatrixD* charge; - for (Int_t iSide = 0; iSide < 2; iSide++) { - for (Int_t k = 0; k < fNPhiSlices; k++) { - if (iSide == 0) { - charge = fMatrixChargeA[k]; - } else { - charge = fMatrixChargeC[k]; - } - - phi0 = fListPhi[k]; - - for (Int_t i = 0; i < fNRRows; i++) { - radius0 = fListR[i]; - - for (Int_t j = 0; j < fNZColumns; j++) { - if (iSide == 0) { - z0 = fListZ[j]; - } else { - z0 = -fListZ[j]; - } - - (*charge)(i, j) = norm * InterpolatePhi(hisSpaceCharge3D, phi0, radius0, z0); - - } // end j - } // end i - } // end phi - } - - fInterpolatorChargeA->SetValue(fMatrixChargeA); - if (fInterpolationOrder > 2) { - fInterpolatorChargeA->InitCubicSpline(); - } - fInterpolatorChargeC->SetValue(fMatrixChargeC); - if (fInterpolationOrder > 2) { - fInterpolatorChargeC->InitCubicSpline(); - } -} - -/// SetInputCharge -/// -/// \param hisSpaceCharge3D TH3* histogram for space charge -/// \param norm Double_t norm/weight -/// \param side Int_t side = 0 => side A, side = 1 => side C -/// -/// side effects: create Charge interpolator -void AliTPCSpaceCharge3DCalc::SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm, Int_t side) -{ - if (side == 0) { - fHistogram3DSpaceChargeA = hisSpaceCharge3D; - } else { - fHistogram3DSpaceChargeC = hisSpaceCharge3D; - } - - Double_t rMin = hisSpaceCharge3D->GetYaxis()->GetBinCenter(0); - Double_t rMax = hisSpaceCharge3D->GetYaxis()->GetBinUpEdge(hisSpaceCharge3D->GetYaxis()->GetNbins()); - Double_t zMin = hisSpaceCharge3D->GetZaxis()->GetBinCenter(0); - Double_t zMax = hisSpaceCharge3D->GetZaxis()->GetBinCenter(hisSpaceCharge3D->GetZaxis()->GetNbins()); - Double_t radius0, z0, phi0; - TMatrixD* charge; - - for (Int_t k = 0; k < fNPhiSlices; k++) { - if (side == 0) { - charge = fMatrixChargeA[k]; - } else { - charge = fMatrixChargeC[k]; - } - - phi0 = fListPhi[k]; - for (Int_t i = 0; i < fNRRows; i++) { - radius0 = fListR[i]; - for (Int_t j = 0; j < fNZColumns; j++) { - z0 = fListZ[j]; - - if (radius0 > rMin && radius0 < rMax && z0 > zMin && z0 < zMax) { - (*charge)(i, j) = norm * InterpolatePhi(hisSpaceCharge3D, phi0, radius0, z0); - } - } // end j - } // end i - } // end phi - - if (side == 0) { - fInterpolatorChargeA->SetValue(fMatrixChargeA); - fInterpolatorChargeA->InitCubicSpline(); - } else { - fInterpolatorChargeC->SetValue(fMatrixChargeC); - fInterpolatorChargeC->InitCubicSpline(); - } - - fInitLookUp = kFALSE; -} - -/// InterpolationPhi is only used for reading from TH3F (since it is not cylindrical) -/// -/// \param r -/// \param z -/// \return -Double_t AliTPCSpaceCharge3DCalc::InterpolatePhi(const TH3* h3, const Double_t phi, const Double_t r, const Double_t z) -{ - - Int_t ubx = h3->GetXaxis()->FindBin(phi); - if (phi < h3->GetXaxis()->GetBinCenter(ubx)) { - ubx -= 1; - } - Int_t obx = ubx + 1; - Int_t uby = h3->GetYaxis()->FindBin(r); - if (r < h3->GetYaxis()->GetBinCenter(uby)) { - uby -= 1; - } - Int_t oby = uby + 1; - Int_t ubz = h3->GetZaxis()->FindBin(z); - if (z < h3->GetZaxis()->GetBinCenter(ubz)) { - ubz -= 1; - } - Int_t obz = ubz + 1; - - if (uby <= 0 || ubz <= 0 || - oby > h3->GetYaxis()->GetNbins() || obz > h3->GetZaxis()->GetNbins()) { - return 0; - } - - if (ubx <= 0) { - ubx = h3->GetXaxis()->GetNbins(); - } - - if (obx > h3->GetXaxis()->GetNbins()) { - obx = 1; - } - - Double_t xw = h3->GetXaxis()->GetBinCenter(obx) - h3->GetXaxis()->GetBinCenter(ubx); - Double_t yw = h3->GetYaxis()->GetBinCenter(oby) - h3->GetYaxis()->GetBinCenter(uby); - Double_t zw = h3->GetZaxis()->GetBinCenter(obz) - h3->GetZaxis()->GetBinCenter(ubz); - Double_t xd = (phi - h3->GetXaxis()->GetBinCenter(ubx)) / xw; - Double_t yd = (r - h3->GetYaxis()->GetBinCenter(uby)) / yw; - Double_t zd = (z - h3->GetZaxis()->GetBinCenter(ubz)) / zw; - Double_t v[] = {h3->GetBinContent(ubx, uby, ubz), h3->GetBinContent(ubx, uby, obz), - h3->GetBinContent(ubx, oby, ubz), h3->GetBinContent(ubx, oby, obz), - h3->GetBinContent(obx, uby, ubz), h3->GetBinContent(obx, uby, obz), - h3->GetBinContent(obx, oby, ubz), h3->GetBinContent(obx, oby, obz)}; - Double_t i1 = v[0] * (1 - zd) + v[1] * zd; - Double_t i2 = v[2] * (1 - zd) + v[3] * zd; - Double_t j1 = v[4] * (1 - zd) + v[5] * zd; - Double_t j2 = v[6] * (1 - zd) + v[7] * zd; - Double_t w1 = i1 * (1 - yd) + i2 * yd; - Double_t w2 = j1 * (1 - yd) + j2 * yd; - Double_t result = w1 * (1 - xd) + w2 * xd; - return result; -} -/// returns the (input) space charge density at a given point according -/// Note: input in [cm], output in [C/m^3/e0] !! -Float_t AliTPCSpaceCharge3DCalc::GetSpaceChargeDensity(Float_t r, Float_t phi, Float_t z) -{ - while (phi < 0) { - phi += TMath::TwoPi(); - } - while (phi > TMath::TwoPi()) { - phi -= TMath::TwoPi(); - } - - const Int_t order = 1; // - - const Float_t x[] = {r, phi, z}; - Float_t sc = 0; - if (z > -1e-16) { - sc = GetChargeCylAC(x, 0); - } else { - sc = GetChargeCylAC(x, 18); - } - - return sc; -} -/// returns the (input) space charge density at a given point according -/// Note: input in [cm], output in [C/m^3/e0] !! -Float_t AliTPCSpaceCharge3DCalc::GetPotential(Float_t r, Float_t phi, Float_t z) -{ - while (phi < 0) { - phi += TMath::TwoPi(); - } - while (phi > TMath::TwoPi()) { - phi -= TMath::TwoPi(); - } - - const Int_t order = 1; // - - const Float_t x[] = {r, phi, z}; - Float_t v = 0; - if (z > -1e-16) { - v = GetPotentialCylAC(x, 0); - } else { - v = GetPotentialCylAC(x, 18); - } - - return v; -} -/// -/// -/// \param matricesDistDrDz TMatrixD ** matrix of global distortion drDist (r direction) -/// \param matricesDistDPhiRDz TMatrixD ** matrix of global distortion dRPhi (phi r direction) -/// \param matricesDistDz TMatrixD ** matrix of global distortion dzDist (z direction) -/// \param rList Double_t * points of r in the grid (ascending mode) -/// \param zList Double_t * points of z in the grid (ascending mode) -/// \param phiList Double_t * points of phi in the grid (ascending mode) -/// \param nRRow Int_t number of grid in r direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of grid in phi direction -/// \param nStep Int_t number of step to calculate local dist -/// -void AliTPCSpaceCharge3DCalc::InverseGlobalToLocalDistortionGlobalInvTable( - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, Double_t* rList, - Double_t* zList, Double_t* phiList, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Int_t nStep, const Bool_t useCylAC, Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t type) -{ - Double_t z, phi, r, zAfter, zPrevious, ddR, ddRPhi, ddZ, zl, drDist, dRPhi, dzDist, ddPhi, dPhi, deltaZ, r0, z0, phi0; - Float_t x[3], dx[3], pdx[3]; - Int_t roc; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - - // correction build up for inverse flow - TMatrixD* corrDrDz; - TMatrixD* corrDPhiRDz; - TMatrixD* corrDz; - TMatrixD* listR; - TMatrixD* listPhi; - TMatrixD* listZ; - TMatrixD* matricesCorrDrDz[phiSlice]; - TMatrixD* matricesCorrDPhiRDz[phiSlice]; - TMatrixD* matricesCorrDz[phiSlice]; - TMatrixD* matricesRList[phiSlice]; - TMatrixD* matricesPhiList[phiSlice]; - TMatrixD* matricesZList[phiSlice]; - - for (Int_t m = 0; m < phiSlice; m++) { - matricesCorrDrDz[m] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[m] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[m] = new TMatrixD(nRRow, nZColumn); - - matricesRList[m] = new TMatrixD(nRRow, nZColumn); - matricesPhiList[m] = new TMatrixD(nRRow, nZColumn); - matricesZList[m] = new TMatrixD(nRRow, nZColumn); - } - - AliTPCLookUpTable3DInterpolatorIrregularD* lookupInverseCorr = new AliTPCLookUpTable3DInterpolatorIrregularD( - nRRow, matricesCorrDrDz, matricesRList, phiSlice, matricesCorrDPhiRDz, - matricesPhiList, nZColumn, matricesCorrDz, matricesZList, 2, - stepR, stepZ, stepPhi, type); - - lookupInverseCorr->SetKernelType(GetRBFKernelType()); - - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - - listR = matricesRList[k]; - listPhi = matricesPhiList[k]; - listZ = matricesZList[k]; - - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - - for (Int_t j = 0; j < nZColumn; j++) { - (*listR)(i, j) = rList[i]; - (*listPhi)(i, j) = phiList[k]; - (*listZ)(i, j) = zList[j]; - } - } - } - - // 1) create global correction - deltaZ = (zList[1] - zList[0]); - Int_t iAnchor, kAnchor, zAnchor; - - for (Int_t j = nZColumn - 2; j >= 0; j--) { - - roc = 0; // FIXME - for (Int_t k = 0; k < phiSlice; k++) { - - corrDrDz = matricesCorrDrDz[k]; - corrDPhiRDz = matricesCorrDPhiRDz[k]; - corrDz = matricesCorrDz[k]; - - listR = matricesRList[k]; - listPhi = matricesPhiList[k]; - listZ = matricesZList[k]; - - for (Int_t i = 0; i < nRRow; i++) { - // get global distortion - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - - drDist = 0.0; - dzDist = 0.0; - dRPhi = 0.0; - - x[0] = r; - x[1] = phi; - x[2] = z; - - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, dx); - } else { - GetDistortionCyl(x, roc, dx); - } - - drDist = dx[0]; - dzDist = dx[2]; - dRPhi = dx[1]; - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - - (*corrDrDz)(i, j + 1) = -drDist; - (*corrDz)(i, j + 1) = -dzDist; - (*corrDPhiRDz)(i, j + 1) = -dRPhi; - - (*listR)(i, j + 1) = r + drDist; - (*listPhi)(i, j + 1) = phi + dRPhi / r; - (*listZ)(i, j + 1) = z + dzDist; - } - } - lookupInverseCorr->CopyFromMatricesToInterpolator(j + 1); - } - // 2) calculate local distortion - for (Int_t j = nZColumn - 2; j >= 0; j--) { - roc = 0; // FIXME - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - // get global distortion - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - drDist = 0.0; - dzDist = 0.0; - dRPhi = 0.0; - - if (j < nZColumn - 2) { - // get global distortion of this point - x[0] = r; - x[1] = phi; - x[2] = z; - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, dx); - } else { - GetDistortionCyl(x, roc, dx); - } - - r0 = r + dx[0]; - z0 = zList[j + 1] + dx[2]; - phi0 = phi + (dx[1] / r); - iAnchor = TMath::FloorNint((r0 - AliTPCPoissonSolver::fgkIFCRadius) / gridSizeR); - kAnchor = TMath::FloorNint(phi0 / gridSizePhi); - zAnchor = TMath::FloorNint(z0 / gridSizeZ); - - if (j > nZColumn - (GetIrregularGridSize() + 2)) { - lookupInverseCorr->GetValue(r0, phi0, z0, drDist, dRPhi, dzDist, iAnchor, kAnchor, zAnchor, - nRRow / 4 + 1, phiSlice / 4 + 1, 1, 0); - } else { - lookupInverseCorr->GetValue(r0, phi0, z0, drDist, dRPhi, dzDist, iAnchor, kAnchor, zAnchor, - nRRow / 4 + 1, phiSlice / 4 + 1, GetIrregularGridSize(), 0); - } - - phi0 = phi0 + ((dRPhi) / r0); - r0 = r0 + (drDist); - z0 += dzDist; - - x[0] = r0; - x[1] = phi0; - x[2] = z0; - - if (phi0 < 0) { - phi0 = TMath::TwoPi() + phi0; - } - if (phi0 > TMath::TwoPi()) { - phi0 = phi0 - TMath::TwoPi(); - } - - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, pdx); - } else { - GetDistortionCyl(x, roc, pdx); - } - - drDist = (dx[0] - pdx[0]); - dzDist = (dx[2] - pdx[2]); - dRPhi = (dx[1] - pdx[1]); - - } else if (j == (nZColumn - 2)) { - - x[0] = r; - x[1] = phi; - x[2] = zList[j]; - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, dx); - } else { - GetDistortionCyl(x, roc, dx); - } - - x[2] = zList[j + 1]; - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, pdx); - } else { - GetDistortionCyl(x, roc, pdx); - } - drDist = (dx[0] - pdx[0]); - dzDist = (dx[2] - pdx[2]); - dRPhi = (dx[1] - pdx[1]); - } - - (*distDrDz)(i, j) = drDist; - (*distDz)(i, j) = dzDist; - (*distDPhiRDz)(i, j) = dRPhi; - } - } - } - - for (Int_t m = 0; m < phiSlice; m++) { - delete matricesCorrDrDz[m]; - delete matricesCorrDPhiRDz[m]; - delete matricesCorrDz[m]; - delete matricesRList[m]; - delete matricesPhiList[m]; - delete matricesZList[m]; - } - delete lookupInverseCorr; -} -/// -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param matricesInvLocalIntErDz -/// \param matricesInvLocalIntEPhiDz -/// \param matricesInvLocalIntEz -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param rList -/// \param zList -/// \param phiList -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseLocalDistortionToElectricField( - TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, - TMatrixD** matricesInvLocalIntEz, TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, Double_t* rList, Double_t* zList, Double_t* phiList, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - // calculate integral - Float_t localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, z2; - Double_t r; - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - TMatrixD* distDrDz; - TMatrixD* distDz; - TMatrixD* distDPhiRDz; - TMatrixD* tDistDz; - TMatrixD* tDistDPhiRDz; - TMatrixD* tDistDrDz; - Float_t c02c12 = fC0 * fC0 + fC1 * fC1; - - // solve local integration - for (Int_t j = 0; j < nZColumn; j++) { - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDz = matricesDistDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - - tDistDrDz = matricesInvLocalIntErDz[k]; - tDistDz = matricesInvLocalIntEz[k]; - tDistDPhiRDz = matricesInvLocalIntEPhiDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - localIntErOverEz = fC0 * (*distDrDz)(i, j) - fC1 * (*distDPhiRDz)(i, j); - localIntErOverEz = localIntErOverEz / (fC0 * fC0 + fC1 * fC1); - localIntEPhiOverEz = ((*distDrDz)(i, j) - (fC0 * localIntErOverEz)) / fC1; - localIntDeltaEz = -1 * (*distDz)(i, j) / AliTPCPoissonSolver::fgkdvdE; // two times? - (*tDistDrDz)(i, j) = localIntErOverEz; - (*tDistDPhiRDz)(i, j) = localIntEPhiOverEz; - (*tDistDz)(i, j) = localIntDeltaEz; - } - } - } - TMatrixD* mEPhi; - TMatrixD* mEr; - TMatrixD* mEz; - - // use central-backward-forward difference for calculating Electric field component - for (Int_t m = 0; m < phiSlice; m++) { - mEPhi = matricesEPhi[m]; - mEr = matricesEr[m]; - mEz = matricesEz[m]; - distDrDz = matricesInvLocalIntErDz[m]; - distDPhiRDz = matricesInvLocalIntEPhiDz[m]; - distDz = matricesInvLocalIntEz[m]; - for (Int_t i = 0; i < nRRow; i++) { - (*mEr)(i, 0) = ((*distDrDz)(i, 0) / gridSizeZ) * -1 * ezField; - (*mEPhi)(i, 0) = ((*distDPhiRDz)(i, 0) / gridSizeZ) * -1 * ezField; - (*mEz)(i, 0) = ((*distDz)(i, 0) / gridSizeZ); - (*mEr)(i, nZColumn - 1) = - ((-0.5 * (*distDrDz)(i, nZColumn - 3) + 1.5 * (*distDrDz)(i, nZColumn - 2)) / gridSizeZ) * ezField; - (*mEPhi)(i, nZColumn - 1) = - ((-0.5 * (*distDPhiRDz)(i, nZColumn - 3) + 1.5 * (*distDPhiRDz)(i, nZColumn - 2)) / gridSizeZ) * - ezField; - (*mEz)(i, nZColumn - 1) = - (-0.5 * (*distDz)(i, nZColumn - 3) + 1.5 * (*distDz)(i, nZColumn - 2)) / gridSizeZ; - } - - for (Int_t i = 0; i < nRRow; i++) { - for (Int_t j = 1; j < nZColumn - 1; j++) { - (*mEr)(i, j) = (((*distDrDz)(i, j) + (*distDrDz)(i, j - 1)) / (2 * gridSizeZ)) * - ezField; // z direction - (*mEPhi)(i, j) = (((*distDPhiRDz)(i, j) + (*distDPhiRDz)(i, j - 1)) / (2 * gridSizeZ)) * - ezField; // z direction - (*mEz)(i, j) = ((*distDz)(i, j) + (*distDz)(i, j - 1)) / (2 * gridSizeZ); // z direction - } - } - } -} -/// Inverse Electric Field to Charge -/// using partial differential -/// -/// \param matricesCharge -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param rList -/// \param zList -/// \param phiList -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseElectricFieldToCharge( - TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - Double_t* rList, Double_t* zList, Double_t* phiList, const Int_t nRRow, - const Int_t nZColumn, const Int_t phiSlice) -{ - - Float_t radius; - Double_t drDist, dzDist, dPhi; - Int_t mPlus, mMinus, mPlus2, mMinus2, signPlus, signMinus; - Int_t symmetry = 0; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - - for (Int_t m = 0; m < phiSlice; m++) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - mPlus2 = m + 2; - mMinus2 = m - 2; - if (symmetry == 1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } else if (symmetry == -1) { // Anti-symmetry in phi - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculations is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - if (mPlus2 > phiSlice - 1) { - mPlus2 = m + 2 - phiSlice; - } - if (mMinus2 < 0) { - mMinus2 = m - 2 + phiSlice; - } - } - - TMatrixD& matrixCharge = *matricesCharge[m]; - TMatrixD& matrixEr = *matricesEr[m]; - TMatrixD& matrixEz = *matricesEz[m]; - TMatrixD& matrixEPhi = *matricesEPhi[m]; - TMatrixD& matrixEPhiM = *matricesEPhi[mMinus]; - TMatrixD& matrixEPhiP = *matricesEPhi[mPlus]; - TMatrixD& matrixEPhiM2 = *matricesEPhi[mMinus2]; - TMatrixD& matrixEPhiP2 = *matricesEPhi[mPlus2]; - - // for non-boundary V - for (Int_t i = 2; i < nRRow - 2; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 2; j < nZColumn - 2; j++) { - drDist = (-matrixEr(i + 2, j) + 8 * matrixEr(i + 1, j) - 8 * matrixEr(i - 1, j) + matrixEr(i - 2, j)) / - (12 * gridSizeR); // r direction - dzDist = (-matrixEz(i, j + 2) + 8 * matrixEz(i, j + 1) - 8 * matrixEz(i, j - 1) + matrixEz(i, j - 2)) / - (12 * gridSizeZ); // r direction - dPhi = (-matrixEPhiP2(i, j) + 8 * matrixEPhiP(i, j) - 8 * matrixEPhiM(i, j) + matrixEPhiM2(i, j)) / - (12 * gridSizePhi); // phi - - matrixCharge(i, j) = -1 * (matrixEr(i, j) / radius + drDist + dPhi / radius + dzDist); - } - } - - // for boundary in r - for (Int_t j = 2; j < nZColumn - 2; j++) { - - // r near inner radius - // for index r[0] - radius = AliTPCPoissonSolver::fgkIFCRadius; - drDist = (-(11.0 / 6.0) * matrixEr(0, j) + (3.0 * matrixEr(1, j)) - (1.5 * matrixEr(2, j)) + - ((1.0 / 3.0) * matrixEr(3, j))) / - gridSizeR; // forward difference - - // drDist = ( -(1.5)*matrixEr(0,j) + (2.0*matrixEr(1,j)) - (0.5*matrixEr(2,j)) ) / gridSizeR; - - dzDist = (-matrixEz(0, j + 2) + 8 * matrixEz(0, j + 1) - 8 * matrixEz(0, j - 1) + matrixEz(0, j - 2)) / - (12.0 * gridSizeZ); // z direction - dPhi = (-matrixEPhiP2(0, j) + 8 * matrixEPhiP(0, j) - 8 * matrixEPhiM(0, j) + matrixEPhiM2(0, j)) / - (12.0 * gridSizePhi); - - matrixCharge(0, j) = -1 * (matrixEr(0, j) / radius + drDist + dPhi / radius + dzDist); - - // index use central difference 3-point center - radius = AliTPCPoissonSolver::fgkIFCRadius + gridSizeR; - // drDist = (-matrixEr(3,j) +6.0*matrixEr(2,j) - 3.0*matrixEr(1,j) - 2*matrixEr(0,j) ) / (6.0*gridSizeR) ; // forward difference - drDist = (matrixEr(2, j) - matrixEr(0, j)) / (2.0 * gridSizeR); - - dzDist = (-matrixEz(1, j + 2) + 8 * matrixEz(1, j + 1) - 8 * matrixEz(1, j - 1) + matrixEz(1, j - 2)) / - (12 * gridSizeZ); // z direction - dPhi = (-matrixEPhiP2(1, j) + 8 * matrixEPhiP(1, j) - 8 * matrixEPhiM(1, j) + matrixEPhiM2(1, j)) / - (12 * gridSizePhi); - matrixCharge(1, j) = -1 * (matrixEr(1, j) / radius + drDist + dPhi / radius + dzDist); - - // index use central difference 3-point center - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 2) * gridSizeR; - // drDist = (2.0 * matrixEr(nRRow - 1,j) + 3.0*matrixEr(nRRow - 2,j) - 6.0*matrixEr(nRRow -3,j) + matrixEr(nRRow-4,j) ) / (6.0*gridSizeR) ; - drDist = (matrixEr(nRRow - 1, j) - matrixEr(nRRow - 3, j)) / (2.0 * gridSizeR); - - dzDist = (-matrixEz(nRRow - 2, j + 2) + 8 * matrixEz(nRRow - 2, j + 1) - 8 * matrixEz(nRRow - 2, j - 1) + - matrixEz(nRRow - 2, j - 2)) / - (12 * gridSizeZ); - dPhi = (-matrixEPhiP2(nRRow - 2, j) + 8 * matrixEPhiP(nRRow - 2, j) - 8 * matrixEPhiM(nRRow - 2, j) + - matrixEPhiM2(nRRow - 2, j)) / - (12.0 * gridSizePhi); - matrixCharge(nRRow - 2, j) = -1 * (matrixEr(nRRow - 2, j) / radius + drDist + dPhi / radius + dzDist); - - // index r[nRRow -1] backward difference - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 1) * gridSizeR; - //drDist = ( 1.5*matrixEr(nRRow-1,j) - 2.0*matrixEr(nRRow-2,j) + 0.5*matrixEr(nRRow-3,j) ) / gridSizeR ; // backward difference - drDist = - (-(11.0 / 6.0) * matrixEr(nRRow - 1, j) + (3.0 * matrixEr(nRRow - 2, j)) - - (1.5 * matrixEr(nRRow - 3, j)) + - ((1.0 / 3.0) * matrixEr(nRRow - 4, j))) / - (-1 * gridSizeR); - - //dzDist = ( matrixEz(nRRow-1,j+1) - matrixEz(nRRow-1,j-1) ) / (2*gridSizeZ) ; // z direction - dzDist = (-matrixEz(nRRow - 1, j + 2) + 8 * matrixEz(nRRow - 1, j + 1) - 8 * matrixEz(nRRow - 1, j - 1) + - matrixEz(nRRow - 1, j - 2)) / - (12 * gridSizeZ); - - dPhi = (-matrixEPhiP2(nRRow - 1, j) + 8 * matrixEPhiP(nRRow - 1, j) - 8 * matrixEPhiM(nRRow - 1, j) + - matrixEPhiM2(nRRow - 1, j)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, j) = -1 * (matrixEr(nRRow - 1, j) / radius + drDist + dPhi / radius + dzDist); - } - - // boundary z - for (Int_t i = 2; i < nRRow - 2; i++) { - // z[0] - radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - dzDist = (-(11.0 / 6.0) * matrixEz(i, 0) + (3.0 * matrixEz(i, 1)) - (1.5 * matrixEz(i, 2)) + - ((1.0 / 3.0) * matrixEz(i, 3))) / - (1 * gridSizeZ); // forward difference - drDist = (-matrixEr(i + 2, 0) + 8 * matrixEr(i + 1, 0) - 8 * matrixEr(i - 1, 0) + matrixEr(i - 2, 0)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, 0) + 8 * matrixEPhiP(i, 0) - 8 * matrixEPhiM(i, 0) + matrixEPhiM2(i, 0)) / - (12 * gridSizePhi); - matrixCharge(i, 0) = -1 * (matrixEr(i, 0) / radius + drDist + dPhi / radius + dzDist); - - dzDist = (matrixEz(i, 2) - matrixEz(i, 0)) / (2.0 * gridSizeZ); // forward difference - - drDist = (-matrixEr(i + 2, 1) + 8 * matrixEr(i + 1, 1) - 8 * matrixEr(i - 1, 1) + matrixEr(i - 2, 1)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, 1) + 8 * matrixEPhiP(i, 1) - 8 * matrixEPhiM(i, 1) + matrixEPhiM2(i, 1)) / - (12 * gridSizePhi); - matrixCharge(i, 1) = -1 * (matrixEr(i, 1) / radius + drDist + dPhi / radius + dzDist); - - dzDist = (matrixEz(i, nZColumn - 1) - matrixEz(i, nZColumn - 3)) / (2.0 * gridSizeZ); // forward difference - - drDist = (-matrixEr(i + 2, nZColumn - 2) + 8 * matrixEr(i + 1, nZColumn - 2) - - 8 * matrixEr(i - 1, nZColumn - 2) + - matrixEr(i - 2, nZColumn - 2)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, nZColumn - 2) + 8 * matrixEPhiP(i, nZColumn - 2) - - 8 * matrixEPhiM(i, nZColumn - 2) + - matrixEPhiM2(i, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(i, nZColumn - 2) = -1 * (matrixEr(i, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - - dzDist = (-(11.0 / 6.0) * matrixEz(i, nZColumn - 1) + (3.0 * matrixEz(i, nZColumn - 2)) - - (1.5 * matrixEz(i, nZColumn - 3)) + ((1.0 / 3.0) * matrixEz(i, nZColumn - 4))) / - (-gridSizeZ); // backward difference - drDist = (-matrixEr(i + 2, nZColumn - 1) + 8 * matrixEr(i + 1, nZColumn - 1) - - 8 * matrixEr(i - 1, nZColumn - 1) + - matrixEr(i - 2, nZColumn - 1)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, nZColumn - 1) + 8 * matrixEPhiP(i, nZColumn - 1) - - 8 * matrixEPhiM(i, nZColumn - 1) + - matrixEPhiM2(i, nZColumn - 1)) / - (12 * gridSizePhi); - matrixCharge(i, nZColumn - 1) = -1 * (matrixEr(i, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - } - // for corner points - // corner points for EPhi - radius = AliTPCPoissonSolver::fgkIFCRadius; - drDist = - (-0.5 * matrixEr(2, 0) + 2.0 * matrixEr(1, 0) - 1.5 * matrixEr(0, 0)) / gridSizeR; // forward difference - dzDist = - (-0.5 * matrixEz(0, 2) + 2.0 * matrixEz(0, 1) - 1.5 * matrixEz(0, 0)) / gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(0, 0) + 8 * matrixEPhiP(0, 0) - 8 * matrixEPhiM(0, 0) + matrixEPhiM2(0, 0)) / - (12 * gridSizePhi); - matrixCharge(0, 0) = -1 * (matrixEr(0, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = - (-0.5 * matrixEr(2, 1) + 2.0 * matrixEr(1, 1) - 1.5 * matrixEr(0, 1)) / gridSizeR; // forward difference - dzDist = (matrixEz(0, 2) - matrixEz(0, 0)) / (2.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(0, 1) + 8 * matrixEPhiP(0, 1) - 8 * matrixEPhiM(0, 1) + matrixEPhiM2(0, 1)) / - (12 * gridSizePhi); - matrixCharge(0, 1) = -1 * (matrixEr(0, 1) / radius + drDist + dPhi / radius + dzDist); - drDist = - (-0.5 * matrixEr(2, nZColumn - 2) + 2.0 * matrixEr(1, nZColumn - 2) - 1.5 * matrixEr(0, nZColumn - 2)) / - gridSizeR; // forward difference - dzDist = (2.0 * matrixEz(0, nZColumn - 1) + 3.0 * matrixEz(0, nZColumn - 2) - 6.0 * matrixEz(0, nZColumn - 3) + - matrixEz(0, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(0, nZColumn - 2) + 8 * matrixEPhiP(0, nZColumn - 2) - 8 * matrixEPhiM(0, nZColumn - 2) + - matrixEPhiM2(0, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(0, nZColumn - 2) = -1 * (matrixEr(0, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - drDist = - (-0.5 * matrixEr(2, nZColumn - 1) + 2.0 * matrixEr(1, nZColumn - 1) - 1.5 * matrixEr(0, nZColumn - 1)) / - gridSizeR; // forward difference - dzDist = (1.5 * matrixEz(0, nZColumn - 1) - 2.0 * matrixEz(0, nZColumn - 2) + 0.5 * matrixEz(0, nZColumn - 3)) / - gridSizeZ; // backward difference - dPhi = (-matrixEPhiP2(0, nZColumn - 1) + 8 * matrixEPhiP(0, nZColumn - 1) - 8 * matrixEPhiM(0, nZColumn - 1) + - matrixEPhiM2(0, nZColumn - 1)) / - (12 * gridSizePhi); - matrixCharge(0, nZColumn - 1) = -1 * (matrixEr(0, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - - radius = AliTPCPoissonSolver::fgkIFCRadius + gridSizeR; - drDist = (-matrixEr(3, 0) + 6.0 * matrixEr(2, 0) - 3.0 * matrixEr(1, 0) - 2 * matrixEr(0, 0)) / - (6.0 * gridSizeR); // forward difference - dzDist = - (-0.5 * matrixEz(1, 2) + 2.0 * matrixEz(1, 1) - 1.5 * matrixEz(1, 0)) / gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(1, 0) + 8 * matrixEPhiP(1, 0) - 8 * matrixEPhiM(1, 0) + matrixEPhiM2(1, 0)) / - (12 * gridSizePhi); - matrixCharge(1, 0) = -1 * (matrixEr(1, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = (-matrixEr(3, 1) + 6.0 * matrixEr(2, 1) - 3.0 * matrixEr(1, 1) - 2 * matrixEr(0, 1)) / - (6.0 * gridSizeR); // forward difference - dzDist = (-matrixEz(1, 3) + 6.0 * matrixEz(1, 2) - 3.0 * matrixEz(1, 1) - 2 * matrixEz(1, 0)) / - (6.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(1, 1) + 8 * matrixEPhiP(1, 1) - 8 * matrixEPhiM(1, 1) + matrixEPhiM2(1, 1)) / - (12 * gridSizePhi); - matrixCharge(1, 1) = -1 * (matrixEr(1, 1) / radius + drDist + dPhi / radius + dzDist); - drDist = (-matrixEr(3, nZColumn - 2) + 6.0 * matrixEr(2, nZColumn - 2) - 3.0 * matrixEr(1, nZColumn - 2) - - 2 * matrixEr(0, nZColumn - 2)) / - (6.0 * gridSizeR); // forward difference - dzDist = (2.0 * matrixEz(1, nZColumn - 1) + 3.0 * matrixEz(1, nZColumn - 2) - 6.0 * matrixEz(1, nZColumn - 3) + - matrixEz(1, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(1, nZColumn - 2) + 8 * matrixEPhiP(1, nZColumn - 2) - 8 * matrixEPhiM(1, nZColumn - 2) + - matrixEPhiM2(1, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(1, nZColumn - 2) = -1 * (matrixEr(1, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - - drDist = (-matrixEr(3, nZColumn - 1) + 6.0 * matrixEr(2, nZColumn - 1) - 3.0 * matrixEr(1, nZColumn - 1) - - 2 * matrixEr(0, nZColumn - 1)) / - (6.0 * gridSizeR); // forward difference - dzDist = (1.5 * matrixEz(1, nZColumn - 1) - 2.0 * matrixEz(1, nZColumn - 2) + 0.5 * matrixEz(1, nZColumn - 3)) / - gridSizeZ; // backward difference - dPhi = (-matrixEPhiP2(1, nZColumn - 1) + 8 * matrixEPhiP(1, nZColumn - 1) - 8 * matrixEPhiM(1, nZColumn - 1) + - matrixEPhiM2(1, nZColumn - 1)) / - (12 * gridSizePhi); - matrixCharge(1, nZColumn - 1) = -1 * (matrixEr(1, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 2) * gridSizeR; - drDist = (2.0 * matrixEr(nRRow - 1, 0) + 3.0 * matrixEr(nRRow - 2, 0) - 6.0 * matrixEr(nRRow - 3, 0) + - matrixEr(nRRow - 4, 0)) / - (6.0 * gridSizeR); // backward difference - dzDist = (-0.5 * matrixEz(nRRow - 2, 2) + 2.0 * matrixEz(nRRow - 2, 1) - 1.5 * matrixEz(nRRow - 2, 0)) / - gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(nRRow - 2, 0) + 8 * matrixEPhiP(nRRow - 2, 0) - 8 * matrixEPhiM(nRRow - 2, 0) + - matrixEPhiM2(nRRow - 2, 0)) / - (12 * gridSizePhi); - - matrixCharge(nRRow - 2, 0) = -1 * (matrixEr(nRRow - 2, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = (2.0 * matrixEr(nRRow - 1, 1) + 3.0 * matrixEr(nRRow - 2, 1) - 6.0 * matrixEr(nRRow - 3, 1) + - matrixEr(nRRow - 4, 1)) / - (6.0 * gridSizeR); // backward difference - dzDist = (-matrixEz(nRRow - 2, 3) + 6.0 * matrixEz(nRRow - 2, 2) - 3.0 * matrixEz(nRRow - 2, 1) - - 2 * matrixEz(nRRow - 2, 0)) / - (6.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(nRRow - 2, 1) + 8 * matrixEPhiP(nRRow - 2, 1) - 8 * matrixEPhiM(nRRow - 2, 1) + - matrixEPhiM2(nRRow - 2, 1)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 2, 1) = -1 * (matrixEr(nRRow - 2, 1) / radius + drDist + dPhi / radius + dzDist); - drDist = (2.0 * matrixEr(nRRow - 1, nZColumn - 2) + 3.0 * matrixEr(nRRow - 2, nZColumn - 2) - - 6.0 * matrixEr(nRRow - 3, nZColumn - 2) + matrixEr(nRRow - 4, nZColumn - 2)) / - (6.0 * gridSizeR); // backward difference - dzDist = (2.0 * matrixEz(nRRow - 2, nZColumn - 1) + 3.0 * matrixEz(nRRow - 2, nZColumn - 2) - - 6.0 * matrixEz(nRRow - 2, nZColumn - 3) + matrixEz(nRRow - 2, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(nRRow - 2, nZColumn - 2) + 8 * matrixEPhiP(nRRow - 2, nZColumn - 2) - - 8 * matrixEPhiM(nRRow - 2, nZColumn - 2) + matrixEPhiM2(nRRow - 2, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 2, nZColumn - 2) = - -1 * (matrixEr(nRRow - 2, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - drDist = (2.0 * matrixEr(nRRow - 1, nZColumn - 1) + 3.0 * matrixEr(nRRow - 2, nZColumn - 1) - - 6.0 * matrixEr(nRRow - 3, nZColumn - 1) + matrixEr(nRRow - 4, nZColumn - 1)) / - (6.0 * gridSizeR); // backward difference - dzDist = (1.5 * matrixEz(0, nZColumn - 1) - 2.0 * matrixEz(0, nZColumn - 2) + 0.5 * matrixEz(0, nZColumn - 3)) / - gridSizeZ; // backward difference - dPhi = (-matrixEPhiP2(nRRow - 2, nZColumn - 1) + 8 * matrixEPhiP(nRRow - 2, nZColumn - 1) - - 8 * matrixEPhiM(nRRow - 2, nZColumn - 1) + matrixEPhiM2(nRRow - 2, nZColumn - 1)) / - (12 * gridSizePhi); - - matrixCharge(nRRow - 2, nZColumn - 1) = - -1 * (matrixEr(nRRow - 2, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 1) * gridSizeR; - drDist = (1.5 * matrixEr(nRRow - 1, 0) - 2.0 * matrixEr(nRRow - 2, 0) + 0.5 * matrixEr(nRRow - 3, 0)) / - gridSizeR; // backward difference - dzDist = (-0.5 * matrixEz(nRRow - 1, 2) + 2.0 * matrixEz(nRRow - 1, 1) - 1.5 * matrixEz(nRRow - 1, 0)) / - gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(nRRow - 1, 0) + 8 * matrixEPhiP(nRRow - 1, 0) - 8 * matrixEPhiM(nRRow - 1, 0) + - matrixEPhiM2(nRRow - 1, 0)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, 0) = -1 * (matrixEr(nRRow - 1, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = (1.5 * matrixEr(nRRow - 1, 1) - 2.0 * matrixEr(nRRow - 2, 1) + 0.5 * matrixEr(nRRow - 3, 1)) / - gridSizeR; // backward difference - dzDist = (-matrixEz(nRRow - 1, 3) + 6.0 * matrixEz(nRRow - 1, 2) - 3.0 * matrixEz(nRRow - 1, 1) - - 2 * matrixEz(nRRow - 1, 0)) / - (6.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(nRRow - 1, 1) + 8 * matrixEPhiP(nRRow - 1, 1) - 8 * matrixEPhiM(nRRow - 1, 1) + - matrixEPhiM2(nRRow - 1, 1)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, 1) = -1 * (matrixEr(nRRow - 1, 1) / radius + drDist + dPhi / radius + dzDist); - - drDist = (1.5 * matrixEr(nRRow - 1, nZColumn - 2) - 2.0 * matrixEr(nRRow - 2, nZColumn - 2) + - 0.5 * matrixEr(nRRow - 3, nZColumn - 2)) / - gridSizeR; // backward difference - dzDist = (2.0 * matrixEz(nRRow - 1, nZColumn - 1) + 3.0 * matrixEz(nRRow - 1, nZColumn - 2) - - 6.0 * matrixEz(nRRow - 1, nZColumn - 3) + matrixEz(nRRow - 1, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(nRRow - 1, nZColumn - 2) + 8 * matrixEPhiP(nRRow - 1, nZColumn - 2) - - 8 * matrixEPhiM(nRRow - 1, nZColumn - 2) + matrixEPhiM2(nRRow - 1, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, nZColumn - 2) = - -1 * (matrixEr(nRRow - 1, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - - drDist = (1.5 * matrixEr(nRRow - 1, nZColumn - 1) - 2.0 * matrixEr(nRRow - 2, nZColumn - 1) + - 0.5 * matrixEr(nRRow - 3, nZColumn - 1)) / - gridSizeR; // backward difference - dzDist = (1.5 * matrixEz(nRRow - 1, nZColumn - 1) - 2.0 * matrixEz(nRRow - 1, nZColumn - 2) + - 0.5 * matrixEz(nRRow - 1, nZColumn - 3)) / - gridSizeZ; // backward difference - - dPhi = (-matrixEPhiP2(nRRow - 1, nZColumn - 1) + 8 * matrixEPhiP(nRRow - 1, nZColumn - 1) - - 8 * matrixEPhiM(nRRow - 1, nZColumn - 1) + matrixEPhiM2(nRRow - 1, nZColumn - 1)) / - (12 * gridSizePhi); - - matrixCharge(nRRow - 1, nZColumn - 1) = - -1 * (matrixEr(nRRow - 1, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - } -} -/// -/// \param matricesCharge -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param matricesInvLocalIntErDz -/// \param matricesInvLocalIntEPhiDz -/// \param matricesInvLocalEz -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param nSize -/// \param useCylAC -/// \param stepR -/// \param stepZ -/// \param stepPhi -/// \param interpType -/// \param inverseType -void AliTPCSpaceCharge3DCalc::InverseDistortionMaps( - TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t nSize, - const Bool_t useCylAC, Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t interpType) -{ - // can inverse after lookup table for global distortion been calculated - Double_t* rList = new Double_t[nRRow]; - Double_t* zList = new Double_t[nZColumn]; - Double_t* phiList = new Double_t[phiSlice]; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = (j * gridSizeZ); - } - // memory allocation - if (fInitLookUp) { - // 1) get local distortion - InverseGlobalToLocalDistortionGlobalInvTable(matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, - zList, phiList, nRRow, nZColumn, phiSlice, nSize, useCylAC, - stepR, stepZ, stepPhi, interpType); - - fLookupInverseDistA->SetLookUpR(matricesDistDrDz); - fLookupInverseDistA->SetLookUpPhi(matricesDistDPhiRDz); - fLookupInverseDistA->SetLookUpZ(matricesDistDz); - fLookupInverseDistA->CopyFromMatricesToInterpolator(); - - // 2) calculate local integral - InverseLocalDistortionToElectricField(matricesEr, matricesEPhi, matricesEz, matricesInvLocalIntErDz, - matricesInvLocalIntEPhiDz, matricesInvLocalEz, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, zList, - phiList, nRRow, nZColumn, phiSlice); - // 3) get potential from electric field assuming zero boundaries - InverseElectricFieldToCharge(matricesCharge, matricesEr, matricesEPhi, matricesEz, rList, zList, phiList, nRRow, - nZColumn, phiSlice); - } - - // copy charge inverse here just for side A (TODO: do for side C) - for (Int_t k = 0; k < phiSlice; k++) { - *(fMatrixChargeInverseA[k]) = *(matricesCharge[k]); - } - fInterpolatorInverseChargeA->SetValue(fMatrixChargeInverseA); - fInterpolatorInverseChargeA->InitCubicSpline(); - - delete[] zList; - delete[] rList; - delete[] phiList; -} -/// CalculateEField (New Version: with reorganization of modules) -/// Calculate E field based on look-up table created by Poisson Solver -/// * Differentiate V(r) and solve for E(r) using special equations for the first and last row -/// * Integrate E(r)/E(z) from point of origin to pad plane -/// * Differentiate V(r) and solve for E(phi) -/// * Integrate E(phi)/E(z) from point of origin to pad plane -/// * Differentiate V(r) and solve for E(z) using special equations for the first and last row -/// * Integrate (E(z)-Ez(ROC)) from point of origin to pad plane -/// -/// \param matricesV TMatrixD** 3D matrix representing calculated potential -/// \param matricesErOverEz TMatrix** 3D matrix representing e-field at Er/Ez -/// \param matricesEPhiOverEz TMatrix** 3D matrix representing e-field at EPhi/Ez -/// \param matricesDeltaZ TMatrix** 3D matrix representing e-field at DeltaZ -/// \param nRRow Int_t number of nRRow (in R direction) -/// \param nZColumn Int_t number of nZColumn (in Z direction) -/// \param phiSlice Int_t number of (phi slices in phi direction) -/// \param symmetry Int_t symmetry? -/// \param rocDisplace rocDisplacement -/// -/// \pre Matrix matricesV is assumed had been calculated by Poisson solver -/// \post Results of Integration and Derivations for E-field calculation are stored in matricesErOverEz, matricesEPhiOverEz, matricesDeltaZ -/// -void AliTPCSpaceCharge3DCalc::CalculateEField( - TMatrixD** matricesV, TMatrixD** matricesErOverEz, TMatrixD** matricesEPhiOverEz, - TMatrixD** matricesDeltaEz, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Int_t symmetry, Bool_t rocDisplacement) -{ - - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - TMatrixD *matricesEr[phiSlice], *matricesEz[phiSlice], *matricesEPhi[phiSlice]; - - //Allocate memory for electric field r,z, phi direction - for (Int_t k = 0; k < phiSlice; k++) { - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - } - - //Differentiate V(r) and solve for E(r) using special equations for the first and last row - TStopwatch w; - w.Start(); - - ElectricField(matricesV, matricesEr, matricesEPhi, matricesEz, nRRow, nZColumn, - phiSlice, gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Time for calculation E-field CPU = %f s\n", w.CpuTime())); - - //Integrate E(r)/E(z) from point of origin to pad plane - - IntegrateEz(matricesErOverEz, matricesEr, nRRow, nZColumn, phiSlice, ezField); - IntegrateEz(matricesEPhiOverEz, matricesEPhi, nRRow, nZColumn, phiSlice, ezField); - IntegrateEz(matricesDeltaEz, matricesEz, nRRow, nZColumn, phiSlice, -1.0); - - // calculate z distortion from the integrated Delta Ez residuals - // and include the equivalence (Volt to cm) of the ROC shift !! - for (Int_t m = 0; m < phiSlice; m++) { - TMatrixD& arrayV = *matricesV[m]; - TMatrixD& deltaEz = *matricesDeltaEz[m]; - - for (Int_t j = 0; j < nZColumn; j++) { - for (Int_t i = 0; i < nRRow; i++) { - // Scale the Ez distortions with the drift velocity -> delivers cm - deltaEz(i, j) = deltaEz(i, j) * AliTPCPoissonSolver::fgkdvdE; - // ROC Potential in cm equivalent - Double_t dzROCShift = arrayV(i, nZColumn - 1) / ezField; - if (rocDisplacement) { - deltaEz(i, j) = deltaEz(i, j) + dzROCShift; // add the ROC mis alignment - } - } - } - } - // clear the temporary arrays lists - - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesEr[k]; - delete matricesEz[k]; - delete matricesEPhi[k]; - } -} -/// -/// Integrate at z direction Ez for electron drift calculation -/// -/// -/// \param matricesExOverEz TMatrixD** 3D matrix representing ExOverEz -/// \param matricesEx TMatrix** 3D matrix representing e-field at x direction -/// \param nRRow const Int_t number of nRRow (in R direction) -/// \param nZColumn const Int_t number of nZColumn (in Z direction) -/// \param phiSlice const Int_t number of (phiSlice in phi direction) -/// \param ezField const Double_t Electric field in z direction -/// -/// \pre matricesEx is assumed already been calculated by ElectricFieldCalculation -/// \post Matrix matricesExOverEz is calculated by integration of matricesEx -/// -void AliTPCSpaceCharge3DCalc::IntegrateEz( - TMatrixD** matricesExOverEz, TMatrixD** matricesEx, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Double_t ezField) -{ - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - for (Int_t m = 0; m < phiSlice; m++) { - TMatrixD& eXoverEz = *matricesExOverEz[m]; - TMatrixD& arrayEx = *matricesEx[m]; - - for (Int_t j = nZColumn - 1; j >= 0; j--) { - for (Int_t i = 0; i < nRRow; i++) { - - /// Calculate integration from int^{0}_{j} (TODO: Split the integration) - if (j < nZColumn - 3) { - eXoverEz(i, j) = eXoverEz(i, j + 2) + - (gridSizeZ / 3.0) * (arrayEx(i, j) + 4 * arrayEx(i, j + 1) + arrayEx(i, j + 2)) / - (-1 * ezField); - } else { - if (j == nZColumn - 3) { - eXoverEz(i, j) = (gridSizeZ / 3.0) * (arrayEx(i, nZColumn - 3) + 4 * arrayEx(i, nZColumn - 2) + arrayEx(i, nZColumn - 1)) / (-1 * ezField); - } - if (j == nZColumn - 2) { - eXoverEz(i, j) = - (gridSizeZ / 3.0) * (1.5 * arrayEx(i, nZColumn - 2) + 1.5 * arrayEx(i, nZColumn - 1)) / - (-1 * ezField); - } - if (j == nZColumn - 1) { - eXoverEz(i, j) = 0.0; - } - } - } - } - } -} -/// GetCorrection from no-drift -/// -/// \param x Float_t point origin -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylNoDrift(const Float_t x[], const Short_t roc, Float_t dx[]) -{ - /// Calculates the correction due the Space Charge effect within the TPC drift volume - - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::", "Lookup table was not initialized! Performing the initialization now ..."); - // InitSpaceCharge3DDistortion(); - return; - } - - Float_t intEr, intEPhi, intDEz; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi -= TMath::TwoPi(); - } - - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (sign == -1 && z < 0.0) { - printf("call C side\n"); - fLookupIntENoDriftC->GetValue(r, phi, z, intEr, intEPhi, intDEz); - } else { - fLookupIntENoDriftA->GetValue(r, phi, z, intEr, intEPhi, intDEz); - } - - // Calculate distorted position - if (r > 0.0) { - phi = phi + fCorrectionFactor * (fC0 * intEPhi - fC1 * intEr) / r; - r = r + fCorrectionFactor * (fC0 * intEr + fC1 * intEPhi); - } - Double_t dzDist = intDEz * fCorrectionFactor * AliTPCPoissonSolver::fgkdvdE; - - // Calculate correction in cartesian coordinates - dx[0] = -(r - x[0]); - dx[1] = -(phi - x[1]); - dx[2] = -dzDist; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetDistortionCylNoDrift(const Float_t x[], Short_t roc, Float_t dx[]) -{ - /// This function delivers the distortion values dx in respect to the initial coordinates x - /// roc represents the TPC read out chamber (offline numbering convention) - - GetCorrectionCylNoDrift(x, roc, dx); - for (Int_t j = 0; j < 3; ++j) { - dx[j] = -dx[j]; - } -} -/// inverse for no drift -/// inverse from global distortion to local distortion -/// -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param rList -/// \param zList -/// \param phiList -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseGlobalToLocalDistortionNoDrift( - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - Double_t* rList, Double_t* zList, Double_t* phiList, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - - Double_t z, phi, r, zAfter, zPrevious, ddR, ddRPhi, ddZ, drDist, dRPhi, dzDist; - Float_t x[3], dx[3], pdx[3], dxp1[3], dxp2[3]; - Int_t roc; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - } - } - - for (Int_t j = nZColumn - 2; j >= 0; j--) { - roc = 0; // FIXME - for (Int_t k = 0; k < phiSlice; k++) { - - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - // get global distortion - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - zPrevious = zList[j + 1]; - //zAfter = zList[j-1]; - - (*distDrDz)(i, j) = 0.0; - (*distDPhiRDz)(i, j) = 0.0; - (*distDz)(i, j) = 0.0; - drDist = 0.0; - dRPhi = 0.0; - dzDist = 0.0; - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - - x[0] = r; - x[1] = phi; - x[2] = z; - - GetDistortionCylNoDrift(x, roc, dx); - - //x[0] = x[0] + drDist; - //x[1] = x[1] + dRPhi/r; - x[2] = zPrevious; - - GetDistortionCylNoDrift(x, roc, pdx); - - (*distDrDz)(i, j) = (dx[0] - pdx[0]); - (*distDPhiRDz)(i, j) = (dx[1] - pdx[1]) * r; - (*distDz)(i, j) = (dx[2] - pdx[2]); - } - } - } -} -/// -/// \param matricesCharge -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param matricesInvLocalIntErDz -/// \param matricesInvLocalIntEPhiDz -/// \param matricesInvLocalEz -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseDistortionMapsNoDrift( - TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - // can inverse after lookup table for global distortion been calculated - Double_t* rList = new Double_t[nRRow]; - Double_t* zList = new Double_t[nZColumn]; - Double_t* phiList = new Double_t[phiSlice]; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = (j * gridSizeZ); - } - // memory allocation - if (fInitLookUp) { - // 1) get local distortion - InverseGlobalToLocalDistortionNoDrift(matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, zList, - phiList, nRRow, nZColumn, phiSlice); - // 2) calculate local integral - InverseLocalDistortionToElectricField(matricesEr, matricesEPhi, matricesEz, matricesInvLocalIntErDz, - matricesInvLocalIntEPhiDz, matricesInvLocalEz, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, zList, - phiList, nRRow, nZColumn, phiSlice); - // 3) get potential from electric field assuming zero boundaries - InverseElectricFieldToCharge(matricesCharge, matricesEr, matricesEPhi, matricesEz, rList, zList, phiList, nRRow, - nZColumn, phiSlice); - } - delete[] zList; - delete[] rList; - delete[] phiList; -} -/// -/// \param matricesChargeA -/// \param matricesChargeC -/// \param spaceChargeHistogram3D -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::GetChargeDensity( - TMatrixD** matricesChargeA, TMatrixD** matricesChargeC, const TH3* spaceChargeHistogram3D, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - // local variables - Float_t radius0, phi0, z0; - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - TMatrixD* mCharge; - for (Int_t side = 0; side < 2; side++) { - for (Int_t k = 0; k < phiSlice; k++) { - if (side == 0) { - mCharge = matricesChargeA[k]; - } else { - mCharge = matricesChargeC[k]; - } - - phi0 = phiList[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = rList[i]; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = zList[j]; - if (side == 1) { - z0 = -TMath::Abs(zList[j]); - } - if (spaceChargeHistogram3D != nullptr) { - (*mCharge)(i, j) = InterpolatePhi(spaceChargeHistogram3D, phi0, radius0, z0); - //InterpolatePhi(spaceChargeHistogram3D,phi0,radius0,z0); - } - } - } - } - } -} -/// -/// \param x -/// \param roc -/// \return -Double_t AliTPCSpaceCharge3DCalc::GetChargeCylAC(const Float_t x[], Short_t roc) const -{ - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - return fInterpolatorChargeA->GetValue(r, phi, z); - } else { - return fInterpolatorChargeC->GetValue(r, phi, -z); - } -} -/// -/// \param x -/// \param roc -/// \return -Double_t AliTPCSpaceCharge3DCalc::GetPotentialCylAC(const Float_t x[], Short_t roc) const -{ - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - return fInterpolatorPotentialA->GetValue(r, phi, z); - } else { - return fInterpolatorPotentialC->GetValue(r, phi, -z); - } -} -/// chargeInverse -/// -/// \param x -/// \param roc -/// \return -Double_t AliTPCSpaceCharge3DCalc::GetInverseChargeCylAC(const Float_t x[], Short_t roc) const -{ - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-6) { - return fInterpolatorInverseChargeA->GetValue(r, phi, z); - } else { - return fInterpolatorInverseChargeC->GetValue(r, phi, z); - } -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) -{ - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupDistA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupDistC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetLocalCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]) -{ - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupCorrA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupCorrC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// Get Electric field from look up table -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetElectricFieldCyl(const Float_t x[], Short_t roc, Double_t dx[]) -{ - Double_t eR, ePhi, eZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupElectricFieldA->GetValue(r, phi, z, eR, ePhi, eZ); - } else { - fLookupElectricFieldC->GetValue(r, phi, -z, eR, ePhi, eZ); - eZ = -1 * eZ; - } - - dx[0] = eR; - dx[1] = ePhi; - dx[2] = eZ; -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetInverseLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::", "Lookup table was not initialized! Performing the initialization now ..."); - InitSpaceCharge3DPoissonIntegralDz(129, 129, 144, 100, 1e-8); - } - - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupInverseDistA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupInverseDistC->GetValue(r, phi, -z, dR, dRPhi, dZ); - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -/// Function for setting Potential Boundary Values and Charge distribution input TFormula -/// -/// \param vTestFunction -/// \param rhoTestFunction -/// -void AliTPCSpaceCharge3DCalc::SetPotentialBoundaryAndChargeFormula(TFormula* vTestFunction, TFormula* rhoTestFunction) -{ - /**** allocate memory for charge ***/ - // we allocate pointer to TMatrixD array to picture 3D (slices), this representation should be revised - // since it is easier for GPU implementation to run for 1D memory - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - fFormulaPotentialV = vTestFunction; - fFormulaChargeRho = rhoTestFunction; - - // grid size for one side - TMatrixD* chargeA; - TMatrixD* chargeC; - Double_t radius0, z0, phi0, z0neg; - Int_t indexB = 0; - for (Int_t k = 0; k < fNPhiSlices; k++) { - chargeA = fMatrixChargeA[k]; - chargeC = fMatrixChargeC[k]; - - phi0 = k * gridSizePhi; - - /// Fill the non-boundary values - for (Int_t i = 0; i < fNRRows; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + (i * gridSizeR); - for (Int_t j = 0; j < fNZColumns; j++) { - z0 = j * gridSizeZ; - z0neg = -z0; - - (*chargeA)(i, j) = -1.0 * rhoTestFunction->Eval(radius0, phi0, z0); - (*chargeC)(i, j) = -1.0 * rhoTestFunction->Eval(radius0, phi0, z0neg); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // if ((i == 0) || (i == fNRRows - 1) || (j == 0) || (j == fNZColumns - 1)) { - // fListPotentialBoundaryA[indexB] = vTestFunction->Eval(radius0, phi0, z0); - // fListPotentialBoundaryC[indexB] = vTestFunction->Eval(radius0, phi0, z0neg); - // indexB++; - // } - - } // end j - } // end i - } // end phi - - fInterpolatorChargeA->SetValue(fMatrixChargeA); - fInterpolatorChargeA->InitCubicSpline(); - fInterpolatorChargeC->SetValue(fMatrixChargeC); - fInterpolatorChargeC->InitCubicSpline(); -} - -/// Set interpolation -void AliTPCSpaceCharge3DCalc::SetInterpolationOrder(Int_t order) -{ - fInterpolationOrder = order; - - fInterpolatorChargeA->SetOrder(fInterpolationOrder); - fInterpolatorChargeC->SetOrder(fInterpolationOrder); - fInterpolatorPotentialA->SetOrder(fInterpolationOrder); - fInterpolatorPotentialC->SetOrder(fInterpolationOrder); - fInterpolatorInverseChargeA->SetOrder(fInterpolationOrder); - fInterpolatorInverseChargeC->SetOrder(fInterpolationOrder); - - fLookupDistA->SetOrder(fInterpolationOrder); - - fLookupDistC->SetOrder(fInterpolationOrder); - - fLookupInverseDistA->SetOrder(fInterpolationOrder); - - fLookupInverseDistC->SetOrder(fInterpolationOrder); - - fLookupElectricFieldA->SetOrder(fInterpolationOrder); - fLookupElectricFieldC->SetOrder(fInterpolationOrder); - fLookupIntDistA->SetOrder(fInterpolationOrder); - fLookupIntDistC->SetOrder(fInterpolationOrder); - fLookupIntCorrA->SetOrder(fInterpolationOrder); - fLookupIntCorrC->SetOrder(fInterpolationOrder); - fLookupIntENoDriftA->SetOrder(fInterpolationOrder); - fLookupIntENoDriftC->SetOrder(fInterpolationOrder); - fLookupIntCorrIrregularA->SetOrder(fInterpolationOrder); - - fLookupIntCorrIrregularC->SetOrder(fInterpolationOrder); -} - -void AliTPCSpaceCharge3DCalc::SetDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC) -{ - fMatrixIntDistDrEzA = matrixIntDistDrA; - fMatrixIntDistDPhiREzA = matrixIntDistDrphiA; - fMatrixIntDistDzA = matrixIntDistDzA; - fLookupIntDistA->SetLookUpR(fMatrixIntDistDrEzA); - fLookupIntDistA->SetLookUpPhi(fMatrixIntDistDPhiREzA); - fLookupIntDistA->SetLookUpZ(fMatrixIntDistDzA); - fLookupIntDistA->CopyFromMatricesToInterpolator(); - - fMatrixIntDistDrEzC = matrixIntDistDrC; - fMatrixIntDistDPhiREzC = matrixIntDistDrphiC; - fMatrixIntDistDzC = matrixIntDistDzC; - fLookupIntDistC->SetLookUpR(fMatrixIntDistDrEzC); - fLookupIntDistC->SetLookUpPhi(fMatrixIntDistDPhiREzC); - fLookupIntDistC->SetLookUpZ(fMatrixIntDistDzC); - fLookupIntDistC->CopyFromMatricesToInterpolator(); - - fInitLookUp = kTRUE; -} \ No newline at end of file diff --git a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.h b/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.h deleted file mode 100644 index bf686deff2b0f..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.h +++ /dev/null @@ -1,454 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 AliTPCSpaceCharge3DCalc.h -/// \brief This class provides distortion and correction map calculation with integration following electron drift -/// TODO: validate distortion z by comparing with exisiting classes -/// -/// \author Rifki Sadikin <rifki.sadikin@cern.ch>, Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#ifndef ALI_TPC_SPACECHARGE3D_CALC_H -#define ALI_TPC_SPACECHARGE3D_CALC_H - -#include "TF1.h" -#include "TF2.h" -#include "TH3F.h" -#include "TMatrixD.h" - -#include "AliTPCPoissonSolver.h" -#include "AliTPCLookUpTable3DInterpolatorD.h" -#include "AliTPC3DCylindricalInterpolator.h" -#include "AliTPCLookUpTable3DInterpolatorIrregularD.h" -#include "AliTPC3DCylindricalInterpolatorIrregular.h" - -class TFormula; - -class AliTPCSpaceCharge3DCalc -{ - public: - AliTPCSpaceCharge3DCalc(); - AliTPCSpaceCharge3DCalc(Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice); - AliTPCSpaceCharge3DCalc(Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, - Int_t interpolationOrder, Int_t irregularGridSize, Int_t rbfKernelType); - ~AliTPCSpaceCharge3DCalc(); - void InitSpaceCharge3DPoissonIntegralDz(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Double_t stopConvergence); - - // outdated, to be removed after modifications in aliroot are pushed - void InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TMatrixD** matricesErA, TMatrixD** matricesEphiA, TMatrixD** matricesEzA, - TMatrixD** matricesErC, TMatrixD** matricesEphiC, TMatrixD** matricesEzC, - TMatrixD** matricesDistDrDzA, TMatrixD** matricesDistDPhiRDzA, TMatrixD** matricesDistDzA, - TMatrixD** matricesCorrDrDzA, TMatrixD** matricesCorrDPhiRDzA, TMatrixD** matricesCorrDzA, - TMatrixD** matricesDistDrDzC, TMatrixD** matricesDistDPhiRDzC, TMatrixD** matricesDistDzC, - TMatrixD** matricesCorrDrDzC, TMatrixD** matricesCorrDPhiRDzC, TMatrixD** matricesCorrDzC, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction); - - void InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction); - - void - InitSpaceCharge3DPoisson(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence); - void ForceInitSpaceCharge3DPoissonIntegralDz(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Double_t stopConvergence); - void GetDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetDistortionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const; - void GetDistortion(const Float_t x[], Short_t roc, Float_t dx[]) const; - - void GetCorrection(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrection(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const; - - Double_t GetChargeCylAC(const Float_t x[], Short_t roc) const; - Double_t GetPotentialCylAC(const Float_t x[], Short_t roc) const; - - Double_t GetInverseChargeCylAC(const Float_t x[], Short_t roc) const; - - void SetCorrectionType(Int_t correctionType) { fCorrectionType = correctionType; } - - enum { - kNumSector = 18 - }; - - enum CorrectionType { - kRegularInterpolator = 0, ///< use interpolation with regular interpolator for correction look up table - kIrregularInterpolator = 1, ///< use irregular interpolator for correction look up table - }; - - enum IntegrationStrategy { - kNaive = 0, ///< use interpolation with regular interpolator for correction look up table - kOpt = 1, ///< use irregular interpolator for correction look up table - }; - void SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm); - void SetInputSpaceCharge(TH3* hisSpaceCharge3D) { SetInputSpaceCharge(hisSpaceCharge3D, 1); } - void SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm, Int_t side); - void SetInputSpaceCharge(TH3* hisSpaceCharge3D, Int_t side) { SetInputSpaceCharge(hisSpaceCharge3D, 1, side); } - - void SetInputSpaceChargeA(TMatrixD** matricesLookUpCharge) - { - fInterpolatorChargeA->SetValue(matricesLookUpCharge); - fInterpolatorChargeA->InitCubicSpline(); - } - - void SetInputSpaceChargeC(TMatrixD** matricesLookUpCharge) - { - fInterpolatorChargeC->SetValue(matricesLookUpCharge); - fInterpolatorChargeC->InitCubicSpline(); - } - - void SetNRRows(Int_t nRRow) { fNRRows = nRRow; } - - void SetNPhiSlices(Int_t nPhiSlice) { fNPhiSlices = nPhiSlice; } - - void SetNZColumns(Int_t nZColumn) { fNZColumns = nZColumn; } - - Int_t GetNRRows() { return fNRRows; } - - Int_t GetNPhiSlices() { return fNPhiSlices; } - - Int_t GetNZColumns() { return fNZColumns; } - - void SetPoissonSolver(AliTPCPoissonSolver* poissonSolver) - { - if (fPoissonSolver != nullptr) { - delete fPoissonSolver; - } - fPoissonSolver = poissonSolver; - } - - AliTPCPoissonSolver* GetPoissonSolver() { return fPoissonSolver; } - - void SetInterpolationOrder(Int_t order); - - Int_t GetInterpolationOrder() { return fInterpolationOrder; } - - void SetOmegaTauT1T2(Float_t omegaTau, Float_t t1, Float_t t2) - { - const Double_t wt0 = t2 * omegaTau; - fC0 = 1. / (1. + wt0 * wt0); - const Double_t wt1 = t1 * omegaTau; - fC1 = wt1 / (1. + wt1 * wt1); - }; - - void SetC0C1(Float_t c0, Float_t c1) - { - fC0 = c0; - fC1 = c1; - } - - Float_t GetC0() const { return fC0; } - - Float_t GetC1() const { return fC1; } - - void SetCorrectionFactor(Float_t correctionFactor) { fCorrectionFactor = correctionFactor; } - - Float_t GetCorrectionFactor() const { return fCorrectionFactor; } - - void InverseDistortionMaps(TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, TMatrixD** matricesInvLocalIntErDz, - TMatrixD**, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t nStep, - const Bool_t useCylAC, Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t interpType); - - void InverseDistortionMapsNoDrift(TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, TMatrixD** matricesInvLocalIntErDz, - TMatrixD** matricesInvLocalIntEPhiDz, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice); - - void GetCorrectionCylNoDrift(const Float_t x[], const Short_t roc, Float_t dx[]); - - void GetDistortionCylNoDrift(const Float_t x[], Short_t roc, Float_t dx[]); - - void InverseGlobalToLocalDistortionNoDrift(TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, Double_t* rList, Double_t* zList, - Double_t* phiList, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice); - - void GetChargeDensity(TMatrixD** matricesChargeA, TMatrixD** matricesChargeC, const TH3* spaceChargeHistogram3D, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice); - - void GetInverseLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]); - - void GetLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]); - - void GetLocalCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]); - - void SetIrregularGridSize(Int_t size) { fIrregularGridSize = size; } - - Int_t GetIrregularGridSize() { return fIrregularGridSize; } - - Int_t GetRBFKernelType() { return fRBFKernelType; } - - void SetPotentialBoundaryAndChargeFormula(TFormula* vTestFunction, TFormula* rhoTestFunction); - TFormula* GetPotentialVFormula() const { return fFormulaPotentialV; } - TFormula* GetChargeRhoFormula() const { return fFormulaChargeRho; } - - void SetBoundaryIFCA(TF1* f1) { fFormulaBoundaryIFCA = f1; } - - void SetBoundaryIFCC(TF1* f1) { fFormulaBoundaryIFCC = f1; } - - void SetBoundaryOFCA(TF1* f1) { fFormulaBoundaryOFCA = f1; } - - void SetBoundaryOFCC(TF1* f1) { fFormulaBoundaryOFCC = f1; } - - void SetBoundaryROCA(TF1* f1) { fFormulaBoundaryROCA = f1; } - - void SetBoundaryROCC(TF1* f1) { fFormulaBoundaryROCC = f1; } - - void SetBoundaryCE(TF1* f1) { fFormulaBoundaryCE = f1; } - - void SetElectricFieldFormula(TFormula* formulaEr, TFormula* formulaEPhi, TFormula* formulaEz) - { - fFormulaEr = formulaEr; - fFormulaEPhi = formulaEPhi; - fFormulaEz = formulaEz; - } - TFormula* GetErFormula() const { return fFormulaEr; } - TFormula* GetEPhiFormula() const { return fFormulaEPhi; } - TFormula* GetEzFormula() const { return fFormulaEz; } - - Float_t GetSpaceChargeDensity(Float_t r, Float_t phi, Float_t z); - Float_t GetPotential(Float_t r, Float_t phi, Float_t z); - void GetElectricFieldCyl(const Float_t x[], Short_t roc, Double_t dx[]); - struct Profile { - Double_t poissonSolverTime; - Double_t electricFieldTime; - Double_t localDistortionTime; - Double_t globalDistortionTime; - Double_t interpolationInitTime; - Int_t iteration; - }; - - Profile GetProfile() { return myProfile; } - void SetIntegrationStrategy(Int_t integrationStrategy) { fIntegrationStrategy = integrationStrategy; } - - void SetDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC); - - private: - Profile myProfile; //!<! - Int_t fNRRows = 129; ///< the maximum on row-slices so far ~ 2cm slicing - Int_t fNPhiSlices = 180; ///< the maximum of phi-slices so far = (8 per sector) - Int_t fNZColumns = 129; ///< the maximum on column-slices so ~ 2cm slicing - Float_t fC0 = 0.f; ///< coefficient C0 (compare Jim Thomas's notes for definitions) - Float_t fC1 = 0.f; ///< coefficient C1 (compare Jim Thomas's notes for definitions) - Float_t fCorrectionFactor = 1.f; ///< Space Charge Correction factor in comparison to initialized - - Bool_t fInitLookUp = kFALSE; ///< flag to check if the Look Up table was created - Double_t* fListR; //[fNRRows] list of r-coordinate of grids - Double_t* fListPhi; //[fNPhiSlices] list of \f$ \phi\f$ -coordinate of grids - Double_t* fListZ; //[fNZColumns] - Double_t* fListZA; //[fNZColumns] list of z-coordinate of grids - Double_t* fListZC; //[fNZColumns] list of z-coordinate of grids - // / TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* fListPotentialBoundaryA; //[(fNRRows + fNZColumns) * 2 * fNPhiSlices] - // Double_t* fListPotentialBoundaryC; //[(fNRRows + fNZColumns) * 2 * fNPhiSlices] - - Int_t fCorrectionType = 1; ///< use regular or irregular grid method - Int_t fInterpolationOrder = 5; ///< Order of interpolation (1-> tri linear, 2->Lagrange interpolation order 2, 3> cubic spline) - Int_t fIrregularGridSize = 3; ///< Size of irregular grid cubes for interpolation (min 3) - Int_t fRBFKernelType = 0; ///< RBF kernel type - Int_t fIntegrationStrategy = 0; ///< Strategy for integration - - TMatrixD** fMatrixIntDistDrEzA; //!<! Matrices for storing Global distortion \f$ R \f$ direction for Side A - TMatrixD** fMatrixIntDistDPhiREzA; //!<! Matrices for storing Global \f$ \phi R \f$ Distortion for Side A - TMatrixD** fMatrixIntDistDzA; //!<! Matrices for storing Global \f$ z \f$ Distortion for Side A - - TMatrixD** fMatrixIntDistDrEzC; //!<! Matrices for storing Global \f$ R \f$ direction for Side C - TMatrixD** fMatrixIntDistDPhiREzC; //!<! Matrices for storing Global \f$ \phi R \f$ Distortion for Side C - TMatrixD** fMatrixIntDistDzC; //!<! Matrices for storing Global \f$ z \f$ Distortion for Side C - - TMatrixD** fMatrixErOverEzA; //!<! Matrices for storing Er Over Ez for intermediate value for side A - TMatrixD** fMatrixEPhiOverEzA; //!<! Matrices for storing EPhi Over Ez for intermediate value for side A - TMatrixD** fMatrixDeltaEzA; //!<! Matrices for storing delta Ez for intermediate value for side A - - TMatrixD** fMatrixErOverEzC; //!<! Matrices for storing Er Over Ez for intermediate value for Side C - TMatrixD** fMatrixEPhiOverEzC; //!<! Matrices for storing EPhi Over Ez for intermediate value for Side C - TMatrixD** fMatrixDeltaEzC; //!<! Matrices for storing delta Ez for intermediate value for side A - - TMatrixD** fMatrixIntCorrDrEzA; //!<! Matrices for storing Global \f$ R \f$ correction for side A - TMatrixD** fMatrixIntCorrDPhiREzA; //!<! Matrices for storing Global \f$ \phi R \f$ correction for side A - TMatrixD** fMatrixIntCorrDzA; //!<! Matrices for storing Global \f$ X \f$ correction for side A - - TMatrixD** fMatrixIntCorrDrEzC; //!<! Matrices for storing Global \f$ R \f$ correction for side C - TMatrixD** fMatrixIntCorrDPhiREzC; //!<! Matrices for storing Global \f$ \phi R \f$ correction for side C - TMatrixD** fMatrixIntCorrDzC; //!<! Matrices for storing Global \f$ X \f$ correction for side C - - TMatrixD** fMatrixIntCorrDrEzIrregularA; //!<! Matrices for storing global \f$ R \f$ correction irregular type for side A - TMatrixD** fMatrixIntCorrDPhiREzIrregularA; //!<! Matrices for storing Global \f$ \phi R \f$ correction irregular type for side A - TMatrixD** fMatrixIntCorrDzIrregularA; //!<! Matrices for storing Global \f$ z \f$ correction irregular type for side A - - TMatrixD** fMatrixRListIrregularA; //!<! Matrices for storing distorted \f$ R \f$ side A - TMatrixD** fMatrixPhiListIrregularA; //!<! Matrices for storing distorted \f$ \phi \f$ side A - TMatrixD** fMatrixZListIrregularA; //!<! Matrices for storing distorted \f$ z \f$ side A - - TMatrixD** fMatrixIntCorrDrEzIrregularC; //!<! Matrices for storing Global \f$ R \f$ correction irregular type for side C - TMatrixD** fMatrixIntCorrDPhiREzIrregularC; //!<! Matrices for storing Global \f$ \phi R \f$ correction irregular type for side C - TMatrixD** fMatrixIntCorrDzIrregularC; //!<! Matrices for storing Global \f$ z \f$ correction irregular type for side C - - TMatrixD** fMatrixRListIrregularC; //!<! Matrices for storing distorted \f$ R \f$ side C - TMatrixD** fMatrixPhiListIrregularC; //!<! Matrices for storing distorted \f$ \phi \f$ side C - TMatrixD** fMatrixZListIrregularC; //!<! Matrices for storing distorted \f$ z \f$ side C - - // look up for charge densities - TMatrixD** fMatrixChargeA; //!<! Matrices for storing input charge densities side A - TMatrixD** fMatrixChargeC; //!<! Matrices for storing input charge densities side C - TMatrixD** fMatrixChargeInverseA; //!<! Matrices for storing charge densities from backward algorithm side A - TMatrixD** fMatrixChargeInverseC; //!<! Matrices for storing charge densities from backward algorithm side C - - TMatrixD** fMatrixPotentialA; //!<! Matrices for storing potential side A - TMatrixD** fMatrixPotentialC; //!<! Matrices for storing potential side C - - AliTPC3DCylindricalInterpolator* fInterpolatorChargeA = nullptr; //!<! interpolator for charge densities side A - AliTPC3DCylindricalInterpolator* fInterpolatorChargeC = nullptr; //!<! interpolator for charge densities side C - AliTPC3DCylindricalInterpolator* fInterpolatorPotentialA = nullptr; //!<! interpolator for charge densities side A - AliTPC3DCylindricalInterpolator* fInterpolatorPotentialC = nullptr; //!<! interpolator for charge densities side C - AliTPC3DCylindricalInterpolator* fInterpolatorInverseChargeA = nullptr; //!<! interpolator for inverse charge densities side A - AliTPC3DCylindricalInterpolator* fInterpolatorInverseChargeC = nullptr; //!<! interpolator for inverse charge densities side C - - AliTPCLookUpTable3DInterpolatorD* fLookupIntDistA = nullptr; //-> look-up table for global distortion side A - AliTPCLookUpTable3DInterpolatorD* fLookupIntCorrA = nullptr; //-> look-up table for global correction side A - AliTPCLookUpTable3DInterpolatorD* fLookupIntDistC = nullptr; //-> look-up table for global distortion side C - AliTPCLookUpTable3DInterpolatorD* fLookupIntCorrC = nullptr; //-> look-up table for global correction side C - AliTPCLookUpTable3DInterpolatorIrregularD* fLookupIntCorrIrregularA = nullptr; //!<! look-up table for global correction side A (method irregular) - AliTPCLookUpTable3DInterpolatorIrregularD* fLookupIntCorrIrregularC = nullptr; //!<! look-up table for global correction side C (method irregular) - AliTPCLookUpTable3DInterpolatorD* fLookupIntENoDriftA = nullptr; //!<! look-up table for no drift integration side A - AliTPCLookUpTable3DInterpolatorD* fLookupIntENoDriftC = nullptr; //!<! look-up table for no drift integration side C - AliTPCLookUpTable3DInterpolatorD* fLookupDistA = nullptr; //!<! look-up table for local distortion side A - AliTPCLookUpTable3DInterpolatorD* fLookupDistC = nullptr; //!<! look-up table for local distortion side C - AliTPCLookUpTable3DInterpolatorD* fLookupCorrA = nullptr; //!<! look-up table for local correction side A - AliTPCLookUpTable3DInterpolatorD* fLookupCorrC = nullptr; //!<! look-up table for local correction side C - AliTPCLookUpTable3DInterpolatorD* fLookupInverseDistA = nullptr; //!<! look-up table for local distortion (from inverse) side A - AliTPCLookUpTable3DInterpolatorD* fLookupInverseDistC = nullptr; //!<! look-up table for local distortion (from inverse) side C - - AliTPCLookUpTable3DInterpolatorD* fLookupElectricFieldA = nullptr; //!<! look-up table for electric field side A - AliTPCLookUpTable3DInterpolatorD* fLookupElectricFieldC = nullptr; //!<! look-up table for electric field side C - - TH3* fHistogram3DSpaceCharge = nullptr; //!<! Histogram with the input space charge histogram - used as an optional input - TH3* fHistogram3DSpaceChargeA = nullptr; //!<! Histogram with the input space charge histogram - used as an optional input side A - TH3* fHistogram3DSpaceChargeC = nullptr; //!<! Histogram with the input space charge histogram - used as an optional input side C - TF1* fFormulaBoundaryIFCA = nullptr; //!<! function define boundary values for IFC side A V(z) assuming symmetry in phi and r. - TF1* fFormulaBoundaryIFCC = nullptr; //!<! function define boundary values for IFC side C V(z) assuming symmetry in phi and r. - TF1* fFormulaBoundaryOFCA = nullptr; //!<! function define boundary values for OFC side A V(z) assuming symmetry in phi and r. - TF1* fFormulaBoundaryOFCC = nullptr; //!<! function define boundary values for IFC side C V(z) assuming symmetry in phi and r. - TF1* fFormulaBoundaryROCA = nullptr; //!<! function define boundary values for ROC side A V(r) assuming symmetry in phi and z. - TF1* fFormulaBoundaryROCC = nullptr; //!<! function define boundary values for ROC side V V(t) assuming symmetry in phi and z. - TF1* fFormulaBoundaryCE = nullptr; //!<! function define boundary values for CE V(z) assuming symmetry in phi and z. - - TFormula* fFormulaPotentialV = nullptr; //!<! potential V(r,rho,z) function - TFormula* fFormulaChargeRho = nullptr; //!<! charge density Rho(r,rho,z) function - - // analytic formula for E - TFormula* fFormulaEPhi = nullptr; //!<! ePhi EPhi(r,rho,z) electric field (phi) function - TFormula* fFormulaEr = nullptr; //!<! er Er(r,rho,z) electric field (r) function - TFormula* fFormulaEz = nullptr; //!<! ez Ez(r,rho,z) electric field (z) function - - AliTPCPoissonSolver* fPoissonSolver = nullptr; //!<! Pointer to a poisson solver - - void ElectricField(TMatrixD** matricesV, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlices, const Float_t gridSizeR, - const Float_t gridSizePhi, const Float_t gridSizeZ, const Int_t symmetry, - const Float_t innerRadius); - - void - LocalDistCorrDz(TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, TMatrixD** matricesDistDrDz, - TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, TMatrixD** matricesCorrDrDz, - TMatrixD** matricesCorrDPhiRDz, TMatrixD** matricesCorrDz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Float_t gridSizeZ, const Double_t ezField); - void - LocalDistCorrDz(TFormula* intErDzFunction, TFormula* intEPhiRDzFunction, TFormula* intDzFunction, TFormula* ezFunction, - Double_t* rList, Double_t* phiList, Double_t* zList, TMatrixD** matricesDistDrDz, - TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, TMatrixD** matricesCorrDrDz, - TMatrixD** matricesCorrDPhiRDz, TMatrixD** matricesCorrDz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Float_t gridSizeZ, const Double_t ezField); - - void IntegrateDistCorrDriftLineDz(AliTPCLookUpTable3DInterpolatorD* lookupLocalDist, TMatrixD** matricesGDistDrDz, - TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr, TMatrixD** matricesGCorrDrDz, - TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, - TMatrixD** matricesGCorrIrregularDz, - TMatrixD** matricesRIrregular, TMatrixD** matricesPhiIrregular, - TMatrixD** matricesZIrregular, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Double_t* rList, const Double_t* phiList, const Double_t* zList); - - // outdated, to be removed once modifications in aliroot are pushed - void IntegrateDistCorrDriftLineDz( - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction, - const Double_t ezField, TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, - TMatrixD** matricesGDistDz, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, - TMatrixD** matricesGCorrIrregularDz, TMatrixD** matricesRIrregular, TMatrixD** matricesPhiIrregular, - TMatrixD** matricesZIrregular, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Double_t* rList, - const Double_t* phiList, const Double_t* zList); - - void IntegrateDistCorrDriftLineDz( - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction, TFormula* ezF, - const Double_t ezField, TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, - TMatrixD** matricesGDistDz, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, - TMatrixD** matricesGCorrIrregularDz, TMatrixD** matricesRIrregular, TMatrixD** matricesPhiIrregular, - TMatrixD** matricesZIrregular, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Double_t* rList, - const Double_t* phiList, const Double_t* zList); - - void IntegrateDistCorrDriftLineDzWithLookUp(AliTPCLookUpTable3DInterpolatorD* lookupLocalDist, TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr, TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, Double_t* rList, Double_t* phiList, Double_t* zList); - - void FillLookUpTable(AliTPCLookUpTable3DInterpolatorD* lookupGlobal, TMatrixD** lookupRDz, TMatrixD** lookupPhiRDz, - TMatrixD** lookupDz, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Double_t* rList, const Double_t* phiList, const Double_t* zList); - - Double_t InterpolatePhi(const TH3* h3, const Double_t phi, const Double_t r, const Double_t z); - - void InverseGlobalToLocalDistortionGlobalInvTable(TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, Double_t* rList, Double_t* zList, - Double_t* phiList, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Int_t nStep, const Bool_t useCylAC, - Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t type); - - void InverseLocalDistortionToElectricField(TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, - TMatrixD** matricesInvLocalIntEz, TMatrixD** matricesDistDrDz, - TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - Double_t* rList, Double_t* zList, Double_t* phiList, const Int_t nRRow, - const Int_t nZColumn, const Int_t phiSlice); - - void InverseElectricFieldToCharge(TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, Double_t* rList, Double_t* zList, Double_t* phiList, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice); - - void CalculateEField(TMatrixD** matricesV, TMatrixD** matricesErOverEz, TMatrixD** matricesEPhiOverEz, - TMatrixD** matricesDeltaEz, const Int_t nRRow, const Int_t nZColumn, const Int_t nPhiSlice, - const Int_t symmetry, Bool_t rocDisplacement = kFALSE); - - void - IntegrateEz(TMatrixD** matricesExOverEz, TMatrixD** matricesEx, const Int_t nRRow, const Int_t nZColumn, - const Int_t nPhiSlice, const Double_t ezField); - - void InitAllocateMemory(); - - /// \cond CLASSIMP - ClassDefNV(AliTPCSpaceCharge3DCalc, 1); - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/CMakeLists.txt b/GPU/TPCSpaceChargeBase/CMakeLists.txt deleted file mode 100644 index 3743bb73ff6d4..0000000000000 --- a/GPU/TPCSpaceChargeBase/CMakeLists.txt +++ /dev/null @@ -1,78 +0,0 @@ -# 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". -# -# See http://alice-o2.web.cern.ch/license for full licensing information. -# -# In applying this license CERN does 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(MODULE TPCSpaceChargeBase) - -set(SRCS - AliTPC3DCylindricalInterpolator.cxx - AliTPC3DCylindricalInterpolatorIrregular.cxx - AliTPCLookUpTable3DInterpolatorD.cxx - AliTPCLookUpTable3DInterpolatorIrregularD.cxx - AliTPCPoissonSolver.cxx - AliTPCSpaceCharge3DCalc.cxx) -string(REPLACE ".cxx" ".h" HDRS_CINT "${SRCS}") - -if(${ALIGPU_BUILD_TYPE} STREQUAL "O2") - o2_add_library(TPCSpaceChargeBase - TARGETVARNAME targetName - PUBLIC_LINK_LIBRARIES ROOT::Matrix ROOT::Hist - PUBLIC_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR} - SOURCES ${SRCS}) - - o2_target_root_dictionary(TPCSpaceChargeBase - HEADERS ${HDRS_CINT} - LINKDEF TPCSpaceChargeBaseLinkDef.h) - - o2_add_test(SpaceChargeBase - SOURCES ctest/testTPCSpaceChargeBase.cxx - COMPONENT_NAME gpu - LABELS gpu - PUBLIC_LINK_LIBRARIES O2::TPCSpaceChargeBase) - - target_compile_definitions(${targetName} PRIVATE GPUCA_O2_LIB) - - install(FILES ${HDRS_CINT} DESTINATION include/GPU) -endif() - -if(${ALIGPU_BUILD_TYPE} STREQUAL "ALIROOT") - add_definitions(-DGPUCA_ALIROOT_LIB) - include_directories(SYSTEM ${ROOT_INCLUDE_DIR}) - include_directories(${AliRoot_SOURCE_DIR}/GPU/TPCSpaceChargeBase) - - # Generate the dictionary - get_directory_property(incdirs INCLUDE_DIRECTORIES) - generate_dictionary("Ali${MODULE}" "TPCSpaceChargeBaseLinkDef.h" - "${HDRS_CINT}" "${incdirs}") - - set(ROOT_DEPENDENCIES Core Hist MathCore Matrix Physics) - - # Generate the ROOT map Dependecies - set(LIBDEPS ${ROOT_DEPENDENCIES}) - generate_rootmap("Ali${MODULE}" "${LIBDEPS}" - "${CMAKE_CURRENT_SOURCE_DIR}/TPCSpaceChargeBaseLinkDef.h") - - # Add a library to the project using the specified source files - add_library_tested(Ali${MODULE} SHARED ${SRCS} G__Ali${MODULE}.cxx) - target_link_libraries(Ali${MODULE} ${LIBDEPS}) - - # Additional compilation flags - set_target_properties(Ali${MODULE} PROPERTIES COMPILE_FLAGS "") - - # System dependent: Modify the way the library is build - if(${CMAKE_SYSTEM} MATCHES Darwin) - set_target_properties(Ali${MODULE} - PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") - endif(${CMAKE_SYSTEM} MATCHES Darwin) - - # Installation - install(TARGETS Ali${MODULE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) - - install(FILES ${HDRS_CINT} DESTINATION include) -endif() diff --git a/GPU/TPCSpaceChargeBase/TPCSpaceChargeBaseLinkDef.h b/GPU/TPCSpaceChargeBase/TPCSpaceChargeBaseLinkDef.h deleted file mode 100644 index 1df5061c43b3b..0000000000000 --- a/GPU/TPCSpaceChargeBase/TPCSpaceChargeBaseLinkDef.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TPCSpaceChargeBaseLinkDef.h -/// \author Ernst Hellbaer - -#if defined(__CINT__) || defined(__CLING__) - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class AliTPC3DCylindricalInterpolator + ; -#pragma link C++ class AliTPC3DCylindricalInterpolatorIrregular + ; -#pragma link C++ struct AliTPC3DCylindricalInterpolatorIrregular::KDTreeNode + ; -#pragma link C++ class AliTPCLookUpTable3DInterpolatorD + ; -#pragma link C++ class AliTPCLookUpTable3DInterpolatorIrregularD + ; -#pragma link C++ class AliTPCPoissonSolver + ; -#pragma link C++ struct AliTPCPoissonSolver::MGParameters + ; -#pragma link C++ class AliTPCSpaceCharge3DCalc + ; - -#endif diff --git a/GPU/TPCSpaceChargeBase/ctest/testTPCSpaceChargeBase.cxx b/GPU/TPCSpaceChargeBase/ctest/testTPCSpaceChargeBase.cxx deleted file mode 100644 index 8df9e76bfb543..0000000000000 --- a/GPU/TPCSpaceChargeBase/ctest/testTPCSpaceChargeBase.cxx +++ /dev/null @@ -1,26 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 testTPCSpaceChargeBase.cxx -/// \author Ernst Hellbaer - -#define BOOST_TEST_MODULE Test TPC Space - Charge Base Class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include <boost/test/unit_test.hpp> -#include "AliTPCSpaceCharge3DCalc.h" - -/// @brief Basic test if we can create the method class -BOOST_AUTO_TEST_CASE(TPCSpaceChargeBase_test1) -{ - auto spacecharge = new AliTPCSpaceCharge3DCalc; - delete spacecharge; -} diff --git a/GPU/Utils/FlatObject.h b/GPU/Utils/FlatObject.h index 9299f1f0da1b8..8077fd18b6912 100644 --- a/GPU/Utils/FlatObject.h +++ b/GPU/Utils/FlatObject.h @@ -16,8 +16,6 @@ #ifndef ALICEOW_GPUCOMMON_TPCFASTTRANSFORMATION_FLATOBJECT_H #define ALICEOW_GPUCOMMON_TPCFASTTRANSFORMATION_FLATOBJECT_H -#undef NDEBUG - #if !defined(GPUCA_GPUCODE_DEVICE) #include <cstddef> #include <memory> @@ -318,7 +316,9 @@ class FlatObject char* mFlatBufferContainer = nullptr; //[mFlatBufferSize] Optional container for the flat buffer char* mFlatBufferPtr = nullptr; //! Pointer to the flat buffer +#ifndef GPUCA_ALIROOT_LIB ClassDefNV(FlatObject, 1); +#endif }; /// ======================================================================================================== @@ -386,30 +386,7 @@ inline void FlatObject::cloneFromObject(const FlatObject& obj, char* newFlatBuff std::memcpy(mFlatBufferPtr, obj.mFlatBufferPtr, obj.mFlatBufferSize); mConstructionMask = (unsigned int)ConstructionState::Constructed; } -#endif - -inline char* FlatObject::releaseInternalBuffer() -{ - // returns an pointer to the internal buffer. Makes the internal container variable empty. - char* contPtr = mFlatBufferContainer; - mFlatBufferContainer = nullptr; - return contPtr; -} - -inline void FlatObject::adoptInternalBuffer(char* buf) -{ - // buf becomes the new internal buffer, after it was already set as new setActualBufferAddress - assert((mFlatBufferPtr == buf)); - mFlatBufferContainer = buf; -} - -inline void FlatObject::clearInternalBufferPtr() -{ - // we just release the internal buffer ressetting it to nullptr - mFlatBufferContainer = nullptr; -} -#ifndef GPUCA_GPUCODE // code invisible on GPU inline void FlatObject::moveBufferTo(char* newFlatBufferPtr) { /// sets buffer pointer to the new address, move the buffer content there. @@ -423,33 +400,75 @@ inline void FlatObject::moveBufferTo(char* newFlatBufferPtr) mFlatBufferContainer = mFlatBufferPtr; } } -#endif -inline void FlatObject::setActualBufferAddress(char* actualFlatBufferPtr) +template <class T> +inline std::string FlatObject::stressTest(T& obj) { - /// Sets the actual location of the external flat buffer after it has been moved (i.e. to another maschine) + /// Test the flat object functionality for an object of a child class T + /// the obj is modified here. Check if it is functional after the test. /// - /// It sets mFlatBufferPtr to actualFlatBufferPtr. - /// A daughter class should update all the pointers inside the buffer in the new location. + std::string err; - mFlatBufferPtr = actualFlatBufferPtr; + if (!obj.isConstructed()) { + return "tested object is not constructed!"; + } + + T tst; + tst.cloneFromObject(obj, nullptr); + if (!tst.isConstructed() || !tst.isBufferInternal()) { + return "error at cloneFromObject()!"; + } + + obj.destroy(); + + char* buf0 = tst.releaseInternalBuffer(); + char* buf1 = new char[tst.getFlatBufferSize()]; + char* buf2 = new char[tst.getFlatBufferSize()]; + std::memcpy(buf1, tst.getFlatBufferPtr(), tst.getFlatBufferSize()); + tst.setActualBufferAddress(buf1); + delete[] buf0; + + tst.setFutureBufferAddress(buf2); + std::memcpy(buf2, buf1, tst.getFlatBufferSize()); + delete[] buf1; + + if (tst.isBufferInternal()) { + return err = "error, buffer should be external!"; + } + + tst.adoptInternalBuffer(buf2); + if (!tst.isBufferInternal()) { + return err = "error, buffer should be internal!"; + } + + obj.cloneFromObject(tst, nullptr); + if (!obj.isBufferInternal()) { + return err = "error, buffer should be internal!"; + } + + return err; } -inline void FlatObject::setFutureBufferAddress(char* futureFlatBufferPtr) +inline void FlatObject::printC() const { - /// Sets a future location of the external flat buffer before moving it to this location. - /// - /// A daughter class should already reset all the pointers inside the current buffer to the future location - /// without touching memory in the future location. - - assert(!isBufferInternal()); - mFlatBufferPtr = futureFlatBufferPtr; -#ifndef GPUCA_GPUCODE // code invisible on GPU - delete[] mFlatBufferContainer; // for a case.. -#endif // !GPUCA_GPUCODE - mFlatBufferContainer = nullptr; + /// Print the content of the flat buffer + bool lfdone = false; + for (int i = 0; i < mFlatBufferSize; i++) { + unsigned char v = mFlatBufferPtr[i]; + lfdone = false; + printf("0x%02x ", v); + if (i && ((i + 1) % 20) == 0) { + printf("\n"); + lfdone = true; + } + } + if (!lfdone) { + printf("\n"); + } } +#endif // GPUCA_GPUCODE + #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU template <class T, class TFile> inline int FlatObject::writeToFile(T& obj, TFile& outf, const char* name) @@ -494,72 +513,57 @@ inline T* FlatObject::readFromFile(TFile& inpf, const char* name) pobj->setActualBufferAddress(pobj->mFlatBufferContainer); return pobj; } -#endif +#endif // GPUCA_GPUCODE || GPUCA_STANDALONE -#if !defined(GPUCA_GPUCODE) // code invisible on GPU +#ifndef GPUCA_GPUCODE_DEVICE -template <class T> -inline std::string FlatObject::stressTest(T& obj) +inline char* FlatObject::releaseInternalBuffer() { - /// Test the flat object functionality for an object of a child class T - /// the obj is modified here. Check if it is functional after the test. - /// - std::string err; - - if (!obj.isConstructed()) - return "tested object is not constructed!"; - - T tst; - tst.cloneFromObject(obj, nullptr); - if (!tst.isConstructed() || !tst.isBufferInternal()) - return "error at cloneFromObject()!"; - - obj.destroy(); - - char* buf0 = tst.releaseInternalBuffer(); - char* buf1 = new char[tst.getFlatBufferSize()]; - char* buf2 = new char[tst.getFlatBufferSize()]; - std::memcpy(buf1, tst.getFlatBufferPtr(), tst.getFlatBufferSize()); - tst.setActualBufferAddress(buf1); - delete[] buf0; - - tst.setFutureBufferAddress(buf2); - std::memcpy(buf2, buf1, tst.getFlatBufferSize()); - delete[] buf1; + // returns an pointer to the internal buffer. Makes the internal container variable empty. + char* contPtr = mFlatBufferContainer; + mFlatBufferContainer = nullptr; + return contPtr; +} - if (tst.isBufferInternal()) - return err = "error, buffer should be external!"; +inline void FlatObject::adoptInternalBuffer(char* buf) +{ + // buf becomes the new internal buffer, after it was already set as new setActualBufferAddress + assert((mFlatBufferPtr == buf)); + mFlatBufferContainer = buf; +} - tst.adoptInternalBuffer(buf2); - if (!tst.isBufferInternal()) - return err = "error, buffer should be internal!"; +inline void FlatObject::clearInternalBufferPtr() +{ + // we just release the internal buffer ressetting it to nullptr + mFlatBufferContainer = nullptr; +} - obj.cloneFromObject(tst, nullptr); - if (!obj.isBufferInternal()) - return err = "error, buffer should be internal!"; +inline void FlatObject::setActualBufferAddress(char* actualFlatBufferPtr) +{ + /// Sets the actual location of the external flat buffer after it has been moved (i.e. to another maschine) + /// + /// It sets mFlatBufferPtr to actualFlatBufferPtr. + /// A daughter class should update all the pointers inside the buffer in the new location. - return err; + mFlatBufferPtr = actualFlatBufferPtr; } -inline void FlatObject::printC() const +inline void FlatObject::setFutureBufferAddress(char* futureFlatBufferPtr) { - /// Print the content of the flat buffer - bool lfdone = false; - for (int i = 0; i < mFlatBufferSize; i++) { - unsigned char v = mFlatBufferPtr[i]; - lfdone = false; - printf("0x%02x ", v); - if (i && ((i + 1) % 20) == 0) { - printf("\n"); - lfdone = true; - } - } - if (!lfdone) { - printf("\n"); - } + /// Sets a future location of the external flat buffer before moving it to this location. + /// + /// A daughter class should already reset all the pointers inside the current buffer to the future location + /// without touching memory in the future location. + + assert(!isBufferInternal()); + mFlatBufferPtr = futureFlatBufferPtr; +#ifndef GPUCA_GPUCODE // code invisible on GPU + delete[] mFlatBufferContainer; // for a case.. +#endif // !GPUCA_GPUCODE + mFlatBufferContainer = nullptr; } -#endif // GPUCA_GPUCODE +#endif //GPUCA_GPUCODE_DEVICE } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 05054953ca90d..d839826bdfb6a 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -16,8 +16,8 @@ if(pythia_FOUND) set(pythiaTarget MC::Pythia) endif() -if(HepMC_FOUND) - set(hepmcTarget MC::HepMC) +if(HepMC3_FOUND) + set(hepmcTarget MC::HepMC3) endif() o2_add_library(Generators @@ -25,13 +25,16 @@ o2_add_library(Generators src/Trigger.cxx src/TriggerParticle.cxx src/GeneratorTGenerator.cxx + src/GeneratorExternalParam.cxx src/GeneratorFromFile.cxx src/PDG.cxx src/PrimaryGenerator.cxx src/InteractionDiamondParam.cxx + src/TriggerExternalParam.cxx src/TriggerParticleParam.cxx src/BoxGunParam.cxx src/QEDGenParam.cxx + src/GenCosmicsParam.cxx src/GeneratorFactory.cxx $<$<BOOL:${pythia6_FOUND}>:src/GeneratorPythia6.cxx> $<$<BOOL:${pythia6_FOUND}>:src/GeneratorPythia6Param.cxx> @@ -39,7 +42,8 @@ o2_add_library(Generators $<$<BOOL:${pythia_FOUND}>:src/DecayerPythia8.cxx> $<$<BOOL:${pythia_FOUND}>:src/GeneratorPythia8Param.cxx> $<$<BOOL:${pythia_FOUND}>:src/DecayerPythia8Param.cxx> - $<$<BOOL:${HepMC_FOUND}>:src/GeneratorHepMC.cxx> + $<$<BOOL:${HepMC3_FOUND}>:src/GeneratorHepMC.cxx> + $<$<BOOL:${HepMC3_FOUND}>:src/GeneratorHepMCParam.cxx> PUBLIC_LINK_LIBRARIES FairRoot::Base O2::SimConfig O2::CommonUtils O2::SimulationDataFormat ${pythia6Target} ${pythiaTarget} ${hepmcTarget} FairRoot::Gen @@ -53,7 +57,7 @@ if(pythia_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_PYTHIA8) endif() -if(HepMC_FOUND) +if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() @@ -62,14 +66,16 @@ set(headers include/Generators/Trigger.h include/Generators/TriggerParticle.h include/Generators/GeneratorTGenerator.h + include/Generators/GeneratorExternalParam.h include/Generators/GeneratorFromFile.h include/Generators/PDG.h include/Generators/PrimaryGenerator.h include/Generators/InteractionDiamondParam.h + include/Generators/TriggerExternalParam.h include/Generators/TriggerParticleParam.h - include/Generators/ConfigurationMacroHelper.h include/Generators/BoxGunParam.h - include/Generators/QEDGenParam.h) + include/Generators/QEDGenParam.h + include/Generators/GenCosmicsParam.h) if (pythia6_FOUND) list(APPEND headers include/Generators/GeneratorPythia6.h @@ -85,8 +91,9 @@ if(pythia_FOUND) include/Generators/GeneratorFactory.h) endif() -if(HepMC_FOUND) +if(HepMC3_FOUND) list(APPEND headers include/Generators/GeneratorHepMC.h) + list(APPEND headers include/Generators/GeneratorHepMCParam.h) endif() o2_target_root_dictionary(Generators HEADERS ${headers}) @@ -105,6 +112,11 @@ if(doBuildSimulation) PUBLIC_LINK_LIBRARIES O2::Generators O2::SimConfig LABELS generators) + + o2_add_test_root_macro(share/external/GenCosmicsLoader.C + PUBLIC_LINK_LIBRARIES O2::Generators + O2::SimConfig + LABELS generators) endif() o2_add_test_root_macro(share/external/tgenerator.C @@ -125,20 +137,6 @@ o2_add_test_root_macro(share/egconfig/pythia8_userhooks_charm.C LABELS generators) endif() -install(FILES share/external/extgen.C share/external/pythia6.C - share/external/tgenerator.C share/external/QEDLoader.C share/external/QEDepem.C - share/external/trigger_multiplicity.C - share/external/trigger_mpi.C - DESTINATION share/Generators/external/) - -install(FILES share/egconfig/pythia8_inel.cfg - share/egconfig/pythia8_hf.cfg - share/egconfig/pythia8_hi.cfg - share/egconfig/pythia8_userhooks_charm.C - DESTINATION share/Generators/egconfig/) - -install(FILES share/pythia8/decays/base.cfg - share/pythia8/decays/bjpsidimuon.cfg - DESTINATION share/Generators/pythia8/decays/) - - +o2_data_file(COPY share/external DESTINATION Generators) +o2_data_file(COPY share/egconfig DESTINATION Generators) +o2_data_file(COPY share/pythia8 DESTINATION Generators) diff --git a/Generators/include/Generators/DecayerPythia8Param.h b/Generators/include/Generators/DecayerPythia8Param.h index 94b3111de2c42..b3f2397af94d8 100644 --- a/Generators/include/Generators/DecayerPythia8Param.h +++ b/Generators/include/Generators/DecayerPythia8Param.h @@ -29,8 +29,9 @@ namespace eventgen **/ struct DecayerPythia8Param : public o2::conf::ConfigurableParamHelper<DecayerPythia8Param> { - std::string config = "${O2_ROOT}/share/Generators/pythia8/decays/base.cfg"; + std::string config[8] = {"${O2_ROOT}/share/Generators/pythia8/decays/base.cfg", "", "", "", "", "", "", ""}; bool verbose = false; + bool showChanged = false; O2ParamDef(DecayerPythia8Param, "DecayerPythia8"); }; diff --git a/Generators/include/Generators/GenCosmicsParam.h b/Generators/include/Generators/GenCosmicsParam.h new file mode 100644 index 0000000000000..372bd9ec3b0a0 --- /dev/null +++ b/Generators/include/Generators/GenCosmicsParam.h @@ -0,0 +1,56 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_GENCOSMICSPARAM_H +#define ALICEO2_GENCOSMICSPARAM_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +// @file GenCosmicsParam +// @author ruben.shahoyan@cern.ch +// @brief Parameters for cosmics generation + +namespace o2 +{ +namespace eventgen +{ +struct GenCosmicsParam : public o2::conf::ConfigurableParamHelper<GenCosmicsParam> { + enum GenParamType : int { ParamMI, + ParamACORDE, + ParamTPC }; // source parameterizations + enum AccType : int { ITS0, + ITS1, + ITS2, + ITS3, + ITS4, + ITS5, + ITS6, + TPC, + Custom }; + GenParamType param = ParamTPC; + AccType accept = TPC; + int nPart = 1; ///< number of particles per event + int maxTrials = 10000000; ///< number of failed trials to abandon generation + float maxAngle = 45.; ///< max angle wrt azimuth to generate (in degrees) + float origin = 550.; ///< create particle at this radius + float pmin = 0.5; ///< min total momentum + float pmax = 100; ///< max total momentum + float customAccX = 250; ///< require particle to pass within this |X| at Y=0 if AccType=custom is selected + float customAccZ = 250; ///< require particle to pass within this |Z| at Y=0 if AccType=custom is selected + + // boilerplate stuff + make principal key + O2ParamDef(GenCosmicsParam, "cosmics"); +}; + +} // namespace eventgen +} // namespace o2 + +#endif diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 9f8d3a23c1085..0d62b05a03d1a 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -18,7 +18,13 @@ #include "Generators/Trigger.h" #include <vector> -class FairMCEventHeader; +namespace o2 +{ +namespace dataformats +{ +class MCEventHeader; +} +} // namespace o2 namespace o2 { @@ -59,7 +65,11 @@ class Generator : public FairGenerator *@param pStack The stack *@return kTRUE if successful, kFALSE if not **/ - Bool_t ReadEvent(FairPrimaryGenerator* primGen) override; + Bool_t ReadEvent(FairPrimaryGenerator* primGen) final; + + /** methods to override **/ + virtual Bool_t generateEvent() = 0; + virtual Bool_t importParticles() = 0; /** setters **/ void setMomentumUnit(double val) { mMomentumUnit = val; }; @@ -71,8 +81,14 @@ class Generator : public FairGenerator void addTrigger(Trigger trigger) { mTriggers.push_back(trigger); }; void addDeepTrigger(DeepTrigger trigger) { mDeepTriggers.push_back(trigger); }; + /** getters **/ + const std::vector<TParticle>& getParticles() const { return mParticles; }; //! + + /** other **/ + void clearParticles() { mParticles.clear(); }; + /** notification methods **/ - virtual void notifyEmbedding(const FairMCEventHeader* mcHeader){}; + virtual void notifyEmbedding(const o2::dataformats::MCEventHeader* eventHeader){}; protected: /** copy constructor **/ @@ -80,12 +96,8 @@ class Generator : public FairGenerator /** operator= **/ Generator& operator=(const Generator&); - /** methods to override **/ - virtual Bool_t generateEvent() = 0; - virtual Bool_t importParticles() = 0; - /** methods that can be overridded **/ - virtual void updateHeader(FairMCEventHeader* eventHeader){}; + virtual void updateHeader(o2::dataformats::MCEventHeader* eventHeader){}; /** internal methods **/ Bool_t addTracks(FairPrimaryGenerator* primGen); diff --git a/Generators/include/Generators/GeneratorExternalParam.h b/Generators/include/Generators/GeneratorExternalParam.h new file mode 100644 index 0000000000000..1fa2fa860cae1 --- /dev/null +++ b/Generators/include/Generators/GeneratorExternalParam.h @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 R+Preghenella - September 2020 + +#ifndef ALICEO2_EVENTGEN_GENERATOREXTERNALPARAM_H_ +#define ALICEO2_EVENTGEN_GENERATOREXTERNALPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace eventgen +{ + +/** + ** a parameter class/struct to keep the settings of + ** the external event-generator and + ** allow the user to modify them + **/ +struct GeneratorExternalParam : public o2::conf::ConfigurableParamHelper<GeneratorExternalParam> { + std::string fileName = ""; + std::string funcName = ""; + O2ParamDef(GeneratorExternalParam, "GeneratorExternal"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_GENERATOREXTERNALPARAM_H_ diff --git a/Generators/include/Generators/GeneratorFromFile.h b/Generators/include/Generators/GeneratorFromFile.h index 42038353e5937..2e2da9c8025e2 100644 --- a/Generators/include/Generators/GeneratorFromFile.h +++ b/Generators/include/Generators/GeneratorFromFile.h @@ -14,6 +14,7 @@ #define ALICEO2_GENERATORFROMFILE_H_ #include "FairGenerator.h" +#include "Generators/Generator.h" class TBranch; class TFile; @@ -58,6 +59,33 @@ class GeneratorFromFile : public FairGenerator ClassDefOverride(GeneratorFromFile, 1); }; +/// This class implements a generic FairGenerator which +/// reads the particles from an external O2 sim kinematics file. +class GeneratorFromO2Kine : public o2::eventgen::Generator +{ + public: + GeneratorFromO2Kine() = default; + GeneratorFromO2Kine(const char* name); + + // the o2 Generator interface methods + bool generateEvent() override + { /* trivial - actual work in importParticles */ + return true; + } + bool importParticles() override; + + // Set from which event to start + void SetStartEvent(int start); + + private: + TFile* mEventFile = nullptr; //! the file containing the persistent events + TBranch* mEventBranch = nullptr; //! the branch containing the persistent events + int mEventCounter = 0; + int mEventsAvailable = 0; + bool mSkipNonTrackable = true; //! whether to pass non-trackable (decayed particles) to the MC stack + ClassDefOverride(GeneratorFromO2Kine, 1); +}; + } // end namespace eventgen } // end namespace o2 diff --git a/Generators/include/Generators/GeneratorHepMC.h b/Generators/include/Generators/GeneratorHepMC.h index 0176bc3525aa3..f479111bc9f67 100644 --- a/Generators/include/Generators/GeneratorHepMC.h +++ b/Generators/include/Generators/GeneratorHepMC.h @@ -16,12 +16,21 @@ #include "Generators/Generator.h" #include <fstream> +#ifdef GENERATORS_WITH_HEPMC3_DEPRECATED namespace HepMC { class Reader; class GenEvent; class FourVector; } // namespace HepMC +#else +namespace HepMC3 +{ +class Reader; +class GenEvent; +class FourVector; +} // namespace HepMC3 +#endif namespace o2 { @@ -45,6 +54,10 @@ class GeneratorHepMC : public Generator /** Initialize the generator if needed **/ Bool_t Init() override; + /** methods to override **/ + Bool_t generateEvent() override; + Bool_t importParticles() override; + /** setters **/ void setVersion(Int_t val) { mVersion = val; }; void setFileName(std::string val) { mFileName = val; }; @@ -55,19 +68,24 @@ class GeneratorHepMC : public Generator /** operator= **/ GeneratorHepMC& operator=(const GeneratorHepMC&); - /** methods to override **/ - Bool_t generateEvent() override; - Bool_t importParticles() override; - /** methods **/ +#ifdef GENERATORS_WITH_HEPMC3_DEPRECATED const HepMC::FourVector getBoostedVector(const HepMC::FourVector& vector, Double_t boost); +#else + const HepMC3::FourVector getBoostedVector(const HepMC3::FourVector& vector, Double_t boost); +#endif /** HepMC interface **/ std::ifstream mStream; //! std::string mFileName; Int_t mVersion; +#ifdef GENERATORS_WITH_HEPMC3_DEPRECATED HepMC::Reader* mReader; //! HepMC::GenEvent* mEvent; //! +#else + HepMC3::Reader* mReader; //! + HepMC3::GenEvent* mEvent; //! +#endif ClassDefOverride(GeneratorHepMC, 1); diff --git a/Generators/include/Generators/GeneratorHepMCParam.h b/Generators/include/Generators/GeneratorHepMCParam.h new file mode 100644 index 0000000000000..0c2826a687c85 --- /dev/null +++ b/Generators/include/Generators/GeneratorHepMCParam.h @@ -0,0 +1,40 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 R+Preghenella - January 2020 + +#ifndef ALICEO2_EVENTGEN_GENERATORHEPMCPARAM_H_ +#define ALICEO2_EVENTGEN_GENERATORHEPMCPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include <string> + +namespace o2 +{ +namespace eventgen +{ + +/** + ** a parameter class/struct to keep the settings of + ** the HepMC event generator and + ** allow the user to modify them + **/ + +struct GeneratorHepMCParam : public o2::conf::ConfigurableParamHelper<GeneratorHepMCParam> { + std::string fileName = ""; + int version = 2; + O2ParamDef(GeneratorHepMCParam, "HepMC"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_GENERATORHEPMCPARAM_H_ diff --git a/Generators/include/Generators/GeneratorPythia8.h b/Generators/include/Generators/GeneratorPythia8.h index 9332d193bdd66..1369d531cc129 100644 --- a/Generators/include/Generators/GeneratorPythia8.h +++ b/Generators/include/Generators/GeneratorPythia8.h @@ -38,27 +38,63 @@ class GeneratorPythia8 : public Generator /** Initialize the generator if needed **/ Bool_t Init() override; + /** methods to override **/ + Bool_t generateEvent() override; + Bool_t importParticles() override { return importParticles(mPythia.event); }; + /** setters **/ void setConfig(std::string val) { mConfig = val; }; void setHooksFileName(std::string val) { mHooksFileName = val; }; void setHooksFuncName(std::string val) { mHooksFuncName = val; }; + void setUserHooks(Pythia8::UserHooks* hooks) + { +#if PYTHIA_VERSION_INTEGER < 8300 + mPythia.setUserHooksPtr(hooks); +#else + mPythia.setUserHooksPtr(std::shared_ptr<Pythia8::UserHooks>(hooks)); +#endif + } /** methods **/ bool readString(std::string val) { return mPythia.readString(val, true); }; bool readFile(std::string val) { return mPythia.readFile(val, true); }; + /** utilities **/ + void getNcoll(int& nColl) + { + getNcoll(mPythia.info, nColl); + }; + void getNpart(int& nPart) + { + getNpart(mPythia.info, nPart); + }; + void getNpart(int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg) + { + getNpart(mPythia.info, nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg); + }; + void getNremn(int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg) + { + getNremn(mPythia.event, nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg); + }; + protected: /** copy constructor **/ GeneratorPythia8(const GeneratorPythia8&); /** operator= **/ GeneratorPythia8& operator=(const GeneratorPythia8&); - /** methods to override **/ - Bool_t generateEvent() override; - Bool_t importParticles() override; - /** methods that can be overridded **/ - void updateHeader(FairMCEventHeader* eventHeader) override; + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override; + + /** internal methods **/ + Bool_t importParticles(Pythia8::Event& event); + + /** utilities **/ + void selectFromAncestor(int ancestor, Pythia8::Event& inputEvent, Pythia8::Event& outputEvent); + void getNcoll(const Pythia8::Info& info, int& nColl); + void getNpart(const Pythia8::Info& info, int& nPart); + void getNpart(const Pythia8::Info& info, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg); + void getNremn(const Pythia8::Event& event, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg); /** Pythia8 **/ Pythia8::Pythia mPythia; //! diff --git a/Generators/include/Generators/GeneratorPythia8Param.h b/Generators/include/Generators/GeneratorPythia8Param.h index e4c3b77a163ca..e9133da76e6f0 100644 --- a/Generators/include/Generators/GeneratorPythia8Param.h +++ b/Generators/include/Generators/GeneratorPythia8Param.h @@ -32,7 +32,7 @@ struct GeneratorPythia8Param : public o2::conf::ConfigurableParamHelper<Generato std::string config = ""; std::string hooksFileName = ""; std::string hooksFuncName = ""; - O2ParamDef(GeneratorPythia8Param, "Pythia8"); + O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8"); }; } // end namespace eventgen diff --git a/Generators/include/Generators/GeneratorTGenerator.h b/Generators/include/Generators/GeneratorTGenerator.h index 72dfe17a4a003..54e257aede20e 100644 --- a/Generators/include/Generators/GeneratorTGenerator.h +++ b/Generators/include/Generators/GeneratorTGenerator.h @@ -37,6 +37,10 @@ class GeneratorTGenerator : public Generator /** destructor **/ ~GeneratorTGenerator() override; + /** methods to override **/ + Bool_t generateEvent() override; + Bool_t importParticles() override; + /** setters **/ void setTGenerator(TGenerator* val) { mTGenerator = val; }; const TGenerator* getTGenerator() const { return mTGenerator; } @@ -49,10 +53,6 @@ class GeneratorTGenerator : public Generator /** operator= **/ GeneratorTGenerator& operator=(const GeneratorTGenerator&); - /** methods to override **/ - Bool_t generateEvent() override; - Bool_t importParticles() override; - /** TGenerator interface **/ TGenerator* mTGenerator; TClonesArray* mCloneParticles; diff --git a/Generators/include/Generators/PrimaryGenerator.h b/Generators/include/Generators/PrimaryGenerator.h index 484db8b0c2a70..9c576118fcbca 100644 --- a/Generators/include/Generators/PrimaryGenerator.h +++ b/Generators/include/Generators/PrimaryGenerator.h @@ -55,18 +55,14 @@ class PrimaryGenerator : public FairPrimaryGenerator **/ Bool_t GenerateEvent(FairGenericStack* pStack) override; - /** Public method AddTrack - Adding a track to the MC stack. To be called within the ReadEvent - methods of the registered generators. - *@param pdgid Particle ID (PDG code) - *@param px,py,pz Momentum coordinates [GeV] - *@param vx,vy,vz Track origin relative to event vertex - **/ + /** ALICE-o2 AddTrack with mother/daughter indices **/ void AddTrack(Int_t pdgid, Double_t px, Double_t py, Double_t pz, Double_t vx, Double_t vy, Double_t vz, - Int_t parent = -1, Bool_t wanttracking = true, + Int_t mother1 = -1, Int_t mother2 = -1, + Int_t daughter1 = -1, Int_t daughter2 = -1, + Bool_t wanttracking = true, Double_t e = -9e9, Double_t tof = 0., - Double_t weight = 0., TMCProcess proc = kPPrimary) override; + Double_t weight = 0., TMCProcess proc = kPPrimary); /** initialize the generator **/ Bool_t Init() override; diff --git a/Generators/include/Generators/Pythia6Generator.h b/Generators/include/Generators/Pythia6Generator.h deleted file mode 100644 index 9dd94f2ff714e..0000000000000 --- a/Generators/include/Generators/Pythia6Generator.h +++ /dev/null @@ -1,128 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence version 3 (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ -// ------------------------------------------------------------------------- -// ----- M. Al-Turany June 2014 ----- -// ------------------------------------------------------------------------- - -// ------------------------------------------------------------------------- -// ----- Pythia6Generator header file ----- -// ----- Created 08/08/08 by S. Spataro ----- -// ------------------------------------------------------------------------- - -/** Pythia6Generator.h - *@author S.Spataro <spataro@to.infn.it> - * - The Pythia6Generator reads a Pythia6 input file. The file must contain - for each event a header line of the format: - - [start] -1 20 - 3 -2212 0 0 0 0 0.00000000E+00 0.00000000E+00 0.14000000E+02 0.14031406E+02 0.93827000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 2212 0 0 0 0 0.00000000E+00 0.00000000E+00 -0.13444107E-16 0.93827000E+00 0.93827000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 -2 1 0 0 0 -0.70661074E+00 -0.81156104E+00 0.49379331E+01 0.50538217E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 2 2 0 0 0 -0.79043780E+00 0.32642680E+00 0.10299757E+01 0.13387293E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 -2 3 0 0 0 -0.70661074E+00 -0.81156104E+00 0.49379331E+01 0.50538217E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 2 4 0 0 0 -0.79043780E+00 0.32642680E+00 0.10299757E+01 0.13387293E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 23 5 6 0 0 -0.14970485E+01 -0.48513424E+00 0.59679088E+01 0.63925510E+01 0.16650116E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 11 7 0 0 0 -0.16024130E+01 0.20883507E-01 0.32233123E+01 0.35997091E+01 0.51000000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 -11 7 0 0 0 0.10536443E+00 -0.50601774E+00 0.27445965E+01 0.27928419E+01 0.51000000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 2 23 7 0 11 13 -0.14970485E+01 -0.48513424E+00 0.59679088E+01 0.63925510E+01 0.16650116E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 1 -11 9 0 0 0 0.10536442E+00 -0.50601769E+00 0.27445962E+01 0.27928416E+01 0.51000000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 1 11 8 0 0 0 -0.16017105E+01 0.20863360E-01 0.32218964E+01 0.35981284E+01 0.51000000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 1 22 8 0 0 0 -0.70246172E-03 0.20096726E-04 0.14161816E-02 0.15809575E-02 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 2 -2103 1 0 16 16 0.70661074E+00 0.81156104E+00 0.69735272E+01 0.70980957E+01 0.77133000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 2 2101 2 0 16 16 0.79043780E+00 -0.32642680E+00 0.10585640E+01 0.14790292E+01 0.57933000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 2 92 14 15 17 18 0.14970485E+01 0.48513424E+00 0.80320912E+01 0.85771248E+01 0.25643853E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 2 -2214 16 0 19 20 0.81382620E+00 0.70515618E+00 0.61830559E+01 0.63829315E+01 0.11627878E+01 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 1 2212 16 0 0 0 0.68322234E+00 -0.22002195E+00 0.18490353E+01 0.21941934E+01 0.93827000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 1 -2112 17 0 0 0 0.78494059E+00 0.52384336E+00 0.55944336E+01 0.57507411E+01 0.93957000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 1 -211 17 0 0 0 0.28885610E-01 0.18131282E+00 0.58862227E+00 0.63219038E+00 0.13957000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 2 25 - 3 -2212 0 0 0 0 0.00000000E+00 0.00000000E+00 0.14000000E+02 0.14031406E+02 0.93827000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - 3 2212 0 0 0 0 0.00000000E+00 0.00000000E+00 -0.13444107E-16 0.93827000E+00 0.93827000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 - - -... - [stop] - - where the first row has the number of event and the number of particles, and below "N" is the line - number of the event, - - Derived from FairGenerator. -**/ - -#ifndef PND_PYTHIAGENERATOR_H -#define PND_PYTHIAGENERATOR_H - -#ifdef __CLING__ -#define _DLFCN_H_ -#define _DLFCN_H -#endif - -#include <cstdio> // for FILE -#include "FairGenerator.h" // for FairGenerator -#include "Rtypes.h" // for Int_t, Pythia6Generator::Class, Bool_t, etc -class FairPrimaryGenerator; // lines 68-68 - -namespace o2 -{ -namespace eventgen -{ - -class Pythia6Generator : public FairGenerator -{ - - public: - /** Default constructor without arguments should not be used. **/ - Pythia6Generator(); - - /** Standard constructor. - ** @param fileName The input file name - **/ - Pythia6Generator(const char* fileName); - - /** Destructor. **/ - ~Pythia6Generator() override; - - /** Reads on event from the input file and pushes the tracks onto - ** the stack. Abstract method in base class. - ** @param primGen pointer to the CbmrimaryGenerator - **/ - Bool_t ReadEvent(FairPrimaryGenerator* primGen) override; - - void SetVerbose(Int_t verb) { mVerbose = verb; }; - - private: - const Char_t* mFileName; //! Input file Name - FILE* mInputFile; //! File - Int_t mVerbose; //! Verbose Level - - /** Private method CloseInput. Just for convenience. Closes the - ** input file properly. Called from destructor and from ReadEvent. **/ - void CloseInput(); - - /** PDG data base */ - - // TDatabasePDG *mPDG; //! - - ClassDefOverride(Pythia6Generator, 1); -}; - -} // namespace eventgen -} // namespace o2 -#endif diff --git a/Generators/include/Generators/Pythia8Generator.h b/Generators/include/Generators/Pythia8Generator.h deleted file mode 100644 index 9b60415dd5504..0000000000000 --- a/Generators/include/Generators/Pythia8Generator.h +++ /dev/null @@ -1,87 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence version 3 (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ -// ------------------------------------------------------------------------- -// ----- M. Al-Turany June 2014 ----- -// ------------------------------------------------------------------------- - -#ifndef PNDP8GENERATOR_H -#define PNDP8GENERATOR_H 1 - -#include "FairGenerator.h" // for FairGenerator -#include "Rtypes.h" // for Double_t, Bool_t, Int_t, etc -#include <memory> - -class FairPrimaryGenerator; -namespace Pythia8 -{ -class Pythia; -class RndmEngine; -} // namespace Pythia8 -namespace o2 -{ -namespace eventgen -{ - -class Pythia8Generator : public FairGenerator -{ - public: - /** default constructor **/ - Pythia8Generator(); - - /** destructor **/ - ~Pythia8Generator() override; - - /** public method ReadEvent **/ - Bool_t ReadEvent(FairPrimaryGenerator*) override; - void SetParameters(const char*); - void Print(); //! - - Bool_t Init() override; //! - - void SetMom(Double_t mom) { mMom = mom; }; - void SetId(Double_t id) { mId = id; }; - void SetHNLId(Int_t id) { mHNL = id; }; - void UseRandom1() - { - mUseRandom1 = kTRUE; - mUseRandom3 = kFALSE; - }; - void UseRandom3() - { - mUseRandom1 = kFALSE; - mUseRandom3 = kTRUE; - }; - void GetPythiaInstance(int); - - private: - std::unique_ptr<Pythia8::Pythia> mPythia; //! - std::unique_ptr<Pythia8::RndmEngine> mRandomEngine; //! - - protected: - Double_t mMom; // proton momentum - Int_t mHNL; // HNL ID - Int_t mId; // target type - Bool_t mUseRandom1; // flag to use TRandom1 - Bool_t mUseRandom3; // flag to use TRandom3 (default) - - ClassDefOverride(Pythia8Generator, 1); -}; - -} // namespace eventgen -} // namespace o2 -#endif /* !PNDP8GENERATOR_H */ diff --git a/Generators/include/Generators/TriggerExternalParam.h b/Generators/include/Generators/TriggerExternalParam.h new file mode 100644 index 0000000000000..2360ca16f36c7 --- /dev/null +++ b/Generators/include/Generators/TriggerExternalParam.h @@ -0,0 +1,38 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 R+Preghenella - September 2020 + +#ifndef ALICEO2_EVENTGEN_TRIGGEREXTERNALPARAM_H_ +#define ALICEO2_EVENTGEN_TRIGGEREXTERNALPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace eventgen +{ + +/** + ** a parameter class/struct to keep the settings of + ** the event-generator external trigger and + ** allow the user to modify them + **/ +struct TriggerExternalParam : public o2::conf::ConfigurableParamHelper<TriggerExternalParam> { + std::string fileName = ""; + std::string funcName = ""; + O2ParamDef(TriggerExternalParam, "TriggerExternal"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_TRIGGEREXTERNALPARAM_H_ diff --git a/Generators/share/egconfig/pythia8_userhooks_charm.C b/Generators/share/egconfig/pythia8_userhooks_charm.C index 95f824a8c9aae..19004c3f4a9f2 100644 --- a/Generators/share/egconfig/pythia8_userhooks_charm.C +++ b/Generators/share/egconfig/pythia8_userhooks_charm.C @@ -1,6 +1,6 @@ // Pythia8 UserHooks // -// usage: o2sim -g pythia8 --configKeyValue "Pythia8.hooksFileName=pythia8_userhooks_charm.C" +// usage: o2sim -g pythia8 --configKeyValues "GeneratorPythia8.hooksFileName=pythia8_userhooks_charm.C" // /// \author R+Preghenella - February 2020 diff --git a/Generators/share/external/GenCosmics.C b/Generators/share/external/GenCosmics.C new file mode 100644 index 0000000000000..417ce670e78f1 --- /dev/null +++ b/Generators/share/external/GenCosmics.C @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +//< Macro to run QED background generator, us it as e.g. +//< o2-sim -n10000 -m PIPE ITS TPC -g extgen --configKeyValues "GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/GenCosmicsLoader.C" + +R__LOAD_LIBRARY(libGeneratorCosmics) + +using namespace o2::eventgen; + +o2::eventgen::GeneratorTGenerator* GenCosmics() +{ + auto genCosm = new GeneratorCosmics(); + auto& cosmParam = GenCosmicsParam::Instance(); + + if (cosmParam.param == GenCosmicsParam::ParamMI) { + genCosm->setParamMI(); + } else if (cosmParam.param == GenCosmicsParam::ParamACORDE) { + genCosm->setParamACORDE(); + } else if (cosmParam.param == GenCosmicsParam::ParamTPC) { + genCosm->setParamTPC(); + } else { + LOG(FATAL) << "Unknown cosmics param type " << cosmParam.param; + } + + genCosm->setNPart(cosmParam.nPart); + genCosm->setPRange(cosmParam.pmin, cosmParam.pmax); + + switch (cosmParam.accept) { + case GenCosmicsParam::ITS0: + genCosm->requireITS0(); + break; + case GenCosmicsParam::ITS1: + genCosm->requireITS1(); + break; + case GenCosmicsParam::ITS2: + genCosm->requireITS2(); + break; + case GenCosmicsParam::ITS3: + genCosm->requireITS3(); + break; + case GenCosmicsParam::ITS4: + genCosm->requireITS4(); + break; + case GenCosmicsParam::ITS5: + genCosm->requireITS5(); + break; + case GenCosmicsParam::ITS6: + genCosm->requireITS6(); + break; + case GenCosmicsParam::TPC: + genCosm->requireTPC(); + break; + case GenCosmicsParam::Custom: + genCosm->requireXZAccepted(cosmParam.customAccX, cosmParam.customAccZ); + break; + default: + LOG(FATAL) << "Unknown cosmics acceptance type " << cosmParam.accept; + break; + } + + genCosm->Init(); + + // instance and configure TGenerator interface + auto tgen = new o2::eventgen::GeneratorTGenerator(); + tgen->setMomentumUnit(1.); // [GeV/c] + tgen->setEnergyUnit(1.); // [GeV/c] + tgen->setPositionUnit(1.); // [cm] + tgen->setTimeUnit(1.); // [s] + tgen->setTGenerator(genCosm); + fg = tgen; + return tgen; +} diff --git a/Generators/share/external/GenCosmicsLoader.C b/Generators/share/external/GenCosmicsLoader.C new file mode 100644 index 0000000000000..7242a7e2cb9c1 --- /dev/null +++ b/Generators/share/external/GenCosmicsLoader.C @@ -0,0 +1,35 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +//< Loader macro to run QED background generator from QEDepem.C macro, use it as e.g. +//< o2-sim -n10000 -m PIPE ITS TPC -g extgen --configKeyValues "GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/GenCosmicsLoader.C" +//< Generation options can be changed by providing --configKeyValues "cosmics.maxAngle=30.;cosmics.accept=ITS0" etc. +//< See GenCosmicsParam for available options + +#include <FairLogger.h> + +FairGenerator* fg = nullptr; + +FairGenerator* GenCosmicsLoader() +{ + const TString macroName = "GenCosmics"; + gSystem->Load("libGeneratorCosmics"); + + // the path of the macro to load depends on where it was installed, we assume that its installation + // directory is the same as of the loader macro + std::ostringstream mstr; + mstr << __FILE__; + TString macroFullName = Form("%s/%s.C", gSystem->DirName(mstr.str().c_str()), macroName.Data()); + LOG(INFO) << "\nLoading " << macroFullName.Data() << "\n"; + + gROOT->LoadMacro(macroFullName.Data()); + gInterpreter->ProcessLine(Form("%s()", macroName.Data())); + return fg; +} diff --git a/Generators/share/external/QEDLoader.C b/Generators/share/external/QEDLoader.C index c35986b6f6d38..2426233316707 100644 --- a/Generators/share/external/QEDLoader.C +++ b/Generators/share/external/QEDLoader.C @@ -9,7 +9,7 @@ // or submit itself to any jurisdiction. //< Loader macro to run QED background generator from QEDepem.C macro, use it as e.g. -//< o2-sim -n10000 -m PIPE ITS T0 MFT --noemptyevents -g extgen --extGenFile QEDloader.C +//< o2-sim -n10000 -m PIPE ITS T0 MFT --noemptyevents -g external --configKeyValues "GeneratorExternal.fileName=QEDloader.C" #include <FairLogger.h> FairGenerator* fg = nullptr; @@ -17,7 +17,7 @@ FairGenerator* fg = nullptr; FairGenerator* QEDLoader() { const TString macroName = "QEDepem"; - gSystem->Load("libTEPEMGEN.so"); + gSystem->Load("libTEPEMGEN"); // the path of the macro to load depends on where it was installed, we assume that its installation // directory is the same as of the loader macro diff --git a/Generators/share/external/QEDepem.C b/Generators/share/external/QEDepem.C index ddb9efa7ca3b9..4ed3cdcc444c9 100644 --- a/Generators/share/external/QEDepem.C +++ b/Generators/share/external/QEDepem.C @@ -9,9 +9,9 @@ // or submit itself to any jurisdiction. //< Macro to run QED background generator, us it as e.g. -//< o2-sim -n10000 -m PIPE ITS T0 MFT --noemptyevents -g extgen --extGenFile QEDloader.C +//< o2-sim -n10000 -m PIPE ITS T0 MFT --noemptyevents -g external --configKeyValues "GeneratorExternal.fileName=QEDloader.C" -R__LOAD_LIBRARY(libTEPEMGEN.so) +R__LOAD_LIBRARY(libTEPEMGEN) o2::eventgen::GeneratorTGenerator* QEDepem() { diff --git a/Generators/share/external/README.md b/Generators/share/external/README.md index da3d956209bbb..6eab4521b7faa 100644 --- a/Generators/share/external/README.md +++ b/Generators/share/external/README.md @@ -8,13 +8,25 @@ ------------ -## qedbkg.C +## QEDLoader.C / QEDepem.C - Invokes TGenEpEmv1 generator from [AEGIS](https://github.com/AliceO2Group/AEGIS) package for Pb-Pb → e+e- generation. + optional parameters are rapidity and pT ranges to generate, can be provided as key/value option, e.g. `` -o2-sim -n 1000 -m PIPE ITS -g extgen --extGenFile $O2_ROOT/share/Generators/external/QEDLoader.C --configKeyValues 'QEDGenParam.yMin=-3;QEDGenParam.ptMin=0.001' +o2-sim -n 1000 -m PIPE ITS -g external --configKeyValues 'GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/QEDLoader.C;QEDGenParam.yMin=-3;QEDGenParam.ptMin=0.001' `` The x-section of the process depends on the applied cuts, it is calculated on the fly and stored in the ``qedgenparam.ini`` file. +## GenCosmicsLoader.C / GenCosmics.C + +- Invokes GenerateCosmics generators from [AEGIS](https://github.com/AliceO2Group/AEGIS) package. + +``o2-sim -n1000 -m PIPE ITS TPC -g extgen --configKeyValues "GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/GenCosmicsLoader.C"`` + +Generation options can be changed by providing ``--configKeyValues "cosmics.maxAngle=30.;cosmics.accept=ITS0"`` etc. +For instance, to generate track defined at radius 500 cm, with maximal angle wrt the azimuth of 40 degress and passing via ITS layer 0 at Y=0: + +``o2-sim -n100 -m PIPE ITS TPC -g extgen --configKeyValues "cosmics.maxAngle=40.;cosmics.accept=ITS0;cosmics.origin=500;GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/GenCosmicsLoader.C"`` + +See GenCosmicsParam class for available options. ------------ diff --git a/Generators/share/external/hijing.C b/Generators/share/external/hijing.C index 393a81b543cbf..cfd13ef384a80 100644 --- a/Generators/share/external/hijing.C +++ b/Generators/share/external/hijing.C @@ -1,6 +1,5 @@ // configures a AliGenHijing class from AliRoot -// usage: o2sim -g extgen --extGenFile hijing.C -// options: --extGenFunc hijing(5020., 0., 20.) +// usage: o2sim -g external --configKeyValues 'GeneratorExternal.fileName=hijing.C;GeneratorExternal.funcName="hijing(5020., 0., 20.)"' /// \author R+Preghenella - October 2018 diff --git a/Generators/share/external/pythia6.C b/Generators/share/external/pythia6.C index e5c012dd51e81..1ba993a1e7eb7 100644 --- a/Generators/share/external/pythia6.C +++ b/Generators/share/external/pythia6.C @@ -1,6 +1,5 @@ // configures a TPythia6 class -// usage: o2sim -g extgen --extGenFile pythia6.C -// options: --extGenFunc pythia6(14000., "pythia.settings") +// usage: o2sim -g external --configKeyValues 'GeneratorExternal.fileName=pythia6.C;GeneratorExternal.funcName="pythia6(14000., "pythia.settings")"' /// \author R+Preghenella - October 2018 diff --git a/Generators/share/external/trigger_mpi.C b/Generators/share/external/trigger_mpi.C index e4872370796ca..c2feff38ed16d 100644 --- a/Generators/share/external/trigger_mpi.C +++ b/Generators/share/external/trigger_mpi.C @@ -1,7 +1,6 @@ // MPI trigger // -// usage: o2sim --trigger external --extTrgFile trigger_mpi.C -// options: --extTrgFunc "trigger_mpi()" +// usage: o2sim --trigger external --configKeyValues 'TriggerExternal.fileName=trigger_mpi.C;TriggerExternal.funcName="trigger_mpi()"' // /// \author R+Preghenella - February 2020 diff --git a/Generators/share/external/trigger_multiplicity.C b/Generators/share/external/trigger_multiplicity.C index 045fc676e0ad6..94336d43d80d5 100644 --- a/Generators/share/external/trigger_multiplicity.C +++ b/Generators/share/external/trigger_multiplicity.C @@ -1,7 +1,6 @@ // multiplicity trigger // -// usage: o2sim --trigger external --extTrgFile trigger_multiplicity.C -// options: --extTrgFunc "trigger_multiplicity(-0.8, 0.8, 100)" +// usage: o2sim --trigger external --configKeyValues 'TriggerExternal.fileName=trigger_multiplicity.C;TriggerExternal.funcName="trigger_multiplicity(-0.8, 0.8, 100)"' // /// \author R+Preghenella - February 2020 diff --git a/Generators/share/pythia8/decays/force_hadronic_D.cfg b/Generators/share/pythia8/decays/force_hadronic_D.cfg new file mode 100644 index 0000000000000..e51167aae4398 --- /dev/null +++ b/Generators/share/pythia8/decays/force_hadronic_D.cfg @@ -0,0 +1,96 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file reproduces the configuration achieved with ForceHadronicD(0,0,0) + +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.00276 0 333 211 +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 3124 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +4122:addChannel = 1 0.0159 0 2212 311 +4122:addChannel = 1 0.0130 0 3122 211 +### add Xic+ decays absent in PYTHIA8 decay table +4232:addChannel = 1 0.2 0 2212 -313 +4232:addChannel = 1 0.2 0 2212 -321 211 +4232:addChannel = 1 0.2 0 3324 211 +4232:addChannel = 1 0.2 0 3312 211 211 +### add Xic0 decays absent in PYTHIA8 decay table +4132:addChannel = 1 0.2 0 3312 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +3124:onMode = off +3124:onIfAll = 2212 321 + +### Omega_c -> Omega pi +4332:onMode = off +4332:onIfMatch = 3334 211 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4112:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D0 -> K pi +421:onIfMatch = 321 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 3124 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 +### Lambda_c -> Lambda pi +4122:onIfMatch = 3122 211 +### Lambda_c -> p K0 +4122:onIfMatch = 2212 311 + +### Xic+ -> pK*0 +4232:onIfMatch = 2212 313 +### Xic+ -> p K- pi+ +4232:onIfMatch = 2212 321 211 +### Xic+ -> Xi*0 pi+, Xi*->Xi- pi+ +4232:onIfMatch = 3324 211 +### Xic+ -> Xi- pi+ pi+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 diff --git a/Generators/share/pythia8/decays/force_hadronic_D_forceLcChannel1.cfg b/Generators/share/pythia8/decays/force_hadronic_D_forceLcChannel1.cfg new file mode 100644 index 0000000000000..33abbb1b6f340 --- /dev/null +++ b/Generators/share/pythia8/decays/force_hadronic_D_forceLcChannel1.cfg @@ -0,0 +1,12 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (force Lc channel 1) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(0,0,1) + +### force Lc -> p K pi +4122:onMode = off +4122:onIfMatch = 2212 321 211 diff --git a/Generators/share/pythia8/decays/force_hadronic_D_forceLcChannel2.cfg b/Generators/share/pythia8/decays/force_hadronic_D_forceLcChannel2.cfg new file mode 100644 index 0000000000000..d79dec4d8b88f --- /dev/null +++ b/Generators/share/pythia8/decays/force_hadronic_D_forceLcChannel2.cfg @@ -0,0 +1,12 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (force Lc channel 2) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(0,0,2) + +### force Lc -> p K0s +4122:onMode = off +4122:onIfMatch = 2212 311 diff --git a/Generators/share/pythia8/decays/force_hadronic_D_use4bodies.cfg b/Generators/share/pythia8/decays/force_hadronic_D_use4bodies.cfg new file mode 100644 index 0000000000000..3178dad7d368b --- /dev/null +++ b/Generators/share/pythia8/decays/force_hadronic_D_use4bodies.cfg @@ -0,0 +1,15 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (use 4 bodies option) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(1,0,0) + +### D0 -> K pi pi pi +421:onIfMatch = 321 211 211 211 +### D0 -> K pi rho +421:onIfMatch = 321 211 113 +### D0 -> K*0 pi pi +421:onIfMatch = 313 211 211 diff --git a/Generators/share/pythia8/decays/force_hadronic_D_useDtoV0.cfg b/Generators/share/pythia8/decays/force_hadronic_D_useDtoV0.cfg new file mode 100644 index 0000000000000..5b50c59e0130c --- /dev/null +++ b/Generators/share/pythia8/decays/force_hadronic_D_useDtoV0.cfg @@ -0,0 +1,13 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (use D to V0 option) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(0,1,0) + +### Ds -> K0K +431:onIfMatch = 311 321 +### Ds -> K0pi +411:onIfMatch = 311 211 diff --git a/Generators/share/pythia8/decays/hypertriton.cfg b/Generators/share/pythia8/decays/hypertriton.cfg new file mode 100644 index 0000000000000..518fbbf6418ba --- /dev/null +++ b/Generators/share/pythia8/decays/hypertriton.cfg @@ -0,0 +1,12 @@ +### particle definition +### id:all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 + +### helium-3 +1000020030:all = helium3 helium3_bar 0 6 0 2.80923 0. 0. 0. 0. +1000020030:mayDecay = off + +### hypertriton +1010010030:all = hypertriton hypertriton_bar 0 3 0 2.99131 0. 0. 0. 78.9 +1010010030:mayDecay = on +1010010030:oneChannel = 1 1. 0 1000020030 -211 + diff --git a/Generators/src/DecayerPythia8.cxx b/Generators/src/DecayerPythia8.cxx index adb4b00f36df1..77c4c04cff7b7 100644 --- a/Generators/src/DecayerPythia8.cxx +++ b/Generators/src/DecayerPythia8.cxx @@ -38,22 +38,29 @@ void DecayerPythia8::Init() auto& param = DecayerPythia8Param::Instance(); LOG(INFO) << "Init \'DecayerPythia8\' with following parameters"; LOG(INFO) << param; - if (!param.config.empty()) { - std::stringstream ss(param.config); - std::string config; - while (getline(ss, config, ' ')) { - config = gSystem->ExpandPathName(config.c_str()); - if (!mPythia.readFile(config, true)) { - LOG(FATAL) << "Failed to init \'DecayerPythia8\': problems with configuration file " - << config; - return; - } + for (int i = 0; i < 8; ++i) { + if (param.config[i].empty()) { + continue; + } + std::string config = gSystem->ExpandPathName(param.config[i].c_str()); + LOG(INFO) << "Reading configuration from file: " << config; + if (!mPythia.readFile(config, true)) { + LOG(FATAL) << "Failed to init \'DecayerPythia8\': problems with configuration file " + << config; + return; } } /** verbose flag **/ mVerbose = param.verbose; + /** show changed particle data **/ + if (param.showChanged) { + mPythia.readString(std::string("Init:showChangedParticleData on")); + } else { + mPythia.readString(std::string("Init:showChangedParticleData off")); + } + /** initialise **/ if (!mPythia.init()) { LOG(FATAL) << "Failed to init \'DecayerPythia8\': init returned with error"; @@ -71,8 +78,9 @@ void DecayerPythia8::Decay(Int_t pdg, TLorentzVector* lv) mPythia.event.clear(); mPythia.event.append(pdg, 11, 0, 0, lv->Px(), lv->Py(), lv->Pz(), lv->E(), lv->M()); mPythia.moreDecays(); - if (mVerbose) + if (mVerbose) { mPythia.event.list(); + } mPythia.particleData.mayDecay(pdg, mayDecay); // restore mayDecay status } diff --git a/Generators/src/GenCosmicsParam.cxx b/Generators/src/GenCosmicsParam.cxx new file mode 100644 index 0000000000000..6d23cdf0747fa --- /dev/null +++ b/Generators/src/GenCosmicsParam.cxx @@ -0,0 +1,15 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "Generators/GenCosmicsParam.h" + +using namespace o2::eventgen; + +O2ParamImpl(o2::eventgen::GenCosmicsParam); diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 2507773da3566..edb627c2909c9 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -12,6 +12,8 @@ #include "Generators/Generator.h" #include "Generators/Trigger.h" +#include "Generators/PrimaryGenerator.h" +#include "SimulationDataFormat/MCEventHeader.h" #include "FairPrimaryGenerator.h" #include "FairLogger.h" #include <cmath> @@ -67,24 +69,34 @@ Bool_t mParticles.clear(); /** generate event **/ - if (!generateEvent()) + if (!generateEvent()) { return kFALSE; + } /** import particles **/ - if (!importParticles()) + if (!importParticles()) { return kFALSE; + } /** trigger event **/ - if (triggerEvent()) + if (triggerEvent()) { break; + } } /** add tracks **/ - if (!addTracks(primGen)) + if (!addTracks(primGen)) { return kFALSE; + } /** update header **/ - updateHeader(primGen->GetEvent()); + auto header = primGen->GetEvent(); + auto o2header = dynamic_cast<o2::dataformats::MCEventHeader*>(header); + if (!header) { + LOG(FATAL) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; + return kFALSE; + } + updateHeader(o2header); /** success **/ return kTRUE; @@ -97,20 +109,29 @@ Bool_t { /** add tracks **/ + auto o2primGen = dynamic_cast<PrimaryGenerator*>(primGen); + if (!o2primGen) { + LOG(FATAL) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; + return kFALSE; + } + /** loop over particles **/ for (const auto& particle : mParticles) { - primGen->AddTrack(particle.GetPdgCode(), - particle.Px() * mMomentumUnit, - particle.Py() * mMomentumUnit, - particle.Pz() * mMomentumUnit, - particle.Vx() * mPositionUnit, - particle.Vy() * mPositionUnit, - particle.Vz() * mPositionUnit, - particle.GetMother(0), - particle.GetStatusCode() == 1, - particle.Energy() * mEnergyUnit, - particle.T() * mTimeUnit, - particle.GetWeight()); + o2primGen->AddTrack(particle.GetPdgCode(), + particle.Px() * mMomentumUnit, + particle.Py() * mMomentumUnit, + particle.Pz() * mMomentumUnit, + particle.Vx() * mPositionUnit, + particle.Vy() * mPositionUnit, + particle.Vz() * mPositionUnit, + particle.GetMother(0), + particle.GetMother(1), + particle.GetDaughter(0), + particle.GetDaughter(1), + particle.GetStatusCode() == 1, + particle.Energy() * mEnergyUnit, + particle.T() * mTimeUnit, + particle.GetWeight()); } /** success **/ @@ -136,36 +157,42 @@ Bool_t /** trigger event **/ /** check trigger presence **/ - if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) + if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { return kTRUE; + } /** check trigger mode **/ Bool_t triggered; - if (mTriggerMode == kTriggerOFF) + if (mTriggerMode == kTriggerOFF) { return kTRUE; - else if (mTriggerMode == kTriggerOR) + } else if (mTriggerMode == kTriggerOR) { triggered = kFALSE; - else if (mTriggerMode == kTriggerAND) + } else if (mTriggerMode == kTriggerAND) { triggered = kTRUE; - else + } else { return kTRUE; + } /** loop over triggers **/ for (const auto& trigger : mTriggers) { auto retval = trigger(mParticles); - if (mTriggerMode == kTriggerOR) + if (mTriggerMode == kTriggerOR) { triggered |= retval; - if (mTriggerMode == kTriggerAND) + } + if (mTriggerMode == kTriggerAND) { triggered &= retval; + } } /** loop over deep triggers **/ for (const auto& trigger : mDeepTriggers) { auto retval = trigger(mInterface, mInterfaceName); - if (mTriggerMode == kTriggerOR) + if (mTriggerMode == kTriggerOR) { triggered |= retval; - if (mTriggerMode == kTriggerAND) + } + if (mTriggerMode == kTriggerAND) { triggered &= retval; + } } /** return **/ diff --git a/Generators/src/GeneratorExternalParam.cxx b/Generators/src/GeneratorExternalParam.cxx new file mode 100644 index 0000000000000..dbed9392540a4 --- /dev/null +++ b/Generators/src/GeneratorExternalParam.cxx @@ -0,0 +1,14 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 R+Preghenella - September 2020 + +#include "Generators/GeneratorExternalParam.h" +O2ParamImpl(o2::eventgen::GeneratorExternalParam); diff --git a/Generators/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index d532ff0022896..798d97f567f1c 100644 --- a/Generators/src/GeneratorFactory.cxx +++ b/Generators/src/GeneratorFactory.cxx @@ -26,13 +26,19 @@ #include <Generators/GeneratorPythia8Param.h> #endif #include <Generators/GeneratorTGenerator.h> +#include <Generators/GeneratorExternalParam.h> #ifdef GENERATORS_WITH_HEPMC3 #include <Generators/GeneratorHepMC.h> +#include <Generators/GeneratorHepMCParam.h> #endif #include <Generators/BoxGunParam.h> +#include <Generators/PDG.h> #include <Generators/TriggerParticle.h> +#include <Generators/TriggerExternalParam.h> #include <Generators/TriggerParticleParam.h> -#include "Generators/ConfigurationMacroHelper.h" +#include "CommonUtils/ConfigurationMacroHelper.h" + +#include "TRandom.h" namespace o2 { @@ -62,18 +68,17 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair auto gen = new o2::eventgen::GeneratorPythia8(); LOG(INFO) << "Reading \'Pythia8\' base configuration: " << config << std::endl; gen->readFile(config); - auto& param = GeneratorPythia8Param::Instance(); - LOG(INFO) << "Init \'Pythia8\' generator with following parameters"; - LOG(INFO) << param; - gen->setConfig(param.config); - gen->setHooksFileName(param.hooksFileName); - gen->setHooksFuncName(param.hooksFuncName); + auto seed = (gRandom->GetSeed() % 900000000); + LOG(INFO) << "Using random seed from gRandom % 900000000: " << seed; + gen->readString("Random:setSeed on"); + gen->readString("Random:seed " + std::to_string(seed)); return gen; }; #endif /** generators **/ + o2::PDG::addParticlesToPdgDataBase(); auto genconfig = conf.getGenerator(); if (genconfig.compare("boxgen") == 0) { // a simple "box" generator configurable via BoxGunparam @@ -133,12 +138,21 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair extGen->SetStartEvent(conf.getStartEvent()); primGen->AddGenerator(extGen); LOG(INFO) << "using external kinematics"; + } else if (genconfig.compare("extkinO2") == 0) { + // external kinematics from previous O2 output + auto extGen = new o2::eventgen::GeneratorFromO2Kine(conf.getExtKinematicsFileName().c_str()); + extGen->SetStartEvent(conf.getStartEvent()); + primGen->AddGenerator(extGen); + LOG(INFO) << "using external O2 kinematics"; #ifdef GENERATORS_WITH_HEPMC3 } else if (genconfig.compare("hepmc") == 0) { // external HepMC file + auto& param = GeneratorHepMCParam::Instance(); + LOG(INFO) << "Init \'GeneratorHepMC\' with following parameters"; + LOG(INFO) << param; auto hepmcGen = new o2::eventgen::GeneratorHepMC(); - hepmcGen->setFileName(conf.getHepMCFileName()); - hepmcGen->setVersion(2); + hepmcGen->setFileName(param.fileName); + hepmcGen->setVersion(param.version); primGen->AddGenerator(hepmcGen); #endif #ifdef GENERATORS_WITH_PYTHIA6 @@ -186,11 +200,14 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair auto py8 = makePythia8Gen(py8config); primGen->AddGenerator(py8); #endif - } else if (genconfig.compare("extgen") == 0) { + } else if (genconfig.compare("external") == 0 || genconfig.compare("extgen") == 0) { // external generator via configuration macro - auto extgen_filename = conf.getExtGeneratorFileName(); - auto extgen_func = conf.getExtGeneratorFuncName(); - auto extgen = GetFromMacro<FairGenerator*>(extgen_filename, extgen_func, "FairGenerator*", "extgen"); + auto& params = GeneratorExternalParam::Instance(); + LOG(INFO) << "Setting up external generator with following parameters"; + LOG(INFO) << params; + auto extgen_filename = params.fileName; + auto extgen_func = params.funcName; + auto extgen = o2::conf::GetFromMacro<FairGenerator*>(extgen_filename, extgen_func, "FairGenerator*", "extgen"); if (!extgen) { LOG(FATAL) << "Failed to retrieve \'extgen\': problem with configuration "; } @@ -223,12 +240,15 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair trigger = TriggerParticle(TriggerParticleParam::Instance()); } else if (trgconfig.compare("external") == 0) { // external trigger via configuration macro - auto external_trigger_filename = conf.getExtTriggerFileName(); - auto external_trigger_func = conf.getExtTriggerFuncName(); - trigger = GetFromMacro<o2::eventgen::Trigger>(external_trigger_filename, external_trigger_func, "o2::eventgen::Trigger", "trigger"); + auto& params = TriggerExternalParam::Instance(); + LOG(INFO) << "Setting up external trigger with following parameters"; + LOG(INFO) << params; + auto external_trigger_filename = params.fileName; + auto external_trigger_func = params.funcName; + trigger = o2::conf::GetFromMacro<o2::eventgen::Trigger>(external_trigger_filename, external_trigger_func, "o2::eventgen::Trigger", "trigger"); if (!trigger) { LOG(INFO) << "Trying to retrieve a \'o2::eventgen::DeepTrigger\' type" << std::endl; - deeptrigger = GetFromMacro<o2::eventgen::DeepTrigger>(external_trigger_filename, external_trigger_func, "o2::eventgen::DeepTrigger", "deeptrigger"); + deeptrigger = o2::conf::GetFromMacro<o2::eventgen::DeepTrigger>(external_trigger_filename, external_trigger_func, "o2::eventgen::DeepTrigger", "deeptrigger"); } if (!trigger && !deeptrigger) { LOG(FATAL) << "Failed to retrieve \'external trigger\': problem with configuration "; @@ -246,10 +266,12 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair return; } generator->setTriggerMode(o2::eventgen::Generator::kTriggerOR); - if (trigger) + if (trigger) { generator->addTrigger(trigger); - if (deeptrigger) + } + if (deeptrigger) { generator->addDeepTrigger(deeptrigger); + } } } diff --git a/Generators/src/GeneratorFromFile.cxx b/Generators/src/GeneratorFromFile.cxx index 1838438aca3fb..afbe1ad109dc6 100644 --- a/Generators/src/GeneratorFromFile.cxx +++ b/Generators/src/GeneratorFromFile.cxx @@ -9,11 +9,13 @@ // or submit itself to any jurisdiction. #include "Generators/GeneratorFromFile.h" +#include "SimulationDataFormat/MCTrack.h" #include <FairLogger.h> #include <FairPrimaryGenerator.h> #include <TBranch.h> #include <TClonesArray.h> #include <TFile.h> +#include <TMCProcess.h> #include <TParticle.h> #include <TTree.h> #include <sstream> @@ -40,8 +42,9 @@ GeneratorFromFile::GeneratorFromFile(const char* name) // std::cout << "probing for " << eventstring << "\n"; object = mEventFile->Get(eventstringstr.str().c_str()); // std::cout << "got " << object << "\n"; - if (object != nullptr) + if (object != nullptr) { mEventsAvailable++; + } } while (object != nullptr); LOG(INFO) << "Found " << mEventsAvailable << " events in this file \n"; } @@ -109,7 +112,7 @@ Bool_t GeneratorFromFile::ReadEvent(FairPrimaryGenerator* primGen) auto isFirstTrackableDescendant = [](TParticle const& p) { const int kTransportBit = BIT(14); // The particle should have not set kDone bit and its status should not exceed 1 - if (p.GetUniqueID() > 0 || !p.TestBit(kTransportBit)) { + if ((p.GetUniqueID() > 0 && p.GetUniqueID() != kPNoProcess) || !p.TestBit(kTransportBit)) { return false; } return true; @@ -152,7 +155,93 @@ Bool_t GeneratorFromFile::ReadEvent(FairPrimaryGenerator* primGen) return kFALSE; } +// based on O2 kinematics + +GeneratorFromO2Kine::GeneratorFromO2Kine(const char* name) +{ + mEventFile = TFile::Open(name); + if (mEventFile == nullptr) { + LOG(FATAL) << "EventFile " << name << " not found"; + return; + } + // the kinematics will be stored inside a branch MCTrack + // different events are stored inside different entries + auto tree = (TTree*)mEventFile->Get("o2sim"); + if (tree) { + mEventBranch = tree->GetBranch("MCTrack"); + if (mEventBranch) { + mEventsAvailable = mEventBranch->GetEntries(); + LOG(INFO) << "Found " << mEventsAvailable << " events in this file"; + return; + } + } + LOG(ERROR) << "Problem reading events from file " << name; +} + +void GeneratorFromO2Kine::SetStartEvent(int start) +{ + if (start < mEventsAvailable) { + mEventCounter = start; + } else { + LOG(ERROR) << "start event bigger than available events\n"; + } +} + +bool GeneratorFromO2Kine::importParticles() +{ + // NOTE: This should be usable with kinematics files without secondaries + // It might need some adjustment to make it work with secondaries or to continue + // from a kinematics snapshot + + if (mEventCounter < mEventsAvailable) { + int particlecounter = 0; + + std::vector<o2::MCTrack>* tracks = nullptr; + mEventBranch->SetAddress(&tracks); + mEventBranch->GetEntry(mEventCounter); + + for (auto& t : *tracks) { + // I guess we only want primaries (unless later on we continue a simulation) + if (!t.isPrimary()) { + continue; + } + + auto pdg = t.GetPdgCode(); + auto px = t.Px(); + auto py = t.Py(); + auto pz = t.Pz(); + auto vx = t.Vx(); + auto vy = t.Vy(); + auto vz = t.Vz(); + auto m1 = t.getMotherTrackId(); + auto m2 = t.getSecondMotherTrackId(); + auto d1 = t.getFirstDaughterTrackId(); + auto d2 = t.getLastDaughterTrackId(); + auto e = t.GetEnergy(); + auto vt = t.T(); + auto weight = 1.; // p.GetWeight() ?? + auto wanttracking = t.getToBeDone(); + LOG(DEBUG) << "Putting primary " << pdg; + + mParticles.push_back(TParticle(pdg, wanttracking, m1, m2, d1, d2, px, py, pz, e, vx, vy, vz, vt)); + particlecounter++; + } + mEventCounter++; + + if (tracks) { + delete tracks; + } + + LOG(INFO) << "Event generator put " << particlecounter << " on stack"; + return true; + } else { + LOG(ERROR) << "GeneratorFromO2Kine: Ran out of events\n"; + } + return false; +} + } // namespace eventgen } // end namespace o2 ClassImp(o2::eventgen::GeneratorFromFile); +ClassImp(o2::eventgen::GeneratorFromO2Kine); diff --git a/Generators/src/GeneratorHepMC.cxx b/Generators/src/GeneratorHepMC.cxx index e738f4dc18d99..ac0b76c4e666c 100644 --- a/Generators/src/GeneratorHepMC.cxx +++ b/Generators/src/GeneratorHepMC.cxx @@ -11,21 +11,15 @@ /// \author R+Preghenella - August 2017 #include "Generators/GeneratorHepMC.h" -#include "HepMC/ReaderAscii.h" -#include "HepMC/ReaderAsciiHepMC2.h" -#include "HepMC/GenEvent.h" -#include "HepMC/GenParticle.h" -#include "HepMC/GenVertex.h" -#include "HepMC/FourVector.h" +#include "Generators/GeneratorHepMCParam.h" +#include "HepMC3/ReaderAscii.h" +#include "HepMC3/ReaderAsciiHepMC2.h" +#include "HepMC3/GenEvent.h" +#include "HepMC3/GenParticle.h" +#include "HepMC3/GenVertex.h" +#include "HepMC3/FourVector.h" #include "TParticle.h" - -/** - HepMC/Errors.h of HepMC3 defines DEBUG as a logging macro, and this interferes with FairLogger. - Undefining it for the time being, while thinking about a possible solution for this issue. -**/ -#ifdef DEBUG -#undef DEBUG -#endif +#include "TSystem.h" #include "FairLogger.h" #include "FairPrimaryGenerator.h" @@ -44,7 +38,7 @@ GeneratorHepMC::GeneratorHepMC() { /** default constructor **/ - mEvent = new HepMC::GenEvent(); + mEvent = new HepMC3::GenEvent(); mInterface = reinterpret_cast<void*>(mEvent); mInterfaceName = "hepmc"; } @@ -56,7 +50,7 @@ GeneratorHepMC::GeneratorHepMC(const Char_t* name, const Char_t* title) { /** constructor **/ - mEvent = new HepMC::GenEvent(); + mEvent = new HepMC3::GenEvent(); mInterface = reinterpret_cast<void*>(mEvent); mInterfaceName = "hepmc"; } @@ -67,14 +61,16 @@ GeneratorHepMC::~GeneratorHepMC() { /** default destructor **/ - if (mStream.is_open()) + if (mStream.is_open()) { mStream.close(); + } if (mReader) { mReader->close(); delete mReader; } - if (mEvent) + if (mEvent) { delete mEvent; + } } /*****************************************************************/ @@ -86,10 +82,11 @@ Bool_t GeneratorHepMC::generateEvent() /** clear and read event **/ mEvent->clear(); mReader->read_event(*mEvent); - if (mReader->failed()) + if (mReader->failed()) { return kFALSE; + } /** set units to desired output **/ - mEvent->set_units(HepMC::Units::GEV, HepMC::Units::MM); + mEvent->set_units(HepMC3::Units::GEV, HepMC3::Units::MM); /** success **/ return kTRUE; @@ -153,9 +150,10 @@ Bool_t GeneratorHepMC::Init() Generator::Init(); /** open file **/ - mStream.open(mFileName); + std::string filename = gSystem->ExpandPathName(mFileName.c_str()); + mStream.open(filename); if (!mStream.is_open()) { - LOG(FATAL) << "Cannot open input file: " << mFileName << std::endl; + LOG(FATAL) << "Cannot open input file: " << filename << std::endl; return kFALSE; } @@ -163,10 +161,10 @@ Bool_t GeneratorHepMC::Init() switch (mVersion) { case 2: mStream.close(); - mReader = new HepMC::ReaderAsciiHepMC2(mFileName); + mReader = new HepMC3::ReaderAsciiHepMC2(filename); break; case 3: - mReader = new HepMC::ReaderAscii(mStream); + mReader = new HepMC3::ReaderAscii(mStream); break; default: LOG(FATAL) << "Unsupported HepMC version: " << mVersion << std::endl; diff --git a/Generators/src/GeneratorHepMCParam.cxx b/Generators/src/GeneratorHepMCParam.cxx new file mode 100644 index 0000000000000..cf838412e3bc5 --- /dev/null +++ b/Generators/src/GeneratorHepMCParam.cxx @@ -0,0 +1,14 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 R+Preghenella - January 2020 + +#include "Generators/GeneratorHepMCParam.h" +O2ParamImpl(o2::eventgen::GeneratorHepMCParam); diff --git a/Generators/src/GeneratorPythia6.cxx b/Generators/src/GeneratorPythia6.cxx index 3473acf87a957..470d1ecb89e5b 100644 --- a/Generators/src/GeneratorPythia6.cxx +++ b/Generators/src/GeneratorPythia6.cxx @@ -69,18 +69,21 @@ Bool_t GeneratorPythia6::Init() for (std::string line; getline(fin, line);) { /** remove comments **/ line = line.substr(0, line.find_first_of(comment)); - if (line.size() == 0) + if (line.size() == 0) { continue; + } /** remove leading/trailing whitespaces **/ const auto line_begin = line.find_first_not_of(whitespace); const auto line_end = line.find_last_not_of(whitespace); if (line_begin == std::string::npos || - line_end == std::string::npos) + line_end == std::string::npos) { continue; + } const auto line_range = line_end - line_begin + 1; line = line.substr(line_begin, line_range); - if (line.size() == 0) + if (line.size() == 0) { continue; + } /** process command **/ readString(line.c_str()); } diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index 578b09a0839f5..54a4a4e6fd0d8 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -11,11 +11,15 @@ /// \author R+Preghenella - January 2020 #include "Generators/GeneratorPythia8.h" -#include "Generators/ConfigurationMacroHelper.h" +#include "Generators/GeneratorPythia8Param.h" +#include "CommonUtils/ConfigurationMacroHelper.h" #include "FairLogger.h" #include "TParticle.h" -#include "FairMCEventHeader.h" +#include "SimulationDataFormat/MCEventHeader.h" #include "Pythia8/HIUserHooks.h" +#include "TSystem.h" + +#include <iostream> namespace o2 { @@ -31,6 +35,14 @@ GeneratorPythia8::GeneratorPythia8() : Generator("ALICEo2", "ALICEo2 Pythia8 Gen mInterface = reinterpret_cast<void*>(&mPythia); mInterfaceName = "pythia8"; + + auto& param = GeneratorPythia8Param::Instance(); + LOG(INFO) << "Instance \'Pythia8\' generator with following parameters"; + LOG(INFO) << param; + + setConfig(param.config); + setHooksFileName(param.hooksFileName); + setHooksFuncName(param.hooksFuncName); } /*****************************************************************/ @@ -54,25 +66,28 @@ Bool_t GeneratorPythia8::Init() /** read configuration **/ if (!mConfig.empty()) { - if (!mPythia.readFile(mConfig, true)) { - LOG(FATAL) << "Failed to init \'Pythia8\': problems with configuration file " - << mConfig; - return false; + std::stringstream ss(mConfig); + std::string config; + while (getline(ss, config, ' ')) { + config = gSystem->ExpandPathName(config.c_str()); + LOG(INFO) << "Reading configuration from file: " << config; + if (!mPythia.readFile(config, true)) { + LOG(FATAL) << "Failed to init \'Pythia8\': problems with configuration file " + << config; + return false; + } } } /** user hooks via configuration macro **/ if (!mHooksFileName.empty()) { - auto hooks = GetFromMacro<Pythia8::UserHooks*>(mHooksFileName, mHooksFuncName, "Pythia8::UserHooks*", "pythia8_user_hooks"); + LOG(INFO) << "Applying \'Pythia8\' user hooks: " << mHooksFileName << " -> " << mHooksFuncName; + auto hooks = o2::conf::GetFromMacro<Pythia8::UserHooks*>(mHooksFileName, mHooksFuncName, "Pythia8::UserHooks*", "pythia8_user_hooks"); if (!hooks) { LOG(FATAL) << "Failed to init \'Pythia8\': problem with user hooks configuration "; return false; } -#if PYTHIA_VERSION_INTEGER < 8300 - mPythia.setUserHooksPtr(hooks); -#else - mPythia.setUserHooksPtr(std::shared_ptr<Pythia8::UserHooks>(hooks)); -#endif + setUserHooks(hooks); } #if PYTHIA_VERSION_INTEGER < 8300 @@ -105,8 +120,9 @@ Bool_t /** generate event **/ /** generate event **/ - if (!mPythia.next()) + if (!mPythia.next()) { return false; + } #if PYTHIA_VERSION_INTEGER < 8300 /** [NOTE] The issue with large particle production vertex when running @@ -144,15 +160,15 @@ Bool_t /*****************************************************************/ Bool_t - GeneratorPythia8::importParticles() + GeneratorPythia8::importParticles(Pythia8::Event& event) { /** import particles **/ /* loop over particles */ // auto weight = mPythia.info.weight(); // TBD: use weights - auto nParticles = mPythia.event.size(); - for (Int_t iparticle = 0; iparticle < nParticles; iparticle++) { // first particle is system - auto particle = mPythia.event[iparticle]; + auto nParticles = event.size(); + for (Int_t iparticle = 1; iparticle < nParticles; iparticle++) { // first particle is system + auto particle = event[iparticle]; auto pdg = particle.id(); auto st = particle.statusHepMC(); auto px = particle.px(); @@ -163,10 +179,10 @@ Bool_t auto vy = particle.yProd(); auto vz = particle.zProd(); auto vt = particle.tProd(); - auto m1 = particle.mother1(); - auto m2 = particle.mother2(); - auto d1 = particle.daughter1(); - auto d2 = particle.daughter2(); + auto m1 = particle.mother1() - 1; + auto m2 = particle.mother2() - 1; + auto d1 = particle.daughter1() - 1; + auto d2 = particle.daughter2() - 1; mParticles.push_back(TParticle(pdg, st, m1, m2, d1, d2, px, py, pz, et, vx, vy, vz, vt)); } @@ -176,19 +192,232 @@ Bool_t /*****************************************************************/ -void GeneratorPythia8::updateHeader(FairMCEventHeader* eventHeader) +void GeneratorPythia8::updateHeader(o2::dataformats::MCEventHeader* eventHeader) { /** update header **/ + eventHeader->putInfo<std::string>("generator", "pythia8"); + eventHeader->putInfo<int>("version", PYTHIA_VERSION_INTEGER); + #if PYTHIA_VERSION_INTEGER < 8300 auto hiinfo = mPythia.info.hiinfo; #else auto hiinfo = mPythia.info.hiInfo; #endif - /** set impact parameter if in heavy-ion mode **/ - if (hiinfo) + if (hiinfo) { + /** set impact parameter **/ eventHeader->SetB(hiinfo->b()); + eventHeader->putInfo<double>("Bimpact", hiinfo->b()); + /** set Ncoll, Npart and Nremn **/ + int nColl, nPart; + int nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg; + int nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg; + getNcoll(nColl); + getNpart(nPart); + getNpart(nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg); + getNremn(nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg); + eventHeader->putInfo<int>("Ncoll", nColl); + eventHeader->putInfo<int>("Npart", nPart); + eventHeader->putInfo<int>("Npart_proj_p", nPartProtonProj); + eventHeader->putInfo<int>("Npart_proj_n", nPartNeutronProj); + eventHeader->putInfo<int>("Npart_targ_p", nPartProtonTarg); + eventHeader->putInfo<int>("Npart_targ_n", nPartNeutronTarg); + eventHeader->putInfo<int>("Nremn_proj_p", nRemnProtonProj); + eventHeader->putInfo<int>("Nremn_proj_n", nRemnNeutronProj); + eventHeader->putInfo<int>("Nremn_targ_p", nRemnProtonTarg); + eventHeader->putInfo<int>("Nremn_targ_n", nRemnNeutronTarg); + } +} + +/*****************************************************************/ + +void GeneratorPythia8::selectFromAncestor(int ancestor, Pythia8::Event& inputEvent, Pythia8::Event& outputEvent) +{ + + /** select from ancestor + fills the output event with all particles related to + an ancestor of the input event **/ + + // recursive selection via lambda function + std::set<int> selected; + std::function<void(int)> select; + select = [&](int i) { + selected.insert(i); + auto dl = inputEvent[i].daughterList(); + for (auto j : dl) { + select(j); + } + }; + select(ancestor); + + // map selected particle index to output index + std::map<int, int> indexMap; + int index = outputEvent.size(); + for (auto i : selected) { + indexMap[i] = index++; + } + + // adjust mother/daughter indices and append to output event + for (auto i : selected) { + auto p = mPythia.event[i]; + auto m1 = indexMap[p.mother1()]; + auto m2 = indexMap[p.mother2()]; + auto d1 = indexMap[p.daughter1()]; + auto d2 = indexMap[p.daughter2()]; + p.mothers(m1, m2); + p.daughters(d1, d2); + + outputEvent.append(p); + } +} + +/*****************************************************************/ + +void GeneratorPythia8::getNcoll(const Pythia8::Info& info, int& nColl) +{ + + /** compute number of collisions from sub-collision information **/ + +#if PYTHIA_VERSION_INTEGER < 8300 + auto hiinfo = info.hiinfo; +#else + auto hiinfo = info.hiInfo; +#endif + + nColl = 0; + + if (!hiinfo) { + return; + } + + // loop over sub-collisions + auto scptr = hiinfo->subCollisionsPtr(); + for (auto sc : *scptr) { + + // wounded nucleon flag in projectile/target + auto pW = sc.proj->status() == Pythia8::Nucleon::ABS; // according to C.Bierlich this should be == Nucleon::ABS + auto tW = sc.targ->status() == Pythia8::Nucleon::ABS; + + // increase number of collisions if both are wounded + if (pW && tW) { + nColl++; + } + } +} + +/*****************************************************************/ + +void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nPart) +{ + + /** compute number of participants as the sum of all participants nucleons **/ + + int nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg; + getNpart(info, nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg); + nPart = nProtonProj + nNeutronProj + nProtonTarg + nNeutronTarg; +} + +/*****************************************************************/ + +void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg) +{ + + /** compute number of participants from sub-collision information **/ + +#if PYTHIA_VERSION_INTEGER < 8300 + auto hiinfo = info.hiinfo; +#else + auto hiinfo = info.hiInfo; +#endif + + nProtonProj = nNeutronProj = nProtonTarg = nNeutronTarg = 0; + if (!hiinfo) { + return; + } + + // keep track of wounded nucleons + std::vector<Pythia8::Nucleon*> projW; + std::vector<Pythia8::Nucleon*> targW; + + // loop over sub-collisions + auto scptr = hiinfo->subCollisionsPtr(); + for (auto sc : *scptr) { + + // wounded nucleon flag in projectile/target + auto pW = sc.proj->status() == Pythia8::Nucleon::ABS || sc.proj->status() == Pythia8::Nucleon::DIFF; // according to C.Bierlich this should be == Nucleon::ABS || Nucleon::DIFF + auto tW = sc.targ->status() == Pythia8::Nucleon::ABS || sc.targ->status() == Pythia8::Nucleon::DIFF; + + // increase number of wounded projectile nucleons if not yet in the wounded vector + if (pW && std::find(projW.begin(), projW.end(), sc.proj) == projW.end()) { + projW.push_back(sc.proj); + if (sc.proj->id() == 2212) { + nProtonProj++; + } else if (sc.proj->id() == 2112) { + nNeutronProj++; + } + } + + // increase number of wounded target nucleons if not yet in the wounded vector + if (tW && std::find(targW.begin(), targW.end(), sc.targ) == targW.end()) { + targW.push_back(sc.targ); + if (sc.targ->id() == 2212) { + nProtonTarg++; + } else if (sc.targ->id() == 2112) { + nNeutronTarg++; + } + } + } +} + +/*****************************************************************/ + +void GeneratorPythia8::getNremn(const Pythia8::Event& event, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg) +{ + + /** compute number of spectators from the nuclear remnant of the beams **/ + + // reset + nProtonProj = nNeutronProj = nProtonTarg = nNeutronTarg = 0; + auto nNucRem = 0; + + // particle loop + auto nparticles = event.size(); + for (int ipa = 0; ipa < nparticles; ++ipa) { + const auto particle = event[ipa]; + auto pdg = particle.id(); + + // nuclear remnants have pdg code = ±10LZZZAAA9 + if (pdg < 1000000000) { + continue; // must be nucleus + } + if (pdg % 10 != 9) { + continue; // first digit must be 9 + } + nNucRem++; + + // extract A, Z and L from pdg code + pdg /= 10; + auto A = pdg % 1000; + pdg /= 1000; + auto Z = pdg % 1000; + pdg /= 1000; + auto L = pdg % 10; + + if (particle.pz() > 0.) { + nProtonProj = Z; + nNeutronProj = A - Z; + } + if (particle.pz() < 0.) { + nProtonTarg = Z; + nNeutronTarg = A - Z; + } + + } // end of particle loop + + if (nNucRem > 2) { + LOG(WARNING) << " GeneratorPythia8: found more than two nuclear remnants (weird)"; + } } /*****************************************************************/ diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index b4ed8bec7610d..646e8d0576256 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -27,8 +27,10 @@ #pragma link C++ class o2::eventgen::Generator + ; #pragma link C++ class o2::eventgen::GeneratorTGenerator + ; +#pragma link C++ class o2::eventgen::GeneratorExternalParam + ; #ifdef GENERATORS_WITH_HEPMC3 #pragma link C++ class o2::eventgen::GeneratorHepMC + ; +#pragma link C++ class o2::eventgen::GeneratorHepMCParam + ; #endif #ifdef GENERATORS_WITH_PYTHIA6 #pragma link C++ class o2::eventgen::GeneratorPythia6 + ; @@ -45,17 +47,22 @@ #pragma link C++ class o2::eventgen::GeneratorFactory + ; #endif #pragma link C++ class o2::eventgen::GeneratorFromFile + ; +#pragma link C++ class o2::eventgen::GeneratorFromO2Kine + ; #pragma link C++ class o2::PDG + ; #pragma link C++ class o2::eventgen::PrimaryGenerator + ; #pragma link C++ enum o2::eventgen::EVertexDistribution; #pragma link C++ class o2::eventgen::InteractionDiamondParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::InteractionDiamondParam > +; +#pragma link C++ class o2::eventgen::TriggerExternalParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::TriggerExternalParam> + ; #pragma link C++ class o2::eventgen::TriggerParticleParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::TriggerParticleParam > +; #pragma link C++ class o2::eventgen::BoxGunParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::BoxGunParam > +; #pragma link C++ class o2::eventgen::QEDGenParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::QEDGenParam > +; +#pragma link C++ class o2::eventgen::GenCosmicsParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GenCosmicsParam> + ; #endif diff --git a/Generators/src/PrimaryGenerator.cxx b/Generators/src/PrimaryGenerator.cxx index cb372a102f19d..205a65355d51d 100644 --- a/Generators/src/PrimaryGenerator.cxx +++ b/Generators/src/PrimaryGenerator.cxx @@ -14,6 +14,7 @@ #include "Generators/Generator.h" #include "Generators/InteractionDiamondParam.h" #include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/Stack.h" #include "FairLogger.h" #include "FairGenericStack.h" @@ -40,8 +41,9 @@ PrimaryGenerator::~PrimaryGenerator() mEmbedFile->Close(); delete mEmbedFile; } - if (mEmbedEvent) + if (mEmbedEvent) { delete mEmbedEvent; + } } /*****************************************************************/ @@ -76,8 +78,9 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) /** generate event **/ /** normal generation if no embedding **/ - if (!mEmbedTree) + if (!mEmbedTree) { return FairPrimaryGenerator::GenerateEvent(pStack); + } /** this is for embedding **/ @@ -89,13 +92,15 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) auto genList = GetListOfGenerators(); for (int igen = 0; igen < genList->GetEntries(); ++igen) { auto o2gen = dynamic_cast<Generator*>(genList->At(igen)); - if (o2gen) + if (o2gen) { o2gen->notifyEmbedding(mEmbedEvent); + } } /** generate event **/ - if (!FairPrimaryGenerator::GenerateEvent(pStack)) + if (!FairPrimaryGenerator::GenerateEvent(pStack)) { return kFALSE; + } /** add embedding info to event header **/ auto o2event = dynamic_cast<MCEventHeader*>(fEvent); @@ -116,7 +121,9 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) void PrimaryGenerator::AddTrack(Int_t pdgid, Double_t px, Double_t py, Double_t pz, Double_t vx, Double_t vy, Double_t vz, - Int_t parent, Bool_t wanttracking, + Int_t mother1, Int_t mother2, + Int_t daughter1, Int_t daughter2, + Bool_t wanttracking, Double_t e, Double_t tof, Double_t weight, TMCProcess proc) { @@ -146,9 +153,19 @@ void PrimaryGenerator::AddTrack(Int_t pdgid, Double_t px, Double_t py, Double_t Int_t ntr = 0; // Track number; to be filled by the stack Int_t status = 0; // Generation status - if (parent != -1) { - parent += fMCIndexOffset; - } // correct for tracks which are in list before generator is called + // correct for tracks which are in list before generator is called + if (mother1 != -1) { + mother1 += fMCIndexOffset; + } + if (mother2 != -1) { + mother2 += fMCIndexOffset; + } + if (daughter1 != -1) { + daughter1 += fMCIndexOffset; + } + if (daughter2 != -1) { + daughter2 += fMCIndexOffset; + } /** if it is a K0/antiK0 to be tracked, convert it into K0s/K0L. @@ -170,9 +187,14 @@ void PrimaryGenerator::AddTrack(Int_t pdgid, Double_t px, Double_t py, Double_t } /** add track to stack **/ - fStack->PushTrack(doTracking, parent, pdgid, px, py, pz, - e, vx, vy, vz, tof, polx, poly, polz, proc, ntr, - weight, status, parent); + auto stack = dynamic_cast<o2::data::Stack*>(fStack); + if (!stack) { + LOG(FATAL) << "Stack must be an o2::data:Stack"; + return; // must be the o2 stack + } + stack->PushTrack(doTracking, mother1, pdgid, px, py, pz, + e, vx, vy, vz, tof, polx, poly, polz, proc, ntr, + weight, status, mother2, daughter1, daughter2); fNTracks++; } diff --git a/Generators/src/Pythia6Generator.cxx b/Generators/src/Pythia6Generator.cxx deleted file mode 100644 index 3a84114b30c22..0000000000000 --- a/Generators/src/Pythia6Generator.cxx +++ /dev/null @@ -1,146 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence version 3 (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ -// ------------------------------------------------------------------------- -// ----- M. Al-Turany June 2014 ----- -// ------------------------------------------------------------------------- -// ------------------------------------------------------------------------- -// ----- Pythia6Generator source file ----- -// ----- Created 08/08/08 by S. Spataro ----- -// ------------------------------------------------------------------------- -#include "Generators/Pythia6Generator.h" - -#include "FairPrimaryGenerator.h" - -#include <iostream> -#include <cstdio> - -using std::cout; -using std::endl; -using std::max; -namespace o2 -{ -namespace eventgen -{ - -// ----- Default constructor ------------------------------------------ -Pythia6Generator::Pythia6Generator() = default; -// ------------------------------------------------------------------------ - -// ----- Standard constructor ----------------------------------------- -Pythia6Generator::Pythia6Generator(const char* fileName) - : mFileName(fileName), mInputFile(nullptr), mVerbose(0) -{ - cout << "-I Pythia6Generator: Opening input file " << mFileName << endl; - if ((mInputFile = fopen(mFileName, "r")) == nullptr) { - Fatal("Pythia6Generator", "Cannot open input file."); - } - - // mPDG=TDatabasePDG::Instance(); -} -// ------------------------------------------------------------------------ - -// ----- Destructor --------------------------------------------------- -Pythia6Generator::~Pythia6Generator() -{ - CloseInput(); -} -// ------------------------------------------------------------------------ - -// ----- Public method ReadEvent -------------------------------------- -Bool_t Pythia6Generator::ReadEvent(FairPrimaryGenerator* primGen) -{ - - // Check for input file - if (!mInputFile) { - // if ( ! mInputFile->is_open() ) { - cout << "-E Pythia6Generator: Input file not open!" << endl; - return kFALSE; - } - - // Define event variable to be read from file - Int_t ntracks = 0, eventID = 0, ncols = 0; - - // Define track variables to be read from file - Int_t nLev = 0, pdgID = 0, nM1 = -1, nM2 = -1, nDF = -1, nDL = -1; - Float_t fPx = 0., fPy = 0., fPz = 0., fM = 0., fE = 0.; - Float_t fVx = 0., fVy = 0., fVz = 0., fT = 0.; - - // Read event header line from input file - - Int_t max_nr = 0; - - Text_t buffer[200]; - ncols = fscanf(mInputFile, "%d\t%d", &eventID, &ntracks); - - if (ncols && ntracks > 0) { - - if (mVerbose > 0) - cout << "Event number: " << eventID << "\tNtracks: " << ntracks << endl; - - for (Int_t ll = 0; ll < ntracks; ll++) { - ncols = fscanf(mInputFile, "%d %d %d %d %d %d %f %f %f %f %f %f %f %f %f", &nLev, &pdgID, &nM1, &nM2, &nDF, &nDL, &fPx, &fPy, &fPz, &fE, &fM, &fVx, &fVy, &fVz, &fT); - if (mVerbose > 0) - cout << nLev << "\t" << pdgID << "\t" << nM1 << "\t" << nM2 << "\t" << nDF << "\t" << nDL << "\t" << fPx << "\t" << fPy << "\t" << fPz << "\t" << fE << "\t" << fM << "\t" << fVx << "\t" << fVy << "\t" << fVz << "\t" << fT << endl; - if (nLev == 1) - primGen->AddTrack(pdgID, fPx, fPy, fPz, fVx, fVy, fVz); - } - } else { - cout << "-I Pythia6Generator: End of input file reached " << endl; - CloseInput(); - return kFALSE; - } - - // If end of input file is reached : close it and abort run - if (feof(mInputFile)) { - cout << "-I Pythia6Generator: End of input file reached " << endl; - CloseInput(); - return kFALSE; - } - - /* - cout << "-I Pythia6Generator: Event " << eventID << ", vertex = (" - << vx << "," << vy << "," << vz << ") cm, multiplicity " - << ntracks << endl; - */ - - return kTRUE; -} -// ------------------------------------------------------------------------ - -// ----- Private method CloseInput ------------------------------------ -void Pythia6Generator::CloseInput() -{ - if (mInputFile) { - //if ( mInputFile->is_open() ) { - { - cout << "-I Pythia6Generator: Closing input file " - << mFileName << endl; - // mInputFile->close(); - - fclose(mInputFile); - } - delete mInputFile; - mInputFile = nullptr; - } -} -// ------------------------------------------------------------------------ - -} // namespace eventgen -} // namespace o2 - -ClassImp(o2::eventgen::Pythia6Generator); diff --git a/Generators/src/Pythia8Generator.cxx b/Generators/src/Pythia8Generator.cxx deleted file mode 100644 index 3e6f8a59990a1..0000000000000 --- a/Generators/src/Pythia8Generator.cxx +++ /dev/null @@ -1,196 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence version 3 (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ -// ------------------------------------------------------------------------- -// ----- M. Al-Turany June 2014 ----- -// ------------------------------------------------------------------------- - -#include <cmath> -#include "TROOT.h" -#include "Pythia8/Basics.h" // for RndmEngine -#include "Pythia8/Pythia.h" -#include "FairPrimaryGenerator.h" -#include "FairGenerator.h" -#include "TRandom.h" // for TRandom -#include "TRandom1.h" // for TRandom1 -#include "TRandom3.h" // for TRandom3, gRandom - -#include "Generators/Pythia8Generator.h" - -using namespace Pythia8; - -namespace o2 -{ -namespace eventgen -{ - -class PyTr1Rng : public RndmEngine -{ - public: - PyTr1Rng() { mRng = std::make_unique<TRandom1>(gRandom->GetSeed()); }; - ~PyTr1Rng() override = default; - - Double_t flat() override { return mRng->TRandom1::Rndm(); }; - - private: - std::unique_ptr<TRandom1> mRng; //! -}; - -class PyTr3Rng : public RndmEngine -{ - public: - PyTr3Rng() { mRng = std::make_unique<TRandom3>(gRandom->GetSeed()); }; - ~PyTr3Rng() override = default; - - Double_t flat() override { return mRng->TRandom3::Rndm(); }; - - private: - std::unique_ptr<TRandom3> mRng; //! -}; - -// ----- Default constructor ------------------------------------------- -Pythia8Generator::Pythia8Generator() -{ - mUseRandom1 = kFALSE; - mUseRandom3 = kTRUE; - mId = 2212; // proton - mMom = 400; // proton - mHNL = 0; // HNL if set to !=0, for example 9900014, only track - mPythia = std::make_unique<Pythia>(); -} -// ------------------------------------------------------------------------- - -// ----- Default constructor ------------------------------------------- -Bool_t Pythia8Generator::Init() -{ - if (mUseRandom1) { - mRandomEngine = std::make_unique<PyTr1Rng>(); - } - if (mUseRandom3) { - mRandomEngine = std::make_unique<PyTr3Rng>(); - } - mPythia->setRndmEnginePtr(mRandomEngine.get()); - - /** commenting these lines - as they would override external settings **/ - /** - cout<<"Beam Momentum "<<fMom<<endl; - // Set arguments in Settings database. - mPythia.settings.mode("Beams:idA", fId); - mPythia.settings.mode("Beams:idB", 2212); - mPythia.settings.mode("Beams:frameType", 3); - mPythia.settings.parm("Beams:pxA", 0.); - mPythia.settings.parm("Beams:pyA", 0.); - mPythia.settings.parm("Beams:pzA", fMom); - mPythia.settings.parm("Beams:pxB", 0.); - mPythia.settings.parm("Beams:pyB", 0.); - mPythia.settings.parm("Beams:pzB", 0.); - **/ - mPythia->init(); - return kTRUE; -} -// ------------------------------------------------------------------------- - -// ----- Destructor ---------------------------------------------------- -Pythia8Generator::~Pythia8Generator() = default; -// ------------------------------------------------------------------------- - -// ----- Passing the event --------------------------------------------- -Bool_t Pythia8Generator::ReadEvent(FairPrimaryGenerator* cpg) -{ - const double mm2cm = 0.1; - const double clight = 2.997924580e10; //cm/c - Int_t npart = 0; - while (npart == 0) { - mPythia->next(); - for (int i = 0; i < mPythia->event.size(); i++) { - if (mPythia->event[i].isFinal()) { - // only send HNL decay products to G4 - if (mHNL != 0) { - Int_t im = mPythia->event[i].mother1(); - if (mPythia->event[im].id() == mHNL) { - // for the moment, hardcode 110m is maximum decay length - Double_t z = mPythia->event[i].zProd(); - Double_t x = abs(mPythia->event[i].xProd()); - Double_t y = abs(mPythia->event[i].yProd()); - // cout<<"debug HNL decay pos "<<x<<" "<< y<<" "<< z <<endl; - if (z < 11000. && z > 7000. && x < 250. && y < 250.) { - npart++; - } - } - } else { - npart++; - } - }; - }; - // happens if a charm particle being produced which does decay without producing a HNL. Try another event. - // if (npart == 0){ mPythia->event.list();} - }; - // cout<<"debug p8 event 0 " << mPythia->event[0].id()<< " "<< mPythia->event[1].id()<< " " - // << mPythia->event[2].id()<< " "<< npart <<endl; - for (Int_t ii = 0; ii < mPythia->event.size(); ii++) { - Bool_t wanttracking = true; - if (!mPythia->event[ii].isFinal()) - wanttracking = false; - Double_t z = mPythia->event[ii].zProd(); - Double_t x = mPythia->event[ii].xProd(); - Double_t y = mPythia->event[ii].yProd(); - Double_t pz = mPythia->event[ii].pz(); - Double_t px = mPythia->event[ii].px(); - Double_t py = mPythia->event[ii].py(); - Double_t t = mPythia->event[ii].tProd(); - x *= mm2cm; - y *= mm2cm; - z *= mm2cm; - t *= mm2cm / clight; - Double_t e = -9e9; - // handle system entry separately - if (!wanttracking) - e = mPythia->event[ii].e(); - // - printf("AddTrack from Pythia8 %5d \n", (Int_t)mPythia->event[ii].id()); - cpg->AddTrack((Int_t)mPythia->event[ii].id(), px, py, pz, x, y, z, - (Int_t)mPythia->event[ii].mother1(), wanttracking, e, t); - } // particle lloop - - return kTRUE; -} -// ------------------------------------------------------------------------- -void Pythia8Generator::SetParameters(const char* par) -{ - // Set Parameters - mPythia->readString(par); - cout << R"(mPythia->readString(")" << par << R"("))" << endl; -} - -// ------------------------------------------------------------------------- -void Pythia8Generator::Print() -{ - mPythia->settings.listAll(); -} -// ------------------------------------------------------------------------- -void Pythia8Generator::GetPythiaInstance(int arg) -{ - mPythia->particleData.list(arg); - cout << "canDecay " << mPythia->particleData.canDecay(arg) << " " << mPythia->particleData.mayDecay(arg) << endl; -} -// ------------------------------------------------------------------------- - -} // namespace eventgen -} // namespace o2 - -ClassImp(o2::eventgen::Pythia8Generator); diff --git a/Generators/src/TriggerExternalParam.cxx b/Generators/src/TriggerExternalParam.cxx new file mode 100644 index 0000000000000..0c164bbe06c1d --- /dev/null +++ b/Generators/src/TriggerExternalParam.cxx @@ -0,0 +1,14 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 R+Preghenella - September 2020 + +#include "Generators/TriggerExternalParam.h" +O2ParamImpl(o2::eventgen::TriggerExternalParam); diff --git a/Generators/src/TriggerParticle.cxx b/Generators/src/TriggerParticle.cxx index ece35bb65b278..dca26f5874a80 100644 --- a/Generators/src/TriggerParticle.cxx +++ b/Generators/src/TriggerParticle.cxx @@ -25,16 +25,21 @@ Trigger TriggerParticle(const TriggerParticleParam& param) LOG(INFO) << param; return [¶m](const std::vector<TParticle>& particles) -> bool { for (const auto& particle : particles) { - if (particle.GetPdgCode() != param.pdg) + if (particle.GetPdgCode() != param.pdg) { continue; - if (particle.Pt() < param.ptMin || particle.Pt() > param.ptMax) + } + if (particle.Pt() < param.ptMin || particle.Pt() > param.ptMax) { continue; - if (particle.Eta() < param.etaMin || particle.Eta() > param.etaMax) + } + if (particle.Eta() < param.etaMin || particle.Eta() > param.etaMax) { continue; - if (particle.Phi() < param.phiMin || particle.Phi() > param.phiMax) + } + if (particle.Phi() < param.phiMin || particle.Phi() > param.phiMax) { continue; - if (particle.Y() < param.yMin || particle.Y() > param.yMax) + } + if (particle.Y() < param.yMin || particle.Y() > param.yMax) { continue; + } return true; /** trigger fired **/ } return false; /** trigger did not fire **/ diff --git a/README.md b/README.md index 880f581943956..b25c4f680e874 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ [![](http://ali-ci.cern.ch/repo/buildstatus/AliceO2Group/AliceO2/dev/build_O2_o2.svg)](https://ali-ci.cern.ch/repo/logs/AliceO2Group/AliceO2/dev/latest/build_O2_o2/fullLog.txt) [![](http://ali-ci.cern.ch/repo/buildstatus/AliceO2Group/AliceO2/dev/build_o2_macos.svg)](https://ali-ci.cern.ch/repo/logs/AliceO2Group/AliceO2/dev/latest/build_o2_macos/fullLog.txt) [![](http://ali-ci.cern.ch/repo/buildstatus/AliceO2Group/AliceO2/dev/build_o2checkcode_o2.svg)](https://ali-ci.cern.ch/repo/logs/AliceO2Group/AliceO2/dev/latest/build_o2checkcode_o2/fullLog.txt) -[![](http://ali-ci.cern.ch/repo/buildstatus/AliceO2Group/AliceO2/dev/build_O2_o2-dev-fairroot.svg)](https://ali-ci.cern.ch/repo/logs/AliceO2Group/AliceO2/dev/latest/build_O2_o2-dev-fairroot/fullLog.txt) <!-- /// \endcond --> diff --git a/Steer/DigitizerWorkflow/CMakeLists.txt b/Steer/DigitizerWorkflow/CMakeLists.txt index e24058089e4f9..ac32458d355b7 100644 --- a/Steer/DigitizerWorkflow/CMakeLists.txt +++ b/Steer/DigitizerWorkflow/CMakeLists.txt @@ -31,6 +31,7 @@ o2_add_executable(digitizer-workflow src/TOFDigitizerSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::Steer + O2::CommonConstants O2::EMCALSimulation O2::FT0Simulation O2::FV0Simulation @@ -54,3 +55,12 @@ o2_add_executable(digitizer-workflow O2::TRDWorkflow O2::DataFormatsTRD O2::ZDCSimulation) + + +o2_add_executable(mctruth-testworkflow + COMPONENT_NAME sim + SOURCES src/MCTruthTestWorkflow.cxx + src/MCTruthSourceSpec.cxx + src/MCTruthWriterSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::SimulationDataFormat) diff --git a/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.cxx index 488595cbc756e..2621c2355f512 100644 --- a/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.cxx @@ -81,8 +81,9 @@ void DigitizerSpec::retrieveHits(const char* brname, void DigitizerSpec::run(framework::ProcessingContext& pc) { - if (mFinished) + if (mFinished) { return; + } // read collision context from input auto context = pc.inputs().get<o2::steer::DigitizationContext*>("collisioncontext"); diff --git a/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.h b/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.h index ef1bc66fa7709..909fea0f6a316 100644 --- a/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/CPVDigitizerSpec.h @@ -30,7 +30,7 @@ namespace cpv /// \author Dmitri Peresunko, NRC "Kurchatov institute" /// \author Adopted from PHOS code /// \since Jan, 2020 -class DigitizerSpec : public o2::base::BaseDPLDigitizer +class DigitizerSpec final : public o2::base::BaseDPLDigitizer { public: /// \brief Constructor diff --git a/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.cxx index e111b2c4dedda..ab7478b66a16d 100644 --- a/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.cxx @@ -9,6 +9,7 @@ // or submit itself to any jurisdiction. #include "EMCALDigitizerSpec.h" +#include "CommonConstants/Triggers.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" #include "Framework/DataProcessorSpec.h" @@ -48,8 +49,9 @@ void DigitizerSpec::initDigitizerTask(framework::InitContext& ctx) void DigitizerSpec::run(framework::ProcessingContext& ctx) { - if (mFinished) + if (mFinished) { return; + } // read collision context from input auto context = ctx.inputs().get<o2::steer::DigitizationContext*>("collisioncontext"); @@ -58,8 +60,9 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) LOG(DEBUG) << "GOT " << timesview.size() << " COLLISSION TIMES"; // if there is nothing to do ... return - if (timesview.size() == 0) + if (timesview.size() == 0) { return; + } TStopwatch timer; timer.Start(); @@ -92,8 +95,9 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) mDigitizer.setEventTime(timesview[collID].getTimeNS()); - if (!mDigitizer.isLive()) + if (!mDigitizer.isLive()) { continue; + } if (mDigitizer.isEmpty()) { mDigitizer.initCycle(); @@ -125,7 +129,7 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(mAccumulatedDigits)); labelAccum.mergeAtBack(mLabels); LOG(INFO) << "Have " << mAccumulatedDigits.size() << " digits "; - triggers.emplace_back(timesview[trigID], indexStart, mDigits.size()); + triggers.emplace_back(timesview[trigID], o2::trigger::PhT, indexStart, mDigits.size()); indexStart = mAccumulatedDigits.size(); mDigits.clear(); mLabels.clear(); diff --git a/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.h b/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.h index 52e9ad30a72f2..6820ae102ae34 100644 --- a/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.h @@ -35,7 +35,7 @@ namespace emcal /// \author Anders Garritt Knospe <anders.knospe@cern.ch>, University of Houston /// \author Markus Fasel <markus.fasel@cern.ch> Oak Ridge National laboratory /// \since Nov 12, 2018 -class DigitizerSpec : public o2::base::BaseDPLDigitizer +class DigitizerSpec final : public o2::base::BaseDPLDigitizer { public: /// \brief Constructor diff --git a/Steer/DigitizerWorkflow/src/FDDDigitWriterSpec.h b/Steer/DigitizerWorkflow/src/FDDDigitWriterSpec.h index 7a007ad136bb6..7b4e31cbca772 100644 --- a/Steer/DigitizerWorkflow/src/FDDDigitWriterSpec.h +++ b/Steer/DigitizerWorkflow/src/FDDDigitWriterSpec.h @@ -16,7 +16,8 @@ #include "Framework/InputSpec.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" namespace o2 @@ -31,13 +32,49 @@ o2::framework::DataProcessorSpec getFDDDigitWriterSpec(bool mctruth = true) { using InputSpec = framework::InputSpec; using MakeRootTreeWriterSpec = framework::MakeRootTreeWriterSpec; + + // the callback to be set as hook for custom action when the writer is closed + auto finishWriting = [](TFile* outputfile, TTree* outputtree) { + const auto* brArr = outputtree->GetListOfBranches(); + int64_t nent = 0; + for (const auto* brc : *brArr) { + int64_t n = ((const TBranch*)brc)->GetEntries(); + if (nent && (nent != n)) { + LOG(ERROR) << "Branches have different number of entries"; + } + nent = n; + } + outputtree->SetEntries(nent); + outputtree->Write(); + outputfile->Close(); + }; + + // custom handler for labels: + // essentially transform the input container (as registered in the original branch definition) to the special output format for labels + auto customlabelhandler = [](TBranch& branch, std::vector<char> const& labeldata, framework::DataRef const& ref) { + o2::dataformats::ConstMCTruthContainerView<o2::fdd::MCLabel> labels(labeldata); + // make the actual output object by adopting/casting the buffer + // into a split format + o2::dataformats::IOMCTruthContainerView outputcontainer(labeldata); + auto br = framework::RootTreeWriter::remapBranch(branch, &outputcontainer); + br->Fill(); + br->ResetAddress(); + }; + + auto labelsdef = BranchDefinition<std::vector<char>>{InputSpec{"labelinput", "FDD", "DIGITLBL"}, + "FDDDigitLabels", "labels-branch-name", + // this branch definition is disabled if MC labels are not processed + (mctruth ? 1 : 0), + customlabelhandler}; + return MakeRootTreeWriterSpec("FDDDigitWriter", "fdddigits.root", "o2sim", 1, + MakeRootTreeWriterSpec::CustomClose(finishWriting), BranchDefinition<std::vector<o2::fdd::Digit>>{InputSpec{"digitBCinput", "FDD", "DIGITSBC"}, "FDDDigit"}, BranchDefinition<std::vector<o2::fdd::ChannelData>>{InputSpec{"digitChinput", "FDD", "DIGITSCH"}, "FDDDigitCh"}, - BranchDefinition<o2::dataformats::MCTruthContainer<o2::fdd::MCLabel>>{InputSpec{"labelinput", "FDD", "DIGITLBL"}, "FDDDigitLabels", mctruth ? 1 : 0})(); + std::move(labelsdef))(); } } // end namespace fdd diff --git a/Steer/DigitizerWorkflow/src/FDDDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/FDDDigitizerSpec.cxx index 51f08f5ab1205..4b72eeb32dbaa 100644 --- a/Steer/DigitizerWorkflow/src/FDDDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/FDDDigitizerSpec.cxx @@ -18,7 +18,7 @@ #include "Headers/DataHeader.h" #include "Steer/HitProcessingManager.h" // for DigitizationContext #include "DetectorsBase/BaseDPLDigitizer.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" #include "FDDSimulation/Digitizer.h" @@ -75,6 +75,7 @@ class FDDDPLDigitizerTask : public o2::base::BaseDPLDigitizer // loop over all composite collisions given from context // (aka loop over all the interaction records) std::vector<o2::fdd::Hit> hits; + o2::dataformats::MCTruthContainer<o2::fdd::MCLabel> labels; for (int collID = 0; collID < irecords.size(); ++collID) { @@ -90,20 +91,22 @@ class FDDDPLDigitizerTask : public o2::base::BaseDPLDigitizer mDigitizer.setEventID(part.entryID); mDigitizer.setSrcID(part.sourceID); - mDigitizer.process(hits, mDigitsBC, mDigitsCh, mLabels); + mDigitizer.process(hits, mDigitsBC, mDigitsCh, labels); } } o2::InteractionTimeRecord terminateIR; terminateIR.orbit = 0xffffffff; // supply IR in the infinite future to flush all cached BC mDigitizer.setInteractionRecord(terminateIR); - mDigitizer.flush(mDigitsBC, mDigitsCh, mLabels); + mDigitizer.flush(mDigitsBC, mDigitsCh, labels); // send out to next stage pc.outputs().snapshot(Output{"FDD", "DIGITSBC", 0, Lifetime::Timeframe}, mDigitsBC); pc.outputs().snapshot(Output{"FDD", "DIGITSCH", 0, Lifetime::Timeframe}, mDigitsCh); if (pc.outputs().isAllowed({"FDD", "DIGITLBL", 0})) { - pc.outputs().snapshot(Output{"FDD", "DIGITLBL", 0, Lifetime::Timeframe}, mLabels); + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::fdd::MCLabel>>(Output{"FDD", "DIGITLBL", 0, Lifetime::Timeframe}); + labels.flatten_to(sharedlabels); + labels.clear_andfreememory(); } LOG(INFO) << "FDD: Sending ROMode= " << mROMode << " to GRPUpdater"; @@ -120,7 +123,6 @@ class FDDDPLDigitizerTask : public o2::base::BaseDPLDigitizer std::vector<TChain*> mSimChains; std::vector<o2::fdd::ChannelData> mDigitsCh; std::vector<o2::fdd::Digit> mDigitsBC; - o2::dataformats::MCTruthContainer<o2::fdd::MCLabel> mLabels; // labels which get filled // RS: at the moment using hardcoded flag for continuous readout o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::CONTINUOUS; // readout mode diff --git a/Steer/DigitizerWorkflow/src/FT0DigitWriterSpec.h b/Steer/DigitizerWorkflow/src/FT0DigitWriterSpec.h index 8ac752f065083..35fd476eccfbf 100644 --- a/Steer/DigitizerWorkflow/src/FT0DigitWriterSpec.h +++ b/Steer/DigitizerWorkflow/src/FT0DigitWriterSpec.h @@ -39,10 +39,11 @@ o2::framework::DataProcessorSpec getFT0DigitWriterSpec(bool mctruth) 1, BranchDefinition<std::vector<o2::ft0::Digit>>{InputSpec{"digitBCinput", "FT0", "DIGITSBC"}, "FT0DIGITSBC"}, BranchDefinition<std::vector<o2::ft0::ChannelData>>{InputSpec{"digitChinput", "FT0", "DIGITSCH"}, "FT0DIGITSCH"}, + BranchDefinition<std::vector<o2::ft0::DetTrigInput>>{InputSpec{"digitTrinput", "FT0", "TRIGGERINPUT"}, "TRIGGERINPUT"}, BranchDefinition<o2::dataformats::MCTruthContainer<o2::ft0::MCLabel>>{InputSpec{"labelinput", "FT0", "DIGITSMCTR"}, "FT0DIGITSMCTR", mctruth ? 1 : 0})(); } } // namespace ft0 } // end namespace o2 -#endif /* STEER_DIGITIZERWORKFLOW_ITSMFTDIGITWRITER_H_ */ +#endif /* STEER_DIGITIZERWORKFLOW_FT0DIGITWRITER_H_ */ diff --git a/Steer/DigitizerWorkflow/src/FT0DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/FT0DigitizerSpec.cxx index 7f63d80c95333..e248bc8506cf4 100644 --- a/Steer/DigitizerWorkflow/src/FT0DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/FT0DigitizerSpec.cxx @@ -101,15 +101,16 @@ class FT0DPLDigitizerTask : public o2::base::BaseDPLDigitizer // call actual digitization procedure mDigitizer.setEventID(part.entryID); mDigitizer.setSrcID(part.sourceID); - mDigitizer.process(&hits, mDigitsBC, mDigitsCh, labels); + mDigitizer.process(&hits, mDigitsBC, mDigitsCh, mDigitsTrig, labels); } } } - mDigitizer.flush_all(mDigitsBC, mDigitsCh, labels); + mDigitizer.flush_all(mDigitsBC, mDigitsCh, mDigitsTrig, labels); // send out to next stage pc.outputs().snapshot(Output{"FT0", "DIGITSBC", 0, Lifetime::Timeframe}, mDigitsBC); pc.outputs().snapshot(Output{"FT0", "DIGITSCH", 0, Lifetime::Timeframe}, mDigitsCh); + pc.outputs().snapshot(Output{"FT0", "TRIGGERINPUT", 0, Lifetime::Timeframe}, mDigitsTrig); if (pc.outputs().isAllowed({"FT0", "DIGITSMCTR", 0})) { pc.outputs().snapshot(Output{"FT0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); } @@ -128,6 +129,7 @@ class FT0DPLDigitizerTask : public o2::base::BaseDPLDigitizer bool mFinished = false; std::vector<o2::ft0::ChannelData> mDigitsCh; std::vector<o2::ft0::Digit> mDigitsBC; + std::vector<o2::ft0::DetTrigInput> mDigitsTrig; Bool_t mContinuous = kFALSE; ///< flag to do continuous simulation double mFairTimeUnitInNS = 1; ///< Fair time unit in ns @@ -153,6 +155,7 @@ o2::framework::DataProcessorSpec getFT0DigitizerSpec(int channel, bool mctruth) std::vector<OutputSpec> outputs; outputs.emplace_back("FT0", "DIGITSBC", 0, Lifetime::Timeframe); outputs.emplace_back("FT0", "DIGITSCH", 0, Lifetime::Timeframe); + outputs.emplace_back("FT0", "TRIGGERINPUT", 0, Lifetime::Timeframe); if (mctruth) { outputs.emplace_back("FT0", "DIGITSMCTR", 0, Lifetime::Timeframe); } diff --git a/Steer/DigitizerWorkflow/src/FV0DigitWriterSpec.h b/Steer/DigitizerWorkflow/src/FV0DigitWriterSpec.h index 6e2474086277e..460392b544980 100644 --- a/Steer/DigitizerWorkflow/src/FV0DigitWriterSpec.h +++ b/Steer/DigitizerWorkflow/src/FV0DigitWriterSpec.h @@ -16,7 +16,7 @@ #include "Framework/InputSpec.h" #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/BCData.h" -#include "FV0Simulation/MCLabel.h" +#include "DataFormatsFV0/MCLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -38,10 +38,11 @@ o2::framework::DataProcessorSpec getFV0DigitWriterSpec(bool mctruth = true) 1, BranchDefinition<std::vector<o2::fv0::BCData>>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC"}, BranchDefinition<std::vector<o2::fv0::ChannelData>>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh"}, + BranchDefinition<std::vector<o2::fv0::DetTrigInput>>{InputSpec{"digitTrinput", "FV0", "TRIGGERINPUT"}, "TRIGGERINPUT"}, BranchDefinition<o2::dataformats::MCTruthContainer<o2::fv0::MCLabel>>{InputSpec{"labelinput", "FV0", "DIGITLBL"}, "FV0DigitLabels", mctruth ? 1 : 0})(); } } // namespace fv0 } // end namespace o2 -#endif /* STEER_DIGITIZERWORKFLOW_ITSMFTDIGITWRITER_H_ */ +#endif /* STEER_DIGITIZERWORKFLOW_FV0DIGITWRITER_H_ */ diff --git a/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx index b96aa8a5ef4ef..c009774cc7eed 100644 --- a/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx @@ -25,7 +25,7 @@ #include "DataFormatsParameters/GRPObject.h" #include "FV0Simulation/Digitizer.h" #include "FV0Simulation/DigitizationConstant.h" -#include "FV0Simulation/MCLabel.h" +#include "DataFormatsFV0/MCLabel.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DetectorsBase/BaseDPLDigitizer.h" #include <TFile.h> @@ -50,6 +50,7 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer { LOG(INFO) << "FV0DPLDigitizerTask:init"; mDigitizer.init(); + mDisableQED = ic.options().get<bool>("disable-qed"); //TODO: QED implementation to be tested } void run(framework::ProcessingContext& pc) @@ -62,11 +63,12 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer // read collision context from input auto context = pc.inputs().get<o2::steer::DigitizationContext*>("collisioncontext"); context->initSimChains(o2::detectors::DetID::FV0, mSimChains); + const bool withQED = context->isQEDProvided() && !mDisableQED; //TODO: QED implementation to be tested mDigitizer.setTimeStamp(context->getGRP().getTimeStart()); - auto& irecords = context->getEventRecords(); - auto& eventParts = context->getEventParts(); + auto& irecords = context->getEventRecords(withQED); //TODO: QED implementation to be tested + auto& eventParts = context->getEventParts(withQED); //TODO: QED implementation to be tested // loop over all composite collisions given from context // (aka loop over all the interaction records) @@ -85,18 +87,23 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer // call actual digitization procedure mDigitizer.setEventId(part.entryID); mDigitizer.setSrcId(part.sourceID); - mDigitizer.process(hits); + mDigitizer.process(hits, mDigitsBC, mDigitsCh, mDigitsTrig, mLabels); } - mDigitizer.analyseWaveformsAndStore(mDigitsBC, mDigitsCh, mLabels); LOG(INFO) << "[FV0] Has " << mDigitsBC.size() << " BC elements, " << mDigitsCh.size() << " mDigitsCh elements"; } + o2::InteractionTimeRecord terminateIR; + terminateIR.orbit = 0xffffffff; // supply IR in the infinite future to flush all cached BC + mDigitizer.setInteractionRecord(terminateIR); + mDigitizer.flush(mDigitsBC, mDigitsCh, mDigitsTrig, mLabels); + // here we have all digits and we can send them to consumer (aka snapshot it onto output) LOG(INFO) << "FV0: Sending " << mDigitsBC.size() << " digitsBC and " << mDigitsCh.size() << " digitsCh."; // send out to next stage pc.outputs().snapshot(Output{"FV0", "DIGITSBC", 0, Lifetime::Timeframe}, mDigitsBC); pc.outputs().snapshot(Output{"FV0", "DIGITSCH", 0, Lifetime::Timeframe}, mDigitsCh); + pc.outputs().snapshot(Output{"FV0", "TRIGGERINPUT", 0, Lifetime::Timeframe}, mDigitsTrig); if (pc.outputs().isAllowed({"FV0", "DIGITLBL", 0})) { pc.outputs().snapshot(Output{"FV0", "DIGITLBL", 0, Lifetime::Timeframe}, mLabels); } @@ -114,10 +121,12 @@ class FV0DPLDigitizerTask : public o2::base::BaseDPLDigitizer std::vector<TChain*> mSimChains; std::vector<o2::fv0::ChannelData> mDigitsCh; std::vector<o2::fv0::BCData> mDigitsBC; + std::vector<o2::fv0::DetTrigInput> mDigitsTrig; o2::dataformats::MCTruthContainer<o2::fv0::MCLabel> mLabels; // labels which get filled // RS: at the moment using hardcoded flag for continuous readout o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::CONTINUOUS; // readout mode + bool mDisableQED = false; }; o2::framework::DataProcessorSpec getFV0DigitizerSpec(int channel, bool mctruth) @@ -130,6 +139,7 @@ o2::framework::DataProcessorSpec getFV0DigitizerSpec(int channel, bool mctruth) std::vector<OutputSpec> outputs; outputs.emplace_back("FV0", "DIGITSBC", 0, Lifetime::Timeframe); outputs.emplace_back("FV0", "DIGITSCH", 0, Lifetime::Timeframe); + outputs.emplace_back("FV0", "TRIGGERINPUT", 0, Lifetime::Timeframe); if (mctruth) { outputs.emplace_back("FV0", "DIGITLBL", 0, Lifetime::Timeframe); } @@ -142,8 +152,9 @@ o2::framework::DataProcessorSpec getFV0DigitizerSpec(int channel, bool mctruth) outputs, AlgorithmSpec{adaptFromTask<FV0DPLDigitizerTask>()}, - - Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}}}; + Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}, + {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; + //Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}}}; } } // end namespace fv0 diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index 76b76824dc546..03c5af766a837 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -18,7 +18,7 @@ #include "Headers/DataHeader.h" #include "Steer/HitProcessingManager.h" // for DigitizationContext #include "DataFormatsITSMFT/Digit.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DetectorsBase/BaseDPLDigitizer.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" @@ -63,7 +63,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } else { geom = o2::mft::GeometryTGeo::Instance(); } - geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::L2G)); // make sure L2G matrices are loaded + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); // make sure L2G matrices are loaded mDigitizer.setGeometry(geom); mDisableQED = ic.options().get<bool>("disable-qed"); @@ -100,6 +100,51 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer mDigitizer.setROFRecords(&mROFRecords); mDigitizer.setMCLabels(&mLabels); + // digits are directly put into DPL owned resource + auto& digitsAccum = pc.outputs().make<std::vector<itsmft::Digit>>(Output{mOrigin, "DIGITS", 0, Lifetime::Timeframe}); + + auto accumulate = [this, &digitsAccum]() { + // accumulate result of single event processing, called after processing every event supplied + // AND after the final flushing via digitizer::fillOutputContainer + if (!mDigits.size()) { + return; // no digits were flushed, nothing to accumulate + } + static int fixMC2ROF = 0; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID + auto ndigAcc = digitsAccum.size(); + std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(digitsAccum)); + + // fix ROFrecords references on ROF entries + auto nROFRecsOld = mROFRecordsAccum.size(); + + for (int i = 0; i < mROFRecords.size(); i++) { + auto& rof = mROFRecords[i]; + rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); + rof.print(); + + if (mFixMC2ROF < mMC2ROFRecordsAccum.size()) { // fix ROFRecord entry in MC2ROF records + for (int m2rid = mFixMC2ROF; m2rid < mMC2ROFRecordsAccum.size(); m2rid++) { + // need to register the ROFRecors entry for MC event starting from this entry + auto& mc2rof = mMC2ROFRecordsAccum[m2rid]; + if (rof.getROFrame() == mc2rof.minROF) { + mFixMC2ROF++; + mc2rof.rofRecordID = nROFRecsOld + i; + mc2rof.print(); + } + } + } + } + + std::copy(mROFRecords.begin(), mROFRecords.end(), std::back_inserter(mROFRecordsAccum)); + if (mWithMCTruth) { + mLabelsAccum.mergeAtBack(mLabels); + } + LOG(INFO) << "Added " << mDigits.size() << " digits "; + // clean containers from already accumulated stuff + mLabels.clear(); + mDigits.clear(); + mROFRecords.clear(); + }; // and accumulate lambda + auto& eventParts = context->getEventParts(withQED); // loop over all composite collisions given from context (aka loop over all the interaction records) for (int collID = 0; collID < timesview.size(); ++collID) { @@ -128,11 +173,15 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer accumulate(); // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) - pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0, Lifetime::Timeframe}, mDigitsAccum); + pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0, Lifetime::Timeframe}, mROFRecordsAccum); if (mWithMCTruth) { pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0, Lifetime::Timeframe}, mMC2ROFRecordsAccum); - pc.outputs().snapshot(Output{mOrigin, "DIGITSMCTR", 0, Lifetime::Timeframe}, mLabelsAccum); + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{mOrigin, "DIGITSMCTR", 0, Lifetime::Timeframe}); + mLabelsAccum.flatten_to(sharedlabels); + // free space of existing label containers + mLabels.clear_andfreememory(); + mLabelsAccum.clear_andfreememory(); } LOG(INFO) << mID.getName() << ": Sending ROMode= " << mROMode << " to GRPUpdater"; pc.outputs().snapshot(Output{mOrigin, "ROMode", 0, Lifetime::Timeframe}, mROMode); @@ -149,48 +198,6 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer protected: ITSMFTDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth) {} - void accumulate() - { - // accumulate result of single event processing, called after processing every event supplied - // AND after the final flushing via digitizer::fillOutputContainer - if (!mDigits.size()) { - return; // no digits were flushed, nothing to accumulate - } - static int fixMC2ROF = 0; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID - auto ndigAcc = mDigitsAccum.size(); - std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(mDigitsAccum)); - - // fix ROFrecords references on ROF entries - auto nROFRecsOld = mROFRecordsAccum.size(); - - for (int i = 0; i < mROFRecords.size(); i++) { - auto& rof = mROFRecords[i]; - rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); - rof.print(); - - if (mFixMC2ROF < mMC2ROFRecordsAccum.size()) { // fix ROFRecord entry in MC2ROF records - for (int m2rid = mFixMC2ROF; m2rid < mMC2ROFRecordsAccum.size(); m2rid++) { - // need to register the ROFRecors entry for MC event starting from this entry - auto& mc2rof = mMC2ROFRecordsAccum[m2rid]; - if (rof.getROFrame() == mc2rof.minROF) { - mFixMC2ROF++; - mc2rof.rofRecordID = nROFRecsOld + i; - mc2rof.print(); - } - } - } - } - std::copy(mROFRecords.begin(), mROFRecords.end(), std::back_inserter(mROFRecordsAccum)); - if (mWithMCTruth) { - mLabelsAccum.mergeAtBack(mLabels); - } - LOG(INFO) << "Added " << mDigits.size() << " digits "; - // clean containers from already accumulated stuff - mLabels.clear(); - mDigits.clear(); - mROFRecords.clear(); - } - bool mWithMCTruth = true; bool mFinished = false; bool mDisableQED = false; @@ -198,7 +205,6 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; o2::itsmft::Digitizer mDigitizer; std::vector<o2::itsmft::Digit> mDigits; - std::vector<o2::itsmft::Digit> mDigitsAccum; std::vector<o2::itsmft::ROFRecord> mROFRecords; std::vector<o2::itsmft::ROFRecord> mROFRecordsAccum; std::vector<o2::itsmft::Hit> mHits; diff --git a/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx index e8e210bf6acb6..c6c8bafc155e8 100644 --- a/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx @@ -60,6 +60,7 @@ class MCHDPLDigitizerTask : public o2::base::BaseDPLDigitizer for (auto& record : irecords) { LOG(DEBUG) << "MCH TIME RECEIVED " << record.getTimeNS(); + LOG(DEBUG) << "MCH TIME RECEIVED: bc " << record.bc; } auto& eventParts = context->getEventParts(); @@ -69,7 +70,8 @@ class MCHDPLDigitizerTask : public o2::base::BaseDPLDigitizer // loop over all composite collisions given from context // (aka loop over all the interaction records) for (int collID = 0; collID < irecords.size(); ++collID) { - mDigitizer.setEventTime(irecords[collID].getTimeNS()); + // mDigitizer.setEventTime(irecords[collID].getTimeNS()); + mDigitizer.setEventTime(irecords[collID].bc); // for each collision, loop over the constituents event and source IDs // (background signal merging is basically taking place here) for (auto& part : eventParts[collID]) { diff --git a/Steer/DigitizerWorkflow/src/MCTruthReaderSpec.h b/Steer/DigitizerWorkflow/src/MCTruthReaderSpec.h new file mode 100644 index 0000000000000..eb507f4d4afe1 --- /dev/null +++ b/Steer/DigitizerWorkflow/src/MCTruthReaderSpec.h @@ -0,0 +1,103 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SRC_MCTRUTHREADERSPEC_H_ +#define STEER_DIGITIZERWORKFLOW_SRC_MCTRUTHREADERSPEC_H_ + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" +#include <SimulationDataFormat/ConstMCTruthContainer.h> +#include <SimulationDataFormat/MCCompLabel.h> +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" +#include "TTree.h" +#include "TFile.h" +#include "TString.h" +#include <sstream> + +using namespace o2::framework; + +namespace o2 +{ + +class MCTruthReaderTask : public o2::framework::Task +{ + public: + MCTruthReaderTask(bool newmctruth) : mNew{newmctruth} {} + + void init(framework::InitContext& ic) override + { + LOG(INFO) << "Initializing MCTruth reader "; + } + + void run(framework::ProcessingContext& pc) override + { + if (mFinished) { + return; + } + LOG(INFO) << "Running MCTruth reader "; + auto labelfilename = pc.inputs().get<TString*>("trigger"); + LOG(INFO) << "Opening file " << labelfilename->Data(); + TFile f(labelfilename->Data(), "OPEN"); + auto tree = (TTree*)f.Get("o2sim"); + auto br = tree->GetBranch("Labels"); + + if (mNew) { + // + dataformats::IOMCTruthContainerView* iocontainer = nullptr; + br->SetAddress(&iocontainer); + br->GetEntry(0); + + // publish the labels in a const shared memory container + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{"TST", "LABELS2", 0, Lifetime::Timeframe}); + iocontainer->copyandflatten(sharedlabels); + + } else { + // the original way with the MCTruthContainer + dataformats::MCTruthContainer<MCCompLabel>* mccontainer = nullptr; + br->SetAddress(&mccontainer); + br->GetEntry(0); + + LOG(INFO) << "MCCONTAINER CHECK" << mccontainer; + LOG(INFO) << "MCCONTAINER CHECK" << mccontainer->getNElements(); + + // publish the original labels + pc.outputs().snapshot(Output{"TST", "LABELS2", 0, Lifetime::Timeframe}, *mccontainer); + } + // we should be only called once; tell DPL that this process is ready to exit + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + mFinished = true; + } + + private: + bool mFinished = false; + bool mNew = false; +}; + +o2::framework::DataProcessorSpec getMCTruthReaderSpec(bool newmctruth) +{ + std::vector<InputSpec> inputs; + // input to notify that labels can be read + inputs.emplace_back("trigger", "TST", "TRIGGERREAD", 0, Lifetime::Timeframe); + return DataProcessorSpec{ + "MCTruthReader", + inputs, + Outputs{{"TST", "LABELS2", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask<MCTruthReaderTask>(newmctruth)}, + Options{}}; +} + +} // end namespace o2 + +#endif diff --git a/Steer/DigitizerWorkflow/src/MCTruthSourceSpec.cxx b/Steer/DigitizerWorkflow/src/MCTruthSourceSpec.cxx new file mode 100644 index 0000000000000..c80ae833cb31b --- /dev/null +++ b/Steer/DigitizerWorkflow/src/MCTruthSourceSpec.cxx @@ -0,0 +1,96 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" +#include <SimulationDataFormat/ConstMCTruthContainer.h> +#include <SimulationDataFormat/MCCompLabel.h> +#include "Framework/Task.h" +#include "Framework/Logger.h" + +using namespace o2::framework; + +namespace o2 +{ + +class MCTruthSourceTask : public o2::framework::Task +{ + public: + MCTruthSourceTask(bool newmctruth) : mNew{newmctruth} {} + + void init(framework::InitContext& ic) override + { + LOG(INFO) << "Initializing MCTruth source"; + mSize = ic.options().get<int>("size"); + } + + void run(framework::ProcessingContext& pc) override + { + if (mFinished) { + return; + } + LOG(INFO) << "Creating MCTruth container"; + + using TruthElement = o2::MCCompLabel; + using Container = dataformats::MCTruthContainer<TruthElement>; + Container container; + // create a very large container and stream it to TTree + for (int i = 0; i < mSize; ++i) { + container.addElement(i, TruthElement(i, i, i)); + container.addElement(i, TruthElement(i + 1, i, i)); + } + + if (mNew) { + LOG(INFO) << "New serialization"; + // we need to flatten it and write to managed shared memory container + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{"TST", "LABELS", 0, Lifetime::Timeframe}); + container.flatten_to(sharedlabels); + sleep(1); + } else { + LOG(INFO) << "Old serialization"; + pc.outputs().snapshot({"TST", "LABELS", 0, Lifetime::Timeframe}, container); + sleep(1); + } + + // we should be only called once; tell DPL that this process is ready to exit + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + mFinished = true; + } + + private: + bool mFinished = false; + int mSize = 0; + bool mNew = false; + o2::dataformats::MCTruthContainer<long> mLabels; // labels which get filled +}; + +o2::framework::DataProcessorSpec getMCTruthSourceSpec(bool newmctruth) +{ + // create the full data processor spec using + // a name identifier + // input description + // algorithmic description (here a lambda getting called once to setup the actual processing function) + // options that can be used for this processor (here: input file names where to take the hits) + std::vector<OutputSpec> outputs; + outputs.emplace_back("TST", "LABELS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "MCTruthSource", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask<MCTruthSourceTask>(newmctruth)}, + Options{ + {"size", VariantType::Int, 100000, {"Sample size"}}}}; +} + +} // end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/MCTruthSourceSpec.h b/Steer/DigitizerWorkflow/src/MCTruthSourceSpec.h new file mode 100644 index 0000000000000..e9a23c36c260e --- /dev/null +++ b/Steer/DigitizerWorkflow/src/MCTruthSourceSpec.h @@ -0,0 +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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SRC_MCTRUTHSOURCESPEC_H_ +#define STEER_DIGITIZERWORKFLOW_SRC_MCTRUTHSOURCESPEC_H_ + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +o2::framework::DataProcessorSpec getMCTruthSourceSpec(bool mctruth = true); +} // end namespace o2 + +#endif diff --git a/Steer/DigitizerWorkflow/src/MCTruthTestWorkflow.cxx b/Steer/DigitizerWorkflow/src/MCTruthTestWorkflow.cxx new file mode 100644 index 0000000000000..4f91872726a8a --- /dev/null +++ b/Steer/DigitizerWorkflow/src/MCTruthTestWorkflow.cxx @@ -0,0 +1,53 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <boost/program_options.hpp> + +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/DeviceSpec.h" +#include "MCTruthSourceSpec.h" +#include "MCTruthWriterSpec.h" +#include "MCTruthReaderSpec.h" + +using namespace o2::framework; + +void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions) +{ + // option to disable MC truth + workflowOptions.push_back(ConfigParamSpec{"newmctruth", o2::framework::VariantType::Bool, false, {"enable new container"}}); + workflowOptions.push_back(ConfigParamSpec{"consumers", o2::framework::VariantType::Int, 1, {"number of mc consumers"}}); +} + +#include "Framework/runDataProcessing.h" + +/// This function is required to be implemented to define the workflow +/// specifications +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + bool newmctruth = configcontext.options().get<bool>("newmctruth"); + + // connect the source + specs.emplace_back(o2::getMCTruthSourceSpec(newmctruth)); + // connect some consumers + for (int i = 0; i < configcontext.options().get<int>("consumers"); ++i) { + specs.emplace_back(o2::getMCTruthWriterSpec(i, i == 0, newmctruth)); + } + // connect a device reading back the labels + specs.emplace_back(o2::getMCTruthReaderSpec(newmctruth)); + // connect a device reading back the labels + specs.emplace_back(o2::getMCTruthWriterSpec(-1, false, newmctruth)); + + return specs; +} diff --git a/Steer/DigitizerWorkflow/src/MCTruthWriterSpec.cxx b/Steer/DigitizerWorkflow/src/MCTruthWriterSpec.cxx new file mode 100644 index 0000000000000..b35db1ad01954 --- /dev/null +++ b/Steer/DigitizerWorkflow/src/MCTruthWriterSpec.cxx @@ -0,0 +1,126 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" +#include <SimulationDataFormat/ConstMCTruthContainer.h> +#include <SimulationDataFormat/MCCompLabel.h> +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" +#include "TTree.h" +#include "TFile.h" +#include "TString.h" +#include <sstream> + +using namespace o2::framework; + +namespace o2 +{ + +class MCTruthWriterTask : public o2::framework::Task +{ + public: + MCTruthWriterTask(int id, bool doio, bool newmctruth) : mID{id}, mIO{doio}, mNew{newmctruth} {} + + void init(framework::InitContext& ic) override + { + LOG(INFO) << "Initializing MCTruth consumer " << mID; + } + + void run(framework::ProcessingContext& pc) override + { + if (mFinished) { + return; + } + LOG(INFO) << "Running MCTruth consumer " << mID; + TString labelfilename; + if (mNew) { + auto labels = pc.inputs().get<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(mID >= 0 ? "labels" : "labels2"); + LOG(INFO) << "GOT " << labels.getNElements() << " labels"; + + sleep(1); + + if (mIO) { + dataformats::IOMCTruthContainerView io(labels); + labelfilename = "labels_new.root"; + TFile f(labelfilename.Data(), "RECREATE"); + TTree tree("o2sim", "o2sim"); + auto br = tree.Branch("Labels", &io); + tree.Fill(); + f.Write(); + f.Close(); + } + + sleep(1); + } else { + auto labels = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>(mID >= 0 ? "labels" : "labels2"); + LOG(INFO) << "GOT " << labels->getNElements() << " labels"; + + sleep(1); + + if (mIO) { + labelfilename = "labels_old.root"; + TFile f(labelfilename.Data(), "RECREATE"); + TTree tree("o2sim", "o2sim"); + auto rawptr = labels.get(); + auto br = tree.Branch("Labels", &rawptr); + tree.Fill(); + f.Write(); + f.Close(); + } + sleep(1); + } + if (mIO) { + // this triggers the reader process + pc.outputs().snapshot({"TST", "TRIGGERREAD", 0, Lifetime::Timeframe}, labelfilename); + } + + // we should be only called once; tell DPL that this process is ready to exit + pc.services().get<ControlService>().readyToQuit(QuitRequest::Me); + mFinished = true; + } + + private: + bool mFinished = false; + bool mNew = false; + bool mIO = false; + int mID = 0; + o2::dataformats::MCTruthContainer<long> mLabels; // labels which get filled +}; + +o2::framework::DataProcessorSpec getMCTruthWriterSpec(int id, bool doio, bool newmctruth) +{ + std::vector<InputSpec> inputs; + if (id == -1) { + // we use this id as a secondary consumer of the LABELS2 channel + inputs.emplace_back("labels2", "TST", "LABELS2", 0, Lifetime::Timeframe); + } else { + inputs.emplace_back("labels", "TST", "LABELS", 0, Lifetime::Timeframe); + } + std::stringstream str; + str << "MCTruthWriter" << id; + + std::vector<OutputSpec> outputs; + if (doio) { + outputs.emplace_back("TST", "TRIGGERREAD", 0, Lifetime::Timeframe); + } + return DataProcessorSpec{ + str.str(), + inputs, + outputs, + AlgorithmSpec{adaptFromTask<MCTruthWriterTask>(id, doio, newmctruth)}, + Options{}}; +} + +} // end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/MCTruthWriterSpec.h b/Steer/DigitizerWorkflow/src/MCTruthWriterSpec.h new file mode 100644 index 0000000000000..436077f1b0386 --- /dev/null +++ b/Steer/DigitizerWorkflow/src/MCTruthWriterSpec.h @@ -0,0 +1,26 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_SRC_ZDCDIGITWRITERSPEC_H_ +#define STEER_DIGITIZERWORKFLOW_SRC_ZDCDIGITWRITERSPEC_H_ + +#include "Framework/DataProcessorSpec.h" +#include "Framework/InputSpec.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2 +{ + +o2::framework::DataProcessorSpec getMCTruthWriterSpec(int id, bool doio, bool newmctruth = true); + +} // end namespace o2 + +#endif /* STEER_DIGITIZERWORKFLOW_SRC_ZDCDIGITWRITERSPEC_H_ */ diff --git a/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.cxx index 658ef2efa08bb..fde0562970a56 100644 --- a/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.cxx @@ -75,8 +75,9 @@ void DigitizerSpec::retrieveHits(const char* brname, void DigitizerSpec::run(framework::ProcessingContext& pc) { - if (mFinished) + if (mFinished) { return; + } // read collision context from input auto context = pc.inputs().get<o2::steer::DigitizationContext*>("collisioncontext"); diff --git a/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.h b/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.h index 7b655b0b01482..b4af953807142 100644 --- a/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.h @@ -31,7 +31,7 @@ namespace phos /// \author Dmitri Peresunko, NRC "Kurchatov institute" /// \author Adopted from EMCAL code write by Markus Fasel /// \since Dec, 2019 -class DigitizerSpec : public o2::base::BaseDPLDigitizer +class DigitizerSpec final : public o2::base::BaseDPLDigitizer { public: /// \brief Constructor diff --git a/Steer/DigitizerWorkflow/src/SimReaderSpec.cxx b/Steer/DigitizerWorkflow/src/SimReaderSpec.cxx index 05a85d5424793..ad8922be63902 100644 --- a/Steer/DigitizerWorkflow/src/SimReaderSpec.cxx +++ b/Steer/DigitizerWorkflow/src/SimReaderSpec.cxx @@ -85,6 +85,10 @@ DataProcessorSpec getSimReaderSpec(SubspecRange range, const std::vector<std::st // initialize fundamental objects auto& mgr = steer::HitProcessingManager::instance(); + // init gRandom to random start + // TODO: offer option to set seed + gRandom->SetSeed(0); + if (simprefixes.size() == 0) { LOG(ERROR) << "No simulation prefix available"; } else { @@ -125,6 +129,7 @@ DataProcessorSpec getSimReaderSpec(SubspecRange range, const std::vector<std::st } mgr.getInteractionSampler().init(); + mgr.getInteractionSampler().print(); // doing a random event selection/subsampling? mgr.setRandomEventSequence(ctx.options().get<int>("randomsample") > 0); @@ -151,19 +156,23 @@ DataProcessorSpec getSimReaderSpec(SubspecRange range, const std::vector<std::st last.bc = o2::constants::lhc::LHCMaxBunches; const float ratio = ctx.options().get<float>("qed-x-section-ratio"); + if (ratio <= 0.) { + throw std::runtime_error("no meaningful qed-x-section-ratio was provided"); + } const float hadronicrate = ctx.options().get<float>("interactionRate"); const float qedrate = ratio * hadronicrate; LOG(INFO) << "QED RATE " << qedrate; qedInteractionSampler.setInteractionRate(qedrate); qedInteractionSampler.setFirstIR(first); qedInteractionSampler.init(); + qedInteractionSampler.print(); std::vector<o2::InteractionTimeRecord> qedinteractionrecords; o2::InteractionTimeRecord t; LOG(INFO) << "GENERATING COL TIMES"; - do { - t = qedInteractionSampler.generateCollisionTime(); + t = qedInteractionSampler.generateCollisionTime(); + while ((t = qedInteractionSampler.generateCollisionTime()) < last) { qedinteractionrecords.push_back(t); - } while (t < last); + } LOG(INFO) << "DONE GENERATING COL TIMES"; // get digitization context and add QED stuff @@ -202,7 +211,7 @@ DataProcessorSpec getSimReaderSpec(SubspecRange range, const std::vector<std::st {"firstOrbit", VariantType::Int, 1, {"First orbit in interaction sampling"}}, {"bcPatternFile", VariantType::String, "", {"Interacting BC pattern file (e.g. from CreateBCPattern.C)"}}, {"simPrefixQED", VariantType::String, "", {"Sim (QED) input prefix (example: path/o2qed). The prefix allows to find files like path/o2qed_Kine.root etc."}}, - {"qed-x-section-ratio", VariantType::Float, 10000.0f, {"Ratio of cross sections QED/hadronic events. Determines QED interaction rate from hadronic interaction rate."}}, + {"qed-x-section-ratio", VariantType::Float, -1.f, {"Ratio of cross sections QED/hadronic events. Determines QED interaction rate from hadronic interaction rate."}}, {"outcontext", VariantType::String, "collisioncontext.root", {"Output file for collision context"}}, {"incontext", VariantType::String, "", {"Take collision context from this file"}}, {"ncollisions,n", diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index 3b61c66e454e4..27b67d7d7f2e6 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -37,7 +37,7 @@ // for TOF #include "TOFDigitizerSpec.h" -#include "TOFWorkflow/TOFDigitWriterSpec.h" +#include "TOFWorkflowUtils/TOFDigitWriterSpec.h" // for FT0 #include "FT0DigitizerSpec.h" @@ -576,7 +576,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // the CPV part if (isEnabled(o2::detectors::DetID::CPV)) { detList.emplace_back(o2::detectors::DetID::CPV); - // connect the PHOS digitization + // connect the CPV digitization specs.emplace_back(o2::cpv::getCPVDigitizerSpec(fanoutsize++, mctruth)); // add PHOS writer specs.emplace_back(o2::cpv::getCPVDigitWriterSpec(mctruth)); diff --git a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx index aa5c4087e146c..743dd00bdf9f5 100644 --- a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx @@ -125,7 +125,7 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer // loop over all composite collisions given from context // (aka loop over all the interaction records) for (int collID = 0; collID < timesview.size(); ++collID) { - mDigitizer->setEventTime(timesview[collID].getTimeNS()); + mDigitizer->setEventTime(timesview[collID]); // for each collision, loop over the constituents event and source IDs // (background signal merging is basically taking place here) @@ -163,6 +163,14 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "DIGITSMCTR", 0, Lifetime::Timeframe}, *mcLabVecOfVec); } pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe}, *readoutwindow); + + // send empty pattern from digitizer (it may change in future) + static std::vector<uint32_t> patterns; + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe}, patterns); + + DigitHeader& digitH = mDigitizer->getDigitHeader(); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe}, digitH); + LOG(INFO) << "TOF: Sending ROMode= " << roMode << " to GRPUpdater"; pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "ROMode", 0, Lifetime::Timeframe}, roMode); @@ -197,8 +205,10 @@ DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth) inputs.emplace_back("tofccdbChannelCalib", o2::header::gDataOriginTOF, "ChannelCalib"); } std::vector<OutputSpec> outputs; + outputs.emplace_back(o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); if (mctruth) { outputs.emplace_back(o2::header::gDataOriginTOF, "DIGITSMCTR", 0, Lifetime::Timeframe); } diff --git a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx b/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx index 88ce2d479ebc1..53ca18ec2a4c8 100644 --- a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx @@ -24,7 +24,8 @@ #include "DataFormatsTPC/Digit.h" #include "TPCSimulation/CommonMode.h" #include <SimulationDataFormat/MCCompLabel.h> -#include <SimulationDataFormat/MCTruthContainer.h> +#include <SimulationDataFormat/ConstMCTruthContainer.h> +#include <SimulationDataFormat/IOMCTruthContainerView.h> #include <TFile.h> #include <TTree.h> #include <TBranch.h> @@ -62,24 +63,21 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfigur for (TObject* entry : *brlist) { auto br = static_cast<TBranch*>(entry); int brentries = br->GetEntries(); - if (entries == -1) { - entries = brentries; - } else { - if (brentries != entries && !TString(br->GetName()).Contains("CommonMode")) { - LOG(WARNING) << "INCONSISTENT NUMBER OF ENTRIES IN BRANCH " << br->GetName() << ": " << entries << " vs " << brentries; - } + entries = std::max(entries, brentries); + if (brentries != entries && !TString(br->GetName()).Contains("CommonMode")) { + LOG(WARNING) << "INCONSISTENT NUMBER OF ENTRIES IN BRANCH " << br->GetName() << ": " << entries << " vs " << brentries; } } if (entries > 0) { + LOG(INFO) << "Setting entries to " << entries; outputtree->SetEntries(entries); + // outputtree->Write("", TObject::kOverwrite); + outputfile->Close(); } - outputtree->Write(); - outputfile->Close(); }; //branch definitions for RootTreeWriter spec using DigitsOutputType = std::vector<o2::tpc::Digit>; - using MCLabelContainer = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; using CommonModeOutputType = std::vector<o2::tpc::CommonMode>; // extracts the sector from header of an input @@ -156,6 +154,7 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfigur if (sector >= 0) { LOG(INFO) << "DIGIT SIZE " << digiData.size(); const auto& trigS = (*trigP2Sect.get())[sector]; + int entries = 0; if (!trigS.size()) { std::runtime_error("Digits for sector " + std::to_string(sector) + " are received w/o info on grouping in triggers"); } else { // check consistency of Ndigits with that of expected from the trigger @@ -171,7 +170,9 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfigur auto ptr = &digiData; branch.SetAddress(&ptr); branch.Fill(); + entries++; branch.ResetAddress(); + branch.DropBaskets("all"); } else { // triggered mode (>1 entries will be written) std::vector<o2::tpc::Digit> digGroup; // group of digits related to single trigger auto ptr = &digGroup; @@ -182,19 +183,31 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfigur digGroup.emplace_back(digiData[group.getFirstEntry() + i]); // fetch digits of given trigger } branch.Fill(); + entries++; } branch.ResetAddress(); + branch.DropBaskets("all"); } } + auto tree = branch.GetTree(); + tree->SetEntries(entries); + tree->Write("", TObject::kOverwrite); } }; // handler for labels // TODO: this is almost a copy of the above, reduce to a single methods with amends - auto fillLabels = [extractSector, trigP2Sect](TBranch& branch, MCLabelContainer const& labeldata, DataRef const& ref) { + auto fillLabels = [extractSector, trigP2Sect](TBranch& branch, std::vector<char> const& labelbuffer, DataRef const& ref) { + o2::dataformats::IOMCTruthContainerView outputcontainer; + o2::dataformats::ConstMCTruthContainerView<o2::MCCompLabel> labeldata(labelbuffer); + // first of all redefine the output format (special to labels) + auto tree = branch.GetTree(); auto sector = extractSector(ref); + auto br = framework::RootTreeWriter::remapBranch(branch, &outputcontainer); + auto const* dh = DataRefUtils::getHeader<DataHeader*>(ref); LOG(INFO) << "HAVE LABEL DATA FOR SECTOR " << sector << " ON CHANNEL " << dh->subSpecification; + int entries = 0; if (sector >= 0) { LOG(INFO) << "MCTRUTH ELEMENTS " << labeldata.getIndexedSize() << " WITH " << labeldata.getNElements() << " LABELS"; @@ -211,25 +224,32 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfigur } { if (trigS.size() == 1) { // just 1 entry (continous mode?), use labels directly - auto ptr = &labeldata; - branch.SetAddress(&ptr); - branch.Fill(); - branch.ResetAddress(); + outputcontainer.adopt(labelbuffer); + br->Fill(); + br->ResetAddress(); + br->DropBaskets("all"); + entries = 1; } else { o2::dataformats::MCTruthContainer<o2::MCCompLabel> lblGroup; // labels for group of digits related to single trigger - auto ptr = &lblGroup; - branch.SetAddress(&ptr); for (auto const& group : trigS) { lblGroup.clear(); for (int i = 0; i < group.getEntries(); i++) { auto lbls = labeldata.getLabels(group.getFirstEntry() + i); lblGroup.addElements(i, lbls); } - branch.Fill(); + // init the output container + std::vector<char> flatbuffer; + lblGroup.flatten_to(flatbuffer); + outputcontainer.adopt(flatbuffer); + br->Fill(); + br->DropBaskets("all"); + entries++; } - branch.ResetAddress(); + br->ResetAddress(); } } + tree->SetEntries(entries); + tree->Write("", TObject::kOverwrite); } }; @@ -248,13 +268,13 @@ DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfigur getIndex, getName}; - auto labelsdef = BranchDefinition<MCLabelContainer>{InputSpec{"labelinput", ConcreteDataTypeMatcher{"TPC", "DIGITSMCTR"}}, - "TPCDigitMCTruth", "labels-branch-name", - // this branch definition is disabled if MC labels are not processed - (mctruth ? laneConfiguration.size() : 0), - fillLabels, - getIndex, - getName}; + auto labelsdef = BranchDefinition<std::vector<char>>{InputSpec{"labelinput", ConcreteDataTypeMatcher{"TPC", "DIGITSMCTR"}}, + "TPCDigitMCTruth", "labels-branch-name", + // this branch definition is disabled if MC labels are not processed + (mctruth ? laneConfiguration.size() : 0), + fillLabels, + getIndex, + getName}; auto commddef = BranchDefinition<CommonModeOutputType>{InputSpec{"commonmode", ConcreteDataTypeMatcher{"TPC", "COMMONMODE"}}, "TPCCommonMode", "common-mode-branch-name", diff --git a/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx index 9c7a63a01af53..23bf4da2201d6 100644 --- a/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx @@ -22,7 +22,7 @@ #include "TChain.h" #include "TSystem.h" #include <SimulationDataFormat/MCCompLabel.h> -#include <SimulationDataFormat/MCTruthContainer.h> +#include <SimulationDataFormat/ConstMCTruthContainer.h> #include "Framework/Task.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsTPC/TPCSectorHeader.h" @@ -38,6 +38,7 @@ using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; using DigiGroupRef = o2::dataformats::RangeReference<int, int>; +using SC = o2::tpc::SpaceCharge<double, 129, 129, 180>; namespace o2 { @@ -89,29 +90,14 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer readSpaceCharge.push_back(substr); } if (readSpaceCharge[0].size() != 0) { // use pre-calculated space-charge object - std::unique_ptr<o2::tpc::SpaceCharge> spaceCharge; if (!gSystem->AccessPathName(readSpaceCharge[0].data())) { - auto fileSC = std::unique_ptr<TFile>(TFile::Open(readSpaceCharge[0].data())); - if (fileSC->FindKey(readSpaceCharge[1].data())) { - spaceCharge.reset((o2::tpc::SpaceCharge*)fileSC->Get(readSpaceCharge[1].data())); - } - } - if (spaceCharge.get() != nullptr) { - LOG(INFO) << "Using pre-calculated space-charge object: " << readSpaceCharge[1].data(); - mDigitizer.setUseSCDistortions(spaceCharge.release()); + TFile fileSC(readSpaceCharge[0].data(), "READ"); + mDigitizer.setUseSCDistortions(fileSC); } else { LOG(ERROR) << "Space-charge object or file not found!"; } } else { // create new space-charge object either with empty TPC or an initial space-charge density provided by histogram - o2::tpc::SpaceCharge::SCDistortionType distortionType = useDistortions == 2 ? o2::tpc::SpaceCharge::SCDistortionType::SCDistortionsConstant : o2::tpc::SpaceCharge::SCDistortionType::SCDistortionsRealistic; - auto gridSizeString = ic.options().get<std::string>("gridSize"); - std::vector<int> gridSize; - std::stringstream ss(gridSizeString); - while (ss.good()) { - std::string substr; - getline(ss, substr, ','); - gridSize.push_back(std::stoi(substr)); - } + SC::SCDistortionType distortionType = useDistortions == 2 ? SC::SCDistortionType::SCDistortionsConstant : SC::SCDistortionType::SCDistortionsRealistic; auto inputHistoString = ic.options().get<std::string>("initialSpaceChargeDensity"); std::vector<std::string> inputHisto; std::stringstream ssHisto(inputHistoString); @@ -130,9 +116,9 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer } if (hisSCDensity.get() != nullptr) { LOG(INFO) << "TPC: Providing initial space-charge density histogram: " << hisSCDensity->GetName(); - mDigitizer.setUseSCDistortions(distortionType, hisSCDensity.get(), gridSize[0], gridSize[1], gridSize[2]); + mDigitizer.setUseSCDistortions(distortionType, hisSCDensity.get()); } else { - if (distortionType == SpaceCharge::SCDistortionType::SCDistortionsConstant) { + if (distortionType == SC::SCDistortionType::SCDistortionsConstant) { LOG(ERROR) << "Input space-charge density histogram or file not found!"; } } @@ -199,14 +185,12 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer uint64_t activeSectors = 0; activeSectors = sectorHeader->activeSectors; - // lambda that snapshots digits to be sent out; prepares and attaches header with sector information - auto snapshotDigits = [this, sector, &pc, activeSectors, &dh](std::vector<o2::tpc::Digit> const& digits) { + // lambda that creates a DPL owned buffer to accumulate the digits (in shared memory) + auto makeDigitBuffer = [this, sector, &pc, activeSectors, &dh]() { o2::tpc::TPCSectorHeader header{sector}; header.activeSectors = activeSectors; - // note that snapshoting only works with non-const references (to be fixed?) - pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast<SubSpecificationType>(dh->subSpecification), Lifetime::Timeframe, - header}, - const_cast<std::vector<o2::tpc::Digit>&>(digits)); + return &pc.outputs().make<std::vector<o2::tpc::Digit>>(Output{"TPC", "DIGITS", static_cast<SubSpecificationType>(dh->subSpecification), Lifetime::Timeframe, + header}); }; // lambda that snapshots the common mode vector to be sent out; prepares and attaches header with sector information auto snapshotCommonMode = [this, sector, &pc, activeSectors, &dh](std::vector<o2::tpc::CommonMode> const& commonMode) { @@ -222,9 +206,8 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer o2::tpc::TPCSectorHeader header{sector}; header.activeSectors = activeSectors; if (mWithMCTruth) { - pc.outputs().snapshot(Output{"TPC", "DIGITSMCTR", static_cast<SubSpecificationType>(dh->subSpecification), - Lifetime::Timeframe, header}, - const_cast<o2::dataformats::MCTruthContainer<o2::MCCompLabel>&>(labels)); + auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{"TPC", "DIGITSMCTR", static_cast<SubSpecificationType>(dh->subSpecification), Lifetime::Timeframe, header}); + labels.flatten_to(sharedlabels); } }; // lambda that snapshots digits grouping (triggers) to be sent out; prepares and attaches header with sector information @@ -237,7 +220,7 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer const_cast<std::vector<DigiGroupRef>&>(events)); }; - std::vector<o2::tpc::Digit> digitsAccum; // accumulator for digits + auto digitsAccum = makeDigitBuffer(); // accumulator for digits o2::dataformats::MCTruthContainer<o2::MCCompLabel> labelAccum; // timeframe accumulator for labels std::vector<CommonMode> commonModeAccum; std::vector<DigiGroupRef> eventAccum; @@ -260,14 +243,14 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer auto& eventParts = context->getEventParts(); - auto flushDigitsAndLabels = [this, &digitsAccum, &labelAccum, &commonModeAccum](bool finalFlush = false) { + auto flushDigitsAndLabels = [this, digitsAccum, &labelAccum, &commonModeAccum](bool finalFlush = false) { // flush previous buffer mDigits.clear(); mLabels.clear(); mCommonMode.clear(); mDigitizer.flush(mDigits, mLabels, mCommonMode, finalFlush); LOG(INFO) << "TPC: Flushed " << mDigits.size() << " digits, " << mLabels.getNElements() << " labels and " << mCommonMode.size() << " common mode entries"; - std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(digitsAccum)); + std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(*digitsAccum)); if (mWithMCTruth) { labelAccum.mergeAtBack(mLabels); } @@ -289,7 +272,7 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer if (!isContinuous) { mDigitizer.setStartTime(sampaProcessing.getTimeBinFromTime(eventTime)); } - int startSize = digitsAccum.size(); + int startSize = digitsAccum->size(); // for each collision, loop over the constituents event and source IDs // (background signal merging is basically taking place here) @@ -310,7 +293,7 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer flushDigitsAndLabels(); if (!isContinuous) { - eventAccum.emplace_back(startSize, digitsAccum.size() - startSize); + eventAccum.emplace_back(startSize, digitsAccum->size() - startSize); } } } @@ -319,12 +302,12 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer if (isContinuous) { LOG(INFO) << "TPC: Final flush"; flushDigitsAndLabels(true); - eventAccum.emplace_back(0, digitsAccum.size()); // all digits are grouped to 1 super-event pseudo-triggered mode + eventAccum.emplace_back(0, digitsAccum->size()); // all digits are grouped to 1 super-event pseudo-triggered mode } // send out to next stage snapshotEvents(eventAccum); - snapshotDigits(digitsAccum); + // snapshotDigits(digitsAccum); --> done automatically snapshotCommonMode(commonModeAccum); snapshotLabels(labelAccum); @@ -371,7 +354,6 @@ o2::framework::DataProcessorSpec getTPCDigitizerSpec(int channel, bool writeGRP, outputs, AlgorithmSpec{adaptFromTask<TPCDPLDigitizerTask>()}, Options{{"distortionType", VariantType::Int, 0, {"Distortion type to be used. 0 = no distortions (default), 1 = realistic distortions (not implemented yet), 2 = constant distortions"}}, - {"gridSize", VariantType::String, "129,144,129", {"Comma separated list of number of bins in (r,phi,z) for distortion lookup tables (r and z can only be 2**N + 1, N=1,2,3,...)"}}, {"initialSpaceChargeDensity", VariantType::String, "", {"Path to root file containing TH3 with initial space-charge density and name of the TH3 (comma separated)"}}, {"readSpaceCharge", VariantType::String, "", {"Path to root file containing pre-calculated space-charge object and name of the object (comma separated)"}}, {"TPCtriggered", VariantType::Bool, false, {"Impose triggered RO mode (default: continuous)"}}}}; diff --git a/Steer/include/Steer/InteractionSampler.h b/Steer/include/Steer/InteractionSampler.h index deffa584251be..f4ebf6727b283 100644 --- a/Steer/include/Steer/InteractionSampler.h +++ b/Steer/include/Steer/InteractionSampler.h @@ -78,7 +78,7 @@ class InteractionSampler o2::BunchFilling mBCFilling; ///< patter of active BCs std::vector<float> mTimeInBC; ///< interaction times within single BC std::vector<uint16_t> mInteractingBCs; // vector of interacting BCs - size_t mCurrBCIdx = 0; ///< counter for current interacting bunch + int mCurrBCIdx = 0; ///< counter for current interacting bunch static constexpr float DefIntRate = 50e3; ///< default interaction rate diff --git a/Steer/include/Steer/MCKinematicsReader.h b/Steer/include/Steer/MCKinematicsReader.h index e031a251c7e65..e83099b65818b 100644 --- a/Steer/include/Steer/MCKinematicsReader.h +++ b/Steer/include/Steer/MCKinematicsReader.h @@ -11,12 +11,16 @@ #include "SimulationDataFormat/DigitizationContext.h" #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/TrackReference.h" +#include "SimulationDataFormat/MCTruthContainer.h" #include <vector> class TChain; namespace o2 { + namespace steer { @@ -77,8 +81,32 @@ class MCKinematicsReader /// get all mothers/daughters of the given label + /// return all track references associated to a source/event/track + gsl::span<o2::TrackReference> getTrackRefs(int source, int event, int track) const; + /// return all track references associated to a source/event + const std::vector<o2::TrackReference>& getTrackRefsByEvent(int source, int event) const; + /// return all track references associated to a event/track (when initialized from kinematics directly) + gsl::span<o2::TrackReference> getTrackRefs(int event, int track) const; + + /// retrieves the MCEventHeader for a given eventID and sourceID + o2::dataformats::MCEventHeader const& getMCEventHeader(int source, int event) const; + + /// Get number of sources + size_t getNSources() const; + + /// Get number of events + size_t getNEvents(int source) const; + + DigitizationContext const* getDigitizationContext() const + { + return mDigitizationContext; + } + private: void loadTracksForSource(int source) const; + void loadHeadersForSource(int source) const; + void loadTrackRefsForSource(int source) const; + void initIndexedTrackRefs(std::vector<o2::TrackReference>& refs, o2::dataformats::MCTruthContainer<o2::TrackReference>& indexedrefs) const; DigitizationContext const* mDigitizationContext = nullptr; @@ -87,6 +115,8 @@ class MCKinematicsReader // a vector of tracks foreach source and each collision mutable std::vector<std::vector<std::vector<o2::MCTrack>>> mTracks; // the in-memory track container + mutable std::vector<std::vector<o2::dataformats::MCEventHeader>> mHeaders; // the in-memory header container + mutable std::vector<std::vector<o2::dataformats::MCTruthContainer<o2::TrackReference>>> mIndexedTrackRefs; // the in-memory track ref container bool mInitialized = false; // whether initialized }; @@ -125,5 +155,47 @@ inline std::vector<MCTrack> const& MCKinematicsReader::getTracks(int event) cons return getTracks(0, event); } +inline o2::dataformats::MCEventHeader const& MCKinematicsReader::getMCEventHeader(int source, int event) const +{ + if (mHeaders.at(source).size() == 0) { + loadHeadersForSource(source); + } + return mHeaders.at(source)[event]; +} + +inline gsl::span<o2::TrackReference> MCKinematicsReader::getTrackRefs(int source, int event, int track) const +{ + if (mIndexedTrackRefs[source].size() == 0) { + loadTrackRefsForSource(source); + } + return mIndexedTrackRefs[source][event].getLabels(track); +} + +inline const std::vector<o2::TrackReference>& MCKinematicsReader::getTrackRefsByEvent(int source, int event) const +{ + if (mIndexedTrackRefs[source].size() == 0) { + loadTrackRefsForSource(source); + } + return mIndexedTrackRefs[source][event].getTruthArray(); +} + +inline gsl::span<o2::TrackReference> MCKinematicsReader::getTrackRefs(int event, int track) const +{ + return getTrackRefs(0, event, track); +} + +inline size_t MCKinematicsReader::getNSources() const +{ + return mTracks.size(); +} + +inline size_t MCKinematicsReader::getNEvents(int source) const +{ + if (mTracks[source].size() == 0) { + loadTracksForSource(source); + } + return mTracks[source].size(); +} + } // namespace steer } // namespace o2 diff --git a/Steer/include/Steer/O2RunSim.h b/Steer/include/Steer/O2RunSim.h index 709d54461e62d..90ea8df528542 100644 --- a/Steer/include/Steer/O2RunSim.h +++ b/Steer/include/Steer/O2RunSim.h @@ -36,6 +36,7 @@ #include "FairTask.h" // for FairTask #include "FairTrajFilter.h" // for FairTrajFilter #include "TRandom.h" +#include <TObjString.h> #include <Steer/O2MCApplication.h> namespace o2 diff --git a/Steer/src/InteractionSampler.cxx b/Steer/src/InteractionSampler.cxx index 53184678892df..b59cca5c1804d 100644 --- a/Steer/src/InteractionSampler.cxx +++ b/Steer/src/InteractionSampler.cxx @@ -55,16 +55,18 @@ void InteractionSampler::init() // Poisson distribution of number of collisions in the bunch excluding 0 mNCollBCGenerator.initialize([mu]() { int n = 0; - while ((n = gRandom->Poisson(mu)) == 0) + while ((n = gRandom->Poisson(mu)) == 0) { ; + } return n; }); auto trms = mBCTimeRMS; mCollTimeGenerator.initialize([trms]() { float t; // make sure it does not go outside half bunch - while (std::abs(t = gRandom->Gaus(0, trms)) > o2::constants::lhc::LHCBunchSpacingNS / 2.1) + while (std::abs(t = gRandom->Gaus(0, trms)) > o2::constants::lhc::LHCBunchSpacingNS / 2.1) { ; + } return t; }); @@ -74,7 +76,8 @@ void InteractionSampler::init() while (mCurrBCIdx < mInteractingBCs.size() && mInteractingBCs[mCurrBCIdx] < mIR.bc) { mCurrBCIdx++; } - mCurrBCIdx = mCurrBCIdx == 0 ? mInteractingBCs.size() : mCurrBCIdx--; + // set the "current BC" right in front of the 1st BC to generate. There will be a jump by at least 1 during generation + mCurrBCIdx--; } //_________________________________________________ diff --git a/Steer/src/MCKinematicsReader.cxx b/Steer/src/MCKinematicsReader.cxx index ec16b76570038..293513d8a8ef6 100644 --- a/Steer/src/MCKinematicsReader.cxx +++ b/Steer/src/MCKinematicsReader.cxx @@ -10,12 +10,33 @@ #include "DetectorsCommonDataFormats/NameConf.h" #include "Steer/MCKinematicsReader.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/TrackReference.h" #include <TChain.h> #include <vector> #include "FairLogger.h" using namespace o2::steer; +void MCKinematicsReader::initIndexedTrackRefs(std::vector<o2::TrackReference>& refs, o2::dataformats::MCTruthContainer<o2::TrackReference>& indexedrefs) const +{ + // sort trackrefs according to track index then according to track length + std::sort(refs.begin(), refs.end(), [](const o2::TrackReference& a, const o2::TrackReference& b) { + if (a.getTrackID() == b.getTrackID()) { + return a.getLength() < b.getLength(); + } + return a.getTrackID() < b.getTrackID(); + }); + + // make final indexed container for track references + indexedrefs.clear(); + for (auto& ref : refs) { + if (ref.getTrackID() >= 0) { + indexedrefs.addElement(ref.getTrackID(), ref); + } + } +} + void MCKinematicsReader::loadTracksForSource(int source) const { auto chain = mInputChains[source]; @@ -35,6 +56,51 @@ void MCKinematicsReader::loadTracksForSource(int source) const } } +void MCKinematicsReader::loadHeadersForSource(int source) const +{ + auto chain = mInputChains[source]; + if (chain) { + // todo: get name from NameConfig + auto br = chain->GetBranch("MCEventHeader."); + if (br) { + o2::dataformats::MCEventHeader* header = nullptr; + br->SetAddress(&header); + mHeaders[source].resize(br->GetEntries()); + for (int event = 0; event < br->GetEntries(); ++event) { + br->GetEntry(event); + mHeaders[source][event] = *header; + } + } else { + LOG(WARN) << "MCHeader branch not found"; + } + } +} + +void MCKinematicsReader::loadTrackRefsForSource(int source) const +{ + auto chain = mInputChains[source]; + if (chain) { + // todo: get name from NameConfig + auto br = chain->GetBranch("TrackRefs"); + if (br) { + std::vector<o2::TrackReference>* refs = nullptr; + br->SetAddress(&refs); + mIndexedTrackRefs[source].resize(br->GetEntries()); + for (int event = 0; event < br->GetEntries(); ++event) { + br->GetEntry(event); + if (refs) { + // we convert the original flat vector into an indexed structure + initIndexedTrackRefs(*refs, mIndexedTrackRefs[source][event]); + delete refs; + refs = nullptr; + } + } + } else { + LOG(WARN) << "TrackRefs branch not found"; + } + } +} + bool MCKinematicsReader::initFromDigitContext(std::string_view name) { if (mInitialized) { @@ -54,6 +120,8 @@ bool MCKinematicsReader::initFromDigitContext(std::string_view name) // load the kinematics information mTracks.resize(mInputChains.size()); + mHeaders.resize(mInputChains.size()); + mIndexedTrackRefs.resize(mInputChains.size()); // actual loading will be done only if someone asks // the first time for a particular source ... @@ -70,6 +138,8 @@ bool MCKinematicsReader::initFromKinematics(std::string_view name) mInputChains.emplace_back(new TChain("o2sim")); mInputChains.back()->AddFile(o2::base::NameConf::getMCKinematicsFileName(name.data()).c_str()); mTracks.resize(1); + mHeaders.resize(1); + mIndexedTrackRefs.resize(1); mInitialized = true; return true; diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 50776cc5db802..f31ea54e84679 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -183,8 +183,9 @@ void O2MCApplicationBase::AddParticles() LOG(INFO) << param; // check if there are PDG codes requested for user decay - if (param.pdglist.empty()) + if (param.pdglist.empty()) { return; + } // loop over PDG codes in the string std::stringstream ss(param.pdglist); @@ -247,7 +248,6 @@ void O2MCApplication::SendData() attachSubEventInfo(simdataparts, *mSubEventInfo); auto tracks = attachBranch<std::vector<o2::MCTrack>>("MCTrack", *mSimDataChannel, simdataparts); attachBranch<std::vector<o2::TrackReference>>("TrackRefs", *mSimDataChannel, simdataparts); - attachBranch<o2::dataformats::MCTruthContainer<o2::TrackReference>>("IndexedTrackRefs", *mSimDataChannel, simdataparts); assert(tracks->size() == mSubEventInfo->npersistenttracks); for (auto det : listActiveDetectors) { diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt index 54813f2448236..eb91bcfe5ac0a 100644 --- a/Utilities/CMakeLists.txt +++ b/Utilities/CMakeLists.txt @@ -8,14 +8,9 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -if(ALIROOT) - # FIXME: not tested - add_subdirectory(hough) -endif(ALIROOT) - add_subdirectory(aliceHLTwrapper) add_subdirectory(O2MessageMonitor) -add_subdirectory(DataFlow) +add_subdirectory(DataSampling) add_subdirectory(Publishers) add_subdirectory(DataCompression) add_subdirectory(Mergers) diff --git a/Utilities/DataCompression/include/DataCompression/HuffmanCodec.h b/Utilities/DataCompression/include/DataCompression/HuffmanCodec.h index 2e07e0b5a31dc..5b6cda6bac249 100644 --- a/Utilities/DataCompression/include/DataCompression/HuffmanCodec.h +++ b/Utilities/DataCompression/include/DataCompression/HuffmanCodec.h @@ -436,8 +436,9 @@ class HuffmanModel : public _BASE } node->getLeftChild()->setBinaryCode(codelen + 1, c); int branchlen = assignCode(node->getLeftChild()); - if (retcodelen < branchlen) + if (retcodelen < branchlen) { retcodelen = branchlen; + } } if (node->getRightChild()) { // bit '0' branch code_type c = node->getBinaryCode(); @@ -449,8 +450,9 @@ class HuffmanModel : public _BASE } node->getRightChild()->setBinaryCode(codelen + 1, c); int branchlen = assignCode(node->getRightChild()); - if (retcodelen < branchlen) + if (retcodelen < branchlen) { retcodelen = branchlen; + } } return retcodelen; } diff --git a/Utilities/DataFlow/CMakeLists.txt b/Utilities/DataFlow/CMakeLists.txt deleted file mode 100644 index 05e9e26bd0c31..0000000000000 --- a/Utilities/DataFlow/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -# 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". -# -# See http://alice-o2.web.cern.ch/license for full licensing information. -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. - -# the bucket contains the following dependencies - common_boost_bucket - Base - -# Headers - O2device - dl the 'dl' dependency is needed as the device -# boilerplate code in runSimpleMQStateMachine.h uses dlopen etc. Probably this -# hidden dependency can be avoided by including the to some compiled FairMQ -# library - -o2_add_library(DataFlow - SOURCES src/FakeTimeframeBuilder.cxx - src/FakeTimeframeGeneratorDevice.cxx - src/HeartbeatSampler.cxx - src/SubframeBuilderDevice.cxx - src/TimeframeParser.cxx - src/TimeframeReaderDevice.cxx - src/TimeframeValidatorDevice.cxx - src/TimeframeWriterDevice.cxx - src/EPNReceiverDevice.cxx - src/FLPSenderDevice.cxx - PUBLIC_LINK_LIBRARIES O2::Headers O2::TimeFrame FairMQ::FairMQ - O2::Device) - -o2_target_man_page(DataFlow NAME o2-timeframe-reader-device) -o2_target_man_page(DataFlow NAME o2-timeframe-writer-device) -o2_target_man_page(DataFlow NAME o2-subframebuilder-device) - -o2_add_executable(fake-timeframegenerator-device - SOURCES src/runFakeTimeframeGeneratorDevice - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(heartbeat-sampler - SOURCES src/runHeartbeatSampler - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(subframebuilder-device - SOURCES src/runSubframeBuilderDevice - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(timeframe-reader-device - SOURCES src/runTimeframeReaderDevice - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(timeframe-validator-device - SOURCES src/runTimeframeValidatorDevice - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(timeframe-writer-device - SOURCES src/runTimeframeWriterDevice - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(epn-receiver-device - SOURCES src/runEPNReceiver - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(flp-sender-device - SOURCES src/runFLPSender - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_executable(timeframe-validation-tool - SOURCES src/TimeframeValidationTool - PUBLIC_LINK_LIBRARIES O2::DataFlow) - -o2_add_test(TimeframeParser - SOURCES test/test_TimeframeParser - PUBLIC_LINK_LIBRARIES O2::DataFlow - COMPONENT_NAME dataflow - LABELS utils) - -o2_add_test(SubframeUtils01 - SOURCES test/test_SubframeUtils01 - PUBLIC_LINK_LIBRARIES O2::DataFlow - COMPONENT_NAME dataflow - LABELS utils) - -o2_add_test(PayloadMerger01 - SOURCES test/test_PayloadMerger01 - PUBLIC_LINK_LIBRARIES O2::DataFlow - COMPONENT_NAME dataflow - LABELS utils) diff --git a/Utilities/DataFlow/doc/o2-subframebuilder-device.1.in b/Utilities/DataFlow/doc/o2-subframebuilder-device.1.in deleted file mode 100644 index 653053997d842..0000000000000 --- a/Utilities/DataFlow/doc/o2-subframebuilder-device.1.in +++ /dev/null @@ -1,48 +0,0 @@ -.\" Manpage for o2-subframebuilder-device. -.TH AliceO2 1 "12 May 2017" "1.0" "o2-subframebuilder-device man page" - -.SH NAME - -o2-subframebuilder-device - aggregate HBF in input as a single STF - -.SH SYNOPSIS - -o2-subframebuilder-device [options] - -.SH DESCRIPTION - -o2-subframebuilder-device will take in input a number of HeartBeat Frames -(HBF) and merge them in a single STF, with a policy defined by the -passed options. - -.SH OPTIONS - ---self-triggered Time frame duration - -.TP 5 - ---in-chan-name [NAME] Name of the input channel - -.TP 5 - ---out-chan-name [NAME] Name of the output channel - -.TP 5 - ---detector-name [NAME] Name of detector as data source - -.TP 5 - ---flp-id arg [NAME] ID of the FLP used as data source - -.TP 5 - ---strip-hbf Strip HeartBeatHeader (HBH) & HeartBeatTrailer (HBT) from each HBF - -.SH SEE ALSO - -FLPSenderDEvice(1), o2-epn-receiver-device(1), o2-heartbeat-sampler(1), TimeframeValidator(1) - -.SH BUGS - -Lots of bugs diff --git a/Utilities/DataFlow/doc/o2-timeframe-reader-device.1.in b/Utilities/DataFlow/doc/o2-timeframe-reader-device.1.in deleted file mode 100644 index bdf460cadc976..0000000000000 --- a/Utilities/DataFlow/doc/o2-timeframe-reader-device.1.in +++ /dev/null @@ -1,29 +0,0 @@ -.\" Manpage for o2-timeframe-reader-device. -.TH man 1 "12 May 2017" "1.0" "o2-timeframe-reader-device man page" - -.SH NAME - -o2-timeframe-reader-device - read a timeframe from disk - -.SH SYNOPSIS - -o2-timeframe-reader-device --input-file [FILE] - -.SH DESCRIPTION - -o2-timeframe-reader-device will read a Timeframe from the FILE on disk and streams it -via FairMQ - -.SH OPTIONS - -.TP 5 - ---input-file [FILE] the file to be streamed - -.SH SEE ALSO - -o2-timeframe-writer-device(1) - -.SH BUGS - -Lots of bugs diff --git a/Utilities/DataFlow/doc/o2-timeframe-writer-device.1.in b/Utilities/DataFlow/doc/o2-timeframe-writer-device.1.in deleted file mode 100644 index a4828d9f12d41..0000000000000 --- a/Utilities/DataFlow/doc/o2-timeframe-writer-device.1.in +++ /dev/null @@ -1,29 +0,0 @@ -.\" Manpage for o2-timeframe-writer-device. -.TH man 1 "12 May 2017" "1.0" "o2-timeframe-writer-device man page" - -.SH NAME - -o2-timeframe-writer-device - writes a timeframe to disk - -.SH SYNOPSIS - -o2-timeframe-writer-device --input-file [FILE] - -.SH DESCRIPTION - -o2-timeframe-writer-device will receive a Timeframe from FairMQ transport and stream -it via FairMQ. - -.SH OPTIONS - -.TP 5 - ---output-file [FILE] the file where to stream results - -.SH SEE ALSO - -o2-timeframe-reader-device(1) - -.SH BUGS - -Lots of bugs diff --git a/Utilities/DataFlow/include/DataFlow/EPNReceiverDevice.h b/Utilities/DataFlow/include/DataFlow/EPNReceiverDevice.h deleted file mode 100644 index 3e3127283b852..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/EPNReceiverDevice.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_DEVICES_EPNRECEIVER_H_ -#define ALICEO2_DEVICES_EPNRECEIVER_H_ - -#include <string> -#include <unordered_map> -#include <unordered_set> -#include <chrono> - -#include <FairMQDevice.h> - -namespace o2 -{ -namespace devices -{ - -/// Container for (sub-)timeframes - -struct TFBuffer { - FairMQParts parts; - std::chrono::steady_clock::time_point start; - std::chrono::steady_clock::time_point end; -}; - -/// Receives sub-timeframes from the flpSenders and merges these into full timeframes. - -class EPNReceiverDevice : public FairMQDevice -{ - public: - EPNReceiverDevice() = default; - ~EPNReceiverDevice() final = default; - void InitTask() final; - - /// Prints the contents of the timeframe container - void PrintBuffer(const std::unordered_map<uint16_t, TFBuffer>& buffer) const; - - /// Discared incomplete timeframes after \p fBufferTimeoutInMs. - void DiscardIncompleteTimeframes(); - - protected: - /// Overloads the Run() method of FairMQDevice - void Run() override; - - std::unordered_map<uint16_t, TFBuffer> mTimeframeBuffer; ///< Stores (sub-)timeframes - std::unordered_set<uint16_t> mDiscardedSet; ///< Set containing IDs of dropped timeframes - - int mNumFLPs = 0; ///< Number of flpSenders - int mBufferTimeoutInMs = 5000; ///< Time after which incomplete timeframes are dropped - int mTestMode = 0; ///< Run the device in test mode (only syncSampler+flpSender+epnReceiver) - - std::string mInChannelName = ""; - std::string mOutChannelName = ""; - std::string mAckChannelName = ""; -}; - -} // namespace devices -} // namespace o2 - -#endif diff --git a/Utilities/DataFlow/include/DataFlow/FLPSenderDevice.h b/Utilities/DataFlow/include/DataFlow/FLPSenderDevice.h deleted file mode 100644 index 3492c0f5853d5..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/FLPSenderDevice.h +++ /dev/null @@ -1,72 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_DEVICES_FLPSENDER_H_ -#define ALICEO2_DEVICES_FLPSENDER_H_ - -#include <string> -#include <queue> -#include <unordered_map> -#include <chrono> - -#include <FairMQDevice.h> - -namespace o2 -{ -namespace devices -{ - -/// Sends sub-timframes to epnReceivers -/// -/// Sub-timeframes are received from the previous step (or generated in test-mode) -/// and are sent to epnReceivers. Target epnReceiver is determined from the timeframe ID: -/// targetEpnReceiver = timeframeId % numEPNs (numEPNs is same for every flpSender, although some may be inactive). - -class FLPSenderDevice : public FairMQDevice -{ - public: - /// Default constructor - FLPSenderDevice() = default; - - /// Default destructor - ~FLPSenderDevice() final = default; - - protected: - /// Overloads the InitTask() method of FairMQDevice - void InitTask() final; - - /// Overloads the Run() method of FairMQDevice - void Run() final; - - private: - /// Sends the "oldest" element from the sub-timeframe container - void sendFrontData(); - - std::queue<FairMQParts> mSTFBuffer; ///< Buffer for sub-timeframes - std::queue<std::chrono::steady_clock::time_point> mArrivalTime; ///< Stores arrival times of sub-timeframes - - int mNumEPNs = 0; ///< Number of epnReceivers - unsigned int mIndex = 0; ///< Index of the flpSender among other flpSenders - unsigned int mSendOffset = 0; ///< Offset for staggering output - unsigned int mSendDelay = 8; ///< Delay for staggering output - - int mEventSize = 10000; ///< Size of the sub-timeframe body (only for test mode) - int mTestMode = false; ///< Run the device in test mode (only syncSampler+flpSender+epnReceiver) - uint16_t mTimeFrameId; - - std::string mInChannelName = ""; - std::string mOutChannelName = ""; - int mLastTimeframeId = -1; -}; - -} // namespace devices -} // namespace o2 - -#endif diff --git a/Utilities/DataFlow/include/DataFlow/FakeTimeframeBuilder.h b/Utilities/DataFlow/include/DataFlow/FakeTimeframeBuilder.h deleted file mode 100644 index 87e72cc0df998..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/FakeTimeframeBuilder.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 DATAFLOW_FAKETIMEFRAMEBUILDER_H_ -#define DATAFLOW_FAKETIMEFRAMEBUILDER_H_ - -#include "Headers/DataHeader.h" -#include <vector> -#include <memory> -#include <functional> - -namespace o2 -{ -namespace data_flow -{ - -struct FakeTimeframeSpec { - const char* origin; - const char* dataDescription; - std::function<void(char*, size_t)> bufferFiller; - size_t bufferSize; -}; - -/** Generate a timeframe from the provided specification - */ -std::unique_ptr<char[]> fakeTimeframeGenerator(std::vector<FakeTimeframeSpec>& specs, std::size_t& totalSize); - -} // namespace data_flow -} // namespace o2 -#endif /* DATAFLOW_FAKETIMEFRAMEBUILDER_H_ */ diff --git a/Utilities/DataFlow/include/DataFlow/FakeTimeframeGeneratorDevice.h b/Utilities/DataFlow/include/DataFlow/FakeTimeframeGeneratorDevice.h deleted file mode 100644 index 5c25ec45f7394..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/FakeTimeframeGeneratorDevice.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_FAKE_TIMEFRAME_GENERATOR_H_ -#define ALICEO2_FAKE_TIMEFRAME_GENERATOR_H_ - -#include "O2Device/O2Device.h" - -namespace o2 -{ -namespace data_flow -{ - -/// A device which writes to file the timeframes. -class FakeTimeframeGeneratorDevice : public base::O2Device -{ - public: - static constexpr const char* OptionKeyOutputChannelName = "output-channel-name"; - static constexpr const char* OptionKeyMaxTimeframes = "max-timeframes"; - - /// Default constructor - FakeTimeframeGeneratorDevice(); - - /// Default destructor - ~FakeTimeframeGeneratorDevice() override = default; - - void InitTask() final; - - protected: - /// Overloads the ConditionalRun() method of FairMQDevice - bool ConditionalRun() final; - - std::string mOutChannelName; - size_t mMaxTimeframes; - size_t mTimeframeCount; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/Utilities/DataFlow/include/DataFlow/HeartbeatSampler.h b/Utilities/DataFlow/include/DataFlow/HeartbeatSampler.h deleted file mode 100644 index d979cc2961aa5..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/HeartbeatSampler.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 HEARTBEATSAMPLER_H -#define HEARTBEATSAMPLER_H - -// @file HeartbeatSampler.h -// @author Matthias Richter -// @since 2017-02-03 -// @brief Heartbeat sampler device - -#include "Headers/DataHeader.h" -#include "Headers/HeartbeatFrame.h" -#include "O2Device/O2Device.h" -#include <string> - -namespace o2 -{ -namespace data_flow -{ - -/// @class HeartbeatSampler -/// @brief A sampler for heartbeat triggers -/// -/// The device is going to be used in an emulation of the reader -/// processes on the FLP -/// The heartbeat triggers are sent out with constant frequency, the -/// period in nano seconds can be configured by option --period -/// -/// TODO: the class can evolve to a general clock sampler device with -/// configurable period, even randomly distributed -class HeartbeatSampler : public base::O2Device -{ - public: - typedef o2::base::O2Message O2Message; - - static constexpr const char* OptionKeyOutputChannelName = "out-chan-name"; - static constexpr const char* OptionKeyPeriod = "period"; - - HeartbeatSampler() = default; - ~HeartbeatSampler() final = default; - - protected: - /// overloading the InitTask() method of FairMQDevice - void InitTask() final; - - /// overloading ConditionalRun method of FairMQDevice - bool ConditionalRun() final; - - private: - /// publishing period (configurable) - uint32_t mPeriod = 1000000000; - /// name of the (configurable) - std::string mOutputChannelName = "output"; - /// number of elapsed periods - int mCount = 0; -}; - -} // namespace data_flow -}; // namespace o2 -#endif diff --git a/Utilities/DataFlow/include/DataFlow/PayloadMerger.h b/Utilities/DataFlow/include/DataFlow/PayloadMerger.h deleted file mode 100644 index 81c4544a5f717..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/PayloadMerger.h +++ /dev/null @@ -1,131 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 PAYLOAD_MERGER_H -#define PAYLOAD_MERGER_H - -#include <map> -#include <cstdint> -#include <vector> -#include <functional> -#include <cstring> - -#include <fairmq/FairMQMessage.h> - -namespace o2 -{ -namespace dataflow -{ -/// Helper class that given a set of FairMQMessage, merges (part of) their -/// payload into a separate memory area. -/// -/// - Append multiple messages via the aggregate method -/// - Finalise buffer creation with the finalise call. -template <typename ID> -class PayloadMerger -{ - public: - using MergeableId = ID; - using MessageMap = std::multimap<MergeableId, std::unique_ptr<FairMQMessage>>; - using PayloadExtractor = std::function<size_t(char**, char*, size_t)>; - using IdExtractor = std::function<MergeableId(std::unique_ptr<FairMQMessage>&)>; - using MergeCompletionCheker = std::function<bool(MergeableId, MessageMap&)>; - - /// Helper class to merge FairMQMessages sharing a user defined class of equivalence, - /// specified by @makeId. Completeness of the class of equivalence can be asserted by - /// the @checkIfComplete policy. It's also possible to specify a user defined way of - /// extracting the parts of the payload to be merged via the extractPayload method. - PayloadMerger(IdExtractor makeId, - MergeCompletionCheker checkIfComplete, - PayloadExtractor extractPayload = fullPayloadExtractor) - : mMakeId{makeId}, - mCheckIfComplete{checkIfComplete}, - mExtractPayload{extractPayload} - { - } - - /// Aggregates @payload to all the ones with the same id. - /// @return the id extracted from the payload via the constructor - /// specified id policy (mMakeId callback). - MergeableId aggregate(std::unique_ptr<FairMQMessage>& payload) - { - auto id = mMakeId(payload); - mPartsMap.emplace(std::make_pair(id, std::move(payload))); - return id; - } - - /// This merges a set of messages sharing the same id @id to a unique buffer - /// @out, so that it can be either consumed or sent as a message itself. - /// The decision on whether the merge must happen is done by the constructor - /// specified policy mCheckIfComplete which can, for example, decide - /// to merge when a certain number of subparts are reached. - /// Merging at the moment requires an extra copy, but in principle this could - /// be easily extended to support scatter - gather. - size_t finalise(char** out, MergeableId& id) - { - *out = nullptr; - if (mCheckIfComplete(id, mPartsMap) == false) { - return 0; - } - // If we are here, it means we can send the messages that belong - // to some predefined class of equivalence, identified by the MERGEABLE_ID, - // to the receiver. This is done by the following process: - // - // - Extract what we actually want to send (this might be data embedded inside the message itself) - // - Calculate the aggregate size of all the payloads. - // - Copy all the parts into a final payload - // - Create the header part - // - Create the payload part - // - Send - std::vector<std::pair<char*, size_t>> parts; - - size_t sum = 0; - auto range = mPartsMap.equal_range(id); - for (auto hi = range.first, he = range.second; hi != he; ++hi) { - std::unique_ptr<FairMQMessage>& payload = hi->second; - std::pair<char*, size_t> part; - part.second = mExtractPayload(&part.first, reinterpret_cast<char*>(payload->GetData()), payload->GetSize()); - parts.push_back(part); - sum += part.second; - } - - auto* payload = new char[sum](); - size_t offset = 0; - for (auto& part : parts) { - // Right now this does a copy. In principle this could be done with some sort of - // vectorized I/O - memcpy(payload + offset, part.first, part.second); - offset += part.second; - } - - mPartsMap.erase(id); - *out = payload; - return sum; - } - - // Helper method which leaves the payload untouched - static int64_t fullPayloadExtractor(char** payload, - char* buffer, - size_t bufferSize) - { - *payload = buffer; - return bufferSize; - } - - private: - IdExtractor mMakeId; - MergeCompletionCheker mCheckIfComplete; - PayloadExtractor mExtractPayload; - - MessageMap mPartsMap; -}; -} // namespace dataflow -} // namespace o2 - -#endif // PAYLOAD_MERGER_H diff --git a/Utilities/DataFlow/include/DataFlow/SubframeBuilderDevice.h b/Utilities/DataFlow/include/DataFlow/SubframeBuilderDevice.h deleted file mode 100644 index f4f6d2dc261d7..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/SubframeBuilderDevice.h +++ /dev/null @@ -1,139 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SUBFRAMEBUILDERDEVICE_H -#define SUBFRAMEBUILDERDEVICE_H - -/// @file SubframeBuilderDevice.h -/// @author Giulio Eulisse, Matthias Richter, Sandro Wenzel -/// @since 2017-02-07 -/// @brief Demonstrator device for a subframe builder - -#include "Headers/DataHeader.h" -#include "Headers/HeartbeatFrame.h" -#include "O2Device/O2Device.h" -#include "DataFlow/PayloadMerger.h" -#include "DataFlow/SubframeUtils.h" -#include <cstring> -#include <map> - -class FairMQParts; - -namespace o2 -{ -namespace data_flow -{ - -/// @class SubframeBuilderDevice -/// A demonstrator device for building of sub timeframes -/// -/// The scheme in the demonstrator chain assumes multiple data publishers -/// for different input chains of the CRU. The output of these publishers -/// is assembled to a sub timeframe by this device. -/// -/// The device implements the high-level API of the FairMQDevice -/// run loop. The registered method is called once per message -/// which is in itself a multipart message. It;s parts are passed -/// with a FairMQParts object. Not yet clear if this approach is -/// suited for the frame builder as it needs the data from multiple -/// channels with the same channel name. Depends on the validity of -/// the message data. Very likely the message parts are no longer -/// valid after leaving the handler method. But one could apply a -/// scheme with unique pointers and move semantics -/// -/// The duration should be with respect to a time constant, which in -/// itself needs to be configurable, now the time constant is -/// hard-coded microseconds -class SubframeBuilderDevice : public base::O2Device -{ - public: - using O2Message = o2::base::O2Message; - using SubframeId = o2::dataflow::SubframeId; - using Merger = dataflow::PayloadMerger<SubframeId>; - - static constexpr const char* OptionKeyInputChannelName = "in-chan-name"; - static constexpr const char* OptionKeyOutputChannelName = "out-chan-name"; - static constexpr const char* OptionKeyOrbitDuration = "orbit-duration"; - static constexpr const char* OptionKeyOrbitsPerTimeframe = "orbits-per-timeframe"; - static constexpr const char* OptionKeyInDataFile = "indatafile-name"; - static constexpr const char* OptionKeyDetector = "detector-name"; - static constexpr const char* OptionKeyFLPId = "flp-id"; - static constexpr const char* OptionKeyStripHBF = "strip-hbf"; - - // TODO: this is just a first mockup, remove it - // Default start time for all the producers is 8/4/1977 - // Timeframe start time will be ((N * duration) + start time) where - // N is the incremental number of timeframes being sent out. - // TODO: replace this with a unique Heartbeat from a common device. - static constexpr uint32_t DefaultOrbitDuration = 88924; - static constexpr uint32_t DefaultOrbitsPerTimeframe = 256; - static constexpr uint64_t DefaultHeartbeatStart = 229314600000000000LL; - - /// Default constructor - SubframeBuilderDevice(); - - /// Default destructor - ~SubframeBuilderDevice() final; - - protected: - /// overloading the InitTask() method of FairMQDevice - void InitTask() final; - - /// data handling method to be registered as handler in the - /// FairMQDevice API method OnData - /// The device base class handles the state loop in the RUNNING - /// state and calls the handler when receiving a message on one channel - /// The multiple parts included in one message are provided in the - /// FairMQParts object. - bool HandleData(FairMQParts& msgParts, int /*index*/); - - /// Build the frame and send it - /// For the moment a simple mockup composing a DataHeader and adding it - /// to the multipart message together with the SubframeMetadata as payload - bool BuildAndSendFrame(FairMQParts& parts); - - private: - uint32_t mOrbitsPerTimeframe; - // FIXME: lookup the actual value - uint32_t mOrbitDuration; - std::string mInputChannelName = ""; - std::string mOutputChannelName = ""; - size_t mFLPId = 0; - bool mStripHBF = false; - std::unique_ptr<Merger> mMerger; - - uint64_t mHeartbeatStart = DefaultHeartbeatStart; - - template <typename T> - size_t fakeHBHPayloadHBT(char** buffer, std::function<void(T&, int)> filler, int numOfElements) - { - // LOG(INFO) << "SENDING TPC PAYLOAD\n"; - auto payloadSize = sizeof(header::HeartbeatHeader) + sizeof(T) * numOfElements + sizeof(header::HeartbeatTrailer); - *buffer = new char[payloadSize]; - auto* hbh = reinterpret_cast<header::HeartbeatHeader*>(*buffer); - assert(payloadSize > 0); - assert(payloadSize - sizeof(header::HeartbeatTrailer) > 0); - auto* hbt = reinterpret_cast<header::HeartbeatTrailer*>(payloadSize - sizeof(header::HeartbeatTrailer)); - - T* payload = reinterpret_cast<T*>(*buffer + sizeof(header::HeartbeatHeader)); - for (int i = 0; i < numOfElements; ++i) { - new (payload + i) T; - // put some random toy time stamp to each cluster - filler(payload[i], i); - } - return payloadSize; - } -}; - -} // namespace data_flow -}; // namespace o2 -#endif diff --git a/Utilities/DataFlow/include/DataFlow/SubframeUtils.h b/Utilities/DataFlow/include/DataFlow/SubframeUtils.h deleted file mode 100644 index 69b5f3d4a6c24..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/SubframeUtils.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 DATAFLOW_SUBFRAMEUTILS_H -#define DATAFLOW_SUBFRAMEUTILS_H - -#include <tuple> -#include <cstddef> -#include "Headers/HeartbeatFrame.h" - -namespace o2 -{ -namespace dataflow -{ - -int64_t extractDetectorPayloadStrip(char** payload, char* buffer, size_t bufferSize) -{ - *payload = buffer + sizeof(o2::header::HeartbeatHeader); - return bufferSize - sizeof(o2::header::HeartbeatHeader) - sizeof(o2::header::HeartbeatTrailer); -} - -struct SubframeId { - size_t timeframeId; - size_t socketId; - - // operator needed for the equal_range algorithm/ multimap method - bool operator<(const SubframeId& rhs) const - { - return std::tie(timeframeId, socketId) < std::tie(rhs.timeframeId, rhs.socketId); - } -}; - -SubframeId makeIdFromHeartbeatHeader(const header::HeartbeatHeader& header, size_t socketId, size_t orbitsPerTimeframe) -{ - SubframeId id = { - .timeframeId = header.orbit / orbitsPerTimeframe, - .socketId = socketId}; - return id; -} - -} /* namespace dataflow */ -} /* namespace o2 */ - -#endif // DATAFLOW_SUBFRAMEUTILS_H diff --git a/Utilities/DataFlow/include/DataFlow/TimeframeParser.h b/Utilities/DataFlow/include/DataFlow/TimeframeParser.h deleted file mode 100644 index 590982c1dfb04..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/TimeframeParser.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TIMEFRAME_PARSER_H_ -#define TIMEFRAME_PARSER_H_ - -#include <iosfwd> -#include <functional> - -class FairMQParts; - -namespace o2 -{ -namespace data_flow -{ - -/// An helper function which takes a std::istream pointing -/// to a naively persisted timeframe and pumps its parts to -/// FairMQParts, ready to be shipped via FairMQ. -void streamTimeframe(std::istream& stream, - std::function<void(FairMQParts& parts, char* buffer, size_t size)> onAddPart, - std::function<void(FairMQParts& parts)> onSend); - -void streamTimeframe(std::ostream& stream, FairMQParts& parts); - -} // namespace data_flow -} // namespace o2 - -#endif // TIMEFRAME_PARSER_H diff --git a/Utilities/DataFlow/include/DataFlow/TimeframeReaderDevice.h b/Utilities/DataFlow/include/DataFlow/TimeframeReaderDevice.h deleted file mode 100644 index f4359b6f21142..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/TimeframeReaderDevice.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TIMEFRAME_READER_H_ -#define ALICEO2_TIMEFRAME_READER_H_ - -#include "O2Device/O2Device.h" -#include <fstream> - -namespace o2 -{ -namespace data_flow -{ - -/// A device which writes to file the timeframes. -class TimeframeReaderDevice : public base::O2Device -{ - public: - static constexpr const char* OptionKeyOutputChannelName = "output-channel-name"; - static constexpr const char* OptionKeyInputFileName = "input-file"; - - /// Default constructor - TimeframeReaderDevice(); - - /// Default destructor - ~TimeframeReaderDevice() override = default; - - void InitTask() final; - - protected: - /// Overloads the ConditionalRun() method of FairMQDevice - bool ConditionalRun() final; - - std::string mOutChannelName; - std::string mInFileName; - std::fstream mFile; - std::vector<std::string> mSeen; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/Utilities/DataFlow/include/DataFlow/TimeframeValidatorDevice.h b/Utilities/DataFlow/include/DataFlow/TimeframeValidatorDevice.h deleted file mode 100644 index 453b0ea669d5b..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/TimeframeValidatorDevice.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TIMEFRAMEVALIDATOR_H_ -#define ALICEO2_TIMEFRAMEVALIDATOR_H_ - -#include "O2Device/O2Device.h" - -namespace o2 -{ -namespace data_flow -{ - -/// A validating device for time frame data (coming from EPN) -class TimeframeValidatorDevice : public base::O2Device -{ - public: - static constexpr const char* OptionKeyInputChannelName = "input-channel-name"; - - /// Default constructor - TimeframeValidatorDevice(); - - /// Default destructor - ~TimeframeValidatorDevice() override = default; - - void InitTask() final; - - protected: - /// Overloads the Run() method of FairMQDevice - void Run() final; - - std::string mInChannelName; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/Utilities/DataFlow/include/DataFlow/TimeframeWriterDevice.h b/Utilities/DataFlow/include/DataFlow/TimeframeWriterDevice.h deleted file mode 100644 index 73fceed644d48..0000000000000 --- a/Utilities/DataFlow/include/DataFlow/TimeframeWriterDevice.h +++ /dev/null @@ -1,59 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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_TIMEFRAME_WRITER_DEVICE_H_ -#define ALICEO2_TIMEFRAME_WRITER_DEVICE_H_ - -#include "O2Device/O2Device.h" -#include <fstream> - -namespace o2 -{ -namespace data_flow -{ - -/// A device which writes to file the timeframes. -class TimeframeWriterDevice : public base::O2Device -{ - public: - static constexpr const char* OptionKeyInputChannelName = "input-channel-name"; - static constexpr const char* OptionKeyOutputFileName = "output-file"; - static constexpr const char* OptionKeyMaxTimeframesPerFile = "max-timeframes-per-file"; - static constexpr const char* OptionKeyMaxFileSize = "max-file-size"; - static constexpr const char* OptionKeyMaxFiles = "max-files"; - - /// Default constructor - TimeframeWriterDevice(); - - /// Default destructor - ~TimeframeWriterDevice() override = default; - - void InitTask() final; - - /// The PostRun will trigger saving the file to disk - void PostRun() final; - - protected: - /// Overloads the Run() method of FairMQDevice - void Run() final; - - std::string mInChannelName; - std::string mOutFileName; - std::fstream mFile; - size_t mMaxTimeframes; - size_t mMaxFileSize; - size_t mMaxFiles; - size_t mFileCount; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/Utilities/DataFlow/run/CreateSetup.py b/Utilities/DataFlow/run/CreateSetup.py deleted file mode 100755 index 0ebe8f8f38a82..0000000000000 --- a/Utilities/DataFlow/run/CreateSetup.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/python -# a very simple python script to -# create and scale a topology/conf + runfile for a variable list of -# HeartbeatSampler + DataPublisher + ... + FLP + EPN -# author: S. Wenzel -#FIXME: decide whether to config for xterm or tmux - -import json - -NumFLP = 2 -NumDP = NumFLP # in principle we could have different number of data publishers -NumEPN = 4 -EPNStartSocket = 6000 -HeartbeatSocket = 5000 -HeartbeatBaseName = "o2-heartbeat-sampler" -EPNBaseName = "epnReceiver" -SFBStartSocket = 5500 -FLPStartSocket = 4000 -ValidatorStartSocket = 7000 -FLPBaseName = "flpSender" -DPBaseName = "o2-datapublisher-device" -SFBBaseName = "subframeBuilder" -tcpbase = "tcp://127.0.0.1:" -detectors = ["TPC","ITS"] -datadescription = ["TPCCLUSTER", "ITSRAW"] - -configfilename = "customconfig.json" -runscriptfilename = "customrunscript.sh" - -# some potentially reusable API to construct devices -def initDevice(name): - device = {} - device["id"] = name - device["channels"] = [] # empty list for device - return device - -# creates one channel; empty sockets -def initChannel(name, type, connect): - channel = {} - channel["name"] = name - channel["type"] = type - channel["method"] = connect - channel["sockets"] = [] - return channel - -def initAddress(url): - return {"address":url} - -def addChannel(device, channel): - device["channels"].append(channel) - -def addAddress(channel, address): - channel["sockets"].append(address) - -# adds a custom key:value pair to a dictionary -def addPair(element, key, value): - element[key]=value - -# code describing our devices follows - -def writeOneDP(id): - device = initDevice(DPBaseName+str(id)) - inchannel = initChannel("input","sub","connect") - addAddress(inchannel, initAddress(tcpbase + str(HeartbeatSocket))) - outchannel = initChannel("output","pub","bind") - addAddress(outchannel, initAddress(tcpbase + str(SFBStartSocket + id))) - addPair(outchannel, "sndBufSize", "10") - addChannel(device, inchannel) - addChannel(device, outchannel) - return device - -def writeOneSFB(id): - device = initDevice(SFBBaseName + str(id)) - # the input channel - inchannel = initChannel("input", "sub", "connect") - addAddress(inchannel, initAddress(tcpbase + str(SFBStartSocket+id))) - addPair(inchannel, "sndBufSize", "10") - # the output channel - outchannel = initChannel("output", "pub", "bind") - addAddress(outchannel, initAddress(tcpbase + str(FLPStartSocket+id))) - addPair(outchannel, "sndBufSize", "10") - addChannel(device,inchannel) - addChannel(device,outchannel) - return device - -def writeHBSamplerDevice(): - device = initDevice(HeartbeatBaseName) - outchannel = initChannel("output", "pub", "bind") - addAddress(outchannel, initAddress(tcpbase + str(HeartbeatSocket))) - addChannel(device, outchannel) - return device - -# write timeframeValidator -def writeTFV(): - device = initDevice("timeframeValidator") - channel = initChannel("input", "sub", "connect") - addAddress(channel, initAddress(tcpbase + str(ValidatorStartSocket))) - addChannel(device, channel) - return device - -# write single flp device -def writeOneFLP(id): - device = initDevice(FLPBaseName + str(id)) - inchannel = initChannel("input","sub","connect") - addAddress(inchannel, initAddress(tcpbase + str(FLPStartSocket+id))) - outchannel = initChannel("output", "push", "connect") - # write all EPN addresses - for i in range(0,NumEPN): - addAddress(outchannel, initAddress(tcpbase + str(EPNStartSocket+i))) - addChannel(device, inchannel) - addChannel(device, outchannel) - return device - -def writeOneEPN(id): - # write a single EPN device - device = initDevice(EPNBaseName + str(id)) - inchannel = initChannel("input", "pull", "bind") - addAddress(inchannel, initAddress(tcpbase + str(EPNStartSocket+id))) - # the ack channel - ackchannel = initChannel("ack", "push", "connect") - addAddress(ackchannel, initAddress("tcp://127.0.0.1:5990")) - addPair(ackchannel, "ratelogging", "0") - # the output channel (time frame validator) - outchannel = initChannel("output", "pub", "bind") - addAddress(outchannel, initAddress(tcpbase + str(ValidatorStartSocket))) - addChannel(device, inchannel) - addChannel(device, ackchannel) - addChannel(device, outchannel) - return device - -def addFLPDevices(devicelist): - for i in range(0,NumFLP): - devicelist.append(writeOneDP(i)) - devicelist.append(writeOneSFB(i)) - devicelist.append(writeOneFLP(i)) - -def addEPNDevices(devicelist): - for i in range(0,NumEPN): - devicelist.append(writeOneEPN(i)) - -def getConf(): - conf = {} - conf["fairMQOptions"] = {"devices" : []} - devicelist = conf["fairMQOptions"]["devices"] - # append all the the devices to this list - addFLPDevices(devicelist) - addEPNDevices(devicelist) - devicelist.append(writeTFV()) - devicelist.append(writeHBSamplerDevice()) - return conf - -def writeJSONConf(): - print "creating JOSN config " + configfilename - with open(configfilename, 'w') as f: - f.write(json.dumps(getConf())) - -def writeRunScript(): - xtermcommand = "xterm -hold -e " - dumpstring = [] - # treat data publishers - for i in range(0, NumDP): - # we might need to give information about detector to DP as well - command = "o2-datapublisher-device --id DataPublisherDevice" + str(i) - command += " --data-description " + datadescription[i%2] - command += " --mq-config " + configfilename + " --in-chan-name input --out-chan-name output &" - dumpstring.append(xtermcommand + command) - - # treat sfbdevices - for i in range(0, NumFLP): - command = "o2-subframebuilder-device --id subframeBuilder" + str(i) - command += " --mq-config " + configfilename + " --detector " + detectors[i%2] + " &" - dumpstring.append(xtermcommand + command) - # treat flpSender - for i in range(0, NumFLP): - flpCommand = "flpSender --id flpSender" + str(i) - flpCommand += " --mq-config " + configfilename + " --in-chan-name input" - flpCommand += " --out-chan-name output --num-epns " + str(NumEPN) + " --flp-index " + str(i) - flpCommand += "&" - dumpstring.append(xtermcommand + flpCommand) - # treat EPN receiver - for i in range(0, NumEPN): - epnCommand = "epnReceiver --id epnReceiver" + str(i) - epnCommand += " --mq-config " + configfilename + " --in-chan-name input --out-chan-name output" - epnCommand += " --num-flps " + str(NumFLP) + "&" - dumpstring.append(xtermcommand + epnCommand) - # treat timeFrameValidator - command = "o2-timeframe-validator-device --id timeframeValidator --mq-config " + configfilename + " customconfig.json --input-channel-name input &" - dumpstring.append(xtermcommand + command) - - # treat heartbeatsampler - command = "o2-heartbeat-sampler --id heartbeatSampler --mq-config " + configfilename + " --out-chan-name output &" - dumpstring.append(xtermcommand + command) - - print "creating runscript " + runscriptfilename - with open(runscriptfilename, 'w') as f: - f.write('\n'.join(dumpstring)) - -if __name__ == '__main__': - writeJSONConf() - writeRunScript() diff --git a/Utilities/DataFlow/run/confBasicSetup.json b/Utilities/DataFlow/run/confBasicSetup.json deleted file mode 100644 index 4fe1a1c633d61..0000000000000 --- a/Utilities/DataFlow/run/confBasicSetup.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "fairMQOptions": - { - "devices": - [ - { - "id": "subframeBuilder", - "channels": - [ - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5550" } - ], - "sndBufSize": "10" - }] - }, - - { - "id": "flpSender", - "channels": - [{ - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5550" } - ], - "rcvBufSize": "10" - }, - { - "name": "output", - "type": "push", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5570" } - ], - "sndBufSize": "10" - }] - }, - - { - "id": "epnReceiver", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5570"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, - - { - "id": "timeframeValidator", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5580"} - ], - "sndBufSize": "10" - } - ] - } - - ] - } -} diff --git a/Utilities/DataFlow/run/confComplexSetup.json b/Utilities/DataFlow/run/confComplexSetup.json deleted file mode 100644 index cbb69fb7434ce..0000000000000 --- a/Utilities/DataFlow/run/confComplexSetup.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "fairMQOptions": - { - "devices": - [ - { - "id": "subframeBuilderTPC", - "channels": - [ - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5550" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "flpSenderTPC", - "channels": - [{ - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5550" } - ], - "rcvBufSize": "10" - }, - { - "name": "output", - "type": "push", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5570" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "subframeBuilderITS", - "channels": - [ - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5551" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "flpSenderITS", - "channels": - [{ - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5551" } - ], - "rcvBufSize": "10" - }, - { - "name": "output", - "type": "push", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5570" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "epnReceiver", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5570"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, - { - "id": "timeframeValidator", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5580"} - ], - "sndBufSize": "10" - } - ] - } - ] - } -} diff --git a/Utilities/DataFlow/run/confComplexSetup2.json b/Utilities/DataFlow/run/confComplexSetup2.json deleted file mode 100644 index 3c527d7616713..0000000000000 --- a/Utilities/DataFlow/run/confComplexSetup2.json +++ /dev/null @@ -1,345 +0,0 @@ -{ - "fairMQOptions": - { - "devices": - [ - { - "id": "heartbeatSampler", - "channels": - [ - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5450" } - ], - "sndBufSize": "10" - } - ] - }, - { - "id": "DataPublisherDeviceTPC", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets":[ - {"address": "tcp://127.0.0.1:5450"} - ] - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:6550" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "DataPublisherDeviceITS", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets":[ - {"address": "tcp://127.0.0.1:5450"} - ] - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:7550" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "subframeBuilderTPC", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets":[ - {"address": "tcp://127.0.0.1:6550"} - ] - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5550" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "flpSenderTPC", - "channels": - [ - { - "name": "input-channel-name", - "type": "sub", - "method": "connect", - "sockets":[ - {"address": "tcp://127.0.0.1:5450"} - ] - }, - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5550" } - ], - "rcvBufSize": "10" - }, - { - "name": "output", - "type": "push", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5570" }, - { "address": "tcp://127.0.0.1:5571" }, - { "address": "tcp://127.0.0.1:5572" }, - { "address": "tcp://127.0.0.1:5573" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "subframeBuilderITS", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets":[ - {"address": "tcp://127.0.0.1:7550"} - ] - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5551" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "flpSenderITS", - "channels": - [{ - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5551" } - ], - "rcvBufSize": "10" - }, - { - "name": "output", - "type": "push", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5570" }, - { "address": "tcp://127.0.0.1:5571" }, - { "address": "tcp://127.0.0.1:5572" }, - { "address": "tcp://127.0.0.1:5573" } - ], - "sndBufSize": "10" - }] - }, - { - "id": "epnReceiver1", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5570"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, -{ - "id": "epnReceiver1", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5570"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, - { - "id": "epnReceiver2", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5571"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, - { - "id": "epnReceiver3", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5572"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, -{ - "id": "epnReceiver4", - "channels": - [ - { - "name": "input", - "type": "pull", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5573"} - ], - "sndBufSize": "10" - }, - { - "name": "ack", - "type": "push", - "method": "connect", - "address": "tcp://127.0.0.1:5990", - "rateLogging": "0" - }, - { - "name": "output", - "type": "pub", - "method": "bind", - "address": "tcp://127.0.0.1:5580", - "sndBufSize": "10" - } - ] - }, - { - "id": "timeframeValidator", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5580"} - ], - "sndBufSize": "10" - } - ] - } - ] - } -} diff --git a/Utilities/DataFlow/run/confFakeTimeframe.json b/Utilities/DataFlow/run/confFakeTimeframe.json deleted file mode 100644 index ae1f686087f25..0000000000000 --- a/Utilities/DataFlow/run/confFakeTimeframe.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "fairMQOptions": - { - "devices": - [ - { - "id": "FakeTimeframeGeneratorDevice", - "channels": - [ - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://*:5550" } - ], - "sndBufSize": "10" - } - ] - }, - { - "id": "TimeframeWriterDevice", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5550"} - ], - "sndBufSize": "10" - } - ] - }, - { - "id": "TimeframeReaderDevice", - "channels": - [ - { - "name": "output", - "type": "pub", - "method": "bind", - "sockets": - [ - { "address": "tcp://127.0.0.1:5551"} - ], - "sndBufSize": "10" - } - ] - }, - { - "id": "TimeframeValidatorDevice", - "channels": - [ - { - "name": "input", - "type": "sub", - "method": "connect", - "sockets": - [ - { "address": "tcp://127.0.0.1:5551"} - ], - "sndBufSize": "10" - } - ] - } - ] - } -} diff --git a/Utilities/DataFlow/run/startBasicSetup.sh b/Utilities/DataFlow/run/startBasicSetup.sh deleted file mode 100755 index e69106eab5dc5..0000000000000 --- a/Utilities/DataFlow/run/startBasicSetup.sh +++ /dev/null @@ -1,13 +0,0 @@ -# simple start script to lauch a basic setup -# Prerequisites: -# - expects the configuration file to be in the working directory -# - O2 bin and lib set n the shell environment - - -xterm -geometry 80x25+0+0 -hold -e epnReceiver --id epnReceiver --mq-config confBasicSetup.json --in-chan-name input --out-chan-name output --num-flps 1 & - -xterm -geometry 80x25+500+0 -hold -e flpSender --id flpSender --mq-config confBasicSetup.json --in-chan-name input --out-chan-name output --num-epns 1 & - -xterm -geometry 80x25+1000+0 -hold -e o2-subframebuilder-device --id subframeBuilder --mq-config confBasicSetup.json --self-triggered & - -xterm -geometry 80x25+1500+0 -hold -e o2-timeframe-validator-device --id timeframeValidator --mq-config confBasicSetup.json --input-channel-name input & diff --git a/Utilities/DataFlow/run/startComplexSetup.sh b/Utilities/DataFlow/run/startComplexSetup.sh deleted file mode 100755 index 6280269be5d16..0000000000000 --- a/Utilities/DataFlow/run/startComplexSetup.sh +++ /dev/null @@ -1,24 +0,0 @@ -# simple start script to launch a more complex setup -# with 2 data publishers (inside subframebuilder) + 2 attached flpSenders -# Prerequisites: -# - expects the configuration file to be in the working directory -# - O2 bin and lib set n the shell environment - - -# we have just one epn and 2 flps -xterm -geometry 80x25+0+0 -hold -e epnReceiver --id epnReceiver --mq-config confComplexSetup.json --in-chan-name input --out-chan-name output --num-flps 2 & - -# this is the flp for TPC -xterm -geometry 80x25+500+0 -hold -e flpSender --id flpSenderTPC --mq-config confComplexSetup.json --in-chan-name input --out-chan-name output --num-epns 1 --flp-index 0 & - -# this is the flp for ITS -xterm -geometry 80x25+800+0 -hold -e flpSender --id flpSenderITS --mq-config confComplexSetup.json --in-chan-name input --out-chan-name output --num-epns 1 --flp-index 1 & - -# this is the subtimeframe publisher for TPC -xterm -geometry 80x25+0+500 -hold -e o2-subframebuilder-device --id subframeBuilderTPC --mq-config confComplexSetup.json --self-triggered --detector TPC & - -# this is the subtimeframe publisher for ITS -xterm -geometry 80x25+500+500 -hold -e o2-subframebuilder-device --id subframeBuilderITS --mq-config confComplexSetup.json --self-triggered --detector ITS & - -# consumer and validator of the full EPN time frame -xterm -geometry 80x25+800+500 -hold -e o2-timeframe-validator-device --id timeframeValidator --mq-config confComplexSetup.json --input-channel-name input & diff --git a/Utilities/DataFlow/run/startComplexSetup2.sh b/Utilities/DataFlow/run/startComplexSetup2.sh deleted file mode 100755 index a0c343658ad8a..0000000000000 --- a/Utilities/DataFlow/run/startComplexSetup2.sh +++ /dev/null @@ -1,36 +0,0 @@ -# simple start script to launch a more complex setup -# with 2 data publishers (inside subframebuilder) + 2 attached flpSenders + 4 EPNS -# Prerequisites: -# - expects the configuration file to be in the working directory -# - O2 bin and lib set n the shell environment - -# it would be nice having a script that generates the configuration file for N FLP and M EPNS - -# Start one HBSampler device -xterm -geometry 80x25+0+0 -hold -e o2-heartbeat-sampler --id heartbeatSampler --mq-config confComplexSetup2.json --out-chan-name output & - -# Data publishers -xterm -geometry 80x25+500+0 -hold -e o2-datapublisher-device --id DataPublisherDeviceTPC --mq-config confComplexSetup2.json --in-chan-name input --out-chan-name output --data-description TPCCLUSTER & -xterm -geometry 80x25+500+400 -hold -e o2-datapublisher-device --id DataPublisherDeviceITS --mq-config confComplexSetup2.json --in-chan-name input --out-chan-name output --data-description ITSRAW & - - -# this is the subtimeframe publisher for TPC -xterm -geometry 80x25+1000+0 -hold -e o2-subframebuilder-device --id subframeBuilderTPC --mq-config confComplexSetup2.json --detector TPC & - -# this is the subtimeframe publisher for ITS -xterm -geometry 80x25+1000+400 -hold -e o2-subframebuilder-device --id subframeBuilderITS --mq-config confComplexSetup2.json --detector ITS & - -# this is the flp for TPC -xterm -geometry 80x25+1500+0 -hold -e o2-flp-sender-device --id flpSenderTPC --mq-config confComplexSetup2.json --in-chan-name input --out-chan-name output --num-epns 4 --flp-index 0 & - -# this is the flp for ITS -xterm -geometry 80x25+1500+400 -hold -e o2-flp-sender-device --id flpSenderITS --mq-config confComplexSetup2.json --in-chan-name input --out-chan-name output --num-epns 4 --flp-index 1 & - -# we have 4 epn and 2 flps -xterm -geometry 80x25+2000+0 -hold -e o2-epn-receiver-device --id epnReceiver1 --mq-config confComplexSetup2.json --buffer-timeout 10000 --in-chan-name input --out-chan-name output --num-flps 2 & -xterm -geometry 80x25+2000+400 -hold -e o2-epn-receiver-device --id epnReceiver2 --mq-config confComplexSetup2.json --buffer-timeout 10000 --in-chan-name input --out-chan-name output --num-flps 2 & -xterm -geometry 80x25+2000+800 -hold -e o2-epn-receiver-device --id epnReceiver3 --mq-config confComplexSetup2.json --buffer-timeout 10000 --in-chan-name input --out-chan-name output --num-flps 2 & -xterm -geometry 80x25+2000+1200 -hold -e o2-epn-receiver-device --id epnReceiver4 --mq-config confComplexSetup2.json --buffer-timeout 10000 --in-chan-name input --out-chan-name output --num-flps 2 & - -# consumer and validator of the full EPN time frame -xterm -geometry 80x25+2000+500 -hold -e o2-timeframe-validator-device --id timeframeValidator --mq-config confComplexSetup2.json --input-channel-name input & diff --git a/Utilities/DataFlow/run/startTimeframeExample.sh b/Utilities/DataFlow/run/startTimeframeExample.sh deleted file mode 100755 index f9c108825cc3a..0000000000000 --- a/Utilities/DataFlow/run/startTimeframeExample.sh +++ /dev/null @@ -1,4 +0,0 @@ -xterm -geometry 80x25+0+0 -hold -e FakeTimeframeGeneratorDevice --id FakeTimeframeGeneratorDevice --mq-config confFakeTimeframe.json --output-channel-name output & -xterm -geometry 80x25+0+0 -hold -e o2-timeframe-writer-device --id TimeframeWriterDevice --mq-config confFakeTimeframe.json --input-channel-name input --max-timeframes 1 --output-file data.o2tf & -#xterm -geometry 80x25+0+0 -hold -e o2-timeframe-reader-device --id TimeframeReaderDevice --mq-config confFakeTimeframe.json --input-file data.o2tf --output-channel-name output & -#xterm -geometry 80x25+0+0 -hold -e o2-timeframe-validator-device --id TimeframeValidatorDevice --mq-config confFakeTimeframe.json --input-channel-name input & diff --git a/Utilities/DataFlow/src/EPNReceiverDevice.cxx b/Utilities/DataFlow/src/EPNReceiverDevice.cxx deleted file mode 100644 index 8c5649680e31d..0000000000000 --- a/Utilities/DataFlow/src/EPNReceiverDevice.cxx +++ /dev/null @@ -1,183 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <cstddef> // size_t -#include <fstream> // writing to file (DEBUG) -#include <cstring> -#include <iomanip> - -#include <FairMQLogger.h> -#include <options/FairMQProgOptions.h> - -#include "DataFlow/EPNReceiverDevice.h" -#include "Headers/DataHeader.h" -#include "Headers/SubframeMetadata.h" -#include "O2Device/Compatibility.h" -#include "TimeFrame/TimeFrame.h" - -#include <iomanip> - -using namespace std; -using namespace std::chrono; -using namespace o2::devices; -using SubframeMetadata = o2::data_flow::SubframeMetadata; -using TPCTestPayload = o2::data_flow::TPCTestPayload; -using TPCTestCluster = o2::data_flow::TPCTestCluster; -using IndexElement = o2::dataformats::IndexElement; - -void EPNReceiverDevice::InitTask() -{ - mNumFLPs = GetConfig()->GetValue<int>("num-flps"); - mBufferTimeoutInMs = GetConfig()->GetValue<int>("buffer-timeout"); - mTestMode = GetConfig()->GetValue<int>("test-mode"); - mInChannelName = GetConfig()->GetValue<string>("in-chan-name"); - mOutChannelName = GetConfig()->GetValue<string>("out-chan-name"); - mAckChannelName = GetConfig()->GetValue<string>("ack-chan-name"); -} - -void EPNReceiverDevice::PrintBuffer(const unordered_map<uint16_t, TFBuffer>& buffer) const -{ - string header = "===== "; - - for (int i = 1; i <= mNumFLPs; ++i) { - stringstream out; - out << i % 10; - header += out.str(); - //i > 9 ? header += " " : header += " "; - } - LOG(INFO) << header; - - for (auto& it : buffer) { - string stars = ""; - for (unsigned int j = 1; j <= (it.second).parts.Size(); ++j) { - stars += "*"; - } - LOG(INFO) << setw(4) << it.first << ": " << stars; - } -} - -void EPNReceiverDevice::DiscardIncompleteTimeframes() -{ - auto it = mTimeframeBuffer.begin(); - - while (it != mTimeframeBuffer.end()) { - if (duration_cast<milliseconds>(steady_clock::now() - (it->second).start).count() > mBufferTimeoutInMs) { - LOG(WARN) << "Timeframe #" << it->first << " incomplete after " << mBufferTimeoutInMs << " milliseconds, discarding"; - mDiscardedSet.insert(it->first); - mTimeframeBuffer.erase(it++); - LOG(WARN) << "Number of discarded timeframes: " << mDiscardedSet.size(); - } else { - // LOG(INFO) << "Timeframe #" << it->first << " within timeout, buffering..."; - ++it; - } - } -} - -void EPNReceiverDevice::Run() -{ - uint16_t id = 0; // holds the timeframe id of the currently arrived sub-timeframe. - - FairMQChannel& ackOutChannel = fChannels.at(mAckChannelName).at(0); - - // Simple multi timeframe index - using TimeframeId = int; - using FlpId = int; - std::multimap<TimeframeId, IndexElement> index; - std::multimap<TimeframeId, FlpId> flpIds; - - while (compatibility::FairMQ13<FairMQDevice>::IsRunning(this)) { - FairMQParts subtimeframeParts; - if (Receive(subtimeframeParts, mInChannelName, 0, 100) <= 0) - continue; - - assert(subtimeframeParts.Size() >= 2); - - const auto* dh = o2::header::get<header::DataHeader*>(subtimeframeParts.At(0)->GetData()); - assert(strncmp(dh->dataDescription.str, "SUBTIMEFRAMEMD", 16) == 0); - SubframeMetadata* sfm = reinterpret_cast<SubframeMetadata*>(subtimeframeParts.At(1)->GetData()); - id = o2::data_flow::timeframeIdFromTimestamp(sfm->startTime, sfm->duration); - auto flpId = sfm->flpIndex; - - if (mDiscardedSet.find(id) == mDiscardedSet.end()) { - if (mTimeframeBuffer.find(id) == mTimeframeBuffer.end()) { - // if this is the first part with this ID, save the receive time. - mTimeframeBuffer[id].start = steady_clock::now(); - } - flpIds.insert(std::make_pair(id, flpId)); - LOG(INFO) << "Timeframe ID " << id << " for startTime " << sfm->startTime << "\n"; - // If the received ID has not previously been discarded, store - // the data part in the buffer For the moment we just concatenate - // the subtimeframes and add an index for their description at - // the end. Given every second part is a data header we skip - // every two parts to populate the index. Moreover we know that - // the SubframeMetadata is always in the second part, so we can - // extract the flpId from there. - for (size_t i = 0; i < subtimeframeParts.Size(); ++i) { - if (i % 2 == 0) { - const auto* adh = o2::header::get<header::DataHeader*>(subtimeframeParts.At(i)->GetData()); - auto ie = std::make_pair(*adh, index.count(id) * 2); - index.insert(std::make_pair(id, ie)); - } - mTimeframeBuffer[id].parts.AddPart(move(subtimeframeParts.At(i))); - } - } else { - // if received ID has been previously discarded. - LOG(WARN) << "Received part from an already discarded timeframe with id " << id; - } - - if (flpIds.count(id) == mNumFLPs) { - LOG(INFO) << "Timeframe " << id << " complete. Publishing.\n"; - o2::header::DataHeader tih; - std::vector<IndexElement> flattenedIndex; - - tih.dataDescription = o2::header::DataDescription("TIMEFRAMEINDEX"); - tih.dataOrigin = o2::header::DataOrigin("EPN"); - tih.subSpecification = 0; - tih.payloadSize = index.count(id) * sizeof(flattenedIndex.front()); - void* indexData = malloc(tih.payloadSize); - auto indexRange = index.equal_range(id); - for (auto ie = indexRange.first; ie != indexRange.second; ++ie) { - flattenedIndex.push_back(ie->second); - } - memcpy(indexData, flattenedIndex.data(), tih.payloadSize); - - mTimeframeBuffer[id].parts.AddPart(NewSimpleMessage(tih)); - mTimeframeBuffer[id].parts.AddPart(NewMessage( - indexData, tih.payloadSize, - [](void* data, void* hint) { free(data); }, nullptr)); - // LOG(INFO) << "Collected all parts for timeframe #" << id; - // when all parts are collected send then to the output channel - Send(mTimeframeBuffer[id].parts, mOutChannelName); - LOG(INFO) << "Index count for " << id << " " << index.count(id) << "\n"; - index.erase(id); - LOG(INFO) << "Index count for " << id << " " << index.count(id) << "\n"; - flpIds.erase(id); - - if (mTestMode > 0) { - // Send an acknowledgement back to the sampler to measure the round trip time - unique_ptr<FairMQMessage> ack(NewMessage(sizeof(uint16_t))); - memcpy(ack->GetData(), &id, sizeof(uint16_t)); - - if (ackOutChannel.Send(ack, 0) <= 0) { - LOG(ERROR) << "Could not send acknowledgement without blocking"; - } - } - - mTimeframeBuffer.erase(id); - } - - // LOG(WARN) << "Buffer size: " << fTimeframeBuffer.size(); - - // Check if any incomplete timeframes in the buffer are older than - // timeout period, and discard them if they are - // QUESTION: is this really what we want to do? - DiscardIncompleteTimeframes(); - } -} diff --git a/Utilities/DataFlow/src/FLPSenderDevice.cxx b/Utilities/DataFlow/src/FLPSenderDevice.cxx deleted file mode 100644 index 1cecd8ed3d62a..0000000000000 --- a/Utilities/DataFlow/src/FLPSenderDevice.cxx +++ /dev/null @@ -1,100 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <cstdint> -#include <cassert> - -#include <FairMQLogger.h> -#include <FairMQMessage.h> -#include <options/FairMQProgOptions.h> - -#include "Headers/DataHeader.h" -#include "Headers/SubframeMetadata.h" -#include "DataFlow/FLPSenderDevice.h" -#include "O2Device/Compatibility.h" - -using namespace std; -using namespace std::chrono; -using namespace o2::devices; -using SubframeMetadata = o2::data_flow::SubframeMetadata; - -void FLPSenderDevice::InitTask() -{ - mIndex = GetConfig()->GetValue<int>("flp-index"); - mEventSize = GetConfig()->GetValue<int>("event-size"); - mNumEPNs = GetConfig()->GetValue<int>("num-epns"); - mTestMode = GetConfig()->GetValue<int>("test-mode"); - mSendOffset = GetConfig()->GetValue<int>("send-offset"); - mSendDelay = GetConfig()->GetValue<int>("send-delay"); - mInChannelName = GetConfig()->GetValue<string>("in-chan-name"); - mOutChannelName = GetConfig()->GetValue<string>("out-chan-name"); -} - -void FLPSenderDevice::Run() -{ - // base buffer, to be copied from for every timeframe body (zero-copy) - FairMQMessagePtr baseMsg(NewMessage(mEventSize)); - - // store the channel reference to avoid traversing the map on every loop iteration - //FairMQChannel& dataInChannel = fChannels.at(fInChannelName).at(0); - - while (compatibility::FairMQ13<FairMQDevice>::IsRunning(this)) { - // - Get the SubtimeframeMetadata - // - Add the current FLP id to the SubtimeframeMetadata - // - Forward to the EPN the whole subtimeframe - FairMQParts subtimeframeParts; - if (Receive(subtimeframeParts, mInChannelName, 0, 100) <= 0) - continue; - - assert(subtimeframeParts.Size() != 0); - assert(subtimeframeParts.Size() >= 2); - const auto* dh = o2::header::get<header::DataHeader*>(subtimeframeParts.At(0)->GetData()); - assert(strncmp(dh->dataDescription.str, "SUBTIMEFRAMEMD", 16) == 0); - - SubframeMetadata* sfm = reinterpret_cast<SubframeMetadata*>(subtimeframeParts.At(1)->GetData()); - sfm->flpIndex = mIndex; - - mArrivalTime.push(steady_clock::now()); - mSTFBuffer.push(move(subtimeframeParts)); - - // if offset is 0 - send data out without staggering. - assert(mSTFBuffer.size() > 0); - - if (mSendOffset == 0 && mSTFBuffer.size() > 0) { - sendFrontData(); - } else if (mSTFBuffer.size() > 0) { - if (duration_cast<milliseconds>(steady_clock::now() - mArrivalTime.front()).count() >= (mSendDelay * mSendOffset)) { - sendFrontData(); - } else { - // LOG(INFO) << "buffering..."; - } - } - } -} - -inline void FLPSenderDevice::sendFrontData() -{ - SubframeMetadata* sfm = static_cast<SubframeMetadata*>(mSTFBuffer.front().At(1)->GetData()); - uint16_t currentTimeframeId = o2::data_flow::timeframeIdFromTimestamp(sfm->startTime, sfm->duration); - if (mLastTimeframeId != -1) { - if (currentTimeframeId == mLastTimeframeId) { - LOG(ERROR) << "Sent same consecutive timeframe ids\n"; - } - } - mLastTimeframeId = currentTimeframeId; - - // for which EPN is the message? - int direction = currentTimeframeId % mNumEPNs; - if (Send(mSTFBuffer.front(), mOutChannelName, direction, 0) < 0) { - LOG(ERROR) << "Failed to queue sub-timeframe #" << currentTimeframeId << " to EPN[" << direction << "]"; - } - mSTFBuffer.pop(); - mArrivalTime.pop(); -} diff --git a/Utilities/DataFlow/src/FakeTimeframeBuilder.cxx b/Utilities/DataFlow/src/FakeTimeframeBuilder.cxx deleted file mode 100644 index 432ea03e95be7..0000000000000 --- a/Utilities/DataFlow/src/FakeTimeframeBuilder.cxx +++ /dev/null @@ -1,114 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "DataFlow/FakeTimeframeBuilder.h" -#include "TimeFrame/TimeFrame.h" -#include "Headers/DataHeader.h" -#include <string> -#include <vector> -#include <functional> -#include <cstring> - -using DataHeader = o2::header::DataHeader; -using DataDescription = o2::header::DataDescription; -using DataOrigin = o2::header::DataOrigin; -using IndexElement = o2::dataformats::IndexElement; - -namespace -{ -o2::header::DataDescription lookupDataDescription(const char* key) -{ - if (strcmp(key, "RAWDATA") == 0) - return o2::header::gDataDescriptionRawData; - else if (strcmp(key, "CLUSTERS") == 0) - return o2::header::gDataDescriptionClusters; - else if (strcmp(key, "TRACKS") == 0) - return o2::header::gDataDescriptionTracks; - else if (strcmp(key, "CONFIG") == 0) - return o2::header::gDataDescriptionConfig; - else if (strcmp(key, "INFO") == 0) - return o2::header::gDataDescriptionInfo; - return o2::header::gDataDescriptionInvalid; -} - -o2::header::DataOrigin lookupDataOrigin(const char* key) -{ - if (strcmp(key, "TPC") == 0) - return o2::header::gDataOriginTPC; - if (strcmp(key, "TRD") == 0) - return o2::header::gDataOriginTRD; - if (strcmp(key, "TOF") == 0) - return o2::header::gDataOriginTOF; - if (strcmp(key, "ITS") == 0) - return o2::header::gDataOriginITS; - return o2::header::gDataOriginInvalid; -} - -} // namespace - -namespace o2 -{ -namespace data_flow -{ - -std::unique_ptr<char[]> fakeTimeframeGenerator(std::vector<FakeTimeframeSpec>& specs, std::size_t& totalSize) -{ - // Calculate the total size of your timeframe. This is - // given by: - // - N*The size of the data header (this should actually depend on the - // kind of data as different dataDescriptions will probably have - // different headers). - // - Sum_N(The size of the buffer_i) - // - The size of the index header - // - N*sizeof(dataheader) - // Assuming all the data header - size_t sizeOfHeaders = specs.size() * sizeof(DataHeader); - size_t sizeOfBuffers = 0; - for (auto&& spec : specs) { - sizeOfBuffers += spec.bufferSize; - } - size_t sizeOfIndexHeader = sizeof(DataHeader); - size_t sizeOfIndex = sizeof(IndexElement) * specs.size(); - totalSize = sizeOfHeaders + sizeOfBuffers + sizeOfIndexHeader + sizeOfIndex; - - // Add the actual - data - auto buffer = std::make_unique<char[]>(totalSize); - char* bi = buffer.get(); - std::vector<IndexElement> headers; - int count = 0; - for (auto&& spec : specs) { - IndexElement el; - el.first.dataDescription = lookupDataDescription(spec.dataDescription); - el.first.dataOrigin = lookupDataOrigin(spec.origin); - el.first.payloadSize = spec.bufferSize; - el.first.headerSize = sizeof(el.first); - el.second = count++; - // Let's zero at least the header... - memset(bi, 0, sizeof(el.first)); - memcpy(bi, &el, sizeof(el.first)); - headers.push_back(el); - bi += sizeof(el.first); - spec.bufferFiller(bi, spec.bufferSize); - bi += spec.bufferSize; - } - - // Add the index - DataHeader index; - index.dataDescription = DataDescription("TIMEFRAMEINDEX"); - index.dataOrigin = DataOrigin("FKE"); - index.headerSize = sizeOfIndexHeader; - index.payloadSize = sizeOfIndex; - memcpy(bi, &index, sizeof(index)); - memcpy(bi + sizeof(index), headers.data(), headers.size() * sizeof(IndexElement)); - return std::move(buffer); -} - -} // namespace data_flow -} // namespace o2 diff --git a/Utilities/DataFlow/src/FakeTimeframeGeneratorDevice.cxx b/Utilities/DataFlow/src/FakeTimeframeGeneratorDevice.cxx deleted file mode 100644 index 81c07bcc82f5c..0000000000000 --- a/Utilities/DataFlow/src/FakeTimeframeGeneratorDevice.cxx +++ /dev/null @@ -1,92 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <cstring> - -#include "DataFlow/FakeTimeframeGeneratorDevice.h" -#include "DataFlow/FakeTimeframeBuilder.h" -#include "DataFlow/TimeframeParser.h" -#include "Headers/SubframeMetadata.h" -#include "Headers/DataHeader.h" -#include <options/FairMQProgOptions.h> -#include <vector> - -using DataHeader = o2::header::DataHeader; - -namespace -{ -struct OneShotReadBuf : public std::streambuf { - OneShotReadBuf(char* s, std::size_t n) - { - setg(s, s, s + n); - } -}; -} // namespace -namespace o2 -{ -namespace data_flow -{ - -FakeTimeframeGeneratorDevice::FakeTimeframeGeneratorDevice() - : O2Device{}, mOutChannelName{}, mMaxTimeframes{}, mTimeframeCount{0} -{ -} - -void FakeTimeframeGeneratorDevice::InitTask() -{ - mOutChannelName = GetConfig()->GetValue<std::string>(OptionKeyOutputChannelName); - mMaxTimeframes = GetConfig()->GetValue<size_t>(OptionKeyMaxTimeframes); -} - -bool FakeTimeframeGeneratorDevice::ConditionalRun() -{ - auto addPartFn = [this](FairMQParts& parts, char* buffer, size_t size) { - parts.AddPart(this->NewMessage( - buffer, - size, - [](void* data, void* hint) { delete[](char*) data; }, - nullptr)); - }; - auto sendFn = [this](FairMQParts& parts) { this->Send(parts, this->mOutChannelName); }; - auto zeroFiller = [](char* b, size_t s) { memset(b, 0, s); }; - - std::vector<o2::data_flow::FakeTimeframeSpec> specs = { - {.origin = "TPC", - .dataDescription = "CLUSTERS", - .bufferFiller = zeroFiller, - .bufferSize = 1000}, - {.origin = "ITS", - .dataDescription = "CLUSTERS", - .bufferFiller = zeroFiller, - .bufferSize = 500}}; - - try { - size_t totalSize; - auto buffer = fakeTimeframeGenerator(specs, totalSize); - OneShotReadBuf osrb(buffer.get(), totalSize); - std::istream s(&osrb); - - streamTimeframe(s, - addPartFn, - sendFn); - } catch (std::runtime_error& e) { - LOG(ERROR) << e.what() << "\n"; - } - - mTimeframeCount++; - - if (mTimeframeCount < mMaxTimeframes) { - return true; - } - return false; -} - -} // namespace data_flow -} // namespace o2 diff --git a/Utilities/DataFlow/src/HeartbeatSampler.cxx b/Utilities/DataFlow/src/HeartbeatSampler.cxx deleted file mode 100644 index d960a5941d9c9..0000000000000 --- a/Utilities/DataFlow/src/HeartbeatSampler.cxx +++ /dev/null @@ -1,59 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 HeartbeatSampler.h -// @author Matthias Richter -// @since 2017-02-03 -// @brief Heartbeat sampler device - -#include <thread> // this_thread::sleep_for -#include <chrono> - -#include "DataFlow/HeartbeatSampler.h" -#include "Headers/HeartbeatFrame.h" -#include <options/FairMQProgOptions.h> - -void o2::data_flow::HeartbeatSampler::InitTask() -{ - mPeriod = GetConfig()->GetValue<uint32_t>(OptionKeyPeriod); - mOutputChannelName = GetConfig()->GetValue<std::string>(OptionKeyOutputChannelName); -} - -bool o2::data_flow::HeartbeatSampler::ConditionalRun() -{ - std::this_thread::sleep_for(std::chrono::nanoseconds(mPeriod)); - - o2::header::HeartbeatStatistics hbfPayload; - - o2::header::DataHeader dh; - dh.dataDescription = o2::header::gDataDescriptionHeartbeatFrame; - dh.dataOrigin = o2::header::DataOrigin("SMPL"); - dh.subSpecification = 0; - dh.payloadSize = sizeof(hbfPayload); - - // Note: the block type of both header an trailer members of the envelope - // structure are autmatically initialized to the appropriate block type - // and size '1' (i.e. only one 64bit word) - o2::header::HeartbeatFrameEnvelope specificHeader; - specificHeader.header.orbit = mCount; - specificHeader.trailer.hbAccept = 1; - - O2Message outgoing; - - // build multipart message from header and payload - o2::base::addDataBlock(outgoing, {dh, specificHeader}, NewSimpleMessage(hbfPayload)); - - // send message - Send(outgoing, mOutputChannelName.c_str()); - outgoing.fParts.clear(); - - mCount++; - return true; -} diff --git a/Utilities/DataFlow/src/SubframeBuilderDevice.cxx b/Utilities/DataFlow/src/SubframeBuilderDevice.cxx deleted file mode 100644 index 9047f74a207d4..0000000000000 --- a/Utilities/DataFlow/src/SubframeBuilderDevice.cxx +++ /dev/null @@ -1,148 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SubframeBuilderDevice.cxx -/// @author Giulio Eulisse, Matthias Richter, Sandro Wenzel -/// @since 2017-02-07 -/// @brief Demonstrator device for a subframe builder - -#include <thread> // this_thread::sleep_for -#include <chrono> -#include <functional> - -#include "DataFlow/SubframeBuilderDevice.h" -#include "DataFlow/SubframeUtils.h" -#include "Headers/SubframeMetadata.h" -#include "Headers/HeartbeatFrame.h" -#include "Headers/DataHeader.h" -#include <options/FairMQProgOptions.h> - -using HeartbeatHeader = o2::header::HeartbeatHeader; -using HeartbeatTrailer = o2::header::HeartbeatTrailer; -using DataHeader = o2::header::DataHeader; -using SubframeId = o2::dataflow::SubframeId; - -o2::data_flow::SubframeBuilderDevice::SubframeBuilderDevice() - : O2Device() -{ -} - -o2::data_flow::SubframeBuilderDevice::~SubframeBuilderDevice() = default; - -void o2::data_flow::SubframeBuilderDevice::InitTask() -{ - mOrbitDuration = GetConfig()->GetValue<uint32_t>(OptionKeyOrbitDuration); - mOrbitsPerTimeframe = GetConfig()->GetValue<uint32_t>(OptionKeyOrbitsPerTimeframe); - mInputChannelName = GetConfig()->GetValue<std::string>(OptionKeyInputChannelName); - mOutputChannelName = GetConfig()->GetValue<std::string>(OptionKeyOutputChannelName); - mFLPId = GetConfig()->GetValue<size_t>(OptionKeyFLPId); - mStripHBF = GetConfig()->GetValue<bool>(OptionKeyStripHBF); - - LOG(INFO) << "Obtaining data from DataPublisher\n"; - // Now that we have all the information lets create the policies to do the - // payload extraction and merging and create the actual PayloadMerger. - - // We extract the timeframeId from the number of orbits. - // FIXME: handle multiple socket ids - Merger::IdExtractor makeId = [this](std::unique_ptr<FairMQMessage>& msg) -> SubframeId { - HeartbeatHeader* hbh = reinterpret_cast<HeartbeatHeader*>(msg->GetData()); - SubframeId id = {.timeframeId = hbh->orbit / this->mOrbitsPerTimeframe, - .socketId = 0}; - return id; - }; - - // We extract the payload differently depending on wether we want to strip - // the header or not. - Merger::PayloadExtractor payloadExtractor = [this](char** out, char* in, size_t inSize) -> size_t { - if (!this->mStripHBF) { - return Merger::fullPayloadExtractor(out, in, inSize); - } - return o2::dataflow::extractDetectorPayloadStrip(out, in, inSize); - }; - - // Whether a given timeframe is complete depends on how many orbits per - // timeframe we want. - Merger::MergeCompletionCheker checkIfComplete = - [this](Merger::MergeableId id, Merger::MessageMap& map) { - return map.count(id) < this->mOrbitsPerTimeframe; - }; - - mMerger.reset(new Merger(makeId, checkIfComplete, payloadExtractor)); - OnData(mInputChannelName.c_str(), &o2::data_flow::SubframeBuilderDevice::HandleData); -} - -bool o2::data_flow::SubframeBuilderDevice::BuildAndSendFrame(FairMQParts& inParts) -{ - auto id = mMerger->aggregate(inParts.At(1)); - - char** outBuffer; - size_t outSize = mMerger->finalise(outBuffer, id); - // In this case we do not have enough subtimeframes for id, - // so we simply return. - if (outSize == 0) - return true; - // If we reach here, it means we do have enough subtimeframes. - - // top level subframe header, the DataHeader is going to be used with - // description "SUBTIMEFRAMEMD" - // this should be defined in a common place, and also the origin - // the origin can probably name a detector identifier, but not sure if - // all CRUs of a FLP in all cases serve a single detector - o2::header::DataHeader dh; - dh.dataDescription = o2::header::DataDescription("SUBTIMEFRAMEMD"); - dh.dataOrigin = o2::header::DataOrigin("FLP"); - dh.subSpecification = mFLPId; - dh.payloadSize = sizeof(SubframeMetadata); - - DataHeader payloadheader(*o2::header::get<DataHeader*>((byte*)inParts.At(0)->GetData())); - - // subframe meta information as payload - SubframeMetadata md; - // id is really the first orbit in the timeframe. - md.startTime = id.timeframeId * mOrbitsPerTimeframe * static_cast<uint64_t>(mOrbitDuration); - md.duration = mOrbitDuration * mOrbitsPerTimeframe; - LOG(INFO) << "Start time for subframe (" << md.startTime << ", " - << md.duration - << ")"; - - // Add the metadata about the merged subtimeframes - // FIXME: do we really need this? - O2Message outgoing; - o2::base::addDataBlock(outgoing, dh, NewSimpleMessage(md)); - - // Add the actual merged payload. - o2::base::addDataBlock(outgoing, payloadheader, - NewMessage( - *outBuffer, outSize, - [](void* data, void* hint) { delete[] reinterpret_cast<char*>(hint); }, *outBuffer)); - // send message - Send(outgoing, mOutputChannelName.c_str()); - // FIXME: do we actually need this? outgoing should go out of scope - outgoing.fParts.clear(); - - return true; -} - -bool o2::data_flow::SubframeBuilderDevice::HandleData(FairMQParts& msgParts, int /*index*/) -{ - // loop over header payload pairs in the incoming multimessage - // for each pair - // - check timestamp - // - create new subtimeframe if none existing where the timestamp of the data fits - // - add pair to the corresponding subtimeframe - - // check for completed subtimeframes and send all completed frames - // the builder does not implement the routing to the EPN, this is done in the - // specific FLP-EPN setup - // to fit into the simple emulation of event/frame ids in the flpSender the order of - // subtimeframes needs to be preserved - BuildAndSendFrame(msgParts); - return true; -} diff --git a/Utilities/DataFlow/src/TimeframeParser.cxx b/Utilities/DataFlow/src/TimeframeParser.cxx deleted file mode 100644 index 4e3cc4f2001b8..0000000000000 --- a/Utilities/DataFlow/src/TimeframeParser.cxx +++ /dev/null @@ -1,204 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TimeframeValidatorDevice.cxx -/// @author Giulio Eulisse, Matthias Richter, Sandro Wenzel -/// @since 2017-02-07 -/// @brief Validator device for a full time frame - -#include <thread> // this_thread::sleep_for -#include <chrono> -#include <cstring> - -#include "DataFlow/TimeframeParser.h" -#include "Headers/SubframeMetadata.h" -#include "Headers/DataHeader.h" -#include "TimeFrame/TimeFrame.h" - -#include <options/FairMQProgOptions.h> -#include <FairMQParts.h> - -using DataHeader = o2::header::DataHeader; -using DataDescription = o2::header::DataDescription; -using IndexElement = o2::dataformats::IndexElement; - -namespace o2 -{ -namespace data_flow -{ - -// Possible states for the parsing of a timeframe -// PARSE_BEGIN_STREAM -> -enum ParsingState { - PARSE_BEGIN_STREAM = 0, - PARSE_BEGIN_TIMEFRAME, - PARSE_BEGIN_PAIR, - PARSE_DATA_HEADER, - PARSE_CONCRETE_HEADER, - PARSE_PAYLOAD, - PARSE_END_PAIR, - PARSE_END_TIMEFRAME, - PARSE_END_STREAM, - ERROR -}; - -struct StreamingState { - StreamingState() = default; - - ParsingState state = PARSE_BEGIN_STREAM; - bool hasDataHeader = false; - bool hasConcreteHeader = false; - void* payloadBuffer = nullptr; - void* headerBuffer = nullptr; - DataHeader dh; // The current DataHeader being parsed -}; - -void streamTimeframe(std::istream& stream, - std::function<void(FairMQParts& parts, char* buffer, size_t size)> onAddPart, - std::function<void(FairMQParts& parts)> onSend) -{ - FairMQParts parts; - StreamingState state; - assert(state.state == PARSE_BEGIN_STREAM); - while (true) { - switch (state.state) { - case PARSE_BEGIN_STREAM: - LOG(INFO) << "In PARSE_BEGIN_STREAM\n"; - state.state = PARSE_BEGIN_TIMEFRAME; - break; - case PARSE_BEGIN_TIMEFRAME: - LOG(INFO) << "In PARSE_BEGIN_TIMEFRAME\n"; - state.state = PARSE_BEGIN_PAIR; - break; - case PARSE_BEGIN_PAIR: - LOG(INFO) << "In PARSE_BEGIN_PAIR\n"; - state.state = PARSE_DATA_HEADER; - state.hasDataHeader = false; - state.payloadBuffer = nullptr; - state.headerBuffer = nullptr; - break; - case PARSE_DATA_HEADER: - LOG(INFO) << "In PARSE_DATA_HEADER\n"; - if (state.hasDataHeader) { - throw std::runtime_error("DataHeader already present."); - } else if (state.payloadBuffer) { - throw std::runtime_error("Unexpected payload."); - } - LOG(INFO) << "Reading dataheader of " << sizeof(state.dh) << " bytes\n"; - stream.read(reinterpret_cast<char*>(&state.dh), sizeof(state.dh)); - // If we have a TIMEFRAMEINDEX part and we find the eof, we are done. - if (stream.eof()) { - throw std::runtime_error("Premature end of stream"); - } - - // Otherwise we move to the state which is responsible for parsing the - // kind of header. - state.state = PARSE_CONCRETE_HEADER; - break; - case PARSE_CONCRETE_HEADER: - LOG(INFO) << "In PARSE_CONCRETE_HEADER\n"; - if (state.headerBuffer) { - throw std::runtime_error("File has two consecutive headers"); - } - if (state.dh.headerSize < sizeof(DataHeader)) { - std::ostringstream str; - str << "Bad header size. Should be greater then " - << sizeof(DataHeader) - << ". Found " << state.dh.headerSize << "\n"; - throw std::runtime_error(str.str()); - } - // We get the full header size and read the rest of the header - state.headerBuffer = malloc(state.dh.headerSize); - memcpy(state.headerBuffer, &state.dh, sizeof(state.dh)); - LOG(INFO) << "Reading rest of the header of " << state.dh.headerSize - sizeof(state.dh) << " bytes\n"; - stream.read(reinterpret_cast<char*>(state.headerBuffer) + sizeof(state.dh), - state.dh.headerSize - sizeof(state.dh)); - // Handle the case the file was truncated. - if (stream.eof()) { - throw std::runtime_error("Unexpected end of file"); - } - onAddPart(parts, reinterpret_cast<char*>(state.headerBuffer), state.dh.headerSize); - // Move to parse the payload - state.state = PARSE_PAYLOAD; - break; - case PARSE_PAYLOAD: - LOG(INFO) << "In PARSE_PAYLOAD\n"; - if (state.payloadBuffer) { - throw std::runtime_error("File has two consecutive payloads"); - } - state.payloadBuffer = new char[state.dh.payloadSize]; - LOG(INFO) << "Reading payload of " << state.dh.payloadSize << " bytes\n"; - stream.read(reinterpret_cast<char*>(state.payloadBuffer), state.dh.payloadSize); - if (stream.eof()) { - throw std::runtime_error("Unexpected end of file"); - } - onAddPart(parts, reinterpret_cast<char*>(state.payloadBuffer), state.dh.payloadSize); - state.state = PARSE_END_PAIR; - break; - case PARSE_END_PAIR: - LOG(INFO) << "In PARSE_END_PAIR\n"; - state.state = state.dh == DataDescription("TIMEFRAMEINDEX") ? PARSE_END_TIMEFRAME : PARSE_BEGIN_PAIR; - break; - case PARSE_END_TIMEFRAME: - LOG(INFO) << "In PARSE_END_TIMEFRAME\n"; - onSend(parts); - // Check if we have more. If not, we can declare success. - stream.peek(); - if (stream.eof()) { - state.state = PARSE_END_STREAM; - } else { - state.state = PARSE_BEGIN_TIMEFRAME; - } - break; - case PARSE_END_STREAM: - return; - break; - default: - break; - } - } -} - -void streamTimeframe(std::ostream& stream, FairMQParts& parts) -{ - if (parts.Size() < 2) { - throw std::runtime_error("Expecting at least 2 parts\n"); - } - - auto indexHeader = o2::header::get<DataHeader*>(parts.At(parts.Size() - 2)->GetData()); - // FIXME: Provide iterator pair API for the index - // Index should really be something which provides an - // iterator pair API so that we can sort / find / lower_bound - // easily. Right now we simply use it a C-style array. - auto index = reinterpret_cast<IndexElement*>(parts.At(parts.Size() - 1)->GetData()); - - LOG(INFO) << "This time frame has " << parts.Size() << " parts.\n"; - auto indexEntries = indexHeader->payloadSize / sizeof(IndexElement); - if (indexHeader->dataDescription != DataDescription("TIMEFRAMEINDEX")) { - throw std::runtime_error("Could not find a valid index header\n"); - } - LOG(INFO) << indexHeader->dataDescription.str << "\n"; - LOG(INFO) << "This time frame has " << indexEntries << "entries in the index.\n"; - if ((indexEntries * 2 + 2) != (parts.Size())) { - std::stringstream err; - err << "Mismatched index and received parts. Expected " - << (parts.Size() - 2 * 2) << " found " << indexEntries; - throw std::runtime_error(err.str()); - } - - LOG(INFO) << "Everything is fine with received timeframe\n"; - for (size_t i = 0; i < parts.Size(); ++i) { - stream.write(reinterpret_cast<const char*>(parts.At(i)->GetData()), - parts.At(i)->GetSize()); - } -} - -} // namespace data_flow -} // namespace o2 diff --git a/Utilities/DataFlow/src/TimeframeReaderDevice.cxx b/Utilities/DataFlow/src/TimeframeReaderDevice.cxx deleted file mode 100644 index 5113838bb0a5a..0000000000000 --- a/Utilities/DataFlow/src/TimeframeReaderDevice.cxx +++ /dev/null @@ -1,69 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 <cstring> - -#include "DataFlow/TimeframeReaderDevice.h" -#include "DataFlow/TimeframeParser.h" -#include "Headers/SubframeMetadata.h" -#include "Headers/DataHeader.h" -#include <options/FairMQProgOptions.h> - -using DataHeader = o2::header::DataHeader; - -namespace o2 -{ -namespace data_flow -{ - -TimeframeReaderDevice::TimeframeReaderDevice() - : O2Device{}, mOutChannelName{}, mFile{} -{ -} - -void TimeframeReaderDevice::InitTask() -{ - mOutChannelName = GetConfig()->GetValue<std::string>(OptionKeyOutputChannelName); - mInFileName = GetConfig()->GetValue<std::string>(OptionKeyInputFileName); - mSeen.clear(); -} - -bool TimeframeReaderDevice::ConditionalRun() -{ - auto addPartFn = [this](FairMQParts& parts, char* buffer, size_t size) { - parts.AddPart(this->NewMessage( - buffer, - size, - [](void* data, void* hint) { delete[](char*) data; }, - nullptr)); - }; - auto sendFn = [this](FairMQParts& parts) { this->Send(parts, this->mOutChannelName); }; - - // FIXME: For the moment we support a single file. This should really be a glob. We - // should also have a strategy for watching directories. - std::vector<std::string> files; - files.push_back(mInFileName); - for (auto&& fn : files) { - mFile.open(fn, std::ofstream::in | std::ofstream::binary); - try { - streamTimeframe(mFile, - addPartFn, - sendFn); - } catch (std::runtime_error& e) { - LOG(ERROR) << e.what() << "\n"; - } - mSeen.push_back(fn); - } - - return false; -} - -} // namespace data_flow -} // namespace o2 diff --git a/Utilities/DataFlow/src/TimeframeValidationTool.cxx b/Utilities/DataFlow/src/TimeframeValidationTool.cxx deleted file mode 100644 index 71a5904b76447..0000000000000 --- a/Utilities/DataFlow/src/TimeframeValidationTool.cxx +++ /dev/null @@ -1,63 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "DataFlow/TimeframeParser.h" -#include "fairmq/FairMQParts.h" -#include <vector> -#include <string> -#include <cctype> -#include <cstdio> -#include <cstdlib> -#include <unistd.h> -#include <fstream> - -// A simple tool which verifies timeframe files -int main(int argc, char** argv) -{ - int c; - opterr = 0; - - while ((c = getopt(argc, argv, "")) != -1) { - switch (c) { - case '?': - if (isprint(optopt)) - fprintf(stderr, "Unknown option `-%c'.\n", optopt); - else - fprintf(stderr, - "Unknown option character `\\x%x'.\n", - optopt); - return 1; - default: - abort(); - } - } - - std::vector<std::string> filenames; - for (size_t index = optind; index < argc; index++) { - filenames.emplace_back(std::string(argv[index])); - } - - for (auto&& fn : filenames) { - LOG(INFO) << "Processing file" << fn << "\n"; - std::ifstream s(fn); - FairMQParts parts; - auto onAddParts = [](FairMQParts& p, char* buffer, size_t size) { - }; - auto onSend = [](FairMQParts& p) { - }; - - try { - o2::data_flow::streamTimeframe(s, onAddParts, onSend); - } catch (std::runtime_error& e) { - LOG(ERROR) << e.what() << std::endl; - exit(1); - } - } -} diff --git a/Utilities/DataFlow/src/TimeframeValidatorDevice.cxx b/Utilities/DataFlow/src/TimeframeValidatorDevice.cxx deleted file mode 100644 index f1468e8e7527e..0000000000000 --- a/Utilities/DataFlow/src/TimeframeValidatorDevice.cxx +++ /dev/null @@ -1,151 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TimeframeValidatorDevice.cxx -/// @author Giulio Eulisse, Matthias Richter, Sandro Wenzel -/// @since 2017-02-07 -/// @brief Validator device for a full time frame - -#include <thread> // this_thread::sleep_for -#include <chrono> - -#include "DataFlow/TimeframeValidatorDevice.h" -#include "TimeFrame/TimeFrame.h" -#include "Headers/SubframeMetadata.h" -#include "Headers/DataHeader.h" -#include "O2Device/Compatibility.h" - -#include <options/FairMQProgOptions.h> - -using DataHeader = o2::header::DataHeader; -using DataOrigin = o2::header::DataOrigin; -using DataDescription = o2::header::DataDescription; -using IndexElement = o2::dataformats::IndexElement; - -o2::data_flow::TimeframeValidatorDevice::TimeframeValidatorDevice() - : O2Device(), mInChannelName() -{ -} - -void o2::data_flow::TimeframeValidatorDevice::InitTask() -{ - mInChannelName = GetConfig()->GetValue<std::string>(OptionKeyInputChannelName); -} - -void o2::data_flow::TimeframeValidatorDevice::Run() -{ - while (compatibility::FairMQ13<FairMQDevice>::IsRunning(this)) { - FairMQParts timeframeParts; - if (Receive(timeframeParts, mInChannelName, 0, 100) <= 0) - continue; - - if (timeframeParts.Size() < 2) { - LOG(ERROR) << "Expecting at least 2 parts\n"; - } - - auto indexHeader = o2::header::get<header::DataHeader*>(timeframeParts.At(timeframeParts.Size() - 2)->GetData()); - // FIXME: Provide iterator pair API for the index - // Index should really be something which provides an - // iterator pair API so that we can sort / find / lower_bound - // easily. Right now we simply use it a C-style array. - auto index = reinterpret_cast<IndexElement*>(timeframeParts.At(timeframeParts.Size() - 1)->GetData()); - - // TODO: fill this with checks on time frame - LOG(INFO) << "This time frame has " << timeframeParts.Size() << " parts.\n"; - auto indexEntries = indexHeader->payloadSize / sizeof(DataHeader); - if (indexHeader->dataDescription != DataDescription("TIMEFRAMEINDEX")) { - LOG(ERROR) << "Could not find a valid index header\n"; - } - LOG(INFO) << indexHeader->dataDescription.str << "\n"; - LOG(INFO) << "This time frame has " << indexEntries << "entries in the index.\n"; - if ((indexEntries * 2 + 2) != (timeframeParts.Size())) { - LOG(ERROR) << "Mismatched index and received parts\n"; - } - - // - Use the index to find out if we have TPC data - // - Get the part with the TPC data - // - Validate TPCCluster dummy data - // - Validate ITSRaw dummy data - int tpcIndex = -1; - int itsIndex = -1; - - for (int ii = 0; ii < indexEntries; ++ii) { - IndexElement& ie = index[ii]; - assert(ie.second >= 0); - LOG(DEBUG) << ie.first.dataDescription.str << " " - << ie.first.dataOrigin.str << std::endl; - if ((ie.first.dataOrigin == header::gDataOriginTPC) && (ie.first.dataDescription == header::gDataDescriptionClusters)) { - tpcIndex = ie.second; - } - if ((ie.first.dataOrigin == header::gDataOriginITS) && (ie.first.dataDescription == header::gDataDescriptionClusters)) { - itsIndex = ie.second; - } - } - - if (tpcIndex < 0) { - LOG(ERROR) << "Could not find expected TPC payload\n"; - continue; - } - if (itsIndex < 0) { - LOG(ERROR) << "Could not find expected ITS payload\n"; - continue; - } - LOG(DEBUG) << "TPC Index " << tpcIndex << "\n"; - LOG(DEBUG) << "ITS Index " << itsIndex << "\n"; - - // Data header it at position - 1 - auto tpcHeader = reinterpret_cast<DataHeader*>(timeframeParts.At(tpcIndex)->GetData()); - if ((tpcHeader->dataDescription != header::gDataDescriptionClusters) || - (tpcHeader->dataOrigin != header::gDataOriginTPC)) { - LOG(ERROR) << "Wrong data description. Expecting TPC - CLUSTERS, found " - << tpcHeader->dataOrigin.str << " - " - << tpcHeader->dataDescription.str << "\n"; - continue; - } - auto tpcPayload = reinterpret_cast<TPCTestCluster*>(timeframeParts.At(tpcIndex + 1)->GetData()); - if (tpcHeader->payloadSize % sizeof(TPCTestCluster)) { - LOG(ERROR) << "TPC - CLUSTERS Size Mismatch\n"; - } - auto numOfClusters = tpcHeader->payloadSize / sizeof(TPCTestCluster); - for (size_t ci = 0; ci < numOfClusters; ++ci) { - TPCTestCluster& cluster = tpcPayload[ci]; - if (cluster.z != 1.5) { - LOG(ERROR) << "TPC Data mismatch. Expecting z = 1.5 got " << cluster.z << "\n"; - break; - } - if (cluster.timeStamp != ci) { - LOG(ERROR) << "TPC Data mismatch. Expecting " << ci << " got " << cluster.timeStamp << "\n"; - break; - } - } - - // Data header it at position - 1 - auto itsHeader = reinterpret_cast<DataHeader*>(timeframeParts.At(itsIndex)->GetData()); - if ((itsHeader->dataDescription != header::gDataDescriptionClusters) || (itsHeader->dataOrigin != header::gDataOriginITS)) { - LOG(ERROR) << "Wrong data description. Expecting ITS - CLUSTERS, found " - << itsHeader->dataOrigin.str << " - " << itsHeader->dataDescription.str << "\n"; - continue; - } - auto itsPayload = reinterpret_cast<ITSRawData*>(timeframeParts.At(itsIndex + 1)->GetData()); - if (itsHeader->payloadSize % sizeof(ITSRawData)) { - LOG(ERROR) << "ITS - CLUSTERS Size Mismatch.\n"; - } - numOfClusters = itsHeader->payloadSize / sizeof(ITSRawData); - for (size_t ci = 0; ci < numOfClusters; ++ci) { - ITSRawData& cluster = itsPayload[ci]; - if (cluster.timeStamp != ci) { - LOG(ERROR) << "ITS Data mismatch. Expecting " << ci - << " got " << cluster.timeStamp << "\n"; - break; - } - } - LOG(INFO) << "Everything is fine with received timeframe\n"; - } -} diff --git a/Utilities/DataFlow/src/TimeframeWriterDevice.cxx b/Utilities/DataFlow/src/TimeframeWriterDevice.cxx deleted file mode 100644 index 84b41bf2efe11..0000000000000 --- a/Utilities/DataFlow/src/TimeframeWriterDevice.cxx +++ /dev/null @@ -1,95 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 TimeframeValidatorDevice.cxx -/// @author Giulio Eulisse, Matthias Richter, Sandro Wenzel -/// @since 2017-02-07 -/// @brief Validator device for a full time frame - -#include <thread> // this_thread::sleep_for -#include <chrono> - -#include "DataFlow/TimeframeWriterDevice.h" -#include "DataFlow/TimeframeParser.h" -#include "TimeFrame/TimeFrame.h" -#include "Headers/SubframeMetadata.h" -#include "Headers/DataHeader.h" -#include "O2Device/Compatibility.h" - -#include <options/FairMQProgOptions.h> -#include <boost/filesystem.hpp> - -using DataHeader = o2::header::DataHeader; -using IndexElement = o2::dataformats::IndexElement; - -namespace o2 -{ -namespace data_flow -{ - -TimeframeWriterDevice::TimeframeWriterDevice() - : O2Device{}, mInChannelName{}, mFile{}, mMaxTimeframes{}, mMaxFileSize{}, mMaxFiles{}, mFileCount{0} -{ -} - -void TimeframeWriterDevice::InitTask() -{ - mInChannelName = GetConfig()->GetValue<std::string>(OptionKeyInputChannelName); - mOutFileName = GetConfig()->GetValue<std::string>(OptionKeyOutputFileName); - mMaxTimeframes = GetConfig()->GetValue<size_t>(OptionKeyMaxTimeframesPerFile); - mMaxFileSize = GetConfig()->GetValue<size_t>(OptionKeyMaxFileSize); - mMaxFiles = GetConfig()->GetValue<size_t>(OptionKeyMaxFiles); -} - -void TimeframeWriterDevice::Run() -{ - boost::filesystem::path p(mOutFileName); - size_t streamedTimeframes = 0; - bool needsNewFile = true; - while (compatibility::FairMQ13<FairMQDevice>::IsRunning(this) && mFileCount < mMaxFiles) { - // In case we need to process more than one file, - // the filename is split in basename and extension - // and we call the files `<basename><count>.<extension>`. - if (needsNewFile) { - std::string filename = mOutFileName; - if (mMaxFiles > 1) { - std::string base_path(mOutFileName, 0, mOutFileName.find_last_of(".")); - std::string extension(mOutFileName, mOutFileName.find_last_of(".")); - filename = base_path + std::to_string(mFileCount) + extension; - } - LOG(INFO) << "Opening " << filename << " for output\n"; - mFile.open(filename.c_str(), std::ofstream::out | std::ofstream::binary); - needsNewFile = false; - } - - FairMQParts timeframeParts; - if (Receive(timeframeParts, mInChannelName, 0, 100) <= 0) - continue; - - streamTimeframe(mFile, timeframeParts); - if ((mFile.tellp() > mMaxFileSize) || (streamedTimeframes++ > mMaxTimeframes)) { - mFile.flush(); - mFile.close(); - mFileCount++; - needsNewFile = true; - } - } -} - -void TimeframeWriterDevice::PostRun() -{ - if (mFile.is_open()) { - mFile.flush(); - mFile.close(); - } -} - -} // namespace data_flow -} // namespace o2 diff --git a/Utilities/DataFlow/src/runEPNReceiver.cxx b/Utilities/DataFlow/src/runEPNReceiver.cxx deleted file mode 100644 index b4681116052df..0000000000000 --- a/Utilities/DataFlow/src/runEPNReceiver.cxx +++ /dev/null @@ -1,41 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/** - * runEPNReceiver.cxx - * - * @since 2013-01-21 - * @author D. Klein, A. Rybalchenko, M. Al-Turany, C. Kouzinopoulos - */ - -#include "runFairMQDevice.h" -#include "DataFlow/EPNReceiverDevice.h" - -#include <string> - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - ("buffer-timeout", bpo::value<int>()->default_value(1000), "Buffer timeout in milliseconds") - ("num-flps", bpo::value<int>()->required(), "Number of FLPs") - ("test-mode", bpo::value<int>()->default_value(0), "Run in test mode") - ("in-chan-name", bpo::value<std::string>()->default_value("stf2"), "Name of the input channel (sub-time frames)") - ("out-chan-name", bpo::value<std::string>()->default_value("tf"), "Name of the output channel (time frames)") - ("ack-chan-name", bpo::value<std::string>()->default_value("ack"), "Name of the acknowledgement channel"); - // clang-format on -} - -FairMQDevice* getDevice(const FairMQProgOptions& config) -{ - return new o2::devices::EPNReceiverDevice(); -} diff --git a/Utilities/DataFlow/src/runFLPSender.cxx b/Utilities/DataFlow/src/runFLPSender.cxx deleted file mode 100644 index ed2b94d648954..0000000000000 --- a/Utilities/DataFlow/src/runFLPSender.cxx +++ /dev/null @@ -1,36 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/FLPSenderDevice.h" - -#include <string> - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - ("flp-index", bpo::value<int>()->default_value(0), "FLP Index (for debugging in test mode)") - ("event-size", bpo::value<int>()->default_value(1000), "Event size in bytes (test mode)") - ("num-epns", bpo::value<int>()->required(), "Number of EPNs") - ("test-mode", bpo::value<int>()->default_value(0), "Run in test mode") - ("send-offset", bpo::value<int>()->default_value(0), "Offset for staggered sending") - ("send-delay", bpo::value<int>()->default_value(8), "Delay for staggered sending") - ("in-chan-name", bpo::value<std::string>()->default_value("stf1"), "Name of the input channel (sub-time frames)") - ("out-chan-name", bpo::value<std::string>()->default_value("stf2"), "Name of the output channel (sub-time frames)"); - // clang-format on -} - -FairMQDevice* getDevice(const FairMQProgOptions& config) -{ - return new o2::devices::FLPSenderDevice(); -} diff --git a/Utilities/DataFlow/src/runFakeTimeframeGeneratorDevice.cxx b/Utilities/DataFlow/src/runFakeTimeframeGeneratorDevice.cxx deleted file mode 100644 index 755cf2b8f111f..0000000000000 --- a/Utilities/DataFlow/src/runFakeTimeframeGeneratorDevice.cxx +++ /dev/null @@ -1,34 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/FakeTimeframeGeneratorDevice.h" -#include <vector> - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - (o2::data_flow::FakeTimeframeGeneratorDevice::OptionKeyOutputChannelName, - bpo::value<std::string>()->default_value("output"), - "Name of the output channel"); - options.add_options() - (o2::data_flow::FakeTimeframeGeneratorDevice::OptionKeyMaxTimeframes, - bpo::value<std::string>()->default_value("1"), - "Number of timeframes to generate"); - // clang-format on -} - -FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) -{ - return new o2::data_flow::FakeTimeframeGeneratorDevice(); -} diff --git a/Utilities/DataFlow/src/runHeartbeatSampler.cxx b/Utilities/DataFlow/src/runHeartbeatSampler.cxx deleted file mode 100644 index e5fcd7b12c6b2..0000000000000 --- a/Utilities/DataFlow/src/runHeartbeatSampler.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/HeartbeatSampler.h" - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - (o2::data_flow::HeartbeatSampler::OptionKeyPeriod, - bpo::value<uint32_t>()->default_value(1000000000), - "sampling period") - (o2::data_flow::HeartbeatSampler::OptionKeyOutputChannelName, - bpo::value<std::string>()->default_value("output"), - "Name of the output channel"); - // clang-format on -} - -FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) -{ - return new o2::data_flow::HeartbeatSampler(); -} diff --git a/Utilities/DataFlow/src/runSubframeBuilderDevice.cxx b/Utilities/DataFlow/src/runSubframeBuilderDevice.cxx deleted file mode 100644 index 1c26bf66d6e5f..0000000000000 --- a/Utilities/DataFlow/src/runSubframeBuilderDevice.cxx +++ /dev/null @@ -1,50 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/SubframeBuilderDevice.h" - -namespace bpo = boost::program_options; - -constexpr uint32_t o2::data_flow::SubframeBuilderDevice::DefaultOrbitDuration; -constexpr uint32_t o2::data_flow::SubframeBuilderDevice::DefaultOrbitsPerTimeframe; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - (o2::data_flow::SubframeBuilderDevice::OptionKeyOrbitDuration, - bpo::value<uint32_t>()->default_value(o2::data_flow::SubframeBuilderDevice::DefaultOrbitDuration), - "Orbit duration") - (o2::data_flow::SubframeBuilderDevice::OptionKeyOrbitsPerTimeframe, - bpo::value<uint32_t>()->default_value(o2::data_flow::SubframeBuilderDevice::DefaultOrbitsPerTimeframe), - "Orbits per timeframe") - (o2::data_flow::SubframeBuilderDevice::OptionKeyInputChannelName, - bpo::value<std::string>()->default_value("input"), - "Name of the input channel") - (o2::data_flow::SubframeBuilderDevice::OptionKeyOutputChannelName, - bpo::value<std::string>()->default_value("output"), - "Name of the output channel") - (o2::data_flow::SubframeBuilderDevice::OptionKeyDetector, - bpo::value<std::string>()->default_value("TPC"), - "Name of detector as data source") - (o2::data_flow::SubframeBuilderDevice::OptionKeyFLPId, - bpo::value<size_t>()->default_value(0), - "ID of the FLP used as data source") - (o2::data_flow::SubframeBuilderDevice::OptionKeyStripHBF, - bpo::bool_switch()->default_value(false), - "Strip HBH & HBT from each HBF"); - // clang-format on -} - -FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) -{ - return new o2::data_flow::SubframeBuilderDevice(); -} diff --git a/Utilities/DataFlow/src/runTimeframeReaderDevice.cxx b/Utilities/DataFlow/src/runTimeframeReaderDevice.cxx deleted file mode 100644 index 57574a0dd7811..0000000000000 --- a/Utilities/DataFlow/src/runTimeframeReaderDevice.cxx +++ /dev/null @@ -1,33 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/TimeframeReaderDevice.h" - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - (o2::data_flow::TimeframeReaderDevice::OptionKeyOutputChannelName, - bpo::value<std::string>()->default_value("output"), - "Name of the output channel"); - options.add_options() - (o2::data_flow::TimeframeReaderDevice::OptionKeyInputFileName, - bpo::value<std::string>()->default_value("data.o2tf"), - "Name of the input file"); - // clang-format on -} - -FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) -{ - return new o2::data_flow::TimeframeReaderDevice(); -} diff --git a/Utilities/DataFlow/src/runTimeframeValidatorDevice.cxx b/Utilities/DataFlow/src/runTimeframeValidatorDevice.cxx deleted file mode 100644 index 34b5e90d46e69..0000000000000 --- a/Utilities/DataFlow/src/runTimeframeValidatorDevice.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/TimeframeValidatorDevice.h" - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - (o2::data_flow::TimeframeValidatorDevice::OptionKeyInputChannelName, - bpo::value<std::string>()->default_value("input"), - "Name of the input channel"); - // clang-format on -} - -FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) -{ - return new o2::data_flow::TimeframeValidatorDevice(); -} diff --git a/Utilities/DataFlow/src/runTimeframeWriterDevice.cxx b/Utilities/DataFlow/src/runTimeframeWriterDevice.cxx deleted file mode 100644 index 112c0cb3f8be1..0000000000000 --- a/Utilities/DataFlow/src/runTimeframeWriterDevice.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "runFairMQDevice.h" -#include "DataFlow/TimeframeWriterDevice.h" - -namespace bpo = boost::program_options; - -void addCustomOptions(bpo::options_description& options) -{ - // clang-format off - options.add_options() - (o2::data_flow::TimeframeWriterDevice::OptionKeyInputChannelName, - bpo::value<std::string>()->default_value("input"), - "Name of the input channel"); - options.add_options() - (o2::data_flow::TimeframeWriterDevice::OptionKeyOutputFileName, - bpo::value<std::string>()->default_value("data.o2tf"), - "Name of the input channel"); - options.add_options() - (o2::data_flow::TimeframeWriterDevice::OptionKeyMaxFiles, - bpo::value<size_t>()->default_value(1), - "Maximum number of files to write"); - options.add_options() - (o2::data_flow::TimeframeWriterDevice::OptionKeyMaxTimeframesPerFile, - bpo::value<size_t>()->default_value(1), - "Maximum number of timeframes per file"); - options.add_options() - (o2::data_flow::TimeframeWriterDevice::OptionKeyMaxFileSize, - bpo::value<size_t>()->default_value(-1), - "Maximum size per file"); - // clang-format on -} - -FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) -{ - return new o2::data_flow::TimeframeWriterDevice(); -} diff --git a/Utilities/DataFlow/test/test_PayloadMerger01.cxx b/Utilities/DataFlow/test/test_PayloadMerger01.cxx deleted file mode 100644 index 40cc1e252a84c..0000000000000 --- a/Utilities/DataFlow/test/test_PayloadMerger01.cxx +++ /dev/null @@ -1,83 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Utilities DataFlowTest -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include <boost/test/unit_test.hpp> - -#include <iostream> -#include <memory> -#include "DataFlow/PayloadMerger.h" -#include "DataFlow/FakeTimeframeBuilder.h" -#include "DataFlow/TimeframeParser.h" -#include "DataFlow/SubframeUtils.h" -#include "fairmq/FairMQTransportFactory.h" -#include "fairmq/FairMQParts.h" - -using SubframeId = o2::dataflow::SubframeId; -using HeartbeatHeader = o2::header::HeartbeatHeader; -using HeartbeatTrailer = o2::header::HeartbeatTrailer; - -SubframeId fakeAddition(o2::dataflow::PayloadMerger<SubframeId>& merger, - std::shared_ptr<FairMQTransportFactory>& transport, - int64_t orbit) -{ - // Create a message - // - // We set orbit to be always the same and the actual contents to be 127 - static size_t dummyMessageSize = 1000; - auto msg = transport->CreateMessage(dummyMessageSize); - char* b = reinterpret_cast<char*>(msg->GetData()) + sizeof(HeartbeatHeader); - for (size_t i = 0; i < (dummyMessageSize - sizeof(HeartbeatHeader)); ++i) { - b[i] = orbit; - } - b[0] = 127; - HeartbeatHeader* header = reinterpret_cast<HeartbeatHeader*>(msg->GetData()); - header->orbit = orbit; - return merger.aggregate(msg); -} - -BOOST_AUTO_TEST_CASE(PayloadMergerTest) -{ - auto zmq = FairMQTransportFactory::CreateTransportFactory("zeromq"); - - // Needs three subtimeframes to merge them - auto checkIfComplete = [](SubframeId id, o2::dataflow::PayloadMerger<SubframeId>::MessageMap& m) -> bool { - return m.count(id) >= 3; - }; - - // Id is given by the orbit, 2 orbits per timeframe - auto makeId = [](std::unique_ptr<FairMQMessage>& msg) { - auto header = reinterpret_cast<o2::header::HeartbeatHeader const*>(msg->GetData()); - return o2::dataflow::makeIdFromHeartbeatHeader(*header, 0, 2); - }; - - o2::dataflow::PayloadMerger<SubframeId> merger(makeId, checkIfComplete, o2::dataflow::extractDetectorPayloadStrip); - char* finalBuf = new char[3000]; - size_t finalSize = 0; - auto id = fakeAddition(merger, zmq, 1); - finalSize = merger.finalise(&finalBuf, id); - BOOST_CHECK(finalSize == 0); // Not enough parts, not merging yet. - id = fakeAddition(merger, zmq, 1); - finalSize = merger.finalise(&finalBuf, id); - BOOST_CHECK(finalSize == 0); // Not enough parts, not merging yet. - id = fakeAddition(merger, zmq, 2); - finalSize = merger.finalise(&finalBuf, id); - BOOST_CHECK(finalSize == 0); // Different ID, not merging yet. - id = fakeAddition(merger, zmq, 1); - finalSize = merger.finalise(&finalBuf, id); - BOOST_CHECK(finalSize); // Now we merge! - size_t partSize = (1000 - sizeof(HeartbeatHeader) - sizeof(HeartbeatTrailer)); - BOOST_CHECK(finalSize == 3 * partSize); // This should be the calculated size - for (size_t i = 0; i < finalSize; ++i) { - BOOST_CHECK(finalBuf[i] == ((i % partSize) == 0 ? 127 : 1)); - } -} diff --git a/Utilities/DataFlow/test/test_SubframeUtils01.cxx b/Utilities/DataFlow/test/test_SubframeUtils01.cxx deleted file mode 100644 index 32605f9075808..0000000000000 --- a/Utilities/DataFlow/test/test_SubframeUtils01.cxx +++ /dev/null @@ -1,59 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Utilities DataFlowTest -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include "DataFlow/SubframeUtils.h" -#include <boost/test/unit_test.hpp> -#include <iostream> - -BOOST_AUTO_TEST_CASE(SubframeUtils01) -{ - o2::dataflow::SubframeId a; - a.timeframeId = 0; - a.socketId = 1; - o2::dataflow::SubframeId b; - b.timeframeId = 1; - b.socketId = 0; - BOOST_CHECK(a < b); - char* buf = new char[1000]; - memset(buf, 126, 1000); - for (size_t i = sizeof(o2::header::HeartbeatHeader); i < 1000 - sizeof(o2::header::HeartbeatHeader); ++i) { - buf[i] = 0; - } - BOOST_CHECK(buf[0] == 126); - BOOST_CHECK(buf[sizeof(o2::header::HeartbeatHeader)] == 0); - BOOST_CHECK(buf[sizeof(o2::header::HeartbeatHeader) - 1] == 126); - char* realPayload = nullptr; - size_t realSize = o2::dataflow::extractDetectorPayloadStrip(&realPayload, buf, 1000); - BOOST_CHECK(realPayload != nullptr); - BOOST_CHECK(realSize == 1000 - sizeof(o2::header::HeartbeatHeader) - sizeof(o2::header::HeartbeatTrailer)); - BOOST_CHECK(realPayload == buf + sizeof(o2::header::HeartbeatHeader)); - BOOST_CHECK(realPayload[0] == 0); - - o2::header::HeartbeatHeader header1; - header1.orbit = 0; - o2::header::HeartbeatHeader header2; - header2.orbit = 255; - o2::header::HeartbeatHeader header3; - header3.orbit = 256; - - auto id1 = o2::dataflow::makeIdFromHeartbeatHeader(header1, 1, 256); - auto id2 = o2::dataflow::makeIdFromHeartbeatHeader(header2, 1, 256); - auto id3 = o2::dataflow::makeIdFromHeartbeatHeader(header3, 1, 256); - BOOST_CHECK(!(id1 < id2)); // Maybe we should provide an == operator - BOOST_CHECK(!(id2 < id1)); - BOOST_CHECK(id1 < id3); - BOOST_CHECK(id2 < id3); - BOOST_CHECK(id1.timeframeId == 0); - BOOST_CHECK(id3.timeframeId == 1); -} diff --git a/Utilities/DataFlow/test/test_TimeframeParser.cxx b/Utilities/DataFlow/test/test_TimeframeParser.cxx deleted file mode 100644 index 338d491c25ac8..0000000000000 --- a/Utilities/DataFlow/test/test_TimeframeParser.cxx +++ /dev/null @@ -1,59 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 "DataFlow/TimeframeParser.h" -#include "DataFlow/FakeTimeframeBuilder.h" -#include "Headers/DataHeader.h" -#include <FairMQParts.h> -#include <istream> -#include <cstdlib> - -struct OneShotReadBuf : public std::streambuf { - OneShotReadBuf(char* s, std::size_t n) - { - setg(s, s, s + n); - } -}; - -using DataHeader = o2::header::DataHeader; - -int main(int argc, char** argv) -{ - // Construct a dummy timeframe. - // Stream it and get the parts - FairMQParts parts; - auto onAddParts = [](FairMQParts& p, char* buffer, size_t size) { - LOG(INFO) << "Adding part to those to be sent.\n"; - }; - auto onSend = [](FairMQParts& p) { - LOG(INFO) << "Everything OK. Sending parts\n"; - }; - - // Prepare a test timeframe to be streamed - auto zeroFiller = [](char* b, size_t s) { memset(b, 0, s); }; - std::vector<o2::data_flow::FakeTimeframeSpec> specs = { - {.origin = "TPC", - .dataDescription = "CLUSTERS", - .bufferFiller = zeroFiller, - .bufferSize = 1000}}; - - size_t testBufferSize; - auto testBuffer = fakeTimeframeGenerator(specs, testBufferSize); - - OneShotReadBuf osrb(testBuffer.get(), testBufferSize); - std::istream s(&osrb); - - try { - o2::data_flow::streamTimeframe(s, onAddParts, onSend); - } catch (std::runtime_error& e) { - LOG(ERROR) << e.what() << std::endl; - exit(1); - } -} diff --git a/Utilities/DataSampling/CMakeLists.txt b/Utilities/DataSampling/CMakeLists.txt new file mode 100644 index 0000000000000..549f87122175f --- /dev/null +++ b/Utilities/DataSampling/CMakeLists.txt @@ -0,0 +1,87 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +# FIXME: the LinkDef should not be in the public area + +o2_add_library(DataSampling + SOURCES src/DataSampling.cxx + src/DataSamplingConditionFactory.cxx + src/DataSamplingHeader.cxx + src/DataSamplingConditionCustom.cxx + src/DataSamplingConditionNConsecutive.cxx + src/DataSamplingConditionPayloadSize.cxx + src/DataSamplingConditionRandom.cxx + src/DataSamplingHeader.cxx + src/DataSamplingPolicy.cxx + src/DataSamplingReadoutAdapter.cxx + src/Dispatcher.cxx + + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataSampling) + +#o2_target_root_dictionary( +# Mergers +# HEADERS include/Mergers/MergeInterface.h +# include/Mergers/CustomMergeableObject.h +# include/Mergers/CustomMergeableTObject.h +# LINKDEF include/Mergers/LinkDef.h) + +# tests with input data + +o2_data_file(COPY test/test_DataSampling.json test/test_DataSamplingEmpty.json DESTINATION tests) + +o2_add_test(DataSampling NAME test_DataSampling_test_DataSampling + SOURCES test/test_DataSampling.cxx + COMPONENT_NAME DataSampling + LABELS datasampling + PUBLIC_LINK_LIBRARIES O2::DataSampling + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) + +foreach(t + DataSamplingCondition + DataSamplingHeader + DataSamplingPolicy + ) + + # FIXME ? The NAME parameter of o2_add_test is only needed to help the current + # o2.sh recipe. If the recipe is changed, those params can go away, if needed. + + o2_add_test(${t} NAME test_DataSampling_test_${t} + SOURCES test/test_${t}.cxx + COMPONENT_NAME DataSampling + LABELS datasampling + PUBLIC_LINK_LIBRARIES O2::DataSampling) +endforeach() + +o2_data_file(COPY etc/exampleDataSamplingConfig.json DESTINATION etc) + +o2_add_dpl_workflow(standalone + SOURCES src/dataSamplingStandalone.cxx + COMPONENT_NAME DataSampling + PUBLIC_LINK_LIBRARIES O2::DataSampling) + +o2_add_dpl_workflow(datasampling-pod-and-root + SOURCES test/dataSamplingPodAndRoot.cxx + COMPONENT_NAME DataSampling + PUBLIC_LINK_LIBRARIES O2::DataSampling) + +o2_add_dpl_workflow(datasampling-parallel + SOURCES test/dataSamplingParallel.cxx + COMPONENT_NAME DataSampling + PUBLIC_LINK_LIBRARIES O2::DataSampling) + +o2_add_dpl_workflow(datasampling-time-pipeline + SOURCES test/dataSamplingTimePipeline.cxx + COMPONENT_NAME DataSampling + PUBLIC_LINK_LIBRARIES O2::DataSampling) + +o2_add_dpl_workflow(datasampling-benchmark + SOURCES test/dataSamplingBenchmark.cxx + COMPONENT_NAME DataSampling + PUBLIC_LINK_LIBRARIES O2::DataSampling) \ No newline at end of file diff --git a/Utilities/DataSampling/README.md b/Utilities/DataSampling/README.md new file mode 100644 index 0000000000000..104890f06ceb8 --- /dev/null +++ b/Utilities/DataSampling/README.md @@ -0,0 +1,107 @@ +<!-- doxy +\page refUtilitiesDataSampling Data Sampling +/doxy --> + +## Data Sampling + +Data Sampling provides a possibility to sample data in DPL workflows based on certain conditions ( 5% randomly, when a payload is greater than 4234 bytes, etc.). The job of passing the right data is done by a data processor called `Dispatcher`. A desired data stream is specified in form of Data Sampling Policies, configured by JSON structures (example below) or by using dedicated interface methods (for advanced use). +``` +{ + "id": "policy_example1", # name of the policy + "active": "false", # activation flag + "machines": [ # list of machines where the policy should be run (now ignored) + "aido2flp1", + "aido2flp2" + ], # list of data that should be sampled, the format is: + # binding1:origin1/description1/subSpec1[;binding2:...] + "query": "clusters:TPC/CLUSTERS/0;tracks:TPC/TRACKS/0", + # optional list of outputspecs for sampled data, matching the query + # if not present or specified, the default format is used + "outputs": "sampled_clusters:DS/CLUSTERS/0;sampled_tracks:DS/TRACKS/0", + "samplingConditions": [ # list of sampling conditions + { + "condition": "random", # condition type + "fraction": "0.1", # condition-dependent parameter: fraction of data to sample + "seed": "2112" # condition-dependent parameter: seed of PRNG + } + ], + "blocking": "false" # should the dispatcher block the main data flow? (now ignored) +} +``` + +### Usage + +One can use Data Sampling either by merging the standalone Data Sampling workflow with other DPL workflows: +```bash +o2-workflow-abc | o2-datasampling-standalone --config json://path/to/config.json | o2-workflow-xyz +``` +...or by incorporating the code below into a DPL workflow which needs sampling: +```cpp +#include "DataSampling/DataSampling.h" +using namespace o2::framework; +using namespace o2::utilities; +void customize(std::vector<CompletionPolicy>& policies) +{ + DataSampling::CustomizeInfrastructure(policies); +} + +void customize(std::vector<ChannelConfigurationPolicy>& policies) +{ + DataSampling::CustomizeInfrastructure(policies); +} + +#include "Framework/runDataProcessing.h" + +std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext &ctx) +{ + + WorkflowSpec workflow; +// <declaration of other DPL processors> + + DataSampling::GenerateInfrastructure(workflow, "json:///absolute/path/to/config/file.json"); + + return workflow; +} +``` + +Sampled data can be subscribed to by adding `InputSpecs` provided by `std::vector<InputSpec> DataSampling::InputSpecsForPolicy(const std::string& policiesSource, const std::string& policyName)` to a chosen data processor. Then, they can be accessed by the bindings specified in the configuration file. Dispatcher adds a `DataSamplingHeader` to the header stack, which contains statistics like total number of evaluated/accepted messages for a given Policy or the sampling time since epoch. +If no sampling policies are specified, Dispatcher will not be spawned. + +The [o2-datasampling-pod-and-root](https://github.com/AliceO2Group/AliceO2/blob/dev/Utilities/DataSampling/test/dataSamplingPodAndRoot.cxx) workflow can serve as a usage example. + +## Data Sampling Conditions + +The following sampling conditions are available. When more than one is used, a positive decision is taken when all the conditions are fulfilled. +- **DataSamplingConditionRandom** - pseudo-randomly accepts specified fraction of incoming messages. +```json +{ + "condition": "random", + "fraction": "0.1", + "seed": "22222" +} +``` +- **DataSamplingConditionNConsecutive** - approves n consecutive samples in defined cycle. It assumes that timesliceID always increments by one. +```json +{ + "condition": "nConsecutive", + "samplesNumber": "3", + "cycleSize": "100" +} +``` +- **DataSamplingConditionPayloadSize** - approves messages having payload size within specified boundaries. +```json +{ + "condition": "payloadSize", + "lowerLimit": "300", + "upperLimit": "500" +} +``` +- **DataSamplingConditionCustom** - loads a custom condition, which should inherit from DataSamplingCondition, from a specified library. +```json +{ + "condition": "custom", + "moduleName": "QcExample", + "className": "o2::quality_control_modules::example::ExampleCondition", + "customParam": "value" +} +``` diff --git a/Framework/TestWorkflows/etc/exampleDataSamplingConfig.json b/Utilities/DataSampling/etc/exampleDataSamplingConfig.json similarity index 100% rename from Framework/TestWorkflows/etc/exampleDataSamplingConfig.json rename to Utilities/DataSampling/etc/exampleDataSamplingConfig.json diff --git a/Utilities/DataSampling/include/DataSampling/DataSampling.h b/Utilities/DataSampling/include/DataSampling/DataSampling.h new file mode 100644 index 0000000000000..6ef4f9a0ca2d4 --- /dev/null +++ b/Utilities/DataSampling/include/DataSampling/DataSampling.h @@ -0,0 +1,122 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FRAMEWORK_DATASAMPLING_H +#define FRAMEWORK_DATASAMPLING_H + +/// \file DataSampling.h +/// \brief Definition of O2 Data Sampling, v1.0 +/// +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#include "Framework/WorkflowSpec.h" +#include "Framework/InputSpec.h" +#include <string> +#include <boost/property_tree/ptree_fwd.hpp> + +namespace o2::configuration +{ +class ConfigurationInterface; +} + +namespace o2::framework +{ +class CompletionPolicy; +class ChannelConfigurationPolicy; +} // namespace o2::framework + +namespace o2::utilities +{ + +class Dispatcher; + +/// A class responsible for providing data from main processing flow to QC tasks. +/// +/// This class generates message-passing infrastructure to provide desired amount of data to Quality Control tasks or +/// any other clients. Data to be sampled is declared in DataSamplingPolicy'ies configuration file - an example can be +/// found in O2/Framework/TestWorkflows/exampleDataSamplingConfig.json). +/// +/// In-code usage: +/// \code{.cxx} +/// void customize(std::vector<CompletionPolicy>& policies) +/// { +/// DataSampling::CustomizeInfrastructure(policies); +/// } +/// +/// void customize(std::vector<ChannelConfigurationPolicy>& policies) +/// { +/// DataSampling::CustomizeInfrastructure(policies); +/// } +/// +/// #include "Framework/runDataProcessing.h" +/// +/// std::vector<DataProcessorSpec> defineDataProcessing(ConfigContext &ctx) +/// { +/// WorkflowSpec workflow; +/// // <declaration of other DPL processors> +/// +/// const std::string configurationFilePath = <absolute file path>; +/// DataSampling::GenerateInfrastructure(workflow, configurationFilePath); +/// +/// return workflow; +/// } +/// \endcode + +class DataSampling +{ + public: + /// \brief Deleted default constructor. This class is stateless. + DataSampling() = delete; + /// \brief Generates data sampling infrastructure. + /// \param workflow DPL workflow with already declared data processors which provide data desired by + /// QC tasks. + /// \param policiesSource Path to configuration file. + /// \param threads Number of dispatcher threads, that will handle the data + static void GenerateInfrastructure(framework::WorkflowSpec& workflow, const std::string& policiesSource, size_t threads = 1); + + /// \brief Generates data sampling infrastructure. + /// \param workflow DPL workflow with already declared data processors which provide data desired by + /// QC tasks. + /// \param policiesSource boost::property_tree::ptree with the configuration + /// \param threads Number of dispatcher threads, that will handle the data + static void GenerateInfrastructure(framework::WorkflowSpec& workflow, boost::property_tree::ptree const& policies, size_t threads = 1); + /// \brief Configures dispatcher to consume any data immediately. + static void CustomizeInfrastructure(std::vector<framework::CompletionPolicy>&); + /// \brief Applies blocking/nonblocking data sampling configuration to the workflow. + static void CustomizeInfrastructure(std::vector<framework::ChannelConfigurationPolicy>&); + /// \brief Provides InputSpecs to receive data for given DataSamplingPolicy + static std::vector<framework::InputSpec> InputSpecsForPolicy(const std::string& policiesSource, const std::string& policyName); + /// \brief Provides InputSpecs to receive data for given DataSamplingPolicy + /// @deprecated + static std::vector<framework::InputSpec> InputSpecsForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); + /// \brief Provides InputSpecs to receive data for given DataSamplingPolicy + static std::vector<framework::InputSpec> InputSpecsForPolicy(std::shared_ptr<configuration::ConfigurationInterface> config, const std::string& policyName); + /// \brief Provides OutputSpecs of given DataSamplingPolicy + static std::vector<framework::OutputSpec> OutputSpecsForPolicy(const std::string& policiesSource, const std::string& policyName); + /// \brief Provides OutputSpecs of given DataSamplingPolicy + static std::vector<framework::OutputSpec> OutputSpecsForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); + /// \brief Provides the port to be used for a proxy of given DataSamplingPolicy + static uint16_t PortForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); + /// \brief Provides the port to be used for a proxy of given DataSamplingPolicy + static uint16_t PortForPolicy(const std::string& policiesSource, const std::string& policyName); + /// \brief Provides the machines where given DataSamplingPolicy is enabled + static std::vector<std::string> MachinesForPolicy(configuration::ConfigurationInterface* const config, const std::string& policyName); + /// \brief Provides the port to be used for a proxy of given DataSamplingPolicy + static std::vector<std::string> MachinesForPolicy(const std::string& policiesSource, const std::string& policyName); + + private: + static void DoGenerateInfrastructure(Dispatcher&, framework::WorkflowSpec& workflow, boost::property_tree::ptree const& policies, size_t threads = 1); + // Internal functions, used by GenerateInfrastructure() + static std::string createDispatcherName(); +}; + +} // namespace o2::utilities + +#endif // FRAMEWORK_DATASAMPLING_H diff --git a/Framework/Core/include/Framework/DataSamplingCondition.h b/Utilities/DataSampling/include/DataSampling/DataSamplingCondition.h similarity index 89% rename from Framework/Core/include/Framework/DataSamplingCondition.h rename to Utilities/DataSampling/include/DataSampling/DataSamplingCondition.h index a5710b199ca3a..754f9049a1561 100644 --- a/Framework/Core/include/Framework/DataSamplingCondition.h +++ b/Utilities/DataSampling/include/DataSampling/DataSamplingCondition.h @@ -21,10 +21,10 @@ #include <boost/property_tree/ptree_fwd.hpp> #include <string> -namespace o2::framework +namespace o2::utilities { -/// A standarised data sampling condition, to decide if given data sample should be passed forward. +/// A standardised data sampling condition, to decide if given data sample should be passed forward. class DataSamplingCondition { public: @@ -39,6 +39,6 @@ class DataSamplingCondition virtual bool decide(const o2::framework::DataRef&) = 0; }; -} // namespace o2::framework +} // namespace o2::utilities #endif //ALICEO2_DATASAMPLINGCONDITION_H diff --git a/Framework/Core/include/Framework/DataSamplingConditionFactory.h b/Utilities/DataSampling/include/DataSampling/DataSamplingConditionFactory.h similarity index 94% rename from Framework/Core/include/Framework/DataSamplingConditionFactory.h rename to Utilities/DataSampling/include/DataSampling/DataSamplingConditionFactory.h index 2728010607dc9..fb62efa80e89f 100644 --- a/Framework/Core/include/Framework/DataSamplingConditionFactory.h +++ b/Utilities/DataSampling/include/DataSampling/DataSamplingConditionFactory.h @@ -16,11 +16,11 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "DataSamplingCondition.h" +#include "DataSampling/DataSamplingCondition.h" #include <memory> #include <string> -namespace o2::framework +namespace o2::utilities { /// A factory of DataSamplingConditions children. @@ -42,6 +42,6 @@ class DataSamplingConditionFactory static std::unique_ptr<DataSamplingCondition> createDataSamplingConditionCustom(); }; -} // namespace o2::framework +} // namespace o2::utilities #endif //ALICEO2_DATASAMPLINGCONDITIONFACTORY_H diff --git a/Framework/Core/include/Framework/DataSamplingHeader.h b/Utilities/DataSampling/include/DataSampling/DataSamplingHeader.h similarity index 97% rename from Framework/Core/include/Framework/DataSamplingHeader.h rename to Utilities/DataSampling/include/DataSampling/DataSamplingHeader.h index 10d9eb5899edf..03856ccec2348 100644 --- a/Framework/Core/include/Framework/DataSamplingHeader.h +++ b/Utilities/DataSampling/include/DataSampling/DataSamplingHeader.h @@ -20,7 +20,7 @@ #include "Headers/DataHeader.h" -namespace o2::framework +namespace o2::utilities { struct DataSamplingHeader : public header::BaseHeader { @@ -48,6 +48,6 @@ struct DataSamplingHeader : public header::BaseHeader { static const DataSamplingHeader* Get(const BaseHeader* baseHeader); }; -} // namespace o2::framework +} // namespace o2::utilities #endif //ALICEO2_DATASAMPLINGHEADER_H diff --git a/Utilities/DataSampling/include/DataSampling/DataSamplingPolicy.h b/Utilities/DataSampling/include/DataSampling/DataSamplingPolicy.h new file mode 100644 index 0000000000000..3330aea33686b --- /dev/null +++ b/Utilities/DataSampling/include/DataSampling/DataSamplingPolicy.h @@ -0,0 +1,105 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_DATASAMPLINGPOLICY_H +#define ALICEO2_DATASAMPLINGPOLICY_H + +/// \file DataSamplingPolicy.h +/// \brief A declaration of O2 Data Sampling Policy +/// +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#include "Headers/DataHeader.h" +#include "Framework/InputSpec.h" +#include "Framework/Output.h" +#include "Framework/OutputSpec.h" +#include "DataSampling/DataSamplingCondition.h" +#include "Framework/DataSpecUtils.h" + +#include <boost/property_tree/ptree_fwd.hpp> + +namespace o2::utilities +{ + +/// A class representing certain policy of sampling data. +/// +/// This class stores information about specified sampling policy - data headers and conditions of sampling. +/// For given InputSpec, it can provide corresponding Output to pass data further. +class DataSamplingPolicy +{ + private: + using PathVectorBase = std::vector<std::pair<framework::InputSpec, framework::OutputSpec>>; + class PathMap : public PathVectorBase + { + public: + PathMap() = default; + ~PathMap() = default; + const PathVectorBase::const_iterator find(const framework::ConcreteDataMatcher& input) const + { + return std::find_if(begin(), end(), [input](const auto& el) { + return framework::DataSpecUtils::match(el.first, input); + }); + } + }; + + public: + /// \brief Configures a policy using structured configuration entry. + static DataSamplingPolicy fromConfiguration(const boost::property_tree::ptree&); + + /// \brief Constructor. + DataSamplingPolicy(std::string name); + /// \brief Destructor + ~DataSamplingPolicy() = default; + /// \brief Move constructor + DataSamplingPolicy(DataSamplingPolicy&&) = default; + /// \brief Move assignment + DataSamplingPolicy& operator=(DataSamplingPolicy&&) = default; + + /// \brief Adds a new association between inputs and outputs. + void registerPath(const framework::InputSpec&, const framework::OutputSpec&); + /// \brief Adds a new association between inputs and outputs. + // void registerPolicy(framework::InputSpec&&, framework::OutputSpec&&); + /// \brief Adds a new sampling condition. + void registerCondition(std::unique_ptr<DataSamplingCondition>&&); + /// \brief Sets a raw FairMQChannel. Deprecated, do not use. + void setFairMQOutputChannel(std::string); + + /// \brief Returns true if this policy requires data with given InputSpec. + bool match(const framework::ConcreteDataMatcher& input) const; + /// \brief Returns true if user-defined conditions of sampling are fulfilled. + bool decide(const o2::framework::DataRef&); + /// \brief Returns Output for given InputSpec to pass data forward. + framework::Output prepareOutput(const framework::ConcreteDataMatcher& input, framework::Lifetime lifetime = framework::Lifetime::Timeframe) const; + + const std::string& getName() const; + const PathMap& getPathMap() const; + // optional fairmq channel to send stuff outside of DPL + const std::string& getFairMQOutputChannel() const; + std::string getFairMQOutputChannelName() const; + uint32_t getTotalAcceptedMessages() const; + uint32_t getTotalEvaluatedMessages() const; + + static header::DataOrigin createPolicyDataOrigin(); + static header::DataDescription createPolicyDataDescription(std::string policyName, size_t id); + + private: + std::string mName; + PathMap mPaths; + std::vector<std::unique_ptr<DataSamplingCondition>> mConditions; + std::string mFairMQOutputChannel; + + // stats + uint32_t mTotalAcceptedMessages = 0; + uint32_t mTotalEvaluatedMessages = 0; +}; + +} // namespace o2::utilities + +#endif // ALICEO2_DATASAMPLINGPOLICY_H diff --git a/Framework/Core/include/Framework/DataSamplingReadoutAdapter.h b/Utilities/DataSampling/include/DataSampling/DataSamplingReadoutAdapter.h similarity index 83% rename from Framework/Core/include/Framework/DataSamplingReadoutAdapter.h rename to Utilities/DataSampling/include/DataSampling/DataSamplingReadoutAdapter.h index c9a185ab10509..74084dde0112c 100644 --- a/Framework/Core/include/Framework/DataSamplingReadoutAdapter.h +++ b/Utilities/DataSampling/include/DataSampling/DataSamplingReadoutAdapter.h @@ -11,19 +11,19 @@ #ifndef ALICEO2_DATASAMPLINGREADOUTADAPTER_H #define ALICEO2_DATASAMPLINGREADOUTADAPTER_H -#include "ExternalFairMQDeviceProxy.h" +#include "Framework/ExternalFairMQDeviceProxy.h" namespace o2 { -namespace framework +namespace utilities { /// An adapter function for data sent by Readout for Data Sampling / Quality control purposes. /// It adds DataHeader and DataProcessingHeader using information found in received DataBlockHeaderBase /// header and given OutputSpec. It **IGNORES** SubSpecification in OutputSpec and uses linkID instead. -InjectorFunction dataSamplingReadoutAdapter(OutputSpec const& spec); +framework::InjectorFunction dataSamplingReadoutAdapter(framework::OutputSpec const& spec); -} // namespace framework +} // namespace utilities } // namespace o2 #endif //ALICEO2_DATASAMPLINGREADOUTADAPTER_H diff --git a/Utilities/DataSampling/include/DataSampling/Dispatcher.h b/Utilities/DataSampling/include/DataSampling/Dispatcher.h new file mode 100644 index 0000000000000..b342f9478f0e1 --- /dev/null +++ b/Utilities/DataSampling/include/DataSampling/Dispatcher.h @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Dispatcher.h +/// \brief Declaration of Dispatcher for O2 Data Sampling +/// +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#ifndef ALICEO2_DISPATCHER_H +#define ALICEO2_DISPATCHER_H + +#include <string> +#include <vector> +#include <memory> + +#include "Framework/DataProcessorSpec.h" +#include "Framework/DeviceSpec.h" +#include "Framework/Task.h" + +class FairMQDevice; + +namespace o2::monitoring +{ +class Monitoring; +} + +namespace o2::utilities +{ + +class DataSamplingHeader; +class DataSamplingPolicy; + +class Dispatcher : public framework::Task +{ + public: + /// \brief Constructor + Dispatcher(const std::string name, const std::string reconfigurationSource); + /// \brief Destructor + ~Dispatcher() override; + + /// \brief Dispatcher init callback + void init(framework::InitContext& ctx) override; + /// \brief Dispatcher process callback + void run(framework::ProcessingContext& ctx) override; + + /// \brief Register a Data Sampling Policy + void registerPolicy(std::unique_ptr<DataSamplingPolicy>&&); + /// \brief Returns the number of registered policies. + size_t numberOfPolicies(); + + const std::string& getName(); + /// \brief Assembles InputSpecs of all registered policies in a single vector, removing overlapping entries. + framework::Inputs getInputSpecs(); + framework::Outputs getOutputSpecs(); + framework::Options getOptions(); + + private: + DataSamplingHeader prepareDataSamplingHeader(const DataSamplingPolicy& policy, const framework::DeviceSpec& spec); + header::Stack extractAdditionalHeaders(const char* inputHeaderStack) const; + void reportStats(monitoring::Monitoring& monitoring) const; + void send(framework::DataAllocator& dataAllocator, const framework::DataRef& inputData, framework::Output&& output) const; + void sendFairMQ(FairMQDevice* device, const framework::DataRef& inputData, const std::string& fairMQChannel, + header::Stack&& stack) const; + + std::string mName; + std::string mReconfigurationSource; + // policies should be shared between all pipeline threads + std::vector<std::shared_ptr<DataSamplingPolicy>> mPolicies; +}; + +} // namespace o2::utilities + +#endif //ALICEO2_DISPATCHER_H diff --git a/Framework/Core/scripts/o2-datasampling-benchmark.sh b/Utilities/DataSampling/scripts/o2-datasampling-benchmark.sh similarity index 100% rename from Framework/Core/scripts/o2-datasampling-benchmark.sh rename to Utilities/DataSampling/scripts/o2-datasampling-benchmark.sh diff --git a/Framework/Core/src/DataSampling.cxx b/Utilities/DataSampling/src/DataSampling.cxx similarity index 82% rename from Framework/Core/src/DataSampling.cxx rename to Utilities/DataSampling/src/DataSampling.cxx index baea5b9f05489..766dadd9e22b3 100644 --- a/Framework/Core/src/DataSampling.cxx +++ b/Utilities/DataSampling/src/DataSampling.cxx @@ -13,9 +13,9 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "Framework/DataSampling.h" -#include "Framework/DataSamplingPolicy.h" -#include "Framework/Dispatcher.h" +#include "DataSampling/DataSampling.h" +#include "DataSampling/DataSamplingPolicy.h" +#include "DataSampling/Dispatcher.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DataSpecUtils.h" #include "Framework/Logger.h" @@ -24,9 +24,10 @@ #include <Configuration/ConfigurationFactory.h> using namespace o2::configuration; +using namespace o2::framework; using SubSpecificationType = o2::header::DataHeader::SubSpecificationType; -namespace o2::framework +namespace o2::utilities { std::string DataSampling::createDispatcherName() @@ -37,6 +38,10 @@ std::string DataSampling::createDispatcherName() void DataSampling::GenerateInfrastructure(WorkflowSpec& workflow, const std::string& policiesSource, size_t threads) { std::unique_ptr<ConfigurationInterface> cfg = ConfigurationFactory::getConfiguration(policiesSource); + if (cfg->getRecursive("").count("dataSamplingPolicies") == 0) { + LOG(WARN) << "No \"dataSamplingPolicies\" structure found in the config file. If no Data Sampling is expected, then it is completely fine."; + return; + } auto policiesTree = cfg->getRecursive("dataSamplingPolicies"); Dispatcher dispatcher(createDispatcherName(), policiesSource); DataSampling::DoGenerateInfrastructure(dispatcher, workflow, policiesTree, threads); @@ -51,7 +56,6 @@ void DataSampling::GenerateInfrastructure(WorkflowSpec& workflow, const boost::p void DataSampling::DoGenerateInfrastructure(Dispatcher& dispatcher, WorkflowSpec& workflow, const boost::property_tree::ptree& policiesTree, size_t threads) { LOG(DEBUG) << "Generating Data Sampling infrastructure..."; - Options options; for (auto&& policyConfig : policiesTree) { @@ -59,7 +63,7 @@ void DataSampling::DoGenerateInfrastructure(Dispatcher& dispatcher, WorkflowSpec // We don't want the Dispatcher to exit due to one faulty Policy try { - policy = std::make_unique<DataSamplingPolicy>(policyConfig.second); + dispatcher.registerPolicy(std::make_unique<DataSamplingPolicy>(DataSamplingPolicy::fromConfiguration(policyConfig.second))); } catch (const std::exception& ex) { LOG(WARN) << "Could not load the Data Sampling Policy '" << policyConfig.second.get_optional<std::string>("id").value_or("") << "', because: " << ex.what(); @@ -69,27 +73,17 @@ void DataSampling::DoGenerateInfrastructure(Dispatcher& dispatcher, WorkflowSpec << policyConfig.second.get_optional<std::string>("id").value_or("") << "'"; continue; } - - for (const auto& path : policy->getPathMap()) { - dispatcher.registerPath({path.first, path.second}); - } - - if (!policy->getFairMQOutputChannel().empty()) { - options.push_back({"channel-config", VariantType::String, policy->getFairMQOutputChannel().c_str(), {"Out-of-band channel config"}}); - LOG(DEBUG) << " - registering output FairMQ channel '" << policy->getFairMQOutputChannel() << "'"; - } } - options.push_back({"period-timer-stats", framework::VariantType::Int, 10 * 1000000, {"Dispatcher's stats timer period"}}); - if (dispatcher.getInputSpecs().size() > 0) { + if (dispatcher.numberOfPolicies() > 0) { DataProcessorSpec spec; spec.name = dispatcher.getName(); spec.inputs = dispatcher.getInputSpecs(); spec.outputs = dispatcher.getOutputSpecs(); - spec.algorithm = adaptFromTask<Dispatcher>(std::move(dispatcher)); spec.maxInputTimeslices = threads; spec.labels = {{"DataSampling"}, {"Dispatcher"}}; - spec.options = options; + spec.options = dispatcher.getOptions(); + spec.algorithm = adaptFromTask<Dispatcher>(std::move(dispatcher)); workflow.emplace_back(std::move(spec)); } else { @@ -122,7 +116,25 @@ std::vector<InputSpec> DataSampling::InputSpecsForPolicy(ConfigurationInterface* for (auto&& policyConfig : policiesTree) { if (policyConfig.second.get<std::string>("id") == policyName) { - DataSamplingPolicy policy(policyConfig.second); + auto policy = DataSamplingPolicy::fromConfiguration(policyConfig.second); + for (const auto& path : policy.getPathMap()) { + InputSpec input = DataSpecUtils::matchingInput(path.second); + inputs.push_back(input); + } + break; + } + } + return inputs; +} + +std::vector<InputSpec> DataSampling::InputSpecsForPolicy(std::shared_ptr<configuration::ConfigurationInterface> config, const std::string& policyName) +{ + std::vector<InputSpec> inputs; + auto policiesTree = config->getRecursive("dataSamplingPolicies"); + + for (auto&& policyConfig : policiesTree) { + if (policyConfig.second.get<std::string>("id") == policyName) { + auto policy = DataSamplingPolicy::fromConfiguration(policyConfig.second); for (const auto& path : policy.getPathMap()) { InputSpec input = DataSpecUtils::matchingInput(path.second); inputs.push_back(input); @@ -146,7 +158,7 @@ std::vector<OutputSpec> DataSampling::OutputSpecsForPolicy(ConfigurationInterfac for (auto&& policyConfig : policiesTree) { if (policyConfig.second.get<std::string>("id") == policyName) { - DataSamplingPolicy policy(policyConfig.second); + auto policy = DataSamplingPolicy::fromConfiguration(policyConfig.second); for (const auto& path : policy.getPathMap()) { outputs.push_back(path.second); } @@ -194,4 +206,4 @@ std::vector<std::string> DataSampling::MachinesForPolicy(const std::string& poli return MachinesForPolicy(config.get(), policyName); } -} // namespace o2::framework +} // namespace o2::utilities diff --git a/Framework/Core/src/DataSamplingConditionCustom.cxx b/Utilities/DataSampling/src/DataSamplingConditionCustom.cxx similarity index 93% rename from Framework/Core/src/DataSamplingConditionCustom.cxx rename to Utilities/DataSampling/src/DataSamplingConditionCustom.cxx index f02d9128df274..f9dca006e8b5b 100644 --- a/Framework/Core/src/DataSamplingConditionCustom.cxx +++ b/Utilities/DataSampling/src/DataSamplingConditionCustom.cxx @@ -13,8 +13,8 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "Framework/DataSamplingCondition.h" -#include "Framework/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingCondition.h" +#include "DataSampling/DataSamplingConditionFactory.h" #include "Headers/DataHeader.h" #include <fairlogger/Logger.h> @@ -24,7 +24,9 @@ #include <TSystem.h> #include <boost/property_tree/ptree.hpp> -namespace o2::framework +using namespace o2::framework; + +namespace o2::utilities { /// \brief A DataSamplingCondition which makes decisions based on payload size. @@ -81,4 +83,4 @@ std::unique_ptr<DataSamplingCondition> DataSamplingConditionFactory::createDataS return std::make_unique<DataSamplingConditionCustom>(); } -} // namespace o2::framework +} // namespace o2::utilities diff --git a/Framework/Core/src/DataSamplingConditionFactory.cxx b/Utilities/DataSampling/src/DataSamplingConditionFactory.cxx similarity index 90% rename from Framework/Core/src/DataSamplingConditionFactory.cxx rename to Utilities/DataSampling/src/DataSamplingConditionFactory.cxx index 251b4925320ec..94dc64bd746a4 100644 --- a/Framework/Core/src/DataSamplingConditionFactory.cxx +++ b/Utilities/DataSampling/src/DataSamplingConditionFactory.cxx @@ -16,9 +16,10 @@ #include <memory> #include <stdexcept> -#include "Framework/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingConditionFactory.h" +using namespace o2::framework; -namespace o2::framework +namespace o2::utilities { std::unique_ptr<DataSamplingCondition> DataSamplingConditionFactory::create(std::string name) @@ -35,4 +36,4 @@ std::unique_ptr<DataSamplingCondition> DataSamplingConditionFactory::create(std: throw std::runtime_error("DataSamplingCondition '" + name + "' unknown."); } -} // namespace o2::framework +} // namespace o2::utilities diff --git a/Framework/Core/src/DataSamplingConditionNConsecutive.cxx b/Utilities/DataSampling/src/DataSamplingConditionNConsecutive.cxx similarity index 92% rename from Framework/Core/src/DataSamplingConditionNConsecutive.cxx rename to Utilities/DataSampling/src/DataSamplingConditionNConsecutive.cxx index 4d42df42f4ae0..48f63f4c79e44 100644 --- a/Framework/Core/src/DataSamplingConditionNConsecutive.cxx +++ b/Utilities/DataSampling/src/DataSamplingConditionNConsecutive.cxx @@ -13,14 +13,16 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "Framework/DataSamplingCondition.h" -#include "Framework/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingCondition.h" +#include "DataSampling/DataSamplingConditionFactory.h" #include "Framework/DataProcessingHeader.h" #include "Framework/Logger.h" #include <boost/property_tree/ptree.hpp> -namespace o2::framework +using namespace o2::framework; + +namespace o2::utilities { using namespace o2::header; @@ -65,4 +67,4 @@ std::unique_ptr<DataSamplingCondition> DataSamplingConditionFactory::createDataS return std::make_unique<DataSamplingConditionNConsecutive>(); } -} // namespace o2::framework +} // namespace o2::utilities diff --git a/Framework/Core/src/DataSamplingConditionPayloadSize.cxx b/Utilities/DataSampling/src/DataSamplingConditionPayloadSize.cxx similarity index 91% rename from Framework/Core/src/DataSamplingConditionPayloadSize.cxx rename to Utilities/DataSampling/src/DataSamplingConditionPayloadSize.cxx index 5a5b1adc3582f..3afa25b0d4dbd 100644 --- a/Framework/Core/src/DataSamplingConditionPayloadSize.cxx +++ b/Utilities/DataSampling/src/DataSamplingConditionPayloadSize.cxx @@ -13,13 +13,15 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "Framework/DataSamplingCondition.h" -#include "Framework/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingCondition.h" +#include "DataSampling/DataSamplingConditionFactory.h" #include "Headers/DataHeader.h" #include "Framework/Logger.h" #include <boost/property_tree/ptree.hpp> -namespace o2::framework +using namespace o2::framework; + +namespace o2::utilities { using namespace o2::header; @@ -62,4 +64,4 @@ std::unique_ptr<DataSamplingCondition> DataSamplingConditionFactory::createDataS return std::make_unique<DataSamplingConditionPayloadSize>(); } -} // namespace o2::framework +} // namespace o2::utilities diff --git a/Framework/Core/src/DataSamplingConditionRandom.cxx b/Utilities/DataSampling/src/DataSamplingConditionRandom.cxx similarity index 93% rename from Framework/Core/src/DataSamplingConditionRandom.cxx rename to Utilities/DataSampling/src/DataSamplingConditionRandom.cxx index 60b7def97440c..48b00b929a9cb 100644 --- a/Framework/Core/src/DataSamplingConditionRandom.cxx +++ b/Utilities/DataSampling/src/DataSamplingConditionRandom.cxx @@ -13,15 +13,17 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "Framework/DataSamplingCondition.h" -#include "Framework/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingCondition.h" +#include "DataSampling/DataSamplingConditionFactory.h" #include "Framework/DataProcessingHeader.h" #include "PCG/pcg_random.hpp" #include <boost/property_tree/ptree.hpp> -namespace o2::framework +using namespace o2::framework; + +namespace o2::utilities { // todo: consider using run number as a seed @@ -82,4 +84,4 @@ std::unique_ptr<DataSamplingCondition> DataSamplingConditionFactory::createDataS return std::make_unique<DataSamplingConditionRandom>(); } -} // namespace o2::framework +} // namespace o2::utilities diff --git a/Framework/Core/src/DataSamplingHeader.cxx b/Utilities/DataSampling/src/DataSamplingHeader.cxx similarity index 84% rename from Framework/Core/src/DataSamplingHeader.cxx rename to Utilities/DataSampling/src/DataSamplingHeader.cxx index b887e7b4fc787..95e21088a9df7 100644 --- a/Framework/Core/src/DataSamplingHeader.cxx +++ b/Utilities/DataSampling/src/DataSamplingHeader.cxx @@ -13,9 +13,9 @@ /// /// \author Piotr Konopka, piotr.jan.konopka@cern.ch -#include "Framework/DataSamplingHeader.h" +#include "DataSampling/DataSamplingHeader.h" -namespace o2::framework +namespace o2::utilities { DataSamplingHeader::DataSamplingHeader() : BaseHeader(sizeof(DataSamplingHeader), sHeaderType, sSerializationMethod, sVersion) @@ -37,8 +37,8 @@ const DataSamplingHeader* DataSamplingHeader::Get(const BaseHeader* baseHeader) } // storage for DataSamplingHeader static members -const uint32_t o2::framework::DataSamplingHeader::sVersion = 1; -const o2::header::HeaderType o2::framework::DataSamplingHeader::sHeaderType = header::String2<uint64_t>("DataSamp"); -const o2::header::SerializationMethod o2::framework::DataSamplingHeader::sSerializationMethod = o2::header::gSerializationMethodNone; +const uint32_t o2::utilities::DataSamplingHeader::sVersion = 1; +const o2::header::HeaderType o2::utilities::DataSamplingHeader::sHeaderType = header::String2<uint64_t>("DataSamp"); +const o2::header::SerializationMethod o2::utilities::DataSamplingHeader::sSerializationMethod = o2::header::gSerializationMethodNone; -} // namespace o2::framework \ No newline at end of file +} // namespace o2::utilities \ No newline at end of file diff --git a/Utilities/DataSampling/src/DataSamplingPolicy.cxx b/Utilities/DataSampling/src/DataSamplingPolicy.cxx new file mode 100644 index 0000000000000..452f61ab14cfd --- /dev/null +++ b/Utilities/DataSampling/src/DataSamplingPolicy.cxx @@ -0,0 +1,188 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 DataSamplingPolicy.cxx +/// \brief Implementation of O2 Data Sampling Policy +/// +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#include "DataSampling/DataSamplingPolicy.h" +#include "DataSampling/DataSamplingHeader.h" +#include "DataSampling/DataSamplingConditionFactory.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/DataDescriptorQueryBuilder.h" +#include "Framework/Logger.h" + +#include <boost/property_tree/ptree.hpp> + +using namespace o2::framework; + +namespace o2::utilities +{ + +using boost::property_tree::ptree; + +DataSamplingPolicy::DataSamplingPolicy(std::string name) : mName(std::move(name)) +{ +} + +void DataSamplingPolicy::registerPath(const InputSpec& inputSpec, const OutputSpec& outputSpec) +{ + mPaths.emplace_back(inputSpec, outputSpec); +} + +void DataSamplingPolicy::registerCondition(std::unique_ptr<DataSamplingCondition>&& condition) +{ + mConditions.emplace_back(std::move(condition)); +} + +void DataSamplingPolicy::setFairMQOutputChannel(std::string channel) +{ + mFairMQOutputChannel = std::move(channel); +} + +DataSamplingPolicy DataSamplingPolicy::fromConfiguration(const ptree& config) +{ + auto name = config.get<std::string>("id"); + DataSamplingPolicy policy(name); + + size_t outputId = 0; + std::vector<InputSpec> inputSpecs = DataDescriptorQueryBuilder::parse(config.get<std::string>("query").c_str()); + std::vector<OutputSpec> outputSpecs; + // Optionally user can specify the outputs, + if (auto outputsQuery = config.get<std::string>("outputs", ""); !outputsQuery.empty()) { + std::vector<InputSpec> outputsAsInputSpecs = DataDescriptorQueryBuilder::parse(outputsQuery.c_str()); + if (outputsAsInputSpecs.size() != inputSpecs.size()) { + throw std::runtime_error( + "The number of outputs should match the number of inputs (queries)," + " which is not the case for the policy '" + + name + "'(" + + std::to_string(inputSpecs.size()) + " inputs vs. " + std::to_string(outputsAsInputSpecs.size()) + " outputs)."); + } + for (const auto& outputAsInputSpec : outputsAsInputSpecs) { + outputSpecs.emplace_back(DataSpecUtils::asOutputSpec(outputAsInputSpec)); + } + } else { // otherwise default format will be used + for (const auto& inputSpec : inputSpecs) { + if (DataSpecUtils::getOptionalSubSpec(inputSpec).has_value()) { + outputSpecs.emplace_back(OutputSpec{ + {inputSpec.binding}, + createPolicyDataOrigin(), + createPolicyDataDescription(name, outputId++), + DataSpecUtils::getOptionalSubSpec(inputSpec).value(), + inputSpec.lifetime}); + } else { + outputSpecs.emplace_back(OutputSpec{ + {inputSpec.binding}, + {createPolicyDataOrigin(), createPolicyDataDescription(name, outputId++)}, + inputSpec.lifetime}); + } + } + } + assert(inputSpecs.size() == outputSpecs.size()); + for (size_t i = 0; i < inputSpecs.size(); i++) { + policy.registerPath(inputSpecs[i], outputSpecs[i]); + } + + for (const auto& conditionConfig : config.get_child("samplingConditions")) { + auto condition = DataSamplingConditionFactory::create(conditionConfig.second.get<std::string>("condition")); + condition->configure(conditionConfig.second); + policy.registerCondition(std::move(condition)); + } + + policy.setFairMQOutputChannel(config.get_optional<std::string>("fairMQOutput").value_or("")); + + return policy; +} + +bool DataSamplingPolicy::match(const ConcreteDataMatcher& input) const +{ + return mPaths.find(input) != mPaths.end(); +} + +bool DataSamplingPolicy::decide(const o2::framework::DataRef& dataRef) +{ + bool decision = std::all_of(mConditions.begin(), mConditions.end(), + [dataRef](std::unique_ptr<DataSamplingCondition>& condition) { + return condition->decide(dataRef); + }); + + mTotalAcceptedMessages += decision; + mTotalEvaluatedMessages++; + + return decision; +} + +Output DataSamplingPolicy::prepareOutput(const ConcreteDataMatcher& input, Lifetime lifetime) const +{ + auto result = mPaths.find(input); + if (result != mPaths.end()) { + auto dataType = DataSpecUtils::asConcreteDataTypeMatcher(result->second); + return Output{dataType.origin, dataType.description, input.subSpec, lifetime}; + } else { + return Output{header::gDataOriginInvalid, header::gDataDescriptionInvalid}; + } +} + +const std::string& DataSamplingPolicy::getName() const +{ + return mName; +} + +const DataSamplingPolicy::PathMap& DataSamplingPolicy::getPathMap() const +{ + return mPaths; +} + +const std::string& DataSamplingPolicy::getFairMQOutputChannel() const +{ + return mFairMQOutputChannel; +} + +std::string DataSamplingPolicy::getFairMQOutputChannelName() const +{ + size_t nameBegin = mFairMQOutputChannel.find("name=") + sizeof("name=") - 1; + size_t nameEnd = mFairMQOutputChannel.find_first_of(',', nameBegin); + std::string name = mFairMQOutputChannel.substr(nameBegin, nameEnd - nameBegin); + return name; +} + +uint32_t DataSamplingPolicy::getTotalAcceptedMessages() const +{ + return mTotalAcceptedMessages; +} +uint32_t DataSamplingPolicy::getTotalEvaluatedMessages() const +{ + return mTotalEvaluatedMessages; +} + +header::DataOrigin DataSamplingPolicy::createPolicyDataOrigin() +{ + return header::DataOrigin("DS"); +} + +header::DataDescription DataSamplingPolicy::createPolicyDataDescription(std::string policyName, size_t id) +{ + if (id > 99) { + throw std::runtime_error("Maximum 100 inputs in DataSamplingPolicy are supported. Call the developers if you really need more."); + } + + if (policyName.size() > 14) { + LOG(WARNING) << "DataSamplingPolicy name '" << policyName << "' is longer than 14 characters, we have to trim it. " + << "Use a shorter policy name to avoid potential output name conflicts."; + policyName.resize(14); + } + + header::DataDescription outputDescription; + outputDescription.runtimeInit((policyName + std::to_string(id)).c_str()); + return outputDescription; +} + +} // namespace o2::utilities diff --git a/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx b/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx new file mode 100644 index 0000000000000..4c613af6eb6f1 --- /dev/null +++ b/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DataSampling/DataSamplingReadoutAdapter.h" +#include "Framework/DataProcessingHeader.h" +#include "Headers/DataHeader.h" +#include "Framework/DataSpecUtils.h" +#include <atomic> + +using namespace o2::framework; + +namespace o2::utilities +{ + +using DataHeader = o2::header::DataHeader; + +static std::atomic<unsigned int> blockId = 0; + +InjectorFunction dataSamplingReadoutAdapter(OutputSpec const& spec) +{ + return [spec](FairMQDevice& device, FairMQParts& parts, ChannelRetriever channelRetriever) { + for (size_t i = 0; i < parts.Size(); ++i) { + + DataHeader dh; + ConcreteDataTypeMatcher dataType = DataSpecUtils::asConcreteDataTypeMatcher(spec); + dh.dataOrigin = dataType.origin; + dh.dataDescription = dataType.description; + dh.subSpecification = DataSpecUtils::getOptionalSubSpec(spec).value_or(0xFF); + dh.payloadSize = parts.At(i)->GetSize(); + dh.payloadSerializationMethod = o2::header::gSerializationMethodNone; + + DataProcessingHeader dph{++blockId, 0}; + o2::header::Stack headerStack{dh, dph}; + sendOnChannel(device, std::move(headerStack), std::move(parts.At(i)), spec, channelRetriever); + } + }; +} + +} // namespace o2::utilities diff --git a/Utilities/DataSampling/src/Dispatcher.cxx b/Utilities/DataSampling/src/Dispatcher.cxx new file mode 100644 index 0000000000000..e4e434ff66fa8 --- /dev/null +++ b/Utilities/DataSampling/src/Dispatcher.cxx @@ -0,0 +1,267 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Dispatcher.cxx +/// \brief Implementation of Dispatcher for O2 Data Sampling +/// +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#include "DataSampling/Dispatcher.h" +#include "Framework/RawDeviceService.h" +#include "DataSampling/DataSamplingPolicy.h" +#include "DataSampling/DataSamplingHeader.h" +#include "Framework/DataProcessingHeader.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/Logger.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputRecordWalker.h" + +#include "Framework/Monitoring.h" +#include <Configuration/ConfigurationInterface.h> +#include <Configuration/ConfigurationFactory.h> +#include <fairmq/FairMQDevice.h> + +using namespace o2::configuration; +using namespace o2::monitoring; +using namespace o2::framework; + +namespace o2::utilities +{ + +Dispatcher::Dispatcher(std::string name, const std::string reconfigurationSource) + : mName(name), mReconfigurationSource(reconfigurationSource) +{ +} + +Dispatcher::~Dispatcher() = default; + +void Dispatcher::init(InitContext& ctx) +{ + LOG(DEBUG) << "Reading Data Sampling Policies..."; + + boost::property_tree::ptree policiesTree; + + if (mReconfigurationSource.empty() == false) { + std::unique_ptr<ConfigurationInterface> cfg = ConfigurationFactory::getConfiguration(mReconfigurationSource); + policiesTree = cfg->getRecursive("dataSamplingPolicies"); + mPolicies.clear(); + } else if (ctx.options().isSet("sampling-config-ptree")) { + policiesTree = ctx.options().get<boost::property_tree::ptree>("sampling-config-ptree"); + mPolicies.clear(); + } else { + ; // we use policies declared during workflow init. + } + + for (auto&& policyConfig : policiesTree) { + // we don't want the Dispatcher to exit due to one faulty Policy + try { + mPolicies.emplace_back(std::make_shared<DataSamplingPolicy>(DataSamplingPolicy::fromConfiguration(policyConfig.second))); + } catch (std::exception& ex) { + LOG(WARN) << "Could not load the Data Sampling Policy '" + << policyConfig.second.get_optional<std::string>("id").value_or("") << "', because: " << ex.what(); + } catch (...) { + LOG(WARN) << "Could not load the Data Sampling Policy '" + << policyConfig.second.get_optional<std::string>("id").value_or("") << "'"; + } + } +} + +void Dispatcher::run(ProcessingContext& ctx) +{ + for (const auto& input : InputRecordWalker(ctx.inputs())) { + + const auto* inputHeader = header::get<header::DataHeader*>(input.header); + ConcreteDataMatcher inputMatcher{inputHeader->dataOrigin, inputHeader->dataDescription, inputHeader->subSpecification}; + + for (auto& policy : mPolicies) { + // todo: consider getting the outputSpec in match to improve performance + // todo: consider matching (and deciding) in completion policy to save some time + + if (policy->match(inputMatcher) && policy->decide(input)) { + // We copy every header which is not DataHeader or DataProcessingHeader, + // so that custom data-dependent headers are passed forward, + // and we add a DataSamplingHeader. + header::Stack headerStack{ + std::move(extractAdditionalHeaders(input.header)), + std::move(prepareDataSamplingHeader(*policy.get(), ctx.services().get<const DeviceSpec>()))}; + + if (!policy->getFairMQOutputChannel().empty()) { + sendFairMQ(ctx.services().get<RawDeviceService>().device(), input, policy->getFairMQOutputChannelName(), + std::move(headerStack)); + } else { + Output output = policy->prepareOutput(inputMatcher, input.spec->lifetime); + output.metaHeader = std::move(header::Stack{std::move(output.metaHeader), std::move(headerStack)}); + send(ctx.outputs(), input, std::move(output)); + } + } + } + } + + if (ctx.inputs().isValid("timer-stats")) { + reportStats(ctx.services().get<Monitoring>()); + } +} + +void Dispatcher::reportStats(Monitoring& monitoring) const +{ + uint64_t dispatcherTotalEvaluatedMessages = 0; + uint64_t dispatcherTotalAcceptedMessages = 0; + + for (const auto& policy : mPolicies) { + dispatcherTotalEvaluatedMessages += policy->getTotalEvaluatedMessages(); + dispatcherTotalAcceptedMessages += policy->getTotalAcceptedMessages(); + } + + monitoring.send({dispatcherTotalEvaluatedMessages, "Dispatcher_messages_evaluated"}); + monitoring.send({dispatcherTotalAcceptedMessages, "Dispatcher_messages_passed"}); +} + +DataSamplingHeader Dispatcher::prepareDataSamplingHeader(const DataSamplingPolicy& policy, const DeviceSpec& spec) +{ + uint64_t sampleTime = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count()); + + DataSamplingHeader::DeviceIDType id; + id.runtimeInit(spec.id.substr(0, DataSamplingHeader::deviceIDTypeSize).c_str()); + + return { + sampleTime, + policy.getTotalAcceptedMessages(), + policy.getTotalEvaluatedMessages(), + id}; +} + +header::Stack Dispatcher::extractAdditionalHeaders(const char* inputHeaderStack) const +{ + header::Stack headerStack; + + const auto* first = header::BaseHeader::get(reinterpret_cast<const byte*>(inputHeaderStack)); + for (const auto* current = first; current != nullptr; current = current->next()) { + if (current->description != header::DataHeader::sHeaderType && + current->description != DataProcessingHeader::sHeaderType) { + headerStack = std::move(header::Stack{std::move(headerStack), *current}); + } + } + + return headerStack; +} + +void Dispatcher::send(DataAllocator& dataAllocator, const DataRef& inputData, Output&& output) const +{ + const auto* inputHeader = header::get<header::DataHeader*>(inputData.header); + dataAllocator.snapshot(output, inputData.payload, inputHeader->payloadSize, inputHeader->payloadSerializationMethod); +} + +// ideally this should be in a separate proxy device or use Lifetime::External +void Dispatcher::sendFairMQ(FairMQDevice* device, const DataRef& inputData, const std::string& fairMQChannel, + header::Stack&& stack) const +{ + const auto* dh = header::get<header::DataHeader*>(inputData.header); + assert(dh); + const auto* dph = header::get<DataProcessingHeader*>(inputData.header); + assert(dph); + + header::DataHeader dhout{dh->dataDescription, dh->dataOrigin, dh->subSpecification, dh->payloadSize}; + dhout.payloadSerializationMethod = dh->payloadSerializationMethod; + DataProcessingHeader dphout{dph->startTime, dph->duration}; + o2::header::Stack headerStack{dhout, dphout, stack}; + + auto channelAlloc = o2::pmr::getTransportAllocator(device->Transport()); + FairMQMessagePtr msgHeaderStack = o2::pmr::getMessage(std::move(headerStack), channelAlloc); + + char* payloadCopy = new char[dh->payloadSize]; + memcpy(payloadCopy, inputData.payload, dh->payloadSize); + auto cleanupFcn = [](void* data, void*) { delete[] reinterpret_cast<char*>(data); }; + FairMQMessagePtr msgPayload(device->NewMessage(payloadCopy, dh->payloadSize, cleanupFcn, payloadCopy)); + + FairMQParts message; + message.AddPart(move(msgHeaderStack)); + message.AddPart(move(msgPayload)); + + int64_t bytesSent = device->Send(message, fairMQChannel); +} + +void Dispatcher::registerPolicy(std::unique_ptr<DataSamplingPolicy>&& policy) +{ + mPolicies.emplace_back(std::move(policy)); +} + +const std::string& Dispatcher::getName() +{ + return mName; +} + +Inputs Dispatcher::getInputSpecs() +{ + Inputs declaredInputs; + + // Add data inputs. Avoid duplicates and inputs which include others (e.g. "TST/DATA" includes "TST/DATA/1". + for (const auto& policy : mPolicies) { + for (const auto& path : policy->getPathMap()) { + auto& potentiallyNewInput = path.first; + + // The idea is that we remove all existing inputs which are covered by the potentially new input. + // If there are none which are broader than the new one, then we add it. + auto newInputIsBroader = [&potentiallyNewInput](const InputSpec& other) { + return DataSpecUtils::includes(potentiallyNewInput, other); + }; + declaredInputs.erase(std::remove_if(declaredInputs.begin(), declaredInputs.end(), newInputIsBroader), declaredInputs.end()); + + auto declaredInputIsBroader = [&potentiallyNewInput](const InputSpec& other) { + return DataSpecUtils::includes(other, potentiallyNewInput); + }; + if (std::none_of(declaredInputs.begin(), declaredInputs.end(), declaredInputIsBroader)) { + declaredInputs.push_back(potentiallyNewInput); + } + } + } + + // add timer input + header::DataDescription timerDescription; + timerDescription.runtimeInit(("TIMER-" + mName).substr(0, 16).c_str()); + declaredInputs.emplace_back(InputSpec{"timer-stats", "DS", timerDescription, 0, Lifetime::Timer}); + + return declaredInputs; +} + +Outputs Dispatcher::getOutputSpecs() +{ + Outputs declaredOutputs; + for (const auto& policy : mPolicies) { + for (const auto& [_policyInput, policyOutput] : policy->getPathMap()) { + (void)_policyInput; + // In principle Data Sampling Policies should have different outputs. + // We may add a check to be very gentle with users. + declaredOutputs.push_back(policyOutput); + } + } + return declaredOutputs; +} +framework::Options Dispatcher::getOptions() +{ + o2::framework::Options options; + for (const auto& policy : mPolicies) { + if (!policy->getFairMQOutputChannel().empty()) { + if (!options.empty()) { + throw std::runtime_error("Maximum one policy with raw FairMQ channel is allowed, more have been declared."); + } + options.push_back({"channel-config", VariantType::String, policy->getFairMQOutputChannel().c_str(), {"Out-of-band channel config"}}); + LOG(DEBUG) << " - registering output FairMQ channel '" << policy->getFairMQOutputChannel() << "'"; + } + } + options.push_back({"period-timer-stats", framework::VariantType::Int, 10 * 1000000, {"Dispatcher's stats timer period"}}); + + return options; +} +size_t Dispatcher::numberOfPolicies() +{ + return mPolicies.size(); +} + +} // namespace o2::utilities diff --git a/Utilities/DataSampling/src/dataSamplingStandalone.cxx b/Utilities/DataSampling/src/dataSamplingStandalone.cxx new file mode 100644 index 0000000000000..e6fb96fbdbdd5 --- /dev/null +++ b/Utilities/DataSampling/src/dataSamplingStandalone.cxx @@ -0,0 +1,45 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 "DataSampling/DataSampling.h" +#include "Framework/CompletionPolicyHelpers.h" +#include <vector> + +using namespace o2::framework; +using namespace o2::utilities; + +void customize(std::vector<CompletionPolicy>& policies) +{ + DataSampling::CustomizeInfrastructure(policies); +} + +void customize(std::vector<ChannelConfigurationPolicy>& policies) +{ + DataSampling::CustomizeInfrastructure(policies); +} + +void customize(std::vector<ConfigParamSpec>& workflowOptions) +{ + workflowOptions.push_back(ConfigParamSpec{"config", VariantType::String, "", {"path to the Data Sampling configuration file"}}); + workflowOptions.push_back(ConfigParamSpec{"dispatchers", VariantType::Int, 1, {"amount of parallel Dispatchers"}}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + auto configurationPath = config.options().get<std::string>("config"); + auto numberOfDispatchers = config.options().get<int>("dispatchers"); + + WorkflowSpec specs; + DataSampling::GenerateInfrastructure(specs, configurationPath, numberOfDispatchers); + return specs; +} \ No newline at end of file diff --git a/Framework/TestWorkflows/src/dataSamplingBenchmark.cxx b/Utilities/DataSampling/test/dataSamplingBenchmark.cxx similarity index 98% rename from Framework/TestWorkflows/src/dataSamplingBenchmark.cxx rename to Utilities/DataSampling/test/dataSamplingBenchmark.cxx index cd111e16ae2f0..8d5773b9eeb75 100644 --- a/Framework/TestWorkflows/src/dataSamplingBenchmark.cxx +++ b/Utilities/DataSampling/test/dataSamplingBenchmark.cxx @@ -9,11 +9,12 @@ // or submit itself to any jurisdiction. #include "Framework/ConfigParamSpec.h" -#include "Framework/DataSampling.h" +#include "DataSampling/DataSampling.h" #include "Framework/CompletionPolicyHelpers.h" #include <vector> using namespace o2::framework; +using namespace o2::utilities; void customize(std::vector<CompletionPolicy>& policies) { @@ -62,12 +63,13 @@ void sysinfo(sysinfo* info) #include "Headers/DataHeader.h" #include "Framework/ControlService.h" -#include "Framework/DataSampling.h" -#include "Framework/DataSamplingPolicy.h" +#include "DataSampling/DataSampling.h" +#include "DataSampling/DataSamplingPolicy.h" #include "Framework/RawDeviceService.h" #include "Framework/runDataProcessing.h" using namespace o2::framework; +using namespace o2::utilities; using SubSpec = o2::header::DataHeader::SubSpecificationType; namespace bipc = ::boost::interprocess; diff --git a/Framework/TestWorkflows/src/dataSamplingParallel.cxx b/Utilities/DataSampling/test/dataSamplingParallel.cxx similarity index 96% rename from Framework/TestWorkflows/src/dataSamplingParallel.cxx rename to Utilities/DataSampling/test/dataSamplingParallel.cxx index b0b70f52567b7..280f2733f2920 100644 --- a/Framework/TestWorkflows/src/dataSamplingParallel.cxx +++ b/Utilities/DataSampling/test/dataSamplingParallel.cxx @@ -8,11 +8,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Framework/DataSampling.h" +#include "DataSampling/DataSampling.h" #include <thread> using namespace o2::framework; +using namespace o2::utilities; void customize(std::vector<CompletionPolicy>& policies) { DataSampling::CustomizeInfrastructure(policies); @@ -24,7 +25,7 @@ void customize(std::vector<ChannelConfigurationPolicy>& policies) #include "Framework/InputSpec.h" #include "Framework/DataProcessorSpec.h" -#include "Framework/DataSampling.h" +#include "DataSampling/DataSampling.h" #include "Framework/DataSpecUtils.h" #include "Framework/ParallelContext.h" #include "Framework/runDataProcessing.h" @@ -97,8 +98,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) DataProcessorSpec simpleQcTask{ "simpleQcTask", Inputs{ - { "TPC_CLUSTERS_S", { "DS", "simpleQcTask-0" } }, - { "TPC_CLUSTERS_P_S", { "DS", "simpleQcTask-1" } } + { "TPC_CLUSTERS_S", { "DS", "simpleQcTask0" } }, + { "TPC_CLUSTERS_P_S", { "DS", "simpleQcTask1" } } }, Outputs{}, AlgorithmSpec{ diff --git a/Framework/TestWorkflows/src/dataSamplingPodAndRoot.cxx b/Utilities/DataSampling/test/dataSamplingPodAndRoot.cxx similarity index 96% rename from Framework/TestWorkflows/src/dataSamplingPodAndRoot.cxx rename to Utilities/DataSampling/test/dataSamplingPodAndRoot.cxx index 7c7e6656a242d..eeb8220985119 100644 --- a/Framework/TestWorkflows/src/dataSamplingPodAndRoot.cxx +++ b/Utilities/DataSampling/test/dataSamplingPodAndRoot.cxx @@ -9,10 +9,12 @@ // or submit itself to any jurisdiction. #include "Framework/RootSerializationSupport.h" -#include "Framework/DataSampling.h" +#include "DataSampling/DataSampling.h" #include <thread> using namespace o2::framework; +using namespace o2::utilities; + void customize(std::vector<CompletionPolicy>& policies) { DataSampling::CustomizeInfrastructure(policies); @@ -26,9 +28,10 @@ void customize(std::vector<ChannelConfigurationPolicy>& policies) #include "Framework/DataProcessorSpec.h" #include "Framework/runDataProcessing.h" #include "Framework/TMessageSerializer.h" -#include "Framework/DataSamplingHeader.h" +#include "DataSampling/DataSamplingHeader.h" #include "Framework/Logger.h" #include <TClonesArray.h> +#include <TObjString.h> #include <TH1F.h> #include <TString.h> @@ -84,8 +87,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) DataProcessorSpec qcTaskTpc{ "qcTaskTpc", Inputs{ - { "TPC_CLUSTERS_S", {"DS", "simpleQcTask-0"}}, - { "TPC_CLUSTERS_P_S", {"DS", "simpleQcTask-1"}} + { "TPC_CLUSTERS_S", {"DS", "simpleQcTask0"}}, + { "TPC_CLUSTERS_P_S", {"DS", "simpleQcTask1"}} }, Outputs{}, AlgorithmSpec{ @@ -177,8 +180,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) DataProcessorSpec rootQcTask{ "rootQcTask", { - InputSpec{ "TST_HISTOS_S", {"DS", "rootQcTask-0"}}, - InputSpec{ "TST_STRING_S", {"DS", "rootQcTask-1"}}, + InputSpec{ "TST_HISTOS_S", {"DS", "rootQcTask0"}}, + InputSpec{ "TST_STRING_S", {"DS", "rootQcTask1"}}, }, Outputs{}, AlgorithmSpec{ diff --git a/Framework/TestWorkflows/src/dataSamplingTimePipeline.cxx b/Utilities/DataSampling/test/dataSamplingTimePipeline.cxx similarity index 96% rename from Framework/TestWorkflows/src/dataSamplingTimePipeline.cxx rename to Utilities/DataSampling/test/dataSamplingTimePipeline.cxx index 255d5773f3de3..c5cc028d14c81 100644 --- a/Framework/TestWorkflows/src/dataSamplingTimePipeline.cxx +++ b/Utilities/DataSampling/test/dataSamplingTimePipeline.cxx @@ -8,11 +8,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Framework/DataSampling.h" +#include "DataSampling/DataSampling.h" #include <thread> using namespace o2::framework; +using namespace o2::utilities; + void customize(std::vector<CompletionPolicy>& policies) { DataSampling::CustomizeInfrastructure(policies); @@ -24,7 +26,7 @@ void customize(std::vector<ChannelConfigurationPolicy>& policies) #include "Framework/InputSpec.h" #include "Framework/DataProcessorSpec.h" -#include "Framework/DataSampling.h" +#include "DataSampling/DataSampling.h" #include "Framework/ParallelContext.h" #include "Framework/runDataProcessing.h" @@ -83,8 +85,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) DataProcessorSpec simpleQcTask{ "simpleQcTask", Inputs{ - { "TPC_CLUSTERS_S", { "DS", "simpleQcTask-0" } }, - { "TPC_CLUSTERS_P_S", { "DS", "simpleQcTask-1" } } + { "TPC_CLUSTERS_S", { "DS", "simpleQcTask0" } }, + { "TPC_CLUSTERS_P_S", { "DS", "simpleQcTask1" } } }, Outputs{}, AlgorithmSpec{ diff --git a/Utilities/DataSampling/test/test_DataSampling.cxx b/Utilities/DataSampling/test/test_DataSampling.cxx new file mode 100644 index 0000000000000..e3eff2d236310 --- /dev/null +++ b/Utilities/DataSampling/test/test_DataSampling.cxx @@ -0,0 +1,303 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Framework DataSampling +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> + +#include "DataSampling/DataSampling.h" +#include "DataSampling/Dispatcher.h" +#include "DataSampling/DataSamplingPolicy.h" +#include "Framework/DataProcessingHeader.h" +#include "Framework/ExternalFairMQDeviceProxy.h" +#include "DataSampling/DataSamplingReadoutAdapter.h" +#include "Framework/DataSpecUtils.h" + +#include "Headers/DataHeader.h" + +#include <Configuration/ConfigurationFactory.h> + +using namespace o2::framework; +using namespace o2::utilities; +using namespace o2::configuration; + +using DataHeader = o2::header::DataHeader; +using DataOrigin = o2::header::DataOrigin; +using DataDescription = o2::header::DataDescription; + +BOOST_AUTO_TEST_CASE(DataSamplingSimpleFlow) +{ + WorkflowSpec workflow{ + {"producer", + Inputs{}, + Outputs{{"TPC", "CLUSTERS"}}}, + {"processingStage", + Inputs{{"dataTPC", "TPC", "CLUSTERS"}}, + Outputs{{"TPC", "CLUSTERS_P"}}}}; + + std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; + std::cout << "config file : " + << "json:/" << configFilePath << std::endl; + DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath); + + auto disp = std::find_if(workflow.begin(), workflow.end(), + [](const DataProcessorSpec& d) { + return d.name.find("Dispatcher") != std::string::npos; + }); + BOOST_REQUIRE(disp != workflow.end()); + + auto input = std::find_if(disp->inputs.begin(), disp->inputs.end(), + [](const InputSpec& in) { + return DataSpecUtils::match(in, DataOrigin("TPC"), DataDescription("CLUSTERS"), 0) && in.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(input != disp->inputs.end()); + + input = std::find_if(disp->inputs.begin(), disp->inputs.end(), + [](const InputSpec& in) { + return DataSpecUtils::match(in, DataOrigin("TPC"), DataDescription("CLUSTERS_P"), 0) && in.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(input != disp->inputs.end()); + + auto output = std::find_if(disp->outputs.begin(), disp->outputs.end(), + [](const OutputSpec& out) { + return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters0", 0}) && out.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(output != disp->outputs.end()); + + output = std::find_if(disp->outputs.begin(), disp->outputs.end(), + [](const OutputSpec& out) { + return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters1", 0}) && out.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(output != disp->outputs.end()); + + BOOST_CHECK(disp->algorithm.onInit != nullptr); +} + +BOOST_AUTO_TEST_CASE(DataSamplingParallelFlow) +{ + WorkflowSpec workflow{ + {"producer", + Inputs{}, + {OutputSpec{"TPC", "CLUSTERS", 0}, + OutputSpec{"TPC", "CLUSTERS", 1}, + OutputSpec{"TPC", "CLUSTERS", 2}}}}; + + auto processingStages = parallel( + DataProcessorSpec{ + "processingStage", + Inputs{{"dataTPC", "TPC", "CLUSTERS"}}, + Outputs{{"TPC", "CLUSTERS_P"}}}, + 3, + [](DataProcessorSpec& spec, size_t index) { + DataSpecUtils::updateMatchingSubspec(spec.inputs[0], index); + DataSpecUtils::updateMatchingSubspec(spec.outputs[0], index); + }); + + workflow.insert(std::end(workflow), std::begin(processingStages), std::end(processingStages)); + + std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; + DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath); + + for (int i = 0; i < 3; ++i) { + auto disp = std::find_if(workflow.begin(), workflow.end(), + [i](const DataProcessorSpec& d) { + return d.name.find("Dispatcher") != std::string::npos; + }); + BOOST_REQUIRE(disp != workflow.end()); + + auto input = std::find_if(disp->inputs.begin(), disp->inputs.end(), + [i](const InputSpec& in) { + return DataSpecUtils::match(in, ConcreteDataMatcher{DataOrigin("TPC"), DataDescription("CLUSTERS"), static_cast<DataHeader::SubSpecificationType>(i)}) && in.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(input != disp->inputs.end()); + + input = std::find_if(disp->inputs.begin(), disp->inputs.end(), + [i](const InputSpec& in) { + return DataSpecUtils::match(in, ConcreteDataMatcher{DataOrigin("TPC"), DataDescription("CLUSTERS_P"), static_cast<DataHeader::SubSpecificationType>(i)}) && in.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(input != disp->inputs.end()); + + auto output = std::find_if(disp->outputs.begin(), disp->outputs.end(), + [](const OutputSpec& out) { + return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters0", 0}) && out.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(output != disp->outputs.end()); + + output = std::find_if(disp->outputs.begin(), disp->outputs.end(), + [](const OutputSpec& out) { + return DataSpecUtils::match(out, ConcreteDataMatcher{"DS", "tpcclusters1", 0}) && out.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(output != disp->outputs.end()); + + BOOST_CHECK(disp->algorithm.onInit != nullptr); + } +} + +BOOST_AUTO_TEST_CASE(DataSamplingTimePipelineFlow) +{ + WorkflowSpec workflow{ + {"producer", + Inputs{}, + {OutputSpec{"TPC", "CLUSTERS", 0, Lifetime::Timeframe}}}, + timePipeline( + DataProcessorSpec{ + "processingStage", + Inputs{ + {"dataTPC", "TPC", "CLUSTERS", 0, Lifetime::Timeframe}}, + Outputs{ + {"TPC", "CLUSTERS_P", 0, Lifetime::Timeframe}}}, + 3)}; + + std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; + DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath, 3); + + auto disp = std::find_if(workflow.begin(), workflow.end(), + [](const DataProcessorSpec& d) { + return d.name.find("Dispatcher") != std::string::npos; + }); + + BOOST_REQUIRE(disp != workflow.end()); + BOOST_CHECK_EQUAL(disp->inputs.size(), 4); + BOOST_CHECK_EQUAL(disp->outputs.size(), 3); + BOOST_CHECK(disp->algorithm.onInit != nullptr); + BOOST_CHECK_EQUAL(disp->maxInputTimeslices, 3); +} + +BOOST_AUTO_TEST_CASE(DataSamplingFairMq) +{ + WorkflowSpec workflow{ + specifyExternalFairMQDeviceProxy( + "readout-proxy", + Outputs{{"TPC", "RAWDATA"}}, + "fake-channel-config", + dataSamplingReadoutAdapter({"TPC", "RAWDATA"}))}; + + std::string configFilePath = std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; + DataSampling::GenerateInfrastructure(workflow, "json:/" + configFilePath); + + auto disp = std::find_if(workflow.begin(), workflow.end(), + [](const DataProcessorSpec& d) { + return d.name.find("Dispatcher") != std::string::npos; + }); + BOOST_REQUIRE(disp != workflow.end()); + + auto input = std::find_if(disp->inputs.begin(), disp->inputs.end(), + [](const InputSpec& in) { + return DataSpecUtils::match(in, ConcreteDataMatcher{DataOrigin("TPC"), DataDescription("RAWDATA"), 0}) && in.lifetime == Lifetime::Timeframe; + }); + BOOST_CHECK(input != disp->inputs.end()); + + auto channelConfig = std::find_if(disp->options.begin(), disp->options.end(), + [](const ConfigParamSpec& opt) { + return opt.name == "channel-config"; + }); + BOOST_REQUIRE(channelConfig != disp->options.end()); +} + +BOOST_AUTO_TEST_CASE(InputSpecsForPolicy) +{ + std::string configFilePath = "json:/" + std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSampling.json"; + std::vector<InputSpec> inputs = DataSampling::InputSpecsForPolicy(configFilePath, "tpcclusters"); + + BOOST_CHECK_EQUAL(inputs.size(), 2); + BOOST_CHECK(DataSpecUtils::match(inputs[0], ConcreteDataTypeMatcher{"DS", "tpcclusters0"})); + BOOST_CHECK_EQUAL(inputs[0].binding, "clusters"); + BOOST_CHECK(DataSpecUtils::match(inputs[1], ConcreteDataTypeMatcher{"DS", "tpcclusters1"})); + BOOST_CHECK_EQUAL(inputs[1].binding, "clusters_p"); + + std::unique_ptr<ConfigurationInterface> config = ConfigurationFactory::getConfiguration(configFilePath); + inputs = DataSampling::InputSpecsForPolicy(config.get(), "tpcclusters"); + + BOOST_CHECK_EQUAL(inputs.size(), 2); +} + +BOOST_AUTO_TEST_CASE(DataSamplingEmptyConfig) +{ + std::string configFilePath = "json:/" + std::string(getenv("O2_ROOT")) + "/share/tests/test_DataSamplingEmpty.json"; + + WorkflowSpec workflow; + BOOST_CHECK_NO_THROW(DataSampling::GenerateInfrastructure(workflow, configFilePath)); +} + +BOOST_AUTO_TEST_CASE(DataSamplingOverlappingInputs) +{ + { + // policy3 includes 1 and 2, so we should have only one inputspec for data, one for timer + Dispatcher dispatcher("dispatcher", ""); + auto policy1 = std::make_unique<DataSamplingPolicy>("policy1"); + policy1->registerPath({"vcxz", "TST", "AAAA", 0}, {{"erwv"}, "DS", "AAAA"}); + + auto policy2 = std::make_unique<DataSamplingPolicy>("policy2"); + policy2->registerPath({"fdsa", "TST", "AAAA", 0}, {{"fdsf"}, "DS", "BBBB"}); + + auto policy3 = std::make_unique<DataSamplingPolicy>("policy3"); + policy3->registerPath({"asdf", {"TST", "AAAA"}}, {{"erwv"}, "DS", "CCCC"}); + + dispatcher.registerPolicy(std::move(policy1)); + dispatcher.registerPolicy(std::move(policy2)); + dispatcher.registerPolicy(std::move(policy3)); + + auto inputs = dispatcher.getInputSpecs(); + + BOOST_REQUIRE_EQUAL(inputs.size(), 2); + BOOST_CHECK_EQUAL(inputs[0], (InputSpec{"asdf", {"TST", "AAAA"}})); + BOOST_CHECK_EQUAL(inputs[1], (InputSpec{"timer-stats", "DS", "TIMER-dispatcher", 0, Lifetime::Timer})); + } + + { + // policy3 includes 1 and 2, so we should have only one inputspec for data, one for timer + // same as before, but different order of registration + Dispatcher dispatcher("dispatcher", ""); + auto policy1 = std::make_unique<DataSamplingPolicy>("policy1"); + policy1->registerPath({"vcxz", "TST", "AAAA", 0}, {{"erwv"}, "DS", "AAAA"}); + + auto policy2 = std::make_unique<DataSamplingPolicy>("policy2"); + policy2->registerPath({"fdsa", "TST", "AAAA", 0}, {{"fdsf"}, "DS", "BBBB"}); + + auto policy3 = std::make_unique<DataSamplingPolicy>("policy3"); + policy3->registerPath({"asdf", {"TST", "AAAA"}}, {{"erwv"}, "DS", "CCCC"}); + + dispatcher.registerPolicy(std::move(policy3)); + dispatcher.registerPolicy(std::move(policy1)); + dispatcher.registerPolicy(std::move(policy2)); + + auto inputs = dispatcher.getInputSpecs(); + + BOOST_REQUIRE_EQUAL(inputs.size(), 2); + BOOST_CHECK_EQUAL(inputs[0], (InputSpec{"asdf", {"TST", "AAAA"}})); + BOOST_CHECK_EQUAL(inputs[1], (InputSpec{"timer-stats", "DS", "TIMER-dispatcher", 0, Lifetime::Timer})); + } + + { + // three different inputs + timer + Dispatcher dispatcher("dispatcher", ""); + auto policy1 = std::make_unique<DataSamplingPolicy>("policy1"); + policy1->registerPath({"vcxz", "TST", "AAAA", 0}, {{"erwv"}, "DS", "AAAA"}); + + auto policy2 = std::make_unique<DataSamplingPolicy>("policy2"); + policy2->registerPath({"sfsd", "TST", "BBBB", 0}, {{"fdsf"}, "DS", "BBBB"}); + + auto policy3 = std::make_unique<DataSamplingPolicy>("policy3"); + policy3->registerPath({"asdf", {"TST", "CCCC"}}, {{"erwv"}, "DS", "CCCC"}); + + dispatcher.registerPolicy(std::move(policy1)); + dispatcher.registerPolicy(std::move(policy2)); + dispatcher.registerPolicy(std::move(policy3)); + + auto inputs = dispatcher.getInputSpecs(); + + BOOST_REQUIRE_EQUAL(inputs.size(), 4); + BOOST_CHECK_EQUAL(inputs[0], (InputSpec{"vcxz", "TST", "AAAA"})); + BOOST_CHECK_EQUAL(inputs[1], (InputSpec{"sfsd", "TST", "BBBB"})); + BOOST_CHECK_EQUAL(inputs[2], (InputSpec{"asdf", {"TST", "CCCC"}})); + BOOST_CHECK_EQUAL(inputs[3], (InputSpec{"timer-stats", "DS", "TIMER-dispatcher", 0, Lifetime::Timer})); + } +} diff --git a/Framework/Core/test/test_DataSampling.json b/Utilities/DataSampling/test/test_DataSampling.json similarity index 100% rename from Framework/Core/test/test_DataSampling.json rename to Utilities/DataSampling/test/test_DataSampling.json diff --git a/Framework/Core/test/test_DataSamplingCondition.cxx b/Utilities/DataSampling/test/test_DataSamplingCondition.cxx similarity index 98% rename from Framework/Core/test/test_DataSamplingCondition.cxx rename to Utilities/DataSampling/test/test_DataSamplingCondition.cxx index e8fe6d7bb6f17..8b77e299df5e7 100644 --- a/Framework/Core/test/test_DataSamplingCondition.cxx +++ b/Utilities/DataSampling/test/test_DataSamplingCondition.cxx @@ -15,13 +15,14 @@ #include <boost/test/unit_test.hpp> #include <boost/property_tree/ptree.hpp> -#include "Framework/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingConditionFactory.h" #include "Framework/DataRef.h" #include "Framework/DataProcessingHeader.h" #include "Headers/DataHeader.h" #include "Headers/Stack.h" using namespace o2::framework; +using namespace o2::utilities; using namespace o2::header; BOOST_AUTO_TEST_CASE(DataSamplingConditionRandom) diff --git a/Utilities/DataSampling/test/test_DataSamplingEmpty.json b/Utilities/DataSampling/test/test_DataSamplingEmpty.json new file mode 100644 index 0000000000000..7a73a41bfdf76 --- /dev/null +++ b/Utilities/DataSampling/test/test_DataSamplingEmpty.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Framework/Core/test/test_DataSamplingHeader.cxx b/Utilities/DataSampling/test/test_DataSamplingHeader.cxx similarity index 97% rename from Framework/Core/test/test_DataSamplingHeader.cxx rename to Utilities/DataSampling/test/test_DataSamplingHeader.cxx index 1ff6d6fbff3af..44c26270572ba 100644 --- a/Framework/Core/test/test_DataSamplingHeader.cxx +++ b/Utilities/DataSampling/test/test_DataSamplingHeader.cxx @@ -13,11 +13,11 @@ #include <boost/test/unit_test.hpp> -#include "Framework/DataSamplingHeader.h" +#include "DataSampling/DataSamplingHeader.h" #include "Headers/Stack.h" #include "Headers/DataHeader.h" -using namespace o2::framework; +using namespace o2::utilities; using namespace o2::header; BOOST_AUTO_TEST_CASE(DataSamplingHeaderDefault) diff --git a/Utilities/DataSampling/test/test_DataSamplingPolicy.cxx b/Utilities/DataSampling/test/test_DataSamplingPolicy.cxx new file mode 100644 index 0000000000000..1723dc913fe28 --- /dev/null +++ b/Utilities/DataSampling/test/test_DataSamplingPolicy.cxx @@ -0,0 +1,140 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Framework DataSamplingPolicy +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> +#include <boost/property_tree/ptree.hpp> + +#include "DataSampling/DataSamplingConditionFactory.h" +#include "DataSampling/DataSamplingPolicy.h" +#include "Framework/DataRef.h" +#include "Framework/DataProcessingHeader.h" + +using namespace o2::framework; +using namespace o2::utilities; +using namespace o2::header; + +// an example of DataSamplingPolicy JSON object +// { +// "id" : "policy_example1", +// "active" : "false", +// "machines" : [ +// "aidlalala1", +// "aidlalala2" +// ] +// "query" : "c:TST/CHLEB/33;m:TST/MLEKO/33", +// "outputs" : "cc:TST/CHLEB_S/33;mm:TST/MLEKO_S/33", "": "this is optional, if not specified, default format is used", +// "samplingConditions" : [ +// { +// "condition" : "random", +// "fraction" : "0.1", +// "seed" : "2137" +// } +// ], +// "blocking" : "false" +// } + +BOOST_AUTO_TEST_CASE(DataSamplingPolicyFromConfiguration) +{ + using boost::property_tree::ptree; + + ptree config; + config.put("id", "my_policy"); + config.put("active", "true"); + config.put("query", "c:TST/CHLEB/33;m:TST/MLEKO/33"); + ptree samplingConditions; + ptree conditionRandom; + conditionRandom.put("condition", "random"); + conditionRandom.put("fraction", "0.1"); + conditionRandom.put("seed", "2137"); + samplingConditions.push_back(std::make_pair("", conditionRandom)); + config.add_child("samplingConditions", samplingConditions); + config.put("blocking", "false"); + + { + auto policy = std::move(DataSamplingPolicy::fromConfiguration(config)); + + BOOST_CHECK_EQUAL(policy.getName(), "my_policy"); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"DS", "my_policy0", 33})); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "MLEKO", 33})) == (Output{"DS", "my_policy1", 33})); + const auto& map = policy.getPathMap(); + BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "CHLEB", 33})).second == (OutputSpec{"DS", "my_policy0", 33})); + BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "MLEKO", 33})).second == (OutputSpec{"DS", "my_policy1", 33})); + BOOST_CHECK_EQUAL(map.size(), 2); + + BOOST_CHECK(policy.match(ConcreteDataMatcher{"TST", "CHLEB", 33})); + BOOST_CHECK(!policy.match(ConcreteDataMatcher{"TST", "SZYNKA", 33})); + + DataProcessingHeader dph{555, 0}; + o2::header::Stack headerStack{dph}; + DataRef dr{nullptr, reinterpret_cast<const char*>(headerStack.data()), nullptr}; + policy.decide(dr); // just make sure it does not crash + } + + config.put("id", "too-long-policy-name"); + { + auto policy = std::move(DataSamplingPolicy::fromConfiguration(config)); + BOOST_CHECK_EQUAL(policy.getName(), "too-long-policy-name"); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"DS", "too-long-polic0", 33})); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "MLEKO", 33})) == (Output{"DS", "too-long-polic1", 33})); + } + + // with custom outputs + config.put("outputs", "cc:TST/CHLEB_S/33;mm:TST/MLEKO_S/33"); + { + auto policy = std::move(DataSamplingPolicy::fromConfiguration(config)); + + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"TST", "CHLEB_S", 33})); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "MLEKO", 33})) == (Output{"TST", "MLEKO_S", 33})); + const auto& map = policy.getPathMap(); + BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "CHLEB", 33})).second == (OutputSpec{"TST", "CHLEB_S", 33})); + BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "MLEKO", 33})).second == (OutputSpec{"TST", "MLEKO_S", 33})); + BOOST_CHECK_EQUAL(map.size(), 2); + } + // with custom outputs which are wildcards + config.put("outputs", "cc:TST/CHLEB_S;mm:TST/MLEKO_S"); + { + auto policy = std::move(DataSamplingPolicy::fromConfiguration(config)); + + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"TST", "CHLEB_S", 33})); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "MLEKO", 33})) == (Output{"TST", "MLEKO_S", 33})); + const auto& map = policy.getPathMap(); + BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "CHLEB", 33})).second == (OutputSpec{{"TST", "CHLEB_S"}})); + BOOST_CHECK((*map.find(ConcreteDataMatcher{"TST", "MLEKO", 33})).second == (OutputSpec{{"TST", "MLEKO_S"}})); + BOOST_CHECK_EQUAL(map.size(), 2); + } +} + +BOOST_AUTO_TEST_CASE(DataSamplingPolicyFromMethods) +{ + DataSamplingPolicy policy("my_policy"); + auto conditionNConsecutive = DataSamplingConditionFactory::create("nConsecutive"); + BOOST_REQUIRE(conditionNConsecutive); + + boost::property_tree::ptree config; + config.put("samplesNumber", 3); + config.put("cycleSize", 10); + conditionNConsecutive->configure(config); + + policy.registerCondition(std::move(conditionNConsecutive)); + + policy.registerPath({"tststs", {"TST", "CHLEB"}}, {{"asdf"}, "AA", "BBBB"}); + BOOST_CHECK((policy.prepareOutput(ConcreteDataMatcher{"TST", "CHLEB", 33})) == (Output{"AA", "BBBB", 33})); +} + +BOOST_AUTO_TEST_CASE(DataSamplingPolicyStaticMethods) +{ + BOOST_CHECK(DataSamplingPolicy::createPolicyDataOrigin() == DataOrigin("DS")); + BOOST_CHECK(DataSamplingPolicy::createPolicyDataDescription("asdf", 0) == DataDescription("asdf0")); + BOOST_CHECK(DataSamplingPolicy::createPolicyDataDescription("asdfasdfasdfasdf", 0) == DataDescription("asdfasdfasdfas0")); + BOOST_CHECK(DataSamplingPolicy::createPolicyDataDescription("asdfasdfasdfasdf", 10) == DataDescription("asdfasdfasdfas10")); +} \ No newline at end of file diff --git a/Utilities/Mergers/CMakeLists.txt b/Utilities/Mergers/CMakeLists.txt index 071d7f8fe938f..d28670f7c72cf 100644 --- a/Utilities/Mergers/CMakeLists.txt +++ b/Utilities/Mergers/CMakeLists.txt @@ -62,6 +62,11 @@ o2_add_executable(benchmark-full-vs-diff COMPONENT_NAME mergers PUBLIC_LINK_LIBRARIES O2::Mergers benchmark::benchmark) +o2_add_executable(benchmark-types + SOURCES test/benchmark_Types.cxx + COMPONENT_NAME mergers + PUBLIC_LINK_LIBRARIES O2::Mergers benchmark::benchmark) + o2_add_test(InfrastructureBuilder SOURCES test/test_InfrastructureBuilder.cxx COMPONENT_NAME mergers diff --git a/Utilities/Mergers/src/FullHistoryMerger.cxx b/Utilities/Mergers/src/FullHistoryMerger.cxx index be2569545b8b4..9504e603e2246 100644 --- a/Utilities/Mergers/src/FullHistoryMerger.cxx +++ b/Utilities/Mergers/src/FullHistoryMerger.cxx @@ -20,7 +20,7 @@ #include "Headers/DataHeader.h" #include "Framework/InputRecordWalker.h" - +#include "Framework/Logger.h" #include <Monitoring/MonitoringFactory.h> using namespace o2::header; diff --git a/Utilities/Mergers/src/IntegratingMerger.cxx b/Utilities/Mergers/src/IntegratingMerger.cxx index 046b804496b3a..0607e0e78888b 100644 --- a/Utilities/Mergers/src/IntegratingMerger.cxx +++ b/Utilities/Mergers/src/IntegratingMerger.cxx @@ -21,6 +21,7 @@ #include <Monitoring/MonitoringFactory.h> #include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" //#include "Framework/DataRef.h" //using namespace o2; diff --git a/Utilities/Mergers/src/MergerAlgorithm.cxx b/Utilities/Mergers/src/MergerAlgorithm.cxx index 6b0d263bd46ac..7ababdb1571db 100644 --- a/Utilities/Mergers/src/MergerAlgorithm.cxx +++ b/Utilities/Mergers/src/MergerAlgorithm.cxx @@ -24,6 +24,7 @@ #include <TTree.h> #include <THnSparse.h> #include <TObjArray.h> +#include <TGraph.h> namespace o2::mergers::algorithm { @@ -81,6 +82,8 @@ void merge(TObject* const target, TObject* const other) errorCode = reinterpret_cast<THnBase*>(target)->Merge(&otherCollection); } else if (target->InheritsFrom(TTree::Class())) { errorCode = reinterpret_cast<TTree*>(target)->Merge(&otherCollection); + } else if (target->InheritsFrom(TGraph::Class())) { + errorCode = reinterpret_cast<TGraph*>(target)->Merge(&otherCollection); } else { throw std::runtime_error("Object with type '" + std::string(target->ClassName()) + "' is not one of the mergeable types."); } diff --git a/Utilities/Mergers/test/benchmark_FullVsDiff.cxx b/Utilities/Mergers/test/benchmark_FullVsDiff.cxx index 98c24265a984c..b20ea506682d8 100644 --- a/Utilities/Mergers/test/benchmark_FullVsDiff.cxx +++ b/Utilities/Mergers/test/benchmark_FullVsDiff.cxx @@ -20,7 +20,7 @@ #include <TF3.h> #include <TRandom.h> #include <TRandomGen.h> - +#include <chrono> #include <ctime> const size_t entriesInDiff = 50; @@ -45,15 +45,22 @@ static void BM_MergingTH1I(benchmark::State& state) } TH1I* m = new TH1I("merged", "merged", bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } for (auto _ : state) { - state.PauseTiming(); if (state.range(0) == FULL_OBJECTS) { m->Reset(); } - state.ResumeTiming(); + auto start = std::chrono::high_resolution_clock::now(); m->Merge(collection, "-NOCHECK"); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } delete collection; @@ -76,15 +83,22 @@ static void BM_MergingTH2I(benchmark::State& state) } TH2I* m = new TH2I("merged", "merged", bins, 0, 1000000, bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } for (auto _ : state) { - state.PauseTiming(); if (state.range(0) == FULL_OBJECTS) { m->Reset(); } - state.ResumeTiming(); + auto start = std::chrono::high_resolution_clock::now(); m->Merge(collection, "-NOCHECK"); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } delete collection; @@ -109,15 +123,22 @@ static void BM_MergingTH3I(benchmark::State& state) } TH3I* m = new TH3I("merged", "merged", bins, 0, 1000000, bins, 0, 1000000, bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } for (auto _ : state) { - state.PauseTiming(); if (state.range(0) == FULL_OBJECTS) { m->Reset(); } - state.ResumeTiming(); + auto start = std::chrono::high_resolution_clock::now(); m->Merge(collection, "-NOCHECK"); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } delete collection; @@ -142,7 +163,6 @@ static void BM_MergingTHnSparse(benchmark::State& state) auto* m = new THnSparseI("merged", "merged", dim, binsDims, mins, maxs); for (auto _ : state) { - state.PauseTiming(); TCollection* collection = new TObjArray(); collection->SetOwner(true); for (size_t i = 0; i < collectionSize; i++) { @@ -150,7 +170,7 @@ static void BM_MergingTHnSparse(benchmark::State& state) auto* h = new THnSparseI(("test" + std::to_string(i)).c_str(), "test", dim, binsDims, mins, maxs); for (size_t entry = 0; entry < entries; entry++) { gen.RndmArray(dim, randomArray); - for (double r : randomArray) { + for (double& r : randomArray) { r *= max; } h->Fill(randomArray); @@ -162,10 +182,12 @@ static void BM_MergingTHnSparse(benchmark::State& state) m->Reset(); } - state.ResumeTiming(); + auto start = std::chrono::high_resolution_clock::now(); m->Merge(collection); + auto end = std::chrono::high_resolution_clock::now(); - state.PauseTiming(); + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); delete collection; } @@ -198,7 +220,6 @@ static void BM_MergingTTree(benchmark::State& state) TTree* merged = createTree("merged"); for (auto _ : state) { - state.PauseTiming(); TCollection* collection = new TObjArray(); collection->SetOwner(true); for (size_t i = 0; i < collectionSize; i++) { @@ -216,25 +237,25 @@ static void BM_MergingTTree(benchmark::State& state) merged->Reset(); } - state.ResumeTiming(); - merged->Merge(collection); + auto start = std::chrono::high_resolution_clock::now(); + merged->Merge(collection, "-NOCHECK"); + auto end = std::chrono::high_resolution_clock::now(); - state.PauseTiming(); - - delete collection; + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } delete merged; } -BENCHMARK(BM_MergingTH1I)->Arg(DIFF_OBJECTS); -BENCHMARK(BM_MergingTH1I)->Arg(FULL_OBJECTS); -BENCHMARK(BM_MergingTH2I)->Arg(DIFF_OBJECTS); -BENCHMARK(BM_MergingTH2I)->Arg(FULL_OBJECTS); -BENCHMARK(BM_MergingTH3I)->Arg(DIFF_OBJECTS); -BENCHMARK(BM_MergingTH3I)->Arg(FULL_OBJECTS); -BENCHMARK(BM_MergingTHnSparse)->Arg(DIFF_OBJECTS); -BENCHMARK(BM_MergingTHnSparse)->Arg(FULL_OBJECTS); -BENCHMARK(BM_MergingTTree)->Arg(DIFF_OBJECTS); -BENCHMARK(BM_MergingTTree)->Arg(FULL_OBJECTS); +BENCHMARK(BM_MergingTH1I)->Arg(DIFF_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTH1I)->Arg(FULL_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTH2I)->Arg(DIFF_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTH2I)->Arg(FULL_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTH3I)->Arg(DIFF_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTH3I)->Arg(FULL_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTHnSparse)->Arg(DIFF_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTHnSparse)->Arg(FULL_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTTree)->Arg(DIFF_OBJECTS)->UseManualTime(); +BENCHMARK(BM_MergingTTree)->Arg(FULL_OBJECTS)->UseManualTime(); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/Utilities/Mergers/test/benchmark_MergingCollections.cxx b/Utilities/Mergers/test/benchmark_MergingCollections.cxx index 0a5c4585e8fbe..701c4d4d38175 100644 --- a/Utilities/Mergers/test/benchmark_MergingCollections.cxx +++ b/Utilities/Mergers/test/benchmark_MergingCollections.cxx @@ -22,91 +22,143 @@ #include <TRandomGen.h> #include <boost/histogram.hpp> - +#include <chrono> namespace bh = boost::histogram; #include <ctime> +#define MAX_SIZE_COLLECTION (1 << 10) +#define BENCHMARK_RANGE_COLLECTIONS Arg(1)->Arg(1 << 2)->Arg(1 << 4)->Arg(1 << 6)->Arg(1 << 8)->Arg(1 << 10) + static void BM_mergingCollectionsTH1I(benchmark::State& state) { - size_t collectionSize = state.range(0); - size_t bins = 62500; // makes 250kB - - TCollection* collection = new TObjArray(); - collection->SetOwner(true); - TF1* uni = new TF1("uni", "1", 0, 1000000); - for (size_t i = 0; i < collectionSize; i++) { - TH1I* h = new TH1I(("test" + std::to_string(i)).c_str(), "test", bins, 0, 1000000); - h->FillRandom("uni", 50000); - collection->Add(h); - } - - TH1I* m = new TH1I("merged", "merged", bins, 0, 1000000); + const size_t collectionSize = state.range(0); + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; + const size_t bins = 62500; // makes 250kB for (auto _ : state) { - m->Merge(collection, "-NOCHECK"); - } + std::vector<std::unique_ptr<TCollection>> collections; + for (size_t ci = 0; ci < numberOfCollections; ci++) { + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + TF1* uni = new TF1("uni", "1", 0, 1000000); + for (size_t i = 0; i < collectionSize; i++) { + TH1I* h = new TH1I(("test" + std::to_string(ci) + "-" + std::to_string(i)).c_str(), "test", bins, 0, 1000000); + h->FillRandom("uni", 50000); + collection->Add(h); + } + collections.push_back(std::move(collection)); + delete uni; + } + auto m = std::make_unique<TH1I>("merged", "merged", bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& collection : collections) { + m->Merge(collection.get(), "-NOCHECK"); + } + auto end = std::chrono::high_resolution_clock::now(); - delete collection; - delete m; - delete uni; + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); + } } static void BM_mergingCollectionsTH2I(benchmark::State& state) { + const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; + const size_t bins = 250; // 250 bins * 250 bins * 4B makes 250kB - size_t collectionSize = state.range(0); - size_t bins = 250; // 250 bins * 250 bins * 4B makes 250kB - - TCollection* collection = new TObjArray(); - collection->SetOwner(true); TF2* uni = new TF2("uni", "1", 0, 1000000, 0, 1000000); - for (size_t i = 0; i < collectionSize; i++) { - TH2I* h = new TH2I(("test" + std::to_string(i)).c_str(), "test", bins, 0, 1000000, bins, 0, 1000000); - h->FillRandom("uni", 50000); - collection->Add(h); - } - - TH2I* m = new TH2I("merged", "merged", bins, 0, 1000000, bins, 0, 1000000); for (auto _ : state) { - m->Merge(collection, "-NOCHECK"); - } - delete collection; - delete m; + std::vector<std::unique_ptr<TCollection>> collections; + + for (size_t ci = 0; ci < numberOfCollections; ci++) { + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + for (size_t i = 0; i < collectionSize; i++) { + TH2I* h = new TH2I(("test" + std::to_string(ci) + "-" + std::to_string(i)).c_str(), "test", + bins, 0, 1000000, + bins, 0, 1000000); + h->FillRandom("uni", 50000); + collection->Add(h); + } + collections.push_back(std::move(collection)); + } + auto m = std::make_unique<TH2I>("merged", "merged", bins, 0, 1000000, bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } + + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& collection : collections) { + m->Merge(collection.get(), "-NOCHECK"); + } + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); + } + delete uni; } static void BM_mergingCollectionsTH3I(benchmark::State& state) { - size_t collectionSize = state.range(0); - size_t bins = 40; // 40 bins * 40 bins * 40 bins * 4B makes 256kB + const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; + const size_t bins = 40; // 40 bins * 40 bins * 40 bins * 4B makes 256kB - TCollection* collection = new TObjArray(); - collection->SetOwner(true); TF3* uni = new TF3("uni", "1", 0, 1000000, 0, 1000000, 0, 1000000); - for (size_t i = 0; i < collectionSize; i++) { - TH3I* h = new TH3I(("test" + std::to_string(i)).c_str(), "test", - bins, 0, 1000000, - bins, 0, 1000000, - bins, 0, 1000000); - h->FillRandom("uni", 50000); - collection->Add(h); - } - - TH3I* m = new TH3I("merged", "merged", bins, 0, 1000000, bins, 0, 1000000, bins, 0, 1000000); for (auto _ : state) { - m->Merge(collection, "-NOCHECK"); + + std::vector<std::unique_ptr<TCollection>> collections; + + for (size_t ci = 0; ci < numberOfCollections; ci++) { + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + for (size_t i = 0; i < collectionSize; i++) { + TH3I* h = new TH3I(("test" + std::to_string(ci) + "-" + std::to_string(i)).c_str(), "test", + bins, 0, 1000000, + bins, 0, 1000000, + bins, 0, 1000000); + h->FillRandom("uni", 50000); + collection->Add(h); + } + collections.push_back(std::move(collection)); + } + auto m = std::make_unique<TH3I>("merged", "merged", bins, 0, 1000000, bins, 0, 1000000, bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } + + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& collection : collections) { + m->Merge(collection.get(), "-NOCHECK"); + } + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } - delete collection; - delete m; + delete uni; } static void BM_mergingCollectionsTHNSparse(benchmark::State& state) { - size_t collectionSize = state.range(0); + const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; const Double_t min = 0.0; const Double_t max = 1000000.0; @@ -122,36 +174,42 @@ static void BM_mergingCollectionsTHNSparse(benchmark::State& state) for (auto _ : state) { - state.PauseTiming(); - TCollection* collection = new TObjArray(); - collection->SetOwner(true); - for (size_t i = 0; i < collectionSize; i++) { - - auto* h = new THnSparseI(("test" + std::to_string(i)).c_str(), "test", dim, binsDims, mins, maxs); - for (size_t entry = 0; entry < 50000; entry++) { - gen.RndmArray(dim, randomArray); - for (double& r : randomArray) { - r *= max; + std::vector<std::unique_ptr<TCollection>> collections; + + for (size_t ci = 0; ci < numberOfCollections; ci++) { + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + for (size_t i = 0; i < collectionSize; i++) { + auto* h = new THnSparseI(("test" + std::to_string(ci) + "-" + std::to_string(i)).c_str(), "test", dim, binsDims, mins, maxs); + for (size_t entry = 0; entry < 50000; entry++) { + gen.RndmArray(dim, randomArray); + for (double& r : randomArray) { + r *= max; + } + h->Fill(randomArray); } - h->Fill(randomArray); + collection->Add(h); } - collection->Add(h); + collections.push_back(std::move(collection)); } - auto* m = new THnSparseI("merged", "merged", dim, binsDims, mins, maxs); + auto m = std::make_unique<THnSparseI>("merged", "merged", dim, binsDims, mins, maxs); - state.ResumeTiming(); - m->Merge(collection); - - state.PauseTiming(); + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& collection : collections) { + m->Merge(collection.get()); + } + auto end = std::chrono::high_resolution_clock::now(); - delete collection; - delete m; + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } } static void BM_mergingCollectionsTTree(benchmark::State& state) { - size_t collectionSize = state.range(0); + const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; struct format1 { Int_t a; @@ -174,37 +232,47 @@ static void BM_mergingCollectionsTTree(benchmark::State& state) Double_t randomArray[5]; for (auto _ : state) { - state.PauseTiming(); - TCollection* collection = new TObjArray(); - collection->SetOwner(true); - for (size_t i = 0; i < collectionSize; i++) { - TTree* t = createTree(std::to_string(i)); - for (size_t entry = 0; entry < 6250; entry++) { - gen.RndmArray(5, randomArray); - branch1 = {static_cast<Int_t>(randomArray[0]), static_cast<Long64_t>(randomArray[1]), static_cast<Float_t>(randomArray[2]), randomArray[3]}; - branch2 = randomArray[4]; - t->Fill(); + std::vector<std::unique_ptr<TCollection>> collections; + + for (size_t ci = 0; ci < numberOfCollections; ci++) { + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + for (size_t i = 0; i < collectionSize; i++) { + TTree* t = createTree(std::to_string(ci) + "-" + std::to_string(i)); + for (size_t entry = 0; entry < 6250; entry++) { + gen.RndmArray(5, randomArray); + branch1 = {static_cast<Int_t>(randomArray[0]), static_cast<Long64_t>(randomArray[1]), static_cast<Float_t>(randomArray[2]), randomArray[3]}; + branch2 = randomArray[4]; + t->Fill(); + } + collection->Add(t); } - collection->Add(t); + collections.emplace_back(std::move(collection)); } TTree* merged = createTree("merged"); - state.ResumeTiming(); - merged->Merge(collection); + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& collection : collections) { + merged->Merge(collection.get()); + } + auto end = std::chrono::high_resolution_clock::now(); - state.PauseTiming(); + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); - delete collection; delete merged; } } static void BM_mergingPODCollections(benchmark::State& state) { - size_t collectionSize = state.range(0); - size_t bins = 62500; // makes 250kB + const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; + const size_t bins = 62500; // makes 250kB - std::vector<std::vector<float>*> collection; + using PODHistoType = std::vector<float>; + using PODHistoTypePtr = std::unique_ptr<PODHistoType>; TF1* uni = new TF1("uni", "1", 0, 1000000); const size_t randoms = 50000; @@ -212,37 +280,48 @@ static void BM_mergingPODCollections(benchmark::State& state) gen.SetSeed(std::random_device()()); Double_t randomArray[randoms]; - for (size_t i = 0; i < collectionSize; i++) { - auto* v = new std::vector<float>(bins, 0); - gen.RndmArray(randoms, randomArray); - for (double r : randomArray) { - size_t idx = r * bins; - if (idx != bins) { - (*v)[idx] += 1; + for (auto _ : state) { + std::vector<std::unique_ptr<std::vector<PODHistoTypePtr>>> collections; + + for (size_t ci = 0; ci < numberOfCollections; ci++) { + auto collection = std::make_unique<std::vector<PODHistoTypePtr>>(); + for (size_t i = 0; i < collectionSize; i++) { + auto h = PODHistoTypePtr(new PODHistoType(bins, 0)); + gen.RndmArray(randoms, randomArray); + for (double r : randomArray) { + size_t idx = r * bins; + if (idx != bins) { + (*h)[idx] += 1; + } + } + collection->emplace_back(std::move(h)); } + collections.emplace_back(std::move(collection)); } - collection.push_back(v); - } - - auto* m = new std::vector<float>(bins, 0); - - auto merge = [&](size_t i) { - auto* v = collection[i]; - for (size_t b = 0; b < bins; b++) { - (*m)[b] += (*v)[b]; + auto m = PODHistoTypePtr(new PODHistoType(bins, 0)); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + (*m)[i] = 1; } - }; - for (auto _ : state) { - for (size_t i = 0; i < collectionSize; i++) { - merge(i); + auto merge = [&](const std::vector<PODHistoTypePtr>& collection, size_t i) { + auto h = collection[i].get(); + for (size_t b = 0; b < bins; b++) { + (*m)[b] += (*h)[b]; + } + }; + + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& collection : collections) { + for (size_t i = 0; i < collectionSize; i++) { + merge(*collection, i); + } } - } + auto end = std::chrono::high_resolution_clock::now(); - for (size_t i = 0; i < collectionSize; i++) { - delete collection[i]; + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } - delete m; delete uni; } @@ -251,11 +330,15 @@ static void BM_mergingBoostRegular1DCollections(benchmark::State& state) const double min = 0.0; const double max = 1000000.0; const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; const size_t bins = 62500; // makes 250kB auto merged = bh::make_histogram(bh::axis::regular<>(bins, min, max, "x")); + merged += merged; // avoid memory overcommitment by doing something with data. + using HistoType = decltype(merged); + using CollectionHistoType = std::vector<HistoType>; - std::vector<decltype(merged)> collection; TF1* uni = new TF1("uni", "1", 0, 1000000); const size_t randoms = 50000; @@ -263,26 +346,40 @@ static void BM_mergingBoostRegular1DCollections(benchmark::State& state) gen.SetSeed(std::random_device()()); Double_t randomArray[randoms]; - for (size_t i = 0; i < collectionSize; i++) { - collection.emplace_back(std::move(bh::make_histogram(bh::axis::regular<>(bins, min, max, "x")))); + for (auto _ : state) { - auto& h = collection.back(); - static_assert(std::is_reference<decltype(h)>::value); + std::vector<CollectionHistoType> collections; - gen.RndmArray(randoms, randomArray); - for (double r : randomArray) { - h(r * max); + for (size_t ci = 0; ci < numberOfCollections; ci++) { + CollectionHistoType collection; + for (size_t i = 0; i < collectionSize; i++) { + collection.emplace_back(std::move(bh::make_histogram(bh::axis::regular<>(bins, min, max, "x")))); + + auto& h = collection.back(); + static_assert(std::is_reference<decltype(h)>::value); + + gen.RndmArray(randoms, randomArray); + for (double r : randomArray) { + h(r * max); + } + } + collections.emplace_back(std::move(collection)); } - } - auto merge = [&](size_t i) { - merged += collection[i]; - }; + auto merge = [&](const CollectionHistoType& collection) { + for (size_t i = 0; i < collectionSize; i++) { + merged += collection[i]; + } + }; - for (auto _ : state) { - for (size_t i = 0; i < collectionSize; i++) { - merge(i); + auto start = std::chrono::high_resolution_clock::now(); + for (size_t ci = 0; ci < numberOfCollections; ci++) { + merge(collections[ci]); } + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } delete uni; @@ -293,11 +390,15 @@ static void BM_mergingBoostRegular2DCollections(benchmark::State& state) const double min = 0.0; const double max = 1000000.0; const size_t collectionSize = state.range(0); + // We should always merge the same amount of objects (histograms) in one benchmark cycle. + const size_t numberOfCollections = MAX_SIZE_COLLECTION / collectionSize; const size_t bins = 250; // 250 bins * 250 bins * 4B makes 250kB auto merged = bh::make_histogram(bh::axis::regular<>(bins, min, max, "x"), bh::axis::regular<>(bins, min, max, "y")); + merged += merged; // avoid memory overcommitment by doing something with data. + using HistoType = decltype(merged); + using CollectionHistoType = std::vector<HistoType>; - std::vector<decltype(merged)> collection; TF1* uni = new TF1("uni", "1", 0, 1000000); const size_t randoms = 50000; @@ -306,52 +407,67 @@ static void BM_mergingBoostRegular2DCollections(benchmark::State& state) Double_t randomArrayX[randoms]; Double_t randomArrayY[randoms]; - for (size_t i = 0; i < collectionSize; i++) { - collection.emplace_back(std::move(bh::make_histogram(bh::axis::regular<>(bins, min, max, "x"), bh::axis::regular<>(bins, min, max, "y")))); + for (auto _ : state) { - auto& h = collection.back(); - static_assert(std::is_reference<decltype(h)>::value); + state.PauseTiming(); + std::vector<CollectionHistoType> collections; - gen.RndmArray(randoms, randomArrayX); - gen.RndmArray(randoms, randomArrayY); - for (size_t r = 0; r < randoms; r++) { - h(randomArrayX[r] * max, randomArrayY[r] * max); + for (size_t ci = 0; ci < numberOfCollections; ci++) { + CollectionHistoType collection; + for (size_t i = 0; i < collectionSize; i++) { + collection.emplace_back(std::move(bh::make_histogram(bh::axis::regular<>(bins, min, max, "x"), bh::axis::regular<>(bins, min, max, "y")))); + + auto& h = collection.back(); + static_assert(std::is_reference<decltype(h)>::value); + + gen.RndmArray(randoms, randomArrayX); + gen.RndmArray(randoms, randomArrayY); + for (size_t r = 0; r < randoms; r++) { + h(randomArrayX[r] * max, randomArrayY[r] * max); + } + } + collections.emplace_back(std::move(collection)); } - } - auto merge = [&](size_t i) { - merged += collection[i]; - }; + auto merge = [&](const CollectionHistoType& collection) { + for (size_t i = 0; i < collectionSize; i++) { + merged += collection[i]; + } + }; - for (auto _ : state) { - for (size_t i = 0; i < collectionSize; i++) { - merge(i); + auto start = std::chrono::high_resolution_clock::now(); + for (size_t ci = 0; ci < numberOfCollections; ci++) { + merge(collections[ci]); } + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + state.SetIterationTime(elapsed_seconds.count()); } delete uni; } // one by one comparison -BENCHMARK(BM_mergingCollectionsTH1I)->Arg(1); -BENCHMARK(BM_mergingCollectionsTH2I)->Arg(1); -BENCHMARK(BM_mergingCollectionsTH3I)->Arg(1); -BENCHMARK(BM_mergingCollectionsTHNSparse)->Arg(1); -BENCHMARK(BM_mergingPODCollections)->Arg(1); -BENCHMARK(BM_mergingBoostRegular1DCollections)->Arg(1); -BENCHMARK(BM_mergingBoostRegular2DCollections)->Arg(1); -BENCHMARK(BM_mergingCollectionsTTree)->Arg(1); +BENCHMARK(BM_mergingCollectionsTH1I)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTH1I)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTH2I)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTH3I)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTHNSparse)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingPODCollections)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingBoostRegular1DCollections)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingBoostRegular2DCollections)->Arg(1)->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTTree)->Arg(1)->UseManualTime(); // collections -#define BENCHMARK_RANGE_COLLECTIONS Arg(1)->Arg(1 << 2)->Arg(1 << 4)->Arg(1 << 6)->Arg(1 << 8)->Arg(1 << 10) -BENCHMARK(BM_mergingCollectionsTH1I)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingCollectionsTH2I)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingCollectionsTH3I)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingCollectionsTHNSparse)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingPODCollections)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingBoostRegular1DCollections)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingBoostRegular2DCollections)->BENCHMARK_RANGE_COLLECTIONS; -BENCHMARK(BM_mergingCollectionsTTree)->BENCHMARK_RANGE_COLLECTIONS; +BENCHMARK(BM_mergingCollectionsTH1I)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTH2I)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTH3I)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTHNSparse)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingPODCollections)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingBoostRegular1DCollections)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingBoostRegular2DCollections)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); +BENCHMARK(BM_mergingCollectionsTTree)->BENCHMARK_RANGE_COLLECTIONS->UseManualTime(); BENCHMARK_MAIN(); diff --git a/Utilities/Mergers/test/benchmark_Types.cxx b/Utilities/Mergers/test/benchmark_Types.cxx new file mode 100644 index 0000000000000..e1ec7fba59af1 --- /dev/null +++ b/Utilities/Mergers/test/benchmark_Types.cxx @@ -0,0 +1,740 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <TObjArray.h> +#include <TH1.h> +#include <TH2.h> +#include <TH3.h> +#include <TTree.h> +#include <THnSparse.h> +#include <TF1.h> +#include <TF2.h> +#include <TF3.h> +#include <TRandom.h> +#include <TRandomGen.h> + +#include <chrono> +#include <ctime> +#include <TMessage.h> + +#include "Framework/TMessageSerializer.h" + +#include <type_traits> + +#include <iostream> +#include <fstream> + +#include <boost/archive/binary_iarchive.hpp> +#include <boost/archive/binary_oarchive.hpp> +#include <boost/histogram.hpp> +#include <boost/histogram/serialization.hpp> +#include <sstream> + +namespace bh = boost::histogram; + +enum class Measurement { + Size, + SizeAfterSerialisation, + Deserialisation, + Merging, + Serialisation +}; + +struct Results { + size_t sizeBytes = 0; + size_t sizeSerialisedBytes = 0; + double deserialisationSeconds = 0; + double mergingSeconds = 0; + double serialisationSeconds = 0; +}; + +struct Parameters { + constexpr static Parameters forHistograms(size_t objectSize, size_t entries) + { + return {objectSize, 0, 0, entries}; + } + constexpr static Parameters forSparse(size_t bins, size_t dimensions, size_t entries) + { + return {0, bins, dimensions, entries}; + } + constexpr static Parameters forTrees(size_t branches, size_t branchSize, size_t entries) + { + return {0, 0, 0, entries, branches, branchSize}; + } + size_t objectSize = 0; + size_t bins = 0; + size_t dimensions = 0; + size_t entries = 0; + size_t branches = 0; + size_t branchSize = 0; +}; + +auto measure = [](Measurement m, auto* o, auto* i) -> double { + switch (m) { + case Measurement::Size: { + const double scale = 1.0; //000000000.0; + if constexpr (std::is_base_of<TObject, typename std::remove_pointer<decltype(o)>::type>::value) { + if (o->InheritsFrom(TH1::Class())) { + // this includes TH1, TH2, TH3 + // i don't see an easy way to find out the size of a cell, i assume that they are Int + return reinterpret_cast<TH1*>(o)->GetNcells() * sizeof(Int_t) / scale; + } else if (o->InheritsFrom(THnSparse::Class())) { + auto* sparse = reinterpret_cast<THnSparse*>(o); + // this is has to be multiplied with bin (entry) size, but we cannot get it from th einterface. + return sparse->GetNChunks() * sparse->GetChunkSize() / scale; + } else if (o->InheritsFrom(THnBase::Class())) { + // this includes THn and THnSparse + return reinterpret_cast<THnBase*>(o)->GetNbins() * sizeof(Int_t) / scale; + } else if (o->InheritsFrom(TTree::Class())) { + size_t totalSize = 0; + auto tree = reinterpret_cast<TTree*>(o); + auto branchList = tree->GetListOfBranches(); + for (const auto* branch : *branchList) { + totalSize += dynamic_cast<const TBranch*>(branch)->GetTotalSize(); + } + return totalSize / scale; + } else { + throw std::runtime_error("Object with type '" + std::string(o->ClassName()) + "' is not one of the mergeable types."); + } + } else { + // boost + return o->size() * sizeof(int); + } + } + case Measurement::Merging: { + auto end = std::chrono::high_resolution_clock::now(); + auto start = std::chrono::high_resolution_clock::now(); + if constexpr (std::is_base_of<TObject, typename std::remove_pointer<decltype(o)>::type>::value) { + if (o->InheritsFrom(TH1::Class())) { + // this includes TH1, TH2, TH3 + start = std::chrono::high_resolution_clock::now(); + reinterpret_cast<TH1*>(o)->Merge(i, "-NOCHECK"); + } else if (o->InheritsFrom(THnBase::Class())) { + // this includes THn and THnSparse + start = std::chrono::high_resolution_clock::now(); + reinterpret_cast<THnBase*>(o)->Merge(i); + } else if (o->InheritsFrom(TTree::Class())) { + start = std::chrono::high_resolution_clock::now(); + reinterpret_cast<TTree*>(o)->Merge(i); + } else { + throw std::runtime_error("Object with type '" + std::string(o->ClassName()) + "' is not one of the mergeable types."); + } + end = std::chrono::high_resolution_clock::now(); + } else { + // boost + *o += *i; + end = std::chrono::high_resolution_clock::now(); + (void)*o; + } + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + return elapsed_seconds.count(); + } + case Measurement::Serialisation: { + auto end = std::chrono::high_resolution_clock::now(); + auto start = std::chrono::high_resolution_clock::now(); + if constexpr (std::is_base_of<TObject, typename std::remove_pointer<decltype(o)>::type>::value) { + TMessage* tm = new TMessage(kMESS_OBJECT); + tm->WriteObject(o); + end = std::chrono::high_resolution_clock::now(); + (void)*tm; + delete tm; + } else { + // boost + std::ostringstream os; + std::string buf; + boost::archive::binary_oarchive oa(os); + oa << *o; + end = std::chrono::high_resolution_clock::now(); + (void)os; // hopefully this will prevent from optimising this code out. + } + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + return elapsed_seconds.count(); + } + case Measurement::Deserialisation: { + auto start = std::chrono::high_resolution_clock::now(); + auto end = std::chrono::high_resolution_clock::now(); + if constexpr (std::is_base_of<TObject, typename std::remove_pointer<decltype(o)>::type>::value) { + TMessage* tm = new TMessage(kMESS_OBJECT); + tm->WriteObject(o); + start = std::chrono::high_resolution_clock::now(); + + o2::framework::FairTMessage ftm(const_cast<char*>(tm->Buffer()), tm->BufferSize()); + auto* storedClass = ftm.GetClass(); + if (storedClass == nullptr) { + throw std::runtime_error("Unknown stored class"); + } + auto* tObjectClass = TClass::GetClass(typeid(TObject)); + + if (!storedClass->InheritsFrom(tObjectClass)) { + throw std::runtime_error("Class '" + std::string(storedClass->GetName()) + "'does not inherit from TObject"); + } + + auto* object = ftm.ReadObjectAny(storedClass); + if (object == nullptr) { + throw std::runtime_error("Failed to read object with name '" + std::string(storedClass->GetName()) + "' from message using ROOT serialization."); + } + + auto tobject = static_cast<TObject*>(object); + end = std::chrono::high_resolution_clock::now(); + (void)*tobject; + delete tm; + delete tobject; + } else { + std::ostringstream os; + std::string buf; + boost::archive::binary_oarchive oa(os); + oa << *o; + buf = os.str(); + + start = std::chrono::high_resolution_clock::now(); + auto deserialisedHistogram = typename std::remove_pointer<decltype(o)>::type(); + std::istringstream is(buf); + boost::archive::binary_iarchive ia(is); + ia >> deserialisedHistogram; + end = std::chrono::high_resolution_clock::now(); + assert(deserialisedHistogram == *o); + } + auto elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); + return elapsed_seconds.count(); + } + case Measurement::SizeAfterSerialisation: { + const double scale = 1.0; //1000000000.0; + if constexpr (std::is_base_of<TObject, typename std::remove_pointer<decltype(o)>::type>::value) { + TMessage* tm = new TMessage(kMESS_OBJECT); + tm->WriteObject(o); + auto serialisedSize = tm->BufferSize(); + delete tm; + return serialisedSize / scale; + } else { + std::ostringstream os; + std::string buf; + boost::archive::binary_oarchive oa(os); + oa << *i; + buf = os.str(); + return buf.size() / scale; + } + } + } + throw; +}; + +static std::vector<Results> BM_TH1I(size_t repetitions, const Parameters p) +{ + const size_t objSize = p.objectSize; + const size_t entries = p.entries; + const size_t bins = objSize / sizeof(Int_t); + + auto m = std::make_unique<TH1I>("merged", "merged", bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins; i++) { + m->SetBinContent(i, 1); + } + + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + auto uni = std::make_unique<TF1>("uni", "1", 0, 1000000); + TH1I* h = new TH1I("test", "test", bins, 0, 1000000); + collection->Add(h); + + std::vector<Results> allResults; + for (size_t r = 0; r < repetitions; r++) { + h->Reset(); + h->FillRandom("uni", entries); + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, m.get(), collection.get()); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, m.get(), collection.get()); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, m.get(), collection.get()); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, m.get(), collection.get()); + iterationResults.mergingSeconds = measure(Measurement::Merging, m.get(), collection.get()); + allResults.push_back(iterationResults); + } + return allResults; +} + +static std::vector<Results> BM_TH2I(size_t repetitions, const Parameters p) +{ + const size_t objSize = p.objectSize; + const size_t entries = p.entries; + const size_t bins = std::sqrt(objSize / sizeof(Int_t)); + + auto m = std::make_unique<TH2I>("merged", "merged", bins, 0, 1000000, bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins * bins; i++) { + m->SetBinContent(i, 1); + } + + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + auto uni = std::make_unique<TF2>("uni", "1", 0, 1000000, 0, 1000000); + auto* h = new TH2I("test", "test", bins, 0, 1000000, bins, 0, 1000000); + collection->Add(h); + + std::vector<Results> allResults; + for (size_t r = 0; r < repetitions; r++) { + h->Reset(); + h->FillRandom("uni", entries); + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, m.get(), collection.get()); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, m.get(), collection.get()); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, m.get(), collection.get()); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, m.get(), collection.get()); + iterationResults.mergingSeconds = measure(Measurement::Merging, m.get(), collection.get()); + allResults.push_back(iterationResults); + } + return allResults; +} + +static std::vector<Results> BM_TH3I(size_t repetitions, const Parameters p) +{ + const size_t objSize = p.objectSize; + const size_t entries = p.entries; + const size_t bins = std::pow(objSize / sizeof(Int_t), 1 / 3.0); + + auto m = std::make_unique<TH3I>("merged", "merged", bins, 0, 1000000, bins, 0, 1000000, bins, 0, 1000000); + // avoid memory overcommitment by doing something with data. + for (size_t i = 0; i < bins * bins * bins; i++) { + m->SetBinContent(i, 1); + } + + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + auto uni = std::make_unique<TF3>("uni", "1", 0, 1000000, 0, 1000000, 0, 1000000); + auto* h = new TH3I("test", "test", bins, 0, 1000000, bins, 0, 1000000, bins, 0, 1000000); + collection->Add(h); + + std::vector<Results> allResults; + for (size_t r = 0; r < repetitions; r++) { + h->Reset(); + h->FillRandom("uni", entries); + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, m.get(), collection.get()); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, m.get(), collection.get()); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, m.get(), collection.get()); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, m.get(), collection.get()); + iterationResults.mergingSeconds = measure(Measurement::Merging, m.get(), collection.get()); + allResults.push_back(iterationResults); + } + return allResults; +} + +template <typename storageT> +static std::vector<Results> BM_BoostRegular1D(size_t repetitions, const Parameters p) +{ + const size_t entries = p.entries; + const size_t bins = p.objectSize / sizeof(int32_t); + const double min = 0.0; + const double max = 1000000.0; + + auto merged = bh::make_histogram_with(storageT(), bh::axis::regular<>(bins, min, max, "x")); + merged += merged; // avoid memory overcommitment by doing something with data. + using HistoType = decltype(merged); + + TRandomMixMax gen; + gen.SetSeed(std::random_device()()); + Double_t randomArray[entries]; + + std::vector<Results> allResults; + for (size_t r = 0; r < repetitions; r++) { + auto h = bh::make_histogram_with(storageT(), bh::axis::regular<>(bins, min, max, "x")); + gen.RndmArray(entries, randomArray); + for (double rnd : randomArray) { + h(rnd * max); + } + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, &merged, &h); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, &merged, &h); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, &merged, &h); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, &merged, &h); + iterationResults.mergingSeconds = measure(Measurement::Merging, &merged, &h); + allResults.push_back(iterationResults); + } + return allResults; +} + +template <typename storageT> +static std::vector<Results> BM_BoostRegular2D(size_t repetitions, const Parameters p) +{ + const size_t entries = p.entries; + const size_t bins = std::sqrt(p.objectSize / sizeof(int32_t)); + const double min = 0.0; + const double max = 1000000.0; + + auto merged = bh::make_histogram_with(storageT(), bh::axis::regular<>(bins, min, max, "x"), bh::axis::regular<>(bins, min, max, "y")); + merged += merged; // avoid memory overcommitment by doing something with data. + + TRandomMixMax gen; + gen.SetSeed(std::random_device()()); + Double_t randomArrayX[entries]; + Double_t randomArrayY[entries]; + + std::vector<Results> allResults; + for (size_t r = 0; r < repetitions; r++) { + auto h = bh::make_histogram_with(storageT(), bh::axis::regular<>(bins, min, max, "x"), bh::axis::regular<>(bins, min, max, "y")); + gen.RndmArray(entries, randomArrayX); + gen.RndmArray(entries, randomArrayY); + for (size_t rnd = 0; rnd < entries; rnd++) { + h(randomArrayX[rnd] * max, randomArrayY[rnd] * max); + } + + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, &merged, &h); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, &merged, &h); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, &merged, &h); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, &merged, &h); + iterationResults.mergingSeconds = measure(Measurement::Merging, &merged, &h); + allResults.push_back(iterationResults); + } + return allResults; +} + +static std::vector<Results> BM_THNSparseI(size_t repetitions, const Parameters p) +{ + const size_t bins = p.bins; + const size_t dim = p.dimensions; + const size_t entries = p.entries; + + const Double_t min = 0.0; + const Double_t max = 1000000.0; + const std::vector<Int_t> binsDims(dim, bins); + const std::vector<Double_t> mins(dim, min); + const std::vector<Double_t> maxs(dim, max); + + TRandomMixMax gen; + gen.SetSeed(std::random_device()()); + Double_t randomArray[dim]; + + std::vector<Results> allResults; + for (size_t rep = 0; rep < repetitions; rep++) { + // histograms have to be created in each loop repetition, otherwise i get strange segfaults with large number of entries. + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + auto* h = new THnSparseI("test", "test", dim, binsDims.data(), mins.data(), maxs.data()); + collection->Add(h); + + auto m = std::make_unique<THnSparseI>("merged", "merged", dim, binsDims.data(), mins.data(), maxs.data()); + + for (size_t entry = 0; entry < entries; entry++) { + gen.RndmArray(dim, randomArray); + for (double& r : randomArray) { + r *= max; + } + h->Fill(randomArray); + } + + for (size_t entry = 0; entry < entries; entry++) { + gen.RndmArray(dim, randomArray); + for (double& r : randomArray) { + r *= max; + } + m->Fill(randomArray); + } + + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, m.get(), collection.get()); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, m.get(), collection.get()); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, m.get(), collection.get()); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, m.get(), collection.get()); + iterationResults.mergingSeconds = measure(Measurement::Merging, m.get(), collection.get()); + allResults.push_back(iterationResults); + } + return allResults; +} + +static std::vector<Results> BM_TTree(size_t repetitions, const Parameters p) +{ + const size_t branchSize = p.branchSize; + const size_t branches = p.branches; + const size_t entries = p.entries; + + using branch_t = std::vector<uint64_t>; + std::vector<branch_t> branchCollection; + for (size_t i = 0; i < branches; i++) { + branchCollection.emplace_back(branchSize, 0); + } + auto createTree = [&](std::string name) -> TTree* { + TTree* tree = new TTree(); + for (size_t i = 0; i < branchCollection.size(); i++) { + tree->Branch(("b" + std::to_string(i)).c_str(), + &branchCollection[i], + ("array" + std::to_string(i) + "[" + std::to_string(branchSize) + "]:l").c_str()); + } + tree->SetName(name.c_str()); + return tree; + }; + + auto fillTree = [&](TTree* t) { + TRandomMixMax gen; + gen.SetSeed(std::random_device()()); + Float_t randomArray[branchSize]; + + for (size_t entry = 0; entry < entries; entry++) { + for (auto& branch : branchCollection) { + gen.RndmArray(branchSize, randomArray); + for (size_t i = 0; i < branchSize; i++) { + branch[i] = static_cast<uint64_t>(randomArray[i]); + } + } + t->Fill(); + } + }; + + std::vector<Results> allResults; + for (size_t r = 0; r < repetitions; r++) { + std::unique_ptr<TCollection> collection = std::make_unique<TObjArray>(); + collection->SetOwner(true); + TTree* t = createTree("input"); + fillTree(t); + collection->Add(t); + + TTree* m = createTree("merged"); + fillTree(m); + + Results iterationResults; + iterationResults.sizeBytes = measure(Measurement::Size, m, collection.get()); + iterationResults.sizeSerialisedBytes = measure(Measurement::SizeAfterSerialisation, m, collection.get()); + iterationResults.deserialisationSeconds = measure(Measurement::Deserialisation, m, collection.get()); + iterationResults.serialisationSeconds = measure(Measurement::Serialisation, m, collection.get()); + iterationResults.mergingSeconds = measure(Measurement::Merging, m, collection.get()); + allResults.push_back(iterationResults); + + delete m; + } + return allResults; +} + +void printHeaderCSV(std::ostream& out) +{ + out << "name," + "objectSize,bins,dimensions,entries,branches,branchSize," + "sizeBytes,sizeSerialisedBytes,deserialisationSeconds,mergingSeconds,serialisationSeconds" + "\n"; +} + +void printResultsCSV(std::ostream& out, std::string name, const Parameters& p, const std::vector<Results>& results) +{ + for (const auto r : results) { + out << name << "," + << p.objectSize << "," << p.bins << "," << p.dimensions << "," << p.entries << "," << p.branches << "," << p.branchSize << "," + << r.sizeBytes << "," << r.sizeSerialisedBytes << "," << r.deserialisationSeconds << "," << r.mergingSeconds << "," << r.serialisationSeconds + << '\n'; + } +} + +int main(int argc, const char* argv[]) +{ + if (argc < 2) { + throw std::runtime_error("Output file name expected"); + } + + std::ofstream file; + file.open(argv[1]); + printHeaderCSV(file); + printHeaderCSV(std::cout); + + size_t repetitions = argc < 3 ? 1 : std::atoll(argv[2]); + + { + // TH1I + std::vector<Parameters> parameters{ + Parameters::forHistograms(8 << 0, 50000), + Parameters::forHistograms(8 << 3, 50000), + Parameters::forHistograms(8 << 6, 50000), + Parameters::forHistograms(8 << 9, 50000), + Parameters::forHistograms(8 << 12, 50000), + Parameters::forHistograms(8 << 15, 50000), + Parameters::forHistograms(8 << 18, 50000), + Parameters::forHistograms(8 << 21, 50000)}; + for (const auto& p : parameters) { + auto results = BM_TH1I(repetitions, p); + printResultsCSV(file, "TH1I", p, results); + printResultsCSV(std::cout, "TH1I", p, results); + } + } + + { + // TH2I + std::vector<Parameters> parameters{ + Parameters::forHistograms(8 << 0, 50000), + Parameters::forHistograms(8 << 3, 50000), + Parameters::forHistograms(8 << 6, 50000), + Parameters::forHistograms(8 << 9, 50000), + Parameters::forHistograms(8 << 12, 50000), + Parameters::forHistograms(8 << 15, 50000), + Parameters::forHistograms(8 << 18, 50000), + Parameters::forHistograms(8 << 21, 50000)}; + for (const auto& p : parameters) { + auto results = BM_TH2I(repetitions, p); + printResultsCSV(file, "TH2I", p, results); + printResultsCSV(std::cout, "TH2I", p, results); + } + } + + { + // TH3I + std::vector<Parameters> parameters{ + Parameters::forHistograms(8 << 0, 50000), + Parameters::forHistograms(8 << 3, 50000), + Parameters::forHistograms(8 << 6, 50000), + Parameters::forHistograms(8 << 9, 50000), + Parameters::forHistograms(8 << 12, 50000), + Parameters::forHistograms(8 << 15, 50000), + Parameters::forHistograms(8 << 18, 50000), + Parameters::forHistograms(8 << 21, 50000)}; + for (const auto& p : parameters) { + auto results = BM_TH3I(repetitions, p); + printResultsCSV(file, "TH3I", p, results); + printResultsCSV(std::cout, "TH3I", p, results); + } + } + + { + // THnSparseI + std::vector<Parameters> parameters{ + Parameters::forSparse(8, 8, 512), + Parameters::forSparse(64, 8, 512), + Parameters::forSparse(512, 8, 512), + Parameters::forSparse(4096, 8, 512), + Parameters::forSparse(32768, 8, 512), + Parameters::forSparse(512, 2, 512), + Parameters::forSparse(512, 4, 512), + Parameters::forSparse(512, 8, 512), + Parameters::forSparse(512, 16, 512), + Parameters::forSparse(512, 32, 512), + Parameters::forSparse(512, 64, 512), + Parameters::forSparse(512, 8, 1), + Parameters::forSparse(512, 8, 8), + Parameters::forSparse(512, 8, 64), + Parameters::forSparse(512, 8, 512), + Parameters::forSparse(512, 8, 4096), + Parameters::forSparse(512, 8, 32768), + Parameters::forSparse(512, 8, 262144), + Parameters::forSparse(512, 8, 2097152), + // Parameters::forSparse(512, 8, 16777216), + Parameters::forSparse(32, 4, 1), + Parameters::forSparse(32, 4, 8), + Parameters::forSparse(32, 4, 64), + Parameters::forSparse(32, 4, 512), + Parameters::forSparse(32, 4, 4096), + Parameters::forSparse(32, 4, 32768), + Parameters::forSparse(32, 4, 262144), + Parameters::forSparse(32, 4, 2097152), + Parameters::forSparse(32, 4, 16777216)}; + for (const auto& p : parameters) { + auto results = BM_THNSparseI(repetitions, p); + printResultsCSV(file, "THnSparseI", p, results); + printResultsCSV(std::cout, "THnSparseI", p, results); + } + } + + { + // TTree + std::vector<Parameters> parameters{ + Parameters::forTrees(8, 8, 8), + Parameters::forTrees(8, 8, 8 << 3), + Parameters::forTrees(8, 8, 8 << 6), + Parameters::forTrees(8, 8, 8 << 9), + Parameters::forTrees(8, 8, 8 << 12), + Parameters::forTrees(8, 8, 8 << 15), + Parameters::forTrees(8, 8, 8 << 18), + Parameters::forTrees(8, 1 << 0, 8 << 12), + Parameters::forTrees(8, 1 << 2, 8 << 12), + Parameters::forTrees(8, 1 << 4, 8 << 12), + Parameters::forTrees(8, 1 << 6, 8 << 12), + Parameters::forTrees(8, 1 << 8, 8 << 12), + Parameters::forTrees(1 << 0, 8, 8 << 12), + Parameters::forTrees(1 << 2, 8, 8 << 12), + Parameters::forTrees(1 << 4, 8, 8 << 12), + Parameters::forTrees(1 << 6, 8, 8 << 12), + Parameters::forTrees(1 << 8, 8, 8 << 12)}; + for (const auto& p : parameters) { + auto results = BM_TTree(repetitions, p); + printResultsCSV(file, "TTree", p, results); + printResultsCSV(std::cout, "TTree", p, results); + } + } + + { + // boost regular 1D. We use a combination of template and macro to be able to use static storage (std::array) with different parameters. +#define BM_BOOST1DARRAY_FOR(objSize, entries) \ + { \ + constexpr auto p = Parameters::forHistograms(objSize, entries); \ + auto results = BM_BoostRegular1D<std::array<int32_t, p.objectSize / sizeof(int32_t) + 2>>(repetitions, p); \ + printResultsCSV(file, "BoostRegular1DArray", p, results); \ + printResultsCSV(std::cout, "BoostRegular1DArray", p, results); \ + } + + BM_BOOST1DARRAY_FOR(8 << 0, 50000); + BM_BOOST1DARRAY_FOR(8 << 3, 50000); + BM_BOOST1DARRAY_FOR(8 << 6, 50000); + BM_BOOST1DARRAY_FOR(8 << 9, 50000); + BM_BOOST1DARRAY_FOR(8 << 12, 50000); + BM_BOOST1DARRAY_FOR(8 << 15, 50000); + } + + { + // boost regular 1D. +#define BM_BOOST1DVECTOR_FOR(objSize, entries) \ + { \ + constexpr auto p = Parameters::forHistograms(objSize, entries); \ + auto results = BM_BoostRegular1D<std::vector<int32_t>>(repetitions, p); \ + printResultsCSV(file, "BoostRegular1DVector", p, results); \ + printResultsCSV(std::cout, "BoostRegular1DVector", p, results); \ + } + + BM_BOOST1DVECTOR_FOR(8 << 0, 50000); + BM_BOOST1DVECTOR_FOR(8 << 3, 50000); + BM_BOOST1DVECTOR_FOR(8 << 6, 50000); + BM_BOOST1DVECTOR_FOR(8 << 9, 50000); + BM_BOOST1DVECTOR_FOR(8 << 12, 50000); + BM_BOOST1DVECTOR_FOR(8 << 15, 50000); + BM_BOOST1DVECTOR_FOR(8 << 18, 50000); + BM_BOOST1DVECTOR_FOR(8 << 21, 50000); + } + + { + // boost regular 2D. We use a combination of template and macro to be able to use static storage (std::array) with different parameters. +#define BM_BOOST2DARRAY_FOR(objSize, arrSize, entries) \ + { \ + constexpr auto p = Parameters::forHistograms(objSize, entries); \ + auto results = BM_BoostRegular2D<std::array<int32_t, arrSize>>(repetitions, p); \ + printResultsCSV(file, "BoostRegular2DArray", p, results); \ + printResultsCSV(std::cout, "BoostRegular2DArray", p, results); \ + } + + BM_BOOST2DARRAY_FOR(8 << 0, 10, 50000); + BM_BOOST2DARRAY_FOR(8 << 3, 36, 50000); + BM_BOOST2DARRAY_FOR(8 << 6, 178, 50000); + BM_BOOST2DARRAY_FOR(8 << 9, 1156, 50000); + BM_BOOST2DARRAY_FOR(8 << 12, 8558, 50000); + BM_BOOST2DARRAY_FOR(8 << 15, 66564, 50000); + } + + { + // boost regular 2D. +#define BM_BOOST2DVECTOR_FOR(objSize, entries) \ + { \ + constexpr auto p = Parameters::forHistograms(objSize, entries); \ + auto results = BM_BoostRegular2D<std::vector<int32_t>>(repetitions, p); \ + printResultsCSV(file, "BoostRegular2DVector", p, results); \ + printResultsCSV(std::cout, "BoostRegular2DVector", p, results); \ + } + + BM_BOOST2DVECTOR_FOR(8 << 0, 50000); + BM_BOOST2DVECTOR_FOR(8 << 3, 50000); + BM_BOOST2DVECTOR_FOR(8 << 6, 50000); + BM_BOOST2DVECTOR_FOR(8 << 9, 50000); + BM_BOOST2DVECTOR_FOR(8 << 12, 50000); + BM_BOOST2DVECTOR_FOR(8 << 15, 50000); + BM_BOOST2DVECTOR_FOR(8 << 18, 50000); + BM_BOOST2DVECTOR_FOR(8 << 21, 50000); + } + + file.close(); + return 0; +} \ No newline at end of file diff --git a/Utilities/Mergers/test/emptyLoopBenchmark.cxx b/Utilities/Mergers/test/emptyLoopBenchmark.cxx index be98727faf80b..2ec37b5f8895e 100644 --- a/Utilities/Mergers/test/emptyLoopBenchmark.cxx +++ b/Utilities/Mergers/test/emptyLoopBenchmark.cxx @@ -17,6 +17,7 @@ #include "Framework/CompletionPolicy.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DeviceSpec.h" +#include "Framework/Monitoring.h" using namespace o2::framework; @@ -116,4 +117,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) specs.push_back(heWhoRequestsExit); return specs; -} \ No newline at end of file +} diff --git a/Utilities/Mergers/test/test_Algorithm.cxx b/Utilities/Mergers/test/test_Algorithm.cxx index a87414f5963c5..c9648b8cef10e 100644 --- a/Utilities/Mergers/test/test_Algorithm.cxx +++ b/Utilities/Mergers/test/test_Algorithm.cxx @@ -24,6 +24,7 @@ #include "Mergers/CustomMergeableObject.h" #include <TObjArray.h> +#include <TObjString.h> #include <TH1.h> #include <TH2.h> #include <TH3.h> @@ -31,6 +32,8 @@ #include <TTree.h> #include <THnSparse.h> #include <TF1.h> +#include <TGraph.h> +#include <TProfile.h> //using namespace o2::framework; using namespace o2::mergers; @@ -188,6 +191,38 @@ BOOST_AUTO_TEST_CASE(MergerSingularObjects) delete other; delete target; } + { + constexpr Int_t points = 5; + Double_t x1[] = {0, 1, 2, 3, 4}; + Double_t y1[] = {0, 1, 2, 3, 4}; + Double_t x2[] = {5, 6, 7, 8, 9}; + Double_t y2[] = {5, 6, 7, 8, 9}; + + TGraph* target = new TGraph(points, x1, y1); + TGraph* other = new TGraph(points, x2, y2); + + BOOST_CHECK_NO_THROW(algorithm::merge(target, other)); + BOOST_CHECK_EQUAL(target->GetN(), 2 * points); + + delete other; + delete target; + } + { + auto target = new TProfile("hprof1", "hprof1", bins, min, max, min, max); + target->Fill(2, 2, 1); + auto other = new TProfile("hprof2", "hprof2", bins, min, max, min, max); + other->Fill(5, 5, 1); + + BOOST_CHECK_NO_THROW(algorithm::merge(target, other)); + BOOST_CHECK_EQUAL(target->GetEntries(), 2); + BOOST_CHECK_EQUAL(target->GetBinContent(target->FindBin(0)), 0); + BOOST_CHECK_EQUAL(target->GetBinContent(target->FindBin(1)), 0); + BOOST_CHECK_CLOSE(target->GetBinContent(target->FindBin(2)), 2, 0.001); + BOOST_CHECK_CLOSE(target->GetBinContent(target->FindBin(5)), 5, 0.001); + + delete other; + delete target; + } { auto* target = new CustomMergeableTObject("obj1", 123); auto* other = new CustomMergeableTObject("obj2", 321); diff --git a/Utilities/O2MessageMonitor/src/O2MessageMonitor.cxx b/Utilities/O2MessageMonitor/src/O2MessageMonitor.cxx index 9181b194e35d0..1d2dc6f8dfa0b 100644 --- a/Utilities/O2MessageMonitor/src/O2MessageMonitor.cxx +++ b/Utilities/O2MessageMonitor/src/O2MessageMonitor.cxx @@ -45,8 +45,9 @@ void O2MessageMonitor::InitTask() mIterations = GetConfig()->GetValue<int>("n"); mPayload = GetConfig()->GetValue<std::string>("payload"); std::string tmp = GetConfig()->GetValue<std::string>("name"); - if (!tmp.empty()) + if (!tmp.empty()) { mName = tmp; + } mLimitOutputCharacters = GetConfig()->GetValue<int>("limit"); } diff --git a/Utilities/PCG/include/PCG/pcg_extras.hpp b/Utilities/PCG/include/PCG/pcg_extras.hpp index 9a612a2cbdee2..cb40f933426b1 100644 --- a/Utilities/PCG/include/PCG/pcg_extras.hpp +++ b/Utilities/PCG/include/PCG/pcg_extras.hpp @@ -132,8 +132,9 @@ std::basic_ostream<CharT, Traits>& if (desired_width > 16) { out.width(desired_width - 16); } - if (highpart != 0 || desired_width > 16) + if (highpart != 0 || desired_width > 16) { out << highpart; + } CharT oldfill = '\0'; if (highpart != 0) { out.width(16); @@ -168,8 +169,9 @@ std::basic_istream<CharT, Traits>& { typename std::basic_istream<CharT, Traits>::sentry s(in); - if (!s) + if (!s) { return in; + } constexpr auto BASE = pcg128_t(10ULL); pcg128_t current(0ULL); @@ -177,8 +179,9 @@ std::basic_istream<CharT, Traits>& bool overflow = false; for (;;) { CharT wide_ch = in.get(); - if (!in.good()) + if (!in.good()) { break; + } auto ch = in.narrow(wide_ch, '\0'); if (ch < '0' || ch > '9') { in.unget(); @@ -194,8 +197,9 @@ std::basic_istream<CharT, Traits>& if (did_nothing || overflow) { in.setstate(std::ios::failbit); - if (overflow) + if (overflow) { current = ~pcg128_t(0ULL); + } } value = current; @@ -223,8 +227,9 @@ std::basic_istream<CharT, Traits>& { uint32_t value = 0xdecea5edU; in >> value; - if (!in && value == 0xdecea5edU) + if (!in && value == 0xdecea5edU) { return in; + } if (value > uint8_t(~0)) { in.setstate(std::ios::failbit); value = ~0U; @@ -425,10 +430,11 @@ SrcIter uneven_copy_impl( src_t value = 0; while (dest_first != dest_last) { - if ((count++ % SCALE) == 0) + if ((count++ % SCALE) == 0) { value = *src_first++; // Get more bits - else + } else { value >>= DEST_BITS; // Move down bits + } *dest_first++ = dest_t(value); // Truncates, ignores high bits. } @@ -549,8 +555,9 @@ auto bounded_rand(RngType& rng, typename RngType::result_type upper_bound) rtype threshold = (RngType::max() - RngType::min() + rtype(1) - upper_bound) % upper_bound; for (;;) { rtype r = rng() - RngType::min(); - if (r >= threshold) + if (r >= threshold) { return r % upper_bound; + } } } @@ -599,8 +606,9 @@ class seed_seq_from template <typename Iter> void generate(Iter start, Iter finish) { - for (auto i = start; i != finish; ++i) + for (auto i = start; i != finish; ++i) { *i = result_type(rng_()); + } } constexpr size_t size() const @@ -654,11 +662,13 @@ std::ostream& operator<<(std::ostream& out, printable_typename<T>) int status; char* pretty_name = abi::__cxa_demangle(implementation_typename, nullptr, nullptr, &status); - if (status == 0) + if (status == 0) { out << pretty_name; + } free(static_cast<void*>(pretty_name)); - if (status == 0) + if (status == 0) { return out; + } #endif out << implementation_typename; return out; diff --git a/Utilities/PCG/include/PCG/pcg_random.hpp b/Utilities/PCG/include/PCG/pcg_random.hpp index f98b863c1cffb..467167e9ff361 100644 --- a/Utilities/PCG/include/PCG/pcg_random.hpp +++ b/Utilities/PCG/include/PCG/pcg_random.hpp @@ -419,10 +419,11 @@ class engine : protected output_mixin, public: result_type operator()() { - if (output_previous) + if (output_previous) { return this->output(base_generate0()); - else + } else { return this->output(base_generate()); + } } result_type operator()(result_type upper_bound) @@ -1155,8 +1156,9 @@ struct inside_out : private baseclass { bool crosses_zero = forwards ? dist_to_zero <= delta : (-dist_to_zero) <= delta; - if (!forwards) + if (!forwards) { delta = -delta; + } state = baseclass::advance(state, delta, mult, inc); randval = baseclass::output(state); return crosses_zero; @@ -1212,13 +1214,15 @@ class extended : public baseclass if (may_tick) { bool tick = kdd ? (state & tick_mask) == state_type(0u) : (state >> tick_shift) == state_type(0u); - if (tick) + if (tick) { advance_table(); + } } if (may_tock) { bool tock = state == state_type(0u); - if (tock) + if (tock) { advance_table(); + } } return data_[index]; } @@ -1348,8 +1352,9 @@ template <bitcount_t table_pow2, bitcount_t advance_pow2, void extended<table_pow2, advance_pow2, baseclass, extvalclass, kdd>::datainit( const result_type* data) { - for (size_t i = 0; i < table_size; ++i) + for (size_t i = 0; i < table_size; ++i) { data_[i] = data[i]; + } } template <bitcount_t table_pow2, bitcount_t advance_pow2, @@ -1414,8 +1419,9 @@ std::basic_ostream<CharT, Traits>& << rng.increment() << space << rng.state_; - for (const auto& datum : rng.data_) + for (const auto& datum : rng.data_) { out << space << datum; + } out.flags(orig_flags); out.fill(orig_fill); @@ -1434,15 +1440,17 @@ std::basic_istream<CharT, Traits>& auto& base_rng = static_cast<baseclass&>(new_rng); in >> base_rng; - if (in.fail()) + if (in.fail()) { return in; + } auto orig_flags = in.flags(std::ios_base::dec | std::ios_base::skipws); for (auto& datum : new_rng.data_) { in >> datum; - if (in.fail()) + if (in.fail()) { goto bail; + } } rng = new_rng; @@ -1509,21 +1517,25 @@ void extended<table_pow2, advance_pow2, baseclass, extvalclass, kdd>::advance( state_type adv_mask = baseclass::is_mcg ? tick_mask << 2 : tick_mask; state_type next_advance_distance = this->distance(zero, adv_mask); - if (!forwards) + if (!forwards) { next_advance_distance = (-next_advance_distance) & tick_mask; + } if (next_advance_distance < (distance & tick_mask)) { ++ticks; } - if (ticks) + if (ticks) { advance_table(ticks, forwards); + } } if (forwards) { - if (may_tock && this->distance(zero) <= distance) + if (may_tock && this->distance(zero) <= distance) { advance_table(); + } baseclass::advance(distance); } else { - if (may_tock && -(this->distance(zero)) <= distance) + if (may_tock && -(this->distance(zero)) <= distance) { advance_table(state_type(1U), false); + } baseclass::advance(-distance); } } diff --git a/Utilities/Publishers/include/Publishers/DataPublisherDevice.h b/Utilities/Publishers/include/Publishers/DataPublisherDevice.h index f48a722407146..342acbf3dbbc6 100644 --- a/Utilities/Publishers/include/Publishers/DataPublisherDevice.h +++ b/Utilities/Publishers/include/Publishers/DataPublisherDevice.h @@ -35,7 +35,7 @@ namespace utilities /// Utility device for data publishing /// /// TODO: Generalize with an input policy -class DataPublisherDevice : public base::O2Device +class DataPublisherDevice final : public base::O2Device { public: typedef o2::base::O2Message O2Message; diff --git a/Utilities/Publishers/src/DataPublisherDevice.cxx b/Utilities/Publishers/src/DataPublisherDevice.cxx index 0e3a6b1e9c727..598ac8c5413e7 100644 --- a/Utilities/Publishers/src/DataPublisherDevice.cxx +++ b/Utilities/Publishers/src/DataPublisherDevice.cxx @@ -126,8 +126,9 @@ bool DataPublisherDevice::HandleO2LogicalBlock(const byte* headerBuffer, // not handled in O2Device::ForEach at the moment // indicate that the block has not been processed by a 'false' if (!dataHeader || - (dataHeader->dataDescription) != o2::header::gDataDescriptionHeartbeatFrame) + (dataHeader->dataDescription) != o2::header::gDataDescriptionHeartbeatFrame) { return false; + } if (!hbfEnvelope) { LOG(ERROR) << "no heartbeat frame envelope header found"; @@ -190,8 +191,9 @@ bool DataPublisherDevice::AppendFile(const char* name, std::vector<o2::byte>& bu { bool result = true; std::ifstream ifile(name, std::ifstream::binary); - if (ifile.bad()) + if (ifile.bad()) { return false; + } // get length of file: ifile.seekg(0, ifile.end); diff --git a/Utilities/README.md b/Utilities/README.md index 026a778c559b1..48e1bfbb91473 100644 --- a/Utilities/README.md +++ b/Utilities/README.md @@ -11,6 +11,7 @@ This module contains the following submodules: * \subpage refUtilitiesDataCompression * \subpage refUtilitiesDataFlow +* \subpage refUtilitiesDataSampling * \subpage refUtilitiesMCStepLogger * \subpage refUtilitiesMergers * \subpage refUtilitiesO2Device diff --git a/Utilities/Tools/CMakeLists.txt b/Utilities/Tools/CMakeLists.txt index 3fbf468c4ad7c..aa9a9ba83a6a0 100644 --- a/Utilities/Tools/CMakeLists.txt +++ b/Utilities/Tools/CMakeLists.txt @@ -8,5 +8,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. +add_subdirectory(cpulimit) install(PROGRAMS monitor-mem.sh DESTINATION share/scripts/) +install(PROGRAMS jobutils.sh DESTINATION share/scripts/) +install(PROGRAMS grid_submit.sh DESTINATION share/scripts/) diff --git a/Utilities/Tools/cpulimit/.clang-format b/Utilities/Tools/cpulimit/.clang-format new file mode 100644 index 0000000000000..a43d914ec38dd --- /dev/null +++ b/Utilities/Tools/cpulimit/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false \ No newline at end of file diff --git a/Utilities/Tools/cpulimit/CMakeLists.txt b/Utilities/Tools/cpulimit/CMakeLists.txt new file mode 100644 index 0000000000000..deeb42e6726ed --- /dev/null +++ b/Utilities/Tools/cpulimit/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_executable(cpulimit + cpulimit.c list.c process_group.c process_iterator.c) +target_compile_definitions(cpulimit PUBLIC _GNU_SOURCE) + +install(TARGETS cpulimit DESTINATION share/scripts/) diff --git a/Utilities/Tools/cpulimit/README b/Utilities/Tools/cpulimit/README new file mode 100644 index 0000000000000..20f543f9491ee --- /dev/null +++ b/Utilities/Tools/cpulimit/README @@ -0,0 +1,2 @@ +These sources have been copied from https://github.com/opsengine/cpulimit +commit f4d2682804931e. \ No newline at end of file diff --git a/Utilities/Tools/cpulimit/cpulimit.c b/Utilities/Tools/cpulimit/cpulimit.c new file mode 100644 index 0000000000000..e7c801496ba3d --- /dev/null +++ b/Utilities/Tools/cpulimit/cpulimit.c @@ -0,0 +1,534 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ************************************************************** + * + * This is a simple program to limit the cpu usage of a process + * If you modify this code, send me a copy please + * + * Get the latest version at: http://github.com/opsengine/cpulimit + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <time.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#if defined(__APPLE__) +#include <sys/sysctl.h> +#endif +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/wait.h> + +#if defined(__APPLE__) || defined(__FREEBSD__) +#include <libgen.h> +#endif + +#include "process_group.h" +#include "list.h" + +#ifdef HAVE_SYS_SYSINFO_H +#include <sys/sysinfo.h> +#endif + +#ifdef __APPLE__ +#include "memrchr.c" +#endif + +//some useful macro +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +//control time slot in microseconds +//each slot is splitted in a working slice and a sleeping slice +//TODO: make it adaptive, based on the actual system load +#define TIME_SLOT 100000 + +#define MAX_PRIORITY -10 + +/* GLOBAL VARIABLES */ + +//the "family" +struct process_group pgroup; +//pid of cpulimit +pid_t cpulimit_pid; +//name of this program (maybe cpulimit...) +char *program_name; + +//number of cpu +int NCPU; + +/* CONFIGURATION VARIABLES */ + +//verbose mode +int verbose = 0; +//lazy mode (exits if there is no process) +int lazy = 0; + +//SIGINT and SIGTERM signal handler +static void quit(int sig) +{ + //let all the processes continue if stopped + struct list_node *node = NULL; + if (pgroup.proclist != NULL) + { + for (node = pgroup.proclist->first; node != NULL; node = node->next) { + struct process *p = (struct process*)(node->data); + kill(p->pid, SIGCONT); + } + close_process_group(&pgroup); + } + //fix ^C little problem + printf("\r"); + fflush(stdout); + exit(0); +} + +//return t1-t2 in microseconds (no overflow checks, so better watch out!) +static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) +{ + return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); +} + +static void print_usage(FILE *stream, int exit_code) +{ + fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); + fprintf(stream, " OPTIONS\n"); + fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU); + fprintf(stream, " -v, --verbose show control statistics\n"); + fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); + fprintf(stream, " -i, --include-children limit also the children processes\n"); + fprintf(stream, " -h, --help display this help and exit\n"); + fprintf(stream, " TARGET must be exactly one of these:\n"); + fprintf(stream, " -p, --pid=N pid of the process (implies -z)\n"); + fprintf(stream, " -e, --exe=FILE name of the executable program file or path name\n"); + fprintf(stream, " COMMAND [ARGS] run this command and limit it (implies -z)\n"); + fprintf(stream, "\nReport bugs to <marlonx80@hotmail.com>.\n"); + exit(exit_code); +} + +static void increase_priority() { + //find the best available nice value + int old_priority = getpriority(PRIO_PROCESS, 0); + int priority = old_priority; + while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) { + priority--; + } + if (priority != old_priority) { + if (verbose) { printf("Priority changed to %d\n", priority); } + } + else { + if (verbose) { printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); } + } +} + +/* Get the number of CPUs */ +static int get_ncpu() { + int ncpu; +#ifdef _SC_NPROCESSORS_ONLN + ncpu = sysconf(_SC_NPROCESSORS_ONLN); +#elif defined __APPLE__ + int mib[2] = {CTL_HW, HW_NCPU}; + size_t len = sizeof(ncpu); + sysctl(mib, 2, &ncpu, &len, NULL, 0); +#elif defined _GNU_SOURCE + ncpu = get_nprocs(); +#else + ncpu = -1; +#endif + return ncpu; +} + +int get_pid_max() +{ +#ifdef __linux__ + //read /proc/sys/kernel/pid_max + static char buffer[1024]; + FILE *fd = fopen("/proc/sys/kernel/pid_max", "r"); + if (fd==NULL) { return -1; } + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + return atoi(buffer); +#elif defined __FreeBSD__ + return 99998; +#elif defined __APPLE__ + return 99998; +#endif +} + +void limit_process(pid_t pid, double limit, int include_children) +{ + //slice of the slot in which the process is allowed to run + struct timespec twork; + //slice of the slot in which the process is stopped + struct timespec tsleep; + //when the last twork has started + struct timeval startwork; + //when the last twork has finished + struct timeval endwork; + //initialization + memset(&twork, 0, sizeof(struct timespec)); + memset(&tsleep, 0, sizeof(struct timespec)); + memset(&startwork, 0, sizeof(struct timeval)); + memset(&endwork, 0, sizeof(struct timeval)); + //last working time in microseconds + unsigned long workingtime = 0; + //generic list item + struct list_node *node; + //counter + int c = 0; + + //get a better priority + increase_priority(); + + //build the family + init_process_group(&pgroup, pid, include_children); + + if (verbose) { printf("Members in the process group owned by %d: %d\n", pgroup.target_pid, pgroup.proclist->count); } + + //rate at which we are keeping active the processes (range 0-1) + //1 means that the process are using all the twork slice + double workingrate = -1; + while(1) { + update_process_group(&pgroup); + + if (pgroup.proclist->count==0) { + if (verbose) { printf("No more processes.\n"); } + break; + } + + //total cpu actual usage (range 0-1) + //1 means that the processes are using 100% cpu + double pcpu = -1; + + //estimate how much the controlled processes are using the cpu in the working interval + for (node = pgroup.proclist->first; node != NULL; node = node->next) { + struct process *proc = (struct process*)(node->data); + if (proc->cpu_usage < 0) { + continue; + } + if (pcpu < 0) { pcpu = 0; } + pcpu += proc->cpu_usage; + } + + //adjust work and sleep time slices + if (pcpu < 0) { + //it's the 1st cycle, initialize workingrate + pcpu = limit; + workingrate = limit; + twork.tv_nsec = TIME_SLOT * limit * 1000; + } + else { + //adjust workingrate + workingrate = MIN(workingrate / pcpu * limit, 1); + twork.tv_nsec = TIME_SLOT * 1000 * workingrate; + } + tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; + + if (verbose) { + if (c%200==0) { + printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); + } + if (c%10==0 && c>0) { + printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n", pcpu*100, twork.tv_nsec/1000, tsleep.tv_nsec/1000, workingrate*100); + } + } + + //resume processes + node = pgroup.proclist->first; + while (node != NULL) + { + struct list_node *next_node = node->next; + struct process *proc = (struct process*)(node->data); + if (kill(proc->pid,SIGCONT) != 0) { + //process is dead, remove it from family + if (verbose) { fprintf(stderr, "SIGCONT failed. Process %d dead!\n", proc->pid); } + //remove process from group + delete_node(pgroup.proclist, node); + remove_process(&pgroup, proc->pid); + } + node = next_node; + } + + //now processes are free to run (same working slice for all) + gettimeofday(&startwork, NULL); + nanosleep(&twork, NULL); + gettimeofday(&endwork, NULL); + workingtime = timediff(&endwork, &startwork); + + long delay = workingtime - twork.tv_nsec/1000; + if (c>0 && delay>10000) { + //delay is too much! signal to user? + //fprintf(stderr, "%d %ld us\n", c, delay); + } + + if (tsleep.tv_nsec>0) { + //stop processes only if tsleep>0 + node = pgroup.proclist->first; + while (node != NULL) + { + struct list_node *next_node = node->next; + struct process *proc = (struct process*)(node->data); + if (kill(proc->pid,SIGSTOP)!=0) { + //process is dead, remove it from family + if (verbose) { fprintf(stderr, "SIGSTOP failed. Process %d dead!\n", proc->pid); } + //remove process from group + delete_node(pgroup.proclist, node); + remove_process(&pgroup, proc->pid); + } + node = next_node; + } + //now the processes are sleeping + nanosleep(&tsleep,NULL); + } + c++; + } + close_process_group(&pgroup); +} + +int main(int argc, char **argv) { + //argument variables + const char *exe = NULL; + int perclimit = 0; + int exe_ok = 0; + int pid_ok = 0; + int limit_ok = 0; + pid_t pid = 0; + int include_children = 0; + + //get program name + char *p = (char*)memrchr(argv[0], (unsigned int)'/', strlen(argv[0])); + program_name = p==NULL ? argv[0] : (p+1); + //get current pid + cpulimit_pid = getpid(); + //get cpu count + NCPU = get_ncpu(); + + //parse arguments + int next_option; + int option_index = 0; + //A string listing valid short options letters + const char* short_options = "+p:e:l:vzih"; + //An array describing valid long options + const struct option long_options[] = { + { "pid", required_argument, NULL, 'p' }, + { "exe", required_argument, NULL, 'e' }, + { "limit", required_argument, NULL, 'l' }, + { "verbose", no_argument, NULL, 'v' }, + { "lazy", no_argument, NULL, 'z' }, + { "include-children", no_argument, NULL, 'i' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + do { + next_option = getopt_long(argc, argv, short_options,long_options, &option_index); + switch(next_option) { + case 'p': + pid = atoi(optarg); + pid_ok = 1; + break; + case 'e': + exe = optarg; + exe_ok = 1; + break; + case 'l': + perclimit = atoi(optarg); + limit_ok = 1; + break; + case 'v': + verbose = 1; + break; + case 'z': + lazy = 1; + break; + case 'i': + include_children = 1; + break; + case 'h': + print_usage(stdout, 1); + break; + case '?': + print_usage(stderr, 1); + break; + case -1: + break; + default: + abort(); + } + } while(next_option != -1); + + if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { + fprintf(stderr,"Error: Invalid value for argument PID\n"); + print_usage(stderr, 1); + exit(1); + } + if (pid != 0) { + lazy = 1; + } + + if (!limit_ok) { + fprintf(stderr,"Error: You must specify a cpu limit percentage\n"); + print_usage(stderr, 1); + exit(1); + } + double limit = perclimit / 100.0; + if (limit<0 || limit >NCPU) { + fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU); + print_usage(stderr, 1); + exit(1); + } + + int command_mode = optind < argc; + if (exe_ok + pid_ok + command_mode == 0) { + fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n"); + print_usage(stderr, 1); + exit(1); + } + + if (exe_ok + pid_ok + command_mode > 1) { + fprintf(stderr,"Error: You must specify exactly one target process, either by name, pid, or command line\n"); + print_usage(stderr, 1); + exit(1); + } + + //all arguments are ok! + signal(SIGINT, quit); + signal(SIGTERM, quit); + + //print the number of available cpu + if (verbose) { printf("%d cpu detected\n", NCPU); } + + if (command_mode) { + int i; + //executable file + const char *cmd = argv[optind]; + //command line arguments + char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*)); + if (cmd_args==NULL) { exit(2); } + for (i=0; i<argc-optind; i++) { + cmd_args[i] = argv[i+optind]; + } + cmd_args[i] = NULL; + + if (verbose) { + printf("Running command: '%s", cmd); + for (i=1; i<argc-optind; i++) { + printf(" %s", cmd_args[i]); + } + printf("'\n"); + } + + int child = fork(); + if (child < 0) { + exit(EXIT_FAILURE); + } + else if (child == 0) { + //target process code + int ret = execvp(cmd, cmd_args); + //if we are here there was an error, show it + perror("Error"); + exit(ret); + } + else { + //parent code + free(cmd_args); + int limiter = fork(); + if (limiter < 0) { + exit(EXIT_FAILURE); + } + else if (limiter > 0) { + //parent + int status_process; + int status_limiter; + waitpid(child, &status_process, 0); + waitpid(limiter, &status_limiter, 0); + if (WIFEXITED(status_process)) { + if (verbose) { printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process)); } + exit(WEXITSTATUS(status_process)); + } + printf("Process %d terminated abnormally\n", child); + exit(status_process); + } + else { + //limiter code + if (verbose) { printf("Limiting process %d\n",child); } + limit_process(child, limit, include_children); + exit(0); + } + } + } + + while(1) { + //look for the target process..or wait for it + pid_t ret = 0; + if (pid_ok) { + //search by pid + ret = find_process_by_pid(pid); + if (ret == 0) { + printf("No process found\n"); + } + else if (ret < 0) { + printf("Process found but you aren't allowed to control it\n"); + } + } + else { + //search by file or path name + ret = find_process_by_name(exe); + if (ret == 0) { + printf("No process found\n"); + } + else if (ret < 0) { + printf("Process found but you aren't allowed to control it\n"); + } + else { + pid = ret; + } + } + if (ret > 0) { + if (ret == cpulimit_pid) { + printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret); + exit(1); + } + printf("Process %d found\n", pid); + //control + limit_process(pid, limit, include_children); + } + if (lazy) { break; } + sleep(2); + }; + + exit(0); +} diff --git a/Utilities/Tools/cpulimit/list.c b/Utilities/Tools/cpulimit/list.c new file mode 100644 index 0000000000000..2ac36708d4a08 --- /dev/null +++ b/Utilities/Tools/cpulimit/list.c @@ -0,0 +1,148 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdlib.h> +#include <string.h> + +#include "list.h" + +#define EMPTYLIST NULL + +void init_list(struct list *l,int keysize) { + l->first=l->last=NULL; + l->keysize=keysize; + l->count=0; +} + +struct list_node *add_elem(struct list *l,void *elem) { + struct list_node *newnode=(struct list_node*)malloc(sizeof(struct list_node)); + newnode->data=elem; + newnode->previous=l->last; + newnode->next=NULL; + if (l->count==0) { + l->first=l->last=newnode; + } + else { + l->last->next=newnode; + l->last=newnode; + } + l->count++; + return newnode; +} + +void delete_node(struct list *l,struct list_node *node) { + if (l->count==1) { + l->first=l->last=NULL; + } + else if (node==l->first) { + node->next->previous=NULL; + l->first=node->next; + } + else if (node==l->last) { + node->previous->next=NULL; + l->last=node->previous; + } + else { + node->previous->next=node->next; + node->next->previous=node->previous; + } + l->count--; + free(node); +} + +void destroy_node(struct list *l,struct list_node *node) { + free(node->data); + node->data=NULL; + delete_node(l,node); +} + +int is_empty_list(struct list *l) { + return (l->count==0?TRUE:FALSE); +} + +int get_list_count(struct list *l) { + return l->count; +} + +void *first_elem(struct list *l) { + return l->first->data; +} + +struct list_node *first_node(struct list *l) { + return l->first; +} + +void *last_elem(struct list *l) { + return l->last->data; +} + +struct list_node *last_node(struct list *l) { + return l->last; +} + +struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length) { + struct list_node *tmp; + tmp=l->first; + while(tmp!=NULL) { + if(!memcmp((char*)tmp->data+offset,elem,length==0?l->keysize:length)) { return (tmp); } + tmp=tmp->next; + } + return EMPTYLIST; +} + +struct list_node *locate_node(struct list *l,void *elem) { + return(xlocate_node(l,elem,0,0)); +} + +void *xlocate_elem(struct list *l,void *elem,int offset,int length) { + struct list_node *node=xlocate_node(l,elem,offset,length); + return(node==NULL?NULL:node->data); +} + +void *locate_elem(struct list *l,void *elem) { + return(xlocate_elem(l,elem,0,0)); +} + +void clear_list(struct list *l) { + while(l->first!=EMPTYLIST) { + struct list_node *tmp; + tmp=l->first; + l->first=l->first->next; + free(tmp); + tmp=NULL; + } + l->last=EMPTYLIST; + l->count=0; +} + +void destroy_list(struct list *l) { + while(l->first!=EMPTYLIST) { + struct list_node *tmp; + tmp=l->first; + l->first=l->first->next; + free(tmp->data); + tmp->data=NULL; + free(tmp); + tmp=NULL; + } + l->last=EMPTYLIST; + l->count=0; +} diff --git a/Utilities/Tools/cpulimit/list.h b/Utilities/Tools/cpulimit/list.h new file mode 100644 index 0000000000000..0b43a2b39c0f3 --- /dev/null +++ b/Utilities/Tools/cpulimit/list.h @@ -0,0 +1,138 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __LIST__ + +#define __LIST__ + +#ifndef TRUE + #define TRUE 1 + #define FALSE 0 +#endif + +struct list_node { + //pointer to the content of the node + void *data; + //pointer to previous node + struct list_node *previous; + //pointer to next node + struct list_node *next; +}; + +struct list { + //first node + struct list_node *first; + //last node + struct list_node *last; + //size of the search key in bytes + int keysize; + //element count + int count; +}; + +/* + * Initialize a list, with a specified key size + */ +void init_list(struct list *l,int keysize); + +/* + * Add a new element at the end of the list + * return the pointer to the new node + */ +struct list_node *add_elem(struct list *l,void *elem); + +/* + * Delete a node + */ +void delete_node(struct list *l,struct list_node *node); + +/* + * Delete a node from the list, even the content pointed by it + * Use only when the content is a dynamically allocated pointer + */ +void destroy_node(struct list *l,struct list_node *node); + +/* + * Check whether a list is empty or not + */ +int is_empty_list(struct list *l); + +/* + * Return the element count of the list + */ +int get_list_count(struct list *l); + +/* + * Return the first element (content of the node) from the list + */ +void *first_elem(struct list *l); + +/* + * Return the first node from the list + */ +struct list_node *first_node(struct list *l); + +/* + * Return the last element (content of the node) from the list + */ +void *last_elem(struct list *l); + +/* + * Return the last node from the list + */ +struct list_node *last_node(struct list *l); + +/* + * Search an element of the list by content + * the comparison is done from the specified offset and for a specified length + * if offset=0, the comparison starts from the address pointed by data + * if length=0, default keysize is used for length + * if the element is found, return the node address + * else return NULL + */ +struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length); + +/* + * The same of xlocate_node(), but return the content of the node + */ +void *xlocate_elem(struct list *l,void *elem,int offset,int length); + +/* + * The same of calling xlocate_node() with offset=0 and length=0 + */ +struct list_node *locate_node(struct list *l,void *elem); + +/* + * The same of locate_node, but return the content of the node + */ +void *locate_elem(struct list *l,void *elem); + +/* + * Delete all the elements in the list + */ +void clear_list(struct list *l); + +/* + * Delete every element in the list, and free the memory pointed by all the node data + */ +void destroy_list(struct list *l); + +#endif diff --git a/Utilities/Tools/cpulimit/memrchr.c b/Utilities/Tools/cpulimit/memrchr.c new file mode 100644 index 0000000000000..1f3787020afad --- /dev/null +++ b/Utilities/Tools/cpulimit/memrchr.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2007 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +/* + * Reverse memchr() + * Find the last occurrence of 'c' in the buffer 's' of size 'n'. + */ +void * +memrchr(s, c, n) + const void *s; + int c; + size_t n; +{ + if (n != 0) { + const unsigned char *cp; + cp = (unsigned char *)s + n; + do { + if (*(--cp) == (unsigned char)c) + return((void *)cp); + } while (--n != 0); + } + return((void *)0); +} diff --git a/Utilities/Tools/cpulimit/process_group.c b/Utilities/Tools/cpulimit/process_group.c new file mode 100644 index 0000000000000..98b6b5ed54c25 --- /dev/null +++ b/Utilities/Tools/cpulimit/process_group.c @@ -0,0 +1,209 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#if defined(__APPLE__) || defined(__FREEBSD__) +#include <libgen.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/time.h> +#include <signal.h> + +#include <assert.h> + +#include "process_iterator.h" +#include "process_group.h" +#include "list.h" + +// look for a process by pid +// search_pid : pid of the wanted process +// return: pid of the found process, if successful +// negative pid, if the process does not exist or if the signal fails +int find_process_by_pid(pid_t pid) +{ + return (kill(pid,0)==0) ? pid : -pid; +} + +// look for a process with a given name +// process: the name of the wanted process. it can be an absolute path name to the executable file +// or just the file name +// return: pid of the found process, if it is found +// 0, if it's not found +// negative pid, if it is found but it's not possible to control it +int find_process_by_name(const char *process_name) +{ + //pid of the target process + pid_t pid = -1; + + //process iterator + struct process_iterator it; + struct process proc; + struct process_filter filter; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &proc) != -1) + { + //process found + if (strncmp(basename(proc.command), process_name, strlen(process_name))==0 && kill(pid,SIGCONT)==0) { + //process is ok! + pid = proc.pid; + break; + } + } + if (close_process_iterator(&it) != 0) exit(1); + if (pid >= 0) { + //ok, the process was found + return pid; + } + else { + //process not found + return 0; + } +} + +int init_process_group(struct process_group *pgroup, int target_pid, int include_children) +{ + //hashtable initialization + memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); + pgroup->target_pid = target_pid; + pgroup->include_children = include_children; + pgroup->proclist = (struct list*)malloc(sizeof(struct list)); + init_list(pgroup->proclist, 4); + memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); + update_process_group(pgroup); + return 0; +} + +int close_process_group(struct process_group *pgroup) +{ + int i; + int size = sizeof(pgroup->proctable) / sizeof(struct process*); + for (i=0; i<size; i++) { + if (pgroup->proctable[i] != NULL) { + //free() history for each process + destroy_list(pgroup->proctable[i]); + free(pgroup->proctable[i]); + pgroup->proctable[i] = NULL; + } + } + clear_list(pgroup->proclist); + free(pgroup->proclist); + pgroup->proclist = NULL; + return 0; +} + +void remove_terminated_processes(struct process_group *pgroup) +{ + //TODO +} + +//return t1-t2 in microseconds (no overflow checks, so better watch out!) +static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) +{ + return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); +} + +//parameter in range 0-1 +#define ALFA 0.08 +#define MIN_DT 20 + +void update_process_group(struct process_group *pgroup) +{ + struct process_iterator it; + struct process tmp_process; + struct process_filter filter; + struct timeval now; + gettimeofday(&now, NULL); + //time elapsed from previous sample (in ms) + long dt = timediff(&now, &pgroup->last_update) / 1000; + filter.pid = pgroup->target_pid; + filter.include_children = pgroup->include_children; + init_process_iterator(&it, &filter); + clear_list(pgroup->proclist); + init_list(pgroup->proclist, 4); + + while (get_next_process(&it, &tmp_process) != -1) + { +// struct timeval t; +// gettimeofday(&t, NULL); +// printf("T=%ld.%ld PID=%d PPID=%d START=%d CPUTIME=%d\n", t.tv_sec, t.tv_usec, tmp_process.pid, tmp_process.ppid, tmp_process.starttime, tmp_process.cputime); + int hashkey = pid_hashfn(tmp_process.pid); + if (pgroup->proctable[hashkey] == NULL) + { + //empty bucket + pgroup->proctable[hashkey] = malloc(sizeof(struct list)); + struct process *new_process = malloc(sizeof(struct process)); + tmp_process.cpu_usage = -1; + memcpy(new_process, &tmp_process, sizeof(struct process)); + init_list(pgroup->proctable[hashkey], 4); + add_elem(pgroup->proctable[hashkey], new_process); + add_elem(pgroup->proclist, new_process); + } + else + { + //existing bucket + struct process *p = (struct process*)locate_elem(pgroup->proctable[hashkey], &tmp_process); + if (p == NULL) + { + //process is new. add it + struct process *new_process = malloc(sizeof(struct process)); + tmp_process.cpu_usage = -1; + memcpy(new_process, &tmp_process, sizeof(struct process)); + add_elem(pgroup->proctable[hashkey], new_process); + add_elem(pgroup->proclist, new_process); + } + else + { + assert(tmp_process.pid == p->pid); + assert(tmp_process.starttime == p->starttime); + add_elem(pgroup->proclist, p); + if (dt < MIN_DT) continue; + //process exists. update CPU usage + double sample = 1.0 * (tmp_process.cputime - p->cputime) / dt; + if (p->cpu_usage == -1) { + //initialization + p->cpu_usage = sample; + } + else { + //usage adjustment + p->cpu_usage = (1.0-ALFA) * p->cpu_usage + ALFA * sample; + } + p->cputime = tmp_process.cputime; + } + } + } + close_process_iterator(&it); + if (dt < MIN_DT) return; + pgroup->last_update = now; +} + +int remove_process(struct process_group *pgroup, int pid) +{ + int hashkey = pid_hashfn(pid); + if (pgroup->proctable[hashkey] == NULL) return 1; //nothing to delete + struct list_node *node = (struct list_node*)locate_node(pgroup->proctable[hashkey], &pid); + if (node == NULL) return 2; + delete_node(pgroup->proctable[hashkey], node); + return 0; +} diff --git a/Utilities/Tools/cpulimit/process_group.h b/Utilities/Tools/cpulimit/process_group.h new file mode 100644 index 0000000000000..5a5b581554a80 --- /dev/null +++ b/Utilities/Tools/cpulimit/process_group.h @@ -0,0 +1,55 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __PROCESS_GROUP_H + +#define __PROCESS_GROUP_H + +#include "process_iterator.h" + +#include "list.h" + +#define PIDHASH_SZ 1024 +#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) + +struct process_group +{ + //hashtable with all the processes (array of struct list of struct process) + struct list *proctable[PIDHASH_SZ]; + struct list *proclist; + pid_t target_pid; + int include_children; + struct timeval last_update; +}; + +int init_process_group(struct process_group *pgroup, int target_pid, int include_children); + +void update_process_group(struct process_group *pgroup); + +int close_process_group(struct process_group *pgroup); + +int find_process_by_pid(pid_t pid); + +int find_process_by_name(const char *process_name); + +int remove_process(struct process_group *pgroup, int pid); + +#endif diff --git a/Utilities/Tools/cpulimit/process_iterator.c b/Utilities/Tools/cpulimit/process_iterator.c new file mode 100644 index 0000000000000..8b4019d237f2b --- /dev/null +++ b/Utilities/Tools/cpulimit/process_iterator.c @@ -0,0 +1,49 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef __APPLE__ +#include <sys/procfs.h> +#endif +#include <time.h> +#include "process_iterator.h" + +//See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 + +#ifdef __linux__ + +#include "process_iterator_linux.c" + +#elif defined __FreeBSD__ + +#include "process_iterator_freebsd.c" + +#elif defined __APPLE__ + +#include "process_iterator_apple.c" + +#else + +#error Platform not supported + +#endif diff --git a/Utilities/Tools/cpulimit/process_iterator.h b/Utilities/Tools/cpulimit/process_iterator.h new file mode 100644 index 0000000000000..70520b68a6e88 --- /dev/null +++ b/Utilities/Tools/cpulimit/process_iterator.h @@ -0,0 +1,97 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __PROCESS_ITERATOR_H + +#define __PROCESS_ITERATOR_H + +#include <unistd.h> +#include <limits.h> +#include <dirent.h> + +//USER_HZ detection, from openssl code +#ifndef HZ +# if defined(_SC_CLK_TCK) \ + && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) +# define HZ ((double)sysconf(_SC_CLK_TCK)) +# else +# ifndef CLK_TCK +# ifndef _BSD_CLK_TCK_ /* FreeBSD hack */ +# define HZ 100.0 +# else /* _BSD_CLK_TCK_ */ +# define HZ ((double)_BSD_CLK_TCK_) +# endif +# else /* CLK_TCK */ +# define HZ ((double)CLK_TCK) +# endif +# endif +#endif + +#ifdef __FreeBSD__ +#include <kvm.h> +#endif + +// process descriptor +struct process { + //pid of the process + pid_t pid; + //ppid of the process + pid_t ppid; + //start time (unix timestamp) + int starttime; + //cputime used by the process (in milliseconds) + int cputime; + //actual cpu usage estimation (value in range 0-1) + double cpu_usage; + //absolute path of the executable file + char command[PATH_MAX+1]; +}; + +struct process_filter { + int pid; + int include_children; + char program_name[PATH_MAX+1]; +}; + +struct process_iterator { +#ifdef __linux__ + DIR *dip; + int boot_time; +#elif defined __FreeBSD__ + kvm_t *kd; + struct kinfo_proc *procs; + int count; + int i; +#elif defined __APPLE__ + int i; + int count; + int *pidlist; +#endif + struct process_filter *filter; +}; + +int init_process_iterator(struct process_iterator *i, struct process_filter *filter); + +int get_next_process(struct process_iterator *i, struct process *p); + +int close_process_iterator(struct process_iterator *i); + +#endif diff --git a/Utilities/Tools/cpulimit/process_iterator_apple.c b/Utilities/Tools/cpulimit/process_iterator_apple.c new file mode 100644 index 0000000000000..b878ed8c9a946 --- /dev/null +++ b/Utilities/Tools/cpulimit/process_iterator_apple.c @@ -0,0 +1,148 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Author: Simon Sigurdhsson + * + */ + +#include <errno.h> +#include <stdio.h> +#include <libproc.h> + +int unique_nonzero_ints(int* arr_in, int len_in, int* arr_out) { + int* source = arr_in; + if (arr_out == NULL) return -1; + if (arr_in == arr_out) { + source = malloc(sizeof(int)*len_in); + memcpy(source, arr_in, sizeof(int)*len_in); + memset(arr_out, -1, sizeof(int)*len_in); + } + int len_out = 0; + int i, j; + for (i=0; i<len_in; i++) { + int found = 0; + if (source[i] == 0) continue; + for (j=0; !found && j<len_out; j++) { + found = (source[i] == arr_out[j]) ? 1 : 0; + } + if (!found) { + arr_out[len_out++] = source[i]; + } + } + if (arr_in == arr_out) { + free(source); + } + return len_out-1; +} + +int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { + it->i = 0; + /* Find out how much to allocate for it->pidlist */ + if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { + fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); + return -1; + } + /* Allocate and populate it->pidlist */ + if ((it->pidlist = (int *)malloc((it->count)*sizeof(int))) == NULL) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + } + if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) { + fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); + return -1; + } + it->count = unique_nonzero_ints(it->pidlist, it->count, it->pidlist); + it->filter = filter; + return 0; +} + +static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { + int bytes; + process->pid = ti->pbsd.pbi_pid; + process->ppid = ti->pbsd.pbi_ppid; + process->starttime = ti->pbsd.pbi_start_tvsec; + process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; + bytes = strlen(ti->pbsd.pbi_comm); + memcpy(process->command, ti->pbsd.pbi_comm, (bytes < PATH_MAX ? bytes : PATH_MAX) + 1); + return 0; +} + +static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { + int bytes; + bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); + if (bytes <= 0) { + if (!(errno & (EPERM | ESRCH))) { + fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); + } + return -1; + } else if (bytes < sizeof(ti)) { + fprintf(stderr, "proc_pidinfo: too few bytes; expected %ld, got %d\n", sizeof(ti), bytes); + return -1; + } + return 0; +} + +int get_next_process(struct process_iterator *it, struct process *p) { + if (it->i == it->count) return -1; + if (it->filter->pid != 0 && !it->filter->include_children) { + struct proc_taskallinfo ti; + if (get_process_pti(it->filter->pid, &ti) != 0) { + it->i = it->count = 0; + return -1; + } + it->i = it->count = 1; + return pti2proc(&ti, p); + } + while (it->i < it->count) { + struct proc_taskallinfo ti; + if (get_process_pti(it->pidlist[it->i], &ti) != 0) { + it->i++; + continue; + } + if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) { + it->i++; + continue; + } + if (it->filter->pid != 0 && it->filter->include_children) { + pti2proc(&ti, p); + it->i++; + if (p->pid != it->pidlist[it->i - 1]) // I don't know why this can happen + continue; + if (p->pid != it->filter->pid && p->ppid != it->filter->pid) + continue; + return 0; + } + else if (it->filter->pid == 0) + { + pti2proc(&ti, p); + it->i++; + return 0; + } + } + return -1; +} + +int close_process_iterator(struct process_iterator *it) { + free(it->pidlist); + it->pidlist = NULL; + it->filter = NULL; + it->count = 0; + it->i = 0; + return 0; +} diff --git a/Utilities/Tools/cpulimit/process_iterator_freebsd.c b/Utilities/Tools/cpulimit/process_iterator_freebsd.c new file mode 100644 index 0000000000000..a6381123e1251 --- /dev/null +++ b/Utilities/Tools/cpulimit/process_iterator_freebsd.c @@ -0,0 +1,119 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <sys/sysctl.h> +#include <sys/user.h> +#include <fcntl.h> +#include <paths.h> + +int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { + char errbuf[_POSIX2_LINE_MAX]; + it->i = 0; + /* Open the kvm interface, get a descriptor */ + if ((it->kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) { + fprintf(stderr, "kvm_open: %s\n", errbuf); + return -1; + } + /* Get the list of processes. */ + if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) { + kvm_close(it->kd); +// fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); + return -1; + } + it->filter = filter; + return 0; +} + +static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) +{ + proc->pid = kproc->ki_pid; + proc->ppid = kproc->ki_ppid; + proc->cputime = kproc->ki_runtime / 1000; + proc->starttime = kproc->ki_start.tv_sec; + char **args = kvm_getargv(kd, kproc, sizeof(proc->command)); + if (args == NULL) return -1; + memcpy(proc->command, args[0], strlen(args[0]) + 1); + return 0; +} + +static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) +{ + int count; + struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); + if (count == 0 || kproc == NULL) + { +// fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd)); + return -1; + } + kproc2proc(kd, kproc, process); + return 0; +} + +int get_next_process(struct process_iterator *it, struct process *p) { + if (it->i == it->count) + { + return -1; + } + if (it->filter->pid != 0 && !it->filter->include_children) + { + if (get_single_process(it->kd, it->filter->pid, p) != 0) + { + it->i = it->count = 0; + return -1; + } + it->i = it->count = 1; + return 0; + } + while (it->i < it->count) + { + struct kinfo_proc *kproc = &(it->procs[it->i]); + if (kproc->ki_flag & P_SYSTEM) + { + // skip system processes + it->i++; + continue; + } + if (it->filter->pid != 0 && it->filter->include_children) + { + kproc2proc(it->kd, kproc, p); + it->i++; + if (p->pid != it->filter->pid && p->ppid != it->filter->pid) + continue; + return 0; + } + else if (it->filter->pid == 0) + { + kproc2proc(it->kd, kproc, p); + it->i++; + return 0; + } + } + return -1; +} + +int close_process_iterator(struct process_iterator *it) { + if (kvm_close(it->kd) == -1) { + fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); + return -1; + } + return 0; +} + diff --git a/Utilities/Tools/cpulimit/process_iterator_linux.c b/Utilities/Tools/cpulimit/process_iterator_linux.c new file mode 100644 index 0000000000000..c8cdd07adcbe9 --- /dev/null +++ b/Utilities/Tools/cpulimit/process_iterator_linux.c @@ -0,0 +1,181 @@ +/** + * + * cpulimit - a CPU limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <sys/vfs.h> + +static int get_boot_time() +{ + int uptime = 0; + FILE *fp = fopen ("/proc/uptime", "r"); + if (fp != NULL) + { + char buf[BUFSIZ]; + char *b = fgets(buf, BUFSIZ, fp); + if (b == buf) + { + char *end_ptr; + double upsecs = strtod(buf, &end_ptr); + uptime = (int)upsecs; + } + fclose (fp); + } + time_t now = time(NULL); + return now - uptime; +} + +static int check_proc() +{ + struct statfs mnt; + if (statfs("/proc", &mnt) < 0) + return 0; + if (mnt.f_type!=0x9fa0) + return 0; + return 1; +} + +int init_process_iterator(struct process_iterator *it, struct process_filter *filter) +{ + if (!check_proc()) { + fprintf(stderr, "procfs is not mounted!\nAborting\n"); + exit(-2); + } + //open a directory stream to /proc directory + if ((it->dip = opendir("/proc")) == NULL) + { + perror("opendir"); + return -1; + } + it->filter = filter; + it->boot_time = get_boot_time(); + return 0; +} + +static int read_process_info(pid_t pid, struct process *p) +{ + static char buffer[1024]; + static char statfile[32]; + static char exefile[1024]; + p->pid = pid; + //read stat file + sprintf(statfile, "/proc/%d/stat", p->pid); + FILE *fd = fopen(statfile, "r"); + if (fd==NULL) return -1; + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + char *token = strtok(buffer, " "); + int i; + for (i=0; i<3; i++) token = strtok(NULL, " "); + p->ppid = atoi(token); + for (i=0; i<10; i++) + token = strtok(NULL, " "); + p->cputime = atoi(token) * 1000 / HZ; + token = strtok(NULL, " "); + p->cputime += atoi(token) * 1000 / HZ; + for (i=0; i<7; i++) + token = strtok(NULL, " "); + p->starttime = atoi(token) / sysconf(_SC_CLK_TCK); + //read command line + sprintf(exefile,"/proc/%d/cmdline", p->pid); + fd = fopen(exefile, "r"); + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + strcpy(p->command, buffer); + return 0; +} + +static pid_t getppid_of(pid_t pid) +{ + char statfile[20]; + char buffer[1024]; + sprintf(statfile, "/proc/%d/stat", pid); + FILE *fd = fopen(statfile, "r"); + if (fd==NULL) return -1; + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + char *token = strtok(buffer, " "); + int i; + for (i=0; i<3; i++) token = strtok(NULL, " "); + return atoi(token); +} + +static int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + int ppid = child_pid; + while(ppid > 1 && ppid != parent_pid) { + ppid = getppid_of(ppid); + } + return ppid == parent_pid; +} + +int get_next_process(struct process_iterator *it, struct process *p) +{ + if (it->dip == NULL) + { + //end of processes + return -1; + } + if (it->filter->pid != 0 && !it->filter->include_children) + { + int ret = read_process_info(it->filter->pid, p); + //p->starttime += it->boot_time; + closedir(it->dip); + it->dip = NULL; + if (ret != 0) return -1; + return 0; + } + struct dirent *dit = NULL; + //read in from /proc and seek for process dirs + while ((dit = readdir(it->dip)) != NULL) { + if(strtok(dit->d_name, "0123456789") != NULL) + continue; + p->pid = atoi(dit->d_name); + if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) continue; + read_process_info(p->pid, p); + //p->starttime += it->boot_time; + break; + } + if (dit == NULL) + { + //end of processes + closedir(it->dip); + it->dip = NULL; + return -1; + } + return 0; +} + +int close_process_iterator(struct process_iterator *it) { + if (it->dip != NULL && closedir(it->dip) == -1) { + perror("closedir"); + return 1; + } + it->dip = NULL; + return 0; +} diff --git a/Utilities/Tools/grid_submit.sh b/Utilities/Tools/grid_submit.sh new file mode 100755 index 0000000000000..b26839a3c87e3 --- /dev/null +++ b/Utilities/Tools/grid_submit.sh @@ -0,0 +1,183 @@ +#!/bin/bash + +# A helper script, making it easy to submit existing +# scripts as an ALIEN GRID job (with the following notation): +# +# grid-submit my_script.sh jobname +# +# The script then handles all interaction with the GRID automatically. The user +# does not need to create JDLs files nor upload them to the GRID manually/herself. +# +# The script can also simulate execution of the job locally. To this end, it suffices +# to say +# +# ALIEN_PROC_ID=1 grid-submit my_script.sh +# +# Currently handles only a very basic JDL configuration. Further improvements would be: +# +# -) allow JDL customization via command line arguments or JDL tags inside the script +# + +# set -o pipefail + +function per() { printf "\033[31m$1\033[m\n" >&2; } +function pok() { printf "\033[32m$1\033[m\n" >&2; } +function banner() { echo ; echo ==================== $1 ==================== ; } + +if [[ ! $ALIEN_PROC_ID && ! $1 ]]; then + per "Please give a job script" + exit 1 +fi + +# find out if this script is really executed on GRID +# in this case, we should find an environment variable JALIEN_TOKEN_CERT +ONGRID=0 +[ "${JALIEN_TOKEN_CERT}" ] && ONGRID=1 + +# General job configuration +MY_USER=${ALIEN_USER:-`whoami`} +if [[ ! $MY_USER ]]; then + per "Problems retrieving current AliEn user. Did you run alien-token-init?" + exit 1 +fi +MY_HOMEDIR="/alice/cern.ch/user/${MY_USER:0:1}/${MY_USER}" +MY_BINDIR="$MY_HOMEDIR/bintest" +MY_JOBPREFIX="$MY_HOMEDIR/${ALIEN_TOPWORKDIR:-selfjobs}" +MY_JOBSCRIPT="$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")" # the job script with full path +MY_JOBNAME=${2:-$(basename ${MY_JOBSCRIPT})} +MY_JOBNAMEDATE="${MY_JOBNAME}-$(date -u +%Y%m%d-%H%M%S)" +MY_JOBWORKDIR="$MY_JOBPREFIX/${MY_JOBNAMEDATE}" # ISO-8601 UTC + +pok "Your job's working directory will be $MY_JOBWORKDIR" +pok "Set the job name by running $0 <scriptname> <jobname>" + +# +# Generate local workdir +# +if [[ "${ONGRID}" == "0" ]]; then + WORKDIR=${WORKDIR:-/tmp/alien_work/$(basename "$MY_JOBWORKDIR")} + mkdir -p ${WORKDIR} + cp "${MY_JOBSCRIPT}" "${WORKDIR}/alien_jobscript.sh" +fi + +# +# Submitter code +# +if [[ ! $ALIEN_PROC_ID ]]; then + # We are not on a worker node: assuming client --> test if alien is there? + which alien.py 2> /dev/null + # check exit code + if [[ ! "$?" == "0" ]]; then + XJALIEN_LATEST=`find /cvmfs/alice.cern.ch/el7-x86_64/Modules/modulefiles/xjalienfs -type f -printf "%f\n" | tail -n1` + banner "Loading xjalienfs package $XJALIEN_LATEST since not yet loaded" + eval "$(/cvmfs/alice.cern.ch/bin/alienv printenv xjalienfs::"$XJALIEN_LATEST")" + fi + + # Create temporary workdir to assemble files, and submit from there (or execute locally) + cd "$(dirname "$0")" + THIS_SCRIPT="$PWD/$(basename "$0")" + + cd "${WORKDIR}" + + # ---- Generate JDL ---------------- + # TODO: Make this configurable or read from a preamble section in the jobfile + cat > "${MY_JOBNAMEDATE}.jdl" <<EOF +Executable = "${MY_BINDIR}/${MY_JOBNAMEDATE}.sh"; +InputFile = "LF:${MY_JOBWORKDIR}/alien_jobscript.sh"; +OutputDir = "${MY_JOBWORKDIR}"; +Output = { + "*.log*,log.txt@disk=2" +}; +Requirements = member(other.GridPartitions,"multicore_8"); +MemorySize = "60GB"; +TTL=80000; +EOF +# + + pok "Local working directory is $PWD" + + pok "Preparing job \"$MY_JOBNAMEDATE\"" + ( + set -x + alien.py rmdir "$MY_JOBWORKDIR" || true # remove existing job dir + alien.py mkdir "$MY_BINDIR" || true # create bindir + alien.py mkdir "$MY_JOBPREFIX" || true # create job output prefix + alien.py mkdir jdl || true + alien.py mkdir "$MY_JOBWORKDIR" || true + alien.py rm "$MY_BINDIR/${MY_JOBNAMEDATE}.sh" || true # remove current job script + alien.py cp "${PWD}/${MY_JOBNAMEDATE}.jdl" alien://${MY_HOMEDIR}/jdl/${MY_JOBNAMEDATE}.jdl@ALICE::CERN::EOS || true # copy the jdl + alien.py cp "$THIS_SCRIPT" alien://${MY_BINDIR}/${MY_JOBNAMEDATE}.sh@ALICE::CERN::EOS || true # copy current job script to AliEn + alien.py cp "${MY_JOBSCRIPT}" alien://${MY_JOBWORKDIR}/alien_jobscript.sh@ALICE::CERN::EOS || true + ) &> alienlog.txt + + pok "Submitting job \"${MY_JOBNAMEDATE}\" from $PWD" + ( + alien.py submit jdl/${MY_JOBNAMEDATE}.jdl || true + ) &>> alienlog.txt + + MY_JOBID=$( (grep 'Your new job ID is' alienlog.txt | grep -oE '[0-9]+' || true) | sort -n | tail -n1) + if [[ $MY_JOBID ]]; then + pok "OK, display progress on https://alimonitor.cern.ch/agent/jobs/details.jsp?pid=$MY_JOBID" + else + per "Job submission failed: error log follows" + cat alienlog.txt + fi + + exit 0 +fi + +#################################################################################################### +# The following part is executed on the worker node or locally +#################################################################################################### +if [[ "${ONGRID}" == 0 ]]; then + banner "Executing job in directory ${WORKDIR}" + cd "${WORKDIR}" 2> /dev/null +fi + +# All is redirected to log.txt but kept on stdout as well +if [[ $ALIEN_PROC_ID ]]; then + exec &> >(tee -a log.txt) +fi + +# ----------- START JOB PREAMBLE ----------------------------- +banner "Environment" +env + +banner "OS detection" +lsb_release -a || true +cat /etc/os-release || true +cat /etc/redhat-release || true + +if [ ! "$O2_ROOT" ]; then + O2_PACKAGE_LATEST=`find /cvmfs/alice.cern.ch/el7-x86_64/Modules/modulefiles/O2 -type f -printf "%f\n" | tail -n1` + banner "Loading O2 package $O2_PACKAGE_LATEST" + eval "$(/cvmfs/alice.cern.ch/bin/alienv printenv O2::"$O2_PACKAGE_LATEST")" +fi +if [ ! "$XJALIEN_ROOT" ]; then + XJALIEN_LATEST=`find /cvmfs/alice.cern.ch/el7-x86_64/Modules/modulefiles/xjalienfs -type f -printf "%f\n" | tail -n1` + banner "Loading XJALIEN package $XJALIEN_LATEST" + eval "$(/cvmfs/alice.cern.ch/bin/alienv printenv xjalienfs::"$XJALIEN_LATEST")" +fi +if [ ! "$O2DPG_ROOT" ]; then + O2DPG_LATEST=`find /cvmfs/alice.cern.ch/el7-x86_64/Modules/modulefiles/O2DPG -type f -printf "%f\n" | tail -n1` + banner "Loading O2DPG package $O2DPG_LATEST" + eval "$(/cvmfs/alice.cern.ch/bin/alienv printenv O2DPG::"$O2DPG_LATEST")" +fi + +banner "Running workflow" + +ldd `which o2-sim` > ldd.log +ls > ls.log + +# collect some common information + +cat /proc/cpuinfo > cpuinfo.log +cat /proc/meminfo > meminfo.log + +# ----------- EXECUTE ACTUAL JOB ----------------------------- +# source the actual job script from the work dir +chmod +x ./alien_jobscript.sh +./alien_jobscript.sh + +# We need to exit for the ALIEN JOB HANDLER! +exit 0 diff --git a/Utilities/Tools/jobutils.sh b/Utilities/Tools/jobutils.sh new file mode 100644 index 0000000000000..84ae6552a26eb --- /dev/null +++ b/Utilities/Tools/jobutils.sh @@ -0,0 +1,362 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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: Sandro Wenzel + +# This file contains a couple of utility functions for reuse +# in shell job scripts (such as on the GRID). +# In order to use these functions in scripts, this file needs to be +# simply sourced into the target script. The script needs bash versions > 4 + +# TODOs: +# -harmonize use of bc/awk for calculations +# -harmonize coding style for variables + +o2_cleanup_shm_files() { + # check if we have lsof (otherwise we do nothing) + which lsof &> /dev/null + if [ "$?" = "0" ]; then + # find shared memory files **CURRENTLY IN USE** by FairMQ + USEDFILES=`lsof -u $(whoami) | grep -e \"/dev/shm/.*fmq\" | sed 's/.*\/dev/\/dev/g' | sort | uniq | tr '\n' ' '` + + echo "${USEDFILES}" + if [ ! "${USEDFILES}" ]; then + # in this case we can remove everything + COMMAND="find /dev/shm/ -user $(whoami) -name \"*fmq_*\" -delete 2> /dev/null" + else + # build exclusion list + for f in ${USEDFILES}; do + LOGICALOP="" + [ "${EXCLPATTERN}" ] && LOGICALOP="-o" + EXCLPATTERN="${EXCLPATTERN} ${LOGICALOP} -wholename ${f}" + done + COMMAND="find /dev/shm/ -user $(whoami) -type f -not \( ${EXCLPATTERN} \) -delete 2> /dev/null" + fi + eval "${COMMAND}" + else + echo "Can't do shared mem cleanup: lsof not found" + fi +} + +# Function to find out all the (recursive) child processes starting from a parent PID. +# The output includes the parent +childprocs() { + local parent=$1 + if [ ! "$2" ]; then + child_pid_list="" + fi + if [ "$parent" ] ; then + child_pid_list="$child_pid_list $parent" + for childpid in $(pgrep -P ${parent}); do + childprocs $childpid "nottoplevel" + done; + fi + # return via a string list (only if toplevel) + if [ ! "$2" ]; then + echo "${child_pid_list}" + fi +} + +taskwrapper_cleanup() { + MOTHERPID=$1 + SIGNAL=${2:-SIGTERM} + for p in $(childprocs ${MOTHERPID}); do + echo "killing child $p" + kill -s ${SIGNAL} $p 2> /dev/null + done + sleep 2 + # remove leftover shm files + o2_cleanup_shm_files +} + +taskwrapper_cleanup_handler() { + PID=$1 + SIGNAL=$2 + echo "CLEANUP HANDLER FOR PROCESS ${PID} AND SIGNAL ${SIGNAL}" + taskwrapper_cleanup ${PID} ${SIGNAL} + # I prefer to exit the current job completely + exit 1 +} + +# Function wrapping some process and asyncronously supervises and controls it. +# Main features provided at the moment are: +# - optional recording of walltime and memory consumption (time evolution) +# - optional recording of CPU utilization +# - Some job control and error detection (in particular for DPL workflows). +# If exceptions are found, all participating processes will be sent a termination signal. +# The rational behind this function is to be able to determine failing +# conditions early and prevent longtime hanging executables +# (until DPL offers signal handling and automatic shutdown) +# - possibility to provide user hooks for "start" and "failure" +# - possibility to skip (jump over) job alltogether +# - possibility to define timeout +# - possibility to control/limit the CPU load +taskwrapper() { + local logfile=$1 + shift 1 + local command="$*" + + STARTTIME=$SECONDS + + # launch the actual command in the background + echo "Launching task: ${command} &> $logfile &" + # the command might be a complex block: For the timing measurement below + # it is better to execute this as a script + SCRIPTNAME="${logfile}_tmp.sh" + echo "${command}" > ${SCRIPTNAME} + chmod +x ${SCRIPTNAME} + + # this gives some possibility to customize the wrapper + # and do some special task at the start. The hook takes 2 arguments: + # The original command and the logfile + if [ "${JOBUTILS_JOB_STARTHOOK}" ]; then + hook="${JOBUTILS_JOB_STARTHOOK} '$command' $logfile" + eval "${hook}" + fi + + # We offer the possibility to jump this stage/task when a "done" file is present. + # (this is mainly interesting for debugging in order to avoid going through all pipeline stages again) + # The feature should be used with care! To make this nice, a proper dependency chain and a checksum mechanism + # needs to be put into place. + if [ "${JOBUTILS_SKIPDONE}" ]; then + if [ -f "${logfile}_done" ]; then + echo "Skipping task since file ${logfile}_done found"; + [ ! "${JOBUTILS_KEEPJOBSCRIPT}" ] && rm ${SCRIPTNAME} 2> /dev/null + return 0 + fi + fi + [ -f "${logfile}_done" ] && rm "${logfile}"_done + + # the time command is non-standard on MacOS + if [ "$(uname)" == "Darwin" ]; then + GTIME=$(which gtime) + TIMECOMMAND=${GTIME:+"${GTIME} --output=${logfile}_time"} + else + TIMECOMMAND="/usr/bin/time --output=${logfile}_time" + fi + + # with or without memory monitoring ? + finalcommand="TIME=\"#walltime %e\" ${TIMECOMMAND} ./${SCRIPTNAME}" + if [[ "$(uname)" != "Darwin" && "${JOBUTILS_MONITORMEM}" ]]; then + finalcommand="TIME=\"#walltime %e\" ${O2_ROOT}/share/scripts/monitor-mem.sh ${TIMECOMMAND} './${SCRIPTNAME}'" + fi + echo "Running: ${finalcommand}" > ${logfile} + eval ${finalcommand} >> ${logfile} 2>&1 & #can't disown here since we want to retrieve exit status later on + + # THE NEXT PART IS THE SUPERVISION PART + # get the PID + PID=$! + # register signal handlers + trap "taskwrapper_cleanup_handler ${PID} SIGINT" SIGINT + trap "taskwrapper_cleanup_handler ${PID} SIGTERM" SIGTERM + + cpucounter=1 + NLOGICALCPUS=$(getNumberOfLogicalCPUCores) + + reduction_factor=1 + while [ 1 ]; do + # We don't like to see critical problems in the log file. + + # We need to grep on multitude of things: + # - all sorts of exceptions (may need to fine-tune) + # - segmentation violation + # - there was a crash + # - bus error (often occuring with shared mem) + pattern="-e \"xception\" \ + -e \"segmentation violation\" \ + -e \"error while setting up workflow\" \ + -e \"bus error\" \ + -e \"Assertion.*failed\" \ + -e \"There was a crash.\"" + + grepcommand="grep -H ${pattern} $logfile ${JOBUTILS_JOB_SUPERVISEDFILES} >> encountered_exceptions_list 2>/dev/null" + eval ${grepcommand} + + grepcommand="grep -h --count ${pattern} $logfile ${JOBUTILS_JOB_SUPERVISEDFILES} 2>/dev/null" + # using eval here since otherwise the pattern is translated to a + # a weirdly quoted stringlist + RC=$(eval ${grepcommand}) + + # if we see an exception we will bring down the DPL workflow + # after having given it some chance to shut-down itself + # basically --> send kill to all children + if [ "$RC" != "" -a "$RC" != "0" ]; then + echo "Detected critical problem in logfile $logfile" + + # this gives some possibility to customize the wrapper + # and do some special task at the start. The hook takes 2 arguments: + # The original command and the logfile + if [ "${JOBUTILS_JOB_FAILUREHOOK}" ]; then + hook="${JOBUTILS_JOB_FAILUREHOOK} '$command' $logfile" + eval "${hook}" + fi + + sleep 2 + + taskwrapper_cleanup ${PID} SIGKILL + + RC_ACUM=$((RC_ACUM+1)) + [ ! "${JOBUTILS_KEEPJOBSCRIPT}" ] && rm ${SCRIPTNAME} 2> /dev/null + return 1 + fi + + # check if command returned which may bring us out of the loop + ps -p $PID > /dev/null + [ $? == 1 ] && break + + if [ "${JOBUTILS_MONITORCPU}" ] || [ "${JOBUTILS_LIMITLOAD}" ]; then + # NOTE: The following section is "a bit" compute intensive and currently not optimized + # A careful evaluation of awk vs bc or other tools might be needed -- or a move to a more + # system oriented language/tool + + for p in $limitPIDs; do + wait ${p} + done + + # get some CPU usage statistics per process --> actual usage can be calculated thereafter + total=`awk 'BEGIN{s=0}/cpu /{for (i=1;i<=NF;i++) s+=$i;} END {print s}' /proc/stat` + previous_total=${current_total} + current_total=${total} + # quickly fetch the data + childpids=$(childprocs ${PID}) + + for p in $childpids; do + while read -r name utime stime; do + echo "${cpucounter} ${p} ${total} ${utime} ${stime} ${name}" >> ${logfile}_cpuusage + previous[$p]=${current[$p]} + current[$p]=${utime} + name[$p]=${name} + done <<<$(awk '//{print $2" "$14" "$15}' /proc/${p}/stat 2>/dev/null) + done + # do some calculations based on the data + totalCPU=0 # actual CPU load measured + totalCPU_unlimited=0 # extrapolated unlimited CPU load + line="" + for p in $childpids; do + C=${current[$p]} + P=${previous[$p]} + CT=${total} + PT=${previous_total} + # echo "${p} : current ${C} previous ${P} ${CT} ${PT}" + thisCPU[$p]=$(awk -v "c=${C}" -v "p=${P}" -v "ct=${CT}" -v "pt=${PT}" -v "ncpu=${NLOGICALCPUS}" 'BEGIN { print 100.*ncpu*(c-p)/(ct-pt); }') + line="${line} $p:${thisCPU[$p]}" + totalCPU=$(awk -v "t=${totalCPU}" -v "this=${thisCPU[$p]}" 'BEGIN { print (t + this); }') + previousfactor=1 + [ ${waslimited[$p]} ] && previousfactor=${reduction_factor} + totalCPU_unlimited=$(awk -v "t=${totalCPU_unlimited}" -v "this=${thisCPU[$p]}" -v f="${previousfactor}" 'BEGIN { print (t + this/f); }') + # echo "CPU last time window ${p} : ${thisCPU[$p]}" + done + + echo "${line}" + echo "${cpucounter} totalCPU = ${totalCPU} -- without limitation ${totalCPU_unlimited}" + # We can check if the total load is above a resource limit + # And take corrective actions if we extend by 10% + limitPIDs="" + unset waslimited + if (( $(echo "${totalCPU_unlimited} > 1.1*${JOBUTILS_LIMITLOAD}" | bc -l) )); then + # we reduce each pid proportionally for the time until the next check and record the reduction factor in place + oldreduction=${reduction_factor} + reduction_factor=$(awk -v limit="${JOBUTILS_LIMITLOAD}" -v cur="${totalCPU_unlimited}" 'BEGIN{ print limit/cur;}') + echo "APPLYING REDUCTION = ${reduction_factor}" + + for p in $childpids; do + cpulim=$(awk -v a="${thisCPU[${p}]}" -v newr="${reduction_factor}" -v oldr="${oldreduction}" 'BEGIN { r=(a/oldr)*newr; print r; if(r > 0.05) {exit 0;} exit 1; }') + if [ $? = "0" ]; then + # we only apply to jobs above a certain threshold + echo "Setting CPU lim for job ${p} / ${name[$p]} to ${cpulim}"; + + timeout ${JOBUTILS_WRAPPER_SLEEP} ${O2_ROOT}/share/scripts/cpulimit -l ${cpulim} -p ${p} > /dev/null 2> /dev/null & disown + proc=$! + limitPIDs="${limitPIDs} ${proc}" + waslimited[$p]=1 + fi + done + else + echo "RESETING REDUCTION = 1" + reduction_factor=1. + fi + let cpucounter=cpucounter+1 + fi + + # a good moment to check for jobs timeout (or other resources) + if [ "$JOBUTILS_JOB_TIMEOUT" ]; then + $(awk -v S="${SECONDS}" -v T="${JOBUTILS_JOB_TIMEOUT}" -v START="${STARTTIME}" 'BEGIN {if((S-START)>T){exit 1;} exit 0;}') + if [ "$?" = "1" ]; then + echo "task timeout reached .. killing all processes"; + taskwrapper_cleanup $PID SIGKILL + # call a more specialized hook for this?? + if [ "${JOBUTILS_JOB_FAILUREHOOK}" ]; then + hook="${JOBUTILS_JOB_FAILUREHOOK} '$command' $logfile" + eval "${hook}" + fi + return 1 + fi + fi + + # sleep for some time (can be customized for power user) + sleep ${JOBUTILS_WRAPPER_SLEEP:-10} + done + + # wait for PID and fetch return code + # ?? should directly exit here? + wait $PID + # return code + RC=$? + RC_ACUM=$((RC_ACUM+RC)) + if [ "${RC}" -eq "0" ]; then + # if return code 0 we mark this task as done + echo "Command \"${command}\" successfully finished." > "${logfile}"_done + echo "The presence of this file can be used to skip this command in future runs" >> "${logfile}"_done + echo "of the pipeline by setting the JOBUTILS_SKIPDONE environment variable." >> "${logfile}"_done + else + echo "command ${command} had nonzero exit code ${RC}" + fi + [ ! "${JOBUTILS_KEEPJOBSCRIPT}" ] && rm ${SCRIPTNAME} 2> /dev/null + + # deregister signal handlers + trap '' SIGINT + trap '' SIGTERM + + o2_cleanup_shm_files #--> better to register a general trap at EXIT + + # this gives some possibility to customize the wrapper + # and do some special task at the ordinary exit. The hook takes 3 arguments: + # - The original command + # - the logfile + # - the return code from the execution + if [ "${JOBUTILS_JOB_ENDHOOK}" ]; then + hook="${JOBUTILS_JOB_ENDHOOK} '$command' $logfile ${RC}" + eval "${hook}" + fi + + return ${RC} +} + +getNumberOfPhysicalCPUCores() { + if [ "$(uname)" == "Darwin" ]; then + CORESPERSOCKET=`system_profiler SPHardwareDataType | grep "Total Number of Cores:" | awk '{print $5}'` + SOCKETS=`system_profiler SPHardwareDataType | grep "Number of Processors:" | awk '{print $4}'` + else + # Do something under GNU/Linux platform + CORESPERSOCKET=`lscpu | grep "Core(s) per socket" | awk '{print $4}'` + SOCKETS=`lscpu | grep "Socket(s)" | awk '{print $2}'` + fi + N=`bc <<< "${CORESPERSOCKET}*${SOCKETS}"` + echo "${N}" +} + +getNumberOfLogicalCPUCores() { + if [ "$(uname)" == "Darwin" ]; then + echo $(sysctl -n hw.logicalcpu) + else + # Do something under GNU/Linux platform + echo $(grep "processor" /proc/cpuinfo | wc -l) + fi +} diff --git a/Utilities/Tools/monitor-mem.sh b/Utilities/Tools/monitor-mem.sh index ff416d87e0686..159bf83ca5a66 100755 --- a/Utilities/Tools/monitor-mem.sh +++ b/Utilities/Tools/monitor-mem.sh @@ -39,9 +39,13 @@ while [ 1 ] ; do ps -p $PID > /dev/null [ $? == 1 ] && echo "Job finished; Exiting " && break - sleep 0.005 + # take a measurement at some defined rate (can be customized) + sleep ${JOBUTILS_MONITORMEM_SLEEP:-0.005} done +# record return code of original command +wait ${PID} +RC=$? # print summary MAXMEM=`awk '/^[0-9]/{ for(i=1; i<=NF;i++) j+=$i; print j; j=0 }' ${memlogfile} | awk 'BEGIN {m = 0} //{if($1>m){m=$1}} END {print m}'` @@ -49,3 +53,5 @@ AVGMEM=`awk '/^[0-9]/{ for(i=1; i<=NF;i++) j+=$i; print j; j=0 }' ${memlogfile} echo "PROCESS MAX MEM = ${MAXMEM}" echo "PROCESS AVG MEM = ${AVGMEM}" + +exit ${RC} diff --git a/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTDataTypes.h b/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTDataTypes.h index f9bf1ce387c22..f618d117c388c 100644 --- a/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTDataTypes.h +++ b/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTDataTypes.h @@ -1579,12 +1579,16 @@ namespace std */ inline bool MatchExactly(const AliHLTComponentDataType& dt1, const AliHLTComponentDataType& dt2) { - for (int i = 0; i < kAliHLTComponentDataTypefIDsize; i++) - if (dt1.fID[i] != dt2.fID[i]) + for (int i = 0; i < kAliHLTComponentDataTypefIDsize; i++) { + if (dt1.fID[i] != dt2.fID[i]) { return false; - for (int i = 0; i < kAliHLTComponentDataTypefOriginSize; i++) - if (dt1.fOrigin[i] != dt2.fOrigin[i]) + } + } + for (int i = 0; i < kAliHLTComponentDataTypefOriginSize; i++) { + if (dt1.fOrigin[i] != dt2.fOrigin[i]) { return false; + } + } return true; } @@ -1595,10 +1599,12 @@ inline bool MatchExactly(const AliHLTComponentDataType& dt1, const AliHLTCompone */ inline bool operator==(const AliHLTComponentDataType& dt1, const AliHLTComponentDataType& dt2) { - if (MatchExactly(dt1, kAliHLTAllDataTypes)) + if (MatchExactly(dt1, kAliHLTAllDataTypes)) { return true; - if (MatchExactly(dt2, kAliHLTAllDataTypes)) + } + if (MatchExactly(dt2, kAliHLTAllDataTypes)) { return true; + } bool any1 = true, any2 = true, void1 = true, void2 = true, match = true; for (int i = 0; i < kAliHLTComponentDataTypefOriginSize; i++) { @@ -1607,8 +1613,9 @@ inline bool operator==(const AliHLTComponentDataType& dt1, const AliHLTComponent void1 &= (dt1.fOrigin[i] == kAliHLTDataOriginVoid[i]); void2 &= (dt2.fOrigin[i] == kAliHLTDataOriginVoid[i]); match &= dt1.fOrigin[i] == dt2.fOrigin[i]; - if (!(match || (any2 && !void1) || (any1 && !void2))) + if (!(match || (any2 && !void1) || (any1 && !void2))) { return false; + } } any1 = true, any2 = true, match = true; @@ -1618,8 +1625,9 @@ inline bool operator==(const AliHLTComponentDataType& dt1, const AliHLTComponent void1 &= (dt1.fID[i] == kAliHLTVoidDataTypeID[i]); void2 &= (dt2.fID[i] == kAliHLTVoidDataTypeID[i]); match &= dt1.fID[i] == dt2.fID[i]; - if (!(match || (any2 && !void1) || (any1 && !void2))) + if (!(match || (any2 && !void1) || (any1 && !void2))) { return false; + } } return true; } @@ -1639,8 +1647,9 @@ inline bool operator!=(const AliHLTComponentDataType& dt1, const AliHLTComponent inline AliHLTComponentDataType operator|(const AliHLTComponentDataType srcdt, const char origin[kAliHLTComponentDataTypefOriginSize]) { AliHLTComponentDataType dt = srcdt; - for (int i = 0; i < kAliHLTComponentDataTypefOriginSize; i++) + for (int i = 0; i < kAliHLTComponentDataTypefOriginSize; i++) { dt.fOrigin[i] = origin[i]; + } return dt; } @@ -1653,10 +1662,12 @@ inline AliHLTComponentDataType AliHLTComponentDataTypeInitializer(const char id[ { AliHLTComponentDataType dt = kAliHLTVoidDataType; int i = 0; - for (i = 0; i < kAliHLTComponentDataTypefIDsize && id[i] != 0; i++) + for (i = 0; i < kAliHLTComponentDataTypefIDsize && id[i] != 0; i++) { dt.fID[i] = id[i]; - for (i = 0; i < kAliHLTComponentDataTypefOriginSize && origin[i] != 0; i++) + } + for (i = 0; i < kAliHLTComponentDataTypefOriginSize && origin[i] != 0; i++) { dt.fOrigin[i] = origin[i]; + } return dt; } diff --git a/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTHOMERData.h b/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTHOMERData.h index 8d408e5c4e2eb..59c0ab41f93e3 100644 --- a/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTHOMERData.h +++ b/Utilities/aliceHLTwrapper/include/aliceHLTwrapper/AliHLTHOMERData.h @@ -220,8 +220,9 @@ class AliHLTHOMERBlockDescriptor void Initialize() { if (fHeader) { - for (unsigned ii = 0; ii < kCount_64b_Words; ii++) + for (unsigned ii = 0; ii < kCount_64b_Words; ii++) { ((homer_uint64*)fHeader)[ii] = (homer_uint64)0; + } ((homer_uint64*)fHeader)[kID_64b_Offset] = HOMER_BLOCK_DESCRIPTOR_TYPEID; ((homer_uint64*)fHeader)[kLength_64b_Offset] = GetHOMERBlockDescriptorSize(); ((homer_uint8*)fHeader)[kByteOrderAttribute_8b_Offset] = kHOMERNativeByteOrder; @@ -231,132 +232,157 @@ class AliHLTHOMERBlockDescriptor void SetByteOrder(homer_uint8 bo) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kByteOrderAttribute_8b_Offset] = bo; + } } homer_uint8 GetByteOrder() const { - if (fHeader) + if (fHeader) { return ((homer_uint8*)fHeader)[kByteOrderAttribute_8b_Offset]; + } return 0xFF; } void SetVersion(homer_uint8 v) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kVersionAttribute_8b_Offset] = v; + } } void SetID(homer_uint64 id) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kID_64b_Offset] = id; + } } void SetHeaderLength(homer_uint64 l) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kLength_64b_Offset] = l; + } } homer_uint64 GetHeaderLength() const { - if (fHeader) + if (fHeader) { return ((homer_uint64*)fHeader)[kLength_64b_Offset]; + } return 0; } void SetAlignment(homer_uint8 type, homer_uint8 align) const { - if (fHeader && type < 6) + if (fHeader && type < 6) { ((homer_uint8*)fHeader)[kAlignment_8b_StartOffset + type] = align; + } } void SetUInt64Alignment(homer_uint8 align) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kUInt64Alignment_8b_Offset] = align; + } } void SetUInt32Alignment(homer_uint8 align) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kUInt32Alignment_8b_Offset] = align; + } } void SetUInt16Alignment(homer_uint8 align) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kUInt16Alignment_8b_Offset] = align; + } } void SetUInt8Alignment(homer_uint8 align) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kUInt8Alignment_8b_Offset] = align; + } } void SetDoubleAlignment(homer_uint8 align) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kDoubleAlignment_8b_Offset] = align; + } } void SetFloatAlignment(homer_uint8 align) const { - if (fHeader) + if (fHeader) { ((homer_uint8*)fHeader)[kFloatAlignment_8b_Offset] = align; + } } void SetType(homer_uint64 t) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kType_64b_Offset] = t; + } } void SetSubType1(homer_uint64 st1) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kSubType1_64b_Offset] = st1; + } } void SetSubType2(homer_uint64 st2) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kSubType2_64b_Offset] = st2; + } } void SetBirth_s(homer_uint64 bs) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kBirth_s_64b_Offset] = bs; + } } void SetBirth_us(homer_uint64 bus) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kBirth_us_64b_Offset] = bus; + } } void SetProducerNode(homer_uint64 pn) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kProducerNode_64b_Offset] = pn; + } } void SetBlockOffset(homer_uint64 bo) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kOffset_64b_Offset] = bo; + } } homer_uint64 GetBlockOffset() const { - if (fHeader) + if (fHeader) { return ((homer_uint64*)fHeader)[kOffset_64b_Offset]; + } return 0; } void SetBlockSize(homer_uint64 bs) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kSize_64b_Offset] = bs; + } } homer_uint64 GetBlockSize() const { - if (fHeader) + if (fHeader) { return ((homer_uint64*)fHeader)[kSize_64b_Offset]; + } return 0; } void SetStatusFlags(homer_uint64 bs) const { - if (fHeader) + if (fHeader) { ((homer_uint64*)fHeader)[kStatusFlags_64b_Offset] = bs; + } } homer_uint64 GetStatusFlags() const { - if (fHeader) + if (fHeader) { return ((homer_uint64*)fHeader)[kStatusFlags_64b_Offset]; + } return 0; } diff --git a/Utilities/aliceHLTwrapper/src/Component.cxx b/Utilities/aliceHLTwrapper/src/Component.cxx index 41b8a6eaaf9b8..115faa1b3f765 100644 --- a/Utilities/aliceHLTwrapper/src/Component.cxx +++ b/Utilities/aliceHLTwrapper/src/Component.cxx @@ -120,7 +120,9 @@ int Component::init(int argc, char** argv) bpo::store(bpo::parse_command_line(argc, argv, od), varmap); for (int option = 0; option < OptionKeyLast; ++option) { - if (varmap.count(OptionKeys[option]) == 0) continue; + if (varmap.count(OptionKeys[option]) == 0) { + continue; + } switch (option) { case OptionKeyLibrary: break; case OptionKeyComponent: break; @@ -186,8 +188,9 @@ int Component::init(int argc, char** argv) mpSystem = iface.release(); // load the component library - if ((iResult = mpSystem->loadLibrary(varmap[OptionKeys[OptionKeyLibrary]].as<string>().c_str())) != 0) + if ((iResult = mpSystem->loadLibrary(varmap[OptionKeys[OptionKeyLibrary]].as<string>().c_str())) != 0) { return iResult > 0 ? -iResult : iResult; + } // chop the parameter string in order to provide parameters in the argc/argv format vector<const char*> parameters; @@ -198,9 +201,13 @@ int Component::init(int argc, char** argv) char* iterator = parameterBuffer.get(); parameters.emplace_back(iterator); for (; *iterator != 0; iterator++) { - if (*iterator != ' ') continue; + if (*iterator != ' ') { + continue; + } *iterator = 0; // separate strings - if (*(iterator + 1) != ' ' && *(iterator + 1) != 0) parameters.emplace_back(iterator + 1); + if (*(iterator + 1) != ' ' && *(iterator + 1) != 0) { + parameters.emplace_back(iterator + 1); + } } } @@ -222,7 +229,9 @@ int Component::init(int argc, char** argv) int Component::process(vector<MessageFormat::BufferDesc_t>& dataArray, cballoc_signal_t* cbAllocate) { - if (!mpSystem) return -ENOSYS; + if (!mpSystem) { + return -ENOSYS; + } int iResult = 0; unsigned outputBufferSize = 0; @@ -294,9 +303,13 @@ int Component::process(vector<MessageFormat::BufferDesc_t>& dataArray, outputBlockCnt = 0; // TODO: check if that is working with the corresponding allocation method of the // component environment - if (pOutputBlocks) delete[] pOutputBlocks; + if (pOutputBlocks) { + delete[] pOutputBlocks; + } pOutputBlocks = nullptr; - if (pEventDoneData) delete pEventDoneData; + if (pEventDoneData) { + delete pEventDoneData; + } pEventDoneData = nullptr; iResult = mpSystem->processEvent(mProcessor, &evtData, &inputBlocks[0], &trigData, @@ -336,7 +349,9 @@ int Component::process(vector<MessageFormat::BufferDesc_t>& dataArray, AliHLTComponentBlockData* pFiltered = pOutputBlocks; for (unsigned blockIndex = 0; blockIndex < outputBlockCnt; blockIndex++, pOutputBlock++) { // filter special data blocks - if (pOutputBlock->fDataType == kDataTypeEvent) continue; + if (pOutputBlock->fDataType == kDataTypeEvent) { + continue; + } // block descriptors without any attached payload are propagated bool bValid = pOutputBlock->fSize == 0; @@ -387,9 +402,13 @@ int Component::process(vector<MessageFormat::BufferDesc_t>& dataArray, // until released. inputBlocks.clear(); outputBlockCnt = 0; - if (pOutputBlocks) delete[] pOutputBlocks; + if (pOutputBlocks) { + delete[] pOutputBlocks; + } pOutputBlocks = nullptr; - if (pEventDoneData) delete pEventDoneData; + if (pEventDoneData) { + delete pEventDoneData; + } pEventDoneData = nullptr; return -iResult; diff --git a/Utilities/aliceHLTwrapper/src/EventSampler.cxx b/Utilities/aliceHLTwrapper/src/EventSampler.cxx index 1c3009401375a..46a4b7f601a79 100644 --- a/Utilities/aliceHLTwrapper/src/EventSampler.cxx +++ b/Utilities/aliceHLTwrapper/src/EventSampler.cxx @@ -115,7 +115,7 @@ void EventSampler::Run() /// inherited from FairMQDevice int iResult=0; - boost::thread samplerThread(boost::bind(&EventSampler::samplerLoop, this)); + boost::thread samplerThread([this] { EventSampler::samplerLoop(); }); unique_ptr<FairMQPoller> poller(fTransportFactory->CreatePoller(fChannels["data-in"])); @@ -216,7 +216,9 @@ void EventSampler::samplerLoop() unsigned initialDelayInSeconds=mInitialDelay/1000; unsigned initialDelayInUSeconds=mInitialDelay%1000; unsigned eventPeriodInSeconds=mEventPeriod/1000000; - if (initialDelayInSeconds>0) sleep(initialDelayInSeconds); + if (initialDelayInSeconds > 0) { + sleep(initialDelayInSeconds); + } usleep(initialDelayInUSeconds); unique_ptr<FairMQMessage> msg(fTransportFactory->CreateMessage()); @@ -253,7 +255,10 @@ void EventSampler::samplerLoop() } mNEvents++; - if (eventPeriodInSeconds>0) sleep(eventPeriodInSeconds); - else usleep(mEventPeriod); + if (eventPeriodInSeconds > 0) { + sleep(eventPeriodInSeconds); + } else { + usleep(mEventPeriod); + } } } diff --git a/Utilities/aliceHLTwrapper/src/HOMERFactory.cxx b/Utilities/aliceHLTwrapper/src/HOMERFactory.cxx index 9b75e367754af..f9c70a1da1ad6 100644 --- a/Utilities/aliceHLTwrapper/src/HOMERFactory.cxx +++ b/Utilities/aliceHLTwrapper/src/HOMERFactory.cxx @@ -88,8 +88,9 @@ HOMERFactory::~HOMERFactory() = default; AliHLTHOMERReader* HOMERFactory::OpenReader(const char* hostname, unsigned short port) { // Open Reader instance for host - if (sLibraryStatus < 0) + if (sLibraryStatus < 0) { return nullptr; + } sLibraryStatus = LoadHOMERLibrary(); if (sLibraryStatus <= 0) { @@ -107,8 +108,9 @@ AliHLTHOMERReader* HOMERFactory::OpenReader(const char* hostname, unsigned short AliHLTHOMERReader* HOMERFactory::OpenReader(unsigned int tcpCnt, const char** hostnames, unsigned short* ports) { // Open Reader instance for a list of hosts - if (sLibraryStatus < 0) + if (sLibraryStatus < 0) { return nullptr; + } sLibraryStatus = LoadHOMERLibrary(); if (sLibraryStatus <= 0) { @@ -127,8 +129,9 @@ AliHLTHOMERReader* HOMERFactory::OpenReader(unsigned int tcpCnt, const char** ho AliHLTHOMERReader* HOMERFactory::OpenReaderBuffer(const AliHLTUInt8_t* pBuffer, int size) { // Open Reader instance for a data buffer - if (sLibraryStatus < 0) + if (sLibraryStatus < 0) { return nullptr; + } sLibraryStatus = LoadHOMERLibrary(); if (sLibraryStatus <= 0) { @@ -148,8 +151,9 @@ int HOMERFactory::DeleteReader(AliHLTHOMERReader* pReader) // delete a reader // the actual deletion function is inside the HOMER library - if (sLibraryStatus < 0) + if (sLibraryStatus < 0) { return sLibraryStatus; + } sLibraryStatus = LoadHOMERLibrary(); if (sLibraryStatus <= 0) { @@ -166,8 +170,9 @@ int HOMERFactory::DeleteReader(AliHLTHOMERReader* pReader) AliHLTHOMERWriter* HOMERFactory::OpenWriter() { // open a Writer instance - if (sLibraryStatus < 0) + if (sLibraryStatus < 0) { return nullptr; + } sLibraryStatus = LoadHOMERLibrary(); if (sLibraryStatus <= 0) { @@ -185,8 +190,9 @@ AliHLTHOMERWriter* HOMERFactory::OpenWriter() int HOMERFactory::DeleteWriter(AliHLTHOMERWriter* pWriter) { // see header file for class documentation - if (sLibraryStatus < 0) + if (sLibraryStatus < 0) { return sLibraryStatus; + } sLibraryStatus = LoadHOMERLibrary(); if (sLibraryStatus <= 0) { @@ -262,15 +268,17 @@ int HOMERFactory::UnloadHOMERLibrary() const char** library = &sLibraries[0]; int* refcount = &sLibRefCount[0]; while (*library != nullptr) { - if (strcmp(*library, mLoadedLib) == 0) + if (strcmp(*library, mLoadedLib) == 0) { break; + } ++library; ++refcount; } // Decrease the reference count and remove the library if it is zero. - if (*refcount >= 0) + if (*refcount >= 0) { --(*refcount); + } if (*refcount == 0) { /* Matthias 2014-05-08 that part of the code has been disabled for the ROOT-independent version. TODO: dlopen is maintaining an internal reference count, which also makes the diff --git a/Utilities/aliceHLTwrapper/src/MessageFormat.cxx b/Utilities/aliceHLTwrapper/src/MessageFormat.cxx index ca6ef9c62589f..6d383f068ae9f 100644 --- a/Utilities/aliceHLTwrapper/src/MessageFormat.cxx +++ b/Utilities/aliceHLTwrapper/src/MessageFormat.cxx @@ -58,8 +58,9 @@ MessageFormat::MessageFormat() MessageFormat::~MessageFormat() { - if (mpFactory) + if (mpFactory) { delete mpFactory; + } mpFactory = nullptr; } @@ -144,9 +145,9 @@ int MessageFormat::addMessages(const std::vector<BufferDesc_t>& list) if (data.mSize > 0) { unsigned nofEventHeaders = mListEvtData.size(); int result = addMessage(data.mP, data.mSize); - if (result >= 0) + if (result >= 0) { totalCount += result; - else { + } else { cerr << "warning: no valid data blocks in message " << i << endl; } } else { @@ -161,8 +162,9 @@ int MessageFormat::readBlockSequence(uint8_t* buffer, unsigned size, { // read a sequence of blocks consisting of AliHLTComponentBlockData followed by payload // from a buffer - if (buffer == nullptr) + if (buffer == nullptr) { return 0; + } unsigned position = 0; std::vector<BlockDescriptor> input; while (position + sizeof(AliHLTComponentBlockData) < size) { @@ -197,13 +199,16 @@ int MessageFormat::readHOMERFormat(uint8_t* buffer, unsigned size, std::vector<BlockDescriptor>& descriptorList) const { // read message payload in HOMER format - if (mpFactory == nullptr) + if (mpFactory == nullptr) { const_cast<MessageFormat*>(this)->mpFactory = new o2::alice_hlt::HOMERFactory; - if (buffer == nullptr || mpFactory == nullptr) + } + if (buffer == nullptr || mpFactory == nullptr) { return -EINVAL; + } unique_ptr<AliHLTHOMERReader> reader(mpFactory->OpenReaderBuffer(buffer, size)); - if (reader.get() == nullptr) + if (reader.get() == nullptr) { return -ENOMEM; + } unsigned nofBlocks = 0; if (reader->ReadNextEvent() == 0) { @@ -377,14 +382,16 @@ std::vector<MessageFormat::BufferDesc_t> MessageFormat::createMessages(const Ali pTarget = MakeTarget(msgSize, position, cbAllocate); memcpy(pTarget, headerMessage.data(), msgSize); mMessages.emplace_back(pTarget, msgSize); - if (cbAllocate == nullptr) + if (cbAllocate == nullptr) { position += msgSize; + } msgSize = dh.payloadSize; pTarget = MakeTarget(msgSize, position, cbAllocate); memcpy(pTarget, &hbfPayload, msgSize); mMessages.emplace_back(pTarget, msgSize); - if (cbAllocate == nullptr) + if (cbAllocate == nullptr) { position += msgSize; + } } unsigned msgSize = 0; do { @@ -442,8 +449,9 @@ std::vector<MessageFormat::BufferDesc_t> MessageFormat::createMessages(const Ali if (mOutputMode == kOutputModeMultiPart) { // send one descriptor per block back to device mMessages.emplace_back(pTarget, offset); - if (cbAllocate == nullptr) + if (cbAllocate == nullptr) { position += offset; + } offset = 0; } else if (mOutputMode == kOutputModeO2) { o2::header::DataHeader dh; @@ -454,8 +462,9 @@ std::vector<MessageFormat::BufferDesc_t> MessageFormat::createMessages(const Ali memcpy(pTarget, &dh, sizeof(o2::header::DataHeader)); offset += sizeof(o2::header::DataHeader); mMessages.emplace_back(pTarget, offset); - if (cbAllocate == nullptr) + if (cbAllocate == nullptr) { position += offset; + } offset = 0; if (mHeartbeatHeader.headerWord == 0) { // no heartbeat information availalbe, send the buffer @@ -472,8 +481,9 @@ std::vector<MessageFormat::BufferDesc_t> MessageFormat::createMessages(const Ali memcpy(pTarget + offset, &mHeartbeatTrailer, sizeof(mHeartbeatTrailer)); offset += sizeof(mHeartbeatTrailer); mMessages.emplace_back(pTarget, offset); - if (cbAllocate == nullptr) + if (cbAllocate == nullptr) { position += offset; + } offset = 0; } } @@ -518,13 +528,16 @@ AliHLTHOMERWriter* MessageFormat::createHOMERFormat(const AliHLTComponentBlockDa { // send data blocks in HOMER format in one message int iResult = 0; - if (mpFactory == nullptr) + if (mpFactory == nullptr) { const_cast<MessageFormat*>(this)->mpFactory = new o2::alice_hlt::HOMERFactory; - if (!mpFactory) + } + if (!mpFactory) { return nullptr; + } unique_ptr<AliHLTHOMERWriter> writer(mpFactory->OpenWriter()); - if (writer.get() == nullptr) + if (writer.get() == nullptr) { return nullptr; + } homer_uint64 homerHeader[kCount_64b_Words]; HOMERBlockDescriptor homerDescriptor(homerHeader); diff --git a/Utilities/aliceHLTwrapper/src/SystemInterface.cxx b/Utilities/aliceHLTwrapper/src/SystemInterface.cxx index da2a99e531f58..4026f55be909d 100644 --- a/Utilities/aliceHLTwrapper/src/SystemInterface.cxx +++ b/Utilities/aliceHLTwrapper/src/SystemInterface.cxx @@ -169,23 +169,26 @@ int SystemInterface::releaseSystem() bookkeeping of loaded libraries and unloading them before releasing the system? */ int iResult = 0; - if (mpAliHLTExtFctDeinitSystem) + if (mpAliHLTExtFctDeinitSystem) { iResult = (*mpAliHLTExtFctDeinitSystem)(); + } clear(); return iResult; } int SystemInterface::loadLibrary(const char* libname) { - if (!mpAliHLTExtFctLoadLibrary) + if (!mpAliHLTExtFctLoadLibrary) { return -ENOSYS; + } return (*mpAliHLTExtFctLoadLibrary)(libname); } int SystemInterface::unloadLibrary(const char* libname) { - if (!mpAliHLTExtFctUnloadLibrary) + if (!mpAliHLTExtFctUnloadLibrary) { return -ENOSYS; + } return (*mpAliHLTExtFctUnloadLibrary)(libname); } @@ -196,15 +199,17 @@ int SystemInterface::createComponent(const char* componentId, AliHLTComponentHandle* handle, const char* description) { - if (!mpAliHLTExtFctCreateComponent) + if (!mpAliHLTExtFctCreateComponent) { return -ENOSYS; + } return (*mpAliHLTExtFctCreateComponent)(componentId, environParam, argc, argv, handle, description); } int SystemInterface::destroyComponent(AliHLTComponentHandle handle) { - if (!mpAliHLTExtFctDestroyComponent) + if (!mpAliHLTExtFctDestroyComponent) { return -ENOSYS; + } return (*mpAliHLTExtFctDestroyComponent)(handle); } @@ -215,24 +220,27 @@ int SystemInterface::processEvent(AliHLTComponentHandle handle, AliHLTUInt32_t* outputBlockCnt, AliHLTComponentBlockData** outputBlocks, AliHLTComponentEventDoneData** edd) { - if (!mpAliHLTExtFctProcessEvent) + if (!mpAliHLTExtFctProcessEvent) { return -ENOSYS; + } return (*mpAliHLTExtFctProcessEvent)(handle, evtData, blocks, trigData, outputPtr, size, outputBlockCnt, outputBlocks, edd); } int SystemInterface::getOutputDataType(AliHLTComponentHandle handle, AliHLTComponentDataType* dataType) { - if (!mpAliHLTExtFctGetOutputDataType) + if (!mpAliHLTExtFctGetOutputDataType) { return -ENOSYS; + } return (*mpAliHLTExtFctGetOutputDataType)(handle, dataType); } int SystemInterface::getOutputSize(AliHLTComponentHandle handle, unsigned long* constEventBase, unsigned long* constBlockBase, double* inputBlockMultiplier) { - if (!mpAliHLTExtFctGetOutputSize) + if (!mpAliHLTExtFctGetOutputSize) { return -ENOSYS; + } return (*mpAliHLTExtFctGetOutputSize)(handle, constEventBase, constEventBase, inputBlockMultiplier); } @@ -264,7 +272,8 @@ void* SystemInterface::alloc(void* /*param*/, unsigned long size) void SystemInterface::dealloc(void* buffer, unsigned long /*size*/) { // deallocate memory - if (buffer == nullptr) + if (buffer == nullptr) { return; + } free(buffer); } diff --git a/Utilities/aliceHLTwrapper/src/WrapperDevice.cxx b/Utilities/aliceHLTwrapper/src/WrapperDevice.cxx index 6ad1f5155bee5..7ba026f7ba714 100644 --- a/Utilities/aliceHLTwrapper/src/WrapperDevice.cxx +++ b/Utilities/aliceHLTwrapper/src/WrapperDevice.cxx @@ -80,8 +80,9 @@ void WrapperDevice::InitTask() int iResult = 0; std::unique_ptr<Component> component(new o2::alice_hlt::Component); - if (!component.get()) + if (!component.get()) { return /*-ENOMEM*/; + } // loop over program options, check if the option was used and // add it together with the parameter to the argument vector. @@ -192,15 +193,17 @@ void WrapperDevice::Run() } } } - if (receivedAtLeastOneMessage) + if (receivedAtLeastOneMessage) { nReadCycles++; + } if (inputsReceived < numInputs) { continue; } mNSamples++; mTotalReadCycles += nReadCycles; - if (mMaxReadCycles < 0 || mMaxReadCycles < nReadCycles) + if (mMaxReadCycles < 0 || mMaxReadCycles < nReadCycles) { mMaxReadCycles = nReadCycles; + } // if (nReadCycles>1) { // LOG(INFO) << "------ recieved complete Msg from " << numInputs << " input(s) after " << nReadCycles << " read cycles" ; // } @@ -210,10 +213,12 @@ void WrapperDevice::Run() if (mLastSampleTime >= 0) { int sampleTimeDiff = duration.count() - mLastSampleTime; - if (mMinTimeBetweenSample < 0 || sampleTimeDiff < mMinTimeBetweenSample) + if (mMinTimeBetweenSample < 0 || sampleTimeDiff < mMinTimeBetweenSample) { mMinTimeBetweenSample = sampleTimeDiff; - if (mMaxTimeBetweenSample < 0 || sampleTimeDiff > mMaxTimeBetweenSample) + } + if (mMaxTimeBetweenSample < 0 || sampleTimeDiff > mMaxTimeBetweenSample) { mMaxTimeBetweenSample = sampleTimeDiff; + } } mLastSampleTime = duration.count(); if (duration.count() - mLastCalcTime > 1000) { diff --git a/Utilities/aliceHLTwrapper/src/runComponent.cxx b/Utilities/aliceHLTwrapper/src/runComponent.cxx index 4ed7636cab512..17d07cc37d61f 100644 --- a/Utilities/aliceHLTwrapper/src/runComponent.cxx +++ b/Utilities/aliceHLTwrapper/src/runComponent.cxx @@ -59,10 +59,11 @@ int main(int argc, char** argv) if (arg[1] == 'i' || arg[1] == 'o') { if (i + 1 >= argc) { cerr << "missing file name for option " << arg << endl; - } else if (arg[1] == 'i') + } else if (arg[1] == 'i') { inputFileName = argv[i + 1]; - else + } else { outputFileName = argv[i + 1]; + } break; } } @@ -101,11 +102,13 @@ int main(int argc, char** argv) if ((iResult = component.process(blockData)) < 0) { cerr << "error: init failed with " << iResult << endl; } - if (inputBuffer) + if (inputBuffer) { delete[] inputBuffer; + } inputBuffer = nullptr; - if (iResult < 0) + if (iResult < 0) { return -iResult; + } // for now, only the first buffer is written if (blockData.size() > 0) { diff --git a/Utilities/aliceHLTwrapper/test/testMessageFormat.cxx b/Utilities/aliceHLTwrapper/test/testMessageFormat.cxx index b28091e839e35..83681cbc2d117 100644 --- a/Utilities/aliceHLTwrapper/test/testMessageFormat.cxx +++ b/Utilities/aliceHLTwrapper/test/testMessageFormat.cxx @@ -50,8 +50,9 @@ BOOST_AUTO_TEST_CASE(test_createMessagesModeMultiPart) for (auto& desc : dataDescriptors) { totalPayloadSize += desc.fSize; hexDump("HLT data descriptor", &desc, sizeof(desc)); - if (!desc.fPtr) + if (!desc.fPtr) { continue; + } hexDump(" data payload", (char*)desc.fPtr + desc.fOffset, desc.fSize); } @@ -100,8 +101,9 @@ BOOST_AUTO_TEST_CASE(test_createMessagesModeSequence) for (auto& desc : dataDescriptors) { totalPayloadSize += desc.fSize; hexDump("HLT data descriptor", &desc, sizeof(desc)); - if (!desc.fPtr) + if (!desc.fPtr) { continue; + } hexDump(" data payload", (char*)desc.fPtr + desc.fOffset, desc.fSize); } @@ -142,8 +144,9 @@ BOOST_AUTO_TEST_CASE(test_createMessagesModeO2) for (auto& desc : dataDescriptors) { totalPayloadSize += desc.fSize; hexDump("HLT data descriptor", &desc, sizeof(desc)); - if (!desc.fPtr) + if (!desc.fPtr) { continue; + } hexDump(" data payload", (char*)desc.fPtr + desc.fOffset, desc.fSize); } @@ -173,8 +176,9 @@ BOOST_AUTO_TEST_CASE(test_createMessagesModeO2) dataidx = 0; for (auto& desc : descriptors) { hexDump("Readback: HLT data descriptor", &desc, sizeof(desc)); - if (!desc.fPtr) + if (!desc.fPtr) { continue; + } const char* data = (char*)desc.fPtr + desc.fOffset; hexDump(" data payload", data, desc.fSize); BOOST_CHECK(dataFields[dataidx++] == data); @@ -226,8 +230,9 @@ BOOST_AUTO_TEST_CASE(test_createHeartbeatFrame) for (auto& desc : dataDescriptors) { totalPayloadSize += desc.fSize; hexDump("HLT data descriptor", &desc, sizeof(desc)); - if (!desc.fPtr) + if (!desc.fPtr) { continue; + } hexDump(" data payload", (char*)desc.fPtr + desc.fOffset, desc.fSize); } diff --git a/Utilities/hough/CMakeLists.txt b/Utilities/hough/CMakeLists.txt deleted file mode 100644 index 12c3adcc99d92..0000000000000 --- a/Utilities/hough/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# 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". -# -# See http://alice-o2.web.cern.ch/license for full licensing information. -# -# In applying this license CERN does 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(MODULE_NAME "hough") - -o2_setup(NAME ${MODULE_NAME}) - -set(Exe_Names runHough) - -set(Exe_Source runHough.cxx) - -set(BUCKET_NAME hough_bucket) - -list(LENGTH Exe_Names _length) -math(EXPR _length ${_length}-1) - -foreach(_file RANGE 0 ${_length}) - list(GET Exe_Names ${_file} _name) - list(GET Exe_Source ${_file} _src) - o2_generate_executable(EXE_NAME - ${_name} - SOURCES - ${_src} - BUCKET_NAME - ${BUCKET_NAME}) -endforeach(_file RANGE 0 ${_length}) diff --git a/Utilities/hough/README.md b/Utilities/hough/README.md deleted file mode 100644 index 3d8f44e8ccb43..0000000000000 --- a/Utilities/hough/README.md +++ /dev/null @@ -1,46 +0,0 @@ -<!-- doxy -\page refUtilitieshough Hough Transform -/doxy --> - -# Hough Transform - -This is the groundwork for the Hough Transform algorithm implementation. The runHough executable takes as an argument an event number (i.e. runHough 032) and for the given event it loads all clusters from the corresponding data files. - -### Step by Step installation and execution - -1. The runHough executable depends on the AliRoot HLT libraries. For this, an optional dependency to AliRoot has been added to the AliceO2 framework. To build the executable, the path to the AliRoot installation must be given at configuration time. For example: - - cmake -DCMAKE_INSTALL_PREFIX:PATH=.. -DCMAKE_CXX_FLAGS="-std=c++11" .. -DALIROOT="/opt/alice/external/AliRoot" - -It is important that AliRoot, FairRoot and AliceO2 have been built against the same version of ROOT. To ensure that the prerequisite packages were compiled and installed correctly, the alfaconfig.sh script that is included as part of the FairSoft installation can be used. - -2. Raw data files should be retrieved from AliEn for a given run. The implementation can be tested using the raw files corresponding to run 167808: - - alien-token-init <username> - aliensh - - cd /alice/data/2011/LHC11h/000167808/raw/ - cp 11000167808000.10.root file://tmp - exit - - mv /tmp/11000167808000.10.root raw.root - -Then, the necessary scripts to perform the clusterization for the data should be coppied to the current directory: - - cp ${AliRoot}/HLT/exa/recraw-local.C . - cp ${AliRoot}/HLT/exa/EnableHLTInGRP.C . - cp ${AliceO2}devices/aliceHLTwrapper/macros/hltConfigurations.C . - -Finally, the following commands should executed: - - aliroot -b -q -l hltConfigurations.C recraw-local.C'("raw.root", "raw://", 0, 0, "HLT TPC", "loglevel=0x79 chains=cluster-collection", "local://./OCDB")' - - aliroot -b -q -l EnableHLTInGRP.C'(167808, "local://./OCDB", "local://./OCDB")' - rm galice.root QA.root - aliroot -b -q -l hltConfigurations.C recraw-local.C'("raw.root", "local://OCDB", -1, -1, "HLT", "loglevel=0x79 chains=cluster-collection")' 2>&1 | tee cluster-collection.log - -The result should be a directory, called "emulated-tpc-clusters" that will be the input of runHough. By executing - - runHough 032 - -the executable will load all the clusters from the emulated-tpc-clusters/event032 subdirectory. After the execution, a graphics file "clusters.pdf" will be created in the current directory depicting the coordinates of the loaded clusters. diff --git a/Utilities/hough/runHough.cxx b/Utilities/hough/runHough.cxx deleted file mode 100644 index 5c767e98bcf60..0000000000000 --- a/Utilities/hough/runHough.cxx +++ /dev/null @@ -1,180 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 runHough.cxx -/// \brief Implementation of a cluster loader -/// \author Charis Kouzinopoulos - -#include "AliHLTTPCTrackGeometry.h" -#include "AliHLTTPCClusterDataFormat.h" -#include "AliHLTTPCSpacePointContainer.h" -#include "AliHLTComponent.h" -#include "AliHLTTPCDefinitions.h" - -#include "TCanvas.h" -#include "TGraph2D.h" - -#include "boost/filesystem.hpp" - -#include <sstream> - -std::unique_ptr<AliHLTTPCSpacePointContainer> spacepoints; -vector<float> clusterCoordinates; - -void drawData(int totalNumberOfClusters, std::string dataFilename) -{ - TCanvas* c1 = new TCanvas("c1", dataFilename.c_str(), 0, 0, 800, 600); - TGraph2D* dt = new TGraph2D(10000); - - for (Int_t i = 0; i < totalNumberOfClusters; i++) { - dt->SetPoint(i, clusterCoordinates[i * 4 + 1], clusterCoordinates[i * 4 + 2], clusterCoordinates[i * 4 + 3]); - } - - // Draw with colored dots - dt->SetMarkerStyle(1); - dt->Draw("pcol"); - - c1->Print("clusters.pdf"); -} - -void printData(int totalNumberOfClusters) -{ - cout << "Cluster ID" << setw(13) << "X coordinate" << setw(13) << "Y coordinate" << setw(13) << "Z coordinate" - << endl; - - for (int i = 0; i < totalNumberOfClusters; i++) { - cout << (AliHLTUInt32_t)clusterCoordinates[i * 4] << setw(13) << clusterCoordinates[i * 4 + 1] << setw(13) - << clusterCoordinates[i * 4 + 2] << setw(13) << clusterCoordinates[i * 4 + 3] << endl; - } -} - -void addDataToCoordinatesVector(AliHLTUInt32_t clusterID, float XCoordinate, float YCoordinate, float ZCoordinate) -{ - clusterCoordinates.push_back((float)clusterID); - clusterCoordinates.push_back(XCoordinate); - clusterCoordinates.push_back(YCoordinate); - clusterCoordinates.push_back(ZCoordinate); -} - -int processData(std::string dataPath, std::string dataType, std::string dataOrigin) -{ - // Open data file for reading - std::ifstream inputData(dataPath.c_str(), std::ifstream::binary); - if (!inputData) { - std::cerr << "Error, cluster data file " << dataPath << " could not be accessed" << endl; - std::exit(1); - } - - // Get length of file - inputData.seekg(0, inputData.end); - int dataLength = inputData.tellg(); - inputData.seekg(0, inputData.beg); - - // Allocate memory and read file to memory - char* inputBuffer = new char[dataLength]; - inputData.read(inputBuffer, dataLength); - inputData.close(); - - // Retrieve the TPC slice and partition from the filename - std::string currentSliceString(dataPath, dataPath.length() - 6, 2); - std::string currentPartitionString(dataPath, dataPath.length() - 2, 2); - - AliHLTUInt8_t currentSlice = std::stoul(currentSliceString, nullptr, 16); - AliHLTUInt8_t currentPartition = std::stoul(currentPartitionString, nullptr, 16); - - // Initialize a cluster point collection - spacepoints = std::unique_ptr<AliHLTTPCSpacePointContainer>(new AliHLTTPCSpacePointContainer); - if (!spacepoints.get()) { - std::cerr << "Error, could not create a space point collection" << endl; - std::exit(1); - } - - // Create an AliHLTComponentBlockData object, fill it with default values and then set its pointer to the data buffer - AliHLTComponentBlockData bd; - AliHLTComponent::FillBlockData(bd); - bd.fPtr = inputBuffer; - bd.fSize = dataLength; - // bd.fDataType=kAliHLTVoidDataType; - AliHLTComponent::SetDataType(bd.fDataType, dataType.c_str(), dataOrigin.c_str()); - bd.fSpecification = kAliHLTVoidDataSpec; - - // Set slice and partition - AliHLTTPCDefinitions::EncodeDataSpecification(currentSlice, currentSlice, currentPartition, currentPartition); - - // Add the AliHLTComponentBlockData object to AliHLTTPCSpacePointContainer - int numberOfClusters = spacepoints->AddInputBlock(&bd); - - // cout << *spacepoints << endl; - - // Retrieve the cluster information from AliHLTTPCSpacePointContainer - std::vector<AliHLTUInt32_t> clusterIDs; - spacepoints->GetClusterIDs(clusterIDs); - - // Append the cluster IDs and their X, Y and Z coordinates to the clusterCoordinates vector - for (vector<AliHLTUInt32_t>::const_iterator element = clusterIDs.begin(); element != clusterIDs.end(); element++) { - AliHLTUInt32_t clusterID = *element; - - addDataToCoordinatesVector(clusterID, spacepoints->GetX(clusterID), spacepoints->GetY(clusterID), - spacepoints->GetZ(clusterID)); - } - - // De-allocate memory space - if (inputBuffer) { - delete[] inputBuffer; - } - inputBuffer = NULL; - - return numberOfClusters; -} - -int main(int argc, char** argv) -{ - if (argc != 2) { - std::cerr << "Usage: " << argv[0] << " <event number>" << endl; - std::exit(1); - } - - // Create data path - std::string dataFilename = "emulated-tpc-clusters/event"; - dataFilename += argv[1]; - - boost::filesystem::path dataPath(dataFilename); - boost::filesystem::directory_iterator endIterator; - - typedef std::multimap<std::time_t, boost::filesystem::path> result_set_t; - result_set_t result_set; - - std::string dataType = "CLUSTERS", dataOrigin = "TPC "; - - int totalNumberOfClusters = 0, totalNumberOfDataFiles = 0; - - // Traverse the filesystem and execute processData for each cluster file found - if (boost::filesystem::exists(dataPath) && boost::filesystem::is_directory(dataPath)) { - for (boost::filesystem::directory_iterator directoryIterator(dataPath); directoryIterator != endIterator; - ++directoryIterator) { - if (boost::filesystem::is_regular_file(directoryIterator->status())) { - totalNumberOfClusters += processData(directoryIterator->path().string(), dataType, dataOrigin); - totalNumberOfDataFiles++; - } - } - } else { - std::cerr << "Path " << dataPath.string() << "/ could not be found or does not contain any valid data files" - << endl; - exit(1); - } - - cout << "Added " << totalNumberOfClusters << " clusters from " << totalNumberOfDataFiles << " data files" << endl; - - // printData(totalNumberOfClusters); - - drawData(totalNumberOfClusters, dataFilename); - - return 0; -} diff --git a/Utilities/rANS/CMakeLists.txt b/Utilities/rANS/CMakeLists.txt index b627df2b74f69..a56d9098ffe49 100644 --- a/Utilities/rANS/CMakeLists.txt +++ b/Utilities/rANS/CMakeLists.txt @@ -10,11 +10,51 @@ o2_add_library(rANS SOURCES src/SymbolStatistics.cxx + src/FrequencyTable.cxx PUBLIC_LINK_LIBRARIES FairLogger::FairLogger) + o2_add_test(EncodeDecode NAME EncodeDecode SOURCES test/test_ransEncodeDecode.cxx PUBLIC_LINK_LIBRARIES O2::rANS COMPONENT_NAME rANS LABELS utils) + +o2_add_test(FrequencyTable + NAME FrequencyTable + SOURCES test/test_ransFrequencyTable.cxx + PUBLIC_LINK_LIBRARIES O2::rANS + COMPONENT_NAME rANS + LABELS utils) + +o2_add_test(CombinedIterator + NAME CombinedIterator + SOURCES test/test_ransCombinedIterator.cxx + PUBLIC_LINK_LIBRARIES O2::rANS + COMPONENT_NAME rANS + LABELS utils) + +o2_add_executable(CombinedIterator + SOURCES benchmarks/bench_ransCombinedIterator.cxx + COMPONENT_NAME rANS + IS_BENCHMARK + PUBLIC_LINK_LIBRARIES O2::rANS benchmark::benchmark) + +o2_add_executable(rans-encode-decode-8 + TARGETVARNAME targetName + SOURCES run/bin-encode-decode.cxx + PUBLIC_LINK_LIBRARIES O2::rANS Boost::program_options) +target_compile_definitions(${targetName} PRIVATE -DSOURCE_T=uint8_t) + +o2_add_executable(rans-encode-decode-16 + TARGETVARNAME targetName + SOURCES run/bin-encode-decode.cxx + PUBLIC_LINK_LIBRARIES O2::rANS Boost::program_options) +target_compile_definitions(${targetName} PRIVATE -DSOURCE_T=uint16_t) + +o2_add_executable(rans-encode-decode-32 + TARGETVARNAME targetName + SOURCES run/bin-encode-decode.cxx + PUBLIC_LINK_LIBRARIES O2::rANS Boost::program_options) +target_compile_definitions(${targetName} PRIVATE -DSOURCE_T=uint32_t) \ No newline at end of file diff --git a/Utilities/rANS/benchmarks/bench_ransCombinedIterator.cxx b/Utilities/rANS/benchmarks/bench_ransCombinedIterator.cxx new file mode 100644 index 0000000000000..8b3ab59181cd0 --- /dev/null +++ b/Utilities/rANS/benchmarks/bench_ransCombinedIterator.cxx @@ -0,0 +1,113 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 bench_ransCombinedIterator.cxx +/// @author Michael Lettrich +/// @since Nov 18, 2020 +/// @brief + +#include <vector> + +#include <benchmark/benchmark.h> + +#include "rANS/utils.h" + +static void BM_Array_Read_Copy(benchmark::State& state) +{ + std::vector<uint32_t> a(state.range(0), 0x0); + std::vector<uint32_t> b(state.range(0), 0x1); + std::vector<uint32_t> c(state.range(0), 0x0); + for (auto _ : state) { + std::vector<uint32_t> tmp(state.range(0)); + + for (size_t i = 0; i < a.size(); ++i) { + tmp[i] = b[i] + (a[i] << 16); + } + + for (size_t i = 0; i < tmp.size(); ++i) { + c[i] = tmp[i] + 1; + } + } +} + +BENCHMARK(BM_Array_Read_Copy)->RangeMultiplier(2)->Range(10e4, 2 * 10e6); + +static void BM_Array_Read_Iterator(benchmark::State& state) +{ + std::vector<uint32_t> a(state.range(0), 0x0); + std::vector<uint32_t> b(state.range(0), 0x1); + std::vector<uint32_t> c(state.range(0), 0x0); + + for (auto _ : state) { + auto readOP = [](auto iterA, auto iterB) -> uint32_t { + return *iterB + (*iterA << 16); + }; + + const o2::rans::utils::CombinedInputIterator begin(a.begin(), b.begin(), readOP); + const o2::rans::utils::CombinedInputIterator end(a.end(), b.end(), readOP); + + auto cIter = c.begin(); + + for (auto iter = begin; iter != end; ++iter) { + *cIter = *iter + 1; + ++cIter; + } + } +} + +BENCHMARK(BM_Array_Read_Iterator)->RangeMultiplier(2)->Range(10e4, 2 * 10e6); + +static void BM_Array_Write_Copy(benchmark::State& state) +{ + std::vector<uint32_t> a(state.range(0), 0x0); + std::vector<uint32_t> b(state.range(0), 0x0); + std::vector<uint32_t> c(state.range(0), 0x0001000f); + for (auto _ : state) { + std::vector<uint32_t> tmp(state.range(0)); + + for (size_t i = 0; i < c.size(); ++i) { + tmp[i] = c[i] + 1; + } + + for (size_t i = 0; i < a.size(); ++i) { + const uint32_t shift = 16; + a[i] = tmp[i] >> shift; + b[i] = tmp[i] & ((1 << shift) - 1); + } + } +} + +BENCHMARK(BM_Array_Write_Copy)->RangeMultiplier(2)->Range(10e4, 2 * 10e6); + +static void BM_Array_Write_Iterator(benchmark::State& state) +{ + std::vector<uint32_t> a(state.range(0), 0x0); + std::vector<uint32_t> b(state.range(0), 0x0); + std::vector<uint32_t> c(state.range(0), 0x0001000f); + + for (auto _ : state) { + auto writeOP = [](auto iterA, auto iterB, uint32_t value) -> void { + const uint32_t shift = 16; + *iterA = value >> shift; + *iterB = value & ((1 << shift) - 1); + }; + + o2::rans::utils::CombinedOutputIterator out(a.begin(), b.begin(), writeOP); + + for (auto iter = c.begin(); iter != c.end(); ++iter) { + *out = *iter + 1; + ++out; + } + } +} + +BENCHMARK(BM_Array_Write_Iterator)->RangeMultiplier(2)->Range(10e4, 2 * 10e6); + +BENCHMARK_MAIN(); diff --git a/Utilities/rANS/include/rANS/Coder.h b/Utilities/rANS/include/rANS/Coder.h deleted file mode 100644 index 388b90f9b50f9..0000000000000 --- a/Utilities/rANS/include/rANS/Coder.h +++ /dev/null @@ -1,377 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 Coder.h -/// @author Michael Lettrich -/// @since 2019-05-10 -/// @brief Stateless class for coding and decoding symbols using rANS - -#ifndef RANS_CODER_H -#define RANS_CODER_H - -#include <vector> -#include <cstdint> -#include <cassert> -#include <type_traits> - -#include "DecoderSymbol.h" -#include "EncoderSymbol.h" -#include "helper.h" - -namespace o2 -{ -namespace rans -{ -__extension__ typedef unsigned __int128 uint128; - -// READ ME FIRST: -// -// This is designed like a typical arithmetic coder API, but there's three -// twists you absolutely should be aware of before you start hacking: -// -// 1. You need to encode data in *reverse* - last symbol first. rANS works -// like a stack: last in, first out. -// 2. Likewise, the encoder outputs bytes *in reverse* - that is, you give -// it a pointer to the *end* of your buffer (exclusive), and it will -// slowly move towards the beginning as more bytes are emitted. -// 3. Unlike basically any other entropy coder implementation you might -// have used, you can interleave data from multiple independent rANS -// encoders into the same bytestream without any extra signaling; -// you can also just write some bytes by yourself in the middle if -// you want to. This is in addition to the usual arithmetic encoder -// property of being able to switch models on the fly. Writing raw -// bytes can be useful when you have some data that you know is -// incompressible, and is cheaper than going through the rANS encode -// function. Using multiple rANS coders on the same byte stream wastes -// a few bytes compared to using just one, but execution of two -// independent encoders can happen in parallel on superscalar and -// Out-of-Order CPUs, so this can be *much* faster in tight decoding -// loops. -// -// This is why all the rANS functions take the write pointer as an -// argument instead of just storing it in some context struct. - -// -------------------------------------------------------------------------- -template <typename State_T, typename Stream_T> -class Coder -{ - // the Coder works either with a 64Bit state and 32 bit streaming or - //a 32 Bit state and 8 Bit streaming We need to make sure it gets initialized with - //the right template arguments at compile time. - static_assert((sizeof(State_T) == sizeof(uint32_t) && sizeof(Stream_T) == sizeof(uint8_t)) || - (sizeof(State_T) == sizeof(uint64_t) && sizeof(Stream_T) == sizeof(uint32_t)), - "Coder can either be 32Bit with 8 Bit stream type or 64 Bit Type with 32 Bit stream type"); - - public: - Coder(); - - // Initializes the encoder - void encInit(); - - // Encodes a single symbol with range start "start" and frequency "freq". - // All frequencies are assumed to sum to "1 << scale_bits", and the - // resulting bytes get written to ptr (which is updated). - // - // NOTE: With rANS, you need to encode symbols in *reverse order*, i.e. from - // beginning to end! Likewise, the output bytestream is written *backwards*: - // ptr starts pointing at the end of the output buffer and keeps decrementing. - template <typename Stream_IT> - Stream_IT encPut(Stream_IT iter, uint32_t start, uint32_t freq, uint32_t scale_bits); - - // Flushes the rANS encoder. - template <typename Stream_IT> - Stream_IT encFlush(Stream_IT iter); - - // Initializes a rANS decoder. - // Unlike the encoder, the decoder works forwards as you'd expect. - template <typename Stream_IT> - Stream_IT decInit(Stream_IT iter); - - // Returns the current cumulative frequency (map it to a symbol yourself!) - uint32_t decGet(uint32_t scale_bits); - - // Advances in the bit stream by "popping" a single symbol with range start - // "start" and frequency "freq". All frequencies are assumed to sum to "1 << scale_bits", - // and the resulting bytes get written to ptr (which is updated). - template <typename Stream_IT> - Stream_IT decAdvance(Stream_IT iter, uint32_t start, uint32_t freq, uint32_t scale_bits); - - // Encodes a given symbol. This is faster than straight RansEnc since we can do - // multiplications instead of a divide. - // - // See Rans32EncSymbolInit for a description of how this works. - template <typename Stream_IT> - Stream_IT encPutSymbol(Stream_IT iter, const EncoderSymbol<State_T>& sym, uint32_t scale_bits); - - // Equivalent to Rans32DecAdvance that takes a symbol. - template <typename Stream_IT> - Stream_IT decAdvanceSymbol(Stream_IT iter, const DecoderSymbol& sym, uint32_t scale_bits); - - // Advances in the bit stream by "popping" a single symbol with range start - // "start" and frequency "freq". All frequencies are assumed to sum to "1 << scale_bits". - // No renormalization or output happens. - void decAdvanceStep(uint32_t start, uint32_t freq, uint32_t scale_bits); - - // Equivalent to Rans32DecAdvanceStep that takes a symbol. - void decAdvanceSymbolStep(const DecoderSymbol& sym, uint32_t scale_bits); - - // Renormalize. - template <typename Stream_IT> - Stream_IT decRenorm(Stream_IT iter); - - private: - State_T mState; - - // Renormalize the encoder. - template <typename Stream_IT> - std::tuple<State_T, Stream_IT> encRenorm(State_T x, Stream_IT iter, uint32_t freq, uint32_t scale_bits); - - // Renormalize. - template <typename Stream_IT> - std::tuple<State_T, Stream_IT> decRenorm(State_T x, Stream_IT iter); - - // L ('l' in the paper) is the lower bound of our normalization interval. - // Between this and our byte-aligned emission, we use 31 (not 32!) bits. - // This is done intentionally because exact reciprocals for 31-bit uints - // fit in 32-bit uints: this permits some optimizations during encoding. - inline static constexpr State_T LOWER_BOUND = needs64Bit<State_T>() ? (1u << 31) : (1u << 23); // lower bound of our normalization interval - - inline static constexpr State_T STREAM_BITS = sizeof(Stream_T) * 8; // lower bound of our normalization interval -}; - -template <typename State_T, typename Stream_T> -Coder<State_T, Stream_T>::Coder() : mState(){}; - -template <typename State_T, typename Stream_T> -void Coder<State_T, Stream_T>::encInit() -{ - mState = LOWER_BOUND; -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::encPut(Stream_IT iter, uint32_t start, uint32_t freq, uint32_t scale_bits) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - // renormalize - Stream_IT streamPos; - State_T x; - std::tie(x, streamPos) = encRenorm(mState, iter, freq, scale_bits); - - // x = C(s,x) - mState = ((x / freq) << scale_bits) + (x % freq) + start; - return streamPos; -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::encFlush(Stream_IT iter) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - Stream_IT streamPos = iter; - - if constexpr (needs64Bit<State_T>()) { - ++streamPos; - *streamPos = static_cast<Stream_T>(mState >> 32); - ++streamPos; - *streamPos = static_cast<Stream_T>(mState >> 0); - assert(std::distance(iter, streamPos) == 2); - } else { - ++streamPos; - *streamPos = static_cast<Stream_T>(mState >> 24); - ++streamPos; - *streamPos = static_cast<Stream_T>(mState >> 16); - ++streamPos; - *streamPos = static_cast<Stream_T>(mState >> 8); - ++streamPos; - *streamPos = static_cast<Stream_T>(mState >> 0); - assert(std::distance(iter, streamPos) == 4); - } - - mState = 0; - return streamPos; -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::decInit(Stream_IT iter) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - State_T x = 0; - Stream_IT streamPos = iter; - - if constexpr (needs64Bit<State_T>()) { - x = static_cast<State_T>(*streamPos) << 0; - --streamPos; - x |= static_cast<State_T>(*streamPos) << 32; - --streamPos; - assert(std::distance(streamPos, iter) == 2); - } else { - x = static_cast<State_T>(*streamPos) << 0; - --streamPos; - x |= static_cast<State_T>(*streamPos) << 8; - --streamPos; - x |= static_cast<State_T>(*streamPos) << 16; - --streamPos; - x |= static_cast<State_T>(*streamPos) << 24; - --streamPos; - assert(std::distance(streamPos, iter) == 4); - } - - mState = x; - return streamPos; -}; - -template <typename State_T, typename Stream_T> -uint32_t Coder<State_T, Stream_T>::decGet(uint32_t scale_bits) -{ - return mState & ((1u << scale_bits) - 1); -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::decAdvance(Stream_IT iter, uint32_t start, uint32_t freq, uint32_t scale_bits) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - State_T mask = (1ull << scale_bits) - 1; - - // s, x = D(x) - State_T x = mState; - x = freq * (x >> scale_bits) + (x & mask) - start; - - // renormalize - Stream_IT streamPos; - std::tie(mState, streamPos) = this->decRenorm(x, iter); - return streamPos; -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::encPutSymbol(Stream_IT iter, const EncoderSymbol<State_T>& sym, uint32_t scale_bits) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - assert(sym.freq != 0); // can't encode symbol with freq=0 - - // renormalize - Stream_IT streamPos; - State_T x; - std::tie(x, streamPos) = encRenorm(mState, iter, sym.freq, scale_bits); - - // x = C(s,x) - State_T q; - - if constexpr (needs64Bit<State_T>()) { - // This code needs support for 64-bit long multiplies with 128-bit result - // (or more precisely, the top 64 bits of a 128-bit result). - q = static_cast<State_T>((static_cast<uint128>(x) * sym.rcp_freq) >> 64); - } else { - q = static_cast<State_T>((static_cast<uint64_t>(x) * sym.rcp_freq) >> 32); - } - q = q >> sym.rcp_shift; - - mState = x + sym.bias + q * sym.cmpl_freq; - return streamPos; -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::decAdvanceSymbol(Stream_IT iter, const DecoderSymbol& sym, uint32_t scale_bits) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - return decAdvance(iter, sym.start, sym.freq, scale_bits); -}; - -template <typename State_T, typename Stream_T> -void Coder<State_T, Stream_T>::decAdvanceStep(uint32_t start, uint32_t freq, uint32_t scale_bits) -{ - State_T mask = (1u << scale_bits) - 1; - - // s, x = D(x) - State_T x = mState; - mState = freq * (x >> scale_bits) + (x & mask) - start; -}; - -template <typename State_T, typename Stream_T> -void Coder<State_T, Stream_T>::decAdvanceSymbolStep(const DecoderSymbol& sym, uint32_t scale_bits) -{ - decAdvanceStep(sym.start, sym.freq, scale_bits); -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -Stream_IT Coder<State_T, Stream_T>::decRenorm(Stream_IT iter) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - Stream_IT streamPos; - std::tie(mState, streamPos) = this->decRenorm(mState, iter); - return streamPos; -} - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -inline std::tuple<State_T, Stream_IT> Coder<State_T, Stream_T>::encRenorm(State_T x, Stream_IT iter, uint32_t freq, uint32_t scale_bits) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - Stream_IT streamPos = iter; - - State_T x_max = ((LOWER_BOUND >> scale_bits) << STREAM_BITS) * freq; // this turns into a shift. - if (x >= x_max) { - if constexpr (needs64Bit<State_T>()) { - ++streamPos; - *streamPos = static_cast<Stream_T>(x); - x >>= STREAM_BITS; - assert(x < x_max); - } else { - do { - ++streamPos; - *streamPos = static_cast<Stream_T>(x & 0xff); - x >>= STREAM_BITS; - } while (x >= x_max); - } - } - return std::make_tuple(x, streamPos); -}; - -template <typename State_T, typename Stream_T> -template <typename Stream_IT> -inline std::tuple<State_T, Stream_IT> Coder<State_T, Stream_T>::decRenorm(State_T x, Stream_IT iter) -{ - static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); - - Stream_IT streamPos = iter; - - // renormalize - if (x < LOWER_BOUND) { - if constexpr (needs64Bit<State_T>()) { - x = (x << STREAM_BITS) | *streamPos; - --streamPos; - assert(x >= LOWER_BOUND); - } else { - - do { - x = (x << STREAM_BITS) | *streamPos; - --streamPos; - } while (x < LOWER_BOUND); - } - } - return std::make_tuple(x, streamPos); -} - -} // namespace rans -} // namespace o2 - -#endif /* RANS_CODER_H */ diff --git a/Utilities/rANS/include/rANS/Decoder.h b/Utilities/rANS/include/rANS/Decoder.h index b4e2e920c5628..b9ace6e49fc2b 100644 --- a/Utilities/rANS/include/rANS/Decoder.h +++ b/Utilities/rANS/include/rANS/Decoder.h @@ -16,29 +16,35 @@ #ifndef RANS_DECODER_H #define RANS_DECODER_H +#include "internal/Decoder.h" + #include <cstddef> #include <type_traits> #include <iostream> +#include <memory> #include <fairlogger/Logger.h> -#include "SymbolTable.h" -#include "DecoderSymbol.h" -#include "ReverseSymbolLookupTable.h" -#include "Coder.h" +#include "FrequencyTable.h" +#include "internal/DecoderSymbol.h" +#include "internal/ReverseSymbolLookupTable.h" +#include "internal/SymbolTable.h" +#include "internal/Decoder.h" +#include "internal/SymbolStatistics.h" namespace o2 { namespace rans { + template <typename coder_T, typename stream_T, typename source_T> class Decoder { - private: - using decoderSymbol_t = SymbolTable<DecoderSymbol>; - using reverseSymbolLookupTable_t = ReverseSymbolLookupTable<source_T>; - using ransDecoder = Coder<coder_T, stream_T>; + protected: + using decoderSymbolTable_t = internal::SymbolTable<internal::DecoderSymbol>; + using reverseSymbolLookupTable_t = internal::ReverseSymbolLookupTable; + using ransDecoder = internal::Decoder<coder_T, stream_T>; public: Decoder(const Decoder& d); @@ -46,17 +52,21 @@ class Decoder Decoder<coder_T, stream_T, source_T>& operator=(const Decoder& d); Decoder<coder_T, stream_T, source_T>& operator=(Decoder&& d) = default; ~Decoder() = default; - Decoder(const SymbolStatistics& stats, size_t probabilityBits); + Decoder(const FrequencyTable& stats, size_t probabilityBits); template <typename stream_IT, typename source_IT> - void process(const source_IT outputBegin, const stream_IT inputEnd, size_t numSymbols) const; + void process(const source_IT outputBegin, const stream_IT inputEnd, size_t messageLength) const; + + size_t getAlphabetRangeBits() const { return mSymbolTable->getAlphabetRangeBits(); } + int getMinSymbol() const { return mSymbolTable->getMinSymbol(); } + int getMaxSymbol() const { return mSymbolTable->getMaxSymbol(); } using coder_t = coder_T; using stream_t = stream_T; using source_t = source_T; - private: - std::unique_ptr<decoderSymbol_t> mSymbolTable; + protected: + std::unique_ptr<decoderSymbolTable_t> mSymbolTable; std::unique_ptr<reverseSymbolLookupTable_t> mReverseLUT; size_t mProbabilityBits; }; @@ -64,80 +74,84 @@ class Decoder template <typename coder_T, typename stream_T, typename source_T> Decoder<coder_T, stream_T, source_T>::Decoder(const Decoder& d) : mSymbolTable(nullptr), mReverseLUT(nullptr), mProbabilityBits(d.mProbabilityBits) { - mSymbolTable = std::make_unique<decoderSymbol_t>(*d.mSymbolTable); + mSymbolTable = std::make_unique<decoderSymbolTable_t>(*d.mSymbolTable); mReverseLUT = std::make_unique<reverseSymbolLookupTable_t>(*d.mReverseLUT); } template <typename coder_T, typename stream_T, typename source_T> Decoder<coder_T, stream_T, source_T>& Decoder<coder_T, stream_T, source_T>::operator=(const Decoder& d) { - mSymbolTable = std::make_unique<decoderSymbol_t>(*d.mSymbolTable); + mSymbolTable = std::make_unique<decoderSymbolTable_t>(*d.mSymbolTable); mReverseLUT = std::make_unique<reverseSymbolLookupTable_t>(*d.mReverseLUT); mProbabilityBits = d.mProbabilityBits; return *this; } template <typename coder_T, typename stream_T, typename source_T> -Decoder<coder_T, stream_T, source_T>::Decoder(const SymbolStatistics& stats, size_t probabilityBits) : mSymbolTable(nullptr), mReverseLUT(nullptr), mProbabilityBits(probabilityBits) +Decoder<coder_T, stream_T, source_T>::Decoder(const FrequencyTable& frequencies, size_t probabilityBits) : mSymbolTable(nullptr), mReverseLUT(nullptr), mProbabilityBits(probabilityBits) { + using namespace internal; + + SymbolStatistics stats(frequencies, mProbabilityBits); + mProbabilityBits = stats.getSymbolTablePrecision(); + RANSTimer t; t.start(); - mSymbolTable = std::make_unique<decoderSymbol_t>(stats, probabilityBits); + mSymbolTable = std::make_unique<decoderSymbolTable_t>(stats); t.stop(); LOG(debug1) << "Decoder SymbolTable inclusive time (ms): " << t.getDurationMS(); t.start(); - mReverseLUT = std::make_unique<reverseSymbolLookupTable_t>(probabilityBits, stats); + mReverseLUT = std::make_unique<reverseSymbolLookupTable_t>(mProbabilityBits, stats); t.stop(); LOG(debug1) << "ReverseSymbolLookupTable inclusive time (ms): " << t.getDurationMS(); }; template <typename coder_T, typename stream_T, typename source_T> template <typename stream_IT, typename source_IT> -void Decoder<coder_T, stream_T, source_T>::process(const source_IT outputBegin, const stream_IT inputEnd, size_t numSymbols) const +void Decoder<coder_T, stream_T, source_T>::process(const source_IT outputBegin, const stream_IT inputEnd, size_t messageLength) const { + using namespace internal; LOG(trace) << "start decoding"; RANSTimer t; t.start(); static_assert(std::is_same<typename std::iterator_traits<source_IT>::value_type, source_T>::value); static_assert(std::is_same<typename std::iterator_traits<stream_IT>::value_type, stream_T>::value); - if (numSymbols == 0) { + if (messageLength == 0) { LOG(warning) << "Empty message passed to decoder, skipping decode process"; return; } - ransDecoder rans0, rans1; stream_IT inputIter = inputEnd; source_IT it = outputBegin; // make Iter point to the last last element --inputIter; - inputIter = rans0.decInit(inputIter); - inputIter = rans1.decInit(inputIter); + ransDecoder rans0, rans1; + inputIter = rans0.init(inputIter); + inputIter = rans1.init(inputIter); - for (size_t i = 0; i < (numSymbols & ~1); i += 2) { - const stream_T s0 = - (*mReverseLUT)[rans0.decGet(mProbabilityBits)]; - const stream_T s1 = - (*mReverseLUT)[rans1.decGet(mProbabilityBits)]; + for (size_t i = 0; i < (messageLength & ~1); i += 2) { + const int64_t s0 = (*mReverseLUT)[rans0.get(mProbabilityBits)]; + const int64_t s1 = (*mReverseLUT)[rans1.get(mProbabilityBits)]; *it++ = s0; *it++ = s1; - rans0.decAdvanceSymbolStep((*mSymbolTable)[s0], mProbabilityBits); - rans1.decAdvanceSymbolStep((*mSymbolTable)[s1], mProbabilityBits); - inputIter = rans0.decRenorm(inputIter); - inputIter = rans1.decRenorm(inputIter); + inputIter = rans0.advanceSymbol(inputIter, (*mSymbolTable)[s0], mProbabilityBits); + inputIter = rans1.advanceSymbol(inputIter, (*mSymbolTable)[s1], mProbabilityBits); } - // last byte, if number of bytes was odd - if (numSymbols & 1) { - const stream_T s0 = - (*mReverseLUT)[rans0.decGet(mProbabilityBits)]; + // last byte, if message length was odd + if (messageLength & 1) { + const int64_t s0 = (*mReverseLUT)[rans0.get(mProbabilityBits)]; *it = s0; - inputIter = rans0.decAdvanceSymbol(inputIter, (*mSymbolTable)[s0], mProbabilityBits); + inputIter = rans0.advanceSymbol(inputIter, (*mSymbolTable)[s0], mProbabilityBits); } t.stop(); - LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); + LOG(debug1) << "Decoder::" << __func__ << " { DecodedSymbols: " << messageLength << "," + << "processedBytes: " << messageLength * sizeof(source_T) << "," + << " inclusiveTimeMS: " << t.getDurationMS() << "," + << " BandwidthMiBPS: " << std::fixed << std::setprecision(2) << (messageLength * sizeof(source_T) * 1.0) / (t.getDurationS() * 1.0 * (1 << 20)) << "}"; LOG(trace) << "done decoding"; } diff --git a/Utilities/rANS/include/rANS/DedupDecoder.h b/Utilities/rANS/include/rANS/DedupDecoder.h new file mode 100644 index 0000000000000..b9c3538b95dcf --- /dev/null +++ b/Utilities/rANS/include/rANS/DedupDecoder.h @@ -0,0 +1,104 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Decoder.h +/// @author Michael Lettrich +/// @since 2020-04-06 +/// @brief Decoder - decode a rANS encoded state back into source symbols + +#ifndef RANS_DEDUPDECODER_H +#define RANS_DEDUPDECODER_H + +#include "Decoder.h" + +#include <cstddef> +#include <type_traits> +#include <iostream> +#include <string> + +#include <fairlogger/Logger.h> + +#include "internal/DecoderSymbol.h" +#include "internal/ReverseSymbolLookupTable.h" +#include "internal/SymbolTable.h" +#include "internal/Decoder.h" + +namespace o2 +{ +namespace rans +{ + +template <typename coder_T, typename stream_T, typename source_T> +class DedupDecoder : public Decoder<coder_T, stream_T, source_T> +{ + //inherit constructors; + using Decoder<coder_T, stream_T, source_T>::Decoder; + + public: + using duplicatesMap_t = std::map<uint32_t, uint32_t>; + + template <typename stream_IT, typename source_IT> + void process(const source_IT outputBegin, const stream_IT inputEnd, size_t messageLength, duplicatesMap_t& duplicates) const; +}; + +template <typename coder_T, typename stream_T, typename source_T> +template <typename stream_IT, typename source_IT> +void DedupDecoder<coder_T, stream_T, source_T>::process(const source_IT outputBegin, const stream_IT inputEnd, size_t messageLength, duplicatesMap_t& duplicates) const +{ + using namespace internal; + using ransDecoder = internal::Decoder<coder_T, stream_T>; + LOG(trace) << "start decoding"; + RANSTimer t; + t.start(); + static_assert(std::is_same<typename std::iterator_traits<source_IT>::value_type, source_T>::value); + static_assert(std::is_same<typename std::iterator_traits<stream_IT>::value_type, stream_T>::value); + + if (messageLength == 0) { + LOG(warning) << "Empty message passed to decoder, skipping decode process"; + return; + } + + stream_IT inputIter = inputEnd; + source_IT it = outputBegin; + + // make Iter point to the last last element + --inputIter; + + ransDecoder rans; + inputIter = rans.init(inputIter); + + for (size_t i = 0; i < (messageLength); i++) { + const auto s = (*this->mReverseLUT)[rans.get(this->mProbabilityBits)]; + + // deduplication + auto duplicatesIter = duplicates.find(i); + if (duplicatesIter != duplicates.end()) { + LOG(trace) << "pos[" << i << "]: restoring " << duplicatesIter->second << " duplicates of symbol " << (char)s; + for (int d = 0; d < duplicatesIter->second; d++) { + *it++ = s; + i++; + } + } + *it++ = s; + inputIter = rans.advanceSymbol(inputIter, (*this->mSymbolTable)[s], this->mProbabilityBits); + } + + t.stop(); + LOG(debug1) << "Decoder::" << __func__ << " { DecodedSymbols: " << messageLength << "," + << "processedBytes: " << messageLength * sizeof(source_T) << "," + << " inclusiveTimeMS: " << t.getDurationMS() << "," + << " BandwidthMiBPS: " << std::fixed << std::setprecision(2) << (messageLength * sizeof(source_T) * 1.0) / (t.getDurationS() * 1.0 * (1 << 20)) << "}"; + + LOG(trace) << "done decoding"; +} +} // namespace rans +} // namespace o2 + +#endif /* RANS_DEDUPDECODER_H */ diff --git a/Utilities/rANS/include/rANS/DedupEncoder.h b/Utilities/rANS/include/rANS/DedupEncoder.h new file mode 100644 index 0000000000000..3a4efaf2d9f74 --- /dev/null +++ b/Utilities/rANS/include/rANS/DedupEncoder.h @@ -0,0 +1,161 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Encoder.h +/// @author Michael Lettrich +/// @since 2020-04-06 +/// @brief Encoder - code symbol into a rANS encoded state + +#ifndef INCLUDE_RANS_DEDUPENCODER_H_ +#define INCLUDE_RANS_DEDUPENCODER_H_ + +#include "Encoder.h" + +#include <memory> +#include <algorithm> +#include <iomanip> +#include <map> +#include <cstdint> +#include <string> + +#include <fairlogger/Logger.h> +#include <stdexcept> + +#include "internal/EncoderSymbol.h" +#include "internal/helper.h" +#include "internal/SymbolTable.h" + +namespace o2 +{ +namespace rans +{ + +template <typename coder_T, typename stream_T, typename source_T> +class DedupEncoder : public Encoder<coder_T, stream_T, source_T> +{ + //inherit constructors; + using Encoder<coder_T, stream_T, source_T>::Encoder; + + public: + using duplicatesMap_t = std::map<uint32_t, uint32_t>; + + template <typename stream_IT, typename source_IT> + const stream_IT process(const stream_IT outputBegin, const stream_IT outputEnd, + const source_IT inputBegin, source_IT inputEnd, duplicatesMap_t& duplicates) const; +}; + +template <typename coder_T, typename stream_T, typename source_T> +template <typename stream_IT, typename source_IT> +const stream_IT DedupEncoder<coder_T, stream_T, source_T>::process(const stream_IT outputBegin, const stream_IT outputEnd, const source_IT inputBegin, const source_IT inputEnd, duplicatesMap_t& duplicates) const +{ + using namespace internal; + using ransCoder = internal::Encoder<coder_T, stream_T>; + LOG(trace) << "start encoding"; + RANSTimer t; + t.start(); + + static_assert(std::is_same<typename std::iterator_traits<source_IT>::value_type, source_T>::value); + static_assert(std::is_same<typename std::iterator_traits<stream_IT>::value_type, stream_T>::value); + + if (inputBegin == inputEnd) { + LOG(warning) << "passed empty message to encoder, skip encoding"; + return outputEnd; + } + + if (outputBegin == outputEnd) { + const std::string errorMessage("Unallocated encode buffer passed to encoder. Aborting"); + LOG(error) << errorMessage; + throw std::runtime_error(errorMessage); + } + + ransCoder rans; + + stream_IT outputIter = outputBegin; + source_IT inputIT = inputEnd; + + const auto inputBufferSize = std::distance(inputBegin, inputEnd); + + auto encode = [&inputBegin, &duplicates, this](source_IT symbolIter, stream_IT outputIter, ransCoder& coder) { + const source_T symbol = *symbolIter; + const auto& encoderSymbol = (*this->mSymbolTable)[symbol]; + + // dedup step: + auto dedupIT = symbolIter; + //advance on source by one. + --dedupIT; + size_t numDuplicates = 0; + + // find out how many duplicates we have + while (*dedupIT == symbol) { + --dedupIT; + ++numDuplicates; + } + + // if we have a duplicate treat it. + if (numDuplicates > 0) { + LOG(trace) << "pos[" << std::distance(inputBegin, symbolIter) - 1 << "]: found " << numDuplicates << " duplicates of symbol " << (char)symbol; + duplicates.emplace(std::distance(inputBegin, symbolIter) - 1, numDuplicates); + } + + return std::tuple(++dedupIT, coder.putSymbol(outputIter, encoderSymbol, this->mProbabilityBits)); + }; + + while (inputIT > inputBegin) { // NB: working in reverse! + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans); + assert(outputIter < outputEnd); + } + outputIter = rans.flush(outputIter); + // first iterator past the range so that sizes, distances and iterators work correctly. + ++outputIter; + + assert(!(outputIter > outputEnd)); + + // deal with overflow + if (outputIter > outputEnd) { + const std::string exceptionText = [&]() { + std::stringstream ss; + ss << __func__ << " detected overflow in encode buffer: allocated:" << std::distance(outputBegin, outputEnd) << ", used:" << std::distance(outputBegin, outputIter); + return ss.str(); + }(); + + LOG(error) << exceptionText; + throw std::runtime_error(exceptionText); + } + + t.stop(); + LOG(debug1) << "Encoder::" << __func__ << " {ProcessedBytes: " << inputBufferSize * sizeof(source_T) << "," + << " inclusiveTimeMS: " << t.getDurationMS() << "," + << " BandwidthMiBPS: " << std::fixed << std::setprecision(2) << (inputBufferSize * sizeof(source_T) * 1.0) / (t.getDurationS() * 1.0 * (1 << 20)) << "}"; + +// advanced diagnostics for debug builds +#if !defined(NDEBUG) + + const auto inputBufferSizeB = inputBufferSize * sizeof(source_T); + const auto outputBufferSizeB = std::distance(outputBegin, outputIter) * sizeof(stream_T); + + LOG(debug2) << "EncoderProperties: {" + << "sourceTypeB: " << sizeof(source_T) << ", " + << "streamTypeB: " << sizeof(stream_T) << ", " + << "coderTypeB: " << sizeof(coder_T) << ", " + << "probabilityBits: " << this->mProbabilityBits << ", " + << "inputBufferSizeB: " << inputBufferSizeB << ", " + << "outputBufferSizeB: " << outputBufferSizeB << ", " + << "compressionFactor: " << std::fixed << std::setprecision(2) << static_cast<double>(inputBufferSizeB) / static_cast<double>(outputBufferSizeB) << "}"; +#endif + + LOG(trace) << "done encoding"; + + return outputIter; +}; + +} // namespace rans +} // namespace o2 + +#endif /* INCLUDE_RANS_DEDUPENCODER_H_ */ diff --git a/Utilities/rANS/include/rANS/Encoder.h b/Utilities/rANS/include/rANS/Encoder.h index 063890ac9552e..907302b14202c 100644 --- a/Utilities/rANS/include/rANS/Encoder.h +++ b/Utilities/rANS/include/rANS/Encoder.h @@ -16,26 +16,30 @@ #ifndef RANS_ENCODER_H #define RANS_ENCODER_H +#include "internal/Encoder.h" + #include <memory> #include <algorithm> +#include <iomanip> #include <fairlogger/Logger.h> #include <stdexcept> -#include "SymbolTable.h" -#include "EncoderSymbol.h" -#include "Coder.h" -#include "helper.h" +#include "internal/EncoderSymbol.h" +#include "internal/helper.h" +#include "internal/SymbolTable.h" +#include "FrequencyTable.h" namespace o2 { namespace rans { + template <typename coder_T, typename stream_T, typename source_T> class Encoder { - private: - using encoderSymbolTable_t = SymbolTable<EncoderSymbol<coder_T>>; + protected: + using encoderSymbolTable_t = internal::SymbolTable<internal::EncoderSymbol<coder_T>>; public: Encoder() = delete; @@ -47,21 +51,26 @@ class Encoder Encoder(const encoderSymbolTable_t& e, size_t probabilityBits); Encoder(encoderSymbolTable_t&& e, size_t probabilityBits); - Encoder(const SymbolStatistics& stats, size_t probabilityBits); + Encoder(const FrequencyTable& frequencies, size_t probabilityBits); template <typename stream_IT, typename source_IT> const stream_IT process(const stream_IT outputBegin, const stream_IT outputEnd, const source_IT inputBegin, const source_IT inputEnd) const; + size_t getProbabilityBits() const { return mProbabilityBits; } + size_t getAlphabetRangeBits() const { return mSymbolTable->getAlphabetRangeBits(); } + int getMinSymbol() const { return mSymbolTable->getMinSymbol(); } + int getMaxSymbol() const { return mSymbolTable->getMaxSymbol(); } + using coder_t = coder_T; using stream_t = stream_T; using source_t = source_T; - private: + protected: std::unique_ptr<encoderSymbolTable_t> mSymbolTable; size_t mProbabilityBits; - using ransCoder = Coder<coder_T, stream_T>; + using ransCoder = internal::Encoder<coder_T, stream_T>; }; template <typename coder_T, typename stream_T, typename source_T> @@ -88,21 +97,26 @@ template <typename coder_T, typename stream_T, typename source_T> Encoder<coder_T, stream_T, source_T>::Encoder(encoderSymbolTable_t&& e, size_t probabilityBits) : mSymbolTable(std::move(e.mSymbolTable)), mProbabilityBits(probabilityBits){}; template <typename coder_T, typename stream_T, typename source_T> -Encoder<coder_T, stream_T, source_T>::Encoder(const SymbolStatistics& stats, +Encoder<coder_T, stream_T, source_T>::Encoder(const FrequencyTable& frequencies, size_t probabilityBits) : mSymbolTable(nullptr), mProbabilityBits(probabilityBits) { + using namespace internal; + + SymbolStatistics stats(frequencies, mProbabilityBits); + mProbabilityBits = stats.getSymbolTablePrecision(); + RANSTimer t; t.start(); - mSymbolTable = std::make_unique<encoderSymbolTable_t>(stats, probabilityBits); + mSymbolTable = std::make_unique<encoderSymbolTable_t>(stats); t.stop(); LOG(debug1) << "Encoder SymbolTable inclusive time (ms): " << t.getDurationMS(); } template <typename coder_T, typename stream_T, typename source_T> template <typename stream_IT, typename source_IT> -const stream_IT Encoder<coder_T, stream_T, source_T>::Encoder::process( - const stream_IT outputBegin, const stream_IT outputEnd, const source_IT inputBegin, const source_IT inputEnd) const +const stream_IT Encoder<coder_T, stream_T, source_T>::Encoder::process(const stream_IT outputBegin, const stream_IT outputEnd, const source_IT inputBegin, const source_IT inputEnd) const { + using namespace internal; LOG(trace) << "start encoding"; RANSTimer t; t.start(); @@ -122,30 +136,31 @@ const stream_IT Encoder<coder_T, stream_T, source_T>::Encoder::process( } ransCoder rans0, rans1; - rans0.encInit(); - rans1.encInit(); stream_IT outputIter = outputBegin; source_IT inputIT = inputEnd; const auto inputBufferSize = std::distance(inputBegin, inputEnd); + auto encode = [this](source_IT symbolIter, stream_IT outputIter, ransCoder& coder) { + const source_T symbol = *symbolIter; + const auto& encoderSymbol = (*this->mSymbolTable)[symbol]; + return std::tuple(symbolIter, coder.putSymbol(outputIter, encoderSymbol, this->mProbabilityBits)); + }; + // odd number of bytes? if (inputBufferSize & 1) { - const coder_T s = *(--inputIT); - outputIter = rans0.encPutSymbol(outputIter, (*mSymbolTable)[s], mProbabilityBits); + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans0); assert(outputIter < outputEnd); } while (inputIT > inputBegin) { // NB: working in reverse! - const coder_T s1 = *(--inputIT); - const coder_T s0 = *(--inputIT); - outputIter = rans1.encPutSymbol(outputIter, (*mSymbolTable)[s1], mProbabilityBits); - outputIter = rans0.encPutSymbol(outputIter, (*mSymbolTable)[s0], mProbabilityBits); + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans1); + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans0); assert(outputIter < outputEnd); } - outputIter = rans1.encFlush(outputIter); - outputIter = rans0.encFlush(outputIter); + outputIter = rans1.flush(outputIter); + outputIter = rans0.flush(outputIter); // first iterator past the range so that sizes, distances and iterators work correctly. ++outputIter; @@ -164,17 +179,24 @@ const stream_IT Encoder<coder_T, stream_T, source_T>::Encoder::process( } t.stop(); - LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); + LOG(debug1) << "Encoder::" << __func__ << " {ProcessedBytes: " << inputBufferSize * sizeof(source_T) << "," + << " inclusiveTimeMS: " << t.getDurationMS() << "," + << " BandwidthMiBPS: " << std::fixed << std::setprecision(2) << (inputBufferSize * sizeof(source_T) * 1.0) / (t.getDurationS() * 1.0 * (1 << 20)) << "}"; // advanced diagnostics for debug builds #if !defined(NDEBUG) + + const auto inputBufferSizeB = inputBufferSize * sizeof(source_T); + const auto outputBufferSizeB = std::distance(outputBegin, outputIter) * sizeof(stream_T); + LOG(debug2) << "EncoderProperties: {" << "sourceTypeB: " << sizeof(source_T) << ", " << "streamTypeB: " << sizeof(stream_T) << ", " << "coderTypeB: " << sizeof(coder_T) << ", " << "probabilityBits: " << mProbabilityBits << ", " - << "inputBufferSizeB: " << inputBufferSize * sizeof(source_T) << ", " - << "outputBufferSizeB: " << std::distance(outputBegin, outputIter) * sizeof(stream_T) << "}"; + << "inputBufferSizeB: " << inputBufferSizeB << ", " + << "outputBufferSizeB: " << outputBufferSizeB << ", " + << "compressionFactor: " << std::fixed << std::setprecision(2) << static_cast<double>(inputBufferSizeB) / static_cast<double>(outputBufferSizeB) << "}"; #endif LOG(trace) << "done encoding"; diff --git a/Utilities/rANS/include/rANS/FrequencyTable.h b/Utilities/rANS/include/rANS/FrequencyTable.h new file mode 100644 index 0000000000000..789a3ab599919 --- /dev/null +++ b/Utilities/rANS/include/rANS/FrequencyTable.h @@ -0,0 +1,259 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FrequencyTable.h +/// @author Michael Lettrich +/// @since 2019-05-08 +/// @brief Histogram to depict frequencies of source symbols for rANS compression. + +#ifndef INCLUDE_RANS_FREQUENCYTABLE_H_ +#define INCLUDE_RANS_FREQUENCYTABLE_H_ + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <iostream> +#include <iterator> +#include <numeric> +#include <type_traits> +#include <vector> + +#include <fairlogger/Logger.h> + +#include "internal/helper.h" + +namespace o2 +{ +namespace rans +{ +class FrequencyTable; + +std::ostream& operator<<(std::ostream& out, const FrequencyTable& fTable); + +class FrequencyTable +{ + public: + using value_t = int32_t; + + FrequencyTable() : mMin(0), mMax(0), mNumSamples(0), mFrequencyTable(){}; + + FrequencyTable(value_t min, value_t max) : mMin(min), mMax(max), mNumSamples(0), mFrequencyTable(mMax - mMin + 1, 0) + { + assert(mMax >= mMin); + } + + FrequencyTable(size_t range) : FrequencyTable(0, internal::bitsToRange(range) - 1) + { + assert(range >= 1); + }; + + template <typename Source_IT> + void addSamples(Source_IT begin, Source_IT end, value_t min = 0, value_t max = 0); + + template <typename Freq_IT> + void addFrequencies(Freq_IT begin, Freq_IT end, value_t min, value_t max); + + value_t operator[](value_t index) const; + + size_t size() const; + + const uint32_t* data() const; + + const uint32_t* cbegin() const; + + const uint32_t* cend() const; + + uint32_t* begin(); + + uint32_t* end(); + + const uint32_t* begin() const; + + const uint32_t* end() const; + + FrequencyTable& operator+(FrequencyTable& other); + + value_t getMinSymbol() const; + value_t getMaxSymbol() const; + + size_t getAlphabetRangeBits() const; + + size_t getNumSamples() const; + + size_t getUsedAlphabetSize() const; + + private: + void resizeFrequencyTable(value_t min, value_t max); + + value_t mMin; + value_t mMax; + size_t mNumSamples; + std::vector<uint32_t> mFrequencyTable; +}; + +template <typename Source_IT> +void FrequencyTable::addSamples(Source_IT begin, Source_IT end, value_t min, value_t max) +{ + static_assert(std::is_integral<typename std::iterator_traits<Source_IT>::value_type>::value); + + LOG(trace) << "start adding samples"; + internal::RANSTimer t; + t.start(); + + if (begin == end) { + LOG(debug) << "Passed empty message to " << __func__; // RS this is ok for empty columns + return; + } + + if (min == max) { + const auto& [minIter, maxIter] = std::minmax_element(begin, end); + resizeFrequencyTable(*minIter, *maxIter); + + } else { + resizeFrequencyTable(min, max); + } + + for (auto it = begin; it != end; ++it) { + assert((*it - mMin) < mFrequencyTable.size()); + mFrequencyTable[*it - mMin]++; + } + mNumSamples = std::distance(begin, end); + + t.stop(); + LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); + +#if !defined(NDEBUG) + LOG(debug2) << *this; +#endif + LOG(trace) << "done adding samples"; +} + +template <typename Freq_IT> +void FrequencyTable::addFrequencies(Freq_IT begin, Freq_IT end, value_t min, value_t max) +{ + static_assert(std::is_integral<typename std::iterator_traits<Freq_IT>::value_type>::value); + + LOG(trace) << "start adding frequencies"; + internal::RANSTimer t; + t.start(); + + if (begin == end) { + LOG(debug) << "Passed empty FrequencyTable to " << __func__; // RS this is ok for empty columns + return; + } + + // ensure correct size of frequency table and grow it if necessary + resizeFrequencyTable(min, max); + + // either 0 or offset from array start. + const size_t offset = std::abs(mMin - min); + + // ftableA[i] += fTableB[i+offset] + std::transform(begin, end, + mFrequencyTable.begin() + offset, + mFrequencyTable.begin() + offset, + [this](typename std::iterator_traits<Freq_IT>::value_type first, uint32_t second) { + mNumSamples += first; + return first + second; }); + + t.stop(); + LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); + +#if !defined(NDEBUG) + LOG(debug2) << *this; +#endif + + LOG(trace) << "done adding frequencies"; +} + +inline typename FrequencyTable::value_t FrequencyTable::operator[](value_t index) const +{ + const value_t idx = index - mMin; + assert(idx >= 0); + assert(idx < mFrequencyTable.size()); + return mFrequencyTable[index]; +} + +inline size_t FrequencyTable::size() const +{ + return mFrequencyTable.size(); +} + +inline const uint32_t* FrequencyTable::data() const +{ + return mFrequencyTable.data(); +} + +inline const uint32_t* FrequencyTable::cbegin() const +{ + return mFrequencyTable.data(); +} + +inline const uint32_t* FrequencyTable::cend() const +{ + return mFrequencyTable.data() + mFrequencyTable.size(); +} + +inline uint32_t* FrequencyTable::begin() +{ + return const_cast<uint32_t*>(static_cast<const FrequencyTable*>(this)->begin()); +} + +inline uint32_t* FrequencyTable::end() +{ + return const_cast<uint32_t*>(static_cast<const FrequencyTable*>(this)->end()); +} + +inline const uint32_t* FrequencyTable::begin() const +{ + return cbegin(); +} + +inline const uint32_t* FrequencyTable::end() const +{ + return cend(); +} + +inline FrequencyTable& FrequencyTable::operator+(FrequencyTable& other) +{ + addFrequencies(std::begin(other), std::end(other), other.getMinSymbol(), other.getMaxSymbol()); + return *this; +} + +inline typename FrequencyTable::value_t FrequencyTable::getMinSymbol() const +{ + return mMin; +} + +inline typename FrequencyTable::value_t FrequencyTable::getMaxSymbol() const +{ + return mMax; +} + +inline size_t FrequencyTable::getAlphabetRangeBits() const +{ + if (mMax - mMin > 0) { + return std::ceil(std::log2(mMax - mMin)); + } else if (mMax - mMin == 0) { + return 1; + } else { + return 0; + } +} + +inline size_t FrequencyTable::getNumSamples() const +{ + return mNumSamples; +} + +} // namespace rans +} // namespace o2 + +#endif /* INCLUDE_RANS_FREQUENCYTABLE_H_ */ diff --git a/Utilities/rANS/include/rANS/LiteralDecoder.h b/Utilities/rANS/include/rANS/LiteralDecoder.h new file mode 100644 index 0000000000000..4e8b2716184c9 --- /dev/null +++ b/Utilities/rANS/include/rANS/LiteralDecoder.h @@ -0,0 +1,108 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Decoder.h +/// @author Michael Lettrich +/// @since 2020-04-06 +/// @brief Decoder - decode a rANS encoded state back into source symbols + +#ifndef RANS_LITERALDECODER_H +#define RANS_LITERALDECODER_H + +#include "Decoder.h" + +#include <cstddef> +#include <type_traits> +#include <iostream> +#include <string> + +#include <fairlogger/Logger.h> + +#include "internal/DecoderSymbol.h" +#include "internal/ReverseSymbolLookupTable.h" +#include "internal/SymbolTable.h" +#include "internal/Decoder.h" + +namespace o2 +{ +namespace rans +{ + +template <typename coder_T, typename stream_T, typename source_T> +class LiteralDecoder : public Decoder<coder_T, stream_T, source_T> +{ + //inherit constructors; + using Decoder<coder_T, stream_T, source_T>::Decoder; + + public: + template <typename stream_IT, typename source_IT> + void process(const source_IT outputBegin, const stream_IT inputEnd, size_t messageLength, std::vector<source_T>& literals) const; +}; + +template <typename coder_T, typename stream_T, typename source_T> +template <typename stream_IT, typename source_IT> +void LiteralDecoder<coder_T, stream_T, source_T>::process(const source_IT outputBegin, const stream_IT inputEnd, size_t messageLength, std::vector<source_T>& literals) const +{ + using namespace internal; + using ransDecoder = internal::Decoder<coder_T, stream_T>; + LOG(trace) << "start decoding"; + RANSTimer t; + t.start(); + static_assert(std::is_same<typename std::iterator_traits<source_IT>::value_type, source_T>::value); + static_assert(std::is_same<typename std::iterator_traits<stream_IT>::value_type, stream_T>::value); + + if (messageLength == 0) { + LOG(warning) << "Empty message passed to decoder, skipping decode process"; + return; + } + + stream_IT inputIter = inputEnd; + source_IT it = outputBegin; + + auto decode = [&, this](ransDecoder& decoder) { + auto cumul = decoder.get(this->mProbabilityBits); + const auto streamSymbol = (*this->mReverseLUT)[cumul]; + source_T symbol = streamSymbol; + if (this->mSymbolTable->isRareSymbol(streamSymbol)) { + symbol = literals.back(); + literals.pop_back(); + } + + return std::make_tuple(symbol, decoder.advanceSymbol(inputIter, (*this->mSymbolTable)[streamSymbol], this->mProbabilityBits)); + }; + + // make Iter point to the last last element + --inputIter; + + ransDecoder rans0, rans1; + inputIter = rans0.init(inputIter); + inputIter = rans1.init(inputIter); + + for (size_t i = 0; i < (messageLength & ~1); i += 2) { + std::tie(*it++, inputIter) = decode(rans0); + std::tie(*it++, inputIter) = decode(rans1); + } + + // last byte, if message length was odd + if (messageLength & 1) { + std::tie(*it++, inputIter) = decode(rans0); + } + t.stop(); + LOG(debug1) << "Decoder::" << __func__ << " { DecodedSymbols: " << messageLength << "," + << "processedBytes: " << messageLength * sizeof(source_T) << "," + << " inclusiveTimeMS: " << t.getDurationMS() << "," + << " BandwidthMiBPS: " << std::fixed << std::setprecision(2) << (messageLength * sizeof(source_T) * 1.0) / (t.getDurationS() * 1.0 * (1 << 20)) << "}"; + + LOG(trace) << "done decoding"; +} +} // namespace rans +} // namespace o2 + +#endif /* RANS_LITERALDECODER_H */ diff --git a/Utilities/rANS/include/rANS/LiteralEncoder.h b/Utilities/rANS/include/rANS/LiteralEncoder.h new file mode 100644 index 0000000000000..989ee035cb5e5 --- /dev/null +++ b/Utilities/rANS/include/rANS/LiteralEncoder.h @@ -0,0 +1,148 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Encoder.h +/// @author Michael Lettrich +/// @since 2020-04-06 +/// @brief Encoder - code symbol into a rANS encoded state + +#ifndef RANS_LITERAL_ENCODER_H +#define RANS_LITERAL_ENCODER_H + +#include "Encoder.h" + +#include <memory> +#include <algorithm> +#include <iomanip> + +#include <fairlogger/Logger.h> +#include <stdexcept> + +#include "internal/EncoderSymbol.h" +#include "internal/helper.h" +#include "internal/SymbolTable.h" + +namespace o2 +{ +namespace rans +{ + +template <typename coder_T, typename stream_T, typename source_T> +class LiteralEncoder : public Encoder<coder_T, stream_T, source_T> +{ + //inherit constructors; + using Encoder<coder_T, stream_T, source_T>::Encoder; + + public: + template <typename stream_IT, typename source_IT> + const stream_IT process(const stream_IT outputBegin, const stream_IT outputEnd, + const source_IT inputBegin, source_IT inputEnd, std::vector<source_T>& literals) const; +}; + +template <typename coder_T, typename stream_T, typename source_T> +template <typename stream_IT, typename source_IT> +const stream_IT LiteralEncoder<coder_T, stream_T, source_T>::process(const stream_IT outputBegin, const stream_IT outputEnd, const source_IT inputBegin, const source_IT inputEnd, std::vector<source_T>& literals) const +{ + using namespace internal; + using ransCoder = internal::Encoder<coder_T, stream_T>; + LOG(trace) << "start encoding"; + RANSTimer t; + t.start(); + + static_assert(std::is_same<typename std::iterator_traits<source_IT>::value_type, source_T>::value); + static_assert(std::is_same<typename std::iterator_traits<stream_IT>::value_type, stream_T>::value); + + if (inputBegin == inputEnd) { + LOG(warning) << "passed empty message to encoder, skip encoding"; + return outputEnd; + } + + if (outputBegin == outputEnd) { + const std::string errorMessage("Unallocated encode buffer passed to encoder. Aborting"); + LOG(error) << errorMessage; + throw std::runtime_error(errorMessage); + } + + ransCoder rans0, rans1; + + stream_IT outputIter = outputBegin; + source_IT inputIT = inputEnd; + + const auto inputBufferSize = std::distance(inputBegin, inputEnd); + + auto encode = [&literals, this](source_IT symbolIter, stream_IT outputIter, ransCoder& coder) { + const source_T symbol = *symbolIter; + const auto& encoderSymbol = (*this->mSymbolTable)[symbol]; + if (this->mSymbolTable->isRareSymbol(symbol)) { + literals.push_back(symbol); + } + return std::tuple(symbolIter, coder.putSymbol(outputIter, encoderSymbol, this->mProbabilityBits)); + }; + + // odd number of bytes? + if (inputBufferSize & 1) { + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans0); + assert(outputIter < outputEnd); + } + + while (inputIT > inputBegin) { // NB: working in reverse! + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans1); + std::tie(inputIT, outputIter) = encode(--inputIT, outputIter, rans0); + assert(outputIter < outputEnd); + } + outputIter = rans1.flush(outputIter); + outputIter = rans0.flush(outputIter); + // first iterator past the range so that sizes, distances and iterators work correctly. + ++outputIter; + + assert(!(outputIter > outputEnd)); + + // deal with overflow + if (outputIter > outputEnd) { + const std::string exceptionText = [&]() { + std::stringstream ss; + ss << __func__ << " detected overflow in encode buffer: allocated:" << std::distance(outputBegin, outputEnd) << ", used:" << std::distance(outputBegin, outputIter); + return ss.str(); + }(); + + LOG(error) << exceptionText; + throw std::runtime_error(exceptionText); + } + + t.stop(); + LOG(debug1) << "Encoder::" << __func__ << " {ProcessedBytes: " << inputBufferSize * sizeof(source_T) << "," + << " inclusiveTimeMS: " << t.getDurationMS() << "," + << " BandwidthMiBPS: " << std::fixed << std::setprecision(2) << (inputBufferSize * sizeof(source_T) * 1.0) / (t.getDurationS() * 1.0 * (1 << 20)) << "}"; + +// advanced diagnostics for debug builds +#if !defined(NDEBUG) + + const auto inputBufferSizeB = inputBufferSize * sizeof(source_T); + const auto outputBufferSizeB = std::distance(outputBegin, outputIter) * sizeof(stream_T); + + LOG(debug2) << "EncoderProperties: {" + << "sourceTypeB: " << sizeof(source_T) << ", " + << "streamTypeB: " << sizeof(stream_T) << ", " + << "coderTypeB: " << sizeof(coder_T) << ", " + << "probabilityBits: " << this->mProbabilityBits << ", " + << "inputBufferSizeB: " << inputBufferSizeB << ", " + << "outputBufferSizeB: " << outputBufferSizeB << ", " + << "compressionFactor: " << std::fixed << std::setprecision(2) << static_cast<double>(inputBufferSizeB) / static_cast<double>(outputBufferSizeB) << "}"; +#endif + + LOG(trace) << "done encoding"; + + return outputIter; +}; + +} // namespace rans +} // namespace o2 + +#endif /* RANS_LITERAL_ENCODER_H */ diff --git a/Utilities/rANS/include/rANS/ReverseSymbolLookupTable.h b/Utilities/rANS/include/rANS/ReverseSymbolLookupTable.h deleted file mode 100644 index ec6d306009830..0000000000000 --- a/Utilities/rANS/include/rANS/ReverseSymbolLookupTable.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 ReverseSymbolLookupTable.h -/// @author Michael Lettrich -/// @since 2020-04-06 -/// @brief Maps CDF back to source symbol - needed for the decoder - -#ifndef RANS_REVERSESYMBOLLOOKUPTABLE_H -#define RANS_REVERSESYMBOLLOOKUPTABLE_H - -#include <vector> -#include <type_traits> -#include <fairlogger/Logger.h> - -#include "SymbolStatistics.h" -#include "helper.h" - -namespace o2 -{ -namespace rans -{ -template <typename source_t> -class ReverseSymbolLookupTable -{ - public: - ReverseSymbolLookupTable(size_t probabilityBits, - const SymbolStatistics& stats) : mLut() - { - LOG(trace) << "start building reverse symbol lookup table"; - - if (stats.getAlphabetSize() == 0) { - LOG(warning) << "SymbolStatistics of empty message passed to " << __func__; - return; - } - - mLut.resize(bitsToRange(probabilityBits)); - // go over all symbols - for (int symbol = stats.getMinSymbol(); symbol <= stats.getMaxSymbol(); - symbol++) { - for (uint32_t cumulative = stats[symbol].second; - cumulative < stats[symbol].second + stats[symbol].first; cumulative++) { - mLut[cumulative] = symbol; - } - } - -// advanced diagnostics for debug builds -#if !defined(NDEBUG) - LOG(debug2) << "reverseSymbolLookupTableProperties: {" - << "elements: " << mLut.size() << ", " - << "sizeB: " << mLut.size() * sizeof(typename std::decay_t<decltype(mLut)>::value_type) << "}"; -#endif - - LOG(trace) << "done building reverse symbol lookup table"; - }; - - inline source_t operator[](size_t cummulative) const - { - return mLut[cummulative]; - }; - - private: - std::vector<source_t> mLut; -}; - -} // namespace rans -} // namespace o2 - -#endif /* RANS_REVERSESYMBOLLOOKUPTABLE_H */ diff --git a/Utilities/rANS/include/rANS/SymbolStatistics.h b/Utilities/rANS/include/rANS/SymbolStatistics.h deleted file mode 100644 index c37bef0a18664..0000000000000 --- a/Utilities/rANS/include/rANS/SymbolStatistics.h +++ /dev/null @@ -1,224 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SymbolStatistics.h -/// @author Michael Lettrich -/// @since 2019-05-08 -/// @brief Structure to depict the distribution of symbols in the source message. - -#ifndef RANS_SYMBOLSTATISTICS_H -#define RANS_SYMBOLSTATISTICS_H - -#include <algorithm> -#include <cassert> -#include <iostream> -#include <numeric> -#include <vector> -#include <cmath> -#include <iterator> - -#include <fairlogger/Logger.h> - -#include "helper.h" - -namespace o2 -{ -namespace rans -{ - -class SymbolStatistics -{ - public: - class Iterator - { - public: - Iterator(size_t index, const SymbolStatistics& stats); - - using difference_type = int64_t; - using value_type = std::pair<uint32_t, uint32_t>; - using pointer = const std::pair<uint32_t, uint32_t>*; - using reference = const std::pair<uint32_t, uint32_t>&; - using iterator_category = std::random_access_iterator_tag; - - const Iterator& operator++(); - - value_type operator*() const; - - bool operator!=(const Iterator& other) const; - - private: - size_t mIndex; - const SymbolStatistics& mStats; - }; - - public: - template <typename IT> - SymbolStatistics(const IT begin, const IT end, size_t range = 0); - - template <typename IT> - SymbolStatistics(const IT begin, const IT end, size_t min, size_t max, size_t messageLength); - - void rescaleToNBits(size_t bits); - - int getMinSymbol() const; - int getMaxSymbol() const; - size_t getAlphabetSize() const; - size_t getAlphabetRangeBits() const; - size_t getNUsedAlphabetSymbols() const; - - size_t getMessageLength() const; - - std::pair<uint32_t, uint32_t> operator[](size_t index) const; - - SymbolStatistics::Iterator begin() const; - SymbolStatistics::Iterator end() const; - - const auto& getFrequencyTable() const { return mFrequencyTable; } - - private: - void buildCumulativeFrequencyTable(); - - template <typename IT> - void buildFrequencyTable(const IT begin, const IT end, size_t range); - - int mMin; - int mMax; - size_t mNUsedAlphabetSymbols; - size_t mMessageLength; - - std::vector<uint32_t> mFrequencyTable; - std::vector<uint32_t> mCumulativeFrequencyTable; -}; - -std::ostream& operator<<(std::ostream& strm, const SymbolStatistics& a); - -template <typename IT> -SymbolStatistics::SymbolStatistics(const IT begin, const IT end, size_t range) : mMin(0), mMax(0), mNUsedAlphabetSymbols(0), mMessageLength(0), mFrequencyTable(), mCumulativeFrequencyTable() -{ - LOG(trace) << "start building symbol statistics"; - RANSTimer t; - t.start(); - if (begin == end) { - LOG(warning) << "Passed empty message to " << __func__; - return; - } - - buildFrequencyTable(begin, end, range); - - for (auto i : mFrequencyTable) { - if (i > 0) { - mNUsedAlphabetSymbols++; - } - } - - buildCumulativeFrequencyTable(); - - mMessageLength = mCumulativeFrequencyTable.back(); - - assert(mFrequencyTable.size() > 0); - assert(mCumulativeFrequencyTable.size() > 1); - assert(mCumulativeFrequencyTable.size() - mFrequencyTable.size() == 1); - - t.stop(); - LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); - -// advanced diagnostics in debug builds -#if !defined(NDEBUG) - [&]() { - const uint messageRange = [&]() -> uint { - if (range > 0) { - return range; - } else if (mMax - mMin == 0) { - return 1; - } else { - return std::ceil(std::log2(mMax - mMin)); - } }(); - - double entropy = 0; - for (auto frequency : mFrequencyTable) { - if (frequency > 0) { - const double p = (frequency * 1.0) / mMessageLength; - entropy -= p * std::log2(p); - } - } - - LOG(debug2) << "messageProperties: {" - << "numSymbols: " << mMessageLength << ", " - << "alphabetRange: " << messageRange << ", " - << "alphabetSize: " << mNUsedAlphabetSymbols << ", " - << "minSymbol: " << mMin << ", " - << "maxSymbol: " << mMax << ", " - << "entropy: " << entropy << ", " - << "bufferSizeB: " << mMessageLength * sizeof(typename std::iterator_traits<IT>::value_type) << ", " - << "actualSizeB: " << static_cast<int>(mMessageLength * messageRange / 8) << ", " - << "entropyMessageB: " << static_cast<int>(std::ceil(entropy * mMessageLength / 8)) << "}"; - - LOG(debug2) << "SymbolStatistics: {" - << "entries: " << mFrequencyTable.size() << ", " - << "frequencyTableSizeB: " << mFrequencyTable.size() * sizeof(std::decay_t<decltype(mFrequencyTable)>::value_type) << ", " - << "CumulativeFrequencyTableSizeB: " << mCumulativeFrequencyTable.size() * sizeof(std::decay_t<decltype(mCumulativeFrequencyTable)>::value_type) << "}"; - }(); -#endif - - LOG(trace) << "done building symbol statistics"; -} - -template <typename IT> -SymbolStatistics::SymbolStatistics(const IT begin, const IT end, size_t min, size_t max, size_t messageLength) : mMin(min), mMax(max), mNUsedAlphabetSymbols(0), mMessageLength(messageLength), mFrequencyTable(begin, end), mCumulativeFrequencyTable() -{ - LOG(trace) << "start loading external symbol statistics"; - for (auto i : mFrequencyTable) { - if (i > 0) { - mNUsedAlphabetSymbols++; - } - } - - buildCumulativeFrequencyTable(); - - LOG(trace) << "done loading external symbol statistics"; -} - -template <typename IT> -void SymbolStatistics::buildFrequencyTable(const IT begin, const IT end, - size_t range) -{ - LOG(trace) << "start building frequency table"; - // find min_ and max_ - const auto minmax = std::minmax_element(begin, end); - - if (range > 0) { - mMin = 0; - mMax = (1 << range) - 1; - - // do checks - if (static_cast<unsigned int>(mMin) > *minmax.first) { - throw std::runtime_error("min of data too small for specified range"); - } - - if (static_cast<unsigned int>(mMax) < *minmax.second) { - throw std::runtime_error("max of data too big for specified range"); - } - } else { - mMin = *minmax.first; - mMax = *minmax.second; - } - assert(mMax >= mMin); - - mFrequencyTable.resize(std::abs(mMax - mMin) + 1, 0); - - for (IT it = begin; it != end; it++) { - mFrequencyTable[*it - mMin]++; - } - LOG(trace) << "done building frequency table"; -} -} // namespace rans -} // namespace o2 - -#endif /* RANS_SYMBOLSTATISTICS_H */ diff --git a/Utilities/rANS/include/rANS/SymbolTable.h b/Utilities/rANS/include/rANS/SymbolTable.h deleted file mode 100644 index 0c421cfce97f2..0000000000000 --- a/Utilities/rANS/include/rANS/SymbolTable.h +++ /dev/null @@ -1,69 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 SymbolTable.h -/// @author Michael Lettrich -/// @since 2019-06-21 -/// @brief Container for information needed to encode/decode a symbol of the alphabet - -#ifndef RANS_SYMBOLTABLE_H -#define RANS_SYMBOLTABLE_H - -#include <vector> -#include <cstdint> - -#include <fairlogger/Logger.h> - -#include "SymbolStatistics.h" - -namespace o2 -{ -namespace rans -{ - -template <typename T> -class SymbolTable -{ - public: - SymbolTable(const SymbolStatistics& symbolStats, uint64_t probabiltyBits) : mMin(symbolStats.getMinSymbol()) - { - LOG(trace) << "start building symbol table"; - mSymbolTable.reserve(symbolStats.getAlphabetSize()); - - for (const auto& entry : symbolStats) { - mSymbolTable.emplace_back(entry.second, entry.first, probabiltyBits); - } - -// advanced diagnostics for debug builds -#if !defined(NDEBUG) - LOG(debug2) << "SymbolTableProperties: {" - << "entries:" << mSymbolTable.size() << ", " - << "sizeB: " << mSymbolTable.size() * sizeof(T) << "}"; -#endif - - LOG(trace) << "done building symbol table"; - } - - const T& operator[](int index) const - { - auto idx = index - mMin; - assert(idx >= 0); - assert(idx < mSymbolTable.size()); - return mSymbolTable[idx]; - } - - private: - int mMin; - std::vector<T> mSymbolTable; -}; -} // namespace rans -} // namespace o2 - -#endif /* RANS_SYMBOLTABLE_H */ diff --git a/Utilities/rANS/include/rANS/helper.h b/Utilities/rANS/include/rANS/helper.h deleted file mode 100644 index 72146eec943cf..0000000000000 --- a/Utilities/rANS/include/rANS/helper.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does 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 helper.h -/// @author Michael Lettrich -/// @since 2019-05-21 -/// @brief various helper functions - -#ifndef RANS_HELPER_H -#define RANS_HELPER_H - -#include <cstddef> -#include <cmath> -#include <chrono> - -namespace o2 -{ -namespace rans -{ - -template <typename T> -inline constexpr bool needs64Bit() -{ - return sizeof(T) > 4; -} - -inline constexpr size_t bitsToRange(size_t bits) -{ - return 1 << bits; -} - -inline size_t calculateMaxBufferSize(size_t num, size_t rangeBits, size_t sizeofStreamT) -{ - // RS: w/o safety margin the o2-test-ctf-io produces an overflow in the Encoder::process - constexpr size_t SaferyMargin = 16; - return std::ceil(1.20 * (num * rangeBits * 1.0) / (sizeofStreamT * 8.0)) + SaferyMargin; -} - -//rans default values -constexpr size_t ProbabilityBits8Bit = 10; -constexpr size_t ProbabilityBits16Bit = 22; -constexpr size_t ProbabilityBits25Bit = 25; - -class RANSTimer -{ - public: - void start() { mStart = std::chrono::high_resolution_clock::now(); }; - void stop() { mStop = std::chrono::high_resolution_clock::now(); }; - double getDurationMS() - { - std::chrono::duration<double, std::milli> duration = mStop - mStart; - return duration.count(); - } - - private: - std::chrono::time_point<std::chrono::high_resolution_clock> mStart; - std::chrono::time_point<std::chrono::high_resolution_clock> mStop; -}; - -} // namespace rans -} // namespace o2 - -#endif /* RANS_HELPER_H */ diff --git a/Utilities/rANS/include/rANS/internal/Decoder.h b/Utilities/rANS/include/rANS/internal/Decoder.h new file mode 100644 index 0000000000000..d983a1f91a34d --- /dev/null +++ b/Utilities/rANS/include/rANS/internal/Decoder.h @@ -0,0 +1,165 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Decoder.h +/// @author Michael Lettrich +/// @since 2019-05-10 +/// @brief lass for decoding symbols using rANS + +#ifndef RANS_INTERNAL_DECODER_H_ +#define RANS_INTERNAL_DECODER_H_ + +#include <vector> +#include <cstdint> +#include <cassert> +#include <tuple> +#include <type_traits> + +#include "DecoderSymbol.h" +#include "EncoderSymbol.h" +#include "helper.h" + +namespace o2 +{ +namespace rans +{ +namespace internal +{ +__extension__ typedef unsigned __int128 uint128; + +template <typename State_T, typename Stream_T> +class Decoder +{ + // the Coder works either with a 64Bit state and 32 bit streaming or + //a 32 Bit state and 8 Bit streaming We need to make sure it gets initialized with + //the right template arguments at compile time. + static_assert((sizeof(State_T) == sizeof(uint32_t) && sizeof(Stream_T) == sizeof(uint8_t)) || + (sizeof(State_T) == sizeof(uint64_t) && sizeof(Stream_T) == sizeof(uint32_t)), + "Coder can either be 32Bit with 8 Bit stream type or 64 Bit Type with 32 Bit stream type"); + + public: + Decoder(); + + // Initializes a rANS decoder. + // Unlike the encoder, the decoder works forwards as you'd expect. + template <typename Stream_IT> + Stream_IT init(Stream_IT iter); + + // Returns the current cumulative frequency (map it to a symbol yourself!) + uint32_t get(uint32_t scale_bits); + + // Equivalent to Rans32DecAdvance that takes a symbol. + template <typename Stream_IT> + Stream_IT advanceSymbol(Stream_IT iter, const DecoderSymbol& sym, uint32_t scale_bits); + + private: + State_T mState; + + // Renormalize. + template <typename Stream_IT> + std::tuple<State_T, Stream_IT> renorm(State_T x, Stream_IT iter); + + // L ('l' in the paper) is the lower bound of our normalization interval. + // Between this and our byte-aligned emission, we use 31 (not 32!) bits. + // This is done intentionally because exact reciprocals for 31-bit uints + // fit in 32-bit uints: this permits some optimizations during encoding. + inline static constexpr State_T LOWER_BOUND = needs64Bit<State_T>() ? (1u << 31) : (1u << 23); // lower bound of our normalization interval + + inline static constexpr State_T STREAM_BITS = sizeof(Stream_T) * 8; // lower bound of our normalization interval +}; + +template <typename State_T, typename Stream_T> +Decoder<State_T, Stream_T>::Decoder() : mState(0){}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +Stream_IT Decoder<State_T, Stream_T>::init(Stream_IT iter) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + + State_T x = 0; + Stream_IT streamPos = iter; + + if constexpr (needs64Bit<State_T>()) { + x = static_cast<State_T>(*streamPos) << 0; + --streamPos; + x |= static_cast<State_T>(*streamPos) << 32; + --streamPos; + assert(std::distance(streamPos, iter) == 2); + } else { + x = static_cast<State_T>(*streamPos) << 0; + --streamPos; + x |= static_cast<State_T>(*streamPos) << 8; + --streamPos; + x |= static_cast<State_T>(*streamPos) << 16; + --streamPos; + x |= static_cast<State_T>(*streamPos) << 24; + --streamPos; + assert(std::distance(streamPos, iter) == 4); + } + + mState = x; + return streamPos; +}; + +template <typename State_T, typename Stream_T> +uint32_t Decoder<State_T, Stream_T>::get(uint32_t scale_bits) +{ + return mState & ((1u << scale_bits) - 1); +}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +Stream_IT Decoder<State_T, Stream_T>::advanceSymbol(Stream_IT iter, const DecoderSymbol& sym, uint32_t scale_bits) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + + State_T mask = (1ull << scale_bits) - 1; + + // s, x = D(x) + State_T x = mState; + x = sym.freq * (x >> scale_bits) + (x & mask) - sym.start; + + // renormalize + Stream_IT streamPos; + std::tie(mState, streamPos) = this->renorm(x, iter); + return streamPos; +}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +inline std::tuple<State_T, Stream_IT> Decoder<State_T, Stream_T>::renorm(State_T x, Stream_IT iter) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + + Stream_IT streamPos = iter; + + // renormalize + if (x < LOWER_BOUND) { + if constexpr (needs64Bit<State_T>()) { + x = (x << STREAM_BITS) | *streamPos; + --streamPos; + assert(x >= LOWER_BOUND); + } else { + + do { + x = (x << STREAM_BITS) | *streamPos; + --streamPos; + } while (x < LOWER_BOUND); + } + } + return std::make_tuple(x, streamPos); +} + +} // namespace internal +} // namespace rans +} // namespace o2 + +#endif /* RANS_INTERNAL_DECODER_H_ */ diff --git a/Utilities/rANS/include/rANS/DecoderSymbol.h b/Utilities/rANS/include/rANS/internal/DecoderSymbol.h similarity index 88% rename from Utilities/rANS/include/rANS/DecoderSymbol.h rename to Utilities/rANS/include/rANS/internal/DecoderSymbol.h index d022b3f8bdadf..b35a690fd8b3b 100644 --- a/Utilities/rANS/include/rANS/DecoderSymbol.h +++ b/Utilities/rANS/include/rANS/internal/DecoderSymbol.h @@ -13,8 +13,8 @@ /// @since 2019-05-21 /// @brief Structure containing all relevant information for decoding a rANS encoded symbol -#ifndef RANS_DECODERSYMBOL_H -#define RANS_DECODERSYMBOL_H +#ifndef RANS_INTERNAL_DECODERSYMBOL_H +#define RANS_INTERNAL_DECODERSYMBOL_H #include <cstdint> @@ -22,6 +22,8 @@ namespace o2 { namespace rans { +namespace internal +{ // Decoder symbols are straightforward. struct DecoderSymbol { @@ -37,8 +39,8 @@ struct DecoderSymbol { uint32_t start; // Start of range. uint32_t freq; // Symbol frequency. }; - +} // namespace internal } // namespace rans } // namespace o2 -#endif /* RANS_DECODERSYMBOL_H */ +#endif /* RANS_INTERNAL_DECODERSYMBOL_H */ diff --git a/Utilities/rANS/include/rANS/internal/Encoder.h b/Utilities/rANS/include/rANS/internal/Encoder.h new file mode 100644 index 0000000000000..c3e9c9fb04b89 --- /dev/null +++ b/Utilities/rANS/include/rANS/internal/Encoder.h @@ -0,0 +1,195 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 Encoder.h +/// @author Michael Lettrich +/// @since 2019-05-10 +/// @brief class for encoding symbols using rANS + +#ifndef RANS_INTERNAL_ENCODER_H +#define RANS_INTERNAL_ENCODER_H + +#include <vector> +#include <cstdint> +#include <cassert> +#include <type_traits> +#include <tuple> + +#include "DecoderSymbol.h" +#include "EncoderSymbol.h" +#include "helper.h" + +namespace o2 +{ +namespace rans +{ +namespace internal +{ + +template <typename State_T, typename Stream_T> +class Encoder +{ + __extension__ typedef unsigned __int128 uint128; + + // the Coder works either with a 64Bit state and 32 bit streaming or + //a 32 Bit state and 8 Bit streaming We need to make sure it gets initialized with + //the right template arguments at compile time. + static_assert((sizeof(State_T) == sizeof(uint32_t) && sizeof(Stream_T) == sizeof(uint8_t)) || + (sizeof(State_T) == sizeof(uint64_t) && sizeof(Stream_T) == sizeof(uint32_t)), + "Coder can either be 32Bit with 8 Bit stream type or 64 Bit Type with 32 Bit stream type"); + + public: + Encoder(); + + // Encodes a single symbol with range start "start" and frequency "freq". + // All frequencies are assumed to sum to "1 << scale_bits", and the + // resulting bytes get written to ptr (which is updated). + // + // NOTE: With rANS, you need to encode symbols in *reverse order*, i.e. from + // beginning to end! Likewise, the output bytestream is written *backwards*: + // ptr starts pointing at the end of the output buffer and keeps decrementing. + template <typename Stream_IT> + Stream_IT put(Stream_IT iter, uint32_t start, uint32_t freq, uint32_t scale_bits); + + // Flushes the rANS encoder. + template <typename Stream_IT> + Stream_IT flush(Stream_IT iter); + + // Encodes a given symbol. This is faster than straight RansEnc since we can do + // multiplications instead of a divide. + // + // See Rans32EncSymbolInit for a description of how this works. + template <typename Stream_IT> + Stream_IT putSymbol(Stream_IT iter, const EncoderSymbol<State_T>& sym, uint32_t scale_bits); + + private: + State_T mState; + + // Renormalize the encoder. + template <typename Stream_IT> + std::tuple<State_T, Stream_IT> renorm(State_T x, Stream_IT iter, uint32_t freq, uint32_t scale_bits); + + // L ('l' in the paper) is the lower bound of our normalization interval. + // Between this and our byte-aligned emission, we use 31 (not 32!) bits. + // This is done intentionally because exact reciprocals for 31-bit uints + // fit in 32-bit uints: this permits some optimizations during encoding. + inline static constexpr State_T LOWER_BOUND = needs64Bit<State_T>() ? (1u << 31) : (1u << 23); // lower bound of our normalization interval + + inline static constexpr State_T STREAM_BITS = sizeof(Stream_T) * 8; // lower bound of our normalization interval +}; + +template <typename State_T, typename Stream_T> +Encoder<State_T, Stream_T>::Encoder() : mState(LOWER_BOUND){}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +Stream_IT Encoder<State_T, Stream_T>::put(Stream_IT iter, uint32_t start, uint32_t freq, uint32_t scale_bits) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + // renormalize + Stream_IT streamPos; + State_T x; + std::tie(x, streamPos) = renorm(mState, iter, freq, scale_bits); + + // x = C(s,x) + mState = ((x / freq) << scale_bits) + (x % freq) + start; + return streamPos; +}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +Stream_IT Encoder<State_T, Stream_T>::flush(Stream_IT iter) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + + Stream_IT streamPos = iter; + + if constexpr (needs64Bit<State_T>()) { + ++streamPos; + *streamPos = static_cast<Stream_T>(mState >> 32); + ++streamPos; + *streamPos = static_cast<Stream_T>(mState >> 0); + assert(std::distance(iter, streamPos) == 2); + } else { + ++streamPos; + *streamPos = static_cast<Stream_T>(mState >> 24); + ++streamPos; + *streamPos = static_cast<Stream_T>(mState >> 16); + ++streamPos; + *streamPos = static_cast<Stream_T>(mState >> 8); + ++streamPos; + *streamPos = static_cast<Stream_T>(mState >> 0); + assert(std::distance(iter, streamPos) == 4); + } + + mState = 0; + return streamPos; +}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +Stream_IT Encoder<State_T, Stream_T>::putSymbol(Stream_IT iter, const EncoderSymbol<State_T>& sym, uint32_t scale_bits) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + + assert(sym.freq != 0); // can't encode symbol with freq=0 + + // renormalize + Stream_IT streamPos; + State_T x; + std::tie(x, streamPos) = renorm(mState, iter, sym.freq, scale_bits); + + // x = C(s,x) + State_T q = 0; + + if constexpr (needs64Bit<State_T>()) { + // This code needs support for 64-bit long multiplies with 128-bit result + // (or more precisely, the top 64 bits of a 128-bit result). + q = static_cast<State_T>((static_cast<uint128>(x) * sym.rcp_freq) >> 64); + } else { + q = static_cast<State_T>((static_cast<uint64_t>(x) * sym.rcp_freq) >> 32); + } + q = q >> sym.rcp_shift; + + mState = x + sym.bias + q * sym.cmpl_freq; + return streamPos; +}; + +template <typename State_T, typename Stream_T> +template <typename Stream_IT> +inline std::tuple<State_T, Stream_IT> Encoder<State_T, Stream_T>::renorm(State_T x, Stream_IT iter, uint32_t freq, uint32_t scale_bits) +{ + static_assert(std::is_same<typename std::iterator_traits<Stream_IT>::value_type, Stream_T>::value); + + Stream_IT streamPos = iter; + + State_T x_max = ((LOWER_BOUND >> scale_bits) << STREAM_BITS) * freq; // this turns into a shift. + if (x >= x_max) { + if constexpr (needs64Bit<State_T>()) { + ++streamPos; + *streamPos = static_cast<Stream_T>(x); + x >>= STREAM_BITS; + assert(x < x_max); + } else { + do { + ++streamPos; + *streamPos = static_cast<Stream_T>(x & 0xff); + x >>= STREAM_BITS; + } while (x >= x_max); + } + } + return std::make_tuple(x, streamPos); +}; + +} // namespace internal +} // namespace rans +} // namespace o2 + +#endif /* RANS_INTERNAL_ENCODER_H */ diff --git a/Utilities/rANS/include/rANS/EncoderSymbol.h b/Utilities/rANS/include/rANS/internal/EncoderSymbol.h similarity index 95% rename from Utilities/rANS/include/rANS/EncoderSymbol.h rename to Utilities/rANS/include/rANS/internal/EncoderSymbol.h index 8499a8afbd95f..08fccf7fff284 100644 --- a/Utilities/rANS/include/rANS/EncoderSymbol.h +++ b/Utilities/rANS/include/rANS/internal/EncoderSymbol.h @@ -13,8 +13,8 @@ /// @since 2019-05-21 /// @brief Structure containing all relevant information to encode a symbol. -#ifndef RANS_ENCODERSYMBOL_H -#define RANS_ENCODERSYMBOL_H +#ifndef RANS_INTERNAL_ENCODERSYMBOL_H +#define RANS_INTERNAL_ENCODERSYMBOL_H #include <cstdint> #include <cassert> @@ -25,7 +25,8 @@ namespace o2 { namespace rans { - +namespace internal +{ // Encoder symbol description // This (admittedly odd) selection of parameters was chosen to make // RansEncPutSymbol as cheap as possible. @@ -91,8 +92,9 @@ struct EncoderSymbol { // Alverson, "Integer Division using reciprocals" // shift=ceil(log2(freq)) uint32_t shift = 0; - while (freq > (1u << shift)) + while (freq > (1u << shift)) { shift++; + } if constexpr (needs64Bit<T>()) { uint64_t x0, x1, t0, t1; @@ -125,7 +127,8 @@ struct EncoderSymbol { uint32_t rcp_shift; // Reciprocal shift }; +} // namespace internal } //namespace rans } //namespace o2 -#endif /* RANS_ENCODERSYMBOL_H */ +#endif /* RANS_INTERNAL_ENCODERSYMBOL_H */ diff --git a/Utilities/rANS/include/rANS/internal/ReverseSymbolLookupTable.h b/Utilities/rANS/include/rANS/internal/ReverseSymbolLookupTable.h new file mode 100644 index 0000000000000..7b10a3a6d5fc3 --- /dev/null +++ b/Utilities/rANS/include/rANS/internal/ReverseSymbolLookupTable.h @@ -0,0 +1,88 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 ReverseSymbolLookupTable.h +/// @author Michael Lettrich +/// @since 2020-04-06 +/// @brief Maps CDF back to source symbol - needed for the decoder + +#ifndef RANS_INTERNAL_REVERSESYMBOLLOOKUPTABLE_H +#define RANS_INTERNAL_REVERSESYMBOLLOOKUPTABLE_H + +#include <vector> +#include <type_traits> +#include <fairlogger/Logger.h> + +#include "helper.h" +#include "SymbolStatistics.h" + +namespace o2 +{ +namespace rans +{ +namespace internal +{ + +class ReverseSymbolLookupTable +{ + public: + ReverseSymbolLookupTable(size_t probabilityBits, + const SymbolStatistics& stats) : mLut() + { + LOG(trace) << "start building reverse symbol lookup table"; + + if (stats.size() == 1) { + LOG(warning) << "SymbolStatistics of empty message passed to " << __func__; + return; + } + + mLut.resize(bitsToRange(probabilityBits)); + // go over all symbols + for (auto symbolIT = std::begin(stats); symbolIT != std::end(stats); ++symbolIT) { + auto symbol = stats.getMinSymbol() + std::distance(std::begin(stats), symbolIT); + const auto [symFrequency, symCumulated] = *symbolIT; + for (auto cumulative = symCumulated; + cumulative < symCumulated + symFrequency; cumulative++) { + mLut[cumulative] = symbol; + } + } + +// for (int symbol = stats.getMinSymbol(); symbol <= stats.getMaxSymbol(); +// symbol++) { +// for (uint32_t cumulative = stats[symbol].second; +// cumulative < stats[symbol].second + stats[symbol].first; cumulative++) { +// mLut[cumulative] = symbol; +// } +// } + +// advanced diagnostics for debug builds +#if !defined(NDEBUG) + LOG(debug2) << "reverseSymbolLookupTableProperties: {" + << "elements: " << mLut.size() << ", " + << "sizeB: " << mLut.size() * sizeof(typename std::decay_t<decltype(mLut)>::value_type) << "}"; +#endif + + LOG(trace) << "done building reverse symbol lookup table"; + }; + + inline int32_t operator[](size_t cummulative) const + { + return mLut[cummulative]; + }; + + private: + std::vector<int32_t> mLut; +}; + +} // namespace internal +} // namespace rans +} // namespace o2 + +#endif /* RANS_INTERNAL_REVERSESYMBOLLOOKUPTABLE_H */ diff --git a/Utilities/rANS/include/rANS/internal/SymbolStatistics.h b/Utilities/rANS/include/rANS/internal/SymbolStatistics.h new file mode 100644 index 0000000000000..18af55e313c6c --- /dev/null +++ b/Utilities/rANS/include/rANS/internal/SymbolStatistics.h @@ -0,0 +1,270 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SymbolStatistics.h +/// @author Michael Lettrich +/// @since 2019-05-08 +/// @brief Structure to depict the distribution of symbols in the source message. + +#ifndef RANS_SYMBOLSTATISTICS_H +#define RANS_SYMBOLSTATISTICS_H + +#include <algorithm> +#include <cassert> +#include <iostream> +#include <numeric> +#include <vector> +#include <cmath> +#include <iterator> +#include <type_traits> + +#include <fairlogger/Logger.h> + +#include "helper.h" +#include "rANS/FrequencyTable.h" + +namespace o2 +{ +namespace rans +{ +namespace internal +{ + +constexpr size_t MIN_SCALE = 16; +constexpr size_t MAX_SCALE = 27; + +class SymbolStatistics +{ + + public: + class Iterator + { + public: + Iterator(size_t index, const SymbolStatistics& stats) : mIndex(index), mStats(stats){}; + + using difference_type = int64_t; + using value_type = std::tuple<uint32_t, uint32_t>; + using pointer = const std::tuple<uint32_t, uint32_t>*; + using reference = const std::tuple<uint32_t, uint32_t>&; + using iterator_category = std::random_access_iterator_tag; + + const Iterator& operator++(); + + const Iterator& operator--(); + + difference_type operator-(const Iterator& other) const; + + difference_type operator-(size_t idx) const; + + const Iterator& operator-(size_t idx); + + value_type operator*() const; + + bool operator!=(const Iterator& other) const; + + private: + size_t mIndex; + const SymbolStatistics& mStats; + }; + + public: + SymbolStatistics(const FrequencyTable& frequencyTable, size_t scaleBits = 0); + + template <typename Source_IT> + SymbolStatistics(const Source_IT begin, const Source_IT end, int64_t min, int64_t max, size_t scaleBits, size_t nUsedAlphabetSymbols); + + std::tuple<uint32_t, uint32_t> operator[](int64_t index) const; + std::tuple<uint32_t, uint32_t> at(size_t pos) const; + + Iterator begin() const; + Iterator end() const; + + Iterator getEscapeSymbol() const; + + size_t size() const; + + int64_t getMinSymbol() const; + int64_t getMaxSymbol() const; + + size_t getNUsedAlphabetSymbols() const; + + size_t getSymbolTablePrecision() const; + + private: + void buildCumulativeFrequencyTable(); + + void rescale(); + + int64_t mMin; + int64_t mMax; + size_t mScaleBits; + size_t mNUsedAlphabetSymbols; + + std::vector<uint32_t> mFrequencyTable; + std::vector<uint32_t> mCumulativeFrequencyTable; + + static constexpr size_t MAX_RANGE = 26; +}; + +template <typename Source_IT> +SymbolStatistics::SymbolStatistics(const Source_IT begin, const Source_IT end, int64_t min, int64_t max, size_t scaleBits, size_t nUsedAlphabetSymbols) : mMin(min), mMax(max), mScaleBits(scaleBits), mFrequencyTable(), mNUsedAlphabetSymbols(nUsedAlphabetSymbols), mCumulativeFrequencyTable() +{ + + using namespace internal; + LOG(trace) << "start building symbol statistics"; + RANSTimer t; + t.start(); + + if (begin == end) { + LOG(debug) << "Passed empty message to " << __func__; // RS this is ok for empty columns + return; + } + + // if we did not calculate renormalization size, calculate it. + if (scaleBits == 0) { + mScaleBits = [&, this]() { + const size_t minScale = MIN_SCALE; + const size_t maxScale = MAX_SCALE; + const size_t calculated = static_cast<uint32_t>(3 * std::ceil(std::log2(getNUsedAlphabetSymbols())) / 2 + 2); + return std::max(minScale, std::min(maxScale, calculated)); + }(); + } + + //additional bit for escape symbol + mFrequencyTable.reserve(std::distance(begin, end) + 1); + + mFrequencyTable.insert(mFrequencyTable.begin(), begin, end); + //incompressible symbol + mFrequencyTable.push_back(1); + + // range check + if (mMax - mMin > 1 << (MAX_RANGE - 1)) { + const std::string errmsg = [&]() { + std::stringstream ss; + ss << "Range of source message " << std::ceil(std::log2(mMax - mMin)) << "Bits surpasses maximal allowed range of " << MAX_RANGE << "Bits."; + return ss.str(); + }(); + LOG(error) << errmsg; + throw std::runtime_error(errmsg); + } + + buildCumulativeFrequencyTable(); + rescale(); + + assert(mFrequencyTable.size() > 1); + assert(mCumulativeFrequencyTable.size() > 2); + assert(mCumulativeFrequencyTable.size() - mFrequencyTable.size() == 1); + + t.stop(); + LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); + +// advanced diagnostics in debug builds +#if !defined(NDEBUG) + LOG(debug2) << "SymbolStatistics: {" + << "entries: " << mFrequencyTable.size() << ", " + << "frequencyTableSizeB: " << mFrequencyTable.size() * sizeof(typename std::decay_t<decltype(mFrequencyTable)>::value_type) << ", " + << "CumulativeFrequencyTableSizeB: " << mCumulativeFrequencyTable.size() * sizeof(typename std::decay_t<decltype(mCumulativeFrequencyTable)>::value_type) << "}"; +#endif + + LOG(trace) << "done building symbol statistics"; +} + +inline int64_t SymbolStatistics::getMinSymbol() const +{ + return mMin; +} + +inline int64_t SymbolStatistics::getMaxSymbol() const +{ + return mMax; +} + +inline size_t SymbolStatistics::size() const +{ + return mFrequencyTable.size(); +} + +inline size_t SymbolStatistics::getSymbolTablePrecision() const +{ + return mScaleBits; +} + +inline std::tuple<uint32_t, uint32_t> SymbolStatistics::operator[](int64_t index) const +{ + assert(index - mMin < mFrequencyTable.size()); + return {mFrequencyTable[index], mCumulativeFrequencyTable[index]}; +} + +inline std::tuple<uint32_t, uint32_t> SymbolStatistics::at(size_t pos) const +{ + assert(pos < mFrequencyTable.size()); + return {mFrequencyTable[pos], mCumulativeFrequencyTable[pos]}; +} + +inline SymbolStatistics::Iterator SymbolStatistics::begin() const +{ + return SymbolStatistics::Iterator(0, *this); +} + +inline SymbolStatistics::Iterator SymbolStatistics::end() const +{ + return SymbolStatistics::Iterator(mFrequencyTable.size(), *this); +} + +inline SymbolStatistics::Iterator SymbolStatistics::getEscapeSymbol() const +{ + return mFrequencyTable.size() > 0 ? SymbolStatistics::Iterator(mFrequencyTable.size() - 1, *this) : end(); +} + +inline const SymbolStatistics::Iterator& SymbolStatistics::Iterator::operator++() +{ + ++mIndex; + assert(mIndex <= mStats.mFrequencyTable.size()); + return *this; +} + +inline const SymbolStatistics::Iterator& SymbolStatistics::Iterator::operator--() +{ + --mIndex; + assert(mIndex >= 0); + return *this; +} + +inline SymbolStatistics::Iterator::difference_type SymbolStatistics::Iterator::operator-(const Iterator& other) const +{ + return mIndex - other.mIndex; +} + +inline SymbolStatistics::Iterator::difference_type SymbolStatistics::Iterator::operator-(size_t index) const +{ + return mIndex - index; +} + +inline const SymbolStatistics::Iterator& SymbolStatistics::Iterator::operator-(size_t index) +{ + mIndex -= index; + return *this; +} + +inline typename SymbolStatistics::Iterator::value_type SymbolStatistics::Iterator::operator*() const +{ + return std::move(mStats.at(mIndex)); +} + +inline bool SymbolStatistics::Iterator::operator!=(const Iterator& other) const +{ + return this->mIndex != other.mIndex; +} + +} // namespace internal +} // namespace rans +} // namespace o2 + +#endif /* RANS_SYMBOLSTATISTICS_H */ diff --git a/Utilities/rANS/include/rANS/internal/SymbolTable.h b/Utilities/rANS/include/rANS/internal/SymbolTable.h new file mode 100644 index 0000000000000..d13fb340853db --- /dev/null +++ b/Utilities/rANS/include/rANS/internal/SymbolTable.h @@ -0,0 +1,147 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 SymbolTable.h +/// @author Michael Lettrich +/// @since 2019-06-21 +/// @brief Container for information needed to encode/decode a symbol of the alphabet + +#ifndef RANS_INTERNAL_SYMBOLTABLE_H +#define RANS_INTERNAL_SYMBOLTABLE_H + +#include <vector> +#include <cstdint> +#include <cmath> +#include <fairlogger/Logger.h> + +#include "SymbolStatistics.h" + +namespace o2 +{ +namespace rans +{ +namespace internal +{ + +template <typename T> +class SymbolTable +{ + + public: + SymbolTable(const SymbolStatistics& symbolStats); + + const T& operator[](int64_t index) const; + + const T& getEscapeSymbol() const; + + bool isRareSymbol(int64_t index) const; + + size_t getAlphabetRangeBits() const; + int getMinSymbol() const { return mMin; } + int getMaxSymbol() const { return mMax; } + + private: + int mMin; + int mMax; + std::vector<T*> mIndex; + std::vector<T> mSymbols; + std::unique_ptr<T> mEscapeSymbol; +}; + +template <typename T> +SymbolTable<T>::SymbolTable(const SymbolStatistics& symbolStats) : mMin(symbolStats.getMinSymbol()), mMax(symbolStats.getMaxSymbol()), mIndex(), mSymbols(), mEscapeSymbol(nullptr) +{ + LOG(trace) << "start building symbol table"; + + mIndex.reserve(symbolStats.size()); + mSymbols.reserve(symbolStats.getNUsedAlphabetSymbols()); + + mEscapeSymbol = [&]() -> std::unique_ptr<T> { + const auto it = symbolStats.getEscapeSymbol(); + if (it != symbolStats.end()) { + const auto [symFrequency, symCumulated] = *(it); + return std::make_unique<T>(symCumulated, symFrequency, symbolStats.getSymbolTablePrecision()); + } else { + return nullptr; + } + }(); + + for (auto it = symbolStats.begin(); it != symbolStats.getEscapeSymbol(); ++it) { + const auto [symFrequency, symCumulated] = *it; + if (symFrequency) { + mSymbols.emplace_back(symCumulated, symFrequency, symbolStats.getSymbolTablePrecision()); + mIndex.emplace_back(&mSymbols.back()); + } else { + mIndex.emplace_back(mEscapeSymbol.get()); + } + } + + //escape symbol + mIndex.emplace_back(mEscapeSymbol.get()); + +// advanced diagnostics for debug builds +#if !defined(NDEBUG) + LOG(debug2) << "SymbolTableProperties: {" + << "entries:" << mSymbols.size() << ", " + << "sizeB: " << mSymbols.size() * sizeof(T) + mIndex.size() * sizeof(T*) << "}"; +#endif + + LOG(trace) << "done building symbol table"; +} + +template <typename T> +inline const T& SymbolTable<T>::operator[](int64_t index) const +{ + const int64_t idx = index - mMin; + // static cast to unsigned: idx < 0 => (uint)idx > MAX_INT => idx > mIndex.size() + if (static_cast<uint64_t>(idx) < mIndex.size()) { + return *(mIndex[idx]); + } else { + return *mEscapeSymbol; + } +} + +template <typename T> +inline bool SymbolTable<T>::isRareSymbol(int64_t index) const +{ + const int64_t idx = index - mMin; + // static cast to unsigned: idx < 0 => (uint)idx > MAX_INT => idx > mIndex.size() + if (static_cast<uint64_t>(idx) < mIndex.size()) { + assert(mEscapeSymbol != nullptr); + return mIndex[idx] == mEscapeSymbol.get(); + } else { + return true; + } +} + +template <typename T> +inline const T& SymbolTable<T>::getEscapeSymbol() const +{ + assert(mEscapeSymbol != nullptr); + return *mEscapeSymbol; +} + +template <typename T> +inline size_t SymbolTable<T>::getAlphabetRangeBits() const +{ + if (mMax - mMin > 0) { + return std::ceil(std::log2(mMax - mMin)); + } else if (mMax - mMin == 0) { + return 1; + } else { + return 0; + } +} + +} // namespace internal +} // namespace rans +} // namespace o2 + +#endif /* RANS_INTERNAL_SYMBOLTABLE_H */ diff --git a/Utilities/rANS/include/rANS/internal/helper.h b/Utilities/rANS/include/rANS/internal/helper.h new file mode 100644 index 0000000000000..b8c5c46283d43 --- /dev/null +++ b/Utilities/rANS/include/rANS/internal/helper.h @@ -0,0 +1,67 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 helper.h +/// @author Michael Lettrich +/// @since 2019-05-21 +/// @brief various helper functions + +#ifndef RANS_INTERNAL_HELPER_H +#define RANS_INTERNAL_HELPER_H + +#include <cstddef> +#include <cmath> +#include <chrono> + +namespace o2 +{ +namespace rans +{ +namespace internal +{ + +template <typename T> +inline constexpr bool needs64Bit() +{ + return sizeof(T) > 4; +} + +inline constexpr size_t bitsToRange(size_t bits) +{ + return 1 << bits; +} + +class RANSTimer +{ + public: + void start() { mStart = std::chrono::high_resolution_clock::now(); }; + void stop() { mStop = std::chrono::high_resolution_clock::now(); }; + double getDurationMS() + { + std::chrono::duration<double, std::milli> duration = mStop - mStart; + return duration.count(); + } + + double getDurationS() + { + std::chrono::duration<double, std::ratio<1>> duration = mStop - mStart; + return duration.count(); + } + + private: + std::chrono::time_point<std::chrono::high_resolution_clock> mStart; + std::chrono::time_point<std::chrono::high_resolution_clock> mStop; +}; + +} // namespace internal +} // namespace rans +} // namespace o2 + +#endif /* RANS_INTERNAL_HELPER_H */ diff --git a/Utilities/rANS/include/rANS/rans.h b/Utilities/rANS/include/rANS/rans.h index 9650389ecdd65..4353abbec8001 100644 --- a/Utilities/rANS/include/rANS/rans.h +++ b/Utilities/rANS/include/rANS/rans.h @@ -16,9 +16,14 @@ #ifndef RANS_RANS_H #define RANS_RANS_H -#include "rANS/SymbolStatistics.h" -#include "rANS/Encoder.h" -#include "rANS/Decoder.h" +#include "FrequencyTable.h" +#include "Encoder.h" +#include "Decoder.h" +#include "DedupEncoder.h" +#include "DedupDecoder.h" +#include "LiteralEncoder.h" +#include "LiteralDecoder.h" +#include "internal/helper.h" namespace o2 { @@ -34,6 +39,34 @@ using Decoder32 = Decoder<uint32_t, uint8_t, source_T>; template <typename source_T> using Decoder64 = Decoder<uint64_t, uint32_t, source_T>; +template <typename source_T> +using LiteralEncoder32 = LiteralEncoder<uint32_t, uint8_t, source_T>; +template <typename source_T> +using LiteralEncoder64 = LiteralEncoder<uint64_t, uint32_t, source_T>; + +template <typename source_T> +using LiteralDecoder32 = LiteralDecoder<uint32_t, uint8_t, source_T>; +template <typename source_T> +using LiteralDecoder64 = LiteralDecoder<uint64_t, uint32_t, source_T>; + +template <typename source_T> +using DedupEncoder32 = DedupEncoder<uint32_t, uint8_t, source_T>; +template <typename source_T> +using DedupEncoder64 = DedupEncoder<uint64_t, uint32_t, source_T>; + +template <typename source_T> +using DedupDecoder32 = DedupDecoder<uint32_t, uint8_t, source_T>; +template <typename source_T> +using DedupDecoder64 = DedupDecoder<uint64_t, uint32_t, source_T>; + +inline size_t calculateMaxBufferSize(size_t num, size_t rangeBits, size_t sizeofStreamT) +{ + // // RS: w/o safety margin the o2-test-ctf-io produces an overflow in the Encoder::process + // constexpr size_t SaferyMargin = 16; + // return std::ceil(1.20 * (num * rangeBits * 1.0) / (sizeofStreamT * 8.0)) + SaferyMargin; + return num * sizeofStreamT; +} + } // namespace rans } // namespace o2 diff --git a/Utilities/rANS/include/rANS/utils.h b/Utilities/rANS/include/rANS/utils.h new file mode 100644 index 0000000000000..69517c65be9da --- /dev/null +++ b/Utilities/rANS/include/rANS/utils.h @@ -0,0 +1,16 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 utils.h +/// @author Michael Lettrich +/// @since 2020-11-18 +/// @brief header for utils + +#include "utils/CombinedIterator.h" diff --git a/Utilities/rANS/include/rANS/utils/CombinedIterator.h b/Utilities/rANS/include/rANS/utils/CombinedIterator.h new file mode 100644 index 0000000000000..46a92d45ccfba --- /dev/null +++ b/Utilities/rANS/include/rANS/utils/CombinedIterator.h @@ -0,0 +1,222 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 CombinedIterator.h +/// \brief +/// \author michael.lettrich@cern.ch + +#ifndef INCLUDE_RANS_UTILS_COMBINEDITERATOR_H_ +#define INCLUDE_RANS_UTILS_COMBINEDITERATOR_H_ + +#include <cstddef> +#include <cassert> +#include <cstdint> +#include <type_traits> +#include <iostream> +#include <iterator> + +namespace o2 +{ +namespace rans +{ +namespace utils +{ + +template <class iterA_T, class iterB_T, class F> +class CombinedInputIterator +{ + using difference_type = std::ptrdiff_t; + using value_type = std::result_of<F(iterA_T, iterB_T)>; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::input_iterator_tag; + + public: + CombinedInputIterator(iterA_T iterA, iterB_T iterB, F functor); + CombinedInputIterator(const CombinedInputIterator& iter) = default; + CombinedInputIterator(CombinedInputIterator&& iter) = default; + CombinedInputIterator& operator=(const CombinedInputIterator& other); + CombinedInputIterator& operator=(CombinedInputIterator&& other) = default; + ~CombinedInputIterator() = default; + + //comparison + bool operator==(const CombinedInputIterator& other) const; + bool operator!=(const CombinedInputIterator& other) const; + + //pointer arithmetics + CombinedInputIterator& operator++(); + CombinedInputIterator operator++(int); + + // dereference + auto operator*() const; + + private: + iterA_T mIterA; + iterB_T mIterB; + F mFunctor; + + public: + friend std::ostream& operator<<(std::ostream& o, const CombinedInputIterator& iter) + { + o << "CombinedInputIterator{iterA: " << &(iter.mIterA) << ", iterB: " << &(iter.mIterB) << "}"; + return o; + } +}; + +template <class iterA_T, class iterB_T, class F> +class CombinedOutputIterator +{ + + class Proxy + { + public: + Proxy(CombinedOutputIterator& iter); + + template <typename value_T> + Proxy& operator=(value_T value); + + private: + CombinedOutputIterator& mIter; + }; + + using difference_type = std::ptrdiff_t; + using value_type = Proxy; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::input_iterator_tag; + + public: + CombinedOutputIterator(iterA_T iterA, iterB_T iterB, F functor); + CombinedOutputIterator(const CombinedOutputIterator& iter) = default; + CombinedOutputIterator(CombinedOutputIterator&& iter) = default; + CombinedOutputIterator& operator=(const CombinedOutputIterator& other); + CombinedOutputIterator& operator=(CombinedOutputIterator&& other) = default; + ~CombinedOutputIterator() = default; + + //pointer arithmetics + CombinedOutputIterator& operator++(); + CombinedOutputIterator operator++(int); + + // dereference + value_type operator*(); + + private: + iterA_T mIterA; + iterB_T mIterB; + F mFunctor; + + public: + friend std::ostream& operator<<(std::ostream& o, const CombinedOutputIterator& iter) + { + o << "CombinedOutputIterator{iterA: " << &(iter.mIterA) << ", iterB: " << &(iter.mIterB) << "}"; + return o; + } +}; + +template <class iterA_T, class iterB_T, class F> +CombinedInputIterator<iterA_T, iterB_T, F>::CombinedInputIterator(iterA_T iterA, iterB_T iterB, F functor) : mIterA(iterA), mIterB(iterB), mFunctor(functor) +{ +} + +template <class iterA_T, class iterB_T, class F> +auto CombinedInputIterator<iterA_T, iterB_T, F>::operator=(const CombinedInputIterator& other) -> CombinedInputIterator& +{ + mIterA = other.mIterA; + mIterB = other.mIterB; + return *this; +} + +template <class iterA_T, class iterB_T, class F> +inline bool CombinedInputIterator<iterA_T, iterB_T, F>::operator==(const CombinedInputIterator& other) const +{ + return (mIterA == other.mIterA) && (mIterB == other.mIterB); +} + +template <class iterA_T, class iterB_T, class F> +inline bool CombinedInputIterator<iterA_T, iterB_T, F>::operator!=(const CombinedInputIterator& other) const +{ + return !(*this == other); +} + +template <class iterA_T, class iterB_T, class F> +inline auto CombinedInputIterator<iterA_T, iterB_T, F>::operator++() -> CombinedInputIterator& +{ + ++mIterA; + ++mIterB; + return *this; +} + +template <class iterA_T, class iterB_T, class F> +inline auto CombinedInputIterator<iterA_T, iterB_T, F>::operator++(int) -> CombinedInputIterator +{ + auto res = *this; + ++(*this); + return res; +} + +template <class iterA_T, class iterB_T, class F> +inline auto CombinedInputIterator<iterA_T, iterB_T, F>::operator*() const +{ + return mFunctor(mIterA, mIterB); +} + +template <class iterA_T, class iterB_T, class F> +CombinedOutputIterator<iterA_T, iterB_T, F>::CombinedOutputIterator(iterA_T iterA, iterB_T iterB, F functor) : mIterA(iterA), mIterB(iterB), mFunctor(functor) +{ +} + +template <class iterA_T, class iterB_T, class F> +auto CombinedOutputIterator<iterA_T, iterB_T, F>::operator=(const CombinedOutputIterator& other) -> CombinedOutputIterator& +{ + mIterA = other.mIterA; + mIterB = other.mIterB; + return *this; +} + +template <class iterA_T, class iterB_T, class F> +inline auto CombinedOutputIterator<iterA_T, iterB_T, F>::operator++() -> CombinedOutputIterator& +{ + ++mIterA; + ++mIterB; + return *this; +} + +template <class iterA_T, class iterB_T, class F> +inline auto CombinedOutputIterator<iterA_T, iterB_T, F>::operator++(int) -> CombinedOutputIterator +{ + auto res = *this; + ++(*this); + return res; +} + +template <class iterA_T, class iterB_T, class F> +inline auto CombinedOutputIterator<iterA_T, iterB_T, F>::operator*() -> value_type +{ + return Proxy(*this); +} + +template <class iterA_T, class iterB_T, class F> +CombinedOutputIterator<iterA_T, iterB_T, F>::Proxy::Proxy(CombinedOutputIterator& iter) : mIter(iter) +{ +} + +template <class iterA_T, class iterB_T, class F> +template <typename value_T> +inline auto CombinedOutputIterator<iterA_T, iterB_T, F>::Proxy::operator=(value_T value) -> CombinedOutputIterator::Proxy& +{ + mIter.mFunctor(mIter.mIterA, mIter.mIterB, value); + return *this; +} + +} // namespace utils +} // namespace rans +} // namespace o2 + +#endif /* INCLUDE_RANS_UTILS_COMBINEDITERATOR_H_ */ diff --git a/Utilities/rANS/run/bin-encode-decode.cxx b/Utilities/rANS/run/bin-encode-decode.cxx new file mode 100644 index 0000000000000..a3af57b308e0e --- /dev/null +++ b/Utilities/rANS/run/bin-encode-decode.cxx @@ -0,0 +1,147 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 bin-encode-decode.cpp +/// @author Michael Lettrich +/// @since 2020-06-22 +/// @brief benchmark encode/decode using rans on binary data. + +#include "rANS/rans.h" + +#include <boost/program_options.hpp> + +#include <fairlogger/Logger.h> + +namespace bpo = boost::program_options; + +#ifndef SOURCE_T +#define SOURCE_T uint8_t +#endif + +using source_t = SOURCE_T; +using coder_t = uint64_t; +using stream_t = uint32_t; +static const uint REPETITIONS = 5; + +template <typename T> +void readFile(const std::string& filename, std::vector<T>* tokens) +{ + std::ifstream is(filename, std::ios_base::binary | std::ios_base::in); + if (is) { + // get length of file: + is.seekg(0, is.end); + size_t length = is.tellg(); + is.seekg(0, is.beg); + + // reserve size of tokens + if (!tokens) { + throw std::runtime_error("Cannot read file into nonexistent vector"); + } + + if (length % sizeof(T)) { + throw std::runtime_error("Filesize is not a multiple of datatype."); + } + // size the vector appropriately + size_t num_elems = length / sizeof(T); + tokens->resize(num_elems); + + // read data as a block: + is.read(reinterpret_cast<char*>(tokens->data()), length); + is.close(); + } +} + +int main(int argc, char* argv[]) +{ + + bpo::options_description options("Allowed options"); + // clang-format off + options.add_options() + ("help,h", "print usage message") + ("file,f",bpo::value<std::string>(), "file to compress") + ("samples,s",bpo::value<uint32_t>(), "how often to run benchmark") + ("bits,b",bpo::value<uint32_t>(), "resample dictionary to N Bits") + ("range,r",bpo::value<uint32_t>()->default_value(0), "range of the source data") + ("log_severity,l",bpo::value<std::string>(), "severity of FairLogger"); + // clang-format on + + bpo::variables_map vm; + bpo::store(bpo::parse_command_line(argc, argv, options), vm); + bpo::notify(vm); + + if (vm.count("help")) { + std::cout << options << "\n"; + return 0; + } + + const std::string filename = [&]() { + if (vm.count("file")) { + return vm["file"].as<std::string>(); + } else { + LOG(error) << "missing path to input file"; + exit(1); + } + }(); + + const uint32_t probabilityBits = [&]() { + if (vm.count("bits")) { + return vm["bits"].as<uint32_t>(); + } else { + return 0u; + } + }(); + + const uint32_t symbolRangeBits = [&]() { + if (vm.count("range")) { + return vm["range"].as<uint32_t>(); + } else { + return static_cast<uint32_t>(0); + } + }(); + + const uint32_t repetitions = [&]() { + if (vm.count("samples")) { + return vm["samples"].as<uint32_t>(); + } else { + return REPETITIONS; + } + }(); + + if (vm.count("log_severity")) { + fair::Logger::SetConsoleSeverity(vm["log_severity"].as<std::string>().c_str()); + } + for (size_t i = 0; i < repetitions; i++) { + LOG(info) << "repetion: " << i; + std::vector<source_t> tokens; + readFile(filename, &tokens); + + o2::rans::FrequencyTable frequencies; + frequencies.addSamples(std::begin(tokens), std::end(tokens)); + + std::vector<stream_t> encoderBuffer(256 << 20, 0); + const auto encodedMessageEnd = [&]() { + const o2::rans::Encoder64<source_t> encoder{frequencies, probabilityBits}; + return encoder.process(encoderBuffer.begin(), encoderBuffer.end(), std::begin(tokens), std::end(tokens)); + }(); + + std::vector<source_t> decoderBuffer(tokens.size()); + [&]() { + o2::rans::Decoder64<source_t> decoder{frequencies, probabilityBits}; + decoder.process(decoderBuffer.begin(), encodedMessageEnd, std::distance(std::begin(tokens), std::end(tokens))); + }(); + + if (std::memcmp(tokens.data(), decoderBuffer.data(), + tokens.size() * sizeof(source_t))) { + LOG(error) << "Decoder failed tests"; + } else { + LOG(info) << "Decoder passed tests"; + } + } +} diff --git a/Utilities/rANS/src/FrequencyTable.cxx b/Utilities/rANS/src/FrequencyTable.cxx new file mode 100644 index 0000000000000..50d638941f373 --- /dev/null +++ b/Utilities/rANS/src/FrequencyTable.cxx @@ -0,0 +1,99 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 FrequencyTable.cxx +/// @author Michael Lettrich +/// @since Aug 1, 2020 +/// @brief Implementation of a frequency table for rANS symbole (i.e. a histogram) + +#include "rANS/FrequencyTable.h" + +namespace o2 +{ +namespace rans +{ + +void FrequencyTable::resizeFrequencyTable(value_t min, value_t max) +{ + LOG(trace) << "start resizing frequency table"; + internal::RANSTimer t; + t.start(); + // calculate new dimensions + const value_t newMin = std::min(mMin, min); + const value_t newMax = std::max(mMax, max); + const size_t newSize = newMax - newMin + 1; + + // empty - init; + if (mMin == 0 && mMax == 0) { + mFrequencyTable.resize(newSize, 0); + } + + // if the new size is bigger than the old one we need to resize the frequency table + if ((newSize) > mFrequencyTable.size()) { + const size_t offset = newMin < mMin ? std::abs(min - mMin) : 0; + std::vector<uint32_t> tmpFrequencyTable; + tmpFrequencyTable.reserve(newSize); + // insert initial offset if applicable + tmpFrequencyTable.insert(std::begin(tmpFrequencyTable), offset, 0); + // append current frequency table + tmpFrequencyTable.insert(std::end(tmpFrequencyTable), mFrequencyTable.begin(), mFrequencyTable.end()); + //fill tail with zeroes if applicable + const size_t tail = newMax > mMax ? std::abs(max - mMax) : 0; + tmpFrequencyTable.insert(std::end(tmpFrequencyTable), tail, 0); + + mFrequencyTable = std::move(tmpFrequencyTable); + } + + assert(mFrequencyTable.size() == newSize); + mMin = newMin; + mMax = newMax; + + t.stop(); + LOG(debug1) << __func__ << " inclusive time (ms): " << t.getDurationMS(); + LOG(trace) << "done resizing frequency table"; +} + +size_t FrequencyTable::getUsedAlphabetSize() const +{ + size_t nUsedAlphabetSymbols = 0; + + for (auto freq : *this) { + if (freq > 0) { + nUsedAlphabetSymbols++; + } + } + return nUsedAlphabetSymbols; +} + +std::ostream& operator<<(std::ostream& out, const FrequencyTable& fTable) +{ + double entropy = 0; + for (auto frequency : fTable) { + if (frequency > 0) { + const double p = (frequency * 1.0) / fTable.getNumSamples(); + entropy -= p * std::log2(p); + } + } + + out << "FrequencyTable: {" + << "numSymbols: " << fTable.getNumSamples() << ", " + << "alphabetRange: " << fTable.getAlphabetRangeBits() << ", " + << "alphabetSize: " << fTable.getUsedAlphabetSize() << ", " + << "minSymbol: " << fTable.getMinSymbol() << ", " + << "maxSymbol: " << fTable.getMaxSymbol() << ", " + << "sizeFrequencyTableB: " << fTable.size() << ", " + << "sizeFrequencyTableB: " << fTable.size() * sizeof(typename o2::rans::FrequencyTable::value_t) << ", " + << "entropy: " << entropy << "}"; + + return out; +} + +} //namespace rans +} // namespace o2 diff --git a/Utilities/rANS/src/SymbolStatistics.cxx b/Utilities/rANS/src/SymbolStatistics.cxx index 397c4ed89024f..ca9934b236443 100644 --- a/Utilities/rANS/src/SymbolStatistics.cxx +++ b/Utilities/rANS/src/SymbolStatistics.cxx @@ -15,16 +15,24 @@ #include <cmath> -#include "rANS/SymbolStatistics.h" -#include "rANS/helper.h" +#include "rANS/internal/SymbolStatistics.h" +#include "rANS/internal/helper.h" namespace o2 { namespace rans { +namespace internal +{ + +SymbolStatistics::SymbolStatistics(const FrequencyTable& frequencyTable, size_t scaleBits) : SymbolStatistics(frequencyTable.begin(), frequencyTable.end(), frequencyTable.getMinSymbol(), frequencyTable.getMaxSymbol(), scaleBits, frequencyTable.getUsedAlphabetSize()){}; -void SymbolStatistics::rescaleToNBits(size_t bits) +void SymbolStatistics::rescale() { + + auto getFrequency = [this](size_t i) { return mCumulativeFrequencyTable[i + 1] - mCumulativeFrequencyTable[i]; }; + + using namespace internal; LOG(trace) << "start rescaling frequency table"; RANSTimer t; t.start(); @@ -34,65 +42,54 @@ void SymbolStatistics::rescaleToNBits(size_t bits) return; } - const size_t newCumulatedFrequency = bitsToRange(bits); - assert(newCumulatedFrequency >= mFrequencyTable.size()); + const size_t newCumulatedFrequency = bitsToRange(mScaleBits); + assert(newCumulatedFrequency >= this->getNUsedAlphabetSymbols() + 1); size_t cumulatedFrequencies = mCumulativeFrequencyTable.back(); + std::vector<uint32_t> sortIdx; + sortIdx.reserve(getNUsedAlphabetSymbols()); + // resample distribution based on cumulative frequencies_ - for (size_t i = 1; i <= mFrequencyTable.size(); i++) - mCumulativeFrequencyTable[i] = - (static_cast<uint64_t>(newCumulatedFrequency) * - mCumulativeFrequencyTable[i]) / - cumulatedFrequencies; - - // if we nuked any non-0 frequency symbol to 0, we need to steal - // the range to make the frequency nonzero from elsewhere. - // - // this is not at all optimal, i'm just doing the first thing that comes to - // mind. for (size_t i = 0; i < mFrequencyTable.size(); i++) { - if (mFrequencyTable[i] && - mCumulativeFrequencyTable[i + 1] == mCumulativeFrequencyTable[i]) { - // symbol i was set to zero freq - - // find best symbol to steal frequency from (try to steal from low-freq - // ones) - std::pair<size_t, size_t> stealFromEntry{mFrequencyTable.size(), ~0u}; - for (size_t j = 0; j < mFrequencyTable.size(); j++) { - uint32_t frequency = - mCumulativeFrequencyTable[j + 1] - mCumulativeFrequencyTable[j]; - if (frequency > 1 && frequency < stealFromEntry.second) { - stealFromEntry.second = frequency; - stealFromEntry.first = j; - } - } - assert(stealFromEntry.first != mFrequencyTable.size()); - - // and steal from it! - if (stealFromEntry.first < i) { - for (size_t j = stealFromEntry.first + 1; j <= i; j++) - mCumulativeFrequencyTable[j]--; - } else { - assert(stealFromEntry.first > i); - for (size_t j = i + 1; j <= stealFromEntry.first; j++) - mCumulativeFrequencyTable[j]++; - } + if (mFrequencyTable[i]) { + sortIdx.push_back(i); // we will sort only those memorize only those entries which can be used + } + } + + std::sort(sortIdx.begin(), sortIdx.end(), [&](uint32_t i, uint32_t j) { return getFrequency(i) < getFrequency(j); }); + size_t need_shift = 0; + for (size_t i = 0; i < sortIdx.size(); i++) { + if (static_cast<uint64_t>(getFrequency(sortIdx[i])) * (newCumulatedFrequency - need_shift) / cumulatedFrequencies >= 1) { + break; + } + need_shift++; + } + + size_t shift = 0; + auto beforeUpdate = mCumulativeFrequencyTable[0]; + for (size_t i = 0; i < mFrequencyTable.size(); i++) { + if (mFrequencyTable[i] && static_cast<uint64_t>(mCumulativeFrequencyTable[i + 1] - beforeUpdate) * (newCumulatedFrequency - need_shift) / cumulatedFrequencies < 1) { + shift++; } + beforeUpdate = mCumulativeFrequencyTable[i + 1]; + mCumulativeFrequencyTable[i + 1] = (static_cast<uint64_t>(newCumulatedFrequency - need_shift) * mCumulativeFrequencyTable[i + 1]) / cumulatedFrequencies + shift; } + assert(shift == need_shift); // calculate updated freqs and make sure we didn't screw anything up assert(mCumulativeFrequencyTable.front() == 0 && mCumulativeFrequencyTable.back() == newCumulatedFrequency); + for (size_t i = 0; i < mFrequencyTable.size(); i++) { - if (mFrequencyTable[i] == 0) + if (mFrequencyTable[i] == 0) { assert(mCumulativeFrequencyTable[i + 1] == mCumulativeFrequencyTable[i]); - else + } else { assert(mCumulativeFrequencyTable[i + 1] > mCumulativeFrequencyTable[i]); + } // calc updated freq - mFrequencyTable[i] = - mCumulativeFrequencyTable[i + 1] - mCumulativeFrequencyTable[i]; + mFrequencyTable[i] = getFrequency(i); } // for(int i = 0; i<static_cast<int>(freqs.getNumSymbols()); i++){ // std::cout << i << ": " << i + min_ << " " << freqs[i] << " " << @@ -106,35 +103,11 @@ void SymbolStatistics::rescaleToNBits(size_t bits) LOG(trace) << "done rescaling frequency table"; } -int SymbolStatistics::getMinSymbol() const { return mMin; } - -int SymbolStatistics::getMaxSymbol() const { return mMax; } - -size_t SymbolStatistics::getAlphabetSize() const { return mFrequencyTable.size(); } - -size_t SymbolStatistics::getAlphabetRangeBits() const -{ - return std::max(std::ceil(std::log2(mMax - mMin)), 1.0); -} - size_t SymbolStatistics::getNUsedAlphabetSymbols() const { return mNUsedAlphabetSymbols; } -size_t SymbolStatistics::getMessageLength() const -{ - return mMessageLength; -} - -std::pair<uint32_t, uint32_t> SymbolStatistics::operator[](size_t index) const -{ - assert(index - mMin < mFrequencyTable.size()); - - return std::make_pair(mFrequencyTable[index - mMin], - mCumulativeFrequencyTable[index - mMin]); -} - void SymbolStatistics::buildCumulativeFrequencyTable() { LOG(trace) << "start building cumulative frequency table"; @@ -147,40 +120,6 @@ void SymbolStatistics::buildCumulativeFrequencyTable() LOG(trace) << "done building cumulative frequency table"; } -SymbolStatistics::Iterator SymbolStatistics::begin() const -{ - return SymbolStatistics::Iterator(this->getMinSymbol(), *this); -} - -SymbolStatistics::Iterator SymbolStatistics::end() const -{ - if (mFrequencyTable.empty()) { - return this->begin(); // begin == end for empty stats; - } else { - return SymbolStatistics::Iterator(this->getMaxSymbol() + 1, *this); - } -} - -SymbolStatistics::Iterator::Iterator(size_t index, - const SymbolStatistics& stats) - : mIndex(index), mStats(stats) {} - -const SymbolStatistics::Iterator& SymbolStatistics::Iterator::operator++() -{ - ++mIndex; - assert(mIndex <= mStats.getMaxSymbol() + 1); - return *this; -} - -std::pair<uint32_t, uint32_t> SymbolStatistics::Iterator::operator*() const -{ - return std::move(mStats[mIndex]); -} - -bool SymbolStatistics::Iterator::operator!=(const Iterator& other) const -{ - return this->mIndex != other.mIndex; -} - +} // namespace internal } // namespace rans } // namespace o2 diff --git a/Utilities/rANS/test/test_ransCombinedIterator.cxx b/Utilities/rANS/test/test_ransCombinedIterator.cxx new file mode 100644 index 0000000000000..e9a14774df33e --- /dev/null +++ b/Utilities/rANS/test/test_ransCombinedIterator.cxx @@ -0,0 +1,164 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ransCombinedIterator.cxx +/// @author michael.lettrich@cern.ch +/// @since 2020-10-28 +/// @brief + +#define BOOST_TEST_MODULE Utility test +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> + +#include "rANS/utils.h" + +struct test_CombninedIteratorFixture { + std::vector<uint16_t> a{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + std::vector<uint16_t> b{a.rbegin(), a.rend()}; + size_t shift = 16; + std::vector<uint32_t> aAndB{0x0001000f, 0x0002000e, 0x0003000d, 0x0004000c, 0x0005000b, + 0x0006000a, 0x00070009, 0x00080008, 0x00090007, 0x000a0006, + 0x000b0005, 0x000c0004, 0x000d0003, 0x000e0002, 0x000f0001}; +}; + +class ReadShiftFunctor +{ + public: + ReadShiftFunctor(size_t shift) : mShift(shift){}; + + template <typename iterA_T, typename iterB_T> + inline uint32_t operator()(iterA_T iterA, iterB_T iterB) const + { + return *iterB + (static_cast<uint32_t>(*iterA) << mShift); + }; + + private: + size_t mShift; +}; + +class WriteShiftFunctor +{ + public: + WriteShiftFunctor(size_t shift) : mShift(shift){}; + + template <typename iterA_T, typename iterB_T> + inline void operator()(iterA_T iterA, iterB_T iterB, uint32_t value) const + { + *iterA = value >> mShift; + *iterB = value & ((1 << mShift) - 1); + }; + + private: + size_t mShift; +}; + +BOOST_FIXTURE_TEST_CASE(test_CombinedInputIteratorBase, test_CombninedIteratorFixture) +{ + + // auto readOP = [](auto iterA, auto iterB) -> uint32_t { + // return *iterB + (static_cast<uint32_t>(*iterA) << 16); + // }; + + ReadShiftFunctor f(shift); + + o2::rans::utils::CombinedInputIterator iter(a.begin(), b.begin(), f); + // test equal + const o2::rans::utils::CombinedInputIterator first(a.begin(), b.begin(), f); + BOOST_CHECK_EQUAL(iter, first); + // test not equal + const o2::rans::utils::CombinedInputIterator second(++(a.begin()), ++(b.begin()), f); + BOOST_CHECK_NE(iter, second); + // test pre increment + ++iter; + BOOST_CHECK_EQUAL(iter, second); + //test postIncrement + iter = first; + BOOST_CHECK_EQUAL(iter++, first); + BOOST_CHECK_EQUAL(iter, second); + //test deref + const uint32_t val = first.operator*(); + BOOST_CHECK_EQUAL(val, aAndB.front()); +} + +BOOST_FIXTURE_TEST_CASE(test_CombinedOutputIteratorBase, test_CombninedIteratorFixture) +{ + std::vector<uint16_t> aOut(2, 0x0); + std::vector<uint16_t> bOut(2, 0x0); + + // auto writeOP = [](auto iterA, auto iterB, uint32_t value) -> void { + // const uint32_t shift = 16; + // *iterA = value >> shift; + // *iterB = value & ((1 << shift) - 1); + // }; + + WriteShiftFunctor f(shift); + + o2::rans::utils::CombinedOutputIterator iter(aOut.begin(), bOut.begin(), f); + + // test deref: + *iter = aAndB[0]; + BOOST_CHECK_EQUAL(aOut[0], a[0]); + BOOST_CHECK_EQUAL(bOut[0], b[0]); + aOut[0] = 0x0; + bOut[0] = 0x0; + + // test pre increment + *(++iter) = aAndB[1]; + BOOST_CHECK_EQUAL(aOut[0], 0); + BOOST_CHECK_EQUAL(bOut[0], 0); + BOOST_CHECK_EQUAL(aOut[1], a[1]); + BOOST_CHECK_EQUAL(bOut[1], b[1]); + aOut.assign(2, 0x0); + bOut.assign(2, 0x0); + iter = o2::rans::utils::CombinedOutputIterator(aOut.begin(), bOut.begin(), f); + + // test post increment + auto preInc = iter++; + *preInc = aAndB[0]; + BOOST_CHECK_EQUAL(aOut[0], a[0]); + BOOST_CHECK_EQUAL(bOut[0], b[0]); + BOOST_CHECK_EQUAL(aOut[1], 0x0); + BOOST_CHECK_EQUAL(bOut[1], 0x0); + aOut.assign(2, 0x0); + bOut.assign(2, 0x0); + *iter = aAndB[1]; + BOOST_CHECK_EQUAL(aOut[0], 0); + BOOST_CHECK_EQUAL(bOut[0], 0); + BOOST_CHECK_EQUAL(aOut[1], a[1]); + BOOST_CHECK_EQUAL(bOut[1], b[1]); +} + +BOOST_FIXTURE_TEST_CASE(test_CombinedInputIteratorReadArray, test_CombninedIteratorFixture) +{ + // auto readOP = [](auto iterA, auto iterB) -> uint32_t { + // return *iterB + (static_cast<uint32_t>(*iterA) << 16); + // }; + ReadShiftFunctor f(shift); + + const o2::rans::utils::CombinedInputIterator begin(a.begin(), b.begin(), f); + const o2::rans::utils::CombinedInputIterator end(a.end(), b.end(), f); + BOOST_CHECK_EQUAL_COLLECTIONS(begin, end, aAndB.begin(), aAndB.end()); +} + +BOOST_FIXTURE_TEST_CASE(test_CombinedOutputIteratorWriteArray, test_CombninedIteratorFixture) +{ + std::vector<uint16_t> aRes(a.size(), 0); + std::vector<uint16_t> bRes(b.size(), 0); + + o2::rans::utils::CombinedOutputIterator iter(aRes.begin(), bRes.begin(), WriteShiftFunctor(shift)); + for (auto input : aAndB) { + *iter++ = input; + } + + BOOST_CHECK_EQUAL_COLLECTIONS(aRes.begin(), aRes.end(), a.begin(), a.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(bRes.begin(), bRes.end(), b.begin(), b.end()); +} diff --git a/Utilities/rANS/test/test_ransEncodeDecode.cxx b/Utilities/rANS/test/test_ransEncodeDecode.cxx index d8b2fb4e4051f..83389dc06b746 100644 --- a/Utilities/rANS/test/test_ransEncodeDecode.cxx +++ b/Utilities/rANS/test/test_ransEncodeDecode.cxx @@ -37,6 +37,10 @@ struct Fixture { using encoder_t = o2::rans::Encoder<coder_t, stream_t, source_t>; using decoder_t = o2::rans::Decoder<coder_t, stream_t, source_t>; + using literalEncoder_t = o2::rans::LiteralEncoder<coder_t, stream_t, source_t>; + using literalDecoder_t = o2::rans::LiteralDecoder<coder_t, stream_t, source_t>; + using dedupEncoder_t = o2::rans::DedupEncoder<coder_t, stream_t, source_t>; + using dedupDecoder_t = o2::rans::DedupDecoder<coder_t, stream_t, source_t>; //TUNIG parameters // how many bits do we resample the symbol statistics to? @@ -49,38 +53,37 @@ struct Fixture { // 25Bits for 25Bit alphabets const uint probabilityBits = P; - const std::string source = ""; + const std::string source; }; template <typename CODER_T, typename STREAM_T, uint P> struct FixtureFull : public Fixture<CODER_T, STREAM_T, P> { const std::string source = R"(Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium - doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis - et quasi architecto beatae vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, - quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores - eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, - quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora - incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. ut enim ad minima veniam, - quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea - commodi consequatur? quis autem vel eum iure reprehenderit, qui in ea voluptate velit - esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, - quo voluptas nulla pariatur?)"; +doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis +et quasi architecto beatae vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, +quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores +eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, +quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora +incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. ut enim ad minima veniam, +quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea +commodi consequatur? quis autem vel eum iure reprehenderit, qui in ea voluptate velit +esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, +quo voluptas nulla pariatur?)"; }; typedef boost::mpl::vector<Fixture<uint32_t, uint8_t, 14>, Fixture<uint64_t, uint32_t, 18>, FixtureFull<uint32_t, uint8_t, 14>, FixtureFull<uint64_t, uint32_t, 18>> Fixtures; BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_EncodeDecode, T, Fixtures, T) { - + fair::Logger::SetConsoleSeverity("trace"); // iterate over the message and create PDF and CDF for each symbol in the message - o2::rans::SymbolStatistics stats{std::begin(T::source), std::end(T::source)}; - // For performance reason the distributions must be rescaled to be a power of two. - stats.rescaleToNBits(T::probabilityBits); + o2::rans::FrequencyTable frequencies; + frequencies.addSamples(std::begin(T::source), std::end(T::source)); // buffer to write the rANS coded message into std::vector<typename T::stream_t> encoderBuffer(1 << 20, 0); //create a stateful encoder object that builds an encoding table from the given symbol statistics - const typename T::encoder_t encoder{stats, T::probabilityBits}; + const typename T::encoder_t encoder{frequencies, T::probabilityBits}; // encoder rANS encodes symbols from SOURCE array to encoder Buffer. Since coder and decoder // are mathematical inverse functions on the ANS state, they operate as a stack - // i.e. the decoder outputs the message in reverse order of the encoder. @@ -93,9 +96,84 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_EncodeDecode, T, Fixtures, T) // The decoded message will go into the decoder buffer which will have as many symbols as the original message std::vector<typename T::source_t> decoderBuffer(std::distance(std::begin(T::source), std::end(T::source)), 0); // create a stateful decoder object that build a decoding table from the given symbol statistics - const typename T::decoder_t decoder{stats, T::probabilityBits}; + const typename T::decoder_t decoder{frequencies, T::probabilityBits}; + // the decoder unwinds the rANS state in the encoder buffer starting at ransBegin and decodes it into the decode buffer; + decoder.process(decoderBuffer.begin(), encodedMessageEnd, std::distance(std::begin(T::source), std::end(T::source))); + + //the decodeBuffer and the source message have to be identical afterwards. + BOOST_REQUIRE(std::memcmp(&(*T::source.begin()), decoderBuffer.data(), + decoderBuffer.size() * sizeof(typename T::source_t)) == 0); +} + +typedef boost::mpl::vector<FixtureFull<uint32_t, uint8_t, 14>, FixtureFull<uint64_t, uint32_t, 14>> LiteralFixtures; + +BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_EncodeDecode_literals, T, LiteralFixtures, T) +{ + fair::Logger::SetConsoleSeverity("trace"); + + // iterate over the message and create PDF and CDF for each symbol in the message + o2::rans::FrequencyTable frequencies; + frequencies.addSamples(std::begin(T::source), std::end(T::source)); + + std::string adaptedSource = "\\"; + adaptedSource.append(T::source); + adaptedSource.append("&%=/*!"); + + // buffer to write the rANS coded message into + std::vector<typename T::stream_t> encoderBuffer(1 << 20, 0); + //create a stateful encoder object that builds an encoding table from the given symbol statistics + const typename T::literalEncoder_t encoder{frequencies, T::probabilityBits}; + // encoder rANS encodes symbols from SOURCE array to encoder Buffer. Since coder and decoder + // are mathematical inverse functions on the ANS state, they operate as a stack - + // i.e. the decoder outputs the message in reverse order of the encoder. + // By convention the encoder runs backwards over the input so that the decoder can return + // the data in the expected order. The encoder will start from encoderBuffer.begin() + // and return an iterator one element past the last entry written. + // This means the encodeded message ranges from encoderBuffer.begin() to encodedMessageEnd -1, with (encodedMessageEnd -encoderBuffer.begin()) entries written. + std::vector<typename T::source_t> literals; + auto encodedMessageEnd = encoder.process(encoderBuffer.begin(), encoderBuffer.end(), std::begin(adaptedSource), std::end(adaptedSource), literals); + + // The decoded message will go into the decoder buffer which will have as many symbols as the original message + std::vector<typename T::source_t> decoderBuffer(std::distance(std::begin(adaptedSource), std::end(adaptedSource)), 0); + // create a stateful decoder object that build a decoding table from the given symbol statistics + const typename T::literalDecoder_t decoder{frequencies, T::probabilityBits}; + // the decoder unwinds the rANS state in the encoder buffer starting at ransBegin and decodes it into the decode buffer; + decoder.process(decoderBuffer.begin(), encodedMessageEnd, std::distance(std::begin(adaptedSource), std::end(adaptedSource)), literals); + + //the decodeBuffer and the source message have to be identical afterwards. + BOOST_REQUIRE(std::memcmp(&(*adaptedSource.begin()), decoderBuffer.data(), + decoderBuffer.size() * sizeof(typename T::source_t)) == 0); +} + +typedef boost::mpl::vector<FixtureFull<uint32_t, uint8_t, 14>, FixtureFull<uint64_t, uint32_t, 14>> DedupFixtures; + +BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_EncodeDecode_dedup, T, DedupFixtures, T) +{ + fair::Logger::SetConsoleSeverity("trace"); + // iterate over the message and create PDF and CDF for each symbol in the message + o2::rans::FrequencyTable frequencies; + frequencies.addSamples(std::begin(T::source), std::end(T::source)); + + // buffer to write the rANS coded message into + std::vector<typename T::stream_t> encoderBuffer(1 << 20, 0); + //create a stateful encoder object that builds an encoding table from the given symbol statistics + const typename T::dedupEncoder_t encoder{frequencies, T::probabilityBits}; + // encoder rANS encodes symbols from SOURCE array to encoder Buffer. Since coder and decoder + // are mathematical inverse functions on the ANS state, they operate as a stack - + // i.e. the decoder outputs the message in reverse order of the encoder. + // By convention the encoder runs backwards over the input so that the decoder can return + // the data in the expected order. The encoder will start from encoderBuffer.begin() + // and return an iterator one element past the last entry written. + // This means the encodeded message ranges from encoderBuffer.begin() to encodedMessageEnd -1, with (encodedMessageEnd -encoderBuffer.begin()) entries written. + std::map<uint32_t, uint32_t> duplicates; + auto encodedMessageEnd = encoder.process(encoderBuffer.begin(), encoderBuffer.end(), std::begin(T::source), std::end(T::source), duplicates); + + // The decoded message will go into the decoder buffer which will have as many symbols as the original message + std::vector<typename T::source_t> decoderBuffer(std::distance(std::begin(T::source), std::end(T::source)), 0); + // create a stateful decoder object that build a decoding table from the given symbol statistics + const typename T::dedupDecoder_t decoder{frequencies, T::probabilityBits}; // the decoder unwinds the rANS state in the encoder buffer starting at ransBegin and decodes it into the decode buffer; - decoder.process(decoderBuffer.begin(), encodedMessageEnd, stats.getMessageLength()); + decoder.process(decoderBuffer.begin(), encodedMessageEnd, std::distance(std::begin(T::source), std::end(T::source)), duplicates); //the decodeBuffer and the source message have to be identical afterwards. BOOST_REQUIRE(std::memcmp(&(*T::source.begin()), decoderBuffer.data(), diff --git a/Utilities/rANS/test/test_ransFrequencyTable.cxx b/Utilities/rANS/test/test_ransFrequencyTable.cxx new file mode 100644 index 0000000000000..b67d40584201d --- /dev/null +++ b/Utilities/rANS/test/test_ransFrequencyTable.cxx @@ -0,0 +1,80 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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_ransFrequencyTable.cxx +/// @author Michael Lettrich +/// @since Aug 1, 2020 +/// @brief + +#define BOOST_TEST_MODULE Utility test +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include <boost/test/unit_test.hpp> + +#include "rANS/rans.h" + +BOOST_AUTO_TEST_CASE(test_addSamples) +{ + std::vector<int> A{5, 5, 6, 6, 8, 8, 8, 8, 8, -1, -5, 2, 7, 3}; + std::vector<uint32_t> histA{1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 1, 5}; + + o2::rans::FrequencyTable fA; + fA.addSamples(std::begin(A), std::end(A)); + + BOOST_CHECK_EQUAL(fA.getMinSymbol(), -5); + BOOST_CHECK_EQUAL(fA.getMaxSymbol(), 8); + BOOST_CHECK_EQUAL(fA.size(), histA.size()); + + BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(fA), std::end(fA), std::begin(histA), std::end(histA)); + + std::vector<int> B{10, -10}; + fA.addSamples(std::begin(B), std::end(B)); + + BOOST_CHECK_EQUAL(fA.getMinSymbol(), -10); + BOOST_CHECK_EQUAL(fA.getMaxSymbol(), 10); + + std::vector<uint32_t> histAandB{1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 1, 5, 0, 1}; + BOOST_CHECK_EQUAL(fA.size(), histAandB.size()); + + BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(fA), std::end(fA), std::begin(histAandB), std::end(histAandB)); +} + +BOOST_AUTO_TEST_CASE(test_addFrequencies) +{ + std::vector<int> A{5, 5, 6, 6, 8, 8, 8, 8, 8, -1, -5, 2, 7, 3}; + std::vector<uint32_t> histA{1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 1, 5}; + + o2::rans::FrequencyTable ftmp; + ftmp.addSamples(std::begin(A), std::end(A)); + + o2::rans::FrequencyTable fA; + fA.addFrequencies(std::begin(ftmp), std::end(ftmp), ftmp.getMinSymbol(), ftmp.getMaxSymbol()); + + BOOST_CHECK_EQUAL(fA.getMinSymbol(), -5); + BOOST_CHECK_EQUAL(fA.getMaxSymbol(), 8); + BOOST_CHECK_EQUAL(fA.size(), histA.size()); + + BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(fA), std::end(fA), std::begin(histA), std::end(histA)); + + std::vector<int> B{10, 8, -10}; + o2::rans::FrequencyTable fB; + fB.addSamples(std::begin(B), std::end(B)); + + fA = fA + fB; + + BOOST_CHECK_EQUAL(fA.getMinSymbol(), -10); + BOOST_CHECK_EQUAL(fA.getMaxSymbol(), 10); + + std::vector<uint32_t> histAandB{1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 1, 6, 0, 1}; + BOOST_CHECK_EQUAL(fA.size(), histAandB.size()); + + BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(fA), std::end(fA), std::begin(histAandB), std::end(histAandB)); +} diff --git a/cmake/AddRootDictionary.cmake b/cmake/AddRootDictionary.cmake index 7de4ee0809e42..8f627247c9183 100644 --- a/cmake/AddRootDictionary.cmake +++ b/cmake/AddRootDictionary.cmake @@ -111,8 +111,11 @@ function(add_root_dictionary target) set(pcmBase ${dictionary}_rdict.pcm) set(pcmFile ${lib_output_dir}/${pcmBase}) set(rootmapFile ${lib_output_dir}/lib${basename}.rootmap) + + set(O2_TARGETPCMMAP_TARGET "${O2_TARGETPCMMAP_TARGET};${target}" CACHE INTERNAL "target/PCM map (target)") + set(O2_TARGETPCMMAP_PCM "${O2_TARGETPCMMAP_PCM};${pcmFile}" CACHE INTERNAL "target/PCM map (pcm)") - # get the list of compile_definitions + # get the list of compile_definitions set(prop $<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>) # Build the LD_LIBRARY_PATH required to get rootcling running fine @@ -141,10 +144,11 @@ function(add_root_dictionary target) --include_dirs -I$<JOIN:${includeDirs},$<SEMICOLON>-I> $<$<BOOL:${prop}>:--compile_defs> $<$<BOOL:${prop}>:-D$<JOIN:${prop},$<SEMICOLON>-D>> + --pcmdeps "$<REMOVE_DUPLICATES:${list_pcm_deps_${target}}>" --headers "${headers}" COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/${pcmBase} ${pcmFile} - DEPENDS ${headers}) + DEPENDS ${headers} "$<REMOVE_DUPLICATES:${list_pcm_deps_${target}}>") # cmake-format: on # add dictionary source to the target sources diff --git a/cmake/O2ReportNonTestedMacros.cmake b/cmake/O2ReportNonTestedMacros.cmake index 6a204039bff38..4a2b80d398682 100644 --- a/cmake/O2ReportNonTestedMacros.cmake +++ b/cmake/O2ReportNonTestedMacros.cmake @@ -10,9 +10,11 @@ include_guard() -include(O2RootMacroExclusionList) include(O2GetListOfMacros) +set(LIST_OF_ROOT_MACRO_TESTS "" CACHE INTERNAL "List of macros to test for loading") +set(LIST_OF_ROOT_MACRO_TESTS_COMPILED "" CACHE INTERNAL "List of macros to test for compilation") + # # Loop over the Root macros that exists in the repository and check whether # there is one (or two) test for that macro (i.e. the o2_add_test_root_macro @@ -24,6 +26,7 @@ include(O2GetListOfMacros) # simply reported). # function(o2_report_non_tested_macros) + include(O2RootMacroExclusionList) cmake_parse_arguments(PARSE_ARGV 0 diff --git a/cmake/O2RootMacroExclusionList.cmake b/cmake/O2RootMacroExclusionList.cmake index 9b19cd6922f4f..890840ad40e7a 100644 --- a/cmake/O2RootMacroExclusionList.cmake +++ b/cmake/O2RootMacroExclusionList.cmake @@ -17,13 +17,9 @@ include(O2GetListOfMacros) # list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST - Detectors/ITSMFT/ITS/macros/EVE/rootlogon.C - Detectors/ITSMFT/ITS/macros/test/rootlogon.C - Detectors/ITSMFT/ITS/macros/test/CheckCOG.C # temporary exclude until fix for full Clusters elimination Detectors/ITSMFT/ITS/macros/test/CheckLUtime.C # temporary exclude until fix for full Clusters elimination - Detectors/ITSMFT/ITS/macros/test/DisplayTrack.C # temporary exclude until fix for full Clusters elimination Detectors/ITSMFT/ITS/macros/test/dictionary_integrity_test.C # temporary exclude until fix for full Clusters elimination - Detectors/MUON/MCH/Simulation/macros/rootlogon.C + Detectors/MUON/MCH/Geometry/Test/rootlogon.C Detectors/Passive/macro/PutFrameInTop.C Detectors/TPC/reconstruction/macro/addInclude.C Detectors/TPC/reconstruction/macro/getTPCTransformationExample.C @@ -32,12 +28,15 @@ list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST Detectors/TRD/base/macros/Readocdb.C Detectors/TRD/base/macros/PrintTrapConfig.C Detectors/TRD/base/macros/ConvertRun2DigitsAndTracklets.C + Detectors/TRD/macros/convertRun2ToRun3Digits.C + Detectors/TRD/macros/ParseTrapRawOutput.C Detectors/EMCAL/calib/macros/ReadTestBadChannelMap_CCDBApi.C GPU/GPUTracking/Merger/macros/checkPropagation.C # Needs AliRoot AliExternalTrackParam GPU/GPUTracking/Merger/macros/fitPolynomialFieldIts.C # Needs AliRoot AliMagF GPU/GPUTracking/Merger/macros/fitPolynomialFieldTpc.C # Needs AliRoot AliMagF GPU/GPUTracking/Merger/macros/fitPolynomialFieldTrd.C # Needs AliRoot AliMagF GPU/GPUTracking/Standalone/tools/dump.C # Needs AliRoot ALiHLTSystem + GPU/GPUTracking/Standalone/tools/dumpTRDClusterMatrices.C # Needs AliRoot AliCDBManager, AliGeomManager and AliTRDgeometry GPU/GPUTracking/TRDTracking/macros/checkDbgOutput.C # Needs AliRoot TStatToolkit GPU/TPCFastTransformation/alirootMacro/createTPCFastTransform.C # Needs AliTPCCalibDB GPU/TPCFastTransformation/alirootMacro/generateTPCDistortionNTupleAliRoot.C # Needs AliTPCCalibDB @@ -48,11 +47,13 @@ list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST Detectors/TOF/prototyping/ConvertRun2CalibrationToO2.C Generators/share/external/hijing.C Generators/share/external/QEDepem.C + Generators/share/external/GenCosmics.C macro/SetIncludePath.C macro/loadExtDepLib.C macro/load_all_libs.C macro/putCondition.C - macro/rootlogon.C) + macro/rootlogon.C + Detectors/DCS/test/processor_dpcom_o2.C) if(NOT BUILD_SIMULATION) # some complete sub_directories are not added to the build when not building @@ -63,6 +64,7 @@ if(NOT BUILD_SIMULATION) o2_get_list_of_macros(${CMAKE_SOURCE_DIR}/Detectors/gconfig macros) list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST ${macros}) list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST Generators/share/external/QEDLoader.C) + list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST Generators/share/external/GenCosmicsLoader.C) list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST Generators/share/egconfig/pythia8_userhooks_charm.C) list(APPEND O2_ROOT_MACRO_EXCLUSION_LIST Generators/share/external/trigger_mpi.C) endif() diff --git a/cmake/O2SetROOTPCMDependencies.cmake b/cmake/O2SetROOTPCMDependencies.cmake new file mode 100644 index 0000000000000..915cc7858234d --- /dev/null +++ b/cmake/O2SetROOTPCMDependencies.cmake @@ -0,0 +1,42 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does 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_guard() + +set(O2_TARGETPCMMAP_TARGET "" CACHE INTERNAL "target/PCM map (target)") +set(O2_TARGETPCMMAP_PCM "" CACHE INTERNAL "target/PCM map (pcm)") + +function(set_root_pcm_dependencies) + foreach(target pcm IN ZIP_LISTS O2_TARGETPCMMAP_TARGET O2_TARGETPCMMAP_PCM) + if (NOT pcm STREQUAL "") + #message(STATUS "target ${target} has pcm ${pcm}") + list(APPEND target_pcms_${target} ${pcm}) + endif() + endforeach() + + foreach(target pcm IN ZIP_LISTS O2_TARGETPCMMAP_TARGET O2_TARGETPCMMAP_PCM) + if (NOT pcm STREQUAL "") + unset(pcm_dep_list) + get_target_property(dep_targets ${target} LINK_LIBRARIES) + foreach(dep_target IN LISTS dep_targets) + if (dep_target MATCHES "^O2::") + string(REPLACE "O2::" "" dep_target ${dep_target}) + o2_name_target(${dep_target} NAME dep_target) + #message(STATUS "target ${target} depends on ${dep_target}") + foreach(dep_pcm IN LISTS target_pcms_${dep_target}) + #message(STATUS "${pcm} depends on ${dep_pcm}") + list(APPEND pcm_dep_list ${dep_pcm}) + endforeach() + endif() + endforeach() + set(list_pcm_deps_${target} "${pcm_dep_list}" CACHE INTERNAL "List of pcm dependencies for ${target}") + endif() + endforeach() +endfunction() diff --git a/cmake/rootcling_wrapper.sh.in b/cmake/rootcling_wrapper.sh.in index 1a53153cd0374..72e101cd1cbf8 100755 --- a/cmake/rootcling_wrapper.sh.in +++ b/cmake/rootcling_wrapper.sh.in @@ -37,6 +37,10 @@ while [[ $# -gt 0 ]]; do ROOTMAP_FILE="$2" shift 2 ;; + --pcmdeps) + PCMDEPS="$2" + shift 2 + ;; *) if [[ -z "$1" ]]; then shift @@ -68,16 +72,32 @@ if [[ ! $ROOTMAP_FILE ]]; then exit 1 fi +case $OSTYPE in + darwin*) + unset PCMDEPS + ;; + *) + ;; +esac + LOGFILE=${DICTIONARY_FILE}.log @CMAKE_COMMAND@ -E env LD_LIBRARY_PATH=${LD_LIBRARY_PATH} @ROOT_rootcling_CMD@ \ -f $DICTIONARY_FILE \ -inlineInputHeader \ + -noGlobalUsingStd \ -rmf ${ROOTMAP_FILE} \ -rml ${ROOTMAP_LIBRARY_NAME} \ ${INCLUDE_DIRS//;/ } \ ${COMPILE_DEFINITIONS//;/ } \ - ${HEADERS//;/ } > ${LOGFILE} 2>&1 || cat ${LOGFILE} >&2 + ${PCMDEPS:+-m }${PCMDEPS//;/ -m } \ + ${HEADERS//;/ } \ + > ${LOGFILE} 2>&1 || cat ${LOGFILE} >&2 + +if [[ $? != "0" ]]; then + rm $DICTIONARY_FILE + exit 1 +fi MSG="Warning: Unused class rule" if [[ -s ${LOGFILE} ]]; then diff --git a/dependencies/FindFastJet.cmake b/dependencies/FindFastJet.cmake index 6d6ad46b7e3a9..a61abdf7e7a28 100644 --- a/dependencies/FindFastJet.cmake +++ b/dependencies/FindFastJet.cmake @@ -107,6 +107,13 @@ if(${${PKGNAME}_FOUND}) foreach(incdir ${${PKGNAME}_INCLUDE_DIRS}) target_include_directories(${PKGNAME}::${PKGNAME} INTERFACE ${incdir}) endforeach() + + find_library(lib_contrib NAMES "fastjetcontribfragile" PATHS ${${PKGNAME}_LIB_DIRS} NO_DEFAULT_PATH) + if(lib_contrib) + message(STATUS "adding FastJet contrib") + add_library(${PKGNAME}::Contrib IMPORTED INTERFACE GLOBAL) + target_link_libraries(${PKGNAME}::Contrib INTERFACE ${lib_contrib}) + endif() endif() unset(PKGNAME) diff --git a/dependencies/FindFlukaVMC.cmake b/dependencies/FindFlukaVMC.cmake new file mode 100644 index 0000000000000..eb7f138f3085d --- /dev/null +++ b/dependencies/FindFlukaVMC.cmake @@ -0,0 +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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +# use the GEANT4_VMCConfig.cmake provided by the Geant4VMC installation but +# amend the target geant4vmc with the include directories + +find_package(FlukaVMC NO_MODULE) +if(NOT FlukaVMC_FOUND) +return() +endif() + +set_target_properties(flukavmc + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${FlukaVMC_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES + $<TARGET_FILE_DIR:flukavmc>) + +# Promote the imported target to global visibility +# (so we can alias it) +set_target_properties(flukavmc PROPERTIES IMPORTED_GLOBAL TRUE) + +add_library(MC::FlukaVMC ALIAS flukavmc) diff --git a/dependencies/FindHepMC3.cmake b/dependencies/FindHepMC3.cmake new file mode 100644 index 0000000000000..3f9233814f66d --- /dev/null +++ b/dependencies/FindHepMC3.cmake @@ -0,0 +1,28 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +# use the HepMCConfig.cmake provided by the HepMC3 installation to create a +# single target HepMC with the include directories and libraries we need + +find_package(HepMC3 NO_MODULE) +if(NOT HepMC3_FOUND) + return() +endif() + +add_library(HepMC3 IMPORTED INTERFACE) + +set_target_properties(HepMC3 + PROPERTIES + INTERFACE_LINK_LIBRARIES "${HEPMC3_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${HEPMC3_INCLUDE_DIR}") + +# Promote the imported target to global visibility (so we can alias it) +set_target_properties(HepMC3 PROPERTIES IMPORTED_GLOBAL TRUE) +add_library(MC::HepMC3 ALIAS HepMC3) diff --git a/dependencies/FindJAliEnROOT.cmake b/dependencies/FindJAliEnROOT.cmake new file mode 100644 index 0000000000000..b48e90b40a9b4 --- /dev/null +++ b/dependencies/FindJAliEnROOT.cmake @@ -0,0 +1,33 @@ +# 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". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. +find_path(JALIEN_ROOT_INCLUDE_DIR TJAlienFile.h PATH_SUFFIXES include + PATHS + ${JALIEN_ROOT_ROOT}) + +find_library(JAliEnRoot_LIB JAliEnROOT PATHS ${JALIEN_ROOT_ROOT}/lib) + +if(NOT JALIEN_ROOT_INCLUDE_DIR) + set(JAliEnROOT_FOUND FALSE) + return() +endif() + +set(JAliEnROOT_FOUND TRUE) + +if(NOT TARGET JAliEn::JAliEn) + get_filename_component(libdir ${JAliEnRoot_LIB} DIRECTORY) + add_library(JAliEn::JAliEn INTERFACE IMPORTED) + set_target_properties(JAliEn::JAliEn PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${JALIEN_ROOT_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${JAliEnRoot_LIB} + INTERFACE_LINK_DIRECTORIES ${libdir} + ) +endif() + +mark_as_advanced(JALIEN_ROOT_INCLUDE_DIR) diff --git a/dependencies/FindLibUV.cmake b/dependencies/FindLibUV.cmake index f284c6e563710..4f193a6f2a5da 100644 --- a/dependencies/FindLibUV.cmake +++ b/dependencies/FindLibUV.cmake @@ -50,11 +50,13 @@ They may be set by end users to point at libuv components. #----------------------------------------------------------------------------- find_library(LibUV_LIBRARY NAMES uv libuv + PATHS $ENV{LIBUV_ROOT}/lib ) mark_as_advanced(LibUV_LIBRARY) find_path(LibUV_INCLUDE_DIR NAMES uv.h + PATHS $ENV{LIBUV_ROOT}/include ) mark_as_advanced(LibUV_INCLUDE_DIR) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 2a91c46dda45b..3677bc9080249 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -27,8 +27,8 @@ string(TOUPPER "${ENABLE_HIP}" ENABLE_HIP) # Detect and enable CUDA if(ENABLE_CUDA) - set(CUDA_MINIMUM_VERSION "10.1") - set(CMAKE_CUDA_STANDARD 14) + set(CUDA_MINIMUM_VERSION "11.0") + set(CMAKE_CUDA_STANDARD 17) set(CMAKE_CUDA_STANDARD_REQUIRED TRUE) include(CheckLanguage) check_language(CUDA) @@ -39,14 +39,8 @@ if(ENABLE_CUDA) set(CMAKE_CUDA_COMPILER "nvcc") #check_language does not treat the HOST_COMPILER flag correctly, we force it and will fail below if wrong. endif() endif() - if (ENABLE_CUDA STREQUAL "AUTO") - find_package(cub) - set_package_properties(cub PROPERTIES TYPE OPTIONAL) - else() - find_package(cub REQUIRED) - set_package_properties(cub PROPERTIES TYPE REQUIRED) - endif() - if(CMAKE_CUDA_COMPILER AND CUB_FOUND) + if(CMAKE_CUDA_COMPILER) + cmake_minimum_required(VERSION 3.18 FATAL_ERROR) enable_language(CUDA) get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) if(NOT CUDA IN_LIST LANGUAGES) @@ -59,7 +53,7 @@ if(ENABLE_CUDA) # Forward CXX flags to CUDA C++ Host compiler (for warnings, gdb, etc.) STRING(REGEX REPLACE "\-std=[^ ]*" "" CMAKE_CXX_FLAGS_NOSTD ${CMAKE_CXX_FLAGS}) # Need to strip c++17 imposed by alidist defaults - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler \"${CMAKE_CXX_FLAGS_NOSTD}\" --expt-relaxed-constexpr -Xptxas -v") + set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler \"${CMAKE_CXX_FLAGS_NOSTD}\" --expt-relaxed-constexpr --extended-lambda --allow-unsupported-compiler -Xptxas -v") set(CMAKE_CUDA_FLAGS_DEBUG "${CMAKE_CUDA_FLAGS_DEBUG} -lineinfo -Xcompiler \"${CMAKE_CXX_FLAGS_DEBUG}\" -Xptxas -O0 -Xcompiler -O0") if(NOT CMAKE_BUILD_TYPE STREQUAL "DEBUG") set(CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE}} -Xcompiler \"${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}\" -Xptxas -O4 -Xcompiler -O4 -use_fast_math --ftz=true") @@ -68,16 +62,15 @@ if(ENABLE_CUDA) set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Werror=cross-execution-space-call") endif() if(CUDA_COMPUTETARGET) - set( - CMAKE_CUDA_FLAGS - "${CMAKE_CUDA_FLAGS} -gencode arch=compute_${CUDA_COMPUTETARGET},code=sm_${CUDA_COMPUTETARGET}" - ) + set(CMAKE_CUDA_ARCHITECTURES ${CUDA_COMPUTETARGET}) + else() + set(CMAKE_CUDA_ARCHITECTURES 61-virtual) endif() set(CUDA_ENABLED ON) message(STATUS "CUDA found (Version ${CMAKE_CUDA_COMPILER_VERSION})") elseif(NOT ENABLE_CUDA STREQUAL "AUTO") - message(FATAL_ERROR "CUDA not found (Compiler: ${CMAKE_CUDA_COMPILER}, CUB: ${CUB_FOUND})") + message(FATAL_ERROR "CUDA not found (Compiler: ${CMAKE_CUDA_COMPILER})") endif() endif() @@ -167,21 +160,11 @@ if(ENABLE_HIP) CACHE PATH "Path to which HIP has been installed") endif() endif() - if(NOT DEFINED HCC_HOME) - if(NOT DEFINED ENV{HCC_HOME}) - set(HCC_HOME - "${HIP_PATH}/../hcc" - CACHE PATH "Path to which HCC has been installed") - else() - set(HCC_HOME - $ENV{HCC_HOME} - CACHE PATH "Path to which HCC has been installed") - endif() - endif() - if(EXISTS "${HIP_PATH}" AND EXISTS "${HCC_HOME}") + if(EXISTS "${HIP_PATH}") get_filename_component(hip_ROOT "${HIP_PATH}" ABSOLUTE) - get_filename_component(hcc_ROOT "${HCC_HOME}" ABSOLUTE) + set(ROCM_WARN_TOOLCHAIN_VAR OFF CACHE BOOL "" FORCE) + set(ROCM_ERROR_TOOLCHAIN_VAR OFF CACHE BOOL "" FORCE) find_package(hip) find_package(hipcub) find_package(rocprim) @@ -199,15 +182,18 @@ if(ENABLE_HIP) endif() if(hip_FOUND AND hipcub_FOUND AND rocthrust_FOUND AND rocprim_FOUND AND hip_HIPCC_EXECUTABLE) set(HIP_ENABLED ON) - set_target_properties(rocthrust PROPERTIES IMPORTED_GLOBAL TRUE) - add_library(ROCm::rocThrust ALIAS rocthrust) + set_target_properties(roc::rocthrust PROPERTIES IMPORTED_GLOBAL TRUE) message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE})") + set(O2_HIP_CMAKE_CXX_FLAGS "-fcuda-flush-denormals-to-zero -Wno-invalid-command-line-argument -Wno-unused-command-line-argument -Wno-invalid-constexpr -Wno-ignored-optimization-argument -Wno-unused-private-field") + if(HIP_AMDGPUTARGET) + set(O2_HIP_CMAKE_CXX_FLAGS "${O2_HIP_CMAKE_CXX_FLAGS} --amdgpu-target=${HIP_AMDGPUTARGET}") + endif() endif() endif() if(NOT HIP_ENABLED AND NOT ENABLE_HIP STREQUAL "AUTO") message( FATAL_ERROR - "HIP requested but HIP_PATH=${HIP_PATH} or HCC_HOME=${HCC_HOME} does not exist" + "HIP requested but HIP_PATH=${HIP_PATH} does not exist" ) endif() diff --git a/dependencies/FindXRootD.cmake b/dependencies/FindXRootD.cmake new file mode 100644 index 0000000000000..23167369eb781 --- /dev/null +++ b/dependencies/FindXRootD.cmake @@ -0,0 +1,236 @@ +################################################################################ +# Module for locating XRootD. +# +# XROOTD_FOUND +# Indicates whether the library has been found. +# +# XROOTD_INCLUDE_DIRS +# Specifies XRootD include directory. +# +# XROOTD_LIBRARIES +# Specifies XRootD libraries that should be passed to target_link_libararies. +# +# XROOTD_<COMPONENT>_LIBRARIES +# Specifies the libraries of a specific <COMPONENT> +# +# XROOTD_<COMPONENT>_FOUND +# Indicates whether the specified <COMPONENT> was found. +# +# List of components: CLIENT, UTILS, SERVER, POSIX, HTTP and SSI +################################################################################ + + +################################################################################ +# Set XRootD include paths +################################################################################ +FIND_PATH( XROOTD_INCLUDE_DIRS XrdVersion.hh + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES include/xrootd + PATHS /opt/xrootd +) + +IF( NOT "${XROOTD_INCLUDE_DIRS}" STREQUAL "XROOTD_INCLUDE_DIRS-NOTFOUND" ) + SET( XROOTD_FOUND TRUE ) +ENDIF() + +IF( NOT XROOTD_FOUND ) + LIST( APPEND _XROOTD_MISSING_COMPONENTS XROOTD_FOUND ) +ENDIF() + +################################################################################ +# XRootD client libs +# - libXrdCl +################################################################################ +FIND_LIBRARY( XROOTD_CLIENT_LIBRARIES XrdCl + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +IF( NOT "${XROOTD_CLIENT_LIBRARIES}" STREQUAL "XROOTD_CLIENT_LIBRARIES-NOTFOUND" ) + SET( XROOTD_CLIENT_FOUND TRUE ) + LIST( APPEND XROOTD_LIBRARIES ${XROOTD_CLIENT_LIBRARIES} ) +ENDIF() + +IF( XRootD_FIND_REQUIRED_CLIENT AND NOT XROOTD_CLIENT_FOUND ) + MESSAGE( "XRootD client required but not found!" ) + LIST( APPEND _XROOTD_MISSING_COMPONENTS XROOTD_CLIENT_FOUND ) + UNSET( XROOTD_FOUND ) +ENDIF() + +################################################################################ +# XRootD utils libs +# - libXrdUtils +################################################################################ +FIND_LIBRARY( XROOTD_UTILS_LIBRARIES XrdUtils + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +IF( NOT "${XROOTD_UTILS_LIBRARIES}" STREQUAL "XROOTD_UTILS_LIBRARIES-NOTFOUND" ) + SET( XROOTD_UTILS_FOUND TRUE ) + LIST( APPEND XROOTD_LIBRARIES ${XROOTD_UTILS_LIBRARIES} ) +ENDIF() + +IF( XRootD_FIND_REQUIRED_UTILS AND NOT XROOTD_UTILS_FOUND ) + MESSAGE( "XRootD utils required but not found!" ) + LIST( APPEND _XROOTD_MISSING_COMPONENTS XROOTD_UTILS_FOUND ) + UNSET( XROOTD_FOUND ) +ENDIF() + +################################################################################ +# XRootD server libs +# - libXrdServer +################################################################################ +FIND_LIBRARY( XROOTD_SERVER_LIBRARIES XrdServer + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +IF( NOT "${XROOTD_SERVER_LIBRARIES}" STREQUAL "XROOTD_SERVER_LIBRARIES-NOTFOUND" ) + SET( XROOTD_SERVER_FOUND TRUE ) + LIST( APPEND XROOTD_LIBRARIES ${XROOTD_SERVER_LIBRARIES} ) +ENDIF() + +IF( XRootD_FIND_REQUIRED_SERVER AND NOT XROOTD_SERVER_FOUND ) + MESSAGE( "XRootD server required but not found!" ) + LIST( APPEND _XROOTD_MISSING_COMPONENTS XROOTD_SERVER_FOUND ) + UNSET( XROOTD_FOUND ) +ENDIF() + +################################################################################ +# XRootD posix libs +# - libXrdPosix +# - libXrdPosixPreload +################################################################################ +FIND_LIBRARY( XROOTD_POSIX_LIBRARY XrdPosix + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +FIND_LIBRARY( XROOTD_POSIX_PRELOAD_LIBRARY XrdPosixPreload + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +IF( NOT "${XROOTD_POSIX_LIBRARY}" STREQUAL "XROOTD_POSIX_LIBRARY-NOTFOUND" ) + IF( NOT "${XROOTD_POSIX_PRELOAD_LIBRARY}" STREQUAL "XROOTD_POSIX_PRELOAD_LIBRARY-NOTFOUND" ) + SET( XROOTD_POSIX_LIBRARIES ${XROOTD_POSIX_LIBRARY} ${XROOTD_POSIX_PRELOAD_LIBRARY} ) + SET( XROOTD_POSIX_FOUND TRUE ) + LIST( APPEND XROOTD_LIBRARIES ${XROOTD_POSIX_LIBRARIES} ) + ENDIF() +ENDIF() + +IF( XRootD_FIND_REQUIRED_POSIX AND NOT XROOTD_POSIX_FOUND ) + MESSAGE( "XRootD posix required but not found!" ) + LIST( APPEND _XROOTD_MISSING_COMPONENTS XROOTD_POSIX_FOUND ) + UNSET( XROOTD_FOUND ) +ENDIF() + +################################################################################ +# XRootD HTTP (XrdHttp) libs +# - libXrdHtppUtils +################################################################################ +FIND_LIBRARY( XROOTD_HTTP_LIBRARIES XrdHttpUtils + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +IF( NOT "${XROOTD_HTTP_LIBRARIES}" STREQUAL "XROOTD_HTTP_LIBRARIES-NOTFOUND" ) + SET( XROOTD_HTTP_FOUND TRUE ) + LIST( APPEND XROOTD_LIBRARIES ${XROOTD_HTTP_LIBRARIES} ) +ENDIF() + +IF( XRootD_FIND_REQUIRED_HTTP AND NOT XROOTD_HTTP_FOUND ) + MESSAGE( "XRootD http required but not found!" ) + LIST( APPEND _XROOTD_MISSING_COMPONENTS XROOTD_HTTP_FOUND ) + UNSET( XROOTD_FOUND ) +ENDIF() + +################################################################################ +# XRootD SSI libs +# - XrdSsiLib +# - XrdSsiShMap +################################################################################ +FIND_LIBRARY( XROOTD_SSI_LIBRARY XrdSsiLib + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +FIND_LIBRARY( XROOTD_SSI_SHMAP_LIBRARY XrdSsiShMap + HINTS + ${XROOTD_DIR} + $ENV{XROOTD_DIR} + /usr + /opt/xrootd + PATH_SUFFIXES lib lib64 +) + +IF( NOT "${XROOTD_SSI_LIBRARY}" STREQUAL "XROOTD_SSI_LIBRARY-NOTFOUND" ) + IF( NOT "${XROOTD_SSI_SHMAP_LIBRARY}" STREQUAL "XROOTD_SSI_SHMAP_LIBRARY-NOTFOUND" ) + SET( XROOTD_SSI_LIBRARIES ${XROOTD_SSI_LIBRARY} ${XROOTD_SSI_SHMAP_LIBRARY} ) + SET( XROOTD_SSI_FOUND TRUE ) + LIST( APPEND XROOTD_LIBRARIES ${XROOTD_SSI_LIBRARIES} ) + ENDIF() +ENDIF() + +IF( XRootD_FIND_REQUIRED_SSI AND NOT XROOTD_SSI_FOUND ) + MESSAGE( "XRootD ssi required but not found!" ) + LIST (APPEND _XROOTD_MISSING_COMPONENTS XROOTD_SSI_FOUND ) + UNSET( XROOTD_FOUND ) +ENDIF() + +################################################################################ +# Set up the XRootD find module +################################################################################ + +IF( XRootD_FIND_REQUIRED ) + INCLUDE( FindPackageHandleStandardArgs ) + FIND_PACKAGE_HANDLE_STANDARD_ARGS( XRootD + REQUIRED_VARS XROOTD_INCLUDE_DIRS ${_XROOTD_MISSING_COMPONENTS} + ) +ENDIF() + + +if (XROOTD_CLIENT_FOUND) +if(NOT TARGET XRootD::Client) + get_filename_component(libdir ${XROOTD_CLIENT_LIBRARIES} DIRECTORY) + add_library(XRootD::Client INTERFACE IMPORTED) + set_target_properties(XRootD::Client PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${XROOTD_INCLUDE_DIRS} + INTERFACE_LINK_LIBRARIES ${XROOTD_CLIENT_LIBRARIES} + INTERFACE_LINK_DIRECTORIES ${libdir} + ) +endif() +endif() diff --git a/dependencies/Findcub.cmake b/dependencies/Findcub.cmake deleted file mode 100644 index 733799b2c4f2f..0000000000000 --- a/dependencies/Findcub.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# 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". -# -# See http://alice-o2.web.cern.ch/license for full licensing information. -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. - -find_path(CUB_INCLUDE_DIR cub/cub.cuh PATHS ${cub_ROOT} $ENV{CUB_ROOT} ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) - -if(NOT CUB_INCLUDE_DIR) - set(CUB_FOUND FALSE) - return() -endif() - -set(CUB_FOUND TRUE) - -if(NOT TARGET cub::cub) - add_library(cub::cub INTERFACE IMPORTED) - target_include_directories(cub::cub SYSTEM INTERFACE ${CUB_INCLUDE_DIR}) -endif() - -mark_as_advanced(CUB_INCLUDE_DIR) diff --git a/dependencies/Findms_gsl.cmake b/dependencies/Findms_gsl.cmake index b3098d1468197..64ae38d326701 100644 --- a/dependencies/Findms_gsl.cmake +++ b/dependencies/Findms_gsl.cmake @@ -9,7 +9,7 @@ # submit itself to any jurisdiction. find_path(MS_GSL_INCLUDE_DIR gsl/gsl PATH_SUFFIXES ms_gsl/include include - PATHS $ENV{MS_GSL_ROOT}) + HINTS $ENV{MS_GSL_ROOT}) if(NOT MS_GSL_INCLUDE_DIR) set(MS_GSL_FOUND FALSE) diff --git a/dependencies/O2CompileFlags.cmake b/dependencies/O2CompileFlags.cmake index f54e05cad7b4b..85a98847e1511 100644 --- a/dependencies/O2CompileFlags.cmake +++ b/dependencies/O2CompileFlags.cmake @@ -33,6 +33,9 @@ IF(ENABLE_CASSERT) #For the CI, we want to have <cassert> assertions enabled ELSE() set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG") + if (CMAKE_BUILD_TYPE STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") + set(FAIR_MIN_SEVERITY "info") + endif() ENDIF() set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_Fortran_FLAGS_RELEASE "-O2") diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 93cc13d58df3e..b1e751a46ff79 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -103,6 +103,12 @@ set_package_properties(RapidJSON PROPERTIES TYPE REQUIRED) find_package(CURL) set_package_properties(CURL PROPERTIES TYPE REQUIRED) +find_package(JAliEnROOT MODULE) +set_package_properties(JAliEnROOT PROPERTIES TYPE RECOMMENDED) + +find_package(XRootD MODULE) +set_package_properties(XRootD PROPERTIES TYPE RECOMMENDED) + # MC specific packages message(STATUS "Input BUILD_SIMULATION=${BUILD_SIMULATION}") include("${CMAKE_CURRENT_LIST_DIR}/O2SimulationDependencies.cmake") diff --git a/dependencies/O2FindDependenciesFromAliBuild.cmake b/dependencies/O2FindDependenciesFromAliBuild.cmake index 6cb2795804f6c..2b551a01dc189 100644 --- a/dependencies/O2FindDependenciesFromAliBuild.cmake +++ b/dependencies/O2FindDependenciesFromAliBuild.cmake @@ -154,6 +154,7 @@ function(o2_find_dependencies_from_alibuild) protected_set_root(Geant3 GEANT3) protected_set_root(Geant4 GEANT4) protected_set_root(Geant4VMC GEANT4_VMC) + protected_set_root(FlukaVMC FLUKA_VMC) protected_set_root(VGM vgm) protected_set_root(HepMC HepMC3) diff --git a/dependencies/O2SimulationDependencies.cmake b/dependencies/O2SimulationDependencies.cmake index 7231c58706de8..e62073d18bc44 100644 --- a/dependencies/O2SimulationDependencies.cmake +++ b/dependencies/O2SimulationDependencies.cmake @@ -63,14 +63,18 @@ set_package_properties(Geant4 find_package(Geant4VMC MODULE) set_package_properties(Geant4VMC PROPERTIES TYPE ${mcPackageRequirement}) +find_package(FlukaVMC MODULE) +set_package_properties(FlukaVMC PROPERTIES TYPE OPTIONAL) + find_package(VGM MODULE) set_package_properties(VGM PROPERTIES TYPE ${mcPackageRequirement}) -find_package(HepMC MODULE) -set_package_properties(HepMC +find_package(HepMC3 MODULE) +set_package_properties(HepMC3 PROPERTIES - TYPE ${mcPackageRequirement} DESCRIPTION - "the HepMC3 event record package") + TYPE OPTIONAL DESCRIPTION + "the HepMC3 event record package") + set(doBuildSimulation OFF) if(pythia_FOUND @@ -79,7 +83,7 @@ if(pythia_FOUND AND Geant4_FOUND AND Geant4VMC_FOUND AND VGM_FOUND - AND HepMC_FOUND) + AND HepMC3_FOUND) set(doBuildSimulation ON) endif() diff --git a/doc/DetectorSimulation.md b/doc/DetectorSimulation.md index 4ed2e4df2d5f8..978bcd6b51005 100644 --- a/doc/DetectorSimulation.md +++ b/doc/DetectorSimulation.md @@ -92,6 +92,7 @@ Important parameters influencing the transport simulation are: | Diamond | Parameter allowing to set the interaction vertex location and the spread/width. Is used in all event generators. | | Pythia6 | Parameters that influence the pythia6 generator. | | Pythia8 | Parameters that influence the pythia8 generator. | +| HepMC | Parameters that influence the HepMC generator. | | TriggerParticle | Parameters influencing the trigger mechanism in particle generators. | Detectors may also have parameters influencing various pieces such geometry layout, material composition etc. @@ -131,15 +132,15 @@ o2-sim -m PIPE ITS MFT -g pythia8 -n 50 You may contribute to the documentation by asking a question #### 1. **How can I interface an event generator from ALIROOT**? -In order to access event generators from ALIROOT, such as `THijing` or `TPyhtia6`, you may use the `-g extgen` command line option followed by a ROOT macro setting up the event +In order to access event generators from ALIROOT, such as `THijing` or `TPyhtia6`, you may use the `-g external` command line option followed by a ROOT macro setting up the event generator. Examples thereof are available in the installation directory `$O2_ROOT/share/Generators/external`. For example, in order to simulate with 10 Pythia6 events, the following command can be run: ``` -o2-sim -n 10 -g extgen --extGenFile $O2_ROOT/share/Generators/external/pythia6.C +o2-sim -n 10 -g external --configKeyValues 'GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/pythia6.C' ``` -Macro arguments can be passed via -`--extGenFunc pythia6(14000., "pythia.settings")`. +Macro arguments can be passed setting `GeneratorExternal.funcName` +`GeneratorExternal.funcName=pythia6(14000., "pythia.settings")`. Users may write there own macros in order to customize to their needs. @@ -186,7 +187,7 @@ o2-sim -g pythia8 -t particle --configKeyValues "TriggerParticle.pdg=333;Trigger Custom triggers can also be constructed by the user to provide unlimited flexibility in the trigger needs. An external trigger function can be specified via command line arguments ``` -o2-sim -g pythia8 -t external --extTrgFile path_to_trigger_macro.C --extTrgFunc "the_function(some, parameters)" +o2-sim -g pythia8 -t external --configKeyValues 'TriggerExternal.fileName=path_to_trigger_macro.C;TriggerExternal.funcName="the_function(some, parameters)"' ``` The function must comply with a simple protocol and return a lambda function defined as follows ``` @@ -204,7 +205,7 @@ To allow users to define triggers that go beyond the particle stack generated by This allows the user to go deep into the core of the event generator, whenever this is possible. For this reason, this is called a 'DeepTrigger'. A 'DeepTrigger' is attached to the simulation in the same way as a normal trigger ``` -o2-sim -g pythia8 -t external --extTrgFile path_to_deep_trigger_macro.C --extTrgFunc "the_deep_function(some, parameters)" +o2-sim -g pythia8 -t external --configKeyValues 'TriggerExternal.fileName=path_to_deep_trigger_macro.C;TriggerExternal.funcName="the_deep_function(some, parameters)"' ``` In this case the function must comply with a similar, but different protocol than before and return a lambda function defined as follows ``` o2::eventgen::DeepTrigger the_deep_function() @@ -226,8 +227,7 @@ Deep triggers is just a name to a new functionality that allows the user to defi Here is an example of a deep trigger implementation in Pythia8. ``` -// usage: o2sim --trigger external --extTrgFile trigger_mpi.C -// options: --extTrgFunc "trigger_mpi()" +// usage: o2sim --trigger external --configKeyValues 'TriggerExternal.fileName=trigger_mpi.C;TriggerExternal.funcName="trigger_mpi()"' #include "Generators/Trigger.h" #include "Pythia8/Pythia.h" @@ -258,7 +258,7 @@ This functionality might be of use for users who want to be able to steer the ev An example of a configuration macro is this one ``` -// usage: o2sim -g pythia8 --configKeyValue "Pythia8.hooksFileName=pythia8_userhooks_charm.C" +// usage: o2sim -g pythia8 --configKeyValues "GeneratorPythia8.hooksFileName=pythia8_userhooks_charm.C" #include "Generators/Trigger.h" #include "Pythia8/Pythia.h" @@ -293,8 +293,7 @@ Pythia8::UserHooks* A new Pythia6 interface is provided via GeneratorPythia6. This complies with the o2::eventgen::Generator protocol, and hence the user is allowed to use all the trigger functionalities. The class can also be used for DeepTriggers as this modified macro shows. ``` -// usage: o2sim --trigger external --extTrgFile trigger_mpi.C -// options: --extTrgFunc "trigger_mpi()" +// usage: o2sim --trigger external --configKeyValues "TriggerExternal.fileName=trigger_mpi.C;TriggerExternal.funcName="trigger_mpi()"' #include "Generators/Trigger.h" #include "Pythia8/Pythia.h" @@ -442,6 +441,8 @@ for (int pos = 0; pos < alldigits.size(); ++pos) { } } ``` +Note, that one can also access kinematics directly after the transport simulation. +In this case, one needs to initialize the MCKinematicsReader in a different mode. # Simulation tutorials/examples <a name="Examples"></a> @@ -450,10 +451,15 @@ Other helpful resources are the scripts used for regression testing in [prodtest | Example | Short Description | | --------------------- | -------------------------------------------------------------------------------------- | +| [HF_Embedding_Pythia8](../run/SimExamples/HF_Embedding_Pythia8) | Example showing how to setup a complex HF simulation for embedding | | [AliRoot_Hijing](../run/SimExamples/AliRoot_Hijing) | Example showing how to use Hijing from AliRoot for event generation | +| [AliRoot_AMPT](../run/SimExamples/AliRoot_AMPT) | Example showing how to use AMPT from AliRoot for event generation | | [Adaptive_Pythia8](../run/SimExamples/Adaptive_Pythia8) | Complex example showing **generator configuration for embedding** that cat adapt the response based on the background event | | [Signal_ImpactB](../run/SimExamples/Signal_ImpactB) | Example showing **generator configuration for embedding** that cat adapt to the impact parameter of the background event | +| [PrimaryKinematics](../run/SimExamples/JustPrimaryKinematics) | Example showing how to obtain only primary kinematics via transport configuration | | [HepMC_STARlight](../run/SimExamples/HepMC_STARlight) | Simple example showing **generator configuration** that runs a standalone `STARlight` generation that couples to the `o2` via a `HepMC` file | | [Jet_Embedding_Pythia](../run/SimExamples/Jet_Embedding_Pythia8) | Complex example showing **generator configuration**, **digitization embedding**, **MCTrack access** | +| [Selective_Transport](../run/SimExamples/Selective_Transport) | Simple example showing how to run simulation transporting only a custumisable set of particles | +| [Custom_EventInfo](../run/SimExamples/Custom_EventInfo) | Simple example showing how to add custom information to the MC event header | | [sim_challenge.sh](../prodtests/sim_challenge.sh) | Basic example doing a **simple transport, digitization, reconstruction pipeline** on the full dectector. All stages use parallelism. | | [sim_performance.sh](../prodtests/sim_performance_test.sh) | Basic example for serial transport and linearized digitization sequence (one detector after the other). Serves as standard performance candle. | diff --git a/doc/README.md b/doc/README.md index 4c8224a712850..68520ab356009 100644 --- a/doc/README.md +++ b/doc/README.md @@ -14,4 +14,6 @@ This module contains the documentation pages. * \subpage refdocDetectorSimulation * \subpage refdocDoxygenInstructions * \subpage refdocManPages +* \subpage refdocUpgrades +* \subpage refprodtestsfull-system-test /doxy --> diff --git a/doc/Upgrades.md b/doc/Upgrades.md index e0de38e04c19d..1051082894dab 100644 --- a/doc/Upgrades.md +++ b/doc/Upgrades.md @@ -1,3 +1,7 @@ +<!-- doxy +\page refdocUpgrades Upgrades +/doxy --> + # Detector upgrades in O2 ## Conventions diff --git a/doc/data/2021-01-o2_prs.json b/doc/data/2021-01-o2_prs.json new file mode 100644 index 0000000000000..57cb126d02555 --- /dev/null +++ b/doc/data/2021-01-o2_prs.json @@ -0,0 +1,3818 @@ +{ + "repository": { + "pullRequests": { + "edges": [ + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-11T08:46:01Z", + "title": "Populate MC event header with information from current Pythia8 event", + "number": 5133, + "author": { + "login": "preghenella" + }, + "files": { + "edges": [ + { + "node": { + "path": "Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h" + } + }, + { + "node": { + "path": "DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h" + } + }, + { + "node": { + "path": "DataFormats/simulation/src/MCEventHeader.cxx" + } + }, + { + "node": { + "path": "Generators/include/Generators/Generator.h" + } + }, + { + "node": { + "path": "Generators/include/Generators/GeneratorPythia8.h" + } + }, + { + "node": { + "path": "Generators/src/Generator.cxx" + } + }, + { + "node": { + "path": "Generators/src/GeneratorPythia8.cxx" + } + }, + { + "node": { + "path": "doc/DetectorSimulation.md" + } + }, + { + "node": { + "path": "run/SimExamples/Adaptive_Pythia8/adaptive_pythia8.macro" + } + }, + { + "node": { + "path": "run/SimExamples/AliRoot_AMPT/aliroot_ampt.macro" + } + }, + { + "node": { + "path": "run/SimExamples/Custom_EventInfo/generator.macro" + } + }, + { + "node": { + "path": "run/SimExamples/Custom_EventInfo/pythia8_inel.cfg" + } + }, + { + "node": { + "path": "run/SimExamples/Custom_EventInfo/read_event_info.macro" + } + }, + { + "node": { + "path": "run/SimExamples/Custom_EventInfo/run.sh" + } + }, + { + "node": { + "path": "run/SimExamples/Custom_EventInfo/sim.ini" + } + }, + { + "node": { + "path": "run/SimExamples/HF_Embedding_Pythia8/GeneratorHF.macro" + } + }, + { + "node": { + "path": "run/SimExamples/README.md" + } + }, + { + "node": { + "path": "run/SimExamples/Signal_ImpactB/signal_impactb.macro" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-08T08:24:42Z", + "title": "RootSerializableKeyValueStore: Add print function", + "number": 5134, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h" + } + }, + { + "node": { + "path": "Common/Utils/src/RootSerializableKeyValueStore.cxx" + } + }, + { + "node": { + "path": "Common/Utils/test/testRootSerializableKeyValueStore.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-08T19:51:25Z", + "title": "Add trigger inputs branch ", + "number": 5135, + "author": { + "login": "AllaMaevskaya" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h" + } + }, + { + "node": { + "path": "Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h" + } + }, + { + "node": { + "path": "Detectors/FIT/FT0/simulation/src/Digitizer.cxx" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/FT0DigitWriterSpec.h" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/FT0DigitizerSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "Analysis/Task/PWGDQ: update dileptonEE.cxx", + "number": 5136, + "author": { + "login": "dsekihat" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGDQ/dileptonEE.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/tableMaker.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Prototype for background-on-signal filtering for ITS digitization (WIP)", + "number": 5137, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx" + } + }, + { + "node": { + "path": "dependencies/O2Dependencies.cmake" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T14:31:51Z", + "title": "PWGDQ utility classes moved from AnalysisCore to Analysis/Tasks/PWGDQ", + "number": 5138, + "author": { + "login": "iarsene" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Core/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Core/src/AnalysisCoreLinkDef.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/AnalysisCompositeCut.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/AnalysisCut.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/CutsLibrary.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/HistogramManager.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/HistogramsLibrary.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/VarManager.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/src/AnalysisCompositeCut.cxx" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/src/AnalysisCut.cxx" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/src/HistogramManager.cxx" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/src/PWGDQCoreLinkDef.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/src/VarManager.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/dileptonEE.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/dileptonMuMu.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/filterPP.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/tableMaker.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/tableMakerMuon_pp.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/tableMaker_pp.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/tableReader.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/reducedEventAnalysis.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-08T20:28:09Z", + "title": "[EMCAL-677] Propagate trigger bits from RDH to TriggerRecord", + "number": 5139, + "author": { + "login": "mfasDa" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx" + } + }, + { + "node": { + "path": "Detectors/EMCAL/simulation/src/RawWriter.cxx" + } + }, + { + "node": { + "path": "Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/EMCALDigitizerSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-08T19:22:37Z", + "title": "Fix: do not invoke FIT recpoints reader with --use-fit in raw data input mode", + "number": 5140, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-09T00:15:28Z", + "title": "Use GPUTPCO2InterfaceRefit for TPC-ITS matches refit + misc fixes.", + "number": 5141, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h" + } + }, + { + "node": { + "path": "DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h" + } + }, + { + "node": { + "path": "Detectors/Base/include/DetectorsBase/Propagator.h" + } + }, + { + "node": { + "path": "Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h" + } + }, + { + "node": { + "path": "Detectors/GlobalTracking/src/MatchTPCITS.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/reconstruction/src/GPUCATracking.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-09T08:45:20Z", + "title": "Several unrelated fixes in GPU code", + "number": 5142, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h" + } + }, + { + "node": { + "path": "DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUParam.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUSettingsList.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Standalone/CMakeLists.txt" + } + }, + { + "node": { + "path": "GPU/GPUTracking/dEdx/GPUdEdx.h" + } + }, + { + "node": { + "path": "dependencies/FindO2GPU.cmake" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-11T08:44:21Z", + "title": "Fixes for OMP and for dumping events for the standalone benchmark", + "number": 5143, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/reconstruction/src/GPUCATracking.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/CATrackerSpec.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUReconstructionCPU.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2Interface.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-10T14:53:05Z", + "title": "Fix: increment EMCAL TriggerRecord class version", + "number": 5144, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T07:48:46Z", + "title": "embedding_pileup_check: don't assume 'view' label format", + "number": 5145, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "macro/analyzeDigitLabels.C" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Change Tau0 max to 10 mm in Pythia8 configurations", + "number": 5146, + "author": { + "login": "preghenella" + }, + "files": { + "edges": [ + { + "node": { + "path": "Generators/share/egconfig/pythia8_hf.cfg" + } + }, + { + "node": { + "path": "Generators/share/egconfig/pythia8_hi.cfg" + } + }, + { + "node": { + "path": "Generators/share/egconfig/pythia8_inel.cfg" + } + }, + { + "node": { + "path": "run/SimExamples/Adaptive_Pythia8/pythia8_inel.cfg" + } + }, + { + "node": { + "path": "run/SimExamples/Custom_EventInfo/pythia8_inel.cfg" + } + }, + { + "node": { + "path": "run/SimExamples/HF_Embedding_Pythia8/pythia8_ccbar.template" + } + }, + { + "node": { + "path": "run/SimExamples/Jet_Embedding_Pythia8/pythia8_hard.cfg" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T10:53:10Z", + "title": "[FV0][O2-1849] Trigger inputs for CTP simulation", + "number": 5147, + "author": { + "login": "mslupeck" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/BCData.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h" + } + }, + { + "node": { + "path": "Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h" + } + }, + { + "node": { + "path": "Detectors/FIT/FV0/simulation/src/Digitizer.cxx" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/FV0DigitWriterSpec.h" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/FV0DigitizerSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "[ALICE 3] Add separate class to describe A3 beam pipe + 12-layer A3 layout", + "number": 5148, + "author": { + "login": "mconcas" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/Upgrades/ALICE3/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/Passive/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/Passive/include/A3DetectorsPassive/A3PassiveBase.h" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/Passive/include/A3DetectorsPassive/A3Pipe.h" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/Passive/src/A3PassiveBase.cxx" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/Passive/src/A3PassiveLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/Passive/src/A3Pipe.cxx" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h" + } + }, + { + "node": { + "path": "Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx" + } + }, + { + "node": { + "path": "macro/CMakeLists.txt" + } + }, + { + "node": { + "path": "macro/build_geometry.C" + } + }, + { + "node": { + "path": "run/CMakeLists.txt" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "DPL: workaround Ubuntu libc bug", + "number": 5149, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/include/Framework/Lifetime.h" + } + }, + { + "node": { + "path": "Framework/Core/src/CommonMessageBackends.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/DataProcessingDevice.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/DeviceSpecHelpers.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/runDataProcessing.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-16T18:37:32Z", + "title": "PWGHF: fix of the histogram registry in the Lc task", + "number": 5150, + "author": { + "login": "DelloStritto" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskD0.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskDPlus.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskLc.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T12:12:42Z", + "title": "Small fixes", + "number": 5151, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Utilities/Tools/jobutils.sh" + } + }, + { + "node": { + "path": "run/o2sim_parallel.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T12:13:05Z", + "title": "Revert \"Populate event header with information of the current Pythia8…", + "number": 5152, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Generators/src/GeneratorPythia8.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T14:57:36Z", + "title": "Avoid 2D params in SVertexer configurable params", + "number": 5153, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/Vertexing/include/DetectorsVertexing/SVertexerParams.h" + } + }, + { + "node": { + "path": "Detectors/Vertexing/src/SVertexer.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "DPL: publish aod-file-read-path metric", + "number": 5154, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/src/runDataProcessing.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T08:03:14Z", + "title": "Add multiplicity distribution task", + "number": 5155, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGCF/correlations.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGCF/filterCF.cxx" + } + }, + { + "node": { + "path": "Analysis/Tutorials/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tutorials/src/multiplicityEventTrackSelection.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T17:22:43Z", + "title": "o2-sim: Better signal propagation; small fix in jobutils", + "number": 5156, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Utilities/Tools/jobutils.sh" + } + }, + { + "node": { + "path": "run/o2sim_parallel.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T15:22:29Z", + "title": "Add CPV raw generation, reco, entropy encoding to full system test scripts", + "number": 5157, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "prodtests/full-system-test/dpl-workflow.sh" + } + }, + { + "node": { + "path": "prodtests/full-system-test/setenv.sh" + } + }, + { + "node": { + "path": "prodtests/full_system_test.sh" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T15:22:25Z", + "title": "use filters", + "number": 5158, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGUD/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGUD/upcAnalysis.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T12:10:30Z", + "title": "fix to allow iteratorAt on filtered tables", + "number": 5159, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/include/Framework/ASoA.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T19:08:58Z", + "title": "add alien SE", + "number": 5160, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T14:58:15Z", + "title": "DPL: do not mask errors when generating workflows", + "number": 5161, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "cmake/O2AddWorkflow.cmake" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "DPL: protect against non-existing AnalysisPlugin", + "number": 5162, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/src/WorkflowHelpers.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-12T20:05:09Z", + "title": "Bring back info treatment in MCEventHeader", + "number": 5163, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h" + } + }, + { + "node": { + "path": "Generators/src/GeneratorPythia8.cxx" + } + }, + { + "node": { + "path": "run/O2HitMerger.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T08:30:48Z", + "title": "Allow multiple test workflows with non-overlapping TF-ids", + "number": 5164, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TOF/calibration/testWorkflow/DataGeneratorSpec.h" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/testWorkflow/README.md" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/testWorkflow/data-generator-workflow.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T10:43:43Z", + "title": "DPL utils: allow customising output-proxy", + "number": 5165, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Utils/src/dpl-output-proxy.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "DPL examples: add monitoring usage to diamond example", + "number": 5166, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/TestWorkflows/src/o2DiamondWorkflow.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "Use o2_add_executable for DataSampling standalone workflow", + "number": 5167, + "author": { + "login": "knopers8" + }, + "files": { + "edges": [ + { + "node": { + "path": "Utilities/DataSampling/CMakeLists.txt" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T12:09:56Z", + "title": "DPL: increase max size of string metrics to 256 bytes", + "number": 5168, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/include/Framework/DeviceMetricsInfo.h" + } + }, + { + "node": { + "path": "Framework/Core/test/test_DeviceMetricsInfo.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T15:09:37Z", + "title": "jobutils: exit workflows on first task error", + "number": 5169, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Utilities/Tools/jobutils.sh" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T08:15:54Z", + "title": "Create a PR with new CHANGELOG entries", + "number": 5170, + "author": { + "login": "TimoWilken" + }, + "files": { + "edges": [ + { + "node": { + "path": ".github/workflows/reports.yml" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "[DCS][TOF] Make the data point random generator more reusable.", + "number": 5171, + "author": { + "login": "aphecetche" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/DCS/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/README.md" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/include/DCStestWorkflow/DCSRandomDataGeneratorSpec.h" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/DCSConsumerSpec.h" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/DCSDataGeneratorSpec.h" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/DCSDataProcessorSpec.h" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/DCSRandomDataGeneratorSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/dcs-data-client-workflow.cxx" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/dcs-data-workflow.cxx" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/dcs-proxy.cxx" + } + }, + { + "node": { + "path": "Detectors/DCS/testWorkflow/src/dcs-random-data-workflow.cxx" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/testWorkflow/README.md" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/testWorkflow/tof-dcs-sim-workflow.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T08:48:50Z", + "title": "Fix o2_add_dpl_workflow on Ubuntu and some other systems", + "number": 5172, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Utilities/DataSampling/CMakeLists.txt" + } + }, + { + "node": { + "path": "cmake/O2AddWorkflow.cmake" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "Update nuclei task", + "number": 5173, + "author": { + "login": "lbariogl" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx" + } + }, + { + "node": { + "path": "Generators/src/GeneratorPythia8.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T13:20:11Z", + "title": "Add histrogram register and track selection", + "number": 5174, + "author": { + "login": "lbariogl" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T12:18:33Z", + "title": "GPU: remove leftover debug messages", + "number": 5175, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/workflow/src/CATrackerSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T12:34:24Z", + "title": "GPU: Fix memory buffer scaling factor when memory allocation strategy is auto", + "number": 5176, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2Interface.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T20:54:43Z", + "title": "AliEn metrics", + "number": 5177, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx" + } + }, + { + "node": { + "path": "Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h" + } + }, + { + "node": { + "path": "Framework/Core/include/Framework/DataInputDirector.h" + } + }, + { + "node": { + "path": "Framework/Core/src/DataInputDirector.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/runDataProcessing.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T23:16:02Z", + "title": "Fixes in entropy compression memory management", + "number": 5178, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h" + } + }, + { + "node": { + "path": "Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h" + } + }, + { + "node": { + "path": "Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h" + } + }, + { + "node": { + "path": "Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h" + } + }, + { + "node": { + "path": "Detectors/TOF/reconstruction/src/CTFCoder.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/reconstruction/src/CTFCoder.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-13T20:50:20Z", + "title": "Work towards getting the TPC Tracking QA run standalone from a tracks ROOT file", + "number": 5179, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/qc/include/TPCQC/Tracking.h" + } + }, + { + "node": { + "path": "Detectors/TPC/qc/src/Tracking.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/CATrackerSpec.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2InterfaceQA.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Standalone/qa/GPUQA.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Standalone/qa/GPUQA.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T08:52:06Z", + "title": "Do not encode TDC errors to compressed output stream", + "number": 5180, + "author": { + "login": "preghenella" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TOF/compression/src/Compressor.cxx" + } + }, + { + "node": { + "path": "Detectors/TOF/workflow/src/CompressedDecodingTask.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T08:48:11Z", + "title": "Standalone TPC Tracking QA (independent from o2-tpc-reco-workflow)", + "number": 5181, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/workflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/include/TPCWorkflow/TrackReaderSpec.h" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/TrackReaderWorkflow.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Standalone/qa/GPUQA.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T08:31:06Z", + "title": "Add split tables per particle species in PID response", + "number": 5182, + "author": { + "login": "njacazio" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/DataModel/include/AnalysisDataModel/PID/PIDResponse.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/spectraTOF.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/spectraTOF_split.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/spectraTPC.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/spectraTPC_split.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/pidTOF_split.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/pidTPC.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/pidTPC_split.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T18:31:31Z", + "title": "Adding opening and run time metrics", + "number": 5183, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx" + } + }, + { + "node": { + "path": "Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h" + } + }, + { + "node": { + "path": "Framework/Core/include/Framework/DeviceMetricsInfo.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T13:23:32Z", + "title": "Update CommonUtilsLinkDef.h", + "number": 5184, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Common/Utils/src/CommonUtilsLinkDef.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-14T19:20:50Z", + "title": "Add TPC QC histograms for (limited) monitoring of cluster rejection on the fly while processing without MC information", + "number": 5185, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/workflow/src/CATrackerSpec.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUSettingsList.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Standalone/qa/GPUQA.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Standalone/qa/GPUQA.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-17T19:22:53Z", + "title": "GPU: Add option to tpc-reco-workflow to ship shared cluster map created during tracking", + "number": 5186, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/workflow/include/TPCWorkflow/CATrackerSpec.h" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/CATrackerSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/RecoWorkflow.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/tpc-reco-workflow.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUSettingsList.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Global/GPUChainTracking.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Global/GPUChainTracking.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2Interface.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2Interface.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Merger/GPUTPCGMMerger.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T20:28:42Z", + "title": "Fix: remap RootTreeWriter branches only once", + "number": 5187, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/RecoWorkflow.cxx" + } + }, + { + "node": { + "path": "Detectors/TRD/workflow/src/TRDDigitWriterSpec.cxx" + } + }, + { + "node": { + "path": "Framework/Utils/include/DPLUtils/RootTreeWriter.h" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/FDDDigitWriterSpec.h" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T10:11:13Z", + "title": "The following changelog has been automatically generated.", + "number": 5188, + "author": { + "login": "github-actions" + }, + "files": { + "edges": [ + { + "node": { + "path": "CHANGELOG.md" + } + }, + { + "node": { + "path": "doc/data/2021-01-o2_prs.json" + } + }, + { + "node": { + "path": "doc/data/2021-01-o2_releases.json" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T12:36:18Z", + "title": "DPL: add `--forwarding-policy none` to help message", + "number": 5189, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/src/WorkflowCustomizationHelpers.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "fix CCDB publication time stamp, add draw from CCDB", + "number": 5190, + "author": { + "login": "wiechula" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPedestalSpec.h" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "The following changelog has been automatically generated.", + "number": 5191, + "author": { + "login": "github-actions" + }, + "files": { + "edges": [ + { + "node": { + "path": "CHANGELOG.md" + } + }, + { + "node": { + "path": "doc/data/2021-01-o2_prs.json" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T17:56:16Z", + "title": "FT0 LUT prepared as Singleton for DigitBlockFT0 usage", + "number": 5192, + "author": { + "login": "afurs" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h" + } + }, + { + "node": { + "path": "Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h" + } + }, + { + "node": { + "path": "Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "TOFFEElight processing", + "number": 5193, + "author": { + "login": "chiarazampolli" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TOF/calibration/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/include/TOFCalibration/TOFFEElightConfig.h" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/include/TOFCalibration/TOFFEElightReader.h" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/src/TOFFEElightConfig.cxx" + } + }, + { + "node": { + "path": "Detectors/TOF/calibration/src/TOFFEElightReader.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T15:45:47Z", + "title": "GPU: Put reasonable max time bin for triggered TPC data", + "number": 5194, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "GPU/GPUTracking/Global/GPUChainTracking.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/TPCClusterFinder/clusterFinderDefs.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T23:22:58Z", + "title": "fix for 2 readers and 1 input file", + "number": 5195, + "author": { + "login": "jgrosseo" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T14:17:20Z", + "title": "small fix in jobutils", + "number": 5196, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "Utilities/Tools/jobutils.sh" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Fixed memory leak in DataContainer3D", + "number": 5197, + "author": { + "login": "matthias-kleiner" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T08:12:30Z", + "title": "small improvement in fake track rejection", + "number": 5198, + "author": { + "login": "pillot" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/MUON/MCH/Tracking/README.md" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Tracking/include/MCHTracking/Track.h" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Tracking/src/Track.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Tracking/src/TrackFinder.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Tracking/src/TrackFinderOriginal.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-15T23:14:55Z", + "title": "(O2-1203) [CTF] use merging/splitting iterators during CTF ecoding/decoding for TPC ", + "number": 5199, + "author": { + "login": "MichaelLettrich" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h" + } + }, + { + "node": { + "path": "Detectors/Base/include/DetectorsBase/CTFCoderBase.h" + } + }, + { + "node": { + "path": "Detectors/CTF/test/test_ctf_io_tpc.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h" + } + }, + { + "node": { + "path": "Detectors/TPC/reconstruction/src/CTFCoder.cxx" + } + }, + { + "node": { + "path": "Utilities/rANS/benchmarks/bench_ransCombinedIterator.cxx" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/Decoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/DedupDecoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/DedupEncoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/Encoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/LiteralDecoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/LiteralEncoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/internal/Decoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/internal/Encoder.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/internal/helper.h" + } + }, + { + "node": { + "path": "Utilities/rANS/include/rANS/utils/CombinedIterator.h" + } + }, + { + "node": { + "path": "Utilities/rANS/test/test_ransCombinedIterator.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-16T09:08:23Z", + "title": "PWGHF changing default preselection values for hyperloop", + "number": 5200, + "author": { + "login": "nzardosh" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Core/include/AnalysisCore/HFConfigurables.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFTrackIndexSkimsCreator.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "PWGHF, RecoDecay: Add gen. level pT of matched D0 candidates.", + "number": 5201, + "author": { + "login": "vkucera" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Core/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Core/include/AnalysisCore/RecoDecay.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFTrackIndexSkimsCreator.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskD0.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-16T16:06:35Z", + "title": "[EMCAL-630] Store result of the raw fit as energy", + "number": 5202, + "author": { + "login": "mfasDa" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-17T19:28:52Z", + "title": "DPL: move driver communication to a separate Service", + "number": 5203, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Core/CMakeLists.txt" + } + }, + { + "node": { + "path": "Framework/Core/include/Framework/CommonServices.h" + } + }, + { + "node": { + "path": "Framework/Core/include/Framework/ControlService.h" + } + }, + { + "node": { + "path": "Framework/Core/include/Framework/DriverClient.h" + } + }, + { + "node": { + "path": "Framework/Core/include/Framework/TextControlService.h" + } + }, + { + "node": { + "path": "Framework/Core/src/CommonServices.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/ControlService.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/TextDriverClient.cxx" + } + }, + { + "node": { + "path": "Framework/Core/src/TextDriverClient.h" + } + }, + { + "node": { + "path": "Framework/Core/src/runDataProcessing.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T12:37:28Z", + "title": "PWGHF: Use pre-selections for Dplus", + "number": 5204, + "author": { + "login": "fcatalan92" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskDPlus.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T08:09:01Z", + "title": "Change task names, move initializers", + "number": 5205, + "author": { + "login": "ddobrigk" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGLF/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/cascadeanalysis.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/cascadebuilder.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/cascadefinder.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/lambdakzeroanalysis.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/lambdakzerobuilder.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/lambdakzerofinder.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T08:55:29Z", + "title": "TableMaker task updated to use configurables for system type, event and track selections", + "number": 5206, + "author": { + "login": "iarsene" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/HistogramsLibrary.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/include/PWGDQCore/VarManager.h" + } + }, + { + "node": { + "path": "Analysis/PWGDQ/src/VarManager.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGDQ/tableMaker.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "[O2-1805] add BUILD_ANALYSIS cmake option (default ON)", + "number": 5207, + "author": { + "login": "aphecetche" + }, + "files": { + "edges": [ + { + "node": { + "path": "CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/CMakeLists.txt" + } + }, + { + "node": { + "path": "cmake/O2DefineOptions.cmake" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T01:47:12Z", + "title": "TPC-ITS matching will use tpc-reco-workflow or on-the-fly created shared cl.map ", + "number": 5208, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h" + } + }, + { + "node": { + "path": "Detectors/GlobalTracking/src/MatchTPCITS.cxx" + } + }, + { + "node": { + "path": "Detectors/GlobalTrackingWorkflow/src/MatchTPCITSWorkflow.cxx" + } + }, + { + "node": { + "path": "Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/include/TPCWorkflow/ClusterSharingMapSpec.h" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/ClusterSharingMapSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "small bugfix in nuclex code", + "number": 5209, + "author": { + "login": "akalweit" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T07:33:56Z", + "title": "small bug (re)fix, tpc momentum for dedx instead pvtx", + "number": 5210, + "author": { + "login": "akalweit" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/PWGLF/NucleiSpectraTask.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T07:22:47Z", + "title": "o2-sim: Use vmc->ProcessEvent for Geant4", + "number": 5211, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "run/O2SimDevice.h" + } + } + ] + } + } + }, + { + "node": { + "state": "CLOSED", + "mergedAt": null, + "title": "[CMake][Generators] doBuildSimulation variable was intended to be local only", + "number": 5212, + "author": { + "login": "aphecetche" + }, + "files": { + "edges": [ + { + "node": { + "path": "Generators/CMakeLists.txt" + } + }, + { + "node": { + "path": "dependencies/O2SimulationDependencies.cmake" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T13:12:24Z", + "title": "Limit number of OMP TPC workers for small events", + "number": 5213, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "GPU/GPUTracking/Base/GPUReconstruction.cxx" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUReconstruction.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUReconstructionCPU.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Base/GPUSettingsList.h" + } + }, + { + "node": { + "path": "GPU/GPUTracking/Global/GPUChainTracking.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Add tables for the compressed Nsigma values", + "number": 5214, + "author": { + "login": "njacazio" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/DataModel/include/AnalysisDataModel/PID/PIDResponse.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/spectraTOF_tiny.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGLF/spectraTPC_tiny.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/pidTOF_tiny.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/pidTPC_tiny.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-18T09:32:17Z", + "title": "Only run reports.yml in AliceO2Group/AliceO2", + "number": 5215, + "author": { + "login": "TimoWilken" + }, + "files": { + "edges": [ + { + "node": { + "path": ".github/workflows/reports.yml" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T08:45:42Z", + "title": "TPC Workflow: Move cluster / digit reading from tpc tracking spec into a helper", + "number": 5216, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/TPC/src/WorkflowHelper.cxx" + } + }, + { + "node": { + "path": "Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/CATrackerSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/ClusterSharingMapSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/RecoWorkflow.cxx" + } + }, + { + "node": { + "path": "Detectors/TPC/workflow/src/ZSSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Setting up Skimming Tasks - spectraTPC", + "number": 5217, + "author": { + "login": "nzardosh" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Tasks/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/SkimmingTutorials/CMakeLists.txt" + } + }, + { + "node": { + "path": "Analysis/Tasks/SkimmingTutorials/DataModel/LFDerived.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/SkimmingTutorials/LFDerived.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/SkimmingTutorials/spectraTPCAnalyser.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/SkimmingTutorials/spectraTPCProvider.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/SkimmingTutorials/spectraTPCReference.cxx" + } + }, + { + "node": { + "path": "Common/MathUtils/include/MathUtils/Utils.h" + } + }, + { + "node": { + "path": "Common/MathUtils/include/MathUtils/detail/TypeTruncation.h" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "PWGHF: Adding flag selection in the hadron tasks", + "number": 5218, + "author": { + "login": "DelloStritto" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/DataModel/include/AnalysisDataModel/HFSecondaryVertex.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFD0CandidateSelector.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFLcCandidateSelector.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskD0.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/taskLc.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "First version of ALICE3toAO2D macro", + "number": 5219, + "author": { + "login": "ddobrigk" + }, + "files": { + "edges": [ + { + "node": { + "path": "macro/ALICE3/ALICE3toAO2D.C" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "SpaceCharge: added function to set the density and potential directly", + "number": 5220, + "author": { + "login": "matthias-kleiner" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T15:36:52Z", + "title": "refactor TRD digit class", + "number": 5221, + "author": { + "login": "bazinski" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/TRD/base/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/TRD/base/include/TRDBase/Digit.h" + } + }, + { + "node": { + "path": "Detectors/TRD/base/include/TRDBase/FeeParam.h" + } + }, + { + "node": { + "path": "Detectors/TRD/base/src/Digit.cxx" + } + }, + { + "node": { + "path": "Detectors/TRD/base/src/FeeParam.cxx" + } + }, + { + "node": { + "path": "Detectors/TRD/base/src/TRDBaseLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/TRD/simulation/src/Digitizer.cxx" + } + }, + { + "node": { + "path": "Detectors/TRD/workflow/src/TRDTrapSimulatorSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "introduce MCH ROFRecord.h + modify tracking workflows to process several ROF per TF", + "number": 5222, + "author": { + "login": "pillot" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/MUON/MCH/CMakeLists.txt" + } + }, + { + "node": { + "path": "DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/ROFRecord.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/MUON/MCH/src/DataFormatsMCHLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/README.md" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/ClusterSamplerSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/TrackAtVertexSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/TrackFinderOriginalSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/TrackFinderSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/TrackFitterSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/TrackSamplerSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/TrackSinkSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/MUON/MCH/Workflow/src/VertexSamplerSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T07:51:53Z", + "title": "PWGHF : syncing decays in the preselection with enums used for MC", + "number": 5223, + "author": { + "login": "nzardosh" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Core/include/AnalysisCore/HFConfigurables.h" + } + }, + { + "node": { + "path": "Analysis/DataModel/include/AnalysisDataModel/HFSecondaryVertex.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFTrackIndexSkimsCreator.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "PWGHF: Shift decay type values to allow usage in arrays and bitmaps.", + "number": 5224, + "author": { + "login": "vkucera" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/DataModel/include/AnalysisDataModel/HFSecondaryVertex.h" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFCandidateCreator2Prong.cxx" + } + }, + { + "node": { + "path": "Analysis/Tasks/PWGHF/HFCandidateCreator3Prong.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T09:18:17Z", + "title": "[EMCAL-630] Fix assignment of energy and time", + "number": 5225, + "author": { + "login": "mfasDa" + }, + "files": { + "edges": [ + { + "node": { + "path": "Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T14:04:52Z", + "title": "Bugfix: WorkflowHelper.h was not installled", + "number": 5226, + "author": { + "login": "davidrohr" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/TPC/CMakeLists.txt" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T16:07:16Z", + "title": "Check for selective primary transport also in case of parallel sim", + "number": 5227, + "author": { + "login": "preghenella" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/simulation/include/SimulationDataFormat/MCTrack.h" + } + }, + { + "node": { + "path": "DataFormats/simulation/include/SimulationDataFormat/Stack.h" + } + }, + { + "node": { + "path": "DataFormats/simulation/src/Stack.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T13:19:49Z", + "title": "o2-sim: Make configfile prefixed with correct name", + "number": 5228, + "author": { + "login": "sawenzel" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/NameConf.h" + } + }, + { + "node": { + "path": "doc/DetectorSimulation.md" + } + }, + { + "node": { + "path": "macro/o2sim.C" + } + } + ] + } + } + }, + { + "node": { + "state": "MERGED", + "mergedAt": "2021-01-19T18:31:34Z", + "title": "raw-parser logs DataHeader and DataProcessingHeader info", + "number": 5229, + "author": { + "login": "shahor02" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Utils/include/DPLUtils/DPLRawParser.h" + } + }, + { + "node": { + "path": "Framework/Utils/src/raw-parser.cxx" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Pileup and calibration DA for PHOS and CPV", + "number": 5230, + "author": { + "login": "peressounko" + }, + "files": { + "edges": [ + { + "node": { + "path": "DataFormats/Detectors/PHOS/include/DataFormatsPHOS/Cluster.h" + } + }, + { + "node": { + "path": "DataFormats/Detectors/PHOS/src/Cluster.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/base/include/CPVBase/CPVSimParams.h" + } + }, + { + "node": { + "path": "Detectors/CPV/base/include/CPVBase/Geometry.h" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/include/CPVCalibWorkflow/CPVBadMapCalibDevice.h" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/include/CPVCalibWorkflow/CPVGainCalibDevice.h" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/include/CPVCalibWorkflow/CPVPedestalCalibDevice.h" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/CPVCalibWorkflow/src/cpv-calib-workflow.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/include/CPVCalib/CalibParams.h" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/include/CPVCalib/Pedestals.h" + } + }, + { + "node": { + "path": "Detectors/CPV/calib/src/Pedestals.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/simulation/include/CPVSimulation/Digitizer.h" + } + }, + { + "node": { + "path": "Detectors/CPV/simulation/include/CPVSimulation/RawWriter.h" + } + }, + { + "node": { + "path": "Detectors/CPV/simulation/src/Detector.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/simulation/src/Digitizer.cxx" + } + }, + { + "node": { + "path": "Detectors/CPV/workflow/src/RecoWorkflow.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/base/files/alignment.root" + } + }, + { + "node": { + "path": "Detectors/PHOS/base/include/PHOSBase/Geometry.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/base/include/PHOSBase/PHOSSimParams.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/base/src/Geometry.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/CMakeLists.txt" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/include/PHOSCalibWorkflow/PHOSCalibCollector.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/include/PHOSCalibWorkflow/PHOSCalibCollectorSpec.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/include/PHOSCalibWorkflow/PHOSHGLGRatioCalibDevice.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/include/PHOSCalibWorkflow/PHOSPedestalCalibDevice.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/src/PHOSCalibCollector.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/src/PHOSCalibCollectorSpec.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/src/PHOSCalibWorkflowLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/src/PHOSHGLGRatioCalibDevice.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/src/PHOSPedestalCalibDevice.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/PHOSCalibWorkflow/src/phos-calib-workflow.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/include/PHOSCalib/BadChannelMap.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/include/PHOSCalib/CalibParams.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/include/PHOSCalib/Pedestals.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/src/BadChannelMap.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/src/CalibParams.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/src/PHOSCalibLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/calib/src/Pedestals.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/reconstruction/include/PHOSReconstruction/CaloRawFitter.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/reconstruction/include/PHOSReconstruction/FullCluster.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/reconstruction/src/FullCluster.cxx" + } + }, + { + "node": { + "path": "Detectors/PHOS/reconstruction/src/PHOSReconstructionLinkDef.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/simulation/include/PHOSSimulation/Digitizer.h" + } + }, + { + "node": { + "path": "Detectors/PHOS/simulation/src/Digitizer.cxx" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/CPVDigitizerSpec.cxx" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/CPVDigitizerSpec.h" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.cxx" + } + }, + { + "node": { + "path": "Steer/DigitizerWorkflow/src/PHOSDigitizerSpec.h" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "Make sure benchmark::benchmark stays optional (O2-1973)", + "number": 5231, + "author": { + "login": "ktf" + }, + "files": { + "edges": [ + { + "node": { + "path": "Framework/Utils/CMakeLists.txt" + } + }, + { + "node": { + "path": "Utilities/Mergers/CMakeLists.txt" + } + }, + { + "node": { + "path": "Utilities/rANS/CMakeLists.txt" + } + } + ] + } + } + }, + { + "node": { + "state": "OPEN", + "mergedAt": null, + "title": "PWGHF : further restricitng HF preselection for hyperloop testing", + "number": 5232, + "author": { + "login": "nzardosh" + }, + "files": { + "edges": [ + { + "node": { + "path": "Analysis/Core/include/AnalysisCore/HFConfigurables.h" + } + } + ] + } + } + } + ] + } + } +} diff --git a/doc/data/2021-01-o2_releases.json b/doc/data/2021-01-o2_releases.json new file mode 100644 index 0000000000000..fe7288db28373 --- /dev/null +++ b/doc/data/2021-01-o2_releases.json @@ -0,0 +1,38 @@ +{ + "repository": { + "releases": { + "edges": [ + { + "node": { + "tagName": "O2-1.0.0", + "publishedAt": "2018-11-21T13:41:46Z" + } + }, + { + "node": { + "tagName": "v1.2.0", + "publishedAt": "2020-02-25T09:10:00Z" + } + }, + { + "node": { + "tagName": "v1.3.0", + "publishedAt": "2020-09-22T07:21:04Z" + } + }, + { + "node": { + "tagName": "v20.49", + "publishedAt": "2020-12-11T16:12:56Z" + } + }, + { + "node": { + "tagName": "v21.01", + "publishedAt": "2021-01-05T16:47:05Z" + } + } + ] + } + } +} diff --git a/doc/doxyfile-with-graphs.in b/doc/doxyfile-with-graphs.in new file mode 100644 index 0000000000000..d60d01d5bcb59 --- /dev/null +++ b/doc/doxyfile-with-graphs.in @@ -0,0 +1,313 @@ +# Doxyfile 1.8.8 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = @PROJECT_NAME@ +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = "@CMAKE_SOURCE_DIR@/doc/images/o2_logo.png" +OUTPUT_DIRECTORY = "@DOC_OUTPUT_DIR@" +CREATE_SUBDIRS = YES +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 0 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = YES +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = "@CMAKE_SOURCE_DIR@" +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.cxx *.h *.inl *.md +RECURSIVE = YES +EXCLUDE = .git/ \ + build/ \ + build-dir/ \ + html-docs/ \ + doxygen cmake config gconfig geometry input parameters .svn vis +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = G__* ClassImp build_* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = @CMAKE_SOURCE_DIR@/doc/scripts/filter_for_doxygen.sh +FILTER_PATTERNS = *.md +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/README.md +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = .git +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = YES +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +DIA_PATH = +HIDE_UNDOC_RELATIONS = NO +HAVE_DOT = YES +DOT_NUM_THREADS = 1 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +UML_LIMIT_NUM_FIELDS = 50 +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = svg +INTERACTIVE_SVG = YES +DOT_PATH = "@DOT_PATH@" +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +DOT_GRAPH_MAX_NODES = 100 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = YES +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/doc/doxyfile.in b/doc/doxyfile.in index d60d01d5bcb59..08b7d37770b20 100644 --- a/doc/doxyfile.in +++ b/doc/doxyfile.in @@ -278,10 +278,10 @@ EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES +CLASS_DIAGRAMS = NO DIA_PATH = HIDE_UNDOC_RELATIONS = NO -HAVE_DOT = YES +HAVE_DOT = NO DOT_NUM_THREADS = 1 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 diff --git a/macro/CMakeLists.txt b/macro/CMakeLists.txt index d8530594637f2..f159d724cbb4e 100644 --- a/macro/CMakeLists.txt +++ b/macro/CMakeLists.txt @@ -15,7 +15,7 @@ install(FILES CheckDigits_mft.C SetIncludePath.C analyzeHits.C - duplicateHits.C + duplicateHits.C migrateSimFiles.C analyzeDigitLabels.C analyzeOriginHits.C @@ -33,6 +33,7 @@ install(FILES CheckDigits_mft.C readITSDigits.C rootlogon.C runCATrackingClusterNative.C + runTPCRefit.C run_CRUDataSkimming_its.C run_calib_tof.C run_clus_itsSA.C @@ -52,6 +53,7 @@ install(FILES CheckDigits_mft.C run_rawdecoding_mft.C run_trac_ca_its.C run_trac_its.C + run_trac_alice3.C CreateBCPattern.C DESTINATION share/macro/) @@ -113,6 +115,7 @@ if (ENABLE_UPGRADES) O2::TPCSimulation O2::ITSSimulation O2::ITS3Simulation + O2::TRKSimulation O2::MFTSimulation O2::MCHSimulation O2::MIDSimulation @@ -182,11 +185,11 @@ o2_add_test_root_macro( O2::MCHSimulation O2::MIDSimulation O2::ZDCSimulation - O2::CPVBase + O2::CPVBase O2::CPVSimulation O2::ZDCSimulation) -if(Geant4_FOUND) +if(Geant4_FOUND AND BUILD_SIMULATION) o2_add_test_root_macro(o2sim.C PUBLIC_LINK_LIBRARIES O2::Generators O2::DetectorsPassive @@ -234,6 +237,13 @@ o2_add_test_root_macro(runCATrackingClusterNative.C O2::ReconstructionDataFormats O2::SimulationDataFormat O2::TPCReconstruction +LABELS tpc) +# FIXME: move to subsystem dir +o2_add_test_root_macro(runTPCRefit.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC + O2::ReconstructionDataFormats + O2::SimulationDataFormat + O2::TPCReconstruction LABELS tpc) # FIXME: move to subsystem dir @@ -260,7 +270,7 @@ o2_add_test_root_macro(run_clus_itsSA.C # FIXME: move to subsystem dir o2_add_test_root_macro(run_clus_tof.C - PUBLIC_LINK_LIBRARIES O2::TOFReconstruction O2::Framework O2::TOFBase + PUBLIC_LINK_LIBRARIES O2::TOFReconstruction O2::Framework O2::TOFBase O2::SimulationDataFormat O2::TOFCalibration O2::DataFormatsTOF LABELS tof) @@ -371,6 +381,11 @@ o2_add_test_root_macro(run_trac_its.C O2::SimulationDataFormat LABELS its) +# FIXME: move to subsystem dir +o2_add_test_root_macro(run_trac_alice3.C + PUBLIC_LINK_LIBRARIES O2::GPUTracking O2::ITSMFTSimulation + LABELS its COMPILE_ONLY) + # # NOTE: commented out until unit testing reenabled FIXME : re-enable or delete ? # diff --git a/macro/CheckDigits_mft.C b/macro/CheckDigits_mft.C index 3b1d2aead1582..379c0603cfb91 100644 --- a/macro/CheckDigits_mft.C +++ b/macro/CheckDigits_mft.C @@ -43,7 +43,7 @@ void CheckDigits_mft(Int_t nEvents = 1, Int_t nMuons = 10, TString mcEngine = "T gFile->Get("FairGeoParSet"); auto* gman = o2::mft::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::L2G)); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); SegmentationAlpide seg; @@ -83,7 +83,7 @@ void CheckDigits_mft(Int_t nEvents = 1, Int_t nMuons = 10, TString mcEngine = "T Int_t ix = d->getRow(), iz = d->getColumn(); Float_t x = 0.f, z = 0.f; seg.detectorToLocal(ix, iz, x, z); - const Point3D<Float_t> locD(x, 0., z); + const o2::math_utils::Point3D<Float_t> locD(x, 0., z); const auto& labs = labels->getLabels(nd); diff --git a/macro/analyzeDigitLabels.C b/macro/analyzeDigitLabels.C index a476c620bbb44..fddee682f9fb1 100644 --- a/macro/analyzeDigitLabels.C +++ b/macro/analyzeDigitLabels.C @@ -4,10 +4,12 @@ #include "TString.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" #include "DataFormatsPHOS/MCLabel.h" #include "DataFormatsFT0/MCLabel.h" #include "DataFormatsFDD/MCLabel.h" -#include "FV0Simulation/MCLabel.h" +#include "DataFormatsFV0/MCLabel.h" #include "ZDCSimulation/MCLabel.h" #include "MIDSimulation/MCLabel.h" #include "TRDBase/MCLabel.h" @@ -78,15 +80,30 @@ void analyse(TTree* tr, const char* brname, Accumulator& prop) if (!br) { return; } + auto classname = br->GetClassName(); auto entries = br->GetEntries(); - o2::dataformats::MCTruthContainer<LabelType>* labels = nullptr; - br->SetAddress(&labels); + if (strcmp("IOMCTruthContainerView", classname) == 0) { + o2::dataformats::IOMCTruthContainerView* io2 = nullptr; + br->SetAddress(&io2); - for (int i = 0; i < entries; ++i) { - br->GetEntry(i); + for (int i = 0; i < entries; ++i) { + br->GetEntry(i); + o2::dataformats::ConstMCTruthContainer<LabelType> labels; + io2->copyandflatten(labels); - for (int i = 0; i < (int)labels->getIndexedSize(); ++i) { - prop.addLabels(labels->getLabels(i)); + for (int i = 0; i < (int)labels.getIndexedSize(); ++i) { + prop.addLabels(labels.getLabels(i)); + } + } + } else { + // standard MC truth container + o2::dataformats::MCTruthContainer<LabelType>* labels = nullptr; + br->SetAddress(&labels); + for (int i = 0; i < entries; ++i) { + br->GetEntry(i); + for (int i = 0; i < (int)labels->getIndexedSize(); ++i) { + prop.addLabels(labels->getLabels(i)); + } } } }; diff --git a/macro/build_geometry.C b/macro/build_geometry.C index ef29710ce1283..aaef25e3168a3 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -47,6 +47,7 @@ #ifdef ENABLE_UPGRADES #include <ITS3Simulation/Detector.h> +#include <TRKSimulation/Detector.h> #endif void finalize_geometry(FairRunSim* run); @@ -87,27 +88,7 @@ void build_geometry(FairRunSim* run = nullptr) run->SetMaterials("media.geo"); // Materials // we need a field to properly init the media - int fld = confref.getConfigData().mField, fldAbs = std::abs(fld); - float fldCoeff; - o2::field::MagFieldParam::BMap_t fldType; - switch (fldAbs) { - case 5: - fldType = o2::field::MagFieldParam::k5kG; - fldCoeff = fld > 0 ? 1. : -1; - break; - case 0: - fldType = o2::field::MagFieldParam::k5kG; - fldCoeff = 0; - break; - case 2: - fldType = o2::field::MagFieldParam::k2kG; - fldCoeff = fld > 0 ? 1. : -1; - break; - default: - LOG(FATAL) << "Field option " << fld << " is not supported, use +-2, +-5 or 0"; - }; - - auto field = new o2::field::MagneticField("Maps", "Maps", fldCoeff, fldCoeff, fldType); + auto field = o2::field::MagneticField::createNominalField(confref.getConfigData().mField); run->SetField(field); // Create geometry @@ -146,7 +127,11 @@ void build_geometry(FairRunSim* run = nullptr) // beam pipe if (isActivated("PIPE")) { #ifdef ENABLE_UPGRADES - run->AddModule(new o2::passive::Pipe("PIPE", "Beam pipe", 1.6f, 0.05)); + if (isActivated("IT3") || isActivated("TRK")) { + run->AddModule(new o2::passive::Pipe("PIPE", "Beam pipe", 1.6f, 0.05)); + } else { + run->AddModule(new o2::passive::Pipe("PIPE", "Beam pipe")); + } #else run->AddModule(new o2::passive::Pipe("PIPE", "Beam pipe")); #endif @@ -191,9 +176,15 @@ void build_geometry(FairRunSim* run = nullptr) #ifdef ENABLE_UPGRADES if (isActivated("IT3")) { // ITS3 - auto its3 = new o2::its3::Detector(kTRUE); + auto its3 = new o2::its3::Detector(true); run->AddModule(its3); } + + if (isActivated("TRK")) { + // ALICE 3 TRK + auto trk = new o2::trk::Detector(true); + run->AddModule(trk); + } #endif if (isActivated("ITS")) { diff --git a/macro/checkTOFMatching.C b/macro/checkTOFMatching.C index 0678d69b9f544..80f07ebf52b7a 100644 --- a/macro/checkTOFMatching.C +++ b/macro/checkTOFMatching.C @@ -40,7 +40,7 @@ void checkTOFMatching(bool batchMode = true) TTree* tpcTree = (TTree*)ftracksTPC->Get("events"); std::vector<o2::tpc::TrackTPC>* mTPCTracksArrayInp = new std::vector<o2::tpc::TrackTPC>; tpcTree->SetBranchAddress("Tracks", &mTPCTracksArrayInp); - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mcTPC = new o2::dataformats::MCTruthContainer<o2::MCCompLabel>(); + std::vector<o2::MCCompLabel>* mcTPC = new std::vector<o2::MCCompLabel>(); tpcTree->SetBranchAddress("TracksMCTruth", &mcTPC); // getting the ITS tracks @@ -48,7 +48,7 @@ void checkTOFMatching(bool batchMode = true) TTree* itsTree = (TTree*)ftracksITS->Get("o2sim"); std::vector<o2::its::TrackITS>* mITSTracksArrayInp = new std::vector<o2::its::TrackITS>; itsTree->SetBranchAddress("ITSTrack", &mITSTracksArrayInp); - o2::dataformats::MCTruthContainer<o2::MCCompLabel>* mcITS = new o2::dataformats::MCTruthContainer<o2::MCCompLabel>(); + std::vector<o2::MCCompLabel>* mcITS = new std::vector<o2::MCCompLabel>(); itsTree->SetBranchAddress("ITSTrackMCTruth", &mcITS); // getting the TOF clusters @@ -274,23 +274,19 @@ void checkTOFMatching(bool batchMode = true) Printf("matched ITStrack index = %d", evIdxITS); #endif // getting the TPC labels - const auto& labelsTPC = mcTPC->getLabels(evIdxTPC); + const auto& labelsTPC = (*mcTPC)[evIdxTPC]; #ifdef DEBUG - for (uint ilabel = 0; ilabel < labelsTPC.size(); ilabel++) { - Printf("TPC label %d: trackID = %d, eventID = %d, sourceID = %d", ilabel, labelsTPC[ilabel].getTrackID(), labelsTPC[ilabel].getEventID(), labelsTPC[ilabel].getSourceID()); - } + Printf("TPC label: trackID = %d, eventID = %d, sourceID = %d", labelsTPC.getTrackID(), labelsTPC.getEventID(), labelsTPC.getSourceID()); #endif // getting the ITS labels - const auto& labelsITS = mcITS->getLabels(evIdxITS); + const auto& labelsITS = (*mcITS)[evIdxITS]; #ifdef DEBUG - for (uint ilabel = 0; ilabel < labelsITS.size(); ilabel++) { - Printf("ITS label %d: trackID = %d, eventID = %d, sourceID = %d", ilabel, labelsITS[ilabel].getTrackID(), labelsITS[ilabel].getEventID(), labelsITS[ilabel].getSourceID()); - } + Printf("ITS label: trackID = %d, eventID = %d, sourceID = %d", ilabel, labelsITS.getTrackID(), labelsITS.getEventID(), labelsITS.getSourceID()); #endif bool bMatched = kFALSE; for (uint ilabel = 0; ilabel < labelsTOF.size(); ilabel++) { - if ((abs(labelsTPC[0].getTrackID()) == labelsTOF[ilabel].getTrackID() && labelsTPC[0].getEventID() == labelsTOF[ilabel].getEventID() && labelsTPC[0].getSourceID() == labelsTOF[ilabel].getSourceID()) || (labelsITS[0].getTrackID() == labelsTOF[ilabel].getTrackID() && labelsITS[0].getEventID() == labelsTOF[ilabel].getEventID() && labelsITS[0].getSourceID() == labelsTOF[ilabel].getSourceID())) { + if ((abs(labelsTPC.getTrackID()) == labelsTOF[ilabel].getTrackID() && labelsTPC.getEventID() == labelsTOF[ilabel].getEventID() && labelsTPC.getSourceID() == labelsTOF[ilabel].getSourceID()) || (labelsITS.getTrackID() == labelsTOF[ilabel].getTrackID() && labelsITS.getEventID() == labelsTOF[ilabel].getEventID() && labelsITS.getSourceID() == labelsTOF[ilabel].getSourceID())) { nGoodMatches++; bMatched = kTRUE; break; @@ -352,23 +348,19 @@ void checkTOFMatching(bool batchMode = true) o2::dataformats::TrackTPCITS trackITSTPC = mTracksArrayInp->at(i); const auto evIdxTPCcheck = trackITSTPC.getRefTPC(); const auto evIdxITScheck = trackITSTPC.getRefITS(); - const auto& labelsTPCcheck = mcTPC->getLabels(evIdxTPCcheck); - for (uint ilabel = 0; ilabel < labelsTPCcheck.size(); ilabel++) { - if (abs(labelsTPCcheck[ilabel].getTrackID()) == trackIdTOF && labelsTPCcheck[ilabel].getEventID() == eventIdTOF && labelsTPCcheck[ilabel].getSourceID() == sourceIdTOF) { + const auto& labelsTPCcheck = (*mcTPC)[evIdxTPCcheck]; + if (abs(labelsTPCcheck.getTrackID()) == trackIdTOF && labelsTPCcheck.getEventID() == eventIdTOF && labelsTPCcheck.getSourceID() == sourceIdTOF) { #ifdef DEBUG - Printf("The TPC track that should have been matched to TOF is number %d", i); + Printf("The TPC track that should have been matched to TOF is number %d", i); #endif - TPCfound = true; - } + TPCfound = true; } - const auto& labelsITScheck = mcITS->getLabels(evIdxITScheck); - for (uint ilabel = 0; ilabel < labelsITScheck.size(); ilabel++) { - if (labelsITScheck[ilabel].getTrackID() == trackIdTOF && labelsITScheck[ilabel].getEventID() == eventIdTOF && labelsITScheck[ilabel].getSourceID() == sourceIdTOF) { + const auto& labelsITScheck = (*mcITS)[evIdxITScheck]; + if (labelsITScheck.getTrackID() == trackIdTOF && labelsITScheck.getEventID() == eventIdTOF && labelsITScheck.getSourceID() == sourceIdTOF) { #ifdef DEBUG - Printf("The ITS track that should have been matched to TOF is number %d", i); + Printf("The ITS track that should have been matched to TOF is number %d", i); #endif - ITSfound = true; - } + ITSfound = true; } } #ifdef DEBUG diff --git a/macro/compareTOFDigits.C b/macro/compareTOFDigits.C index f944a3d54a6a6..b9b450f2f86a8 100644 --- a/macro/compareTOFDigits.C +++ b/macro/compareTOFDigits.C @@ -47,10 +47,28 @@ bool compareTOFDigits(std::string inpName1 = "tofdigitsOr.root", std::string inp int nro1 = row1.size(); int nro2 = row2.size(); + for (int ii = 1; ii < t2->GetEntries(); ii++) { + t2->GetEvent(ii); + nro2 += row2.size(); + } + + int row2lastSize = row2.size(); + while (row1[nro1 - 1].size() == 0 && nro1 > 0) nro1--; - while (row2[nro2 - 1].size() == 0 && nro2 > 0) + while (row2[row2lastSize - 1].size() == 0 && row2lastSize > 0) { + row2lastSize--; nro2--; + } + + if (row2lastSize == 0) { + t2->GetEvent(t2->GetEntries() - 2); + row2lastSize = row2.size(); + while (row2[row2lastSize - 1].size() == 0 && row2lastSize > 0) { + row2lastSize--; + nro2--; + } + } if (nro1 != nro2) { printf("N readout windows different!!!! %d != %d \n", nro1, nro2); @@ -60,15 +78,29 @@ bool compareTOFDigits(std::string inpName1 = "tofdigitsOr.root", std::string inp printf("N readout windows = %d\n", nro1); - for (int i = 0; i < nro1; i++) { - if (row1[i].size() != row2[i].size()) { - printf("Readout window %d) different number of digits in this window!!!! %d != %d \n", i, int(row1[i].size()), int(row2[i].size())); + int offset = 0; + t2->GetEvent(0); + int nro2c = row2.size(); + int next = 1; + + for (int k = 0; k < nro1; k++) { + if (k >= nro2c) { + offset = nro2c; + t2->GetEvent(next); + nro2c += row2.size(); + next++; + } + int i = k; + int i2 = i - offset; + + if (row1[i].size() != row2[i2].size()) { + printf("Readout window %d) different number of digits in this window!!!! %d != %d \n", i, int(row1[i].size()), int(row2[i2].size())); status = false; return status; } auto digitsRO1 = row1.at(i).getBunchChannelData(digits1); - auto digitsRO2 = row2.at(i).getBunchChannelData(digits2); + auto digitsRO2 = row2.at(i2).getBunchChannelData(digits2); for (int j = 0; j < row1[i].size(); j++) { bool digitstatus = true; @@ -83,7 +115,7 @@ bool compareTOFDigits(std::string inpName1 = "tofdigitsOr.root", std::string inp } if (digitsRO1[j].getBC() != digitsRO2[j].getBC()) { - printf("RO %d - Digit %d/%d) Different BCs %d != %d \n", i, j, row1[i].size(), digitsRO1[j].getBC(), digitsRO2[j].getBC()); + printf("RO %d - Digit %d/%d) Different BCs %lu != %lu \n", i, j, row1[i].size(), digitsRO1[j].getBC(), digitsRO2[j].getBC()); digitstatus = false; } diff --git a/macro/compareTopologyDistributions.C b/macro/compareTopologyDistributions.C index 3104cf23d7794..1224eb8a5e120 100644 --- a/macro/compareTopologyDistributions.C +++ b/macro/compareTopologyDistributions.C @@ -24,9 +24,8 @@ void compareTopologyDistributions( string output_file_name = "comparison.root") { if (dictionary_file_name.empty()) { - dictionary_file_name = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, "", ".root"); + dictionary_file_name = o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, "", ".bin"); } - TFile dictionary_file(dictionary_file_name.c_str()); o2::itsmft::TopologyDictionary dict; dict.readBinaryFile(dictionary_file_name.c_str()); int dict_size = dict.getSize(); @@ -49,6 +48,8 @@ void compareTopologyDistributions( hRec->Scale(1 / hRec->Integral()); TFile output_file(output_file_name.c_str(), "RECREATE"); + hRec->Write(); + hDictio->Write(); TCanvas* cOut = new TCanvas("cOut", "cOut", 800, 600); cOut->SetLogy(); hDictio->GetYaxis()->SetRangeUser(1.e-8, 1.2); diff --git a/macro/duplicateHits.C b/macro/duplicateHits.C index 3dbe8511b958c..2acfbedd419e0 100644 --- a/macro/duplicateHits.C +++ b/macro/duplicateHits.C @@ -172,7 +172,6 @@ void duplicateHits(const char* filebase = "o2sim", const char* newfilebase = "o2 duplicate<o2::dataformats::MCEventHeader>(kintree, "MCEventHeader.", newkintree, factor); // TODO: fix EventIDs in the newly created MCEventHeaders duplicate<o2::TrackReference>(kintree, "TrackRefs", newkintree, factor); - duplicate<o2::dataformats::MCTruthContainer<o2::TrackReference>>(kintree, "IndexedTrackRefs", newkintree, factor); newkintree->Write(); // duplicating hits diff --git a/macro/readITSDigits.C b/macro/readITSDigits.C index 0958ca9f5312f..2fef209670934 100644 --- a/macro/readITSDigits.C +++ b/macro/readITSDigits.C @@ -9,7 +9,8 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/Digit.h" #include "SimulationDataFormat/DigitizationContext.h" -#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" #include "SimulationDataFormat/MCCompLabel.h" /// Example of accessing the digits of MCEvent digitized with continous readout @@ -42,7 +43,20 @@ void readITSDigits(std::string path = "./", std::vector<o2::itsmft::Digit>* dv = nullptr; digiTree->SetBranchAddress("ITSDigit", &dv); o2::dataformats::MCTruthContainer<o2::MCCompLabel>* labels = nullptr; - digiTree->SetBranchAddress("ITSDigitMCTruth", &labels); + o2::dataformats::IOMCTruthContainerView* labelROOTbuffer = nullptr; + o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel> constlabels; + + // for backward compatibility we check what is stored in the file + auto labelClass = digiTree->GetBranch("ITSDigitMCTruth")->GetClassName(); + bool oldlabelformat = false; + if (TString(labelClass).Contains("IOMCTruth")) { + // new format + digiTree->SetBranchAddress("ITSDigitMCTruth", &labelROOTbuffer); + } else { + // old format + digiTree->SetBranchAddress("ITSDigitMCTruth", &labels); + oldlabelformat = true; + } // ROF record entries in the digit tree std::vector<o2::itsmft::ROFRecord>* rofRecVec = nullptr; @@ -53,6 +67,9 @@ void readITSDigits(std::string path = "./", digiTree->SetBranchAddress("ITSDigitMC2ROF", &mc2rofVec); digiTree->GetEntry(0); + if (!oldlabelformat) { + labelROOTbuffer->copyandflatten(constlabels); + } // MC collisions record auto runContext = reinterpret_cast<o2::steer::DigitizationContext*>(rcFile->GetObjectChecked("DigitizationContext", "o2::steer::DigitizationContext")); @@ -89,7 +106,7 @@ void readITSDigits(std::string path = "./", int dgid = rofrec.getFirstEntry(); const auto& digit0 = (*dv)[dgid]; - const auto& labs0 = labels->getLabels(dgid); + const auto& labs0 = oldlabelformat ? labels->getLabels(dgid) : constlabels.getLabels(dgid); printf("1st digit of this ROF (Entry: %6d) :", dgid); digit0.print(std::cout); printf(" MCinfo: "); @@ -97,7 +114,7 @@ void readITSDigits(std::string path = "./", dgid = rofrec.getFirstEntry() + rofrec.getNEntries() - 1; const auto& digit1 = (*dv)[dgid]; - const auto& labs1 = labels->getLabels(dgid); + const auto& labs1 = oldlabelformat ? labels->getLabels(dgid) : constlabels.getLabels(dgid); printf("1st digit of this ROF (Entry: %6d) :", dgid); digit1.print(std::cout); printf(" MCinfo: "); diff --git a/macro/runCATrackingClusterNative.C b/macro/runCATrackingClusterNative.C index 1f8efc1837eaa..95d1f215fbeb9 100644 --- a/macro/runCATrackingClusterNative.C +++ b/macro/runCATrackingClusterNative.C @@ -40,8 +40,6 @@ using namespace o2::tpc; using namespace o2::dataformats; using namespace std; -using MCLabelContainer = MCTruthContainer<o2::MCCompLabel>; - #if !defined(__CLING__) || defined(__ROOTCLING__) // Disable in interpreted mode due to missing rootmaps // This is a prototype of a macro to test running the HLT O2 CA Tracking library on a root input file containg @@ -74,8 +72,8 @@ int runCATrackingClusterNative(TString inputFile, TString outputFile) bool doMC = true; TFile fin(inputFile); - for (int i = 0; i < Constants::MAXSECTOR; i++) { - for (int j = 0; j < Constants::MAXGLOBALPADROW; j++) { + for (int i = 0; i < o2::tpc::constants::MAXSECTOR; i++) { + for (int j = 0; j < o2::tpc::constants::MAXGLOBALPADROW; j++) { TString contName = Form("clusters_sector_%d_row_%d", i, j); TObject* tmp = fin.FindObjectAny(contName); if (tmp == nullptr) { @@ -100,7 +98,7 @@ int runCATrackingClusterNative(TString inputFile, TString outputFile) vector<TrackTPC> tracks; vector<TPCClRefElem> trackClusRefs; - MCLabelContainer tracksMC; + std::vector<o2::MCCompLabel> tracksMC; TFile fout(outputFile, "recreate"); TTree tout("events", "events"); diff --git a/macro/runTPCRefit.C b/macro/runTPCRefit.C new file mode 100644 index 0000000000000..13ead3aed2fcd --- /dev/null +++ b/macro/runTPCRefit.C @@ -0,0 +1,124 @@ +// 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". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does 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 <fstream> +#include <iostream> +#include <vector> +#include "TSystem.h" + +#include "TROOT.h" + +#include "GPUO2InterfaceRefit.h" +#include "TPCReconstruction/TPCFastTransformHelperO2.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsTPC/ClusterNative.h" +#include "DataFormatsTPC/ClusterNativeHelper.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsTPC/Constants.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#else +#pragma cling load("libO2TPCReconstruction") +#pragma cling load("libO2DataFormatsTPC") +#endif + +using namespace o2::gpu; +using namespace o2::tpc; +using namespace o2::dataformats; +using namespace o2::base; +using namespace o2::track; +using namespace std; + +int runTPCRefit(TString trackFile = "tpctracks.root", TString clusterFile = "tpc-native-clusters.root") +{ + GeometryManager::loadGeometry(); + Propagator::initFieldFromGRP(NameConf::getGRPFileName()); + const auto grp = o2::parameters::GRPObject::loadFrom("o2sim_grp.root"); + float bz = 5.00668f * grp->getL3Current() / 30000.; + std::unique_ptr<TPCFastTransform> trans = std::move(TPCFastTransformHelperO2::instance()->create(0)); + auto* prop = Propagator::Instance(); + + ClusterNativeAccess clusterIndex; + std::vector<TrackTPC>* tracks = nullptr; + std::vector<TPCClRefElem>* trackHitRefs = nullptr; + + TFile file(trackFile.Data()); + auto tree = (TTree*)file.Get("tpcrec"); + if (tree == nullptr) { + std::cout << "Error getting tree\n"; + return 1; + } + tree->SetBranchAddress("TPCTracks", &tracks); + tree->SetBranchAddress("ClusRefs", &trackHitRefs); + + ClusterNativeHelper::Reader tpcClusterReader{}; + tpcClusterReader.init(clusterFile.Data()); + std::unique_ptr<ClusterNative[]> clusterBuffer{}; + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clusterMCBuffer; + const size_t maxEvent = tree->GetEntriesFast(); + if (maxEvent != tpcClusterReader.getTreeSize()) { + std::cout << "Mismatch of entries in cluster and track file\n"; + return 1; + } + for (unsigned int iEvent = 0; iEvent < maxEvent; ++iEvent) { + std::cout << "Event " << iEvent << " of " << maxEvent << "\n"; + tree->GetEntry(iEvent); + memset(&clusterIndex, 0, sizeof(clusterIndex)); + tpcClusterReader.read(iEvent); + int retVal = tpcClusterReader.fillIndex(clusterIndex, clusterBuffer, clusterMCBuffer); + if (retVal < 0) { + std::cout << "Error reading clusters (code " << retVal << ")\n"; + return 1; + } + GPUTPCO2InterfaceRefit refit(&clusterIndex, trans.get(), bz, trackHitRefs->data(), nullptr, tracks, prop); + //refit.setGPUTrackFitInProjections(false); // Enable full 3D fit without assuming y and Z are uncorrelated + for (unsigned int i = 0; i < tracks->size(); i++) { + TrackTPC trk = (*tracks)[i]; + refit.setTrackReferenceX(trk.getX()); + std::cout << "\nTrack " << i << "\n"; + std::cout << "Org track:\n"; + trk.print(); + std::cout << "Refitting as GPU track\n"; + int retval = refit.RefitTrackAsGPU(trk, false, true); + if (retval < 0) { + std::cout << "Refit as GPU track failed " << retval << "\n"; + } else { + std::cout << "Succeeded using " << retval << " hits (chi2 = " << trk.getChi2() << ")\n"; + trk.print(); + } + trk = (*tracks)[i]; + std::cout << "Refitting as TrackParCov track\n"; + retval = refit.RefitTrackAsTrackParCov(trk, false, true); + if (retval < 0) { + std::cout << "Refit as TrackParCov track failed " << retval << "\n"; + } else { + std::cout << "Succeeded using " << retval << " hits (chi2 = " << trk.getChi2() << ")\n"; + trk.print(); + } + trk = (*tracks)[i]; + TrackParCov trkX = trk; + float chi2 = trk.getChi2(); + std::cout << "Refitting as TrackParCov track with TrackParCov input\n"; + retval = refit.RefitTrackAsTrackParCov(trkX, trk.getClusterRef(), trk.getTime0(), &chi2, false, true); + if (retval < 0) { + std::cout << "Refit as TrackParCov track failed " << retval << "\n"; + } else { + std::cout << "Succeeded using " << retval << " hits (chi2 = " << chi2 << ")\n"; + trkX.print(); + } + } + } + + return 0; +} diff --git a/macro/run_primary_vertexer_ITS.C b/macro/run_primary_vertexer_ITS.C index 0b01978e186a0..fc0599a84ae91 100644 --- a/macro/run_primary_vertexer_ITS.C +++ b/macro/run_primary_vertexer_ITS.C @@ -73,8 +73,8 @@ int run_primary_vertexer_ITS(const GPUDataTypes::DeviceType dtype = GPUDataTypes o2::base::GeometryManager::loadGeometry(path); o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); - geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); // request cached transforms + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // request cached transforms // Get event header TChain mcHeaderTree("o2sim"); @@ -170,7 +170,7 @@ int run_primary_vertexer_ITS(const GPUDataTypes::DeviceType dtype = GPUDataTypes for (size_t iROfCount{static_cast<size_t>(startAt)}; iROfCount < static_cast<size_t>(stopAt); ++iROfCount) { auto& rof = (*rofs)[iROfCount]; - o2::its::ROframe frame(iROfCount); // to get meaningful roframeId + o2::its::ROframe frame(iROfCount, 7); // to get meaningful roframeId std::cout << "ROframe: " << iROfCount << std::endl; auto it = pattIt; diff --git a/macro/run_trac_alice3.C b/macro/run_trac_alice3.C new file mode 100644 index 0000000000000..ca5fea1be42bb --- /dev/null +++ b/macro/run_trac_alice3.C @@ -0,0 +1,262 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include <string> + +#include <TFile.h> +#include <TChain.h> +#include <TH2D.h> +#include <TBranch.h> +#include <TRandom3.h> +#include <TGeoGlobalMagField.h> +#include <vector> +#include <Math/Point3Dfwd.h> +#include "DataFormatsITSMFT/ROFRecord.h" +#include "ITSMFTSimulation/Hit.h" +#include "ITStracking/Configuration.h" +#include "ITStracking/IOUtils.h" +#include "ITStracking/Tracker.h" +#include "ITStracking/TrackerTraitsCPU.h" +#include "ITStracking/Vertexer.h" +#include "DataFormatsITSMFT/Cluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "SimulationDataFormat/MCTrack.h" +#include "MathUtils/Cartesian.h" +#include "ReconstructionDataFormats/DCA.h" +#include "ReconstructionDataFormats/Vertex.h" +#endif + +using o2::its::MemoryParameters; +using o2::its::TrackingParameters; +using o2::itsmft::Hit; +using std::string; + +constexpr bool kUseSmearing{false}; + +struct particle { + int pdg = 0; + int nLayers = 0; + float pt; + float eta; + float phi; + float recoPt; + float recoEta; + float energyFirst; + float energyLast; + int isReco = 0; // 0 = no, 1 = good, 2 = fake +}; + +float getDetLengthFromEta(const float eta, const float radius) +{ + return 10. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta)))); +} + +void run_trac_alice3(const string hitsFileName = "o2sim_HitsTRK.root") +{ + + TChain mcTree("o2sim"); + mcTree.AddFile("o2sim_Kine.root"); + mcTree.SetBranchStatus("*", 0); //disable all branches + mcTree.SetBranchStatus("MCTrack*", 1); + + std::vector<o2::MCTrack>* mcArr = nullptr; + mcTree.SetBranchAddress("MCTrack", &mcArr); + + o2::its::Vertexer vertexer(new o2::its::VertexerTraits()); + TChain itsHits("o2sim"); + + itsHits.AddFile(hitsFileName.data()); + + o2::its::Tracker tracker(new o2::its::TrackerTraitsCPU()); + tracker.setBz(5.f); + + std::uint32_t roFrame; + std::vector<Hit>* hits = nullptr; + itsHits.SetBranchAddress("TRKHit", &hits); + + std::vector<TrackingParameters> trackParams(4); + trackParams[0].NLayers = 10; + trackParams[0].MinTrackLength = 10; + + std::vector<float> LayerRadii = {1.8f, 2.8f, 3.8f, 8.0f, 20.0f, 25.0f, 40.0f, 55.f, 80.0f, 100.f}; + std::vector<float> LayerZ(10); + for (int i{0}; i < 10; ++i) + LayerZ[i] = getDetLengthFromEta(1.44, LayerRadii[i]) + 1.; + std::vector<float> TrackletMaxDeltaZ = {0.1f, 0.1f, 0.3f, 0.3f, 0.3f, 0.3f, 0.5f, 0.5f, 0.5f}; + std::vector<float> CellMaxDCA = {0.05f, 0.04f, 0.05f, 0.2f, 0.4f, 0.5f, 0.5f, 0.5f}; + std::vector<float> CellMaxDeltaZ = {0.2f, 0.4f, 0.5f, 0.6f, 3.0f, 3.0f, 3.0f, 3.0f}; + std::vector<float> NeighbourMaxDeltaCurvature = {0.008f, 0.0025f, 0.003f, 0.0035f, 0.004f, 0.004f, 0.005f}; + std::vector<float> NeighbourMaxDeltaN = {0.002f, 0.0090f, 0.002f, 0.005f, 0.005f, 0.005f, 0.005f}; + + trackParams[0].LayerRadii = LayerRadii; + trackParams[0].LayerZ = LayerZ; + trackParams[0].TrackletMaxDeltaPhi = 0.3; + trackParams[0].CellMaxDeltaPhi = 0.15; + trackParams[0].CellMaxDeltaTanLambda = 0.03; + trackParams[0].TrackletMaxDeltaZ = TrackletMaxDeltaZ; + trackParams[0].CellMaxDCA = CellMaxDCA; + trackParams[0].CellMaxDeltaZ = CellMaxDeltaZ; + trackParams[0].NeighbourMaxDeltaCurvature = NeighbourMaxDeltaCurvature; + trackParams[0].NeighbourMaxDeltaN = NeighbourMaxDeltaN; + + std::vector<MemoryParameters> memParams(4); + std::vector<float> CellsMemoryCoefficients = {2.3208e-08f * 20, 2.104e-08f * 20, 1.6432e-08f * 20, 1.2412e-08f * 20, 1.3543e-08f * 20, 1.5e-08f * 20, 1.6e-08f * 20, 1.7e-08f * 20}; + std::vector<float> TrackletsMemoryCoefficients = {0.0016353f * 1000, 0.0013627f * 1000, 0.000984f * 1000, 0.00078135f * 1000, 0.00057934f * 1000, 0.00052217f * 1000, 0.00052217f * 1000, 0.00052217f * 1000, 0.00052217f * 1000}; + memParams[0].CellsMemoryCoefficients = CellsMemoryCoefficients; + memParams[0].TrackletsMemoryCoefficients = TrackletsMemoryCoefficients; + memParams[0].MemoryOffset = 8000; + + for (int i = 1; i < 4; ++i) { + memParams[i] = memParams[i - 1]; + trackParams[i] = trackParams[i - 1]; + // trackParams[i].MinTrackLength -= 2; + trackParams[i].TrackletMaxDeltaPhi = trackParams[i].TrackletMaxDeltaPhi * 3 > TMath::Pi() ? TMath::Pi() : trackParams[i].TrackletMaxDeltaPhi * 3; + trackParams[i].CellMaxDeltaPhi = trackParams[i].CellMaxDeltaPhi * 3 > TMath::Pi() ? TMath::Pi() : trackParams[i].CellMaxDeltaPhi * 3; + trackParams[i].CellMaxDeltaTanLambda *= 3; + for (auto& val : trackParams[i].TrackletMaxDeltaZ) + val *= 3; + for (auto& val : trackParams[i].CellMaxDCA) + val *= 3; + for (auto& val : trackParams[i].CellMaxDeltaZ) + val *= 3; + for (auto& val : trackParams[i].NeighbourMaxDeltaCurvature) + val *= 3; + for (auto& val : trackParams[i].NeighbourMaxDeltaN) + val *= 3; + } + + tracker.setParameters(memParams, trackParams); + + constexpr int nBins = 100; + constexpr float minPt = 0.01; + constexpr float maxPt = 10; + double newBins[nBins + 1]; + newBins[0] = minPt; + double factor = pow(maxPt / minPt, 1. / nBins); + for (int i = 1; i <= nBins; i++) { + newBins[i] = factor * newBins[i - 1]; + } + + TH1D genH("gen", ";#it{p}_{T} (GeV/#it{c});", nBins, newBins); + TH1D recH("rec", "Efficiency;#it{p}_{T} (GeV/#it{c});", nBins, newBins); + TH1D fakH("fak", "Fake rate;#it{p}_{T} (GeV/#it{c});", nBins, newBins); + TH2D deltaPt("deltapt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{p}_{T} (GeV/#it{c});", nBins, newBins, 100, -0.1, 0.1); + TH2D dcaxy("dcaxy", ";#it{p}_{T} (GeV/#it{c});DCA_{xy} (#mum);", nBins, newBins, 200, -200, 200); + TH2D dcaz("dcaz", ";#it{p}_{T} (GeV/#it{c});DCA_{z} (#mum);", nBins, newBins, 200, -200, 200); + TH2D deltaE("deltaE", ";#it{p}_{T} (GeV/#it{c});#DeltaE (MeV);", nBins, newBins, 200, 0, 100); + + ROOT::Math::XYZPointF pos{0.f, 0.f, 0.f}; + const std::array<float, 6> cov{1.e-4, 0., 0., 1.e-4, 0., 1.e-4}; + o2::dataformats::VertexBase vtx(pos, cov); + o2::dataformats::DCA dca; + + for (int iEvent{0}; iEvent < itsHits.GetEntriesFast(); ++iEvent) { + std::cout << "*************** Event " << iEvent << " ***************" << std::endl; + itsHits.GetEntry(iEvent); + mcTree.GetEvent(iEvent); + o2::its::ROframe event{iEvent, 10}; + + int id{0}; + std::map<int, particle> mapPDG; + for (auto& hit : *hits) { + const int layer{hit.GetDetectorID()}; + float xyz[3]{hit.GetX(), hit.GetY(), hit.GetZ()}; + float r{std::hypot(xyz[0], xyz[1])}; + float phi{std::atan2(-xyz[1], -xyz[0]) + o2::its::constants::math::Pi}; + + if (kUseSmearing) { + phi = gRandom->Gaus(phi, std::asin(0.0005f / r)); + xyz[0] = r * std::cos(phi); + xyz[1] = r * std::sin(phi); + xyz[2] = gRandom->Gaus(xyz[2], 0.0005f); + } + + event.addTrackingFrameInfoToLayer(layer, xyz[0], xyz[1], xyz[2], r, phi, std::array<float, 2>{0.f, xyz[2]}, + std::array<float, 3>{0.0005f * 0.0005f, 0.f, 0.0005f * 0.0005f}); + event.addClusterToLayer(layer, xyz[0], xyz[1], xyz[2], event.getClustersOnLayer(layer).size()); + event.addClusterLabelToLayer(layer, o2::MCCompLabel(hit.GetTrackID(), iEvent, iEvent, false)); + event.addClusterExternalIndexToLayer(layer, id++); + if (mapPDG.find(hit.GetTrackID()) == mapPDG.end()) { + mapPDG[hit.GetTrackID()] = particle(); + mapPDG[hit.GetTrackID()].nLayers |= 1 << layer; + mapPDG[hit.GetTrackID()].pt = std::hypot(hit.GetPx(), hit.GetPy()); + if (hit.GetTrackID() < mcArr->size()) { + auto part = mcArr->at(hit.GetTrackID()); + mapPDG[hit.GetTrackID()].energyFirst = part.GetEnergy(); + mapPDG[hit.GetTrackID()].pdg = part.GetPdgCode(); + mapPDG[hit.GetTrackID()].pt = part.GetPt(); + mapPDG[hit.GetTrackID()].eta = part.GetEta(); + mapPDG[hit.GetTrackID()].phi = part.GetPhi(); + } + } else { + mapPDG[hit.GetTrackID()].nLayers |= 1 << layer; + mapPDG[hit.GetTrackID()].energyLast = hit.GetE(); + } + } + roFrame = iEvent; + + vertexer.clustersToVertices(event); + int nPart10layers{0}; + for (auto& part : mapPDG) { + if (part.second.nLayers == 0x3FF) { + nPart10layers++; + genH.Fill(part.second.pt); + deltaE.Fill(part.second.pt, 1000 * (part.second.energyFirst - part.second.energyLast)); + } + } + tracker.clustersToTracks(event); + auto& tracks = tracker.getTracks(); + auto& tracksLabels = tracker.getTrackLabels(); + std::cout << "**** " << nPart10layers << " particles with 10 layers " << tracks.size() << " tracks" << std::endl; + int good{0}; + for (unsigned int i{0}; i < tracks.size(); ++i) { + auto& lab = tracksLabels[i]; + auto& track = tracks[i]; + int trackID = std::abs(lab.getTrackID()); + if (mapPDG.find(trackID) != mapPDG.end()) { + if (!mapPDG[trackID].pdg) + std::cout << "strange" << std::endl; + mapPDG[trackID].isReco = lab.isFake() ? 2 : 1; + mapPDG[trackID].recoPt = track.getPt(); + mapPDG[trackID].recoEta = track.getEta(); + (lab.isFake() ? fakH : recH).Fill(mapPDG[trackID].pt); + deltaPt.Fill(mapPDG[trackID].pt, (mapPDG[trackID].pt - mapPDG[trackID].recoPt) / mapPDG[trackID].pt); + if (!track.propagateToDCA(vtx, tracker.getBz(), &dca)) { + std::cout << "Propagation failed." << std::endl; + } else { + dcaxy.Fill(mapPDG[trackID].pt, dca.getY() * 1.e4); + dcaz.Fill(mapPDG[trackID].pt, dca.getZ() * 1.e4); + } + } + } + } + + TH1D dcaxy_res(recH); + dcaxy_res.Reset(); + dcaxy_res.SetNameTitle("dcaxy_res", ";#it{p}_{T} (GeV/#it{c});#sigma(DCA_{xy}) (#mum);"); + TH1D dcaz_res(recH); + dcaz_res.Reset(); + dcaz_res.SetNameTitle("dcaz_res", ";#it{p}_{T} (GeV/#it{c});#sigma(DCA_{z}) (#mum);"); + for (int i = 1; i <= nBins; ++i) { + auto proj = dcaxy.ProjectionY(Form("xy%i", i), i, i); + dcaxy_res.SetBinContent(i, proj->GetStdDev()); + dcaxy_res.SetBinError(i, proj->GetStdDevError()); + proj = dcaz.ProjectionY(Form("z%i", i), i, i); + dcaz_res.SetBinContent(i, proj->GetStdDev()); + dcaz_res.SetBinError(i, proj->GetStdDevError()); + } + + TFile output("output.root", "recreate"); + recH.Divide(&genH); + fakH.Divide(&genH); + recH.Write(); + fakH.SetLineColor(kRed); + fakH.Write(); + genH.Write(); + deltaPt.Write(); + dcaxy.Write(); + dcaz.Write(); + dcaz_res.Write(); + dcaxy_res.Write(); + deltaE.Write(); +} diff --git a/macro/run_trac_ca_its.C b/macro/run_trac_ca_its.C index 3dbedbafc4abf..431939a9a8c1a 100644 --- a/macro/run_trac_ca_its.C +++ b/macro/run_trac_ca_its.C @@ -60,16 +60,16 @@ void run_trac_ca_its(std::string path = "./", std::string inputGRP = "o2sim_grp.root") { - gSystem->Load("libO2ITStracking.so"); + gSystem->Load("libO2ITStracking"); //std::unique_ptr<GPUReconstruction> rec(GPUReconstruction::CreateInstance()); - std::unique_ptr<GPUReconstruction> rec(GPUReconstruction::CreateInstance("CUDA", true)); // for GPU with CUDA - auto* chainITS = rec->AddChain<GPUChainITS>(); - rec->Init(); + // std::unique_ptr<GPUReconstruction> rec(GPUReconstruction::CreateInstance("CUDA", true)); // for GPU with CUDA + // auto* chainITS = rec->AddChain<GPUChainITS>(); + // rec->Init(); - o2::its::Tracker tracker(chainITS->GetITSTrackerTraits()); - //o2::its::Tracker tracker(new o2::its::TrackerTraitsCPU()); - o2::its::ROframe event(0); + // o2::its::Tracker tracker(chainITS->GetITSTrackerTraits()); + o2::its::Tracker tracker(new o2::its::TrackerTraitsCPU()); + o2::its::ROframe event(0, 7); if (path.back() != '/') { path += '/'; @@ -98,9 +98,8 @@ void run_trac_ca_its(std::string path = "./", LOG(INFO) << "ITS is in " << (isContITS ? "CONTINUOS" : "TRIGGERED") << " readout mode"; auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, - o2::TransformType::L2G)); // request cached transforms - + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // request cached transforms //>>>---------- attach input data --------------->>> TChain itsClusters("o2sim"); @@ -165,7 +164,7 @@ void run_trac_ca_its(std::string path = "./", std::vector<o2::itsmft::ROFRecord> vertROFvec, *vertROFvecPtr = &vertROFvec; std::vector<Vertex> vertices, *verticesPtr = &vertices; - MCLabCont trackLabels, *trackLabelsPtr = &trackLabels; + std::vector<o2::MCCompLabel> trackLabels, *trackLabelsPtr = &trackLabels; outTree.Branch("ITSTrack", &tracksITSPtr); outTree.Branch("ITSTrackClusIdx", &trackClIdxPtr); outTree.Branch("ITSTrackMCTruth", &trackLabelsPtr); diff --git a/macro/run_trac_its.C b/macro/run_trac_its.C index 0421a33e5ad15..252db1a899072 100644 --- a/macro/run_trac_its.C +++ b/macro/run_trac_its.C @@ -31,12 +31,14 @@ #include "DetectorsCommonDataFormats/NameConf.h" #endif +#include "ReconstructionDataFormats/PrimaryVertex.h" // hack to silence JIT compiler #include "ITStracking/ROframe.h" #include "ITStracking/IOUtils.h" #include "ITStracking/Vertexer.h" #include "ITStracking/VertexerTraits.h" using MCLabCont = o2::dataformats::MCTruthContainer<o2::MCCompLabel>; +using MCLabContTr = std::vector<o2::MCCompLabel>; using Vertex = o2::dataformats::Vertex<o2::dataformats::TimeStamp<int>>; void run_trac_its(std::string path = "./", std::string outputfile = "o2trac_its.root", @@ -72,7 +74,7 @@ void run_trac_its(std::string path = "./", std::string outputfile = "o2trac_its. o2::base::GeometryManager::loadGeometry(inputGeom); auto gman = o2::its::GeometryTGeo::Instance(); - gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2GRot)); // request cached transforms + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)); // request cached transforms o2::base::Propagator::initFieldFromGRP(grp); auto field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField()); @@ -140,7 +142,7 @@ void run_trac_its(std::string path = "./", std::string outputfile = "o2trac_its. std::vector<o2::itsmft::ROFRecord> vertROFvec, *vertROFvecPtr = &vertROFvec; std::vector<Vertex> vertices, *verticesPtr = &vertices; - MCLabCont trackLabels, *trackLabelsPtr = &trackLabels; + MCLabContTr trackLabels, *trackLabelsPtr = &trackLabels; outTree.Branch("ITSTrack", &tracksITSPtr); outTree.Branch("ITSTrackClusIdx", &trackClIdxPtr); outTree.Branch("ITSTrackMCTruth", &trackLabelsPtr); @@ -164,7 +166,7 @@ void run_trac_its(std::string path = "./", std::string outputfile = "o2trac_its. o2::its::VertexerTraits vertexerTraits; o2::its::Vertexer vertexer(&vertexerTraits); - o2::its::ROframe event(0); + o2::its::ROframe event(0, 7); gsl::span<const unsigned char> patt(patterns->data(), patterns->size()); auto pattIt = patt.begin(); diff --git a/prodtests/CMakeLists.txt b/prodtests/CMakeLists.txt index 58dee216b7114..34deddb802429 100644 --- a/prodtests/CMakeLists.txt +++ b/prodtests/CMakeLists.txt @@ -9,4 +9,10 @@ # submit itself to any jurisdiction. -install(PROGRAMS sim_performance_test.sh sim_challenge.sh check_embedding_pileup.sh produce_QEDhits.sh DESTINATION prodtests) +install(PROGRAMS sim_performance_test.sh sim_challenge.sh check_embedding_pileup.sh produce_QEDhits.sh + full_system_test.sh + DESTINATION prodtests) +install(DIRECTORY full-system-test + DESTINATION prodtests + PATTERN * + PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) diff --git a/prodtests/embedding_benchmark.sh b/prodtests/embedding_benchmark.sh index 5e2e3ba51900a..11b97cf6523a9 100755 --- a/prodtests/embedding_benchmark.sh +++ b/prodtests/embedding_benchmark.sh @@ -37,7 +37,7 @@ taskwrapper() { # launch the actual command in the background echo "Launching task: ${command} &> $logfile &" - command="TIME=\"#walltime %e\" ${O2_ROOT}/share/scripts/monitor-mem.sh /usr/bin/time --output=${logfile}_time '${command}'" + command="TIME=\"#walltime %e %M\" ${O2_ROOT}/share/scripts/monitor-mem.sh /usr/bin/time --output=${logfile}_time '${command}'" eval ${command} &> $logfile & # THE NEXT PART IS THE SUPERVISION PART @@ -106,18 +106,19 @@ taskwrapper() { # default number of events -nevS=100 # nevents signal -nevBG=10 # nevents background +nevS=${nevS:-100} # nevents signal +nevBG=${nevBG:-10} # nevents background NCPUS=8 # default on GRID # default interaction rates in kHz intRate=50 -generS="pythia8hf" -generBG="pythia8hi" +generS=${generS:-"pythia8hf"} +generBG=${generBG:-"pythia8hi"} +scenario=${scenario:-"local"} # or network # default sim engine -engine="TGeant3" +engine=${engine:-"TGeant3"} # options to pass to every workflow gloOpt=" -b --run " @@ -129,6 +130,34 @@ Usage() exit } +CACHEPATH="/eos/user/a/aliperf/simulation/mock_data/background_cache/1" + +# function performing network transfer of kinematic background files --> needed for signal production +Fetch_Background_EOS_Kine() { + echo "Start Fetching background kinematics" + eos cp root://eosuser.cern.ch//${CACHEPATH}/o2simbg_Kine.root . + echo "Fetching background kinematics finished" +} + +# function performing network transfer of hits background files --> needed for digitization +Fetch_Background_EOS_Hits() { + echo "Start Fetching background hits" + # better do this with an xrootd query but should be ok to start from + export EOS_MGM_URL=root://eosuser.cern.ch + eos cp ${CACHEPATH}/o2simbg_geometry.root . + eos cp ${CACHEPATH}/o2simbg_grp.root . + eos cp ${CACHEPATH}/o2simbg_Hits*.root . + echo "Fetching background hits finished" +} + +# Upload to EOS +Upload_Background_EOS() { + echo "Start uploading background event" + export EOS_MGM_URL=root://eosuser.cern.ch + eos cp o2simbg* ${CACHEPATH} + echo "Uploading background hits finished" +} + while [ $# -gt 0 ] ; do case $1 in -e) engine=$2; shift 2 ;; @@ -137,33 +166,78 @@ while [ $# -gt 0 ] ; do esac done -echo "Running background simulation for $nevBG $collSyst events with $generBG generator and engine $engine" -taskwrapper sim_bg.log o2-sim -j ${NCPUS} -n"$nevBG" --configKeyValue "Diamond.width[2]=6." -g "$generBG" -e "$engine" -o o2simbg --skipModules ZDC -# extract time - -echo "Running signal simulation for $nevS $collSyst events with $generS generator and engine $engine" -taskwrapper sim_s.log o2-sim -j ${NCPUS} -n"$nevS" --configKeyValue "Diamond.width[2]=6." -g "$generS" -e "$engine" -o o2sims --embedIntoFile o2simbg_Kine.root --skipModules ZDC -# extract time - -echo "Running digitization for $intRate kHz interaction rate" -# We run the digitization in stages in order to use -# the given number of CPUs in a good way. The order and sequence here can be modified and improvements will be -# achievable when "REST" contains more parallelism. TPC and TRD should use the resources fully already. -intRate=$((1000*(intRate))); -taskwrapper digi_TPC.log o2-sim-digitizer-workflow --tpc-lanes ${NCPUS} --onlyDet TPC $gloOpt --sims o2simbg,o2sims -n ${nevS} --interactionRate $intRate --disable-mc -taskwrapper digi_TRD.log o2-sim-digitizer-workflow --onlyDet TRD $gloOpt --configKeyValues="TRDSimParams.digithreads=${NCPUS}" --sims o2simbg,o2sims -n ${nevS} --interactionRate $intRate --disable-mc -taskwrapper digi_REST.log o2-sim-digitizer-workflow --skipDet TRD,TPC $gloOpt --sims o2simbg,o2sims -n ${nevS} --interactionRate $intRate --disable-mc - -# extract gain from embedding procedure and background reusage by analysing reported times: -transporttime_BG=`awk '/walltime/{print $2}' sim_bg.log_time` -transporttime_S=`awk '/walltime/{print $2}' sim_s.log_time` -digi_time_TPC=`awk '/walltime/{print $2}' digi_TPC.log_time` -digi_time_TRD=`awk '/walltime/{print $2}' digi_TRD.log_time` -digi_time_REST=`awk '/walltime/{print $2}' digi_REST.log_time` - -echo "$transporttime_BG $transporttime_S $digi_time_TPC $digi_time_TRD $digi_time_REST" | awk -v nS="${nevS}" -v nB="${nevBG}" '//{noembed=($1*nS/(1.*nB) + $2) + $3 + $4 + $5;embed=($1 + $2) + $3 + $4 + $5; print "NBG "nB " NSignal "nS" Ordinary-time "noembed," Embed-time "embed" Ratio ",noembed/embed }' - -# TODO: compare this to scenario where we fetch background events from the network! -# Run this actually containerized to fix the CPU + MEMORY space. +produce_background() +{ + echo "Running background simulation for $nevBG $collSyst events with $generBG generator and engine $engine" + taskwrapper sim_bg.log o2-sim -j ${NCPUS} -n"$nevBG" --configKeyValues "Diamond.width[2]=6." -g "$generBG" -e "$engine" -o o2simbg --skipModules ZDC --seed ${SEED:-1} +} + +produce_signal() +{ + echo "Running signal simulation for $nevS $collSyst events with $generS generator and engine $engine" + taskwrapper sim_s.log o2-sim -j ${NCPUS} -n"$nevS" --configKeyValues "Diamond.width[2]=6." -g "$generS" -e "$engine" -o o2sims --embedIntoFile o2simbg_Kine.root --skipModules ZDC --seed ${SEED:-1} +} + +do_transport_local() +{ + produce_background + Upload_Background_EOS + produce_signal +} + +do_transport_usecachedBG() +{ + echo "Start fetching background kinematics" + Fetch_Background_EOS_Kine & + KINE_PID=$! + + Fetch_Background_EOS_Hits & + HITS_PID=$! + + wait ${KINE_PID} + produce_signal + + wait ${HITS_PID} +} + +digitize() { + echo "Running digitization for $intRate kHz interaction rate" + # We run the digitization in stages in order to use + # the given number of CPUs in a good way. The order and sequence here can be modified and improvements will be + # achievable when "REST" contains more parallelism. TPC and TRD should use the resources fully already. + intRate=$((1000*(intRate))); + taskwrapper digi_TPC.log o2-sim-digitizer-workflow --tpc-lanes ${NCPUS} --onlyDet TPC $gloOpt --sims o2simbg,o2sims -n ${nevS} --interactionRate $intRate --disable-mc -- + taskwrapper digi_TRD.log o2-sim-digitizer-workflow --onlyDet TRD $gloOpt --configKeyValues="TRDSimParams.digithreads=${NCPUS}" --sims o2simbg,o2sims -n ${nevS} --interactionRate $intRate --disable-mc --incontext collisioncontext.root + taskwrapper digi_REST.log o2-sim-digitizer-workflow --skipDet TRD,TPC $gloOpt --sims o2simbg,o2sims -n ${nevS} --interactionRate $intRate --disable-mc --incontext collisioncontext.root +} + +if [[ "${scenario}" == "local" ]]; then + do_transport_local + digitize + + # extract gain from embedding procedure and background reusage by analysing reported times: + transporttime_BG=`awk '/walltime/{print $2}' sim_bg.log_time` + transporttime_S=`awk '/walltime/{print $2}' sim_s.log_time` + digi_time_TPC=`awk '/walltime/{print $2}' digi_TPC.log_time` + digi_time_TRD=`awk '/walltime/{print $2}' digi_TRD.log_time` + digi_time_REST=`awk '/walltime/{print $2}' digi_REST.log_time` + echo "$transporttime_BG $transporttime_S $digi_time_TPC $digi_time_TRD $digi_time_REST" + echo "$transporttime_BG $transporttime_S $digi_time_TPC $digi_time_TRD $digi_time_REST" | awk -v nS="${nevS}" -v nB="${nevBG}" '//{digi=$3+$4+$5;noembed=($1*nS/(1.*nB) + $2) + digi;embed=($1 + $2) + digi; embedCachedIO=($2 + digi);print "NBG "nB " NSignal "nS" Ordinary-time "noembed," Embed-time "embed" Embed-time (cached optimal) "embedCachedIO" Ratio ",noembed/embed" Ratio optimal"noembed/embedCachedIO }' +fi + +if [[ "${scenario}" == "network" ]]; then + # Scenario where we fetch background events from the network! + do_transport_usecachedBG + + digitize + + # extract gain from embedding procedure and background reusage by analysing reported times: + transporttime_S=`awk '/walltime/{print $2}' sim_s.log_time` + digi_time_TPC=`awk '/walltime/{print $2}' digi_TPC.log_time` + digi_time_TRD=`awk '/walltime/{print $2}' digi_TRD.log_time` + digi_time_REST=`awk '/walltime/{print $2}' digi_REST.log_time` + + echo "$transporttime_S $digi_time_TPC $digi_time_TRD $digi_time_REST" +fi rm localhost* diff --git a/prodtests/full-system-test/README.md b/prodtests/full-system-test/README.md new file mode 100644 index 0000000000000..632125811c7a8 --- /dev/null +++ b/prodtests/full-system-test/README.md @@ -0,0 +1,73 @@ +<!-- doxy +\page refprodtestsfull-system-test Full system test configuration and scripts +/doxy --> + +## Full system test configuration and scripts + +The full system test workflow scripts consist of 3 shell scripts: +* `dpl-workflow.sh` : The main script that runs the dpl-workflow for the reconstruction. + It can read the input either internally, or receive it externally by one of the others. +* `raw-reader.sh` : Runs the `o2-raw-file-reader` to read the raw files as external input to `dpl-workflow.sh`. +* `datadistribution.sh` : Run the `StfBuilder` to read time frame files as external input to `dpl-workflow.sh`. + +One can either run the `dpl-workflow.sh` standalone (with `EXTINPUT=0`) or in parallel with one of the other scripts in separate shells (with `EXTINPUT=1`) + +In addition, there is the shared `setenv.sh` script which sets default configuration options, and there is the additional benchmark script: +* `start_tmux.sh` : This starts the full test in the configuration for the EPN with 2 NUMA domains, 512 GB RAM, 8 GPUs. + It will run tmux with 3 sessions, running twice the `dpl-workflow.sh` and once one of the external input scripts (selected via `dd` and `rr` command line option). + * Please note that `start_tmux.sh` overrides several of the environment options (see below) with the defaults for the EPN. + The only relevant options for `start_tmux.sh` should be `TFDELAY` and `GPUMEMSIZE`. + * Note also that while `dpl-workflow.sh` is a generic flexible script that can be used for actual operation, `start_tmux.sh` is a benchmark script to demonstrate how the full workflow is supposed to run on the EPN. + It is meant for standalone tests and not to really start the actual processing on the EPN. + +The `dpl-workflow.sh` can run both the synchronous and the asynchronous workflow, selected via the `SYNCMODE` option (see below), but note the following constraints. +* By default, it will run the full chain (EPN + FLP parts) such that it can operate as a full standalone benchmark processing simulated raw data. +* In order to run only the EPN part (skipping the steps that will run on the FLP), an `EPNONLY` option will be added later. + +All settings are configured via environment variables. +The default settings (if no env variable is exported) are defined in `setenv.sh` which is sourced by all other scripts. +(Please note that `start_tmux.sh` overrides a couple of options with EPN defaults). +The following options exist (some of the options are not used in all scripts, and might behave slightly differently as noted): +* `NTIMEFRAMES`: Number of time frames to process. + * `dpl-workflow.sh` without `EXTINPUT`: Will replay the timeframe `NTIMEFRAMES` time and then exit. + * `raw-reader.sh` : Will replay the timeframe `NTIMEFRAMES` time and `raw-reader.sh` will exit, the dpl-workflows will keep running. + * Ignored by `datadistribution.sh`, it will always run in an endless loop. +* `TFDELAY`: Delay in seconds between publishing time frames (1 / rate). +* `NGPUS`: Number of GPUs to use, data distributed round-robin. +* `GPUTYPE`: GPU Tracking backend to use, can be CPU / CUDA / HIP / OCL / OCL2. +* `SHMSIZE`: Size of the global shared memory segment. +* `DDSHMSIZE`: Size of shared memory unmanaged region for DataDistribution Input. +* `GPUMEMSIZE`: Size of allocated GPU memory (if GPUTYPE != CPU) +* `HOSTMEMSIZE`: Size of allocated host memory for GPU reconstruction (0 = default). + * For `GPUTYPE = CPU`: TPC Tracking scratch memory size. (Default 0 -> dynamic allocation.) + * Otherwise : Size of page-locked host memory for GPU processing. (Defauls 0 -> 1 GB.) +* `SAVECTF`: Save the CTF to a root file. +* `CREATECTFDICT`: Create CTF dictionary. + * 0: Read `ctf_dictionary.root` as input. + * 1: Create `ctf_dictionary.root`. Note that this was already done automatically if the raw data was simulated with `full_system_test.sh`. +* `SYNCMODE`: Run only reconstruction steps of the synchronous reconstruction. + * Note that there is no `ASYNCMODE` but instead the `CTFINPUT` option already enforces asynchronous processing. +* `NUMAGPUIDS`: NUMAID-aware GPU id selection. Needed for the full EPN configuration with 8 GPUs, 2 NUMA domains, 4 GPUs per domain. + In this configuration, 2 instances of `dpl-workflow.sh` must run in parallel. + To be used in combination with `NUMAID` to select the id per workflow. + `start_tmux.sh` will set up these variables automatically. +* `NUMAID`: SHM segment id to use for shipping data as well as set of GPUs to use (use `0` / `1` for 2 NUMA domains, 0 = GPUS `0` to `NGPUS - 1`, 1 = GPUS `NGPUS` to `2 * NGPUS - 1`) +* 0: Runs all reconstruction steps, of sync and of async reconstruction, using raw data input. +* 1: Runs only the steps of synchronous reconstruction, using raw data input. +* `EXTINPUT`: Receive input from raw FMQ channel instead of running o2-raw-file-reader. + * 0: `dpl-workflow.sh` can run as standalone benchmark, and will read the input itself. + * 1: To be used in combination with either `datadistribution.sh` or `raw-reader.sh` or with another DataDistribution instance. +* `CTFINPUT`: Read input from CTF ROOT file. This option is incompatible to EXTINPUT=1. The CTF ROOT file can be stored via SAVECTF=1. +* `NHBPERTF`: Time frame length (in HBF) +* `GLOBALDPLOPT`: Global DPL workflow options appended to o2-dpl-run. +* `EPNPIPELINES`: Set default EPN pipeline multiplicities. + Normally the workflow will start 1 dpl device per processor. + For some of the CPU parts, this is insufficient to keep step with the GPU processing rate, e.g. one ITS-TPC matcher on the CPU is slower than the TPC tracking on multiple GPUs. + This option adds some multiplicies for CPU processes using DPL's pipeline feature. + The settings were tuned for EPN processing with 8 GPUs. + It is auto-selected by `start-tmux.sh`. +* `SEVERITY`: Log verbosity (e.g. info or error) +* `SHMTHROW`: Throw exception when running out of SHM memory. + It is suggested to leave this enabled (default) on tests on the laptop to get an actual error when it runs out of memory. + This is disabled in `start_tmux.sh`, to avoid breaking the processing while there is a chance that another process might free memory and we can continue. +* `NORATELOG`: Disable FairMQ Rate Logging. diff --git a/prodtests/full-system-test/datadistribution.sh b/prodtests/full-system-test/datadistribution.sh new file mode 100755 index 0000000000000..23e4c9be2c4b0 --- /dev/null +++ b/prodtests/full-system-test/datadistribution.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +MYDIR="$(dirname $(readlink -f $0))" +source $MYDIR/setenv.sh + +if [ "0$ALIENVLVL" == "0" ]; then + eval "`alienv shell-helper`" + alienv --no-refresh load DataDistribution/latest +fi + +export DATADIST_SHM_DELAY=10 +export TF_DIR=./raw/timeframe +export TFRATE=$(awk "BEGIN {printf \"%.6f\",1/$TFDELAY}") + +ARGS_ALL="--session default --severity $SEVERITY --shm-segment-size $SHMSIZE --no-cleanup" + +StfBuilder --id stfb --session default --transport shmem \ + --dpl-channel-name dpl-chan --channel-config "name=dpl-chan,type=push,method=bind,address=ipc://@stfb-to-dpl,transport=shmem,rateLogging=1" \ + --data-source-dir ${TF_DIR} \ + --data-source-rate=0.01 \ + --data-source-repeat \ + --data-source-regionsize=${DDSHMSIZE} \ + --data-source-headersize=1024 \ + --data-source-enable \ + --data-source-preread 5 \ + --severity debug \ + ${ARGS_ALL} diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh new file mode 100755 index 0000000000000..551e82ed829e6 --- /dev/null +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +MYDIR="$(dirname $(readlink -f $0))" +source $MYDIR/setenv.sh + +if [ "0$ALIENVLVL" == "0" ]; then + eval "`alienv shell-helper`" + alienv --no-refresh load O2/latest +fi + +# Set general arguments +ARGS_ALL="--session default --severity $SEVERITY --shm-segment-id $NUMAID --shm-segment-size $SHMSIZE" +if [ $EXTINPUT == 1 ] || [ $NUMAGPUIDS == 1 ]; then + ARGS_ALL+=" --no-cleanup" +fi +if [ $SHMTHROW == 0 ]; then + ARGS_ALL+=" --shm-throw-bad-alloc 0" +fi +if [ $NORATELOG == 1 ]; then + ARGS_ALL+=" --fairmq-rate-logging 0" +fi + +# Set some individual workflow arguments depending on configuration +TPC_INPUT=zsraw +TPC_OUTPUT=tracks,clusters,disable-writer +TPC_CONFIG= +TPC_CONFIG_KEY= +TOF_INPUT=raw +TOF_OUTPUT=clusters,matching-info +ITS_CONFIG= +ITS_CONFIG_KEY= +if [ $SYNCMODE == 1 ]; then + ITS_CONFIG_KEY+="fastMultConfig.cutMultClusLow=30;fastMultConfig.cutMultClusHigh=2000;fastMultConfig.cutMultVtxHigh=500" + TPC_CONFIG_KEY+=" GPU_global.synchronousProcessing=1;" +fi +if [ $CTFINPUT == 1 ]; then + ITS_CONFIG+=" --async-phase" +else + ITS_CONFIG+=" --entropy-encoding" + TOF_OUTPUT+=",ctf" + TPC_OUTPUT+=",encoded-clusters" +fi + +if [ $GPUTYPE == "HIP" ]; then + if [ $NUMAID == 0 ] || [ $NUMAGPUIDS == 0 ]; then + export TIMESLICEOFFSET=0 + else + export TIMESLICEOFFSET=$NGPUS + fi + TPC_CONFIG_KEY+="GPU_proc.deviceNum=0;GPU_global.mutexMemReg=true;" + TPC_CONFIG+=" --environment \"ROCR_VISIBLE_DEVICES={timeslice${TIMESLICEOFFSET}}\"" + export HSA_NO_SCRATCH_RECLAIM=1 + #export HSA_TOOLS_LIB=/opt/rocm/lib/librocm-debug-agent.so.2 +else + TPC_CONFIG_KEY+="GPU_proc.deviceNum=-2;" +fi + +if [ $GPUTYPE != "CPU" ]; then + TPC_CONFIG_KEY+="GPU_proc.forceMemoryPoolSize=$GPUMEMSIZE;" + if [ $HOSTMEMSIZE == "0" ]; then + HOSTMEMSIZE=$(( 1 << 30 )) + fi +fi +if [ $HOSTMEMSIZE != "0" ]; then + TPC_CONFIG_KEY+="GPU_proc.forceHostMemoryPoolSize=$HOSTMEMSIZE;" +fi + +if [ $EPNPIPELINES == 1 ]; then + N_TPCENT=$(($(expr 7 \* $NGPUS / 4) > 0 ? $(expr 7 \* $NGPUS / 4) : 1)) + N_TPCITS=$(($(expr 7 \* $NGPUS / 4) > 0 ? $(expr 7 \* $NGPUS / 4) : 1)) + N_ITSDEC=$(($(expr 3 \* $NGPUS / 4) > 0 ? $(expr 3 \* $NGPUS / 4) : 1)) +else + N_TPCENT=1 + N_TPCITS=1 + N_ITSDEC=1 +fi + +# Input workflow +if [ $CTFINPUT == 1 ]; then + TPC_INPUT=compressed-clusters-ctf + TOF_INPUT=digits + WORKFLOW="o2-ctf-reader-workflow --ctf-input o2_ctf_0000000000.root $ARGS_ALL | " +elif [ $EXTINPUT == 1 ]; then + WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --dataspec \"B:TPC/RAWDATA;C:ITS/RAWDATA;D:TOF/RAWDATA;D:MFT/RAWDATA;E:FT0/RAWDATA;F:MID/RAWDATA;G:EMC/RAWDATA;H:PHS/RAWDATA;I:CPV/RAWDATA\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@stfb-to-dpl,transport=shmem,rateLogging=0\" | " +else + WORKFLOW="o2-raw-file-reader-workflow $ARGS_ALL --configKeyValues \"HBFUtils.nHBFPerTF=$NHBPERTF;\" --delay $TFDELAY --loop $NTIMEFRAMES --max-tf 0 --input-conf rawAll.cfg | " +fi + +#Decoder workflows +if [ $CTFINPUT == 0 ]; then + WORKFLOW+="o2-itsmft-stf-decoder-workflow $ARGS_ALL --pipeline its-stf-decoder:$N_ITSDEC | " + WORKFLOW+="o2-itsmft-stf-decoder-workflow $ARGS_ALL --runmft true | " + WORKFLOW+="o2-ft0-flp-dpl-workflow $ARGS_ALL --disable-root-output | " + WORKFLOW+="o2-mid-raw-to-digits-workflow $ARGS_ALL | " + WORKFLOW+="o2-tof-compressor $ARGS_ALL | " +fi + +# Common workflows +WORKFLOW+="o2-its-reco-workflow $ARGS_ALL --trackerCA $DISABLE_MC --clusters-from-upstream --disable-root-output $ITS_CONFIG --configKeyValues \"HBFUtils.nHBFPerTF=128;$ITS_CONFIG_KEY\" | " +WORKFLOW+="o2-tpc-reco-workflow ${ARGS_ALL/--severity $SEVERITY/--severity $SEVERITY_TPC} --input-type=$TPC_INPUT $DISABLE_MC --output-type $TPC_OUTPUT --pipeline tpc-tracker:$NGPUS,tpc-entropy-encoder:$N_TPCENT $TPC_CONFIG --configKeyValues \"HBFUtils.nHBFPerTF=$NHBPERTF;GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$TPC_CONFIG_KEY\" | " +WORKFLOW+="o2-tpcits-match-workflow $ARGS_ALL --disable-root-input --disable-root-output $DISABLE_MC --pipeline itstpc-track-matcher:$N_TPCITS --configKeyValues \"HBFUtils.nHBFPerTF=128;\" | " +WORKFLOW+="o2-ft0-reco-workflow $ARGS_ALL --disable-root-input --disable-root-output $DISABLE_MC --configKeyValues \"HBFUtils.nHBFPerTF=128;\" | " +WORKFLOW+="o2-tof-reco-workflow $ARGS_ALL --configKeyValues \"HBFUtils.nHBFPerTF=$NHBPERTF\" --input-type $TOF_INPUT --output-type $TOF_OUTPUT --disable-root-input --disable-root-output $DISABLE_MC | " + +# Workflows disabled in sync mode +if [ $SYNCMODE == 0 ]; then + WORKFLOW+="o2-mid-reco-workflow $ARGS_ALL --disable-root-output $DISABLE_MC | " + WORKFLOW+="o2-mft-reco-workflow $ARGS_ALL --clusters-from-upstream $DISABLE_MC --disable-root-output --configKeyValues \"HBFUtils.nHBFPerTF=128;\" | " + WORKFLOW+="o2-primary-vertexing-workflow $ARGS_ALL $DISABLE_MC --disable-root-input --disable-root-output --validate-with-ft0 | " + WORKFLOW+="o2-secondary-vertexing-workflow $ARGS_ALL --disable-root-input --disable-root-output | " +fi + +# Workflows disabled in async mode +if [ $CTFINPUT == 0 ]; then + WORKFLOW+="o2-phos-reco-workflow $ARGS_ALL --input-type raw --output-type cells $DISABLE_MC | " + WORKFLOW+="o2-cpv-reco-workflow $ARGS_ALL --input-type raw --output-type digits $DISABLE_MC | " + WORKFLOW+="o2-cpv-reco-workflow $ARGS_ALL --input-type digits --output-type clusters $DISABLE_MC | " + WORKFLOW+="o2-emcal-reco-workflow $ARGS_ALL --input-type raw --output-type cells --disable-root-output $DISABLE_MC | " + + WORKFLOW+="o2-itsmft-entropy-encoder-workflow $ARGS_ALL --runmft true | " + WORKFLOW+="o2-ft0-entropy-encoder-workflow $ARGS_ALL | " + WORKFLOW+="o2-mid-entropy-encoder-workflow $ARGS_ALL | " + WORKFLOW+="o2-phos-entropy-encoder-workflow $ARGS_ALL | " + WORKFLOW+="o2-cpv-entropy-encoder-workflow $ARGS_ALL | " + WORKFLOW+="o2-emcal-entropy-encoder-workflow $ARGS_ALL | " + + WORKFLOW+="o2-tpc-scdcalib-interpolation-workflow $ARGS_ALL --disable-root-output --disable-root-input | " + + # Output workflow + CTF_OUTPUT_TYPE="none" + if [ $CREATECTFDICT == 1 ] && [ $SAVECTF == 1 ]; then CTF_OUTPUT_TYPE="both"; fi + if [ $CREATECTFDICT == 1 ] && [ $SAVECTF == 0 ]; then CTF_OUTPUT_TYPE="dict"; fi + if [ $CREATECTFDICT == 0 ] && [ $SAVECTF == 1 ]; then CTF_OUTPUT_TYPE="ctf"; fi + CMD_CTF="o2-ctf-writer-workflow $ARGS_ALL --output-type $CTF_OUTPUT_TYPE --onlyDet ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV" + if [ $CREATECTFDICT == 1 ] && [ $; then + CMD_CTF+=" --save-dict-after 1" + fi + WORKFLOW+="$CMD_CTF | " +fi + +# DPL run binary +WORKFLOW+="o2-dpl-run $ARGS_ALL $GLOBALDPLOPT --run" + +# Execute the command we have assembled +#echo Running workflow: +#echo $WORKFLOW | sed "s/| */|\n/g" +#echo +eval $WORKFLOW diff --git a/prodtests/full-system-test/raw-reader.sh b/prodtests/full-system-test/raw-reader.sh new file mode 100755 index 0000000000000..466b30bab4a50 --- /dev/null +++ b/prodtests/full-system-test/raw-reader.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ "0$ALIENVLVL" == "0" ]; then + eval "`alienv shell-helper`" + alienv --no-refresh load O2/latest +fi + +MYDIR="$(dirname $(readlink -f $0))" +source $MYDIR/setenv.sh + +ARGS_ALL="--session default --shm-throw-bad-alloc 0 --no-cleanup" +if [ $NUMAGPUIDS == 1 ]; then + TMPSIZE=$(expr $DDSHMSIZE \* 1024 \* 1024) + ARGS_ALL+=" --shm-segment-id 2 --shm-segment-size $TMPSIZE --shm-mlock-segment 1 --shm-zero-segment 1" +else + ARGS_ALL+=" --shm-segment-size $SHMSIZE" +fi +if [ $NORATELOG == 1 ]; then + ARGS_ALL+=" --fairmq-rate-logging 0" +fi + +o2-raw-file-reader-workflow $ARGS_ALL --loop $NTIMEFRAMES --delay $TFDELAY --input-conf rawAll.cfg --configKeyValues "HBFUtils.nHBFPerTF=$NHBPERTF;" --max-tf 0 --raw-channel-config "name=dpl-chan,type=push,method=bind,address=ipc://@stfb-to-dpl,transport=shmem,rateLogging=0" $GLOBALDPLOPT --run diff --git a/prodtests/full-system-test/setenv.sh b/prodtests/full-system-test/setenv.sh new file mode 100755 index 0000000000000..fb8332b52c95c --- /dev/null +++ b/prodtests/full-system-test/setenv.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Make sure we can open sufficiently many files / allocate enough memory +ulimit -n 4096 && ulimit -l unlimited && ulimit -m unlimited && ulimit -l unlimited +if [ $? != 0 ]; then + echo Error setting ulimits + exit 1 +fi + +if [ -z "$NTIMEFRAMES" ]; then export NTIMEFRAMES=1; fi # Number of time frames to process +if [ -z "$TFDELAY" ]; then export TFDELAY=100; fi # Delay in seconds between publishing time frames +if [ -z "$NGPUS" ]; then export NGPUS=1; fi # Number of GPUs to use, data distributed round-robin +if [ -z "$GPUTYPE" ]; then export GPUTYPE=CPU; fi # GPU Tracking backend to use, can be CPU / CUDA / HIP / OCL / OCL2 +if [ -z "$SHMSIZE" ]; then export SHMSIZE=$(( 128 << 30 )); fi # Size of shared memory for messages +if [ -z "$DDSHMSIZE" ]; then export DDSHMSIZE=$(( 32 << 10 )); fi # Size of shared memory for DD Input +if [ -z "$GPUMEMSIZE" ]; then export GPUMEMSIZE=$(( 13 << 30 )); fi # Size of allocated GPU memory (if GPUTYPE != CPU) +if [ -z "$HOSTMEMSIZE" ]; then export HOSTMEMSIZE=0; fi # Size of allocated host memory for GPU reconstruction (0 = default) +if [ -z "$CREATECTFDICT" ]; then export CREATECTFDICT=0; fi # Create CTF dictionary +if [ -z "$SAVECTF" ]; then export SAVECTF=0; fi # Save the CTF to a ROOT file +if [ -z "$SYNCMODE" ]; then export SYNCMODE=0; fi # Run only reconstruction steps of the synchronous reconstruction +if [ -z "$NUMAID" ]; then export NUMAID=0; fi # SHM segment id to use for shipping data as well as set of GPUs to use (use 0 / 1 for 2 NUMA domains) +if [ -z "$NUMAGPUIDS" ]; then export NUMAGPUIDS=0; fi # NUMAID-aware GPU id selection +if [ -z "$EXTINPUT" ]; then export EXTINPUT=0; fi # Receive input from raw FMQ channel instead of running o2-raw-file-reader +if [ -z "$CTFINPUT" ]; then export CTFINPUT=0; fi # Read input from CTF (incompatible to EXTINPUT=1) +if [ -z "$NHBPERTF" ]; then export NHBPERTF=128; fi # Time frame length (in HBF) +if [ -z "$GLOBALDPLOPT" ]; then export GLOBALDPLOPT=; fi # Global DPL workflow options appended at the end +if [ -z "$EPNPIPELINES" ]; then export EPNPIPELINES=0; fi # Set default EPN pipeline multiplicities +if [ -z "$SEVERITY" ]; then export SEVERITY="info"; fi # Log verbosity +if [ -z "$SHMTHROW" ]; then export SHMTHROW=1; fi # Throw exception when running out of SHM +if [ -z "$NORATELOG" ]; then export NORATELOG=1; fi # Disable FairMQ Rate Logging + +SEVERITY_TPC="info" # overrides severity for the tpc workflow +DISABLE_MC="--disable-mc" + +if [ $EXTINPUT == 1 ] && [ $CTFINPUT == 1 ]; then + echo EXTINPUT and CTFINPUT are incompatible + exit 1 +fi +if [ $SAVECTF == 1 ] && [ $CTFINPUT == 1 ]; then + echo SAVECTF and CTFINPUT are incompatible + exit 1 +fi +if [ $SYNCMODE == 1 ] && [ $CTFINPUT == 1 ]; then + echo SYNCMODE and CTFINPUT are incompatible + exit 1 +fi diff --git a/prodtests/full-system-test/start_tmux.sh b/prodtests/full-system-test/start_tmux.sh new file mode 100755 index 0000000000000..9d90513111f2a --- /dev/null +++ b/prodtests/full-system-test/start_tmux.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +if [ "0$1" == "0" ]; then + echo Please indicate whether to start with raw-reader [rr] or with DataDistribution [dd] + exit 1 +fi + +MYDIR="$(dirname $(readlink -f $0))" +source $MYDIR/setenv.sh + +# This sets up the hardcoded configuration to run the full system workflow on the EPN +export NGPUS=4 +export GPUTYPE=HIP +export SHMSIZE=$(( 128 << 30 )) +export DDSHMSIZE=$(( 64 << 10 )) +export GPUMEMSIZE=$(( 24 << 30 )) +export NUMAGPUIDS=1 +export EXTINPUT=1 +export EPNPIPELINES=1 +export SYNCMODE=1 +export SHMTHROW=0 +export SEVERITY=error + +if [ $1 == "dd" ]; then + export CMD=datadistribution.sh +else + export CMD=raw-reader.sh + export NTIMEFRAMES=1000000 +fi + +if [ ! -f matbud.root -a -f ctf_dictionary.root ]; then + echo matbud.root or ctf_dictionary.root missing + exit 1 +fi + +rm -f /dev/shm/*fmq* + +tmux \ + new-session "NUMAID=0 numactl --membind 0 --cpunodebind 0 $MYDIR/dpl-workflow.sh; echo END; sleep 1000" \; \ + split-window "sleep 30; NUMAID=1 numactl --membind 1 --cpunodebind 1 $MYDIR/dpl-workflow.sh; echo END; sleep 1000" \; \ + split-window "sleep 60; numactl --interleave=all $MYDIR/$CMD; echo END; sleep 1000" diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh new file mode 100755 index 0000000000000..b3cdd6512b737 --- /dev/null +++ b/prodtests/full_system_test.sh @@ -0,0 +1,142 @@ +#!/bin/bash +# +# A workflow performing a full system test: +# - simulation of digits +# - creation of raw data +# - reconstruction of raw data +# +# Note that this might require a production server to run. +# +# This script can use additional binary objects which can be optionally provided: +# - matbud.root + ITSdictionary.bin +# +# authors: D. Rohr / S. Wenzel + +# include jobutils, which notably brings +# --> the taskwrapper as a simple control and monitoring tool +# (look inside the jobutils.sh file for documentation) +# --> utilities to query CPU count +. ${O2_ROOT}/share/scripts/jobutils.sh + + +NEvents=${NEvents:-10} #550 for full TF (the number of PbPb events) +NEventsQED=${NEventsQED:-1000} #35000 for full TF +NCPUS=$(getNumberOfPhysicalCPUCores) +echo "Found ${NCPUS} physical CPU cores" +NJOBS=${NJOBS:-"${NCPUS}"} +SHMSIZE=${SHMSIZE:-8000000000} # Size of shared memory for messages (use 128 GB for 550 event full TF) +TPCTRACKERSCRATCHMEMORY=${SHMSIZE:-4000000000} # Size of memory allocated by TPC tracker. (Use 24 GB for 550 event full TF) +ENABLE_GPU_TEST=${ENABLE_GPU_TEST:-0} # Run the full system test also on the GPU +NTIMEFRAMES=${NTIMEFRAMES:-1} # Number of time frames to process +TFDELAY=${TFDELAY:-100} # Delay in seconds between publishing time frames +NOMCLABELS="--disable-mc" + +# allow skipping +JOBUTILS_SKIPDONE=ON +# enable memory monitoring (independent on whether DPL or not) +JOBUTILS_MONITORMEM=ON +# CPU monitoring JOBUTILS_MONITORCPU=ON + +# prepare some metrics file for the monitoring system +METRICFILE=metrics.dat +CONFIG="full_system_test_N${NEvents}" +HOST=`hostname` + +# include header information such as tested alidist tag and O2 tag +TAG="conf=${CONFIG},host=${HOST}${ALIDISTCOMMIT:+,alidist=$ALIDISTCOMMIT}${O2COMMIT:+,o2=$O2COMMIT}" +echo "versions,${TAG} alidist=\"${ALIDISTCOMMIT}\",O2=\"${O2COMMIT}\" " > ${METRICFILE} + + +ulimit -n 4096 # Make sure we can open sufficiently many files +mkdir -p qed +cd qed +taskwrapper qedsim.log o2-sim -j $NJOBS -n$NEventsQED -m PIPE ITS MFT FT0 FV0 -g extgen --configKeyValues '"GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/QEDLoader.C;QEDGenParam.yMin=-5;QEDGenParam.yMax=6;QEDGenParam.ptMin=0.001;QEDGenParam.ptMax=1.;Diamond.width[2]=6."' +cd .. + +GLOBALDPLOPT="-b" # --monitoring-backend no-op:// is currently removed due to https://alice.its.cern.ch/jira/browse/O2-1887 +taskwrapper sim.log o2-sim -n $NEvents --skipModules ZDC --configKeyValues "Diamond.width[2]=6." -g pythia8hi -j $NJOBS +taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents --simPrefixQED qed/o2sim --qed-x-section-ratio 3735 ${NOMCLABELS} --firstOrbit 0 --firstBC 0 --skipDet TRD --tpc-lanes $((NJOBS < 36 ? NJOBS : 36)) --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} +taskwrapper digiTRD.log o2-sim-digitizer-workflow -n $NEvents --simPrefixQED qed/o2sim --qed-x-section-ratio 3735 ${NOMCLABELS} --firstOrbit 0 --firstBC 0 --onlyDet TRD --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} --incontext collisioncontext.root --configKeyValues "TRDSimParams.digithreads=${NJOBS}" +mkdir -p raw +taskwrapper itsraw.log o2-its-digi2raw --file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -o raw/ITS +taskwrapper mftraw.log o2-mft-digi2raw --file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -o raw/MFT +taskwrapper ft0raw.log o2-ft0-digi2raw --file-per-link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -o raw/FT0 +taskwrapper tpcraw.log o2-tpc-digits-to-rawzs --file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -i tpcdigits.root -o raw/TPC +taskwrapper tofraw.log o2-tof-reco-workflow ${GLOBALDPLOPT} --tof-raw-file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' --output-type raw --tof-raw-outdir raw/TOF +taskwrapper midraw.log o2-mid-digits-to-raw-workflow ${GLOBALDPLOPT} --mid-raw-outdir raw/MID --mid-raw-perlink --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' +taskwrapper emcraw.log o2-emcal-rawcreator --file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -o raw/EMC +taskwrapper phsraw.log o2-phos-digi2raw --file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -o raw/PHS +taskwrapper cpvraw.log o2-cpv-digi2raw --file-for link --configKeyValues '"HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=0"' -o raw/CPV +cat raw/*/*.cfg > rawAll.cfg + +# We run the workflow in both CPU-only and With-GPU mode +STAGES="NOGPU" +if [ $ENABLE_GPU_TEST != "0" ]; then + STAGES+=" WITHGPU" +fi +STAGES+=" ASYNC" +for STAGE in $STAGES; do + + ARGS_ALL="--session default" + DICTCREATION="" + if [[ "$STAGE" = "WITHGPU" ]]; then + export CREATECTFDICT=0 + export GPUTYPE=CUDA + export GPUMEMSIZE=6000000000 + export HOSTMEMSIZE=1000000000 + export SYNCMODE=1 + export CTFINPUT=0 + export SAVECTF=0 + elif [[ "$STAGE" = "ASYNC" ]]; then + export CREATECTFDICT=1 + export GPUTYPE=CPU + export SYNCMODE=0 + export HOSTMEMSIZE=$TPCTRACKERSCRATCHMEMORY + export CTFINPUT=1 + export SAVECTF=0 + else + export CREATECTFDICT=1 + export GPUTYPE=CPU + export SYNCMODE=0 + export HOSTMEMSIZE=$TPCTRACKERSCRATCHMEMORY + export CTFINPUT=0 + export SAVECTF=1 + rm -f ctf_dictionary.root + fi + export SHMSIZE + export NTIMEFRAMES + export TFDELAY + export GLOBALDPLOPT + + logfile=reco_${STAGE}.log + taskwrapper ${logfile} "$O2_ROOT/prodtests/full-system-test/dpl-workflow.sh" + + # --- record interesting metrics to monitor ---- + # boolean flag indicating if workflow completed successfully at all + RC=$? + SUCCESS=0 + [ -f "${logfile}_done" ] && [ "$RC" = 0 ] && SUCCESS=1 + echo "success_${STAGE},${TAG} value=${SUCCESS}" >> ${METRICFILE} + + if [ "${SUCCESS}" = "1" ]; then + # runtime + walltime=`grep "#walltime" ${logfile}_time | awk '//{print $2}'` + echo "walltime_${STAGE},${TAG} value=${walltime}" >> ${METRICFILE} + + # GPU reconstruction (also in CPU version) processing time + gpurecotime=`grep "tpc-tracker" reco_NOGPU.log | grep -e "Total Wall Time:" | awk '//{printf "%f", $6/1000000}'` + echo "gpurecotime_${STAGE},${TAG} value=${gpurecotime}" >> ${METRICFILE} + + # memory + maxmem=`awk '/PROCESS MAX MEM/{print $5}' ${logfile}` # in MB + avgmem=`awk '/PROCESS AVG MEM/{print $5}' ${logfile}` # in MB + echo "maxmem_${STAGE},${TAG} value=${maxmem}" >> ${METRICFILE} + echo "avgmem_${STAGE},${TAG} value=${avgmem}" >> ${METRICFILE} + + # some physics quantities + tpctracks=`grep "tpc-tracker" ${logfile} | grep -e "found.*track" | awk '//{print $4}'` + echo "tpctracks_${STAGE},${TAG} value=${tpctracks}" >> ${METRICFILE} + tpcclusters=`grep -e "Event has.*TPC Clusters" ${logfile} | awk '//{print $5}'` + echo "tpcclusters_${STAGE},${TAG} value=${tpcclusters}" >> ${METRICFILE} + fi +done diff --git a/prodtests/produce_QEDhits.sh b/prodtests/produce_QEDhits.sh index bdab7189b0829..d737f1b2d7a4d 100755 --- a/prodtests/produce_QEDhits.sh +++ b/prodtests/produce_QEDhits.sh @@ -26,6 +26,6 @@ echo "Running simulation for $nev $collSyst events with $gener generator and eng # we only need passive material and QED sensitive detectors close to beampipe # set -x -o2-sim -j 8 -m PIPE ABSO DIPO SHIL ITS FT0 MFT -n"$nev" -e "$engine" --noemptyevents -g extgen --extGenFile ${O2_ROOT}/share/Generators/external/QEDLoader.C --configKeyValues "SimCutParams.maxRTracking=300" -o o2simqed +o2-sim -j 8 -m PIPE ABSO DIPO SHIL ITS FT0 MFT -n"$nev" -e "$engine" --noemptyevents -g external --configKeyValues "GeneratorExternal.fileName=${O2_ROOT}/share/Generators/external/QEDLoader.C;SimCutParams.maxRTracking=300" -o o2simqed root -q -b -l ${O2_ROOT}/share/macro/analyzeHits.C\(\"o2simqed\"\) > QEDhitstats.log diff --git a/prodtests/sim_challenge.sh b/prodtests/sim_challenge.sh index 8e4369f21e8da..76656450d122e 100755 --- a/prodtests/sim_challenge.sh +++ b/prodtests/sim_challenge.sh @@ -3,105 +3,9 @@ # chain of algorithms from MC and reco -# ----------- START WITH UTILITY FUNCTIONS ---------------------------- - -child_pid_list= -# finds out all the (recursive) child process starting from a parent -# output includes the parent -# output is saved in child_pid_list -childprocs() { - local parent=$1 - if [ "$parent" ] ; then - child_pid_list="$child_pid_list $parent" - for childpid in $(pgrep -P ${parent}); do - childprocs $childpid - done; - fi -} - -# accumulate return codes -RC_ACUM=0 - -taskwrapper() { - # A simple task wrapper launching a DPL workflow in the background - # and checking the output for exceptions. If exceptions are found, - # all participating processes will be sent a termination signal. - # The rational behind this function is to be able to determine failing - # conditions early and prevent longtime hanging executables - # (until DPL offers signal handling and automatic shutdown) - - local logfile=$1 - shift 1 - local command="$*" - - # launch the actual command in the background - echo "Launching task: ${command} &> $logfile &" - command="TIME=\"#walltime %e\" ${O2_ROOT}/share/scripts/monitor-mem.sh /usr/bin/time --output=${logfile}_time '${command}'" - eval ${command} &> $logfile & - - # THE NEXT PART IS THE SUPERVISION PART - # get the PID - PID=$! - - while [ 1 ]; do - # We don't like to see critical problems in the log file. - - # We need to grep on multitude of things: - # - all sorts of exceptions (may need to fine-tune) - # - segmentation violation - # - there was a crash - pattern="-e \"xception\" \ - -e \"segmentation violation\" \ - -e \"error while setting up workflow\" \ - -e \"There was a crash.\"" - - grepcommand="grep -H ${pattern} $logfile >> encountered_exceptions_list 2>/dev/null" - eval ${grepcommand} - - grepcommand="grep -h --count ${pattern} $logfile 2>/dev/null" - # using eval here since otherwise the pattern is translated to a - # a weirdly quoted stringlist - RC=$(eval ${grepcommand}) - - # if we see an exception we will bring down the DPL workflow - # after having given it some chance to shut-down itself - # basically --> send kill to all children - if [ "$RC" != "" -a "$RC" != "0" ]; then - echo "Detected critical problem in logfile $logfile" - sleep 2 - - # query processes still alive - child_pid_list= - childprocs ${PID} - for p in $child_pid_list; do - echo "killing child $p" - kill $p - done - - RC_ACUM=$((RC_ACUM+1)) - return 1 - fi - - # check if command returned which may bring us out of the loop - ps -p $PID > /dev/null - [ $? == 1 ] && break - - # sleep for some time - sleep 5 - done - - # wait for PID and fetch return code - # ?? should directly exit here? - wait $PID - # return code - RC=$? - RC_ACUM=$((RC_ACUM+RC)) - [ ! "${RC} -eq 0" ] && echo "command ${command} had nonzero exit code ${RC}" - - return ${RC} -} - -# ----------- START WITH ACTUAL SCRIPT ---------------------------- +# ------------ LOAD UTILITY FUNCTIONS ---------------------------- +. ${O2_ROOT}/share/scripts/jobutils.sh +# ----------- START WITH ACTUAL SCRIPT --------------------------- # default number of events @@ -122,8 +26,13 @@ generPbPb="pythia8hi" engine="TGeant3" # options to pass to every workflow -gloOpt=" -b --run " +gloOpt=" -b --run --shm-segment-size 10000000000" +# option to set the number of sim workers +simWorker="" + +# option to set the number of tpc-lanes +tpcLanes="" Usage() { @@ -139,12 +48,15 @@ while [ $# -gt 0 ] ; do -r) intRate=$2; shift 2 ;; -e) engine=$2; shift 2 ;; -f) fromstage=$2; shift 2 ;; + -j) simWorker="-j $2"; shift 2 ;; + -l) tpcLanes="--tpc-lanes $2"; shift 2 ;; -h) Usage ;; *) echo "Wrong input"; Usage; esac done -collSyst="${collSyst,,}" # convert to lower case +# convert to lower case (the bash construct ${collSyst,,} is less portable) +collSyst=`echo "$collSyst" | awk '{print tolower($0)}'` if [ "$collSyst" == "pp" ]; then gener="$generPP" [[ "nev" -lt "1" ]] && nev="$nevPP" @@ -161,7 +73,8 @@ fi dosim="0" dodigi="0" doreco="0" -fromstage="${fromstage,,}" +# convert to lowercase +fromstage=`echo "$fromstage" | awk '{print tolower($0)}'` if [ "$fromstage" == "sim" ]; then dosim="1" dodigi="1" @@ -180,16 +93,16 @@ fi if [ "$dosim" == "1" ]; then #--------------------------------------------------- echo "Running simulation for $nev $collSyst events with $gener generator and engine $engine" - taskwrapper sim.log o2-sim -n"$nev" --configKeyValue "Diamond.width[2]=6." -g "$gener" -e "$engine" + taskwrapper sim.log o2-sim -n"$nev" --configKeyValue "Diamond.width[2]=6." -g "$gener" -e "$engine" $simWorker ##------ extract number of hits - root -q -b -l ${O2_ROOT}/share/macro/analyzeHits.C > hitstats.log + taskwrapper hitstats.log root -q -b -l ${O2_ROOT}/share/macro/analyzeHits.C fi if [ "$dodigi" == "1" ]; then echo "Running digitization for $intRate kHz interaction rate" intRate=$((1000*(intRate))); - taskwrapper digi.log o2-sim-digitizer-workflow $gloOpt --interactionRate $intRate + taskwrapper digi.log o2-sim-digitizer-workflow $gloOpt --interactionRate $intRate $tpcLanes echo "Return status of digitization: $?" # existing checks #root -b -q O2/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C+ @@ -202,7 +115,7 @@ if [ "$doreco" == "1" ]; then echo "Return status of tpcreco: $?" echo "Running ITS reco flow" - taskwrapper itsreco.log o2-its-reco-workflow $gloOpt + taskwrapper itsreco.log o2-its-reco-workflow --trackerCA --async-phase $gloOpt echo "Return status of itsreco: $?" # existing checks @@ -214,10 +127,10 @@ if [ "$doreco" == "1" ]; then taskwrapper mftreco.log o2-mft-reco-workflow $gloOpt echo "Return status of mftreco: $?" - echo "Running FIT(FT0) reco flow" - #needs FIT digitized data - taskwrapper fitreco.log o2-fit-reco-workflow $gloOpt - echo "Return status of fitreco: $?" + echo "Running FT0 reco flow" + #needs FT0 digitized data + taskwrapper ft0reco.log o2-ft0-reco-workflow $gloOpt + echo "Return status of ft0reco: $?" echo "Running ITS-TPC macthing flow" #needs results of o2-tpc-reco-workflow, o2-its-reco-workflow and o2-fit-reco-workflow @@ -229,10 +142,17 @@ if [ "$doreco" == "1" ]; then taskwrapper tofMatch.log o2-tof-reco-workflow $gloOpt echo "Return status of its-tpc-tof match: $?" + echo "Running primary vertex finding flow" + #needs results of TPC-ITS matching and FIT workflows + taskwrapper pvfinder.log o2-primary-vertexing-workflow $gloOpt + echo "Return status of primary vertexing: $?" + echo "Running TOF matching QA" #need results of ITSTPC-TOF matching (+ TOF clusters and ITS-TPC tracks) - root -b -q -l $O2_ROOT/share/macro/checkTOFMatching.C 1>tofmatch_qa.log 2>&1 + taskwrapper tofmatch_qa.log root -b -q -l $O2_ROOT/share/macro/checkTOFMatching.C echo "Return status of TOF matching qa: $?" -fi -exit ${RC_ACUM} + echo "Producing AOD" + taskwrapper aod.log o2-aod-producer-workflow --aod-writer-keep dangling --aod-writer-resfile "AO2D" --aod-writer-resmode UPDATE --aod-timeframe-id 1 + echo "Return status of AOD production: $?" +fi diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index dc8efaaaffd49..82ff45288bdd6 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -26,6 +26,7 @@ target_link_libraries(allsim O2::HMPIDSimulation O2::ITSSimulation O2::ITS3Simulation + O2::TRKSimulation O2::MCHBase O2::MCHSimulation O2::MFTSimulation @@ -102,11 +103,21 @@ o2_data_file(COPY o2simtopology.json DESTINATION config) # perform # * # some checks on kinematics and track references + +# make workspace for simulation tests +set(SIMTESTDIR ${CMAKE_BINARY_DIR}/o2sim_tests) +file(MAKE_DIRECTORY ${SIMTESTDIR}) + +# define environment needed for simulation tests (during build phase) +# needed because the build environment is different to runtime environment +set(SIMENV "ALICE_O2SIM_DUMPLOG=ON;O2_ROOT=${CMAKE_BINARY_DIR}/stage;VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/share;ROOT_INCLUDE_PATH=$ENV{ROOT_INCLUDE_PATH}:$ENV{HEPMC3_ROOT}/include;ROOT_DYN_PATH=$ENV{ROOTSYS}/lib") +message(STATUS "SIMENV = ${SIMENV}") + o2_name_target(sim NAME o2simExecutable IS_EXE) o2_name_target(sim-serial NAME o2simSerialExecutable IS_EXE) o2_add_test_wrapper(NAME o2sim_G4 - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${SIMTESTDIR} DONT_FAIL_ON_TIMEOUT MAX_ATTEMPTS 2 TIMEOUT 400 @@ -119,21 +130,18 @@ o2_add_test_wrapper(NAME o2sim_G4 TGeant4 -o o2simG4 + ENVIRONMENT "${SIMENV}" LABELS "g4;sim;long") set_tests_properties(o2sim_G4 PROPERTIES PASS_REGULAR_EXPRESSION "SIMULATION RETURNED SUCCESFULLY" FIXTURES_SETUP G4) -set_property(TEST o2sim_G4 APPEND PROPERTY ENVIRONMENT - ALICE_O2SIM_DUMPLOG=ON - O2_ROOT=${CMAKE_BINARY_DIR}/stage - VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/share) set_property(TEST o2sim_G4 APPEND PROPERTY ENVIRONMENT ${G4ENV}) # # note that the MT is currently only supported in the non FairMQ version o2_add_test_wrapper(NAME o2sim_G4_mt - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${SIMTESTDIR} DONT_FAIL_ON_TIMEOUT MAX_ATTEMPTS 2 TIMEOUT 400 @@ -146,22 +154,20 @@ o2_add_test_wrapper(NAME o2sim_G4_mt on -o o2simG4MT + ENVIRONMENT "${SIMENV}" LABELS "g4;sim;long") set_tests_properties(o2sim_G4_mt PROPERTIES PASS_REGULAR_EXPRESSION "Macro finished succesfully") -set_property(TEST o2sim_G4_mt APPEND PROPERTY ENVIRONMENT - O2_ROOT=${CMAKE_BINARY_DIR}/stage - VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/share) set_property(TEST o2sim_G4_mt APPEND PROPERTY ENVIRONMENT ${G4ENV}) o2_add_test(CheckStackG4 SOURCES checkStack.cxx NAME o2sim_checksimkinematics_G4 - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${SIMTESTDIR} COMMAND_LINE_ARGS o2simG4 - PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat + PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::Steer NO_BOOST_TEST LABELS "g4;sim;long") @@ -169,7 +175,7 @@ set_tests_properties(o2sim_checksimkinematics_G4 PROPERTIES FIXTURES_REQUIRED G4) o2_add_test_wrapper(NAME o2sim_G3 - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${SIMTESTDIR} DONT_FAIL_ON_TIMEOUT MAX_ATTEMPTS 3 COMMAND $<TARGET_FILE:${o2simExecutable}> @@ -182,7 +188,8 @@ o2_add_test_wrapper(NAME o2sim_G3 -o o2simG3 LABELS g3 sim long - ENVIRONMENT "O2_ROOT=${CMAKE_BINARY_DIR}/stage;VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/share") + ENVIRONMENT "${SIMENV}" +) # set properties for G3 ... we use fixtures to force execution after G4 (since # they require multiple CPUs) @@ -193,14 +200,13 @@ set_tests_properties(o2sim_G3 G4 FIXTURES_SETUP G3) -set_property(TEST o2sim_G3 APPEND PROPERTY ENVIRONMENT "ALICE_O2SIM_DUMPLOG=ON") o2_add_test(CheckStackG3 SOURCES checkStack.cxx NAME o2sim_checksimkinematics_G3 - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${SIMTESTDIR} COMMAND_LINE_ARGS o2simG3 - PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat + PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat O2::Steer NO_BOOST_TEST LABELS "g3;sim;long") @@ -208,25 +214,49 @@ o2_add_test(CheckStackG3 set_tests_properties(o2sim_checksimkinematics_G3 PROPERTIES FIXTURES_REQUIRED G3) + o2_add_test_wrapper(NAME o2sim_hepmc - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${SIMTESTDIR} DONT_FAIL_ON_TIMEOUT MAX_ATTEMPTS 2 TIMEOUT 400 COMMAND $<TARGET_FILE:${o2simExecutable}> COMMAND_LINE_ARGS -n 2 - -j + -j 2 -g hepmc - --HepMCFile - ${CMAKE_SOURCE_DIR}/Generators/share/data/pythia.hepmc + --configKeyValues + "HepMC.fileName=${CMAKE_SOURCE_DIR}/Generators/share/data/pythia.hepmc;HepMC.version=2" -o o2simhepmc LABELS long sim hepmc3 - ENVIRONMENT "O2_ROOT=${CMAKE_BINARY_DIR}/stage;VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/share") + ENVIRONMENT "${SIMENV}") set_tests_properties(o2sim_hepmc PROPERTIES PASS_REGULAR_EXPRESSION "SIMULATION RETURNED SUCCESFULLY") + +# somewhat analyse the logfiles as another means to detect problems +o2_add_test_wrapper(NAME o2sim_G3_checklogs + WORKING_DIRECTORY ${SIMTESTDIR} + DONT_FAIL_ON_TIMEOUT + COMMAND ${CMAKE_SOURCE_DIR}/run/simlogcheck.sh + COMMAND_LINE_ARGS o2simG3_serverlog o2simG3_mergerlog o2simG3_workerlog0 + LABELS long sim hepmc3) + +set_tests_properties(o2sim_G3_checklogs + PROPERTIES FIXTURES_REQUIRED G3) + +# somewhat analyse the logfiles as another means to detect problems +o2_add_test_wrapper(NAME o2sim_G4_checklogs + WORKING_DIRECTORY ${SIMTESTDIR} + DONT_FAIL_ON_TIMEOUT + COMMAND ${CMAKE_SOURCE_DIR}/run/simlogcheck.sh + COMMAND_LINE_ARGS o2simG4_serverlog o2simG4_mergerlog o2simG4_workerlog0 + LABELS long sim +) + +set_tests_properties(o2sim_G3_checklogs + PROPERTIES FIXTURES_REQUIRED G4) diff --git a/run/O2HitMerger.h b/run/O2HitMerger.h index 469776b67b89d..2b90119ea2715 100644 --- a/run/O2HitMerger.h +++ b/run/O2HitMerger.h @@ -61,9 +61,11 @@ #include <map> #include <vector> #include <csignal> +#include <mutex> #ifdef ENABLE_UPGRADES #include <ITS3Simulation/Detector.h> +#include <TRKSimulation/Detector.h> #endif namespace o2 @@ -191,7 +193,7 @@ class O2HitMerger : public FairMQDevice void fillBranch(int eventID, std::string const& name, T* ptr) { // fetch tree into which to fill - + const std::lock_guard<std::mutex> lock(mMapsMtx); auto iter = mEventToTTreeMap.find(eventID); if (iter == mEventToTTreeMap.end()) { { @@ -260,7 +262,6 @@ class O2HitMerger : public FairMQDevice fillSubEventInfoEntry(info); consumeData<std::vector<o2::MCTrack>>(info.eventID, "MCTrack", data, index); consumeData<std::vector<o2::TrackReference>>(info.eventID, "TrackRefs", data, index); - consumeData<o2::dataformats::MCTruthContainer<o2::TrackReference>>(info.eventID, "IndexedTrackRefs", data, index); while (index < data.Size()) { consumeHits(info.eventID, data, index); } @@ -308,18 +309,21 @@ class O2HitMerger : public FairMQDevice return expectmore; } + void cleanEvent(int eventID) + { + // remove tree for that eventID + const std::lock_guard<std::mutex> lock(mMapsMtx); + delete mEventToTTreeMap[eventID]; + delete mEventToTMemFileMap[eventID]; + mEventToTTreeMap.erase(eventID); + mEventToTMemFileMap.erase(eventID); // remove memfile + } + template <typename T> void backInsert(T const& from, T& to) { std::copy(from.begin(), from.end(), std::back_inserter(to)); } - // specialization for o2::MCTruthContainer<S> - template <typename S> - void backInsert(o2::dataformats::MCTruthContainer<S> const& from, - o2::dataformats::MCTruthContainer<S>& to) - { - to.mergeAtBack(from); - } void reorderAndMergeMCTRacks(TTree& origin, TTree& target, const std::vector<int>& nprimaries, const std::vector<int>& nsubevents) { @@ -418,8 +422,9 @@ class O2HitMerger : public FairMQDevice } else { // loop over subevents Int_t nprimTot = 0; - for (auto entry = 0; entry < entries; entry++) + for (auto entry = 0; entry < entries; entry++) { nprimTot += nprimaries[entry]; + } Int_t idelta0 = 0; Int_t idelta1 = nprimTot; for (auto entry = entries - 1; entry >= 0; --entry) { @@ -449,8 +454,9 @@ class O2HitMerger : public FairMQDevice { Int_t cId = track.getMotherTrackId(); Int_t ioffset = (cId < nprim) ? idelta0 : idelta1; - if (cId != -1) + if (cId != -1) { track.SetMotherTrackId(cId + ioffset); + } } void updateTrackIdWithOffset(TrackReference& ref, Int_t nprim, Int_t idelta0, Int_t idelta1) @@ -545,7 +551,7 @@ class O2HitMerger : public FairMQDevice std::vector<int> nprimaries; // collecting primary particles in each subevent std::vector<int> nsubevents; // collecting of subevent numbers - std::unique_ptr<o2::dataformats::MCEventHeader> eventheader; // The event header + o2::dataformats::MCEventHeader* eventheader = nullptr; // The event header // the MC labels (trackID) for hits o2::data::SubEventInfo* info = nullptr; @@ -556,9 +562,9 @@ class O2HitMerger : public FairMQDevice trackoffsets.emplace_back(info->npersistenttracks); nprimaries.emplace_back(info->nprimarytracks); nsubevents.emplace_back(info->part); + info->mMCEventHeader.printInfo(); if (eventheader == nullptr) { - eventheader = std::unique_ptr<dataformats::MCEventHeader>( - new dataformats::MCEventHeader(info->mMCEventHeader)); + eventheader = &info->mMCEventHeader; } else { eventheader->getMCEventStats().add(info->mMCEventHeader.getMCEventStats()); } @@ -568,15 +574,15 @@ class O2HitMerger : public FairMQDevice if (confref.isFilterOutNoHitEvents()) { if (eventheader && eventheader->getMCEventStats().getNHits() == 0) { LOG(INFO) << " Taking out event " << eventID << " due to no hits "; - + cleanEvent(eventID); return false; } } // put the event headers into the new TTree - o2::dataformats::MCEventHeader* headerptr = eventheader.get(); - auto headerbr = o2::base::getOrMakeBranch(*mOutTree, "MCEventHeader.", &headerptr); - headerbr->SetAddress(&headerptr); + eventheader->printInfo(); + auto headerbr = o2::base::getOrMakeBranch(*mOutTree, "MCEventHeader.", &eventheader); + headerbr->SetAddress(&eventheader); headerbr->Fill(); headerbr->ResetAddress(); @@ -596,7 +602,6 @@ class O2HitMerger : public FairMQDevice reorderAndMergeMCTRacks(*tree, *mOutTree, nprimaries, subevOrdered); Int_t ioffset = 0; remapTrackIdsAndMerge<std::vector<o2::TrackReference>>("TrackRefs", *tree, *mOutTree, trackoffsets, nprimaries, subevOrdered); - merge<o2::dataformats::MCTruthContainer<o2::TrackReference>>("IndexedTrackRefs", *tree, *mOutTree); // c) do the merge procedure for all hits ... delegate this to detector specific functions // since they know about types; number of branches; etc. @@ -617,12 +622,7 @@ class O2HitMerger : public FairMQDevice LOG(INFO) << "outtree has file " << mOutTree->GetDirectory()->GetFile()->GetName(); mOutFile->Write("", TObject::kOverwrite); - // remove tree for that eventID - delete mEventToTTreeMap[eventID]; - mEventToTTreeMap.erase(eventID); - // remove memfile - delete mEventToTMemFileMap[eventID]; - mEventToTMemFileMap.erase(eventID); + cleanEvent(eventID); LOG(INFO) << "MERGING HITS TOOK " << timer.RealTime(); return true; @@ -642,7 +642,7 @@ class O2HitMerger : public FairMQDevice std::unordered_map<int, TTree*> mEventToTTreeMap; //! in memory trees to collect / presort incoming data per event std::unordered_map<int, TMemFile*> mEventToTMemFileMap; //! files associated to the TTrees std::thread mMergerIOThread; //! a thread used to do hit merging and IO flushing asynchronously - + std::mutex mMapsMtx; //! int mEntries = 0; //! counts the number of entries in the branches int mEventChecksum = 0; //! checksum for events int mNExpectedEvents = 0; //! number of events that we expect to receive @@ -742,6 +742,10 @@ void O2HitMerger::initDetInstances() mDetectorInstances[i] = std::move(std::make_unique<o2::its3::Detector>(true)); counter++; } + if (i == DetID::TRK) { + mDetectorInstances[i] = std::move(std::make_unique<o2::trk::Detector>(true)); + counter++; + } #endif // init the detector specific output files initHitTreeAndOutFile(i); diff --git a/run/O2PrimaryServerDevice.h b/run/O2PrimaryServerDevice.h index 0b5178f7512eb..655b3fae8b815 100644 --- a/run/O2PrimaryServerDevice.h +++ b/run/O2PrimaryServerDevice.h @@ -27,6 +27,8 @@ #include <SimConfig/SimConfig.h> #include <CommonUtils/ConfigurableParam.h> #include <CommonUtils/RngHelper.h> +#include "Field/MagneticField.h" +#include <TGeoGlobalMagField.h> #include <typeinfo> #include <thread> #include <TROOT.h> @@ -37,7 +39,7 @@ namespace o2 namespace devices { -class O2PrimaryServerDevice : public FairMQDevice +class O2PrimaryServerDevice final : public FairMQDevice { public: /// Default constructor @@ -60,7 +62,12 @@ class O2PrimaryServerDevice : public FairMQDevice TStopwatch timer; timer.Start(); auto& conf = o2::conf::SimConfig::Instance(); - o2::conf::ConfigurableParam::updateFromString(conf.getKeyValueString()); + + // init magnetic field as it might be needed by the generator + auto field = o2::field::MagneticField::createNominalField(conf.getConfigData().mField); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + o2::eventgen::GeneratorFactory::setPrimaryGenerator(conf, &mPrimGen); mPrimGen.SetEvent(&mEventHeader); @@ -96,6 +103,11 @@ class O2PrimaryServerDevice : public FairMQDevice for (auto& keyvalue : vm) { LOG(INFO) << "///// " << keyvalue.first << " " << keyvalue.second.value().type().name(); } + // update the parameters from an INI/JSON file, if given (overrides code-based version) + o2::conf::ConfigurableParam::updateFromFile(conf.getConfigFile()); + // update the parameters from stuff given at command line (overrides file-based version) + o2::conf::ConfigurableParam::updateFromString(conf.getKeyValueString()); + // MC ENGINE LOG(INFO) << "ENGINE SET TO " << vm["mcEngine"].as<std::string>(); // CHUNK SIZE diff --git a/run/O2SimDevice.h b/run/O2SimDevice.h index c0736bbe6229b..ddbce28ea8adc 100644 --- a/run/O2SimDevice.h +++ b/run/O2SimDevice.h @@ -39,7 +39,7 @@ class TMessageWrapper : public TMessage }; // device representing a simulation worker -class O2SimDevice : public FairMQDevice +class O2SimDevice final : public FairMQDevice { public: O2SimDevice() = default; @@ -188,14 +188,8 @@ class O2SimDevice : public FairMQDevice << "part " << info.part << "/" << info.nparts; gRandom->SetSeed(chunk->mSubEventInfo.seed); - auto& conf = o2::conf::SimConfig::Instance(); - if (strcmp(conf.getMCEngine().c_str(), "TGeant4") == 0) { - mVMC->ProcessEvent(); - } else { - // for Geant3 at least calling ProcessEvent is not enough - // as some hooks are not called - mVMC->ProcessRun(1); - } + // Process one event + mVMC->ProcessRun(1); FairSystemInfo sysinfo; LOG(INFO) << "TIME-STAMP " << mTimer.RealTime() << "\t"; diff --git a/run/SimExamples/Adaptive_Pythia8/README.md b/run/SimExamples/Adaptive_Pythia8/README.md index b605ff80e2aef..eb97a422993c6 100644 --- a/run/SimExamples/Adaptive_Pythia8/README.md +++ b/run/SimExamples/Adaptive_Pythia8/README.md @@ -11,4 +11,4 @@ Pythia8 events are generated according to the configuration given in the file `a The settings are as such that Pythia8 is initialised using the `pythia8_inel.cfg` configuration file. The customisation allows the generator to receive and react to a notification that signals the embedding status of the simulation, giving the header of the background event for determination of the subsequent actions. In this case, the number of pythia8 events to be embedded is calculated according to a formula that uses the number of primary particles of the background events. -The macro file is specified via the argument of `--extGenFile` whereas the specific function call to retrieve the configuration and define the formula is specified via the argument of `--extGenFunc`. +The macro file is specified via `--configKeyValues` setting `GeneratorExternal.fileName` whereas the specific function call to retrieve the configuration and define the formula is specified via `GeneratorExternal.funcName`. diff --git a/run/SimExamples/Adaptive_Pythia8/adaptive_pythia8.macro b/run/SimExamples/Adaptive_Pythia8/adaptive_pythia8.macro index 170095da3ca34..b126c0fcfccc6 100644 --- a/run/SimExamples/Adaptive_Pythia8/adaptive_pythia8.macro +++ b/run/SimExamples/Adaptive_Pythia8/adaptive_pythia8.macro @@ -3,21 +3,20 @@ // Example of an implementation of an external event generator // that is used and adapts it behavior in an embedding scenario. // -// usage: o2sim -g extgen --extGenFile adaptive_pythia8.C -// options: --extGenFunc "adaptive_pythia8(\"0.001 * x\") +// usage: o2sim -g external --configKeyValues 'GeneratorExternal.fileName=adaptive_pythia8.C;GeneratorExternal.funcName="adaptive_pythia8(\"0.001 * x\")"' using namespace o2::eventgen; class Adaptive_Pythia8 : public GeneratorPythia8 { public: - Adaptive_Pythia8(char *formula = "0.01 * x") : + Adaptive_Pythia8(const char *formula = "0.01 * x") : GeneratorPythia8(), mFormula("formula", formula) { }; ~Adaptive_Pythia8() = default; // update the number of events to be generated // according to background primaries and formula - void notifyEmbedding(const FairMCEventHeader *bkgHeader) override { + void notifyEmbedding(const o2::dataformats::MCEventHeader *bkgHeader) override { auto nPrimaries = bkgHeader->GetNPrim(); mEvents = mFormula.Eval(nPrimaries); }; @@ -43,7 +42,7 @@ private: }; FairGenerator* -adaptive_pythia8(char *formula = "0.01 * x") +adaptive_pythia8(const char *formula = "0.01 * x") { std::cout << " --- adaptive_pythia8 initialising with formula: " << formula << std::endl; auto py8 = new Adaptive_Pythia8(formula); diff --git a/run/SimExamples/Adaptive_Pythia8/run.sh b/run/SimExamples/Adaptive_Pythia8/run.sh index 61a7af344611d..9e01e36647a4b 100755 --- a/run/SimExamples/Adaptive_Pythia8/run.sh +++ b/run/SimExamples/Adaptive_Pythia8/run.sh @@ -11,7 +11,7 @@ set -x # PART a) NBGR=5 -o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS -o bkg --configKeyValue \ +o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS -o bkg --configKeyValues \ "Diamond.position[2]=0.1;Diamond.width[2]=0.05" # PART b) @@ -22,6 +22,6 @@ o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS -o bkg --configKeyValue \ # of subsequent actions. In this case, the number of pythia8 events to be embedded is calculated according # to a formula that uses the number of primary particles of the background events NSGN=10 -o2-sim -j 20 -n ${NSGN} extgen -m PIPE ITS \ - -g extgen --extGenFile pythia8_adaptive.macro --extGenFunc "pythia8_adaptive(\"0.002 * x\")" \ +o2-sim -j 20 -n ${NSGN} -m PIPE ITS \ + -g external --configKeyValues 'GeneratorExternal.fileName=adaptive_pythia8.macro;GeneratorExternal.funcName=adaptive_pythia8("0.002 * x")' \ --embedIntoFile bkg_Kine.root -o sgn > logsgn 2>&1 diff --git a/run/SimExamples/AliRoot_AMPT/README.md b/run/SimExamples/AliRoot_AMPT/README.md new file mode 100644 index 0000000000000..b413f6eeffe11 --- /dev/null +++ b/run/SimExamples/AliRoot_AMPT/README.md @@ -0,0 +1,21 @@ +<!-- doxy +\page refrunSimExamplesAliRoot_AMPT Example AliRoot_AMPT +/doxy --> + +This is a complex simulation example showing how to run event simulation using the AMPT event generator interface from AliRoot. +A wrapper class AliRoot_AMPT is defined to keep the AliGenAmpt instance and configure it. +It also provides methods to set a random event plane before event generation and to update the event header. +The overall setup is steered by the function `ampt(double energy = 5020., double bMin = 0., double bMax = 20.)` defined in the macro `aliroot_ampt.macro`. + +The macro file is specified via `--configKeyValues` setting `GeneratorExternal.fileName` whereas the specific function call to retrieven the configuration is specified via `GeneratorExternal.funcName`. + +# IMPORTANT +To run this example you need to load an AliRoot package compatible with the O2. +for more details, https://alice.its.cern.ch/jira/browse/AOGM-246 + +AliRoot needs to be loaded **after** O2 in the following sense: +`alienv enter O2/latest,AliRoot/latest` +The other order may show unresolved symbol problems. + +# WARNING +The physics output of this simulation is not fully tested and validated. diff --git a/run/SimExamples/AliRoot_AMPT/aliroot_ampt.macro b/run/SimExamples/AliRoot_AMPT/aliroot_ampt.macro new file mode 100644 index 0000000000000..09d39f4378527 --- /dev/null +++ b/run/SimExamples/AliRoot_AMPT/aliroot_ampt.macro @@ -0,0 +1,102 @@ +// configures a AliGenAmpt class from AliRoot +// usage: o2sim -g external --configKeyValues 'GeneratorExternal.fileName=aliroot_ampt.macro;GeneratorExternal.funcName="aliroot_ampt(5020., 0., 20.)"' + +/// \author R+Preghenella - October 2018 + +R__LOAD_LIBRARY(libAMPT) +R__LOAD_LIBRARY(libTAmpt) + +class AliRoot_AMPT : public o2::eventgen::GeneratorTGenerator +{ + +public: + + AliRoot_AMPT() { + + // instance and configure AMPT + AliGenAmpt *genHi = new AliGenAmpt(-1); + genHi->SetEnergyCMS(5020.); + genHi->SetReferenceFrame("CMS"); + genHi->SetProjectile("A", 208, 82); + genHi->SetTarget("A", 208, 82); + genHi->SetIsoft(1); //1: defaul - 4: string melting + genHi->SetStringFrag(0.5, 0.9); //Lund string frgmentation parameters + genHi->SetPtHardMin(3); + genHi->SetImpactParameterRange(0., 20.); + + // Xmu = 3.2 fm^-1 and as = 0.33 ==> sigma_{partonic} = 1.5mb + // Ntmax = 150 + // v_2{2} = 0.102105 +/- 0.000266894 + // v_2{4} = 0.0829477 +/- 0.00106158 + + genHi->SetNtMax(150); // NTMAX: number of timesteps (D=150) + genHi->SetXmu(3.2264); // parton screening mass in fm^(-1) (D=3.2264d0) + + genHi->SetJetQuenching(0); // enable jet quenching + genHi->SetShadowing(1); // enable shadowing + // genHi->SetDecaysOff(1); // neutral pion and heavy particle decays switched off + genHi->SetSpectators(0); // track spectators + //Boost into LHC lab frame + genHi->SetBoostLHC(1); + // genHi->Init(); + genHi->SetRandomReactionPlane(kTRUE); + + + // save pointers to objects + mGenAMPT = genHi; + }; + + ~AliRoot_AMPT() { + delete mGenAMPT; + } + + bool Init() { + + // initialize AMPT + mGenAMPT->Init(); + + // configure TGenerator interface + setMomentumUnit(1.); // [GeV/c] + setEnergyUnit(1.); // [GeV/c] + setPositionUnit(0.1); // [cm] + setTimeUnit(3.3356410e-12); // [s] + setTGenerator(mGenAMPT->GetMC()); + + // initialise TGenerator interface + return o2::eventgen::GeneratorTGenerator::Init(); + } + + bool generateEvent() { + // set a random event plane + auto theAMPT = (TAmpt *)mGenAMPT->GetMC(); + TRandom *r = AliAmptRndm::GetAmptRandom(); + theAMPT->SetReactionPlaneAngle(TMath::TwoPi()*r->Rndm()); + + // generate the event + return o2::eventgen::GeneratorTGenerator::generateEvent(); + } + + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) { + auto theAMPT = (TAmpt *)mGenAMPT->GetMC(); + auto impactB = theAMPT->GetHINT1(19); + auto evPlane = theAMPT->GetReactionPlaneAngle(); + eventHeader->SetB(impactB); + eventHeader->SetRotX(0.); + eventHeader->SetRotY(0.); + eventHeader->SetRotZ(evPlane); + std::cout << " Updated the event header: impactB = " << impactB << ", evPlane = " << evPlane << std::endl; + }; + + AliGenAmpt *mGenAMPT = nullptr; +}; + + +FairGenerator* +ampt(double energy = 5020., double bMin = 0., double bMax = 20., int isoft = 1) +{ + auto gen = new AliRoot_AMPT(); + gen->mGenAMPT->SetEnergyCMS(energy); + gen->mGenAMPT->SetImpactParameterRange(bMin, bMax); + gen->mGenAMPT->SetIsoft(isoft); + return gen; +} diff --git a/run/SimExamples/AliRoot_AMPT/run.sh b/run/SimExamples/AliRoot_AMPT/run.sh new file mode 100755 index 0000000000000..04635cc35590a --- /dev/null +++ b/run/SimExamples/AliRoot_AMPT/run.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# +# Please, refer to the README.md for more information +# + +set -x + +NEV=5 +ENERGY=5020. +BMIN=15. +BMAX=20. +o2-sim -j 20 -n ${NEV} -g external -m PIPE ITS TPC -o sim \ + --configKeyValues "GeneratorExternal.fileName=aliroot_ampt.macro;GeneratorExternal.funcName=ampt(${ENERGY}, ${BMIN}, ${BMAX})" diff --git a/run/SimExamples/AliRoot_Hijing/README.md b/run/SimExamples/AliRoot_Hijing/README.md index 4b4477f021ecf..581c2659477d6 100644 --- a/run/SimExamples/AliRoot_Hijing/README.md +++ b/run/SimExamples/AliRoot_Hijing/README.md @@ -5,7 +5,7 @@ This is a simple simulation example showing how to run event simulation using the Hijing event generator interface from AliRoot. The configuration of AliGenHijing is performed by the function `hijing(double energy = 5020., double bMin = 0., double bMax = 20.)` defined in the macro `aliroot_hijing.macro`. -The macro file is specified via the argument of `--extGenFile` whereas the specific function call to retrieven the configuration is specified via the argument of `--extGenFunc`. +The macro file is specified via `--configKeyValues` setting `GeneratorExternal.fileName` whereas the specific function call to retrieven the configuration is specified via `GeneratorExternal.funcName`. # IMPORTANT To run this example you need to load an AliRoot package compatible with the O2. diff --git a/run/SimExamples/AliRoot_Hijing/aliroot_hijing.macro b/run/SimExamples/AliRoot_Hijing/aliroot_hijing.macro index 393a81b543cbf..cfd13ef384a80 100644 --- a/run/SimExamples/AliRoot_Hijing/aliroot_hijing.macro +++ b/run/SimExamples/AliRoot_Hijing/aliroot_hijing.macro @@ -1,6 +1,5 @@ // configures a AliGenHijing class from AliRoot -// usage: o2sim -g extgen --extGenFile hijing.C -// options: --extGenFunc hijing(5020., 0., 20.) +// usage: o2sim -g external --configKeyValues 'GeneratorExternal.fileName=hijing.C;GeneratorExternal.funcName="hijing(5020., 0., 20.)"' /// \author R+Preghenella - October 2018 diff --git a/run/SimExamples/AliRoot_Hijing/run.sh b/run/SimExamples/AliRoot_Hijing/run.sh index 2a99a81083a76..423f188e96bf7 100755 --- a/run/SimExamples/AliRoot_Hijing/run.sh +++ b/run/SimExamples/AliRoot_Hijing/run.sh @@ -7,9 +7,9 @@ # 'hijing(double energy = 5020., double bMin = 0., double bMax = 20.)' defined # in the macro 'aliroot_hijing.macro'. # -# The macro file is specified via the argument of '--extGenFile' +# The macro file is specified via '--configKeyValues' setting 'GeneratorExternal.fileName' # whereas the specific function call to retrieven the configuration -# is specified via the argument of '--extGenFunc' +# is specified via 'GeneratorExternal.funcName' # # IMPORTANT # To run this example you need to load an AliRoot package compatible with the O2. @@ -25,6 +25,5 @@ NEV=5 ENERGY=5020. BMIN=0 BMAX=20. -o2-sim -j 20 -n ${NEV} -g extgen -m PIPE ITS TPC -o sim \ - --extGenFile "aliroot_hijing.macro" --extGenFunc "hijing(${ENERGY}, ${BMIN}, ${BMAX})" \ - --configKeyValue "Diamond.position[2]=0.1;Diamond.width[2]=0.05" +o2-sim -j 20 -n ${NEV} -g external -m PIPE ITS TPC -o sim \ + --configKeyValues "GeneratorExternal.fileName=aliroot_hijing.macro;GeneratorExternal.funcName=hijing(${ENERGY}, ${BMIN}, ${BMAX})" diff --git a/run/SimExamples/Custom_EventInfo/generator.macro b/run/SimExamples/Custom_EventInfo/generator.macro new file mode 100644 index 0000000000000..62be63a47d9ad --- /dev/null +++ b/run/SimExamples/Custom_EventInfo/generator.macro @@ -0,0 +1,42 @@ +class Generator : public o2::eventgen::GeneratorPythia8 { + +public: + + Generator() = default; + ~Generator() = default; + + /** static functions that define the information + stored in the header (key and data type) + and provide a mean to retrieve it + **/ + + static void setXsection(o2::dataformats::MCEventHeader* head, double val) { head->putInfo<double>("Xsection", val); }; + static double getXsection(o2::dataformats::MCEventHeader* head, bool& isvalid) { return head->getInfo<double>("Xsection", isvalid); }; + + static void setXsectionErr(o2::dataformats::MCEventHeader* head, double val) { head->putInfo<double>("Xsection_err", val); }; + static double getXsectionErr(o2::dataformats::MCEventHeader* head, bool& isvalid) { return head->getInfo<double>("Xsection_err", isvalid); }; + + static void setNmpi(o2::dataformats::MCEventHeader* head, int val) { head->putInfo<int>("Nmpi", val); }; + static int getNmpi(o2::dataformats::MCEventHeader* head, bool& isvalid) { return head->getInfo<int>("Nmpi", isvalid); }; + +private: + + void updateHeader(o2::dataformats::MCEventHeader* head) final { + + // call original function + o2::eventgen::GeneratorPythia8::updateHeader(head); + + // store cross-section and Nmpi + setXsection(head, mPythia.info.sigmaGen()); + setXsectionErr(head, mPythia.info.sigmaErr()); + setNmpi(head, mPythia.info.nMPI()); + + }; + +}; + +FairGenerator* +generator() +{ + return new Generator; +} diff --git a/run/SimExamples/Custom_EventInfo/pythia8_inel.cfg b/run/SimExamples/Custom_EventInfo/pythia8_inel.cfg new file mode 100644 index 0000000000000..fdea3cc091984 --- /dev/null +++ b/run/SimExamples/Custom_EventInfo/pythia8_inel.cfg @@ -0,0 +1,11 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 14000. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 0.001 diff --git a/run/SimExamples/Custom_EventInfo/read_event_info.macro b/run/SimExamples/Custom_EventInfo/read_event_info.macro new file mode 100644 index 0000000000000..bc2378e58ca0f --- /dev/null +++ b/run/SimExamples/Custom_EventInfo/read_event_info.macro @@ -0,0 +1,31 @@ +#include "generator.macro" + +void +read_event_info(const char *fname) +{ + + auto fin = TFile::Open(fname); + auto tin = (TTree*)fin->Get("o2sim"); + auto head = new o2::dataformats::MCEventHeader; + tin->SetBranchAddress("MCEventHeader.", &head); + + bool isvalid; + + for (int iev = 0; iev < tin->GetEntries(); ++iev) { + + tin->GetEntry(iev); + + std::cout << " ---------------" << std::endl; + auto Xsection = Generator::getXsection(head, isvalid); + if (isvalid) + std::cout << " Xsection = " << Xsection << std::endl; + auto XsectionErr = Generator::getXsectionErr(head, isvalid); + if (isvalid) + std::cout << " XsectionErr = " << XsectionErr << std::endl; + auto Nmpi = Generator::getNmpi(head, isvalid); + if (isvalid) + std::cout << " Nmpi = " << Nmpi << std::endl; + + } + +} diff --git a/run/SimExamples/Custom_EventInfo/run.sh b/run/SimExamples/Custom_EventInfo/run.sh new file mode 100755 index 0000000000000..77f14f8d7e3f0 --- /dev/null +++ b/run/SimExamples/Custom_EventInfo/run.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# This is a simulation example showing how to run simulation with Pythia8 +# with an external generator that adds custom information to the event header +# +# + +set -x + +MODULES="PIPE ITS TPC" +EVENTS=100 +NWORKERS=8 + +### generate some events with the external generator that will +### provide some custom information in the event header + +o2-sim -j ${NWORKERS} -n ${EVENTS} -g external -m ${MODULES} \ + --configFile sim.ini > log 2>&1 + +### read the kinematics to print the custom information stored by +### the external generator that we ran before + +root -b -q -l "read_event_info.macro(\"o2sim_Kine.root\")" diff --git a/run/SimExamples/Custom_EventInfo/sim.ini b/run/SimExamples/Custom_EventInfo/sim.ini new file mode 100644 index 0000000000000..708adffd2e11b --- /dev/null +++ b/run/SimExamples/Custom_EventInfo/sim.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=generator.macro +funcName=generator() + +[GeneratorPythia8] +config=pythia8_inel.cfg diff --git a/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/README.md b/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/README.md index 607a745c28fbe..edbead91dac14 100644 --- a/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/README.md +++ b/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/README.md @@ -19,10 +19,11 @@ On top of that we have to setup the external decayer to perform the decay forcin The default external decayer configuration is loaded from ``` ${O2_ROOT}/share/Generators/pythia8/decays/base.cfg +which is assigned to the slot #0 of the configuration parameter 'DecayerPythia8.config'. ``` What we want to do is to add on top of the default configuration some extra settings that are provided in the `decay_lambda_neutron_dalitz.cfg` file. This can be done with ``` ---configKeyValues 'DecayerPythia8.config=${O2_ROOT}/share/Generators/pythia8/decays/base.cfg decay_lambda_neutron_dalitz.cfg' +--configKeyValues 'DecayerPythia8.config[1]=decay_lambda_neutron_dalitz.cfg' ``` diff --git a/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/run.sh b/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/run.sh index 95a916080cc61..5185e6625672f 100755 --- a/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/run.sh +++ b/run/SimExamples/ForceDecay_Lambda_Neutron_Dalitz/run.sh @@ -14,13 +14,14 @@ # On top of that we have to setup the external decayer to perform the decay forcing the channels we desire. # The default external decayer configuration is loaded from # ${O2_ROOT}/share/Generators/pythia8/decays/base.cfg +# which is assigned to the slot #0 of the configuration parameter 'DecayerPythia8.config'. # # What we want to do is to add on top of the default configuration some extra settings that are provided in the `decay_lambda_neutron_dalitz.cfg` file. # This can be done with -# --configKeyValues 'DecayerPythia8.config=${O2_ROOT}/share/Generators/pythia8/decays/base.cfg decay_lambda_neutron_dalitz.cfg' +# --configKeyValues 'DecayerPythia8.config[1]=decay_lambda_neutron_dalitz.cfg' # set -x NEV=10 -o2-sim -j 20 -n ${NEV} -g boxgen -m PIPE ITS TPC --configKeyValues 'BoxGun.pdg=3122;BoxGun.number=1;SimUserDecay.pdglist=3122 111;DecayerPythia8.config=${O2_ROOT}/share/Generators/pythia8/decays/base.cfg decay_lambda_neutron_dalitz.cfg;DecayerPythia8.verbose=true' +o2-sim -j 20 -n ${NEV} -g boxgen -m PIPE ITS TPC --configKeyValues 'BoxGun.pdg=3122;BoxGun.number=1;SimUserDecay.pdglist=3122 111;DecayerPythia8.config[1]=decay_lambda_neutron_dalitz.cfg;DecayerPythia8.verbose=true' diff --git a/run/SimExamples/HF_Embedding_Pythia8/GeneratorHF.macro b/run/SimExamples/HF_Embedding_Pythia8/GeneratorHF.macro new file mode 100644 index 0000000000000..7c6674deb0de5 --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/GeneratorHF.macro @@ -0,0 +1,118 @@ +/// \author R+Preghenella - July 2020 + +// Example of an implementation of an event generator +// that provides HF signals for embedding in background + +#include "Pythia8/Pythia.h" + +namespace o2 { +namespace eventgen { + +class GeneratorHF : public GeneratorPythia8 +{ + +public: + + GeneratorHF() : GeneratorPythia8() { }; + ~GeneratorHF() = default; + + // We initialise the local Pythia8 event where we store the particles + // of the signal event that is the sum of multiple Pythia8 events + // generated according to the generateEvent() function below. + Bool_t Init() override { + mOutputEvent.init("(GeneratorHF output event)", &mPythia.particleData); + return GeneratorPythia8::Init(); + } + + // This function is called by the primary generator + // for each event in case we are in embedding mode. + // We use it to setup the number of signal events + // to be generated and to be embedded on the background. + void notifyEmbedding(const o2::dataformats::MCEventHeader *bkgHeader) override { + mEvents = mFormula.Eval(bkgHeader->GetB()); + std::cout << " --- notify embedding: impact parameter is " << bkgHeader->GetB() << ", generating " << mEvents << " signal events " << std::endl; + }; + + // We override this function to be able to generate multiple + // events and build an output event that is the sum of them + // where we have stripped out only the sub-event starting from + // the c-cbar ancestor particle + Bool_t generateEvent() override { + + // reset counter and event + mOutputEvent.reset(); + + // loop over number of events to be generated + int nEvents = 0; + while (nEvents < mEvents) { + + // generate event + if (!GeneratorPythia8::generateEvent()) return false; + + // find the c-cbar ancestor + auto ancestor = findAncestor(mPythia.event); + if (ancestor < 0) continue; + + // append ancestor and its daughters to the output event + selectFromAncestor(ancestor, mPythia.event, mOutputEvent); + nEvents++; + } + + if (mVerbose) mOutputEvent.list(); + + return true; + }; + + // We override this event to import the particles from the + // output event that we have constructed as the sum of multiple + // Pythia8 sub-events as generated above + Bool_t importParticles() override { + return GeneratorPythia8::importParticles(mOutputEvent); + } + + // search for c-cbar mother with at least one c at midrapidity + int findAncestor(Pythia8::Event& event) { + for (int ipa = 0; ipa < event.size(); ++ipa) { + auto daughterList = event[ipa].daughterList(); + bool hasc = false, hascbar = false, atmidy = false; + for (auto ida : daughterList) { + if (event[ida].id() == 4) hasc = true; + if (event[ida].id() == -4) hascbar = true; + if (fabs(event[ida].y()) < mRapidity) atmidy = true; + } + if (hasc && hascbar && atmidy) + return ipa; + } + return -1; + }; + + void setRapidity(double val) { mRapidity = val; }; + void setVerbose(bool val) { mVerbose = val; }; + void setFormula(std::string val) { mFormula.Compile(val.c_str()); }; + +private: + + TFormula mFormula; + int mEvents = 1; + Pythia8::Event mOutputEvent; + double mRapidity = 1.5; + bool mVerbose = false; + +}; + +}} + +/** generator instance and settings **/ + +FairGenerator* +GeneratorHF(double rapidity = 1.5, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorHF(); + gen->setRapidity(rapidity); + gen->setVerbose(verbose); + gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); + + return gen; +} + + diff --git a/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D.cfg b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D.cfg new file mode 100644 index 0000000000000..e51167aae4398 --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D.cfg @@ -0,0 +1,96 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file reproduces the configuration achieved with ForceHadronicD(0,0,0) + +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.00276 0 333 211 +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 3124 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +4122:addChannel = 1 0.0159 0 2212 311 +4122:addChannel = 1 0.0130 0 3122 211 +### add Xic+ decays absent in PYTHIA8 decay table +4232:addChannel = 1 0.2 0 2212 -313 +4232:addChannel = 1 0.2 0 2212 -321 211 +4232:addChannel = 1 0.2 0 3324 211 +4232:addChannel = 1 0.2 0 3312 211 211 +### add Xic0 decays absent in PYTHIA8 decay table +4132:addChannel = 1 0.2 0 3312 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +3124:onMode = off +3124:onIfAll = 2212 321 + +### Omega_c -> Omega pi +4332:onMode = off +4332:onIfMatch = 3334 211 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4112:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D0 -> K pi +421:onIfMatch = 321 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 3124 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 +### Lambda_c -> Lambda pi +4122:onIfMatch = 3122 211 +### Lambda_c -> p K0 +4122:onIfMatch = 2212 311 + +### Xic+ -> pK*0 +4232:onIfMatch = 2212 313 +### Xic+ -> p K- pi+ +4232:onIfMatch = 2212 321 211 +### Xic+ -> Xi*0 pi+, Xi*->Xi- pi+ +4232:onIfMatch = 3324 211 +### Xic+ -> Xi- pi+ pi+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 diff --git a/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_forceLcChannel1.cfg b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_forceLcChannel1.cfg new file mode 100644 index 0000000000000..33abbb1b6f340 --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_forceLcChannel1.cfg @@ -0,0 +1,12 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (force Lc channel 1) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(0,0,1) + +### force Lc -> p K pi +4122:onMode = off +4122:onIfMatch = 2212 321 211 diff --git a/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_forceLcChannel2.cfg b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_forceLcChannel2.cfg new file mode 100644 index 0000000000000..d79dec4d8b88f --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_forceLcChannel2.cfg @@ -0,0 +1,12 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (force Lc channel 2) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(0,0,2) + +### force Lc -> p K0s +4122:onMode = off +4122:onIfMatch = 2212 311 diff --git a/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_use4bodies.cfg b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_use4bodies.cfg new file mode 100644 index 0000000000000..3178dad7d368b --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_use4bodies.cfg @@ -0,0 +1,15 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (use 4 bodies option) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(1,0,0) + +### D0 -> K pi pi pi +421:onIfMatch = 321 211 211 211 +### D0 -> K pi rho +421:onIfMatch = 321 211 113 +### D0 -> K*0 pi pi +421:onIfMatch = 313 211 211 diff --git a/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_useDtoV0.cfg b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_useDtoV0.cfg new file mode 100644 index 0000000000000..5b50c59e0130c --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/decays/force_hadronic_D_useDtoV0.cfg @@ -0,0 +1,13 @@ +### author: Roberto Preghenella (preghenella@bo.infn.it) +### since: July 2020 + +### Force golden D decay modes (use D to V0 option) +### the origin is from AliRoot AliDecayerPythia8::ForceHadronicD (Latest commit c509466 on May 17) +### +### This file has to be used in conjunction with force_hadronic_D.cfg and loaded +### afterwards to reproduce the configuration achieved with ForceHadronicD(0,1,0) + +### Ds -> K0K +431:onIfMatch = 311 321 +### Ds -> K0pi +411:onIfMatch = 311 211 diff --git a/run/SimExamples/HF_Embedding_Pythia8/o2sim_configuration_bkg.ini b/run/SimExamples/HF_Embedding_Pythia8/o2sim_configuration_bkg.ini new file mode 100644 index 0000000000000..5242ab6ef7e1d --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/o2sim_configuration_bkg.ini @@ -0,0 +1,8 @@ +[Diamond] +position[0]=0.0 +position[1]=0.0 +position[2]=0.1 + +width[0]=0.05 +width[1]=0.05 +width[2]=6.0 diff --git a/run/SimExamples/HF_Embedding_Pythia8/o2sim_configuration_sgn.ini b/run/SimExamples/HF_Embedding_Pythia8/o2sim_configuration_sgn.ini new file mode 100644 index 0000000000000..d0f74533c1386 --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/o2sim_configuration_sgn.ini @@ -0,0 +1,12 @@ +[GeneratorPythia8] +config = pythia8.cfg +hooksFileName = pythia8_userhooks_ccbar.macro +hooksFuncName = pythia8_userhooks_ccbar(1.5) + +[DecayerPythia8] +config[0] = ${O2_ROOT}/share/Generators/pythia8/decays/base.cfg +config[1] = decays/force_hadronic_D.cfg +config[2] = decays/force_hadronic_D_use4bodies.cfg + +[SimUserDecay] +pdglist = 411 421 431 4112 4122 4232 4132 diff --git a/run/SimExamples/HF_Embedding_Pythia8/pythia8_ccbar.template b/run/SimExamples/HF_Embedding_Pythia8/pythia8_ccbar.template new file mode 100644 index 0000000000000..70c45acc4299b --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/pythia8_ccbar.template @@ -0,0 +1,19 @@ +### random seed +Random:setSeed on +Random:seed ${rndSeed} + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 14000. # GeV + +### processes +HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar + +### phase space cuts +PhaseSpace:pTHatMin ${pTHatMin} # the minimum invariant pT +PhaseSpace:pTHatMax ${pTHatMax} # the maximum invariant pT + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 0.001 diff --git a/run/SimExamples/HF_Embedding_Pythia8/pythia8_userhooks_ccbar.macro b/run/SimExamples/HF_Embedding_Pythia8/pythia8_userhooks_ccbar.macro new file mode 100644 index 0000000000000..6cba98017684e --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/pythia8_userhooks_ccbar.macro @@ -0,0 +1,47 @@ +/// \author R+Preghenella - July 2020 + +/// This Pythia8 UserHooks can veto the processing at parton level. +/// The partonic event is scanned searching for a c-cbar mother +/// with at least one of the c quarks produced withing a fiducial +/// window around midrapidity that can be specified by the user. + +#include "Pythia8/Pythia.h" + +class UserHooks_ccbar : public Pythia8::UserHooks +{ + + public: + UserHooks_ccbar() = default; + ~UserHooks_ccbar() = default; + bool canVetoPartonLevel() override { return true; }; + bool doVetoPartonLevel(const Pythia8::Event& event) override { + // search for c-cbar mother with at least one c at midrapidity + for (int ipa = 0; ipa < event.size(); ++ipa) { + auto daughterList = event[ipa].daughterList(); + bool hasc = false, hascbar = false, atmidy = false; + for (auto ida : daughterList) { + if (event[ida].id() == 4) hasc = true; + if (event[ida].id() == -4) hascbar = true; + if (fabs(event[ida].y()) < mRapidity) atmidy = true; + } + if (hasc && hascbar && atmidy) + return false; // found it, do not veto event + } + return true; // did not find it, veto event + }; + + void setRapidity(double val) { mRapidity = val; }; + +private: + + double mRapidity = 1.5; + +}; + +Pythia8::UserHooks* + pythia8_userhooks_ccbar(double rapidity = 1.5) +{ + auto hooks = new UserHooks_ccbar(); + hooks->setRapidity(rapidity); + return hooks; +} diff --git a/run/SimExamples/HF_Embedding_Pythia8/run.sh b/run/SimExamples/HF_Embedding_Pythia8/run.sh new file mode 100755 index 0000000000000..1ff92a4e5d08c --- /dev/null +++ b/run/SimExamples/HF_Embedding_Pythia8/run.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# This is a simulation example showing the following things +# +# a) how to run a simple background event simulation with some parameter customization +# b) how to generate a pythia8 configuration file replacing values from a template configuration +# c) how to run a simulation producing signal events using several specific pythia8 settings and user hooks +# d) how to run digitization embedding/overlaying background and signal hits +# +# The main focus in this example is on PART c). PART b) just relaces some template values in the Pythia8 configuration file. +# +# An external event generator configuration `-g external` is loaded from the file defined via `--configKeyValues` setting +# `GeneratorExternal.fileName=GeneratorHF.macro` by running the code defined in the function `GeneratorExternal.funcName="GeneratorHF()"`. +# Special configuration parameters are loaded from the INI file `--configFile o2sim_configuration_sgn.ini`. +# +# GeneratorPythia8.config defines the Pythia8 configuration file name. +# +# We configured to bias towards c-cbar processes where we can select them baed on pt-hat bins. +# +# GeneratorPythia8.hooksFileName defines the file name where to load the custom Pythia8 hooks +# GeneratorPythia8.hooskFuncName defines the function call to be run to retrieve the custom Pythia8 hools. +# +# Hooks are used in this example to speedup the event generation. Event generation is paused at parton level. +# We check if there are the partons of our interest, if not we veto the event. This saves time because we +# do not have to process the full hadronisation step of uninteresting events. +# Look inside `pythia8_userhooks_ccbar.macro` for details on how this is implemented +# +# DecayerPythia8.config[0], [1] and [2] define several configuration files for the external decayer. +# The files are loaded in series. Look inside the corresponding `.cfg` for details. +# +# SimUserDecay.pdglist instructs Geant not to process the decays, but to hand them to the external decayer. +# + +set -x + +MODULES="PIPE ITS TPC" +BKGEVENTS=5 +SIGEVENTS=20 +NWORKERS=8 + +# PART a) + +o2-sim -j ${NWORKERS} -n ${BKGEVENTS} -g pythia8hi -m ${MODULES} -o bkg \ + --configFile o2sim_configuration_bkg.ini \ + > logbkg 2>&1 + +# PART b) + +RNDSEED=0 # [default = 0] time-based random seed +PTHATMIN=0. # [default = 0] +PTHATMAX=-1. # [default = -1] + +sed -e "s/\${rndSeed}/$RNDSEED/" \ + -e "s/\${pTHatMin}/$PTHATMIN/" \ + -e "s/\${pTHatMax}/$PTHATMAX/" \ + pythia8_ccbar.template > pythia8.cfg + +# PART c) + +o2-sim -j ${NWORKERS} -n ${SIGEVENTS} -g external -m ${MODULES} -o sgn \ + --configKeyValues "GeneratorExternal.fileName=GeneratorHF.macro;GeneratorExternal.funcName=GeneratorHF()" \ + --configFile o2sim_configuration_sgn.ini \ + --embedIntoFile bkg_Kine.root \ + > logsgn 2>&1 + +# PART d) + +o2-sim-digitizer-workflow --sims bkg,sgn --tpc-lanes 4 -b --run diff --git a/run/SimExamples/HepMC_STARlight/run.sh b/run/SimExamples/HepMC_STARlight/run.sh index 5cc7e5ad477b3..b75cee52b7117 100755 --- a/run/SimExamples/HepMC_STARlight/run.sh +++ b/run/SimExamples/HepMC_STARlight/run.sh @@ -15,5 +15,4 @@ env -i HOME="$HOME" USER="$USER" PATH="/bin:/usr/bin:/usr/local/bin" \ # PART b) NEV=1000 o2-sim -j 20 -n ${NEV} -g hepmc -m PIPE ITS -o sim \ - --HepMCFile starlight.hepmc \ - --configKeyValue "Diamond.position[2]=0.1;Diamond.width[2]=0.05" + --configKeyValues "HepMC.fileName=starlight.hepmc;HepMC.version=2;Diamond.position[2]=0.1;Diamond.width[2]=0.05" diff --git a/run/SimExamples/Jet_Embedding_Pythia8/pythia8_userhooks_jets.macro b/run/SimExamples/Jet_Embedding_Pythia8/pythia8_userhooks_jets.macro index 16bdede325310..9986f65e3e5df 100644 --- a/run/SimExamples/Jet_Embedding_Pythia8/pythia8_userhooks_jets.macro +++ b/run/SimExamples/Jet_Embedding_Pythia8/pythia8_userhooks_jets.macro @@ -1,6 +1,6 @@ // Pythia8 UserHooks // -// usage: o2sim -g pythia8 --configKeyValue "Pythia8.hooksFileName=pythia8_userhooks_jets.C" +// usage: o2sim -g pythia8 --configKeyValues "GeneratorPythia8.hooksFileName=pythia8_userhooks_jets.C" // /// \author R+Preghenella - April 2020 diff --git a/run/SimExamples/Jet_Embedding_Pythia8/run.sh b/run/SimExamples/Jet_Embedding_Pythia8/run.sh index db1bc07c028fe..e063ee2a1b872 100755 --- a/run/SimExamples/Jet_Embedding_Pythia8/run.sh +++ b/run/SimExamples/Jet_Embedding_Pythia8/run.sh @@ -12,14 +12,14 @@ set -x # PART a) NBGR=5 -o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS TPC -o bkg --configKeyValue \ +o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS TPC -o bkg --configKeyValues \ "Diamond.position[2]=0.1;Diamond.width[2]=0.05" # PART b) # produce hard jets using a pythia8 configuration given in a file 'pythia8_hard.cfg'; event selection is done by a user hook specified # in file 'pythia8_userhooks_jets.macro' and using same vertex setting as background events (via --embedInto) NSGN=10 -o2-sim -j 20 -n ${NSGN} -g pythia8 -m PIPE ITS TPC --configKeyValue "Pythia8.config=pythia8_hard.cfg;Pythia8.hooksFileName=pythia8_userhooks_jets.macro" --embedIntoFile bkg_Kine.root -o sgn > logsgn 2>&1 +o2-sim -j 20 -n ${NSGN} -g pythia8 -m PIPE ITS TPC --configKeyValues "GeneratorPythia8.config=pythia8_hard.cfg;GeneratorPythia8.hooksFileName=pythia8_userhooks_jets.macro" --embedIntoFile bkg_Kine.root -o sgn > logsgn 2>&1 # PART c) # digitization with summation of signal on top of background events diff --git a/run/SimExamples/JustPrimaryKinematics/only_primarykine.ini b/run/SimExamples/JustPrimaryKinematics/only_primarykine.ini new file mode 100644 index 0000000000000..43daa9f866da4 --- /dev/null +++ b/run/SimExamples/JustPrimaryKinematics/only_primarykine.ini @@ -0,0 +1,20 @@ +[SimCutParams] +maxRTracking=2 +maxAbsZTracking=2 +ZmaxA=2 +ZmaxC=2 +[GlobalSimProcs] +PAIR=0 +COMP=0 +PHOT=0 +PFIS=0 +DRAY=0 +ANNI=0 +BREM=0 +HADR=0 +MUNU=0 +DCAY=0 +LOSS=0 +MULS=0 +CKOV=0 + diff --git a/run/SimExamples/JustPrimaryKinematics/run.sh b/run/SimExamples/JustPrimaryKinematics/run.sh new file mode 100755 index 0000000000000..018df38f10976 --- /dev/null +++ b/run/SimExamples/JustPrimaryKinematics/run.sh @@ -0,0 +1,14 @@ +# A very simple example, showing how to configure o2-sim to skip +# transport/physics and save just the originally generated event. +# While this can be achieved in potentially many ways, the example is instructive +# to demonstrate the configuration mechanism in general. +# Here the mechanism is to switch off physics in the configuration file and set very +# tight geometry cuts so that Geant will not do work. + +# first stage --> produce events using Pythia8 (no transport) +o2-sim -n 10 -g pythia8 -m CAVE --configFile only_primarykine.ini + +# second stage --> read back events from O2 kine (no transport) +o2-sim -n 10 -g extkinO2 --extKinFile o2sim_Kine.root -m CAVE --configFile only_primarykine.ini -o o2sim2 + +# ideally here, both kinematics files should be identical diff --git a/run/SimExamples/README.md b/run/SimExamples/README.md index 8802f6e6411b7..7423842b46d16 100644 --- a/run/SimExamples/README.md +++ b/run/SimExamples/README.md @@ -5,12 +5,17 @@ # Simulation Examples <!-- doxy +* \subpage refrunSimExamplesHF_Embedding_Pythia8 * \subpage refrunSimExamplesSignal_ImpactB * \subpage refrunSimExamplesTrigger_ImpactB_Pythia8 * \subpage refrunSimExamplesAdaptive_Pythia8 * \subpage refrunSimExamplesAliRoot_Hijing +* \subpage refrunSimExamplesAliRoot_AMPT * \subpage refrunSimExamplesHepMC_STARlight * \subpage refrunSimExamplesJet_Embedding_Pythia8 * \subpage refrunSimExamplesStepMonitoringSimple1 * \subpage refrunSimExamplesForceDecay_Lambda_Neutron_Dalitz +* \subpage refrunSimExamplesJustPrimaryKinematics +* \subpage refrunSimExamplesSelective_Transport +* \subpage refrunSimExamplesCustom_EventInfo /doxy --> diff --git a/run/SimExamples/Selective_Transport/run.sh b/run/SimExamples/Selective_Transport/run.sh new file mode 100755 index 0000000000000..ab1e51310bb1b --- /dev/null +++ b/run/SimExamples/Selective_Transport/run.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# This is a simulation example showing how to run simulation and selectively +# transport particles through the detector geometry according to configurable settings +# +# The simulation uses Pythia8 pp generator with default settings. +# The specific settings of the simulation are defined in the sim.ini file. +# +# Only the parameters for the selective particle transport are adjusted, namely +# ``` +# [Stack] +# transportPrimary=external +# transportPrimaryFileName=transportPDG.macro +# transportPrimaryFuncName=transportPDG(321) +# transportPrimaryInvert=false +# ``` +# +# `transportPrimary` defines the mode of selection, `external` means that the definition is loaded from a plugin macro +# defined by the function call `transportPrimaryFuncName` from the file `transportPrimaryFileName`. +# `transportPrimaryInvert` allows one to invert the definition, hence to inhibit transport for the selected particles. +# + +set -x + +MODULES="PIPE ITS TPC" +EVENTS=100 +NWORKERS=8 + +o2-sim -j ${NWORKERS} -n ${EVENTS} -g pythia8 -m ${MODULES} -o step1 \ + --configFile sim.ini \ + > logstep1 2>&1 + diff --git a/run/SimExamples/Selective_Transport/sim.ini b/run/SimExamples/Selective_Transport/sim.ini new file mode 100644 index 0000000000000..c445eca8c3098 --- /dev/null +++ b/run/SimExamples/Selective_Transport/sim.ini @@ -0,0 +1,5 @@ +[Stack] +transportPrimary=external +transportPrimaryFileName=transportPDG.macro +transportPrimaryFuncName=transportPDG(321) +transportPrimaryInvert=false diff --git a/run/SimExamples/Selective_Transport/transportPDG.macro b/run/SimExamples/Selective_Transport/transportPDG.macro new file mode 100644 index 0000000000000..942d3db4fe43f --- /dev/null +++ b/run/SimExamples/Selective_Transport/transportPDG.macro @@ -0,0 +1,7 @@ +o2::data::Stack::TransportFcn +transportPDG(int pdg = 321) +{ + return [pdg](const TParticle& p, const std::vector<TParticle>& particles) -> bool { + return p.GetPdgCode() == pdg; + }; +} diff --git a/run/SimExamples/Signal_ImpactB/README.md b/run/SimExamples/Signal_ImpactB/README.md index 539b64acec41d..8230d35241fc9 100644 --- a/run/SimExamples/Signal_ImpactB/README.md +++ b/run/SimExamples/Signal_ImpactB/README.md @@ -11,6 +11,6 @@ Custom signal events generated according to the configuration given in a file 's The custom event generator receives and react to a notification that signals the embedding status of the simulation, giving the header of the background event for determination of subsequent actions. In this case, the impact paramereter from the background event is used to calculate the number of particles to be generated as signal -The macro file is specified via the argument of `--extGenFile` whereas the specific function call to retrieve the configuration and define the formula is specified via the argument of `--extGenFunc`. +The macro file is specified via `--configKeyValues` setting `GeneratorExternal.funcName` whereas the specific function call to retrieve the configuration and define the formula is specified via `GeneratorExternal.funcName`. The event generator for the background embedding needs to be capable of providing an impact parameter. In this case, Pythia8 heavy-ion model provides such value. diff --git a/run/SimExamples/Signal_ImpactB/run.sh b/run/SimExamples/Signal_ImpactB/run.sh index e6d8a54cd29f7..f82547faf20aa 100755 --- a/run/SimExamples/Signal_ImpactB/run.sh +++ b/run/SimExamples/Signal_ImpactB/run.sh @@ -11,7 +11,7 @@ set -x # PART a) NBGR=5 -o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS -o bkg --configKeyValue \ +o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS -o bkg --configKeyValues \ "Diamond.position[2]=0.1;Diamond.width[2]=0.05" # PART b) @@ -21,6 +21,6 @@ o2-sim -j 20 -n ${NBGR} -g pythia8hi -m PIPE ITS -o bkg --configKeyValue \ # of subsequent actions. In this case, the impact paramereter from the backgorund event # is used to calculate the number of particles to be generated as signal NSGN=10 -o2-sim -j 20 -n ${NSGN} extgen -m PIPE ITS \ - -g extgen --extGenFile signal_impactb.macro --extGenFunc "signal_impactb(333, \"20. / (x + 1.)\")" \ +o2-sim -j 20 -n ${NSGN} -m PIPE ITS \ + -g external --configKeyValues 'GeneratorExternal.fileName=signal_impactb.macro;GeneratorExternal.funcName=signal_impactb(333, "20. / (x + 1.)")' \ --embedIntoFile bkg_Kine.root -o sgn > logsgn 2>&1 diff --git a/run/SimExamples/Signal_ImpactB/signal_impactb.macro b/run/SimExamples/Signal_ImpactB/signal_impactb.macro index ccc07451b2dcd..902ca7b84bf1d 100644 --- a/run/SimExamples/Signal_ImpactB/signal_impactb.macro +++ b/run/SimExamples/Signal_ImpactB/signal_impactb.macro @@ -3,8 +3,7 @@ // Example of an implementation of an external event generator // that is used and adapts it behavior in an embedding scenario. // -// usage: o2sim -g extgen --extGenFile signal_impactb.C -// options: --extGenFunc "signal_impactb(\"20. / (x + 1.)\") +// usage: o2sim -g external --configKeyValues 'GeneratorExternal.fileName=signal_impactb.C;GeneratorExternal.funcName="signal_impactb(\"20. / (x + 1.)\")"' using namespace o2::eventgen; @@ -29,7 +28,7 @@ public: // update the number of particles to be generated // according to impact parameter and formula - void notifyEmbedding(const FairMCEventHeader *bkgHeader) override { + void notifyEmbedding(const o2::dataformats::MCEventHeader *bkgHeader) override { auto b = bkgHeader->GetB(); mNSignals = mFormula.Eval(b); std::cout << " --- notify embedding: b = " << b << ", nparticles = " << mNSignals << std::endl; diff --git a/run/SimExamples/Trigger_ImpactB_Pythia8/README.md b/run/SimExamples/Trigger_ImpactB_Pythia8/README.md index b72f2597ce9cb..3137c51b367d2 100644 --- a/run/SimExamples/Trigger_ImpactB_Pythia8/README.md +++ b/run/SimExamples/Trigger_ImpactB_Pythia8/README.md @@ -9,7 +9,7 @@ The trigger selection defines impact parameters to be accepted according to a mi The definition and configuration of the external `DeepTrigger` is performed by the function `trigger_impactb_pythia8(double bMin = 0., double bMax = 20.)` defined in the macro `trigger_impactb_pythia8.macro`. -The macro file is specified via the argument of `--extTrgFile` whereas the specific function call to retrieve the configuration is specified via the argument of `--extTrgFunc`. +The macro file and function names are specified via `--configKeyValues` setting `TriggerExternal.fileName` and `TriggerExternal.funcName`, respectively. # WARNING Using a trigger is not always the most efficient way to deal with event generation. diff --git a/run/SimExamples/Trigger_ImpactB_Pythia8/run.sh b/run/SimExamples/Trigger_ImpactB_Pythia8/run.sh index 81f390fdda95c..924e8e2d0ed2b 100755 --- a/run/SimExamples/Trigger_ImpactB_Pythia8/run.sh +++ b/run/SimExamples/Trigger_ImpactB_Pythia8/run.sh @@ -14,12 +14,12 @@ # the function `trigger_impactb_pythia8(double bMin = 0., double bMax = 20.)` # defined in the macro `trigger_impactb_pythia8.macro`. -# The macro file is specified via the argument of `--extTrgFile` whereas the specific -# function call to retrieve the configuration is specified via the argument of `--extTrgFunc`. +# The macro file and function names are specified via `--configKeyValues` setting +# `TriggerExternal.fileName` and `TriggerExternal.funcName`, respectively. set -x NEV=10 BMIN=15. BMAX=20. -o2-sim -j 20 -n ${NEV} -g pythia8hi -m PIPE ITS TPC --trigger external --extTrgFile trigger_impactb_pythia8.macro --extTrgFunc "trigger_impactb_pythia8(${BMIN}, ${BMAX})" +o2-sim -j 20 -n ${NEV} -g pythia8hi -m PIPE ITS TPC --trigger external --configKeyValues "TriggerExternal.fileName=trigger_impactb_pythia8.macro;TriggerExternal.funcName=trigger_impactb_pythia8(${BMIN}, ${BMAX})" diff --git a/run/checkStack.cxx b/run/checkStack.cxx index 42c3dd0c53900..79da689a4eeb4 100644 --- a/run/checkStack.cxx +++ b/run/checkStack.cxx @@ -15,6 +15,7 @@ #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/Stack.h" #include "SimulationDataFormat/TrackReference.h" +#include "Steer/MCKinematicsReader.h" #include "TFile.h" #include "TTree.h" #ifdef NDEBUG @@ -46,16 +47,13 @@ int main(int argc, char** argv) assert(refbr); refbr->SetAddress(&trackrefs); - o2::dataformats::MCTruthContainer<o2::TrackReference>* indexedtrackrefs = nullptr; - auto irefbr = tr->GetBranch("IndexedTrackRefs"); - irefbr->SetAddress(&indexedtrackrefs); + o2::steer::MCKinematicsReader mcreader(nameprefix, o2::steer::MCKinematicsReader::Mode::kMCKine); - for (int i = 0; i < mcbr->GetEntries(); ++i) { - mcbr->GetEntry(i); - refbr->GetEntry(i); - irefbr->GetEntry(i); - LOG(DEBUG) << "-- Entry --" << i << "\n"; - LOG(DEBUG) << "Have " << mctracks->size() << " tracks \n"; + for (int eventID = 0; eventID < mcbr->GetEntries(); ++eventID) { + mcbr->GetEntry(eventID); + refbr->GetEntry(eventID); + LOG(DEBUG) << "-- Entry --" << eventID; + LOG(DEBUG) << "Have " << mctracks->size() << " tracks"; int ti = 0; // record tracks that left a hit in TPC @@ -68,24 +66,22 @@ int main(int argc, char** argv) if (t.leftTrace(o2::detectors::DetID::TPC)) { trackidsinTPC.emplace_back(ti); } - LOG(DEBUG) << " track " << ti << "\t" << t.getMotherTrackId() << " hits " << t.hasHits() << "\n"; + LOG(DEBUG) << " track " << ti << "\t" << t.getMotherTrackId() << " hits " << t.hasHits(); ti++; } - LOG(DEBUG) << "Have " << trackidsinTPC.size() << " tracks with hits in TPC\n"; - LOG(DEBUG) << "Have " << trackrefs->size() << " track refs \n"; - LOG(DEBUG) << "Have " << indexedtrackrefs->getIndexedSize() << " mapped tracks\n"; - LOG(DEBUG) << "Have " << indexedtrackrefs->getNElements() << " track refs\n"; + LOG(DEBUG) << "Have " << trackidsinTPC.size() << " tracks with hits in TPC"; + LOG(DEBUG) << "Have " << trackrefs->size() << " track refs"; + // check correct working of MCKinematicsReader bool havereferences = trackrefs->size(); if (havereferences) { - for (auto& i : trackidsinTPC) { - auto labels = indexedtrackrefs->getLabels(i); - assert(labels.size() > 0); - LOG(DEBUG) << " Track " << i << " has " << labels.size() << " TrackRefs " - << "\n"; - for (int j = 0; j < labels.size(); ++j) { - LOG(DEBUG) << labels[j]; + for (auto& trackID : trackidsinTPC) { + auto trackrefs = mcreader.getTrackRefs(eventID, trackID); + assert(trackrefs.size() > 0); + LOG(DEBUG) << " Track " << trackID << " has " << trackrefs.size() << " TrackRefs"; + for (auto& ref : trackrefs) { + assert(ref.getTrackID() == trackID); } } } diff --git a/run/o2sim_parallel.cxx b/run/o2sim_parallel.cxx index 18cb9dfa38446..670704e51dc73 100644 --- a/run/o2sim_parallel.cxx +++ b/run/o2sim_parallel.cxx @@ -36,9 +36,29 @@ #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" -static const char* serverlogname = "serverlog"; -static const char* workerlogname = "workerlog"; -static const char* mergerlogname = "mergerlog"; +std::string getServerLogName() +{ + auto& conf = o2::conf::SimConfig::Instance(); + std::stringstream str; + str << conf.getOutPrefix() << "_serverlog"; + return str.str(); +} + +std::string getWorkerLogName() +{ + auto& conf = o2::conf::SimConfig::Instance(); + std::stringstream str; + str << conf.getOutPrefix() << "_workerlog"; + return str.str(); +} + +std::string getMergerLogName() +{ + auto& conf = o2::conf::SimConfig::Instance(); + std::stringstream str; + str << conf.getOutPrefix() << "_mergerlog"; + return str.str(); +} void cleanup() { @@ -49,21 +69,21 @@ void cleanup() if (getenv("ALICE_O2SIM_DUMPLOG")) { std::cerr << "------------- START OF EVENTSERVER LOG ----------" << std::endl; std::stringstream catcommand1; - catcommand1 << "cat " << serverlogname << ";"; + catcommand1 << "cat " << getServerLogName() << ";"; if (system(catcommand1.str().c_str()) != 0) { LOG(WARN) << "error executing system call"; } std::cerr << "------------- START OF SIM WORKER(S) LOG --------" << std::endl; std::stringstream catcommand2; - catcommand2 << "cat " << workerlogname << "*;"; + catcommand2 << "cat " << getWorkerLogName() << "*;"; if (system(catcommand2.str().c_str()) != 0) { LOG(WARN) << "error executing system call"; } std::cerr << "------------- START OF MERGER LOG ---------------" << std::endl; std::stringstream catcommand3; - catcommand3 << "cat " << mergerlogname << ";"; + catcommand3 << "cat " << getMergerLogName() << ";"; if (system(catcommand3.str().c_str()) != 0) { LOG(WARN) << "error executing system call"; } @@ -80,6 +100,10 @@ int checkresult() // easy check: see if we have number of entries in output tree == number of events asked std::string filename = o2::base::NameConf::getMCKinematicsFileName(conf.getOutPrefix().c_str()); TFile f(filename.c_str(), "OPEN"); + if (f.IsZombie()) { + LOG(WARN) << "Kinematics file corrupted or does not exist"; + return 1; + } auto tr = static_cast<TTree*>(f.Get("o2sim")); if (!tr) { errors++; @@ -94,12 +118,18 @@ int checkresult() return errors; } +std::vector<int> gChildProcesses; // global vector of child pids + // signal handler for graceful exit void sighandler(int signal) { if (signal == SIGINT || signal == SIGTERM) { - LOG(INFO) << "signal caught ... clean up and exit"; + LOG(INFO) << "o2-sim driver: Signal caught ... clean up and exit"; cleanup(); + // forward signal to all children + for (auto& pid : gChildProcesses) { + kill(pid, signal); + } exit(0); } } @@ -281,7 +311,7 @@ int main(int argc, char* argv[]) // the server int pid = fork(); if (pid == 0) { - int fd = open(serverlogname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + int fd = open(getServerLogName().c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); setenv("ALICE_O2SIMSERVERTODRIVER_PIPE", std::to_string(pipe_serverdriver_fd[1]).c_str(), 1); dup2(fd, 1); // make stdout go to file @@ -322,9 +352,9 @@ int main(int argc, char* argv[]) } return r; } else { - childpids.push_back(pid); + gChildProcesses.push_back(pid); close(pipe_serverdriver_fd[1]); - std::cout << "Spawning particle server on PID " << pid << "; Redirect output to " << serverlogname << "\n"; + std::cout << "Spawning particle server on PID " << pid << "; Redirect output to " << getServerLogName() << "\n"; launchThreadMonitoringEvents(pipe_serverdriver_fd[0], "DISTRIBUTING EVENT : "); } @@ -336,7 +366,7 @@ int main(int argc, char* argv[]) for (int id = 0; id < nworkers; ++id) { // the workers std::stringstream workerlogss; - workerlogss << workerlogname << id; + workerlogss << getWorkerLogName() << id; // the workers std::stringstream workerss; @@ -356,7 +386,7 @@ int main(int argc, char* argv[]) "worker", "--mq-config", localconfig.c_str(), "--severity", "info", (char*)nullptr); return 0; } else { - childpids.push_back(pid); + gChildProcesses.push_back(pid); std::cout << "Spawning sim worker " << id << " on PID " << pid << "; Redirect output to " << workerlogss.str() << "\n"; } @@ -370,7 +400,7 @@ int main(int argc, char* argv[]) pid = fork(); if (pid == 0) { - int fd = open(mergerlogname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + int fd = open(getMergerLogName().c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); dup2(fd, 1); // make stdout go to file dup2(fd, 2); // make stderr go to file - you may choose to not do this // or perhaps send stderr to another file @@ -384,14 +414,14 @@ int main(int argc, char* argv[]) (char*)nullptr); return 0; } else { - std::cout << "Spawning hit merger on PID " << pid << "; Redirect output to " << mergerlogname << "\n"; - childpids.push_back(pid); + std::cout << "Spawning hit merger on PID " << pid << "; Redirect output to " << getMergerLogName() << "\n"; + gChildProcesses.push_back(pid); close(pipe_mergerdriver_fd[1]); launchThreadMonitoringEvents(pipe_mergerdriver_fd[0], "EVENT FINISHED : "); } // wait on merger (which when exiting completes the workflow) - auto mergerpid = childpids.back(); + auto mergerpid = gChildProcesses.back(); int status, cpid; // wait just blocks and waits until any child returns; but we make sure to wait until merger is here @@ -402,8 +432,8 @@ int main(int argc, char* argv[]) } // we bring down all processes if one of them aborts if (WTERMSIG(status) == SIGABRT) { - for (auto p : childpids) { - kill(p, SIGABRT); + for (auto p : gChildProcesses) { + kill(p, SIGTERM); } cleanup(); LOG(FATAL) << "ABORTING DUE TO ABORT IN COMPONENT"; @@ -414,7 +444,7 @@ int main(int argc, char* argv[]) LOG(INFO) << "Simulation process took " << timer.RealTime() << " s"; // make sure the rest shuts down - for (auto p : childpids) { + for (auto p : gChildProcesses) { if (p != mergerpid) { LOG(DEBUG) << "SHUTTING DOWN CHILD PROCESS " << p; kill(p, SIGTERM); diff --git a/run/o2simtopology.json b/run/o2simtopology.json index cfe8cd8234b43..8f65580a95b7b 100644 --- a/run/o2simtopology.json +++ b/run/o2simtopology.json @@ -10,7 +10,7 @@ { "type":"req", "method":"connect", - "address":"ipc://primary-get_25005", + "address":"ipc:///tmp/primary-get_25005", "sndBufSize":"100000", "rcvBufSize":"100000", "rateLogging":"0" @@ -23,7 +23,7 @@ { "type":"push", "method":"connect", - "address":"ipc://hitmerger-simdata_25009", + "address":"ipc:///tmp/hitmerger-simdata_25009", "sndBufSize":1000, "rcvBufSize":1000, "rateLogging":0 @@ -41,7 +41,7 @@ { "type":"rep", "method":"bind", - "address":"ipc://primary-get_25005", + "address":"ipc:///tmp/primary-get_25005", "sndBufSize":100000, "rcvBufSize":100000, "rateLogging":0 @@ -59,7 +59,7 @@ { "type":"req", "method":"connect", - "address":"ipc://primary-get_25005", + "address":"ipc:///tmp/primary-get_25005", "sndBufSize":"1000", "rcvBufSize":"1000", "rateLogging":"0" @@ -72,7 +72,7 @@ { "type":"pull", "method":"bind", - "address":"ipc://hitmerger-simdata_25009", + "address":"ipc:///tmp/hitmerger-simdata_25009", "sndBufSize":1000, "rcvBufSize":1000, "rateLogging":0 diff --git a/run/simlogcheck.sh b/run/simlogcheck.sh new file mode 100755 index 0000000000000..ced2cd01294c0 --- /dev/null +++ b/run/simlogcheck.sh @@ -0,0 +1,28 @@ +#!/bin/bash -l +set -x + +# a script checking logfiles for signs of error +# can be used to detect most common errors automatically +# returns/exits with 0 if no problem + +error=0 + +for file in "$@"; do + echo "looking into ${file}" + + # We don't want to see "Error" or "ERROR" messages + value=$(grep "Error" ${file}) + if [ -n "${value}" ]; then + echo "check for Error failed" + let error=error+1 + fi + + value=$(grep "ERROR" ${file}) + if [ -n "${value}" ]; then + echo "check for ERROR failed" + let error=error+1 + fi + +done + +exit ${error} diff --git a/scripts/profile-compilation b/scripts/profile-compilation new file mode 100755 index 0000000000000..b002a4a8ccd7d --- /dev/null +++ b/scripts/profile-compilation @@ -0,0 +1,7 @@ +#!/bin/bash + +COMPILE_COMMADS=$PWD/compile_commands.json +FILENAME=$1 +CLANG_PATH=${CLANG_PATH:-clang++} +cd `cat $COMPILE_COMMADS | jq ".[] | select(.file|test(\".*$FILENAME\"))" | jq -r '.directory'` +`cat $COMPILE_COMMADS | jq ".[] | select(.file|test(\".*$FILENAME\"))" | jq ".command |= (sub(\"^.*/c[+][+]\";\"$CLANG_PATH -ftime-trace\"))" | jq -r '.command'` diff --git a/scripts/profile-compilation-merge b/scripts/profile-compilation-merge new file mode 100755 index 0000000000000..85dadd1cfea4b --- /dev/null +++ b/scripts/profile-compilation-merge @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +"""Combine JSON from multiple -ftime-traces into one. + +scripts/profile-compilation-merge foo.json bar.json. + +results in ./combined.json +""" + +import json +import sys + +if __name__ == '__main__': + start_time = 0 + combined_data = [] + for filename in sys.argv[1:]: + with open(filename, 'r') as f: + file_time = None + for event in json.load(f)['traceEvents']: + # Skip metadata events + # Skip total events + # Filter out shorter events to reduce data size + if event['ph'] == 'M' or \ + event['name'].startswith('Total') or \ + event['dur'] < 5000: + continue + + if event['name'] == 'ExecuteCompiler': + # Find how long this compilation takes + file_time = event['dur'] + # Set the file name in ExecuteCompiler + #event['args']['detail'] = filename + + # Offset start time to make compiles sequential + event['ts'] += start_time + + # Add data to combined + combined_data.append(event) + + # Increase the start time for the next file + # Add 1 to avoid issues with simultaneous events + start_time += file_time + 1 + + with open('combined.json', 'w') as f: + json.dump({'traceEvents': sorted(combined_data, key=lambda k: k['ts'])}, f) diff --git a/scripts/py-analysis-benchmark.py b/scripts/py-analysis-benchmark.py index 3b89be3ff2f69..7eb73e71852c8 100644 --- a/scripts/py-analysis-benchmark.py +++ b/scripts/py-analysis-benchmark.py @@ -1,3 +1,16 @@ +""" +Script for the local benchmarking of the o2 analysis tasks, +running them with multiple processing jobs (NCORES) +and multiple readers (NREADERS) over input files (INPUT_FILE). +Tasks to be benchmarked are in the BENCHMARK_TASKS dict. + +Usage: python3 py-analysis-benchmark.py + +Ouput: CSV file (OUTPUT_CSV) with benchmarking results: +'tname', 'ncores', 'nreaders', 'time_mean' (s), 'time_std' (s), +'input_size' (MB), 'input_length', 'timestamp', 'cpu_load', 'ncpu', 'machine' +""" + import csv from datetime import datetime import itertools @@ -7,55 +20,85 @@ import subprocess import timeit -O2_ROOT = os.environ.get('O2_ROOT') -if not O2_ROOT: - print('O2_ROOT not found') - raise ValueError - -INPUT_FILE = '@filelist.txt' -OUTPUT_CSV = 'benchmark_data.csv' -with open(INPUT_FILE[1:],'r') as f: - fnames = f.readlines() - input_size = round(sum([os.stat(l.strip('\n')).st_size for l in fnames])/1024/1024) - input_length = len(fnames) +def get_cl_output(cmd) -> str: + try: + output_ = str(subprocess.check_output(cmd, shell=True), 'utf-8') + except subprocess.CalledProcessError: + output_ = '' + return output_.strip('\n') -NCORES = [1, 2, 4] -NREADERS = [1, 2, 4] -NTRIALS = 3 -CPU_SELECTION = False +def get_cpu_load(): + uptime_ = get_cl_output('uptime') + load_last_15 = uptime_.split(' ')[-1] + return load_last_15 -SHA256SUM_TASK = Template('cat ${file_list} | xargs -P ${n} -n1 -I{} sha256sum {}') -#COMPOSITE_TASK = Template('o2-analysis-trackselection -b --pipeline track-selection:${n},track-extension:${n} --aod-file ${file_list} --readers ${n} | o2-analysistutorial-histogram-track-selection -b --pipeline histogram-track-selection:${n} --select=0') +def get_timestamp(): + return datetime.now().strftime("%m/%d/%Y %H:%M") + + +def get_time_std(t_res): + try: + std_ = stat.stdev(t_res) + except stat.StatisticsError: + std_ = -1 + return std_ + +#benchmarking setup +INPUT_FILE = '@filelist.txt' +OUTPUT_CSV = 'benchmark_data.csv' +NCORES = [1, 2, 4] +NREADERS = [1, 2, 4] +NTRIALS = 2 +LARGE_SHM_SEGMENT_SIZE = False +CPU_SELECTION = False + +#tasks to be benchmarked BENCHMARK_TASKS = { + 'o2-analysistutorial-void': '-b --pipeline void:${n}', 'o2-analysistutorial-histograms': '-b --pipeline eta-and-phi-histograms:${n},pt-histogram:${n},etaphi-histogram:${n}', 'o2-analysis-trackselection': '-b --pipeline track-selection:${n},track_extension:${n}', - #'o2-analysis-vertexing-hf': '-b --pipeline vertexerhf-candidatebuildingDzero:${n},vertexerhf-decayvertexbuilder2prong:${n}', + 'o2-analysis-correlations': '-b --pipeline correlation-task:${n}', + #'o2-analysis-vertexing-hf': '-b --pipeline vertexerhf-candidatebuildingDzero:${n},vertexerhf-decayvertexbuilder2prong:${n}' } - + + +O2_ROOT = os.environ.get('O2_ROOT') +if not O2_ROOT: + print('O2_ROOT not found') + raise ValueError + +MACHINE = get_cl_output('hostname') +NCPU = get_cl_output('grep processor /proc/cpuinfo | wc -l') +with open(INPUT_FILE[1:],'r') as f: + fnames = f.readlines() + INPUT_SIZE = round(sum([os.stat(l.strip('\n')).st_size for l in fnames])/1024/1024) + INPUT_LENGTH = len(fnames) + + +SHA256SUM_TASK = Template('cat ${file_list} | xargs -P ${n} -n1 -I{} sha256sum {}') +#COMPOSITE_TASK = Template('o2-analysis-trackselection -b --pipeline track-selection:${n},track-extension:${n} --aod-file ${file_list} --readers ${n} | o2-analysistutorial-histogram-track-selection -b --pipeline histogram-track-selection:${n} --select=0') + for k in BENCHMARK_TASKS: BENCHMARK_TASKS[k] = Template(BENCHMARK_TASKS[k]) with open(OUTPUT_CSV, 'w') as f: writer = csv.writer(f) - writer.writerow(['tname', 'ncores', 'nreaders', 'time_mean', 'time_std', 'input_size', 'input_length']) + writer.writerow(('tname', 'ncores', 'nreaders', 'time_mean', 'time_std', + 'input_size', 'input_length', 'timestamp', 'cpu_load', 'ncpu', 'machine')) for ncores in NCORES: cmd_sha256sum = SHA256SUM_TASK.substitute(file_list=INPUT_FILE[1:], n=str(ncores)) t = timeit.Timer('os.system(cmd_sha256sum)', globals=globals()) t_res = t.repeat(NTRIALS, 1) - writer.writerow( ('sha256sum', ncores, -1, stat.mean(t_res), stat.stdev(t_res), input_size, input_length) ) + writer.writerow( ('sha256sum', ncores, -1, stat.mean(t_res), get_time_std(t_res), + INPUT_SIZE, INPUT_LENGTH, get_timestamp(), get_cpu_load(), NCPU, MACHINE) ) for ncores, nreaders in itertools.product(NCORES, NREADERS): - - #cmd_composite = COMPOSITE_TASK.substitute(file_list=INPUT_FILE,n=str(ncores)) - #t = timeit.Timer('os.system(cmd_composite)', globals=globals()) - #t_res = t.repeat(NTRIALS, 1) - #writer.writerow( ('analysistutorial-histogram-track-selection', ncores, nreaders, stat.mean(t_res), stat.stdev(t_res), input_size, input_length) ) - + for tname, targ in BENCHMARK_TASKS.items(): targ = targ.substitute(n=str(ncores)) cmd_list = [tname] + targ.split(' ') @@ -65,13 +108,17 @@ cmd_list = ['taskset','-c','5,15'] + cmd_list elif ncores == 4: cmd_list = ['taskset','-c','1,3,11,13'] + cmd_list + + if LARGE_SHM_SEGMENT_SIZE: + cmd_list += ['--shm-segment-size', str(16000000000)] cmd_list += ['--aod-file', INPUT_FILE] cmd_list += ['--readers', str(nreaders)] t = timeit.Timer('subprocess.run(cmd_list)', globals=globals()) t_res = t.repeat(NTRIALS, 1) - writer.writerow( (tname[3:], ncores, nreaders, stat.mean(t_res), stat.stdev(t_res), input_size, input_length) ) + writer.writerow( (tname[3:], ncores, nreaders, stat.mean(t_res), get_time_std(t_res), + INPUT_SIZE, INPUT_LENGTH, get_timestamp(), get_cpu_load(), NCPU, MACHINE) ) #alinsure #numa0 0-11,24-35 diff --git a/tests/ensure-executable-naming-convention.sh.in b/tests/ensure-executable-naming-convention.sh.in index 9e6d6a1b73c02..5eb64dd0f5af3 100755 --- a/tests/ensure-executable-naming-convention.sh.in +++ b/tests/ensure-executable-naming-convention.sh.in @@ -28,6 +28,7 @@ RE_IS_BENCH="^o2-bench" RE_START_WITH_O2_OR_TEST="^o2-" RE_ONLY_LOWER_CASE_OR_DASH_OR_COMMA="^[a-z0-9]([a-z0-9\.]+|-)+[a-z0-9]$" RE_SUBSYSTEM="(o2)-([a-z,0-9]+)+" # o2-subsystem +RE_EXEMPTION="cpulimit" KNOWN_GROUPS="alicehlt \ analysis aod d0 simple \ @@ -40,7 +41,10 @@ fit its mch mid tpc \ tpcits \ flp heartbeat message epn \ mergers \ -subframebuilder sync timeframe framework" +subframebuilder sync timeframe framework \ +analysistutorial tof trd phos mft mchmapping mchraw \ +calibration fdd ft0 fv0 rans raw secondary \ +cpv ctf dpl emcal dcs itsmft primary reco pidparam" [[ -d @CMAKE_RUNTIME_OUTPUT_DIRECTORY@ ]] || exit 0 @@ -50,6 +54,7 @@ for F in *; do [[ -x $F && -f $F ]] || continue [[ $F =~ $RE_IS_TEST ]] && continue # convention for tests is yet to come [[ $F =~ $RE_IS_BENCH ]] && continue # convention for tests is yet to come + [[ $F =~ $RE_EXEMPTION ]] && continue # allow special executables to not follow the convention [[ ! $F =~ $RE_START_WITH_O2_OR_TEST ]] && error_msg $F "does not start with o2-" [[ ! $F =~ $RE_ONLY_LOWER_CASE_OR_DASH_OR_COMMA ]] && error_msg $F "should only contains lowercase letters, numbers, or dashes" [[ $F =~ $RE_SUBSYSTEM ]] && SUBSYSTEM=${BASH_REMATCH[2]}